From 97459518fad82b19059d7e5cd796ead961c38db6 Mon Sep 17 00:00:00 2001 From: "Jason D. McCormick" Date: Sun, 11 Dec 2022 13:20:54 -0500 Subject: go back to the original org --- ddns-update-rfc2136 | 147 ++++++++++++++++++++++++++++++++++++++++ he-dns/he-dyndns | 162 -------------------------------------------- he-dns/he-dyndns-bash | 91 ------------------------- he-dns/he-dyndns.conf | 4 -- he-dyndns | 162 ++++++++++++++++++++++++++++++++++++++++++++ he-dyndns-bash | 91 +++++++++++++++++++++++++ he-dyndns.conf | 4 ++ rfc2136/ddns-update-rfc2136 | 147 ---------------------------------------- ros-ddns-addrlist | 3 + routeros/ros-ddns-addrlist | 3 - 10 files changed, 407 insertions(+), 407 deletions(-) create mode 100755 ddns-update-rfc2136 delete mode 100755 he-dns/he-dyndns delete mode 100755 he-dns/he-dyndns-bash delete mode 100644 he-dns/he-dyndns.conf create mode 100755 he-dyndns create mode 100755 he-dyndns-bash create mode 100644 he-dyndns.conf delete mode 100755 rfc2136/ddns-update-rfc2136 create mode 100644 ros-ddns-addrlist delete mode 100644 routeros/ros-ddns-addrlist diff --git a/ddns-update-rfc2136 b/ddns-update-rfc2136 new file mode 100755 index 0000000..6bd07ce --- /dev/null +++ b/ddns-update-rfc2136 @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 + +import argparse +import ipaddress +import netifaces +import logging +import logging.handlers +import os.path +import socket +import subprocess +import sys + + +def build_ip(ip_string): + try: + ip = ipaddress.ip_address(ip_string) + except ValueError: + log.error("manually specified address is invalid format: %s" % ip_string) + sys.exit(1) + except: + log.error(str(e)) + sys.exit(1) + + return ip + +def get_interface_addrs(iface, addrtype): + try: + iaddrs = netifaces.ifaddresses(iface) + i = iaddrs[addrtype] + log.debug("interface {} addrs of type {}: {}".format(iface,addrtype,i)) + except ValueError as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error("No interface named {} found".format(iface)) + sys.exit(1) + except KeyError as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error("No address of type {} found on interface {}".format(addrtype, iface)) + sys.exit(1) + except Exception as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error(str(e)) + sys.exit(1) + + return i + +def build_nsupdate_cmd(server, zone, record, rrtype, ttl, addr): + log.debug("BNC: server: {}".format(server)) + log.debug("BNC: zone: {}".format(zone)) + log.debug("BNC: record: {}".format(record)) + log.debug("BNC: rrtype: {}".format(rrtype)) + log.debug("BNC: ttl: {}".format(ttl)) + log.debug("BNC: addr: {}".format(addr)) + + cmdstr = "server {}\n".format(server) + cmdstr += "zone {}\n".format(zone) + cmdstr += "update delete {} {}\n".format(record, rrtype) + cmdstr += "update add {} {} {} {}\n".format(record, ttl, rrtype, addr) + cmdstr += "send\n" + cmdstr += "quit\n" + + return cmdstr + +def main(): + ap = argparse.ArgumentParser(description="Update dynamic DNS records using RFC2136") + ap.add_argument("interface", help="interface to obtain IP from (for IPv6, takes the numerically first global address on the interface)") + ap.add_argument("record", help="DNS record to update") + ap.add_argument("zone", help="Zone name to update (e.g. example.com)") + ap.add_argument("server", help="Server to update (IP or FQDN)") + ap.add_argument("--v4", help="Update IPv4 A record", action="store_true", default=False) + ap.add_argument("--v6", help="Update IPv6 AAAA record (default)", action="store_true", default=True) + ap.add_argument("--addr4", help="Update A record with provided IP rather than detected IP (causes 'interface' to be ignored)") + ap.add_argument("--addr6", help="Update AAAA record with provided IP rather than detected IP (causes 'interface' to be ignored)") + ap.add_argument("--ttl", help="TTL to assign to record (default 300)", default=300) + ap.add_argument("--keyfile", help="Alternate location for key config file (default /etc/ddns-RECORD.key where RECORD is the record name provided as the argument)") + ap.add_argument("--debug", help="Enable debug logging", action="store_true", default=False) + args = ap.parse_args() + + if args.debug: + log.setLevel(logging.DEBUG) + else: + log.setLevel(logging.ERROR) + + if args.keyfile: + keyfile = args.keyfile + else: + keyfile = "/etc/ddns-{}.key".format(args.record) + log.debug("KEYFILE: {}".format(keyfile)) + + if args.v4: + addrtype = netifaces.AF_INET + else: + addrtype = netifaces.AF_INET6 + + ip = False + if args.addr4: + ip = build_ip(args.addr4) + + if args.addr6: + ip = build_ip(args.addr6) + + try: + if not os.path.exists(keyfile): + raise Exception("missing key file {}".format(keyfile)) + + if ip: + addr = ip + log.debug("Manual IP: {}".format(addr)) + else: + addrarr = get_interface_addrs(args.interface, addrtype) + addr = addrarr.pop(0)['addr'] + log.debug("Found IP: {}".format(addr)) + + rrtype = "AAAA" + if args.v4: + rrtype = "A" + + nsupdatecmd = build_nsupdate_cmd(args.server, args.zone, args.record, rrtype, args.ttl, addr) + + nsupdate = subprocess.run( + "nsupdate -k {}".format(keyfile), + shell=True, + check=True, + input = bytes(nsupdatecmd,'utf-8'), + capture_output = True + ) + + log.debug("nsupdate return code: {}".format(nsupdate.returncode)) + + except subprocess.CalledProcessError as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error("Error executing: " + e.cmd) + log.error("nsupdate stderr: " + e.stderr.decode("utf-8").strip()) + sys.exit(1) + + except Exception as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error(str(e)) + sys.exit(1) + +if __name__ == "__main__": + log = logging.getLogger("ddns-update-rfc2136") + lh = logging.StreamHandler() + lf = logging.Formatter(fmt='%(name)s: %(levelname)s: %(message)s') + lh.setFormatter(lf) + log.addHandler(lh) + main() + sys.exit(0) diff --git a/he-dns/he-dyndns b/he-dns/he-dyndns deleted file mode 100755 index bec3b60..0000000 --- a/he-dns/he-dyndns +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import base64 -import configparser -import dns.resolver -import ipaddress -import logging -import logging.handlers -import os.path -import socket -import sys -import urllib - - -HE_DNS_API_HOST = "dyn.dns.he.net" - -def build_ip(ip_string): - try: - ip = ipaddress.ip_address(ip_string) - except ValueError: - log.error("manually specified address is invalid format: %s" % ip_string) - sys.exit(1) - except: - log.error(str(e)) - sys.exit(1) - - return ip - -def haveIPv6(): - haveIPv6 = True - try: - s = socket.socket(socket.AF_INET6) - s.connect((HE_DNS_API_HOST,80)) - except socket.gaierror as e: - log.debug("Exception {}".format(e.__class__)) - log.error("Testing IPv6 connection: " + str(e)) - sys.exit(1) - except OSError as e: - log.debug("Exception {}".format(e.__class__)) - log.debug(str(e)) - haveIPv6 = False - except TimeoutError as e: - log.error("Testing IPv6 connection: " + str(e)) - sys.exit(1) - except Exception as e: - log.debug("Exception {}".format(e.__class__)) - log.error(str(e)) - haveIPv6 = False - return haveIPv6 - -def getKey(keyfile, record): - if not os.path.exists(keyfile): - raise Exception("missing keyfile {}".format(keyfile)) - - config = configparser.ConfigParser() - config.read(keyfile) - - if not "keys" in config: - raise Exception("keyfile lacks [keys] section") - - if not record in config["keys"]: - raise Exception("keyfile has no entry for {}".format(record)) - - return config["keys"][record] - -def main(): - ap = argparse.ArgumentParser(description="Update Hurricane Electric DNS dynamic record") - ap.add_argument("record", help="DNS record to update") - ap.add_argument("--v4", help="Update IPv4 A record (default)", action="store_true", default=True) - ap.add_argument("--v6", help="Update IPv6 AAAA record", action="store_true", default=False) - ap.add_argument("--addr4", help="Update A record with provided IP rather than detected IP") - ap.add_argument("--addr6", help="Update AAAA record with provided IP rather than detected IP") - ap.add_argument("--keyfile", help="Alternate location for key config file (default /etc/he-dns-secret.conf)") - ap.add_argument("--key", help="HE DDNS key for record (by default read from /etc/he-dns-secret.conf or --keyfile)") - ap.add_argument("--debug", help="Enable debug logging", action="store_true", default=False) - args = ap.parse_args() - - if args.debug: - log.setLevel(logging.DEBUG) - else: - log.setLevel(logging.ERROR) - - if args.keyfile: - log.debug("KEYFILE: {}".format(args.keyfile)) - keyfile = args.keyfile - else: - log.debug("KEYFILE: /etc/he-dyndns.conf") - keyfile = "/etc/he-dyndns.conf" - - if args.addr4: - ip = build_ip(args.addr4) - - if args.addr6: - ip = build_ip(args.addr6) - - if args.v6: - if not haveIPv6 and not args.addr6: - log.error("no IPv6 detected and --v6 specified without --addr6 (try --debug?)") - sys.exit(1) - - try: - - if args.key: - log.debug("--key override; ignore --kefile") - apikey = args.key - else: - apikey = getKey(keyfile, args.record) - - if args.v6: - ans = dns.resolver.resolve(HE_DNS_API_HOST, "AAAA")[0] - log.debug("DNS resolved: {}".format(ans.to_text())) - apihost = "[{}]".format(ans.to_text()) - else: - ans = dns.resolver.resolve(HE_DNS_API_HOST, "A")[0] - log.debug("DNS resolved: {}".format(ans.to_text())) - apihost = ans.to_text() - - # build the API URL to be called - url = "http://{}/nic/update?hostname={}".format(apihost, args.record) - if args.addr4 or args.addr6: - url += "&myip={}".format(ip) - log.debug("API request URL: " + url) - - # make the HTTP request - authstr = base64.b64encode(bytes("%s:%s" % ( args.record, apikey ), "ascii")) - log.debug("auth string: %s" % authstr.decode("utf-8")) - req = urllib.request.Request(url) - req.add_header("Host", HE_DNS_API_HOST) - req.add_header("Authorization", "Basic %s" % authstr.decode("utf-8")) - with urllib.request.urlopen(req) as response: - this_page = response.read() - log.debug("HE response: " + this_page.decode("utf-8")) - res = this_page.decode("utf-8").split()[0] - - if res == "good": - return True - elif res == "badauth": - raise Exception("HE badauth: wrong key or record not configured for dynamic updates") - elif res == "nochg": - return True - else: - raise Exception("HE returned {}: unknown response code".format(res)) - - except dns.resolver.NXDOMAIN as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error(str(e)) - sys.exit(1) - - except Exception as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error(str(e)) - sys.exit(1) - -if __name__ == "__main__": - log = logging.getLogger("he-dyndns") - lh = logging.StreamHandler() - lf = logging.Formatter(fmt='%(name)s: %(levelname)s: %(message)s') - lh.setFormatter(lf) - log.addHandler(lh) - main() - sys.exit(0) diff --git a/he-dns/he-dyndns-bash b/he-dns/he-dyndns-bash deleted file mode 100755 index 0ce3730..0000000 --- a/he-dns/he-dyndns-bash +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# -# Copyright (C)2019 by Jason D. McCormick -# Licensed under the Apache License 2.0. See LICENSE included -# with the distribution bundle that contained this file. -# - -PATH="/usr/bin:/bin:/usr/sbin:/sbin" - -## KNOWLEDGE IS GOOD ## -usage() { echo "Usage: $0 [-s SECRET] -d HOSTNAME[,HOSTNAME,...]" 1>&2; exit 1; } - -## DO THE UPDATE FUNCTION ONCE FOR HAPPY EDITING ## -doUpdate() { - - if [ ! -z $2 ]; then - myip="&myip=$2" - fi - - /usr/bin/curl -s$1 "https://$FQDN:$CODE@$DNSSITE/nic/update?hostname=$FQDN$myip" > $tfile - if ! egrep -q '(good|nochg)' $tfile; then - echo -n "v$1 change: " - cat $tfile - echo - retval=1 - fi -} - -## MAIN ## -while getopts "46a:A:hd:s:" arg; do - case "${arg}" in - 4) - DO4=Y - ;; - 6) - DO6=Y - ;; - a) - ADDR4=${OPTARG} - ;; - A) - ADDR6=${OPTARG} - ;; - d) - FQDNS=${OPTARG} - ;; - s) - CODE=${OPTARG} - ;; - *) - usage - ;; - esac -done - -if [ -z "${FQDNS}" ]; then - usage -fi - -if [ ! -f /etc/he-dns-secret ] && [ -z "${CODE}" ]; then - usage -fi - -if [ -z "${CODE}" ]; then - . /etc/he-dns-secret - - if [ -z "${CODE}" ]; then - echo "/etc/he-dns-secret does not contain a CODE= variable" - exit 1 - fi -fi - -DNSSITE="dyn.dns.he.net"; -retval=0 -tfile=$(/bin/mktemp) - -for FQDN in $(echo $FQDNS | tr ',' ' ') -do - if [ ! -z ${DO4} ]; then - doUpdate 4 ${ADDR4} - fi - - if [ ! -z ${DO6} ]; then - doUpdate 6 ${ADDR6} - fi -done - -rm $tfile - -exit $retval - diff --git a/he-dns/he-dyndns.conf b/he-dns/he-dyndns.conf deleted file mode 100644 index 26b387b..0000000 --- a/he-dns/he-dyndns.conf +++ /dev/null @@ -1,4 +0,0 @@ -# List API keys one key per domain record in the format -# record = key -[keys] -dyntest.mfamily.org = yEuFfO74TskIX2NG0Yod diff --git a/he-dyndns b/he-dyndns new file mode 100755 index 0000000..bec3b60 --- /dev/null +++ b/he-dyndns @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +import argparse +import base64 +import configparser +import dns.resolver +import ipaddress +import logging +import logging.handlers +import os.path +import socket +import sys +import urllib + + +HE_DNS_API_HOST = "dyn.dns.he.net" + +def build_ip(ip_string): + try: + ip = ipaddress.ip_address(ip_string) + except ValueError: + log.error("manually specified address is invalid format: %s" % ip_string) + sys.exit(1) + except: + log.error(str(e)) + sys.exit(1) + + return ip + +def haveIPv6(): + haveIPv6 = True + try: + s = socket.socket(socket.AF_INET6) + s.connect((HE_DNS_API_HOST,80)) + except socket.gaierror as e: + log.debug("Exception {}".format(e.__class__)) + log.error("Testing IPv6 connection: " + str(e)) + sys.exit(1) + except OSError as e: + log.debug("Exception {}".format(e.__class__)) + log.debug(str(e)) + haveIPv6 = False + except TimeoutError as e: + log.error("Testing IPv6 connection: " + str(e)) + sys.exit(1) + except Exception as e: + log.debug("Exception {}".format(e.__class__)) + log.error(str(e)) + haveIPv6 = False + return haveIPv6 + +def getKey(keyfile, record): + if not os.path.exists(keyfile): + raise Exception("missing keyfile {}".format(keyfile)) + + config = configparser.ConfigParser() + config.read(keyfile) + + if not "keys" in config: + raise Exception("keyfile lacks [keys] section") + + if not record in config["keys"]: + raise Exception("keyfile has no entry for {}".format(record)) + + return config["keys"][record] + +def main(): + ap = argparse.ArgumentParser(description="Update Hurricane Electric DNS dynamic record") + ap.add_argument("record", help="DNS record to update") + ap.add_argument("--v4", help="Update IPv4 A record (default)", action="store_true", default=True) + ap.add_argument("--v6", help="Update IPv6 AAAA record", action="store_true", default=False) + ap.add_argument("--addr4", help="Update A record with provided IP rather than detected IP") + ap.add_argument("--addr6", help="Update AAAA record with provided IP rather than detected IP") + ap.add_argument("--keyfile", help="Alternate location for key config file (default /etc/he-dns-secret.conf)") + ap.add_argument("--key", help="HE DDNS key for record (by default read from /etc/he-dns-secret.conf or --keyfile)") + ap.add_argument("--debug", help="Enable debug logging", action="store_true", default=False) + args = ap.parse_args() + + if args.debug: + log.setLevel(logging.DEBUG) + else: + log.setLevel(logging.ERROR) + + if args.keyfile: + log.debug("KEYFILE: {}".format(args.keyfile)) + keyfile = args.keyfile + else: + log.debug("KEYFILE: /etc/he-dyndns.conf") + keyfile = "/etc/he-dyndns.conf" + + if args.addr4: + ip = build_ip(args.addr4) + + if args.addr6: + ip = build_ip(args.addr6) + + if args.v6: + if not haveIPv6 and not args.addr6: + log.error("no IPv6 detected and --v6 specified without --addr6 (try --debug?)") + sys.exit(1) + + try: + + if args.key: + log.debug("--key override; ignore --kefile") + apikey = args.key + else: + apikey = getKey(keyfile, args.record) + + if args.v6: + ans = dns.resolver.resolve(HE_DNS_API_HOST, "AAAA")[0] + log.debug("DNS resolved: {}".format(ans.to_text())) + apihost = "[{}]".format(ans.to_text()) + else: + ans = dns.resolver.resolve(HE_DNS_API_HOST, "A")[0] + log.debug("DNS resolved: {}".format(ans.to_text())) + apihost = ans.to_text() + + # build the API URL to be called + url = "http://{}/nic/update?hostname={}".format(apihost, args.record) + if args.addr4 or args.addr6: + url += "&myip={}".format(ip) + log.debug("API request URL: " + url) + + # make the HTTP request + authstr = base64.b64encode(bytes("%s:%s" % ( args.record, apikey ), "ascii")) + log.debug("auth string: %s" % authstr.decode("utf-8")) + req = urllib.request.Request(url) + req.add_header("Host", HE_DNS_API_HOST) + req.add_header("Authorization", "Basic %s" % authstr.decode("utf-8")) + with urllib.request.urlopen(req) as response: + this_page = response.read() + log.debug("HE response: " + this_page.decode("utf-8")) + res = this_page.decode("utf-8").split()[0] + + if res == "good": + return True + elif res == "badauth": + raise Exception("HE badauth: wrong key or record not configured for dynamic updates") + elif res == "nochg": + return True + else: + raise Exception("HE returned {}: unknown response code".format(res)) + + except dns.resolver.NXDOMAIN as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error(str(e)) + sys.exit(1) + + except Exception as e: + log.debug("Exception class: {}".format(e.__class__)) + log.error(str(e)) + sys.exit(1) + +if __name__ == "__main__": + log = logging.getLogger("he-dyndns") + lh = logging.StreamHandler() + lf = logging.Formatter(fmt='%(name)s: %(levelname)s: %(message)s') + lh.setFormatter(lf) + log.addHandler(lh) + main() + sys.exit(0) diff --git a/he-dyndns-bash b/he-dyndns-bash new file mode 100755 index 0000000..0ce3730 --- /dev/null +++ b/he-dyndns-bash @@ -0,0 +1,91 @@ +#!/bin/bash +# +# Copyright (C)2019 by Jason D. McCormick +# Licensed under the Apache License 2.0. See LICENSE included +# with the distribution bundle that contained this file. +# + +PATH="/usr/bin:/bin:/usr/sbin:/sbin" + +## KNOWLEDGE IS GOOD ## +usage() { echo "Usage: $0 [-s SECRET] -d HOSTNAME[,HOSTNAME,...]" 1>&2; exit 1; } + +## DO THE UPDATE FUNCTION ONCE FOR HAPPY EDITING ## +doUpdate() { + + if [ ! -z $2 ]; then + myip="&myip=$2" + fi + + /usr/bin/curl -s$1 "https://$FQDN:$CODE@$DNSSITE/nic/update?hostname=$FQDN$myip" > $tfile + if ! egrep -q '(good|nochg)' $tfile; then + echo -n "v$1 change: " + cat $tfile + echo + retval=1 + fi +} + +## MAIN ## +while getopts "46a:A:hd:s:" arg; do + case "${arg}" in + 4) + DO4=Y + ;; + 6) + DO6=Y + ;; + a) + ADDR4=${OPTARG} + ;; + A) + ADDR6=${OPTARG} + ;; + d) + FQDNS=${OPTARG} + ;; + s) + CODE=${OPTARG} + ;; + *) + usage + ;; + esac +done + +if [ -z "${FQDNS}" ]; then + usage +fi + +if [ ! -f /etc/he-dns-secret ] && [ -z "${CODE}" ]; then + usage +fi + +if [ -z "${CODE}" ]; then + . /etc/he-dns-secret + + if [ -z "${CODE}" ]; then + echo "/etc/he-dns-secret does not contain a CODE= variable" + exit 1 + fi +fi + +DNSSITE="dyn.dns.he.net"; +retval=0 +tfile=$(/bin/mktemp) + +for FQDN in $(echo $FQDNS | tr ',' ' ') +do + if [ ! -z ${DO4} ]; then + doUpdate 4 ${ADDR4} + fi + + if [ ! -z ${DO6} ]; then + doUpdate 6 ${ADDR6} + fi +done + +rm $tfile + +exit $retval + diff --git a/he-dyndns.conf b/he-dyndns.conf new file mode 100644 index 0000000..26b387b --- /dev/null +++ b/he-dyndns.conf @@ -0,0 +1,4 @@ +# List API keys one key per domain record in the format +# record = key +[keys] +dyntest.mfamily.org = yEuFfO74TskIX2NG0Yod diff --git a/rfc2136/ddns-update-rfc2136 b/rfc2136/ddns-update-rfc2136 deleted file mode 100755 index 6bd07ce..0000000 --- a/rfc2136/ddns-update-rfc2136 +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import ipaddress -import netifaces -import logging -import logging.handlers -import os.path -import socket -import subprocess -import sys - - -def build_ip(ip_string): - try: - ip = ipaddress.ip_address(ip_string) - except ValueError: - log.error("manually specified address is invalid format: %s" % ip_string) - sys.exit(1) - except: - log.error(str(e)) - sys.exit(1) - - return ip - -def get_interface_addrs(iface, addrtype): - try: - iaddrs = netifaces.ifaddresses(iface) - i = iaddrs[addrtype] - log.debug("interface {} addrs of type {}: {}".format(iface,addrtype,i)) - except ValueError as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error("No interface named {} found".format(iface)) - sys.exit(1) - except KeyError as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error("No address of type {} found on interface {}".format(addrtype, iface)) - sys.exit(1) - except Exception as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error(str(e)) - sys.exit(1) - - return i - -def build_nsupdate_cmd(server, zone, record, rrtype, ttl, addr): - log.debug("BNC: server: {}".format(server)) - log.debug("BNC: zone: {}".format(zone)) - log.debug("BNC: record: {}".format(record)) - log.debug("BNC: rrtype: {}".format(rrtype)) - log.debug("BNC: ttl: {}".format(ttl)) - log.debug("BNC: addr: {}".format(addr)) - - cmdstr = "server {}\n".format(server) - cmdstr += "zone {}\n".format(zone) - cmdstr += "update delete {} {}\n".format(record, rrtype) - cmdstr += "update add {} {} {} {}\n".format(record, ttl, rrtype, addr) - cmdstr += "send\n" - cmdstr += "quit\n" - - return cmdstr - -def main(): - ap = argparse.ArgumentParser(description="Update dynamic DNS records using RFC2136") - ap.add_argument("interface", help="interface to obtain IP from (for IPv6, takes the numerically first global address on the interface)") - ap.add_argument("record", help="DNS record to update") - ap.add_argument("zone", help="Zone name to update (e.g. example.com)") - ap.add_argument("server", help="Server to update (IP or FQDN)") - ap.add_argument("--v4", help="Update IPv4 A record", action="store_true", default=False) - ap.add_argument("--v6", help="Update IPv6 AAAA record (default)", action="store_true", default=True) - ap.add_argument("--addr4", help="Update A record with provided IP rather than detected IP (causes 'interface' to be ignored)") - ap.add_argument("--addr6", help="Update AAAA record with provided IP rather than detected IP (causes 'interface' to be ignored)") - ap.add_argument("--ttl", help="TTL to assign to record (default 300)", default=300) - ap.add_argument("--keyfile", help="Alternate location for key config file (default /etc/ddns-RECORD.key where RECORD is the record name provided as the argument)") - ap.add_argument("--debug", help="Enable debug logging", action="store_true", default=False) - args = ap.parse_args() - - if args.debug: - log.setLevel(logging.DEBUG) - else: - log.setLevel(logging.ERROR) - - if args.keyfile: - keyfile = args.keyfile - else: - keyfile = "/etc/ddns-{}.key".format(args.record) - log.debug("KEYFILE: {}".format(keyfile)) - - if args.v4: - addrtype = netifaces.AF_INET - else: - addrtype = netifaces.AF_INET6 - - ip = False - if args.addr4: - ip = build_ip(args.addr4) - - if args.addr6: - ip = build_ip(args.addr6) - - try: - if not os.path.exists(keyfile): - raise Exception("missing key file {}".format(keyfile)) - - if ip: - addr = ip - log.debug("Manual IP: {}".format(addr)) - else: - addrarr = get_interface_addrs(args.interface, addrtype) - addr = addrarr.pop(0)['addr'] - log.debug("Found IP: {}".format(addr)) - - rrtype = "AAAA" - if args.v4: - rrtype = "A" - - nsupdatecmd = build_nsupdate_cmd(args.server, args.zone, args.record, rrtype, args.ttl, addr) - - nsupdate = subprocess.run( - "nsupdate -k {}".format(keyfile), - shell=True, - check=True, - input = bytes(nsupdatecmd,'utf-8'), - capture_output = True - ) - - log.debug("nsupdate return code: {}".format(nsupdate.returncode)) - - except subprocess.CalledProcessError as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error("Error executing: " + e.cmd) - log.error("nsupdate stderr: " + e.stderr.decode("utf-8").strip()) - sys.exit(1) - - except Exception as e: - log.debug("Exception class: {}".format(e.__class__)) - log.error(str(e)) - sys.exit(1) - -if __name__ == "__main__": - log = logging.getLogger("ddns-update-rfc2136") - lh = logging.StreamHandler() - lf = logging.Formatter(fmt='%(name)s: %(levelname)s: %(message)s') - lh.setFormatter(lf) - log.addHandler(lh) - main() - sys.exit(0) diff --git a/ros-ddns-addrlist b/ros-ddns-addrlist new file mode 100644 index 0000000..dfa3fec --- /dev/null +++ b/ros-ddns-addrlist @@ -0,0 +1,3 @@ +:local newip [ :resolve ros-copper.mfamily.org ] +:local roscopper [/ipv6 firewall address-list find comment="ros-copper"] +/ipv6 firewall address set numbers=$roscopper address=$newip diff --git a/routeros/ros-ddns-addrlist b/routeros/ros-ddns-addrlist deleted file mode 100644 index dfa3fec..0000000 --- a/routeros/ros-ddns-addrlist +++ /dev/null @@ -1,3 +0,0 @@ -:local newip [ :resolve ros-copper.mfamily.org ] -:local roscopper [/ipv6 firewall address-list find comment="ros-copper"] -/ipv6 firewall address set numbers=$roscopper address=$newip -- cgit v1.2.3