"""DNS resolution check — pure probe returning a result dict.""" from __future__ import annotations import asyncio import logging import socket logger = logging.getLogger(__name__) async def dns_check(address: str, expected_ip: str | None = None) -> dict: """Resolve `address`. Returns {is_up, resolved_ip, error_msg}. Up = resolution succeeded AND (no expected_ip, or at least one returned record matches expected_ip). resolved_ip is the first A/AAAA record. """ result = await _resolve(address) if not result["resolved"]: return {"is_up": False, "resolved_ip": None, "error_msg": "Resolution failed"} resolved_ip = result["ip"] if expected_ip and expected_ip not in result["all_ips"]: return { "is_up": False, "resolved_ip": resolved_ip, "error_msg": f"Expected {expected_ip} not in {', '.join(result['all_ips'])}", } return {"is_up": True, "resolved_ip": resolved_ip, "error_msg": None} async def _resolve(address: str) -> dict: """Resolve hostname. Returns all A/AAAA records; first is the resolved_ip.""" try: loop = asyncio.get_running_loop() infos = await loop.run_in_executor( None, socket.getaddrinfo, address, None, socket.AF_UNSPEC, socket.SOCK_STREAM ) if infos: all_ips = [info[4][0] for info in infos] return {"resolved": True, "ip": all_ips[0], "all_ips": all_ips} return {"resolved": False, "ip": None, "all_ips": []} except Exception as exc: logger.debug("DNS resolution failed for %s: %s", address, exc) return {"resolved": False, "ip": None, "all_ips": []}