66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""Hostname resolver: zeroconf when available, stdlib multicast fallback.
|
|
|
|
Public API:
|
|
resolve(host, family="v4"|"v6", timeout=1.0, retries=2, use_cache=True)
|
|
-> str | None
|
|
"""
|
|
from . import cache
|
|
from . import _stdlib_resolve as _std
|
|
from ._pick import pick_best
|
|
|
|
|
|
def _try_zeroconf(host: str, family: str, timeout: float) -> tuple[str, int] | None:
|
|
try:
|
|
from zeroconf import Zeroconf, AddressResolver, IPVersion
|
|
except ImportError:
|
|
return None
|
|
hostname = host if host.endswith(".") else host + "."
|
|
ver = IPVersion.V6Only if family == "v6" else IPVersion.V4Only
|
|
zc = Zeroconf(ip_version=IPVersion.All)
|
|
try:
|
|
r = AddressResolver(hostname)
|
|
# request() blocks up to `timeout` milliseconds
|
|
if not r.request(zc, int(timeout * 1000)):
|
|
return None
|
|
addrs = r.parsed_addresses(ver)
|
|
best = pick_best(addrs, family=family)
|
|
if not best:
|
|
return None
|
|
return best, 120
|
|
except Exception:
|
|
return None
|
|
finally:
|
|
try:
|
|
zc.close()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def resolve(host: str, family: str = "v4", timeout: float = 1.0,
|
|
retries: int = 2, use_cache: bool = True) -> str | None:
|
|
hostname = host if host.endswith(".local") else f"{host}.local"
|
|
if use_cache:
|
|
hit = cache.get(hostname, family)
|
|
if hit:
|
|
return hit
|
|
per_try = max(0.3, timeout / max(1, retries))
|
|
|
|
# IPv4: try stdlib first (fast, no zeroconf startup cost).
|
|
if family == "v4":
|
|
for _ in range(max(1, retries)):
|
|
recs = _std.query_all(hostname, per_try)
|
|
if recs:
|
|
best = pick_best([ip for ip, _ in recs], family="v4")
|
|
ttl = next((t for ip, t in recs if ip == best), 120)
|
|
cache.put(hostname, best, family=family, ttl=ttl or 120)
|
|
return best
|
|
|
|
# IPv6 or IPv4-stdlib-miss: try zeroconf (heavier but RFC-complete).
|
|
for _ in range(max(1, retries)):
|
|
res = _try_zeroconf(hostname, family, per_try)
|
|
if res:
|
|
ip, ttl = res
|
|
cache.put(hostname, ip, family=family, ttl=ttl)
|
|
return ip
|
|
return None
|