Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ This is the resolution of #548, which reported `TransType.L2TP` (115) as registe
- **Fixed** -- `httpv1`'s `_RE_METHOD` was unanchored and `re.match` anchors only at the start, so it prefix-matched, and the request-line reader then passed the whole `para1` to `Method.get` rather than the captured `method` group. Together those meant `b'Get'` matched on the single character `G`, satisfied the guard that decides a start-line is a request, and handed the entire mixed-case token to a lookup that raised on it. Fixing either half alone still gives a wrong answer -- normalising the lookup would parse `b'Get'` as `GET` off a one-character match, and passing the group would parse it as a method named `G`. The pattern is now anchored at both ends and the captured group is what is looked up, so a token that is not a method is a malformed request line rather than a mis-parsed one. Method tokens are case-sensitive per [RFC 9110 Section 9.1](https://datatracker.ietf.org/doc/html/rfc9110#section-9.1), so no `re.I` was added: `GET` parses, `Get` and `get` are rejected (#583).
- **Fixed** -- `_RE_STATUS` in the same reader carried the same unanchored prefix defect, found by auditing `_RE_METHOD`'s siblings, and it escaped as the wrong exception type. That pattern is only a guard -- the value is taken from `int(para2)` on the raw token -- so a prefix match let a malformed status past the guard and then out of `int()` uncaught, where `_read_http_header` documents `ProtocolError`. Measured: a status of `200x` raised `ValueError: invalid literal for int() with base 10: b'200x'`, and one of `2000` raised `ValueError: 2000 is not a valid StatusCode`; both are now `ProtocolError`. [RFC 9112 Section 4](https://datatracker.ietf.org/doc/html/rfc9112#section-4) gives `status-code = 3DIGIT`, exactly three, so the anchor is what the grammar already said -- the production lives in HTTP/1.1 because `status-code` is part of its `status-line`, while [RFC 9110 Section 15](https://datatracker.ietf.org/doc/html/rfc9110#section-15) covers the code semantics and the IANA registry rather than the syntax. `_RE_VERSION` was audited at the same time and is safe as it stands, because both of its call sites read the captured group rather than the raw token (#583).
- **Fixed** -- `get()`'s documented `default` was ignored on the integer path throughout the generated `pcapkit.const` tree, because `get` delegated the lookup to the enum call and `_missing_` has no access to the caller's `default` -- so `Hardware.get(99999, 0)` raised `ValueError: 99999 is not a valid Hardware` instead of returning the fallback it was handed. The integer path now consults `default` before letting the lookup error escape. `-1`, the placeholder the generated signature already carried, is what separates "no default was supplied" from "a default was supplied and should be used", so a caller that asked for no fallback still gets the error rather than a silent substitution. The sweep #584 asked for puts the scope at 110 of the 118 integer registries, not the three the issue named; the two carrying a bespoke integer fallback of their own, `pcapng` `OptionType` and `reg` `AppType`, are deliberately left alone, since neither drops a default by raising. Not reachable from wire data -- every value a wire field can carry already resolves -- so this is a contract fix rather than a parse fix. Applied to the nine vendor templates as well as the 113 generated modules, and a new test renders the shared template and compares it against the module generated from it, so a regeneration cannot quietly undo it (#584).
- **Added** -- `CITATION.cff`, citation metadata in Citation File Format 1.2.0, which GitHub renders as the repository's "Cite this repository" button and which citation managers and dependency inventories read directly. It is the machine-readable half of the attribution BSD-3-Clause already asks for, so credit carries into a paper or a bill of materials rather than depending on a reader opening `LICENSE`. Validated with `cffconvert --validate` and against the published 1.2.0 schema; `doi` and `orcid` are omitted rather than invented, since neither exists for this project today and both are checked formats, so a wrong value would still validate. The licence itself is deliberately unchanged -- still BSD-3-Clause, no `NOTICE` file, no change to its terms. Alongside it the copyright line moves from `2018-2023` to `2018-2026` -- `LICENSE` was its only occurrence in the tree, since `docs/source/conf.py` already derives its own from the current year -- and a stray `s` after the closing `DAMAGE.` of the licence text, present since the Mozilla-to-BSD relicence, is removed, so the wording now matches canonical BSD-3-Clause exactly (#615).
- **Fixed** -- reading a big-endian classic PCAP byte-swapped every record header field, and then crashed. `Frame.unpack` seeded the file's declared byte order under the key `bytesorder` where the frame schema's `byteorder_callback` reads `byteorder`, so the lookup never found it and always fell back to `sys.byteorder` -- the reading host's order rather than the file's. On a little-endian host reading a little-endian capture that fallback gives the right answer by coincidence, and every capture in this repository was little-endian, so the wrong code path has always produced correct results. Against a big-endian capture, measured before the fix, frame 1 of `big_endian.pcap` read `ts_sec=3106905`, `ts_usec=1088553216` and `incl_len=1241513984` for a record whose real values are `1500000000`, `123456` and `74`, dating the frame to 1970-02-05 rather than to 2017-07-14. `incl_len` is the payload length, so that first record then consumed the whole file and the second was read with a negative payload length, raising `ValueError: read length must be non-negative or -1` out of the schema -- which is the reported crash, and it is the *second* symptom rather than the first. The sibling `Frame.pack` eleven lines earlier spelled the key correctly, which is what marks this as a slip rather than a second key, and the fallback is what made a misspelled key indistinguishable from an absent one; `byteorder_callback` now records that it is the definition of the key and why the fallback hides a typo (#605).
- **Added** -- `examples/generators/endian.py`, and the byte-order tests that read what it writes. There was no big-endian `.pcap` in the repository at all, which is why #605 survived its own code review: the one-character fix leaves the corrected path exactly as untested as the broken one. The generator writes three captures -- `big_endian.pcap` (magic `a1 b2 c3 d4`), `big_endian_nanosecond.pcap` (`a1 b2 3c 4d`, the first fixture to take that branch of the magic-number table) and `little_endian.pcap` (`d4 c3 b2 a1`) -- carrying the *same three records* in each container, so the tests can assert that the byte order makes no difference to what is read out rather than only that the big-endian file matches numbers written down in a test. Frame 3 is captured short, 1200 octets on the wire cut to a 96-octet `snaplen`, so `incl_len` and `orig_len` differ and cannot both be satisfied by one byte-swapped value. `test_frame_endian_runtime.py` drives all three through `extract()` and walks each file's record chain with `struct` to derive its own expectations; a unit-tier case in `test_header_frame_unit.py` builds a two-record big-endian capture in memory instead, so the regression is also caught by the fixture-free selection CI runs on every push. All four fail on the unfixed tree -- the three fixture-backed ones by that `ValueError`, the in-memory one by `AssertionError: 3106905 != 1500000000` -- while the little-endian twin passes on both trees, which is what shows the records themselves are not the variable (#605).
- **Changed** -- `tests/protocols/transport/test_tcp_udp_unit.py` now reaches the MP_JOIN dispatchers through `TCP()` itself, instead of assigning a Python `set` to `_flags` on a bare `TCP.__new__(TCP)`. A `set` answers the membership tests `_make_mptcp_join` and `_read_mptcp_join` use, so every flag branch ran and both TCP modules read 100% statement and branch coverage -- while the attribute had neither the `aenum.IntFlag` type production assigns nor the ordering that governs when it exists at all, which is how #587 stayed invisible behind that number and how the `cast('Enum_Flags', 0)` no-op behind it went unnoticed too. Measured on the rewrite, against the 17 tests of that file: revert #587's hoist and two of them fail with `AttributeError: 'TCP' object has no attribute '_flags'` where all 17 passed before; restore the `cast` and two fail with `TypeError: argument of type 'int' is not a container or iterable`, again where all 17 passed. The library is unchanged and the file's tests still pass, so the coverage numbers do not move -- the point is what the same numbers are now worth (#603).
Expand Down
40 changes: 40 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Citation metadata for PyPCAPKit, in Citation File Format 1.2.0.
# c.f. https://citation-file-format.github.io
#
# This is the machine-readable half of the attribution that BSD-3-Clause asks for.
# GitHub renders it as the repository's "Cite this repository" button, and citation
# managers, Zenodo and dependency inventories read it directly -- so credit carries
# into a paper or a bill of materials instead of depending on a reader noticing
# ``LICENSE``. It grants nothing and restricts nothing; the licence is unchanged.
#
# ``version`` and ``date-released`` describe the newest *published* release, so they
# move with ``pcapkit.__version__`` in ``pcapkit/__init__.py`` and the matching entry
# under ``docs/source/changelog/`` when a release goes out.
#
# No ``doi`` and no ``orcid``: neither exists for this project or its author today,
# and both fields are checked formats rather than free text, so an invented value
# would validate and still be wrong. They are omitted rather than guessed.
cff-version: 1.2.0
message: If you use this software, please cite it as below.
title: PyPCAPKit
abstract: >-
PyPCAPKit is a comprehensive, Python-native network packet analysis library: a
PCAP file extraction, construction and analysis toolkit that dissects protocols
in more detail than the common extractors do, over a choice of extraction
engines, and exposes them through a Pythonic interface.
type: software
authors:
- given-names: Jarry
family-names: Shaw
email: jarryshaw@icloud.com
alias: JarryShaw
repository-code: https://github.com/JarryShaw/PyPCAPKit
url: https://jarryshaw.github.io/PyPCAPKit/
repository-artifact: https://pypi.org/project/pypcapkit/
license: BSD-3-Clause
version: 1.5.0b4
date-released: '2026-09-20'
keywords:
- network
- pcap
- packet
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2018-2023, Jarry Shaw
Copyright (c) 2018-2026, Jarry Shaw
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand All @@ -26,4 +26,4 @@ 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.s
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 changes: 16 additions & 0 deletions docs/source/changelog/1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,22 @@ pull requests between #326 and #509.
well as the 113 generated modules, and a new test renders the shared template
and compares it against the module generated from it, so a regeneration
cannot quietly undo it (#584).
* **Added** -- ``CITATION.cff``, citation metadata in Citation File Format
1.2.0, which GitHub renders as the repository's "Cite this repository" button
and which citation managers and dependency inventories read directly. It is
the machine-readable half of the attribution BSD-3-Clause already asks for, so
credit carries into a paper or a bill of materials rather than depending on a
reader opening ``LICENSE``. Validated with ``cffconvert --validate`` and
against the published 1.2.0 schema; ``doi`` and ``orcid`` are omitted rather
than invented, since neither exists for this project today and both are
checked formats, so a wrong value would still validate. The licence itself is
deliberately unchanged -- still BSD-3-Clause, no ``NOTICE`` file, no change to
its terms. Alongside it the copyright line moves from ``2018-2023`` to
``2018-2026`` -- ``LICENSE`` was its only occurrence in the tree, since
``docs/source/conf.py`` already derives its own from the current year -- and a
stray ``s`` after the closing ``DAMAGE.`` of the licence text, present since
the Mozilla-to-BSD relicence, is removed, so the wording now matches canonical
BSD-3-Clause exactly (#615).
* **Fixed** -- reading a big-endian classic PCAP byte-swapped every record header
field, and then crashed. ``Frame.unpack`` seeded the file's declared byte order
under the key ``bytesorder`` where the frame schema's ``byteorder_callback``
Expand Down
Loading