131 lines
3.0 KiB
Python
131 lines
3.0 KiB
Python
"""
|
|
DDNS Service - Dynamic DNS update service.
|
|
|
|
A daemon that accepts HTTP(S) requests to dynamically update DNS entries.
|
|
Includes CLI administration tools for user and hostname management.
|
|
"""
|
|
|
|
import datetime
|
|
|
|
__version__ = "1.0.1"
|
|
__author__ = "Thomas Oettli <spacefreak@noop.ch>"
|
|
|
|
__all__ = [
|
|
"app",
|
|
"cleanup",
|
|
"cli",
|
|
"config",
|
|
"datetime_aware_utc",
|
|
"datetime_naive_utc",
|
|
"datetime_str",
|
|
"dns",
|
|
"email",
|
|
"logging",
|
|
"main",
|
|
"models",
|
|
"now_utc",
|
|
"ratelimit",
|
|
"server",
|
|
"STATUS_GOOD",
|
|
"STATUS_NOCHG",
|
|
"STATUS_BADAUTH",
|
|
"STATUS_NOHOST",
|
|
"STATUS_DNSERR",
|
|
"STATUS_ABUSE",
|
|
"STATUS_BADIP",
|
|
]
|
|
|
|
# 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"
|
|
|
|
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z"
|
|
|
|
# Datetime convention:
|
|
# All datetime objects in this codebase are timezone-aware.
|
|
# - now_utc(): returns timezone-aware UTC datetime
|
|
# - datetime_str(): converts naive UTC (adds tzinfo for formatting)
|
|
# or timezone-aware datetime to display string
|
|
# - Database stores/returns naive datetimes (always UTC by convention)
|
|
# - Database models automatically convert between naive/timezone-aware datetimes
|
|
|
|
|
|
def now_utc():
|
|
"""
|
|
Get current date and time in UTC.
|
|
|
|
Returns:
|
|
Timezone-aware datetime object in UTC.
|
|
"""
|
|
return datetime.datetime.now(datetime.UTC)
|
|
|
|
|
|
def datetime_naive_utc(dt):
|
|
"""
|
|
Convert datetime to naive UTC datetime.
|
|
|
|
Args:
|
|
dt: Datetime object (naive UTC or timezone-aware or None).
|
|
|
|
Returns:
|
|
Naive datetime object in UTC or None if dt is not a datetime.
|
|
"""
|
|
if not isinstance(dt, datetime.datetime):
|
|
return None
|
|
|
|
if not dt.tzinfo:
|
|
return dt
|
|
|
|
return dt.astimezone(datetime.UTC).replace(tzinfo=None)
|
|
|
|
|
|
def datetime_aware_utc(dt):
|
|
"""
|
|
Convert datetime to UTC datetime.
|
|
|
|
Args:
|
|
dt: Datetime object (naive UTC or timezone-aware or None).
|
|
|
|
Returns:
|
|
Timzone-aware datetime object in UTC or None if dt is not a datetime.
|
|
"""
|
|
if not isinstance(dt, datetime.datetime):
|
|
return None
|
|
|
|
if not dt.tzinfo:
|
|
return dt.replace(tzinfo=datetime.UTC)
|
|
|
|
if dt.tzinfo == datetime.UTC:
|
|
return dt
|
|
|
|
return dt.astimezone(datetime.UTC)
|
|
|
|
|
|
def datetime_str(dt, utc=False):
|
|
"""
|
|
Convert datetime to display string.
|
|
|
|
Assumes naive datetimes are UTC per codebase convention.
|
|
|
|
Args:
|
|
dt: Datetime object (naive UTC or timezone-aware).
|
|
utc: If True, display in UTC; otherwise convert to local timezone.
|
|
|
|
Returns:
|
|
Formatted datetime string, or "Never" if dt is not a datetime.
|
|
"""
|
|
if not isinstance(dt, datetime.datetime):
|
|
return "Never"
|
|
|
|
aware_dt = dt.replace(tzinfo=datetime.UTC) if not dt.tzinfo else dt
|
|
|
|
if utc:
|
|
return aware_dt.strftime(DATETIME_FORMAT)
|
|
else:
|
|
return aware_dt.astimezone().strftime(DATETIME_FORMAT)
|