-#!/bin/bash
+#!/usr/bin/python
+import sys
+import re
+import argparse
+from subprocess import Popen, PIPE
+import ipaddr
-hostname=$1
-myip=$2
-type=$3 # A or AAAA
-ttl=600
-domain=dyn.colgarra.priv.at
-echo -e "update delete $hostname.$domain $type\nupdate add $hostname.$domain $ttl IN $type $myip\n" | nsupdate -l
+class NsupdateError(Exception):
+ def __init__(self, returncode):
+ self.returncode = returncode
+
+
+def ipfamily_by_ip(ip):
+ if isinstance(ip, ipaddr.IPv4Address):
+ return 'A'
+ elif isinstance(ip, ipaddr.IPv6Address):
+ return 'AAAA'
+ assert False
+
+
+def nsupdate_add(fqdn, ttl, ip):
+ """
+ :param fqdn: Fully qualified domain name
+ :param ip_family: A or AAAA
+ :raises an NsupdateError in case of errors."""
+ command = "update add {fqdn} {ttl} IN {ip_family} {ip}\n\n".format(fqdn=fqdn, ttl=ttl, ip_family=ipfamily_by_ip(ip), ip=ip)
+ p = Popen(['nsupdate', '-l'], stdin=PIPE)
+ p.communicate(command)
+ if p.returncode != 0:
+ raise NsupdateError(p.returncode)
+
+
+def nsupdate_delete(fqdn, ip_family):
+ """
+ :param fqdn: Fully qualified domain name
+ :param ip_family: A or AAAA
+ :raises an NsupdateError in case of errors."""
+ command = "update delete {fqdn} {ip_family}\n\n".format(fqdn=fqdn, ip_family=ip_family)
+ p = Popen(['nsupdate', '-l'], stdin=PIPE)
+ p.communicate(command)
+ if p.returncode != 0:
+ raise NsupdateError(p.returncode)
+
+
+def main(args):
+ try:
+ if args.delete:
+ if args.ip is None:
+ nsupdate_delete(args.fqdn, 'A')
+ nsupdate_delete(args.fqdn, 'AAAA')
+ else:
+ nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip))
+ else:
+ nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip))
+ nsupdate_add(args.fqdn, args.ttl, args.ip)
+ except NsupdateError as e:
+ sys.exit(e.returncode)
+
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Add or delete a domain name from dyndns (simplifies call to nsupdate).')
+ parser.add_argument('-d', '--delete', action='store_true', help='delete instead of add')
+ parser.add_argument('-i', '--ip', help='IP address (either IPv4 or IPv6)')
+ # parser.add_argument('-t', '--ttl', type=int, default=600, help='TTL (default: 600)')
+ parser.add_argument('fqdn', help='fully qualified domain name to add or delete, e.g. myserver.dyn.example.com')
+ args = parser.parse_args()
+
+ args.ttl = 600 # <---- TODO
+
+ # check ip
+ if not args.delete and not args.ip:
+ parser.error('The IP address is mandatory')
+ if args.ip:
+ try:
+ args.ip = ipaddr.IPAddress(args.ip) # throws an exception if the IP address is not valid
+ except ValueError:
+ parser.error('The IP address is not valid')
+
+ # check fqdn
+ if re.match(r'[-0-9a-z]+(\.[-0-9a-z]+)*$', args.fqdn) is None:
+ parser.error('The fqdn has an invalid format.')
+
+ main(args)