Add URL parameter to enable notification of IP changes
This commit is contained in:
@@ -166,6 +166,7 @@ Without TSIG authentication, the DNS server must allow updates based on IP addre
|
|||||||
### Endpoints
|
### Endpoints
|
||||||
|
|
||||||
Configure one or more HTTP endpoints. If no endpoints are defined, a default endpoint at `/update` is created with standard parameter names.
|
Configure one or more HTTP endpoints. If no endpoints are defined, a default endpoint at `/update` is created with standard parameter names.
|
||||||
|
Set an empty list of names to disable a parameter.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[[endpoints]]
|
[[endpoints]]
|
||||||
@@ -176,6 +177,7 @@ ipv4 = ["myip", "ipv4", "ip4"]
|
|||||||
ipv6 = ["myip6", "ipv6", "ip6"]
|
ipv6 = ["myip6", "ipv6", "ip6"]
|
||||||
username = ["username", "user"]
|
username = ["username", "user"]
|
||||||
password = ["password", "pass", "token"]
|
password = ["password", "pass", "token"]
|
||||||
|
notify_change = ["notify_change"]
|
||||||
|
|
||||||
[[endpoints]]
|
[[endpoints]]
|
||||||
path = "/nic/update"
|
path = "/nic/update"
|
||||||
@@ -185,6 +187,7 @@ ipv4 = ["myip"]
|
|||||||
ipv6 = ["myip6"]
|
ipv6 = ["myip6"]
|
||||||
username = ["username"]
|
username = ["username"]
|
||||||
password = ["password"]
|
password = ["password"]
|
||||||
|
notify_change = []
|
||||||
```
|
```
|
||||||
|
|
||||||
**Default accepted parameter names** (first match wins):
|
**Default accepted parameter names** (first match wins):
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ from_address = "ddns@example.com" # required if email.enabled
|
|||||||
# ipv6 (IPv6 address): myip6, ipv6, ip6
|
# ipv6 (IPv6 address): myip6, ipv6, ip6
|
||||||
# username: username, user
|
# username: username, user
|
||||||
# password: password, pass, token
|
# password: password, pass, token
|
||||||
|
# notify_change: notify_change
|
||||||
|
#
|
||||||
# Multiple endpoints can be defined with custom parameter names
|
# Multiple endpoints can be defined with custom parameter names
|
||||||
|
|
||||||
# [[endpoints]]
|
# [[endpoints]]
|
||||||
# path = "/update"
|
# path = "/update"
|
||||||
# [endpoints.params]
|
# [endpoints.params]
|
||||||
@@ -74,6 +77,7 @@ from_address = "ddns@example.com" # required if email.enabled
|
|||||||
# ipv6 = ["myip6", "ipv6", "ip6"]
|
# ipv6 = ["myip6", "ipv6", "ip6"]
|
||||||
# username = ["username", "user"]
|
# username = ["username", "user"]
|
||||||
# password = ["password", "pass", "token"]
|
# password = ["password", "pass", "token"]
|
||||||
|
# notify_change = ["notify_change"]
|
||||||
|
|
||||||
# [[endpoints]]
|
# [[endpoints]]
|
||||||
# path = "/nic/update"
|
# path = "/nic/update"
|
||||||
@@ -83,3 +87,4 @@ from_address = "ddns@example.com" # required if email.enabled
|
|||||||
# ipv6 = ["myip6"]
|
# ipv6 = ["myip6"]
|
||||||
# username = ["username"]
|
# username = ["username"]
|
||||||
# password = ["password"]
|
# password = ["password"]
|
||||||
|
# notify_change = []
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ def cmd_hostname_list(args, app):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"{'Hostname':<35} {'User':<15} {'Zone':<20} "
|
f"\n{'Hostname':<35} {'User':<15} {'Zone':<20} "
|
||||||
f"{'DNS-TTL':<8} {'Exp-TTL':<8} {'Last-Update IPv4':<25} {'Last-Update IPv6'}"
|
f"{'DNS-TTL':<8} {'Exp-TTL':<8} {'Last-Update IPv4':<25} {'Last-Update IPv6'}"
|
||||||
)
|
)
|
||||||
print("-" * 140)
|
print("-" * 140)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ DEFAULT_ENDPOINT_PARAMS = {
|
|||||||
"ipv6": ["myip6", "ipv6", "ip6"],
|
"ipv6": ["myip6", "ipv6", "ip6"],
|
||||||
"username": ["username", "user"],
|
"username": ["username", "user"],
|
||||||
"password": ["password", "pass", "token"],
|
"password": ["password", "pass", "token"],
|
||||||
|
"notify_change": ["notify_change"],
|
||||||
}
|
}
|
||||||
|
|
||||||
VALID_PARAM_KEYS = frozenset(DEFAULT_ENDPOINT_PARAMS.keys())
|
VALID_PARAM_KEYS = frozenset(DEFAULT_ENDPOINT_PARAMS.keys())
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class EmailService:
|
|||||||
logging.error(f"Email send failed: to={to} error={e}")
|
logging.error(f"Email send failed: to={to} error={e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def send_changed_notification(
|
def send_change_notification(
|
||||||
self,
|
self,
|
||||||
email,
|
email,
|
||||||
hostname,
|
hostname,
|
||||||
@@ -135,8 +135,8 @@ class EmailService:
|
|||||||
"expiry_ttl": hostname.expiry_ttl,
|
"expiry_ttl": hostname.expiry_ttl,
|
||||||
}
|
}
|
||||||
|
|
||||||
subject = f"DDNS hostname expired: {fqdn}"
|
subject = f"DDNS hostname changed: {fqdn}"
|
||||||
body = self.expiry_template.render(**template_variables)
|
body = self.change_template.render(**template_variables)
|
||||||
|
|
||||||
return self.send(email, subject, body)
|
return self.send(email, subject, body)
|
||||||
|
|
||||||
|
|||||||
@@ -317,7 +317,8 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
now = utc_now()
|
now = utc_now()
|
||||||
|
|
||||||
changed = False
|
ipv4_changed = False
|
||||||
|
ipv6_changed = False
|
||||||
if ipv4:
|
if ipv4:
|
||||||
hostname.last_ipv4_update = now
|
hostname.last_ipv4_update = now
|
||||||
if ipv4 != hostname.last_ipv4:
|
if ipv4 != hostname.last_ipv4:
|
||||||
@@ -330,7 +331,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
|||||||
hostname.dns_ttl
|
hostname.dns_ttl
|
||||||
)
|
)
|
||||||
hostname.last_ipv4 = ipv4
|
hostname.last_ipv4 = ipv4
|
||||||
changed = True
|
ipv4_changed = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
hostname.save()
|
hostname.save()
|
||||||
logging.error(
|
logging.error(
|
||||||
@@ -351,7 +352,7 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
|||||||
hostname.dns_ttl
|
hostname.dns_ttl
|
||||||
)
|
)
|
||||||
hostname.last_ipv6 = ipv6
|
hostname.last_ipv6 = ipv6
|
||||||
changed = True
|
ipv6_changed = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
hostname.save()
|
hostname.save()
|
||||||
logging.error(
|
logging.error(
|
||||||
@@ -364,15 +365,15 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
|||||||
hostname.save()
|
hostname.save()
|
||||||
|
|
||||||
changed_addrs = ""
|
changed_addrs = ""
|
||||||
if ipv4:
|
if ipv4_changed:
|
||||||
changed_addrs += f"ipv4={ipv4}"
|
changed_addrs += f" ipv4={ipv4}"
|
||||||
if ipv6:
|
if ipv6_changed:
|
||||||
changed_addrs += f" ipv6={ipv6}"
|
changed_addrs += f" ipv6={ipv6}"
|
||||||
|
|
||||||
if not changed:
|
if not ipv4_changed and not ipv6_changed:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"No change: client={client_ip} hostname={hostname.hostname} "
|
f"No change: client={client_ip} hostname={hostname.hostname} "
|
||||||
f"zone={hostname.zone} {changed_addrs}"
|
f"zone={hostname.zone}{changed_addrs}"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
200, "nochg",
|
200, "nochg",
|
||||||
@@ -381,8 +382,21 @@ class DDNSRequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Updated: client={client_ip} hostname={hostname.hostname} "
|
f"Updated: client={client_ip} hostname={hostname.hostname} "
|
||||||
f"zone={hostname.zone} {changed_addrs}"
|
f"zone={hostname.zone}{changed_addrs}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
notify_change = extract_param(params, endpoint["params"]["notify_change"])
|
||||||
|
if notify_change and notify_change.lower() not in ["0", "no", "off"]:
|
||||||
|
try:
|
||||||
|
self.app.email_service.send_change_notification(
|
||||||
|
hostname.user.email,
|
||||||
|
hostname,
|
||||||
|
ipv4_changed,
|
||||||
|
ipv6_changed
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Sending change notification error: {e}")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
200, "good",
|
200, "good",
|
||||||
{"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6}
|
{"ipv4": hostname.last_ipv4, "ipv6": hostname.last_ipv6}
|
||||||
|
|||||||
Reference in New Issue
Block a user