241 lines
6.6 KiB
Python
241 lines
6.6 KiB
Python
#! /usr/bin/env python
|
|
# dnsupdate.py
|
|
# Updates a dynamic dns-record using a TSIG key.
|
|
|
|
|
|
def checkerror(msg, show=""):
|
|
# Determine if there are any errors reported
|
|
if len(msg['error']) > 0:
|
|
if show and not msg.has_key('quiet'):
|
|
for error in msg['error']:
|
|
print "Error: %s" % error
|
|
print "Use -h option for help"
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
|
|
|
|
def get_ipaddress():
|
|
# Connect to a remote server to determine which ip address
|
|
# this host connects from
|
|
import re
|
|
import urllib
|
|
remote = opener = urllib.FancyURLopener({})
|
|
try:
|
|
f = opener.open(msg['ipurl'])
|
|
page = f.read()
|
|
res = re.search('[12]?[0-9]?[0-9](\.[12]?[0-9]?[0-9]){3}', page)
|
|
ip = res.group()
|
|
return ip
|
|
except:
|
|
msg['error'].append("Could not determine ip address automatically,\n use -i switch to enter manually")
|
|
|
|
|
|
|
|
def getparams(msg):
|
|
# Read command line parameters and input values
|
|
from optparse import OptionParser
|
|
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("--force",
|
|
action="store_true",
|
|
help="Force the action")
|
|
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", "--hostname",
|
|
type="string",
|
|
help="Hostname of local machine")
|
|
parser.add_option("-q", "--quiet",
|
|
action="store_true",
|
|
help="Quit mode")
|
|
parser.add_option("-s", "--keysecret",
|
|
type="string",
|
|
help="TSIG key")
|
|
parser.add_option("-u", "--ipurl",
|
|
type="string",
|
|
help="URL to ip server")
|
|
parser.add_option("-t", "--ttl",
|
|
type="int",
|
|
help="TTL in seconds")
|
|
parser.add_option("-v", "--verbose",
|
|
action="store_true",
|
|
help="Print progress information")
|
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
if options.config:
|
|
msg['cfgfile'] = options.config
|
|
|
|
msg = readcfg(msg)
|
|
|
|
# Populate the basic params
|
|
base_params = ['delete',
|
|
'domain',
|
|
'force',
|
|
'hostname',
|
|
'keyname',
|
|
'keysecret',
|
|
'quiet',
|
|
'ttl',
|
|
'ipurl']
|
|
|
|
for param in base_params:
|
|
if eval('options.' + param):
|
|
msg[param] = eval('options.' + param)
|
|
|
|
# These parameters needs special handling
|
|
if options.ipaddress:
|
|
msg['ipaddress'] = options.ipaddress
|
|
else:
|
|
ip = get_ipaddress()
|
|
if ip:
|
|
msg['ipaddress'] = ip
|
|
|
|
if options.verbose and msg.has_key('quiet'):
|
|
del msg['quiet']
|
|
|
|
return msg
|
|
|
|
|
|
|
|
def readcfg(msg):
|
|
# Read the config file for pre configured values
|
|
import os.path
|
|
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():
|
|
line = line.strip()
|
|
if line.find("#", 0, 1) == 0 or not line:
|
|
continue
|
|
(key, value) = line.split('\t', 1)
|
|
if not value.strip().lower() == "false":
|
|
msg[key] = value.strip()
|
|
|
|
cfgfile.close()
|
|
return msg
|
|
|
|
|
|
|
|
def update(msg):
|
|
# The update function connects to the dns server
|
|
import dns.query
|
|
import dns.tsigkeyring
|
|
import dns.update
|
|
|
|
# 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 keysecret.")
|
|
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):
|
|
# Check if the ip address exists and if it needs an update
|
|
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)
|
|
|
|
err = checkerror(msg)
|
|
if err == 0:
|
|
if not msg.has_key('force'):
|
|
verify_ip(msg)
|
|
|
|
err = checkerror(msg)
|
|
if err == 0:
|
|
update(msg)
|
|
checkerror(msg)
|
|
if err == 0:
|
|
sys.exit(0)
|
|
|
|
err = checkerror(msg, show="errors")
|
|
sys.exit(1)
|
|
|