Use db model hostname validation in cli and improve exception handling
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argon2
|
||||
import base64
|
||||
import ipaddress
|
||||
import json
|
||||
@@ -9,11 +10,6 @@ import logging
|
||||
import signal
|
||||
import ssl
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
import argon2
|
||||
|
||||
from . import (
|
||||
datetime_str,
|
||||
@@ -27,9 +23,18 @@ from . import (
|
||||
STATUS_BADIP,
|
||||
)
|
||||
from .cleanup import ExpiredRecordsCleanupThread, RateLimitCleanupThread
|
||||
from .dns import detect_ip_type
|
||||
from .logging import clear_txn_id, set_txn_id
|
||||
from .models import DoesNotExist, get_hostname_for_user, get_user
|
||||
from .dns import detect_ip_type, encode_dnsname, EncodingError
|
||||
from .models import (
|
||||
DatabaseError,
|
||||
DoesNotExist,
|
||||
EncodingError,
|
||||
get_hostname_for_user,
|
||||
get_user
|
||||
)
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
|
||||
def extract_param(params, aliases):
|
||||
@@ -123,7 +128,9 @@ class DDNSServer(ThreadingHTTPServer):
|
||||
logging.info(f"Waiting for {self.active_requests} active request(s)")
|
||||
self.requests_done.wait(timeout=timeout)
|
||||
if self.active_requests > 0:
|
||||
logging.warning(f"Shutdown timeout, {self.active_requests} request(s) still active")
|
||||
logging.warning(
|
||||
f"Shutdown timeout, {self.active_requests} request(s) still active"
|
||||
)
|
||||
|
||||
def server_close(self):
|
||||
"""Shutdown thread pool and close server."""
|
||||
@@ -212,6 +219,9 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
||||
set_txn_id()
|
||||
try:
|
||||
self._handle_get_request()
|
||||
except Exception as e:
|
||||
logging.exception(f"Uncaught exception: {e}")
|
||||
self.respond(500, "Internal Server Error")
|
||||
finally:
|
||||
clear_txn_id()
|
||||
|
||||
@@ -259,39 +269,41 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
# Validate credentials
|
||||
try:
|
||||
user = get_user(username)
|
||||
self.app.password_hasher.verify(user.password_hash, password)
|
||||
except (DoesNotExist, argon2.exceptions.VerifyMismatchError):
|
||||
logging.warning(f"Auth failed: client={client_ip} user={username}")
|
||||
self._handle_bad_request(client_ip, 401, STATUS_BADAUTH)
|
||||
return
|
||||
try:
|
||||
user = get_user(username)
|
||||
self.app.password_hasher.verify(user.password_hash, password)
|
||||
except (DoesNotExist, argon2.exceptions.VerifyMismatchError):
|
||||
logging.warning(f"Auth failed: client={client_ip} user={username}")
|
||||
self._handle_bad_request(client_ip, 401, STATUS_BADAUTH)
|
||||
return
|
||||
|
||||
# Get hostname parameter
|
||||
hostname_param = extract_param(params, endpoint["params"]["hostname"])
|
||||
if not hostname_param:
|
||||
logging.warning(f"Missing hostname: client={client_ip} user={username}")
|
||||
self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
|
||||
return
|
||||
# Get hostname parameter
|
||||
hostname_param = extract_param(params, endpoint["params"]["hostname"])
|
||||
if not hostname_param:
|
||||
logging.warning(f"Missing hostname: client={client_ip} user={username}")
|
||||
self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
|
||||
return
|
||||
|
||||
# Validate and encode hostname
|
||||
try:
|
||||
hostname_param = encode_dnsname(hostname_param)
|
||||
except EncodingError:
|
||||
logging.warning(
|
||||
f"Invalid hostname: client={client_ip}, "
|
||||
f"hostname={hostname_param}")
|
||||
self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
|
||||
return
|
||||
# Check hostname ownership
|
||||
try:
|
||||
hostname = get_hostname_for_user(hostname_param, user)
|
||||
except DoesNotExist:
|
||||
logging.warning(
|
||||
f"Access denied: client={client_ip} user={username} "
|
||||
f"hostname={hostname_param}"
|
||||
)
|
||||
self._handle_bad_request(client_ip, 403, STATUS_NOHOST)
|
||||
return
|
||||
except EncodingError:
|
||||
logging.warning(
|
||||
f"Invalid hostname: client={client_ip}, "
|
||||
f"hostname={hostname_param}")
|
||||
self._handle_bad_request(client_ip, 400, STATUS_NOHOST)
|
||||
return
|
||||
|
||||
# Check hostname ownership
|
||||
try:
|
||||
hostname = get_hostname_for_user(hostname_param, user)
|
||||
except DoesNotExist:
|
||||
logging.warning(
|
||||
f"Access denied: client={client_ip} user={username} "
|
||||
f"hostname={hostname_param}"
|
||||
)
|
||||
self._handle_bad_request(client_ip, 403, STATUS_NOHOST)
|
||||
except DatabaseError as e:
|
||||
logging.error(f"Database error: {e}")
|
||||
self.respond(500, "Internal Server Error")
|
||||
return
|
||||
|
||||
# Good rate limit check
|
||||
|
||||
Reference in New Issue
Block a user