|
4 | 4 | import re |
5 | 5 | import uuid |
6 | 6 | from pathlib import Path |
7 | | -from typing import Iterator |
| 7 | +from typing import Callable, Iterator |
8 | 8 |
|
9 | 9 | from flow.record.fieldtypes import posix_path |
10 | 10 |
|
@@ -49,7 +49,7 @@ def __init__(self, target: Target): |
49 | 49 | super().__init__(target) |
50 | 50 | self._add_mounts() |
51 | 51 | self._add_devices() |
52 | | - self._hostname_dict = self._parse_hostname_string() |
| 52 | + self._hostname, self._domain = self._parse_hostname_string() |
53 | 53 | self._hosts_dict = self._parse_hosts_string() |
54 | 54 | self._os_release = self._parse_os_release() |
55 | 55 |
|
@@ -156,78 +156,83 @@ def architecture(self) -> str | None: |
156 | 156 |
|
157 | 157 | @export(property=True) |
158 | 158 | def hostname(self) -> str | None: |
159 | | - hosts_string = self._hosts_dict.get("hostname", "localhost") |
160 | | - return self._hostname_dict.get("hostname", hosts_string) |
| 159 | + return self._hostname or self._hosts_dict.get("hostname", "localhost") |
161 | 160 |
|
162 | 161 | @export(property=True) |
163 | 162 | def domain(self) -> str | None: |
164 | | - domain = self._hostname_dict.get("domain", "localhost") |
165 | | - if domain == "localhost": |
166 | | - domain = self._hosts_dict["hostname", "localhost"] |
167 | | - if domain == self.hostname: |
168 | | - return domain # domain likely not defined, so localhost is the domain. |
169 | | - return domain |
| 163 | + if self._domain is None or self._domain == "localhost": |
| 164 | + # fall back to /etc/hosts file |
| 165 | + return self._hosts_dict.get("hostname") |
| 166 | + return self._domain |
170 | 167 |
|
171 | 168 | @export(property=True) |
172 | 169 | def os(self) -> str: |
173 | 170 | return OperatingSystem.UNIX.value |
174 | 171 |
|
175 | | - def _parse_rh_legacy(self, path: Path) -> str | None: |
176 | | - hostname = None |
177 | | - file_contents = path.open("rt").readlines() |
178 | | - for line in file_contents: |
179 | | - if not line.startswith("HOSTNAME"): |
180 | | - continue |
181 | | - _, _, hostname = line.rstrip().partition("=") |
182 | | - return hostname |
183 | | - |
184 | | - def _parse_hostname_string(self, paths: list[str] | None = None) -> dict[str, str] | None: |
185 | | - """Returns a dict containing the hostname and domain name portion of the path(s) specified. |
| 172 | + def _parse_hostname_string( |
| 173 | + self, paths: list[tuple[str, Callable[[Path], str] | None]] | None = None |
| 174 | + ) -> tuple[str | None, str | None]: |
| 175 | + """Returns a tuple containing respectively the hostname and domain name portion of the path(s) specified. |
186 | 176 |
|
187 | 177 | Args: |
188 | | - paths (list): list of paths |
189 | | - """ |
190 | | - redhat_legacy_path = "/etc/sysconfig/network" |
191 | | - paths = paths or ["/etc/hostname", "/etc/HOSTNAME", "/proc/sys/kernel/hostname", redhat_legacy_path] |
192 | | - hostname_dict = {"hostname": None, "domain": None} |
| 178 | + paths (list): list of tuples with paths and callables to parse the path or None |
193 | 179 |
|
194 | | - for path in paths: |
195 | | - path = self.target.fs.path(path) |
196 | | - |
197 | | - if not path.exists(): |
| 180 | + Returns: |
| 181 | + Tuple with hostname and domain strings. |
| 182 | + """ |
| 183 | + hostname = None |
| 184 | + domain = None |
| 185 | + |
| 186 | + paths = paths or [ |
| 187 | + ("/etc/hostname", None), |
| 188 | + ("/etc/HOSTNAME", None), |
| 189 | + ("/proc/sys/kernel/hostname", None), |
| 190 | + ("/etc/sysconfig/network", self._parse_rh_legacy), |
| 191 | + ("/etc/hosts", self._parse_etc_hosts), # fallback if no other hostnames are found |
| 192 | + ] |
| 193 | + |
| 194 | + for path, callable in paths: |
| 195 | + if not (path := self.target.fs.path(path)).exists(): |
198 | 196 | continue |
199 | 197 |
|
200 | | - if path.as_posix() == redhat_legacy_path: |
201 | | - hostname_string = self._parse_rh_legacy(path) |
| 198 | + if callable: |
| 199 | + hostname = callable(path) |
202 | 200 | else: |
203 | | - hostname_string = path.open("rt").read().rstrip() |
| 201 | + hostname = path.open("rt").read().rstrip() |
| 202 | + |
| 203 | + if hostname and "." in hostname: |
| 204 | + hostname, domain = hostname.split(".", maxsplit=1) |
204 | 205 |
|
205 | | - if hostname_string and "." in hostname_string: |
206 | | - hostname_string = hostname_string.split(".", maxsplit=1) |
207 | | - hostname_dict = {"hostname": hostname_string[0], "domain": hostname_string[1]} |
208 | | - elif hostname_string != "": |
209 | | - hostname_dict = {"hostname": hostname_string, "domain": None} |
210 | | - else: |
211 | | - hostname_dict = {"hostname": None, "domain": None} |
212 | 206 | break # break whenever a valid hostname is found |
213 | 207 |
|
214 | | - return hostname_dict |
| 208 | + # Can be an empty string due to splitting of hostname and domain |
| 209 | + return hostname or None, domain or None |
| 210 | + |
| 211 | + def _parse_rh_legacy(self, path: Path) -> str | None: |
| 212 | + hostname = None |
| 213 | + file_contents = path.open("rt").readlines() |
| 214 | + for line in file_contents: |
| 215 | + if not line.startswith("HOSTNAME"): |
| 216 | + continue |
| 217 | + _, _, hostname = line.rstrip().partition("=") |
| 218 | + return hostname |
| 219 | + |
| 220 | + def _parse_etc_hosts(self, path: Path) -> str | None: |
| 221 | + for line in path.open("rt"): |
| 222 | + if line.startswith(("127.0.0.1 ", "::1 ")) and "localhost" not in line: |
| 223 | + return line.split(" ")[1] |
215 | 224 |
|
216 | 225 | def _parse_hosts_string(self, paths: list[str] | None = None) -> dict[str, str]: |
217 | 226 | paths = paths or ["/etc/hosts"] |
218 | | - hosts_string = {"ip": None, "hostname": None} |
| 227 | + hosts_string = {} |
219 | 228 |
|
220 | 229 | for path in paths: |
221 | 230 | for fs in self.target.filesystems: |
222 | 231 | if fs.exists(path): |
223 | 232 | for line in fs.path(path).open("rt").readlines(): |
224 | | - line = line.split() |
225 | | - if not line: |
| 233 | + if not (line := line.split()): |
226 | 234 | continue |
227 | | - |
228 | | - if (line[0].startswith("127.0.") or line[0].startswith("::1")) and line[ |
229 | | - 1 |
230 | | - ].lower() != "localhost": |
| 235 | + if line[0].startswith(("127.0.", "::1")): |
231 | 236 | hosts_string = {"ip": line[0], "hostname": line[1]} |
232 | 237 | return hosts_string |
233 | 238 |
|
|
0 commit comments