This commit is contained in:
2026-01-22 00:36:48 +01:00
parent cb476c0a1a
commit f297a8d740
6 changed files with 86 additions and 160 deletions

View File

@@ -14,6 +14,75 @@ import dns.tsigkeyring
import dns.update
# Valid hostname label pattern (after punycode encoding)
LABEL_PATTERN = re.compile(
r'^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$', re.IGNORECASE
)
class EncodingError(Exception):
"""Raised when hostname encoding fails."""
pass
def encode_dnsname(hostname):
"""
Encode hostname to ASCII using punycode (IDNA).
Args:
hostname: Hostname string, possibly with unicode characters.
Returns:
ASCII-encoded hostname.
Raises:
EncodingError: If hostname is invalid.
"""
hostname = hostname.lower().strip()
if not hostname:
raise EncodingError("Hostname cannot be empty")
# Remove trailing dot if present
if hostname.endswith('.'):
hostname = hostname[:-1]
if len(hostname) > 253:
raise EncodingError("Hostname too long (max 253 characters)")
try:
# Encode each label using IDNA
labels = hostname.split('.')
encoded_labels = []
for label in labels:
if not label:
raise EncodingError("Empty label in hostname")
# Encode to punycode if needed
try:
encoded = label.encode('idna').decode('ascii')
except UnicodeError as e:
raise EncodingError(f"Invalid label '{label}': {e}")
if len(encoded) > 63:
raise EncodingError(
f"Label '{label}' too long (max 63 characters)"
)
if not LABEL_PATTERN.match(encoded):
raise EncodingError(f"Invalid label format: '{label}'")
encoded_labels.append(encoded)
return '.'.join(encoded_labels)
except EncodingError:
raise
except Exception as e:
raise EncodingError(f"Invalid hostname '{hostname}': {e}")
def detect_ip_type(ip):
try:
addr = ipaddress.ip_address(ip)