2016-08-02 18:19:19 +03:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import print_function
|
2016-08-02 20:21:42 +03:00
|
|
|
import time
|
2016-08-02 18:19:19 +03:00
|
|
|
|
|
|
|
import dnslib
|
|
|
|
from dnslib import server
|
|
|
|
from dnslib import dns
|
|
|
|
|
|
|
|
from IPy import IP
|
|
|
|
|
2016-08-12 18:12:11 +03:00
|
|
|
from onedns import utils
|
|
|
|
|
2016-08-02 18:19:19 +03:00
|
|
|
|
|
|
|
class DynamicResolver(server.BaseResolver):
|
|
|
|
"""
|
|
|
|
Dynamic In-Memory DNS Resolver
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, domain, one_kwargs={}):
|
|
|
|
"""
|
|
|
|
Initialise resolver from zone list
|
|
|
|
Stores RRs as a list of (label, type, rr) tuples
|
|
|
|
"""
|
|
|
|
self.domain = domain
|
|
|
|
self.zone = []
|
|
|
|
self._tcp_server = None
|
|
|
|
self._udp_server = None
|
|
|
|
|
|
|
|
def resolve(self, request, handler):
|
|
|
|
"""
|
|
|
|
Respond to DNS request - parameters are request packet & handler.
|
|
|
|
Method is expected to return DNS response
|
|
|
|
"""
|
|
|
|
reply = request.reply()
|
|
|
|
qname = request.q.qname
|
|
|
|
qtype = dnslib.QTYPE[request.q.qtype]
|
2016-08-03 18:45:57 +03:00
|
|
|
A_RECORDS = ['A', 'AAAA']
|
2016-08-02 18:19:19 +03:00
|
|
|
for name, rtype, rr in self.zone:
|
|
|
|
# Check if label & type match
|
|
|
|
if qname == name and (qtype in [rtype, 'ANY'] or rtype == 'CNAME'):
|
|
|
|
reply.add_answer(rr)
|
|
|
|
# Check for A/AAAA records associated with reply and
|
|
|
|
# add in additional section
|
|
|
|
if rtype in ['CNAME', 'NS', 'MX', 'PTR']:
|
|
|
|
for a_name, a_rtype, a_rr in self.zone:
|
2016-08-03 18:45:57 +03:00
|
|
|
if a_name == rr.rdata.label and a_rtype in A_RECORDS:
|
2016-08-02 18:19:19 +03:00
|
|
|
reply.add_ar(a_rr)
|
|
|
|
if not reply.rr:
|
|
|
|
reply.header.rcode = dnslib.RCODE.NXDOMAIN
|
|
|
|
return reply
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
self.zone = []
|
|
|
|
|
2016-08-12 18:13:50 +03:00
|
|
|
def add_host(self, name, ip, zone=None):
|
|
|
|
zone = zone or self.zone
|
|
|
|
self._add_forward(name, ip, zone=zone)
|
|
|
|
self._add_reverse(ip, name, zone=zone)
|
2016-08-02 18:19:19 +03:00
|
|
|
|
|
|
|
def _get_fqdn(self, name):
|
2016-08-12 18:12:11 +03:00
|
|
|
return utils.get_fqdn(name, self.domain)
|
2016-08-02 18:19:19 +03:00
|
|
|
|
2016-08-12 18:13:50 +03:00
|
|
|
def _add_forward(self, name, ip, zone=None):
|
|
|
|
zone = zone or self.zone
|
2016-08-02 18:19:19 +03:00
|
|
|
f = dnslib.RR(rname=dnslib.DNSLabel(self._get_fqdn(name)),
|
|
|
|
rtype=dnslib.QTYPE.reverse['A'],
|
|
|
|
rclass=dnslib.CLASS.reverse['IN'],
|
|
|
|
rdata=dns.A(ip))
|
2016-08-12 18:13:50 +03:00
|
|
|
zone.append((f.rname, 'A', f))
|
2016-08-02 18:19:19 +03:00
|
|
|
|
2016-08-12 18:13:50 +03:00
|
|
|
def _add_reverse(self, ip, name, zone=None):
|
|
|
|
zone = zone or self.zone
|
2016-08-02 18:19:19 +03:00
|
|
|
ip = IP(ip)
|
|
|
|
r = dnslib.RR(rname=dnslib.DNSLabel(ip.reverseName()),
|
|
|
|
rtype=dnslib.QTYPE.reverse['PTR'],
|
|
|
|
rclass=dnslib.CLASS.reverse['IN'],
|
|
|
|
rdata=dns.PTR(self._get_fqdn(name)))
|
2016-08-12 18:13:50 +03:00
|
|
|
zone.append((r.rname, 'PTR', r))
|
2016-08-02 18:19:19 +03:00
|
|
|
|
|
|
|
def start(self, dns_address='0.0.0.0', dns_port=53,
|
|
|
|
api_address='127.0.0.1', api_port=8000, tcp=False, udplen=0,
|
|
|
|
log="request,reply,truncated,error", log_prefix=False):
|
|
|
|
logger = server.DNSLogger(log, log_prefix)
|
|
|
|
|
|
|
|
print("Starting OneDNS (%s:%d) [%s]" %
|
|
|
|
(dns_address or "*", dns_port, "UDP/TCP" if tcp else "UDP"))
|
|
|
|
|
|
|
|
server.DNSHandler.udplen = udplen
|
|
|
|
|
|
|
|
self._udp_server = server.DNSServer(self, port=dns_port,
|
|
|
|
address=dns_address, logger=logger)
|
|
|
|
self._udp_server.start_thread()
|
|
|
|
|
|
|
|
if tcp:
|
|
|
|
self._tcp_server = server.DNSServer(self, port=dns_port,
|
|
|
|
address=dns_address, tcp=True,
|
|
|
|
logger=logger)
|
|
|
|
self._tcp_server.start_thread()
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
for srv in [self._tcp_server, self._udp_server]:
|
|
|
|
if srv:
|
|
|
|
srv.stop()
|
|
|
|
srv.server.socket.close()
|
2016-08-02 20:21:42 +03:00
|
|
|
|
|
|
|
def daemon(self, *args, **kwargs):
|
|
|
|
testing = kwargs.pop('testing', False)
|
|
|
|
if self._udp_server is None or not self._udp_server.isAlive():
|
|
|
|
self.start(*args, **kwargs)
|
|
|
|
while self._udp_server.isAlive():
|
|
|
|
time.sleep(1)
|
|
|
|
if testing:
|
|
|
|
break
|