sltmodel 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,129 @@
1
+ """Conditional StructLayout definitions for reassembled packet payloads."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import dataclass
6
+ from functools import lru_cache
7
+ from pathlib import Path
8
+ from types import SimpleNamespace
9
+
10
+ from sltcalc import SltEval
11
+ from sltcodec import EnumDef, StructDef, StructLayout, TypeDict
12
+
13
+ from .resources import load_pcap_layout, load_pcapng_layout
14
+
15
+
16
+ @dataclass
17
+ class PayloadStructDef:
18
+ """A condition and the StructLayout used when that condition matches."""
19
+
20
+ condition: str
21
+ struct_layout: StructLayout
22
+
23
+ def to_dict(self) -> dict:
24
+ """Return a JSON-serializable representation."""
25
+ type_dict = self.struct_layout.type_dict
26
+ return {
27
+ "condition": self.condition,
28
+ "struct_layout": {
29
+ "struct_def_name": self.struct_layout.struct_def_name,
30
+ "type_dict": {
31
+ "enum_dict": {
32
+ key: value.to_dict()
33
+ for key, value in type_dict.enum_dict.items()
34
+ },
35
+ "struct_dict": {
36
+ key: value.to_dict()
37
+ for key, value in type_dict.struct_dict.items()
38
+ },
39
+ },
40
+ },
41
+ }
42
+
43
+ @classmethod
44
+ def from_dict(cls, value: dict) -> "PayloadStructDef":
45
+ """Build one conditional definition from decoded JSON data."""
46
+ layout_data = value["struct_layout"]
47
+ type_data = layout_data["type_dict"]
48
+ type_dict = TypeDict(
49
+ enum_dict={
50
+ key: EnumDef.from_dict(item)
51
+ for key, item in type_data.get("enum_dict", {}).items()
52
+ },
53
+ struct_dict={
54
+ key: StructDef.from_dict(item)
55
+ for key, item in type_data.get("struct_dict", {}).items()
56
+ },
57
+ )
58
+ return cls(
59
+ condition=str(value["condition"]),
60
+ struct_layout=StructLayout(
61
+ str(layout_data.get("struct_def_name", "")),
62
+ type_dict,
63
+ ),
64
+ )
65
+
66
+
67
+ def packet_eval_env(packet) -> dict:
68
+ """Return the sltcalc environment for a reassembled packet."""
69
+ return {
70
+ "key": packet.key,
71
+ "payload": packet.data,
72
+ "ip_version": packet.ip_version,
73
+ "source": packet.source_bytes,
74
+ "destination": packet.destination_bytes,
75
+ "protocol": packet.protocol,
76
+ "identification": packet.identification,
77
+ }
78
+
79
+
80
+ def _enum_eval_env(enum_dict: dict) -> dict:
81
+ """Return enum definitions in the attribute form supported by SltEval."""
82
+ return {
83
+ name: SimpleNamespace(**enum_def.values)
84
+ for name, enum_def in enum_dict.items()
85
+ }
86
+
87
+
88
+ @lru_cache(maxsize=1)
89
+ def _bundled_enum_eval_env() -> dict:
90
+ """Return enums bundled with the PCAP and PCAPNG layouts."""
91
+ enum_dict = {}
92
+ for layout_loader in (load_pcap_layout, load_pcapng_layout):
93
+ enum_dict.update(layout_loader().type_dict.enum_dict)
94
+ return _enum_eval_env(enum_dict)
95
+
96
+
97
+ def matching_struct_layout(
98
+ definitions: list[PayloadStructDef],
99
+ packet,
100
+ ) -> StructLayout | None:
101
+ """Return the StructLayout from the first matching definition."""
102
+ for definition in definitions:
103
+ env = packet_eval_env(packet)
104
+ env.update(_bundled_enum_eval_env())
105
+ env.update(_enum_eval_env(definition.struct_layout.type_dict.enum_dict))
106
+ if SltEval(env).eval(definition.condition):
107
+ return definition.struct_layout
108
+ return None
109
+
110
+
111
+ def load_payload_struct_defs(path: str | Path) -> list[PayloadStructDef]:
112
+ """Load conditional payload definitions from JSON."""
113
+ data = json.loads(Path(path).read_text(encoding="utf-8"))
114
+ definitions = data.get("payload_struct_defs", data)
115
+ if not isinstance(definitions, list):
116
+ raise TypeError("payload_struct_defs must be a list.")
117
+ return [PayloadStructDef.from_dict(item) for item in definitions]
118
+
119
+
120
+ def save_payload_struct_defs(
121
+ definitions: list[PayloadStructDef],
122
+ path: str | Path,
123
+ ) -> None:
124
+ """Save conditional payload definitions as JSON."""
125
+ data = {
126
+ "payload_struct_defs":
127
+ [definition.to_dict() for definition in definitions]
128
+ }
129
+ Path(path).write_text(json.dumps(data, indent=2), encoding="utf-8")
@@ -0,0 +1,22 @@
1
+ """Bundled capture StructLayout resources."""
2
+ from importlib.resources import as_file, files
3
+
4
+ from sltcodec import StructLayout, load_struct_layout
5
+
6
+ __all__ = ["load_pcap_layout", "load_pcapng_layout"]
7
+
8
+
9
+ def _load_layout(filename: str) -> StructLayout:
10
+ resource = files(__name__).joinpath(filename)
11
+ with as_file(resource) as path:
12
+ return load_struct_layout(path)
13
+
14
+
15
+ def load_pcap_layout() -> StructLayout:
16
+ """Load the bundled PCAP StructLayout."""
17
+ return _load_layout("pcap.json")
18
+
19
+
20
+ def load_pcapng_layout() -> StructLayout:
21
+ """Load the bundled PCAPNG StructLayout."""
22
+ return _load_layout("pcapng.json")