"""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
cgitb.enable()
-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')
-
-
# Base class for our exceptions
class DynDnsError(Exception):
pass
class AuthError(DynDnsError):
returncode = 'badauth'
+class CredentialsMissing(AuthError):
+ pass
+
class UsernameMissing(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 OfflineInvalid(DynDnsError):
returncode = 'badparam' # not documented at dyn.com
+
+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')
+
+
+# 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
+
+
try:
+ # check username and password
+ if username is None and password is None:
+ raise CredentialsMissing()
+
# check username
if username is None:
raise UsernameMissing()
# 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"
+ print "Status: 200 OK"
print
print error.returncode