Now errors in blockip related subcommands are ignored.
[toast/tdyndns.git] / cgi-bin / tdyndns.py
1 #!/usr/bin/python
2 """Dynamic DNS script. Expects URLs from routers in the form
3 http://dyndns.colgarra.priv.at/nic/update?hostname=<domain>&myip=<ipaddr>
4 """
5 import os
6 import re
7 import cgi
8 from subprocess import call
9 import ipaddr
10
11
12 # Configuration
13 DEBUG = False
14
15
16 # Just for debugging:
17 if DEBUG:
18         import cgitb
19         cgitb.enable()
20
21
22 # Base class for our exceptions
23 class DynDnsError(Exception):
24         pass
25
26 class HostnameError(DynDnsError):
27         returncode = 'notfqdn'
28
29 class HostnameMissing(HostnameError):
30         pass
31
32 class PasswordWrong(HostnameError):
33         pass
34
35 class MyipError(DynDnsError):
36         returncode = 'badip' # not documented at dyn.com
37
38 class MyipMissing(MyipError):
39         pass
40
41 class MyipInvalid(MyipError):
42         pass
43
44 class OfflineInvalid(DynDnsError):
45         returncode = 'badparam' # not documented at dyn.com
46
47 class NsupdateError(DynDnsError):
48         returncode = 'nohost'
49
50
51 fields = cgi.FieldStorage()
52
53 # the following fields are supported by most dyndns providers
54 # if a parameter is not provided, the .getvalue method returns None
55 hostname = fields.getvalue('hostname')
56 myip     = fields.getvalue('myip')
57 wildcard = fields.getvalue('wildcard')
58 mx       = fields.getvalue('mx')
59 backmx   = fields.getvalue('backmx')
60 offline  = fields.getvalue('offline')
61 system   = fields.getvalue('system')
62 url      = fields.getvalue('url')
63
64
65 try:
66         # check hostname
67         if hostname is None:
68                 raise HostnameMissing()
69         if re.match(r'[-0-9a-z]+(\.[-0-9a-z]+)*$', hostname) is None:
70                 raise HostnameInvalid()
71
72         # strip zone
73         hostname = hostname.strip()
74
75         # check offline
76         if offline is None or offline.lower() == 'no':
77                 offline = False
78         elif offline.lower() == 'yes':
79                 offline = True
80         else:
81                 raise OfflineInvalid()
82
83         # check IP address
84         if not offline:
85                 if myip is None:
86                         # try HTTP_X_FORWARDED_FOR
87                         myip = os.environ.get('HTTP_X_FORWARDED_FOR')
88                         if not myip: # empty string if not present
89                                 # try REMOTE_ADDR
90                                 myip = os.environ.get('REMOTE_ADDR')
91                                 if not myip: # empty string if not present
92                                         raise MyipMissing()
93         if not myip is None:
94                 try:
95                         ipaddr.IPAddress(myip) # throws an exception if the IP address is not valid
96                 except ValueError:
97                         raise MyipInvalid()
98
99         # update bind
100         call_params = ['sudo', 'tdyndns_update']
101         if offline:
102                 call_params.append('--delete')
103         if myip is not None:
104                 call_params.extend(['--ip', myip])
105         call_params.append(hostname)
106         retcode = call(call_params)
107         if retcode != 0:
108                 raise NsupdateError()
109
110         # return success
111         print "Content-Type: text/html"
112         print
113         print "good"
114         # Note: we should return 'nochg' in case the IP has not changed, however we don't know yet.
115
116
117 except DynDnsError as error:
118         print "Content-Type: text/html"
119         print "Status: 200 OK"
120         print
121         print error.returncode
122