Skip to content

feat(application): dissect every whole TLS record in a TCP segment (#2517) - #2537

Open
JarryShaw wants to merge 4 commits into
mainfrom
feat/2517-tls-segment-records
Open

JarryShaw wants to merge 4 commits into
mainfrom
feat/2517-tls-segment-records

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else
  • const — regenerates constant or registry tables, members keeping their numeric values

Description of your pull request and other information

Closes #2517, closes #2525.

…s the record (#2525)

An SSLv2 CLIENT-HELLO advertising 0x0300, 0x0301 or 0x0303 on TCP 443
parsed as SSLv2, but TLS's record-header read had already warned
`packet length < 0`, and under an `error` filter the hello became Raw.

- `TLS.unpack` runs the TLS trial (header read and `read`) inside
  `hold_warnings`, then the SSLv2 fallback in a block of its own.
- A successful fallback drops the TLS trial's warnings; a failed one
  drops its own. When both fail, TLS's are dropped under `beholder` and
  reported as the error leaves outside it, as #2308 rules.
- `read` reads the TLS record alone; the fallback moves to `unpack`.

Tests: new test_tls_sslv2_fallback_warnings_2525_unit.py, under both
`always` and `error`; 17 of its cases fail without the fix.
…2517)

TLS dissected only a segment's first record and left every later one
as Raw, e.g. the Alert after a ChangeCipherSpec.

- `TLS._import_next_layer` dissects the remainder as a further TLS
  layer when a whole record opens it, so each record is its own layer
  and carries the next as its payload; anything else stays Raw.
- `_opens_whole_record` reads only the header (content type, 0x03xx
  version, fragment inside the segment), so a trailing partial record
  stays Raw with no trial parse; `beholder` keeps a whole record that
  fails to decode as Raw.
- FRAME_LAYER_LIMIT bounds the records, as for any other layer.

Tests: segment cases in the TLS round-trip module (parse->rebuild and
make->parse byte-exact) and SegmentRecordTests; 16 fail without the fix.
…rial (#2517)

Owner rulings on #2517: Q2 (A), a record after the first in a segment
that fails to decode becomes Raw directly, while the first keeps its
SSLv2 fallback; Q1 (a), chained layers, goes onto the conventions.

- `TLS.unpack` takes `chained`, which `_import_next_record` passes, and
  skips `_read_sslv2_fallback` for such a record.
- `_RECORD_HEADER_LENGTH` names the record header's length for
  `_opens_whole_record`, `length` and `__length_hint__`.
- tls.rst and the module docstring name the exceptions to dissecting each
  record: one that fails to decode, a parse told to stop at TLS, and
  FRAME_LAYER_LIMIT. Application's docstring qualifies the -1 sentinel.
- protocol-family-grouping.rst records the chained-layer ruling.
- `TLS.unpack`'s Note keeps only what the code needs.

Tests: test_only_the_first_record_gets_the_sslv2_trial counts the
fallback calls on CCS + malformed ClientHello; it fails without the fix.
Review round on #2537.

- protocol-family-grouping.rst: the ruling moves to its own section,
  scoped to TLS; DTLS (#2535) and SSLv2 do not chain, and the intro
  lists #2517 among the rulings.
- tls.rst and the tls.py module docstring add octets that open no record
  to the exceptions: they stay Raw with no error.
- test_tls_declares_chained_on_unpack_and_not_on_make pins `chained` as
  a declared keyword of `TLS.unpack`, as HIP's `extension` is pinned; it
  fails against the tls.py that predates `chained`.
@JarryShaw JarryShaw added feat Pull requests that add a new capability (feat: subject prefix) review: running A cross-review is in flight against the current head - no verdict yet labels Nov 1, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Fix round at a738c7aaf, cross-reviewed (Sonnet, round 2): GOOD TO GO.

The rendered Sphinx output is unverified locally, because the venv's sphinx-autodoc-typehints is behind the pin.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Nov 1, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Cross-review (Sonnet) at 507f237bd: NEEDS CHANGES, for documentation only.

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Coverage: 93.95% (unit tier, Python 3.15, a738c7aaf, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 2 95.16%
pcapkit/const 20087 144 2710 125 98.78%
pcapkit/corekit 2605 74 818 37 96.52%
pcapkit/dumpkit 281 3 100 3 98.43%
pcapkit/foundation 3194 27 1112 31 98.65%
pcapkit/interface 138 4 52 3 96.32%
pcapkit/protocols 19930 213 5242 207 98.32%
pcapkit/toolkit 924 36 334 10 95.55%
pcapkit/utilities 502 7 144 5 98.14%
pcapkit/vendor 5009 2222 1148 182 52.31%

Per-file detail: the coverage-html artifact of this run.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat Pull requests that add a new capability (feat: subject prefix) review: good-to-go Cross-review at the current head says ready; CI state is separate

Projects

Status: In review

1 participant