dissect.target 3.19.dev37__py3-none-any.whl → 3.19.dev39__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,16 +4,21 @@ import logging
4
4
  import stat
5
5
  import zipfile
6
6
  from datetime import datetime, timezone
7
- from typing import BinaryIO, Optional
7
+ from typing import BinaryIO, Iterator
8
8
 
9
9
  from dissect.util.stream import BufferedStream
10
10
 
11
- from dissect.target.exceptions import FileNotFoundError
11
+ from dissect.target.exceptions import (
12
+ FileNotFoundError,
13
+ FilesystemError,
14
+ IsADirectoryError,
15
+ NotADirectoryError,
16
+ NotASymlinkError,
17
+ )
12
18
  from dissect.target.filesystem import (
13
19
  Filesystem,
14
20
  FilesystemEntry,
15
21
  VirtualDirectory,
16
- VirtualFile,
17
22
  VirtualFilesystem,
18
23
  )
19
24
  from dissect.target.helpers import fsutil
@@ -33,7 +38,7 @@ class ZipFilesystem(Filesystem):
33
38
  def __init__(
34
39
  self,
35
40
  fh: BinaryIO,
36
- base: Optional[str] = None,
41
+ base: str | None = None,
37
42
  *args,
38
43
  **kwargs,
39
44
  ):
@@ -52,12 +57,7 @@ class ZipFilesystem(Filesystem):
52
57
  continue
53
58
 
54
59
  rel_name = fsutil.normpath(mname[len(self.base) :], alt_separator=self.alt_separator)
55
-
56
- # NOTE: Normally we would check here if the member is a symlink or not
57
-
58
- entry_cls = ZipFilesystemDirectoryEntry if member.is_dir() else ZipFilesystemEntry
59
- file_entry = entry_cls(self, rel_name, member)
60
- self._fs.map_file_entry(rel_name, file_entry)
60
+ self._fs.map_file_entry(rel_name, ZipFilesystemEntry(self, rel_name, member))
61
61
 
62
62
  @staticmethod
63
63
  def _detect(fh: BinaryIO) -> bool:
@@ -69,60 +69,95 @@ class ZipFilesystem(Filesystem):
69
69
  return self._fs.get(path, relentry=relentry)
70
70
 
71
71
 
72
- class ZipFilesystemEntry(VirtualFile):
72
+ # Note: We subclass from VirtualDirectory because VirtualFilesystem is currently only compatible with VirtualDirectory
73
+ # Subclass from VirtualDirectory so we get that compatibility for free, and override the rest to do our own thing
74
+ class ZipFilesystemEntry(VirtualDirectory):
75
+ fs: ZipFilesystem
76
+ entry: zipfile.ZipInfo
77
+
78
+ def __init__(self, fs: ZipFilesystem, path: str, entry: zipfile.ZipInfo):
79
+ super().__init__(fs, path)
80
+ self.entry = entry
81
+
73
82
  def open(self) -> BinaryIO:
74
- """Returns file handle (file-like object)."""
83
+ if self.is_dir():
84
+ raise IsADirectoryError(self.path)
85
+
86
+ if self.is_symlink():
87
+ return self._resolve().open()
88
+
75
89
  try:
76
90
  return BufferedStream(self.fs.zip.open(self.entry), size=self.entry.file_size)
77
91
  except Exception:
78
- raise FileNotFoundError()
92
+ raise FileNotFoundError(self.path)
79
93
 
80
- def readlink(self) -> str:
81
- """Read the link if this entry is a symlink. Returns a string."""
82
- raise NotImplementedError()
94
+ def iterdir(self) -> Iterator[str]:
95
+ if not self.is_dir():
96
+ raise NotADirectoryError(self.path)
83
97
 
84
- def readlink_ext(self) -> FilesystemEntry:
85
- """Read the link if this entry is a symlink. Returns a filesystem entry."""
86
- raise NotImplementedError()
98
+ entry = self._resolve()
99
+ if isinstance(entry, ZipFilesystemEntry):
100
+ yield from super(ZipFilesystemEntry, entry).iterdir()
101
+ else:
102
+ yield from entry.iterdir()
87
103
 
88
- def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
89
- """Return the stat information of this entry."""
90
- return self.lstat()
104
+ def scandir(self) -> Iterator[FilesystemEntry]:
105
+ if not self.is_dir():
106
+ raise NotADirectoryError(self.path)
91
107
 
92
- def lstat(self) -> fsutil.stat_result:
93
- """Return the stat information of the given path, without resolving links."""
94
- # ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
95
- return fsutil.stat_result(
96
- [
97
- stat.S_IFREG | 0o777,
98
- self.entry.header_offset,
99
- id(self.fs),
100
- 1,
101
- 0,
102
- 0,
103
- self.entry.file_size,
104
- 0,
105
- datetime(*self.entry.date_time, tzinfo=timezone.utc).timestamp(),
106
- 0,
107
- ]
108
- )
108
+ entry = self._resolve()
109
+ if isinstance(entry, ZipFilesystemEntry):
110
+ yield from super(ZipFilesystemEntry, entry).scandir()
111
+ else:
112
+ yield from entry.scandir()
109
113
 
114
+ def is_dir(self, follow_symlinks: bool = True) -> bool:
115
+ try:
116
+ entry = self._resolve(follow_symlinks=follow_symlinks)
117
+ except FilesystemError:
118
+ return False
110
119
 
111
- class ZipFilesystemDirectoryEntry(VirtualDirectory):
112
- def __init__(self, fs: ZipFilesystem, path: str, entry: zipfile.ZipInfo):
113
- super().__init__(fs, path)
114
- self.entry = entry
120
+ if isinstance(entry, ZipFilesystemEntry):
121
+ return entry.entry.is_dir()
122
+ return isinstance(entry, VirtualDirectory)
123
+
124
+ def is_file(self, follow_symlinks: bool = True) -> bool:
125
+ try:
126
+ entry = self._resolve(follow_symlinks=follow_symlinks)
127
+ except FilesystemError:
128
+ return False
129
+
130
+ if isinstance(entry, ZipFilesystemEntry):
131
+ return not entry.entry.is_dir()
132
+ return False
133
+
134
+ def is_symlink(self) -> bool:
135
+ return stat.S_ISLNK(self.entry.external_attr >> 16)
136
+
137
+ def readlink(self) -> str:
138
+ if not self.is_symlink():
139
+ raise NotASymlinkError()
140
+ return self.fs.zip.open(self.entry).read().decode()
141
+
142
+ def readlink_ext(self) -> FilesystemEntry:
143
+ return FilesystemEntry.readlink_ext(self)
115
144
 
116
145
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
117
- """Return the stat information of this entry."""
118
- return self.lstat()
146
+ return self._resolve(follow_symlinks=follow_symlinks).lstat()
119
147
 
120
148
  def lstat(self) -> fsutil.stat_result:
121
149
  """Return the stat information of the given path, without resolving links."""
122
150
  # ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
151
+ mode = self.entry.external_attr >> 16
152
+
153
+ if self.entry.is_dir() and not stat.S_ISDIR(mode):
154
+ mode = stat.S_IFDIR | mode
155
+ elif not self.entry.is_dir() and not stat.S_ISREG(mode):
156
+ mode = stat.S_IFREG | mode
157
+
123
158
  return fsutil.stat_result(
124
159
  [
125
- stat.S_IFDIR | 0o777,
160
+ mode,
126
161
  self.entry.header_offset,
127
162
  id(self.fs),
128
163
  1,
@@ -142,3 +142,40 @@ EmptyRecord = RecordDescriptor(
142
142
  "empty",
143
143
  [],
144
144
  )
145
+
146
+ COMMON_INTERFACE_ELEMENTS = [
147
+ ("string", "name"),
148
+ ("string", "type"),
149
+ ("boolean", "enabled"),
150
+ ("string", "mac"),
151
+ ("net.ipaddress[]", "dns"),
152
+ ("net.ipaddress[]", "ip"),
153
+ ("net.ipaddress[]", "gateway"),
154
+ ("string", "source"),
155
+ ]
156
+
157
+
158
+ UnixInterfaceRecord = TargetRecordDescriptor(
159
+ "unix/network/interface",
160
+ COMMON_INTERFACE_ELEMENTS,
161
+ )
162
+
163
+ WindowsInterfaceRecord = TargetRecordDescriptor(
164
+ "windows/network/interface",
165
+ [
166
+ *COMMON_INTERFACE_ELEMENTS,
167
+ ("varint", "vlan"),
168
+ ("string", "metric"),
169
+ ("datetime", "last_connected"),
170
+ ],
171
+ )
172
+
173
+ MacInterfaceRecord = TargetRecordDescriptor(
174
+ "macos/network/interface",
175
+ [
176
+ *COMMON_INTERFACE_ELEMENTS,
177
+ ("varint", "vlan"),
178
+ ("string", "proxy"),
179
+ ("varint", "interface_service_order"),
180
+ ],
181
+ )
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Iterator, Union
4
+
5
+ from flow.record.fieldtypes.net import IPAddress, IPNetwork
6
+
7
+ from dissect.target.helpers.record import (
8
+ MacInterfaceRecord,
9
+ UnixInterfaceRecord,
10
+ WindowsInterfaceRecord,
11
+ )
12
+ from dissect.target.plugin import Plugin, export, internal
13
+ from dissect.target.target import Target
14
+
15
+ InterfaceRecord = Union[UnixInterfaceRecord, WindowsInterfaceRecord, MacInterfaceRecord]
16
+
17
+
18
+ class NetworkPlugin(Plugin):
19
+ __namespace__ = "network"
20
+
21
+ def __init__(self, target: Target):
22
+ super().__init__(target)
23
+ self._interface_list: list[InterfaceRecord] | None = None
24
+
25
+ def check_compatible(self) -> None:
26
+ pass
27
+
28
+ def _interfaces(self) -> Iterator[InterfaceRecord]:
29
+ yield from ()
30
+
31
+ def _get_record_type(self, field_name: str) -> Iterator[Any]:
32
+ for record in self.interfaces():
33
+ if (output := getattr(record, field_name, None)) is None:
34
+ continue
35
+
36
+ if isinstance(output, list):
37
+ yield from output
38
+ else:
39
+ yield output
40
+
41
+ @export(record=InterfaceRecord)
42
+ def interfaces(self) -> Iterator[InterfaceRecord]:
43
+ # Only search for the interfaces once
44
+ if self._interface_list is None:
45
+ self._interface_list = list(self._interfaces())
46
+
47
+ yield from self._interface_list
48
+
49
+ @export
50
+ def ips(self) -> list[IPAddress]:
51
+ return list(self._get_record_type("ip"))
52
+
53
+ @export
54
+ def gateways(self) -> list[IPAddress]:
55
+ return list(self._get_record_type("gateway"))
56
+
57
+ @export
58
+ def macs(self) -> list[str]:
59
+ return list(self._get_record_type("mac"))
60
+
61
+ @export
62
+ def dns(self) -> list[str]:
63
+ return list(self._get_record_type("dns"))
64
+
65
+ @internal
66
+ def with_ip(self, ip_addr: str) -> Iterator[InterfaceRecord]:
67
+ for interface in self.interfaces():
68
+ if ip_addr in interface.ip:
69
+ yield interface
70
+
71
+ @internal
72
+ def with_mac(self, mac: str) -> Iterator[InterfaceRecord]:
73
+ for interface in self.interfaces():
74
+ if interface.mac == mac:
75
+ yield interface
76
+
77
+ @internal
78
+ def in_cidr(self, cidr: str) -> Iterator[InterfaceRecord]:
79
+ cidr = IPNetwork(cidr)
80
+ for interface in self.interfaces():
81
+ if any(ip_addr in cidr for ip_addr in interface.ip):
82
+ yield interface
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev37
3
+ Version: 3.19.dev39
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
@@ -42,7 +42,7 @@ dissect/target/filesystems/tar.py,sha256=kQNhcEDPX005svse039OeR2AGSDigGuGz2AKoVr
42
42
  dissect/target/filesystems/vmfs.py,sha256=sRtYBUAKTKcHrjCXqpFJ8GIVU-ERjqxhB2zXBndtcXU,4955
43
43
  dissect/target/filesystems/vmtar.py,sha256=LlKWkTIuLemQmG9yGqL7980uC_AOL77_GWhbJc_grSk,804
44
44
  dissect/target/filesystems/xfs.py,sha256=kIyFGKYlyFYC7H3jaEv-lNKtBW4ZkD92H0WpfGcr1ww,4498
45
- dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgGQRTQ,4462
45
+ dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rljwXs,5467
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
@@ -61,7 +61,7 @@ dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q
61
61
  dissect/target/helpers/network_managers.py,sha256=ByBSe2K3c8hgQC6dokcf-hHdmPcD8PmrOj0xs1C3yhs,25743
62
62
  dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
63
63
  dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
64
- dissect/target/helpers/record.py,sha256=lWl7k2Mp9Axllm0tXzPGJx2zj2zONsyY_p5g424T0Lc,4826
64
+ dissect/target/helpers/record.py,sha256=zwqEnFSgxgX6JdhhF4zycMMZK09crCTWWEFzRxZSuC8,5658
65
65
  dissect/target/helpers/record_modifier.py,sha256=3I_rC5jqvl0TsW3V8OQ6Dltz_D8J4PU1uhhzbJGKm9c,3245
66
66
  dissect/target/helpers/regutil.py,sha256=kX-sSZbW8Qkg29Dn_9zYbaQrwLumrr4Y8zJ1EhHXIAM,27337
67
67
  dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
@@ -180,6 +180,7 @@ dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHn
180
180
  dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
181
181
  dissect/target/plugins/general/example.py,sha256=6B_YOqajRBLNWBEOfIL_HnLaEANBF8KKoc0mweihiug,6034
182
182
  dissect/target/plugins/general/loaders.py,sha256=6iUxhlSAgo7qSE8_XFxgiihK8sdMiP-s4k0W5Iv8m9k,879
183
+ dissect/target/plugins/general/network.py,sha256=Ol4Ls1w78-7zpmVaQQOZG27rvYOhJLFVhomZj5kwibs,2430
183
184
  dissect/target/plugins/general/osinfo.py,sha256=RdK5mw3-H9H3sGXz8yP8U_p3wUG1Ww7_HBKZpFdsbTE,1358
184
185
  dissect/target/plugins/general/plugins.py,sha256=4URjS6DN1Ey6Cqlbyx6NfFGgQZpWDrqxl8KLcZFODGE,4479
185
186
  dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
@@ -348,10 +349,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
348
349
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
349
350
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
350
351
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
351
- dissect.target-3.19.dev37.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
352
- dissect.target-3.19.dev37.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
353
- dissect.target-3.19.dev37.dist-info/METADATA,sha256=mXarkSk3Roesdmk1FGrFsYXYFROLuwmF6QxdMO3YOVQ,12719
354
- dissect.target-3.19.dev37.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
355
- dissect.target-3.19.dev37.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
356
- dissect.target-3.19.dev37.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
357
- dissect.target-3.19.dev37.dist-info/RECORD,,
352
+ dissect.target-3.19.dev39.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
353
+ dissect.target-3.19.dev39.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
354
+ dissect.target-3.19.dev39.dist-info/METADATA,sha256=c6QzWVFBBowHJF8fihCzibN_ELCTHhj59sC_YGZIqhY,12719
355
+ dissect.target-3.19.dev39.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
356
+ dissect.target-3.19.dev39.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
357
+ dissect.target-3.19.dev39.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
358
+ dissect.target-3.19.dev39.dist-info/RECORD,,