|
| 1 | +""" |
| 2 | +This module implements hostname <-> IP addr resolution logic (it is not as simple as it may look). |
| 3 | +
|
| 4 | +Most of CMAPI/Columnstore code assumes that each node has 1 IP address and 1 hostname. |
| 5 | +But in reality it can have N IP addresses and M hostnames, and we need some way to choose which IP/hostname will be primary. |
| 6 | +Also some of these names will not be visible from the other hosts (like local aliases from systemd). |
| 7 | +And some sources are more reliable than the others (DNS > /etc/hosts), so we must order them by reliablity and choose the most reliable. |
| 8 | +
|
| 9 | +So: |
| 10 | +1. We must choose 1 IP address and 0/1 hostnames as primary (of many) |
| 11 | +2. We need to filter out unreliable names |
| 12 | +3. We must order IPs/hostnames by source reliability |
| 13 | +4. There can be very many resolving sources, so we cannot resolve everything ourselves, and must rely on OS resolving |
| 14 | + (see /etc/nsswitch.conf, there are local /etc/hosts, DNS, mDNS, systemd-resolved, LDAP, myhostname, etc) |
| 15 | +5. But most important sources (DNS and /etc/hosts, recommended by our manual...) must be checked and ordered by our policy |
| 16 | +""" |
1 | 17 | import hashlib |
2 | 18 | import ipaddress |
3 | 19 | import logging |
|
12 | 28 |
|
13 | 29 | from cmapi_server.exceptions import CMAPIBasicError, ResolutionError, ResolutionPolicyViolationError |
14 | 30 | from cmapi_server.managers.network import NetworkManager |
| 31 | +from cmapi_server.managers.resolving_sources import get_resolving_source |
15 | 32 |
|
16 | 33 | IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address] |
17 | 34 |
|
@@ -310,44 +327,14 @@ def _get_identity_from_non_fqdn(self, hostname: str) -> HostIdentity: |
310 | 327 |
|
311 | 328 | def _resolve_dns(self, hostname: str) -> list[IPAddress]: |
312 | 329 | """Resolve the given hostname using DNS and return addresses.""" |
313 | | - ipv4_texts: list[str] = [] |
314 | | - ipv6_texts: list[str] = [] |
315 | | - try: |
316 | | - ipv4_texts = self._dns_resolve_ipv4(hostname) |
317 | | - except dns.resolver.NoAnswer: |
318 | | - logger.warning('IPv4 lookup returned no records for %s', hostname) |
319 | | - ipv4_texts = [] |
320 | | - except Exception: |
321 | | - logger.exception('IPv4 lookup unexpected failure for %s', hostname) |
322 | | - raise |
323 | | - |
324 | | - if self._policy.allow_ipv6: |
325 | | - try: |
326 | | - ipv6_texts = self._dns_resolve_ipv6(hostname) |
327 | | - except dns.resolver.NoAnswer: |
328 | | - logger.warning('IPv6 lookup returned no records for %s', hostname) |
329 | | - ipv6_texts = [] |
330 | | - except Exception: |
331 | | - logger.exception('IPv6 lookup unexpected failure for %s', hostname) |
332 | | - raise |
333 | | - |
334 | | - addrs: list[IPAddress] = [] |
335 | | - for ip_text in ipv4_texts + ipv6_texts: |
336 | | - ip = _ip_or_none(ip_text) |
337 | | - if ip is None: |
338 | | - logger.error('DNS returned invalid IP address %s for host name %s, skipping', ip_text, hostname) |
339 | | - continue |
340 | | - addrs.append(ip) |
341 | | - |
342 | | - return addrs |
| 330 | + resolver = get_resolving_source('dns') |
| 331 | + return resolver.resolve(hostname) |
343 | 332 |
|
344 | 333 | def _get_names_of_ip(self, ip: IPAddress) -> list[str]: |
345 | 334 | """Fetch PTR names for an IP via DNS.""" |
346 | 335 | try: |
347 | | - return self._dns_reverse(str(ip)) |
348 | | - except dns.resolver.NoAnswer: |
349 | | - logger.warning('ip-to-name lookup returned no records for %s', ip) |
350 | | - return [] |
| 336 | + resolver = get_resolving_source('dns') |
| 337 | + return resolver.reverse(ip) |
351 | 338 | except Exception: |
352 | 339 | logger.exception('ip-to-name lookup unexpected failure for %s', ip) |
353 | 340 | raise |
|
0 commit comments