import cookiesoption
import dns
import os
+import threading
+import time
+
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
from recursortests import RecursorTest
def tearDownClass(cls):
cls.tearDownRecursor()
- def assertResponseMatches(self, query, expectedRRs, response):
- expectedResponse = dns.message.make_response(query)
-
- if query.flags & dns.flags.RD:
- expectedResponse.flags |= dns.flags.RA
- if query.flags & dns.flags.CD:
- expectedResponse.flags |= dns.flags.CD
-
- expectedResponse.answer = expectedRRs
- print(expectedResponse)
- print(response)
- self.assertEquals(response, expectedResponse)
-
def testA(self):
name = 'gettag.lua.'
expected = [
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
+ def testAAAA(self):
+ name = 'gettag-tcpaaaa.lua.'
+ expected = [
+ dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:db8::1'),
+ dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-28', 'gettag-tcp'])
+ ]
+ query = dns.message.make_query(name, 'AAAA', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ res = self.sendTCPQuery(query)
+ self.assertResponseMatches(query, expected, res)
+
def testSubnet(self):
name = 'gettag-subnet.lua.'
subnet = '192.0.2.255'
res = self.sendUDPQuery(query)
self.assertResponseMatches(query, expected, res)
-# TODO:
-# - postresolve
-# - preoutquery
-# - ipfilter
-# - prerpz
-# - nxdomain
-# - nodata
+class GettagRecursorDistributesQueriesTest(GettagRecursorTest):
+ _confdir = 'LuaGettagDistributes'
+ _config_template = """
+ log-common-errors=yes
+ gettag-needs-edns-options=yes
+ pdns-distributes-queries=yes
+ threads=2
+ """
+
+hooksReactorRunning = False
+
+class UDPHooksResponder(DatagramProtocol):
+
+ def datagramReceived(self, datagram, address):
+ request = dns.message.from_wire(datagram)
+
+ response = dns.message.make_response(request)
+ response.flags |= dns.flags.AA
+
+ if request.question[0].name == dns.name.from_text('nxdomain.luahooks.example.'):
+ soa = dns.rrset.from_text('luahooks.example.', 86400, dns.rdataclass.IN, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
+ response.authority.append(soa)
+ response.set_rcode(dns.rcode.NXDOMAIN)
+
+ elif request.question[0].name == dns.name.from_text('nodata.luahooks.example.'):
+ soa = dns.rrset.from_text('luahooks.example.', 86400, dns.rdataclass.IN, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
+ response.authority.append(soa)
+
+ elif request.question[0].name == dns.name.from_text('postresolve.luahooks.example.'):
+ answer = dns.rrset.from_text('postresolve.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
+ response.answer.append(answer)
+
+ self.transport.write(response.to_wire(), address)
+
+class LuaHooksRecursorTest(RecursorTest):
+ _confdir = 'LuaHooks'
+ _config_template = """
+forward-zones=luahooks.example=%s.23
+log-common-errors=yes
+quiet=no
+ """ % (os.environ['PREFIX'])
+ _lua_dns_script_file = """
+
+ allowedips = newNMG()
+ allowedips:addMask("%s.0/24")
+
+ function ipfilter(remoteip, localip, dh)
+ -- allow only 127.0.0.1 and AD=0
+ if allowedips:match(remoteip) and not dh:getAD() then
+ return false
+ end
+
+ return true
+ end
+
+ function nodata(dq)
+ if dq.qtype == pdns.AAAA and dq.qname == newDN("nodata.luahooks.example.") then
+ dq:addAnswer(pdns.AAAA, "2001:DB8::1")
+ return true
+ end
+
+ return false
+ end
+
+ function nxdomain(dq)
+ if dq.qtype == pdns.A and dq.qname == newDN("nxdomain.luahooks.example.") then
+ dq.rcode=0
+ dq:addAnswer(pdns.A, "192.0.2.1")
+ return true
+ end
+
+ return false
+ end
+
+ function postresolve(dq)
+ if dq.qtype == pdns.A and dq.qname == newDN("postresolve.luahooks.example.") then
+ local records = dq:getRecords()
+ for k,v in pairs(records) do
+ if v.type == pdns.A and v:getContent() == "192.0.2.1" then
+ v:changeContent("192.0.2.42")
+ v.ttl=1
+ end
+ end
+ dq:setRecords(records)
+ return true
+ end
+
+ return false
+ end
+
+ function preoutquery(dq)
+ if dq.remoteaddr:equal(newCA("%s.23")) and dq.qname == newDN("preout.luahooks.example.") and dq.qtype == pdns.A then
+ dq.rcode = -3 -- "kill"
+ return true
+ end
+
+ return false
+ end
+
+ """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+
+ @classmethod
+ def startResponders(cls):
+ global hooksReactorRunning
+ print("Launching responders..")
+
+ address = cls._PREFIX + '.23'
+ port = 53
+
+ if not hooksReactorRunning:
+ reactor.listenUDP(port, UDPHooksResponder(), interface=address)
+ hooksReactorRunning = True
+
+ if not reactor.running:
+ cls._UDPResponder = threading.Thread(name='UDP Hooks Responder', target=reactor.run, args=(False,))
+ cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.start()
+
+ @classmethod
+ def setUpClass(cls):
+ cls.setUpSockets()
+
+ cls.startResponders()
+
+ confdir = os.path.join('configs', cls._confdir)
+ cls.createConfigDir(confdir)
+
+ cls.generateRecursorConfig(confdir)
+ cls.startRecursor(confdir, cls._recursorPort)
+
+ print("Launching tests..")
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.tearDownRecursor()
+
+ def testNoData(self):
+ expected = dns.rrset.from_text('nodata.luahooks.example.', 3600, dns.rdataclass.IN, 'AAAA', '2001:DB8::1')
+ query = dns.message.make_query('nodata.luahooks.example.', 'AAAA', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testVanillaNXD(self):
+ #expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
+ query = dns.message.make_query('nxdomain.luahooks.example.', 'AAAA', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
+
+ def testHookedNXD(self):
+ expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
+ query = dns.message.make_query('nxdomain.luahooks.example.', 'A', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testPostResolve(self):
+ expected = dns.rrset.from_text('postresolve.luahooks.example.', 1, dns.rdataclass.IN, 'A', '192.0.2.42')
+ query = dns.message.make_query('postresolve.luahooks.example.', 'A', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.assertEqual(res.answer[0].ttl, 1)
+
+ def testIPFilterHeader(self):
+ query = dns.message.make_query('ipfiler.luahooks.example.', 'A', 'IN')
+ query.flags |= dns.flags.AD
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertEqual(res, None)
+
+ def testPreOutInterceptedQuery(self):
+ query = dns.message.make_query('preout.luahooks.example.', 'A', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+
+ def testPreOutNotInterceptedQuery(self):
+ query = dns.message.make_query('preout.luahooks.example.', 'AAAA', 'IN')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+
+class LuaHooksRecursorDistributesTest(LuaHooksRecursorTest):
+ _confdir = 'LuaHooksDistributes'
+ _config_template = """
+forward-zones=luahooks.example=%s.23
+log-common-errors=yes
+pdns-distributes-queries=yes
+threads=2
+quiet=no
+ """ % (os.environ['PREFIX'])
+
+class DNS64Test(RecursorTest):
+ """Tests the dq.followupAction("getFakeAAAARecords")"""
+
+ _confdir = 'dns64'
+ _config_template = """
+ """
+ _lua_dns_script_file = """
+ prefix = "2001:DB8:64::"
+
+ function nodata (dq)
+ if dq.qtype ~= pdns.AAAA then
+ return false
+ end -- only AAAA records
+
+ -- don't fake AAAA records if DNSSEC validation failed
+ if dq.validationState == pdns.validationstates.Bogus then
+ return false
+ end
+
+ dq.followupFunction = "getFakeAAAARecords"
+ dq.followupPrefix = prefix
+ dq.followupName = dq.qname
+ return true
+ end
+ """
+
+ def testAtoAAAA(self):
+ expected = [
+ dns.rrset.from_text('ns.secure.example.', 15, dns.rdataclass.IN, 'AAAA', '2001:db8:64::7f00:9')
+ ]
+ query = dns.message.make_query('ns.secure.example', 'AAAA')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEqual(len(res.answer), 1)
+ self.assertEqual(len(res.authority), 0)
+ self.assertResponseMatches(query, expected, res)
+
+ def testAtoCNAMEtoAAAA(self):
+ expected = [
+ dns.rrset.from_text('cname-to-insecure.secure.example.', 3600, dns.rdataclass.IN, 'CNAME', 'node1.insecure.example.'),
+ dns.rrset.from_text('node1.insecure.example.', 3600, dns.rdataclass.IN, 'AAAA', '2001:db8:64::c000:206')
+ ]
+ query = dns.message.make_query('cname-to-insecure.secure.example.', 'AAAA')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEqual(len(res.answer), 2)
+ self.assertEqual(len(res.authority), 0)
+ self.assertResponseMatches(query, expected, res)
+
+
+class PDNSRandomTest(RecursorTest):
+ """Tests if pdnsrandom works"""
+
+ _confdir = 'pdnsrandom'
+ _config_template = """
+ """
+ _lua_dns_script_file = """
+ function preresolve (dq)
+ dq.rcode = pdns.NOERROR
+ dq:addAnswer(pdns.TXT, pdnsrandom())
+ return true
+ end
+ """
+
+ def testRandom(self):
+ query = dns.message.make_query('whatever.example.', 'TXT')
+
+ ans = set()
+
+ ret = self.sendUDPQuery(query)
+ ans.add(ret.answer[0])
+ ret = self.sendUDPQuery(query)
+ ans.add(ret.answer[0])
+
+ self.assertEqual(len(ans), 2)