Repository navigation
Commit 53f1e57
authored
fix(reassembly): the deferred TCP analyser dispatches on AppType, not bare ports (#2421)
Follow-up to #2418, which made `Deferred` generic over what its analyser
dispatches on but left the TCP side of that constraint as `tuple[int, int]`.
The owner's note on it: we are still using int tuples for transport ports, so
move to AppType tuples.
* `TCP.submit` resolves `bufid[1]` / `bufid[3]` through `Transport._make_port`
before building the `Deferred`, in a new `TCP._make_ports` helper. A deferred
analysis now dispatches on a named service rather than on a number.
* `_KT`'s second constraint is `tuple[AppType_TCP, AppType_TCP]` -- the
per-transport registry `pcapkit.const.reg.apptype.tcp.TCP`, which is what the
resolution really answers with (`type(...) is TCP`, measured), not the base
`AppType`. `Transport.analyze` keeps the base, being the one method TCP, UDP
and SCTP share, and says so.
* `Transport.analyze` tests the chosen port against `__proto__` exactly as it
arrived and unwraps it to the registry's `int` key only when it is a member,
so a bare `int` caller keeps working -- the method is in v1.4.1 -- and
`(None, None)` or `(54321, '80')` still fall back to `Raw` as on mainline
rather than raising or dispatching.
* `_make_ports` reaches `_make_port` through `TCP` rather than through
`self.protocol`: `__protocol_type__` is an extension point whose analyser need
only define `analyze`, so `self.protocol._make_port` would be an
`AttributeError` for one that is not a `Transport`. Going through `_make_port`
rather than calling `AppType.get` directly is what keeps #2287's bool
rejection: `AppType.get(True, proto=tcp)` resolves `True` as port 1 and
answers `<TCP.tcpmux: 1 [tcp]>`, measured.
* `DatagramID`'s ports stay bare `int`s, and its docstring now says why: it is
the flow identity split out of `BufferID`, a dict key every toolkit adapter
builds from a plain port number and which is dumped as part of the report.
* Docs entries for `TCP._make_ports` and for `AppType.__hash__`, both now named
in public prose (#1940).
All 65,536 ports resolve to a member -- an assignment to its canonical service,
an IANA span to that span's label, anything else to an unregistered `unknown` --
none raise and the registry does not grow, so this adds no way for reassembly to
raise. For a registered port the member is the one `TCP.src` reads off a parsed
header; for an unassigned one the two are equal but not identical and named
apart, which the docstrings record rather than overstate. UDP and SCTP need no
change: neither has a reassembler, so `Transport.analyze` has no caller but
`TCP.submit`.
Make an unregistered member survive pickling
--------------------------------------------
Dispatching on members rather than numbers put them inside the `Deferred` that
`TCP.submit` stores as a `Datagram`'s `packet`, and that made an unresolved
datagram unpicklable for most ports. `AppType.get` answers a port it holds no
assignment for through `AppType._unregistered_member`, which builds a member
deliberately absent from every one of the registry's lookup tables so the
lookup does not grow it (#860). `Enum.__reduce_ex__` reduces a member to
`(cls, (value,))` -- a value-keyed lookup, i.e. the one table such a member is
missing from -- so it pickled fine and then raised on read-back:
ValueError: 'unknown [50000 - tcp]' is not a valid TCP
Measured before the fix: 59,413 of the 65,536 possible TCP ports failed to
round-trip, 90.7% of the space, and not merely the truly unregistered `unknown`
ones but every IANA-span label too. All four sample captures with TCP flows
failed, every datagram, because a client socket is ephemeral.
* `EnumRegistry._make_reducible` installs a per-instance `__reduce_ex__` that
rebuilds through `cls._unregistered_member(*args)` instead of through a value
lookup, with module-level `_rebuild_unregistered_member` /
`_reduce_unregistered_member` so that pickle protocols below 4 can name the
callable. This is the technique `EnumField._unregistered_member` already uses
for the same class of problem on the *parse* path (#575, #1774).
* `AppType._unregistered_member` opts in, reducing to its own
`(value, name, proto)` rather than to the crafted `'svc [port - proto]'`
string it hands its base -- reducing to the latter would rebuild a member
whose value had been formatted twice. It is opt-in per registry for exactly
that reason; a registry whose `_missing_` calls the base method directly is
unchanged.
* Rebuilding re-enters `_unregistered_member` and never `cls.__new__`, so an
unpickled member is unregistered just as the original was and no table grows
-- `__members__`, `_value2member_map_` and `__registry__` all measured
unchanged before and after, on every transport. A restored member is itself
reducible, so a datagram read back from a dump can be written out again.
After: all 65,536 ports round-trip on every transport (262,144 members), on
every pickle protocol 0-5, under `copy.copy`/`copy.deepcopy`, and all four
named captures load back -- 231 deferred datagrams across `http.pcap`,
`stream.pcap`, `tcp.pcap` and `http6.cap`. A registered member still unpickles
to the identical shared singleton, which pins that the reducer went on the
unregistered path only.
* `tests/const/test_const_apptype_unregistered_pickle_2417_unit.py` pins the
member-level round trip over the whole port space, every protocol, and the
no-growth property.
* `tests/foundation/reassembly/test_datagram_deferred_pickle_2417_unit.py` pins
the symptom on a synthesised datagram, unassigned and assigned alike.
`test_datagram_deferred_pickle_2417_runtime.py` pins the same thing end to
end over the four sample captures, split into its own fixture-dependent
module because CI's unit-tests leg never generates them -- the unit file
reading a generated capture directly is what broke that leg. Both scope to
pickle protocols 2-5: `Deferred` declares `__slots__` and no `__getstate__`,
which protocols 0 and 1 cannot serialise at all. That is pre-existing and
independent of these members -- it reproduces for an assigned flow and for a
bare `Deferred(..., 42, b'x')` holding a plain `int` -- so it is recorded
there rather than worked around, and wants its own issue.
* The #1774 canary asserted that `get`'s miss member was *not* picklable, which
was a defect rather than a distinction. It now asserts the round trip works
and that the member's `name` is the only remaining difference from what a
parse returns -- which is what still justifies the peek. `AppType.__hash__`
gained the docstring its new `#1940` entry requires.
mypy over `pcapkit/` is byte-identical to mainline (98 errors in 32 files, same
set); `pylint` with the Makefile flags reports the same message set and counts
over the touched files as the pre-review revision; `isort -l100 -ppcapkit` is
clean. Tests: 876 tests / 25,888 subtests over `foundation/reassembly`,
`protocols/transport`, the `const` registries, the field-pickling suites, the
`#1940` docs guard and the docstring contract, with 4 skips that predate this
branch (the local venv has sphinx-autodoc-typehints 3.13.6, not the 3.17.2
`docs/requirements.txt` pins). Reassembly over the sample captures resolves the
same next layer as before, port for port.1 parent 867616b commit 53f1e57
15 files changed
Lines changed: 1153 additions & 61 deletions
File tree
- docs/source/pcapkit
- const
- foundation/reassembly
- pcapkit
- const/reg/apptype
- corekit
- foundation/reassembly
- data
- protocols/transport
- vendor/reg/apptype
- tests
- const
- corekit
- foundation/reassembly
- project
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
| 112 | + | |
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2497 | 2497 | | |
2498 | 2498 | | |
2499 | 2499 | | |
| 2500 | + | |
| 2501 | + | |
| 2502 | + | |
| 2503 | + | |
| 2504 | + | |
| 2505 | + | |
| 2506 | + | |
| 2507 | + | |
| 2508 | + | |
| 2509 | + | |
| 2510 | + | |
2500 | 2511 | | |
2501 | 2512 | | |
2502 | 2513 | | |
| |||
2941 | 2952 | | |
2942 | 2953 | | |
2943 | 2954 | | |
| 2955 | + | |
| 2956 | + | |
| 2957 | + | |
| 2958 | + | |
| 2959 | + | |
| 2960 | + | |
| 2961 | + | |
| 2962 | + | |
| 2963 | + | |
| 2964 | + | |
| 2965 | + | |
| 2966 | + | |
| 2967 | + | |
| 2968 | + | |
| 2969 | + | |
| 2970 | + | |
2944 | 2971 | | |
2945 | 2972 | | |
2946 | 2973 | | |
| |||
2956 | 2983 | | |
2957 | 2984 | | |
2958 | 2985 | | |
2959 | | - | |
| 2986 | + | |
| 2987 | + | |
| 2988 | + | |
2960 | 2989 | | |
2961 | 2990 | | |
2962 | 2991 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
| 99 | + | |
99 | 100 | | |
100 | 101 | | |
101 | 102 | | |
| |||
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
107 | | - | |
| 108 | + | |
108 | 109 | | |
109 | 110 | | |
110 | 111 | | |
| |||
709 | 710 | | |
710 | 711 | | |
711 | 712 | | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | | - | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
148 | 149 | | |
149 | 150 | | |
150 | 151 | | |
151 | | - | |
152 | | - | |
| 152 | + | |
| 153 | + | |
153 | 154 | | |
154 | 155 | | |
155 | 156 | | |
156 | 157 | | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
161 | 175 | | |
162 | 176 | | |
163 | 177 | | |
164 | 178 | | |
165 | 179 | | |
166 | 180 | | |
167 | | - | |
| 181 | + | |
| 182 | + | |
168 | 183 | | |
169 | 184 | | |
170 | 185 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
84 | 85 | | |
85 | 86 | | |
86 | 87 | | |
87 | | - | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
88 | 108 | | |
89 | 109 | | |
90 | 110 | | |
| |||
150 | 170 | | |
151 | 171 | | |
152 | 172 | | |
153 | | - | |
| 173 | + | |
154 | 174 | | |
155 | 175 | | |
156 | 176 | | |
| |||
0 commit comments