]> granicus.if.org Git - pdns/commitdiff
rec: Add some regression tests for our Lua hooks
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 19 Jun 2018 10:30:57 +0000 (12:30 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 19 Jun 2018 11:54:32 +0000 (13:54 +0200)
regression-tests.recursor-dnssec/cookiesoption.py [new file with mode: 0644]
regression-tests.recursor-dnssec/recursortests.py
regression-tests.recursor-dnssec/test_Lua.py [new file with mode: 0644]

diff --git a/regression-tests.recursor-dnssec/cookiesoption.py b/regression-tests.recursor-dnssec/cookiesoption.py
new file mode 100644 (file)
index 0000000..15ab172
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env python2
+
+import dns
+import dns.edns
+import dns.flags
+import dns.message
+import dns.query
+
+class CookiesOption(dns.edns.Option):
+    """Implementation of draft-ietf-dnsop-cookies-09.
+    """
+
+    def __init__(self, client, server):
+        super(CookiesOption, self).__init__(10)
+
+        if len(client) != 8:
+            raise Exception('invalid client cookie length')
+
+        if server is not None and len(server) != 0 and (len(server) < 8 or len(server) > 32):
+            raise Exception('invalid server cookie length')
+
+        self.client = client
+        self.server = server
+
+    def to_wire(self, file):
+        """Create EDNS packet as defined in draft-ietf-dnsop-cookies-09."""
+
+        file.write(self.client)
+        if self.server and len(self.server) > 0:
+            file.write(self.server)
+
+    def from_wire(cls, otype, wire, current, olen):
+        """Read EDNS packet as defined in draft-ietf-dnsop-cookies-09.
+
+        Returns:
+            An instance of CookiesOption based on the EDNS packet
+        """
+
+        data = wire[current:current + olen]
+        if len(data) != 8 and (len(data) < 16 or len(data) > 40):
+            raise Exception('Invalid EDNS Cookies option')
+
+        client = data[:8]
+        if len(data) > 8:
+            server = data[8:]
+        else:
+            server = None
+
+        return cls(client, server)
+
+    from_wire = classmethod(from_wire)
+
+    def __repr__(self):
+        return '%s(%s, %s)' % (
+            self.__class__.__name__,
+            self.client,
+            self.server
+        )
+
+    def __eq__(self, other):
+        if not isinstance(other, CookiesOption):
+            return False
+        if self.client != other.client:
+            return False
+        if self.server != other.server:
+            return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+
+dns.edns._type_to_class[0x000A] = CookiesOption
+
index 4641df3cf78545b4f85eea90ea41691928d5fdc9..8bfafa05dba7574371910106e6174a3947833467 100644 (file)
@@ -42,6 +42,7 @@ disable-syslog=yes
 """
     _config_params = []
     _lua_config_file = None
+    _lua_dns_script_file = None
     _roothints = """
 .                        3600 IN NS  ns.root.
 ns.root.                 3600 IN A   %s.8
@@ -444,10 +445,15 @@ distributor-threads=1""".format(confdir=confdir,
                 luaconfpath = os.path.join(confdir, 'conffile.lua')
                 with open(luaconfpath, 'w') as luaconf:
                     if cls._root_DS:
-                        luaconf.write("addDS('.', '%s')" % cls._root_DS)
+                        luaconf.write("addDS('.', '%s')\n" % cls._root_DS)
                     if cls._lua_config_file:
                         luaconf.write(cls._lua_config_file)
                 conf.write("lua-config-file=%s\n" % luaconfpath)
+            if cls._lua_dns_script_file:
+                luascriptpath = os.path.join(confdir, 'dnsscript.lua')
+                with open(luascriptpath, 'w') as luascript:
+                    luascript.write(cls._lua_dns_script_file)
+                conf.write("lua-dns-script=%s\n" % luascriptpath)
             if cls._roothints:
                 roothintspath = os.path.join(confdir, 'root.hints')
                 with open(roothintspath, 'w') as roothints:
diff --git a/regression-tests.recursor-dnssec/test_Lua.py b/regression-tests.recursor-dnssec/test_Lua.py
new file mode 100644 (file)
index 0000000..f615cf6
--- /dev/null
@@ -0,0 +1,207 @@
+import clientsubnetoption
+import cookiesoption
+import dns
+import os
+
+from recursortests import RecursorTest
+
+class GettagRecursorTest(RecursorTest):
+    _confdir = 'LuaGettag'
+    _config_template = """
+    log-common-errors=yes
+    gettag-needs-edns-options=yes
+    """
+    _lua_dns_script_file = """
+    function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
+
+      local tags = {}
+      local data = {}
+
+      -- make sure we can pass data around to the other hooks
+      data['canary'] = 'from-gettag'
+
+      -- test that the remote addr is valid
+      if remote:toString() ~= '127.0.0.1' then
+        pdnslog("invalid remote")
+        table.insert(tags, 'invalid remote '..remote:toString())
+        return 1, tags, data
+      end
+
+      -- test that the local addr is valid
+      if localip:toString() ~= '127.0.0.1' then
+        pdnslog("invalid local")
+        table.insert(tags, 'invalid local '..localip:toString())
+        return 1, tags, data
+      end
+
+      if not ednssubnet:empty() then
+         table.insert(tags, 'edns-subnet-'..ednssubnet:toString())
+      end
+
+      for k,v in pairs(ednsoptions) do
+        table.insert(tags, 'ednsoption-'..k..'-count-'..v:count())
+        local len = 0
+        local values = v:getValues()
+        for j,l in pairs(values) do
+          len = len + l:len()
+
+          -- check that the old interface (before 4.2.0) still works
+          if j == 0 then
+            if l:len() ~= v.size then
+              table.insert(tags, 'size obtained via the old edns option interface does not match')
+            end
+            value = v:getContent()
+            if value ~= l then
+              table.insert(tags, 'content obtained via the old edns option interface does not match')
+            end
+          end
+        end
+        table.insert(tags, 'ednsoption-'..k..'-total-len-'..len)
+      end
+
+      if tcp then
+        table.insert(tags, 'gettag-tcp')
+      end
+
+      -- test that tags are passed to other hooks
+      table.insert(tags, qname:toString())
+      table.insert(tags, 'gettag-qtype-'..qtype)
+
+      return 0, tags, data
+    end
+
+    function preresolve(dq)
+
+      -- test that we are getting the tags set by gettag()
+      -- and also getting the correct qname
+      local found = false
+      for _, tag in pairs(dq:getPolicyTags()) do
+        if dq.qname:equal(tag) then
+          found = true
+        end
+        dq:addAnswer(pdns.TXT, '"'..tag..'"')
+      end
+
+      if not found then
+        pdnslog("not valid tag found")
+        dq.rcode = pdns.REFUSED
+        return true
+      end
+
+      if dq.data['canary'] ~= 'from-gettag' then
+        pdnslog("did not get any data from gettag")
+        dq.rcode = pdns.REFUSED
+        return true
+      end
+
+      if dq.qtype == pdns.A then
+        dq:addAnswer(pdns.A, '192.0.2.1')
+      elseif dq.qtype == pdns.AAAA then
+        dq:addAnswer(pdns.AAAA, '2001:db8::1')
+      end
+
+      return true
+    end
+    """
+
+    @classmethod
+    def setUpClass(cls):
+
+        cls.setUpSockets()
+        confdir = os.path.join('configs', cls._confdir)
+        cls.createConfigDir(confdir)
+        cls.generateRecursorConfig(confdir)
+        cls.startRecursor(confdir, cls._recursorPort)
+
+    @classmethod
+    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 = [
+            dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
+            dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-1'])
+            ]
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertResponseMatches(query, expected, res)
+
+    def testTCPA(self):
+        name = 'gettag-tcpa.lua.'
+        expected = [
+            dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
+            dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [ name, 'gettag-qtype-1', 'gettag-tcp'])
+            ]
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendTCPQuery(query)
+        self.assertResponseMatches(query, expected, res)
+
+    def testAAAA(self):
+        name = 'gettag-aaaa.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'])
+            ]
+        query = dns.message.make_query(name, 'AAAA', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertResponseMatches(query, expected, res)
+
+    def testSubnet(self):
+        name = 'gettag-subnet.lua.'
+        subnet = '192.0.2.255'
+        subnetMask = 32
+        ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
+        expected = [
+            dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
+            dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [name, 'gettag-qtype-1', 'edns-subnet-' + subnet + '/' + str(subnetMask),
+                                                                         'ednsoption-8-count-1', 'ednsoption-8-total-len-8']),
+            ]
+        query = dns.message.make_query(name, 'A', want_dnssec=True, options=[ecso])
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertResponseMatches(query, expected, res)
+
+    def testEDNSOptions(self):
+        name = 'gettag-ednsoptions.lua.'
+        subnet = '192.0.2.255'
+        subnetMask = 32
+        ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
+        eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+        eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
+
+        expected = [
+            dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'),
+            dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [name, 'gettag-qtype-1', 'edns-subnet-' + subnet + '/' + str(subnetMask),
+                                                                         'ednsoption-10-count-2', 'ednsoption-10-total-len-32',
+                                                                         'ednsoption-8-count-1', 'ednsoption-8-total-len-8'
+                                                                        ]),
+            ]
+        query = dns.message.make_query(name, 'A', want_dnssec=True, options=[eco1,ecso,eco2])
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertResponseMatches(query, expected, res)
+
+# TODO:
+# - postresolve
+# - preoutquery
+# - ipfilter
+# - prerpz
+# - nxdomain
+# - nodata