from collections.abc import Iterable from .adapters import ( DNS_RECORDS, PROXY_HOSTS, NormalizedObject, Observation, ) from .normalizer import normalize_hostname, normalize_ipv4 def project_proxy_hosts_to_dns( observations: Iterable[Observation], target_ip: str, *, include_disabled: bool = False, ) -> list[Observation]: dns_target = normalize_ipv4(target_ip, "DNS target IP") projected: list[Observation] = [] for observation in observations: proxy_host = observation.object if proxy_host.object_type != PROXY_HOSTS: continue if proxy_host.local_key is None: raise RuntimeError( "Proxy-host observations require an endpoint-local key" ) enabled = proxy_host.payload.get("enabled") if not isinstance(enabled, bool): raise RuntimeError( f"Proxy host {proxy_host.local_key} has invalid " "normalized enabled state" ) if not enabled and not include_disabled: continue domain_names = proxy_host.payload.get("domain_names") if not isinstance(domain_names, (list, tuple)): raise RuntimeError( f"Proxy host {proxy_host.local_key} has invalid " "normalized domain_names" ) for raw_domain in domain_names: hostname, wildcard = normalize_hostname( str(raw_domain), allow_wildcard=True, ) projected.append( Observation( endpoint_id=observation.endpoint_id, object=NormalizedObject( object_type=DNS_RECORDS, logical_key=hostname, local_key=( f"{proxy_host.local_key}:{hostname}" ), payload={ "hostname": hostname, "ip": dns_target, "record_type": "A", "wildcard": wildcard, }, ), observed_at=observation.observed_at, ) ) return sorted( projected, key=lambda item: ( item.endpoint_id, item.object.logical_key, item.object.local_key or "", ), )