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.
- sltmodel/__init__.py +43 -0
- sltmodel/infosize.py +93 -0
- sltmodel/packet_data.py +518 -0
- sltmodel/payload_struct_defs.py +129 -0
- sltmodel/resources/__init__.py +22 -0
- sltmodel/resources/pcap.json +604 -0
- sltmodel/resources/pcapng.json +535 -0
- sltmodel/struct_instance.py +185 -0
- sltmodel-0.1.0.dist-info/METADATA +52 -0
- sltmodel-0.1.0.dist-info/RECORD +12 -0
- sltmodel-0.1.0.dist-info/WHEEL +4 -0
- sltmodel-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -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")
|