Skip to content

Instantly share code, notes, and snippets.

@H3wastooshort
Last active November 3, 2025 22:39
Show Gist options
  • Select an option

  • Save H3wastooshort/c46277d13a9c9d6b380ee3ce3ca7ecde to your computer and use it in GitHub Desktop.

Select an option

Save H3wastooshort/c46277d13a9c9d6b380ee3ce3ca7ecde to your computer and use it in GitHub Desktop.
script for updating DNS records dynamically via RFC 2136
from json import load
from sys import argv
import subprocess
from time import sleep, monotonic
with open(argv[1],"r") as file:
config=load(file)
def send_nsupdate(ip,domain,keyfile,server,zone,ttl=60):
cmd=f"""server {server}
zone {zone}
update delete {domain} AAAA
send
update add {domain} {ttl} AAAA {ip[1]}
send
update delete {domain} A
send
update add {domain} {ttl} A {ip[0]}
send
quit"""
#print(cmd)
sp = subprocess.run(['nsupdate', '-k', keyfile], input=cmd, text=True)
if sp.returncode != 0:
print("ERROR, return code",sp.returncode)
else:
print(domain,"OK")
def update_ip(conf,ip):
for host in config["hosts"]:
send_nsupdate(ip,host["domain"],host["keyfile"],host["server"],host["zone"],host["ttl"])
def get_ip(conf):
v4=subprocess.check_output(config["ipv4_cmd"]).decode('utf-8').strip()
v6=subprocess.check_output(config["ipv6_cmd"]).decode('utf-8').strip()
return (v4,v6)
prev_ip=(None,None)
last_update_time=0
while True:
try:
ip=get_ip(config)
except subprocess.CalledProcessError:
sleep(int(config["interval"]))
continue
seconds_since_last_update = monotonic() - last_update_time
if ip != prev_ip or seconds_since_last_update > config['max_interval']:
print("Updating to",ip)
update_ip(config,ip)
prev_ip=ip
last_update_time=monotonic()
else:
print("IP has not changed")
print()
sleep(int(config["interval"]))
[Unit]
Description=RFC 2136 Dynamic DNS Update Client
Wants=network-online.target
After=network-online.target nss-lookup.target
[Service]
Type=exec
User=dyndns
ExecStart=python3 /opt/rfc2136_updater.py /etc/rfc2136/conf.json
Restart=on-failure
[Install]
WantedBy=multi-user.target
{
"interval": 10,
"max_interval": 3600,
"ipv4_cmd": ["bash","/opt/fritzbox_ipv4.sh"],
"ipv6_cmd": ["bash","/opt/interface_ipv6.sh"],
"hosts": [
{
"server": "45.129.95.255",
"keyfile": "/etc/rfc2136/glauca-aaaa.private",
"ttl": "120",
"zone": "example.com",
"domain": "aaaa.example.com"
},
{
"server": "45.129.95.255",
"keyfile": "/etc/rfc2136/glauca-bbbb.private",
"ttl": "120",
"zone": "example.com",
"domain": "bbbb.example.com"
}
]
}
#!/bin/bash
#
# Script to fetch IP from fritzbox
#
# Contributed by @Rusk85 in request #45
# Script can be used in the configuration by adding
#
# use=cmd, cmd=/etc/ddclient/get-ip-from-fritzbox
#
# All credits for this one liner go to the author of this blog:
# http://scytale.name/blog/2010/01/fritzbox-wan-ip
# Disclaimer: It might be necessary to make the script executable
# Set default hostname to connect to the FritzBox
: ${FRITZ_BOX_HOSTNAME:=fritz.box}
curl -s -H 'Content-Type: text/xml; charset="utf-8"' \
-H 'SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress' \
-d '<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body> <u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1" /></s:Body></s:Envelope>' \
"http://$FRITZ_BOX_HOSTNAME:49000/igdupnp/control/WANIPConn1" | \
grep -Eo '\<[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){3}\>'
ip -6 addr show scope global | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | grep ^2 | grep IPV6_HOST_ID_HERE | head -n 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment