46 lines
1.4 KiB
Python
46 lines
1.4 KiB
Python
import ipaddress
|
|
import re
|
|
|
|
def hostname_matches_domain(hostname: str, domain: str) -> bool:
|
|
"""Match a domain apex and any subdomain, without suffix false positives."""
|
|
candidate = hostname.lower().rstrip(".")
|
|
if candidate.startswith("*."):
|
|
candidate = candidate[2:]
|
|
|
|
base = domain.lower().rstrip(".")
|
|
return candidate == base or candidate.endswith("." + base)
|
|
|
|
|
|
_LABEL_RE = re.compile(r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$")
|
|
|
|
|
|
def normalize_hostname(value: str, allow_wildcard: bool = True) -> tuple[str, bool]:
|
|
hostname = value.strip().rstrip(".").lower()
|
|
wildcard = False
|
|
|
|
if allow_wildcard and hostname.startswith("*."):
|
|
wildcard = True
|
|
hostname = hostname[2:]
|
|
|
|
if not hostname or len(hostname) > 253:
|
|
raise RuntimeError(f"Invalid hostname: {value!r}")
|
|
|
|
for label in hostname.split("."):
|
|
if not _LABEL_RE.match(label):
|
|
raise RuntimeError(f"Invalid hostname: {value!r}")
|
|
|
|
normalized = f"*.{hostname}" if wildcard else hostname
|
|
return normalized, wildcard
|
|
|
|
|
|
def normalize_ipv4(value: str, label: str = "IP") -> str:
|
|
try:
|
|
addr = ipaddress.ip_address(value.strip())
|
|
except ValueError as exc:
|
|
raise RuntimeError(f"{label}: {value!r} is not a valid IP address") from exc
|
|
|
|
if addr.version != 4:
|
|
raise RuntimeError(f"{label}: only IPv4 is currently supported")
|
|
|
|
return str(addr)
|