Skip to content

Commit 3edd77f

Browse files
committed
fix(corekit)!: non-zero padding octets are zeroed on rebuild (#1223)
PaddingField now packs the octets it was given, and zeros when it has none or they are the wrong width, so a freshly built packet is unchanged. Schema.pack delegates its padding branch to the field. Each site carries the octets through its reader, data model and make(): - HOPOPT/IPv6-Opts PadN `pad`, QS report `unused` - IPv6-Frag `reserved_octet`; IPv6-Route SR0/Type 2 `reserved`, RPL `padding` - MH PadN option and Flow-ID PadN sub-option `data` (#1329) - HTTP/2 DATA/HEADERS/PUSH_PROMISE `padding`; OSPF crypto auth `reserved` - HIP: `reserved` on the 12 parameters listed on #1223 - HIP and SCTP trailing alignment padding: kept on the data model as `padding` only when not all zeros, and written back on make Existing tests that build these data models by hand now supply the new fields.
1 parent a8cc025 commit 3edd77f

35 files changed

Lines changed: 639 additions & 83 deletions

‎pcapkit/corekit/fields/strings.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,4 +348,26 @@ class PaddingField(BytesField):
348348
callback: Callback function to be called upon
349349
:meth:`self.__call__ <pcapkit.corekit.fields.field.FieldBase.__call__>`.
350350
351+
Padding packs back as the octets it was given, so a schema unpacked from
352+
non-zero padding rebuilds byte for byte. A value that is absent, or whose
353+
width no longer matches the field's, packs as zeros instead -- the default
354+
for a freshly built packet.
355+
351356
"""
357+
358+
def pack(self, value: 'Optional[bytes]', packet: 'dict[str, Any]') -> 'bytes': # pylint: disable=unused-argument
359+
"""Pack field value into :obj:`bytes`.
360+
361+
Args:
362+
value: Field value, i.e. the padding octets as captured.
363+
packet: Packet data.
364+
365+
Returns:
366+
Packed field value: ``value`` itself if it is exactly as wide as the
367+
field, or that many zero octets otherwise.
368+
369+
"""
370+
length = self.length
371+
if isinstance(value, (bytes, bytearray)) and len(value) == length:
372+
return bytes(value)
373+
return bytes(length)

‎pcapkit/protocols/application/httpv2.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ def _read_http_data(self, schema: 'Schema_DataFrame', *,
611611
reserved=header.stream['reserved'],
612612
sid=header.stream['sid'],
613613
data=schema.data,
614+
padding=schema.padding if flag.PADDED else b'',
614615
)
615616
return data
616617

@@ -669,6 +670,7 @@ def _read_http_headers(self, schema: 'Schema_HeadersFrame', *,
669670
stream_dependency=schema.stream_dep['sid'] if flag.PRIORITY else 0,
670671
weight=(schema.weight + 1) if flag.PRIORITY else 0,
671672
fragment=schema.fragment,
673+
padding=schema.padding if flag.PADDED else b'',
672674
)
673675
return data
674676

@@ -878,6 +880,7 @@ def _read_http_push_promise(self, schema: 'Schema_PushPromiseFrame', *,
878880
promised_reserved=schema.stream['reserved'],
879881
promised_sid=schema.stream['sid'],
880882
fragment=schema.fragment,
883+
padding=schema.padding if flag.PADDED else b'',
881884
)
882885

883886
return data
@@ -1088,6 +1091,7 @@ def _make_http_data(self, frame: 'Optional[Data_DataFrame]' = None, *,
10881091
padded: 'bool' = False,
10891092
pad_len: 'int' = 0,
10901093
data: 'bytes' = b'',
1094+
padding: 'bytes' = b'',
10911095
**kwargs: 'Any') -> 'tuple[Schema_DataFrame, Flags]':
10921096
"""Make HTTP/2 ``DATA`` frame.
10931097
@@ -1097,6 +1101,8 @@ def _make_http_data(self, frame: 'Optional[Data_DataFrame]' = None, *,
10971101
padded: Padded flag, implied by a non-zero ``pad_len``.
10981102
pad_len: Padding length.
10991103
data: Frame data.
1104+
padding: Padding octets, written as given if exactly ``pad_len``
1105+
octets and as zeros otherwise.
11001106
**kwargs: Arbitrary keyword arguments.
11011107
11021108
Returns:
@@ -1109,6 +1115,7 @@ def _make_http_data(self, frame: 'Optional[Data_DataFrame]' = None, *,
11091115

11101116
pad_len = frame.pad_len
11111117
data = frame.data
1118+
padding = getattr(frame, 'padding', b'')
11121119

11131120
flags = Schema_DataFrame.Flags(0)
11141121
if end_stream:
@@ -1119,6 +1126,7 @@ def _make_http_data(self, frame: 'Optional[Data_DataFrame]' = None, *,
11191126
return Schema_DataFrame(
11201127
pad_len=pad_len,
11211128
data=data,
1129+
padding=padding,
11221130
), flags
11231131

11241132
def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *,
@@ -1130,6 +1138,7 @@ def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *,
11301138
sid_dep: 'Optional[int]' = None,
11311139
weight: 'int' = 0,
11321140
fragment: 'bytes' = b'',
1141+
padding: 'bytes' = b'',
11331142
**kwargs: 'Any') -> 'tuple[Schema_HeadersFrame, Flags]':
11341143
"""Make HTTP/2 ``HEADERS`` frame.
11351144
@@ -1143,6 +1152,8 @@ def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *,
11431152
sid_dep: Dependency stream identifier.
11441153
weight: Priority weight value.
11451154
fragment: Header block fragment.
1155+
padding: Padding octets, written as given if exactly ``pad_len``
1156+
octets and as zeros otherwise.
11461157
**kwargs: Arbitrary keyword arguments.
11471158
11481159
Returns:
@@ -1160,6 +1171,7 @@ def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *,
11601171
sid_dep = frame.stream_dependency
11611172
weight = frame.weight
11621173
fragment = frame.fragment
1174+
padding = getattr(frame, 'padding', b'')
11631175
else:
11641176
priority = sid_dep is not None
11651177
sid_dep = sid_dep or 0
@@ -1182,6 +1194,7 @@ def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *,
11821194
},
11831195
weight=weight - 1 if weight else 0,
11841196
fragment=fragment,
1197+
padding=padding,
11851198
), flags
11861199

11871200
def _make_http_priority(self, frame: 'Optional[Data_PriorityFrame]' = None, *,
@@ -1304,6 +1317,7 @@ def _make_http_push_promise(self, frame: 'Optional[Data_PushPromiseFrame]' = Non
13041317
promised_reserved: 'int' = 0,
13051318
promised_sid: 'int' = 0,
13061319
fragment: 'bytes' = b'',
1320+
padding: 'bytes' = b'',
13071321
**kwargs: 'Any') -> 'tuple[Schema_PushPromiseFrame, Flags]':
13081322
"""Make HTTP/2 ``PUSH_PROMISE`` frame.
13091323
@@ -1315,6 +1329,8 @@ def _make_http_push_promise(self, frame: 'Optional[Data_PushPromiseFrame]' = Non
13151329
promised_reserved: Reserved bit of the promised stream identifier.
13161330
promised_sid: Promised stream identifier.
13171331
fragment: Header block fragment.
1332+
padding: Padding octets, written as given if exactly ``pad_len``
1333+
octets and as zeros otherwise.
13181334
**kwargs: Arbitrary keyword arguments.
13191335
13201336
Returns:
@@ -1328,6 +1344,7 @@ def _make_http_push_promise(self, frame: 'Optional[Data_PushPromiseFrame]' = Non
13281344
promised_reserved = frame.promised_reserved
13291345
promised_sid = frame.promised_sid
13301346
fragment = frame.fragment
1347+
padding = getattr(frame, 'padding', b'')
13311348

13321349
flags = Schema_PushPromiseFrame.Flags(0)
13331350
if end_headers:
@@ -1342,6 +1359,7 @@ def _make_http_push_promise(self, frame: 'Optional[Data_PushPromiseFrame]' = Non
13421359
'sid': promised_sid,
13431360
},
13441361
fragment=fragment,
1362+
padding=padding,
13451363
), flags
13461364

13471365
def _make_http_ping(self, frame: 'Optional[Data_PingFrame]' = None, *,

‎pcapkit/protocols/application/ospf.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ def _read_encrypt_auth(self, schema: 'Schema_CryptographicAuthentication') -> 'D
392392
393393
"""
394394
auth = Data_CryptographicAuthentication(
395+
reserved=schema.reserved,
395396
key_id=schema.key_id,
396397
len=schema.len,
397398
seq=schema.seq,
@@ -422,6 +423,7 @@ def _make_encrypt_auth(self,
422423
return Schema_CryptographicAuthentication.unpack(auth_data) # type: ignore[call-arg,misc]
423424
if isinstance(auth_data, Data_CryptographicAuthentication):
424425
return Schema_CryptographicAuthentication(
426+
reserved=auth_data.reserved,
425427
key_id=auth_data.key_id,
426428
len=auth_data.len,
427429
seq=auth_data.seq,

‎pcapkit/protocols/data/application/httpv2.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ class DataFrame(HTTP):
8989
pad_len: 'int'
9090
#: Frame payload.
9191
data: 'bytes'
92+
#: Padding octets, as captured (empty unless ``PADDED``).
93+
padding: 'bytes'
9294

9395
if TYPE_CHECKING:
94-
def __init__(self, length: 'int', type: 'Frame', flags: 'DataFrameFlags', reserved: 'int', pad_len: 'int', sid: 'int', data: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
96+
def __init__(self, length: 'int', type: 'Frame', flags: 'DataFrameFlags', reserved: 'int', pad_len: 'int', sid: 'int', data: 'bytes', padding: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
9597

9698

9799
@info_final
@@ -127,9 +129,11 @@ class HeadersFrame(HTTP):
127129
weight: 'int'
128130
#: Header block fragment.
129131
fragment: 'bytes'
132+
#: Padding octets, as captured (empty unless ``PADDED``).
133+
padding: 'bytes'
130134

131135
if TYPE_CHECKING:
132-
def __init__(self, length: 'int', type: 'Frame', flags: 'HeadersFrameFlags', reserved: 'int', pad_len: 'int', sid: 'int', excl_dependency: 'bool', stream_dependency: 'int', weight: 'int', fragment: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
136+
def __init__(self, length: 'int', type: 'Frame', flags: 'HeadersFrameFlags', reserved: 'int', pad_len: 'int', sid: 'int', excl_dependency: 'bool', stream_dependency: 'int', weight: 'int', fragment: 'bytes', padding: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
133137

134138

135139
@info_final
@@ -237,9 +241,11 @@ class PushPromiseFrame(HTTP):
237241
promised_sid: 'int'
238242
#: Header block fragment.
239243
fragment: 'bytes'
244+
#: Padding octets, as captured (empty unless ``PADDED``).
245+
padding: 'bytes'
240246

241247
if TYPE_CHECKING:
242-
def __init__(self, length: 'int', type: 'Frame', flags: 'Flags', reserved: 'int', pad_len: 'int', sid: 'int', promised_reserved: 'int', promised_sid: 'int', fragment: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
248+
def __init__(self, length: 'int', type: 'Frame', flags: 'Flags', reserved: 'int', pad_len: 'int', sid: 'int', promised_reserved: 'int', promised_sid: 'int', fragment: 'bytes', padding: 'bytes') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,line-too-long,redefined-builtin
243249

244250

245251
@info_final

‎pcapkit/protocols/data/application/ospf.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
class CryptographicAuthentication(Data):
2121
"""Data model for OSPF cryptographic authentication."""
2222

23+
#: The two reserved octets, as captured.
24+
reserved: 'bytes'
2325
#: Key ID.
2426
key_id: 'int'
2527
#: Authentication data length.
@@ -28,7 +30,7 @@ class CryptographicAuthentication(Data):
2830
seq: 'int'
2931

3032
if TYPE_CHECKING:
31-
def __init__(self, key_id: 'int', len: 'int', seq: 'int') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,redefined-builtin
33+
def __init__(self, reserved: 'bytes', key_id: 'int', len: 'int', seq: 'int') -> 'None': ... # pylint: disable=unused-argument,super-init-not-called,multiple-statements,redefined-builtin
3234

3335

3436
@info_final

0 commit comments

Comments
 (0)