+++ /dev/null
-#!/usr/bin/python
-import sys
-import re
-import argparse
-from subprocess import Popen, PIPE
-import ipaddr
-
-
-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()
-
- # 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)
--- /dev/null
+#!/usr/bin/python
+import sys
+import re
+import argparse
+from subprocess import Popen, PIPE
+import ipaddr
+
+
+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()
+
+ # 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)
+++ /dev/null
-#!/usr/bin/python2.7
-"""Dynamic DNS script. Expects URLs from routers in the form
-http://dyndns.colgarra.priv.at/nic/update?username=<username>&password=<pass>&hostname=<domain>&myip=<ipaddr>
-"""
-import os
-import re
-import cgi
-import pwd
-import base64
-from subprocess import call
-import ipaddr
-
-
-# Configuration
-PASSWORD = 'hygCithOrs5'
-DEBUG = False
-
-
-# Just for debugging:
-if DEBUG:
- import cgitb
- cgitb.enable()
-
-
-# Base class for our exceptions
-class DynDnsError(Exception):
- pass
-
-class AuthError(DynDnsError):
- returncode = 'badauth'
-
-class CredentialsMissing(AuthError):
- pass
-
-class UsernameMissing(AuthError):
- pass
-
-class UsernameInvalid(AuthError):
- pass
-
-class PasswordMissing(AuthError):
- pass
-
-class PasswordWrong(AuthError):
- pass
-
-class AuthWrongMethod(AuthError):
- returncode = 'wrongauthmethod' # not documented at dyn.com
-
-class AuthBasicError(AuthError):
- returncode = 'authbasicerror' # not documented at dyn.com
-
-class HostnameError(DynDnsError):
- returncode = 'notfqdn'
-
-class HostnameMissing(HostnameError):
- pass
-
-class PasswordWrong(HostnameError):
- pass
-
-class MyipError(DynDnsError):
- returncode = 'badip' # not documented at dyn.com
-
-class MyipMissing(MyipError):
- pass
-
-class MyipInvalid(MyipError):
- pass
-
-class OfflineInvalid(DynDnsError):
- returncode = 'badparam' # not documented at dyn.com
-
-class NsupdateError(DynDnsError):
- returncode = 'nohost'
-
-
-fields = cgi.FieldStorage()
-
-# the following fields are supported by most dyndns providers
-# if a parameter is not provided, the .getvalue method returns None
-username = fields.getvalue('username')
-password = fields.getvalue('password')
-hostname = fields.getvalue('hostname')
-myip = fields.getvalue('myip')
-wildcard = fields.getvalue('wildcard')
-mx = fields.getvalue('mx')
-backmx = fields.getvalue('backmx')
-offline = fields.getvalue('offline')
-system = fields.getvalue('system')
-url = fields.getvalue('url')
-
-
-try:
- # Optional Auth Basic
- auth = os.environ.get('HTTP_AUTHORIZATION') # auth == 'Basic cGhpbGlwcDpka2ZhamRrZg=='
- if auth: # empty string if HTTP_AUTHORIZATION not present
- auth_parts = auth.split(' ')
- auth_method = 'Basic'
- if len(auth_parts) != 2 or auth_parts[0] != auth_method:
- raise AuthWrongMethod()
- try:
- auth_decoded = base64.b64decode(auth_parts[1]) # auth_decoded == 'philipp:dkfajdkf'
- except TypeError:
- raise AuthBasicError()
- auth_decoded_parts = auth_decoded.split(':')
- if len(auth_decoded_parts) != 2:
- raise AuthBasicError()
- username, password = auth_decoded_parts
-
- # check username and password
- if username is None and password is None:
- raise CredentialsMissing()
-
- # check username
- if username is None:
- raise UsernameMissing()
- try:
- user_info = pwd.getpwnam(username)
- except KeyError:
- raise UsernameInvalid()
- if user_info.pw_uid < 1000:
- raise UsernameInvalid()
-
- # check password
- if password is None:
- raise PasswordMissing()
- if password != PASSWORD:
- raise PasswordWrong()
-
- # check hostname
- if hostname is None:
- raise HostnameMissing()
- if re.match(r'[-0-9a-z]+(\.[-0-9a-z]+)*$', hostname) is None:
- raise HostnameInvalid()
-
- # strip zone
- hostname = hostname.strip()
-
- # check offline
- if offline is None or offline.lower() == 'no':
- offline = False
- elif offline.lower() == 'yes':
- offline = True
- else:
- raise OfflineInvalid()
-
- # check IP address
- if not offline:
- if myip is None:
- # try HTTP_X_FORWARDED_FOR
- myip = os.environ.get('HTTP_X_FORWARDED_FOR')
- if not myip: # empty string if not present
- # try REMOTE_ADDR
- myip = os.environ.get('REMOTE_ADDR')
- if not myip: # empty string if not present
- raise MyipMissing()
- if not myip is None:
- try:
- ipaddr.IPAddress(myip) # throws an exception if the IP address is not valid
- except ValueError:
- raise MyipInvalid()
-
- # update bind
- call_params = ['sudo', 'nsupdate_dyndns']
- if offline:
- call_params.append('--delete')
- if myip is not None:
- call_params.extend(['--ip', myip])
- call_params.append(hostname)
- retcode = call(call_params)
- if retcode != 0:
- raise NsupdateError()
-
- # return success
- print "Content-Type: text/html"
- print
- print "good"
- # Note: we should return 'nochg' in case the IP has not changed, however we don't know yet.
-
-
-except CredentialsMissing as error:
- print "Content-Type: text/html"
- print "Status: 401 Unauthorized"
- print "WWW-Authenticate: Basic realm='tdyndns'"
- print
-
-except DynDnsError as error:
- print "Content-Type: text/html"
- print "Status: 200 OK"
- print
- print error.returncode
-
--- /dev/null
+#!/usr/bin/python2.7
+"""Dynamic DNS script. Expects URLs from routers in the form
+http://dyndns.colgarra.priv.at/nic/update?username=<username>&password=<pass>&hostname=<domain>&myip=<ipaddr>
+"""
+import os
+import re
+import cgi
+import pwd
+import base64
+from subprocess import call
+import ipaddr
+
+
+# Configuration
+PASSWORD = 'hygCithOrs5'
+DEBUG = False
+
+
+# Just for debugging:
+if DEBUG:
+ import cgitb
+ cgitb.enable()
+
+
+# Base class for our exceptions
+class DynDnsError(Exception):
+ pass
+
+class AuthError(DynDnsError):
+ returncode = 'badauth'
+
+class CredentialsMissing(AuthError):
+ pass
+
+class UsernameMissing(AuthError):
+ pass
+
+class UsernameInvalid(AuthError):
+ pass
+
+class PasswordMissing(AuthError):
+ pass
+
+class PasswordWrong(AuthError):
+ pass
+
+class AuthWrongMethod(AuthError):
+ returncode = 'wrongauthmethod' # not documented at dyn.com
+
+class AuthBasicError(AuthError):
+ returncode = 'authbasicerror' # not documented at dyn.com
+
+class HostnameError(DynDnsError):
+ returncode = 'notfqdn'
+
+class HostnameMissing(HostnameError):
+ pass
+
+class PasswordWrong(HostnameError):
+ pass
+
+class MyipError(DynDnsError):
+ returncode = 'badip' # not documented at dyn.com
+
+class MyipMissing(MyipError):
+ pass
+
+class MyipInvalid(MyipError):
+ pass
+
+class OfflineInvalid(DynDnsError):
+ returncode = 'badparam' # not documented at dyn.com
+
+class NsupdateError(DynDnsError):
+ returncode = 'nohost'
+
+
+fields = cgi.FieldStorage()
+
+# the following fields are supported by most dyndns providers
+# if a parameter is not provided, the .getvalue method returns None
+username = fields.getvalue('username')
+password = fields.getvalue('password')
+hostname = fields.getvalue('hostname')
+myip = fields.getvalue('myip')
+wildcard = fields.getvalue('wildcard')
+mx = fields.getvalue('mx')
+backmx = fields.getvalue('backmx')
+offline = fields.getvalue('offline')
+system = fields.getvalue('system')
+url = fields.getvalue('url')
+
+
+try:
+ # Optional Auth Basic
+ auth = os.environ.get('HTTP_AUTHORIZATION') # auth == 'Basic cGhpbGlwcDpka2ZhamRrZg=='
+ if auth: # empty string if HTTP_AUTHORIZATION not present
+ auth_parts = auth.split(' ')
+ auth_method = 'Basic'
+ if len(auth_parts) != 2 or auth_parts[0] != auth_method:
+ raise AuthWrongMethod()
+ try:
+ auth_decoded = base64.b64decode(auth_parts[1]) # auth_decoded == 'philipp:dkfajdkf'
+ except TypeError:
+ raise AuthBasicError()
+ auth_decoded_parts = auth_decoded.split(':')
+ if len(auth_decoded_parts) != 2:
+ raise AuthBasicError()
+ username, password = auth_decoded_parts
+
+ # check username and password
+ if username is None and password is None:
+ raise CredentialsMissing()
+
+ # check username
+ if username is None:
+ raise UsernameMissing()
+ try:
+ user_info = pwd.getpwnam(username)
+ except KeyError:
+ raise UsernameInvalid()
+ if user_info.pw_uid < 1000:
+ raise UsernameInvalid()
+
+ # check password
+ if password is None:
+ raise PasswordMissing()
+ if password != PASSWORD:
+ raise PasswordWrong()
+
+ # check hostname
+ if hostname is None:
+ raise HostnameMissing()
+ if re.match(r'[-0-9a-z]+(\.[-0-9a-z]+)*$', hostname) is None:
+ raise HostnameInvalid()
+
+ # strip zone
+ hostname = hostname.strip()
+
+ # check offline
+ if offline is None or offline.lower() == 'no':
+ offline = False
+ elif offline.lower() == 'yes':
+ offline = True
+ else:
+ raise OfflineInvalid()
+
+ # check IP address
+ if not offline:
+ if myip is None:
+ # try HTTP_X_FORWARDED_FOR
+ myip = os.environ.get('HTTP_X_FORWARDED_FOR')
+ if not myip: # empty string if not present
+ # try REMOTE_ADDR
+ myip = os.environ.get('REMOTE_ADDR')
+ if not myip: # empty string if not present
+ raise MyipMissing()
+ if not myip is None:
+ try:
+ ipaddr.IPAddress(myip) # throws an exception if the IP address is not valid
+ except ValueError:
+ raise MyipInvalid()
+
+ # update bind
+ call_params = ['sudo', 'tdyndns_update']
+ if offline:
+ call_params.append('--delete')
+ if myip is not None:
+ call_params.extend(['--ip', myip])
+ call_params.append(hostname)
+ retcode = call(call_params)
+ if retcode != 0:
+ raise NsupdateError()
+
+ # return success
+ print "Content-Type: text/html"
+ print
+ print "good"
+ # Note: we should return 'nochg' in case the IP has not changed, however we don't know yet.
+
+
+except CredentialsMissing as error:
+ print "Content-Type: text/html"
+ print "Status: 401 Unauthorized"
+ print "WWW-Authenticate: Basic realm='tdyndns'"
+ print
+
+except DynDnsError as error:
+ print "Content-Type: text/html"
+ print "Status: 200 OK"
+ print
+ print error.returncode
+