2 # Copyright 2019 Exentrique Solutions Ltd
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
27 sys.stderr.write('backend (%s): %s\n' % (os.getpid(), msg))
36 _log('writing: %s' % a)
41 sys.stdout.write('\t')
44 sys.stdout.write('\n')
51 line = sys.stdin.readline()
53 _log('read line: %s' % line)
54 return line.strip().split('\t')
64 self.name_servers = {}
65 self.blacklisted_ips = []
68 fname = self._get_config_filename()
69 if not os.path.exists(fname):
70 _log('%s does not exist' % fname)
73 with open(fname) as fp:
74 config = ConfigParser.ConfigParser()
77 self.id = config.get('soa', 'id')
78 self.soa = '%s %s %s' % (config.get('soa', 'ns'), config.get('soa', 'hostmaster'), self.id)
79 self.domain = config.get('main', 'domain')
80 self.ip_address = config.get('main', 'ipaddress')
81 self.ttl = config.get('main', 'ttl')
83 for entry in config.items('nameservers'):
84 self.name_servers[entry[0]] = entry[1]
86 if config.has_section("blacklist"):
87 for entry in config.items("blacklist"):
88 self.blacklisted_ips.append(entry[1])
90 _log('Name servers: %s' % self.name_servers)
91 _log('ID: %s' % self.id)
92 _log('TTL %s' % self.ttl)
93 _log('SOA: %s' % self.soa)
94 _log('IP Address: %s' % self.ip_address)
95 _log('DOMAIN: %s' % self.domain)
96 _log("Blacklist: %s" % self.blacklisted_ips)
100 handshake = _get_next()
101 if handshake[1] != '1':
102 _log('Not version 1: %s' % handshake)
104 _write('OK', 'We are good')
105 _log('Done handshake')
110 _log("cmd: %s" % cmd)
117 _log('did not understand: %s' % cmd)
121 qname = cmd[1].lower()
124 if (qtype == 'A' or qtype == 'ANY') and qname.endswith(self.domain):
125 if qname == self.domain:
126 self.handle_self(self.domain)
127 elif qname in self.name_servers:
128 self.handle_nameservers(qname)
130 self.handle_subdomains(qname)
131 elif qtype == 'SOA' and qname.endswith(self.domain):
132 self.handle_soa(qname)
134 self.handle_unknown(qtype, qname)
136 def handle_self(self, name):
137 _write('DATA', name, 'IN', 'A', self.ttl, self.id, self.ip_address)
138 self.write_name_servers(name)
141 def handle_subdomains(self, qname):
142 subdomain = qname[0:qname.find(self.domain) - 1]
144 subparts = subdomain.split('.')
145 if len(subparts) < 4:
147 _log('subparts less than 4')
148 self.handle_self(qname)
151 ip_address_parts = subparts[-4:]
153 _log('ip: %s' % ip_address_parts)
154 for part in ip_address_parts:
155 if re.match('^\d{1,3}$', part) is None:
157 _log('%s is not a number' % part)
158 self.handle_self(qname)
161 if parti < 0 or parti > 255:
163 _log('%d is too big/small' % parti)
164 self.handle_self(qname)
167 ip_address = ".".join(ip_address_parts)
168 if ip_address in self.blacklisted_ips:
169 self.handle_blacklisted(ip_address)
172 _write('DATA', qname, 'IN', 'A', self.ttl, self.id, '%s.%s.%s.%s' % (ip_address_parts[0], ip_address_parts[1], ip_address_parts[2], ip_address_parts[3]))
173 self.write_name_servers(qname)
176 def handle_nameservers(self, qname):
177 ip = self.name_servers[qname]
178 _write('DATA', qname, 'IN', 'A', self.ttl, self.id, ip)
181 def write_name_servers(self, qname):
182 for nameServer in self.name_servers:
183 _write('DATA', qname, 'IN', 'NS', self.ttl, self.id, nameServer)
185 def handle_soa(self, qname):
186 _write('DATA', qname, 'IN', 'SOA', self.ttl, self.id, self.soa)
189 def handle_unknown(self, qtype, qname):
190 _write('LOG', 'Unknown type: %s, domain: %s' % (qtype, qname))
193 def handle_blacklisted(self, ip_address):
194 _write('LOG', 'Blacklisted: %s' % ip_address)
197 def _get_config_filename(self):
198 return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'backend.conf')
201 if __name__ == '__main__':
202 backend = DynamicBackend()