add blockip_whitelist_add and blockip_whitelist_delete
[toast/tdyndns.git] / bin / tdyndns_update
1 #!/usr/bin/python
2 import sys
3 import re
4 import argparse
5 from subprocess import Popen, PIPE
6 import ipaddr
7
8
9 class NsupdateError(Exception):
10         def __init__(self, returncode):
11                 self.returncode = returncode
12
13
14 class BlockipError(Exception):
15         def __init__(self, returncode):
16                 self.returncode = returncode
17
18
19 def ipfamily_by_ip(ip):
20         if isinstance(ip, ipaddr.IPv4Address):
21                 return 'A'
22         elif isinstance(ip, ipaddr.IPv6Address):
23                 return 'AAAA'
24         assert False
25
26
27 def nsupdate_add(fqdn, ttl, ip):
28         """
29         :param fqdn: Fully qualified domain name
30         :param ip_family: A or AAAA
31         :raises an NsupdateError in case of errors."""
32         command = "update add {fqdn} {ttl} IN {ip_family} {ip}\n\n".format(fqdn=fqdn, ttl=ttl, ip_family=ipfamily_by_ip(ip), ip=ip)
33         p = Popen(['nsupdate', '-l'], stdin=PIPE)
34         p.communicate(command)
35         if p.returncode != 0:
36                 raise NsupdateError(p.returncode)
37
38 def nsupdate_delete(fqdn, ip_family):
39         """
40         :param fqdn: Fully qualified domain name
41         :param ip_family: A or AAAA
42         :raises an NsupdateError in case of errors."""
43         command = "update delete {fqdn} {ip_family}\n\n".format(fqdn=fqdn, ip_family=ip_family)
44         p = Popen(['nsupdate', '-l'], stdin=PIPE)
45         p.communicate(command)
46         if p.returncode != 0:
47                 raise NsupdateError(p.returncode)
48
49
50 def blockip_whitelist_add(ip):
51         """
52         :param ip: ipv4 address
53         :raises a BlockipError in case of errors."""
54         command = "-I blockip -s {ip} -j ACCEPT\n\n".format(ip=ip)
55         p = Popen(['iptables'], stdin=PIPE)
56         p.communicate(command)
57         if p.returncode != 0:
58                 raise NsupdateError(p.returncode)
59
60
61 def blockip_whitelist_delete(ip):
62         """
63         :param ip: ipv4 address
64         :raises a BlockipError in case of errors."""
65         command = "-D blockip -s {ip} -j ACCEPT\n\n".format(ip=ip)
66         p = Popen(['iptables'], stdin=PIPE)
67         p.communicate(command)
68         if p.returncode != 0:
69                 raise NsupdateError(p.returncode)
70
71
72 def main(args):
73         try:
74                 if args.delete:
75                         if args.ip is None:
76                                 nsupdate_delete(args.fqdn, 'A')
77                                 nsupdate_delete(args.fqdn, 'AAAA')
78                         else:
79                                 nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip))
80                                 if ipfamily_by_ip(args.ip) == 'A':
81                                         blockip_whitelist_delete(args.ip)
82                 else:
83                         nsupdate_delete(args.fqdn, ipfamily_by_ip(args.ip))
84                         nsupdate_add(args.fqdn, args.ttl, args.ip)
85                         if ipfamily_by_ip(args.ip) == 'A':
86                                 blockip_whitelist_add(args.ip)
87         except NsupdateError as e:
88                 sys.exit(e.returncode)
89
90
91
92 if __name__ == '__main__':
93         parser = argparse.ArgumentParser(description='Add or delete a domain name from dyndns (simplifies call to nsupdate).')
94         parser.add_argument('-d', '--delete', action='store_true', help='delete instead of add')
95         parser.add_argument('-i', '--ip', help='IP address (either IPv4 or IPv6)')
96         parser.add_argument('-t', '--ttl', type=int, default=600, help='TTL (default: 600)')
97         parser.add_argument('fqdn', help='fully qualified domain name to add or delete, e.g. myserver.dyn.example.com')
98         args = parser.parse_args()
99
100         # check ip
101         if not args.delete and not args.ip:
102                 parser.error('The IP address is mandatory')
103         if args.ip:
104                 try:
105                         args.ip = ipaddr.IPAddress(args.ip) # throws an exception if the IP address is not valid
106                 except ValueError:
107                         parser.error('The IP address is not valid')
108
109         # check fqdn
110         if re.match(r'[-0-9a-z]+(\.[-0-9a-z]+)*$', args.fqdn) is None:
111                 parser.error('The fqdn has an invalid format.')
112
113         main(args)