pax_global_header00006660000000000000000000000064142773515030014521gustar00rootroot0000000000000052 comment=5fc039033f7f9245b89c1d8f1e6dd3aad110e56f dpkt-1.9.8/000077500000000000000000000000001427735150300125025ustar00rootroot00000000000000dpkt-1.9.8/.github/000077500000000000000000000000001427735150300140425ustar00rootroot00000000000000dpkt-1.9.8/.github/ISSUE_TEMPLATE/000077500000000000000000000000001427735150300162255ustar00rootroot00000000000000dpkt-1.9.8/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013541427735150300207220ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Details(please complete the following information):** - OS: [e.g. Windows10, Debian, etc] - Python Version [e.g. 3.8.6 ] - Please upload any pcap files that are needed to reproduce the issue. (Please try to have just the offending packets in the pcap to reduce upload size) **Additional context** Add any other context about the problem here. dpkt-1.9.8/.github/workflows/000077500000000000000000000000001427735150300160775ustar00rootroot00000000000000dpkt-1.9.8/.github/workflows/codeql-analysis.yml000066400000000000000000000044701427735150300217170ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '17 3 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 dpkt-1.9.8/.github/workflows/python-package.yml000066400000000000000000000033411427735150300215350ustar00rootroot00000000000000# This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: Python package on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ['2.7', '3.5', '3.6', '3.7', '3.8', '3.9', 'pypy3'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install flake8 pytest coverage pytest-cov - name: pytest and generate coverage report run: | coverage run --source dpkt -m pytest dpkt - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Coveralls Python uses: AndreMiras/coveralls-python-action@v20201129 with: parallel: true flag-name: Unit Test - Python ${{ matrix.python-version }} github-token: ${{ secrets.GITHUB_TOKEN }} coveralls_finish: needs: build runs-on: ubuntu-latest steps: - name: Push to Coveralls uses: AndreMiras/coveralls-python-action@develop with: parallel-finished: true dpkt-1.9.8/.gitignore000066400000000000000000000006641427735150300145000ustar00rootroot00000000000000# Compiled/intermediate Python files *.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build sdist __pycache__ # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox .cache # iPython temp files .ipynb_checkpoints # Mac DS files .DS_Store # ReadTheDocs build directory _build # IntelliJ project files *.iml *.iws *.ipr .idea/ # eclipse project file .settings/ .classpath .project .pydevprojectdpkt-1.9.8/AUTHORS000066400000000000000000000027261427735150300135610ustar00rootroot00000000000000 Original author --------------- Dug Song Contributors ------------ Timur Alperovich radiotap module Nic Bellamy HTTP header parsing fix the grugq better RTP module David Helder bug fixes Przemyslaw Karwasiecki TABLE_DUMP in MRT module Reza Lotun MetaPacket cleanup Jeff Nathan bug fixes Tim Newsham IPv6 bugfixing and improvements keisuke.nishimoto@gmail.com Snoop file parser Jon Oberheide STUN, H.225, TPKT, NTP, RIP, Diameter, SCTP, BGP, MRT, RX modules plotnikoff@gmail.com handle dynamic imports from py2exe/freeze.py/zipped egg packages simdream@gmail.com handle multiple cookie values in HTTP Owen Stephens IP6 extension header support Robert Stone Netflow and QQ modules Thomas Taranowski dnet IP checksum bug on i386 Jirka Vejrazka bug fixes Tim Yardley DHCP definitions Oscar Ibatullin pcapng module, core improvements, bit fields, pretty print, creating parsers doc Kyle Keppler Python 3 port Hao Sun Python 3 port Brian Wylie Examples, Docs, Tests, CI, Python 3 port crocogorical Extend test coverage dpkt-1.9.8/LICENSE000066400000000000000000000027251427735150300135150ustar00rootroot00000000000000 Copyright (c) 2004 Dug Song All rights reserved, all wrongs reversed. 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. The names of the authors and copyright holders may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. dpkt-1.9.8/MANIFEST.in000066400000000000000000000001661427735150300142430ustar00rootroot00000000000000 include AUTHORS CHANGES README.rst LICENSE recursive-include docs * prune docs/_build global-exclude __pycache__/* dpkt-1.9.8/Makefile000066400000000000000000000010111427735150300141330ustar00rootroot00000000000000 help: @echo "clean - remove all build/python artifacts" @echo "clean-build - remove build artifacts" @echo "clean-pyc - remove Python file artifacts" clean: clean-build clean-pyc clean-build: rm -fr build/ rm -fr dist/ rm -fr deb_dist/ rm -fr *.egg-info rm -fr *.tar.gz rm -fr .tox rm -fr .coverage rm -fr .cache rm -fr .pytest_cache find . -name '__pycache__' -exec rm -fr {} + clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + dpkt-1.9.8/README.md000066400000000000000000000013151427735150300137610ustar00rootroot00000000000000# dpkt ![Python package](https://github.com/kbandla/dpkt/workflows/Python%20package/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/kbandla/dpkt/badge.svg?branch=master)](https://coveralls.io/github/kbandla/dpkt?branch=master) [![supported-versions](https://img.shields.io/pypi/pyversions/dpkt.svg)](https://pypi.python.org/pypi/dpkt) [![supported-versions](https://img.shields.io/pypi/implementation/dpkt.svg)](https://pypi.python.org/pypi/dpkt) The dpkt project is a python module for fast, simple packet parsing, with definitions for the basic TCP/IP protocols. ### Installation ``` pip install dpkt ``` ### Examples and Documentation - [Main Docs for DPKT](https://kbandla.github.io/dpkt) dpkt-1.9.8/docs/000077500000000000000000000000001427735150300134325ustar00rootroot00000000000000dpkt-1.9.8/docs/_config.yml000066400000000000000000000000321427735150300155540ustar00rootroot00000000000000theme: jekyll-theme-hackerdpkt-1.9.8/docs/admin_notes.md000066400000000000000000000032611427735150300162560ustar00rootroot00000000000000# Notes ## PyPI Release How-To Notes and information on how to do the PyPI release for the dpkt project. For full details on packaging you can reference this page [Packaging](https://packaging.python.org/tutorials/packaging-projects/#packaging-your-project) The following instructions should work, but things change :) ### Package Requirements ``` bash - pip install tox - pip install --upgrade setuptools wheel - pip install twine ``` ### Setup pypirc The easiest thing to do is setup a \~/.pypirc file with the following contents ``` bash [distutils] index-servers = pypi testpypi [pypi] repository=https://upload.pypi.org/legacy/ username= password= [testpypi] repository=https://test.pypi.org/legacy/ username= password= ``` ### Tox Background Tox will install the dpkt package into a blank virtualenv and then execute all the tests against the newly installed package. So if everything goes okay, you know the pypi package installed fine and the tests (which pull from the installed dpkt package) also ran okay. ### Make sure ALL tests pass ``` bash $ cd dpkt $ tox ``` If ALL the test above pass... ### Create the TEST PyPI Release ``` bash $ vi dpkt/__init__.py and bump the version $ python setup.py sdist bdist_wheel $ twine upload dist/* -r testpypi ``` ### Install the TEST PyPI Release ``` bash $ pip install --index-url https://test.pypi.org/simple dpkt ``` ### Create the REAL PyPI Release ``` bash $ twine upload dist/* -r pypi ``` ### Push changes to Github ``` bash $ git add dpkt/__init__.py $ get commit -m "dpkt version 1.8.7 (or whatever)" $ git tag v1.8.7 (or whatever) $ git push --tags $ git push ``` dpkt-1.9.8/docs/api/000077500000000000000000000000001427735150300142035ustar00rootroot00000000000000dpkt-1.9.8/docs/api/api_auto.rst000066400000000000000000000175311427735150300165450ustar00rootroot00000000000000 dpkt.ah module -------------- .. automodule:: dpkt.ah :members: :undoc-members: :show-inheritance: dpkt.aim module --------------- .. automodule:: dpkt.aim :members: :undoc-members: :show-inheritance: dpkt.aoe module --------------- .. automodule:: dpkt.aoe :members: :undoc-members: :show-inheritance: dpkt.aoeata module ------------------ .. automodule:: dpkt.aoeata :members: :undoc-members: :show-inheritance: dpkt.aoecfg module ------------------ .. automodule:: dpkt.aoecfg :members: :undoc-members: :show-inheritance: dpkt.arp module --------------- .. automodule:: dpkt.arp :members: :undoc-members: :show-inheritance: dpkt.asn1 module ---------------- .. automodule:: dpkt.asn1 :members: :undoc-members: :show-inheritance: dpkt.bgp module --------------- .. automodule:: dpkt.bgp :members: :undoc-members: :show-inheritance: dpkt.cdp module --------------- .. automodule:: dpkt.cdp :members: :undoc-members: :show-inheritance: dpkt.crc32c module ------------------ .. automodule:: dpkt.crc32c :members: :undoc-members: :show-inheritance: dpkt.dhcp module ---------------- .. automodule:: dpkt.dhcp :members: :undoc-members: :show-inheritance: dpkt.diameter module -------------------- .. automodule:: dpkt.diameter :members: :undoc-members: :show-inheritance: dpkt.dns module --------------- .. automodule:: dpkt.dns :members: :undoc-members: :show-inheritance: dpkt.dpkt module ---------------- .. automodule:: dpkt.dpkt :members: :undoc-members: :show-inheritance: dpkt.dtp module --------------- .. automodule:: dpkt.dtp :members: :undoc-members: :show-inheritance: dpkt.esp module --------------- .. automodule:: dpkt.esp :members: :undoc-members: :show-inheritance: dpkt.ethernet module -------------------- .. automodule:: dpkt.ethernet :members: :undoc-members: :show-inheritance: dpkt.gre module --------------- .. automodule:: dpkt.gre :members: :undoc-members: :show-inheritance: dpkt.gzip module ---------------- .. automodule:: dpkt.gzip :members: :undoc-members: :show-inheritance: dpkt.h225 module ---------------- .. automodule:: dpkt.h225 :members: :undoc-members: :show-inheritance: dpkt.hsrp module ---------------- .. automodule:: dpkt.hsrp :members: :undoc-members: :show-inheritance: dpkt.http module ---------------- .. automodule:: dpkt.http :members: :undoc-members: :show-inheritance: dpkt.icmp module ---------------- .. automodule:: dpkt.icmp :members: :undoc-members: :show-inheritance: dpkt.icmp6 module ----------------- .. automodule:: dpkt.icmp6 :members: :undoc-members: :show-inheritance: dpkt.ieee80211 module --------------------- .. automodule:: dpkt.ieee80211 :members: :undoc-members: :show-inheritance: dpkt.igmp module ---------------- .. automodule:: dpkt.igmp :members: :undoc-members: :show-inheritance: dpkt.ip module -------------- .. automodule:: dpkt.ip :members: :undoc-members: :show-inheritance: dpkt.ip6 module --------------- .. automodule:: dpkt.ip6 :members: :undoc-members: :show-inheritance: dpkt.ipx module --------------- .. automodule:: dpkt.ipx :members: :undoc-members: :show-inheritance: dpkt.llc module --------------- .. automodule:: dpkt.llc :members: :undoc-members: :show-inheritance: dpkt.loopback module -------------------- .. automodule:: dpkt.loopback :members: :undoc-members: :show-inheritance: dpkt.mrt module --------------- .. automodule:: dpkt.mrt :members: :undoc-members: :show-inheritance: dpkt.netbios module ------------------- .. automodule:: dpkt.netbios :members: :undoc-members: :show-inheritance: dpkt.netflow module ------------------- .. automodule:: dpkt.netflow :members: :undoc-members: :show-inheritance: dpkt.ntp module --------------- .. automodule:: dpkt.ntp :members: :undoc-members: :show-inheritance: dpkt.ospf module ---------------- .. automodule:: dpkt.ospf :members: :undoc-members: :show-inheritance: dpkt.pcap module ---------------- .. automodule:: dpkt.pcap :members: :undoc-members: :show-inheritance: dpkt.pim module --------------- .. automodule:: dpkt.pim :members: :undoc-members: :show-inheritance: dpkt.pmap module ---------------- .. automodule:: dpkt.pmap :members: :undoc-members: :show-inheritance: dpkt.ppp module --------------- .. automodule:: dpkt.ppp :members: :undoc-members: :show-inheritance: dpkt.pppoe module ----------------- .. automodule:: dpkt.pppoe :members: :undoc-members: :show-inheritance: dpkt.qq module -------------- .. automodule:: dpkt.qq :members: :undoc-members: :show-inheritance: dpkt.radiotap module -------------------- .. automodule:: dpkt.radiotap :members: :undoc-members: :show-inheritance: dpkt.radius module ------------------ .. automodule:: dpkt.radius :members: :undoc-members: :show-inheritance: dpkt.rfb module --------------- .. automodule:: dpkt.rfb :members: :undoc-members: :show-inheritance: dpkt.rip module --------------- .. automodule:: dpkt.rip :members: :undoc-members: :show-inheritance: dpkt.rpc module --------------- .. automodule:: dpkt.rpc :members: :undoc-members: :show-inheritance: dpkt.rtp module --------------- .. automodule:: dpkt.rtp :members: :undoc-members: :show-inheritance: dpkt.rx module -------------- .. automodule:: dpkt.rx :members: :undoc-members: :show-inheritance: dpkt.sccp module ---------------- .. automodule:: dpkt.sccp :members: :undoc-members: :show-inheritance: dpkt.sctp module ---------------- .. automodule:: dpkt.sctp :members: :undoc-members: :show-inheritance: dpkt.sip module --------------- .. automodule:: dpkt.sip :members: :undoc-members: :show-inheritance: dpkt.sll module --------------- .. automodule:: dpkt.sll :members: :undoc-members: :show-inheritance: dpkt.smb module --------------- .. automodule:: dpkt.smb :members: :undoc-members: :show-inheritance: dpkt.snoop module ----------------- .. automodule:: dpkt.snoop :members: :undoc-members: :show-inheritance: dpkt.ssl module --------------- .. automodule:: dpkt.ssl :members: :undoc-members: :show-inheritance: dpkt.ssl_ciphersuites module ---------------------------- .. automodule:: dpkt.ssl_ciphersuites :members: :undoc-members: :show-inheritance: dpkt.stp module --------------- .. automodule:: dpkt.stp :members: :undoc-members: :show-inheritance: dpkt.stun module ---------------- .. automodule:: dpkt.stun :members: :undoc-members: :show-inheritance: dpkt.tcp module --------------- .. automodule:: dpkt.tcp :members: :undoc-members: :show-inheritance: dpkt.telnet module ------------------ .. automodule:: dpkt.telnet :members: :undoc-members: :show-inheritance: dpkt.tftp module ---------------- .. automodule:: dpkt.tftp :members: :undoc-members: :show-inheritance: dpkt.tns module --------------- .. automodule:: dpkt.tns :members: :undoc-members: :show-inheritance: dpkt.tpkt module ---------------- .. automodule:: dpkt.tpkt :members: :undoc-members: :show-inheritance: dpkt.udp module --------------- .. automodule:: dpkt.udp :members: :undoc-members: :show-inheritance: dpkt.vrrp module ---------------- .. automodule:: dpkt.vrrp :members: :undoc-members: :show-inheritance: dpkt.yahoo module ----------------- .. automodule:: dpkt.yahoo :members: :undoc-members: :show-inheritance: dpkt-1.9.8/docs/api/index.rst000066400000000000000000000003361427735150300160460ustar00rootroot00000000000000 API Reference ============= The dpkt API reference section is currently a work in progress, please have patience as we fill in and improve the documentation. **dpkt Modules** .. toctree:: :maxdepth: 4 api_auto dpkt-1.9.8/docs/authors.md000066400000000000000000000036261427735150300154500ustar00rootroot00000000000000# Authors ## Original author Dug Song \<\> ## Contributors - Timur Alperovich \<\> radiotap module - Nic Bellamy \<\> HTTP header parsing fix - the grugq \<\> better RTP module - David Helder \<\> bug fixes - Przemyslaw Karwasiecki \<\> TABLE\_DUMP in MRT module - Reza Lotun \<\> MetaPacket cleanup - Jeff Nathan \<\> bug fixes - Tim Newsham \<\> IPv6 bugfixing and improvements - Snoop file parser - Jon Oberheide \<\> STUN, H.225, TPKT, NTP, RIP, Diameter, SCTP, BGP, MRT, RX modules - handle dynamic imports from py2exe/freeze.py/zipped egg packages - handle multiple cookie values in HTTP - Owen Stephens \<\> IP6 extension header support - Robert Stone \<\> Netflow and QQ modules - Thomas Taranowski \<\> dnet IP checksum bug on i386 - Jirka Vejrazka bug fixes - Tim Yardley \<\> DHCP definitions - Oscar Ibatullin \<\> pcapng module, core improvements, bit fields, pretty print, creating parsers doc - Kyle Keppler \<\> Python 3 port - Hao Sun \<\> Python 3 port - Brian Wylie \<\> Examples, Docs, Tests, CI, Python 3 port - crocogorical \<\> Extend test coverage - Schwaggot \<\> pcapng module, support for Packet Block and handling of UTF-8 option comments If you want to contribute to dpkt, see `contributing`. dpkt-1.9.8/docs/badges.md000066400000000000000000000016021427735150300152000ustar00rootroot00000000000000[![Travis-CI Build Status](http://img.shields.io/travis/kbandla/dpkt.svg)](https://travis-ci.org/kbandla/dpkt) [![Coverage Status](http://img.shields.io/coveralls/kbandla/dpkt.svg)](https://coveralls.io/r/kbandla/dpkt) [![Code Quality Status](https://landscape.io/github/kbandla/dpkt/master/landscape.svg)](https://landscape.io/github/kbandla/dpkt/master) [![PyPI Package monthly downloads](http://img.shields.io/pypi/dm/dpkt.svg)](https://pypi.python.org/pypi/dpkt) [![PyPI Package latest release](http://img.shields.io/pypi/v/dpkt.svg)](https://pypi.python.org/pypi/dpkt) [![PyPI Wheel](https://img.shields.io/pypi/wheel/dpkt.svg)](https://pypi.python.org/pypi/dpkt) [![Supported versions](https://img.shields.io/pypi/pyversions/dpkt.svg)](https://pypi.python.org/pypi/dpkt) [![Supported implementations](https://img.shields.io/pypi/implementation/dpkt.svg)](https://pypi.python.org/pypi/dpkt) dpkt-1.9.8/docs/changelog.md000066400000000000000000000060761427735150300157140ustar00rootroot00000000000000# Changelog ## 1.9.8 **[2022-08-17]** - Fixed endianness issues in PCAPNG, Loopback - Improved MPLS unpacking to include IPv6 - Fixed unpacking of multiple records in TLS messages - Updated docstrings for multiples modules - Fixed a long-standing issue where serializing IP would change its length - Fixed IEEE 802.11 Beacon byte ordering - Graceful handling of PCAPNG option comment UTF-8 decoding errors - Added support for PCAPNG Packet Block - Added modpcap reader support ## 1.9.7.2 **[2021-08-16]** - Fixed performance regression (https://github.com/kbandla/dpkt/issues/611) ## 1.9.7 **[2021-08-16]** - Moved the project documentation from Read the Docs(RST) to github.io(MarkDown) - Added a new mechanism for creating bit-sized field definitions in the protocol parsers (Packet.\_\_bit_fields\_\_) - Added pretty printing capability aka Packet.pprint(), Packet.\_\_pprint_funcs\_\_ - Added documentation on developing protocol parsers in dpkt (creating_parsers.md) - Added a universal pcap+pcapng reader (dpkt.pcap.UniversalReader) - Improved TLS ClientHello and ServerHello parsing: return an "Unknown" ciphersuite instead of raising an exception, add codes for rfc8701, GREASE ciphersutes - Added function to get IP protocol name - Modified Packet.\_\_getitem\_\_() and added Packet.\_\_contains\_\_() to address the nested protocol layers - Fixed payload length interpretation in AH decoder - Improved handling of invalid chunks in HTTP and SCTP - Fixed decoding of IPv6 fragments after the 1st fragment - Support rfc3540 nonce sum flag in TCP ## 1.9.6 **[2021-05-21]** - Added in the TLS 1.3 Cipher Suite from the RFC 8446 dated August 2018 - Added support for Linux cooked capture v2, SLL2. ## 1.9.5 **[2021-02-07]** - New example showing how to process truncated DNS packets (examples/print_dns_truncated.py). - Corrected typo in BGP.notification attribute. - BGP.Update.Attribute.MPReachNLRI.SNPA now inherits from dpkt.Packet. - Byteorder is now specified when packing GRE optional fields. - \#517: Improvement to Radiotap class, supporting multi-byte and misaligned flags fields. Endianness is now enforced. - Github issue template added for bug reporting. - Compliance with flake8 formatting. - asn1.py::utctime method now returns time in UTC, instead of local. - Allow multiple InterfaceDescriptionBlocks with pcapng.Writer. - SCTP decoder DATA chunk padding aligned to 4-bytes, and improved handling of .data field. - IEEE80211 DELBA frame now works on big and little-endian architectures. - Introduce compat.ntole which converts from network byte order to little-endian byte order, regardless of host endianness. - Ethernet class now attempts to unpack the padding and trailer if present. - Added anonymous property to cipher suites, which returns True if the cipher suite starts with 'anon'. - Added pfs (Perfect Forward Secrecy) and aead (Authenticated Encryption with Additional Data) properties to cipher suites. - Added old CHACHA20-POLY1305 related cipher suites to TLS CipherSuite list. - Remove redundant num_compression_methods from TLSClientHello - Testing improved from 90% coverage to over 99%. dpkt-1.9.8/docs/contributing.md000066400000000000000000000017641427735150300164730ustar00rootroot00000000000000# Contributing ## Report a Bug or Make a Feature Request Please go to the GitHub Issues page: . ## Checkout the Code ``` git clone https://github.com/kbandla/dpkt.git ``` ## Become a Developer The dpkt package uses the 'GitHub Flow' model: [GitHub Flow](http://scottchacon.com/2011/08/31/github-flow.html) If you'd like to submit a PR to fix/improve dpkt, you should create a 'fork' of the repository and open a Pull Request (PR) and one of the developers will review the PR and give feedback. The following page has good instructions on creating a PR from a fork: make a fork and then create a Pull Request . ### New Feature or Bug ``` $ git checkout -b my-awesome $ git push -u origin my-awesome $ ; git push $ ; git push $ pytest dpkt (this will run all the tests) ``` - Go to github and hit 'New pull request' - Someone reviews it and says 'AOK/give feedback and merges dpkt-1.9.8/docs/creating_parsers.md000066400000000000000000000236001427735150300173100ustar00rootroot00000000000000# Key concepts of creating protocol parsers in dpkt by Oscar Ibatullin \[\] a contributor/maintainer of dpkt. ## Parser class definition Let's look at the IPv4 parser, defined in `dpkt/ip.py`, as an example. ```python class IP(dpkt.Packet): """Internet Protocol.""" __hdr__ = ( ('_v_hl', 'B', (4 << 4) | (20 >> 2)), ('tos', 'B', 0), ('len', 'H', 20), ('id', 'H', 0), ('_flags_offset', 'H', 0), ('ttl', 'B', 64), ('p', 'B', 0), ('sum', 'H', 0), ('src', '4s', b'\x00' * 4), ('dst', '4s', b'\x00' * 4) ) __bit_fields__ = { '_v_hl': ( ('v', 4), # version, 4 bits ('hl', 4), # header len, 4 bits ), '_flags_offset': ( ('rf', 1), # reserved bit ('df', 1), # don't fragment ('mf', 1), # more fragments ('offset', 13), # fragment offset, 13 bits ) } __pprint_funcs__ = { 'dst': inet_to_str, 'src': inet_to_str, 'p': get_ip_proto_name } ``` A lot is going on in the header, before we even got to `__init__`\! Here is the breakdown: 1. Note the main `class IP` inherits from `dpkt.Packet` 2. `__hdr__` defines a list of fields in the protocol header as 3-item tuples: *(field name, python struct format, default value)*. The fields are arranged in the order they appear on the wire. Field names generally follow the protocol definitions (e.g. RFC), but there are some rules to naming the fields that affect `dpkt` processing: * a name that doesn't start with an underscore represents a regular public protocol field. *Examples:* `tos`, `len`, `id` * a name that starts with an underscore and contains NO more underscores is considered private and gets hidden in `__repr__` and `pprint()` outputs; this is useful for hiding fields reserved for future use, or fields that should be decoded according to some custom rules. *Example:* `_reserved` * a name that starts with an underscore and DOES contain more underscores is similarly considered private and hidden, but gets processed as a collection of multiple protocol fields, separated by underscore. Each field name may contain up to 1 underscore as well. These fields are only created when the class definition contains matching property definitions, which could be defined explicitly or created automagically via `__bit_fields__` (more on this later). *Examples:* * `_foo_bar_m_flag` will map to fields named `foo`, `bar`, `m_flag`, when the class contains properties with these names (note `foo_bar_m` will be ignored since it contains two underscores). * in the IP class the `_v_hl` field itself is hidden in the output of `__repr__` and `pprint()`, and is decoded into `v` and `hl` fields that are displayed instead. The second component of the tuple specifies the format of the protocol field, as it corresponds to Python's native `struct` module. `'B'` means the field will decode to an unsigned byte, `'H'` - to an unsigned word, etc. The default byte order is big endian (network order). Endianness can be changed to little endian by specifying `__byte_order__ = '<'` in the class definition. 3. Next, `__bit_fields__` is an optional dict that helps decode compound protocol fields, such as `_v_hl` or `_flags_offset` in the IP class. Each field name (as it appears in `__hdr__`) maps to a list (technically a tuple) of tuples, defining the bit fields in the network order (from high to low). Each tuple is *(bit field name, size in bits)*. The total sum of bit sizes must match the overall size of the placeholder field. For example, `_v_hl` is decoded to 1 byte (`'B'`), or 8 bits; `v` (the IP version) occupies the high 4 bits and `hl` (IP header length) occupies the lower 4 bits. `_flags_offset` that is 2 bytes long (`'H'`) is decoded into 3 1-bit flags followed by a 13-bit offset, total of 16 bytes. Similarly to the naming rules of `__hdr__`, a bit field name starting with an underscore is made invisible in the output. When dpkt processes `__bit_fields__` it auto-creates class properties that enable interfacing with the bit fields directly, specifically: get the value (`ip.v`), modify the value (`ip.v = 6`), and reset the value back to its default (`del ip.v`). In certain cases, auto-properties can't be applied; they still can be created explicitly. Look at `class SMB` inside `dpkt/smb.py` in how it decodes the `pid` protocol field. 4. Next, `__pprint_funcs__` is an optional dict that does not control protocol decoding, but helps with pretty printing of the decoded packet using the `pprint()` method. Each key in this map is a name of the protocol field, and each value is a callable that will be run with a single argument of the protocol field value. For example, it's nice to see human readable IP addresses for `src` and `dst` fields by passing the raw bytes to `inet_to_str` function. ## Standard methods Let's look at the standard methods of the `Packet` class and how they contribute to parsing (aka unpacking or deserializing) and constructing (aka packing or serializing) the packet. ```python class IP(dpkt.Packet): ... def __init__(self, *args, **kwargs): super(IP, self).__init__(*args, **kwargs) ... def __len__(self): return self.__hdr_len__ + len(self.opts) + len(self.data) def __bytes__(self): # calculate IP checksum if self.sum == 0: self.sum = dpkt.in_cksum(self.pack_hdr() + bytes(self.opts)) ... return self.pack_hdr() + bytes(self.opts) + bytes(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) ... self.opts = ... # add IP options ... self.data = ... # bytes that remain after unpacking def pack_hdr(self): buf = dpkt.Packet.pack_hdr(self) ... return buf ``` Instantiating the class with a bytes buffer (`ip = dpkt.ip.IP(buf)`) will trigger the unpacking sequence as follows: 1. `__init__(buf)` calls `self.unpack(buf)` 2. `Packet.unpack()` creates protocol fields given in `__hdr__` as class attributes, and sets `self.data` to the remaining unparsed bytes in the buffer. Child classes typically extend the `Packet.unpack()` method to create additional custom attributes, that are not given in the `__hdr__` (such as `opts` for IP options below). Packing is the opposite of unpacking of course; given an instance of a parsed packet, packing will return serialized packet as a `bytes` object (`bytes(ip) => buf`). It goes as follows: 1. Calling `bytes(obj)` invokes `self.__bytes__(obj)` 2. `Packet.__bytes()__` calls `self.pack_hdr()` and returns its result with appended `bytes(self.data)`. The latter recursively triggers serialization of `self.data`, which could be another packet class, e.g. `Ethernet(.., data=IP(.., data=TCP(...)))`, so everything gets serialized. 3. `Packet.pack_hdr()` iterates over the protocol fields given in `__hdr__`, calls `struct.pack()` on them and returns the resulting bytes. Child classes typically extend the `Packet.__bytes__()` method to process custom attributes, that are not given in the `__hdr__`, or to override some values before `pack_hdr()` turns them into bytes. See how the IP parser overrides `__bytes__` to calculate the IP checksum prior to packing, and insert `bytes(self.opts)` between the packed header and data. ### \_\_len\_\_ `__len__()` returns the size of the serialized packet and is typically invoked when calling `len(obj)`. Note how in the IP class, this method calls other functions to calculate size, then sums the lengths together, and it **does not** perform serialization. It may be tempting to implement `__len__` by serializing the packet into bytes and returning the size of the resulting buffer (`return len(bytes(self))`). While this works and is acceptable in some cases, dpkt views this as an anti-pattern that should be avoided. ### \_\_repr\_\_ and pprint() These methods are provided by `dpkt.Packet` and are typically not overridden in the child class. However they are important to understand when developing protocol parsers. Both `repr()` and `pprint()` are responsible for the output, and both produce valid interpretable Python, but there are some differences: 1. `__repr__` returns a short one-liner printable string, while `pprint()` actually prints and returns nothing 2. `__repr__` does not include protocol fields if their value is default, i.e. it will only display a field when it differs from the default. *Example:* in IPv4 the version always equals 4 so normally field `v` is not included. 3. `pprint()` is verbose; its output is one field per line, indented, outdented and commented, and contrary to `__repr__` it includes all protocol fields, even when their value IS default. 4. `__repr__` does not use the `__pprint_funcs__` and returns raw values. See below how `src` and `dst` IP addresses get human readable interpretation with `pprint()`, but not with `__repr__`. ```python # repr() >>> ip IP(len=34, p=17, sum=29376, src=b'\x01\x02\x03\x04', dst=b'\x01\x02\x03\x04', opts=b'', data=UDP(sport=111, dport=222, ulen=14, sum=48949, data=b'foobar')) # IP version field is default and is not returned by repr() >>> ip.v 4 >>> ip.pprint() IP( v=4, hl=5, tos=0, len=34, id=0, rf=0, df=0, mf=0, offset=0, ttl=64, p=17, # UDP sum=29376, src=b'\x01\x02\x03\x04', # 1.2.3.4 dst=b'\x01\x02\x03\x04', # 1.2.3.4 opts=b'', data=UDP( sport=111, dport=222, ulen=14, sum=48949, data=b'foobar' ) # UDP ) # IP ``` dpkt-1.9.8/docs/index.md000066400000000000000000000014061427735150300150640ustar00rootroot00000000000000 # dpkt The dpkt project is a python module for fast, simple packet parsing, with definitions for the basic TCP/IP protocols. ### Installation ``` pip install dpkt ``` ### Examples - [Print Packets](print_packets.md) - [Print ICMP](print_icmp.md) - [Print HTTP](print_http_requests.md) ### Documentation - [DPKT Key Concepts for Creating Parsers](creating_parsers.md) - [Changelog](changelog.md) - [Authors](authors.md) - [Contributing](contributing.md) - [Project Plans (TBD)](plans.md) - [Admin Notes](admin_notes.md) ## About This code is based on [dpkt code](https://code.google.com/p/dpkt/) lead by Dug Song and is now being maintained and improved by an extended set of [developers](https://github.com/kbandla/dpkt/graphs/contributors). ## LICENSE BSD 3-Clause dpkt-1.9.8/docs/license.md000066400000000000000000000000711427735150300153740ustar00rootroot00000000000000# License BSD 3-Clause License, as the upstream project dpkt-1.9.8/docs/plans.md000066400000000000000000000000541427735150300150700ustar00rootroot00000000000000# Development plans TBD: Insert Stuff Here dpkt-1.9.8/docs/print_http_requests.md000066400000000000000000000075671427735150300201210ustar00rootroot00000000000000# Print HTTP Requests Example This example expands on the print\_packets example. It checks for HTTP request headers and displays their contents. **NOTE:** We are not reconstructing 'flows' so the request (and response if you tried to parse it) will only parse correctly if they fit within a single packet. Requests can often fit in a single packet but Responses almost never will. For proper reconstruction of flows you may want to look at other projects that use DPKT ( and others) **Code Excerpt** ``` python # For each packet in the pcap process the contents for timestamp, buf in pcap: # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print 'Non IP Packet type not supported %s\n' % eth.data.__class__.__name__ continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Check for TCP in the transport layer if isinstance(ip.data, dpkt.tcp.TCP): # Set the TCP data tcp = ip.data # Now see if we can parse the contents as a HTTP request try: request = dpkt.http.Request(tcp.data) except (dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError): continue # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print 'Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp)) print 'Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type print 'IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % \ (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset) print 'HTTP request: %s\n' % repr(request) ``` **Example Output** ``` Timestamp: 2004-05-13 10:17:08.222534 Ethernet Frame: 00:00:01:00:00:00 fe:ff:20:00:01:00 2048 IP: 145.254.160.237 -> 65.208.228.223 (len=519 ttl=128 DF=1 MF=0 offset=0) HTTP request: Request(body='', uri='/download.html', headers={'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip,deflate', 'connection': 'keep-alive', 'keep-alive': '300', 'accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1', 'user-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113', 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'host': 'www.ethereal.com', 'referer': 'http://www.ethereal.com/development.html'}, version='1.1', data='', method='GET') Timestamp: 2004-05-13 10:17:10.295515 Ethernet Frame: 00:00:01:00:00:00 fe:ff:20:00:01:00 2048 IP: 145.254.160.237 -> 216.239.59.99 (len=761 ttl=128 DF=1 MF=0 offset=0) HTTP request: Request(body='', uri='/pagead/ads?client=ca-pub-2309191948673629&random=1084443430285&lmt=1082467020&format=468x60_as&output=html&url=http%3A%2F%2Fwww.ethereal.com%2Fdownload.html&color_bg=FFFFFF&color_text=333333&color_link=000000&color_url=666633&color_border=666633', headers={'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip,deflate', 'connection': 'keep-alive', 'keep-alive': '300', 'accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1', 'user-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113', 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'host': 'pagead2.googlesyndication.com', 'referer': 'http://www.ethereal.com/download.html'}, version='1.1', data='', method='GET') ... ``` **See full code at: ** dpkt-1.9.8/docs/print_icmp.md000066400000000000000000000044741427735150300161310ustar00rootroot00000000000000# Print ICMP Example This example expands on the print\_packets example. It checks for ICMP packets and displays the ICMP contents. **Code Excerpt** ``` python # For each packet in the pcap process the contents for timestamp, buf in pcap: # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print 'Non IP Packet type not supported %s\n' % eth.data.__class__.__name__ continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Now check if this is an ICMP packet if isinstance(ip.data, dpkt.icmp.ICMP): icmp = ip.data # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print 'Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp)) print 'Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type print 'IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % \ (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset) print 'ICMP: type:%d code:%d checksum:%d data: %s\n' % (icmp.type, icmp.code, icmp.sum, repr(icmp.data)) ``` **Example Output** ``` Timestamp: 2013-05-30 22:45:17.283187 Ethernet Frame: 60:33:4b:13:c5:58 02:1a:11:f0:c8:3b 2048 IP: 192.168.43.9 -> 8.8.8.8 (len=84 ttl=64 DF=0 MF=0 offset=0) ICMP: type:8 code:0 checksum:48051 data: Echo(id=55099, data='Q\xa7\xd6}\x00\x04Q\xe4\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') Timestamp: 2013-05-30 22:45:17.775391 Ethernet Frame: 02:1a:11:f0:c8:3b 60:33:4b:13:c5:58 2048 IP: 8.8.8.8 -> 192.168.43.9 (len=84 ttl=40 DF=0 MF=0 offset=0) ICMP: type:0 code:0 checksum:50099 data: Echo(id=55099, data='Q\xa7\xd6}\x00\x04Q\xe4\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') ... ``` **Set full code at: ** dpkt-1.9.8/docs/print_packets.md000066400000000000000000000044621427735150300166300ustar00rootroot00000000000000# Print Packets Example This example uses DPKT to read in a pcap file and print out the contents of the packets. This example is focused on the fields in the Ethernet Frame and IP packet. **Code Excerpt** ``` python # For each packet in the pcap process the contents for timestamp, buf in pcap: # Print out the timestamp in UTC print 'Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp)) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) print 'Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type # Make sure the Ethernet frame contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print 'Non IP Packet type not supported %s\n' % eth.data.__class__.__name__ continue # Now access the data within the Ethernet frame (the IP packet) # Pulling out src, dst, length, fragment info, TTL, and Protocol ip = eth.data # Print out the info, including the fragment flags and offset print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, ip.df, ip.mf, ip.offset)) # Pretty print the last packet print('** Pretty print demo **\n') eth.pprint() ``` **Example Output** ``` Timestamp: 2004-05-13 10:17:07.311224 Ethernet Frame: 00:00:01:00:00:00 fe:ff:20:00:01:00 2048 IP: 145.254.160.237 -> 65.208.228.223 (len=48 ttl=128 DF=1 MF=0 offset=0) Timestamp: 2004-05-13 10:17:08.222534 Ethernet Frame: fe:ff:20:00:01:00 00:00:01:00:00:00 2048 IP: 65.208.228.223 -> 145.254.160.237 (len=48 ttl=47 DF=1 MF=0 offset=0) ** Pretty print demo ** Ethernet( dst=b'\x00\x00\x01\x00\x00\x00', # 00:00:01:00:00:00 src=b'\xfe\xff \x00\x01\x00', # fe:ff:20:00:01:00 type=2048, data=IP( v=4, hl=5, tos=0, len=40, id=0, off=16384, ttl=47, p=6, sum=62004, # 0xf234 src=b'A\xd0\xe4\xdf', # 65.208.228.223 dst=b'\x91\xfe\xa0\xed', # 145.254.160.237 opts=b'', data=TCP( sport=80, dport=3372, seq=290236745, ack=951058420, off=5, flags=16, # ACK win=6432, sum=15459, # 0x3c63 urp=0, opts=b'', ) # TCP ) # IP ) # Ethernet ``` **See full code at: ** dpkt-1.9.8/dpkt/000077500000000000000000000000001427735150300134445ustar00rootroot00000000000000dpkt-1.9.8/dpkt/__init__.py000066400000000000000000000034661427735150300155660ustar00rootroot00000000000000"""fast, simple packet creation and parsing.""" from __future__ import absolute_import from __future__ import division import sys __author__ = 'Various' __author_email__ = '' __license__ = 'BSD-3-Clause' __url__ = 'https://github.com/kbandla/dpkt' __version__ = '1.9.8' from .dpkt import * from . import ah from . import aoe from . import aim from . import arp from . import asn1 from . import bgp from . import cdp from . import dhcp from . import diameter from . import dns from . import dtp from . import esp from . import ethernet from . import gre from . import gzip from . import h225 from . import hsrp from . import http from . import http2 from . import icmp from . import icmp6 from . import ieee80211 from . import igmp from . import ip from . import ip6 from . import ipx from . import llc from . import loopback from . import mrt from . import netbios from . import netflow from . import ntp from . import ospf from . import pcap from . import pcapng from . import pim from . import pmap from . import ppp from . import pppoe from . import qq from . import radiotap from . import radius from . import rfb from . import rip from . import rpc from . import rtp from . import rx from . import sccp from . import sctp from . import sip from . import sll from . import sll2 from . import smb from . import ssl from . import stp from . import stun from . import tcp from . import telnet from . import tftp from . import tns from . import tpkt from . import udp from . import vrrp from . import yahoo # Note: list() is used to get a copy of the dict in order to avoid # "RuntimeError: dictionary changed size during iteration" # exception in Python 3 caused by _mod_init() funcs that load another modules for name, mod in list(sys.modules.items()): if name.startswith('dpkt.') and hasattr(mod, '_mod_init'): mod._mod_init() dpkt-1.9.8/dpkt/ah.py000066400000000000000000000043311427735150300144070ustar00rootroot00000000000000# $Id: ah.py 34 2007-01-28 07:54:20Z dugsong $ # -*- coding: utf-8 -*- """Authentication Header.""" from __future__ import absolute_import from . import dpkt from . import ip class AH(dpkt.Packet): """Authentication Header. The Authentication Header (AH) protocol provides data origin authentication, data integrity, and replay protection. Attributes: __hdr__: Header fields of AH. auth: Authentication body. data: Message data. """ __hdr__ = ( ('nxt', 'B', 0), ('len', 'B', 0), # payload length ('rsvd', 'H', 0), ('spi', 'I', 0), ('seq', 'I', 0) ) auth = b'' def unpack(self, buf): dpkt.Packet.unpack(self, buf) auth_len = max(4*self.len - 4, 0) # see RFC 4302, section 2.2 self.auth = self.data[:auth_len] buf = self.data[auth_len:] try: self.data = ip.IP.get_proto(self.nxt)(buf) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): self.data = buf def __len__(self): return self.__hdr_len__ + len(self.auth) + len(self.data) def __bytes__(self): return self.pack_hdr() + bytes(self.auth) + bytes(self.data) def test_default_creation(): ah = AH() assert ah.nxt == 0 assert ah.len == 0 assert ah.rsvd == 0 assert ah.spi == 0 assert ah.seq == 0 assert len(ah) == ah.__hdr_len__ assert bytes(ah) == b'\x00' * 12 def test_creation_from_buf(): from binascii import unhexlify buf_ip = unhexlify( '04' # IP '0000000000000000000000' '4500002200000000401172c001020304' '01020304006f00de000ebf35666f6f626172' ) ah = AH(buf_ip) assert ah.nxt == 4 # IP assert isinstance(ah.data, ip.IP) assert len(ah) == 46 assert bytes(ah) == buf_ip buf_not_ip = unhexlify( '37' # Not registered '0000000000000000000000' '4500002200000000401172c001020304' '01020304006f00de000ebf35666f6f626172' ) ah_not_ip = AH(buf_not_ip) assert ah_not_ip.nxt == 0x37 assert isinstance(ah_not_ip.data, bytes) assert len(ah_not_ip) == 46 assert bytes(ah_not_ip) == buf_not_ip dpkt-1.9.8/dpkt/aim.py000066400000000000000000000123511427735150300145660ustar00rootroot00000000000000# $Id: aim.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """AOL Instant Messenger.""" from __future__ import absolute_import import struct from . import dpkt # OSCAR: http://iserverd1.khstu.ru/oscar/ class FLAP(dpkt.Packet): """Frame Layer Protocol. See more about the FLAP on https://en.wikipedia.org/wiki/OSCAR_protocol#FLAP_header Attributes: __hdr__: Header fields of FLAP. data: Message data. """ __hdr__ = ( ('ast', 'B', 0x2a), # '*' ('type', 'B', 0), ('seq', 'H', 0), ('len', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.ast != 0x2a: raise dpkt.UnpackError('invalid FLAP header') if len(self.data) < self.len: raise dpkt.NeedData('%d left, %d needed' % (len(self.data), self.len)) class SNAC(dpkt.Packet): """Simple Network Atomic Communication. See more about the SNAC on https://en.wikipedia.org/wiki/OSCAR_protocol#SNAC_data Attributes: __hdr__: Header fields of SNAC. """ __hdr__ = ( ('family', 'H', 0), ('subtype', 'H', 0), ('flags', 'H', 0), ('reqid', 'I', 0) ) def tlv(buf): n = 4 try: t, l_ = struct.unpack('>HH', buf[:n]) except struct.error: raise dpkt.UnpackError('invalid type, length fields') v = buf[n:n + l_] if len(v) < l_: raise dpkt.NeedData('%d left, %d needed' % (len(v), l_)) buf = buf[n + l_:] return t, l_, v, buf # TOC 1.0: http://jamwt.com/Py-TOC/PROTOCOL # TOC 2.0: http://www.firestuff.org/projects/firetalk/doc/toc2.txt def testAIM(): testdata = ( b'\x2a\x02\xac\xf3\x00\x81\x00\x03\x00\x0b\x00\x00\xfa\x45\x55\x64\x0e\x75\x73\x72\x6e\x61' b'\x6d\x65\x72\x65\x6d\x6f\x76\x65\x64\x00\x00\x00\x0a\x00\x01\x00\x02\x12\x90\x00\x44\x00' b'\x01\x00\x00\x03\x00\x04\x58\x90\x54\x36\x00\x45\x00\x04\x00\x00\x0f\x93\x00\x21\x00\x08' b'\x00\x85\x00\x7d\x00\x7d\x00\x00\x00\x41\x00\x01\x00\x00\x37\x00\x04\x00\x00\x00\x00\x00' b'\x0d\x00\x00\x00\x19\x00\x00\x00\x1d\x00\x24\x00\x00\x00\x05\x02\x01\xd2\x04\x72\x00\x01' b'\x00\x05\x02\x01\xd2\x04\x72\x00\x03\x00\x05\x2b\x00\x00\x2a\xcc\x00\x81\x00\x05\x2b\x00' b'\x00\x13\xf1' ) flap = FLAP(testdata) assert flap.ast == 0x2a assert flap.type == 0x02 assert flap.seq == 44275 assert flap.len == 129 assert flap.data == ( b'\x00\x03\x00\x0b\x00\x00\xfa\x45\x55\x64\x0e\x75\x73\x72\x6e\x61\x6d\x65\x72\x65\x6d\x6f' b'\x76\x65\x64\x00\x00\x00\x0a\x00\x01\x00\x02\x12\x90\x00\x44\x00\x01\x00\x00\x03\x00\x04' b'\x58\x90\x54\x36\x00\x45\x00\x04\x00\x00\x0f\x93\x00\x21\x00\x08\x00\x85\x00\x7d\x00\x7d' b'\x00\x00\x00\x41\x00\x01\x00\x00\x37\x00\x04\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x19\x00' b'\x00\x00\x1d\x00\x24\x00\x00\x00\x05\x02\x01\xd2\x04\x72\x00\x01\x00\x05\x02\x01\xd2\x04' b'\x72\x00\x03\x00\x05\x2b\x00\x00\x2a\xcc\x00\x81\x00\x05\x2b\x00\x00\x13\xf1' ) snac = SNAC(flap.data) assert snac.family == 3 assert snac.subtype == 11 assert snac.flags == 0 assert snac.reqid == 0xfa455564 assert snac.data == ( b'\x0e\x75\x73\x72\x6e\x61\x6d\x65\x72\x65\x6d\x6f\x76\x65\x64\x00\x00\x00\x0a\x00\x01\x00' b'\x02\x12\x90\x00\x44\x00\x01\x00\x00\x03\x00\x04\x58\x90\x54\x36\x00\x45\x00\x04\x00\x00' b'\x0f\x93\x00\x21\x00\x08\x00\x85\x00\x7d\x00\x7d\x00\x00\x00\x41\x00\x01\x00\x00\x37\x00' b'\x04\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x19\x00\x00\x00\x1d\x00\x24\x00\x00\x00\x05\x02' b'\x01\xd2\x04\x72\x00\x01\x00\x05\x02\x01\xd2\x04\x72\x00\x03\x00\x05\x2b\x00\x00\x2a\xcc' b'\x00\x81\x00\x05\x2b\x00\x00\x13\xf1' ) # skip over the buddyname and TLV count in Oncoming Buddy message tlvdata = snac.data[19:] tlvCount = 0 while tlvdata: t, l_, v, tlvdata = tlv(tlvdata) tlvCount += 1 if tlvCount == 1: # just check function return for first TLV assert t == 0x01 assert l_ == 2 assert v == b'\x12\x90' assert tlvdata == ( b'\x00\x44\x00\x01\x00\x00\x03\x00\x04\x58\x90\x54\x36\x00\x45\x00\x04\x00\x00\x0f' b'\x93\x00\x21\x00\x08\x00\x85\x00\x7d\x00\x7d\x00\x00\x00\x41\x00\x01\x00\x00\x37' b'\x00\x04\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x19\x00\x00\x00\x1d\x00\x24\x00\x00' b'\x00\x05\x02\x01\xd2\x04\x72\x00\x01\x00\x05\x02\x01\xd2\x04\x72\x00\x03\x00\x05' b'\x2b\x00\x00\x2a\xcc\x00\x81\x00\x05\x2b\x00\x00\x13\xf1' ) # make sure we extracted 10 TLVs assert tlvCount == 10 def testExceptions(): testdata = b'xxxxxx' try: FLAP(testdata) except dpkt.UnpackError as e: assert str(e) == 'invalid FLAP header' testdata = b'*\x02\x12\x34\x00\xff' try: FLAP(testdata) except dpkt.NeedData as e: assert str(e) == '0 left, 255 needed' try: t, l_, v, _ = tlv(b'x') except dpkt.UnpackError as e: assert str(e) == 'invalid type, length fields' try: t, l_, v, _ = tlv(b'\x00\x01\x00\xff') except dpkt.NeedData as e: assert str(e) == '0 left, 255 needed' dpkt-1.9.8/dpkt/aoe.py000066400000000000000000000066041427735150300145700ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ATA over Ethernet Protocol.""" from __future__ import absolute_import import struct from . import dpkt from .compat import iteritems class AOE(dpkt.Packet): """ATA over Ethernet Protocol. See more about the AOE on https://en.wikipedia.org/wiki/ATA_over_Ethernet Attributes: __hdr__: Header fields of AOE. data: Message data. """ __hdr__ = ( ('_ver_fl', 'B', 0x10), ('err', 'B', 0), ('maj', 'H', 0), ('min', 'B', 0), ('cmd', 'B', 0), ('tag', 'I', 0), ) __bit_fields__ = { '_ver_fl': ( ('ver', 4), ('fl', 4), ) } _cmdsw = {} @classmethod def set_cmd(cls, cmd, pktclass): cls._cmdsw[cmd] = pktclass @classmethod def get_cmd(cls, cmd): return cls._cmdsw[cmd] def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: self.data = self._cmdsw[self.cmd](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, struct.error, dpkt.UnpackError): pass AOE_CMD_ATA = 0 AOE_CMD_CFG = 1 AOE_FLAG_RSP = 1 << 3 def _load_cmds(): prefix = 'AOE_CMD_' g = globals() for k, v in iteritems(g): if k.startswith(prefix): name = 'aoe' + k[len(prefix):].lower() try: mod = __import__(name, g, level=1) AOE.set_cmd(v, getattr(mod, name.upper())) except (ImportError, AttributeError): continue def _mod_init(): """Post-initialization called when all dpkt modules are fully loaded""" if not AOE._cmdsw: _load_cmds() def test_creation(): aoe = AOE() # hdr fields assert aoe._ver_fl == 0x10 assert aoe.err == 0 assert aoe.maj == 0 assert aoe.min == 0 assert aoe.cmd == 0 assert aoe.tag == 0 assert bytes(aoe) == b'\x10' + b'\x00' * 9 def test_properties(): aoe = AOE() # property getters assert aoe.ver == 1 assert aoe.fl == 0 # property setters aoe.ver = 2 assert aoe.ver == 2 assert aoe._ver_fl == 0x20 aoe.fl = 12 assert aoe.fl == 12 assert aoe._ver_fl == 0x2C def test_unpack(): from binascii import unhexlify buf = unhexlify( '1000000000' '00' # cmd: AOE_CMD_ATA '00000000' # tag ) aoe = AOE(buf) # AOE_CMD_ATA specified, but no data supplied assert aoe.data == b'' buf = unhexlify( '1000000000' '00' # cmd: AOE_CMD_ATA '00000000' # tag # AOEDATA specification '030a6b190000000045000028941f0000e30699b4232b2400de8e8442abd100500035e1' '2920d9000000229bf0e204656b' ) aoe = AOE(buf) assert aoe.aoeata == aoe.data def test_cmds(): import dpkt assert AOE.get_cmd(AOE_CMD_ATA) == dpkt.aoeata.AOEATA assert AOE.get_cmd(AOE_CMD_CFG) == dpkt.aoecfg.AOECFG def test_cmd_loading(): # this test checks that failing to load a module isn't catastrophic standard_cmds = AOE._cmdsw # delete the existing code->module mappings AOE._cmdsw = {} assert not AOE._cmdsw # create a new global constant pointing to a module which doesn't exist globals()['AOE_CMD_FAIL'] = "FAIL" _mod_init() # check that the same modules were loaded, ignoring the fail assert AOE._cmdsw == standard_cmds dpkt-1.9.8/dpkt/aoeata.py000066400000000000000000000021521427735150300152500ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ATA over Ethernet ATA command""" from __future__ import print_function from __future__ import absolute_import from . import dpkt ATA_DEVICE_IDENTIFY = 0xec class AOEATA(dpkt.Packet): """ATA over Ethernet ATA command. See more about the AOEATA on https://en.wikipedia.org/wiki/ATA_over_Ethernet Attributes: __hdr__: Header fields of AOEATA. data: Message data. """ __hdr__ = ( ('aflags', 'B', 0), ('errfeat', 'B', 0), ('scnt', 'B', 0), ('cmdstat', 'B', ATA_DEVICE_IDENTIFY), ('lba0', 'B', 0), ('lba1', 'B', 0), ('lba2', 'B', 0), ('lba3', 'B', 0), ('lba4', 'B', 0), ('lba5', 'B', 0), ('res', 'H', 0), ) # XXX: in unpack, switch on ATA command like icmp does on type def test_aoeata(): s = (b'\x03\x0a\x6b\x19\x00\x00\x00\x00\x45\x00\x00\x28\x94\x1f\x00\x00\xe3\x06\x99\xb4\x23\x2b' b'\x24\x00\xde\x8e\x84\x42\xab\xd1\x00\x50\x00\x35\xe1\x29\x20\xd9\x00\x00\x00\x22\x9b\xf0\xe2\x04\x65\x6b') aoeata = AOEATA(s) assert (bytes(aoeata) == s) dpkt-1.9.8/dpkt/aoecfg.py000066400000000000000000000014741427735150300152500ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ATA over Ethernet ATA command""" from __future__ import print_function from __future__ import absolute_import from . import dpkt class AOECFG(dpkt.Packet): """ATA over Ethernet ATA command. See more about the AOE on \ https://en.wikipedia.org/wiki/ATA_over_Ethernet Attributes: __hdr__: Header fields of AOECFG. data: Message data. """ __hdr__ = ( ('bufcnt', 'H', 0), ('fwver', 'H', 0), ('scnt', 'B', 0), ('aoeccmd', 'B', 0), ('cslen', 'H', 0), ) def test_aoecfg(): s = (b'\x01\x02\x03\x04\x05\x06\x11\x12\x13\x14\x15\x16\x88\xa2\x10\x00\x00\x01\x02\x01\x80' b'\x00\x00\x00\x12\x34\x00\x00\x00\x00\x04\x00' + b'\0xed' * 1024) aoecfg = AOECFG(s[14 + 10:]) assert (aoecfg.bufcnt == 0x1234) dpkt-1.9.8/dpkt/arp.py000066400000000000000000000021671427735150300146060ustar00rootroot00000000000000# $Id: arp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Address Resolution Protocol.""" from __future__ import absolute_import from . import dpkt # Hardware address format ARP_HRD_ETH = 0x0001 # ethernet hardware ARP_HRD_IEEE802 = 0x0006 # IEEE 802 hardware # Protocol address format ARP_PRO_IP = 0x0800 # IP protocol # ARP operation ARP_OP_REQUEST = 1 # request to resolve ha given pa ARP_OP_REPLY = 2 # response giving hardware address ARP_OP_REVREQUEST = 3 # request to resolve pa given ha ARP_OP_REVREPLY = 4 # response giving protocol address class ARP(dpkt.Packet): """Address Resolution Protocol. See more about the ARP on \ https://en.wikipedia.org/wiki/Address_Resolution_Protocol Attributes: __hdr__: Header fields of ARP. """ __hdr__ = ( ('hrd', 'H', ARP_HRD_ETH), ('pro', 'H', ARP_PRO_IP), ('hln', 'B', 6), # hardware address length ('pln', 'B', 4), # protocol address length ('op', 'H', ARP_OP_REQUEST), ('sha', '6s', b''), ('spa', '4s', b''), ('tha', '6s', b''), ('tpa', '4s', b'') ) dpkt-1.9.8/dpkt/asn1.py000066400000000000000000000214631427735150300146660ustar00rootroot00000000000000# $Id: asn1.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Abstract Syntax Notation #1.""" from __future__ import absolute_import from __future__ import print_function import struct from calendar import timegm from . import dpkt from .compat import compat_ord # Type class CLASSMASK = 0xc0 UNIVERSAL = 0x00 APPLICATION = 0x40 CONTEXT = 0x80 PRIVATE = 0xc0 # Constructed (vs. primitive) CONSTRUCTED = 0x20 # Universal-class tags TAGMASK = 0x1f INTEGER = 2 BIT_STRING = 3 # arbitrary bit string OCTET_STRING = 4 # arbitrary octet string NULL = 5 OID = 6 # object identifier SEQUENCE = 16 # ordered collection of types SET = 17 # unordered collection of types PRINT_STRING = 19 # printable string T61_STRING = 20 # T.61 (8-bit) character string IA5_STRING = 22 # ASCII UTC_TIME = 23 def utctime(buf): """Convert ASN.1 UTCTime string to UTC float. TODO: Long description here. Args: buf: A buffer with format "yymnddhhmm" Returns: A floating point number, indicates seconds since the Epoch. """ yy = int(buf[:2]) mn = int(buf[2:4]) dd = int(buf[4:6]) hh = int(buf[6:8]) mm = int(buf[8:10]) try: ss = int(buf[10:12]) buf = buf[12:] except TypeError: ss = 0 buf = buf[10:] if buf[0] == '+': hh -= int(buf[1:3]) mm -= int(buf[3:5]) elif buf[0] == '-': hh += int(buf[1:3]) mm += int(buf[3:5]) return timegm((2000 + yy, mn, dd, hh, mm, ss, 0, 0, 0)) def decode(buf): """Sleazy ASN.1 decoder. TODO: Long description here. Args: buf: A buffer with Sleazy ASN.1 data. Returns: A list of (id, value) tuples from ASN.1 BER/DER encoded buffer. Raises: UnpackError: An error occurred the ASN.1 length exceed. """ msg = [] while buf: t = compat_ord(buf[0]) constructed = t & CONSTRUCTED tag = t & TAGMASK l_ = compat_ord(buf[1]) c = 0 if constructed and l_ == 128: # XXX - constructed, indefinite length msg.append((t, decode(buf[2:]))) elif l_ >= 128: c = l_ & 127 if c == 1: l_ = compat_ord(buf[2]) elif c == 2: l_ = struct.unpack('>H', buf[2:4])[0] elif c == 3: l_ = struct.unpack('>I', buf[1:5])[0] & 0xfff c = 2 elif c == 4: l_ = struct.unpack('>I', buf[2:6])[0] else: # XXX - can be up to 127 bytes, but... raise dpkt.UnpackError('excessive long-form ASN.1 length %d' % l_) # Skip type, length buf = buf[2 + c:] # Parse content if constructed: msg.append((t, decode(buf))) elif tag == INTEGER: if l_ == 0: n = 0 elif l_ == 1: n = compat_ord(buf[0]) elif l_ == 2: n = struct.unpack('>H', buf[:2])[0] elif l_ == 3: n = struct.unpack('>I', buf[:4])[0] >> 8 elif l_ == 4: n = struct.unpack('>I', buf[:4])[0] else: raise dpkt.UnpackError('excessive integer length > %d bytes' % l_) msg.append((t, n)) elif tag == UTC_TIME: msg.append((t, utctime(buf[:l_]))) else: msg.append((t, buf[:l_])) # Skip content buf = buf[l_:] return msg def test_asn1(): s = ( b'0\x82\x02Q\x02\x01\x0bc\x82\x02J\x04xcn=Douglas J Song 1, ou=Information Technology Division,' b' ou=Faculty and Staff, ou=People, o=University of Michigan, c=US\n\x01\x00\n\x01\x03\x02\x01' b'\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectclass0\x82\x01\xb0\x04\rmemberOfGroup\x04\x03acl' b'\x04\x02cn\x04\x05title\x04\rpostalAddress\x04\x0ftelephoneNumber\x04\x04mail\x04\x06member' b'\x04\thomePhone\x04\x11homePostalAddress\x04\x0bobjectClass\x04\x0bdescription\x04\x18' b'facsimileTelephoneNumber\x04\x05pager\x04\x03uid\x04\x0cuserPassword\x04\x08joinable\x04\x10' b'associatedDomain\x04\x05owner\x04\x0erfc822ErrorsTo\x04\x08ErrorsTo\x04\x10rfc822RequestsTo\x04\n' b'RequestsTo\x04\tmoderator\x04\nlabeledURL\x04\nonVacation\x04\x0fvacationMessage\x04\x05drink\x04\x0e' b'lastModifiedBy\x04\x10lastModifiedTime\x04\rmodifiersname\x04\x0fmodifytimestamp\x04\x0ccreatorsname' b'\x04\x0fcreatetimestamp' ) assert decode(s) == [ (48, [ (2, 11), (99, [ (4, ( b'cn=Douglas J Song 1, ' b'ou=Information Technology Division, ' b'ou=Faculty and Staff, ' b'ou=People, ' b'o=University of Michigan, ' b'c=US' )), (10, b'\x00'), (10, b'\x03'), (2, 0), (2, 0), (1, b'\x00'), (135, b'objectclass'), (48, [ (4, b'memberOfGroup'), (4, b'acl'), (4, b'cn'), (4, b'title'), (4, b'postalAddress'), (4, b'telephoneNumber'), (4, b'mail'), (4, b'member'), (4, b'homePhone'), (4, b'homePostalAddress'), (4, b'objectClass'), (4, b'description'), (4, b'facsimileTelephoneNumber'), (4, b'pager'), (4, b'uid'), (4, b'userPassword'), (4, b'joinable'), (4, b'associatedDomain'), (4, b'owner'), (4, b'rfc822ErrorsTo'), (4, b'ErrorsTo'), (4, b'rfc822RequestsTo'), (4, b'RequestsTo'), (4, b'moderator'), (4, b'labeledURL'), (4, b'onVacation'), (4, b'vacationMessage'), (4, b'drink'), (4, b'lastModifiedBy'), (4, b'lastModifiedTime'), (4, b'modifiersname'), (4, b'modifytimestamp'), (4, b'creatorsname'), (4, b'createtimestamp'), ]) ]) ]) ] def test_utctime(): buf = ( '201005' # yymndd '012345' # hhmmss '+1234' # +hhmm ) assert utctime(buf) == 1601815785.0 buf = ( '201005' # yymndd '012345' # hhmmss '-1234' # -hhmm ) assert utctime(buf) == 1601906265.0 def test_decode(): import pytest from binascii import unhexlify buf = unhexlify( '20' # CONSTRUCTED '80' # 128 | 0 ) assert decode(buf) == [(32, []), (32, [])] # unpacking UTC_TIME buf = unhexlify( '17' # t: code: UTC_TIME '81' # l_: code: 128 | 1 (constructed '22' # data len '3230313030353031323334352b30303030' ) assert decode(buf) == [(23, 1601861025.0)] # unpacking 2-byte size; zero-length integer buf = unhexlify( '02' # t: INTEGER '82' # l_: 128 | 2 '0000' # new l_ ) assert decode(buf) == [(2, 0)] # unpacking 3-byte size buf = unhexlify( '02' # t: INTEGER '83' # l_: 128 | 3 '000001' # new l_ ) assert decode(buf) == [(2, 1)] # unpacking 4-byte size buf = unhexlify( '02' # t: INTEGER '84' # l_: 128 | 4 '00000002' # new l_ 'abcd' ) assert decode(buf) == [(2, 43981)] # unpacking 4-byte size buf = unhexlify( '02' # t: INTEGER '85' # l_: 128 | 5 ) with pytest.raises(dpkt.UnpackError, match="excessive long-form ASN.1 length 133"): decode(buf) # unpacking 1-byte size; 4-byte integer buf = unhexlify( '02' # t: INTEGER '81' # l_: 128 | 1 '04' # new l_ '12345678' # integer ) assert decode(buf) == [(2, 305419896)] # unpacking 1-byte size; 4-byte integer buf = unhexlify( '02' # t: INTEGER '81' # l_: 128 | 1 '05' # new l_ ) with pytest.raises(dpkt.UnpackError, match="excessive integer length > 5 bytes"): decode(buf) # unpacking 1-byte size; 3-byte integer buf = unhexlify( '02' # t: INTEGER '81' # l_: 128 | 1 '03' # new l_ '123456' # integer '02' # t: INTEGER '81' # l_: 128 | 1 '00' # new l_ ) assert decode(buf) == [ (2, 1193046), (2, 0), ] dpkt-1.9.8/dpkt/bgp.py000077500000000000000000001442271427735150300146030ustar00rootroot00000000000000# $Id: bgp.py 76 2011-01-06 15:51:30Z dugsong $ # -*- coding: utf-8 -*- """Border Gateway Protocol.""" from __future__ import print_function from __future__ import absolute_import import struct import socket from . import dpkt from .compat import compat_ord # Border Gateway Protocol 4 - RFC 4271 # Communities Attribute - RFC 1997 # Capabilities - RFC 3392 # Route Refresh - RFC 2918 # Route Reflection - RFC 4456 # Confederations - RFC 3065 # Cease Subcodes - RFC 4486 # NOPEER Community - RFC 3765 # Multiprotocol Extensions - 2858 # Advertisement of Multiple Paths in BGP - RFC 7911 # BGP Support for Four-Octet Autonomous System (AS) Number Spac - RFC 6793 # Message Types OPEN = 1 UPDATE = 2 NOTIFICATION = 3 KEEPALIVE = 4 ROUTE_REFRESH = 5 # Attribute Types ORIGIN = 1 AS_PATH = 2 NEXT_HOP = 3 MULTI_EXIT_DISC = 4 LOCAL_PREF = 5 ATOMIC_AGGREGATE = 6 AGGREGATOR = 7 COMMUNITIES = 8 ORIGINATOR_ID = 9 CLUSTER_LIST = 10 MP_REACH_NLRI = 14 MP_UNREACH_NLRI = 15 # Origin Types ORIGIN_IGP = 0 ORIGIN_EGP = 1 INCOMPLETE = 2 # AS Path Types AS_SET = 1 AS_SEQUENCE = 2 AS_CONFED_SEQUENCE = 3 AS_CONFED_SET = 4 # Reserved Communities Types NO_EXPORT = 0xffffff01 NO_ADVERTISE = 0xffffff02 NO_EXPORT_SUBCONFED = 0xffffff03 NO_PEER = 0xffffff04 # Common AFI types AFI_IPV4 = 1 AFI_IPV6 = 2 AFI_L2VPN = 25 # Multiprotocol SAFI types SAFI_UNICAST = 1 SAFI_MULTICAST = 2 SAFI_UNICAST_MULTICAST = 3 SAFI_EVPN = 70 # OPEN Message Optional Parameters AUTHENTICATION = 1 CAPABILITY = 2 # Capability Types CAP_MULTIPROTOCOL = 1 CAP_ROUTE_REFRESH = 2 # NOTIFICATION Error Codes MESSAGE_HEADER_ERROR = 1 OPEN_MESSAGE_ERROR = 2 UPDATE_MESSAGE_ERROR = 3 HOLD_TIMER_EXPIRED = 4 FSM_ERROR = 5 CEASE = 6 # Message Header Error Subcodes CONNECTION_NOT_SYNCHRONIZED = 1 BAD_MESSAGE_LENGTH = 2 BAD_MESSAGE_TYPE = 3 # OPEN Message Error Subcodes UNSUPPORTED_VERSION_NUMBER = 1 BAD_PEER_AS = 2 BAD_BGP_IDENTIFIER = 3 UNSUPPORTED_OPTIONAL_PARAMETER = 4 AUTHENTICATION_FAILURE = 5 UNACCEPTABLE_HOLD_TIME = 6 UNSUPPORTED_CAPABILITY = 7 # UPDATE Message Error Subcodes MALFORMED_ATTRIBUTE_LIST = 1 UNRECOGNIZED_ATTRIBUTE = 2 MISSING_ATTRIBUTE = 3 ATTRIBUTE_FLAGS_ERROR = 4 ATTRIBUTE_LENGTH_ERROR = 5 INVALID_ORIGIN_ATTRIBUTE = 6 AS_ROUTING_LOOP = 7 INVALID_NEXT_HOP_ATTRIBUTE = 8 OPTIONAL_ATTRIBUTE_ERROR = 9 INVALID_NETWORK_FIELD = 10 MALFORMED_AS_PATH = 11 # Cease Error Subcodes MAX_NUMBER_OF_PREFIXES_REACHED = 1 ADMINISTRATIVE_SHUTDOWN = 2 PEER_DECONFIGURED = 3 ADMINISTRATIVE_RESET = 4 CONNECTION_REJECTED = 5 OTHER_CONFIGURATION_CHANGE = 6 CONNECTION_COLLISION_RESOLUTION = 7 OUT_OF_RESOURCES = 8 class BGP(dpkt.Packet): """Border Gateway Protocol. BGP is an inter-AS routing protocol. See more about the BGP on https://en.wikipedia.org/wiki/Border_Gateway_Protocol Attributes: __hdr__: Header fields of BGP. marker: (bytes): Marker. Included for compatibility, must be set to all ones. (16 bytes) len: (int): Length: Total length of the message in octets, including the header. (2 bytes) type: (int): Type: Type of BGP message. (1 byte) """ __hdr__ = ( ('marker', '16s', '\xff' * 16), ('len', 'H', 0), ('type', 'B', OPEN) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.len - self.__hdr_len__] if self.type == OPEN: self.data = self.open = self.Open(self.data) elif self.type == UPDATE: self.data = self.update = self.Update(self.data) elif self.type == NOTIFICATION: self.data = self.notification = self.Notification(self.data) elif self.type == KEEPALIVE: self.data = self.keepalive = self.Keepalive(self.data) elif self.type == ROUTE_REFRESH: self.data = self.route_refresh = self.RouteRefresh(self.data) class Open(dpkt.Packet): __hdr__ = ( ('v', 'B', 4), ('asn', 'H', 0), ('holdtime', 'H', 0), ('identifier', 'I', 0), ('param_len', 'B', 0) ) __hdr_defaults__ = { 'parameters': [] } def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] plen = self.param_len while plen > 0: param = self.Parameter(self.data) self.data = self.data[len(param):] plen -= len(param) l_.append(param) self.data = self.parameters = l_ def __len__(self): return self.__hdr_len__ + sum(map(len, self.parameters)) def __bytes__(self): params = b''.join(map(bytes, self.parameters)) self.param_len = len(params) return self.pack_hdr() + params class Parameter(dpkt.Packet): __hdr__ = ( ('type', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.len] if self.type == AUTHENTICATION: self.data = self.authentication = self.Authentication(self.data) elif self.type == CAPABILITY: self.data = self.capability = self.Capability(self.data) class Authentication(dpkt.Packet): __hdr__ = ( ('code', 'B', 0), ) class Capability(dpkt.Packet): __hdr__ = ( ('code', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.len] class Update(dpkt.Packet): __hdr_defaults__ = { 'withdrawn': [], 'attributes': [], 'announced': [] } def unpack(self, buf): self.data = buf # Withdrawn Routes wlen = struct.unpack('>H', self.data[:2])[0] self.data = self.data[2:] l_ = [] while wlen > 0: route = RouteIPV4(self.data) self.data = self.data[len(route):] wlen -= len(route) l_.append(route) self.withdrawn = l_ # Path Attributes plen = struct.unpack('>H', self.data[:2])[0] self.data = self.data[2:] l_ = [] while plen > 0: attr = self.Attribute(self.data) self.data = self.data[len(attr):] plen -= len(attr) l_.append(attr) self.attributes = l_ # Announced Routes l_ = [] while self.data: if len(self.data) % 9 == 0: route = ExtendedRouteIPV4(self.data) else: route = RouteIPV4(self.data) self.data = self.data[len(route):] l_.append(route) self.announced = l_ def __len__(self): return 2 + sum(map(len, self.withdrawn)) + \ 2 + sum(map(len, self.attributes)) + \ sum(map(len, self.announced)) def __bytes__(self): return struct.pack('>H', sum(map(len, self.withdrawn))) + \ b''.join(map(bytes, self.withdrawn)) + \ struct.pack('>H', sum(map(len, self.attributes))) + \ b''.join(map(bytes, self.attributes)) + \ b''.join(map(bytes, self.announced)) class Attribute(dpkt.Packet): __hdr__ = ( ('flags', 'B', 0), ('type', 'B', 0) ) @property def optional(self): return (self.flags >> 7) & 0x1 @optional.setter def optional(self, o): self.flags = (self.flags & ~0x80) | ((o & 0x1) << 7) @property def transitive(self): return (self.flags >> 6) & 0x1 @transitive.setter def transitive(self, t): self.flags = (self.flags & ~0x40) | ((t & 0x1) << 6) @property def partial(self): return (self.flags >> 5) & 0x1 @partial.setter def partial(self, p): self.flags = (self.flags & ~0x20) | ((p & 0x1) << 5) @property def extended_length(self): return (self.flags >> 4) & 0x1 @extended_length.setter def extended_length(self, e): self.flags = (self.flags & ~0x10) | ((e & 0x1) << 4) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.extended_length: self.len = struct.unpack('>H', self.data[:2])[0] self.data = self.data[2:] else: self.len = struct.unpack('B', self.data[:1])[0] self.data = self.data[1:] self.data = self.data[:self.len] if self.type == ORIGIN: self.data = self.origin = self.Origin(self.data) elif self.type == AS_PATH: self.data = self.as_path = self.ASPath(self.data) elif self.type == NEXT_HOP: self.data = self.next_hop = self.NextHop(self.data) elif self.type == MULTI_EXIT_DISC: self.data = self.multi_exit_disc = self.MultiExitDisc(self.data) elif self.type == LOCAL_PREF: self.data = self.local_pref = self.LocalPref(self.data) elif self.type == ATOMIC_AGGREGATE: self.data = self.atomic_aggregate = self.AtomicAggregate(self.data) elif self.type == AGGREGATOR: self.data = self.aggregator = self.Aggregator(self.data) elif self.type == COMMUNITIES: self.data = self.communities = self.Communities(self.data) elif self.type == ORIGINATOR_ID: self.data = self.originator_id = self.OriginatorID(self.data) elif self.type == CLUSTER_LIST: self.data = self.cluster_list = self.ClusterList(self.data) elif self.type == MP_REACH_NLRI: self.data = self.mp_reach_nlri = self.MPReachNLRI(self.data) elif self.type == MP_UNREACH_NLRI: self.data = self.mp_unreach_nlri = self.MPUnreachNLRI(self.data) def __len__(self): if self.extended_length: attr_len = 2 else: attr_len = 1 return self.__hdr_len__ + attr_len + len(self.data) def __bytes__(self): if self.extended_length: attr_len_str = struct.pack('>H', self.len) else: attr_len_str = struct.pack('B', self.len) return self.pack_hdr() + attr_len_str + bytes(self.data) class Origin(dpkt.Packet): __hdr__ = ( ('type', 'B', ORIGIN_IGP), ) class ASPath(dpkt.Packet): __hdr_defaults__ = { 'segments': [] } def unpack(self, buf): self.data = buf l_ = [] as4 = len(self.data) == 6 while self.data: if as4: seg = self.ASPathSegment4(self.data) else: seg = self.ASPathSegment(self.data) self.data = self.data[len(seg):] l_.append(seg) self.data = self.segments = l_ def __len__(self): return sum(map(len, self.data)) def __bytes__(self): return b''.join(map(bytes, self.data)) class ASPathSegment(dpkt.Packet): __hdr__ = ( ('type', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] for i in range(self.len): AS = struct.unpack('>H', self.data[:2])[0] self.data = self.data[2:] l_.append(AS) self.data = self.path = l_ def __len__(self): return self.__hdr_len__ + 2 * len(self.path) def __bytes__(self): as_str = b'' for AS in self.path: as_str += struct.pack('>H', AS) return self.pack_hdr() + as_str class ASPathSegment4(dpkt.Packet): __hdr__ = ( ('type', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] for i in range(self.len): if len(self.data) >= 4: AS = struct.unpack('>I', self.data[:4])[0] self.data = self.data[4:] l_.append(AS) self.path = l_ def __len__(self): return self.__hdr_len__ + 4 * len(self.path) def __bytes__(self): as_str = b'' for AS in self.path: as_str += struct.pack('>I', AS) return self.pack_hdr() + as_str class NextHop(dpkt.Packet): __hdr__ = ( ('ip', 'I', 0), ) class MultiExitDisc(dpkt.Packet): __hdr__ = ( ('value', 'I', 0), ) class LocalPref(dpkt.Packet): __hdr__ = ( ('value', 'I', 0), ) class AtomicAggregate(dpkt.Packet): def unpack(self, buf): pass def __len__(self): return 0 def __bytes__(self): return b'' class Aggregator(dpkt.Packet): __hdr__ = ( ('asn', 'H', 0), ('ip', 'I', 0) ) class Communities(dpkt.Packet): __hdr_defaults__ = { 'list': [] } def unpack(self, buf): self.data = buf l_ = [] while self.data: val = struct.unpack('>I', self.data[:4])[0] if (0x00000000 <= val <= 0x0000ffff) or (0xffff0000 <= val <= 0xffffffff): comm = self.ReservedCommunity(self.data[:4]) else: comm = self.Community(self.data[:4]) self.data = self.data[len(comm):] l_.append(comm) self.data = self.list = l_ def __len__(self): return sum(map(len, self.data)) def __bytes__(self): return b''.join(map(bytes, self.data)) class Community(dpkt.Packet): __hdr__ = ( ('asn', 'H', 0), ('value', 'H', 0) ) class ReservedCommunity(dpkt.Packet): __hdr__ = ( ('value', 'I', 0), ) class OriginatorID(dpkt.Packet): __hdr__ = ( ('value', 'I', 0), ) class ClusterList(dpkt.Packet): __hdr_defaults__ = { 'list': [] } def unpack(self, buf): self.data = buf l_ = [] while self.data: id = struct.unpack('>I', self.data[:4])[0] self.data = self.data[4:] l_.append(id) self.data = self.list = l_ def __len__(self): return 4 * len(self.list) def __bytes__(self): cluster_str = b'' for val in self.list: cluster_str += struct.pack('>I', val) return cluster_str class MPReachNLRI(dpkt.Packet): __hdr__ = ( ('afi', 'H', AFI_IPV4), ('safi', 'B', SAFI_UNICAST), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) # Next Hop hop_len = 4 if self.afi == AFI_IPV6: hop_len = 16 l_ = [] nlen = struct.unpack('B', self.data[:1])[0] self.data = self.data[1:] # next_hop is kept for backward compatibility self.next_hop = self.data[:nlen] while nlen > 0: hop = self.data[:hop_len] l_.append(hop) self.data = self.data[hop_len:] nlen -= hop_len self.next_hops = l_ # SNPAs l_ = [] num_snpas = struct.unpack('B', self.data[:1])[0] self.data = self.data[1:] for i in range(num_snpas): snpa = self.SNPA(self.data) self.data = self.data[len(snpa):] l_.append(snpa) self.snpas = l_ if self.afi == AFI_IPV4: Route = RouteIPV4 elif self.afi == AFI_IPV6: Route = RouteIPV6 elif self.afi == AFI_L2VPN: Route = RouteEVPN else: Route = RouteGeneric # Announced Routes l_ = [] while self.data: route = Route(self.data) self.data = self.data[len(route):] l_.append(route) self.data = self.announced = l_ def __len__(self): return self.__hdr_len__ + \ 1 + sum(map(len, self.next_hops)) + \ 1 + sum(map(len, self.snpas)) + \ sum(map(len, self.announced)) def __bytes__(self): return self.pack_hdr() + \ struct.pack('B', sum(map(len, self.next_hops))) + \ b''.join(map(bytes, self.next_hops)) + \ struct.pack('B', len(self.snpas)) + \ b''.join(map(bytes, self.snpas)) + \ b''.join(map(bytes, self.announced)) class SNPA(dpkt.Packet): __hdr__ = ( ('len', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:(self.len + 1) // 2] class MPUnreachNLRI(dpkt.Packet): __hdr__ = ( ('afi', 'H', AFI_IPV4), ('safi', 'B', SAFI_UNICAST), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.afi == AFI_IPV4: Route = RouteIPV4 elif self.afi == AFI_IPV6: Route = RouteIPV6 elif self.afi == AFI_L2VPN: Route = RouteEVPN else: Route = RouteGeneric # Withdrawn Routes l_ = [] while self.data: route = Route(self.data) self.data = self.data[len(route):] l_.append(route) self.data = self.withdrawn = l_ def __len__(self): return self.__hdr_len__ + sum(map(len, self.data)) def __bytes__(self): return self.pack_hdr() + b''.join(map(bytes, self.data)) class Notification(dpkt.Packet): __hdr__ = ( ('code', 'B', 0), ('subcode', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.error = self.data class Keepalive(dpkt.Packet): def unpack(self, buf): pass def __len__(self): return 0 def __bytes__(self): return b'' class RouteRefresh(dpkt.Packet): __hdr__ = ( ('afi', 'H', AFI_IPV4), ('rsvd', 'B', 0), ('safi', 'B', SAFI_UNICAST) ) class RouteGeneric(dpkt.Packet): __hdr__ = ( ('len', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.prefix = self.data[:(self.len + 7) // 8] class RouteIPV4(dpkt.Packet): __hdr__ = ( ('len', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) tmp = self.data[:(self.len + 7) // 8] tmp += (4 - len(tmp)) * b'\x00' self.data = self.prefix = tmp def __repr__(self): cidr = '%s/%d' % (socket.inet_ntoa(self.prefix), self.len) return '%s(%s)' % (self.__class__.__name__, cidr) def __len__(self): return self.__hdr_len__ + (self.len + 7) // 8 def __bytes__(self): return self.pack_hdr() + self.prefix[:(self.len + 7) // 8] class ExtendedRouteIPV4(RouteIPV4): __hdr__ = ( ('path_id', 'I', 0), ('len', 'B', 0), ) def __repr__(self): cidr = '%s/%d PathId %d' % (socket.inet_ntoa(self.prefix), self.len, self.path_id) return '%s(%s)' % (self.__class__.__name__, cidr) class RouteIPV6(dpkt.Packet): __hdr__ = ( ('len', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) tmp = self.data[:(self.len + 7) // 8] tmp += (16 - len(tmp)) * b'\x00' self.data = self.prefix = tmp def __len__(self): return self.__hdr_len__ + (self.len + 7) // 8 def __bytes__(self): return self.pack_hdr() + self.prefix[:(self.len + 7) // 8] class RouteEVPN(dpkt.Packet): __hdr__ = ( ('type', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.route_data = buf = self.data[:self.len] self.data = self.data[self.len:] # Get route distinguisher. self.rd = buf[:8] buf = buf[8:] # Get route information. Not all fields are present on all route types. if self.type != 0x3: self.esi = buf[:10] buf = buf[10:] if self.type != 0x4: self.eth_id = buf[:4] buf = buf[4:] if self.type == 0x2: self.mac_address_length = compat_ord(buf[0]) if self.mac_address_length == 48: self.mac_address = buf[1:7] buf = buf[7:] else: self.mac_address = None buf = buf[1:] if self.type != 0x1: self.ip_address_length = compat_ord(buf[0]) if self.ip_address_length == 128: self.ip_address = buf[1:17] buf = buf[17:] elif self.ip_address_length == 32: self.ip_address = buf[1:5] buf = buf[5:] else: self.ip_address = None buf = buf[1:] if self.type in [0x1, 0x2]: self.mpls_label_stack = buf[:3] buf = buf[3:] if self.len > len(buf): self.mpls_label_stack += buf[:3] def __len__(self): return self.__hdr_len__ + self.len def __bytes__(self): return self.pack_hdr() + self.route_data __bgp1 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04' __bgp2 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x63\x02\x00\x00\x00\x48\x40\x01' b'\x01\x00\x40\x02\x0a\x01\x02\x01\xf4\x01\xf4\x02\x01\xfe\xbb\x40\x03\x04\xc0\xa8\x00\x0f\x40\x05\x04' b'\x00\x00\x00\x64\x40\x06\x00\xc0\x07\x06\xfe\xba\xc0\xa8\x00\x0a\xc0\x08\x0c\xfe\xbf\x00\x01\x03\x16' b'\x00\x04\x01\x54\x00\xfa\x80\x09\x04\xc0\xa8\x00\x0f\x80\x0a\x04\xc0\xa8\x00\xfa\x16\xc0\xa8\x04' ) __bgp3 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x79\x02\x00\x00\x00\x62\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x08\x00\x02\x01\x2c\x00\x00\x01\x2c\xc0\x80' b'\x24\x00\x00\xfd\xe9\x40\x01\x01\x00\x40\x02\x04\x02\x01\x15\xb3\x40\x05\x04\x00\x00\x00\x2c\x80\x09' b'\x04\x16\x05\x05\x05\x80\x0a\x04\x16\x05\x05\x05\x90\x0e\x00\x1e\x00\x01\x80\x0c\x00\x00\x00\x00\x00' b'\x00\x00\x00\x0c\x04\x04\x04\x00\x60\x18\x77\x01\x00\x00\x01\xf4\x00\x00\x01\xf4\x85' ) __bgp4 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x2d\x01\x04\x00\xed\x00\x5a\xc6' b'\x6e\x83\x7d\x10\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00' ) # BGP-EVPN type 1-4 packets for testing. __bgp5 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x60\x02\x00\x00\x00\x49\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x10\x03\x0c\x00\x00\x00\x00\x00\x08\x00\x02' b'\x03\xe8\x00\x00\x00\x02\x90\x0e\x00\x24\x00\x19\x46\x04\x01\x01\x01\x02\x00\x01\x19\x00\x01\x01\x01' b'\x01\x02\x00\x02\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00\x00\x00\x00\x02\x00\x00\x02' ) __bgp6 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x6f\x02\x00\x00\x00\x58\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x10\x03\x0c\x00\x00\x00\x00\x00\x08\x00\x02' b'\x03\xe8\x00\x00\x00\x02\x90\x0e\x00\x33\x00\x19\x46\x04\x01\x01\x01\x02\x00\x02\x28\x00\x01\x01\x01' b'\x01\x02\x00\x02\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00\x00\x00\x00\x02\x30\xcc\xaa\x02\x9c\xd8\x29' b'\x20\xc0\xb4\x01\x02\x00\x00\x02\x00\x00\x00' ) __bgp7 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x58\x02\x00\x00\x00\x41\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x10\x03\x0c\x00\x00\x00\x00\x00\x08\x00\x02' b'\x03\xe8\x00\x00\x00\x02\x90\x0e\x00\x1c\x00\x19\x46\x04\x01\x01\x01\x02\x00\x03\x11\x00\x01\x01\x01' b'\x01\x02\x00\x02\x00\x00\x00\x02\x20\xc0\xb4\x01\x02' ) __bgp8 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x5f\x02\x00\x00\x00\x48\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x10\x03\x0c\x00\x00\x00\x00\x00\x08\x00\x02' b'\x03\xe8\x00\x00\x00\x02\x90\x0e\x00\x23\x00\x19\x46\x04\x01\x01\x01\x02\x00\x04\x18\x00\x01\x01\x01' b'\x01\x02\x00\x02\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00\x20\xc0\xb4\x01\x02' ) __bgp9 = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x7b\x02\x00\x00\x00\x64\x40\x01' b'\x01\x00\x40\x02\x00\x40\x05\x04\x00\x00\x00\x64\xc0\x10\x10\x03\x0c\x00\x00\x00\x00\x00\x08\x00\x02' b'\x03\xe8\x00\x00\x00\x02\x90\x0e\x00\x3f\x00\x19\x46\x04\x01\x01\x01\x02\x00\x02\x34\x00\x01\x01\x01' b'\x01\x02\x00\x02\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00\x00\x00\x00\x02\x30\xcc\xaa\x02\x9c\xd8\x29' b'\x80\xc0\xb4\x01\x02\xc0\xb4\x01\x02\xc0\xb4\x01\x02\xc0\xb4\x01\x02\x00\x00\x02\x00\x00\x00' ) def test_pack(): assert (__bgp1 == bytes(BGP(__bgp1))) assert (__bgp2 == bytes(BGP(__bgp2))) assert (__bgp3 == bytes(BGP(__bgp3))) assert (__bgp4 == bytes(BGP(__bgp4))) assert (__bgp5 == bytes(BGP(__bgp5))) assert (__bgp6 == bytes(BGP(__bgp6))) assert (__bgp7 == bytes(BGP(__bgp7))) assert (__bgp8 == bytes(BGP(__bgp8))) assert (__bgp9 == bytes(BGP(__bgp9))) def test_unpack(): b1 = BGP(__bgp1) assert (b1.len == 19) assert (b1.type == KEEPALIVE) assert (b1.keepalive is not None) b2 = BGP(__bgp2) assert (b2.type == UPDATE) assert (len(b2.update.withdrawn) == 0) assert (len(b2.update.announced) == 1) assert (len(b2.update.attributes) == 9) a = b2.update.attributes[1] assert (a.type == AS_PATH) assert (a.len == 10) assert (len(a.as_path.segments) == 2) s = a.as_path.segments[0] assert (s.type == AS_SET) assert (s.len == 2) assert (len(s.path) == 2) assert (s.path[0] == 500) a = b2.update.attributes[6] assert (a.type == COMMUNITIES) assert (a.len == 12) assert (len(a.communities.list) == 3) c = a.communities.list[0] assert (c.asn == 65215) assert (c.value == 1) r = b2.update.announced[0] assert (r.len == 22) assert (r.prefix == b'\xc0\xa8\x04\x00') b3 = BGP(__bgp3) assert (b3.type == UPDATE) assert (len(b3.update.withdrawn) == 0) assert (len(b3.update.announced) == 0) assert (len(b3.update.attributes) == 6) a = b3.update.attributes[0] assert (not a.optional) assert (a.transitive) assert (not a.partial) assert (not a.extended_length) assert (a.type == ORIGIN) assert (a.len == 1) o = a.origin assert (o.type == ORIGIN_IGP) a = b3.update.attributes[5] assert (a.optional) assert (not a.transitive) assert (not a.partial) assert (a.extended_length) assert (a.type == MP_REACH_NLRI) assert (a.len == 30) m = a.mp_reach_nlri assert (m.afi == AFI_IPV4) assert (len(m.snpas) == 0) assert (len(m.announced) == 1) p = m.announced[0] assert (p.len == 96) b4 = BGP(__bgp4) assert (b4.len == 45) assert (b4.type == OPEN) assert (b4.open.asn == 237) assert (b4.open.param_len == 16) assert (len(b4.open.parameters) == 3) p = b4.open.parameters[0] assert (p.type == CAPABILITY) assert (p.len == 6) c = p.capability assert (c.code == CAP_MULTIPROTOCOL) assert (c.len == 4) assert (c.data == b'\x00\x01\x00\x01') c = b4.open.parameters[2].capability assert (c.code == CAP_ROUTE_REFRESH) assert (c.len == 0) b5 = BGP(__bgp5) assert (b5.len == 96) assert (b5.type == UPDATE) assert (len(b5.update.withdrawn) == 0) a = b5.update.attributes[-1] assert (a.type == MP_REACH_NLRI) assert (a.len == 36) m = a.mp_reach_nlri assert (m.afi == AFI_L2VPN) assert (m.safi == SAFI_EVPN) r = m.announced[0] assert (r.type == 1) assert (r.len == 25) assert (r.rd == b'\x00\x01\x01\x01\x01\x02\x00\x02') assert (r.esi == b'\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00') assert (r.eth_id == b'\x00\x00\x00\x02') assert (r.mpls_label_stack == b'\x00\x00\x02') b6 = BGP(__bgp6) assert (b6.len == 111) assert (b6.type == UPDATE) assert (len(b6.update.withdrawn) == 0) a = b6.update.attributes[-1] assert (a.type == MP_REACH_NLRI) assert (a.len == 51) m = a.mp_reach_nlri assert (m.afi == AFI_L2VPN) assert (m.safi == SAFI_EVPN) r = m.announced[0] assert (r.type == 2) assert (r.len == 40) assert (r.rd == b'\x00\x01\x01\x01\x01\x02\x00\x02') assert (r.esi == b'\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00') assert (r.eth_id == b'\x00\x00\x00\x02') assert (r.mac_address_length == 48) assert (r.mac_address == b'\xcc\xaa\x02\x9c\xd8\x29') assert (r.ip_address_length == 32) assert (r.ip_address == b'\xc0\xb4\x01\x02') assert (r.mpls_label_stack == b'\x00\x00\x02\x00\x00\x00') b7 = BGP(__bgp7) assert (b7.len == 88) assert (b7.type == UPDATE) assert (len(b7.update.withdrawn) == 0) a = b7.update.attributes[-1] assert (a.type == MP_REACH_NLRI) assert (a.len == 28) m = a.mp_reach_nlri assert (m.afi == AFI_L2VPN) assert (m.safi == SAFI_EVPN) r = m.announced[0] assert (r.type == 3) assert (r.len == 17) assert (r.rd == b'\x00\x01\x01\x01\x01\x02\x00\x02') assert (r.eth_id == b'\x00\x00\x00\x02') assert (r.ip_address_length == 32) assert (r.ip_address == b'\xc0\xb4\x01\x02') b8 = BGP(__bgp8) assert (b8.len == 95) assert (b8.type == UPDATE) assert (len(b8.update.withdrawn) == 0) a = b8.update.attributes[-1] assert (a.type == MP_REACH_NLRI) assert (a.len == 35) m = a.mp_reach_nlri assert (m.afi == AFI_L2VPN) assert (m.safi == SAFI_EVPN) r = m.announced[0] assert (r.type == 4) assert (r.len == 24) assert (r.rd == b'\x00\x01\x01\x01\x01\x02\x00\x02') assert (r.esi == b'\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00') assert (r.ip_address_length == 32) assert (r.ip_address == b'\xc0\xb4\x01\x02') b9 = BGP(__bgp9) assert (b9.len == 123) assert (b9.type == UPDATE) assert (len(b9.update.withdrawn) == 0) a = b9.update.attributes[-1] assert (a.type == MP_REACH_NLRI) assert (a.len == 63) m = a.mp_reach_nlri assert (m.afi == AFI_L2VPN) assert (m.safi == SAFI_EVPN) r = m.announced[0] assert (r.type == 2) assert (r.len == 52) assert (r.rd == b'\x00\x01\x01\x01\x01\x02\x00\x02') assert (r.esi == b'\x05\x00\x00\x03\xe8\x00\x00\x04\x00\x00') assert (r.eth_id == b'\x00\x00\x00\x02') assert (r.mac_address_length == 48) assert (r.mac_address == b'\xcc\xaa\x02\x9c\xd8\x29') assert (r.ip_address_length == 128) assert (r.ip_address == b'\xc0\xb4\x01\x02\xc0\xb4\x01\x02\xc0\xb4\x01\x02\xc0\xb4\x01\x02') assert (r.mpls_label_stack == b'\x00\x00\x02\x00\x00\x00') def test_bgp_mp_nlri_20_1_mp_reach_nlri_next_hop(): # test for https://github.com/kbandla/dpkt/issues/485 __bgp = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x6c\x02\x00\x00\x00\x55\x40\x01' b'\x01\x00\x40\x02\x04\x02\x01\xfd\xe9\x80\x04\x04\x00\x00\x00\x00\x80\x0e\x40\x00\x02\x01\x20\x20\x01' b'\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfe\x80\x00\x00\x00\x00\x00\x00\xc0\x01\x0b' b'\xff\xfe\x7e\x00\x00\x00\x40\x20\x01\x0d\xb8\x00\x01\x00\x02\x40\x20\x01\x0d\xb8\x00\x01\x00\x01\x40' b'\x20\x01\x0d\xb8\x00\x01\x00\x00' ) assert (__bgp == bytes(BGP(__bgp))) bgp = BGP(__bgp) assert (len(bgp.data) == 89) assert (bgp.type == UPDATE) assert (len(bgp.update.withdrawn) == 0) assert (len(bgp.update.announced) == 0) assert (len(bgp.update.attributes) == 4) attribute = bgp.update.attributes[0] assert (attribute.type == ORIGIN) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.len == 1) o = attribute.origin assert (o.type == ORIGIN_IGP) attribute = bgp.update.attributes[1] assert (attribute.type == AS_PATH) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 64) assert (attribute.len == 4) assert (len(attribute.as_path.segments) == 1) segment = attribute.as_path.segments[0] assert (segment.type == AS_SEQUENCE) assert (segment.len == 1) assert (len(segment.path) == 1) assert (segment.path[0] == 65001) attribute = bgp.update.attributes[2] assert (attribute.type == MULTI_EXIT_DISC) assert (attribute.optional) assert (not attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x80) assert (attribute.len == 4) assert (attribute.multi_exit_disc.value == 0) attribute = bgp.update.attributes[3] assert (attribute.type == MP_REACH_NLRI) assert (attribute.optional) assert (not attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x80) assert (attribute.len == 64) mp_reach_nlri = attribute.mp_reach_nlri assert (mp_reach_nlri.afi == AFI_IPV6) assert (mp_reach_nlri.safi == SAFI_UNICAST) assert (len(mp_reach_nlri.snpas) == 0) assert (len(mp_reach_nlri.announced) == 3) prefix = mp_reach_nlri.announced[0] assert (socket.inet_ntop(socket.AF_INET6, prefix.prefix) == '2001:db8:1:2::') assert (prefix.len == 64) prefix = mp_reach_nlri.announced[1] assert (socket.inet_ntop(socket.AF_INET6, prefix.prefix) == '2001:db8:1:1::') assert (prefix.len == 64) prefix = mp_reach_nlri.announced[2] assert (socket.inet_ntop(socket.AF_INET6, prefix.prefix) == '2001:db8:1::') assert (prefix.len == 64) assert (len(mp_reach_nlri.next_hops) == 2) assert (socket.inet_ntop(socket.AF_INET6, mp_reach_nlri.next_hops[0]) == '2001:db8::1') assert (socket.inet_ntop(socket.AF_INET6, mp_reach_nlri.next_hops[1]) == 'fe80::c001:bff:fe7e:0') assert (mp_reach_nlri.next_hop == b''.join(mp_reach_nlri.next_hops)) def test_bgp_add_path_6_1_as_path(): # test for https://github.com/kbandla/dpkt/issues/481 # Error processing BGP data: packet 6 : message 1 of bgp-add-path.cap # https://packetlife.net/media/captures/bgp-add-path.cap __bgp = ( b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x59\x02\x00\x00\x00\x30\x40\x01' b'\x01\x00\x40\x02\x06\x02\x01\x00\x00\xfb\xff\x40\x03\x04\x0a\x00\x0e\x01\x80\x04\x04\x00\x00\x00\x00' b'\x40\x05\x04\x00\x00\x00\x64\x80\x0a\x04\x0a\x00\x22\x04\x80\x09\x04\x0a\x00\x0f\x01\x00\x00\x00\x01' b'\x20\x05\x05\x05\x05\x00\x00\x00\x01\x20\xc0\xa8\x01\x05' ) bgp = BGP(__bgp) assert (__bgp == bytes(bgp)) assert (len(bgp) == 89) assert (bgp.type == UPDATE) assert (len(bgp.update.withdrawn) == 0) announced = bgp.update.announced assert (len(announced) == 2) assert (announced[0].len == 32) assert (announced[0].path_id == 1) assert (socket.inet_ntop(socket.AF_INET, bytes(announced[0].prefix)) == '5.5.5.5') assert (announced[1].len == 32) assert (announced[1].path_id == 1) assert (socket.inet_ntop(socket.AF_INET, bytes(announced[1].prefix)) == '192.168.1.5') assert (len(bgp.update.attributes) == 7) attribute = bgp.update.attributes[0] assert (attribute.type == ORIGIN) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x40) assert (attribute.len == 1) assert (attribute.origin.type == ORIGIN_IGP) attribute = bgp.update.attributes[1] assert (attribute.type == AS_PATH) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x40) assert (attribute.len == 6) assert (len(attribute.as_path.segments) == 1) segment = attribute.as_path.segments[0] assert (segment.type == AS_SEQUENCE) assert (segment.len == 1) assert (len(segment.path) == 1) assert (segment.path[0] == 64511) attribute = bgp.update.attributes[2] assert (attribute.type == NEXT_HOP) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x40) assert (attribute.len == 4) assert (socket.inet_ntop(socket.AF_INET, bytes(attribute.next_hop)) == '10.0.14.1') attribute = bgp.update.attributes[3] assert (attribute.type == MULTI_EXIT_DISC) assert (attribute.optional) assert (not attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x80) assert (attribute.len == 4) assert (attribute.multi_exit_disc.value == 0) attribute = bgp.update.attributes[4] assert (attribute.type == LOCAL_PREF) assert (not attribute.optional) assert (attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x40) assert (attribute.len == 4) assert (attribute.local_pref.value == 100) attribute = bgp.update.attributes[5] assert (attribute.type == CLUSTER_LIST) assert (attribute.optional) assert (not attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x80) assert (attribute.len == 4) assert (socket.inet_ntop(socket.AF_INET, bytes(attribute.cluster_list)) == '10.0.34.4') attribute = bgp.update.attributes[6] assert (attribute.type == ORIGINATOR_ID) assert (attribute.optional) assert (not attribute.transitive) assert (not attribute.partial) assert (not attribute.extended_length) assert (attribute.flags == 0x80) assert (attribute.len == 4) assert (socket.inet_ntop(socket.AF_INET, bytes(attribute.originator_id)) == '10.0.15.1') def test_attribute_accessors(): from binascii import unhexlify buf = unhexlify( '00' # flags '01' # type (ORIGIN) '01' # length '00' # Origin type ) attribute = BGP.Update.Attribute(buf) assert isinstance(attribute.data, BGP.Update.Attribute.Origin) for attr in ['optional', 'transitive', 'partial', 'extended_length']: assert getattr(attribute, attr) == 0 # check we can set.. setattr(attribute, attr, 1) assert getattr(attribute, attr) == 1 # and also unset setattr(attribute, attr, 0) assert getattr(attribute, attr) == 0 def test_snpa(): from binascii import unhexlify buf = unhexlify( '04' # len (in semi-octets) '1234' # data ) snpa = BGP.Update.Attribute.MPReachNLRI.SNPA(buf) assert snpa.len == 4 # length of the data in semi-octets assert len(snpa) == 3 # length of the snpa in bytes (including header) assert bytes(snpa) == buf def test_mpreachnlri(): from binascii import unhexlify buf = unhexlify( '0000' # afi '00' # safi '00' # nlen '01' # num SNPAs # SNPA '04' # len '1234' # data ) mp = BGP.Update.Attribute.MPReachNLRI(buf) assert len(mp.snpas) == 1 assert bytes(mp) == buf def test_notification(): from binascii import unhexlify buf_notification = unhexlify( '11' # code '22' # subcode '33' # error ) notification = BGP.Notification(buf_notification) assert notification.code == 0x11 assert notification.subcode == 0x22 assert notification.error == b'\x33' assert bytes(notification) == buf_notification buf_bgp_hdr = unhexlify( '11111111111111111111111111111111' # marker '0016' # len '03' # type (NOTIFICATION) ) bgp = BGP(buf_bgp_hdr + buf_notification) assert hasattr(bgp, 'notification') assert isinstance(bgp.data, BGP.Notification) assert bgp.data.code == 0x11 assert bgp.data.subcode == 0x22 assert bgp.data.error == b'\x33' assert bytes(bgp) == buf_bgp_hdr + buf_notification def test_keepalive(): keepalive = BGP.Keepalive(b'\x11') assert len(keepalive) == 0 assert bytes(keepalive) == b'' def test_routegeneric(): from binascii import unhexlify buf = unhexlify( '08' # len (bits) '11' # prefix ) routegeneric = RouteGeneric(buf) assert routegeneric.len == 8 assert routegeneric.prefix == b'\x11' assert bytes(routegeneric) == buf assert len(routegeneric) == 2 def test_routeipv4(): from binascii import unhexlify buf = unhexlify( '08' # len (bits) '11' # prefix ) routeipv4 = RouteIPV4(buf) assert routeipv4.len == 8 # prefix len in bits assert routeipv4.prefix == b'\x11\x00\x00\x00' assert repr(routeipv4) == "RouteIPV4(17.0.0.0/8)" assert bytes(routeipv4) == buf assert len(routeipv4) == 2 # header + prefix(bytes) def test_routeipv6(): from binascii import unhexlify buf = unhexlify( '08' # len (bits) '22' # prefix ) routeipv6 = RouteIPV4(buf) assert routeipv6.len == 8 # prefix len in bits assert routeipv6.prefix == b'\x22\x00\x00\x00' assert bytes(routeipv6) == buf assert len(routeipv6) == 2 # header + prefix(bytes) def test_extendedrouteipv4(): from binascii import unhexlify buf = unhexlify( '00000001' # path_id '20' # len (bits) '05050505' # prefix ) extendedrouteipv4 = ExtendedRouteIPV4(buf) assert extendedrouteipv4.path_id == 1 assert extendedrouteipv4.len == 32 assert extendedrouteipv4.prefix == unhexlify('05050505') assert repr(extendedrouteipv4) == "ExtendedRouteIPV4(5.5.5.5/32 PathId 1)" assert bytes(extendedrouteipv4) == buf assert len(extendedrouteipv4) == len(buf) def test_routeevpn(): from binascii import unhexlify buf = unhexlify( '02' # type '1a' # len # route distinguisher '1111111111111111' # esi '22222222222222222222' # eth_id '33333333' # mac address '00' # len (bits) # ip address '00' # len (bits) # mpls '6666' # label stack ) routeevpn = RouteEVPN(buf) assert routeevpn.type == 2 assert routeevpn.len == 26 assert routeevpn.esi == unhexlify('22222222222222222222') assert routeevpn.eth_id == unhexlify('33333333') assert routeevpn.mac_address_length == 0 assert routeevpn.mac_address is None assert routeevpn.ip_address_length == 0 assert routeevpn.ip_address is None assert routeevpn.mpls_label_stack == unhexlify('6666') assert bytes(routeevpn) == buf assert len(routeevpn) == len(buf) def test_route_refresh(): from binascii import unhexlify buf_route_refresh = unhexlify( '1111' # afi '22' # rsvd '33' # safi ) route_refresh = BGP.RouteRefresh(buf_route_refresh) assert route_refresh.afi == 0x1111 assert route_refresh.rsvd == 0x22 assert route_refresh.safi == 0x33 assert bytes(route_refresh) == buf_route_refresh buf_bgp_hdr = unhexlify( '11111111111111111111111111111111' # marker '0017' # len '05' # type (ROUTE_REFRESH) ) bgp = BGP(buf_bgp_hdr + buf_route_refresh) assert hasattr(bgp, 'route_refresh') assert isinstance(bgp.data, BGP.RouteRefresh) assert bgp.data.afi == 0x1111 assert bgp.data.rsvd == 0x22 assert bgp.data.safi == 0x33 assert bytes(bgp) == buf_bgp_hdr + buf_route_refresh def test_mpunreachnlri(): from binascii import unhexlify buf_routeipv4 = unhexlify( '08' # len (bits) '11' # prefix ) buf_routeipv6 = unhexlify( '08' # len (bits) '22' # prefix ) buf_routeevpn = unhexlify( '02' # type '1a' # len # route distinguisher '1111111111111111' # esi '22222222222222222222' # eth_id '33333333' # mac address '00' # len (bits) # ip address '00' # len (bits) # mpls '6666' # label stack ) buf_routegeneric = unhexlify( '08' # len (bits) '33' # prefix ) afi = struct.Struct('>H') routes = ( (AFI_IPV4, buf_routeipv4, RouteIPV4), (AFI_IPV6, buf_routeipv6, RouteIPV6), (AFI_L2VPN, buf_routeevpn, RouteEVPN), # this afi does not exist, so we will parse as RouteGeneric (1234, buf_routegeneric, RouteGeneric), ) for afi_id, buf, cls in routes: buf = afi.pack(afi_id) + b'\xcc' + buf mpu = BGP.Update.Attribute.MPUnreachNLRI(buf) assert mpu.afi == afi_id assert mpu.safi == 0xcc assert len(mpu.data) == 1 route = mpu.data[0] assert isinstance(route, cls) assert bytes(mpu) == buf assert len(mpu) == len(buf) # test the unpacking of the routes, as an Attribute attribute_hdr = struct.Struct('BBB') for afi_id, buf, cls in routes: buf_mpunreachnlri = afi.pack(afi_id) + b'\xcc' + buf buf_attribute_hdr = attribute_hdr.pack(0, MP_UNREACH_NLRI, len(buf_mpunreachnlri)) buf = buf_attribute_hdr + buf_mpunreachnlri attribute = BGP.Update.Attribute(buf) assert isinstance(attribute.data, BGP.Update.Attribute.MPUnreachNLRI) routes = attribute.data.data assert len(routes) == 1 assert isinstance(routes[0], cls) def test_update_withdrawn(): from binascii import unhexlify buf_ipv4 = unhexlify( '08' # len (bits) '11' # prefix ) packed_length = struct.Struct('>H').pack wlen, plen = packed_length(len(buf_ipv4)), packed_length(0) buf = wlen + buf_ipv4 + plen update = BGP.Update(buf) assert len(update.withdrawn) == 1 route = update.withdrawn[0] assert isinstance(route, RouteIPV4) assert bytes(update) == buf def test_parameters(): from binascii import unhexlify buf = unhexlify( '44' # v '1111' # asn '2222' # holdtime '33333333' # identifier '03' # param_len # Parameter '01' # type (AUTHENTICATION) '01' # len # Authentication '11' # code ) bgp_open = BGP.Open(buf) assert len(bgp_open.parameters) == 1 parameter = bgp_open.parameters[0] assert isinstance(parameter, BGP.Open.Parameter) assert isinstance(parameter.data, BGP.Open.Parameter.Authentication) assert bytes(bgp_open) == buf assert len(bgp_open) == len(buf) def test_reservedcommunities(): from binascii import unhexlify buf = unhexlify( # ReservedCommunity '00002222' # value ) communities = BGP.Update.Attribute.Communities(buf) assert len(communities.data) == 1 community = communities.data[0] assert isinstance(community, BGP.Update.Attribute.Communities.ReservedCommunity) assert len(community) == 4 assert bytes(community) == buf assert len(communities) == 4 assert bytes(communities) == buf dpkt-1.9.8/dpkt/cdp.py000066400000000000000000000144621427735150300145730ustar00rootroot00000000000000# $Id: cdp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Cisco Discovery Protocol.""" from __future__ import absolute_import from . import dpkt CDP_DEVID = 1 # string CDP_ADDRESS = 2 CDP_PORTID = 3 # string CDP_CAPABILITIES = 4 # 32-bit bitmask CDP_VERSION = 5 # string CDP_PLATFORM = 6 # string CDP_IPPREFIX = 7 CDP_VTP_MGMT_DOMAIN = 9 # string CDP_NATIVE_VLAN = 10 # 16-bit integer CDP_DUPLEX = 11 # 8-bit boolean CDP_TRUST_BITMAP = 18 # 8-bit bitmask0x13 CDP_UNTRUST_COS = 19 # 8-bit port CDP_SYSTEM_NAME = 20 # string CDP_SYSTEM_OID = 21 # 10-byte binary string CDP_MGMT_ADDRESS = 22 # 32-bit number of addrs, Addresses CDP_LOCATION = 23 # string class CDP(dpkt.Packet): """Cisco Discovery Protocol. Cisco Discovery Protocol (CDP) is a proprietary Data Link Layer protocol developed by Cisco Systems in 1994 by Keith McCloghrie and Dino Farinacci. It is used to share information about other directly connected Cisco equipment, such as the operating system version and IP address. See more on https://en.wikipedia.org/wiki/Cisco_Discovery_Protocol Attributes: __hdr__: Header fields of CDP. version: (int): CDP protocol version. (1 byte) ttl: (int): Time to live. The amount of time in seconds that a receiver should retain the information contained in this packet. (1 byte) sum: (int): Checksum. (2 bytes) """ __hdr__ = ( ('version', 'B', 2), ('ttl', 'B', 180), ('sum', 'H', 0) ) class TLV(dpkt.Packet): """Type–length–value When constructing the packet, len is not mandatory: if not provided, then self.data must be this exact TLV payload Attributes: __hdr__: Header fields of TLV. type: (int): Type (2 bytes) len: (int): The total length in bytes of the Type, Length and Data fields. (2 bytes) """ __hdr__ = ( ('type', 'H', 0), ('len', 'H', 0) ) def data_len(self): if self.len: return self.len - self.__hdr_len__ return len(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.data_len()] def __len__(self): return self.__hdr_len__ + len(self.data) def __bytes__(self): if hasattr(self, 'len') and not self.len: self.len = len(self) return self.pack_hdr() + bytes(self.data) class Address(TLV): # XXX - only handle NLPID/IP for now __hdr__ = ( ('ptype', 'B', 1), # protocol type (NLPID) ('plen', 'B', 1), # protocol length ('p', 'B', 0xcc), # IP ('alen', 'H', 4) # address length ) def data_len(self): return self.alen class TLV_Addresses(TLV): __hdr__ = ( ('type', 'H', CDP_ADDRESS), ('len', 'H', 0), # 17), ('Addresses', 'L', 1), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) buf = self.data l_ = [] while buf: # find the right TLV according to Type value tlv_find_type = self.TLV(buf).type # if this TLV is not in tlv_types, use the default TLV class tlv = self.tlv_types.get(tlv_find_type, self.TLV)(buf) l_.append(bytes(tlv)) buf = buf[len(tlv):] self.tlvs = l_ self.data = b''.join(l_) def __len__(self): return self.__hdr_len__ + len(self.data) def __bytes__(self): data = bytes(self.data) if not self.sum: self.sum = dpkt.in_cksum(self.pack_hdr() + data) return self.pack_hdr() + data # keep here the TLV classes whose header is different from the generic TLV header (example : TLV_Addresses) tlv_types = {CDP_ADDRESS: TLV_Addresses} def test_cdp(): import socket from . import ethernet ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01' b'\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67') rr1 = CDP(ss) assert bytes(rr1) == ss # construction ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01' b'\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67') p1 = CDP.TLV_Addresses(data=CDP.Address(data=socket.inet_aton('192.168.1.103'))) p2 = CDP.TLV(type=CDP_DEVID, data=b'cisco') data = p2.pack() + p1.pack() rr2 = CDP(data=data) assert bytes(rr2) == ss s = (b'\x01\x00\x0c\xcc\xcc\xcc\xc4\x022k\x00\x00\x01T\xaa\xaa\x03\x00\x00\x0c \x00\x02\xb4,B' b'\x00\x01\x00\x06R2\x00\x05\x00\xffCisco IOS Software, 3700 Software (C3745-ADVENTERPRI' b'SEK9_SNA-M), Version 12.4(25d), RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.' b'cisco.com/techsupport\nCopyright (c) 1986-2010 by Cisco Systems, Inc.\nCompiled Wed 18' b'-Aug-10 08:18 by prod_rel_team\x00\x06\x00\x0eCisco 3745\x00\x02\x00\x11\x00\x00\x00\x01' b'\x01\x01\xcc\x00\x04\n\x00\x00\x02\x00\x03\x00\x13FastEthernet0/0\x00\x04\x00\x08\x00' b'\x00\x00)\x00\t\x00\x04\x00\x0b\x00\x05\x00') eth = ethernet.Ethernet(s) assert isinstance(eth.data.data, CDP) assert len(eth.data.data.tlvs) == 8 # number of CDP TLVs; ensures they are decoded assert str(eth) == str(s) assert len(eth) == len(s) def test_tlv(): from binascii import unhexlify # len field set to 0 buf_no_len = unhexlify( '0000' # type '0000' # len 'abcd' # data ) buf_with_len = unhexlify( '0000' # type '0006' # len 'abcd' # data ) tlv = CDP.TLV(buf_no_len) assert tlv.type == 0 assert tlv.len == 0 assert tlv.data_len() == 2 assert tlv.data == b'\xab\xcd' assert bytes(tlv) == buf_with_len # len field set manually tlv = CDP.TLV(buf_with_len) assert tlv.type == 0 assert tlv.len == 6 assert tlv.data_len() == 2 assert tlv.data == b'\xab\xcd' assert bytes(tlv) == buf_with_len def test_address(): from binascii import unhexlify buf = unhexlify( '00' # ptype '11' # plen '22' # p '3333' # alen ) address = CDP.Address(buf) assert address.data_len() == 0x3333 dpkt-1.9.8/dpkt/compat.py000066400000000000000000000024571427735150300153110ustar00rootroot00000000000000from __future__ import absolute_import from struct import pack, unpack import sys if sys.version_info < (3,): compat_ord = ord else: def compat_ord(char): return char try: from itertools import izip compat_izip = izip except ImportError: compat_izip = zip try: from cStringIO import StringIO except ImportError: from io import StringIO try: from BytesIO import BytesIO except ImportError: from io import BytesIO if sys.version_info < (3,): def iteritems(d, **kw): return d.iteritems(**kw) def intround(num): return int(round(num)) else: def iteritems(d, **kw): return iter(d.items(**kw)) # python3 will return an int if you round to 0 decimal places intround = round def ntole(v): """convert a 2-byte word from the network byte order (big endian) to little endian; replaces socket.ntohs() to work on both little and big endian architectures """ return unpack('> 8) ^ crc32c_table[(crc ^ b) & 0xff] return crc def done(crc): tmp = ~crc & 0xffffffff b0 = tmp & 0xff b1 = (tmp >> 8) & 0xff b2 = (tmp >> 16) & 0xff b3 = (tmp >> 24) & 0xff crc = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 return crc def cksum(buf): """Return computed CRC-32c checksum.""" return done(add(0xffffffff, buf)) def test_crc32c(): def bswap32(x): from struct import pack, unpack return unpack('I', x))[0] # reference test value from CRC catalogue # http://reveng.sourceforge.net/crc-catalogue/17plus.htm#crc.cat.crc-32c # SCTP uses tranport-level mirrored byte ordering, so we bswap32 assert cksum(b'') == 0 assert cksum(b'123456789') == bswap32(0xe3069283) dpkt-1.9.8/dpkt/dhcp.py000066400000000000000000000223331427735150300147370ustar00rootroot00000000000000# $Id: dhcp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Dynamic Host Configuration Protocol.""" from __future__ import print_function from __future__ import absolute_import import struct from . import arp from . import dpkt from .compat import compat_ord DHCP_OP_REQUEST = 1 DHCP_OP_REPLY = 2 DHCP_MAGIC = 0x63825363 # DHCP option codes DHCP_OPT_NETMASK = 1 # I: subnet mask DHCP_OPT_TIMEOFFSET = 2 DHCP_OPT_ROUTER = 3 # s: list of router ips DHCP_OPT_TIMESERVER = 4 DHCP_OPT_NAMESERVER = 5 DHCP_OPT_DNS_SVRS = 6 # s: list of DNS servers DHCP_OPT_LOGSERV = 7 DHCP_OPT_COOKIESERV = 8 DHCP_OPT_LPRSERV = 9 DHCP_OPT_IMPSERV = 10 DHCP_OPT_RESSERV = 11 DHCP_OPT_HOSTNAME = 12 # s: client hostname DHCP_OPT_BOOTFILESIZE = 13 DHCP_OPT_DUMPFILE = 14 DHCP_OPT_DOMAIN = 15 # s: domain name DHCP_OPT_SWAPSERV = 16 DHCP_OPT_ROOTPATH = 17 DHCP_OPT_EXTENPATH = 18 DHCP_OPT_IPFORWARD = 19 DHCP_OPT_SRCROUTE = 20 DHCP_OPT_POLICYFILTER = 21 DHCP_OPT_MAXASMSIZE = 22 DHCP_OPT_IPTTL = 23 DHCP_OPT_MTUTIMEOUT = 24 DHCP_OPT_MTUTABLE = 25 DHCP_OPT_MTUSIZE = 26 DHCP_OPT_LOCALSUBNETS = 27 DHCP_OPT_BROADCASTADDR = 28 DHCP_OPT_DOMASKDISCOV = 29 DHCP_OPT_MASKSUPPLY = 30 DHCP_OPT_DOROUTEDISC = 31 DHCP_OPT_ROUTERSOLICIT = 32 DHCP_OPT_STATICROUTE = 33 DHCP_OPT_TRAILERENCAP = 34 DHCP_OPT_ARPTIMEOUT = 35 DHCP_OPT_ETHERENCAP = 36 DHCP_OPT_TCPTTL = 37 DHCP_OPT_TCPKEEPALIVE = 38 DHCP_OPT_TCPALIVEGARBAGE = 39 DHCP_OPT_NISDOMAIN = 40 DHCP_OPT_NISSERVERS = 41 DHCP_OPT_NISTIMESERV = 42 DHCP_OPT_VENDSPECIFIC = 43 DHCP_OPT_NBNS = 44 DHCP_OPT_NBDD = 45 DHCP_OPT_NBTCPIP = 46 DHCP_OPT_NBTCPSCOPE = 47 DHCP_OPT_XFONT = 48 DHCP_OPT_XDISPLAYMGR = 49 DHCP_OPT_REQ_IP = 50 # I: IP address DHCP_OPT_LEASE_SEC = 51 # I: lease seconds DHCP_OPT_OPTIONOVERLOAD = 52 DHCP_OPT_MSGTYPE = 53 # B: message type DHCP_OPT_SERVER_ID = 54 # I: server IP address DHCP_OPT_PARAM_REQ = 55 # s: list of option codes DHCP_OPT_MESSAGE = 56 DHCP_OPT_MAXMSGSIZE = 57 DHCP_OPT_RENEWTIME = 58 DHCP_OPT_REBINDTIME = 59 DHCP_OPT_VENDOR_ID = 60 # s: vendor class id DHCP_OPT_CLIENT_ID = 61 # Bs: idtype, id (idtype 0: FQDN, idtype 1: MAC) DHCP_OPT_NISPLUSDOMAIN = 64 DHCP_OPT_NISPLUSSERVERS = 65 DHCP_OPT_MOBILEIPAGENT = 68 DHCP_OPT_SMTPSERVER = 69 DHCP_OPT_POP3SERVER = 70 DHCP_OPT_NNTPSERVER = 71 DHCP_OPT_WWWSERVER = 72 DHCP_OPT_FINGERSERVER = 73 DHCP_OPT_IRCSERVER = 74 DHCP_OPT_STSERVER = 75 DHCP_OPT_STDASERVER = 76 # DHCP message type values DHCPDISCOVER = 1 DHCPOFFER = 2 DHCPREQUEST = 3 DHCPDECLINE = 4 DHCPACK = 5 DHCPNAK = 6 DHCPRELEASE = 7 DHCPINFORM = 8 class DHCP(dpkt.Packet): """Dynamic Host Configuration Protocol. The Dynamic Host Configuration Protocol (DHCP) is a network management protocol used on Internet Protocol (IP) networks for automatically assigning IP addresses and other communication parameters to devices connected to the network using a client–server architecture. Attributes: __hdr__: Header fields of DHCP. op: (int): Operation. Message op code / message type. 1 = BOOTREQUEST, 2 = BOOTREPLY. (1 byte) hrd: (int): Hardware type. Hardware address type, see ARP section in "Assigned Numbers" RFC; e.g., '1' = 10mb ethernet. (1 byte) hln: (int): Hardware Length. Hardware address length (e.g. '6' for 10mb ethernet). (1 byte) hops: (int): Hops. Client sets to zero, optionally used by relay agents when booting via a relay agent. (1 byte) xid: (int): Transaction ID. A random number chosen by the client, used by the client and server to associate messages and responses between a client and a server. (4 bytes) secs: (int): Seconds. Filled in by client, seconds elapsed since client began address acquisition or renewal process. (2 bytes) flags: (int): DHCP Flags. (2 bytes) ciaddr: (int): Client IP address. Only filled in if client is in BOUND, RENEW or REBINDING state and can respond to ARP requests. (4 bytes) yiaddr: (int): User IP address. (4 bytes) siaddr: (int): Server IP address. IP address of next server to use in bootstrap; returned in DHCPOFFER, DHCPACK by server. (4 bytes) giaddr: (int): Gateway IP address. Relay agent IP address, used in booting via a relay agent. (4 bytes) chaddr: (int): Client hardware address. (16 bytes) sname: (int): Server Hostname. Optional, null terminated string. (64 bytes) file: (int): Boot file name. Null terminated string; "generic" name or null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER. (128 bytes) magic: (int): Magic cookie. Optional parameters field. See the options documents for a list of defined options. (4 bytes) """ __hdr__ = ( ('op', 'B', DHCP_OP_REQUEST), ('hrd', 'B', arp.ARP_HRD_ETH), # just like ARP.hrd ('hln', 'B', 6), # and ARP.hln ('hops', 'B', 0), ('xid', 'I', 0xdeadbeef), ('secs', 'H', 0), ('flags', 'H', 0), ('ciaddr', 'I', 0), ('yiaddr', 'I', 0), ('siaddr', 'I', 0), ('giaddr', 'I', 0), ('chaddr', '16s', 16 * b'\x00'), ('sname', '64s', 64 * b'\x00'), ('file', '128s', 128 * b'\x00'), ('magic', 'I', DHCP_MAGIC), ) opts = ( (DHCP_OPT_MSGTYPE, chr(DHCPDISCOVER)), (DHCP_OPT_PARAM_REQ, ''.join(map(chr, (DHCP_OPT_REQ_IP, DHCP_OPT_ROUTER, DHCP_OPT_NETMASK, DHCP_OPT_DNS_SVRS)))) ) # list of (type, data) tuples def __len__(self): return self.__hdr_len__ + \ sum([2 + len(o[1]) for o in self.opts]) + 1 + len(self.data) def __bytes__(self): return self.pack_hdr() + self.pack_opts() + bytes(self.data) def pack_opts(self): """Return packed options string.""" if not self.opts: return b'' l_ = [] for t, data in self.opts: l_.append(struct.pack("BB%is" % len(data), t, len(data), data)) l_.append(b'\xff') return b''.join(l_) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.chaddr = self.chaddr[:self.hln] buf = self.data l_ = [] while buf: t = compat_ord(buf[0]) if t == 0xff: buf = buf[1:] break elif t == 0: buf = buf[1:] else: n = compat_ord(buf[1]) l_.append((t, buf[2:2 + n])) buf = buf[2 + n:] self.opts = l_ self.data = buf def test_dhcp(): s = ( b'\x01\x01\x06\x00\xad\x53\xc8\x63\xb8\x87\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x02\x55\x82\xf3\xa6\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\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\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\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\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\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\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\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x63\x82\x53\x63\x35\x01\x01\xfb\x01\x01\x3d\x07\x01\x00' b'\x02\x55\x82\xf3\xa6\x32\x04\x0a\x00\x01\x65\x0c\x09\x47\x75\x69\x6e\x65\x76\x65\x72\x65\x3c\x08\x4d' b'\x53\x46\x54\x20\x35\x2e\x30\x37\x0a\x01\x0f\x03\x06\x2c\x2e\x2f\x1f\x21\x2b\xff\x00\x00\x00\x00\x00' ) dhcp = DHCP(s) assert (s == bytes(dhcp)) assert len(dhcp) == 300 assert isinstance(dhcp.chaddr, bytes) assert isinstance(dhcp.sname, bytes) assert isinstance(dhcp.file, bytes) # Test default construction dhcp = DHCP() assert isinstance(dhcp.chaddr, bytes) assert isinstance(dhcp.sname, bytes) assert isinstance(dhcp.file, bytes) def test_no_opts(): from binascii import unhexlify buf_small_hdr = unhexlify( '00' # op '00' # hrd '06' # hln '12' # hops 'deadbeef' # xid '1234' # secs '9866' # flags '00000000' # ciaddr '00000000' # yiaddr '00000000' # siaddr '00000000' # giaddr ) buf = b''.join([ buf_small_hdr, b'\x00' * 16, # chaddr b'\x11' * 64, # sname b'\x22' * 128, # file b'\x44' * 4, # magic b'\x00' # data ]) dhcp = DHCP(buf) assert dhcp.opts == [] assert dhcp.data == b'' assert dhcp.pack_opts() == b'' dpkt-1.9.8/dpkt/diameter.py000066400000000000000000000164351427735150300156210ustar00rootroot00000000000000# $Id: diameter.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Diameter.""" from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt from .compat import compat_ord # Diameter Base Protocol - RFC 3588 # http://tools.ietf.org/html/rfc3588 # Request/Answer Command Codes ABORT_SESSION = 274 ACCOUNTING = 271 CAPABILITIES_EXCHANGE = 257 DEVICE_WATCHDOG = 280 DISCONNECT_PEER = 282 RE_AUTH = 258 SESSION_TERMINATION = 275 class Diameter(dpkt.Packet): """Diameter. Diameter is an authentication, authorization, and accounting protocol for computer networks. It evolved from the earlier RADIUS protocol. It belongs to the application layer protocols in the internet protocol suite. Attributes: __hdr__: Header fields of Diameter. v: (int) Version. The version of the Diameter Base Protocol. As of 2014, the only value supported is 1. (1 byte) len: (bytes): Message Length. The Message Length field indicates the length of the Diameter message in bytes, including the header fields and the padded AVPs. (3 bytes) flags: (int): Command flags. (Request, Proxiable, Error, Potentially re-transmitted message) (1 byte) cmd: (bytes): Commands. Determine the action that is to be taken for a particular message. (3 bytes) app_id: (int): Application-ID. Application-ID is used to identify for which Diameter application the message is applicable. (4 bytes) hop_id: (int): Hop-by-Hop Identifier. Used to match the requests with their answers as the same value in the request is used in the response. (4 bytes) end_id: (int): End-to-End Identifier. used to detect duplicate messages along with the combination of the Origin-Host AVP. (4 bytes) """ __hdr__ = ( ('v', 'B', 1), ('len', '3s', 0), ('flags', 'B', 0), ('cmd', '3s', 0), ('app_id', 'I', 0), ('hop_id', 'I', 0), ('end_id', 'I', 0) ) @property def request_flag(self): return (self.flags >> 7) & 0x1 @request_flag.setter def request_flag(self, r): self.flags = (self.flags & ~0x80) | ((r & 0x1) << 7) @property def proxiable_flag(self): return (self.flags >> 6) & 0x1 @proxiable_flag.setter def proxiable_flag(self, p): self.flags = (self.flags & ~0x40) | ((p & 0x1) << 6) @property def error_flag(self): return (self.flags >> 5) & 0x1 @error_flag.setter def error_flag(self, e): self.flags = (self.flags & ~0x20) | ((e & 0x1) << 5) @property def retransmit_flag(self): return (self.flags >> 4) & 0x1 @retransmit_flag.setter def retransmit_flag(self, t): self.flags = (self.flags & ~0x10) | ((t & 0x1) << 4) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.cmd = (compat_ord(self.cmd[0]) << 16) | \ (compat_ord(self.cmd[1]) << 8) | \ (compat_ord(self.cmd[2])) self.len = (compat_ord(self.len[0]) << 16) | \ (compat_ord(self.len[1]) << 8) | \ (compat_ord(self.len[2])) self.data = self.data[:self.len - self.__hdr_len__] l_ = [] while self.data: avp = AVP(self.data) l_.append(avp) self.data = self.data[len(avp):] self.data = self.avps = l_ def pack_hdr(self): self.len = struct.pack("BBB", (self.len >> 16) & 0xff, (self.len >> 8) & 0xff, self.len & 0xff) self.cmd = struct.pack("BBB", (self.cmd >> 16) & 0xff, (self.cmd >> 8) & 0xff, self.cmd & 0xff) return dpkt.Packet.pack_hdr(self) def __len__(self): return self.__hdr_len__ + sum(map(len, self.data)) def __bytes__(self): return self.pack_hdr() + b''.join(map(bytes, self.data)) class AVP(dpkt.Packet): __hdr__ = ( ('code', 'I', 0), ('flags', 'B', 0), ('len', '3s', 0), ) @property def vendor_flag(self): return (self.flags >> 7) & 0x1 @vendor_flag.setter def vendor_flag(self, v): self.flags = (self.flags & ~0x80) | ((v & 0x1) << 7) @property def mandatory_flag(self): return (self.flags >> 6) & 0x1 @mandatory_flag.setter def mandatory_flag(self, m): self.flags = (self.flags & ~0x40) | ((m & 0x1) << 6) @property def protected_flag(self): return (self.flags >> 5) & 0x1 @protected_flag.setter def protected_flag(self, p): self.flags = (self.flags & ~0x20) | ((p & 0x1) << 5) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.len = (compat_ord(self.len[0]) << 16) | \ (compat_ord(self.len[1]) << 8) | \ (compat_ord(self.len[2])) if self.vendor_flag: self.vendor = struct.unpack('>I', self.data[:4])[0] self.data = self.data[4:self.len - self.__hdr_len__] else: self.data = self.data[:self.len - self.__hdr_len__] def pack_hdr(self): self.len = struct.pack("BBB", (self.len >> 16) & 0xff, (self.len >> 8) & 0xff, self.len & 0xff) data = dpkt.Packet.pack_hdr(self) if self.vendor_flag: data += struct.pack('>I', self.vendor) return data def __len__(self): length = self.__hdr_len__ + len(self.data) if self.vendor_flag: length += 4 return length __s = (b'\x01\x00\x00\x28\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x41\xc8\x00\x00\x00\x0c\x00\x00' b'\x01\x08\x40\x00\x00\x0c\x68\x30\x30\x32\x00\x00\x01\x28\x40\x00\x00\x08') __t = (b'\x01\x00\x00\x2c\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x41\xc8\x00\x00\x00\x0c\x00\x00' b'\x01\x08\xc0\x00\x00\x10\xde\xad\xbe\xef\x68\x30\x30\x32\x00\x00\x01\x28\x40\x00\x00\x08') def test_pack(): d = Diameter(__s) assert (__s == bytes(d)) assert len(d) == len(__s) d = Diameter(__t) assert (__t == bytes(d)) assert len(d) == len(__t) def test_unpack(): d = Diameter(__s) assert (d.len == 40) # assert (d.cmd == DEVICE_WATCHDOG_REQUEST) assert (d.request_flag == 1) assert (d.error_flag == 0) assert (len(d.avps) == 2) avp = d.avps[0] # assert (avp.code == ORIGIN_HOST) assert (avp.mandatory_flag == 1) assert (avp.vendor_flag == 0) assert (avp.len == 12) assert (len(avp) == 12) assert (avp.data == b'\x68\x30\x30\x32') # also test the optional vendor id support d = Diameter(__t) assert (d.len == 44) avp = d.avps[0] assert (avp.vendor_flag == 1) assert (avp.len == 16) assert (len(avp) == 16) assert (avp.vendor == 3735928559) assert (avp.data == b'\x68\x30\x30\x32') def test_diameter_properties(): diameter = Diameter() for prop in ['request_flag', 'proxiable_flag', 'error_flag', 'retransmit_flag']: assert hasattr(diameter, prop) assert getattr(diameter, prop) == 0 setattr(diameter, prop, 1) assert getattr(diameter, prop) == 1 def test_avp_properties(): avp = AVP() for prop in ['vendor_flag', 'mandatory_flag', 'protected_flag']: assert hasattr(avp, prop) assert getattr(avp, prop) == 0 setattr(avp, prop, 1) assert getattr(avp, prop) == 1 dpkt-1.9.8/dpkt/dns.py000066400000000000000000000633041427735150300146100ustar00rootroot00000000000000# $Id: dns.py 27 2006-11-21 01:22:52Z dahelder $ # -*- coding: utf-8 -*- """Domain Name System.""" from __future__ import print_function from __future__ import absolute_import import struct import codecs from . import dpkt from .compat import compat_ord DNS_Q = 0 DNS_R = 1 # Opcodes DNS_QUERY = 0 DNS_IQUERY = 1 DNS_STATUS = 2 DNS_NOTIFY = 4 DNS_UPDATE = 5 # Flags DNS_CD = 0x0010 # checking disabled DNS_AD = 0x0020 # authenticated data DNS_Z = 0x0040 # unused DNS_RA = 0x0080 # recursion available DNS_RD = 0x0100 # recursion desired DNS_TC = 0x0200 # truncated DNS_AA = 0x0400 # authoritative answer DNS_QR = 0x8000 # response ( query / response ) # Response codes DNS_RCODE_NOERR = 0 DNS_RCODE_FORMERR = 1 DNS_RCODE_SERVFAIL = 2 DNS_RCODE_NXDOMAIN = 3 DNS_RCODE_NOTIMP = 4 DNS_RCODE_REFUSED = 5 DNS_RCODE_YXDOMAIN = 6 DNS_RCODE_YXRRSET = 7 DNS_RCODE_NXRRSET = 8 DNS_RCODE_NOTAUTH = 9 DNS_RCODE_NOTZONE = 10 # RR types DNS_A = 1 DNS_NS = 2 DNS_CNAME = 5 DNS_SOA = 6 DNS_NULL = 10 DNS_PTR = 12 DNS_HINFO = 13 DNS_MX = 15 DNS_TXT = 16 DNS_AAAA = 28 DNS_SRV = 33 DNS_OPT = 41 # RR classes DNS_IN = 1 DNS_CHAOS = 3 DNS_HESIOD = 4 DNS_ANY = 255 def pack_name(name, off, label_ptrs): name = codecs.encode(name, 'utf-8') if name: labels = name.split(b'.') else: labels = [] labels.append(b'') buf = b'' for i, label in enumerate(labels): key = b'.'.join(labels[i:]).upper() ptr = label_ptrs.get(key) if ptr is None: if len(key) > 1: ptr = off + len(buf) if ptr < 0xc000: label_ptrs[key] = ptr i = len(label) buf += struct.pack("B", i) + label else: buf += struct.pack('>H', (0xc000 | ptr)) break return buf def unpack_name(buf, off): name = [] saved_off = 0 start_off = off name_length = 0 while True: if off >= len(buf): raise dpkt.NeedData() n = compat_ord(buf[off]) if n == 0: off += 1 break elif (n & 0xc0) == 0xc0: ptr = struct.unpack('>H', buf[off:off + 2])[0] & 0x3fff if ptr >= start_off: raise dpkt.UnpackError('Invalid label compression pointer') off += 2 if not saved_off: saved_off = off start_off = off = ptr elif (n & 0xc0) == 0x00: off += 1 name.append(buf[off:off + n]) name_length += n + 1 if name_length > 255: raise dpkt.UnpackError('name longer than 255 bytes') off += n else: raise dpkt.UnpackError('Invalid label length %02x' % n) if not saved_off: saved_off = off return codecs.decode(b'.'.join(name), 'utf-8'), saved_off class DNS(dpkt.Packet): """Domain Name System. The Domain Name System (DNS) is the hierarchical and decentralized naming system used to identify computers, services, and other resources reachable through the Internet or other Internet Protocol (IP) networks. The resource records contained in the DNS associate domain names with other forms of information. Attributes: __hdr__ (tuple(header_name, c_type, offset)): Header fields of DNS. id: (int): Identification. Used to match request/reply packets. op: (int): Operation qd: (int): Query Definition an: (int): Answer ns: (int): Name Server ar: (int): Additional Record """ __hdr__ = ( ('id', 'H', 0), ('op', 'H', DNS_RD), # recursive query # XXX - lists of query, RR objects ('qd', 'H', []), ('an', 'H', []), ('ns', 'H', []), ('ar', 'H', []) ) @property def qr(self): """DNS Query/Response. 1 bit""" return int((self.op & DNS_QR) == DNS_QR) @qr.setter def qr(self, v): if v: self.op |= DNS_QR else: self.op &= ~DNS_QR @property def opcode(self): """Operation code. 4 bits.""" return (self.op >> 11) & 0xf @opcode.setter def opcode(self, v): self.op = (self.op & ~0x7800) | ((v & 0xf) << 11) @property def aa(self): """Authoritative Answer. 1 bit. Specifies that the responding name server is an authority for the domain name in question section.""" return int((self.op & DNS_AA) == DNS_AA) @aa.setter def aa(self, v): if v: self.op |= DNS_AA else: self.op &= ~DNS_AA @property def tc(self): """Truncated. 1 bit. Indicates that only the first 512 bytes of the reply was returned.""" return int((self.op & DNS_TC) == DNS_TC) @tc.setter def tc(self, v): if v: self.op |= DNS_TC else: self.op &= ~DNS_TC @property def rd(self): """Recursion Desired. 1 bit. May be set in a query and is copied into the response. If set, the name server is directed to pursue the query recursively. Recursive query support is optional.""" return int((self.op & DNS_RD) == DNS_RD) @rd.setter def rd(self, v): if v: self.op |= DNS_RD else: self.op &= ~DNS_RD @property def ra(self): """Recursion Available. 1 bit. Indicates if recursive query support is available in the name server.""" return int((self.op & DNS_RA) == DNS_RA) @ra.setter def ra(self, v): if v: self.op |= DNS_RA else: self.op &= ~DNS_RA @property def zero(self): """Zero 1 bit""" return int((self.op & DNS_Z) == DNS_Z) @zero.setter def zero(self, v): if v: self.op |= DNS_Z else: self.op &= ~DNS_Z @property def rcode(self): """Return code. 4 bits.""" return self.op & 0xf @rcode.setter def rcode(self, v): self.op = (self.op & ~0xf) | (v & 0xf) class Q(dpkt.Packet): """DNS question.""" __hdr__ = ( ('name', '1025s', b''), ('type', 'H', DNS_A), ('cls', 'H', DNS_IN) ) # XXX - suk def __len__(self): raise NotImplementedError __str__ = __len__ def unpack(self, buf): raise NotImplementedError class RR(Q): """DNS resource record.""" __hdr__ = ( ('name', '1025s', b''), ('type', 'H', DNS_A), ('cls', 'H', DNS_IN), ('ttl', 'I', 0), ('rlen', 'H', 4), ('rdata', 's', b'') ) def pack_rdata(self, off, label_ptrs): # XXX - yeah, this sux if self.rdata: return self.rdata if self.type == DNS_A: return self.ip elif self.type == DNS_NS: return pack_name(self.nsname, off, label_ptrs) elif self.type == DNS_CNAME: return pack_name(self.cname, off, label_ptrs) elif self.type == DNS_PTR: return pack_name(self.ptrname, off, label_ptrs) elif self.type == DNS_SOA: l_ = [] l_.append(pack_name(self.mname, off, label_ptrs)) l_.append(pack_name(self.rname, off + len(l_[0]), label_ptrs)) l_.append(struct.pack('>IIIII', self.serial, self.refresh, self.retry, self.expire, self.minimum)) return b''.join(l_) elif self.type == DNS_MX: return struct.pack('>H', self.preference) + \ pack_name(self.mxname, off + 2, label_ptrs) elif self.type == DNS_TXT or self.type == DNS_HINFO: return b''.join(struct.pack('B', len(x)) + x for x in self.text) elif self.type == DNS_AAAA: return self.ip6 elif self.type == DNS_SRV: return struct.pack('>HHH', self.priority, self.weight, self.port) + \ pack_name(self.srvname, off + 6, label_ptrs) elif self.type == DNS_OPT: return b'' # self.rdata else: raise dpkt.PackError('RR type %s is not supported' % self.type) def unpack_rdata(self, buf, off): if self.type == DNS_A: self.ip = self.rdata elif self.type == DNS_NS: self.nsname, off = unpack_name(buf, off) elif self.type == DNS_CNAME: self.cname, off = unpack_name(buf, off) elif self.type == DNS_PTR: self.ptrname, off = unpack_name(buf, off) elif self.type == DNS_SOA: self.mname, off = unpack_name(buf, off) self.rname, off = unpack_name(buf, off) self.serial, self.refresh, self.retry, self.expire, self.minimum = \ struct.unpack('>IIIII', buf[off:off + 20]) elif self.type == DNS_MX: self.preference = struct.unpack('>H', self.rdata[:2]) self.mxname, off = unpack_name(buf, off + 2) elif self.type == DNS_TXT or self.type == DNS_HINFO: self.text = [] buf = self.rdata while buf: n = compat_ord(buf[0]) self.text.append(codecs.decode(buf[1:1 + n], 'utf-8')) buf = buf[1 + n:] elif self.type == DNS_AAAA: self.ip6 = self.rdata elif self.type == DNS_NULL: self.null = codecs.encode(self.rdata, 'hex') elif self.type == DNS_SRV: self.priority, self.weight, self.port = struct.unpack('>HHH', self.rdata[:6]) self.srvname, off = unpack_name(buf, off + 6) elif self.type == DNS_OPT: pass # RFC-6891: OPT is a pseudo-RR not carrying any DNS data else: raise dpkt.UnpackError('RR type %s is not supported' % self.type) def pack_q(self, buf, q): """Append packed DNS question and return buf.""" return buf + pack_name(q.name, len(buf), self.label_ptrs) + struct.pack('>HH', q.type, q.cls) def unpack_q(self, buf, off): """Return DNS question and new offset.""" q = self.Q() q.name, off = unpack_name(buf, off) q.type, q.cls = struct.unpack('>HH', buf[off:off + 4]) off += 4 return q, off def pack_rr(self, buf, rr): """Append packed DNS RR and return buf.""" name = pack_name(rr.name, len(buf), self.label_ptrs) rdata = rr.pack_rdata(len(buf) + len(name) + 10, self.label_ptrs) return buf + name + struct.pack('>HHIH', rr.type, rr.cls, rr.ttl, len(rdata)) + rdata def unpack_rr(self, buf, off): """Return DNS RR and new offset.""" rr = self.RR() rr.name, off = unpack_name(buf, off) rr.type, rr.cls, rr.ttl, rdlen = struct.unpack('>HHIH', buf[off:off + 10]) off += 10 rr.rdata = buf[off:off + rdlen] rr.rlen = rdlen rr.unpack_rdata(buf, off) off += rdlen return rr, off def unpack(self, buf): dpkt.Packet.unpack(self, buf) off = self.__hdr_len__ cnt = self.qd # FIXME: This relies on this being properly set somewhere else self.qd = [] for _ in range(cnt): q, off = self.unpack_q(buf, off) self.qd.append(q) for x in ('an', 'ns', 'ar'): cnt = getattr(self, x, 0) setattr(self, x, []) for _ in range(cnt): rr, off = self.unpack_rr(buf, off) getattr(self, x).append(rr) self.data = b'' def __len__(self): # XXX - cop out return len(bytes(self)) def __bytes__(self): # XXX - compress names on the fly self.label_ptrs = {} buf = struct.pack(self.__hdr_fmt__, self.id, self.op, len(self.qd), len(self.an), len(self.ns), len(self.ar)) for q in self.qd: buf = self.pack_q(buf, q) for x in ('an', 'ns', 'ar'): for rr in getattr(self, x): buf = self.pack_rr(buf, rr) del self.label_ptrs return buf # TESTS def define_testdata(): """ Reference test data is stored in the dynamically defined class. It is created in this way so that we can import unhexlify only during testing, and not during normal use. """ from binascii import unhexlify class TestData(object): a_resp = unhexlify( "059c8180000100010000000106676f6f676c6503636f6d0000010001c00c00010" "0010000012b0004d83ace2e0000290200000000000000" ) aaaa_resp = unhexlify( "7f228180000100010000000005676d61696c03636f6d00001c0001c00c001c000" "10000012b00102a001450400908020000000000002005" ) cname_resp = unhexlify( "a154818000010001000000000377777705676d61696c03636f6d0000010001c00" "c000500010000545f000e046d61696c06676f6f676c65c016" ) invalid_rr = unhexlify( "000001000000000100000000046e616d650000150001000000000000" ) mx_resp = unhexlify( "053b8180000100010000000006676f6f676c6503636f6d00000f0001c00c000f0" "001000002570011001e04616c7432056173706d78016cc00c" ) null_resp = unhexlify( "12b0840000010001000000000b626c6168626c616836363606706972617465037" "3656100000a0001c00c000a00010000000000095641434b4403c5e901" ) opt_resp = unhexlify( "8d6e0110000100000000000104783131310678787878313106616b616d6169036" "e657400000100010000290fa0000080000000" ) ptr_resp = unhexlify( "67028180000100010003000001310131033231310331343107696e2d616464720" "46172706100000c0001c00c000c000100000d3600240764656661756c740a762d" "756d63652d69667305756d6e657405756d6963680365647500c00e00020001000" "00d36000d0673686162627903696673c04fc00e0002000100000d36000f0c6669" "73682d6c6963656e7365c06dc00e0002000100000d36000b04646e73320369746" "4c04f" ) soa_resp = unhexlify( "851f8180000100010000000006676f6f676c6503636f6d0000060001c00c00060" "001000000230026036e7332c00c09646e732d61646d696ec00c0a747447000003" "8400000384000007080000003c" ) srv_resp = unhexlify( "7f2281800001000100000000075f6a6162626572045f746370066a61626265720" "3636f6d0000210001c00c0021000100000e0f001a000a000014950764656e6a61" "6232066a616262657203636f6d00" ) txt_resp = unhexlify( "10328180000100010000000006676f6f676c6503636f6d0000100001c00c00100" "0010000010e00100f763d7370663120707472203f616c6c" ) return TestData() def test_basic(): buf = define_testdata().a_resp my_dns = DNS(buf) assert my_dns.qd[0].name == 'google.com' assert my_dns.an[0].name == 'google.com' assert bytes(my_dns) == buf class TryExceptException: def __init__(self, exception_type, msg=''): self.exception_type = exception_type self.msg = msg def __call__(self, f, *args, **kwargs): def wrapper(*args, **kwargs): try: f() except self.exception_type as e: if self.msg: assert str(e) == self.msg else: raise Exception("There should have been an Exception raised") return wrapper @TryExceptException(Exception, msg='There should have been an Exception raised') def test_TryExceptException(): """Check that we can catch a function which does not throw an exception when it is supposed to""" @TryExceptException(NotImplementedError) def fun(): pass try: fun() except Exception as e: raise e @TryExceptException(NotImplementedError) def test_Q_len(): """Test in place for when the method is written""" q = DNS.Q() len(q) @TryExceptException(NotImplementedError) def test_Q_unpack(): """Test in place for when the method is written""" q = DNS.Q() q.unpack(None) def property_runner(prop, ops, set_to=None): if set_to is None: set_to = [False, True, False] buf = define_testdata().a_resp dns = DNS(buf) for set_to, op in zip(set_to, ops): setattr(dns, prop, set_to) assert dns.op == op assert getattr(dns, prop) == set_to def test_qr(): property_runner('qr', ops=[384, 33152, 384]) def test_opcode(): property_runner('opcode', ops=[33152, 35200, 33152]) def test_aa(): property_runner('aa', ops=[33152, 34176, 33152]) def test_tc(): property_runner('tc', ops=[33152, 33664, 33152]) def test_rd(): property_runner('rd', ops=[32896, 33152, 32896]) def test_ra(): property_runner('ra', ops=[33024, 33152, 33024]) def test_zero(): property_runner('zero', ops=[33152, 33216, 33152]) def test_rcode(): property_runner('rcode', ops=[33152, 33153, 33152]) def test_PTR(): buf = define_testdata().ptr_resp my_dns = DNS(buf) assert my_dns.qd[0].name == '1.1.211.141.in-addr.arpa' and \ my_dns.an[0].ptrname == 'default.v-umce-ifs.umnet.umich.edu' and \ my_dns.ns[0].nsname == 'shabby.ifs.umich.edu' and \ my_dns.ns[1].ttl == 3382 and \ my_dns.ns[2].nsname == 'dns2.itd.umich.edu' assert buf == bytes(my_dns) def test_OPT(): buf = define_testdata().opt_resp my_dns = DNS(buf) my_rr = my_dns.ar[0] assert my_rr.type == DNS_OPT assert my_rr.rlen == 0 and my_rr.rdata == b'' assert bytes(my_dns) == buf my_rr.rdata = b'\x00\x00\x00\x02\x00\x00' # add 1 attribute tlv my_dns2 = DNS(bytes(my_dns)) my_rr2 = my_dns2.ar[0] assert my_rr2.rlen == 6 and my_rr2.rdata == b'\x00\x00\x00\x02\x00\x00' def test_pack_name(): # Empty name is \0 x = pack_name('', 0, {}) assert x == b'\0' @TryExceptException(dpkt.UnpackError) def test_unpack_name(): """If the offset is longer than the buffer, there will be an UnpackError""" unpack_name(b' ', 0) @TryExceptException(dpkt.UnpackError) def test_random_data(): DNS(b'\x83z0\xd2\x9a\xec\x94_7\xf3\xb7+\x85"?\xf0\xfb') @TryExceptException(dpkt.UnpackError) def test_circular_pointers(): DNS(b'\xc0\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\xc0\x00') @TryExceptException(dpkt.UnpackError) def test_very_long_name(): DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00' + (b'\x10abcdef0123456789' * 16) + b'\x00') def test_null_response(): buf = define_testdata().null_resp my_dns = DNS(buf) assert my_dns.qd[0].name == 'blahblah666.pirate.sea' and \ my_dns.an[0].null == b'5641434b4403c5e901' assert str(buf) == str(my_dns) def test_txt_response(): buf = define_testdata().txt_resp my_dns = DNS(buf) my_rr = my_dns.an[0] assert my_rr.type == DNS_TXT assert my_rr.name == 'google.com' assert my_rr.text == ['v=spf1 ptr ?all'] assert str(my_dns) == str(buf) assert bytes(my_dns) == buf def test_rdata_TXT(): rr = DNS.RR( type=DNS_TXT, text=[b'v=spf1 ptr ?all', b'a=something'] ) packdata = rr.pack_rdata(0, {}) correct = b'\x0fv=spf1 ptr ?all\x0ba=something' assert packdata == correct def test_rdata_HINFO(): rr = DNS.RR( type=DNS_HINFO, text=[b'v=spf1 ptr ?all', b'a=something'] ) packdata = rr.pack_rdata(0, {}) correct = b'\x0fv=spf1 ptr ?all\x0ba=something' assert packdata == correct def test_rdata_rdata(): rr = DNS.RR( name='zc.akadns.org', ttl=123446, rdata=b'?\xf1\xc76', ) packdata = rr.pack_rdata(0, {}) correct = b'?\xf1\xc76' assert packdata == correct def test_rdata_A(): rr = DNS.RR( name='zc.akadns.org', ttl=123446, ip=b'?\xf1\xc76', type=DNS_A, ) packdata = rr.pack_rdata(0, {}) correct = b'?\xf1\xc76' assert packdata == correct def test_rdata_NS(): rr = DNS.RR( nsname='zc.akadns.org', ttl=123446, ip=b'?\xf1\xc76', type=DNS_NS, ) packdata = rr.pack_rdata(0, {}) correct = b'\x02zc\x06akadns\x03org\x00' assert packdata == correct def test_rdata_CNAME(): rr = DNS.RR( cname='zc.akadns.org', ttl=123446, ip=b'?\xf1\xc76', type=DNS_CNAME, ) packdata = rr.pack_rdata(0, {}) correct = b'\x02zc\x06akadns\x03org\x00' assert packdata == correct def test_rdata_PTR(): rr = DNS.RR( ptrname='default.v-umce-ifs.umnet.umich.edu', ttl=1236, ip=b'?\xf1\xc76', type=DNS_PTR, ) packdata = rr.pack_rdata(0, {}) correct = b'\x07default\nv-umce-ifs\x05umnet\x05umich\x03edu\x00' assert packdata == correct def test_rdata_SOA(): rr = DNS.RR( mname='blah.google.com', rname='moo.blah.com', serial=12345666, refresh=123463, retry=209834, minimum=9000, expire=28341, type=DNS_SOA, ) packdata = rr.pack_rdata(0, {}) correct = ( b'\x04blah\x06google\x03com\x00\x03moo\x04blah\xc0\x0c\x00\xbcaB' b'\x00\x01\xe2G\x00\x033\xaa\x00\x00n\xb5\x00\x00#(') assert packdata == correct def test_rdata_MX(): rr = DNS.RR( type=DNS_MX, preference=2124, mxname='mail.google.com', ) packdata = rr.pack_rdata(0, {}) correct = b'\x08L\x04mail\x06google\x03com\x00' assert packdata == correct def test_rdata_AAAA(): ip6 = b'&\x07\xf8\xb0@\x0c\x0c\x03\x00\x00\x00\x00\x00\x00\x00\x1a' rr = DNS.RR( type=DNS_AAAA, ip6=ip6, ) packdata = rr.pack_rdata(0, {}) correct = ip6 assert packdata == correct def test_rdata_SRV(): rr = DNS.RR( type=DNS_SRV, ttl=86400, priority=0, weight=5, port=5060, srvname='_sip._tcp.example.com', ) packdata = rr.pack_rdata(0, {}) correct = b'\x00\x00\x00\x05\x13\xc4\x04_sip\x04_tcp\x07example\x03com\x00' assert packdata == correct def test_rdata_OPT(): rr = DNS.RR( type=DNS_OPT, ) # TODO: This is hardcoded to return b''. Is this intentional? packdata = rr.pack_rdata(0, {}) correct = b'' assert packdata == correct def test_dns_len(): my_dns = DNS() assert len(my_dns) == 12 @TryExceptException(dpkt.PackError) def test_rdata_FAIL(): DNS.RR(type=12345666).pack_rdata(0, {}) def test_soa(): buf = define_testdata().soa_resp soa = DNS(buf) assert soa.id == 34079 assert soa.op == 33152 assert len(soa.qd) == 1 q = soa.qd[0] assert q.name == 'google.com' assert q.type == DNS_SOA assert q.cls == DNS_IN assert len(soa.an) == 1 a = soa.an[0] assert a.name == 'google.com' assert a.type == DNS_SOA assert a.cls == DNS_IN assert a.ttl == 35 assert a.retry == 900 assert a.mname == 'ns2.google.com' assert a.minimum == 60 assert a.refresh == 900 assert a.expire == 1800 assert a.serial == 175404103 assert a.rlen == 38 assert a.rname == 'dns-admin.google.com' assert a.rdata == b'\x03ns2\xc0\x0c\tdns-admin\xc0\x0c\nttG\x00\x00\x03\x84\x00\x00\x03\x84\x00\x00\x07\x08\x00\x00\x00<' assert soa.ar == [] def test_mx(): buf = define_testdata().mx_resp mx = DNS(buf) assert mx.id == 1339 assert mx.op == 33152 assert len(mx.qd) == 1 q = mx.qd[0] assert q.name == 'google.com' assert q.type == DNS_MX assert q.cls == DNS_IN assert len(mx.an) == 1 a = mx.an[0] assert a.type == DNS_MX assert a.cls == DNS_IN assert a.name == 'google.com' assert a.ttl == 599 assert a.mxname == 'alt2.aspmx.l.google.com' assert a.preference == (30,) assert a.rlen == 17 assert a.rdata == b'\x00\x1e\x04alt2\x05aspmx\x01l\xc0\x0c' assert mx.ar == [] def test_aaaa(): buf = define_testdata().aaaa_resp aaaa = DNS(buf) aaaa.id = 32546 aaaa.op = 33152 assert len(aaaa.qd) == 1 q = aaaa.qd[0] assert q.type == DNS_AAAA assert q.name == 'gmail.com' assert len(aaaa.an) == 1 a = aaaa.an[0] assert a.type == DNS_AAAA assert a.cls == DNS_IN assert a.name == 'gmail.com' assert a.ttl == 299 assert a.ip6 == b'*\x00\x14P@\t\x08\x02\x00\x00\x00\x00\x00\x00 \x05' assert a.rlen == 16 assert a.rdata == b'*\x00\x14P@\t\x08\x02\x00\x00\x00\x00\x00\x00 \x05' assert aaaa.ar == [] def test_srv(): buf = define_testdata().srv_resp srv = DNS(buf) srv.id = 32546 srv.op = 33152 assert len(srv.qd) == 1 q = srv.qd[0] assert q.type == DNS_SRV assert q.name == '_jabber._tcp.jabber.com' assert q.cls == DNS_IN assert len(srv.an) == 1 a = srv.an[0] assert a.type == DNS_SRV assert a.cls == DNS_IN assert a.name == '_jabber._tcp.jabber.com' assert a.port == 5269 assert a.ttl == 3599 assert a.srvname == 'denjab2.jabber.com' assert a.priority == 10 assert a.weight == 0 assert a.rlen == 26 assert a.rdata == b'\x00\n\x00\x00\x14\x95\x07denjab2\x06jabber\x03com\x00' assert srv.ar == [] def test_cname(): buf = define_testdata().cname_resp cname = DNS(buf) cname.id = 41300 cname.op = 33152 assert len(cname.qd) == 1 q = cname.qd[0] assert q.type == DNS_A assert q.cls == DNS_IN assert q.name == 'www.gmail.com' assert len(cname.an) == 1 a = cname.an[0] assert a.type == DNS_CNAME assert a.cls == DNS_IN assert a.name == 'www.gmail.com' assert a.ttl == 21599 assert a.cname == 'mail.google.com' assert a.rlen == 14 assert a.rdata == b'\x04mail\x06google\xc0\x16' assert cname.ar == [] @TryExceptException(dpkt.UnpackError) def test_invalid_rr(): buf = define_testdata().invalid_rr DNS(buf) dpkt-1.9.8/dpkt/dpkt.py000066400000000000000000000455001427735150300147640ustar00rootroot00000000000000# $Id: dpkt.py 43 2007-08-02 22:42:59Z jon.oberheide $ # -*- coding: utf-8 -*- """Simple packet creation and parsing. The dpkt project is a python module for fast, simple packet parsing, with definitions for the basic TCP/IP protocols. """ from __future__ import absolute_import, print_function import copy import struct from functools import partial from itertools import chain from .compat import compat_ord, compat_izip, iteritems, ntole class Error(Exception): pass class UnpackError(Error): pass class NeedData(UnpackError): pass class PackError(Error): pass # See the "creating parsers" documentation for how all of this works class _MetaPacket(type): def __new__(cls, clsname, clsbases, clsdict): t = type.__new__(cls, clsname, clsbases, clsdict) st = getattr(t, '__hdr__', None) if st is not None: # XXX - __slots__ only created in __new__() clsdict['__slots__'] = [x[0] for x in st] + ['data'] t = type.__new__(cls, clsname, clsbases, clsdict) t.__hdr_fields__ = [x[0] for x in st] t.__hdr_fmt__ = getattr(t, '__byte_order__', '>') + ''.join([x[1] for x in st]) t.__hdr_len__ = struct.calcsize(t.__hdr_fmt__) t.__hdr_defaults__ = dict(compat_izip( t.__hdr_fields__, [x[2] for x in st])) # process __bit_fields__ bit_fields = getattr(t, '__bit_fields__', None) if bit_fields: t.__bit_fields_defaults__ = {} # bit fields equivalent of __hdr_defaults__ for (ph_name, ph_struct, ph_default) in t.__hdr__: # ph: placeholder variable for the bit field if ph_name in bit_fields: field_defs = bit_fields[ph_name] bits_total = sum(bf[1] for bf in field_defs) # total size in bits bits_used = 0 # make sure the sum of bits matches the overall size of the placeholder field assert bits_total == struct.calcsize(ph_struct) * 8, \ "the overall count of bits in [%s] as declared in __bit_fields__ " \ "does not match its struct size in __hdr__" % ph_name for (bf_name, bf_size) in field_defs: if bf_name.startswith('_'): # do not create properties for _private fields bits_used += bf_size continue shift = bits_total - bits_used - bf_size mask = (2**bf_size - 1) << shift # all zeroes except the field bits mask_inv = (2**bits_total - 1) - mask # inverse mask bits_used += bf_size # calculate the default value for the bit field bf_default = (t.__hdr_defaults__[ph_name] & mask) >> shift t.__bit_fields_defaults__[bf_name] = bf_default # create getter, setter and delete properties for the bit fields def make_getter(ph_name=ph_name, mask=mask, shift=shift): def getter_func(self): ph_val = getattr(self, ph_name) return (ph_val & mask) >> shift return getter_func def make_setter(ph_name=ph_name, mask_inv=mask_inv, shift=shift, bf_name=bf_name, max_val=2**bf_size): def setter_func(self, bf_val): # ensure the given value fits into the number of bits available if bf_val >= max_val: raise ValueError('value %s is too large for field %s' % (bf_val, bf_name)) ph_val = getattr(self, ph_name) ph_val_new = (bf_val << shift) | (ph_val & mask_inv) setattr(self, ph_name, ph_val_new) return setter_func # delete property to set the bit field back to its default value def make_delete(bf_name=bf_name, bf_default=bf_default): def delete_func(self): setattr(self, bf_name, bf_default) return delete_func setattr(t, bf_name, property(make_getter(), make_setter(), make_delete())) # optional map of functions for pretty printing # {field_name: callable(field_value) -> str, ..} # define as needed in the child protocol classes #t.__pprint_funcs__ = {} - disabled here to keep the base class lightweight # placeholder for __public_fields__, a class attribute used in __repr__ and pprint() t.__public_fields__ = None return t class Packet(_MetaPacket("Temp", (object,), {})): r"""Base packet class, with metaclass magic to generate members from self.__hdr__. Attributes: __hdr__: Packet header should be defined as a list of (name, structfmt, default) tuples. __byte_order__: Byte order, can be set to override the default ('>') Example: >>> class Foo(Packet): ... __hdr__ = (('foo', 'I', 1), ('bar', 'H', 2), ('baz', '4s', 'quux')) ... >>> foo = Foo(bar=3) >>> foo Foo(bar=3) >>> str(foo) '\x00\x00\x00\x01\x00\x03quux' >>> foo.bar 3 >>> foo.baz 'quux' >>> foo.foo = 7 >>> foo.baz = 'whee' >>> foo Foo(baz='whee', foo=7, bar=3) >>> Foo('hello, world!') Foo(baz=' wor', foo=1751477356L, bar=28460, data='ld!') """ def __init__(self, *args, **kwargs): """Packet constructor with ([buf], [field=val,...]) prototype. Arguments: buf -- optional packet buffer to unpack Optional keyword arguments correspond to members to set (matching fields in self.__hdr__, or 'data'). """ self.data = b'' if args: try: self.unpack(args[0]) except struct.error: if len(args[0]) < self.__hdr_len__: raise NeedData('got %d, %d needed at least' % (len(args[0]), self.__hdr_len__)) raise UnpackError('invalid %s: %r' % (self.__class__.__name__, args[0])) else: if hasattr(self, '__hdr_fields__'): for k in self.__hdr_fields__: setattr(self, k, copy.copy(self.__hdr_defaults__[k])) for k, v in iteritems(kwargs): setattr(self, k, v) if hasattr(self, '__hdr_fmt__'): self._pack_hdr = partial(struct.pack, self.__hdr_fmt__) def __len__(self): return self.__hdr_len__ + len(self.data) # legacy def __iter__(self): return iter((fld, getattr(self, fld)) for fld in self.__class__.__hdr_fields__) def __getitem__(self, kls): """Return the 1st occurrence of the underlying data layer, raise KeyError otherwise.""" dd = self.data while isinstance(dd, Packet): if dd.__class__ == kls: return dd dd = dd.data raise KeyError(kls) def __contains__(self, kls): """Return True is the given data layer is present in the stack.""" try: return bool(self.__getitem__(kls)) except KeyError: return False def _create_public_fields(self): """Construct __public_fields__ to be used inside __repr__ and pprint""" l_ = [] for field_name, _, _ in getattr(self, '__hdr__', []): # public fields defined in __hdr__; "public" means not starting with an underscore if field_name[0] != '_': l_.append(field_name) # (1) # if a field name starts with an underscore, and does NOT contain more underscores, # it is considered hidden and is ignored (good for fields reserved for future use) # if a field name starts with an underscore, and DOES contain more underscores, # it is viewed as a complex field where underscores separate the named properties # of the class; elif '_' in field_name[1:]: # (1) search for these properties in __bit_fields__ where they are explicitly defined if field_name in getattr(self, '__bit_fields__', {}): for (prop_name, _) in self.__bit_fields__[field_name]: if isinstance(getattr(self.__class__, prop_name, None), property): l_.append(prop_name) # (2) split by underscore into 1- and 2-component names and look for properties with such names; # Example: _foo_bar_baz -> look for properties named "foo", "bar", "baz", "foo_bar" and "bar_baz" # (but not "foo_bar_baz" since it contains more than one underscore) else: fns = field_name[1:].split('_') for prop_name in chain(fns, ('_'.join(x) for x in zip(fns, fns[1:]))): if isinstance(getattr(self.__class__, prop_name, None), property): l_.append(prop_name) # check for duplicates, there shouldn't be any assert len(l_) == len(set(l_)) self.__class__.__public_fields__ = l_ # store it in the class attribute def __repr__(self): if self.__public_fields__ is None: self._create_public_fields() # Collect and display protocol fields in order: # 1. public fields defined in __hdr__, unless their value is default # 2. properties derived from _private fields defined in __hdr__ and __bit_fields__ # 3. dynamically added fields from self.__dict__, unless they are _private # 4. self.data when it's present l_ = [] # (1) and (2) are done via __public_fields__; just filter out defaults here for field_name in self.__public_fields__: field_value = getattr(self, field_name) if (hasattr(self, '__hdr_defaults__') and field_name in self.__hdr_defaults__ and field_value == self.__hdr_defaults__[field_name]): continue if (hasattr(self, '__bit_fields_defaults__') and field_name in self.__bit_fields_defaults__ and field_value == self.__bit_fields_defaults__[field_name]): continue l_.append('%s=%r' % (field_name, field_value)) # (3) l_.extend( ['%s=%r' % (attr_name, attr_value) for attr_name, attr_value in iteritems(self.__dict__) if attr_name[0] != '_' and # exclude _private attributes attr_name != self.data.__class__.__name__.lower()]) # exclude fields like ip.udp # (4) if self.data: l_.append('data=%r' % self.data) return '%s(%s)' % (self.__class__.__name__, ', '.join(l_)) def pprint(self, indent=1): """Human friendly pretty-print.""" if self.__public_fields__ is None: self._create_public_fields() l_ = [] def add_field(fn, fv): """name=value, # pretty-print form (if available)""" try: l_.append('%s=%r, # %s' % (fn, fv, self.__pprint_funcs__[fn](fv))) except (AttributeError, KeyError): l_.append('%s=%r,' % (fn, fv)) for field_name in self.__public_fields__: add_field(field_name, getattr(self, field_name)) for attr_name, attr_value in iteritems(self.__dict__): if (attr_name[0] != '_' and # exclude _private attributes attr_name != self.data.__class__.__name__.lower()): # exclude fields like ip.udp if type(attr_value) == list and attr_value: # expand non-empty lists to print one item per line l_.append('%s=[' % attr_name) for av1 in attr_value: l_.append(' ' + repr(av1) + ',') # XXX - TODO: support pretty-print l_.append('],') else: add_field(attr_name, attr_value) print('%s(' % self.__class__.__name__) # class name, opening brace for ii in l_: print(' ' * indent, '%s' % ii) if self.data: if isinstance(self.data, Packet): # recursively descend to lower layers print(' ' * indent, 'data=', end='') self.data.pprint(indent=indent + 2) else: print(' ' * indent, 'data=%r' % self.data) print(' ' * (indent - 1), end='') print(') # %s' % self.__class__.__name__) # closing brace # class name def __str__(self): return str(self.__bytes__()) def __bytes__(self): return self.pack_hdr() + bytes(self.data) def pack_hdr(self): """Return packed header string.""" try: return self._pack_hdr( *[getattr(self, k) for k in self.__hdr_fields__] ) except (TypeError, struct.error): vals = [] for k in self.__hdr_fields__: v = getattr(self, k) if isinstance(v, tuple): vals.extend(v) else: vals.append(v) try: return struct.pack(self.__hdr_fmt__, *vals) except struct.error as e: raise PackError(str(e)) def pack(self): """Return packed header + self.data string.""" return bytes(self) def unpack(self, buf): """Unpack packet header fields from buf, and set self.data.""" for k, v in compat_izip(self.__hdr_fields__, struct.unpack(self.__hdr_fmt__, buf[:self.__hdr_len__])): setattr(self, k, v) self.data = buf[self.__hdr_len__:] # XXX - ''.join([(len(`chr(x)`)==3) and chr(x) or '.' for x in range(256)]) __vis_filter = ( b'................................ !"#$%&\'()*+,-./0123456789:;<=>?' b'@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~.' b'................................................................' b'................................................................') def hexdump(buf, length=16): """Return a hexdump output string of the given buffer.""" n = 0 res = [] while buf: line, buf = buf[:length], buf[length:] hexa = ' '.join(['%02x' % compat_ord(x) for x in line]) line = line.translate(__vis_filter).decode('utf-8') res.append(' %04d: %-*s %s' % (n, length * 3, hexa, line)) n += length return '\n'.join(res) def in_cksum_add(s, buf): n = len(buf) cnt = (n // 2) * 2 a = struct.unpack('<{}H'.format(n // 2), buf[:cnt]) # unpack as little endian words res = s + sum(a) if cnt != n: res += compat_ord(buf[-1]) return res def in_cksum_done(s): s = (s >> 16) + (s & 0xffff) s += (s >> 16) return ntole(~s & 0xffff) def in_cksum(buf): """Return computed Internet checksum.""" return in_cksum_done(in_cksum_add(0, buf)) def test_utils(): __buf = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e' __hd = ' 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e ...............' h = hexdump(__buf) assert (h == __hd) assert in_cksum_add(0, __buf) == 12600 # endianness c = in_cksum(__buf) assert (c == 51150) # test Packet.__getitem__ and __contains__ methods def test_getitem_contains(): import pytest class Foo(Packet): __hdr__ = (('foo', 'I', 0),) class Bar(Packet): __hdr__ = (('bar', 'I', 0),) class Baz(Packet): __hdr__ = (('baz', 'I', 0),) class Zeb(Packet): pass ff = Foo(foo=1, data=Bar(bar=2, data=Baz(attr=Zeb()))) # __contains__ assert Bar in ff assert Baz in ff assert Baz in ff.data assert Zeb not in ff assert Zeb not in Baz() # __getitem__ assert isinstance(ff[Bar], Bar) assert isinstance(ff[Baz], Baz) assert isinstance(ff[Bar][Baz], Baz) with pytest.raises(KeyError): ff[Baz][Bar] with pytest.raises(KeyError): ff[Zeb] with pytest.raises(KeyError): Bar()[Baz] def test_pack_hdr_overflow(): """Try to fit too much data into struct packing""" import pytest class Foo(Packet): __hdr__ = ( ('foo', 'I', 1), ('bar', 'I', (1, 2)), ) foo = Foo(foo=2**32) with pytest.raises(PackError): bytes(foo) def test_bit_fields_overflow(): """Try to fit too much data into too few bits""" import pytest class Foo(Packet): __hdr__ = ( ('_a_b', 'B', 0), ) __bit_fields__ = { '_a_b': ( ('a', 2), ('b', 6), ) } foo = Foo() with pytest.raises(ValueError): foo.a = 5 def test_pack_hdr_tuple(): """Test the unpacking of a tuple for a single format string""" class Foo(Packet): __hdr__ = ( ('bar', 'II', (1, 2)), ) foo = Foo() b = bytes(foo) assert b == b'\x00\x00\x00\x01\x00\x00\x00\x02' def test_unpacking_failure(): # during dynamic-sized unpacking in the subclass there may be struct.errors raised, # but if the header has unpacked correctly, a different error is raised by the superclass import pytest class TestPacket(Packet): __hdr__ = (('test', 'B', 0),) def unpack(self, buf): Packet.unpack(self, buf) self.attribute = struct.unpack('B', buf[1:]) with pytest.raises(UnpackError, match="invalid TestPacket: "): TestPacket(b'\x00') # header will unpack successfully def test_repr(): """complex test for __repr__, __public_fields__""" class TestPacket(Packet): __hdr__ = ( ('_a_b', 'B', 1), # 'a' and 'b' bit fields ('_rsv', 'B', 0), # hidden reserved field ('_c_flag', 'B', 1), # 'c_flag' property ('d', 'B', 0) # regular field ) __bit_fields__ = { '_a_b': ( ('a', 4), ('b', 4), ), } @property def c_flag(self): return (self.a | self.b) # init with default values test_packet = TestPacket() # test repr with all default values so expect no output # (except for the explicitly defined property, where dpkt doesn't process defaults yet) assert repr(test_packet) == "TestPacket(c_flag=1)" # init with non-default values test_packet = TestPacket(b'\x12\x11\x00\x04') # ensure the display fields were cached and propagated via class attribute assert test_packet.__public_fields__ == ['a', 'b', 'c_flag', 'd'] # verify repr assert repr(test_packet) == "TestPacket(a=1, b=2, c_flag=3, d=4)" dpkt-1.9.8/dpkt/dtp.py000066400000000000000000000032001427735150300146000ustar00rootroot00000000000000# $Id: dtp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Dynamic Trunking Protocol.""" from __future__ import absolute_import import struct from . import dpkt TRUNK_NAME = 0x01 MAC_ADDR = 0x04 class DTP(dpkt.Packet): """Dynamic Trunking Protocol. The Dynamic Trunking Protocol (DTP) is a proprietary networking protocol developed by Cisco Systems for the purpose of negotiating trunking on a link between two VLAN-aware switches, and for negotiating the type of trunking encapsulation to be used. It works on Layer 2 of the OSI model. VLAN trunks formed using DTP may utilize either IEEE 802.1Q or Cisco ISL trunking protocols. Attributes: __hdr__: Header fields of DTP. v: (int) Version. (1 byte) """ __hdr__ = ( ('v', 'B', 0), ) # rest is TLVs def unpack(self, buf): dpkt.Packet.unpack(self, buf) buf = self.data tvs = [] while buf: t, l_ = struct.unpack('>HH', buf[:4]) v, buf = buf[4:4 + l_], buf[4 + l_:] tvs.append((t, v)) self.data = tvs def __bytes__(self): return b''.join([struct.pack('>HH', t, len(v)) + v for t, v in self.data]) def test_creation(): dtp1 = DTP() assert dtp1.v == 0 from binascii import unhexlify buf = unhexlify( '04' '0001' # type '0002' # length '1234' # value ) dtp2 = DTP(buf) assert dtp2.v == 4 assert len(dtp2.data) == 1 tlvs = dtp2.data tlv = tlvs[0] key, value = tlv assert key == 1 assert value == unhexlify('1234') assert bytes(dtp2) == buf[1:] dpkt-1.9.8/dpkt/edp.py000066400000000000000000000043631427735150300145740ustar00rootroot00000000000000"""Extreme Discovery Protocol.""" from __future__ import absolute_import import dpkt class EDP(dpkt.Packet): __hdr__ = ( ('version', 'B', 1), ('reserved', 'B', 0), ('hlen', 'H', 0), ('sum', 'H', 0), ('seq', 'H', 0), ('mid', 'H', 0), ('mac', '6s', b'') ) def __bytes__(self): if not self.sum: self.sum = dpkt.in_cksum(dpkt.Packet.__bytes__(self)) return dpkt.Packet.__bytes__(self) class TestEDP(object): """ Test basic EDP functionality. """ @classmethod def setup_class(cls): from binascii import unhexlify cls.buf = unhexlify( '01' # version '00' # reserved '013c' # hlen '9e76' # sum '001b' # seq '0000' # mid '080027' # mac '2d90ed990200240000000000000000000000000f020207000000000000000000000000000000009901010445584f532d32000000000000000' '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' '00000000000000000000000000000000099000004' ) cls.p = EDP(cls.buf) def test_version(self): assert (self.p.version == 1) def test_reserved(self): assert (self.p.reserved == 0) def test_hlen(self): assert (self.p.hlen == 316) def test_sum(self): assert (self.p.sum == 40566) def test_seq(self): assert (self.p.seq == 27) def test_mid(self): assert (self.p.mid == 0) def test_mac(self): assert (self.p.mac == b"\x08\x00'-\x90\xed") def test_bytes(self): assert bytes(self.p) == self.buf # force recalculation of the checksum edp = EDP(self.buf) edp.sum = 0 assert edp.sum == 0 assert bytes(edp) == self.buf dpkt-1.9.8/dpkt/esp.py000066400000000000000000000020021427735150300145770ustar00rootroot00000000000000# $Id: esp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Encapsulated Security Protocol.""" from __future__ import absolute_import from . import dpkt class ESP(dpkt.Packet): """Encapsulated Security Protocol. Encapsulating Security Payload (ESP) is a member of the Internet Protocol Security (IPsec) set of protocols that encrypt and authenticate the packets of data between computers using a Virtual Private Network (VPN). The focus and layer on which ESP operates makes it possible for VPNs to function securely. Attributes: __hdr__: Header fields of ESP. spi: (int): Security Parameters Index. An arbitrary value that, in combination with the destination IP address and security protocol (ESP), uniquely identifies the SA for this datagram. (4 bytes) spi: (int): Sequence number. This field contains a monotonically increasing counter value. (4 bytes) """ __hdr__ = ( ('spi', 'I', 0), ('seq', 'I', 0) ) dpkt-1.9.8/dpkt/ethernet.py000066400000000000000000000763041427735150300156460ustar00rootroot00000000000000# $Id: ethernet.py 65 2010-03-26 02:53:51Z dugsong $ # -*- coding: utf-8 -*- """ Ethernet II, LLC (802.3+802.2), LLC/SNAP, and Novell raw 802.3, with automatic 802.1q, MPLS, PPPoE, and Cisco ISL decapsulation. """ from __future__ import print_function from __future__ import absolute_import import struct from zlib import crc32 from . import dpkt from . import llc from .utils import mac_to_str from .compat import compat_ord, iteritems, isstr ETH_CRC_LEN = 4 ETH_HDR_LEN = 14 ETH_LEN_MIN = 64 # minimum frame length with CRC ETH_LEN_MAX = 1518 # maximum frame length with CRC ETH_MTU = (ETH_LEN_MAX - ETH_HDR_LEN - ETH_CRC_LEN) ETH_MIN = (ETH_LEN_MIN - ETH_HDR_LEN - ETH_CRC_LEN) # Ethernet payload types - http://standards.ieee.org/regauth/ethertype ETH_TYPE_UNKNOWN = 0x0000 ETH_TYPE_EDP = 0x00bb # Extreme Networks Discovery Protocol ETH_TYPE_PUP = 0x0200 # PUP protocol ETH_TYPE_IP = 0x0800 # IP protocol ETH_TYPE_ARP = 0x0806 # address resolution protocol ETH_TYPE_AOE = 0x88a2 # AoE protocol ETH_TYPE_CDP = 0x2000 # Cisco Discovery Protocol ETH_TYPE_DTP = 0x2004 # Cisco Dynamic Trunking Protocol ETH_TYPE_REVARP = 0x8035 # reverse addr resolution protocol ETH_TYPE_8021Q = 0x8100 # IEEE 802.1Q VLAN tagging ETH_TYPE_8021AD = 0x88a8 # IEEE 802.1ad ETH_TYPE_QINQ1 = 0x9100 # Legacy QinQ ETH_TYPE_QINQ2 = 0x9200 # Legacy QinQ ETH_TYPE_IPX = 0x8137 # Internetwork Packet Exchange ETH_TYPE_IP6 = 0x86DD # IPv6 protocol ETH_TYPE_PPP = 0x880B # PPP ETH_TYPE_MPLS = 0x8847 # MPLS ETH_TYPE_MPLS_MCAST = 0x8848 # MPLS Multicast ETH_TYPE_PPPoE_DISC = 0x8863 # PPP Over Ethernet Discovery Stage ETH_TYPE_PPPoE = 0x8864 # PPP Over Ethernet Session Stage ETH_TYPE_LLDP = 0x88CC # Link Layer Discovery Protocol ETH_TYPE_TEB = 0x6558 # Transparent Ethernet Bridging ETH_TYPE_PROFINET = 0x8892 # PROFINET protocol # all QinQ types for fast checking _ETH_TYPES_QINQ = frozenset([ETH_TYPE_8021Q, ETH_TYPE_8021AD, ETH_TYPE_QINQ1, ETH_TYPE_QINQ2]) class Ethernet(dpkt.Packet): """Ethernet. Ethernet II, LLC (802.3+802.2), LLC/SNAP, and Novell raw 802.3, with automatic 802.1q, MPLS, PPPoE, and Cisco ISL decapsulation. Attributes: __hdr__: Header fields of Ethernet. dst: (bytes): Destination MAC address src: (bytes): Source MAC address type: (int): Ethernet frame type (Ethernet II, Novell raw IEEE 802.3, IEEE 802.2 LLC, IEEE 802.2 SNAP) """ __hdr__ = ( ('dst', '6s', b''), ('src', '6s', b''), ('type', 'H', ETH_TYPE_IP) ) _typesw = {} _typesw_rev = {} # reverse mapping __pprint_funcs__ = { 'dst': mac_to_str, 'src': mac_to_str, } def __init__(self, *args, **kwargs): self._next_type = None dpkt.Packet.__init__(self, *args, **kwargs) # if data was given in kwargs, try to unpack it if self.data: if isstr(self.data) or isinstance(self.data, bytes): self._unpack_data(self.data) def _unpack_data(self, buf): # unpack vlan tag and mpls label stacks if self._next_type in _ETH_TYPES_QINQ: self.vlan_tags = [] # support up to 2 tags (double tagging aka QinQ) for _ in range(2): tag = VLANtag8021Q(buf) buf = buf[tag.__hdr_len__:] self.vlan_tags.append(tag) self._next_type = tag.type if self._next_type != ETH_TYPE_8021Q: break # backward compatibility, use the 1st tag self.vlanid, self.priority, self.cfi = self.vlan_tags[0].as_tuple() elif self._next_type == ETH_TYPE_MPLS or self._next_type == ETH_TYPE_MPLS_MCAST: self.labels = [] # old list containing labels as tuples self.mpls_labels = [] # new list containing labels as instances of MPLSlabel # XXX - max # of labels is undefined, just use 24 for i in range(24): lbl = MPLSlabel(buf) buf = buf[lbl.__hdr_len__:] self.mpls_labels.append(lbl) self.labels.append(lbl.as_tuple()) if lbl.s: # bottom of stack break # poor man's heuristics to guessing the next type if compat_ord(buf[0]) == 0x45: # IP version 4 + header len 20 bytes self._next_type = ETH_TYPE_IP elif compat_ord(buf[0]) & 0xf0 == 0x60: # IP version 6 self._next_type = ETH_TYPE_IP6 # pseudowire Ethernet elif len(buf) >= self.__hdr_len__: if buf[:2] == b'\x00\x00': # looks like the control word (ECW) buf = buf[4:] # skip the ECW self._next_type = ETH_TYPE_TEB # re-use TEB class mapping to decode Ethernet try: eth_type = self._next_type or self.type self.data = self._typesw[eth_type](buf) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): self.data = buf def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.type > 1500: # Ethernet II self._next_type = self.type self._unpack_data(self.data) elif (self.dst.startswith(b'\x01\x00\x0c\x00\x00') or self.dst.startswith(b'\x03\x00\x0c\x00\x00')): # Cisco ISL tag = VLANtagISL(buf) buf = buf[tag.__hdr_len__:] self.vlan_tags = [tag] self.vlan = tag.id # backward compatibility self.unpack(buf) elif self.data.startswith(b'\xff\xff'): # Novell "raw" 802.3 self.type = ETH_TYPE_IPX self.data = self.ipx = self._typesw[ETH_TYPE_IPX](self.data[2:]) elif self.type == ETH_TYPE_UNKNOWN: # Unknown type, assume Ethernet self._unpack_data(self.data) else: # IEEE 802.3 Ethernet - LLC # try to unpack FCS, padding and trailer here # we follow a heuristic approach similar to that of Wireshark # size of eth body, not including the header eth_len = self.len = self.type # actual size of the remaining data, could include eth body, padding, fcs, trailer data_len = len(self.data) if data_len > eth_len: # everything after eth body tail = self.data[eth_len:] # could be padding + fcs, possibly trailer if len(tail) > 4: # determine size of padding if eth_len < 46: # 46=60-14; 14=size of eth hdr; all padded to 60 bytes pad_len = 46 - eth_len padding = tail[:pad_len] # heuristic if padding == pad_len * b'\x00': # padding is likely zeroes self.padding = padding tail = tail[pad_len:] # else proceed to decode as fcs+trailer # 4 bytes FCS and possible trailer if len(tail) >= 4: self.fcs = struct.unpack('>I', tail[:4])[0] tail = tail[4:] if tail: self.trailer = tail self.data = self.llc = llc.LLC(self.data[:eth_len]) def pack_hdr(self): tags_buf = b'' new_type = self.type # replacement self.type when packing eth header is_isl = False # ISL wraps Ethernet, this determines order of packing if getattr(self, 'mpls_labels', None): # mark all labels with s=0, last one with s=1 for lbl in self.mpls_labels: lbl.s = 0 lbl.s = 1 # set encapsulation type if new_type not in (ETH_TYPE_MPLS, ETH_TYPE_MPLS_MCAST): new_type = ETH_TYPE_MPLS tags_buf = b''.join(lbl.pack_hdr() for lbl in self.mpls_labels) elif getattr(self, 'vlan_tags', None): # set last tag type to next layer pointed by self.data last_tag_type = self.type # default if isinstance(self.data, dpkt.Packet): last_tag_type = self._typesw_rev.get(self.data.__class__, self.type) # set encapsulation types t1 = self.vlan_tags[0] if len(self.vlan_tags) == 1: if isinstance(t1, VLANtag8021Q): if new_type not in _ETH_TYPES_QINQ: # preserve the type if already set new_type = ETH_TYPE_8021Q t1.type = last_tag_type elif isinstance(t1, VLANtagISL): t1.type = 0 # 0 means Ethernet is_isl = True elif len(self.vlan_tags) == 2: t2 = self.vlan_tags[1] if isinstance(t1, VLANtag8021Q) and isinstance(t2, VLANtag8021Q): t1.type = ETH_TYPE_8021Q if new_type not in _ETH_TYPES_QINQ: new_type = ETH_TYPE_8021AD t2.type = last_tag_type else: raise dpkt.PackError('maximum is 2 VLAN tags per Ethernet frame') tags_buf = b''.join(tag.pack_hdr() for tag in self.vlan_tags) # initial type is based on next layer, pointed by self.data; # try to find an ETH_TYPE matching the data class elif isinstance(self.data, dpkt.Packet): new_type = self._typesw_rev.get(self.data.__class__, new_type) # if self.data is LLC then this is IEEE 802.3 Ethernet and self.type # then actually encodes the length of data if isinstance(self.data, llc.LLC): new_type = len(self.data) hdr_buf = dpkt.Packet.pack_hdr(self)[:-2] + struct.pack('>H', new_type) if not is_isl: return hdr_buf + tags_buf else: return tags_buf + hdr_buf def __bytes__(self): tail = b'' if isinstance(self.data, llc.LLC): fcs = b'' if hasattr(self, 'fcs'): if self.fcs: fcs = self.fcs else: # if fcs field is present but 0/None, then compute it and add to the tail fcs_buf = self.pack_hdr() + bytes(self.data) # if ISL header is present, exclude it from the calculation if getattr(self, 'vlan_tags', None): if isinstance(self.vlan_tags[0], VLANtagISL): fcs_buf = fcs_buf[VLANtagISL.__hdr_len__:] fcs_buf += getattr(self, 'padding', b'') revcrc = crc32(fcs_buf) & 0xffffffff fcs = struct.unpack('I', revcrc))[0] # bswap32 fcs = struct.pack('>I', fcs) tail = getattr(self, 'padding', b'') + fcs + getattr(self, 'trailer', b'') return bytes(dpkt.Packet.__bytes__(self) + tail) def __len__(self): tags = getattr(self, 'mpls_labels', []) + getattr(self, 'vlan_tags', []) _len = dpkt.Packet.__len__(self) + sum(t.__hdr_len__ for t in tags) if isinstance(self.data, llc.LLC): _len += len(getattr(self, 'padding', b'')) if hasattr(self, 'fcs'): _len += 4 _len += len(getattr(self, 'trailer', b'')) return _len @classmethod def set_type(cls, t, pktclass): cls._typesw[t] = pktclass cls._typesw_rev[pktclass] = t @classmethod def get_type(cls, t): return cls._typesw[t] @classmethod def get_type_rev(cls, k): return cls._typesw_rev[k] # XXX - auto-load Ethernet dispatch table from ETH_TYPE_* definitions def __load_types(): g = globals() for k, v in iteritems(g): if k.startswith('ETH_TYPE_'): name = k[9:] modname = name.lower() try: mod = __import__(modname, g, level=1) Ethernet.set_type(v, getattr(mod, name)) except (ImportError, AttributeError): continue # add any special cases below Ethernet.set_type(ETH_TYPE_TEB, Ethernet) def _mod_init(): """Post-initialization called when all dpkt modules are fully loaded""" if not Ethernet._typesw: __load_types() # Misc protocols class MPLSlabel(dpkt.Packet): """A single entry in MPLS label stack""" __hdr__ = ( ('_val_exp_s_ttl', 'I', 0), ) # field names are according to RFC3032 __bit_fields__ = { '_val_exp_s_ttl': ( ('val', 20), # label value, 20 bits ('exp', 3), # experimental use, 3 bits ('s', 1), # bottom of stack flag, 1 bit ('ttl', 8), # time to live, 8 bits ) } def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = b'' def as_tuple(self): # backward-compatible representation return (self.val, self.exp, self.ttl) class VLANtag8021Q(dpkt.Packet): """IEEE 802.1q VLAN tag""" __hdr__ = ( ('_pri_cfi_id', 'H', 0), ('type', 'H', ETH_TYPE_IP) ) __bit_fields__ = { '_pri_cfi_id': ( ('pri', 3), # priority, 3 bits ('cfi', 1), # canonical format indicator, 1 bit ('id', 12), # VLAN id, 12 bits ) } def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = b'' def as_tuple(self): return (self.id, self.pri, self.cfi) class VLANtagISL(dpkt.Packet): """Cisco Inter-Switch Link VLAN tag""" __hdr__ = ( ('da', '5s', b'\x01\x00\x0c\x00\x00'), ('_type_pri', 'B', 3), ('sa', '6s', b''), ('len', 'H', 0), ('snap', '3s', b'\xaa\xaa\x03'), ('hsa', '3s', b'\x00\x00\x0c'), ('_id_bpdu', 'H', 0), ('indx', 'H', 0), ('res', 'H', 0) ) __bit_fields__ = { '_type_pri': ( ('type', 4), # encapsulation type, 4 bits; 0 means Ethernet ('pri', 4), # user defined bits, 2 lo bits are used; means priority ), '_id_bpdu': ( ('id', 15), # vlan id, 15 bits ('bpdu', 1), # bridge protocol data unit indicator ) } def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = b'' # Unit tests def test_eth(): from . import ip6 from . import tcp s = (b'\x00\xb0\xd0\xe1\x80\x72\x00\x11\x24\x8c\x11\xde\x86\xdd\x60\x00\x00\x00' b'\x00\x28\x06\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x11\x24\xff\xfe\x8c' b'\x11\xde\xfe\x80\x00\x00\x00\x00\x00\x00\x02\xb0\xd0\xff\xfe\xe1\x80\x72' b'\xcd\xd3\x00\x16\xff\x50\xd7\x13\x00\x00\x00\x00\xa0\x02\xff\xff\x67\xd3' b'\x00\x00\x02\x04\x05\xa0\x01\x03\x03\x00\x01\x01\x08\x0a\x7d\x18\x3a\x61' b'\x00\x00\x00\x00') eth = Ethernet(s) assert eth assert isinstance(eth.data, ip6.IP6) assert isinstance(eth.data.data, tcp.TCP) assert str(eth) == str(s) assert len(eth) == len(s) def test_eth_zero_ethtype(): s = (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\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\x08\x89\x12\x04') eth = Ethernet(s) assert eth assert eth.type == ETH_TYPE_UNKNOWN assert str(eth) == str(s) assert len(eth) == len(s) def test_eth_init_with_data(): # initialize with a data string, test that it gets unpacked from . import arp eth1 = Ethernet( dst=b'PQRSTU', src=b'ABCDEF', type=ETH_TYPE_ARP, data=b'\x00\x01\x08\x00\x06\x04\x00\x01123456abcd7890abwxyz') assert isinstance(eth1.data, arp.ARP) # now initialize with a class, test packing eth2 = Ethernet( dst=b'PQRSTU', src=b'ABCDEF', data=arp.ARP(sha=b'123456', spa=b'abcd', tha=b'7890ab', tpa=b'wxyz')) assert str(eth1) == str(eth2) assert len(eth1) == len(eth2) def test_mpls_label(): s = b'\x00\x01\x0b\xff' m = MPLSlabel(s) assert m.val == 16 assert m.exp == 5 assert m.s == 1 assert m.ttl == 255 assert str(m) == str(s) assert len(m) == len(s) def test_802dot1q_tag(): s = b'\xa0\x76\x01\x65' t = VLANtag8021Q(s) assert t.pri == 5 assert t.cfi == 0 assert t.id == 118 assert str(t) == str(s) t.cfi = 1 assert str(t) == str(b'\xb0\x76\x01\x65') assert len(t) == len(s) def test_isl_tag(): s = (b'\x01\x00\x0c\x00\x00\x03\x00\x02\xfd\x2c\xb8\x97\x00\x00\xaa\xaa\x03\x00\x00\x00\x04\x57' b'\x00\x00\x00\x00') t = VLANtagISL(s) assert t.pri == 3 assert t.id == 555 assert t.bpdu == 1 assert str(t) == str(s) assert len(t) == len(s) def test_eth_802dot1q(): from . import ip s = (b'\x00\x60\x08\x9f\xb1\xf3\x00\x40\x05\x40\xef\x24\x81\x00\x90\x20\x08' b'\x00\x45\x00\x00\x34\x3b\x64\x40\x00\x40\x06\xb7\x9b\x83\x97\x20\x81' b'\x83\x97\x20\x15\x04\x95\x17\x70\x51\xd4\xee\x9c\x51\xa5\x5b\x36\x80' b'\x10\x7c\x70\x12\xc7\x00\x00\x01\x01\x08\x0a\x00\x04\xf0\xd4\x01\x99' b'\xa3\xfd') eth = Ethernet(s) assert eth.cfi == 1 assert eth.vlanid == 32 assert eth.priority == 4 assert len(eth.vlan_tags) == 1 assert eth.vlan_tags[0].type == ETH_TYPE_IP assert isinstance(eth.data, ip.IP) # construction assert str(eth) == str(s), 'pack 1' assert str(eth) == str(s), 'pack 2' assert len(eth) == len(s) # construction with kwargs eth2 = Ethernet(src=eth.src, dst=eth.dst, vlan_tags=eth.vlan_tags, data=eth.data) assert str(eth2) == str(s) # construction w/o the tag del eth.vlan_tags, eth.cfi, eth.vlanid, eth.priority assert str(eth) == str(s[:12] + b'\x08\x00' + s[18:]) def test_eth_802dot1q_stacked(): # 2 VLAN tags from binascii import unhexlify import pytest from . import ip s = unhexlify( '001bd41ba4d80013c3dfae18810000768100000a0800' '45000064000f0000ff01929b0a760a010a760a020800' 'ceb70003000000000000001faf70abcdabcdabcdabcd' 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd' 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd' 'abcdabcdabcdabcdabcdabcd' ) eth = Ethernet(s) assert eth.type == ETH_TYPE_8021Q assert len(eth.vlan_tags) == 2 assert eth.vlan_tags[0].id == 118 assert eth.vlan_tags[1].id == 10 assert eth.vlan_tags[0].type == ETH_TYPE_8021Q assert eth.vlan_tags[1].type == ETH_TYPE_IP assert [t.as_tuple() for t in eth.vlan_tags] == [(118, 0, 0), (10, 0, 0)] assert isinstance(eth.data, ip.IP) # construction assert len(eth) == len(s) assert bytes(eth) == s # test packing failure with too many tags eth.vlan_tags += eth.vlan_tags[0] # just duplicate the first tag with pytest.raises(dpkt.PackError, match='maximum is 2 VLAN tags per Ethernet frame'): bytes(eth) # construction with kwargs eth2 = Ethernet(src=eth.src, dst=eth.dst, vlan_tags=eth.vlan_tags[:2], data=eth.data) # construction sets ip.type to 802.1ad instead of 802.1q so account for it assert str(eth2) == str(s[:12] + b'\x88\xa8' + s[14:]) # construction w/o the tags del eth.vlan_tags, eth.cfi, eth.vlanid, eth.priority assert str(eth) == str(s[:12] + b'\x08\x00' + s[22:]) def test_eth_vlan_arp(): from . import arp # 2 VLAN tags + ARP s = (b'\xff\xff\xff\xff\xff\xff\xca\x03\x0d\xb4\x00\x1c\x81\x00\x00\x64\x81\x00\x00\xc8\x08\x06' b'\x00\x01\x08\x00\x06\x04\x00\x01\xca\x03\x0d\xb4\x00\x1c\xc0\xa8\x02\xc8\x00\x00\x00\x00' b'\x00\x00\xc0\xa8\x02\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') eth = Ethernet(s) assert len(eth.vlan_tags) == 2 assert eth.vlan_tags[0].type == ETH_TYPE_8021Q assert eth.vlan_tags[1].type == ETH_TYPE_ARP assert isinstance(eth.data, arp.ARP) def test_eth_mpls_stacked(): # Eth - MPLS - MPLS - IP - ICMP from . import ip from . import icmp s = (b'\x00\x30\x96\xe6\xfc\x39\x00\x30\x96\x05\x28\x38\x88\x47\x00\x01\x20\xff\x00\x01\x01\xff' b'\x45\x00\x00\x64\x00\x50\x00\x00\xff\x01\xa7\x06\x0a\x1f\x00\x01\x0a\x22\x00\x01\x08\x00' b'\xbd\x11\x0f\x65\x12\xa0\x00\x00\x00\x00\x00\x53\x9e\xe0' + b'\xab\xcd' * 32) eth = Ethernet(s) assert len(eth.mpls_labels) == 2 assert eth.mpls_labels[0].val == 18 assert eth.mpls_labels[1].val == 16 assert eth.labels == [(18, 0, 255), (16, 0, 255)] assert isinstance(eth.data, ip.IP) assert isinstance(eth.data.data, icmp.ICMP) # exercise .pprint() for the coverage tests eth.pprint() # construction assert str(eth) == str(s), 'pack 1' assert str(eth) == str(s), 'pack 2' assert len(eth) == len(s) # construction with kwargs eth2 = Ethernet(src=eth.src, dst=eth.dst, mpls_labels=eth.mpls_labels, data=eth.data) assert str(eth2) == str(s) # construction w/o labels del eth.labels, eth.mpls_labels assert str(eth) == str(s[:12] + b'\x08\x00' + s[22:]) def test_eth_mpls_ipv6(): # Eth - MPLS - IP6 - TCP from . import ip6 from . import tcp s = ( b'\x00\x30\x96\xe6\xfc\x39\x00\x30\x96\x05\x28\x38\x88\x47\x00\x01' b'\x01\xff\x62\x8c\xed\x7b\x00\x28\x06\xfd\x22\x22\x22\x22\x03\x3f' b'\x53\xd3\x48\xfb\x8b\x5a\x41\x7f\xe6\x17\x11\x11\x11\x11\x40\x0b' b'\x08\x09\x00\x00\x00\x00\x00\x00\x20\x0e\xa1\x8e\x01\xbb\xd6\xde' b'\x73\x17\x00\x00\x00\x00\xa0\x02\xff\xff\x58\x7f\x00\x00\x02\x04' b'\x05\x8c\x04\x02\x08\x0a\x69\x23\xe8\x63\x00\x00\x00\x00\x01\x03' b'\x03\x0a\xaf\x9c\xb6\x93') eth = Ethernet(s) assert len(eth.mpls_labels) == 1 assert eth.mpls_labels[0].val == 16 assert eth.labels == [(16, 0, 255)] assert isinstance(eth.data, ip6.IP6) assert isinstance(eth.data.data, tcp.TCP) def test_isl_eth_llc_stp(): # ISL - 802.3 Ethernet(w/FCS) - LLC - STP from . import stp s = (b'\x01\x00\x0c\x00\x00\x03\x00\x02\xfd\x2c\xb8\x97\x00\x00\xaa\xaa\x03\x00\x00\x00\x02\x9b' b'\x00\x00\x00\x00\x01\x80\xc2\x00\x00\x00\x00\x02\xfd\x2c\xb8\x98\x00\x26\x42\x42\x03\x00' b'\x00\x00\x00\x00\x80\x00\x00\x02\xfd\x2c\xb8\x83\x00\x00\x00\x00\x80\x00\x00\x02\xfd\x2c' b'\xb8\x83\x80\x26\x00\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x41\xc6' b'\x75\xd6') eth = Ethernet(s) assert eth.vlan == 333 assert len(eth.vlan_tags) == 1 assert eth.vlan_tags[0].id == 333 assert eth.vlan_tags[0].pri == 3 # check that FCS and padding were decoded assert eth.fcs == 0x41c675d6 assert eth.padding == b'\x00' * 8 # stack assert isinstance(eth.data, llc.LLC) assert isinstance(eth.data.data, stp.STP) # construction assert str(eth) == str(s), 'pack 1' assert str(eth) == str(s), 'pack 2' assert len(eth) == len(s) # construction with kwargs eth2 = Ethernet(src=eth.src, dst=eth.dst, vlan_tags=eth.vlan_tags, data=eth.data) eth2.padding = b'\x00' * 8 # test FCS computation eth2.fcs = None assert str(eth2) == str(s) # TODO: test padding construction # eth2.padding = None # assert str(eth2) == str(s) # construction w/o the ISL tag del eth.vlan_tags, eth.vlan assert str(eth) == str(s[26:]) def test_eth_llc_snap_cdp(): # 802.3 Ethernet - LLC/SNAP - CDP from . import cdp s = (b'\x01\x00\x0c\xcc\xcc\xcc\xc4\x022k\x00\x00\x01T\xaa\xaa\x03\x00\x00\x0c \x00\x02\xb4,B' b'\x00\x01\x00\x06R2\x00\x05\x00\xffCisco IOS Software, 3700 Software (C3745-ADVENTERPRI' b'SEK9_SNA-M), Version 12.4(25d), RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.' b'cisco.com/techsupport\nCopyright (c) 1986-2010 by Cisco Systems, Inc.\nCompiled Wed 18' b'-Aug-10 08:18 by prod_rel_team\x00\x06\x00\x0eCisco 3745\x00\x02\x00\x11\x00\x00\x00\x01' b'\x01\x01\xcc\x00\x04\n\x00\x00\x02\x00\x03\x00\x13FastEthernet0/0\x00\x04\x00\x08\x00' b'\x00\x00)\x00\t\x00\x04\x00\x0b\x00\x05\x00') eth = Ethernet(s) # stack assert isinstance(eth.data, llc.LLC) assert isinstance(eth.data.data, cdp.CDP) assert len(eth.data.data.tlvs) == 8 # number of CDP TLVs; ensures they are decoded assert str(eth) == str(s), 'pack 1' assert str(eth) == str(s), 'pack 2' assert len(eth) == len(s) def test_eth_llc_ipx(): # 802.3 Ethernet - LLC - IPX from . import ipx s = (b'\xff\xff\xff\xff\xff\xff\x00\xb0\xd0\x22\xf7\xf3\x00\x54\xe0\xe0\x03\xff\xff\x00\x50\x00' b'\x14\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x04\x55\x00\x00\x00\x00\x00\xb0\xd0\x22\xf7' b'\xf3\x04\x55\x00\x01\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\x01\x01\x02\x5f\x5f\x4d\x53\x42' b'\x52\x4f\x57\x53\x45\x5f\x5f\x02\x01\x00') eth = Ethernet(s) # stack assert isinstance(eth.data, llc.LLC) assert isinstance(eth.data.data, ipx.IPX) assert eth.data.data.pt == 0x14 assert str(eth) == str(s), 'pack 1' assert str(eth) == str(s), 'pack 2' assert len(eth) == len(s) def test_eth_pppoe(): # Eth - PPPoE - IPv6 - UDP - DHCP6 from . import ip6 from . import ppp from . import pppoe from . import udp s = (b'\xca\x01\x0e\x88\x00\x06\xcc\x05\x0e\x88\x00\x00\x88\x64\x11\x00\x00\x11\x00\x64\x57\x6e' b'\x00\x00\x00\x00\x3a\x11\xff\xfe\x80\x00\x00\x00\x00\x00\x00\xce\x05\x0e\xff\xfe\x88\x00' b'\x00\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x02\x22\x02\x23\x00' b'\x3a\x1a\x67\x01\xfc\x24\xab\x00\x08\x00\x02\x05\xe9\x00\x01\x00\x0a\x00\x03\x00\x01\xcc' b'\x05\x0e\x88\x00\x00\x00\x06\x00\x06\x00\x19\x00\x17\x00\x18\x00\x19\x00\x0c\x00\x09\x00' b'\x01\x00\x00\x00\x00\x00\x00\x00\x00') eth = Ethernet(s) # stack assert isinstance(eth.data, pppoe.PPPoE) assert isinstance(eth.data.data, ppp.PPP) assert isinstance(eth.data.data.data, ip6.IP6) assert isinstance(eth.data.data.data.data, udp.UDP) # construction assert str(eth) == str(s) assert len(eth) == len(s) def test_eth_2mpls_ecw_eth_llc_stp(): # Eth - MPLS - MPLS - PW ECW - 802.3 Eth(no FCS) - LLC - STP from . import stp s = (b'\xcc\x01\x0d\x5c\x00\x10\xcc\x00\x0d\x5c\x00\x10\x88\x47\x00\x01\x20\xfe\x00\x01\x01\xff' b'\x00\x00\x00\x00\x01\x80\xc2\x00\x00\x00\xcc\x04\x0d\x5c\xf0\x00\x00\x26\x42\x42\x03\x00' b'\x00\x00\x00\x00\x80\x00\xcc\x04\x0d\x5c\x00\x00\x00\x00\x00\x00\x80\x00\xcc\x04\x0d\x5c' b'\x00\x00\x80\x01\x00\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00') eth = Ethernet(s) assert len(eth.mpls_labels) == 2 assert eth.mpls_labels[0].val == 18 assert eth.mpls_labels[1].val == 16 # stack eth2 = eth.data assert isinstance(eth2, Ethernet) assert eth2.len == 38 # 802.3 Ethernet # no FCS, no trailer, just 8 bytes of padding (60=38+14+8) assert not hasattr(eth2, 'fcs') assert eth2.padding == b'\x00' * 8 assert isinstance(eth2.data, llc.LLC) assert isinstance(eth2.data.data, stp.STP) assert eth2.data.data.port_id == 0x8001 # construction # XXX - FIXME: make packing account for the ECW # assert str(eth) == str(s) # QinQ: Eth - 802.1ad - 802.1Q - IP def test_eth_802dot1ad_802dot1q_ip(): from . import ip s = (b'\x00\x10\x94\x00\x00\x0c\x00\x10\x94\x00\x00\x14\x88\xa8\x00\x1e\x81\x00\x00\x64\x08\x00' b'\x45\x00\x05\xc2\x54\xb0\x00\x00\xff\xfd\xdd\xbf\xc0\x55\x01\x16\xc0\x55\x01\x0e' + 1434 * b'\x00' + b'\x4f\xdc\xcd\x64\x20\x8d\xb6\x4e\xa8\x45\xf8\x80\xdd\x0c\xf9\x72\xc4' b'\xd0\xcf\xcb\x46\x6d\x62\x7a') eth = Ethernet(s) assert eth.type == ETH_TYPE_8021AD assert eth.vlan_tags[0].id == 30 assert eth.vlan_tags[1].id == 100 assert isinstance(eth.data, ip.IP) e1 = Ethernet(s[:-1458]) # strip IP data # construction e2 = Ethernet( dst=b'\x00\x10\x94\x00\x00\x0c', src=b'\x00\x10\x94\x00\x00\x14', type=ETH_TYPE_8021AD, vlan_tags=[ VLANtag8021Q(pri=0, id=30, cfi=0), VLANtag8021Q(pri=0, id=100, cfi=0) ], data=ip.IP( len=1474, id=21680, ttl=255, p=253, sum=56767, src=b'\xc0U\x01\x16', dst=b'\xc0U\x01\x0e', opts=b'' ) ) assert str(e1) == str(e2) def test_eth_pack(): eth = Ethernet(data=b'12345') assert str(eth) def test_eth_802dot1q_with_unfamiliar_data(): profinet_data = ( b'\xfe\xff\x05\x01\x05\x01\x00\x02\x00\x00\x00\x6c\x02' b'\x05\x00\x12\x00\x00\x02\x01\x02\x02\x02\x03\x02\x04\x02\x05\x02' b'\x06\x01\x01\x01\x02\x02\x01\x00\x08\x00\x00\x53\x37\x2d\x33\x30' b'\x30\x02\x02\x00\x22\x00\x00\x70\x6c\x63\x78\x62\x33\x30\x30\x78' b'\x6b\x63\x70\x75\x78\x61\x33\x31\x37\x2d\x32\x78\x61\x70\x6e\x78' b'\x72\x64\x70\x32\x32\x63\x66\x02\x03\x00\x06\x00\x00\x00\x2a\x01' b'\x01\x02\x04\x00\x04\x00\x00\x02\x00\x01\x02\x00\x0e\x00\x01\xc0' b'\xa8\x3c\x87\xff\xff\xff\x00\xc0\xa8\x3c\x87') s = (b'\x00\x0c\x29\x65\x1c\x29\x00\x0e\x8c\x8a\xa2\x5e\x81\x00\x00\x00' b'\x88\x92' + profinet_data) eth = Ethernet(s) assert eth.type == ETH_TYPE_8021Q assert len(eth.vlan_tags) == 1 assert eth.vlan_tags[0].type == ETH_TYPE_PROFINET assert isinstance(eth.data, bytes) assert eth.data == profinet_data def test_eth_802dot1q_with_arp_data(): # https://github.com/kbandla/dpkt/issues/460 from .arp import ARP e = Ethernet(src=b'foobar', dst=b'\xff' * 6) v = VLANtag8021Q(pri=0, cfi=0, id=1) e.vlan_tags = [v] a = ARP(sha=b'foobar', spa=b'\x0a\x0a\x0a\x0a', tha=b'', tpa=b'\x0a\x0a\x0a\x05') e.data = a assert bytes(e) == ( b'\xff\xff\xff\xff\xff\xfffoobar\x81\x00\x00\x01\x08\x06' # 0x0806 = next layer is ARP b'\x00\x01\x08\x00\x06\x04\x00\x01foobar\x0a\x0a\x0a\x0a' b'\x00\x00\x00\x00\x00\x00\x0a\x0a\x0a\x05') # 802.3 Ethernet - LLC/STP - Padding - FCS - Metamako trailer def test_eth_8023_llc_trailer(): # https://github.com/kbandla/dpkt/issues/438 d = (b'\x01\x80\xc2\x00\x00\x00\x78\x0c\xf0\xb4\xd8\x91\x00\x27\x42\x42\x03\x00\x00\x02\x02\x3c' b'\x00\x01\x2c\x33\x11\xf2\x39\xc1\x00\x00\x00\x02\x80\x01\x78\x0c\xf0\xb4\xd8\xbc\x80\xaa' b'\x01\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4d\xb9\x81\x20\x5c\x1e' b'\x5f\xba\x3a\xa5\x47\xfa\x01\x8e\x52\x03') eth = Ethernet(d) assert eth.len == 39 assert eth.padding == b'\x00\x00\x00\x00\x00\x00\x00' assert eth.fcs == 0x4db98120 assert eth.trailer == b'\x5c\x1e\x5f\xba\x3a\xa5\x47\xfa\x01\x8e\x52\x03' assert isinstance(eth.data, llc.LLC) # packing assert bytes(eth) == d # FCS computation eth.fcs = None assert bytes(eth) == d def test_eth_novell(): from binascii import unhexlify import dpkt buf = unhexlify( '010203040506' # dst '0708090a0b0c' # src '0000' # type (ignored) 'ffff' # indicates Novell # IPX packet '0000' # sum '0001' # len '02' # tc '03' # pt '0102030405060708090a0b0c' # dst '0102030405060708090a0b0c' # src ) eth = Ethernet(buf) assert isinstance(eth.data, dpkt.ipx.IPX) assert eth.data.tc == 2 assert eth.data.data == b'' dpkt-1.9.8/dpkt/gre.py000066400000000000000000000151531427735150300146000ustar00rootroot00000000000000# $Id: gre.py 75 2010-08-03 14:42:19Z jon.oberheide $ # -*- coding: utf-8 -*- """Generic Routing Encapsulation.""" from __future__ import absolute_import import struct import codecs from . import dpkt from . import ethernet from .compat import compat_izip GRE_CP = 0x8000 # Checksum Present GRE_RP = 0x4000 # Routing Present GRE_KP = 0x2000 # Key Present GRE_SP = 0x1000 # Sequence Present GRE_SS = 0x0800 # Strict Source Route GRE_AP = 0x0080 # Acknowledgment Present GRE_opt_fields = ( (GRE_CP | GRE_RP, 'sum', 'H'), (GRE_CP | GRE_RP, 'off', 'H'), (GRE_KP, 'key', 'I'), (GRE_SP, 'seq', 'I'), (GRE_AP, 'ack', 'I') ) class GRE(dpkt.Packet): """Generic Routing Encapsulation. Generic Routing Encapsulation, or GRE, is a protocol for encapsulating data packets that use one routing protocol inside the packets of another protocol. "Encapsulating" means wrapping one data packet within another data packet, like putting a box inside another box. GRE is one way to set up a direct point-to-point connection across a network, for the purpose of simplifying connections between separate networks. It works with a variety of network layer protocols. Attributes: __hdr__: Header fields of GRE. flags: (int): Flag bits. (2 bytes) p: (int): Protocol Type (2 bytes) """ __hdr__ = ( ('flags', 'H', 0), ('p', 'H', 0x0800), # ETH_TYPE_IP ) sre = () @property def v(self): return self.flags & 0x7 @v.setter def v(self, v): self.flags = (self.flags & ~0x7) | (v & 0x7) @property def recur(self): """Recursion control bits. (3 bits)""" return (self.flags >> 5) & 0x7 @recur.setter def recur(self, v): self.flags = (self.flags & ~0xe0) | ((v & 0x7) << 5) class SRE(dpkt.Packet): __hdr__ = [ ('family', 'H', 0), ('off', 'B', 0), ('len', 'B', 0) ] def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.len] def opt_fields_fmts(self): if self.v == 0: fields, fmts = [], [] opt_fields = GRE_opt_fields else: fields, fmts = ['len', 'callid'], ['H', 'H'] opt_fields = GRE_opt_fields[-2:] for flags, field, fmt in opt_fields: if self.flags & flags: fields.append(field) fmts.append(fmt) return fields, fmts def unpack(self, buf): dpkt.Packet.unpack(self, buf) fields, fmts = self.opt_fields_fmts() if fields: fmt = ''.join(fmts) fmtlen = struct.calcsize(fmt) vals = struct.unpack("!" + fmt, self.data[:fmtlen]) self.data = self.data[fmtlen:] self.__dict__.update(dict(compat_izip(fields, vals))) if self.flags & GRE_RP: l_ = [] while True: sre = self.SRE(self.data) self.data = self.data[len(sre):] l_.append(sre) if not sre.len: break self.sre = l_ try: self.data = ethernet.Ethernet._typesw[self.p](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): # data already set pass def __len__(self): opt_fmtlen = struct.calcsize(''.join(self.opt_fields_fmts()[1])) return self.__hdr_len__ + opt_fmtlen + sum(map(len, self.sre)) + len(self.data) def __bytes__(self): fields, fmts = self.opt_fields_fmts() if fields: vals = [] for f in fields: vals.append(getattr(self, f)) opt_s = struct.pack('!' + ''.join(fmts), *vals) else: opt_s = b'' return self.pack_hdr() + opt_s + b''.join(map(bytes, self.sre)) + bytes(self.data) def test_gre_v1(): # Runs all the test associated with this class/file s = codecs.decode("3081880a0067178000068fb100083a76", 'hex') + b"A" * 103 g = GRE(s) assert g.v == 1 assert g.p == 0x880a assert g.seq == 430001 assert g.ack == 539254 assert g.callid == 6016 assert g.len == 103 assert g.data == b"A" * 103 assert len(g) == len(s) s = codecs.decode("3001880a00b2001100083ab8", 'hex') + b"A" * 178 g = GRE(s) assert g.v == 1 assert g.p == 0x880a assert g.seq == 539320 assert g.callid == 17 assert g.len == 178 assert g.data == b"A" * 178 assert len(g) == len(s) def test_gre_len(): from binascii import unhexlify gre = GRE() assert len(gre) == 4 buf = unhexlify("3081880a0067178000068fb100083a76") + b"\x41" * 103 gre = GRE(buf) assert bytes(gre) == buf assert len(gre) == len(buf) def test_gre_accessors(): gre = GRE() for attr in ['v', 'recur']: print(attr) assert hasattr(gre, attr) assert getattr(gre, attr) == 0 setattr(gre, attr, 1) assert getattr(gre, attr) == 1 def test_sre_creation(): from binascii import unhexlify buf = unhexlify( '0000' # family '00' # off '02' # len 'ffff' ) sre = GRE.SRE(buf) assert sre.data == b'\xff\xff' assert len(sre) == 6 assert bytes(sre) == buf def test_gre_nested_sre(): from binascii import unhexlify buf = unhexlify( '4000' # flags (GRE_RP) '0800' # p (ETH_TYPE_IP) '0001' # sum '0002' # off # SRE entry '0003' # family '04' # off '02' # len 'ffff' # SRE entry (no len => last element) '0006' # family '00' # off '00' # len ) gre = GRE(buf) assert hasattr(gre, 'sre') assert isinstance(gre.sre, list) assert len(gre.sre) == 2 assert len(gre) == len(buf) assert bytes(gre) == buf assert gre.data == b'' def test_gre_next_layer(): from binascii import unhexlify from . import ipx buf = unhexlify( '0000' # flags (NONE) '8137' # p (ETH_TYPE_IPX) # IPX packet '0000' # sum '0001' # len '02' # tc '03' # pt '0102030405060708090a0b0c' # dst 'c0b0a0908070605040302010' # src ) gre = GRE(buf) assert hasattr(gre, 'ipx') assert isinstance(gre.data, ipx.IPX) assert gre.data.tc == 2 assert gre.data.src == unhexlify('c0b0a0908070605040302010') assert gre.data.dst == unhexlify('0102030405060708090a0b0c') assert len(gre) == len(buf) assert bytes(gre) == buf dpkt-1.9.8/dpkt/gzip.py000066400000000000000000000211061427735150300147670ustar00rootroot00000000000000# $Id: gzip.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """GNU zip.""" from __future__ import print_function from __future__ import absolute_import import struct import zlib from . import dpkt # RFC 1952 GZIP_MAGIC = b'\x1f\x8b' # Compression methods GZIP_MSTORED = 0 GZIP_MCOMPRESS = 1 GZIP_MPACKED = 2 GZIP_MLZHED = 3 GZIP_MDEFLATE = 8 # Flags GZIP_FTEXT = 0x01 GZIP_FHCRC = 0x02 GZIP_FEXTRA = 0x04 GZIP_FNAME = 0x08 GZIP_FCOMMENT = 0x10 GZIP_FENCRYPT = 0x20 GZIP_FRESERVED = 0xC0 # OS GZIP_OS_MSDOS = 0 GZIP_OS_AMIGA = 1 GZIP_OS_VMS = 2 GZIP_OS_UNIX = 3 GZIP_OS_VMCMS = 4 GZIP_OS_ATARI = 5 GZIP_OS_OS2 = 6 GZIP_OS_MACOS = 7 GZIP_OS_ZSYSTEM = 8 GZIP_OS_CPM = 9 GZIP_OS_TOPS20 = 10 GZIP_OS_WIN32 = 11 GZIP_OS_QDOS = 12 GZIP_OS_RISCOS = 13 GZIP_OS_UNKNOWN = 255 GZIP_FENCRYPT_LEN = 12 class GzipExtra(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('id', '2s', b''), ('len', 'H', 0) ) class Gzip(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('magic', '2s', GZIP_MAGIC), ('method', 'B', GZIP_MDEFLATE), ('flags', 'B', 0), ('mtime', 'I', 0), ('xflags', 'B', 0), ('os', 'B', GZIP_OS_UNIX), ) def __init__(self, *args, **kwargs): self.extra = None self.filename = None self.comment = None super(Gzip, self).__init__(*args, **kwargs) def unpack(self, buf): super(Gzip, self).unpack(buf) if self.flags & GZIP_FEXTRA: if len(self.data) < 2: raise dpkt.NeedData('Gzip extra') n = struct.unpack(' len(self.tpkt.data): raise dpkt.UnpackError('invalid TPKT length') buf = self.tpkt.data # Q.931 payload dpkt.Packet.unpack(self, buf) buf = buf[self.__hdr_len__:] self.ref_val = buf[:self.ref_len] buf = buf[self.ref_len:] self.type = struct.unpack('B', buf[:1])[0] buf = buf[1:] # Information Elements l_ = [] while buf: ie = self.IE(buf) l_.append(ie) buf = buf[len(ie):] self.data = l_ def __len__(self): return self.tpkt.__hdr_len__ + self.__hdr_len__ + sum(map(len, self.data)) def __bytes__(self): return self.tpkt.pack_hdr() + self.pack_hdr() + self.ref_val + \ struct.pack('B', self.type) + b''.join(map(bytes, self.data)) class IE(dpkt.Packet): __hdr__ = ( ('type', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) buf = buf[self.__hdr_len__:] # single-byte IE if self.type & 0x80: self.len = 0 self.data = b'' # multi-byte IE else: # special PER-encoded UUIE if self.type == USER_TO_USER: self.len = struct.unpack('>H', buf[:2])[0] buf = buf[2:] # normal TLV-like IE else: self.len = struct.unpack('B', buf[:1])[0] buf = buf[1:] self.data = buf[:self.len] def __len__(self): if self.type & 0x80: n = 0 else: if self.type == USER_TO_USER: n = 2 else: n = 1 return self.__hdr_len__ + self.len + n def __bytes__(self): if self.type & 0x80: length_str = b'' else: if self.type == USER_TO_USER: length_str = struct.pack('>H', self.len) else: length_str = struct.pack('B', self.len) return struct.pack('B', self.type) + length_str + self.data __s = ( b'\x03\x00\x04\x11\x08\x02\x54\x2b\x05\x04\x03\x88\x93\xa5\x28\x0e\x4a\x6f\x6e\x20\x4f\x62\x65\x72\x68\x65\x69\x64\x65' b'\x00\x7e\x03\xf0\x05\x20\xb8\x06\x00\x08\x91\x4a\x00\x04\x01\x40\x0c\x00\x4a\x00\x6f\x00\x6e\x00\x20\x00\x4f\x00\x62' b'\x00\x65\x00\x72\x00\x68\x00\x65\x00\x69\x00\x64\x00\x65\x22\xc0\x09\x00\x00\x3d\x06\x65\x6b\x69\x67\x61\x00\x00\x14' b'\x32\x2e\x30\x2e\x32\x20\x28\x4f\x50\x41\x4c\x20\x76\x32\x2e\x32\x2e\x32\x29\x00\x00\x00\x01\x40\x15\x00\x74\x00\x63' b'\x00\x70\x00\x24\x00\x68\x00\x33\x00\x32\x00\x33\x00\x2e\x00\x76\x00\x6f\x00\x78\x00\x67\x00\x72\x00\x61\x00\x74\x00' b'\x69\x00\x61\x00\x2e\x00\x6f\x00\x72\x00\x67\x00\x42\x87\x23\x2c\x06\xb8\x00\x6a\x8b\x1d\x0c\xb7\x06\xdb\x11\x9e\xca' b'\x00\x10\xa4\x89\x6d\x6a\x00\xc5\x1d\x80\x04\x07\x00\x0a\x00\x01\x7a\x75\x30\x11\x00\x5e\x88\x1d\x0c\xb7\x06\xdb\x11' b'\x9e\xca\x00\x10\xa4\x89\x6d\x6a\x82\x2b\x0e\x30\x40\x00\x00\x06\x04\x01\x00\x4c\x10\x09\x00\x00\x3d\x0f\x53\x70\x65' b'\x65\x78\x20\x62\x73\x34\x20\x57\x69\x64\x65\x36\x80\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41' b'\x13\x8b\x26\x00\x00\x64\x0c\x10\x09\x00\x00\x3d\x0f\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x57\x69\x64\x65\x36\x80' b'\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b\x00\x2a\x40\x00\x00\x06\x04\x01\x00\x4c\x10\x09\x00\x00\x3d\x09\x69\x4c' b'\x42\x43\x2d\x31\x33\x6b\x33\x80\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41\x13\x8b\x20\x00\x00' b'\x65\x0c\x10\x09\x00\x00\x3d\x09\x69\x4c\x42\x43\x2d\x31\x33\x6b\x33\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b' b'\x00\x20\x40\x00\x00\x06\x04\x01\x00\x4e\x0c\x03\x00\x83\x00\x80\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98' b'\xa0\x26\x41\x13\x8b\x16\x00\x00\x66\x0e\x0c\x03\x00\x83\x00\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b\x00\x4b' b'\x40\x00\x00\x06\x04\x01\x00\x4c\x10\xb5\x00\x53\x4c\x2a\x02\x00\x00\x00\x00\x00\x40\x01\x00\x00\x40\x01\x02\x00\x08' b'\x00\x00\x00\x00\x00\x31\x00\x01\x00\x40\x1f\x00\x00\x59\x06\x00\x00\x41\x00\x00\x00\x02\x00\x40\x01\x00\x00\x80\x11' b'\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41\x13\x8b\x41\x00\x00\x67\x0c\x10\xb5\x00\x53\x4c\x2a\x02' b'\x00\x00\x00\x00\x00\x40\x01\x00\x00\x40\x01\x02\x00\x08\x00\x00\x00\x00\x00\x31\x00\x01\x00\x40\x1f\x00\x00\x59\x06' b'\x00\x00\x41\x00\x00\x00\x02\x00\x40\x01\x00\x00\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b\x00\x32\x40\x00\x00' b'\x06\x04\x01\x00\x4c\x10\x09\x00\x00\x3d\x11\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x4e\x61\x72\x72\x6f\x77\x33\x80' b'\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41\x13\x8b\x28\x00\x00\x68\x0c\x10\x09\x00\x00\x3d\x11' b'\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x4e\x61\x72\x72\x6f\x77\x33\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b' b'\x00\x1d\x40\x00\x00\x06\x04\x01\x00\x4c\x60\x1d\x80\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41' b'\x13\x8b\x13\x00\x00\x69\x0c\x60\x1d\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b\x00\x1d\x40\x00\x00\x06\x04\x01' b'\x00\x4c\x20\x1d\x80\x11\x1c\x00\x01\x00\x98\xa0\x26\x41\x13\x8a\x00\x98\xa0\x26\x41\x13\x8b\x13\x00\x00\x6a\x0c\x20' b'\x1d\x80\x0b\x0d\x00\x01\x00\x98\xa0\x26\x41\x13\x8b\x00\x01\x00\x01\x00\x01\x00\x01\x00\x81\x03\x02\x80\xf8\x02\x70' b'\x01\x06\x00\x08\x81\x75\x00\x0b\x80\x13\x80\x01\xf4\x00\x01\x00\x00\x01\x00\x00\x01\x00\x00\x0c\xc0\x01\x00\x01\x80' b'\x0b\x80\x00\x00\x20\x20\x09\x00\x00\x3d\x0f\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x57\x69\x64\x65\x36\x80\x00\x01' b'\x20\x20\x09\x00\x00\x3d\x09\x69\x4c\x42\x43\x2d\x31\x33\x6b\x33\x80\x00\x02\x24\x18\x03\x00\xe6\x00\x80\x00\x03\x20' b'\x20\xb5\x00\x53\x4c\x2a\x02\x00\x00\x00\x00\x00\x40\x01\x00\x00\x40\x01\x02\x00\x08\x00\x00\x00\x00\x00\x31\x00\x01' b'\x00\x40\x1f\x00\x00\x59\x06\x00\x00\x41\x00\x00\x00\x02\x00\x40\x01\x00\x00\x80\x00\x04\x20\x20\x09\x00\x00\x3d\x11' b'\x53\x70\x65\x65\x78\x20\x62\x73\x34\x20\x4e\x61\x72\x72\x6f\x77\x33\x80\x00\x05\x20\xc0\xef\x80\x00\x06\x20\x40\xef' b'\x80\x00\x07\x08\xe0\x03\x51\x00\x80\x01\x00\x80\x00\x08\x08\xd0\x03\x51\x00\x80\x01\x00\x80\x00\x09\x83\x01\x50\x80' b'\x00\x0a\x83\x01\x10\x80\x00\x0b\x83\x01\x40\x00\x80\x01\x03\x06\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00' b'\x06\x01\x00\x07\x00\x08\x00\x00\x09\x01\x00\x0a\x00\x0b\x07\x01\x00\x32\x80\xa6\xff\x4c\x02\x80\x01\x80' ) def test_pack(): h = H225(__s) assert (__s == bytes(h)) assert len(h) == 1038 # len(__s) == 1041 def test_unpack(): h = H225(__s) assert (h.tpkt.v == 3) assert (h.tpkt.rsvd == 0) assert (h.tpkt.len == 1041) assert (h.proto == 8) assert (h.type == SETUP) assert (len(h.data) == 3) ie = h.data[0] assert (ie.type == BEARER_CAPABILITY) assert (ie.len == 3) ie = h.data[1] assert (ie.type == DISPLAY) assert (ie.len == 14) ie = h.data[2] assert (ie.type == USER_TO_USER) assert (ie.len == 1008) def test_tpkt_unpack_errors(): import pytest from binascii import unhexlify # invalid version buf_tpkt_version0 = unhexlify( '00' # v '00' # rsvd '0000' # len ) with pytest.raises(dpkt.UnpackError, match="invalid TPKT version"): H225(buf_tpkt_version0) # invalid reserved value buf_tpkt_rsvd = unhexlify( '03' # v 'ff' # rsvd '0000' # len ) with pytest.raises(dpkt.UnpackError, match="invalid TPKT reserved value"): H225(buf_tpkt_rsvd) # invalid len buf_tpkt_len = unhexlify( '03' # v '00' # rsvd 'ffff' # len ) with pytest.raises(dpkt.UnpackError, match="invalid TPKT length"): H225(buf_tpkt_len) def test_unpack_ie(): ie = H225.IE(b'\x80') assert ie.len == 0 assert ie.data == b'' assert len(ie) == 1 assert bytes(ie) == b'\x80' dpkt-1.9.8/dpkt/hsrp.py000066400000000000000000000042571427735150300150020ustar00rootroot00000000000000# $Id: hsrp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Cisco Hot Standby Router Protocol.""" from __future__ import absolute_import from . import dpkt # Opcodes HELLO = 0 COUP = 1 RESIGN = 2 # States INITIAL = 0x00 LEARN = 0x01 LISTEN = 0x02 SPEAK = 0x04 STANDBY = 0x08 ACTIVE = 0x10 class HSRP(dpkt.Packet): """Cisco Hot Standby Router Protocol. It is a Cisco proprietary redundancy protocol for establishing a fault-tolerant default gateway. Version 1 of the protocol was described in RFC 2281 in 1998. Version 2 of the protocol includes improvements and supports IPv6 but there is no corresponding RFC published for this version. Attributes: __hdr__: Header fields of HSRP. version: (int): Version. HSRP version number. (1 byte) opcode: (int): Operation code. (Hello - 0, Coup - 1, Resign - 2) (1 byte) state: (int): State. This field describes the current state of the router sending the message. (1 byte) hello: (int): Hellotime. This field is only meaningful in Hello messages. It contains the approximate period between the Hello messages that the router sends. The time is given in seconds.(1 byte) hold: (int): Holdtime. This field is only meaningful in Hello messages. It contains the amount of time that the current Hello message should be considered valid. The time is given in seconds. (1 byte) priority: (int): Priority. This field is used to elect the active and standby routers. (1 byte) group: (int): Group. This field identifies the standby group. (1 byte) rsvd: (int): Reserved. (1 byte) auth: (bytes): Authentication Data. This field contains a clear text 8 character reused password. (8 bytes) vip: (bytes): Virtual IP Address. The virtual IP address used by this group. (4 bytes) """ __hdr__ = ( ('version', 'B', 0), ('opcode', 'B', 0), ('state', 'B', 0), ('hello', 'B', 0), ('hold', 'B', 0), ('priority', 'B', 0), ('group', 'B', 0), ('rsvd', 'B', 0), ('auth', '8s', b'cisco'), ('vip', '4s', b'') ) dpkt-1.9.8/dpkt/http.py000066400000000000000000000531421427735150300150020ustar00rootroot00000000000000# $Id: http.py 86 2013-03-05 19:25:19Z andrewflnr@gmail.com $ # -*- coding: utf-8 -*- """Hypertext Transfer Protocol.""" from __future__ import print_function from __future__ import absolute_import from collections import OrderedDict from . import dpkt from .compat import BytesIO, iteritems def parse_headers(f): """Return dict of HTTP headers parsed from a file object.""" d = OrderedDict() while 1: # The following logic covers two kinds of loop exit criteria. # 1) If the header is valid, when we reached the end of the header, # f.readline() would return with '\r\n', then after strip(), # we can break the loop. # 2) If this is a weird header, which do not ends with '\r\n', # f.readline() would return with '', then after strip(), # we still get an empty string, also break the loop. line = f.readline().strip().decode("ascii", "ignore") if not line: break l_ = line.split(':', 1) if len(l_[0].split()) != 1: raise dpkt.UnpackError('invalid header: %r' % line) k = l_[0].lower() v = len(l_) != 1 and l_[1].lstrip() or '' if k in d: if not type(d[k]) is list: d[k] = [d[k]] d[k].append(v) else: d[k] = v return d def parse_body(f, headers): """Return HTTP body parsed from a file object, given HTTP header dict.""" if headers.get('transfer-encoding', '').lower() == 'chunked': l_ = [] found_end = False while 1: try: sz = f.readline().split(None, 1)[0] except IndexError: raise dpkt.UnpackError('missing chunk size') try: n = int(sz, 16) except ValueError: raise dpkt.UnpackError('invalid chunk size') if n == 0: found_end = True buf = f.read(n) if f.readline().strip(): break if n and len(buf) == n: l_.append(buf) else: # only possible when len(buf) < n, which will happen if the # file object ends before reading a complete file chunk break if not found_end: raise dpkt.NeedData('premature end of chunked body') body = b''.join(l_) elif 'content-length' in headers: n = int(headers['content-length']) body = f.read(n) if len(body) != n: raise dpkt.NeedData('short body (missing %d bytes)' % (n - len(body))) elif 'content-type' in headers: body = f.read() else: # XXX - need to handle HTTP/0.9 body = b'' return body class Message(dpkt.Packet): """Hypertext Transfer Protocol headers + body. HTTP messages are how data is exchanged between a server and a client. There are two types of messages: requests sent by the client to trigger an action on the server, and responses, the answer from the server. HTTP messages are composed of textual information encoded in ASCII, and span over multiple lines. Attributes: __hdr__: Header fields of HTTP. The start-line and HTTP headers of the HTTP message are collectively known as the head of the requests, whereas its payload is known as the body. """ __metaclass__ = type __hdr_defaults__ = {} headers = None body = None def __init__(self, *args, **kwargs): if args: self.unpack(args[0]) else: self.headers = OrderedDict() self.body = b'' self.data = b'' # NOTE: changing this to iteritems breaks py3 compatibility for k, v in self.__hdr_defaults__.items(): setattr(self, k, v) for k, v in iteritems(kwargs): setattr(self, k, v) def unpack(self, buf, is_body_allowed=True): f = BytesIO(buf) # Parse headers self.headers = parse_headers(f) # Parse body if is_body_allowed: self.body = parse_body(f, self.headers) else: self.body = b'' # Save the rest self.data = f.read() def pack_hdr(self): return ''.join(['%s: %s\r\n' % t for t in iteritems(self.headers)]) def __len__(self): return len(str(self)) def __str__(self): return '%s\r\n%s' % (self.pack_hdr(), self.body.decode("utf8", "ignore")) def __bytes__(self): return self.pack_hdr().encode("ascii", "ignore") + b'\r\n' + (self.body or b'') class Request(Message): """Hypertext Transfer Protocol Request. HTTP requests are messages sent by the client to initiate an action on the server. Their start-line contain three elements. An HTTP method, a verb (like GET, PUT or POST) or a noun (like HEAD or OPTIONS), The request target, usually a URL, or the absolute path of the protocol, port, and domain are usually characterized by the request context and The HTTP version, which defines the structure of the remaining message, acting as an indicator of the expected version to use for the response. Attributes: __hdr__: Header fields of HTTP request. Many headers can appear in requests. They can be divided in several groups: General headers, like Via, apply to the message as a whole. Request headers, like User-Agent or Accept, modify the request by specifying it further (like Accept- Language), by giving context (like Referer), or by conditionally restricting it (like If-None). Representation headers like Content-Type that describe the original format of the message data and any encoding applied (only present if the message has a body). """ __hdr_defaults__ = { 'method': 'GET', 'uri': '/', 'version': '1.0', } __methods = dict.fromkeys(( 'GET', 'PUT', 'ICY', 'COPY', 'HEAD', 'LOCK', 'MOVE', 'POLL', 'POST', 'BCOPY', 'BMOVE', 'MKCOL', 'TRACE', 'LABEL', 'MERGE', 'DELETE', 'SEARCH', 'UNLOCK', 'REPORT', 'UPDATE', 'NOTIFY', 'BDELETE', 'CONNECT', 'OPTIONS', 'CHECKIN', 'PROPFIND', 'CHECKOUT', 'CCM_POST', 'SUBSCRIBE', 'PROPPATCH', 'BPROPFIND', 'BPROPPATCH', 'UNCHECKOUT', 'MKACTIVITY', 'MKWORKSPACE', 'UNSUBSCRIBE', 'RPC_CONNECT', 'VERSION-CONTROL', 'BASELINE-CONTROL' )) __proto = 'HTTP' def unpack(self, buf): f = BytesIO(buf) line = f.readline().decode("ascii", "ignore") l_ = line.strip().split() if len(l_) < 2: raise dpkt.UnpackError('invalid request: %r' % line) if l_[0] not in self.__methods: raise dpkt.UnpackError('invalid http method: %r' % l_[0]) if len(l_) == 2: # HTTP/0.9 does not specify a version in the request line self.version = '0.9' else: if not l_[2].startswith(self.__proto): raise dpkt.UnpackError('invalid http version: %r' % l_[2]) self.version = l_[2][len(self.__proto) + 1:] self.method = l_[0] self.uri = l_[1] Message.unpack(self, f.read()) def __str__(self): return '%s %s %s/%s\r\n' % (self.method, self.uri, self.__proto, self.version) + Message.__str__(self) def __bytes__(self): str_out = '%s %s %s/%s\r\n' % (self.method, self.uri, self.__proto, self.version) return str_out.encode("ascii", "ignore") + Message.__bytes__(self) class Response(Message): """Hypertext Transfer Protocol Response. The start line of an HTTP response, called the status line, contains the following information. The protocol version, usually HTTP/1.1, a status code, indicating success or failure of the request. Common status codes are 200, 404, or 302, a status text. A brief, purely informational, textual description of the status code to help a human understand the HTTP message. A typical status line looks like: HTTP/1.1 404 Not Found. Attributes: __hdr__: Header fields of HTTP Response. Many headers can appear in responses. These can be divided into several groups: General headers, like Via, apply to the whole message. Response headers, like Vary and Accept-Ranges, give additional information about the server which doesn't fit in the status line. Representation headers like Content-Type that describe the original format of the message data and any encoding applied (only present if the message has a body). """ __hdr_defaults__ = { 'version': '1.0', 'status': '200', 'reason': 'OK' } __proto = 'HTTP' def unpack(self, buf): f = BytesIO(buf) line = f.readline() l_ = line.strip().decode("ascii", "ignore").split(None, 2) if len(l_) < 2 or not l_[0].startswith(self.__proto) or not l_[1].isdigit(): raise dpkt.UnpackError('invalid response: %r' % line) self.version = l_[0][len(self.__proto) + 1:] self.status = l_[1] self.reason = l_[2] if len(l_) > 2 else '' # RFC Sec 4.3. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3. # For response messages, whether or not a message-body is included with # a message is dependent on both the request method and the response # status code (section 6.1.1). All responses to the HEAD request method # MUST NOT include a message-body, even though the presence of entity- # header fields might lead one to believe they do. All 1xx # (informational), 204 (no content), and 304 (not modified) responses # MUST NOT include a message-body. All other responses do include a # message-body, although it MAY be of zero length. is_body_allowed = int(self.status) >= 200 and 204 != int(self.status) != 304 Message.unpack(self, f.read(), is_body_allowed) def __str__(self): return '%s/%s %s %s\r\n' % (self.__proto, self.version, self.status, self.reason) + Message.__str__(self) def __bytes__(self): str_out = '%s/%s %s %s\r\n' % (self.__proto, self.version, self.status, self.reason) return str_out.encode("ascii", "ignore") + Message.__bytes__(self) def test_parse_request(): s = (b"""POST /main/redirect/ab/1,295,,00.html HTTP/1.0\r\nReferer: http://www.email.com/login/snap/login.jhtml\r\n""" b"""Connection: Keep-Alive\r\nUser-Agent: Mozilla/4.75 [en] (X11; U; OpenBSD 2.8 i386; Nav)\r\n""" b"""Host: ltd.snap.com\r\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n""" b"""Accept-Encoding: gzip\r\nAccept-Language: en\r\nAccept-Charset: iso-8859-1,*,utf-8\r\n""" b"""Content-type: application/x-www-form-urlencoded\r\nContent-length: 61\r\n\r\n""" b"""sn=em&mn=dtest4&pw=this+is+atest&fr=true&login=Sign+in&od=www""") r = Request(s) assert r.method == 'POST' assert r.uri == '/main/redirect/ab/1,295,,00.html' assert r.body == b'sn=em&mn=dtest4&pw=this+is+atest&fr=true&login=Sign+in&od=www' assert r.headers['content-type'] == 'application/x-www-form-urlencoded' Request(s[:60]) def test_format_request(): r = Request() assert str(r) == 'GET / HTTP/1.0\r\n\r\n' r.method = 'POST' r.uri = '/foo/bar/baz.html' r.headers['content-type'] = 'text/plain' r.headers['content-length'] = '5' r.body = b'hello' s = str(r) assert s.startswith('POST /foo/bar/baz.html HTTP/1.0\r\n') assert s.endswith('\r\n\r\nhello') assert '\r\ncontent-length: 5\r\n' in s assert '\r\ncontent-type: text/plain\r\n' in s s = bytes(r) assert s.startswith(b'POST /foo/bar/baz.html HTTP/1.0\r\n') assert s.endswith(b'\r\n\r\nhello') assert b'\r\ncontent-length: 5\r\n' in s assert b'\r\ncontent-type: text/plain\r\n' in s r = Request(bytes(r)) assert bytes(r) == s def test_chunked_response(): from binascii import unhexlify header = ( b"HTTP/1.1 200 OK\r\n" b"Cache-control: no-cache\r\n" b"Pragma: no-cache\r\n" b"Content-Type: text/javascript; charset=utf-8\r\n" b"Content-Encoding: gzip\r\n" b"Transfer-Encoding: chunked\r\n" b"Set-Cookie: S=gmail=agg:gmail_yj=v2s:gmproxy=JkU; Domain=.google.com; Path=/\r\n" b"Server: GFE/1.3\r\n" b"Date: Mon, 12 Dec 2005 22:33:23 GMT\r\n" b"\r\n" ) body = unhexlify( '610d0a1f8b08000000000000000d0a3135320d0a6d914d4fc4201086effe0a82c99e58' '4a4be9b6eec1e81e369e34f1e061358652da12596880bafaef85ee1a2ff231990cef30' '3cc381a0c301e610c13ca765595435a1a4ace1db153aa49d0cfa354b00f62eaaeb86d5' '79cd485995348ebc2a688c8e214c3759e627eb82575acf3e381e6487853158d863e6bc' '175a898fac208465de0a215d961769b5027b7bc27a301e0f23379c77337699329dfcc2' '6338ea5b2f4550d6bcce84d0ceabf760271fac53d2c7d2fb94024edc040feeba195803' '547457d7b4d9920abc58a73bb09b2710243f46fdf3437a50748a55efb8c88b2d18edec' '3ce083850821f8225bb0d36a826893b8cfd89bbadad09214a4610d630d654dfd873d58' '3b68d96a3be0646217c202bdb046c2696e23fb3ab6c47815d69f8aafcf290b5ebce769' '11808b004401d82f8278f6d8f74a28ae2f11701f2bc470093afefddfa359faae347f00' 'c5a595a1e20100000d0a300d0a0d0a' ) buf = header + body r = Response(buf) assert r.version == '1.1' assert r.status == '200' assert r.reason == 'OK' def test_multicookie_response(): s = (b"""HTTP/1.x 200 OK\r\nSet-Cookie: first_cookie=cookie1; path=/; domain=.example.com\r\n""" b"""Set-Cookie: second_cookie=cookie2; path=/; domain=.example.com\r\nContent-Length: 0\r\n\r\n""") r = Response(s) assert type(r.headers['set-cookie']) is list assert len(r.headers['set-cookie']) == 2 def test_noreason_response(): s = b"""HTTP/1.1 200 \r\n\r\n""" r = Response(s) assert r.reason == '' assert bytes(r) == s def test_response_with_body(): r = Response() r.body = b'foo' assert str(r) == 'HTTP/1.0 200 OK\r\n\r\nfoo' assert bytes(r) == b'HTTP/1.0 200 OK\r\n\r\nfoo' repr(r) def test_body_forbidden_response(): s = b'HTTP/1.1 304 Not Modified\r\n'\ b'Content-Type: text/css\r\n'\ b'Last-Modified: Wed, 14 Jan 2009 16:42:11 GMT\r\n'\ b'ETag: "3a7-496e15e3"\r\n'\ b'Cache-Control: private, max-age=414295\r\n'\ b'Date: Wed, 22 Sep 2010 17:55:54 GMT\r\n'\ b'Connection: keep-alive\r\n'\ b'Vary: Accept-Encoding\r\n\r\n'\ b'HTTP/1.1 200 OK\r\n'\ b'Server: Sun-ONE-Web-Server/6.1\r\n'\ b'ntCoent-length: 257\r\n'\ b'Content-Type: application/x-javascript\r\n'\ b'Last-Modified: Wed, 06 Jan 2010 19:34:06 GMT\r\n'\ b'ETag: "101-4b44e5ae"\r\n'\ b'Accept-Ranges: bytes\r\n'\ b'Content-Encoding: gzip\r\n'\ b'Cache-Control: private, max-age=439726\r\n'\ b'Date: Wed, 22 Sep 2010 17:55:54 GMT\r\n'\ b'Connection: keep-alive\r\n'\ b'Vary: Accept-Encoding\r\n' result = [] while s: msg = Response(s) s = msg.data result.append(msg) # the second HTTP response should be an standalone message assert len(result) == 2 def test_request_version(): s = b"""GET / HTTP/1.0\r\n\r\n""" r = Request(s) assert r.method == 'GET' assert r.uri == '/' assert r.version == '1.0' s = b"""GET /\r\n\r\n""" r = Request(s) assert r.method == 'GET' assert r.uri == '/' assert r.version == '0.9' import pytest s = b"""GET / CHEESE/1.0\r\n\r\n""" with pytest.raises(dpkt.UnpackError, match="invalid http version: u?'CHEESE/1.0'"): Request(s) def test_valid_header(): # valid header. s = b'POST /main/redirect/ab/1,295,,00.html HTTP/1.0\r\n' \ b'Referer: http://www.email.com/login/snap/login.jhtml\r\n' \ b'Connection: Keep-Alive\r\n' \ b'User-Agent: Mozilla/4.75 [en] (X11; U; OpenBSD 2.8 i386; Nav)\r\n' \ b'Host: ltd.snap.com\r\n' \ b'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n' \ b'Accept-Encoding: gzip\r\n' \ b'Accept-Language: en\r\n' \ b'Accept-Charset: iso-8859-1,*,utf-8\r\n' \ b'Content-type: application/x-www-form-urlencoded\r\n' \ b'Content-length: 61\r\n\r\n' \ b'sn=em&mn=dtest4&pw=this+is+atest&fr=true&login=Sign+in&od=www' r = Request(s) assert r.method == 'POST' assert r.uri == '/main/redirect/ab/1,295,,00.html' assert r.body == b'sn=em&mn=dtest4&pw=this+is+atest&fr=true&login=Sign+in&od=www' assert r.headers['content-type'] == 'application/x-www-form-urlencoded' def test_weird_end_header(): s_weird_end = b'POST /main/redirect/ab/1,295,,00.html HTTP/1.0\r\n' \ b'Referer: http://www.email.com/login/snap/login.jhtml\r\n' \ b'Connection: Keep-Alive\r\n' \ b'User-Agent: Mozilla/4.75 [en] (X11; U; OpenBSD 2.8 i386; Nav)\r\n' \ b'Host: ltd.snap.com\r\n' \ b'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n' \ b'Accept-Encoding: gzip\r\n' \ b'Accept-Language: en\r\n' \ b'Accept-Charset: iso-8859-1,*,utf-8\r\n' \ b'Content-type: application/x-www-form-urlencoded\r\n' \ b'Cookie: TrackID=1PWdcr3MO_C611BGW' r = Request(s_weird_end) assert r.method == 'POST' assert r.uri == '/main/redirect/ab/1,295,,00.html' assert r.headers['content-type'] == 'application/x-www-form-urlencoded' def test_gzip_response(): import zlib # valid response, compressed using gzip s = b'HTTP/1.0 200 OK\r\n' \ b'Server: SimpleHTTP/0.6 Python/2.7.12\r\n' \ b'Date: Fri, 10 Mar 2017 20:43:08 GMT\r\n' \ b'Content-type: text/plain\r\n' \ b'Content-Encoding: gzip\r\n' \ b'Content-Length: 68\r\n' \ b'Last-Modified: Fri, 10 Mar 2017 20:40:43 GMT\r\n\r\n' \ b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\x0b\xc9\xc8,V\x00\xa2D' \ b'\x85\xb2\xd4\xa2J\x85\xe2\xdc\xc4\x9c\x1c\x85\xb4\xcc\x9cT\x85\x92' \ b'|\x85\x92\xd4\xe2\x12\x85\xf4\xaa\xcc\x02\x85\xa2\xd4\xe2\x82\xfc' \ b'\xbc\xe2\xd4b=.\x00\x01(m\xad2\x00\x00\x00' r = Response(s) assert r.version == '1.0' assert r.status == '200' assert r.reason == 'OK' # Make a zlib compressor with the appropriate gzip options decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) body = decompressor.decompress(r.body) assert body.startswith(b'This is a very small file') def test_message(): # s = b'Date: Fri, 10 Mar 2017 20:43:08 GMT\r\n' # FIXME - unused r = Message(content_length=68) assert r.content_length == 68 assert len(r) == 2 def test_invalid(): import pytest s = b'INVALID / HTTP/1.0\r\n' with pytest.raises(dpkt.UnpackError, match="invalid http method: u?'INVALID'"): Request(s) s = b'A' with pytest.raises(dpkt.UnpackError, match="invalid response: b?'A'"): Response(s) s = b'HTTT 200 OK' with pytest.raises(dpkt.UnpackError, match="invalid response: b?'HTTT 200 OK'"): Response(s) s = b'HTTP TWO OK' with pytest.raises(dpkt.UnpackError, match="invalid response: b?'HTTP TWO OK'"): Response(s) s = ( b'HTTP/1.0 200 OK\r\n' b'Invalid Header: invalid\r\n' ) with pytest.raises(dpkt.UnpackError, match="invalid header: "): Response(s) s = ( b"HTTP/1.1 200 OK\r\n" b"Transfer-Encoding: chunked\r\n" b"\r\n" b"\r\n" ) with pytest.raises(dpkt.UnpackError, match="missing chunk size"): Response(s) s = ( b"HTTP/1.1 200 OK\r\n" b"Transfer-Encoding: chunked\r\n" b"\r\n" b"\x01\r\na" ) with pytest.raises(dpkt.UnpackError, match="invalid chunk size"): Response(s) s = ( b"HTTP/1.1 200 OK\r\n" b"Transfer-Encoding: chunked\r\n" b"\r\n" b"2\r\n" b"abcd" ) with pytest.raises(dpkt.NeedData, match="premature end of chunked body"): Response(s) s = ( b"HTTP/1.1 200 OK\r\n" b"Content-Length: 68\r\n" b"\r\n" b"a\r\n" ) with pytest.raises(dpkt.NeedData, match=r"short body \(missing 65 bytes\)"): Response(s) # messy header. s_messy_header = b'aaaaaaaaa\r\nbbbbbbbbb' with pytest.raises(dpkt.UnpackError, match="invalid request: u?'aaaaaaaa"): Request(s_messy_header) def test_response_str(): s = ( b'HTTP/1.0 200 OK\r\n' b'Server: SimpleHTTP/0.6 Python/2.7.12\r\n' b'Date: Fri, 10 Mar 2017 20:43:08 GMT\r\n' b'Content-type: text/plain\r\n' ) # the headers are processed to lowercase keys resp = [ 'HTTP/1.0 200 OK', 'server: SimpleHTTP/0.6 Python/2.7.12', 'date: Fri, 10 Mar 2017 20:43:08 GMT', 'content-type: text/plain', '', '', ] r_str = str(Response(s)) s_arr = sorted(resp) resp_arr = sorted(r_str.split('\r\n')) for line1, line2 in zip(s_arr, resp_arr): assert line1 == line2 def test_request_str(): s = b'GET / HTTP/1.0\r\n' r = Request(s) req = 'GET / HTTP/1.0\r\n\r\n' assert req == str(r) def test_parse_body(): import pytest from .compat import BytesIO buf = BytesIO( b'05\r\n' # size b'ERR' # longer than size ) buf.seek(0) headers = { 'transfer-encoding': 'chunked', } with pytest.raises(dpkt.NeedData, match="premature end of chunked body"): parse_body(buf, headers) dpkt-1.9.8/dpkt/http2.py000066400000000000000000000710371427735150300150670ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Hypertext Transfer Protocol Version 2.""" import struct import codecs from . import dpkt HTTP2_PREFACE = b'\x50\x52\x49\x20\x2a\x20\x48\x54\x54\x50\x2f\x32\x2e\x30\x0d\x0a\x0d\x0a\x53\x4d\x0d\x0a\x0d\x0a' # Frame types HTTP2_FRAME_DATA = 0 HTTP2_FRAME_HEADERS = 1 HTTP2_FRAME_PRIORITY = 2 HTTP2_FRAME_RST_STREAM = 3 HTTP2_FRAME_SETTINGS = 4 HTTP2_FRAME_PUSH_PROMISE = 5 HTTP2_FRAME_PING = 6 HTTP2_FRAME_GOAWAY = 7 HTTP2_FRAME_WINDOW_UPDATE = 8 HTTP2_FRAME_CONTINUATION = 9 # Flags HTTP2_FLAG_END_STREAM = 0x01 # for DATA and HEADERS frames HTTP2_FLAG_ACK = 0x01 # for SETTINGS and PING frames HTTP2_FLAG_END_HEADERS = 0x04 HTTP2_FLAG_PADDED = 0x08 HTTP2_FLAG_PRIORITY = 0x20 # Settings HTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x1 HTTP2_SETTINGS_ENABLE_PUSH = 0x2 HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3 HTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x4 HTTP2_SETTINGS_MAX_FRAME_SIZE = 0x5 HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6 # Error codes HTTP2_NO_ERROR = 0x0 HTTP2_PROTOCOL_ERROR = 0x1 HTTP2_INTERNAL_ERROR = 0x2 HTTP2_FLOW_CONTROL_ERROR = 0x3 HTTP2_SETTINGS_TIMEOUT = 0x4 HTTP2_STREAM_CLOSED = 0x5 HTTP2_FRAME_SIZE_ERROR = 0x6 HTTP2_REFUSED_STREAM = 0x7 HTTP2_CANCEL = 0x8 HTTP2_COMPRESSION_ERROR = 0x9 HTTP2_CONNECT_ERROR = 0xa HTTP2_ENHANCE_YOUR_CALM = 0xb HTTP2_INADEQUATE_SECURITY = 0xc HTTP2_HTTP_1_1_REQUIRED = 0xd error_code_str = { HTTP2_NO_ERROR: 'NO_ERROR', HTTP2_PROTOCOL_ERROR: 'PROTOCOL_ERROR', HTTP2_INTERNAL_ERROR: 'INTERNAL_ERROR', HTTP2_FLOW_CONTROL_ERROR: 'FLOW_CONTROL_ERROR', HTTP2_SETTINGS_TIMEOUT: 'SETTINGS_TIMEOUT', HTTP2_STREAM_CLOSED: 'STREAM_CLOSED', HTTP2_FRAME_SIZE_ERROR: 'FRAME_SIZE_ERROR', HTTP2_REFUSED_STREAM: 'REFUSED_STREAM', HTTP2_CANCEL: 'CANCEL', HTTP2_COMPRESSION_ERROR: 'COMPRESSION_ERROR', HTTP2_CONNECT_ERROR: 'CONNECT_ERROR', HTTP2_ENHANCE_YOUR_CALM: 'ENHANCE_YOUR_CALM', HTTP2_INADEQUATE_SECURITY: 'INADEQUATE_SECURITY', HTTP2_HTTP_1_1_REQUIRED: 'HTTP_1_1_REQUIRED', } class HTTP2Exception(Exception): pass class Preface(dpkt.Packet): __hdr__ = ( ('preface', '24s', HTTP2_PREFACE), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.preface != HTTP2_PREFACE: raise HTTP2Exception('Invalid HTTP/2 preface') self.data = '' class Frame(dpkt.Packet): """ An HTTP/2 frame as defined in RFC 7540 """ # struct.unpack can't handle the 3-byte int, so we parse it as bytes # (and store it as bytes so dpkt doesn't get confused), and turn it into # an int in a user-facing property __hdr__ = ( ('length_bytes', '3s', 0), ('type', 'B', 0), ('flags', 'B', 0), ('stream_id', 'I', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) # only take the right number of bytes self.data = self.data[:self.length] if len(self.data) != self.length: raise dpkt.NeedData @property def length(self): return struct.unpack('!I', b'\x00' + self.length_bytes)[0] class Priority(dpkt.Packet): """ Payload of a PRIORITY frame, also used in HEADERS frame with FLAG_PRIORITY. Also used in the HEADERS frame if the PRIORITY flag is set. """ __hdr__ = ( ('stream_dep', 'I', 0), ('weight', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if len(self.data) != 0: raise HTTP2Exception('Invalid number of bytes in PRIORITY frame') self.exclusive = (self.stream_dep & 0x80000000) != 0 self.stream_dep &= 0x7fffffff self.weight += 1 class Setting(dpkt.Packet): """ A key-value pair used in the SETTINGS frame. """ __hdr__ = ( ('identifier', 'H', 0), ('value', 'I', 0), ) class PaddedFrame(Frame): """ Abstract class for frame types that support the FLAG_PADDED flag: DATA, HEADERS and PUSH_PROMISE. """ def unpack(self, buf): Frame.unpack(self, buf) if self.flags & HTTP2_FLAG_PADDED: if self.length == 0: raise HTTP2Exception('Missing padding length in PADDED frame') self.pad_length = struct.unpack('B', self.data[0:1])[0] if self.length <= self.pad_length: raise HTTP2Exception('Missing padding bytes in PADDED frame') self.unpadded_data = self.data[1:-self.pad_length] else: self.unpadded_data = self.data class DataFrame(PaddedFrame): """ Frame of type DATA. """ @property def payload(self): return self.unpadded_data class HeadersFrame(PaddedFrame): """ Frame of type HEADERS. """ def unpack(self, buf): PaddedFrame.unpack(self, buf) if self.flags & HTTP2_FLAG_PRIORITY: if len(self.unpadded_data) < 5: raise HTTP2Exception('Missing stream dependency in HEADERS frame with PRIORITY flag') self.priority = Priority(self.unpadded_data[:5]) self.block_fragment = self.unpadded_data[5:] else: self.block_fragment = self.unpadded_data class PriorityFrame(Frame): """ Frame of type PRIORITY. """ def unpack(self, buf): Frame.unpack(self, buf) self.priority = Priority(self.data) class RSTStreamFrame(Frame): """ Frame of type RST_STREAM. """ def unpack(self, buf): Frame.unpack(self, buf) if self.length != 4: raise HTTP2Exception('Invalid number of bytes in RST_STREAM frame (must be 4)') self.error_code = struct.unpack('!I', self.data)[0] class SettingsFrame(Frame): """ Frame of type SETTINGS. """ def unpack(self, buf): Frame.unpack(self, buf) if self.length % 6 != 0: raise HTTP2Exception('Invalid number of bytes in SETTINGS frame (must be multiple of 6)') self.settings = [] i = 0 while i < self.length: self.settings.append(Setting(self.data[i:i + 6])) i += 6 class PushPromiseFrame(PaddedFrame): """ Frame of type PUSH_PROMISE. """ def unpack(self, buf): PaddedFrame.unpack(self, buf) if len(self.unpadded_data) < 4: raise HTTP2Exception('Missing promised stream ID in PUSH_PROMISE frame') self.promised_id = struct.unpack('!I', self.data[:4])[0] self.block_fragment = self.unpadded_data[4:] class PingFrame(Frame): """ Frame of type PING. """ def unpack(self, buf): Frame.unpack(self, buf) if self.length != 8: raise HTTP2Exception('Invalid number of bytes in PING frame (must be 8)') class GoAwayFrame(Frame): """ Frame of type GO_AWAY. """ def unpack(self, buf): Frame.unpack(self, buf) if self.length < 8: raise HTTP2Exception('Invalid number of bytes in GO_AWAY frame') self.last_stream_id = struct.unpack('!I', self.data[:4])[0] self.error_code = struct.unpack('!I', self.data[4:8])[0] self.debug_data = self.data[8:] class WindowUpdateFrame(Frame): """ Frame of type WINDOW_UPDATE. """ def unpack(self, buf): Frame.unpack(self, buf) if self.length != 4: raise HTTP2Exception('Invalid number of bytes in WINDOW_UPDATE frame (must be 4)') self.window_increment = struct.unpack('!I', self.data)[0] class ContinuationFrame(Frame): """ Frame of type CONTINUATION. """ def unpack(self, buf): Frame.unpack(self, buf) self.block_fragment = self.data FRAME_TYPES = { HTTP2_FRAME_DATA: ('DATA', DataFrame), HTTP2_FRAME_HEADERS: ('HEADERS', HeadersFrame), HTTP2_FRAME_PRIORITY: ('PRIORITY', PriorityFrame), HTTP2_FRAME_RST_STREAM: ('RST_STREAM', RSTStreamFrame), HTTP2_FRAME_SETTINGS: ('SETTINGS', SettingsFrame), HTTP2_FRAME_PUSH_PROMISE: ('PUSH_PROMISE', PushPromiseFrame), HTTP2_FRAME_PING: ('PING', PingFrame), HTTP2_FRAME_GOAWAY: ('GOAWAY', GoAwayFrame), HTTP2_FRAME_WINDOW_UPDATE: ('WINDOW_UPDATE', WindowUpdateFrame), HTTP2_FRAME_CONTINUATION: ('CONTINUATION', ContinuationFrame), } class FrameFactory(object): def __new__(cls, buf): if len(buf) < 4: raise dpkt.NeedData t = struct.unpack('B', buf[3:4])[0] frame_type = FRAME_TYPES.get(t, None) if frame_type is None: raise HTTP2Exception('Invalid frame type: ' + hex(t)) return frame_type[1](buf) def frame_multi_factory(buf, preface=False): """ Attempt to parse one or more Frame's out of buf Args: buf: string containing HTTP/2 frames. May have an incomplete frame at the end. preface: expect an HTTP/2 preface at the beginning of the buffer. Returns: [Frame] int, total bytes consumed, != len(buf) if an incomplete frame was left at the end. """ i = 0 n = len(buf) frames = [] if preface: try: p = Preface(buf) i += len(p) except dpkt.NeedData: return [], 0 while i < n: try: frame = FrameFactory(buf[i:]) frames.append(frame) i += len(frame) except dpkt.NeedData: break return frames, i class TestFrame(object): """Some data found in real traffic""" @classmethod def setup_class(cls): # First TLS AppData record sent by Firefox (decrypted) record = codecs.decode(b'505249202a20485454502f322e300d0a' b'0d0a534d0d0a0d0a00000c0400000000' b'00000400020000000500004000000004' b'08000000000000bf0001000005020000' b'00000300000000c80000050200000000' b'05000000006400000502000000000700' b'00000000000005020000000009000000' b'070000000502000000000b0000000300', 'hex') cls.frames, cls.i = frame_multi_factory(record, preface=True) def test_frame(self): import pytest # Too short pytest.raises(dpkt.NeedData, Frame, codecs.decode(b'000001' # length b'0000' # type, flags b'deadbeef', # stream id 'hex')) def test_data(self): # Padded DATA frame frame_data_padded = FrameFactory(codecs.decode(b'000008' # length b'0008' # type, flags b'12345678' # stream id b'05' # pad length b'abcd' # data b'1122334455', # padding 'hex')) assert (frame_data_padded.length == 8) assert (frame_data_padded.type == HTTP2_FRAME_DATA) assert (frame_data_padded.flags == HTTP2_FLAG_PADDED) assert (frame_data_padded.stream_id == 0x12345678) assert (frame_data_padded.data == b'\x05\xAB\xCD\x11\x22\x33\x44\x55') assert (frame_data_padded.pad_length == 5) assert (frame_data_padded.unpadded_data == b'\xAB\xCD') assert (frame_data_padded.payload == b'\xAB\xCD') # empty DATA frame frame_data_empty_end = FrameFactory(codecs.decode(b'000000' # length b'0001' # type, flags b'deadbeef', # stream id 'hex')) assert (frame_data_empty_end.length == 0) assert (frame_data_empty_end.type == HTTP2_FRAME_DATA) assert (frame_data_empty_end.flags == HTTP2_FLAG_END_STREAM) assert (frame_data_empty_end.stream_id == 0xdeadbeef) assert (frame_data_empty_end.data == b'') assert (frame_data_empty_end.unpadded_data == b'') assert (frame_data_empty_end.payload == b'') import pytest # Invalid padding with pytest.raises(HTTP2Exception) as e: DataFrame(codecs.decode(b'000000' # length b'0008' # type, flags b'12345678' # stream id b'', # missing padding 'hex')) assert (str(e.value) == 'Missing padding length in PADDED frame') with pytest.raises(HTTP2Exception) as e: DataFrame(codecs.decode(b'000001' # length b'0008' # type, flags b'12345678' # stream id b'01' b'', # missing padding bytes 'hex')) assert (str(e.value) == 'Missing padding bytes in PADDED frame') def test_headers(self): frame_headers = FrameFactory(codecs.decode(b'000003' # length b'0100' # type, flags b'deadbeef' # stream id b'f00baa', # block fragment 'hex')) assert (frame_headers.length == 3) assert (frame_headers.type == HTTP2_FRAME_HEADERS) assert (frame_headers.flags == 0) assert (frame_headers.stream_id == 0xdeadbeef) assert (frame_headers.data == b'\xF0\x0B\xAA') assert (frame_headers.unpadded_data == b'\xF0\x0B\xAA') assert (frame_headers.block_fragment == b'\xF0\x0B\xAA') frame_headers_prio = FrameFactory(codecs.decode(b'000008' # length b'0120' # type, flags b'deadbeef' # stream id b'cafebabe10' # priority b'f00baa', # block fragment 'hex')) assert (frame_headers_prio.length == 8) assert (frame_headers_prio.type == HTTP2_FRAME_HEADERS) assert (frame_headers_prio.flags == HTTP2_FLAG_PRIORITY) assert (frame_headers_prio.stream_id == 0xdeadbeef) assert (frame_headers_prio.data == b'\xCA\xFE\xBA\xBE\x10\xF0\x0B\xAA') assert (frame_headers_prio.unpadded_data == b'\xCA\xFE\xBA\xBE\x10\xF0\x0B\xAA') assert (frame_headers_prio.priority.exclusive is True) assert (frame_headers_prio.priority.stream_dep == 0x4afebabe) assert (frame_headers_prio.priority.weight == 0x11) assert (frame_headers_prio.block_fragment == b'\xF0\x0B\xAA') import pytest # Invalid priority with pytest.raises(HTTP2Exception) as e: HeadersFrame(codecs.decode(b'000002' # length b'0120' # type, flags b'deadbeef' # stream id b'1234', # invalid priority 'hex')) assert (str(e.value) == 'Missing stream dependency in HEADERS frame with PRIORITY flag') def test_priority(self): frame_priority = FrameFactory(codecs.decode(b'000005' # length b'0200' # type, flags b'deadbeef' # stream id b'cafebabe' # stream dep b'12', # weight 'hex')) assert (frame_priority.length == 5) assert (frame_priority.type == HTTP2_FRAME_PRIORITY) assert (frame_priority.flags == 0) assert (frame_priority.stream_id == 0xdeadbeef) assert (frame_priority.data == b'\xCA\xFE\xBA\xBE\x12') assert (frame_priority.priority.data == b'') assert (frame_priority.priority.exclusive is True) assert (frame_priority.priority.stream_dep == 0x4afebabe) assert (frame_priority.priority.weight == 0x13) import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: PriorityFrame(codecs.decode(b'000006' # length b'0200' # type, flags b'deadbeef' # stream id b'cafebabe' # stream dep b'12' # weight b'00', # unexpected additional payload 'hex')) assert (str(e.value) == 'Invalid number of bytes in PRIORITY frame') def test_rst_stream(self): frame_rst = FrameFactory(codecs.decode(b'000004' # length b'0300' # type, flags b'deadbeef' # stream id b'0000000c', # error code 'hex')) assert (frame_rst.length == 4) assert (frame_rst.type == HTTP2_FRAME_RST_STREAM) assert (frame_rst.flags == 0) assert (frame_rst.stream_id == 0xdeadbeef) assert (frame_rst.data == b'\x00\x00\x00\x0c') assert (frame_rst.error_code == HTTP2_INADEQUATE_SECURITY) import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: RSTStreamFrame(codecs.decode(b'000005' # length b'0300' # type, flags b'deadbeef' # stream id b'0000000c' # error code b'00', # unexpected additional payload 'hex')) assert (str(e.value) == 'Invalid number of bytes in RST_STREAM frame (must be 4)') def test_settings(self): frame_settings = FrameFactory(codecs.decode(b'00000c' # length b'0400' # type, flags b'00000000' # stream id # settings b'0004' # setting id b'00020000' # setting value b'0005' # setting id b'00004000', # setting value 'hex')) assert (frame_settings.length == 12) assert (frame_settings.type == HTTP2_FRAME_SETTINGS) assert (frame_settings.flags == 0) assert (frame_settings.stream_id == 0) assert (len(frame_settings.settings) == 2) assert (frame_settings.settings[0].identifier == HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) assert (frame_settings.settings[0].value == 0x20000) assert (frame_settings.settings[1].identifier == HTTP2_SETTINGS_MAX_FRAME_SIZE) assert (frame_settings.settings[1].value == 0x4000) # Settings ack, with empty payload frame_settings_ack = FrameFactory(codecs.decode(b'000000' # length b'0401' # type, flags b'00000000', # stream id 'hex')) assert (frame_settings_ack.length == 0) assert (frame_settings_ack.type == HTTP2_FRAME_SETTINGS) assert (frame_settings_ack.flags == HTTP2_FLAG_ACK) assert (frame_settings_ack.stream_id == 0) assert (len(frame_settings_ack.settings) == 0) import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: SettingsFrame(codecs.decode(b'000005' # length b'0400' # type, flags b'deadbeef' # stream id b'1234567890', # invalid length 'hex')) assert (str(e.value) == 'Invalid number of bytes in SETTINGS frame (must be multiple of 6)') def test_push_promise(self): frame_pp = FrameFactory(codecs.decode(b'000007' # length b'0500' # type, flags b'deadbeef' # stream id b'cafebabe' # promised id b'123456', # some block fragment 'hex')) assert (frame_pp.length == 7) assert (frame_pp.type == HTTP2_FRAME_PUSH_PROMISE) assert (frame_pp.flags == 0) assert (frame_pp.stream_id == 0xdeadbeef) assert (frame_pp.promised_id == 0xcafebabe) assert (frame_pp.block_fragment == b'\x12\x34\x56') import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: PushPromiseFrame(codecs.decode(b'000003' # length b'0500' # type, flags b'deadbeef' # stream id b'cafeba', # missing promised id 'hex')) assert (str(e.value) == 'Missing promised stream ID in PUSH_PROMISE frame') def test_ping(self): frame_ping = FrameFactory(codecs.decode(b'000008' # length b'0600' # type, flags b'deadbeef' # stream id b'cafebabe12345678', # user data 'hex')) assert (frame_ping.length == 8) assert (frame_ping.type == HTTP2_FRAME_PING) assert (frame_ping.flags == 0) assert (frame_ping.stream_id == 0xdeadbeef) assert (frame_ping.data == b'\xCA\xFE\xBA\xBE\x12\x34\x56\x78') import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: PingFrame(codecs.decode(b'000005' # length b'0600' # type, flags b'deadbeef' # stream id b'1234567890', # invalid length 'hex')) assert (str(e.value) == 'Invalid number of bytes in PING frame (must be 8)') def test_goaway(self): frame_goaway = FrameFactory(codecs.decode(b'00000a' # length b'0700' # type, flags b'deadbeef' # stream id b'00000000' # last stream id b'00000000' # error code b'cafe', # debug data 'hex')) assert (frame_goaway.length == 10) assert (frame_goaway.type == HTTP2_FRAME_GOAWAY) assert (frame_goaway.flags == 0) assert (frame_goaway.stream_id == 0xdeadbeef) assert (frame_goaway.last_stream_id == 0) assert (frame_goaway.error_code == HTTP2_NO_ERROR) assert (frame_goaway.debug_data == b'\xCA\xFE') import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: GoAwayFrame(codecs.decode(b'000005' # length b'0700' # type, flags b'deadbeef' # stream id b'1234567890', # invalid length 'hex')) assert (str(e.value) == 'Invalid number of bytes in GO_AWAY frame') def test_window_update(self): frame_wu = FrameFactory(codecs.decode(b'000004' # length b'0800' # type, flags b'deadbeef' # stream id b'12345678', # window increment 'hex')) assert (frame_wu.length == 4) assert (frame_wu.type == HTTP2_FRAME_WINDOW_UPDATE) assert (frame_wu.flags == 0) assert (frame_wu.stream_id == 0xdeadbeef) assert (frame_wu.window_increment == 0x12345678) import pytest # Invalid length with pytest.raises(HTTP2Exception) as e: WindowUpdateFrame(codecs.decode(b'000005' # length b'0800' # type, flags b'deadbeef' # stream id b'1234567890', # invalid length 'hex')) assert (str(e.value) == 'Invalid number of bytes in WINDOW_UPDATE frame (must be 4)') def test_continuation(self): frame_cont = FrameFactory(codecs.decode(b'000003' # length b'0900' # type, flags b'deadbeef' # stream id b'f00baa', # block fragment 'hex')) assert (frame_cont.length == 3) assert (frame_cont.type == HTTP2_FRAME_CONTINUATION) assert (frame_cont.flags == 0) assert (frame_cont.stream_id == 0xdeadbeef) assert (frame_cont.block_fragment == b'\xF0\x0B\xAA') def test_factory(self): import pytest # Too short pytest.raises(dpkt.NeedData, FrameFactory, codecs.decode(b'000000', 'hex')) # Invalid type with pytest.raises(HTTP2Exception) as e: FrameFactory(codecs.decode(b'000000' # length b'abcd' # type, flags b'deadbeef', # stream id 'hex')) assert (str(e.value) == 'Invalid frame type: 0xab') def test_preface(self): import pytest # Preface pytest.raises(dpkt.NeedData, Preface, codecs.decode(b'505249202a20485454502f322e300d0a', 'hex')) pytest.raises(dpkt.NeedData, Preface, b'\x00' * 23) with pytest.raises(HTTP2Exception) as e: Preface(b'\x00' * 24) assert (str(e.value) == 'Invalid HTTP/2 preface') def test_multi(self): assert (self.i == 128) assert (len(self.frames) == 7) assert (self.frames[0].length == 12) assert (self.frames[1].length == 4) assert (self.frames[2].length == 5) assert (self.frames[3].length == 5) assert (self.frames[4].length == 5) assert (self.frames[5].length == 5) assert (self.frames[6].length == 5) assert (self.frames[0].type == HTTP2_FRAME_SETTINGS) assert (self.frames[1].type == HTTP2_FRAME_WINDOW_UPDATE) assert (self.frames[2].type == HTTP2_FRAME_PRIORITY) assert (self.frames[3].type == HTTP2_FRAME_PRIORITY) assert (self.frames[4].type == HTTP2_FRAME_PRIORITY) assert (self.frames[5].type == HTTP2_FRAME_PRIORITY) assert (self.frames[6].type == HTTP2_FRAME_PRIORITY) assert (self.frames[0].flags == 0) assert (self.frames[1].flags == 0) assert (self.frames[2].flags == 0) assert (self.frames[3].flags == 0) assert (self.frames[4].flags == 0) assert (self.frames[5].flags == 0) assert (self.frames[6].flags == 0) assert (self.frames[0].stream_id == 0) assert (self.frames[1].stream_id == 0) assert (self.frames[2].stream_id == 3) assert (self.frames[3].stream_id == 5) assert (self.frames[4].stream_id == 7) assert (self.frames[5].stream_id == 9) assert (self.frames[6].stream_id == 11) frames, i = frame_multi_factory( codecs.decode(b'505249202a20485454502f322e300d0a', 'hex'), preface=True) assert (len(frames) == 0) assert (i == 0) # Only preface was parsed frames, i = frame_multi_factory( codecs.decode(b'505249202a20485454502f322e300d0a' b'0d0a534d0d0a0d0a00000c0400000000', 'hex'), preface=True) assert (len(frames) == 0) assert (i == 24) dpkt-1.9.8/dpkt/icmp.py000066400000000000000000000141331427735150300147500ustar00rootroot00000000000000# $Id: icmp.py 45 2007-08-03 00:05:22Z jon.oberheide $ # -*- coding: utf-8 -*- """Internet Control Message Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt # Types (icmp_type) and codes (icmp_code) - # http://www.iana.org/assignments/icmp-parameters ICMP_CODE_NONE = 0 # for types without codes ICMP_ECHOREPLY = 0 # echo reply ICMP_UNREACH = 3 # dest unreachable, codes: ICMP_UNREACH_NET = 0 # bad net ICMP_UNREACH_HOST = 1 # bad host ICMP_UNREACH_PROTO = 2 # bad protocol ICMP_UNREACH_PORT = 3 # bad port ICMP_UNREACH_NEEDFRAG = 4 # IP_DF caused drop ICMP_UNREACH_SRCFAIL = 5 # src route failed ICMP_UNREACH_NET_UNKNOWN = 6 # unknown net ICMP_UNREACH_HOST_UNKNOWN = 7 # unknown host ICMP_UNREACH_ISOLATED = 8 # src host isolated ICMP_UNREACH_NET_PROHIB = 9 # for crypto devs ICMP_UNREACH_HOST_PROHIB = 10 # ditto ICMP_UNREACH_TOSNET = 11 # bad tos for net ICMP_UNREACH_TOSHOST = 12 # bad tos for host ICMP_UNREACH_FILTER_PROHIB = 13 # prohibited access ICMP_UNREACH_HOST_PRECEDENCE = 14 # precedence error ICMP_UNREACH_PRECEDENCE_CUTOFF = 15 # precedence cutoff ICMP_SRCQUENCH = 4 # packet lost, slow down ICMP_REDIRECT = 5 # shorter route, codes: ICMP_REDIRECT_NET = 0 # for network ICMP_REDIRECT_HOST = 1 # for host ICMP_REDIRECT_TOSNET = 2 # for tos and net ICMP_REDIRECT_TOSHOST = 3 # for tos and host ICMP_ALTHOSTADDR = 6 # alternate host address ICMP_ECHO = 8 # echo service ICMP_RTRADVERT = 9 # router advertise, codes: ICMP_RTRADVERT_NORMAL = 0 # normal ICMP_RTRADVERT_NOROUTE_COMMON = 16 # selective routing ICMP_RTRSOLICIT = 10 # router solicitation ICMP_TIMEXCEED = 11 # time exceeded, code: ICMP_TIMEXCEED_INTRANS = 0 # ttl==0 in transit ICMP_TIMEXCEED_REASS = 1 # ttl==0 in reass ICMP_PARAMPROB = 12 # ip header bad ICMP_PARAMPROB_ERRATPTR = 0 # req. opt. absent ICMP_PARAMPROB_OPTABSENT = 1 # req. opt. absent ICMP_PARAMPROB_LENGTH = 2 # bad length ICMP_TSTAMP = 13 # timestamp request ICMP_TSTAMPREPLY = 14 # timestamp reply ICMP_INFO = 15 # information request ICMP_INFOREPLY = 16 # information reply ICMP_MASK = 17 # address mask request ICMP_MASKREPLY = 18 # address mask reply ICMP_TRACEROUTE = 30 # traceroute ICMP_DATACONVERR = 31 # data conversion error ICMP_MOBILE_REDIRECT = 32 # mobile host redirect ICMP_IP6_WHEREAREYOU = 33 # IPv6 where-are-you ICMP_IP6_IAMHERE = 34 # IPv6 i-am-here ICMP_MOBILE_REG = 35 # mobile registration req ICMP_MOBILE_REGREPLY = 36 # mobile registration reply ICMP_DNS = 37 # domain name request ICMP_DNSREPLY = 38 # domain name reply ICMP_SKIP = 39 # SKIP ICMP_PHOTURIS = 40 # Photuris ICMP_PHOTURIS_UNKNOWN_INDEX = 0 # unknown sec index ICMP_PHOTURIS_AUTH_FAILED = 1 # auth failed ICMP_PHOTURIS_DECOMPRESS_FAILED = 2 # decompress failed ICMP_PHOTURIS_DECRYPT_FAILED = 3 # decrypt failed ICMP_PHOTURIS_NEED_AUTHN = 4 # no authentication ICMP_PHOTURIS_NEED_AUTHZ = 5 # no authorization ICMP_TYPE_MAX = 40 class ICMP(dpkt.Packet): """Internet Control Message Protocol. The Internet Control Message Protocol (ICMP) is a supporting protocol in the Internet protocol suite. It is used by network devices, including routers, to send error messages and operational information indicating success or failure when communicating with another IP address. Attributes: __hdr__: Header fields of ICMP. type: (int): ICMP type (1 byte) code: (int): ICMP subtype (1 byte) sum: (int): Internet checksum (RFC 1071) for error checking, calculated from the ICMP header and data with value 0 substituted for this field. (2 bytes) """ __hdr__ = ( ('type', 'B', 8), ('code', 'B', 0), ('sum', 'H', 0) ) class Echo(dpkt.Packet): __hdr__ = (('id', 'H', 0), ('seq', 'H', 0)) class Quote(dpkt.Packet): __hdr__ = (('pad', 'I', 0),) def unpack(self, buf): dpkt.Packet.unpack(self, buf) from . import ip self.data = self.ip = ip.IP(self.data) class Unreach(Quote): __hdr__ = (('pad', 'H', 0), ('mtu', 'H', 0)) class Quench(Quote): pass class Redirect(Quote): __hdr__ = (('gw', 'I', 0),) class ParamProbe(Quote): __hdr__ = (('ptr', 'B', 0), ('pad1', 'B', 0), ('pad2', 'H', 0)) class TimeExceed(Quote): pass _typesw = {0: Echo, 3: Unreach, 4: Quench, 5: Redirect, 8: Echo, 11: TimeExceed} def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: self.data = self._typesw[self.type](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): pass def __bytes__(self): if not self.sum: self.sum = dpkt.in_cksum(dpkt.Packet.__bytes__(self)) return dpkt.Packet.__bytes__(self) def test_icmp(): s = ( b'\x03\x0a\x6b\x19\x00\x00\x00\x00\x45\x00\x00\x28\x94\x1f\x00\x00\xe3\x06\x99\xb4\x23\x2b' b'\x24\x00\xde\x8e\x84\x42\xab\xd1\x00\x50\x00\x35\xe1\x29\x20\xd9\x00\x00\x00\x22\x9b\xf0' b'\xe2\x04\x65\x6b' ) r = ICMP(s) assert bytes(r) == s # construction s = ( b'\x00\x00\x53\x87\x00\x01\x03\xd6\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e' b'\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x41\x42\x43\x44\x45\x46\x47\x48\x49' ) p = ICMP( type=0, sum=0x5387, data=ICMP.Echo( id=1, seq=0x03d6, data=b'ABCDEFGHIJKLMNOPQRSTUVWABCDEFGHI' ) ) assert bytes(p) == s # test checksum p = ICMP( type=0, data=ICMP.Echo( id=1, seq=0x03d6, data=b'ABCDEFGHIJKLMNOPQRSTUVWABCDEFGHI' ) ) assert bytes(p) == s assert p.sum == 0x5387 def test_invalid_data(): from binascii import unhexlify buf = unhexlify( '01' # type (invalid entry) '00' # code '0000' # sum 'abcd' # data ) icmp = ICMP(buf) # no additional attributes have been added due to the type being invalid assert dir(icmp) == dir(ICMP()) dpkt-1.9.8/dpkt/icmp6.py000066400000000000000000000060141427735150300150350ustar00rootroot00000000000000# $Id: icmp6.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Internet Control Message Protocol for IPv6.""" from __future__ import absolute_import from . import dpkt ICMP6_DST_UNREACH = 1 # dest unreachable, codes: ICMP6_PACKET_TOO_BIG = 2 # packet too big ICMP6_TIME_EXCEEDED = 3 # time exceeded, code: ICMP6_PARAM_PROB = 4 # ip6 header bad ICMP6_ECHO_REQUEST = 128 # echo service ICMP6_ECHO_REPLY = 129 # echo reply MLD_LISTENER_QUERY = 130 # multicast listener query MLD_LISTENER_REPORT = 131 # multicast listener report MLD_LISTENER_DONE = 132 # multicast listener done # RFC2292 decls ICMP6_MEMBERSHIP_QUERY = 130 # group membership query ICMP6_MEMBERSHIP_REPORT = 131 # group membership report ICMP6_MEMBERSHIP_REDUCTION = 132 # group membership termination ND_ROUTER_SOLICIT = 133 # router solicitation ND_ROUTER_ADVERT = 134 # router advertisement ND_NEIGHBOR_SOLICIT = 135 # neighbor solicitation ND_NEIGHBOR_ADVERT = 136 # neighbor advertisement ND_REDIRECT = 137 # redirect ICMP6_ROUTER_RENUMBERING = 138 # router renumbering ICMP6_WRUREQUEST = 139 # who are you request ICMP6_WRUREPLY = 140 # who are you reply ICMP6_FQDN_QUERY = 139 # FQDN query ICMP6_FQDN_REPLY = 140 # FQDN reply ICMP6_NI_QUERY = 139 # node information request ICMP6_NI_REPLY = 140 # node information reply ICMP6_MAXTYPE = 201 class ICMP6(dpkt.Packet): """Internet Control Message Protocol for IPv6. Internet Control Message Protocol version 6 (ICMPv6) is the implementation of the Internet Control Message Protocol (ICMP) for Internet Protocol version 6 (IPv6). ICMPv6 is an integral part of IPv6 and performs error reporting and diagnostic functions. Attributes: __hdr__: Header fields of ICMPv6. type: (int): Type. Control messages are identified by the value in the type field. (1 byte) code: (int): Code. The code field gives additional context information for the message. (1 byte) sum: (int): Checksum. ICMPv6 provides a minimal level of message integrity verification. (2 bytes) """ __hdr__ = ( ('type', 'B', 0), ('code', 'B', 0), ('sum', 'H', 0) ) class Error(dpkt.Packet): __hdr__ = (('pad', 'I', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) from . import ip6 self.data = self.ip6 = ip6.IP6(self.data) class Unreach(Error): pass class TooBig(Error): __hdr__ = (('mtu', 'I', 1232), ) class TimeExceed(Error): pass class ParamProb(Error): __hdr__ = (('ptr', 'I', 0), ) class Echo(dpkt.Packet): __hdr__ = (('id', 'H', 0), ('seq', 'H', 0)) _typesw = {1: Unreach, 2: TooBig, 3: TimeExceed, 4: ParamProb, 128: Echo, 129: Echo} def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: self.data = self._typesw[self.type](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): pass dpkt-1.9.8/dpkt/ieee80211.py000066400000000000000000000773571427735150300153440ustar00rootroot00000000000000# $Id: 80211.py 53 2008-12-18 01:22:57Z jon.oberheide $ # -*- coding: utf-8 -*- """IEEE 802.11.""" from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt from .compat import ntole, ntole64 # Frame Types MGMT_TYPE = 0 CTL_TYPE = 1 DATA_TYPE = 2 # Frame Sub-Types M_ASSOC_REQ = 0 M_ASSOC_RESP = 1 M_REASSOC_REQ = 2 M_REASSOC_RESP = 3 M_PROBE_REQ = 4 M_PROBE_RESP = 5 M_BEACON = 8 M_ATIM = 9 M_DISASSOC = 10 M_AUTH = 11 M_DEAUTH = 12 M_ACTION = 13 C_BLOCK_ACK_REQ = 8 C_BLOCK_ACK = 9 C_PS_POLL = 10 C_RTS = 11 C_CTS = 12 C_ACK = 13 C_CF_END = 14 C_CF_END_ACK = 15 D_DATA = 0 D_DATA_CF_ACK = 1 D_DATA_CF_POLL = 2 D_DATA_CF_ACK_POLL = 3 D_NULL = 4 D_CF_ACK = 5 D_CF_POLL = 6 D_CF_ACK_POLL = 7 D_QOS_DATA = 8 D_QOS_CF_ACK = 9 D_QOS_CF_POLL = 10 D_QOS_CF_ACK_POLL = 11 D_QOS_NULL = 12 D_QOS_CF_POLL_EMPTY = 14 TO_DS_FLAG = 10 FROM_DS_FLAG = 1 INTER_DS_FLAG = 11 # Bitshifts for Frame Control _VERSION_MASK = 0x0300 _TYPE_MASK = 0x0c00 _SUBTYPE_MASK = 0xf000 _TO_DS_MASK = 0x0001 _FROM_DS_MASK = 0x0002 _MORE_FRAG_MASK = 0x0004 _RETRY_MASK = 0x0008 _PWR_MGT_MASK = 0x0010 _MORE_DATA_MASK = 0x0020 _WEP_MASK = 0x0040 _ORDER_MASK = 0x0080 _FRAGMENT_NUMBER_MASK = 0x000F _SEQUENCE_NUMBER_MASK = 0XFFF0 _VERSION_SHIFT = 8 _TYPE_SHIFT = 10 _SUBTYPE_SHIFT = 12 _TO_DS_SHIFT = 0 _FROM_DS_SHIFT = 1 _MORE_FRAG_SHIFT = 2 _RETRY_SHIFT = 3 _PWR_MGT_SHIFT = 4 _MORE_DATA_SHIFT = 5 _WEP_SHIFT = 6 _ORDER_SHIFT = 7 _SEQUENCE_NUMBER_SHIFT = 4 # IEs IE_SSID = 0 IE_RATES = 1 IE_FH = 2 IE_DS = 3 IE_CF = 4 IE_TIM = 5 IE_IBSS = 6 IE_HT_CAPA = 45 IE_ESR = 50 IE_HT_INFO = 61 FCS_LENGTH = 4 FRAMES_WITH_CAPABILITY = [M_BEACON, M_ASSOC_RESP, M_ASSOC_REQ, M_REASSOC_REQ, ] # Block Ack control constants _ACK_POLICY_SHIFT = 0 _MULTI_TID_SHIFT = 1 _COMPRESSED_SHIFT = 2 _TID_SHIFT = 12 _ACK_POLICY_MASK = 0x0001 _MULTI_TID_MASK = 0x0002 _COMPRESSED_MASK = 0x0004 _TID_MASK = 0xf000 _COMPRESSED_BMP_LENGTH = 8 _BMP_LENGTH = 128 # Action frame categories BLOCK_ACK = 3 # Block ack category action codes BLOCK_ACK_CODE_REQUEST = 0 BLOCK_ACK_CODE_RESPONSE = 1 BLOCK_ACK_CODE_DELBA = 2 class IEEE80211(dpkt.Packet): """IEEE 802.11. IEEE 802.11 is part of the IEEE 802 set of local area network (LAN) technical standards, and specifies the set of media access control (MAC) and physical layer (PHY) protocols for implementing wireless local area network (WLAN) computer communication. Attributes: __hdr__: Header fields of IEEE802.11. framectl: (int): Frame control (2 bytes) duration: (int): Duration ID (2 bytes) """ __hdr__ = ( ('framectl', 'H', 0), ('duration', 'H', 0) ) # The standard really defines the entire MAC protocol as little-endian on the wire, # however there is broken logic in the rest of the module preventing this from working right now # __byte_order__ = '<' @property def version(self): return (self.framectl & _VERSION_MASK) >> _VERSION_SHIFT @version.setter def version(self, val): self.framectl = (val << _VERSION_SHIFT) | (self.framectl & ~_VERSION_MASK) @property def type(self): return (self.framectl & _TYPE_MASK) >> _TYPE_SHIFT @type.setter def type(self, val): self.framectl = (val << _TYPE_SHIFT) | (self.framectl & ~_TYPE_MASK) @property def subtype(self): return (self.framectl & _SUBTYPE_MASK) >> _SUBTYPE_SHIFT @subtype.setter def subtype(self, val): self.framectl = (val << _SUBTYPE_SHIFT) | (self.framectl & ~_SUBTYPE_MASK) @property def to_ds(self): return (self.framectl & _TO_DS_MASK) >> _TO_DS_SHIFT @to_ds.setter def to_ds(self, val): self.framectl = (val << _TO_DS_SHIFT) | (self.framectl & ~_TO_DS_MASK) @property def from_ds(self): return (self.framectl & _FROM_DS_MASK) >> _FROM_DS_SHIFT @from_ds.setter def from_ds(self, val): self.framectl = (val << _FROM_DS_SHIFT) | (self.framectl & ~_FROM_DS_MASK) @property def more_frag(self): return (self.framectl & _MORE_FRAG_MASK) >> _MORE_FRAG_SHIFT @more_frag.setter def more_frag(self, val): self.framectl = (val << _MORE_FRAG_SHIFT) | (self.framectl & ~_MORE_FRAG_MASK) @property def retry(self): return (self.framectl & _RETRY_MASK) >> _RETRY_SHIFT @retry.setter def retry(self, val): self.framectl = (val << _RETRY_SHIFT) | (self.framectl & ~_RETRY_MASK) @property def pwr_mgt(self): return (self.framectl & _PWR_MGT_MASK) >> _PWR_MGT_SHIFT @pwr_mgt.setter def pwr_mgt(self, val): self.framectl = (val << _PWR_MGT_SHIFT) | (self.framectl & ~_PWR_MGT_MASK) @property def more_data(self): return (self.framectl & _MORE_DATA_MASK) >> _MORE_DATA_SHIFT @more_data.setter def more_data(self, val): self.framectl = (val << _MORE_DATA_SHIFT) | (self.framectl & ~_MORE_DATA_MASK) @property def wep(self): return (self.framectl & _WEP_MASK) >> _WEP_SHIFT @wep.setter def wep(self, val): self.framectl = (val << _WEP_SHIFT) | (self.framectl & ~_WEP_MASK) @property def order(self): return (self.framectl & _ORDER_MASK) >> _ORDER_SHIFT @order.setter def order(self, val): self.framectl = (val << _ORDER_SHIFT) | (self.framectl & ~_ORDER_MASK) def unpack_ies(self, buf): self.ies = [] ie_decoder = { IE_SSID: ('ssid', self.IE), IE_RATES: ('rate', self.IE), IE_FH: ('fh', self.FH), IE_DS: ('ds', self.DS), IE_CF: ('cf', self.CF), IE_TIM: ('tim', self.TIM), IE_IBSS: ('ibss', self.IBSS), IE_HT_CAPA: ('ht_capa', self.IE), IE_ESR: ('esr', self.IE), IE_HT_INFO: ('ht_info', self.IE) } # each IE starts with an ID and a length while len(buf) > FCS_LENGTH: ie_id = struct.unpack('B', buf[:1])[0] try: parser = ie_decoder[ie_id][1] name = ie_decoder[ie_id][0] except KeyError: parser = self.IE name = 'ie_' + str(ie_id) ie = parser(buf) ie.data = buf[2:2 + ie.len] setattr(self, name, ie) self.ies.append(ie) buf = buf[2 + ie.len:] class Capability(object): def __init__(self, field): self.ess = field & 1 self.ibss = (field >> 1) & 1 self.cf_poll = (field >> 2) & 1 self.cf_poll_req = (field >> 3) & 1 self.privacy = (field >> 4) & 1 self.short_preamble = (field >> 5) & 1 self.pbcc = (field >> 6) & 1 self.hopping = (field >> 7) & 1 self.spec_mgmt = (field >> 8) & 1 self.qos = (field >> 9) & 1 self.short_slot = (field >> 10) & 1 self.apsd = (field >> 11) & 1 self.dsss = (field >> 13) & 1 self.delayed_blk_ack = (field >> 14) & 1 self.imm_blk_ack = (field >> 15) & 1 def __init__(self, *args, **kwargs): if kwargs and 'fcs' in kwargs: self.fcs_present = kwargs.pop('fcs') else: self.fcs_present = False super(IEEE80211, self).__init__(*args, **kwargs) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = buf[self.__hdr_len__:] m_decoder = { M_BEACON: ('beacon', self.Beacon), M_ASSOC_REQ: ('assoc_req', self.Assoc_Req), M_ASSOC_RESP: ('assoc_resp', self.Assoc_Resp), M_DISASSOC: ('diassoc', self.Disassoc), M_REASSOC_REQ: ('reassoc_req', self.Reassoc_Req), M_REASSOC_RESP: ('reassoc_resp', self.Assoc_Resp), M_AUTH: ('auth', self.Auth), M_PROBE_RESP: ('probe_resp', self.Beacon), M_DEAUTH: ('deauth', self.Deauth), M_ACTION: ('action', self.Action) } c_decoder = { C_RTS: ('rts', self.RTS), C_CTS: ('cts', self.CTS), C_ACK: ('ack', self.ACK), C_BLOCK_ACK_REQ: ('bar', self.BlockAckReq), C_BLOCK_ACK: ('back', self.BlockAck), C_CF_END: ('cf_end', self.CFEnd), } d_dsData = { 0: self.Data, FROM_DS_FLAG: self.DataFromDS, TO_DS_FLAG: self.DataToDS, INTER_DS_FLAG: self.DataInterDS } # For now decode everything with DATA. Haven't checked about other QoS # additions d_decoder = { # modified the decoder to consider the ToDS and FromDS flags # Omitting the 11 case for now D_DATA: ('data_frame', d_dsData), D_NULL: ('data_frame', d_dsData), D_QOS_DATA: ('data_frame', d_dsData), D_QOS_NULL: ('data_frame', d_dsData) } decoder = { MGMT_TYPE: m_decoder, CTL_TYPE: c_decoder, DATA_TYPE: d_decoder } # Strip off the FCS field if self.fcs_present: self.fcs = struct.unpack('> _COMPRESSED_SHIFT @compressed.setter def compressed(self, val): self.ctl = (val << _COMPRESSED_SHIFT) | (self.ctl & ~_COMPRESSED_MASK) @property def ack_policy(self): return (self.ctl & _ACK_POLICY_MASK) >> _ACK_POLICY_SHIFT @ack_policy.setter def ack_policy(self, val): self.ctl = (val << _ACK_POLICY_SHIFT) | (self.ctl & ~_ACK_POLICY_MASK) @property def multi_tid(self): return (self.ctl & _MULTI_TID_MASK) >> _MULTI_TID_SHIFT @multi_tid.setter def multi_tid(self, val): self.ctl = (val << _MULTI_TID_SHIFT) | (self.ctl & ~_MULTI_TID_MASK) @property def tid(self): return (self.ctl & _TID_MASK) >> _TID_SHIFT @tid.setter def tid(self, val): self.ctl = (val << _TID_SHIFT) | (self.ctl & ~_TID_MASK) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = buf[self.__hdr_len__:] self.ctl = ntole(self.ctl) if self.compressed: self.bmp = struct.unpack('8s', self.data[0:_COMPRESSED_BMP_LENGTH])[0] else: self.bmp = struct.unpack('128s', self.data[0:_BMP_LENGTH])[0] self.data = self.data[len(self.__hdr__) + len(self.bmp):] class _FragmentNumSeqNumMixin(object): @property def fragment_number(self): return ntole(self.frag_seq) & _FRAGMENT_NUMBER_MASK @property def sequence_number(self): return (ntole(self.frag_seq) & _SEQUENCE_NUMBER_MASK) >> _SEQUENCE_NUMBER_SHIFT class RTS(dpkt.Packet): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6) ) class CTS(dpkt.Packet): __hdr__ = ( ('dst', '6s', '\x00' * 6), ) class ACK(dpkt.Packet): __hdr__ = ( ('dst', '6s', '\x00' * 6), ) class CFEnd(dpkt.Packet): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ) class MGMT_Frame(dpkt.Packet, _FragmentNumSeqNumMixin): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ('bssid', '6s', '\x00' * 6), ('frag_seq', 'H', 0) ) class Beacon(dpkt.Packet): __hdr__ = ( ('timestamp', 'Q', 0), ('interval', 'H', 0), ('capability', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.timestamp = ntole64(self.timestamp) self.interval = ntole(self.interval) class Disassoc(dpkt.Packet): __hdr__ = ( ('reason', 'H', 0), ) class Assoc_Req(dpkt.Packet): __hdr__ = ( ('capability', 'H', 0), ('interval', 'H', 0) ) class Assoc_Resp(dpkt.Packet): __hdr__ = ( ('capability', 'H', 0), ('status', 'H', 0), ('aid', 'H', 0) ) class Reassoc_Req(dpkt.Packet): __hdr__ = ( ('capability', 'H', 0), ('interval', 'H', 0), ('current_ap', '6s', '\x00' * 6) ) # This obviously doesn't support any of AUTH frames that use encryption class Auth(dpkt.Packet): __hdr__ = ( ('algorithm', 'H', 0), ('auth_seq', 'H', 0), ) class Deauth(dpkt.Packet): __hdr__ = ( ('reason', 'H', 0), ) class Action(dpkt.Packet): __hdr__ = ( ('category', 'B', 0), ('code', 'B', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) action_parser = { BLOCK_ACK: { BLOCK_ACK_CODE_REQUEST: ('block_ack_request', IEEE80211.BlockAckActionRequest), BLOCK_ACK_CODE_RESPONSE: ('block_ack_response', IEEE80211.BlockAckActionResponse), BLOCK_ACK_CODE_DELBA: ('block_ack_delba', IEEE80211.BlockAckActionDelba), }, } try: decoder = action_parser[self.category][self.code][1] field_name = action_parser[self.category][self.code][0] except KeyError: raise dpkt.UnpackError("KeyError: category=%s code=%s" % (self.category, self.code)) field = decoder(self.data) setattr(self, field_name, field) self.data = field.data class BlockAckActionRequest(dpkt.Packet): __hdr__ = ( ('dialog', 'B', 0), ('parameters', 'H', 0), ('timeout', 'H', 0), ('starting_seq', 'H', 0), ) class BlockAckActionResponse(dpkt.Packet): __hdr__ = ( ('dialog', 'B', 0), ('status_code', 'H', 0), ('parameters', 'H', 0), ('timeout', 'H', 0), ) class BlockAckActionDelba(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('delba_param_set', 'H', 0), ('reason_code', 'H', 0), # ('gcr_group_addr', '8s', '\x00' * 8), # Standard says it must be there, but it isn't? ) class Data(dpkt.Packet, _FragmentNumSeqNumMixin): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ('bssid', '6s', '\x00' * 6), ('frag_seq', 'H', 0) ) class DataFromDS(dpkt.Packet, _FragmentNumSeqNumMixin): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('bssid', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ('frag_seq', 'H', 0) ) class DataToDS(dpkt.Packet, _FragmentNumSeqNumMixin): __hdr__ = ( ('bssid', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ('dst', '6s', '\x00' * 6), ('frag_seq', 'H', 0) ) class DataInterDS(dpkt.Packet, _FragmentNumSeqNumMixin): __hdr__ = ( ('dst', '6s', '\x00' * 6), ('src', '6s', '\x00' * 6), ('da', '6s', '\x00' * 6), ('frag_seq', 'H', 0), ('sa', '6s', '\x00' * 6) ) class QoS_Data(dpkt.Packet): __hdr__ = ( ('control', 'H', 0), ) class IE(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.info = buf[2:self.len + 2] class FH(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0), ('tu', 'H', 0), ('hopset', 'B', 0), ('hoppattern', 'B', 0), ('hopindex', 'B', 0) ) class DS(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0), ('ch', 'B', 0) ) class CF(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0), ('count', 'B', 0), ('period', 'B', 0), ('max', 'H', 0), ('dur', 'H', 0) ) class TIM(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0), ('count', 'B', 0), ('period', 'B', 0), ('ctrl', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.bitmap = buf[5:self.len + 2] class IBSS(dpkt.Packet): __hdr__ = ( ('id', 'B', 0), ('len', 'B', 0), ('atim', 'H', 0) ) def test_802211_ack(): s = b'\xd4\x00\x00\x00\x00\x12\xf0\xb6\x1c\xa4\xff\xff\xff\xff' ieee = IEEE80211(s, fcs=True) assert ieee.version == 0 assert ieee.type == CTL_TYPE assert ieee.subtype == C_ACK assert ieee.to_ds == 0 assert ieee.from_ds == 0 assert ieee.pwr_mgt == 0 assert ieee.more_data == 0 assert ieee.wep == 0 assert ieee.order == 0 assert ieee.ack.dst == b'\x00\x12\xf0\xb6\x1c\xa4' fcs = struct.unpack('> 2)), ('tos', 'B', 0), ('len', 'H', 20), ('id', 'H', 0), ('_flags_offset', 'H', 0), # XXX - previously ip.off ('ttl', 'B', 64), ('p', 'B', 0), ('sum', 'H', 0), ('src', '4s', b'\x00' * 4), ('dst', '4s', b'\x00' * 4) ) __bit_fields__ = { '_v_hl': ( ('v', 4), # version, 4 bits ('hl', 4), # header len, 4 bits ), '_flags_offset': ( ('rf', 1), # reserved bit ('df', 1), # don't fragment ('mf', 1), # more fragments ('offset', 13), # fragment offset, 13 bits ) } __pprint_funcs__ = { 'dst': inet_to_str, 'src': inet_to_str, 'sum': hex, # display checksum in hex 'p': get_ip_proto_name } _protosw = {} opts = b'' def __init__(self, *args, **kwargs): super(IP, self).__init__(*args, **kwargs) # If IP packet is not initialized by string and the len field has # been rewritten. if not args and 'len' not in kwargs: self.len = self.__len__() def __len__(self): return self.__hdr_len__ + len(self.opts) + len(self.data) def __bytes__(self): if self.sum == 0: self.len = self.__len__() self.sum = dpkt.in_cksum(self.pack_hdr() + bytes(self.opts)) if (self.p == 6 or self.p == 17) and (self._flags_offset & (IP_MF | IP_OFFMASK)) == 0 and \ isinstance(self.data, dpkt.Packet) and self.data.sum == 0: # Set zeroed TCP and UDP checksums for non-fragments. p = bytes(self.data) s = dpkt.struct.pack('>4s4sxBH', self.src, self.dst, self.p, len(p)) s = dpkt.in_cksum_add(0, s) s = dpkt.in_cksum_add(s, p) self.data.sum = dpkt.in_cksum_done(s) # RFC 768 (Fields): # If the computed checksum is zero, it is transmitted as all # ones (the equivalent in one's complement arithmetic). An all # zero transmitted checksum value means that the transmitter # generated no checksum (for debugging or for higher level # protocols that don't care). if self.p == 17 and self.data.sum == 0: self.data.sum = 0xffff # XXX - skip transports which don't need the pseudoheader return self.pack_hdr() + bytes(self.opts) + bytes(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) ol = ((self._v_hl & 0xf) << 2) - self.__hdr_len__ if ol < 0: raise dpkt.UnpackError('invalid header length') self.opts = buf[self.__hdr_len__:self.__hdr_len__ + ol] if self.len: buf = buf[self.__hdr_len__ + ol:self.len] else: # very likely due to TCP segmentation offload buf = buf[self.__hdr_len__ + ol:] try: self.data = self._protosw[self.p](buf) if self.offset == 0 else buf setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): self.data = buf @classmethod def set_proto(cls, p, pktclass): cls._protosw[p] = pktclass @classmethod def get_proto(cls, p): return cls._protosw[p] # XXX - compatibility; to be deprecated @property def off(self): deprecation_warning("IP.off is deprecated") return self._flags_offset @off.setter def off(self, val): deprecation_warning("IP.off is deprecated") self.offset = val # IP Headers IP_ADDR_LEN = 0x04 IP_ADDR_BITS = 0x20 IP_HDR_LEN = 0x14 IP_OPT_LEN = 0x02 IP_OPT_LEN_MAX = 0x28 IP_HDR_LEN_MAX = IP_HDR_LEN + IP_OPT_LEN_MAX IP_LEN_MAX = 0xffff IP_LEN_MIN = IP_HDR_LEN # Reserved Addresses IP_ADDR_ANY = "\x00\x00\x00\x00" # 0.0.0.0 IP_ADDR_BROADCAST = "\xff\xff\xff\xff" # 255.255.255.255 IP_ADDR_LOOPBACK = "\x7f\x00\x00\x01" # 127.0.0.1 IP_ADDR_MCAST_ALL = "\xe0\x00\x00\x01" # 224.0.0.1 IP_ADDR_MCAST_LOCAL = "\xe0\x00\x00\xff" # 224.0.0.255 # Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474") IP_TOS_DEFAULT = 0x00 # default IP_TOS_LOWDELAY = 0x10 # low delay IP_TOS_THROUGHPUT = 0x08 # high throughput IP_TOS_RELIABILITY = 0x04 # high reliability IP_TOS_LOWCOST = 0x02 # low monetary cost - XXX IP_TOS_ECT = 0x02 # ECN-capable transport IP_TOS_CE = 0x01 # congestion experienced # IP precedence (high 3 bits of ip_tos), hopefully unused IP_TOS_PREC_ROUTINE = 0x00 IP_TOS_PREC_PRIORITY = 0x20 IP_TOS_PREC_IMMEDIATE = 0x40 IP_TOS_PREC_FLASH = 0x60 IP_TOS_PREC_FLASHOVERRIDE = 0x80 IP_TOS_PREC_CRITIC_ECP = 0xa0 IP_TOS_PREC_INTERNETCONTROL = 0xc0 IP_TOS_PREC_NETCONTROL = 0xe0 # Fragmentation flags (ip_off) IP_RF = 0x8000 # reserved IP_DF = 0x4000 # don't fragment IP_MF = 0x2000 # more fragments (not last frag) IP_OFFMASK = 0x1fff # mask for fragment offset # Time-to-live (ip_ttl), seconds IP_TTL_DEFAULT = 64 # default ttl, RFC 1122, RFC 1340 IP_TTL_MAX = 255 # maximum ttl # Protocol (ip_p) - http://www.iana.org/assignments/protocol-numbers IP_PROTO_IP = 0 # dummy for IP IP_PROTO_HOPOPTS = IP_PROTO_IP # IPv6 hop-by-hop options IP_PROTO_ICMP = 1 # ICMP IP_PROTO_IGMP = 2 # IGMP IP_PROTO_GGP = 3 # gateway-gateway protocol IP_PROTO_IPIP = 4 # IP in IP IP_PROTO_ST = 5 # ST datagram mode IP_PROTO_TCP = 6 # TCP IP_PROTO_CBT = 7 # CBT IP_PROTO_EGP = 8 # exterior gateway protocol IP_PROTO_IGP = 9 # interior gateway protocol IP_PROTO_BBNRCC = 10 # BBN RCC monitoring IP_PROTO_NVP = 11 # Network Voice Protocol IP_PROTO_PUP = 12 # PARC universal packet IP_PROTO_ARGUS = 13 # ARGUS IP_PROTO_EMCON = 14 # EMCON IP_PROTO_XNET = 15 # Cross Net Debugger IP_PROTO_CHAOS = 16 # Chaos IP_PROTO_UDP = 17 # UDP IP_PROTO_MUX = 18 # multiplexing IP_PROTO_DCNMEAS = 19 # DCN measurement IP_PROTO_HMP = 20 # Host Monitoring Protocol IP_PROTO_PRM = 21 # Packet Radio Measurement IP_PROTO_IDP = 22 # Xerox NS IDP IP_PROTO_TRUNK1 = 23 # Trunk-1 IP_PROTO_TRUNK2 = 24 # Trunk-2 IP_PROTO_LEAF1 = 25 # Leaf-1 IP_PROTO_LEAF2 = 26 # Leaf-2 IP_PROTO_RDP = 27 # "Reliable Datagram" proto IP_PROTO_IRTP = 28 # Inet Reliable Transaction IP_PROTO_TP = 29 # ISO TP class 4 IP_PROTO_NETBLT = 30 # Bulk Data Transfer IP_PROTO_MFPNSP = 31 # MFE Network Services IP_PROTO_MERITINP = 32 # Merit Internodal Protocol IP_PROTO_SEP = 33 # Sequential Exchange proto IP_PROTO_3PC = 34 # Third Party Connect proto IP_PROTO_IDPR = 35 # Interdomain Policy Route IP_PROTO_XTP = 36 # Xpress Transfer Protocol IP_PROTO_DDP = 37 # Datagram Delivery Proto IP_PROTO_CMTP = 38 # IDPR Ctrl Message Trans IP_PROTO_TPPP = 39 # TP++ Transport Protocol IP_PROTO_IL = 40 # IL Transport Protocol IP_PROTO_IP6 = 41 # IPv6 IP_PROTO_SDRP = 42 # Source Demand Routing IP_PROTO_ROUTING = 43 # IPv6 routing header IP_PROTO_FRAGMENT = 44 # IPv6 fragmentation header IP_PROTO_RSVP = 46 # Reservation protocol IP_PROTO_GRE = 47 # General Routing Encap IP_PROTO_MHRP = 48 # Mobile Host Routing IP_PROTO_ENA = 49 # ENA IP_PROTO_ESP = 50 # Encap Security Payload IP_PROTO_AH = 51 # Authentication Header IP_PROTO_INLSP = 52 # Integated Net Layer Sec IP_PROTO_SWIPE = 53 # SWIPE IP_PROTO_NARP = 54 # NBMA Address Resolution IP_PROTO_MOBILE = 55 # Mobile IP, RFC 2004 IP_PROTO_TLSP = 56 # Transport Layer Security IP_PROTO_SKIP = 57 # SKIP IP_PROTO_ICMP6 = 58 # ICMP for IPv6 IP_PROTO_NONE = 59 # IPv6 no next header IP_PROTO_DSTOPTS = 60 # IPv6 destination options IP_PROTO_ANYHOST = 61 # any host internal proto IP_PROTO_CFTP = 62 # CFTP IP_PROTO_ANYNET = 63 # any local network IP_PROTO_EXPAK = 64 # SATNET and Backroom EXPAK IP_PROTO_KRYPTOLAN = 65 # Kryptolan IP_PROTO_RVD = 66 # MIT Remote Virtual Disk IP_PROTO_IPPC = 67 # Inet Pluribus Packet Core IP_PROTO_DISTFS = 68 # any distributed fs IP_PROTO_SATMON = 69 # SATNET Monitoring IP_PROTO_VISA = 70 # VISA Protocol IP_PROTO_IPCV = 71 # Inet Packet Core Utility IP_PROTO_CPNX = 72 # Comp Proto Net Executive IP_PROTO_CPHB = 73 # Comp Protocol Heart Beat IP_PROTO_WSN = 74 # Wang Span Network IP_PROTO_PVP = 75 # Packet Video Protocol IP_PROTO_BRSATMON = 76 # Backroom SATNET Monitor IP_PROTO_SUNND = 77 # SUN ND Protocol IP_PROTO_WBMON = 78 # WIDEBAND Monitoring IP_PROTO_WBEXPAK = 79 # WIDEBAND EXPAK IP_PROTO_EON = 80 # ISO CNLP IP_PROTO_VMTP = 81 # Versatile Msg Transport IP_PROTO_SVMTP = 82 # Secure VMTP IP_PROTO_VINES = 83 # VINES IP_PROTO_TTP = 84 # TTP IP_PROTO_NSFIGP = 85 # NSFNET-IGP IP_PROTO_DGP = 86 # Dissimilar Gateway Proto IP_PROTO_TCF = 87 # TCF IP_PROTO_EIGRP = 88 # EIGRP IP_PROTO_OSPF = 89 # Open Shortest Path First IP_PROTO_SPRITERPC = 90 # Sprite RPC Protocol IP_PROTO_LARP = 91 # Locus Address Resolution IP_PROTO_MTP = 92 # Multicast Transport Proto IP_PROTO_AX25 = 93 # AX.25 Frames IP_PROTO_IPIPENCAP = 94 # yet-another IP encap IP_PROTO_MICP = 95 # Mobile Internet Ctrl IP_PROTO_SCCSP = 96 # Semaphore Comm Sec Proto IP_PROTO_ETHERIP = 97 # Ethernet in IPv4 IP_PROTO_ENCAP = 98 # encapsulation header IP_PROTO_ANYENC = 99 # private encryption scheme IP_PROTO_GMTP = 100 # GMTP IP_PROTO_IFMP = 101 # Ipsilon Flow Mgmt Proto IP_PROTO_PNNI = 102 # PNNI over IP IP_PROTO_PIM = 103 # Protocol Indep Multicast IP_PROTO_ARIS = 104 # ARIS IP_PROTO_SCPS = 105 # SCPS IP_PROTO_QNX = 106 # QNX IP_PROTO_AN = 107 # Active Networks IP_PROTO_IPCOMP = 108 # IP Payload Compression IP_PROTO_SNP = 109 # Sitara Networks Protocol IP_PROTO_COMPAQPEER = 110 # Compaq Peer Protocol IP_PROTO_IPXIP = 111 # IPX in IP IP_PROTO_VRRP = 112 # Virtual Router Redundancy IP_PROTO_PGM = 113 # PGM Reliable Transport IP_PROTO_ANY0HOP = 114 # 0-hop protocol IP_PROTO_L2TP = 115 # Layer 2 Tunneling Proto IP_PROTO_DDX = 116 # D-II Data Exchange (DDX) IP_PROTO_IATP = 117 # Interactive Agent Xfer IP_PROTO_STP = 118 # Schedule Transfer Proto IP_PROTO_SRP = 119 # SpectraLink Radio Proto IP_PROTO_UTI = 120 # UTI IP_PROTO_SMP = 121 # Simple Message Protocol IP_PROTO_SM = 122 # SM IP_PROTO_PTP = 123 # Performance Transparency IP_PROTO_ISIS = 124 # ISIS over IPv4 IP_PROTO_FIRE = 125 # FIRE IP_PROTO_CRTP = 126 # Combat Radio Transport IP_PROTO_CRUDP = 127 # Combat Radio UDP IP_PROTO_SSCOPMCE = 128 # SSCOPMCE IP_PROTO_IPLT = 129 # IPLT IP_PROTO_SPS = 130 # Secure Packet Shield IP_PROTO_PIPE = 131 # Private IP Encap in IP IP_PROTO_SCTP = 132 # Stream Ctrl Transmission IP_PROTO_FC = 133 # Fibre Channel IP_PROTO_RSVPIGN = 134 # RSVP-E2E-IGNORE IP_PROTO_RAW = 255 # Raw IP packets IP_PROTO_RESERVED = IP_PROTO_RAW # Reserved IP_PROTO_MAX = 255 # XXX - auto-load IP dispatch table from IP_PROTO_* definitions def __load_protos(): g = globals() for k, v in iteritems(g): if k.startswith('IP_PROTO_'): name = k[9:] _ip_proto_names[v] = name try: mod = __import__(name.lower(), g, level=1) IP.set_proto(v, getattr(mod, name)) except (ImportError, AttributeError): continue def _mod_init(): """Post-initialization called when all dpkt modules are fully loaded""" if not IP._protosw: __load_protos() def test_ip(): from . import udp s = b'E\x00\x00"\x00\x00\x00\x00@\x11r\xc0\x01\x02\x03\x04\x01\x02\x03\x04\x00o\x00\xde\x00\x0e\xbf5foobar' ip = IP(id=0, src=b'\x01\x02\x03\x04', dst=b'\x01\x02\x03\x04', p=17) u = udp.UDP(sport=111, dport=222) u.data = b'foobar' u.ulen += len(u.data) ip.data = u ip.len += len(u) assert (bytes(ip) == s) assert (ip.v == 4) assert (ip.hl == 5) ip = IP(s) assert (bytes(ip) == s) assert (ip.udp.sport == 111) assert (ip.udp.data == b'foobar') def test_dict(): ip = IP(id=0, src=b'\x01\x02\x03\x04', dst=b'\x01\x02\x03\x04', p=17) d = dict(ip) assert (d['src'] == b'\x01\x02\x03\x04') assert (d['dst'] == b'\x01\x02\x03\x04') assert (d['id'] == 0) assert (d['p'] == 17) def test_hl(): # Todo check this test method s = (b'BB\x03\x00\x00\x00\x00\x00\x00\x00\xd0\x00\xec\xbc\xa5\x00\x00\x00\x03\x80\x00\x00\xd0' b'\x01\xf2\xac\xa5"0\x01\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00') try: IP(s) except dpkt.UnpackError: pass def test_opt(): s = (b'\x4f\x00\x00\x3c\xae\x08\x00\x00\x40\x06\x18\x10\xc0\xa8\x0a\x26\xc0\xa8\x0a\x01\x07\x27' b'\x08\x01\x02\x03\x04\x01\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') ip = IP(s) ip.sum = 0 assert (bytes(ip) == s) def test_iplen(): # ensure the ip.len is not affected by serialization # https://github.com/kbandla/dpkt/issues/279 , https://github.com/kbandla/dpkt/issues/625 s = (b'\x45\x00\x00\xa3\xb6\x7a\x00\x00\x80\x11\x5e\x6f\xc0\xa8\x03\x02\x97\x47\xca\x6e\xc7\x38' b'\x64\xdf\x00\x8f\x4a\xfa\x26\xd8\x15\xbe\xd9\x42\xae\x66\xe1\xce\x14\x5f\x06\x79\x4b\x13' b'\x02\xad\xa4\x8b\x69\x1c\x7a\xf6\xd5\x3d\x45\xaa\xba\xcd\x24\x77\xc2\xe7\x5f\x6a\xcc\xb5' b'\x1f\x21\xfa\x62\xf0\xf3\x32\xe1\xe4\xf0\x20\x1f\x47\x61\xec\xbc\xb1\x0e\x6c\xf0\xb8\x6d' b'\x7f\x96\x9b\x35\x03\xa1\x79\x05\xc5\xfd\x2a\xf7\xfa\x35\xe3\x0e\x04\xd0\xc7\x4e\x94\x72' b'\x3d\x07\x5a\xa8\x53\x2a\x5d\x03\xf7\x04\xc4\xa8\xb8\xa1') ip_len1 = IP(s).len # original len assert (IP(bytes(IP(s))).len == ip_len1) def test_zerolen(): from . import tcp d = b'X' * 2048 s = (b'\x45\x00\x00\x00\x34\xce\x40\x00\x80\x06\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\xcc\x4e' b'\x0c\x38\x60\xff\xc6\x4e\x5f\x8a\x12\x98\x50\x18\x40\x29\x3a\xa3\x00\x00') + d ip = IP(s) assert (isinstance(ip.data, tcp.TCP)) assert (ip.tcp.data == d) def test_constuctor(): ip1 = IP(data=b"Hello world!") ip2 = IP(data=b"Hello world!", len=0) ip3 = IP(bytes(ip1)) ip4 = IP(bytes(ip2)) assert (bytes(ip1) == bytes(ip3)) assert (bytes(ip1) == b'E\x00\x00 \x00\x00\x00\x00@\x00z\xdf\x00\x00\x00\x00\x00\x00\x00\x00Hello world!') assert (bytes(ip2) == bytes(ip4)) assert (bytes(ip2) == b'E\x00\x00 \x00\x00\x00\x00@\x00z\xdf\x00\x00\x00\x00\x00\x00\x00\x00Hello world!') def test_frag(): from . import ethernet s = (b'\x00\x23\x20\xd4\x2a\x8c\x00\x23\x20\xd4\x2a\x8c\x08\x00\x45\x00\x00\x54\x00\x00\x40\x00' b'\x40\x01\x25\x8d\x0a\x00\x00\x8f\x0a\x00\x00\x8e\x08\x00\x2e\xa0\x01\xff\x23\x73\x20\x48' b'\x4a\x4d\x00\x00\x00\x00\x78\x85\x02\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17' b'\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d' b'\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37') ip = ethernet.Ethernet(s).ip assert (ip.rf == 0) assert (ip.df == 1) assert (ip.mf == 0) assert (ip.offset == 0) # test setters of fragmentation related attributes. ip.rf = 1 ip.df = 0 ip.mf = 1 ip.offset = 1480 assert (ip.rf == 1) assert (ip.df == 0) assert (ip.mf == 1) assert (ip.offset == 1480) def test_property_setters(): ip = IP() assert ip.v == 4 ip.v = 6 assert ip.v == 6 # test property delete del ip.v assert ip.v == 4 # back to default assert ip.hl == 5 ip.hl = 7 assert ip.hl == 7 del ip.hl assert ip.hl == 5 # coverage ip.off = 10 assert ip.off == 10 def test_default_udp_checksum(): from dpkt.udp import UDP udp = UDP(sport=1, dport=0xffdb) ip = IP(src=b'\x00\x00\x00\x01', dst=b'\x00\x00\x00\x01', p=17, data=udp) assert ip.p == 17 assert ip.data.sum == 0 # this forces recalculation of the data layer checksum bytes(ip) # during calculation the checksum was evaluated to 0x0000 # this was then conditionally set to 0xffff per RFC768 assert ip.data.sum == 0xffff def test_get_proto_name(): assert get_ip_proto_name(6) == 'TCP' assert get_ip_proto_name(999) is None dpkt-1.9.8/dpkt/ip6.py000066400000000000000000000515621427735150300145250ustar00rootroot00000000000000# $Id: ip6.py 87 2013-03-05 19:41:04Z andrewflnr@gmail.com $ # -*- coding: utf-8 -*- """Internet Protocol, version 6.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt from . import ip from . import tcp from .compat import compat_ord from .utils import inet_to_str import struct # The allowed extension headers and their classes (in order according to RFC). EXT_HDRS = [ip.IP_PROTO_HOPOPTS, ip.IP_PROTO_ROUTING, ip.IP_PROTO_FRAGMENT, ip.IP_PROTO_AH, ip.IP_PROTO_ESP, ip.IP_PROTO_DSTOPTS] # EXT_HDRS_CLS - classes is below - after all the used classes are defined. class IP6(dpkt.Packet): """Internet Protocol, version 6. Internet Protocol version 6 (IPv6) is the most recent version of the Internet Protocol (IP), the communications protocol that provides an identification and location system for computers on networks and routes traffic across the Internet. IPv6 was developed by the Internet Engineering Task Force (IETF) to deal with the long-anticipated problem of IPv4 address exhaustion, and is intended to replace IPv4. Attributes: __hdr__: Header fields of IPv6. _v_fc_flow: v: (int): Version (4 bits), fc (int): Traffic Class (6+2 bits) flow (int): Flow Label (20 bits). plen: (int): Payload Length (2 bytes). The size of the payload in octets, including any extension headers. nxt: (bytes): Next Header (1 byte). Specifies the type of the next header. hlim: (bytes): Hop Limit (1 byte). Replaces the time to live field in IPv4. src: (int): Source Address (16 bytes). The unicast IPv6 address of the sending node. dst: (int): Destination Address (16 bytes). Unicast or multicast address of the destination node(s). """ __hdr__ = ( ('_v_fc_flow', 'I', 0x60000000), ('plen', 'H', 0), # payload length (not including header) ('nxt', 'B', 0), # next header protocol ('hlim', 'B', 0), # hop limit ('src', '16s', b''), ('dst', '16s', b'') ) __bit_fields__ = { '_v_fc_flow': ( ('v', 4), # version, 4 hi bits ('fc', 8), # traffic class, 8 bits ('flow', 20), # flow label, 20 lo bits ) } __pprint_funcs__ = { 'src': inet_to_str, 'dst': inet_to_str } _protosw = ip.IP._protosw def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.extension_hdrs = {} # NOTE: self.extension_hdrs is not accurate, as it doesn't support duplicate header types. # According to RFC-1883 "Each extension header should occur at most once, except for the # Destination Options header which should occur at most twice". # Secondly, the .headers_str() method attempts to pack the extension headers in order as # defined in the RFC, however it doesn't adjust the next header (nxt) pointer accordingly. # Here we introduce the new field .all_extension_headers; it allows duplicate types and # keeps the original order. self.all_extension_headers = [] if self.plen: buf = self.data[:self.plen] else: # due to jumbo payload or TSO buf = self.data next_ext_hdr = self.nxt while next_ext_hdr in EXT_HDRS: ext = EXT_HDRS_CLS[next_ext_hdr](buf) self.extension_hdrs[next_ext_hdr] = ext self.all_extension_headers.append(ext) buf = buf[ext.length:] next_ext_hdr = getattr(ext, 'nxt', None) # set the payload protocol id if next_ext_hdr is not None: self.p = next_ext_hdr # do not decode fragments after the first fragment # https://github.com/kbandla/dpkt/issues/575 if self.nxt == 44 and ext.frag_off > 0: # 44 = IP_PROTO_FRAGMENT self.data = buf return try: self.data = self._protosw[next_ext_hdr](buf) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): self.data = buf def headers_str(self): nxt = self.nxt # If all_extension_headers is available, return the headers as they originally appeared if hasattr(self, 'all_extension_headers') and self.all_extension_headers: # get the nxt header from the last one nxt = self.all_extension_headers[-1].nxt return nxt, b''.join(bytes(ext) for ext in self.all_extension_headers) # Output extension headers in order defined in RFC1883 (except dest opts) header_str = b"" if hasattr(self, 'extension_hdrs'): for hdr in EXT_HDRS: if hdr in self.extension_hdrs: nxt = self.extension_hdrs[hdr].nxt header_str += bytes(self.extension_hdrs[hdr]) return nxt, header_str def __bytes__(self): self.p, hdr_str = self.headers_str() # set TCP, UDP, and ICMPv6 checksums if ((self.p == 6 or self.p == 17 or self.p == 58) and hasattr(self.data, 'sum') and not self.data.sum): p = bytes(self.data) s = struct.pack('>16s16sxBH', self.src, self.dst, self.p, len(p)) s = dpkt.in_cksum_add(0, s) s = dpkt.in_cksum_add(s, p) self.data.sum = dpkt.in_cksum_done(s) return self.pack_hdr() + hdr_str + bytes(self.data) def __len__(self): baselen = self.__hdr_len__ + len(self.data) if hasattr(self, 'all_extension_headers') and self.all_extension_headers: return baselen + sum(len(hh) for hh in self.all_extension_headers) elif hasattr(self, 'extension_hdrs') and self.extension_hdrs: return baselen + sum(len(hh) for hh in self.extension_hdrs.values()) return baselen @classmethod def set_proto(cls, p, pktclass): cls._protosw[p] = pktclass @classmethod def get_proto(cls, p): return cls._protosw[p] class IP6ExtensionHeader(dpkt.Packet): """ An extension header is very similar to a 'sub-packet'. We just want to re-use all the hdr unpacking etc. """ pass class IP6OptsHeader(IP6ExtensionHeader): __hdr__ = ( ('nxt', 'B', 0), # next extension header protocol ('len', 'B', 0) # option data length in 8 octet units (ignoring first 8 octets) so, len 0 == 64bit header ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.length = (self.len + 1) * 8 options = [] index = 0 while index < self.length - 2: try: opt_type = compat_ord(self.data[index]) # PAD1 option if opt_type == 0: index += 1 continue opt_length = compat_ord(self.data[index + 1]) if opt_type == 1: # PADN option # PADN uses opt_length bytes in total index += opt_length + 2 continue options.append({ 'type': opt_type, 'opt_length': opt_length, 'data': self.data[index + 2:index + 2 + opt_length] }) # add the two chars and the option_length, to move to the next option index += opt_length + 2 except IndexError: raise dpkt.NeedData self.options = options self.data = buf[2:self.length] # keep raw data with all pad options, but not the following data class IP6HopOptsHeader(IP6OptsHeader): pass class IP6DstOptsHeader(IP6OptsHeader): pass class IP6RoutingHeader(IP6ExtensionHeader): __hdr__ = ( ('nxt', 'B', 0), # next extension header protocol ('len', 'B', 0), # extension data length in 8 octet units (ignoring first 8 octets) (<= 46 for type 0) ('type', 'B', 0), # routing type (currently, only 0 is used) ('segs_left', 'B', 0), # remaining segments in route, until destination (<= 23) ('_rsvd_sl_bits', 'I', 0) ) __bit_fields__ = { '_rsvd_sl_bits': ( ('_rsvd', 8), # reserved (1 byte) ('sl_bits', 24), # strict/loose bitmap for addresses ) } def unpack(self, buf): hdr_size = 8 addr_size = 16 dpkt.Packet.unpack(self, buf) addresses = [] num_addresses = self.len // 2 buf = buf[hdr_size:hdr_size + num_addresses * addr_size] for i in range(num_addresses): addresses.append(buf[i * addr_size: i * addr_size + addr_size]) self.data = buf self.addresses = addresses self.length = self.len * 8 + 8 class IP6FragmentHeader(IP6ExtensionHeader): __hdr__ = ( ('nxt', 'B', 0), # next extension header protocol ('_resv', 'B', 0), # reserved, set to 0 ('_frag_off_resv_m', 'H', 0), ('id', 'I', 0) # fragments id ) __bit_fields__ = { '_frag_off_resv_m': ( ('frag_off', 13), # frag offset, 13 bits ('_resv', 2), # reserved zero (2 bits) ('m_flag', 1), # more frags flag ) } def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.length = self.__hdr_len__ self.data = b'' class IP6AHHeader(IP6ExtensionHeader): __hdr__ = ( ('nxt', 'B', 0), # next extension header protocol ('len', 'B', 0), # length of header in 4 octet units (ignoring first 2 units) ('_resv', 'H', 0), # reserved, 2 bytes of 0 ('spi', 'I', 0), # SPI security parameter index ('seq', 'I', 0) # sequence no. ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.length = (self.len + 2) * 4 self.auth_data = self.data[:(self.len - 1) * 4] class IP6ESPHeader(IP6ExtensionHeader): __hdr__ = ( ('spi', 'I', 0), ('seq', 'I', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.length = self.__hdr_len__ + len(self.data) EXT_HDRS_CLS = {ip.IP_PROTO_HOPOPTS: IP6HopOptsHeader, ip.IP_PROTO_ROUTING: IP6RoutingHeader, ip.IP_PROTO_FRAGMENT: IP6FragmentHeader, ip.IP_PROTO_ESP: IP6ESPHeader, ip.IP_PROTO_AH: IP6AHHeader, ip.IP_PROTO_DSTOPTS: IP6DstOptsHeader} # Unit tests def test_ipg(): s = (b'\x60\x00\x00\x00\x00\x28\x06\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x11\x24\xff\xfe\x8c' b'\x11\xde\xfe\x80\x00\x00\x00\x00\x00\x00\x02\xb0\xd0\xff\xfe\xe1\x80\x72\xcd\xca\x00\x16' b'\x04\x84\x46\xd5\x00\x00\x00\x00\xa0\x02\xff\xff\xf8\x09\x00\x00\x02\x04\x05\xa0\x01\x03' b'\x03\x00\x01\x01\x08\x0a\x7d\x18\x35\x3f\x00\x00\x00\x00') _ip = IP6(s) # basic properties assert _ip.v == 6 assert _ip.fc == 0 assert _ip.flow == 0 _ip.data.sum = 0 s2 = bytes(_ip) assert s == s2 def test_dict(): s = (b'\x60\x00\x00\x00\x00\x28\x06\x40\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x11\x24\xff\xfe\x8c' b'\x11\xde\xfe\x80\x00\x00\x00\x00\x00\x00\x02\xb0\xd0\xff\xfe\xe1\x80\x72\xcd\xca\x00\x16' b'\x04\x84\x46\xd5\x00\x00\x00\x00\xa0\x02\xff\xff\xf8\x09\x00\x00\x02\x04\x05\xa0\x01\x03' b'\x03\x00\x01\x01\x08\x0a\x7d\x18\x35\x3f\x00\x00\x00\x00') _ip = IP6(s) d = dict(_ip) # basic properties assert d['src'] == b'\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x11\x24\xff\xfe\x8c\x11\xde' assert d['dst'] == b'\xfe\x80\x00\x00\x00\x00\x00\x00\x02\xb0\xd0\xff\xfe\xe1\x80\x72' def test_ip6_routing_header(): s = (b'\x60\x00\x00\x00\x00\x3c\x2b\x40\x20\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\xde\xca\x20\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x02' b'\x00\x00\x00\x00\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x20\x22' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00\x50\x00\x00\x00\x00' b'\x00\x00\x00\x00\x50\x02\x20\x00\x91\x7f\x00\x00') _ip = IP6(s) s2 = bytes(_ip) # 43 is Routing header id assert len(_ip.extension_hdrs[43].addresses) == 2 assert _ip.tcp assert s == s2 def test_ip6_fragment_header(): s = b'\x06\xee\xff\xf9\x00\x00\xff\xff' fh = IP6FragmentHeader(s) # s2 = str(fh) variable 's2' is not used assert fh.nxt == 6 assert fh.id == 65535 assert fh.frag_off == 8191 assert fh.m_flag == 1 # test packing fh._frag_off_resv_m = 0 fh.frag_off = 8191 fh.m_flag = 1 assert bytes(fh) == s # IP6 with fragment header s = (b'\x60\x00\x00\x00\x00\x10\x2c\x00\x02\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x02\x03\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x29\x00\x00\x01' b'\x00\x00\x00\x00\x60\x00\x00\x00\x00\x10\x2c\x00') _ip = IP6(s) assert bytes(_ip) == s def test_ip6_options_header(): s = (b'\x3b\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00') options = IP6OptsHeader(s).options assert len(options) == 3 assert bytes(IP6OptsHeader(s)) == s def test_ip6_ah_header(): s = b'\x3b\x04\x00\x00\x02\x02\x02\x02\x01\x01\x01\x01\x78\x78\x78\x78\x78\x78\x78\x78' ah = IP6AHHeader(s) assert ah.length == 24 assert ah.auth_data == b'xxxxxxxx' assert ah.spi == 0x2020202 assert ah.seq == 0x1010101 assert bytes(ah) == s def test_ip6_esp_header(): s = (b'\x00\x00\x01\x00\x00\x00\x00\x44\xe2\x4f\x9e\x68\xf3\xcd\xb1\x5f\x61\x65\x42\x8b\x78\x0b' b'\x4a\xfd\x13\xf0\x15\x98\xf5\x55\x16\xa8\x12\xb3\xb8\x4d\xbc\x16\xb2\x14\xbe\x3d\xf9\x96' b'\xd4\xa0\x39\x1f\x85\x74\x25\x81\x83\xa6\x0d\x99\xb6\xba\xa3\xcc\xb6\xe0\x9a\x78\xee\xf2' b'\xaf\x9a') esp = IP6ESPHeader(s) assert esp.length == 68 assert esp.spi == 256 assert bytes(esp) == s def test_ip6_extension_headers(): p = (b'\x60\x00\x00\x00\x00\x3c\x2b\x40\x20\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\xde\xca\x20\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x02' b'\x00\x00\x00\x00\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x20\x22' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00\x50\x00\x00\x00\x00' b'\x00\x00\x00\x00\x50\x02\x20\x00\x91\x7f\x00\x00') _ip = IP6(p) o = (b'\x3b\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00') _ip.extension_hdrs[0] = IP6HopOptsHeader(o) fh = b'\x06\xee\xff\xfb\x00\x00\xff\xff' _ip.extension_hdrs[44] = IP6FragmentHeader(fh) ah = b'\x3b\x04\x00\x00\x02\x02\x02\x02\x01\x01\x01\x01\x78\x78\x78\x78\x78\x78\x78\x78' _ip.extension_hdrs[51] = IP6AHHeader(ah) do = b'\x3b\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' _ip.extension_hdrs[60] = IP6DstOptsHeader(do) assert len(_ip.extension_hdrs) == 5 # this is a legacy unit test predating the addition of .all_extension_headers # this way of adding extension headers does not update .all_extension_headers # so we need to kick .all_extension_headers to force the __len__() method pick up # the updated legacy attribute and calculate the len correctly del _ip.all_extension_headers assert len(_ip) == len(p) + len(o) + len(fh) + len(ah) + len(do) def test_ip6_all_extension_headers(): # https://github.com/kbandla/dpkt/pull/403 s = (b'\x60\x00\x00\x00\x00\x47\x3c\x40\xfe\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' b'\x00\x02\xfe\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x3c\x00\x01\x04' b'\x00\x00\x00\x00\x3c\x00\x01\x04\x00\x00\x00\x00\x2c\x00\x01\x04\x00\x00\x00\x00\x2c\x00' b'\x00\x00\x00\x00\x00\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x2c\x00\x01\x04\x00\x00\x00\x00' b'\x3a\x00\x00\x00\x00\x00\x00\x00\x80\x00\xd8\xe5\x0c\x1a\x00\x00\x50\x61\x79\x4c\x6f\x61' b'\x64') _ip = IP6(s) assert _ip.p == 58 # ICMPv6 hdrs = _ip.all_extension_headers assert len(hdrs) == 7 assert isinstance(hdrs[0], IP6DstOptsHeader) assert isinstance(hdrs[3], IP6FragmentHeader) assert isinstance(hdrs[5], IP6DstOptsHeader) assert bytes(_ip) == s assert len(_ip) == len(s) def test_ip6_gen_tcp_ack(): t = tcp.TCP() t.win = 8192 t.dport = 80 t.sport = 4711 t.flags = tcp.TH_ACK t.seq = 22 t.ack = 33 ipp = IP6() ipp.src = b'\xfd\x00\x00\x00\x00\x00\x00\x00\xc8\xba\x88\x88\x00\xaa\xbb\x01' ipp.dst = b'\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\xc1\n@*' ipp.hlim = 64 ipp.nxt = ip.IP_PROTO_TCP ipp.data = t ipp.plen = ipp.data.ulen = len(ipp.data) assert len(bytes(ipp)) == 60 assert ipp.p == ip.IP_PROTO_TCP # Second part of testing - with ext headers. ipp.p = 0 o = (b'\x3b\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00') ipp.extension_hdrs = {} ipp.extension_hdrs[0] = IP6HopOptsHeader(o) ipp.extension_hdrs[0].nxt = ip.IP_PROTO_TCP ipp.nxt = ip.proto = ip.IP_PROTO_HOPOPTS _p, exthdrs = ipp.headers_str() ipp.plen = len(exthdrs) + len(ipp.data) assert bytes(ipp) assert ipp.p == ip.IP_PROTO_TCP assert ipp.nxt == ip.IP_PROTO_HOPOPTS def test_ip6_opts(): import pytest # https://github.com/kbandla/dpkt/issues/477 s = (b'\x52\x54\x00\xf3\x83\x6f\x52\x54\x00\x86\x33\xd9\x86\xdd\x60\x00\x00\x00\x05\x08\x3a\xff' b'\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xfd\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\xd2\xf3\x00\x00\x05\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x01\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' b'\x00\x50\xd4\x34\x1a\x48\x24\x50\x6d\x8d\xb3\xc2\x80\x10\x01\xf6\x46\xe8\x00\x00\x01\x01' b'\x08\x0a\xd7\x9d\x6b\x8a\x3a\xd1\xf4\x58\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61' b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61' b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61' b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x0a') from dpkt.ethernet import Ethernet assert Ethernet(s) assert Ethernet(s).ip6 assert Ethernet(s).ip6.icmp6 assert Ethernet(s).ip6.icmp6.data with pytest.raises(dpkt.NeedData): IP6(Ethernet(s).ip6.icmp6.data) # should raise NeedData from binascii import unhexlify buf_ip6_opts = unhexlify( '00' # nxt '00' # len '000000000000' # only padding ) ip6opt = IP6OptsHeader(buf_ip6_opts) assert ip6opt.options == [] assert ip6opt.data == b'\x00' * 6 def test_ip6_routing_properties(): ip6rh = IP6RoutingHeader() assert ip6rh.sl_bits == 0 ip6rh.sl_bits = 1024 assert ip6rh.sl_bits == 1024 def test_ip6_fragment_properties(): ip6fh = IP6FragmentHeader() assert ip6fh.frag_off == 0 ip6fh.frag_off = 1234 assert ip6fh.frag_off == 1234 assert ip6fh.m_flag == 0 ip6fh.m_flag = 1 assert ip6fh.m_flag == 1 def test_ip6_properties(): ip6 = IP6() assert ip6.v == 6 ip6.v = 10 assert ip6.v == 10 assert ip6.fc == 0 ip6.fc = 5 assert ip6.fc == 5 assert ip6.flow == 0 ip6.flow = 4 assert ip6.flow == 4 # property delete del ip6.v del ip6.fc del ip6.flow assert ip6.v == 6 assert ip6.fc == 0 assert ip6.flow == 0 def test_proto_accessors(): class Proto: pass assert 'PROTO' not in IP6._protosw IP6.set_proto('PROTO', Proto) assert IP6.get_proto('PROTO') == Proto def test_ip6_fragment_no_decode(): # https://github.com/kbandla/dpkt/issues/575 from . import udp # fragment 0 s = (b'\x60\x00' b'\x00\x00\x00\x2c\x11\x3f\x20\x01\x06\x38\x05\x01\x8e\xfe\xcc\x4a' b'\x48\x39\xfa\x79\x04\xdc\x20\x01\x05\x00\x00\x60\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x30\xde\xf2\x00\x35\x00\x2c\x61\x50\x4d\x8b' b'\x01\x20\x00\x01\x00\x00\x00\x00\x00\x01\x03\x69\x73\x63\x03\x6f' b'\x72\x67\x00\x00\xff\x00\x01\x00\x00\x29\x10\x00\x00\x00\x80\x00' b'\x00\x00') frag0 = IP6(s) assert type(frag0.data) == udp.UDP s = (b'\x60\x00\x00\x00\x01\x34\x2c\x35\x20\x01\x05\x00\x00\x60\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x30\x20\x01\x06\x38\x05\x01\x8e\xfe' b'\xcc\x4a\x48\x39\xfa\x79\x04\xdc' b'\x11\x72\x31\xb9\xc1\x0f\xcf\x7c\x61\x62\x63\x64\x65\x66\x67\x68') # partial data frag2 = IP6(s) assert type(frag2.data) == bytes # test packing assert bytes(frag2) == s dpkt-1.9.8/dpkt/ipip.py000066400000000000000000000002531427735150300147570ustar00rootroot00000000000000# Defines a copy of the IP protocol as IPIP so the protocol parsing in ip.py # can decode IPIP packets. from __future__ import absolute_import from .ip import IP as IPIP dpkt-1.9.8/dpkt/ipx.py000066400000000000000000000020431427735150300146150ustar00rootroot00000000000000# $Id: ipx.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Internetwork Packet Exchange.""" from __future__ import absolute_import from . import dpkt IPX_HDR_LEN = 30 class IPX(dpkt.Packet): """Internetwork Packet Exchange. Internetwork Packet Exchange (IPX) is the network layer protocol in the IPX/SPX protocol suite. IPX is derived from Xerox Network Systems' IDP. It also has the ability to act as a transport layer protocol. Attributes: __hdr__: Header fields of IPX. sum: (int): Checksum (2 bytes). len: (int): Packet Length (including the IPX header / 2 bytes). tc: (int): Transport Control (hop count / 1 byte). pt: (int): Packet Type (1 byte). dst: (bytes): Destination address (12 bytes). src: (bytes): Source address (12 bytes). """ __hdr__ = ( ('sum', 'H', 0xffff), ('len', 'H', IPX_HDR_LEN), ('tc', 'B', 0), ('pt', 'B', 0), ('dst', '12s', b''), ('src', '12s', b'') ) dpkt-1.9.8/dpkt/llc.py000066400000000000000000000106751427735150300146010ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt from . import stp class LLC(dpkt.Packet): """802.2 Logical Link Control (LLC) data communication protocol. Attributes: __hdr__ = ( ('dsap', 'B', 0xaa), # Destination Service Access Point ('ssap', 'B', 0xaa), # Source Service Access Point ('ctl', 'B', 3) # Control Byte ) """ __hdr__ = ( ('dsap', 'B', 0xaa), # Destination Service Access Point ('ssap', 'B', 0xaa), # Source Service Access Point ('ctl', 'B', 3) # Control Byte ) @property def is_snap(self): return self.dsap == self.ssap == 0xaa def unpack(self, buf): from .ethernet import Ethernet, ETH_TYPE_IP, ETH_TYPE_IPX dpkt.Packet.unpack(self, buf) if self.is_snap: self.oui, self.type = struct.unpack('>IH', b'\x00' + self.data[:5]) self.data = self.data[5:] try: self.data = Ethernet.get_type(self.type)(self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): pass else: # non-SNAP if self.dsap == 0x06: # SAP_IP self.data = self.ip = Ethernet.get_type(ETH_TYPE_IP)(self.data) elif self.dsap == 0x10 or self.dsap == 0xe0: # SAP_NETWARE{1,2} self.data = self.ipx = Ethernet.get_type(ETH_TYPE_IPX)(self.data) elif self.dsap == 0x42: # SAP_STP self.data = self.stp = stp.STP(self.data) def pack_hdr(self): buf = dpkt.Packet.pack_hdr(self) if self.is_snap: # add SNAP sublayer oui = getattr(self, 'oui', 0) _type = getattr(self, 'type', 0) if not _type and isinstance(self.data, dpkt.Packet): from .ethernet import Ethernet try: _type = Ethernet.get_type_rev(self.data.__class__) except KeyError: pass buf += struct.pack('>IH', oui, _type)[1:] return buf def __len__(self): # add 5 bytes of SNAP header if needed return self.__hdr_len__ + 5 * int(self.is_snap) + len(self.data) def test_llc(): from . import ip from . import ethernet s = (b'\xaa\xaa\x03\x00\x00\x00\x08\x00\x45\x00\x00\x28\x07\x27\x40\x00\x80\x06\x1d' b'\x39\x8d\xd4\x37\x3d\x3f\xf5\xd1\x69\xc0\x5f\x01\xbb\xb2\xd6\xef\x23\x38\x2b' b'\x4f\x08\x50\x10\x42\x04\xac\x17\x00\x00') llc_pkt = LLC(s) ip_pkt = llc_pkt.data assert isinstance(ip_pkt, ip.IP) assert llc_pkt.type == ethernet.ETH_TYPE_IP assert ip_pkt.dst == b'\x3f\xf5\xd1\x69' assert str(llc_pkt) == str(s) assert len(llc_pkt) == len(s) # construction with SNAP header llc_pkt = LLC(ssap=0xaa, dsap=0xaa, data=ip.IP(s[8:])) assert str(llc_pkt) == str(s) # no SNAP llc_pkt = LLC(ssap=6, dsap=6, data=ip.IP(s[8:])) assert isinstance(llc_pkt.data, ip.IP) assert str(llc_pkt) == str(b'\x06\x06\x03' + s[8:]) def test_unpack_sap_ip(): from binascii import unhexlify from . import ip buf_llc = unhexlify( '06' # dsap (SAP_IP) 'aa' # ssap '03' # ctl ) buf_ip = unhexlify( '45' # _v_hl '00' # tos '0014' # len '0000' # id '0000' # off '80' # ttl '06' # p 'd47e' # sum '11111111' # src '22222222' # dst ) buf = buf_llc + buf_ip llc = LLC(buf) assert isinstance(llc.data, ip.IP) def test_unpack_exception_handling(): from binascii import unhexlify buf_llc = unhexlify( 'aa' # dsap (SAP_IP) 'aa' # ssap '03' # ctl '111111' # oui '2222' # type (not valid ethertype) ) llc = LLC(buf_llc) assert not isinstance(llc.data, dpkt.Packet) def test_pack_hdr_invalid_class(): from binascii import unhexlify class InvalidClass(dpkt.Packet): __hdr__ = (('test', 'B', 0x22),) llc = LLC(dsap=0xaa, ssap=0xaa, ctl=3, oui=0x111111, data=InvalidClass()) correct = unhexlify( 'aa' # dsap 'aa' # ssap '03' # ctl '111111' # oui '0000' # type '22' # data in test class header ) assert bytes(llc) == correct dpkt-1.9.8/dpkt/loopback.py000066400000000000000000000047561427735150300156240ustar00rootroot00000000000000# $Id: loopback.py 38 2007-03-17 03:33:16Z dugsong $ # -*- coding: utf-8 -*- """Platform-dependent loopback header.""" # https://wiki.wireshark.org/NullLoopback from __future__ import absolute_import from . import dpkt from . import ethernet from . import ip from . import ip6 class Loopback(dpkt.Packet): """Platform-dependent loopback header. TODO: Longer class information.... Attributes: __hdr__: Header fields of Loopback. TODO. """ __hdr__ = (('family', 'I', 0), ) __byte_order__ = '@' def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.family in (0x02, 0x02000000): self.family = 2 self.data = ip.IP(self.data) elif self.family in (0x18, 0x18000000): self.family = 24 self.data = ip6.IP6(self.data) elif self.family in (0x1c, 0x1c000000): self.family = 28 self.data = ip6.IP6(self.data) elif self.family in (0x1e, 0x1e000000): self.family = 30 self.data = ip6.IP6(self.data) else: self.data = ethernet.Ethernet(self.data) def test_ethernet_unpack(): buf = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x08\x00' hdr = b'\x00\x02\x00\x02' lo = Loopback(hdr + buf) assert lo.family in (0x02000200, 0x00020002) # little endian, big endian assert isinstance(lo.data, ethernet.Ethernet) assert lo.data.src == b'\x07\x08\t\n\x0b\x0c' assert lo.data.dst == b'\x01\x02\x03\x04\x05\x06' def test_ip_unpack(): buf = b'E\x00\x004\xbd\x04@\x00@\x06\x7f\xbd\x7f\x00\x00\x02\x7f\x00\x00\x01' for hdr in (b'\x00\x00\x00\x02', b'\x02\x00\x00\x00'): lo = Loopback(hdr + buf) assert lo.family == 2 assert isinstance(lo.data, ip.IP) assert lo.data.src == b'\x7f\x00\x00\x02' assert lo.data.dst == b'\x7f\x00\x00\x01' def test_ip6_unpack(): import struct buf = (b'\x60\x00\x00\x00\x00\x14\x06\x38\x26\x07\xf8\xb0\x40\x0c\x0c\x03\x00\x00\x00\x00\x00\x00' b'\x00\x1a\x20\x01\x04\x70\xe5\xbf\xde\xad\x49\x57\x21\x74\xe8\x2c\x48\x87') hdr_suffix = b'\x00' * 3 for family in (24, 28, 30): hdr = struct.pack('B', family) + hdr_suffix lo = Loopback(hdr + buf) assert lo.family == family assert isinstance(lo.data, ip6.IP6) assert lo.data.src == b'&\x07\xf8\xb0@\x0c\x0c\x03\x00\x00\x00\x00\x00\x00\x00\x1a' assert lo.data.dst == b' \x01\x04p\xe5\xbf\xde\xadIW!t\xe8,H\x87' dpkt-1.9.8/dpkt/mrt.py000066400000000000000000000061401427735150300146210ustar00rootroot00000000000000# $Id: mrt.py 29 2007-01-26 02:29:07Z jon.oberheide $ # -*- coding: utf-8 -*- """Multi-threaded Routing Toolkit.""" from __future__ import absolute_import from . import dpkt from . import bgp # Multi-threaded Routing Toolkit # http://www.ietf.org/internet-drafts/draft-ietf-grow-mrt-03.txt # MRT Types NULL = 0 START = 1 DIE = 2 I_AM_DEAD = 3 PEER_DOWN = 4 BGP = 5 # Deprecated by BGP4MP RIP = 6 IDRP = 7 RIPNG = 8 BGP4PLUS = 9 # Deprecated by BGP4MP BGP4PLUS_01 = 10 # Deprecated by BGP4MP OSPF = 11 TABLE_DUMP = 12 BGP4MP = 16 BGP4MP_ET = 17 ISIS = 32 ISIS_ET = 33 OSPF_ET = 64 # BGP4MP Subtypes BGP4MP_STATE_CHANGE = 0 BGP4MP_MESSAGE = 1 BGP4MP_ENTRY = 2 BGP4MP_SNAPSHOT = 3 BGP4MP_MESSAGE_32BIT_AS = 4 # Address Family Types AFI_IPv4 = 1 AFI_IPv6 = 2 class MRTHeader(dpkt.Packet): __hdr__ = ( ('ts', 'I', 0), ('type', 'H', 0), ('subtype', 'H', 0), ('len', 'I', 0) ) class TableDump(dpkt.Packet): __hdr__ = ( ('view', 'H', 0), ('seq', 'H', 0), ('prefix', 'I', 0), ('prefix_len', 'B', 0), ('status', 'B', 1), ('originated_ts', 'I', 0), ('peer_ip', 'I', 0), ('peer_as', 'H', 0), ('attr_len', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) plen = self.attr_len l_ = [] while plen > 0: attr = bgp.BGP.Update.Attribute(self.data) self.data = self.data[len(attr):] plen -= len(attr) l_.append(attr) self.attributes = l_ class BGP4MPMessage(dpkt.Packet): __hdr__ = ( ('src_as', 'H', 0), ('dst_as', 'H', 0), ('intf', 'H', 0), ('family', 'H', AFI_IPv4), ('src_ip', 'I', 0), ('dst_ip', 'I', 0) ) class BGP4MPMessage_32(dpkt.Packet): __hdr__ = ( ('src_as', 'I', 0), ('dst_as', 'I', 0), ('intf', 'H', 0), ('family', 'H', AFI_IPv4), ('src_ip', 'I', 0), ('dst_ip', 'I', 0) ) def test_tabledump(): from binascii import unhexlify buf_tabledump = unhexlify( '0001' # view '0002' # seq '00000003' # prefix '04' # prefix_len '05' # status '00000006' # originated_ts '00000007' # peer_ip '0008' # peer_as '0002' # attr_len ) buf_attrs = unhexlify( '01' # flags '01' # type (ORIGIN) '01' # length '02' # Origin.type (INCOMPLETE) ) buf = buf_tabledump + buf_attrs table_dump = TableDump(buf) assert table_dump.view == 1 assert table_dump.seq == 2 assert table_dump.prefix == 3 assert table_dump.prefix_len == 4 assert table_dump.status == 5 assert table_dump.originated_ts == 6 assert table_dump.peer_ip == 7 assert table_dump.peer_as == 8 assert table_dump.attr_len == 2 assert len(table_dump.attributes) == 1 attr = table_dump.attributes[0] assert isinstance(attr, bgp.BGP.Update.Attribute) assert isinstance(attr.data, bgp.BGP.Update.Attribute.Origin) dpkt-1.9.8/dpkt/netbios.py000066400000000000000000000235151427735150300154670ustar00rootroot00000000000000# $Id: netbios.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Network Basic Input/Output System.""" from __future__ import absolute_import import struct from . import dpkt from . import dns from .compat import compat_ord def encode_name(name): """ Return the NetBIOS first-level encoded name. 14.1. FIRST LEVEL ENCODING The first level representation consists of two parts: - NetBIOS name - NetBIOS scope identifier The 16 byte NetBIOS name is mapped into a 32 byte wide field using a reversible, half-ASCII, biased encoding. Each half-octet of the NetBIOS name is encoded into one byte of the 32 byte field. The first half octet is encoded into the first byte, the second half- octet into the second byte, etc. Each 4-bit, half-octet of the NetBIOS name is treated as an 8-bit, right-adjusted, zero-filled binary number. This number is added to value of the ASCII character 'A' (hexadecimal 41). The resulting 8- bit number is stored in the appropriate byte. The following diagram demonstrates this procedure: 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |a b c d|w x y z| ORIGINAL BYTE +-+-+-+-+-+-+-+-+ | | +--------+ +--------+ | | SPLIT THE NIBBLES v v 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |0 0 0 0 a b c d| |0 0 0 0 w x y z| +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ | | + + ADD 'A' | | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |0 1 0 0 0 0 0 1| |0 1 0 0 0 0 0 1| +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ This encoding results in a NetBIOS name being represented as a sequence of 32 ASCII, upper-case characters from the set {A,B,C...N,O,P}. The NetBIOS scope identifier is a valid domain name (without a leading dot). An ASCII dot (2E hexadecimal) and the scope identifier are appended to the encoded form of the NetBIOS name, the result forming a valid domain name. """ l_ = [] for c in struct.pack('16s', name.encode()): c = compat_ord(c) l_.append(chr((c >> 4) + 0x41)) l_.append(chr((c & 0xf) + 0x41)) return ''.join(l_) def decode_name(nbname): """ Return the NetBIOS first-level decoded nbname. """ if len(nbname) != 32: return nbname l_ = [] for i in range(0, 32, 2): l_.append( chr( ((ord(nbname[i]) - 0x41) << 4) | ((ord(nbname[i + 1]) - 0x41) & 0xf) ) ) return ''.join(l_).split('\x00', 1)[0] # RR types NS_A = 0x01 # IP address NS_NS = 0x02 # Name Server NS_NULL = 0x0A # NULL NS_NB = 0x20 # NetBIOS general Name Service NS_NBSTAT = 0x21 # NetBIOS NODE STATUS # RR classes NS_IN = 1 # NBSTAT name flags NS_NAME_G = 0x8000 # group name (as opposed to unique) NS_NAME_DRG = 0x1000 # deregister NS_NAME_CNF = 0x0800 # conflict NS_NAME_ACT = 0x0400 # active NS_NAME_PRM = 0x0200 # permanent # NBSTAT service names nbstat_svcs = { # (service, unique): list of ordered (name prefix, service name) tuples (0x00, 0): [('', 'Domain Name')], (0x00, 1): [('IS~', 'IIS'), ('', 'Workstation Service')], (0x01, 0): [('__MSBROWSE__', 'Master Browser')], (0x01, 1): [('', 'Messenger Service')], (0x03, 1): [('', 'Messenger Service')], (0x06, 1): [('', 'RAS Server Service')], (0x1B, 1): [('', 'Domain Master Browser')], (0x1C, 0): [('INet~Services', 'IIS'), ('', 'Domain Controllers')], (0x1D, 1): [('', 'Master Browser')], (0x1E, 0): [('', 'Browser Service Elections')], (0x1F, 1): [('', 'NetDDE Service')], (0x20, 1): [('Forte_$ND800ZA', 'DCA IrmaLan Gateway Server Service'), ('', 'File Server Service')], (0x21, 1): [('', 'RAS Client Service')], (0x22, 1): [('', 'Microsoft Exchange Interchange(MSMail Connector)')], (0x23, 1): [('', 'Microsoft Exchange Store')], (0x24, 1): [('', 'Microsoft Exchange Directory')], (0x2B, 1): [('', 'Lotus Notes Server Service')], (0x2F, 0): [('IRISMULTICAST', 'Lotus Notes')], (0x30, 1): [('', 'Modem Sharing Server Service')], (0x31, 1): [('', 'Modem Sharing Client Service')], (0x33, 0): [('IRISNAMESERVER', 'Lotus Notes')], (0x43, 1): [('', 'SMS Clients Remote Control')], (0x44, 1): [('', 'SMS Administrators Remote Control Tool')], (0x45, 1): [('', 'SMS Clients Remote Chat')], (0x46, 1): [('', 'SMS Clients Remote Transfer')], (0x4C, 1): [('', 'DEC Pathworks TCPIP service on Windows NT')], (0x52, 1): [('', 'DEC Pathworks TCPIP service on Windows NT')], (0x87, 1): [('', 'Microsoft Exchange MTA')], (0x6A, 1): [('', 'Microsoft Exchange IMC')], (0xBE, 1): [('', 'Network Monitor Agent')], (0xBF, 1): [('', 'Network Monitor Application')] } def node_to_service_name(name_service_flags): name, service, flags = name_service_flags try: unique = int(flags & NS_NAME_G == 0) for namepfx, svcname in nbstat_svcs[(service, unique)]: if name.startswith(namepfx): return svcname except KeyError: pass return '' class NS(dns.DNS): """ NetBIOS Name Service. RFC1002: https://tools.ietf.org/html/rfc1002 """ class Q(dns.DNS.Q): pass class RR(dns.DNS.RR): """NetBIOS resource record. RFC1001: 14. REPRESENTATION OF NETBIOS NAMES NetBIOS names as seen across the client interface to NetBIOS are exactly 16 bytes long. Within the NetBIOS-over-TCP protocols, a longer representation is used. There are two levels of encoding. The first level maps a NetBIOS name into a domain system name. The second level maps the domain system name into the "compressed" representation required for interaction with the domain name system. Except in one packet, the second level representation is the only NetBIOS name representation used in NetBIOS-over-TCP packet formats. The exception is the RDATA field of a NODE STATUS RESPONSE packet. """ _node_name_struct = struct.Struct('>15s B H') _node_name_len = _node_name_struct.size def unpack_rdata(self, buf, off): if self.type == NS_A: self.ip = self.rdata elif self.type == NS_NBSTAT: num_names = compat_ord(self.rdata[0]) self.nodenames = [ self._node_name_struct.unpack_from( self.rdata, 1+idx*self._node_name_len ) for idx in range(num_names) ] # XXX - skip stats class Session(dpkt.Packet): """NetBIOS Session Service.""" __hdr__ = ( ('type', 'B', 0), ('flags', 'B', 0), ('len', 'H', 0) ) SSN_MESSAGE = 0 SSN_REQUEST = 1 SSN_POSITIVE = 2 SSN_NEGATIVE = 3 SSN_RETARGET = 4 SSN_KEEPALIVE = 5 class Datagram(dpkt.Packet): """NetBIOS Datagram Service.""" __hdr__ = ( ('type', 'B', 0), ('flags', 'B', 0), ('id', 'H', 0), ('src', 'I', 0), ('sport', 'H', 0), ('len', 'H', 0), ('off', 'H', 0) ) DGRAM_UNIQUE = 0x10 DGRAM_GROUP = 0x11 DGRAM_BROADCAST = 0x12 DGRAM_ERROR = 0x13 DGRAM_QUERY = 0x14 DGRAM_POSITIVE = 0x15 DGRAM_NEGATIVE = 0x16 def test_encode_name(): assert encode_name('The NetBIOS name') == 'FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF' # rfc1002 assert encode_name('FRED ') == 'EGFCEFEECACACACACACACACACACACACA' # https://github.com/kbandla/dpkt/issues/458 assert encode_name('*') == 'CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' def test_decode_name(): assert decode_name('FEGIGFCAEOGFHEECEJEPFDCAGOGBGNGF') == 'The NetBIOS name' # original botched example from rfc1001 assert decode_name('FEGHGFCAEOGFHEECEJEPFDCAHEGBGNGF') == 'Tge NetBIOS tame' assert decode_name('CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') == '*' # decode a name which is not 32 chars long assert decode_name('CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB') == 'CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB' def test_node_to_service_name(): svcname = node_to_service_name(("ISS", 0x00, 0x0800)) assert svcname == "Workstation Service" def test_node_to_service_name_keyerror(): svcname = node_to_service_name(("ISS", 0xff, 0x0800)) assert svcname == "" def test_rr(): import pytest from binascii import unhexlify rr = NS.RR() with pytest.raises(NotImplementedError): len(rr) buf = unhexlify(''.join([ '01', # A record '0001', # DNS_IN '00000000', # TTL '0000', # rlen ])) rr.unpack_rdata(buf, 0) assert rr.ip == rr.rdata def test_rr_nbstat(): from binascii import unhexlify buf = unhexlify(''.join([ '41' * 1025, # Name '0033', # NS_NBSTAT '0001', # DNS_IN '00000000', # TTL '0004', # rlen ])) rdata = ( b'\x02' # NUM_NAMES b'ABCDEFGHIJKLMNO\x2f\x01\x02' b'PQRSTUVWXYZABCD\x43\x03\x04' ) rr = NS.RR( type=NS_NBSTAT, rdata=rdata, ) assert rr.type == NS_NBSTAT rr.unpack_rdata(buf, 0) assert rr.nodenames == [ (b'ABCDEFGHIJKLMNO', 0x2f, 0x0102), (b'PQRSTUVWXYZABCD', 0x43, 0x0304), ] def test_ns(): from binascii import unhexlify ns = NS() correct = unhexlify( '0000' '0100' '0000000000000000' ) assert bytes(ns) == correct dpkt-1.9.8/dpkt/netflow.py000066400000000000000000000330411427735150300154750ustar00rootroot00000000000000# $Id: netflow.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Cisco Netflow.""" from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt from .compat import compat_izip class NetflowBase(dpkt.Packet): """Base class for Cisco Netflow packets. NetFlow is a feature that was introduced on Cisco routers around 1996 that provides the ability to collect IP network traffic as it enters or exits an interface. By analyzing the data provided by NetFlow, a network administrator can determine things such as the source and destination of traffic, class of service, and the causes of congestion. https://www.ciscopress.com/articles/article.asp?p=2812391&seqNum=3 Attributes: __hdr__: Header fields of NetflowBase. """ __hdr__ = ( ('version', 'H', 1), ('count', 'H', 0), ('sys_uptime', 'I', 0), ('unix_sec', 'I', 0), ('unix_nsec', 'I', 0) ) def __len__(self): return self.__hdr_len__ + (len(self.data[0]) * self.count) def __bytes__(self): # for now, don't try to enforce any size limits self.count = len(self.data) return self.pack_hdr() + b''.join(map(bytes, self.data)) def unpack(self, buf): dpkt.Packet.unpack(self, buf) buf = self.data l_ = [] while buf: flow = self.NetflowRecord(buf) l_.append(flow) buf = buf[len(flow):] self.data = l_ class NetflowRecordBase(dpkt.Packet): """Base class for netflow v1-v7 netflow records. TODO: Longer class information.... Attributes: __hdr__: Header fields of NetflowRecordBase. TODO. """ # performance optimizations def __len__(self): # don't bother with data return self.__hdr_len__ def __bytes__(self): # don't bother with data return self.pack_hdr() def unpack(self, buf): # don't bother with data for k, v in compat_izip(self.__hdr_fields__, struct.unpack(self.__hdr_fmt__, buf[:self.__hdr_len__])): setattr(self, k, v) self.data = b"" class Netflow1(NetflowBase): """Netflow Version 1. TODO: Longer class information.... Attributes: __hdr__: Header fields of Netflow Version 1. TODO. """ class NetflowRecord(NetflowBase.NetflowRecordBase): """Netflow v1 flow record. TODO: Longer class information.... Attributes: __hdr__: Header fields of Netflow Version 1 flow record. TODO. """ __hdr__ = ( ('src_addr', 'I', 0), ('dst_addr', 'I', 0), ('next_hop', 'I', 0), ('input_iface', 'H', 0), ('output_iface', 'H', 0), ('pkts_sent', 'I', 0), ('bytes_sent', 'I', 0), ('start_time', 'I', 0), ('end_time', 'I', 0), ('src_port', 'H', 0), ('dst_port', 'H', 0), ('pad1', 'H', 0), ('ip_proto', 'B', 0), ('tos', 'B', 0), ('tcp_flags', 'B', 0), ('pad2', 'B', 0), ('pad3', 'H', 0), ('reserved', 'I', 0) ) # FYI, versions 2-4 don't appear to have ever seen the light of day. class Netflow5(NetflowBase): """Netflow Version 5. Popular NetFlow version on many routers from different vendors. Limited to IPv4 flows. Attributes: __hdr__: Header fields of Netflow Version 5. """ __hdr__ = NetflowBase.__hdr__ + ( ('flow_sequence', 'I', 0), ('engine_type', 'B', 0), ('engine_id', 'B', 0), ('reserved', 'H', 0), ) class NetflowRecord(NetflowBase.NetflowRecordBase): """Netflow v5 flow record. Attributes: __hdr__: Header fields of Netflow Version 5 flow record. """ __hdr__ = ( ('src_addr', 'I', 0), ('dst_addr', 'I', 0), ('next_hop', 'I', 0), ('input_iface', 'H', 0), ('output_iface', 'H', 0), ('pkts_sent', 'I', 0), ('bytes_sent', 'I', 0), ('start_time', 'I', 0), ('end_time', 'I', 0), ('src_port', 'H', 0), ('dst_port', 'H', 0), ('pad1', 'B', 0), ('tcp_flags', 'B', 0), ('ip_proto', 'B', 0), ('tos', 'B', 0), ('src_as', 'H', 0), ('dst_as', 'H', 0), ('src_mask', 'B', 0), ('dst_mask', 'B', 0), ('pad2', 'H', 0), ) class Netflow6(NetflowBase): """Netflow Version 6. (Obsolete.) No longer supported by Cisco, but may be found in the field. Attributes: __hdr__: Header fields of Netflow Version 6. """ __hdr__ = Netflow5.__hdr__ class NetflowRecord(NetflowBase.NetflowRecordBase): """Netflow v6 flow record. Attributes: __hdr__: Header fields of Netflow Version 6 flow record. """ __hdr__ = ( ('src_addr', 'I', 0), ('dst_addr', 'I', 0), ('next_hop', 'I', 0), ('input_iface', 'H', 0), ('output_iface', 'H', 0), ('pkts_sent', 'I', 0), ('bytes_sent', 'I', 0), ('start_time', 'I', 0), ('end_time', 'I', 0), ('src_port', 'H', 0), ('dst_port', 'H', 0), ('pad1', 'B', 0), ('tcp_flags', 'B', 0), ('ip_proto', 'B', 0), ('tos', 'B', 0), ('src_as', 'H', 0), ('dst_as', 'H', 0), ('src_mask', 'B', 0), ('dst_mask', 'B', 0), ('in_encaps', 'B', 0), ('out_encaps', 'B', 0), ('peer_nexthop', 'I', 0), ) class Netflow7(NetflowBase): """Netflow Version 7. (Obsolete.) Like version 5, with a source router field. Attributes: __hdr__: Header fields of Netflow Version 7. """ __hdr__ = NetflowBase.__hdr__ + ( ('flow_sequence', 'I', 0), ('reserved', 'I', 0), ) class NetflowRecord(NetflowBase.NetflowRecordBase): """Netflow v6 flow record. TODO: Longer class information.... Attributes: __hdr__: Header fields of Netflow Version 6 flow record. TODO. """ __hdr__ = ( ('src_addr', 'I', 0), ('dst_addr', 'I', 0), ('next_hop', 'I', 0), ('input_iface', 'H', 0), ('output_iface', 'H', 0), ('pkts_sent', 'I', 0), ('bytes_sent', 'I', 0), ('start_time', 'I', 0), ('end_time', 'I', 0), ('src_port', 'H', 0), ('dst_port', 'H', 0), ('flags', 'B', 0), ('tcp_flags', 'B', 0), ('ip_proto', 'B', 0), ('tos', 'B', 0), ('src_as', 'H', 0), ('dst_as', 'H', 0), ('src_mask', 'B', 0), ('dst_mask', 'B', 0), ('pad2', 'H', 0), ('router_sc', 'I', 0), ) # No support for v8 or v9 yet. def test_net_flow_v1_unpack(): from binascii import unhexlify __sample_v1 = unhexlify( '00010018677a613c4200fc1c24930870ac012057c0a863f70a0002010003000a0000000100000228677a372c677a372c5c1b0050ac01112c10000' '0000004001bac011853ac18d9aac0a832020003001900000001000005dc677a377c677a377cd8e30050ac01062c100000000004001bac011418ac' '188dcdc0a832660003000700000001000005dc677a3790677a37908a81176fac0106361000000000040003ac0f2724ac01e51dc0a832060004001' 'b0000000100000228677a3a38677a3a38a3511236ac2906fd180000000004001bac011645ac23178ec0a832060003001b0000000100000228677a' '3a4c677a3a4cc9ff0050ac1f0686020000000003001bac0d09ffac019995c0a832060004001b00000001000005dc677a3a58677a3a58ee390017a' 'c0106de1000000000040003ac0e4ad8ac01ae2fc0a832060004001b00000001000005dc677a3a68677a3a68b36e0015ac01068110000000000400' '1bac012338ac01d92ac0a832060003001b00000001000005dc677a3a74677a3a7400008350ac2101ab100000000003001bac0a6037ac2a934ac0a' '832060004001b00000001000005dc677a3a74677a3a7400000000ac0132a91000000000040007ac0a471fac01fd4ac0a832060004001b00000001' '00000028677a3a88677a3a8821996987ac1e067e020000000003001bac0128c9ac0142c4c0a83202000300190000000100000028677a3a88677a3' 'a887d360050ac0106fe100000000004001bac0b08e8ac0146e2c0a832020004001900000001000005dc677a3a9c677a3a9c60696987ac01063b10' '0000000004001bac011d24ac3cf0c3c0a832060003001b00000001000005dc677a3a9c677a3a9c46320014ac0106731800000000040003ac0b115' '1ac01de06c0a832060004001b00000001000005dc677a3ab0677a3ab0ef231a2bac2906e9100000000004001bac0c52d9ac016fe8c0a832020004' '001900000001000005dc677a3ac4677a3ac4136e006eac1906a81000000000030019ac013dddac017deec0a832660003000700000001000000286' '77a3ac4677a3ac40000dcbbac0101d3100000000004001bac0f28d1ac01cca5c0a832060004001b00000001000005dc677a3ad8677a3ad8c57317' '6fac1906231800000000030007ac0a855bc0a8636e0a0002010004000a00000001000005dc677a3ae4677a3ae4bf6c0050ac0106cf10000000000' '40007ac01301fac182145c0a832660003000700000001000005dc677a3b00677a3b00119504bec0a806ea100000000003000aac0130b6ac1ef4aa' 'c0a832060003001b00000001000005dc677a3b34677a3b3488640017ac01061f100000000004001bac01235fac1eb009c0a832060003001b00000' '001000005dc677a3b48677a3b4881530050ac20064e100000000003001bac0104d9ac019463c0a832060003001b0000000100000228677a3b5c67' '7a3b5c55100050ac010650180000000004001bac013caeac2aac21c0a832060003001b00000001000000fa677a3b84677a3b840ce70050ac0111f' 'd100000000004001bac011f1fac17ed69c0a832020003001900000001000005dc677a3b98677a3b98ba170016ac01067c1000000000030007' ) nf = Netflow1(__sample_v1) assert len(nf.data) == 24 # print repr(nfv1) def test_net_flow_v5_unpack(): from binascii import unhexlify buf_nf5_header = unhexlify( '0005001db5fac9d03a0b4142265677de9b73763100010000' ) buf_nf5_records = list(map(unhexlify, ( 'ac0a86a6ac01aaf7c0a83232027100690000000100000228b5fa8114b5fa811435320050000006000000000000000000', 'ac019144ac1443e4c0a83216006902710000000100000028b5fa9bbdb5fa9bbd005085d7000006000000000000000000', 'ac17e2d7ac018c56c0a832320271006900000001000005dcb5fa6fb8b5fa6fb876e8176f000006000000000000000000', 'ac0ef2e5ac0191b2c0a832320271006900000001000000fab5fa81eeb5fa81eed0eb0015000006000000000000000000', 'ac0a436aac29a7090a000201027100db0000000100000228b5fa8592b5fa85928cb00035000006000000000000000000', 'ac01963dac151aa8c0a832160069027100000001000005dcb5fa86e0b5fa86e0b4e700c2000006000000000000000000', 'ac0156d1ac018615c0a832320271006900000001000005dcb5fa7d3ab5fa7d3a5b510050000006000000000000000000', 'ac32f1b1ac2919ca0a000201027100db00000001000005dcb5fa83c3b5fa83c3162c0015000006000000000000000000', 'ac0c4134ac019a7ac0a832320271006900000001000005dcb5fa8da7b5fa8da717330015000006000000000000000000', 'ac1ed284ac29d8d20a000201027100db00000001000005dcb5fa8e97b5fa8e97372a176f000006000000000000000000', 'ac01854aac2011fcc0a83216006902710000000100000228b5fa8834b5fa8834f5dd008f000006000000000000000000', 'ac010480ac3c5b6e0a000201027100db00000001000005dcb5fa9d72b5fa9d7273240016000006000000000000000000', 'ac01b94aac22c9d7c0a83216006902710000000100000028b5fa9072b5fa90720f8d00c2000006000000000000000000', 'ac2aa310ac01b419c0a83232027100690000000100000028b5fa9203b5fa920370660015000006000000000000000000', 'ac01ab6fac1e7f69c0a832160069027100000001000005dcb5fa937fb5fa937f00500b98000006000000000000000000', 'ac0c0aeaac01a115c0a832320271006900000001000005dcb5fa79cfb5fa79cf5b3317e0000006000000000000000000', 'ac01bbb3ac29758c0a000201006900db00000001000000fab5fa9433b5fa943300501eca000006000000000000000000', 'ac0f4a60ac01ab94c0a83232027100690000000100000228b5fa875bb5fa875b9ad62fab000006000000000000000000', 'ac2a0f93ac01b8a3c0a83232027100690000000100000028b5fa89bbb5fa89bb6ee10050000006000000000000000000', 'ac0193a1ac16800cc0a83216006902710000000100000028b5fa8726b5fa872600000000000001000000000000000000', 'ac01835aac1f52cdc0a832160069027100000001000005dcb5fa900db5fa900df72a008a000006000000000000000000', 'ac0ce0adac01a856c0a832320271006900000001000005dcb5fa9cf6b5fa9cf6e57c1a2b000006000000000000000000', 'ac1ecc54ac3c78260a000201027100db00000001000005dcb5fa80eab5fa80ea0000000000002f000000000000000000', 'ac01bb18ac017c7ac0a832160069027100000001000000fab5fa8870b5fa887000500b7d000006000000000000000000', 'ac170e72ac018fddc0a83232027100690000000100000228b5fa89f7b5fa89f70df7008a000006000000000000000000', 'ac0abb04ac3cb0150a000201027100db00000001000005dcb5fa90a9b5fa90a99cd0008f000006000000000000000000', 'ac0a7a3fac2903c80a000201027100db00000001000005dcb5fa7565b5fa7565eea60050000006000000000000000000', 'ac01b505c0a8639f0a000201006900db00000001000005dcb5fa7bc7b5fa7bc7005086a9000006000000000000000000', 'ac32a51bac2930bf0a000201027100db00000001000000fab5fa9b5ab5fa9b5a43f917e0000006000000000000000000', ))) buf_input = buf_nf5_header + b''.join(buf_nf5_records) nf = Netflow5(buf_input) assert nf.version == 5 assert nf.count == 29 assert nf.sys_uptime == 3053111760 assert nf.unix_sec == 973816130 assert len(nf) == len(buf_input) assert bytes(nf) == buf_input assert len(nf.data) == 29 for idx, record in enumerate(nf.data): assert bytes(record) == buf_nf5_records[idx] assert len(record) == 48 dpkt-1.9.8/dpkt/ntp.py000066400000000000000000000041031427735150300146150ustar00rootroot00000000000000# $Id: ntp.py 48 2008-05-27 17:31:15Z yardley $ # -*- coding: utf-8 -*- """Network Time Protocol.""" from __future__ import print_function from . import dpkt # NTP v4 # Leap Indicator (LI) Codes NO_WARNING = 0 LAST_MINUTE_61_SECONDS = 1 LAST_MINUTE_59_SECONDS = 2 ALARM_CONDITION = 3 # Mode Codes RESERVED = 0 SYMMETRIC_ACTIVE = 1 SYMMETRIC_PASSIVE = 2 CLIENT = 3 SERVER = 4 BROADCAST = 5 CONTROL_MESSAGE = 6 PRIVATE = 7 class NTP(dpkt.Packet): """Network Time Protocol. The Network Time Protocol (NTP) is a networking protocol for clock synchronization between computer systems over packet-switched, variable-latency data networks. In operation since before 1985, NTP is one of the oldest Internet protocols in current use. NTP was designed by David L. Mills of the University of Delaware. Attributes: __hdr__: Header fields of NTP. TODO. """ __hdr__ = ( ('flags', 'B', 0), ('stratum', 'B', 0), ('interval', 'B', 0), ('precision', 'B', 0), ('delay', 'I', 0), ('dispersion', 'I', 0), ('id', '4s', 0), ('update_time', '8s', 0), ('originate_time', '8s', 0), ('receive_time', '8s', 0), ('transmit_time', '8s', 0) ) __bit_fields__ = { 'flags': ( ('li', 2), # leap indicator, 2 hi bits ('v', 3), # version, 3 bits ('mode', 3), # mode, 3 lo bits ) } __s = (b'\x24\x02\x04\xef\x00\x00\x00\x84\x00\x00\x33\x27\xc1\x02\x04\x02\xc8\x90\xec\x11\x22\xae' b'\x07\xe5\xc8\x90\xf9\xd9\xc0\x7e\x8c\xcd\xc8\x90\xf9\xd9\xda\xc5\xb0\x78\xc8\x90\xf9\xd9\xda\xc6\x8a\x93') def test_ntp_pack(): n = NTP(__s) assert (__s == bytes(n)) def test_ntp_unpack(): n = NTP(__s) assert (n.li == NO_WARNING) assert (n.v == 4) assert (n.mode == SERVER) assert (n.stratum == 2) assert (n.id == b'\xc1\x02\x04\x02') # test get/set functions n.li = ALARM_CONDITION n.v = 3 n.mode = CLIENT assert (n.li == ALARM_CONDITION) assert (n.v == 3) assert (n.mode == CLIENT) dpkt-1.9.8/dpkt/ospf.py000066400000000000000000000025121427735150300147650ustar00rootroot00000000000000# $Id: ospf.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Open Shortest Path First.""" from __future__ import absolute_import from . import dpkt AUTH_NONE = 0 AUTH_PASSWORD = 1 AUTH_CRYPTO = 2 class OSPF(dpkt.Packet): """Open Shortest Path First. TODO: Longer class information.... Attributes: __hdr__: Header fields of OSPF. TODO. """ __hdr__ = ( ('v', 'B', 0), ('type', 'B', 0), ('len', 'H', 0), ('router', 'I', 0), ('area', 'I', 0), ('sum', 'H', 0), ('atype', 'H', 0), ('auth', '8s', b'') ) def __bytes__(self): if not self.sum: self.sum = dpkt.in_cksum(dpkt.Packet.__bytes__(self)) return dpkt.Packet.__bytes__(self) def test_creation(): ospf = OSPF() assert ospf.v == 0 assert ospf.type == 0 assert ospf.len == 0 assert ospf.router == 0 assert ospf.area == 0 assert ospf.sum == 0 assert ospf.atype == 0 assert ospf.auth == b'' # sum is 0, so it will be recalculated assert bytes(ospf) == b''.join([ b'\x00' * 12, b'\xff\xff', b'\x00' * 10 ]) ospf.sum = 0x1234 # sum is not 0, so it will be used assert bytes(ospf) == b''.join([ b'\x00' * 12, b'\x12\x34', b'\x00' * 10 ]) dpkt-1.9.8/dpkt/pcap.py000066400000000000000000000445501427735150300147510ustar00rootroot00000000000000# $Id: pcap.py 77 2011-01-06 15:59:38Z dugsong $ # -*- coding: utf-8 -*- """Libpcap file format.""" from __future__ import print_function from __future__ import absolute_import import sys import time from decimal import Decimal from . import dpkt from .compat import intround # big endian magics TCPDUMP_MAGIC = 0xa1b2c3d4 TCPDUMP_MAGIC_NANO = 0xa1b23c4d MODPCAP_MAGIC = 0xa1b2cd34 # little endian magics PMUDPCT_MAGIC = 0xd4c3b2a1 PMUDPCT_MAGIC_NANO = 0x4d3cb2a1 PACPDOM_MAGIC = 0x34cdb2a1 PCAP_VERSION_MAJOR = 2 PCAP_VERSION_MINOR = 4 # see http://www.tcpdump.org/linktypes.html for explanations 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 DLT_PFSYNC = 18 DLT_PPP_SERIAL = 50 DLT_PPP_ETHER = 51 DLT_ATM_RFC1483 = 100 DLT_RAW = 101 DLT_C_HDLC = 104 DLT_IEEE802_11 = 105 DLT_FRELAY = 107 DLT_LOOP = 108 DLT_LINUX_SLL = 113 DLT_LTALK = 114 DLT_PFLOG = 117 DLT_PRISM_HEADER = 119 DLT_IP_OVER_FC = 122 DLT_SUNATM = 123 DLT_IEEE802_11_RADIO = 127 DLT_ARCNET_LINUX = 129 DLT_APPLE_IP_OVER_IEEE1394 = 138 DLT_MTP2_WITH_PHDR = 139 DLT_MTP2 = 140 DLT_MTP3 = 141 DLT_SCCP = 142 DLT_DOCSIS = 143 DLT_LINUX_IRDA = 144 DLT_USER0 = 147 DLT_USER1 = 148 DLT_USER2 = 149 DLT_USER3 = 150 DLT_USER4 = 151 DLT_USER5 = 152 DLT_USER6 = 153 DLT_USER7 = 154 DLT_USER8 = 155 DLT_USER9 = 156 DLT_USER10 = 157 DLT_USER11 = 158 DLT_USER12 = 159 DLT_USER13 = 160 DLT_USER14 = 161 DLT_USER15 = 162 DLT_IEEE802_11_RADIO_AVS = 163 DLT_BACNET_MS_TP = 165 DLT_PPP_PPPD = 166 DLT_GPRS_LLC = 169 DLT_GPF_T = 170 DLT_GPF_F = 171 DLT_LINUX_LAPD = 177 DLT_BLUETOOTH_HCI_H4 = 187 DLT_USB_LINUX = 189 DLT_PPI = 192 DLT_IEEE802_15_4 = 195 DLT_SITA = 196 DLT_ERF = 197 DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 201 DLT_AX25_KISS = 202 DLT_LAPD = 203 DLT_PPP_WITH_DIR = 204 DLT_C_HDLC_WITH_DIR = 205 DLT_FRELAY_WITH_DIR = 206 DLT_IPMB_LINUX = 209 DLT_IEEE802_15_4_NONASK_PHY = 215 DLT_USB_LINUX_MMAPPED = 220 DLT_FC_2 = 224 DLT_FC_2_WITH_FRAME_DELIMS = 225 DLT_IPNET = 226 DLT_CAN_SOCKETCAN = 227 DLT_IPV4 = 228 DLT_IPV6 = 229 DLT_IEEE802_15_4_NOFCS = 230 DLT_DBUS = 231 DLT_DVB_CI = 235 DLT_MUX27010 = 236 DLT_STANAG_5066_D_PDU = 237 DLT_NFLOG = 239 DLT_NETANALYZER = 240 DLT_NETANALYZER_TRANSPARENT = 241 DLT_IPOIB = 242 DLT_MPEG_2_TS = 243 DLT_NG40 = 244 DLT_NFC_LLCP = 245 DLT_INFINIBAND = 247 DLT_SCTP = 248 DLT_USBPCAP = 249 DLT_RTAC_SERIAL = 250 DLT_BLUETOOTH_LE_LL = 251 DLT_NETLINK = 253 DLT_BLUETOOTH_LINUX_MONITOR = 253 DLT_BLUETOOTH_BREDR_BB = 255 DLT_BLUETOOTH_LE_LL_WITH_PHDR = 256 DLT_PROFIBUS_DL = 257 DLT_PKTAP = 258 DLT_EPON = 259 DLT_IPMI_HPM_2 = 260 DLT_ZWAVE_R1_R2 = 261 DLT_ZWAVE_R3 = 262 DLT_WATTSTOPPER_DLM = 263 DLT_ISO_14443 = 264 DLT_LINUX_SLL2 = 276 if sys.platform.find('openbsd') != -1: DLT_LOOP = 12 DLT_RAW = 14 else: DLT_LOOP = 108 DLT_RAW = 12 dltoff = {DLT_NULL: 4, DLT_EN10MB: 14, DLT_IEEE802: 22, DLT_ARCNET: 6, DLT_SLIP: 16, DLT_PPP: 4, DLT_FDDI: 21, DLT_PFLOG: 48, DLT_PFSYNC: 4, DLT_LOOP: 4, DLT_LINUX_SLL: 16, DLT_LINUX_SLL2: 20} class PktHdr(dpkt.Packet): """pcap packet header. TODO: Longer class information.... Attributes: __hdr__: Header fields of pcap header. TODO. """ __hdr__ = ( ('tv_sec', 'I', 0), ('tv_usec', 'I', 0), ('caplen', 'I', 0), ('len', 'I', 0), ) class PktModHdr(dpkt.Packet): """modified pcap packet header. https://wiki.wireshark.org/Development/LibpcapFileFormat#modified-pcap TODO: Longer class information.... Attributes: __hdr__: Header fields of pcap header. TODO. """ __hdr__ = ( ('tv_sec', 'I', 0), ('tv_usec', 'I', 0), ('caplen', 'I', 0), ('len', 'I', 0), ('ifindex', 'I', 0), ('protocol', 'H', 0), ('pkt_type', 'B', 0), ('pad', 'B', 0), ) class LEPktHdr(PktHdr): __byte_order__ = '<' class LEPktModHdr(PktModHdr): __byte_order__ = '<' MAGIC_TO_PKT_HDR = { TCPDUMP_MAGIC: PktHdr, TCPDUMP_MAGIC_NANO: PktHdr, MODPCAP_MAGIC: PktModHdr, PMUDPCT_MAGIC: LEPktHdr, PMUDPCT_MAGIC_NANO: LEPktHdr, PACPDOM_MAGIC: LEPktModHdr } class FileHdr(dpkt.Packet): """pcap file header. TODO: Longer class information.... Attributes: __hdr__: Header fields of pcap file header. TODO. """ __hdr__ = ( ('magic', 'I', TCPDUMP_MAGIC), ('v_major', 'H', PCAP_VERSION_MAJOR), ('v_minor', 'H', PCAP_VERSION_MINOR), ('thiszone', 'I', 0), ('sigfigs', 'I', 0), ('snaplen', 'I', 1500), ('linktype', 'I', 1), ) class LEFileHdr(FileHdr): __byte_order__ = '<' class Writer(object): """Simple pcap dumpfile writer. TODO: Longer class information.... Attributes: __hdr__: Header fields of simple pcap dumpfile writer. TODO. """ __le = sys.byteorder == 'little' def __init__(self, fileobj, snaplen=1500, linktype=DLT_EN10MB, nano=False): self.__f = fileobj self._precision = 9 if nano else 6 self._precision_multiplier = 10**self._precision magic = TCPDUMP_MAGIC_NANO if nano else TCPDUMP_MAGIC if self.__le: fh = LEFileHdr(snaplen=snaplen, linktype=linktype, magic=magic) self._PktHdr = LEPktHdr() else: fh = FileHdr(snaplen=snaplen, linktype=linktype, magic=magic) self._PktHdr = PktHdr() self._pack_hdr = self._PktHdr._pack_hdr self.__f.write(bytes(fh)) def writepkt(self, pkt, ts=None): """Write single packet and optional timestamp to file. Args: pkt: `bytes` will be called on this and written to file. ts (float): Timestamp in seconds. Defaults to current time. """ if ts is None: ts = time.time() self.writepkt_time(bytes(pkt), ts) def writepkt_time(self, pkt, ts): """Write single packet and its timestamp to file. Args: pkt (bytes): Some `bytes` to write to the file ts (float): Timestamp in seconds """ n = len(pkt) sec = int(ts) usec = intround(ts % 1 * self._precision_multiplier) ph = self._pack_hdr(sec, usec, n, n) self.__f.write(ph + pkt) def writepkts(self, pkts): """Write an iterable of packets to file. Timestamps should be in seconds. Packets must be of type `bytes` as they will not be cast. Args: pkts: iterable containing (ts, pkt) """ fd = self.__f pack_hdr = self._pack_hdr precision_multiplier = self._precision_multiplier for ts, pkt in pkts: n = len(pkt) sec = int(ts) usec = intround(ts % 1 * precision_multiplier) ph = pack_hdr(sec, usec, n, n) fd.write(ph + pkt) def close(self): self.__f.close() class Reader(object): """Simple pypcap-compatible pcap file reader. TODO: Longer class information.... Attributes: __hdr__: Header fields of simple pypcap-compatible pcap file reader. TODO. """ def __init__(self, fileobj): self.name = getattr(fileobj, 'name', '<%s>' % fileobj.__class__.__name__) self.__f = fileobj buf = self.__f.read(FileHdr.__hdr_len__) self.__fh = FileHdr(buf) # save magic magic = self.__fh.magic if magic in (PMUDPCT_MAGIC, PMUDPCT_MAGIC_NANO, PACPDOM_MAGIC): self.__fh = LEFileHdr(buf) if magic not in MAGIC_TO_PKT_HDR: raise ValueError('invalid tcpdump header') self.__ph = MAGIC_TO_PKT_HDR[magic] if self.__fh.linktype in dltoff: self.dloff = dltoff[self.__fh.linktype] else: self.dloff = 0 self._divisor = Decimal('1E9') if magic in (TCPDUMP_MAGIC_NANO, PMUDPCT_MAGIC_NANO) else 1E6 self.snaplen = self.__fh.snaplen self.filter = '' self.__iter = iter(self) @property def fd(self): return self.__f.fileno() def fileno(self): return self.fd def datalink(self): return self.__fh.linktype def setfilter(self, value, optimize=1): raise NotImplementedError def readpkts(self): return list(self) def __next__(self): return next(self.__iter) next = __next__ # Python 2 compat def dispatch(self, cnt, callback, *args): """Collect and process packets with a user callback. Return the number of packets processed, or 0 for a savefile. Arguments: cnt -- number of packets to process; or 0 to process all packets until EOF callback -- function with (timestamp, pkt, *args) prototype *args -- optional arguments passed to callback on execution """ processed = 0 if cnt > 0: for _ in range(cnt): try: ts, pkt = next(iter(self)) except StopIteration: break callback(ts, pkt, *args) processed += 1 else: for ts, pkt in self: callback(ts, pkt, *args) processed += 1 return processed def loop(self, callback, *args): self.dispatch(0, callback, *args) def __iter__(self): while 1: buf = self.__f.read(self.__ph.__hdr_len__) if not buf: break hdr = self.__ph(buf) buf = self.__f.read(hdr.caplen) yield (hdr.tv_sec + (hdr.tv_usec / self._divisor), buf) class UniversalReader(object): """ Universal pcap reader for the libpcap and pcapng file formats """ def __new__(cls, fileobj): try: pcap = Reader(fileobj) except ValueError as e1: fileobj.seek(0) try: from . import pcapng pcap = pcapng.Reader(fileobj) except ValueError as e2: raise ValueError('unknown pcap format; libpcap error: %s, pcapng error: %s' % (e1, e2)) return pcap ################################################################################ # TESTS # ################################################################################ class TryExceptException: def __init__(self, exception_type, msg=''): self.exception_type = exception_type self.msg = msg def __call__(self, f, *args, **kwargs): def wrapper(*args, **kwargs): try: f() except self.exception_type as e: if self.msg: assert str(e) == self.msg else: raise Exception("There should have been an Exception raised") return wrapper @TryExceptException(Exception, msg='There should have been an Exception raised') def test_TryExceptException(): """Check that we can catch a function which does not throw an exception when it is supposed to""" @TryExceptException(NotImplementedError) def fun(): pass try: fun() except Exception as e: raise e def test_pcap_endian(): be = b'\xa1\xb2\xc3\xd4\x00\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x01' le = b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00' befh = FileHdr(be) lefh = LEFileHdr(le) assert (befh.linktype == lefh.linktype) class TestData(): pcap = ( # full libpcap file with one packet 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' b'\xb2\x67\x4a\x42\xae\x91\x07\x00\x46\x00\x00\x00\x46\x00\x00\x00\x00\xc0\x9f\x32\x41\x8c\x00\xe0' b'\x18\xb1\x0c\xad\x08\x00\x45\x00\x00\x38\x00\x00\x40\x00\x40\x11\x65\x47\xc0\xa8\xaa\x08\xc0\xa8' b'\xaa\x14\x80\x1b\x00\x35\x00\x24\x85\xed' ) modified_pcap = ( b'\x34\xcd\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x01\x00\x00\x00' b'\x3c\xfb\x80\x61\x6d\x32\x08\x00\x03\x00\x00\x00\x72\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\xff\xff\xff' ) def test_reader(): import pytest data = TestData().pcap # --- BytesIO tests --- from .compat import BytesIO # BytesIO fobj = BytesIO(data) reader = Reader(fobj) assert reader.name == '' _, buf1 = next(iter(reader)) assert buf1 == data[FileHdr.__hdr_len__ + PktHdr.__hdr_len__:] assert reader.datalink() == 1 with pytest.raises(NotImplementedError): reader.setfilter(1, 2) # --- dispatch() tests --- # test count = 0 fobj.seek(0) reader = Reader(fobj) assert reader.dispatch(0, lambda ts, pkt: None) == 1 # test count > 0 fobj.seek(0) reader = Reader(fobj) assert reader.dispatch(4, lambda ts, pkt: None) == 1 # test iterative dispatch fobj.seek(0) reader = Reader(fobj) assert reader.dispatch(1, lambda ts, pkt: None) == 1 assert reader.dispatch(1, lambda ts, pkt: None) == 0 # test loop() over all packets fobj.seek(0) reader = Reader(fobj) class Count: counter = 0 @classmethod def inc(cls): cls.counter += 1 reader.loop(lambda ts, pkt: Count.inc()) assert Count.counter == 1 def test_reader_dloff(): from binascii import unhexlify buf_filehdr = unhexlify( 'a1b2c3d4' # TCPDUMP_MAGIC '0001' # v_major '0002' # v_minor '00000000' # thiszone '00000000' # sigfigs '00000100' # snaplen '00000023' # linktype (not known) ) buf_pkthdr = unhexlify( '00000003' # tv_sec '00000005' # tv_usec '00000004' # caplen '00000004' # len ) from .compat import BytesIO fobj = BytesIO(buf_filehdr + buf_pkthdr + b'\x11' * 4) reader = Reader(fobj) # confirm that if the linktype is unknown, it defaults to 0 assert reader.dloff == 0 assert next(reader) == (3.000005, b'\x11' * 4) @TryExceptException(ValueError, msg="invalid tcpdump header") def test_reader_badheader(): from .compat import BytesIO fobj = BytesIO(b'\x00' * 24) _ = Reader(fobj) # noqa def test_reader_fd(): data = TestData().pcap import tempfile with tempfile.TemporaryFile() as fd: fd.write(data) fd.seek(0) reader = Reader(fd) assert reader.fd == fd.fileno() assert reader.fileno() == fd.fileno() def test_reader_modified_pcap_type(): data = TestData().modified_pcap import tempfile with tempfile.TemporaryFile() as fd: fd.write(data) fd.seek(0) reader = Reader(fd) assert reader.fd == fd.fileno() assert reader.fileno() == fd.fileno() timestamp, pkts = next(reader) assert pkts == 3 * b'\xff' assert timestamp == 1635842876.537197000 class WriterTestWrap: """ Decorate a writer test function with an instance of this class. The test will be provided with a writer object, which it should write some pkts to. After the test has run, the BytesIO object will be passed to a Reader, which will compare each pkt to the return value of the test. """ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def __call__(self, f, *args, **kwargs): def wrapper(*args, **kwargs): from .compat import BytesIO for little_endian in [True, False]: fobj = BytesIO() _sysle = Writer._Writer__le Writer._Writer__le = little_endian f.__globals__['writer'] = Writer(fobj, **self.kwargs.get('writer', {})) f.__globals__['fobj'] = fobj pkts = f(*args, **kwargs) fobj.flush() fobj.seek(0) assert pkts for (ts_out, pkt_out), (ts_in, pkt_in) in zip(pkts, Reader(fobj).readpkts()): assert ts_out == ts_in assert pkt_out == pkt_in # 'noqa' for flake8 to ignore these since writer was injected into globals writer.close() # noqa Writer._Writer__le = _sysle return wrapper @WriterTestWrap() def test_writer_precision_normal(): ts, pkt = 1454725786.526401, b'foo' writer.writepkt(pkt, ts=ts) # noqa return [(ts, pkt)] @WriterTestWrap(writer={'nano': True}) def test_writer_precision_nano(): ts, pkt = Decimal('1454725786.010203045'), b'foo' writer.writepkt(pkt, ts=ts) # noqa return [(ts, pkt)] @WriterTestWrap(writer={'nano': False}) def test_writer_precision_nano_fail(): """if writer is not set to nano, supplying this timestamp should be truncated""" ts, pkt = (Decimal('1454725786.010203045'), b'foo') writer.writepkt(pkt, ts=ts) # noqa return [(1454725786.010203, pkt)] @WriterTestWrap() def test_writepkt_no_time(): ts, pkt = 1454725786.526401, b'foooo' _tmp = time.time time.time = lambda: ts writer.writepkt(pkt) # noqa time.time = _tmp return [(ts, pkt)] @WriterTestWrap(writer={'snaplen': 10}) def test_writepkt_snaplen(): ts, pkt = 1454725786.526401, b'foooo' writer.writepkt(pkt, ts) # noqa return [(ts, pkt)] @WriterTestWrap() def test_writepkt_with_time(): ts, pkt = 1454725786.526401, b'foooo' writer.writepkt(pkt, ts) # noqa return [(ts, pkt)] @WriterTestWrap() def test_writepkt_time(): ts, pkt = 1454725786.526401, b'foooo' writer.writepkt_time(pkt, ts) # noqa return [(ts, pkt)] @WriterTestWrap() def test_writepkts(): """writing multiple packets from a list""" pkts = [ (1454725786.526401, b"fooo"), (1454725787.526401, b"barr"), (3243204320.093211, b"grill"), (1454725789.526401, b"lol"), ] writer.writepkts(pkts) # noqa return pkts def test_universal_reader(): import pytest from .compat import BytesIO from . import pcapng # libpcap data = TestData().pcap fobj = BytesIO(data) reader = UniversalReader(fobj) assert isinstance(reader, Reader) # pcapng data = pcapng.define_testdata().valid_pcapng fobj = BytesIO(data) reader = UniversalReader(fobj) assert isinstance(reader, pcapng.Reader) # unknown fobj = BytesIO(b'\x42' * 1000) with pytest.raises(ValueError): reader = UniversalReader(fobj) dpkt-1.9.8/dpkt/pcapng.py000066400000000000000000001377461427735150300153100ustar00rootroot00000000000000"""pcap Next Generation file format""" # Spec: https://pcapng.github.io/pcapng/ # pylint: disable=no-member # pylint: disable=attribute-defined-outside-init from __future__ import print_function from __future__ import absolute_import from struct import pack as struct_pack, unpack as struct_unpack from time import time import sys from . import dpkt from .compat import BytesIO, intround BYTE_ORDER_MAGIC = 0x1A2B3C4D BYTE_ORDER_MAGIC_LE = 0x4D3C2B1A PCAPNG_VERSION_MAJOR = 1 PCAPNG_VERSION_MINOR = 0 # Block types PCAPNG_BT_IDB = 0x00000001 # Interface Description Block PCAPNG_BT_PB = 0x00000002 # Packet Block (deprecated) PCAPNG_BT_SPB = 0x00000003 # Simple Packet Block PCAPNG_BT_EPB = 0x00000006 # Enhanced Packet Block PCAPNG_BT_SHB = 0x0A0D0D0A # Section Header Block # Options PCAPNG_OPT_ENDOFOPT = 0 # end of options PCAPNG_OPT_COMMENT = 1 # comment # SHB options PCAPNG_OPT_SHB_HARDWARE = 2 # description of the hardware PCAPNG_OPT_SHB_OS = 3 # name of the operating system PCAPNG_OPT_SHB_USERAPPL = 4 # name of the application # IDB options PCAPNG_OPT_IF_NAME = 2 # interface name PCAPNG_OPT_IF_DESCRIPTION = 3 # interface description PCAPNG_OPT_IF_IPV4ADDR = 4 # IPv4 network address and netmask for the interface PCAPNG_OPT_IF_IPV6ADDR = 5 # IPv6 network address and prefix length for the interface PCAPNG_OPT_IF_MACADDR = 6 # interface hardware MAC address PCAPNG_OPT_IF_EUIADDR = 7 # interface hardware EUI address PCAPNG_OPT_IF_SPEED = 8 # interface speed in bits/s PCAPNG_OPT_IF_TSRESOL = 9 # timestamp resolution PCAPNG_OPT_IF_TZONE = 10 # time zone PCAPNG_OPT_IF_FILTER = 11 # capture filter PCAPNG_OPT_IF_OS = 12 # operating system PCAPNG_OPT_IF_FCSLEN = 13 # length of the Frame Check Sequence in bits PCAPNG_OPT_IF_TSOFFSET = 14 # offset (in seconds) that must be added to packet timestamp # 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 DLT_PFSYNC = 18 DLT_IEEE802_11 = 105 DLT_LINUX_SLL = 113 DLT_PFLOG = 117 DLT_IEEE802_11_RADIO = 127 if sys.platform.find('openbsd') != -1: DLT_LOOP = 12 DLT_RAW = 14 else: DLT_LOOP = 108 DLT_RAW = 12 dltoff = {DLT_NULL: 4, DLT_EN10MB: 14, DLT_IEEE802: 22, DLT_ARCNET: 6, DLT_SLIP: 16, DLT_PPP: 4, DLT_FDDI: 21, DLT_PFLOG: 48, DLT_PFSYNC: 4, DLT_LOOP: 4, DLT_LINUX_SLL: 16} # def _swap32b(i): """Swap endianness of an uint32""" return struct_unpack('I', i))[0] def _align32b(i): """Return int `i` aligned to the 32-bit boundary""" r = i % 4 return i if not r else i + 4 - r def _padded(s): """Return bytes `s` padded with zeroes to align to the 32-bit boundary""" return struct_pack('%ss' % _align32b(len(s)), s) def _padded_tolen(s, tolen): """Return bytes `s` padded with `tolen` zeroes to align to the 32-bit boundary""" return struct_pack('%ss' % tolen, s) def _padlen(s): """Return size of padding required to align str `s` to the 32-bit boundary""" return _align32b(len(s)) - len(s) class _PcapngBlock(dpkt.Packet): """Base class for a pcapng block with Options""" __hdr__ = ( ('type', 'I', 0), # block type ('len', 'I', 12), # block total length: total size of this block, in octets # ( body, variable size ) ('_len', 'I', 12), # dup of len ) def unpack_hdr(self, buf): dpkt.Packet.unpack(self, buf) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.len > len(buf): raise dpkt.NeedData self._do_unpack_options(buf) def _do_unpack_options(self, buf, oo=None): self.opts = [] self.data = '' oo = oo or self.__hdr_len__ - 4 # options offset ol = self.len - oo - 4 # length opts_buf = buf[oo:oo + ol] while opts_buf: opt = (PcapngOptionLE(opts_buf) if self.__hdr_fmt__[0] == '<' else PcapngOption(opts_buf)) self.opts.append(opt) opts_buf = opts_buf[len(opt):] if opt.code == PCAPNG_OPT_ENDOFOPT: break # duplicate total length field self._len = struct_unpack(self.__hdr_fmt__[0] + 'I', buf[-4:])[0] if self._len != self.len: raise dpkt.UnpackError('length fields do not match') def _do_pack_options(self): if not getattr(self, 'opts', None): return b'' if self.opts[-1].code != PCAPNG_OPT_ENDOFOPT: raise dpkt.PackError('options must end with opt_endofopt') return b''.join(bytes(o) for o in self.opts) def __bytes__(self): opts_buf = self._do_pack_options() n = len(opts_buf) + self.__hdr_len__ self.len = n self._len = n hdr_buf = self._pack_hdr(self.type, n, n) return b''.join([hdr_buf[:-4], opts_buf, hdr_buf[-4:]]) def __len__(self): if not getattr(self, 'opts', None): return self.__hdr_len__ opts_len = sum(len(o) for o in self.opts) return self.__hdr_len__ + opts_len class PcapngBlockLE(_PcapngBlock): __byte_order__ = '<' class PcapngOption(dpkt.Packet): """A single Option""" __hdr__ = ( ('code', 'H', PCAPNG_OPT_ENDOFOPT), ('len', 'H', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = buf[self.__hdr_len__:self.__hdr_len__ + self.len] # decode comment if self.code == PCAPNG_OPT_COMMENT: try: self.text = self.data.decode('utf-8') except UnicodeDecodeError as ude: if b'\x00' in self.data: self.text = self.data[:self.data.index(b'\x00')].decode('ascii') else: raise ude def __bytes__(self): # encode comment if self.code == PCAPNG_OPT_COMMENT: text = getattr(self, 'text', self.data) self.data = text.encode('utf-8') if not isinstance(text, bytes) else text self.len = len(self.data) hdr = self._pack_hdr(self.code, self.len) return hdr + _padded(self.data) def __len__(self): return self.__hdr_len__ + len(self.data) + _padlen(self.data) def __repr__(self): if self.code == PCAPNG_OPT_ENDOFOPT: return '{0}(opt_endofopt)'.format(self.__class__.__name__) else: return dpkt.Packet.__repr__(self) class PcapngOptionLE(PcapngOption): __byte_order__ = '<' class SectionHeaderBlock(_PcapngBlock): """Section Header block""" __hdr__ = ( ('type', 'I', PCAPNG_BT_SHB), ('len', 'I', 28), ('bom', 'I', BYTE_ORDER_MAGIC), ('v_major', 'H', PCAPNG_VERSION_MAJOR), ('v_minor', 'H', PCAPNG_VERSION_MINOR), ('sec_len', 'q', -1), # section length, -1 = auto # ( options, variable size ) ('_len', 'I', 28) ) def __bytes__(self): opts_buf = self._do_pack_options() n = len(opts_buf) + self.__hdr_len__ self.len = n self._len = n hdr_buf = self._pack_hdr( self.type, n, self.bom, self.v_major, self.v_minor, self.sec_len, n, ) return b''.join([hdr_buf[:-4], opts_buf, hdr_buf[-4:]]) class SectionHeaderBlockLE(SectionHeaderBlock): __byte_order__ = '<' class InterfaceDescriptionBlock(_PcapngBlock): """Interface Description block""" __hdr__ = ( ('type', 'I', PCAPNG_BT_IDB), ('len', 'I', 20), ('linktype', 'H', DLT_EN10MB), ('_reserved', 'H', 0), ('snaplen', 'I', 1500), # ( options, variable size ) ('_len', 'I', 20) ) def __bytes__(self): opts_buf = self._do_pack_options() n = len(opts_buf) + self.__hdr_len__ self.len = n self._len = n hdr_buf = self._pack_hdr( self.type, n, self.linktype, self._reserved, self.snaplen, n, ) return b''.join([hdr_buf[:-4], opts_buf, hdr_buf[-4:]]) class InterfaceDescriptionBlockLE(InterfaceDescriptionBlock): __byte_order__ = '<' class EnhancedPacketBlock(_PcapngBlock): """Enhanced Packet block""" __hdr__ = ( ('type', 'I', PCAPNG_BT_EPB), ('len', 'I', 64), ('iface_id', 'I', 0), ('ts_high', 'I', 0), # timestamp high ('ts_low', 'I', 0), # timestamp low ('caplen', 'I', 0), # captured len, size of pkt_data ('pkt_len', 'I', 0), # actual packet len # ( pkt_data, variable size ) # ( options, variable size ) ('_len', 'I', 64) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.len > len(buf): raise dpkt.NeedData # packet data po = self.__hdr_len__ - 4 # offset of pkt_data self.pkt_data = buf[po:po + self.caplen] # skip padding between pkt_data and options opts_offset = po + _align32b(self.caplen) self._do_unpack_options(buf, opts_offset) def __bytes__(self): pkt_buf = self.pkt_data pkt_len = len(pkt_buf) self.caplen = pkt_len self.pkt_len = pkt_len opts_buf = self._do_pack_options() n = self.__hdr_len__ + _align32b(self.caplen) + len(opts_buf) self.len = n self._len = n hdr_buf = self._pack_hdr( self.type, n, self.iface_id, self.ts_high, self.ts_low, pkt_len, pkt_len, n ) return b''.join([hdr_buf[:-4], _padded(pkt_buf), opts_buf, hdr_buf[-4:]]) def __len__(self): opts_len = sum(len(o) for o in self.opts) return self.__hdr_len__ + _align32b(self.caplen) + opts_len class EnhancedPacketBlockLE(EnhancedPacketBlock): __byte_order__ = '<' class PacketBlock(EnhancedPacketBlock): """Packet block (deprecated)""" __hdr__ = ( ('type', 'I', PCAPNG_BT_PB), ('len', 'I', 64), ('iface_id', 'H', 0), ('drops_count', 'H', 0), # local drop counter ('ts_high', 'I', 0), # timestamp high ('ts_low', 'I', 0), # timestamp low ('caplen', 'I', 0), # captured len, size of pkt_data ('pkt_len', 'I', 0), # actual packet len # ( pkt_data, variable size ) # ( options, variable size ) ('_len', 'I', 64) ) def __bytes__(self): pkt_buf = self.pkt_data pkt_len = len(pkt_buf) self.caplen = pkt_len self.pkt_len = pkt_len opts_buf = self._do_pack_options() n = self.__hdr_len__ + _align32b(self.caplen) + len(opts_buf) self.len = n self._len = n hdr_buf = self._pack_hdr( self.type, n, self.iface_id, self.drops_count, self.ts_high, self.ts_low, pkt_len, pkt_len, n ) return b''.join([hdr_buf[:-4], _padded(pkt_buf), opts_buf, hdr_buf[-4:]]) class PacketBlockLE(PacketBlock): __byte_order__ = '<' class Writer(object): """Simple pcapng dumpfile writer.""" __le = sys.byteorder == 'little' def __init__(self, fileobj, snaplen=1500, linktype=DLT_EN10MB, shb=None, idb=None): """ Create a pcapng dumpfile writer for the given fileobj. shb can be an instance of SectionHeaderBlock(LE) idb can be an instance of InterfaceDescriptionBlock(LE) (or sequence of them) """ self.__f = fileobj if shb: self._validate_block('shb', shb, SectionHeaderBlock) if idb: try: for idb_ in idb: self._validate_block('idb', idb_, InterfaceDescriptionBlock) except (TypeError, ValueError): # not iter or _validate_block failed self._validate_block('idb', idb, InterfaceDescriptionBlock) idb = [idb] if self.__le: shb = shb or SectionHeaderBlockLE() idb = idb or [InterfaceDescriptionBlockLE(snaplen=snaplen, linktype=linktype)] self._kls = EnhancedPacketBlockLE else: shb = shb or SectionHeaderBlock() idb = idb or [InterfaceDescriptionBlock(snaplen=snaplen, linktype=linktype)] self._kls = EnhancedPacketBlock self.__f.write(bytes(shb)) for idb_ in idb: self.__f.write(bytes(idb_)) def _validate_block(self, arg_name, blk, expected_cls): """Check a user-defined block for correct type and endianness""" if not isinstance(blk, expected_cls): raise ValueError('{0}: expecting class {1}'.format( arg_name, expected_cls.__name__)) if self.__le and blk.__hdr_fmt__[0] == '>': raise ValueError('{0}: expecting class {1}LE on a little-endian system'.format( arg_name, expected_cls.__name__)) if not self.__le and blk.__hdr_fmt__[0] == '<': raise ValueError('{0}: expecting class {1} on a big-endian system'.format( arg_name, expected_cls.__name__.replace('LE', ''))) def writepkt(self, pkt, ts=None): """ Write a single packet with an optional timestamp. Args: pkt: buffer or instance of EnhancedPacketBlock(LE) ts: Unix timestamp in seconds since Epoch (e.g. 1454725786.99) """ if isinstance(pkt, EnhancedPacketBlock): self._validate_block('pkt', pkt, EnhancedPacketBlock) if ts is not None: # ts as an argument gets precedence ts = intround(ts * 1e6) elif pkt.ts_high == pkt.ts_low == 0: ts = intround(time() * 1e6) if ts is not None: pkt.ts_high = ts >> 32 pkt.ts_low = ts & 0xffffffff self.__f.write(bytes(pkt)) return # pkt is a buffer - wrap it into an EPB if ts is None: ts = time() self.writepkt_time(pkt, ts) def writepkt_time(self, pkt, ts): """ Write a single packet with a mandatory timestamp. Args: pkt: a buffer ts: Unix timestamp in seconds since Epoch (e.g. 1454725786.99) """ ts = intround(ts * 1e6) # to int microseconds s = pkt n = len(s) epb = self._kls( ts_high=ts >> 32, ts_low=ts & 0xffffffff, caplen=n, pkt_len=n, pkt_data=s ) self.__f.write(bytes(epb)) def writepkts(self, pkts): """ Take an iterable of (ts, pkt), and write to file. """ kls = self._kls() ph = kls._pack_hdr fd = self.__f iface_id = kls.iface_id pkt_type = kls.type opts_buf = kls._do_pack_options() opts_len = len(opts_buf) hdr_len = kls.__hdr_len__ precalc_n = hdr_len + opts_len for ts, pkt in pkts: ts = intround(ts * 1e6) # to int microseconds pkt_len = len(pkt) pkt_len_align = _align32b(pkt_len) n = precalc_n + pkt_len_align hdr_buf = ph( pkt_type, n, iface_id, ts >> 32, ts & 0xffffffff, pkt_len, pkt_len, n ) buf = b''.join([ hdr_buf[:-4], _padded_tolen(pkt, pkt_len_align), opts_buf, hdr_buf[-4:] ]) fd.write(buf) def close(self): self.__f.close() class Reader(object): """Simple pypcap-compatible pcapng file reader.""" def __init__(self, fileobj): self.name = getattr(fileobj, 'name', '<{0}>'.format(fileobj.__class__.__name__)) self.__f = fileobj shb = SectionHeaderBlock() buf = self.__f.read(shb.__hdr_len__) if len(buf) < shb.__hdr_len__: raise ValueError('invalid pcapng header') # unpack just the header since endianness is not known shb.unpack_hdr(buf) if shb.type != PCAPNG_BT_SHB: raise ValueError('invalid pcapng header: not a SHB') # determine the correct byte order and reload full SHB if shb.bom == BYTE_ORDER_MAGIC_LE: self.__le = True buf += self.__f.read(_swap32b(shb.len) - shb.__hdr_len__) shb = SectionHeaderBlockLE(buf) elif shb.bom == BYTE_ORDER_MAGIC: self.__le = False buf += self.__f.read(shb.len - shb.__hdr_len__) shb = SectionHeaderBlock(buf) else: raise ValueError('unknown endianness') # check if this version is supported if shb.v_major != PCAPNG_VERSION_MAJOR: raise ValueError('unknown pcapng version {0}.{1}'.format(shb.v_major, shb.v_minor,)) # look for a mandatory IDB idb = None while 1: buf = self.__f.read(8) if len(buf) < 8: break blk_type, blk_len = struct_unpack('II', buf) buf += self.__f.read(blk_len - 8) if blk_type == PCAPNG_BT_IDB: idb = (InterfaceDescriptionBlockLE(buf) if self.__le else InterfaceDescriptionBlock(buf)) break # just skip other blocks if idb is None: raise ValueError('IDB not found') # set timestamp resolution and offset self._divisor = float(1e6) # defaults self._tsoffset = 0 for opt in idb.opts: if opt.code == PCAPNG_OPT_IF_TSRESOL: # if MSB=0, the remaining bits is a neg power of 10 (e.g. 6 means microsecs) # if MSB=1, the remaining bits is a neg power of 2 (e.g. 10 means 1/1024 of second) opt_val = struct_unpack('b', opt.data)[0] pow_num = 2 if opt_val & 0b10000000 else 10 self._divisor = float(pow_num ** (opt_val & 0b01111111)) elif opt.code == PCAPNG_OPT_IF_TSOFFSET: # 64-bit int that specifies an offset (in seconds) that must be added to the # timestamp of each packet self._tsoffset = struct_unpack('q', opt.data)[0] if idb.linktype in dltoff: self.dloff = dltoff[idb.linktype] else: self.dloff = 0 self.idb = idb self.snaplen = idb.snaplen self.filter = '' self.__iter = iter(self) @property def fd(self): return self.__f.fileno() def fileno(self): return self.fd def datalink(self): return self.idb.linktype def setfilter(self, value, optimize=1): raise NotImplementedError def readpkts(self): return list(self) def __next__(self): return next(self.__iter) next = __next__ # Python 2 compat def dispatch(self, cnt, callback, *args): """Collect and process packets with a user callback. Return the number of packets processed, or 0 for a savefile. Arguments: cnt -- number of packets to process; or 0 to process all packets until EOF callback -- function with (timestamp, pkt, *args) prototype *args -- optional arguments passed to callback on execution """ processed = 0 if cnt > 0: for _ in range(cnt): try: ts, pkt = next(iter(self)) except StopIteration: break callback(ts, pkt, *args) processed += 1 else: for ts, pkt in self: callback(ts, pkt, *args) processed += 1 return processed def loop(self, callback, *args): self.dispatch(0, callback, *args) def __iter__(self): while 1: buf = self.__f.read(8) if len(buf) < 8: break blk_type, blk_len = struct_unpack('II', buf) buf += self.__f.read(blk_len - 8) if blk_type == PCAPNG_BT_EPB: epb = EnhancedPacketBlockLE(buf) if self.__le else EnhancedPacketBlock(buf) ts = self._tsoffset + (((epb.ts_high << 32) | epb.ts_low) / self._divisor) yield (ts, epb.pkt_data) elif blk_type == PCAPNG_BT_PB: pb = PacketBlockLE(buf) if self.__le else PacketBlock(buf) ts = self._tsoffset + (((pb.ts_high << 32) | pb.ts_low) / self._divisor) yield (ts, pb.pkt_data) # just ignore other blocks ######### # TESTS # ######### def test_shb(): """Test SHB with options""" buf = ( b'\x0a\x0d\x0d\x0a\x58\x00\x00\x00\x4d\x3c\x2b\x1a\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff' b'\xff\xff\x04\x00\x31\x00\x54\x53\x68\x61\x72\x6b\x20\x31\x2e\x31\x30\x2e\x30\x72\x63\x32' b'\x20\x28\x53\x56\x4e\x20\x52\x65\x76\x20\x34\x39\x35\x32\x36\x20\x66\x72\x6f\x6d\x20\x2f' b'\x74\x72\x75\x6e\x6b\x2d\x31\x2e\x31\x30\x29\x00\x00\x00\x00\x00\x00\x00\x58\x00\x00\x00') opt_buf = b'\x04\x00\x31\x00TShark 1.10.0rc2 (SVN Rev 49526 from /trunk-1.10)\x00\x00\x00' # block unpacking shb = SectionHeaderBlockLE(buf) assert shb.type == PCAPNG_BT_SHB assert shb.bom == BYTE_ORDER_MAGIC assert shb.v_major == 1 assert shb.v_minor == 0 assert shb.sec_len == -1 assert shb.data == '' # options unpacking assert len(shb.opts) == 2 assert shb.opts[0].code == PCAPNG_OPT_SHB_USERAPPL assert shb.opts[0].data == b'TShark 1.10.0rc2 (SVN Rev 49526 from /trunk-1.10)' assert shb.opts[0].len == len(shb.opts[0].data) assert shb.opts[1].code == PCAPNG_OPT_ENDOFOPT assert shb.opts[1].len == 0 # option packing assert str(shb.opts[0]) == str(opt_buf) assert len(shb.opts[0]) == len(opt_buf) assert bytes(shb.opts[1]) == b'\x00\x00\x00\x00' # block packing assert bytes(shb) == bytes(buf) assert str(shb) == str(buf) assert len(shb) == len(buf) def test_idb(): """Test IDB with options""" buf = ( b'\x01\x00\x00\x00\x20\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\x09\x00\x01\x00\x06\x00' b'\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00') # block unpacking idb = InterfaceDescriptionBlockLE(buf) assert idb.type == PCAPNG_BT_IDB assert idb.linktype == DLT_EN10MB assert idb.snaplen == 0xffff assert idb.data == '' # options unpacking assert len(idb.opts) == 2 assert idb.opts[0].code == PCAPNG_OPT_IF_TSRESOL assert idb.opts[0].len == 1 assert idb.opts[0].data == b'\x06' assert idb.opts[1].code == PCAPNG_OPT_ENDOFOPT assert idb.opts[1].len == 0 # option packing assert bytes(idb.opts[0]) == b'\x09\x00\x01\x00\x06\x00\x00\x00' assert len(idb.opts[0]) == 8 assert bytes(idb.opts[1]) == b'\x00\x00\x00\x00' # block packing assert bytes(idb) == bytes(buf) assert str(idb) == str(buf) assert len(idb) == len(buf) def test_epb(): """Test EPB with a non-ascii comment option""" buf = ( b'\x06\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x73\xe6\x04\x00\xbe\x37\xe2\x19\x4a\x00' b'\x00\x00\x4a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x45\x00' b'\x00\x3c\x5d\xb3\x40\x00\x40\x06\xdf\x06\x7f\x00\x00\x01\x7f\x00\x00\x01\x98\x34\x11\x4e' b'\x95\xcb\x2d\x3a\x00\x00\x00\x00\xa0\x02\xaa\xaa\xfe\x30\x00\x00\x02\x04\xff\xd7\x04\x02' b'\x08\x0a\x05\x8f\x70\x89\x00\x00\x00\x00\x01\x03\x03\x07\x00\x00\x01\x00\x0a\x00\xd0\xbf' b'\xd0\xb0\xd0\xba\xd0\xb5\xd1\x82\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00') # block unpacking epb = EnhancedPacketBlockLE(buf) assert epb.type == PCAPNG_BT_EPB assert epb.caplen == len(epb.pkt_data) assert epb.pkt_len == len(epb.pkt_data) assert epb.caplen == 74 assert epb.ts_high == 321139 assert epb.ts_low == 434255806 assert epb.data == '' # options unpacking assert len(epb.opts) == 2 assert epb.opts[0].code == PCAPNG_OPT_COMMENT assert epb.opts[0].text == u'\u043f\u0430\u043a\u0435\u0442' assert epb.opts[1].code == PCAPNG_OPT_ENDOFOPT assert epb.opts[1].len == 0 # option packing assert bytes(epb.opts[0]) == b'\x01\x00\x0a\x00\xd0\xbf\xd0\xb0\xd0\xba\xd0\xb5\xd1\x82\x00\x00' assert len(epb.opts[0]) == 16 assert bytes(epb.opts[1]) == b'\x00\x00\x00\x00' # block packing assert bytes(epb) == bytes(buf) assert str(epb) == str(buf) assert len(epb) == len(buf) def test_pb(): """Test PB with a non-ascii comment option""" buf = ( b'\x02\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x73\xe6\x04\x00\xbe\x37\xe2\x19\x4a\x00' b'\x00\x00\x4a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x45\x00' b'\x00\x3c\x5d\xb3\x40\x00\x40\x06\xdf\x06\x7f\x00\x00\x01\x7f\x00\x00\x01\x98\x34\x11\x4e' b'\x95\xcb\x2d\x3a\x00\x00\x00\x00\xa0\x02\xaa\xaa\xfe\x30\x00\x00\x02\x04\xff\xd7\x04\x02' b'\x08\x0a\x05\x8f\x70\x89\x00\x00\x00\x00\x01\x03\x03\x07\x00\x00\x01\x00\x0a\x00\xd0\xbf' b'\xd0\xb0\xd0\xba\xd0\xb5\xd1\x82\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00') # block unpacking pb = PacketBlockLE(buf) assert pb.type == PCAPNG_BT_PB assert pb.caplen == len(pb.pkt_data) assert pb.iface_id == 0 assert pb.drops_count == 0 assert pb.pkt_len == len(pb.pkt_data) assert pb.caplen == 74 assert pb.ts_high == 321139 assert pb.ts_low == 434255806 assert pb.data == '' # options unpacking assert len(pb.opts) == 2 assert pb.opts[0].code == PCAPNG_OPT_COMMENT assert pb.opts[0].text == u'\u043f\u0430\u043a\u0435\u0442' assert pb.opts[1].code == PCAPNG_OPT_ENDOFOPT assert pb.opts[1].len == 0 # option packing assert bytes(pb.opts[0]) == b'\x01\x00\x0a\x00\xd0\xbf\xd0\xb0\xd0\xba\xd0\xb5\xd1\x82\x00\x00' assert len(pb.opts[0]) == 16 assert bytes(pb.opts[1]) == b'\x00\x00\x00\x00' # block packing assert bytes(pb) == bytes(buf) assert str(pb) == str(buf) assert len(pb) == len(buf) def test_pb_read(): """ Test PB parsing as part of file """ pb_packet = ( b'\x02\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x73\xe6\x04\x00\xbe\x37\xe2\x19\x4a\x00' b'\x00\x00\x4a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x45\x00' b'\x00\x3c\x5d\xb3\x40\x00\x40\x06\xdf\x06\x7f\x00\x00\x01\x7f\x00\x00\x01\x98\x34\x11\x4e' b'\x95\xcb\x2d\x3a\x00\x00\x00\x00\xa0\x02\xaa\xaa\xfe\x30\x00\x00\x02\x04\xff\xd7\x04\x02' b'\x08\x0a\x05\x8f\x70\x89\x00\x00\x00\x00\x01\x03\x03\x07\x00\x00\x01\x00\x0a\x00\xd0\xbf' b'\xd0\xb0\xd0\xba\xd0\xb5\xd1\x82\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00') buf = define_testdata().valid_pcapng + pb_packet fobj = BytesIO(buf) # test reading reader = Reader(fobj) # first packet is EPB and comes from define_testdata().valid_pcapng ts, buf1 = next(iter(reader)) assert ts == 1442984653.210838 # second packet is concatenated PB, pb_packet defined above ts, buf2 = next(iter(reader)) assert ts == 1379281936.72595 def test_epb_ascii_comment_option(): """Test EPB with an ascii comment option""" buf = ( b'\x06\x00\x00\x00\x7c\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x79\xd2\xdf\xe1\x44\x00' b'\x00\x00\x44\x00\x00\x00\x00\x00\x00\x01\x00\x06\x00\x0b\xdb\x43\xe7\x4b\xf6\x7f\x08\x00' b'\x45\x00\x00\x34\x2b\x1f\x40\x00\x40\x06\x15\x63\x82\xd9\xfa\x81\x82\xd9\xfa\x0d\x17\x70' b'\xec\x3e\x02\xba\x94\x38\x81\x52\x4a\x39\x80\x10\xbb\x5d\x53\x0d\x00\x00\x01\x01\x08\x0a' b'\x03\xf9\xc7\xbf\x04\x02\x38\x28\x01\x00\x0f\x00\x50\x61\x63\x6b\x65\x74\x20\x23\x31\x00' b'\x78\x4d\x39\x87\x0c\x00\x00\x00\x00\x00\x7c\x00\x00\x00') # block unpacking epb = EnhancedPacketBlockLE(buf) # options unpacking assert len(epb.opts) == 2 assert epb.opts[0].code == PCAPNG_OPT_COMMENT assert epb.opts[0].text == 'Packet #1' assert epb.opts[1].code == PCAPNG_OPT_ENDOFOPT assert epb.opts[1].len == 0 # option packing assert bytes(epb.opts[0]) == b'\x01\x00\x09\x00\x50\x61\x63\x6b\x65\x74\x20\x23\x31\x00\x00\x00' assert len(epb.opts[0]) == 16 assert bytes(epb.opts[1]) == b'\x00\x00\x00\x00' def test_epb_invalid_utf8_comment_option(): """Test EPB with an invalid (non UTF-8, non-zero terminated ascii) comment option""" buf = ( b'\x06\x00\x00\x00\x7c\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x79\xd2\xdf\xe1\x44\x00' b'\x00\x00\x44\x00\x00\x00\x00\x00\x00\x01\x00\x06\x00\x0b\xdb\x43\xe7\x4b\xf6\x7f\x08\x00' b'\x45\x00\x00\x34\x2b\x1f\x40\x00\x40\x06\x15\x63\x82\xd9\xfa\x81\x82\xd9\xfa\x0d\x17\x70' b'\xec\x3e\x02\xba\x94\x38\x81\x52\x4a\x39\x80\x10\xbb\x5d\x53\x0d\x00\x00\x01\x01\x08\x0a' b'\x03\xf9\xc7\xbf\x04\x02\x38\x28\x01\x00\x0f\x00\x50\x61\x63\x6b\x65\x74\x20\x23\x31\x20' b'\x78\x4d\x39\x87\x0c\x00\x00\x00\x00\x00\x7c\x00\x00\x00') try: EnhancedPacketBlockLE(buf) except Exception as e: assert isinstance(e, UnicodeDecodeError) def test_simple_write_read(): """Test writing a basic pcapng and then reading it""" fobj = BytesIO() writer = Writer(fobj, snaplen=0x2000, linktype=DLT_LINUX_SLL) writer.writepkt(b'foo', ts=1454725786.526401) fobj.flush() fobj.seek(0) reader = Reader(fobj) assert reader.snaplen == 0x2000 assert reader.datalink() == DLT_LINUX_SLL ts, buf1 = next(iter(reader)) assert ts == 1454725786.526401 assert buf1 == b'foo' # test dispatch() fobj.seek(0) reader = Reader(fobj) assert reader.dispatch(1, lambda ts, pkt: None) == 1 assert reader.dispatch(1, lambda ts, pkt: None) == 0 fobj.close() def test_pcapng_header(): """Reading an empty file will fail as the header length is incorrect""" fobj = BytesIO() try: Reader(fobj) except Exception as e: assert isinstance(e, ValueError) def define_testdata(): class TestData(object): def __init__(self): self.valid_shb_le = SectionHeaderBlockLE(opts=[ PcapngOptionLE(code=3, data=b'64-bit Windows 8.1, build 9600'), PcapngOptionLE(code=4, data=b'Dumpcap 1.12.7 (v1.12.7-0-g7fc8978 from master-1.12)'), PcapngOptionLE() ]) self.valid_shb_be = SectionHeaderBlock(opts=[ PcapngOption(code=3, data=b'64-bit Windows 8.1, build 9600'), PcapngOption(code=4, data=b'Dumpcap 1.12.7 (v1.12.7-0-g7fc8978 from master-1.12)'), PcapngOption() ]) self.valid_idb_le = InterfaceDescriptionBlockLE(snaplen=0x40000, opts=[ PcapngOptionLE(code=2, data=b'\\Device\\NPF_{3BBF21A7-91AE-4DDB-AB2C-C782999C22D5}'), PcapngOptionLE(code=9, data=b'\x06'), PcapngOptionLE(code=12, data=b'64-bit Windows 8.1, build 9600'), PcapngOptionLE() ]) self.valid_idb_be = InterfaceDescriptionBlock(snaplen=0x40000, opts=[ PcapngOption(code=2, data=b'\\Device\\NPF_{3BBF21A7-91AE-4DDB-AB2C-C782999C22D5}'), PcapngOption(code=9, data=b'\x06'), PcapngOption(code=12, data=b'64-bit Windows 8.1, build 9600'), PcapngOption() ]) self.valid_pcapng = ( b'\x0a\x0d\x0d\x0a\x7c\x00\x00\x00\x4d\x3c\x2b\x1a\x01\x00\x00' b'\x00\xff\xff\xff\xff\xff\xff\xff\xff\x03\x00\x1e\x00\x36\x34' b'\x2d\x62\x69\x74\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x38\x2e' b'\x31\x2c\x20\x62\x75\x69\x6c\x64\x20\x39\x36\x30\x30\x00\x00' b'\x04\x00\x34\x00\x44\x75\x6d\x70\x63\x61\x70\x20\x31\x2e\x31' b'\x32\x2e\x37\x20\x28\x76\x31\x2e\x31\x32\x2e\x37\x2d\x30\x2d' b'\x67\x37\x66\x63\x38\x39\x37\x38\x20\x66\x72\x6f\x6d\x20\x6d' b'\x61\x73\x74\x65\x72\x2d\x31\x2e\x31\x32\x29\x00\x00\x00\x00' b'\x7c\x00\x00\x00\x01\x00\x00\x00\x7c\x00\x00\x00\x01\x00\x00' b'\x00\x00\x00\x04\x00\x02\x00\x32\x00\x5c\x44\x65\x76\x69\x63' b'\x65\x5c\x4e\x50\x46\x5f\x7b\x33\x42\x42\x46\x32\x31\x41\x37' b'\x2d\x39\x31\x41\x45\x2d\x34\x44\x44\x42\x2d\x41\x42\x32\x43' b'\x2d\x43\x37\x38\x32\x39\x39\x39\x43\x32\x32\x44\x35\x7d\x00' b'\x00\x09\x00\x01\x00\x06\x00\x00\x00\x0c\x00\x1e\x00\x36\x34' b'\x2d\x62\x69\x74\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x38\x2e' b'\x31\x2c\x20\x62\x75\x69\x6c\x64\x20\x39\x36\x30\x30\x00\x00' b'\x00\x00\x00\x00\x7c\x00\x00\x00\x06\x00\x00\x00\x84\x00\x00' b'\x00\x00\x00\x00\x00\x63\x20\x05\x00\xd6\xc4\xab\x0b\x4a\x00' b'\x00\x00\x4a\x00\x00\x00\x08\x00\x27\x96\xcb\x7c\x52\x54\x00' b'\x12\x35\x02\x08\x00\x45\x00\x00\x3c\xa4\x40\x00\x00\x1f\x01' b'\x27\xa2\xc0\xa8\x03\x28\x0a\x00\x02\x0f\x00\x00\x56\xf0\x00' b'\x01\x00\x6d\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c' b'\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x41\x42\x43\x44' b'\x45\x46\x47\x48\x49\x00\x00\x01\x00\x0f\x00\x64\x70\x6b\x74' b'\x20\x69\x73\x20\x61\x77\x65\x73\x6f\x6d\x65\x00\x00\x00\x00' b'\x00\x84\x00\x00\x00' ) self.valid_pkts = [ (1442984653.210838, (b"\x08\x00'\x96\xcb|RT\x00\x125\x02\x08\x00E\x00\x00<\xa4@" b"\x00\x00\x1f\x01'\xa2\xc0\xa8\x03(\n\x00\x02\x0f\x00\x00V" b"\xf0\x00\x01\x00mABCDEFGHIJKLMNOPQRSTUVWABCDEFGHI")) ] self.valid_epb_be = EnhancedPacketBlock(opts=[ PcapngOption(code=1, text=b'dpkt is awesome'), PcapngOption() ], pkt_data=( b'\x08\x00\x27\x96\xcb\x7c\x52\x54\x00\x12\x35\x02\x08\x00\x45' b'\x00\x00\x3c\xa4\x40\x00\x00\x1f\x01\x27\xa2\xc0\xa8\x03\x28' b'\x0a\x00\x02\x0f\x00\x00\x56\xf0\x00\x01\x00\x6d\x41\x42\x43' b'\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52' b'\x53\x54\x55\x56\x57\x41\x42\x43\x44\x45\x46\x47\x48\x49' )) self.valid_epb_le = EnhancedPacketBlockLE(opts=[ PcapngOptionLE(code=1, text=b'dpkt is awesome'), PcapngOptionLE() ], pkt_data=( b'\x08\x00\x27\x96\xcb\x7c\x52\x54\x00\x12\x35\x02\x08\x00\x45' b'\x00\x00\x3c\xa4\x40\x00\x00\x1f\x01\x27\xa2\xc0\xa8\x03\x28' b'\x0a\x00\x02\x0f\x00\x00\x56\xf0\x00\x01\x00\x6d\x41\x42\x43' b'\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52' b'\x53\x54\x55\x56\x57\x41\x42\x43\x44\x45\x46\x47\x48\x49' )) @property def shb_idb_epb_le(self): return self.valid_shb_le, self.valid_idb_le, self.valid_epb_le @property def shb_idb_epb_be(self): return self.valid_shb_be, self.valid_idb_be, self.valid_epb_be @property def shb_idb_epb(self): return self.shb_idb_epb_le if sys.byteorder == 'little' else self.shb_idb_epb_be return TestData() def pre_test(f): def wrapper(*args, **kwargs): fobj = BytesIO() f.__globals__['fobj'] = fobj ret = f(*args, **kwargs) fobj.flush() fobj.seek(0) return ret return wrapper class WriterTestWrap: """ Decorate a writer test function with an instance of this class. The test will be provided with a writer object, which it should write some pkts to. After the test has run, the BytesIO object will be passed to a Reader, which will compare each pkt to the return value of the test. """ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def __call__(self, f, *args, **kwargs): def wrapper(*args, **kwargs): from .compat import BytesIO for little_endian in [True, False]: fobj = BytesIO() _sysle = Writer._Writer__le Writer._Writer__le = little_endian f.__globals__['writer'] = Writer(fobj, **self.kwargs.get('writer', {})) f.__globals__['fobj'] = fobj pkts = f(*args, **kwargs) fobj.flush() fobj.seek(0) assert pkts, "You must return the input data from the test" for (ts_out, pkt_out), (ts_in, pkt_in) in zip(pkts, iter(Reader(fobj))): assert ts_out == ts_in assert pkt_out == pkt_in # 'noqa' for flake8 to ignore these since writer and fobj were injected into globals writer.close() # noqa Writer._Writer__le = _sysle del f.__globals__['writer'] del f.__globals__['fobj'] return wrapper class PostTest: def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def __call__(self, f, *args, **kwargs): def wrapper(*args, **kwargs): ret = f(*args, **kwargs) fobj = f.__globals__['fobj'] test_type = self.kwargs.get('test') if test_type == 'assertion': isexception = False try: Reader(fobj) except Exception as e: isexception = True assert isinstance(e, self.kwargs['type']) assert str(e) == self.kwargs['msg'] assert isexception, "No assertion raised!" elif test_type == 'compare_property': prop = self.kwargs['property'] reader = Reader(fobj) assert bytes(ret) == bytes(getattr(reader, prop)) elif test_type == 'compare_method': method = self.kwargs['method'] reader = Reader(fobj) comp = getattr(reader, method)() assert comp == ret else: raise Exception("No test type specified") return wrapper @PostTest(test='assertion', type=ValueError, msg='invalid pcapng header: not a SHB') @pre_test def test_shb_header(): shb = define_testdata().valid_shb_le shb.type = 123456666 fobj.write(bytes(shb)) # noqa @PostTest(test='assertion', type=ValueError, msg='unknown endianness') @pre_test def test_shb_bom(): shb = define_testdata().valid_shb_le shb.bom = 12345666 fobj.write(bytes(shb)) # noqa @PostTest(test='assertion', type=ValueError, msg='unknown pcapng version 123.45') @pre_test def test_shb_version(): shb = define_testdata().valid_shb_le shb.v_major = 123 shb.v_minor = 45 fobj.write(bytes(shb)) # noqa @PostTest(test='assertion', type=ValueError, msg='IDB not found') @pre_test def test_no_idb(): shb = define_testdata().valid_shb_le fobj.write(bytes(shb)+b'aaaa') # noqa @PostTest(test='compare_property', property='idb') @pre_test def test_idb_opt_offset(): """Test that the timestamp offset is correctly written and read""" shb = define_testdata().valid_shb_le idb = define_testdata().valid_idb_le idb.opts.insert(0, PcapngOptionLE( code=PCAPNG_OPT_IF_TSOFFSET, data=struct_pack('64 bytes, the length of their header""" shb, idb, epb = define_testdata().shb_idb_epb buf = b'quite-long-but-not-long-enough-at-least-32' try: epb.unpack(buf) except Exception as e: assert isinstance(e, dpkt.NeedData) def test_epb_unpack_length_mismatch(): """Force calculated len to be 0 when unpacking epb, this should fail when unpacking""" shb, idb, epb = define_testdata().shb_idb_epb unpackme = bytes(epb) unpackme = unpackme[:-4] + b'\x00' * 4 try: epb.unpack(unpackme) except Exception as e: assert isinstance(e, dpkt.UnpackError) assert str(e) == 'length fields do not match' def test_pcapng_block_len_no_opts(): """_PcapngBlock should return its own header __len__ if it has no opts""" block = _PcapngBlock() assert len(block) == 12 def test_reader_file_descriptor(): """Reader has .fd and .fileno() convenience members. Compare them to the actual fobj that was passed in""" pcapng = define_testdata().valid_pcapng import tempfile with tempfile.TemporaryFile() as fobj: fobj.write(pcapng) fobj.seek(0) reader = Reader(fobj) assert reader.fd == fobj.fileno() assert reader.fileno() == fobj.fileno() def test_posttest(): """Check that PostTest wrapper doesn't fail silently""" @PostTest() @pre_test def fun(): pass try: fun() except Exception as e: assert str(e) == 'No test type specified' dpkt-1.9.8/dpkt/pim.py000066400000000000000000000037711427735150300146130ustar00rootroot00000000000000# $Id: pim.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Protocol Independent Multicast.""" from __future__ import absolute_import from . import dpkt class PIM(dpkt.Packet): """Protocol Independent Multicast. Protocol Independent Multicast (PIM) is a collection of multicast routing protocols, each optimized for a different environment. There are two main PIM protocols, PIM Sparse Mode and PIM Dense Mode. A third PIM protocol, Bi-directional PIM, is less widely used. Attributes: __hdr__: Header fields of PIM. _v_type: (int): Version (4 bits) and type (4 bits). PIM version number and Message type. (1 byte) _rsvd: (int): Reserved. Always cleared to zero. (1 byte) sum: (int): Checksum. The 16-bit one's complement of the one's complement sum of the entire PIM message, excluding the data portion in the Register message.(2 bytes) """ __hdr__ = ( ('_v_type', 'B', 0x20), ('_rsvd', 'B', 0), ('sum', 'H', 0) ) __bit_fields__ = { '_v_type': ( ('v', 4), ('type', 4), ) } def __bytes__(self): if not self.sum: self.sum = dpkt.in_cksum(dpkt.Packet.__bytes__(self)) return dpkt.Packet.__bytes__(self) def test_pim(): from binascii import unhexlify buf = unhexlify( '20' # _v_type '00' # rsvd 'df93' # sum '000100020069' # data ) pimdata = PIM(buf) assert bytes(pimdata) == buf # force checksum recalculation pimdata = PIM(buf) pimdata.sum = 0 assert pimdata.sum == 0 assert bytes(pimdata) == buf assert pimdata.v == 2 assert pimdata.type == 0 # test setters buf_modified = unhexlify( '31' # _v_type '00' # rsvd 'df93' # sum '000100020069' # data ) pimdata.v = 3 pimdata.type = 1 assert bytes(pimdata) == buf_modified dpkt-1.9.8/dpkt/pmap.py000066400000000000000000000020101427735150300147440ustar00rootroot00000000000000# $Id: pmap.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Portmap / rpcbind.""" from __future__ import absolute_import from . import dpkt PMAP_PROG = 100000 PMAP_PROCDUMP = 4 PMAP_VERS = 2 class Pmap(dpkt.Packet): """Portmap / rpcbind. The port mapper (rpc.portmap or just portmap, or rpcbind) is an Open Network Computing Remote Procedure Call (ONC RPC) service that runs on network nodes that provide other ONC RPC services. The port mapper service always uses TCP or UDP port 111; a fixed port is required for it, as a client would not be able to get the port number for the port mapper service from the port mapper itself. Attributes: __hdr__: Header fields of Pmap. prog: (int) Program. (4 bytes) vers: (int) Version. (4 bytes) prot: (int) Protocol. (4 bytes) port: (int) Port. (4 bytes) """ __hdr__ = ( ('prog', 'I', 0), ('vers', 'I', 0), ('prot', 'I', 0), ('port', 'I', 0), ) dpkt-1.9.8/dpkt/ppp.py000066400000000000000000000111021427735150300146100ustar00rootroot00000000000000# $Id: ppp.py 65 2010-03-26 02:53:51Z dugsong $ # -*- coding: utf-8 -*- """Point-to-Point Protocol.""" from __future__ import absolute_import import struct from . import dpkt # XXX - finish later # http://www.iana.org/assignments/ppp-numbers PPP_IP = 0x21 # Internet Protocol PPP_IP6 = 0x57 # Internet Protocol v6 # Protocol field compression PFC_BIT = 0x01 class PPP(dpkt.Packet): """Point-to-Point Protocol. Point-to-Point Protocol (PPP) is a data link layer (layer 2) communication protocol between two routers directly without any host or any other networking in between. It can provide connection authentication, transmission encryption and data compression. Note: This class is subclassed in PPPoE Attributes: __hdr__: Header fields of PPP. addr: (int): Address. 0xFF, standard broadcast address. (1 byte) cntrl: (int): Control. 0x03, unnumbered data. (1 byte) p: (int): Protocol. PPP ID of embedded data. (1 byte) """ __hdr__ = ( ('addr', 'B', 0xff), ('cntrl', 'B', 3), ('p', 'B', PPP_IP), ) _protosw = {} @classmethod def set_p(cls, p, pktclass): cls._protosw[p] = pktclass @classmethod def get_p(cls, p): return cls._protosw[p] def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.p & PFC_BIT == 0: try: self.p = struct.unpack('>H', buf[2:4])[0] except struct.error: raise dpkt.NeedData self.data = self.data[1:] try: self.data = self._protosw[self.p](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, struct.error, dpkt.UnpackError): pass def pack_hdr(self): try: if self.p > 0xff: return struct.pack('>BBH', self.addr, self.cntrl, self.p) return dpkt.Packet.pack_hdr(self) except struct.error as e: raise dpkt.PackError(str(e)) def __load_protos(): g = globals() for k, v in g.items(): if k.startswith('PPP_'): name = k[4:] modname = name.lower() try: mod = __import__(modname, g, level=1) PPP.set_p(v, getattr(mod, name)) except (ImportError, AttributeError): continue def _mod_init(): """Post-initialization called when all dpkt modules are fully loaded""" if not PPP._protosw: __load_protos() def test_ppp(): # Test protocol compression s = b"\xff\x03\x21" p = PPP(s) assert p.p == 0x21 s = b"\xff\x03\x00\x21" p = PPP(s) assert p.p == 0x21 def test_ppp_short(): s = b"\xff\x03\x00" import pytest pytest.raises(dpkt.NeedData, PPP, s) def test_packing(): p = PPP() assert p.pack_hdr() == b"\xff\x03\x21" p.p = 0xc021 # LCP assert p.pack_hdr() == b"\xff\x03\xc0\x21" def test_ppp_classmethods(): import pytest class TestProto(dpkt.Packet): pass proto_number = 123 # asserting that this proto is not currently added with pytest.raises(KeyError): PPP.get_p(proto_number) PPP.set_p(proto_number, TestProto) assert PPP.get_p(proto_number) == TestProto # we need to reset the class, or impact other tests del PPP._protosw[proto_number] def test_unpacking_exceptions(): from dpkt import ip from binascii import unhexlify buf_ppp = unhexlify( 'ff' # addr '03' # cntrl '21' # p (PPP_IP) ) buf_ip = unhexlify( '45' # _v_hl '00' # tos '0014' # len '0000' # id '0000' # off '80' # ttl '06' # p 'd47e' # sum '11111111' # src '22222222' # dst ) buf = buf_ppp + buf_ip ppp = PPP(buf) assert hasattr(ppp, 'ip') assert isinstance(ppp.data, ip.IP) assert bytes(ppp) == buf def test_ppp_packing_error(): import pytest # addr is a 1-byte field, so this will overflow when packing ppp = PPP(p=257, addr=1234) with pytest.raises(dpkt.PackError): ppp.pack_hdr() def test_proto_loading(): # test that failure to load protocol handlers isn't catastrophic standard_protos = PPP._protosw # delete existing protos PPP._protosw = {} assert not PPP._protosw # inject a new global variable to be picked up globals()['PPP_NON_EXISTENT_PROTO'] = "FAIL" _mod_init() # we should get the same answer as if NON_EXISTENT_PROTO didn't exist assert PPP._protosw == standard_protos dpkt-1.9.8/dpkt/pppoe.py000066400000000000000000000120461427735150300151440ustar00rootroot00000000000000# $Id: pppoe.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """PPP-over-Ethernet.""" from __future__ import absolute_import import struct import codecs from . import dpkt from . import ppp # RFC 2516 codes PPPoE_PADI = 0x09 PPPoE_PADO = 0x07 PPPoE_PADR = 0x19 PPPoE_PADS = 0x65 PPPoE_PADT = 0xA7 PPPoE_SESSION = 0x00 class PPPoE(dpkt.Packet): """PPP-over-Ethernet. The Point-to-Point Protocol over Ethernet (PPPoE) is a network protocol for encapsulating Point-to-Point Protocol (PPP) frames inside Ethernet frames. It appeared in 1999, in the context of the boom of DSL as the solution for tunneling packets over the DSL connection to the ISP's IP network, and from there to the rest of the Internet. Attributes: __hdr__: Header fields of PPPoE. _v_type: v: (int): Version (4 bits) type: (int): Type (4 bits) code: (int): Code. (1 byte) session: (int): Session ID. (2 bytes) len: (int): Payload length. (2 bytes) """ __hdr__ = ( ('_v_type', 'B', 0x11), ('code', 'B', 0), ('session', 'H', 0), ('len', 'H', 0) # payload length ) __bit_fields__ = { '_v_type': ( ('v', 4), ('type', 4), ) } def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: if self.code == 0: # We need to use the pppoe.PPP header here, because PPPoE # doesn't do the normal encapsulation. self.data = self.ppp = PPP(self.data) except dpkt.UnpackError: pass class PPP(ppp.PPP): # Light version for protocols without the usual encapsulation, for PPPoE __hdr__ = ( # Usuaully two-bytes, but while protocol compression is not recommended, it is supported ('p', 'B', ppp.PPP_IP), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.p & ppp.PFC_BIT == 0: try: self.p = struct.unpack('>H', buf[:2])[0] except struct.error: raise dpkt.NeedData self.data = self.data[1:] try: self.data = self._protosw[self.p](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, struct.error, dpkt.UnpackError): pass def pack_hdr(self): try: # Protocol compression is *not* recommended (RFC2516), but we do it anyway if self.p > 0xff: return struct.pack('>H', self.p) return dpkt.Packet.pack_hdr(self) except struct.error as e: raise dpkt.PackError(str(e)) def test_pppoe_discovery(): s = ("11070000002801010000010300046413" "85180102000442524153010400103d0f" "0587062484f2df32b9ddfd77bd5b") s = codecs.decode(s, 'hex') p = PPPoE(s) assert p.code == PPPoE_PADO assert p.v == 1 assert p.type == 1 s = ("11190000002801010000010300046413" "85180102000442524153010400103d0f" "0587062484f2df32b9ddfd77bd5b") s = codecs.decode(s, 'hex') p = PPPoE(s) assert p.code == PPPoE_PADR assert p.pack_hdr() == s[:6] def test_pppoe_session(): s = "11000011000cc0210101000a050605fcd459" s = codecs.decode(s, 'hex') p = PPPoE(s) assert p.code == PPPoE_SESSION assert isinstance(p.ppp, PPP) assert p.data.p == 0xc021 # LCP assert len(p.data.data) == 10 assert p.data.pack_hdr() == b"\xc0\x21" s = ("110000110066005760000000003c3a40fc000000000000000000000000000001" "fc0000000002010000000000000100018100bf291f9700010102030405060708" "090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728" "292a2b2c2d2e2f3031323334") s = codecs.decode(s, 'hex') p = PPPoE(s) assert p.code == PPPoE_SESSION assert isinstance(p.ppp, PPP) assert p.data.p == ppp.PPP_IP6 assert p.data.data.p == 58 # ICMPv6 assert p.ppp.pack_hdr() == b"\x57" def test_ppp_packing(): p = PPP() assert p.pack_hdr() == b"\x21" p.p = 0xc021 # LCP assert p.pack_hdr() == b"\xc0\x21" def test_ppp_short(): import pytest pytest.raises(dpkt.NeedData, PPP, b"\x00") def test_pppoe_properties(): pppoe = PPPoE() assert pppoe.v == 1 pppoe.v = 7 assert pppoe.v == 7 assert pppoe.type == 1 pppoe.type = 5 assert pppoe.type == 5 def test_pppoe_unpack_error(): from binascii import unhexlify buf = unhexlify( "11" # v/type "00" # code "0011" # session "0066" # len "00" # data ) # this initialization swallows the UnpackError raised pppoe = PPPoE(buf) # unparsed data is still available assert pppoe.data == b'\x00' def test_ppp_pack_hdr(): import pytest from binascii import unhexlify buf = unhexlify( '01' # protocol, with compression bit set 'ff' # incomplete data ) ppp = PPP(buf) ppp.p = 1234567 with pytest.raises(dpkt.PackError): ppp.pack_hdr() # XXX - TODO TLVs, etc. dpkt-1.9.8/dpkt/qq.py000066400000000000000000000152721427735150300144460ustar00rootroot00000000000000# $Id: qq.py 48 2008-05-27 17:31:15Z yardley $ # -*- coding: utf-8 -*- from __future__ import absolute_import from .dpkt import Packet # header_type QQ_HEADER_BASIC_FAMILY = 0x02 QQ_HEADER_P2P_FAMILY = 0x00 QQ_HEADER_03_FAMILY = 0x03 QQ_HEADER_04_FAMILY = 0x04 QQ_HEADER_05_FAMILY = 0x05 header_type_str = [ "QQ_HEADER_P2P_FAMILY", "Unknown Type", "QQ_HEADER_03_FAMILY", "QQ_HEADER_04_FAMILY", "QQ_HEADER_05_FAMILY", ] # command QQ_CMD_LOGOUT = 0x0001 QQ_CMD_KEEP_ALIVE = 0x0002 QQ_CMD_MODIFY_INFO = 0x0004 QQ_CMD_SEARCH_USER = 0x0005 QQ_CMD_GET_USER_INFO = 0x0006 QQ_CMD_ADD_FRIEND = 0x0009 QQ_CMD_DELETE_FRIEND = 0x000A QQ_CMD_ADD_FRIEND_AUTH = 0x000B QQ_CMD_CHANGE_STATUS = 0x000D QQ_CMD_ACK_SYS_MSG = 0x0012 QQ_CMD_SEND_IM = 0x0016 QQ_CMD_RECV_IM = 0x0017 QQ_CMD_REMOVE_SELF = 0x001C QQ_CMD_REQUEST_KEY = 0x001D QQ_CMD_LOGIN = 0x0022 QQ_CMD_GET_FRIEND_LIST = 0x0026 QQ_CMD_GET_ONLINE_OP = 0x0027 QQ_CMD_SEND_SMS = 0x002D QQ_CMD_CLUSTER_CMD = 0x0030 QQ_CMD_TEST = 0x0031 QQ_CMD_GROUP_DATA_OP = 0x003C QQ_CMD_UPLOAD_GROUP_FRIEND = 0x003D QQ_CMD_FRIEND_DATA_OP = 0x003E QQ_CMD_DOWNLOAD_GROUP_FRIEND = 0x0058 QQ_CMD_FRIEND_LEVEL_OP = 0x005C QQ_CMD_PRIVACY_DATA_OP = 0x005E QQ_CMD_CLUSTER_DATA_OP = 0x005F QQ_CMD_ADVANCED_SEARCH = 0x0061 QQ_CMD_REQUEST_LOGIN_TOKEN = 0x0062 QQ_CMD_USER_PROPERTY_OP = 0x0065 QQ_CMD_TEMP_SESSION_OP = 0x0066 QQ_CMD_SIGNATURE_OP = 0x0067 QQ_CMD_RECV_MSG_SYS = 0x0080 QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS = 0x0081 QQ_CMD_WEATHER_OP = 0x00A6 QQ_CMD_ADD_FRIEND_EX = 0x00A7 QQ_CMD_AUTHORIZE = 0X00A8 QQ_CMD_UNKNOWN = 0xFFFF QQ_SUB_CMD_SEARCH_ME_BY_QQ_ONLY = 0x03 QQ_SUB_CMD_SHARE_GEOGRAPHY = 0x04 QQ_SUB_CMD_GET_FRIEND_LEVEL = 0x02 QQ_SUB_CMD_GET_CLUSTER_ONLINE_MEMBER = 0x01 QQ_05_CMD_REQUEST_AGENT = 0x0021 QQ_05_CMD_REQUEST_FACE = 0x0022 QQ_05_CMD_TRANSFER = 0x0023 QQ_05_CMD_REQUEST_BEGIN = 0x0026 QQ_CLUSTER_CMD_CREATE_CLUSTER = 0x01 QQ_CLUSTER_CMD_MODIFY_MEMBER = 0x02 QQ_CLUSTER_CMD_MODIFY_CLUSTER_INFO = 0x03 QQ_CLUSTER_CMD_GET_CLUSTER_INFO = 0x04 QQ_CLUSTER_CMD_ACTIVATE_CLUSTER = 0x05 QQ_CLUSTER_CMD_SEARCH_CLUSTER = 0x06 QQ_CLUSTER_CMD_JOIN_CLUSTER = 0x07 QQ_CLUSTER_CMD_JOIN_CLUSTER_AUTH = 0x08 QQ_CLUSTER_CMD_EXIT_CLUSTER = 0x09 QQ_CLUSTER_CMD_SEND_IM = 0x0A QQ_CLUSTER_CMD_GET_ONLINE_MEMBER = 0x0B QQ_CLUSTER_CMD_GET_MEMBER_INFO = 0x0C QQ_CLUSTER_CMD_MODIFY_CARD = 0x0E QQ_CLUSTER_CMD_GET_CARD_BATCH = 0x0F QQ_CLUSTER_CMD_GET_CARD = 0x10 QQ_CLUSTER_CMD_COMMIT_ORGANIZATION = 0x11 QQ_CLUSTER_CMD_UPDATE_ORGANIZATION = 0x12 QQ_CLUSTER_CMD_COMMIT_MEMBER_ORGANIZATION = 0x13 QQ_CLUSTER_CMD_GET_VERSION_ID = 0x19 QQ_CLUSTER_CMD_SEND_IM_EX = 0x1A QQ_CLUSTER_CMD_SET_ROLE = 0x1B QQ_CLUSTER_CMD_TRANSFER_ROLE = 0x1C QQ_CLUSTER_CMD_CREATE_TEMP = 0x30 QQ_CLUSTER_CMD_MODIFY_TEMP_MEMBER = 0x31 QQ_CLUSTER_CMD_EXIT_TEMP = 0x32 QQ_CLUSTER_CMD_GET_TEMP_INFO = 0x33 QQ_CLUSTER_CMD_MODIFY_TEMP_INFO = 0x34 QQ_CLUSTER_CMD_SEND_TEMP_IM = 0x35 QQ_CLUSTER_CMD_SUB_CLUSTER_OP = 0x36 QQ_CLUSTER_CMD_ACTIVATE_TEMP = 0x37 QQ_CLUSTER_SUB_CMD_ADD_MEMBER = 0x01 QQ_CLUSTER_SUB_CMD_REMOVE_MEMBER = 0x02 QQ_CLUSTER_SUB_CMD_GET_SUBJECT_LIST = 0x02 QQ_CLUSTER_SUB_CMD_GET_DIALOG_LIST = 0x01 QQ_SUB_CMD_GET_ONLINE_FRIEND = 0x2 QQ_SUB_CMD_GET_ONLINE_SERVICE = 0x3 QQ_SUB_CMD_UPLOAD_GROUP_NAME = 0x2 QQ_SUB_CMD_DOWNLOAD_GROUP_NAME = 0x1 QQ_SUB_CMD_SEND_TEMP_SESSION_IM = 0x01 QQ_SUB_CMD_BATCH_DOWNLOAD_FRIEND_REMARK = 0x0 QQ_SUB_CMD_UPLOAD_FRIEND_REMARK = 0x1 QQ_SUB_CMD_REMOVE_FRIEND_FROM_LIST = 0x2 QQ_SUB_CMD_DOWNLOAD_FRIEND_REMARK = 0x3 QQ_SUB_CMD_MODIFY_SIGNATURE = 0x01 QQ_SUB_CMD_DELETE_SIGNATURE = 0x02 QQ_SUB_CMD_GET_SIGNATURE = 0x03 QQ_SUB_CMD_GET_USER_PROPERTY = 0x01 QQ_SUB_CMD_GET_WEATHER = 0x01 QQ_FILE_CMD_HEART_BEAT = 0x0001 QQ_FILE_CMD_HEART_BEAT_ACK = 0x0002 QQ_FILE_CMD_TRANSFER_FINISHED = 0x0003 QQ_FILE_CMD_FILE_OP = 0x0007 QQ_FILE_CMD_FILE_OP_ACK = 0x0008 QQ_FILE_CMD_SENDER_SAY_HELLO = 0x0031 QQ_FILE_CMD_SENDER_SAY_HELLO_ACK = 0x0032 QQ_FILE_CMD_RECEIVER_SAY_HELLO = 0x0033 QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK = 0x0034 QQ_FILE_CMD_NOTIFY_IP_ACK = 0x003C QQ_FILE_CMD_PING = 0x003D QQ_FILE_CMD_PONG = 0x003E QQ_FILE_CMD_YES_I_AM_BEHIND_FIREWALL = 0x0040 QQ_FILE_CMD_REQUEST_AGENT = 0x0001 QQ_FILE_CMD_CHECK_IN = 0x0002 QQ_FILE_CMD_FORWARD = 0x0003 QQ_FILE_CMD_FORWARD_FINISHED = 0x0004 QQ_FILE_CMD_IT_IS_TIME = 0x0005 QQ_FILE_CMD_I_AM_READY = 0x0006 command_str = { 0x0001: "QQ_CMD_LOGOUT", 0x0002: "QQ_CMD_KEEP_ALIVE", 0x0004: "QQ_CMD_MODIFY_INFO", 0x0005: "QQ_CMD_SEARCH_USER", 0x0006: "QQ_CMD_GET_USER_INFO", 0x0009: "QQ_CMD_ADD_FRIEND", 0x000A: "QQ_CMD_DELETE_FRIEND", 0x000B: "QQ_CMD_ADD_FRIEND_AUTH", 0x000D: "QQ_CMD_CHANGE_STATUS", 0x0012: "QQ_CMD_ACK_SYS_MSG", 0x0016: "QQ_CMD_SEND_IM", 0x0017: "QQ_CMD_RECV_IM", 0x001C: "QQ_CMD_REMOVE_SELF", 0x001D: "QQ_CMD_REQUEST_KEY", 0x0022: "QQ_CMD_LOGIN", 0x0026: "QQ_CMD_GET_FRIEND_LIST", 0x0027: "QQ_CMD_GET_ONLINE_OP", 0x002D: "QQ_CMD_SEND_SMS", 0x0030: "QQ_CMD_CLUSTER_CMD", 0x0031: "QQ_CMD_TEST", 0x003C: "QQ_CMD_GROUP_DATA_OP", 0x003D: "QQ_CMD_UPLOAD_GROUP_FRIEND", 0x003E: "QQ_CMD_FRIEND_DATA_OP", 0x0058: "QQ_CMD_DOWNLOAD_GROUP_FRIEND", 0x005C: "QQ_CMD_FRIEND_LEVEL_OP", 0x005E: "QQ_CMD_PRIVACY_DATA_OP", 0x005F: "QQ_CMD_CLUSTER_DATA_OP", 0x0061: "QQ_CMD_ADVANCED_SEARCH", 0x0062: "QQ_CMD_REQUEST_LOGIN_TOKEN", 0x0065: "QQ_CMD_USER_PROPERTY_OP", 0x0066: "QQ_CMD_TEMP_SESSION_OP", 0x0067: "QQ_CMD_SIGNATURE_OP", 0x0080: "QQ_CMD_RECV_MSG_SYS", 0x0081: "QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS", 0x00A6: "QQ_CMD_WEATHER_OP", 0x00A7: "QQ_CMD_ADD_FRIEND_EX", 0x00A8: "QQ_CMD_AUTHORIZE", 0xFFFF: "QQ_CMD_UNKNOWN", 0x0021: "_CMD_REQUEST_AGENT", # 0x0022: "_CMD_REQUEST_FACE", # FIXME - dup dict key 0x0023: "_CMD_TRANSFER", # 0x0026: "_CMD_REQUEST_BEGIN", # FIXME - dup dict key } class QQBasicPacket(Packet): __hdr__ = ( ('header_type', 'B', 2), ('source', 'H', 0), ('command', 'H', 0), ('sequence', 'H', 0), ('qqNum', 'L', 0), ) class QQ3Packet(Packet): __hdr__ = ( ('header_type', 'B', 3), ('command', 'B', 0), ('sequence', 'H', 0), ('unknown1', 'L', 0), ('unknown2', 'L', 0), ('unknown3', 'L', 0), ('unknown4', 'L', 0), ('unknown5', 'L', 0), ('unknown6', 'L', 0), ('unknown7', 'L', 0), ('unknown8', 'L', 0), ('unknown9', 'L', 0), ('unknown10', 'B', 1), ('unknown11', 'B', 0), ('unknown12', 'B', 0), ('source', 'H', 0), ('unknown13', 'B', 0), ) class QQ5Packet(Packet): __hdr__ = ( ('header_type', 'B', 5), ('source', 'H', 0), ('unknown', 'H', 0), ('command', 'H', 0), ('sequence', 'H', 0), ('qqNum', 'L', 0), ) dpkt-1.9.8/dpkt/radiotap.py000066400000000000000000000337071427735150300156330ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Radiotap""" from __future__ import print_function from __future__ import absolute_import from . import dpkt from . import ieee80211 from .compat import compat_ord # Ref: http://www.radiotap.org # Fields Ref: http://www.radiotap.org/defined-fields/all # Present flags _TSFT_SHIFT = 0 _FLAGS_SHIFT = 1 _RATE_SHIFT = 2 _CHANNEL_SHIFT = 3 _FHSS_SHIFT = 4 _ANT_SIG_SHIFT = 5 _ANT_NOISE_SHIFT = 6 _LOCK_QUAL_SHIFT = 7 _TX_ATTN_SHIFT = 8 _DB_TX_ATTN_SHIFT = 9 _DBM_TX_POWER_SHIFT = 10 _ANTENNA_SHIFT = 11 _DB_ANT_SIG_SHIFT = 12 _DB_ANT_NOISE_SHIFT = 13 _RX_FLAGS_SHIFT = 14 _CHANNELPLUS_SHIFT = 18 _EXT_SHIFT = 31 # Flags elements _FLAGS_SIZE = 2 _CFP_FLAG_SHIFT = 0 _PREAMBLE_SHIFT = 1 _WEP_SHIFT = 2 _FRAG_SHIFT = 3 _FCS_SHIFT = 4 _DATA_PAD_SHIFT = 5 _BAD_FCS_SHIFT = 6 _SHORT_GI_SHIFT = 7 # Channel type _CHAN_TYPE_SIZE = 4 _CHANNEL_TYPE_SHIFT = 4 _CCK_SHIFT = 5 _OFDM_SHIFT = 6 _TWO_GHZ_SHIFT = 7 _FIVE_GHZ_SHIFT = 8 _PASSIVE_SHIFT = 9 _DYN_CCK_OFDM_SHIFT = 10 _GFSK_SHIFT = 11 _GSM_SHIFT = 12 _STATIC_TURBO_SHIFT = 13 _HALF_RATE_SHIFT = 14 _QUARTER_RATE_SHIFT = 15 # Flags offsets and masks _FCS_MASK = 0x10 class Radiotap(dpkt.Packet): """Radiotap. Attributes: __hdr__: Header fields of Radiotap. version: (int): Version (1 byte) pad: (int): Padding (1 byte) length: (int): Length (2 bytes) """ __hdr__ = ( ('version', 'B', 0), ('pad', 'B', 0), ('length', 'H', 0), ) __byte_order__ = '<' def _is_present(self, bit): index = bit // 8 mask = 1 << (bit % 8) return 1 if self.present_flags[index] & mask else 0 def _set_bit(self, bit, val): # present_flags is a bytearray, this gets the element index = bit // 8 # mask retrieves every bit except our one mask = ~(1 << (bit % 8) & 0xff) # retrieve all of the bits, then or in the val at the appropriate place # as the mask does not return the value at `bit`, if `val` is zero, the bit remains zero self.present_flags[index] = (self.present_flags[index] & mask) | (val << bit % 8) @property def tsft_present(self): return self._is_present(_TSFT_SHIFT) @tsft_present.setter def tsft_present(self, val): self._set_bit(_TSFT_SHIFT, val) @property def flags_present(self): return self._is_present(_FLAGS_SHIFT) @flags_present.setter def flags_present(self, val): self._set_bit(_FLAGS_SHIFT, val) @property def rate_present(self): return self._is_present(_RATE_SHIFT) @rate_present.setter def rate_present(self, val): self._set_bit(_RATE_SHIFT, val) @property def channel_present(self): return self._is_present(_CHANNEL_SHIFT) @channel_present.setter def channel_present(self, val): self._set_bit(_CHANNEL_SHIFT, val) @property def fhss_present(self): return self._is_present(_FHSS_SHIFT) @fhss_present.setter def fhss_present(self, val): self._set_bit(_FHSS_SHIFT, val) @property def ant_sig_present(self): return self._is_present(_ANT_SIG_SHIFT) @ant_sig_present.setter def ant_sig_present(self, val): self._set_bit(_ANT_SIG_SHIFT, val) @property def ant_noise_present(self): return self._is_present(_ANT_NOISE_SHIFT) @ant_noise_present.setter def ant_noise_present(self, val): self._set_bit(_ANT_NOISE_SHIFT, val) @property def lock_qual_present(self): return self._is_present(_LOCK_QUAL_SHIFT) @lock_qual_present.setter def lock_qual_present(self, val): self._set_bit(_LOCK_QUAL_SHIFT, val) @property def tx_attn_present(self): return self._is_present(_TX_ATTN_SHIFT) @tx_attn_present.setter def tx_attn_present(self, val): self._set_bit(_TX_ATTN_SHIFT, val) @property def db_tx_attn_present(self): return self._is_present(_DB_TX_ATTN_SHIFT) @db_tx_attn_present.setter def db_tx_attn_present(self, val): self._set_bit(_DB_TX_ATTN_SHIFT, val) @property def dbm_tx_power_present(self): return self._is_present(_DBM_TX_POWER_SHIFT) @dbm_tx_power_present.setter def dbm_tx_power_present(self, val): self._set_bit(_DBM_TX_POWER_SHIFT, val) @property def ant_present(self): return self._is_present(_ANTENNA_SHIFT) @ant_present.setter def ant_present(self, val): self._set_bit(_ANTENNA_SHIFT, val) @property def db_ant_sig_present(self): return self._is_present(_DB_ANT_SIG_SHIFT) @db_ant_sig_present.setter def db_ant_sig_present(self, val): self._set_bit(_DB_ANT_SIG_SHIFT, val) @property def db_ant_noise_present(self): return self._is_present(_DB_ANT_NOISE_SHIFT) @db_ant_noise_present.setter def db_ant_noise_present(self, val): self._set_bit(_DB_ANT_NOISE_SHIFT, val) @property def rx_flags_present(self): return self._is_present(_RX_FLAGS_SHIFT) @rx_flags_present.setter def rx_flags_present(self, val): self._set_bit(_RX_FLAGS_SHIFT, val) @property def chanplus_present(self): return self._is_present(_CHANNELPLUS_SHIFT) @chanplus_present.setter def chanplus_present(self, val): self._set_bit(_CHANNELPLUS_SHIFT, val) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = buf[self.length:] self.fields = [] buf = buf[self.__hdr_len__:] self.present_flags = bytearray(buf[:4]) buf = buf[4:] ext_bit = _EXT_SHIFT while self._is_present(ext_bit): self.present_flags += bytearray(buf[:4]) buf = buf[4:] ext_bit += 32 # decode each field into self. (eg. self.tsft) as well as append it self.fields list field_decoder = [ ('tsft', self.tsft_present, self.TSFT), ('flags', self.flags_present, self.Flags), ('rate', self.rate_present, self.Rate), ('channel', self.channel_present, self.Channel), ('fhss', self.fhss_present, self.FHSS), ('ant_sig', self.ant_sig_present, self.AntennaSignal), ('ant_noise', self.ant_noise_present, self.AntennaNoise), ('lock_qual', self.lock_qual_present, self.LockQuality), ('tx_attn', self.tx_attn_present, self.TxAttenuation), ('db_tx_attn', self.db_tx_attn_present, self.DbTxAttenuation), ('dbm_tx_power', self.dbm_tx_power_present, self.DbmTxPower), ('ant', self.ant_present, self.Antenna), ('db_ant_sig', self.db_ant_sig_present, self.DbAntennaSignal), ('db_ant_noise', self.db_ant_noise_present, self.DbAntennaNoise), ('rx_flags', self.rx_flags_present, self.RxFlags), ('chanplus', self.chanplus_present, self.ChannelPlus) ] offset = self.__hdr_len__ + len(self.present_flags) for name, present_bit, parser in field_decoder: if present_bit: ali = parser.__alignment__ if ali > 1 and offset % ali: padding = ali - offset % ali buf = buf[padding:] offset += padding field = parser(buf) field.data = b'' setattr(self, name, field) self.fields.append(field) buf = buf[len(field):] offset += len(field) if len(self.data) > 0: if self.flags_present and self.flags.fcs: self.data = ieee80211.IEEE80211(self.data, fcs=self.flags.fcs) else: self.data = ieee80211.IEEE80211(self.data) class RadiotapField(dpkt.Packet): __alignment__ = 1 __byte_order__ = '<' class Antenna(RadiotapField): __hdr__ = ( ('index', 'B', 0), ) class AntennaNoise(RadiotapField): __hdr__ = ( ('db', 'b', 0), ) class AntennaSignal(RadiotapField): __hdr__ = ( ('db', 'b', 0), ) class Channel(RadiotapField): __alignment__ = 2 __hdr__ = ( ('freq', 'H', 0), ('flags', 'H', 0), ) class FHSS(RadiotapField): __hdr__ = ( ('set', 'B', 0), ('pattern', 'B', 0), ) class Flags(RadiotapField): __hdr__ = ( ('val', 'B', 0), ) @property def fcs(self): return (self.val & _FCS_MASK) >> _FCS_SHIFT @fcs.setter def fcs(self, v): self.val = (v << _FCS_SHIFT) | (v & ~_FCS_MASK) class LockQuality(RadiotapField): __alignment__ = 2 __hdr__ = ( ('val', 'H', 0), ) class RxFlags(RadiotapField): __alignment__ = 2 __hdr__ = ( ('val', 'H', 0), ) class Rate(RadiotapField): __hdr__ = ( ('val', 'B', 0), ) class TSFT(RadiotapField): __alignment__ = 8 __hdr__ = ( ('usecs', 'Q', 0), ) class TxAttenuation(RadiotapField): __alignment__ = 2 __hdr__ = ( ('val', 'H', 0), ) class DbTxAttenuation(RadiotapField): __alignment__ = 2 __hdr__ = ( ('db', 'H', 0), ) class DbAntennaNoise(RadiotapField): __hdr__ = ( ('db', 'B', 0), ) class DbAntennaSignal(RadiotapField): __hdr__ = ( ('db', 'B', 0), ) class DbmTxPower(RadiotapField): __hdr__ = ( ('dbm', 'B', 0), ) class ChannelPlus(RadiotapField): __alignment__ = 4 __hdr__ = ( ('flags', 'I', 0), ('freq', 'H', 0), ('channel', 'B', 0), ('maxpower', 'B', 0), ) def test_radiotap_1(): s = b'\x00\x00\x00\x18\x6e\x48\x00\x00\x00\x02\x6c\x09\xa0\x00\xa8\x81\x02\x00\x00\x00\x00\x00\x00\x00' rad = Radiotap(s) assert(rad.version == 0) assert(rad.present_flags == b'\x6e\x48\x00\x00') assert(rad.tsft_present == 0) assert(rad.flags_present == 1) assert(rad.rate_present == 1) assert(rad.channel_present == 1) assert(rad.fhss_present == 0) assert(rad.ant_sig_present == 1) assert(rad.ant_noise_present == 1) assert(rad.lock_qual_present == 0) assert(rad.db_tx_attn_present == 0) assert(rad.dbm_tx_power_present == 0) assert(rad.ant_present == 1) assert(rad.db_ant_sig_present == 0) assert(rad.db_ant_noise_present == 0) assert(rad.rx_flags_present == 1) assert(rad.channel.freq == 0x096c) assert(rad.channel.flags == 0xa0) assert(len(rad.fields) == 7) def test_radiotap_2(): s = (b'\x00\x00\x30\x00\x2f\x40\x00\xa0\x20\x08\x00\xa0\x20\x08\x00\xa0\x20\x08\x00\x00\x00\x00' b'\x00\x00\x08\x84\xbd\xac\x28\x00\x00\x00\x10\x02\x85\x09\xa0\x00\xa5\x00\x00\x00\xa1\x00' b'\x9f\x01\xa1\x02') rad = Radiotap(s) assert(rad.version == 0) assert(rad.present_flags == b'\x2f\x40\x00\xa0\x20\x08\x00\xa0\x20\x08\x00\xa0\x20\x08\x00\x00') assert(rad.tsft_present) assert(rad.flags_present) assert(rad.rate_present) assert(rad.channel_present) assert(not rad.fhss_present) assert(rad.ant_sig_present) assert(not rad.ant_noise_present) assert(not rad.lock_qual_present) assert(not rad.db_tx_attn_present) assert(not rad.dbm_tx_power_present) assert(not rad.ant_present) assert(not rad.db_ant_sig_present) assert(not rad.db_ant_noise_present) assert(rad.rx_flags_present) assert(rad.channel.freq == 2437) assert(rad.channel.flags == 0x00a0) assert(len(rad.fields) == 6) assert(rad.flags_present) assert(rad.flags.fcs) def test_fcs(): s = b'\x00\x00\x1a\x00\x2f\x48\x00\x00\x34\x8f\x71\x09\x00\x00\x00\x00\x10\x0c\x85\x09\xc0\x00\xcc\x01\x00\x00' rt = Radiotap(s) assert(rt.flags_present == 1) assert(rt.flags.fcs == 1) def test_radiotap_3(): # xchannel aka channel plus field s = ( b'\x00\x00\x20\x00\x67\x08\x04\x00\x84\x84\x66\x25\x00\x00\x00\x00\x22\x0c\xd6\xa0\x01\x00\x00\x00\x40' b'\x01\x00\x00\x3c\x14\x24\x11\x08\x02\x00\x00\xff\xff\xff\xff\xff\xff\x06\x03\x7f\x07\xa0\x16\x00\x19' b'\xe3\xd3\x53\x52\x00\x8e\xaa\xaa\x03\x00\x00\x00\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x19\xe3' b'\xd3\x53\x52\xa9\xfe\xf7\x00\x00\x00\x00\x00\x00\x00\x4f\x67\x32\x38' ) rt = Radiotap(s) assert rt.ant_noise.db == -96 assert rt.ant_sig.db == -42 assert rt.ant.index == 1 assert rt.chanplus_present assert rt.chanplus.flags == 0x140 assert rt.chanplus.freq == 5180 assert rt.chanplus.channel == 36 assert rt.chanplus.maxpower == 17 assert len(rt.fields) == 7 assert repr(rt.data).startswith('IEEE80211') def test_radiotap_properties(): from binascii import unhexlify buf = unhexlify( '00' '00' '0018' '0000000000000000000000000000000000000000' ) radiotap = Radiotap(buf) property_keys = [ 'tsft', 'flags', 'rate', 'channel', 'fhss', 'ant_sig', 'ant_noise', 'lock_qual', 'tx_attn', 'db_tx_attn', 'dbm_tx_power', 'ant', 'db_ant_sig', 'db_ant_noise', 'rx_flags', 'chanplus' ] for prop in [key + '_present' for key in property_keys]: print(prop) assert hasattr(radiotap, prop) assert getattr(radiotap, prop) == 0 setattr(radiotap, prop, 1) assert getattr(radiotap, prop) == 1 setattr(radiotap, prop, 0) assert getattr(radiotap, prop) == 0 def test_radiotap_unpack_fcs(): from binascii import unhexlify buf = unhexlify( '00' # version '00' # pad '1800' # length '6e48000011026c09a000a8810200000000000000' 'd40000000012f0b61ca4ffffffff' ) radiotap = Radiotap(buf) assert radiotap.data.fcs_present == 1 def test_flags(): flags = Radiotap.Flags(b'\x00') assert flags.fcs == 0 flags.fcs = 1 assert flags.fcs == 1 dpkt-1.9.8/dpkt/radius.py000066400000000000000000000110301427735150300153000ustar00rootroot00000000000000# $Id: radius.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Remote Authentication Dial-In User Service.""" from __future__ import absolute_import from . import dpkt from .compat import compat_ord # http://www.untruth.org/~josh/security/radius/radius-auth.html # RFC 2865 class RADIUS(dpkt.Packet): """Remote Authentication Dial-In User Service. Remote Authentication Dial-In User Service (RADIUS) is a networking protocol that provides centralized authentication, authorization, and accounting (AAA) management for users who connect and use a network service. RADIUS was developed by Livingston Enterprises in 1991 as an access server authentication and accounting protocol. It was later brought into IEEE 802 and IETF standards. Attributes: __hdr__: Header fields of RADIUS. code: (int): Code. (1 byte) id: (int): ID (1 byte) len: (int): Length (2 bytes) auth: (int): Authentication (16 bytes) """ __hdr__ = ( ('code', 'B', 0), ('id', 'B', 0), ('len', 'H', 4), ('auth', '16s', b'') ) attrs = b'' def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.attrs = parse_attrs(self.data) self.data = b'' def parse_attrs(buf): """Parse attributes buffer into a list of (type, data) tuples.""" attrs = [] while buf: t = compat_ord(buf[0]) l_ = compat_ord(buf[1]) if l_ < 2: break d, buf = buf[2:l_], buf[l_:] attrs.append((t, d)) return attrs # Codes RADIUS_ACCESS_REQUEST = 1 RADIUS_ACCESS_ACCEPT = 2 RADIUS_ACCESS_REJECT = 3 RADIUS_ACCT_REQUEST = 4 RADIUS_ACCT_RESPONSE = 5 RADIUS_ACCT_STATUS = 6 RADIUS_ACCESS_CHALLENGE = 11 # Attributes RADIUS_USER_NAME = 1 RADIUS_USER_PASSWORD = 2 RADIUS_CHAP_PASSWORD = 3 RADIUS_NAS_IP_ADDR = 4 RADIUS_NAS_PORT = 5 RADIUS_SERVICE_TYPE = 6 RADIUS_FRAMED_PROTOCOL = 7 RADIUS_FRAMED_IP_ADDR = 8 RADIUS_FRAMED_IP_NETMASK = 9 RADIUS_FRAMED_ROUTING = 10 RADIUS_FILTER_ID = 11 RADIUS_FRAMED_MTU = 12 RADIUS_FRAMED_COMPRESSION = 13 RADIUS_LOGIN_IP_HOST = 14 RADIUS_LOGIN_SERVICE = 15 RADIUS_LOGIN_TCP_PORT = 16 # unassigned RADIUS_REPLY_MESSAGE = 18 RADIUS_CALLBACK_NUMBER = 19 RADIUS_CALLBACK_ID = 20 # unassigned RADIUS_FRAMED_ROUTE = 22 RADIUS_FRAMED_IPX_NETWORK = 23 RADIUS_STATE = 24 RADIUS_CLASS = 25 RADIUS_VENDOR_SPECIFIC = 26 RADIUS_SESSION_TIMEOUT = 27 RADIUS_IDLE_TIMEOUT = 28 RADIUS_TERMINATION_ACTION = 29 RADIUS_CALLED_STATION_ID = 30 RADIUS_CALLING_STATION_ID = 31 RADIUS_NAS_ID = 32 RADIUS_PROXY_STATE = 33 RADIUS_LOGIN_LAT_SERVICE = 34 RADIUS_LOGIN_LAT_NODE = 35 RADIUS_LOGIN_LAT_GROUP = 36 RADIUS_FRAMED_ATALK_LINK = 37 RADIUS_FRAMED_ATALK_NETWORK = 38 RADIUS_FRAMED_ATALK_ZONE = 39 # 40-59 reserved for accounting RADIUS_CHAP_CHALLENGE = 60 RADIUS_NAS_PORT_TYPE = 61 RADIUS_PORT_LIMIT = 62 RADIUS_LOGIN_LAT_PORT = 63 def test_parse_attrs(): from binascii import unhexlify buf = unhexlify( '01' # type (RADIUS_USER_NAME) '06' # end of attribute value '75736572' # value ('user') '00' '00' ) attrs = parse_attrs(buf) assert len(attrs) == 1 type0, value0 = attrs[0] assert type0 == RADIUS_USER_NAME assert value0 == b'user' def test_parse_multiple_attrs(): from binascii import unhexlify buf = unhexlify( '01' # type (RADIUS_USER_NAME) '06' # end of attribute value '75736572' # value ('user') '02' # type (RADIUS_USER_PASSWORD) '0a' # end of attribute value '70617373776f7264' # value ('password') ) attrs = parse_attrs(buf) assert len(attrs) == 2 type0, value0 = attrs[0] assert type0 == RADIUS_USER_NAME assert value0 == b'user' type1, value1 = attrs[1] assert type1 == RADIUS_USER_PASSWORD assert value1 == b'password' def test_radius_unpacking(): from binascii import unhexlify buf_attrs = unhexlify( '01' # type (RADIUS_USER_NAME) '06' # end of attribute value '75736572' # value ('user') ) buf_radius_header = unhexlify( '01' # code '34' # id '1234' # len '0123456789abcdef' # auth '0123456789abcdef' # auth ) buf = buf_radius_header + buf_attrs radius = RADIUS(buf) assert len(radius.attrs) == 1 name0, value0 = radius.attrs[0] assert name0 == 1 assert value0 == b'user' dpkt-1.9.8/dpkt/rfb.py000066400000000000000000000036071427735150300145750ustar00rootroot00000000000000# $Id: rfb.py 47 2008-05-27 02:10:00Z jon.oberheide $ # -*- coding: utf-8 -*- """Remote Framebuffer Protocol.""" from __future__ import absolute_import from . import dpkt # Remote Framebuffer Protocol # http://www.realvnc.com/docs/rfbproto.pdf # Client to Server Messages CLIENT_SET_PIXEL_FORMAT = 0 CLIENT_SET_ENCODINGS = 2 CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3 CLIENT_KEY_EVENT = 4 CLIENT_POINTER_EVENT = 5 CLIENT_CUT_TEXT = 6 # Server to Client Messages SERVER_FRAMEBUFFER_UPDATE = 0 SERVER_SET_COLOUR_MAP_ENTRIES = 1 SERVER_BELL = 2 SERVER_CUT_TEXT = 3 class RFB(dpkt.Packet): """Remote Framebuffer Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of RADIUS. TODO. """ __hdr__ = ( ('type', 'B', 0), ) class SetPixelFormat(dpkt.Packet): __hdr__ = ( ('pad', '3s', b''), ('pixel_fmt', '16s', b'') ) class SetEncodings(dpkt.Packet): __hdr__ = ( ('pad', '1s', b''), ('num_encodings', 'H', 0) ) class FramebufferUpdateRequest(dpkt.Packet): __hdr__ = ( ('incremental', 'B', 0), ('x_position', 'H', 0), ('y_position', 'H', 0), ('width', 'H', 0), ('height', 'H', 0) ) class KeyEvent(dpkt.Packet): __hdr__ = ( ('down_flag', 'B', 0), ('pad', '2s', b''), ('key', 'I', 0) ) class PointerEvent(dpkt.Packet): __hdr__ = ( ('button_mask', 'B', 0), ('x_position', 'H', 0), ('y_position', 'H', 0) ) class FramebufferUpdate(dpkt.Packet): __hdr__ = ( ('pad', '1s', b''), ('num_rects', 'H', 0) ) class SetColourMapEntries(dpkt.Packet): __hdr__ = ( ('pad', '1s', b''), ('first_colour', 'H', 0), ('num_colours', 'H', 0) ) class CutText(dpkt.Packet): __hdr__ = ( ('pad', '3s', b''), ('length', 'I', 0) ) dpkt-1.9.8/dpkt/rip.py000066400000000000000000000053101427735150300146070ustar00rootroot00000000000000# $Id: rip.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Routing Information Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt # RIP v2 - RFC 2453 # http://tools.ietf.org/html/rfc2453 REQUEST = 1 RESPONSE = 2 class RIP(dpkt.Packet): """Routing Information Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of RIP. TODO. """ __hdr__ = ( ('cmd', 'B', REQUEST), ('v', 'B', 2), ('rsvd', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] self.auth = None while self.data: rte = RTE(self.data[:20]) if rte.family == 0xFFFF: self.auth = Auth(self.data[:20]) else: l_.append(rte) self.data = self.data[20:] self.data = self.rtes = l_ def __len__(self): n = self.__hdr_len__ if self.auth: n += len(self.auth) n += sum(map(len, self.rtes)) return n def __bytes__(self): auth = b'' if self.auth: auth = bytes(self.auth) return self.pack_hdr() + auth + b''.join(map(bytes, self.rtes)) class RTE(dpkt.Packet): __hdr__ = ( ('family', 'H', 2), ('route_tag', 'H', 0), ('addr', 'I', 0), ('subnet', 'I', 0), ('next_hop', 'I', 0), ('metric', 'I', 1) ) class Auth(dpkt.Packet): __hdr__ = ( ('rsvd', 'H', 0xFFFF), ('type', 'H', 2), ('auth', '16s', 0) ) def test_creation_with_auth(): from binascii import unhexlify buf_auth = unhexlify( 'ffff' # rsvd '0002' # type '0123456789abcdef' # auth '0123456789abcdef' # auth ) auth_direct = Auth(buf_auth) assert bytes(auth_direct) == buf_auth buf_rte = unhexlify( '0002' # family '0000' # route_tag '01020300' # addr 'ffffff00' # subnet '00000000' # next_hop '00000001' # metric ) rte = RTE(buf_rte) assert bytes(rte) == buf_rte buf_rip = unhexlify( '02' # cmd '02' # v '0000' # rsvd ) rip = RIP(buf_rip + buf_auth + buf_rte) assert rip.auth assert rip.auth.rsvd == 0xffff assert rip.auth.type == 2 assert rip.auth.auth == unhexlify('0123456789abcdef') * 2 assert len(rip.rtes) == 1 rte = rip.rtes[0] assert rte.family == 2 assert rte.route_tag == 0 assert rte.metric == 1 assert bytes(rip) == buf_rip + buf_auth + buf_rte assert len(rip) == len(buf_rip + buf_auth + buf_rte) dpkt-1.9.8/dpkt/rpc.py000066400000000000000000000224031427735150300146030ustar00rootroot00000000000000# $Id: rpc.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Remote Procedure Call.""" from __future__ import absolute_import import struct from . import dpkt # RPC.dir CALL = 0 REPLY = 1 # RPC.Auth.flavor AUTH_NONE = AUTH_NULL = 0 AUTH_UNIX = 1 AUTH_SHORT = 2 AUTH_DES = 3 # RPC.Reply.stat MSG_ACCEPTED = 0 MSG_DENIED = 1 # RPC.Reply.Accept.stat SUCCESS = 0 PROG_UNAVAIL = 1 PROG_MISMATCH = 2 PROC_UNAVAIL = 3 GARBAGE_ARGS = 4 SYSTEM_ERR = 5 # RPC.Reply.Reject.stat RPC_MISMATCH = 0 AUTH_ERROR = 1 class RPC(dpkt.Packet): """Remote Procedure Call. RFC 5531: https://tools.ietf.org/html/rfc5531 TODO: Longer class information.... Attributes: __hdr__: Header fields of RPC. TODO. """ __hdr__ = ( ('xid', 'I', 0), ('dir', 'I', CALL) ) class Auth(dpkt.Packet): __hdr__ = (('flavor', 'I', AUTH_NONE), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) n = struct.unpack('>I', self.data[:4])[0] self.data = self.data[4:4 + n] def __len__(self): return 8 + len(self.data) def __bytes__(self): return self.pack_hdr() + struct.pack('>I', len(self.data)) + bytes(self.data) class Call(dpkt.Packet): __hdr__ = ( ('rpcvers', 'I', 2), ('prog', 'I', 0), ('vers', 'I', 0), ('proc', 'I', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.cred = RPC.Auth(self.data) self.verf = RPC.Auth(self.data[len(self.cred):]) self.data = self.data[len(self.cred) + len(self.verf):] def __len__(self): return len(bytes(self)) # XXX def __bytes__(self): return dpkt.Packet.__bytes__(self) + \ bytes(getattr(self, 'cred', RPC.Auth())) + \ bytes(getattr(self, 'verf', RPC.Auth())) + \ bytes(self.data) class Reply(dpkt.Packet): __hdr__ = (('stat', 'I', MSG_ACCEPTED), ) class Accept(dpkt.Packet): __hdr__ = (('stat', 'I', SUCCESS), ) def unpack(self, buf): self.verf = RPC.Auth(buf) buf = buf[len(self.verf):] self.stat = struct.unpack('>I', buf[:4])[0] if self.stat == SUCCESS: self.data = buf[4:] elif self.stat == PROG_MISMATCH: self.low, self.high = struct.unpack('>II', buf[4:12]) self.data = buf[12:] def __len__(self): if self.stat == PROG_MISMATCH: n = 8 else: n = 0 return len(self.verf) + 4 + n + len(self.data) def __bytes__(self): if self.stat == PROG_MISMATCH: return bytes(self.verf) + \ struct.pack('>III', self.stat, self.low, self.high) + self.data return bytes(self.verf) + dpkt.Packet.__bytes__(self) class Reject(dpkt.Packet): __hdr__ = (('stat', 'I', AUTH_ERROR), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.stat == RPC_MISMATCH: self.low, self.high = struct.unpack('>II', self.data[:8]) self.data = self.data[8:] elif self.stat == AUTH_ERROR: self.why = struct.unpack('>I', self.data[:4])[0] self.data = self.data[4:] def __len__(self): if self.stat == RPC_MISMATCH: n = 8 elif self.stat == AUTH_ERROR: n = 4 else: n = 0 return 4 + n + len(self.data) def __bytes__(self): if self.stat == RPC_MISMATCH: return struct.pack('>III', self.stat, self.low, self.high) + self.data elif self.stat == AUTH_ERROR: return struct.pack('>II', self.stat, self.why) + self.data return dpkt.Packet.__bytes__(self) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.stat == MSG_ACCEPTED: self.data = self.accept = self.Accept(self.data) elif self.stat == MSG_DENIED: self.data = self.reject = self.Reject(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.dir == CALL: self.data = self.call = self.Call(self.data) elif self.dir == REPLY: self.data = self.reply = self.Reply(self.data) def unpack_xdrlist(cls, buf): l_ = [] while buf: if buf.startswith(b'\x00\x00\x00\x01'): p = cls(buf[4:]) l_.append(p) buf = p.data elif buf.startswith(b'\x00\x00\x00\x00'): break else: raise dpkt.UnpackError('invalid XDR list') return l_ def pack_xdrlist(*args): return b'\x00\x00\x00\x01'.join(map(bytes, args)) + b'\x00\x00\x00\x00' def test_auth(): from binascii import unhexlify auth1 = RPC.Auth() assert auth1.flavor == AUTH_NONE buf = unhexlify('0000000000000000') assert bytes(auth1) == buf auth2 = RPC.Auth(buf) assert auth2.flavor == AUTH_NONE assert len(auth2) == 8 def test_call(): from binascii import unhexlify call1 = RPC.Call() assert call1.rpcvers == 2 assert call1.prog == 0 assert call1.vers == 0 assert call1.proc == 0 buf = unhexlify( '0000000200000000000000000000000000000000000000000000000000000000' ) assert bytes(call1) == buf call2 = RPC.Call(buf) assert call2.rpcvers == 2 assert call2.prog == 0 assert call2.vers == 0 assert call2.proc == 0 assert len(call2) == 32 assert bytes(call2) == buf def test_reply(): from binascii import unhexlify reply1 = RPC.Reply() assert reply1.stat == MSG_ACCEPTED assert bytes(reply1) == b'\00' * 4 buf_accepted = unhexlify( '00000000' # MSG_ACCEPTED '0000000000000000' # Auth '00000000' # SUCCESS '0000000000000000' # Auth ) reply_accepted = RPC.Reply(buf_accepted) assert reply_accepted.stat == MSG_ACCEPTED assert bytes(reply_accepted) == buf_accepted assert len(reply_accepted) == 24 buf_denied = unhexlify( '00000001' # MSG_DENIED '00000000' # RPC_MISMATCH '00000000' # low 'FFFFFFFF' # high '0000000000000000' # Auth ) reply_denied = RPC.Reply(buf_denied) assert reply_denied.stat == MSG_DENIED assert bytes(reply_denied) == buf_denied assert len(reply_denied) == 24 def test_accept(): from binascii import unhexlify accept1 = RPC.Reply.Accept() assert accept1.stat == SUCCESS buf_success = unhexlify( '0000000000000000' # Auth '00000000' # SUCCESS '0000000000000000' # Auth ) accept_success = RPC.Reply.Accept(buf_success) assert accept_success.stat == SUCCESS assert len(accept_success) == 20 assert bytes(accept_success) == buf_success buf_prog_mismatch = unhexlify( '0000000000000000' # Auth '00000002' # PROG_MISMATCH '0000000000000000' # Auth ) accept_prog_mismatch = RPC.Reply.Accept(buf_prog_mismatch) assert accept_prog_mismatch.stat == PROG_MISMATCH assert len(accept_prog_mismatch) == 20 assert bytes(accept_prog_mismatch) == buf_prog_mismatch def test_reject(): from binascii import unhexlify reject1 = RPC.Reply.Reject() assert reject1.stat == AUTH_ERROR buf_rpc_mismatch = unhexlify( '00000000' # RPC_MISMATCH '00000000' # low 'FFFFFFFF' # high '0000000000000000' # Auth ) reject2 = RPC.Reply.Reject(buf_rpc_mismatch) assert bytes(reject2) == buf_rpc_mismatch assert reject2.low == 0 assert reject2.high == 0xffffffff assert len(reject2) == 20 buf_auth_error = unhexlify( '00000001' # AUTH_ERROR '00000000' # low 'FFFFFFFF' # high '0000000000000000' # Auth ) reject3 = RPC.Reply.Reject(buf_auth_error) assert bytes(reject3) == buf_auth_error assert len(reject3) == 20 buf_other = unhexlify( '00000002' # NOT IMPLEMENTED '00000000' # low 'FFFFFFFF' # high '0000000000000000' # Auth ) reject4 = RPC.Reply.Reject(buf_other) assert bytes(reject4) == buf_other assert len(reject4) == 20 def test_rpc(): from binascii import unhexlify rpc = RPC() assert rpc.xid == 0 assert rpc.dir == CALL buf_call = unhexlify( '00000000' # xid '00000000' # CALL '0000000200000000000000000000000000000000000000000000000000000000' ) rpc_call = RPC(buf_call) assert bytes(rpc_call) == buf_call buf_reply = unhexlify( '00000000' # xid '00000001' # REPLY '00000000' # MSG_ACCEPTED '0000000000000000' # Auth '00000000' # SUCCESS '0000000000000000' # Auth ) rpc_reply = RPC(buf_reply) assert bytes(rpc_reply) == buf_reply dpkt-1.9.8/dpkt/rtp.py000066400000000000000000000117251427735150300146310ustar00rootroot00000000000000# $Id: rtp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Real-Time Transport Protocol.""" from __future__ import absolute_import from .dpkt import Packet # version 1100 0000 0000 0000 ! 0xC000 14 # p 0010 0000 0000 0000 ! 0x2000 13 # x 0001 0000 0000 0000 ! 0x1000 12 # cc 0000 1111 0000 0000 ! 0x0F00 8 # m 0000 0000 1000 0000 ! 0x0080 7 # pt 0000 0000 0111 1111 ! 0x007F 0 # _VERSION_MASK = 0xC000 _P_MASK = 0x2000 _X_MASK = 0x1000 _CC_MASK = 0x0F00 _M_MASK = 0x0080 _PT_MASK = 0x007F _VERSION_SHIFT = 14 _P_SHIFT = 13 _X_SHIFT = 12 _CC_SHIFT = 8 _M_SHIFT = 7 _PT_SHIFT = 0 VERSION = 2 class RTP(Packet): """Real-Time Transport Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of RTP. TODO. """ __hdr__ = ( ('_type', 'H', 0x8000), ('seq', 'H', 0), ('ts', 'I', 0), ('ssrc', 'I', 0), ) csrc = b'' @property def version(self): return (self._type & _VERSION_MASK) >> _VERSION_SHIFT @version.setter def version(self, ver): self._type = (ver << _VERSION_SHIFT) | (self._type & ~_VERSION_MASK) @property def p(self): return (self._type & _P_MASK) >> _P_SHIFT @p.setter def p(self, p): self._type = (p << _P_SHIFT) | (self._type & ~_P_MASK) @property def x(self): return (self._type & _X_MASK) >> _X_SHIFT @x.setter def x(self, x): self._type = (x << _X_SHIFT) | (self._type & ~_X_MASK) @property def cc(self): return (self._type & _CC_MASK) >> _CC_SHIFT @cc.setter def cc(self, cc): self._type = (cc << _CC_SHIFT) | (self._type & ~_CC_MASK) @property def m(self): return (self._type & _M_MASK) >> _M_SHIFT @m.setter def m(self, m): self._type = (m << _M_SHIFT) | (self._type & ~_M_MASK) @property def pt(self): return (self._type & _PT_MASK) >> _PT_SHIFT @pt.setter def pt(self, m): self._type = (m << _PT_SHIFT) | (self._type & ~_PT_MASK) def __len__(self): return self.__hdr_len__ + len(self.csrc) + len(self.data) def __bytes__(self): return self.pack_hdr() + self.csrc + bytes(self.data) def unpack(self, buf): super(RTP, self).unpack(buf) self.csrc = buf[self.__hdr_len__:self.__hdr_len__ + self.cc * 4] self.data = buf[self.__hdr_len__ + self.cc * 4:] def test_rtp(): rtp = RTP( b'\x80\x08\x4d\x01\x00\x01\x00\xe0\x34\x3f\xfa\x34\x53\x53\x53\x56\x53\x5d\x56\x57\xd5\xd6' b'\xd1\xde\xdf\xd3\xd9\xda\xdf\xdc\xdf\xd8\xdd\xd4\xdd\xd9\xd1\xd6\xdc\xda\xde\xdd\xc7\xc1' b'\xdf\xdf\xda\xdb\xdd\xdd\xc4\xd9\x55\x57\xd4\x50\x44\x44\x5b\x44\x4f\x4c\x47\x40\x4c\x47' b'\x59\x5b\x58\x5d\x56\x56\x53\x56\xd5\xd5\x54\x55\xd6\xd6\xd4\xd1\xd1\xd0\xd1\xd5\xdd\xd6' b'\x55\xd4\xd6\xd1\xd4\xd6\xd7\xd7\xd5\xd4\xd0\xd7\xd1\xd4\xd2\xdc\xd6\xdc\xdf\xdc\xdd\xd2' b'\xde\xdc\xd0\xdd\xdc\xd0\xd6\xd6\xd6\x55\x54\x55\x57\x57\x56\x50\x50\x5c\x5c\x52\x5d\x5d' b'\x5f\x5e\x5d\x5e\x52\x50\x52\x56\x54\x57\x55\x55\xd4\xd7\x55\xd5\x55\x55\x55\x55\x55\x54' b'\x57\x54\x55\x55\xd5\xd5\xd7\xd6\xd7\xd1\xd1\xd3\xd2\xd3\xd2\xd2\xd3\xd3' ) assert (rtp.version == 2) assert (rtp.p == 0) assert (rtp.x == 0) assert (rtp.cc == 0) assert (rtp.m == 0) assert (rtp.pt == 8) assert (rtp.seq == 19713) assert (rtp.ts == 65760) assert (rtp.ssrc == 0x343ffa34) assert (len(rtp) == 172) assert (bytes(rtp) == ( b'\x80\x08\x4d\x01\x00\x01\x00\xe0\x34\x3f\xfa\x34\x53\x53\x53\x56\x53\x5d\x56\x57\xd5\xd6' b'\xd1\xde\xdf\xd3\xd9\xda\xdf\xdc\xdf\xd8\xdd\xd4\xdd\xd9\xd1\xd6\xdc\xda\xde\xdd\xc7\xc1' b'\xdf\xdf\xda\xdb\xdd\xdd\xc4\xd9\x55\x57\xd4\x50\x44\x44\x5b\x44\x4f\x4c\x47\x40\x4c\x47' b'\x59\x5b\x58\x5d\x56\x56\x53\x56\xd5\xd5\x54\x55\xd6\xd6\xd4\xd1\xd1\xd0\xd1\xd5\xdd\xd6' b'\x55\xd4\xd6\xd1\xd4\xd6\xd7\xd7\xd5\xd4\xd0\xd7\xd1\xd4\xd2\xdc\xd6\xdc\xdf\xdc\xdd\xd2' b'\xde\xdc\xd0\xdd\xdc\xd0\xd6\xd6\xd6\x55\x54\x55\x57\x57\x56\x50\x50\x5c\x5c\x52\x5d\x5d' b'\x5f\x5e\x5d\x5e\x52\x50\x52\x56\x54\x57\x55\x55\xd4\xd7\x55\xd5\x55\x55\x55\x55\x55\x54' b'\x57\x54\x55\x55\xd5\xd5\xd7\xd6\xd7\xd1\xd1\xd3\xd2\xd3\xd2\xd2\xd3\xd3' )) # the following tests RTP header setters rtp = RTP() rtp.m = 1 rtp.pt = 3 rtp.seq = 1234 rtp.ts = 5678 rtp.ssrc = 0xabcdef01 assert (rtp.m == 1) assert (rtp.pt == 3) assert (rtp.seq == 1234) assert (rtp.ts == 5678) assert (rtp.ssrc == 0xabcdef01) def test_rtp_properties(): from .compat import compat_izip rtp = RTP() properties = ['version', 'p', 'x', 'cc', 'm', 'pt'] defaults = [2, 0, 0, 0, 0, 0] for prop, default in compat_izip(properties, defaults): assert hasattr(rtp, prop) assert getattr(rtp, prop) == default setattr(rtp, prop, 1) assert getattr(rtp, prop) == 1 dpkt-1.9.8/dpkt/rx.py000066400000000000000000000017221427735150300144510ustar00rootroot00000000000000# $Id: rx.py 23 2006-11-08 15:45:33Z jonojono $ # -*- coding: utf-8 -*- """Rx Protocol.""" from __future__ import absolute_import from . import dpkt # Types DATA = 0x01 ACK = 0x02 BUSY = 0x03 ABORT = 0x04 ACKALL = 0x05 CHALLENGE = 0x06 RESPONSE = 0x07 DEBUG = 0x08 # Flags CLIENT_INITIATED = 0x01 REQUEST_ACK = 0x02 LAST_PACKET = 0x04 MORE_PACKETS = 0x08 SLOW_START_OK = 0x20 JUMBO_PACKET = 0x20 # Security SEC_NONE = 0x00 SEC_BCRYPT = 0x01 SEC_RXKAD = 0x02 SEC_RXKAD_ENC = 0x03 class Rx(dpkt.Packet): """Rx Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of Rx. TODO. """ __hdr__ = ( ('epoch', 'I', 0), ('cid', 'I', 0), ('call', 'I', 1), ('seq', 'I', 0), ('serial', 'I', 1), ('type', 'B', 0), ('flags', 'B', CLIENT_INITIATED), ('status', 'B', 0), ('security', 'B', 0), ('sum', 'H', 0), ('service', 'H', 0) ) dpkt-1.9.8/dpkt/sccp.py000066400000000000000000000146211427735150300147520ustar00rootroot00000000000000# $Id: sccp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Cisco Skinny Client Control Protocol.""" from __future__ import absolute_import from . import dpkt KEYPAD_BUTTON = 0x00000003 OFF_HOOK = 0x00000006 ON_HOOK = 0x00000007 OPEN_RECEIVE_CHANNEL_ACK = 0x00000022 START_TONE = 0x00000082 STOP_TONE = 0x00000083 SET_LAMP = 0x00000086 SET_SPEAKER_MODE = 0x00000088 START_MEDIA_TRANSMIT = 0x0000008A STOP_MEDIA_TRANSMIT = 0x0000008B CALL_INFO = 0x0000008F DEFINE_TIME_DATE = 0x00000094 DISPLAY_TEXT = 0x00000099 OPEN_RECEIVE_CHANNEL = 0x00000105 CLOSE_RECEIVE_CHANNEL = 0x00000106 SELECT_SOFTKEYS = 0x00000110 CALL_STATE = 0x00000111 DISPLAY_PROMPT_STATUS = 0x00000112 CLEAR_PROMPT_STATUS = 0x00000113 ACTIVATE_CALL_PLANE = 0x00000116 class ActivateCallPlane(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('line_instance', 'I', 0), ) class CallInfo(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('calling_party_name', '40s', b''), ('calling_party', '24s', b''), ('called_party_name', '40s', b''), ('called_party', '24s', b''), ('line_instance', 'I', 0), ('call_id', 'I', 0), ('call_type', 'I', 0), ('orig_called_party_name', '40s', b''), ('orig_called_party', '24s', b'') ) class CallState(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('call_state', 'I', 12), # 12: Proceed, 15: Connected ('line_instance', 'I', 1), ('call_id', 'I', 0) ) class ClearPromptStatus(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('line_instance', 'I', 1), ('call_id', 'I', 0) ) class CloseReceiveChannel(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('conference_id', 'I', 0), ('passthruparty_id', 'I', 0), ) class DisplayPromptStatus(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('msg_timeout', 'I', 0), ('display_msg', '32s', b''), ('line_instance', 'I', 1), ('call_id', 'I', 0) ) class DisplayText(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('display_msg', '36s', b''), ) class KeypadButton(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('button', 'I', 0), ) class OpenReceiveChannel(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('conference_id', 'I', 0), ('passthruparty_id', 'I', 0), ('ms_packet', 'I', 0), ('payload_capability', 'I', 4), # 4: G.711 u-law 64k ('echo_cancel_type', 'I', 4), ('g723_bitrate', 'I', 0), ) class OpenReceiveChannelAck(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('channel_status', 'I', 0), ('ip', '4s', b''), ('port', 'I', 0), ('passthruparty_id', 'I', 0), ) class SelectStartKeys(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('line_id', 'I', 1), ('call_id', 'I', 0), ('softkey_set', 'I', 8), ('softkey_map', 'I', 0xffffffff) ) class SetLamp(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('stimulus', 'I', 9), # 9: Line ('stimulus_instance', 'I', 1), ('lamp_mode', 'I', 1), ) class SetSpeakerMode(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('speaker', 'I', 2), # 2: SpeakerOff ) class StartMediaTransmission(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('conference_id', 'I', 0), ('passthruparty_id', 'I', 0), ('ipv4_or_ipv6', 'I', 0), ('remote_ip', '16s', b''), ('remote_port', 'I', 0), ('ms_packet', 'I', 0), ('payload_capability', 'I', 4), # 4: G.711 u-law 64k ('precedence', 'I', 0), ('silence_suppression', 'I', 0), ('max_frames_per_pkt', 'I', 1), ('g723_bitrate', 'I', 0), ('call_reference', 'I', 0) ) class StartTone(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('tone', 'I', 0x24), # 0x24: AlertingTone ) class StopMediaTransmission(dpkt.Packet): __byte_order__ = '<' __hdr__ = ( ('conference_id', 'I', 0), ('passthruparty_id', 'I', 0), ) class SCCP(dpkt.Packet): """Cisco Skinny Client Control Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of SCCP. TODO. """ __byte_order__ = '<' __hdr__ = ( ('len', 'I', 0), ('rsvd', 'I', 0), ('msgid', 'I', 0), ('msg', '0s', b''), ) _msgsw = { KEYPAD_BUTTON: KeypadButton, OPEN_RECEIVE_CHANNEL_ACK: OpenReceiveChannelAck, START_TONE: StartTone, SET_LAMP: SetLamp, START_MEDIA_TRANSMIT: StartMediaTransmission, STOP_MEDIA_TRANSMIT: StopMediaTransmission, CALL_INFO: CallInfo, DISPLAY_TEXT: DisplayText, OPEN_RECEIVE_CHANNEL: OpenReceiveChannel, CLOSE_RECEIVE_CHANNEL: CloseReceiveChannel, CALL_STATE: CallState, DISPLAY_PROMPT_STATUS: DisplayPromptStatus, CLEAR_PROMPT_STATUS: ClearPromptStatus, ACTIVATE_CALL_PLANE: ActivateCallPlane, } def unpack(self, buf): dpkt.Packet.unpack(self, buf) n = self.len - 4 if n > len(self.data): raise dpkt.NeedData('not enough data') self.msg, self.data = self.data[:n], self.data[n:] try: p = self._msgsw[self.msgid](self.msg) setattr(self, p.__class__.__name__.lower(), p) except (KeyError, dpkt.UnpackError): pass def test_sccp(): import pytest from binascii import unhexlify buf = unhexlify( '08000000' # len '00000000' # rsvd '03000000' # msgid (KEYPAD_BUTTON) 'abcdef01' # msg '23456789' # daat ) sccp = SCCP(buf) assert sccp.msg == b'\xab\xcd\xef\x01' assert sccp.data == b'\x23\x45\x67\x89' assert isinstance(sccp.keypadbutton, KeypadButton) # len is too long for data, raises NeedData buf = unhexlify( '88880000' # len '00000000' # rsvd '00000003' # msgid (KEYPAD_BUTTON) 'abcdef01' # msg ) with pytest.raises(dpkt.NeedData): SCCP(buf) # msgid is invalid, raises KeyError on _msgsw (silently caught) buf = unhexlify( '08000000' # len '00000000' # rsvd '00000003' # msgid (invalid) 'abcdef01' # msg ) sccp = SCCP(buf) assert sccp.msg == b'\xab\xcd\xef\x01' assert sccp.data == b'' dpkt-1.9.8/dpkt/sctp.py000066400000000000000000000143771427735150300150030ustar00rootroot00000000000000# $Id: sctp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Stream Control Transmission Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt from . import crc32c # Stream Control Transmission Protocol # http://tools.ietf.org/html/rfc2960 # Chunk Types DATA = 0 INIT = 1 INIT_ACK = 2 SACK = 3 HEARTBEAT = 4 HEARTBEAT_ACK = 5 ABORT = 6 SHUTDOWN = 7 SHUTDOWN_ACK = 8 ERROR = 9 COOKIE_ECHO = 10 COOKIE_ACK = 11 ECNE = 12 CWR = 13 SHUTDOWN_COMPLETE = 14 class SCTP(dpkt.Packet): """Stream Control Transmission Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of SCTP. TODO. """ __hdr__ = ( ('sport', 'H', 0), ('dport', 'H', 0), ('vtag', 'I', 0), ('sum', 'I', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] while self.data: chunk = Chunk(self.data) l_.append(chunk) if len(chunk) == 0: self.data = b'' break self.data = self.data[len(chunk):] self.chunks = l_ def __len__(self): return self.__hdr_len__ + sum(len(x) for x in self.chunks) def __bytes__(self): l_ = [bytes(x) for x in self.chunks] if self.sum == 0: s = crc32c.add(0xffffffff, self.pack_hdr()) for x in l_: s = crc32c.add(s, x) self.sum = crc32c.done(s) return self.pack_hdr() + b''.join(l_) class Chunk(dpkt.Packet): __hdr__ = ( ('type', 'B', INIT), ('flags', 'B', 0), ('len', 'H', 0) ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) self.data = self.data[:self.len - self.__hdr_len__] self.padding = b'' # optional padding for DATA chunks # SCTP DATA Chunked is padded, 4-bytes aligned if self.type == DATA and self.len % 4: plen = 4 - self.len % 4 # padded length if plen: pos = self.__hdr_len__ + len(self.data) # end of data in buf self.padding = buf[pos:pos + plen] def __len__(self): return self.len + len(self.padding) def __bytes__(self): return self.pack_hdr() + bytes(self.data) + self.padding __s = (b'\x80\x44\x00\x50\x00\x00\x00\x00\x30\xba\xef\x54\x01\x00\x00\x3c\x3b\xb9\x9c\x46\x00\x01' b'\xa0\x00\x00\x0a\xff\xff\x2b\x2d\x7e\xb2\x00\x05\x00\x08\x9b\xe6\x18\x9b\x00\x05\x00\x08' b'\x9b\xe6\x18\x9c\x00\x0c\x00\x06\x00\x05\x00\x00\x80\x00\x00\x04\xc0\x00\x00\x04\xc0\x06' b'\x00\x08\x00\x00\x00\x00') def test_sctp_pack(): sctp = SCTP(__s) assert (__s == bytes(sctp)) sctp.sum = 0 assert (__s == bytes(sctp)) def test_sctp_unpack(): sctp = SCTP(__s) assert (sctp.sport == 32836) assert (sctp.dport == 80) assert (len(sctp.chunks) == 1) assert (len(sctp) == 72) chunk = sctp.chunks[0] assert (chunk.type == INIT) assert (chunk.len == 60) def test_sctp_data_chunk(): # https://github.com/kbandla/dpkt/issues/499 # packet 5 from 'sctp-www.cap' downloaded from https://wiki.wireshark.org/SampleCaptures # chunk len == 419 so requires padding to a 4-byte boundary d = (b'\x80\x44\x00\x50\xd2\x6a\xc1\xe5\x70\xe5\x5b\x4c\x00\x03\x01\xa3\x2b\x2d\x7e\xb2\x00\x00' b'\x00\x00\x00\x00\x00\x00\x47\x45\x54\x20\x2f\x20\x48\x54\x54\x50\x2f\x31\x2e\x31\x0d\x0a' b'\x48\x6f\x73\x74\x3a\x20\x32\x30\x33\x2e\x32\x35\x35\x2e\x32\x35\x32\x2e\x31\x39\x34\x0d' b'\x0a\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35' b'\x2e\x30\x20\x28\x58\x31\x31\x3b\x20\x55\x3b\x20\x4c\x69\x6e\x75\x78\x20\x69\x36\x38\x36' b'\x3b\x20\x6b\x6f\x2d\x4b\x52\x3b\x20\x72\x76\x3a\x31\x2e\x37\x2e\x31\x32\x29\x20\x47\x65' b'\x63\x6b\x6f\x2f\x32\x30\x30\x35\x31\x30\x30\x37\x20\x44\x65\x62\x69\x61\x6e\x2f\x31\x2e' b'\x37\x2e\x31\x32\x2d\x31\x0d\x0a\x41\x63\x63\x65\x70\x74\x3a\x20\x74\x65\x78\x74\x2f\x78' b'\x6d\x6c\x2c\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x6d\x6c\x2c\x61\x70\x70' b'\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x68\x74\x6d\x6c\x2b\x78\x6d\x6c\x2c\x74\x65\x78' b'\x74\x2f\x68\x74\x6d\x6c\x3b\x71\x3d\x30\x2e\x39\x2c\x74\x65\x78\x74\x2f\x70\x6c\x61\x69' b'\x6e\x3b\x71\x3d\x30\x2e\x38\x2c\x69\x6d\x61\x67\x65\x2f\x70\x6e\x67\x2c\x2a\x2f\x2a\x3b' b'\x71\x3d\x30\x2e\x35\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x4c\x61\x6e\x67\x75\x61\x67\x65' b'\x3a\x20\x6b\x6f\x2c\x65\x6e\x2d\x75\x73\x3b\x71\x3d\x30\x2e\x37\x2c\x65\x6e\x3b\x71\x3d' b'\x30\x2e\x33\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x45\x6e\x63\x6f\x64\x69\x6e\x67\x3a\x20' b'\x67\x7a\x69\x70\x2c\x64\x65\x66\x6c\x61\x74\x65\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x43' b'\x68\x61\x72\x73\x65\x74\x3a\x20\x45\x55\x43\x2d\x4b\x52\x2c\x75\x74\x66\x2d\x38\x3b\x71' b'\x3d\x30\x2e\x37\x2c\x2a\x3b\x71\x3d\x30\x2e\x37\x0d\x0a\x4b\x65\x65\x70\x2d\x41\x6c\x69' b'\x76\x65\x3a\x20\x33\x30\x30\x0d\x0a\x43\x6f\x6e\x6e\x65\x63\x74\x69\x6f\x6e\x3a\x20\x6b' b'\x65\x65\x70\x2d\x61\x6c\x69\x76\x65\x0d\x0a\x0d\x0a\x00') # <-- ends with \x00 padding sctp = SCTP(d) assert sctp.chunks assert len(sctp.chunks) == 1 ch = sctp.chunks[0] assert ch.type == DATA assert ch.len == 419 assert len(ch) == 420 # 419 +1 byte padding assert ch.data[-14:] == b'keep-alive\r\n\r\n' # no padding byte at the end # no remaining sctp data assert sctp.data == b'' # test packing of the padded chunk assert bytes(ch) == d[SCTP.__hdr_len__:] def test_malformed_sctp_data_chunk(): # packet 7964 from '4.pcap' downloaded from https://research.unsw.edu.au/projects/unsw-nb15-dataset d = (b'\x27\x0f\xe1\xc3\xc2\x73\x4d\x32\x4f\x54\x27\x8c' #header b'\x0b\x00\x00\x04' #chunk 0, COOKIE_ACK chunk b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') #chunk 1, malformed DATA chunk, size labeled as 0 sctp = SCTP(d) assert sctp.chunks assert len(sctp.chunks) == 2 ch = sctp.chunks[1] assert ch.type == DATA assert ch.len == 0 assert len(ch) == 0 assert ch.data == b'\x00\x00' # no remaining sctp data assert sctp.data == b'' dpkt-1.9.8/dpkt/sip.py000066400000000000000000000022141427735150300146100ustar00rootroot00000000000000# $Id: sip.py 48 2008-05-27 17:31:15Z yardley $ # -*- coding: utf-8 -*- """Session Initiation Protocol.""" from __future__ import absolute_import from . import http class Request(http.Request): """SIP request. TODO: Longer class information.... Attributes: __hdr__: Header fields of SIP request. TODO. """ __hdr_defaults__ = { 'method': 'INVITE', 'uri': 'sip:user@example.com', 'version': '2.0', 'headers': {'To': '', 'From': '', 'Call-ID': '', 'CSeq': '', 'Contact': ''} } __methods = dict.fromkeys(( 'ACK', 'BYE', 'CANCEL', 'INFO', 'INVITE', 'MESSAGE', 'NOTIFY', 'OPTIONS', 'PRACK', 'PUBLISH', 'REFER', 'REGISTER', 'SUBSCRIBE', 'UPDATE' )) __proto = 'SIP' class Response(http.Response): """SIP response. TODO: Longer class information.... Attributes: __hdr__: Header fields of SIP response. TODO. """ __hdr_defaults__ = { 'version': '2.0', 'status': '200', 'reason': 'OK', 'headers': {'To': '', 'From': '', 'Call-ID': '', 'CSeq': '', 'Contact': ''} } __proto = 'SIP' dpkt-1.9.8/dpkt/sll.py000066400000000000000000000033151427735150300146120ustar00rootroot00000000000000# $Id: sll.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Linux libpcap "cooked" capture encapsulation.""" from __future__ import absolute_import from . import arp from . import dpkt from . import ethernet class SLL(dpkt.Packet): """Linux libpcap "cooked" capture encapsulation. TODO: Longer class information.... Attributes: __hdr__: Header fields of SLL. TODO. """ __hdr__ = ( ('type', 'H', 0), # 0: to us, 1: bcast, 2: mcast, 3: other, 4: from us ('hrd', 'H', arp.ARP_HRD_ETH), ('hlen', 'H', 6), # hardware address length ('hdr', '8s', b''), # first 8 bytes of link-layer header ('ethtype', 'H', ethernet.ETH_TYPE_IP), ) _typesw = ethernet.Ethernet._typesw def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: self.data = self._typesw[self.ethtype](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): pass def test_sll(): slldata = (b'\x00\x00\x00\x01\x00\x06\x00\x0b\xdb\x52\x0e\x08\xf6\x7f\x08\x00\x45\x00\x00\x34' b'\xcc\x6c\x40\x00\x40\x06\x74\x08\x82\xd9\xfa\x8e\x82\xd9\xfa\x0d') slltest = SLL(slldata) assert slltest.type == 0 assert slltest.hrd == 1 assert slltest.hlen == 6 assert slltest.hdr == b'\x00\x0b\xdb\x52\x0e\x08\xf6\x7f' assert slltest.ethtype == 0x0800 # give invalid ethtype of 0x1234 to make sure error is caught slldata2 = (b'\x00\x00\x00\x01\x00\x06\x00\x0b\xdb\x52\x0e\x08\xf6\x7f\x12\x34\x45\x00\x00\x34' b'\xcc\x6c\x40\x00\x40\x06\x74\x08\x82\xd9\xfa\x8e\x82\xd9\xfa\x0d') slltest = SLL(slldata2) dpkt-1.9.8/dpkt/sll2.py000066400000000000000000000036561427735150300147040ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Linux libpcap "cooked v2" capture encapsulation.""" from __future__ import absolute_import from . import arp from . import dpkt from . import ethernet class SLL2(dpkt.Packet): """Linux libpcap "cooked v2" capture encapsulation. See https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html Attributes: __hdr__: Header fields of SLLv2. """ __hdr__ = ( ('ethtype', 'H', ethernet.ETH_TYPE_IP), ('mbz', 'H', 0), # reserved ('intindex', 'i', 0), # the 1-based index of the interface on which the packet was observed ('hrd', 'H', arp.ARP_HRD_ETH), ('type', 'B', 0), # 0: to us, 1: bcast, 2: mcast, 3: other, 4: from us ('hlen', 'B', 6), # hardware address length ('hdr', '8s', b''), # first 8 bytes of link-layer header ) _typesw = ethernet.Ethernet._typesw def unpack(self, buf): dpkt.Packet.unpack(self, buf) try: self.data = self._typesw[self.ethtype](self.data) setattr(self, self.data.__class__.__name__.lower(), self.data) except (KeyError, dpkt.UnpackError): pass def test_sll2(): sll2data = (b'\x08\x00\x00\x00\x00\x00\x00\x03\x00\x01\x00\x06\x00\x0b\xdb\x52\x0e\x08\xf6\x7f' b'\x45\x00\x00\x34\xcc\x6c\x40\x00\x40\x06\x74\x08\x82\xd9\xfa\x8e\x82\xd9\xfa\x0d') sll2test = SLL2(sll2data) assert sll2test.type == 0 assert sll2test.mbz == 0 assert sll2test.intindex == 3 assert sll2test.hrd == 1 assert sll2test.hlen == 6 assert sll2test.hdr == b'\x00\x0b\xdb\x52\x0e\x08\xf6\x7f' assert sll2test.ethtype == 0x0800 # give invalid ethtype of 0x1234 to make sure error is handled sll2data2 = (b'\x12\x34\x00\x00\x00\x00\x00\x03\x00\x01\x00\x06\x00\x0b\xdb\x52\x0e\x08\xf6\x7f' b'\x45\x00\x00\x34\xcc\x6c\x40\x00\x40\x06\x74\x08\x82\xd9\xfa\x8e\x82\xd9\xfa\x0d') sll2test2 = SLL2(sll2data2) dpkt-1.9.8/dpkt/smb.py000066400000000000000000000072221427735150300146020ustar00rootroot00000000000000# $Id: smb.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Server Message Block.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt # https://msdn.microsoft.com/en-us/library/ee441774.aspx SMB_FLAGS_LOCK_AND_READ_OK = 0x01 SMB_FLAGS_BUF_AVAIL = 0x02 SMB_FLAGS_CASE_INSENSITIVE = 0x08 SMB_FLAGS_CANONICALIZED_PATHS = 0x10 SMB_FLAGS_OPLOCK = 0x20 SMB_FLAGS_OPBATCH = 0x40 SMB_FLAGS_REPLY = 0x80 SMB_FLAGS2_LONG_NAMES = 0x0001 SMB_FLAGS2_EXTENDED_ATTRIBUTES = 0x0002 SMB_FLAGS2_SECURITY_SIGNATURES = 0x0004 SMB_FLAGS2_COMPRESSED = 0x0008 SMB_FLAGS2_SECURITY_SIGNATURES_REQUIRED = 0x0010 SMB_FLAGS2_IS_LONG_NAME = 0x0040 SMB_FLAGS2_REVERSE_PATH = 0x0400 SMB_FLAGS2_EXTENDED_SECURITY = 0x0800 SMB_FLAGS2_DFS = 0x1000 SMB_FLAGS2_PAGING_IO = 0x2000 SMB_FLAGS2_NT_STATUS = 0x4000 SMB_FLAGS2_UNICODE = 0x8000 SMB_STATUS_SUCCESS = 0x00000000 class SMB(dpkt.Packet): r"""Server Message Block. Server Message Block (SMB) is a communication protocol[1] that Microsoft created for providing shared access to files and printers across nodes on a network. It also provides an authenticated inter-process communication (IPC) mechanism. Attributes: __hdr__: SMB Headers proto: (bytes): Protocol. This field MUST contain the 4-byte literal string '\xFF', 'S', 'M', 'B' (4 bytes) cmd: (int): Command. Defines SMB command. (1 byte) status: (int): Status. Communicates error messages from the server to the client. (4 bytes) flags: (int): Flags. Describes various features in effect for the message.(1 byte) flags2: (int): Flags2. Represent various features in effect for the message. Unspecified bits are reserved and MUST be zero. (2 bytes) _pidhi: (int): PIDHigh. Represents the high-order bytes of a process identifier (PID) (2 bytes) security: (bytes): SecurityFeatures. Has three possible interpretations. (8 bytes) rsvd: (int): Reserved. This field is reserved and SHOULD be set to 0x0000. (2 bytes) tid: (int): TID. A tree identifier (TID). (2 bytes) _pidlo: (int): PIDLow. The lower 16-bits of the PID. (2 bytes) uid: (int): UID. A user identifier (UID). (2 bytes) mid: (int): MID. A multiplex identifier (MID).(2 bytes) """ __byte_order__ = '<' __hdr__ = [ ('proto', '4s', b'\xffSMB'), ('cmd', 'B', 0), ('status', 'I', SMB_STATUS_SUCCESS), ('flags', 'B', 0), ('flags2', 'H', 0), ('_pidhi', 'H', 0), ('security', '8s', b''), ('rsvd', 'H', 0), ('tid', 'H', 0), ('_pidlo', 'H', 0), ('uid', 'H', 0), ('mid', 'H', 0) ] @property def pid(self): return (self._pidhi << 16) | self._pidlo @pid.setter def pid(self, v): self._pidhi = v >> 16 self._pidlo = v & 0xffff def test_smb(): buf = (b'\xffSMB\xa0\x00\x00\x00\x00\x08\x03\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x08\xfa\x7a\x00\x08\x53\x02') smb = SMB(buf) assert smb.flags == SMB_FLAGS_CASE_INSENSITIVE assert smb.flags2 == (SMB_FLAGS2_UNICODE | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_EXTENDED_SECURITY | SMB_FLAGS2_EXTENDED_ATTRIBUTES | SMB_FLAGS2_LONG_NAMES) assert smb.pid == 31482 assert smb.uid == 2048 assert smb.mid == 595 print(repr(smb)) smb = SMB() smb.pid = 0x00081020 smb.uid = 0x800 assert str(smb) == str(b'\xffSMB\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x20\x10\x00\x08\x00\x00') dpkt-1.9.8/dpkt/snoop.py000066400000000000000000000352571427735150300151700ustar00rootroot00000000000000# $Id$ # -*- coding: utf-8 -*- """Snoop file format.""" from __future__ import absolute_import import time from abc import abstractmethod from . import dpkt from .compat import intround # RFC 1761 SNOOP_MAGIC = 0x736E6F6F70000000 SNOOP_VERSION = 2 SDL_8023 = 0 SDL_8024 = 1 SDL_8025 = 2 SDL_8026 = 3 SDL_ETHER = 4 SDL_HDLC = 5 SDL_CHSYNC = 6 SDL_IBMCC = 7 SDL_FDDI = 8 SDL_OTHER = 9 dltoff = {SDL_ETHER: 14} class PktHdr(dpkt.Packet): """snoop packet header. TODO: Longer class information.... Attributes: __hdr__: Header fields of snoop packet header. TODO. """ __byte_order__ = '!' __hdr__ = ( # 32-bit unsigned integer representing the length in octets of the # captured packet as received via a network. ('orig_len', 'I', 0), # 32-bit unsigned integer representing the length of the Packet Data # field. This is the number of octets of the captured packet that are # included in this packet record. If the received packet was # truncated, the Included Length field will be less than the Original # Length field. ('incl_len', 'I', 0), # 32-bit unsigned integer representing the total length of this packet # record in octets. This includes the 24 octets of descriptive # information, the length of the Packet Data field, and the length of # the Pad field. ('rec_len', 'I', 0), # 32-bit unsigned integer representing the number of packets that were # lost by the system that created the packet file between the first # packet record in the file and this one. Packets may be lost because # of insufficient resources in the capturing system, or for other # reasons. Note: some implementations lack the ability to count # dropped packets. Those implementations may set the cumulative drops # value to zero. ('cum_drops', 'I', 0), # 32-bit unsigned integer representing the time, in seconds since # January 1, 1970, when the packet arrived. ('ts_sec', 'I', 0), # 32-bit unsigned integer representing microsecond resolution of packet # arrival time. ('ts_usec', 'I', 0), ) class FileHdr(dpkt.Packet): """snoop file header. TODO: Longer class information.... Attributes: __hdr__: Header fields of snoop file header. TODO. """ __byte_order__ = '!' __hdr__ = ( ('magic', 'Q', SNOOP_MAGIC), ('v', 'I', SNOOP_VERSION), ('linktype', 'I', SDL_ETHER), ) class FileWriter(object): def __init__(self, fileobj): self._f = fileobj self.write = self._f.write def close(self): self._f.close() def writepkt(self, pkt, ts=None): """Write single packet and optional timestamp to file. Args: pkt: `bytes` will be called on this and written to file. ts (float): Timestamp in seconds. Defaults to current time. """ if ts is None: ts = time.time() self.writepkt_time(bytes(pkt), ts) @abstractmethod def writepkt_time(self, pkt, ts): """Write single packet and its timestamp to file. Args: pkt (bytes): Some `bytes` to write to the file ts (float): Timestamp in seconds """ pass class Writer(FileWriter): """Simple snoop dumpfile writer. TODO: Longer class information.... Attributes: TODO. """ precision_multiplier = 1000000 def __init__(self, fileobj, linktype=SDL_ETHER): super(Writer, self).__init__(fileobj) fh = FileHdr(linktype=linktype) self._PktHdr = PktHdr() self._pack_hdr = self._PktHdr._pack_hdr self.write(bytes(fh)) def writepkt_time(self, pkt, ts): """Write single packet and its timestamp to file. Args: pkt (bytes): Some `bytes` to write to the file ts (float): Timestamp in seconds """ pkt_len = len(pkt) pad_len = (4 - pkt_len) & 3 pkt_header = self._pack_hdr( pkt_len, pkt_len, PktHdr.__hdr_len__ + pkt_len + pad_len, 0, int(ts), intround(ts % 1 * self.precision_multiplier), ) self.write(pkt_header + pkt + b'\x00' * pad_len) def writepkts(self, pkts): """Write an iterable of packets to file. Timestamps should be in seconds. Packets must be of type `bytes` as they will not be cast. Args: pkts: iterable containing (ts, pkt) """ # take local references to these variables so we don't need to # dereference every time in the loop write = self.write pack_hdr = self._pack_hdr for ts, pkt in pkts: pkt_len = len(pkt) pad_len = (4 - pkt_len) & 3 pkt_header = pack_hdr( pkt_len, pkt_len, PktHdr.__hdr_len__ + pkt_len + pad_len, 0, int(ts), intround(ts % 1 * self.precision_multiplier), ) write(pkt_header + pkt + b'\x00' * pad_len) class FileReader(object): def __init__(self, fileobj): self.name = getattr(fileobj, 'name', '<%s>' % fileobj.__class__.__name__) self._f = fileobj self.filter = '' @property def fd(self): return self._f.fileno() def fileno(self): return self.fd def setfilter(self, value, optimize=1): raise NotImplementedError def readpkts(self): return list(self) def dispatch(self, cnt, callback, *args): """Collect and process packets with a user callback. Return the number of packets processed, or 0 for a savefile. Arguments: cnt -- number of packets to process; or 0 to process all packets until EOF callback -- function with (timestamp, pkt, *args) prototype *args -- optional arguments passed to callback on execution """ processed = 0 if cnt > 0: for _ in range(cnt): try: ts, pkt = next(self) except StopIteration: break callback(ts, pkt, *args) processed += 1 else: for ts, pkt in self: callback(ts, pkt, *args) processed += 1 return processed def loop(self, callback, *args): """ Convenience method which will apply the callback to all packets. Returns the number of packets processed. Arguments: callback -- function with (timestamp, pkt, *args) prototype *args -- optional arguments passed to callback on execution """ return self.dispatch(0, callback, *args) def __iter__(self): return self class Reader(FileReader): """Simple pypcap-compatible snoop file reader. TODO: Longer class information.... Attributes: TODO. """ def __init__(self, fileobj): super(Reader, self).__init__(fileobj) buf = self._f.read(FileHdr.__hdr_len__) self._fh = FileHdr(buf) self._ph = PktHdr if self._fh.magic != SNOOP_MAGIC: raise ValueError('invalid snoop header') self.dloff = dltoff[self._fh.linktype] def datalink(self): return self._fh.linktype def __next__(self): buf = self._f.read(self._ph.__hdr_len__) if not buf: raise StopIteration hdr = self._ph(buf) buf = self._f.read(hdr.rec_len - self._ph.__hdr_len__) return (hdr.ts_sec + (hdr.ts_usec / 1000000.0), buf[:hdr.incl_len]) next = __next__ def test_snoop_pkt_header(): from binascii import unhexlify buf = unhexlify( '000000010000000200000003000000040000000500000006' ) pkt = PktHdr(buf) assert pkt.orig_len == 1 assert pkt.incl_len == 2 assert pkt.rec_len == 3 assert pkt.cum_drops == 4 assert pkt.ts_sec == 5 assert pkt.ts_usec == 6 assert bytes(pkt) == buf def test_snoop_file_header(): from binascii import unhexlify buf = unhexlify( '000000000000000b000000160000014d' ) hdr = FileHdr(buf) assert hdr.magic == 11 assert hdr.v == 22 assert hdr.linktype == 333 class TestSnoopWriter(object): @classmethod def setup_class(cls): from .compat import BytesIO from binascii import unhexlify cls.fobj = BytesIO() # write the file header only cls.writer = Writer(cls.fobj) cls.file_header = unhexlify( '736e6f6f700000000000000200000004' ) cls.pkt = unhexlify( '000000010000000200000003000000040000000500000006' ) cls.pkt_and_header = unhexlify( '00000018' # orig_len '00000018' # incl_len '00000030' # rec_len '00000000' # cum_drops '00000000' # ts_sec '00000000' # ts_usec # data '000000010000000200000003000000040000000500000006' ) def test_snoop_file_writer_filehdr(self): # jump to the start and read the file header self.fobj.seek(0) buf = self.fobj.read() assert buf == self.file_header def test_writepkt(self): loc = self.fobj.tell() self.writer.writepkt(self.pkt) # jump back to just before the writing of the packet self.fobj.seek(loc) # read the packet back in buf = self.fobj.read() # compare everything except the timestamp assert buf[:16] == self.pkt_and_header[:16] assert buf[24:] == self.pkt_and_header[24:] def test_writepkt_time(self): loc = self.fobj.tell() self.writer.writepkt_time(self.pkt, 0) self.fobj.seek(loc) # read the packet we just wrote buf = self.fobj.read() assert buf == self.pkt_and_header def test_writepkts(self): loc = self.fobj.tell() self.writer.writepkts([ (0, self.pkt), (1, self.pkt), (2, self.pkt), ]) self.fobj.seek(loc) buf = self.fobj.read() pkt_len = len(self.pkt_and_header) # chunk up the file and check each packet for idx in range(0, 3): pkt = buf[idx * pkt_len:(idx + 1) * pkt_len] assert pkt[:16] == self.pkt_and_header[:16] assert pkt[16:20] == dpkt.struct.pack('>I', idx) assert pkt[20:] == self.pkt_and_header[20:] def test_snoop_writer_close(self): assert not self.fobj.closed # check that the underlying file object is closed self.writer.close() assert self.fobj.closed class TestSnoopReader(object): @classmethod def setup_class(cls): from binascii import unhexlify cls.header = unhexlify( '736e6f6f700000000000000200000004' ) cls.pkt_header = unhexlify( '00000018' # orig_len '00000018' # incl_len '00000030' # rec_len '00000000' # cum_drops '00000000' # ts_sec '00000000' # ts_usec ) cls.pkt_bytes = unhexlify( # data '000000010000000200000003000000040000000500000006' ) def setup_method(self): from .compat import BytesIO self.fobj = BytesIO( self.header + self.pkt_header + self.pkt_bytes ) self.reader = Reader(self.fobj) def test_open(self): assert self.reader.dloff == 14 assert self.reader.datalink() == SDL_ETHER def test_invalid_magic(self): import pytest self.fobj.seek(0) self.fobj.write(b'\x00' * 4) self.fobj.seek(0) with pytest.raises(ValueError, match='invalid snoop header'): Reader(self.fobj) def test_read_pkt(self): ts, pkt = next(self.reader) assert ts == 0 assert pkt == self.pkt_bytes def test_readpkts(self): pkts = self.reader.readpkts() assert len(pkts) == 1 ts, buf = pkts[0] assert ts == 0 assert buf == self.pkt_bytes class TestFileWriter(object): def setup_method(self): from .compat import BytesIO self.fobj = BytesIO() self.writer = FileWriter(self.fobj) def test_write(self): buf = b'\x01' * 10 self.writer.write(buf) self.fobj.seek(0) assert self.fobj.read() == buf def test_close(self): assert not self.fobj.closed self.writer.close() assert self.fobj.closed class TestFileReader(object): """ Testing for the FileReader superclass which Reader inherits from. """ pkts = [ (0, b'000001'), (1, b'000002'), (2, b'000003'), ] class SampleReader(FileReader): """ Very simple class which returns index as timestamp, and unparsed buffer as packet """ def __init__(self, fobj): super(TestFileReader.SampleReader, self).__init__(fobj) self._iter = iter(TestFileReader.pkts) def __next__(self): return next(self._iter) next = __next__ def setup_method(self): import tempfile self.fd = tempfile.TemporaryFile() self.reader = self.SampleReader(self.fd) def test_attributes(self): import pytest assert self.reader.name == self.fd.name assert self.reader.fd == self.fd.fileno() assert self.reader.fileno() == self.fd.fileno() assert self.reader.filter == '' with pytest.raises(NotImplementedError): self.reader.setfilter(1, 2) def test_readpkts_list(self): pkts = self.reader.readpkts() print(len(pkts)) for idx, (ts, buf) in enumerate(pkts): assert ts == idx assert buf == self.pkts[idx][1] def test_readpkts_iter(self): for idx, (ts, buf) in enumerate(self.reader): assert ts == idx assert buf == self.pkts[idx][1] def test_dispatch_all(self): assert self.reader.dispatch(0, lambda ts, pkt: None) == 3 def test_dispatch_some(self): assert self.reader.dispatch(2, lambda ts, pkt: None) == 2 def test_dispatch_termination(self): assert self.reader.dispatch(20, lambda ts, pkt: None) == 3 def test_loop(self): class Count: counter = 0 @classmethod def inc(cls): cls.counter += 1 assert self.reader.loop(lambda ts, pkt: Count.inc()) == 3 assert Count.counter == 3 def test_next(self): ts, buf = next(self.reader) assert ts == 0 assert buf == self.pkts[0][1] dpkt-1.9.8/dpkt/ssl.py000066400000000000000000001410551427735150300146250ustar00rootroot00000000000000# $Id: ssl.py 90 2014-04-02 22:06:23Z andrewflnr@gmail.com $ # Portion Copyright 2012 Google Inc. All rights reserved. # -*- coding: utf-8 -*- """Secure Sockets Layer / Transport Layer Security.""" from __future__ import absolute_import import struct import binascii from . import dpkt from . import ssl_ciphersuites from .compat import compat_ord from .utils import deprecation_warning # # Note from April 2011: cde...@gmail.com added code that parses SSL3/TLS messages more in depth. # # Jul 2012: afleenor@google.com modified and extended SSL support further. # # SSL 2.0 is deprecated in RFC 6176 class SSL2(dpkt.Packet): __hdr__ = ( ('len', 'H', 0), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) # In SSL, all data sent is encapsulated in a record, an object which is # composed of a header and some non-zero amount of data. Each record header # contains a two or three byte length code. If the most significant bit is # set in the first byte of the record length code then the record has # no padding and the total header length will be 2 bytes, otherwise the # record has padding and the total header length will be 3 bytes. The # record header is transmitted before the data portion of the record. if self.len & 0x8000: n = self.len = self.len & 0x7FFF self.msg, self.data = self.data[:n], self.data[n:] else: # Note that in the long header case (3 bytes total), the second most # significant bit in the first byte has special meaning. When zero, # the record being sent is a data record. When one, the record # being sent is a security escape (there are currently no examples # of security escapes; this is reserved for future versions of the # protocol). In either case, the length code describes how much # data is in the record. n = self.len = self.len & 0x3FFF padlen = compat_ord(self.data[0]) self.msg = self.data[1:1 + n] self.pad = self.data[1 + n:1 + n + padlen] self.data = self.data[1 + n + padlen:] # SSL 3.0 is deprecated in RFC 7568 # Use class TLS for >= SSL 3.0 class TLS(dpkt.Packet): __hdr__ = ( ('type', 'B', ''), ('version', 'H', ''), ('len', 'H', ''), ) def __init__(self, *args, **kwargs): self.records = [] dpkt.Packet.__init__(self, *args, **kwargs) def unpack(self, buf): dpkt.Packet.unpack(self, buf) pointer = 0 while len(self.data[pointer:]) > 0: end = pointer + 5 + struct.unpack("!H", buf[pointer + 3:pointer + 5])[0] self.records.append(TLSRecord(buf[pointer:end])) pointer = end self.data = self.data[pointer:] # SSLv3/TLS versions SSL3_V = 0x0300 TLS1_V = 0x0301 TLS11_V = 0x0302 TLS12_V = 0x0303 ssl3_versions_str = { SSL3_V: 'SSL3', TLS1_V: 'TLS 1.0', TLS11_V: 'TLS 1.1', TLS12_V: 'TLS 1.2' } SSL3_VERSION_BYTES = set((b'\x03\x00', b'\x03\x01', b'\x03\x02', b'\x03\x03')) # Alert levels SSL3_AD_WARNING = 1 SSL3_AD_FATAL = 2 alert_level_str = { SSL3_AD_WARNING: 'SSL3_AD_WARNING', SSL3_AD_FATAL: 'SSL3_AD_FATAL' } # SSL3 alert descriptions SSL3_AD_CLOSE_NOTIFY = 0 SSL3_AD_UNEXPECTED_MESSAGE = 10 # fatal SSL3_AD_BAD_RECORD_MAC = 20 # fatal SSL3_AD_DECOMPRESSION_FAILURE = 30 # fatal SSL3_AD_HANDSHAKE_FAILURE = 40 # fatal SSL3_AD_NO_CERTIFICATE = 41 SSL3_AD_BAD_CERTIFICATE = 42 SSL3_AD_UNSUPPORTED_CERTIFICATE = 43 SSL3_AD_CERTIFICATE_REVOKED = 44 SSL3_AD_CERTIFICATE_EXPIRED = 45 SSL3_AD_CERTIFICATE_UNKNOWN = 46 SSL3_AD_ILLEGAL_PARAMETER = 47 # fatal # TLS1 alert descriptions TLS1_AD_DECRYPTION_FAILED = 21 TLS1_AD_RECORD_OVERFLOW = 22 TLS1_AD_UNKNOWN_CA = 48 # fatal TLS1_AD_ACCESS_DENIED = 49 # fatal TLS1_AD_DECODE_ERROR = 50 # fatal TLS1_AD_DECRYPT_ERROR = 51 TLS1_AD_EXPORT_RESTRICTION = 60 # fatal TLS1_AD_PROTOCOL_VERSION = 70 # fatal TLS1_AD_INSUFFICIENT_SECURITY = 71 # fatal TLS1_AD_INTERNAL_ERROR = 80 # fatal TLS1_AD_USER_CANCELLED = 90 TLS1_AD_NO_RENEGOTIATION = 100 # /* codes 110-114 are from RFC3546 */ TLS1_AD_UNSUPPORTED_EXTENSION = 110 TLS1_AD_CERTIFICATE_UNOBTAINABLE = 111 TLS1_AD_UNRECOGNIZED_NAME = 112 TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE = 113 TLS1_AD_BAD_CERTIFICATE_HASH_VALUE = 114 TLS1_AD_UNKNOWN_PSK_IDENTITY = 115 # fatal # Mapping alert types to strings alert_description_str = { SSL3_AD_CLOSE_NOTIFY: 'SSL3_AD_CLOSE_NOTIFY', SSL3_AD_UNEXPECTED_MESSAGE: 'SSL3_AD_UNEXPECTED_MESSAGE', SSL3_AD_BAD_RECORD_MAC: 'SSL3_AD_BAD_RECORD_MAC', SSL3_AD_DECOMPRESSION_FAILURE: 'SSL3_AD_DECOMPRESSION_FAILURE', SSL3_AD_HANDSHAKE_FAILURE: 'SSL3_AD_HANDSHAKE_FAILURE', SSL3_AD_NO_CERTIFICATE: 'SSL3_AD_NO_CERTIFICATE', SSL3_AD_BAD_CERTIFICATE: 'SSL3_AD_BAD_CERTIFICATE', SSL3_AD_UNSUPPORTED_CERTIFICATE: 'SSL3_AD_UNSUPPORTED_CERTIFICATE', SSL3_AD_CERTIFICATE_REVOKED: 'SSL3_AD_CERTIFICATE_REVOKED', SSL3_AD_CERTIFICATE_EXPIRED: 'SSL3_AD_CERTIFICATE_EXPIRED', SSL3_AD_CERTIFICATE_UNKNOWN: 'SSL3_AD_CERTIFICATE_UNKNOWN', SSL3_AD_ILLEGAL_PARAMETER: 'SSL3_AD_ILLEGAL_PARAMETER', TLS1_AD_DECRYPTION_FAILED: 'TLS1_AD_DECRYPTION_FAILED', TLS1_AD_RECORD_OVERFLOW: 'TLS1_AD_RECORD_OVERFLOW', TLS1_AD_UNKNOWN_CA: 'TLS1_AD_UNKNOWN_CA', TLS1_AD_ACCESS_DENIED: 'TLS1_AD_ACCESS_DENIED', TLS1_AD_DECODE_ERROR: 'TLS1_AD_DECODE_ERROR', TLS1_AD_DECRYPT_ERROR: 'TLS1_AD_DECRYPT_ERROR', TLS1_AD_EXPORT_RESTRICTION: 'TLS1_AD_EXPORT_RESTRICTION', TLS1_AD_PROTOCOL_VERSION: 'TLS1_AD_PROTOCOL_VERSION', TLS1_AD_INSUFFICIENT_SECURITY: 'TLS1_AD_INSUFFICIENT_SECURITY', TLS1_AD_INTERNAL_ERROR: 'TLS1_AD_INTERNAL_ERROR', TLS1_AD_USER_CANCELLED: 'TLS1_AD_USER_CANCELLED', TLS1_AD_NO_RENEGOTIATION: 'TLS1_AD_NO_RENEGOTIATION', TLS1_AD_UNSUPPORTED_EXTENSION: 'TLS1_AD_UNSUPPORTED_EXTENSION', TLS1_AD_CERTIFICATE_UNOBTAINABLE: 'TLS1_AD_CERTIFICATE_UNOBTAINABLE', TLS1_AD_UNRECOGNIZED_NAME: 'TLS1_AD_UNRECOGNIZED_NAME', TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: 'TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE', TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: 'TLS1_AD_BAD_CERTIFICATE_HASH_VALUE', TLS1_AD_UNKNOWN_PSK_IDENTITY: 'TLS1_AD_UNKNOWN_PSK_IDENTITY' } # struct format strings for parsing buffer lengths # don't forget, you have to pad a 3-byte value with \x00 _SIZE_FORMATS = ['!B', '!H', '!I', '!I'] def parse_variable_array(buf, lenbytes): """ Parse an array described using the 'Type name' syntax from the spec Read a length at the start of buf, and returns that many bytes after, in a tuple with the TOTAL bytes consumed (including the size). This does not check that the array is the right length for any given datatype. """ # first have to figure out how to parse length assert lenbytes <= 4 # pretty sure 4 is impossible, too size_format = _SIZE_FORMATS[lenbytes - 1] padding = b'\x00' if lenbytes == 3 else b'' # read off the length size = struct.unpack(size_format, padding + buf[:lenbytes])[0] # read the actual data data = buf[lenbytes:lenbytes + size] # if len(data) != size: insufficient data return data, size + lenbytes def parse_extensions(buf): """ Parse TLS extensions in passed buf. Returns an ordered list of extension tuples with ordinal extension type as first value and extension data as second value. Passed buf must start with the 2-byte extensions length TLV. http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml """ extensions_length = struct.unpack('!H', buf[:2])[0] extensions = [] pointer = 2 while pointer < extensions_length: ext_type = struct.unpack('!H', buf[pointer:pointer + 2])[0] pointer += 2 ext_data, parsed = parse_variable_array(buf[pointer:], 2) extensions.append((ext_type, ext_data)) pointer += parsed return extensions class SSL3Exception(Exception): pass class TLSRecord(dpkt.Packet): """ SSLv3 or TLSv1+ packet. In addition to the fields specified in the header, there are compressed and decrypted fields, indicating whether, in the language of the spec, this is a TLSPlaintext, TLSCompressed, or TLSCiphertext. The application will have to figure out when it's appropriate to change these values. """ __hdr__ = ( ('type', 'B', 0), ('version', 'H', 0), ('length', 'H', 0), ) def __init__(self, *args, **kwargs): # assume plaintext unless specified otherwise in arguments self.compressed = kwargs.pop('compressed', False) self.encrypted = kwargs.pop('encrypted', False) # parent constructor dpkt.Packet.__init__(self, *args, **kwargs) # make sure length and data are consistent self.length = len(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) header_length = self.__hdr_len__ self.data = buf[header_length:header_length + self.length] # make sure buffer was long enough if len(self.data) != self.length: raise dpkt.NeedData('TLSRecord data was too short.') # assume compressed and encrypted when it's been parsed from # raw data self.compressed = True self.encrypted = True class TLSChangeCipherSpec(dpkt.Packet): """ ChangeCipherSpec message is just a single byte with value 1 """ __hdr__ = (('type', 'B', 1),) class TLSAppData(str): """ As far as TLSRecord is concerned, AppData is just an opaque blob. """ pass class TLSAlert(dpkt.Packet): __hdr__ = ( ('level', 'B', 1), ('description', 'B', 0), ) class TLSHelloRequest(dpkt.Packet): __hdr__ = tuple() class TLSClientHello(dpkt.Packet): __hdr__ = ( ('version', 'H', 0x0301), ('random', '32s', '\x00' * 32), ) # the rest is variable-length and has to be done manually def unpack(self, buf): dpkt.Packet.unpack(self, buf) # now session, cipher suites, extensions are in self.data self.session_id, pointer = parse_variable_array(self.data, 1) # handle ciphersuites ciphersuites, parsed = parse_variable_array(self.data[pointer:], 2) pointer += parsed num_ciphersuites = int(len(ciphersuites) / 2) self.ciphersuites = [ ssl_ciphersuites.BY_CODE.get(code, ssl_ciphersuites.get_unknown_ciphersuite(code)) for code in struct.unpack('!' + num_ciphersuites * 'H', ciphersuites)] # check len(ciphersuites) % 2 == 0 ? # compression methods compression_methods, parsed = parse_variable_array(self.data[pointer:], 1) pointer += parsed self.compression_methods = struct.unpack('{0}B'.format(len(compression_methods)), compression_methods) # Parse extensions if present if len(self.data[pointer:]) >= 6: self.extensions = parse_extensions(self.data[pointer:]) class TLSServerHello(dpkt.Packet): __hdr__ = ( ('version', 'H', '0x0301'), ('random', '32s', '\x00' * 32), ) # session is variable, forcing rest to be manual def unpack(self, buf): try: dpkt.Packet.unpack(self, buf) self.session_id, pointer = parse_variable_array(self.data, 1) # single cipher suite code = struct.unpack('!H', self.data[pointer:pointer + 2])[0] self.ciphersuite = \ ssl_ciphersuites.BY_CODE.get(code, ssl_ciphersuites.get_unknown_ciphersuite(code)) pointer += 2 # single compression method self.compression_method = struct.unpack('!B', self.data[pointer:pointer + 1])[0] pointer += 1 # Parse extensions if present if len(self.data[pointer:]) >= 6: self.extensions = parse_extensions(self.data[pointer:]) except struct.error: # probably data too short raise dpkt.NeedData # XXX - legacy, deprecated # for whatever reason these attributes were named differently than their sister attributes in TLSClientHello @property def cipher_suite(self): deprecation_warning("TLSServerHello.cipher_suite is deprecated and renamed to .ciphersuite") return self.ciphersuite @property def compression(self): deprecation_warning("TLSServerHello.compression is deprecated and renamed to .compression_method") return self.compression_method class TLSCertificate(dpkt.Packet): __hdr__ = tuple() def unpack(self, buf): try: dpkt.Packet.unpack(self, buf) all_certs, all_certs_len = parse_variable_array(self.data, 3) self.certificates = [] pointer = 3 while pointer < all_certs_len: cert, parsed = parse_variable_array(self.data[pointer:], 3) self.certificates.append((cert)) pointer += parsed except struct.error: raise dpkt.NeedData class TLSUnknownHandshake(dpkt.Packet): __hdr__ = tuple() TLSNewSessionTicket = TLSUnknownHandshake TLSServerKeyExchange = TLSUnknownHandshake TLSCertificateRequest = TLSUnknownHandshake TLSServerHelloDone = TLSUnknownHandshake TLSCertificateVerify = TLSUnknownHandshake TLSClientKeyExchange = TLSUnknownHandshake TLSFinished = TLSUnknownHandshake # mapping of handshake type ids to their names # and the classes that implement them HANDSHAKE_TYPES = { 0: ('HelloRequest', TLSHelloRequest), 1: ('ClientHello', TLSClientHello), 2: ('ServerHello', TLSServerHello), 4: ('NewSessionTicket', TLSNewSessionTicket), 11: ('Certificate', TLSCertificate), 12: ('ServerKeyExchange', TLSServerKeyExchange), 13: ('CertificateRequest', TLSCertificateRequest), 14: ('ServerHelloDone', TLSServerHelloDone), 15: ('CertificateVerify', TLSCertificateVerify), 16: ('ClientKeyExchange', TLSClientKeyExchange), 20: ('Finished', TLSFinished), } class TLSHandshake(dpkt.Packet): """ A TLS Handshake message This goes for all messages encapsulated in the Record layer, but especially important for handshakes and app data: A message may be spread across a number of TLSRecords, in addition to the possibility of there being more than one in a given Record. You have to put together the contents of TLSRecord's yourself. """ # struct.unpack can't handle the 3-byte int, so we parse it as bytes # (and store it as bytes so dpkt doesn't get confused), and turn it into # an int in a user-facing property __hdr__ = ( ('type', 'B', 0), ('length_bytes', '3s', 0), ) __pprint_funcs__ = { 'length_bytes': lambda x: struct.unpack('!I', b'\x00' + x)[0] } def unpack(self, buf): dpkt.Packet.unpack(self, buf) # Wait, might there be more than one message of self.type? embedded_type = HANDSHAKE_TYPES.get(self.type, None) if embedded_type is None: raise SSL3Exception('Unknown or invalid handshake type %d' % self.type) # only take the right number of bytes self.data = self.data[:self.length] if len(self.data) != self.length: raise dpkt.NeedData # get class out of embedded_type tuple self.data = embedded_type[1](self.data) @property def length(self): return struct.unpack('!I', b'\x00' + self.length_bytes)[0] RECORD_TYPES = { 20: TLSChangeCipherSpec, 21: TLSAlert, 22: TLSHandshake, 23: TLSAppData, } class SSLFactory(object): def __new__(cls, buf): v = buf[1:3] if v in SSL3_VERSION_BYTES: return TLSRecord(buf) # SSL2 has no characteristic header or magic bytes, so we just assume # that the msg is an SSL2 msg if it is not detected as SSL3+ return SSL2(buf) def tls_multi_factory(buf): """ Attempt to parse one or more TLSRecord's out of buf Args: buf: string containing SSL/TLS messages. May have an incomplete record on the end Returns: [TLSRecord] int, total bytes consumed, != len(buf) if an incomplete record was left at the end. Raises SSL3Exception. """ i, n = 0, len(buf) msgs = [] while i + 5 <= n: v = buf[i + 1:i + 3] if v in SSL3_VERSION_BYTES: try: msg = TLSRecord(buf[i:]) msgs.append(msg) except dpkt.NeedData: break else: raise SSL3Exception('Bad TLS version in buf: %r' % buf[i:i + 5]) i += len(msg) return msgs, i _hexdecode = binascii.a2b_hex class TestTLS(object): """ Test basic TLS functionality. Test that each TLSRecord is correctly discovered and added to TLS.records """ @classmethod def setup_class(cls): cls.p = TLS( b'\x16\x03\x00\x02\x06\x01\x00\x02\x02\x03\x03\x58\x5c\x2f\xf7\x2a\x65\x99\x49\x87\x71\xf5' b'\x95\x14\xf1\x0a\xf6\x8c\x68\xf9\xef\x30\xd0\xda\xdc\x9e\x1a\xf6\x4d\x10\x91\x47\x6a\x00' b'\x00\x84\xc0\x2b\xc0\x2c\xc0\x86\xc0\x87\xc0\x09\xc0\x23\xc0\x0a\xc0\x24\xc0\x72\xc0\x73' b'\xc0\x08\xc0\x07\xc0\x2f\xc0\x30\xc0\x8a\xc0\x8b\xc0\x13\xc0\x27\xc0\x14\xc0\x28\xc0\x76' b'\xc0\x77\xc0\x12\xc0\x11\x00\x9c\x00\x9d\xc0\x7a\xc0\x7b\x00\x2f\x00\x3c\x00\x35\x00\x3d' b'\x00\x41\x00\xba\x00\x84\x00\xc0\x00\x0a\x00\x05\x00\x04\x00\x9e\x00\x9f\xc0\x7c\xc0\x7d' b'\x00\x33\x00\x67\x00\x39\x00\x6b\x00\x45\x00\xbe\x00\x88\x00\xc4\x00\x16\x00\xa2\x00\xa3' b'\xc0\x80\xc0\x81\x00\x32\x00\x40\x00\x38\x00\x6a\x00\x44\x00\xbd\x00\x87\x00\xc3\x00\x13' b'\x00\x66\x01\x00\x01\x55\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x11\x00\x0f\x00' b'\x00\x0c\x77\x77\x77\x2e\x69\x61\x6e\x61\x2e\x6f\x72\x67\xff\x01\x00\x01\x00\x00\x23\x00' b'\x00\x00\x0a\x00\x0c\x00\x0a\x00\x13\x00\x15\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01' b'\x00\x00\x0d\x00\x1c\x00\x1a\x04\x01\x04\x02\x04\x03\x05\x01\x05\x03\x06\x01\x06\x03\x03' b'\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x15\x00\xf4\x00\xf2\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\x00\x00\x00' b'\x00\x00\x00\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\x00\x00\x00' b'\x00\x00\x00\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\x00\x00\x00' b'\x00\x00\x00\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\x00\x00\x00' b'\x00\x00\x00\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\x00\x00\x00' b'\x00\x00\x00\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' ) # multiple records in first handshake taken from TLSv1.2 capture with 73 cipher suites # https://bugs.wireshark.org/bugzilla/attachment.cgi?id=11612 # This data is extracted from and verified by Wireshark cls.p2 = TLS( b'\x16\x03\x03\x00\x42\x02\x00\x00\x3e\x03\x03\x52\x36\x2c\x10\xa2' b'\x66\x5e\x32\x3a\x2a\xdb\x4b\x9d\xa0\xc1\x0d\x4a\x88\x23\x71\x92' b'\x72\xf8\xb4\xc9\x7a\xf2\x4f\x92\x78\x48\x12\x00\xc0\x30\x01\x00' b'\x16\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x23' b'\x00\x00\x00\x0f\x00\x01\x01\x16\x03\x03\x01\xc3\x0b\x00\x01\xbf' b'\x00\x01\xbc\x00\x01\xb9\x30\x82\x01\xb5\x30\x82\x01\x1e\x02\x09' b'\x00\xf4\xa7\x2f\xd3\xe8\xfc\x37\xc4\x30\x0d\x06\x09\x2a\x86\x48' b'\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x1f\x31\x1d\x30\x1b\x06\x03' b'\x55\x04\x03\x0c\x14\x54\x65\x73\x74\x20\x43\x65\x72\x74\x69\x66' b'\x69\x63\x61\x74\x65\x20\x52\x53\x41\x30\x1e\x17\x0d\x31\x33\x30' b'\x39\x31\x35\x32\x31\x35\x31\x31\x30\x5a\x17\x0d\x32\x33\x30\x39' b'\x31\x33\x32\x31\x35\x31\x31\x30\x5a\x30\x1f\x31\x1d\x30\x1b\x06' b'\x03\x55\x04\x03\x0c\x14\x54\x65\x73\x74\x20\x43\x65\x72\x74\x69' b'\x66\x69\x63\x61\x74\x65\x20\x52\x53\x41\x30\x81\x9f\x30\x0d\x06' b'\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00' b'\x30\x81\x89\x02\x81\x81\x00\xac\x35\x2a\x93\x7f\xc5\x4f\x18\x98' b'\xb2\x9f\xa0\xfb\x34\xe6\xe2\x8b\x9e\xd7\x46\x91\x07\xd8\x48\x8a' b'\xa8\x43\x8b\xfa\xc0\xff\xb7\xca\xd5\x5f\x58\xbe\xe4\x2f\x20\x1c' b'\x3e\xf9\x42\xf4\xb0\x27\x9a\xb6\xb0\x01\xbf\x97\x40\xaa\xc4\x2a' b'\x1c\xac\x93\x70\xb4\x8e\x94\xda\x38\xcb\xb4\x5e\x14\xb6\xcc\x19' b'\x66\xe8\x06\xf2\x99\xec\x49\x0c\x91\x09\x96\xe6\x9a\xe1\x66\xe5' b'\x84\x64\x2f\xa2\x4c\xe3\x21\xac\x42\x75\xec\x8c\xe9\xf6\xd9\x9e' b'\x40\xcb\x1d\x02\xc3\x8c\x68\xf0\x2b\x46\x1c\xb3\x27\x39\x75\x0e' b'\x2a\xc4\xd9\x9c\xb6\xb4\x4d\x02\x03\x01\x00\x01\x30\x0d\x06\x09' b'\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x67' b'\x43\x4c\xa8\xa4\x3e\xeb\x1b\x32\x28\x70\x8b\xdb\xeb\xfe\xf1\xb3' b'\x70\x39\x95\x34\x33\x26\xef\x54\xb6\x22\xf9\xe1\xd5\xe6\xc3\x76' b'\x96\xe5\xc1\x14\x61\x5b\xa5\xc2\x6c\xe7\xe6\xef\x00\x26\xec\xbc' b'\x48\x27\xf5\x3d\x73\x66\x15\x37\x9c\xaa\x87\x97\xef\x22\xda\x58' b'\x51\xbb\x33\xe9\xc8\x46\x44\xd1\xc9\x9d\x35\xcc\x66\x05\x29\xb4' b'\x64\x5f\x6d\xe1\x21\x0d\x45\x68\xac\x06\x43\x15\xe1\xc6\xc4\xc8' b'\xb4\xfa\xc3\x34\xfd\x49\x39\xcb\x22\x01\x8a\x30\x34\x50\xb0\x24' b'\x55\x7b\x6c\x6d\x5c\xf6\x33\x1a\x6c\xf6\x77\xa6\x2c\x9a\x32\x16' b'\x03\x03\x00\xcd\x0c\x00\x00\xc9\x03\x00\x17\x41\x04\x97\xe0\xa1' b'\x4e\xd7\x18\xa0\xe8\x17\xbf\xe1\xa0\xc1\xad\x25\x65\xfd\x35\x94' b'\x1b\xe1\xc2\xdf\x8a\x23\xdf\xef\xfb\xd3\xed\xe5\x4f\x61\x04\xf0' b'\x0b\x73\x26\x22\xf5\x59\x05\xc3\x31\x30\xf0\xba\xe0\x51\x9d\x33' b'\xa4\x58\xc9\x7c\x9e\x94\xad\xf7\x47\x78\x1d\xf4\x3b\x06\x01\x00' b'\x80\x4a\x39\x59\xd3\xdb\xbe\x40\x32\x7a\x44\x06\xe6\x2a\x2b\xfc' b'\x5d\xc6\x45\x32\x19\xf0\x56\xb4\xbf\x60\x77\xa1\xbe\xde\xaf\xfb' b'\x36\xb1\x03\x2a\xc2\xa2\xed\x12\xb0\x9b\xad\x4b\x68\x9b\xd1\xe0' b'\xac\x4a\xa1\x28\x11\x5e\xa6\xd1\x4d\x7a\xc3\xd8\xcc\x49\x33\x43' b'\xeb\x32\x8a\xd8\x5e\x4f\xb1\xd9\xcc\x2e\xfa\x82\x7b\x28\x50\xfb' b'\x7e\x8a\x0e\x85\xd7\x6c\xae\xc9\x89\xc0\x33\x63\x90\x46\x9e\x67' b'\x84\x40\x2e\xc5\x09\xe4\x36\x0c\x35\xc9\x8c\x4c\x50\x9f\x66\x84' b'\xb0\x6e\x84\x61\x42\x79\x20\x19\x63\xfe\xfa\x25\xe7\x3f\xa0\xac' b'\xb3\x16\x03\x03\x00\x04\x0e\x00\x00\x00' ) def test_records_length(self): assert (len(self.p.records) == 1) assert (len(self.p2.records) == 4) def test_record_type(self): assert (self.p.records[0].type == 22) assert (all([rec.type == 22 for rec in self.p2.records])) def test_record_version(self): assert (self.p.records[0].version == 768) assert (all([rec.version == 771 for rec in self.p2.records])) class TestTLSRecord(object): """ Test basic TLSRecord functionality For this test, the contents of the record doesn't matter, since we're not parsing the next layer. """ @classmethod def setup_class(cls): # add some extra data, to make sure length is parsed correctly cls.p = TLSRecord(b'\x17\x03\x01\x00\x08abcdefghzzzzzzzzzzz') def test_content_type(self): assert (self.p.type == 23) def test_version(self): assert (self.p.version == 0x0301) def test_length(self): assert (self.p.length == 8) def test_data(self): assert (self.p.data == b'abcdefgh') def test_initial_flags(self): assert (self.p.compressed is True) assert (self.p.encrypted is True) def test_repack(self): p2 = TLSRecord(type=23, version=0x0301, data=b'abcdefgh') assert (p2.type == 23) assert (p2.version == 0x0301) assert (p2.length == 8) assert (p2.data == b'abcdefgh') assert (p2.pack() == self.p.pack()) def test_total_length(self): # that len(p) includes header assert (len(self.p) == 13) def test_raises_need_data_when_buf_is_short(self): import pytest pytest.raises(dpkt.NeedData, TLSRecord, b'\x16\x03\x01\x00\x10abc') class TestTLSChangeCipherSpec(object): """It's just a byte. This will be quick, I promise""" @classmethod def setup_class(cls): cls.p = TLSChangeCipherSpec(b'\x01') def test_parses(self): assert (self.p.type == 1) def test_total_length(self): assert (len(self.p) == 1) class TestTLSAppData(object): """AppData is basically just a string""" def test_value(self): d = TLSAppData('abcdefgh') assert (d == 'abcdefgh') class TestTLSHandshake(object): @classmethod def setup_class(cls): cls.h = TLSHandshake(b'\x00\x00\x00\x01\xff') def test_created_inside_message(self): assert (isinstance(self.h.data, TLSHelloRequest) is True) def test_length(self): assert (self.h.length == 0x01) def test_raises_need_data(self): import pytest pytest.raises(dpkt.NeedData, TLSHandshake, b'\x00\x00\x01\x01') class TestClientHello(object): """This data is extracted from and verified by Wireshark""" @classmethod def setup_class(cls): cls.data = _hexdecode( b"01000199" # handshake header b"0301" # version b"5008220ce5e0e78b6891afe204498c9363feffbe03235a2d9e05b7d990eb708d" # rand b"2009bc0192e008e6fa8fe47998fca91311ba30ddde14a9587dc674b11c3d3e5ed1" # session id # cipher suites b"005200ffc00ac0140088008700390038c00fc00500840035c007c009c011c0130045004400330032" b"c00cc00ec002c0040096004100050004002fc008c01200160013c00dc003000ac006c010c00bc00100020001" b"0100" # compression methods # extensions b"00fc0000000e000c0000096c6f63616c686f7374000a00080006001700180019000b000201000023" b"00d0a50b2e9f618a9ea9bf493ef49b421835cd2f6b05bbe1179d8edf70d58c33d656e8696d36d7e7" b"e0b9d3ecc0e4de339552fa06c64c0fcb550a334bc43944e2739ca342d15a9ebbe981ac87a0d38160" b"507d47af09bdc16c5f0ee4cdceea551539382333226048a026d3a90a0535f4a64236467db8fee22b" b"041af986ad0f253bc369137cd8d8cd061925461d7f4d7895ca9a4181ab554dad50360ac31860e971" b"483877c9335ac1300c5e78f3e56f3b8e0fc16358fcaceefd5c8d8aaae7b35be116f8832856ca6114" b"4fcdd95e071b94d0cf7233740000" b"FFFFFFFFFFFFFFFF") # random garbage cls.p = TLSHandshake(cls.data) def test_client_hello_constructed(self): """Make sure the correct class was constructed""" # print self.p assert (isinstance(self.p.data, TLSClientHello) is True) # def testClientDateCorrect(self): # self.assertEqual(self.p.random_unixtime, 1342710284) def test_client_random_correct(self): assert (self.p.data.random == _hexdecode(b'5008220ce5e0e78b6891afe204498c9363feffbe03235a2d9e05b7d990eb708d')) def test_ciphersuites(self): assert (tuple([c.code for c in self.p.data.ciphersuites]) == struct.unpack('!{0}H'.format( len(self.p.data.ciphersuites)), _hexdecode( b'00ffc00ac0140088008700390038c00fc00500840035c007c009c011c0130045004400330032c00c' b'c00ec002c0040096004100050004002fc008c01200160013c00dc003000ac006c010c00bc00100020001'))) assert (len(self.p.data.ciphersuites) == 41) def test_session_id(self): assert (self.p.data.session_id == _hexdecode(b'09bc0192e008e6fa8fe47998fca91311ba30ddde14a9587dc674b11c3d3e5ed1')) def test_compression_methods(self): assert (list(self.p.data.compression_methods) == [0x00, ]) def test_total_length(self): assert (len(self.p) == 413) class TestServerHello(object): """Again, from Wireshark""" @classmethod def setup_class(cls): cls.data = _hexdecode( b'0200004d03015008220c8ec43c5462315a7c99f5d5b6bff009ad285b51dc18485f352e9fdecd2009' b'bc0192e008e6fa8fe47998fca91311ba30ddde14a9587dc674b11c3d3e5ed10002000005ff01000100') cls.p = TLSHandshake(cls.data) def test_constructed(self): assert (isinstance(self.p.data, TLSServerHello) is True) # def testDateCorrect(self): # self.assertEqual(self.p.random_unixtime, 1342710284) def test_random_correct(self): assert (self.p.data.random == _hexdecode(b'5008220c8ec43c5462315a7c99f5d5b6bff009ad285b51dc18485f352e9fdecd')) def test_ciphersuite(self): assert (self.p.data.ciphersuite.name == 'TLS_RSA_WITH_NULL_SHA') assert (self.p.data.cipher_suite.name == 'TLS_RSA_WITH_NULL_SHA') # deprecated; still test for coverage def test_compression_method(self): assert (self.p.data.compression_method == 0) assert (self.p.data.compression == 0) # deprecated; still test for coverage def test_total_length(self): assert (len(self.p) == 81) class TestTLSCertificate(object): """We use a 2016 certificate record from iana.org as test data.""" @classmethod def setup_class(cls): cls.p = TLSHandshake( b'\x0b\x00\x0b\x45\x00\x0b\x42\x00\x06\x87\x30\x82\x06\x83\x30\x82\x05\x6b\xa0\x03\x02\x01\x02\x02\x10\x09\xca' b'\xbb\xe2\x19\x1c\x8f\x56\x9d\xd4\xb6\xdd\x25\x0f\x21\xd8\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b' b'\x05\x00\x30\x70\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x15\x30\x13\x06\x03\x55\x04\x0a\x13' b'\x0c\x44\x69\x67\x69\x43\x65\x72\x74\x20\x49\x6e\x63\x31\x19\x30\x17\x06\x03\x55\x04\x0b\x13\x10\x77\x77\x77' b'\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x31\x2f\x30\x2d\x06\x03\x55\x04\x03\x13\x26\x44\x69\x67' b'\x69\x43\x65\x72\x74\x20\x53\x48\x41\x32\x20\x48\x69\x67\x68\x20\x41\x73\x73\x75\x72\x61\x6e\x63\x65\x20\x53' b'\x65\x72\x76\x65\x72\x20\x43\x41\x30\x1e\x17\x0d\x31\x34\x31\x30\x32\x37\x30\x30\x30\x30\x30\x30\x5a\x17\x0d' b'\x31\x38\x30\x31\x30\x33\x31\x32\x30\x30\x30\x30\x5a\x30\x81\xa3\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02' b'\x55\x53\x31\x13\x30\x11\x06\x03\x55\x04\x08\x13\x0a\x43\x61\x6c\x69\x66\x6f\x72\x6e\x69\x61\x31\x14\x30\x12' b'\x06\x03\x55\x04\x07\x13\x0b\x4c\x6f\x73\x20\x41\x6e\x67\x65\x6c\x65\x73\x31\x3c\x30\x3a\x06\x03\x55\x04\x0a' b'\x13\x33\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x43\x6f\x72\x70\x6f\x72\x61\x74\x69\x6f\x6e\x20\x66\x6f\x72\x20' b'\x41\x73\x73\x69\x67\x6e\x65\x64\x20\x4e\x61\x6d\x65\x73\x20\x61\x6e\x64\x20\x4e\x75\x6d\x62\x65\x72\x73\x31' b'\x16\x30\x14\x06\x03\x55\x04\x0b\x13\x0d\x49\x54\x20\x4f\x70\x65\x72\x61\x74\x69\x6f\x6e\x73\x31\x13\x30\x11' b'\x06\x03\x55\x04\x03\x0c\x0a\x2a\x2e\x69\x61\x6e\x61\x2e\x6f\x72\x67\x30\x82\x02\x22\x30\x0d\x06\x09\x2a\x86' b'\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x00\x30\x82\x02\x0a\x02\x82\x02\x01\x00\x9d\xbd\xfd\xde' b'\xb5\xca\xe5\x3a\x55\x97\x47\xe2\xfd\xa6\x37\x28\xe4\xab\xa6\x0f\x18\xb7\x9a\x69\xf0\x33\x10\xbf\x01\x64\xe5' b'\xee\x7d\xb6\xb1\x5b\xf5\x6d\xf2\x3f\xdd\xba\xe6\xa1\xbb\x38\x44\x9b\x8c\x88\x3f\x18\x10\x2b\xbd\x8b\xb6\x55' b'\xac\x0e\x2d\xac\x2e\xe3\xed\x5c\xf4\x31\x58\x68\xd2\xc5\x98\x06\x82\x84\x85\x4b\x24\x89\x4d\xcd\x4b\xd3\x78' b'\x11\xf0\xad\x3a\x28\x2c\xd4\xb4\xe5\x99\xff\xd0\x7d\x8d\x2d\x3f\x24\x78\x55\x4f\x81\x02\x0b\x32\x0e\xe1\x2f' b'\x44\x94\x8e\x2e\xa1\xed\xbc\x99\x0b\x83\x0c\xa5\xcc\xa6\xb4\xa8\x39\xfb\x27\xb5\x18\x50\xc9\x84\x7e\xac\x74' b'\xf2\x66\x09\xeb\x24\x36\x5b\x97\x51\xfb\x1c\x32\x08\xf5\x69\x13\xba\xcb\xca\xe4\x92\x01\x34\x7c\x78\xb7\xe5' b'\x4a\x9d\x99\x97\x94\x04\xc3\x7f\x00\xfb\x65\xdb\x84\x9f\xd7\x5e\x3a\x68\x77\x0c\x30\xf2\xab\xe6\x5b\x33\x25' b'\x6f\xb5\x9b\x45\x00\x50\xb0\x0d\x81\x39\xd4\xd8\x0d\x36\xf7\xbc\x46\xda\xf3\x03\xe4\x8f\x0f\x07\x91\xb2\xfd' b'\xd7\x2e\xc6\x0b\x2c\xb3\xad\x53\x3c\x3f\x28\x8c\x9c\x19\x4e\x49\x33\x7a\x69\xc4\x96\x73\x1f\x08\x6d\x4f\x1f' b'\x98\x25\x90\x07\x13\xe2\xa5\x51\xd0\x5c\xb6\x05\x75\x67\x85\x0d\x91\xe6\x00\x1c\x4c\xe2\x71\x76\xf0\x95\x78' b'\x73\xa9\x5b\x88\x0a\xcb\xec\x19\xe7\xbd\x9b\xcf\x12\x86\xd0\x45\x2b\x73\x78\x9c\x41\x90\x5d\xd4\x70\x97\x1c' b'\xd7\x3a\xea\x52\xc7\x7b\x08\x0c\xd7\x79\xaf\x58\x23\x4f\x33\x72\x25\xc2\x6f\x87\xa8\xc1\x3e\x2a\x65\xe9\xdd' b'\x4e\x03\xa5\xb4\x1d\x7e\x06\xb3\x35\x3f\x38\x12\x9b\x23\x27\xa5\x31\xec\x96\x27\xa2\x1d\xc4\x23\x73\x3a\xa0' b'\x29\xd4\x98\x94\x48\xba\x33\x22\x89\x1c\x1a\x56\x90\xdd\xf2\xd2\x5c\x8e\xc8\xaa\xa8\x94\xb1\x4a\xa9\x21\x30' b'\xc6\xb6\xd9\x69\xa2\x1f\xf6\x71\xb6\x0c\x4c\x92\x3a\x94\xa9\x3e\xa1\xdd\x04\x92\xc9\x33\x93\xca\x6e\xdd\x61' b'\xf3\x3c\xa7\x7e\x92\x08\xd0\x1d\x6b\xd1\x51\x07\x66\x2e\xc0\x88\x73\x3d\xf4\xc8\x76\xa7\xe1\x60\x8b\x82\x97' b'\x3a\x0f\x75\x92\xe8\x4e\xd1\x55\x79\xd1\x81\xe7\x90\x24\xae\x8a\x7e\x4b\x9f\x00\x78\xeb\x20\x05\xb2\x3f\x9d' b'\x09\xa1\xdf\x1b\xbc\x7d\xe2\xa5\xa6\x08\x5a\x36\x46\xd9\xfa\xdb\x0e\x9d\xa2\x73\xa5\xf4\x03\xcd\xd4\x28\x31' b'\xce\x6f\x0c\xa4\x68\x89\x58\x56\x02\xbb\x8b\xc3\x6b\xb3\xbe\x86\x1f\xf6\xd1\xa6\x2e\x35\x02\x03\x01\x00\x01' b'\xa3\x82\x01\xe3\x30\x82\x01\xdf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x51\x68\xff\x90\xaf\x02' b'\x07\x75\x3c\xcc\xd9\x65\x64\x62\xa2\x12\xb8\x59\x72\x3b\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\xc7\xd0' b'\xac\xef\x89\x8b\x20\xe4\xb9\x14\x66\x89\x33\x03\x23\x94\xf6\xbf\x3a\x61\x30\x1f\x06\x03\x55\x1d\x11\x04\x18' b'\x30\x16\x82\x0a\x2a\x2e\x69\x61\x6e\x61\x2e\x6f\x72\x67\x82\x08\x69\x61\x6e\x61\x2e\x6f\x72\x67\x30\x0e\x06' b'\x03\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa0\x30\x1d\x06\x03\x55\x1d\x25\x04\x16\x30\x14\x06\x08\x2b' b'\x06\x01\x05\x05\x07\x03\x01\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x02\x30\x75\x06\x03\x55\x1d\x1f\x04\x6e\x30' b'\x6c\x30\x34\xa0\x32\xa0\x30\x86\x2e\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x33\x2e\x64\x69\x67\x69\x63\x65' b'\x72\x74\x2e\x63\x6f\x6d\x2f\x73\x68\x61\x32\x2d\x68\x61\x2d\x73\x65\x72\x76\x65\x72\x2d\x67\x33\x2e\x63\x72' b'\x6c\x30\x34\xa0\x32\xa0\x30\x86\x2e\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x34\x2e\x64\x69\x67\x69\x63\x65' b'\x72\x74\x2e\x63\x6f\x6d\x2f\x73\x68\x61\x32\x2d\x68\x61\x2d\x73\x65\x72\x76\x65\x72\x2d\x67\x33\x2e\x63\x72' b'\x6c\x30\x42\x06\x03\x55\x1d\x20\x04\x3b\x30\x39\x30\x37\x06\x09\x60\x86\x48\x01\x86\xfd\x6c\x01\x01\x30\x2a' b'\x30\x28\x06\x08\x2b\x06\x01\x05\x05\x07\x02\x01\x16\x1c\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x64' b'\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x2f\x43\x50\x53\x30\x81\x83\x06\x08\x2b\x06\x01\x05\x05\x07\x01' b'\x01\x04\x77\x30\x75\x30\x24\x06\x08\x2b\x06\x01\x05\x05\x07\x30\x01\x86\x18\x68\x74\x74\x70\x3a\x2f\x2f\x6f' b'\x63\x73\x70\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x30\x4d\x06\x08\x2b\x06\x01\x05\x05\x07\x30' b'\x02\x86\x41\x68\x74\x74\x70\x3a\x2f\x2f\x63\x61\x63\x65\x72\x74\x73\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e' b'\x63\x6f\x6d\x2f\x44\x69\x67\x69\x43\x65\x72\x74\x53\x48\x41\x32\x48\x69\x67\x68\x41\x73\x73\x75\x72\x61\x6e' b'\x63\x65\x53\x65\x72\x76\x65\x72\x43\x41\x2e\x63\x72\x74\x30\x0c\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x02\x30' b'\x00\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x70\x31\x4c\x38\xe7\xc0' b'\x2f\xd8\x08\x10\x50\x0b\x9d\xf6\xda\xe8\x5d\xe9\xb2\x3e\x29\xfb\xd6\x8b\xfd\xb5\xf2\x34\x11\xc8\x9a\xcf\xaf' b'\x9a\xe0\x5a\xf9\x12\x3a\x8a\xa6\xbc\xe6\x95\x4a\x4e\x68\xdc\x7c\xfc\x48\x0a\x65\xd7\x6f\x22\x9c\x4b\xd5\xf5' b'\x67\x4b\x0c\x9a\xc6\xd0\x6a\x37\xa1\xa1\xc1\x45\xc3\x95\x61\x20\xb8\xef\xe6\x7c\x88\x7a\xb4\xff\x7d\x6a\xa9' b'\x50\xff\x36\x98\xf2\x7c\x4a\x19\xd5\x9d\x93\xa3\x9a\xca\x5a\x7b\x6d\x6c\x75\xe3\x49\x74\xe5\x0f\x5a\x59\x00' b'\x05\xb3\xcb\x66\x5d\xdb\xd7\x07\x4f\x9f\xcb\xcb\xf9\xc5\x02\x28\xd5\xe2\x55\x96\xb6\x4a\xda\x16\x0b\x48\xf7' b'\x7a\x93\xaa\xce\xd2\x26\x17\xbf\xe0\x05\xe0\x0f\xe2\x0a\x53\x2a\x0a\xdc\xb8\x18\xc8\x78\xdc\x5d\x66\x49\x27' b'\x77\x77\xca\x1a\x81\x4e\x21\xd0\xb5\x33\x08\xaf\x40\x78\xbe\x45\x54\x71\x5e\x4c\xe4\x82\x8b\x01\x2f\x25\xff' b'\xa1\x3a\x6c\xeb\x30\xd2\x0a\x75\xde\xba\x8a\x34\x4e\x41\xd6\x27\xfa\x63\x8f\xef\xf3\x8a\x30\x63\xa0\x18\x75' b'\x19\xb3\x9b\x05\x3f\x71\x34\xd9\xcd\x83\xe6\x09\x1a\xcc\xf5\xd2\xe3\xa0\x5e\xdf\xa1\xdf\xbe\x18\x1a\x87\xad' b'\x86\xba\x24\xfe\x6b\x97\xfe\x00\x04\xb5\x30\x82\x04\xb1\x30\x82\x03\x99\xa0\x03\x02\x01\x02\x02\x10\x04\xe1' b'\xe7\xa4\xdc\x5c\xf2\xf3\x6d\xc0\x2b\x42\xb8\x5d\x15\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b' b'\x05\x00\x30\x6c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x15\x30\x13\x06\x03\x55\x04\x0a\x13' b'\x0c\x44\x69\x67\x69\x43\x65\x72\x74\x20\x49\x6e\x63\x31\x19\x30\x17\x06\x03\x55\x04\x0b\x13\x10\x77\x77\x77' b'\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x31\x2b\x30\x29\x06\x03\x55\x04\x03\x13\x22\x44\x69\x67' b'\x69\x43\x65\x72\x74\x20\x48\x69\x67\x68\x20\x41\x73\x73\x75\x72\x61\x6e\x63\x65\x20\x45\x56\x20\x52\x6f\x6f' b'\x74\x20\x43\x41\x30\x1e\x17\x0d\x31\x33\x31\x30\x32\x32\x31\x32\x30\x30\x30\x30\x5a\x17\x0d\x32\x38\x31\x30' b'\x32\x32\x31\x32\x30\x30\x30\x30\x5a\x30\x70\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x15\x30' b'\x13\x06\x03\x55\x04\x0a\x13\x0c\x44\x69\x67\x69\x43\x65\x72\x74\x20\x49\x6e\x63\x31\x19\x30\x17\x06\x03\x55' b'\x04\x0b\x13\x10\x77\x77\x77\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x31\x2f\x30\x2d\x06\x03\x55' b'\x04\x03\x13\x26\x44\x69\x67\x69\x43\x65\x72\x74\x20\x53\x48\x41\x32\x20\x48\x69\x67\x68\x20\x41\x73\x73\x75' b'\x72\x61\x6e\x63\x65\x20\x53\x65\x72\x76\x65\x72\x20\x43\x41\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86' b'\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xb6\xe0\x2f\xc2\x24\x06' b'\xc8\x6d\x04\x5f\xd7\xef\x0a\x64\x06\xb2\x7d\x22\x26\x65\x16\xae\x42\x40\x9b\xce\xdc\x9f\x9f\x76\x07\x3e\xc3' b'\x30\x55\x87\x19\xb9\x4f\x94\x0e\x5a\x94\x1f\x55\x56\xb4\xc2\x02\x2a\xaf\xd0\x98\xee\x0b\x40\xd7\xc4\xd0\x3b' b'\x72\xc8\x14\x9e\xef\x90\xb1\x11\xa9\xae\xd2\xc8\xb8\x43\x3a\xd9\x0b\x0b\xd5\xd5\x95\xf5\x40\xaf\xc8\x1d\xed' b'\x4d\x9c\x5f\x57\xb7\x86\x50\x68\x99\xf5\x8a\xda\xd2\xc7\x05\x1f\xa8\x97\xc9\xdc\xa4\xb1\x82\x84\x2d\xc6\xad' b'\xa5\x9c\xc7\x19\x82\xa6\x85\x0f\x5e\x44\x58\x2a\x37\x8f\xfd\x35\xf1\x0b\x08\x27\x32\x5a\xf5\xbb\x8b\x9e\xa4' b'\xbd\x51\xd0\x27\xe2\xdd\x3b\x42\x33\xa3\x05\x28\xc4\xbb\x28\xcc\x9a\xac\x2b\x23\x0d\x78\xc6\x7b\xe6\x5e\x71' b'\xb7\x4a\x3e\x08\xfb\x81\xb7\x16\x16\xa1\x9d\x23\x12\x4d\xe5\xd7\x92\x08\xac\x75\xa4\x9c\xba\xcd\x17\xb2\x1e' b'\x44\x35\x65\x7f\x53\x25\x39\xd1\x1c\x0a\x9a\x63\x1b\x19\x92\x74\x68\x0a\x37\xc2\xc2\x52\x48\xcb\x39\x5a\xa2' b'\xb6\xe1\x5d\xc1\xdd\xa0\x20\xb8\x21\xa2\x93\x26\x6f\x14\x4a\x21\x41\xc7\xed\x6d\x9b\xf2\x48\x2f\xf3\x03\xf5' b'\xa2\x68\x92\x53\x2f\x5e\xe3\x02\x03\x01\x00\x01\xa3\x82\x01\x49\x30\x82\x01\x45\x30\x12\x06\x03\x55\x1d\x13' b'\x01\x01\xff\x04\x08\x30\x06\x01\x01\xff\x02\x01\x00\x30\x0e\x06\x03\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02' b'\x01\x86\x30\x1d\x06\x03\x55\x1d\x25\x04\x16\x30\x14\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x01\x06\x08\x2b\x06' b'\x01\x05\x05\x07\x03\x02\x30\x34\x06\x08\x2b\x06\x01\x05\x05\x07\x01\x01\x04\x28\x30\x26\x30\x24\x06\x08\x2b' b'\x06\x01\x05\x05\x07\x30\x01\x86\x18\x68\x74\x74\x70\x3a\x2f\x2f\x6f\x63\x73\x70\x2e\x64\x69\x67\x69\x63\x65' b'\x72\x74\x2e\x63\x6f\x6d\x30\x4b\x06\x03\x55\x1d\x1f\x04\x44\x30\x42\x30\x40\xa0\x3e\xa0\x3c\x86\x3a\x68\x74' b'\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x34\x2e\x64\x69\x67\x69\x63\x65\x72\x74\x2e\x63\x6f\x6d\x2f\x44\x69\x67\x69' b'\x43\x65\x72\x74\x48\x69\x67\x68\x41\x73\x73\x75\x72\x61\x6e\x63\x65\x45\x56\x52\x6f\x6f\x74\x43\x41\x2e\x63' b'\x72\x6c\x30\x3d\x06\x03\x55\x1d\x20\x04\x36\x30\x34\x30\x32\x06\x04\x55\x1d\x20\x00\x30\x2a\x30\x28\x06\x08' b'\x2b\x06\x01\x05\x05\x07\x02\x01\x16\x1c\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x64\x69\x67\x69\x63' b'\x65\x72\x74\x2e\x63\x6f\x6d\x2f\x43\x50\x53\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x51\x68\xff\x90\xaf' b'\x02\x07\x75\x3c\xcc\xd9\x65\x64\x62\xa2\x12\xb8\x59\x72\x3b\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80' b'\x14\xb1\x3e\xc3\x69\x03\xf8\xbf\x47\x01\xd4\x98\x26\x1a\x08\x02\xef\x63\x64\x2b\xc3\x30\x0d\x06\x09\x2a\x86' b'\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x18\x8a\x95\x89\x03\xe6\x6d\xdf\x5c\xfc\x1d\x68\xea' b'\x4a\x8f\x83\xd6\x51\x2f\x8d\x6b\x44\x16\x9e\xac\x63\xf5\xd2\x6e\x6c\x84\x99\x8b\xaa\x81\x71\x84\x5b\xed\x34' b'\x4e\xb0\xb7\x79\x92\x29\xcc\x2d\x80\x6a\xf0\x8e\x20\xe1\x79\xa4\xfe\x03\x47\x13\xea\xf5\x86\xca\x59\x71\x7d' b'\xf4\x04\x96\x6b\xd3\x59\x58\x3d\xfe\xd3\x31\x25\x5c\x18\x38\x84\xa3\xe6\x9f\x82\xfd\x8c\x5b\x98\x31\x4e\xcd' b'\x78\x9e\x1a\xfd\x85\xcb\x49\xaa\xf2\x27\x8b\x99\x72\xfc\x3e\xaa\xd5\x41\x0b\xda\xd5\x36\xa1\xbf\x1c\x6e\x47' b'\x49\x7f\x5e\xd9\x48\x7c\x03\xd9\xfd\x8b\x49\xa0\x98\x26\x42\x40\xeb\xd6\x92\x11\xa4\x64\x0a\x57\x54\xc4\xf5' b'\x1d\xd6\x02\x5e\x6b\xac\xee\xc4\x80\x9a\x12\x72\xfa\x56\x93\xd7\xff\xbf\x30\x85\x06\x30\xbf\x0b\x7f\x4e\xff' b'\x57\x05\x9d\x24\xed\x85\xc3\x2b\xfb\xa6\x75\xa8\xac\x2d\x16\xef\x7d\x79\x27\xb2\xeb\xc2\x9d\x0b\x07\xea\xaa' b'\x85\xd3\x01\xa3\x20\x28\x41\x59\x43\x28\xd2\x81\xe3\xaa\xf6\xec\x7b\x3b\x77\xb6\x40\x62\x80\x05\x41\x45\x01' b'\xef\x17\x06\x3e\xde\xc0\x33\x9b\x67\xd3\x61\x2e\x72\x87\xe4\x69\xfc\x12\x00\x57\x40\x1e\x70\xf5\x1e\xc9\xb4' ) def test_num_certs(self): assert (len(self.p.data.certificates) == 2) class TestTLSMultiFactory(object): """Made up test data""" @classmethod def setup_class(cls): cls.data = _hexdecode(b'1703010010' # header 1 b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' # data 1 b'1703010010' # header 2 b'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' # data 2 b'1703010010' # header 3 b'CCCCCCCC') # data 3 (incomplete) cls.msgs, cls.bytes_parsed = tls_multi_factory(cls.data) def test_num_messages(self): # only complete messages should be parsed, incomplete ones left # in buffer assert (len(self.msgs) == 2) def test_bytes_parsed(self): assert (self.bytes_parsed == (5 + 16) * 2) def test_first_msg_data(self): assert (self.msgs[0].data == _hexdecode(b'AA' * 16)) def test_second_msg_data(self): assert (self.msgs[1].data == _hexdecode(b'BB' * 16)) def test_incomplete(self): import pytest msgs, n = tls_multi_factory(_hexdecode(b'17')) assert (len(msgs) == 0) assert (n == 0) msgs, n = tls_multi_factory(_hexdecode(b'1703')) assert (len(msgs) == 0) assert (n == 0) msgs, n = tls_multi_factory(_hexdecode(b'170301')) assert (len(msgs) == 0) assert (n == 0) msgs, n = tls_multi_factory(_hexdecode(b'17030100')) assert (len(msgs) == 0) assert (n == 0) msgs, n = tls_multi_factory(_hexdecode(b'1703010000')) assert (len(msgs) == 1) assert (n == 5) with pytest.raises(SSL3Exception, match='Bad TLS version in buf: '): tls_multi_factory(_hexdecode(b'000000000000')) def test_ssl2(): from binascii import unhexlify buf_padding = unhexlify( '0001' # len '02' # padlen '03' # msg '0405' # pad '0607' # data ) ssl2 = SSL2(buf_padding) assert ssl2.len == 1 assert ssl2.msg == b'\x03' assert ssl2.pad == b'\x04\x05' assert ssl2.data == b'\x06\x07' buf_no_padding = unhexlify( '8001' # len '03' # msg '0607' # data ) ssl2 = SSL2(buf_no_padding) assert ssl2.len == 1 assert ssl2.msg == b'\x03' assert ssl2.data == b'\x06\x07' def test_clienthello_invalidcipher(): # NOTE: this test relies on ciphersuite 0x001c not being in ssl_ciphersuites.py CIPHERSUITES. # IANA has reserved this value to avoid conflict with SSLv3, but if it gets reassigned, # a new value should be chosen to fix this test. from binascii import unhexlify buf = unhexlify( '0301' # version '0000000000000000000000000000000000000000000000000000000000000000' # random '01' # session_id length '02' # session_id '0002' # ciphersuites len '001c' # ciphersuite (reserved; not implemented '00' ) th = TLSClientHello(buf) assert th.ciphersuites[0].name == 'Unknown' def test_serverhello_invalidcipher(): # NOTE: this test relies on ciphersuite 0x001c not being in ssl_ciphersuites.py CIPHERSUITES. # IANA has reserved this value to avoid conflict with SSLv3, but if it gets reassigned, # a new value should be chosen to fix this test. import pytest from binascii import unhexlify buf = unhexlify( '0301' # version '0000000000000000000000000000000000000000000000000000000000000000' # random '01' # session_id length '02' # session_id '001c' # ciphersuite (reserved; not implemented '00' ) th = TLSServerHello(buf) assert th.ciphersuite.name == 'Unknown' # remove the final byte from the ciphersuite so it will fail unpacking buf = buf[:-1] with pytest.raises(dpkt.NeedData): TLSServerHello(buf) def test_tlscertificate_unpacking_error(): import pytest from binascii import unhexlify buf = unhexlify( '000003' # certs len '0000' # certs (invalid, as size < 3) ) with pytest.raises(dpkt.NeedData): TLSCertificate(buf) def test_tlshandshake_invalid_type(): import pytest from binascii import unhexlify buf = unhexlify( '7b' # type (invalid) '000000' # length_bytes ) with pytest.raises(SSL3Exception, match='Unknown or invalid handshake type 123'): TLSHandshake(buf) def test_sslfactory(): from binascii import unhexlify buf_tls31 = unhexlify( '00' # type '0301' # version '0000' # length ) tls = SSLFactory(buf_tls31) assert isinstance(tls, TLSRecord) buf_ssl2 = unhexlify( '00' # type '0000' # not an SSL3+ version ) ssl2 = SSLFactory(buf_ssl2) assert isinstance(ssl2, SSL2) def test_extensions(): from binascii import unhexlify buf = unhexlify( b"010000e0" # handshake header b"0303" # version b"60b92b07b6b0e1dffd0ac313788a6d54056d24f73c4d7425631e29b11be97b22" # rand b"20b3330000ab415e3356226b305993bfb76b2d50bfaeb5298549723b594c999479" # session id # cipher suites b"0026c02cc02bc030c02fc024c023c028c027c00ac009c014c013009d009c003d003c0035002f000a" b"0100" # compression methods # extensions b"006d00000023002100001e73656c662e6576656e74732e646174612e6d6963726f736f66742e636f" b"6d000500050100000000000a00080006001d00170018000b00020100000d001a0018080408050806" b"0401050102010403050302030202060106030023000000170000ff01000100" b"ffeeddcc" # extra 4 bytes ) handshake = TLSHandshake(buf) hello = handshake.data assert len(hello.extensions) == 8 assert hello.extensions[-1] == (65281, b'\x00') dpkt-1.9.8/dpkt/ssl_ciphersuites.py000066400000000000000000001124661427735150300174200ustar00rootroot00000000000000# Copyright 2012 Google Inc. All rights reserved. # -*- coding: utf-8 -*- """ Nicely formatted cipher suite definitions for TLS A list of cipher suites in the form of CipherSuite objects. These are supposed to be immutable; don't mess with them. """ class CipherSuite(object): """ Encapsulates a cipher suite. Members/args: * code: two-byte ID code, as int * kx: key exchange algorithm, e.g. 'RSA' or 'DHE' * auth: authentication algorithm, e.g. 'RSA' or 'DSS' * cipher: stream or block cipher algorithm, e.g. 'AES_128' * mode: mode of operation for block ciphers, e.g. 'CBC' or 'GCM' * mac: message authentication code algorithm, e.g. 'MD5' or 'SHA256' * name: cipher suite name as defined in the RFCs, e.g. 'TLS_RSA_WITH_RC4_40_MD5', can be generated by default from the other parameters * encoding: encoding algorithm, defaults to cipher+mode Additional members: * kx_auth: kx+auth algorithm, as 'KeyExchangeAlgorithm' in RFCs """ def __init__(self, code, kx, auth, cipher, mode, mac, name=None, encoding=None): self.code = code # We strip trailing whitespace here because we want to format the # global table nicely while making pylint happy. self._kx = kx.rstrip() self._auth = auth.rstrip() self.cipher = cipher.rstrip() self.mode = mode.rstrip() self.mac = mac.rstrip() self._name = name self._encoding = encoding @property def kx(self): if self._kx == '': # for PSK return self._auth else: return self._kx @property def auth(self): if self._auth == '': # for RSA return self._kx else: return self._auth @property def kx_auth(self): if self._auth == '': # for RSA return self._kx elif self._kx == '': # for PSK return self._auth else: return self._kx + '_' + self._auth @property def encoding(self): if self._encoding is None: if self.mode == '': return self.cipher else: return self.cipher + '_' + self.mode else: return self._encoding @property def name(self): if self._name is None: if self.mac == '': # for CCM and CCM_8 modes return 'TLS_' + self.kx_auth + '_WITH_' + self.encoding else: return 'TLS_' + self.kx_auth + '_WITH_' + self.encoding + '_' + self.mac else: return self._name def __repr__(self): return 'CipherSuite(0x%04x, %s)' % (self.code, self.name) MAC_SIZES = { 'MD5': 16, 'SHA': 20, 'SHA256': 32, 'SHA384': 48, } BLOCK_SIZES = { '3DES_EDE': 8, 'AES_128': 16, 'AES_256': 16, 'ARIA': 16, 'CAMELLIA_128': 16, 'CAMELLIA_256': 16, 'CHACHA20': 64, 'DES': 8, 'DES40': 8, 'IDEA': 8, 'RC2_40': 8, 'RC4_40': None, 'RC4_128': None, 'SEED': 16, } @property def mac_size(self): """In bytes. Default to 0.""" return self.MAC_SIZES.get(self.mac, 0) @property def block_size(self): """In bytes. Default to 1.""" return self.BLOCK_SIZES.get(self.cipher, 1) @property def pfs(self): return self.kx in ('DHE', 'ECDHE') @property def aead(self): return self.mode in ('CCM', 'CCM_8', 'GCM') @property def anonymous(self): return self.auth.startswith('anon') def get_unknown_ciphersuite(code): return CipherSuite(code, '', '', '', '', '', name='Unknown') # master list of CipherSuite Objects # Full list from IANA: # https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml CIPHERSUITES = [ # not a real cipher suite, can be ignored, see RFC5746 CipherSuite(0x00ff, 'NULL', ' ', 'NULL ', ' ', 'NULL', 'TLS_EMPTY_RENEGOTIATION_INFO'), # RFC7507 CipherSuite(0x5600, '', ' ', '', '', '', 'TLS_FALLBACK'), CipherSuite(0xffff, '', ' ', '', '', '', 'UNKNOWN_CIPHER'), # RFC2246 : TLS 1.0 CipherSuite(0x0000, 'NULL', ' ', 'NULL ', ' ', 'NULL'), CipherSuite(0x0001, 'RSA', ' ', 'NULL ', ' ', 'MD5'), CipherSuite(0x0002, 'RSA', ' ', 'NULL ', ' ', 'SHA'), CipherSuite(0x0003, 'RSA_EXPORT', ' ', 'RC4_40 ', ' ', 'MD5'), CipherSuite(0x0004, 'RSA', ' ', 'RC4_128 ', ' ', 'MD5'), CipherSuite(0x0005, 'RSA', ' ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0x0006, 'RSA_EXPORT', ' ', 'RC2_40 ', 'CBC ', 'MD5', encoding='RC2_CBC_40'), CipherSuite(0x0007, 'RSA', ' ', 'IDEA ', 'CBC ', 'SHA'), CipherSuite(0x0008, 'RSA_EXPORT', ' ', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x0009, 'RSA', ' ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x000a, 'RSA', ' ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x000b, 'DH', 'DSS_EXPORT', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x000c, 'DH', 'DSS ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x000d, 'DH', 'DSS ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x000e, 'DH', 'RSA_EXPORT', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x000f, 'DH', 'RSA ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x0010, 'DH', 'RSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0011, 'DHE', 'DSS_EXPORT', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x0012, 'DHE', 'DSS ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x0013, 'DHE', 'DSS ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0014, 'DHE', 'RSA_EXPORT', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x0015, 'DHE', 'RSA ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x0016, 'DHE', 'RSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0017, 'DH', 'anon_EXPORT', 'RC4_40 ', ' ', 'MD5'), CipherSuite(0x0018, 'DH', 'anon ', 'RC4_128 ', ' ', 'MD5'), CipherSuite(0x0019, 'DH', 'anon_EXPORT', 'DES40 ', 'CBC ', 'SHA'), CipherSuite(0x001a, 'DH', 'anon ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x001b, 'DH', 'anon ', '3DES_EDE', 'CBC ', 'SHA'), # Reserved: 0x1c-0x1d # RFC4346 : TLS 1.1 # RFC2712 CipherSuite(0x001e, 'KRB5', ' ', 'DES ', 'CBC ', 'SHA'), CipherSuite(0x001f, 'KRB5', ' ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0020, 'KRB5', ' ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0x0021, 'KRB5', ' ', 'IDEA ', 'CBC ', 'SHA'), CipherSuite(0x0022, 'KRB5', ' ', 'DES ', 'CBC ', 'MD5'), CipherSuite(0x0023, 'KRB5', ' ', '3DES_EDE', 'CBC ', 'MD5'), CipherSuite(0x0024, 'KRB5', ' ', 'RC4_128 ', ' ', 'MD5'), CipherSuite(0x0025, 'KRB5', ' ', 'IDEA ', 'CBC ', 'MD5'), CipherSuite(0x0026, 'KRB5_EXPORT', ' ', 'DES40 ', 'CBC ', 'SHA', encoding='DES_CBC_40'), CipherSuite(0x0027, 'KRB5_EXPORT', ' ', 'RC2_40 ', 'CBC ', 'SHA', encoding='RC2_CBC_40'), CipherSuite(0x0028, 'KRB5_EXPORT', ' ', 'RC4_40 ', ' ', 'SHA'), CipherSuite(0x0029, 'KRB5_EXPORT', ' ', 'DES40 ', 'CBC ', 'MD5', encoding='DES_CBC_40'), CipherSuite(0x002a, 'KRB5_EXPORT', ' ', 'RC2_40 ', 'CBC ', 'MD5', encoding='RC2_CBC_40'), CipherSuite(0x002b, 'KRB5_EXPORT', ' ', 'RC4_40 ', ' ', 'MD5'), # RFC4785 CipherSuite(0x002c, ' ', 'PSK ', 'NULL ', ' ', 'SHA'), CipherSuite(0x002d, 'DHE ', 'PSK ', 'NULL ', ' ', 'SHA'), CipherSuite(0x002e, 'RSA ', 'PSK ', 'NULL ', ' ', 'SHA'), # RFC3268 CipherSuite(0x002f, 'RSA ', ' ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0030, 'DH ', 'DSS ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0031, 'DH ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0032, 'DHE ', 'DSS ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0033, 'DHE ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0034, 'DH ', 'anon ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0035, 'RSA ', ' ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x0036, 'DH ', 'DSS ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x0037, 'DH ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x0038, 'DHE ', 'DSS ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x0039, 'DHE ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x003a, 'DH ', 'anon ', 'AES_256 ', 'CBC ', 'SHA'), # RFC5246 : TLS 1.2 CipherSuite(0x003b, 'RSA ', ' ', 'NULL ', ' ', 'SHA256'), CipherSuite(0x003c, 'RSA ', ' ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x003d, 'RSA ', ' ', 'AES_256 ', 'CBC ', 'SHA256'), CipherSuite(0x003e, 'DH ', 'DSS ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x003f, 'DH ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x0040, 'DHE ', 'DSS ', 'AES_128 ', 'CBC ', 'SHA256'), # RFC5932 CipherSuite(0x0041, 'RSA ', ' ', 'CAMELLIA_128', 'CBC', 'SHA'), CipherSuite(0x0042, 'DH ', 'DSS ', 'CAMELLIA_128', 'CBC', 'SHA'), CipherSuite(0x0043, 'DH ', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA'), CipherSuite(0x0044, 'DHE ', 'DSS ', 'CAMELLIA_128', 'CBC', 'SHA'), CipherSuite(0x0045, 'DHE ', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA'), CipherSuite(0x0046, 'DH ', 'anon ', 'CAMELLIA_128', 'CBC', 'SHA'), # Reserved: 0x47-5c # Unassigned: 0x5d-5f # Reserved: 0x60-66 # RFC5246 : TLS 1.2 CipherSuite(0x0067, 'DHE ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x0068, 'DH ', 'DSS ', 'AES_256 ', 'CBC ', 'SHA256'), CipherSuite(0x0069, 'DH ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA256'), CipherSuite(0x006a, 'DHE ', 'DSS ', 'AES_256 ', 'CBC ', 'SHA256'), CipherSuite(0x006b, 'DHE ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA256'), CipherSuite(0x006c, 'DH ', 'anon ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x006d, 'DH ', 'anon ', 'AES_256 ', 'CBC ', 'SHA256'), # Unassigned: 0x6e-83 # RFC5932 CipherSuite(0x0084, 'RSA ', ' ', 'CAMELLIA_256', 'CBC', 'SHA'), CipherSuite(0x0085, 'DH ', 'DSS ', 'CAMELLIA_256', 'CBC', 'SHA'), CipherSuite(0x0086, 'DH ', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA'), CipherSuite(0x0087, 'DHE ', 'DSS ', 'CAMELLIA_256', 'CBC', 'SHA'), CipherSuite(0x0088, 'DHE ', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA'), CipherSuite(0x0089, 'DH ', 'anon ', 'CAMELLIA_256', 'CBC', 'SHA'), # RFC4279 CipherSuite(0x008a, ' ', 'PSK ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0x008b, ' ', 'PSK ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x008c, ' ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x008d, ' ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x008e, 'DHE ', 'PSK ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0x008f, 'DHE ', 'PSK ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0090, 'DHE ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0091, 'DHE ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0x0092, 'RSA ', 'PSK ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0x0093, 'RSA ', 'PSK ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0x0094, 'RSA ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0x0095, 'RSA ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA'), # RFC4162 CipherSuite(0x0096, 'RSA ', ' ', 'SEED ', 'CBC ', 'SHA'), CipherSuite(0x0097, 'DH ', 'DSS ', 'SEED ', 'CBC ', 'SHA'), CipherSuite(0x0098, 'DH ', 'RSA ', 'SEED ', 'CBC ', 'SHA'), CipherSuite(0x0099, 'DHE ', 'DSS ', 'SEED ', 'CBC ', 'SHA'), CipherSuite(0x009a, 'DHE ', 'RSA ', 'SEED ', 'CBC ', 'SHA'), CipherSuite(0x009b, 'DH ', 'anon ', 'SEED ', 'CBC ', 'SHA'), # RFC5288 CipherSuite(0x009c, 'RSA ', ' ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x009d, 'RSA ', ' ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x009e, 'DHE ', 'RSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x009f, 'DHE ', 'RSA ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00a0, 'DH ', 'RSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00a1, 'DH ', 'RSA ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00a2, 'DHE ', 'DSS ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00a3, 'DHE ', 'DSS ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00a4, 'DH ', 'DSS ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00a5, 'DH ', 'DSS ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00a6, 'DH ', 'anon ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00a7, 'DH ', 'anon ', 'AES_256 ', 'GCM ', 'SHA384'), # RFC5487 CipherSuite(0x00a8, ' ', 'PSK ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00a9, ' ', 'PSK ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00aa, 'DHE ', 'PSK ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00ab, 'DHE ', 'PSK ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00ac, 'RSA ', 'PSK ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0x00ad, 'RSA ', 'PSK ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0x00ae, ' ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x00af, ' ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0x00b0, ' ', 'PSK ', 'NULL ', ' ', 'SHA256'), CipherSuite(0x00b1, ' ', 'PSK ', 'NULL ', ' ', 'SHA384'), CipherSuite(0x00b2, 'DHE ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x00b3, 'DHE ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0x00b4, 'DHE ', 'PSK ', 'NULL ', ' ', 'SHA256'), CipherSuite(0x00b5, 'DHE ', 'PSK ', 'NULL ', ' ', 'SHA384'), CipherSuite(0x00b6, 'RSA ', 'PSK ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0x00b7, 'RSA ', 'PSK ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0x00b8, 'RSA ', 'PSK ', 'NULL ', ' ', 'SHA256'), CipherSuite(0x00b9, 'RSA ', 'PSK ', 'NULL ', ' ', 'SHA384'), # RFC5932 CipherSuite(0x00ba, 'RSA ', ' ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00bb, 'DH ', 'DSS ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00bc, 'DH ', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00bd, 'DHE ', 'DSS ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00be, 'DHE ', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00bf, 'DH ', 'anon ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0x00c0, 'RSA ', ' ', 'CAMELLIA_256', 'CBC', 'SHA256'), CipherSuite(0x00c1, 'DH ', 'DSS ', 'CAMELLIA_256', 'CBC', 'SHA256'), CipherSuite(0x00c2, 'DH ', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA256'), CipherSuite(0x00c3, 'DHE ', 'DSS ', 'CAMELLIA_256', 'CBC', 'SHA256'), CipherSuite(0x00c4, 'DHE ', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA256'), CipherSuite(0x00c5, 'DH ', 'anon ', 'CAMELLIA_256', 'CBC', 'SHA256'), # RFC8446 TLS 1.3 CipherSuite(0x1301, ' ', ' ', 'AES_128 ', 'GCM ', 'SHA256', name='TLS_AES_128_GCM_SHA256'), CipherSuite(0x1302, ' ', ' ', 'AES_256 ', 'GCM ', 'SHA384', name='TLS_AES_256_GCM_SHA384'), CipherSuite(0x1303, ' ', ' ', 'CHACHA20', 'POLY1305', 'SHA256', name='TLS_CHACHA20_POLY1305_SHA256'), CipherSuite(0x1304, ' ', ' ', 'AES_128 ', 'CCM ', 'SHA256', name='TLS_AES_128_CCM_SHA256'), CipherSuite(0x1305, ' ', ' ', 'AES_128 ', 'CCM_8 ', 'SHA256', name='TLS_AES_128_CCM_8_SHA256'), # RFC4492 CipherSuite(0xc001, 'ECDH ', 'ECDSA ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc002, 'ECDH ', 'ECDSA ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc003, 'ECDH ', 'ECDSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc004, 'ECDH ', 'ECDSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc005, 'ECDH ', 'ECDSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc006, 'ECDHE', 'ECDSA ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc007, 'ECDHE', 'ECDSA ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc008, 'ECDHE', 'ECDSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc009, 'ECDHE', 'ECDSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc00a, 'ECDHE', 'ECDSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc00b, 'ECDH ', 'RSA ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc00c, 'ECDH ', 'RSA ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc00d, 'ECDH ', 'RSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc00e, 'ECDH ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc00f, 'ECDH ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc010, 'ECDHE', 'RSA ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc011, 'ECDHE', 'RSA ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc012, 'ECDHE', 'RSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc013, 'ECDHE', 'RSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc014, 'ECDHE', 'RSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc015, 'ECDH ', 'anon ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc016, 'ECDH ', 'anon ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc017, 'ECDH ', 'anon ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc018, 'ECDH ', 'anon ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc019, 'ECDH ', 'anon ', 'AES_256 ', 'CBC ', 'SHA'), # RFC5054 CipherSuite(0xc01a, 'SRP_SHA', ' ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc01b, 'SRP_SHA', 'RSA ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc01c, 'SRP_SHA', 'DSS ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc01d, 'SRP_SHA', ' ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc01e, 'SRP_SHA', 'RSA ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc01f, 'SRP_SHA', 'DSS ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc020, 'SRP_SHA', ' ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc021, 'SRP_SHA', 'RSA ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc022, 'SRP_SHA', 'DSS ', 'AES_256 ', 'CBC ', 'SHA'), # RFC5289 CipherSuite(0xc023, 'ECDHE', 'ECDSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0xc024, 'ECDHE', 'ECDSA ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0xc025, 'ECDH ', 'ECDSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0xc026, 'ECDH ', 'ECDSA ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0xc027, 'ECDHE', 'RSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0xc028, 'ECDHE', 'RSA ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0xc029, 'ECDH ', 'RSA ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0xc02a, 'ECDH ', 'RSA ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0xc02b, 'ECDHE', 'ECDSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0xc02c, 'ECDHE', 'ECDSA ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0xc02d, 'ECDH ', 'ECDSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0xc02e, 'ECDH ', 'ECDSA ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0xc02f, 'ECDHE', 'RSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0xc030, 'ECDHE', 'RSA ', 'AES_256 ', 'GCM ', 'SHA384'), CipherSuite(0xc031, 'ECDH ', 'RSA ', 'AES_128 ', 'GCM ', 'SHA256'), CipherSuite(0xc032, 'ECDH ', 'RSA ', 'AES_256 ', 'GCM ', 'SHA384'), # RFC5489 CipherSuite(0xc033, 'ECDHE', 'PSK ', 'RC4_128 ', ' ', 'SHA'), CipherSuite(0xc034, 'ECDHE', 'PSK ', '3DES_EDE', 'CBC ', 'SHA'), CipherSuite(0xc035, 'ECDHE', 'PSK ', 'AES_128 ', 'CBC ', 'SHA'), CipherSuite(0xc036, 'ECDHE', 'PSK ', 'AES_256 ', 'CBC ', 'SHA'), CipherSuite(0xc037, 'ECDHE', 'PSK ', 'AES_128 ', 'CBC ', 'SHA256'), CipherSuite(0xc038, 'ECDHE', 'PSK ', 'AES_256 ', 'CBC ', 'SHA384'), CipherSuite(0xc039, 'ECDHE', 'PSK ', 'NULL ', ' ', 'SHA'), CipherSuite(0xc03a, 'ECDHE', 'PSK ', 'NULL ', ' ', 'SHA256'), CipherSuite(0xc03b, 'ECDHE', 'PSK ', 'NULL ', ' ', 'SHA384'), # RFC6209 CipherSuite(0xc03c, 'RSA ', ' ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc03d, 'RSA ', ' ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc03e, 'DH ', 'DSS ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc03f, 'DH ', 'DSS ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc040, 'DH ', 'RSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc041, 'DH ', 'RSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc042, 'DHE ', 'DSS ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc043, 'DHE ', 'DSS ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc044, 'DHE ', 'RSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc045, 'DHE ', 'RSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc046, 'DH ', 'anon ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc047, 'DH ', 'anon ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc048, 'ECDHE', 'ECDSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc049, 'ECDHE', 'ECDSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc04a, 'ECDH ', 'ECDSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc04b, 'ECDH ', 'ECDSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc04c, 'ECDHE', 'RSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc04d, 'ECDHE', 'RSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc04e, 'ECDH ', 'RSA ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc04f, 'ECDH ', 'RSA ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc050, 'RSA ', ' ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc051, 'RSA ', ' ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc052, 'DHE ', 'RSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc053, 'DHE ', 'RSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc054, 'DH ', 'RSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc055, 'DH ', 'RSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc056, 'DHE ', 'DSS ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc057, 'DHE ', 'DSS ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc058, 'DH ', 'DSS ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc059, 'DH ', 'DSS ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc05a, 'DH ', 'anon ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc05b, 'DH ', 'anon ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc05c, 'ECDHE', 'ECDSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc05d, 'ECDHE', 'ECDSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc05e, 'ECDH ', 'ECDSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc05f, 'ECDH ', 'ECDSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc060, 'ECDHE', 'RSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc061, 'ECDHE', 'RSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc062, 'ECDH ', 'RSA ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc063, 'ECDH ', 'RSA ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc064, ' ', 'PSK ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc065, ' ', 'PSK ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc066, 'DHE ', 'PSK ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc067, 'DHE ', 'PSK ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc068, 'RSA ', 'PSK ', 'ARIA_128', 'CBC ', 'SHA256'), CipherSuite(0xc069, 'RSA ', 'PSK ', 'ARIA_256', 'CBC ', 'SHA384'), CipherSuite(0xc06a, ' ', 'PSK ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc06b, ' ', 'PSK ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc06c, 'DHE ', 'PSK ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc06d, 'DHE ', 'PSK ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc06e, 'RSA ', 'PSK ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc06f, 'RSA ', 'PSK ', 'ARIA_256', 'GCM ', 'SHA384'), CipherSuite(0xc070, 'ECDHE', 'PSK ', 'ARIA_128', 'GCM ', 'SHA256'), CipherSuite(0xc071, 'ECDHE', 'PSK ', 'ARIA_256', 'GCM ', 'SHA384'), # RFC6367 CipherSuite(0xc072, 'ECDHE', 'ECDSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc073, 'ECDHE', 'ECDSA ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc074, 'ECDH ', 'ECDSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc075, 'ECDH ', 'ECDSA ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc076, 'ECDHE', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc077, 'ECDHE', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc078, 'ECDH ', 'RSA ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc079, 'ECDH ', 'RSA ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc07a, 'RSA ', ' ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc07b, 'RSA ', ' ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc07c, 'DHE ', 'RSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc07d, 'DHE ', 'RSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc07e, 'DH ', 'RSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc07f, 'DH ', 'RSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc080, 'DHE ', 'DSS ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc081, 'DHE ', 'DSS ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc082, 'DH ', 'DSS ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc083, 'DH ', 'DSS ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc084, 'DH ', 'anon ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc085, 'DH ', 'anon ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc086, 'ECDHE', 'ECDSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc087, 'ECDHE', 'ECDSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc088, 'ECDH ', 'ECDSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc089, 'ECDH ', 'ECDSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc08a, 'ECDHE', 'RSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc08b, 'ECDHE', 'RSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc08c, 'ECDH ', 'RSA ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc08d, 'ECDH ', 'RSA ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc08e, ' ', 'PSK ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc08f, ' ', 'PSK ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc090, 'DHE ', 'PSK ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc091, 'DHE ', 'PSK ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc092, 'RSA ', 'PSK ', 'CAMELLIA_128', 'GCM', 'SHA256'), CipherSuite(0xc093, 'RSA ', 'PSK ', 'CAMELLIA_256', 'GCM', 'SHA384'), CipherSuite(0xc094, ' ', 'PSK ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc095, ' ', 'PSK ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc096, 'DHE ', 'PSK ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc097, 'DHE ', 'PSK ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc098, 'RSA ', 'PSK ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc099, 'RSA ', 'PSK ', 'CAMELLIA_256', 'CBC', 'SHA384'), CipherSuite(0xc09a, 'ECDHE', 'PSK ', 'CAMELLIA_128', 'CBC', 'SHA256'), CipherSuite(0xc09b, 'ECDHE', 'PSK ', 'CAMELLIA_256', 'CBC', 'SHA384'), # RFC6655 CipherSuite(0xc09c, 'RSA ', ' ', 'AES_128 ', 'CCM ', ''), CipherSuite(0xc09d, 'RSA ', ' ', 'AES_256 ', 'CCM ', ''), CipherSuite(0xc09e, 'DHE ', 'RSA ', 'AES_128 ', 'CCM ', ''), CipherSuite(0xc09f, 'DHE ', 'RSA ', 'AES_256 ', 'CCM ', ''), CipherSuite(0xc0a0, 'RSA ', ' ', 'AES_128 ', 'CCM_8', ''), CipherSuite(0xc0a1, 'RSA ', ' ', 'AES_256 ', 'CCM_8', ''), CipherSuite(0xc0a2, 'DHE ', 'RSA ', 'AES_128 ', 'CCM_8', ''), CipherSuite(0xc0a3, 'DHE ', 'RSA ', 'AES_256 ', 'CCM_8', ''), CipherSuite(0xc0a4, ' ', 'PSK ', 'AES_128 ', 'CCM ', ''), CipherSuite(0xc0a5, ' ', 'PSK ', 'AES_256 ', 'CCM ', ''), CipherSuite(0xc0a6, 'DHE ', 'PSK ', 'AES_128 ', 'CCM ', ''), CipherSuite(0xc0a7, 'DHE ', 'PSK ', 'AES_256 ', 'CCM ', ''), CipherSuite(0xc0a8, ' ', 'PSK ', 'AES_128 ', 'CCM_8', ''), CipherSuite(0xc0a9, ' ', 'PSK ', 'AES_256 ', 'CCM_8', ''), CipherSuite(0xc0aa, 'DHE ', 'PSK ', 'AES_128 ', 'CCM_8', ''), CipherSuite(0xc0ab, 'DHE ', 'PSK ', 'AES_256 ', 'CCM_8', ''), # RFC7251 CipherSuite(0xc0ac, 'ECDHE', 'ECDSA ', 'AES_128 ', 'CCM ', ''), CipherSuite(0xc0ad, 'ECDHE', 'ECDSA ', 'AES_256 ', 'CCM ', ''), CipherSuite(0xc0ae, 'ECDHE', 'ECDSA ', 'AES_128 ', 'CCM_8', ''), CipherSuite(0xc0af, 'ECDHE', 'ECDSA ', 'AES_256 ', 'CCM_8', ''), # Unassigned: 0xc0b0-0xcca7 CipherSuite(0xcc13, 'ECDHE', 'RSA ', 'CHACHA20', 'POLY1305', 'SHA256', 'OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'), CipherSuite(0xcc14, 'ECDHE', 'ECDSA ', 'CHACHA20', 'POLY1305', 'SHA256', 'OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256'), CipherSuite(0xcc15, 'DHE ', 'RSA ', 'CHACHA20', 'POLY1305', 'SHA256', 'OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256'), # RFC7905 CipherSuite(0xcca8, 'ECDHE', 'RSA ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xcca9, 'ECDHE', 'ECDSA ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xccaa, 'DHE ', 'RSA ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xccab, ' ', 'PSK ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xccac, 'ECDHE', 'PSK ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xccad, 'DHE ', 'PSK ', 'CHACHA20', 'POLY1305', 'SHA256'), CipherSuite(0xccae, 'RSA ', 'PSK ', 'CHACHA20', 'POLY1305', 'SHA256'), # RFC8701 // GREASE (Generate Random Extensions And Sustain Extensibility) CipherSuite(0x0a0a, '', '', '', '', '', 'GREASE'), CipherSuite(0x1a1a, '', '', '', '', '', 'GREASE'), CipherSuite(0x2a2a, '', '', '', '', '', 'GREASE'), CipherSuite(0x3a3a, '', '', '', '', '', 'GREASE'), CipherSuite(0x4a4a, '', '', '', '', '', 'GREASE'), CipherSuite(0x5a5a, '', '', '', '', '', 'GREASE'), CipherSuite(0x6a6a, '', '', '', '', '', 'GREASE'), CipherSuite(0x7a7a, '', '', '', '', '', 'GREASE'), CipherSuite(0x8a8a, '', '', '', '', '', 'GREASE'), CipherSuite(0x9a9a, '', '', '', '', '', 'GREASE'), CipherSuite(0xaaaa, '', '', '', '', '', 'GREASE'), CipherSuite(0xbaba, '', '', '', '', '', 'GREASE'), CipherSuite(0xcaca, '', '', '', '', '', 'GREASE'), CipherSuite(0xdada, '', '', '', '', '', 'GREASE'), CipherSuite(0xeaea, '', '', '', '', '', 'GREASE'), CipherSuite(0xfafa, '', '', '', '', '', 'GREASE'), # Unassigned: 0xccaf-0xfefd # Reserved: 0xfefe-0xffff ] BY_CODE = dict( (cipher.code, cipher) for cipher in CIPHERSUITES) # This is a function to avoid artificially increased coverage BY_NAME_DICT = None def BY_NAME(name): # We initialize the dictionary only on the first call global BY_NAME_DICT if BY_NAME_DICT is None: BY_NAME_DICT = dict((suite.name, suite) for suite in CIPHERSUITES) return BY_NAME_DICT[name] NULL_SUITE = BY_CODE[0x0000] class TestCipherSuites(object): def test_kx(self): # A test from each RFC assert (BY_CODE[0x0005].kx == 'RSA') assert (BY_CODE[0x0021].kx == 'KRB5') assert (BY_CODE[0x002d].kx == 'DHE') assert (BY_CODE[0x0034].kx == 'DH') assert (BY_CODE[0x003c].kx == 'RSA') assert (BY_CODE[0x0042].kx == 'DH') assert (BY_CODE[0x006a].kx == 'DHE') assert (BY_CODE[0x0084].kx == 'RSA') assert (BY_CODE[0x0091].kx == 'DHE') assert (BY_CODE[0x0098].kx == 'DH') assert (BY_CODE[0x00ab].kx == 'DHE') assert (BY_CODE[0x00b0].kx == 'PSK') assert (BY_CODE[0x00bb].kx == 'DH') assert (BY_CODE[0xc008].kx == 'ECDHE') assert (BY_CODE[0xc016].kx == 'ECDH') assert (BY_CODE[0xc01d].kx == 'SRP_SHA') assert (BY_CODE[0xc027].kx == 'ECDHE') assert (BY_CODE[0xc036].kx == 'ECDHE') assert (BY_CODE[0xc045].kx == 'DHE') assert (BY_CODE[0xc052].kx == 'DHE') assert (BY_CODE[0xc068].kx == 'RSA') assert (BY_CODE[0xc074].kx == 'ECDH') assert (BY_CODE[0xc08d].kx == 'ECDH') assert (BY_CODE[0xc09d].kx == 'RSA') assert (BY_CODE[0xc0a2].kx == 'DHE') assert (BY_CODE[0xc0ad].kx == 'ECDHE') assert (BY_CODE[0xcc13].kx == 'ECDHE') assert (BY_CODE[0xcca8].kx == 'ECDHE') assert (BY_CODE[0xccae].kx == 'RSA') def test_auth(self): # A test from each RFC assert (BY_CODE[0x0005].auth == 'RSA') assert (BY_CODE[0x0021].auth == 'KRB5') assert (BY_CODE[0x002d].auth == 'PSK') assert (BY_CODE[0x0034].auth == 'anon') assert (BY_CODE[0x003c].auth == 'RSA') assert (BY_CODE[0x0042].auth == 'DSS') assert (BY_CODE[0x006a].auth == 'DSS') assert (BY_CODE[0x0084].auth == 'RSA') assert (BY_CODE[0x0091].auth == 'PSK') assert (BY_CODE[0x0098].auth == 'RSA') assert (BY_CODE[0x00ab].auth == 'PSK') assert (BY_CODE[0x00b0].auth == 'PSK') assert (BY_CODE[0x00bb].auth == 'DSS') assert (BY_CODE[0xc008].auth == 'ECDSA') assert (BY_CODE[0xc016].auth == 'anon') assert (BY_CODE[0xc01d].auth == 'SRP_SHA') assert (BY_CODE[0xc027].auth == 'RSA') assert (BY_CODE[0xc036].auth == 'PSK') assert (BY_CODE[0xc045].auth == 'RSA') assert (BY_CODE[0xc052].auth == 'RSA') assert (BY_CODE[0xc068].auth == 'PSK') assert (BY_CODE[0xc074].auth == 'ECDSA') assert (BY_CODE[0xc08d].auth == 'RSA') assert (BY_CODE[0xc09d].auth == 'RSA') assert (BY_CODE[0xc0a2].auth == 'RSA') assert (BY_CODE[0xc0ad].auth == 'ECDSA') assert (BY_CODE[0xcc14].auth == 'ECDSA') assert (BY_CODE[0xcca8].auth == 'RSA') assert (BY_CODE[0xccae].auth == 'PSK') def test_pfs(self): assert (BY_NAME('TLS_RSA_WITH_RC4_128_SHA').pfs is False) assert (BY_NAME('TLS_DHE_DSS_WITH_AES_256_CBC_SHA256').pfs is True) assert (BY_NAME('TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA').pfs is True) def test_aead(self): assert (BY_NAME('TLS_RSA_WITH_AES_128_CBC_SHA256').aead is False) assert (BY_NAME('TLS_RSA_WITH_AES_256_CCM').aead is True) assert (BY_NAME('TLS_DHE_RSA_WITH_AES_128_CCM_8').aead is True) assert (BY_NAME('TLS_DHE_PSK_WITH_AES_256_GCM_SHA384').aead is True) def test_anonymous(self): assert (BY_NAME('TLS_RSA_WITH_RC4_128_SHA').anonymous is False) assert (BY_NAME('TLS_DH_anon_WITH_AES_128_CBC_SHA').anonymous is True) assert (BY_NAME('TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA').anonymous is True) def test_by_name_and_code(self): # Special cases: # - explicit name assert (BY_CODE[0x00ff] == BY_NAME('TLS_EMPTY_RENEGOTIATION_INFO')) # - explicit encoding (DES_40 + CBC = DES_CBC_40) assert (BY_CODE[0x0026] == BY_NAME('TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA')) # A test from each RFC assert (BY_CODE[0x0005] == BY_NAME('TLS_RSA_WITH_RC4_128_SHA')) assert (BY_CODE[0x0021] == BY_NAME('TLS_KRB5_WITH_IDEA_CBC_SHA')) assert (BY_CODE[0x002d] == BY_NAME('TLS_DHE_PSK_WITH_NULL_SHA')) assert (BY_CODE[0x0034] == BY_NAME('TLS_DH_anon_WITH_AES_128_CBC_SHA')) assert (BY_CODE[0x003c] == BY_NAME('TLS_RSA_WITH_AES_128_CBC_SHA256')) assert (BY_CODE[0x0042] == BY_NAME('TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA')) assert (BY_CODE[0x006a] == BY_NAME('TLS_DHE_DSS_WITH_AES_256_CBC_SHA256')) assert (BY_CODE[0x0084] == BY_NAME('TLS_RSA_WITH_CAMELLIA_256_CBC_SHA')) assert (BY_CODE[0x0091] == BY_NAME('TLS_DHE_PSK_WITH_AES_256_CBC_SHA')) assert (BY_CODE[0x0098] == BY_NAME('TLS_DH_RSA_WITH_SEED_CBC_SHA')) assert (BY_CODE[0x00ab] == BY_NAME('TLS_DHE_PSK_WITH_AES_256_GCM_SHA384')) assert (BY_CODE[0x00b0] == BY_NAME('TLS_PSK_WITH_NULL_SHA256')) assert (BY_CODE[0x00bb] == BY_NAME('TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256')) assert (BY_CODE[0x1303] == BY_NAME('TLS_CHACHA20_POLY1305_SHA256')) assert (BY_CODE[0xc008] == BY_NAME('TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA')) assert (BY_CODE[0xc016] == BY_NAME('TLS_ECDH_anon_WITH_RC4_128_SHA')) assert (BY_CODE[0xc01d] == BY_NAME('TLS_SRP_SHA_WITH_AES_128_CBC_SHA')) assert (BY_CODE[0xc027] == BY_NAME('TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256')) assert (BY_CODE[0xc036] == BY_NAME('TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA')) assert (BY_CODE[0xc045] == BY_NAME('TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384')) assert (BY_CODE[0xc052] == BY_NAME('TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256')) assert (BY_CODE[0xc068] == BY_NAME('TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256')) assert (BY_CODE[0xc074] == BY_NAME('TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256')) assert (BY_CODE[0xc08d] == BY_NAME('TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384')) assert (BY_CODE[0xc09d] == BY_NAME('TLS_RSA_WITH_AES_256_CCM')) assert (BY_CODE[0xc0a2] == BY_NAME('TLS_DHE_RSA_WITH_AES_128_CCM_8')) assert (BY_CODE[0xc0ad] == BY_NAME('TLS_ECDHE_ECDSA_WITH_AES_256_CCM')) assert (BY_CODE[0xcca8] == BY_NAME('TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256')) assert (BY_CODE[0xccae] == BY_NAME('TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256')) assert (BY_CODE[0xcc15] == BY_NAME('OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256')) def test_repr(self): cs = CipherSuite(0x0009, 'RSA', ' ', 'DES ', 'CBC ', 'SHA') assert repr(cs) == "CipherSuite(0x0009, TLS_RSA_WITH_DES_CBC_SHA)" assert cs.mac_size == 20 assert cs.block_size == 8 repr(BY_CODE[0x6a6a]) == "CipherSuite(0x6a6a, GREASE)" dpkt-1.9.8/dpkt/stp.py000066400000000000000000000037251427735150300146330ustar00rootroot00000000000000# $Id: stp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Spanning Tree Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt class STP(dpkt.Packet): """Spanning Tree Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of STP. TODO. """ __hdr__ = ( ('proto_id', 'H', 0), ('v', 'B', 0), ('type', 'B', 0), ('flags', 'B', 0), ('root_id', '8s', b''), ('root_path', 'I', 0), ('bridge_id', '8s', b''), ('port_id', 'H', 0), ('_age', 'H', 0), ('_max_age', 'H', 0), ('_hello', 'H', 0), ('_fd', 'H', 0) ) @property def age(self): return self._age >> 8 @age.setter def age(self, age): self._age = age << 8 @property def max_age(self): return self._max_age >> 8 @max_age.setter def max_age(self, max_age): self._max_age = max_age << 8 @property def hello(self): return self._hello >> 8 @hello.setter def hello(self, hello): self._hello = hello << 8 @property def fd(self): return self._fd >> 8 @fd.setter def fd(self, fd): self._fd = fd << 8 def test_stp(): buf = (b'\x00\x00\x02\x02\x3e\x80\x00\x08\x00\x27\xad\xa3\x41\x00\x00\x00\x00\x80\x00\x08\x00\x27' b'\xad\xa3\x41\x80\x01\x00\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x02\x00\x14\x00') stp = STP(buf) assert stp.proto_id == 0 assert stp.port_id == 0x8001 assert stp.age == 0 assert stp.max_age == 20 assert stp.hello == 2 assert stp.fd == 15 assert bytes(stp) == buf stp.fd = 100 assert stp.pack_hdr()[-2:] == b'\x64\x00' # 100 << 8 def test_properties(): stp = STP() stp.age = 10 assert stp.age == 10 stp.max_age = 20 assert stp.max_age == 20 stp.hello = 1234 assert stp.hello == 1234 dpkt-1.9.8/dpkt/stun.py000066400000000000000000000055041427735150300150130ustar00rootroot00000000000000# $Id: stun.py 47 2008-05-27 02:10:00Z jon.oberheide $ # -*- coding: utf-8 -*- """Simple Traversal of UDP through NAT.""" from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt # STUN - RFC 3489 # http://tools.ietf.org/html/rfc3489 # Each packet has a 20 byte header followed by 0 or more attribute TLVs. # Message Types BINDING_REQUEST = 0x0001 BINDING_RESPONSE = 0x0101 BINDING_ERROR_RESPONSE = 0x0111 SHARED_SECRET_REQUEST = 0x0002 SHARED_SECRET_RESPONSE = 0x0102 SHARED_SECRET_ERROR_RESPONSE = 0x0112 # Message Attributes MAPPED_ADDRESS = 0x0001 RESPONSE_ADDRESS = 0x0002 CHANGE_REQUEST = 0x0003 SOURCE_ADDRESS = 0x0004 CHANGED_ADDRESS = 0x0005 USERNAME = 0x0006 PASSWORD = 0x0007 MESSAGE_INTEGRITY = 0x0008 ERROR_CODE = 0x0009 UNKNOWN_ATTRIBUTES = 0x000a REFLECTED_FROM = 0x000b class STUN(dpkt.Packet): """Simple Traversal of UDP through NAT. STUN - RFC 3489 http://tools.ietf.org/html/rfc3489 Each packet has a 20 byte header followed by 0 or more attribute TLVs. Attributes: __hdr__: Header fields of STUN. type: (int): STUN Message Type (2 bytes) len: (int): Message Length (2 bytes) xid: (bytes): Magic Cookie and Transaction ID (16 bytes) """ __hdr__ = ( ('type', 'H', 0), ('len', 'H', 0), ('xid', '16s', 0) ) def tlv(buf): n = 4 t, l_ = struct.unpack('>HH', buf[:n]) v = buf[n:n + l_] pad = (n - l_ % n) % n buf = buf[n + l_ + pad:] return t, l_, v, buf def parse_attrs(buf): """Parse STUN.data buffer into a list of (attribute, data) tuples.""" attrs = [] while buf: t, _, v, buf = tlv(buf) attrs.append((t, v)) return attrs def test_stun_response(): s = (b'\x01\x01\x00\x0c\x21\x12\xa4\x42\x53\x4f\x70\x43\x69\x69\x35\x4a\x66\x63\x31\x7a\x00\x01' b'\x00\x08\x00\x01\x11\x22\x33\x44\x55\x66') m = STUN(s) assert m.type == BINDING_RESPONSE assert m.len == 12 attrs = parse_attrs(m.data) assert attrs == [(MAPPED_ADDRESS, b'\x00\x01\x11\x22\x33\x44\x55\x66'), ] def test_stun_padded(): s = (b'\x00\x01\x00\x54\x21\x12\xa4\x42\x35\x59\x53\x6e\x42\x71\x70\x56\x77\x61\x39\x4f\x00\x06' b'\x00\x17\x70\x4c\x79\x5a\x48\x52\x3a\x47\x77\x4c\x33\x41\x48\x42\x6f\x76\x75\x62\x4c\x76' b'\x43\x71\x6e\x00\x80\x2a\x00\x08\x18\x8b\x10\x4c\x69\x7b\xf6\x5b\x00\x25\x00\x00\x00\x24' b'\x00\x04\x6e\x00\x1e\xff\x00\x08\x00\x14\x60\x2b\xc7\xfc\x0d\x10\x63\xaa\xc5\x38\x1c\xcb' b'\x96\xa9\x73\x08\x73\x9a\x96\x0c\x80\x28\x00\x04\xd1\x62\xea\x65') m = STUN(s) assert m.type == BINDING_REQUEST assert m.len == 84 attrs = parse_attrs(m.data) assert len(attrs) == 6 assert attrs[0] == (USERNAME, b'pLyZHR:GwL3AHBovubLvCqn') assert attrs[4][0] == MESSAGE_INTEGRITY dpkt-1.9.8/dpkt/tcp.py000066400000000000000000000154711427735150300146140ustar00rootroot00000000000000# $Id: tcp.py 42 2007-08-02 22:38:47Z jon.oberheide $ # -*- coding: utf-8 -*- """Transmission Control Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt from .compat import compat_ord # TCP control flags TH_FIN = 0x01 # end of data TH_SYN = 0x02 # synchronize sequence numbers TH_RST = 0x04 # reset connection TH_PUSH = 0x08 # push TH_ACK = 0x10 # acknowledgment number set TH_URG = 0x20 # urgent pointer set TH_ECE = 0x40 # ECN echo, RFC 3168 TH_CWR = 0x80 # congestion window reduced TH_NS = 0x100 # nonce sum, RFC 3540 TCP_PORT_MAX = 65535 # maximum port TCP_WIN_MAX = 65535 # maximum (unscaled) window def tcp_flags_to_str(val): ff = [] if val & TH_FIN: ff.append('FIN') if val & TH_SYN: ff.append('SYN') if val & TH_RST: ff.append('RST') if val & TH_PUSH: ff.append('PUSH') if val & TH_ACK: ff.append('ACK') if val & TH_URG: ff.append('URG') if val & TH_ECE: ff.append('ECE') if val & TH_CWR: ff.append('CWR') if val & TH_NS: ff.append('NS') return ','.join(ff) class TCP(dpkt.Packet): """Transmission Control Protocol. The Transmission Control Protocol (TCP) is one of the main protocols of the Internet protocol suite. It originated in the initial network implementation in which it complemented the Internet Protocol (IP). Attributes: sport - source port dport - destination port seq - sequence number ack - acknowledgement number off - data offset in 32-bit words flags - TCP flags win - TCP window size sum - checksum urp - urgent pointer opts - TCP options buffer; call parse_opts() to parse """ __hdr__ = ( ('sport', 'H', 0xdead), ('dport', 'H', 0), ('seq', 'I', 0xdeadbeef), ('ack', 'I', 0), ('_off_flags', 'H', ((5 << 12) | TH_SYN)), ('win', 'H', TCP_WIN_MAX), ('sum', 'H', 0), ('urp', 'H', 0) ) __bit_fields__ = { '_off_flags': ( ('off', 4), # 4 hi bits ('_rsv', 3), # 3 bits reserved ('flags', 9), # 9 lo bits ) } __pprint_funcs__ = { 'flags': tcp_flags_to_str, 'sum': hex, # display checksum in hex } opts = b'' def __len__(self): return self.__hdr_len__ + len(self.opts) + len(self.data) def __bytes__(self): return self.pack_hdr() + bytes(self.opts) + bytes(self.data) def unpack(self, buf): dpkt.Packet.unpack(self, buf) ol = ((self._off_flags >> 12) << 2) - self.__hdr_len__ if ol < 0: raise dpkt.UnpackError('invalid header length') self.opts = buf[self.__hdr_len__:self.__hdr_len__ + ol] self.data = buf[self.__hdr_len__ + ol:] # Options (opt_type) - http://www.iana.org/assignments/tcp-parameters TCP_OPT_EOL = 0 # end of option list TCP_OPT_NOP = 1 # no operation TCP_OPT_MSS = 2 # maximum segment size TCP_OPT_WSCALE = 3 # window scale factor, RFC 1072 TCP_OPT_SACKOK = 4 # SACK permitted, RFC 2018 TCP_OPT_SACK = 5 # SACK, RFC 2018 TCP_OPT_ECHO = 6 # echo (obsolete), RFC 1072 TCP_OPT_ECHOREPLY = 7 # echo reply (obsolete), RFC 1072 TCP_OPT_TIMESTAMP = 8 # timestamp, RFC 1323 TCP_OPT_POCONN = 9 # partial order conn, RFC 1693 TCP_OPT_POSVC = 10 # partial order service, RFC 1693 TCP_OPT_CC = 11 # connection count, RFC 1644 TCP_OPT_CCNEW = 12 # CC.NEW, RFC 1644 TCP_OPT_CCECHO = 13 # CC.ECHO, RFC 1644 TCP_OPT_ALTSUM = 14 # alt checksum request, RFC 1146 TCP_OPT_ALTSUMDATA = 15 # alt checksum data, RFC 1146 TCP_OPT_SKEETER = 16 # Skeeter TCP_OPT_BUBBA = 17 # Bubba TCP_OPT_TRAILSUM = 18 # trailer checksum TCP_OPT_MD5 = 19 # MD5 signature, RFC 2385 TCP_OPT_SCPS = 20 # SCPS capabilities TCP_OPT_SNACK = 21 # selective negative acks TCP_OPT_REC = 22 # record boundaries TCP_OPT_CORRUPT = 23 # corruption experienced TCP_OPT_SNAP = 24 # SNAP TCP_OPT_TCPCOMP = 26 # TCP compression filter TCP_OPT_MAX = 27 def parse_opts(buf): """Parse TCP option buffer into a list of (option, data) tuples.""" opts = [] while buf: o = compat_ord(buf[0]) if o > TCP_OPT_NOP: try: # advance buffer at least 2 bytes = 1 type + 1 length l_ = max(2, compat_ord(buf[1])) d, buf = buf[2:l_], buf[l_:] except (IndexError, ValueError): # print 'bad option', repr(str(buf)) opts.append(None) # XXX break else: # options 0 and 1 are not followed by length byte d, buf = b'', buf[1:] opts.append((o, d)) return opts def test_parse_opts(): # normal scenarios buf = b'\x02\x04\x23\x00\x01\x01\x04\x02' opts = parse_opts(buf) assert opts == [ (TCP_OPT_MSS, b'\x23\x00'), (TCP_OPT_NOP, b''), (TCP_OPT_NOP, b''), (TCP_OPT_SACKOK, b'') ] buf = b'\x01\x01\x05\x0a\x37\xf8\x19\x70\x37\xf8\x29\x78' opts = parse_opts(buf) assert opts == [ (TCP_OPT_NOP, b''), (TCP_OPT_NOP, b''), (TCP_OPT_SACK, b'\x37\xf8\x19\x70\x37\xf8\x29\x78') ] # test a zero-length option buf = b'\x02\x00\x01' opts = parse_opts(buf) assert opts == [ (TCP_OPT_MSS, b''), (TCP_OPT_NOP, b'') ] # test a one-byte malformed option buf = b'\xff' opts = parse_opts(buf) assert opts == [None] def test_offset(): tcpheader = TCP(b'\x01\xbb\xc0\xd7\xb6\x56\xa8\xb9\xd1\xac\xaa\xb1\x50\x18\x40\x00\x56\xf8\x00\x00') assert tcpheader.off == 5 # test setting header offset tcpheader.off = 8 assert bytes(tcpheader) == b'\x01\xbb\xc0\xd7\xb6\x56\xa8\xb9\xd1\xac\xaa\xb1\x80\x18\x40\x00\x56\xf8\x00\x00' def test_tcp_flags_to_str(): assert tcp_flags_to_str(0x18) == 'PUSH,ACK' assert tcp_flags_to_str(0x12) == 'SYN,ACK' # for code coverage assert tcp_flags_to_str(0x1ff) == 'FIN,SYN,RST,PUSH,ACK,URG,ECE,CWR,NS' def test_tcp_unpack(): data = (b'\x00\x50\x0d\x2c\x11\x4c\x61\x8b\x38\xaf\xfe\x14\x70\x12\x16\xd0' b'\x5b\xdc\x00\x00\x02\x04\x05\x64\x01\x01\x04\x02') tcp = TCP(data) assert tcp.flags == (TH_SYN | TH_ACK) assert tcp.off == 7 assert tcp.win == 5840 assert tcp.dport == 3372 assert tcp.seq == 290218379 assert tcp.ack == 951057940 def test_tcp_pack(): tcp = TCP( sport=3372, dport=80, seq=951057939, ack=0, off=7, flags=TH_SYN, win=8760, sum=0xc30c, urp=0, opts=b'\x02\x04\x05\xb4\x01\x01\x04\x02' ) assert bytes(tcp) == ( b'\x0d\x2c\x00\x50\x38\xaf\xfe\x13\x00\x00\x00\x00\x70\x02\x22\x38' b'\xc3\x0c\x00\x00\x02\x04\x05\xb4\x01\x01\x04\x02') # TODO: add checksum calculation dpkt-1.9.8/dpkt/telnet.py000066400000000000000000000123401427735150300153110ustar00rootroot00000000000000# $Id: telnet.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Telnet.""" from __future__ import print_function from __future__ import absolute_import import struct from .compat import compat_ord IAC = 255 # interpret as command: DONT = 254 # you are not to use option DO = 253 # please, you use option WONT = 252 # I won't use option WILL = 251 # I will use option SB = 250 # interpret as subnegotiation GA = 249 # you may reverse the line EL = 248 # erase the current line EC = 247 # erase the current character AYT = 246 # are you there AO = 245 # abort output--but let prog finish IP = 244 # interrupt process--permanently BREAK = 243 # break DM = 242 # data mark--for connect. cleaning NOP = 241 # nop SE = 240 # end sub negotiation EOR = 239 # end of record (transparent mode) ABORT = 238 # Abort process SUSP = 237 # Suspend process xEOF = 236 # End of file: EOF is already used... SYNCH = 242 # for telfunc calls def strip_options(buf): """Return a list of lines and dict of options from telnet data.""" l_ = buf.split(struct.pack("B", IAC)) # print l_ b = [] d = {} subopt = False for w in l_: if not w: continue o = compat_ord(w[0]) if o > SB: # print 'WILL/WONT/DO/DONT/IAC', `w` w = w[2:] elif o == SE: # print 'SE', `w` w = w[1:] subopt = False elif o == SB: # print 'SB', `w` subopt = True for opt in (b'USER', b'DISPLAY', b'TERM'): p = w.find(opt + b'\x01') if p != -1: d[opt] = w[p + len(opt) + 1:].split(b'\x00', 1)[0] w = None elif subopt: w = None if w: w = w.replace(b'\x00', b'\n').splitlines() if not w[-1]: w.pop() b.extend(w) return b, d def test_telnet(): l_ = [] s = (b'\xff\xfb\x25\xff\xfa\x25\x00\x00\x00\xff\xf0\xff\xfd\x26\xff\xfa\x26\x05\xff\xf0\xff\xfa' b'\x26\x01\x01\x02\xff\xf0\xff\xfb\x18\xff\xfb\x20\xff\xfb\x23\xff\xfb\x27\xff\xfc\x24\xff' b'\xfa\x20\x00\x33\x38\x34\x30\x30\x2c\x33\x38\x34\x30\x30\xff\xf0\xff\xfa\x23\x00\x64\x6f' b'\x75\x67\x68\x62\x6f\x79\x2e\x63\x69\x74\x69\x2e\x75\x6d\x69\x63\x68\x2e\x65\x64\x75\x3a' b'\x30\x2e\x30\xff\xf0\xff\xfa\x27\x00\x00\x44\x49\x53\x50\x4c\x41\x59\x01\x64\x6f\x75\x67' b'\x68\x62\x6f\x79\x2e\x63\x69\x74\x69\x2e\x75\x6d\x69\x63\x68\x2e\x65\x64\x75\x3a\x30\x2e' b'\x30\x00\x55\x53\x45\x52\x01\x64\x75\x67\x73\x6f\x6e\x67\xff\xf0\xff\xfa\x18\x00\x58\x54' b'\x45\x52\x4d\xff\xf0\xff\xfd\x03\xff\xfc\x01\xff\xfb\x1f\xff\xfa\x1f\x00\x50\x00\x28\xff' b'\xf0\xff\xfd\x05\xff\xfb\x21\xff\xfd\x01\x66\x75\x67\x6c\x79\x0d\x00\x79\x6f\x64\x61\x0d' b'\x00\x62\x61\x73\x68\x74\x61\x72\x64\x0d\x00') l_.append(s) s = (b'\xff\xfd\x01\xff\xfd\x03\xff\xfb\x18\xff\xfb\x1f\xff\xfa\x1f\x00\x58\x00\x32\xff\xf0\x61' b'\x64\x6d\x69\x6e\x0d\x00\xff\xfa\x18\x00\x4c\x49\x4e\x55\x58\xff\xf0\x66\x6f\x6f\x62\x61' b'\x72\x0d\x00\x65\x6e\x61\x62\x6c\x65\x0d\x00\x66\x6f\x6f\x62\x61\x72\x0d\x00\x0d\x00\x73' b'\x68\x6f\x77\x20\x69\x70\x20\x69\x6e\x74\x20\x56\x6c\x61\x6e\x20\x36\x36\x36\x0d\x00') l_.append(s) s = (b'\xff\xfb\x25\xff\xfa\x25\x00\x00\x00\xff\xf0\xff\xfd\x26\xff\xfa\x26\x05\xff\xf0\xff\xfa' b'\x26\x01\x01\x02\xff\xf0\xff\xfb\x26\xff\xfb\x18\xff\xfb\x20\xff\xfb\x23\xff\xfb\x27\xff' b'\xfc\x24\xff\xfa\x20\x00\x33\x38\x34\x30\x30\x2c\x33\x38\x34\x30\x30\xff\xf0\xff\xfa\x23' b'\x00\x64\x6f\x75\x67\x68\x62\x6f\x79\x2e\x63\x69\x74\x69\x2e\x75\x6d\x69\x63\x68\x2e\x65' b'\x64\x75\x3a\x30\x2e\x30\xff\xf0\xff\xfa\x27\x00\x00\x44\x49\x53\x50\x4c\x41\x59\x01\x64' b'\x6f\x75\x67\x68\x62\x6f\x79\x2e\x63\x69\x74\x69\x2e\x75\x6d\x69\x63\x68\x2e\x65\x64\x75' b'\x3a\x30\x2e\x30\x00\x55\x53\x45\x52\x01\x64\x75\x67\x73\x6f\x6e\x67\xff\xf0\xff\xfa\x18' b'\x00\x58\x54\x45\x52\x4d\xff\xf0\xff\xfd\x03\xff\xfc\x01\xff\xfb\x22\xff\xfa\x22\x03\x01' b'\x03\x00\x03\x62\x03\x04\x02\x0f\x05\x00\xff\xff\x07\x62\x1c\x08\x02\x04\x09\x42\x1a\x0a' b'\x02\x7f\x0b\x02\x15\x0c\x02\x17\x0d\x02\x12\x0e\x02\x16\x0f\x02\x11\x10\x02\x13\x11\x00' b'\xff\xff\x12\x00\xff\xff\xff\xf0\xff\xfb\x1f\xff\xfa\x1f\x00\x50\x00\x28\xff\xf0\xff\xfd' b'\x05\xff\xfb\x21\xff\xfa\x22\x01\x0f\xff\xf0\xff\xfd\x01\xff\xfe\x01\xff\xfa\x22\x03\x01' b'\x80\x00\xff\xf0\xff\xfd\x01\x77\x65\x72\x64\x0d\x0a\xff\xfe\x01\x79\x6f\x64\x61\x0d\x0a' b'\xff\xfd\x01\x64\x61\x72\x74\x68\x76\x61\x64\x65\x72\x0d\x0a\xff\xfe\x01') l_.append(s) exp = [([b'fugly', b'yoda', b'bashtard'], {b'USER': b'dugsong', b'DISPLAY': b'doughboy.citi.umich.edu:0.0'}), ([b'admin', b'foobar', b'enable', b'foobar', b'', b'show ip int Vlan 666'], {}), ([b'werd', b'yoda', b'darthvader'], {b'USER': b'dugsong', b'DISPLAY': b'doughboy.citi.umich.edu:0.0'})] assert (list(map(strip_options, l_)) == exp) def test_trailing_null(): from binascii import unhexlify buf = unhexlify( '0100020000' ) b, d = strip_options(buf) assert b == [b'\x01', b'\x02'] assert d == {} dpkt-1.9.8/dpkt/tftp.py000066400000000000000000000073331427735150300150010ustar00rootroot00000000000000# $Id: tftp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Trivial File Transfer Protocol.""" from __future__ import print_function from __future__ import absolute_import import struct from . import dpkt # Opcodes OP_RRQ = 1 # read request OP_WRQ = 2 # write request OP_DATA = 3 # data packet OP_ACK = 4 # acknowledgment OP_ERR = 5 # error code # Error codes EUNDEF = 0 # not defined ENOTFOUND = 1 # file not found EACCESS = 2 # access violation ENOSPACE = 3 # disk full or allocation exceeded EBADOP = 4 # illegal TFTP operation EBADID = 5 # unknown transfer ID EEXISTS = 6 # file already exists ENOUSER = 7 # no such user class TFTP(dpkt.Packet): """Trivial File Transfer Protocol. Trivial File Transfer Protocol (TFTP) is a simple lockstep File Transfer Protocol which allows a client to get a file from or put a file onto a remote host. One of its primary uses is in the early stages of nodes booting from a local area network. TFTP has been used for this application because it is very simple to implement. Attributes: __hdr__: Header fields of TFTP. opcode: Operation Code (2 bytes) """ __hdr__ = (('opcode', 'H', 1), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) if self.opcode in (OP_RRQ, OP_WRQ): l_ = self.data.split(b'\x00') self.filename = l_[0] self.mode = l_[1] self.data = b'' elif self.opcode in (OP_DATA, OP_ACK): self.block = struct.unpack('>H', self.data[:2])[0] self.data = self.data[2:] elif self.opcode == OP_ERR: self.errcode = struct.unpack('>H', self.data[:2])[0] self.errmsg = self.data[2:].split(b'\x00')[0] self.data = b'' def __len__(self): return len(bytes(self)) def __bytes__(self): if self.opcode in (OP_RRQ, OP_WRQ): s = self.filename + b'\x00' + self.mode + b'\x00' elif self.opcode in (OP_DATA, OP_ACK): s = struct.pack('>H', self.block) elif self.opcode == OP_ERR: s = struct.pack('>H', self.errcode) + (b'%s\x00' % self.errmsg) else: s = b'' return self.pack_hdr() + s + self.data def test_op_rrq(): from binascii import unhexlify buf = unhexlify( '0001' # opcode (OP_RRQ) '726663313335302e747874' # filename (rfc1350.txt) '00' # null terminator '6f63746574' # mode (octet) '00' # null terminator ) tftp = TFTP(buf) assert tftp.filename == b'rfc1350.txt' assert tftp.mode == b'octet' assert bytes(tftp) == buf assert len(tftp) == len(buf) def test_op_data(): from binascii import unhexlify buf = unhexlify( '0003' # opcode (OP_DATA) '0001' # block '0a0a4e6574776f726b20576f726b696e672047726f7570' ) tftp = TFTP(buf) assert tftp.block == 1 assert tftp.data == b'\x0a\x0aNetwork Working Group' assert bytes(tftp) == buf assert len(tftp) == len(buf) def test_op_err(): from binascii import unhexlify buf = unhexlify( '0005' # opcode (OP_ERR) '0007' # errcode (ENOUSER) '0a0a4e6574776f726b20576f726b696e672047726f757000' ) tftp = TFTP(buf) assert tftp.errcode == ENOUSER assert tftp.errmsg == b'\x0a\x0aNetwork Working Group' assert tftp.data == b'' assert bytes(tftp) == buf def test_op_other(): from binascii import unhexlify buf = unhexlify( '0006' # opcode (doesn't exist) 'abcdef' # trailing data ) tftp = TFTP(buf) assert tftp.opcode == 6 assert bytes(tftp) == buf assert tftp.data == unhexlify('abcdef') dpkt-1.9.8/dpkt/tns.py000066400000000000000000000023371427735150300146270ustar00rootroot00000000000000# $Id: tns.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Transparent Network Substrate.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt class TNS(dpkt.Packet): """Transparent Network Substrate. TODO: Longer class information.... Attributes: __hdr__: Header fields of TNS. TODO. """ __hdr__ = ( ('length', 'H', 0), ('pktsum', 'H', 0), ('type', 'B', 0), ('rsvd', 'B', 0), ('hdrsum', 'H', 0), ('msg', '0s', b''), ) def unpack(self, buf): dpkt.Packet.unpack(self, buf) n = self.length - self.__hdr_len__ if n > len(self.data): raise dpkt.NeedData('short message (missing %d bytes)' % (n - len(self.data))) self.msg = self.data[:n] self.data = self.data[n:] def test_tns(): s = (b'\x00\x23\x00\x00\x01\x00\x00\x00\x01\x34\x01\x2c\x00\x00\x08\x00\x7f' b'\xff\x4f\x98\x00\x00\x00\x01\x00\x01\x00\x22\x00\x00\x00\x00\x01\x01X') t = TNS(s) assert t.msg.startswith(b'\x01\x34') # test a truncated packet try: t = TNS(s[:-10]) except dpkt.NeedData: pass dpkt-1.9.8/dpkt/tpkt.py000066400000000000000000000014771427735150300150110ustar00rootroot00000000000000# $Id: tpkt.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """ISO Transport Service on top of the TCP (TPKT).""" from __future__ import absolute_import from . import dpkt # TPKT - RFC 1006 Section 6 # http://www.faqs.org/rfcs/rfc1006.html class TPKT(dpkt.Packet): """ISO Transport Service on top of the TCP (TPKT). "Emulate" ISO transport services COTP on top of TCP. The two major points missing in TCP (compared to COTP) are the TSAP addressing and the detection of packet boundaries on the receiving host. Attributes: __hdr__: Header fields of TPKT. v: (int): Version (1 byte) rsvd: (int): Reserved (1 byte) len: (int): Packet Length (2 bytes) """ __hdr__ = ( ('v', 'B', 3), ('rsvd', 'B', 0), ('len', 'H', 0) ) dpkt-1.9.8/dpkt/udp.py000066400000000000000000000017311427735150300146100ustar00rootroot00000000000000# $Id: udp.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """User Datagram Protocol.""" from __future__ import absolute_import from . import dpkt UDP_HDR_LEN = 8 UDP_PORT_MAX = 65535 class UDP(dpkt.Packet): """User Datagram Protocol. User Datagram Protocol (UDP) is one of the core members of the Internet protocol suite. With UDP, computer applications can send messages, in this case referred to as datagrams, to other hosts on an Internet Protocol (IP) network. Prior communications are not required in order to set up communication channels or data paths. Attributes: __hdr__: Header fields of UDP. sport: (int): Source port. (2 bytes) dport: (int): Destination port. (2 bytes) ulen: (int): Length. (2 bytes) sum: (int): Checksum. (2 bytes) """ __hdr__ = ( ('sport', 'H', 0xdead), ('dport', 'H', 0), ('ulen', 'H', 8), ('sum', 'H', 0) ) dpkt-1.9.8/dpkt/utils.py000066400000000000000000000044621427735150300151640ustar00rootroot00000000000000"""Various Utility Functions""" import socket import warnings from .compat import compat_ord from .dns import DNS def mac_to_str(address): r"""Convert a MAC address to a readable/printable string Args: address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06') Returns: str: Printable/readable MAC address """ return ':'.join('%02x' % compat_ord(b) for b in address) def inet_to_str(inet): """Convert inet object to a string Args: inet (inet struct): inet network address Returns: str: Printable/readable IP address """ # First try ipv4 and then ipv6 try: return socket.inet_ntop(socket.AF_INET, inet) except ValueError: return socket.inet_ntop(socket.AF_INET6, inet) def make_dict(obj): """Create a dictionary out of a non-builtin object""" # Recursion base case if is_builtin(obj): return obj output_dict = {} for key in dir(obj): if not key.startswith('__') and not callable(getattr(obj, key)): attr = getattr(obj, key) if isinstance(attr, list): output_dict[key] = [] for item in attr: output_dict[key].append(make_dict(item)) else: output_dict[key] = make_dict(attr) return output_dict def is_builtin(obj): return obj.__class__.__module__ in ['__builtin__', 'builtins'] def deprecation_warning(*args): """print a deprecation warning""" warnings.warn(*args, stacklevel=2) def test_utils(): """Test the utility methods""" from binascii import unhexlify from pprint import pprint print(mac_to_str(b'\x01\x02\x03\x04\x05\x06')) assert mac_to_str(b'\x01\x02\x03\x04\x05\x06') == '01:02:03:04:05:06' print(inet_to_str(b'\x91\xfe\xa0\xed')) assert inet_to_str(b'\x91\xfe\xa0\xed') == '145.254.160.237' ipv6_inet = b' \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4' assert inet_to_str(ipv6_inet) == '2001:db8:85a3::8a2e:370:7334' # Test the make_dict method with a DNS response packet a_resp = unhexlify("059c8180000100010000000106676f6f676c6503636f6d0000010001c00c00010" "0010000012b0004d83ace2e0000290200000000000000") my_dns = DNS(a_resp) pprint(make_dict(my_dns)) dpkt-1.9.8/dpkt/vrrp.py000066400000000000000000000037061427735150300150150ustar00rootroot00000000000000# $Id: vrrp.py 88 2013-03-05 19:43:17Z andrewflnr@gmail.com $ # -*- coding: utf-8 -*- """Virtual Router Redundancy Protocol.""" from __future__ import print_function from __future__ import absolute_import from . import dpkt class VRRP(dpkt.Packet): """Virtual Router Redundancy Protocol. TODO: Longer class information.... Attributes: __hdr__: Header fields of VRRP. TODO. """ __hdr__ = ( ('_v_type', 'B', 0x21), ('vrid', 'B', 0), ('priority', 'B', 0), ('count', 'B', 0), ('atype', 'B', 0), ('advtime', 'B', 0), ('sum', 'H', 0), ) __bit_fields__ = { '_v_type': ( ('v', 4), ('type', 4), ) } addrs = () auth = '' def unpack(self, buf): dpkt.Packet.unpack(self, buf) l_ = [] off = 0 for off in range(0, 4 * self.count, 4): l_.append(self.data[off:off + 4]) self.addrs = l_ self.auth = self.data[off + 4:] self.data = '' def __len__(self): return self.__hdr_len__ + (4 * self.count) + len(self.auth) def __bytes__(self): data = b''.join(self.addrs) + self.auth if not self.sum: self.sum = dpkt.in_cksum(self.pack_hdr() + data) return self.pack_hdr() + data def test_vrrp(): # no addresses s = b'\x00\x00\x00\x00\x00\x00\xff\xff' v = VRRP(s) assert v.sum == 0xffff assert bytes(v) == s # have address s = b'\x21\x01\x64\x01\x00\x01\xba\x52\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00' v = VRRP(s) assert v.count == 1 assert v.addrs == [b'\xc0\xa8\x00\x01'] # 192.168.0.1 assert bytes(v) == s # test checksum generation v.sum = 0 assert bytes(v) == s # test length assert len(v) == len(s) # test getters assert v.v == 2 assert v.type == 1 # test setters v.v = 3 v.type = 2 assert bytes(v)[0] == b'\x32'[0] dpkt-1.9.8/dpkt/yahoo.py000066400000000000000000000032301427735150300151330ustar00rootroot00000000000000# $Id: yahoo.py 23 2006-11-08 15:45:33Z dugsong $ # -*- coding: utf-8 -*- """Yahoo Messenger.""" from __future__ import absolute_import from . import dpkt class YHOO(dpkt.Packet): """Yahoo Messenger. Yahoo! Messenger (sometimes abbreviated Y!M) was an advertisement-supported instant messaging client and associated protocol provided by Yahoo!. Yahoo! Messenger was provided free of charge and could be downloaded and used with a generic "Yahoo ID" which also allowed access to other Yahoo! services, such as Yahoo! Mail. The service also offered VoIP, file transfers, webcam hosting, a text messaging service, and chat rooms in various categories. Attributes: __hdr__: Header fields of Yahoo Messenger. version: (bytes): Version. (8 bytes) length: (int): Length. (4 bytes) service: (int): Service. (4 bytes) connid: (int): Connection ID. (4 bytes) magic: (int): Magic. (4 bytes) unknown: (int): Unknown. (4 bytes) type: (int): Type. (4 bytes) nick1: (bytes): Nick1. (36 bytes) nick2: (bytes): Nick2. (36 bytes) """ __hdr__ = [ ('version', '8s', ' ' * 8), ('length', 'I', 0), ('service', 'I', 0), ('connid', 'I', 0), ('magic', 'I', 0), ('unknown', 'I', 0), ('type', 'I', 0), ('nick1', '36s', ' ' * 36), ('nick2', '36s', ' ' * 36) ] __byte_order__ = '<' class YMSG(dpkt.Packet): __hdr__ = [ ('version', '8s', ' ' * 8), ('length', 'H', 0), ('type', 'H', 0), ('unknown1', 'I', 0), ('unknown2', 'I', 0) ] dpkt-1.9.8/examples/000077500000000000000000000000001427735150300143205ustar00rootroot00000000000000dpkt-1.9.8/examples/__init__.py000066400000000000000000000000001427735150300164170ustar00rootroot00000000000000dpkt-1.9.8/examples/data/000077500000000000000000000000001427735150300152315ustar00rootroot00000000000000dpkt-1.9.8/examples/data/README.md000066400000000000000000000006131427735150300165100ustar00rootroot00000000000000### PCAP Data A good resource for PCAP data is : [https://wiki.wireshark.org/SampleCaptures]() - #### http.pcap: A simple HTTP request and response - #### nb6-http.pcap: provided by french ISP SFR, there are three different HTTP requests: first was sent on the private IPv4 network (IPoE), second was sent on the public IPv4 network, third was sent on the public IPv6 network (L2TP tunnel). dpkt-1.9.8/examples/data/dns_icmp.pcap000066400000000000000000000070641427735150300177010ustar00rootroot00000000000000ò}֧Q"PP;`3KXEBЦ@ҩ+ +5.hR8888in-addrarpa }֧Q8||`3KX;En@@c$++ 5ZR8888in-addrarpa   google-public-dns-agooglecom}֧Q3Rbb;`3KXET&@W+ ;Q}Q  !"#$%&'()*+,-./01234567}֧Q bb`3KX;ET(+ ó;Q}Q  !"#$%&'()*+,-./01234567~֧Qwibb;`3KXET@$+ h;Q~V-  !"#$%&'()*+,-./01234567~֧QRvbb`3KX;ET(+ h;Q~V-  !"#$%&'()*+,-./01234567֧Q!Ybb;`3KXET@d + ;QX  !"#$%&'()*+,-./01234567֧Qbb`3KX;ET(+ ;QX  !"#$%&'()*+,-./01234567֧QOPP;`3KXEB%@}Z+ +5.i]4488in-addrarpa ֧Q|||`3KX;En@@c$++ 5ZAi]4488in-addrarpa  ] google-public-dns-bgooglecom֧Qbb;`3KXET@$+ +;Qրݘ  !"#$%&'()*+,-./01234567֧QRbb;`3KXET@+ (;Qց  !"#$%&'()*+,-./01234567֧Q bb`3KX;ET(+ 0;Qց  !"#$%&'()*+,-./01234567֧Q.bb;`3KXET*@W+ $;Qւ  !"#$%&'()*+,-./01234567֧QPP;`3KXEB@g+ +5.:2224in-addrarpa ֧Q<tt`3KX;Ef@@c,++ 5R:2224in-addrarpa  b resolversLevel3net֧QQbb;`3KXET@-+ ;QքP  !"#$%&'()*+,-./01234567֧Qcbb`3KX;ET2p+ ;QքP  !"#$%&'()*+,-./01234567֧QSbb;`3KXET@+ ;QօS  !"#$%&'()*+,-./01234567֧Q bb`3KX;ET2o+ ;QօS  !"#$%&'()*+,-./01234567֧QVbb;`3KXETV@2.+ ;QֆU  !"#$%&'()*+,-./01234567֧Qebb`3KX;ET2n+ ;QֆU  !"#$%&'()*+,-./01234567֧Q݈MM;`3KXE?@+ +â5+T!!www wiresharkorg֧Q]]`3KX;EO@@cC++ 5â;!!www wiresharkorg X*A֧Qv"MM;`3KXE?dk+ +c5+\,Xwww wiresharkorg֧QH]]`3KX;EO@@cC++ 5c;,Xwww wiresharkorg X*A֧Q+bb;`3KXET@G+ *A;Qֈ  !"#$%&'()*+,-./01234567֧Q bb`3KX;ETJc0{ʮ*A+ ;Qֈ  !"#$%&'()*+,-./01234567֧Qbb;`3KXET@0+ *A<;Q։P  !"#$%&'()*+,-./01234567֧Qbb`3KX;ETJd0{ɮ*A+ <;Q։P  !"#$%&'()*+,-./01234567֧Qbb;`3KXETɍ@+ *AB;Q֊H  !"#$%&'()*+,-./01234567֧Q;bb`3KX;ETJe0{Ȯ*A+ B;Q֊H  !"#$%&'()*+,-./01234567dpkt-1.9.8/examples/data/http.pcap000066400000000000000000000623131427735150300170620ustar00rootroot00000000000000ò#K@>> E0A@A ,P8p"8 $K@Fe>> E0@/,AߑP ,La8p[d$K@Fe66 E(D@A ,P8LaP%yd$K@Fe EE@A ,P8LaP%XGET /download.html HTTP/1.1 Host: www.ethereal.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://www.ethereal.com/development.html $K@ 66 E(@/1AߑP ,La8P !$K@k) E@/,1AߑP ,La8P + HTTP/1.1 200 OK Date: Thu, 13 May 2004 10:17:12 GMT Server: Apache Last-Modified: Tue, 20 Apr 2004 13:17:00 GMT ETag: "9a01a-4696-7e354b00" Accept-Ranges: bytes Content-Length: 18070 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/html; charset=ISO-8859-1 Ethereal: Download

Ethereal

Download
%K@66 E(F@A ,P8LfP%r!%K@ E@/,0AߑP ,Lf8P Q Search:

Official Releases

The Official source code release and installers for Windows, Red Hat Linux/Fedora, and Solaris can be found on the main Ethereal web site and its mirrors.

Source Code

HTTP: Main site Australia Australia Germany Japan Korea Malaysia Sweden SourceForge

FTP: Main site Australia Australia Austria Germany Japan Mexico Sweden

The latest development sources are available via anonymous CVS.

Windows 98/ME/2000/XP/2003 Installers

HTTP: Main site Australia Australia Germany Japan Korea Malaysia Sweden SourceForge

FTP: Main site Australia Australia Austria Germany Japan Mexico Sweden

Red Hat Linux / Fedora Packages

HTTP: Main site Australia Australia Germany Japan Korea Malaysia Sweden

FTP: Main site Australia Australia Austria Germany Japan Mexico Sweden

Solaris Packages

HTTP: Main site Australia Australia Germany Japan Korea Malaysia Sweden SourceForge

FTP: Main site Australia Australia Austria Germany Japan Mexico Sweden

Other Platforms

Binary distributions and ready-to-compile packages are available for most platforms. Please note these packages may depend on external libraries including GLib/GTK+, libpcap, Net-SNMP, PCRE, and GNU ADNS. You may have to download and install them before installing Ethereal.

Platform Location(s)
Apple Computer:
Mac OS X
Fink Project DarwinPorts
Be (Palm?):
BeOS
BeBits
Debian:
Debian GNU/Linux
&K@iM  E@/,*AߑP ,LH8P  stable, testing, unstable
The FreeBSD Project:
FreeBSD
ports
Gentoo Technologies:
Gentoo Linux
portage
HP:
Tru64 Unix
The Written Word (4.0d, 5.1)1
HP:
HP-UX
UK, Germany, Netherlands, US, Canada, South Afr&K@iM 66 E(O@A ,P8LP%Le&K@q E@/,)AߑP ,L8P ica
(more mirrors are listed on each site's home page),
The Written Word (10.20, 11.00, 11.11)1
IBM:
AIX
Bull archive
Vienna University mirror
UCLA mirror
IBM:
S/390 Linux (Red Hat 7.2)
Ashley Chaloner
MandrakeSoft:
Mandrake Linux
Cooker (in the contrib section)
Microsoft:
Windows (Intel, 32-bit)
local archive
OPENEXTRA
NetBSD Foundation:
NetBSD
Ls"bO,BĀ=a3b6YeXH_@XYM$ #:D~OSTrt,4H9W!E&X=P9a<-Ol-mhp7(O?a:-knhie qZU#psgpuT߁`bpdBrHϠ@ LT#!CjbllaƝ,drp4嘳ڳ:ajpH=dz̼7zM}S%|GT5>#U Ȟgq*$4ky[r}UMៅ0[8<8?8utUt8GIyH يOh%|\4yn \r/:;rHjTjBĜ5典AnLIR񣹅. w y`Inp.'0hnp'T*@/w@5<ʺ(ϐ{5@_ /.}r=KJ qcbX}Q >R<ݯbsHuRφT>xJ+tb)LU(>bOOƞmOPoF'L mЋ(:m0hA]eBj'K@ Eȅ7;cP +.kY6 Pz)Gc/ ;孖u%\ ~+%@yPϲoǫ% )$P+-JyQ\vrmj h [擄}-e@)\ֈHWaqzOϺB gAx;MѼ6/¿{9'K@66 E(S@>;c +P6 .kYP"8Y'K@h] E@/,(AߑP ,L8P kgsrc/net/ethereal/README.html">packages
OpenBSD:
OpenBSD
ports
PLD Team:
PLD Linux
FTP site
Red Hat:
Red Hat Linux
RPMFind (requires glibc)
Henri Gomez
The Written Word (7.1)1
local archive
ROCK Linux:
ROCK Linux
package
SCO (formerly Caldera):
UnixWare/OpenUnix
Skunkware: UnixWare 7 Open UNIX 8
SGI:
Irix
The Written Word (6.5)1
SGI Freeware
Slackware Linux:
Slackware Linux
Linux Packages
Sun Microsystems:
Solaris/Intel
Solaris 01/01 update (unsupported)
Sun Microsystems:
Solaris/SPARC
local archive (8, 9)
The Written Word (2.5.1 - 9)1
Sunfreeware.com (7, 8)
Solaris 8 and 9 Companion Software CDs (unsupported)
SuSE:
SuSE Linux
SuSE FTP site. Mirrors are also available.

If you know of any binary distribution not listed here, please send mail to ethereal-web[AT]ethereal.com .

[1] Each Ethereal package produced by The Written Word depends on the zlib, Glib, GTK+, Perl, and Net-SNMP packages. Please refer to The Written Word's documentation for installation instructions. Please do not call The Written Word for support. Email free-support[AT]thewrittenword.com with questions.

Other Downloads

Sample Captures

A menagerie of capture files is available on our sample captures pag'K@`/ 66 E(W@ݑA ,P8L

Documentation

A PDF version of the Ethereal User's Guide is available in the documentation page.

AIX is a registered trademark of International Business Machines, Inc. Tru64 is a registered trademark of Compaq Computer Corporation. Debian is a registered trademark of Software In The Public Interest, Inc. FreeBSD is a registered trademark of Walnut Creek CDROM, Inc. HP-UX is a registered trademark of Hewlett-Packard Company. Irix is a registered trademark of Silicon Graphics, Inc. LinuxPPC is a trademark of Jeff Carr. Mac OS is a registered trademark of Apple Computer, Inc. NetBSD is a registered trademark of the NetBSD Foundation. Red Hat is a registered trademark of Red Hat, Inc. Linux is a registered trademark of Linus Torvalds. SCO and Unixware are registered trademarks of Santa Cruz Operation, Inc. Slackware is a registered trademark of Patrick Volkerding. Solaris is a registered trademark of Sun Microsystems, Inc. SuSE is a registered trademark of SuSE AG. Microsoft, Windows, Windows 95, Windows 98, Windows ME, Windows NT, Windows 2000, and Windows XP are registered trademarks of Microsoft, Inc. All other trademarks on this site are property of their respective owners.

(K@66 E(\@ؑA ,P8LHP$1q5K@O66 E(@/1AߑP ,LH8P  z/ns1 9servicescomadmindw3 :]%Rv ]]3asd;I!EGda@@N_cmB |53http service-ncdnsfrnet]%Rz s3ad;!E@9@umB _c5|nhttp service-ncdnsfrnet 2/ns1 9servicescomadmin;w3 :]%R __3asd;K!EId@@+_cmB 55Cncdnnb6dslneufboxneuffr]%Rlvs3ad;!E@9@gmB _c5|ncdnnb6dslneufboxneuffr http service-ncdnsfrnet9 t]9 t]]%R#zJJErE<P@@' ]P+kЬ d]%RJJrEE<@;"] PKŜ+k8Ϟ d ]%RBBErE4Q@@. ]P+kKŝ h+k d]%RErER@@ ]P+kKŝ hB dGET /nb6dsl_Vers%203.3.4_ter/nb6-3.3.4_ter.sha256sum HTTP/1.1 User-Agent: curl/7.33.0 Host: ncdn.nb6dsl.neufbox.neuf.fr Accept: */* ]%Rc*BBrEE4^@;] PKŝ+l~6$ d]%R9.rEE^@;] PKŝ+l~z dHTTP/1.1 200 OK Server: Apache Last-Modified: Thu, 01 Aug 2013 18:16:36 GMT Cache-Control: max-age=86400 Expires: Fri, 03 Jan 2014 02:45:16 GMT X-Varnish: 1740521562 Content-Length: 497 Accept-Ranges: bytes Date: Thu, 02 Jan 2014 08:37:49 GMT X-Varnish: 3638095267 3619940513 Age: 21153 Connection: keep-alive Via: 1.1 varnish, 1.1 cbv4-ncdn-cache06, 1.1 cbv4-ncdn-cache00 # # UUID: 27f4b1e0-6628-49cb-b59b-dc2234dc0e5d # # NB6 3.3.4 sha256sum # 3ece945cf370ff987ea184f3ac859d590b0a830d74949e8aa0c71900663d4d6c NB6-BOOTLOADER-R3.2.4 62797705144e82b7c44963aa8ea13442d12057e7d01f33ed67bfab3f6d4e70d5 NB6-MAIN-R3.3.4 0ea220adfb9dd1c845d9ec0e3d537aab281b5d45ba51b3688c69074225dd8cb4 NB6-CONFIG-R3.3.4.2 f00d0b3b8e6a88efaac18f80967e83c49b3454bbe26764a70a0dbfee3973442d NB6-RESCUE-R3.1.8 d6f592f35abe6e9914fe1b14560b764ee23c846d6c9b5bdecd3300a2b3afeb25 NB6-ADSL-A2pD035p ]%R.BBErE4S@@, ]P+l~K! d҉^%R:=BBErE4T@@+ ]P+l~K! d扏^%R{BBrEE4^@;] PK+l2u d^%R|BBErE4U@@* ]P+lK!m d`%R<<0~g~)EE   1RX<`%Rp<<0~`EE  z>s3ad;*!E(mH_c J2b%Rc JJs3ad;6!E4R.mH_c J2^! H 49"b%R HH3asd;4!E2[[@_&_cmH j! H e%RVVV3asd;B!E@@@9_cmBo5,:Vipv4 icanhazipcome%RLVV3asd;B!E@@@9_cmB o5,DVipv4 icanhazipcome%R>VV3asd;B!E@@@9_cmB؊5,6 ipv4 icanhazipcome%R,<<<EE  @ 1RBrvvs3ad;b!E`@9@mB _c5oLVipv4 icanhazipcom ,Ee ,Ede%R@s3asd;~!E|@r_cmB <E`@9@mB _c5oLVipv4 icanhazipcom ,Ee ,Ede%RzRR3asd;>!E!E<@,(&Ed_cP]mtfq  0J)e%Rd* JJ3asd;6!E4O[@?_cEd]Ptfn< )0Je%R* 3asd;!EO\@?_cEd]Ptfn )0JGET / HTTP/1.1 User-Agent: curl/7.34.0 Host: ipv4.icanhazip.com Accept: */* e%R u JJs3ad;6!E4p@,Ed_cP]ntgG; 0)e%Rz s3ad;!Ep@,=Ed_cP]ntgGm 0)HTTP/1.1 200 OK Date: Thu, 02 Jan 2014 08:37:57 GMT Server: Apache Content-Length: 14 Content-Type: text/plain; charset=UTF-8 X-RTFM: Learn about this site at http://bit.ly/14DAh2o and don't abuse the service X-YOU-SHOULD-APPLY-FOR-A-JOB: If you're reading this, apply here: http://rackertalent.com/ X-ICANHAZNODE: icanhazip1.nugget 95.136.242.99 e%R# JJ3asd;6!E4O]@?_cEd]PtgGӀ: * 0e%Rz JJ3asd;6!E4O^@?_cEd]PtgGӀ9 * 0e%R JJs3ad;6!E4p@,Ed_cP]tgH9n 0* e%Rr JJ3asd;6!E4O_@?_cEd]PtgHԀ97 *80j%R<<>?AEE  ,bR#!RŪomB_c54jT1eipv6 icanhazipcom2ns rackspacemajorhgmailRU^,m%Rts3ad;z!Ex@;>umB_c5f$d>kKipv6 icanhazipcom ,&  ,& n%RlO3asd;x!Ev[]@^_cmHb jW`(?*^!-%r"& PGlq 3n%Rxs3ad;z!Ex,%mH_cdJ2^W`(5& *^!-%r"PISGo 03n%RX3asd;p!En[^@^_cmHZ jW` ?*^!-%r"& PGISڶ 40n%R3asd;!E[_@^_cmH jW`r?*^!-%r"& PGISu 40GET / HTTP/1.1 User-Agent: curl/7.34.0 Host: ipv6.icanhazip.com Accept: */* n%Rs3ad;r!EpUmH_c\J2^W` 5& *^!-%r"PISG  04n%Rs3ad;!EmH_cJ2^W`5& *^!-%r"PISGZ 04HTTP/1.1 200 OK Date: Thu, 02 Jan 2014 08:38:06 GMT Server: Apache Content-Length: 38 Content-Type: text/plain; charset=UTF-8 X-RTFM: Learn about this site at http://bit.ly/14DAh2o and don't abuse the service X-YOU-SHOULD-APPLY-FOR-A-JOB: If you're reading this, apply here: http://rackertalent.com/ X-ICANHAZNODE: icanhazip1.nugget 2a00:5e80:101:212d:504:07b1:2572:db22 n%R3asd;p!En[`@^_cmHZ jW` ?*^!-%r"& PGISS 470n%RΨ3asd;p!En[a@^_cmHZ jW` ?*^!-%r"& PGISR 470n%R;W s3ad;r!EpmH_c\J2^W` 5& *^!-%r"PISGದ 0o47n%Re 3asd;p!En[b@^_cmHZ jW` ?*^!-%r"& PGISm 4m0odpkt-1.9.8/examples/data/truncated_dns.pcap000066400000000000000000000003601427735150300207320ustar00rootroot00000000000000ò._' GG^E@;*d5I5ZӁ apispd samsungdmcom ,api samsungspd3<6K3<cQ 3<42y3<43<423<dpkt-1.9.8/examples/data/truncated_dns_2.pcap000066400000000000000000000003671427735150300211620ustar00rootroot00000000000000òK_ǒt&G(w߬E >)de5іՁmycelium-wallets3 amazonawscom | s3-1-w>4e>Cns-1035 awsdns-01org>Cns-294 awsdns-36)>K_ڒdpkt-1.9.8/examples/old/000077500000000000000000000000001427735150300150765ustar00rootroot00000000000000dpkt-1.9.8/examples/old/dhcprequest.py000066400000000000000000000030251427735150300177770ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import sys import socket # Since pcapy is not a requirement of dpkt, test the import and give message try: import pcapy except ImportError: print('Could not import pcapy. Please do a $pip install pcapy') sys.exit(1) # dpkt imports from dpkt import dhcp from dpkt import udp from dpkt import ip from dpkt import ethernet # Grab the default interface and use that for the injection devices = pcapy.findalldevs() iface_name = devices[0] print('Auto Setting Interface to: {:s}'.format(iface_name)) interface = pcapy.open_live(iface_name, 65536, 1, 0) # Get local ip src_ip = socket.inet_pton(socket.AF_INET, interface.getnet()) # Generate broadcast ip and eth_addr broadcast_ip = socket.inet_pton(socket.AF_INET, '255.255.255.255') broadcast_eth_addr = b'\xFF\xFF\xFF\xFF\xFF\xFF' # build a dhcp discover packet to request an ip d = dhcp.DHCP( xid=1337, op=dhcp.DHCPDISCOVER, opts=( (dhcp.DHCP_OP_REQUEST, b''), (dhcp.DHCP_OPT_REQ_IP, b''), (dhcp.DHCP_OPT_ROUTER, b''), (dhcp.DHCP_OPT_NETMASK, b''), (dhcp.DHCP_OPT_DNS_SVRS, b'') ) ) # build udp packet u = udp.UDP( dport=67, sport=68, data=d ) u.ulen = len(u) # build ip packet i = ip.IP( dst=broadcast_ip, src=src_ip, data=u, p=ip.IP_PROTO_UDP ) i.len = len(i) # build ethernet frame e = ethernet.Ethernet( dst=broadcast_eth_addr, data=i ) # Inject the packet (send it out) interface.sendpacket(bytes(e)) print('DHCP request sent!') dpkt-1.9.8/examples/old/dnsping.py000066400000000000000000000034721427735150300171200ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import random import socket import dpkt import ping class DNSPing(ping.Ping): def __init__(self): ping.Ping.__init__(self) self.op.add_option('-z', dest='zone', type='string', default=socket.gethostname().split('.', 1)[1], help='Domain to formulate queries in') self.op.add_option('-n', dest='hostname', type='string', help='Query only for a given hostname') self.op.add_option('-p', dest='port', type='int', default=53, help='Remote DNS server port') self.op.add_option('-R', dest='norecurse', action='store_true', help='Disable recursive queries') def open_sock(self, opts): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect((opts.ip, opts.port)) sock.settimeout(opts.wait) return sock def gen_ping(self, opts): for i in range(opts.count): dns = dpkt.dns.DNS(id=i) if opts.norecurse: dns.op &= ~dpkt.dns.DNS_RD if not opts.hostname: name = '%s.%s' % (str(random.random())[-6:], opts.zone) else: name = opts.hostname dns.qd = [dpkt.dns.DNS.Q(name=name)] yield str(dns) def print_header(self, opts): print('DNSPING %s:' % opts.ip, end='') if opts.hostname: print('Name: %s' % opts.hostname) else: print('Name: *.%s' % opts.zone) def print_reply(self, opts, buf, rtt): dns = dpkt.dns.DNS(buf) print('%d bytes from %s: id=%d time=%.3f ms' % (len(buf), opts.ip, dns.id, rtt * 1000)) if __name__ == '__main__': DNSPing().main() dpkt-1.9.8/examples/old/nbtping.py000066400000000000000000000027101427735150300171110ustar00rootroot00000000000000#!/usr/bin/env python import socket from dpkt import netbios import ping class NBTPing(ping.Ping): def __init__(self): ping.Ping.__init__(self) self.op.add_option('-p', dest='port', type='int', default=137, help='Remote NetBIOS name server port') def open_sock(self, opts): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect((opts.ip, opts.port)) sock.settimeout(opts.wait) return sock def gen_ping(self, opts): for i in range(opts.count): ns = netbios.NS(id=i, qd=[netbios.NS.Q(type=netbios.NS_NBSTAT, name='*')]) yield str(ns) def print_header(self, opts): print('NBTPING %s:' % opts.ip) def print_reply(self, opts, buf, rtt): ns = netbios.NS(buf) d = {} for rr in ns.an: for name, svc, flags in rr.nodenames: unique = (flags & netbios.NS_NAME_G == 0) if svc == 0 and unique and 'host' not in d: d['host'] = name elif svc == 0x03 and unique: if 'user' not in d or d['user'].startswith(d['host']): d['user'] = name print('%d bytes from %s: id=%d time=%.3f ms host=%s user=%s' % (len(buf), opts.ip, ns.id, rtt * 1000, d.get('host', ''), d.get('user', ''))) if __name__ == '__main__': NBTPing().main() dpkt-1.9.8/examples/old/ping.py000066400000000000000000000073421427735150300164130ustar00rootroot00000000000000#!/usr/bin/env python import math import optparse import random import socket import sys import time import dpkt class Ping(object): def __init__(self): usage = '%prog [OPTIONS] ' self.op = optparse.OptionParser(usage=usage) self.op.add_option('-c', dest='count', type='int', default=sys.maxint, help='Total number of queries to send') self.op.add_option('-i', dest='wait', type='float', default=1, help='Specify packet interval timeout in seconds') def gen_ping(self, opts): pass def open_sock(self, opts): pass def print_header(self, opts): pass def print_reply(self, opts, buf, rtt): pass def main(self, argv=None): if not argv: argv = sys.argv[1:] opts, args = self.op.parse_args(argv) if not args: self.op.error('missing host') elif len(args) > 1: self.op.error('only one host may be specified') host = args[0] opts.ip = socket.gethostbyname(host) sock = self.open_sock(opts) sent = rcvd = rtt_max = rtt_sum = rtt_sumsq = 0 rtt_min = 0xffff try: self.print_header(opts) for ping in self.gen_ping(opts): try: start = time.time() sock.send(ping) buf = sock.recv(0xffff) rtt = time.time() - start if rtt < rtt_min: rtt_min = rtt if rtt > rtt_max: rtt_max = rtt rtt_sum += rtt rtt_sumsq += rtt * rtt self.print_reply(opts, buf, rtt) rcvd += 1 except socket.timeout: pass sent += 1 time.sleep(opts.wait) except KeyboardInterrupt: pass print('\n--- %s ping statistics ---' % opts.ip) print('%d packets transmitted, %d packets received, %.1f%% packet loss' % (sent, rcvd, (float(sent - rcvd) / sent) * 100)) rtt_avg = rtt_sum / sent if rtt_min == 0xffff: rtt_min = 0 print('round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms' % (rtt_min * 1000, rtt_avg * 1000, rtt_max * 1000, math.sqrt((rtt_sumsq / sent) - (rtt_avg * rtt_avg)) * 1000)) class ICMPPing(Ping): def __init__(self): Ping.__init__(self) self.op.add_option('-p', dest='payload', type='string', default='hello world!', help='Echo payload string') def open_sock(self, opts): sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1) sock.connect((opts.ip, 1)) sock.settimeout(opts.wait) return sock def gen_ping(self, opts): for i in range(opts.count): icmp = dpkt.icmp.ICMP( type=8, data=dpkt.icmp.ICMP.Echo(id=random.randint(0, 0xffff), seq=i, data=opts.payload)) yield str(icmp) def print_header(self, opts): print('PING %s: %d data bytes' % (opts.ip, len(opts.payload))) def print_reply(self, opts, buf, rtt): ip = dpkt.ip.IP(buf) if sys.platform == 'darwin': # XXX - work around raw socket bug on MacOS X ip.data = ip.icmp = dpkt.icmp.ICMP(buf[20:]) ip.len = len(ip.data) print('%d bytes from %s: icmp_seq=%d ip_id=%d ttl=%d time=%.3f ms' % (len(ip.icmp), opts.ip, ip.icmp.echo.seq, ip.id, ip.ttl, rtt * 1000)) if __name__ == '__main__': p = ICMPPing() p.main() dpkt-1.9.8/examples/print_dns_truncated.py000066400000000000000000000065361427735150300207550ustar00rootroot00000000000000""" Use DPKT to read in a pcap file and print out the contents of truncated DNS packets. This example show how to read/handle truncated packets """ import sys import dpkt import datetime from dpkt.utils import mac_to_str, inet_to_str, make_dict from pprint import pprint def print_packet(buf): """Print out information about each packet in a pcap Args: buf: buffer of bytes for this packet """ print(type(buf)) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) print('Ethernet Frame: ', mac_to_str(eth.src), mac_to_str(eth.dst), eth.type) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) return # Now unpack the data within the Ethernet frame (the IP packet) # Pulling out src, dst, length, fragment info, TTL, and Protocol ip = eth.data # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset)) # Check for UDP in the transport layer if isinstance(ip.data, dpkt.udp.UDP): # Set the UDP data udp = ip.data print('UDP: sport={:d} dport={:d} sum={:d} ulen={:d}'.format(udp.sport, udp.dport, udp.sum, udp.ulen)) # Now see if we can parse the contents of the truncated DNS request try: dns = dpkt.dns.DNS() dns.unpack(udp.data) except (dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError, Exception) as e: print('\nError Parsing DNS, Might be a truncated packet...') print('Exception: {!r}'.format(e)) # Print out the DNS info print('Queries: {:d}'.format(len(dns.qd))) for query in dns.qd: print('\t {:s} Type:{:d}'.format(query.name, query.type)) print('Answers: {:d}'.format(len(dns.an))) for answer in dns.an: if answer.type == 5: print('\t {:s}: type: CNAME Answer: {:s}'.format(answer.name, answer.cname)) elif answer.type == 1: print('\t {:s}: type: A Answer: {:s}'.format(answer.name, inet_to_str(answer.ip))) else: pprint(make_dict(answer)) def process_packets(pcap): """Process each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # For each packet in the pcap process the contents try: for timestamp, buf in pcap: # Print out the timestamp in UTC print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) print_packet(buf) except dpkt.dpkt.NeedData: print('\nPCAP capture is truncated, stopping processing...') sys.exit(1) def test(): """Open up a test pcap file and print out the packets""" with open('data/truncated_dns_2.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) process_packets(pcap) if __name__ == '__main__': test() dpkt-1.9.8/examples/print_http_requests.py000066400000000000000000000053541427735150300210270ustar00rootroot00000000000000""" This example expands on the print_packets example. It checks for HTTP request headers and displays their contents. NOTE: We are not reconstructing 'flows' so the request (and response if you tried to parse it) will only parse correctly if they fit within a single packet. Requests can often fit in a single packet but Responses almost never will. For proper reconstruction of flows you may want to look at other projects that use DPKT (http://chains.readthedocs.io and others) """ import dpkt import datetime from dpkt.utils import mac_to_str, inet_to_str def print_http_requests(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # For each packet in the pcap process the contents for timestamp, buf in pcap: # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Check for TCP in the transport layer if isinstance(ip.data, dpkt.tcp.TCP): # Set the TCP data tcp = ip.data # Now see if we can parse the contents as a HTTP request try: request = dpkt.http.Request(tcp.data) except (dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError): continue # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) print('Ethernet Frame: ', mac_to_str(eth.src), mac_to_str(eth.dst), eth.type) print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset)) print('HTTP request: %s\n' % repr(request)) # Check for Header spanning acrossed TCP segments if not tcp.data.endswith(b'\r\n'): print('\nHEADER TRUNCATED! Reassemble TCP segments!\n') def test(): """Open up a test pcap file and print out the packets""" with open('data/http.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_http_requests(pcap) if __name__ == '__main__': test() dpkt-1.9.8/examples/print_icmp.py000066400000000000000000000040341427735150300170370ustar00rootroot00000000000000#!/usr/bin/env python """ This example expands on the print_packets example. It checks for ICMP packets and displays the ICMP contents. """ import dpkt import datetime from dpkt.utils import mac_to_str, inet_to_str def print_icmp(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # For each packet in the pcap process the contents for timestamp, buf in pcap: # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Now check if this is an ICMP packet if isinstance(ip.data, dpkt.icmp.ICMP): icmp = ip.data # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) print('Ethernet Frame: ', mac_to_str(eth.src), mac_to_str(eth.dst), eth.type) print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset)) print('ICMP: type:%d code:%d checksum:%d data: %s\n' % (icmp.type, icmp.code, icmp.sum, repr(icmp.data))) def test(): """Open up a test pcap file and print out the packets""" with open('data/dns_icmp.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_icmp(pcap) if __name__ == '__main__': test() dpkt-1.9.8/examples/print_packets.py000066400000000000000000000033201427735150300175360ustar00rootroot00000000000000""" Use DPKT to read in a pcap file and print out the contents of the packets. This example is focused on the fields in the Ethernet Frame and IP packet. """ import dpkt import datetime from dpkt.utils import mac_to_str, inet_to_str def print_packets(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # For each packet in the pcap process the contents for timestamp, buf in pcap: # Print out the timestamp in UTC print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) print('Ethernet Frame: ', mac_to_str(eth.src), mac_to_str(eth.dst), eth.type) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now access the data within the Ethernet frame (the IP packet) # Pulling out src, dst, length, fragment info, TTL, and Protocol ip = eth.data # Print out the info, including the fragment flags and offset print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, ip.df, ip.mf, ip.offset)) # Pretty print the last packet print('** Pretty print demo **\n') eth.pprint() def test(): """Open up a test pcap file and print out the packets""" with open('data/http.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_packets(pcap) if __name__ == '__main__': test() dpkt-1.9.8/setup.cfg000066400000000000000000000004601427735150300143230ustar00rootroot00000000000000[bdist_wheel] [aliases] release = sdist bdist_wheel upload -r pypi test_release = sdist bdist_wheel upload -r pypitest [flake8] max-line-length = 140 [tool:pytest] addopts= -v --cov-report term-missing python_files=*.py python_functions=test norecursedirs=.tox .git *.egg-info __pycache__ dist build dpkt-1.9.8/setup.py000077500000000000000000000032451427735150300142230ustar00rootroot00000000000000import sys try: from setuptools import setup except ImportError: from distutils.core import setup package_name = 'dpkt' description = 'fast, simple packet creation / parsing, with definitions for the basic TCP/IP protocols' readme = open('README.md').read() requirements = [] # PyPI Readme long_description = open('README.md').read() # Pull in the package package = __import__(package_name) package_version = package.__version__ if "bdist_msi" in sys.argv: # The MSI build target does not support a 4 digit version, e.g. '1.2.3.4' # therefore we remove the last digit. package_version, _, _ = package_version.rpartition('.') setup(name=package_name, version=package_version, author=package.__author__, author_email=package.__author_email__, url=package.__url__, description=description, long_description=long_description, long_description_content_type='text/markdown', packages=['dpkt'], install_requires=requirements, license='BSD', zip_safe=False, classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ]) dpkt-1.9.8/tox.ini000066400000000000000000000010451427735150300140150ustar00rootroot00000000000000[tox] envlist = py27, py35, py36, py37, py38 [testenv] install_command = pip install {opts} {packages} deps = pytest coverage pytest-cov commands = py.test {posargs:--cov=dpkt dpkt} [testenv:style] deps = flake8 commands = python setup.py flake8 [testenv:coveralls] passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH deps = coveralls usedevelop = true commands = coverage report coveralls [coverage:run] relative_files = True command_line = -m pytest dpkt [coverage:report] exclude_lines = @abstractmethod