#! /usr/bin/env python # dnsupdate.py # Updates a dynamic dns-record using a TSIG key. def checkerror(msg): if len(msg['error']) > 0: if not msg.has_key('quiet'): for error in msg['error']: print "Error: %s" % error print "Use -h for help" return 1 else: return 0 def get_ipaddress(): # Connect to the dns server to determine which ip address # this host connects from import urllib remote = opener = urllib.FancyURLopener({}) try: f = opener.open("http://www.wahlberg.se/~fredrik/ip.php") ip = f.read().strip() return ip except: msg['error'].append("Could not determine ip address automatically,\nuse -i switch to enter manually") def getparams(msg): from optparse import OptionParser import sys # Define option parameters usage = "usage: %prog [OPTIONS]" parser = OptionParser(usage) parser.add_option("-c", "--config", type="string", help="Alternate config file") parser.add_option("--delete", action="store_true", help="Remove the host from the zone") parser.add_option("-d", "--domain", type="string", help="Domain to update") parser.add_option("-i", "--ipaddress", type="string", help="IP-address of the host [auto detected]") parser.add_option("-k", "--keyname", type="string", help="Name of the TSIG key") parser.add_option("-n", "--name", type="string", help="Hostname of local machine") parser.add_option("-p", "--password", type="string", help="TSIG key") parser.add_option("-q", "--quiet", action="store_true", help="Quit mode") parser.add_option("-t", "--ttl", type="int", help="TTL in seconds") (options, args) = parser.parse_args() if options.config: msg['cfgfile'] = options.config msg = readcfg(msg) if options.ipaddress: msg['ipaddress'] = options.ipaddress else: ip = get_ipaddress() if ip: msg['ipaddress'] = ip if options.delete: msg['delete'] = options.delete if options.domain: msg['domain'] = options.domain if options.keyname: msg['keyname'] = options.keyname if options.name: msg['hostname'] = options.name if options.password: msg['keysecret'] = options.password if options.quiet: msg['quiet'] = options.quiet if options.ttl: msg['ttl'] = options.ttl return msg def readcfg(msg): import os.path # Reads the config file for default info if not msg.has_key('cfgfile'): if os.path.exists(os.path.expanduser("~/.dnsupdaterc")): cfgfile = open(os.path.expanduser("~/.dnsupdaterc"), 'r') else: return msg else: if not os.path.exists(msg['cfgfile']): msg['error'].append("No such file: %s" % msg['cfgfile']) return msg cfgfile = open(msg['cfgfile'], 'r') for line in cfgfile.readlines(): (key, value) = line.split('\t', 1) msg[key] = value.strip() cfgfile.close() return msg def update(msg): import dns.query import dns.tsigkeyring import dns.update # The update function connects to the dns server # The name of the key and the secret keyring = dns.tsigkeyring.from_text({ msg['keyname']: msg['keysecret'] }) # dns.update.Update(name of domain, keyring, keyname) update = dns.update.Update(msg['domain'], keyring=keyring, keyname=msg['keyname']) if msg.has_key('delete'): update.delete(msg['hostname']) else: # update.replace(hostname, ttl, record-type, new ip) update.replace(msg['hostname'], msg['ttl'], 'a', msg['ipaddress']) # doit, servername try: response = dns.query.tcp(update, '217.78.32.198') except: msg['error'].append("An error has occurred, check your keyname and password.") return # Verify response if not msg.has_key('quiet'): if response.rcode() == 0: if msg.has_key("delete"): print "Host '%s.%s' has been deleted" % (msg['hostname'], msg['domain']) else: print "Host '%s.%s' has been added with ip address %s" % (msg['hostname'], msg['domain'], msg['ipaddress']) else: print "An error has occurred, the server returned:\n%s" % response def validate(msg): import re import string # Verify all required data is present and sanity check incoming data req_vals = ['domain', 'hostname', 'ipaddress', 'keyname', 'keysecret', 'ttl'] for value in req_vals: if not msg.has_key(value): msg['error'].append('Missing "%s" parameter' % value) #global hostname, ipaddress, ttl if msg.has_key('hostname') and msg.has_key('domain'): msg['hostname'] = string.replace(msg['hostname'], "." + msg['domain'], '') if msg.has_key('ipaddress'): if not re.search('^[12]?[0-9]?[0-9](\.[12]?[0-9]?[0-9]){3}$', msg['ipaddress']): msg['error'].append("Invalid ip address '%s'" % msg['ipaddress']) return msg def verify_ip(msg): import dns.resolver try: ans = dns.resolver.query(msg['hostname'] + "." + msg['domain'], 'A') for res in ans: ip = res.to_text() except: ip = "" if ip == msg['ipaddress'] and not msg.has_key('delete'): msg['error'].append("Nameserver already up to date") elif ip == "" and msg.has_key('delete'): msg['error'].append("Nameserver does not recognise the hostname") return msg if __name__=="__main__": import sys msg = {} msg['error'] = [] getparams(msg) validate(msg) verify_ip(msg) err = checkerror(msg) if err == 0: update(msg) checkerror(msg) if err == 0: sys.exit(0) sys.exit(1)