dissect.target 3.21.dev1__py3-none-any.whl → 3.21.dev2__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.
- dissect/target/helpers/configutil.py +2 -2
- dissect/target/helpers/record.py +12 -6
- dissect/target/helpers/utils.py +20 -1
- dissect/target/plugins/general/network.py +1 -1
- dissect/target/plugins/os/unix/bsd/osx/network.py +2 -1
- dissect/target/plugins/os/unix/linux/network.py +338 -0
- dissect/target/plugins/os/windows/network.py +2 -1
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/METADATA +1 -1
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/RECORD +14 -13
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/LICENSE +0 -0
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/WHEEL +0 -0
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.21.dev1.dist-info → dissect.target-3.21.dev2.dist-info}/top_level.txt +0 -0
@@ -891,7 +891,7 @@ KNOWN_FILES: dict[str, type[ConfigurationParser]] = {
|
|
891
891
|
}
|
892
892
|
|
893
893
|
|
894
|
-
def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None, *args, **kwargs) ->
|
894
|
+
def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None, *args, **kwargs) -> ConfigurationParser:
|
895
895
|
"""Parses the content of an ``path`` or ``entry`` to a dictionary.
|
896
896
|
|
897
897
|
Args:
|
@@ -922,7 +922,7 @@ def parse_config(
|
|
922
922
|
entry: FilesystemEntry,
|
923
923
|
hint: Optional[str] = None,
|
924
924
|
options: Optional[ParserOptions] = None,
|
925
|
-
) ->
|
925
|
+
) -> ConfigurationParser:
|
926
926
|
parser_type = _select_parser(entry, hint)
|
927
927
|
|
928
928
|
parser = parser_type.create_parser(options)
|
dissect/target/helpers/record.py
CHANGED
@@ -145,33 +145,40 @@ EmptyRecord = RecordDescriptor(
|
|
145
145
|
|
146
146
|
COMMON_INTERFACE_ELEMENTS = [
|
147
147
|
("string", "name"),
|
148
|
+
("string[]", "mac"),
|
148
149
|
("string", "type"),
|
149
150
|
("boolean", "enabled"),
|
150
|
-
("string", "mac"),
|
151
151
|
("net.ipaddress[]", "dns"),
|
152
152
|
("net.ipaddress[]", "ip"),
|
153
153
|
("net.ipaddress[]", "gateway"),
|
154
|
+
("net.ipnetwork[]", "network"),
|
154
155
|
("string", "source"),
|
155
156
|
]
|
156
157
|
|
157
158
|
|
158
159
|
UnixInterfaceRecord = TargetRecordDescriptor(
|
159
160
|
"unix/network/interface",
|
160
|
-
|
161
|
+
[
|
162
|
+
*COMMON_INTERFACE_ELEMENTS,
|
163
|
+
("boolean", "dhcp_ipv4"), # NetworkManager allows for dual-stack configurations.
|
164
|
+
("boolean", "dhcp_ipv6"),
|
165
|
+
("datetime", "last_connected"),
|
166
|
+
("varint[]", "vlan"),
|
167
|
+
("string", "configurator"),
|
168
|
+
],
|
161
169
|
)
|
162
170
|
|
163
171
|
WindowsInterfaceRecord = TargetRecordDescriptor(
|
164
172
|
"windows/network/interface",
|
165
173
|
[
|
166
174
|
*COMMON_INTERFACE_ELEMENTS,
|
167
|
-
("varint", "vlan"),
|
168
|
-
("net.ipnetwork[]", "network"),
|
169
175
|
("varint", "metric"),
|
170
176
|
("stringlist", "search_domain"),
|
171
177
|
("datetime", "first_connected"),
|
172
178
|
("datetime", "last_connected"),
|
173
179
|
("net.ipaddress[]", "subnetmask"),
|
174
180
|
("boolean", "dhcp"),
|
181
|
+
("varint", "vlan"),
|
175
182
|
],
|
176
183
|
)
|
177
184
|
|
@@ -179,10 +186,9 @@ MacInterfaceRecord = TargetRecordDescriptor(
|
|
179
186
|
"macos/network/interface",
|
180
187
|
[
|
181
188
|
*COMMON_INTERFACE_ELEMENTS,
|
182
|
-
("varint", "vlan"),
|
183
|
-
("net.ipnetwork[]", "network"),
|
184
189
|
("varint", "interface_service_order"),
|
185
190
|
("boolean", "dhcp"),
|
191
|
+
("varint", "vlan"),
|
186
192
|
],
|
187
193
|
)
|
188
194
|
|
dissect/target/helpers/utils.py
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import logging
|
2
4
|
import re
|
3
5
|
import urllib.parse
|
4
6
|
from datetime import datetime, timezone, tzinfo
|
5
7
|
from enum import Enum
|
6
8
|
from pathlib import Path
|
7
|
-
from typing import BinaryIO, Callable, Iterator, Optional, Union
|
9
|
+
from typing import BinaryIO, Callable, Iterator, Optional, TypeVar, Union
|
8
10
|
|
9
11
|
from dissect.util.ts import from_unix
|
10
12
|
|
@@ -24,6 +26,23 @@ def findall(buf: bytes, needle: bytes) -> Iterator[int]:
|
|
24
26
|
offset += 1
|
25
27
|
|
26
28
|
|
29
|
+
T = TypeVar("T")
|
30
|
+
|
31
|
+
|
32
|
+
def to_list(value: T | list[T]) -> list[T]:
|
33
|
+
"""Convert a single value or a list of values to a list.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
value: The value to convert.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
A list of values.
|
40
|
+
"""
|
41
|
+
if not isinstance(value, list):
|
42
|
+
return [value]
|
43
|
+
return value
|
44
|
+
|
45
|
+
|
27
46
|
class StrEnum(str, Enum):
|
28
47
|
"""Sortable and serializible string-based enum"""
|
29
48
|
|
@@ -84,9 +84,10 @@ class MacNetworkPlugin(NetworkPlugin):
|
|
84
84
|
network=network,
|
85
85
|
interface_service_order=interface_service_order,
|
86
86
|
dhcp=dhcp,
|
87
|
+
mac=[],
|
87
88
|
_target=self.target,
|
88
89
|
)
|
89
90
|
|
90
91
|
except Exception as e:
|
91
|
-
self.target.log.warning("Error reading configuration for network device %s
|
92
|
+
self.target.log.warning("Error reading configuration for network device %s", name, exc_info=e)
|
92
93
|
continue
|
@@ -0,0 +1,338 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
from datetime import datetime, timezone
|
6
|
+
from ipaddress import ip_address, ip_interface
|
7
|
+
from typing import TYPE_CHECKING, Any, Iterator, Literal, NamedTuple
|
8
|
+
|
9
|
+
from dissect.target.helpers import configutil
|
10
|
+
from dissect.target.helpers.record import UnixInterfaceRecord
|
11
|
+
from dissect.target.helpers.utils import to_list
|
12
|
+
from dissect.target.plugins.general.network import NetworkPlugin
|
13
|
+
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from ipaddress import IPv4Address, IPv4Interface, IPv6Address, IPv6Interface
|
16
|
+
|
17
|
+
from dissect.target import Target
|
18
|
+
from dissect.target.target import TargetPath
|
19
|
+
|
20
|
+
NetAddress = IPv4Address | IPv6Address
|
21
|
+
NetInterface = IPv4Interface | IPv6Interface
|
22
|
+
|
23
|
+
|
24
|
+
class LinuxNetworkPlugin(NetworkPlugin):
|
25
|
+
"""Linux network interface plugin."""
|
26
|
+
|
27
|
+
def _interfaces(self) -> Iterator[UnixInterfaceRecord]:
|
28
|
+
"""Try all available network configuration managers and aggregate the results."""
|
29
|
+
for manager_cls in MANAGERS:
|
30
|
+
manager: LinuxNetworkConfigParser = manager_cls(self.target)
|
31
|
+
yield from manager.interfaces()
|
32
|
+
|
33
|
+
|
34
|
+
VlanIdByInterface = dict[str, set[int]]
|
35
|
+
|
36
|
+
|
37
|
+
class LinuxNetworkConfigParser:
|
38
|
+
def __init__(self, target: Target):
|
39
|
+
self._target = target
|
40
|
+
|
41
|
+
def _config_files(self, config_paths: list[str], glob: str) -> list[TargetPath]:
|
42
|
+
"""Returns all configuration files in config_paths matching the given extension."""
|
43
|
+
all_files = []
|
44
|
+
for config_path in config_paths:
|
45
|
+
paths = self._target.fs.path(config_path).glob(glob)
|
46
|
+
all_files.extend(config_file for config_file in paths if config_file.is_file())
|
47
|
+
|
48
|
+
return sorted(all_files, key=lambda p: p.stem)
|
49
|
+
|
50
|
+
def interfaces(self) -> Iterator[UnixInterfaceRecord]:
|
51
|
+
"""Parse network interfaces from configuration files."""
|
52
|
+
yield from ()
|
53
|
+
|
54
|
+
|
55
|
+
class NetworkManagerConfigParser(LinuxNetworkConfigParser):
|
56
|
+
"""NetworkManager configuration parser.
|
57
|
+
|
58
|
+
NetworkManager configuration files are generally in an INI-like format.
|
59
|
+
Note that Red Hat and Fedora deprecated ifcfg files.
|
60
|
+
Documentation: https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html
|
61
|
+
"""
|
62
|
+
|
63
|
+
config_paths: list[str] = [
|
64
|
+
"/etc/NetworkManager/system-connections/",
|
65
|
+
"/usr/lib/NetworkManager/system-connections/",
|
66
|
+
"/run/NetworkManager/system-connections/",
|
67
|
+
]
|
68
|
+
|
69
|
+
@dataclass
|
70
|
+
class ParserContext:
|
71
|
+
source: str
|
72
|
+
uuid: str | None = None
|
73
|
+
last_connected: datetime | None = None
|
74
|
+
name: str | None = None
|
75
|
+
mac_address: str | None = None
|
76
|
+
type: str = ""
|
77
|
+
dns: set[NetAddress] = field(default_factory=set)
|
78
|
+
ip_interfaces: set[NetInterface] = field(default_factory=set)
|
79
|
+
gateways: set[NetAddress] = field(default_factory=set)
|
80
|
+
dhcp_ipv4: bool = False
|
81
|
+
dhcp_ipv6: bool = False
|
82
|
+
vlan: set[int] = field(default_factory=set)
|
83
|
+
|
84
|
+
def to_record(self) -> UnixInterfaceRecord:
|
85
|
+
return UnixInterfaceRecord(
|
86
|
+
source=self.source,
|
87
|
+
last_connected=self.last_connected,
|
88
|
+
name=self.name,
|
89
|
+
mac=[self.mac_address] if self.mac_address else [],
|
90
|
+
type=self.type,
|
91
|
+
dhcp_ipv4=self.dhcp_ipv4,
|
92
|
+
dhcp_ipv6=self.dhcp_ipv6,
|
93
|
+
dns=list(self.dns),
|
94
|
+
ip=[interface.ip for interface in self.ip_interfaces],
|
95
|
+
network=[interface.network for interface in self.ip_interfaces],
|
96
|
+
gateway=list(self.gateways),
|
97
|
+
vlan=list(self.vlan),
|
98
|
+
configurator="NetworkManager",
|
99
|
+
)
|
100
|
+
|
101
|
+
def interfaces(self) -> Iterator[UnixInterfaceRecord]:
|
102
|
+
connections: list[NetworkManagerConfigParser.ParserContext] = []
|
103
|
+
vlan_id_by_interface: VlanIdByInterface = {}
|
104
|
+
|
105
|
+
for connection_file_path in self._config_files(self.config_paths, "*"):
|
106
|
+
try:
|
107
|
+
config = configutil.parse(connection_file_path, hint="ini")
|
108
|
+
context = self.ParserContext(source=connection_file_path)
|
109
|
+
common_section: dict[str, str] = config.get("connection", {})
|
110
|
+
context.type = common_section.get("type", "")
|
111
|
+
sub_type: dict[str, str] = config.get(context.type, {})
|
112
|
+
|
113
|
+
if context.type == "vlan":
|
114
|
+
self._parse_vlan(sub_type, vlan_id_by_interface)
|
115
|
+
continue
|
116
|
+
|
117
|
+
for ip_version in ["ipv4", "ipv6"]:
|
118
|
+
ip_section: dict[str, str] = config.get(ip_version, {})
|
119
|
+
for key, value in ip_section.items():
|
120
|
+
self._parse_ip_section_key(key, value, context, ip_version)
|
121
|
+
|
122
|
+
context.name = common_section.get("interface-name")
|
123
|
+
context.mac_address = sub_type.get("mac-address")
|
124
|
+
context.uuid = common_section.get("uuid")
|
125
|
+
context.source = str(connection_file_path)
|
126
|
+
context.last_connected = self._parse_lastconnected(common_section.get("timestamp", ""))
|
127
|
+
|
128
|
+
connections.append(context)
|
129
|
+
|
130
|
+
except Exception as e:
|
131
|
+
self._target.log.warning("Error parsing network config file %s", connection_file_path)
|
132
|
+
self._target.log.debug("", exc_info=e)
|
133
|
+
|
134
|
+
for connection in connections:
|
135
|
+
vlan_ids_from_interface = vlan_id_by_interface.get(connection.name, set())
|
136
|
+
connection.vlan.update(vlan_ids_from_interface)
|
137
|
+
|
138
|
+
vlan_ids_from_uuid = vlan_id_by_interface.get(connection.uuid, set())
|
139
|
+
connection.vlan.update(vlan_ids_from_uuid)
|
140
|
+
|
141
|
+
yield connection.to_record()
|
142
|
+
|
143
|
+
def _parse_route(self, route: str) -> NetAddress | None:
|
144
|
+
"""Parse a route and return gateway IP address."""
|
145
|
+
if (elements := route.split(",")) and len(elements) > 1:
|
146
|
+
return ip_address(elements[1])
|
147
|
+
|
148
|
+
return None
|
149
|
+
|
150
|
+
def _parse_lastconnected(self, last_connected: str) -> datetime | None:
|
151
|
+
"""Parse last connected timestamp."""
|
152
|
+
if not last_connected:
|
153
|
+
return None
|
154
|
+
|
155
|
+
return datetime.fromtimestamp(int(last_connected), timezone.utc)
|
156
|
+
|
157
|
+
def _parse_ip_section_key(
|
158
|
+
self, key: str, value: str, context: ParserContext, ip_version: Literal["ipv4", "ipv6"]
|
159
|
+
) -> None:
|
160
|
+
if not (trimmed := value.strip()):
|
161
|
+
return
|
162
|
+
|
163
|
+
if key == "dns":
|
164
|
+
context.dns.update(ip_address(addr) for addr in trimmed.split(";") if addr)
|
165
|
+
elif key.startswith("address"):
|
166
|
+
# Undocumented: single gateway on address line. Observed when running:
|
167
|
+
# nmcli connection add type ethernet ... ip4 192.168.2.138/24 gw4 192.168.2.1
|
168
|
+
ip, *gateway = trimmed.split(",", 1)
|
169
|
+
context.ip_interfaces.add(ip_interface(ip))
|
170
|
+
if gateway:
|
171
|
+
context.gateways.add(ip_address(gateway[0]))
|
172
|
+
elif key.startswith("gateway"):
|
173
|
+
context.gateways.add(ip_address(trimmed))
|
174
|
+
elif key == "method":
|
175
|
+
if ip_version == "ipv4":
|
176
|
+
context.dhcp_ipv4 = trimmed == "auto"
|
177
|
+
elif ip_version == "ipv6":
|
178
|
+
context.dhcp_ipv6 = trimmed == "auto"
|
179
|
+
elif key.startswith("route"):
|
180
|
+
if gateway := self._parse_route(value):
|
181
|
+
context.gateways.add(gateway)
|
182
|
+
|
183
|
+
def _parse_vlan(self, sub_type: dict[str, Any], vlan_id_by_interface: VlanIdByInterface) -> None:
|
184
|
+
parent_interface = sub_type.get("parent")
|
185
|
+
vlan_id = sub_type.get("id")
|
186
|
+
if not parent_interface or not vlan_id:
|
187
|
+
return
|
188
|
+
|
189
|
+
ids = vlan_id_by_interface.setdefault(parent_interface, set())
|
190
|
+
ids.add(int(vlan_id))
|
191
|
+
|
192
|
+
|
193
|
+
class SystemdNetworkConfigParser(LinuxNetworkConfigParser):
|
194
|
+
"""Systemd network configuration parser.
|
195
|
+
|
196
|
+
Systemd network configuration files are generally in an INI-like format with some quirks.
|
197
|
+
Note that drop-in directories are not yet supported.
|
198
|
+
|
199
|
+
Documentation: https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html
|
200
|
+
"""
|
201
|
+
|
202
|
+
config_paths: list[str] = [
|
203
|
+
"/etc/systemd/network/",
|
204
|
+
"/run/systemd/network/",
|
205
|
+
"/usr/lib/systemd/network/",
|
206
|
+
"/usr/local/lib/systemd/network/",
|
207
|
+
]
|
208
|
+
|
209
|
+
class DhcpConfig(NamedTuple):
|
210
|
+
ipv4: bool
|
211
|
+
ipv6: bool
|
212
|
+
|
213
|
+
# Can be enclosed in brackets for IPv6. Can also have port, iface name, and SNI, which we ignore.
|
214
|
+
# Example: [1111:2222::3333]:9953%ifname#example.com
|
215
|
+
dns_ip_patttern = re.compile(
|
216
|
+
r"(?P<withoutBrackets>(?:\d{1,3}\.){3}\d{1,3})|\[(?P<withBrackets>\[?[0-9a-fA-F:]+\]?)\]"
|
217
|
+
)
|
218
|
+
|
219
|
+
def interfaces(self) -> Iterator:
|
220
|
+
virtual_networks = self._parse_virtual_networks()
|
221
|
+
yield from self._parse_networks(virtual_networks)
|
222
|
+
|
223
|
+
def _parse_virtual_networks(self) -> VlanIdByInterface:
|
224
|
+
"""Parse virtual network configurations from systemd network configuration files."""
|
225
|
+
|
226
|
+
virtual_networks: VlanIdByInterface = {}
|
227
|
+
for config_file in self._config_files(self.config_paths, "*.netdev"):
|
228
|
+
try:
|
229
|
+
virtual_network_config = configutil.parse(config_file, hint="systemd")
|
230
|
+
net_dev_section: dict[str, str] = virtual_network_config.get("NetDev", {})
|
231
|
+
if net_dev_section.get("Kind") != "vlan":
|
232
|
+
continue
|
233
|
+
|
234
|
+
vlan_id = virtual_network_config.get("VLAN", {}).get("Id")
|
235
|
+
if (name := net_dev_section.get("Name")) and vlan_id:
|
236
|
+
vlan_ids = virtual_networks.setdefault(name, set())
|
237
|
+
vlan_ids.add(int(vlan_id))
|
238
|
+
except Exception as e:
|
239
|
+
self._target.log.warning("Error parsing virtual network config file %s", config_file)
|
240
|
+
self._target.log.debug("", exc_info=e)
|
241
|
+
|
242
|
+
return virtual_networks
|
243
|
+
|
244
|
+
def _parse_networks(self, virtual_networks: VlanIdByInterface) -> Iterator[UnixInterfaceRecord]:
|
245
|
+
"""Parse network configurations from systemd network configuration files."""
|
246
|
+
for config_file in self._config_files(self.config_paths, "*.network"):
|
247
|
+
try:
|
248
|
+
config = configutil.parse(config_file, hint="systemd")
|
249
|
+
|
250
|
+
match_section: dict[str, str] = config.get("Match", {})
|
251
|
+
network_section: dict[str, str] = config.get("Network", {})
|
252
|
+
link_section: dict[str, str] = config.get("Link", {})
|
253
|
+
|
254
|
+
ip_interfaces: set[NetInterface] = set()
|
255
|
+
gateways: set[NetAddress] = set()
|
256
|
+
dns: set[NetAddress] = set()
|
257
|
+
mac_addresses: set[str] = set()
|
258
|
+
|
259
|
+
if link_mac := link_section.get("MACAddress"):
|
260
|
+
mac_addresses.add(link_mac)
|
261
|
+
mac_addresses.update(match_section.get("MACAddress", "").split())
|
262
|
+
mac_addresses.update(match_section.get("PermanentMACAddress", "").split())
|
263
|
+
|
264
|
+
dns_value = to_list(network_section.get("DNS", []))
|
265
|
+
dns.update(self._parse_dns_ip(dns_ip) for dns_ip in dns_value)
|
266
|
+
|
267
|
+
address_value = to_list(network_section.get("Address", []))
|
268
|
+
ip_interfaces.update(ip_interface(addr) for addr in address_value)
|
269
|
+
|
270
|
+
gateway_value = to_list(network_section.get("Gateway", []))
|
271
|
+
gateways.update(ip_address(gateway) for gateway in gateway_value)
|
272
|
+
|
273
|
+
vlan_ids: set[int] = set()
|
274
|
+
vlan_names = to_list(network_section.get("VLAN", []))
|
275
|
+
for vlan_name in vlan_names:
|
276
|
+
if ids := virtual_networks.get(vlan_name):
|
277
|
+
vlan_ids.update(ids)
|
278
|
+
|
279
|
+
# There are possibly multiple route sections, but they are collapsed into one by the parser.
|
280
|
+
route_section: dict[str, Any] = config.get("Route", {})
|
281
|
+
gateway_values = to_list(route_section.get("Gateway", []))
|
282
|
+
gateways.update(filter(None, map(self._parse_gateway, gateway_values)))
|
283
|
+
|
284
|
+
dhcp_ipv4, dhcp_ipv6 = self._parse_dhcp(network_section.get("DHCP"))
|
285
|
+
|
286
|
+
yield UnixInterfaceRecord(
|
287
|
+
source=str(config_file),
|
288
|
+
type=match_section.get("Type"),
|
289
|
+
enabled=None, # Unknown, dependent on run-time state
|
290
|
+
dhcp_ipv4=dhcp_ipv4,
|
291
|
+
dhcp_ipv6=dhcp_ipv6,
|
292
|
+
name=match_section.get("Name"),
|
293
|
+
dns=list(dns),
|
294
|
+
mac=list(mac_addresses),
|
295
|
+
ip=[interface.ip for interface in ip_interfaces],
|
296
|
+
network=[interface.network for interface in ip_interfaces],
|
297
|
+
gateway=list(gateways),
|
298
|
+
vlan=list(vlan_ids),
|
299
|
+
configurator="systemd-networkd",
|
300
|
+
)
|
301
|
+
except Exception as e:
|
302
|
+
self._target.log.warning("Error parsing network config file %s", config_file)
|
303
|
+
self._target.log.debug("", exc_info=e)
|
304
|
+
|
305
|
+
def _parse_dns_ip(self, address: str) -> NetAddress:
|
306
|
+
"""Parse DNS address from systemd network configuration file.
|
307
|
+
|
308
|
+
The optional brackets and port number make this hard to parse.
|
309
|
+
See https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html and search for DNS.
|
310
|
+
"""
|
311
|
+
|
312
|
+
if match := self.dns_ip_patttern.search(address):
|
313
|
+
return ip_address(match.group("withoutBrackets") or match.group("withBrackets"))
|
314
|
+
|
315
|
+
raise ValueError(f"Invalid DNS address format: {address}")
|
316
|
+
|
317
|
+
def _parse_dhcp(self, value: str | None) -> DhcpConfig:
|
318
|
+
"""Parse DHCP value from systemd network configuration file to a named tuple (ipv4, ipv6)."""
|
319
|
+
|
320
|
+
if value is None or value == "no":
|
321
|
+
return self.DhcpConfig(ipv4=False, ipv6=False)
|
322
|
+
elif value == "yes":
|
323
|
+
return self.DhcpConfig(ipv4=True, ipv6=True)
|
324
|
+
elif value == "ipv4":
|
325
|
+
return self.DhcpConfig(ipv4=True, ipv6=False)
|
326
|
+
elif value == "ipv6":
|
327
|
+
return self.DhcpConfig(ipv4=False, ipv6=True)
|
328
|
+
|
329
|
+
raise ValueError(f"Invalid DHCP value: {value}")
|
330
|
+
|
331
|
+
def _parse_gateway(self, value: str | None) -> NetAddress | None:
|
332
|
+
if (not value) or (value in {"_dhcp4", "_ipv6ra"}):
|
333
|
+
return None
|
334
|
+
|
335
|
+
return ip_address(value)
|
336
|
+
|
337
|
+
|
338
|
+
MANAGERS = [NetworkManagerConfigParser, SystemdNetworkConfigParser]
|
@@ -281,7 +281,8 @@ class WindowsNetworkPlugin(NetworkPlugin):
|
|
281
281
|
pass
|
282
282
|
|
283
283
|
# Extract the rest of the device information
|
284
|
-
|
284
|
+
if mac_address := _try_value(subkey, "NetworkAddress"):
|
285
|
+
device_info["mac"] = [mac_address]
|
285
286
|
device_info["vlan"] = _try_value(subkey, "VlanID")
|
286
287
|
|
287
288
|
if timestamp := _try_value(subkey, "NetworkInterfaceInstallTimestamp"):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.21.
|
3
|
+
Version: 3.21.dev2
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -46,7 +46,7 @@ dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rl
|
|
46
46
|
dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
|
48
48
|
dissect/target/helpers/config.py,sha256=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
|
49
|
-
dissect/target/helpers/configutil.py,sha256=
|
49
|
+
dissect/target/helpers/configutil.py,sha256=xXCmQA939iacd6cHr1SwNv6dyUo2bKMWsAI0TxwL24I,31428
|
50
50
|
dissect/target/helpers/cyber.py,sha256=2kAA2YjWnPfKn_aTmRSse4uB64lq6AnNX19TD8Alylc,16830
|
51
51
|
dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
|
52
52
|
dissect/target/helpers/docs.py,sha256=G5Ll1cX2kYUfNcFFI-qjeUQ5Di60RAVpVnlB72F4Vqk,5153
|
@@ -60,12 +60,12 @@ dissect/target/helpers/mount.py,sha256=7Y4B0uY2c00p23hmuMKSgwAwxkKcY4spyAMO1kdcV
|
|
60
60
|
dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
|
61
61
|
dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
|
62
62
|
dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
|
63
|
-
dissect/target/helpers/record.py,sha256=
|
63
|
+
dissect/target/helpers/record.py,sha256=VRwPE8OIotWzfxw-_ep_eXG-Iml6xzhodwPlbQYYaoY,6540
|
64
64
|
dissect/target/helpers/record_modifier.py,sha256=cRNDhUYMmx4iEKyEr5Pqy9xiFgxr_GBNJPp_omkQsEU,4094
|
65
65
|
dissect/target/helpers/regutil.py,sha256=hnJRTOQs4IqN4EroBoSNC4HDvuA8w6U1dqNBvu-ILn0,28376
|
66
66
|
dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
|
67
67
|
dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
|
68
|
-
dissect/target/helpers/utils.py,sha256=
|
68
|
+
dissect/target/helpers/utils.py,sha256=1UZNTUVBmtS2clbyIsi6mHOVw0jqPktM8OwCxCbOiY0,4587
|
69
69
|
dissect/target/helpers/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
70
|
dissect/target/helpers/compat/path_310.py,sha256=PsLDIodlp3Hv5u-w7GDl6_LnTtchBYcRjz2MicX1egg,16982
|
71
71
|
dissect/target/helpers/compat/path_311.py,sha256=2aydxCMWu1pN8PTBCo8HUbHRMC1xO-hj013j4QxaogE,18182
|
@@ -190,7 +190,7 @@ dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHn
|
|
190
190
|
dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
|
191
191
|
dissect/target/plugins/general/example.py,sha256=mYAbhtfQmUBj2L2C1DFt9bWpI7rQLJwCIYUsNLcA_pc,6053
|
192
192
|
dissect/target/plugins/general/loaders.py,sha256=z_t55Q1XNjmTOxq0E4tCwpZ-utFyxiLKyAJIFgJMlJs,1508
|
193
|
-
dissect/target/plugins/general/network.py,sha256=
|
193
|
+
dissect/target/plugins/general/network.py,sha256=I9wdFbBkDik1S9zvTi7sN20MdjJ2c_5tT8X8bgxWx5U,3257
|
194
194
|
dissect/target/plugins/general/osinfo.py,sha256=oU-vmMiA-oaSEQWTSyn6-yQiH2sLQT6aTQHRd0677wo,1415
|
195
195
|
dissect/target/plugins/general/plugins.py,sha256=9KJ70YvYwBfxt19C9yISv8YE4mOdHNvP16fTCTHC68U,6033
|
196
196
|
dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
|
@@ -220,7 +220,7 @@ dissect/target/plugins/os/unix/bsd/openbsd/__init__.py,sha256=47DEQpj8HBSa-_TImW
|
|
220
220
|
dissect/target/plugins/os/unix/bsd/openbsd/_os.py,sha256=9npz-osM-wHmjOACUqof5N5HJeps7J8KuyenUS5MZDs,923
|
221
221
|
dissect/target/plugins/os/unix/bsd/osx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
222
222
|
dissect/target/plugins/os/unix/bsd/osx/_os.py,sha256=hNFB1rwahLwgZD1kc3T4xalFusT88EoxM2Mh-8jOW_w,3440
|
223
|
-
dissect/target/plugins/os/unix/bsd/osx/network.py,sha256=
|
223
|
+
dissect/target/plugins/os/unix/bsd/osx/network.py,sha256=3m71T-T-DnZFVidrokuHpr89lZOe_4drt-Xwam1ebfw,3767
|
224
224
|
dissect/target/plugins/os/unix/bsd/osx/user.py,sha256=5rsGhsntBW9IXYIOrLpfYpSsJcBDL61QJkuZ456lXlE,2411
|
225
225
|
dissect/target/plugins/os/unix/esxi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
226
226
|
dissect/target/plugins/os/unix/esxi/_os.py,sha256=s6pAgUyfHh3QcY6sgvk5uVMmLvqK1tIHWR7MSbrFn8w,17789
|
@@ -233,6 +233,7 @@ dissect/target/plugins/os/unix/linux/environ.py,sha256=n7KttVzUtBHTIXQuS1DI5Azv6
|
|
233
233
|
dissect/target/plugins/os/unix/linux/iptables.py,sha256=qTzY5PHHXA33WnPYb5NESgoSwI7ECZ8YPoEe_Fmln-8,6045
|
234
234
|
dissect/target/plugins/os/unix/linux/modules.py,sha256=-LThb5mcKtngVfIICpdOGLtgJPc99WQ8Qufwddt8YgQ,2500
|
235
235
|
dissect/target/plugins/os/unix/linux/netstat.py,sha256=EBpbK4BD3pZ0fKCR3ZMmVip4eQ0f6x_9yumA8vsUKPw,1691
|
236
|
+
dissect/target/plugins/os/unix/linux/network.py,sha256=KfGfYhtrzpHHefaHjTpdbGSLu6IN4anweYt3V02D9zU,14392
|
236
237
|
dissect/target/plugins/os/unix/linux/network_managers.py,sha256=nr0gA3hBqsvP9k6xAYmRYwGRB1kMtXB1k0pe78jWdFI,25768
|
237
238
|
dissect/target/plugins/os/unix/linux/proc.py,sha256=jm35fAasnNbObN2tpflwQuCfVYLDkTP2EDrzYG42ZSk,23354
|
238
239
|
dissect/target/plugins/os/unix/linux/processes.py,sha256=xAJswf06HZsY8JhQ11xfJw1OLTZ1q9XZbu7_a7k2UpY,2019
|
@@ -289,7 +290,7 @@ dissect/target/plugins/os/windows/generic.py,sha256=RJ1znzsIa4CFxmdMh91SjMY_pnjw
|
|
289
290
|
dissect/target/plugins/os/windows/jumplist.py,sha256=3gZk6O1B3lKK2Jxe0B-HapOCEehk94CYNvCVDpQC9nQ,11773
|
290
291
|
dissect/target/plugins/os/windows/lnk.py,sha256=KTqhw0JMW-KjAxe4xlRDNSRSx-th-_nPVgTGyBaKmW0,7891
|
291
292
|
dissect/target/plugins/os/windows/locale.py,sha256=QiLWGgWrGBGHiXgep5iSOo6VNim4YC-xd4MdW0BUJPA,2486
|
292
|
-
dissect/target/plugins/os/windows/network.py,sha256=
|
293
|
+
dissect/target/plugins/os/windows/network.py,sha256=lziv_oqmRaVKLW-Ep8CZqfd160eVmL1RswTlPgoVapg,11336
|
293
294
|
dissect/target/plugins/os/windows/notifications.py,sha256=xxfMEY_noDxMVqvT3QS1a3j-X3qAYikOtT6v2owxuCY,17480
|
294
295
|
dissect/target/plugins/os/windows/prefetch.py,sha256=wbbYoy05gWbJfRsM2ci4wPG7kM58OocVwXD3hkQlbRw,10647
|
295
296
|
dissect/target/plugins/os/windows/recyclebin.py,sha256=zx58hDCvcrD_eJl9nJmr_i80krSN03ya8nQzWFr2Tw0,4917
|
@@ -380,10 +381,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
380
381
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
381
382
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
382
383
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
383
|
-
dissect.target-3.21.
|
384
|
-
dissect.target-3.21.
|
385
|
-
dissect.target-3.21.
|
386
|
-
dissect.target-3.21.
|
387
|
-
dissect.target-3.21.
|
388
|
-
dissect.target-3.21.
|
389
|
-
dissect.target-3.21.
|
384
|
+
dissect.target-3.21.dev2.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
385
|
+
dissect.target-3.21.dev2.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
386
|
+
dissect.target-3.21.dev2.dist-info/METADATA,sha256=CTHczoLyn0a2fvRz-vr8ZIwgmq45_UFoO7PLB2SQpqA,13302
|
387
|
+
dissect.target-3.21.dev2.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
388
|
+
dissect.target-3.21.dev2.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
389
|
+
dissect.target-3.21.dev2.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
390
|
+
dissect.target-3.21.dev2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|