X-Git-Url: https://git.toastfreeware.priv.at/toast/tdyndns.git/blobdiff_plain/89d757f016b07ca7a40ad45d6568973a2dcda2d5..HEAD:/bin/tdyndns_update diff --git a/bin/tdyndns_update b/bin/tdyndns_update index b43286e..8806c8a 100755 --- a/bin/tdyndns_update +++ b/bin/tdyndns_update @@ -1,19 +1,15 @@ #!/usr/bin/python +import os import sys import re import argparse -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, check_call, CalledProcessError import ipaddr +import dns.zone # http://www.dnspython.org/ -class NsupdateError(Exception): - def __init__(self, returncode): - self.returncode = returncode - - -class BlockipError(Exception): - def __init__(self, returncode): - self.returncode = returncode +def sync_dynamic_zones(): + check_call(['rndc', 'sync']) def ipfamily_by_ip(ip): @@ -24,49 +20,72 @@ def ipfamily_by_ip(ip): assert False +def forward_lookup(fqdn, ip_family): + """Returns the ip address of the fqdn or None if none is found.. + + :param fqdn: Fully qualified domain name. + :param ip_family: 'A' or 'AAAA'""" + filename = '/var/cache/bind/dyn.colgarra.priv.at' + zonename = os.path.basename(filename) + zone = dns.zone.from_file(filename, zonename, relativize=False) + for name, ttl, rdata in zone.iterate_rdatas(ip_family): + if str(name)[:-1] == fqdn: # [:-1] removes trailing dot + return ipaddr.IPAddress(rdata.address) + + +def reverse_lookup(ip): + """Returns an iterator of fqdns for the given IP address. + + :param ip: Instance of ipaddr.IPv4Address or ipaddr.IPv6Address""" + filename = '/var/cache/bind/dyn.colgarra.priv.at' + zonename = os.path.basename(filename) + zone = dns.zone.from_file(filename, zonename, relativize=False) + for name, ttl, rdata in zone.iterate_rdatas(ipfamily_by_ip(ip)): + if ipaddr.IPAddress(rdata.address) == ip: + yield str(name) + + 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.""" + :raises an CalledProcessError 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) + raise CalledProcessError(p.returncode, 'nsupdate -l') 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.""" + :raises an CalledProcessError 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) + raise CalledProcessError(p.returncode, 'nsupdate -l') def blockip_whitelist_add(ip): """ :param ip: ipv4 address - :raises a BlockipError in case of errors.""" - command = "-I blockip -s {ip} -j ACCEPT\n\n".format(ip=ip) - p = Popen(['iptables'], stdin=PIPE) - p.communicate(command) - if p.returncode != 0: - raise NsupdateError(p.returncode) + """ + if ipfamily_by_ip(ip) == 'A': + command = ['iptables', '-I', 'blockip', '-s', str(ip), '-j', 'ACCEPT'] + p = Popen(command, stderr=PIPE) + stdout, stderr = p.communicate() def blockip_whitelist_delete(ip): """ :param ip: ipv4 address - :raises a BlockipError in case of errors.""" - command = "-D blockip -s {ip} -j ACCEPT\n\n".format(ip=ip) - p = Popen(['iptables'], stdin=PIPE) - p.communicate(command) - if p.returncode != 0: - raise NsupdateError(p.returncode) + """ + if ipfamily_by_ip(ip) == 'A': + command = ['iptables', '-D', 'blockip', '-s', str(ip), '-j', 'ACCEPT'] + p = Popen(command, stderr=PIPE) + stdout, stderr = p.communicate() def main(args): @@ -76,15 +95,23 @@ def main(args): nsupdate_delete(args.fqdn, 'A') nsupdate_delete(args.fqdn, 'AAAA') else: + ipfamily = ipfamily_by_ip(args.ip) + sync_dynamic_zones() + old_ip = forward_lookup(args.fqdn, ipfamily) nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip)) - if ipfamily_by_ip(args.ip) == 'A': - blockip_whitelist_delete(args.ip) + if old_ip is not None: + blockip_whitelist_delete(old_ip) else: - nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip)) + ipfamily = ipfamily_by_ip(args.ip) + sync_dynamic_zones() + old_ip = forward_lookup(args.fqdn, ipfamily) + nsupdate_delete(args.fqdn, ipfamily) nsupdate_add(args.fqdn, args.ttl, args.ip) - if ipfamily_by_ip(args.ip) == 'A': + if old_ip != args.ip: + if old_ip is not None: + blockip_whitelist_delete(old_ip) blockip_whitelist_add(args.ip) - except NsupdateError as e: + except CalledProcessError as e: sys.exit(e.returncode)