Return status constants instead of strings

This commit is contained in:
2026-01-23 21:25:33 +01:00
parent faa1e4afd5
commit 8b186d6e95
2 changed files with 42 additions and 16 deletions

View File

@@ -10,6 +10,15 @@ import datetime
__version__ = "1.0.0" __version__ = "1.0.0"
__author__ = "Thomas Oettli <spacefreak@noop.ch>" __author__ = "Thomas Oettli <spacefreak@noop.ch>"
# DynDNS-compatible response statuses
STATUS_GOOD = "good"
STATUS_NOCHG = "nochg"
STATUS_BADAUTH = "badauth"
STATUS_NOHOST = "nohost"
STATUS_DNSERR = "dnserr"
STATUS_ABUSE = "abuse"
STATUS_BADIP = "badip"
__all__ = [ __all__ = [
"app", "app",
"cleanup", "cleanup",
@@ -23,6 +32,13 @@ __all__ = [
"models", "models",
"ratelimit", "ratelimit",
"server", "server",
"STATUS_GOOD",
"STATUS_NOCHG",
"STATUS_BADAUTH",
"STATUS_NOHOST",
"STATUS_DNSERR",
"STATUS_ABUSE",
"STATUS_BADIP",
] ]
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z" DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z"

View File

@@ -14,7 +14,17 @@ from urllib.parse import parse_qs, urlparse
import argon2 import argon2
from . import datetime_str, utc_now from . import (
datetime_str,
utc_now,
STATUS_GOOD,
STATUS_NOCHG,
STATUS_BADAUTH,
STATUS_NOHOST,
STATUS_DNSERR,
STATUS_ABUSE,
STATUS_BADIP,
)
from .cleanup import ExpiredRecordsCleanupThread, RateLimitCleanupThread from .cleanup import ExpiredRecordsCleanupThread, RateLimitCleanupThread
from .logging import clear_txn_id, set_txn_id from .logging import clear_txn_id, set_txn_id
from .models import DoesNotExist, get_hostname_for_user, get_user from .models import DoesNotExist, get_hostname_for_user, get_user
@@ -196,7 +206,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
logging.warning( logging.warning(
f"Rate limited (bad): client={client_ip}, " f"Rate limited (bad): client={client_ip}, "
f"retry_at={datetime_str(retry_at)}") f"retry_at={datetime_str(retry_at)}")
self.respond(429, "abuse") self.respond(429, STATUS_ABUSE)
return return
# Parse URL # Parse URL
@@ -219,7 +229,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
if not username or not password: if not username or not password:
logging.warning(f"Auth failed: client={client_ip} user=anonymous") logging.warning(f"Auth failed: client={client_ip} user=anonymous")
self._handle_bad_request(client_ip, 401, "badauth") self._handle_bad_request(client_ip, 401, STATUS_BADAUTH)
return return
# Validate credentials # Validate credentials
@@ -228,14 +238,14 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
self.app.password_hasher.verify(user.password_hash, password) self.app.password_hasher.verify(user.password_hash, password)
except (DoesNotExist, argon2.exceptions.VerifyMismatchError): except (DoesNotExist, argon2.exceptions.VerifyMismatchError):
logging.warning(f"Auth failed: client={client_ip} user={username}") logging.warning(f"Auth failed: client={client_ip} user={username}")
self._handle_bad_request(client_ip, 401, "badauth") self._handle_bad_request(client_ip, 401, STATUS_BADAUTH)
return return
# Get hostname parameter # Get hostname parameter
hostname_param = extract_param(params, endpoint["params"]["hostname"]) hostname_param = extract_param(params, endpoint["params"]["hostname"])
if not hostname_param: if not hostname_param:
logging.warning(f"Missing hostname: client={client_ip} user={username}") logging.warning(f"Missing hostname: client={client_ip} user={username}")
self._handle_bad_request(client_ip, 400, "nohost") self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
return return
# Validate and encode hostname # Validate and encode hostname
@@ -245,7 +255,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
logging.warning( logging.warning(
f"Invalid hostname: client={client_ip}, " f"Invalid hostname: client={client_ip}, "
f"hostname={hostname_param}") f"hostname={hostname_param}")
self._handle_bad_request(client_ip, 400, "nohost") self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
return return
# Check hostname ownership # Check hostname ownership
@@ -256,7 +266,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
f"Access denied: client={client_ip} user={username} " f"Access denied: client={client_ip} user={username} "
f"hostname={hostname_param}" f"hostname={hostname_param}"
) )
self._handle_bad_request(client_ip, 403, "nohost") self._handle_bad_request(client_ip, 403, STATUS_NOHOST)
return return
# Good rate limit check # Good rate limit check
@@ -266,7 +276,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
logging.warning( logging.warning(
f"Rate limited: client={client_ip}, " f"Rate limited: client={client_ip}, "
f"retry_at={datetime_str(retry_at)}") f"retry_at={datetime_str(retry_at)}")
self.respond(429, "abuse") self.respond(429, STATUS_ABUSE)
return return
# Record good request # Record good request
@@ -305,7 +315,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
else: else:
ipv6 = myip ipv6 = myip
except ValueError: except ValueError:
return (400, "badip") return (400, STATUS_BADIP)
# Process myip6 parameter # Process myip6 parameter
if myip6: if myip6:
@@ -314,9 +324,9 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
if rtype == "AAAA": if rtype == "AAAA":
ipv6 = myip6 ipv6 = myip6
else: else:
return (400, "badip") return (400, STATUS_BADIP)
except ValueError: except ValueError:
return (400, "badip") return (400, STATUS_BADIP)
# Auto-detect from client IP if no params # Auto-detect from client IP if no params
if ipv4 is None and ipv6 is None: if ipv4 is None and ipv6 is None:
@@ -327,7 +337,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
else: else:
ipv6 = ip ipv6 = ip
except ValueError: except ValueError:
return (400, "badip") return (400, STATUS_BADIP)
now = utc_now() now = utc_now()
@@ -352,7 +362,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
f"DNS update failed: client={client_ip} hostname={hostname.hostname} " f"DNS update failed: client={client_ip} hostname={hostname.hostname} "
f"zone={hostname.zone} ipv4={ipv4} error={e}" f"zone={hostname.zone} ipv4={ipv4} error={e}"
) )
return (500, "dnserr") return (500, STATUS_DNSERR)
if ipv6: if ipv6:
hostname.last_ipv6_update = now hostname.last_ipv6_update = now
@@ -373,7 +383,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
f"DNS update failed: client={client_ip} hostname={hostname.hostname} " f"DNS update failed: client={client_ip} hostname={hostname.hostname} "
f"zone={hostname.zone} ipv6={ipv6} error={e}" f"zone={hostname.zone} ipv6={ipv6} error={e}"
) )
return (500, "dnserr") return (500, STATUS_DNSERR)
# Update database # Update database
hostname.save() hostname.save()
@@ -394,7 +404,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
f"zone={hostname.zone}{changed_addrs} notify_change={str(notify_change).lower()}" f"zone={hostname.zone}{changed_addrs} notify_change={str(notify_change).lower()}"
) )
return ( return (
200, "nochg", 200, STATUS_NOCHG,
{"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6} {"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6}
) )
@@ -415,7 +425,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
logging.error(f"Sending change notification error: {e}") logging.error(f"Sending change notification error: {e}")
return ( return (
200, "good", 200, STATUS_GOOD,
{"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6} {"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6}
) )